commit d1907a5191c544e2d3e270164e14b7e844351b03 Author: gb <741021719@qq.com> Date: Tue May 3 11:56:07 2022 +0800 initialize diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..b6b71b6 --- /dev/null +++ b/build.sh @@ -0,0 +1,33 @@ +cd ../ +mkdir build +cd build +mkdir imgpc +cd imgpc +rm -rf * +cmake ../../device/hgdriver/ImageProcess +make + +cd .. +mkdir hgdev +cd hgdev +rm -rf * +cmake ../../device/hgdriver/hgdev +make + +cd .. +mkdir wrapper +cd wrapper +rm -rf * +cmake ../../device/hgdriver/huagaoxxx_warraper_ex +make + +cd .. +mkdir hgsane +cd hgsane +rm -rf * +cmake ../../device/hgsane +make +sudo cp ../../release/Linux/x86_64/libsane-hgsane.so /usr/lib/x86_64-linux-gnu/sane/libsane-hgsane.so.1 + + + diff --git a/hgdriver/3rdparty/cyusb/inc/CyAPI.h b/hgdriver/3rdparty/cyusb/inc/CyAPI.h new file mode 100644 index 0000000..3f7959a --- /dev/null +++ b/hgdriver/3rdparty/cyusb/inc/CyAPI.h @@ -0,0 +1,462 @@ +//______________________________________________________________________________ +// +// Copyright (c) Cypress Semiconductor, 2003 +// All rights reserved. +// +//______________________________________________________________________________ + +#ifndef CyUSBH +#define CyUSBH + +#ifndef __USB200_H__ +#define __USB200_H__ +#include + +#pragma pack(push,1) /// + +typedef struct _USB_DEVICE_DESCRIPTOR { //设备描述符 + UCHAR bLength; //长度 + UCHAR bDescriptorType; //描述符类型 + USHORT bcdUSB; //USB + UCHAR bDeviceClass; //设备类 + UCHAR bDeviceSubClass; //设备派生类 + UCHAR bDeviceProtocol; //设备协议 + UCHAR bMaxPacketSize0; //最大数据包尺寸 + USHORT idVendor; //厂商ID + USHORT idProduct; //产品ID + USHORT bcdDevice; //设备 + UCHAR iManufacturer; //制造商 + UCHAR iProduct; //产品 + UCHAR iSerialNumber; //序列号 + UCHAR bNumConfigurations;//配置 +} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; + +typedef struct _USB_ENDPOINT_DESCRIPTOR { //端点描述符 + UCHAR bLength; //长度 + UCHAR bDescriptorType; //描述符类型 + UCHAR bEndpointAddress; //端点地址 + UCHAR bmAttributes; //端点属性 + USHORT wMaxPacketSize; //最大数据包尺寸 + UCHAR bInterval; //间隔 +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct _USB_CONFIGURATION_DESCRIPTOR { //配置描述符 + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct _USB_INTERFACE_DESCRIPTOR { //接口描述符 + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct _USB_STRING_DESCRIPTOR { //字符串描述符 + UCHAR bLength; + UCHAR bDescriptorType; + WCHAR bString[1]; +} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR; + +typedef struct _USB_COMMON_DESCRIPTOR { //USB串口描述符 + UCHAR bLength; + UCHAR bDescriptorType; +} USB_COMMON_DESCRIPTOR, *PUSB_COMMON_DESCRIPTOR; +#pragma pack(pop) +#endif +//______________________________________________________________________________ + +class CCyIsoPktInfo { //包信息 +public: + LONG Status; //包状态 + LONG Length; //包长度 +}; + +//______________________________________________________________________________ + + +// {AE18AA60-7F6A-11d4-97DD-00010229B959} +static GUID CYUSBDRV_GUID = {0xae18aa60, 0x7f6a, 0x11d4, 0x97, 0xdd, 0x0, 0x1, 0x2, 0x29, 0xb9, 0x59}; + +typedef enum {TGT_DEVICE, TGT_INTFC, TGT_ENDPT, TGT_OTHER } CTL_XFER_TGT_TYPE; +typedef enum {REQ_STD, REQ_CLASS, REQ_VENDOR } CTL_XFER_REQ_TYPE; +typedef enum {DIR_TO_DEVICE, DIR_FROM_DEVICE } CTL_XFER_DIR_TYPE; +typedef enum {XMODE_BUFFERED, XMODE_DIRECT } XFER_MODE_TYPE; + +const int MAX_ENDPTS = 16; +const int MAX_INTERFACES = 8; +const int USB_STRING_MAXLEN = 256; + + +//////////////////////////////////////////////////////////////////////////////// +// +// The CCyEndPoint ABSTRACT Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyUSBEndPoint +{ +protected: + bool WaitForIO(OVERLAPPED *ovLapStatus); + + virtual PUCHAR BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); //直接传输模式 + virtual PUCHAR BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); //缓冲传输模式 + + +public: + + CCyUSBEndPoint(void); + CCyUSBEndPoint(CCyUSBEndPoint& ept); + CCyUSBEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + HANDLE hDevice; + + // The fields of an EndPoint Descriptor + UCHAR DscLen; + UCHAR DscType; + UCHAR Address; + UCHAR Attributes; + USHORT MaxPktSize; + USHORT PktsPerFrame; + UCHAR Interval; + + // Other fields + ULONG TimeOut; + ULONG UsbdStatus; + ULONG NtStatus; + + DWORD bytesWritten; + DWORD LastError; + bool bIn; + + XFER_MODE_TYPE XferMode; + + bool XferData(PUCHAR buf, LONG &len, CCyIsoPktInfo* pktInfos = NULL); + bool XferData(PUCHAR buf, LONG &bufLen, CCyIsoPktInfo* pktInfos, bool pktMode); + virtual PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov) = 0; + virtual bool FinishDataXfer(PUCHAR buf, LONG &len, OVERLAPPED *ov, PUCHAR pXmitBuf, CCyIsoPktInfo* pktInfos = NULL); + bool WaitForXfer(OVERLAPPED *ov, ULONG tOut); + ULONG GetXferSize(void); + void SetXferSize(ULONG xfer); + + bool Reset(void); + bool Abort(void); + +private: + + + +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Control Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyControlEndPoint : public CCyUSBEndPoint +{ +private: + +public: + CCyControlEndPoint(void); + CCyControlEndPoint(CCyControlEndPoint& ept); + CCyControlEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + CTL_XFER_TGT_TYPE Target; + CTL_XFER_REQ_TYPE ReqType; + CTL_XFER_DIR_TYPE Direction; + + UCHAR ReqCode; + WORD Value; + WORD Index; + + bool Read(PUCHAR buf, LONG &len); + bool Write(PUCHAR buf, LONG &len); + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Isoc Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyIsocEndPoint : public CCyUSBEndPoint +{ + +protected: + virtual PUCHAR BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); + virtual PUCHAR BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); + +public: + CCyIsocEndPoint(void); + CCyIsocEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); + CCyIsoPktInfo* CreatePktInfos(LONG bufLen, int &packets); + +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Bulk Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyBulkEndPoint : public CCyUSBEndPoint +{ +public: + CCyBulkEndPoint(void); + CCyBulkEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Interrupt Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyInterruptEndPoint : public CCyUSBEndPoint +{ +public: + CCyInterruptEndPoint(void); + CCyInterruptEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Interface Class +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBInterface +{ +private: +protected: +public: + CCyUSBEndPoint *EndPoints[MAX_ENDPTS]; // Holds pointers to all the interface's endpoints, plus a pointer to the Control endpoint zero + + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; // Not counting the control endpoint + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; + + UCHAR bAltSettings; + USHORT wTotalLength; // Needed in case Intfc has additional (non-endpt) descriptors + + + CCyUSBInterface(HANDLE h, PUSB_INTERFACE_DESCRIPTOR pIntfcDescriptor); + CCyUSBInterface(CCyUSBInterface& ifc); // Copy Constructor + ~CCyUSBInterface(void); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// The Config Class +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBConfig +{ +private: + +protected: +public: + CCyUSBInterface *Interfaces[MAX_INTERFACES]; + + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; + + UCHAR AltInterfaces; + + + CCyUSBConfig(void); + CCyUSBConfig(CCyUSBConfig& cfg); // Copy Constructor + CCyUSBConfig(HANDLE h, PUSB_CONFIGURATION_DESCRIPTOR pConfigDescr); + ~CCyUSBConfig(void); + + + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// The USB Device Class - This is the main class that contains members of all the +// other classes. +// +// To use the library, create an instance of this Class and call it's Open method +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBDevice +{ +// The public members are accessible (i.e. corruptible) by the user of the library +// Algorithms of the class don't rely on any public members. Instead, they use the +// private members of the class for their calculations. + +public: + + CCyUSBDevice(HANDLE hnd = NULL, GUID guid = CYUSBDRV_GUID, BOOL bOpen = true); + ~CCyUSBDevice(void); + + CCyUSBEndPoint **EndPoints; // Shortcut to USBCfgs[CfgNum]->Interfaces[IntfcIndex]->Endpoints + CCyUSBEndPoint *EndPointOf(UCHAR addr); + + CCyControlEndPoint *ControlEndPt; + CCyIsocEndPoint *IsocInEndPt; + CCyIsocEndPoint *IsocOutEndPt; + CCyBulkEndPoint *BulkInEndPt; + CCyBulkEndPoint *BulkOutEndPt; + CCyInterruptEndPoint *InterruptInEndPt; + CCyInterruptEndPoint *InterruptOutEndPt; + + USHORT StrLangID; + ULONG UsbdStatus; + ULONG NtStatus; + ULONG DriverVersion; + ULONG USBDIVersion; + char DeviceName[USB_STRING_MAXLEN]; + char FriendlyName[USB_STRING_MAXLEN]; + wchar_t Manufacturer[USB_STRING_MAXLEN]; + wchar_t Product[USB_STRING_MAXLEN]; + wchar_t SerialNumber[USB_STRING_MAXLEN]; + + CHAR DevPath[USB_STRING_MAXLEN]; + + USHORT BcdUSB; + USHORT VendorID; + USHORT ProductID; + UCHAR USBAddress; + UCHAR DevClass; + UCHAR DevSubClass; + UCHAR DevProtocol; + UCHAR MaxPacketSize; + USHORT BcdDevice; + + UCHAR ConfigValue; + UCHAR ConfigAttrib; + UCHAR MaxPower; + + UCHAR IntfcClass; + UCHAR IntfcSubClass; + UCHAR IntfcProtocol; + bool bHighSpeed; + + DWORD BytesXfered; + + + UCHAR DeviceCount(void); + UCHAR ConfigCount(void); + UCHAR IntfcCount(void); + UCHAR AltIntfcCount(void); + UCHAR EndPointCount(void); + + UCHAR Config(void) { return CfgNum; } // Normally 0 + void SetConfig(UCHAR cfg); + + UCHAR Interface(void) { return IntfcNum; } // Usually 0 + // No SetInterface method since only 1 intfc per device (per Windows) + + UCHAR AltIntfc(void); + bool SetAltIntfc(UCHAR alt); + + GUID DriverGUID(void) { return DrvGuid; } + HANDLE DeviceHandle(void) { return hDevice; } + void UsbdStatusString(ULONG stat, PCHAR s); + bool CreateHandle(UCHAR dev); + void DestroyHandle(); + + bool Open(UCHAR dev); + void Close(void); + bool Reset(void); + bool ReConnect(void); + bool Suspend(void); + bool Resume(void); + bool IsOpen(void) { return (hDevice != INVALID_HANDLE_VALUE); } + + UCHAR PowerState(void); + + + void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR descr); + void GetConfigDescriptor(PUSB_CONFIGURATION_DESCRIPTOR descr); + void GetIntfcDescriptor(PUSB_INTERFACE_DESCRIPTOR descr); + CCyUSBConfig GetUSBConfig(int index); + + +private: + + USB_DEVICE_DESCRIPTOR USBDeviceDescriptor; + PUSB_CONFIGURATION_DESCRIPTOR USBConfigDescriptors[2]; + + CCyUSBConfig *USBCfgs[2]; + + HANDLE hWnd; + HANDLE hDevice; + HANDLE hDevNotification; + HANDLE hHndNotification; + + GUID DrvGuid; + + UCHAR Devices; + UCHAR Interfaces; + UCHAR AltInterfaces; + UCHAR Configs; + + UCHAR DevNum; + UCHAR CfgNum; + UCHAR IntfcNum; // The current selected interface's bInterfaceNumber + UCHAR IntfcIndex; // The entry in the Config's interfaces table matching to IntfcNum and AltSetting + + void GetDevDescriptor(void); + void GetCfgDescriptor(int descIndex); + void GetString(wchar_t *s, UCHAR sIndex); + void SetStringDescrLanguage(void); + void SetAltIntfcParams(UCHAR alt); + bool IoControl(ULONG cmd, PUCHAR buf, ULONG len); + + void SetEndPointPtrs(void); + void GetDeviceName(void); + void GetFriendlyName(void); + void GetDriverVer(void); + void GetUSBDIVer(void); + void GetSpeed(void); + void GetUSBAddress(void); + //void CloseEndPtHandles(void); + + bool RegisterForPnpEvents(HANDLE h); +}; + +//--------------------------------------------------------------------------- +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/api/StopWatch.h b/hgdriver/3rdparty/hgOCR/include/api/StopWatch.h new file mode 100644 index 0000000..afa4ee9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/StopWatch.h @@ -0,0 +1,34 @@ +#pragma once +#include + +class StopWatch +{ +public: + StopWatch() { + _start = std::chrono::steady_clock::now(); + } + + void reset() { + _start = std::chrono::steady_clock::now(); + } + + double elapsed_s() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_ms() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_us() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_ns() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + +private: + std::chrono::steady_clock::time_point _start; +}; + diff --git a/hgdriver/3rdparty/hgOCR/include/api/apitypes.h b/hgdriver/3rdparty/hgOCR/include/api/apitypes.h new file mode 100644 index 0000000..2cb38ad --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/apitypes.h @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////// +// File: apitypes.h +// Description: Types used in both the API and internally +// Author: Ray Smith +// Created: Wed Mar 03 09:22:53 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_API_APITYPES_H__ +#define TESSERACT_API_APITYPES_H__ + +#include "publictypes.h" + +// The types used by the API and Page/ResultIterator can be found in: +// ccstruct/publictypes.h +// ccmain/resultiterator.h +// ccmain/pageiterator.h +// API interfaces and API users should be sure to include this file, rather +// than the lower-level one, and lower-level code should be sure to include +// only the lower-level file. + +#endif // TESSERACT_API_APITYPES_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/api/baseapi.cpp b/hgdriver/3rdparty/hgOCR/include/api/baseapi.cpp new file mode 100644 index 0000000..5cce561 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/baseapi.cpp @@ -0,0 +1,2892 @@ +/********************************************************************** + * File: baseapi.cpp + * Description: Simple API for calling tesseract. + * Author: Ray Smith + * Created: Fri Oct 06 15:35:01 PDT 2006 + * + * (C) Copyright 2006, Google Inc. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#ifdef __linux__ +#include +#endif + +#if defined(_WIN32) +#ifdef _MSC_VER +#include "vcsversion.h" +#include "mathfix.h" +#elif MINGW +// workaround for stdlib.h with -std=c++11 for _splitpath and _MAX_FNAME +#undef __STRICT_ANSI__ +#endif // _MSC_VER +#include +#include +#else +#include +#include +#include +#endif // _WIN32 + +#include +#include +#include +#include + +#include "allheaders.h" + +#include "baseapi.h" +#include "blobclass.h" +#include "resultiterator.h" +#include "mutableiterator.h" +#include "thresholder.h" +#include "tesseractclass.h" +#include "pageres.h" +#include "paragraphs.h" +#include "tessvars.h" +#include "control.h" +#include "dict.h" +#include "pgedit.h" +#include "paramsd.h" +#include "output.h" +#include "globaloc.h" +#include "globals.h" +#include "edgblob.h" +#include "equationdetect.h" +#include "tessbox.h" +#include "makerow.h" +#include "otsuthr.h" +#include "osdetect.h" +#include "params.h" +#include "renderer.h" +#include "strngs.h" +#include "openclwrapper.h" + +BOOL_VAR(stream_filelist, FALSE, "Stream a filelist from stdin"); + +namespace tesseract { + + /** Minimum sensible image size to be worth running tesseract. */ + const int kMinRectSize = 10; + /** Character returned when Tesseract couldn't recognize as anything. */ + const char kTesseractReject = '~'; + /** Character used by UNLV error counter as a reject. */ + const char kUNLVReject = '~'; + /** Character used by UNLV as a suspect marker. */ + const char kUNLVSuspect = '^'; + /** + * Filename used for input image file, from which to derive a name to search + * for a possible UNLV zone file, if none is specified by SetInputName. + */ + const char* kInputFile = "noname.tif"; + /** + * Temp file used for storing current parameters before applying retry values. + */ + const char* kOldVarsFile = "failed_vars.txt"; + /** Max string length of an int. */ + const int kMaxIntSize = 22; + /** + * Minimum believable resolution. Used as a default if there is no other + * information, as it is safer to under-estimate than over-estimate. + */ + const int kMinCredibleResolution = 70; + /** Maximum believable resolution. */ + const int kMaxCredibleResolution = 2400; + + TessBaseAPI::TessBaseAPI() + : tesseract_(NULL), + osd_tesseract_(NULL), + equ_detect_(NULL), + // Thresholder is initialized to NULL here, but will be set before use by: + // A constructor of a derived API, SetThresholder(), or + // created implicitly when used in InternalSetImage. + thresholder_(NULL), + paragraph_models_(NULL), + block_list_(NULL), + page_res_(NULL), + input_file_(NULL), + output_file_(NULL), + datapath_(NULL), + language_(NULL), + last_oem_requested_(OEM_DEFAULT), + recognition_done_(false), + truth_cb_(NULL), + rect_left_(0), rect_top_(0), rect_width_(0), rect_height_(0), + image_width_(0), image_height_(0) { + unknown_title_ = ""; + } + + TessBaseAPI::~TessBaseAPI() { + End(); + } + + /** + * Returns the version identifier as a static string. Do not delete. + */ + const char* TessBaseAPI::Version() { +#if defined(GIT_REV) && (defined(DEBUG) || defined(_DEBUG)) + return GIT_REV; +#else + return TESSERACT_VERSION_STR; +#endif + } + + /** + * If compiled with OpenCL AND an available OpenCL + * device is deemed faster than serial code, then + * "device" is populated with the cl_device_id + * and returns sizeof(cl_device_id) + * otherwise *device=NULL and returns 0. + */ +#ifdef USE_OPENCL +#if USE_DEVICE_SELECTION +#include "opencl_device_selection.h" +#endif +#endif + size_t TessBaseAPI::getOpenCLDevice(void **data) { +#ifdef USE_OPENCL +#if USE_DEVICE_SELECTION + ds_device device = OpenclDevice::getDeviceSelection(); + if (device.type == DS_DEVICE_OPENCL_DEVICE) { + *data = reinterpret_cast(new cl_device_id); + memcpy(*data, &device.oclDeviceID, sizeof(cl_device_id)); + return sizeof(cl_device_id); + } +#endif +#endif + + *data = NULL; + return 0; + } + + /** + * Writes the thresholded image to stderr as a PBM file on receipt of a + * SIGSEGV, SIGFPE, or SIGBUS signal. (Linux/Unix only). + */ + void TessBaseAPI::CatchSignals() { +#ifdef __linux__ + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = &signal_exit; + action.sa_flags = SA_RESETHAND; + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGFPE, &action, NULL); + sigaction(SIGBUS, &action, NULL); +#else + // Warn API users that an implementation is needed. + tprintf("CatchSignals has no non-linux implementation!\n"); +#endif + } + + /** + * Set the name of the input file. Needed only for training and + * loading a UNLV zone file. + */ + void TessBaseAPI::SetInputName(const char* name) { + if (input_file_ == NULL) + input_file_ = new STRING(name); + else + *input_file_ = name; + } + + /** Set the name of the output files. Needed only for debugging. */ + void TessBaseAPI::SetOutputName(const char* name) { + if (output_file_ == NULL) + output_file_ = new STRING(name); + else + *output_file_ = name; + } + + bool TessBaseAPI::SetVariable(const char* name, const char* value) { + if (tesseract_ == NULL) tesseract_ = new Tesseract; + return ParamUtils::SetParam(name, value, SET_PARAM_CONSTRAINT_NON_INIT_ONLY, + tesseract_->params()); + } + + bool TessBaseAPI::SetDebugVariable(const char* name, const char* value) { + if (tesseract_ == NULL) tesseract_ = new Tesseract; + return ParamUtils::SetParam(name, value, SET_PARAM_CONSTRAINT_DEBUG_ONLY, + tesseract_->params()); + } + + bool TessBaseAPI::GetIntVariable(const char *name, int *value) const { + IntParam *p = ParamUtils::FindParam( + name, GlobalParams()->int_params, tesseract_->params()->int_params); + if (p == NULL) return false; + *value = (inT32)(*p); + return true; + } + + bool TessBaseAPI::GetBoolVariable(const char *name, bool *value) const { + BoolParam *p = ParamUtils::FindParam( + name, GlobalParams()->bool_params, tesseract_->params()->bool_params); + if (p == NULL) return false; + *value = (BOOL8)(*p); + return true; + } + + const char *TessBaseAPI::GetStringVariable(const char *name) const { + StringParam *p = ParamUtils::FindParam( + name, GlobalParams()->string_params, tesseract_->params()->string_params); + return (p != NULL) ? p->string() : NULL; + } + + bool TessBaseAPI::GetDoubleVariable(const char *name, double *value) const { + DoubleParam *p = ParamUtils::FindParam( + name, GlobalParams()->double_params, tesseract_->params()->double_params); + if (p == NULL) return false; + *value = (double)(*p); + return true; + } + + /** Get value of named variable as a string, if it exists. */ + bool TessBaseAPI::GetVariableAsString(const char *name, STRING *val) { + return ParamUtils::GetParamAsString(name, tesseract_->params(), val); + } + + /** Print Tesseract parameters to the given file. */ + void TessBaseAPI::PrintVariables(FILE *fp) const { + ParamUtils::PrintParams(fp, tesseract_->params()); + } + + /** + * The datapath must be the name of the data directory (no ending /) or + * some other file in which the data directory resides (for instance argv[0].) + * The language is (usually) an ISO 639-3 string or NULL will default to eng. + * If numeric_mode is true, then only digits and Roman numerals will + * be returned. + * @return: 0 on success and -1 on initialization failure. + */ + int TessBaseAPI::Init(const char* datapath, const char* language, + OcrEngineMode oem, char **configs, int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_non_debug_params) { + PERF_COUNT_START("TessBaseAPI::Init") + // Default language is "eng". + if (language == NULL) language = "eng"; + // If the datapath, OcrEngineMode or the language have changed - start again. + // Note that the language_ field stores the last requested language that was + // initialized successfully, while tesseract_->lang stores the language + // actually used. They differ only if the requested language was NULL, in + // which case tesseract_->lang is set to the Tesseract default ("eng"). + if (tesseract_ != NULL && + (datapath_ == NULL || language_ == NULL || + *datapath_ != datapath || last_oem_requested_ != oem || + (*language_ != language && tesseract_->lang != language))) { + delete tesseract_; + tesseract_ = NULL; + } + // PERF_COUNT_SUB("delete tesseract_") +#ifdef USE_OPENCL + OpenclDevice od; + od.InitEnv(); +#endif + PERF_COUNT_SUB("OD::InitEnv()") + bool reset_classifier = true; + if (tesseract_ == NULL) { + reset_classifier = false; + tesseract_ = new Tesseract; + if (tesseract_->init_tesseract( + datapath, output_file_ != NULL ? output_file_->string() : NULL, + language, oem, configs, configs_size, vars_vec, vars_values, + set_only_non_debug_params) != 0) { + return -1; + } + } + PERF_COUNT_SUB("update tesseract_") + // Update datapath and language requested for the last valid initialization. + if (datapath_ == NULL) + datapath_ = new STRING(datapath); + else + *datapath_ = datapath; + if ((strcmp(datapath_->string(), "") == 0) && + (strcmp(tesseract_->datadir.string(), "") != 0)) + *datapath_ = tesseract_->datadir; + + if (language_ == NULL) + language_ = new STRING(language); + else + *language_ = language; + last_oem_requested_ = oem; + // PERF_COUNT_SUB("update last_oem_requested_") + // For same language and datapath, just reset the adaptive classifier. + if (reset_classifier) { + tesseract_->ResetAdaptiveClassifier(); + PERF_COUNT_SUB("tesseract_->ResetAdaptiveClassifier()") + } + PERF_COUNT_END + return 0; + } + + /** + * Returns the languages string used in the last valid initialization. + * If the last initialization specified "deu+hin" then that will be + * returned. If hin loaded eng automatically as well, then that will + * not be included in this list. To find the languages actually + * loaded use GetLoadedLanguagesAsVector. + * The returned string should NOT be deleted. + */ + const char* TessBaseAPI::GetInitLanguagesAsString() const { + return (language_ == NULL || language_->string() == NULL) ? + "" : language_->string(); + } + + /** + * Returns the loaded languages in the vector of STRINGs. + * Includes all languages loaded by the last Init, including those loaded + * as dependencies of other loaded languages. + */ + void TessBaseAPI::GetLoadedLanguagesAsVector( + GenericVector* langs) const { + langs->clear(); + if (tesseract_ != NULL) { + langs->push_back(tesseract_->lang); + int num_subs = tesseract_->num_sub_langs(); + for (int i = 0; i < num_subs; ++i) + langs->push_back(tesseract_->get_sub_lang(i)->lang); + } + } + + /** + * Returns the available languages in the vector of STRINGs. + */ + void TessBaseAPI::GetAvailableLanguagesAsVector( + GenericVector* langs) const { + langs->clear(); + if (tesseract_ != NULL) { +#ifdef _WIN32 + STRING pattern = tesseract_->datadir + "/*." + kTrainedDataSuffix; + char fname[_MAX_FNAME]; + WIN32_FIND_DATA data; + BOOL result = TRUE; + HANDLE handle = FindFirstFile(pattern.string(), &data); + if (handle != INVALID_HANDLE_VALUE) { + for (; result; result = FindNextFile(handle, &data)) { + _splitpath(data.cFileName, NULL, NULL, fname, NULL); + langs->push_back(STRING(fname)); + } + FindClose(handle); + } +#else // _WIN32 + DIR *dir; + struct dirent *dirent; + char *dot; + + STRING extension = STRING(".") + kTrainedDataSuffix; + + dir = opendir(tesseract_->datadir.string()); + if (dir != NULL) { + while ((dirent = readdir(dir))) { + // Skip '.', '..', and hidden files + if (dirent->d_name[0] != '.') { + if (strstr(dirent->d_name, extension.string()) != NULL) { + dot = strrchr(dirent->d_name, '.'); + // This ensures that .traineddata is at the end of the file name + if (strncmp(dot, extension.string(), + strlen(extension.string())) == 0) { + *dot = '\0'; + langs->push_back(STRING(dirent->d_name)); + } + } + } + } + closedir(dir); + } +#endif + } + } + + /** + * Init only the lang model component of Tesseract. The only functions + * that work after this init are SetVariable and IsValidWord. + * WARNING: temporary! This function will be removed from here and placed + * in a separate API at some future time. + */ + int TessBaseAPI::InitLangMod(const char* datapath, const char* language) { + if (tesseract_ == NULL) + tesseract_ = new Tesseract; + else + ParamUtils::ResetToDefaults(tesseract_->params()); + return tesseract_->init_tesseract_lm(datapath, NULL, language); + } + + /** + * Init only for page layout analysis. Use only for calls to SetImage and + * AnalysePage. Calls that attempt recognition will generate an error. + */ + void TessBaseAPI::InitForAnalysePage() { + if (tesseract_ == NULL) { + tesseract_ = new Tesseract; + tesseract_->InitAdaptiveClassifier(false); + } + } + + /** + * Read a "config" file containing a set of parameter name, value pairs. + * Searches the standard places: tessdata/configs, tessdata/tessconfigs + * and also accepts a relative or absolute path name. + */ + void TessBaseAPI::ReadConfigFile(const char* filename) { + tesseract_->read_config_file(filename, SET_PARAM_CONSTRAINT_NON_INIT_ONLY); + } + + /** Same as above, but only set debug params from the given config file. */ + void TessBaseAPI::ReadDebugConfigFile(const char* filename) { + tesseract_->read_config_file(filename, SET_PARAM_CONSTRAINT_DEBUG_ONLY); + } + + /** + * Set the current page segmentation mode. Defaults to PSM_AUTO. + * The mode is stored as an IntParam so it can also be modified by + * ReadConfigFile or SetVariable("tessedit_pageseg_mode", mode as string). + */ + void TessBaseAPI::SetPageSegMode(PageSegMode mode) { + if (tesseract_ == NULL) + tesseract_ = new Tesseract; + tesseract_->tessedit_pageseg_mode.set_value(mode); + } + + /** Return the current page segmentation mode. */ + PageSegMode TessBaseAPI::GetPageSegMode() const { + if (tesseract_ == NULL) + return PSM_SINGLE_BLOCK; + return static_cast( + static_cast(tesseract_->tessedit_pageseg_mode)); + } + + /** + * Recognize a rectangle from an image and return the result as a string. + * May be called many times for a single Init. + * Currently has no error checking. + * Greyscale of 8 and color of 24 or 32 bits per pixel may be given. + * Palette color images will not work properly and must be converted to + * 24 bit. + * Binary images of 1 bit per pixel may also be given but they must be + * byte packed with the MSB of the first byte being the first pixel, and a + * one pixel is WHITE. For binary images set bytes_per_pixel=0. + * The recognized text is returned as a char* which is coded + * as UTF8 and must be freed with the delete [] operator. + */ + char* TessBaseAPI::TesseractRect(const unsigned char* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height) { + if (tesseract_ == NULL || width < kMinRectSize || height < kMinRectSize) + return NULL; // Nothing worth doing. + + // Since this original api didn't give the exact size of the image, + // we have to invent a reasonable value. + int bits_per_pixel = bytes_per_pixel == 0 ? 1 : bytes_per_pixel * 8; + SetImage(imagedata, bytes_per_line * 8 / bits_per_pixel, height + top, + bytes_per_pixel, bytes_per_line); + SetRectangle(left, top, width, height); + + return GetUTF8Text(); + } + + /** + * Call between pages or documents etc to free up memory and forget + * adaptive data. + */ + void TessBaseAPI::ClearAdaptiveClassifier() { + if (tesseract_ == NULL) + return; + tesseract_->ResetAdaptiveClassifier(); + tesseract_->ResetDocumentDictionary(); + } + + /** + * Provide an image for Tesseract to recognize. Format is as + * TesseractRect above. Copies the image buffer and converts to Pix. + * SetImage clears all recognition results, and sets the rectangle to the + * full image, so it may be followed immediately by a GetUTF8Text, and it + * will automatically perform recognition. + */ + void TessBaseAPI::SetImage(const unsigned char* imagedata, + int width, int height, + int bytes_per_pixel, int bytes_per_line) { + if (InternalSetImage()) { + thresholder_->SetImage(imagedata, width, height, + bytes_per_pixel, bytes_per_line); + SetInputImage(thresholder_->GetPixRect()); + } + } + + void TessBaseAPI::SetSourceResolution(int ppi) { + if (thresholder_) + thresholder_->SetSourceYResolution(ppi); + else + tprintf("Please call SetImage before SetSourceResolution.\n"); + } + + /** + * Provide an image for Tesseract to recognize. As with SetImage above, + * Tesseract takes its own copy of the image, so it need not persist until + * after Recognize. + * Pix vs raw, which to use? + * Use Pix where possible. Tesseract uses Pix as its internal representation + * and it is therefore more efficient to provide a Pix directly. + */ + void TessBaseAPI::SetImage(Pix* pix) { + if (InternalSetImage()) { + thresholder_->SetImage(pix); + SetInputImage(thresholder_->GetPixRect()); + } + } + + /** + * Restrict recognition to a sub-rectangle of the image. Call after SetImage. + * Each SetRectangle clears the recogntion results so multiple rectangles + * can be recognized with the same image. + */ + void TessBaseAPI::SetRectangle(int left, int top, int width, int height) { + if (thresholder_ == NULL) + return; + thresholder_->SetRectangle(left, top, width, height); + ClearResults(); + } + + /** + * ONLY available after SetImage if you have Leptonica installed. + * Get a copy of the internal thresholded image from Tesseract. + */ + Pix* TessBaseAPI::GetThresholdedImage() { + if (tesseract_ == NULL || thresholder_ == NULL) + return NULL; + if (tesseract_->pix_binary() == NULL) + Threshold(tesseract_->mutable_pix_binary()); + return pixClone(tesseract_->pix_binary()); + } + + /** + * Get the result of page layout analysis as a leptonica-style + * Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + */ + Boxa* TessBaseAPI::GetRegions(Pixa** pixa) { + return GetComponentImages(RIL_BLOCK, false, pixa, NULL); + } + + /** + * Get the textlines as a leptonica-style Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + * If blockids is not NULL, the block-id of each line is also returned as an + * array of one element per line. delete [] after use. + * If paraids is not NULL, the paragraph-id of each line within its block is + * also returned as an array of one element per line. delete [] after use. + */ + Boxa* TessBaseAPI::GetTextlines(const bool raw_image, const int raw_padding, + Pixa** pixa, int** blockids, int** paraids) { + return GetComponentImages(RIL_TEXTLINE, true, raw_image, raw_padding, + pixa, blockids, paraids); + } + + /** + * Get textlines and strips of image regions as a leptonica-style Boxa, Pixa + * pair, in reading order. Enables downstream handling of non-rectangular + * regions. + * Can be called before or after Recognize. + * If blockids is not NULL, the block-id of each line is also returned as an + * array of one element per line. delete [] after use. + */ + Boxa* TessBaseAPI::GetStrips(Pixa** pixa, int** blockids) { + return GetComponentImages(RIL_TEXTLINE, false, pixa, blockids); + } + + /** + * Get the words as a leptonica-style + * Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + */ + Boxa* TessBaseAPI::GetWords(Pixa** pixa) { + return GetComponentImages(RIL_WORD, true, pixa, NULL); + } + + /** + * Gets the individual connected (text) components (created + * after pages segmentation step, but before recognition) + * as a leptonica-style Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + */ + Boxa* TessBaseAPI::GetConnectedComponents(Pixa** pixa) { + return GetComponentImages(RIL_SYMBOL, true, pixa, NULL); + } + + /** + * Get the given level kind of components (block, textline, word etc.) as a + * leptonica-style Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + * If blockids is not NULL, the block-id of each component is also returned + * as an array of one element per component. delete [] after use. + * If text_only is true, then only text components are returned. + */ + Boxa* TessBaseAPI::GetComponentImages(PageIteratorLevel level, + bool text_only, bool raw_image, + const int raw_padding, + Pixa** pixa, int** blockids, + int** paraids) { + PageIterator* page_it = GetIterator(); + if (page_it == NULL) + page_it = AnalyseLayout(); + if (page_it == NULL) + return NULL; // Failed. + + // Count the components to get a size for the arrays. + int component_count = 0; + int left, top, right, bottom; + + TessResultCallback* get_bbox = NULL; + if (raw_image) { + // Get bounding box in original raw image with padding. + get_bbox = NewPermanentTessCallback(page_it, &PageIterator::BoundingBox, + level, raw_padding, + &left, &top, &right, &bottom); + } + else { + // Get bounding box from binarized imaged. Note that this could be + // differently scaled from the original image. + get_bbox = NewPermanentTessCallback(page_it, + &PageIterator::BoundingBoxInternal, + level, &left, &top, &right, &bottom); + } + do { + if (get_bbox->Run() && + (!text_only || PTIsTextType(page_it->BlockType()))) + ++component_count; + } while (page_it->Next(level)); + + Boxa* boxa = boxaCreate(component_count); + if (pixa != NULL) + *pixa = pixaCreate(component_count); + if (blockids != NULL) + *blockids = new int[component_count]; + if (paraids != NULL) + *paraids = new int[component_count]; + + int blockid = 0; + int paraid = 0; + int component_index = 0; + page_it->Begin(); + do { + if (get_bbox->Run() && + (!text_only || PTIsTextType(page_it->BlockType()))) { + Box* lbox = boxCreate(left, top, right - left, bottom - top); + boxaAddBox(boxa, lbox, L_INSERT); + if (pixa != NULL) { + Pix* pix = NULL; + if (raw_image) { + pix = page_it->GetImage(level, raw_padding, GetInputImage(), &left, + &top); + } + else { + pix = page_it->GetBinaryImage(level); + } + pixaAddPix(*pixa, pix, L_INSERT); + pixaAddBox(*pixa, lbox, L_CLONE); + } + if (paraids != NULL) { + (*paraids)[component_index] = paraid; + if (page_it->IsAtFinalElement(RIL_PARA, level)) + ++paraid; + } + if (blockids != NULL) { + (*blockids)[component_index] = blockid; + if (page_it->IsAtFinalElement(RIL_BLOCK, level)) { + ++blockid; + paraid = 0; + } + } + ++component_index; + } + } while (page_it->Next(level)); + delete page_it; + delete get_bbox; + return boxa; + } + + int TessBaseAPI::GetThresholdedImageScaleFactor() const { + if (thresholder_ == NULL) { + return 0; + } + return thresholder_->GetScaleFactor(); + } + + /** Dump the internal binary image to a PGM file. */ + void TessBaseAPI::DumpPGM(const char* filename) { + if (tesseract_ == NULL) + return; + FILE *fp = fopen(filename, "wb"); + Pix* pix = tesseract_->pix_binary(); + int width = pixGetWidth(pix); + int height = pixGetHeight(pix); + l_uint32* data = pixGetData(pix); + fprintf(fp, "P5 %d %d 255\n", width, height); + for (int y = 0; y < height; ++y, data += pixGetWpl(pix)) { + for (int x = 0; x < width; ++x) { + uinT8 b = GET_DATA_BIT(data, x) ? 0 : 255; + fwrite(&b, 1, 1, fp); + } + } + fclose(fp); + } + +#ifndef NO_CUBE_BUILD + /** + * Placeholder for call to Cube and test that the input data is correct. + * reskew is the direction of baselines in the skewed image in + * normalized (cos theta, sin theta) form, so (0.866, 0.5) would represent + * a 30 degree anticlockwise skew. + */ + int CubeAPITest(Boxa* boxa_blocks, Pixa* pixa_blocks, + Boxa* boxa_words, Pixa* pixa_words, + const FCOORD& reskew, Pix* page_pix, + PAGE_RES* page_res) { + int block_count = boxaGetCount(boxa_blocks); + ASSERT_HOST(block_count == pixaGetCount(pixa_blocks)); + // Write each block to the current directory as junk_write_display.nnn.png. + for (int i = 0; i < block_count; ++i) { + Pix* pix = pixaGetPix(pixa_blocks, i, L_CLONE); + pixDisplayWrite(pix, 1); + } + int word_count = boxaGetCount(boxa_words); + ASSERT_HOST(word_count == pixaGetCount(pixa_words)); + int pr_word = 0; + PAGE_RES_IT page_res_it(page_res); + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward(), ++pr_word) { + WERD_RES *word = page_res_it.word(); + WERD_CHOICE* choice = word->best_choice; + // Write the first 100 words to files names wordims/.tif. + if (pr_word < 100) { + STRING filename("wordims/"); + if (choice != NULL) { + filename += choice->unichar_string(); + } + else { + char numbuf[32]; + filename += "unclassified"; + snprintf(numbuf, 32, "%03d", pr_word); + filename += numbuf; + } + filename += ".tif"; + Pix* pix = pixaGetPix(pixa_words, pr_word, L_CLONE); + pixWrite(filename.string(), pix, IFF_TIFF_G4); + } + } + ASSERT_HOST(pr_word == word_count); + return 0; + } +#endif // NO_CUBE_BUILD + + /** + * Runs page layout analysis in the mode set by SetPageSegMode. + * May optionally be called prior to Recognize to get access to just + * the page layout results. Returns an iterator to the results. + * If merge_similar_words is true, words are combined where suitable for use + * with a line recognizer. Use if you want to use AnalyseLayout to find the + * textlines, and then want to process textline fragments with an external + * line recognizer. + * Returns NULL on error or an empty page. + * The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + PageIterator* TessBaseAPI::AnalyseLayout() { return AnalyseLayout(false); } + + PageIterator* TessBaseAPI::AnalyseLayout(bool merge_similar_words) { + if (FindLines() == 0) { + if (block_list_->empty()) + return NULL; // The page was empty. + page_res_ = new PAGE_RES(merge_similar_words, block_list_, NULL); + DetectParagraphs(false); + return new PageIterator( + page_res_, tesseract_, thresholder_->GetScaleFactor(), + thresholder_->GetScaledYResolution(), + rect_left_, rect_top_, rect_width_, rect_height_); + } + return NULL; + } + + int TessBaseAPI::AnalyseLayout1() + { + return FindLines(); + } + + /** + * Recognize the tesseract global image and return the result as Tesseract + * internal structures. + */ + int TessBaseAPI::Recognize(ETEXT_DESC* monitor) { + if (tesseract_ == NULL) + return -1; + if (FindLines() != 0) + return -1; + delete page_res_; +#if 1 + if (block_list_->empty()) { + page_res_ = new PAGE_RES(false, block_list_, + &tesseract_->prev_word_best_choice_); + //return 0; // Empty page. + } +#endif + + tesseract_->SetBlackAndWhitelist(); + recognition_done_ = true; + if (tesseract_->tessedit_resegment_from_line_boxes) { + page_res_ = tesseract_->ApplyBoxes(*input_file_, true, block_list_); + } + else if (tesseract_->tessedit_resegment_from_boxes) { + page_res_ = tesseract_->ApplyBoxes(*input_file_, false, block_list_); + } + else { + // TODO(rays) LSTM here. + page_res_ = new PAGE_RES(false, + block_list_, &tesseract_->prev_word_best_choice_); + } + if (page_res_ == NULL) { + return -1; + } + if (tesseract_->tessedit_make_boxes_from_boxes) { + tesseract_->CorrectClassifyWords(page_res_); + return 0; + } + + if (truth_cb_ != NULL) { + tesseract_->wordrec_run_blamer.set_value(true); + PageIterator *page_it = new PageIterator( + page_res_, tesseract_, thresholder_->GetScaleFactor(), + thresholder_->GetScaledYResolution(), + rect_left_, rect_top_, rect_width_, rect_height_); + truth_cb_->Run(tesseract_->getDict().getUnicharset(), + image_height_, page_it, this->tesseract()->pix_grey()); + delete page_it; + } + + int result = 0; + if (tesseract_->interactive_display_mode) { +#ifndef GRAPHICS_DISABLED + tesseract_->pgeditor_main(rect_width_, rect_height_, page_res_); +#endif // GRAPHICS_DISABLED + // The page_res is invalid after an interactive session, so cleanup + // in a way that lets us continue to the next page without crashing. + delete page_res_; + page_res_ = NULL; + return -1; + } + else if (tesseract_->tessedit_train_from_boxes) { + STRING fontname; + ExtractFontName(*output_file_, &fontname); + tesseract_->ApplyBoxTraining(fontname, page_res_); + } + else if (tesseract_->tessedit_ambigs_training) { + FILE *training_output_file = tesseract_->init_recog_training(*input_file_); + // OCR the page segmented into words by tesseract. + tesseract_->recog_training_segmented( + *input_file_, page_res_, monitor, training_output_file); + fclose(training_output_file); + } + else { + // Now run the main recognition. + bool wait_for_text = true; + GetBoolVariable("paragraph_text_based", &wait_for_text); + if (!wait_for_text) DetectParagraphs(false); + if (tesseract_->recog_all_words(page_res_, monitor, NULL, NULL, 0)) { + if (wait_for_text) DetectParagraphs(true); + } + else { + result = -1; + } + } + return result; + } + + /** Tests the chopper by exhaustively running chop_one_blob. */ + int TessBaseAPI::RecognizeForChopTest(ETEXT_DESC* monitor) { + if (tesseract_ == NULL) + return -1; + if (thresholder_ == NULL || thresholder_->IsEmpty()) { + tprintf("Please call SetImage before attempting recognition."); + return -1; + } + if (page_res_ != NULL) + ClearResults(); + if (FindLines() != 0) + return -1; + // Additional conditions under which chopper test cannot be run + if (tesseract_->interactive_display_mode) return -1; + + recognition_done_ = true; + + page_res_ = new PAGE_RES(false, block_list_, + &(tesseract_->prev_word_best_choice_)); + + PAGE_RES_IT page_res_it(page_res_); + + while (page_res_it.word() != NULL) { + WERD_RES *word_res = page_res_it.word(); + GenericVector boxes; + tesseract_->MaximallyChopWord(boxes, page_res_it.block()->block, + page_res_it.row()->row, word_res); + page_res_it.forward(); + } + return 0; + } + + // Takes ownership of the input pix. + void TessBaseAPI::SetInputImage(Pix* pix) { tesseract_->set_pix_original(pix); } + + Pix* TessBaseAPI::GetInputImage() { return tesseract_->pix_original(); } + + const char * TessBaseAPI::GetInputName() { + if (input_file_) + return input_file_->c_str(); + return NULL; + } + + const char * TessBaseAPI::GetDatapath() { + return tesseract_->datadir.c_str(); + } + + int TessBaseAPI::GetSourceYResolution() { + return thresholder_->GetSourceYResolution(); + } + + // If flist exists, get data from there. Otherwise get data from buf. + // Seems convoluted, but is the easiest way I know of to meet multiple + // goals. Support streaming from stdin, and also work on platforms + // lacking fmemopen. + bool TessBaseAPI::ProcessPagesFileList(FILE *flist, + STRING *buf, + const char* retry_config, + int timeout_millisec, + TessResultRenderer* renderer, + int tessedit_page_number) { + if (!flist && !buf) return false; + int page = (tessedit_page_number >= 0) ? tessedit_page_number : 0; + char pagename[MAX_PATH]; + + GenericVector lines; + if (!flist) { + buf->split('\n', &lines); + if (lines.empty()) return false; + } + + // Skip to the requested page number. + for (int i = 0; i < page; i++) { + if (flist) { + if (fgets(pagename, sizeof(pagename), flist) == NULL) break; + } + } + + // Begin producing output + if (renderer && !renderer->BeginDocument(unknown_title_)) { + return false; + } + + // Loop over all pages - or just the requested one + while (true) { + if (flist) { + if (fgets(pagename, sizeof(pagename), flist) == NULL) break; + } + else { + if (page >= lines.size()) break; + snprintf(pagename, sizeof(pagename), "%s", lines[page].c_str()); + } + chomp_string(pagename); + Pix *pix = pixRead(pagename); + if (pix == NULL) { + tprintf("Image file %s cannot be read!\n", pagename); + return false; + } + tprintf("Page %d : %s\n", page, pagename); + bool r = ProcessPage(pix, page, pagename, retry_config, + timeout_millisec, renderer, nullptr, 0); + pixDestroy(&pix); + if (!r) return false; + if (tessedit_page_number >= 0) break; + ++page; + } + + // Finish producing output + if (renderer && !renderer->EndDocument()) { + return false; + } + return true; + } + + bool TessBaseAPI::ProcessPagesMultipageTiff(const l_uint8 *data, + size_t size, + const char* filename, + const char* retry_config, + int timeout_millisec, + TessResultRenderer* renderer, + int tessedit_page_number) { +#ifndef ANDROID_BUILD + Pix *pix = NULL; + int page = (tessedit_page_number >= 0) ? tessedit_page_number : 0; + size_t offset = 0; + for (; ; ++page) { + if (tessedit_page_number >= 0) + page = tessedit_page_number; + pix = (data) ? pixReadMemFromMultipageTiff(data, size, &offset) + : pixReadFromMultipageTiff(filename, &offset); + if (pix == NULL) break; + tprintf("Page %d\n", page + 1); + char page_str[kMaxIntSize]; + snprintf(page_str, kMaxIntSize - 1, "%d", page); + SetVariable("applybox_page", page_str); + bool r = ProcessPage(pix, page, filename, retry_config, + timeout_millisec, renderer, nullptr, 0); + pixDestroy(&pix); + if (!r) return false; + if (tessedit_page_number >= 0) break; + if (!offset) break; + } + return true; +#else + return false; +#endif + } + + // Master ProcessPages calls ProcessPagesInternal and then does any post- + // processing required due to being in a training mode. + bool TessBaseAPI::ProcessPages(const char* filename, const char* retry_config, + int timeout_millisec, + TessResultRenderer* renderer) { + bool result = + ProcessPagesInternal(filename, retry_config, timeout_millisec, renderer); + if (result) { + if (tesseract_->tessedit_train_from_boxes && + !tesseract_->WriteTRFile(*output_file_)) { + tprintf("Write of TR file failed: %s\n", output_file_->string()); + return false; + } + } + return result; + } + + // In the ideal scenario, Tesseract will start working on data as soon + // as it can. For example, if you steam a filelist through stdin, we + // should start the OCR process as soon as the first filename is + // available. This is particularly useful when hooking Tesseract up to + // slow hardware such as a book scanning machine. + // + // Unfortunately there are tradeoffs. You can't seek on stdin. That + // makes automatic detection of datatype (TIFF? filelist? PNG?) + // impractical. So we support a command line flag to explicitly + // identify the scenario that really matters: filelists on + // stdin. We'll still do our best if the user likes pipes. + bool TessBaseAPI::ProcessPagesInternal(const char* filename, + const char* retry_config, + int timeout_millisec, + TessResultRenderer* renderer) { + PERF_COUNT_START("ProcessPages") + bool stdInput = !strcmp(filename, "stdin") || !strcmp(filename, "-"); + if (stdInput) { +#ifdef WIN32 + if (_setmode(_fileno(stdin), _O_BINARY) == -1) + tprintf("ERROR: cin to binary: %s", strerror(errno)); +#endif // WIN32 + } + + if (stream_filelist) { + return ProcessPagesFileList(stdin, NULL, retry_config, + timeout_millisec, renderer, + tesseract_->tessedit_page_number); + } + + // At this point we are officially in autodection territory. + // That means any data in stdin must be buffered, to make it + // seekable. + std::string buf; + const l_uint8 *data = NULL; + if (stdInput) { + buf.assign((std::istreambuf_iterator(std::cin)), + (std::istreambuf_iterator())); + data = reinterpret_cast(buf.data()); + } + + // Here is our autodetection + int format; + int r = (stdInput) ? + findFileFormatBuffer(data, &format) : + findFileFormat(filename, &format); + + // Maybe we have a filelist + if (r != 0 || format == IFF_UNKNOWN) { + STRING s; + if (stdInput) { + s = buf.c_str(); + } + else { + std::ifstream t(filename); + std::string u((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + s = u.c_str(); + } + return ProcessPagesFileList(NULL, &s, retry_config, + timeout_millisec, renderer, + tesseract_->tessedit_page_number); + } + + // Maybe we have a TIFF which is potentially multipage + bool tiff = (format == IFF_TIFF || format == IFF_TIFF_PACKBITS || + format == IFF_TIFF_RLE || format == IFF_TIFF_G3 || + format == IFF_TIFF_G4 || format == IFF_TIFF_LZW || + format == IFF_TIFF_ZIP); + + // Fail early if we can, before producing any output + Pix *pix = NULL; + if (!tiff) { + pix = (stdInput) ? pixReadMem(data, buf.size()) : pixRead(filename); + if (pix == NULL) { + return false; + } + } + + // Begin the output + if (renderer && !renderer->BeginDocument(unknown_title_)) { + pixDestroy(&pix); + return false; + } + + // Produce output + r = (tiff) ? + ProcessPagesMultipageTiff(data, buf.size(), filename, retry_config, + timeout_millisec, renderer, + tesseract_->tessedit_page_number) : + ProcessPage(pix, 0, filename, retry_config, + timeout_millisec, renderer, nullptr, 0); + + // Clean up memory as needed + pixDestroy(&pix); + + // End the output + if (!r || (renderer && !renderer->EndDocument())) { + return false; + } + PERF_COUNT_END + return true; + } + + bool TessBaseAPI::ProcessPage(Pix* pix, int page_index, const char* filename, + const char* retry_config, int timeout_millisec, + TessResultRenderer* renderer, + const char* jpgdata, int len) { + PERF_COUNT_START("ProcessPage") + SetInputName(filename); + SetImage(pix); + bool failed = false; + + if (tesseract_->tessedit_pageseg_mode == PSM_AUTO_ONLY) { + // Disabled character recognition + PageIterator* it = AnalyseLayout(); + + if (it == NULL) { + failed = true; + } + else { + delete it; + } + } + else if (tesseract_->tessedit_pageseg_mode == PSM_OSD_ONLY) { + failed = FindLines() != 0; + } + else if (timeout_millisec > 0) { + // Running with a timeout. + ETEXT_DESC monitor; + monitor.cancel = NULL; + monitor.cancel_this = NULL; + monitor.set_deadline_msecs(timeout_millisec); + + // Now run the main recognition. + failed = Recognize(&monitor) < 0; + } + else { + // Normal layout and character recognition with no timeout. + failed = Recognize(NULL) < 0; + } + + if (tesseract_->tessedit_write_images) { +#ifndef ANDROID_BUILD + Pix* page_pix = GetThresholdedImage(); + pixWrite("tessinput.tif", page_pix, IFF_TIFF_G4); +#endif // ANDROID_BUILD + } + + if (failed && retry_config != NULL && retry_config[0] != '\0') { + // Save current config variables before switching modes. + FILE* fp = fopen(kOldVarsFile, "wb"); + PrintVariables(fp); + fclose(fp); + // Switch to alternate mode for retry. + ReadConfigFile(retry_config); + SetImage(pix); + Recognize(NULL); + // Restore saved config variables. + ReadConfigFile(kOldVarsFile); + } + + if (renderer && !failed) { + failed = !renderer->AddImage(this, jpgdata, len); + } + + PERF_COUNT_END + return !failed; + } + + /** + * Get a left-to-right iterator to the results of LayoutAnalysis and/or + * Recognize. The returned iterator must be deleted after use. + */ + LTRResultIterator* TessBaseAPI::GetLTRIterator() { + if (tesseract_ == NULL || page_res_ == NULL) + return NULL; + return new LTRResultIterator( + page_res_, tesseract_, + thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), + rect_left_, rect_top_, rect_width_, rect_height_); + } + + /** + * Get a reading-order iterator to the results of LayoutAnalysis and/or + * Recognize. The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + ResultIterator* TessBaseAPI::GetIterator() { + if (tesseract_ == NULL || page_res_ == NULL) + return NULL; + return ResultIterator::StartOfParagraph(LTRResultIterator( + page_res_, tesseract_, + thresholder_->GetScaleFactor(), thresholder_->GetScaledYResolution(), + rect_left_, rect_top_, rect_width_, rect_height_)); + } + + /** + * Get a mutable iterator to the results of LayoutAnalysis and/or Recognize. + * The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + MutableIterator* TessBaseAPI::GetMutableIterator() { + if (tesseract_ == NULL || page_res_ == NULL) + return NULL; + return new MutableIterator(page_res_, tesseract_, + thresholder_->GetScaleFactor(), + thresholder_->GetScaledYResolution(), + rect_left_, rect_top_, rect_width_, rect_height_); + } + + /** Make a text string from the internal data structures. */ + char* TessBaseAPI::GetUTF8Text() { + if (tesseract_ == NULL || + (!recognition_done_ && Recognize(NULL) < 0)) + return NULL; + STRING text(""); + ResultIterator *it = GetIterator(); + do { + if (it->Empty(RIL_PARA)) continue; + char *para_text = it->GetUTF8Text(RIL_PARA); + text += para_text; + delete[]para_text; + } while (it->Next(RIL_PARA)); + char* result = new char[text.length() + 1]; + strncpy(result, text.string(), text.length() + 1); + delete it; + return result; + } + + /** + * Gets the block orientation at the current iterator position. + */ + static tesseract::Orientation GetBlockTextOrientation(const PageIterator *it) { + tesseract::Orientation orientation; + tesseract::WritingDirection writing_direction; + tesseract::TextlineOrder textline_order; + float deskew_angle; + it->Orientation(&orientation, &writing_direction, &textline_order, + &deskew_angle); + return orientation; + } + + /** + * Fits a line to the baseline at the given level, and appends its coefficients + * to the hOCR string. + * NOTE: The hOCR spec is unclear on how to specify baseline coefficients for + * rotated textlines. For this reason, on textlines that are not upright, this + * method currently only inserts a 'textangle' property to indicate the rotation + * direction and does not add any baseline information to the hocr string. + */ + static void AddBaselineCoordsTohOCR(const PageIterator *it, + PageIteratorLevel level, + STRING* hocr_str) { + tesseract::Orientation orientation = GetBlockTextOrientation(it); + if (orientation != ORIENTATION_PAGE_UP) { + hocr_str->add_str_int("; textangle ", 360 - orientation * 90); + return; + } + + int left, top, right, bottom; + it->BoundingBox(level, &left, &top, &right, &bottom); + + // Try to get the baseline coordinates at this level. + int x1, y1, x2, y2; + if (!it->Baseline(level, &x1, &y1, &x2, &y2)) + return; + // Following the description of this field of the hOCR spec, we convert the + // baseline coordinates so that "the bottom left of the bounding box is the + // origin". + x1 -= left; + x2 -= left; + y1 -= bottom; + y2 -= bottom; + + // Now fit a line through the points so we can extract coefficients for the + // equation: y = p1 x + p0 + double p1 = 0; + double p0 = 0; + if (x1 == x2) { + // Problem computing the polynomial coefficients. + return; + } + p1 = (y2 - y1) / static_cast(x2 - x1); + p0 = y1 - static_cast(p1 * x1); + + hocr_str->add_str_double("; baseline ", round(p1 * 1000.0) / 1000.0); + hocr_str->add_str_double(" ", round(p0 * 1000.0) / 1000.0); + } + + static void AddIdTohOCR(STRING* hocr_str, const std::string base, int num1, + int num2) { + const size_t BUFSIZE = 64; + char id_buffer[BUFSIZE]; + if (num2 >= 0) { + snprintf(id_buffer, BUFSIZE - 1, "%s_%d_%d", base.c_str(), num1, num2); + } + else { + snprintf(id_buffer, BUFSIZE - 1, "%s_%d", base.c_str(), num1); + } + id_buffer[BUFSIZE - 1] = '\0'; + *hocr_str += " id='"; + *hocr_str += id_buffer; + *hocr_str += "'"; + } + + static void AddBoxTohOCR(const ResultIterator* it, PageIteratorLevel level, + STRING* hocr_str) { + int left, top, right, bottom; + it->BoundingBox(level, &left, &top, &right, &bottom); + // This is the only place we use double quotes instead of single quotes, + // but it may too late to change for consistency + hocr_str->add_str_int(" title=\"bbox ", left); + hocr_str->add_str_int(" ", top); + hocr_str->add_str_int(" ", right); + hocr_str->add_str_int(" ", bottom); + // Add baseline coordinates & heights for textlines only. + if (level == RIL_TEXTLINE) { + AddBaselineCoordsTohOCR(it, level, hocr_str); + // add custom height measures + float row_height, descenders, ascenders; // row attributes + it->RowAttributes(&row_height, &descenders, &ascenders); + // TODO(rays): Do we want to limit these to a single decimal place? + hocr_str->add_str_double("; x_size ", row_height); + hocr_str->add_str_double("; x_descenders ", descenders * -1); + hocr_str->add_str_double("; x_ascenders ", ascenders); + } + *hocr_str += "\">"; + } + + static void AddBoxToTSV(const PageIterator* it, PageIteratorLevel level, + STRING* hocr_str) { + int left, top, right, bottom; + it->BoundingBox(level, &left, &top, &right, &bottom); + hocr_str->add_str_int("\t", left); + hocr_str->add_str_int("\t", top); + hocr_str->add_str_int("\t", right - left); + hocr_str->add_str_int("\t", bottom - top); + } + + /** + * Make a HTML-formatted string with hOCR markup from the internal + * data structures. + * page_number is 0-based but will appear in the output as 1-based. + * Image name/input_file_ can be set by SetInputName before calling + * GetHOCRText + * STL removed from original patch submission and refactored by rays. + */ + char* TessBaseAPI::GetHOCRText(int page_number) { + return GetHOCRText(NULL, page_number); + } + + /** + * Make a HTML-formatted string with hOCR markup from the internal + * data structures. + * page_number is 0-based but will appear in the output as 1-based. + * Image name/input_file_ can be set by SetInputName before calling + * GetHOCRText + * STL removed from original patch submission and refactored by rays. + */ + char* TessBaseAPI::GetHOCRText(ETEXT_DESC* monitor, int page_number) { + if (tesseract_ == NULL || (page_res_ == NULL && Recognize(monitor) < 0)) + return NULL; + + int lcnt = 1, bcnt = 1, pcnt = 1, wcnt = 1; + int page_id = page_number + 1; // hOCR uses 1-based page numbers. + bool para_is_ltr = true; // Default direction is LTR + const char* paragraph_lang = NULL; + bool font_info = false; + GetBoolVariable("hocr_font_info", &font_info); + + STRING hocr_str(""); + + if (input_file_ == NULL) + SetInputName(NULL); + +#ifdef _WIN32 + // convert input name from ANSI encoding to utf-8 + int str16_len = + MultiByteToWideChar(CP_ACP, 0, input_file_->string(), -1, NULL, 0); + wchar_t *uni16_str = new WCHAR[str16_len]; + str16_len = MultiByteToWideChar(CP_ACP, 0, input_file_->string(), -1, + uni16_str, str16_len); + int utf8_len = WideCharToMultiByte(CP_UTF8, 0, uni16_str, str16_len, NULL, 0, + NULL, NULL); + char *utf8_str = new char[utf8_len]; + WideCharToMultiByte(CP_UTF8, 0, uni16_str, str16_len, utf8_str, + utf8_len, NULL, NULL); + *input_file_ = utf8_str; + delete[] uni16_str; + delete[] utf8_str; +#endif + + hocr_str += "
string()); + } + else { + hocr_str += "unknown"; + } + hocr_str.add_str_int("\"; bbox ", rect_left_); + hocr_str.add_str_int(" ", rect_top_); + hocr_str.add_str_int(" ", rect_width_); + hocr_str.add_str_int(" ", rect_height_); + hocr_str.add_str_int("; ppageno ", page_number); + hocr_str += "'>\n"; + + ResultIterator *res_it = GetIterator(); + while (!res_it->Empty(RIL_BLOCK)) { + if (res_it->Empty(RIL_WORD)) { + res_it->Next(RIL_WORD); + continue; + } + + // Open any new block/paragraph/textline. + if (res_it->IsAtBeginningOf(RIL_BLOCK)) { + para_is_ltr = true; // reset to default direction + hocr_str += "
IsAtBeginningOf(RIL_PARA)) { + hocr_str += "\n

ParagraphIsLtr(); + if (!para_is_ltr) { + hocr_str += " dir='rtl'"; + } + AddIdTohOCR(&hocr_str, "par", page_id, pcnt); + paragraph_lang = res_it->WordRecognitionLanguage(); + if (paragraph_lang) { + hocr_str += " lang='"; + hocr_str += paragraph_lang; + hocr_str += "'"; + } + AddBoxTohOCR(res_it, RIL_PARA, &hocr_str); + } + if (res_it->IsAtBeginningOf(RIL_TEXTLINE)) { + hocr_str += "\n BoundingBox(RIL_WORD, &left, &top, &right, &bottom); + font_name = res_it->WordFontAttributes(&bold, &italic, &underlined, + &monospace, &serif, &smallcaps, + &pointsize, &font_id); + hocr_str.add_str_int(" title='bbox ", left); + hocr_str.add_str_int(" ", top); + hocr_str.add_str_int(" ", right); + hocr_str.add_str_int(" ", bottom); + hocr_str.add_str_int("; x_wconf ", res_it->Confidence(RIL_WORD)); + if (font_info) { + if (font_name) { + hocr_str += "; x_font "; + hocr_str += HOcrEscape(font_name); + } + hocr_str.add_str_int("; x_fsize ", pointsize); + } + hocr_str += "'"; + const char* lang = res_it->WordRecognitionLanguage(); + if (lang && (!paragraph_lang || strcmp(lang, paragraph_lang))) { + hocr_str += " lang='"; + hocr_str += lang; + hocr_str += "'"; + } + switch (res_it->WordDirection()) { + // Only emit direction if different from current paragraph direction + case DIR_LEFT_TO_RIGHT: + if (!para_is_ltr) hocr_str += " dir='ltr'"; + break; + case DIR_RIGHT_TO_LEFT: + if (para_is_ltr) hocr_str += " dir='rtl'"; + break; + case DIR_MIX: + case DIR_NEUTRAL: + default: // Do nothing. + break; + } + hocr_str += ">"; + bool last_word_in_line = res_it->IsAtFinalElement(RIL_TEXTLINE, RIL_WORD); + bool last_word_in_para = res_it->IsAtFinalElement(RIL_PARA, RIL_WORD); + bool last_word_in_block = res_it->IsAtFinalElement(RIL_BLOCK, RIL_WORD); + if (bold) hocr_str += ""; + if (italic) hocr_str += ""; + do { + const char *grapheme = res_it->GetUTF8Text(RIL_SYMBOL); + if (grapheme && grapheme[0] != 0) { + hocr_str += HOcrEscape(grapheme); + } + delete[]grapheme; + res_it->Next(RIL_SYMBOL); + } while (!res_it->Empty(RIL_BLOCK) && !res_it->IsAtBeginningOf(RIL_WORD)); + if (italic) hocr_str += ""; + if (bold) hocr_str += ""; + hocr_str += " "; + wcnt++; + // Close any ending block/paragraph/textline. + if (last_word_in_line) { + hocr_str += "\n "; + lcnt++; + } + if (last_word_in_para) { + hocr_str += "\n

\n"; + pcnt++; + para_is_ltr = true; // back to default direction + } + if (last_word_in_block) { + hocr_str += "
\n"; + bcnt++; + } + } + hocr_str += "
\n"; + + char *ret = new char[hocr_str.length() + 1]; + strcpy(ret, hocr_str.string()); + delete res_it; + return ret; + } + + /** + * Make a TSV-formatted string from the internal data structures. + * page_number is 0-based but will appear in the output as 1-based. + */ + char* TessBaseAPI::GetTSVText(int page_number) { + if (tesseract_ == NULL || (page_res_ == NULL && Recognize(NULL) < 0)) + return NULL; + + int lcnt = 1, bcnt = 1, pcnt = 1, wcnt = 1; + int page_id = page_number + 1; // we use 1-based page numbers. + + STRING tsv_str(""); + + int page_num = page_id, block_num = 0, par_num = 0, line_num = 0, + word_num = 0; + + tsv_str.add_str_int("1\t", page_num); // level 1 - page + tsv_str.add_str_int("\t", block_num); + tsv_str.add_str_int("\t", par_num); + tsv_str.add_str_int("\t", line_num); + tsv_str.add_str_int("\t", word_num); + tsv_str.add_str_int("\t", rect_left_); + tsv_str.add_str_int("\t", rect_top_); + tsv_str.add_str_int("\t", rect_width_); + tsv_str.add_str_int("\t", rect_height_); + tsv_str += "\t-1\t\n"; + + ResultIterator* res_it = GetIterator(); + while (!res_it->Empty(RIL_BLOCK)) { + if (res_it->Empty(RIL_WORD)) { + res_it->Next(RIL_WORD); + continue; + } + + // Add rows for any new block/paragraph/textline. + if (res_it->IsAtBeginningOf(RIL_BLOCK)) { + block_num++, par_num = 0, line_num = 0, word_num = 0; + tsv_str.add_str_int("2\t", page_num); // level 2 - block + tsv_str.add_str_int("\t", block_num); + tsv_str.add_str_int("\t", par_num); + tsv_str.add_str_int("\t", line_num); + tsv_str.add_str_int("\t", word_num); + AddBoxToTSV(res_it, RIL_BLOCK, &tsv_str); + tsv_str += "\t-1\t\n"; // end of row for block + } + if (res_it->IsAtBeginningOf(RIL_PARA)) { + par_num++, line_num = 0, word_num = 0; + tsv_str.add_str_int("3\t", page_num); // level 3 - paragraph + tsv_str.add_str_int("\t", block_num); + tsv_str.add_str_int("\t", par_num); + tsv_str.add_str_int("\t", line_num); + tsv_str.add_str_int("\t", word_num); + AddBoxToTSV(res_it, RIL_PARA, &tsv_str); + tsv_str += "\t-1\t\n"; // end of row for para + } + if (res_it->IsAtBeginningOf(RIL_TEXTLINE)) { + line_num++, word_num = 0; + tsv_str.add_str_int("4\t", page_num); // level 4 - line + tsv_str.add_str_int("\t", block_num); + tsv_str.add_str_int("\t", par_num); + tsv_str.add_str_int("\t", line_num); + tsv_str.add_str_int("\t", word_num); + AddBoxToTSV(res_it, RIL_TEXTLINE, &tsv_str); + tsv_str += "\t-1\t\n"; // end of row for line + } + + // Now, process the word... + int left, top, right, bottom; + bool bold, italic, underlined, monospace, serif, smallcaps; + int pointsize, font_id; + const char* font_name; + res_it->BoundingBox(RIL_WORD, &left, &top, &right, &bottom); + font_name = + res_it->WordFontAttributes(&bold, &italic, &underlined, &monospace, + &serif, &smallcaps, &pointsize, &font_id); + word_num++; + tsv_str.add_str_int("5\t", page_num); // level 5 - word + tsv_str.add_str_int("\t", block_num); + tsv_str.add_str_int("\t", par_num); + tsv_str.add_str_int("\t", line_num); + tsv_str.add_str_int("\t", word_num); + tsv_str.add_str_int("\t", left); + tsv_str.add_str_int("\t", top); + tsv_str.add_str_int("\t", right - left); + tsv_str.add_str_int("\t", bottom - top); + tsv_str.add_str_int("\t", res_it->Confidence(RIL_WORD)); + tsv_str += "\t"; + + // Increment counts if at end of block/paragraph/textline. + if (res_it->IsAtFinalElement(RIL_TEXTLINE, RIL_WORD)) lcnt++; + if (res_it->IsAtFinalElement(RIL_PARA, RIL_WORD)) pcnt++; + if (res_it->IsAtFinalElement(RIL_BLOCK, RIL_WORD)) bcnt++; + + do { + tsv_str += res_it->GetUTF8Text(RIL_SYMBOL); + res_it->Next(RIL_SYMBOL); + } while (!res_it->Empty(RIL_BLOCK) && !res_it->IsAtBeginningOf(RIL_WORD)); + tsv_str += "\n"; // end of row + wcnt++; + } + + char* ret = new char[tsv_str.length() + 1]; + strcpy(ret, tsv_str.string()); + delete res_it; + return ret; + } + + /** The 5 numbers output for each box (the usual 4 and a page number.) */ + const int kNumbersPerBlob = 5; + /** + * The number of bytes taken by each number. Since we use inT16 for ICOORD, + * assume only 5 digits max. + */ + const int kBytesPerNumber = 5; + /** + * Multiplier for max expected textlength assumes (kBytesPerNumber + space) + * * kNumbersPerBlob plus the newline. Add to this the + * original UTF8 characters, and one kMaxBytesPerLine for safety. + */ + const int kBytesPerBoxFileLine = (kBytesPerNumber + 1) * kNumbersPerBlob + 1; + /** Max bytes in the decimal representation of inT64. */ + const int kBytesPer64BitNumber = 20; + /** + * A maximal single box could occupy kNumbersPerBlob numbers at + * kBytesPer64BitNumber digits (if someone sneaks in a 64 bit value) and a + * space plus the newline and the maximum length of a UNICHAR. + * Test against this on each iteration for safety. + */ + const int kMaxBytesPerLine = kNumbersPerBlob * (kBytesPer64BitNumber + 1) + 1 + + UNICHAR_LEN; + + /** + * The recognized text is returned as a char* which is coded + * as a UTF8 box file and must be freed with the delete [] operator. + * page_number is a 0-base page index that will appear in the box file. + */ + char* TessBaseAPI::GetBoxText(int page_number) { + if (tesseract_ == NULL || + (!recognition_done_ && Recognize(NULL) < 0)) + return NULL; + int blob_count; + int utf8_length = TextLength(&blob_count); + int total_length = blob_count * kBytesPerBoxFileLine + utf8_length + + kMaxBytesPerLine; + char* result = new char[total_length]; + result[0] = '\0'; + int output_length = 0; + LTRResultIterator* it = GetLTRIterator(); + do { + int left, top, right, bottom; + if (it->BoundingBox(RIL_SYMBOL, &left, &top, &right, &bottom)) { + char* text = it->GetUTF8Text(RIL_SYMBOL); + // Tesseract uses space for recognition failure. Fix to a reject + // character, kTesseractReject so we don't create illegal box files. + for (int i = 0; text[i] != '\0'; ++i) { + if (text[i] == ' ') + text[i] = kTesseractReject; + } + snprintf(result + output_length, total_length - output_length, + "%s %d %d %d %d %d\n", + text, left, image_height_ - bottom, + right, image_height_ - top, page_number); + output_length += strlen(result + output_length); + delete[] text; + // Just in case... + if (output_length + kMaxBytesPerLine > total_length) + break; + } + } while (it->Next(RIL_SYMBOL)); + delete it; + return result; + } + + /** + * Conversion table for non-latin characters. + * Maps characters out of the latin set into the latin set. + * TODO(rays) incorporate this translation into unicharset. + */ + const int kUniChs[] = { + 0x20ac, 0x201c, 0x201d, 0x2018, 0x2019, 0x2022, 0x2014, 0 + }; + /** Latin chars corresponding to the unicode chars above. */ + const int kLatinChs[] = { + 0x00a2, 0x0022, 0x0022, 0x0027, 0x0027, 0x00b7, 0x002d, 0 + }; + + /** + * The recognized text is returned as a char* which is coded + * as UNLV format Latin-1 with specific reject and suspect codes + * and must be freed with the delete [] operator. + */ + char* TessBaseAPI::GetUNLVText() { + if (tesseract_ == NULL || + (!recognition_done_ && Recognize(NULL) < 0)) + return NULL; + bool tilde_crunch_written = false; + bool last_char_was_newline = true; + bool last_char_was_tilde = false; + + int total_length = TextLength(NULL); + PAGE_RES_IT page_res_it(page_res_); + char* result = new char[total_length]; + char* ptr = result; + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD_RES *word = page_res_it.word(); + // Process the current word. + if (word->unlv_crunch_mode != CR_NONE) { + if (word->unlv_crunch_mode != CR_DELETE && + (!tilde_crunch_written || + (word->unlv_crunch_mode == CR_KEEP_SPACE && + word->word->space() > 0 && + !word->word->flag(W_FUZZY_NON) && + !word->word->flag(W_FUZZY_SP)))) { + if (!word->word->flag(W_BOL) && + word->word->space() > 0 && + !word->word->flag(W_FUZZY_NON) && + !word->word->flag(W_FUZZY_SP)) { + /* Write a space to separate from preceding good text */ + *ptr++ = ' '; + last_char_was_tilde = false; + } + if (!last_char_was_tilde) { + // Write a reject char. + last_char_was_tilde = true; + *ptr++ = kUNLVReject; + tilde_crunch_written = true; + last_char_was_newline = false; + } + } + } + else { + // NORMAL PROCESSING of non tilde crunched words. + tilde_crunch_written = false; + tesseract_->set_unlv_suspects(word); + const char* wordstr = word->best_choice->unichar_string().string(); + const STRING& lengths = word->best_choice->unichar_lengths(); + int length = lengths.length(); + int i = 0; + int offset = 0; + + if (last_char_was_tilde && + word->word->space() == 0 && wordstr[offset] == ' ') { + // Prevent adjacent tilde across words - we know that adjacent tildes + // within words have been removed. + // Skip the first character. + offset = lengths[i++]; + } + if (i < length && wordstr[offset] != 0) { + if (!last_char_was_newline) + *ptr++ = ' '; + else + last_char_was_newline = false; + for (; i < length; offset += lengths[i++]) { + if (wordstr[offset] == ' ' || + wordstr[offset] == kTesseractReject) { + *ptr++ = kUNLVReject; + last_char_was_tilde = true; + } + else { + if (word->reject_map[i].rejected()) + *ptr++ = kUNLVSuspect; + UNICHAR ch(wordstr + offset, lengths[i]); + int uni_ch = ch.first_uni(); + for (int j = 0; kUniChs[j] != 0; ++j) { + if (kUniChs[j] == uni_ch) { + uni_ch = kLatinChs[j]; + break; + } + } + if (uni_ch <= 0xff) { + *ptr++ = static_cast(uni_ch); + last_char_was_tilde = false; + } + else { + *ptr++ = kUNLVReject; + last_char_was_tilde = true; + } + } + } + } + } + if (word->word->flag(W_EOL) && !last_char_was_newline) { + /* Add a new line output */ + *ptr++ = '\n'; + tilde_crunch_written = false; + last_char_was_newline = true; + last_char_was_tilde = false; + } + } + *ptr++ = '\n'; + *ptr = '\0'; + return result; + } + + /** + * Detect the orientation of the input image and apparent script (alphabet). + * orient_deg is the detected clockwise rotation of the input image in degrees (0, 90, 180, 270) + * orient_conf is the confidence (15.0 is reasonably confident) + * script_name is an ASCII string, the name of the script, e.g. "Latin" + * script_conf is confidence level in the script + * Returns true on success and writes values to each parameter as an output + */ + bool TessBaseAPI::DetectOrientationScript(int* orient_deg, float* orient_conf, const char** script_name, float* script_conf) { + OSResults osr; + + bool osd = DetectOS(&osr); + if (!osd) { + return false; + } + + int orient_id = osr.best_result.orientation_id; + int script_id = osr.get_best_script(orient_id); + if (orient_conf) + *orient_conf = osr.best_result.oconfidence; + if (orient_deg) + *orient_deg = orient_id * 90; // convert quadrant to degrees + + if (script_name) { + const char* script = + osr.unicharset->get_script_from_script_id(script_id); + + *script_name = script; + } + + if (script_conf) + *script_conf = osr.best_result.sconfidence; + + return true; + } + + /** + * The recognized text is returned as a char* which is coded + * as UTF8 and must be freed with the delete [] operator. + * page_number is a 0-based page index that will appear in the osd file. + */ + char* TessBaseAPI::GetOsdText(int page_number) { + int orient_deg; + float orient_conf; + const char* script_name; + float script_conf; + + if (!DetectOrientationScript(&orient_deg, &orient_conf, &script_name, &script_conf)) + return NULL; + + // clockwise rotation needed to make the page upright + int rotate = OrientationIdToValue(orient_deg / 90); + + const int kOsdBufsize = 255; + char* osd_buf = new char[kOsdBufsize]; + snprintf(osd_buf, kOsdBufsize, + "Page number: %d\n" + "Orientation in degrees: %d\n" + "Rotate: %d\n" + "Orientation confidence: %.2f\n" + "Script: %s\n" + "Script confidence: %.2f\n", + page_number, orient_deg, rotate, orient_conf, script_name, + script_conf); + + return osd_buf; + } + + /** Returns the average word confidence for Tesseract page result. */ + int TessBaseAPI::MeanTextConf() { + int* conf = AllWordConfidences(); + if (!conf) return 0; + int sum = 0; + int *pt = conf; + while (*pt >= 0) sum += *pt++; + if (pt != conf) sum /= pt - conf; + delete[] conf; + return sum; + } + + /** Returns an array of all word confidences, terminated by -1. */ + int* TessBaseAPI::AllWordConfidences() { + if (tesseract_ == NULL || + (!recognition_done_ && Recognize(NULL) < 0)) + return NULL; + int n_word = 0; + PAGE_RES_IT res_it(page_res_); + for (res_it.restart_page(); res_it.word() != NULL; res_it.forward()) + n_word++; + + int* conf = new int[n_word + 1]; + n_word = 0; + for (res_it.restart_page(); res_it.word() != NULL; res_it.forward()) { + WERD_RES *word = res_it.word(); + WERD_CHOICE* choice = word->best_choice; + int w_conf = static_cast(100 + 5 * choice->certainty()); + // This is the eq for converting Tesseract confidence to 1..100 + if (w_conf < 0) w_conf = 0; + if (w_conf > 100) w_conf = 100; + conf[n_word++] = w_conf; + } + conf[n_word] = -1; + return conf; + } + + /** + * Applies the given word to the adaptive classifier if possible. + * The word must be SPACE-DELIMITED UTF-8 - l i k e t h i s , so it can + * tell the boundaries of the graphemes. + * Assumes that SetImage/SetRectangle have been used to set the image + * to the given word. The mode arg should be PSM_SINGLE_WORD or + * PSM_CIRCLE_WORD, as that will be used to control layout analysis. + * The currently set PageSegMode is preserved. + * Returns false if adaption was not possible for some reason. + */ + bool TessBaseAPI::AdaptToWordStr(PageSegMode mode, const char* wordstr) { + int debug = 0; + GetIntVariable("applybox_debug", &debug); + bool success = true; + PageSegMode current_psm = GetPageSegMode(); + SetPageSegMode(mode); + SetVariable("classify_enable_learning", "0"); + char* text = GetUTF8Text(); + if (debug) { + tprintf("Trying to adapt \"%s\" to \"%s\"\n", text, wordstr); + } + if (text != NULL) { + PAGE_RES_IT it(page_res_); + WERD_RES* word_res = it.word(); + if (word_res != NULL) { + word_res->word->set_text(wordstr); + } + else { + success = false; + } + // Check to see if text matches wordstr. + int w = 0; + int t = 0; + for (t = 0; text[t] != '\0'; ++t) { + if (text[t] == '\n' || text[t] == ' ') + continue; + while (wordstr[w] != '\0' && wordstr[w] == ' ') + ++w; + if (text[t] != wordstr[w]) + break; + ++w; + } + if (text[t] != '\0' || wordstr[w] != '\0') { + // No match. + delete page_res_; + GenericVector boxes; + page_res_ = tesseract_->SetupApplyBoxes(boxes, block_list_); + tesseract_->ReSegmentByClassification(page_res_); + tesseract_->TidyUp(page_res_); + PAGE_RES_IT pr_it(page_res_); + if (pr_it.word() == NULL) + success = false; + else + word_res = pr_it.word(); + } + else { + word_res->BestChoiceToCorrectText(); + } + if (success) { + tesseract_->EnableLearning = true; + tesseract_->LearnWord(NULL, word_res); + } + delete[] text; + } + else { + success = false; + } + SetPageSegMode(current_psm); + return success; + } + + /** + * Free up recognition results and any stored image data, without actually + * freeing any recognition data that would be time-consuming to reload. + * Afterwards, you must call SetImage or TesseractRect before doing + * any Recognize or Get* operation. + */ + void TessBaseAPI::Clear() { + if (thresholder_ != NULL) + thresholder_->Clear(); + ClearResults(); + if (tesseract_ != NULL) SetInputImage(NULL); + } + + /** + * Close down tesseract and free up all memory. End() is equivalent to + * destructing and reconstructing your TessBaseAPI. + * Once End() has been used, none of the other API functions may be used + * other than Init and anything declared above it in the class definition. + */ + void TessBaseAPI::End() { + Clear(); + delete thresholder_; + thresholder_ = NULL; + delete page_res_; + page_res_ = NULL; + delete block_list_; + block_list_ = NULL; + if (paragraph_models_ != NULL) { + paragraph_models_->delete_data_pointers(); + delete paragraph_models_; + paragraph_models_ = NULL; + } + if (osd_tesseract_ == tesseract_) + osd_tesseract_ = NULL; + delete tesseract_; + tesseract_ = NULL; + delete osd_tesseract_; + osd_tesseract_ = NULL; + delete equ_detect_; + equ_detect_ = NULL; + delete input_file_; + input_file_ = NULL; + delete output_file_; + output_file_ = NULL; + delete datapath_; + datapath_ = NULL; + delete language_; + language_ = NULL; + } + + // Clear any library-level memory caches. + // There are a variety of expensive-to-load constant data structures (mostly + // language dictionaries) that are cached globally -- surviving the Init() + // and End() of individual TessBaseAPI's. This function allows the clearing + // of these caches. + void TessBaseAPI::ClearPersistentCache() { + Dict::GlobalDawgCache()->DeleteUnusedDawgs(); + } + + /** + * Check whether a word is valid according to Tesseract's language model + * returns 0 if the word is invalid, non-zero if valid + */ + int TessBaseAPI::IsValidWord(const char *word) { + return tesseract_->getDict().valid_word(word); + } + // Returns true if utf8_character is defined in the UniCharset. + bool TessBaseAPI::IsValidCharacter(const char *utf8_character) { + return tesseract_->unicharset.contains_unichar(utf8_character); + } + + + // TODO(rays) Obsolete this function and replace with a more aptly named + // function that returns image coordinates rather than tesseract coordinates. + bool TessBaseAPI::GetTextDirection(int* out_offset, float* out_slope) { + PageIterator* it = AnalyseLayout(); + if (it == NULL) { + return false; + } + int x1, x2, y1, y2; + it->Baseline(RIL_TEXTLINE, &x1, &y1, &x2, &y2); + // Calculate offset and slope (NOTE: Kind of ugly) + if (x2 <= x1) x2 = x1 + 1; + // Convert the point pair to slope/offset of the baseline (in image coords.) + *out_slope = static_cast(y2 - y1) / (x2 - x1); + *out_offset = static_cast(y1 - *out_slope * x1); + // Get the y-coord of the baseline at the left and right edges of the + // textline's bounding box. + int left, top, right, bottom; + if (!it->BoundingBox(RIL_TEXTLINE, &left, &top, &right, &bottom)) { + delete it; + return false; + } + int left_y = IntCastRounded(*out_slope * left + *out_offset); + int right_y = IntCastRounded(*out_slope * right + *out_offset); + // Shift the baseline down so it passes through the nearest bottom-corner + // of the textline's bounding box. This is the difference between the y + // at the lowest (max) edge of the box and the actual box bottom. + *out_offset += bottom - MAX(left_y, right_y); + // Switch back to bottom-up tesseract coordinates. Requires negation of + // the slope and height - offset for the offset. + *out_slope = -*out_slope; + *out_offset = rect_height_ - *out_offset; + delete it; + + return true; + } + + /** Sets Dict::letter_is_okay_ function to point to the given function. */ + void TessBaseAPI::SetDictFunc(DictFunc f) { + if (tesseract_ != NULL) { + tesseract_->getDict().letter_is_okay_ = f; + } + } + + /** + * Sets Dict::probability_in_context_ function to point to the given + * function. + * + * @param f A single function that returns the probability of the current + * "character" (in general a utf-8 string), given the context of a previous + * utf-8 string. + */ + void TessBaseAPI::SetProbabilityInContextFunc(ProbabilityInContextFunc f) { + if (tesseract_ != NULL) { + tesseract_->getDict().probability_in_context_ = f; + // Set it for the sublangs too. + int num_subs = tesseract_->num_sub_langs(); + for (int i = 0; i < num_subs; ++i) { + tesseract_->get_sub_lang(i)->getDict().probability_in_context_ = f; + } + } + } + + /** Sets Wordrec::fill_lattice_ function to point to the given function. */ + void TessBaseAPI::SetFillLatticeFunc(FillLatticeFunc f) { + if (tesseract_ != NULL) tesseract_->fill_lattice_ = f; + } + + /** Common code for setting the image. */ + bool TessBaseAPI::InternalSetImage() { + if (tesseract_ == NULL) { + tprintf("Please call Init before attempting to set an image."); + return false; + } + if (thresholder_ == NULL) + thresholder_ = new ImageThresholder; + ClearResults(); + return true; + } + + /** + * Run the thresholder to make the thresholded image, returned in pix, + * which must not be NULL. *pix must be initialized to NULL, or point + * to an existing pixDestroyable Pix. + * The usual argument to Threshold is Tesseract::mutable_pix_binary(). + */ + void TessBaseAPI::Threshold(Pix** pix) { + ASSERT_HOST(pix != NULL); + if (*pix != NULL) + pixDestroy(pix); + // Zero resolution messes up the algorithms, so make sure it is credible. + int y_res = thresholder_->GetScaledYResolution(); + if (y_res < kMinCredibleResolution || y_res > kMaxCredibleResolution) { + // Use the minimum default resolution, as it is safer to under-estimate + // than over-estimate resolution. + tprintf("Warning. Invalid resolution %d dpi. Using %d instead.\n", + y_res, kMinCredibleResolution); + thresholder_->SetSourceYResolution(kMinCredibleResolution); + } + PageSegMode pageseg_mode = + static_cast( + static_cast(tesseract_->tessedit_pageseg_mode)); + thresholder_->ThresholdToPix(pageseg_mode, pix); + thresholder_->GetImageSizes(&rect_left_, &rect_top_, + &rect_width_, &rect_height_, + &image_width_, &image_height_); + if (!thresholder_->IsBinary()) { + tesseract_->set_pix_thresholds(thresholder_->GetPixRectThresholds()); + tesseract_->set_pix_grey(thresholder_->GetPixRectGrey()); + } + else { + tesseract_->set_pix_thresholds(NULL); + tesseract_->set_pix_grey(NULL); + } + // Set the internal resolution that is used for layout parameters from the + // estimated resolution, rather than the image resolution, which may be + // fabricated, but we will use the image resolution, if there is one, to + // report output point sizes. + int estimated_res = ClipToRange(thresholder_->GetScaledEstimatedResolution(), + kMinCredibleResolution, + kMaxCredibleResolution); + if (estimated_res != thresholder_->GetScaledEstimatedResolution()) { + tprintf("Estimated resolution %d out of range! Corrected to %d\n", + thresholder_->GetScaledEstimatedResolution(), estimated_res); + } + tesseract_->set_source_resolution(estimated_res); + SavePixForCrash(estimated_res, *pix); + } + + /** Find lines from the image making the BLOCK_LIST. */ + int TessBaseAPI::FindLines() { + if (thresholder_ == NULL || thresholder_->IsEmpty()) { + tprintf("Please call SetImage before attempting recognition."); + return -1; + } + if (recognition_done_) + ClearResults(); + if (!block_list_->empty()) { + return 0; + } + if (tesseract_ == NULL) { + tesseract_ = new Tesseract; + tesseract_->InitAdaptiveClassifier(false); + } + if (tesseract_->pix_binary() == NULL) + Threshold(tesseract_->mutable_pix_binary()); + if (tesseract_->ImageWidth() > MAX_INT16 || + tesseract_->ImageHeight() > MAX_INT16) { + tprintf("Image too large: (%d, %d)\n", + tesseract_->ImageWidth(), tesseract_->ImageHeight()); + return -1; + } + + tesseract_->PrepareForPageseg(); + + if (tesseract_->textord_equation_detect) { + if (equ_detect_ == NULL && datapath_ != NULL) { + equ_detect_ = new EquationDetect(datapath_->string(), NULL); + } + tesseract_->SetEquationDetect(equ_detect_); + } + + Tesseract* osd_tess = osd_tesseract_; + OSResults osr; + if (PSM_OSD_ENABLED(tesseract_->tessedit_pageseg_mode) && osd_tess == NULL) { + if (strcmp(language_->string(), "osd") == 0) { + osd_tess = tesseract_; + } + else { + osd_tesseract_ = new Tesseract; + if (osd_tesseract_->init_tesseract( + datapath_->string(), NULL, "osd", OEM_TESSERACT_ONLY, + NULL, 0, NULL, NULL, false) == 0) { + osd_tess = osd_tesseract_; + osd_tesseract_->set_source_resolution( + thresholder_->GetSourceYResolution()); + } + else { + tprintf("Warning: Auto orientation and script detection requested," + " but osd language failed to load\n"); + delete osd_tesseract_; + osd_tesseract_ = NULL; + } + } + } + + if (tesseract_->SegmentPage(input_file_, block_list_, osd_tess, &osr) < 0) + return -1; + // If Devanagari is being recognized, we use different images for page seg + // and for OCR. + tesseract_->PrepareForTessOCR(block_list_, osd_tess, &osr); + return osr.best_result.orientation_id; + return 0; + } + + /** Delete the pageres and clear the block list ready for a new page. */ + void TessBaseAPI::ClearResults() { + if (tesseract_ != NULL) { + tesseract_->Clear(); + } + if (page_res_ != NULL) { + delete page_res_; + page_res_ = NULL; + } + recognition_done_ = false; + if (block_list_ == NULL) + block_list_ = new BLOCK_LIST; + else + block_list_->clear(); + if (paragraph_models_ != NULL) { + paragraph_models_->delete_data_pointers(); + delete paragraph_models_; + paragraph_models_ = NULL; + } + SavePixForCrash(0, NULL); + } + + /** + * Return the length of the output text string, as UTF8, assuming + * liberally two spacing marks after each word (as paragraphs end with two + * newlines), and assuming a single character reject marker for each rejected + * character. + * Also return the number of recognized blobs in blob_count. + */ + int TessBaseAPI::TextLength(int* blob_count) { + if (tesseract_ == NULL || page_res_ == NULL) + return 0; + + PAGE_RES_IT page_res_it(page_res_); + int total_length = 2; + int total_blobs = 0; + // Iterate over the data structures to extract the recognition result. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD_RES *word = page_res_it.word(); + WERD_CHOICE* choice = word->best_choice; + if (choice != NULL) { + total_blobs += choice->length() + 2; + total_length += choice->unichar_string().length() + 2; + for (int i = 0; i < word->reject_map.length(); ++i) { + if (word->reject_map[i].rejected()) + ++total_length; + } + } + } + if (blob_count != NULL) + *blob_count = total_blobs; + return total_length; + } + + /** + * Estimates the Orientation And Script of the image. + * Returns true if the image was processed successfully. + */ + bool TessBaseAPI::DetectOS(OSResults* osr) { + if (tesseract_ == NULL) + return false; + ClearResults(); + if (tesseract_->pix_binary() == NULL) + Threshold(tesseract_->mutable_pix_binary()); + if (input_file_ == NULL) + input_file_ = new STRING(kInputFile); + return orientation_and_script_detection(*input_file_, osr, tesseract_); + } + + void TessBaseAPI::set_min_orientation_margin(double margin) { + tesseract_->min_orientation_margin.set_value(margin); + } + + /** + * Return text orientation of each block as determined in an earlier page layout + * analysis operation. Orientation is returned as the number of ccw 90-degree + * rotations (in [0..3]) required to make the text in the block upright + * (readable). Note that this may not necessary be the block orientation + * preferred for recognition (such as the case of vertical CJK text). + * + * Also returns whether the text in the block is believed to have vertical + * writing direction (when in an upright page orientation). + * + * The returned array is of length equal to the number of text blocks, which may + * be less than the total number of blocks. The ordering is intended to be + * consistent with GetTextLines(). + */ + void TessBaseAPI::GetBlockTextOrientations(int** block_orientation, + bool** vertical_writing) { + delete[] * block_orientation; + *block_orientation = NULL; + delete[] * vertical_writing; + *vertical_writing = NULL; + BLOCK_IT block_it(block_list_); + + block_it.move_to_first(); + int num_blocks = 0; + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { + if (!block_it.data()->poly_block()->IsText()) { + continue; + } + ++num_blocks; + } + if (!num_blocks) { + tprintf("WARNING: Found no blocks\n"); + return; + } + *block_orientation = new int[num_blocks]; + *vertical_writing = new bool[num_blocks]; + block_it.move_to_first(); + int i = 0; + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); + block_it.forward()) { + if (!block_it.data()->poly_block()->IsText()) { + continue; + } + FCOORD re_rotation = block_it.data()->re_rotation(); + float re_theta = re_rotation.angle(); + FCOORD classify_rotation = block_it.data()->classify_rotation(); + float classify_theta = classify_rotation.angle(); + double rot_theta = -(re_theta - classify_theta) * 2.0 / PI; + if (rot_theta < 0) rot_theta += 4; + int num_rotations = static_cast(rot_theta + 0.5); + (*block_orientation)[i] = num_rotations; + // The classify_rotation is non-zero only if the text has vertical + // writing direction. + (*vertical_writing)[i] = classify_rotation.y() != 0.0f; + ++i; + } + } + + // ____________________________________________________________________________ + // Ocropus add-ons. + + /** Find lines from the image making the BLOCK_LIST. */ + BLOCK_LIST* TessBaseAPI::FindLinesCreateBlockList() { + FindLines(); + BLOCK_LIST* result = block_list_; + block_list_ = NULL; + return result; + } + + /** + * Delete a block list. + * This is to keep BLOCK_LIST pointer opaque + * and let go of including the other headers. + */ + void TessBaseAPI::DeleteBlockList(BLOCK_LIST *block_list) { + delete block_list; + } + + + ROW *TessBaseAPI::MakeTessOCRRow(float baseline, + float xheight, + float descender, + float ascender) { + inT32 xstarts[] = { -32000 }; + double quad_coeffs[] = { 0, 0, baseline }; + return new ROW(1, + xstarts, + quad_coeffs, + xheight, + ascender - (baseline + xheight), + descender - baseline, + 0, + 0); + } + + /** Creates a TBLOB* from the whole pix. */ + TBLOB *TessBaseAPI::MakeTBLOB(Pix *pix) { + int width = pixGetWidth(pix); + int height = pixGetHeight(pix); + BLOCK block("a character", TRUE, 0, 0, 0, 0, width, height); + + // Create C_BLOBs from the page + extract_edges(pix, &block); + + // Merge all C_BLOBs + C_BLOB_LIST *list = block.blob_list(); + C_BLOB_IT c_blob_it(list); + if (c_blob_it.empty()) + return NULL; + // Move all the outlines to the first blob. + C_OUTLINE_IT ol_it(c_blob_it.data()->out_list()); + for (c_blob_it.forward(); + !c_blob_it.at_first(); + c_blob_it.forward()) { + C_BLOB *c_blob = c_blob_it.data(); + ol_it.add_list_after(c_blob->out_list()); + } + // Convert the first blob to the output TBLOB. + return TBLOB::PolygonalCopy(false, c_blob_it.data()); + } + + /** + * This method baseline normalizes a TBLOB in-place. The input row is used + * for normalization. The denorm is an optional parameter in which the + * normalization-antidote is returned. + */ + void TessBaseAPI::NormalizeTBLOB(TBLOB *tblob, ROW *row, bool numeric_mode) { + TBOX box = tblob->bounding_box(); + float x_center = (box.left() + box.right()) / 2.0f; + float baseline = row->base_line(x_center); + float scale = kBlnXHeight / row->x_height(); + tblob->Normalize(NULL, NULL, NULL, x_center, baseline, scale, scale, + 0.0f, static_cast(kBlnBaselineOffset), false, NULL); + } + + /** + * Return a TBLOB * from the whole pix. + * To be freed later with delete. + */ + TBLOB *make_tesseract_blob(float baseline, float xheight, + float descender, float ascender, + bool numeric_mode, Pix* pix) { + TBLOB *tblob = TessBaseAPI::MakeTBLOB(pix); + + // Normalize TBLOB + ROW *row = + TessBaseAPI::MakeTessOCRRow(baseline, xheight, descender, ascender); + TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode); + delete row; + return tblob; + } + + /** + * Adapt to recognize the current image as the given character. + * The image must be preloaded into pix_binary_ and be just an image + * of a single character. + */ + void TessBaseAPI::AdaptToCharacter(const char *unichar_repr, + int length, + float baseline, + float xheight, + float descender, + float ascender) { + UNICHAR_ID id = tesseract_->unicharset.unichar_to_id(unichar_repr, length); + TBLOB *blob = make_tesseract_blob(baseline, xheight, descender, ascender, + tesseract_->classify_bln_numeric_mode, + tesseract_->pix_binary()); + float threshold; + float best_rating = -100; + + + // Classify to get a raw choice. + BLOB_CHOICE_LIST choices; + tesseract_->AdaptiveClassifier(blob, &choices); + BLOB_CHOICE_IT choice_it; + choice_it.set_to_list(&choices); + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + if (choice_it.data()->rating() > best_rating) { + best_rating = choice_it.data()->rating(); + } + } + + threshold = tesseract_->matcher_good_threshold; + + if (blob->outlines) + tesseract_->AdaptToChar(blob, id, kUnknownFontinfoId, threshold, + tesseract_->AdaptedTemplates); + delete blob; + } + + + PAGE_RES* TessBaseAPI::RecognitionPass1(BLOCK_LIST* block_list) { + PAGE_RES *page_res = new PAGE_RES(false, block_list, + &(tesseract_->prev_word_best_choice_)); + tesseract_->recog_all_words(page_res, NULL, NULL, NULL, 1); + return page_res; + } + + PAGE_RES* TessBaseAPI::RecognitionPass2(BLOCK_LIST* block_list, + PAGE_RES* pass1_result) { + if (!pass1_result) + pass1_result = new PAGE_RES(false, block_list, + &(tesseract_->prev_word_best_choice_)); + tesseract_->recog_all_words(pass1_result, NULL, NULL, NULL, 2); + return pass1_result; + } + + void TessBaseAPI::DetectParagraphs(bool after_text_recognition) { + int debug_level = 0; + GetIntVariable("paragraph_debug_level", &debug_level); + if (paragraph_models_ == NULL) + paragraph_models_ = new GenericVector; + MutableIterator *result_it = GetMutableIterator(); + do { // Detect paragraphs for this block + GenericVector models; + ::tesseract::DetectParagraphs(debug_level, after_text_recognition, + result_it, &models); + *paragraph_models_ += models; + } while (result_it->Next(RIL_BLOCK)); + delete result_it; + } + + struct TESS_CHAR : ELIST_LINK { + char *unicode_repr; + int length; // of unicode_repr + float cost; + TBOX box; + + TESS_CHAR(float _cost, const char *repr, int len = -1) : cost(_cost) { + length = (len == -1 ? strlen(repr) : len); + unicode_repr = new char[length + 1]; + strncpy(unicode_repr, repr, length); + } + + TESS_CHAR() { // Satisfies ELISTIZE. + } + ~TESS_CHAR() { + delete[] unicode_repr; + } + }; + + ELISTIZEH(TESS_CHAR) + ELISTIZE(TESS_CHAR) + + static void add_space(TESS_CHAR_IT* it) { + TESS_CHAR *t = new TESS_CHAR(0, " "); + it->add_after_then_move(t); + } + + + static float rating_to_cost(float rating) { + rating = 100 + rating; + // cuddled that to save from coverage profiler + // (I have never seen ratings worse than -100, + // but the check won't hurt) + if (rating < 0) rating = 0; + return rating; + } + + /** + * Extract the OCR results, costs (penalty points for uncertainty), + * and the bounding boxes of the characters. + */ + static void extract_result(TESS_CHAR_IT* out, + PAGE_RES* page_res) { + PAGE_RES_IT page_res_it(page_res); + int word_count = 0; + while (page_res_it.word() != NULL) { + WERD_RES *word = page_res_it.word(); + const char *str = word->best_choice->unichar_string().string(); + const char *len = word->best_choice->unichar_lengths().string(); + TBOX real_rect = word->word->bounding_box(); + + if (word_count) + add_space(out); + int n = strlen(len); + for (int i = 0; i < n; i++) { + TESS_CHAR *tc = new TESS_CHAR(rating_to_cost(word->best_choice->rating()), + str, *len); + tc->box = real_rect.intersection(word->box_word->BlobBox(i)); + out->add_after_then_move(tc); + str += *len; + len++; + } + page_res_it.forward(); + word_count++; + } + } + + /** + * Extract the OCR results, costs (penalty points for uncertainty), + * and the bounding boxes of the characters. + */ + int TessBaseAPI::TesseractExtractResult(char** text, + int** lengths, + float** costs, + int** x0, + int** y0, + int** x1, + int** y1, + PAGE_RES* page_res) { + TESS_CHAR_LIST tess_chars; + TESS_CHAR_IT tess_chars_it(&tess_chars); + extract_result(&tess_chars_it, page_res); + tess_chars_it.move_to_first(); + int n = tess_chars.length(); + int text_len = 0; + *lengths = new int[n]; + *costs = new float[n]; + *x0 = new int[n]; + *y0 = new int[n]; + *x1 = new int[n]; + *y1 = new int[n]; + int i = 0; + for (tess_chars_it.mark_cycle_pt(); + !tess_chars_it.cycled_list(); + tess_chars_it.forward(), i++) { + TESS_CHAR *tc = tess_chars_it.data(); + text_len += (*lengths)[i] = tc->length; + (*costs)[i] = tc->cost; + (*x0)[i] = tc->box.left(); + (*y0)[i] = tc->box.bottom(); + (*x1)[i] = tc->box.right(); + (*y1)[i] = tc->box.top(); + } + char *p = *text = new char[text_len]; + + tess_chars_it.move_to_first(); + for (tess_chars_it.mark_cycle_pt(); + !tess_chars_it.cycled_list(); + tess_chars_it.forward()) { + TESS_CHAR *tc = tess_chars_it.data(); + strncpy(p, tc->unicode_repr, tc->length); + p += tc->length; + } + return n; + } + + /** This method returns the features associated with the input blob. */ + // The resulting features are returned in int_features, which must be + // of size MAX_NUM_INT_FEATURES. The number of features is returned in + // num_features (or 0 if there was a failure). + // On return feature_outline_index is filled with an index of the outline + // corresponding to each feature in int_features. + // TODO(rays) Fix the caller to out outline_counts instead. + void TessBaseAPI::GetFeaturesForBlob(TBLOB* blob, + INT_FEATURE_STRUCT* int_features, + int* num_features, + int* feature_outline_index) { + GenericVector outline_counts; + GenericVector bl_features; + GenericVector cn_features; + INT_FX_RESULT_STRUCT fx_info; + tesseract_->ExtractFeatures(*blob, false, &bl_features, + &cn_features, &fx_info, &outline_counts); + if (cn_features.empty() || cn_features.size() > MAX_NUM_INT_FEATURES) { + *num_features = 0; + return; // Feature extraction failed. + } + *num_features = cn_features.size(); + memcpy(int_features, &cn_features[0], *num_features * sizeof(cn_features[0])); + // TODO(rays) Pass outline_counts back and simplify the calling code. + if (feature_outline_index != NULL) { + int f = 0; + for (int i = 0; i < outline_counts.size(); ++i) { + while (f < outline_counts[i]) + feature_outline_index[f++] = i; + } + } + } + + // This method returns the row to which a box of specified dimensions would + // belong. If no good match is found, it returns NULL. + ROW* TessBaseAPI::FindRowForBox(BLOCK_LIST* blocks, + int left, int top, int right, int bottom) { + TBOX box(left, bottom, right, top); + BLOCK_IT b_it(blocks); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOCK* block = b_it.data(); + if (!box.major_overlap(block->bounding_box())) + continue; + ROW_IT r_it(block->row_list()); + for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { + ROW* row = r_it.data(); + if (!box.major_overlap(row->bounding_box())) + continue; + WERD_IT w_it(row->word_list()); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD* word = w_it.data(); + if (box.major_overlap(word->bounding_box())) + return row; + } + } + } + return NULL; + } + + /** Method to run adaptive classifier on a blob. */ + void TessBaseAPI::RunAdaptiveClassifier(TBLOB* blob, + int num_max_matches, + int* unichar_ids, + float* ratings, + int* num_matches_returned) { + BLOB_CHOICE_LIST* choices = new BLOB_CHOICE_LIST; + tesseract_->AdaptiveClassifier(blob, choices); + BLOB_CHOICE_IT choices_it(choices); + int& index = *num_matches_returned; + index = 0; + for (choices_it.mark_cycle_pt(); + !choices_it.cycled_list() && index < num_max_matches; + choices_it.forward()) { + BLOB_CHOICE* choice = choices_it.data(); + unichar_ids[index] = choice->unichar_id(); + ratings[index] = choice->rating(); + ++index; + } + *num_matches_returned = index; + delete choices; + } + + /** This method returns the string form of the specified unichar. */ + const char* TessBaseAPI::GetUnichar(int unichar_id) { + return tesseract_->unicharset.id_to_unichar(unichar_id); + } + + /** Return the pointer to the i-th dawg loaded into tesseract_ object. */ + const Dawg *TessBaseAPI::GetDawg(int i) const { + if (tesseract_ == NULL || i >= NumDawgs()) return NULL; + return tesseract_->getDict().GetDawg(i); + } + + /** Return the number of dawgs loaded into tesseract_ object. */ + int TessBaseAPI::NumDawgs() const { + return tesseract_ == NULL ? 0 : tesseract_->getDict().NumDawgs(); + } + +#ifndef NO_CUBE_BUILD + /** Return a pointer to underlying CubeRecoContext object if present. */ + CubeRecoContext *TessBaseAPI::GetCubeRecoContext() const { + return (tesseract_ == NULL) ? NULL : tesseract_->GetCubeRecoContext(); + } +#endif // NO_CUBE_BUILD + + /** Escape a char string - remove <>&"' with HTML codes. */ + STRING HOcrEscape(const char* text) { + STRING ret; + const char *ptr; + for (ptr = text; *ptr; ptr++) { + switch (*ptr) { + case '<': ret += "<"; break; + case '>': ret += ">"; break; + case '&': ret += "&"; break; + case '"': ret += """; break; + case '\'': ret += "'"; break; + default: ret += *ptr; + } + } + return ret; + } + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/api/baseapi.h b/hgdriver/3rdparty/hgOCR/include/api/baseapi.h new file mode 100644 index 0000000..da283cf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/baseapi.h @@ -0,0 +1,922 @@ +/////////////////////////////////////////////////////////////////////// +// File: baseapi.h +// Description: Simple API for calling tesseract. +// Author: Ray Smith +// Created: Fri Oct 06 15:35:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_API_BASEAPI_H__ +#define TESSERACT_API_BASEAPI_H__ + +#define TESSERACT_VERSION_STR "3.05.02" +#define TESSERACT_VERSION 0x030502 +#define MAKE_VERSION(major, minor, patch) (((major) << 16) | ((minor) << 8) | \ + (patch)) + +#include +// To avoid collision with other typenames include the ABSOLUTE MINIMUM +// complexity of includes here. Use forward declarations wherever possible +// and hide includes of complex types in baseapi.cpp. +#include "platform.h" +#include "apitypes.h" +#include "thresholder.h" +#include "unichar.h" +#include "tesscallback.h" +#include "publictypes.h" +#include "pageiterator.h" +#include "resultiterator.h" + +template class GenericVector; +class PAGE_RES; +class PAGE_RES_IT; +class ParagraphModel; +struct BlamerBundle; +class BLOCK_LIST; +class DENORM; +class MATRIX; +class ROW; +class STRING; +class WERD; +struct Pix; +struct Box; +struct Pixa; +struct Boxa; +class ETEXT_DESC; +struct OSResults; +class TBOX; +class UNICHARSET; +class WERD_CHOICE_LIST; + +struct INT_FEATURE_STRUCT; +typedef INT_FEATURE_STRUCT *INT_FEATURE; +struct TBLOB; + +namespace tesseract { + +#ifndef NO_CUBE_BUILD + class CubeRecoContext; +#endif // NO_CUBE_BUILD + class Dawg; + class Dict; + class EquationDetect; + class PageIterator; + class LTRResultIterator; + class ResultIterator; + class MutableIterator; + class TessResultRenderer; + class Tesseract; + class Trie; + class Wordrec; + + typedef int (Dict::*DictFunc)(void* void_dawg_args, + UNICHAR_ID unichar_id, bool word_end) const; + typedef double (Dict::*ProbabilityInContextFunc)(const char* lang, + const char* context, + int context_bytes, + const char* character, + int character_bytes); + typedef float (Dict::*ParamsModelClassifyFunc)( + const char *lang, void *path); + typedef void (Wordrec::*FillLatticeFunc)(const MATRIX &ratings, + const WERD_CHOICE_LIST &best_choices, + const UNICHARSET &unicharset, + BlamerBundle *blamer_bundle); + typedef TessCallback4 + TruthCallback; + + /** + * Base class for all tesseract APIs. + * Specific classes can add ability to work on different inputs or produce + * different outputs. + * This class is mostly an interface layer on top of the Tesseract instance + * class to hide the data types so that users of this class don't have to + * include any other Tesseract headers. + */ + class TESS_API TessBaseAPI { + public: + TessBaseAPI(); + virtual ~TessBaseAPI(); + + /** + * Returns the version identifier as a static string. Do not delete. + */ + static const char* Version(); + + /** + * If compiled with OpenCL AND an available OpenCL + * device is deemed faster than serial code, then + * "device" is populated with the cl_device_id + * and returns sizeof(cl_device_id) + * otherwise *device=NULL and returns 0. + */ + static size_t getOpenCLDevice(void **device); + + /** + * Writes the thresholded image to stderr as a PBM file on receipt of a + * SIGSEGV, SIGFPE, or SIGBUS signal. (Linux/Unix only). + */ + static void CatchSignals(); + + /** + * Set the name of the input file. Needed for training and + * reading a UNLV zone file, and for searchable PDF output. + */ + void SetInputName(const char* name); + /** + * These functions are required for searchable PDF output. + * We need our hands on the input file so that we can include + * it in the PDF without transcoding. If that is not possible, + * we need the original image. Finally, resolution metadata + * is stored in the PDF so we need that as well. + */ + const char* GetInputName(); + // Takes ownership of the input pix. + void SetInputImage(Pix *pix); + Pix* GetInputImage(); + int GetSourceYResolution(); + const char* GetDatapath(); + + /** Set the name of the bonus output files. Needed only for debugging. */ + void SetOutputName(const char* name); + + /** + * Set the value of an internal "parameter." + * Supply the name of the parameter and the value as a string, just as + * you would in a config file. + * Returns false if the name lookup failed. + * Eg SetVariable("tessedit_char_blacklist", "xyz"); to ignore x, y and z. + * Or SetVariable("classify_bln_numeric_mode", "1"); to set numeric-only mode. + * SetVariable may be used before Init, but settings will revert to + * defaults on End(). + * + * Note: Must be called after Init(). Only works for non-init variables + * (init variables should be passed to Init()). + */ + bool SetVariable(const char* name, const char* value); + bool SetDebugVariable(const char* name, const char* value); + + /** + * Returns true if the parameter was found among Tesseract parameters. + * Fills in value with the value of the parameter. + */ + bool GetIntVariable(const char *name, int *value) const; + bool GetBoolVariable(const char *name, bool *value) const; + bool GetDoubleVariable(const char *name, double *value) const; + + /** + * Returns the pointer to the string that represents the value of the + * parameter if it was found among Tesseract parameters. + */ + const char *GetStringVariable(const char *name) const; + + /** + * Print Tesseract parameters to the given file. + */ + void PrintVariables(FILE *fp) const; + + /** + * Get value of named variable as a string, if it exists. + */ + bool GetVariableAsString(const char *name, STRING *val); + + /** + * Instances are now mostly thread-safe and totally independent, + * but some global parameters remain. Basically it is safe to use multiple + * TessBaseAPIs in different threads in parallel, UNLESS: + * you use SetVariable on some of the Params in classify and textord. + * If you do, then the effect will be to change it for all your instances. + * + * Start tesseract. Returns zero on success and -1 on failure. + * NOTE that the only members that may be called before Init are those + * listed above here in the class definition. + * + * The datapath must be the name of the parent directory of tessdata and + * must end in / . Any name after the last / will be stripped. + * The language is (usually) an ISO 639-3 string or NULL will default to eng. + * It is entirely safe (and eventually will be efficient too) to call + * Init multiple times on the same instance to change language, or just + * to reset the classifier. + * The language may be a string of the form [~][+[~]]* indicating + * that multiple languages are to be loaded. Eg hin+eng will load Hindi and + * English. Languages may specify internally that they want to be loaded + * with one or more other languages, so the ~ sign is available to override + * that. Eg if hin were set to load eng by default, then hin+~eng would force + * loading only hin. The number of loaded languages is limited only by + * memory, with the caveat that loading additional languages will impact + * both speed and accuracy, as there is more work to do to decide on the + * applicable language, and there is more chance of hallucinating incorrect + * words. + * WARNING: On changing languages, all Tesseract parameters are reset + * back to their default values. (Which may vary between languages.) + * If you have a rare need to set a Variable that controls + * initialization for a second call to Init you should explicitly + * call End() and then use SetVariable before Init. This is only a very + * rare use case, since there are very few uses that require any parameters + * to be set before Init. + * + * If set_only_non_debug_params is true, only params that do not contain + * "debug" in the name will be set. + */ + int Init(const char* datapath, const char* language, OcrEngineMode mode, + char **configs, int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_non_debug_params); + int Init(const char* datapath, const char* language, OcrEngineMode oem) { + return Init(datapath, language, oem, NULL, 0, NULL, NULL, false); + } + int Init(const char* datapath, const char* language) { + return Init(datapath, language, OEM_DEFAULT, NULL, 0, NULL, NULL, false); + } + + /** + * Returns the languages string used in the last valid initialization. + * If the last initialization specified "deu+hin" then that will be + * returned. If hin loaded eng automatically as well, then that will + * not be included in this list. To find the languages actually + * loaded use GetLoadedLanguagesAsVector. + * The returned string should NOT be deleted. + */ + const char* GetInitLanguagesAsString() const; + + /** + * Returns the loaded languages in the vector of STRINGs. + * Includes all languages loaded by the last Init, including those loaded + * as dependencies of other loaded languages. + */ + void GetLoadedLanguagesAsVector(GenericVector* langs) const; + + /** + * Returns the available languages in the vector of STRINGs. + */ + void GetAvailableLanguagesAsVector(GenericVector* langs) const; + + /** + * Init only the lang model component of Tesseract. The only functions + * that work after this init are SetVariable and IsValidWord. + * WARNING: temporary! This function will be removed from here and placed + * in a separate API at some future time. + */ + int InitLangMod(const char* datapath, const char* language); + + /** + * Init only for page layout analysis. Use only for calls to SetImage and + * AnalysePage. Calls that attempt recognition will generate an error. + */ + void InitForAnalysePage(); + + /** + * Read a "config" file containing a set of param, value pairs. + * Searches the standard places: tessdata/configs, tessdata/tessconfigs + * and also accepts a relative or absolute path name. + * Note: only non-init params will be set (init params are set by Init()). + */ + void ReadConfigFile(const char* filename); + /** Same as above, but only set debug params from the given config file. */ + void ReadDebugConfigFile(const char* filename); + + /** + * Set the current page segmentation mode. Defaults to PSM_SINGLE_BLOCK. + * The mode is stored as an IntParam so it can also be modified by + * ReadConfigFile or SetVariable("tessedit_pageseg_mode", mode as string). + */ + void SetPageSegMode(PageSegMode mode); + + /** Return the current page segmentation mode. */ + PageSegMode GetPageSegMode() const; + + /** + * Recognize a rectangle from an image and return the result as a string. + * May be called many times for a single Init. + * Currently has no error checking. + * Greyscale of 8 and color of 24 or 32 bits per pixel may be given. + * Palette color images will not work properly and must be converted to + * 24 bit. + * Binary images of 1 bit per pixel may also be given but they must be + * byte packed with the MSB of the first byte being the first pixel, and a + * 1 represents WHITE. For binary images set bytes_per_pixel=0. + * The recognized text is returned as a char* which is coded + * as UTF8 and must be freed with the delete [] operator. + * + * Note that TesseractRect is the simplified convenience interface. + * For advanced uses, use SetImage, (optionally) SetRectangle, Recognize, + * and one or more of the Get*Text functions below. + */ + char* TesseractRect(const unsigned char* imagedata, + int bytes_per_pixel, int bytes_per_line, + int left, int top, int width, int height); + + /** + * Call between pages or documents etc to free up memory and forget + * adaptive data. + */ + void ClearAdaptiveClassifier(); + + /** + * @defgroup AdvancedAPI Advanced API + * The following methods break TesseractRect into pieces, so you can + * get hold of the thresholded image, get the text in different formats, + * get bounding boxes, confidences etc. + */ + /* @{ */ + + /** + * Provide an image for Tesseract to recognize. Format is as + * TesseractRect above. Copies the image buffer and converts to Pix. + * SetImage clears all recognition results, and sets the rectangle to the + * full image, so it may be followed immediately by a GetUTF8Text, and it + * will automatically perform recognition. + */ + void SetImage(const unsigned char* imagedata, int width, int height, + int bytes_per_pixel, int bytes_per_line); + + /** + * Provide an image for Tesseract to recognize. As with SetImage above, + * Tesseract takes its own copy of the image, so it need not persist until + * after Recognize. + * Pix vs raw, which to use? + * Use Pix where possible. Tesseract uses Pix as its internal representation + * and it is therefore more efficient to provide a Pix directly. + */ + void SetImage(Pix* pix); + + /** + * Set the resolution of the source image in pixels per inch so font size + * information can be calculated in results. Call this after SetImage(). + */ + void SetSourceResolution(int ppi); + + /** + * Restrict recognition to a sub-rectangle of the image. Call after SetImage. + * Each SetRectangle clears the recogntion results so multiple rectangles + * can be recognized with the same image. + */ + void SetRectangle(int left, int top, int width, int height); + + /** + * In extreme cases only, usually with a subclass of Thresholder, it + * is possible to provide a different Thresholder. The Thresholder may + * be preloaded with an image, settings etc, or they may be set after. + * Note that Tesseract takes ownership of the Thresholder and will + * delete it when it it is replaced or the API is destructed. + */ + void SetThresholder(ImageThresholder* thresholder) { + delete thresholder_; + thresholder_ = thresholder; + ClearResults(); + } + + /** + * Get a copy of the internal thresholded image from Tesseract. + * Caller takes ownership of the Pix and must pixDestroy it. + * May be called any time after SetImage, or after TesseractRect. + */ + Pix* GetThresholdedImage(); + + /** + * Get the result of page layout analysis as a leptonica-style + * Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + */ + Boxa* GetRegions(Pixa** pixa); + + /** + * Get the textlines as a leptonica-style + * Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + * If raw_image is true, then extract from the original image instead of the + * thresholded image and pad by raw_padding pixels. + * If blockids is not NULL, the block-id of each line is also returned as an + * array of one element per line. delete [] after use. + * If paraids is not NULL, the paragraph-id of each line within its block is + * also returned as an array of one element per line. delete [] after use. + */ + Boxa* GetTextlines(const bool raw_image, const int raw_padding, + Pixa** pixa, int** blockids, int** paraids); + /* + Helper method to extract from the thresholded image. (most common usage) + */ + Boxa* GetTextlines(Pixa** pixa, int** blockids) { + return GetTextlines(false, 0, pixa, blockids, NULL); + } + + /** + * Get textlines and strips of image regions as a leptonica-style Boxa, Pixa + * pair, in reading order. Enables downstream handling of non-rectangular + * regions. + * Can be called before or after Recognize. + * If blockids is not NULL, the block-id of each line is also returned as an + * array of one element per line. delete [] after use. + */ + Boxa* GetStrips(Pixa** pixa, int** blockids); + + /** + * Get the words as a leptonica-style + * Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + */ + Boxa* GetWords(Pixa** pixa); + + /** + * Gets the individual connected (text) components (created + * after pages segmentation step, but before recognition) + * as a leptonica-style Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + * Note: the caller is responsible for calling boxaDestroy() + * on the returned Boxa array and pixaDestroy() on cc array. + */ + Boxa* GetConnectedComponents(Pixa** cc); + + /** + * Get the given level kind of components (block, textline, word etc.) as a + * leptonica-style Boxa, Pixa pair, in reading order. + * Can be called before or after Recognize. + * If blockids is not NULL, the block-id of each component is also returned + * as an array of one element per component. delete [] after use. + * If blockids is not NULL, the paragraph-id of each component with its block + * is also returned as an array of one element per component. delete [] after + * use. + * If raw_image is true, then portions of the original image are extracted + * instead of the thresholded image and padded with raw_padding. + * If text_only is true, then only text components are returned. + */ + Boxa* GetComponentImages(const PageIteratorLevel level, + const bool text_only, const bool raw_image, + const int raw_padding, + Pixa** pixa, int** blockids, int** paraids); + // Helper function to get binary images with no padding (most common usage). + Boxa* GetComponentImages(const PageIteratorLevel level, + const bool text_only, + Pixa** pixa, int** blockids) { + return GetComponentImages(level, text_only, false, 0, pixa, blockids, NULL); + } + + /** + * Returns the scale factor of the thresholded image that would be returned by + * GetThresholdedImage() and the various GetX() methods that call + * GetComponentImages(). + * Returns 0 if no thresholder has been set. + */ + int GetThresholdedImageScaleFactor() const; + + /** + * Dump the internal binary image to a PGM file. + * @deprecated Use GetThresholdedImage and write the image using pixWrite + * instead if possible. + */ + void DumpPGM(const char* filename); + + /** + * Runs page layout analysis in the mode set by SetPageSegMode. + * May optionally be called prior to Recognize to get access to just + * the page layout results. Returns an iterator to the results. + * If merge_similar_words is true, words are combined where suitable for use + * with a line recognizer. Use if you want to use AnalyseLayout to find the + * textlines, and then want to process textline fragments with an external + * line recognizer. + * Returns NULL on error or an empty page. + * The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + PageIterator* AnalyseLayout(); + PageIterator* AnalyseLayout(bool merge_similar_words); + int AnalyseLayout1(); + + /** + * Recognize the image from SetAndThresholdImage, generating Tesseract + * internal structures. Returns 0 on success. + * Optional. The Get*Text functions below will call Recognize if needed. + * After Recognize, the output is kept internally until the next SetImage. + */ + int Recognize(ETEXT_DESC* monitor); + + /** + * Methods to retrieve information after SetAndThresholdImage(), + * Recognize() or TesseractRect(). (Recognize is called implicitly if needed.) + */ + + /** Variant on Recognize used for testing chopper. */ + int RecognizeForChopTest(ETEXT_DESC* monitor); + + /** + * Turns images into symbolic text. + * + * filename can point to a single image, a multi-page TIFF, + * or a plain text list of image filenames. + * + * retry_config is useful for debugging. If not NULL, you can fall + * back to an alternate configuration if a page fails for some + * reason. + * + * timeout_millisec terminates processing if any single page + * takes too long. Set to 0 for unlimited time. + * + * renderer is responible for creating the output. For example, + * use the TessTextRenderer if you want plaintext output, or + * the TessPDFRender to produce searchable PDF. + * + * If tessedit_page_number is non-negative, will only process that + * single page. Works for multi-page tiff file, or filelist. + * + * Returns true if successful, false on error. + */ + bool ProcessPages(const char* filename, const char* retry_config, + int timeout_millisec, TessResultRenderer* renderer); + // Does the real work of ProcessPages. + bool ProcessPagesInternal(const char* filename, const char* retry_config, + int timeout_millisec, TessResultRenderer* renderer); + + /** + * Turn a single image into symbolic text. + * + * The pix is the image processed. filename and page_index are + * metadata used by side-effect processes, such as reading a box + * file or formatting as hOCR. + * + * See ProcessPages for desciptions of other parameters. + */ + bool ProcessPage(Pix* pix, int page_index, const char* filename, + const char* retry_config, int timeout_millisec, + TessResultRenderer* renderer, + const char* jpgdata, int len); + + /** + * Get a reading-order iterator to the results of LayoutAnalysis and/or + * Recognize. The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + ResultIterator* GetIterator(); + + /** + * Get a mutable iterator to the results of LayoutAnalysis and/or Recognize. + * The returned iterator must be deleted after use. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + */ + MutableIterator* GetMutableIterator(); + + /** + * The recognized text is returned as a char* which is coded + * as UTF8 and must be freed with the delete [] operator. + */ + char* GetUTF8Text(); + + /** + * Make a HTML-formatted string with hOCR markup from the internal + * data structures. + * page_number is 0-based but will appear in the output as 1-based. + * monitor can be used to + * cancel the recognition + * receive progress callbacks + */ + char* GetHOCRText(ETEXT_DESC* monitor, int page_number); + + /** + * Make a HTML-formatted string with hOCR markup from the internal + * data structures. + * page_number is 0-based but will appear in the output as 1-based. + */ + char* GetHOCRText(int page_number); + + /** + * Make a TSV-formatted string from the internal data structures. + * page_number is 0-based but will appear in the output as 1-based. + */ + char* GetTSVText(int page_number); + + /** + * The recognized text is returned as a char* which is coded in the same + * format as a box file used in training. Returned string must be freed with + * the delete [] operator. + * Constructs coordinates in the original image - not just the rectangle. + * page_number is a 0-based page index that will appear in the box file. + */ + char* GetBoxText(int page_number); + + /** + * The recognized text is returned as a char* which is coded + * as UNLV format Latin-1 with specific reject and suspect codes + * and must be freed with the delete [] operator. + */ + char* GetUNLVText(); + + /** + * Detect the orientation of the input image and apparent script (alphabet). + * orient_deg is the detected clockwise rotation of the input image in degrees (0, 90, 180, 270) + * orient_conf is the confidence (15.0 is reasonably confident) + * script_name is an ASCII string, the name of the script, e.g. "Latin" + * script_conf is confidence level in the script + * Returns true on success and writes values to each parameter as an output + */ + bool DetectOrientationScript(int* orient_deg, float* orient_conf, const char** script_name, float* script_conf); + + /** + * The recognized text is returned as a char* which is coded + * as UTF8 and must be freed with the delete [] operator. + * page_number is a 0-based page index that will appear in the osd file. + */ + char* GetOsdText(int page_number); + + /** Returns the (average) confidence value between 0 and 100. */ + int MeanTextConf(); + /** + * Returns all word confidences (between 0 and 100) in an array, terminated + * by -1. The calling function must delete [] after use. + * The number of confidences should correspond to the number of space- + * delimited words in GetUTF8Text. + */ + int* AllWordConfidences(); + + /** + * Applies the given word to the adaptive classifier if possible. + * The word must be SPACE-DELIMITED UTF-8 - l i k e t h i s , so it can + * tell the boundaries of the graphemes. + * Assumes that SetImage/SetRectangle have been used to set the image + * to the given word. The mode arg should be PSM_SINGLE_WORD or + * PSM_CIRCLE_WORD, as that will be used to control layout analysis. + * The currently set PageSegMode is preserved. + * Returns false if adaption was not possible for some reason. + */ + bool AdaptToWordStr(PageSegMode mode, const char* wordstr); + + /** + * Free up recognition results and any stored image data, without actually + * freeing any recognition data that would be time-consuming to reload. + * Afterwards, you must call SetImage or TesseractRect before doing + * any Recognize or Get* operation. + */ + void Clear(); + + /** + * Close down tesseract and free up all memory. End() is equivalent to + * destructing and reconstructing your TessBaseAPI. + * Once End() has been used, none of the other API functions may be used + * other than Init and anything declared above it in the class definition. + */ + void End(); + + /** + * Clear any library-level memory caches. + * There are a variety of expensive-to-load constant data structures (mostly + * language dictionaries) that are cached globally -- surviving the Init() + * and End() of individual TessBaseAPI's. This function allows the clearing + * of these caches. + **/ + static void ClearPersistentCache(); + + /** + * Check whether a word is valid according to Tesseract's language model + * @return 0 if the word is invalid, non-zero if valid. + * @warning temporary! This function will be removed from here and placed + * in a separate API at some future time. + */ + int IsValidWord(const char *word); + // Returns true if utf8_character is defined in the UniCharset. + bool IsValidCharacter(const char *utf8_character); + + + bool GetTextDirection(int* out_offset, float* out_slope); + + /** Sets Dict::letter_is_okay_ function to point to the given function. */ + void SetDictFunc(DictFunc f); + + /** Sets Dict::probability_in_context_ function to point to the given + * function. + */ + void SetProbabilityInContextFunc(ProbabilityInContextFunc f); + + /** Sets Wordrec::fill_lattice_ function to point to the given function. */ + void SetFillLatticeFunc(FillLatticeFunc f); + + /** + * Estimates the Orientation And Script of the image. + * @return true if the image was processed successfully. + */ + bool DetectOS(OSResults*); + + /** This method returns the features associated with the input image. */ + void GetFeaturesForBlob(TBLOB* blob, INT_FEATURE_STRUCT* int_features, + int* num_features, int* feature_outline_index); + + /** + * This method returns the row to which a box of specified dimensions would + * belong. If no good match is found, it returns NULL. + */ + static ROW* FindRowForBox(BLOCK_LIST* blocks, int left, int top, + int right, int bottom); + + /** + * Method to run adaptive classifier on a blob. + * It returns at max num_max_matches results. + */ + void RunAdaptiveClassifier(TBLOB* blob, + int num_max_matches, + int* unichar_ids, + float* ratings, + int* num_matches_returned); + + /** This method returns the string form of the specified unichar. */ + const char* GetUnichar(int unichar_id); + + /** Return the pointer to the i-th dawg loaded into tesseract_ object. */ + const Dawg *GetDawg(int i) const; + + /** Return the number of dawgs loaded into tesseract_ object. */ + int NumDawgs() const; + + /** Returns a ROW object created from the input row specification. */ + static ROW *MakeTessOCRRow(float baseline, float xheight, + float descender, float ascender); + + /** Returns a TBLOB corresponding to the entire input image. */ + static TBLOB *MakeTBLOB(Pix *pix); + + /** + * This method baseline normalizes a TBLOB in-place. The input row is used + * for normalization. The denorm is an optional parameter in which the + * normalization-antidote is returned. + */ + static void NormalizeTBLOB(TBLOB *tblob, ROW *row, bool numeric_mode); + + Tesseract* tesseract() const { return tesseract_; } + + OcrEngineMode oem() const { return last_oem_requested_; } + + void InitTruthCallback(TruthCallback *cb) { truth_cb_ = cb; } + +#ifndef NO_CUBE_BUILD + /** Return a pointer to underlying CubeRecoContext object if present. */ + CubeRecoContext *GetCubeRecoContext() const; +#endif // NO_CUBE_BUILD + + void set_min_orientation_margin(double margin); + + /** + * Return text orientation of each block as determined by an earlier run + * of layout analysis. + */ + void GetBlockTextOrientations(int** block_orientation, + bool** vertical_writing); + + /** Find lines from the image making the BLOCK_LIST. */ + BLOCK_LIST* FindLinesCreateBlockList(); + + /** + * Delete a block list. + * This is to keep BLOCK_LIST pointer opaque + * and let go of including the other headers. + */ + static void DeleteBlockList(BLOCK_LIST* block_list); + /* @} */ + + protected: + + /** Common code for setting the image. Returns true if Init has been called. */ + TESS_LOCAL bool InternalSetImage(); + + /** + * Run the thresholder to make the thresholded image. If pix is not NULL, + * the source is thresholded to pix instead of the internal IMAGE. + */ + TESS_LOCAL virtual void Threshold(Pix** pix); + + /** + * Find lines from the image making the BLOCK_LIST. + * @return 0 on success. + */ + TESS_LOCAL int FindLines(); + + /** Delete the pageres and block list ready for a new page. */ + void ClearResults(); + + /** + * Return an LTR Result Iterator -- used only for training, as we really want + * to ignore all BiDi smarts at that point. + * delete once you're done with it. + */ + TESS_LOCAL LTRResultIterator* GetLTRIterator(); + + /** + * Return the length of the output text string, as UTF8, assuming + * one newline per line and one per block, with a terminator, + * and assuming a single character reject marker for each rejected character. + * Also return the number of recognized blobs in blob_count. + */ + TESS_LOCAL int TextLength(int* blob_count); + + /** @defgroup ocropusAddOns ocropus add-ons */ + /* @{ */ + + /** + * Adapt to recognize the current image as the given character. + * The image must be preloaded and be just an image of a single character. + */ + TESS_LOCAL void AdaptToCharacter(const char *unichar_repr, + int length, + float baseline, + float xheight, + float descender, + float ascender); + + /** Recognize text doing one pass only, using settings for a given pass. */ + TESS_LOCAL PAGE_RES* RecognitionPass1(BLOCK_LIST* block_list); + TESS_LOCAL PAGE_RES* RecognitionPass2(BLOCK_LIST* block_list, + PAGE_RES* pass1_result); + + //// paragraphs.cpp //////////////////////////////////////////////////// + TESS_LOCAL void DetectParagraphs(bool after_text_recognition); + + /** + * Extract the OCR results, costs (penalty points for uncertainty), + * and the bounding boxes of the characters. + */ + TESS_LOCAL static int TesseractExtractResult(char** text, + int** lengths, + float** costs, + int** x0, + int** y0, + int** x1, + int** y1, + PAGE_RES* page_res); + + TESS_LOCAL const PAGE_RES* GetPageRes() const { return page_res_; } + /* @} */ + + + protected: + Tesseract* tesseract_; ///< The underlying data object. + Tesseract* osd_tesseract_; ///< For orientation & script detection. + EquationDetect* equ_detect_; ///* paragraph_models_; + BLOCK_LIST* block_list_; ///< The page layout. + PAGE_RES* page_res_; ///< The page-level data. + STRING* input_file_; ///< Name used by training code. + STRING* output_file_; ///< Name used by debug code. + STRING* datapath_; ///< Current location of tessdata. + STRING* language_; ///< Last initialized language. + OcrEngineMode last_oem_requested_; ///< Last ocr language mode requested. + bool recognition_done_; ///< page_res_ contains recognition data. + TruthCallback *truth_cb_; /// fxn for setting truth_* in WERD_RES + + /** + * @defgroup ThresholderParams Thresholder Parameters + * Parameters saved from the Thresholder. Needed to rebuild coordinates. + */ + /* @{ */ + int rect_left_; + int rect_top_; + int rect_width_; + int rect_height_; + int image_width_; + int image_height_; + /* @} */ + + private: + // A list of image filenames gets special consideration + bool ProcessPagesFileList(FILE *fp, + STRING *buf, + const char* retry_config, int timeout_millisec, + TessResultRenderer* renderer, + int tessedit_page_number); + // TIFF supports multipage so gets special consideration. + bool ProcessPagesMultipageTiff(const unsigned char *data, + size_t size, + const char* filename, + const char* retry_config, + int timeout_millisec, + TessResultRenderer* renderer, + int tessedit_page_number); + // There's currently no way to pass a document title from the + // Tesseract command line, and we have multiple places that choose + // to set the title to an empty string. Using a single named + // variable will hopefully reduce confusion if the situation changes + // in the future. + const char *unknown_title_; + }; // class TessBaseAPI. + + /** Escape a char string - remove &<>"' with HTML codes. */ + STRING HOcrEscape(const char* text); +} // namespace tesseract. + +#endif // TESSERACT_API_BASEAPI_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/api/capi.cpp b/hgdriver/3rdparty/hgOCR/include/api/capi.cpp new file mode 100644 index 0000000..5cc108e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/capi.cpp @@ -0,0 +1,826 @@ +/////////////////////////////////////////////////////////////////////// +// File: capi.cpp +// Description: C-API TessBaseAPI +// +// (C) Copyright 2012, 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 TESS_CAPI_INCLUDE_BASEAPI +# define TESS_CAPI_INCLUDE_BASEAPI +#endif +#include "capi.h" +#include "genericvector.h" +#include "strngs.h" + +TESS_API const char* TESS_CALL TessVersion() +{ + return TessBaseAPI::Version(); +} + +TESS_API void TESS_CALL TessDeleteText(char* text) +{ + delete[] text; +} + +TESS_API void TESS_CALL TessDeleteTextArray(char** arr) +{ + for (char** pos = arr; *pos != NULL; ++pos) + delete[] * pos; + delete[] arr; +} + +TESS_API void TESS_CALL TessDeleteIntArray(int* arr) +{ + delete[] arr; +} + +TESS_API void TESS_CALL TessDeleteBlockList(BLOCK_LIST* block_list) +{ + TessBaseAPI::DeleteBlockList(block_list); +} + +TESS_API TessResultRenderer* TESS_CALL TessTextRendererCreate(const char* outputbase) +{ + return new TessTextRenderer(outputbase); +} + +TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate(const char* outputbase) +{ + return new TessHOcrRenderer(outputbase); +} + +TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate2(const char* outputbase, BOOL font_info) +{ + return new TessHOcrRenderer(outputbase, font_info); +} + +TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreate(const char* outputbase, const char* datadir) +{ + return new TessPDFRenderer(outputbase, datadir, false); +} + +TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreateTextonly(const char* outputbase, const char* datadir, + BOOL textonly) +{ + return new TessPDFRenderer(outputbase, datadir, textonly); +} + +TESS_API TessResultRenderer* TESS_CALL TessUnlvRendererCreate(const char* outputbase) +{ + return new TessUnlvRenderer(outputbase); +} + +TESS_API TessResultRenderer* TESS_CALL TessBoxTextRendererCreate(const char* outputbase) +{ + return new TessBoxTextRenderer(outputbase); +} + +TESS_API void TESS_CALL TessDeleteResultRenderer(TessResultRenderer* renderer) +{ + delete renderer; +} + +TESS_API void TESS_CALL TessResultRendererInsert(TessResultRenderer* renderer, TessResultRenderer* next) +{ + renderer->insert(next); +} + +TESS_API TessResultRenderer* TESS_CALL TessResultRendererNext(TessResultRenderer* renderer) +{ + return renderer->next(); +} + +TESS_API BOOL TESS_CALL TessResultRendererBeginDocument(TessResultRenderer* renderer, const char* title) +{ + return renderer->BeginDocument(title); +} + +TESS_API BOOL TESS_CALL TessResultRendererAddImage(TessResultRenderer* renderer, TessBaseAPI* api) +{ + return renderer->AddImage(api, nullptr, 0); +} + +TESS_API BOOL TESS_CALL TessResultRendererEndDocument(TessResultRenderer* renderer) +{ + return renderer->EndDocument(); +} + +TESS_API const char* TESS_CALL TessResultRendererExtention(TessResultRenderer* renderer) +{ + return renderer->file_extension(); +} + +TESS_API const char* TESS_CALL TessResultRendererTitle(TessResultRenderer* renderer) +{ + return renderer->title(); +} + +TESS_API int TESS_CALL TessResultRendererImageNum(TessResultRenderer* renderer) +{ + return renderer->imagenum(); +} + +TESS_API TessBaseAPI* TESS_CALL TessBaseAPICreate() +{ + return new TessBaseAPI; +} + +TESS_API void TESS_CALL TessBaseAPIDelete(TessBaseAPI* handle) +{ + delete handle; +} + +TESS_API size_t TESS_CALL TessBaseAPIGetOpenCLDevice(TessBaseAPI* handle, void **device) +{ + return handle->getOpenCLDevice(device); +} + +TESS_API void TESS_CALL TessBaseAPISetInputName(TessBaseAPI* handle, const char* name) +{ + handle->SetInputName(name); +} + +TESS_API const char* TESS_CALL TessBaseAPIGetInputName(TessBaseAPI* handle) +{ + return handle->GetInputName(); +} + +TESS_API void TESS_CALL TessBaseAPISetInputImage(TessBaseAPI* handle, Pix* pix) +{ + handle->SetInputImage(pix); +} + +TESS_API Pix* TESS_CALL TessBaseAPIGetInputImage(TessBaseAPI* handle) +{ + return handle->GetInputImage(); +} + +TESS_API int TESS_CALL TessBaseAPIGetSourceYResolution(TessBaseAPI* handle) +{ + return handle->GetSourceYResolution(); +} + +TESS_API const char* TESS_CALL TessBaseAPIGetDatapath(TessBaseAPI* handle) +{ + return handle->GetDatapath(); +} + +TESS_API void TESS_CALL TessBaseAPISetOutputName(TessBaseAPI* handle, const char* name) +{ + handle->SetOutputName(name); +} + +TESS_API BOOL TESS_CALL TessBaseAPISetVariable(TessBaseAPI* handle, const char* name, const char* value) +{ + return handle->SetVariable(name, value) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessBaseAPISetDebugVariable(TessBaseAPI* handle, const char* name, const char* value) +{ + return handle->SetVariable(name, value) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessBaseAPIGetIntVariable(const TessBaseAPI* handle, const char* name, int* value) +{ + return handle->GetIntVariable(name, value) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessBaseAPIGetBoolVariable(const TessBaseAPI* handle, const char* name, BOOL* value) +{ + bool boolValue; + if (handle->GetBoolVariable(name, &boolValue)) + { + *value = boolValue ? TRUE : FALSE; + return TRUE; + } + else + { + return FALSE; + } +} + +TESS_API BOOL TESS_CALL TessBaseAPIGetDoubleVariable(const TessBaseAPI* handle, const char* name, double* value) +{ + return handle->GetDoubleVariable(name, value) ? TRUE : FALSE; +} + +TESS_API const char* TESS_CALL TessBaseAPIGetStringVariable(const TessBaseAPI* handle, const char* name) +{ + return handle->GetStringVariable(name); +} + +TESS_API void TESS_CALL TessBaseAPIPrintVariables(const TessBaseAPI* handle, FILE* fp) +{ + handle->PrintVariables(fp); +} + +TESS_API BOOL TESS_CALL TessBaseAPIPrintVariablesToFile(const TessBaseAPI* handle, const char* filename) +{ + FILE* fp = fopen(filename, "w"); + if (fp != NULL) + { + handle->PrintVariables(fp); + fclose(fp); + return TRUE; + } + return FALSE; +} + +TESS_API BOOL TESS_CALL TessBaseAPIGetVariableAsString(TessBaseAPI* handle, const char* name, STRING* val) +{ + return handle->GetVariableAsString(name, val) ? TRUE : FALSE; +} + +TESS_API int TESS_CALL TessBaseAPIInit4(TessBaseAPI* handle, const char* datapath, const char* language, + TessOcrEngineMode mode, char** configs, int configs_size, + char** vars_vec, char** vars_values, size_t vars_vec_size, + BOOL set_only_non_debug_params) +{ + GenericVector varNames; + GenericVector varValues; + if (vars_vec != NULL && vars_values != NULL) { + for (size_t i = 0; i < vars_vec_size; i++) { + varNames.push_back(STRING(vars_vec[i])); + varValues.push_back(STRING(vars_values[i])); + } + } + + return handle->Init(datapath, language, mode, configs, configs_size, &varNames, &varValues, set_only_non_debug_params); +} + + +TESS_API int TESS_CALL TessBaseAPIInit1(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem, + char** configs, int configs_size) +{ + return handle->Init(datapath, language, oem, configs, configs_size, NULL, NULL, false); +} + +TESS_API int TESS_CALL TessBaseAPIInit2(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem) +{ + return handle->Init(datapath, language, oem); +} + +TESS_API int TESS_CALL TessBaseAPIInit3(TessBaseAPI* handle, const char* datapath, const char* language) +{ + return handle->Init(datapath, language); +} + +TESS_API const char* TESS_CALL TessBaseAPIGetInitLanguagesAsString(const TessBaseAPI* handle) +{ + return handle->GetInitLanguagesAsString(); +} + +TESS_API char** TESS_CALL TessBaseAPIGetLoadedLanguagesAsVector(const TessBaseAPI* handle) +{ + GenericVector languages; + handle->GetLoadedLanguagesAsVector(&languages); + char** arr = new char*[languages.size() + 1]; + for (int index = 0; index < languages.size(); ++index) + arr[index] = languages[index].strdup(); + arr[languages.size()] = NULL; + return arr; +} + +TESS_API char** TESS_CALL TessBaseAPIGetAvailableLanguagesAsVector(const TessBaseAPI* handle) +{ + GenericVector languages; + handle->GetAvailableLanguagesAsVector(&languages); + char** arr = new char*[languages.size() + 1]; + for (int index = 0; index < languages.size(); ++index) + arr[index] = languages[index].strdup(); + arr[languages.size()] = NULL; + return arr; +} + +TESS_API int TESS_CALL TessBaseAPIInitLangMod(TessBaseAPI* handle, const char* datapath, const char* language) +{ + return handle->InitLangMod(datapath, language); +} + +TESS_API void TESS_CALL TessBaseAPIInitForAnalysePage(TessBaseAPI* handle) +{ + handle->InitForAnalysePage(); +} + +TESS_API void TESS_CALL TessBaseAPIReadConfigFile(TessBaseAPI* handle, const char* filename) +{ + handle->ReadConfigFile(filename); +} + +TESS_API void TESS_CALL TessBaseAPIReadDebugConfigFile(TessBaseAPI* handle, const char* filename) +{ + handle->ReadDebugConfigFile(filename); +} + +TESS_API void TESS_CALL TessBaseAPISetPageSegMode(TessBaseAPI* handle, TessPageSegMode mode) +{ + handle->SetPageSegMode(mode); +} + +TESS_API TessPageSegMode TESS_CALL TessBaseAPIGetPageSegMode(const TessBaseAPI* handle) +{ + return handle->GetPageSegMode(); +} + +TESS_API char* TESS_CALL TessBaseAPIRect(TessBaseAPI* handle, const unsigned char* imagedata, + int bytes_per_pixel, int bytes_per_line, + int left, int top, int width, int height) +{ + return handle->TesseractRect(imagedata, bytes_per_pixel, bytes_per_line, left, top, width, height); +} + +TESS_API void TESS_CALL TessBaseAPIClearAdaptiveClassifier(TessBaseAPI* handle) +{ + handle->ClearAdaptiveClassifier(); +} + +TESS_API void TESS_CALL TessBaseAPISetImage(TessBaseAPI* handle, const unsigned char* imagedata, int width, int height, + int bytes_per_pixel, int bytes_per_line) +{ + handle->SetImage(imagedata, width, height, bytes_per_pixel, bytes_per_line); +} + +TESS_API void TESS_CALL TessBaseAPISetImage2(TessBaseAPI* handle, struct Pix* pix) +{ + return handle->SetImage(pix); +} + +TESS_API void TESS_CALL TessBaseAPISetSourceResolution(TessBaseAPI* handle, int ppi) +{ + handle->SetSourceResolution(ppi); +} + +TESS_API void TESS_CALL TessBaseAPISetRectangle(TessBaseAPI* handle, int left, int top, int width, int height) +{ + handle->SetRectangle(left, top, width, height); +} + +TESS_API void TESS_CALL TessBaseAPISetThresholder(TessBaseAPI* handle, TessImageThresholder* thresholder) +{ + handle->SetThresholder(thresholder); +} + +TESS_API struct Pix* TESS_CALL TessBaseAPIGetThresholdedImage(TessBaseAPI* handle) +{ + return handle->GetThresholdedImage(); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetRegions(TessBaseAPI* handle, struct Pixa** pixa) +{ + return handle->GetRegions(pixa); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines(TessBaseAPI* handle, struct Pixa** pixa, int** blockids) +{ + return handle->GetTextlines(pixa, blockids); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetTextlines1(TessBaseAPI* handle, const BOOL raw_image, const int raw_padding, + struct Pixa** pixa, int** blockids, int** paraids) +{ + return handle->GetTextlines(raw_image, raw_padding, pixa, blockids, paraids); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetStrips(TessBaseAPI* handle, struct Pixa** pixa, int** blockids) +{ + return handle->GetStrips(pixa, blockids); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetWords(TessBaseAPI* handle, struct Pixa** pixa) +{ + return handle->GetWords(pixa); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetConnectedComponents(TessBaseAPI* handle, struct Pixa** cc) +{ + return handle->GetConnectedComponents(cc); +} + +TESS_API struct Boxa* TESS_CALL TessBaseAPIGetComponentImages(TessBaseAPI* handle, TessPageIteratorLevel level, BOOL text_only, struct Pixa** pixa, int** blockids) +{ + return handle->GetComponentImages(level, text_only != FALSE, pixa, blockids); +} + +TESS_API struct Boxa* +TESS_CALL TessBaseAPIGetComponentImages1(TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, + const BOOL raw_image, const int raw_padding, + struct Pixa** pixa, int** blockids, int** paraids) +{ + return handle->GetComponentImages(level, text_only != FALSE, raw_image, raw_padding, pixa, blockids, paraids); +} + +TESS_API int TESS_CALL TessBaseAPIGetThresholdedImageScaleFactor(const TessBaseAPI* handle) +{ + return handle->GetThresholdedImageScaleFactor(); +} + +TESS_API void TESS_CALL TessBaseAPIDumpPGM(TessBaseAPI* handle, const char* filename) +{ + handle->DumpPGM(filename); +} + +TESS_API TessPageIterator* TESS_CALL TessBaseAPIAnalyseLayout(TessBaseAPI* handle) +{ + return handle->AnalyseLayout(); +} + +TESS_API int TESS_CALL TessBaseAPIRecognize(TessBaseAPI* handle, ETEXT_DESC* monitor) +{ + return handle->Recognize(monitor); +} + +TESS_API int TESS_CALL TessBaseAPIRecognizeForChopTest(TessBaseAPI* handle, ETEXT_DESC* monitor) +{ + return handle->RecognizeForChopTest(monitor); +} + +TESS_API BOOL TESS_CALL TessBaseAPIProcessPages(TessBaseAPI* handle, const char* filename, const char* retry_config, + int timeout_millisec, TessResultRenderer* renderer) +{ + if (handle->ProcessPages(filename, retry_config, timeout_millisec, renderer)) + return TRUE; + else + return FALSE; +} + +TESS_API BOOL TESS_CALL TessBaseAPIProcessPage(TessBaseAPI* handle, struct Pix* pix, int page_index, const char* filename, + const char* retry_config, int timeout_millisec, TessResultRenderer* renderer) +{ + if (handle->ProcessPage(pix, page_index, filename, retry_config, timeout_millisec, renderer, nullptr, 0)) + return TRUE; + else + return FALSE; +} + +TESS_API TessResultIterator* TESS_CALL TessBaseAPIGetIterator(TessBaseAPI* handle) +{ + return handle->GetIterator(); +} + +TESS_API TessMutableIterator* TESS_CALL TessBaseAPIGetMutableIterator(TessBaseAPI* handle) +{ + return handle->GetMutableIterator(); +} + +TESS_API char* TESS_CALL TessBaseAPIGetUTF8Text(TessBaseAPI* handle) +{ + return handle->GetUTF8Text(); +} + +TESS_API char* TESS_CALL TessBaseAPIGetHOCRText(TessBaseAPI* handle, int page_number) +{ + return handle->GetHOCRText(NULL, page_number); +} + +TESS_API char* TESS_CALL TessBaseAPIGetBoxText(TessBaseAPI* handle, int page_number) +{ + return handle->GetBoxText(page_number); +} + +TESS_API char* TESS_CALL TessBaseAPIGetUNLVText(TessBaseAPI* handle) +{ + return handle->GetUNLVText(); +} + +TESS_API int TESS_CALL TessBaseAPIMeanTextConf(TessBaseAPI* handle) +{ + return handle->MeanTextConf(); +} + +TESS_API int* TESS_CALL TessBaseAPIAllWordConfidences(TessBaseAPI* handle) +{ + return handle->AllWordConfidences(); +} + +TESS_API BOOL TESS_CALL TessBaseAPIAdaptToWordStr(TessBaseAPI* handle, TessPageSegMode mode, const char* wordstr) +{ + return handle->AdaptToWordStr(mode, wordstr) ? TRUE : FALSE; +} + +TESS_API void TESS_CALL TessBaseAPIClear(TessBaseAPI* handle) +{ + handle->Clear(); +} + +TESS_API void TESS_CALL TessBaseAPIEnd(TessBaseAPI* handle) +{ + handle->End(); +} + +TESS_API int TESS_CALL TessBaseAPIIsValidWord(TessBaseAPI* handle, const char* word) +{ + return handle->IsValidWord(word); +} + +TESS_API BOOL TESS_CALL TessBaseAPIGetTextDirection(TessBaseAPI* handle, int* out_offset, float* out_slope) +{ + return handle->GetTextDirection(out_offset, out_slope) ? TRUE : FALSE; +} + +TESS_API void TESS_CALL TessBaseAPISetDictFunc(TessBaseAPI* handle, TessDictFunc f) +{ + handle->SetDictFunc(f); +} + +TESS_API void TESS_CALL TessBaseAPIClearPersistentCache(TessBaseAPI* handle) +{ + handle->ClearPersistentCache(); +} + +TESS_API void TESS_CALL TessBaseAPISetProbabilityInContextFunc(TessBaseAPI* handle, TessProbabilityInContextFunc f) +{ + handle->SetProbabilityInContextFunc(f); +} + +TESS_API BOOL TESS_CALL TessBaseAPIDetectOS(TessBaseAPI* handle, OSResults* results) +{ + return FALSE; // Unsafe ABI, return FALSE always +} + +TESS_API BOOL TESS_CALL TessBaseAPIDetectOrientationScript(TessBaseAPI* handle, + int* orient_deg, float* orient_conf, const char** script_name, float* script_conf) +{ + bool success; + success = handle->DetectOrientationScript(orient_deg, orient_conf, script_name, script_conf); + return (BOOL)success; +} + + +TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, INT_FEATURE_STRUCT* int_features, + int* num_features, int* FeatureOutlineIndex) +{ + handle->GetFeaturesForBlob(blob, int_features, num_features, FeatureOutlineIndex); +} + +TESS_API ROW* TESS_CALL TessFindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom) +{ + return TessBaseAPI::FindRowForBox(blocks, left, top, right, bottom); +} + +TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, int num_max_matches, + int* unichar_ids, float* ratings, int* num_matches_returned) +{ + handle->RunAdaptiveClassifier(blob, num_max_matches, unichar_ids, ratings, num_matches_returned); +} + +TESS_API const char* TESS_CALL TessBaseAPIGetUnichar(TessBaseAPI* handle, int unichar_id) +{ + return handle->GetUnichar(unichar_id); +} + +TESS_API const TessDawg* TESS_CALL TessBaseAPIGetDawg(const TessBaseAPI* handle, int i) +{ + return handle->GetDawg(i); +} + +TESS_API int TESS_CALL TessBaseAPINumDawgs(const TessBaseAPI* handle) +{ + return handle->NumDawgs(); +} + +TESS_API ROW* TESS_CALL TessMakeTessOCRRow(float baseline, float xheight, float descender, float ascender) +{ + return TessBaseAPI::MakeTessOCRRow(baseline, xheight, descender, ascender); +} + +TESS_API TBLOB* TESS_CALL TessMakeTBLOB(struct Pix* pix) +{ + return TessBaseAPI::MakeTBLOB(pix); +} + +TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB* tblob, ROW* row, BOOL numeric_mode) +{ + TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode != FALSE); +} + +TESS_API TessOcrEngineMode TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle) +{ + return handle->oem(); +} + +TESS_API void TESS_CALL TessBaseAPIInitTruthCallback(TessBaseAPI* handle, TessTruthCallback* cb) +{ + handle->InitTruthCallback(cb); +} + +#ifndef NO_CUBE_BUILD +TESS_API TessCubeRecoContext* TESS_CALL TessBaseAPIGetCubeRecoContext(const TessBaseAPI* handle) +{ + return handle->GetCubeRecoContext(); +} +#endif // NO_CUBE_BUILD + +TESS_API void TESS_CALL TessBaseAPISetMinOrientationMargin(TessBaseAPI* handle, double margin) +{ + handle->set_min_orientation_margin(margin); +} + +TESS_API void TESS_CALL TessBaseGetBlockTextOrientations(TessBaseAPI* handle, int** block_orientation, bool** vertical_writing) +{ + handle->GetBlockTextOrientations(block_orientation, vertical_writing); +} + +TESS_API BLOCK_LIST* TESS_CALL TessBaseAPIFindLinesCreateBlockList(TessBaseAPI* handle) +{ + return handle->FindLinesCreateBlockList(); +} + +TESS_API void TESS_CALL TessPageIteratorDelete(TessPageIterator* handle) +{ + delete handle; +} + +TESS_API TessPageIterator* TESS_CALL TessPageIteratorCopy(const TessPageIterator* handle) +{ + return new TessPageIterator(*handle); +} + +TESS_API void TESS_CALL TessPageIteratorBegin(TessPageIterator* handle) +{ + handle->Begin(); +} + +TESS_API BOOL TESS_CALL TessPageIteratorNext(TessPageIterator* handle, TessPageIteratorLevel level) +{ + return handle->Next(level) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessPageIteratorIsAtBeginningOf(const TessPageIterator* handle, TessPageIteratorLevel level) +{ + return handle->IsAtBeginningOf(level) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessPageIteratorIsAtFinalElement(const TessPageIterator* handle, TessPageIteratorLevel level, + TessPageIteratorLevel element) +{ + return handle->IsAtFinalElement(level, element) ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessPageIteratorBoundingBox(const TessPageIterator* handle, TessPageIteratorLevel level, + int* left, int* top, int* right, int* bottom) +{ + return handle->BoundingBox(level, left, top, right, bottom) ? TRUE : FALSE; +} + +TESS_API TessPolyBlockType TESS_CALL TessPageIteratorBlockType(const TessPageIterator* handle) +{ + return handle->BlockType(); +} + +TESS_API struct Pix* TESS_CALL TessPageIteratorGetBinaryImage(const TessPageIterator* handle, TessPageIteratorLevel level) +{ + return handle->GetBinaryImage(level); +} + +TESS_API struct Pix* TESS_CALL TessPageIteratorGetImage(const TessPageIterator* handle, TessPageIteratorLevel level, int padding, + struct Pix* original_image, int* left, int* top) +{ + return handle->GetImage(level, padding, original_image, left, top); +} + +TESS_API BOOL TESS_CALL TessPageIteratorBaseline(const TessPageIterator* handle, TessPageIteratorLevel level, + int* x1, int* y1, int* x2, int* y2) +{ + return handle->Baseline(level, x1, y1, x2, y2) ? TRUE : FALSE; +} + +TESS_API void TESS_CALL TessPageIteratorOrientation(TessPageIterator* handle, TessOrientation* orientation, + TessWritingDirection* writing_direction, TessTextlineOrder* textline_order, + float* deskew_angle) +{ + handle->Orientation(orientation, writing_direction, textline_order, deskew_angle); +} + +TESS_API void TESS_CALL TessPageIteratorParagraphInfo(TessPageIterator* handle, TessParagraphJustification* justification, + BOOL *is_list_item, BOOL *is_crown, int *first_line_indent) +{ + bool bool_is_list_item, bool_is_crown; + handle->ParagraphInfo(justification, &bool_is_list_item, &bool_is_crown, first_line_indent); + if (is_list_item) + *is_list_item = bool_is_list_item ? TRUE : FALSE; + if (is_crown) + *is_crown = bool_is_crown ? TRUE : FALSE; +} + + +TESS_API void TESS_CALL TessResultIteratorDelete(TessResultIterator* handle) +{ + delete handle; +} + +TESS_API TessResultIterator* TESS_CALL TessResultIteratorCopy(const TessResultIterator* handle) +{ + return new TessResultIterator(*handle); +} + +TESS_API TessPageIterator* TESS_CALL TessResultIteratorGetPageIterator(TessResultIterator* handle) +{ + return handle; +} + +TESS_API const TessPageIterator* TESS_CALL TessResultIteratorGetPageIteratorConst(const TessResultIterator* handle) +{ + return handle; +} + +TESS_API TessChoiceIterator* TESS_CALL TessResultIteratorGetChoiceIterator(const TessResultIterator* handle) +{ + return new TessChoiceIterator(*handle); +} + +TESS_API BOOL TESS_CALL TessResultIteratorNext(TessResultIterator* handle, TessPageIteratorLevel level) +{ + return handle->Next(level); +} + +TESS_API char* TESS_CALL TessResultIteratorGetUTF8Text(const TessResultIterator* handle, TessPageIteratorLevel level) +{ + return handle->GetUTF8Text(level); +} + +TESS_API float TESS_CALL TessResultIteratorConfidence(const TessResultIterator* handle, TessPageIteratorLevel level) +{ + return handle->Confidence(level); +} + +TESS_API const char* TESS_CALL TessResultIteratorWordRecognitionLanguage(const TessResultIterator* handle) +{ + return handle->WordRecognitionLanguage(); +} + +TESS_API const char* TESS_CALL TessResultIteratorWordFontAttributes(const TessResultIterator* handle, BOOL* is_bold, BOOL* is_italic, + BOOL* is_underlined, BOOL* is_monospace, BOOL* is_serif, + BOOL* is_smallcaps, int* pointsize, int* font_id) +{ + bool bool_is_bold, bool_is_italic, bool_is_underlined, bool_is_monospace, bool_is_serif, bool_is_smallcaps; + const char* ret = handle->WordFontAttributes(&bool_is_bold, &bool_is_italic, &bool_is_underlined, &bool_is_monospace, &bool_is_serif, + &bool_is_smallcaps, pointsize, font_id); + if (is_bold) + *is_bold = bool_is_bold ? TRUE : FALSE; + if (is_italic) + *is_italic = bool_is_italic ? TRUE : FALSE; + if (is_underlined) + *is_underlined = bool_is_underlined ? TRUE : FALSE; + if (is_monospace) + *is_monospace = bool_is_monospace ? TRUE : FALSE; + if (is_serif) + *is_serif = bool_is_serif ? TRUE : FALSE; + if (is_smallcaps) + *is_smallcaps = bool_is_smallcaps ? TRUE : FALSE; + return ret; +} + +TESS_API BOOL TESS_CALL TessResultIteratorWordIsFromDictionary(const TessResultIterator* handle) +{ + return handle->WordIsFromDictionary() ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessResultIteratorWordIsNumeric(const TessResultIterator* handle) +{ + return handle->WordIsNumeric() ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSuperscript(const TessResultIterator* handle) +{ + return handle->SymbolIsSuperscript() ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSubscript(const TessResultIterator* handle) +{ + return handle->SymbolIsSubscript() ? TRUE : FALSE; +} + +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsDropcap(const TessResultIterator* handle) +{ + return handle->SymbolIsDropcap() ? TRUE : FALSE; +} + +TESS_API void TESS_CALL TessChoiceIteratorDelete(TessChoiceIterator* handle) +{ + delete handle; +} + +TESS_API BOOL TESS_CALL TessChoiceIteratorNext(TessChoiceIterator* handle) +{ + return handle->Next(); +} + +TESS_API const char* TESS_CALL TessChoiceIteratorGetUTF8Text(const TessChoiceIterator* handle) +{ + return handle->GetUTF8Text(); +} + +TESS_API float TESS_CALL TessChoiceIteratorConfidence(const TessChoiceIterator* handle) +{ + return handle->Confidence(); +} diff --git a/hgdriver/3rdparty/hgOCR/include/api/capi.h b/hgdriver/3rdparty/hgOCR/include/api/capi.h new file mode 100644 index 0000000..c0aa761 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/capi.h @@ -0,0 +1,409 @@ +/////////////////////////////////////////////////////////////////////// +// File: capi.h +// Description: C-API TessBaseAPI +// +// (C) Copyright 2012, 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 API_CAPI_H_ +#define API_CAPI_H_ + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +# include "baseapi.h" +# include "pageiterator.h" +# include "resultiterator.h" +# include "renderer.h" +#else +# include "platform.h" +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef TESS_CALL +# if defined(WIN32) +# define TESS_CALL __cdecl +# else +# define TESS_CALL +# endif +#endif + +#ifndef BOOL +# define BOOL int +# define TRUE 1 +# define FALSE 0 +#endif + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +typedef tesseract::TessResultRenderer TessResultRenderer; +typedef tesseract::TessTextRenderer TessTextRenderer; +typedef tesseract::TessHOcrRenderer TessHOcrRenderer; +typedef tesseract::TessPDFRenderer TessPDFRenderer; +typedef tesseract::TessUnlvRenderer TessUnlvRenderer; +typedef tesseract::TessBoxTextRenderer TessBoxTextRenderer; +typedef tesseract::TessBaseAPI TessBaseAPI; +typedef tesseract::PageIterator TessPageIterator; +typedef tesseract::ResultIterator TessResultIterator; +typedef tesseract::MutableIterator TessMutableIterator; +typedef tesseract::ChoiceIterator TessChoiceIterator; +typedef tesseract::OcrEngineMode TessOcrEngineMode; +typedef tesseract::PageSegMode TessPageSegMode; +typedef tesseract::ImageThresholder TessImageThresholder; +typedef tesseract::PageIteratorLevel TessPageIteratorLevel; +typedef tesseract::DictFunc TessDictFunc; +typedef tesseract::ProbabilityInContextFunc TessProbabilityInContextFunc; +// typedef tesseract::ParamsModelClassifyFunc TessParamsModelClassifyFunc; +typedef tesseract::FillLatticeFunc TessFillLatticeFunc; +typedef tesseract::Dawg TessDawg; +typedef tesseract::TruthCallback TessTruthCallback; +#ifndef NO_CUBE_BUILD +typedef tesseract::CubeRecoContext TessCubeRecoContext; +#endif // NO_CUBE_BUILD +typedef tesseract::Orientation TessOrientation; +typedef tesseract::ParagraphJustification TessParagraphJustification; +typedef tesseract::WritingDirection TessWritingDirection; +typedef tesseract::TextlineOrder TessTextlineOrder; +typedef PolyBlockType TessPolyBlockType; +#else +typedef struct TessResultRenderer TessResultRenderer; +typedef struct TessTextRenderer TessTextRenderer; +typedef struct TessHOcrRenderer TessHOcrRenderer; +typedef struct TessPDFRenderer TessPDFRenderer; +typedef struct TessUnlvRenderer TessUnlvRenderer; +typedef struct TessBoxTextRenderer TessBoxTextRenderer; +typedef struct TessBaseAPI TessBaseAPI; +typedef struct TessPageIterator TessPageIterator; +typedef struct TessResultIterator TessResultIterator; +typedef struct TessMutableIterator TessMutableIterator; +typedef struct TessChoiceIterator TessChoiceIterator; +typedef enum TessOcrEngineMode { OEM_TESSERACT_ONLY, OEM_CUBE_ONLY, OEM_TESSERACT_CUBE_COMBINED, OEM_DEFAULT } TessOcrEngineMode; +typedef enum TessPageSegMode { PSM_OSD_ONLY, PSM_AUTO_OSD, PSM_AUTO_ONLY, PSM_AUTO, PSM_SINGLE_COLUMN, PSM_SINGLE_BLOCK_VERT_TEXT, + PSM_SINGLE_BLOCK, PSM_SINGLE_LINE, PSM_SINGLE_WORD, PSM_CIRCLE_WORD, PSM_SINGLE_CHAR, PSM_SPARSE_TEXT, + PSM_SPARSE_TEXT_OSD, PSM_COUNT } TessPageSegMode; +typedef enum TessPageIteratorLevel { RIL_BLOCK, RIL_PARA, RIL_TEXTLINE, RIL_WORD, RIL_SYMBOL} TessPageIteratorLevel; +typedef enum TessPolyBlockType { PT_UNKNOWN, PT_FLOWING_TEXT, PT_HEADING_TEXT, PT_PULLOUT_TEXT, PT_EQUATION, PT_INLINE_EQUATION, + PT_TABLE, PT_VERTICAL_TEXT, PT_CAPTION_TEXT, PT_FLOWING_IMAGE, PT_HEADING_IMAGE, + PT_PULLOUT_IMAGE, PT_HORZ_LINE, PT_VERT_LINE, PT_NOISE, PT_COUNT } TessPolyBlockType; +typedef enum TessOrientation { ORIENTATION_PAGE_UP, ORIENTATION_PAGE_RIGHT, ORIENTATION_PAGE_DOWN, ORIENTATION_PAGE_LEFT } TessOrientation; +typedef enum TessParagraphJustification { JUSTIFICATION_UNKNOWN, JUSTIFICATION_LEFT, JUSTIFICATION_CENTER, JUSTIFICATION_RIGHT } TessParagraphJustification; +typedef enum TessWritingDirection { WRITING_DIRECTION_LEFT_TO_RIGHT, WRITING_DIRECTION_RIGHT_TO_LEFT, WRITING_DIRECTION_TOP_TO_BOTTOM } TessWritingDirection; +typedef enum TessTextlineOrder { TEXTLINE_ORDER_LEFT_TO_RIGHT, TEXTLINE_ORDER_RIGHT_TO_LEFT, TEXTLINE_ORDER_TOP_TO_BOTTOM } TessTextlineOrder; +typedef struct ETEXT_DESC ETEXT_DESC; +#endif + +struct Pix; +struct Boxa; +struct Pixa; + +/* General free functions */ + +TESS_API const char* + TESS_CALL TessVersion(); +TESS_API void TESS_CALL TessDeleteText(char* text); +TESS_API void TESS_CALL TessDeleteTextArray(char** arr); +TESS_API void TESS_CALL TessDeleteIntArray(int* arr); +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API void TESS_CALL TessDeleteBlockList(BLOCK_LIST* block_list); +#endif + +/* Renderer API */ +TESS_API TessResultRenderer* TESS_CALL TessTextRendererCreate(const char* outputbase); +TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate(const char* outputbase); +TESS_API TessResultRenderer* TESS_CALL TessHOcrRendererCreate2(const char* outputbase, BOOL font_info); +TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreate(const char* outputbase, const char* datadir); +TESS_API TessResultRenderer* TESS_CALL TessPDFRendererCreateTextonly(const char* outputbase, const char* datadir, + BOOL textonly); +TESS_API TessResultRenderer* TESS_CALL TessUnlvRendererCreate(const char* outputbase); +TESS_API TessResultRenderer* TESS_CALL TessBoxTextRendererCreate(const char* outputbase); + +TESS_API void TESS_CALL TessDeleteResultRenderer(TessResultRenderer* renderer); +TESS_API void TESS_CALL TessResultRendererInsert(TessResultRenderer* renderer, TessResultRenderer* next); +TESS_API TessResultRenderer* + TESS_CALL TessResultRendererNext(TessResultRenderer* renderer); +TESS_API BOOL TESS_CALL TessResultRendererBeginDocument(TessResultRenderer* renderer, const char* title); +TESS_API BOOL TESS_CALL TessResultRendererAddImage(TessResultRenderer* renderer, TessBaseAPI* api); +TESS_API BOOL TESS_CALL TessResultRendererEndDocument(TessResultRenderer* renderer); + +TESS_API const char* TESS_CALL TessResultRendererExtention(TessResultRenderer* renderer); +TESS_API const char* TESS_CALL TessResultRendererTitle(TessResultRenderer* renderer); +TESS_API int TESS_CALL TessResultRendererImageNum(TessResultRenderer* renderer); + +/* Base API */ + +TESS_API TessBaseAPI* + TESS_CALL TessBaseAPICreate(); +TESS_API void TESS_CALL TessBaseAPIDelete(TessBaseAPI* handle); + +TESS_API size_t TESS_CALL TessBaseAPIGetOpenCLDevice(TessBaseAPI* handle, void **device); + +TESS_API void TESS_CALL TessBaseAPISetInputName( TessBaseAPI* handle, const char* name); +TESS_API const char* TESS_CALL TessBaseAPIGetInputName(TessBaseAPI* handle); + +TESS_API void TESS_CALL TessBaseAPISetInputImage(TessBaseAPI* handle, struct Pix* pix); +TESS_API struct Pix* TESS_CALL TessBaseAPIGetInputImage(TessBaseAPI* handle); + +TESS_API int TESS_CALL TessBaseAPIGetSourceYResolution(TessBaseAPI* handle); +TESS_API const char* TESS_CALL TessBaseAPIGetDatapath(TessBaseAPI* handle); + +TESS_API void TESS_CALL TessBaseAPISetOutputName(TessBaseAPI* handle, const char* name); + +TESS_API BOOL TESS_CALL TessBaseAPISetVariable(TessBaseAPI* handle, const char* name, const char* value); +TESS_API BOOL TESS_CALL TessBaseAPISetDebugVariable(TessBaseAPI* handle, const char* name, const char* value); + +TESS_API BOOL TESS_CALL TessBaseAPIGetIntVariable( const TessBaseAPI* handle, const char* name, int* value); +TESS_API BOOL TESS_CALL TessBaseAPIGetBoolVariable( const TessBaseAPI* handle, const char* name, BOOL* value); +TESS_API BOOL TESS_CALL TessBaseAPIGetDoubleVariable(const TessBaseAPI* handle, const char* name, double* value); +TESS_API const char* + TESS_CALL TessBaseAPIGetStringVariable(const TessBaseAPI* handle, const char* name); + +TESS_API void TESS_CALL TessBaseAPIPrintVariables( const TessBaseAPI* handle, FILE* fp); +TESS_API BOOL TESS_CALL TessBaseAPIPrintVariablesToFile(const TessBaseAPI* handle, const char* filename); +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API BOOL TESS_CALL TessBaseAPIGetVariableAsString(TessBaseAPI* handle, const char* name, STRING* val); +#endif + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API int TESS_CALL TessBaseAPIInit(TessBaseAPI* handle, const char* datapath, const char* language, + TessOcrEngineMode mode, char** configs, int configs_size, + const STRING* vars_vec, size_t vars_vec_size, + const STRING* vars_values, size_t vars_values_size, BOOL set_only_init_params); +#endif +TESS_API int TESS_CALL TessBaseAPIInit1(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem, + char** configs, int configs_size); +TESS_API int TESS_CALL TessBaseAPIInit2(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode oem); +TESS_API int TESS_CALL TessBaseAPIInit3(TessBaseAPI* handle, const char* datapath, const char* language); + +TESS_API int TESS_CALL TessBaseAPIInit4(TessBaseAPI* handle, const char* datapath, const char* language, TessOcrEngineMode mode, + char** configs, int configs_size, + char** vars_vec, char** vars_values, size_t vars_vec_size, + BOOL set_only_non_debug_params); + +TESS_API const char* + TESS_CALL TessBaseAPIGetInitLanguagesAsString(const TessBaseAPI* handle); +TESS_API char** + TESS_CALL TessBaseAPIGetLoadedLanguagesAsVector(const TessBaseAPI* handle); +TESS_API char** + TESS_CALL TessBaseAPIGetAvailableLanguagesAsVector(const TessBaseAPI* handle); + +TESS_API int TESS_CALL TessBaseAPIInitLangMod(TessBaseAPI* handle, const char* datapath, const char* language); +TESS_API void TESS_CALL TessBaseAPIInitForAnalysePage(TessBaseAPI* handle); + +TESS_API void TESS_CALL TessBaseAPIReadConfigFile(TessBaseAPI* handle, const char* filename); +TESS_API void TESS_CALL TessBaseAPIReadDebugConfigFile(TessBaseAPI* handle, const char* filename); + +TESS_API void TESS_CALL TessBaseAPISetPageSegMode(TessBaseAPI* handle, TessPageSegMode mode); +TESS_API TessPageSegMode + TESS_CALL TessBaseAPIGetPageSegMode(const TessBaseAPI* handle); + +TESS_API char* TESS_CALL TessBaseAPIRect(TessBaseAPI* handle, const unsigned char* imagedata, + int bytes_per_pixel, int bytes_per_line, + int left, int top, int width, int height); + +TESS_API void TESS_CALL TessBaseAPIClearAdaptiveClassifier(TessBaseAPI* handle); + +TESS_API void TESS_CALL TessBaseAPISetImage(TessBaseAPI* handle, const unsigned char* imagedata, int width, int height, + int bytes_per_pixel, int bytes_per_line); +TESS_API void TESS_CALL TessBaseAPISetImage2(TessBaseAPI* handle, struct Pix* pix); + +TESS_API void TESS_CALL TessBaseAPISetSourceResolution(TessBaseAPI* handle, int ppi); + +TESS_API void TESS_CALL TessBaseAPISetRectangle(TessBaseAPI* handle, int left, int top, int width, int height); + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API void TESS_CALL TessBaseAPISetThresholder(TessBaseAPI* handle, TessImageThresholder* thresholder); +#endif + +TESS_API struct Pix* + TESS_CALL TessBaseAPIGetThresholdedImage( TessBaseAPI* handle); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetRegions( TessBaseAPI* handle, struct Pixa** pixa); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetTextlines( TessBaseAPI* handle, struct Pixa** pixa, int** blockids); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetTextlines1( TessBaseAPI* handle, const BOOL raw_image, const int raw_padding, + struct Pixa** pixa, int** blockids, int** paraids); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetStrips( TessBaseAPI* handle, struct Pixa** pixa, int** blockids); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetWords( TessBaseAPI* handle, struct Pixa** pixa); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetConnectedComponents(TessBaseAPI* handle, struct Pixa** cc); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetComponentImages( TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, + struct Pixa** pixa, int** blockids); +TESS_API struct Boxa* + TESS_CALL TessBaseAPIGetComponentImages1( TessBaseAPI* handle, const TessPageIteratorLevel level, const BOOL text_only, + const BOOL raw_image, const int raw_padding, + struct Pixa** pixa, int** blockids, int** paraids); + +TESS_API int TESS_CALL TessBaseAPIGetThresholdedImageScaleFactor(const TessBaseAPI* handle); + +TESS_API void TESS_CALL TessBaseAPIDumpPGM(TessBaseAPI* handle, const char* filename); + +TESS_API TessPageIterator* + TESS_CALL TessBaseAPIAnalyseLayout(TessBaseAPI* handle); + +TESS_API int TESS_CALL TessBaseAPIRecognize(TessBaseAPI* handle, ETEXT_DESC* monitor); +TESS_API int TESS_CALL TessBaseAPIRecognizeForChopTest(TessBaseAPI* handle, ETEXT_DESC* monitor); +TESS_API BOOL TESS_CALL TessBaseAPIProcessPages(TessBaseAPI* handle, const char* filename, const char* retry_config, + int timeout_millisec, TessResultRenderer* renderer); +TESS_API BOOL TESS_CALL TessBaseAPIProcessPage(TessBaseAPI* handle, struct Pix* pix, int page_index, const char* filename, + const char* retry_config, int timeout_millisec, TessResultRenderer* renderer); + +TESS_API TessResultIterator* + TESS_CALL TessBaseAPIGetIterator(TessBaseAPI* handle); +TESS_API TessMutableIterator* + TESS_CALL TessBaseAPIGetMutableIterator(TessBaseAPI* handle); + +TESS_API char* TESS_CALL TessBaseAPIGetUTF8Text(TessBaseAPI* handle); +TESS_API char* TESS_CALL TessBaseAPIGetHOCRText(TessBaseAPI* handle, int page_number); +TESS_API char* TESS_CALL TessBaseAPIGetBoxText(TessBaseAPI* handle, int page_number); +TESS_API char* TESS_CALL TessBaseAPIGetUNLVText(TessBaseAPI* handle); +TESS_API int TESS_CALL TessBaseAPIMeanTextConf(TessBaseAPI* handle); +TESS_API int* TESS_CALL TessBaseAPIAllWordConfidences(TessBaseAPI* handle); +TESS_API BOOL TESS_CALL TessBaseAPIAdaptToWordStr(TessBaseAPI* handle, TessPageSegMode mode, const char* wordstr); + +TESS_API void TESS_CALL TessBaseAPIClear(TessBaseAPI* handle); +TESS_API void TESS_CALL TessBaseAPIEnd(TessBaseAPI* handle); + +TESS_API int TESS_CALL TessBaseAPIIsValidWord(TessBaseAPI* handle, const char* word); +TESS_API BOOL TESS_CALL TessBaseAPIGetTextDirection(TessBaseAPI* handle, int* out_offset, float* out_slope); + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API void TESS_CALL TessBaseAPISetDictFunc(TessBaseAPI* handle, TessDictFunc f); +TESS_API void TESS_CALL TessBaseAPIClearPersistentCache(TessBaseAPI* handle); +TESS_API void TESS_CALL TessBaseAPISetProbabilityInContextFunc(TessBaseAPI* handle, TessProbabilityInContextFunc f); + +TESS_API void TESS_CALL TessBaseAPISetFillLatticeFunc(TessBaseAPI* handle, TessFillLatticeFunc f); + +// Deprecated, no longer working +TESS_API BOOL TESS_CALL TessBaseAPIDetectOS(TessBaseAPI* handle, OSResults* results); + +// Call TessDeleteText(*best_script_name) to free memory allocated by this function +TESS_API BOOL TESS_CALL TessBaseAPIDetectOrientationScript(TessBaseAPI* handle, + int* orient_deg, float* orient_conf, const char **script_name, float* script_conf); + +TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, INT_FEATURE_STRUCT* int_features, + int* num_features, int* FeatureOutlineIndex); + +TESS_API ROW* TESS_CALL TessFindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom); +TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, int num_max_matches, + int* unichar_ids, float* ratings, int* num_matches_returned); +#endif + +TESS_API const char* + TESS_CALL TessBaseAPIGetUnichar(TessBaseAPI* handle, int unichar_id); + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API const TessDawg* + TESS_CALL TessBaseAPIGetDawg(const TessBaseAPI* handle, int i); +TESS_API int TESS_CALL TessBaseAPINumDawgs(const TessBaseAPI* handle); +#endif + +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API ROW* TESS_CALL TessMakeTessOCRRow(float baseline, float xheight, float descender, float ascender); +TESS_API TBLOB* + TESS_CALL TessMakeTBLOB(Pix* pix); +TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB* tblob, ROW* row, BOOL numeric_mode); + +TESS_API TessOcrEngineMode + TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle); +TESS_API void TESS_CALL TessBaseAPIInitTruthCallback(TessBaseAPI* handle, TessTruthCallback* cb); + +#ifndef NO_CUBE_BUILD +TESS_API TessCubeRecoContext* + TESS_CALL TessBaseAPIGetCubeRecoContext(const TessBaseAPI* handle); +#endif // NO_CUBE_BUILD +#endif + +TESS_API void TESS_CALL TessBaseAPISetMinOrientationMargin(TessBaseAPI* handle, double margin); +#ifdef TESS_CAPI_INCLUDE_BASEAPI +TESS_API void TESS_CALL TessBaseGetBlockTextOrientations(TessBaseAPI* handle, int** block_orientation, BOOL** vertical_writing); + +TESS_API BLOCK_LIST* + TESS_CALL TessBaseAPIFindLinesCreateBlockList(TessBaseAPI* handle); +#endif + +/* Page iterator */ + +TESS_API void TESS_CALL TessPageIteratorDelete(TessPageIterator* handle); +TESS_API TessPageIterator* + TESS_CALL TessPageIteratorCopy(const TessPageIterator* handle); + +TESS_API void TESS_CALL TessPageIteratorBegin(TessPageIterator* handle); +TESS_API BOOL TESS_CALL TessPageIteratorNext(TessPageIterator* handle, TessPageIteratorLevel level); +TESS_API BOOL TESS_CALL TessPageIteratorIsAtBeginningOf(const TessPageIterator* handle, TessPageIteratorLevel level); +TESS_API BOOL TESS_CALL TessPageIteratorIsAtFinalElement(const TessPageIterator* handle, TessPageIteratorLevel level, + TessPageIteratorLevel element); + +TESS_API BOOL TESS_CALL TessPageIteratorBoundingBox(const TessPageIterator* handle, TessPageIteratorLevel level, + int* left, int* top, int* right, int* bottom); +TESS_API TessPolyBlockType + TESS_CALL TessPageIteratorBlockType(const TessPageIterator* handle); + +TESS_API struct Pix* + TESS_CALL TessPageIteratorGetBinaryImage(const TessPageIterator* handle, TessPageIteratorLevel level); +TESS_API struct Pix* + TESS_CALL TessPageIteratorGetImage(const TessPageIterator* handle, TessPageIteratorLevel level, int padding, + struct Pix* original_image, int* left, int* top); + +TESS_API BOOL TESS_CALL TessPageIteratorBaseline(const TessPageIterator* handle, TessPageIteratorLevel level, + int* x1, int* y1, int* x2, int* y2); + +TESS_API void TESS_CALL TessPageIteratorOrientation(TessPageIterator* handle, TessOrientation* orientation, + TessWritingDirection* writing_direction, TessTextlineOrder* textline_order, + float* deskew_angle); + +TESS_API void TESS_CALL TessPageIteratorParagraphInfo(TessPageIterator* handle, TessParagraphJustification* justification, + BOOL *is_list_item, BOOL *is_crown, int *first_line_indent); + +/* Result iterator */ + +TESS_API void TESS_CALL TessResultIteratorDelete(TessResultIterator* handle); +TESS_API TessResultIterator* + TESS_CALL TessResultIteratorCopy(const TessResultIterator* handle); +TESS_API TessPageIterator* + TESS_CALL TessResultIteratorGetPageIterator(TessResultIterator* handle); +TESS_API const TessPageIterator* + TESS_CALL TessResultIteratorGetPageIteratorConst(const TessResultIterator* handle); +TESS_API TessChoiceIterator* + TESS_CALL TessResultIteratorGetChoiceIterator(const TessResultIterator* handle); + +TESS_API BOOL TESS_CALL TessResultIteratorNext(TessResultIterator* handle, TessPageIteratorLevel level); +TESS_API char* TESS_CALL TessResultIteratorGetUTF8Text(const TessResultIterator* handle, TessPageIteratorLevel level); +TESS_API float TESS_CALL TessResultIteratorConfidence(const TessResultIterator* handle, TessPageIteratorLevel level); +TESS_API const char* + TESS_CALL TessResultIteratorWordRecognitionLanguage(const TessResultIterator* handle); +TESS_API const char* + TESS_CALL TessResultIteratorWordFontAttributes(const TessResultIterator* handle, BOOL* is_bold, BOOL* is_italic, + BOOL* is_underlined, BOOL* is_monospace, BOOL* is_serif, + BOOL* is_smallcaps, int* pointsize, int* font_id); + +TESS_API BOOL TESS_CALL TessResultIteratorWordIsFromDictionary(const TessResultIterator* handle); +TESS_API BOOL TESS_CALL TessResultIteratorWordIsNumeric(const TessResultIterator* handle); +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSuperscript(const TessResultIterator* handle); +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsSubscript(const TessResultIterator* handle); +TESS_API BOOL TESS_CALL TessResultIteratorSymbolIsDropcap(const TessResultIterator* handle); + +TESS_API void TESS_CALL TessChoiceIteratorDelete(TessChoiceIterator* handle); +TESS_API BOOL TESS_CALL TessChoiceIteratorNext(TessChoiceIterator* handle); +TESS_API const char* TESS_CALL TessChoiceIteratorGetUTF8Text(const TessChoiceIterator* handle); +TESS_API float TESS_CALL TessChoiceIteratorConfidence(const TessChoiceIterator* handle); + +#ifdef __cplusplus +} +#endif + +#endif // API_CAPI_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/api/config_auto.h b/hgdriver/3rdparty/hgOCR/include/api/config_auto.h new file mode 100644 index 0000000..41df404 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/config_auto.h @@ -0,0 +1,9 @@ +#define HAVE_LIBJPEG 1 +#define HAVE_LIBTIFF 1 +#define HAVE_LIBPNG 1 +#define HAVE_LIBZ 1 +#define HAVE_LIBGIF 1 +#define HAVE_LIBUNGIF 0 +#define HAVE_LIBWEBP 1 +#define HAVE_LIBJP2K 1 +#define LIBJP2K_HEADER \ No newline at end of file diff --git a/hgdriver/3rdparty/hgOCR/include/api/pdfrenderer.cpp b/hgdriver/3rdparty/hgOCR/include/api/pdfrenderer.cpp new file mode 100644 index 0000000..0133720 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/pdfrenderer.cpp @@ -0,0 +1,1265 @@ +锘/////////////////////////////////////////////////////////////////////// +// File: pdfrenderer.cpp +// Description: PDF rendering interface to inject into TessBaseAPI +// +// (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. +// +/////////////////////////////////////////////////////////////////////// + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "allheaders.h" +#include "baseapi.h" +#include "math.h" +#include "renderer.h" +#include "strngs.h" +#include "tprintf.h" + +#ifdef _MSC_VER +#include "mathfix.h" +#endif + +/* + +Design notes from Ken Sharp, with light editing. + +We think one solution is a font with a single glyph (.notdef) and a +CIDToGIDMap which maps all the CIDs to 0. That map would then be +stored as a stream in the PDF file, and when flate compressed should +be pretty small. The font, of course, will be approximately the same +size as the one you currently use. + +I'm working on such a font now, the CIDToGIDMap is trivial, you just +create a stream object which contains 128k bytes (2 bytes per possible +CID and your CIDs range from 0 to 65535) and where you currently have +"/CIDToGIDMap /Identity" you would have "/CIDToGIDMap 0 R". + +Note that if, in future, you were to use a different (ie not 2 byte) +CMap for character codes you could trivially extend the CIDToGIDMap. + +The following is an explanation of how some of the font stuff works, +this may be too simple for you in which case please accept my +apologies, its hard to know how much knowledge someone has. You can +skip all this anyway, its just for information. + +The font embedded in a PDF file is usually intended just to be +rendered, but extensions allow for at least some ability to locate (or +copy) text from a document. This isn't something which was an original +goal of the PDF format, but its been retro-fitted, presumably due to +popular demand. + +To do this reliably the PDF file must contain a ToUnicode CMap, a +device for mapping character codes to Unicode code points. If one of +these is present, then this will be used to convert the character +codes into Unicode values. If its not present then the reader will +fall back through a series of heuristics to try and guess the +result. This is, as you would expect, prone to failure. + +This doesn't concern you of course, since you always write a ToUnicode +CMap, so because you are writing the text in text rendering mode 3 it +would seem that you don't really need to worry about this, but in the +PDF spec you cannot have an isolated ToUnicode CMap, it has to be +attached to a font, so in order to get even copy/paste to work you +need to define a font. + +This is what leads to problems, tools like pdfwrite assume that they +are going to be able to (or even have to) modify the font entries, so +they require that the font being embedded be valid, and to be honest +the font Tesseract embeds isn't valid (for this purpose). + + +To see why lets look at how text is specified in a PDF file: + +(Test) Tj + +Now that looks like text but actually it isn't. Each of those bytes is +a 'character code'. When it comes to rendering the text a complex +sequence of events takes place, which converts the character code into +'something' which the font understands. Its entirely possible via +character mappings to have that text render as 'Sftu' + +For simple fonts (PostScript type 1), we use the character code as the +index into an Encoding array (256 elements), each element of which is +a glyph name, so this gives us a glyph name. We then consult the +CharStrings dictionary in the font, that's a complex object which +contains pairs of keys and values, you can use the key to retrieve a +given value. So we have a glyph name, we then use that as the key to +the dictionary and retrieve the associated value. For a type 1 font, +the value is a glyph program that describes how to draw the glyph. + +For CIDFonts, its a little more complicated. Because CIDFonts can be +large, using a glyph name as the key is unreasonable (it would also +lead to unfeasibly large Encoding arrays), so instead we use a 'CID' +as the key. CIDs are just numbers. + +But.... We don't use the character code as the CID. What we do is use +a CMap to convert the character code into a CID. We then use the CID +to key the CharStrings dictionary and proceed as before. So the 'CMap' +is the equivalent of the Encoding array, but its a more compact and +flexible representation. + +Note that you have to use the CMap just to find out how many bytes +constitute a character code, and it can be variable. For example you +can say if the first byte is 0x00->0x7f then its just one byte, if its +0x80->0xf0 then its 2 bytes and if its 0xf0->0xff then its 3 bytes. I +have seen CMaps defining character codes up to 5 bytes wide. + +Now that's fine for 'PostScript' CIDFonts, but its not sufficient for +TrueType CIDFonts. The thing is that TrueType fonts are accessed using +a Glyph ID (GID) (and the LOCA table) which may well not be anything +like the CID. So for this case PDF includes a CIDToGIDMap. That maps +the CIDs to GIDs, and we can then use the GID to get the glyph +description from the GLYF table of the font. + +So for a TrueType CIDFont, character-code->CID->GID->glyf-program. + +Looking at the PDF file I was supplied with we see that it contains +text like : + +<0x0075> Tj + +So we start by taking the character code (117) and look it up in the +CMap. Well you don't supply a CMap, you just use the Identity-H one +which is predefined. So character code 117 maps to CID 117. Then we +use the CIDToGIDMap, again you don't supply one, you just use the +predefined 'Identity' map. So CID 117 maps to GID 117. But the font we +were supplied with only contains 116 glyphs. + +Now for Latin that's not a huge problem, you can just supply a bigger +font. But for more complex languages that *is* going to be more of a +problem. Either you need to supply a font which contains glyphs for +all the possible CID->GID mappings, or we need to think laterally. + +Our solution using a TrueType CIDFont is to intervene at the +CIDToGIDMap stage and convert all the CIDs to GID 0. Then we have a +font with just one glyph, the .notdef glyph at GID 0. This is what I'm +looking into now. + +It would also be possible to have a 'PostScript' (ie type 1 outlines) +CIDFont which contained 1 glyph, and a CMap which mapped all character +codes to CID 0. The effect would be the same. + +Its possible (I haven't checked) that the PostScript CIDFont and +associated CMap would be smaller than the TrueType font and associated +CIDToGIDMap. + +--- in a followup --- + +OK there is a small problem there, if I use GID 0 then Acrobat gets +upset about it and complains it cannot extract the font. If I set the +CIDToGIDMap so that all the entries are 1 instead, it's happy. Totally +mad...... + +*/ + +namespace tesseract { + + // Use for PDF object fragments. Must be large enough + // to hold a colormap with 256 colors in the verbose + // PDF representation. + static const int kBasicBufSize = 2048; + + // If the font is 10 pts, nominal character width is 5 pts + static const int kCharWidth = 2; + + // Used for memory allocation. A codepoint must take no more than this + // many bytes, when written in the PDF way. e.g. "<0063>" for the + // letter 'c' + static const int kMaxBytesPerCodepoint = 20; + + /********************************************************************** + * PDF Renderer interface implementation + **********************************************************************/ + + TessPDFRenderer::TessPDFRenderer(const char *outputbase, const char *datadir) + : TessResultRenderer(outputbase, "pdf") { + TessPDFRenderer(outputbase, datadir, false); + } + + TessPDFRenderer::TessPDFRenderer(const char *outputbase, const char *datadir, + bool textonly) + : TessResultRenderer(outputbase, "pdf") { + obj_ = 0; + datadir_ = datadir; + textonly_ = textonly; + offsets_.push_back(0); + } + + void TessPDFRenderer::AppendPDFObjectDIY(size_t objectsize) { + offsets_.push_back(objectsize + offsets_.back()); + obj_++; + } + + void TessPDFRenderer::AppendPDFObject(const char *data) { + AppendPDFObjectDIY(strlen(data)); + AppendString((const char *)data); + } + + // Helper function to prevent us from accidentally writing + // scientific notation to an HOCR or PDF file. Besides, three + // decimal points are all you really need. + double prec(double x) { + double kPrecision = 1000.0; + double a = round(x * kPrecision) / kPrecision; + if (a == -0) + return 0; + return a; + } + + long dist2(int x1, int y1, int x2, int y2) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + } + + // Viewers like evince can get really confused during copy-paste when + // the baseline wanders around. So I've decided to project every word + // onto the (straight) line baseline. All numbers are in the native + // PDF coordinate system, which has the origin in the bottom left and + // the unit is points, which is 1/72 inch. Tesseract reports baselines + // left-to-right no matter what the reading order is. We need the + // word baseline in reading order, so we do that conversion here. Returns + // the word's baseline origin and length. + void GetWordBaseline(int writing_direction, int ppi, int height, + int word_x1, int word_y1, int word_x2, int word_y2, + int line_x1, int line_y1, int line_x2, int line_y2, + double *x0, double *y0, double *length) { + if (writing_direction == WRITING_DIRECTION_RIGHT_TO_LEFT) { + Swap(&word_x1, &word_x2); + Swap(&word_y1, &word_y2); + } + double word_length; + double x, y; + { + int px = word_x1; + int py = word_y1; + double l2 = dist2(line_x1, line_y1, line_x2, line_y2); + if (l2 == 0) { + x = line_x1; + y = line_y1; + } + else { + double t = ((px - line_x2) * (line_x2 - line_x1) + + (py - line_y2) * (line_y2 - line_y1)) / l2; + x = line_x2 + t * (line_x2 - line_x1); + y = line_y2 + t * (line_y2 - line_y1); + } + word_length = sqrt(static_cast(dist2(word_x1, word_y1, + word_x2, word_y2))); + word_length = word_length * 72.0 / ppi; + x = x * 72 / ppi; + y = height - (y * 72.0 / ppi); + } + *x0 = x; + *y0 = y; + *length = word_length; + } + + // Compute coefficients for an affine matrix describing the rotation + // of the text. If the text is right-to-left such as Arabic or Hebrew, + // we reflect over the Y-axis. This matrix will set the coordinate + // system for placing text in the PDF file. + // + // RTL + // [ x' ] = [ a b ][ x ] = [-1 0 ] [ cos sin ][ x ] + // [ y' ] [ c d ][ y ] [ 0 1 ] [-sin cos ][ y ] + void AffineMatrix(int writing_direction, + int line_x1, int line_y1, int line_x2, int line_y2, + double *a, double *b, double *c, double *d) { + double theta = atan2(static_cast(line_y1 - line_y2), + static_cast(line_x2 - line_x1)); + *a = cos(theta); + *b = sin(theta); + *c = -sin(theta); + *d = cos(theta); + switch (writing_direction) { + case WRITING_DIRECTION_RIGHT_TO_LEFT: + *a = -*a; + *b = -*b; + break; + case WRITING_DIRECTION_TOP_TO_BOTTOM: + // TODO(jbreiden) Consider using the vertical PDF writing mode. + break; + default: + break; + } + } + + // There are some really awkward PDF viewers in the wild, such as + // 'Preview' which ships with the Mac. They do a better job with text + // selection and highlighting when given perfectly flat baseline + // instead of very slightly tilted. We clip small tilts to appease + // these viewers. I chose this threshold large enough to absorb noise, + // but small enough that lines probably won't cross each other if the + // whole page is tilted at almost exactly the clipping threshold. + void ClipBaseline(int ppi, int x1, int y1, int x2, int y2, + int *line_x1, int *line_y1, + int *line_x2, int *line_y2) { + *line_x1 = x1; + *line_y1 = y1; + *line_x2 = x2; + *line_y2 = y2; + double rise = abs(y2 - y1) * 72 / ppi; + double run = abs(x2 - x1) * 72 / ppi; + if (rise < 2.0 && 2.0 < run) + *line_y1 = *line_y2 = (y1 + y2) / 2; + } + + bool CodepointToUtf16be(int code, char utf16[kMaxBytesPerCodepoint]) { + if ((code > 0xD7FF && code < 0xE000) || code > 0x10FFFF) { + tprintf("Dropping invalid codepoint %d\n", code); + return false; + } + if (code < 0x10000) { + snprintf(utf16, kMaxBytesPerCodepoint, "%04X", code); + } + else { + int a = code - 0x010000; + int high_surrogate = (0x03FF & (a >> 10)) + 0xD800; + int low_surrogate = (0x03FF & a) + 0xDC00; + snprintf(utf16, kMaxBytesPerCodepoint, + "%04X%04X", high_surrogate, low_surrogate); + } + return true; + } + + char* TessPDFRenderer::GetPDFTextObjects(TessBaseAPI* api, + double width, double height) { + STRING pdf_str(""); + double ppi = api->GetSourceYResolution(); + + // These initial conditions are all arbitrary and will be overwritten + double old_x = 0.0, old_y = 0.0; + int old_fontsize = 0; + tesseract::WritingDirection old_writing_direction = + WRITING_DIRECTION_LEFT_TO_RIGHT; + bool new_block = true; + int fontsize = 0; + double a = 1; + double b = 0; + double c = 0; + double d = 1; + + // TODO(jbreiden) This marries the text and image together. + // Slightly cleaner from an abstraction standpoint if this were to + // live inside a separate text object. + pdf_str += "q "; + pdf_str.add_str_double("", prec(width)); + pdf_str += " 0 0 "; + pdf_str.add_str_double("", prec(height)); + pdf_str += " 0 0 cm"; + if (!textonly_) { + pdf_str += " /Im1 Do"; + } + pdf_str += " Q\n"; + + int line_x1 = 0; + int line_y1 = 0; + int line_x2 = 0; + int line_y2 = 0; + + ResultIterator *res_it = api->GetIterator(); + while (!res_it->Empty(RIL_BLOCK)) { + if (res_it->IsAtBeginningOf(RIL_BLOCK)) { + pdf_str += "BT\n3 Tr"; // Begin text object, use invisible ink + old_fontsize = 0; // Every block will declare its fontsize + new_block = true; // Every block will declare its affine matrix + } + + if (res_it->IsAtBeginningOf(RIL_TEXTLINE)) { + int x1, y1, x2, y2; + res_it->Baseline(RIL_TEXTLINE, &x1, &y1, &x2, &y2); + ClipBaseline(ppi, x1, y1, x2, y2, &line_x1, &line_y1, &line_x2, &line_y2); + } + + if (res_it->Empty(RIL_WORD)) { + res_it->Next(RIL_WORD); + continue; + } + + // Writing direction changes at a per-word granularity + tesseract::WritingDirection writing_direction; + { + tesseract::Orientation orientation; + tesseract::TextlineOrder textline_order; + float deskew_angle; + res_it->Orientation(&orientation, &writing_direction, + &textline_order, &deskew_angle); + if (writing_direction != WRITING_DIRECTION_TOP_TO_BOTTOM) { + switch (res_it->WordDirection()) { + case DIR_LEFT_TO_RIGHT: + writing_direction = WRITING_DIRECTION_LEFT_TO_RIGHT; + break; + case DIR_RIGHT_TO_LEFT: + writing_direction = WRITING_DIRECTION_RIGHT_TO_LEFT; + break; + default: + writing_direction = old_writing_direction; + } + } + } + + // Where is word origin and how long is it? + double x, y, word_length; + { + int word_x1, word_y1, word_x2, word_y2; + res_it->Baseline(RIL_WORD, &word_x1, &word_y1, &word_x2, &word_y2); + GetWordBaseline(writing_direction, ppi, height, + word_x1, word_y1, word_x2, word_y2, + line_x1, line_y1, line_x2, line_y2, + &x, &y, &word_length); + } + + if (writing_direction != old_writing_direction || new_block) { + AffineMatrix(writing_direction, + line_x1, line_y1, line_x2, line_y2, &a, &b, &c, &d); + pdf_str.add_str_double(" ", prec(a)); // . This affine matrix + pdf_str.add_str_double(" ", prec(b)); // . sets the coordinate + pdf_str.add_str_double(" ", prec(c)); // . system for all + pdf_str.add_str_double(" ", prec(d)); // . text that follows. + pdf_str.add_str_double(" ", prec(x)); // . + pdf_str.add_str_double(" ", prec(y)); // . + pdf_str += (" Tm "); // Place cursor absolutely + new_block = false; + } + else { + double dx = x - old_x; + double dy = y - old_y; + pdf_str.add_str_double(" ", prec(dx * a + dy * b)); + pdf_str.add_str_double(" ", prec(dx * c + dy * d)); + pdf_str += (" Td "); // Relative moveto + } + old_x = x; + old_y = y; + old_writing_direction = writing_direction; + + // Adjust font size on a per word granularity. Pay attention to + // fontsize, old_fontsize, and pdf_str. We've found that for + // in Arabic, Tesseract will happily return a fontsize of zero, + // so we make up a default number to protect ourselves. + { + bool bold, italic, underlined, monospace, serif, smallcaps; + int font_id; + res_it->WordFontAttributes(&bold, &italic, &underlined, &monospace, + &serif, &smallcaps, &fontsize, &font_id); + const int kDefaultFontsize = 8; + if (fontsize <= 0) + fontsize = kDefaultFontsize; + if (fontsize != old_fontsize) { + char textfont[20]; + snprintf(textfont, sizeof(textfont), "/f-0-0 %d Tf ", fontsize); + pdf_str += textfont; + old_fontsize = fontsize; + } + } + + bool last_word_in_line = res_it->IsAtFinalElement(RIL_TEXTLINE, RIL_WORD); + bool last_word_in_block = res_it->IsAtFinalElement(RIL_BLOCK, RIL_WORD); + STRING pdf_word(""); + int pdf_word_len = 0; + do { + const char *grapheme = res_it->GetUTF8Text(RIL_SYMBOL); + if (grapheme && grapheme[0] != '\0') { + GenericVector unicodes; + UNICHAR::UTF8ToUnicode(grapheme, &unicodes); + char utf16[kMaxBytesPerCodepoint]; + for (int i = 0; i < unicodes.length(); i++) { + int code = unicodes[i]; + if (CodepointToUtf16be(code, utf16)) { + pdf_word += utf16; + pdf_word_len++; + } + } + } + delete[]grapheme; + res_it->Next(RIL_SYMBOL); + } while (!res_it->Empty(RIL_BLOCK) && !res_it->IsAtBeginningOf(RIL_WORD)); + if (word_length > 0 && pdf_word_len > 0 && fontsize > 0) { + double h_stretch = + kCharWidth * prec(100.0 * word_length / (fontsize * pdf_word_len)); + pdf_str.add_str_double("", h_stretch); + pdf_str += " Tz"; // horizontal stretch + pdf_str += " [ <"; + pdf_str += pdf_word; // UTF-16BE representation + pdf_str += "> ] TJ"; // show the text + } + if (last_word_in_line) { + pdf_str += " \n"; + } + if (last_word_in_block) { + pdf_str += "ET\n"; // end the text object + } + } + char *ret = new char[pdf_str.length() + 1]; + strcpy(ret, pdf_str.string()); + delete res_it; + return ret; + } + + bool TessPDFRenderer::BeginDocumentHandler() { + char buf[kBasicBufSize]; + size_t n; + + n = snprintf(buf, sizeof(buf), + "%%PDF-1.5\n" + "%%%c%c%c%c\n", + 0xDE, 0xAD, 0xBE, 0xEB); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + // CATALOG + n = snprintf(buf, sizeof(buf), + "1 0 obj\n" + "<<\n" + " /Type /Catalog\n" + " /Pages %ld 0 R\n" + ">>\n" + "endobj\n", + 2L); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + // We are reserving object #2 for the /Pages + // object, which I am going to create and write + // at the end of the PDF file. + AppendPDFObject(""); + + // TYPE0 FONT + n = snprintf(buf, sizeof(buf), + "3 0 obj\n" + "<<\n" + " /BaseFont /GlyphLessFont\n" + " /DescendantFonts [ %ld 0 R ]\n" + " /Encoding /Identity-H\n" + " /Subtype /Type0\n" + " /ToUnicode %ld 0 R\n" + " /Type /Font\n" + ">>\n" + "endobj\n", + 4L, // CIDFontType2 font + 6L // ToUnicode + ); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + // CIDFONTTYPE2 + n = snprintf(buf, sizeof(buf), + "4 0 obj\n" + "<<\n" + " /BaseFont /GlyphLessFont\n" + " /CIDToGIDMap %ld 0 R\n" + " /CIDSystemInfo\n" + " <<\n" + " /Ordering (Identity)\n" + " /Registry (Adobe)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %ld 0 R\n" + " /Subtype /CIDFontType2\n" + " /Type /Font\n" + " /DW %d\n" + ">>\n" + "endobj\n", + 5L, // CIDToGIDMap + 7L, // Font descriptor + 1000 / kCharWidth); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + // CIDTOGIDMAP + const int kCIDToGIDMapSize = 2 * (1 << 16); + unsigned char *cidtogidmap = new unsigned char[kCIDToGIDMapSize]; + for (int i = 0; i < kCIDToGIDMapSize; i++) { + cidtogidmap[i] = (i % 2) ? 1 : 0; + } + size_t len; + unsigned char *comp = + zlibCompress(cidtogidmap, kCIDToGIDMapSize, &len); + + unsigned char aaa[210]; + memcpy(aaa, comp, 210); + delete[] cidtogidmap; + n = snprintf(buf, sizeof(buf), + "5 0 obj\n" + "<<\n" + " /Length %lu /Filter /FlateDecode\n" + ">>\n" + "stream\n", + (unsigned long)len); + if (n >= sizeof(buf)) { + lept_free(comp); + return false; + } + AppendString(buf); + long objsize = strlen(buf); + AppendData(reinterpret_cast(comp), len); + objsize += len; + lept_free(comp); + const char *endstream_endobj = + "endstream\n" + "endobj\n"; + AppendString(endstream_endobj); + objsize += strlen(endstream_endobj); + AppendPDFObjectDIY(objsize); + + const char *stream = + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<<\n" + " /Registry (Adobe)\n" + " /Ordering (UCS)\n" + " /Supplement 0\n" + ">> def\n" + "/CMapName /Adobe-Identify-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n" + "<0000> \n" + "endcodespacerange\n" + "1 beginbfrange\n" + "<0000> <0000>\n" + "endbfrange\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n"; + + // TOUNICODE + n = snprintf(buf, sizeof(buf), + "6 0 obj\n" + "<< /Length %lu >>\n" + "stream\n" + "%s" + "endstream\n" + "endobj\n", (unsigned long)strlen(stream), stream); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + // FONT DESCRIPTOR + n = snprintf(buf, sizeof(buf), + "7 0 obj\n" + "<<\n" + " /Ascent %d\n" + " /CapHeight %d\n" + " /Descent -1\n" // Spec says must be negative + " /Flags 5\n" // FixedPitch + Symbolic + " /FontBBox [ 0 0 %d %d ]\n" + " /FontFile2 %ld 0 R\n" + " /FontName /GlyphLessFont\n" + " /ItalicAngle 0\n" + " /StemV 80\n" + " /Type /FontDescriptor\n" + ">>\n" + "endobj\n", + 1000, + 1000, + 1000 / kCharWidth, + 1000, + 8L // Font data + ); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + + n = snprintf(buf, sizeof(buf), "%s/pdf.ttf", datadir_); + if (n >= sizeof(buf)) return false; + FILE *fp = fopen("tessdata/pdf.ttf", "rb"); + if (!fp) { + tprintf("Can not open file \"%s\"!\n", buf); + return false; + } + fseek(fp, 0, SEEK_END); + long int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *buffer = new char[size]; + if (fread(buffer, 1, size, fp) != size) { + fclose(fp); + delete[] buffer; + return false; + } + fclose(fp); + // FONTFILE2 + n = snprintf(buf, sizeof(buf), + "8 0 obj\n" + "<<\n" + " /Length %ld\n" + " /Length1 %ld\n" + ">>\n" + "stream\n", size, size); + if (n >= sizeof(buf)) { + delete[] buffer; + return false; + } + AppendString(buf); + objsize = strlen(buf); + AppendData(buffer, size); + delete[] buffer; + objsize += size; + AppendString(endstream_endobj); + objsize += strlen(endstream_endobj); + AppendPDFObjectDIY(objsize); + return true; + } + + bool TessPDFRenderer::imageToPDFObj(Pix *pix, + char *filename, + long int objnum, + char **pdf_object, + long int *pdf_object_size) { + size_t n; + char b0[kBasicBufSize]; + char b1[kBasicBufSize]; + char b2[kBasicBufSize]; + if (!pdf_object_size || !pdf_object) + return false; + *pdf_object = NULL; + *pdf_object_size = 0; + if (!filename) + return false; + + L_Compressed_Data *cid = NULL; + const int kJpegQuality = 85; + + int format, sad; + findFileFormat(filename, &format); + if (pixGetSpp(pix) == 4 && format == IFF_PNG) { + Pix *p1 = pixAlphaBlendUniform(pix, 0xffffff00); + sad = pixGenerateCIData(p1, L_FLATE_ENCODE, 0, 0, &cid); + pixDestroy(&p1); + } + else { + sad = l_generateCIDataForPdf(filename, pix, kJpegQuality, &cid); + } + + if (sad || !cid) { + l_CIDataDestroy(&cid); + return false; + } + + const char *group4 = ""; + const char *filter; + switch (cid->type) { + case L_FLATE_ENCODE: + filter = "/FlateDecode"; + break; + case L_JPEG_ENCODE: + filter = "/DCTDecode"; + break; + case L_G4_ENCODE: + filter = "/CCITTFaxDecode"; + group4 = " /K -1\n"; + break; + case L_JP2K_ENCODE: + filter = "/JPXDecode"; + break; + default: + l_CIDataDestroy(&cid); + return false; + } + + // Maybe someday we will accept RGBA but today is not that day. + // It requires creating an /SMask for the alpha channel. + // http://stackoverflow.com/questions/14220221 + const char *colorspace; + if (cid->ncolors > 0) { + n = snprintf(b0, sizeof(b0), + " /ColorSpace [ /Indexed /DeviceRGB %d %s ]\n", + cid->ncolors - 1, cid->cmapdatahex); + if (n >= sizeof(b0)) { + l_CIDataDestroy(&cid); + return false; + } + colorspace = b0; + } + else { + switch (cid->spp) { + case 1: + colorspace = " /ColorSpace /DeviceGray\n"; + break; + case 3: + colorspace = " /ColorSpace /DeviceRGB\n"; + break; + default: + l_CIDataDestroy(&cid); + return false; + } + } + + int predictor = (cid->predictor) ? 14 : 1; + + // IMAGE + n = snprintf(b1, sizeof(b1), + "%ld 0 obj\n" + "<<\n" + " /Length %ld\n" + " /Subtype /Image\n", + objnum, (unsigned long)cid->nbytescomp); + if (n >= sizeof(b1)) { + l_CIDataDestroy(&cid); + return false; + } + + n = snprintf(b2, sizeof(b2), + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent %d\n" + " /Filter %s\n" + " /DecodeParms\n" + " <<\n" + " /Predictor %d\n" + " /Colors %d\n" + "%s" + " /Columns %d\n" + " /BitsPerComponent %d\n" + " >>\n" + ">>\n" + "stream\n", + cid->w, cid->h, cid->bps, filter, predictor, cid->spp, + group4, cid->w, cid->bps); + if (n >= sizeof(b2)) { + l_CIDataDestroy(&cid); + return false; + } + + const char *b3 = + "endstream\n" + "endobj\n"; + + size_t b1_len = strlen(b1); + size_t b2_len = strlen(b2); + size_t b3_len = strlen(b3); + size_t colorspace_len = strlen(colorspace); + + *pdf_object_size = + b1_len + colorspace_len + b2_len + cid->nbytescomp + b3_len; + *pdf_object = new char[*pdf_object_size]; + + char *p = *pdf_object; + memcpy(p, b1, b1_len); + p += b1_len; + memcpy(p, colorspace, colorspace_len); + p += colorspace_len; + memcpy(p, b2, b2_len); + p += b2_len; + memcpy(p, cid->datacomp, cid->nbytescomp); + p += cid->nbytescomp; + memcpy(p, b3, b3_len); + l_CIDataDestroy(&cid); + return true; + } + + bool TessPDFRenderer::imageToPDFObj(const char* jpgdata, int len, long int objnum, + char **pdf_object, long int *pdf_object_size) + { + size_t n; + char b0[kBasicBufSize]; + char b1[kBasicBufSize]; + char b2[kBasicBufSize]; + if (!pdf_object_size || !pdf_object) + return false; + *pdf_object = NULL; + *pdf_object_size = 0; + + L_Compressed_Data *cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); + char* data = const_cast(jpgdata); + cid->w = 1653; + cid->h = 2338; + cid->res = 200; + cid->bps = 8; + cid->type = L_JPEG_ENCODE; + cid->nbytescomp = len; + cid->datacomp = reinterpret_cast(data); + cid->spp = 1; + + if (!cid) + { + l_CIDataDestroy(&cid); + return false; + } + + const char *group4 = ""; + const char *filter; + switch (cid->type) { + case L_FLATE_ENCODE: + filter = "/FlateDecode"; + break; + case L_JPEG_ENCODE: + filter = "/DCTDecode"; + break; + case L_G4_ENCODE: + filter = "/CCITTFaxDecode"; + group4 = " /K -1\n"; + break; + case L_JP2K_ENCODE: + filter = "/JPXDecode"; + break; + default: + l_CIDataDestroy(&cid); + return false; + } + + // Maybe someday we will accept RGBA but today is not that day. + // It requires creating an /SMask for the alpha channel. + // http://stackoverflow.com/questions/14220221 + const char *colorspace; + if (cid->ncolors > 0) { + n = snprintf(b0, sizeof(b0), + " /ColorSpace [ /Indexed /DeviceRGB %d %s ]\n", + cid->ncolors - 1, cid->cmapdatahex); + if (n >= sizeof(b0)) { + l_CIDataDestroy(&cid); + return false; + } + colorspace = b0; + } + else { + switch (cid->spp) { + case 1: + colorspace = " /ColorSpace /DeviceGray\n"; + break; + case 3: + colorspace = " /ColorSpace /DeviceRGB\n"; + break; + default: + l_CIDataDestroy(&cid); + return false; + } + } + + int predictor = (cid->predictor) ? 14 : 1; + + // IMAGE + n = snprintf(b1, sizeof(b1), + "%ld 0 obj\n" + "<<\n" + " /Length %ld\n" + " /Subtype /Image\n", + objnum, (unsigned long)cid->nbytescomp); + if (n >= sizeof(b1)) { + l_CIDataDestroy(&cid); + return false; + } + + n = snprintf(b2, sizeof(b2), + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent %d\n" + " /Filter %s\n" + " /DecodeParms\n" + " <<\n" + " /Predictor %d\n" + " /Colors %d\n" + "%s" + " /Columns %d\n" + " /BitsPerComponent %d\n" + " >>\n" + ">>\n" + "stream\n", + cid->w, cid->h, cid->bps, filter, predictor, cid->spp, + group4, cid->w, cid->bps); + if (n >= sizeof(b2)) { + l_CIDataDestroy(&cid); + return false; + } + + const char *b3 = + "endstream\n" + "endobj\n"; + + size_t b1_len = strlen(b1); + size_t b2_len = strlen(b2); + size_t b3_len = strlen(b3); + size_t colorspace_len = strlen(colorspace); + + *pdf_object_size = + b1_len + colorspace_len + b2_len + cid->nbytescomp + b3_len; + *pdf_object = new char[*pdf_object_size]; + + char *p = *pdf_object; + memcpy(p, b1, b1_len); + p += b1_len; + memcpy(p, colorspace, colorspace_len); + p += colorspace_len; + memcpy(p, b2, b2_len); + p += b2_len; + memcpy(p, cid->datacomp, cid->nbytescomp); + p += cid->nbytescomp; + memcpy(p, b3, b3_len); + l_CIDataDestroy(&cid); + return true; + } + + bool TessPDFRenderer::AddImageHandler(TessBaseAPI* api) { + size_t n; + char buf[kBasicBufSize]; + char buf2[kBasicBufSize]; + Pix *pix = api->GetInputImage(); + char *filename = (char *)api->GetInputName(); + int ppi = api->GetSourceYResolution(); + if (!pix || ppi <= 0) + return false; + double width = pixGetWidth(pix) * 72.0 / ppi; + double height = pixGetHeight(pix) * 72.0 / ppi; + + snprintf(buf2, sizeof(buf2), "/XObject << /Im1 %ld 0 R >>\n", obj_ + 2); + const char *xobject = (textonly_) ? "" : buf2; + + // PAGE + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Type /Page\n" + " /Parent %ld 0 R\n" + " /MediaBox [0 0 %.2f %.2f]\n" + " /Contents %ld 0 R\n" + " /Resources\n" + " <<\n" + " %s" + " /ProcSet [ /PDF /Text /ImageB /ImageI /ImageC ]\n" + " /Font << /f-0-0 %ld 0 R >>\n" + " >>\n" + ">>\n" + "endobj\n", + obj_, + 2L, // Pages object + width, height, + obj_ + 1, // Contents object + xobject, // Image object + 3L); // Type0 Font + if (n >= sizeof(buf)) return false; + pages_.push_back(obj_); + AppendPDFObject(buf); + + // CONTENTS + char* pdftext = GetPDFTextObjects(api, width, height); + long pdftext_len = strlen(pdftext); + unsigned char *pdftext_casted = reinterpret_cast(pdftext); + size_t len; + unsigned char *comp_pdftext = + zlibCompress(pdftext_casted, pdftext_len, &len); + long comp_pdftext_len = len; + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Length %ld /Filter /FlateDecode\n" + ">>\n" + "stream\n", obj_, comp_pdftext_len); + if (n >= sizeof(buf)) { + delete[] pdftext; + lept_free(comp_pdftext); + return false; + } + AppendString(buf); + long objsize = strlen(buf); + AppendData(reinterpret_cast(comp_pdftext), comp_pdftext_len); + objsize += comp_pdftext_len; + lept_free(comp_pdftext); + delete[] pdftext; + const char *b2 = + "endstream\n" + "endobj\n"; + AppendString(b2); + objsize += strlen(b2); + AppendPDFObjectDIY(objsize); + + if (!textonly_) { + char *pdf_object = NULL; + if (!imageToPDFObj(pix, filename, obj_, &pdf_object, &objsize)) { + return false; + } + AppendData(pdf_object, objsize); + AppendPDFObjectDIY(objsize); + delete[] pdf_object; + } + return true; + } + + bool TessPDFRenderer::AddImageHandler(TessBaseAPI * api, const char * jpgdata, int length) + { + size_t n; + char buf[kBasicBufSize]; + char buf2[kBasicBufSize]; + Pix *pix = api->GetInputImage(); + char *filename = (char *)api->GetInputName(); + int ppi = api->GetSourceYResolution(); + if (!pix || ppi <= 0) + return false; + double width = pixGetWidth(pix) * 72.0 / ppi; + double height = pixGetHeight(pix) * 72.0 / ppi; + + snprintf(buf2, sizeof(buf2), "/XObject << /Im1 %ld 0 R >>\n", obj_ + 2); + const char *xobject = (textonly_) ? "" : buf2; + + // PAGE + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Type /Page\n" + " /Parent %ld 0 R\n" + " /MediaBox [0 0 %.2f %.2f]\n" + " /Contents %ld 0 R\n" + " /Resources\n" + " <<\n" + " %s" + " /ProcSet [ /PDF /Text /ImageB /ImageI /ImageC ]\n" + " /Font << /f-0-0 %ld 0 R >>\n" + " >>\n" + ">>\n" + "endobj\n", + obj_, + 2L, // Pages object + width, height, + obj_ + 1, // Contents object + xobject, // Image object + 3L); // Type0 Font + if (n >= sizeof(buf)) return false; + pages_.push_back(obj_); + AppendPDFObject(buf); + + // CONTENTS + char* pdftext = GetPDFTextObjects(api, width, height); + long pdftext_len = strlen(pdftext); + unsigned char *pdftext_casted = reinterpret_cast(pdftext); + size_t len; + unsigned char *comp_pdftext = + zlibCompress(pdftext_casted, pdftext_len, &len); + long comp_pdftext_len = len; + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Length %ld /Filter /FlateDecode\n" + ">>\n" + "stream\n", obj_, comp_pdftext_len); + if (n >= sizeof(buf)) { + delete[] pdftext; + lept_free(comp_pdftext); + return false; + } + AppendString(buf); + long objsize = strlen(buf); + AppendData(reinterpret_cast(comp_pdftext), comp_pdftext_len); + objsize += comp_pdftext_len; + lept_free(comp_pdftext); + delete[] pdftext; + const char *b2 = + "endstream\n" + "endobj\n"; + AppendString(b2); + objsize += strlen(b2); + AppendPDFObjectDIY(objsize); + + if (!textonly_) { + char *pdf_object = NULL; + if (!imageToPDFObj(jpgdata, length, obj_, &pdf_object, &objsize)) { + return false; + } + AppendData(pdf_object, objsize); + AppendPDFObjectDIY(objsize); + delete[] pdf_object; + } + return true; + } + + bool TessPDFRenderer::EndDocumentHandler() { + size_t n; + char buf[kBasicBufSize]; + + // We reserved the /Pages object number early, so that the /Page + // objects could refer to their parent. We finally have enough + // information to go fill it in. Using lower level calls to manipulate + // the offset record in two spots, because we are placing objects + // out of order in the file. + + // PAGES + const long int kPagesObjectNumber = 2; + offsets_[kPagesObjectNumber] = offsets_.back(); // manipulation #1 + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Type /Pages\n" + " /Kids [ ", kPagesObjectNumber); + if (n >= sizeof(buf)) return false; + AppendString(buf); + size_t pages_objsize = strlen(buf); + for (size_t i = 0; i < pages_.size(); i++) { + n = snprintf(buf, sizeof(buf), + "%ld 0 R ", pages_[i]); + if (n >= sizeof(buf)) return false; + AppendString(buf); + pages_objsize += strlen(buf); + } + n = snprintf(buf, sizeof(buf), + "]\n" + " /Count %d\n" + ">>\n" + "endobj\n", pages_.size()); + if (n >= sizeof(buf)) return false; + AppendString(buf); + pages_objsize += strlen(buf); + offsets_.back() += pages_objsize; // manipulation #2 + + // INFO + STRING utf16_title = "FEFF"; // byte_order_marker + GenericVector unicodes; + UNICHAR::UTF8ToUnicode(title(), &unicodes); + char utf16[kMaxBytesPerCodepoint]; + for (int i = 0; i < unicodes.length(); i++) { + int code = unicodes[i]; + if (CodepointToUtf16be(code, utf16)) { + utf16_title += utf16; + } + } + + char* datestr = l_getFormattedDate(); + n = snprintf(buf, sizeof(buf), + "%ld 0 obj\n" + "<<\n" + " /Producer (Tesseract %s)\n" + " /CreationDate (D:%s)\n" + " /Title <%s>\n" + ">>\n" + "endobj\n", + obj_, TESSERACT_VERSION_STR, datestr, utf16_title.c_str()); + lept_free(datestr); + if (n >= sizeof(buf)) return false; + AppendPDFObject(buf); + n = snprintf(buf, sizeof(buf), + "xref\n" + "0 %ld\n" + "0000000000 65535 f \n", obj_); + if (n >= sizeof(buf)) return false; + AppendString(buf); + for (int i = 1; i < obj_; i++) { + n = snprintf(buf, sizeof(buf), "%010ld 00000 n \n", offsets_[i]); + if (n >= sizeof(buf)) return false; + AppendString(buf); + } + n = snprintf(buf, sizeof(buf), + "trailer\n" + "<<\n" + " /Size %ld\n" + " /Root %ld 0 R\n" + " /Info %ld 0 R\n" + ">>\n" + "startxref\n" + "%ld\n" + "%%%%EOF\n", + obj_, + 1L, // catalog + obj_ - 1, // info + offsets_.back()); + if (n >= sizeof(buf)) return false; + AppendString(buf); + return true; + } +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/api/renderer.cpp b/hgdriver/3rdparty/hgOCR/include/api/renderer.cpp new file mode 100644 index 0000000..1f6d3e2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/renderer.cpp @@ -0,0 +1,283 @@ +/////////////////////////////////////////////////////////////////////// +// File: renderer.cpp +// Description: Rendering interface to inject into TessBaseAPI +// +// (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. +// +/////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include +#include "baseapi.h" +#include "genericvector.h" +#include "renderer.h" + +namespace tesseract { + + /********************************************************************** + * Base Renderer interface implementation + **********************************************************************/ + TessResultRenderer::TessResultRenderer(const char *outputbase, + const char* extension) + : file_extension_(extension), + title_(""), imagenum_(-1), + fout_(stdout), + next_(NULL), + happy_(true) { + if (strcmp(outputbase, "-") && strcmp(outputbase, "stdout")) { + STRING outfile = STRING(outputbase) + STRING(".") + STRING(file_extension_); + fout_ = fopen(outfile.string(), "wb"); + if (fout_ == NULL) { + happy_ = false; + } + } + } + + TessResultRenderer::~TessResultRenderer() { + if (fout_ != NULL) { + if (fout_ != stdout) + fclose(fout_); + else + clearerr(fout_); + } + delete next_; + } + + void TessResultRenderer::insert(TessResultRenderer* next) { + if (next == NULL) return; + + TessResultRenderer* remainder = next_; + next_ = next; + if (remainder) { + while (next->next_ != NULL) { + next = next->next_; + } + next->next_ = remainder; + } + } + + bool TessResultRenderer::BeginDocument(const char* title) { + if (!happy_) return false; + title_ = title; + imagenum_ = -1; + bool ok = BeginDocumentHandler(); + if (next_) { + ok = next_->BeginDocument(title) && ok; + } + return ok; + } + + bool TessResultRenderer::AddImage(TessBaseAPI* api, const char* jpgdata, int len) { + if (!happy_) return false; + ++imagenum_; + bool ok = AddImageHandler(api, jpgdata, len); + if (next_) { + ok = next_->AddImage(api, jpgdata, len) && ok; + } + return ok; + } + + bool TessResultRenderer::EndDocument() { + if (!happy_) return false; + bool ok = EndDocumentHandler(); + if (next_) { + ok = next_->EndDocument() && ok; + } + return ok; + } + + void TessResultRenderer::AppendString(const char* s) { + AppendData(s, strlen(s)); + } + + void TessResultRenderer::AppendData(const char* s, int len) { + int n = fwrite(s, 1, len, fout_); + if (n != len) happy_ = false; + } + + bool TessResultRenderer::BeginDocumentHandler() { + return happy_; + } + + bool TessResultRenderer::EndDocumentHandler() { + return happy_; + } + + + /********************************************************************** + * UTF8 Text Renderer interface implementation + **********************************************************************/ + TessTextRenderer::TessTextRenderer(const char *outputbase) + : TessResultRenderer(outputbase, "txt") { + } + + bool TessTextRenderer::AddImageHandler(TessBaseAPI* api) { + char* utf8 = api->GetUTF8Text(); + if (utf8 == NULL) { + return false; + } + + AppendString(utf8); + delete[] utf8; + + bool pageBreak = false; + api->GetBoolVariable("include_page_breaks", &pageBreak); + const char* pageSeparator = api->GetStringVariable("page_separator"); + if (pageBreak) { + AppendString(pageSeparator); + } + + return true; + } + + /********************************************************************** + * HOcr Text Renderer interface implementation + **********************************************************************/ + TessHOcrRenderer::TessHOcrRenderer(const char *outputbase) + : TessResultRenderer(outputbase, "hocr") { + font_info_ = false; + } + + TessHOcrRenderer::TessHOcrRenderer(const char *outputbase, bool font_info) + : TessResultRenderer(outputbase, "hocr") { + font_info_ = font_info; + } + + bool TessHOcrRenderer::BeginDocumentHandler() { + AppendString( + "\n" + "\n" + "\n \n "); + AppendString(title()); + AppendString( + "\n" + "\n" + " \n" + " \n" + "\n\n"); + + return true; + } + + bool TessHOcrRenderer::EndDocumentHandler() { + AppendString(" \n\n"); + + return true; + } + + bool TessHOcrRenderer::AddImageHandler(TessBaseAPI* api) { + char* hocr = api->GetHOCRText(imagenum()); + if (hocr == NULL) return false; + + AppendString(hocr); + delete[] hocr; + + return true; + } + + /********************************************************************** + * TSV Text Renderer interface implementation + **********************************************************************/ + TessTsvRenderer::TessTsvRenderer(const char* outputbase) + : TessResultRenderer(outputbase, "tsv") { + font_info_ = false; + } + + TessTsvRenderer::TessTsvRenderer(const char* outputbase, bool font_info) + : TessResultRenderer(outputbase, "tsv") { + font_info_ = font_info; + } + + bool TessTsvRenderer::BeginDocumentHandler() { + // Output TSV column headings + AppendString( + "level\tpage_num\tblock_num\tpar_num\tline_num\tword_" + "num\tleft\ttop\twidth\theight\tconf\ttext\n"); + return true; + } + + bool TessTsvRenderer::EndDocumentHandler() { return true; } + + bool TessTsvRenderer::AddImageHandler(TessBaseAPI* api) { + char* tsv = api->GetTSVText(imagenum()); + if (tsv == NULL) return false; + + AppendString(tsv); + delete[] tsv; + + return true; + } + + /********************************************************************** + * UNLV Text Renderer interface implementation + **********************************************************************/ + TessUnlvRenderer::TessUnlvRenderer(const char *outputbase) + : TessResultRenderer(outputbase, "unlv") { + } + + bool TessUnlvRenderer::AddImageHandler(TessBaseAPI* api) { + char* unlv = api->GetUNLVText(); + if (unlv == NULL) return false; + + AppendString(unlv); + delete[] unlv; + + return true; + } + + /********************************************************************** + * BoxText Renderer interface implementation + **********************************************************************/ + TessBoxTextRenderer::TessBoxTextRenderer(const char *outputbase) + : TessResultRenderer(outputbase, "box") { + } + + bool TessBoxTextRenderer::AddImageHandler(TessBaseAPI* api) { + char* text = api->GetBoxText(imagenum()); + if (text == NULL) return false; + + AppendString(text); + delete[] text; + + return true; + } + + /********************************************************************** + * Osd Text Renderer interface implementation + **********************************************************************/ + TessOsdRenderer::TessOsdRenderer(const char* outputbase) + : TessResultRenderer(outputbase, "osd") {} + + bool TessOsdRenderer::AddImageHandler(TessBaseAPI* api) { + char* osd = api->GetOsdText(imagenum()); + if (osd == NULL) return false; + + AppendString(osd); + delete[] osd; + + return true; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/api/renderer.h b/hgdriver/3rdparty/hgOCR/include/api/renderer.h new file mode 100644 index 0000000..c3b4af2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/renderer.h @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////// +// File: renderer.h +// Description: Rendering interface to inject into TessBaseAPI +// +// (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_API_RENDERER_H_ +#define TESSERACT_API_RENDERER_H_ + +// To avoid collision with other typenames include the ABSOLUTE MINIMUM +// complexity of includes here. Use forward declarations wherever possible +// and hide includes of complex types in baseapi.cpp. +#include "genericvector.h" +#include "platform.h" +#include "publictypes.h" + +namespace tesseract { + + class TessBaseAPI; + + /** + * Interface for rendering tesseract results into a document, such as text, + * HOCR or pdf. This class is abstract. Specific classes handle individual + * formats. This interface is then used to inject the renderer class into + * tesseract when processing images. + * + * For simplicity implementing this with tesesract version 3.01, + * the renderer contains document state that is cleared from document + * to document just as the TessBaseAPI is. This way the base API can just + * delegate its rendering functionality to injected renderers, and the + * renderers can manage the associated state needed for the specific formats + * in addition to the heuristics for producing it. + */ + class TESS_API TessResultRenderer { + public: + virtual ~TessResultRenderer(); + + // Takes ownership of pointer so must be new'd instance. + // Renderers aren't ordered, but appends the sequences of next parameter + // and existing next(). The renderers should be unique across both lists. + void insert(TessResultRenderer* next); + + // Returns the next renderer or NULL. + TessResultRenderer* next() { return next_; } + + /** + * Starts a new document with the given title. + * This clears the contents of the output data. + * Title should use UTF-8 encoding. + */ + bool BeginDocument(const char* title); + + /** + * Adds the recognized text from the source image to the current document. + * Invalid if BeginDocument not yet called. + * + * Note that this API is a bit weird but is designed to fit into the + * current TessBaseAPI implementation where the api has lots of state + * information that we might want to add in. + */ + bool AddImage(TessBaseAPI * api, const char * jpgdata, int len); + + /** + * Finishes the document and finalizes the output data + * Invalid if BeginDocument not yet called. + */ + bool EndDocument(); + + const char* file_extension() const { return file_extension_; } + const char* title() const { return title_.c_str(); } + + /** + * Returns the index of the last image given to AddImage + * (i.e. images are incremented whether the image succeeded or not) + * + * This is always defined. It means either the number of the + * current image, the last image ended, or in the completed document + * depending on when in the document lifecycle you are looking at it. + * Will return -1 if a document was never started. + */ + int imagenum() const { return imagenum_; } + + protected: + /** + * Called by concrete classes. + * + * outputbase is the name of the output file excluding + * extension. For example, "/path/to/chocolate-chip-cookie-recipe" + * + * extension indicates the file extension to be used for output + * files. For example "pdf" will produce a .pdf file, and "hocr" + * will produce .hocr files. + */ + TessResultRenderer(const char *outputbase, + const char* extension); + + // Hook for specialized handling in BeginDocument() + virtual bool BeginDocumentHandler(); + + // This must be overriden to render the OCR'd results + virtual bool AddImageHandler(TessBaseAPI* api) = 0; + + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) = 0; + + // Hook for specialized handling in EndDocument() + virtual bool EndDocumentHandler(); + + // Renderers can call this to append '\0' terminated strings into + // the output string returned by GetOutput. + // This method will grow the output buffer if needed. + void AppendString(const char* s); + + // Renderers can call this to append binary byte sequences into + // the output string returned by GetOutput. Note that s is not necessarily + // '\0' terminated (and can contain '\0' within it). + // This method will grow the output buffer if needed. + void AppendData(const char* s, int len); + + private: + const char* file_extension_; // standard extension for generated output + STRING title_; // title of document being renderered + int imagenum_; // index of last image added + + FILE* fout_; // output file pointer + TessResultRenderer* next_; // Can link multiple renderers together + bool happy_; // I get grumpy when the disk fills up, etc. + }; + + /** + * Renders tesseract output into a plain UTF-8 text string + */ + class TESS_API TessTextRenderer : public TessResultRenderer { + public: + explicit TessTextRenderer(const char *outputbase); + + protected: + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + }; + + /** + * Renders tesseract output into an hocr text string + */ + class TESS_API TessHOcrRenderer : public TessResultRenderer { + public: + explicit TessHOcrRenderer(const char *outputbase, bool font_info); + explicit TessHOcrRenderer(const char *outputbase); + + protected: + virtual bool BeginDocumentHandler(); + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + virtual bool EndDocumentHandler(); + + private: + bool font_info_; // whether to print font information + }; + + /** + * Renders Tesseract output into a TSV string + */ + class TESS_API TessTsvRenderer : public TessResultRenderer { + public: + explicit TessTsvRenderer(const char* outputbase, bool font_info); + explicit TessTsvRenderer(const char* outputbase); + + protected: + virtual bool BeginDocumentHandler(); + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + virtual bool EndDocumentHandler(); + + private: + bool font_info_; // whether to print font information + }; + + /** + * Renders tesseract output into searchable PDF + */ + class TESS_API TessPDFRenderer : public TessResultRenderer { + public: + // datadir is the location of the TESSDATA. We need it because + // we load a custom PDF font from this location. + TessPDFRenderer(const char* outputbase, const char* datadir); + TessPDFRenderer(const char* outputbase, const char* datadir, bool textonly); + + protected: + + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len); + + virtual bool BeginDocumentHandler(); + + virtual bool AddImageHandler(TessBaseAPI* api); + + virtual bool EndDocumentHandler(); + + private: + // We don't want to have every image in memory at once, + // so we store some metadata as we go along producing + // PDFs one page at a time. At the end, that metadata is + // used to make everything that isn't easily handled in a + // streaming fashion. + long int obj_; // counter for PDF objects + GenericVector offsets_; // offset of every PDF object in bytes + GenericVector pages_; // object number for every /Page object + const char *datadir_; // where to find the custom font + bool textonly_; // skip images if set + // Bookkeeping only. DIY = Do It Yourself. + void AppendPDFObjectDIY(size_t objectsize); + // Bookkeeping + emit data. + void AppendPDFObject(const char *data); + // Create the /Contents object for an entire page. + char* GetPDFTextObjects(TessBaseAPI* api, double width, double height); + // Turn an image into a PDF object. Only transcode if we have to. + static bool imageToPDFObj(Pix *pix, char *filename, long int objnum, + char **pdf_object, long int *pdf_object_size); + static bool imageToPDFObj(const char* jpgdata, int len, long int objnum, + char **pdf_object, long int *pdf_object_size); + }; + + + /** + * Renders tesseract output into a plain UTF-8 text string + */ + class TESS_API TessUnlvRenderer : public TessResultRenderer { + public: + explicit TessUnlvRenderer(const char *outputbase); + + protected: + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + }; + + /** + * Renders tesseract output into a plain UTF-8 text string + */ + class TESS_API TessBoxTextRenderer : public TessResultRenderer { + public: + explicit TessBoxTextRenderer(const char *outputbase); + + protected: + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + }; + + /** + * Renders tesseract output into an osd text string + */ + class TESS_API TessOsdRenderer : public TessResultRenderer { + public: + explicit TessOsdRenderer(const char* outputbase); + + protected: + virtual bool AddImageHandler(TessBaseAPI* api); + virtual bool AddImageHandler(TessBaseAPI* api, const char* jpgdata, int len) { return false; } + }; + +} // namespace tesseract. + +#endif // TESSERACT_API_RENDERER_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/api/tesseractmain.cpp b/hgdriver/3rdparty/hgOCR/include/api/tesseractmain.cpp new file mode 100644 index 0000000..e320ef3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/api/tesseractmain.cpp @@ -0,0 +1,546 @@ +/********************************************************************** +* File: tessedit.cpp (Formerly tessedit.c) +* Description: Main program for merge of tess and editor. +* Author: Ray Smith +* Created: Tue Jan 07 15:21:46 GMT 1992 +* +* (C) Copyright 1992, Hewlett-Packard Ltd. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +* +**********************************************************************/ + +// Include automatically generated configuration file if running autoconf +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include + +#include "allheaders.h" +#include "baseapi.h" +#include "basedir.h" +#include "dict.h" +#include "openclwrapper.h" +#include "osdetect.h" +#include "renderer.h" +#include "strngs.h" +#include "tprintf.h" +#include "StopWatch.h" + +#if defined(HAVE_TIFFIO_H) && defined(_WIN32) + +#include + +static void Win32WarningHandler(const char* module, const char* fmt, + va_list ap) { + if (module != NULL) { + fprintf(stderr, "%s: ", module); + } + fprintf(stderr, "Warning, "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} + +#endif /* HAVE_TIFFIO_H && _WIN32 */ + +void PrintVersionInfo() { + char* versionStrP; + + printf("tesseract %s\n", tesseract::TessBaseAPI::Version()); + + versionStrP = getLeptonicaVersion(); + printf(" %s\n", versionStrP); + lept_free(versionStrP); + + versionStrP = getImagelibVersions(); + printf(" %s\n", versionStrP); + lept_free(versionStrP); + +#ifdef USE_OPENCL + cl_platform_id platform[4]; + cl_uint num_platforms; + + printf(" OpenCL info:\n"); + if (clGetPlatformIDs(4, platform, &num_platforms) == CL_SUCCESS) { + printf(" Found %u platform(s).\n", num_platforms); + for (unsigned n = 0; n < num_platforms; n++) { + char info[256]; + if (clGetPlatformInfo(platform[n], CL_PLATFORM_NAME, 256, info, 0) == + CL_SUCCESS) { + printf(" Platform %u name: %s.\n", n + 1, info); + } + if (clGetPlatformInfo(platform[n], CL_PLATFORM_VERSION, 256, info, 0) == + CL_SUCCESS) { + printf(" Version: %s.\n", info); + } + cl_device_id devices[2]; + cl_uint num_devices; + if (clGetDeviceIDs(platform[n], CL_DEVICE_TYPE_ALL, 2, devices, + &num_devices) == CL_SUCCESS) { + printf(" Found %u device(s).\n", num_devices); + for (unsigned i = 0; i < num_devices; ++i) { + if (clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 256, info, 0) == + CL_SUCCESS) { + printf(" Device %u name: %s.\n", i + 1, info); + } + } + } + } + } +#endif +} + +void PrintUsage(const char* program) { + printf( + "Usage:\n" + " %s --help | --help-psm | --help-oem | --version\n" + " %s --list-langs [--tessdata-dir PATH]\n" + " %s --print-parameters [options...] [configfile...]\n" + " %s imagename|stdin outputbase|stdout [options...] [configfile...]\n", + program, program, program, program); +} + +void PrintHelpForPSM() { + const char* msg = + "Page segmentation modes:\n" + " 0 Orientation and script detection (OSD) only.\n" + " 1 Automatic page segmentation with OSD.\n" + " 2 Automatic page segmentation, but no OSD, or OCR.\n" + " 3 Fully automatic page segmentation, but no OSD. (Default)\n" + " 4 Assume a single column of text of variable sizes.\n" + " 5 Assume a single uniform block of vertically aligned text.\n" + " 6 Assume a single uniform block of text.\n" + " 7 Treat the image as a single text line.\n" + " 8 Treat the image as a single word.\n" + " 9 Treat the image as a single word in a circle.\n" + " 10 Treat the image as a single character.\n" + " 11 Sparse text. Find as much text as possible in no" + " particular order.\n" + " 12 Sparse text with OSD.\n" + " 13 Raw line. Treat the image as a single text line,\n" + "\t\t\tbypassing hacks that are Tesseract-specific.\n"; + + printf("%s", msg); +} + +void PrintHelpForOEM() { + const char* msg = + "OCR Engine modes:\n" + " 0 Original Tesseract only.\n" + " 1 Cube only.\n" + " 2 Tesseract + cube.\n" + " 3 Default, based on what is available.\n"; + + printf("%s", msg); +} + +void PrintHelpMessage(const char* program) { + PrintUsage(program); + + const char* ocr_options = + "OCR options:\n" + " --tessdata-dir PATH Specify the location of tessdata path.\n" + " --user-words PATH Specify the location of user words file.\n" + " --user-patterns PATH Specify the location of user patterns file.\n" + " -l LANG[+LANG] Specify language(s) used for OCR.\n" + " -c VAR=VALUE Set value for config variables.\n" + " Multiple -c arguments are allowed.\n" + " --psm NUM Specify page segmentation mode.\n" + " --oem NUM Specify OCR Engine mode.\n" + "NOTE: These options must occur before any configfile.\n"; + + printf("\n%s\n", ocr_options); + PrintHelpForPSM(); + PrintHelpForOEM(); + + const char* single_options = + "Single options:\n" + " -h, --help Show this help message.\n" + " --help-psm Show page segmentation modes.\n" + " --help-oem Show OCR Engine modes.\n" + " -v, --version Show version information.\n" + " --list-langs List available languages for tesseract engine.\n" + " --print-parameters Print tesseract parameters to stdout.\n"; + + printf("\n%s", single_options); +} + +void SetVariablesFromCLArgs(tesseract::TessBaseAPI* api, int argc, + char** argv) { + char opt1[256], opt2[255]; + for (int i = 0; i < argc; i++) { + if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { + strncpy(opt1, argv[i + 1], 255); + opt1[255] = '\0'; + char* p = strchr(opt1, '='); + if (!p) { + fprintf(stderr, "Missing = in configvar assignment\n"); + exit(1); + } + *p = 0; + strncpy(opt2, strchr(argv[i + 1], '=') + 1, 255); + opt2[254] = 0; + ++i; + + if (!api->SetVariable(opt1, opt2)) { + fprintf(stderr, "Could not set option: %s=%s\n", opt1, opt2); + } + } + } +} + +void PrintLangsList(tesseract::TessBaseAPI* api) { + GenericVector languages; + api->GetAvailableLanguagesAsVector(&languages); + printf("List of available languages (%d):\n", languages.size()); + for (int index = 0; index < languages.size(); ++index) { + STRING& string = languages[index]; + printf("%s\n", string.string()); + } + api->End(); +} + +void PrintBanner() { + tprintf("Tesseract Open Source OCR Engine v%s with Leptonica\n", + tesseract::TessBaseAPI::Version()); +} + +/** + * We have 2 possible sources of pagesegmode: a config file and + * the command line. For backwards compatibility reasons, the + * default in tesseract is tesseract::PSM_SINGLE_BLOCK, but the + * default for this program is tesseract::PSM_AUTO. We will let + * the config file take priority, so the command-line default + * can take priority over the tesseract default, so we use the + * value from the command line only if the retrieved mode + * is still tesseract::PSM_SINGLE_BLOCK, indicating no change + * in any config file. Therefore the only way to force + * tesseract::PSM_SINGLE_BLOCK is from the command line. + * It would be simpler if we could set the value before Init, + * but that doesn't work. + */ +void FixPageSegMode(tesseract::TessBaseAPI* api, + tesseract::PageSegMode pagesegmode) { + if (api->GetPageSegMode() == tesseract::PSM_SINGLE_BLOCK) + api->SetPageSegMode(pagesegmode); +} + +// NOTE: arg_i is used here to avoid ugly *i so many times in this function +void ParseArgs(const int argc, char** argv, const char** lang, + const char** image, const char** outputbase, + const char** datapath, bool* list_langs, bool* print_parameters, + GenericVector* vars_vec, + GenericVector* vars_values, int* arg_i, + tesseract::PageSegMode* pagesegmode, + tesseract::OcrEngineMode* enginemode) { + if (argc == 1) { + PrintHelpMessage(argv[0]); + exit(0); + } + + if (argc == 2) { + if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { + PrintHelpMessage(argv[0]); + exit(0); + } + if ((strcmp(argv[1], "--help-psm") == 0)) { + PrintHelpForPSM(); + exit(0); + } + if ((strcmp(argv[1], "--help-oem") == 0)) { + PrintHelpForOEM(); + exit(0); + } + if ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "--version") == 0)) { + PrintVersionInfo(); + exit(0); + } + } + + bool noocr = false; + int i = 1; + while (i < argc && (*outputbase == NULL || argv[i][0] == '-')) { + if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { + *lang = argv[i + 1]; + ++i; + } + else if (strcmp(argv[i], "--tessdata-dir") == 0 && i + 1 < argc) { + *datapath = argv[i + 1]; + ++i; + } + else if (strcmp(argv[i], "--user-words") == 0 && i + 1 < argc) { + vars_vec->push_back("user_words_file"); + vars_values->push_back(argv[i + 1]); + ++i; + } + else if (strcmp(argv[i], "--user-patterns") == 0 && i + 1 < argc) { + vars_vec->push_back("user_patterns_file"); + vars_values->push_back(argv[i + 1]); + ++i; + } + else if (strcmp(argv[i], "--list-langs") == 0) { + noocr = true; + *list_langs = true; + } + else if (strcmp(argv[i], "-psm") == 0 && i + 1 < argc) { + // The parameter -psm is deprecated and was replaced by --psm. + // It is still supported for compatibility reasons. + *pagesegmode = static_cast(atoi(argv[i + 1])); + ++i; + } + else if (strcmp(argv[i], "--psm") == 0 && i + 1 < argc) { + *pagesegmode = static_cast(atoi(argv[i + 1])); + ++i; + } + else if (strcmp(argv[i], "--oem") == 0 && i + 1 < argc) { + *enginemode = static_cast(atoi(argv[i + 1])); + ++i; + } + else if (strcmp(argv[i], "--print-parameters") == 0) { + noocr = true; + *print_parameters = true; + } + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { + // handled properly after api init + ++i; + } + else if (*image == NULL) { + *image = argv[i]; + } + else if (*outputbase == NULL) { + *outputbase = argv[i]; + } + ++i; + } + + *arg_i = i; + + if (argc == 2 && strcmp(argv[1], "--list-langs") == 0) { + *list_langs = true; + noocr = true; + } + + if (*outputbase == NULL && noocr == false) { + PrintHelpMessage(argv[0]); + exit(1); + } +} + +void PreloadRenderers( + tesseract::TessBaseAPI* api, + tesseract::PointerVector* renderers, + tesseract::PageSegMode pagesegmode, const char* outputbase) { + if (pagesegmode == tesseract::PSM_OSD_ONLY) { + renderers->push_back(new tesseract::TessOsdRenderer(outputbase)); + } + else { + bool b; + api->GetBoolVariable("tessedit_create_hocr", &b); + if (b) { + bool font_info; + api->GetBoolVariable("hocr_font_info", &font_info); + renderers->push_back( + new tesseract::TessHOcrRenderer(outputbase, font_info)); + } + + api->GetBoolVariable("tessedit_create_tsv", &b); + if (b) { + bool font_info; + api->GetBoolVariable("hocr_font_info", &font_info); + renderers->push_back( + new tesseract::TessTsvRenderer(outputbase, font_info)); + } + + api->GetBoolVariable("tessedit_create_pdf", &b); + if (b) { + bool textonly; + api->GetBoolVariable("textonly_pdf", &textonly); + renderers->push_back(new tesseract::TessPDFRenderer( + outputbase, api->GetDatapath(), textonly)); + } + + api->GetBoolVariable("tessedit_write_unlv", &b); + if (b) { + renderers->push_back(new tesseract::TessUnlvRenderer(outputbase)); + } + + api->GetBoolVariable("tessedit_create_boxfile", &b); + if (b) { + renderers->push_back(new tesseract::TessBoxTextRenderer(outputbase)); + } + + api->GetBoolVariable("tessedit_create_txt", &b); + if (b || renderers->empty()) { + renderers->push_back(new tesseract::TessTextRenderer(outputbase)); + } + } + + if (!renderers->empty()) { + // Since the PointerVector auto-deletes, null-out the renderers that are + // added to the root, and leave the root in the vector. + for (int r = 1; r < renderers->size(); ++r) { + (*renderers)[0]->insert((*renderers)[r]); + (*renderers)[r] = NULL; + } + } +} + +/********************************************************************** + * main() + * + **********************************************************************/ + +int main(int argc, char** argv) { + const char* lang = "osd"; + const char* image = NULL; + const char* outputbase = NULL; + const char* datapath = NULL; + bool list_langs = false; + bool print_parameters = false; + int arg_i = 1; + tesseract::PageSegMode pagesegmode = tesseract::PSM_AUTO_OSD; + tesseract::OcrEngineMode enginemode = tesseract::OEM_DEFAULT; + /* main() calls functions like ParseArgs which call exit(). + * This results in memory leaks if vars_vec and vars_values are + * declared as auto variables (destructor is not called then). */ + static GenericVector vars_vec; + static GenericVector vars_values; + +#ifdef NDEBUG + // Disable debugging and informational messages from Leptonica. + setMsgSeverity(L_SEVERITY_ERROR); +#endif + +#if defined(HAVE_TIFFIO_H) && defined(_WIN32) + /* Show libtiff warnings on console (not in GUI). */ + TIFFSetWarningHandler(Win32WarningHandler); +#endif /* HAVE_TIFFIO_H && _WIN32 */ + + ParseArgs(argc, argv, &lang, &image, &outputbase, &datapath, &list_langs, + &print_parameters, &vars_vec, &vars_values, &arg_i, &pagesegmode, + &enginemode); + + bool banner = false; + if (outputbase != NULL && strcmp(outputbase, "-") && + strcmp(outputbase, "stdout")) { + banner = true; + } + + PERF_COUNT_START("Tesseract:main") + + // Call GlobalDawgCache here to create the global DawgCache object before + // the TessBaseAPI object. This fixes the order of destructor calls: + // first TessBaseAPI must be destructed, DawgCache must be the last object. + tesseract::Dict::GlobalDawgCache(); + + // Avoid memory leak caused by auto variable when exit() is called. + static tesseract::TessBaseAPI api; + + api.SetOutputName(outputbase); + + int init_failed = api.Init(datapath, lang, enginemode, &(argv[arg_i]), + argc - arg_i, &vars_vec, &vars_values, false); + if (init_failed) { + fprintf(stderr, "Could not initialize tesseract.\n"); + getchar(); + return EXIT_FAILURE; + } + + SetVariablesFromCLArgs(&api, argc, argv); + + if (list_langs) { + PrintLangsList(&api); + getchar(); + return EXIT_SUCCESS; + } + + if (print_parameters) { + FILE* fout = stdout; + fprintf(stdout, "Tesseract parameters:\n"); + api.PrintVariables(fout); + api.End(); + getchar(); + return EXIT_SUCCESS; + } + + FixPageSegMode(&api, pagesegmode); + + if (pagesegmode == tesseract::PSM_AUTO_OSD) { + int ret_val = EXIT_SUCCESS; + + Pix* pixs = pixRead(image); + if (!pixs) { + fprintf(stderr, "Cannot open input file: %s\n", image); + getchar(); + return 2; + } + + api.SetImage(pixs); + + tesseract::Orientation orientation; + tesseract::WritingDirection direction; + tesseract::TextlineOrder order; + float deskew_angle; + + tesseract::PageIterator* it = api.AnalyseLayout(); + if (it) { + + StopWatch timer; + timer.reset(); + it->Orientation(&orientation, &direction, &order, &deskew_angle); + + tprintf( + "Orientation: %d\nWritingDirection: %d\nTextlineOrder: %d\n" + "Deskew angle: %.4f\n time: %.4f\n img: %s", + orientation, direction, order, deskew_angle, timer.elapsed_s(), image); + getchar(); + } + else { + ret_val = EXIT_FAILURE; + } + + delete it; + + pixDestroy(&pixs); + return ret_val; + } + + // set in_training_mode to true when using one of these configs: + // ambigs.train, box.train, box.train.stderr, linebox, rebox + bool b = false; + bool in_training_mode = + (api.GetBoolVariable("tessedit_ambigs_training", &b) && b) || + (api.GetBoolVariable("tessedit_resegment_from_boxes", &b) && b) || + (api.GetBoolVariable("tessedit_make_boxes_from_boxes", &b) && b); + + // Avoid memory leak caused by auto variable when exit() is called. + static tesseract::PointerVector renderers; + + if (in_training_mode) { + renderers.push_back(NULL); + } + else { + PreloadRenderers(&api, &renderers, pagesegmode, outputbase); + } + + if (!renderers.empty()) { + if (banner) PrintBanner(); + bool succeed = api.ProcessPages(image, NULL, 0, renderers[0]); + if (!succeed) { + fprintf(stderr, "Error during processing.\n"); + return EXIT_FAILURE; + } + } + + PERF_COUNT_END + + return EXIT_SUCCESS; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/adaptions.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/adaptions.cpp new file mode 100644 index 0000000..2379473 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/adaptions.cpp @@ -0,0 +1,126 @@ +/********************************************************************** + * File: adaptions.cpp (Formerly adaptions.c) + * Description: Functions used to adapt to blobs already confidently + * identified + * Author: Chris Newton + * Created: Thu Oct 7 10:17:28 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#pragma warning(disable:4305) // int/float warnings +#endif + +#ifdef __UNIX__ +#include +#endif +#include +#include +#include "tessbox.h" +#include "tessvars.h" +#include "memry.h" +#include "reject.h" +#include "control.h" +#include "stopper.h" +#include "tesseractclass.h" + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +namespace tesseract { + BOOL8 Tesseract::word_adaptable( //should we adapt? + WERD_RES *word, + uinT16 mode) { + if (tessedit_adaption_debug) { + tprintf("Running word_adaptable() for %s rating %.4f certainty %.4f\n", + word->best_choice == NULL ? "" : + word->best_choice->unichar_string().string(), + word->best_choice->rating(), word->best_choice->certainty()); + } + + BOOL8 status = FALSE; + BITS16 flags(mode); + + enum MODES + { + ADAPTABLE_WERD, + ACCEPTABLE_WERD, + CHECK_DAWGS, + CHECK_SPACES, + CHECK_ONE_ELL_CONFLICT, + CHECK_AMBIG_WERD + }; + + /* + 0: NO adaption + */ + if (mode == 0) { + if (tessedit_adaption_debug) tprintf("adaption disabled\n"); + return FALSE; + } + + if (flags.bit(ADAPTABLE_WERD)) { + status |= word->tess_would_adapt; // result of Classify::AdaptableWord() + if (tessedit_adaption_debug && !status) { + tprintf("tess_would_adapt bit is false\n"); + } + } + + if (flags.bit(ACCEPTABLE_WERD)) { + status |= word->tess_accepted; + if (tessedit_adaption_debug && !status) { + tprintf("tess_accepted bit is false\n"); + } + } + + if (!status) { // If not set then + return FALSE; // ignore other checks + } + + if (flags.bit(CHECK_DAWGS) && + (word->best_choice->permuter() != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter() != FREQ_DAWG_PERM) && + (word->best_choice->permuter() != USER_DAWG_PERM) && + (word->best_choice->permuter() != NUMBER_PERM)) { + if (tessedit_adaption_debug) tprintf("word not in dawgs\n"); + return FALSE; + } + + if (flags.bit(CHECK_ONE_ELL_CONFLICT) && one_ell_conflict(word, FALSE)) { + if (tessedit_adaption_debug) tprintf("word has ell conflict\n"); + return FALSE; + } + + if (flags.bit(CHECK_SPACES) && + (strchr(word->best_choice->unichar_string().string(), ' ') != NULL)) { + if (tessedit_adaption_debug) tprintf("word contains spaces\n"); + return FALSE; + } + + if (flags.bit(CHECK_AMBIG_WERD) && + word->best_choice->dangerous_ambig_found()) { + if (tessedit_adaption_debug) tprintf("word is ambiguous\n"); + return FALSE; + } + + if (tessedit_adaption_debug) { + tprintf("returning status %d\n", status); + } + return status; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/applybox.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/applybox.cpp new file mode 100644 index 0000000..a211200 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/applybox.cpp @@ -0,0 +1,814 @@ +/********************************************************************** + * File: applybox.cpp (Formerly applybox.c) + * Description: Re segment rows according to box file data + * Author: Phil Cheatle + * Created: Wed Nov 24 09:11:23 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + +#include +#include +#ifdef __UNIX__ +#include +#include +#endif +#include "allheaders.h" +#include "boxread.h" +#include "chopper.h" +#include "pageres.h" +#include "unichar.h" +#include "unicharset.h" +#include "tesseractclass.h" +#include "genericvector.h" + + /** Max number of blobs to classify together in FindSegmentation. */ +const int kMaxGroupSize = 4; +/// Max fraction of median allowed as deviation in xheight before switching +/// to median. +const double kMaxXHeightDeviationFraction = 0.125; + +/** + * The box file is assumed to contain box definitions, one per line, of the + * following format for blob-level boxes: + * @verbatim + * + * @endverbatim + * and for word/line-level boxes: + * @verbatim + * WordStr # + * @endverbatim + * NOTES: + * The boxes use tesseract coordinates, i.e. 0,0 is at BOTTOM-LEFT. + * + * is 0-based, and the page number is used for multipage input (tiff). + * + * In the blob-level form, each line represents a recognizable unit, which may + * be several UTF-8 bytes, but there is a bounding box around each recognizable + * unit, and no classifier is needed to train in this mode (bootstrapping.) + * + * In the word/line-level form, the line begins with the literal "WordStr", and + * the bounding box bounds either a whole line or a whole word. The recognizable + * units in the word/line are listed after the # at the end of the line and + * are space delimited, ignoring any original spaces on the line. + * Eg. + * @verbatim + * word -> #w o r d + * multi word line -> #m u l t i w o r d l i n e + * @endverbatim + * The recognizable units must be space-delimited in order to allow multiple + * unicodes to be used for a single recognizable unit, eg Hindi. + * + * In this mode, the classifier must have been pre-trained with the desired + * character set, or it will not be able to find the character segmentations. + */ + +namespace tesseract { + + static void clear_any_old_text(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + for (block_it.mark_cycle_pt(); + !block_it.cycled_list(); block_it.forward()) { + ROW_IT row_it(block_it.data()->row_list()); + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + WERD_IT word_it(row_it.data()->word_list()); + for (word_it.mark_cycle_pt(); + !word_it.cycled_list(); word_it.forward()) { + word_it.data()->set_text(""); + } + } + } + } + + // Applies the box file based on the image name fname, and resegments + // the words in the block_list (page), with: + // blob-mode: one blob per line in the box file, words as input. + // word/line-mode: one blob per space-delimited unit after the #, and one word + // per line in the box file. (See comment above for box file format.) + // If find_segmentation is true, (word/line mode) then the classifier is used + // to re-segment words/lines to match the space-delimited truth string for + // each box. In this case, the input box may be for a word or even a whole + // text line, and the output words will contain multiple blobs corresponding + // to the space-delimited input string. + // With find_segmentation false, no classifier is needed, but the chopper + // can still be used to correctly segment touching characters with the help + // of the input boxes. + // In the returned PAGE_RES, the WERD_RES are setup as they would be returned + // from normal classification, ie. with a word, chopped_word, rebuild_word, + // seam_array, denorm, box_word, and best_state, but NO best_choice or + // raw_choice, as they would require a UNICHARSET, which we aim to avoid. + // Instead, the correct_text member of WERD_RES is set, and this may be later + // converted to a best_choice using CorrectClassifyWords. CorrectClassifyWords + // is not required before calling ApplyBoxTraining. + PAGE_RES* Tesseract::ApplyBoxes(const STRING& fname, + bool find_segmentation, + BLOCK_LIST *block_list) { + GenericVector boxes; + GenericVector texts, full_texts; + if (!ReadAllBoxes(applybox_page, true, fname, &boxes, &texts, &full_texts, + NULL)) { + return NULL; // Can't do it. + } + + int box_count = boxes.size(); + int box_failures = 0; + // Add an empty everything to the end. + boxes.push_back(TBOX()); + texts.push_back(STRING()); + full_texts.push_back(STRING()); + + // In word mode, we use the boxes to make a word for each box, but + // in blob mode we use the existing words and maximally chop them first. + PAGE_RES* page_res = find_segmentation ? + NULL : SetupApplyBoxes(boxes, block_list); + clear_any_old_text(block_list); + + for (int i = 0; i < boxes.size() - 1; i++) { + bool foundit = false; + if (page_res != NULL) { + if (i == 0) { + foundit = ResegmentCharBox(page_res, NULL, boxes[i], boxes[i + 1], + full_texts[i].string()); + } + else { + foundit = ResegmentCharBox(page_res, &boxes[i - 1], boxes[i], + boxes[i + 1], full_texts[i].string()); + } + } + else { + foundit = ResegmentWordBox(block_list, boxes[i], boxes[i + 1], + texts[i].string()); + } + if (!foundit) { + box_failures++; + ReportFailedBox(i, boxes[i], texts[i].string(), + "FAILURE! Couldn't find a matching blob"); + } + } + + if (page_res == NULL) { + // In word/line mode, we now maximally chop all the words and resegment + // them with the classifier. + page_res = SetupApplyBoxes(boxes, block_list); + ReSegmentByClassification(page_res); + } + if (applybox_debug > 0) { + tprintf("APPLY_BOXES:\n"); + tprintf(" Boxes read from boxfile: %6d\n", box_count); + if (box_failures > 0) + tprintf(" Boxes failed resegmentation: %6d\n", box_failures); + } + TidyUp(page_res); + return page_res; + } + + // Helper computes median xheight in the image. + static double MedianXHeight(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + STATS xheights(0, block_it.data()->bounding_box().height()); + for (block_it.mark_cycle_pt(); + !block_it.cycled_list(); block_it.forward()) { + ROW_IT row_it(block_it.data()->row_list()); + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + xheights.add(IntCastRounded(row_it.data()->x_height()), 1); + } + } + return xheights.median(); + } + + /// Any row xheight that is significantly different from the median is set + /// to the median. + void Tesseract::PreenXHeights(BLOCK_LIST *block_list) { + double median_xheight = MedianXHeight(block_list); + double max_deviation = kMaxXHeightDeviationFraction * median_xheight; + // Strip all fuzzy space markers to simplify the PAGE_RES. + BLOCK_IT b_it(block_list); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOCK* block = b_it.data(); + ROW_IT r_it(block->row_list()); + for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { + ROW* row = r_it.data(); + float diff = fabs(row->x_height() - median_xheight); + if (diff > max_deviation) { + if (applybox_debug) { + tprintf("row xheight=%g, but median xheight = %g\n", + row->x_height(), median_xheight); + } + row->set_x_height(static_cast(median_xheight)); + } + } + } + } + + /// Builds a PAGE_RES from the block_list in the way required for ApplyBoxes: + /// All fuzzy spaces are removed, and all the words are maximally chopped. + PAGE_RES* Tesseract::SetupApplyBoxes(const GenericVector& boxes, + BLOCK_LIST *block_list) { + PreenXHeights(block_list); + // Strip all fuzzy space markers to simplify the PAGE_RES. + BLOCK_IT b_it(block_list); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOCK* block = b_it.data(); + ROW_IT r_it(block->row_list()); + for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { + ROW* row = r_it.data(); + WERD_IT w_it(row->word_list()); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD* word = w_it.data(); + if (word->cblob_list()->empty()) { + delete w_it.extract(); + } + else { + word->set_flag(W_FUZZY_SP, false); + word->set_flag(W_FUZZY_NON, false); + } + } + } + } + PAGE_RES* page_res = new PAGE_RES(false, block_list, NULL); + PAGE_RES_IT pr_it(page_res); + WERD_RES* word_res; + while ((word_res = pr_it.word()) != NULL) { + MaximallyChopWord(boxes, pr_it.block()->block, + pr_it.row()->row, word_res); + pr_it.forward(); + } + return page_res; + } + + /// Tests the chopper by exhaustively running chop_one_blob. + /// The word_res will contain filled chopped_word, seam_array, denorm, + /// box_word and best_state for the maximally chopped word. + void Tesseract::MaximallyChopWord(const GenericVector& boxes, + BLOCK* block, ROW* row, + WERD_RES* word_res) { + if (!word_res->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, NULL, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + row, block)) { + word_res->CloneChoppedToRebuild(); + return; + } + if (chop_debug) { + tprintf("Maximally chopping word at:"); + word_res->word->bounding_box().print(); + } + GenericVector blob_choices; + ASSERT_HOST(!word_res->chopped_word->blobs.empty()); + float rating = static_cast(MAX_INT8); + for (int i = 0; i < word_res->chopped_word->NumBlobs(); ++i) { + // The rating and certainty are not quite arbitrary. Since + // select_blob_to_chop uses the worst certainty to choose, they all have + // to be different, so starting with MAX_INT8, subtract 1/8 for each blob + // in here, and then divide by e each time they are chopped, which + // should guarantee a set of unequal values for the whole tree of blobs + // produced, however much chopping is required. The chops are thus only + // limited by the ability of the chopper to find suitable chop points, + // and not by the value of the certainties. + BLOB_CHOICE* choice = + new BLOB_CHOICE(0, rating, -rating, -1, 0.0f, 0.0f, 0.0f, BCC_FAKE); + blob_choices.push_back(choice); + rating -= 0.125f; + } + const double e = exp(1.0); // The base of natural logs. + int blob_number; + int right_chop_index = 0; + if (!assume_fixed_pitch_char_segment) { + // We only chop if the language is not fixed pitch like CJK. + SEAM* seam = NULL; + while ((seam = chop_one_blob(boxes, blob_choices, word_res, + &blob_number)) != NULL) { + word_res->InsertSeam(blob_number, seam); + BLOB_CHOICE* left_choice = blob_choices[blob_number]; + rating = left_choice->rating() / e; + left_choice->set_rating(rating); + left_choice->set_certainty(-rating); + // combine confidence w/ serial # + BLOB_CHOICE* right_choice = new BLOB_CHOICE(++right_chop_index, + rating - 0.125f, -rating, -1, + 0.0f, 0.0f, 0.0f, BCC_FAKE); + blob_choices.insert(right_choice, blob_number + 1); + } + } + word_res->CloneChoppedToRebuild(); + word_res->FakeClassifyWord(blob_choices.size(), &blob_choices[0]); + } + + /// Helper to compute the dispute resolution metric. + /// Disputed blob resolution. The aim is to give the blob to the most + /// appropriate boxfile box. Most of the time it is obvious, but if + /// two boxfile boxes overlap significantly it is not. If a small boxfile + /// box takes most of the blob, and a large boxfile box does too, then + /// we want the small boxfile box to get it, but if the small box + /// is much smaller than the blob, we don't want it to get it. + /// Details of the disputed blob resolution: + /// Given a box with area A, and a blob with area B, with overlap area C, + /// then the miss metric is (A-C)(B-C)/(AB) and the box with minimum + /// miss metric gets the blob. + static double BoxMissMetric(const TBOX& box1, const TBOX& box2) { + int overlap_area = box1.intersection(box2).area(); + double miss_metric = box1.area() - overlap_area; + miss_metric /= box1.area(); + miss_metric *= box2.area() - overlap_area; + miss_metric /= box2.area(); + return miss_metric; + } + + /// Gather consecutive blobs that match the given box into the best_state + /// and corresponding correct_text. + /// + /// Fights over which box owns which blobs are settled by pre-chopping and + /// applying the blobs to box or next_box with the least non-overlap. + /// @return false if the box was in error, which can only be caused by + /// failing to find an appropriate blob for a box. + /// + /// This means that occasionally, blobs may be incorrectly segmented if the + /// chopper fails to find a suitable chop point. + bool Tesseract::ResegmentCharBox(PAGE_RES* page_res, const TBOX *prev_box, + const TBOX& box, const TBOX& next_box, + const char* correct_text) { + if (applybox_debug > 1) { + tprintf("\nAPPLY_BOX: in ResegmentCharBox() for %s\n", correct_text); + } + PAGE_RES_IT page_res_it(page_res); + WERD_RES* word_res; + for (word_res = page_res_it.word(); word_res != NULL; + word_res = page_res_it.forward()) { + if (!word_res->box_word->bounding_box().major_overlap(box)) + continue; + if (applybox_debug > 1) { + tprintf("Checking word box:"); + word_res->box_word->bounding_box().print(); + } + int word_len = word_res->box_word->length(); + for (int i = 0; i < word_len; ++i) { + TBOX char_box = TBOX(); + int blob_count = 0; + for (blob_count = 0; i + blob_count < word_len; ++blob_count) { + TBOX blob_box = word_res->box_word->BlobBox(i + blob_count); + if (!blob_box.major_overlap(box)) + break; + if (word_res->correct_text[i + blob_count].length() > 0) + break; // Blob is claimed already. + double current_box_miss_metric = BoxMissMetric(blob_box, box); + double next_box_miss_metric = BoxMissMetric(blob_box, next_box); + if (applybox_debug > 2) { + tprintf("Checking blob:"); + blob_box.print(); + tprintf("Current miss metric = %g, next = %g\n", + current_box_miss_metric, next_box_miss_metric); + } + if (current_box_miss_metric > next_box_miss_metric) + break; // Blob is a better match for next box. + char_box += blob_box; + } + if (blob_count > 0) { + if (applybox_debug > 1) { + tprintf("Index [%d, %d) seem good.\n", i, i + blob_count); + } + if (!char_box.almost_equal(box, 3) && + (box.x_gap(next_box) < -3 || + (prev_box != NULL && prev_box->x_gap(box) < -3))) { + return false; + } + // We refine just the box_word, best_state and correct_text here. + // The rebuild_word is made in TidyUp. + // blob_count blobs are put together to match the box. Merge the + // box_word boxes, save the blob_count in the state and the text. + word_res->box_word->MergeBoxes(i, i + blob_count); + word_res->best_state[i] = blob_count; + word_res->correct_text[i] = correct_text; + if (applybox_debug > 2) { + tprintf("%d Blobs match: blob box:", blob_count); + word_res->box_word->BlobBox(i).print(); + tprintf("Matches box:"); + box.print(); + tprintf("With next box:"); + next_box.print(); + } + // Eliminated best_state and correct_text entries for the consumed + // blobs. + for (int j = 1; j < blob_count; ++j) { + word_res->best_state.remove(i + 1); + word_res->correct_text.remove(i + 1); + } + // Assume that no box spans multiple source words, so we are done with + // this box. + if (applybox_debug > 1) { + tprintf("Best state = "); + for (int j = 0; j < word_res->best_state.size(); ++j) { + tprintf("%d ", word_res->best_state[j]); + } + tprintf("\n"); + tprintf("Correct text = [[ "); + for (int j = 0; j < word_res->correct_text.size(); ++j) { + tprintf("%s ", word_res->correct_text[j].string()); + } + tprintf("]]\n"); + } + return true; + } + } + } + if (applybox_debug > 0) { + tprintf("FAIL!\n"); + } + return false; // Failure. + } + + /// Consume all source blobs that strongly overlap the given box, + /// putting them into a new word, with the correct_text label. + /// Fights over which box owns which blobs are settled by + /// applying the blobs to box or next_box with the least non-overlap. + /// @return false if the box was in error, which can only be caused by + /// failing to find an overlapping blob for a box. + bool Tesseract::ResegmentWordBox(BLOCK_LIST *block_list, + const TBOX& box, const TBOX& next_box, + const char* correct_text) { + if (applybox_debug > 1) { + tprintf("\nAPPLY_BOX: in ResegmentWordBox() for %s\n", correct_text); + } + WERD* new_word = NULL; + BLOCK_IT b_it(block_list); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOCK* block = b_it.data(); + if (!box.major_overlap(block->bounding_box())) + continue; + ROW_IT r_it(block->row_list()); + for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { + ROW* row = r_it.data(); + if (!box.major_overlap(row->bounding_box())) + continue; + WERD_IT w_it(row->word_list()); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD* word = w_it.data(); + if (applybox_debug > 2) { + tprintf("Checking word:"); + word->bounding_box().print(); + } + if (word->text() != NULL && word->text()[0] != '\0') + continue; // Ignore words that are already done. + if (!box.major_overlap(word->bounding_box())) + continue; + 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(); + TBOX blob_box = blob->bounding_box(); + if (!blob_box.major_overlap(box)) + continue; + double current_box_miss_metric = BoxMissMetric(blob_box, box); + double next_box_miss_metric = BoxMissMetric(blob_box, next_box); + if (applybox_debug > 2) { + tprintf("Checking blob:"); + blob_box.print(); + tprintf("Current miss metric = %g, next = %g\n", + current_box_miss_metric, next_box_miss_metric); + } + if (current_box_miss_metric > next_box_miss_metric) + continue; // Blob is a better match for next box. + if (applybox_debug > 2) { + tprintf("Blob match: blob:"); + blob_box.print(); + tprintf("Matches box:"); + box.print(); + tprintf("With next box:"); + next_box.print(); + } + if (new_word == NULL) { + // Make a new word with a single blob. + new_word = word->shallow_copy(); + new_word->set_text(correct_text); + w_it.add_to_end(new_word); + } + C_BLOB_IT new_blob_it(new_word->cblob_list()); + new_blob_it.add_to_end(blob_it.extract()); + } + } + } + } + if (new_word == NULL && applybox_debug > 0) tprintf("FAIL!\n"); + return new_word != NULL; + } + + /// Resegments the words by running the classifier in an attempt to find the + /// correct segmentation that produces the required string. + void Tesseract::ReSegmentByClassification(PAGE_RES* page_res) { + PAGE_RES_IT pr_it(page_res); + WERD_RES* word_res; + for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { + WERD* word = word_res->word; + if (word->text() == NULL || word->text()[0] == '\0') + continue; // Ignore words that have no text. + // Convert the correct text to a vector of UNICHAR_ID + GenericVector target_text; + if (!ConvertStringToUnichars(word->text(), &target_text)) { + tprintf("APPLY_BOX: FAILURE: can't find class_id for '%s'\n", + word->text()); + pr_it.DeleteCurrentWord(); + continue; + } + if (!FindSegmentation(target_text, word_res)) { + tprintf("APPLY_BOX: FAILURE: can't find segmentation for '%s'\n", + word->text()); + pr_it.DeleteCurrentWord(); + continue; + } + } + } + + /// Converts the space-delimited string of utf8 text to a vector of UNICHAR_ID. + /// @return false if an invalid UNICHAR_ID is encountered. + bool Tesseract::ConvertStringToUnichars(const char* utf8, + GenericVector* class_ids) { + for (int step = 0; *utf8 != '\0'; utf8 += step) { + const char* next_space = strchr(utf8, ' '); + if (next_space == NULL) + next_space = utf8 + strlen(utf8); + step = next_space - utf8; + UNICHAR_ID class_id = unicharset.unichar_to_id(utf8, step); + if (class_id == INVALID_UNICHAR_ID) { + return false; + } + while (utf8[step] == ' ') + ++step; + class_ids->push_back(class_id); + } + return true; + } + + /// Resegments the word to achieve the target_text from the classifier. + /// Returns false if the re-segmentation fails. + /// Uses brute-force combination of up to #kMaxGroupSize adjacent blobs, and + /// applies a full search on the classifier results to find the best classified + /// segmentation. As a compromise to obtain better recall, 1-1 ambiguity + /// substitutions ARE used. + bool Tesseract::FindSegmentation(const GenericVector& target_text, + WERD_RES* word_res) { + // Classify all required combinations of blobs and save results in choices. + int word_length = word_res->box_word->length(); + GenericVector* choices = + new GenericVector[word_length]; + for (int i = 0; i < word_length; ++i) { + for (int j = 1; j <= kMaxGroupSize && i + j <= word_length; ++j) { + BLOB_CHOICE_LIST* match_result = classify_piece( + word_res->seam_array, i, i + j - 1, "Applybox", + word_res->chopped_word, word_res->blamer_bundle); + if (applybox_debug > 2) { + tprintf("%d+%d:", i, j); + print_ratings_list("Segment:", match_result, unicharset); + } + choices[i].push_back(match_result); + } + } + // Search the segmentation graph for the target text. Must be an exact + // match. Using wildcards makes it difficult to find the correct + // segmentation even when it is there. + word_res->best_state.clear(); + GenericVector search_segmentation; + float best_rating = 0.0f; + SearchForText(choices, 0, word_length, target_text, 0, 0.0f, + &search_segmentation, &best_rating, &word_res->best_state); + for (int i = 0; i < word_length; ++i) + choices[i].delete_data_pointers(); + delete[] choices; + if (word_res->best_state.empty()) { + // Build the original segmentation and if it is the same length as the + // truth, assume it will do. + int blob_count = 1; + for (int s = 0; s < word_res->seam_array.size(); ++s) { + SEAM* seam = word_res->seam_array[s]; + if (!seam->HasAnySplits()) { + word_res->best_state.push_back(blob_count); + blob_count = 1; + } + else { + ++blob_count; + } + } + word_res->best_state.push_back(blob_count); + if (word_res->best_state.size() != target_text.size()) { + word_res->best_state.clear(); // No good. Original segmentation bad size. + return false; + } + } + word_res->correct_text.clear(); + for (int i = 0; i < target_text.size(); ++i) { + word_res->correct_text.push_back( + STRING(unicharset.id_to_unichar(target_text[i]))); + } + return true; + } + + /// Recursive helper to find a match to the target_text (from text_index + /// position) in the choices (from choices_pos position). + /// @param choices is an array of GenericVectors, of length choices_length, + /// with each element representing a starting position in the word, and the + /// #GenericVector holding classification results for a sequence of consecutive + /// blobs, with index 0 being a single blob, index 1 being 2 blobs etc. + /// @param choices_pos + /// @param choices_length + /// @param target_text + /// @param text_index + /// @param rating + /// @param segmentation + /// @param best_rating + /// @param best_segmentation + void Tesseract::SearchForText(const GenericVector* choices, + int choices_pos, int choices_length, + const GenericVector& target_text, + int text_index, + float rating, GenericVector* segmentation, + float* best_rating, + GenericVector* best_segmentation) { + const UnicharAmbigsVector& table = getDict().getUnicharAmbigs().dang_ambigs(); + for (int length = 1; length <= choices[choices_pos].size(); ++length) { + // Rating of matching choice or worst choice if no match. + float choice_rating = 0.0f; + // Find the corresponding best BLOB_CHOICE. + BLOB_CHOICE_IT choice_it(choices[choices_pos][length - 1]); + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + BLOB_CHOICE* choice = choice_it.data(); + choice_rating = choice->rating(); + UNICHAR_ID class_id = choice->unichar_id(); + if (class_id == target_text[text_index]) { + break; + } + // Search ambigs table. + if (class_id < table.size() && table[class_id] != NULL) { + AmbigSpec_IT spec_it(table[class_id]); + for (spec_it.mark_cycle_pt(); !spec_it.cycled_list(); + spec_it.forward()) { + const AmbigSpec *ambig_spec = spec_it.data(); + // We'll only do 1-1. + if (ambig_spec->wrong_ngram[1] == INVALID_UNICHAR_ID && + ambig_spec->correct_ngram_id == target_text[text_index]) + break; + } + if (!spec_it.cycled_list()) + break; // Found an ambig. + } + } + if (choice_it.cycled_list()) + continue; // No match. + segmentation->push_back(length); + if (choices_pos + length == choices_length && + text_index + 1 == target_text.size()) { + // This is a complete match. If the rating is good record a new best. + if (applybox_debug > 2) { + tprintf("Complete match, rating = %g, best=%g, seglength=%d, best=%d\n", + rating + choice_rating, *best_rating, segmentation->size(), + best_segmentation->size()); + } + if (best_segmentation->empty() || rating + choice_rating < *best_rating) { + *best_segmentation = *segmentation; + *best_rating = rating + choice_rating; + } + } + else if (choices_pos + length < choices_length && + text_index + 1 < target_text.size()) { + if (applybox_debug > 3) { + tprintf("Match found for %d=%s:%s, at %d+%d, recursing...\n", + target_text[text_index], + unicharset.id_to_unichar(target_text[text_index]), + choice_it.data()->unichar_id() == target_text[text_index] + ? "Match" : "Ambig", + choices_pos, length); + } + SearchForText(choices, choices_pos + length, choices_length, target_text, + text_index + 1, rating + choice_rating, segmentation, + best_rating, best_segmentation); + if (applybox_debug > 3) { + tprintf("End recursion for %d=%s\n", target_text[text_index], + unicharset.id_to_unichar(target_text[text_index])); + } + } + segmentation->truncate(segmentation->size() - 1); + } + } + + /// - Counts up the labelled words and the blobs within. + /// - Deletes all unused or emptied words, counting the unused ones. + /// - Resets W_BOL and W_EOL flags correctly. + /// - Builds the rebuild_word and rebuilds the box_word and the best_choice. + void Tesseract::TidyUp(PAGE_RES* page_res) { + int ok_blob_count = 0; + int bad_blob_count = 0; + int ok_word_count = 0; + int unlabelled_words = 0; + PAGE_RES_IT pr_it(page_res); + WERD_RES* word_res; + for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { + int ok_in_word = 0; + int blob_count = word_res->correct_text.size(); + WERD_CHOICE* word_choice = new WERD_CHOICE(word_res->uch_set, blob_count); + word_choice->set_permuter(TOP_CHOICE_PERM); + for (int c = 0; c < blob_count; ++c) { + if (word_res->correct_text[c].length() > 0) { + ++ok_in_word; + } + // Since we only need a fake word_res->best_choice, the actual + // unichar_ids do not matter. Which is fortunate, since TidyUp() + // can be called while training Tesseract, at the stage where + // unicharset is not meaningful yet. + word_choice->append_unichar_id_space_allocated( + INVALID_UNICHAR_ID, word_res->best_state[c], 1.0f, -1.0f); + } + if (ok_in_word > 0) { + ok_blob_count += ok_in_word; + bad_blob_count += word_res->correct_text.size() - ok_in_word; + word_res->LogNewRawChoice(word_choice); + word_res->LogNewCookedChoice(1, false, word_choice); + } + else { + ++unlabelled_words; + if (applybox_debug > 0) { + tprintf("APPLY_BOXES: Unlabelled word at :"); + word_res->word->bounding_box().print(); + } + pr_it.DeleteCurrentWord(); + delete word_choice; + } + } + pr_it.restart_page(); + for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { + // Denormalize back to a BoxWord. + word_res->RebuildBestState(); + word_res->SetupBoxWord(); + word_res->word->set_flag(W_BOL, pr_it.prev_row() != pr_it.row()); + word_res->word->set_flag(W_EOL, pr_it.next_row() != pr_it.row()); + } + if (applybox_debug > 0) { + tprintf(" Found %d good blobs.\n", ok_blob_count); + if (bad_blob_count > 0) { + tprintf(" Leaving %d unlabelled blobs in %d words.\n", + bad_blob_count, ok_word_count); + } + if (unlabelled_words > 0) + tprintf(" %d remaining unlabelled words deleted.\n", unlabelled_words); + } + } + + /** Logs a bad box by line in the box file and box coords.*/ + void Tesseract::ReportFailedBox(int boxfile_lineno, TBOX box, + const char *box_ch, const char *err_msg) { + tprintf("APPLY_BOXES: boxfile line %d/%s ((%d,%d),(%d,%d)): %s\n", + boxfile_lineno + 1, box_ch, + box.left(), box.bottom(), box.right(), box.top(), err_msg); + } + + /** Creates a fake best_choice entry in each WERD_RES with the correct text.*/ + void Tesseract::CorrectClassifyWords(PAGE_RES* page_res) { + PAGE_RES_IT pr_it(page_res); + for (WERD_RES *word_res = pr_it.word(); word_res != NULL; + word_res = pr_it.forward()) { + WERD_CHOICE* choice = new WERD_CHOICE(word_res->uch_set, + word_res->correct_text.size()); + for (int i = 0; i < word_res->correct_text.size(); ++i) { + // The part before the first space is the real ground truth, and the + // rest is the bounding box location and page number. + GenericVector tokens; + word_res->correct_text[i].split(' ', &tokens); + UNICHAR_ID char_id = unicharset.unichar_to_id(tokens[0].string()); + choice->append_unichar_id_space_allocated(char_id, + word_res->best_state[i], + 0.0f, 0.0f); + } + word_res->ClearWordChoices(); + word_res->LogNewRawChoice(choice); + word_res->LogNewCookedChoice(1, false, choice); + } + } + + /// Calls #LearnWord to extract features for labelled blobs within each word. + /// Features are stored in an internal buffer. + void Tesseract::ApplyBoxTraining(const STRING& fontname, PAGE_RES* page_res) { + PAGE_RES_IT pr_it(page_res); + int word_count = 0; + for (WERD_RES *word_res = pr_it.word(); word_res != NULL; + word_res = pr_it.forward()) { + LearnWord(fontname.string(), word_res); + ++word_count; + } + tprintf("Generated training data for %d words\n", word_count); + } + + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/control.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/control.cpp new file mode 100644 index 0000000..0cd8f35 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/control.cpp @@ -0,0 +1,2077 @@ +/****************************************************************** + * File: control.cpp (Formerly control.c) + * Description: Module-independent matcher controller. + * Author: Ray Smith + * Created: Thu Apr 23 11:09:58 BST 1992 + * ReHacked: Tue Sep 22 08:42:49 BST 1992 Phil Cheatle + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include +#include "ocrclass.h" +#include "werdit.h" +#include "drawfx.h" +#include "tessbox.h" +#include "tessvars.h" +#include "pgedit.h" +#include "reject.h" +#include "fixspace.h" +#include "docqual.h" +#include "control.h" +#include "output.h" +#include "callcpp.h" +#include "globals.h" +#include "sorthelper.h" +#include "tesseractclass.h" + +#define MIN_FONT_ROW_COUNT 8 +#define MAX_XHEIGHT_DIFF 3 + +const char* const kBackUpConfigFile = "tempconfigdata.config"; +// Min believable x-height for any text when refitting as a fraction of +// original x-height +const double kMinRefitXHeightFraction = 0.5; + + +/** + * Make a word from the selected blobs and run Tess on them. + * + * @param page_res recognise blobs + * @param selection_box within this box + */ +namespace tesseract { + void Tesseract::recog_pseudo_word(PAGE_RES* page_res, + TBOX &selection_box) { + PAGE_RES_IT* it = make_pseudo_word(page_res, selection_box); + if (it != NULL) { + recog_interactive(it); + it->DeleteCurrentWord(); + delete it; + } + } + + /** + * Recognize a single word in interactive mode. + * + * @param pr_it the page results iterator + */ + BOOL8 Tesseract::recog_interactive(PAGE_RES_IT* pr_it) { + inT16 char_qual; + inT16 good_char_qual; + + WordData word_data(*pr_it); + SetupWordPassN(2, &word_data); + classify_word_and_language(2, pr_it, &word_data); + if (tessedit_debug_quality_metrics) { + WERD_RES* word_res = pr_it->word(); + word_char_quality(word_res, pr_it->row()->row, &char_qual, &good_char_qual); + tprintf("\n%d chars; word_blob_quality: %d; outline_errs: %d; " + "char_quality: %d; good_char_quality: %d\n", + word_res->reject_map.length(), + word_blob_quality(word_res, pr_it->row()->row), + word_outline_errs(word_res), char_qual, good_char_qual); + } + return TRUE; + } + + // Helper function to check for a target word and handle it appropriately. + // Inspired by Jetsoft's requirement to process only single words on pass2 + // and beyond. + // If word_config is not null: + // If the word_box and target_word_box overlap, read the word_config file + // else reset to previous config data. + // return true. + // else + // If the word_box and target_word_box overlap or pass <= 1, return true. + // Note that this function uses a fixed temporary file for storing the previous + // configs, so it is neither thread-safe, nor process-safe, but the assumption + // is that it will only be used for one debug window at a time. + // + // Since this function is used for debugging (and not to change OCR results) + // set only debug params from the word config file. + bool Tesseract::ProcessTargetWord(const TBOX& word_box, + const TBOX& target_word_box, + const char* word_config, + int pass) { + if (word_config != NULL) { + if (word_box.major_overlap(target_word_box)) { + if (backup_config_file_ == NULL) { + backup_config_file_ = kBackUpConfigFile; + FILE* config_fp = fopen(backup_config_file_, "wb"); + ParamUtils::PrintParams(config_fp, params()); + fclose(config_fp); + ParamUtils::ReadParamsFile(word_config, + SET_PARAM_CONSTRAINT_DEBUG_ONLY, + params()); + } + } + else { + if (backup_config_file_ != NULL) { + ParamUtils::ReadParamsFile(backup_config_file_, + SET_PARAM_CONSTRAINT_DEBUG_ONLY, + params()); + backup_config_file_ = NULL; + } + } + } + else if (pass > 1 && !word_box.major_overlap(target_word_box)) { + return false; + } + return true; + } + + /** If tesseract is to be run, sets the words up ready for it. */ + void Tesseract::SetupAllWordsPassN(int pass_n, + const TBOX* target_word_box, + const char* word_config, + PAGE_RES* page_res, + GenericVector* words) { + // Prepare all the words. + PAGE_RES_IT page_res_it(page_res); + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + if (target_word_box == NULL || + ProcessTargetWord(page_res_it.word()->word->bounding_box(), + *target_word_box, word_config, 1)) { + words->push_back(WordData(page_res_it)); + } + } + // Setup all the words for recognition with polygonal approximation. + for (int w = 0; w < words->size(); ++w) { + SetupWordPassN(pass_n, &(*words)[w]); + if (w > 0) (*words)[w].prev_word = &(*words)[w - 1]; + } + } + + // Sets up the single word ready for whichever engine is to be run. + void Tesseract::SetupWordPassN(int pass_n, WordData* word) { + if (pass_n == 1 || !word->word->done) { + if (pass_n == 1) { + word->word->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, NULL, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + word->row, word->block); + } + else if (pass_n == 2) { + // TODO(rays) Should we do this on pass1 too? + word->word->caps_height = 0.0; + if (word->word->x_height == 0.0f) + word->word->x_height = word->row->x_height(); + } + word->lang_words.truncate(0); + for (int s = 0; s <= sub_langs_.size(); ++s) { + // The sub_langs_.size() entry is for the master language. + Tesseract* lang_t = s < sub_langs_.size() ? sub_langs_[s] : this; + WERD_RES* word_res = new WERD_RES; + word_res->InitForRetryRecognition(*word->word); + word->lang_words.push_back(word_res); + // Cube doesn't get setup for pass2. + if (pass_n == 1 || lang_t->tessedit_ocr_engine_mode != OEM_CUBE_ONLY) { + word_res->SetupForRecognition( + lang_t->unicharset, lang_t, BestPix(), + lang_t->tessedit_ocr_engine_mode, NULL, + lang_t->classify_bln_numeric_mode, + lang_t->textord_use_cjk_fp_model, + lang_t->poly_allow_detailed_fx, word->row, word->block); + } + } + } + } + + // Runs word recognition on all the words. + bool Tesseract::RecogAllWordsPassN(int pass_n, ETEXT_DESC* monitor, + PAGE_RES_IT* pr_it, + GenericVector* words) { + // TODO(rays) Before this loop can be parallelized (it would yield a massive + // speed-up) all remaining member globals need to be converted to local/heap + // (eg set_pass1 and set_pass2) and an intermediate adaption pass needs to be + // added. The results will be significantly different with adaption on, and + // deterioration will need investigation. + pr_it->restart_page(); + for (int w = 0; w < words->size(); ++w) { + WordData* word = &(*words)[w]; + if (w > 0) word->prev_word = &(*words)[w - 1]; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + if (pass_n == 1) { + monitor->progress = 70 * w / words->size(); + if (monitor->progress_callback != NULL) { + TBOX box = pr_it->word()->word->bounding_box(); + (*monitor->progress_callback)(monitor->progress, box.left(), + box.right(), box.top(), box.bottom()); + } + } + else { + monitor->progress = 70 + 30 * w / words->size(); + if (monitor->progress_callback != NULL) { + (*monitor->progress_callback)(monitor->progress, 0, 0, 0, 0); + } + } + if (monitor->deadline_exceeded() || + (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, + words->size()))) { + // Timeout. Fake out the rest of the words. + for (; w < words->size(); ++w) { + (*words)[w].word->SetupFake(unicharset); + } + return false; + } + } + if (word->word->tess_failed) { + int s; + for (s = 0; s < word->lang_words.size() && + word->lang_words[s]->tess_failed; ++s) { + } + // If all are failed, skip it. Image words are skipped by this test. + if (s > word->lang_words.size()) continue; + } + // Sync pr_it with the wth WordData. + while (pr_it->word() != NULL && pr_it->word() != word->word) + pr_it->forward(); + ASSERT_HOST(pr_it->word() != NULL); + bool make_next_word_fuzzy = false; + if (ReassignDiacritics(pass_n, pr_it, &make_next_word_fuzzy)) { + // Needs to be setup again to see the new outlines in the chopped_word. + SetupWordPassN(pass_n, word); + } + + classify_word_and_language(pass_n, pr_it, word); + if (tessedit_dump_choices || debug_noise_removal) { + tprintf("Pass%d: %s [%s]\n", pass_n, + word->word->best_choice->unichar_string().string(), + word->word->best_choice->debug_string().string()); + } + pr_it->forward(); + if (make_next_word_fuzzy && pr_it->word() != NULL) { + pr_it->MakeCurrentWordFuzzy(); + } + } + return true; + } + + /** + * recog_all_words() + * + * Walk the page_res, recognizing all the words. + * If monitor is not null, it is used as a progress monitor/timeout/cancel. + * If dopasses is 0, all recognition passes are run, + * 1 just pass 1, 2 passes2 and higher. + * If target_word_box is not null, special things are done to words that + * overlap the target_word_box: + * if word_config is not null, the word config file is read for just the + * target word(s), otherwise, on pass 2 and beyond ONLY the target words + * are processed (Jetsoft modification.) + * Returns false if we cancelled prematurely. + * + * @param page_res page structure + * @param monitor progress monitor + * @param word_config word_config file + * @param target_word_box specifies just to extract a rectangle + * @param dopasses 0 - all, 1 just pass 1, 2 passes 2 and higher + */ + + bool Tesseract::recog_all_words(PAGE_RES* page_res, + ETEXT_DESC* monitor, + const TBOX* target_word_box, + const char* word_config, + int dopasses) { + PAGE_RES_IT page_res_it(page_res); + + if (tessedit_minimal_rej_pass1) { + tessedit_test_adaption.set_value(TRUE); + tessedit_minimal_rejection.set_value(TRUE); + } + + if (dopasses == 0 || dopasses == 1) { + page_res_it.restart_page(); + // ****************** Pass 1 ******************* + + // If the adaptive classifier is full switch to one we prepared earlier, + // ie on the previous page. If the current adaptive classifier is non-empty, + // prepare a backup starting at this page, in case it fills up. Do all this + // independently for each language. + if (AdaptiveClassifierIsFull()) { + SwitchAdaptiveClassifier(); + } + else if (!AdaptiveClassifierIsEmpty()) { + StartBackupAdaptiveClassifier(); + } + // Now check the sub-langs as well. + for (int i = 0; i < sub_langs_.size(); ++i) { + if (sub_langs_[i]->AdaptiveClassifierIsFull()) { + sub_langs_[i]->SwitchAdaptiveClassifier(); + } + else if (!sub_langs_[i]->AdaptiveClassifierIsEmpty()) { + sub_langs_[i]->StartBackupAdaptiveClassifier(); + } + } + // Set up all words ready for recognition, so that if parallelism is on + // all the input and output classes are ready to run the classifier. + GenericVector words; + SetupAllWordsPassN(1, target_word_box, word_config, page_res, &words); + if (tessedit_parallelize) { + PrerecAllWordsPar(words); + } + + stats_.word_count = words.size(); + + stats_.dict_words = 0; + stats_.doc_blob_quality = 0; + stats_.doc_outline_errs = 0; + stats_.doc_char_quality = 0; + stats_.good_char_count = 0; + stats_.doc_good_char_quality = 0; + + most_recently_used_ = this; + // Run pass 1 word recognition. + if (!RecogAllWordsPassN(1, monitor, &page_res_it, &words)) return false; + // Pass 1 post-processing. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + if (page_res_it.word()->word->flag(W_REP_CHAR)) { + fix_rep_char(&page_res_it); + continue; + } + + // Count dict words. + if (page_res_it.word()->best_choice->permuter() == USER_DAWG_PERM) + ++(stats_.dict_words); + + // Update misadaption log (we only need to do it on pass 1, since + // adaption only happens on this pass). + if (page_res_it.word()->blamer_bundle != NULL && + page_res_it.word()->blamer_bundle->misadaption_debug().length() > 0) { + page_res->misadaption_log.push_back( + page_res_it.word()->blamer_bundle->misadaption_debug()); + } + } + } + + if (dopasses == 1) return true; + + // ****************** Pass 2 ******************* + if (tessedit_tess_adaption_mode != 0x0 && !tessedit_test_adaption && + AnyTessLang()) { + page_res_it.restart_page(); + GenericVector words; + SetupAllWordsPassN(2, target_word_box, word_config, page_res, &words); + if (tessedit_parallelize) { + PrerecAllWordsPar(words); + } + most_recently_used_ = this; + // Run pass 2 word recognition. + if (!RecogAllWordsPassN(2, monitor, &page_res_it, &words)) return false; + } + + // The next passes can only be run if tesseract has been used, as cube + // doesn't set all the necessary outputs in WERD_RES. + if (AnyTessLang()) { + // ****************** Pass 3 ******************* + // Fix fuzzy spaces. + set_global_loc_code(LOC_FUZZY_SPACE); + + if (!tessedit_test_adaption && tessedit_fix_fuzzy_spaces + && !tessedit_word_for_word && !right_to_left()) + fix_fuzzy_spaces(monitor, stats_.word_count, page_res); + + // ****************** Pass 4 ******************* + if (tessedit_enable_dict_correction) dictionary_correction_pass(page_res); + if (tessedit_enable_bigram_correction) bigram_correction_pass(page_res); + + // ****************** Pass 5,6 ******************* + rejection_passes(page_res, monitor, target_word_box, word_config); + +#ifndef NO_CUBE_BUILD + // ****************** Pass 7 ******************* + // Cube combiner. + // If cube is loaded and its combiner is present, run it. + if (tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED) { + run_cube_combiner(page_res); + } +#endif + + // ****************** Pass 8 ******************* + font_recognition_pass(page_res); + + // ****************** Pass 9 ******************* + // Check the correctness of the final results. + blamer_pass(page_res); + script_pos_pass(page_res); + } + + // Write results pass. + set_global_loc_code(LOC_WRITE_RESULTS); + // This is now redundant, but retained commented so show how to obtain + // bounding boxes and style information. + + // changed by jetsoft + // needed for dll to output memory structure + if ((dopasses == 0 || dopasses == 2) && (monitor || tessedit_write_unlv)) + output_pass(page_res_it, target_word_box); + // end jetsoft + PageSegMode pageseg_mode = static_cast( + static_cast(tessedit_pageseg_mode)); + textord_.CleanupSingleRowResult(pageseg_mode, page_res); + + // Remove empty words, as these mess up the result iterators. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD_RES* word = page_res_it.word(); + if (word->best_choice == NULL || word->best_choice->length() == 0) + page_res_it.DeleteCurrentWord(); + } + + if (monitor != NULL) { + monitor->progress = 100; + } + return true; + } + + void Tesseract::bigram_correction_pass(PAGE_RES *page_res) { + PAGE_RES_IT word_it(page_res); + + WERD_RES *w_prev = NULL; + WERD_RES *w = word_it.word(); + while (1) { + w_prev = w; + while (word_it.forward() != NULL && + (!word_it.word() || word_it.word()->part_of_combo)) { + // advance word_it, skipping over parts of combos + } + if (!word_it.word()) break; + w = word_it.word(); + if (!w || !w_prev || w->uch_set != w_prev->uch_set) { + continue; + } + if (w_prev->word->flag(W_REP_CHAR) || w->word->flag(W_REP_CHAR)) { + if (tessedit_bigram_debug) { + tprintf("Skipping because one of the words is W_REP_CHAR\n"); + } + continue; + } + // Two words sharing the same language model, excellent! + GenericVector overrides_word1; + GenericVector overrides_word2; + + STRING orig_w1_str = w_prev->best_choice->unichar_string(); + STRING orig_w2_str = w->best_choice->unichar_string(); + WERD_CHOICE prev_best(w->uch_set); + { + int w1start, w1end; + w_prev->best_choice->GetNonSuperscriptSpan(&w1start, &w1end); + prev_best = w_prev->best_choice->shallow_copy(w1start, w1end); + } + WERD_CHOICE this_best(w->uch_set); + { + int w2start, w2end; + w->best_choice->GetNonSuperscriptSpan(&w2start, &w2end); + this_best = w->best_choice->shallow_copy(w2start, w2end); + } + + if (w->tesseract->getDict().valid_bigram(prev_best, this_best)) { + if (tessedit_bigram_debug) { + tprintf("Top choice \"%s %s\" verified by bigram model.\n", + orig_w1_str.string(), orig_w2_str.string()); + } + continue; + } + if (tessedit_bigram_debug > 2) { + tprintf("Examining alt choices for \"%s %s\".\n", + orig_w1_str.string(), orig_w2_str.string()); + } + if (tessedit_bigram_debug > 1) { + if (!w_prev->best_choices.singleton()) { + w_prev->PrintBestChoices(); + } + if (!w->best_choices.singleton()) { + w->PrintBestChoices(); + } + } + float best_rating = 0.0; + int best_idx = 0; + WERD_CHOICE_IT prev_it(&w_prev->best_choices); + for (prev_it.mark_cycle_pt(); !prev_it.cycled_list(); prev_it.forward()) { + WERD_CHOICE *p1 = prev_it.data(); + WERD_CHOICE strip1(w->uch_set); + { + int p1start, p1end; + p1->GetNonSuperscriptSpan(&p1start, &p1end); + strip1 = p1->shallow_copy(p1start, p1end); + } + WERD_CHOICE_IT w_it(&w->best_choices); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD_CHOICE *p2 = w_it.data(); + WERD_CHOICE strip2(w->uch_set); + { + int p2start, p2end; + p2->GetNonSuperscriptSpan(&p2start, &p2end); + strip2 = p2->shallow_copy(p2start, p2end); + } + if (w->tesseract->getDict().valid_bigram(strip1, strip2)) { + overrides_word1.push_back(p1); + overrides_word2.push_back(p2); + if (overrides_word1.size() == 1 || + p1->rating() + p2->rating() < best_rating) { + best_rating = p1->rating() + p2->rating(); + best_idx = overrides_word1.size() - 1; + } + } + } + } + if (!overrides_word1.empty()) { + // Excellent, we have some bigram matches. + if (EqualIgnoringCaseAndTerminalPunct(*w_prev->best_choice, + *overrides_word1[best_idx]) && + EqualIgnoringCaseAndTerminalPunct(*w->best_choice, + *overrides_word2[best_idx])) { + if (tessedit_bigram_debug > 1) { + tprintf("Top choice \"%s %s\" verified (sans case) by bigram " + "model.\n", orig_w1_str.string(), orig_w2_str.string()); + } + continue; + } + STRING new_w1_str = overrides_word1[best_idx]->unichar_string(); + STRING new_w2_str = overrides_word2[best_idx]->unichar_string(); + if (new_w1_str != orig_w1_str) { + w_prev->ReplaceBestChoice(overrides_word1[best_idx]); + } + if (new_w2_str != orig_w2_str) { + w->ReplaceBestChoice(overrides_word2[best_idx]); + } + if (tessedit_bigram_debug > 0) { + STRING choices_description; + int num_bigram_choices + = overrides_word1.size() * overrides_word2.size(); + if (num_bigram_choices == 1) { + choices_description = "This was the unique bigram choice."; + } + else { + if (tessedit_bigram_debug > 1) { + STRING bigrams_list; + const int kMaxChoicesToPrint = 20; + for (int i = 0; i < overrides_word1.size() && + i < kMaxChoicesToPrint; i++) { + if (i > 0) { bigrams_list += ", "; } + WERD_CHOICE *p1 = overrides_word1[i]; + WERD_CHOICE *p2 = overrides_word2[i]; + bigrams_list += p1->unichar_string() + " " + p2->unichar_string(); + if (i == kMaxChoicesToPrint) { + bigrams_list += " ..."; + } + } + choices_description = "There were many choices: {"; + choices_description += bigrams_list; + choices_description += "}"; + } + else { + choices_description.add_str_int("There were ", num_bigram_choices); + choices_description += " compatible bigrams."; + } + } + tprintf("Replaced \"%s %s\" with \"%s %s\" with bigram model. %s\n", + orig_w1_str.string(), orig_w2_str.string(), + new_w1_str.string(), new_w2_str.string(), + choices_description.string()); + } + } + } + } + + void Tesseract::rejection_passes(PAGE_RES* page_res, + ETEXT_DESC* monitor, + const TBOX* target_word_box, + const char* word_config) { + PAGE_RES_IT page_res_it(page_res); + // ****************** Pass 5 ******************* + // Gather statistics on rejects. + int word_index = 0; + while (!tessedit_test_adaption && page_res_it.word() != NULL) { + set_global_loc_code(LOC_MM_ADAPT); + WERD_RES* word = page_res_it.word(); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 95 + 5 * word_index / stats_.word_count; + } + if (word->rebuild_word == NULL) { + // Word was not processed by tesseract. + page_res_it.forward(); + continue; + } + check_debug_pt(word, 70); + + // changed by jetsoft + // specific to its needs to extract one word when need + if (target_word_box && + !ProcessTargetWord(word->word->bounding_box(), + *target_word_box, word_config, 4)) { + page_res_it.forward(); + continue; + } + // end jetsoft + + page_res_it.rej_stat_word(); + int chars_in_word = word->reject_map.length(); + int rejects_in_word = word->reject_map.reject_count(); + + int blob_quality = word_blob_quality(word, page_res_it.row()->row); + stats_.doc_blob_quality += blob_quality; + int outline_errs = word_outline_errs(word); + stats_.doc_outline_errs += outline_errs; + inT16 all_char_quality; + inT16 accepted_all_char_quality; + word_char_quality(word, page_res_it.row()->row, + &all_char_quality, &accepted_all_char_quality); + stats_.doc_char_quality += all_char_quality; + uinT8 permuter_type = word->best_choice->permuter(); + if ((permuter_type == SYSTEM_DAWG_PERM) || + (permuter_type == FREQ_DAWG_PERM) || + (permuter_type == USER_DAWG_PERM)) { + stats_.good_char_count += chars_in_word - rejects_in_word; + stats_.doc_good_char_quality += accepted_all_char_quality; + } + check_debug_pt(word, 80); + if (tessedit_reject_bad_qual_wds && + (blob_quality == 0) && (outline_errs >= chars_in_word)) + word->reject_map.rej_word_bad_quality(); + check_debug_pt(word, 90); + page_res_it.forward(); + } + + if (tessedit_debug_quality_metrics) { + tprintf + ("QUALITY: num_chs= %d num_rejs= %d %5.3f blob_qual= %d %5.3f" + " outline_errs= %d %5.3f char_qual= %d %5.3f good_ch_qual= %d %5.3f\n", + page_res->char_count, page_res->rej_count, + page_res->rej_count / static_cast(page_res->char_count), + stats_.doc_blob_quality, + stats_.doc_blob_quality / static_cast(page_res->char_count), + stats_.doc_outline_errs, + stats_.doc_outline_errs / static_cast(page_res->char_count), + stats_.doc_char_quality, + stats_.doc_char_quality / static_cast(page_res->char_count), + stats_.doc_good_char_quality, + (stats_.good_char_count > 0) ? + (stats_.doc_good_char_quality / + static_cast(stats_.good_char_count)) : 0.0); + } + BOOL8 good_quality_doc = + ((page_res->rej_count / static_cast(page_res->char_count)) <= + quality_rej_pc) && + (stats_.doc_blob_quality / static_cast(page_res->char_count) >= + quality_blob_pc) && + (stats_.doc_outline_errs / static_cast(page_res->char_count) <= + quality_outline_pc) && + (stats_.doc_char_quality / static_cast(page_res->char_count) >= + quality_char_pc); + + // ****************** Pass 6 ******************* + // Do whole document or whole block rejection pass + if (!tessedit_test_adaption) { + set_global_loc_code(LOC_DOC_BLK_REJ); + quality_based_rejection(page_res_it, good_quality_doc); + } + } + + void Tesseract::blamer_pass(PAGE_RES* page_res) { + if (!wordrec_run_blamer) return; + PAGE_RES_IT page_res_it(page_res); + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD_RES *word = page_res_it.word(); + BlamerBundle::LastChanceBlame(wordrec_debug_blamer, word); + page_res->blame_reasons[word->blamer_bundle->incorrect_result_reason()]++; + } + tprintf("Blame reasons:\n"); + for (int bl = 0; bl < IRR_NUM_REASONS; ++bl) { + tprintf("%s %d\n", BlamerBundle::IncorrectReasonName( + static_cast(bl)), + page_res->blame_reasons[bl]); + } + if (page_res->misadaption_log.length() > 0) { + tprintf("Misadaption log:\n"); + for (int i = 0; i < page_res->misadaption_log.length(); ++i) { + tprintf("%s\n", page_res->misadaption_log[i].string()); + } + } + } + + // Sets script positions and detects smallcaps on all output words. + void Tesseract::script_pos_pass(PAGE_RES* page_res) { + PAGE_RES_IT page_res_it(page_res); + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD_RES* word = page_res_it.word(); + if (word->word->flag(W_REP_CHAR)) { + page_res_it.forward(); + continue; + } + float x_height = page_res_it.block()->block->x_height(); + float word_x_height = word->x_height; + if (word_x_height < word->best_choice->min_x_height() || + word_x_height > word->best_choice->max_x_height()) { + word_x_height = (word->best_choice->min_x_height() + + word->best_choice->max_x_height()) / 2.0f; + } + // Test for small caps. Word capheight must be close to block xheight, + // and word must contain no lower case letters, and at least one upper case. + double small_cap_xheight = x_height * kXHeightCapRatio; + double small_cap_delta = (x_height - small_cap_xheight) / 2.0; + if (word->uch_set->script_has_xheight() && + small_cap_xheight - small_cap_delta <= word_x_height && + word_x_height <= small_cap_xheight + small_cap_delta) { + // Scan for upper/lower. + int num_upper = 0; + int num_lower = 0; + for (int i = 0; i < word->best_choice->length(); ++i) { + if (word->uch_set->get_isupper(word->best_choice->unichar_id(i))) + ++num_upper; + else if (word->uch_set->get_islower(word->best_choice->unichar_id(i))) + ++num_lower; + } + if (num_upper > 0 && num_lower == 0) + word->small_caps = true; + } + word->SetScriptPositions(); + } + } + + // Factored helper considers the indexed word and updates all the pointed + // values. + static void EvaluateWord(const PointerVector& words, int index, + float* rating, float* certainty, bool* bad, + bool* valid_permuter, int* right, int* next_left) { + *right = -MAX_INT32; + *next_left = MAX_INT32; + if (index < words.size()) { + WERD_CHOICE* choice = words[index]->best_choice; + if (choice == NULL) { + *bad = true; + } + else { + *rating += choice->rating(); + *certainty = MIN(*certainty, choice->certainty()); + if (!Dict::valid_word_permuter(choice->permuter(), false)) + *valid_permuter = false; + } + *right = words[index]->word->bounding_box().right(); + if (index + 1 < words.size()) + *next_left = words[index + 1]->word->bounding_box().left(); + } + else { + *valid_permuter = false; + *bad = true; + } + } + + // Helper chooses the best combination of words, transferring good ones from + // new_words to best_words. To win, a new word must have (better rating and + // certainty) or (better permuter status and rating within rating ratio and + // certainty within certainty margin) than current best. + // All the new_words are consumed (moved to best_words or deleted.) + // The return value is the number of new_words used minus the number of + // best_words that remain in the output. + static int SelectBestWords(double rating_ratio, + double certainty_margin, + bool debug, + PointerVector* new_words, + PointerVector* best_words) { + // Process the smallest groups of words that have an overlapping word + // boundary at the end. + GenericVector out_words; + // Index into each word vector (best, new). + int b = 0, n = 0; + int num_best = 0, num_new = 0; + while (b < best_words->size() || n < new_words->size()) { + // Start of the current run in each. + int start_b = b, start_n = n; + // Rating of the current run in each. + float b_rating = 0.0f, n_rating = 0.0f; + // Certainty of the current run in each. + float b_certainty = 0.0f, n_certainty = 0.0f; + // True if any word is missing its best choice. + bool b_bad = false, n_bad = false; + // True if all words have a valid permuter. + bool b_valid_permuter = true, n_valid_permuter = true; + + while (b < best_words->size() || n < new_words->size()) { + int b_right = -MAX_INT32; + int next_b_left = MAX_INT32; + EvaluateWord(*best_words, b, &b_rating, &b_certainty, &b_bad, + &b_valid_permuter, &b_right, &next_b_left); + int n_right = -MAX_INT32; + int next_n_left = MAX_INT32; + EvaluateWord(*new_words, n, &n_rating, &n_certainty, &n_bad, + &n_valid_permuter, &n_right, &next_n_left); + if (MAX(b_right, n_right) < MIN(next_b_left, next_n_left)) { + // The word breaks overlap. [start_b,b] and [start_n, n] match. + break; + } + // Keep searching for the matching word break. + if ((b_right < n_right && b < best_words->size()) || + n == new_words->size()) + ++b; + else + ++n; + } + bool new_better = false; + if (!n_bad && (b_bad || (n_certainty > b_certainty && + n_rating < b_rating) || + (!b_valid_permuter && n_valid_permuter && + n_rating < b_rating * rating_ratio && + n_certainty > b_certainty - certainty_margin))) { + // New is better. + for (int i = start_n; i <= n; ++i) { + out_words.push_back((*new_words)[i]); + (*new_words)[i] = NULL; + ++num_new; + } + new_better = true; + } + else if (!b_bad) { + // Current best is better. + for (int i = start_b; i <= b; ++i) { + out_words.push_back((*best_words)[i]); + (*best_words)[i] = NULL; + ++num_best; + } + } + int end_b = b < best_words->size() ? b + 1 : b; + int end_n = n < new_words->size() ? n + 1 : n; + if (debug) { + tprintf("%d new words %s than %d old words: r: %g v %g c: %g v %g" + " valid dict: %d v %d\n", + end_n - start_n, new_better ? "better" : "worse", + end_b - start_b, n_rating, b_rating, + n_certainty, b_certainty, n_valid_permuter, b_valid_permuter); + } + // Move on to the next group. + b = end_b; + n = end_n; + } + // Transfer from out_words to best_words. + best_words->clear(); + for (int i = 0; i < out_words.size(); ++i) + best_words->push_back(out_words[i]); + return num_new - num_best; + } + + // Helper to recognize the word using the given (language-specific) tesseract. + // Returns positive if this recognizer found more new best words than the + // number kept from best_words. + int Tesseract::RetryWithLanguage(const WordData& word_data, + WordRecognizer recognizer, + WERD_RES** in_word, + PointerVector* best_words) { + bool debug = classify_debug_level || cube_debug_level; + if (debug) { + tprintf("Trying word using lang %s, oem %d\n", + lang.string(), static_cast(tessedit_ocr_engine_mode)); + } + // Run the recognizer on the word. + PointerVector new_words; + (this->*recognizer)(word_data, in_word, &new_words); + if (new_words.empty()) { + // Transfer input word to new_words, as the classifier must have put + // the result back in the input. + new_words.push_back(*in_word); + *in_word = NULL; + } + if (debug) { + for (int i = 0; i < new_words.size(); ++i) + new_words[i]->DebugTopChoice("Lang result"); + } + // Initial version is a bit of a hack based on better certainty and rating + // (to reduce false positives from cube) or a dictionary vs non-dictionary + // word. + return SelectBestWords(classify_max_rating_ratio, + classify_max_certainty_margin, + debug, &new_words, best_words); + } + + // Helper returns true if all the words are acceptable. + static bool WordsAcceptable(const PointerVector& words) { + for (int w = 0; w < words.size(); ++w) { + if (words[w]->tess_failed || !words[w]->tess_accepted) return false; + } + return true; + } + + // Moves good-looking "noise"/diacritics from the reject list to the main + // blob list on the current word. Returns true if anything was done, and + // sets make_next_word_fuzzy if blob(s) were added to the end of the word. + bool Tesseract::ReassignDiacritics(int pass, PAGE_RES_IT* pr_it, + bool* make_next_word_fuzzy) { + *make_next_word_fuzzy = false; + WERD* real_word = pr_it->word()->word; + if (real_word->rej_cblob_list()->empty() || + real_word->cblob_list()->empty() || + real_word->rej_cblob_list()->length() > noise_maxperword) + return false; + real_word->rej_cblob_list()->sort(&C_BLOB::SortByXMiddle); + // Get the noise outlines into a vector with matching bool map. + GenericVector outlines; + real_word->GetNoiseOutlines(&outlines); + GenericVector word_wanted; + GenericVector overlapped_any_blob; + GenericVector target_blobs; + AssignDiacriticsToOverlappingBlobs(outlines, pass, real_word, pr_it, + &word_wanted, &overlapped_any_blob, + &target_blobs); + // Filter the outlines that overlapped any blob and put them into the word + // now. This simplifies the remaining task and also makes it more accurate + // as it has more completed blobs to work on. + GenericVector wanted; + GenericVector wanted_blobs; + GenericVector wanted_outlines; + int num_overlapped = 0; + int num_overlapped_used = 0; + for (int i = 0; i < overlapped_any_blob.size(); ++i) { + if (overlapped_any_blob[i]) { + ++num_overlapped; + if (word_wanted[i]) ++num_overlapped_used; + wanted.push_back(word_wanted[i]); + wanted_blobs.push_back(target_blobs[i]); + wanted_outlines.push_back(outlines[i]); + outlines[i] = NULL; + } + } + real_word->AddSelectedOutlines(wanted, wanted_blobs, wanted_outlines, NULL); + AssignDiacriticsToNewBlobs(outlines, pass, real_word, pr_it, &word_wanted, + &target_blobs); + int non_overlapped = 0; + int non_overlapped_used = 0; + for (int i = 0; i < word_wanted.size(); ++i) { + if (word_wanted[i]) ++non_overlapped_used; + if (outlines[i] != NULL) ++non_overlapped_used; + } + if (debug_noise_removal) { + tprintf("Used %d/%d overlapped %d/%d non-overlaped diacritics on word:", + num_overlapped_used, num_overlapped, non_overlapped_used, + non_overlapped); + real_word->bounding_box().print(); + } + // Now we have decided which outlines we want, put them into the real_word. + if (real_word->AddSelectedOutlines(word_wanted, target_blobs, outlines, + make_next_word_fuzzy)) { + pr_it->MakeCurrentWordFuzzy(); + } + // TODO(rays) Parts of combos have a deep copy of the real word, and need + // to have their noise outlines moved/assigned in the same way!! + return num_overlapped_used != 0 || non_overlapped_used != 0; + } + + // Attempts to put noise/diacritic outlines into the blobs that they overlap. + // Input: a set of noisy outlines that probably belong to the real_word. + // Output: word_wanted indicates which outlines are to be assigned to a blob, + // target_blobs indicates which to assign to, and overlapped_any_blob is + // true for all outlines that overlapped a blob. + void Tesseract::AssignDiacriticsToOverlappingBlobs( + const GenericVector& outlines, int pass, WERD* real_word, + PAGE_RES_IT* pr_it, GenericVector* word_wanted, + GenericVector* overlapped_any_blob, + GenericVector* target_blobs) { + GenericVector blob_wanted; + word_wanted->init_to_size(outlines.size(), false); + overlapped_any_blob->init_to_size(outlines.size(), false); + target_blobs->init_to_size(outlines.size(), NULL); + // For each real blob, find the outlines that seriously overlap it. + // A single blob could be several merged characters, so there can be quite + // a few outlines overlapping, and the full engine needs to be used to chop + // and join to get a sensible result. + C_BLOB_IT blob_it(real_word->cblob_list()); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + C_BLOB* blob = blob_it.data(); + TBOX blob_box = blob->bounding_box(); + blob_wanted.init_to_size(outlines.size(), false); + int num_blob_outlines = 0; + for (int i = 0; i < outlines.size(); ++i) { + if (blob_box.major_x_overlap(outlines[i]->bounding_box()) && + !(*word_wanted)[i]) { + blob_wanted[i] = true; + (*overlapped_any_blob)[i] = true; + ++num_blob_outlines; + } + } + if (debug_noise_removal) { + tprintf("%d noise outlines overlap blob at:", num_blob_outlines); + blob_box.print(); + } + // If any outlines overlap the blob, and not too many, classify the blob + // (using the full engine, languages and all), and choose the maximal + // combination of outlines that doesn't hurt the end-result classification + // by too much. Mark them as wanted. + if (0 < num_blob_outlines && num_blob_outlines < noise_maxperblob) { + if (SelectGoodDiacriticOutlines(pass, noise_cert_basechar, pr_it, blob, + outlines, num_blob_outlines, + &blob_wanted)) { + for (int i = 0; i < blob_wanted.size(); ++i) { + if (blob_wanted[i]) { + // Claim the outline and record where it is going. + (*word_wanted)[i] = true; + (*target_blobs)[i] = blob; + } + } + } + } + } + } + + // Attempts to assign non-overlapping outlines to their nearest blobs or + // make new blobs out of them. + void Tesseract::AssignDiacriticsToNewBlobs( + const GenericVector& outlines, int pass, WERD* real_word, + PAGE_RES_IT* pr_it, GenericVector* word_wanted, + GenericVector* target_blobs) { + GenericVector blob_wanted; + word_wanted->init_to_size(outlines.size(), false); + target_blobs->init_to_size(outlines.size(), NULL); + // Check for outlines that need to be turned into stand-alone blobs. + for (int i = 0; i < outlines.size(); ++i) { + if (outlines[i] == NULL) continue; + // Get a set of adjacent outlines that don't overlap any existing blob. + blob_wanted.init_to_size(outlines.size(), false); + int num_blob_outlines = 0; + TBOX total_ol_box(outlines[i]->bounding_box()); + while (i < outlines.size() && outlines[i] != NULL) { + blob_wanted[i] = true; + total_ol_box += outlines[i]->bounding_box(); + ++i; + ++num_blob_outlines; + } + // Find the insertion point. + C_BLOB_IT blob_it(real_word->cblob_list()); + while (!blob_it.at_last() && + blob_it.data_relative(1)->bounding_box().left() <= + total_ol_box.left()) { + blob_it.forward(); + } + // Choose which combination of them we actually want and where to put + // them. + if (debug_noise_removal) + tprintf("Num blobless outlines = %d\n", num_blob_outlines); + C_BLOB* left_blob = blob_it.data(); + TBOX left_box = left_blob->bounding_box(); + C_BLOB* right_blob = blob_it.at_last() ? NULL : blob_it.data_relative(1); + if ((left_box.x_overlap(total_ol_box) || right_blob == NULL || + !right_blob->bounding_box().x_overlap(total_ol_box)) && + SelectGoodDiacriticOutlines(pass, noise_cert_disjoint, pr_it, left_blob, + outlines, num_blob_outlines, + &blob_wanted)) { + if (debug_noise_removal) tprintf("Added to left blob\n"); + for (int j = 0; j < blob_wanted.size(); ++j) { + if (blob_wanted[j]) { + (*word_wanted)[j] = true; + (*target_blobs)[j] = left_blob; + } + } + } + else if (right_blob != NULL && + (!left_box.x_overlap(total_ol_box) || + right_blob->bounding_box().x_overlap(total_ol_box)) && + SelectGoodDiacriticOutlines(pass, noise_cert_disjoint, pr_it, + right_blob, outlines, + num_blob_outlines, &blob_wanted)) { + if (debug_noise_removal) tprintf("Added to right blob\n"); + for (int j = 0; j < blob_wanted.size(); ++j) { + if (blob_wanted[j]) { + (*word_wanted)[j] = true; + (*target_blobs)[j] = right_blob; + } + } + } + else if (SelectGoodDiacriticOutlines(pass, noise_cert_punc, pr_it, NULL, + outlines, num_blob_outlines, + &blob_wanted)) { + if (debug_noise_removal) tprintf("Fitted between blobs\n"); + for (int j = 0; j < blob_wanted.size(); ++j) { + if (blob_wanted[j]) { + (*word_wanted)[j] = true; + (*target_blobs)[j] = NULL; + } + } + } + } + } + + // Starting with ok_outlines set to indicate which outlines overlap the blob, + // chooses the optimal set (approximately) and returns true if any outlines + // are desired, in which case ok_outlines indicates which ones. + bool Tesseract::SelectGoodDiacriticOutlines( + int pass, float certainty_threshold, PAGE_RES_IT* pr_it, C_BLOB* blob, + const GenericVector& outlines, int num_outlines, + GenericVector* ok_outlines) { + STRING best_str; + float target_cert = certainty_threshold; + if (blob != NULL) { + float target_c2; + target_cert = ClassifyBlobAsWord(pass, pr_it, blob, &best_str, &target_c2); + if (debug_noise_removal) { + tprintf("No Noise blob classified as %s=%g(%g) at:", best_str.string(), + target_cert, target_c2); + blob->bounding_box().print(); + } + target_cert -= (target_cert - certainty_threshold) * noise_cert_factor; + } + GenericVector test_outlines = *ok_outlines; + // Start with all the outlines in. + STRING all_str; + GenericVector best_outlines = *ok_outlines; + float best_cert = ClassifyBlobPlusOutlines(test_outlines, outlines, pass, + pr_it, blob, &all_str); + if (debug_noise_removal) { + TBOX ol_box; + for (int i = 0; i < test_outlines.size(); ++i) { + if (test_outlines[i]) ol_box += outlines[i]->bounding_box(); + } + tprintf("All Noise blob classified as %s=%g, delta=%g at:", + all_str.string(), best_cert, best_cert - target_cert); + ol_box.print(); + } + // Iteratively zero out the bit that improves the certainty the most, until + // we get past the threshold, have zero bits, or fail to improve. + int best_index = 0; // To zero out. + while (num_outlines > 1 && best_index >= 0 && + (blob == NULL || best_cert < target_cert || blob != NULL)) { + // Find the best bit to zero out. + best_index = -1; + for (int i = 0; i < outlines.size(); ++i) { + if (test_outlines[i]) { + test_outlines[i] = false; + STRING str; + float cert = ClassifyBlobPlusOutlines(test_outlines, outlines, pass, + pr_it, blob, &str); + if (debug_noise_removal) { + TBOX ol_box; + for (int j = 0; j < outlines.size(); ++j) { + if (test_outlines[j]) ol_box += outlines[j]->bounding_box(); + tprintf("%d", test_outlines[j]); + } + tprintf(" blob classified as %s=%g, delta=%g) at:", str.string(), + cert, cert - target_cert); + ol_box.print(); + } + if (cert > best_cert) { + best_cert = cert; + best_index = i; + best_outlines = test_outlines; + } + test_outlines[i] = true; + } + } + if (best_index >= 0) { + test_outlines[best_index] = false; + --num_outlines; + } + } + if (best_cert >= target_cert) { + // Save the best combination. + *ok_outlines = best_outlines; + if (debug_noise_removal) { + tprintf("%s noise combination ", blob ? "Adding" : "New"); + for (int i = 0; i < best_outlines.size(); ++i) { + tprintf("%d", best_outlines[i]); + } + tprintf(" yields certainty %g, beating target of %g\n", best_cert, + target_cert); + } + return true; + } + return false; + } + + // Classifies the given blob plus the outlines flagged by ok_outlines, undoes + // the inclusion of the outlines, and returns the certainty of the raw choice. + float Tesseract::ClassifyBlobPlusOutlines( + const GenericVector& ok_outlines, + const GenericVector& outlines, int pass_n, PAGE_RES_IT* pr_it, + C_BLOB* blob, STRING* best_str) { + C_OUTLINE_IT ol_it; + C_OUTLINE* first_to_keep = NULL; + if (blob != NULL) { + // Add the required outlines to the blob. + ol_it.set_to_list(blob->out_list()); + first_to_keep = ol_it.data(); + } + for (int i = 0; i < ok_outlines.size(); ++i) { + if (ok_outlines[i]) { + // This outline is to be added. + if (blob == NULL) { + blob = new C_BLOB(outlines[i]); + ol_it.set_to_list(blob->out_list()); + } + else { + ol_it.add_before_stay_put(outlines[i]); + } + } + } + float c2; + float cert = ClassifyBlobAsWord(pass_n, pr_it, blob, best_str, &c2); + ol_it.move_to_first(); + if (first_to_keep == NULL) { + // We created blob. Empty its outlines and delete it. + for (; !ol_it.empty(); ol_it.forward()) ol_it.extract(); + delete blob; + cert = -c2; + } + else { + // Remove the outlines that we put in. + for (; ol_it.data() != first_to_keep; ol_it.forward()) { + ol_it.extract(); + } + } + return cert; + } + + // Classifies the given blob (part of word_data->word->word) as an individual + // word, using languages, chopper etc, returning only the certainty of the + // best raw choice, and undoing all the work done to fake out the word. + float Tesseract::ClassifyBlobAsWord(int pass_n, PAGE_RES_IT* pr_it, + C_BLOB* blob, STRING* best_str, float* c2) { + WERD* real_word = pr_it->word()->word; + WERD* word = real_word->ConstructFromSingleBlob( + real_word->flag(W_BOL), real_word->flag(W_EOL), C_BLOB::deep_copy(blob)); + WERD_RES* word_res = pr_it->InsertSimpleCloneWord(*pr_it->word(), word); + // Get a new iterator that points to the new word. + PAGE_RES_IT it(pr_it->page_res); + while (it.word() != word_res && it.word() != NULL) it.forward(); + ASSERT_HOST(it.word() == word_res); + WordData wd(it); + // Force full initialization. + SetupWordPassN(1, &wd); + classify_word_and_language(pass_n, &it, &wd); + if (debug_noise_removal) { + tprintf("word xheight=%g, row=%g, range=[%g,%g]\n", word_res->x_height, + wd.row->x_height(), wd.word->raw_choice->min_x_height(), + wd.word->raw_choice->max_x_height()); + } + float cert = wd.word->raw_choice->certainty(); + float rat = wd.word->raw_choice->rating(); + *c2 = rat > 0.0f ? cert * cert / rat : 0.0f; + *best_str = wd.word->raw_choice->unichar_string(); + it.DeleteCurrentWord(); + pr_it->ResetWordIterator(); + return cert; + } + + // Generic function for classifying a word. Can be used either for pass1 or + // pass2 according to the function passed to recognizer. + // word_data holds the word to be recognized, and its block and row, and + // pr_it points to the word as well, in case we are running LSTM and it wants + // to output multiple words. + // Recognizes in the current language, and if successful that is all. + // If recognition was not successful, tries all available languages until + // it gets a successful result or runs out of languages. Keeps the best result. + void Tesseract::classify_word_and_language(int pass_n, PAGE_RES_IT* pr_it, + WordData* word_data) { + WordRecognizer recognizer = pass_n == 1 ? &Tesseract::classify_word_pass1 + : &Tesseract::classify_word_pass2; + // Best result so far. + PointerVector best_words; + // Points to the best result. May be word or in lang_words. + WERD_RES* word = word_data->word; + clock_t start_t = clock(); + if (classify_debug_level || cube_debug_level) { + tprintf("%s word with lang %s at:", + word->done ? "Already done" : "Processing", + most_recently_used_->lang.string()); + word->word->bounding_box().print(); + } + if (word->done) { + // If done on pass1, leave it as-is. + if (!word->tess_failed) + most_recently_used_ = word->tesseract; + return; + } + int sub = sub_langs_.size(); + if (most_recently_used_ != this) { + // Get the index of the most_recently_used_. + for (sub = 0; sub < sub_langs_.size() && + most_recently_used_ != sub_langs_[sub]; ++sub) { + } + } + most_recently_used_->RetryWithLanguage( + *word_data, recognizer, &word_data->lang_words[sub], &best_words); + Tesseract* best_lang_tess = most_recently_used_; + if (!WordsAcceptable(best_words)) { + // Try all the other languages to see if they are any better. + if (most_recently_used_ != this && + this->RetryWithLanguage(*word_data, recognizer, + &word_data->lang_words[sub_langs_.size()], + &best_words) > 0) { + best_lang_tess = this; + } + for (int i = 0; !WordsAcceptable(best_words) && i < sub_langs_.size(); + ++i) { + if (most_recently_used_ != sub_langs_[i] && + sub_langs_[i]->RetryWithLanguage(*word_data, recognizer, + &word_data->lang_words[i], + &best_words) > 0) { + best_lang_tess = sub_langs_[i]; + } + } + } + most_recently_used_ = best_lang_tess; + if (!best_words.empty()) { + if (best_words.size() == 1 && !best_words[0]->combination) { + // Move the best single result to the main word. + word_data->word->ConsumeWordResults(best_words[0]); + } + else { + // Words came from LSTM, and must be moved to the PAGE_RES properly. + word_data->word = best_words.back(); + pr_it->ReplaceCurrentWord(&best_words); + } + ASSERT_HOST(word_data->word->box_word != NULL); + } + else { + tprintf("no best words!!\n"); + } + clock_t ocr_t = clock(); + if (tessedit_timing_debug) { + tprintf("%s (ocr took %.2f sec)\n", + word->best_choice->unichar_string().string(), + static_cast(ocr_t - start_t) / CLOCKS_PER_SEC); + } + } + + /** + * classify_word_pass1 + * + * Baseline normalize the word and pass it to Tess. + */ + + void Tesseract::classify_word_pass1(const WordData& word_data, + WERD_RES** in_word, + PointerVector* out_words) { + ROW* row = word_data.row; + BLOCK* block = word_data.block; + prev_word_best_choice_ = word_data.prev_word != NULL + ? word_data.prev_word->word->best_choice : NULL; +#ifndef NO_CUBE_BUILD + // If we only intend to run cube - run it and return. + if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { + cube_word_pass1(block, row, *in_word); + return; + } +#endif + WERD_RES* word = *in_word; + match_word_pass_n(1, word, row, block); + if (!word->tess_failed && !word->word->flag(W_REP_CHAR)) { + word->tess_would_adapt = AdaptableWord(word); + bool adapt_ok = word_adaptable(word, tessedit_tess_adaption_mode); + + if (adapt_ok) { + // Send word to adaptive classifier for training. + word->BestChoiceToCorrectText(); + LearnWord(NULL, word); + // Mark misadaptions if running blamer. + if (word->blamer_bundle != NULL) { + word->blamer_bundle->SetMisAdaptionDebug(word->best_choice, + wordrec_debug_blamer); + } + } + + if (tessedit_enable_doc_dict && !word->IsAmbiguous()) + tess_add_doc_word(word->best_choice); + } + } + + // Helper to report the result of the xheight fix. + void Tesseract::ReportXhtFixResult(bool accept_new_word, float new_x_ht, + WERD_RES* word, WERD_RES* new_word) { + tprintf("New XHT Match:%s = %s ", + word->best_choice->unichar_string().string(), + word->best_choice->debug_string().string()); + word->reject_map.print(debug_fp); + tprintf(" -> %s = %s ", + new_word->best_choice->unichar_string().string(), + new_word->best_choice->debug_string().string()); + new_word->reject_map.print(debug_fp); + tprintf(" %s->%s %s %s\n", + word->guessed_x_ht ? "GUESS" : "CERT", + new_word->guessed_x_ht ? "GUESS" : "CERT", + new_x_ht > 0.1 ? "STILL DOUBT" : "OK", + accept_new_word ? "ACCEPTED" : ""); + } + + // Run the x-height fix-up, based on min/max top/bottom information in + // unicharset. + // Returns true if the word was changed. + // See the comment in fixxht.cpp for a description of the overall process. + bool Tesseract::TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row) { + int original_misfits = CountMisfitTops(word); + if (original_misfits == 0) + return false; + float baseline_shift = 0.0f; + float new_x_ht = ComputeCompatibleXheight(word, &baseline_shift); + if (baseline_shift != 0.0f) { + // Try the shift on its own first. + if (!TestNewNormalization(original_misfits, baseline_shift, word->x_height, + word, block, row)) + return false; + original_misfits = CountMisfitTops(word); + if (original_misfits > 0) { + float new_baseline_shift; + // Now recompute the new x_height. + new_x_ht = ComputeCompatibleXheight(word, &new_baseline_shift); + if (new_x_ht >= kMinRefitXHeightFraction * word->x_height) { + // No test of return value here, as we are definitely making a change + // to the word by shifting the baseline. + TestNewNormalization(original_misfits, baseline_shift, new_x_ht, + word, block, row); + } + } + return true; + } + else if (new_x_ht >= kMinRefitXHeightFraction * word->x_height) { + return TestNewNormalization(original_misfits, 0.0f, new_x_ht, + word, block, row); + } + else { + return false; + } + } + + // Runs recognition with the test baseline shift and x-height and returns true + // if there was an improvement in recognition result. + bool Tesseract::TestNewNormalization(int original_misfits, + float baseline_shift, float new_x_ht, + WERD_RES *word, BLOCK* block, ROW *row) { + bool accept_new_x_ht = false; + WERD_RES new_x_ht_word(word->word); + if (word->blamer_bundle != NULL) { + new_x_ht_word.blamer_bundle = new BlamerBundle(); + new_x_ht_word.blamer_bundle->CopyTruth(*(word->blamer_bundle)); + } + new_x_ht_word.x_height = new_x_ht; + new_x_ht_word.baseline_shift = baseline_shift; + new_x_ht_word.caps_height = 0.0; + new_x_ht_word.SetupForRecognition( + unicharset, this, BestPix(), tessedit_ocr_engine_mode, NULL, + classify_bln_numeric_mode, textord_use_cjk_fp_model, + poly_allow_detailed_fx, row, block); + match_word_pass_n(2, &new_x_ht_word, row, block); + if (!new_x_ht_word.tess_failed) { + int new_misfits = CountMisfitTops(&new_x_ht_word); + if (debug_x_ht_level >= 1) { + tprintf("Old misfits=%d with x-height %f, new=%d with x-height %f\n", + original_misfits, word->x_height, + new_misfits, new_x_ht); + tprintf("Old rating= %f, certainty=%f, new=%f, %f\n", + word->best_choice->rating(), word->best_choice->certainty(), + new_x_ht_word.best_choice->rating(), + new_x_ht_word.best_choice->certainty()); + } + // The misfits must improve and either the rating or certainty. + accept_new_x_ht = new_misfits < original_misfits && + (new_x_ht_word.best_choice->certainty() > + word->best_choice->certainty() || + new_x_ht_word.best_choice->rating() < + word->best_choice->rating()); + if (debug_x_ht_level >= 1) { + ReportXhtFixResult(accept_new_x_ht, new_x_ht, word, &new_x_ht_word); + } + } + if (accept_new_x_ht) { + word->ConsumeWordResults(&new_x_ht_word); + return true; + } + return false; + } + + /** + * classify_word_pass2 + * + * Control what to do with the word in pass 2 + */ + + void Tesseract::classify_word_pass2(const WordData& word_data, + WERD_RES** in_word, + PointerVector* out_words) { + // Return if we do not want to run Tesseract. + if (tessedit_ocr_engine_mode != OEM_TESSERACT_ONLY && + tessedit_ocr_engine_mode != OEM_TESSERACT_CUBE_COMBINED && + word_data.word->best_choice != NULL) + return; + if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { + return; + } + ROW* row = word_data.row; + BLOCK* block = word_data.block; + WERD_RES* word = *in_word; + prev_word_best_choice_ = word_data.prev_word != NULL + ? word_data.prev_word->word->best_choice : NULL; + + set_global_subloc_code(SUBLOC_NORM); + check_debug_pt(word, 30); + if (!word->done) { + word->caps_height = 0.0; + if (word->x_height == 0.0f) + word->x_height = row->x_height(); + match_word_pass_n(2, word, row, block); + check_debug_pt(word, 40); + } + + SubAndSuperscriptFix(word); + + if (!word->tess_failed && !word->word->flag(W_REP_CHAR)) { + if (unicharset.top_bottom_useful() && unicharset.script_has_xheight() && + block->classify_rotation().y() == 0.0f) { + // Use the tops and bottoms since they are available. + TrainedXheightFix(word, block, row); + } + + set_global_subloc_code(SUBLOC_NORM); + } +#ifndef GRAPHICS_DISABLED + if (tessedit_display_outwords) { + if (fx_win == NULL) + create_fx_win(); + clear_fx_win(); + word->rebuild_word->plot(fx_win); + TBOX wbox = word->rebuild_word->bounding_box(); + fx_win->ZoomToRectangle(wbox.left(), wbox.top(), + wbox.right(), wbox.bottom()); + ScrollView::Update(); + } +#endif + set_global_subloc_code(SUBLOC_NORM); + check_debug_pt(word, 50); + } + + + /** + * match_word_pass2 + * + * Baseline normalize the word and pass it to Tess. + */ + + void Tesseract::match_word_pass_n(int pass_n, WERD_RES *word, + ROW *row, BLOCK* block) { + if (word->tess_failed) return; + tess_segment_pass_n(pass_n, word); + + if (!word->tess_failed) { + if (!word->word->flag(W_REP_CHAR)) { + word->fix_quotes(); + if (tessedit_fix_hyphens) + word->fix_hyphens(); + /* Don't trust fix_quotes! - though I think I've fixed the bug */ + if (word->best_choice->length() != word->box_word->length()) { + tprintf("POST FIX_QUOTES FAIL String:\"%s\"; Strlen=%d;" + " #Blobs=%d\n", + word->best_choice->debug_string().string(), + word->best_choice->length(), + word->box_word->length()); + + } + word->tess_accepted = tess_acceptable_word(word); + + // Also sets word->done flag + make_reject_map(word, row, pass_n); + } + } + set_word_fonts(word); + + ASSERT_HOST(word->raw_choice != NULL); + } + + // Helper to return the best rated BLOB_CHOICE in the whole word that matches + // the given char_id, or NULL if none can be found. + static BLOB_CHOICE* FindBestMatchingChoice(UNICHAR_ID char_id, + WERD_RES* word_res) { + // Find the corresponding best BLOB_CHOICE from any position in the word_res. + BLOB_CHOICE* best_choice = NULL; + for (int i = 0; i < word_res->best_choice->length(); ++i) { + BLOB_CHOICE* choice = FindMatchingChoice(char_id, + word_res->GetBlobChoices(i)); + if (choice != NULL) { + if (best_choice == NULL || choice->rating() < best_choice->rating()) + best_choice = choice; + } + } + return best_choice; + } + + // Helper to insert blob_choice in each location in the leader word if there is + // no matching BLOB_CHOICE there already, and correct any incorrect results + // in the best_choice. + static void CorrectRepcharChoices(BLOB_CHOICE* blob_choice, + WERD_RES* word_res) { + WERD_CHOICE* word = word_res->best_choice; + for (int i = 0; i < word_res->best_choice->length(); ++i) { + BLOB_CHOICE* choice = FindMatchingChoice(blob_choice->unichar_id(), + word_res->GetBlobChoices(i)); + if (choice == NULL) { + BLOB_CHOICE_IT choice_it(word_res->GetBlobChoices(i)); + choice_it.add_before_stay_put(new BLOB_CHOICE(*blob_choice)); + } + } + // Correct any incorrect results in word. + for (int i = 0; i < word->length(); ++i) { + if (word->unichar_id(i) != blob_choice->unichar_id()) + word->set_unichar_id(blob_choice->unichar_id(), i); + } + } + + /** + * fix_rep_char() + * The word is a repeated char. (Leader.) Find the repeated char character. + * Create the appropriate single-word or multi-word sequence according to + * the size of spaces in between blobs, and correct the classifications + * where some of the characters disagree with the majority. + */ + void Tesseract::fix_rep_char(PAGE_RES_IT* page_res_it) { + WERD_RES *word_res = page_res_it->word(); + const WERD_CHOICE &word = *(word_res->best_choice); + + // Find the frequency of each unique character in the word. + SortHelper rep_ch(word.length()); + for (int i = 0; i < word.length(); ++i) { + rep_ch.Add(word.unichar_id(i), 1); + } + + // Find the most frequent result. + UNICHAR_ID maxch_id = INVALID_UNICHAR_ID; // most common char + int max_count = rep_ch.MaxCount(&maxch_id); + // Find the best exemplar of a classifier result for maxch_id. + BLOB_CHOICE* best_choice = FindBestMatchingChoice(maxch_id, word_res); + if (best_choice == NULL) { + tprintf("Failed to find a choice for %s, occurring %d times\n", + word_res->uch_set->debug_str(maxch_id).string(), max_count); + return; + } + word_res->done = TRUE; + + // Measure the mean space. + int gap_count = 0; + WERD* werd = word_res->word; + C_BLOB_IT blob_it(werd->cblob_list()); + C_BLOB* prev_blob = blob_it.data(); + for (blob_it.forward(); !blob_it.at_first(); blob_it.forward()) { + C_BLOB* blob = blob_it.data(); + int gap = blob->bounding_box().left(); + gap -= prev_blob->bounding_box().right(); + ++gap_count; + prev_blob = blob; + } + // Just correct existing classification. + CorrectRepcharChoices(best_choice, word_res); + word_res->reject_map.initialise(word.length()); + } + + ACCEPTABLE_WERD_TYPE Tesseract::acceptable_word_string( + const UNICHARSET& char_set, const char *s, const char *lengths) { + int i = 0; + int offset = 0; + int leading_punct_count; + int upper_count = 0; + int hyphen_pos = -1; + ACCEPTABLE_WERD_TYPE word_type = AC_UNACCEPTABLE; + + if (strlen(lengths) > 20) + return word_type; + + /* Single Leading punctuation char*/ + + if (s[offset] != '\0' && STRING(chs_leading_punct).contains(s[offset])) + offset += lengths[i++]; + leading_punct_count = i; + + /* Initial cap */ + while (s[offset] != '\0' && char_set.get_isupper(s + offset, lengths[i])) { + offset += lengths[i++]; + upper_count++; + } + if (upper_count > 1) { + word_type = AC_UPPER_CASE; + } + else { + /* Lower case word, possibly with an initial cap */ + while (s[offset] != '\0' && char_set.get_islower(s + offset, lengths[i])) { + offset += lengths[i++]; + } + if (i - leading_punct_count < quality_min_initial_alphas_reqd) + goto not_a_word; + /* + Allow a single hyphen in a lower case word + - don't trust upper case - I've seen several cases of "H" -> "I-I" + */ + if (lengths[i] == 1 && s[offset] == '-') { + hyphen_pos = i; + offset += lengths[i++]; + if (s[offset] != '\0') { + while ((s[offset] != '\0') && + char_set.get_islower(s + offset, lengths[i])) { + offset += lengths[i++]; + } + if (i < hyphen_pos + 3) + goto not_a_word; + } + } + else { + /* Allow "'s" in NON hyphenated lower case words */ + if (lengths[i] == 1 && (s[offset] == '\'') && + lengths[i + 1] == 1 && (s[offset + lengths[i]] == 's')) { + offset += lengths[i++]; + offset += lengths[i++]; + } + } + if (upper_count > 0) + word_type = AC_INITIAL_CAP; + else + word_type = AC_LOWER_CASE; + } + + /* Up to two different, constrained trailing punctuation chars */ + if (lengths[i] == 1 && s[offset] != '\0' && + STRING(chs_trailing_punct1).contains(s[offset])) + offset += lengths[i++]; + if (lengths[i] == 1 && s[offset] != '\0' && i > 0 && + s[offset - lengths[i - 1]] != s[offset] && + STRING(chs_trailing_punct2).contains(s[offset])) + offset += lengths[i++]; + + if (s[offset] != '\0') + word_type = AC_UNACCEPTABLE; + + not_a_word: + + if (word_type == AC_UNACCEPTABLE) { + /* Look for abbreviation string */ + i = 0; + offset = 0; + if (s[0] != '\0' && char_set.get_isupper(s, lengths[0])) { + word_type = AC_UC_ABBREV; + while (s[offset] != '\0' && + char_set.get_isupper(s + offset, lengths[i]) && + lengths[i + 1] == 1 && s[offset + lengths[i]] == '.') { + offset += lengths[i++]; + offset += lengths[i++]; + } + } + else if (s[0] != '\0' && char_set.get_islower(s, lengths[0])) { + word_type = AC_LC_ABBREV; + while (s[offset] != '\0' && + char_set.get_islower(s + offset, lengths[i]) && + lengths[i + 1] == 1 && s[offset + lengths[i]] == '.') { + offset += lengths[i++]; + offset += lengths[i++]; + } + } + if (s[offset] != '\0') + word_type = AC_UNACCEPTABLE; + } + + return word_type; + } + + BOOL8 Tesseract::check_debug_pt(WERD_RES *word, int location) { + BOOL8 show_map_detail = FALSE; + inT16 i; + + if (!test_pt) + return FALSE; + + tessedit_rejection_debug.set_value(FALSE); + debug_x_ht_level.set_value(0); + + if (word->word->bounding_box().contains(FCOORD(test_pt_x, test_pt_y))) { + if (location < 0) + return TRUE; // For breakpoint use + tessedit_rejection_debug.set_value(TRUE); + debug_x_ht_level.set_value(2); + tprintf("\n\nTESTWD::"); + switch (location) { + case 0: + tprintf("classify_word_pass1 start\n"); + word->word->print(); + break; + case 10: + tprintf("make_reject_map: initial map"); + break; + case 20: + tprintf("make_reject_map: after NN"); + break; + case 30: + tprintf("classify_word_pass2 - START"); + break; + case 40: + tprintf("classify_word_pass2 - Pre Xht"); + break; + case 50: + tprintf("classify_word_pass2 - END"); + show_map_detail = TRUE; + break; + case 60: + tprintf("fixspace"); + break; + case 70: + tprintf("MM pass START"); + break; + case 80: + tprintf("MM pass END"); + break; + case 90: + tprintf("After Poor quality rejection"); + break; + case 100: + tprintf("unrej_good_quality_words - START"); + break; + case 110: + tprintf("unrej_good_quality_words - END"); + break; + case 120: + tprintf("Write results pass"); + show_map_detail = TRUE; + break; + } + if (word->best_choice != NULL) { + tprintf(" \"%s\" ", word->best_choice->unichar_string().string()); + word->reject_map.print(debug_fp); + tprintf("\n"); + if (show_map_detail) { + tprintf("\"%s\"\n", word->best_choice->unichar_string().string()); + for (i = 0; word->best_choice->unichar_string()[i] != '\0'; i++) { + tprintf("**** \"%c\" ****\n", word->best_choice->unichar_string()[i]); + word->reject_map[i].full_print(debug_fp); + } + } + } + else { + tprintf("null best choice\n"); + } + tprintf("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); + tprintf("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); + return TRUE; + } + else { + return FALSE; + } + } + + /** + * find_modal_font + * + * Find the modal font and remove from the stats. + */ + static void find_modal_font( //good chars in word + STATS *fonts, //font stats + inT16 *font_out, //output font + inT8 *font_count //output count + ) { + inT16 font; //font index + inT32 count; //pile couat + + if (fonts->get_total() > 0) { + font = (inT16)fonts->mode(); + *font_out = font; + count = fonts->pile_count(font); + *font_count = count < MAX_INT8 ? count : MAX_INT8; + fonts->add(font, -*font_count); + } + else { + *font_out = -1; + *font_count = 0; + } + } + + /** + * set_word_fonts + * + * Get the fonts for the word. + */ + void Tesseract::set_word_fonts(WERD_RES *word) { + // Don't try to set the word fonts for a cube word, as the configs + // will be meaningless. + if (word->chopped_word == NULL) return; + ASSERT_HOST(word->best_choice != NULL); + + int fontinfo_size = get_fontinfo_table().size(); + if (fontinfo_size == 0) return; + GenericVector font_total_score; + font_total_score.init_to_size(fontinfo_size, 0); + + word->italic = 0; + word->bold = 0; + // Compute the font scores for the word + if (tessedit_debug_fonts) { + tprintf("Examining fonts in %s\n", + word->best_choice->debug_string().string()); + } + for (int b = 0; b < word->best_choice->length(); ++b) { + BLOB_CHOICE* choice = word->GetBlobChoice(b); + if (choice == NULL) continue; + const GenericVector& fonts = choice->fonts(); + for (int f = 0; f < fonts.size(); ++f) { + int fontinfo_id = fonts[f].fontinfo_id; + if (0 <= fontinfo_id && fontinfo_id < fontinfo_size) { + font_total_score[fontinfo_id] += fonts[f].score; + } + } + } + // Find the top and 2nd choice for the word. + int score1 = 0, score2 = 0; + inT16 font_id1 = -1, font_id2 = -1; + for (int f = 0; f < fontinfo_size; ++f) { + if (tessedit_debug_fonts && font_total_score[f] > 0) { + tprintf("Font %s, total score = %d\n", + fontinfo_table_.get(f).name, font_total_score[f]); + } + if (font_total_score[f] > score1) { + score2 = score1; + font_id2 = font_id1; + score1 = font_total_score[f]; + font_id1 = f; + } + else if (font_total_score[f] > score2) { + score2 = font_total_score[f]; + font_id2 = f; + } + } + word->fontinfo = font_id1 >= 0 ? &fontinfo_table_.get(font_id1) : NULL; + word->fontinfo2 = font_id2 >= 0 ? &fontinfo_table_.get(font_id2) : NULL; + // Each score has a limit of MAX_UINT16, so divide by that to get the number + // of "votes" for that font, ie number of perfect scores. + word->fontinfo_id_count = ClipToRange(score1 / MAX_UINT16, 1, MAX_INT8); + word->fontinfo_id2_count = ClipToRange(score2 / MAX_UINT16, 0, MAX_INT8); + if (score1 > 0) { + FontInfo fi = fontinfo_table_.get(font_id1); + if (tessedit_debug_fonts) { + if (word->fontinfo_id2_count > 0) { + tprintf("Word modal font=%s, score=%d, 2nd choice %s/%d\n", + fi.name, word->fontinfo_id_count, + fontinfo_table_.get(font_id2).name, + word->fontinfo_id2_count); + } + else { + tprintf("Word modal font=%s, score=%d. No 2nd choice\n", + fi.name, word->fontinfo_id_count); + } + } + word->italic = (fi.is_italic() ? 1 : -1) * word->fontinfo_id_count; + word->bold = (fi.is_bold() ? 1 : -1) * word->fontinfo_id_count; + } + } + + + /** + * font_recognition_pass + * + * Smooth the fonts for the document. + */ + + void Tesseract::font_recognition_pass(PAGE_RES* page_res) { + PAGE_RES_IT page_res_it(page_res); + WERD_RES *word; // current word + STATS doc_fonts(0, font_table_size_); // font counters + + // Gather font id statistics. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + word = page_res_it.word(); + if (word->fontinfo != NULL) { + doc_fonts.add(word->fontinfo->universal_id, word->fontinfo_id_count); + } + if (word->fontinfo2 != NULL) { + doc_fonts.add(word->fontinfo2->universal_id, word->fontinfo_id2_count); + } + } + inT16 doc_font; // modal font + inT8 doc_font_count; // modal font + find_modal_font(&doc_fonts, &doc_font, &doc_font_count); + if (doc_font_count == 0) + return; + // Get the modal font pointer. + const FontInfo* modal_font = NULL; + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + word = page_res_it.word(); + if (word->fontinfo != NULL && word->fontinfo->universal_id == doc_font) { + modal_font = word->fontinfo; + break; + } + if (word->fontinfo2 != NULL && word->fontinfo2->universal_id == doc_font) { + modal_font = word->fontinfo2; + break; + } + } + ASSERT_HOST(modal_font != NULL); + + // Assign modal font to weak words. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + word = page_res_it.word(); + int length = word->best_choice->length(); + + int count = word->fontinfo_id_count; + if (!(count == length || (length > 3 && count >= length * 3 / 4))) { + word->fontinfo = modal_font; + // Counts only get 1 as it came from the doc. + word->fontinfo_id_count = 1; + word->italic = modal_font->is_italic() ? 1 : -1; + word->bold = modal_font->is_bold() ? 1 : -1; + } + } + } + + // If a word has multiple alternates check if the best choice is in the + // dictionary. If not, replace it with an alternate that exists in the + // dictionary. + void Tesseract::dictionary_correction_pass(PAGE_RES *page_res) { + PAGE_RES_IT word_it(page_res); + for (WERD_RES* word = word_it.word(); word != NULL; + word = word_it.forward()) { + if (word->best_choices.singleton()) + continue; // There are no alternates. + + WERD_CHOICE* best = word->best_choice; + if (word->tesseract->getDict().valid_word(*best) != 0) + continue; // The best choice is in the dictionary. + + WERD_CHOICE_IT choice_it(&word->best_choices); + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + WERD_CHOICE* alternate = choice_it.data(); + if (word->tesseract->getDict().valid_word(*alternate)) { + // The alternate choice is in the dictionary. + if (tessedit_bigram_debug) { + tprintf("Dictionary correction replaces best choice '%s' with '%s'\n", + best->unichar_string().string(), + alternate->unichar_string().string()); + } + // Replace the 'best' choice with a better choice. + word->ReplaceBestChoice(alternate); + break; + } + } + } + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/control.h b/hgdriver/3rdparty/hgOCR/include/ccmain/control.h new file mode 100644 index 0000000..f8a6599 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/control.h @@ -0,0 +1,44 @@ +/********************************************************************** + * File: control.h (Formerly control.h) + * Description: Module-independent matcher controller. + * Author: Ray Smith + * Created: Thu Apr 23 11:09:58 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + /** + * @file control.h + * Module-independent matcher controller. + */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "params.h" +#include "ocrblock.h" +#include "ratngs.h" +#include "statistc.h" +#include "pageres.h" + +enum ACCEPTABLE_WERD_TYPE +{ + AC_UNACCEPTABLE, ///< Unacceptable word + AC_LOWER_CASE, ///< ALL lower case + AC_UPPER_CASE, ///< ALL upper case + AC_INITIAL_CAP, ///< ALL but initial lc + AC_LC_ABBREV, ///< a.b.c. + AC_UC_ABBREV ///< A.B.C. +}; + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/cube_control.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_control.cpp new file mode 100644 index 0000000..ac9e50b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_control.cpp @@ -0,0 +1,440 @@ +/****************************************************************** + * File: cube_control.cpp + * Description: Tesseract class methods for invoking cube convolutional + * neural network word recognizer. + * Author: Raquel Romano + * Created: September 2009 + * + * (C) Copyright 2009, 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 automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "allheaders.h" + +#include "cube_object.h" +#include "cube_reco_context.h" +#include "tesseractclass.h" +#include "tesseract_cube_combiner.h" + +namespace tesseract { + + /** + * @name convert_prob_to_tess_certainty + * + * Normalize a probability in the range [0.0, 1.0] to a tesseract + * certainty in the range [-20.0, 0.0] + */ + static float convert_prob_to_tess_certainty(float prob) { + return (prob - 1.0) * 20.0; + } + + /** + * @name char_box_to_tbox + * + * Create a TBOX from a character bounding box. If nonzero, the + * x_offset accounts for any additional padding of the word box that + * should be taken into account. + * + */ + TBOX char_box_to_tbox(Box* char_box, TBOX word_box, int x_offset) { + l_int32 left; + l_int32 top; + l_int32 width; + l_int32 height; + l_int32 right; + l_int32 bottom; + + boxGetGeometry(char_box, &left, &top, &width, &height); + left += word_box.left() - x_offset; + right = left + width; + top = word_box.bottom() + word_box.height() - top; + bottom = top - height; + return TBOX(left, bottom, right, top); + } + + /** + * @name extract_cube_state + * + * Extract CharSamp objects and character bounding boxes from the + * CubeObject's state. The caller should free both structres. + * + */ + bool Tesseract::extract_cube_state(CubeObject* cube_obj, + int* num_chars, + Boxa** char_boxes, + CharSamp*** char_samples) { + if (!cube_obj) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (extract_cube_state): Invalid cube object " + "passed to extract_cube_state\n"); + } + return false; + } + + // Note that the CubeObject accessors return either the deslanted or + // regular objects search object or beam search object, whichever + // was used in the last call to Recognize() + CubeSearchObject* cube_search_obj = cube_obj->SrchObj(); + if (!cube_search_obj) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (Extract_cube_state): Could not retrieve " + "cube's search object in extract_cube_state.\n"); + } + return false; + } + BeamSearch *beam_search_obj = cube_obj->BeamObj(); + if (!beam_search_obj) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (Extract_cube_state): Could not retrieve " + "cube's beam search object in extract_cube_state.\n"); + } + return false; + } + + // Get the character samples and bounding boxes by backtracking + // through the beam search path + int best_node_index = beam_search_obj->BestPresortedNodeIndex(); + *char_samples = beam_search_obj->BackTrack( + cube_search_obj, best_node_index, num_chars, NULL, char_boxes); + if (!*char_samples) + return false; + return true; + } + + /** + * @name create_cube_box_word + * + * Fill the given BoxWord with boxes from character bounding + * boxes. The char_boxes have local coordinates w.r.t. the + * word bounding box, i.e., the left-most character bbox of each word + * has (0,0) left-top coord, but the BoxWord must be defined in page + * coordinates. + */ + bool Tesseract::create_cube_box_word(Boxa *char_boxes, + int num_chars, + TBOX word_box, + BoxWord* box_word) { + if (!box_word) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (create_cube_box_word): Invalid box_word.\n"); + } + return false; + } + + // Find the x-coordinate of left-most char_box, which could be + // nonzero if the word image was padded before recognition took place. + int x_offset = -1; + for (int i = 0; i < num_chars; ++i) { + Box* char_box = boxaGetBox(char_boxes, i, L_CLONE); + if (x_offset < 0 || char_box->x < x_offset) { + x_offset = char_box->x; + } + boxDestroy(&char_box); + } + + for (int i = 0; i < num_chars; ++i) { + Box* char_box = boxaGetBox(char_boxes, i, L_CLONE); + TBOX tbox = char_box_to_tbox(char_box, word_box, x_offset); + boxDestroy(&char_box); + box_word->InsertBox(i, tbox); + } + return true; + } + + /** + * @name init_cube_objects + * + * Instantiates Tesseract object's CubeRecoContext and TesseractCubeCombiner. + * Returns false if cube context could not be created or if load_combiner is + * true, but the combiner could not be loaded. + */ + bool Tesseract::init_cube_objects(bool load_combiner, + TessdataManager *tessdata_manager) { + ASSERT_HOST(cube_cntxt_ == NULL); + ASSERT_HOST(tess_cube_combiner_ == NULL); + + // Create the cube context object + cube_cntxt_ = CubeRecoContext::Create(this, tessdata_manager, &unicharset); + if (cube_cntxt_ == NULL) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (Tesseract::init_cube_objects()): Failed to " + "instantiate CubeRecoContext\n"); + } + return false; + } + + // Create the combiner object and load the combiner net for target languages. + if (load_combiner) { + tess_cube_combiner_ = new tesseract::TesseractCubeCombiner(cube_cntxt_); + if (!tess_cube_combiner_->LoadCombinerNet()) { + delete cube_cntxt_; + cube_cntxt_ = NULL; + delete tess_cube_combiner_; + tess_cube_combiner_ = NULL; + if (cube_debug_level > 0) + tprintf("Cube ERROR (Failed to instantiate TesseractCubeCombiner\n"); + return false; + } + } + return true; + } + + /** + * @name run_cube_combiner + * + * Iterates through tesseract's results and calls cube on each word, + * combining the results with the existing tesseract result. + */ + void Tesseract::run_cube_combiner(PAGE_RES *page_res) { + if (page_res == NULL || tess_cube_combiner_ == NULL) + return; + PAGE_RES_IT page_res_it(page_res); + // Iterate through the word results and call cube on each word. + for (page_res_it.restart_page(); page_res_it.word() != NULL; + page_res_it.forward()) { + BLOCK* block = page_res_it.block()->block; + if (block->poly_block() != NULL && !block->poly_block()->IsText()) + continue; // Don't deal with non-text blocks. + WERD_RES* word = page_res_it.word(); + // Skip cube entirely if tesseract's certainty is greater than threshold. + int combiner_run_thresh = convert_prob_to_tess_certainty( + cube_cntxt_->Params()->CombinerRunThresh()); + if (word->best_choice->certainty() >= combiner_run_thresh) { + continue; + } + // Use the same language as Tesseract used for the word. + Tesseract* lang_tess = word->tesseract; + + // Setup a trial WERD_RES in which to classify with cube. + WERD_RES cube_word; + cube_word.InitForRetryRecognition(*word); + cube_word.SetupForRecognition(lang_tess->unicharset, this, BestPix(), + OEM_CUBE_ONLY, + NULL, false, false, false, + page_res_it.row()->row, + page_res_it.block()->block); + CubeObject *cube_obj = lang_tess->cube_recognize_word( + page_res_it.block()->block, &cube_word); + if (cube_obj != NULL) + lang_tess->cube_combine_word(cube_obj, &cube_word, word); + delete cube_obj; + } + } + + /** + * @name cube_word_pass1 + * + * Recognizes a single word using (only) cube. Compatible with + * Tesseract's classify_word_pass1/classify_word_pass2. + */ + void Tesseract::cube_word_pass1(BLOCK* block, ROW *row, WERD_RES *word) { + CubeObject *cube_obj = cube_recognize_word(block, word); + delete cube_obj; + } + + /** + * @name cube_recognize_word + * + * Cube recognizer to recognize a single word as with classify_word_pass1 + * but also returns the cube object in case the combiner is needed. + */ + CubeObject* Tesseract::cube_recognize_word(BLOCK* block, WERD_RES* word) { + if (!cube_binary_ || !cube_cntxt_) { + if (cube_debug_level > 0 && !cube_binary_) + tprintf("Tesseract::run_cube(): NULL binary image.\n"); + word->SetupFake(unicharset); + return NULL; + } + TBOX word_box = word->word->bounding_box(); + if (block != NULL && (block->re_rotation().x() != 1.0f || + block->re_rotation().y() != 0.0f)) { + // TODO(rays) We have to rotate the bounding box to get the true coords. + // This will be achieved in the future via DENORM. + // In the mean time, cube can't process this word. + if (cube_debug_level > 0) { + tprintf("Cube can't process rotated word at:"); + word_box.print(); + } + word->SetupFake(unicharset); + return NULL; + } + CubeObject* cube_obj = new tesseract::CubeObject( + cube_cntxt_, cube_binary_, word_box.left(), + pixGetHeight(cube_binary_) - word_box.top(), + word_box.width(), word_box.height()); + if (!cube_recognize(cube_obj, block, word)) { + delete cube_obj; + return NULL; + } + return cube_obj; + } + + /** + * @name cube_combine_word + * + * Combines the cube and tesseract results for a single word, leaving the + * result in tess_word. + */ + void Tesseract::cube_combine_word(CubeObject* cube_obj, WERD_RES* cube_word, + WERD_RES* tess_word) { + float combiner_prob = tess_cube_combiner_->CombineResults(tess_word, + cube_obj); + // If combiner probability is greater than tess/cube combiner + // classifier threshold, i.e. tesseract wins, then just return the + // tesseract result unchanged, as the combiner knows nothing about how + // correct the answer is. If cube and tesseract agree, then improve the + // scores before returning. + WERD_CHOICE* tess_best = tess_word->best_choice; + WERD_CHOICE* cube_best = cube_word->best_choice; + if (cube_debug_level || classify_debug_level) { + tprintf("Combiner prob = %g vs threshold %g\n", + combiner_prob, cube_cntxt_->Params()->CombinerClassifierThresh()); + } + if (combiner_prob >= + cube_cntxt_->Params()->CombinerClassifierThresh()) { + if (tess_best->unichar_string() == cube_best->unichar_string()) { + // Cube and tess agree, so improve the scores. + tess_best->set_rating(tess_best->rating() / 2); + tess_best->set_certainty(tess_best->certainty() / 2); + } + return; + } + // Cube wins. + // It is better for the language combiner to have all tesseract scores, + // so put them in the cube result. + cube_best->set_rating(tess_best->rating()); + cube_best->set_certainty(tess_best->certainty()); + if (cube_debug_level || classify_debug_level) { + tprintf("Cube INFO: tesseract result replaced by cube: %s -> %s\n", + tess_best->unichar_string().string(), + cube_best->unichar_string().string()); + } + tess_word->ConsumeWordResults(cube_word); + } + + /** + * @name cube_recognize + * + * Call cube on the current word, and write the result to word. + * Sets up a fake result and returns false if something goes wrong. + */ + bool Tesseract::cube_recognize(CubeObject *cube_obj, BLOCK* block, + WERD_RES *word) { + // Run cube + WordAltList *cube_alt_list = cube_obj->RecognizeWord(); + if (!cube_alt_list || cube_alt_list->AltCount() <= 0) { + if (cube_debug_level > 0) { + tprintf("Cube returned nothing for word at:"); + word->word->bounding_box().print(); + } + word->SetupFake(unicharset); + return false; + } + + // Get cube's best result and its probability, mapped to tesseract's + // certainty range + char_32 *cube_best_32 = cube_alt_list->Alt(0); + double cube_prob = CubeUtils::Cost2Prob(cube_alt_list->AltCost(0)); + float cube_certainty = convert_prob_to_tess_certainty(cube_prob); + string cube_best_str; + CubeUtils::UTF32ToUTF8(cube_best_32, &cube_best_str); + + // Retrieve Cube's character bounding boxes and CharSamples, + // corresponding to the most recent call to RecognizeWord(). + Boxa *char_boxes = NULL; + CharSamp **char_samples = NULL;; + int num_chars; + if (!extract_cube_state(cube_obj, &num_chars, &char_boxes, &char_samples) + && cube_debug_level > 0) { + tprintf("Cube WARNING (Tesseract::cube_recognize): Cannot extract " + "cube state.\n"); + word->SetupFake(unicharset); + return false; + } + + // Convert cube's character bounding boxes to a BoxWord. + BoxWord cube_box_word; + TBOX tess_word_box = word->word->bounding_box(); + if (word->denorm.block() != NULL) + tess_word_box.rotate(word->denorm.block()->re_rotation()); + bool box_word_success = create_cube_box_word(char_boxes, num_chars, + tess_word_box, + &cube_box_word); + boxaDestroy(&char_boxes); + if (!box_word_success) { + if (cube_debug_level > 0) { + tprintf("Cube WARNING (Tesseract::cube_recognize): Could not " + "create cube BoxWord\n"); + } + word->SetupFake(unicharset); + return false; + } + + // Fill tesseract result's fields with cube results + fill_werd_res(cube_box_word, cube_best_str.c_str(), word); + + // Create cube's best choice. + BLOB_CHOICE** choices = new BLOB_CHOICE*[num_chars]; + for (int i = 0; i < num_chars; ++i) { + UNICHAR_ID uch_id = + cube_cntxt_->CharacterSet()->UnicharID(char_samples[i]->StrLabel()); + choices[i] = new BLOB_CHOICE(uch_id, -cube_certainty, cube_certainty, + -1, 0.0f, 0.0f, 0.0f, BCC_STATIC_CLASSIFIER); + } + word->FakeClassifyWord(num_chars, choices); + // within a word, cube recognizes the word in reading order. + word->best_choice->set_unichars_in_script_order(true); + delete[] choices; + delete[] char_samples; + + // Some sanity checks + ASSERT_HOST(word->best_choice->length() == word->reject_map.length()); + + if (cube_debug_level || classify_debug_level) { + tprintf("Cube result: %s r=%g, c=%g\n", + word->best_choice->unichar_string().string(), + word->best_choice->rating(), + word->best_choice->certainty()); + } + return true; + } + + /** + * @name fill_werd_res + * + * Fill Tesseract's word result fields with cube's. + * + */ + void Tesseract::fill_werd_res(const BoxWord& cube_box_word, + const char* cube_best_str, + WERD_RES* tess_werd_res) { + delete tess_werd_res->box_word; + tess_werd_res->box_word = new BoxWord(cube_box_word); + tess_werd_res->box_word->ClipToOriginalWord(tess_werd_res->denorm.block(), + tess_werd_res->word); + // Fill text and remaining fields + tess_werd_res->word->set_text(cube_best_str); + tess_werd_res->tess_failed = FALSE; + tess_werd_res->tess_accepted = tess_acceptable_word(tess_werd_res); + // There is no output word, so we can' call AdaptableWord, but then I don't + // think we need to. Fudge the result with accepted. + tess_werd_res->tess_would_adapt = tess_werd_res->tess_accepted; + + // Set word to done, i.e., ignore all of tesseract's tests for rejection + tess_werd_res->done = tess_werd_res->tess_accepted; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.cpp new file mode 100644 index 0000000..c9ce489 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.cpp @@ -0,0 +1,184 @@ +/********************************************************************** + * File: cube_reco_context.cpp + * Description: Implementation of the Cube Recognition Context 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 +#include + +#include "cube_reco_context.h" + +#include "classifier_factory.h" +#include "cube_tuning_params.h" +#include "dict.h" +#include "feature_bmp.h" +#include "tessdatamanager.h" +#include "tesseractclass.h" +#include "tess_lang_model.h" + +namespace tesseract { + + /** + * Instantiate a CubeRecoContext object using a Tesseract object. + * CubeRecoContext will not take ownership of tess_obj, but will + * record the pointer to it and will make use of various Tesseract + * components (language model, flags, etc). Thus the caller should + * keep tess_obj alive so long as the instantiated CubeRecoContext is used. + */ + CubeRecoContext::CubeRecoContext(Tesseract *tess_obj) { + tess_obj_ = tess_obj; + lang_ = ""; + loaded_ = false; + lang_mod_ = NULL; + params_ = NULL; + char_classifier_ = NULL; + char_set_ = NULL; + word_size_model_ = NULL; + char_bigrams_ = NULL; + word_unigrams_ = NULL; + noisy_input_ = false; + size_normalization_ = false; + } + + CubeRecoContext::~CubeRecoContext() { + delete char_classifier_; + char_classifier_ = NULL; + + delete word_size_model_; + word_size_model_ = NULL; + + delete char_set_; + char_set_ = NULL; + + delete char_bigrams_; + char_bigrams_ = NULL; + + delete word_unigrams_; + word_unigrams_ = NULL; + + delete lang_mod_; + lang_mod_ = NULL; + + delete params_; + params_ = NULL; + } + + /** + * Returns the path of the data files by looking up the TESSDATA_PREFIX + * environment variable and appending a "tessdata" directory to it + */ + bool CubeRecoContext::GetDataFilePath(string *path) const { + *path = tess_obj_->datadir.string(); + return true; + } + + /** + * The object initialization function that loads all the necessary + * components of a RecoContext. TessdataManager is used to load the + * data from [lang].traineddata file. If TESSDATA_CUBE_UNICHARSET + * component is present, Cube will be instantiated with the unicharset + * specified in this component and the corresponding dictionary + * (TESSDATA_CUBE_SYSTEM_DAWG), and will map Cube's unicharset to + * Tesseract's. Otherwise, TessdataManager will assume that Cube will + * be using Tesseract's unicharset and dawgs, and will load the + * unicharset from the TESSDATA_UNICHARSET component and will load the + * dawgs from TESSDATA_*_DAWG components. + */ + bool CubeRecoContext::Load(TessdataManager *tessdata_manager, + UNICHARSET *tess_unicharset) { + ASSERT_HOST(tess_obj_ != NULL); + tess_unicharset_ = tess_unicharset; + string data_file_path; + + // Get the data file path. + if (GetDataFilePath(&data_file_path) == false) { + fprintf(stderr, "Unable to get data file path\n"); + return false; + } + + // Get the language from the Tesseract object. + lang_ = tess_obj_->lang.string(); + + // Create the char set. + if ((char_set_ = + CharSet::Create(tessdata_manager, tess_unicharset)) == NULL) { + fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to load " + "CharSet\n"); + return false; + } + // Create the language model. + string lm_file_name = data_file_path + lang_ + ".cube.lm"; + string lm_params; + if (!CubeUtils::ReadFileToString(lm_file_name, &lm_params)) { + fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to read cube " + "language model params from %s\n", lm_file_name.c_str()); + return false; + } + lang_mod_ = new TessLangModel(lm_params, data_file_path, + tess_obj_->getDict().load_system_dawg, + tessdata_manager, this); + + // Create the optional char bigrams object. + char_bigrams_ = CharBigrams::Create(data_file_path, lang_); + + // Create the optional word unigrams object. + word_unigrams_ = WordUnigrams::Create(data_file_path, lang_); + + // Create the optional size model. + word_size_model_ = WordSizeModel::Create(data_file_path, lang_, + char_set_, Contextual()); + + // Load tuning params. + params_ = CubeTuningParams::Create(data_file_path, lang_); + if (params_ == NULL) { + fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to read " + "CubeTuningParams from %s\n", data_file_path.c_str()); + return false; + } + + // Create the char classifier. + char_classifier_ = CharClassifierFactory::Create(data_file_path, lang_, + lang_mod_, char_set_, + params_); + if (char_classifier_ == NULL) { + fprintf(stderr, "Cube ERROR (CubeRecoContext::Load): unable to load " + "CharClassifierFactory object from %s\n", data_file_path.c_str()); + return false; + } + + loaded_ = true; + + return true; + } + + /** Creates a CubeRecoContext object using a tesseract object */ + CubeRecoContext * CubeRecoContext::Create(Tesseract *tess_obj, + TessdataManager *tessdata_manager, + UNICHARSET *tess_unicharset) { + // create the object + CubeRecoContext *cntxt = new CubeRecoContext(tess_obj); + // load the necessary components + if (cntxt->Load(tessdata_manager, tess_unicharset) == false) { + fprintf(stderr, "Cube ERROR (CubeRecoContext::Create): unable to init " + "CubeRecoContext object\n"); + delete cntxt; + return NULL; + } + // success + return cntxt; + } +} // tesseract} diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.h b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.h new file mode 100644 index 0000000..120585a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/cube_reco_context.h @@ -0,0 +1,157 @@ +/********************************************************************** + * File: cube_reco_context.h + * Description: Declaration of the Cube Recognition Context 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 CubeRecoContext class abstracts the Cube OCR Engine. Typically a process + // (or a thread) would create one CubeRecoContext object per language. + // The CubeRecoContext object also provides methods to get and set the + // different attribues of the Cube OCR Engine. + +#ifndef CUBE_RECO_CONTEXT_H +#define CUBE_RECO_CONTEXT_H + +#include +#include "neural_net.h" +#include "lang_model.h" +#include "classifier_base.h" +#include "feature_base.h" +#include "char_set.h" +#include "word_size_model.h" +#include "char_bigrams.h" +#include "word_unigrams.h" + +namespace tesseract { + + class Tesseract; + class TessdataManager; + + class CubeRecoContext { + public: + // Reading order enum type + enum ReadOrder { + L2R, + R2L + }; + + // Instantiate using a Tesseract object + CubeRecoContext(Tesseract *tess_obj); + + ~CubeRecoContext(); + + // accessor functions + inline const string & Lang() const { return lang_; } + inline CharSet *CharacterSet() const { return char_set_; } + const UNICHARSET *TessUnicharset() const { return tess_unicharset_; } + inline CharClassifier *Classifier() const { return char_classifier_; } + inline WordSizeModel *SizeModel() const { return word_size_model_; } + inline CharBigrams *Bigrams() const { return char_bigrams_; } + inline WordUnigrams *WordUnigramsObj() const { return word_unigrams_; } + inline TuningParams *Params() const { return params_; } + inline LangModel *LangMod() const { return lang_mod_; } + + // the reading order of the language + inline ReadOrder ReadingOrder() const { + return ((lang_ == "ara") ? R2L : L2R); + } + + // does the language support case + inline bool HasCase() const { + return (lang_ != "ara" && lang_ != "hin"); + } + + inline bool Cursive() const { + return (lang_ == "ara"); + } + + inline bool HasItalics() const { + return (lang_ != "ara" && lang_ != "hin"); + } + + inline bool Contextual() const { + return (lang_ == "ara"); + } + + // RecoContext runtime flags accessor functions + inline bool SizeNormalization() const { return size_normalization_; } + inline bool NoisyInput() const { return noisy_input_; } + inline bool OOD() const { return lang_mod_->OOD(); } + inline bool Numeric() const { return lang_mod_->Numeric(); } + inline bool WordList() const { return lang_mod_->WordList(); } + inline bool Punc() const { return lang_mod_->Punc(); } + inline bool CaseSensitive() const { + return char_classifier_->CaseSensitive(); + } + + inline void SetSizeNormalization(bool size_normalization) { + size_normalization_ = size_normalization; + } + inline void SetNoisyInput(bool noisy_input) { + noisy_input_ = noisy_input; + } + inline void SetOOD(bool ood_enabled) { + lang_mod_->SetOOD(ood_enabled); + } + inline void SetNumeric(bool numeric_enabled) { + lang_mod_->SetNumeric(numeric_enabled); + } + inline void SetWordList(bool word_list_enabled) { + lang_mod_->SetWordList(word_list_enabled); + } + inline void SetPunc(bool punc_enabled) { + lang_mod_->SetPunc(punc_enabled); + } + inline void SetCaseSensitive(bool case_sensitive) { + char_classifier_->SetCaseSensitive(case_sensitive); + } + inline tesseract::Tesseract *TesseractObject() const { + return tess_obj_; + } + + // Returns the path of the data files + bool GetDataFilePath(string *path) const; + // Creates a CubeRecoContext object using a tesseract object. Data + // files are loaded via the tessdata_manager, and the tesseract + // unicharset is provided in order to map Cube's unicharset to + // Tesseract's in the case where the two unicharsets differ. + static CubeRecoContext *Create(Tesseract *tess_obj, + TessdataManager *tessdata_manager, + UNICHARSET *tess_unicharset); + + private: + bool loaded_; + string lang_; + CharSet *char_set_; + UNICHARSET *tess_unicharset_; + WordSizeModel *word_size_model_; + CharClassifier *char_classifier_; + CharBigrams *char_bigrams_; + WordUnigrams *word_unigrams_; + TuningParams *params_; + LangModel *lang_mod_; + Tesseract *tess_obj_; // CubeRecoContext does not own this pointer + bool size_normalization_; + bool noisy_input_; + + // Loads and initialized all the necessary components of a + // CubeRecoContext. See .cpp for more details. + bool Load(TessdataManager *tessdata_manager, + UNICHARSET *tess_unicharset); + }; +} + +#endif // CUBE_RECO_CONTEXT_H diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.cpp new file mode 100644 index 0000000..9bf2877 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.cpp @@ -0,0 +1,134 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: cubeclassifier.cpp +// Description: Cube implementation of a ShapeClassifier. +// Author: Ray Smith +// Created: Wed Nov 23 10:39:45 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. +// +/////////////////////////////////////////////////////////////////////// + +#include "cubeclassifier.h" + +#include "char_altlist.h" +#include "char_set.h" +#include "cube_object.h" +#include "cube_reco_context.h" +#include "tessclassifier.h" +#include "tesseractclass.h" +#include "trainingsample.h" +#include "unicharset.h" + +namespace tesseract { + + CubeClassifier::CubeClassifier(tesseract::Tesseract* tesseract) + : cube_cntxt_(tesseract->GetCubeRecoContext()), + shape_table_(*tesseract->shape_table()) { + } + CubeClassifier::~CubeClassifier() { + } + + /// Classifies the given [training] sample, writing to results. + /// See ShapeClassifier for a full description. + int CubeClassifier::UnicharClassifySample( + const TrainingSample& sample, Pix* page_pix, int debug, + UNICHAR_ID keep_this, GenericVector* results) { + results->clear(); + if (page_pix == NULL) return 0; + + ASSERT_HOST(cube_cntxt_ != NULL); + const TBOX& char_box = sample.bounding_box(); + CubeObject* cube_obj = new tesseract::CubeObject( + cube_cntxt_, page_pix, char_box.left(), + pixGetHeight(page_pix) - char_box.top(), + char_box.width(), char_box.height()); + CharAltList* alt_list = cube_obj->RecognizeChar(); + if (alt_list != NULL) { + alt_list->Sort(); + CharSet* char_set = cube_cntxt_->CharacterSet(); + for (int i = 0; i < alt_list->AltCount(); ++i) { + // Convert cube representation to a shape_id. + int alt_id = alt_list->Alt(i); + int unichar_id = char_set->UnicharID(char_set->ClassString(alt_id)); + if (unichar_id >= 0) + results->push_back(UnicharRating(unichar_id, alt_list->AltProb(i))); + } + delete alt_list; + } + delete cube_obj; + return results->size(); + } + + /** Provides access to the ShapeTable that this classifier works with. */ + const ShapeTable* CubeClassifier::GetShapeTable() const { + return &shape_table_; + } + + CubeTessClassifier::CubeTessClassifier(tesseract::Tesseract* tesseract) + : cube_cntxt_(tesseract->GetCubeRecoContext()), + shape_table_(*tesseract->shape_table()), + pruner_(new TessClassifier(true, tesseract)) { + } + CubeTessClassifier::~CubeTessClassifier() { + delete pruner_; + } + + /// Classifies the given [training] sample, writing to results. + /// See ShapeClassifier for a full description. + int CubeTessClassifier::UnicharClassifySample( + const TrainingSample& sample, Pix* page_pix, int debug, + UNICHAR_ID keep_this, GenericVector* results) { + int num_results = pruner_->UnicharClassifySample(sample, page_pix, debug, + keep_this, results); + if (page_pix == NULL) return num_results; + + ASSERT_HOST(cube_cntxt_ != NULL); + const TBOX& char_box = sample.bounding_box(); + CubeObject* cube_obj = new tesseract::CubeObject( + cube_cntxt_, page_pix, char_box.left(), + pixGetHeight(page_pix) - char_box.top(), + char_box.width(), char_box.height()); + CharAltList* alt_list = cube_obj->RecognizeChar(); + CharSet* char_set = cube_cntxt_->CharacterSet(); + if (alt_list != NULL) { + for (int r = 0; r < num_results; ++r) { + // Get the best cube probability of the unichar in the result. + double best_prob = 0.0; + for (int i = 0; i < alt_list->AltCount(); ++i) { + int alt_id = alt_list->Alt(i); + int unichar_id = char_set->UnicharID(char_set->ClassString(alt_id)); + if (unichar_id == (*results)[r].unichar_id && + alt_list->AltProb(i) > best_prob) { + best_prob = alt_list->AltProb(i); + } + } + (*results)[r].rating = best_prob; + } + delete alt_list; + // Re-sort by rating. + results->sort(&UnicharRating::SortDescendingRating); + } + delete cube_obj; + return results->size(); + } + + /** Provides access to the ShapeTable that this classifier works with. */ + const ShapeTable* CubeTessClassifier::GetShapeTable() const { + return &shape_table_; + } + +} // namespace tesseract + + + diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.h b/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.h new file mode 100644 index 0000000..81667ac --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/cubeclassifier.h @@ -0,0 +1,81 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: cubeclassifier.h +// Description: Cube implementation of a ShapeClassifier. +// Author: Ray Smith +// Created: Wed Nov 23 10:36:32 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 THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ +#define THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ + +#include "shapeclassifier.h" +#include "platform.h" + +namespace tesseract { + + class Classify; + class CubeRecoContext; + class ShapeTable; + class TessClassifier; + class Tesseract; + class TrainingSample; + struct UnicharRating; + + // Cube implementation of a ShapeClassifier. + class TESS_API CubeClassifier : public ShapeClassifier { + public: + explicit CubeClassifier(Tesseract* tesseract); + virtual ~CubeClassifier(); + + // Classifies the given [training] sample, writing to results. + // See ShapeClassifier for a full description. + virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, + int debug, UNICHAR_ID keep_this, + GenericVector* results); + // Provides access to the ShapeTable that this classifier works with. + virtual const ShapeTable* GetShapeTable() const; + + private: + // Cube objects. + CubeRecoContext* cube_cntxt_; + const ShapeTable& shape_table_; + }; + + // Combination of Tesseract class pruner with scoring by cube. + class TESS_API CubeTessClassifier : public ShapeClassifier { + public: + explicit CubeTessClassifier(Tesseract* tesseract); + virtual ~CubeTessClassifier(); + + // Classifies the given [training] sample, writing to results. + // See ShapeClassifier for a full description. + virtual int UnicharClassifySample(const TrainingSample& sample, Pix* page_pix, + int debug, UNICHAR_ID keep_this, + GenericVector* results); + // Provides access to the ShapeTable that this classifier works with. + virtual const ShapeTable* GetShapeTable() const; + + private: + // Cube objects. + CubeRecoContext* cube_cntxt_; + const ShapeTable& shape_table_; + TessClassifier* pruner_; + }; + +} // namespace tesseract + +#endif /* THIRD_PARTY_TESSERACT_CCMAIN_CUBECLASSIFIER_H_ */ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.cpp new file mode 100644 index 0000000..e6244b9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.cpp @@ -0,0 +1,1013 @@ +/****************************************************************** + * File: docqual.cpp (Formerly docqual.c) + * Description: Document Quality Metrics + * Author: Phil Cheatle + * Created: Mon May 9 11:27:28 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + +#include +#include "docqual.h" +#include "reject.h" +#include "tesscallback.h" +#include "tessvars.h" +#include "globals.h" +#include "tesseractclass.h" + +namespace tesseract { + + // A little class to provide the callbacks as we have no pre-bound args. + struct DocQualCallbacks { + explicit DocQualCallbacks(WERD_RES* word0) + : word(word0), match_count(0), accepted_match_count(0) {} + + void CountMatchingBlobs(int index) { + ++match_count; + } + + void CountAcceptedBlobs(int index) { + if (word->reject_map[index].accepted()) + ++accepted_match_count; + ++match_count; + } + + void AcceptIfGoodQuality(int index) { + if (word->reject_map[index].accept_if_good_quality()) + word->reject_map[index].setrej_quality_accept(); + } + + WERD_RES* word; + inT16 match_count; + inT16 accepted_match_count; + }; + + /************************************************************************* + * word_blob_quality() + * How many blobs in the box_word are identical to those of the inword? + * ASSUME blobs in both initial word and box_word are in ascending order of + * left hand blob edge. + *************************************************************************/ + inT16 Tesseract::word_blob_quality(WERD_RES *word, ROW *row) { + if (word->bln_boxes == NULL || + word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) + return 0; + + DocQualCallbacks cb(word); + word->bln_boxes->ProcessMatchedBlobs( + *word->rebuild_word, + NewPermanentTessCallback(&cb, &DocQualCallbacks::CountMatchingBlobs)); + return cb.match_count; + } + + inT16 Tesseract::word_outline_errs(WERD_RES *word) { + inT16 i = 0; + inT16 err_count = 0; + + if (word->rebuild_word != NULL) { + for (int b = 0; b < word->rebuild_word->NumBlobs(); ++b) { + TBLOB* blob = word->rebuild_word->blobs[b]; + err_count += count_outline_errs(word->best_choice->unichar_string()[i], + blob->NumOutlines()); + i++; + } + } + return err_count; + } + + /************************************************************************* + * word_char_quality() + * Combination of blob quality and outline quality - how many good chars are + * there? - I.e chars which pass the blob AND outline tests. + *************************************************************************/ + void Tesseract::word_char_quality(WERD_RES *word, + ROW *row, + inT16 *match_count, + inT16 *accepted_match_count) { + if (word->bln_boxes == NULL || word->rebuild_word == NULL || + word->rebuild_word->blobs.empty()) { + *match_count = 0; + *accepted_match_count = 0; + return; + } + + DocQualCallbacks cb(word); + word->bln_boxes->ProcessMatchedBlobs( + *word->rebuild_word, + NewPermanentTessCallback(&cb, &DocQualCallbacks::CountAcceptedBlobs)); + *match_count = cb.match_count; + *accepted_match_count = cb.accepted_match_count; + } + + /************************************************************************* + * unrej_good_chs() + * Unreject POTENTIAL rejects if the blob passes the blob and outline checks + *************************************************************************/ + void Tesseract::unrej_good_chs(WERD_RES *word, ROW *row) { + if (word->bln_boxes == NULL || + word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) + return; + + DocQualCallbacks cb(word); + word->bln_boxes->ProcessMatchedBlobs( + *word->rebuild_word, + NewPermanentTessCallback(&cb, &DocQualCallbacks::AcceptIfGoodQuality)); + } + + inT16 Tesseract::count_outline_errs(char c, inT16 outline_count) { + int expected_outline_count; + + if (STRING(outlines_odd).contains(c)) + return 0; // Don't use this char + else if (STRING(outlines_2).contains(c)) + expected_outline_count = 2; + else + expected_outline_count = 1; + return abs(outline_count - expected_outline_count); + } + + void Tesseract::quality_based_rejection(PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc) { + if ((tessedit_good_quality_unrej && good_quality_doc)) + unrej_good_quality_words(page_res_it); + doc_and_block_rejection(page_res_it, good_quality_doc); + if (unlv_tilde_crunching) { + tilde_crunch(page_res_it); + tilde_delete(page_res_it); + } + } + + /************************************************************************* + * unrej_good_quality_words() + * Accept potential rejects in words which pass the following checks: + * - Contains a potential reject + * - Word looks like a sensible alpha word. + * - Word segmentation is the same as the original image + * - All characters have the expected number of outlines + * NOTE - the rejection counts are recalculated after unrejection + * - CAN'T do it in a single pass without a bit of fiddling + * - keep it simple but inefficient + *************************************************************************/ + void Tesseract::unrej_good_quality_words( //unreject potential + PAGE_RES_IT &page_res_it) { + WERD_RES *word; + ROW_RES *current_row; + BLOCK_RES *current_block; + int i; + + page_res_it.restart_page(); + while (page_res_it.word() != NULL) { + check_debug_pt(page_res_it.word(), 100); + if (bland_unrej) { + word = page_res_it.word(); + for (i = 0; i < word->reject_map.length(); i++) { + if (word->reject_map[i].accept_if_good_quality()) + word->reject_map[i].setrej_quality_accept(); + } + page_res_it.forward(); + } + else if ((page_res_it.row()->char_count > 0) && + ((page_res_it.row()->rej_count / + (float)page_res_it.row()->char_count) <= + quality_rowrej_pc)) { + word = page_res_it.word(); + if (word->reject_map.quality_recoverable_rejects() && + (tessedit_unrej_any_wd || + acceptable_word_string(*word->uch_set, + word->best_choice->unichar_string().string(), + word->best_choice->unichar_lengths().string()) + != AC_UNACCEPTABLE)) { + unrej_good_chs(word, page_res_it.row()->row); + } + page_res_it.forward(); + } + else { + /* Skip to end of dodgy row */ + current_row = page_res_it.row(); + while ((page_res_it.word() != NULL) && + (page_res_it.row() == current_row)) + page_res_it.forward(); + } + check_debug_pt(page_res_it.word(), 110); + } + page_res_it.restart_page(); + page_res_it.page_res->char_count = 0; + page_res_it.page_res->rej_count = 0; + current_block = NULL; + current_row = NULL; + while (page_res_it.word() != NULL) { + if (current_block != page_res_it.block()) { + current_block = page_res_it.block(); + current_block->char_count = 0; + current_block->rej_count = 0; + } + if (current_row != page_res_it.row()) { + current_row = page_res_it.row(); + current_row->char_count = 0; + current_row->rej_count = 0; + current_row->whole_word_rej_count = 0; + } + page_res_it.rej_stat_word(); + page_res_it.forward(); + } + } + + + /************************************************************************* + * doc_and_block_rejection() + * + * If the page has too many rejects - reject all of it. + * If any block has too many rejects - reject all words in the block + *************************************************************************/ + + void Tesseract::doc_and_block_rejection( //reject big chunks + PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc) { + inT16 block_no = 0; + inT16 row_no = 0; + BLOCK_RES *current_block; + ROW_RES *current_row; + + BOOL8 rej_word; + BOOL8 prev_word_rejected; + inT16 char_quality = 0; + inT16 accepted_char_quality; + + if (page_res_it.page_res->rej_count * 100.0 / + page_res_it.page_res->char_count > tessedit_reject_doc_percent) { + reject_whole_page(page_res_it); + if (tessedit_debug_doc_rejection) { + tprintf("REJECT ALL #chars: %d #Rejects: %d; \n", + page_res_it.page_res->char_count, + page_res_it.page_res->rej_count); + } + } + else { + if (tessedit_debug_doc_rejection) { + tprintf("NO PAGE REJECTION #chars: %d # Rejects: %d; \n", + page_res_it.page_res->char_count, + page_res_it.page_res->rej_count); + } + + /* Walk blocks testing for block rejection */ + + page_res_it.restart_page(); + WERD_RES* word; + while ((word = page_res_it.word()) != NULL) { + current_block = page_res_it.block(); + block_no = current_block->block->index(); + if (current_block->char_count > 0 && + (current_block->rej_count * 100.0 / current_block->char_count) > + tessedit_reject_block_percent) { + if (tessedit_debug_block_rejection) { + tprintf("REJECTING BLOCK %d #chars: %d; #Rejects: %d\n", + block_no, current_block->char_count, + current_block->rej_count); + } + prev_word_rejected = FALSE; + while ((word = page_res_it.word()) != NULL && + (page_res_it.block() == current_block)) { + if (tessedit_preserve_blk_rej_perfect_wds) { + rej_word = word->reject_map.reject_count() > 0 || + word->reject_map.length() < tessedit_preserve_min_wd_len; + if (rej_word && tessedit_dont_blkrej_good_wds && + word->reject_map.length() >= tessedit_preserve_min_wd_len && + acceptable_word_string( + *word->uch_set, + word->best_choice->unichar_string().string(), + word->best_choice->unichar_lengths().string()) != + AC_UNACCEPTABLE) { + word_char_quality(word, page_res_it.row()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != word->reject_map.length(); + } + } + else { + rej_word = TRUE; + } + if (rej_word) { + /* + Reject spacing if both current and prev words are rejected. + NOTE - this is NOT restricted to FUZZY spaces. - When tried this + generated more space errors. + */ + if (tessedit_use_reject_spaces && + prev_word_rejected && + page_res_it.prev_row() == page_res_it.row() && + word->word->space() == 1) + word->reject_spaces = TRUE; + word->reject_map.rej_word_block_rej(); + } + prev_word_rejected = rej_word; + page_res_it.forward(); + } + } + else { + if (tessedit_debug_block_rejection) { + tprintf("NOT REJECTING BLOCK %d #chars: %d # Rejects: %d; \n", + block_no, page_res_it.block()->char_count, + page_res_it.block()->rej_count); + } + + /* Walk rows in block testing for row rejection */ + row_no = 0; + while (page_res_it.word() != NULL && + page_res_it.block() == current_block) { + current_row = page_res_it.row(); + row_no++; + /* Reject whole row if: + fraction of chars on row which are rejected exceed a limit AND + fraction rejects which occur in WHOLE WERD rejects is LESS THAN a + limit + */ + if (current_row->char_count > 0 && + (current_row->rej_count * 100.0 / current_row->char_count) > + tessedit_reject_row_percent && + (current_row->whole_word_rej_count * 100.0 / + current_row->rej_count) < + tessedit_whole_wd_rej_row_percent) { + if (tessedit_debug_block_rejection) { + tprintf("REJECTING ROW %d #chars: %d; #Rejects: %d\n", + row_no, current_row->char_count, + current_row->rej_count); + } + prev_word_rejected = FALSE; + while ((word = page_res_it.word()) != NULL && + page_res_it.row() == current_row) { + /* Preserve words on good docs unless they are mostly rejected*/ + if (!tessedit_row_rej_good_docs && good_quality_doc) { + rej_word = word->reject_map.reject_count() / + static_cast(word->reject_map.length()) > + tessedit_good_doc_still_rowrej_wd; + } + else if (tessedit_preserve_row_rej_perfect_wds) { + /* Preserve perfect words anyway */ + rej_word = word->reject_map.reject_count() > 0 || + word->reject_map.length() < tessedit_preserve_min_wd_len; + if (rej_word && tessedit_dont_rowrej_good_wds && + word->reject_map.length() >= tessedit_preserve_min_wd_len && + acceptable_word_string(*word->uch_set, + word->best_choice->unichar_string().string(), + word->best_choice->unichar_lengths().string()) != + AC_UNACCEPTABLE) { + word_char_quality(word, page_res_it.row()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != word->reject_map.length(); + } + } + else { + rej_word = TRUE; + } + if (rej_word) { + /* + Reject spacing if both current and prev words are rejected. + NOTE - this is NOT restricted to FUZZY spaces. - When tried + this generated more space errors. + */ + if (tessedit_use_reject_spaces && + prev_word_rejected && + page_res_it.prev_row() == page_res_it.row() && + word->word->space() == 1) + word->reject_spaces = TRUE; + word->reject_map.rej_word_row_rej(); + } + prev_word_rejected = rej_word; + page_res_it.forward(); + } + } + else { + if (tessedit_debug_block_rejection) { + tprintf("NOT REJECTING ROW %d #chars: %d # Rejects: %d; \n", + row_no, current_row->char_count, current_row->rej_count); + } + while (page_res_it.word() != NULL && + page_res_it.row() == current_row) + page_res_it.forward(); + } + } + } + } + } + } + +} // namespace tesseract + +/************************************************************************* + * reject_whole_page() + * Don't believe any of it - set the reject map to 00..00 in all words + * + *************************************************************************/ + +void reject_whole_page(PAGE_RES_IT &page_res_it) { + page_res_it.restart_page(); + while (page_res_it.word() != NULL) { + page_res_it.word()->reject_map.rej_word_doc_rej(); + page_res_it.forward(); + } + //whole page is rejected + page_res_it.page_res->rejected = TRUE; +} + +namespace tesseract { + void Tesseract::tilde_crunch(PAGE_RES_IT &page_res_it) { + WERD_RES *word; + GARBAGE_LEVEL garbage_level; + PAGE_RES_IT copy_it; + BOOL8 prev_potential_marked = FALSE; + BOOL8 found_terrible_word = FALSE; + BOOL8 ok_dict_word; + + page_res_it.restart_page(); + while (page_res_it.word() != NULL) { + POLY_BLOCK* pb = page_res_it.block()->block->poly_block(); + if (pb != NULL && !pb->IsText()) { + page_res_it.forward(); + continue; + } + word = page_res_it.word(); + + if (crunch_early_convert_bad_unlv_chs) + convert_bad_unlv_chs(word); + + if (crunch_early_merge_tess_fails) + word->merge_tess_fails(); + + if (word->reject_map.accept_count() != 0) { + found_terrible_word = FALSE; + //Forget earlier potential crunches + prev_potential_marked = FALSE; + } + else { + ok_dict_word = safe_dict_word(word); + garbage_level = garbage_word(word, ok_dict_word); + + if ((garbage_level != G_NEVER_CRUNCH) && + (terrible_word_crunch(word, garbage_level))) { + if (crunch_debug > 0) { + tprintf("T CRUNCHING: \"%s\"\n", + word->best_choice->unichar_string().string()); + } + word->unlv_crunch_mode = CR_KEEP_SPACE; + if (prev_potential_marked) { + while (copy_it.word() != word) { + if (crunch_debug > 0) { + tprintf("P1 CRUNCHING: \"%s\"\n", + copy_it.word()->best_choice->unichar_string().string()); + } + copy_it.word()->unlv_crunch_mode = CR_KEEP_SPACE; + copy_it.forward(); + } + prev_potential_marked = FALSE; + } + found_terrible_word = TRUE; + } + else if ((garbage_level != G_NEVER_CRUNCH) && + (potential_word_crunch(word, + garbage_level, ok_dict_word))) { + if (found_terrible_word) { + if (crunch_debug > 0) { + tprintf("P2 CRUNCHING: \"%s\"\n", + word->best_choice->unichar_string().string()); + } + word->unlv_crunch_mode = CR_KEEP_SPACE; + } + else if (!prev_potential_marked) { + copy_it = page_res_it; + prev_potential_marked = TRUE; + if (crunch_debug > 1) { + tprintf("P3 CRUNCHING: \"%s\"\n", + word->best_choice->unichar_string().string()); + } + } + } + else { + found_terrible_word = FALSE; + //Forget earlier potential crunches + prev_potential_marked = FALSE; + if (crunch_debug > 2) { + tprintf("NO CRUNCH: \"%s\"\n", + word->best_choice->unichar_string().string()); + } + } + } + page_res_it.forward(); + } + } + + + BOOL8 Tesseract::terrible_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level) { + float rating_per_ch; + int adjusted_len; + int crunch_mode = 0; + + if ((word->best_choice->unichar_string().length() == 0) || + (strspn(word->best_choice->unichar_string().string(), " ") == + word->best_choice->unichar_string().length())) + crunch_mode = 1; + else { + adjusted_len = word->reject_map.length(); + if (adjusted_len > crunch_rating_max) + adjusted_len = crunch_rating_max; + rating_per_ch = word->best_choice->rating() / adjusted_len; + + if (rating_per_ch > crunch_terrible_rating) + crunch_mode = 2; + else if (crunch_terrible_garbage && (garbage_level == G_TERRIBLE)) + crunch_mode = 3; + else if ((word->best_choice->certainty() < crunch_poor_garbage_cert) && + (garbage_level != G_OK)) + crunch_mode = 4; + else if ((rating_per_ch > crunch_poor_garbage_rate) && + (garbage_level != G_OK)) + crunch_mode = 5; + } + if (crunch_mode > 0) { + if (crunch_debug > 2) { + tprintf("Terrible_word_crunch (%d) on \"%s\"\n", + crunch_mode, word->best_choice->unichar_string().string()); + } + return TRUE; + } + else + return FALSE; + } + + BOOL8 Tesseract::potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word) { + float rating_per_ch; + int adjusted_len; + const char *str = word->best_choice->unichar_string().string(); + const char *lengths = word->best_choice->unichar_lengths().string(); + BOOL8 word_crunchable; + int poor_indicator_count = 0; + + word_crunchable = !crunch_leave_accept_strings || + word->reject_map.length() < 3 || + (acceptable_word_string(*word->uch_set, + str, lengths) == AC_UNACCEPTABLE && + !ok_dict_word); + + adjusted_len = word->reject_map.length(); + if (adjusted_len > 10) + adjusted_len = 10; + rating_per_ch = word->best_choice->rating() / adjusted_len; + + if (rating_per_ch > crunch_pot_poor_rate) { + if (crunch_debug > 2) { + tprintf("Potential poor rating on \"%s\"\n", + word->best_choice->unichar_string().string()); + } + poor_indicator_count++; + } + + if (word_crunchable && + word->best_choice->certainty() < crunch_pot_poor_cert) { + if (crunch_debug > 2) { + tprintf("Potential poor cert on \"%s\"\n", + word->best_choice->unichar_string().string()); + } + poor_indicator_count++; + } + + if (garbage_level != G_OK) { + if (crunch_debug > 2) { + tprintf("Potential garbage on \"%s\"\n", + word->best_choice->unichar_string().string()); + } + poor_indicator_count++; + } + return poor_indicator_count >= crunch_pot_indicators; + } + + void Tesseract::tilde_delete(PAGE_RES_IT &page_res_it) { + WERD_RES *word; + PAGE_RES_IT copy_it; + BOOL8 deleting_from_bol = FALSE; + BOOL8 marked_delete_point = FALSE; + inT16 debug_delete_mode; + CRUNCH_MODE delete_mode; + inT16 x_debug_delete_mode; + CRUNCH_MODE x_delete_mode; + + page_res_it.restart_page(); + while (page_res_it.word() != NULL) { + word = page_res_it.word(); + + delete_mode = word_deletable(word, debug_delete_mode); + if (delete_mode != CR_NONE) { + if (word->word->flag(W_BOL) || deleting_from_bol) { + if (crunch_debug > 0) { + tprintf("BOL CRUNCH DELETING(%d): \"%s\"\n", + debug_delete_mode, + word->best_choice->unichar_string().string()); + } + word->unlv_crunch_mode = delete_mode; + deleting_from_bol = TRUE; + } + else if (word->word->flag(W_EOL)) { + if (marked_delete_point) { + while (copy_it.word() != word) { + x_delete_mode = word_deletable(copy_it.word(), + x_debug_delete_mode); + if (crunch_debug > 0) { + tprintf("EOL CRUNCH DELETING(%d): \"%s\"\n", + x_debug_delete_mode, + copy_it.word()->best_choice->unichar_string().string()); + } + copy_it.word()->unlv_crunch_mode = x_delete_mode; + copy_it.forward(); + } + } + if (crunch_debug > 0) { + tprintf("EOL CRUNCH DELETING(%d): \"%s\"\n", + debug_delete_mode, + word->best_choice->unichar_string().string()); + } + word->unlv_crunch_mode = delete_mode; + deleting_from_bol = FALSE; + marked_delete_point = FALSE; + } + else { + if (!marked_delete_point) { + copy_it = page_res_it; + marked_delete_point = TRUE; + } + } + } + else { + deleting_from_bol = FALSE; + //Forget earlier potential crunches + marked_delete_point = FALSE; + } + /* + The following step has been left till now as the tess fails are used to + determine if the word is deletable. + */ + if (!crunch_early_merge_tess_fails) + word->merge_tess_fails(); + page_res_it.forward(); + } + } + + + void Tesseract::convert_bad_unlv_chs(WERD_RES *word_res) { + int i; + UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-"); + UNICHAR_ID unichar_space = word_res->uch_set->unichar_to_id(" "); + UNICHAR_ID unichar_tilde = word_res->uch_set->unichar_to_id("~"); + UNICHAR_ID unichar_pow = word_res->uch_set->unichar_to_id("^"); + for (i = 0; i < word_res->reject_map.length(); ++i) { + if (word_res->best_choice->unichar_id(i) == unichar_tilde) { + word_res->best_choice->set_unichar_id(unichar_dash, i); + if (word_res->reject_map[i].accepted()) + word_res->reject_map[i].setrej_unlv_rej(); + } + if (word_res->best_choice->unichar_id(i) == unichar_pow) { + word_res->best_choice->set_unichar_id(unichar_space, i); + if (word_res->reject_map[i].accepted()) + word_res->reject_map[i].setrej_unlv_rej(); + } + } + } + + GARBAGE_LEVEL Tesseract::garbage_word(WERD_RES *word, BOOL8 ok_dict_word) { + enum STATES + { + JUNK, + FIRST_UPPER, + FIRST_LOWER, + FIRST_NUM, + SUBSEQUENT_UPPER, + SUBSEQUENT_LOWER, + SUBSEQUENT_NUM + }; + const char *str = word->best_choice->unichar_string().string(); + const char *lengths = word->best_choice->unichar_lengths().string(); + STATES state = JUNK; + int len = 0; + int isolated_digits = 0; + int isolated_alphas = 0; + int bad_char_count = 0; + int tess_rejs = 0; + int dodgy_chars = 0; + int ok_chars; + UNICHAR_ID last_char = -1; + int alpha_repetition_count = 0; + int longest_alpha_repetition_count = 0; + int longest_lower_run_len = 0; + int lower_string_count = 0; + int longest_upper_run_len = 0; + int upper_string_count = 0; + int total_alpha_count = 0; + int total_digit_count = 0; + + for (; *str != '\0'; str += *(lengths++)) { + len++; + if (word->uch_set->get_isupper(str, *lengths)) { + total_alpha_count++; + switch (state) { + case SUBSEQUENT_UPPER: + case FIRST_UPPER: + state = SUBSEQUENT_UPPER; + upper_string_count++; + if (longest_upper_run_len < upper_string_count) + longest_upper_run_len = upper_string_count; + if (last_char == word->uch_set->unichar_to_id(str, *lengths)) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = word->uch_set->unichar_to_id(str, *lengths); + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_UPPER; + last_char = word->uch_set->unichar_to_id(str, *lengths); + alpha_repetition_count = 1; + upper_string_count = 1; + break; + } + } + else if (word->uch_set->get_islower(str, *lengths)) { + total_alpha_count++; + switch (state) { + case SUBSEQUENT_LOWER: + case FIRST_LOWER: + state = SUBSEQUENT_LOWER; + lower_string_count++; + if (longest_lower_run_len < lower_string_count) + longest_lower_run_len = lower_string_count; + if (last_char == word->uch_set->unichar_to_id(str, *lengths)) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = word->uch_set->unichar_to_id(str, *lengths); + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_LOWER; + last_char = word->uch_set->unichar_to_id(str, *lengths); + alpha_repetition_count = 1; + lower_string_count = 1; + break; + } + } + else if (word->uch_set->get_isdigit(str, *lengths)) { + total_digit_count++; + switch (state) { + case FIRST_NUM: + state = SUBSEQUENT_NUM; + case SUBSEQUENT_NUM: + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + state = FIRST_NUM; + break; + } + } + else { + if (*lengths == 1 && *str == ' ') + tess_rejs++; + else + bad_char_count++; + switch (state) { + case FIRST_NUM: + isolated_digits++; + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + break; + } + state = JUNK; + } + } + + switch (state) { + case FIRST_NUM: + isolated_digits++; + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + break; + } + + if (crunch_include_numerals) { + total_alpha_count += total_digit_count - isolated_digits; + } + + if (crunch_leave_ok_strings && len >= 4 && + 2 * (total_alpha_count - isolated_alphas) > len && + longest_alpha_repetition_count < crunch_long_repetitions) { + if ((crunch_accept_ok && + acceptable_word_string(*word->uch_set, str, lengths) != + AC_UNACCEPTABLE) || + longest_lower_run_len > crunch_leave_lc_strings || + longest_upper_run_len > crunch_leave_uc_strings) + return G_NEVER_CRUNCH; + } + if (word->reject_map.length() > 1 && + strpbrk(str, " ") == NULL && + (word->best_choice->permuter() == SYSTEM_DAWG_PERM || + word->best_choice->permuter() == FREQ_DAWG_PERM || + word->best_choice->permuter() == USER_DAWG_PERM || + word->best_choice->permuter() == NUMBER_PERM || + acceptable_word_string(*word->uch_set, str, lengths) != + AC_UNACCEPTABLE || ok_dict_word)) + return G_OK; + + ok_chars = len - bad_char_count - isolated_digits - + isolated_alphas - tess_rejs; + + if (crunch_debug > 3) { + tprintf("garbage_word: \"%s\"\n", + word->best_choice->unichar_string().string()); + tprintf("LEN: %d bad: %d iso_N: %d iso_A: %d rej: %d\n", + len, + bad_char_count, isolated_digits, isolated_alphas, tess_rejs); + } + if (bad_char_count == 0 && + tess_rejs == 0 && + (len > isolated_digits + isolated_alphas || len <= 2)) + return G_OK; + + if (tess_rejs > ok_chars || + (tess_rejs > 0 && (bad_char_count + tess_rejs) * 2 > len)) + return G_TERRIBLE; + + if (len > 4) { + dodgy_chars = 2 * tess_rejs + bad_char_count + isolated_digits + + isolated_alphas; + if (dodgy_chars > 5 || (dodgy_chars / (float)len) > 0.5) + return G_DODGY; + else + return G_OK; + } + else { + dodgy_chars = 2 * tess_rejs + bad_char_count; + if ((len == 4 && dodgy_chars > 2) || + (len == 3 && dodgy_chars > 2) || dodgy_chars >= len) + return G_DODGY; + else + return G_OK; + } + } + + + /************************************************************************* + * word_deletable() + * DELETE WERDS AT ENDS OF ROWS IF + * Word is crunched && + * ( string length = 0 OR + * > 50% of chars are "|" (before merging) OR + * certainty < -10 OR + * rating /char > 60 OR + * TOP of word is more than 0.5 xht BELOW baseline OR + * BOTTOM of word is more than 0.5 xht ABOVE xht OR + * length of word < 3xht OR + * height of word < 0.7 xht OR + * height of word > 3.0 xht OR + * >75% of the outline BBs have longest dimension < 0.5xht + *************************************************************************/ + + CRUNCH_MODE Tesseract::word_deletable(WERD_RES *word, inT16 &delete_mode) { + int word_len = word->reject_map.length(); + float rating_per_ch; + TBOX box; //BB of word + + if (word->unlv_crunch_mode == CR_NONE) { + delete_mode = 0; + return CR_NONE; + } + + if (word_len == 0) { + delete_mode = 1; + return CR_DELETE; + } + + if (word->rebuild_word != NULL) { + // Cube leaves rebuild_word NULL. + box = word->rebuild_word->bounding_box(); + if (box.height() < crunch_del_min_ht * kBlnXHeight) { + delete_mode = 4; + return CR_DELETE; + } + + if (noise_outlines(word->rebuild_word)) { + delete_mode = 5; + return CR_DELETE; + } + } + + if ((failure_count(word) * 1.5) > word_len) { + delete_mode = 2; + return CR_LOOSE_SPACE; + } + + if (word->best_choice->certainty() < crunch_del_cert) { + delete_mode = 7; + return CR_LOOSE_SPACE; + } + + rating_per_ch = word->best_choice->rating() / word_len; + + if (rating_per_ch > crunch_del_rating) { + delete_mode = 8; + return CR_LOOSE_SPACE; + } + + if (box.top() < kBlnBaselineOffset - crunch_del_low_word * kBlnXHeight) { + delete_mode = 9; + return CR_LOOSE_SPACE; + } + + if (box.bottom() > + kBlnBaselineOffset + crunch_del_high_word * kBlnXHeight) { + delete_mode = 10; + return CR_LOOSE_SPACE; + } + + if (box.height() > crunch_del_max_ht * kBlnXHeight) { + delete_mode = 11; + return CR_LOOSE_SPACE; + } + + if (box.width() < crunch_del_min_width * kBlnXHeight) { + delete_mode = 3; + return CR_LOOSE_SPACE; + } + + delete_mode = 0; + return CR_NONE; + } + + inT16 Tesseract::failure_count(WERD_RES *word) { + const char *str = word->best_choice->unichar_string().string(); + int tess_rejs = 0; + + for (; *str != '\0'; str++) { + if (*str == ' ') + tess_rejs++; + } + return tess_rejs; + } + + + BOOL8 Tesseract::noise_outlines(TWERD *word) { + TBOX box; // BB of outline + inT16 outline_count = 0; + inT16 small_outline_count = 0; + inT16 max_dimension; + float small_limit = kBlnXHeight * crunch_small_outlines_size; + + for (int b = 0; b < word->NumBlobs(); ++b) { + TBLOB* blob = word->blobs[b]; + for (TESSLINE* ol = blob->outlines; ol != NULL; ol = ol->next) { + outline_count++; + box = ol->bounding_box(); + if (box.height() > box.width()) + max_dimension = box.height(); + else + max_dimension = box.width(); + if (max_dimension < small_limit) + small_outline_count++; + } + } + return small_outline_count >= outline_count; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.h b/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.h new file mode 100644 index 0000000..f2df9b2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/docqual.h @@ -0,0 +1,35 @@ +/****************************************************************** + * File: docqual.h (Formerly docqual.h) + * Description: Document Quality Metrics + * Author: Phil Cheatle + * Created: Mon May 9 11:27:28 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DOCQUAL_H +#define DOCQUAL_H + +#include "control.h" + +enum GARBAGE_LEVEL +{ + G_NEVER_CRUNCH, + G_OK, + G_DODGY, + G_TERRIBLE +}; + +inT16 word_blob_quality(WERD_RES *word, ROW *row); +void reject_whole_page(PAGE_RES_IT &page_res_it); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.cpp new file mode 100644 index 0000000..c165bb3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.cpp @@ -0,0 +1,1550 @@ +/////////////////////////////////////////////////////////////////////// +// File: equationdetect.cpp +// Description: Helper classes to detect equations. +// Author: Zongyi (Joe) Liu (joeliu@google.com) +// Created: Fri Aug 31 11:13: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. +// +/////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#include +#include +#endif + +#ifdef __MINGW32__ +#include +#endif + +#include + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "equationdetect.h" + +#include "bbgrid.h" +#include "classify.h" +#include "colpartition.h" +#include "colpartitiongrid.h" +#include "colpartitionset.h" +#include "helpers.h" +#include "ratngs.h" +#include "tesseractclass.h" + +// Config variables. +BOOL_VAR(equationdetect_save_bi_image, false, "Save input bi image"); +BOOL_VAR(equationdetect_save_spt_image, false, "Save special character image"); +BOOL_VAR(equationdetect_save_seed_image, false, "Save the seed image"); +BOOL_VAR(equationdetect_save_merged_image, false, "Save the merged image"); + +namespace tesseract { + + /////////////////////////////////////////////////////////////////////////// + // Utility ColParition sort functions. + /////////////////////////////////////////////////////////////////////////// + static int SortCPByTopReverse(const void* p1, const void* p2) { + const ColPartition* cp1 = *reinterpret_cast(p1); + const ColPartition* cp2 = *reinterpret_cast(p2); + ASSERT_HOST(cp1 != NULL && cp2 != NULL); + const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); + return box2.top() - box1.top(); + } + + static int SortCPByBottom(const void* p1, const void* p2) { + const ColPartition* cp1 = *reinterpret_cast(p1); + const ColPartition* cp2 = *reinterpret_cast(p2); + ASSERT_HOST(cp1 != NULL && cp2 != NULL); + const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); + return box1.bottom() - box2.bottom(); + } + + static int SortCPByHeight(const void* p1, const void* p2) { + const ColPartition* cp1 = *reinterpret_cast(p1); + const ColPartition* cp2 = *reinterpret_cast(p2); + ASSERT_HOST(cp1 != NULL && cp2 != NULL); + const TBOX &box1(cp1->bounding_box()), &box2(cp2->bounding_box()); + return box1.height() - box2.height(); + } + + // TODO(joeliu): we may want to parameterize these constants. + const float kMathDigitDensityTh1 = 0.25; + const float kMathDigitDensityTh2 = 0.1; + const float kMathItalicDensityTh = 0.5; + const float kUnclearDensityTh = 0.25; + const int kSeedBlobsCountTh = 10; + const int kLeftIndentAlignmentCountTh = 1; + + // Returns true if PolyBlockType is of text type or equation type. + inline bool IsTextOrEquationType(PolyBlockType type) { + return PTIsTextType(type) || type == PT_EQUATION; + } + + inline bool IsLeftIndented(const EquationDetect::IndentType type) { + return type == EquationDetect::LEFT_INDENT || + type == EquationDetect::BOTH_INDENT; + } + + inline bool IsRightIndented(const EquationDetect::IndentType type) { + return type == EquationDetect::RIGHT_INDENT || + type == EquationDetect::BOTH_INDENT; + } + + EquationDetect::EquationDetect(const char* equ_datapath, + const char* equ_name) { + const char* default_name = "equ"; + if (equ_name == NULL) { + equ_name = default_name; + } + lang_tesseract_ = NULL; + resolution_ = 0; + page_count_ = 0; + + if (equ_tesseract_.init_tesseract(equ_datapath, equ_name, + OEM_TESSERACT_ONLY)) { + tprintf("Warning: equation region detection requested," + " but %s failed to load from %s\n", equ_name, equ_datapath); + } + + cps_super_bbox_ = NULL; + } + + EquationDetect::~EquationDetect() { + delete(cps_super_bbox_); + } + + void EquationDetect::SetLangTesseract(Tesseract* lang_tesseract) { + lang_tesseract_ = lang_tesseract; + } + + void EquationDetect::SetResolution(const int resolution) { + resolution_ = resolution; + } + + int EquationDetect::LabelSpecialText(TO_BLOCK* to_block) { + if (to_block == NULL) { + tprintf("Warning: input to_block is NULL!\n"); + return -1; + } + + GenericVector blob_lists; + blob_lists.push_back(&(to_block->blobs)); + blob_lists.push_back(&(to_block->large_blobs)); + for (int i = 0; i < blob_lists.size(); ++i) { + BLOBNBOX_IT bbox_it(blob_lists[i]); + for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); + bbox_it.forward()) { + bbox_it.data()->set_special_text_type(BSTT_NONE); + } + } + + return 0; + } + + void EquationDetect::IdentifySpecialText( + BLOBNBOX *blobnbox, const int height_th) { + ASSERT_HOST(blobnbox != NULL); + if (blobnbox->bounding_box().height() < height_th && height_th > 0) { + // For small blob, we simply set to BSTT_NONE. + blobnbox->set_special_text_type(BSTT_NONE); + return; + } + + BLOB_CHOICE_LIST ratings_equ, ratings_lang; + C_BLOB* blob = blobnbox->cblob(); + // TODO(joeliu/rays) Fix this. We may have to normalize separately for + // each classifier here, as they may require different PolygonalCopy. + TBLOB* tblob = TBLOB::PolygonalCopy(false, blob); + const TBOX& box = tblob->bounding_box(); + + // Normalize the blob. Set the origin to the place we want to be the + // bottom-middle, and scaling is to make the height the x-height. + float scaling = static_cast(kBlnXHeight) / box.height(); + float x_orig = (box.left() + box.right()) / 2.0f, y_orig = box.bottom(); + TBLOB* normed_blob = new TBLOB(*tblob); + normed_blob->Normalize(NULL, NULL, NULL, x_orig, y_orig, scaling, scaling, + 0.0f, static_cast(kBlnBaselineOffset), + false, NULL); + equ_tesseract_.AdaptiveClassifier(normed_blob, &ratings_equ); + lang_tesseract_->AdaptiveClassifier(normed_blob, &ratings_lang); + delete normed_blob; + delete tblob; + + // Get the best choice from ratings_lang and rating_equ. As the choice in the + // list has already been sorted by the certainty, we simply use the first + // choice. + BLOB_CHOICE *lang_choice = NULL, *equ_choice = NULL; + if (ratings_lang.length() > 0) { + BLOB_CHOICE_IT choice_it(&ratings_lang); + lang_choice = choice_it.data(); + } + if (ratings_equ.length() > 0) { + BLOB_CHOICE_IT choice_it(&ratings_equ); + equ_choice = choice_it.data(); + } + + float lang_score = lang_choice ? lang_choice->certainty() : -FLT_MAX; + float equ_score = equ_choice ? equ_choice->certainty() : -FLT_MAX; + + const float kConfScoreTh = -5.0f, kConfDiffTh = 1.8; + // The scores here are negative, so the max/min == fabs(min/max). + // float ratio = fmax(lang_score, equ_score) / fmin(lang_score, equ_score); + float diff = fabs(lang_score - equ_score); + BlobSpecialTextType type = BSTT_NONE; + + // Classification. + if (fmax(lang_score, equ_score) < kConfScoreTh) { + // If both score are very small, then mark it as unclear. + type = BSTT_UNCLEAR; + } + else if (diff > kConfDiffTh && equ_score > lang_score) { + // If equ_score is significantly higher, then we classify this character as + // math symbol. + type = BSTT_MATH; + } + else if (lang_choice) { + // For other cases: lang_score is similar or significantly higher. + type = EstimateTypeForUnichar( + lang_tesseract_->unicharset, lang_choice->unichar_id()); + } + + if (type == BSTT_NONE && lang_tesseract_->get_fontinfo_table().get( + lang_choice->fontinfo_id()).is_italic()) { + // For text symbol, we still check if it is italic. + blobnbox->set_special_text_type(BSTT_ITALIC); + } + else { + blobnbox->set_special_text_type(type); + } + } + + BlobSpecialTextType EquationDetect::EstimateTypeForUnichar( + const UNICHARSET& unicharset, const UNICHAR_ID id) const { + STRING s = unicharset.id_to_unichar(id); + if (unicharset.get_isalpha(id)) { + return BSTT_NONE; + } + + if (unicharset.get_ispunctuation(id)) { + // Exclude some special texts that are likely to be confused as math symbol. + static GenericVector ids_to_exclude; + if (ids_to_exclude.empty()) { + static const STRING kCharsToEx[] = { "'", "`", "\"", "\\", ",", ".", + "〈", "〉", "《", "》", "」", "「", "" }; + int i = 0; + while (kCharsToEx[i] != "") { + ids_to_exclude.push_back( + unicharset.unichar_to_id(kCharsToEx[i++].string())); + } + ids_to_exclude.sort(); + } + return ids_to_exclude.bool_binary_search(id) ? BSTT_NONE : BSTT_MATH; + } + + // Check if it is digit. In addition to the isdigit attribute, we also check + // if this character belongs to those likely to be confused with a digit. + static const STRING kDigitsChars = "|"; + if (unicharset.get_isdigit(id) || + (s.length() == 1 && kDigitsChars.contains(s[0]))) { + return BSTT_DIGIT; + } + else { + return BSTT_MATH; + } + } + + void EquationDetect::IdentifySpecialText() { + // Set configuration for Tesseract::AdaptiveClassifier. + equ_tesseract_.tess_cn_matching.set_value(true); // turn it on + equ_tesseract_.tess_bn_matching.set_value(false); + + // Set the multiplier to zero for lang_tesseract_ to improve the accuracy. + int classify_class_pruner = lang_tesseract_->classify_class_pruner_multiplier; + int classify_integer_matcher = + lang_tesseract_->classify_integer_matcher_multiplier; + lang_tesseract_->classify_class_pruner_multiplier.set_value(0); + lang_tesseract_->classify_integer_matcher_multiplier.set_value(0); + + ColPartitionGridSearch gsearch(part_grid_); + ColPartition *part = NULL; + gsearch.StartFullSearch(); + while ((part = gsearch.NextFullSearch()) != NULL) { + if (!IsTextOrEquationType(part->type())) { + continue; + } + IdentifyBlobsToSkip(part); + BLOBNBOX_C_IT bbox_it(part->boxes()); + // Compute the height threshold. + GenericVector blob_heights; + for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); + bbox_it.forward()) { + if (bbox_it.data()->special_text_type() != BSTT_SKIP) { + blob_heights.push_back(bbox_it.data()->bounding_box().height()); + } + } + blob_heights.sort(); + int height_th = blob_heights[blob_heights.size() / 2] / 3 * 2; + for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); + bbox_it.forward()) { + if (bbox_it.data()->special_text_type() != BSTT_SKIP) { + IdentifySpecialText(bbox_it.data(), height_th); + } + } + } + + // Set the multiplier values back. + lang_tesseract_->classify_class_pruner_multiplier.set_value( + classify_class_pruner); + lang_tesseract_->classify_integer_matcher_multiplier.set_value( + classify_integer_matcher); + + if (equationdetect_save_spt_image) { // For debug. + STRING outfile; + GetOutputTiffName("_spt", &outfile); + PaintSpecialTexts(outfile); + } + } + + void EquationDetect::IdentifyBlobsToSkip(ColPartition* part) { + ASSERT_HOST(part); + BLOBNBOX_C_IT blob_it(part->boxes()); + + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + // At this moment, no blob should have been joined. + ASSERT_HOST(!blob_it.data()->joined_to_prev()); + } + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + BLOBNBOX* blob = blob_it.data(); + if (blob->joined_to_prev() || blob->special_text_type() == BSTT_SKIP) { + continue; + } + TBOX blob_box = blob->bounding_box(); + + // Search if any blob can be merged into blob. If found, then we mark all + // these blobs as BSTT_SKIP. + BLOBNBOX_C_IT blob_it2 = blob_it; + bool found = false; + while (!blob_it2.at_last()) { + BLOBNBOX* nextblob = blob_it2.forward(); + const TBOX& nextblob_box = nextblob->bounding_box(); + if (nextblob_box.left() >= blob_box.right()) { + break; + } + const float kWidthR = 0.4, kHeightR = 0.3; + bool xoverlap = blob_box.major_x_overlap(nextblob_box), + yoverlap = blob_box.y_overlap(nextblob_box); + float widthR = static_cast( + MIN(nextblob_box.width(), blob_box.width())) / + MAX(nextblob_box.width(), blob_box.width()); + float heightR = static_cast( + MIN(nextblob_box.height(), blob_box.height())) / + MAX(nextblob_box.height(), blob_box.height()); + + if (xoverlap && yoverlap && widthR > kWidthR && heightR > kHeightR) { + // Found one, set nextblob type and recompute blob_box. + found = true; + nextblob->set_special_text_type(BSTT_SKIP); + blob_box += nextblob_box; + } + } + if (found) { + blob->set_special_text_type(BSTT_SKIP); + } + } + } + + int EquationDetect::FindEquationParts( + ColPartitionGrid* part_grid, ColPartitionSet** best_columns) { + if (!lang_tesseract_) { + tprintf("Warning: lang_tesseract_ is NULL!\n"); + return -1; + } + if (!part_grid || !best_columns) { + tprintf("part_grid/best_columns is NULL!!\n"); + return -1; + } + cp_seeds_.clear(); + part_grid_ = part_grid; + best_columns_ = best_columns; + resolution_ = lang_tesseract_->source_resolution(); + STRING outfile; + page_count_++; + + if (equationdetect_save_bi_image) { + GetOutputTiffName("_bi", &outfile); + pixWrite(outfile.string(), lang_tesseract_->pix_binary(), IFF_TIFF_G4); + } + + // Pass 0: Compute special text type for blobs. + IdentifySpecialText(); + + // Pass 1: Merge parts by overlap. + MergePartsByLocation(); + + // Pass 2: compute the math blob density and find the seed partition. + IdentifySeedParts(); + // We still need separate seed into block seed and inline seed partition. + IdentifyInlineParts(); + + if (equationdetect_save_seed_image) { + GetOutputTiffName("_seed", &outfile); + PaintColParts(outfile); + } + + // Pass 3: expand block equation seeds. + while (!cp_seeds_.empty()) { + GenericVector seeds_expanded; + for (int i = 0; i < cp_seeds_.size(); ++i) { + if (ExpandSeed(cp_seeds_[i])) { + // If this seed is expanded, then we add it into seeds_expanded. Note + // this seed has been removed from part_grid_ if it is expanded. + seeds_expanded.push_back(cp_seeds_[i]); + } + } + // Add seeds_expanded back into part_grid_ and reset cp_seeds_. + for (int i = 0; i < seeds_expanded.size(); ++i) { + InsertPartAfterAbsorb(seeds_expanded[i]); + } + cp_seeds_ = seeds_expanded; + } + + // Pass 4: find math block satellite text partitions and merge them. + ProcessMathBlockSatelliteParts(); + + if (equationdetect_save_merged_image) { // For debug. + GetOutputTiffName("_merged", &outfile); + PaintColParts(outfile); + } + + return 0; + } + + void EquationDetect::MergePartsByLocation() { + while (true) { + ColPartition* part = NULL; + // partitions that have been updated. + GenericVector parts_updated; + ColPartitionGridSearch gsearch(part_grid_); + gsearch.StartFullSearch(); + while ((part = gsearch.NextFullSearch()) != NULL) { + if (!IsTextOrEquationType(part->type())) { + continue; + } + GenericVector parts_to_merge; + SearchByOverlap(part, &parts_to_merge); + if (parts_to_merge.empty()) { + continue; + } + + // Merge parts_to_merge with part, and remove them from part_grid_. + part_grid_->RemoveBBox(part); + for (int i = 0; i < parts_to_merge.size(); ++i) { + ASSERT_HOST(parts_to_merge[i] != NULL && parts_to_merge[i] != part); + part->Absorb(parts_to_merge[i], NULL); + } + gsearch.RepositionIterator(); + + parts_updated.push_back(part); + } + + if (parts_updated.empty()) { // Exit the loop + break; + } + + // Re-insert parts_updated into part_grid_. + for (int i = 0; i < parts_updated.size(); ++i) { + InsertPartAfterAbsorb(parts_updated[i]); + } + } + } + + void EquationDetect::SearchByOverlap( + ColPartition* seed, + GenericVector* parts_overlap) { + ASSERT_HOST(seed != NULL && parts_overlap != NULL); + if (!IsTextOrEquationType(seed->type())) { + return; + } + ColPartitionGridSearch search(part_grid_); + const TBOX& seed_box(seed->bounding_box()); + const int kRadNeighborCells = 30; + search.StartRadSearch((seed_box.left() + seed_box.right()) / 2, + (seed_box.top() + seed_box.bottom()) / 2, + kRadNeighborCells); + search.SetUniqueMode(true); + + // Search iteratively. + ColPartition *part; + GenericVector parts; + const float kLargeOverlapTh = 0.95; + const float kEquXOverlap = 0.4, kEquYOverlap = 0.5; + while ((part = search.NextRadSearch()) != NULL) { + if (part == seed || !IsTextOrEquationType(part->type())) { + continue; + } + const TBOX& part_box(part->bounding_box()); + bool merge = false; + + float x_overlap_fraction = part_box.x_overlap_fraction(seed_box), + y_overlap_fraction = part_box.y_overlap_fraction(seed_box); + + // If part is large overlapped with seed, then set merge to true. + if (x_overlap_fraction >= kLargeOverlapTh && + y_overlap_fraction >= kLargeOverlapTh) { + merge = true; + } + else if (seed->type() == PT_EQUATION && + IsTextOrEquationType(part->type())) { + if ((x_overlap_fraction > kEquXOverlap && y_overlap_fraction > 0.0) || + (x_overlap_fraction > 0.0 && y_overlap_fraction > kEquYOverlap)) { + merge = true; + } + } + + if (merge) { // Remove the part from search and put it into parts. + search.RemoveBBox(); + parts_overlap->push_back(part); + } + } + } + + void EquationDetect::InsertPartAfterAbsorb(ColPartition* part) { + ASSERT_HOST(part); + + // Before insert part back into part_grid_, we will need re-compute some + // of its attributes such as first_column_, last_column_. However, we still + // want to preserve its type. + BlobTextFlowType flow_type = part->flow(); + PolyBlockType part_type = part->type(); + BlobRegionType blob_type = part->blob_type(); + + // Call SetPartitionType to re-compute the attributes of part. + const TBOX& part_box(part->bounding_box()); + int grid_x, grid_y; + part_grid_->GridCoords( + part_box.left(), part_box.bottom(), &grid_x, &grid_y); + part->SetPartitionType(resolution_, best_columns_[grid_y]); + + // Reset the types back. + part->set_type(part_type); + part->set_blob_type(blob_type); + part->set_flow(flow_type); + part->SetBlobTypes(); + + // Insert into part_grid_. + part_grid_->InsertBBox(true, true, part); + } + + void EquationDetect::IdentifySeedParts() { + ColPartitionGridSearch gsearch(part_grid_); + ColPartition *part = NULL; + gsearch.StartFullSearch(); + + GenericVector seeds1, seeds2; + // The left coordinates of indented text partitions. + GenericVector indented_texts_left; + // The foreground density of text partitions. + GenericVector texts_foreground_density; + while ((part = gsearch.NextFullSearch()) != NULL) { + if (!IsTextOrEquationType(part->type())) { + continue; + } + part->ComputeSpecialBlobsDensity(); + bool blobs_check = CheckSeedBlobsCount(part); + const int kTextBlobsTh = 20; + + if (CheckSeedDensity(kMathDigitDensityTh1, kMathDigitDensityTh2, part) && + blobs_check) { + // Passed high density threshold test, save into seeds1. + seeds1.push_back(part); + } + else { + IndentType indent = IsIndented(part); + if (IsLeftIndented(indent) && blobs_check && + CheckSeedDensity(kMathDigitDensityTh2, kMathDigitDensityTh2, part)) { + // Passed low density threshold test and is indented, save into seeds2. + seeds2.push_back(part); + } + else if (!IsRightIndented(indent) && + part->boxes_count() > kTextBlobsTh) { + // This is likely to be a text part, save the features. + const TBOX&box = part->bounding_box(); + if (IsLeftIndented(indent)) { + indented_texts_left.push_back(box.left()); + } + texts_foreground_density.push_back(ComputeForegroundDensity(box)); + } + } + } + + // Sort the features collected from text regions. + indented_texts_left.sort(); + texts_foreground_density.sort(); + float foreground_density_th = 0.15; // Default value. + if (!texts_foreground_density.empty()) { + // Use the median of the texts_foreground_density. + foreground_density_th = 0.8 * texts_foreground_density[ + texts_foreground_density.size() / 2]; + } + + for (int i = 0; i < seeds1.size(); ++i) { + const TBOX& box = seeds1[i]->bounding_box(); + if (CheckSeedFgDensity(foreground_density_th, seeds1[i]) && + !(IsLeftIndented(IsIndented(seeds1[i])) && + CountAlignment(indented_texts_left, box.left()) >= + kLeftIndentAlignmentCountTh)) { + // Mark as PT_EQUATION type. + seeds1[i]->set_type(PT_EQUATION); + cp_seeds_.push_back(seeds1[i]); + } + else { // Mark as PT_INLINE_EQUATION type. + seeds1[i]->set_type(PT_INLINE_EQUATION); + } + } + + for (int i = 0; i < seeds2.size(); ++i) { + if (CheckForSeed2(indented_texts_left, foreground_density_th, seeds2[i])) { + seeds2[i]->set_type(PT_EQUATION); + cp_seeds_.push_back(seeds2[i]); + } + } + } + + float EquationDetect::ComputeForegroundDensity(const TBOX& tbox) { + Pix *pix_bi = lang_tesseract_->pix_binary(); + int pix_height = pixGetHeight(pix_bi); + Box* box = boxCreate(tbox.left(), pix_height - tbox.top(), + tbox.width(), tbox.height()); + Pix *pix_sub = pixClipRectangle(pix_bi, box, NULL); + l_float32 fract; + pixForegroundFraction(pix_sub, &fract); + pixDestroy(&pix_sub); + boxDestroy(&box); + + return fract; + } + + bool EquationDetect::CheckSeedFgDensity(const float density_th, + ColPartition* part) { + ASSERT_HOST(part); + + // Split part horizontall, and check for each sub part. + GenericVector sub_boxes; + SplitCPHorLite(part, &sub_boxes); + float parts_passed = 0.0; + for (int i = 0; i < sub_boxes.size(); ++i) { + float density = ComputeForegroundDensity(sub_boxes[i]); + if (density < density_th) { + parts_passed++; + } + } + + // If most sub parts passed, then we return true. + const float kSeedPartRatioTh = 0.3; + bool retval = (parts_passed / sub_boxes.size() >= kSeedPartRatioTh); + + return retval; + } + + void EquationDetect::SplitCPHor(ColPartition* part, + GenericVector* parts_splitted) { + ASSERT_HOST(part && parts_splitted); + if (part->median_width() == 0 || part->boxes_count() == 0) { + return; + } + + // Make a copy of part, and reset parts_splitted. + ColPartition* right_part = part->CopyButDontOwnBlobs(); + parts_splitted->delete_data_pointers(); + parts_splitted->clear(); + + const double kThreshold = part->median_width() * 3.0; + bool found_split = true; + while (found_split) { + found_split = false; + BLOBNBOX_C_IT box_it(right_part->boxes()); + // Blobs are sorted left side first. If blobs overlap, + // the previous blob may have a "more right" right side. + // Account for this by always keeping the largest "right" + // so far. + int previous_right = MIN_INT32; + + // Look for the next split in the partition. + for (box_it.mark_cycle_pt(); !box_it.cycled_list(); box_it.forward()) { + const TBOX& box = box_it.data()->bounding_box(); + if (previous_right != MIN_INT32 && + box.left() - previous_right > kThreshold) { + // We have a split position. Split the partition in two pieces. + // Insert the left piece in the grid and keep processing the right. + int mid_x = (box.left() + previous_right) / 2; + ColPartition* left_part = right_part; + right_part = left_part->SplitAt(mid_x); + + parts_splitted->push_back(left_part); + left_part->ComputeSpecialBlobsDensity(); + found_split = true; + break; + } + + // The right side of the previous blobs. + previous_right = MAX(previous_right, box.right()); + } + } + + // Add the last piece. + right_part->ComputeSpecialBlobsDensity(); + parts_splitted->push_back(right_part); + } + + void EquationDetect::SplitCPHorLite(ColPartition* part, + GenericVector* splitted_boxes) { + ASSERT_HOST(part && splitted_boxes); + splitted_boxes->clear(); + if (part->median_width() == 0) { + return; + } + + const double kThreshold = part->median_width() * 3.0; + + // Blobs are sorted left side first. If blobs overlap, + // the previous blob may have a "more right" right side. + // Account for this by always keeping the largest "right" + // so far. + TBOX union_box; + int previous_right = MIN_INT32; + BLOBNBOX_C_IT box_it(part->boxes()); + for (box_it.mark_cycle_pt(); !box_it.cycled_list(); box_it.forward()) { + const TBOX& box = box_it.data()->bounding_box(); + if (previous_right != MIN_INT32 && + box.left() - previous_right > kThreshold) { + // We have a split position. + splitted_boxes->push_back(union_box); + previous_right = MIN_INT32; + } + if (previous_right == MIN_INT32) { + union_box = box; + } + else { + union_box += box; + } + // The right side of the previous blobs. + previous_right = MAX(previous_right, box.right()); + } + + // Add the last piece. + if (previous_right != MIN_INT32) { + splitted_boxes->push_back(union_box); + } + } + + bool EquationDetect::CheckForSeed2( + const GenericVector& indented_texts_left, + const float foreground_density_th, + ColPartition* part) { + ASSERT_HOST(part); + const TBOX& box = part->bounding_box(); + + // Check if it is aligned with any indented_texts_left. + if (!indented_texts_left.empty() && + CountAlignment(indented_texts_left, box.left()) >= + kLeftIndentAlignmentCountTh) { + return false; + } + + // Check the foreground density. + if (ComputeForegroundDensity(box) > foreground_density_th) { + return false; + } + + return true; + } + + int EquationDetect::CountAlignment( + const GenericVector& sorted_vec, const int val) const { + if (sorted_vec.empty()) { + return 0; + } + const int kDistTh = static_cast(roundf(0.03 * resolution_)); + int pos = sorted_vec.binary_search(val), count = 0; + + // Search left side. + int index = pos; + while (index >= 0 && abs(val - sorted_vec[index--]) < kDistTh) { + count++; + } + + // Search right side. + index = pos + 1; + while (index < sorted_vec.size() && sorted_vec[index++] - val < kDistTh) { + count++; + } + + return count; + } + + void EquationDetect::IdentifyInlineParts() { + ComputeCPsSuperBBox(); + IdentifyInlinePartsHorizontal(); + int textparts_linespacing = EstimateTextPartLineSpacing(); + IdentifyInlinePartsVertical(true, textparts_linespacing); + IdentifyInlinePartsVertical(false, textparts_linespacing); + } + + void EquationDetect::ComputeCPsSuperBBox() { + ColPartitionGridSearch gsearch(part_grid_); + ColPartition *part = NULL; + gsearch.StartFullSearch(); + if (cps_super_bbox_) { + delete cps_super_bbox_; + } + cps_super_bbox_ = new TBOX(); + while ((part = gsearch.NextFullSearch()) != NULL) { + (*cps_super_bbox_) += part->bounding_box(); + } + } + + void EquationDetect::IdentifyInlinePartsHorizontal() { + ASSERT_HOST(cps_super_bbox_); + GenericVector new_seeds; + const int kMarginDiffTh = IntCastRounded( + 0.5 * lang_tesseract_->source_resolution()); + const int kGapTh = static_cast(roundf( + 1.0 * lang_tesseract_->source_resolution())); + ColPartitionGridSearch search(part_grid_); + search.SetUniqueMode(true); + // The center x coordinate of the cp_super_bbox_. + int cps_cx = cps_super_bbox_->left() + cps_super_bbox_->width() / 2; + for (int i = 0; i < cp_seeds_.size(); ++i) { + ColPartition* part = cp_seeds_[i]; + const TBOX& part_box(part->bounding_box()); + int left_margin = part_box.left() - cps_super_bbox_->left(), + right_margin = cps_super_bbox_->right() - part_box.right(); + bool right_to_left; + if (left_margin + kMarginDiffTh < right_margin && + left_margin < kMarginDiffTh) { + // part is left aligned, so we search if it has any right neighbor. + search.StartSideSearch( + part_box.right(), part_box.top(), part_box.bottom()); + right_to_left = false; + } + else if (left_margin > cps_cx) { + // part locates on the right half on image, so search if it has any left + // neighbor. + search.StartSideSearch( + part_box.left(), part_box.top(), part_box.bottom()); + right_to_left = true; + } + else { // part is not an inline equation. + new_seeds.push_back(part); + continue; + } + ColPartition* neighbor = NULL; + bool side_neighbor_found = false; + while ((neighbor = search.NextSideSearch(right_to_left)) != NULL) { + const TBOX& neighbor_box(neighbor->bounding_box()); + if (!IsTextOrEquationType(neighbor->type()) || + part_box.x_gap(neighbor_box) > kGapTh || + !part_box.major_y_overlap(neighbor_box) || + part_box.major_x_overlap(neighbor_box)) { + continue; + } + // We have found one. Set the side_neighbor_found flag. + side_neighbor_found = true; + break; + } + if (!side_neighbor_found) { // Mark part as PT_INLINE_EQUATION. + part->set_type(PT_INLINE_EQUATION); + } + else { + // Check the geometric feature of neighbor. + const TBOX& neighbor_box(neighbor->bounding_box()); + if (neighbor_box.width() > part_box.width() && + neighbor->type() != PT_EQUATION) { // Mark as PT_INLINE_EQUATION. + part->set_type(PT_INLINE_EQUATION); + } + else { // part is not an inline equation type. + new_seeds.push_back(part); + } + } + } + + // Reset the cp_seeds_ using the new_seeds. + cp_seeds_ = new_seeds; + } + + int EquationDetect::EstimateTextPartLineSpacing() { + ColPartitionGridSearch gsearch(part_grid_); + + // Get the y gap between text partitions; + ColPartition *current = NULL, *prev = NULL; + gsearch.StartFullSearch(); + GenericVector ygaps; + while ((current = gsearch.NextFullSearch()) != NULL) { + if (!PTIsTextType(current->type())) { + continue; + } + if (prev != NULL) { + const TBOX ¤t_box = current->bounding_box(); + const TBOX &prev_box = prev->bounding_box(); + // prev and current should be x major overlap and non y overlap. + if (current_box.major_x_overlap(prev_box) && + !current_box.y_overlap(prev_box)) { + int gap = current_box.y_gap(prev_box); + if (gap < MIN(current_box.height(), prev_box.height())) { + // The gap should be smaller than the height of the bounding boxes. + ygaps.push_back(gap); + } + } + } + prev = current; + } + + if (ygaps.size() < 8) { // We do not have enough data. + return -1; + } + + // Compute the line spacing from ygaps: use the mean of the first half. + ygaps.sort(); + int spacing = 0, count; + for (count = 0; count < ygaps.size() / 2; count++) { + spacing += ygaps[count]; + } + return spacing / count; + } + + void EquationDetect::IdentifyInlinePartsVertical( + const bool top_to_bottom, const int textparts_linespacing) { + if (cp_seeds_.empty()) { + return; + } + + // Sort cp_seeds_. + if (top_to_bottom) { // From top to bottom. + cp_seeds_.sort(&SortCPByTopReverse); + } + else { // From bottom to top. + cp_seeds_.sort(&SortCPByBottom); + } + + GenericVector new_seeds; + for (int i = 0; i < cp_seeds_.size(); ++i) { + ColPartition* part = cp_seeds_[i]; + // If we sort cp_seeds_ from top to bottom, then for each cp_seeds_, we look + // for its top neighbors, so that if two/more inline regions are connected + // to each other, then we will identify the top one, and then use it to + // identify the bottom one. + if (IsInline(!top_to_bottom, textparts_linespacing, part)) { + part->set_type(PT_INLINE_EQUATION); + } + else { + new_seeds.push_back(part); + } + } + cp_seeds_ = new_seeds; + } + + bool EquationDetect::IsInline(const bool search_bottom, + const int textparts_linespacing, + ColPartition* part) { + ASSERT_HOST(part != NULL); + // Look for its nearest vertical neighbor that hardly overlaps in y but + // largely overlaps in x. + ColPartitionGridSearch search(part_grid_); + ColPartition *neighbor = NULL; + const TBOX& part_box(part->bounding_box()); + const float kYGapRatioTh = 1.0; + + if (search_bottom) { + search.StartVerticalSearch(part_box.left(), part_box.right(), + part_box.bottom()); + } + else { + search.StartVerticalSearch(part_box.left(), part_box.right(), + part_box.top()); + } + search.SetUniqueMode(true); + while ((neighbor = search.NextVerticalSearch(search_bottom)) != NULL) { + const TBOX& neighbor_box(neighbor->bounding_box()); + if (part_box.y_gap(neighbor_box) > kYGapRatioTh * + MIN(part_box.height(), neighbor_box.height())) { + // Finished searching. + break; + } + if (!PTIsTextType(neighbor->type())) { + continue; + } + + // Check if neighbor and part is inline similar. + const float kHeightRatioTh = 0.5; + const int kYGapTh = textparts_linespacing > 0 ? + textparts_linespacing + static_cast(roundf(0.02 * resolution_)) : + static_cast(roundf(0.05 * resolution_)); // Default value. + if (part_box.x_overlap(neighbor_box) && // Location feature. + part_box.y_gap(neighbor_box) <= kYGapTh && // Line spacing. + // Geo feature. + static_cast(MIN(part_box.height(), neighbor_box.height())) / + MAX(part_box.height(), neighbor_box.height()) > kHeightRatioTh) { + return true; + } + } + + return false; + } + + bool EquationDetect::CheckSeedBlobsCount(ColPartition* part) { + if (!part) { + return false; + } + const int kSeedMathBlobsCount = 2; + const int kSeedMathDigitBlobsCount = 5; + + int blobs = part->boxes_count(), + math_blobs = part->SpecialBlobsCount(BSTT_MATH), + digit_blobs = part->SpecialBlobsCount(BSTT_DIGIT); + if (blobs < kSeedBlobsCountTh || math_blobs <= kSeedMathBlobsCount || + math_blobs + digit_blobs <= kSeedMathDigitBlobsCount) { + return false; + } + + return true; + } + + bool EquationDetect::CheckSeedDensity( + const float math_density_high, + const float math_density_low, + const ColPartition* part) const { + ASSERT_HOST(part); + float math_digit_density = part->SpecialBlobsDensity(BSTT_MATH) + + part->SpecialBlobsDensity(BSTT_DIGIT); + float italic_density = part->SpecialBlobsDensity(BSTT_ITALIC); + if (math_digit_density > math_density_high) { + return true; + } + if (math_digit_density + italic_density > kMathItalicDensityTh && + math_digit_density > math_density_low) { + return true; + } + + return false; + } + + EquationDetect::IndentType EquationDetect::IsIndented(ColPartition* part) { + ASSERT_HOST(part); + + ColPartitionGridSearch search(part_grid_); + ColPartition *neighbor = NULL; + const TBOX& part_box(part->bounding_box()); + const int kXGapTh = static_cast(roundf(0.5 * resolution_)); + const int kRadiusTh = static_cast(roundf(3.0 * resolution_)); + const int kYGapTh = static_cast(roundf(0.5 * resolution_)); + + // Here we use a simple approximation algorithm: from the center of part, We + // perform the radius search, and check if we can find a neighboring parition + // that locates on the top/bottom left of part. + search.StartRadSearch((part_box.left() + part_box.right()) / 2, + (part_box.top() + part_box.bottom()) / 2, kRadiusTh); + search.SetUniqueMode(true); + bool left_indented = false, right_indented = false; + while ((neighbor = search.NextRadSearch()) != NULL && + (!left_indented || !right_indented)) { + if (neighbor == part) { + continue; + } + const TBOX& neighbor_box(neighbor->bounding_box()); + + if (part_box.major_y_overlap(neighbor_box) && + part_box.x_gap(neighbor_box) < kXGapTh) { + // When this happens, it is likely part is a fragment of an + // over-segmented colpartition. So we return false. + return NO_INDENT; + } + + if (!IsTextOrEquationType(neighbor->type())) { + continue; + } + + // The neighbor should be above/below part, and overlap in x direction. + if (!part_box.x_overlap(neighbor_box) || part_box.y_overlap(neighbor_box)) { + continue; + } + + if (part_box.y_gap(neighbor_box) < kYGapTh) { + int left_gap = part_box.left() - neighbor_box.left(); + int right_gap = neighbor_box.right() - part_box.right(); + if (left_gap > kXGapTh) { + left_indented = true; + } + if (right_gap > kXGapTh) { + right_indented = true; + } + } + } + + if (left_indented && right_indented) { + return BOTH_INDENT; + } + if (left_indented) { + return LEFT_INDENT; + } + if (right_indented) { + return RIGHT_INDENT; + } + return NO_INDENT; + } + + bool EquationDetect::ExpandSeed(ColPartition* seed) { + if (seed == NULL || // This seed has been absorbed by other seeds. + seed->IsVerticalType()) { // We skip vertical type right now. + return false; + } + + // Expand in four directions. + GenericVector parts_to_merge; + ExpandSeedHorizontal(true, seed, &parts_to_merge); + ExpandSeedHorizontal(false, seed, &parts_to_merge); + ExpandSeedVertical(true, seed, &parts_to_merge); + ExpandSeedVertical(false, seed, &parts_to_merge); + SearchByOverlap(seed, &parts_to_merge); + + if (parts_to_merge.empty()) { // We don't find any partition to merge. + return false; + } + + // Merge all partitions in parts_to_merge with seed. We first remove seed + // from part_grid_ as its bounding box is going to expand. Then we add it + // back after it aborbs all parts_to_merge parititions. + part_grid_->RemoveBBox(seed); + for (int i = 0; i < parts_to_merge.size(); ++i) { + ColPartition* part = parts_to_merge[i]; + if (part->type() == PT_EQUATION) { + // If part is in cp_seeds_, then we mark it as NULL so that we won't + // process it again. + for (int j = 0; j < cp_seeds_.size(); ++j) { + if (part == cp_seeds_[j]) { + cp_seeds_[j] = NULL; + break; + } + } + } + + // part has already been removed from part_grid_ in function + // ExpandSeedHorizontal/ExpandSeedVertical. + seed->Absorb(part, NULL); + } + + return true; + } + + void EquationDetect::ExpandSeedHorizontal( + const bool search_left, + ColPartition* seed, + GenericVector* parts_to_merge) { + ASSERT_HOST(seed != NULL && parts_to_merge != NULL); + const float kYOverlapTh = 0.6; + const int kXGapTh = static_cast(roundf(0.2 * resolution_)); + + ColPartitionGridSearch search(part_grid_); + const TBOX& seed_box(seed->bounding_box()); + int x = search_left ? seed_box.left() : seed_box.right(); + search.StartSideSearch(x, seed_box.bottom(), seed_box.top()); + search.SetUniqueMode(true); + + // Search iteratively. + ColPartition *part = NULL; + while ((part = search.NextSideSearch(search_left)) != NULL) { + if (part == seed) { + continue; + } + const TBOX& part_box(part->bounding_box()); + if (part_box.x_gap(seed_box) > kXGapTh) { // Out of scope. + break; + } + + // Check part location. + if ((part_box.left() >= seed_box.left() && search_left) || + (part_box.right() <= seed_box.right() && !search_left)) { + continue; + } + + if (part->type() != PT_EQUATION) { // Non-equation type. + // Skip PT_LINLINE_EQUATION and non text type. + if (part->type() == PT_INLINE_EQUATION || + (!IsTextOrEquationType(part->type()) && + part->blob_type() != BRT_HLINE)) { + continue; + } + // For other types, it should be the near small neighbor of seed. + if (!IsNearSmallNeighbor(seed_box, part_box) || + !CheckSeedNeighborDensity(part)) { + continue; + } + } + else { // Equation type, check the y overlap. + if (part_box.y_overlap_fraction(seed_box) < kYOverlapTh && + seed_box.y_overlap_fraction(part_box) < kYOverlapTh) { + continue; + } + } + + // Passed the check, delete it from search and add into parts_to_merge. + search.RemoveBBox(); + parts_to_merge->push_back(part); + } + } + + void EquationDetect::ExpandSeedVertical( + const bool search_bottom, + ColPartition* seed, + GenericVector* parts_to_merge) { + ASSERT_HOST(seed != NULL && parts_to_merge != NULL && + cps_super_bbox_ != NULL); + const float kXOverlapTh = 0.4; + const int kYGapTh = static_cast(roundf(0.2 * resolution_)); + + ColPartitionGridSearch search(part_grid_); + const TBOX& seed_box(seed->bounding_box()); + int y = search_bottom ? seed_box.bottom() : seed_box.top(); + search.StartVerticalSearch( + cps_super_bbox_->left(), cps_super_bbox_->right(), y); + search.SetUniqueMode(true); + + // Search iteratively. + ColPartition *part = NULL; + GenericVector parts; + int skipped_min_top = INT_MAX, skipped_max_bottom = -1; + while ((part = search.NextVerticalSearch(search_bottom)) != NULL) { + if (part == seed) { + continue; + } + const TBOX& part_box(part->bounding_box()); + + if (part_box.y_gap(seed_box) > kYGapTh) { // Out of scope. + break; + } + + // Check part location. + if ((part_box.bottom() >= seed_box.bottom() && search_bottom) || + (part_box.top() <= seed_box.top() && !search_bottom)) { + continue; + } + + bool skip_part = false; + if (part->type() != PT_EQUATION) { // Non-equation type. + // Skip PT_LINLINE_EQUATION and non text type. + if (part->type() == PT_INLINE_EQUATION || + (!IsTextOrEquationType(part->type()) && + part->blob_type() != BRT_HLINE)) { + skip_part = true; + } + else if (!IsNearSmallNeighbor(seed_box, part_box) || + !CheckSeedNeighborDensity(part)) { + // For other types, it should be the near small neighbor of seed. + skip_part = true; + } + } + else { // Equation type, check the x overlap. + if (part_box.x_overlap_fraction(seed_box) < kXOverlapTh && + seed_box.x_overlap_fraction(part_box) < kXOverlapTh) { + skip_part = true; + } + } + if (skip_part) { + if (part->type() != PT_EQUATION) { + if (skipped_min_top > part_box.top()) { + skipped_min_top = part_box.top(); + } + if (skipped_max_bottom < part_box.bottom()) { + skipped_max_bottom = part_box.bottom(); + } + } + } + else { + parts.push_back(part); + } + } + + // For every part in parts, we need verify it is not above skipped_min_top + // when search top, or not below skipped_max_bottom when search bottom. I.e., + // we will skip a part if it looks like: + // search bottom | search top + // seed: ****************** | part: ********** + // skipped: xxx | skipped: xxx + // part: ********** | seed: *********** + for (int i = 0; i < parts.size(); i++) { + const TBOX& part_box(parts[i]->bounding_box()); + if ((search_bottom && part_box.top() <= skipped_max_bottom) || + (!search_bottom && part_box.bottom() >= skipped_min_top)) { + continue; + } + // Add parts[i] into parts_to_merge, and delete it from part_grid_. + parts_to_merge->push_back(parts[i]); + part_grid_->RemoveBBox(parts[i]); + } + } + + bool EquationDetect::IsNearSmallNeighbor(const TBOX& seed_box, + const TBOX& part_box) const { + const int kXGapTh = static_cast(roundf(0.25 * resolution_)); + const int kYGapTh = static_cast(roundf(0.05 * resolution_)); + + // Check geometric feature. + if (part_box.height() > seed_box.height() || + part_box.width() > seed_box.width()) { + return false; + } + + // Check overlap and distance. + if ((!part_box.major_x_overlap(seed_box) || + part_box.y_gap(seed_box) > kYGapTh) && + (!part_box.major_y_overlap(seed_box) || + part_box.x_gap(seed_box) > kXGapTh)) { + return false; + } + + return true; + } + + bool EquationDetect::CheckSeedNeighborDensity(const ColPartition* part) const { + ASSERT_HOST(part); + if (part->boxes_count() < kSeedBlobsCountTh) { + // Too few blobs, skip the check. + return true; + } + + // We check the math blobs density and the unclear blobs density. + if (part->SpecialBlobsDensity(BSTT_MATH) + + part->SpecialBlobsDensity(BSTT_DIGIT) > kMathDigitDensityTh1 || + part->SpecialBlobsDensity(BSTT_UNCLEAR) > kUnclearDensityTh) { + return true; + } + + return false; + } + + void EquationDetect::ProcessMathBlockSatelliteParts() { + // Iterate over part_grid_, and find all parts that are text type but not + // equation type. + ColPartition *part = NULL; + GenericVector text_parts; + ColPartitionGridSearch gsearch(part_grid_); + gsearch.StartFullSearch(); + while ((part = gsearch.NextFullSearch()) != NULL) { + if (part->type() == PT_FLOWING_TEXT || part->type() == PT_HEADING_TEXT) { + text_parts.push_back(part); + } + } + if (text_parts.empty()) { + return; + } + + // Compute the medium height of the text_parts. + text_parts.sort(&SortCPByHeight); + const TBOX& text_box = text_parts[text_parts.size() / 2]->bounding_box(); + int med_height = text_box.height(); + if (text_parts.size() % 2 == 0 && text_parts.size() > 1) { + const TBOX& text_box = + text_parts[text_parts.size() / 2 - 1]->bounding_box(); + med_height = static_cast(roundf( + 0.5 * (text_box.height() + med_height))); + } + + // Iterate every text_parts and check if it is a math block satellite. + for (int i = 0; i < text_parts.size(); ++i) { + const TBOX& text_box(text_parts[i]->bounding_box()); + if (text_box.height() > med_height) { + continue; + } + GenericVector math_blocks; + if (!IsMathBlockSatellite(text_parts[i], &math_blocks)) { + continue; + } + + // Found. merge text_parts[i] with math_blocks. + part_grid_->RemoveBBox(text_parts[i]); + text_parts[i]->set_type(PT_EQUATION); + for (int j = 0; j < math_blocks.size(); ++j) { + part_grid_->RemoveBBox(math_blocks[j]); + text_parts[i]->Absorb(math_blocks[j], NULL); + } + InsertPartAfterAbsorb(text_parts[i]); + } + } + + bool EquationDetect::IsMathBlockSatellite( + ColPartition* part, GenericVector* math_blocks) { + ASSERT_HOST(part != NULL && math_blocks != NULL); + math_blocks->clear(); + const TBOX& part_box(part->bounding_box()); + // Find the top/bottom nearest neighbor of part. + ColPartition *neighbors[2]; + int y_gaps[2] = { INT_MAX, INT_MAX }; + // The horizontal boundary of the neighbors. + int neighbors_left = INT_MAX, neighbors_right = 0; + for (int i = 0; i < 2; ++i) { + neighbors[i] = SearchNNVertical(i != 0, part); + if (neighbors[i]) { + const TBOX& neighbor_box = neighbors[i]->bounding_box(); + y_gaps[i] = neighbor_box.y_gap(part_box); + if (neighbor_box.left() < neighbors_left) { + neighbors_left = neighbor_box.left(); + } + if (neighbor_box.right() > neighbors_right) { + neighbors_right = neighbor_box.right(); + } + } + } + if (neighbors[0] == neighbors[1]) { + // This happens when part is inside neighbor. + neighbors[1] = NULL; + y_gaps[1] = INT_MAX; + } + + // Check if part is within [neighbors_left, neighbors_right]. + if (part_box.left() < neighbors_left || part_box.right() > neighbors_right) { + return false; + } + + // Get the index of the near one in neighbors. + int index = y_gaps[0] < y_gaps[1] ? 0 : 1; + + // Check the near one. + if (IsNearMathNeighbor(y_gaps[index], neighbors[index])) { + math_blocks->push_back(neighbors[index]); + } + else { + // If the near one failed the check, then we skip checking the far one. + return false; + } + + // Check the far one. + index = 1 - index; + if (IsNearMathNeighbor(y_gaps[index], neighbors[index])) { + math_blocks->push_back(neighbors[index]); + } + + return true; + } + + ColPartition* EquationDetect::SearchNNVertical( + const bool search_bottom, const ColPartition* part) { + ASSERT_HOST(part); + ColPartition *nearest_neighbor = NULL, *neighbor = NULL; + const int kYGapTh = static_cast(roundf(resolution_ * 0.5)); + + ColPartitionGridSearch search(part_grid_); + search.SetUniqueMode(true); + const TBOX& part_box(part->bounding_box()); + int y = search_bottom ? part_box.bottom() : part_box.top(); + search.StartVerticalSearch(part_box.left(), part_box.right(), y); + int min_y_gap = INT_MAX; + while ((neighbor = search.NextVerticalSearch(search_bottom)) != NULL) { + if (neighbor == part || !IsTextOrEquationType(neighbor->type())) { + continue; + } + const TBOX& neighbor_box(neighbor->bounding_box()); + int y_gap = neighbor_box.y_gap(part_box); + if (y_gap > kYGapTh) { // Out of scope. + break; + } + if (!neighbor_box.major_x_overlap(part_box) || + (search_bottom && neighbor_box.bottom() > part_box.bottom()) || + (!search_bottom && neighbor_box.top() < part_box.top())) { + continue; + } + if (y_gap < min_y_gap) { + min_y_gap = y_gap; + nearest_neighbor = neighbor; + } + } + + return nearest_neighbor; + } + + bool EquationDetect::IsNearMathNeighbor( + const int y_gap, const ColPartition *neighbor) const { + if (!neighbor) { + return false; + } + const int kYGapTh = static_cast(roundf(resolution_ * 0.1)); + return neighbor->type() == PT_EQUATION && y_gap <= kYGapTh; + } + + void EquationDetect::GetOutputTiffName(const char* name, + STRING* image_name) const { + ASSERT_HOST(image_name && name); + char page[50]; + snprintf(page, sizeof(page), "%04d", page_count_); + *image_name = STRING(lang_tesseract_->imagebasename) + page + name + ".tif"; + } + + void EquationDetect::PaintSpecialTexts(const STRING& outfile) const { + Pix *pix = NULL, *pixBi = lang_tesseract_->pix_binary(); + pix = pixConvertTo32(pixBi); + ColPartitionGridSearch gsearch(part_grid_); + ColPartition* part = NULL; + gsearch.StartFullSearch(); + while ((part = gsearch.NextFullSearch()) != NULL) { + BLOBNBOX_C_IT blob_it(part->boxes()); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + RenderSpecialText(pix, blob_it.data()); + } + } + + pixWrite(outfile.string(), pix, IFF_TIFF_LZW); + pixDestroy(&pix); + } + + void EquationDetect::PaintColParts(const STRING& outfile) const { + Pix *pix = pixConvertTo32(lang_tesseract_->BestPix()); + ColPartitionGridSearch gsearch(part_grid_); + gsearch.StartFullSearch(); + ColPartition* part = NULL; + while ((part = gsearch.NextFullSearch()) != NULL) { + const TBOX& tbox = part->bounding_box(); + Box *box = boxCreate(tbox.left(), pixGetHeight(pix) - tbox.top(), + tbox.width(), tbox.height()); + if (part->type() == PT_EQUATION) { + pixRenderBoxArb(pix, box, 5, 255, 0, 0); + } + else if (part->type() == PT_INLINE_EQUATION) { + pixRenderBoxArb(pix, box, 5, 0, 255, 0); + } + else { + pixRenderBoxArb(pix, box, 5, 0, 0, 255); + } + boxDestroy(&box); + } + + pixWrite(outfile.string(), pix, IFF_TIFF_LZW); + pixDestroy(&pix); + } + + void EquationDetect::PrintSpecialBlobsDensity(const ColPartition* part) const { + ASSERT_HOST(part); + TBOX box(part->bounding_box()); + int h = pixGetHeight(lang_tesseract_->BestPix()); + tprintf("Printing special blobs density values for ColParition (t=%d,b=%d) ", + h - box.top(), h - box.bottom()); + box.print(); + tprintf("blobs count = %d, density = ", part->boxes_count()); + for (int i = 0; i < BSTT_COUNT; ++i) { + BlobSpecialTextType type = static_cast(i); + tprintf("%d:%f ", i, part->SpecialBlobsDensity(type)); + } + tprintf("\n"); + } + +}; // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.h b/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.h new file mode 100644 index 0000000..6ffac15 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/equationdetect.h @@ -0,0 +1,278 @@ +/////////////////////////////////////////////////////////////////////// +// File: equationdetect.h +// Description: The equation detection class that inherits equationdetectbase. +// Author: Zongyi (Joe) Liu (joeliu@google.com) +// Created: Fri Aug 31 11:13: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_CCMAIN_EQUATIONDETECT_H__ +#define TESSERACT_CCMAIN_EQUATIONDETECT_H__ + +#include "blobbox.h" +#include "equationdetectbase.h" +#include "genericvector.h" +#include "tesseractclass.h" +#include "unichar.h" + +class BLOBNBOX; +class BLOB_CHOICE; +class BLOB_CHOICE_LIST; +class TO_BLOCK_LIST; +class TBOX; +class UNICHARSET; + +namespace tesseract { + + class Tesseract; + class ColPartition; + class ColPartitionGrid; + class ColPartitionSet; + + class EquationDetect : public EquationDetectBase { + public: + EquationDetect(const char* equ_datapath, + const char* equ_language); + ~EquationDetect(); + + enum IndentType { + NO_INDENT, + LEFT_INDENT, + RIGHT_INDENT, + BOTH_INDENT, + INDENT_TYPE_COUNT + }; + + // Reset the lang_tesseract_ pointer. This function should be called before we + // do any detector work. + void SetLangTesseract(Tesseract* lang_tesseract); + + // Iterate over the blobs inside to_block, and set the blobs that we want to + // process to BSTT_NONE. (By default, they should be BSTT_SKIP). The function + // returns 0 upon success. + int LabelSpecialText(TO_BLOCK* to_block); + + // Find possible equation partitions from part_grid. Should be called + // after the special_text_type of blobs are set. + // It returns 0 upon success. + int FindEquationParts(ColPartitionGrid* part_grid, + ColPartitionSet** best_columns); + + // Reset the resolution of the processing image. TEST only function. + void SetResolution(const int resolution); + + protected: + // Identify the special text type for one blob, and update its field. When + // height_th is set (> 0), we will label the blob as BSTT_NONE if its height + // is less than height_th. + void IdentifySpecialText(BLOBNBOX *blob, const int height_th); + + // Estimate the type for one unichar. + BlobSpecialTextType EstimateTypeForUnichar( + const UNICHARSET& unicharset, const UNICHAR_ID id) const; + + // Compute special text type for each blobs in part_grid_. + void IdentifySpecialText(); + + // Identify blobs that we want to skip during special blob type + // classification. + void IdentifyBlobsToSkip(ColPartition* part); + + // The ColPartitions in part_grid_ maybe over-segmented, particularly in the + // block equation regions. So we like to identify these partitions and merge + // them before we do the searching. + void MergePartsByLocation(); + + // Staring from the seed center, we do radius search. And for partitions that + // have large overlaps with seed, we remove them from part_grid_ and add into + // parts_overlap. Note: this function may update the part_grid_, so if the + // caller is also running ColPartitionGridSearch, use the RepositionIterator + // to continue. + void SearchByOverlap(ColPartition* seed, + GenericVector* parts_overlap); + + // Insert part back into part_grid_, after it absorbs some other parts. + void InsertPartAfterAbsorb(ColPartition* part); + + // Identify the colparitions in part_grid_, label them as PT_EQUATION, and + // save them into cp_seeds_. + void IdentifySeedParts(); + + // Check the blobs count for a seed region candidate. + bool CheckSeedBlobsCount(ColPartition* part); + + // Compute the foreground pixel density for a tbox area. + float ComputeForegroundDensity(const TBOX& tbox); + + // Check if part from seed2 label: with low math density and left indented. We + // are using two checks: + // 1. If its left is aligned with any coordinates in indented_texts_left, + // which we assume have been sorted. + // 2. If its foreground density is over foreground_density_th. + bool CheckForSeed2( + const GenericVector& indented_texts_left, + const float foreground_density_th, + ColPartition* part); + + // Count the number of values in sorted_vec that is close to val, used to + // check if a partition is aligned with text partitions. + int CountAlignment( + const GenericVector& sorted_vec, const int val) const; + + // Check for a seed candidate using the foreground pixel density. And we + // return true if the density is below a certain threshold, because characters + // in equation regions usually are apart with more white spaces. + bool CheckSeedFgDensity(const float density_th, ColPartition* part); + + // A light version of SplitCPHor: instead of really doing the part split, we + // simply compute the union bounding box of each splitted part. + void SplitCPHorLite(ColPartition* part, GenericVector* splitted_boxes); + + // Split the part (horizontally), and save the splitted result into + // parts_splitted. Note that it is caller's responsibility to release the + // memory owns by parts_splitted. On the other hand, the part is unchanged + // during this process and still owns the blobs, so do NOT call DeleteBoxes + // when freeing the colpartitions in parts_splitted. + void SplitCPHor(ColPartition* part, + GenericVector* parts_splitted); + + // Check the density for a seed candidate (part) using its math density and + // italic density, returns true if the check passed. + bool CheckSeedDensity(const float math_density_high, + const float math_density_low, + const ColPartition* part) const; + + // Check if part is indented. + IndentType IsIndented(ColPartition* part); + + // Identify inline partitions from cp_seeds_, and re-label them. + void IdentifyInlineParts(); + + // Comute the super bounding box for all colpartitions inside part_grid_. + void ComputeCPsSuperBBox(); + + // Identify inline partitions from cp_seeds_ using the horizontal search. + void IdentifyInlinePartsHorizontal(); + + // Estimate the line spacing between two text partitions. Returns -1 if not + // enough data. + int EstimateTextPartLineSpacing(); + + // Identify inline partitions from cp_seeds_ using vertical search. + void IdentifyInlinePartsVertical(const bool top_to_bottom, + const int textPartsLineSpacing); + + // Check if part is an inline equation zone. This should be called after we + // identified the seed regions. + bool IsInline(const bool search_bottom, + const int textPartsLineSpacing, + ColPartition* part); + + // For a given seed partition, we search the part_grid_ and see if there is + // any partition can be merged with it. It returns true if the seed has been + // expanded. + bool ExpandSeed(ColPartition* seed); + + // Starting from the seed position, we search the part_grid_ + // horizontally/vertically, find all parititions that can be + // merged with seed, remove them from part_grid_, and put them into + // parts_to_merge. + void ExpandSeedHorizontal(const bool search_left, + ColPartition* seed, + GenericVector* parts_to_merge); + void ExpandSeedVertical(const bool search_bottom, + ColPartition* seed, + GenericVector* parts_to_merge); + + // Check if a part_box is the small neighbor of seed_box. + bool IsNearSmallNeighbor(const TBOX& seed_box, + const TBOX& part_box) const; + + // Perform the density check for part, which we assume is nearing a seed + // partition. It returns true if the check passed. + bool CheckSeedNeighborDensity(const ColPartition* part) const; + + // After identify the math blocks, we do one more scanning on all text + // partitions, and check if any of them is the satellite of: + // math blocks: here a p is the satellite of q if: + // 1. q is the nearest vertical neighbor of p, and + // 2. y_gap(p, q) is less than a threshold, and + // 3. x_overlap(p, q) is over a threshold. + // Note that p can be the satellites of two blocks: its top neighbor and + // bottom neighbor. + void ProcessMathBlockSatelliteParts(); + + // Check if part is the satellite of one/two math blocks. If it is, we return + // true, and save the blocks into math_blocks. + bool IsMathBlockSatellite( + ColPartition* part, GenericVector* math_blocks); + + // Search the nearest neighbor of part in one vertical direction as defined in + // search_bottom. It returns the neighbor found that major x overlap with it, + // or NULL when not found. + ColPartition* SearchNNVertical(const bool search_bottom, + const ColPartition* part); + + // Check if the neighbor with vertical distance of y_gap is a near and math + // block partition. + bool IsNearMathNeighbor(const int y_gap, const ColPartition *neighbor) const; + + // Generate the tiff file name for output/debug file. + void GetOutputTiffName(const char* name, STRING* image_name) const; + + // Debugger function that renders ColPartitions on the input image, where: + // parts labeled as PT_EQUATION will be painted in red, PT_INLINE_EQUATION + // will be painted in green, and other parts will be painted in blue. + void PaintColParts(const STRING& outfile) const; + + // Debugger function that renders the blobs in part_grid_ over the input + // image. + void PaintSpecialTexts(const STRING& outfile) const; + + // Debugger function that print the math blobs density values for a + // ColPartition object. + void PrintSpecialBlobsDensity(const ColPartition* part) const; + + // The tesseract engine intialized from equation training data. + Tesseract equ_tesseract_; + + // The tesseract engine used for OCR. This pointer is passed in by the caller, + // so do NOT destroy it in this class. + Tesseract* lang_tesseract_; + + // The ColPartitionGrid that we are processing. This pointer is passed in from + // the caller, so do NOT destroy it in the class. + ColPartitionGrid* part_grid_; + + // A simple array of pointers to the best assigned column division at + // each grid y coordinate. This pointer is passed in from the caller, so do + // NOT destroy it in the class. + ColPartitionSet** best_columns_; + + // The super bounding box of all cps in the part_grid_. + TBOX* cps_super_bbox_; + + // The seed ColPartition for equation region. + GenericVector cp_seeds_; + + // The resolution (dpi) of the processing image. + int resolution_; + + // The number of pages we have processed. + int page_count_; + }; + +} // namespace tesseract + +#endif // TESSERACT_CCMAIN_EQUATIONDETECT_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.cpp new file mode 100644 index 0000000..2bcf3d2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.cpp @@ -0,0 +1,876 @@ +/****************************************************************** + * File: fixspace.cpp (Formerly fixspace.c) + * Description: Implements a pass over the page res, exploring the alternative + * spacing possibilities, trying to use context to improve the + * word spacing +* Author: Phil Cheatle +* Created: Thu Oct 21 11:38:43 BST 1993 +* +* (C) Copyright 1993, Hewlett-Packard Ltd. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +* +**********************************************************************/ + +#include +#include "reject.h" +#include "statistc.h" +#include "control.h" +#include "fixspace.h" +#include "genblob.h" +#include "tessvars.h" +#include "tessbox.h" +#include "globals.h" +#include "tesseractclass.h" + +#define PERFECT_WERDS 999 +#define MAXSPACING 128 /*max expected spacing in pix */ + +namespace tesseract { + + /** + * @name fix_fuzzy_spaces() + * Walk over the page finding sequences of words joined by fuzzy spaces. Extract + * them as a sublist, process the sublist to find the optimal arrangement of + * spaces then replace the sublist in the ROW_RES. + * + * @param monitor progress monitor + * @param word_count count of words in doc + * @param[out] page_res + */ + void Tesseract::fix_fuzzy_spaces(ETEXT_DESC *monitor, + inT32 word_count, + PAGE_RES *page_res) { + BLOCK_RES_IT block_res_it; + ROW_RES_IT row_res_it; + WERD_RES_IT word_res_it_from; + WERD_RES_IT word_res_it_to; + WERD_RES *word_res; + WERD_RES_LIST fuzzy_space_words; + inT16 new_length; + BOOL8 prevent_null_wd_fixsp; // DON'T process blobless wds + inT32 word_index; // current word + + block_res_it.set_to_list(&page_res->block_res_list); + word_index = 0; + for (block_res_it.mark_cycle_pt(); !block_res_it.cycled_list(); + block_res_it.forward()) { + row_res_it.set_to_list(&block_res_it.data()->row_res_list); + for (row_res_it.mark_cycle_pt(); !row_res_it.cycled_list(); + row_res_it.forward()) { + word_res_it_from.set_to_list(&row_res_it.data()->word_res_list); + while (!word_res_it_from.at_last()) { + word_res = word_res_it_from.data(); + while (!word_res_it_from.at_last() && + !(word_res->combination || + word_res_it_from.data_relative(1)->word->flag(W_FUZZY_NON) || + word_res_it_from.data_relative(1)->word->flag(W_FUZZY_SP))) { + fix_sp_fp_word(word_res_it_from, row_res_it.data()->row, + block_res_it.data()->block); + word_res = word_res_it_from.forward(); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 90 + 5 * word_index / word_count; + if (monitor->deadline_exceeded() || + (monitor->cancel != NULL && + (*monitor->cancel)(monitor->cancel_this, stats_.dict_words))) + return; + } + } + + if (!word_res_it_from.at_last()) { + word_res_it_to = word_res_it_from; + prevent_null_wd_fixsp = + word_res->word->cblob_list()->empty(); + if (check_debug_pt(word_res, 60)) + debug_fix_space_level.set_value(10); + word_res_it_to.forward(); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 90 + 5 * word_index / word_count; + if (monitor->deadline_exceeded() || + (monitor->cancel != NULL && + (*monitor->cancel)(monitor->cancel_this, stats_.dict_words))) + return; + } + while (!word_res_it_to.at_last() && + (word_res_it_to.data_relative(1)->word->flag(W_FUZZY_NON) || + word_res_it_to.data_relative(1)->word->flag(W_FUZZY_SP))) { + if (check_debug_pt(word_res, 60)) + debug_fix_space_level.set_value(10); + if (word_res->word->cblob_list()->empty()) + prevent_null_wd_fixsp = TRUE; + word_res = word_res_it_to.forward(); + } + if (check_debug_pt(word_res, 60)) + debug_fix_space_level.set_value(10); + if (word_res->word->cblob_list()->empty()) + prevent_null_wd_fixsp = TRUE; + if (prevent_null_wd_fixsp) { + word_res_it_from = word_res_it_to; + } + else { + fuzzy_space_words.assign_to_sublist(&word_res_it_from, + &word_res_it_to); + fix_fuzzy_space_list(fuzzy_space_words, + row_res_it.data()->row, + block_res_it.data()->block); + new_length = fuzzy_space_words.length(); + word_res_it_from.add_list_before(&fuzzy_space_words); + for (; + !word_res_it_from.at_last() && new_length > 0; + new_length--) { + word_res_it_from.forward(); + } + } + if (test_pt) + debug_fix_space_level.set_value(0); + } + fix_sp_fp_word(word_res_it_from, row_res_it.data()->row, + block_res_it.data()->block); + // Last word in row + } + } + } + } + + void Tesseract::fix_fuzzy_space_list(WERD_RES_LIST &best_perm, + ROW *row, + BLOCK* block) { + inT16 best_score; + WERD_RES_LIST current_perm; + inT16 current_score; + BOOL8 improved = FALSE; + + best_score = eval_word_spacing(best_perm); // default score + dump_words(best_perm, best_score, 1, improved); + + if (best_score != PERFECT_WERDS) + initialise_search(best_perm, current_perm); + + while ((best_score != PERFECT_WERDS) && !current_perm.empty()) { + match_current_words(current_perm, row, block); + current_score = eval_word_spacing(current_perm); + dump_words(current_perm, current_score, 2, improved); + if (current_score > best_score) { + best_perm.clear(); + best_perm.deep_copy(¤t_perm, &WERD_RES::deep_copy); + best_score = current_score; + improved = TRUE; + } + if (current_score < PERFECT_WERDS) + transform_to_next_perm(current_perm); + } + dump_words(best_perm, best_score, 3, improved); + } + +} // namespace tesseract + +void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list) { + WERD_RES_IT src_it(&src_list); + WERD_RES_IT new_it(&new_list); + WERD_RES *src_wd; + WERD_RES *new_wd; + + for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) { + src_wd = src_it.data(); + if (!src_wd->combination) { + new_wd = WERD_RES::deep_copy(src_wd); + new_wd->combination = FALSE; + new_wd->part_of_combo = FALSE; + new_it.add_after_then_move(new_wd); + } + } +} + + +namespace tesseract { + void Tesseract::match_current_words(WERD_RES_LIST &words, ROW *row, + BLOCK* block) { + WERD_RES_IT word_it(&words); + WERD_RES *word; + // Since we are not using PAGE_RES to iterate over words, we need to update + // prev_word_best_choice_ before calling classify_word_pass2(). + prev_word_best_choice_ = NULL; + for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { + word = word_it.data(); + if ((!word->part_of_combo) && (word->box_word == NULL)) { + WordData word_data(block, row, word); + SetupWordPassN(2, &word_data); + classify_word_and_language(2, NULL, &word_data); + } + prev_word_best_choice_ = word->best_choice; + } + } + + /** + * @name eval_word_spacing() + * The basic measure is the number of characters in contextually confirmed + * words. (I.e the word is done) + * If all words are contextually confirmed the evaluation is deemed perfect. + * + * Some fiddles are done to handle "1"s as these are VERY frequent causes of + * fuzzy spaces. The problem with the basic measure is that "561 63" would score + * the same as "56163", though given our knowledge that the space is fuzzy, and + * that there is a "1" next to the fuzzy space, we need to ensure that "56163" + * is preferred. + * + * The solution is to NOT COUNT the score of any word which has a digit at one + * end and a "1Il" as the character the other side of the space. + * + * Conversly, any character next to a "1" within a word is counted as a positive + * score. Thus "561 63" would score 4 (3 chars in a numeric word plus 1 side of + * the "1" joined). "56163" would score 7 - all chars in a numeric word + 2 + * sides of a "1" joined. + * + * The joined 1 rule is applied to any word REGARDLESS of contextual + * confirmation. Thus "PS7a71 3/7a" scores 1 (neither word is contexutally + * confirmed. The only score is from the joined 1. "PS7a713/7a" scores 2. + * + */ + inT16 Tesseract::eval_word_spacing(WERD_RES_LIST &word_res_list) { + WERD_RES_IT word_res_it(&word_res_list); + inT16 total_score = 0; + inT16 word_count = 0; + inT16 done_word_count = 0; + inT16 word_len; + inT16 i; + inT16 offset; + WERD_RES *word; // current word + inT16 prev_word_score = 0; + BOOL8 prev_word_done = FALSE; + BOOL8 prev_char_1 = FALSE; // prev ch a "1/I/l"? + BOOL8 prev_char_digit = FALSE; // prev ch 2..9 or 0 + BOOL8 current_char_1 = FALSE; + BOOL8 current_word_ok_so_far; + STRING punct_chars = "!\"`',.:;"; + BOOL8 prev_char_punct = FALSE; + BOOL8 current_char_punct = FALSE; + BOOL8 word_done = FALSE; + + do { + word = word_res_it.data(); + word_done = fixspace_thinks_word_done(word); + word_count++; + if (word->tess_failed) { + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + prev_word_score = 0; + prev_char_1 = FALSE; + prev_char_digit = FALSE; + prev_word_done = FALSE; + } + else { + /* + Can we add the prev word score and potentially count this word? + Yes IF it didn't end in a 1 when the first char of this word is a digit + AND it didn't end in a digit when the first char of this word is a 1 + */ + word_len = word->reject_map.length(); + current_word_ok_so_far = FALSE; + if (!((prev_char_1 && digit_or_numeric_punct(word, 0)) || + (prev_char_digit && ( + (word_done && + word->best_choice->unichar_lengths().string()[0] == 1 && + word->best_choice->unichar_string()[0] == '1') || + (!word_done && STRING(conflict_set_I_l_1).contains( + word->best_choice->unichar_string()[0])))))) { + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + current_word_ok_so_far = word_done; + } + + if (current_word_ok_so_far) { + prev_word_done = TRUE; + prev_word_score = word_len; + } + else { + prev_word_done = FALSE; + prev_word_score = 0; + } + + /* Add 1 to total score for every joined 1 regardless of context and + rejtn */ + for (i = 0, prev_char_1 = FALSE; i < word_len; i++) { + current_char_1 = word->best_choice->unichar_string()[i] == '1'; + if (prev_char_1 || (current_char_1 && (i > 0))) + total_score++; + prev_char_1 = current_char_1; + } + + /* Add 1 to total score for every joined punctuation regardless of context + and rejtn */ + if (tessedit_prefer_joined_punct) { + for (i = 0, offset = 0, prev_char_punct = FALSE; i < word_len; + offset += word->best_choice->unichar_lengths()[i++]) { + current_char_punct = + punct_chars.contains(word->best_choice->unichar_string()[offset]); + if (prev_char_punct || (current_char_punct && i > 0)) + total_score++; + prev_char_punct = current_char_punct; + } + } + prev_char_digit = digit_or_numeric_punct(word, word_len - 1); + for (i = 0, offset = 0; i < word_len - 1; + offset += word->best_choice->unichar_lengths()[i++]); + prev_char_1 = + ((word_done && (word->best_choice->unichar_string()[offset] == '1')) + || (!word_done && STRING(conflict_set_I_l_1).contains( + word->best_choice->unichar_string()[offset]))); + } + /* Find next word */ + do { + word_res_it.forward(); + } while (word_res_it.data()->part_of_combo); + } while (!word_res_it.at_first()); + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + if (done_word_count == word_count) + return PERFECT_WERDS; + else + return total_score; + } + + BOOL8 Tesseract::digit_or_numeric_punct(WERD_RES *word, int char_position) { + int i; + int offset; + + for (i = 0, offset = 0; i < char_position; + offset += word->best_choice->unichar_lengths()[i++]); + return ( + word->uch_set->get_isdigit( + word->best_choice->unichar_string().string() + offset, + word->best_choice->unichar_lengths()[i]) || + (word->best_choice->permuter() == NUMBER_PERM && + STRING(numeric_punctuation).contains( + word->best_choice->unichar_string().string()[offset]))); + } + +} // namespace tesseract + + +/** + * @name transform_to_next_perm() + * Examines the current word list to find the smallest word gap size. Then walks + * the word list closing any gaps of this size by either inserted new + * combination words, or extending existing ones. + * + * The routine COULD be limited to stop it building words longer than N blobs. + * + * If there are no more gaps then it DELETES the entire list and returns the + * empty list to cause termination. + */ +void transform_to_next_perm(WERD_RES_LIST &words) { + WERD_RES_IT word_it(&words); + WERD_RES_IT prev_word_it(&words); + WERD_RES *word; + WERD_RES *prev_word; + WERD_RES *combo; + WERD *copy_word; + inT16 prev_right = -MAX_INT16; + TBOX box; + inT16 gap; + inT16 min_gap = MAX_INT16; + + for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { + word = word_it.data(); + if (!word->part_of_combo) { + box = word->word->bounding_box(); + if (prev_right > -MAX_INT16) { + gap = box.left() - prev_right; + if (gap < min_gap) + min_gap = gap; + } + prev_right = box.right(); + } + } + if (min_gap < MAX_INT16) { + prev_right = -MAX_INT16; // back to start + word_it.set_to_list(&words); + // Note: we can't use cycle_pt due to inserted combos at start of list. + for (; (prev_right == -MAX_INT16) || !word_it.at_first(); + word_it.forward()) { + word = word_it.data(); + if (!word->part_of_combo) { + box = word->word->bounding_box(); + if (prev_right > -MAX_INT16) { + gap = box.left() - prev_right; + if (gap <= min_gap) { + prev_word = prev_word_it.data(); + if (prev_word->combination) { + combo = prev_word; + } + else { + /* Make a new combination and insert before + * the first word being joined. */ + copy_word = new WERD; + *copy_word = *(prev_word->word); + // deep copy + combo = new WERD_RES(copy_word); + combo->combination = TRUE; + combo->x_height = prev_word->x_height; + prev_word->part_of_combo = TRUE; + prev_word_it.add_before_then_move(combo); + } + combo->word->set_flag(W_EOL, word->word->flag(W_EOL)); + if (word->combination) { + combo->word->join_on(word->word); + // Move blobs to combo + // old combo no longer needed + delete word_it.extract(); + } + else { + // Copy current wd to combo + combo->copy_on(word); + word->part_of_combo = TRUE; + } + combo->done = FALSE; + combo->ClearResults(); + } + else { + prev_word_it = word_it; // catch up + } + } + prev_right = box.right(); + } + } + } + else { + words.clear(); // signal termination + } +} + +namespace tesseract { + void Tesseract::dump_words(WERD_RES_LIST &perm, inT16 score, + inT16 mode, BOOL8 improved) { + WERD_RES_IT word_res_it(&perm); + + if (debug_fix_space_level > 0) { + if (mode == 1) { + stats_.dump_words_str = ""; + for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); + word_res_it.forward()) { + if (!word_res_it.data()->part_of_combo) { + stats_.dump_words_str += + word_res_it.data()->best_choice->unichar_string(); + stats_.dump_words_str += ' '; + } + } + } + + if (debug_fix_space_level > 1) { + switch (mode) { + case 1: + tprintf("EXTRACTED (%d): \"", score); + break; + case 2: + tprintf("TESTED (%d): \"", score); + break; + case 3: + tprintf("RETURNED (%d): \"", score); + break; + } + + for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); + word_res_it.forward()) { + if (!word_res_it.data()->part_of_combo) { + tprintf("%s/%1d ", + word_res_it.data()->best_choice->unichar_string().string(), + (int)word_res_it.data()->best_choice->permuter()); + } + } + tprintf("\"\n"); + } + else if (improved) { + tprintf("FIX SPACING \"%s\" => \"", stats_.dump_words_str.string()); + for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); + word_res_it.forward()) { + if (!word_res_it.data()->part_of_combo) { + tprintf("%s/%1d ", + word_res_it.data()->best_choice->unichar_string().string(), + (int)word_res_it.data()->best_choice->permuter()); + } + } + tprintf("\"\n"); + } + } + } + + BOOL8 Tesseract::fixspace_thinks_word_done(WERD_RES *word) { + if (word->done) + return TRUE; + + /* + Use all the standard pass 2 conditions for mode 5 in set_done() in + reject.c BUT DON'T REJECT IF THE WERD IS AMBIGUOUS - FOR SPACING WE DON'T + CARE WHETHER WE HAVE of/at on/an etc. + */ + if (fixsp_done_mode > 0 && + (word->tess_accepted || + (fixsp_done_mode == 2 && word->reject_map.reject_count() == 0) || + fixsp_done_mode == 3) && + (strchr(word->best_choice->unichar_string().string(), ' ') == NULL) && + ((word->best_choice->permuter() == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter() == FREQ_DAWG_PERM) || + (word->best_choice->permuter() == USER_DAWG_PERM) || + (word->best_choice->permuter() == NUMBER_PERM))) { + return TRUE; + } + else { + return FALSE; + } + } + + + /** + * @name fix_sp_fp_word() + * Test the current word to see if it can be split by deleting noise blobs. If + * so, do the business. + * Return with the iterator pointing to the same place if the word is unchanged, + * or the last of the replacement words. + */ + void Tesseract::fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row, + BLOCK* block) { + WERD_RES *word_res; + WERD_RES_LIST sub_word_list; + WERD_RES_IT sub_word_list_it(&sub_word_list); + inT16 blob_index; + inT16 new_length; + float junk; + + word_res = word_res_it.data(); + if (word_res->word->flag(W_REP_CHAR) || + word_res->combination || + word_res->part_of_combo || + !word_res->word->flag(W_DONT_CHOP)) + return; + + blob_index = worst_noise_blob(word_res, &junk); + if (blob_index < 0) + return; + + if (debug_fix_space_level > 1) { + tprintf("FP fixspace working on \"%s\"\n", + word_res->best_choice->unichar_string().string()); + } + word_res->word->rej_cblob_list()->sort(c_blob_comparator); + sub_word_list_it.add_after_stay_put(word_res_it.extract()); + fix_noisy_space_list(sub_word_list, row, block); + new_length = sub_word_list.length(); + word_res_it.add_list_before(&sub_word_list); + for (; !word_res_it.at_last() && new_length > 1; new_length--) { + word_res_it.forward(); + } + } + + void Tesseract::fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, + BLOCK* block) { + inT16 best_score; + WERD_RES_IT best_perm_it(&best_perm); + WERD_RES_LIST current_perm; + WERD_RES_IT current_perm_it(¤t_perm); + WERD_RES *old_word_res; + inT16 current_score; + BOOL8 improved = FALSE; + + best_score = fp_eval_word_spacing(best_perm); // default score + + dump_words(best_perm, best_score, 1, improved); + + old_word_res = best_perm_it.data(); + // Even deep_copy doesn't copy the underlying WERD unless its combination + // flag is true!. + old_word_res->combination = TRUE; // Kludge to force deep copy + current_perm_it.add_to_end(WERD_RES::deep_copy(old_word_res)); + old_word_res->combination = FALSE; // Undo kludge + + break_noisiest_blob_word(current_perm); + + while (best_score != PERFECT_WERDS && !current_perm.empty()) { + match_current_words(current_perm, row, block); + current_score = fp_eval_word_spacing(current_perm); + dump_words(current_perm, current_score, 2, improved); + if (current_score > best_score) { + best_perm.clear(); + best_perm.deep_copy(¤t_perm, &WERD_RES::deep_copy); + best_score = current_score; + improved = TRUE; + } + if (current_score < PERFECT_WERDS) { + break_noisiest_blob_word(current_perm); + } + } + dump_words(best_perm, best_score, 3, improved); + } + + + /** + * break_noisiest_blob_word() + * Find the word with the blob which looks like the worst noise. + * Break the word into two, deleting the noise blob. + */ + void Tesseract::break_noisiest_blob_word(WERD_RES_LIST &words) { + WERD_RES_IT word_it(&words); + WERD_RES_IT worst_word_it; + float worst_noise_score = 9999; + int worst_blob_index = -1; // Noisiest blob of noisiest wd + int blob_index; // of wds noisiest blob + float noise_score; // of wds noisiest blob + WERD_RES *word_res; + C_BLOB_IT blob_it; + C_BLOB_IT rej_cblob_it; + C_BLOB_LIST new_blob_list; + C_BLOB_IT new_blob_it; + C_BLOB_IT new_rej_cblob_it; + WERD *new_word; + inT16 start_of_noise_blob; + inT16 i; + + for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { + blob_index = worst_noise_blob(word_it.data(), &noise_score); + if (blob_index > -1 && worst_noise_score > noise_score) { + worst_noise_score = noise_score; + worst_blob_index = blob_index; + worst_word_it = word_it; + } + } + if (worst_blob_index < 0) { + words.clear(); // signal termination + return; + } + + /* Now split the worst_word_it */ + + word_res = worst_word_it.data(); + + /* Move blobs before noise blob to a new bloblist */ + + new_blob_it.set_to_list(&new_blob_list); + blob_it.set_to_list(word_res->word->cblob_list()); + for (i = 0; i < worst_blob_index; i++, blob_it.forward()) { + new_blob_it.add_after_then_move(blob_it.extract()); + } + start_of_noise_blob = blob_it.data()->bounding_box().left(); + delete blob_it.extract(); // throw out noise blob + + new_word = new WERD(&new_blob_list, word_res->word); + new_word->set_flag(W_EOL, FALSE); + word_res->word->set_flag(W_BOL, FALSE); + word_res->word->set_blanks(1); // After break + + new_rej_cblob_it.set_to_list(new_word->rej_cblob_list()); + rej_cblob_it.set_to_list(word_res->word->rej_cblob_list()); + for (; + (!rej_cblob_it.empty() && + (rej_cblob_it.data()->bounding_box().left() < start_of_noise_blob)); + rej_cblob_it.forward()) { + new_rej_cblob_it.add_after_then_move(rej_cblob_it.extract()); + } + + WERD_RES* new_word_res = new WERD_RES(new_word); + new_word_res->combination = TRUE; + worst_word_it.add_before_then_move(new_word_res); + + word_res->ClearResults(); + } + + inT16 Tesseract::worst_noise_blob(WERD_RES *word_res, + float *worst_noise_score) { + float noise_score[512]; + int i; + int min_noise_blob; // 1st contender + int max_noise_blob; // last contender + int non_noise_count; + int worst_noise_blob; // Worst blob + float small_limit = kBlnXHeight * fixsp_small_outlines_size; + float non_noise_limit = kBlnXHeight * 0.8; + + if (word_res->rebuild_word == NULL) + return -1; // Can't handle cube words. + + // Normalised. + int blob_count = word_res->box_word->length(); + ASSERT_HOST(blob_count <= 512); + if (blob_count < 5) + return -1; // too short to split + + /* Get the noise scores for all blobs */ + +#ifndef SECURE_NAMES + if (debug_fix_space_level > 5) + tprintf("FP fixspace Noise metrics for \"%s\": ", + word_res->best_choice->unichar_string().string()); +#endif + + for (i = 0; i < blob_count && i < word_res->rebuild_word->NumBlobs(); i++) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; + if (word_res->reject_map[i].accepted()) + noise_score[i] = non_noise_limit; + else + noise_score[i] = blob_noise_score(blob); + + if (debug_fix_space_level > 5) + tprintf("%1.1f ", noise_score[i]); + } + if (debug_fix_space_level > 5) + tprintf("\n"); + + /* Now find the worst one which is far enough away from the end of the word */ + + non_noise_count = 0; + for (i = 0; i < blob_count && non_noise_count < fixsp_non_noise_limit; i++) { + if (noise_score[i] >= non_noise_limit) { + non_noise_count++; + } + } + if (non_noise_count < fixsp_non_noise_limit) + return -1; + + min_noise_blob = i; + + non_noise_count = 0; + for (i = blob_count - 1; i >= 0 && non_noise_count < fixsp_non_noise_limit; + i--) { + if (noise_score[i] >= non_noise_limit) { + non_noise_count++; + } + } + if (non_noise_count < fixsp_non_noise_limit) + return -1; + + max_noise_blob = i; + + if (min_noise_blob > max_noise_blob) + return -1; + + *worst_noise_score = small_limit; + worst_noise_blob = -1; + for (i = min_noise_blob; i <= max_noise_blob; i++) { + if (noise_score[i] < *worst_noise_score) { + worst_noise_blob = i; + *worst_noise_score = noise_score[i]; + } + } + return worst_noise_blob; + } + + float Tesseract::blob_noise_score(TBLOB *blob) { + TBOX box; // BB of outline + inT16 outline_count = 0; + inT16 max_dimension; + inT16 largest_outline_dimension = 0; + + for (TESSLINE* ol = blob->outlines; ol != NULL; ol = ol->next) { + outline_count++; + box = ol->bounding_box(); + if (box.height() > box.width()) { + max_dimension = box.height(); + } + else { + max_dimension = box.width(); + } + + if (largest_outline_dimension < max_dimension) + largest_outline_dimension = max_dimension; + } + + if (outline_count > 5) { + // penalise LOTS of blobs + largest_outline_dimension *= 2; + } + + box = blob->bounding_box(); + if (box.bottom() > kBlnBaselineOffset * 4 || + box.top() < kBlnBaselineOffset / 2) { + // Lax blob is if high or low + largest_outline_dimension /= 2; + } + + return largest_outline_dimension; + } +} // namespace tesseract + +void fixspace_dbg(WERD_RES *word) { + TBOX box = word->word->bounding_box(); + BOOL8 show_map_detail = FALSE; + inT16 i; + + box.print(); + tprintf(" \"%s\" ", word->best_choice->unichar_string().string()); + tprintf("Blob count: %d (word); %d/%d (rebuild word)\n", + word->word->cblob_list()->length(), + word->rebuild_word->NumBlobs(), + word->box_word->length()); + word->reject_map.print(debug_fp); + tprintf("\n"); + if (show_map_detail) { + tprintf("\"%s\"\n", word->best_choice->unichar_string().string()); + for (i = 0; word->best_choice->unichar_string()[i] != '\0'; i++) { + tprintf("**** \"%c\" ****\n", word->best_choice->unichar_string()[i]); + word->reject_map[i].full_print(debug_fp); + } + } + + tprintf("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); + tprintf("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); +} + + +/** + * fp_eval_word_spacing() + * Evaluation function for fixed pitch word lists. + * + * Basically, count the number of "nice" characters - those which are in tess + * acceptable words or in dict words and are not rejected. + * Penalise any potential noise chars + */ +namespace tesseract { + inT16 Tesseract::fp_eval_word_spacing(WERD_RES_LIST &word_res_list) { + WERD_RES_IT word_it(&word_res_list); + WERD_RES *word; + inT16 score = 0; + inT16 i; + float small_limit = kBlnXHeight * fixsp_small_outlines_size; + + for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { + word = word_it.data(); + if (word->rebuild_word == NULL) + continue; // Can't handle cube words. + if (word->done || + word->tess_accepted || + word->best_choice->permuter() == SYSTEM_DAWG_PERM || + word->best_choice->permuter() == FREQ_DAWG_PERM || + word->best_choice->permuter() == USER_DAWG_PERM || + safe_dict_word(word) > 0) { + int num_blobs = word->rebuild_word->NumBlobs(); + UNICHAR_ID space = word->uch_set->unichar_to_id(" "); + for (i = 0; i < word->best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word->rebuild_word->blobs[i]; + if (word->best_choice->unichar_id(i) == space || + blob_noise_score(blob) < small_limit) { + score -= 1; // penalise possibly erroneous non-space + } + else if (word->reject_map[i].accepted()) { + score++; + } + } + } + } + if (score < 0) + score = 0; + return score; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.h b/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.h new file mode 100644 index 0000000..48f2d4f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/fixspace.h @@ -0,0 +1,31 @@ +/****************************************************************** + * File: fixspace.h (Formerly fixspace.h) + * Description: Implements a pass over the page res, exploring the alternative + * spacing possibilities, trying to use context to improve the + word spacing +* Author: Phil Cheatle +* Created: Thu Oct 21 11:38:43 BST 1993 +* +* (C) Copyright 1993, Hewlett-Packard Ltd. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +* +**********************************************************************/ + +#ifndef FIXSPACE_H +#define FIXSPACE_H + +#include "pageres.h" +#include "params.h" + +void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list); +void transform_to_next_perm(WERD_RES_LIST &words); +void fixspace_dbg(WERD_RES *word); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/fixxht.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/fixxht.cpp new file mode 100644 index 0000000..658680e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/fixxht.cpp @@ -0,0 +1,216 @@ +/********************************************************************** + * File: fixxht.cpp (Formerly fixxht.c) + * Description: Improve x_ht and look out for case inconsistencies + * Author: Phil Cheatle + * Created: Thu Aug 5 14:11:08 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include +#include "params.h" +#include "float2int.h" +#include "tesseractclass.h" + +namespace tesseract { + + // Fixxht overview. + // Premise: Initial estimate of x-height is adequate most of the time, but + // occasionally it is incorrect. Most notable causes of failure are: + // 1. Small caps, where the top of the caps is the same as the body text + // xheight. For small caps words the xheight needs to be reduced to correctly + // recognize the caps in the small caps word. + // 2. All xheight lines, such as summer. Here the initial estimate will have + // guessed that the blob tops are caps and will have placed the xheight too low. + // 3. Noise/logos beside words, or changes in font size on a line. Such + // things can blow the statistics and cause an incorrect estimate. + // 4. Incorrect baseline. Can happen when 2 columns are incorrectly merged. + // In this case the x-height is often still correct. + // + // Algorithm. + // Compare the vertical position (top only) of alphnumerics in a word with + // the range of positions in training data (in the unicharset). + // See CountMisfitTops. If any characters disagree sufficiently with the + // initial xheight estimate, then recalculate the xheight, re-run OCR on + // the word, and if the number of vertical misfits goes down, along with + // either the word rating or certainty, then keep the new xheight. + // The new xheight is calculated as follows:ComputeCompatibleXHeight + // For each alphanumeric character that has a vertically misplaced top + // (a misfit), yet its bottom is within the acceptable range (ie it is not + // likely a sub-or super-script) calculate the range of acceptable xheight + // positions from its range of tops, and give each value in the range a + // number of votes equal to the distance of its top from its acceptance range. + // The x-height position with the median of the votes becomes the new + // x-height. This assumes that most characters will be correctly recognized + // even if the x-height is incorrect. This is not a terrible assumption, but + // it is not great. An improvement would be to use a classifier that does + // not care about vertical position or scaling at all. + // Separately collect stats on shifted baselines and apply the same logic to + // computing a best-fit shift to fix the error. If the baseline needs to be + // shifted, but the x-height is OK, returns the original x-height along with + // the baseline shift to indicate that recognition needs to re-run. + + // If the max-min top of a unicharset char is bigger than kMaxCharTopRange + // then the char top cannot be used to judge misfits or suggest a new top. + const int kMaxCharTopRange = 48; + + // Returns the number of misfit blob tops in this word. + int Tesseract::CountMisfitTops(WERD_RES *word_res) { + int bad_blobs = 0; + int num_blobs = word_res->rebuild_word->NumBlobs(); + for (int blob_id = 0; blob_id < num_blobs; ++blob_id) { + TBLOB* blob = word_res->rebuild_word->blobs[blob_id]; + UNICHAR_ID class_id = word_res->best_choice->unichar_id(blob_id); + if (unicharset.get_isalpha(class_id) || unicharset.get_isdigit(class_id)) { + int top = blob->bounding_box().top(); + if (top >= INT_FEAT_RANGE) + top = INT_FEAT_RANGE - 1; + int min_bottom, max_bottom, min_top, max_top; + unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, + &min_top, &max_top); + if (max_top - min_top > kMaxCharTopRange) + continue; + bool bad = top < min_top - x_ht_acceptance_tolerance || + top > max_top + x_ht_acceptance_tolerance; + if (bad) + ++bad_blobs; + if (debug_x_ht_level >= 1) { + tprintf("Class %s is %s with top %d vs limits of %d->%d, +/-%d\n", + unicharset.id_to_unichar(class_id), + bad ? "Misfit" : "OK", top, min_top, max_top, + static_cast(x_ht_acceptance_tolerance)); + } + } + } + return bad_blobs; + } + + // Returns a new x-height maximally compatible with the result in word_res. + // See comment above for overall algorithm. + float Tesseract::ComputeCompatibleXheight(WERD_RES *word_res, + float* baseline_shift) { + STATS top_stats(0, MAX_UINT8); + STATS shift_stats(-MAX_UINT8, MAX_UINT8); + int bottom_shift = 0; + int num_blobs = word_res->rebuild_word->NumBlobs(); + do { + top_stats.clear(); + shift_stats.clear(); + for (int blob_id = 0; blob_id < num_blobs; ++blob_id) { + TBLOB* blob = word_res->rebuild_word->blobs[blob_id]; + UNICHAR_ID class_id = word_res->best_choice->unichar_id(blob_id); + if (unicharset.get_isalpha(class_id) || + unicharset.get_isdigit(class_id)) { + int top = blob->bounding_box().top() + bottom_shift; + // Clip the top to the limit of normalized feature space. + if (top >= INT_FEAT_RANGE) + top = INT_FEAT_RANGE - 1; + int bottom = blob->bounding_box().bottom() + bottom_shift; + int min_bottom, max_bottom, min_top, max_top; + unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, + &min_top, &max_top); + // Chars with a wild top range would mess up the result so ignore them. + if (max_top - min_top > kMaxCharTopRange) + continue; + int misfit_dist = MAX((min_top - x_ht_acceptance_tolerance) - top, + top - (max_top + x_ht_acceptance_tolerance)); + int height = top - kBlnBaselineOffset; + if (debug_x_ht_level >= 2) { + tprintf("Class %s: height=%d, bottom=%d,%d top=%d,%d, actual=%d,%d: ", + unicharset.id_to_unichar(class_id), + height, min_bottom, max_bottom, min_top, max_top, + bottom, top); + } + // Use only chars that fit in the expected bottom range, and where + // the range of tops is sensibly near the xheight. + if (min_bottom <= bottom + x_ht_acceptance_tolerance && + bottom - x_ht_acceptance_tolerance <= max_bottom && + min_top > kBlnBaselineOffset && + max_top - kBlnBaselineOffset >= kBlnXHeight && + misfit_dist > 0) { + // Compute the x-height position using proportionality between the + // actual height and expected height. + int min_xht = DivRounded(height * kBlnXHeight, + max_top - kBlnBaselineOffset); + int max_xht = DivRounded(height * kBlnXHeight, + min_top - kBlnBaselineOffset); + if (debug_x_ht_level >= 2) { + tprintf(" xht range min=%d, max=%d\n", min_xht, max_xht); + } + // The range of expected heights gets a vote equal to the distance + // of the actual top from the expected top. + for (int y = min_xht; y <= max_xht; ++y) + top_stats.add(y, misfit_dist); + } + else if ((min_bottom > bottom + x_ht_acceptance_tolerance || + bottom - x_ht_acceptance_tolerance > max_bottom) && + bottom_shift == 0) { + // Get the range of required bottom shift. + int min_shift = min_bottom - bottom; + int max_shift = max_bottom - bottom; + if (debug_x_ht_level >= 2) { + tprintf(" bottom shift min=%d, max=%d\n", min_shift, max_shift); + } + // The range of expected shifts gets a vote equal to the min distance + // of the actual bottom from the expected bottom, spread over the + // range of its acceptance. + int misfit_weight = abs(min_shift); + if (max_shift > min_shift) + misfit_weight /= max_shift - min_shift; + for (int y = min_shift; y <= max_shift; ++y) + shift_stats.add(y, misfit_weight); + } + else { + if (bottom_shift == 0) { + // Things with bottoms that are already ok need to say so, on the + // 1st iteration only. + shift_stats.add(0, kBlnBaselineOffset); + } + if (debug_x_ht_level >= 2) { + tprintf(" already OK\n"); + } + } + } + } + if (shift_stats.get_total() > top_stats.get_total()) { + bottom_shift = IntCastRounded(shift_stats.median()); + if (debug_x_ht_level >= 2) { + tprintf("Applying bottom shift=%d\n", bottom_shift); + } + } + } while (bottom_shift != 0 && + top_stats.get_total() < shift_stats.get_total()); + // Baseline shift is opposite sign to the bottom shift. + *baseline_shift = -bottom_shift / word_res->denorm.y_scale(); + if (debug_x_ht_level >= 2) { + tprintf("baseline shift=%g\n", *baseline_shift); + } + if (top_stats.get_total() == 0) + return bottom_shift != 0 ? word_res->x_height : 0.0f; + // The new xheight is just the median vote, which is then scaled out + // of BLN space back to pixel space to get the x-height in pixel space. + float new_xht = top_stats.median(); + if (debug_x_ht_level >= 2) { + tprintf("Median xht=%f\n", new_xht); + tprintf("Mode20:A: New x-height = %f (norm), %f (orig)\n", + new_xht, new_xht / word_res->denorm.y_scale()); + } + // The xheight must change by at least x_ht_min_change to be used. + if (fabs(new_xht - kBlnXHeight) >= x_ht_min_change) + return new_xht / word_res->denorm.y_scale(); + else + return bottom_shift != 0 ? word_res->x_height : 0.0f; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.cpp new file mode 100644 index 0000000..f2e59b9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.cpp @@ -0,0 +1,390 @@ +/////////////////////////////////////////////////////////////////////// +// File: ltrresultiterator.cpp +// Description: Iterator for tesseract results in strict left-to-right +// order 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 "ltrresultiterator.h" + +#include "allheaders.h" +#include "pageres.h" +#include "strngs.h" +#include "tesseractclass.h" + +namespace tesseract { + + LTRResultIterator::LTRResultIterator(PAGE_RES* page_res, Tesseract* tesseract, + int scale, int scaled_yres, + int rect_left, int rect_top, + int rect_width, int rect_height) + : PageIterator(page_res, tesseract, scale, scaled_yres, + rect_left, rect_top, rect_width, rect_height), + line_separator_("\n"), + paragraph_separator_("\n") { + } + + LTRResultIterator::~LTRResultIterator() { + } + + // Returns the null terminated UTF-8 encoded text string for the current + // object at the given level. Use delete [] to free after use. + char* LTRResultIterator::GetUTF8Text(PageIteratorLevel level) const { + if (it_->word() == NULL) return NULL; // Already at the end! + STRING text; + PAGE_RES_IT res_it(*it_); + WERD_CHOICE* best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + if (level == RIL_SYMBOL) { + text = res_it.word()->BestUTF8(blob_index_, false); + } + else if (level == RIL_WORD) { + text = best_choice->unichar_string(); + } + else { + bool eol = false; // end of line? + bool eop = false; // end of paragraph? + do { // for each paragraph in a block + do { // for each text line in a paragraph + do { // for each word in a text line + best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + text += best_choice->unichar_string(); + text += " "; + res_it.forward(); + eol = res_it.row() != res_it.prev_row(); + } while (!eol); + text.truncate_at(text.length() - 1); + text += line_separator_; + eop = res_it.block() != res_it.prev_block() || + res_it.row()->row->para() != res_it.prev_row()->row->para(); + } while (level != RIL_TEXTLINE && !eop); + if (eop) text += paragraph_separator_; + } while (level == RIL_BLOCK && res_it.block() == res_it.prev_block()); + } + int length = text.length() + 1; + char* result = new char[length]; + strncpy(result, text.string(), length); + return result; + } + + // Set the string inserted at the end of each text line. "\n" by default. + void LTRResultIterator::SetLineSeparator(const char *new_line) { + line_separator_ = new_line; + } + + // Set the string inserted at the end of each paragraph. "\n" by default. + void LTRResultIterator::SetParagraphSeparator(const char *new_para) { + paragraph_separator_ = new_para; + } + + // Returns the mean confidence of the current object at the given level. + // The number should be interpreted as a percent probability. (0.0f-100.0f) + float LTRResultIterator::Confidence(PageIteratorLevel level) const { + if (it_->word() == NULL) return 0.0f; // Already at the end! + float mean_certainty = 0.0f; + int certainty_count = 0; + PAGE_RES_IT res_it(*it_); + WERD_CHOICE* best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + switch (level) { + case RIL_BLOCK: + do { + best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + mean_certainty += best_choice->certainty(); + ++certainty_count; + res_it.forward(); + } while (res_it.block() == res_it.prev_block()); + break; + case RIL_PARA: + do { + best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + mean_certainty += best_choice->certainty(); + ++certainty_count; + res_it.forward(); + } while (res_it.block() == res_it.prev_block() && + res_it.row()->row->para() == res_it.prev_row()->row->para()); + break; + case RIL_TEXTLINE: + do { + best_choice = res_it.word()->best_choice; + ASSERT_HOST(best_choice != NULL); + mean_certainty += best_choice->certainty(); + ++certainty_count; + res_it.forward(); + } while (res_it.row() == res_it.prev_row()); + break; + case RIL_WORD: + mean_certainty += best_choice->certainty(); + ++certainty_count; + break; + case RIL_SYMBOL: + mean_certainty += best_choice->certainty(blob_index_); + ++certainty_count; + } + if (certainty_count > 0) { + mean_certainty /= certainty_count; + float confidence = 100 + 5 * mean_certainty; + if (confidence < 0.0f) confidence = 0.0f; + if (confidence > 100.0f) confidence = 100.0f; + return confidence; + } + return 0.0f; + } + + void LTRResultIterator::RowAttributes(float* row_height, float* descenders, + float* ascenders) const { + *row_height = it_->row()->row->x_height() + it_->row()->row->ascenders() - + it_->row()->row->descenders(); + *descenders = it_->row()->row->descenders(); + *ascenders = it_->row()->row->ascenders(); + } + + // Returns the font attributes of the current word. If iterating at a higher + // level object than words, eg textlines, then this will return the + // attributes of the first word in that textline. + // The actual return value is a string representing a font name. It points + // to an internal table and SHOULD NOT BE DELETED. Lifespan is the same as + // the iterator itself, ie rendered invalid by various members of + // TessBaseAPI, including Init, SetImage, End or deleting the TessBaseAPI. + // Pointsize is returned in printers points (1/72 inch.) + const char* LTRResultIterator::WordFontAttributes(bool* is_bold, + bool* is_italic, + bool* is_underlined, + bool* is_monospace, + bool* is_serif, + bool* is_smallcaps, + int* pointsize, + int* font_id) const { + if (it_->word() == NULL) return NULL; // Already at the end! + if (it_->word()->fontinfo == NULL) { + *font_id = -1; + return NULL; // No font information. + } + const FontInfo& font_info = *it_->word()->fontinfo; + *font_id = font_info.universal_id; + *is_bold = font_info.is_bold(); + *is_italic = font_info.is_italic(); + *is_underlined = false; // TODO(rays) fix this! + *is_monospace = font_info.is_fixed_pitch(); + *is_serif = font_info.is_serif(); + *is_smallcaps = it_->word()->small_caps; + float row_height = it_->row()->row->x_height() + + it_->row()->row->ascenders() - it_->row()->row->descenders(); + // Convert from pixels to printers points. + *pointsize = scaled_yres_ > 0 + ? static_cast(row_height * kPointsPerInch / scaled_yres_ + 0.5) + : 0; + + return font_info.name; + } + + // Returns the name of the language used to recognize this word. + const char* LTRResultIterator::WordRecognitionLanguage() const { + if (it_->word() == NULL || it_->word()->tesseract == NULL) return NULL; + return it_->word()->tesseract->lang.string(); + } + + // Return the overall directionality of this word. + StrongScriptDirection LTRResultIterator::WordDirection() const { + if (it_->word() == NULL) return DIR_NEUTRAL; + bool has_rtl = it_->word()->AnyRtlCharsInWord(); + bool has_ltr = it_->word()->AnyLtrCharsInWord(); + if (has_rtl && !has_ltr) + return DIR_RIGHT_TO_LEFT; + if (has_ltr && !has_rtl) + return DIR_LEFT_TO_RIGHT; + if (!has_ltr && !has_rtl) + return DIR_NEUTRAL; + return DIR_MIX; + } + + // Returns true if the current word was found in a dictionary. + bool LTRResultIterator::WordIsFromDictionary() const { + if (it_->word() == NULL) return false; // Already at the end! + int permuter = it_->word()->best_choice->permuter(); + return permuter == SYSTEM_DAWG_PERM || permuter == FREQ_DAWG_PERM || + permuter == USER_DAWG_PERM; + } + + // Returns true if the current word is numeric. + bool LTRResultIterator::WordIsNumeric() const { + if (it_->word() == NULL) return false; // Already at the end! + int permuter = it_->word()->best_choice->permuter(); + return permuter == NUMBER_PERM; + } + + // Returns true if the word contains blamer information. + bool LTRResultIterator::HasBlamerInfo() const { + return it_->word() != NULL && it_->word()->blamer_bundle != NULL && + it_->word()->blamer_bundle->HasDebugInfo(); + } + + // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle + // of the current word. + const void *LTRResultIterator::GetParamsTrainingBundle() const { + return (it_->word() != NULL && it_->word()->blamer_bundle != NULL) ? + &(it_->word()->blamer_bundle->params_training_bundle()) : NULL; + } + + // Returns the pointer to the string with blamer information for this word. + // Assumes that the word's blamer_bundle is not NULL. + const char *LTRResultIterator::GetBlamerDebug() const { + return it_->word()->blamer_bundle->debug().string(); + } + + // Returns the pointer to the string with misadaption information for this word. + // Assumes that the word's blamer_bundle is not NULL. + const char *LTRResultIterator::GetBlamerMisadaptionDebug() const { + return it_->word()->blamer_bundle->misadaption_debug().string(); + } + + // Returns true if a truth string was recorded for the current word. + bool LTRResultIterator::HasTruthString() const { + if (it_->word() == NULL) return false; // Already at the end! + if (it_->word()->blamer_bundle == NULL || + it_->word()->blamer_bundle->NoTruth()) { + return false; // no truth information for this word + } + return true; + } + + // Returns true if the given string is equivalent to the truth string for + // the current word. + bool LTRResultIterator::EquivalentToTruth(const char *str) const { + if (!HasTruthString()) return false; + ASSERT_HOST(it_->word()->uch_set != NULL); + WERD_CHOICE str_wd(str, *(it_->word()->uch_set)); + return it_->word()->blamer_bundle->ChoiceIsCorrect(&str_wd); + } + + // Returns the null terminated UTF-8 encoded truth string for the current word. + // Use delete [] to free after use. + char* LTRResultIterator::WordTruthUTF8Text() const { + if (!HasTruthString()) return NULL; + STRING truth_text = it_->word()->blamer_bundle->TruthString(); + int length = truth_text.length() + 1; + char* result = new char[length]; + strncpy(result, truth_text.string(), length); + return result; + } + + // Returns the null terminated UTF-8 encoded normalized OCR string for the + // current word. Use delete [] to free after use. + char* LTRResultIterator::WordNormedUTF8Text() const { + if (it_->word() == NULL) return NULL; // Already at the end! + STRING ocr_text; + WERD_CHOICE* best_choice = it_->word()->best_choice; + const UNICHARSET *unicharset = it_->word()->uch_set; + ASSERT_HOST(best_choice != NULL); + for (int i = 0; i < best_choice->length(); ++i) { + ocr_text += unicharset->get_normed_unichar(best_choice->unichar_id(i)); + } + int length = ocr_text.length() + 1; + char* result = new char[length]; + strncpy(result, ocr_text.string(), length); + return result; + } + + // Returns a pointer to serialized choice lattice. + // Fills lattice_size with the number of bytes in lattice data. + const char *LTRResultIterator::WordLattice(int *lattice_size) const { + if (it_->word() == NULL) return NULL; // Already at the end! + if (it_->word()->blamer_bundle == NULL) return NULL; + *lattice_size = it_->word()->blamer_bundle->lattice_size(); + return it_->word()->blamer_bundle->lattice_data(); + } + + // Returns true if the current symbol is a superscript. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool LTRResultIterator::SymbolIsSuperscript() const { + if (cblob_it_ == NULL && it_->word() != NULL) + return it_->word()->best_choice->BlobPosition(blob_index_) == + SP_SUPERSCRIPT; + return false; + } + + // Returns true if the current symbol is a subscript. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool LTRResultIterator::SymbolIsSubscript() const { + if (cblob_it_ == NULL && it_->word() != NULL) + return it_->word()->best_choice->BlobPosition(blob_index_) == SP_SUBSCRIPT; + return false; + } + + // Returns true if the current symbol is a dropcap. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool LTRResultIterator::SymbolIsDropcap() const { + if (cblob_it_ == NULL && it_->word() != NULL) + return it_->word()->best_choice->BlobPosition(blob_index_) == SP_DROPCAP; + return false; + } + + ChoiceIterator::ChoiceIterator(const LTRResultIterator& result_it) { + ASSERT_HOST(result_it.it_->word() != NULL); + word_res_ = result_it.it_->word(); + BLOB_CHOICE_LIST* choices = NULL; + if (word_res_->ratings != NULL) + choices = word_res_->GetBlobChoices(result_it.blob_index_); + if (choices != NULL && !choices->empty()) { + choice_it_ = new BLOB_CHOICE_IT(choices); + choice_it_->mark_cycle_pt(); + } + else { + choice_it_ = NULL; + } + } + + ChoiceIterator::~ChoiceIterator() { + delete choice_it_; + } + + // Moves to the next choice for the symbol and returns false if there + // are none left. + bool ChoiceIterator::Next() { + if (choice_it_ == NULL) + return false; + choice_it_->forward(); + return !choice_it_->cycled_list(); + } + + // Returns the null terminated UTF-8 encoded text string for the current + // choice. Do NOT use delete [] to free after use. + const char* ChoiceIterator::GetUTF8Text() const { + if (choice_it_ == NULL) + return NULL; + UNICHAR_ID id = choice_it_->data()->unichar_id(); + return word_res_->uch_set->id_to_unichar_ext(id); + } + + // Returns the confidence of the current choice. + // The number should be interpreted as a percent probability. (0.0f-100.0f) + float ChoiceIterator::Confidence() const { + if (choice_it_ == NULL) + return 0.0f; + float confidence = 100 + 5 * choice_it_->data()->certainty(); + if (confidence < 0.0f) confidence = 0.0f; + if (confidence > 100.0f) confidence = 100.0f; + return confidence; + } + + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.h b/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.h new file mode 100644 index 0000000..28d0e56 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/ltrresultiterator.h @@ -0,0 +1,218 @@ +/////////////////////////////////////////////////////////////////////// +// File: ltrresultiterator.h +// Description: Iterator for tesseract results in strict left-to-right +// order that avoids using tesseract internal data structures. +// Author: Ray Smith +// Created: Fri Feb 26 11:01:06 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_LTR_RESULT_ITERATOR_H__ +#define TESSERACT_CCMAIN_LTR_RESULT_ITERATOR_H__ + +#include "platform.h" +#include "pageiterator.h" +#include "unichar.h" + +class BLOB_CHOICE_IT; +class WERD_RES; + +namespace tesseract { + + class Tesseract; + + // Class to iterate over tesseract results, providing access to all levels + // of the page hierarchy, without including any tesseract headers or having + // to handle any tesseract structures. + // WARNING! This class points to data held within the TessBaseAPI class, and + // therefore can only be used while the TessBaseAPI class still exists and + // has not been subjected to a call of Init, SetImage, Recognize, Clear, End + // DetectOS, or anything else that changes the internal PAGE_RES. + // See apitypes.h for the definition of PageIteratorLevel. + // See also base class PageIterator, which contains the bulk of the interface. + // LTRResultIterator adds text-specific methods for access to OCR output. + + class TESS_API LTRResultIterator : public PageIterator { + friend class ChoiceIterator; + public: + // page_res and tesseract come directly from the BaseAPI. + // The rectangle parameters are copied indirectly from the Thresholder, + // via the BaseAPI. They represent the coordinates of some rectangle in an + // original image (in top-left-origin coordinates) and therefore the top-left + // needs to be added to any output boxes in order to specify coordinates + // in the original image. See TessBaseAPI::SetRectangle. + // The scale and scaled_yres are in case the Thresholder scaled the image + // rectangle prior to thresholding. Any coordinates in tesseract's image + // must be divided by scale before adding (rect_left, rect_top). + // The scaled_yres indicates the effective resolution of the binary image + // that tesseract has been given by the Thresholder. + // After the constructor, Begin has already been called. + LTRResultIterator(PAGE_RES* page_res, Tesseract* tesseract, + int scale, int scaled_yres, + int rect_left, int rect_top, + int rect_width, int rect_height); + virtual ~LTRResultIterator(); + + // LTRResultIterators 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. These constructors DO NOT CALL Begin, so + // iterations will continue from the location of src. + // TODO: For now the copy constructor and operator= only need the base class + // versions, but if new data members are added, don't forget to add them! + + // ============= Moving around within the page ============. + + // See PageIterator. + + // ============= Accessing data ==============. + + // Returns the null terminated UTF-8 encoded text string for the current + // object at the given level. Use delete [] to free after use. + char* GetUTF8Text(PageIteratorLevel level) const; + + // Set the string inserted at the end of each text line. "\n" by default. + void SetLineSeparator(const char *new_line); + + // Set the string inserted at the end of each paragraph. "\n" by default. + void SetParagraphSeparator(const char *new_para); + + // Returns the mean confidence of the current object at the given level. + // The number should be interpreted as a percent probability. (0.0f-100.0f) + float Confidence(PageIteratorLevel level) const; + + // Returns the attributes of the current row. + void RowAttributes(float* row_height, float* descenders, + float* ascenders) const; + + // ============= Functions that refer to words only ============. + + // Returns the font attributes of the current word. If iterating at a higher + // level object than words, eg textlines, then this will return the + // attributes of the first word in that textline. + // The actual return value is a string representing a font name. It points + // to an internal table and SHOULD NOT BE DELETED. Lifespan is the same as + // the iterator itself, ie rendered invalid by various members of + // TessBaseAPI, including Init, SetImage, End or deleting the TessBaseAPI. + // Pointsize is returned in printers points (1/72 inch.) + const char* WordFontAttributes(bool* is_bold, + bool* is_italic, + bool* is_underlined, + bool* is_monospace, + bool* is_serif, + bool* is_smallcaps, + int* pointsize, + int* font_id) const; + + // Return the name of the language used to recognize this word. + // On error, NULL. Do not delete this pointer. + const char* WordRecognitionLanguage() const; + + // Return the overall directionality of this word. + StrongScriptDirection WordDirection() const; + + // Returns true if the current word was found in a dictionary. + bool WordIsFromDictionary() const; + + // Returns true if the current word is numeric. + bool WordIsNumeric() const; + + // Returns true if the word contains blamer information. + bool HasBlamerInfo() const; + + // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle + // of the current word. + const void *GetParamsTrainingBundle() const; + + // Returns a pointer to the string with blamer information for this word. + // Assumes that the word's blamer_bundle is not NULL. + const char *GetBlamerDebug() const; + + // Returns a pointer to the string with misadaption information for this word. + // Assumes that the word's blamer_bundle is not NULL. + const char *GetBlamerMisadaptionDebug() const; + + // Returns true if a truth string was recorded for the current word. + bool HasTruthString() const; + + // Returns true if the given string is equivalent to the truth string for + // the current word. + bool EquivalentToTruth(const char *str) const; + + // Returns a null terminated UTF-8 encoded truth string for the current word. + // Use delete [] to free after use. + char* WordTruthUTF8Text() const; + + // Returns a null terminated UTF-8 encoded normalized OCR string for the + // current word. Use delete [] to free after use. + char* WordNormedUTF8Text() const; + + // Returns a pointer to serialized choice lattice. + // Fills lattice_size with the number of bytes in lattice data. + const char *WordLattice(int *lattice_size) const; + + // ============= Functions that refer to symbols only ============. + + // Returns true if the current symbol is a superscript. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool SymbolIsSuperscript() const; + // Returns true if the current symbol is a subscript. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool SymbolIsSubscript() const; + // Returns true if the current symbol is a dropcap. + // If iterating at a higher level object than symbols, eg words, then + // this will return the attributes of the first symbol in that word. + bool SymbolIsDropcap() const; + + protected: + const char *line_separator_; + const char *paragraph_separator_; + }; + + // Class to iterate over the classifier choices for a single RIL_SYMBOL. + class ChoiceIterator { + public: + // Construction is from a LTRResultIterator that points to the symbol of + // interest. The ChoiceIterator allows a one-shot iteration over the + // choices for this symbol and after that is is useless. + explicit ChoiceIterator(const LTRResultIterator& result_it); + ~ChoiceIterator(); + + // Moves to the next choice for the symbol and returns false if there + // are none left. + bool Next(); + + // ============= Accessing data ==============. + + // Returns the null terminated UTF-8 encoded text string for the current + // choice. + // NOTE: Unlike LTRResultIterator::GetUTF8Text, the return points to an + // internal structure and should NOT be delete[]ed to free after use. + const char* GetUTF8Text() const; + + // Returns the confidence of the current choice. + // The number should be interpreted as a percent probability. (0.0f-100.0f) + float Confidence() const; + + private: + // Pointer to the WERD_RES object owned by the API. + WERD_RES* word_res_; + // Iterator over the blob choices. + BLOB_CHOICE_IT* choice_it_; + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_LTR_RESULT_ITERATOR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/mathfix.h b/hgdriver/3rdparty/hgOCR/include/ccmain/mathfix.h new file mode 100644 index 0000000..1b57598 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/mathfix.h @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////// +// File: mathfix.h +// Description: Implement missing math functions +// Author: zdenop +// Created: Fri Feb 03 06:45:06 CET 2012 +// +// (C) Copyright 2012, 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 VS2008_INCLUDE_MATHFIX_H_ +#define VS2008_INCLUDE_MATHFIXT_H_ + +#ifndef _MSC_VER +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif + +#include +#include // for _isnan(), _finite() on VC++ + +#if _MSC_VER < 1800 +#define isnan(x) _isnan(x) +#define isinf(x) (!_finite(x)) +#define fmax max //VC++ does not implement all the provisions of C99 Standard +#define round(x) roundf(x) +inline float roundf(float num) { return num > 0 ? floorf(num + 0.5f) : ceilf(num - 0.5f); } +#endif + +#endif // VS2008_INCLUDE_MATHFIXT_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/mutableiterator.h b/hgdriver/3rdparty/hgOCR/include/ccmain/mutableiterator.h new file mode 100644 index 0000000..f018fbc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/mutableiterator.h @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////// +// File: mutableiterator.h +// Description: Iterator for tesseract results providing access to +// both high-level API and Tesseract internal data structures. +// Author: David Eger +// Created: Thu Feb 24 19:01:06 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_CCMAIN_MUTABLEITERATOR_H__ +#define TESSERACT_CCMAIN_MUTABLEITERATOR_H__ + +#include "resultiterator.h" + +class BLOB_CHOICE_IT; + +namespace tesseract { + + class Tesseract; + + // Class to iterate over tesseract results, providing access to all levels + // of the page hierarchy, without including any tesseract headers or having + // to handle any tesseract structures. + // WARNING! This class points to data held within the TessBaseAPI class, and + // therefore can only be used while the TessBaseAPI class still exists and + // has not been subjected to a call of Init, SetImage, Recognize, Clear, End + // DetectOS, or anything else that changes the internal PAGE_RES. + // See apitypes.h for the definition of PageIteratorLevel. + // See also base class PageIterator, which contains the bulk of the interface. + // ResultIterator adds text-specific methods for access to OCR output. + // MutableIterator adds access to internal data structures. + + class MutableIterator : public ResultIterator { + public: + // See argument descriptions in ResultIterator() + MutableIterator(PAGE_RES* page_res, Tesseract* tesseract, + int scale, int scaled_yres, + int rect_left, int rect_top, + int rect_width, int rect_height) + : ResultIterator( + LTRResultIterator(page_res, tesseract, scale, scaled_yres, rect_left, + rect_top, rect_width, rect_height)) {} + virtual ~MutableIterator() {} + + // See PageIterator and ResultIterator for most calls. + + // Return access to Tesseract internals. + const PAGE_RES_IT *PageResIt() const { return it_; } + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_MUTABLEITERATOR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.cpp new file mode 100644 index 0000000..f0c2765 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.cpp @@ -0,0 +1,585 @@ +/////////////////////////////////////////////////////////////////////// +// File: osdetect.cpp +// Description: Orientation and script detection. +// Author: Samuel Charron +// Ranjith Unnikrishnan +// +// (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 "osdetect.h" + +#include "blobbox.h" +#include "blread.h" +#include "colfind.h" +#include "fontinfo.h" +#include "imagefind.h" +#include "linefind.h" +#include "oldlist.h" +#include "qrsequence.h" +#include "ratngs.h" +#include "strngs.h" +#include "tabvector.h" +#include "tesseractclass.h" +#include "textord.h" + +const int kMinCharactersToTry = 20; +const int kMaxCharactersToTry = 5 * kMinCharactersToTry; + +const float kSizeRatioToReject = 2.0; +const int kMinAcceptableBlobHeight = 10; + +const float kScriptAcceptRatio = 1.3; + +const float kHanRatioInKorean = 0.7; +const float kHanRatioInJapanese = 0.3; + +const float kNonAmbiguousMargin = 1.0; + +// General scripts +static const char* han_script = "Han"; +static const char* latin_script = "Latin"; +static const char* katakana_script = "Katakana"; +static const char* hiragana_script = "Hiragana"; +static const char* hangul_script = "Hangul"; + +// Pseudo-scripts Name +const char* ScriptDetector::korean_script_ = "Korean"; +const char* ScriptDetector::japanese_script_ = "Japanese"; +const char* ScriptDetector::fraktur_script_ = "Fraktur"; + +// Minimum believable resolution. +const int kMinCredibleResolution = 70; + +void OSResults::update_best_orientation() { + float first = orientations[0]; + float second = orientations[1]; + best_result.orientation_id = 0; + if (orientations[0] < orientations[1]) { + first = orientations[1]; + second = orientations[0]; + best_result.orientation_id = 1; + } + for (int i = 2; i < 4; ++i) { + if (orientations[i] > first) { + second = first; + first = orientations[i]; + best_result.orientation_id = i; + } + else if (orientations[i] > second) { + second = orientations[i]; + } + } + // Store difference of top two orientation scores. + best_result.oconfidence = first - second; +} + +void OSResults::set_best_orientation(int orientation_id) { + best_result.orientation_id = orientation_id; + best_result.oconfidence = 0; +} + +void OSResults::update_best_script(int orientation) { + // We skip index 0 to ignore the "Common" script. + float first = scripts_na[orientation][1]; + float second = scripts_na[orientation][2]; + best_result.script_id = 1; + if (scripts_na[orientation][1] < scripts_na[orientation][2]) { + first = scripts_na[orientation][2]; + second = scripts_na[orientation][1]; + best_result.script_id = 2; + } + for (int i = 3; i < kMaxNumberOfScripts; ++i) { + if (scripts_na[orientation][i] > first) { + best_result.script_id = i; + second = first; + first = scripts_na[orientation][i]; + } + else if (scripts_na[orientation][i] > second) { + second = scripts_na[orientation][i]; + } + } + best_result.sconfidence = + (first / second - 1.0) / (kScriptAcceptRatio - 1.0); +} + +int OSResults::get_best_script(int orientation_id) const { + int max_id = -1; + for (int j = 0; j < kMaxNumberOfScripts; ++j) { + const char *script = unicharset->get_script_from_script_id(j); + if (strcmp(script, "Common") && strcmp(script, "NULL")) { + if (max_id == -1 || + scripts_na[orientation_id][j] > scripts_na[orientation_id][max_id]) + max_id = j; + } + } + return max_id; +} + +// Print the script scores for all possible orientations. +void OSResults::print_scores(void) const { + for (int i = 0; i < 4; ++i) { + tprintf("Orientation id #%d", i); + print_scores(i); + } +} + +// Print the script scores for the given candidate orientation. +void OSResults::print_scores(int orientation_id) const { + for (int j = 0; j < kMaxNumberOfScripts; ++j) { + if (scripts_na[orientation_id][j]) { + tprintf("%12s\t: %f\n", unicharset->get_script_from_script_id(j), + scripts_na[orientation_id][j]); + } + } +} + +// Accumulate scores with given OSResults instance and update the best script. +void OSResults::accumulate(const OSResults& osr) { + for (int i = 0; i < 4; ++i) { + orientations[i] += osr.orientations[i]; + for (int j = 0; j < kMaxNumberOfScripts; ++j) + scripts_na[i][j] += osr.scripts_na[i][j]; + } + unicharset = osr.unicharset; + update_best_orientation(); + update_best_script(best_result.orientation_id); +} + +// Detect and erase horizontal/vertical lines and picture regions from the +// image, so that non-text blobs are removed from consideration. +void remove_nontext_regions(tesseract::Tesseract *tess, BLOCK_LIST *blocks, + TO_BLOCK_LIST *to_blocks) { + Pix *pix = tess->pix_binary(); + ASSERT_HOST(pix != NULL); + int vertical_x = 0; + int vertical_y = 1; + tesseract::TabVector_LIST v_lines; + tesseract::TabVector_LIST h_lines; + int resolution; + if (kMinCredibleResolution > pixGetXRes(pix)) { + resolution = kMinCredibleResolution; + tprintf("Warning. Invalid resolution %d dpi. Using %d instead.\n", + pixGetXRes(pix), resolution); + } + else { + resolution = pixGetXRes(pix); + } + + tesseract::LineFinder::FindAndRemoveLines(resolution, false, pix, + &vertical_x, &vertical_y, + NULL, &v_lines, &h_lines); + Pix* im_pix = tesseract::ImageFind::FindImages(pix); + if (im_pix != NULL) { + pixSubtract(pix, pix, im_pix); + pixDestroy(&im_pix); + } + tess->mutable_textord()->find_components(tess->pix_binary(), + blocks, to_blocks); +} + +// Find connected components in the page and process a subset until finished or +// a stopping criterion is met. +// Returns the number of blobs used in making the estimate. 0 implies failure. +int orientation_and_script_detection(STRING& filename, + OSResults* osr, + tesseract::Tesseract* tess) { + STRING name = filename; //truncated name + const char *lastdot; //of name + TBOX page_box; + + lastdot = strrchr(name.string(), '.'); + if (lastdot != NULL) + name[lastdot - name.string()] = '\0'; + + ASSERT_HOST(tess->pix_binary() != NULL) + int width = pixGetWidth(tess->pix_binary()); + int height = pixGetHeight(tess->pix_binary()); + + BLOCK_LIST blocks; + if (!read_unlv_file(name, width, height, &blocks)) + FullPageBlock(width, height, &blocks); + + // Try to remove non-text regions from consideration. + TO_BLOCK_LIST land_blocks, port_blocks; + remove_nontext_regions(tess, &blocks, &port_blocks); + + if (port_blocks.empty()) { + // page segmentation did not succeed, so we need to find_components first. + tess->mutable_textord()->find_components(tess->pix_binary(), + &blocks, &port_blocks); + } + else { + page_box.set_left(0); + page_box.set_bottom(0); + page_box.set_right(width); + page_box.set_top(height); + // Filter_blobs sets up the TO_BLOCKs the same as find_components does. + tess->mutable_textord()->filter_blobs(page_box.topright(), + &port_blocks, true); + } + + return os_detect(&port_blocks, osr, tess); +} + +// Filter and sample the blobs. +// Returns a non-zero number of blobs if the page was successfully processed, or +// zero if the page had too few characters to be reliable +int os_detect(TO_BLOCK_LIST* port_blocks, OSResults* osr, + tesseract::Tesseract* tess) { + int blobs_total = 0; + TO_BLOCK_IT block_it; + block_it.set_to_list(port_blocks); + + BLOBNBOX_CLIST filtered_list; + BLOBNBOX_C_IT filtered_it(&filtered_list); + + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); + block_it.forward()) { + TO_BLOCK* to_block = block_it.data(); + if (to_block->block->poly_block() && + !to_block->block->poly_block()->IsText()) continue; + BLOBNBOX_IT bbox_it; + bbox_it.set_to_list(&to_block->blobs); + for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); + bbox_it.forward()) { + BLOBNBOX* bbox = bbox_it.data(); + C_BLOB* blob = bbox->cblob(); + TBOX box = blob->bounding_box(); + ++blobs_total; + + float y_x = fabs((box.height() * 1.0) / box.width()); + float x_y = 1.0f / y_x; + // Select a >= 1.0 ratio + float ratio = x_y > y_x ? x_y : y_x; + // Blob is ambiguous + if (ratio > kSizeRatioToReject) continue; + if (box.height() < kMinAcceptableBlobHeight) continue; + filtered_it.add_to_end(bbox); + } + } + return os_detect_blobs(NULL, &filtered_list, osr, tess); +} + +// Detect orientation and script from a list of blobs. +// Returns a non-zero number of blobs if the list was successfully processed, or +// zero if the list had too few characters to be reliable. +// If allowed_scripts is non-null and non-empty, it is a list of scripts that +// constrains both orientation and script detection to consider only scripts +// from the list. +int os_detect_blobs(const GenericVector* allowed_scripts, + BLOBNBOX_CLIST* blob_list, OSResults* osr, + tesseract::Tesseract* tess) { + OSResults osr_; + if (osr == NULL) + osr = &osr_; + + osr->unicharset = &tess->unicharset; + OrientationDetector o(allowed_scripts, osr); + ScriptDetector s(allowed_scripts, osr, tess); + + BLOBNBOX_C_IT filtered_it(blob_list); + int real_max = MIN(filtered_it.length(), kMaxCharactersToTry); + // tprintf("Total blobs found = %d\n", blobs_total); + // tprintf("Number of blobs post-filtering = %d\n", filtered_it.length()); + // tprintf("Number of blobs to try = %d\n", real_max); + + // If there are too few characters, skip this page entirely. + if (real_max < kMinCharactersToTry / 2) { + tprintf("Too few characters. Skipping this page\n"); + return 0; + } + + BLOBNBOX** blobs = new BLOBNBOX*[filtered_it.length()]; + int number_of_blobs = 0; + for (filtered_it.mark_cycle_pt(); !filtered_it.cycled_list(); + filtered_it.forward()) { + blobs[number_of_blobs++] = (BLOBNBOX*)filtered_it.data(); + } + QRSequenceGenerator sequence(number_of_blobs); + int num_blobs_evaluated = 0; + for (int i = 0; i < real_max; ++i) { + if (os_detect_blob(blobs[sequence.GetVal()], &o, &s, osr, tess) + && i > kMinCharactersToTry) { + break; + } + ++num_blobs_evaluated; + } + delete[] blobs; + + // Make sure the best_result is up-to-date + int orientation = o.get_orientation(); + osr->update_best_script(orientation); + return num_blobs_evaluated; +} + +// Processes a single blob to estimate script and orientation. +// Return true if estimate of orientation and script satisfies stopping +// criteria. +bool os_detect_blob(BLOBNBOX* bbox, OrientationDetector* o, + ScriptDetector* s, OSResults* osr, + tesseract::Tesseract* tess) { + tess->tess_cn_matching.set_value(true); // turn it on + tess->tess_bn_matching.set_value(false); + C_BLOB* blob = bbox->cblob(); + TBLOB* tblob = TBLOB::PolygonalCopy(tess->poly_allow_detailed_fx, blob); + TBOX box = tblob->bounding_box(); + FCOORD current_rotation(1.0f, 0.0f); + FCOORD rotation90(0.0f, 1.0f); + BLOB_CHOICE_LIST ratings[4]; + // Test the 4 orientations + for (int i = 0; i < 4; ++i) { + // Normalize the blob. Set the origin to the place we want to be the + // bottom-middle after rotation. + // Scaling is to make the rotated height the x-height. + float scaling = static_cast(kBlnXHeight) / box.height(); + float x_origin = (box.left() + box.right()) / 2.0f; + float y_origin = (box.bottom() + box.top()) / 2.0f; + if (i == 0 || i == 2) { + // Rotation is 0 or 180. + y_origin = i == 0 ? box.bottom() : box.top(); + } + else { + // Rotation is 90 or 270. + scaling = static_cast(kBlnXHeight) / box.width(); + x_origin = i == 1 ? box.left() : box.right(); + } + TBLOB* rotated_blob = new TBLOB(*tblob); + rotated_blob->Normalize(NULL, ¤t_rotation, NULL, + x_origin, y_origin, scaling, scaling, + 0.0f, static_cast(kBlnBaselineOffset), + false, NULL); + tess->AdaptiveClassifier(rotated_blob, ratings + i); + delete rotated_blob; + current_rotation.rotate(rotation90); + } + delete tblob; + + bool stop = o->detect_blob(ratings); + s->detect_blob(ratings); + int orientation = o->get_orientation(); + stop = s->must_stop(orientation) && stop; + return stop; +} + + +OrientationDetector::OrientationDetector( + const GenericVector* allowed_scripts, OSResults* osr) { + osr_ = osr; + allowed_scripts_ = allowed_scripts; +} + +// Score the given blob and return true if it is now sure of the orientation +// after adding this block. +bool OrientationDetector::detect_blob(BLOB_CHOICE_LIST* scores) { + float blob_o_score[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float total_blob_o_score = 0.0f; + + for (int i = 0; i < 4; ++i) { + BLOB_CHOICE_IT choice_it(scores + i); + if (!choice_it.empty()) { + BLOB_CHOICE* choice = NULL; + if (allowed_scripts_ != NULL && !allowed_scripts_->empty()) { + // Find the top choice in an allowed script. + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list() && + choice == NULL; choice_it.forward()) { + int choice_script = choice_it.data()->script_id(); + int s = 0; + for (s = 0; s < allowed_scripts_->size(); ++s) { + if ((*allowed_scripts_)[s] == choice_script) { + choice = choice_it.data(); + break; + } + } + } + } + else { + choice = choice_it.data(); + } + if (choice != NULL) { + // The certainty score ranges between [-20,0]. This is converted here to + // [0,1], with 1 indicating best match. + blob_o_score[i] = 1 + 0.05 * choice->certainty(); + total_blob_o_score += blob_o_score[i]; + } + } + } + if (total_blob_o_score == 0.0) return false; + // Fill in any blanks with the worst score of the others. This is better than + // picking an arbitrary probability for it and way better than -inf. + float worst_score = 0.0f; + int num_good_scores = 0; + for (int i = 0; i < 4; ++i) { + if (blob_o_score[i] > 0.0f) { + ++num_good_scores; + if (worst_score == 0.0f || blob_o_score[i] < worst_score) + worst_score = blob_o_score[i]; + } + } + if (num_good_scores == 1) { + // Lower worst if there is only one. + worst_score /= 2.0f; + } + for (int i = 0; i < 4; ++i) { + if (blob_o_score[i] == 0.0f) { + blob_o_score[i] = worst_score; + total_blob_o_score += worst_score; + } + } + // Normalize the orientation scores for the blob and use them to + // update the aggregated orientation score. + for (int i = 0; total_blob_o_score != 0 && i < 4; ++i) { + osr_->orientations[i] += log(blob_o_score[i] / total_blob_o_score); + } + + // TODO(ranjith) Add an early exit test, based on min_orientation_margin, + // as used in pagesegmain.cpp. + return false; +} + +int OrientationDetector::get_orientation() { + osr_->update_best_orientation(); + return osr_->best_result.orientation_id; +} + + +ScriptDetector::ScriptDetector(const GenericVector* allowed_scripts, + OSResults* osr, tesseract::Tesseract* tess) { + osr_ = osr; + tess_ = tess; + allowed_scripts_ = allowed_scripts; + katakana_id_ = tess_->unicharset.add_script(katakana_script); + hiragana_id_ = tess_->unicharset.add_script(hiragana_script); + han_id_ = tess_->unicharset.add_script(han_script); + hangul_id_ = tess_->unicharset.add_script(hangul_script); + japanese_id_ = tess_->unicharset.add_script(japanese_script_); + korean_id_ = tess_->unicharset.add_script(korean_script_); + latin_id_ = tess_->unicharset.add_script(latin_script); + fraktur_id_ = tess_->unicharset.add_script(fraktur_script_); +} + + +// Score the given blob and return true if it is now sure of the script after +// adding this blob. +void ScriptDetector::detect_blob(BLOB_CHOICE_LIST* scores) { + bool done[kMaxNumberOfScripts]; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < kMaxNumberOfScripts; ++j) + done[j] = false; + + BLOB_CHOICE_IT choice_it; + choice_it.set_to_list(scores + i); + + float prev_score = -1; + int script_count = 0; + int prev_id = -1; + int prev_fontinfo_id = -1; + const char* prev_unichar = ""; + const char* unichar = ""; + + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + BLOB_CHOICE* choice = choice_it.data(); + int id = choice->script_id(); + if (allowed_scripts_ != NULL && !allowed_scripts_->empty()) { + // Check that the choice is in an allowed script. + int s = 0; + for (s = 0; s < allowed_scripts_->size(); ++s) { + if ((*allowed_scripts_)[s] == id) break; + } + if (s == allowed_scripts_->size()) continue; // Not found in list. + } + // Script already processed before. + if (done[id]) continue; + done[id] = true; + + unichar = tess_->unicharset.id_to_unichar(choice->unichar_id()); + // Save data from the first match + if (prev_score < 0) { + prev_score = -choice->certainty(); + script_count = 1; + prev_id = id; + prev_unichar = unichar; + prev_fontinfo_id = choice->fontinfo_id(); + } + else if (-choice->certainty() < prev_score + kNonAmbiguousMargin) { + ++script_count; + } + + if (strlen(prev_unichar) == 1) + if (unichar[0] >= '0' && unichar[0] <= '9') + break; + + // if script_count is >= 2, character is ambiguous, skip other matches + // since they are useless. + if (script_count >= 2) + break; + } + // Character is non ambiguous + if (script_count == 1) { + // Update the score of the winning script + osr_->scripts_na[i][prev_id] += 1.0; + + // Workaround for Fraktur + if (prev_id == latin_id_) { + if (prev_fontinfo_id >= 0) { + const tesseract::FontInfo &fi = + tess_->get_fontinfo_table().get(prev_fontinfo_id); + //printf("Font: %s i:%i b:%i f:%i s:%i k:%i (%s)\n", fi.name, + // fi.is_italic(), fi.is_bold(), fi.is_fixed_pitch(), + // fi.is_serif(), fi.is_fraktur(), + // prev_unichar); + if (fi.is_fraktur()) { + osr_->scripts_na[i][prev_id] -= 1.0; + osr_->scripts_na[i][fraktur_id_] += 1.0; + } + } + } + + // Update Japanese / Korean pseudo-scripts + if (prev_id == katakana_id_) + osr_->scripts_na[i][japanese_id_] += 1.0; + if (prev_id == hiragana_id_) + osr_->scripts_na[i][japanese_id_] += 1.0; + if (prev_id == hangul_id_) + osr_->scripts_na[i][korean_id_] += 1.0; + if (prev_id == han_id_) { + osr_->scripts_na[i][korean_id_] += kHanRatioInKorean; + osr_->scripts_na[i][japanese_id_] += kHanRatioInJapanese; + } + } + } // iterate over each orientation +} + +bool ScriptDetector::must_stop(int orientation) { + osr_->update_best_script(orientation); + return osr_->best_result.sconfidence > 1; +} + +// Helper method to convert an orientation index to its value in degrees. +// The value represents the amount of clockwise rotation in degrees that must be +// applied for the text to be upright (readable). +int OrientationIdToValue(const int& id) { + switch (id) { + case 0: + return 0; + case 1: + return 270; + case 2: + return 180; + case 3: + return 90; + default: + return -1; + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.h b/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.h new file mode 100644 index 0000000..f68853a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/osdetect.h @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////// +// File: osdetect.h +// Description: Orientation and script detection. +// Author: Samuel Charron +// Ranjith Unnikrishnan +// +// (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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_OSDETECT_H__ +#define TESSERACT_CCMAIN_OSDETECT_H__ + +#include "strngs.h" +#include "unicharset.h" + +class TO_BLOCK_LIST; +class BLOBNBOX; +class BLOB_CHOICE_LIST; +class BLOBNBOX_CLIST; + +namespace tesseract { + class Tesseract; +} + +// Max number of scripts in ICU + "NULL" + Japanese and Korean + Fraktur +const int kMaxNumberOfScripts = 116 + 1 + 2 + 1; + +struct OSBestResult { + OSBestResult() : orientation_id(0), script_id(0), sconfidence(0.0), + oconfidence(0.0) {} + int orientation_id; + int script_id; + float sconfidence; + float oconfidence; +}; + +struct OSResults { + OSResults() : unicharset(NULL) { + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < kMaxNumberOfScripts; ++j) + scripts_na[i][j] = 0; + orientations[i] = 0; + } + } + void update_best_orientation(); + // Set the estimate of the orientation to the given id. + void set_best_orientation(int orientation_id); + // Update/Compute the best estimate of the script assuming the given + // orientation id. + void update_best_script(int orientation_id); + // Return the index of the script with the highest score for this orientation. + TESS_API int get_best_script(int orientation_id) const; + // Accumulate scores with given OSResults instance and update the best script. + void accumulate(const OSResults& osr); + + // Print statistics. + void print_scores(void) const; + void print_scores(int orientation_id) const; + + // Array holding scores for each orientation id [0,3]. + // Orientation ids [0..3] map to [0, 270, 180, 90] degree orientations of the + // page respectively, where the values refer to the amount of clockwise + // rotation to be applied to the page for the text to be upright and readable. + float orientations[4]; + // Script confidence scores for each of 4 possible orientations. + float scripts_na[4][kMaxNumberOfScripts]; + + UNICHARSET* unicharset; + OSBestResult best_result; +}; + +class OrientationDetector { +public: + OrientationDetector(const GenericVector* allowed_scripts, + OSResults* results); + bool detect_blob(BLOB_CHOICE_LIST* scores); + int get_orientation(); +private: + OSResults* osr_; + const GenericVector* allowed_scripts_; +}; + +class ScriptDetector { +public: + ScriptDetector(const GenericVector* allowed_scripts, + OSResults* osr, tesseract::Tesseract* tess); + void detect_blob(BLOB_CHOICE_LIST* scores); + bool must_stop(int orientation); +private: + OSResults* osr_; + static const char* korean_script_; + static const char* japanese_script_; + static const char* fraktur_script_; + int korean_id_; + int japanese_id_; + int katakana_id_; + int hiragana_id_; + int han_id_; + int hangul_id_; + int latin_id_; + int fraktur_id_; + tesseract::Tesseract* tess_; + const GenericVector* allowed_scripts_; +}; + +int orientation_and_script_detection(STRING& filename, + OSResults*, + tesseract::Tesseract*); + +int os_detect(TO_BLOCK_LIST* port_blocks, + OSResults* osr, + tesseract::Tesseract* tess); + +int os_detect_blobs(const GenericVector* allowed_scripts, + BLOBNBOX_CLIST* blob_list, + OSResults* osr, + tesseract::Tesseract* tess); + +bool os_detect_blob(BLOBNBOX* bbox, OrientationDetector* o, + ScriptDetector* s, OSResults*, + tesseract::Tesseract* tess); + +// Helper method to convert an orientation index to its value in degrees. +// The value represents the amount of clockwise rotation in degrees that must be +// applied for the text to be upright (readable). +TESS_API int OrientationIdToValue(const int& id); + +#endif // TESSERACT_CCMAIN_OSDETECT_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/output.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/output.cpp new file mode 100644 index 0000000..5f203cb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/output.cpp @@ -0,0 +1,450 @@ +/****************************************************************** + * File: output.cpp (Formerly output.c) + * Description: Output pass + * Author: Phil Cheatle + * Created: Thu Aug 4 10:56:08 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include "helpers.h" +#include "tessvars.h" +#include "control.h" +#include "reject.h" +#include "docqual.h" +#include "output.h" +#include "globals.h" +#include "tesseractclass.h" + +#define EPAPER_EXT ".ep" +#define PAGE_YSIZE 3508 +#define CTRL_INSET '\024' //dc4=text inset +#define CTRL_FONT '\016' //so=font change +#define CTRL_DEFAULT '\017' //si=default font +#define CTRL_SHIFT '\022' //dc2=x shift +#define CTRL_TAB '\011' //tab +#define CTRL_NEWLINE '\012' //newline +#define CTRL_HARDLINE '\015' //cr + + /********************************************************************** + * pixels_to_pts + * + * Convert an integer number of pixels to the nearest integer + * number of points. + **********************************************************************/ + +inT32 pixels_to_pts( //convert coords + inT32 pixels, + inT32 pix_res //resolution +) { + float pts; //converted value + + pts = pixels * 72.0 / pix_res; + return (inT32)(pts + 0.5); //round it +} + +namespace tesseract { + void Tesseract::output_pass( //Tess output pass //send to api + PAGE_RES_IT &page_res_it, + const TBOX *target_word_box) { + BLOCK_RES *block_of_last_word; + BOOL8 force_eol; //During output + BLOCK *nextblock; //block of next word + WERD *nextword; //next word + + page_res_it.restart_page(); + block_of_last_word = NULL; + while (page_res_it.word() != NULL) { + check_debug_pt(page_res_it.word(), 120); + + if (target_word_box) { + TBOX current_word_box = page_res_it.word()->word->bounding_box(); + FCOORD center_pt( + (current_word_box.right() + current_word_box.left()) / 2, + (current_word_box.bottom() + current_word_box.top()) / 2); + if (!target_word_box->contains(center_pt)) { + page_res_it.forward(); + continue; + } + } + if (tessedit_write_block_separators && + block_of_last_word != page_res_it.block()) { + block_of_last_word = page_res_it.block(); + } + + force_eol = (tessedit_write_block_separators && + (page_res_it.block() != page_res_it.next_block())) || + (page_res_it.next_word() == NULL); + + if (page_res_it.next_word() != NULL) + nextword = page_res_it.next_word()->word; + else + nextword = NULL; + if (page_res_it.next_block() != NULL) + nextblock = page_res_it.next_block()->block; + else + nextblock = NULL; + //regardless of tilde crunching + write_results(page_res_it, + determine_newline_type(page_res_it.word()->word, + page_res_it.block()->block, + nextword, nextblock), force_eol); + page_res_it.forward(); + } + } + + + /************************************************************************* + * write_results() + * + * All recognition and rejection has now been done. Generate the following: + * .txt file - giving the final best choices with NO highlighting + * .raw file - giving the tesseract top choice output for each word + * .map file - showing how the .txt file has been rejected in the .ep file + * epchoice list - a list of one element per word, containing the text for the + * epaper. Reject strings are inserted. + * inset list - a list of bounding boxes of reject insets - indexed by the + * reject strings in the epchoice text. + *************************************************************************/ + void Tesseract::write_results(PAGE_RES_IT &page_res_it, + char newline_type, // type of newline + BOOL8 force_eol) { // override tilde crunch? + WERD_RES *word = page_res_it.word(); + const UNICHARSET &uchset = *word->uch_set; + int i; + BOOL8 need_reject = FALSE; + UNICHAR_ID space = uchset.unichar_to_id(" "); + + if ((word->unlv_crunch_mode != CR_NONE || + word->best_choice->length() == 0) && + !tessedit_zero_kelvin_rejection && !tessedit_word_for_word) { + if ((word->unlv_crunch_mode != CR_DELETE) && + (!stats_.tilde_crunch_written || + ((word->unlv_crunch_mode == CR_KEEP_SPACE) && + (word->word->space() > 0) && + !word->word->flag(W_FUZZY_NON) && + !word->word->flag(W_FUZZY_SP)))) { + if (!word->word->flag(W_BOL) && + (word->word->space() > 0) && + !word->word->flag(W_FUZZY_NON) && + !word->word->flag(W_FUZZY_SP)) { + stats_.last_char_was_tilde = false; + } + need_reject = TRUE; + } + if ((need_reject && !stats_.last_char_was_tilde) || + (force_eol && stats_.write_results_empty_block)) { + /* Write a reject char - mark as rejected unless zero_rejection mode */ + stats_.last_char_was_tilde = TRUE; + stats_.tilde_crunch_written = true; + stats_.last_char_was_newline = false; + stats_.write_results_empty_block = false; + } + + if ((word->word->flag(W_EOL) && !stats_.last_char_was_newline) || force_eol) { + stats_.tilde_crunch_written = false; + stats_.last_char_was_newline = true; + stats_.last_char_was_tilde = false; + } + + if (force_eol) + stats_.write_results_empty_block = true; + return; + } + + /* NORMAL PROCESSING of non tilde crunched words */ + + stats_.tilde_crunch_written = false; + if (newline_type) + stats_.last_char_was_newline = true; + else + stats_.last_char_was_newline = false; + stats_.write_results_empty_block = force_eol; // about to write a real word + + if (unlv_tilde_crunching && + stats_.last_char_was_tilde && + (word->word->space() == 0) && + !(word->word->flag(W_REP_CHAR) && tessedit_write_rep_codes) && + (word->best_choice->unichar_id(0) == space)) { + /* Prevent adjacent tilde across words - we know that adjacent tildes within + words have been removed */ + word->MergeAdjacentBlobs(0); + } + if (newline_type || + (word->word->flag(W_REP_CHAR) && tessedit_write_rep_codes)) + stats_.last_char_was_tilde = false; + else { + if (word->reject_map.length() > 0) { + if (word->best_choice->unichar_id(word->reject_map.length() - 1) == space) + stats_.last_char_was_tilde = true; + else + stats_.last_char_was_tilde = false; + } + else if (word->word->space() > 0) + stats_.last_char_was_tilde = false; + /* else it is unchanged as there are no output chars */ + } + + ASSERT_HOST(word->best_choice->length() == word->reject_map.length()); + + set_unlv_suspects(word); + check_debug_pt(word, 120); + if (tessedit_rejection_debug) { + tprintf("Dict word: \"%s\": %d\n", + word->best_choice->debug_string().string(), + dict_word(*(word->best_choice))); + } + if (!word->word->flag(W_REP_CHAR) || !tessedit_write_rep_codes) { + if (tessedit_zero_rejection) { + /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ + for (i = 0; i < word->best_choice->length(); ++i) { + if (word->reject_map[i].rejected()) + word->reject_map[i].setrej_minimal_rej_accept(); + } + } + if (tessedit_minimal_rejection) { + /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ + for (i = 0; i < word->best_choice->length(); ++i) { + if ((word->best_choice->unichar_id(i) != space) && + word->reject_map[i].rejected()) + word->reject_map[i].setrej_minimal_rej_accept(); + } + } + } + } +} // namespace tesseract + +/********************************************************************** + * determine_newline_type + * + * Find whether we have a wrapping or hard newline. + * Return FALSE if not at end of line. + **********************************************************************/ + +char determine_newline_type( //test line ends + WERD *word, //word to do + BLOCK *block, //current block + WERD *next_word, //next word + BLOCK *next_block //block of next word +) { + inT16 end_gap; //to right edge + inT16 width; //of next word + TBOX word_box; //bounding + TBOX next_box; //next word + TBOX block_box; //block bounding + + if (!word->flag(W_EOL)) + return FALSE; //not end of line + if (next_word == NULL || next_block == NULL || block != next_block) + return CTRL_NEWLINE; + if (next_word->space() > 0) + return CTRL_HARDLINE; //it is tabbed + word_box = word->bounding_box(); + next_box = next_word->bounding_box(); + block_box = block->bounding_box(); + //gap to eol + end_gap = block_box.right() - word_box.right(); + end_gap -= (inT32)block->space(); + width = next_box.right() - next_box.left(); + // tprintf("end_gap=%d-%d=%d, width=%d-%d=%d, nl=%d\n", + // block_box.right(),word_box.right(),end_gap, + // next_box.right(),next_box.left(),width, + // end_gap>width ? CTRL_HARDLINE : CTRL_NEWLINE); + return end_gap > width ? CTRL_HARDLINE : CTRL_NEWLINE; +} + +/************************************************************************* + * get_rep_char() + * Return the first accepted character from the repetition string. This is the + * character which is repeated - as determined earlier by fix_rep_char() + *************************************************************************/ +namespace tesseract { + UNICHAR_ID Tesseract::get_rep_char(WERD_RES *word) { // what char is repeated? + int i; + for (i = 0; ((i < word->reject_map.length()) && + (word->reject_map[i].rejected())); ++i); + + if (i < word->reject_map.length()) { + return word->best_choice->unichar_id(i); + } + else { + return word->uch_set->unichar_to_id(unrecognised_char.string()); + } + } + + /************************************************************************* + * SUSPECT LEVELS + * + * 0 - don't reject ANYTHING + * 1,2 - partial rejection + * 3 - BEST + * + * NOTE: to reject JUST tess failures in the .map file set suspect_level 3 and + * tessedit_minimal_rejection. + *************************************************************************/ + void Tesseract::set_unlv_suspects(WERD_RES *word_res) { + int len = word_res->reject_map.length(); + const WERD_CHOICE &word = *(word_res->best_choice); + const UNICHARSET &uchset = *word.unicharset(); + int i; + float rating_per_ch; + + if (suspect_level == 0) { + for (i = 0; i < len; i++) { + if (word_res->reject_map[i].rejected()) + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + return; + } + + if (suspect_level >= 3) + return; //Use defaults + + /* NOW FOR LEVELS 1 and 2 Find some stuff to unreject*/ + + if (safe_dict_word(word_res) && + (count_alphas(word) > suspect_short_words)) { + /* Unreject alphas in dictionary words */ + for (i = 0; i < len; ++i) { + if (word_res->reject_map[i].rejected() && + uchset.get_isalpha(word.unichar_id(i))) + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + } + + rating_per_ch = word.rating() / word_res->reject_map.length(); + + if (rating_per_ch >= suspect_rating_per_ch) + return; // Don't touch bad ratings + + if ((word_res->tess_accepted) || (rating_per_ch < suspect_accept_rating)) { + /* Unreject any Tess Acceptable word - but NOT tess reject chs*/ + for (i = 0; i < len; ++i) { + if (word_res->reject_map[i].rejected() && + (!uchset.eq(word.unichar_id(i), " "))) + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + } + + for (i = 0; i < len; i++) { + if (word_res->reject_map[i].rejected()) { + if (word_res->reject_map[i].flag(R_DOC_REJ)) + word_res->reject_map[i].setrej_minimal_rej_accept(); + if (word_res->reject_map[i].flag(R_BLOCK_REJ)) + word_res->reject_map[i].setrej_minimal_rej_accept(); + if (word_res->reject_map[i].flag(R_ROW_REJ)) + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + } + + if (suspect_level == 2) + return; + + if (!suspect_constrain_1Il || + (word_res->reject_map.length() <= suspect_short_words)) { + for (i = 0; i < len; i++) { + if (word_res->reject_map[i].rejected()) { + if ((word_res->reject_map[i].flag(R_1IL_CONFLICT) || + word_res->reject_map[i].flag(R_POSTNN_1IL))) + word_res->reject_map[i].setrej_minimal_rej_accept(); + + if (!suspect_constrain_1Il && + word_res->reject_map[i].flag(R_MM_REJECT)) + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + } + } + + if (acceptable_word_string(*word_res->uch_set, + word.unichar_string().string(), + word.unichar_lengths().string()) != + AC_UNACCEPTABLE || + acceptable_number_string(word.unichar_string().string(), + word.unichar_lengths().string())) { + if (word_res->reject_map.length() > suspect_short_words) { + for (i = 0; i < len; i++) { + if (word_res->reject_map[i].rejected() && + (!word_res->reject_map[i].perm_rejected() || + word_res->reject_map[i].flag(R_1IL_CONFLICT) || + word_res->reject_map[i].flag(R_POSTNN_1IL) || + word_res->reject_map[i].flag(R_MM_REJECT))) { + word_res->reject_map[i].setrej_minimal_rej_accept(); + } + } + } + } + } + + inT16 Tesseract::count_alphas(const WERD_CHOICE &word) { + int count = 0; + for (int i = 0; i < word.length(); ++i) { + if (word.unicharset()->get_isalpha(word.unichar_id(i))) + count++; + } + return count; + } + + + inT16 Tesseract::count_alphanums(const WERD_CHOICE &word) { + int count = 0; + for (int i = 0; i < word.length(); ++i) { + if (word.unicharset()->get_isalpha(word.unichar_id(i)) || + word.unicharset()->get_isdigit(word.unichar_id(i))) + count++; + } + return count; + } + + + BOOL8 Tesseract::acceptable_number_string(const char *s, + const char *lengths) { + BOOL8 prev_digit = FALSE; + + if (*lengths == 1 && *s == '(') + s++; + + if (*lengths == 1 && + ((*s == '$') || (*s == '.') || (*s == '+') || (*s == '-'))) + s++; + + for (; *s != '\0'; s += *(lengths++)) { + if (unicharset.get_isdigit(s, *lengths)) + prev_digit = TRUE; + else if (prev_digit && + (*lengths == 1 && ((*s == '.') || (*s == ',') || (*s == '-')))) + prev_digit = FALSE; + else if (prev_digit && *lengths == 1 && + (*(s + *lengths) == '\0') && ((*s == '%') || (*s == ')'))) + return TRUE; + else if (prev_digit && + *lengths == 1 && (*s == '%') && + (*(lengths + 1) == 1 && *(s + *lengths) == ')') && + (*(s + *lengths + *(lengths + 1)) == '\0')) + return TRUE; + else + return FALSE; + } + return TRUE; + } +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/output.h b/hgdriver/3rdparty/hgOCR/include/ccmain/output.h new file mode 100644 index 0000000..513e0b2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/output.h @@ -0,0 +1,33 @@ +/****************************************************************** + * File: output.h (Formerly output.h) + * Description: Output pass + * Author: Phil Cheatle + * Created: Thu Aug 4 10:56:08 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OUTPUT_H +#define OUTPUT_H + +#include "params.h" + //#include "epapconv.h" +#include "pageres.h" + +/** test line ends */ +char determine_newline_type(WERD *word, ///< word to do + BLOCK *block, ///< current block + WERD *next_word, ///< next word + BLOCK *next_block ///< block of next word +); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.cpp new file mode 100644 index 0000000..338adff --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.cpp @@ -0,0 +1,631 @@ +/////////////////////////////////////////////////////////////////////// +// 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), + include_upper_dots_(false), + include_lower_dots_(false), + 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), + include_upper_dots_(src.include_upper_dots_), + include_lower_dots_(src.include_lower_dots_), + 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_; + include_upper_dots_ = src.include_upper_dots_; + include_lower_dots_ = src.include_lower_dots_; + 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_; + 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(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->restricted_bounding_box(include_upper_dots_, + include_lower_dots_); + break; + case RIL_PARA: + para = it_->row()->row->para(); + // explicit fall-through. + case RIL_TEXTLINE: + box = it_->row()->row->restricted_bounding_box(include_upper_dots_, + include_lower_dots_); + break; + case RIL_WORD: + box = it_->word()->word->restricted_bounding_box(include_upper_dots_, + include_lower_dots_); + 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(box.left()), 0, pix_width); + *top = ClipToRange(pix_height - box.top(), 0, pix_height); + *right = ClipToRange(static_cast(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 { + return BoundingBox(level, 0, left, top, right, bottom); + } + + bool PageIterator::BoundingBox(PageIteratorLevel level, const int padding, + 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_ - padding, + rect_left_, rect_left_ + rect_width_); + *top = ClipToRange(*top / scale_ + rect_top_ - padding, + rect_top_, rect_top_ + rect_height_); + *right = ClipToRange((*right + scale_ - 1) / scale_ + rect_left_ + padding, + *left, rect_left_ + rect_width_); + *bottom = ClipToRange((*bottom + scale_ - 1) / scale_ + rect_top_ + padding, + *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 the polygon outline of the current block. The returned Pta must + * be ptaDestroy-ed after use. */ + Pta* PageIterator::BlockPolygon() const { + if (it_->block() == NULL || it_->block()->block == NULL) + return NULL; // Already at the end! + if (it_->block()->block->poly_block() == NULL) + return NULL; // No layout analysis used - no polygon. + ICOORDELT_IT it(it_->block()->block->poly_block()->points()); + Pta* pta = ptaCreate(it.length()); + int num_pts = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++num_pts) { + ICOORD* pt = it.data(); + // Convert to top-down coords within the input image. + float x = static_cast(pt->x()) / scale_ + rect_left_; + float y = rect_top_ + rect_height_ - static_cast(pt->y()) / scale_; + ptaAddPt(pta, x, y); + } + return pta; + } + + /** + * 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; + if (level == RIL_SYMBOL && cblob_it_ != NULL && + cblob_it_->data()->area() != 0) + return cblob_it_->data()->render(); + Box* box = boxCreate(left, top, right - left, bottom - top); + Pix* pix = pixClipRectangle(tesseract_->pix_binary(), box, NULL); + boxDestroy(&box); + if (level == RIL_BLOCK || level == RIL_PARA) { + // Clip to the block polygon as well. + TBOX mask_box; + Pix* mask = it_->block()->block->render_mask(&mask_box); + int mask_x = left - mask_box.left(); + int mask_y = top - (tesseract_->ImageHeight() - mask_box.top()); + // AND the mask and pix, putting the result in pix. + pixRasterop(pix, MAX(0, -mask_x), MAX(0, -mask_y), pixGetWidth(pix), + pixGetHeight(pix), PIX_SRC & PIX_DST, mask, MAX(0, mask_x), + MAX(0, mask_y)); + pixDestroy(&mask); + } + 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. + * If you do not supply an original image, you will get a binary one. + * Use pixDestroy to delete the image after use. + */ + Pix* PageIterator::GetImage(PageIteratorLevel level, int padding, + Pix* original_img, + int* left, int* top) const { + int right, bottom; + if (!BoundingBox(level, left, top, &right, &bottom)) + return NULL; + if (original_img == 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(original_img, box, NULL); + boxDestroy(&box); + if (level == RIL_BLOCK || level == RIL_PARA) { + // Clip to the block polygon as well. + TBOX mask_box; + Pix* mask = it_->block()->block->render_mask(&mask_box); + // Copy the mask registered correctly into an image the size of grey_pix. + int mask_x = *left - mask_box.left(); + int mask_y = *top - (pixGetHeight(original_img) - mask_box.top()); + int width = pixGetWidth(grey_pix); + int height = pixGetHeight(grey_pix); + Pix* resized_mask = pixCreate(width, height, 1); + pixRasterop(resized_mask, MAX(0, -mask_x), MAX(0, -mask_y), width, height, + PIX_SRC, mask, MAX(0, mask_x), MAX(0, mask_y)); + pixDestroy(&mask); + pixDilateBrick(resized_mask, resized_mask, 2 * padding + 1, + 2 * padding + 1); + pixInvert(resized_mask, resized_mask); + pixSetMasked(grey_pix, resized_mask, MAX_UINT32); + pixDestroy(&resized_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(row->base_line(left) + 0.5)); + int right = box.right(); + ICOORD endpt(right, static_cast(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; + } + + return; + // 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(); + *just = para->model->justification(); + } + + /** + * 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(); + if (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. + 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(); + } + } + + bool PageIterator::SetWordBlamerBundle(BlamerBundle *blamer_bundle) { + if (it_->word() != NULL) { + it_->word()->blamer_bundle = blamer_bundle; + return true; + } + else { + return false; + } + } + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.h b/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.h new file mode 100644 index 0000000..6d19e78 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pageiterator.h @@ -0,0 +1,364 @@ +/////////////////////////////////////////////////////////////////////// +// File: pageiterator.h +// Description: Iterator for tesseract page structure that avoids using +// tesseract internal data structures. +// Author: Ray Smith +// Created: Fri Feb 26 11:01:06 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_PAGEITERATOR_H__ +#define TESSERACT_CCMAIN_PAGEITERATOR_H__ + +#include "publictypes.h" +#include "platform.h" + +struct BlamerBundle; +class C_BLOB_IT; +class PAGE_RES; +class PAGE_RES_IT; +class WERD; +struct Pix; +struct Pta; + +namespace tesseract { + + class Tesseract; + + /** + * Class to iterate over tesseract page structure, providing access to all + * levels of the page hierarchy, without including any tesseract headers or + * having to handle any tesseract structures. + * WARNING! This class points to data held within the TessBaseAPI class, and + * therefore can only be used while the TessBaseAPI class still exists and + * has not been subjected to a call of Init, SetImage, Recognize, Clear, End + * DetectOS, or anything else that changes the internal PAGE_RES. + * See apitypes.h for the definition of PageIteratorLevel. + * See also ResultIterator, derived from PageIterator, which adds in the + * ability to access OCR output with text-specific methods. + */ + + class TESS_API PageIterator { + public: + /** + * page_res and tesseract come directly from the BaseAPI. + * The rectangle parameters are copied indirectly from the Thresholder, + * via the BaseAPI. They represent the coordinates of some rectangle in an + * original image (in top-left-origin coordinates) and therefore the top-left + * needs to be added to any output boxes in order to specify coordinates + * in the original image. See TessBaseAPI::SetRectangle. + * The scale and scaled_yres are in case the Thresholder scaled the image + * rectangle prior to thresholding. Any coordinates in tesseract's image + * must be divided by scale before adding (rect_left, rect_top). + * The scaled_yres indicates the effective resolution of the binary image + * that tesseract has been given by the Thresholder. + * After the constructor, Begin has already been called. + */ + PageIterator(PAGE_RES* page_res, Tesseract* tesseract, + int scale, int scaled_yres, + int rect_left, int rect_top, + int rect_width, int rect_height); + virtual ~PageIterator(); + + /** + * Page/ResultIterators 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. These constructors DO NOT CALL Begin, so + * iterations will continue from the location of src. + */ + PageIterator(const PageIterator& src); + const PageIterator& operator=(const PageIterator& src); + + /** Are we positioned at the same location as other? */ + bool PositionedAtSameWord(const PAGE_RES_IT* other) const; + + // ============= Moving around within the page ============. + + /** + * Moves the iterator to point to the start of the page to begin an + * iteration. + */ + virtual void Begin(); + + /** + * Moves the iterator to the beginning of the paragraph. + * This class implements this functionality by moving it to the zero indexed + * blob of the first (leftmost) word on the first row of the paragraph. + */ + virtual void RestartParagraph(); + + /** + * Return whether this iterator points anywhere in the first textline of a + * paragraph. + */ + bool IsWithinFirstTextlineOfParagraph() const; + + /** + * Moves the iterator to the beginning of the text line. + * This class implements this functionality by moving it to the zero indexed + * blob of the first (leftmost) word of the row. + */ + virtual void RestartRow(); + + /** + * 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 that RIL_SYMBOL will skip non-text blocks, but all other + * PageIteratorLevel level values will visit each non-text block once. + * Think of non text blocks as containing a single para, with a single line, + * with a single imaginary word. + * 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. + */ + virtual bool Next(PageIteratorLevel level); + + /** + * Returns true if the iterator is at the start of an object at the given + * level. + * + * For instance, suppose an iterator it is pointed to the first symbol of the + * first word of the third line of the second paragraph of the first block in + * a page, then: + * it.IsAtBeginningOf(RIL_BLOCK) = false + * it.IsAtBeginningOf(RIL_PARA) = false + * it.IsAtBeginningOf(RIL_TEXTLINE) = true + * it.IsAtBeginningOf(RIL_WORD) = true + * it.IsAtBeginningOf(RIL_SYMBOL) = true + */ + virtual bool IsAtBeginningOf(PageIteratorLevel level) const; + + /** + * 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) + * + * Here's some two-paragraph example + * text. It starts off innocuously + * enough but quickly turns bizarre. + * The author inserts a cornucopia + * of words to guard against confused + * references. + * + * Now take an iterator it pointed to the start of "bizarre." + * it.IsAtFinalElement(RIL_PARA, RIL_SYMBOL) = false + * it.IsAtFinalElement(RIL_PARA, RIL_WORD) = true + * it.IsAtFinalElement(RIL_BLOCK, RIL_WORD) = false + */ + virtual bool IsAtFinalElement(PageIteratorLevel level, + PageIteratorLevel element) const; + + /** + * Returns whether this iterator is positioned + * before other: -1 + * equal to other: 0 + * after other: 1 + */ + int Cmp(const PageIterator &other) const; + + // ============= 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. + + /** + * Controls what to include in a bounding box. Bounding boxes of all levels + * between RIL_WORD and RIL_BLOCK can include or exclude potential diacritics. + * Between layout analysis and recognition, it isn't known where all + * diacritics belong, so this control is used to include or exclude some + * diacritics that are above or below the main body of the word. In most cases + * where the placement is obvious, and after recognition, it doesn't make as + * much difference, as the diacritics will already be included in the word. + */ + void SetBoundingBoxComponents(bool include_upper_dots, + bool include_lower_dots) { + include_upper_dots_ = include_upper_dots; + include_lower_dots_ = include_lower_dots; + } + + /** + * Returns the bounding rectangle of the current object at the given level. + * See comment on coordinate system above. + * Returns false if there is no such object at the current position. + * The returned bounding box is guaranteed to match the size and position + * of the image returned by GetBinaryImage, but may clip foreground pixels + * from a grey image. The padding argument to GetImage can be used to expand + * the image to include more foreground pixels. See GetImage below. + */ + bool BoundingBox(PageIteratorLevel level, + int* left, int* top, int* right, int* bottom) const; + bool BoundingBox(PageIteratorLevel level, const int padding, + int* left, int* top, int* right, int* bottom) const; + /** + * Returns the bounding rectangle of the object in a coordinate system of the + * working image rectangle having its origin at (rect_left_, rect_top_) with + * respect to the original image and is scaled by a factor scale_. + */ + bool BoundingBoxInternal(PageIteratorLevel level, + int* left, int* top, int* right, int* bottom) const; + + /** Returns whether there is no object of a given level. */ + bool Empty(PageIteratorLevel level) const; + + /** + * Returns the type of the current block. See apitypes.h for + * PolyBlockType. + */ + PolyBlockType BlockType() const; + + /** + * Returns the polygon outline of the current block. The returned Pta must + * be ptaDestroy-ed after use. Note that the returned Pta lists the vertices + * of the polygon, and the last edge is the line segment between the last + * point and the first point. NULL will be returned if the iterator is + * at the end of the document or layout analysis was not used. + */ + Pta* BlockPolygon() const; + + /** + * 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. + */ + Pix* GetBinaryImage(PageIteratorLevel level) const; + + /** + * 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. + * If you do not supply an original image, you will get a binary one. + * Use pixDestroy to delete the image after use. + */ + Pix* GetImage(PageIteratorLevel level, int padding, Pix* original_img, + int* left, int* top) const; + + /** + * 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! + * Returns false if there is no baseline at the current position. + */ + bool Baseline(PageIteratorLevel level, + int* x1, int* y1, int* x2, int* y2) const; + + /** + * Returns orientation for the block the iterator points to. + * orientation, writing_direction, textline_order: see publictypes.h + * deskew_angle: after rotating the block so the text orientation is + * upright, how many radians does one have to rotate the + * block anti-clockwise for it to be level? + * -Pi/4 <= deskew_angle <= Pi/4 + */ + void Orientation(tesseract::Orientation *orientation, + tesseract::WritingDirection *writing_direction, + tesseract::TextlineOrder *textline_order, + float *deskew_angle) const; + + /** + * Returns information about the current paragraph, if available. + * + * justification - + * LEFT if ragged right, or fully justified and script is left-to-right. + * RIGHT if ragged left, or fully justified and script is right-to-left. + * unknown if it looks like source code or we have very few lines. + * is_list_item - + * true if we believe this is a member of an ordered or unordered list. + * is_crown - + * true if the first line of the paragraph is aligned with the other + * lines of the paragraph even though subsequent paragraphs have first + * line indents. This typically indicates that this is the continuation + * of a previous paragraph or that it is the very first paragraph in + * the chapter. + * first_line_indent - + * For LEFT aligned paragraphs, the first text line of paragraphs of + * this kind are indented this many pixels from the left edge of the + * rest of the paragraph. + * for RIGHT aligned paragraphs, the first text line of paragraphs of + * this kind are indented this many pixels from the right edge of the + * rest of the paragraph. + * NOTE 1: This value may be negative. + * NOTE 2: if *is_crown == true, the first line of this paragraph is + * actually flush, and first_line_indent is set to the "common" + * first_line_indent for subsequent paragraphs in this block + * of text. + */ + void ParagraphInfo(tesseract::ParagraphJustification *justification, + bool *is_list_item, + bool *is_crown, + int *first_line_indent) const; + + // If the current WERD_RES (it_->word()) is not NULL, sets the BlamerBundle + // of the current word to the given pointer (takes ownership of the pointer) + // and returns true. + // Can only be used when iterating on the word level. + bool SetWordBlamerBundle(BlamerBundle *blamer_bundle); + + protected: + /** + * Sets up the internal data for iterating the blobs of a new word, then + * moves the iterator to the given offset. + */ + TESS_LOCAL void BeginWord(int offset); + + /** Pointer to the page_res owned by the API. */ + PAGE_RES* page_res_; + /** Pointer to the Tesseract object owned by the API. */ + Tesseract* tesseract_; + /** + * The iterator to the page_res_. Owned by this ResultIterator. + * A pointer just to avoid dragging in Tesseract includes. + */ + PAGE_RES_IT* it_; + /** + * The current input WERD being iterated. If there is an output from OCR, + * then word_ is NULL. Owned by the API + */ + WERD* word_; + /** The length of the current word_. */ + int word_length_; + /** The current blob index within the word. */ + int blob_index_; + /** + * Iterator to the blobs within the word. If NULL, then we are iterating + * OCR results in the box_word. + * Owned by this ResultIterator. + */ + C_BLOB_IT* cblob_it_; + /** Control over what to include in bounding boxes. */ + bool include_upper_dots_; + bool include_lower_dots_; + /** Parameters saved from the Thresholder. Needed to rebuild coordinates.*/ + int scale_; + int scaled_yres_; + int rect_left_; + int rect_top_; + int rect_width_; + int rect_height_; + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_PAGEITERATOR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pagesegmain.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/pagesegmain.cpp new file mode 100644 index 0000000..9424204 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pagesegmain.cpp @@ -0,0 +1,434 @@ +/********************************************************************** + * File: pagesegmain.cpp + * Description: Top-level page segmenter for Tesseract. + * Author: Ray Smith + * Created: Thu Sep 25 17:12: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. + * + **********************************************************************/ + +#ifdef _WIN32 +#ifndef unlink +#include +#endif +#else +#include +#endif // _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "allheaders.h" +#include "blobbox.h" +#include "blread.h" +#include "colfind.h" +#include "equationdetect.h" +#include "imagefind.h" +#include "linefind.h" +#include "makerow.h" +#include "osdetect.h" +#include "tabvector.h" +#include "tesseractclass.h" +#include "tessvars.h" +#include "textord.h" +#include "tordmain.h" +#include "wordseg.h" + +namespace tesseract { + + // Max erosions to perform in removing an enclosing circle. + const int kMaxCircleErosions = 8; + + // Helper to remove an enclosing circle from an image. + // If there isn't one, then the image will most likely get badly mangled. + // The returned pix must be pixDestroyed after use. NULL may be returned + // if the image doesn't meet the trivial conditions that it uses to determine + // success. + static Pix* RemoveEnclosingCircle(Pix* pixs) { + Pix* pixsi = pixInvert(NULL, pixs); + Pix* pixc = pixCreateTemplate(pixs); + pixSetOrClearBorder(pixc, 1, 1, 1, 1, PIX_SET); + pixSeedfillBinary(pixc, pixc, pixsi, 4); + pixInvert(pixc, pixc); + pixDestroy(&pixsi); + Pix* pixt = pixAnd(NULL, pixs, pixc); + l_int32 max_count; + pixCountConnComp(pixt, 8, &max_count); + // The count has to go up before we start looking for the minimum. + l_int32 min_count = MAX_INT32; + Pix* pixout = NULL; + for (int i = 1; i < kMaxCircleErosions; i++) { + pixDestroy(&pixt); + pixErodeBrick(pixc, pixc, 3, 3); + pixt = pixAnd(NULL, pixs, pixc); + l_int32 count; + pixCountConnComp(pixt, 8, &count); + if (i == 1 || count > max_count) { + max_count = count; + min_count = count; + } + else if (i > 1 && count < min_count) { + min_count = count; + pixDestroy(&pixout); + pixout = pixCopy(NULL, pixt); // Save the best. + } + else if (count >= min_count) { + break; // We have passed by the best. + } + } + pixDestroy(&pixt); + pixDestroy(&pixc); + return pixout; + } + + /** + * Segment the page according to the current value of tessedit_pageseg_mode. + * pix_binary_ is used as the source image and should not be NULL. + * On return the blocks list owns all the constructed page layout. + */ + int Tesseract::SegmentPage(const STRING* input_file, BLOCK_LIST* blocks, + Tesseract* osd_tess, OSResults* osr) { + ASSERT_HOST(pix_binary_ != NULL); + int width = pixGetWidth(pix_binary_); + int height = pixGetHeight(pix_binary_); + // Get page segmentation mode. + PageSegMode pageseg_mode = static_cast( + static_cast(tessedit_pageseg_mode)); + // If a UNLV zone file can be found, use that instead of segmentation. + if (!PSM_COL_FIND_ENABLED(pageseg_mode) && + input_file != NULL && input_file->length() > 0) { + STRING name = *input_file; + const char* lastdot = strrchr(name.string(), '.'); + if (lastdot != NULL) + name[lastdot - name.string()] = '\0'; + read_unlv_file(name, width, height, blocks); + } + if (blocks->empty()) { + // No UNLV file present. Work according to the PageSegMode. + // First make a single block covering the whole image. + BLOCK_IT block_it(blocks); + BLOCK* block = new BLOCK("", TRUE, 0, 0, 0, 0, width, height); + block->set_right_to_left(right_to_left()); + block_it.add_to_end(block); + } + else { + // UNLV file present. Use PSM_SINGLE_BLOCK. + pageseg_mode = PSM_SINGLE_BLOCK; + } + // The diacritic_blobs holds noise blobs that may be diacritics. They + // are separated out on areas of the image that seem noisy and short-circuit + // the layout process, going straight from the initial partition creation + // right through to after word segmentation, where they are added to the + // rej_cblobs list of the most appropriate word. From there classification + // will determine whether they are used. + BLOBNBOX_LIST diacritic_blobs; + int auto_page_seg_ret_val = 0; + TO_BLOCK_LIST to_blocks; + if (PSM_OSD_ENABLED(pageseg_mode) || PSM_BLOCK_FIND_ENABLED(pageseg_mode) || + PSM_SPARSE(pageseg_mode)) { + auto_page_seg_ret_val = AutoPageSeg( + pageseg_mode, blocks, &to_blocks, + enable_noise_removal ? &diacritic_blobs : NULL, osd_tess, osr); + if (pageseg_mode == PSM_OSD_ONLY) + return auto_page_seg_ret_val; + // To create blobs from the image region bounds uncomment this line: + // to_blocks.clear(); // Uncomment to go back to the old mode. + } + else { + deskew_ = FCOORD(1.0f, 0.0f); + reskew_ = FCOORD(1.0f, 0.0f); + if (pageseg_mode == PSM_CIRCLE_WORD) { + Pix* pixcleaned = RemoveEnclosingCircle(pix_binary_); + if (pixcleaned != NULL) { + pixDestroy(&pix_binary_); + pix_binary_ = pixcleaned; + } + } + } + + if (auto_page_seg_ret_val < 0) { + return -1; + } + + if (blocks->empty()) { + if (textord_debug_tabfind) + tprintf("Empty page\n"); + return 0; // AutoPageSeg found an empty page. + } + bool splitting = + pageseg_devanagari_split_strategy != ShiroRekhaSplitter::NO_SPLIT; + bool cjk_mode = textord_use_cjk_fp_model; + + textord_.TextordPage(pageseg_mode, reskew_, width, height, pix_binary_, + pix_thresholds_, pix_grey_, splitting || cjk_mode, + &diacritic_blobs, blocks, &to_blocks); + return auto_page_seg_ret_val; + } + + // Helper writes a grey image to a file for use by scrollviewer. + // Normally for speed we don't display the image in the layout debug windows. + // If textord_debug_images is true, we draw the image as a background to some + // of the debug windows. printable determines whether these + // images are optimized for printing instead of screen display. + static void WriteDebugBackgroundImage(bool printable, Pix* pix_binary) { + Pix* grey_pix = pixCreate(pixGetWidth(pix_binary), + pixGetHeight(pix_binary), 8); + // Printable images are light grey on white, but for screen display + // they are black on dark grey so the other colors show up well. + if (printable) { + pixSetAll(grey_pix); + pixSetMasked(grey_pix, pix_binary, 192); + } + else { + pixSetAllArbitrary(grey_pix, 64); + pixSetMasked(grey_pix, pix_binary, 0); + } + AlignedBlob::IncrementDebugPix(); + pixWrite(AlignedBlob::textord_debug_pix().string(), grey_pix, IFF_PNG); + pixDestroy(&grey_pix); + } + + /** + * Auto page segmentation. Divide the page image into blocks of uniform + * text linespacing and images. + * + * Resolution (in ppi) is derived from the input image. + * + * The output goes in the blocks list with corresponding TO_BLOCKs in the + * to_blocks list. + * + * If !PSM_COL_FIND_ENABLED(pageseg_mode), then no attempt is made to divide + * the image into columns, but multiple blocks are still made if the text is + * of non-uniform linespacing. + * + * If diacritic_blobs is non-null, then diacritics/noise blobs, that would + * confuse layout anaylsis by causing textline overlap, are placed there, + * with the expectation that they will be reassigned to words later and + * noise/diacriticness determined via classification. + * + * If osd (orientation and script detection) is true then that is performed + * as well. If only_osd is true, then only orientation and script detection is + * performed. If osd is desired, (osd or only_osd) then osr_tess must be + * another Tesseract that was initialized especially for osd, and the results + * will be output into osr (orientation and script result). + */ + int Tesseract::AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST* blocks, + TO_BLOCK_LIST* to_blocks, + BLOBNBOX_LIST* diacritic_blobs, Tesseract* osd_tess, + OSResults* osr) { + if (textord_debug_images) { + WriteDebugBackgroundImage(textord_debug_printable, pix_binary_); + } + Pix* photomask_pix = NULL; + Pix* musicmask_pix = NULL; + // The blocks made by the ColumnFinder. Moved to blocks before return. + BLOCK_LIST found_blocks; + TO_BLOCK_LIST temp_blocks; + + ColumnFinder* finder = SetupPageSegAndDetectOrientation( + pageseg_mode, blocks, osd_tess, osr, &temp_blocks, &photomask_pix, + &musicmask_pix); + int result = 0; + if (finder != NULL) { + TO_BLOCK_IT to_block_it(&temp_blocks); + TO_BLOCK* to_block = to_block_it.data(); + if (musicmask_pix != NULL) { + // TODO(rays) pass the musicmask_pix into FindBlocks and mark music + // blocks separately. For now combine with photomask_pix. + pixOr(photomask_pix, photomask_pix, musicmask_pix); + } + if (equ_detect_) { + finder->SetEquationDetect(equ_detect_); + } + /* 屏蔽歪斜角度识别 + result = finder->FindBlocks( + pageseg_mode, scaled_color_, scaled_factor_, to_block, photomask_pix, + pix_thresholds_, pix_grey_, &found_blocks, diacritic_blobs, to_blocks); + if (result >= 0) + finder->GetDeskewVectors(&deskew_, &reskew_); + */ + delete finder; + } + pixDestroy(&photomask_pix); + pixDestroy(&musicmask_pix); + if (result < 0) return result; + + blocks->clear(); + BLOCK_IT block_it(blocks); + // Move the found blocks to the input/output blocks. + block_it.add_list_after(&found_blocks); + + if (textord_debug_images) { + // The debug image is no longer needed so delete it. + unlink(AlignedBlob::textord_debug_pix().string()); + } + return result; + } + + // Helper adds all the scripts from sid_set converted to ids from osd_set to + // allowed_ids. + static void AddAllScriptsConverted(const UNICHARSET& sid_set, + const UNICHARSET& osd_set, + GenericVector* allowed_ids) { + for (int i = 0; i < sid_set.get_script_table_size(); ++i) { + if (i != sid_set.null_sid()) { + const char* script = sid_set.get_script_from_script_id(i); + allowed_ids->push_back(osd_set.get_script_id_from_name(script)); + } + } + } + + /** + * Sets up auto page segmentation, determines the orientation, and corrects it. + * Somewhat arbitrary chunk of functionality, factored out of AutoPageSeg to + * facilitate testing. + * photo_mask_pix is a pointer to a NULL pointer that will be filled on return + * with the leptonica photo mask, which must be pixDestroyed by the caller. + * to_blocks is an empty list that will be filled with (usually a single) + * block that is used during layout analysis. This ugly API is required + * because of the possibility of a unlv zone file. + * TODO(rays) clean this up. + * See AutoPageSeg for other arguments. + * The returned ColumnFinder must be deleted after use. + */ + ColumnFinder* Tesseract::SetupPageSegAndDetectOrientation( + PageSegMode pageseg_mode, BLOCK_LIST* blocks, Tesseract* osd_tess, + OSResults* osr, TO_BLOCK_LIST* to_blocks, Pix** photo_mask_pix, + Pix** music_mask_pix) { + int vertical_x = 0; + int vertical_y = 1; + TabVector_LIST v_lines; + TabVector_LIST h_lines; + ICOORD bleft(0, 0); + + ASSERT_HOST(pix_binary_ != NULL); + if (tessedit_dump_pageseg_images) { + pixWrite("tessinput.png", pix_binary_, IFF_PNG); + } + // Leptonica is used to find the rule/separator lines in the input. + LineFinder::FindAndRemoveLines(source_resolution_, + textord_tabfind_show_vlines, pix_binary_, + &vertical_x, &vertical_y, music_mask_pix, + &v_lines, &h_lines); + if (tessedit_dump_pageseg_images) + pixWrite("tessnolines.png", pix_binary_, IFF_PNG); + // Leptonica is used to find a mask of the photo regions in the input. + *photo_mask_pix = ImageFind::FindImages(pix_binary_); + if (tessedit_dump_pageseg_images) + pixWrite("tessnoimages.png", pix_binary_, IFF_PNG); + if (!PSM_COL_FIND_ENABLED(pageseg_mode)) v_lines.clear(); + + // The rest of the algorithm uses the usual connected components. + textord_.find_components(pix_binary_, blocks, to_blocks); + + TO_BLOCK_IT to_block_it(to_blocks); + // There must be exactly one input block. + // TODO(rays) handle new textline finding with a UNLV zone file. + ASSERT_HOST(to_blocks->singleton()); + TO_BLOCK* to_block = to_block_it.data(); + TBOX blkbox = to_block->block->bounding_box(); + ColumnFinder* finder = NULL; + + if (to_block->line_size >= 2) { + finder = new ColumnFinder(static_cast(to_block->line_size), + blkbox.botleft(), blkbox.topright(), + source_resolution_, textord_use_cjk_fp_model, + textord_tabfind_aligned_gap_fraction, + &v_lines, &h_lines, vertical_x, vertical_y); + + finder->SetupAndFilterNoise(pageseg_mode, *photo_mask_pix, to_block); + + if (equ_detect_) { + equ_detect_->LabelSpecialText(to_block); + } + + BLOBNBOX_CLIST osd_blobs; + // osd_orientation is the number of 90 degree rotations to make the + // characters upright. (See osdetect.h for precise definition.) + // We want the text lines horizontal, (vertical text indicates vertical + // textlines) which may conflict (eg vertically written CJK). + int osd_orientation = 0; + bool vertical_text = textord_tabfind_force_vertical_text || + pageseg_mode == PSM_SINGLE_BLOCK_VERT_TEXT; + if (!vertical_text && textord_tabfind_vertical_text && + PSM_ORIENTATION_ENABLED(pageseg_mode)) { + vertical_text = + finder->IsVerticallyAlignedText(textord_tabfind_vertical_text_ratio, + to_block, &osd_blobs); + } + if (PSM_OSD_ENABLED(pageseg_mode) && osd_tess != NULL && osr != NULL) { + GenericVector osd_scripts; + if (osd_tess != this) { + // We are running osd as part of layout analysis, so constrain the + // scripts to those allowed by *this. + AddAllScriptsConverted(unicharset, osd_tess->unicharset, &osd_scripts); + for (int s = 0; s < sub_langs_.size(); ++s) { + AddAllScriptsConverted(sub_langs_[s]->unicharset, + osd_tess->unicharset, &osd_scripts); + } + } + os_detect_blobs(&osd_scripts, &osd_blobs, osr, osd_tess); + if (pageseg_mode == PSM_OSD_ONLY) { + delete finder; + return NULL; + } + osd_orientation = osr->best_result.orientation_id; + double osd_score = osr->orientations[osd_orientation]; + double osd_margin = min_orientation_margin * 2; + for (int i = 0; i < 4; ++i) { + if (i != osd_orientation && + osd_score - osr->orientations[i] < osd_margin) { + osd_margin = osd_score - osr->orientations[i]; + } + } + int best_script_id = osr->best_result.script_id; + const char* best_script_str = + osd_tess->unicharset.get_script_from_script_id(best_script_id); + bool cjk = best_script_id == osd_tess->unicharset.han_sid() || + best_script_id == osd_tess->unicharset.hiragana_sid() || + best_script_id == osd_tess->unicharset.katakana_sid() || + strcmp("Japanese", best_script_str) == 0 || + strcmp("Korean", best_script_str) == 0 || + strcmp("Hangul", best_script_str) == 0; + if (cjk) { + finder->set_cjk_script(true); + } + if (osd_margin < min_orientation_margin) { + // The margin is weak. + if (!cjk && !vertical_text && osd_orientation == 2) { + // upside down latin text is improbable with such a weak margin. + tprintf("OSD: Weak margin (%.2f), horiz textlines, not CJK: " + "Don't rotate.\n", osd_margin); + osd_orientation = 0; + } + else { + tprintf( + "OSD: Weak margin (%.2f) for %d blob text block, " + "but using orientation anyway: %d\n", + osd_margin, osd_blobs.length(), osd_orientation); + } + } + } + osd_blobs.shallow_clear(); + finder->CorrectOrientation(to_block, vertical_text, osd_orientation); + } + + return finder; + } + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pagewalk.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/pagewalk.cpp new file mode 100644 index 0000000..b3439f4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pagewalk.cpp @@ -0,0 +1,43 @@ +/********************************************************************** + * File: pagewalk.cpp (Formerly walkers.c) + * Description: Block list processors + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "pageres.h" +#include "tesseractclass.h" + +namespace tesseract { + /** + * @name process_selected_words() + * + * Walk the current block list applying the specified word processor function + * to each word that overlaps the selection_box. + */ + void Tesseract::process_selected_words( + PAGE_RES* page_res, // blocks to check + TBOX & selection_box, + BOOL8(tesseract::Tesseract::*word_processor)(PAGE_RES_IT* pr_it)) { + for (PAGE_RES_IT page_res_it(page_res); page_res_it.word() != NULL; + page_res_it.forward()) { + WERD* word = page_res_it.word()->word; + if (word->bounding_box().overlap(selection_box)) { + if (!(this->*word_processor)(&page_res_it)) + return; + } + } + } +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/par_control.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/par_control.cpp new file mode 100644 index 0000000..c6a22b7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/par_control.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////// +// File: par_control.cpp +// Description: Control code for parallel implementation. +// Author: Ray Smith +// Created: Mon Nov 04 13:23:15 PST 2013 +// +// (C) Copyright 2013, 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 "tesseractclass.h" + +namespace tesseract { + + struct BlobData { + BlobData() : blob(NULL), choices(NULL) {} + BlobData(int index, Tesseract* tess, const WERD_RES& word) + : blob(word.chopped_word->blobs[index]), + tesseract(tess), + choices(&(*word.ratings)(index, index)) {} + + TBLOB* blob; + Tesseract* tesseract; + BLOB_CHOICE_LIST** choices; + }; + + void Tesseract::PrerecAllWordsPar(const GenericVector& words) { + // Prepare all the blobs. + GenericVector blobs; + for (int w = 0; w < words.size(); ++w) { + if (words[w].word->ratings != NULL && + words[w].word->ratings->get(0, 0) == NULL) { + for (int s = 0; s < words[w].lang_words.size(); ++s) { + Tesseract* sub = s < sub_langs_.size() ? sub_langs_[s] : this; + const WERD_RES& word = *words[w].lang_words[s]; + for (int b = 0; b < word.chopped_word->NumBlobs(); ++b) { + blobs.push_back(BlobData(b, sub, word)); + } + } + } + } + // Pre-classify all the blobs. + if (tessedit_parallelize > 1) { + for (int b = 0; b < blobs.size(); ++b) { + *blobs[b].choices = + blobs[b].tesseract->classify_blob(blobs[b].blob, "par", White, NULL); + } + } + else { + // TODO(AMD) parallelize this. + for (int b = 0; b < blobs.size(); ++b) { + *blobs[b].choices = + blobs[b].tesseract->classify_blob(blobs[b].blob, "par", White, NULL); + } + } + } + +} // namespace tesseract. + + diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.cpp new file mode 100644 index 0000000..9875ae8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.cpp @@ -0,0 +1,2625 @@ +/********************************************************************** + * File: paragraphs.cpp + * Description: Paragraph detection for tesseract. + * Author: David Eger + * Created: 25 February 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. + * + **********************************************************************/ +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#endif + +#include + +#include "genericvector.h" +#include "helpers.h" +#include "mutableiterator.h" +#include "ocrpara.h" +#include "pageres.h" +#include "paragraphs.h" +#include "paragraphs_internal.h" +#include "publictypes.h" +#include "ratngs.h" +#include "rect.h" +#include "statistc.h" +#include "strngs.h" +#include "tprintf.h" +#include "unicharset.h" +#include "unicodes.h" + +namespace tesseract { + + // Special "weak" ParagraphModels. + const ParagraphModel *kCrownLeft + = reinterpret_cast(0xDEAD111F); + const ParagraphModel *kCrownRight + = reinterpret_cast(0xDEAD888F); + + // Given the width of a typical space between words, what is the threshold + // by which by which we think left and right alignments for paragraphs + // can vary and still be aligned. + static int Epsilon(int space_pix) { + return space_pix * 4 / 5; + } + + static bool AcceptableRowArgs( + int debug_level, int min_num_rows, const char *function_name, + const GenericVector *rows, + int row_start, int row_end) { + if (row_start < 0 || row_end > rows->size() || row_start > row_end) { + tprintf("Invalid arguments rows[%d, %d) while rows is of size %d.\n", + row_start, row_end, rows->size()); + return false; + } + if (row_end - row_start < min_num_rows) { + if (debug_level > 1) { + tprintf("# Too few rows[%d, %d) for %s.\n", + row_start, row_end, function_name); + } + return false; + } + return true; + } + + // =============================== Debug Code ================================ + + // Convert an integer to a decimal string. + static STRING StrOf(int num) { + char buffer[30]; + snprintf(buffer, sizeof(buffer), "%d", num); + return STRING(buffer); + } + + // Given a row-major matrix of unicode text and a column separator, print + // a formatted table. For ASCII, we get good column alignment. + static void PrintTable(const GenericVector > &rows, + const STRING &colsep) { + GenericVector max_col_widths; + for (int r = 0; r < rows.size(); r++) { + int num_columns = rows[r].size(); + for (int c = 0; c < num_columns; c++) { + int num_unicodes = 0; + for (int i = 0; i < rows[r][c].size(); i++) { + if ((rows[r][c][i] & 0xC0) != 0x80) num_unicodes++; + } + if (c >= max_col_widths.size()) { + max_col_widths.push_back(num_unicodes); + } + else { + if (num_unicodes > max_col_widths[c]) + max_col_widths[c] = num_unicodes; + } + } + } + + GenericVector col_width_patterns; + for (int c = 0; c < max_col_widths.size(); c++) { + col_width_patterns.push_back( + STRING("%-") + StrOf(max_col_widths[c]) + "s"); + } + + for (int r = 0; r < rows.size(); r++) { + for (int c = 0; c < rows[r].size(); c++) { + if (c > 0) + tprintf("%s", colsep.string()); + tprintf(col_width_patterns[c].string(), rows[r][c].string()); + } + tprintf("\n"); + } + } + + STRING RtlEmbed(const STRING &word, bool rtlify) { + if (rtlify) + return STRING(kRLE) + word + STRING(kPDF); + return word; + } + + // Print the current thoughts of the paragraph detector. + static void PrintDetectorState(const ParagraphTheory &theory, + const GenericVector &rows) { + GenericVector > output; + output.push_back(GenericVector()); + output.back().push_back("#row"); + output.back().push_back("space"); + output.back().push_back(".."); + output.back().push_back("lword[widthSEL]"); + output.back().push_back("rword[widthSEL]"); + RowScratchRegisters::AppendDebugHeaderFields(&output.back()); + output.back().push_back("text"); + + for (int i = 0; i < rows.size(); i++) { + output.push_back(GenericVector()); + GenericVector &row = output.back(); + const RowInfo& ri = *rows[i].ri_; + row.push_back(StrOf(i)); + row.push_back(StrOf(ri.average_interword_space)); + row.push_back(ri.has_leaders ? ".." : " "); + row.push_back(RtlEmbed(ri.lword_text, !ri.ltr) + + "[" + StrOf(ri.lword_box.width()) + + (ri.lword_likely_starts_idea ? "S" : "s") + + (ri.lword_likely_ends_idea ? "E" : "e") + + (ri.lword_indicates_list_item ? "L" : "l") + + "]"); + row.push_back(RtlEmbed(ri.rword_text, !ri.ltr) + + "[" + StrOf(ri.rword_box.width()) + + (ri.rword_likely_starts_idea ? "S" : "s") + + (ri.rword_likely_ends_idea ? "E" : "e") + + (ri.rword_indicates_list_item ? "L" : "l") + + "]"); + rows[i].AppendDebugInfo(theory, &row); + row.push_back(RtlEmbed(ri.text, !ri.ltr)); + } + PrintTable(output, " "); + + tprintf("Active Paragraph Models:\n"); + for (int m = 0; m < theory.models().size(); m++) { + tprintf(" %d: %s\n", m + 1, theory.models()[m]->ToString().string()); + } + } + + static void DebugDump( + bool should_print, + const STRING &phase, + const ParagraphTheory &theory, + const GenericVector &rows) { + if (!should_print) + return; + tprintf("# %s\n", phase.string()); + PrintDetectorState(theory, rows); + } + + // Print out the text for rows[row_start, row_end) + static void PrintRowRange(const GenericVector &rows, + int row_start, int row_end) { + tprintf("======================================\n"); + for (int row = row_start; row < row_end; row++) { + tprintf("%s\n", rows[row].ri_->text.string()); + } + tprintf("======================================\n"); + } + + // ============= Brain Dead Language Model (ASCII Version) =================== + + bool IsLatinLetter(int ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); + } + + bool IsDigitLike(int ch) { + return ch == 'o' || ch == 'O' || ch == 'l' || ch == 'I'; + } + + bool IsOpeningPunct(int ch) { + return strchr("'\"({[", ch) != NULL; + } + + bool IsTerminalPunct(int ch) { + return strchr(":'\".?!]})", ch) != NULL; + } + + // Return a pointer after consuming as much text as qualifies as roman numeral. + const char *SkipChars(const char *str, const char *toskip) { + while (*str != '\0' && strchr(toskip, *str)) { str++; } + return str; + } + + const char *SkipChars(const char *str, bool(*skip)(int)) { + while (*str != '\0' && skip(*str)) { str++; } + return str; + } + + const char *SkipOne(const char *str, const char *toskip) { + if (*str != '\0' && strchr(toskip, *str)) return str + 1; + return str; + } + + // Return whether it is very likely that this is a numeral marker that could + // start a list item. Some examples include: + // A I iii. VI (2) 3.5. [C-4] + bool LikelyListNumeral(const STRING &word) { + const char *kRomans = "ivxlmdIVXLMD"; + const char *kDigits = "012345789"; + const char *kOpen = "[{("; + const char *kSep = ":;-.,"; + const char *kClose = "]})"; + + int num_segments = 0; + const char *pos = word.string(); + while (*pos != '\0' && num_segments < 3) { + // skip up to two open parens. + const char *numeral_start = SkipOne(SkipOne(pos, kOpen), kOpen); + const char *numeral_end = SkipChars(numeral_start, kRomans); + if (numeral_end != numeral_start) { + // Got Roman Numeral. Great. + } + else { + numeral_end = SkipChars(numeral_start, kDigits); + if (numeral_end == numeral_start) { + // If there's a single latin letter, we can use that. + numeral_end = SkipChars(numeral_start, IsLatinLetter); + if (numeral_end - numeral_start != 1) + break; + } + } + // We got some sort of numeral. + num_segments++; + // Skip any trailing parens or punctuation. + pos = SkipChars(SkipChars(numeral_end, kClose), kSep); + if (pos == numeral_end) + break; + } + return *pos == '\0'; + } + + bool LikelyListMark(const STRING &word) { + const char *kListMarks = "0Oo*.,+."; + return word.size() == 1 && strchr(kListMarks, word[0]) != NULL; + } + + bool AsciiLikelyListItem(const STRING &word) { + return LikelyListMark(word) || LikelyListNumeral(word); + } + + // ========== Brain Dead Language Model (Tesseract Version) ================ + + // Return the first Unicode Codepoint from werd[pos]. + int UnicodeFor(const UNICHARSET *u, const WERD_CHOICE *werd, int pos) { + if (!u || !werd || pos > werd->length()) + return 0; + return UNICHAR(u->id_to_unichar(werd->unichar_id(pos)), -1).first_uni(); + } + + // A useful helper class for finding the first j >= i so that word[j] + // does not have given character type. + class UnicodeSpanSkipper { + public: + UnicodeSpanSkipper(const UNICHARSET *unicharset, const WERD_CHOICE *word) + : u_(unicharset), word_(word) { + wordlen_ = word->length(); + } + + // Given an input position, return the first position >= pos not punc. + int SkipPunc(int pos); + // Given an input position, return the first position >= pos not digit. + int SkipDigits(int pos); + // Given an input position, return the first position >= pos not roman. + int SkipRomans(int pos); + // Given an input position, return the first position >= pos not alpha. + int SkipAlpha(int pos); + + private: + const UNICHARSET *u_; + const WERD_CHOICE *word_; + int wordlen_; + }; + + int UnicodeSpanSkipper::SkipPunc(int pos) { + while (pos < wordlen_ && u_->get_ispunctuation(word_->unichar_id(pos))) pos++; + return pos; + } + + int UnicodeSpanSkipper::SkipDigits(int pos) { + while (pos < wordlen_ && (u_->get_isdigit(word_->unichar_id(pos)) || + IsDigitLike(UnicodeFor(u_, word_, pos)))) pos++; + return pos; + } + + int UnicodeSpanSkipper::SkipRomans(int pos) { + const char *kRomans = "ivxlmdIVXLMD"; + while (pos < wordlen_) { + int ch = UnicodeFor(u_, word_, pos); + if (ch >= 0xF0 || strchr(kRomans, ch) == 0) break; + pos++; + } + return pos; + } + + int UnicodeSpanSkipper::SkipAlpha(int pos) { + while (pos < wordlen_ && u_->get_isalpha(word_->unichar_id(pos))) pos++; + return pos; + } + + bool LikelyListMarkUnicode(int ch) { + if (ch < 0x80) { + STRING single_ch; + single_ch += ch; + return LikelyListMark(single_ch); + } + switch (ch) { + // TODO(eger) expand this list of unicodes as needed. + case 0x00B0: // degree sign + case 0x2022: // bullet + case 0x25E6: // white bullet + case 0x00B7: // middle dot + case 0x25A1: // white square + case 0x25A0: // black square + case 0x25AA: // black small square + case 0x2B1D: // black very small square + case 0x25BA: // black right-pointing pointer + case 0x25CF: // black circle + case 0x25CB: // white circle + return true; + default: + break; // fall through + } + return false; + } + + // Return whether it is very likely that this is a numeral marker that could + // start a list item. Some examples include: + // A I iii. VI (2) 3.5. [C-4] + bool UniLikelyListItem(const UNICHARSET *u, const WERD_CHOICE *werd) { + if (werd->length() == 1 && LikelyListMarkUnicode(UnicodeFor(u, werd, 0))) + return true; + + UnicodeSpanSkipper m(u, werd); + int num_segments = 0; + int pos = 0; + while (pos < werd->length() && num_segments < 3) { + int numeral_start = m.SkipPunc(pos); + if (numeral_start > pos + 1) break; + int numeral_end = m.SkipRomans(numeral_start); + if (numeral_end == numeral_start) { + numeral_end = m.SkipDigits(numeral_start); + if (numeral_end == numeral_start) { + // If there's a single latin letter, we can use that. + numeral_end = m.SkipAlpha(numeral_start); + if (numeral_end - numeral_start != 1) + break; + } + } + // We got some sort of numeral. + num_segments++; + // Skip any trailing punctuation. + pos = m.SkipPunc(numeral_end); + if (pos == numeral_end) + break; + } + return pos == werd->length(); + } + + // ========= Brain Dead Language Model (combined entry points) ================ + + // Given the leftmost word of a line either as a Tesseract unicharset + werd + // or a utf8 string, set the following attributes for it: + // is_list - this word might be a list number or bullet. + // starts_idea - this word is likely to start a sentence. + // ends_idea - this word is likely to end a sentence. + void LeftWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, + const STRING &utf8, + bool *is_list, bool *starts_idea, bool *ends_idea) { + *is_list = false; + *starts_idea = false; + *ends_idea = false; + if (utf8.size() == 0 || (werd != NULL && werd->length() == 0)) { // Empty + *ends_idea = true; + return; + } + + if (unicharset && werd) { // We have a proper werd and unicharset so use it. + if (UniLikelyListItem(unicharset, werd)) { + *is_list = true; + *starts_idea = true; + *ends_idea = true; + } + if (unicharset->get_isupper(werd->unichar_id(0))) { + *starts_idea = true; + } + if (unicharset->get_ispunctuation(werd->unichar_id(0))) { + *starts_idea = true; + *ends_idea = true; + } + } + else { // Assume utf8 is mostly ASCII + if (AsciiLikelyListItem(utf8)) { + *is_list = true; + *starts_idea = true; + } + int start_letter = utf8[0]; + if (IsOpeningPunct(start_letter)) { + *starts_idea = true; + } + if (IsTerminalPunct(start_letter)) { + *ends_idea = true; + } + if (start_letter >= 'A' && start_letter <= 'Z') { + *starts_idea = true; + } + } + } + + // Given the rightmost word of a line either as a Tesseract unicharset + werd + // or a utf8 string, set the following attributes for it: + // is_list - this word might be a list number or bullet. + // starts_idea - this word is likely to start a sentence. + // ends_idea - this word is likely to end a sentence. + void RightWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, + const STRING &utf8, + bool *is_list, bool *starts_idea, bool *ends_idea) { + *is_list = false; + *starts_idea = false; + *ends_idea = false; + if (utf8.size() == 0 || (werd != NULL && werd->length() == 0)) { // Empty + *ends_idea = true; + return; + } + + if (unicharset && werd) { // We have a proper werd and unicharset so use it. + if (UniLikelyListItem(unicharset, werd)) { + *is_list = true; + *starts_idea = true; + } + UNICHAR_ID last_letter = werd->unichar_id(werd->length() - 1); + if (unicharset->get_ispunctuation(last_letter)) { + *ends_idea = true; + } + } + else { // Assume utf8 is mostly ASCII + if (AsciiLikelyListItem(utf8)) { + *is_list = true; + *starts_idea = true; + } + int last_letter = utf8[utf8.size() - 1]; + if (IsOpeningPunct(last_letter) || IsTerminalPunct(last_letter)) { + *ends_idea = true; + } + } + } + + // =============== Implementation of RowScratchRegisters ===================== + /* static */ + void RowScratchRegisters::AppendDebugHeaderFields( + GenericVector *header) { + header->push_back("[lmarg,lind;rind,rmarg]"); + header->push_back("model"); + } + + void RowScratchRegisters::AppendDebugInfo(const ParagraphTheory &theory, + GenericVector *dbg) const { + char s[30]; + snprintf(s, sizeof(s), "[%3d,%3d;%3d,%3d]", + lmargin_, lindent_, rindent_, rmargin_); + dbg->push_back(s); + STRING model_string; + model_string += static_cast(GetLineType()); + model_string += ":"; + + int model_numbers = 0; + for (int h = 0; h < hypotheses_.size(); h++) { + if (hypotheses_[h].model == NULL) + continue; + if (model_numbers > 0) + model_string += ","; + if (StrongModel(hypotheses_[h].model)) { + model_string += StrOf(1 + theory.IndexOf(hypotheses_[h].model)); + } + else if (hypotheses_[h].model == kCrownLeft) { + model_string += "CrL"; + } + else if (hypotheses_[h].model == kCrownRight) { + model_string += "CrR"; + } + model_numbers++; + } + if (model_numbers == 0) + model_string += "0"; + + dbg->push_back(model_string); + } + + void RowScratchRegisters::Init(const RowInfo &row) { + ri_ = &row; + lmargin_ = 0; + lindent_ = row.pix_ldistance; + rmargin_ = 0; + rindent_ = row.pix_rdistance; + } + + LineType RowScratchRegisters::GetLineType() const { + if (hypotheses_.empty()) + return LT_UNKNOWN; + bool has_start = false; + bool has_body = false; + for (int i = 0; i < hypotheses_.size(); i++) { + switch (hypotheses_[i].ty) { + case LT_START: has_start = true; break; + case LT_BODY: has_body = true; break; + default: + tprintf("Encountered bad value in hypothesis list: %c\n", + hypotheses_[i].ty); + break; + } + } + if (has_start && has_body) + return LT_MULTIPLE; + return has_start ? LT_START : LT_BODY; + } + + LineType RowScratchRegisters::GetLineType(const ParagraphModel *model) const { + if (hypotheses_.empty()) + return LT_UNKNOWN; + bool has_start = false; + bool has_body = false; + for (int i = 0; i < hypotheses_.size(); i++) { + if (hypotheses_[i].model != model) + continue; + switch (hypotheses_[i].ty) { + case LT_START: has_start = true; break; + case LT_BODY: has_body = true; break; + default: + tprintf("Encountered bad value in hypothesis list: %c\n", + hypotheses_[i].ty); + break; + } + } + if (has_start && has_body) + return LT_MULTIPLE; + return has_start ? LT_START : LT_BODY; + } + + void RowScratchRegisters::SetStartLine() { + LineType current_lt = GetLineType(); + if (current_lt != LT_UNKNOWN && current_lt != LT_START) { + tprintf("Trying to set a line to be START when it's already BODY.\n"); + } + if (current_lt == LT_UNKNOWN || current_lt == LT_BODY) { + hypotheses_.push_back_new(LineHypothesis(LT_START, NULL)); + } + } + + void RowScratchRegisters::SetBodyLine() { + LineType current_lt = GetLineType(); + if (current_lt != LT_UNKNOWN && current_lt != LT_BODY) { + tprintf("Trying to set a line to be BODY when it's already START.\n"); + } + if (current_lt == LT_UNKNOWN || current_lt == LT_START) { + hypotheses_.push_back_new(LineHypothesis(LT_BODY, NULL)); + } + } + + void RowScratchRegisters::AddStartLine(const ParagraphModel *model) { + hypotheses_.push_back_new(LineHypothesis(LT_START, model)); + int old_idx = hypotheses_.get_index(LineHypothesis(LT_START, NULL)); + if (old_idx >= 0) + hypotheses_.remove(old_idx); + } + + void RowScratchRegisters::AddBodyLine(const ParagraphModel *model) { + hypotheses_.push_back_new(LineHypothesis(LT_BODY, model)); + int old_idx = hypotheses_.get_index(LineHypothesis(LT_BODY, NULL)); + if (old_idx >= 0) + hypotheses_.remove(old_idx); + } + + void RowScratchRegisters::StartHypotheses(SetOfModels *models) const { + for (int h = 0; h < hypotheses_.size(); h++) { + if (hypotheses_[h].ty == LT_START && StrongModel(hypotheses_[h].model)) + models->push_back_new(hypotheses_[h].model); + } + } + + void RowScratchRegisters::StrongHypotheses(SetOfModels *models) const { + for (int h = 0; h < hypotheses_.size(); h++) { + if (StrongModel(hypotheses_[h].model)) + models->push_back_new(hypotheses_[h].model); + } + } + + void RowScratchRegisters::NonNullHypotheses(SetOfModels *models) const { + for (int h = 0; h < hypotheses_.size(); h++) { + if (hypotheses_[h].model != NULL) + models->push_back_new(hypotheses_[h].model); + } + } + + const ParagraphModel *RowScratchRegisters::UniqueStartHypothesis() const { + if (hypotheses_.size() != 1 || hypotheses_[0].ty != LT_START) + return NULL; + return hypotheses_[0].model; + } + + const ParagraphModel *RowScratchRegisters::UniqueBodyHypothesis() const { + if (hypotheses_.size() != 1 || hypotheses_[0].ty != LT_BODY) + return NULL; + return hypotheses_[0].model; + } + + // Discard any hypotheses whose model is not in the given list. + void RowScratchRegisters::DiscardNonMatchingHypotheses( + const SetOfModels &models) { + if (models.empty()) + return; + for (int h = hypotheses_.size() - 1; h >= 0; h--) { + if (!models.contains(hypotheses_[h].model)) { + hypotheses_.remove(h); + } + } + } + + // ============ Geometry based Paragraph Detection Algorithm ================= + + struct Cluster { + Cluster() : center(0), count(0) {} + Cluster(int cen, int num) : center(cen), count(num) {} + + int center; // The center of the cluster. + int count; // The number of entries within the cluster. + }; + + class SimpleClusterer { + public: + explicit SimpleClusterer(int max_cluster_width) + : max_cluster_width_(max_cluster_width) {} + void Add(int value) { values_.push_back(value); } + int size() const { return values_.size(); } + void GetClusters(GenericVector *clusters); + + private: + int max_cluster_width_; + GenericVectorEqEq values_; + }; + + // Return the index of the cluster closest to value. + int ClosestCluster(const GenericVector &clusters, int value) { + int best_index = 0; + for (int i = 0; i < clusters.size(); i++) { + if (abs(value - clusters[i].center) < + abs(value - clusters[best_index].center)) + best_index = i; + } + return best_index; + } + + void SimpleClusterer::GetClusters(GenericVector *clusters) { + clusters->clear(); + values_.sort(); + for (int i = 0; i < values_.size();) { + int orig_i = i; + int lo = values_[i]; + int hi = lo; + while (++i < values_.size() && values_[i] <= lo + max_cluster_width_) { + hi = values_[i]; + } + clusters->push_back(Cluster((hi + lo) / 2, i - orig_i)); + } + } + + // Calculate left- and right-indent tab stop values seen in + // rows[row_start, row_end) given a tolerance of tolerance. + void CalculateTabStops(GenericVector *rows, + int row_start, int row_end, + int tolerance, + GenericVector *left_tabs, + GenericVector *right_tabs) { + if (!AcceptableRowArgs(0, 1, __func__, rows, row_start, row_end)) + return; + // First pass: toss all left and right indents into clusterers. + SimpleClusterer initial_lefts(tolerance); + SimpleClusterer initial_rights(tolerance); + GenericVector initial_left_tabs; + GenericVector initial_right_tabs; + for (int i = row_start; i < row_end; i++) { + initial_lefts.Add((*rows)[i].lindent_); + initial_rights.Add((*rows)[i].rindent_); + } + initial_lefts.GetClusters(&initial_left_tabs); + initial_rights.GetClusters(&initial_right_tabs); + + // Second pass: cluster only lines that are not "stray" + // An example of a stray line is a page number -- a line whose start + // and end tab-stops are far outside the typical start and end tab-stops + // for the block. + // Put another way, we only cluster data from lines whose start or end + // tab stop is frequent. + SimpleClusterer lefts(tolerance); + SimpleClusterer rights(tolerance); + + // Outlier elimination. We might want to switch this to test outlier-ness + // based on how strange a position an outlier is in instead of or in addition + // to how rare it is. These outliers get re-added if we end up having too + // few tab stops, to work with, however. + int infrequent_enough_to_ignore = 0; + if (row_end - row_start >= 8) infrequent_enough_to_ignore = 1; + if (row_end - row_start >= 20) infrequent_enough_to_ignore = 2; + + for (int i = row_start; i < row_end; i++) { + int lidx = ClosestCluster(initial_left_tabs, (*rows)[i].lindent_); + int ridx = ClosestCluster(initial_right_tabs, (*rows)[i].rindent_); + if (initial_left_tabs[lidx].count > infrequent_enough_to_ignore || + initial_right_tabs[ridx].count > infrequent_enough_to_ignore) { + lefts.Add((*rows)[i].lindent_); + rights.Add((*rows)[i].rindent_); + } + } + lefts.GetClusters(left_tabs); + rights.GetClusters(right_tabs); + + if ((left_tabs->size() == 1 && right_tabs->size() >= 4) || + (right_tabs->size() == 1 && left_tabs->size() >= 4)) { + // One side is really ragged, and the other only has one tab stop, + // so those "insignificant outliers" are probably important, actually. + // This often happens on a page of an index. Add back in the ones + // we omitted in the first pass. + for (int i = row_start; i < row_end; i++) { + int lidx = ClosestCluster(initial_left_tabs, (*rows)[i].lindent_); + int ridx = ClosestCluster(initial_right_tabs, (*rows)[i].rindent_); + if (!(initial_left_tabs[lidx].count > infrequent_enough_to_ignore || + initial_right_tabs[ridx].count > infrequent_enough_to_ignore)) { + lefts.Add((*rows)[i].lindent_); + rights.Add((*rows)[i].rindent_); + } + } + } + lefts.GetClusters(left_tabs); + rights.GetClusters(right_tabs); + + // If one side is almost a two-indent aligned side, and the other clearly + // isn't, try to prune out the least frequent tab stop from that side. + if (left_tabs->size() == 3 && right_tabs->size() >= 4) { + int to_prune = -1; + for (int i = left_tabs->size() - 1; i >= 0; i--) { + if (to_prune < 0 || + (*left_tabs)[i].count < (*left_tabs)[to_prune].count) { + to_prune = i; + } + } + if (to_prune >= 0 && + (*left_tabs)[to_prune].count <= infrequent_enough_to_ignore) { + left_tabs->remove(to_prune); + } + } + if (right_tabs->size() == 3 && left_tabs->size() >= 4) { + int to_prune = -1; + for (int i = right_tabs->size() - 1; i >= 0; i--) { + if (to_prune < 0 || + (*right_tabs)[i].count < (*right_tabs)[to_prune].count) { + to_prune = i; + } + } + if (to_prune >= 0 && + (*right_tabs)[to_prune].count <= infrequent_enough_to_ignore) { + right_tabs->remove(to_prune); + } + } + } + + // Given a paragraph model mark rows[row_start, row_end) as said model + // start or body lines. + // + // Case 1: model->first_indent_ != model->body_indent_ + // Differentiating the paragraph start lines from the paragraph body lines in + // this case is easy, we just see how far each line is indented. + // + // Case 2: model->first_indent_ == model->body_indent_ + // Here, we find end-of-paragraph lines by looking for "short lines." + // What constitutes a "short line" changes depending on whether the text + // ragged-right[left] or fully justified (aligned left and right). + // + // Case 2a: Ragged Right (or Left) text. (eop_threshold == 0) + // We have a new paragraph it the first word would have at the end + // of the previous line. + // + // Case 2b: Fully Justified. (eop_threshold > 0) + // We mark a line as short (end of paragraph) if the offside indent + // is greater than eop_threshold. + void MarkRowsWithModel(GenericVector *rows, + int row_start, int row_end, + const ParagraphModel *model, + bool ltr, + int eop_threshold) { + if (!AcceptableRowArgs(0, 0, __func__, rows, row_start, row_end)) + return; + for (int row = row_start; row < row_end; row++) { + bool valid_first = ValidFirstLine(rows, row, model); + bool valid_body = ValidBodyLine(rows, row, model); + if (valid_first && !valid_body) { + (*rows)[row].AddStartLine(model); + } + else if (valid_body && !valid_first) { + (*rows)[row].AddBodyLine(model); + } + else if (valid_body && valid_first) { + bool after_eop = (row == row_start); + if (row > row_start) { + if (eop_threshold > 0) { + if (model->justification() == JUSTIFICATION_LEFT) { + after_eop = (*rows)[row - 1].rindent_ > eop_threshold; + } + else { + after_eop = (*rows)[row - 1].lindent_ > eop_threshold; + } + } + else { + after_eop = FirstWordWouldHaveFit((*rows)[row - 1], (*rows)[row], + model->justification()); + } + } + if (after_eop) { + (*rows)[row].AddStartLine(model); + } + else { + (*rows)[row].AddBodyLine(model); + } + } + else { + // Do nothing. Stray row. + } + } + } + + // GeometricClassifierState holds all of the information we'll use while + // trying to determine a paragraph model for the text lines in a block of + // text: + // + the rows under consideration [row_start, row_end) + // + the common left- and right-indent tab stops + // + does the block start out left-to-right or right-to-left + // Further, this struct holds the data we amass for the (single) ParagraphModel + // we'll assign to the text lines (assuming we get that far). + struct GeometricClassifierState { + GeometricClassifierState(int dbg_level, + GenericVector *r, + int r_start, int r_end) + : debug_level(dbg_level), rows(r), row_start(r_start), row_end(r_end), + margin(0) { + tolerance = InterwordSpace(*r, r_start, r_end); + CalculateTabStops(r, r_start, r_end, tolerance, + &left_tabs, &right_tabs); + if (debug_level >= 3) { + tprintf("Geometry: TabStop cluster tolerance = %d; " + "%d left tabs; %d right tabs\n", + tolerance, left_tabs.size(), right_tabs.size()); + } + ltr = (*r)[r_start].ri_->ltr; + } + + void AssumeLeftJustification() { + just = tesseract::JUSTIFICATION_LEFT; + margin = (*rows)[row_start].lmargin_; + } + + void AssumeRightJustification() { + just = tesseract::JUSTIFICATION_RIGHT; + margin = (*rows)[row_start].rmargin_; + } + + // Align tabs are the tab stops the text is aligned to. + const GenericVector &AlignTabs() const { + if (just == tesseract::JUSTIFICATION_RIGHT) return right_tabs; + return left_tabs; + } + + // Offside tabs are the tab stops opposite the tabs used to align the text. + // + // Note that for a left-to-right text which is aligned to the right such as + // this function comment, the offside tabs are the horizontal tab stops + // marking the beginning of ("Note", "this" and "marking"). + const GenericVector &OffsideTabs() const { + if (just == tesseract::JUSTIFICATION_RIGHT) return left_tabs; + return right_tabs; + } + + // Return whether the i'th row extends from the leftmost left tab stop + // to the right most right tab stop. + bool IsFullRow(int i) const { + return ClosestCluster(left_tabs, (*rows)[i].lindent_) == 0 && + ClosestCluster(right_tabs, (*rows)[i].rindent_) == 0; + } + + int AlignsideTabIndex(int row_idx) const { + return ClosestCluster(AlignTabs(), (*rows)[row_idx].AlignsideIndent(just)); + } + + // Given what we know about the paragraph justification (just), would the + // first word of row_b have fit at the end of row_a? + bool FirstWordWouldHaveFit(int row_a, int row_b) { + return ::tesseract::FirstWordWouldHaveFit( + (*rows)[row_a], (*rows)[row_b], just); + } + + void PrintRows() const { PrintRowRange(*rows, row_start, row_end); } + + void Fail(int min_debug_level, const char *why) const { + if (debug_level < min_debug_level) return; + tprintf("# %s\n", why); + PrintRows(); + } + + ParagraphModel Model() const { + return ParagraphModel(just, margin, first_indent, body_indent, tolerance); + } + + // We print out messages with a debug level at least as great as debug_level. + int debug_level; + + // The Geometric Classifier was asked to find a single paragraph model + // to fit the text rows (*rows)[row_start, row_end) + GenericVector *rows; + int row_start; + int row_end; + + // The amount by which we expect the text edge can vary and still be aligned. + int tolerance; + + // Is the script in this text block left-to-right? + // HORRIBLE ROUGH APPROXIMATION. TODO(eger): Improve + bool ltr; + + // These left and right tab stops were determined to be the common tab + // stops for the given text. + GenericVector left_tabs; + GenericVector right_tabs; + + // These are parameters we must determine to create a ParagraphModel. + tesseract::ParagraphJustification just; + int margin; + int first_indent; + int body_indent; + + // eop_threshold > 0 if the text is fully justified. See MarkRowsWithModel() + int eop_threshold; + }; + + // Given a section of text where strong textual clues did not help identifying + // paragraph breaks, and for which the left and right indents have exactly + // three tab stops between them, attempt to find the paragraph breaks based + // solely on the outline of the text and whether the script is left-to-right. + // + // Algorithm Detail: + // The selected rows are in the form of a rectangle except + // for some number of "short lines" of the same length: + // + // (A1) xxxxxxxxxxxxx (B1) xxxxxxxxxxxx + // xxxxxxxxxxx xxxxxxxxxx # A "short" line. + // xxxxxxxxxxxxx xxxxxxxxxxxx + // xxxxxxxxxxxxx xxxxxxxxxxxx + // + // We have a slightly different situation if the only short + // line is at the end of the excerpt. + // + // (A2) xxxxxxxxxxxxx (B2) xxxxxxxxxxxx + // xxxxxxxxxxxxx xxxxxxxxxxxx + // xxxxxxxxxxxxx xxxxxxxxxxxx + // xxxxxxxxxxx xxxxxxxxxx # A "short" line. + // + // We'll interpret these as follows based on the reasoning in the comment for + // GeometricClassify(): + // [script direction: first indent, body indent] + // (A1) LtR: 2,0 RtL: 0,0 (B1) LtR: 0,0 RtL: 2,0 + // (A2) LtR: 2,0 RtL: CrR (B2) LtR: CrL RtL: 2,0 + void GeometricClassifyThreeTabStopTextBlock( + int debug_level, + GeometricClassifierState &s, + ParagraphTheory *theory) { + int num_rows = s.row_end - s.row_start; + int num_full_rows = 0; + int last_row_full = 0; + for (int i = s.row_start; i < s.row_end; i++) { + if (s.IsFullRow(i)) { + num_full_rows++; + if (i == s.row_end - 1) last_row_full++; + } + } + + if (num_full_rows < 0.7 * num_rows) { + s.Fail(1, "Not enough full lines to know which lines start paras."); + return; + } + + // eop_threshold gets set if we're fully justified; see MarkRowsWithModel() + s.eop_threshold = 0; + + if (s.ltr) { + s.AssumeLeftJustification(); + } + else { + s.AssumeRightJustification(); + } + + if (debug_level > 0) { + tprintf("# Not enough variety for clear outline classification. " + "Guessing these are %s aligned based on script.\n", + s.ltr ? "left" : "right"); + s.PrintRows(); + } + + if (s.AlignTabs().size() == 2) { // case A1 or A2 + s.first_indent = s.AlignTabs()[1].center; + s.body_indent = s.AlignTabs()[0].center; + } + else { // case B1 or B2 + if (num_rows - 1 == num_full_rows - last_row_full) { + // case B2 + const ParagraphModel *model = s.ltr ? kCrownLeft : kCrownRight; + (*s.rows)[s.row_start].AddStartLine(model); + for (int i = s.row_start + 1; i < s.row_end; i++) { + (*s.rows)[i].AddBodyLine(model); + } + return; + } + else { + // case B1 + s.first_indent = s.body_indent = s.AlignTabs()[0].center; + s.eop_threshold = (s.OffsideTabs()[0].center + + s.OffsideTabs()[1].center) / 2; + } + } + const ParagraphModel *model = theory->AddModel(s.Model()); + MarkRowsWithModel(s.rows, s.row_start, s.row_end, model, + s.ltr, s.eop_threshold); + return; + } + + // This function is called if strong textual clues were not available, but + // the caller hopes that the paragraph breaks will be super obvious just + // by the outline of the text. + // + // The particularly difficult case is figuring out what's going on if you + // don't have enough short paragraph end lines to tell us what's going on. + // + // For instance, let's say you have the following outline: + // + // (A1) xxxxxxxxxxxxxxxxxxxxxx + // xxxxxxxxxxxxxxxxxxxx + // xxxxxxxxxxxxxxxxxxxxxx + // xxxxxxxxxxxxxxxxxxxxxx + // + // Even if we know that the text is left-to-right and so will probably be + // left-aligned, both of the following are possible texts: + // + // (A1a) 1. Here our list item + // with two full lines. + // 2. Here a second item. + // 3. Here our third one. + // + // (A1b) so ends paragraph one. + // Here starts another + // paragraph we want to + // read. This continues + // + // These examples are obvious from the text and should have been caught + // by the StrongEvidenceClassify pass. However, for languages where we don't + // have capital letters to go on (e.g. Hebrew, Arabic, Hindi, Chinese), + // it's worth guessing that (A1b) is the correct interpretation if there are + // far more "full" lines than "short" lines. + void GeometricClassify(int debug_level, + GenericVector *rows, + int row_start, int row_end, + ParagraphTheory *theory) { + if (!AcceptableRowArgs(debug_level, 4, __func__, rows, row_start, row_end)) + return; + if (debug_level > 1) { + tprintf("###############################################\n"); + tprintf("##### GeometricClassify( rows[%d:%d) ) ####\n", + row_start, row_end); + tprintf("###############################################\n"); + } + RecomputeMarginsAndClearHypotheses(rows, row_start, row_end, 10); + + GeometricClassifierState s(debug_level, rows, row_start, row_end); + if (s.left_tabs.size() > 2 && s.right_tabs.size() > 2) { + s.Fail(2, "Too much variety for simple outline classification."); + return; + } + if (s.left_tabs.size() <= 1 && s.right_tabs.size() <= 1) { + s.Fail(1, "Not enough variety for simple outline classification."); + return; + } + if (s.left_tabs.size() + s.right_tabs.size() == 3) { + GeometricClassifyThreeTabStopTextBlock(debug_level, s, theory); + return; + } + + // At this point, we know that one side has at least two tab stops, and the + // other side has one or two tab stops. + // Left to determine: + // (1) Which is the body indent and which is the first line indent? + // (2) Is the text fully justified? + + // If one side happens to have three or more tab stops, assume that side + // is opposite of the aligned side. + if (s.right_tabs.size() > 2) { + s.AssumeLeftJustification(); + } + else if (s.left_tabs.size() > 2) { + s.AssumeRightJustification(); + } + else if (s.ltr) { // guess based on script direction + s.AssumeLeftJustification(); + } + else { + s.AssumeRightJustification(); + } + + if (s.AlignTabs().size() == 2) { + // For each tab stop on the aligned side, how many of them appear + // to be paragraph start lines? [first lines] + int firsts[2] = { 0, 0 }; + // Count the first line as a likely paragraph start line. + firsts[s.AlignsideTabIndex(s.row_start)]++; + // For each line, if the first word would have fit on the previous + // line count it as a likely paragraph start line. + bool jam_packed = true; + for (int i = s.row_start + 1; i < s.row_end; i++) { + if (s.FirstWordWouldHaveFit(i - 1, i)) { + firsts[s.AlignsideTabIndex(i)]++; + jam_packed = false; + } + } + // Make an extra accounting for the last line of the paragraph just + // in case it's the only short line in the block. That is, take its + // first word as typical and see if this looks like the *last* line + // of a paragraph. If so, mark the *other* indent as probably a first. + if (jam_packed && s.FirstWordWouldHaveFit(s.row_end - 1, s.row_end - 1)) { + firsts[1 - s.AlignsideTabIndex(s.row_end - 1)]++; + } + + int percent0firsts, percent1firsts; + percent0firsts = (100 * firsts[0]) / s.AlignTabs()[0].count; + percent1firsts = (100 * firsts[1]) / s.AlignTabs()[1].count; + + // TODO(eger): Tune these constants if necessary. + if ((percent0firsts < 20 && 30 < percent1firsts) || + percent0firsts + 30 < percent1firsts) { + s.first_indent = s.AlignTabs()[1].center; + s.body_indent = s.AlignTabs()[0].center; + } + else if ((percent1firsts < 20 && 30 < percent0firsts) || + percent1firsts + 30 < percent0firsts) { + s.first_indent = s.AlignTabs()[0].center; + s.body_indent = s.AlignTabs()[1].center; + } + else { + // Ambiguous! Probably lineated (poetry) + if (debug_level > 1) { + tprintf("# Cannot determine %s indent likely to start paragraphs.\n", + s.just == tesseract::JUSTIFICATION_LEFT ? "left" : "right"); + tprintf("# Indent of %d looks like a first line %d%% of the time.\n", + s.AlignTabs()[0].center, percent0firsts); + tprintf("# Indent of %d looks like a first line %d%% of the time.\n", + s.AlignTabs()[1].center, percent1firsts); + s.PrintRows(); + } + return; + } + } + else { + // There's only one tab stop for the "aligned to" side. + s.first_indent = s.body_indent = s.AlignTabs()[0].center; + } + + // At this point, we have our model. + const ParagraphModel *model = theory->AddModel(s.Model()); + + // Now all we have to do is figure out if the text is fully justified or not. + // eop_threshold: default to fully justified unless we see evidence below. + // See description on MarkRowsWithModel() + s.eop_threshold = + (s.OffsideTabs()[0].center + s.OffsideTabs()[1].center) / 2; + // If the text is not fully justified, re-set the eop_threshold to 0. + if (s.AlignTabs().size() == 2) { + // Paragraphs with a paragraph-start indent. + for (int i = s.row_start; i < s.row_end - 1; i++) { + if (ValidFirstLine(s.rows, i + 1, model) && + !NearlyEqual(s.OffsideTabs()[0].center, + (*s.rows)[i].OffsideIndent(s.just), s.tolerance)) { + // We found a non-end-of-paragraph short line: not fully justified. + s.eop_threshold = 0; + break; + } + } + } + else { + // Paragraphs with no paragraph-start indent. + for (int i = s.row_start; i < s.row_end - 1; i++) { + if (!s.FirstWordWouldHaveFit(i, i + 1) && + !NearlyEqual(s.OffsideTabs()[0].center, + (*s.rows)[i].OffsideIndent(s.just), s.tolerance)) { + // We found a non-end-of-paragraph short line: not fully justified. + s.eop_threshold = 0; + break; + } + } + } + MarkRowsWithModel(rows, row_start, row_end, model, s.ltr, s.eop_threshold); + } + + // =============== Implementation of ParagraphTheory ===================== + + const ParagraphModel *ParagraphTheory::AddModel(const ParagraphModel &model) { + for (int i = 0; i < models_->size(); i++) { + if ((*models_)[i]->Comparable(model)) + return (*models_)[i]; + } + ParagraphModel *m = new ParagraphModel(model); + models_->push_back(m); + models_we_added_.push_back_new(m); + return m; + } + + void ParagraphTheory::DiscardUnusedModels(const SetOfModels &used_models) { + for (int i = models_->size() - 1; i >= 0; i--) { + ParagraphModel *m = (*models_)[i]; + if (!used_models.contains(m) && models_we_added_.contains(m)) { + models_->remove(i); + models_we_added_.remove(models_we_added_.get_index(m)); + delete m; + } + } + } + + // Examine rows[start, end) and try to determine if an existing non-centered + // paragraph model would fit them perfectly. If so, return a pointer to it. + // If not, return NULL. + const ParagraphModel *ParagraphTheory::Fits( + const GenericVector *rows, int start, int end) const { + for (int m = 0; m < models_->size(); m++) { + const ParagraphModel *model = (*models_)[m]; + if (model->justification() != JUSTIFICATION_CENTER && + RowsFitModel(rows, start, end, model)) + return model; + } + return NULL; + } + + void ParagraphTheory::NonCenteredModels(SetOfModels *models) { + for (int m = 0; m < models_->size(); m++) { + const ParagraphModel *model = (*models_)[m]; + if (model->justification() != JUSTIFICATION_CENTER) + models->push_back_new(model); + } + } + + int ParagraphTheory::IndexOf(const ParagraphModel *model) const { + for (int i = 0; i < models_->size(); i++) { + if ((*models_)[i] == model) + return i; + } + return -1; + } + + bool ValidFirstLine(const GenericVector *rows, + int row, const ParagraphModel *model) { + if (!StrongModel(model)) { + tprintf("ValidFirstLine() should only be called with strong models!\n"); + } + return StrongModel(model) && + model->ValidFirstLine( + (*rows)[row].lmargin_, (*rows)[row].lindent_, + (*rows)[row].rindent_, (*rows)[row].rmargin_); + } + + bool ValidBodyLine(const GenericVector *rows, + int row, const ParagraphModel *model) { + if (!StrongModel(model)) { + tprintf("ValidBodyLine() should only be called with strong models!\n"); + } + return StrongModel(model) && + model->ValidBodyLine( + (*rows)[row].lmargin_, (*rows)[row].lindent_, + (*rows)[row].rindent_, (*rows)[row].rmargin_); + } + + bool CrownCompatible(const GenericVector *rows, + int a, int b, const ParagraphModel *model) { + if (model != kCrownRight && model != kCrownLeft) { + tprintf("CrownCompatible() should only be called with crown models!\n"); + return false; + } + RowScratchRegisters &row_a = (*rows)[a]; + RowScratchRegisters &row_b = (*rows)[b]; + if (model == kCrownRight) { + return NearlyEqual(row_a.rindent_ + row_a.rmargin_, + row_b.rindent_ + row_b.rmargin_, + Epsilon(row_a.ri_->average_interword_space)); + } + return NearlyEqual(row_a.lindent_ + row_a.lmargin_, + row_b.lindent_ + row_b.lmargin_, + Epsilon(row_a.ri_->average_interword_space)); + } + + + // =============== Implementation of ParagraphModelSmearer ==================== + + ParagraphModelSmearer::ParagraphModelSmearer( + GenericVector *rows, + int row_start, int row_end, ParagraphTheory *theory) + : theory_(theory), rows_(rows), row_start_(row_start), + row_end_(row_end) { + if (!AcceptableRowArgs(0, 0, __func__, rows, row_start, row_end)) { + row_start_ = 0; + row_end_ = 0; + return; + } + SetOfModels no_models; + for (int row = row_start - 1; row <= row_end; row++) { + open_models_.push_back(no_models); + } + } + + // see paragraphs_internal.h + void ParagraphModelSmearer::CalculateOpenModels(int row_start, int row_end) { + SetOfModels no_models; + if (row_start < row_start_) row_start = row_start_; + if (row_end > row_end_) row_end = row_end_; + + for (int row = (row_start > 0) ? row_start - 1 : row_start; row < row_end; + row++) { + if ((*rows_)[row].ri_->num_words == 0) { + OpenModels(row + 1) = no_models; + } + else { + SetOfModels &opened = OpenModels(row); + (*rows_)[row].StartHypotheses(&opened); + + // Which models survive the transition from row to row + 1? + SetOfModels still_open; + for (int m = 0; m < opened.size(); m++) { + if (ValidFirstLine(rows_, row, opened[m]) || + ValidBodyLine(rows_, row, opened[m])) { + // This is basic filtering; we check likely paragraph starty-ness down + // below in Smear() -- you know, whether the first word would have fit + // and such. + still_open.push_back_new(opened[m]); + } + } + OpenModels(row + 1) = still_open; + } + } + } + + // see paragraphs_internal.h + void ParagraphModelSmearer::Smear() { + CalculateOpenModels(row_start_, row_end_); + + // For each row which we're unsure about (that is, it is LT_UNKNOWN or + // we have multiple LT_START hypotheses), see if there's a model that + // was recently used (an "open" model) which might model it well. + for (int i = row_start_; i < row_end_; i++) { + RowScratchRegisters &row = (*rows_)[i]; + if (row.ri_->num_words == 0) + continue; + + // Step One: + // Figure out if there are "open" models which are left-alined or + // right-aligned. This is important for determining whether the + // "first" word in a row would fit at the "end" of the previous row. + bool left_align_open = false; + bool right_align_open = false; + for (int m = 0; m < OpenModels(i).size(); m++) { + switch (OpenModels(i)[m]->justification()) { + case JUSTIFICATION_LEFT: left_align_open = true; break; + case JUSTIFICATION_RIGHT: right_align_open = true; break; + default: left_align_open = right_align_open = true; + } + } + // Step Two: + // Use that knowledge to figure out if this row is likely to + // start a paragraph. + bool likely_start; + if (i == 0) { + likely_start = true; + } + else { + if ((left_align_open && right_align_open) || + (!left_align_open && !right_align_open)) { + likely_start = LikelyParagraphStart((*rows_)[i - 1], row, + JUSTIFICATION_LEFT) || + LikelyParagraphStart((*rows_)[i - 1], row, + JUSTIFICATION_RIGHT); + } + else if (left_align_open) { + likely_start = LikelyParagraphStart((*rows_)[i - 1], row, + JUSTIFICATION_LEFT); + } + else { + likely_start = LikelyParagraphStart((*rows_)[i - 1], row, + JUSTIFICATION_RIGHT); + } + } + + // Step Three: + // If this text line seems like an obvious first line of an + // open model, or an obvious continuation of an existing + // modelled paragraph, mark it up. + if (likely_start) { + // Add Start Hypotheses for all Open models that fit. + for (int m = 0; m < OpenModels(i).size(); m++) { + if (ValidFirstLine(rows_, i, OpenModels(i)[m])) { + row.AddStartLine(OpenModels(i)[m]); + } + } + } + else { + // Add relevant body line hypotheses. + SetOfModels last_line_models; + if (i > 0) { + (*rows_)[i - 1].StrongHypotheses(&last_line_models); + } + else { + theory_->NonCenteredModels(&last_line_models); + } + for (int m = 0; m < last_line_models.size(); m++) { + const ParagraphModel *model = last_line_models[m]; + if (ValidBodyLine(rows_, i, model)) + row.AddBodyLine(model); + } + } + + // Step Four: + // If we're still quite unsure about this line, go through all + // models in our theory and see if this row could be the start + // of any of our models. + if (row.GetLineType() == LT_UNKNOWN || + (row.GetLineType() == LT_START && !row.UniqueStartHypothesis())) { + SetOfModels all_models; + theory_->NonCenteredModels(&all_models); + for (int m = 0; m < all_models.size(); m++) { + if (ValidFirstLine(rows_, i, all_models[m])) { + row.AddStartLine(all_models[m]); + } + } + } + // Step Five: + // Since we may have updated the hypotheses about this row, we need + // to recalculate the Open models for the rest of rows[i + 1, row_end) + if (row.GetLineType() != LT_UNKNOWN) { + CalculateOpenModels(i + 1, row_end_); + } + } + } + + // ================ Main Paragraph Detection Algorithm ======================= + + // Find out what ParagraphModels are actually used, and discard any + // that are not. + void DiscardUnusedModels(const GenericVector &rows, + ParagraphTheory *theory) { + SetOfModels used_models; + for (int i = 0; i < rows.size(); i++) { + rows[i].StrongHypotheses(&used_models); + } + theory->DiscardUnusedModels(used_models); + } + + // DowngradeWeakestToCrowns: + // Forget any flush-{left, right} models unless we see two or more + // of them in sequence. + // + // In pass 3, we start to classify even flush-left paragraphs (paragraphs + // where the first line and body indent are the same) as having proper Models. + // This is generally dangerous, since if you start imagining that flush-left + // is a typical paragraph model when it is not, it will lead you to chop normal + // indented paragraphs in the middle whenever a sentence happens to start on a + // new line (see "This" above). What to do? + // What we do is to take any paragraph which is flush left and is not + // preceded by another paragraph of the same model and convert it to a "Crown" + // paragraph. This is a weak pseudo-ParagraphModel which is a placeholder + // for later. It means that the paragraph is flush, but it would be desirable + // to mark it as the same model as following text if it fits. This downgrade + // FlushLeft -> CrownLeft -> Model of following paragraph. Means that we + // avoid making flush left Paragraph Models whenever we see a top-of-the-page + // half-of-a-paragraph. and instead we mark it the same as normal body text. + // + // Implementation: + // + // Comb backwards through the row scratch registers, and turn any + // sequences of body lines of equivalent type abutted against the beginning + // or a body or start line of a different type into a crown paragraph. + void DowngradeWeakestToCrowns(int debug_level, + ParagraphTheory *theory, + GenericVector *rows) { + int start; + for (int end = rows->size(); end > 0; end = start) { + // Search back for a body line of a unique type. + const ParagraphModel *model = NULL; + while (end > 0 && + (model = (*rows)[end - 1].UniqueBodyHypothesis()) == NULL) { + end--; + } + if (end == 0) break; + start = end - 1; + while (start >= 0 && (*rows)[start].UniqueBodyHypothesis() == model) { + start--; // walk back to the first line that is not the same body type. + } + if (start >= 0 && (*rows)[start].UniqueStartHypothesis() == model && + StrongModel(model) && + NearlyEqual(model->first_indent(), model->body_indent(), + model->tolerance())) { + start--; + } + start++; + // Now rows[start, end) is a sequence of unique body hypotheses of model. + if (StrongModel(model) && model->justification() == JUSTIFICATION_CENTER) + continue; + if (!StrongModel(model)) { + while (start > 0 && + CrownCompatible(rows, start - 1, start, model)) + start--; + } + if (start == 0 || + (!StrongModel(model)) || + (StrongModel(model) && !ValidFirstLine(rows, start - 1, model))) { + // crownify rows[start, end) + const ParagraphModel *crown_model = model; + if (StrongModel(model)) { + if (model->justification() == JUSTIFICATION_LEFT) + crown_model = kCrownLeft; + else + crown_model = kCrownRight; + } + (*rows)[start].SetUnknown(); + (*rows)[start].AddStartLine(crown_model); + for (int row = start + 1; row < end; row++) { + (*rows)[row].SetUnknown(); + (*rows)[row].AddBodyLine(crown_model); + } + } + } + DiscardUnusedModels(*rows, theory); + } + + + // Clear all hypotheses about lines [start, end) and reset margins. + // + // The empty space between the left of a row and the block boundary (and + // similarly for the right) is split into two pieces: margin and indent. + // In initial processing, we assume the block is tight and the margin for + // all lines is set to zero. However, if our first pass does not yield + // models for everything, it may be due to an inset paragraph like a + // block-quote. In that case, we make a second pass over that unmarked + // section of the page and reset the "margin" portion of the empty space + // to the common amount of space at the ends of the lines under consid- + // eration. This would be equivalent to percentile set to 0. However, + // sometimes we have a single character sticking out in the right margin + // of a text block (like the 'r' in 'for' on line 3 above), and we can + // really just ignore it as an outlier. To express this, we allow the + // user to specify the percentile (0..100) of indent values to use as + // the common margin for each row in the run of rows[start, end). + void RecomputeMarginsAndClearHypotheses( + GenericVector *rows, int start, int end, + int percentile) { + if (!AcceptableRowArgs(0, 0, __func__, rows, start, end)) + return; + + int lmin, lmax, rmin, rmax; + lmin = lmax = (*rows)[start].lmargin_ + (*rows)[start].lindent_; + rmin = rmax = (*rows)[start].rmargin_ + (*rows)[start].rindent_; + for (int i = start; i < end; i++) { + RowScratchRegisters &sr = (*rows)[i]; + sr.SetUnknown(); + if (sr.ri_->num_words == 0) + continue; + UpdateRange(sr.lmargin_ + sr.lindent_, &lmin, &lmax); + UpdateRange(sr.rmargin_ + sr.rindent_, &rmin, &rmax); + } + STATS lefts(lmin, lmax + 1); + STATS rights(rmin, rmax + 1); + for (int i = start; i < end; i++) { + RowScratchRegisters &sr = (*rows)[i]; + if (sr.ri_->num_words == 0) + continue; + lefts.add(sr.lmargin_ + sr.lindent_, 1); + rights.add(sr.rmargin_ + sr.rindent_, 1); + } + int ignorable_left = lefts.ile(ClipToRange(percentile, 0, 100) / 100.0); + int ignorable_right = rights.ile(ClipToRange(percentile, 0, 100) / 100.0); + for (int i = start; i < end; i++) { + RowScratchRegisters &sr = (*rows)[i]; + int ldelta = ignorable_left - sr.lmargin_; + sr.lmargin_ += ldelta; + sr.lindent_ -= ldelta; + int rdelta = ignorable_right - sr.rmargin_; + sr.rmargin_ += rdelta; + sr.rindent_ -= rdelta; + } + } + + // Return the median inter-word space in rows[row_start, row_end). + int InterwordSpace(const GenericVector &rows, + int row_start, int row_end) { + if (row_end < row_start + 1) return 1; + int word_height = (rows[row_start].ri_->lword_box.height() + + rows[row_end - 1].ri_->lword_box.height()) / 2; + int word_width = (rows[row_start].ri_->lword_box.width() + + rows[row_end - 1].ri_->lword_box.width()) / 2; + STATS spacing_widths(0, 5 + word_width); + for (int i = row_start; i < row_end; i++) { + if (rows[i].ri_->num_words > 1) { + spacing_widths.add(rows[i].ri_->average_interword_space, 1); + } + } + int minimum_reasonable_space = word_height / 3; + if (minimum_reasonable_space < 2) + minimum_reasonable_space = 2; + int median = spacing_widths.median(); + return (median > minimum_reasonable_space) + ? median : minimum_reasonable_space; + } + + // Return whether the first word on the after line can fit in the space at + // the end of the before line (knowing which way the text is aligned and read). + bool FirstWordWouldHaveFit(const RowScratchRegisters &before, + const RowScratchRegisters &after, + tesseract::ParagraphJustification justification) { + if (before.ri_->num_words == 0 || after.ri_->num_words == 0) + return true; + + if (justification == JUSTIFICATION_UNKNOWN) { + tprintf("Don't call FirstWordWouldHaveFit(r, s, JUSTIFICATION_UNKNOWN).\n"); + } + int available_space; + if (justification == JUSTIFICATION_CENTER) { + available_space = before.lindent_ + before.rindent_; + } + else { + available_space = before.OffsideIndent(justification); + } + available_space -= before.ri_->average_interword_space; + + if (before.ri_->ltr) + return after.ri_->lword_box.width() < available_space; + return after.ri_->rword_box.width() < available_space; + } + + // Return whether the first word on the after line can fit in the space at + // the end of the before line (not knowing which way the text goes) in a left + // or right alignemnt. + bool FirstWordWouldHaveFit(const RowScratchRegisters &before, + const RowScratchRegisters &after) { + if (before.ri_->num_words == 0 || after.ri_->num_words == 0) + return true; + + int available_space = before.lindent_; + if (before.rindent_ > available_space) + available_space = before.rindent_; + available_space -= before.ri_->average_interword_space; + + if (before.ri_->ltr) + return after.ri_->lword_box.width() < available_space; + return after.ri_->rword_box.width() < available_space; + } + + bool TextSupportsBreak(const RowScratchRegisters &before, + const RowScratchRegisters &after) { + if (before.ri_->ltr) { + return before.ri_->rword_likely_ends_idea && + after.ri_->lword_likely_starts_idea; + } + else { + return before.ri_->lword_likely_ends_idea && + after.ri_->rword_likely_starts_idea; + } + } + + bool LikelyParagraphStart(const RowScratchRegisters &before, + const RowScratchRegisters &after) { + return before.ri_->num_words == 0 || + (FirstWordWouldHaveFit(before, after) && + TextSupportsBreak(before, after)); + } + + bool LikelyParagraphStart(const RowScratchRegisters &before, + const RowScratchRegisters &after, + tesseract::ParagraphJustification j) { + return before.ri_->num_words == 0 || + (FirstWordWouldHaveFit(before, after, j) && + TextSupportsBreak(before, after)); + } + + // Examine rows[start, end) and try to determine what sort of ParagraphModel + // would fit them as a single paragraph. + // If we can't produce a unique model justification_ = JUSTIFICATION_UNKNOWN. + // If the rows given could be a consistent start to a paragraph, set *consistent + // true. + ParagraphModel InternalParagraphModelByOutline( + const GenericVector *rows, + int start, int end, int tolerance, bool *consistent) { + int ltr_line_count = 0; + for (int i = start; i < end; i++) { + ltr_line_count += static_cast((*rows)[i].ri_->ltr); + } + bool ltr = (ltr_line_count >= (end - start) / 2); + + *consistent = true; + if (!AcceptableRowArgs(0, 2, __func__, rows, start, end)) + return ParagraphModel(); + + // Ensure the caller only passed us a region with a common rmargin and + // lmargin. + int lmargin = (*rows)[start].lmargin_; + int rmargin = (*rows)[start].rmargin_; + int lmin, lmax, rmin, rmax, cmin, cmax; + lmin = lmax = (*rows)[start + 1].lindent_; + rmin = rmax = (*rows)[start + 1].rindent_; + cmin = cmax = 0; + for (int i = start + 1; i < end; i++) { + if ((*rows)[i].lmargin_ != lmargin || (*rows)[i].rmargin_ != rmargin) { + tprintf("Margins don't match! Software error.\n"); + *consistent = false; + return ParagraphModel(); + } + UpdateRange((*rows)[i].lindent_, &lmin, &lmax); + UpdateRange((*rows)[i].rindent_, &rmin, &rmax); + UpdateRange((*rows)[i].rindent_ - (*rows)[i].lindent_, &cmin, &cmax); + } + int ldiff = lmax - lmin; + int rdiff = rmax - rmin; + int cdiff = cmax - cmin; + if (rdiff > tolerance && ldiff > tolerance) { + if (cdiff < tolerance * 2) { + if (end - start < 3) + return ParagraphModel(); + return ParagraphModel(JUSTIFICATION_CENTER, 0, 0, 0, tolerance); + } + *consistent = false; + return ParagraphModel(); + } + if (end - start < 3) // Don't return a model for two line paras. + return ParagraphModel(); + + // These booleans keep us from saying something is aligned left when the body + // left variance is too large. + bool body_admits_left_alignment = ldiff < tolerance; + bool body_admits_right_alignment = rdiff < tolerance; + + ParagraphModel left_model = + ParagraphModel(JUSTIFICATION_LEFT, lmargin, (*rows)[start].lindent_, + (lmin + lmax) / 2, tolerance); + ParagraphModel right_model = + ParagraphModel(JUSTIFICATION_RIGHT, rmargin, (*rows)[start].rindent_, + (rmin + rmax) / 2, tolerance); + + // These booleans keep us from having an indent on the "wrong side" for the + // first line. + bool text_admits_left_alignment = ltr || left_model.is_flush(); + bool text_admits_right_alignment = !ltr || right_model.is_flush(); + + // At least one of the edges is less than tolerance in variance. + // If the other is obviously ragged, it can't be the one aligned to. + // [Note the last line is included in this raggedness.] + if (tolerance < rdiff) { + if (body_admits_left_alignment && text_admits_left_alignment) + return left_model; + *consistent = false; + return ParagraphModel(); + } + if (tolerance < ldiff) { + if (body_admits_right_alignment && text_admits_right_alignment) + return right_model; + *consistent = false; + return ParagraphModel(); + } + + // At this point, we know the body text doesn't vary much on either side. + + // If the first line juts out oddly in one direction or the other, + // that likely indicates the side aligned to. + int first_left = (*rows)[start].lindent_; + int first_right = (*rows)[start].rindent_; + + if (ltr && body_admits_left_alignment && + (first_left < lmin || first_left > lmax)) + return left_model; + if (!ltr && body_admits_right_alignment && + (first_right < rmin || first_right > rmax)) + return right_model; + + *consistent = false; + return ParagraphModel(); + } + + // Examine rows[start, end) and try to determine what sort of ParagraphModel + // would fit them as a single paragraph. If nothing fits, + // justification_ = JUSTIFICATION_UNKNOWN and print the paragraph to debug + // output if we're debugging. + ParagraphModel ParagraphModelByOutline( + int debug_level, + const GenericVector *rows, + int start, int end, int tolerance) { + bool unused_consistent; + ParagraphModel retval = InternalParagraphModelByOutline( + rows, start, end, tolerance, &unused_consistent); + if (debug_level >= 2 && retval.justification() == JUSTIFICATION_UNKNOWN) { + tprintf("Could not determine a model for this paragraph:\n"); + PrintRowRange(*rows, start, end); + } + return retval; + } + + // Do rows[start, end) form a single instance of the given paragraph model? + bool RowsFitModel(const GenericVector *rows, + int start, int end, const ParagraphModel *model) { + if (!AcceptableRowArgs(0, 1, __func__, rows, start, end)) + return false; + if (!ValidFirstLine(rows, start, model)) return false; + for (int i = start + 1; i < end; i++) { + if (!ValidBodyLine(rows, i, model)) return false; + } + return true; + } + + // Examine rows[row_start, row_end) as an independent section of text, + // and mark rows that are exceptionally clear as start-of-paragraph + // and paragraph-body lines. + // + // We presume that any lines surrounding rows[row_start, row_end) may + // have wildly different paragraph models, so we don't key any data off + // of those lines. + // + // We only take the very strongest signals, as we don't want to get + // confused and marking up centered text, poetry, or source code as + // clearly part of a typical paragraph. + void MarkStrongEvidence(GenericVector *rows, + int row_start, int row_end) { + // Record patently obvious body text. + for (int i = row_start + 1; i < row_end; i++) { + const RowScratchRegisters &prev = (*rows)[i - 1]; + RowScratchRegisters &curr = (*rows)[i]; + tesseract::ParagraphJustification typical_justification = + prev.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; + if (!curr.ri_->rword_likely_starts_idea && + !curr.ri_->lword_likely_starts_idea && + !FirstWordWouldHaveFit(prev, curr, typical_justification)) { + curr.SetBodyLine(); + } + } + + // Record patently obvious start paragraph lines. + // + // It's an extremely good signal of the start of a paragraph that + // the first word would have fit on the end of the previous line. + // However, applying just that signal would have us mark random + // start lines of lineated text (poetry and source code) and some + // centered headings as paragraph start lines. Therefore, we use + // a second qualification for a paragraph start: Not only should + // the first word of this line have fit on the previous line, + // but also, this line should go full to the right of the block, + // disallowing a subsequent word from having fit on this line. + + // First row: + { + RowScratchRegisters &curr = (*rows)[row_start]; + RowScratchRegisters &next = (*rows)[row_start + 1]; + tesseract::ParagraphJustification j = + curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; + if (curr.GetLineType() == LT_UNKNOWN && + !FirstWordWouldHaveFit(curr, next, j) && + (curr.ri_->lword_likely_starts_idea || + curr.ri_->rword_likely_starts_idea)) { + curr.SetStartLine(); + } + } + // Middle rows + for (int i = row_start + 1; i < row_end - 1; i++) { + RowScratchRegisters &prev = (*rows)[i - 1]; + RowScratchRegisters &curr = (*rows)[i]; + RowScratchRegisters &next = (*rows)[i + 1]; + tesseract::ParagraphJustification j = + curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; + if (curr.GetLineType() == LT_UNKNOWN && + !FirstWordWouldHaveFit(curr, next, j) && + LikelyParagraphStart(prev, curr, j)) { + curr.SetStartLine(); + } + } + // Last row + { // the short circuit at the top means we have at least two lines. + RowScratchRegisters &prev = (*rows)[row_end - 2]; + RowScratchRegisters &curr = (*rows)[row_end - 1]; + tesseract::ParagraphJustification j = + curr.ri_->ltr ? JUSTIFICATION_LEFT : JUSTIFICATION_RIGHT; + if (curr.GetLineType() == LT_UNKNOWN && + !FirstWordWouldHaveFit(curr, curr, j) && + LikelyParagraphStart(prev, curr, j)) { + curr.SetStartLine(); + } + } + } + + // Look for sequences of a start line followed by some body lines in + // rows[row_start, row_end) and create ParagraphModels for them if + // they seem coherent. + void ModelStrongEvidence(int debug_level, + GenericVector *rows, + int row_start, int row_end, + bool allow_flush_models, + ParagraphTheory *theory) { + if (!AcceptableRowArgs(debug_level, 2, __func__, rows, row_start, row_end)) + return; + + int start = row_start; + while (start < row_end) { + while (start < row_end && (*rows)[start].GetLineType() != LT_START) + start++; + if (start >= row_end - 1) + break; + + int tolerance = Epsilon((*rows)[start + 1].ri_->average_interword_space); + int end = start; + ParagraphModel last_model; + bool next_consistent; + do { + ++end; + // rows[row, end) was consistent. + // If rows[row, end + 1) is not consistent, + // just model rows[row, end) + if (end < row_end - 1) { + RowScratchRegisters &next = (*rows)[end]; + LineType lt = next.GetLineType(); + next_consistent = lt == LT_BODY || + (lt == LT_UNKNOWN && + !FirstWordWouldHaveFit((*rows)[end - 1], (*rows)[end])); + } + else { + next_consistent = false; + } + if (next_consistent) { + ParagraphModel next_model = InternalParagraphModelByOutline( + rows, start, end + 1, tolerance, &next_consistent); + if (((*rows)[start].ri_->ltr && + last_model.justification() == JUSTIFICATION_LEFT && + next_model.justification() != JUSTIFICATION_LEFT) || + (!(*rows)[start].ri_->ltr && + last_model.justification() == JUSTIFICATION_RIGHT && + next_model.justification() != JUSTIFICATION_RIGHT)) { + next_consistent = false; + } + last_model = next_model; + } + else { + next_consistent = false; + } + } while (next_consistent && end < row_end); + // At this point, rows[start, end) looked like it could have been a + // single paragraph. If we can make a good ParagraphModel for it, + // do so and mark this sequence with that model. + if (end > start + 1) { + // emit a new paragraph if we have more than one line. + const ParagraphModel *model = NULL; + ParagraphModel new_model = ParagraphModelByOutline( + debug_level, rows, start, end, + Epsilon(InterwordSpace(*rows, start, end))); + if (new_model.justification() == JUSTIFICATION_UNKNOWN) { + // couldn't create a good model, oh well. + } + else if (new_model.is_flush()) { + if (end == start + 2) { + // It's very likely we just got two paragraph starts in a row. + end = start + 1; + } + else if (start == row_start) { + // Mark this as a Crown. + if (new_model.justification() == JUSTIFICATION_LEFT) { + model = kCrownLeft; + } + else { + model = kCrownRight; + } + } + else if (allow_flush_models) { + model = theory->AddModel(new_model); + } + } + else { + model = theory->AddModel(new_model); + } + if (model) { + (*rows)[start].AddStartLine(model); + for (int i = start + 1; i < end; i++) { + (*rows)[i].AddBodyLine(model); + } + } + } + start = end; + } + } + + // We examine rows[row_start, row_end) and do the following: + // (1) Clear all existing hypotheses for the rows being considered. + // (2) Mark up any rows as exceptionally likely to be paragraph starts + // or paragraph body lines as such using both geometric and textual + // clues. + // (3) Form models for any sequence of start + continuation lines. + // (4) Smear the paragraph models to cover surrounding text. + void StrongEvidenceClassify(int debug_level, + GenericVector *rows, + int row_start, int row_end, + ParagraphTheory *theory) { + if (!AcceptableRowArgs(debug_level, 2, __func__, rows, row_start, row_end)) + return; + + if (debug_level > 1) { + tprintf("#############################################\n"); + tprintf("# StrongEvidenceClassify( rows[%d:%d) )\n", row_start, row_end); + tprintf("#############################################\n"); + } + + RecomputeMarginsAndClearHypotheses(rows, row_start, row_end, 10); + MarkStrongEvidence(rows, row_start, row_end); + + DebugDump(debug_level > 2, "Initial strong signals.", *theory, *rows); + + // Create paragraph models. + ModelStrongEvidence(debug_level, rows, row_start, row_end, false, theory); + + DebugDump(debug_level > 2, "Unsmeared hypotheses.s.", *theory, *rows); + + // At this point, some rows are marked up as paragraphs with model numbers, + // and some rows are marked up as either LT_START or LT_BODY. Now let's + // smear any good paragraph hypotheses forward and backward. + ParagraphModelSmearer smearer(rows, row_start, row_end, theory); + smearer.Smear(); + } + + void SeparateSimpleLeaderLines(GenericVector *rows, + int row_start, int row_end, + ParagraphTheory *theory) { + for (int i = row_start + 1; i < row_end - 1; i++) { + if ((*rows)[i - 1].ri_->has_leaders && + (*rows)[i].ri_->has_leaders && + (*rows)[i + 1].ri_->has_leaders) { + const ParagraphModel *model = theory->AddModel( + ParagraphModel(JUSTIFICATION_UNKNOWN, 0, 0, 0, 0)); + (*rows)[i].AddStartLine(model); + } + } + } + + // Collect sequences of unique hypotheses in row registers and create proper + // paragraphs for them, referencing the paragraphs in row_owners. + void ConvertHypothesizedModelRunsToParagraphs( + int debug_level, + const GenericVector &rows, + GenericVector *row_owners, + ParagraphTheory *theory) { + int end = rows.size(); + int start; + for (; end > 0; end = start) { + start = end - 1; + const ParagraphModel *model = NULL; + // TODO(eger): Be smarter about dealing with multiple hypotheses. + bool single_line_paragraph = false; + SetOfModels models; + rows[start].NonNullHypotheses(&models); + if (!models.empty()) { + model = models[0]; + if (rows[start].GetLineType(model) != LT_BODY) + single_line_paragraph = true; + } + if (model && !single_line_paragraph) { + // walk back looking for more body lines and then a start line. + while (--start > 0 && rows[start].GetLineType(model) == LT_BODY) { + // do nothing + } + if (start < 0 || rows[start].GetLineType(model) != LT_START) { + model = NULL; + } + } + if (model == NULL) { + continue; + } + // rows[start, end) should be a paragraph. + PARA *p = new PARA(); + if (model == kCrownLeft || model == kCrownRight) { + p->is_very_first_or_continuation = true; + // Crown paragraph. + // If we can find an existing ParagraphModel that fits, use it, + // else create a new one. + for (int row = end; row < rows.size(); row++) { + if ((*row_owners)[row] && + (ValidBodyLine(&rows, start, (*row_owners)[row]->model) && + (start == 0 || + ValidFirstLine(&rows, start, (*row_owners)[row]->model)))) { + model = (*row_owners)[row]->model; + break; + } + } + if (model == kCrownLeft) { + // No subsequent model fits, so cons one up. + model = theory->AddModel(ParagraphModel( + JUSTIFICATION_LEFT, rows[start].lmargin_ + rows[start].lindent_, + 0, 0, Epsilon(rows[start].ri_->average_interword_space))); + } + else if (model == kCrownRight) { + // No subsequent model fits, so cons one up. + model = theory->AddModel(ParagraphModel( + JUSTIFICATION_RIGHT, rows[start].rmargin_ + rows[start].rmargin_, + 0, 0, Epsilon(rows[start].ri_->average_interword_space))); + } + } + rows[start].SetUnknown(); + rows[start].AddStartLine(model); + for (int i = start + 1; i < end; i++) { + rows[i].SetUnknown(); + rows[i].AddBodyLine(model); + } + p->model = model; + p->has_drop_cap = rows[start].ri_->has_drop_cap; + p->is_list_item = + model->justification() == JUSTIFICATION_RIGHT + ? rows[start].ri_->rword_indicates_list_item + : rows[start].ri_->lword_indicates_list_item; + for (int row = start; row < end; row++) { + if ((*row_owners)[row] != NULL) { + tprintf("Memory leak! ConvertHypothesizeModelRunsToParagraphs() called " + "more than once!\n"); + delete (*row_owners)[row]; + } + (*row_owners)[row] = p; + } + } + } + + struct Interval { + Interval() : begin(0), end(0) {} + Interval(int b, int e) : begin(b), end(e) {} + + int begin; + int end; + }; + + // Return whether rows[row] appears to be stranded, meaning that the evidence + // for this row is very weak due to context. For instance, two lines of source + // code may happen to be indented at the same tab vector as body text starts, + // leading us to think they are two start-of-paragraph lines. This is not + // optimal. However, we also don't want to mark a sequence of short dialog + // as "weak," so our heuristic is: + // (1) If a line is surrounded by lines of unknown type, it's weak. + // (2) If two lines in a row are start lines for a given paragraph type, but + // after that the same paragraph type does not continue, they're weak. + bool RowIsStranded(const GenericVector &rows, int row) { + SetOfModels row_models; + rows[row].StrongHypotheses(&row_models); + + for (int m = 0; m < row_models.size(); m++) { + bool all_starts = rows[row].GetLineType(); + int run_length = 1; + bool continues = true; + for (int i = row - 1; i >= 0 && continues; i--) { + SetOfModels models; + rows[i].NonNullHypotheses(&models); + switch (rows[i].GetLineType(row_models[m])) { + case LT_START: run_length++; break; + case LT_MULTIPLE: // explicit fall-through + case LT_BODY: run_length++; all_starts = false; break; + case LT_UNKNOWN: // explicit fall-through + default: continues = false; + } + } + continues = true; + for (int i = row + 1; i < rows.size() && continues; i++) { + SetOfModels models; + rows[i].NonNullHypotheses(&models); + switch (rows[i].GetLineType(row_models[m])) { + case LT_START: run_length++; break; + case LT_MULTIPLE: // explicit fall-through + case LT_BODY: run_length++; all_starts = false; break; + case LT_UNKNOWN: // explicit fall-through + default: continues = false; + } + } + if (run_length > 2 || (!all_starts && run_length > 1)) return false; + } + return true; + } + + // Go through rows[row_start, row_end) and gather up sequences that need better + // classification. + // + Sequences of non-empty rows without hypotheses. + // + Crown paragraphs not immediately followed by a strongly modeled line. + // + Single line paragraphs surrounded by text that doesn't match the + // model. + void LeftoverSegments(const GenericVector &rows, + GenericVector *to_fix, + int row_start, int row_end) { + to_fix->clear(); + for (int i = row_start; i < row_end; i++) { + bool needs_fixing = false; + + SetOfModels models; + SetOfModels models_w_crowns; + rows[i].StrongHypotheses(&models); + rows[i].NonNullHypotheses(&models_w_crowns); + if (models.empty() && !models_w_crowns.empty()) { + // Crown paragraph. Is it followed by a modeled line? + for (int end = i + 1; end < rows.size(); end++) { + SetOfModels end_models; + SetOfModels strong_end_models; + rows[end].NonNullHypotheses(&end_models); + rows[end].StrongHypotheses(&strong_end_models); + if (end_models.empty()) { + needs_fixing = true; + break; + } + else if (!strong_end_models.empty()) { + needs_fixing = false; + break; + } + } + } + else if (models.empty() && rows[i].ri_->num_words > 0) { + // No models at all. + needs_fixing = true; + } + + if (!needs_fixing && !models.empty()) { + needs_fixing = RowIsStranded(rows, i); + } + + if (needs_fixing) { + if (!to_fix->empty() && to_fix->back().end == i - 1) + to_fix->back().end = i; + else + to_fix->push_back(Interval(i, i)); + } + } + // Convert inclusive intervals to half-open intervals. + for (int i = 0; i < to_fix->size(); i++) { + (*to_fix)[i].end = (*to_fix)[i].end + 1; + } + } + + // Given a set of row_owners pointing to PARAs or NULL (no paragraph known), + // normalize each row_owner to point to an actual PARA, and output the + // paragraphs in order onto paragraphs. + void CanonicalizeDetectionResults( + GenericVector *row_owners, + PARA_LIST *paragraphs) { + GenericVector &rows = *row_owners; + paragraphs->clear(); + PARA_IT out(paragraphs); + PARA *formerly_null = NULL; + for (int i = 0; i < rows.size(); i++) { + if (rows[i] == NULL) { + if (i == 0 || rows[i - 1] != formerly_null) { + rows[i] = formerly_null = new PARA(); + } + else { + rows[i] = formerly_null; + continue; + } + } + else if (i > 0 && rows[i - 1] == rows[i]) { + continue; + } + out.add_after_then_move(rows[i]); + } + } + + // Main entry point for Paragraph Detection Algorithm. + // + // Given a set of equally spaced textlines (described by row_infos), + // Split them into paragraphs. + // + // Output: + // row_owners - one pointer for each row, to the paragraph it belongs to. + // paragraphs - this is the actual list of PARA objects. + // models - the list of paragraph models referenced by the PARA objects. + // caller is responsible for deleting the models. + void DetectParagraphs(int debug_level, + GenericVector *row_infos, + GenericVector *row_owners, + PARA_LIST *paragraphs, + GenericVector *models) { + GenericVector rows; + ParagraphTheory theory(models); + + // Initialize row_owners to be a bunch of NULL pointers. + row_owners->init_to_size(row_infos->size(), NULL); + + // Set up row scratch registers for the main algorithm. + rows.init_to_size(row_infos->size(), RowScratchRegisters()); + for (int i = 0; i < row_infos->size(); i++) { + rows[i].Init((*row_infos)[i]); + } + + // Pass 1: + // Detect sequences of lines that all contain leader dots (.....) + // These are likely Tables of Contents. If there are three text lines in + // a row with leader dots, it's pretty safe to say the middle one should + // be a paragraph of its own. + SeparateSimpleLeaderLines(&rows, 0, rows.size(), &theory); + + DebugDump(debug_level > 1, "End of Pass 1", theory, rows); + + GenericVector leftovers; + LeftoverSegments(rows, &leftovers, 0, rows.size()); + for (int i = 0; i < leftovers.size(); i++) { + // Pass 2a: + // Find any strongly evidenced start-of-paragraph lines. If they're + // followed by two lines that look like body lines, make a paragraph + // model for that and see if that model applies throughout the text + // (that is, "smear" it). + StrongEvidenceClassify(debug_level, &rows, + leftovers[i].begin, leftovers[i].end, &theory); + + // Pass 2b: + // If we had any luck in pass 2a, we got part of the page and didn't + // know how to classify a few runs of rows. Take the segments that + // didn't find a model and reprocess them individually. + GenericVector leftovers2; + LeftoverSegments(rows, &leftovers2, leftovers[i].begin, leftovers[i].end); + bool pass2a_was_useful = leftovers2.size() > 1 || + (leftovers2.size() == 1 && + (leftovers2[0].begin != 0 || leftovers2[0].end != rows.size())); + if (pass2a_was_useful) { + for (int j = 0; j < leftovers2.size(); j++) { + StrongEvidenceClassify(debug_level, &rows, + leftovers2[j].begin, leftovers2[j].end, + &theory); + } + } + } + + DebugDump(debug_level > 1, "End of Pass 2", theory, rows); + + // Pass 3: + // These are the dregs for which we didn't have enough strong textual + // and geometric clues to form matching models for. Let's see if + // the geometric clues are simple enough that we could just use those. + LeftoverSegments(rows, &leftovers, 0, rows.size()); + for (int i = 0; i < leftovers.size(); i++) { + GeometricClassify(debug_level, &rows, + leftovers[i].begin, leftovers[i].end, &theory); + } + + // Undo any flush models for which there's little evidence. + DowngradeWeakestToCrowns(debug_level, &theory, &rows); + + DebugDump(debug_level > 1, "End of Pass 3", theory, rows); + + // Pass 4: + // Take everything that's still not marked up well and clear all markings. + LeftoverSegments(rows, &leftovers, 0, rows.size()); + for (int i = 0; i < leftovers.size(); i++) { + for (int j = leftovers[i].begin; j < leftovers[i].end; j++) { + rows[j].SetUnknown(); + } + } + + DebugDump(debug_level > 1, "End of Pass 4", theory, rows); + + // Convert all of the unique hypothesis runs to PARAs. + ConvertHypothesizedModelRunsToParagraphs(debug_level, rows, row_owners, + &theory); + + DebugDump(debug_level > 0, "Final Paragraph Segmentation", theory, rows); + + // Finally, clean up any dangling NULL row paragraph parents. + CanonicalizeDetectionResults(row_owners, paragraphs); + } + + // ============ Code interfacing with the rest of Tesseract ================== + + void InitializeTextAndBoxesPreRecognition(const MutableIterator &it, + RowInfo *info) { + // Set up text, lword_text, and rword_text (mostly for debug printing). + STRING fake_text; + PageIterator pit(static_cast(it)); + bool first_word = true; + if (!pit.Empty(RIL_WORD)) { + do { + fake_text += "x"; + if (first_word) info->lword_text += "x"; + info->rword_text += "x"; + if (pit.IsAtFinalElement(RIL_WORD, RIL_SYMBOL) && + !pit.IsAtFinalElement(RIL_TEXTLINE, RIL_SYMBOL)) { + fake_text += " "; + info->rword_text = ""; + first_word = false; + } + } while (!pit.IsAtFinalElement(RIL_TEXTLINE, RIL_SYMBOL) && + pit.Next(RIL_SYMBOL)); + } + if (fake_text.size() == 0) return; + + int lspaces = info->pix_ldistance / info->average_interword_space; + for (int i = 0; i < lspaces; i++) { + info->text += ' '; + } + info->text += fake_text; + + // Set up lword_box, rword_box, and num_words. + PAGE_RES_IT page_res_it = *it.PageResIt(); + WERD_RES *word_res = page_res_it.restart_row(); + ROW_RES *this_row = page_res_it.row(); + + WERD_RES *lword = NULL; + WERD_RES *rword = NULL; + info->num_words = 0; + do { + if (word_res) { + if (!lword) lword = word_res; + if (rword != word_res) info->num_words++; + rword = word_res; + } + word_res = page_res_it.forward(); + } while (page_res_it.row() == this_row); + + if (lword) info->lword_box = lword->word->bounding_box(); + if (rword) info->rword_box = rword->word->bounding_box(); + } + + + // Given a Tesseract Iterator pointing to a text line, fill in the paragraph + // detector RowInfo with all relevant information from the row. + void InitializeRowInfo(bool after_recognition, + const MutableIterator &it, + RowInfo *info) { + if (it.PageResIt()->row() != NULL) { + ROW *row = it.PageResIt()->row()->row; + info->pix_ldistance = row->lmargin(); + info->pix_rdistance = row->rmargin(); + info->average_interword_space = + row->space() > 0 ? row->space() : MAX(row->x_height(), 1); + info->pix_xheight = row->x_height(); + info->has_leaders = false; + info->has_drop_cap = row->has_drop_cap(); + info->ltr = true; // set below depending on word scripts + } + else { + info->pix_ldistance = info->pix_rdistance = 0; + info->average_interword_space = 1; + info->pix_xheight = 1.0; + info->has_leaders = false; + info->has_drop_cap = false; + info->ltr = true; + } + + info->num_words = 0; + info->lword_indicates_list_item = false; + info->lword_likely_starts_idea = false; + info->lword_likely_ends_idea = false; + info->rword_indicates_list_item = false; + info->rword_likely_starts_idea = false; + info->rword_likely_ends_idea = false; + info->has_leaders = false; + info->ltr = 1; + + if (!after_recognition) { + InitializeTextAndBoxesPreRecognition(it, info); + return; + } + info->text = ""; + char *text = it.GetUTF8Text(RIL_TEXTLINE); + int trailing_ws_idx = strlen(text); // strip trailing space + while (trailing_ws_idx > 0 && + // isspace() only takes ASCII + ((text[trailing_ws_idx - 1] & 0x80) == 0) && + isspace(text[trailing_ws_idx - 1])) + trailing_ws_idx--; + if (trailing_ws_idx > 0) { + int lspaces = info->pix_ldistance / info->average_interword_space; + for (int i = 0; i < lspaces; i++) + info->text += ' '; + for (int i = 0; i < trailing_ws_idx; i++) + info->text += text[i]; + } + delete[]text; + + if (info->text.size() == 0) { + return; + } + + PAGE_RES_IT page_res_it = *it.PageResIt(); + GenericVector werds; + WERD_RES *word_res = page_res_it.restart_row(); + ROW_RES *this_row = page_res_it.row(); + int num_leaders = 0; + int ltr = 0; + int rtl = 0; + do { + if (word_res && word_res->best_choice->unichar_string().length() > 0) { + werds.push_back(word_res); + ltr += word_res->AnyLtrCharsInWord() ? 1 : 0; + rtl += word_res->AnyRtlCharsInWord() ? 1 : 0; + if (word_res->word->flag(W_REP_CHAR)) num_leaders++; + } + word_res = page_res_it.forward(); + } while (page_res_it.row() == this_row); + info->ltr = ltr >= rtl; + info->has_leaders = num_leaders > 3; + info->num_words = werds.size(); + if (!werds.empty()) { + WERD_RES *lword = werds[0], *rword = werds[werds.size() - 1]; + info->lword_text = lword->best_choice->unichar_string().string(); + info->rword_text = rword->best_choice->unichar_string().string(); + info->lword_box = lword->word->bounding_box(); + info->rword_box = rword->word->bounding_box(); + LeftWordAttributes(lword->uch_set, lword->best_choice, + info->lword_text, + &info->lword_indicates_list_item, + &info->lword_likely_starts_idea, + &info->lword_likely_ends_idea); + RightWordAttributes(rword->uch_set, rword->best_choice, + info->rword_text, + &info->rword_indicates_list_item, + &info->rword_likely_starts_idea, + &info->rword_likely_ends_idea); + } + } + + // This is called after rows have been identified and words are recognized. + // Much of this could be implemented before word recognition, but text helps + // to identify bulleted lists and gives good signals for sentence boundaries. + void DetectParagraphs(int debug_level, + bool after_text_recognition, + const MutableIterator *block_start, + GenericVector *models) { + // Clear out any preconceived notions. + if (block_start->Empty(RIL_TEXTLINE)) { + return; + } + BLOCK *block = block_start->PageResIt()->block()->block; + block->para_list()->clear(); + bool is_image_block = block->poly_block() && !block->poly_block()->IsText(); + + // Convert the Tesseract structures to RowInfos + // for the paragraph detection algorithm. + MutableIterator row(*block_start); + if (row.Empty(RIL_TEXTLINE)) + return; // end of input already. + + GenericVector row_infos; + do { + if (!row.PageResIt()->row()) + continue; // empty row. + row.PageResIt()->row()->row->set_para(NULL); + row_infos.push_back(RowInfo()); + RowInfo &ri = row_infos.back(); + InitializeRowInfo(after_text_recognition, row, &ri); + } while (!row.IsAtFinalElement(RIL_BLOCK, RIL_TEXTLINE) && + row.Next(RIL_TEXTLINE)); + + // If we're called before text recognition, we might not have + // tight block bounding boxes, so trim by the minimum on each side. + if (!row_infos.empty()) { + int min_lmargin = row_infos[0].pix_ldistance; + int min_rmargin = row_infos[0].pix_rdistance; + for (int i = 1; i < row_infos.size(); i++) { + if (row_infos[i].pix_ldistance < min_lmargin) + min_lmargin = row_infos[i].pix_ldistance; + if (row_infos[i].pix_rdistance < min_rmargin) + min_rmargin = row_infos[i].pix_rdistance; + } + if (min_lmargin > 0 || min_rmargin > 0) { + for (int i = 0; i < row_infos.size(); i++) { + row_infos[i].pix_ldistance -= min_lmargin; + row_infos[i].pix_rdistance -= min_rmargin; + } + } + } + + // Run the paragraph detection algorithm. + GenericVector row_owners; + GenericVector the_paragraphs; + if (!is_image_block) { + DetectParagraphs(debug_level, &row_infos, &row_owners, block->para_list(), + models); + } + else { + row_owners.init_to_size(row_infos.size(), NULL); + CanonicalizeDetectionResults(&row_owners, block->para_list()); + } + + // Now stitch in the row_owners into the rows. + row = *block_start; + for (int i = 0; i < row_owners.size(); i++) { + while (!row.PageResIt()->row()) + row.Next(RIL_TEXTLINE); + row.PageResIt()->row()->row->set_para(row_owners[i]); + row.Next(RIL_TEXTLINE); + } + } + +} // namespace diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.h b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.h new file mode 100644 index 0000000..f022b73 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs.h @@ -0,0 +1,108 @@ +/********************************************************************** + * File: paragraphs.h + * Description: Paragraph Detection data structures. + * Author: David Eger + * Created: 25 February 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_CCMAIN_PARAGRAPHS_H_ +#define TESSERACT_CCMAIN_PARAGRAPHS_H_ + +#include "rect.h" +#include "ocrpara.h" +#include "genericvector.h" +#include "strngs.h" + + +class WERD; +class UNICHARSET; + +namespace tesseract { + + class MutableIterator; + + // This structure captures all information needed about a text line for the + // purposes of paragraph detection. It is meant to be exceedingly light-weight + // so that we can easily test paragraph detection independent of the rest of + // Tesseract. + class RowInfo { + public: + // Constant data derived from Tesseract output. + STRING text; // the full UTF-8 text of the line. + bool ltr; // whether the majority of the text is left-to-right + // TODO(eger) make this more fine-grained. + + bool has_leaders; // does the line contain leader dots (.....)? + bool has_drop_cap; // does the line have a drop cap? + int pix_ldistance; // distance to the left pblock boundary in pixels + int pix_rdistance; // distance to the right pblock boundary in pixels + float pix_xheight; // guessed xheight for the line + int average_interword_space; // average space between words in pixels. + + int num_words; + TBOX lword_box; // in normalized (horiz text rows) space + TBOX rword_box; // in normalized (horiz text rows) space + + STRING lword_text; // the UTF-8 text of the leftmost werd + STRING rword_text; // the UTF-8 text of the rightmost werd + + // The text of a paragraph typically starts with the start of an idea and + // ends with the end of an idea. Here we define paragraph as something that + // may have a first line indent and a body indent which may be different. + // Typical words that start an idea are: + // 1. Words in western scripts that start with + // a capital letter, for example "The" + // 2. Bulleted or numbered list items, for + // example "2." + // Typical words which end an idea are words ending in punctuation marks. In + // this vocabulary, each list item is represented as a paragraph. + bool lword_indicates_list_item; + bool lword_likely_starts_idea; + bool lword_likely_ends_idea; + + bool rword_indicates_list_item; + bool rword_likely_starts_idea; + bool rword_likely_ends_idea; + }; + + // Main entry point for Paragraph Detection Algorithm. + // + // Given a set of equally spaced textlines (described by row_infos), + // Split them into paragraphs. See http://goto/paragraphstalk + // + // Output: + // row_owners - one pointer for each row, to the paragraph it belongs to. + // paragraphs - this is the actual list of PARA objects. + // models - the list of paragraph models referenced by the PARA objects. + // caller is responsible for deleting the models. + void DetectParagraphs(int debug_level, + GenericVector *row_infos, + GenericVector *row_owners, + PARA_LIST *paragraphs, + GenericVector *models); + + // Given a MutableIterator to the start of a block, run DetectParagraphs on + // that block and commit the results to the underlying ROW and BLOCK structs, + // saving the ParagraphModels in models. Caller owns the models. + // We use unicharset during the function to answer questions such as "is the + // first letter of this word upper case?" + void DetectParagraphs(int debug_level, + bool after_text_recognition, + const MutableIterator *block_start, + GenericVector *models); + +} // namespace + +#endif // TESSERACT_CCMAIN_PARAGRAPHS_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs_internal.h b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs_internal.h new file mode 100644 index 0000000..83db2e2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/paragraphs_internal.h @@ -0,0 +1,312 @@ +/********************************************************************** + * File: paragraphs.h + * Description: Paragraph Detection internal data structures. + * Author: David Eger + * Created: 11 March 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_CCMAIN_PARAGRAPHS_INTERNAL_H_ +#define TESSERACT_CCMAIN_PARAGRAPHS_INTERNAL_H_ + +#include "paragraphs.h" +#ifdef _MSC_VER +#include +#else +#include "strings.h" +#endif + + // NO CODE OUTSIDE OF paragraphs.cpp AND TESTS SHOULD NEED TO ACCESS + // DATA STRUCTURES OR FUNCTIONS IN THIS FILE. + +class WERD_CHOICE; + +namespace tesseract { + + // Return whether the given word is likely to be a list item start word. + bool AsciiLikelyListItem(const STRING &word); + + // Return the first Unicode Codepoint from werd[pos]. + int UnicodeFor(const UNICHARSET *u, const WERD_CHOICE *werd, int pos); + + // Set right word attributes given either a unicharset and werd or a utf8 + // string. + void RightWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, + const STRING &utf8, + bool *is_list, bool *starts_idea, bool *ends_idea); + + // Set left word attributes given either a unicharset and werd or a utf8 string. + void LeftWordAttributes(const UNICHARSET *unicharset, const WERD_CHOICE *werd, + const STRING &utf8, + bool *is_list, bool *starts_idea, bool *ends_idea); + + enum LineType { + LT_START = 'S', // First line of a paragraph. + LT_BODY = 'C', // Continuation line of a paragraph. + LT_UNKNOWN = 'U', // No clues. + LT_MULTIPLE = 'M', // Matches for both LT_START and LT_BODY. + }; + + // The first paragraph in a page of body text is often un-indented. + // This is a typographic convention which is common to indicate either that: + // (1) The paragraph is the continuation of a previous paragraph, or + // (2) The paragraph is the first paragraph in a chapter. + // + // I refer to such paragraphs as "crown"s, and the output of the paragraph + // detection algorithm attempts to give them the same paragraph model as + // the rest of the body text. + // + // Nonetheless, while building hypotheses, it is useful to mark the lines + // of crown paragraphs temporarily as crowns, either aligned left or right. + extern const ParagraphModel *kCrownLeft; + extern const ParagraphModel *kCrownRight; + + inline bool StrongModel(const ParagraphModel *model) { + return model != NULL && model != kCrownLeft && model != kCrownRight; + } + + struct LineHypothesis { + LineHypothesis() : ty(LT_UNKNOWN), model(NULL) {} + LineHypothesis(LineType line_type, const ParagraphModel *m) + : ty(line_type), model(m) {} + LineHypothesis(const LineHypothesis &other) + : ty(other.ty), model(other.model) {} + + bool operator==(const LineHypothesis &other) const { + return ty == other.ty && model == other.model; + } + + LineType ty; + const ParagraphModel *model; + }; + + class ParagraphTheory; // Forward Declaration + + typedef GenericVectorEqEq SetOfModels; + + // Row Scratch Registers are data generated by the paragraph detection + // algorithm based on a RowInfo input. + class RowScratchRegisters { + public: + // We presume row will outlive us. + void Init(const RowInfo &row); + + LineType GetLineType() const; + + LineType GetLineType(const ParagraphModel *model) const; + + // Mark this as a start line type, sans model. This is useful for the + // initial marking of probable body lines or paragraph start lines. + void SetStartLine(); + + // Mark this as a body line type, sans model. This is useful for the + // initial marking of probably body lines or paragraph start lines. + void SetBodyLine(); + + // Record that this row fits as a paragraph start line in the given model, + void AddStartLine(const ParagraphModel *model); + // Record that this row fits as a paragraph body line in the given model, + void AddBodyLine(const ParagraphModel *model); + + // Clear all hypotheses about this line. + void SetUnknown() { hypotheses_.truncate(0); } + + // Append all hypotheses of strong models that match this row as a start. + void StartHypotheses(SetOfModels *models) const; + + // Append all hypotheses of strong models matching this row. + void StrongHypotheses(SetOfModels *models) const; + + // Append all hypotheses for this row. + void NonNullHypotheses(SetOfModels *models) const; + + // Discard any hypotheses whose model is not in the given list. + void DiscardNonMatchingHypotheses(const SetOfModels &models); + + // If we have only one hypothesis and that is that this line is a paragraph + // start line of a certain model, return that model. Else return NULL. + const ParagraphModel *UniqueStartHypothesis() const; + + // If we have only one hypothesis and that is that this line is a paragraph + // body line of a certain model, return that model. Else return NULL. + const ParagraphModel *UniqueBodyHypothesis() const; + + // Return the indentation for the side opposite of the aligned side. + int OffsideIndent(tesseract::ParagraphJustification just) const { + switch (just) { + case tesseract::JUSTIFICATION_RIGHT: return lindent_; + case tesseract::JUSTIFICATION_LEFT: return rindent_; + default: return lindent_ > rindent_ ? lindent_ : rindent_; + } + } + + // Return the indentation for the side the text is aligned to. + int AlignsideIndent(tesseract::ParagraphJustification just) const { + switch (just) { + case tesseract::JUSTIFICATION_RIGHT: return rindent_; + case tesseract::JUSTIFICATION_LEFT: return lindent_; + default: return lindent_ > rindent_ ? lindent_ : rindent_; + } + } + + // Append header fields to a vector of row headings. + static void AppendDebugHeaderFields(GenericVector *header); + + // Append data for this row to a vector of debug strings. + void AppendDebugInfo(const ParagraphTheory &theory, + GenericVector *dbg) const; + + const RowInfo *ri_; + + // These four constants form a horizontal box model for the white space + // on the edges of each line. At each point in the algorithm, the following + // shall hold: + // ri_->pix_ldistance = lmargin_ + lindent_ + // ri_->pix_rdistance = rindent_ + rmargin_ + int lmargin_; + int lindent_; + int rindent_; + int rmargin_; + + private: + // Hypotheses of either LT_START or LT_BODY + GenericVectorEqEq hypotheses_; + }; + + // A collection of convenience functions for wrapping the set of + // Paragraph Models we believe correctly model the paragraphs in the image. + class ParagraphTheory { + public: + // We presume models will outlive us, and that models will take ownership + // of any ParagraphModel *'s we add. + explicit ParagraphTheory(GenericVector *models) + : models_(models) {} + GenericVector &models() { return *models_; } + const GenericVector &models() const { return *models_; } + + // Return an existing model if one that is Comparable() can be found. + // Else, allocate a new copy of model to save and return a pointer to it. + const ParagraphModel *AddModel(const ParagraphModel &model); + + // Discard any models we've made that are not in the list of used models. + void DiscardUnusedModels(const SetOfModels &used_models); + + // Return the set of all non-centered models. + void NonCenteredModels(SetOfModels *models); + + // If any of the non-centered paragraph models we know about fit + // rows[start, end), return it. Else NULL. + const ParagraphModel *Fits(const GenericVector *rows, + int start, int end) const; + + int IndexOf(const ParagraphModel *model) const; + + private: + GenericVector *models_; + GenericVectorEqEq models_we_added_; + }; + + bool ValidFirstLine(const GenericVector *rows, + int row, const ParagraphModel *model); + bool ValidBodyLine(const GenericVector *rows, + int row, const ParagraphModel *model); + bool CrownCompatible(const GenericVector *rows, + int a, int b, const ParagraphModel *model); + + // A class for smearing Paragraph Model hypotheses to surrounding rows. + // The idea here is that StrongEvidenceClassify first marks only exceedingly + // obvious start and body rows and constructs models of them. Thereafter, + // we may have left over unmarked lines (mostly end-of-paragraph lines) which + // were too short to have much confidence about, but which fit the models we've + // constructed perfectly and which we ought to mark. This class is used to + // "smear" our models over the text. + class ParagraphModelSmearer { + public: + ParagraphModelSmearer(GenericVector *rows, + int row_start, int row_end, + ParagraphTheory *theory); + + // Smear forward paragraph models from existing row markings to subsequent + // text lines if they fit, and mark any thereafter still unmodeled rows + // with any model in the theory that fits them. + void Smear(); + + private: + // Record in open_models_ for rows [start_row, end_row) the list of models + // currently open at each row. + // A model is still open in a row if some previous row has said model as a + // start hypothesis, and all rows since (including this row) would fit as + // either a body or start line in that model. + void CalculateOpenModels(int row_start, int row_end); + + SetOfModels &OpenModels(int row) { + return open_models_[row - row_start_ + 1]; + } + + ParagraphTheory *theory_; + GenericVector *rows_; + int row_start_; + int row_end_; + + // open_models_ corresponds to rows[start_row_ - 1, end_row_] + // + // open_models_: Contains models which there was an active (open) paragraph + // as of the previous line and for which the left and right + // indents admit the possibility that this text line continues + // to fit the same model. + // TODO(eger): Think about whether we can get rid of "Open" models and just + // use the current hypotheses on RowScratchRegisters. + GenericVector open_models_; + }; + + // Clear all hypotheses about lines [start, end) and reset the margins to the + // percentile (0..100) value of the left and right row edges for this run of + // rows. + void RecomputeMarginsAndClearHypotheses( + GenericVector *rows, int start, int end, + int percentile); + + // Return the median inter-word space in rows[row_start, row_end). + int InterwordSpace(const GenericVector &rows, + int row_start, int row_end); + + // Return whether the first word on the after line can fit in the space at + // the end of the before line (knowing which way the text is aligned and read). + bool FirstWordWouldHaveFit(const RowScratchRegisters &before, + const RowScratchRegisters &after, + tesseract::ParagraphJustification justification); + + // Return whether the first word on the after line can fit in the space at + // the end of the before line (not knowing the text alignment). + bool FirstWordWouldHaveFit(const RowScratchRegisters &before, + const RowScratchRegisters &after); + + // Do rows[start, end) form a single instance of the given paragraph model? + bool RowsFitModel(const GenericVector *rows, + int start, int end, const ParagraphModel *model); + + // Do the text and geometry of two rows support a paragraph break between them? + bool LikelyParagraphStart(const RowScratchRegisters &before, + const RowScratchRegisters &after, + tesseract::ParagraphJustification j); + + // Given a set of row_owners pointing to PARAs or NULL (no paragraph known), + // normalize each row_owner to point to an actual PARA, and output the + // paragraphs in order onto paragraphs. + void CanonicalizeDetectionResults( + GenericVector *row_owners, + PARA_LIST *paragraphs); + +} // namespace +#endif // TESSERACT_CCMAIN_PARAGRAPHS_INTERNAL_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.cpp new file mode 100644 index 0000000..90d6bb9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.cpp @@ -0,0 +1,370 @@ +/////////////////////////////////////////////////////////////////////// +// File: paramsd.cpp +// Description: Tesseract parameter Editor +// Author: Joern Wanke +// Created: Wed Jul 18 10:05: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. +// +/////////////////////////////////////////////////////////////////////// +// +// The parameters editor is used to edit all the parameters used within +// tesseract from the ui. +#ifdef _WIN32 +#else +#include +#include +#endif + +#include + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#ifndef GRAPHICS_DISABLED +#include "paramsd.h" + + +#include "params.h" +#include "scrollview.h" +#include "svmnode.h" + + +#define VARDIR "configs/" /*parameters files */ +#define MAX_ITEMS_IN_SUBMENU 30 + +// The following variables should remain static globals, since they +// are used by debug editor, which uses a single Tesseract instance. +// +// Contains the mappings from unique VC ids to their actual pointers. +static std::map vcMap; +static int nrParams = 0; +static int writeCommands[2]; + +ELISTIZE(ParamContent) + +// Constructors for the various ParamTypes. +ParamContent::ParamContent(tesseract::StringParam* it) { + my_id_ = nrParams; + nrParams++; + param_type_ = VT_STRING; + sIt = it; + vcMap[my_id_] = this; +} +// Constructors for the various ParamTypes. +ParamContent::ParamContent(tesseract::IntParam* it) { + my_id_ = nrParams; + nrParams++; + param_type_ = VT_INTEGER; + iIt = it; + vcMap[my_id_] = this; +} +// Constructors for the various ParamTypes. +ParamContent::ParamContent(tesseract::BoolParam* it) { + my_id_ = nrParams; + nrParams++; + param_type_ = VT_BOOLEAN; + bIt = it; + vcMap[my_id_] = this; +} +// Constructors for the various ParamTypes. +ParamContent::ParamContent(tesseract::DoubleParam* it) { + my_id_ = nrParams; + nrParams++; + param_type_ = VT_DOUBLE; + dIt = it; + vcMap[my_id_] = this; +} + +// Gets a VC object identified by its ID. +ParamContent* ParamContent::GetParamContentById(int id) { + return vcMap[id]; +} + +// Copy the first N words from the source string to the target string. +// Words are delimited by "_". +void ParamsEditor::GetFirstWords( + const char *s, // source string + int n, // number of words + char *t // target string +) { + int full_length = strlen(s); + int reqd_len = 0; // No. of chars requird + const char *next_word = s; + + while ((n > 0) && reqd_len < full_length) { + reqd_len += strcspn(next_word, "_") + 1; + next_word += reqd_len; + n--; + } + strncpy(t, s, reqd_len); + t[reqd_len] = '\0'; // ensure null terminal +} + +// Getter for the name. +const char* ParamContent::GetName() const { + if (param_type_ == VT_INTEGER) { return iIt->name_str(); } + else if (param_type_ == VT_BOOLEAN) { return bIt->name_str(); } + else if (param_type_ == VT_DOUBLE) { return dIt->name_str(); } + else if (param_type_ == VT_STRING) { return sIt->name_str(); } + else + return "ERROR: ParamContent::GetName()"; +} + +// Getter for the description. +const char* ParamContent::GetDescription() const { + if (param_type_ == VT_INTEGER) { return iIt->info_str(); } + else if (param_type_ == VT_BOOLEAN) { return bIt->info_str(); } + else if (param_type_ == VT_DOUBLE) { return dIt->info_str(); } + else if (param_type_ == VT_STRING) { return sIt->info_str(); } + else return NULL; +} + +// Getter for the value. +STRING ParamContent::GetValue() const { + STRING result; + if (param_type_ == VT_INTEGER) { + result.add_str_int("", *iIt); + } + else if (param_type_ == VT_BOOLEAN) { + result.add_str_int("", *bIt); + } + else if (param_type_ == VT_DOUBLE) { + result.add_str_double("", *dIt); + } + else if (param_type_ == VT_STRING) { + if (((STRING) * (sIt)).string() != NULL) { + result = sIt->string(); + } + else { + result = "Null"; + } + } + return result; +} + +// Setter for the value. +void ParamContent::SetValue(const char* val) { + // TODO (wanke) Test if the values actually are properly converted. + // (Quickly visible impacts?) + changed_ = TRUE; + if (param_type_ == VT_INTEGER) { + iIt->set_value(atoi(val)); + } + else if (param_type_ == VT_BOOLEAN) { + bIt->set_value(atoi(val)); + } + else if (param_type_ == VT_DOUBLE) { + dIt->set_value(strtod(val, NULL)); + } + else if (param_type_ == VT_STRING) { + sIt->set_value(val); + } +} + +// Gets the up to the first 3 prefixes from s (split by _). +// For example, tesseract_foo_bar will be split into tesseract,foo and bar. +void ParamsEditor::GetPrefixes(const char* s, STRING* level_one, + STRING* level_two, + STRING* level_three) { + char* p = new char[1024]; + GetFirstWords(s, 1, p); + *level_one = p; + GetFirstWords(s, 2, p); + *level_two = p; + GetFirstWords(s, 3, p); + *level_three = p; + delete[] p; +} + +// Compare two VC objects by their name. +int ParamContent::Compare(const void* v1, const void* v2) { + const ParamContent* one = + *reinterpret_cast(v1); + const ParamContent* two = + *reinterpret_cast(v2); + return strcmp(one->GetName(), two->GetName()); +} + +// Find all editable parameters used within tesseract and create a +// SVMenuNode tree from it. +// TODO (wanke): This is actually sort of hackish. +SVMenuNode* ParamsEditor::BuildListOfAllLeaves(tesseract::Tesseract *tess) { + SVMenuNode* mr = new SVMenuNode(); + ParamContent_LIST vclist; + ParamContent_IT vc_it(&vclist); + // Amount counts the number of entries for a specific char*. + // TODO(rays) get rid of the use of std::map. + std::map amount; + + // Add all parameters to a list. + int v, i; + int num_iterations = (tess->params() == NULL) ? 1 : 2; + for (v = 0; v < num_iterations; ++v) { + tesseract::ParamsVectors *vec = (v == 0) ? GlobalParams() : tess->params(); + for (i = 0; i < vec->int_params.size(); ++i) { + vc_it.add_after_then_move(new ParamContent(vec->int_params[i])); + } + for (i = 0; i < vec->bool_params.size(); ++i) { + vc_it.add_after_then_move(new ParamContent(vec->bool_params[i])); + } + for (i = 0; i < vec->string_params.size(); ++i) { + vc_it.add_after_then_move(new ParamContent(vec->string_params[i])); + } + for (i = 0; i < vec->double_params.size(); ++i) { + vc_it.add_after_then_move(new ParamContent(vec->double_params[i])); + } + } + + // Count the # of entries starting with a specific prefix. + for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) { + ParamContent* vc = vc_it.data(); + STRING tag; + STRING tag2; + STRING tag3; + + GetPrefixes(vc->GetName(), &tag, &tag2, &tag3); + amount[tag.string()]++; + amount[tag2.string()]++; + amount[tag3.string()]++; + } + + vclist.sort(ParamContent::Compare); // Sort the list alphabetically. + + SVMenuNode* other = mr->AddChild("OTHER"); + + // go through the list again and this time create the menu structure. + vc_it.move_to_first(); + for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) { + ParamContent* vc = vc_it.data(); + STRING tag; + STRING tag2; + STRING tag3; + GetPrefixes(vc->GetName(), &tag, &tag2, &tag3); + + if (amount[tag.string()] == 1) { + other->AddChild(vc->GetName(), vc->GetId(), vc->GetValue().string(), + vc->GetDescription()); + } + else { // More than one would use this submenu -> create submenu. + SVMenuNode* sv = mr->AddChild(tag.string()); + if ((amount[tag.string()] <= MAX_ITEMS_IN_SUBMENU) || + (amount[tag2.string()] <= 1)) { + sv->AddChild(vc->GetName(), vc->GetId(), + vc->GetValue().string(), vc->GetDescription()); + } + else { // Make subsubmenus. + SVMenuNode* sv2 = sv->AddChild(tag2.string()); + sv2->AddChild(vc->GetName(), vc->GetId(), + vc->GetValue().string(), vc->GetDescription()); + } + } + } + return mr; +} + +// Event listener. Waits for SVET_POPUP events and processes them. +void ParamsEditor::Notify(const SVEvent* sve) { + if (sve->type == SVET_POPUP) { // only catch SVET_POPUP! + char* param = sve->parameter; + if (sve->command_id == writeCommands[0]) { + WriteParams(param, false); + } + else if (sve->command_id == writeCommands[1]) { + WriteParams(param, true); + } + else { + ParamContent* vc = ParamContent::GetParamContentById( + sve->command_id); + vc->SetValue(param); + sv_window_->AddMessage("Setting %s to %s", + vc->GetName(), vc->GetValue().string()); + } + } +} + +// Integrate the parameters editor as popupmenu into the existing scrollview +// window (usually the pg editor). If sv == null, create a new empty +// empty window and attach the parameters editor to that window (ugly). +ParamsEditor::ParamsEditor(tesseract::Tesseract* tess, + ScrollView* sv) { + if (sv == NULL) { + const char* name = "ParamEditorMAIN"; + sv = new ScrollView(name, 1, 1, 200, 200, 300, 200); + } + + sv_window_ = sv; + + //Only one event handler per window. + //sv->AddEventHandler((SVEventHandler*) this); + + SVMenuNode* svMenuRoot = BuildListOfAllLeaves(tess); + + STRING paramfile; + paramfile = tess->datadir; + paramfile += VARDIR; // parameters dir + paramfile += "edited"; // actual name + + SVMenuNode* std_menu = svMenuRoot->AddChild("Build Config File"); + + writeCommands[0] = nrParams + 1; + std_menu->AddChild("All Parameters", writeCommands[0], + paramfile.string(), "Config file name?"); + + writeCommands[1] = nrParams + 2; + std_menu->AddChild("changed_ Parameters Only", writeCommands[1], + paramfile.string(), "Config file name?"); + + svMenuRoot->BuildMenu(sv, false); +} + + +// Write all (changed_) parameters to a config file. +void ParamsEditor::WriteParams(char *filename, + bool changes_only) { + FILE *fp; // input file + char msg_str[255]; + // if file exists + if ((fp = fopen(filename, "rb")) != NULL) { + fclose(fp); + sprintf(msg_str, "Overwrite file " "%s" "? (Y/N)", filename); + int a = sv_window_->ShowYesNoDialog(msg_str); + if (a == 'n') { + return; + } // don't write + } + + + fp = fopen(filename, "wb"); // can we write to it? + if (fp == NULL) { + sv_window_->AddMessage( + "Can't write to file " + "%s" + "", + filename); + return; + } + + for (std::map::iterator iter = vcMap.begin(); + iter != vcMap.end(); + ++iter) { + ParamContent* cur = iter->second; + if (!changes_only || cur->HasChanged()) { + fprintf(fp, "%-25s %-12s # %s\n", + cur->GetName(), cur->GetValue().string(), cur->GetDescription()); + } + } + fclose(fp); +} +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.h b/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.h new file mode 100644 index 0000000..1fb2347 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/paramsd.h @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////// +// File: paramsd.cpp +// Description: Tesseract parameter editor +// Author: Joern Wanke +// Created: Wed Jul 18 10:05: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. +// +/////////////////////////////////////////////////////////////////////// +// +// Tesseract parameter editor is used to edit all the parameters used +// within tesseract from the ui. +#ifndef GRAPHICS_DISABLED +#ifndef VARABLED_H +#define VARABLED_H + +#include "elst.h" +#ifndef NO_CUBE_BUILD +#include "scrollview.h" +#endif +#include "params.h" +#include "tesseractclass.h" + +class SVMenuNode; + +// A list of all possible parameter types used. +enum ParamType { + VT_INTEGER, + VT_BOOLEAN, + VT_STRING, + VT_DOUBLE +}; + +// A rather hackish helper structure which can take any kind of parameter input +// (defined by ParamType) and do a couple of common operations on them, like +// comparisond or getting its value. It is used in the context of the +// ParamsEditor as a bridge from the internal tesseract parameters to the +// ones displayed by the ScrollView server. +class ParamContent : public ELIST_LINK { +public: + // Compare two VC objects by their name. + static int Compare(const void* v1, const void* v2); + + // Gets a VC object identified by its ID. + static ParamContent* GetParamContentById(int id); + + // Constructors for the various ParamTypes. + ParamContent() { + } + explicit ParamContent(tesseract::StringParam* it); + explicit ParamContent(tesseract::IntParam* it); + explicit ParamContent(tesseract::BoolParam* it); + explicit ParamContent(tesseract::DoubleParam* it); + + + // Getters and Setters. + void SetValue(const char* val); + STRING GetValue() const; + const char* GetName() const; + const char* GetDescription() const; + + int GetId() { return my_id_; } + bool HasChanged() { return changed_; } + +private: + // The unique ID of this VC object. + int my_id_; + // Whether the parameter was changed_ and thus needs to be rewritten. + bool changed_; + // The actual ParamType of this VC object. + ParamType param_type_; + + tesseract::StringParam* sIt; + tesseract::IntParam* iIt; + tesseract::BoolParam* bIt; + tesseract::DoubleParam* dIt; +}; + +ELISTIZEH(ParamContent) + +// The parameters editor enables the user to edit all the parameters used within +// tesseract. It can be invoked on its own, but is supposed to be invoked by +// the program editor. +class ParamsEditor : public SVEventHandler { +public: + // Integrate the parameters editor as popupmenu into the existing scrollview + // window (usually the pg editor). If sv == null, create a new empty + // empty window and attach the parameter editor to that window (ugly). + explicit ParamsEditor(tesseract::Tesseract*, ScrollView* sv = NULL); + + // Event listener. Waits for SVET_POPUP events and processes them. + void Notify(const SVEvent* sve); + +private: + // Gets the up to the first 3 prefixes from s (split by _). + // For example, tesseract_foo_bar will be split into tesseract,foo and bar. + void GetPrefixes(const char* s, STRING* level_one, + STRING* level_two, STRING* level_three); + + // Gets the first n words (split by _) and puts them in t. + // For example, tesseract_foo_bar with N=2 will yield tesseract_foo_. + void GetFirstWords(const char *s, // source string + int n, // number of words + char *t); // target string + +// Find all editable parameters used within tesseract and create a +// SVMenuNode tree from it. + SVMenuNode *BuildListOfAllLeaves(tesseract::Tesseract *tess); + + // Write all (changed_) parameters to a config file. + void WriteParams(char* filename, bool changes_only); + + ScrollView* sv_window_; +}; + +#endif +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.cpp new file mode 100644 index 0000000..79ab21f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.cpp @@ -0,0 +1,1005 @@ +/********************************************************************** + * File: pgedit.cpp (Formerly pgeditor.c) + * Description: Page structure file editor + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + *(C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0(the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http:// www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "pgedit.h" + +#include +#include + +#include "blread.h" +#include "control.h" +#include "paramsd.h" +#include "pageres.h" +#include "tordmain.h" +#include "scrollview.h" +#include "svmnode.h" +#include "statistc.h" +#include "tesseractclass.h" +#include "werdit.h" + +#ifndef GRAPHICS_DISABLED +#define ASC_HEIGHT (2 * kBlnBaselineOffset + kBlnXHeight) +#define X_HEIGHT (kBlnBaselineOffset + kBlnXHeight) +#define BL_HEIGHT kBlnBaselineOffset +#define DESC_HEIGHT 0 +#define MAXSPACING 128 /*max expected spacing in pix */ + +const ERRCODE EMPTYBLOCKLIST = "No blocks to edit"; + +enum CMD_EVENTS +{ + NULL_CMD_EVENT, + CHANGE_DISP_CMD_EVENT, + DUMP_WERD_CMD_EVENT, + SHOW_POINT_CMD_EVENT, + SHOW_BLN_WERD_CMD_EVENT, + DEBUG_WERD_CMD_EVENT, + BLAMER_CMD_EVENT, + BOUNDING_BOX_CMD_EVENT, + CORRECT_TEXT_CMD_EVENT, + POLYGONAL_CMD_EVENT, + BL_NORM_CMD_EVENT, + BITMAP_CMD_EVENT, + IMAGE_CMD_EVENT, + BLOCKS_CMD_EVENT, + BASELINES_CMD_EVENT, + UNIFORM_DISP_CMD_EVENT, + REFRESH_CMD_EVENT, + QUIT_CMD_EVENT, + RECOG_WERDS, + RECOG_PSEUDO, + SHOW_BLOB_FEATURES, + SHOW_SUBSCRIPT_CMD_EVENT, + SHOW_SUPERSCRIPT_CMD_EVENT, + SHOW_ITALIC_CMD_EVENT, + SHOW_BOLD_CMD_EVENT, + SHOW_UNDERLINE_CMD_EVENT, + SHOW_FIXEDPITCH_CMD_EVENT, + SHOW_SERIF_CMD_EVENT, + SHOW_SMALLCAPS_CMD_EVENT, + SHOW_DROPCAPS_CMD_EVENT, +}; + +enum ColorationMode { + CM_RAINBOW, + CM_SUBSCRIPT, + CM_SUPERSCRIPT, + CM_ITALIC, + CM_BOLD, + CM_UNDERLINE, + CM_FIXEDPITCH, + CM_SERIF, + CM_SMALLCAPS, + CM_DROPCAPS +}; + +/* + * + * Some global data + * + */ + +ScrollView* image_win; +ParamsEditor* pe; +bool stillRunning = false; + +#ifdef __UNIX__ +FILE *debug_window = NULL; // opened on demand +#endif +ScrollView* bln_word_window = NULL; // baseline norm words + +CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; // selected words op + +bool recog_done = false; // recog_all_words was called + +// These variables should remain global, since they are only used for the +// debug mode (in which only a single Tesseract thread/instance will be exist). +BITS16 word_display_mode; +static ColorationMode color_mode = CM_RAINBOW; +BOOL8 display_image = FALSE; +BOOL8 display_blocks = FALSE; +BOOL8 display_baselines = FALSE; + +PAGE_RES *current_page_res = NULL; + +STRING_VAR(editor_image_win_name, "EditorImage", + "Editor image window name"); +INT_VAR(editor_image_xpos, 590, "Editor image X Pos"); +INT_VAR(editor_image_ypos, 10, "Editor image Y Pos"); +INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar"); +INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, + "Word bounding box colour"); +INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, + "Blob bounding box colour"); +INT_VAR(editor_image_text_color, ScrollView::WHITE, + "Correct text colour"); + +STRING_VAR(editor_dbwin_name, "EditorDBWin", + "Editor debug window name"); +INT_VAR(editor_dbwin_xpos, 50, "Editor debug window X Pos"); +INT_VAR(editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +INT_VAR(editor_dbwin_height, 24, "Editor debug window height"); +INT_VAR(editor_dbwin_width, 80, "Editor debug window width"); + +STRING_VAR(editor_word_name, "BlnWords", "BL normalized word window"); +INT_VAR(editor_word_xpos, 60, "Word window X Pos"); +INT_VAR(editor_word_ypos, 510, "Word window Y Pos"); +INT_VAR(editor_word_height, 240, "Word window height"); +INT_VAR(editor_word_width, 655, "Word window width"); + +STRING_VAR(editor_debug_config_file, "", "Config file to apply to single words"); + +class BlnEventHandler : public SVEventHandler { +public: + void Notify(const SVEvent* sv_event) { + if (sv_event->type == SVET_DESTROY) + bln_word_window = NULL; + else if (sv_event->type == SVET_CLICK) + show_point(current_page_res, sv_event->x, sv_event->y); + } +}; + +/** + * bln_word_window_handle() + * + * @return a WINDOW for the word window, creating it if necessary + */ +ScrollView* bln_word_window_handle() { // return handle + // not opened yet + if (bln_word_window == NULL) { + pgeditor_msg("Creating BLN word window..."); + bln_word_window = new ScrollView(editor_word_name.string(), + editor_word_xpos, editor_word_ypos, editor_word_width, + editor_word_height, 4000, 4000, true); + BlnEventHandler* a = new BlnEventHandler(); + bln_word_window->AddEventHandler(a); + pgeditor_msg("Creating BLN word window...Done"); + } + return bln_word_window; +} + +/** + * build_image_window() + * + * Destroy the existing image window if there is one. Work out how big the + * new window needs to be. Create it and re-display. + */ + +void build_image_window(int width, int height) { + delete image_win; + image_win = new ScrollView(editor_image_win_name.string(), + editor_image_xpos, editor_image_ypos, + width + 1, + height + editor_image_menuheight + 1, + width, + height, + true); +} + +/** + * display_bln_lines() + * + * Display normalized baseline, x-height, ascender limit and descender limit + */ + +void display_bln_lines(ScrollView* window, + ScrollView::Color colour, + float scale_factor, + float y_offset, + float minx, + float maxx) { + window->Pen(colour); + window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, + maxx, y_offset + scale_factor * DESC_HEIGHT); + window->Line(minx, y_offset + scale_factor * BL_HEIGHT, + maxx, y_offset + scale_factor * BL_HEIGHT); + window->Line(minx, y_offset + scale_factor * X_HEIGHT, + maxx, y_offset + scale_factor * X_HEIGHT); + window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, + maxx, y_offset + scale_factor * ASC_HEIGHT); +} + +/** + * notify() + * + * Event handler that processes incoming events, either forwarding + * them to process_cmd_win_event or process_image_event. + * + */ + +void PGEventHandler::Notify(const SVEvent* event) { + char myval = '0'; + if (event->type == SVET_POPUP) { + pe->Notify(event); + } // These are handled by ParamsEditor + else if (event->type == SVET_EXIT) { stillRunning = false; } + else if (event->type == SVET_MENU) { + if (strcmp(event->parameter, "true") == 0) { myval = 'T'; } + else if (strcmp(event->parameter, "false") == 0) { myval = 'F'; } + tess_->process_cmd_win_event(event->command_id, &myval); + } + else { + tess_->process_image_event(*event); + } +} + +/** + * build_menu() + * + * Construct the menu tree used by the command window + */ +namespace tesseract { + SVMenuNode *Tesseract::build_menu_new() { + SVMenuNode* parent_menu; + SVMenuNode* root_menu_item = new SVMenuNode(); + + SVMenuNode* modes_menu_item = root_menu_item->AddChild("MODES"); + + modes_menu_item->AddChild("Change Display", CHANGE_DISP_CMD_EVENT); + modes_menu_item->AddChild("Dump Word", DUMP_WERD_CMD_EVENT); + modes_menu_item->AddChild("Show Point", SHOW_POINT_CMD_EVENT); + modes_menu_item->AddChild("Show BL Norm Word", SHOW_BLN_WERD_CMD_EVENT); + modes_menu_item->AddChild("Config Words", DEBUG_WERD_CMD_EVENT); + modes_menu_item->AddChild("Recog Words", RECOG_WERDS); + modes_menu_item->AddChild("Recog Blobs", RECOG_PSEUDO); + modes_menu_item->AddChild("Show Blob Features", SHOW_BLOB_FEATURES); + + parent_menu = root_menu_item->AddChild("DISPLAY"); + + parent_menu->AddChild("Blamer", BLAMER_CMD_EVENT, FALSE); + parent_menu->AddChild("Bounding Boxes", BOUNDING_BOX_CMD_EVENT, FALSE); + parent_menu->AddChild("Correct Text", CORRECT_TEXT_CMD_EVENT, FALSE); + parent_menu->AddChild("Polygonal Approx", POLYGONAL_CMD_EVENT, FALSE); + parent_menu->AddChild("Baseline Normalized", BL_NORM_CMD_EVENT, FALSE); + parent_menu->AddChild("Edge Steps", BITMAP_CMD_EVENT, TRUE); + parent_menu->AddChild("Subscripts", SHOW_SUBSCRIPT_CMD_EVENT); + parent_menu->AddChild("Superscripts", SHOW_SUPERSCRIPT_CMD_EVENT); + parent_menu->AddChild("Italics", SHOW_ITALIC_CMD_EVENT); + parent_menu->AddChild("Bold", SHOW_BOLD_CMD_EVENT); + parent_menu->AddChild("Underline", SHOW_UNDERLINE_CMD_EVENT); + parent_menu->AddChild("FixedPitch", SHOW_FIXEDPITCH_CMD_EVENT); + parent_menu->AddChild("Serifs", SHOW_SERIF_CMD_EVENT); + parent_menu->AddChild("SmallCaps", SHOW_SMALLCAPS_CMD_EVENT); + parent_menu->AddChild("DropCaps", SHOW_DROPCAPS_CMD_EVENT); + + + parent_menu = root_menu_item->AddChild("OTHER"); + + parent_menu->AddChild("Quit", QUIT_CMD_EVENT); + parent_menu->AddChild("Show Image", IMAGE_CMD_EVENT, FALSE); + parent_menu->AddChild("ShowBlock Outlines", BLOCKS_CMD_EVENT, FALSE); + parent_menu->AddChild("Show Baselines", BASELINES_CMD_EVENT, FALSE); + parent_menu->AddChild("Uniform Display", UNIFORM_DISP_CMD_EVENT); + parent_menu->AddChild("Refresh Display", REFRESH_CMD_EVENT); + + return root_menu_item; + } + + /** + * do_re_display() + * + * Redisplay page + */ + void Tesseract::do_re_display( + BOOL8(tesseract::Tesseract::*word_painter)(PAGE_RES_IT* pr_it)) { + int block_count = 1; + + image_win->Clear(); + if (display_image != 0) { + image_win->Image(pix_binary_, 0, 0); + } + + image_win->Brush(ScrollView::NONE); + PAGE_RES_IT pr_it(current_page_res); + for (WERD_RES* word = pr_it.word(); word != NULL; word = pr_it.forward()) { + (this->*word_painter)(&pr_it); + if (display_baselines && pr_it.row() != pr_it.prev_row()) + pr_it.row()->row->plot_baseline(image_win, ScrollView::GREEN); + if (display_blocks && pr_it.block() != pr_it.prev_block()) + pr_it.block()->block->plot(image_win, block_count++, ScrollView::RED); + } + image_win->Update(); + } + + /** + * pgeditor_main() + * + * Top level editor operation: + * Setup a new window and an according event handler + * + */ + + void Tesseract::pgeditor_main(int width, int height, PAGE_RES *page_res) { + current_page_res = page_res; + if (current_page_res->block_res_list.empty()) + return; + + recog_done = false; + stillRunning = true; + + build_image_window(width, height); + word_display_mode.turn_on_bit(DF_EDGE_STEP); + do_re_display(&tesseract::Tesseract::word_set_display); +#ifndef GRAPHICS_DISABLED + pe = new ParamsEditor(this, image_win); +#endif + PGEventHandler pgEventHandler(this); + + image_win->AddEventHandler(&pgEventHandler); + image_win->AddMessageBox(); + + SVMenuNode* svMenuRoot = build_menu_new(); + + svMenuRoot->BuildMenu(image_win); + image_win->SetVisible(true); + + image_win->AwaitEvent(SVET_DESTROY); + image_win->AddEventHandler(NULL); + } +} // namespace tesseract + + +/** + * pgeditor_msg() + * + * Display a message - in the command window if there is one, or to stdout + */ + +void pgeditor_msg( // message display + const char *msg) { + image_win->AddMessage(msg); +} + +/** + * pgeditor_show_point() + * + * Display the coordinates of a point in the command window + */ + +void pgeditor_show_point( // display coords + SVEvent *event) { + image_win->AddMessage("Pointing at(%d, %d)", event->x, event->y); +} + +/** + * process_cmd_win_event() + * + * Process a command returned from the command window + * (Just call the appropriate command handler) + */ + +namespace tesseract { + BOOL8 Tesseract::process_cmd_win_event( // UI command semantics + inT32 cmd_event, // which menu item? + char *new_value // any prompt data + ) { + char msg[160]; + BOOL8 exit = FALSE; + + color_mode = CM_RAINBOW; + + // Run recognition on the full page if needed. + switch (cmd_event) { + case BLAMER_CMD_EVENT: + case SHOW_SUBSCRIPT_CMD_EVENT: + case SHOW_SUPERSCRIPT_CMD_EVENT: + case SHOW_ITALIC_CMD_EVENT: + case SHOW_BOLD_CMD_EVENT: + case SHOW_UNDERLINE_CMD_EVENT: + case SHOW_FIXEDPITCH_CMD_EVENT: + case SHOW_SERIF_CMD_EVENT: + case SHOW_SMALLCAPS_CMD_EVENT: + case SHOW_DROPCAPS_CMD_EVENT: + if (!recog_done) { + recog_all_words(current_page_res, NULL, NULL, NULL, 0); + recog_done = true; + } + break; + default: + break; + } + + switch (cmd_event) { + case NULL_CMD_EVENT: + break; + + case CHANGE_DISP_CMD_EVENT: + case DUMP_WERD_CMD_EVENT: + case SHOW_POINT_CMD_EVENT: + case SHOW_BLN_WERD_CMD_EVENT: + case RECOG_WERDS: + case RECOG_PSEUDO: + case SHOW_BLOB_FEATURES: + mode = (CMD_EVENTS)cmd_event; + break; + case DEBUG_WERD_CMD_EVENT: + mode = DEBUG_WERD_CMD_EVENT; + word_config_ = image_win->ShowInputDialog("Config File Name"); + break; + case BOUNDING_BOX_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_BOX); + else + word_display_mode.turn_off_bit(DF_BOX); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BLAMER_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_BLAMER); + else + word_display_mode.turn_off_bit(DF_BLAMER); + do_re_display(&tesseract::Tesseract::word_display); + mode = CHANGE_DISP_CMD_EVENT; + break; + case CORRECT_TEXT_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_TEXT); + else + word_display_mode.turn_off_bit(DF_TEXT); + mode = CHANGE_DISP_CMD_EVENT; + break; + case POLYGONAL_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_POLYGONAL); + else + word_display_mode.turn_off_bit(DF_POLYGONAL); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BL_NORM_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_BN_POLYGONAL); + else + word_display_mode.turn_off_bit(DF_BN_POLYGONAL); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BITMAP_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit(DF_EDGE_STEP); + else + word_display_mode.turn_off_bit(DF_EDGE_STEP); + mode = CHANGE_DISP_CMD_EVENT; + break; + case UNIFORM_DISP_CMD_EVENT: + do_re_display(&tesseract::Tesseract::word_set_display); + break; + case IMAGE_CMD_EVENT: + display_image = (new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case BLOCKS_CMD_EVENT: + display_blocks = (new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case BASELINES_CMD_EVENT: + display_baselines = (new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SUBSCRIPT_CMD_EVENT: + color_mode = CM_SUBSCRIPT; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SUPERSCRIPT_CMD_EVENT: + color_mode = CM_SUPERSCRIPT; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_ITALIC_CMD_EVENT: + color_mode = CM_ITALIC; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_BOLD_CMD_EVENT: + color_mode = CM_BOLD; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_UNDERLINE_CMD_EVENT: + color_mode = CM_UNDERLINE; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_FIXEDPITCH_CMD_EVENT: + color_mode = CM_FIXEDPITCH; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SERIF_CMD_EVENT: + color_mode = CM_SERIF; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SMALLCAPS_CMD_EVENT: + color_mode = CM_SMALLCAPS; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_DROPCAPS_CMD_EVENT: + color_mode = CM_DROPCAPS; + do_re_display(&tesseract::Tesseract::word_display); + break; + case REFRESH_CMD_EVENT: + do_re_display(&tesseract::Tesseract::word_display); + break; + case QUIT_CMD_EVENT: + exit = TRUE; + ScrollView::Exit(); + break; + + default: + sprintf(msg, "Unrecognised event " INT32FORMAT "(%s)", + cmd_event, new_value); + image_win->AddMessage(msg); + break; + } + return exit; + } + + + /** + * process_image_event() + * + * User has done something in the image window - mouse down or up. Work out + * what it is and do something with it. + * If DOWN - just remember where it was. + * If UP - for each word in the selected area do the operation defined by + * the current mode. + */ + void Tesseract::process_image_event( // action in image win + const SVEvent &event) { + // The following variable should remain static, since it is used by + // debug editor, which uses a single Tesseract instance. + static ICOORD down; + ICOORD up; + TBOX selection_box; + char msg[80]; + + switch (event.type) { + + case SVET_SELECTION: + if (event.type == SVET_SELECTION) { + down.set_x(event.x + event.x_size); + down.set_y(event.y + event.y_size); + if (mode == SHOW_POINT_CMD_EVENT) + show_point(current_page_res, event.x, event.y); + } + + up.set_x(event.x); + up.set_y(event.y); + + selection_box = TBOX(down, up); + + switch (mode) { + case CHANGE_DISP_CMD_EVENT: + process_selected_words( + current_page_res, + selection_box, + &tesseract::Tesseract::word_blank_and_set_display); + break; + case DUMP_WERD_CMD_EVENT: + process_selected_words(current_page_res, + selection_box, + &tesseract::Tesseract::word_dumper); + break; + case SHOW_BLN_WERD_CMD_EVENT: + process_selected_words(current_page_res, + selection_box, + &tesseract::Tesseract::word_bln_display); + break; + case DEBUG_WERD_CMD_EVENT: + debug_word(current_page_res, selection_box); + break; + case SHOW_POINT_CMD_EVENT: + break; // ignore up event + + case RECOG_WERDS: + image_win->AddMessage("Recogging selected words"); + this->process_selected_words(current_page_res, + selection_box, + &Tesseract::recog_interactive); + break; + case RECOG_PSEUDO: + image_win->AddMessage("Recogging selected blobs"); + recog_pseudo_word(current_page_res, selection_box); + break; + case SHOW_BLOB_FEATURES: + blob_feature_display(current_page_res, selection_box); + break; + + default: + sprintf(msg, "Mode %d not yet implemented", mode); + image_win->AddMessage(msg); + break; + } + default: + break; + } + } + + /** + * debug_word + * + * Process the whole image, but load word_config_ for the selected word(s). + */ + void Tesseract::debug_word(PAGE_RES* page_res, const TBOX &selection_box) { + ResetAdaptiveClassifier(); + recog_all_words(page_res, NULL, &selection_box, word_config_.string(), 0); + } +} // namespace tesseract + + +/** + * show_point() + * + * Show coords of point, blob bounding box, word bounding box and offset from + * row baseline + */ + +void show_point(PAGE_RES* page_res, float x, float y) { + FCOORD pt(x, y); + PAGE_RES_IT pr_it(page_res); + + const int kBufsize = 512; + char msg[kBufsize]; + char *msg_ptr = msg; + + msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); + + for (WERD_RES* word = pr_it.word(); word != NULL; word = pr_it.forward()) { + if (pr_it.row() != pr_it.prev_row() && + pr_it.row()->row->bounding_box().contains(pt)) { + msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ", + pr_it.row()->row->base_line(x)); + } + if (word->word->bounding_box().contains(pt)) { + TBOX box = word->word->bounding_box(); + msg_ptr += sprintf(msg_ptr, "Wd(%d, %d)/(%d, %d) ", + box.left(), box.bottom(), + box.right(), box.top()); + C_BLOB_IT cblob_it(word->word->cblob_list()); + for (cblob_it.mark_cycle_pt(); + !cblob_it.cycled_list(); + cblob_it.forward()) { + C_BLOB* cblob = cblob_it.data(); + box = cblob->bounding_box(); + if (box.contains(pt)) { + msg_ptr += sprintf(msg_ptr, + "CBlb(%d, %d)/(%d, %d) ", + box.left(), box.bottom(), + box.right(), box.top()); + } + } + } + } + image_win->AddMessage(msg); +} + + +/********************************************************************** + * WERD PROCESSOR FUNCTIONS + * ======================== + * + * These routines are invoked by one or more of: + * process_all_words() + * process_selected_words() + * or + * process_all_words_it() + * process_selected_words_it() + * for each word to be processed + **********************************************************************/ + + /** + * word_blank_and_set_display() Word processor + * + * Blank display of word then redisplay word according to current display mode + * settings + */ +#endif // GRAPHICS_DISABLED +namespace tesseract { +#ifndef GRAPHICS_DISABLED + BOOL8 Tesseract::word_blank_and_set_display(PAGE_RES_IT* pr_it) { + pr_it->word()->word->bounding_box().plot(image_win, ScrollView::BLACK, + ScrollView::BLACK); + return word_set_display(pr_it); + } + + + /** + * word_bln_display() + * + * Normalize word and display in word window + */ + BOOL8 Tesseract::word_bln_display(PAGE_RES_IT* pr_it) { + WERD_RES* word_res = pr_it->word(); + if (word_res->chopped_word == NULL) { + // Setup word normalization parameters. + word_res->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, NULL, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + pr_it->row()->row, pr_it->block()->block); + } + bln_word_window_handle()->Clear(); + display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, + 1.0, 0.0f, -1000.0f, 1000.0f); + C_BLOB_IT it(word_res->word->cblob_list()); + ScrollView::Color color = WERD::NextColor(ScrollView::BLACK); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot_normed(word_res->denorm, color, ScrollView::BROWN, + bln_word_window_handle()); + color = WERD::NextColor(color); + } + bln_word_window_handle()->Update(); + return TRUE; + } + + + + /** + * word_display() Word Processor + * + * Display a word according to its display modes + */ + BOOL8 Tesseract::word_display(PAGE_RES_IT* pr_it) { + WERD_RES* word_res = pr_it->word(); + WERD* word = word_res->word; + TBOX word_bb; // word bounding box + int word_height; // ht of word BB + BOOL8 displayed_something = FALSE; + float shift; // from bot left + C_BLOB_IT c_it; // cblob iterator + + if (color_mode != CM_RAINBOW && word_res->box_word != NULL) { + BoxWord* box_word = word_res->box_word; + WERD_CHOICE* best_choice = word_res->best_choice; + int length = box_word->length(); + if (word_res->fontinfo == NULL) return false; + const FontInfo& font_info = *word_res->fontinfo; + for (int i = 0; i < length; ++i) { + ScrollView::Color color = ScrollView::GREEN; + switch (color_mode) { + case CM_SUBSCRIPT: + if (best_choice->BlobPosition(i) == SP_SUBSCRIPT) + color = ScrollView::RED; + break; + case CM_SUPERSCRIPT: + if (best_choice->BlobPosition(i) == SP_SUPERSCRIPT) + color = ScrollView::RED; + break; + case CM_ITALIC: + if (font_info.is_italic()) + color = ScrollView::RED; + break; + case CM_BOLD: + if (font_info.is_bold()) + color = ScrollView::RED; + break; + case CM_FIXEDPITCH: + if (font_info.is_fixed_pitch()) + color = ScrollView::RED; + break; + case CM_SERIF: + if (font_info.is_serif()) + color = ScrollView::RED; + break; + case CM_SMALLCAPS: + if (word_res->small_caps) + color = ScrollView::RED; + break; + case CM_DROPCAPS: + if (best_choice->BlobPosition(i) == SP_DROPCAP) + color = ScrollView::RED; + break; + // TODO(rays) underline is currently completely unsupported. + case CM_UNDERLINE: + default: + break; + } + image_win->Pen(color); + TBOX box = box_word->BlobBox(i); + image_win->Rectangle(box.left(), box.bottom(), box.right(), box.top()); + } + return true; + } + /* + Note the double coercions of(COLOUR)((inT32)editor_image_word_bb_color) + etc. are to keep the compiler happy. + */ + // display bounding box + if (word->display_flag(DF_BOX)) { + word->bounding_box().plot(image_win, + (ScrollView::Color)((inT32) + editor_image_word_bb_color), + (ScrollView::Color)((inT32) + editor_image_word_bb_color)); + + ScrollView::Color c = (ScrollView::Color) + ((inT32)editor_image_blob_bb_color); + image_win->Pen(c); + c_it.set_to_list(word->cblob_list()); + for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) + c_it.data()->bounding_box().plot(image_win); + displayed_something = TRUE; + } + + // display edge steps + if (word->display_flag(DF_EDGE_STEP)) { // edgesteps available + word->plot(image_win); // rainbow colors + displayed_something = TRUE; + } + + // display poly approx + if (word->display_flag(DF_POLYGONAL)) { + // need to convert + TWERD* tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word); + tword->plot(image_win); + delete tword; + displayed_something = TRUE; + } + + // Display correct text and blamer information. + STRING text; + STRING blame; + if (word->display_flag(DF_TEXT) && word->text() != NULL) { + text = word->text(); + } + if (word->display_flag(DF_BLAMER) && + !(word_res->blamer_bundle != NULL && + word_res->blamer_bundle->incorrect_result_reason() == IRR_CORRECT)) { + text = ""; + const BlamerBundle *blamer_bundle = word_res->blamer_bundle; + if (blamer_bundle == NULL) { + text += "NULL"; + } + else { + text = blamer_bundle->TruthString(); + } + text += " -> "; + STRING best_choice_str; + if (word_res->best_choice == NULL) { + best_choice_str = "NULL"; + } + else { + word_res->best_choice->string_and_lengths(&best_choice_str, NULL); + } + text += best_choice_str; + IncorrectResultReason reason = (blamer_bundle == NULL) ? + IRR_PAGE_LAYOUT : blamer_bundle->incorrect_result_reason(); + ASSERT_HOST(reason < IRR_NUM_REASONS) + blame += " ["; + blame += BlamerBundle::IncorrectReasonName(reason); + blame += "]"; + } + if (text.length() > 0) { + word_bb = word->bounding_box(); + image_win->Pen(ScrollView::RED); + word_height = word_bb.height(); + int text_height = 0.50 * word_height; + if (text_height > 20) text_height = 20; + image_win->TextAttributes("Arial", text_height, false, false, false); + shift = (word_height < word_bb.width()) ? 0.25 * word_height : 0.0f; + image_win->Text(word_bb.left() + shift, + word_bb.bottom() + 0.25 * word_height, text.string()); + if (blame.length() > 0) { + image_win->Text(word_bb.left() + shift, + word_bb.bottom() + 0.25 * word_height - text_height, + blame.string()); + } + + displayed_something = TRUE; + } + + if (!displayed_something) // display BBox anyway + word->bounding_box().plot(image_win, + (ScrollView::Color)((inT32)editor_image_word_bb_color), + (ScrollView::Color)((inT32) + editor_image_word_bb_color)); + return TRUE; + } +#endif // GRAPHICS_DISABLED + + /** + * word_dumper() + * + * Dump members to the debug window + */ + BOOL8 Tesseract::word_dumper(PAGE_RES_IT* pr_it) { + if (pr_it->block()->block != NULL) { + tprintf("\nBlock data...\n"); + pr_it->block()->block->print(NULL, FALSE); + } + tprintf("\nRow data...\n"); + pr_it->row()->row->print(NULL); + tprintf("\nWord data...\n"); + WERD_RES* word_res = pr_it->word(); + word_res->word->print(); + if (word_res->blamer_bundle != NULL && wordrec_debug_blamer && + word_res->blamer_bundle->incorrect_result_reason() != IRR_CORRECT) { + tprintf("Current blamer debug: %s\n", + word_res->blamer_bundle->debug().string()); + } + return TRUE; + } + +#ifndef GRAPHICS_DISABLED + /** + * word_set_display() Word processor + * + * Display word according to current display mode settings + */ + BOOL8 Tesseract::word_set_display(PAGE_RES_IT* pr_it) { + WERD* word = pr_it->word()->word; + word->set_display_flag(DF_BOX, word_display_mode.bit(DF_BOX)); + word->set_display_flag(DF_TEXT, word_display_mode.bit(DF_TEXT)); + word->set_display_flag(DF_POLYGONAL, word_display_mode.bit(DF_POLYGONAL)); + word->set_display_flag(DF_EDGE_STEP, word_display_mode.bit(DF_EDGE_STEP)); + word->set_display_flag(DF_BN_POLYGONAL, + word_display_mode.bit(DF_BN_POLYGONAL)); + word->set_display_flag(DF_BLAMER, word_display_mode.bit(DF_BLAMER)); + return word_display(pr_it); + } + + // page_res is non-const because the iterator doesn't know if you are going + // to change the items it points to! Really a const here though. + void Tesseract::blob_feature_display(PAGE_RES* page_res, + const TBOX& selection_box) { + PAGE_RES_IT* it = make_pseudo_word(page_res, selection_box); + if (it != NULL) { + WERD_RES* word_res = it->word(); + word_res->x_height = it->row()->row->x_height(); + word_res->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, NULL, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + it->row()->row, it->block()->block); + TWERD* bln_word = word_res->chopped_word; + TBLOB* bln_blob = bln_word->blobs[0]; + INT_FX_RESULT_STRUCT fx_info; + GenericVector bl_features; + GenericVector cn_features; + Classify::ExtractFeatures(*bln_blob, classify_nonlinear_norm, &bl_features, + &cn_features, &fx_info, NULL); + // Display baseline features. + ScrollView* bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0); + ClearFeatureSpaceWindow(baseline, bl_win); + for (int f = 0; f < bl_features.size(); ++f) + RenderIntFeature(bl_win, &bl_features[f], ScrollView::GREEN); + bl_win->Update(); + // Display cn features. + ScrollView* cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0); + ClearFeatureSpaceWindow(character, cn_win); + for (int f = 0; f < cn_features.size(); ++f) + RenderIntFeature(cn_win, &cn_features[f], ScrollView::GREEN); + cn_win->Update(); + + it->DeleteCurrentWord(); + delete it; + } + } + + +#endif // GRAPHICS_DISABLED + +} // namespace tesseract + + + diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.h b/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.h new file mode 100644 index 0000000..5649782 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/pgedit.h @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////// +// File: pgedit.h +// Description: Page structure file editor +// Author: Joern Wanke +// Created: Wed Jul 18 10:05: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 PGEDIT_H +#define PGEDIT_H + +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "rect.h" +#include "params.h" +#include "tesseractclass.h" + +class ScrollView; +class SVMenuNode; +struct SVEvent; + +// A small event handler class to process incoming events to +// this window. +class PGEventHandler : public SVEventHandler { +public: + PGEventHandler(tesseract::Tesseract* tess) : tess_(tess) { + } + void Notify(const SVEvent* sve); +private: + tesseract::Tesseract* tess_; +}; + +extern BLOCK_LIST *current_block_list; +extern STRING_VAR_H(editor_image_win_name, "EditorImage", + "Editor image window name"); +extern INT_VAR_H(editor_image_xpos, 590, "Editor image X Pos"); +extern INT_VAR_H(editor_image_ypos, 10, "Editor image Y Pos"); +extern INT_VAR_H(editor_image_height, 680, "Editor image height"); +extern INT_VAR_H(editor_image_width, 655, "Editor image width"); +extern INT_VAR_H(editor_image_word_bb_color, BLUE, + "Word bounding box colour"); +extern INT_VAR_H(editor_image_blob_bb_color, YELLOW, + "Blob bounding box colour"); +extern INT_VAR_H(editor_image_text_color, WHITE, "Correct text colour"); +extern STRING_VAR_H(editor_dbwin_name, "EditorDBWin", + "Editor debug window name"); +extern INT_VAR_H(editor_dbwin_xpos, 50, "Editor debug window X Pos"); +extern INT_VAR_H(editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +extern INT_VAR_H(editor_dbwin_height, 24, "Editor debug window height"); +extern INT_VAR_H(editor_dbwin_width, 80, "Editor debug window width"); +extern STRING_VAR_H(editor_word_name, "BlnWords", + "BL normalised word window"); +extern INT_VAR_H(editor_word_xpos, 60, "Word window X Pos"); +extern INT_VAR_H(editor_word_ypos, 510, "Word window Y Pos"); +extern INT_VAR_H(editor_word_height, 240, "Word window height"); +extern INT_VAR_H(editor_word_width, 655, "Word window width"); +extern double_VAR_H(editor_smd_scale_factor, 1.0, "Scaling for smd image"); + +ScrollView* bln_word_window_handle(); //return handle +void build_image_window(int width, int height); +void display_bln_lines(ScrollView window, + ScrollView::Color colour, + float scale_factor, + float y_offset, + float minx, + float maxx); +//function to call +void pgeditor_msg( //message display + const char *msg); +void pgeditor_show_point( //display coords + SVEvent *event); +//put bln word in box +void show_point(PAGE_RES* page_res, float x, float y); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/recogtraining.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/recogtraining.cpp new file mode 100644 index 0000000..f40893f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/recogtraining.cpp @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////// +// File: recogtraining.cpp +// Description: Functions for ambiguity and parameter training. +// Author: Daria Antonova +// Created: Mon Aug 13 11:26:43 PDT 2009 +// +// (C) Copyright 2009, 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 "tesseractclass.h" + +#include "boxread.h" +#include "control.h" +#include "cutil.h" +#include "host.h" +#include "ratngs.h" +#include "reject.h" +#include "stopper.h" + +namespace tesseract { + + const inT16 kMaxBoxEdgeDiff = 2; + + // Sets flags necessary for recognition in the training mode. + // Opens and returns the pointer to the output file. + FILE *Tesseract::init_recog_training(const STRING &fname) { + if (tessedit_ambigs_training) { + tessedit_tess_adaption_mode.set_value(0); // turn off adaption + tessedit_enable_doc_dict.set_value(0); // turn off document dictionary + // Explore all segmentations. + getDict().stopper_no_acceptable_choices.set_value(1); + } + + STRING output_fname = fname; + const char *lastdot = strrchr(output_fname.string(), '.'); + if (lastdot != NULL) output_fname[lastdot - output_fname.string()] = '\0'; + output_fname += ".txt"; + FILE *output_file = open_file(output_fname.string(), "a+"); + return output_file; + } + + // Copies the bounding box from page_res_it->word() to the given TBOX. + bool read_t(PAGE_RES_IT *page_res_it, TBOX *tbox) { + while (page_res_it->block() != NULL && page_res_it->word() == NULL) + page_res_it->forward(); + + if (page_res_it->word() != NULL) { + *tbox = page_res_it->word()->word->bounding_box(); + + // If tbox->left() is negative, the training image has vertical text and + // all the coordinates of bounding boxes of page_res are rotated by 90 + // degrees in a counterclockwise direction. We need to rotate the TBOX back + // in order to compare with the TBOXes of box files. + if (tbox->left() < 0) { + tbox->rotate(FCOORD(0.0, -1.0)); + } + + return true; + } + else { + return false; + } + } + + // This function takes tif/box pair of files and runs recognition on the image, + // while making sure that the word bounds that tesseract identified roughly + // match to those specified by the input box file. For each word (ngram in a + // single bounding box from the input box file) it outputs the ocred result, + // the correct label, rating and certainty. + void Tesseract::recog_training_segmented(const STRING &fname, + PAGE_RES *page_res, + volatile ETEXT_DESC *monitor, + FILE *output_file) { + STRING box_fname = fname; + const char *lastdot = strrchr(box_fname.string(), '.'); + if (lastdot != NULL) box_fname[lastdot - box_fname.string()] = '\0'; + box_fname += ".box"; + // ReadNextBox() will close box_file + FILE *box_file = open_file(box_fname.string(), "r"); + + PAGE_RES_IT page_res_it; + page_res_it.page_res = page_res; + page_res_it.restart_page(); + STRING label; + + // Process all the words on this page. + TBOX tbox; // tesseract-identified box + TBOX bbox; // box from the box file + bool keep_going; + int line_number = 0; + int examined_words = 0; + do { + keep_going = read_t(&page_res_it, &tbox); + keep_going &= ReadNextBox(applybox_page, &line_number, box_file, &label, + &bbox); + // Align bottom left points of the TBOXes. + while (keep_going && + !NearlyEqual(tbox.bottom(), bbox.bottom(), kMaxBoxEdgeDiff)) { + if (bbox.bottom() < tbox.bottom()) { + page_res_it.forward(); + keep_going = read_t(&page_res_it, &tbox); + } + else { + keep_going = ReadNextBox(applybox_page, &line_number, box_file, &label, + &bbox); + } + } + while (keep_going && + !NearlyEqual(tbox.left(), bbox.left(), kMaxBoxEdgeDiff)) { + if (bbox.left() > tbox.left()) { + page_res_it.forward(); + keep_going = read_t(&page_res_it, &tbox); + } + else { + keep_going = ReadNextBox(applybox_page, &line_number, box_file, &label, + &bbox); + } + } + // OCR the word if top right points of the TBOXes are similar. + if (keep_going && + NearlyEqual(tbox.right(), bbox.right(), kMaxBoxEdgeDiff) && + NearlyEqual(tbox.top(), bbox.top(), kMaxBoxEdgeDiff)) { + ambigs_classify_and_output(label.string(), &page_res_it, output_file); + examined_words++; + } + page_res_it.forward(); + } while (keep_going); + + // Set up scripts on all of the words that did not get sent to + // ambigs_classify_and_output. They all should have, but if all the + // werd_res's don't get uch_sets, tesseract will crash when you try + // to iterate over them. :-( + int total_words = 0; + for (page_res_it.restart_page(); page_res_it.block() != NULL; + page_res_it.forward()) { + if (page_res_it.word()) { + if (page_res_it.word()->uch_set == NULL) + page_res_it.word()->SetupFake(unicharset); + total_words++; + } + } + if (examined_words < 0.85 * total_words) { + tprintf("TODO(antonova): clean up recog_training_segmented; " + " It examined only a small fraction of the ambigs image.\n"); + } + tprintf("recog_training_segmented: examined %d / %d words.\n", + examined_words, total_words); + } + + // Helper prints the given set of blob choices. + static void PrintPath(int length, const BLOB_CHOICE** blob_choices, + const UNICHARSET& unicharset, + const char *label, FILE *output_file) { + float rating = 0.0f; + float certainty = 0.0f; + for (int i = 0; i < length; ++i) { + const BLOB_CHOICE* blob_choice = blob_choices[i]; + fprintf(output_file, "%s", + unicharset.id_to_unichar(blob_choice->unichar_id())); + rating += blob_choice->rating(); + if (certainty > blob_choice->certainty()) + certainty = blob_choice->certainty(); + } + fprintf(output_file, "\t%s\t%.4f\t%.4f\n", + label, rating, certainty); + } + + // Helper recursively prints all paths through the ratings matrix, starting + // at column col. + static void PrintMatrixPaths(int col, int dim, + const MATRIX& ratings, + int length, const BLOB_CHOICE** blob_choices, + const UNICHARSET& unicharset, + const char *label, FILE *output_file) { + for (int row = col; row < dim && row - col < ratings.bandwidth(); ++row) { + if (ratings.get(col, row) != NOT_CLASSIFIED) { + BLOB_CHOICE_IT bc_it(ratings.get(col, row)); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + blob_choices[length] = bc_it.data(); + if (row + 1 < dim) { + PrintMatrixPaths(row + 1, dim, ratings, length + 1, blob_choices, + unicharset, label, output_file); + } + else { + PrintPath(length + 1, blob_choices, unicharset, label, output_file); + } + } + } + } + } + + // Runs classify_word_pass1() on the current word. Outputs Tesseract's + // raw choice as a result of the classification. For words labeled with a + // single unichar also outputs all alternatives from blob_choices of the + // best choice. + void Tesseract::ambigs_classify_and_output(const char *label, + PAGE_RES_IT* pr_it, + FILE *output_file) { + // Classify word. + fflush(stdout); + WordData word_data(*pr_it); + SetupWordPassN(1, &word_data); + classify_word_and_language(1, pr_it, &word_data); + WERD_RES* werd_res = word_data.word; + WERD_CHOICE *best_choice = werd_res->best_choice; + ASSERT_HOST(best_choice != NULL); + + // Compute the number of unichars in the label. + GenericVector encoding; + if (!unicharset.encode_string(label, true, &encoding, NULL, NULL)) { + tprintf("Not outputting illegal unichar %s\n", label); + return; + } + + // Dump all paths through the ratings matrix (which is normally small). + int dim = werd_res->ratings->dimension(); + const BLOB_CHOICE** blob_choices = new const BLOB_CHOICE*[dim]; + PrintMatrixPaths(0, dim, *werd_res->ratings, 0, blob_choices, + unicharset, label, output_file); + delete[] blob_choices; + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/reject.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/reject.cpp new file mode 100644 index 0000000..b259943 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/reject.cpp @@ -0,0 +1,798 @@ +/********************************************************************** + * File: reject.cpp (Formerly reject.c) + * Description: Rejection functions used in tessedit + * Author: Phil Cheatle + * Created: Wed Sep 23 16:50:21 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#pragma warning(disable:4305) // int/float warnings +#endif + +#include "tessvars.h" +#ifdef __UNIX__ +#include +#include +#endif +#include "scanutils.h" +#include +#include +#include "genericvector.h" +#include "reject.h" +#include "control.h" +#include "docqual.h" +#include "globaloc.h" // For err_exit. +#include "globals.h" +#include "helpers.h" + +#include "tesseractclass.h" + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +CLISTIZEH(STRING) CLISTIZE(STRING) + +/************************************************************************* + * set_done() + * + * Set the done flag based on the word acceptability criteria + *************************************************************************/ + + namespace tesseract { + void Tesseract::set_done(WERD_RES *word, inT16 pass) { + word->done = word->tess_accepted && + (strchr(word->best_choice->unichar_string().string(), ' ') == NULL); + bool word_is_ambig = word->best_choice->dangerous_ambig_found(); + bool word_from_dict = word->best_choice->permuter() == SYSTEM_DAWG_PERM || + word->best_choice->permuter() == FREQ_DAWG_PERM || + word->best_choice->permuter() == USER_DAWG_PERM; + if (word->done && (pass == 1) && (!word_from_dict || word_is_ambig) && + one_ell_conflict(word, FALSE)) { + if (tessedit_rejection_debug) tprintf("one_ell_conflict detected\n"); + word->done = FALSE; + } + if (word->done && ((!word_from_dict && + word->best_choice->permuter() != NUMBER_PERM) || word_is_ambig)) { + if (tessedit_rejection_debug) tprintf("non-dict or ambig word detected\n"); + word->done = FALSE; + } + if (tessedit_rejection_debug) { + tprintf("set_done(): done=%d\n", word->done); + word->best_choice->print(""); + } + } + + + /************************************************************************* + * make_reject_map() + * + * Sets the done flag to indicate whether the resylt is acceptable. + * + * Sets a reject map for the word. + *************************************************************************/ + void Tesseract::make_reject_map(WERD_RES *word, ROW *row, inT16 pass) { + int i; + int offset; + + flip_0O(word); + check_debug_pt(word, -1); // For trap only + set_done(word, pass); // Set acceptance + word->reject_map.initialise(word->best_choice->unichar_lengths().length()); + reject_blanks(word); + /* + 0: Rays original heuristic - the baseline + */ + if (tessedit_reject_mode == 0) { + if (!word->done) + reject_poor_matches(word); + } + else if (tessedit_reject_mode == 5) { + /* + 5: Reject I/1/l from words where there is no strong contextual confirmation; + the whole of any unacceptable words (incl PERM rej of dubious 1/I/ls); + and the whole of any words which are very small + */ + if (kBlnXHeight / word->denorm.y_scale() <= min_sane_x_ht_pixels) { + word->reject_map.rej_word_small_xht(); + } + else { + one_ell_conflict(word, TRUE); + /* + Originally the code here just used the done flag. Now I have duplicated + and unpacked the conditions for setting the done flag so that each + mechanism can be turned on or off independently. This works WITHOUT + affecting the done flag setting. + */ + if (rej_use_tess_accepted && !word->tess_accepted) + word->reject_map.rej_word_not_tess_accepted(); + + if (rej_use_tess_blanks && + (strchr(word->best_choice->unichar_string().string(), ' ') != NULL)) + word->reject_map.rej_word_contains_blanks(); + + WERD_CHOICE* best_choice = word->best_choice; + if (rej_use_good_perm) { + if ((best_choice->permuter() == SYSTEM_DAWG_PERM || + best_choice->permuter() == FREQ_DAWG_PERM || + best_choice->permuter() == USER_DAWG_PERM) && + (!rej_use_sensible_wd || + acceptable_word_string(*word->uch_set, + best_choice->unichar_string().string(), + best_choice->unichar_lengths().string()) != + AC_UNACCEPTABLE)) { + // PASSED TEST + } + else if (best_choice->permuter() == NUMBER_PERM) { + if (rej_alphas_in_number_perm) { + for (i = 0, offset = 0; + best_choice->unichar_string()[offset] != '\0'; + offset += best_choice->unichar_lengths()[i++]) { + if (word->reject_map[i].accepted() && + word->uch_set->get_isalpha( + best_choice->unichar_string().string() + offset, + best_choice->unichar_lengths()[i])) + word->reject_map[i].setrej_bad_permuter(); + // rej alpha + } + } + } + else { + word->reject_map.rej_word_bad_permuter(); + } + } + /* Ambig word rejection was here once !!*/ + } + } + else { + tprintf("BAD tessedit_reject_mode\n"); + err_exit(); + } + + if (tessedit_image_border > -1) + reject_edge_blobs(word); + + check_debug_pt(word, 10); + if (tessedit_rejection_debug) { + tprintf("Permuter Type = %d\n", word->best_choice->permuter()); + tprintf("Certainty: %f Rating: %f\n", + word->best_choice->certainty(), word->best_choice->rating()); + tprintf("Dict word: %d\n", dict_word(*(word->best_choice))); + } + + flip_hyphens(word); + check_debug_pt(word, 20); + } +} // namespace tesseract + + +void reject_blanks(WERD_RES *word) { + inT16 i; + inT16 offset; + + for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0'; + offset += word->best_choice->unichar_lengths()[i], i += 1) { + if (word->best_choice->unichar_string()[offset] == ' ') + //rej unrecognised blobs + word->reject_map[i].setrej_tess_failure(); + } +} + +namespace tesseract { + void Tesseract::reject_I_1_L(WERD_RES *word) { + inT16 i; + inT16 offset; + + for (i = 0, offset = 0; word->best_choice->unichar_string()[offset] != '\0'; + offset += word->best_choice->unichar_lengths()[i], i += 1) { + if (STRING(conflict_set_I_l_1). + contains(word->best_choice->unichar_string()[offset])) { + //rej 1Il conflict + word->reject_map[i].setrej_1Il_conflict(); + } + } + } +} // namespace tesseract + + +void reject_poor_matches(WERD_RES *word) { + float threshold = compute_reject_threshold(word->best_choice); + for (int i = 0; i < word->best_choice->length(); ++i) { + if (word->best_choice->unichar_id(i) == UNICHAR_SPACE) + word->reject_map[i].setrej_tess_failure(); + else if (word->best_choice->certainty(i) < threshold) + word->reject_map[i].setrej_poor_match(); + } +} + + +/********************************************************************** + * compute_reject_threshold + * + * Set a rejection threshold for this word. + * Initially this is a trivial function which looks for the largest + * gap in the certainty value. + **********************************************************************/ + +float compute_reject_threshold(WERD_CHOICE* word) { + float threshold; // rejection threshold + float bestgap = 0.0f; // biggest gap + float gapstart; // bottom of gap + // super iterator + BLOB_CHOICE_IT choice_it; // real iterator + + int blob_count = word->length(); + GenericVector ratings; + ratings.resize_no_init(blob_count); + for (int i = 0; i < blob_count; ++i) { + ratings[i] = word->certainty(i); + } + ratings.sort(); + gapstart = ratings[0] - 1; // all reject if none better + if (blob_count >= 3) { + for (int index = 0; index < blob_count - 1; index++) { + if (ratings[index + 1] - ratings[index] > bestgap) { + bestgap = ratings[index + 1] - ratings[index]; + // find biggest + gapstart = ratings[index]; + } + } + } + threshold = gapstart + bestgap / 2; + + return threshold; +} + + +/************************************************************************* + * reject_edge_blobs() + * + * If the word is perilously close to the edge of the image, reject those blobs + * in the word which are too close to the edge as they could be clipped. + *************************************************************************/ +namespace tesseract { + void Tesseract::reject_edge_blobs(WERD_RES *word) { + TBOX word_box = word->word->bounding_box(); + // Use the box_word as it is already denormed back to image coordinates. + int blobcount = word->box_word->length(); + + if (word_box.left() < tessedit_image_border || + word_box.bottom() < tessedit_image_border || + word_box.right() + tessedit_image_border > ImageWidth() - 1 || + word_box.top() + tessedit_image_border > ImageHeight() - 1) { + ASSERT_HOST(word->reject_map.length() == blobcount); + for (int blobindex = 0; blobindex < blobcount; blobindex++) { + TBOX blob_box = word->box_word->BlobBox(blobindex); + if (blob_box.left() < tessedit_image_border || + blob_box.bottom() < tessedit_image_border || + blob_box.right() + tessedit_image_border > ImageWidth() - 1 || + blob_box.top() + tessedit_image_border > ImageHeight() - 1) { + word->reject_map[blobindex].setrej_edge_char(); + // Close to edge + } + } + } + } + + /********************************************************************** + * one_ell_conflict() + * + * Identify words where there is a potential I/l/1 error. + * - A bundle of contextual heuristics! + **********************************************************************/ + BOOL8 Tesseract::one_ell_conflict(WERD_RES *word_res, BOOL8 update_map) { + const char *word; + const char *lengths; + inT16 word_len; //its length + inT16 first_alphanum_index_; + inT16 first_alphanum_offset_; + inT16 i; + inT16 offset; + BOOL8 non_conflict_set_char; //non conf set a/n? + BOOL8 conflict = FALSE; + BOOL8 allow_1s; + ACCEPTABLE_WERD_TYPE word_type; + BOOL8 dict_perm_type; + BOOL8 dict_word_ok; + int dict_word_type; + + word = word_res->best_choice->unichar_string().string(); + lengths = word_res->best_choice->unichar_lengths().string(); + word_len = strlen(lengths); + /* + If there are no occurrences of the conflict set characters then the word + is OK. + */ + if (strpbrk(word, conflict_set_I_l_1.string()) == NULL) + return FALSE; + + /* + There is a conflict if there are NO other (confirmed) alphanumerics apart + from those in the conflict set. + */ + + for (i = 0, offset = 0, non_conflict_set_char = FALSE; + (i < word_len) && !non_conflict_set_char; offset += lengths[i++]) + non_conflict_set_char = + (word_res->uch_set->get_isalpha(word + offset, lengths[i]) || + word_res->uch_set->get_isdigit(word + offset, lengths[i])) && + !STRING(conflict_set_I_l_1).contains(word[offset]); + if (!non_conflict_set_char) { + if (update_map) + reject_I_1_L(word_res); + return TRUE; + } + + /* + If the word is accepted by a dawg permuter, and the first alpha character + is "I" or "l", check to see if the alternative is also a dawg word. If it + is, then there is a potential error otherwise the word is ok. + */ + + dict_perm_type = (word_res->best_choice->permuter() == SYSTEM_DAWG_PERM) || + (word_res->best_choice->permuter() == USER_DAWG_PERM) || + (rej_trust_doc_dawg && + (word_res->best_choice->permuter() == DOC_DAWG_PERM)) || + (word_res->best_choice->permuter() == FREQ_DAWG_PERM); + dict_word_type = dict_word(*(word_res->best_choice)); + dict_word_ok = (dict_word_type > 0) && + (rej_trust_doc_dawg || (dict_word_type != DOC_DAWG_PERM)); + + if ((rej_1Il_use_dict_word && dict_word_ok) || + (rej_1Il_trust_permuter_type && dict_perm_type) || + (dict_perm_type && dict_word_ok)) { + first_alphanum_index_ = first_alphanum_index(word, lengths); + first_alphanum_offset_ = first_alphanum_offset(word, lengths); + if (lengths[first_alphanum_index_] == 1 && + word[first_alphanum_offset_] == 'I') { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; + if (safe_dict_word(word_res) > 0) { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; + if (update_map) + word_res->reject_map[first_alphanum_index_]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; + return FALSE; + } + } + + if (lengths[first_alphanum_index_] == 1 && + word[first_alphanum_offset_] == 'l') { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; + if (safe_dict_word(word_res) > 0) { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; + if (update_map) + word_res->reject_map[first_alphanum_index_]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; + return FALSE; + } + } + return FALSE; + } + + /* + NEW 1Il code. The old code relied on permuter types too much. In fact, + tess will use TOP_CHOICE permute for good things like "palette". + In this code the string is examined independently to see if it looks like + a well formed word. + */ + + /* + REGARDLESS OF PERMUTER, see if flipping a leading I/l generates a + dictionary word. + */ + first_alphanum_index_ = first_alphanum_index(word, lengths); + first_alphanum_offset_ = first_alphanum_offset(word, lengths); + if (lengths[first_alphanum_index_] == 1 && + word[first_alphanum_offset_] == 'l') { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; + if (safe_dict_word(word_res) > 0) + return FALSE; + else + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; + } + else if (lengths[first_alphanum_index_] == 1 && + word[first_alphanum_offset_] == 'I') { + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'l'; + if (safe_dict_word(word_res) > 0) + return FALSE; + else + word_res->best_choice->unichar_string()[first_alphanum_offset_] = 'I'; + } + /* + For strings containing digits: + If there are no alphas OR the numeric permuter liked the word, + reject any non 1 conflict chs + Else reject all conflict chs + */ + if (word_contains_non_1_digit(word, lengths)) { + allow_1s = (alpha_count(word, lengths) == 0) || + (word_res->best_choice->permuter() == NUMBER_PERM); + + inT16 offset; + conflict = FALSE; + for (i = 0, offset = 0; word[offset] != '\0'; + offset += word_res->best_choice->unichar_lengths()[i++]) { + if ((!allow_1s || (word[offset] != '1')) && + STRING(conflict_set_I_l_1).contains(word[offset])) { + if (update_map) + word_res->reject_map[i].setrej_1Il_conflict(); + conflict = TRUE; + } + } + return conflict; + } + /* + For anything else. See if it conforms to an acceptable word type. If so, + treat accordingly. + */ + word_type = acceptable_word_string(*word_res->uch_set, word, lengths); + if ((word_type == AC_LOWER_CASE) || (word_type == AC_INITIAL_CAP)) { + first_alphanum_index_ = first_alphanum_index(word, lengths); + first_alphanum_offset_ = first_alphanum_offset(word, lengths); + if (STRING(conflict_set_I_l_1).contains(word[first_alphanum_offset_])) { + if (update_map) + word_res->reject_map[first_alphanum_index_]. + setrej_1Il_conflict(); + return TRUE; + } + else + return FALSE; + } + else if (word_type == AC_UPPER_CASE) { + return FALSE; + } + else { + if (update_map) + reject_I_1_L(word_res); + return TRUE; + } + } + + + inT16 Tesseract::first_alphanum_index(const char *word, + const char *word_lengths) { + inT16 i; + inT16 offset; + + for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { + if (unicharset.get_isalpha(word + offset, word_lengths[i]) || + unicharset.get_isdigit(word + offset, word_lengths[i])) + return i; + } + return -1; + } + + inT16 Tesseract::first_alphanum_offset(const char *word, + const char *word_lengths) { + inT16 i; + inT16 offset; + + for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { + if (unicharset.get_isalpha(word + offset, word_lengths[i]) || + unicharset.get_isdigit(word + offset, word_lengths[i])) + return offset; + } + return -1; + } + + inT16 Tesseract::alpha_count(const char *word, + const char *word_lengths) { + inT16 i; + inT16 offset; + inT16 count = 0; + + for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { + if (unicharset.get_isalpha(word + offset, word_lengths[i])) + count++; + } + return count; + } + + + BOOL8 Tesseract::word_contains_non_1_digit(const char *word, + const char *word_lengths) { + inT16 i; + inT16 offset; + + for (i = 0, offset = 0; word[offset] != '\0'; offset += word_lengths[i++]) { + if (unicharset.get_isdigit(word + offset, word_lengths[i]) && + (word_lengths[i] != 1 || word[offset] != '1')) + return TRUE; + } + return FALSE; + } + + /************************************************************************* + * dont_allow_1Il() + * Don't unreject LONE accepted 1Il conflict set chars + *************************************************************************/ + void Tesseract::dont_allow_1Il(WERD_RES *word) { + int i = 0; + int offset; + int word_len = word->reject_map.length(); + const char *s = word->best_choice->unichar_string().string(); + const char *lengths = word->best_choice->unichar_lengths().string(); + BOOL8 accepted_1Il = FALSE; + + for (i = 0, offset = 0; i < word_len; + offset += word->best_choice->unichar_lengths()[i++]) { + if (word->reject_map[i].accepted()) { + if (STRING(conflict_set_I_l_1).contains(s[offset])) { + accepted_1Il = TRUE; + } + else { + if (word->uch_set->get_isalpha(s + offset, lengths[i]) || + word->uch_set->get_isdigit(s + offset, lengths[i])) + return; // >=1 non 1Il ch accepted + } + } + } + if (!accepted_1Il) + return; //Nothing to worry about + + for (i = 0, offset = 0; i < word_len; + offset += word->best_choice->unichar_lengths()[i++]) { + if (STRING(conflict_set_I_l_1).contains(s[offset]) && + word->reject_map[i].accepted()) + word->reject_map[i].setrej_postNN_1Il(); + } + } + + + inT16 Tesseract::count_alphanums(WERD_RES *word_res) { + int count = 0; + const WERD_CHOICE *best_choice = word_res->best_choice; + for (int i = 0; i < word_res->reject_map.length(); ++i) { + if ((word_res->reject_map[i].accepted()) && + (word_res->uch_set->get_isalpha(best_choice->unichar_id(i)) || + word_res->uch_set->get_isdigit(best_choice->unichar_id(i)))) { + count++; + } + } + return count; + } + + + // reject all if most rejected. + void Tesseract::reject_mostly_rejects(WERD_RES *word) { + /* Reject the whole of the word if the fraction of rejects exceeds a limit */ + + if ((float)word->reject_map.reject_count() / word->reject_map.length() >= + rej_whole_of_mostly_reject_word_fract) + word->reject_map.rej_word_mostly_rej(); + } + + + BOOL8 Tesseract::repeated_nonalphanum_wd(WERD_RES *word, ROW *row) { + inT16 char_quality; + inT16 accepted_char_quality; + + if (word->best_choice->unichar_lengths().length() <= 1) + return FALSE; + + if (!STRING(ok_repeated_ch_non_alphanum_wds). + contains(word->best_choice->unichar_string()[0])) + return FALSE; + + UNICHAR_ID uch_id = word->best_choice->unichar_id(0); + for (int i = 1; i < word->best_choice->length(); ++i) { + if (word->best_choice->unichar_id(i) != uch_id) return FALSE; + } + + word_char_quality(word, row, &char_quality, &accepted_char_quality); + + if ((word->best_choice->unichar_lengths().length() == char_quality) && + (char_quality == accepted_char_quality)) + return TRUE; + else + return FALSE; + } + + inT16 Tesseract::safe_dict_word(const WERD_RES *werd_res) { + const WERD_CHOICE &word = *werd_res->best_choice; + int dict_word_type = werd_res->tesseract->dict_word(word); + return dict_word_type == DOC_DAWG_PERM ? 0 : dict_word_type; + } + + // Note: After running this function word_res->ratings + // might not contain the right BLOB_CHOICE corresponding to each character + // in word_res->best_choice. + void Tesseract::flip_hyphens(WERD_RES *word_res) { + WERD_CHOICE *best_choice = word_res->best_choice; + int i; + int prev_right = -9999; + int next_left; + TBOX out_box; + float aspect_ratio; + + if (tessedit_lower_flip_hyphen <= 1) + return; + + int num_blobs = word_res->rebuild_word->NumBlobs(); + UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-"); + for (i = 0; i < best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; + out_box = blob->bounding_box(); + if (i + 1 == num_blobs) + next_left = 9999; + else + next_left = word_res->rebuild_word->blobs[i + 1]->bounding_box().left(); + // Don't touch small or touching blobs - it is too dangerous. + if ((out_box.width() > 8 * word_res->denorm.x_scale()) && + (out_box.left() > prev_right) && (out_box.right() < next_left)) { + aspect_ratio = out_box.width() / (float)out_box.height(); + if (word_res->uch_set->eq(best_choice->unichar_id(i), ".")) { + if (aspect_ratio >= tessedit_upper_flip_hyphen && + word_res->uch_set->contains_unichar_id(unichar_dash) && + word_res->uch_set->get_enabled(unichar_dash)) { + /* Certain HYPHEN */ + best_choice->set_unichar_id(unichar_dash, i); + if (word_res->reject_map[i].rejected()) + word_res->reject_map[i].setrej_hyphen_accept(); + } + if ((aspect_ratio > tessedit_lower_flip_hyphen) && + word_res->reject_map[i].accepted()) + //Suspected HYPHEN + word_res->reject_map[i].setrej_hyphen(); + } + else if (best_choice->unichar_id(i) == unichar_dash) { + if ((aspect_ratio >= tessedit_upper_flip_hyphen) && + (word_res->reject_map[i].rejected())) + word_res->reject_map[i].setrej_hyphen_accept(); + //Certain HYPHEN + + if ((aspect_ratio <= tessedit_lower_flip_hyphen) && + (word_res->reject_map[i].accepted())) + //Suspected HYPHEN + word_res->reject_map[i].setrej_hyphen(); + } + } + prev_right = out_box.right(); + } + } + + // Note: After running this function word_res->ratings + // might not contain the right BLOB_CHOICE corresponding to each character + // in word_res->best_choice. + void Tesseract::flip_0O(WERD_RES *word_res) { + WERD_CHOICE *best_choice = word_res->best_choice; + int i; + TBOX out_box; + + if (!tessedit_flip_0O) + return; + + int num_blobs = word_res->rebuild_word->NumBlobs(); + for (i = 0; i < best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; + if (word_res->uch_set->get_isupper(best_choice->unichar_id(i)) || + word_res->uch_set->get_isdigit(best_choice->unichar_id(i))) { + out_box = blob->bounding_box(); + if ((out_box.top() < kBlnBaselineOffset + kBlnXHeight) || + (out_box.bottom() > kBlnBaselineOffset + kBlnXHeight / 4)) + return; //Beware words with sub/superscripts + } + } + UNICHAR_ID unichar_0 = word_res->uch_set->unichar_to_id("0"); + UNICHAR_ID unichar_O = word_res->uch_set->unichar_to_id("O"); + if (unichar_0 == INVALID_UNICHAR_ID || + !word_res->uch_set->get_enabled(unichar_0) || + unichar_O == INVALID_UNICHAR_ID || + !word_res->uch_set->get_enabled(unichar_O)) { + return; // 0 or O are not present/enabled in unicharset + } + for (i = 1; i < best_choice->length(); ++i) { + if (best_choice->unichar_id(i) == unichar_0 || + best_choice->unichar_id(i) == unichar_O) { + /* A0A */ + if ((i + 1) < best_choice->length() && + non_O_upper(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + non_O_upper(*word_res->uch_set, best_choice->unichar_id(i + 1))) { + best_choice->set_unichar_id(unichar_O, i); + } + /* A00A */ + if (non_O_upper(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (i + 1) < best_choice->length() && + (best_choice->unichar_id(i + 1) == unichar_0 || + best_choice->unichar_id(i + 1) == unichar_O) && + (i + 2) < best_choice->length() && + non_O_upper(*word_res->uch_set, best_choice->unichar_id(i + 2))) { + best_choice->set_unichar_id(unichar_O, i); + i++; + } + /* AA0 */ + if ((i > 1) && + non_O_upper(*word_res->uch_set, best_choice->unichar_id(i - 2)) && + non_O_upper(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (((i + 1) < best_choice->length() && + !word_res->uch_set->get_isdigit(best_choice->unichar_id(i + 1)) && + !word_res->uch_set->eq(best_choice->unichar_id(i + 1), "l") && + !word_res->uch_set->eq(best_choice->unichar_id(i + 1), "I")) || + (i == best_choice->length() - 1))) { + best_choice->set_unichar_id(unichar_O, i); + } + /* 9O9 */ + if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (i + 1) < best_choice->length() && + non_0_digit(*word_res->uch_set, best_choice->unichar_id(i + 1))) { + best_choice->set_unichar_id(unichar_0, i); + } + /* 9OOO */ + if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (i + 2) < best_choice->length() && + (best_choice->unichar_id(i + 1) == unichar_0 || + best_choice->unichar_id(i + 1) == unichar_O) && + (best_choice->unichar_id(i + 2) == unichar_0 || + best_choice->unichar_id(i + 2) == unichar_O)) { + best_choice->set_unichar_id(unichar_0, i); + best_choice->set_unichar_id(unichar_0, i + 1); + best_choice->set_unichar_id(unichar_0, i + 2); + i += 2; + } + /* 9OO */ + if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (i + 2) < best_choice->length() && + (best_choice->unichar_id(i + 1) == unichar_0 || + best_choice->unichar_id(i + 1) == unichar_O) && + !word_res->uch_set->get_isupper(best_choice->unichar_id(i + 2))) { + best_choice->set_unichar_id(unichar_0, i); + best_choice->set_unichar_id(unichar_0, i + 1); + i++; + } + /* 9O */ + if (non_0_digit(*word_res->uch_set, best_choice->unichar_id(i - 1)) && + (i + 1) < best_choice->length() && + !word_res->uch_set->get_isupper(best_choice->unichar_id(i + 1))) { + best_choice->set_unichar_id(unichar_0, i); + } + /* 9[.,]OOO.. */ + if ((i > 1) && + (word_res->uch_set->eq(best_choice->unichar_id(i - 1), ".") || + word_res->uch_set->eq(best_choice->unichar_id(i - 1), ",")) && + (word_res->uch_set->get_isdigit(best_choice->unichar_id(i - 2)) || + best_choice->unichar_id(i - 2) == unichar_O)) { + if (best_choice->unichar_id(i - 2) == unichar_O) { + best_choice->set_unichar_id(unichar_0, i - 2); + } + while (i < best_choice->length() && + (best_choice->unichar_id(i) == unichar_O || + best_choice->unichar_id(i) == unichar_0)) { + best_choice->set_unichar_id(unichar_0, i); + i++; + } + i--; + } + } + } + } + + BOOL8 Tesseract::non_O_upper(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) { + return ch_set.get_isupper(unichar_id) && !ch_set.eq(unichar_id, "O"); + } + + BOOL8 Tesseract::non_0_digit(const UNICHARSET& ch_set, UNICHAR_ID unichar_id) { + return ch_set.get_isdigit(unichar_id) && !ch_set.eq(unichar_id, "0"); + } +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/reject.h b/hgdriver/3rdparty/hgOCR/include/ccmain/reject.h new file mode 100644 index 0000000..cffb262 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/reject.h @@ -0,0 +1,34 @@ +/********************************************************************** + * File: reject.h (Formerly reject.h) + * Description: Rejection functions used in tessedit + * Author: Phil Cheatle + * Created: Wed Sep 23 16:50:21 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef REJECT_H +#define REJECT_H + +#include "params.h" +#include "pageres.h" + +void reject_blanks(WERD_RES *word); +void reject_poor_matches(WERD_RES *word); +float compute_reject_threshold(WERD_CHOICE* word); +BOOL8 word_contains_non_1_digit(const char *word, const char *word_lengths); +void dont_allow_1Il(WERD_RES *word); +void flip_hyphens(WERD_RES *word); +void flip_0O(WERD_RES *word); +BOOL8 non_0_digit(const char* str, int length); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.cpp new file mode 100644 index 0000000..105cc78 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.cpp @@ -0,0 +1,683 @@ +/////////////////////////////////////////////////////////////////////// +// File: resultiterator.cpp +// Description: Iterator for tesseract results that is capable of +// iterating in proper reading order over Bi Directional +// (e.g. mixed Hebrew and English) text. +// Author: David Eger +// Created: Fri May 27 13:58:06 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. +// +/////////////////////////////////////////////////////////////////////// + +#include "resultiterator.h" + +#include "allheaders.h" +#include "pageres.h" +#include "strngs.h" +#include "tesseractclass.h" +#include "unicharset.h" +#include "unicodes.h" + +namespace tesseract { + + ResultIterator::ResultIterator(const LTRResultIterator &resit) + : LTRResultIterator(resit) { + in_minor_direction_ = false; + at_beginning_of_minor_run_ = false; + preserve_interword_spaces_ = false; + + BoolParam *p = ParamUtils::FindParam( + "preserve_interword_spaces", GlobalParams()->bool_params, + tesseract_->params()->bool_params); + if (p != NULL) preserve_interword_spaces_ = (bool)(*p); + + current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); + MoveToLogicalStartOfTextline(); + } + + ResultIterator *ResultIterator::StartOfParagraph( + const LTRResultIterator &resit) { + return new ResultIterator(resit); + } + + bool ResultIterator::ParagraphIsLtr() const { + return current_paragraph_is_ltr_; + } + + bool ResultIterator::CurrentParagraphIsLtr() const { + if (!it_->word()) + return true; // doesn't matter. + LTRResultIterator it(*this); + it.RestartParagraph(); + // Try to figure out the ltr-ness of the paragraph. The rules below + // make more sense in the context of a difficult paragraph example. + // Here we denote {ltr characters, RTL CHARACTERS}: + // + // "don't go in there!" DAIS EH + // EHT OTNI DEPMUJ FELSMIH NEHT DNA + // .GNIDLIUB GNINRUB + // + // On the first line, the left-most word is LTR and the rightmost word + // is RTL. Thus, we are better off taking the majority direction for + // the whole paragraph contents. So instead of "the leftmost word is LTR" + // indicating an LTR paragraph, we use a heuristic about what RTL paragraphs + // would not do: Typically an RTL paragraph would *not* start with an LTR + // word. So our heuristics are as follows: + // + // (1) If the first text line has an RTL word in the left-most position + // it is RTL. + // (2) If the first text line has an LTR word in the right-most position + // it is LTR. + // (3) If neither of the above is true, take the majority count for the + // paragraph -- if there are more rtl words, it is RTL. If there + // are more LTR words, it's LTR. + bool leftmost_rtl = it.WordDirection() == DIR_RIGHT_TO_LEFT; + bool rightmost_ltr = it.WordDirection() == DIR_LEFT_TO_RIGHT; + int num_ltr, num_rtl; + num_rtl = leftmost_rtl ? 1 : 0; + num_ltr = (it.WordDirection() == DIR_LEFT_TO_RIGHT) ? 1 : 0; + for (it.Next(RIL_WORD); + !it.Empty(RIL_WORD) && !it.IsAtBeginningOf(RIL_TEXTLINE); + it.Next(RIL_WORD)) { + StrongScriptDirection dir = it.WordDirection(); + rightmost_ltr = (dir == DIR_LEFT_TO_RIGHT); + num_rtl += (dir == DIR_RIGHT_TO_LEFT) ? 1 : 0; + num_ltr += rightmost_ltr ? 1 : 0; + } + if (leftmost_rtl) + return false; + if (rightmost_ltr) + return true; + // First line is ambiguous. Take statistics on the whole paragraph. + if (!it.Empty(RIL_WORD) && !it.IsAtBeginningOf(RIL_PARA)) do { + StrongScriptDirection dir = it.WordDirection(); + num_rtl += (dir == DIR_RIGHT_TO_LEFT) ? 1 : 0; + num_ltr += (dir == DIR_LEFT_TO_RIGHT) ? 1 : 0; + } while (it.Next(RIL_WORD) && !it.IsAtBeginningOf(RIL_PARA)); + return num_ltr >= num_rtl; + } + + const int ResultIterator::kMinorRunStart = -1; + const int ResultIterator::kMinorRunEnd = -2; + const int ResultIterator::kComplexWord = -3; + + void ResultIterator::CalculateBlobOrder( + GenericVector *blob_indices) const { + bool context_is_ltr = current_paragraph_is_ltr_ ^ in_minor_direction_; + blob_indices->clear(); + if (Empty(RIL_WORD)) return; + if (context_is_ltr || it_->word()->UnicharsInReadingOrder()) { + // Easy! just return the blobs in order; + for (int i = 0; i < word_length_; i++) + blob_indices->push_back(i); + return; + } + + // The blobs are in left-to-right order, but the current reading context + // is right-to-left. + const int U_LTR = UNICHARSET::U_LEFT_TO_RIGHT; + const int U_RTL = UNICHARSET::U_RIGHT_TO_LEFT; + const int U_EURO_NUM = UNICHARSET::U_EUROPEAN_NUMBER; + const int U_EURO_NUM_SEP = UNICHARSET::U_EUROPEAN_NUMBER_SEPARATOR; + const int U_EURO_NUM_TERM = UNICHARSET::U_EUROPEAN_NUMBER_TERMINATOR; + const int U_COMMON_NUM_SEP = UNICHARSET::U_COMMON_NUMBER_SEPARATOR; + const int U_OTHER_NEUTRAL = UNICHARSET::U_OTHER_NEUTRAL; + + // Step 1: Scan for and mark European Number sequences + // [:ET:]*[:EN:]+(([:ES:]|[:CS:])?[:EN:]+)*[:ET:]* + GenericVector letter_types; + for (int i = 0; i < word_length_; i++) { + letter_types.push_back(it_->word()->SymbolDirection(i)); + } + // Convert a single separtor sandwiched between two EN's into an EN. + for (int i = 0; i + 2 < word_length_; i++) { + if (letter_types[i] == U_EURO_NUM && letter_types[i + 2] == U_EURO_NUM && + (letter_types[i + 1] == U_EURO_NUM_SEP || + letter_types[i + 1] == U_COMMON_NUM_SEP)) { + letter_types[i + 1] = U_EURO_NUM; + } + } + // Scan for sequences of European Number Terminators around ENs and convert + // them to ENs. + for (int i = 0; i < word_length_; i++) { + if (letter_types[i] == U_EURO_NUM_TERM) { + int j = i + 1; + while (j < word_length_ && letter_types[j] == U_EURO_NUM_TERM) { j++; } + if (j < word_length_ && letter_types[j] == U_EURO_NUM) { + // The sequence [i..j] should be converted to all European Numbers. + for (int k = i; k < j; k++) letter_types[k] = U_EURO_NUM; + } + j = i - 1; + while (j > -1 && letter_types[j] == U_EURO_NUM_TERM) { j--; } + if (j > -1 && letter_types[j] == U_EURO_NUM) { + // The sequence [j..i] should be converted to all European Numbers. + for (int k = j; k <= i; k++) letter_types[k] = U_EURO_NUM; + } + } + } + // Step 2: Convert all remaining types to either L or R. + // Sequences ([:L:]|[:EN:])+ (([:CS:]|[:ON:])+ ([:L:]|[:EN:])+)* -> L. + // All other are R. + for (int i = 0; i < word_length_;) { + int ti = letter_types[i]; + if (ti == U_LTR || ti == U_EURO_NUM) { + // Left to right sequence; scan to the end of it. + int last_good = i; + for (int j = i + 1; j < word_length_; j++) { + int tj = letter_types[j]; + if (tj == U_LTR || tj == U_EURO_NUM) { + last_good = j; + } + else if (tj == U_COMMON_NUM_SEP || tj == U_OTHER_NEUTRAL) { + // do nothing. + } + else { + break; + } + } + // [i..last_good] is the L sequence + for (int k = i; k <= last_good; k++) letter_types[k] = U_LTR; + i = last_good + 1; + } + else { + letter_types[i] = U_RTL; + i++; + } + } + + // At this point, letter_types is entirely U_LTR or U_RTL. + for (int i = word_length_ - 1; i >= 0;) { + if (letter_types[i] == U_RTL) { + blob_indices->push_back(i); + i--; + } + else { + // left to right sequence. scan to the beginning. + int j = i - 1; + for (; j >= 0 && letter_types[j] != U_RTL; j--) {} // pass + // Now (j, i] is LTR + for (int k = j + 1; k <= i; k++) blob_indices->push_back(k); + i = j; + } + } + ASSERT_HOST(blob_indices->size() == word_length_); + } + + static void PrintScriptDirs(const GenericVector &dirs) { + for (int i = 0; i < dirs.size(); i++) { + switch (dirs[i]) { + case DIR_NEUTRAL: tprintf("N "); break; + case DIR_LEFT_TO_RIGHT: tprintf("L "); break; + case DIR_RIGHT_TO_LEFT: tprintf("R "); break; + case DIR_MIX: tprintf("Z "); break; + default: tprintf("? "); break; + } + } + tprintf("\n"); + } + + void ResultIterator::CalculateTextlineOrder( + bool paragraph_is_ltr, + const LTRResultIterator &resit, + GenericVectorEqEq *word_indices) const { + GenericVector directions; + CalculateTextlineOrder(paragraph_is_ltr, resit, &directions, word_indices); + } + + void ResultIterator::CalculateTextlineOrder( + bool paragraph_is_ltr, + const LTRResultIterator &resit, + GenericVector *dirs_arg, + GenericVectorEqEq *word_indices) const { + GenericVector dirs; + GenericVector *directions; + directions = (dirs_arg != NULL) ? dirs_arg : &dirs; + directions->truncate(0); + + // A LTRResultIterator goes strictly left-to-right word order. + LTRResultIterator ltr_it(resit); + ltr_it.RestartRow(); + if (ltr_it.Empty(RIL_WORD)) return; + do { + directions->push_back(ltr_it.WordDirection()); + } while (ltr_it.Next(RIL_WORD) && !ltr_it.IsAtBeginningOf(RIL_TEXTLINE)); + + word_indices->truncate(0); + CalculateTextlineOrder(paragraph_is_ltr, *directions, word_indices); + } + + void ResultIterator::CalculateTextlineOrder( + bool paragraph_is_ltr, + const GenericVector &word_dirs, + GenericVectorEqEq *reading_order) { + reading_order->truncate(0); + if (word_dirs.size() == 0) return; + + // Take all of the runs of minor direction words and insert them + // in reverse order. + int minor_direction, major_direction, major_step, start, end; + if (paragraph_is_ltr) { + start = 0; + end = word_dirs.size(); + major_step = 1; + major_direction = DIR_LEFT_TO_RIGHT; + minor_direction = DIR_RIGHT_TO_LEFT; + } + else { + start = word_dirs.size() - 1; + end = -1; + major_step = -1; + major_direction = DIR_RIGHT_TO_LEFT; + minor_direction = DIR_LEFT_TO_RIGHT; + // Special rule: if there are neutral words at the right most side + // of a line adjacent to a left-to-right word in the middle of the + // line, we interpret the end of the line as a single LTR sequence. + if (word_dirs[start] == DIR_NEUTRAL) { + int neutral_end = start; + while (neutral_end > 0 && word_dirs[neutral_end] == DIR_NEUTRAL) { + neutral_end--; + } + if (neutral_end >= 0 && word_dirs[neutral_end] == DIR_LEFT_TO_RIGHT) { + // LTR followed by neutrals. + // Scan for the beginning of the minor left-to-right run. + int left = neutral_end; + for (int i = left; i >= 0 && word_dirs[i] != DIR_RIGHT_TO_LEFT; i--) { + if (word_dirs[i] == DIR_LEFT_TO_RIGHT) left = i; + } + reading_order->push_back(kMinorRunStart); + for (int i = left; i < word_dirs.size(); i++) { + reading_order->push_back(i); + if (word_dirs[i] == DIR_MIX) reading_order->push_back(kComplexWord); + } + reading_order->push_back(kMinorRunEnd); + start = left - 1; + } + } + } + for (int i = start; i != end;) { + if (word_dirs[i] == minor_direction) { + int j = i; + while (j != end && word_dirs[j] != major_direction) + j += major_step; + if (j == end) j -= major_step; + while (j != i && word_dirs[j] != minor_direction) + j -= major_step; + // [j..i] is a minor direction run. + reading_order->push_back(kMinorRunStart); + for (int k = j; k != i; k -= major_step) { + reading_order->push_back(k); + } + reading_order->push_back(i); + reading_order->push_back(kMinorRunEnd); + i = j + major_step; + } + else { + reading_order->push_back(i); + if (word_dirs[i] == DIR_MIX) reading_order->push_back(kComplexWord); + i += major_step; + } + } + } + + int ResultIterator::LTRWordIndex() const { + int this_word_index = 0; + LTRResultIterator textline(*this); + textline.RestartRow(); + while (!textline.PositionedAtSameWord(it_)) { + this_word_index++; + textline.Next(RIL_WORD); + } + return this_word_index; + } + + void ResultIterator::MoveToLogicalStartOfWord() { + if (word_length_ == 0) { + BeginWord(0); + return; + } + GenericVector blob_order; + CalculateBlobOrder(&blob_order); + if (blob_order.size() == 0 || blob_order[0] == 0) return; + BeginWord(blob_order[0]); + } + + bool ResultIterator::IsAtFinalSymbolOfWord() const { + if (!it_->word()) return true; + GenericVector blob_order; + CalculateBlobOrder(&blob_order); + return blob_order.size() == 0 || blob_order.back() == blob_index_; + } + + bool ResultIterator::IsAtFirstSymbolOfWord() const { + if (!it_->word()) return true; + GenericVector blob_order; + CalculateBlobOrder(&blob_order); + return blob_order.size() == 0 || blob_order[0] == blob_index_; + } + + void ResultIterator::AppendSuffixMarks(STRING *text) const { + if (!it_->word()) return; + bool reading_direction_is_ltr = + current_paragraph_is_ltr_ ^ in_minor_direction_; + // scan forward to see what meta-information the word ordering algorithm + // left us. + // If this word is at the *end* of a minor run, insert the other + // direction's mark; else if this was a complex word, insert the + // current reading order's mark. + GenericVectorEqEq textline_order; + CalculateTextlineOrder(current_paragraph_is_ltr_, + *this, &textline_order); + int this_word_index = LTRWordIndex(); + int i = textline_order.get_index(this_word_index); + if (i < 0) return; + + int last_non_word_mark = 0; + for (i++; i < textline_order.size() && textline_order[i] < 0; i++) { + last_non_word_mark = textline_order[i]; + } + if (last_non_word_mark == kComplexWord) { + *text += reading_direction_is_ltr ? kLRM : kRLM; + } + else if (last_non_word_mark == kMinorRunEnd) { + if (current_paragraph_is_ltr_) { + *text += kLRM; + } + else { + *text += kRLM; + } + } + } + + void ResultIterator::MoveToLogicalStartOfTextline() { + GenericVectorEqEq word_indices; + RestartRow(); + CalculateTextlineOrder(current_paragraph_is_ltr_, + dynamic_cast(*this), + &word_indices); + int i = 0; + for (; i < word_indices.size() && word_indices[i] < 0; i++) { + if (word_indices[i] == kMinorRunStart) in_minor_direction_ = true; + else if (word_indices[i] == kMinorRunEnd) in_minor_direction_ = false; + } + if (in_minor_direction_) at_beginning_of_minor_run_ = true; + if (i >= word_indices.size()) return; + int first_word_index = word_indices[i]; + for (int j = 0; j < first_word_index; j++) { + PageIterator::Next(RIL_WORD); + } + MoveToLogicalStartOfWord(); + } + + void ResultIterator::Begin() { + LTRResultIterator::Begin(); + current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); + in_minor_direction_ = false; + at_beginning_of_minor_run_ = false; + MoveToLogicalStartOfTextline(); + } + + bool ResultIterator::Next(PageIteratorLevel level) { + if (it_->block() == NULL) return false; // already at end! + switch (level) { + case RIL_BLOCK: // explicit fall-through + case RIL_PARA: // explicit fall-through + case RIL_TEXTLINE: + if (!PageIterator::Next(level)) return false; + if (IsWithinFirstTextlineOfParagraph()) { + // if we've advanced to a new paragraph, + // recalculate current_paragraph_is_ltr_ + current_paragraph_is_ltr_ = CurrentParagraphIsLtr(); + } + in_minor_direction_ = false; + MoveToLogicalStartOfTextline(); + return it_->block() != NULL; + case RIL_SYMBOL: + { + GenericVector blob_order; + CalculateBlobOrder(&blob_order); + int next_blob = 0; + while (next_blob < blob_order.size() && + blob_index_ != blob_order[next_blob]) + next_blob++; + next_blob++; + if (next_blob < blob_order.size()) { + // we're in the same word; simply advance one blob. + BeginWord(blob_order[next_blob]); + at_beginning_of_minor_run_ = false; + return true; + } + level = RIL_WORD; // we've fallen through to the next word. + } + case RIL_WORD: // explicit fall-through. + { + if (it_->word() == NULL) return Next(RIL_BLOCK); + GenericVectorEqEq word_indices; + int this_word_index = LTRWordIndex(); + CalculateTextlineOrder(current_paragraph_is_ltr_, + *this, + &word_indices); + int final_real_index = word_indices.size() - 1; + while (final_real_index > 0 && word_indices[final_real_index] < 0) + final_real_index--; + for (int i = 0; i < final_real_index; i++) { + if (word_indices[i] == this_word_index) { + int j = i + 1; + for (; j < final_real_index && word_indices[j] < 0; j++) { + if (word_indices[j] == kMinorRunStart) in_minor_direction_ = true; + if (word_indices[j] == kMinorRunEnd) in_minor_direction_ = false; + } + at_beginning_of_minor_run_ = (word_indices[j - 1] == kMinorRunStart); + // awesome, we move to word_indices[j] + if (BidiDebug(3)) { + tprintf("Next(RIL_WORD): %d -> %d\n", + this_word_index, word_indices[j]); + } + PageIterator::RestartRow(); + for (int k = 0; k < word_indices[j]; k++) { + PageIterator::Next(RIL_WORD); + } + MoveToLogicalStartOfWord(); + return true; + } + } + if (BidiDebug(3)) { + tprintf("Next(RIL_WORD): %d -> EOL\n", this_word_index); + } + // we're going off the end of the text line. + return Next(RIL_TEXTLINE); + } + } + ASSERT_HOST(false); // shouldn't happen. + return false; + } + + bool ResultIterator::IsAtBeginningOf(PageIteratorLevel level) const { + if (it_->block() == NULL) return false; // Already at the end! + if (it_->word() == NULL) return true; // In an image block. + if (level == RIL_SYMBOL) return true; // Always at beginning of a symbol. + + bool at_word_start = IsAtFirstSymbolOfWord(); + if (level == RIL_WORD) return at_word_start; + + ResultIterator line_start(*this); + // move to the first word in the line... + line_start.MoveToLogicalStartOfTextline(); + + bool at_textline_start = at_word_start && *line_start.it_ == *it_; + if (level == RIL_TEXTLINE) return at_textline_start; + + // now we move to the left-most word... + line_start.RestartRow(); + bool at_block_start = at_textline_start && + line_start.it_->block() != line_start.it_->prev_block(); + if (level == RIL_BLOCK) return at_block_start; + + bool at_para_start = at_block_start || + (at_textline_start && + line_start.it_->row()->row->para() != + line_start.it_->prev_row()->row->para()); + if (level == RIL_PARA) return at_para_start; + + ASSERT_HOST(false); // shouldn't happen. + return false; + } + + /** + * NOTE! This is an exact copy of PageIterator::IsAtFinalElement with the + * change that the variable next is now a ResultIterator instead of a + * PageIterator. + */ + bool ResultIterator::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. + ResultIterator next(*this); + next.Next(element); + if (next.Empty(element)) return true; // Reached the end of the page. + while (element > level) { + element = static_cast(element - 1); + if (!next.IsAtBeginningOf(element)) + return false; + } + return true; + } + + /** + * Returns the null terminated UTF-8 encoded text string for the current + * object at the given level. Use delete [] to free after use. + */ + char* ResultIterator::GetUTF8Text(PageIteratorLevel level) const { + if (it_->word() == NULL) return NULL; // Already at the end! + STRING text; + switch (level) { + case RIL_BLOCK: + { + ResultIterator pp(*this); + do { + pp.AppendUTF8ParagraphText(&text); + } while (pp.Next(RIL_PARA) && pp.it_->block() == it_->block()); + } + break; + case RIL_PARA: + AppendUTF8ParagraphText(&text); + break; + case RIL_TEXTLINE: + { + ResultIterator it(*this); + it.MoveToLogicalStartOfTextline(); + it.IterateAndAppendUTF8TextlineText(&text); + } + break; + case RIL_WORD: + AppendUTF8WordText(&text); + break; + case RIL_SYMBOL: + { + bool reading_direction_is_ltr = + current_paragraph_is_ltr_ ^ in_minor_direction_; + if (at_beginning_of_minor_run_) { + text += reading_direction_is_ltr ? kLRM : kRLM; + } + text = it_->word()->BestUTF8(blob_index_, !reading_direction_is_ltr); + if (IsAtFinalSymbolOfWord()) AppendSuffixMarks(&text); + } + break; + } + int length = text.length() + 1; + char* result = new char[length]; + strncpy(result, text.string(), length); + return result; + } + + void ResultIterator::AppendUTF8WordText(STRING *text) const { + if (!it_->word()) return; + ASSERT_HOST(it_->word()->best_choice != NULL); + bool reading_direction_is_ltr = + current_paragraph_is_ltr_ ^ in_minor_direction_; + if (at_beginning_of_minor_run_) { + *text += reading_direction_is_ltr ? kLRM : kRLM; + } + + GenericVector blob_order; + CalculateBlobOrder(&blob_order); + for (int i = 0; i < blob_order.size(); i++) { + *text += it_->word()->BestUTF8(blob_order[i], !reading_direction_is_ltr); + } + AppendSuffixMarks(text); + } + + void ResultIterator::IterateAndAppendUTF8TextlineText(STRING *text) { + if (Empty(RIL_WORD)) { + Next(RIL_WORD); + return; + } + if (BidiDebug(1)) { + GenericVectorEqEq textline_order; + GenericVector dirs; + CalculateTextlineOrder(current_paragraph_is_ltr_, + *this, &dirs, &textline_order); + tprintf("Strong Script dirs [%p/P=%s]: ", it_->row(), + current_paragraph_is_ltr_ ? "ltr" : "rtl"); + PrintScriptDirs(dirs); + tprintf("Logical textline order [%p/P=%s]: ", it_->row(), + current_paragraph_is_ltr_ ? "ltr" : "rtl"); + for (int i = 0; i < textline_order.size(); i++) { + tprintf("%d ", textline_order[i]); + } + tprintf("\n"); + } + + int words_appended = 0; + do { + int numSpaces = preserve_interword_spaces_ ? it_->word()->word->space() + : (words_appended > 0); + for (int i = 0; i < numSpaces; ++i) { + *text += " "; + } + AppendUTF8WordText(text); + words_appended++; + } while (Next(RIL_WORD) && !IsAtBeginningOf(RIL_TEXTLINE)); + if (BidiDebug(1)) { + tprintf("%d words printed\n", words_appended); + } + *text += line_separator_; + // If we just finished a paragraph, add an extra newline. + if (it_->block() == NULL || IsAtBeginningOf(RIL_PARA)) + *text += paragraph_separator_; + } + + void ResultIterator::AppendUTF8ParagraphText(STRING *text) const { + ResultIterator it(*this); + it.RestartParagraph(); + it.MoveToLogicalStartOfTextline(); + if (it.Empty(RIL_WORD)) return; + do { + it.IterateAndAppendUTF8TextlineText(text); + } while (it.it_->block() != NULL && !it.IsAtBeginningOf(RIL_PARA)); + } + + bool ResultIterator::BidiDebug(int min_level) const { + int debug_level = 1; + IntParam *p = ParamUtils::FindParam( + "bidi_debug", GlobalParams()->int_params, + tesseract_->params()->int_params); + if (p != NULL) debug_level = (inT32)(*p); + return debug_level >= min_level; + } + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.h b/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.h new file mode 100644 index 0000000..296d4e4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/resultiterator.h @@ -0,0 +1,244 @@ +/////////////////////////////////////////////////////////////////////// +// File: resultiterator.h +// Description: Iterator for tesseract results that is capable of +// iterating in proper reading order over Bi Directional +// (e.g. mixed Hebrew and English) text. +// Author: David Eger +// Created: Fri May 27 13:58:06 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_CCMAIN_RESULT_ITERATOR_H__ +#define TESSERACT_CCMAIN_RESULT_ITERATOR_H__ + +#include "platform.h" +#include "ltrresultiterator.h" + +template class GenericVector; +template class GenericVectorEqEq; +class BLOB_CHOICE_IT; +class WERD_RES; +class STRING; + +namespace tesseract { + + class Tesseract; + + class TESS_API ResultIterator : public LTRResultIterator { + public: + static ResultIterator *StartOfParagraph(const LTRResultIterator &resit); + + /** + * ResultIterator is copy constructible! + * The default copy constructor works just fine for us. + */ + virtual ~ResultIterator() {} + + // ============= Moving around within the page ============. + /** + * Moves the iterator to point to the start of the page to begin + * an iteration. + */ + virtual void Begin(); + + /** + * Moves to the start of the next object at the given level in the + * page hierarchy in the appropriate reading order and returns false if + * the end of the page was reached. + * NOTE that RIL_SYMBOL will skip non-text blocks, but all other + * PageIteratorLevel level values will visit each non-text block once. + * Think of non text blocks as containing a single para, with a single line, + * with a single imaginary word. + * 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. + */ + virtual bool Next(PageIteratorLevel level); + + /** + * IsAtBeginningOf() returns whether we're at the logical beginning of the + * given level. (as opposed to ResultIterator's left-to-right top-to-bottom + * order). Otherwise, this acts the same as PageIterator::IsAtBeginningOf(). + * For a full description, see pageiterator.h + */ + virtual bool IsAtBeginningOf(PageIteratorLevel level) const; + + /** + * Implement PageIterator's IsAtFinalElement correctly in a BiDi context. + * For instance, IsAtFinalElement(RIL_PARA, RIL_WORD) returns whether we + * point at the last word in a paragraph. See PageIterator for full comment. + */ + virtual bool IsAtFinalElement(PageIteratorLevel level, + PageIteratorLevel element) const; + + // ============= Accessing data ==============. + + /** + * Returns the null terminated UTF-8 encoded text string for the current + * object at the given level. Use delete [] to free after use. + */ + virtual char* GetUTF8Text(PageIteratorLevel level) const; + + /** + * Return whether the current paragraph's dominant reading direction + * is left-to-right (as opposed to right-to-left). + */ + bool ParagraphIsLtr() const; + + // ============= Exposed only for testing =============. + + /** + * Yields the reading order as a sequence of indices and (optional) + * meta-marks for a set of words (given left-to-right). + * The meta marks are passed as negative values: + * kMinorRunStart Start of minor direction text. + * kMinorRunEnd End of minor direction text. + * kComplexWord The next indexed word contains both left-to-right and + * right-to-left characters and was treated as neutral. + * + * For example, suppose we have five words in a text line, + * indexed [0,1,2,3,4] from the leftmost side of the text line. + * The following are all believable reading_orders: + * + * Left-to-Right (in ltr paragraph): + * { 0, 1, 2, 3, 4 } + * Left-to-Right (in rtl paragraph): + * { kMinorRunStart, 0, 1, 2, 3, 4, kMinorRunEnd } + * Right-to-Left (in rtl paragraph): + * { 4, 3, 2, 1, 0 } + * Left-to-Right except for an RTL phrase in words 2, 3 in an ltr paragraph: + * { 0, 1, kMinorRunStart, 3, 2, kMinorRunEnd, 4 } + */ + static void CalculateTextlineOrder( + bool paragraph_is_ltr, + const GenericVector &word_dirs, + GenericVectorEqEq *reading_order); + + static const int kMinorRunStart; + static const int kMinorRunEnd; + static const int kComplexWord; + + protected: + /** + * We presume the data associated with the given iterator will outlive us. + * NB: This is private because it does something that is non-obvious: + * it resets to the beginning of the paragraph instead of staying wherever + * resit might have pointed. + */ + TESS_LOCAL explicit ResultIterator(const LTRResultIterator &resit); + + private: + /** + * Calculates the current paragraph's dominant writing direction. + * Typically, members should use current_paragraph_ltr_ instead. + */ + bool CurrentParagraphIsLtr() const; + + /** + * Returns word indices as measured from resit->RestartRow() = index 0 + * for the reading order of words within a textline given an iterator + * into the middle of the text line. + * In addition to non-negative word indices, the following negative values + * may be inserted: + * kMinorRunStart Start of minor direction text. + * kMinorRunEnd End of minor direction text. + * kComplexWord The previous word contains both left-to-right and + * right-to-left characters and was treated as neutral. + */ + void CalculateTextlineOrder(bool paragraph_is_ltr, + const LTRResultIterator &resit, + GenericVectorEqEq *indices) const; + /** Same as above, but the caller's ssd gets filled in if ssd != NULL. */ + void CalculateTextlineOrder(bool paragraph_is_ltr, + const LTRResultIterator &resit, + GenericVector *ssd, + GenericVectorEqEq *indices) const; + + /** + * What is the index of the current word in a strict left-to-right reading + * of the row? + */ + int LTRWordIndex() const; + + /** + * Given an iterator pointing at a word, returns the logical reading order + * of blob indices for the word. + */ + void CalculateBlobOrder(GenericVector *blob_indices) const; + + /** Precondition: current_paragraph_is_ltr_ is set. */ + void MoveToLogicalStartOfTextline(); + + /** + * Precondition: current_paragraph_is_ltr_ and in_minor_direction_ + * are set. + */ + void MoveToLogicalStartOfWord(); + + /** Are we pointing at the final (reading order) symbol of the word? */ + bool IsAtFinalSymbolOfWord() const; + + /** Are we pointing at the first (reading order) symbol of the word? */ + bool IsAtFirstSymbolOfWord() const; + + /** + * Append any extra marks that should be appended to this word when printed. + * Mostly, these are Unicode BiDi control characters. + */ + void AppendSuffixMarks(STRING *text) const; + + /** Appends the current word in reading order to the given buffer.*/ + void AppendUTF8WordText(STRING *text) const; + + /** + * Appends the text of the current text line, *assuming this iterator is + * positioned at the beginning of the text line* This function + * updates the iterator to point to the first position past the text line. + * Each textline is terminated in a single newline character. + * If the textline ends a paragraph, it gets a second terminal newline. + */ + void IterateAndAppendUTF8TextlineText(STRING *text); + + /** + * Appends the text of the current paragraph in reading order + * to the given buffer. + * Each textline is terminated in a single newline character, and the + * paragraph gets an extra newline at the end. + */ + void AppendUTF8ParagraphText(STRING *text) const; + + /** Returns whether the bidi_debug flag is set to at least min_level. */ + bool BidiDebug(int min_level) const; + + bool current_paragraph_is_ltr_; + + /** + * Is the currently pointed-at character at the beginning of + * a minor-direction run? + */ + bool at_beginning_of_minor_run_; + + /** Is the currently pointed-at character in a minor-direction sequence? */ + bool in_minor_direction_; + + /** + * Should detected inter-word spaces be preserved, or "compressed" to a single + * space character (default behavior). + */ + bool preserve_interword_spaces_; + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_RESULT_ITERATOR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/superscript.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/superscript.cpp new file mode 100644 index 0000000..8544274 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/superscript.cpp @@ -0,0 +1,619 @@ +/****************************************************************** + * File: superscript.cpp + * Description: Correction pass to fix superscripts and subscripts. + * Author: David Eger + * Created: Mon Mar 12 14:05:00 PDT 2012 + * + * (C) Copyright 2012, 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 "normalis.h" +#include "tesseractclass.h" + +static int LeadingUnicharsToChopped(WERD_RES *word, int num_unichars) { + int num_chopped = 0; + for (int i = 0; i < num_unichars; i++) + num_chopped += word->best_state[i]; + return num_chopped; +} + +static int TrailingUnicharsToChopped(WERD_RES *word, int num_unichars) { + int num_chopped = 0; + for (int i = 0; i < num_unichars; i++) + num_chopped += word->best_state[word->best_state.size() - 1 - i]; + return num_chopped; +} + + +namespace tesseract { + + /** + * Given a recognized blob, see if a contiguous collection of sub-pieces + * (chopped blobs) starting at its left might qualify as being a subscript + * or superscript letter based only on y position. Also do this for the + * right side. + */ + void YOutlierPieces(WERD_RES *word, int rebuilt_blob_index, + int super_y_bottom, int sub_y_top, + ScriptPos *leading_pos, int *num_leading_outliers, + ScriptPos *trailing_pos, int *num_trailing_outliers) { + ScriptPos sp_unused1, sp_unused2; + int unused1, unused2; + if (!leading_pos) leading_pos = &sp_unused1; + if (!num_leading_outliers) num_leading_outliers = &unused1; + if (!trailing_pos) trailing_pos = &sp_unused2; + if (!num_trailing_outliers) num_trailing_outliers = &unused2; + + *num_leading_outliers = *num_trailing_outliers = 0; + *leading_pos = *trailing_pos = SP_NORMAL; + + int chopped_start = LeadingUnicharsToChopped(word, rebuilt_blob_index); + int num_chopped_pieces = word->best_state[rebuilt_blob_index]; + ScriptPos last_pos = SP_NORMAL; + int trailing_outliers = 0; + for (int i = 0; i < num_chopped_pieces; i++) { + TBOX box = word->chopped_word->blobs[chopped_start + i]->bounding_box(); + ScriptPos pos = SP_NORMAL; + if (box.bottom() >= super_y_bottom) { + pos = SP_SUPERSCRIPT; + } + else if (box.top() <= sub_y_top) { + pos = SP_SUBSCRIPT; + } + if (pos == SP_NORMAL) { + if (trailing_outliers == i) { + *num_leading_outliers = trailing_outliers; + *leading_pos = last_pos; + } + trailing_outliers = 0; + } + else { + if (pos == last_pos) { + trailing_outliers++; + } + else { + trailing_outliers = 1; + } + } + last_pos = pos; + } + *num_trailing_outliers = trailing_outliers; + *trailing_pos = last_pos; + } + + /** + * Attempt to split off any high (or low) bits at the ends of the word with poor + * certainty and recognize them separately. If the certainty gets much better + * and other sanity checks pass, acccept. + * + * This superscript fix is meant to be called in the second pass of recognition + * when we have tried once and already have a preliminary answer for word. + * + * @return Whether we modified the given word. + */ + bool Tesseract::SubAndSuperscriptFix(WERD_RES *word) { + if (word->tess_failed || word->word->flag(W_REP_CHAR) || + !word->best_choice) { + return false; + } + int num_leading, num_trailing; + ScriptPos sp_leading, sp_trailing; + float leading_certainty, trailing_certainty; + float avg_certainty, unlikely_threshold; + + // Calculate the number of whole suspicious characters at the edges. + GetSubAndSuperscriptCandidates( + word, &num_leading, &sp_leading, &leading_certainty, + &num_trailing, &sp_trailing, &trailing_certainty, + &avg_certainty, &unlikely_threshold); + + const char *leading_pos = sp_leading == SP_SUBSCRIPT ? "sub" : "super"; + const char *trailing_pos = sp_trailing == SP_SUBSCRIPT ? "sub" : "super"; + + int num_blobs = word->best_choice->length(); + + // Calculate the remainder (partial characters) at the edges. + // This accounts for us having classified the best version of + // a word as [speaker?'] when it was instead [speaker.^{21}] + // (that is we accidentally thought the 2 was attached to the period). + int num_remainder_leading = 0, num_remainder_trailing = 0; + if (num_leading + num_trailing < num_blobs && unlikely_threshold < 0.0) { + int super_y_bottom = + kBlnBaselineOffset + kBlnXHeight * superscript_min_y_bottom; + int sub_y_top = + kBlnBaselineOffset + kBlnXHeight * subscript_max_y_top; + int last_word_char = num_blobs - 1 - num_trailing; + float last_char_certainty = word->best_choice->certainty(last_word_char); + if (word->best_choice->unichar_id(last_word_char) != 0 && + last_char_certainty <= unlikely_threshold) { + ScriptPos rpos; + YOutlierPieces(word, last_word_char, super_y_bottom, sub_y_top, + NULL, NULL, &rpos, &num_remainder_trailing); + if (num_trailing > 0 && rpos != sp_trailing) num_remainder_trailing = 0; + if (num_remainder_trailing > 0 && + last_char_certainty < trailing_certainty) { + trailing_certainty = last_char_certainty; + } + } + bool another_blob_available = (num_remainder_trailing == 0) || + num_leading + num_trailing + 1 < num_blobs; + int first_char_certainty = word->best_choice->certainty(num_leading); + if (another_blob_available && + word->best_choice->unichar_id(num_leading) != 0 && + first_char_certainty <= unlikely_threshold) { + ScriptPos lpos; + YOutlierPieces(word, num_leading, super_y_bottom, sub_y_top, + &lpos, &num_remainder_leading, NULL, NULL); + if (num_leading > 0 && lpos != sp_leading) num_remainder_leading = 0; + if (num_remainder_leading > 0 && + first_char_certainty < leading_certainty) { + leading_certainty = first_char_certainty; + } + } + } + + // If nothing to do, bail now. + if (num_leading + num_trailing + + num_remainder_leading + num_remainder_trailing == 0) { + return false; + } + + if (superscript_debug >= 1) { + tprintf("Candidate for superscript detection: %s (", + word->best_choice->unichar_string().string()); + if (num_leading || num_remainder_leading) { + tprintf("%d.%d %s-leading ", num_leading, num_remainder_leading, + leading_pos); + } + if (num_trailing || num_remainder_trailing) { + tprintf("%d.%d %s-trailing ", num_trailing, num_remainder_trailing, + trailing_pos); + } + tprintf(")\n"); + } + if (superscript_debug >= 3) { + word->best_choice->print(); + } + if (superscript_debug >= 2) { + tprintf(" Certainties -- Average: %.2f Unlikely thresh: %.2f ", + avg_certainty, unlikely_threshold); + if (num_leading) + tprintf("Orig. leading (min): %.2f ", leading_certainty); + if (num_trailing) + tprintf("Orig. trailing (min): %.2f ", trailing_certainty); + tprintf("\n"); + } + + // We've now calculated the number of rebuilt blobs we want to carve off. + // However, split_word() works from TBLOBs in chopped_word, so we need to + // convert to those. + int num_chopped_leading = + LeadingUnicharsToChopped(word, num_leading) + num_remainder_leading; + int num_chopped_trailing = + TrailingUnicharsToChopped(word, num_trailing) + num_remainder_trailing; + + int retry_leading = 0; + int retry_trailing = 0; + bool is_good = false; + WERD_RES *revised = TrySuperscriptSplits( + num_chopped_leading, leading_certainty, sp_leading, + num_chopped_trailing, trailing_certainty, sp_trailing, + word, &is_good, &retry_leading, &retry_trailing); + if (is_good) { + word->ConsumeWordResults(revised); + } + else if (retry_leading || retry_trailing) { + int retry_chopped_leading = + LeadingUnicharsToChopped(revised, retry_leading); + int retry_chopped_trailing = + TrailingUnicharsToChopped(revised, retry_trailing); + WERD_RES *revised2 = TrySuperscriptSplits( + retry_chopped_leading, leading_certainty, sp_leading, + retry_chopped_trailing, trailing_certainty, sp_trailing, + revised, &is_good, &retry_leading, &retry_trailing); + if (is_good) { + word->ConsumeWordResults(revised2); + } + delete revised2; + } + delete revised; + return is_good; + } + + /** + * Determine how many characters (rebuilt blobs) on each end of a given word + * might plausibly be superscripts so SubAndSuperscriptFix can try to + * re-recognize them. Even if we find no whole blobs at either end, + * we will set *unlikely_threshold to a certainty that might be used to + * select "bad enough" outlier characters. If *unlikely_threshold is set to 0, + * though, there's really no hope. + * + * @param[in] word The word to examine. + * @param[out] num_rebuilt_leading the number of rebuilt blobs at the start + * of the word which are all up or down and + * seem badly classified. + * @param[out] leading_pos "super" or "sub" (for debugging) + * @param[out] leading_certainty the worst certainty in the leading blobs. + * @param[out] num_rebuilt_trailing the number of rebuilt blobs at the end + * of the word which are all up or down and + * seem badly classified. + * @param[out] trailing_pos "super" or "sub" (for debugging) + * @param[out] trailing_certainty the worst certainty in the trailing blobs. + * @param[out] avg_certainty the average certainty of "normal" blobs in + * the word. + * @param[out] unlikely_threshold the threshold (on certainty) we used to + * select "bad enough" outlier characters. + */ + void Tesseract::GetSubAndSuperscriptCandidates(const WERD_RES *word, + int *num_rebuilt_leading, + ScriptPos *leading_pos, + float *leading_certainty, + int *num_rebuilt_trailing, + ScriptPos *trailing_pos, + float *trailing_certainty, + float *avg_certainty, + float *unlikely_threshold) { + *avg_certainty = *unlikely_threshold = 0.0f; + *num_rebuilt_leading = *num_rebuilt_trailing = 0; + *leading_certainty = *trailing_certainty = 0.0f; + + int super_y_bottom = + kBlnBaselineOffset + kBlnXHeight * superscript_min_y_bottom; + int sub_y_top = + kBlnBaselineOffset + kBlnXHeight * subscript_max_y_top; + + // Step one: Get an average certainty for "normally placed" characters. + + // Counts here are of blobs in the rebuild_word / unichars in best_choice. + *leading_pos = *trailing_pos = SP_NORMAL; + int leading_outliers = 0; + int trailing_outliers = 0; + int num_normal = 0; + float normal_certainty_total = 0.0f; + float worst_normal_certainty = 0.0f; + ScriptPos last_pos = SP_NORMAL; + int num_blobs = word->rebuild_word->NumBlobs(); + for (int b = 0; b < num_blobs; ++b) { + TBOX box = word->rebuild_word->blobs[b]->bounding_box(); + ScriptPos pos = SP_NORMAL; + if (box.bottom() >= super_y_bottom) { + pos = SP_SUPERSCRIPT; + } + else if (box.top() <= sub_y_top) { + pos = SP_SUBSCRIPT; + } + if (pos == SP_NORMAL) { + if (word->best_choice->unichar_id(b) != 0) { + float char_certainty = word->best_choice->certainty(b); + if (char_certainty < worst_normal_certainty) { + worst_normal_certainty = char_certainty; + } + num_normal++; + normal_certainty_total += char_certainty; + } + if (trailing_outliers == b) { + leading_outliers = trailing_outliers; + *leading_pos = last_pos; + } + trailing_outliers = 0; + } + else { + if (last_pos == pos) { + trailing_outliers++; + } + else { + trailing_outliers = 1; + } + } + last_pos = pos; + } + *trailing_pos = last_pos; + if (num_normal >= 3) { // throw out the worst as an outlier. + num_normal--; + normal_certainty_total -= worst_normal_certainty; + } + if (num_normal > 0) { + *avg_certainty = normal_certainty_total / num_normal; + *unlikely_threshold = superscript_worse_certainty * (*avg_certainty); + } + if (num_normal == 0 || + (leading_outliers == 0 && trailing_outliers == 0)) { + return; + } + + // Step two: Try to split off bits of the word that are both outliers + // and have much lower certainty than average + // Calculate num_leading and leading_certainty. + for (*leading_certainty = 0.0f, *num_rebuilt_leading = 0; + *num_rebuilt_leading < leading_outliers; + (*num_rebuilt_leading)++) { + float char_certainty = word->best_choice->certainty(*num_rebuilt_leading); + if (char_certainty > *unlikely_threshold) { + break; + } + if (char_certainty < *leading_certainty) { + *leading_certainty = char_certainty; + } + } + + // Calculate num_trailing and trailing_certainty. + for (*trailing_certainty = 0.0f, *num_rebuilt_trailing = 0; + *num_rebuilt_trailing < trailing_outliers; + (*num_rebuilt_trailing)++) { + int blob_idx = num_blobs - 1 - *num_rebuilt_trailing; + float char_certainty = word->best_choice->certainty(blob_idx); + if (char_certainty > *unlikely_threshold) { + break; + } + if (char_certainty < *trailing_certainty) { + *trailing_certainty = char_certainty; + } + } + } + + + /** + * Try splitting off the given number of (chopped) blobs from the front and + * back of the given word and recognizing the pieces. + * + * @param[in] num_chopped_leading how many chopped blobs from the left + * end of the word to chop off and try recognizing as a + * superscript (or subscript) + * @param[in] leading_certainty the (minimum) certainty had by the + * characters in the original leading section. + * @param[in] leading_pos "super" or "sub" (for debugging) + * @param[in] num_chopped_trailing how many chopped blobs from the right + * end of the word to chop off and try recognizing as a + * superscript (or subscript) + * @param[in] trailing_certainty the (minimum) certainty had by the + * characters in the original trailing section. + * @param[in] trailing_pos "super" or "sub" (for debugging) + * @param[in] word the word to try to chop up. + * @param[out] is_good do we believe our result? + * @param[out] retry_rebuild_leading, retry_rebuild_trailing + * If non-zero, and !is_good, then the caller may have luck trying + * to split the returned word with this number of (rebuilt) leading + * and trailing blobs / unichars. + * @return A word which is the result of re-recognizing as asked. + */ + WERD_RES *Tesseract::TrySuperscriptSplits( + int num_chopped_leading, float leading_certainty, ScriptPos leading_pos, + int num_chopped_trailing, float trailing_certainty, + ScriptPos trailing_pos, + WERD_RES *word, + bool *is_good, + int *retry_rebuild_leading, int *retry_rebuild_trailing) { + int num_chopped = word->chopped_word->NumBlobs(); + + *retry_rebuild_leading = *retry_rebuild_trailing = 0; + + // Chop apart the word into up to three pieces. + + BlamerBundle *bb0 = NULL; + BlamerBundle *bb1 = NULL; + WERD_RES *prefix = NULL; + WERD_RES *core = NULL; + WERD_RES *suffix = NULL; + if (num_chopped_leading > 0) { + prefix = new WERD_RES(*word); + split_word(prefix, num_chopped_leading, &core, &bb0); + } + else { + core = new WERD_RES(*word); + } + + if (num_chopped_trailing > 0) { + int split_pt = num_chopped - num_chopped_trailing - num_chopped_leading; + split_word(core, split_pt, &suffix, &bb1); + } + + // Recognize the pieces in turn. + int saved_cp_multiplier = classify_class_pruner_multiplier; + int saved_im_multiplier = classify_integer_matcher_multiplier; + if (prefix) { + // Turn off Tesseract's y-position penalties for the leading superscript. + classify_class_pruner_multiplier.set_value(0); + classify_integer_matcher_multiplier.set_value(0); + + // Adjust our expectations about the baseline for this prefix. + if (superscript_debug >= 3) { + tprintf(" recognizing first %d chopped blobs\n", num_chopped_leading); + } + recog_word_recursive(prefix); + if (superscript_debug >= 2) { + tprintf(" The leading bits look like %s %s\n", + ScriptPosToString(leading_pos), + prefix->best_choice->unichar_string().string()); + } + + // Restore the normal y-position penalties. + classify_class_pruner_multiplier.set_value(saved_cp_multiplier); + classify_integer_matcher_multiplier.set_value(saved_im_multiplier); + } + + if (superscript_debug >= 3) { + tprintf(" recognizing middle %d chopped blobs\n", + num_chopped - num_chopped_leading - num_chopped_trailing); + } + + if (suffix) { + // Turn off Tesseract's y-position penalties for the trailing superscript. + classify_class_pruner_multiplier.set_value(0); + classify_integer_matcher_multiplier.set_value(0); + + if (superscript_debug >= 3) { + tprintf(" recognizing last %d chopped blobs\n", num_chopped_trailing); + } + recog_word_recursive(suffix); + if (superscript_debug >= 2) { + tprintf(" The trailing bits look like %s %s\n", + ScriptPosToString(trailing_pos), + suffix->best_choice->unichar_string().string()); + } + + // Restore the normal y-position penalties. + classify_class_pruner_multiplier.set_value(saved_cp_multiplier); + classify_integer_matcher_multiplier.set_value(saved_im_multiplier); + } + + // Evaluate whether we think the results are believably better + // than what we already had. + bool good_prefix = !prefix || BelievableSuperscript( + superscript_debug >= 1, *prefix, + superscript_bettered_certainty * leading_certainty, + retry_rebuild_leading, NULL); + bool good_suffix = !suffix || BelievableSuperscript( + superscript_debug >= 1, *suffix, + superscript_bettered_certainty * trailing_certainty, + NULL, retry_rebuild_trailing); + + *is_good = good_prefix && good_suffix; + if (!*is_good && !*retry_rebuild_leading && !*retry_rebuild_trailing) { + // None of it is any good. Quit now. + delete core; + delete prefix; + delete suffix; + return NULL; + } + recog_word_recursive(core); + + // Now paste the results together into core. + if (suffix) { + suffix->SetAllScriptPositions(trailing_pos); + join_words(core, suffix, bb1); + } + if (prefix) { + prefix->SetAllScriptPositions(leading_pos); + join_words(prefix, core, bb0); + core = prefix; + prefix = NULL; + } + + if (superscript_debug >= 1) { + tprintf("%s superscript fix: %s\n", *is_good ? "ACCEPT" : "REJECT", + core->best_choice->unichar_string().string()); + } + return core; + } + + + /** + * Return whether this is believable superscript or subscript text. + * + * We insist that: + * + there are no punctuation marks. + * + there are no italics. + * + no normal-sized character is smaller than superscript_scaledown_ratio + * of what it ought to be, and + * + each character is at least as certain as certainty_threshold. + * + * @param[in] debug If true, spew debug output + * @param[in] word The word whose best_choice we're evaluating + * @param[in] certainty_threshold If any of the characters have less + * certainty than this, reject. + * @param[out] left_ok How many left-side characters were ok? + * @param[out] right_ok How many right-side characters were ok? + * @return Whether the complete best choice is believable as a superscript. + */ + bool Tesseract::BelievableSuperscript(bool debug, + const WERD_RES &word, + float certainty_threshold, + int *left_ok, + int *right_ok) const { + int initial_ok_run_count = 0; + int ok_run_count = 0; + float worst_certainty = 0.0f; + const WERD_CHOICE &wc = *word.best_choice; + + const UnicityTable& fontinfo_table = get_fontinfo_table(); + for (int i = 0; i < wc.length(); i++) { + TBLOB *blob = word.rebuild_word->blobs[i]; + UNICHAR_ID unichar_id = wc.unichar_id(i); + float char_certainty = wc.certainty(i); + bool bad_certainty = char_certainty < certainty_threshold; + bool is_punc = wc.unicharset()->get_ispunctuation(unichar_id); + bool is_italic = word.fontinfo && word.fontinfo->is_italic(); + BLOB_CHOICE *choice = word.GetBlobChoice(i); + if (choice && fontinfo_table.size() > 0) { + // Get better information from the specific choice, if available. + int font_id1 = choice->fontinfo_id(); + bool font1_is_italic = font_id1 >= 0 + ? fontinfo_table.get(font_id1).is_italic() : false; + int font_id2 = choice->fontinfo_id2(); + is_italic = font1_is_italic && + (font_id2 < 0 || fontinfo_table.get(font_id2).is_italic()); + } + + float height_fraction = 1.0f; + float char_height = blob->bounding_box().height(); + float normal_height = char_height; + if (wc.unicharset()->top_bottom_useful()) { + int min_bot, max_bot, min_top, max_top; + wc.unicharset()->get_top_bottom(unichar_id, + &min_bot, &max_bot, + &min_top, &max_top); + float hi_height = max_top - max_bot; + float lo_height = min_top - min_bot; + normal_height = (hi_height + lo_height) / 2; + if (normal_height >= kBlnXHeight) { + // Only ding characters that we have decent information for because + // they're supposed to be normal sized, not tiny specks or dashes. + height_fraction = char_height / normal_height; + } + } + bool bad_height = height_fraction < superscript_scaledown_ratio; + + if (debug) { + if (is_italic) { + tprintf(" Rejecting: superscript is italic.\n"); + } + if (is_punc) { + tprintf(" Rejecting: punctuation present.\n"); + } + const char *char_str = wc.unicharset()->id_to_unichar(unichar_id); + if (bad_certainty) { + tprintf(" Rejecting: don't believe character %s with certainty %.2f " + "which is less than threshold %.2f\n", char_str, + char_certainty, certainty_threshold); + } + if (bad_height) { + tprintf(" Rejecting: character %s seems too small @ %.2f versus " + "expected %.2f\n", char_str, char_height, normal_height); + } + } + if (bad_certainty || bad_height || is_punc || is_italic) { + if (ok_run_count == i) { + initial_ok_run_count = ok_run_count; + } + ok_run_count = 0; + } + else { + ok_run_count++; + } + if (char_certainty < worst_certainty) { + worst_certainty = char_certainty; + } + } + bool all_ok = ok_run_count == wc.length(); + if (all_ok && debug) { + tprintf(" Accept: worst revised certainty is %.2f\n", worst_certainty); + } + if (!all_ok) { + if (left_ok) *left_ok = initial_ok_run_count; + if (right_ok) *right_ok = ok_run_count; + } + return all_ok; + } + + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.cpp new file mode 100644 index 0000000..c5e5b88 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.cpp @@ -0,0 +1,82 @@ +/********************************************************************** + * 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 "mfoutline.h" +#include "tessbox.h" +#include "tesseractclass.h" + +#define EXTERN + + /** + * @name tess_segment_pass_n + * + * Segment a word using the pass_n conditions of the tess segmenter. + * @param pass_n pass number + * @param word word to do + */ + +namespace tesseract { + void Tesseract::tess_segment_pass_n(int pass_n, WERD_RES *word) { + 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 (pass_n == 1) + set_pass1(); + else + set_pass2(); + recog_word(word); + if (word->best_choice == NULL) + word->SetupFake(*word->uch_set); + if (word->word->flag(W_DONT_CHOP)) { + wordrec_enable_assoc.set_value(saved_enable_assoc); + chop_enable.set_value(saved_chop_enable); + } + } + + /** + * @name tess_acceptable_word + * + * @return true if the word is regarded as "good enough". + * @param word_choice after context + * @param raw_choice before context + */ + bool Tesseract::tess_acceptable_word(WERD_RES* word) { + return getDict().AcceptableResult(word); + } + + + /** + * @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 diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.h b/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.h new file mode 100644 index 0000000..a96cbce --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessbox.h @@ -0,0 +1,28 @@ +/********************************************************************** + * File: tessbox.h (Formerly tessbox.h) + * Description: Black boxed Tess for developing a resaljet. + * Author: Ray Smith + * Created: Thu Apr 23 11:03:36 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSBOX_H +#define TESSBOX_H + +#include "ratngs.h" +#include "tesseractclass.h" + + // TODO(ocr-team): Delete this along with other empty header files. + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.cpp new file mode 100644 index 0000000..e0483c1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.cpp @@ -0,0 +1,501 @@ +/********************************************************************** + * File: tessedit.cpp (Formerly tessedit.c) + * Description: (Previously) Main program for merge of tess and editor. + * Now just code to load the language model and various + * engine-specific data files. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "stderr.h" +#include "basedir.h" +#include "tessvars.h" +#include "control.h" +#include "reject.h" +#include "pageres.h" +#include "nwmain.h" +#include "pgedit.h" +#include "tprintf.h" +#include "tessedit.h" +#include "stopper.h" +#include "intmatcher.h" +#include "chop.h" +#include "efio.h" +#include "danerror.h" +#include "globals.h" +#include "tesseractclass.h" +#include "params.h" + +#define VARDIR "configs/" /*variables files */ + // config under api +#define API_CONFIG "configs/api_config" + +ETEXT_DESC *global_monitor = NULL; // progress monitor + +namespace tesseract { + + // Read a "config" file containing a set of variable, value pairs. + // Searches the standard places: tessdata/configs, tessdata/tessconfigs + // and also accepts a relative or absolute path name. + void Tesseract::read_config_file(const char *filename, + SetParamConstraint constraint) { + STRING path = datadir; + path += "configs/"; + path += filename; + FILE* fp; + if ((fp = fopen(path.string(), "rb")) != NULL) { + fclose(fp); + } + else { + path = datadir; + path += "tessconfigs/"; + path += filename; + if ((fp = fopen(path.string(), "rb")) != NULL) { + fclose(fp); + } + else { + path = filename; + } + } + ParamUtils::ReadParamsFile(path.string(), constraint, this->params()); + } + + // Returns false if a unicharset file for the specified language was not found + // or was invalid. + // This function initializes TessdataManager. After TessdataManager is + // no longer needed, TessdataManager::End() should be called. + // + // This function sets tessedit_oem_mode to the given OcrEngineMode oem, unless + // it is OEM_DEFAULT, in which case the value of the variable will be obtained + // from the language-specific config file (stored in [lang].traineddata), from + // the config files specified on the command line or left as the default + // OEM_TESSERACT_ONLY if none of the configs specify this variable. + bool Tesseract::init_tesseract_lang_data( + const char *arg0, const char *textbase, const char *language, + OcrEngineMode oem, char **configs, int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_non_debug_params) { + // Set the basename, compute the data directory. + main_setup(arg0, textbase); + + // Set the language data path prefix + lang = language != NULL ? language : "eng"; + language_data_path_prefix = datadir; + language_data_path_prefix += lang; + language_data_path_prefix += "."; + + // Initialize TessdataManager. + //STRING tessdata_path = language_data_path_prefix + kTrainedDataSuffix; + STRING tessdata_path = arg0; + if (!tessdata_manager.Init(tessdata_path.string(), + tessdata_manager_debug_level)) { + return false; + } + + // If a language specific config file (lang.config) exists, load it in. + if (tessdata_manager.SeekToStart(TESSDATA_LANG_CONFIG)) { + ParamUtils::ReadParamsFromFp( + tessdata_manager.GetDataFilePtr(), + tessdata_manager.GetEndOffset(TESSDATA_LANG_CONFIG), + SET_PARAM_CONSTRAINT_NONE, this->params()); + if (tessdata_manager_debug_level) { + tprintf("Loaded language config file\n"); + } + } + + SetParamConstraint set_params_constraint = set_only_non_debug_params ? + SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY : SET_PARAM_CONSTRAINT_NONE; + // Load tesseract variables from config files. This is done after loading + // language-specific variables from [lang].traineddata file, so that custom + // config files can override values in [lang].traineddata file. + for (int i = 0; i < configs_size; ++i) { + read_config_file(configs[i], set_params_constraint); + } + + // Set params specified in vars_vec (done after setting params from config + // files, so that params in vars_vec can override those from files). + if (vars_vec != NULL && vars_values != NULL) { + for (int i = 0; i < vars_vec->size(); ++i) { + if (!ParamUtils::SetParam((*vars_vec)[i].string(), + (*vars_values)[i].string(), + set_params_constraint, this->params())) { + tprintf("Error setting param %s\n", (*vars_vec)[i].string()); + exit(1); + } + } + } + + if (((STRING &)tessedit_write_params_to_file).length() > 0) { + FILE *params_file = fopen(tessedit_write_params_to_file.string(), "wb"); + if (params_file != NULL) { + ParamUtils::PrintParams(params_file, this->params()); + fclose(params_file); + if (tessdata_manager_debug_level > 0) { + tprintf("Wrote parameters to %s\n", + tessedit_write_params_to_file.string()); + } + } + else { + tprintf("Failed to open %s for writing params.\n", + tessedit_write_params_to_file.string()); + } + } + + // Determine which ocr engine(s) should be loaded and used for recognition. + if (oem != OEM_DEFAULT) tessedit_ocr_engine_mode.set_value(oem); + if (tessdata_manager_debug_level) { + tprintf("Loading Tesseract/Cube with tessedit_ocr_engine_mode %d\n", + static_cast(tessedit_ocr_engine_mode)); + } + + // If we are only loading the config file (and so not planning on doing any + // recognition) then there's nothing else do here. + if (tessedit_init_config_only) { + if (tessdata_manager_debug_level) { + tprintf("Returning after loading config file\n"); + } + return true; + } + + // Load the unicharset + if (!tessdata_manager.SeekToStart(TESSDATA_UNICHARSET) || + !unicharset.load_from_file(tessdata_manager.GetDataFilePtr())) { + return false; + } + if (unicharset.size() > MAX_NUM_CLASSES) { + tprintf("Error: Size of unicharset is greater than MAX_NUM_CLASSES\n"); + return false; + } + if (tessdata_manager_debug_level) tprintf("Loaded unicharset\n"); + right_to_left_ = unicharset.major_right_to_left(); + + // Setup initial unichar ambigs table and read universal ambigs. + UNICHARSET encoder_unicharset; + encoder_unicharset.CopyFrom(unicharset); + unichar_ambigs.InitUnicharAmbigs(unicharset, use_ambigs_for_adaption); + unichar_ambigs.LoadUniversal(encoder_unicharset, &unicharset); + + if (!tessedit_ambigs_training && + tessdata_manager.SeekToStart(TESSDATA_AMBIGS)) { + TFile ambigs_file; + ambigs_file.Open(tessdata_manager.GetDataFilePtr(), + tessdata_manager.GetEndOffset(TESSDATA_AMBIGS) + 1); + unichar_ambigs.LoadUnicharAmbigs( + encoder_unicharset, + &ambigs_file, + ambigs_debug_level, use_ambigs_for_adaption, &unicharset); + if (tessdata_manager_debug_level) tprintf("Loaded ambigs\n"); + } + + // The various OcrEngineMode settings (see publictypes.h) determine which + // engine-specific data files need to be loaded. Currently everything needs + // the base tesseract data, which supplies other useful information, but + // alternative engines, such as cube and LSTM are optional. +#ifndef NO_CUBE_BUILD + if (tessedit_ocr_engine_mode == OEM_CUBE_ONLY) { + ASSERT_HOST(init_cube_objects(false, &tessdata_manager)); + if (tessdata_manager_debug_level) + tprintf("Loaded Cube w/out combiner\n"); + } + else if (tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED) { + ASSERT_HOST(init_cube_objects(true, &tessdata_manager)); + if (tessdata_manager_debug_level) + tprintf("Loaded Cube with combiner\n"); + } +#endif + // Init ParamsModel. + // Load pass1 and pass2 weights (for now these two sets are the same, but in + // the future separate sets of weights can be generated). + for (int p = ParamsModel::PTRAIN_PASS1; + p < ParamsModel::PTRAIN_NUM_PASSES; ++p) { + language_model_->getParamsModel().SetPass( + static_cast(p)); + if (tessdata_manager.SeekToStart(TESSDATA_PARAMS_MODEL)) { + if (!language_model_->getParamsModel().LoadFromFp( + lang.string(), tessdata_manager.GetDataFilePtr(), + tessdata_manager.GetEndOffset(TESSDATA_PARAMS_MODEL))) { + return false; + } + } + } + if (tessdata_manager_debug_level) language_model_->getParamsModel().Print(); + + return true; + } + + // Helper returns true if the given string is in the vector of strings. + static bool IsStrInList(const STRING& str, + const GenericVector& str_list) { + for (int i = 0; i < str_list.size(); ++i) { + if (str_list[i] == str) + return true; + } + return false; + } + + // Parse a string of the form [~][+[~]]*. + // Langs with no prefix get appended to to_load, provided they + // are not in there already. + // Langs with ~ prefix get appended to not_to_load, provided they are not in + // there already. + void Tesseract::ParseLanguageString(const char* lang_str, + GenericVector* to_load, + GenericVector* not_to_load) { + STRING remains(lang_str); + while (remains.length() > 0) { + // Find the start of the lang code and which vector to add to. + const char* start = remains.string(); + while (*start == '+') + ++start; + GenericVector* target = to_load; + if (*start == '~') { + target = not_to_load; + ++start; + } + // Find the index of the end of the lang code in string start. + int end = strlen(start); + const char* plus = strchr(start, '+'); + if (plus != NULL && plus - start < end) + end = plus - start; + STRING lang_code(start); + lang_code.truncate_at(end); + STRING next(start + end); + remains = next; + // Check whether lang_code is already in the target vector and add. + if (!IsStrInList(lang_code, *target)) { + if (tessdata_manager_debug_level) + tprintf("Adding language '%s' to list\n", lang_code.string()); + target->push_back(lang_code); + } + } + } + + // Initialize for potentially a set of languages defined by the language + // string and recursively any additional languages required by any language + // traineddata file (via tessedit_load_sublangs in its config) that is loaded. + // See init_tesseract_internal for args. + int Tesseract::init_tesseract( + const char *arg0, const char *textbase, const char *language, + OcrEngineMode oem, char **configs, int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_non_debug_params) { + GenericVector langs_to_load; + GenericVector langs_not_to_load; + ParseLanguageString(language, &langs_to_load, &langs_not_to_load); + + sub_langs_.delete_data_pointers(); + sub_langs_.clear(); + // Find the first loadable lang and load into this. + // Add any languages that this language requires + bool loaded_primary = false; + // Load the rest into sub_langs_. + for (int lang_index = 0; lang_index < langs_to_load.size(); ++lang_index) { + if (!IsStrInList(langs_to_load[lang_index], langs_not_to_load)) { + const char *lang_str = langs_to_load[lang_index].string(); + Tesseract *tess_to_init; + if (!loaded_primary) { + tess_to_init = this; + } + else { + tess_to_init = new Tesseract; + } + + int result = tess_to_init->init_tesseract_internal( + arg0, textbase, lang_str, oem, configs, configs_size, + vars_vec, vars_values, set_only_non_debug_params); + + if (!loaded_primary) { + if (result < 0) { + tprintf("Failed loading language '%s'\n", lang_str); + } + else { + if (tessdata_manager_debug_level) + tprintf("Loaded language '%s' as main language\n", lang_str); + ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(), + &langs_to_load, &langs_not_to_load); + loaded_primary = true; + } + } + else { + if (result < 0) { + tprintf("Failed loading language '%s'\n", lang_str); + delete tess_to_init; + } + else { + if (tessdata_manager_debug_level) + tprintf("Loaded language '%s' as secondary language\n", lang_str); + sub_langs_.push_back(tess_to_init); + // Add any languages that this language requires + ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(), + &langs_to_load, &langs_not_to_load); + } + } + } + } + if (!loaded_primary) { + tprintf("Tesseract couldn't load any languages!\n"); + return -1; // Couldn't load any language! + } + if (!sub_langs_.empty()) { + // In multilingual mode word ratings have to be directly comparable, + // so use the same language model weights for all languages: + // use the primary language's params model if + // tessedit_use_primary_params_model is set, + // otherwise use default language model weights. + if (tessedit_use_primary_params_model) { + for (int s = 0; s < sub_langs_.size(); ++s) { + sub_langs_[s]->language_model_->getParamsModel().Copy( + this->language_model_->getParamsModel()); + } + tprintf("Using params model of the primary language\n"); + if (tessdata_manager_debug_level) { + this->language_model_->getParamsModel().Print(); + } + } + else { + this->language_model_->getParamsModel().Clear(); + for (int s = 0; s < sub_langs_.size(); ++s) { + sub_langs_[s]->language_model_->getParamsModel().Clear(); + } + if (tessdata_manager_debug_level) + tprintf("Using default language params\n"); + } + } + + SetupUniversalFontIds(); + return 0; + } + + // Common initialization for a single language. + // arg0 is the datapath for the tessdata directory, which could be the + // path of the tessdata directory with no trailing /, or (if tessdata + // lives in the same directory as the executable, the path of the executable, + // hence the name arg0. + // textbase is an optional output file basename (used only for training) + // language is the language code to load. + // oem controls which engine(s) will operate on the image + // configs (argv) is an array of config filenames to load variables from. + // May be NULL. + // configs_size (argc) is the number of elements in configs. + // vars_vec is an optional vector of variables to set. + // vars_values is an optional corresponding vector of values for the variables + // in vars_vec. + // If set_only_init_params is true, then only the initialization variables + // will be set. + int Tesseract::init_tesseract_internal( + const char *arg0, const char *textbase, const char *language, + OcrEngineMode oem, char **configs, int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_non_debug_params) { + if (!init_tesseract_lang_data(arg0, textbase, language, oem, configs, + configs_size, vars_vec, vars_values, + set_only_non_debug_params)) { + return -1; + } + if (tessedit_init_config_only) { + tessdata_manager.End(); + return 0; + } + // If only Cube will be used, skip loading Tesseract classifier's + // pre-trained templates. + bool init_tesseract_classifier = + (tessedit_ocr_engine_mode == OEM_TESSERACT_ONLY || + tessedit_ocr_engine_mode == OEM_TESSERACT_CUBE_COMBINED); + // If only Cube will be used and if it has its own Unicharset, + // skip initializing permuter and loading Tesseract Dawgs. + bool init_dict = + !(tessedit_ocr_engine_mode == OEM_CUBE_ONLY && + tessdata_manager.SeekToStart(TESSDATA_CUBE_UNICHARSET)); + program_editup(textbase, init_tesseract_classifier, init_dict); + tessdata_manager.End(); + return 0; //Normal exit + } + + // Helper builds the all_fonts table by adding new fonts from new_fonts. + static void CollectFonts(const UnicityTable& new_fonts, + UnicityTable* all_fonts) { + for (int i = 0; i < new_fonts.size(); ++i) { + // UnicityTable uniques as we go. + all_fonts->push_back(new_fonts.get(i)); + } + } + + // Helper assigns an id to lang_fonts using the index in all_fonts table. + static void AssignIds(const UnicityTable& all_fonts, + UnicityTable* lang_fonts) { + for (int i = 0; i < lang_fonts->size(); ++i) { + int index = all_fonts.get_id(lang_fonts->get(i)); + lang_fonts->get_mutable(i)->universal_id = index; + } + } + + // Set the universal_id member of each font to be unique among all + // instances of the same font loaded. + void Tesseract::SetupUniversalFontIds() { + // Note that we can get away with bitwise copying FontInfo in + // all_fonts, as it is a temporary structure and we avoid setting the + // delete callback. + UnicityTable all_fonts; + all_fonts.set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); + + // Create the universal ID table. + CollectFonts(get_fontinfo_table(), &all_fonts); + for (int i = 0; i < sub_langs_.size(); ++i) { + CollectFonts(sub_langs_[i]->get_fontinfo_table(), &all_fonts); + } + // Assign ids from the table to each font table. + AssignIds(all_fonts, &get_fontinfo_table()); + for (int i = 0; i < sub_langs_.size(); ++i) { + AssignIds(all_fonts, &sub_langs_[i]->get_fontinfo_table()); + } + font_table_size_ = all_fonts.size(); + } + + // init the LM component + int Tesseract::init_tesseract_lm(const char *arg0, + const char *textbase, + const char *language) { + if (!init_tesseract_lang_data(arg0, textbase, language, OEM_TESSERACT_ONLY, + NULL, 0, NULL, NULL, false)) + return -1; + getDict().SetupForLoad(Dict::GlobalDawgCache()); + getDict().Load(tessdata_manager.GetDataFileName().string(), lang); + getDict().FinishLoad(); + tessdata_manager.End(); + return 0; + } + + void Tesseract::end_tesseract() { + end_recog(); + } + + /* Define command type identifiers */ + + enum CMD_EVENTS + { + ACTION_1_CMD_EVENT, + RECOG_WERDS, + RECOG_PSEUDO, + ACTION_2_CMD_EVENT + }; +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.h b/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.h new file mode 100644 index 0000000..b4b85e4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessedit.h @@ -0,0 +1,29 @@ +/********************************************************************** + * File: tessedit.h (Formerly tessedit.h) + * Description: Main program for merge of tess and editor. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSEDIT_H +#define TESSEDIT_H + +#include "blobs.h" +#include "pgedit.h" + + //progress monitor +extern ETEXT_DESC *global_monitor; + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.cpp new file mode 100644 index 0000000..3e8db47 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.cpp @@ -0,0 +1,305 @@ +/********************************************************************** + * File: tesseract_cube_combiner.h + * Description: Declaration of the Tesseract & Cube results combiner 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 TesseractCubeCombiner class provides the functionality of combining + // the recognition results of Tesseract and Cube at the word level + +#include +#include + +#include "tesseract_cube_combiner.h" + +#include "cube_object.h" +#include "cube_reco_context.h" +#include "cube_utils.h" +#include "neural_net.h" +#include "tesseractclass.h" +#include "word_altlist.h" + +namespace tesseract { + + TesseractCubeCombiner::TesseractCubeCombiner(CubeRecoContext *cube_cntxt) { + cube_cntxt_ = cube_cntxt; + combiner_net_ = NULL; + } + + TesseractCubeCombiner::~TesseractCubeCombiner() { + if (combiner_net_ != NULL) { + delete combiner_net_; + combiner_net_ = NULL; + } + } + + bool TesseractCubeCombiner::LoadCombinerNet() { + ASSERT_HOST(cube_cntxt_); + // Compute the path of the combiner net + string data_path; + cube_cntxt_->GetDataFilePath(&data_path); + string net_file_name = data_path + cube_cntxt_->Lang() + + ".tesseract_cube.nn"; + + // Return false if file does not exist + FILE *fp = fopen(net_file_name.c_str(), "rb"); + if (fp == NULL) + return false; + else + fclose(fp); + + // Load and validate net + combiner_net_ = NeuralNet::FromFile(net_file_name); + if (combiner_net_ == NULL) { + tprintf("Could not read combiner net file %s", net_file_name.c_str()); + return false; + } + else if (combiner_net_->out_cnt() != 2) { + tprintf("Invalid combiner net file %s! Output count != 2\n", + net_file_name.c_str()); + delete combiner_net_; + combiner_net_ = NULL; + return false; + } + return true; + } + + // Normalize a UTF-8 string. Converts the UTF-8 string to UTF32 and optionally + // strips punc and/or normalizes case and then converts back + string TesseractCubeCombiner::NormalizeString(const string &str, + bool remove_punc, + bool norm_case) { + // convert to UTF32 + string_32 str32; + CubeUtils::UTF8ToUTF32(str.c_str(), &str32); + // strip punc and normalize + string_32 new_str32; + for (int idx = 0; idx < str32.length(); idx++) { + // if no punc removal is required or not a punctuation character + if (!remove_punc || iswpunct(str32[idx]) == 0) { + char_32 norm_char = str32[idx]; + // normalize case if required + if (norm_case && iswalpha(norm_char)) { + norm_char = towlower(norm_char); + } + new_str32.push_back(norm_char); + } + } + // convert back to UTF8 + string new_str; + CubeUtils::UTF32ToUTF8(new_str32.c_str(), &new_str); + return new_str; + } + + // Compares 2 strings optionally ignoring punctuation + int TesseractCubeCombiner::CompareStrings(const string &str1, + const string &str2, + bool ignore_punc, + bool ignore_case) { + if (!ignore_punc && !ignore_case) { + return str1.compare(str2); + } + string norm_str1 = NormalizeString(str1, ignore_punc, ignore_case); + string norm_str2 = NormalizeString(str2, ignore_punc, ignore_case); + return norm_str1.compare(norm_str2); + } + + // Check if a string is a valid Tess dict word or not + bool TesseractCubeCombiner::ValidWord(const string &str) { + return (cube_cntxt_->TesseractObject()->getDict().valid_word(str.c_str()) + > 0); + } + + // Public method for computing the combiner features. The agreement + // output parameter will be true if both answers are identical, + // and false otherwise. + bool TesseractCubeCombiner::ComputeCombinerFeatures(const string &tess_str, + int tess_confidence, + CubeObject *cube_obj, + WordAltList *cube_alt_list, + vector *features, + bool *agreement) { + features->clear(); + *agreement = false; + if (cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) + return false; + + // Get Cube's best string; return false if empty + char_32 *cube_best_str32 = cube_alt_list->Alt(0); + if (cube_best_str32 == NULL || CubeUtils::StrLen(cube_best_str32) < 1) + return false; + string cube_best_str; + int cube_best_cost = cube_alt_list->AltCost(0); + int cube_best_bigram_cost = 0; + bool cube_best_bigram_cost_valid = true; + if (cube_cntxt_->Bigrams()) + cube_best_bigram_cost = cube_cntxt_->Bigrams()-> + Cost(cube_best_str32, cube_cntxt_->CharacterSet()); + else + cube_best_bigram_cost_valid = false; + CubeUtils::UTF32ToUTF8(cube_best_str32, &cube_best_str); + + // Get Tesseract's UTF32 string + string_32 tess_str32; + CubeUtils::UTF8ToUTF32(tess_str.c_str(), &tess_str32); + + // Compute agreement flag + *agreement = (tess_str.compare(cube_best_str) == 0); + + // Get Cube's second best string; if empty, return false + char_32 *cube_next_best_str32; + string cube_next_best_str; + int cube_next_best_cost = WORST_COST; + if (cube_alt_list->AltCount() > 1) { + cube_next_best_str32 = cube_alt_list->Alt(1); + if (cube_next_best_str32 == NULL || + CubeUtils::StrLen(cube_next_best_str32) == 0) { + return false; + } + cube_next_best_cost = cube_alt_list->AltCost(1); + CubeUtils::UTF32ToUTF8(cube_next_best_str32, &cube_next_best_str); + } + // Rank of Tesseract's top result in Cube's alternate list + int tess_rank = 0; + for (tess_rank = 0; tess_rank < cube_alt_list->AltCount(); tess_rank++) { + string alt_str; + CubeUtils::UTF32ToUTF8(cube_alt_list->Alt(tess_rank), &alt_str); + if (alt_str == tess_str) + break; + } + + // Cube's cost for tesseract's result. Note that this modifies the + // state of cube_obj, including its alternate list by calling RecognizeWord() + int tess_cost = cube_obj->WordCost(tess_str.c_str()); + // Cube's bigram cost of Tesseract's string + int tess_bigram_cost = 0; + int tess_bigram_cost_valid = true; + if (cube_cntxt_->Bigrams()) + tess_bigram_cost = cube_cntxt_->Bigrams()-> + Cost(tess_str32.c_str(), cube_cntxt_->CharacterSet()); + else + tess_bigram_cost_valid = false; + + // Tesseract confidence + features->push_back(tess_confidence); + // Cube cost of Tesseract string + features->push_back(tess_cost); + // Cube Rank of Tesseract string + features->push_back(tess_rank); + // length of Tesseract OCR string + features->push_back(tess_str.length()); + // Tesseract OCR string in dictionary + features->push_back(ValidWord(tess_str)); + if (tess_bigram_cost_valid) { + // bigram cost of Tesseract string + features->push_back(tess_bigram_cost); + } + // Cube tess_cost of Cube best string + features->push_back(cube_best_cost); + // Cube tess_cost of Cube next best string + features->push_back(cube_next_best_cost); + // length of Cube string + features->push_back(cube_best_str.length()); + // Cube string in dictionary + features->push_back(ValidWord(cube_best_str)); + if (cube_best_bigram_cost_valid) { + // bigram cost of Cube string + features->push_back(cube_best_bigram_cost); + } + // case-insensitive string comparison, including punctuation + int compare_nocase_punc = CompareStrings(cube_best_str, + tess_str, false, true); + features->push_back(compare_nocase_punc == 0); + // case-sensitive string comparison, ignoring punctuation + int compare_case_nopunc = CompareStrings(cube_best_str, + tess_str, true, false); + features->push_back(compare_case_nopunc == 0); + // case-insensitive string comparison, ignoring punctuation + int compare_nocase_nopunc = CompareStrings(cube_best_str, + tess_str, true, true); + features->push_back(compare_nocase_nopunc == 0); + return true; + } + + // The CubeObject parameter is used for 2 purposes: 1) to retrieve + // cube's alt list, and 2) to compute cube's word cost for the + // tesseract result. The call to CubeObject::WordCost() modifies + // the object's alternate list, so previous state will be lost. + float TesseractCubeCombiner::CombineResults(WERD_RES *tess_res, + CubeObject *cube_obj) { + // If no combiner is loaded or the cube object is undefined, + // tesseract wins with probability 1.0 + if (combiner_net_ == NULL || cube_obj == NULL) { + tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " + "Cube objects not initialized; defaulting to Tesseract\n"); + return 1.0; + } + + // Retrieve the alternate list from the CubeObject's current state. + // If the alt list empty, tesseract wins with probability 1.0 + WordAltList *cube_alt_list = cube_obj->AlternateList(); + if (cube_alt_list == NULL) + cube_alt_list = cube_obj->RecognizeWord(); + if (cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) { + tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " + "Cube returned no results; defaulting to Tesseract\n"); + return 1.0; + } + return CombineResults(tess_res, cube_obj, cube_alt_list); + } + + // The alt_list parameter is expected to have been extracted from the + // CubeObject that recognized the word to be combined. The cube_obj + // parameter passed may be either same instance or a separate instance to + // be used only by the combiner. In both cases, its alternate + // list will be modified by an internal call to RecognizeWord(). + float TesseractCubeCombiner::CombineResults(WERD_RES *tess_res, + CubeObject *cube_obj, + WordAltList *cube_alt_list) { + // If no combiner is loaded or the cube object is undefined, or the + // alt list is empty, tesseract wins with probability 1.0 + if (combiner_net_ == NULL || cube_obj == NULL || + cube_alt_list == NULL || cube_alt_list->AltCount() <= 0) { + tprintf("Cube WARNING (TesseractCubeCombiner::CombineResults): " + "Cube result cannot be retrieved; defaulting to Tesseract\n"); + return 1.0; + } + + // Tesseract result string, tesseract confidence, and cost of + // tesseract result according to cube + string tess_str = tess_res->best_choice->unichar_string().string(); + // Map certainty [-20.0, 0.0] to confidence [0, 100] + int tess_confidence = MIN(100, MAX(1, static_cast( + 100 + (5 * tess_res->best_choice->certainty())))); + + // Compute the combiner features. If feature computation fails or + // answers are identical, tesseract wins with probability 1.0 + vector features; + bool agreement; + bool combiner_success = ComputeCombinerFeatures(tess_str, tess_confidence, + cube_obj, cube_alt_list, + &features, &agreement); + if (!combiner_success || agreement) + return 1.0; + + // Classify combiner feature vector and return output (probability + // of tesseract class). + double net_out[2]; + if (!combiner_net_->FeedForward(&features[0], net_out)) + return 1.0; + return net_out[1]; + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.h b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.h new file mode 100644 index 0000000..c157d57 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseract_cube_combiner.h @@ -0,0 +1,93 @@ +/********************************************************************** + * File: tesseract_cube_combiner.h + * Description: Declaration of the Tesseract & Cube results combiner 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 TesseractCubeCombiner class provides the functionality of combining + // the recognition results of Tesseract and Cube at the word level + +#ifndef TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H +#define TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H + +#include +#include +#include "pageres.h" + +namespace tesseract { + + class CubeObject; + class NeuralNet; + class CubeRecoContext; + class WordAltList; + + class TesseractCubeCombiner { + public: + explicit TesseractCubeCombiner(CubeRecoContext *cube_cntxt); + virtual ~TesseractCubeCombiner(); + + // There are 2 public methods for combining the results of tesseract + // and cube. Both return the probability that the Tesseract result is + // correct. The difference between the two interfaces is in how the + // passed-in CubeObject is used. + + // The CubeObject parameter is used for 2 purposes: 1) to retrieve + // cube's alt list, and 2) to compute cube's word cost for the + // tesseract result. Both uses may modify the state of the + // CubeObject (including the BeamSearch state) with a call to + // RecognizeWord(). + float CombineResults(WERD_RES *tess_res, CubeObject *cube_obj); + + // The alt_list parameter is expected to have been extracted from the + // CubeObject that recognized the word to be combined. The cube_obj + // parameter passed in is a separate instance to be used only by + // the combiner. + float CombineResults(WERD_RES *tess_res, CubeObject *cube_obj, + WordAltList *alt_list); + + // Public method for computing the combiner features. The agreement + // output parameter will be true if both answers are identical, + // false otherwise. Modifies the cube_alt_list, so no assumptions + // should be made about its state upon return. + bool ComputeCombinerFeatures(const std::string &tess_res, + int tess_confidence, + CubeObject *cube_obj, + WordAltList *cube_alt_list, + std::vector *features, + bool *agreement); + + // Is the word valid according to Tesseract's language model + bool ValidWord(const std::string &str); + + // Loads the combiner neural network from file, using cube_cntxt_ + // to find path. + bool LoadCombinerNet(); + private: + // Normalize a UTF-8 string. Converts the UTF-8 string to UTF32 and optionally + // strips punc and/or normalizes case and then converts back + std::string NormalizeString(const std::string &str, bool remove_punc, bool norm_case); + + // Compares 2 strings after optionally normalizing them and or stripping + // punctuation + int CompareStrings(const std::string &str1, const std::string &str2, bool ignore_punc, + bool norm_case); + + NeuralNet *combiner_net_; // pointer to the combiner NeuralNet object + CubeRecoContext *cube_cntxt_; // used for language ID and data paths + }; +} + +#endif // TESSERACT_CCMAIN_TESSERACT_CUBE_COMBINER_H diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.cpp new file mode 100644 index 0000000..457471b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.cpp @@ -0,0 +1,769 @@ +/////////////////////////////////////////////////////////////////////// +// File: tesseractclass.cpp +// Description: The Tesseract class. It holds/owns everything needed +// to run Tesseract on a single language, and also a set of +// sub-Tesseracts to run sub-languages. For thread safety, *every* +// variable that was previously global or static (except for +// constant data, and some visual debugging flags) has been moved +// in here, directly, or indirectly. +// This makes it safe to run multiple Tesseracts in different +// threads in parallel, and keeps the different language +// instances separate. +// Some global functions remain, but they are isolated re-entrant +// functions that operate on their arguments. Functions that work +// on variable data have been moved to an appropriate class based +// mostly on the directory hierarchy. For more information see +// slide 6 of "2ArchitectureAndDataStructures" in +// https://drive.google.com/file/d/0B7l10Bj_LprhbUlIUFlCdGtDYkE/edit?usp=sharing +// Some global data and related functions still exist in the +// training-related code, but they don't interfere with normal +// recognition operation. +// Author: Ray Smith +// Created: Fri Mar 07 08:17: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 automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "tesseractclass.h" + +#include "allheaders.h" +#ifndef NO_CUBE_BUILD +#include "cube_reco_context.h" +#endif +#include "edgblob.h" +#include "equationdetect.h" +#include "globals.h" +#ifndef NO_CUBE_BUILD +#include "tesseract_cube_combiner.h" +#endif + +namespace tesseract { + + Tesseract::Tesseract() + : BOOL_MEMBER(tessedit_resegment_from_boxes, false, + "Take segmentation and labeling from box file", + this->params()), + BOOL_MEMBER(tessedit_resegment_from_line_boxes, false, + "Conversion of word/line box file to char box file", + this->params()), + BOOL_MEMBER(tessedit_train_from_boxes, false, + "Generate training data from boxed chars", this->params()), + BOOL_MEMBER(tessedit_make_boxes_from_boxes, false, + "Generate more boxes from boxed chars", this->params()), + BOOL_MEMBER(tessedit_dump_pageseg_images, false, + "Dump intermediate images made during page segmentation", + this->params()), + // The default for pageseg_mode is the old behaviour, so as not to + // upset anything that relies on that. + INT_MEMBER( + tessedit_pageseg_mode, PSM_SINGLE_BLOCK, + "Page seg mode: 0=osd only, 1=auto+osd, 2=auto, 3=col, 4=block," + " 5=line, 6=word, 7=char" + " (Values from PageSegMode enum in publictypes.h)", + this->params()), + INT_INIT_MEMBER(tessedit_ocr_engine_mode, tesseract::OEM_TESSERACT_ONLY, + "Which OCR engine(s) to run (Tesseract, Cube, both)." + " Defaults to loading and running only Tesseract" + " (no Cube,no combiner)." + " Values from OcrEngineMode enum in tesseractclass.h)", + this->params()), + STRING_MEMBER(tessedit_char_blacklist, "", + "Blacklist of chars not to recognize", this->params()), + STRING_MEMBER(tessedit_char_whitelist, "", + "Whitelist of chars to recognize", this->params()), + STRING_MEMBER(tessedit_char_unblacklist, "", + "List of chars to override tessedit_char_blacklist", + this->params()), + BOOL_MEMBER(tessedit_ambigs_training, false, + "Perform training for ambiguities", this->params()), + INT_MEMBER(pageseg_devanagari_split_strategy, + tesseract::ShiroRekhaSplitter::NO_SPLIT, + "Whether to use the top-line splitting process for Devanagari " + "documents while performing page-segmentation.", + this->params()), + INT_MEMBER(ocr_devanagari_split_strategy, + tesseract::ShiroRekhaSplitter::NO_SPLIT, + "Whether to use the top-line splitting process for Devanagari " + "documents while performing ocr.", + this->params()), + STRING_MEMBER(tessedit_write_params_to_file, "", + "Write all parameters to the given file.", this->params()), + BOOL_MEMBER(tessedit_adaption_debug, false, + "Generate and print debug" + " information for adaption", + this->params()), + INT_MEMBER(bidi_debug, 0, "Debug level for BiDi", this->params()), + INT_MEMBER(applybox_debug, 1, "Debug level", this->params()), + INT_MEMBER(applybox_page, 0, "Page number to apply boxes from", + this->params()), + STRING_MEMBER(applybox_exposure_pattern, ".exp", + "Exposure value follows" + " this pattern in the image filename. The name of the image" + " files are expected to be in the form" + " [lang].[fontname].exp[num].tif", + this->params()), + BOOL_MEMBER(applybox_learn_chars_and_char_frags_mode, false, + "Learn both character fragments (as is done in the" + " special low exposure mode) as well as unfragmented" + " characters.", + this->params()), + BOOL_MEMBER(applybox_learn_ngrams_mode, false, + "Each bounding box" + " is assumed to contain ngrams. Only learn the ngrams" + " whose outlines overlap horizontally.", + this->params()), + BOOL_MEMBER(tessedit_display_outwords, false, "Draw output words", + this->params()), + BOOL_MEMBER(tessedit_dump_choices, false, "Dump char choices", + this->params()), + BOOL_MEMBER(tessedit_timing_debug, false, "Print timing stats", + this->params()), + BOOL_MEMBER(tessedit_fix_fuzzy_spaces, true, + "Try to improve fuzzy spaces", this->params()), + BOOL_MEMBER(tessedit_unrej_any_wd, false, + "Don't bother with word plausibility", this->params()), + BOOL_MEMBER(tessedit_fix_hyphens, true, "Crunch double hyphens?", + this->params()), + BOOL_MEMBER(tessedit_redo_xheight, true, "Check/Correct x-height", + this->params()), + BOOL_MEMBER(tessedit_enable_doc_dict, true, + "Add words to the document dictionary", this->params()), + BOOL_MEMBER(tessedit_debug_fonts, false, "Output font info per char", + this->params()), + BOOL_MEMBER(tessedit_debug_block_rejection, false, "Block and Row stats", + this->params()), + BOOL_MEMBER(tessedit_enable_bigram_correction, true, + "Enable correction based on the word bigram dictionary.", + this->params()), + BOOL_MEMBER(tessedit_enable_dict_correction, false, + "Enable single word correction based on the dictionary.", + this->params()), + INT_MEMBER(tessedit_bigram_debug, 0, + "Amount of debug output for bigram correction.", + this->params()), + BOOL_MEMBER(enable_noise_removal, true, + "Remove and conditionally reassign small outlines when they" + " confuse layout analysis, determining diacritics vs noise", + this->params()), + INT_MEMBER(debug_noise_removal, 0, "Debug reassignment of small outlines", + this->params()), + // Worst (min) certainty, for which a diacritic is allowed to make the + // base + // character worse and still be included. + double_MEMBER(noise_cert_basechar, -8.0, + "Hingepoint for base char certainty", this->params()), + // Worst (min) certainty, for which a non-overlapping diacritic is allowed + // to make the base character worse and still be included. + double_MEMBER(noise_cert_disjoint, -1.0, + "Hingepoint for disjoint certainty", this->params()), + // Worst (min) certainty, for which a diacritic is allowed to make a new + // stand-alone blob. + double_MEMBER(noise_cert_punc, -3.0, + "Threshold for new punc char certainty", this->params()), + // Factor of certainty margin for adding diacritics to not count as worse. + double_MEMBER(noise_cert_factor, 0.375, + "Scaling on certainty diff from Hingepoint", + this->params()), + INT_MEMBER(noise_maxperblob, 8, "Max diacritics to apply to a blob", + this->params()), + INT_MEMBER(noise_maxperword, 16, "Max diacritics to apply to a word", + this->params()), + INT_MEMBER(debug_x_ht_level, 0, "Reestimate debug", this->params()), + BOOL_MEMBER(debug_acceptable_wds, false, "Dump word pass/fail chk", + this->params()), + STRING_MEMBER(chs_leading_punct, "('`\"", "Leading punctuation", + this->params()), + STRING_MEMBER(chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation", + this->params()), + STRING_MEMBER(chs_trailing_punct2, ")'`\"", "2nd Trailing punctuation", + this->params()), + double_MEMBER(quality_rej_pc, 0.08, + "good_quality_doc lte rejection limit", this->params()), + double_MEMBER(quality_blob_pc, 0.0, + "good_quality_doc gte good blobs limit", this->params()), + double_MEMBER(quality_outline_pc, 1.0, + "good_quality_doc lte outline error limit", this->params()), + double_MEMBER(quality_char_pc, 0.95, + "good_quality_doc gte good char limit", this->params()), + INT_MEMBER(quality_min_initial_alphas_reqd, 2, "alphas in a good word", + this->params()), + INT_MEMBER(tessedit_tess_adaption_mode, 0x27, + "Adaptation decision algorithm for tess", this->params()), + BOOL_MEMBER(tessedit_minimal_rej_pass1, false, + "Do minimal rejection on pass 1 output", this->params()), + BOOL_MEMBER(tessedit_test_adaption, false, "Test adaption criteria", + this->params()), + BOOL_MEMBER(tessedit_matcher_log, false, "Log matcher activity", + this->params()), + INT_MEMBER(tessedit_test_adaption_mode, 3, + "Adaptation decision algorithm for tess", this->params()), + BOOL_MEMBER(test_pt, false, "Test for point", this->params()), + double_MEMBER(test_pt_x, 99999.99, "xcoord", this->params()), + double_MEMBER(test_pt_y, 99999.99, "ycoord", this->params()), + INT_MEMBER(paragraph_debug_level, 0, "Print paragraph debug info.", + this->params()), + BOOL_MEMBER(paragraph_text_based, true, + "Run paragraph detection on the post-text-recognition " + "(more accurate)", + this->params()), + INT_MEMBER(cube_debug_level, 0, "Print cube debug info.", this->params()), + STRING_MEMBER(outlines_odd, "%| ", "Non standard number of outlines", + this->params()), + STRING_MEMBER(outlines_2, "ij!?%\":;", "Non standard number of outlines", + this->params()), + BOOL_MEMBER(docqual_excuse_outline_errs, false, + "Allow outline errs in unrejection?", this->params()), + BOOL_MEMBER(tessedit_good_quality_unrej, true, + "Reduce rejection on good docs", this->params()), + BOOL_MEMBER(tessedit_use_reject_spaces, true, "Reject spaces?", + this->params()), + double_MEMBER(tessedit_reject_doc_percent, 65.00, + "%rej allowed before rej whole doc", this->params()), + double_MEMBER(tessedit_reject_block_percent, 45.00, + "%rej allowed before rej whole block", this->params()), + double_MEMBER(tessedit_reject_row_percent, 40.00, + "%rej allowed before rej whole row", this->params()), + double_MEMBER(tessedit_whole_wd_rej_row_percent, 70.00, + "Number of row rejects in whole word rejects" + "which prevents whole row rejection", + this->params()), + BOOL_MEMBER(tessedit_preserve_blk_rej_perfect_wds, true, + "Only rej partially rejected words in block rejection", + this->params()), + BOOL_MEMBER(tessedit_preserve_row_rej_perfect_wds, true, + "Only rej partially rejected words in row rejection", + this->params()), + BOOL_MEMBER(tessedit_dont_blkrej_good_wds, false, + "Use word segmentation quality metric", this->params()), + BOOL_MEMBER(tessedit_dont_rowrej_good_wds, false, + "Use word segmentation quality metric", this->params()), + INT_MEMBER(tessedit_preserve_min_wd_len, 2, + "Only preserve wds longer than this", this->params()), + BOOL_MEMBER(tessedit_row_rej_good_docs, true, + "Apply row rejection to good docs", this->params()), + double_MEMBER(tessedit_good_doc_still_rowrej_wd, 1.1, + "rej good doc wd if more than this fraction rejected", + this->params()), + BOOL_MEMBER(tessedit_reject_bad_qual_wds, true, + "Reject all bad quality wds", this->params()), + BOOL_MEMBER(tessedit_debug_doc_rejection, false, "Page stats", + this->params()), + BOOL_MEMBER(tessedit_debug_quality_metrics, false, + "Output data to debug file", this->params()), + BOOL_MEMBER(bland_unrej, false, "unrej potential with no chekcs", + this->params()), + double_MEMBER(quality_rowrej_pc, 1.1, + "good_quality_doc gte good char limit", this->params()), + BOOL_MEMBER(unlv_tilde_crunching, true, + "Mark v.bad words for tilde crunch", this->params()), + BOOL_MEMBER(hocr_font_info, false, "Add font info to hocr output", + this->params()), + BOOL_MEMBER(crunch_early_merge_tess_fails, true, "Before word crunch?", + this->params()), + BOOL_MEMBER(crunch_early_convert_bad_unlv_chs, false, + "Take out ~^ early?", this->params()), + double_MEMBER(crunch_terrible_rating, 80.0, "crunch rating lt this", + this->params()), + BOOL_MEMBER(crunch_terrible_garbage, true, "As it says", this->params()), + double_MEMBER(crunch_poor_garbage_cert, -9.0, + "crunch garbage cert lt this", this->params()), + double_MEMBER(crunch_poor_garbage_rate, 60, + "crunch garbage rating lt this", this->params()), + double_MEMBER(crunch_pot_poor_rate, 40, "POTENTIAL crunch rating lt this", + this->params()), + double_MEMBER(crunch_pot_poor_cert, -8.0, "POTENTIAL crunch cert lt this", + this->params()), + BOOL_MEMBER(crunch_pot_garbage, true, "POTENTIAL crunch garbage", + this->params()), + double_MEMBER(crunch_del_rating, 60, "POTENTIAL crunch rating lt this", + this->params()), + double_MEMBER(crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this", + this->params()), + double_MEMBER(crunch_del_min_ht, 0.7, "Del if word ht lt xht x this", + this->params()), + double_MEMBER(crunch_del_max_ht, 3.0, "Del if word ht gt xht x this", + this->params()), + double_MEMBER(crunch_del_min_width, 3.0, + "Del if word width lt xht x this", this->params()), + double_MEMBER(crunch_del_high_word, 1.5, + "Del if word gt xht x this above bl", this->params()), + double_MEMBER(crunch_del_low_word, 0.5, + "Del if word gt xht x this below bl", this->params()), + double_MEMBER(crunch_small_outlines_size, 0.6, "Small if lt xht x this", + this->params()), + INT_MEMBER(crunch_rating_max, 10, "For adj length in rating per ch", + this->params()), + INT_MEMBER(crunch_pot_indicators, 1, + "How many potential indicators needed", this->params()), + BOOL_MEMBER(crunch_leave_ok_strings, true, "Don't touch sensible strings", + this->params()), + BOOL_MEMBER(crunch_accept_ok, true, "Use acceptability in okstring", + this->params()), + BOOL_MEMBER(crunch_leave_accept_strings, false, + "Don't pot crunch sensible strings", this->params()), + BOOL_MEMBER(crunch_include_numerals, false, "Fiddle alpha figures", + this->params()), + INT_MEMBER(crunch_leave_lc_strings, 4, + "Don't crunch words with long lower case strings", + this->params()), + INT_MEMBER(crunch_leave_uc_strings, 4, + "Don't crunch words with long lower case strings", + this->params()), + INT_MEMBER(crunch_long_repetitions, 3, + "Crunch words with long repetitions", this->params()), + INT_MEMBER(crunch_debug, 0, "As it says", this->params()), + INT_MEMBER(fixsp_non_noise_limit, 1, + "How many non-noise blbs either side?", this->params()), + double_MEMBER(fixsp_small_outlines_size, 0.28, "Small if lt xht x this", + this->params()), + BOOL_MEMBER(tessedit_prefer_joined_punct, false, + "Reward punctation joins", this->params()), + INT_MEMBER(fixsp_done_mode, 1, "What constitues done for spacing", + this->params()), + INT_MEMBER(debug_fix_space_level, 0, "Contextual fixspace debug", + this->params()), + STRING_MEMBER(numeric_punctuation, ".,", + "Punct. chs expected WITHIN numbers", this->params()), + INT_MEMBER(x_ht_acceptance_tolerance, 8, + "Max allowed deviation of blob top outside of font data", + this->params()), + INT_MEMBER(x_ht_min_change, 8, + "Min change in xht before actually trying it", this->params()), + INT_MEMBER(superscript_debug, 0, + "Debug level for sub & superscript fixer", this->params()), + double_MEMBER( + superscript_worse_certainty, 2.0, + "How many times worse " + "certainty does a superscript position glyph need to be for " + "us to try classifying it as a char with a different " + "baseline?", + this->params()), + double_MEMBER( + superscript_bettered_certainty, 0.97, + "What reduction in " + "badness do we think sufficient to choose a superscript " + "over what we'd thought. For example, a value of 0.6 means " + "we want to reduce badness of certainty by at least 40%", + this->params()), + double_MEMBER(superscript_scaledown_ratio, 0.4, + "A superscript scaled down more than this is unbelievably " + "small. For example, 0.3 means we expect the font size to " + "be no smaller than 30% of the text line font size.", + this->params()), + double_MEMBER(subscript_max_y_top, 0.5, + "Maximum top of a character measured as a multiple of " + "x-height above the baseline for us to reconsider whether " + "it's a subscript.", + this->params()), + double_MEMBER(superscript_min_y_bottom, 0.3, + "Minimum bottom of a character measured as a multiple of " + "x-height above the baseline for us to reconsider whether " + "it's a superscript.", + this->params()), + BOOL_MEMBER(tessedit_write_block_separators, false, + "Write block separators in output", this->params()), + BOOL_MEMBER(tessedit_write_rep_codes, false, "Write repetition char code", + this->params()), + BOOL_MEMBER(tessedit_write_unlv, false, "Write .unlv output file", + this->params()), + BOOL_MEMBER(tessedit_create_txt, false, "Write .txt output file", + this->params()), + BOOL_MEMBER(tessedit_create_hocr, false, "Write .html hOCR output file", + this->params()), + BOOL_MEMBER(tessedit_create_tsv, false, "Write .tsv output file", + this->params()), + BOOL_MEMBER(tessedit_create_pdf, false, "Write .pdf output file", + this->params()), + BOOL_MEMBER(textonly_pdf, false, "Create PDF with only one invisible text layer", + this->params()), + STRING_MEMBER(unrecognised_char, "|", + "Output char for unidentified blobs", this->params()), + INT_MEMBER(suspect_level, 99, "Suspect marker level", this->params()), + INT_MEMBER(suspect_space_level, 100, + "Min suspect level for rejecting spaces", this->params()), + INT_MEMBER(suspect_short_words, 2, + "Don't suspect dict wds longer than this", this->params()), + BOOL_MEMBER(suspect_constrain_1Il, false, "UNLV keep 1Il chars rejected", + this->params()), + double_MEMBER(suspect_rating_per_ch, 999.9, + "Don't touch bad rating limit", this->params()), + double_MEMBER(suspect_accept_rating, -999.9, "Accept good rating limit", + this->params()), + BOOL_MEMBER(tessedit_minimal_rejection, false, + "Only reject tess failures", this->params()), + BOOL_MEMBER(tessedit_zero_rejection, false, "Don't reject ANYTHING", + this->params()), + BOOL_MEMBER(tessedit_word_for_word, false, + "Make output have exactly one word per WERD", this->params()), + BOOL_MEMBER(tessedit_zero_kelvin_rejection, false, + "Don't reject ANYTHING AT ALL", this->params()), + BOOL_MEMBER(tessedit_consistent_reps, true, + "Force all rep chars the same", this->params()), + INT_MEMBER(tessedit_reject_mode, 0, "Rejection algorithm", + this->params()), + BOOL_MEMBER(tessedit_rejection_debug, false, "Adaption debug", + this->params()), + BOOL_MEMBER(tessedit_flip_0O, true, "Contextual 0O O0 flips", + this->params()), + double_MEMBER(tessedit_lower_flip_hyphen, 1.5, + "Aspect ratio dot/hyphen test", this->params()), + double_MEMBER(tessedit_upper_flip_hyphen, 1.8, + "Aspect ratio dot/hyphen test", this->params()), + BOOL_MEMBER(rej_trust_doc_dawg, false, + "Use DOC dawg in 11l conf. detector", this->params()), + BOOL_MEMBER(rej_1Il_use_dict_word, false, "Use dictword test", + this->params()), + BOOL_MEMBER(rej_1Il_trust_permuter_type, true, "Don't double check", + this->params()), + BOOL_MEMBER(rej_use_tess_accepted, true, "Individual rejection control", + this->params()), + BOOL_MEMBER(rej_use_tess_blanks, true, "Individual rejection control", + this->params()), + BOOL_MEMBER(rej_use_good_perm, true, "Individual rejection control", + this->params()), + BOOL_MEMBER(rej_use_sensible_wd, false, "Extend permuter check", + this->params()), + BOOL_MEMBER(rej_alphas_in_number_perm, false, "Extend permuter check", + this->params()), + double_MEMBER(rej_whole_of_mostly_reject_word_fract, 0.85, + "if >this fract", this->params()), + INT_MEMBER(tessedit_image_border, 2, "Rej blbs near image edge limit", + this->params()), + STRING_MEMBER(ok_repeated_ch_non_alphanum_wds, "-?*\075", + "Allow NN to unrej", this->params()), + STRING_MEMBER(conflict_set_I_l_1, "Il1[]", "Il1 conflict set", + this->params()), + INT_MEMBER(min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this", + this->params()), + BOOL_MEMBER(tessedit_create_boxfile, false, "Output text with boxes", + this->params()), + INT_MEMBER(tessedit_page_number, -1, + "-1 -> All pages" + " , else specifc page to process", + this->params()), + BOOL_MEMBER(tessedit_write_images, false, + "Capture the image from the IPE", this->params()), + BOOL_MEMBER(interactive_display_mode, false, "Run interactively?", + this->params()), + STRING_MEMBER(file_type, ".tif", "Filename extension", this->params()), + BOOL_MEMBER(tessedit_override_permuter, true, "According to dict_word", + this->params()), + INT_MEMBER(tessdata_manager_debug_level, 0, + "Debug level for" + " TessdataManager functions.", + this->params()), + STRING_MEMBER(tessedit_load_sublangs, "", + "List of languages to load with this one", this->params()), + BOOL_MEMBER(tessedit_use_primary_params_model, false, + "In multilingual mode use params model of the" + " primary language", + this->params()), + double_MEMBER(min_orientation_margin, 7.0, + "Min acceptable orientation margin", this->params()), + BOOL_MEMBER(textord_tabfind_show_vlines, false, "Debug line finding", + this->params()), + BOOL_MEMBER(textord_use_cjk_fp_model, FALSE, "Use CJK fixed pitch model", + this->params()), + BOOL_MEMBER(poly_allow_detailed_fx, false, + "Allow feature extractors to see the original outline", + this->params()), + BOOL_INIT_MEMBER(tessedit_init_config_only, false, + "Only initialize with the config file. Useful if the " + "instance is not going to be used for OCR but say only " + "for layout analysis.", + this->params()), + BOOL_MEMBER(textord_equation_detect, false, "Turn on equation detector", + this->params()), + BOOL_MEMBER(textord_tabfind_vertical_text, true, + "Enable vertical detection", this->params()), + BOOL_MEMBER(textord_tabfind_force_vertical_text, false, + "Force using vertical text page mode", this->params()), + double_MEMBER( + textord_tabfind_vertical_text_ratio, 0.5, + "Fraction of textlines deemed vertical to use vertical page " + "mode", + this->params()), + double_MEMBER( + textord_tabfind_aligned_gap_fraction, 0.75, + "Fraction of height used as a minimum gap for aligned blobs.", + this->params()), + INT_MEMBER(tessedit_parallelize, 0, "Run in parallel where possible", + this->params()), + BOOL_MEMBER(preserve_interword_spaces, false, + "Preserve multiple interword spaces", this->params()), + BOOL_MEMBER(include_page_breaks, FALSE, + "Include page separator string in output text after each " + "image/page.", + this->params()), + STRING_MEMBER(page_separator, "\f", + "Page separator (default is form feed control character)", + this->params()), + + // The following parameters were deprecated and removed from their + // original + // locations. The parameters are temporarily kept here to give Tesseract + // users a chance to updated their [lang].traineddata and config files + // without introducing failures during Tesseract initialization. + // TODO(ocr-team): remove these parameters from the code once we are + // reasonably sure that Tesseract users have updated their data files. + // + // BEGIN DEPRECATED PARAMETERS + BOOL_MEMBER(textord_tabfind_vertical_horizontal_mix, true, + "find horizontal lines such as headers in vertical page mode", + this->params()), + INT_MEMBER(tessedit_ok_mode, 5, "Acceptance decision algorithm", + this->params()), + BOOL_INIT_MEMBER(load_fixed_length_dawgs, true, + "Load fixed length dawgs" + " (e.g. for non-space delimited languages)", + this->params()), + INT_MEMBER(segment_debug, 0, "Debug the whole segmentation process", + this->params()), + BOOL_MEMBER(permute_debug, 0, "Debug char permutation process", + this->params()), + double_MEMBER(bestrate_pruning_factor, 2.0, + "Multiplying factor of" + " current best rate to prune other hypotheses", + this->params()), + BOOL_MEMBER(permute_script_word, 0, + "Turn on word script consistency permuter", this->params()), + BOOL_MEMBER(segment_segcost_rating, 0, + "incorporate segmentation cost in word rating?", + this->params()), + double_MEMBER(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.", + this->params()), + BOOL_MEMBER(permute_fixed_length_dawg, 0, + "Turn on fixed-length phrasebook search permuter", + this->params()), + BOOL_MEMBER(permute_chartype_word, 0, + "Turn on character type (property) consistency permuter", + this->params()), + double_MEMBER(segment_reward_chartype, 0.97, + "Score multipler for char type consistency within a word. ", + this->params()), + double_MEMBER(segment_reward_ngram_best_choice, 0.99, + "Score multipler for ngram permuter's best choice" + " (only used in the Han script path).", + this->params()), + BOOL_MEMBER(ngram_permuter_activated, false, + "Activate character-level n-gram-based permuter", + this->params()), + BOOL_MEMBER(permute_only_top, false, "Run only the top choice permuter", + this->params()), + INT_MEMBER(language_model_fixed_length_choices_depth, 3, + "Depth of blob choice lists to explore" + " when fixed length dawgs are on", + this->params()), + BOOL_MEMBER(use_new_state_cost, FALSE, + "use new state cost heuristics for segmentation state" + " evaluation", + this->params()), + double_MEMBER(heuristic_segcost_rating_base, 1.25, + "base factor for adding segmentation cost into word rating." + "It's a multiplying factor, the larger the value above 1, " + "the bigger the effect of segmentation cost.", + this->params()), + double_MEMBER(heuristic_weight_rating, 1.0, + "weight associated with char rating in combined cost of" + "state", + this->params()), + double_MEMBER(heuristic_weight_width, 1000.0, + "weight associated with width evidence in combined cost of" + " state", + this->params()), + double_MEMBER(heuristic_weight_seamcut, 0.0, + "weight associated with seam cut in combined cost of state", + this->params()), + double_MEMBER(heuristic_max_char_wh_ratio, 2.0, + "max char width-to-height ratio allowed in segmentation", + this->params()), + BOOL_MEMBER(enable_new_segsearch, true, + "Enable new segmentation search path.", this->params()), + double_MEMBER(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, + "Maximum character width-to-height ratio for" + " fixed-pitch fonts", + this->params()), + // END DEPRECATED PARAMETERS + + backup_config_file_(NULL), + pix_binary_(NULL), + cube_binary_(NULL), + pix_grey_(NULL), + pix_original_(NULL), + pix_thresholds_(NULL), + source_resolution_(0), + textord_(this), + right_to_left_(false), + scaled_color_(NULL), + scaled_factor_(-1), + deskew_(1.0f, 0.0f), + reskew_(1.0f, 0.0f), + most_recently_used_(this), + font_table_size_(0), +#ifndef NO_CUBE_BUILD + cube_cntxt_(NULL), + tess_cube_combiner_(NULL), +#endif + equ_detect_(NULL) { + } + + Tesseract::~Tesseract() { + Clear(); + pixDestroy(&pix_original_); + end_tesseract(); + sub_langs_.delete_data_pointers(); +#ifndef NO_CUBE_BUILD + // Delete cube objects. + if (cube_cntxt_ != NULL) { + delete cube_cntxt_; + cube_cntxt_ = NULL; + } + if (tess_cube_combiner_ != NULL) { + delete tess_cube_combiner_; + tess_cube_combiner_ = NULL; + } +#endif + } + + void Tesseract::Clear() { + pixDestroy(&pix_binary_); + pixDestroy(&cube_binary_); + pixDestroy(&pix_grey_); + pixDestroy(&pix_thresholds_); + pixDestroy(&scaled_color_); + deskew_ = FCOORD(1.0f, 0.0f); + reskew_ = FCOORD(1.0f, 0.0f); + splitter_.Clear(); + scaled_factor_ = -1; + for (int i = 0; i < sub_langs_.size(); ++i) + sub_langs_[i]->Clear(); + } + + void Tesseract::SetEquationDetect(EquationDetect* detector) { + equ_detect_ = detector; + equ_detect_->SetLangTesseract(this); + } + + // Clear all memory of adaption for this and all subclassifiers. + void Tesseract::ResetAdaptiveClassifier() { + ResetAdaptiveClassifierInternal(); + for (int i = 0; i < sub_langs_.size(); ++i) { + sub_langs_[i]->ResetAdaptiveClassifierInternal(); + } + } + + // Clear the document dictionary for this and all subclassifiers. + void Tesseract::ResetDocumentDictionary() { + getDict().ResetDocumentDictionary(); + for (int i = 0; i < sub_langs_.size(); ++i) { + sub_langs_[i]->getDict().ResetDocumentDictionary(); + } + } + + void Tesseract::SetBlackAndWhitelist() { + // Set the white and blacklists (if any) + unicharset.set_black_and_whitelist(tessedit_char_blacklist.string(), + tessedit_char_whitelist.string(), + tessedit_char_unblacklist.string()); + // Black and white lists should apply to all loaded classifiers. + for (int i = 0; i < sub_langs_.size(); ++i) { + sub_langs_[i]->unicharset.set_black_and_whitelist( + tessedit_char_blacklist.string(), tessedit_char_whitelist.string(), + tessedit_char_unblacklist.string()); + } + } + + // Perform steps to prepare underlying binary image/other data structures for + // page segmentation. + void Tesseract::PrepareForPageseg() { + textord_.set_use_cjk_fp_model(textord_use_cjk_fp_model); + pixDestroy(&cube_binary_); + cube_binary_ = pixClone(pix_binary()); + // Find the max splitter strategy over all langs. + ShiroRekhaSplitter::SplitStrategy max_pageseg_strategy = + static_cast( + static_cast(pageseg_devanagari_split_strategy)); + for (int i = 0; i < sub_langs_.size(); ++i) { + ShiroRekhaSplitter::SplitStrategy pageseg_strategy = + static_cast( + static_cast(sub_langs_[i]->pageseg_devanagari_split_strategy)); + if (pageseg_strategy > max_pageseg_strategy) + max_pageseg_strategy = pageseg_strategy; + // Clone the cube image to all the sub langs too. + pixDestroy(&sub_langs_[i]->cube_binary_); + sub_langs_[i]->cube_binary_ = pixClone(pix_binary()); + pixDestroy(&sub_langs_[i]->pix_binary_); + sub_langs_[i]->pix_binary_ = pixClone(pix_binary()); + } + // Perform shiro-rekha (top-line) splitting and replace the current image by + // the newly splitted image. + splitter_.set_orig_pix(pix_binary()); + splitter_.set_pageseg_split_strategy(max_pageseg_strategy); + if (splitter_.Split(true)) { + ASSERT_HOST(splitter_.splitted_image()); + pixDestroy(&pix_binary_); + pix_binary_ = pixClone(splitter_.splitted_image()); + } + } + + // Perform steps to prepare underlying binary image/other data structures for + // OCR. The current segmentation is required by this method. + // Note that this method resets pix_binary_ to the original binarized image, + // which may be different from the image actually used for OCR depending on the + // value of devanagari_ocr_split_strategy. + void Tesseract::PrepareForTessOCR(BLOCK_LIST* block_list, + Tesseract* osd_tess, OSResults* osr) { + // Find the max splitter strategy over all langs. + ShiroRekhaSplitter::SplitStrategy max_ocr_strategy = + static_cast( + static_cast(ocr_devanagari_split_strategy)); + for (int i = 0; i < sub_langs_.size(); ++i) { + ShiroRekhaSplitter::SplitStrategy ocr_strategy = + static_cast( + static_cast(sub_langs_[i]->ocr_devanagari_split_strategy)); + if (ocr_strategy > max_ocr_strategy) + max_ocr_strategy = ocr_strategy; + } + // Utilize the segmentation information available. + splitter_.set_segmentation_block_list(block_list); + splitter_.set_ocr_split_strategy(max_ocr_strategy); + // Run the splitter for OCR + bool split_for_ocr = splitter_.Split(false); + // Restore pix_binary to the binarized original pix for future reference. + ASSERT_HOST(splitter_.orig_pix()); + pixDestroy(&pix_binary_); + pix_binary_ = pixClone(splitter_.orig_pix()); + // If the pageseg and ocr strategies are different, refresh the block list + // (from the last SegmentImage call) with blobs from the real image to be used + // for OCR. + if (splitter_.HasDifferentSplitStrategies()) { + BLOCK block("", TRUE, 0, 0, 0, 0, pixGetWidth(pix_binary_), + pixGetHeight(pix_binary_)); + Pix* pix_for_ocr = split_for_ocr ? splitter_.splitted_image() : + splitter_.orig_pix(); + extract_edges(pix_for_ocr, &block); + splitter_.RefreshSegmentationWithNewBlobs(block.blob_list()); + } + // The splitter isn't needed any more after this, so save memory by clearing. + splitter_.Clear(); + } + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.h b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.h new file mode 100644 index 0000000..3355d10 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tesseractclass.h @@ -0,0 +1,1220 @@ +/////////////////////////////////////////////////////////////////////// +// File: tesseractclass.h +// Description: The Tesseract class. It holds/owns everything needed +// to run Tesseract on a single language, and also a set of +// sub-Tesseracts to run sub-languages. For thread safety, *every* +// global variable goes in here, directly, or indirectly. +// This makes it safe to run multiple Tesseracts in different +// threads in parallel, and keeps the different language +// instances separate. +// Author: Ray Smith +// Created: Fri Mar 07 08:17: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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_TESSERACTCLASS_H__ +#define TESSERACT_CCMAIN_TESSERACTCLASS_H__ + +#include "allheaders.h" +#include "control.h" +#include "docqual.h" +#include "devanagari_processing.h" +#include "genericvector.h" +#include "params.h" +#include "ocrclass.h" +#include "textord.h" +#include "wordrec.h" + +class BLOB_CHOICE_LIST_CLIST; +class BLOCK_LIST; +struct OSResults; +class PAGE_RES; +class PAGE_RES_IT; +struct Pix; +class ROW; +class SVMenuNode; +class TBOX; +class TO_BLOCK_LIST; +class WERD; +class WERD_CHOICE; +class WERD_RES; + + +// Top-level class for all tesseract global instance data. +// This class either holds or points to all data used by an instance +// of Tesseract, including the memory allocator. When this is +// complete, Tesseract will be thread-safe. UNTIL THEN, IT IS NOT! +// +// NOTE to developers: Do not create cyclic dependencies through this class! +// The directory dependency tree must remain a tree! The keep this clean, +// lower-level code (eg in ccutil, the bottom level) must never need to +// know about the content of a higher-level directory. +// The following scheme will grant the easiest access to lower-level +// global members without creating a cyclic dependency: +// +// Class Hierarchy (^ = inheritance): +// +// CCUtil (ccutil/ccutil.h) +// ^ Members include: UNICHARSET +// CUtil (cutil/cutil_class.h) +// ^ Members include: TBLOB*, TEXTBLOCK* +// CCStruct (ccstruct/ccstruct.h) +// ^ Members include: Image +// Classify (classify/classify.h) +// ^ Members include: Dict +// WordRec (wordrec/wordrec.h) +// ^ Members include: WERD*, DENORM* +// Tesseract (ccmain/tesseractclass.h) +// Members include: Pix*, CubeRecoContext*, +// TesseractCubeCombiner* +// +// Other important classes: +// +// TessBaseAPI (api/baseapi.h) +// Members include: BLOCK_LIST*, PAGE_RES*, +// Tesseract*, ImageThresholder* +// Dict (dict/dict.h) +// Members include: Image* (private) +// +// NOTE: that each level contains members that correspond to global +// data that is defined (and used) at that level, not necessarily where +// the type is defined so for instance: +// BOOL_VAR_H(textord_show_blobs, false, "Display unsorted blobs"); +// goes inside the Textord class, not the cc_util class. + +namespace tesseract { + + class ColumnFinder; +#ifndef NO_CUBE_BUILD + class CharSamp; + class CubeLineObject; + class CubeObject; + class CubeRecoContext; +#endif + class EquationDetect; + class Tesseract; +#ifndef NO_CUBE_BUILD + class TesseractCubeCombiner; +#endif + + // A collection of various variables for statistics and debugging. + struct TesseractStats { + TesseractStats() + : adaption_word_number(0), + doc_blob_quality(0), + doc_outline_errs(0), + doc_char_quality(0), + good_char_count(0), + doc_good_char_quality(0), + word_count(0), + dict_words(0), + tilde_crunch_written(false), + last_char_was_newline(true), + last_char_was_tilde(false), + write_results_empty_block(true) {} + + inT32 adaption_word_number; + inT16 doc_blob_quality; + inT16 doc_outline_errs; + inT16 doc_char_quality; + inT16 good_char_count; + inT16 doc_good_char_quality; + inT32 word_count; // count of word in the document + inT32 dict_words; // number of dicitionary words in the document + STRING dump_words_str; // accumulator used by dump_words() + // Flags used by write_results() + bool tilde_crunch_written; + bool last_char_was_newline; + bool last_char_was_tilde; + bool write_results_empty_block; + }; + + // Struct to hold all the pointers to relevant data for processing a word. + struct WordData { + WordData() : word(NULL), row(NULL), block(NULL), prev_word(NULL) {} + explicit WordData(const PAGE_RES_IT& page_res_it) + : word(page_res_it.word()), row(page_res_it.row()->row), + block(page_res_it.block()->block), prev_word(NULL) {} + WordData(BLOCK* block_in, ROW* row_in, WERD_RES* word_res) + : word(word_res), row(row_in), block(block_in), prev_word(NULL) {} + + WERD_RES* word; + ROW* row; + BLOCK* block; + WordData* prev_word; + PointerVector lang_words; + }; + + // Definition of a Tesseract WordRecognizer. The WordData provides the context + // of row/block, in_word holds an initialized, possibly pre-classified word, + // that the recognizer may or may not consume (but if so it sets *in_word=NULL) + // and produces one or more output words in out_words, which may be the + // consumed in_word, or may be generated independently. + // This api allows both a conventional tesseract classifier to work, or a + // line-level classifier that generates multiple words from a merged input. + typedef void (Tesseract::*WordRecognizer)(const WordData& word_data, + WERD_RES** in_word, + PointerVector* out_words); + + class Tesseract : public Wordrec { + public: + Tesseract(); + ~Tesseract(); + + // Clear as much used memory as possible without resetting the adaptive + // classifier or losing any other classifier data. + void Clear(); + // Clear all memory of adaption for this and all subclassifiers. + void ResetAdaptiveClassifier(); + // Clear the document dictionary for this and all subclassifiers. + void ResetDocumentDictionary(); + + // Set the equation detector. + void SetEquationDetect(EquationDetect* detector); + + // Simple accessors. + const FCOORD& reskew() const { + return reskew_; + } + // Destroy any existing pix and return a pointer to the pointer. + Pix** mutable_pix_binary() { + pixDestroy(&pix_binary_); + return &pix_binary_; + } + Pix* pix_binary() const { + return pix_binary_; + } + Pix* pix_grey() const { + return pix_grey_; + } + void set_pix_grey(Pix* grey_pix) { + pixDestroy(&pix_grey_); + pix_grey_ = grey_pix; + } + Pix* pix_original() const { return pix_original_; } + // Takes ownership of the given original_pix. + void set_pix_original(Pix* original_pix) { + pixDestroy(&pix_original_); + pix_original_ = original_pix; + } + // Returns a pointer to a Pix representing the best available (original) image + // of the page. Can be of any bit depth, but never color-mapped, as that has + // always been dealt with. Note that in grey and color, 0 is black and 255 is + // white. If the input was binary, then black is 1 and white is 0. + // To tell the difference pixGetDepth() will return 32, 8 or 1. + // In any case, the return value is a borrowed Pix, and should not be + // deleted or pixDestroyed. + Pix* BestPix() const { return pix_original_; } + void set_pix_thresholds(Pix* thresholds) { + pixDestroy(&pix_thresholds_); + pix_thresholds_ = thresholds; + } + int source_resolution() const { + return source_resolution_; + } + void set_source_resolution(int ppi) { + source_resolution_ = ppi; + } + int ImageWidth() const { + return pixGetWidth(pix_binary_); + } + int ImageHeight() const { + return pixGetHeight(pix_binary_); + } + Pix* scaled_color() const { + return scaled_color_; + } + int scaled_factor() const { + return scaled_factor_; + } + void SetScaledColor(int factor, Pix* color) { + scaled_factor_ = factor; + scaled_color_ = color; + } + const Textord& textord() const { + return textord_; + } + Textord* mutable_textord() { + return &textord_; + } + + bool right_to_left() const { + return right_to_left_; + } + int num_sub_langs() const { + return sub_langs_.size(); + } + Tesseract* get_sub_lang(int index) const { + return sub_langs_[index]; + } + // Returns true if any language uses Tesseract (as opposed to cube). + bool AnyTessLang() const { + if (tessedit_ocr_engine_mode != OEM_CUBE_ONLY) return true; + for (int i = 0; i < sub_langs_.size(); ++i) { + if (sub_langs_[i]->tessedit_ocr_engine_mode != OEM_CUBE_ONLY) + return true; + } + return false; + } + + void SetBlackAndWhitelist(); + + // Perform steps to prepare underlying binary image/other data structures for + // page segmentation. Uses the strategy specified in the global variable + // pageseg_devanagari_split_strategy for perform splitting while preparing for + // page segmentation. + void PrepareForPageseg(); + + // Perform steps to prepare underlying binary image/other data structures for + // Tesseract OCR. The current segmentation is required by this method. + // Uses the strategy specified in the global variable + // ocr_devanagari_split_strategy for performing splitting while preparing for + // Tesseract ocr. + void PrepareForTessOCR(BLOCK_LIST* block_list, + Tesseract* osd_tess, OSResults* osr); + + int SegmentPage(const STRING* input_file, BLOCK_LIST* blocks, + Tesseract* osd_tess, OSResults* osr); + void SetupWordScripts(BLOCK_LIST* blocks); + int AutoPageSeg(PageSegMode pageseg_mode, BLOCK_LIST* blocks, + TO_BLOCK_LIST* to_blocks, BLOBNBOX_LIST* diacritic_blobs, + Tesseract* osd_tess, OSResults* osr); + ColumnFinder* SetupPageSegAndDetectOrientation( + PageSegMode pageseg_mode, BLOCK_LIST* blocks, Tesseract* osd_tess, + OSResults* osr, TO_BLOCK_LIST* to_blocks, Pix** photo_mask_pix, + Pix** music_mask_pix); + // par_control.cpp + void PrerecAllWordsPar(const GenericVector& words); + + //// control.h ///////////////////////////////////////////////////////// + bool ProcessTargetWord(const TBOX& word_box, const TBOX& target_word_box, + const char* word_config, int pass); + // Sets up the words ready for whichever engine is to be run + void SetupAllWordsPassN(int pass_n, + const TBOX* target_word_box, + const char* word_config, + PAGE_RES* page_res, + GenericVector* words); + // Sets up the single word ready for whichever engine is to be run. + void SetupWordPassN(int pass_n, WordData* word); + // Runs word recognition on all the words. + bool RecogAllWordsPassN(int pass_n, ETEXT_DESC* monitor, + PAGE_RES_IT* pr_it, + GenericVector* words); + bool recog_all_words(PAGE_RES* page_res, + ETEXT_DESC* monitor, + const TBOX* target_word_box, + const char* word_config, + int dopasses); + void rejection_passes(PAGE_RES* page_res, + ETEXT_DESC* monitor, + const TBOX* target_word_box, + const char* word_config); + void bigram_correction_pass(PAGE_RES *page_res); + void blamer_pass(PAGE_RES* page_res); + // Sets script positions and detects smallcaps on all output words. + void script_pos_pass(PAGE_RES* page_res); + // Helper to recognize the word using the given (language-specific) tesseract. + // Returns positive if this recognizer found more new best words than the + // number kept from best_words. + int RetryWithLanguage(const WordData& word_data, + WordRecognizer recognizer, + WERD_RES** in_word, + PointerVector* best_words); + // Moves good-looking "noise"/diacritics from the reject list to the main + // blob list on the current word. Returns true if anything was done, and + // sets make_next_word_fuzzy if blob(s) were added to the end of the word. + bool ReassignDiacritics(int pass, PAGE_RES_IT* pr_it, + bool* make_next_word_fuzzy); + // Attempts to put noise/diacritic outlines into the blobs that they overlap. + // Input: a set of noisy outlines that probably belong to the real_word. + // Output: outlines that overlapped blobs are set to NULL and put back into + // the word, either in the blobs or in the reject list. + void AssignDiacriticsToOverlappingBlobs( + const GenericVector& outlines, int pass, WERD* real_word, + PAGE_RES_IT* pr_it, GenericVector* word_wanted, + GenericVector* overlapped_any_blob, + GenericVector* target_blobs); + // Attempts to assign non-overlapping outlines to their nearest blobs or + // make new blobs out of them. + void AssignDiacriticsToNewBlobs(const GenericVector& outlines, + int pass, WERD* real_word, PAGE_RES_IT* pr_it, + GenericVector* word_wanted, + GenericVector* target_blobs); + // Starting with ok_outlines set to indicate which outlines overlap the blob, + // chooses the optimal set (approximately) and returns true if any outlines + // are desired, in which case ok_outlines indicates which ones. + bool SelectGoodDiacriticOutlines(int pass, float certainty_threshold, + PAGE_RES_IT* pr_it, C_BLOB* blob, + const GenericVector& outlines, + int num_outlines, + GenericVector* ok_outlines); + // Classifies the given blob plus the outlines flagged by ok_outlines, undoes + // the inclusion of the outlines, and returns the certainty of the raw choice. + float ClassifyBlobPlusOutlines(const GenericVector& ok_outlines, + const GenericVector& outlines, + int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, + STRING* best_str); + // Classifies the given blob (part of word_data->word->word) as an individual + // word, using languages, chopper etc, returning only the certainty of the + // best raw choice, and undoing all the work done to fake out the word. + float ClassifyBlobAsWord(int pass_n, PAGE_RES_IT* pr_it, C_BLOB* blob, + STRING* best_str, float* c2); + void classify_word_and_language(int pass_n, PAGE_RES_IT* pr_it, + WordData* word_data); + void classify_word_pass1(const WordData& word_data, + WERD_RES** in_word, + PointerVector* out_words); + void recog_pseudo_word(PAGE_RES* page_res, // blocks to check + TBOX &selection_box); + + void fix_rep_char(PAGE_RES_IT* page_res_it); + + ACCEPTABLE_WERD_TYPE acceptable_word_string(const UNICHARSET& char_set, + const char *s, + const char *lengths); + void match_word_pass_n(int pass_n, WERD_RES *word, ROW *row, BLOCK* block); + void classify_word_pass2(const WordData& word_data, + WERD_RES** in_word, + PointerVector* out_words); + void ReportXhtFixResult(bool accept_new_word, float new_x_ht, + WERD_RES* word, WERD_RES* new_word); + bool RunOldFixXht(WERD_RES *word, BLOCK* block, ROW *row); + bool TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row); + // Runs recognition with the test baseline shift and x-height and returns true + // if there was an improvement in recognition result. + bool TestNewNormalization(int original_misfits, float baseline_shift, + float new_x_ht, WERD_RES *word, BLOCK* block, + ROW *row); + BOOL8 recog_interactive(PAGE_RES_IT* pr_it); + + // Set fonts of this word. + void set_word_fonts(WERD_RES *word); + void font_recognition_pass(PAGE_RES* page_res); + void dictionary_correction_pass(PAGE_RES* page_res); + BOOL8 check_debug_pt(WERD_RES *word, int location); + + //// superscript.cpp //////////////////////////////////////////////////// + bool SubAndSuperscriptFix(WERD_RES *word_res); + void GetSubAndSuperscriptCandidates(const WERD_RES *word, + int *num_rebuilt_leading, + ScriptPos *leading_pos, + float *leading_certainty, + int *num_rebuilt_trailing, + ScriptPos *trailing_pos, + float *trailing_certainty, + float *avg_certainty, + float *unlikely_threshold); + WERD_RES *TrySuperscriptSplits(int num_chopped_leading, + float leading_certainty, + ScriptPos leading_pos, + int num_chopped_trailing, + float trailing_certainty, + ScriptPos trailing_pos, + WERD_RES *word, + bool *is_good, + int *retry_leading, + int *retry_trailing); + bool BelievableSuperscript(bool debug, + const WERD_RES &word, + float certainty_threshold, + int *left_ok, + int *right_ok) const; + + //// cube_control.cpp /////////////////////////////////////////////////// +#ifndef NO_CUBE_BUILD + bool init_cube_objects(bool load_combiner, + TessdataManager *tessdata_manager); + // Iterates through tesseract's results and calls cube on each word, + // combining the results with the existing tesseract result. + void run_cube_combiner(PAGE_RES *page_res); + // Recognizes a single word using (only) cube. Compatible with + // Tesseract's classify_word_pass1/classify_word_pass2. + void cube_word_pass1(BLOCK* block, ROW *row, WERD_RES *word); + // Cube recognizer to recognize a single word as with classify_word_pass1 + // but also returns the cube object in case the combiner is needed. + CubeObject* cube_recognize_word(BLOCK* block, WERD_RES* word); + // Combines the cube and tesseract results for a single word, leaving the + // result in tess_word. + void cube_combine_word(CubeObject* cube_obj, WERD_RES* cube_word, + WERD_RES* tess_word); + // Call cube on the current word, and write the result to word. + // Sets up a fake result and returns false if something goes wrong. + bool cube_recognize(CubeObject *cube_obj, BLOCK* block, WERD_RES *word); + void fill_werd_res(const BoxWord& cube_box_word, + const char* cube_best_str, + WERD_RES* tess_werd_res); + bool extract_cube_state(CubeObject* cube_obj, int* num_chars, + Boxa** char_boxes, CharSamp*** char_samples); + bool create_cube_box_word(Boxa *char_boxes, int num_chars, + TBOX word_box, BoxWord* box_word); +#endif + //// output.h ////////////////////////////////////////////////////////// + + void output_pass(PAGE_RES_IT &page_res_it, const TBOX *target_word_box); + void write_results(PAGE_RES_IT &page_res_it, // full info + char newline_type, // type of newline + BOOL8 force_eol // override tilde crunch? + ); + void set_unlv_suspects(WERD_RES *word); + UNICHAR_ID get_rep_char(WERD_RES *word); // what char is repeated? + BOOL8 acceptable_number_string(const char *s, + const char *lengths); + inT16 count_alphanums(const WERD_CHOICE &word); + inT16 count_alphas(const WERD_CHOICE &word); + //// tessedit.h //////////////////////////////////////////////////////// + void read_config_file(const char *filename, SetParamConstraint constraint); + // Initialize for potentially a set of languages defined by the language + // string and recursively any additional languages required by any language + // traineddata file (via tessedit_load_sublangs in its config) that is loaded. + // See init_tesseract_internal for args. + int init_tesseract(const char *arg0, + const char *textbase, + const char *language, + OcrEngineMode oem, + char **configs, + int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_init_params); + int init_tesseract(const char *datapath, + const char *language, + OcrEngineMode oem) { + return init_tesseract(datapath, NULL, language, oem, + NULL, 0, NULL, NULL, false); + } + // Common initialization for a single language. + // arg0 is the datapath for the tessdata directory, which could be the + // path of the tessdata directory with no trailing /, or (if tessdata + // lives in the same directory as the executable, the path of the executable, + // hence the name arg0. + // textbase is an optional output file basename (used only for training) + // language is the language code to load. + // oem controls which engine(s) will operate on the image + // configs (argv) is an array of config filenames to load variables from. + // May be NULL. + // configs_size (argc) is the number of elements in configs. + // vars_vec is an optional vector of variables to set. + // vars_values is an optional corresponding vector of values for the variables + // in vars_vec. + // If set_only_init_params is true, then only the initialization variables + // will be set. + int init_tesseract_internal(const char *arg0, + const char *textbase, + const char *language, + OcrEngineMode oem, + char **configs, + int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_init_params); + + // Set the universal_id member of each font to be unique among all + // instances of the same font loaded. + void SetupUniversalFontIds(); + + int init_tesseract_lm(const char *arg0, + const char *textbase, + const char *language); + + void recognize_page(STRING& image_name); + void end_tesseract(); + + bool init_tesseract_lang_data(const char *arg0, + const char *textbase, + const char *language, + OcrEngineMode oem, + char **configs, + int configs_size, + const GenericVector *vars_vec, + const GenericVector *vars_values, + bool set_only_init_params); + + void ParseLanguageString(const char* lang_str, + GenericVector* to_load, + GenericVector* not_to_load); + + //// pgedit.h ////////////////////////////////////////////////////////// + SVMenuNode *build_menu_new(); +#ifndef GRAPHICS_DISABLED + void pgeditor_main(int width, int height, PAGE_RES* page_res); +#endif // GRAPHICS_DISABLED + void process_image_event( // action in image win + const SVEvent &event); + BOOL8 process_cmd_win_event( // UI command semantics + inT32 cmd_event, // which menu item? + char *new_value // any prompt data + ); + void debug_word(PAGE_RES* page_res, const TBOX &selection_box); + void do_re_display( + BOOL8(tesseract::Tesseract::*word_painter)(PAGE_RES_IT* pr_it)); + BOOL8 word_display(PAGE_RES_IT* pr_it); + BOOL8 word_bln_display(PAGE_RES_IT* pr_it); + BOOL8 word_blank_and_set_display(PAGE_RES_IT* pr_its); + BOOL8 word_set_display(PAGE_RES_IT* pr_it); + // #ifndef GRAPHICS_DISABLED + BOOL8 word_dumper(PAGE_RES_IT* pr_it); + // #endif // GRAPHICS_DISABLED + void blob_feature_display(PAGE_RES* page_res, const TBOX& selection_box); + //// reject.h ////////////////////////////////////////////////////////// + // make rej map for word + void make_reject_map(WERD_RES *word, ROW *row, inT16 pass); + BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map); + inT16 first_alphanum_index(const char *word, + const char *word_lengths); + inT16 first_alphanum_offset(const char *word, + const char *word_lengths); + inT16 alpha_count(const char *word, + const char *word_lengths); + BOOL8 word_contains_non_1_digit(const char *word, + const char *word_lengths); + void dont_allow_1Il(WERD_RES *word); + inT16 count_alphanums( //how many alphanums + WERD_RES *word); + void flip_0O(WERD_RES *word); + BOOL8 non_0_digit(const UNICHARSET& ch_set, UNICHAR_ID unichar_id); + BOOL8 non_O_upper(const UNICHARSET& ch_set, UNICHAR_ID unichar_id); + BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row); + void nn_match_word( //Match a word + WERD_RES *word, + ROW *row); + void nn_recover_rejects(WERD_RES *word, ROW *row); + void set_done( //set done flag + WERD_RES *word, + inT16 pass); + inT16 safe_dict_word(const WERD_RES *werd_res); // is best_choice in dict? + void flip_hyphens(WERD_RES *word); + void reject_I_1_L(WERD_RES *word); + void reject_edge_blobs(WERD_RES *word); + void reject_mostly_rejects(WERD_RES *word); + //// adaptions.h /////////////////////////////////////////////////////// + BOOL8 word_adaptable( //should we adapt? + WERD_RES *word, + uinT16 mode); + + //// tfacepp.cpp /////////////////////////////////////////////////////// + void recog_word_recursive(WERD_RES* word); + void recog_word(WERD_RES *word); + void split_and_recog_word(WERD_RES* word); + void split_word(WERD_RES *word, + int split_pt, + WERD_RES **right_piece, + BlamerBundle **orig_blamer_bundle) const; + void join_words(WERD_RES *word, + WERD_RES *word2, + BlamerBundle *orig_bb) const; + //// fixspace.cpp /////////////////////////////////////////////////////// + BOOL8 digit_or_numeric_punct(WERD_RES *word, int char_position); + inT16 eval_word_spacing(WERD_RES_LIST &word_res_list); + void match_current_words(WERD_RES_LIST &words, ROW *row, BLOCK* block); + inT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list); + void fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block); + void fix_fuzzy_space_list(WERD_RES_LIST &best_perm, ROW *row, BLOCK* block); + void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row, BLOCK* block); + void fix_fuzzy_spaces( //find fuzzy words + ETEXT_DESC *monitor, //progress monitor + inT32 word_count, //count of words in doc + PAGE_RES *page_res); + void dump_words(WERD_RES_LIST &perm, inT16 score, + inT16 mode, BOOL8 improved); + BOOL8 fixspace_thinks_word_done(WERD_RES *word); + inT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score); + float blob_noise_score(TBLOB *blob); + void break_noisiest_blob_word(WERD_RES_LIST &words); + //// docqual.cpp //////////////////////////////////////////////////////// + GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word); + BOOL8 potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word); + void tilde_crunch(PAGE_RES_IT &page_res_it); + void unrej_good_quality_words( //unreject potential + PAGE_RES_IT &page_res_it); + void doc_and_block_rejection( //reject big chunks + PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc); + void quality_based_rejection(PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc); + void convert_bad_unlv_chs(WERD_RES *word_res); + void tilde_delete(PAGE_RES_IT &page_res_it); + inT16 word_blob_quality(WERD_RES *word, ROW *row); + void word_char_quality(WERD_RES *word, ROW *row, inT16 *match_count, + inT16 *accepted_match_count); + void unrej_good_chs(WERD_RES *word, ROW *row); + inT16 count_outline_errs(char c, inT16 outline_count); + inT16 word_outline_errs(WERD_RES *word); + BOOL8 terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level); + CRUNCH_MODE word_deletable(WERD_RES *word, inT16 &delete_mode); + inT16 failure_count(WERD_RES *word); + BOOL8 noise_outlines(TWERD *word); + //// pagewalk.cpp /////////////////////////////////////////////////////// + void + process_selected_words( + PAGE_RES* page_res, // blocks to check + //function to call + TBOX & selection_box, + BOOL8(tesseract::Tesseract::*word_processor)(PAGE_RES_IT* pr_it)); + //// tessbox.cpp /////////////////////////////////////////////////////// + void tess_add_doc_word( //test acceptability + WERD_CHOICE *word_choice //after context + ); + void tess_segment_pass_n(int pass_n, WERD_RES *word); + bool tess_acceptable_word(WERD_RES *word); + + //// applybox.cpp ////////////////////////////////////////////////////// + // Applies the box file based on the image name fname, and resegments + // the words in the block_list (page), with: + // blob-mode: one blob per line in the box file, words as input. + // word/line-mode: one blob per space-delimited unit after the #, and one word + // per line in the box file. (See comment above for box file format.) + // If find_segmentation is true, (word/line mode) then the classifier is used + // to re-segment words/lines to match the space-delimited truth string for + // each box. In this case, the input box may be for a word or even a whole + // text line, and the output words will contain multiple blobs corresponding + // to the space-delimited input string. + // With find_segmentation false, no classifier is needed, but the chopper + // can still be used to correctly segment touching characters with the help + // of the input boxes. + // In the returned PAGE_RES, the WERD_RES are setup as they would be returned + // from normal classification, ie. with a word, chopped_word, rebuild_word, + // seam_array, denorm, box_word, and best_state, but NO best_choice or + // raw_choice, as they would require a UNICHARSET, which we aim to avoid. + // Instead, the correct_text member of WERD_RES is set, and this may be later + // converted to a best_choice using CorrectClassifyWords. CorrectClassifyWords + // is not required before calling ApplyBoxTraining. + PAGE_RES* ApplyBoxes(const STRING& fname, bool find_segmentation, + BLOCK_LIST *block_list); + + // Any row xheight that is significantly different from the median is set + // to the median. + void PreenXHeights(BLOCK_LIST *block_list); + + // Builds a PAGE_RES from the block_list in the way required for ApplyBoxes: + // All fuzzy spaces are removed, and all the words are maximally chopped. + PAGE_RES* SetupApplyBoxes(const GenericVector& boxes, + BLOCK_LIST *block_list); + // Tests the chopper by exhaustively running chop_one_blob. + // The word_res will contain filled chopped_word, seam_array, denorm, + // box_word and best_state for the maximally chopped word. + void MaximallyChopWord(const GenericVector& boxes, + BLOCK* block, ROW* row, WERD_RES* word_res); + // Gather consecutive blobs that match the given box into the best_state + // and corresponding correct_text. + // Fights over which box owns which blobs are settled by pre-chopping and + // applying the blobs to box or next_box with the least non-overlap. + // Returns false if the box was in error, which can only be caused by + // failing to find an appropriate blob for a box. + // This means that occasionally, blobs may be incorrectly segmented if the + // chopper fails to find a suitable chop point. + bool ResegmentCharBox(PAGE_RES* page_res, const TBOX *prev_box, + const TBOX& box, const TBOX& next_box, + const char* correct_text); + // Consume all source blobs that strongly overlap the given box, + // putting them into a new word, with the correct_text label. + // Fights over which box owns which blobs are settled by + // applying the blobs to box or next_box with the least non-overlap. + // Returns false if the box was in error, which can only be caused by + // failing to find an overlapping blob for a box. + bool ResegmentWordBox(BLOCK_LIST *block_list, + const TBOX& box, const TBOX& next_box, + const char* correct_text); + // Resegments the words by running the classifier in an attempt to find the + // correct segmentation that produces the required string. + void ReSegmentByClassification(PAGE_RES* page_res); + // Converts the space-delimited string of utf8 text to a vector of UNICHAR_ID. + // Returns false if an invalid UNICHAR_ID is encountered. + bool ConvertStringToUnichars(const char* utf8, + GenericVector* class_ids); + // Resegments the word to achieve the target_text from the classifier. + // Returns false if the re-segmentation fails. + // Uses brute-force combination of up to kMaxGroupSize adjacent blobs, and + // applies a full search on the classifier results to find the best classified + // segmentation. As a compromise to obtain better recall, 1-1 ambigiguity + // substitutions ARE used. + bool FindSegmentation(const GenericVector& target_text, + WERD_RES* word_res); + // Recursive helper to find a match to the target_text (from text_index + // position) in the choices (from choices_pos position). + // Choices is an array of GenericVectors, of length choices_length, with each + // element representing a starting position in the word, and the + // GenericVector holding classification results for a sequence of consecutive + // blobs, with index 0 being a single blob, index 1 being 2 blobs etc. + void SearchForText(const GenericVector* choices, + int choices_pos, int choices_length, + const GenericVector& target_text, + int text_index, + float rating, GenericVector* segmentation, + float* best_rating, GenericVector* best_segmentation); + // Counts up the labelled words and the blobs within. + // Deletes all unused or emptied words, counting the unused ones. + // Resets W_BOL and W_EOL flags correctly. + // Builds the rebuild_word and rebuilds the box_word. + void TidyUp(PAGE_RES* page_res); + // Logs a bad box by line in the box file and box coords. + void ReportFailedBox(int boxfile_lineno, TBOX box, const char *box_ch, + const char *err_msg); + // Creates a fake best_choice entry in each WERD_RES with the correct text. + void CorrectClassifyWords(PAGE_RES* page_res); + // Call LearnWord to extract features for labelled blobs within each word. + // Features are stored in an internal buffer. + void ApplyBoxTraining(const STRING& fontname, PAGE_RES* page_res); + + //// fixxht.cpp /////////////////////////////////////////////////////// + // Returns the number of misfit blob tops in this word. + int CountMisfitTops(WERD_RES *word_res); + // Returns a new x-height in pixels (original image coords) that is + // maximally compatible with the result in word_res. + // Returns 0.0f if no x-height is found that is better than the current + // estimate. + float ComputeCompatibleXheight(WERD_RES *word_res, float* baseline_shift); + //// Data members /////////////////////////////////////////////////////// + // TODO(ocr-team): Find and remove obsolete parameters. + BOOL_VAR_H(tessedit_resegment_from_boxes, false, + "Take segmentation and labeling from box file"); + BOOL_VAR_H(tessedit_resegment_from_line_boxes, false, + "Conversion of word/line box file to char box file"); + BOOL_VAR_H(tessedit_train_from_boxes, false, + "Generate training data from boxed chars"); + BOOL_VAR_H(tessedit_make_boxes_from_boxes, false, + "Generate more boxes from boxed chars"); + BOOL_VAR_H(tessedit_dump_pageseg_images, false, + "Dump intermediate images made during page segmentation"); + INT_VAR_H(tessedit_pageseg_mode, PSM_SINGLE_BLOCK, + "Page seg mode: 0=osd only, 1=auto+osd, 2=auto, 3=col, 4=block," + " 5=line, 6=word, 7=char" + " (Values from PageSegMode enum in publictypes.h)"); + INT_VAR_H(tessedit_ocr_engine_mode, tesseract::OEM_TESSERACT_ONLY, + "Which OCR engine(s) to run (Tesseract, Cube, both). Defaults" + " to loading and running only Tesseract (no Cube, no combiner)." + " (Values from OcrEngineMode enum in tesseractclass.h)"); + STRING_VAR_H(tessedit_char_blacklist, "", + "Blacklist of chars not to recognize"); + STRING_VAR_H(tessedit_char_whitelist, "", + "Whitelist of chars to recognize"); + STRING_VAR_H(tessedit_char_unblacklist, "", + "List of chars to override tessedit_char_blacklist"); + BOOL_VAR_H(tessedit_ambigs_training, false, + "Perform training for ambiguities"); + INT_VAR_H(pageseg_devanagari_split_strategy, + tesseract::ShiroRekhaSplitter::NO_SPLIT, + "Whether to use the top-line splitting process for Devanagari " + "documents while performing page-segmentation."); + INT_VAR_H(ocr_devanagari_split_strategy, + tesseract::ShiroRekhaSplitter::NO_SPLIT, + "Whether to use the top-line splitting process for Devanagari " + "documents while performing ocr."); + STRING_VAR_H(tessedit_write_params_to_file, "", + "Write all parameters to the given file."); + BOOL_VAR_H(tessedit_adaption_debug, false, + "Generate and print debug information for adaption"); + INT_VAR_H(bidi_debug, 0, "Debug level for BiDi"); + INT_VAR_H(applybox_debug, 1, "Debug level"); + INT_VAR_H(applybox_page, 0, "Page number to apply boxes from"); + STRING_VAR_H(applybox_exposure_pattern, ".exp", + "Exposure value follows this pattern in the image" + " filename. The name of the image files are expected" + " to be in the form [lang].[fontname].exp[num].tif"); + BOOL_VAR_H(applybox_learn_chars_and_char_frags_mode, false, + "Learn both character fragments (as is done in the" + " special low exposure mode) as well as unfragmented" + " characters."); + BOOL_VAR_H(applybox_learn_ngrams_mode, false, + "Each bounding box is assumed to contain ngrams. Only" + " learn the ngrams whose outlines overlap horizontally."); + BOOL_VAR_H(tessedit_display_outwords, false, "Draw output words"); + BOOL_VAR_H(tessedit_dump_choices, false, "Dump char choices"); + BOOL_VAR_H(tessedit_timing_debug, false, "Print timing stats"); + BOOL_VAR_H(tessedit_fix_fuzzy_spaces, true, + "Try to improve fuzzy spaces"); + BOOL_VAR_H(tessedit_unrej_any_wd, false, + "Don't bother with word plausibility"); + BOOL_VAR_H(tessedit_fix_hyphens, true, "Crunch double hyphens?"); + BOOL_VAR_H(tessedit_redo_xheight, true, "Check/Correct x-height"); + BOOL_VAR_H(tessedit_enable_doc_dict, true, + "Add words to the document dictionary"); + BOOL_VAR_H(tessedit_debug_fonts, false, "Output font info per char"); + BOOL_VAR_H(tessedit_debug_block_rejection, false, "Block and Row stats"); + BOOL_VAR_H(tessedit_enable_bigram_correction, true, + "Enable correction based on the word bigram dictionary."); + BOOL_VAR_H(tessedit_enable_dict_correction, false, + "Enable single word correction based on the dictionary."); + INT_VAR_H(tessedit_bigram_debug, 0, "Amount of debug output for bigram " + "correction."); + BOOL_VAR_H(enable_noise_removal, true, + "Remove and conditionally reassign small outlines when they" + " confuse layout analysis, determining diacritics vs noise"); + INT_VAR_H(debug_noise_removal, 0, "Debug reassignment of small outlines"); + // Worst (min) certainty, for which a diacritic is allowed to make the base + // character worse and still be included. + double_VAR_H(noise_cert_basechar, -8.0, "Hingepoint for base char certainty"); + // Worst (min) certainty, for which a non-overlapping diacritic is allowed to + // make the base character worse and still be included. + double_VAR_H(noise_cert_disjoint, -2.5, "Hingepoint for disjoint certainty"); + // Worst (min) certainty, for which a diacritic is allowed to make a new + // stand-alone blob. + double_VAR_H(noise_cert_punc, -2.5, "Threshold for new punc char certainty"); + // Factor of certainty margin for adding diacritics to not count as worse. + double_VAR_H(noise_cert_factor, 0.375, + "Scaling on certainty diff from Hingepoint"); + INT_VAR_H(noise_maxperblob, 8, "Max diacritics to apply to a blob"); + INT_VAR_H(noise_maxperword, 16, "Max diacritics to apply to a word"); + INT_VAR_H(debug_x_ht_level, 0, "Reestimate debug"); + BOOL_VAR_H(debug_acceptable_wds, false, "Dump word pass/fail chk"); + STRING_VAR_H(chs_leading_punct, "('`\"", "Leading punctuation"); + STRING_VAR_H(chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); + STRING_VAR_H(chs_trailing_punct2, ")'`\"", "2nd Trailing punctuation"); + double_VAR_H(quality_rej_pc, 0.08, "good_quality_doc lte rejection limit"); + double_VAR_H(quality_blob_pc, 0.0, "good_quality_doc gte good blobs limit"); + double_VAR_H(quality_outline_pc, 1.0, + "good_quality_doc lte outline error limit"); + double_VAR_H(quality_char_pc, 0.95, "good_quality_doc gte good char limit"); + INT_VAR_H(quality_min_initial_alphas_reqd, 2, "alphas in a good word"); + INT_VAR_H(tessedit_tess_adaption_mode, 0x27, + "Adaptation decision algorithm for tess"); + BOOL_VAR_H(tessedit_minimal_rej_pass1, false, + "Do minimal rejection on pass 1 output"); + BOOL_VAR_H(tessedit_test_adaption, false, "Test adaption criteria"); + BOOL_VAR_H(tessedit_matcher_log, false, "Log matcher activity"); + INT_VAR_H(tessedit_test_adaption_mode, 3, + "Adaptation decision algorithm for tess"); + BOOL_VAR_H(test_pt, false, "Test for point"); + double_VAR_H(test_pt_x, 99999.99, "xcoord"); + double_VAR_H(test_pt_y, 99999.99, "ycoord"); + INT_VAR_H(paragraph_debug_level, 0, "Print paragraph debug info."); + BOOL_VAR_H(paragraph_text_based, true, + "Run paragraph detection on the post-text-recognition " + "(more accurate)"); + INT_VAR_H(cube_debug_level, 1, "Print cube debug info."); + STRING_VAR_H(outlines_odd, "%| ", "Non standard number of outlines"); + STRING_VAR_H(outlines_2, "ij!?%\":;", "Non standard number of outlines"); + BOOL_VAR_H(docqual_excuse_outline_errs, false, + "Allow outline errs in unrejection?"); + BOOL_VAR_H(tessedit_good_quality_unrej, true, + "Reduce rejection on good docs"); + BOOL_VAR_H(tessedit_use_reject_spaces, true, "Reject spaces?"); + double_VAR_H(tessedit_reject_doc_percent, 65.00, + "%rej allowed before rej whole doc"); + double_VAR_H(tessedit_reject_block_percent, 45.00, + "%rej allowed before rej whole block"); + double_VAR_H(tessedit_reject_row_percent, 40.00, + "%rej allowed before rej whole row"); + double_VAR_H(tessedit_whole_wd_rej_row_percent, 70.00, + "Number of row rejects in whole word rejects" + "which prevents whole row rejection"); + BOOL_VAR_H(tessedit_preserve_blk_rej_perfect_wds, true, + "Only rej partially rejected words in block rejection"); + BOOL_VAR_H(tessedit_preserve_row_rej_perfect_wds, true, + "Only rej partially rejected words in row rejection"); + BOOL_VAR_H(tessedit_dont_blkrej_good_wds, false, + "Use word segmentation quality metric"); + BOOL_VAR_H(tessedit_dont_rowrej_good_wds, false, + "Use word segmentation quality metric"); + INT_VAR_H(tessedit_preserve_min_wd_len, 2, + "Only preserve wds longer than this"); + BOOL_VAR_H(tessedit_row_rej_good_docs, true, + "Apply row rejection to good docs"); + double_VAR_H(tessedit_good_doc_still_rowrej_wd, 1.1, + "rej good doc wd if more than this fraction rejected"); + BOOL_VAR_H(tessedit_reject_bad_qual_wds, true, + "Reject all bad quality wds"); + BOOL_VAR_H(tessedit_debug_doc_rejection, false, "Page stats"); + BOOL_VAR_H(tessedit_debug_quality_metrics, false, + "Output data to debug file"); + BOOL_VAR_H(bland_unrej, false, "unrej potential with no chekcs"); + double_VAR_H(quality_rowrej_pc, 1.1, + "good_quality_doc gte good char limit"); + BOOL_VAR_H(unlv_tilde_crunching, true, + "Mark v.bad words for tilde crunch"); + BOOL_VAR_H(hocr_font_info, false, + "Add font info to hocr output"); + BOOL_VAR_H(crunch_early_merge_tess_fails, true, "Before word crunch?"); + BOOL_VAR_H(crunch_early_convert_bad_unlv_chs, false, "Take out ~^ early?"); + double_VAR_H(crunch_terrible_rating, 80.0, "crunch rating lt this"); + BOOL_VAR_H(crunch_terrible_garbage, true, "As it says"); + double_VAR_H(crunch_poor_garbage_cert, -9.0, + "crunch garbage cert lt this"); + double_VAR_H(crunch_poor_garbage_rate, 60, "crunch garbage rating lt this"); + double_VAR_H(crunch_pot_poor_rate, 40, "POTENTIAL crunch rating lt this"); + double_VAR_H(crunch_pot_poor_cert, -8.0, "POTENTIAL crunch cert lt this"); + BOOL_VAR_H(crunch_pot_garbage, true, "POTENTIAL crunch garbage"); + double_VAR_H(crunch_del_rating, 60, "POTENTIAL crunch rating lt this"); + double_VAR_H(crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); + double_VAR_H(crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); + double_VAR_H(crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); + double_VAR_H(crunch_del_min_width, 3.0, "Del if word width lt xht x this"); + double_VAR_H(crunch_del_high_word, 1.5, + "Del if word gt xht x this above bl"); + double_VAR_H(crunch_del_low_word, 0.5, "Del if word gt xht x this below bl"); + double_VAR_H(crunch_small_outlines_size, 0.6, "Small if lt xht x this"); + INT_VAR_H(crunch_rating_max, 10, "For adj length in rating per ch"); + INT_VAR_H(crunch_pot_indicators, 1, "How many potential indicators needed"); + BOOL_VAR_H(crunch_leave_ok_strings, true, "Don't touch sensible strings"); + BOOL_VAR_H(crunch_accept_ok, true, "Use acceptability in okstring"); + BOOL_VAR_H(crunch_leave_accept_strings, false, + "Don't pot crunch sensible strings"); + BOOL_VAR_H(crunch_include_numerals, false, "Fiddle alpha figures"); + INT_VAR_H(crunch_leave_lc_strings, 4, + "Don't crunch words with long lower case strings"); + INT_VAR_H(crunch_leave_uc_strings, 4, + "Don't crunch words with long lower case strings"); + INT_VAR_H(crunch_long_repetitions, 3, "Crunch words with long repetitions"); + INT_VAR_H(crunch_debug, 0, "As it says"); + INT_VAR_H(fixsp_non_noise_limit, 1, + "How many non-noise blbs either side?"); + double_VAR_H(fixsp_small_outlines_size, 0.28, "Small if lt xht x this"); + BOOL_VAR_H(tessedit_prefer_joined_punct, false, "Reward punctation joins"); + INT_VAR_H(fixsp_done_mode, 1, "What constitues done for spacing"); + INT_VAR_H(debug_fix_space_level, 0, "Contextual fixspace debug"); + STRING_VAR_H(numeric_punctuation, ".,", + "Punct. chs expected WITHIN numbers"); + INT_VAR_H(x_ht_acceptance_tolerance, 8, + "Max allowed deviation of blob top outside of font data"); + INT_VAR_H(x_ht_min_change, 8, "Min change in xht before actually trying it"); + INT_VAR_H(superscript_debug, 0, "Debug level for sub & superscript fixer"); + double_VAR_H(superscript_worse_certainty, 2.0, "How many times worse " + "certainty does a superscript position glyph need to be for us " + "to try classifying it as a char with a different baseline?"); + double_VAR_H(superscript_bettered_certainty, 0.97, "What reduction in " + "badness do we think sufficient to choose a superscript over " + "what we'd thought. For example, a value of 0.6 means we want " + "to reduce badness of certainty by 40%"); + double_VAR_H(superscript_scaledown_ratio, 0.4, + "A superscript scaled down more than this is unbelievably " + "small. For example, 0.3 means we expect the font size to " + "be no smaller than 30% of the text line font size."); + double_VAR_H(subscript_max_y_top, 0.5, + "Maximum top of a character measured as a multiple of x-height " + "above the baseline for us to reconsider whether it's a " + "subscript."); + double_VAR_H(superscript_min_y_bottom, 0.3, + "Minimum bottom of a character measured as a multiple of " + "x-height above the baseline for us to reconsider whether it's " + "a superscript."); + BOOL_VAR_H(tessedit_write_block_separators, false, + "Write block separators in output"); + BOOL_VAR_H(tessedit_write_rep_codes, false, + "Write repetition char code"); + BOOL_VAR_H(tessedit_write_unlv, false, "Write .unlv output file"); + BOOL_VAR_H(tessedit_create_txt, false, "Write .txt output file"); + BOOL_VAR_H(tessedit_create_hocr, false, "Write .html hOCR output file"); + BOOL_VAR_H(tessedit_create_tsv, false, "Write .tsv output file"); + BOOL_VAR_H(tessedit_create_pdf, false, "Write .pdf output file"); + BOOL_VAR_H(textonly_pdf, false, "Create PDF with only one invisible text layer"); + STRING_VAR_H(unrecognised_char, "|", + "Output char for unidentified blobs"); + INT_VAR_H(suspect_level, 99, "Suspect marker level"); + INT_VAR_H(suspect_space_level, 100, + "Min suspect level for rejecting spaces"); + INT_VAR_H(suspect_short_words, 2, "Don't Suspect dict wds longer than this"); + BOOL_VAR_H(suspect_constrain_1Il, false, "UNLV keep 1Il chars rejected"); + double_VAR_H(suspect_rating_per_ch, 999.9, "Don't touch bad rating limit"); + double_VAR_H(suspect_accept_rating, -999.9, "Accept good rating limit"); + BOOL_VAR_H(tessedit_minimal_rejection, false, "Only reject tess failures"); + BOOL_VAR_H(tessedit_zero_rejection, false, "Don't reject ANYTHING"); + BOOL_VAR_H(tessedit_word_for_word, false, + "Make output have exactly one word per WERD"); + BOOL_VAR_H(tessedit_zero_kelvin_rejection, false, + "Don't reject ANYTHING AT ALL"); + BOOL_VAR_H(tessedit_consistent_reps, true, "Force all rep chars the same"); + INT_VAR_H(tessedit_reject_mode, 0, "Rejection algorithm"); + BOOL_VAR_H(tessedit_rejection_debug, false, "Adaption debug"); + BOOL_VAR_H(tessedit_flip_0O, true, "Contextual 0O O0 flips"); + double_VAR_H(tessedit_lower_flip_hyphen, 1.5, + "Aspect ratio dot/hyphen test"); + double_VAR_H(tessedit_upper_flip_hyphen, 1.8, + "Aspect ratio dot/hyphen test"); + BOOL_VAR_H(rej_trust_doc_dawg, false, "Use DOC dawg in 11l conf. detector"); + BOOL_VAR_H(rej_1Il_use_dict_word, false, "Use dictword test"); + BOOL_VAR_H(rej_1Il_trust_permuter_type, true, "Don't double check"); + BOOL_VAR_H(rej_use_tess_accepted, true, "Individual rejection control"); + BOOL_VAR_H(rej_use_tess_blanks, true, "Individual rejection control"); + BOOL_VAR_H(rej_use_good_perm, true, "Individual rejection control"); + BOOL_VAR_H(rej_use_sensible_wd, false, "Extend permuter check"); + BOOL_VAR_H(rej_alphas_in_number_perm, false, "Extend permuter check"); + double_VAR_H(rej_whole_of_mostly_reject_word_fract, 0.85, "if >this fract"); + INT_VAR_H(tessedit_image_border, 2, "Rej blbs near image edge limit"); + STRING_VAR_H(ok_repeated_ch_non_alphanum_wds, "-?*\075", + "Allow NN to unrej"); + STRING_VAR_H(conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); + INT_VAR_H(min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this"); + BOOL_VAR_H(tessedit_create_boxfile, false, "Output text with boxes"); + INT_VAR_H(tessedit_page_number, -1, + "-1 -> All pages, else specifc page to process"); + BOOL_VAR_H(tessedit_write_images, false, "Capture the image from the IPE"); + BOOL_VAR_H(interactive_display_mode, false, "Run interactively?"); + STRING_VAR_H(file_type, ".tif", "Filename extension"); + BOOL_VAR_H(tessedit_override_permuter, true, "According to dict_word"); + INT_VAR_H(tessdata_manager_debug_level, 0, + "Debug level for TessdataManager functions."); + STRING_VAR_H(tessedit_load_sublangs, "", + "List of languages to load with this one"); + BOOL_VAR_H(tessedit_use_primary_params_model, false, + "In multilingual mode use params model of the primary language"); + // Min acceptable orientation margin (difference in scores between top and 2nd + // choice in OSResults::orientations) to believe the page orientation. + double_VAR_H(min_orientation_margin, 7.0, + "Min acceptable orientation margin"); + BOOL_VAR_H(textord_tabfind_show_vlines, false, "Debug line finding"); + BOOL_VAR_H(textord_use_cjk_fp_model, FALSE, "Use CJK fixed pitch model"); + BOOL_VAR_H(poly_allow_detailed_fx, false, + "Allow feature extractors to see the original outline"); + BOOL_VAR_H(tessedit_init_config_only, false, + "Only initialize with the config file. Useful if the instance is " + "not going to be used for OCR but say only for layout analysis."); + BOOL_VAR_H(textord_equation_detect, false, "Turn on equation detector"); + BOOL_VAR_H(textord_tabfind_vertical_text, true, "Enable vertical detection"); + BOOL_VAR_H(textord_tabfind_force_vertical_text, false, + "Force using vertical text page mode"); + double_VAR_H(textord_tabfind_vertical_text_ratio, 0.5, + "Fraction of textlines deemed vertical to use vertical page " + "mode"); + double_VAR_H(textord_tabfind_aligned_gap_fraction, 0.75, + "Fraction of height used as a minimum gap for aligned blobs."); + INT_VAR_H(tessedit_parallelize, 0, "Run in parallel where possible"); + BOOL_VAR_H(preserve_interword_spaces, false, + "Preserve multiple interword spaces"); + BOOL_VAR_H(include_page_breaks, false, + "Include page separator string in output text after each " + "image/page."); + STRING_VAR_H(page_separator, "\f", + "Page separator (default is form feed control character)"); + + // The following parameters were deprecated and removed from their original + // locations. The parameters are temporarily kept here to give Tesseract + // users a chance to updated their [lang].traineddata and config files + // without introducing failures during Tesseract initialization. + // TODO(ocr-team): remove these parameters from the code once we are + // reasonably sure that Tesseract users have updated their data files. + // + // BEGIN DEPRECATED PARAMETERS + BOOL_VAR_H(textord_tabfind_vertical_horizontal_mix, true, + "find horizontal lines such as headers in vertical page mode"); + INT_VAR_H(tessedit_ok_mode, 5, "Acceptance decision algorithm"); + BOOL_VAR_H(load_fixed_length_dawgs, true, "Load fixed length" + " dawgs (e.g. for non-space delimited languages)"); + INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process"); + BOOL_VAR_H(permute_debug, 0, "char permutation debug"); + 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?"); + 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. "); + 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(ngram_permuter_activated, false, + "Activate character-level n-gram-based permuter"); + BOOL_VAR_H(permute_only_top, false, "Run only the top choice permuter"); + INT_VAR_H(language_model_fixed_length_choices_depth, 3, + "Depth of blob choice lists to explore" + " when fixed length dawgs are on"); + BOOL_VAR_H(use_new_state_cost, FALSE, + "use new state cost heuristics for segmentation state evaluation"); + double_VAR_H(heuristic_segcost_rating_base, 1.25, + "base factor for adding segmentation cost into word rating." + "It's a multiplying factor, the larger the value above 1, " + "the bigger the effect of segmentation cost."); + double_VAR_H(heuristic_weight_rating, 1, + "weight associated with char rating in combined cost of state"); + double_VAR_H(heuristic_weight_width, 1000.0, + "weight associated with width evidence in combined cost of" + " state"); + double_VAR_H(heuristic_weight_seamcut, 0, + "weight associated with seam cut in combined cost of state"); + double_VAR_H(heuristic_max_char_wh_ratio, 2.0, + "max char width-to-height ratio allowed in segmentation"); + BOOL_VAR_H(enable_new_segsearch, false, + "Enable new segmentation search path."); + double_VAR_H(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, + "Maximum character width-to-height ratio for" + "fixed pitch fonts"); + // END DEPRECATED PARAMETERS + + //// ambigsrecog.cpp ///////////////////////////////////////////////////////// + FILE *init_recog_training(const STRING &fname); + void recog_training_segmented(const STRING &fname, + PAGE_RES *page_res, + volatile ETEXT_DESC *monitor, + FILE *output_file); + void ambigs_classify_and_output(const char *label, + PAGE_RES_IT* pr_it, + FILE *output_file); + +#ifndef NO_CUBE_BUILD + inline CubeRecoContext *GetCubeRecoContext() { return cube_cntxt_; } +#endif + + private: + // The filename of a backup config file. If not null, then we currently + // have a temporary debug config file loaded, and backup_config_file_ + // will be loaded, and set to null when debug is complete. + const char* backup_config_file_; + // The filename of a config file to read when processing a debug word. + STRING word_config_; + // Image used for input to layout analysis and tesseract recognition. + // May be modified by the ShiroRekhaSplitter to eliminate the top-line. + Pix* pix_binary_; + // Unmodified image used for input to cube. Always valid. + Pix* cube_binary_; + // Grey-level input image if the input was not binary, otherwise NULL. + Pix* pix_grey_; + // Original input image. Color if the input was color. + Pix* pix_original_; + // Thresholds that were used to generate the thresholded image from grey. + Pix* pix_thresholds_; + // Input image resolution after any scaling. The resolution is not well + // transmitted by operations on Pix, so we keep an independent record here. + int source_resolution_; + // The shiro-rekha splitter object which is used to split top-lines in + // Devanagari words to provide a better word and grapheme segmentation. + ShiroRekhaSplitter splitter_; + // Page segmentation/layout + Textord textord_; + // True if the primary language uses right_to_left reading order. + bool right_to_left_; + Pix* scaled_color_; + int scaled_factor_; + FCOORD deskew_; + FCOORD reskew_; + TesseractStats stats_; + // Sub-languages to be tried in addition to this. + GenericVector sub_langs_; + // Most recently used Tesseract out of this and sub_langs_. The default + // language for the next word. + Tesseract* most_recently_used_; + // The size of the font table, ie max possible font id + 1. + int font_table_size_; +#ifndef NO_CUBE_BUILD + // Cube objects. + CubeRecoContext* cube_cntxt_; + TesseractCubeCombiner *tess_cube_combiner_; +#endif + // Equation detector. Note: this pointer is NOT owned by the class. + EquationDetect* equ_detect_; + }; + +} // namespace tesseract + + +#endif // TESSERACT_CCMAIN_TESSERACTCLASS_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.cpp new file mode 100644 index 0000000..db9a1b5 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.cpp @@ -0,0 +1,24 @@ +/********************************************************************** + * File: tessvars.cpp (Formerly tessvars.c) + * Description: Variables and other globals for tessedit. + * Author: Ray Smith + * Created: Mon Apr 13 13:13:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include + +#include "tessvars.h" + +FILE *debug_fp = stderr; // write debug stuff here diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.h b/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.h new file mode 100644 index 0000000..326efef --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tessvars.h @@ -0,0 +1,27 @@ +/********************************************************************** + * File: tessvars.h (Formerly tessvars.h) + * Description: Variables and other globals for tessedit. + * Author: Ray Smith + * Created: Mon Apr 13 13:13:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSVARS_H +#define TESSVARS_H + +#include + + +extern FILE *debug_fp; // write debug stuff here +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/tfacepp.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/tfacepp.cpp new file mode 100644 index 0000000..c7cb53c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/tfacepp.cpp @@ -0,0 +1,330 @@ +/********************************************************************** + * File: tfacepp.cpp (Formerly tface++.c) + * Description: C++ side of the C/C++ Tess/Editor interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:39:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#pragma warning(disable:4305) // int/float warnings +#pragma warning(disable:4800) // int/bool warnings +#endif + +#include + +#include "blamer.h" +#include "errcode.h" +#include "ratngs.h" +#include "reject.h" +#include "tesseractclass.h" +#include "werd.h" + +#define MAX_UNDIVIDED_LENGTH 24 + + + + /********************************************************************** + * recog_word + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ +namespace tesseract { + void Tesseract::recog_word(WERD_RES *word) { + if (wordrec_skip_no_truth_words && (word->blamer_bundle == NULL || + word->blamer_bundle->incorrect_result_reason() == IRR_NO_TRUTH)) { + if (classify_debug_level) tprintf("No truth for word - skipping\n"); + word->tess_failed = true; + return; + } + ASSERT_HOST(!word->chopped_word->blobs.empty()); + recog_word_recursive(word); + word->SetupBoxWord(); + if (word->best_choice->length() != word->box_word->length()) { + tprintf("recog_word ASSERT FAIL String:\"%s\"; " + "Strlen=%d; #Blobs=%d\n", + word->best_choice->debug_string().string(), + word->best_choice->length(), word->box_word->length()); + } + ASSERT_HOST(word->best_choice->length() == word->box_word->length()); + // Check that the ratings matrix size matches the sum of all the + // segmentation states. + if (!word->StatesAllValid()) { + tprintf("Not all words have valid states relative to ratings matrix!!"); + word->DebugWordChoices(true, NULL); + ASSERT_HOST(word->StatesAllValid()); + } + if (tessedit_override_permuter) { + /* Override the permuter type if a straight dictionary check disagrees. */ + uinT8 perm_type = word->best_choice->permuter(); + if ((perm_type != SYSTEM_DAWG_PERM) && + (perm_type != FREQ_DAWG_PERM) && (perm_type != USER_DAWG_PERM)) { + uinT8 real_dict_perm_type = dict_word(*word->best_choice); + if (((real_dict_perm_type == SYSTEM_DAWG_PERM) || + (real_dict_perm_type == FREQ_DAWG_PERM) || + (real_dict_perm_type == USER_DAWG_PERM)) && + (alpha_count(word->best_choice->unichar_string().string(), + word->best_choice->unichar_lengths().string()) > 0)) { + word->best_choice->set_permuter(real_dict_perm_type); // use dict perm + } + } + if (tessedit_rejection_debug && + perm_type != word->best_choice->permuter()) { + tprintf("Permuter Type Flipped from %d to %d\n", + perm_type, word->best_choice->permuter()); + } + } + // Factored out from control.cpp + ASSERT_HOST((word->best_choice == NULL) == (word->raw_choice == NULL)); + if (word->best_choice == NULL || word->best_choice->length() == 0 || + static_cast(strspn(word->best_choice->unichar_string().string(), + " ")) == word->best_choice->length()) { + word->tess_failed = true; + word->reject_map.initialise(word->box_word->length()); + word->reject_map.rej_word_tess_failure(); + } + else { + word->tess_failed = false; + } + } + + + /********************************************************************** + * recog_word_recursive + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ + void Tesseract::recog_word_recursive(WERD_RES *word) { + int word_length = word->chopped_word->NumBlobs(); // no of blobs + if (word_length > MAX_UNDIVIDED_LENGTH) { + return split_and_recog_word(word); + } + cc_recog(word); + word_length = word->rebuild_word->NumBlobs(); // No of blobs in output. + + // Do sanity checks and minor fixes on best_choice. + if (word->best_choice->length() > word_length) { + word->best_choice->make_bad(); // should never happen + tprintf("recog_word: Discarded long string \"%s\"" + " (%d characters vs %d blobs)\n", + word->best_choice->unichar_string().string(), + word->best_choice->length(), word_length); + tprintf("Word is at:"); + word->word->bounding_box().print(); + } + if (word->best_choice->length() < word_length) { + UNICHAR_ID space_id = unicharset.unichar_to_id(" "); + while (word->best_choice->length() < word_length) { + word->best_choice->append_unichar_id(space_id, 1, 0.0, + word->best_choice->certainty()); + } + } + } + + + /********************************************************************** + * split_and_recog_word + * + * Split the word into 2 smaller pieces at the largest gap. + * Recognize the pieces and stick the results back together. + **********************************************************************/ + void Tesseract::split_and_recog_word(WERD_RES *word) { + // Find the biggest blob gap in the chopped_word. + int bestgap = -MAX_INT32; + int split_index = 0; + for (int b = 1; b < word->chopped_word->NumBlobs(); ++b) { + TBOX prev_box = word->chopped_word->blobs[b - 1]->bounding_box(); + TBOX blob_box = word->chopped_word->blobs[b]->bounding_box(); + int gap = blob_box.left() - prev_box.right(); + if (gap > bestgap) { + bestgap = gap; + split_index = b; + } + } + ASSERT_HOST(split_index > 0); + + WERD_RES *word2 = NULL; + BlamerBundle *orig_bb = NULL; + split_word(word, split_index, &word2, &orig_bb); + + // Recognize the first part of the word. + recog_word_recursive(word); + // Recognize the second part of the word. + recog_word_recursive(word2); + + join_words(word, word2, orig_bb); + } + + + /********************************************************************** + * split_word + * + * Split a given WERD_RES in place into two smaller words for recognition. + * split_pt is the index of the first blob to go in the second word. + * The underlying word is left alone, only the TWERD (and subsequent data) + * are split up. orig_blamer_bundle is set to the original blamer bundle, + * and will now be owned by the caller. New blamer bundles are forged for the + * two pieces. + **********************************************************************/ + void Tesseract::split_word(WERD_RES *word, + int split_pt, + WERD_RES **right_piece, + BlamerBundle **orig_blamer_bundle) const { + ASSERT_HOST(split_pt > 0 && split_pt < word->chopped_word->NumBlobs()); + + // Save a copy of the blamer bundle so we can try to reconstruct it below. + BlamerBundle *orig_bb = + word->blamer_bundle ? new BlamerBundle(*word->blamer_bundle) : NULL; + + WERD_RES *word2 = new WERD_RES(*word); + + // blow away the copied chopped_word, as we want to work with + // the blobs from the input chopped_word so seam_arrays can be merged. + TWERD *chopped = word->chopped_word; + TWERD *chopped2 = new TWERD; + chopped2->blobs.reserve(chopped->NumBlobs() - split_pt); + for (int i = split_pt; i < chopped->NumBlobs(); ++i) { + chopped2->blobs.push_back(chopped->blobs[i]); + } + chopped->blobs.truncate(split_pt); + word->chopped_word = NULL; + delete word2->chopped_word; + word2->chopped_word = NULL; + + const UNICHARSET &unicharset = *word->uch_set; + word->ClearResults(); + word2->ClearResults(); + word->chopped_word = chopped; + word2->chopped_word = chopped2; + word->SetupBasicsFromChoppedWord(unicharset); + word2->SetupBasicsFromChoppedWord(unicharset); + + // Try to adjust the blamer bundle. + if (orig_bb != NULL) { + // TODO(rays) Looks like a leak to me. + // orig_bb should take, rather than copy. + word->blamer_bundle = new BlamerBundle(); + word2->blamer_bundle = new BlamerBundle(); + orig_bb->SplitBundle(chopped->blobs.back()->bounding_box().right(), + word2->chopped_word->blobs[0]->bounding_box().left(), + wordrec_debug_blamer, + word->blamer_bundle, word2->blamer_bundle); + } + + *right_piece = word2; + *orig_blamer_bundle = orig_bb; + } + + + /********************************************************************** + * join_words + * + * The opposite of split_word(): + * join word2 (including any recognized data / seam array / etc) + * onto the right of word and then delete word2. + * Also, if orig_bb is provided, stitch it back into word. + **********************************************************************/ + void Tesseract::join_words(WERD_RES *word, + WERD_RES *word2, + BlamerBundle *orig_bb) const { + TBOX prev_box = word->chopped_word->blobs.back()->bounding_box(); + TBOX blob_box = word2->chopped_word->blobs[0]->bounding_box(); + // Tack the word2 outputs onto the end of the word outputs. + word->chopped_word->blobs += word2->chopped_word->blobs; + word->rebuild_word->blobs += word2->rebuild_word->blobs; + word2->chopped_word->blobs.clear(); + word2->rebuild_word->blobs.clear(); + TPOINT split_pt; + split_pt.x = (prev_box.right() + blob_box.left()) / 2; + split_pt.y = (prev_box.top() + prev_box.bottom() + + blob_box.top() + blob_box.bottom()) / 4; + // Move the word2 seams onto the end of the word1 seam_array. + // Since the seam list is one element short, an empty seam marking the + // end of the last blob in the first word is needed first. + word->seam_array.push_back(new SEAM(0.0f, split_pt)); + word->seam_array += word2->seam_array; + word2->seam_array.truncate(0); + // Fix widths and gaps. + word->blob_widths += word2->blob_widths; + word->blob_gaps += word2->blob_gaps; + // Fix the ratings matrix. + int rat1 = word->ratings->dimension(); + int rat2 = word2->ratings->dimension(); + word->ratings->AttachOnCorner(word2->ratings); + ASSERT_HOST(word->ratings->dimension() == rat1 + rat2); + word->best_state += word2->best_state; + // Append the word choices. + *word->raw_choice += *word2->raw_choice; + + // How many alt choices from each should we try to get? + const int kAltsPerPiece = 2; + // When do we start throwing away extra alt choices? + const int kTooManyAltChoices = 100; + + // Construct the cartesian product of the best_choices of word(1) and word2. + WERD_CHOICE_LIST joined_choices; + WERD_CHOICE_IT jc_it(&joined_choices); + WERD_CHOICE_IT bc1_it(&word->best_choices); + WERD_CHOICE_IT bc2_it(&word2->best_choices); + int num_word1_choices = word->best_choices.length(); + int total_joined_choices = num_word1_choices; + // Nota Bene: For the main loop here, we operate only on the 2nd and greater + // word2 choices, and put them in the joined_choices list. The 1st word2 + // choice gets added to the original word1 choices in-place after we have + // finished with them. + int bc2_index = 1; + for (bc2_it.forward(); !bc2_it.at_first(); bc2_it.forward(), ++bc2_index) { + if (total_joined_choices >= kTooManyAltChoices && + bc2_index > kAltsPerPiece) + break; + int bc1_index = 0; + for (bc1_it.move_to_first(); bc1_index < num_word1_choices; + ++bc1_index, bc1_it.forward()) { + if (total_joined_choices >= kTooManyAltChoices && + bc1_index > kAltsPerPiece) + break; + WERD_CHOICE *wc = new WERD_CHOICE(*bc1_it.data()); + *wc += *bc2_it.data(); + jc_it.add_after_then_move(wc); + ++total_joined_choices; + } + } + // Now that we've filled in as many alternates as we want, paste the best + // choice for word2 onto the original word alt_choices. + bc1_it.move_to_first(); + bc2_it.move_to_first(); + for (bc1_it.mark_cycle_pt(); !bc1_it.cycled_list(); bc1_it.forward()) { + *bc1_it.data() += *bc2_it.data(); + } + bc1_it.move_to_last(); + bc1_it.add_list_after(&joined_choices); + + // Restore the pointer to original blamer bundle and combine blamer + // information recorded in the splits. + if (orig_bb != NULL) { + orig_bb->JoinBlames(*word->blamer_bundle, *word2->blamer_bundle, + wordrec_debug_blamer); + delete word->blamer_bundle; + word->blamer_bundle = orig_bb; + } + word->SetupBoxWord(); + word->reject_map.initialise(word->box_word->length()); + delete word2; + } + + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.cpp new file mode 100644 index 0000000..2156875 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.cpp @@ -0,0 +1,334 @@ +/////////////////////////////////////////////////////////////////////// +// File: thresholder.cpp +// Description: Base API for thresolding images in tesseract. +// Author: Ray Smith +// Created: Mon May 12 11:28:15 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 "allheaders.h" + +#include "thresholder.h" + +#include + +#include "otsuthr.h" + +#include "openclwrapper.h" + +namespace tesseract { + + ImageThresholder::ImageThresholder() + : pix_(NULL), + image_width_(0), image_height_(0), + pix_channels_(0), pix_wpl_(0), + scale_(1), yres_(300), estimated_res_(300) { + SetRectangle(0, 0, 0, 0); + } + + ImageThresholder::~ImageThresholder() { + Clear(); + } + + // Destroy the Pix if there is one, freeing memory. + void ImageThresholder::Clear() { + pixDestroy(&pix_); + } + + // Return true if no image has been set. + bool ImageThresholder::IsEmpty() const { + return pix_ == NULL; + } + + // SetImage makes a copy of all the image data, so it may be deleted + // immediately after this call. + // Greyscale of 8 and color of 24 or 32 bits per pixel may be given. + // Palette color images will not work properly and must be converted to + // 24 bit. + // Binary images of 1 bit per pixel may also be given but they must be + // byte packed with the MSB of the first byte being the first pixel, and a + // one pixel is WHITE. For binary images set bytes_per_pixel=0. + void ImageThresholder::SetImage(const unsigned char* imagedata, + int width, int height, + int bytes_per_pixel, int bytes_per_line) { + int bpp = bytes_per_pixel * 8; + if (bpp == 0) bpp = 1; + Pix* pix = pixCreate(width, height, bpp == 24 ? 32 : bpp); + l_uint32* data = pixGetData(pix); + int wpl = pixGetWpl(pix); + switch (bpp) { + case 1: + for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) { + for (int x = 0; x < width; ++x) { + if (imagedata[x / 8] & (0x80 >> (x % 8))) + CLEAR_DATA_BIT(data, x); + else + SET_DATA_BIT(data, x); + } + } + break; + + case 8: + // Greyscale just copies the bytes in the right order. + for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) { + for (int x = 0; x < width; ++x) + SET_DATA_BYTE(data, x, imagedata[x]); + } + break; + + case 24: + // Put the colors in the correct places in the line buffer. + for (int y = 0; y < height; ++y, imagedata += bytes_per_line) { + for (int x = 0; x < width; ++x, ++data) { + SET_DATA_BYTE(data, COLOR_RED, imagedata[3 * x]); + SET_DATA_BYTE(data, COLOR_GREEN, imagedata[3 * x + 1]); + SET_DATA_BYTE(data, COLOR_BLUE, imagedata[3 * x + 2]); + } + } + break; + + case 32: + // Maintain byte order consistency across different endianness. + for (int y = 0; y < height; ++y, imagedata += bytes_per_line, data += wpl) { + for (int x = 0; x < width; ++x) { + data[x] = (imagedata[x * 4] << 24) | (imagedata[x * 4 + 1] << 16) | + (imagedata[x * 4 + 2] << 8) | imagedata[x * 4 + 3]; + } + } + break; + + default: + tprintf("Cannot convert RAW image to Pix with bpp = %d\n", bpp); + } + pixSetYRes(pix, 300); + SetImage(pix); + pixDestroy(&pix); + } + + // Store the coordinates of the rectangle to process for later use. + // Doesn't actually do any thresholding. + void ImageThresholder::SetRectangle(int left, int top, int width, int height) { + rect_left_ = left; + rect_top_ = top; + rect_width_ = width; + rect_height_ = height; + } + + // Get enough parameters to be able to rebuild bounding boxes in the + // original image (not just within the rectangle). + // Left and top are enough with top-down coordinates, but + // the height of the rectangle and the image are needed for bottom-up. + void ImageThresholder::GetImageSizes(int* left, int* top, + int* width, int* height, + int* imagewidth, int* imageheight) { + *left = rect_left_; + *top = rect_top_; + *width = rect_width_; + *height = rect_height_; + *imagewidth = image_width_; + *imageheight = image_height_; + } + + // Pix vs raw, which to use? Pix is the preferred input for efficiency, + // since raw buffers are copied. + // SetImage for Pix clones its input, so the source pix may be pixDestroyed + // immediately after, but may not go away until after the Thresholder has + // finished with it. + void ImageThresholder::SetImage(const Pix* pix) { + if (pix_ != NULL) + pixDestroy(&pix_); + Pix* src = const_cast(pix); + int depth; + pixGetDimensions(src, &image_width_, &image_height_, &depth); + // Convert the image as necessary so it is one of binary, plain RGB, or + // 8 bit with no colormap. Guarantee that we always end up with our own copy, + // not just a clone of the input. + if (pixGetColormap(src)) { + Pix* tmp = pixRemoveColormap(src, REMOVE_CMAP_BASED_ON_SRC); + depth = pixGetDepth(tmp); + if (depth > 1 && depth < 8) { + pix_ = pixConvertTo8(tmp, false); + pixDestroy(&tmp); + } + else { + pix_ = tmp; + } + } + else if (depth > 1 && depth < 8) { + pix_ = pixConvertTo8(src, false); + } + else { + pix_ = pixCopy(NULL, src); + } + depth = pixGetDepth(pix_); + pix_channels_ = depth / 8; + pix_wpl_ = pixGetWpl(pix_); + scale_ = 1; + estimated_res_ = yres_ = pixGetYRes(pix_); + Init(); + } + + // Threshold the source image as efficiently as possible to the output Pix. + // Creates a Pix and sets pix to point to the resulting pointer. + // Caller must use pixDestroy to free the created Pix. + void ImageThresholder::ThresholdToPix(PageSegMode pageseg_mode, Pix** pix) { + if (pix_channels_ == 0) { + // We have a binary image, but it still has to be copied, as this API + // allows the caller to modify the output. + Pix* original = GetPixRect(); + *pix = pixCopy(NULL, original); + pixDestroy(&original); + } + else { + OtsuThresholdRectToPix(pix_, pix); + } + } + + // Gets a pix that contains an 8 bit threshold value at each pixel. The + // returned pix may be an integer reduction of the binary image such that + // the scale factor may be inferred from the ratio of the sizes, even down + // to the extreme of a 1x1 pixel thresholds image. + // Ideally the 8 bit threshold should be the exact threshold used to generate + // the binary image in ThresholdToPix, but this is not a hard constraint. + // Returns NULL if the input is binary. PixDestroy after use. + Pix* ImageThresholder::GetPixRectThresholds() { + if (IsBinary()) return NULL; + Pix* pix_grey = GetPixRectGrey(); + int width = pixGetWidth(pix_grey); + int height = pixGetHeight(pix_grey); + int* thresholds; + int* hi_values; + OtsuThreshold(pix_grey, 0, 0, width, height, &thresholds, &hi_values); + pixDestroy(&pix_grey); + Pix* pix_thresholds = pixCreate(width, height, 8); + int threshold = thresholds[0] > 0 ? thresholds[0] : 128; + pixSetAllArbitrary(pix_thresholds, threshold); + delete[] thresholds; + delete[] hi_values; + return pix_thresholds; + } + + // Common initialization shared between SetImage methods. + void ImageThresholder::Init() { + SetRectangle(0, 0, image_width_, image_height_); + } + + // Get a clone/copy of the source image rectangle. + // The returned Pix must be pixDestroyed. + // This function will be used in the future by the page layout analysis, and + // the layout analysis that uses it will only be available with Leptonica, + // so there is no raw equivalent. + Pix* ImageThresholder::GetPixRect() { + if (IsFullImage()) { + // Just clone the whole thing. + return pixClone(pix_); + } + else { + // Crop to the given rectangle. + Box* box = boxCreate(rect_left_, rect_top_, rect_width_, rect_height_); + Pix* cropped = pixClipRectangle(pix_, box, NULL); + boxDestroy(&box); + return cropped; + } + } + + // Get a clone/copy of the source image rectangle, reduced to greyscale, + // and at the same resolution as the output binary. + // The returned Pix must be pixDestroyed. + // Provided to the classifier to extract features from the greyscale image. + Pix* ImageThresholder::GetPixRectGrey() { + Pix* pix = GetPixRect(); // May have to be reduced to grey. + int depth = pixGetDepth(pix); + if (depth != 8) { + Pix* result = depth < 8 ? pixConvertTo8(pix, false) + : pixConvertRGBToLuminance(pix); + pixDestroy(&pix); + return result; + } + return pix; + } + + // Otsu thresholds the rectangle, taking the rectangle from *this. + void ImageThresholder::OtsuThresholdRectToPix(Pix* src_pix, + Pix** out_pix) const { + PERF_COUNT_START("OtsuThresholdRectToPix") + int* thresholds; + int* hi_values; + + int num_channels = OtsuThreshold(src_pix, rect_left_, rect_top_, rect_width_, + rect_height_, &thresholds, &hi_values); + // only use opencl if compiled w/ OpenCL and selected device is opencl +#ifdef USE_OPENCL + OpenclDevice od; + if ((num_channels == 4 || num_channels == 1) && + od.selectedDeviceIsOpenCL() && rect_top_ == 0 && rect_left_ == 0) { + od.ThresholdRectToPixOCL((unsigned char*)pixGetData(src_pix), num_channels, + pixGetWpl(src_pix) * 4, thresholds, hi_values, + out_pix /*pix_OCL*/, rect_height_, rect_width_, + rect_top_, rect_left_); + } + else { +#endif + ThresholdRectToPix(src_pix, num_channels, thresholds, hi_values, out_pix); +#ifdef USE_OPENCL + } +#endif + delete[] thresholds; + delete[] hi_values; + + PERF_COUNT_END + } + + /// Threshold the rectangle, taking everything except the src_pix + /// from the class, using thresholds/hi_values to the output pix. + /// NOTE that num_channels is the size of the thresholds and hi_values + // arrays and also the bytes per pixel in src_pix. + void ImageThresholder::ThresholdRectToPix(Pix* src_pix, + int num_channels, + const int* thresholds, + const int* hi_values, + Pix** pix) const { + PERF_COUNT_START("ThresholdRectToPix") + *pix = pixCreate(rect_width_, rect_height_, 1); + uinT32* pixdata = pixGetData(*pix); + int wpl = pixGetWpl(*pix); + int src_wpl = pixGetWpl(src_pix); + uinT32* srcdata = pixGetData(src_pix); + for (int y = 0; y < rect_height_; ++y) { + const uinT32* linedata = srcdata + (y + rect_top_) * src_wpl; + uinT32* pixline = pixdata + y * wpl; + for (int x = 0; x < rect_width_; ++x) { + bool white_result = true; + for (int ch = 0; ch < num_channels; ++ch) { + int pixel = GET_DATA_BYTE(const_cast( + reinterpret_cast(linedata)), + (x + rect_left_) * num_channels + ch); + if (hi_values[ch] >= 0 && + (pixel > thresholds[ch]) == (hi_values[ch] == 0)) { + white_result = false; + break; + } + } + if (white_result) + CLEAR_DATA_BIT(pixline, x); + else + SET_DATA_BIT(pixline, x); + } + } + + PERF_COUNT_END + } + +} // namespace tesseract. + diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.h b/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.h new file mode 100644 index 0000000..fc8bc0d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/thresholder.h @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////// +// File: thresholder.h +// Description: Base API for thresolding images in tesseract. +// Author: Ray Smith +// Created: Mon May 12 11:00:15 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_THRESHOLDER_H__ +#define TESSERACT_CCMAIN_THRESHOLDER_H__ + +#include "platform.h" +#include "publictypes.h" + +struct Pix; + +namespace tesseract { + + /// Base class for all tesseract image thresholding classes. + /// Specific classes can add new thresholding methods by + /// overriding ThresholdToPix. + /// Each instance deals with a single image, but the design is intended to + /// be useful for multiple calls to SetRectangle and ThresholdTo* if + /// desired. + class TESS_API ImageThresholder { + public: + ImageThresholder(); + virtual ~ImageThresholder(); + + /// Destroy the Pix if there is one, freeing memory. + virtual void Clear(); + + /// Return true if no image has been set. + bool IsEmpty() const; + + /// SetImage makes a copy of all the image data, so it may be deleted + /// immediately after this call. + /// Greyscale of 8 and color of 24 or 32 bits per pixel may be given. + /// Palette color images will not work properly and must be converted to + /// 24 bit. + /// Binary images of 1 bit per pixel may also be given but they must be + /// byte packed with the MSB of the first byte being the first pixel, and a + /// one pixel is WHITE. For binary images set bytes_per_pixel=0. + void SetImage(const unsigned char* imagedata, int width, int height, + int bytes_per_pixel, int bytes_per_line); + + /// Store the coordinates of the rectangle to process for later use. + /// Doesn't actually do any thresholding. + void SetRectangle(int left, int top, int width, int height); + + /// Get enough parameters to be able to rebuild bounding boxes in the + /// original image (not just within the rectangle). + /// Left and top are enough with top-down coordinates, but + /// the height of the rectangle and the image are needed for bottom-up. + virtual void GetImageSizes(int* left, int* top, int* width, int* height, + int* imagewidth, int* imageheight); + + /// Return true if the source image is color. + bool IsColor() const { + return pix_channels_ >= 3; + } + + /// Returns true if the source image is binary. + bool IsBinary() const { + return pix_channels_ == 0; + } + + int GetScaleFactor() const { + return scale_; + } + + // Set the resolution of the source image in pixels per inch. + // This should be called right after SetImage(), and will let us return + // appropriate font sizes for the text. + void SetSourceYResolution(int ppi) { + yres_ = ppi; + estimated_res_ = ppi; + } + int GetSourceYResolution() const { + return yres_; + } + int GetScaledYResolution() const { + return scale_ * yres_; + } + // Set the resolution of the source image in pixels per inch, as estimated + // by the thresholder from the text size found during thresholding. + // This value will be used to set internal size thresholds during recognition + // and will not influence the output "point size." The default value is + // the same as the source resolution. (yres_) + void SetEstimatedResolution(int ppi) { + estimated_res_ = ppi; + } + // Returns the estimated resolution, including any active scaling. + // This value will be used to set internal size thresholds during recognition. + int GetScaledEstimatedResolution() const { + return scale_ * estimated_res_; + } + + /// Pix vs raw, which to use? Pix is the preferred input for efficiency, + /// since raw buffers are copied. + /// SetImage for Pix clones its input, so the source pix may be pixDestroyed + /// immediately after, but may not go away until after the Thresholder has + /// finished with it. + void SetImage(const Pix* pix); + + /// Threshold the source image as efficiently as possible to the output Pix. + /// Creates a Pix and sets pix to point to the resulting pointer. + /// Caller must use pixDestroy to free the created Pix. + virtual void ThresholdToPix(PageSegMode pageseg_mode, Pix** pix); + + // Gets a pix that contains an 8 bit threshold value at each pixel. The + // returned pix may be an integer reduction of the binary image such that + // the scale factor may be inferred from the ratio of the sizes, even down + // to the extreme of a 1x1 pixel thresholds image. + // Ideally the 8 bit threshold should be the exact threshold used to generate + // the binary image in ThresholdToPix, but this is not a hard constraint. + // Returns NULL if the input is binary. PixDestroy after use. + virtual Pix* GetPixRectThresholds(); + + /// Get a clone/copy of the source image rectangle. + /// The returned Pix must be pixDestroyed. + /// This function will be used in the future by the page layout analysis, and + /// the layout analysis that uses it will only be available with Leptonica, + /// so there is no raw equivalent. + Pix* GetPixRect(); + + // Get a clone/copy of the source image rectangle, reduced to greyscale, + // and at the same resolution as the output binary. + // The returned Pix must be pixDestroyed. + // Provided to the classifier to extract features from the greyscale image. + virtual Pix* GetPixRectGrey(); + + protected: + // ---------------------------------------------------------------------- + // Utility functions that may be useful components for other thresholders. + + /// Common initialization shared between SetImage methods. + virtual void Init(); + + /// Return true if we are processing the full image. + bool IsFullImage() const { + return rect_left_ == 0 && rect_top_ == 0 && + rect_width_ == image_width_ && rect_height_ == image_height_; + } + + // Otsu thresholds the rectangle, taking the rectangle from *this. + void OtsuThresholdRectToPix(Pix* src_pix, Pix** out_pix) const; + + /// Threshold the rectangle, taking everything except the src_pix + /// from the class, using thresholds/hi_values to the output pix. + /// NOTE that num_channels is the size of the thresholds and hi_values + // arrays and also the bytes per pixel in src_pix. + void ThresholdRectToPix(Pix* src_pix, int num_channels, + const int* thresholds, const int* hi_values, + Pix** pix) const; + + protected: + /// Clone or other copy of the source Pix. + /// The pix will always be PixDestroy()ed on destruction of the class. + Pix* pix_; + + int image_width_; //< Width of source pix_. + int image_height_; //< Height of source pix_. + int pix_channels_; //< Number of 8-bit channels in pix_. + int pix_wpl_; //< Words per line of pix_. + // Limits of image rectangle to be processed. + int scale_; //< Scale factor from original image. + int yres_; //< y pixels/inch in source image. + int estimated_res_; //< Resolution estimate from text size. + int rect_left_; + int rect_top_; + int rect_width_; + int rect_height_; + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_THRESHOLDER_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/vcsversion.h b/hgdriver/3rdparty/hgOCR/include/ccmain/vcsversion.h new file mode 100644 index 0000000..d620733 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/vcsversion.h @@ -0,0 +1,2 @@ +#define GIT_REV "3.05.00dev" + diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.cpp b/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.cpp new file mode 100644 index 0000000..c212b70 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.cpp @@ -0,0 +1,59 @@ +/********************************************************************** + * File: werdit.cpp (Formerly wordit.c) + * Description: An iterator for passing over all the words in a document. + * Author: Ray Smith + * Created: Mon Apr 27 08:51:22 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "werdit.h" + + /********************************************************************** + * make_pseudo_word + * + * Make all the blobs inside a selection into a single word. + * The returned PAGE_RES_IT* it points to the new word. After use, call + * it->DeleteCurrentWord() to delete the fake word, and then + * delete it to get rid of the iterator itself. + **********************************************************************/ + +PAGE_RES_IT* make_pseudo_word(PAGE_RES* page_res, const TBOX& selection_box) { + PAGE_RES_IT pr_it(page_res); + C_BLOB_LIST new_blobs; // list of gathered blobs + C_BLOB_IT new_blob_it = &new_blobs; // iterator + + for (WERD_RES* word_res = pr_it.word(); word_res != NULL; + word_res = pr_it.forward()) { + WERD* word = word_res->word; + if (word->bounding_box().overlap(selection_box)) { + 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(); + if (blob->bounding_box().overlap(selection_box)) { + new_blob_it.add_after_then_move(C_BLOB::deep_copy(blob)); + } + } + if (!new_blobs.empty()) { + WERD* pseudo_word = new WERD(&new_blobs, 1, NULL); + word_res = pr_it.InsertSimpleCloneWord(*word_res, pseudo_word); + PAGE_RES_IT* it = new PAGE_RES_IT(page_res); + while (it->word() != word_res && it->word() != NULL) it->forward(); + ASSERT_HOST(it->word() == word_res); + return it; + } + } + } + return NULL; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.h b/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.h new file mode 100644 index 0000000..5ffc999 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccmain/werdit.h @@ -0,0 +1,27 @@ +/********************************************************************** + * File: wordit.c + * Description: An iterator for passing over all the words in a document. + * Author: Ray Smith + * Created: Mon Apr 27 08:51:22 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef WERDIT_H +#define WERDIT_H + +#include "pageres.h" + +PAGE_RES_IT* make_pseudo_word(PAGE_RES* page_res, const TBOX& selection_box); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.cpp new file mode 100644 index 0000000..0120b19 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.cpp @@ -0,0 +1,603 @@ +/////////////////////////////////////////////////////////////////////// +// File: blamer.cpp +// Description: Module allowing precise error causes to be allocated. +// Author: Rike Antonova +// Refactored: Ray Smith +// Created: Mon Feb 04 14:37:01 PST 2013 +// +// (C) Copyright 2013, 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 "blamer.h" +#include "blobs.h" +#include "matrix.h" +#include "normalis.h" +#include "pageres.h" + +// Names for each value of IncorrectResultReason enum. Keep in sync. +const char kBlameCorrect[] = "corr"; +const char kBlameClassifier[] = "cl"; +const char kBlameChopper[] = "chop"; +const char kBlameClassLMTradeoff[] = "cl/LM"; +const char kBlamePageLayout[] = "pglt"; +const char kBlameSegsearchHeur[] = "ss_heur"; +const char kBlameSegsearchPP[] = "ss_pp"; +const char kBlameClassOldLMTradeoff[] = "cl/old_LM"; +const char kBlameAdaption[] = "adapt"; +const char kBlameNoTruthSplit[] = "no_tr_spl"; +const char kBlameNoTruth[] = "no_tr"; +const char kBlameUnknown[] = "unkn"; + +const char * const kIncorrectResultReasonNames[] = { + kBlameCorrect, + kBlameClassifier, + kBlameChopper, + kBlameClassLMTradeoff, + kBlamePageLayout, + kBlameSegsearchHeur, + kBlameSegsearchPP, + kBlameClassOldLMTradeoff, + kBlameAdaption, + kBlameNoTruthSplit, + kBlameNoTruth, + kBlameUnknown +}; + +const char *BlamerBundle::IncorrectReasonName(IncorrectResultReason irr) { + return kIncorrectResultReasonNames[irr]; +} + +const char *BlamerBundle::IncorrectReason() const { + return kIncorrectResultReasonNames[incorrect_result_reason_]; +} + +// Functions to setup the blamer. +// Whole word string, whole word bounding box. +void BlamerBundle::SetWordTruth(const UNICHARSET& unicharset, + const char* truth_str, const TBOX& word_box) { + truth_word_.InsertBox(0, word_box); + truth_has_char_boxes_ = false; + // Encode the string as UNICHAR_IDs. + GenericVector encoding; + GenericVector lengths; + unicharset.encode_string(truth_str, false, &encoding, &lengths, NULL); + int total_length = 0; + for (int i = 0; i < encoding.size(); total_length += lengths[i++]) { + STRING uch(truth_str + total_length); + uch.truncate_at(lengths[i] - total_length); + UNICHAR_ID id = encoding[i]; + if (id != INVALID_UNICHAR_ID) uch = unicharset.get_normed_unichar(id); + truth_text_.push_back(uch); + } +} + +// Single "character" string, "character" bounding box. +// May be called multiple times to indicate the characters in a word. +void BlamerBundle::SetSymbolTruth(const UNICHARSET& unicharset, + const char* char_str, const TBOX& char_box) { + STRING symbol_str(char_str); + UNICHAR_ID id = unicharset.unichar_to_id(char_str); + if (id != INVALID_UNICHAR_ID) { + STRING normed_uch(unicharset.get_normed_unichar(id)); + if (normed_uch.length() > 0) symbol_str = normed_uch; + } + int length = truth_word_.length(); + truth_text_.push_back(symbol_str); + truth_word_.InsertBox(length, char_box); + if (length == 0) + truth_has_char_boxes_ = true; + else if (truth_word_.BlobBox(length - 1) == char_box) + truth_has_char_boxes_ = false; +} + +// Marks that there is something wrong with the truth text, like it contains +// reject characters. +void BlamerBundle::SetRejectedTruth() { + incorrect_result_reason_ = IRR_NO_TRUTH; + truth_has_char_boxes_ = false; +} + +// Returns true if the provided word_choice is correct. +bool BlamerBundle::ChoiceIsCorrect(const WERD_CHOICE* word_choice) const { + if (word_choice == NULL) return false; + const UNICHARSET* uni_set = word_choice->unicharset(); + STRING normed_choice_str; + for (int i = 0; i < word_choice->length(); ++i) { + normed_choice_str += + uni_set->get_normed_unichar(word_choice->unichar_id(i)); + } + STRING truth_str = TruthString(); + return truth_str == normed_choice_str; +} + +void BlamerBundle::FillDebugString(const STRING &msg, + const WERD_CHOICE *choice, + STRING *debug) { + (*debug) += "Truth "; + for (int i = 0; i < this->truth_text_.length(); ++i) { + (*debug) += this->truth_text_[i]; + } + if (!this->truth_has_char_boxes_) (*debug) += " (no char boxes)"; + if (choice != NULL) { + (*debug) += " Choice "; + STRING choice_str; + choice->string_and_lengths(&choice_str, NULL); + (*debug) += choice_str; + } + if (msg.length() > 0) { + (*debug) += "\n"; + (*debug) += msg; + } + (*debug) += "\n"; +} + +// Sets up the norm_truth_word from truth_word using the given DENORM. +void BlamerBundle::SetupNormTruthWord(const DENORM& denorm) { + // TODO(rays) Is this the last use of denorm in WERD_RES and can it go? + norm_box_tolerance_ = kBlamerBoxTolerance * denorm.x_scale(); + TPOINT topleft; + TPOINT botright; + TPOINT norm_topleft; + TPOINT norm_botright; + for (int b = 0; b < truth_word_.length(); ++b) { + const TBOX &box = truth_word_.BlobBox(b); + topleft.x = box.left(); + topleft.y = box.top(); + botright.x = box.right(); + botright.y = box.bottom(); + denorm.NormTransform(NULL, topleft, &norm_topleft); + denorm.NormTransform(NULL, botright, &norm_botright); + TBOX norm_box(norm_topleft.x, norm_botright.y, + norm_botright.x, norm_topleft.y); + norm_truth_word_.InsertBox(b, norm_box); + } +} + +// Splits *this into two pieces in bundle1 and bundle2 (preallocated, empty +// bundles) where the right edge/ of the left-hand word is word1_right, +// and the left edge of the right-hand word is word2_left. +void BlamerBundle::SplitBundle(int word1_right, int word2_left, bool debug, + BlamerBundle* bundle1, + BlamerBundle* bundle2) const { + STRING debug_str; + // Find truth boxes that correspond to the split in the blobs. + int b; + int begin2_truth_index = -1; + if (incorrect_result_reason_ != IRR_NO_TRUTH && + truth_has_char_boxes_) { + debug_str = "Looking for truth split at"; + debug_str.add_str_int(" end1_x ", word1_right); + debug_str.add_str_int(" begin2_x ", word2_left); + debug_str += "\nnorm_truth_word boxes:\n"; + if (norm_truth_word_.length() > 1) { + norm_truth_word_.BlobBox(0).print_to_str(&debug_str); + for (b = 1; b < norm_truth_word_.length(); ++b) { + norm_truth_word_.BlobBox(b).print_to_str(&debug_str); + if ((abs(word1_right - norm_truth_word_.BlobBox(b - 1).right()) < + norm_box_tolerance_) && + (abs(word2_left - norm_truth_word_.BlobBox(b).left()) < + norm_box_tolerance_)) { + begin2_truth_index = b; + debug_str += "Split found"; + break; + } + } + debug_str += '\n'; + } + } + // Populate truth information in word and word2 with the first and second + // part of the original truth. + if (begin2_truth_index > 0) { + bundle1->truth_has_char_boxes_ = true; + bundle1->norm_box_tolerance_ = norm_box_tolerance_; + bundle2->truth_has_char_boxes_ = true; + bundle2->norm_box_tolerance_ = norm_box_tolerance_; + BlamerBundle *curr_bb = bundle1; + for (b = 0; b < norm_truth_word_.length(); ++b) { + if (b == begin2_truth_index) curr_bb = bundle2; + curr_bb->norm_truth_word_.InsertBox(b, norm_truth_word_.BlobBox(b)); + curr_bb->truth_word_.InsertBox(b, truth_word_.BlobBox(b)); + curr_bb->truth_text_.push_back(truth_text_[b]); + } + } + else if (incorrect_result_reason_ == IRR_NO_TRUTH) { + bundle1->incorrect_result_reason_ = IRR_NO_TRUTH; + bundle2->incorrect_result_reason_ = IRR_NO_TRUTH; + } + else { + debug_str += "Truth split not found"; + debug_str += truth_has_char_boxes_ ? + "\n" : " (no truth char boxes)\n"; + bundle1->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, NULL, debug); + bundle2->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, NULL, debug); + } +} + +// "Joins" the blames from bundle1 and bundle2 into *this. +void BlamerBundle::JoinBlames(const BlamerBundle& bundle1, + const BlamerBundle& bundle2, bool debug) { + STRING debug_str; + IncorrectResultReason irr = incorrect_result_reason_; + if (irr != IRR_NO_TRUTH_SPLIT) debug_str = ""; + if (bundle1.incorrect_result_reason_ != IRR_CORRECT && + bundle1.incorrect_result_reason_ != IRR_NO_TRUTH && + bundle1.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) { + debug_str += "Blame from part 1: "; + debug_str += bundle1.debug_; + irr = bundle1.incorrect_result_reason_; + } + if (bundle2.incorrect_result_reason_ != IRR_CORRECT && + bundle2.incorrect_result_reason_ != IRR_NO_TRUTH && + bundle2.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) { + debug_str += "Blame from part 2: "; + debug_str += bundle2.debug_; + if (irr == IRR_CORRECT) { + irr = bundle2.incorrect_result_reason_; + } + else if (irr != bundle2.incorrect_result_reason_) { + irr = IRR_UNKNOWN; + } + } + incorrect_result_reason_ = irr; + if (irr != IRR_CORRECT && irr != IRR_NO_TRUTH) { + SetBlame(irr, debug_str, NULL, debug); + } +} + +// If a blob with the same bounding box as one of the truth character +// bounding boxes is not classified as the corresponding truth character +// blames character classifier for incorrect answer. +void BlamerBundle::BlameClassifier(const UNICHARSET& unicharset, + const TBOX& blob_box, + const BLOB_CHOICE_LIST& choices, + bool debug) { + if (!truth_has_char_boxes_ || + incorrect_result_reason_ != IRR_CORRECT) + return; // Nothing to do here. + + for (int b = 0; b < norm_truth_word_.length(); ++b) { + const TBOX &truth_box = norm_truth_word_.BlobBox(b); + // Note that we are more strict on the bounding box boundaries here + // than in other places (chopper, segmentation search), since we do + // not have the ability to check the previous and next bounding box. + if (blob_box.x_almost_equal(truth_box, norm_box_tolerance_ / 2)) { + bool found = false; + bool incorrect_adapted = false; + UNICHAR_ID incorrect_adapted_id = INVALID_UNICHAR_ID; + const char *truth_str = truth_text_[b].string(); + // We promise not to modify the list or its contents, using a + // const BLOB_CHOICE* below. + BLOB_CHOICE_IT choices_it(const_cast(&choices)); + for (choices_it.mark_cycle_pt(); !choices_it.cycled_list(); + choices_it.forward()) { + const BLOB_CHOICE* choice = choices_it.data(); + if (strcmp(truth_str, unicharset.get_normed_unichar( + choice->unichar_id())) == 0) { + found = true; + break; + } + else if (choice->IsAdapted()) { + incorrect_adapted = true; + incorrect_adapted_id = choice->unichar_id(); + } + } // end choices_it for loop + if (!found) { + STRING debug_str = "unichar "; + debug_str += truth_str; + debug_str += " not found in classification list"; + SetBlame(IRR_CLASSIFIER, debug_str, NULL, debug); + } + else if (incorrect_adapted) { + STRING debug_str = "better rating for adapted "; + debug_str += unicharset.id_to_unichar(incorrect_adapted_id); + debug_str += " than for correct "; + debug_str += truth_str; + SetBlame(IRR_ADAPTION, debug_str, NULL, debug); + } + break; + } + } // end iterating over blamer_bundle->norm_truth_word +} + +// Checks whether chops were made at all the character bounding box +// boundaries in word->truth_word. If not - blames the chopper for an +// incorrect answer. +void BlamerBundle::SetChopperBlame(const WERD_RES* word, bool debug) { + if (NoTruth() || !truth_has_char_boxes_ || + word->chopped_word->blobs.empty()) { + return; + } + STRING debug_str; + bool missing_chop = false; + int num_blobs = word->chopped_word->blobs.size(); + int box_index = 0; + int blob_index = 0; + inT16 truth_x = -1; + while (box_index < truth_word_.length() && blob_index < num_blobs) { + truth_x = norm_truth_word_.BlobBox(box_index).right(); + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + if (curr_blob->bounding_box().right() < truth_x - norm_box_tolerance_) { + ++blob_index; + continue; // encountered an extra chop, keep looking + } + else if (curr_blob->bounding_box().right() > + truth_x + norm_box_tolerance_) { + missing_chop = true; + break; + } + else { + ++blob_index; + } + } + if (missing_chop || box_index < norm_truth_word_.length()) { + STRING debug_str; + if (missing_chop) { + debug_str.add_str_int("Detected missing chop (tolerance=", + norm_box_tolerance_); + debug_str += ") at Bounding Box="; + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + curr_blob->bounding_box().print_to_str(&debug_str); + debug_str.add_str_int("\nNo chop for truth at x=", truth_x); + } + else { + debug_str.add_str_int("Missing chops for last ", + norm_truth_word_.length() - box_index); + debug_str += " truth box(es)"; + } + debug_str += "\nMaximally chopped word boxes:\n"; + for (blob_index = 0; blob_index < num_blobs; ++blob_index) { + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + curr_blob->bounding_box().print_to_str(&debug_str); + debug_str += '\n'; + } + debug_str += "Truth bounding boxes:\n"; + for (box_index = 0; box_index < norm_truth_word_.length(); ++box_index) { + norm_truth_word_.BlobBox(box_index).print_to_str(&debug_str); + debug_str += '\n'; + } + SetBlame(IRR_CHOPPER, debug_str, word->best_choice, debug); + } +} + +// Blames the classifier or the language model if, after running only the +// chopper, best_choice is incorrect and no blame has been yet set. +// Blames the classifier if best_choice is classifier's top choice and is a +// dictionary word (i.e. language model could not have helped). +// Otherwise, blames the language model (formerly permuter word adjustment). +void BlamerBundle::BlameClassifierOrLangModel( + const WERD_RES* word, + const UNICHARSET& unicharset, bool valid_permuter, bool debug) { + if (valid_permuter) { + // Find out whether best choice is a top choice. + best_choice_is_dict_and_top_choice_ = true; + for (int i = 0; i < word->best_choice->length(); ++i) { + BLOB_CHOICE_IT blob_choice_it(word->GetBlobChoices(i)); + ASSERT_HOST(!blob_choice_it.empty()); + BLOB_CHOICE *first_choice = NULL; + for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); + blob_choice_it.forward()) { // find first non-fragment choice + if (!(unicharset.get_fragment(blob_choice_it.data()->unichar_id()))) { + first_choice = blob_choice_it.data(); + break; + } + } + ASSERT_HOST(first_choice != NULL); + if (first_choice->unichar_id() != word->best_choice->unichar_id(i)) { + best_choice_is_dict_and_top_choice_ = false; + break; + } + } + } + STRING debug_str; + if (best_choice_is_dict_and_top_choice_) { + debug_str = "Best choice is: incorrect, top choice, dictionary word"; + debug_str += " with permuter "; + debug_str += word->best_choice->permuter_name(); + } + else { + debug_str = "Classifier/Old LM tradeoff is to blame"; + } + SetBlame(best_choice_is_dict_and_top_choice_ ? IRR_CLASSIFIER + : IRR_CLASS_OLD_LM_TRADEOFF, + debug_str, word->best_choice, debug); +} + +// Sets up the correct_segmentation_* to mark the correct bounding boxes. +void BlamerBundle::SetupCorrectSegmentation(const TWERD* word, bool debug) { + params_training_bundle_.StartHypothesisList(); + if (incorrect_result_reason_ != IRR_CORRECT || !truth_has_char_boxes_) + return; // Nothing to do here. + + STRING debug_str; + debug_str += "Blamer computing correct_segmentation_cols\n"; + int curr_box_col = 0; + int next_box_col = 0; + int num_blobs = word->NumBlobs(); + if (num_blobs == 0) return; // No blobs to play with. + int blob_index = 0; + inT16 next_box_x = word->blobs[blob_index]->bounding_box().right(); + for (int truth_idx = 0; blob_index < num_blobs && + truth_idx < norm_truth_word_.length(); + ++blob_index) { + ++next_box_col; + inT16 curr_box_x = next_box_x; + if (blob_index + 1 < num_blobs) + next_box_x = word->blobs[blob_index + 1]->bounding_box().right(); + inT16 truth_x = norm_truth_word_.BlobBox(truth_idx).right(); + debug_str.add_str_int("Box x coord vs. truth: ", curr_box_x); + debug_str.add_str_int(" ", truth_x); + debug_str += "\n"; + if (curr_box_x > (truth_x + norm_box_tolerance_)) { + break; // failed to find a matching box + } + else if (curr_box_x >= truth_x - norm_box_tolerance_ && // matched + (blob_index + 1 >= num_blobs || // next box can't be included + next_box_x > truth_x + norm_box_tolerance_)) { + correct_segmentation_cols_.push_back(curr_box_col); + correct_segmentation_rows_.push_back(next_box_col - 1); + ++truth_idx; + debug_str.add_str_int("col=", curr_box_col); + debug_str.add_str_int(" row=", next_box_col - 1); + debug_str += "\n"; + curr_box_col = next_box_col; + } + } + if (blob_index < num_blobs || // trailing blobs + correct_segmentation_cols_.length() != norm_truth_word_.length()) { + debug_str.add_str_int("Blamer failed to find correct segmentation" + " (tolerance=", norm_box_tolerance_); + if (blob_index >= num_blobs) debug_str += " blob == NULL"; + debug_str += ")\n"; + debug_str.add_str_int(" path length ", correct_segmentation_cols_.length()); + debug_str.add_str_int(" vs. truth ", norm_truth_word_.length()); + debug_str += "\n"; + SetBlame(IRR_UNKNOWN, debug_str, NULL, debug); + correct_segmentation_cols_.clear(); + correct_segmentation_rows_.clear(); + } +} + +// Returns true if a guided segmentation search is needed. +bool BlamerBundle::GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const { + return incorrect_result_reason_ == IRR_CORRECT && + !segsearch_is_looking_for_blame_ && + truth_has_char_boxes_ && + !ChoiceIsCorrect(best_choice); +} + +// Setup ready to guide the segmentation search to the correct segmentation. +// The callback pp_cb is used to avoid a cyclic dependency. +// It calls into LMPainPoints::GenerateForBlamer by pre-binding the +// WERD_RES, and the LMPainPoints itself. +// pp_cb must be a permanent callback, and should be deleted by the caller. +void BlamerBundle::InitForSegSearch(const WERD_CHOICE *best_choice, + MATRIX* ratings, UNICHAR_ID wildcard_id, + bool debug, STRING *debug_str, + TessResultCallback2* cb) { + segsearch_is_looking_for_blame_ = true; + if (debug) { + tprintf("segsearch starting to look for blame\n"); + } + // Fill pain points for any unclassifed blob corresponding to the + // correct segmentation state. + *debug_str += "Correct segmentation:\n"; + for (int idx = 0; idx < correct_segmentation_cols_.length(); ++idx) { + debug_str->add_str_int("col=", correct_segmentation_cols_[idx]); + debug_str->add_str_int(" row=", correct_segmentation_rows_[idx]); + *debug_str += "\n"; + if (!ratings->Classified(correct_segmentation_cols_[idx], + correct_segmentation_rows_[idx], + wildcard_id) && + !cb->Run(correct_segmentation_cols_[idx], + correct_segmentation_rows_[idx])) { + segsearch_is_looking_for_blame_ = false; + *debug_str += "\nFailed to insert pain point\n"; + SetBlame(IRR_SEGSEARCH_HEUR, *debug_str, best_choice, debug); + break; + } + } // end for blamer_bundle->correct_segmentation_cols/rows +} +// Returns true if the guided segsearch is in progress. +bool BlamerBundle::GuidedSegsearchStillGoing() const { + return segsearch_is_looking_for_blame_; +} + +// The segmentation search has ended. Sets the blame appropriately. +void BlamerBundle::FinishSegSearch(const WERD_CHOICE *best_choice, + bool debug, STRING *debug_str) { + // If we are still looking for blame (i.e. best_choice is incorrect, but a + // path representing the correct segmentation could be constructed), we can + // blame segmentation search pain point prioritization if the rating of the + // path corresponding to the correct segmentation is better than that of + // best_choice (i.e. language model would have done the correct thing, but + // because of poor pain point prioritization the correct segmentation was + // never explored). Otherwise we blame the tradeoff between the language model + // and the classifier, since even after exploring the path corresponding to + // the correct segmentation incorrect best_choice would have been chosen. + // One special case when we blame the classifier instead is when best choice + // is incorrect, but it is a dictionary word and it classifier's top choice. + if (segsearch_is_looking_for_blame_) { + segsearch_is_looking_for_blame_ = false; + if (best_choice_is_dict_and_top_choice_) { + *debug_str = "Best choice is: incorrect, top choice, dictionary word"; + *debug_str += " with permuter "; + *debug_str += best_choice->permuter_name(); + SetBlame(IRR_CLASSIFIER, *debug_str, best_choice, debug); + } + else if (best_correctly_segmented_rating_ < + best_choice->rating()) { + *debug_str += "Correct segmentation state was not explored"; + SetBlame(IRR_SEGSEARCH_PP, *debug_str, best_choice, debug); + } + else { + if (best_correctly_segmented_rating_ >= + WERD_CHOICE::kBadRating) { + *debug_str += "Correct segmentation paths were pruned by LM\n"; + } + else { + debug_str->add_str_double("Best correct segmentation rating ", + best_correctly_segmented_rating_); + debug_str->add_str_double(" vs. best choice rating ", + best_choice->rating()); + } + SetBlame(IRR_CLASS_LM_TRADEOFF, *debug_str, best_choice, debug); + } + } +} + +// If the bundle is null or still does not indicate the correct result, +// fix it and use some backup reason for the blame. +void BlamerBundle::LastChanceBlame(bool debug, WERD_RES* word) { + if (word->blamer_bundle == NULL) { + word->blamer_bundle = new BlamerBundle(); + word->blamer_bundle->SetBlame(IRR_PAGE_LAYOUT, "LastChanceBlame", + word->best_choice, debug); + } + else if (word->blamer_bundle->incorrect_result_reason_ == IRR_NO_TRUTH) { + word->blamer_bundle->SetBlame(IRR_NO_TRUTH, "Rejected truth", + word->best_choice, debug); + } + else { + bool correct = word->blamer_bundle->ChoiceIsCorrect(word->best_choice); + IncorrectResultReason irr = word->blamer_bundle->incorrect_result_reason_; + if (irr == IRR_CORRECT && !correct) { + STRING debug_str = "Choice is incorrect after recognition"; + word->blamer_bundle->SetBlame(IRR_UNKNOWN, debug_str, word->best_choice, + debug); + } + else if (irr != IRR_CORRECT && correct) { + if (debug) { + tprintf("Corrected %s\n", word->blamer_bundle->debug_.string()); + } + word->blamer_bundle->incorrect_result_reason_ = IRR_CORRECT; + word->blamer_bundle->debug_ = ""; + } + } +} + +// Sets the misadaption debug if this word is incorrect, as this word is +// being adapted to. +void BlamerBundle::SetMisAdaptionDebug(const WERD_CHOICE *best_choice, + bool debug) { + if (incorrect_result_reason_ != IRR_NO_TRUTH && + !ChoiceIsCorrect(best_choice)) { + misadaption_debug_ = "misadapt to word ("; + misadaption_debug_ += best_choice->permuter_name(); + misadaption_debug_ += "): "; + FillDebugString("", best_choice, &misadaption_debug_); + if (debug) { + tprintf("%s\n", misadaption_debug_.string()); + } + } +} + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.h new file mode 100644 index 0000000..8b33b7f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blamer.h @@ -0,0 +1,333 @@ +/////////////////////////////////////////////////////////////////////// +// File: blamer.h +// Description: Module allowing precise error causes to be allocated. +// Author: Rike Antonova +// Refactored: Ray Smith +// Created: Mon Feb 04 14:37:01 PST 2013 +// +// (C) Copyright 2013, 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_CCSTRUCT_BLAMER_H_ +#define TESSERACT_CCSTRUCT_BLAMER_H_ + +#include +#include "boxword.h" +#include "genericvector.h" +#include "matrix.h" +#include "params_training_featdef.h" +#include "ratngs.h" +#include "strngs.h" +#include "tesscallback.h" + +static const inT16 kBlamerBoxTolerance = 5; + +// Enum for expressing the source of error. +// Note: Please update kIncorrectResultReasonNames when modifying this enum. +enum IncorrectResultReason { + // The text recorded in best choice == truth text + IRR_CORRECT, + // Either: Top choice is incorrect and is a dictionary word (language model + // is unlikely to help correct such errors, so blame the classifier). + // Or: the correct unichar was not included in shortlist produced by the + // classifier at all. + IRR_CLASSIFIER, + // Chopper have not found one or more splits that correspond to the correct + // character bounding boxes recorded in BlamerBundle::truth_word. + IRR_CHOPPER, + // Classifier did include correct unichars for each blob in the correct + // segmentation, however its rating could have been too bad to allow the + // language model to pull out the correct choice. On the other hand the + // strength of the language model might have been too weak to favor the + // correct answer, this we call this case a classifier-language model + // tradeoff error. + IRR_CLASS_LM_TRADEOFF, + // Page layout failed to produce the correct bounding box. Blame page layout + // if the truth was not found for the word, which implies that the bounding + // box of the word was incorrect (no truth word had a similar bounding box). + IRR_PAGE_LAYOUT, + // SegSearch heuristic prevented one or more blobs from the correct + // segmentation state to be classified (e.g. the blob was too wide). + IRR_SEGSEARCH_HEUR, + // The correct segmentaiton state was not explored because of poor SegSearch + // pain point prioritization. We blame SegSearch pain point prioritization + // if the best rating of a choice constructed from correct segmentation is + // better than that of the best choice (i.e. if we got to explore the correct + // segmentation state, language model would have picked the correct choice). + IRR_SEGSEARCH_PP, + // Same as IRR_CLASS_LM_TRADEOFF, but used when we only run chopper on a word, + // and thus use the old language model (permuters). + // TODO(antonova): integrate the new language mode with chopper + IRR_CLASS_OLD_LM_TRADEOFF, + // If there is an incorrect adaptive template match with a better score than + // a correct one (either pre-trained or adapted), mark this as adaption error. + IRR_ADAPTION, + // split_and_recog_word() failed to find a suitable split in truth. + IRR_NO_TRUTH_SPLIT, + // Truth is not available for this word (e.g. when words in corrected content + // file are turned into ~~~~ because an appropriate alignment was not found. + IRR_NO_TRUTH, + // The text recorded in best choice != truth text, but none of the above + // reasons are set. + IRR_UNKNOWN, + + IRR_NUM_REASONS +}; + +// Blamer-related information to determine the source of errors. +struct BlamerBundle { + static const char *IncorrectReasonName(IncorrectResultReason irr); + BlamerBundle() : truth_has_char_boxes_(false), + incorrect_result_reason_(IRR_CORRECT), + lattice_data_(NULL) { + ClearResults(); + } + BlamerBundle(const BlamerBundle &other) { + this->CopyTruth(other); + this->CopyResults(other); + } + ~BlamerBundle() { delete[] lattice_data_; } + + // Accessors. + STRING TruthString() const { + STRING truth_str; + for (int i = 0; i < truth_text_.length(); ++i) + truth_str += truth_text_[i]; + return truth_str; + } + IncorrectResultReason incorrect_result_reason() const { + return incorrect_result_reason_; + } + bool NoTruth() const { + return incorrect_result_reason_ == IRR_NO_TRUTH || + incorrect_result_reason_ == IRR_PAGE_LAYOUT; + } + bool HasDebugInfo() const { + return debug_.length() > 0 || misadaption_debug_.length() > 0; + } + const STRING& debug() const { + return debug_; + } + const STRING& misadaption_debug() const { + return misadaption_debug_; + } + void UpdateBestRating(float rating) { + if (rating < best_correctly_segmented_rating_) + best_correctly_segmented_rating_ = rating; + } + int correct_segmentation_length() const { + return correct_segmentation_cols_.length(); + } + // Returns true if the given ratings matrix col,row position is included + // in the correct segmentation path at the given index. + bool MatrixPositionCorrect(int index, const MATRIX_COORD& coord) { + return correct_segmentation_cols_[index] == coord.col && + correct_segmentation_rows_[index] == coord.row; + } + void set_best_choice_is_dict_and_top_choice(bool value) { + best_choice_is_dict_and_top_choice_ = value; + } + const char* lattice_data() const { + return lattice_data_; + } + int lattice_size() const { + return lattice_size_; // size of lattice_data in bytes + } + void set_lattice_data(const char* data, int size) { + lattice_size_ = size; + delete[] lattice_data_; + lattice_data_ = new char[lattice_size_]; + memcpy(lattice_data_, data, lattice_size_); + } + const tesseract::ParamsTrainingBundle& params_training_bundle() const { + return params_training_bundle_; + } + // Adds a new ParamsTrainingHypothesis to the current hypothesis list. + void AddHypothesis(const tesseract::ParamsTrainingHypothesis& hypo) { + params_training_bundle_.AddHypothesis(hypo); + } + + // Functions to setup the blamer. + // Whole word string, whole word bounding box. + void SetWordTruth(const UNICHARSET& unicharset, + const char* truth_str, const TBOX& word_box); + // Single "character" string, "character" bounding box. + // May be called multiple times to indicate the characters in a word. + void SetSymbolTruth(const UNICHARSET& unicharset, + const char* char_str, const TBOX& char_box); + // Marks that there is something wrong with the truth text, like it contains + // reject characters. + void SetRejectedTruth(); + + // Returns true if the provided word_choice is correct. + bool ChoiceIsCorrect(const WERD_CHOICE* word_choice) const; + + void ClearResults() { + norm_truth_word_.DeleteAllBoxes(); + norm_box_tolerance_ = 0; + if (!NoTruth()) incorrect_result_reason_ = IRR_CORRECT; + debug_ = ""; + segsearch_is_looking_for_blame_ = false; + best_correctly_segmented_rating_ = WERD_CHOICE::kBadRating; + correct_segmentation_cols_.clear(); + correct_segmentation_rows_.clear(); + best_choice_is_dict_and_top_choice_ = false; + delete[] lattice_data_; + lattice_data_ = NULL; + lattice_size_ = 0; + } + void CopyTruth(const BlamerBundle &other) { + truth_has_char_boxes_ = other.truth_has_char_boxes_; + truth_word_ = other.truth_word_; + truth_text_ = other.truth_text_; + incorrect_result_reason_ = + (other.NoTruth() ? other.incorrect_result_reason_ : IRR_CORRECT); + } + void CopyResults(const BlamerBundle &other) { + norm_truth_word_ = other.norm_truth_word_; + norm_box_tolerance_ = other.norm_box_tolerance_; + incorrect_result_reason_ = other.incorrect_result_reason_; + segsearch_is_looking_for_blame_ = other.segsearch_is_looking_for_blame_; + best_correctly_segmented_rating_ = other.best_correctly_segmented_rating_; + correct_segmentation_cols_ = other.correct_segmentation_cols_; + correct_segmentation_rows_ = other.correct_segmentation_rows_; + best_choice_is_dict_and_top_choice_ = + other.best_choice_is_dict_and_top_choice_; + if (other.lattice_data_ != NULL) { + lattice_data_ = new char[other.lattice_size_]; + memcpy(lattice_data_, other.lattice_data_, other.lattice_size_); + lattice_size_ = other.lattice_size_; + } + else { + lattice_data_ = NULL; + } + } + const char *IncorrectReason() const; + + // Appends choice and truth details to the given debug string. + void FillDebugString(const STRING &msg, const WERD_CHOICE *choice, + STRING *debug); + + // Sets up the norm_truth_word from truth_word using the given DENORM. + void SetupNormTruthWord(const DENORM& denorm); + + // Splits *this into two pieces in bundle1 and bundle2 (preallocated, empty + // bundles) where the right edge/ of the left-hand word is word1_right, + // and the left edge of the right-hand word is word2_left. + void SplitBundle(int word1_right, int word2_left, bool debug, + BlamerBundle* bundle1, BlamerBundle* bundle2) const; + // "Joins" the blames from bundle1 and bundle2 into *this. + void JoinBlames(const BlamerBundle& bundle1, const BlamerBundle& bundle2, + bool debug); + + // If a blob with the same bounding box as one of the truth character + // bounding boxes is not classified as the corresponding truth character + // blames character classifier for incorrect answer. + void BlameClassifier(const UNICHARSET& unicharset, + const TBOX& blob_box, + const BLOB_CHOICE_LIST& choices, + bool debug); + + + // Checks whether chops were made at all the character bounding box + // boundaries in word->truth_word. If not - blames the chopper for an + // incorrect answer. + void SetChopperBlame(const WERD_RES* word, bool debug); + // Blames the classifier or the language model if, after running only the + // chopper, best_choice is incorrect and no blame has been yet set. + // Blames the classifier if best_choice is classifier's top choice and is a + // dictionary word (i.e. language model could not have helped). + // Otherwise, blames the language model (formerly permuter word adjustment). + void BlameClassifierOrLangModel( + const WERD_RES* word, + const UNICHARSET& unicharset, bool valid_permuter, bool debug); + // Sets up the correct_segmentation_* to mark the correct bounding boxes. + void SetupCorrectSegmentation(const TWERD* word, bool debug); + + // Returns true if a guided segmentation search is needed. + bool GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const; + // Setup ready to guide the segmentation search to the correct segmentation. + // The callback pp_cb is used to avoid a cyclic dependency. + // It calls into LMPainPoints::GenerateForBlamer by pre-binding the + // WERD_RES, and the LMPainPoints itself. + // pp_cb must be a permanent callback, and should be deleted by the caller. + void InitForSegSearch(const WERD_CHOICE *best_choice, + MATRIX* ratings, UNICHAR_ID wildcard_id, + bool debug, STRING *debug_str, + TessResultCallback2* pp_cb); + // Returns true if the guided segsearch is in progress. + bool GuidedSegsearchStillGoing() const; + // The segmentation search has ended. Sets the blame appropriately. + void FinishSegSearch(const WERD_CHOICE *best_choice, + bool debug, STRING *debug_str); + + // If the bundle is null or still does not indicate the correct result, + // fix it and use some backup reason for the blame. + static void LastChanceBlame(bool debug, WERD_RES* word); + + // Sets the misadaption debug if this word is incorrect, as this word is + // being adapted to. + void SetMisAdaptionDebug(const WERD_CHOICE *best_choice, bool debug); + +private: + void SetBlame(IncorrectResultReason irr, const STRING &msg, + const WERD_CHOICE *choice, bool debug) { + incorrect_result_reason_ = irr; + debug_ = IncorrectReason(); + debug_ += " to blame: "; + FillDebugString(msg, choice, &debug_); + if (debug) tprintf("SetBlame(): %s", debug_.string()); + } + +private: + // Set to true when bounding boxes for individual unichars are recorded. + bool truth_has_char_boxes_; + // The true_word (in the original image coordinate space) contains ground + // truth bounding boxes for this WERD_RES. + tesseract::BoxWord truth_word_; + // Same as above, but in normalized coordinates + // (filled in by WERD_RES::SetupForRecognition()). + tesseract::BoxWord norm_truth_word_; + // Tolerance for bounding box comparisons in normalized space. + int norm_box_tolerance_; + // Contains ground truth unichar for each of the bounding boxes in truth_word. + GenericVector truth_text_; + // The reason for incorrect OCR result. + IncorrectResultReason incorrect_result_reason_; + // Debug text associated with the blame. + STRING debug_; + // Misadaption debug information (filled in if this word was misadapted to). + STRING misadaption_debug_; + // Variables used by the segmentation search when looking for the blame. + // Set to true while segmentation search is continued after the usual + // termination condition in order to look for the blame. + bool segsearch_is_looking_for_blame_; + // Best rating for correctly segmented path + // (set and used by SegSearch when looking for blame). + float best_correctly_segmented_rating_; + // Vectors populated by SegSearch to indicate column and row indices that + // correspond to blobs with correct bounding boxes. + GenericVector correct_segmentation_cols_; + GenericVector correct_segmentation_rows_; + // Set to true if best choice is a dictionary word and + // classifier's top choice. + bool best_choice_is_dict_and_top_choice_; + // Serialized segmentation search lattice. + char *lattice_data_; + int lattice_size_; // size of lattice_data in bytes + // Information about hypotheses (paths) explored by the segmentation search. + tesseract::ParamsTrainingBundle params_training_bundle_; +}; + + +#endif // TESSERACT_CCSTRUCT_BLAMER_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blckerr.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/blckerr.h new file mode 100644 index 0000000..e306163 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blckerr.h @@ -0,0 +1,29 @@ +/********************************************************************** + * File: blckerr.h (Formerly blockerr.h) + * Description: Error codes for the page block classes. + * Author: Ray Smith + * Created: Tue Mar 19 17:43:30 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLCKERR_H +#define BLCKERR_H + +#include "errcode.h" + +const ERRCODE BADBLOCKLINE = "Y coordinate in block out of bounds"; +const ERRCODE LOSTBLOCKLINE = "Can't find rectangle for line"; +const ERRCODE ILLEGAL_GRADIENT = "Gradient wrong side of edge step!"; +const ERRCODE WRONG_WORD = "Word doesn't have blobs of that type"; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.cpp new file mode 100644 index 0000000..734d28a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.cpp @@ -0,0 +1,1093 @@ +/********************************************************************** + * File: blobbox.cpp (Formerly blobnbox.c) + * Description: Code for the textord blob class. + * Author: Ray Smith + * Created: Thu Jul 30 09:08:51 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + // Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "blobbox.h" +#include "allheaders.h" +#include "blobs.h" +#include "helpers.h" +#include "normalis.h" + +#define PROJECTION_MARGIN 10 //arbitrary +#define EXTERN + +ELISTIZE(BLOBNBOX) +ELIST2IZE(TO_ROW) +ELISTIZE(TO_BLOCK) + +// Up to 30 degrees is allowed for rotations of diacritic blobs. +const double kCosSmallAngle = 0.866; +// Min aspect ratio for a joined word to indicate an obvious flow direction. +const double kDefiniteAspectRatio = 2.0; +// Multiple of short length in perimeter to make a joined word. +const double kComplexShapePerimeterRatio = 1.5; +// Min multiple of linesize for medium-sized blobs in ReFilterBlobs. +const double kMinMediumSizeRatio = 0.25; +// Max multiple of linesize for medium-sized blobs in ReFilterBlobs. +const double kMaxMediumSizeRatio = 4.0; + +// Rotates the box and the underlying blob. +void BLOBNBOX::rotate(FCOORD rotation) { + cblob_ptr->rotate(rotation); + rotate_box(rotation); + compute_bounding_box(); +} + +// Reflect the box in the y-axis, leaving the underlying blob untouched. +void BLOBNBOX::reflect_box_in_y_axis() { + int left = -box.right(); + box.set_right(-box.left()); + box.set_left(left); +} + +// Rotates the box by the angle given by rotation. +// If the blob is a diacritic, then only small rotations for skew +// correction can be applied. +void BLOBNBOX::rotate_box(FCOORD rotation) { + if (IsDiacritic()) { + ASSERT_HOST(rotation.x() >= kCosSmallAngle) + ICOORD top_pt((box.left() + box.right()) / 2, base_char_top_); + ICOORD bottom_pt(top_pt.x(), base_char_bottom_); + top_pt.rotate(rotation); + base_char_top_ = top_pt.y(); + bottom_pt.rotate(rotation); + base_char_bottom_ = bottom_pt.y(); + box.rotate(rotation); + } + else { + box.rotate(rotation); + set_diacritic_box(box); + } +} + +/********************************************************************** + * BLOBNBOX::merge + * + * Merge this blob with the given blob, which should be after this. + **********************************************************************/ +void BLOBNBOX::merge( //merge blobs + BLOBNBOX *nextblob //blob to join with +) { + box += nextblob->box; //merge boxes + set_diacritic_box(box); + nextblob->joined = TRUE; +} + + +// Merge this with other, taking the outlines from other. +// Other is not deleted, but left for the caller to handle. +void BLOBNBOX::really_merge(BLOBNBOX* other) { + if (cblob_ptr != NULL && other->cblob_ptr != NULL) { + C_OUTLINE_IT ol_it(cblob_ptr->out_list()); + ol_it.add_list_after(other->cblob_ptr->out_list()); + } + compute_bounding_box(); +} + + +/********************************************************************** + * BLOBNBOX::chop + * + * Chop this blob into equal sized pieces using the x height as a guide. + * The blob is not actually chopped. Instead, fake blobs are inserted + * with the relevant bounding boxes. + **********************************************************************/ + +void BLOBNBOX::chop( //chop blobs + BLOBNBOX_IT *start_it, //location of this + BLOBNBOX_IT *end_it, //iterator + FCOORD rotation, //for landscape + float xheight //of line +) { + inT16 blobcount; //no of blobs + BLOBNBOX *newblob; //fake blob + BLOBNBOX *blob; //current blob + inT16 blobindex; //number of chop + inT16 leftx; //left edge of blob + float blobwidth; //width of each + float rightx; //right edge to scan + float ymin, ymax; //limits of new blob + float test_ymin, test_ymax; //limits of part blob + ICOORD bl, tr; //corners of box + BLOBNBOX_IT blob_it; //blob iterator + + //get no of chops + blobcount = (inT16)floor(box.width() / xheight); + if (blobcount > 1 && cblob_ptr != NULL) { + //width of each + blobwidth = (float)(box.width() + 1) / blobcount; + for (blobindex = blobcount - 1, rightx = box.right(); + blobindex >= 0; blobindex--, rightx -= blobwidth) { + ymin = (float)MAX_INT32; + ymax = (float)-MAX_INT32; + blob_it = *start_it; + do { + blob = blob_it.data(); + find_cblob_vlimits(blob->cblob_ptr, rightx - blobwidth, + rightx, + /*rotation, */ test_ymin, test_ymax); + blob_it.forward(); + UpdateRange(test_ymin, test_ymax, &ymin, &ymax); + } while (blob != end_it->data()); + if (ymin < ymax) { + leftx = (inT16)floor(rightx - blobwidth); + if (leftx < box.left()) + leftx = box.left(); //clip to real box + bl = ICOORD(leftx, (inT16)floor(ymin)); + tr = ICOORD((inT16)ceil(rightx), (inT16)ceil(ymax)); + if (blobindex == 0) + box = TBOX(bl, tr); //change box + else { + newblob = new BLOBNBOX; + //box is all it has + newblob->box = TBOX(bl, tr); + //stay on current + newblob->base_char_top_ = tr.y(); + newblob->base_char_bottom_ = bl.y(); + end_it->add_after_stay_put(newblob); + } + } + } + } +} + +// Returns the box gaps between this and its neighbours_ in an array +// indexed by BlobNeighbourDir. +void BLOBNBOX::NeighbourGaps(int gaps[BND_COUNT]) const { + for (int dir = 0; dir < BND_COUNT; ++dir) { + gaps[dir] = MAX_INT16; + BLOBNBOX* neighbour = neighbours_[dir]; + if (neighbour != NULL) { + const TBOX& n_box = neighbour->bounding_box(); + if (dir == BND_LEFT || dir == BND_RIGHT) { + gaps[dir] = box.x_gap(n_box); + } + else { + gaps[dir] = box.y_gap(n_box); + } + } + } +} +// Returns the min and max horizontal and vertical gaps (from NeighbourGaps) +// modified so that if the max exceeds the max dimension of the blob, and +// the min is less, the max is replaced with the min. +// The objective is to catch cases where there is only a single neighbour +// and avoid reporting the other gap as a ridiculously large number +void BLOBNBOX::MinMaxGapsClipped(int* h_min, int* h_max, + int* v_min, int* v_max) const { + int max_dimension = MAX(box.width(), box.height()); + int gaps[BND_COUNT]; + NeighbourGaps(gaps); + *h_min = MIN(gaps[BND_LEFT], gaps[BND_RIGHT]); + *h_max = MAX(gaps[BND_LEFT], gaps[BND_RIGHT]); + if (*h_max > max_dimension && *h_min < max_dimension) *h_max = *h_min; + *v_min = MIN(gaps[BND_ABOVE], gaps[BND_BELOW]); + *v_max = MAX(gaps[BND_ABOVE], gaps[BND_BELOW]); + if (*v_max > max_dimension && *v_min < max_dimension) *v_max = *v_min; +} + +// NULLs out any neighbours that are DeletableNoise to remove references. +void BLOBNBOX::CleanNeighbours() { + for (int dir = 0; dir < BND_COUNT; ++dir) { + BLOBNBOX* neighbour = neighbours_[dir]; + if (neighbour != NULL && neighbour->DeletableNoise()) { + neighbours_[dir] = NULL; + good_stroke_neighbours_[dir] = false; + } + } +} + +// Returns positive if there is at least one side neighbour that has a similar +// stroke width and is not on the other side of a rule line. +int BLOBNBOX::GoodTextBlob() const { + int score = 0; + for (int dir = 0; dir < BND_COUNT; ++dir) { + BlobNeighbourDir bnd = static_cast(dir); + if (good_stroke_neighbour(bnd)) + ++score; + } + return score; +} + +// Returns the number of side neighbours that are of type BRT_NOISE. +int BLOBNBOX::NoisyNeighbours() const { + int count = 0; + for (int dir = 0; dir < BND_COUNT; ++dir) { + BlobNeighbourDir bnd = static_cast(dir); + BLOBNBOX* blob = neighbour(bnd); + if (blob != NULL && blob->region_type() == BRT_NOISE) + ++count; + } + return count; +} + +// Returns true, and sets vert_possible/horz_possible if the blob has some +// feature that makes it individually appear to flow one way. +// eg if it has a high aspect ratio, yet has a complex shape, such as a +// joined word in Latin, Arabic, or Hindi, rather than being a -, I, l, 1 etc. +bool BLOBNBOX::DefiniteIndividualFlow() { + if (cblob() == NULL) return false; + int box_perimeter = 2 * (box.height() + box.width()); + if (box.width() > box.height() * kDefiniteAspectRatio) { + // Attempt to distinguish a wide joined word from a dash. + // If it is a dash, then its perimeter is approximately + // 2 * (box width + stroke width), but more if the outline is noisy, + // so perimeter - 2*(box width + stroke width) should be close to zero. + // A complex shape such as a joined word should have a much larger value. + int perimeter = cblob()->perimeter(); + if (vert_stroke_width() > 0 || perimeter <= 0) + perimeter -= 2 * vert_stroke_width(); + else + perimeter -= 4 * cblob()->area() / perimeter; + perimeter -= 2 * box.width(); + // Use a multiple of the box perimeter as a threshold. + if (perimeter > kComplexShapePerimeterRatio * box_perimeter) { + set_vert_possible(false); + set_horz_possible(true); + return true; + } + } + if (box.height() > box.width() * kDefiniteAspectRatio) { + // As above, but for a putative vertical word vs a I/1/l. + int perimeter = cblob()->perimeter(); + if (horz_stroke_width() > 0 || perimeter <= 0) + perimeter -= 2 * horz_stroke_width(); + else + perimeter -= 4 * cblob()->area() / perimeter; + perimeter -= 2 * box.height(); + if (perimeter > kComplexShapePerimeterRatio * box_perimeter) { + set_vert_possible(true); + set_horz_possible(false); + return true; + } + } + return false; +} + +// Returns true if there is no tabstop violation in merging this and other. +bool BLOBNBOX::ConfirmNoTabViolation(const BLOBNBOX& other) const { + if (box.left() < other.box.left() && box.left() < other.left_rule_) + return false; + if (other.box.left() < box.left() && other.box.left() < left_rule_) + return false; + if (box.right() > other.box.right() && box.right() > other.right_rule_) + return false; + if (other.box.right() > box.right() && other.box.right() > right_rule_) + return false; + return true; +} + +// Returns true if other has a similar stroke width to this. +bool BLOBNBOX::MatchingStrokeWidth(const BLOBNBOX& other, + double fractional_tolerance, + double constant_tolerance) const { + // The perimeter-based width is used as a backup in case there is + // no information in the blob. + double p_width = area_stroke_width(); + double n_p_width = other.area_stroke_width(); + float h_tolerance = horz_stroke_width_ * fractional_tolerance + + constant_tolerance; + float v_tolerance = vert_stroke_width_ * fractional_tolerance + + constant_tolerance; + double p_tolerance = p_width * fractional_tolerance + + constant_tolerance; + bool h_zero = horz_stroke_width_ == 0.0f || other.horz_stroke_width_ == 0.0f; + bool v_zero = vert_stroke_width_ == 0.0f || other.vert_stroke_width_ == 0.0f; + bool h_ok = !h_zero && NearlyEqual(horz_stroke_width_, + other.horz_stroke_width_, h_tolerance); + bool v_ok = !v_zero && NearlyEqual(vert_stroke_width_, + other.vert_stroke_width_, v_tolerance); + bool p_ok = h_zero && v_zero && NearlyEqual(p_width, n_p_width, p_tolerance); + // For a match, at least one of the horizontal and vertical widths + // must match, and the other one must either match or be zero. + // Only if both are zero will we look at the perimeter metric. + return p_ok || ((v_ok || h_ok) && (h_ok || h_zero) && (v_ok || v_zero)); +} + +// Returns a bounding box of the outline contained within the +// given horizontal range. +TBOX BLOBNBOX::BoundsWithinLimits(int left, int right) { + FCOORD no_rotation(1.0f, 0.0f); + float top = box.top(); + float bottom = box.bottom(); + if (cblob_ptr != NULL) { + find_cblob_limits(cblob_ptr, static_cast(left), + static_cast(right), no_rotation, + bottom, top); + } + + if (top < bottom) { + top = box.top(); + bottom = box.bottom(); + } + FCOORD bot_left(left, bottom); + FCOORD top_right(right, top); + TBOX shrunken_box(bot_left); + TBOX shrunken_box2(top_right); + shrunken_box += shrunken_box2; + return shrunken_box; +} + +// Estimates and stores the baseline position based on the shape of the +// outline. +void BLOBNBOX::EstimateBaselinePosition() { + baseline_y_ = box.bottom(); // The default. + if (cblob_ptr == NULL) return; + baseline_y_ = cblob_ptr->EstimateBaselinePosition(); +} + +// Helper to call CleanNeighbours on all blobs on the list. +void BLOBNBOX::CleanNeighbours(BLOBNBOX_LIST* blobs) { + BLOBNBOX_IT blob_it(blobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + blob_it.data()->CleanNeighbours(); + } +} + +// Helper to delete all the deletable blobs on the list. +void BLOBNBOX::DeleteNoiseBlobs(BLOBNBOX_LIST* blobs) { + BLOBNBOX_IT blob_it(blobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + BLOBNBOX* blob = blob_it.data(); + if (blob->DeletableNoise()) { + delete blob->cblob(); + delete blob_it.extract(); + } + } +} + +// Helper to compute edge offsets for all the blobs on the list. +// See coutln.h for an explanation of edge offsets. +void BLOBNBOX::ComputeEdgeOffsets(Pix* thresholds, Pix* grey, + BLOBNBOX_LIST* blobs) { + int grey_height = 0; + int thr_height = 0; + int scale_factor = 1; + if (thresholds != NULL && grey != NULL) { + grey_height = pixGetHeight(grey); + thr_height = pixGetHeight(thresholds); + scale_factor = + IntCastRounded(static_cast(grey_height) / thr_height); + } + BLOBNBOX_IT blob_it(blobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + BLOBNBOX* blob = blob_it.data(); + if (blob->cblob() != NULL) { + // Get the threshold that applies to this blob. + l_uint32 threshold = 128; + if (thresholds != NULL && grey != NULL) { + const TBOX& box = blob->cblob()->bounding_box(); + // Transform the coordinates if required. + TPOINT pt((box.left() + box.right()) / 2, + (box.top() + box.bottom()) / 2); + pixGetPixel(thresholds, pt.x / scale_factor, + thr_height - 1 - pt.y / scale_factor, &threshold); + } + blob->cblob()->ComputeEdgeOffsets(threshold, grey); + } + } +} + + +#ifndef GRAPHICS_DISABLED +// Helper to draw all the blobs on the list in the given body_colour, +// with child outlines in the child_colour. +void BLOBNBOX::PlotBlobs(BLOBNBOX_LIST* list, + ScrollView::Color body_colour, + ScrollView::Color child_colour, + ScrollView* win) { + BLOBNBOX_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot(win, body_colour, child_colour); + } +} + +// Helper to draw only DeletableNoise blobs (unowned, BRT_NOISE) on the +// given list in the given body_colour, with child outlines in the +// child_colour. +void BLOBNBOX::PlotNoiseBlobs(BLOBNBOX_LIST* list, + ScrollView::Color body_colour, + ScrollView::Color child_colour, + ScrollView* win) { + BLOBNBOX_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + BLOBNBOX* blob = it.data(); + if (blob->DeletableNoise()) + blob->plot(win, body_colour, child_colour); + } +} + +ScrollView::Color BLOBNBOX::TextlineColor(BlobRegionType region_type, + BlobTextFlowType flow_type) { + switch (region_type) { + case BRT_HLINE: + return ScrollView::BROWN; + case BRT_VLINE: + return ScrollView::DARK_GREEN; + case BRT_RECTIMAGE: + return ScrollView::RED; + case BRT_POLYIMAGE: + return ScrollView::ORANGE; + case BRT_UNKNOWN: + return flow_type == BTFT_NONTEXT ? ScrollView::CYAN : ScrollView::WHITE; + case BRT_VERT_TEXT: + if (flow_type == BTFT_STRONG_CHAIN || flow_type == BTFT_TEXT_ON_IMAGE) + return ScrollView::GREEN; + if (flow_type == BTFT_CHAIN) + return ScrollView::LIME_GREEN; + return ScrollView::YELLOW; + case BRT_TEXT: + if (flow_type == BTFT_STRONG_CHAIN) + return ScrollView::BLUE; + if (flow_type == BTFT_TEXT_ON_IMAGE) + return ScrollView::LIGHT_BLUE; + if (flow_type == BTFT_CHAIN) + return ScrollView::MEDIUM_BLUE; + if (flow_type == BTFT_LEADER) + return ScrollView::WHEAT; + if (flow_type == BTFT_NONTEXT) + return ScrollView::PINK; + return ScrollView::MAGENTA; + default: + return ScrollView::GREY; + } +} + +// Keep in sync with BlobRegionType. +ScrollView::Color BLOBNBOX::BoxColor() const { + return TextlineColor(region_type_, flow_); +} + +void BLOBNBOX::plot(ScrollView* window, // window to draw in + ScrollView::Color blob_colour, // for outer bits + ScrollView::Color child_colour) { // for holes + if (cblob_ptr != NULL) + cblob_ptr->plot(window, blob_colour, child_colour); +} +#endif +/********************************************************************** + * find_cblob_limits + * + * Scan the outlines of the cblob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_cblob_limits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax) { + inT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list(); + + ymin = (float)MAX_INT32; + ymax = (float)-MAX_INT32; + for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) { + outline = out_it.data(); + pos = outline->start_pos(); //get coords + pos.rotate(rotation); + for (stepindex = 0; stepindex < outline->pathlength(); stepindex++) { + //inside + if (pos.x() >= leftx && pos.x() <= rightx) { + UpdateRange(pos.y(), &ymin, &ymax); + } + vec = outline->step(stepindex); + vec.rotate(rotation); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * find_cblob_vlimits + * + * Scan the outlines of the cblob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_cblob_vlimits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + float &ymin, //output y limits + float &ymax) { + inT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list(); + + ymin = (float)MAX_INT32; + ymax = (float)-MAX_INT32; + for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) { + outline = out_it.data(); + pos = outline->start_pos(); //get coords + for (stepindex = 0; stepindex < outline->pathlength(); stepindex++) { + //inside + if (pos.x() >= leftx && pos.x() <= rightx) { + UpdateRange(pos.y(), &ymin, &ymax); + } + vec = outline->step(stepindex); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * find_cblob_hlimits + * + * Scan the outlines of the cblob to locate the x min and max + * between the given y limits. + **********************************************************************/ + +void find_cblob_hlimits( //get x limits + C_BLOB *blob, //blob to search + float bottomy, //y limits + float topy, + float &xmin, //output x limits + float &xmax) { + inT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list(); + + xmin = (float)MAX_INT32; + xmax = (float)-MAX_INT32; + for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) { + outline = out_it.data(); + pos = outline->start_pos(); //get coords + for (stepindex = 0; stepindex < outline->pathlength(); stepindex++) { + //inside + if (pos.y() >= bottomy && pos.y() <= topy) { + UpdateRange(pos.x(), &xmin, &xmax); + } + vec = outline->step(stepindex); + pos += vec; //move to next + } + } +} + +/********************************************************************** + * crotate_cblob + * + * Rotate the copy by the given vector and return a C_BLOB. + **********************************************************************/ + +C_BLOB *crotate_cblob( //rotate it + C_BLOB *blob, //blob to search + FCOORD rotation //for landscape +) { + C_OUTLINE_LIST out_list; //output outlines + //input outlines + C_OUTLINE_IT in_it = blob->out_list(); + //output outlines + C_OUTLINE_IT out_it = &out_list; + + for (in_it.mark_cycle_pt(); !in_it.cycled_list(); in_it.forward()) { + out_it.add_after_then_move(new C_OUTLINE(in_it.data(), rotation)); + } + return new C_BLOB(&out_list); +} + + +/********************************************************************** + * box_next + * + * Compute the bounding box of this blob with merging of x overlaps + * but no pre-chopping. + * Then move the iterator on to the start of the next blob. + **********************************************************************/ + +TBOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds +) { + BLOBNBOX *blob; //current blob + TBOX result; //total box + + blob = it->data(); + result = blob->bounding_box(); + do { + it->forward(); + blob = it->data(); + if (blob->cblob() == NULL) + //was pre-chopped + result += blob->bounding_box(); + } + //until next real blob + while ((blob->cblob() == NULL) || blob->joined_to_prev()); + return result; +} + + +/********************************************************************** + * box_next_pre_chopped + * + * Compute the bounding box of this blob with merging of x overlaps + * but WITH pre-chopping. + * Then move the iterator on to the start of the next pre-chopped blob. + **********************************************************************/ + +TBOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds +) { + BLOBNBOX *blob; //current blob + TBOX result; //total box + + blob = it->data(); + result = blob->bounding_box(); + do { + it->forward(); + blob = it->data(); + } + //until next real blob + while (blob->joined_to_prev()); + return result; +} + + +/********************************************************************** + * TO_ROW::TO_ROW + * + * Constructor to make a row from a blob. + **********************************************************************/ + +TO_ROW::TO_ROW( //constructor + BLOBNBOX * blob, //first blob + float top, //corrected top + float bottom, //of row + float row_size //ideal +) { + clear(); + y_min = bottom; + y_max = top; + initial_y_min = bottom; + + float diff; //in size + BLOBNBOX_IT it = &blobs; //list of blobs + + it.add_to_end(blob); + diff = top - bottom - row_size; + if (diff > 0) { + y_max -= diff / 2; + y_min += diff / 2; + } + //very small object + else if ((top - bottom) * 3 < row_size) { + diff = row_size / 3 + bottom - top; + y_max += diff / 2; + y_min -= diff / 2; + } +} + +void TO_ROW::print() const { + tprintf("pitch=%d, fp=%g, fps=%g, fpns=%g, prs=%g, prns=%g," + " spacing=%g xh=%g y_origin=%g xev=%d, asc=%g, desc=%g," + " body=%g, minsp=%d maxnsp=%d, thr=%d kern=%g sp=%g\n", + pitch_decision, fixed_pitch, fp_space, fp_nonsp, pr_space, pr_nonsp, + spacing, xheight, y_origin, xheight_evidence, ascrise, descdrop, + body_size, min_space, max_nonspace, space_threshold, kern_size, + space_size); +} + +/********************************************************************** + * TO_ROW:add_blob + * + * Add the blob to the end of the row. + **********************************************************************/ + +void TO_ROW::add_blob( //constructor + BLOBNBOX *blob, //first blob + float top, //corrected top + float bottom, //of row + float row_size //ideal +) { + float allowed; //allowed expansion + float available; //expansion + BLOBNBOX_IT it = &blobs; //list of blobs + + it.add_to_end(blob); + allowed = row_size + y_min - y_max; + if (allowed > 0) { + available = top > y_max ? top - y_max : 0; + if (bottom < y_min) + //total available + available += y_min - bottom; + if (available > 0) { + available += available; //do it gradually + if (available < allowed) + available = allowed; + if (bottom < y_min) + y_min -= (y_min - bottom) * allowed / available; + if (top > y_max) + y_max += (top - y_max) * allowed / available; + } + } +} + + +/********************************************************************** + * TO_ROW:insert_blob + * + * Add the blob to the row in the correct position. + **********************************************************************/ + +void TO_ROW::insert_blob( //constructor + BLOBNBOX *blob //first blob +) { + BLOBNBOX_IT it = &blobs; //list of blobs + + if (it.empty()) + it.add_before_then_move(blob); + else { + it.mark_cycle_pt(); + while (!it.cycled_list() + && it.data()->bounding_box().left() <= + blob->bounding_box().left()) + it.forward(); + if (it.cycled_list()) + it.add_to_end(blob); + else + it.add_before_stay_put(blob); + } +} + + +/********************************************************************** + * TO_ROW::compute_vertical_projection + * + * Compute the vertical projection of a TO_ROW from its blobs. + **********************************************************************/ + +void TO_ROW::compute_vertical_projection() { //project whole row + TBOX row_box; //bound of row + BLOBNBOX *blob; //current blob + TBOX blob_box; //bounding box + BLOBNBOX_IT blob_it = blob_list(); + + if (blob_it.empty()) + return; + row_box = blob_it.data()->bounding_box(); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) + row_box += blob_it.data()->bounding_box(); + + projection.set_range(row_box.left() - PROJECTION_MARGIN, + row_box.right() + PROJECTION_MARGIN); + projection_left = row_box.left() - PROJECTION_MARGIN; + projection_right = row_box.right() + PROJECTION_MARGIN; + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + blob = blob_it.data(); + if (blob->cblob() != NULL) + vertical_cblob_projection(blob->cblob(), &projection); + } +} + + +/********************************************************************** + * TO_ROW::clear + * + * Zero out all scalar members. + **********************************************************************/ +void TO_ROW::clear() { + all_caps = 0; + used_dm_model = 0; + projection_left = 0; + projection_right = 0; + pitch_decision = PITCH_DUNNO; + fixed_pitch = 0.0; + fp_space = 0.0; + fp_nonsp = 0.0; + pr_space = 0.0; + pr_nonsp = 0.0; + spacing = 0.0; + xheight = 0.0; + xheight_evidence = 0; + body_size = 0.0; + ascrise = 0.0; + descdrop = 0.0; + min_space = 0; + max_nonspace = 0; + space_threshold = 0; + kern_size = 0.0; + space_size = 0.0; + y_min = 0.0; + y_max = 0.0; + initial_y_min = 0.0; + m = 0.0; + c = 0.0; + error = 0.0; + para_c = 0.0; + para_error = 0.0; + y_origin = 0.0; + credibility = 0.0; + num_repeated_sets_ = -1; +} + + +/********************************************************************** + * vertical_cblob_projection + * + * Compute the vertical projection of a cblob from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output +) { + //outlines of blob + C_OUTLINE_IT out_it = blob->out_list(); + + for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) { + vertical_coutline_projection(out_it.data(), stats); + } +} + + +/********************************************************************** + * vertical_coutline_projection + * + * Compute the vertical projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output +) { + ICOORD pos; //current point + ICOORD step; //edge step + inT32 length; //of outline + inT16 stepindex; //current step + C_OUTLINE_IT out_it = outline->child(); + + pos = outline->start_pos(); + length = outline->pathlength(); + for (stepindex = 0; stepindex < length; stepindex++) { + step = outline->step(stepindex); + if (step.x() > 0) { + stats->add(pos.x(), -pos.y()); + } + else if (step.x() < 0) { + stats->add(pos.x() - 1, pos.y()); + } + pos += step; + } + + for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) { + vertical_coutline_projection(out_it.data(), stats); + } +} + + +/********************************************************************** + * TO_BLOCK::TO_BLOCK + * + * Constructor to make a TO_BLOCK from a real block. + **********************************************************************/ + +TO_BLOCK::TO_BLOCK( //make a block + BLOCK *src_block //real block +) { + clear(); + block = src_block; +} + +static void clear_blobnboxes(BLOBNBOX_LIST* boxes) { + BLOBNBOX_IT it = boxes; + // A BLOBNBOX generally doesn't own its blobs, so if they do, you + // have to delete them explicitly. + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + BLOBNBOX* box = it.data(); + if (box->cblob() != NULL) + delete box->cblob(); + } +} + +/********************************************************************** + * TO_BLOCK::clear + * + * Zero out all scalar members. + **********************************************************************/ +void TO_BLOCK::clear() { + block = NULL; + pitch_decision = PITCH_DUNNO; + line_spacing = 0.0; + line_size = 0.0; + max_blob_size = 0.0; + baseline_offset = 0.0; + xheight = 0.0; + fixed_pitch = 0.0; + kern_size = 0.0; + space_size = 0.0; + min_space = 0; + max_nonspace = 0; + fp_space = 0.0; + fp_nonsp = 0.0; + pr_space = 0.0; + pr_nonsp = 0.0; + key_row = NULL; +} + + +TO_BLOCK::~TO_BLOCK() { + // Any residual BLOBNBOXes at this stage own their blobs, so delete them. + clear_blobnboxes(&blobs); + clear_blobnboxes(&underlines); + clear_blobnboxes(&noise_blobs); + clear_blobnboxes(&small_blobs); + clear_blobnboxes(&large_blobs); +} + +// Helper function to divide the input blobs over noise, small, medium +// and large lists. Blobs small in height and (small in width or large in width) +// go in the noise list. Dash (-) candidates go in the small list, and +// medium and large are by height. +// SIDE-EFFECT: reset all blobs to initial state by calling Init(). +static void SizeFilterBlobs(int min_height, int max_height, + BLOBNBOX_LIST* src_list, + BLOBNBOX_LIST* noise_list, + BLOBNBOX_LIST* small_list, + BLOBNBOX_LIST* medium_list, + BLOBNBOX_LIST* large_list) { + BLOBNBOX_IT noise_it(noise_list); + BLOBNBOX_IT small_it(small_list); + BLOBNBOX_IT medium_it(medium_list); + BLOBNBOX_IT large_it(large_list); + for (BLOBNBOX_IT src_it(src_list); !src_it.empty(); src_it.forward()) { + BLOBNBOX* blob = src_it.extract(); + blob->ReInit(); + int width = blob->bounding_box().width(); + int height = blob->bounding_box().height(); + if (height < min_height && + (width < min_height || width > max_height)) + noise_it.add_after_then_move(blob); + else if (height > max_height) + large_it.add_after_then_move(blob); + else if (height < min_height) + small_it.add_after_then_move(blob); + else + medium_it.add_after_then_move(blob); + } +} + +// Reorganize the blob lists with a different definition of small, medium +// and large, compared to the original definition. +// Height is still the primary filter key, but medium width blobs of small +// height become small, and very wide blobs of small height stay noise, along +// with small dot-shaped blobs. +void TO_BLOCK::ReSetAndReFilterBlobs() { + int min_height = IntCastRounded(kMinMediumSizeRatio * line_size); + int max_height = IntCastRounded(kMaxMediumSizeRatio * line_size); + BLOBNBOX_LIST noise_list; + BLOBNBOX_LIST small_list; + BLOBNBOX_LIST medium_list; + BLOBNBOX_LIST large_list; + SizeFilterBlobs(min_height, max_height, &blobs, + &noise_list, &small_list, &medium_list, &large_list); + SizeFilterBlobs(min_height, max_height, &large_blobs, + &noise_list, &small_list, &medium_list, &large_list); + SizeFilterBlobs(min_height, max_height, &small_blobs, + &noise_list, &small_list, &medium_list, &large_list); + SizeFilterBlobs(min_height, max_height, &noise_blobs, + &noise_list, &small_list, &medium_list, &large_list); + BLOBNBOX_IT blob_it(&blobs); + blob_it.add_list_after(&medium_list); + blob_it.set_to_list(&large_blobs); + blob_it.add_list_after(&large_list); + blob_it.set_to_list(&small_blobs); + blob_it.add_list_after(&small_list); + blob_it.set_to_list(&noise_blobs); + blob_it.add_list_after(&noise_list); +} + +// Deletes noise blobs from all lists where not owned by a ColPartition. +void TO_BLOCK::DeleteUnownedNoise() { + BLOBNBOX::CleanNeighbours(&blobs); + BLOBNBOX::CleanNeighbours(&small_blobs); + BLOBNBOX::CleanNeighbours(&noise_blobs); + BLOBNBOX::CleanNeighbours(&large_blobs); + BLOBNBOX::DeleteNoiseBlobs(&blobs); + BLOBNBOX::DeleteNoiseBlobs(&small_blobs); + BLOBNBOX::DeleteNoiseBlobs(&noise_blobs); + BLOBNBOX::DeleteNoiseBlobs(&large_blobs); +} + +// Computes and stores the edge offsets on each blob for use in feature +// extraction, using greyscale if the supplied grey and thresholds pixes +// are 8-bit or otherwise (if NULL or not 8 bit) the original binary +// edge step outlines. +// Thresholds must either be the same size as grey or an integer down-scale +// of grey. +// See coutln.h for an explanation of edge offsets. +void TO_BLOCK::ComputeEdgeOffsets(Pix* thresholds, Pix* grey) { + BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &blobs); + BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &small_blobs); + BLOBNBOX::ComputeEdgeOffsets(thresholds, grey, &noise_blobs); +} + +#ifndef GRAPHICS_DISABLED +// Draw the noise blobs from all lists in red. +void TO_BLOCK::plot_noise_blobs(ScrollView* win) { + BLOBNBOX::PlotNoiseBlobs(&noise_blobs, ScrollView::RED, ScrollView::RED, win); + BLOBNBOX::PlotNoiseBlobs(&small_blobs, ScrollView::RED, ScrollView::RED, win); + BLOBNBOX::PlotNoiseBlobs(&large_blobs, ScrollView::RED, ScrollView::RED, win); + BLOBNBOX::PlotNoiseBlobs(&blobs, ScrollView::RED, ScrollView::RED, win); +} + +// Draw the blobs on the various lists in the block in different colors. +void TO_BLOCK::plot_graded_blobs(ScrollView* win) { + BLOBNBOX::PlotBlobs(&noise_blobs, ScrollView::CORAL, ScrollView::BLUE, win); + BLOBNBOX::PlotBlobs(&small_blobs, ScrollView::GOLDENROD, ScrollView::YELLOW, + win); + BLOBNBOX::PlotBlobs(&large_blobs, ScrollView::DARK_GREEN, ScrollView::YELLOW, + win); + BLOBNBOX::PlotBlobs(&blobs, ScrollView::WHITE, ScrollView::BROWN, win); +} + +/********************************************************************** + * plot_blob_list + * + * Draw a list of blobs. + **********************************************************************/ + +void plot_blob_list(ScrollView* win, // window to draw in + BLOBNBOX_LIST *list, // blob list + ScrollView::Color body_colour, // colour to draw + ScrollView::Color child_colour) { // colour of child + BLOBNBOX_IT it = list; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot(win, body_colour, child_colour); + } +} +#endif // GRAPHICS_DISABLED diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.h new file mode 100644 index 0000000..22771ee --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobbox.h @@ -0,0 +1,847 @@ +/********************************************************************** + * File: blobbox.h (Formerly blobnbox.h) + * Description: Code for the textord blob class. + * Author: Ray Smith + * Created: Thu Jul 30 09:08:51 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLOBBOX_H +#define BLOBBOX_H + +#include "clst.h" +#include "elst2.h" +#include "werd.h" +#include "ocrblock.h" +#include "statistc.h" + +enum PITCH_TYPE +{ + PITCH_DUNNO, // insufficient data + PITCH_DEF_FIXED, // definitely fixed + PITCH_MAYBE_FIXED, // could be + PITCH_DEF_PROP, + PITCH_MAYBE_PROP, + PITCH_CORR_FIXED, + PITCH_CORR_PROP +}; + +// The possible tab-stop types of each side of a BLOBNBOX. +// The ordering is important, as it is used for deleting dead-ends in the +// search. ALIGNED, CONFIRMED and VLINE should remain greater than the +// non-aligned, unset, or deleted members. +enum TabType { + TT_NONE, // Not a tab. + TT_DELETED, // Not a tab after detailed analysis. + TT_MAYBE_RAGGED, // Initial designation of a tab-stop candidate. + TT_MAYBE_ALIGNED, // Initial designation of a tab-stop candidate. + TT_CONFIRMED, // Aligned with neighbours. + TT_VLINE // Detected as a vertical line. +}; + +// The possible region types of a BLOBNBOX. +// Note: keep all the text types > BRT_UNKNOWN and all the image types less. +// Keep in sync with kBlobTypes in colpartition.cpp and BoxColor, and the +// *Type static functions below. +enum BlobRegionType { + BRT_NOISE, // Neither text nor image. + BRT_HLINE, // Horizontal separator line. + BRT_VLINE, // Vertical separator line. + BRT_RECTIMAGE, // Rectangular image. + BRT_POLYIMAGE, // Non-rectangular image. + BRT_UNKNOWN, // Not determined yet. + BRT_VERT_TEXT, // Vertical alignment, not necessarily vertically oriented. + BRT_TEXT, // Convincing text. + + BRT_COUNT // Number of possibilities. +}; + +// enum for elements of arrays that refer to neighbours. +// NOTE: keep in this order, so ^2 can be used to flip direction. +enum BlobNeighbourDir { + BND_LEFT, + BND_BELOW, + BND_RIGHT, + BND_ABOVE, + BND_COUNT +}; + +// enum for special type of text characters, such as math symbol or italic. +enum BlobSpecialTextType { + BSTT_NONE, // No special. + BSTT_ITALIC, // Italic style. + BSTT_DIGIT, // Digit symbols. + BSTT_MATH, // Mathmatical symobls (not including digit). + BSTT_UNCLEAR, // Characters with low recognition rate. + BSTT_SKIP, // Characters that we skip labeling (usually too small). + BSTT_COUNT +}; + +inline BlobNeighbourDir DirOtherWay(BlobNeighbourDir dir) { + return static_cast(dir ^ 2); +} + +// BlobTextFlowType indicates the quality of neighbouring information +// related to a chain of connected components, either horizontally or +// vertically. Also used by ColPartition for the collection of blobs +// within, which should all have the same value in most cases. +enum BlobTextFlowType { + BTFT_NONE, // No text flow set yet. + BTFT_NONTEXT, // Flow too poor to be likely text. + BTFT_NEIGHBOURS, // Neighbours support flow in this direction. + BTFT_CHAIN, // There is a weak chain of text in this direction. + BTFT_STRONG_CHAIN, // There is a strong chain of text in this direction. + BTFT_TEXT_ON_IMAGE, // There is a strong chain of text on an image. + BTFT_LEADER, // Leader dots/dashes etc. + BTFT_COUNT +}; + +// Returns true if type1 dominates type2 in a merge. Mostly determined by the +// ordering of the enum, LEADER is weak and dominates nothing. +// The function is anti-symmetric (t1 > t2) === !(t2 > t1), except that +// this cannot be true if t1 == t2, so the result is undefined. +inline bool DominatesInMerge(BlobTextFlowType type1, BlobTextFlowType type2) { + // LEADER always loses. + if (type1 == BTFT_LEADER) return false; + if (type2 == BTFT_LEADER) return true; + // With those out of the way, the ordering of the enum determines the result. + return type1 >= type2; +} + +namespace tesseract { + class ColPartition; +} + +class BLOBNBOX; +ELISTIZEH(BLOBNBOX) +class BLOBNBOX :public ELIST_LINK +{ +public: + BLOBNBOX() { + ConstructionInit(); + } + explicit BLOBNBOX(C_BLOB *srcblob) { + box = srcblob->bounding_box(); + ConstructionInit(); + cblob_ptr = srcblob; + area = static_cast(srcblob->area()); + } + ~BLOBNBOX() { + if (owns_cblob_) delete cblob_ptr; + } + static BLOBNBOX* RealBlob(C_OUTLINE* outline) { + C_BLOB* blob = new C_BLOB(outline); + return new BLOBNBOX(blob); + } + + // Rotates the box and the underlying blob. + void rotate(FCOORD rotation); + + // Methods that act on the box without touching the underlying blob. + // Reflect the box in the y-axis, leaving the underlying blob untouched. + void reflect_box_in_y_axis(); + // Rotates the box by the angle given by rotation. + // If the blob is a diacritic, then only small rotations for skew + // correction can be applied. + void rotate_box(FCOORD rotation); + // Moves just the box by the given vector. + void translate_box(ICOORD v) { + if (IsDiacritic()) { + box.move(v); + base_char_top_ += v.y(); + base_char_bottom_ += v.y(); + } + else { + box.move(v); + set_diacritic_box(box); + } + } + void merge(BLOBNBOX *nextblob); + void really_merge(BLOBNBOX* other); + void chop( // fake chop blob + BLOBNBOX_IT *start_it, // location of this + BLOBNBOX_IT *blob_it, // iterator + FCOORD rotation, // for landscape + float xheight); // line height + + void NeighbourGaps(int gaps[BND_COUNT]) const; + void MinMaxGapsClipped(int* h_min, int* h_max, + int* v_min, int* v_max) const; + void CleanNeighbours(); + // Returns positive if there is at least one side neighbour that has a + // similar stroke width and is not on the other side of a rule line. + int GoodTextBlob() const; + // Returns the number of side neighbours that are of type BRT_NOISE. + int NoisyNeighbours() const; + + // Returns true if the blob is noise and has no owner. + bool DeletableNoise() const { + return owner() == NULL && region_type() == BRT_NOISE; + } + + // Returns true, and sets vert_possible/horz_possible if the blob has some + // feature that makes it individually appear to flow one way. + // eg if it has a high aspect ratio, yet has a complex shape, such as a + // joined word in Latin, Arabic, or Hindi, rather than being a -, I, l, 1. + bool DefiniteIndividualFlow(); + + // Returns true if there is no tabstop violation in merging this and other. + bool ConfirmNoTabViolation(const BLOBNBOX& other) const; + + // Returns true if other has a similar stroke width to this. + bool MatchingStrokeWidth(const BLOBNBOX& other, + double fractional_tolerance, + double constant_tolerance) const; + + // Returns a bounding box of the outline contained within the + // given horizontal range. + TBOX BoundsWithinLimits(int left, int right); + + // Estimates and stores the baseline position based on the shape of the + // outline. + void EstimateBaselinePosition(); + + // Simple accessors. + const TBOX& bounding_box() const { + return box; + } + // Set the bounding box. Use with caution. + // Normally use compute_bounding_box instead. + void set_bounding_box(const TBOX& new_box) { + box = new_box; + base_char_top_ = box.top(); + base_char_bottom_ = box.bottom(); + } + void compute_bounding_box() { + box = cblob_ptr->bounding_box(); + base_char_top_ = box.top(); + base_char_bottom_ = box.bottom(); + baseline_y_ = box.bottom(); + } + const TBOX& reduced_box() const { + return red_box; + } + void set_reduced_box(TBOX new_box) { + red_box = new_box; + reduced = TRUE; + } + inT32 enclosed_area() const { + return area; + } + bool joined_to_prev() const { + return joined != 0; + } + bool red_box_set() const { + return reduced != 0; + } + int repeated_set() const { + return repeated_set_; + } + void set_repeated_set(int set_id) { + repeated_set_ = set_id; + } + C_BLOB *cblob() const { + return cblob_ptr; + } + TabType left_tab_type() const { + return left_tab_type_; + } + void set_left_tab_type(TabType new_type) { + left_tab_type_ = new_type; + } + TabType right_tab_type() const { + return right_tab_type_; + } + void set_right_tab_type(TabType new_type) { + right_tab_type_ = new_type; + } + BlobRegionType region_type() const { + return region_type_; + } + void set_region_type(BlobRegionType new_type) { + region_type_ = new_type; + } + BlobSpecialTextType special_text_type() const { + return spt_type_; + } + void set_special_text_type(BlobSpecialTextType new_type) { + spt_type_ = new_type; + } + BlobTextFlowType flow() const { + return flow_; + } + void set_flow(BlobTextFlowType value) { + flow_ = value; + } + bool vert_possible() const { + return vert_possible_; + } + void set_vert_possible(bool value) { + vert_possible_ = value; + } + bool horz_possible() const { + return horz_possible_; + } + void set_horz_possible(bool value) { + horz_possible_ = value; + } + int left_rule() const { + return left_rule_; + } + void set_left_rule(int new_left) { + left_rule_ = new_left; + } + int right_rule() const { + return right_rule_; + } + void set_right_rule(int new_right) { + right_rule_ = new_right; + } + int left_crossing_rule() const { + return left_crossing_rule_; + } + void set_left_crossing_rule(int new_left) { + left_crossing_rule_ = new_left; + } + int right_crossing_rule() const { + return right_crossing_rule_; + } + void set_right_crossing_rule(int new_right) { + right_crossing_rule_ = new_right; + } + float horz_stroke_width() const { + return horz_stroke_width_; + } + void set_horz_stroke_width(float width) { + horz_stroke_width_ = width; + } + float vert_stroke_width() const { + return vert_stroke_width_; + } + void set_vert_stroke_width(float width) { + vert_stroke_width_ = width; + } + float area_stroke_width() const { + return area_stroke_width_; + } + tesseract::ColPartition* owner() const { + return owner_; + } + void set_owner(tesseract::ColPartition* new_owner) { + owner_ = new_owner; + } + bool leader_on_left() const { + return leader_on_left_; + } + void set_leader_on_left(bool flag) { + leader_on_left_ = flag; + } + bool leader_on_right() const { + return leader_on_right_; + } + void set_leader_on_right(bool flag) { + leader_on_right_ = flag; + } + BLOBNBOX* neighbour(BlobNeighbourDir n) const { + return neighbours_[n]; + } + bool good_stroke_neighbour(BlobNeighbourDir n) const { + return good_stroke_neighbours_[n]; + } + void set_neighbour(BlobNeighbourDir n, BLOBNBOX* neighbour, bool good) { + neighbours_[n] = neighbour; + good_stroke_neighbours_[n] = good; + } + bool IsDiacritic() const { + return base_char_top_ != box.top() || base_char_bottom_ != box.bottom(); + } + int base_char_top() const { + return base_char_top_; + } + int base_char_bottom() const { + return base_char_bottom_; + } + int baseline_position() const { + return baseline_y_; + } + int line_crossings() const { + return line_crossings_; + } + void set_line_crossings(int value) { + line_crossings_ = value; + } + void set_diacritic_box(const TBOX& diacritic_box) { + base_char_top_ = diacritic_box.top(); + base_char_bottom_ = diacritic_box.bottom(); + } + BLOBNBOX* base_char_blob() const { + return base_char_blob_; + } + void set_base_char_blob(BLOBNBOX* blob) { + base_char_blob_ = blob; + } + void set_owns_cblob(bool value) { owns_cblob_ = value; } + + bool UniquelyVertical() const { + return vert_possible_ && !horz_possible_; + } + bool UniquelyHorizontal() const { + return horz_possible_ && !vert_possible_; + } + + // Returns true if the region type is text. + static bool IsTextType(BlobRegionType type) { + return type == BRT_TEXT || type == BRT_VERT_TEXT; + } + // Returns true if the region type is image. + static bool IsImageType(BlobRegionType type) { + return type == BRT_RECTIMAGE || type == BRT_POLYIMAGE; + } + // Returns true if the region type is line. + static bool IsLineType(BlobRegionType type) { + return type == BRT_HLINE || type == BRT_VLINE; + } + // Returns true if the region type cannot be merged. + static bool UnMergeableType(BlobRegionType type) { + return IsLineType(type) || IsImageType(type); + } + // Helper to call CleanNeighbours on all blobs on the list. + static void CleanNeighbours(BLOBNBOX_LIST* blobs); + // Helper to delete all the deletable blobs on the list. + static void DeleteNoiseBlobs(BLOBNBOX_LIST* blobs); + // Helper to compute edge offsets for all the blobs on the list. + // See coutln.h for an explanation of edge offsets. + static void ComputeEdgeOffsets(Pix* thresholds, Pix* grey, + BLOBNBOX_LIST* blobs); + +#ifndef GRAPHICS_DISABLED + // Helper to draw all the blobs on the list in the given body_colour, + // with child outlines in the child_colour. + static void PlotBlobs(BLOBNBOX_LIST* list, + ScrollView::Color body_colour, + ScrollView::Color child_colour, + ScrollView* win); + // Helper to draw only DeletableNoise blobs (unowned, BRT_NOISE) on the + // given list in the given body_colour, with child outlines in the + // child_colour. + static void PlotNoiseBlobs(BLOBNBOX_LIST* list, + ScrollView::Color body_colour, + ScrollView::Color child_colour, + ScrollView* win); + + static ScrollView::Color TextlineColor(BlobRegionType region_type, + BlobTextFlowType flow_type); + + // Keep in sync with BlobRegionType. + ScrollView::Color BoxColor() const; + + void plot(ScrollView* window, // window to draw in + ScrollView::Color blob_colour, // for outer bits + ScrollView::Color child_colour); // for holes +#endif + + // Initializes the bulk of the members to default values for use at + // construction time. + void ConstructionInit() { + cblob_ptr = NULL; + owns_cblob_ = false; + area = 0; + area_stroke_width_ = 0.0f; + horz_stroke_width_ = 0.0f; + vert_stroke_width_ = 0.0f; + ReInit(); + } + // Initializes members set by StrokeWidth and beyond, without discarding + // stored area and strokewidth values, which are expensive to calculate. + void ReInit() { + joined = false; + reduced = false; + repeated_set_ = 0; + left_tab_type_ = TT_NONE; + right_tab_type_ = TT_NONE; + region_type_ = BRT_UNKNOWN; + flow_ = BTFT_NONE; + spt_type_ = BSTT_SKIP; + left_rule_ = 0; + right_rule_ = 0; + left_crossing_rule_ = 0; + right_crossing_rule_ = 0; + if (area_stroke_width_ == 0.0f && area > 0 && cblob() != NULL) + area_stroke_width_ = 2.0f * area / cblob()->perimeter(); + owner_ = NULL; + base_char_top_ = box.top(); + base_char_bottom_ = box.bottom(); + baseline_y_ = box.bottom(); + line_crossings_ = 0; + base_char_blob_ = NULL; + horz_possible_ = false; + vert_possible_ = false; + leader_on_left_ = false; + leader_on_right_ = false; + ClearNeighbours(); + } + + void ClearNeighbours() { + for (int n = 0; n < BND_COUNT; ++n) { + neighbours_[n] = NULL; + good_stroke_neighbours_[n] = false; + } + } + +private: + C_BLOB *cblob_ptr; // edgestep blob + TBOX box; // bounding box + TBOX red_box; // bounding box + int area : 30; // enclosed area + int joined : 1; // joined to prev + int reduced : 1; // reduced box set + int repeated_set_; // id of the set of repeated blobs + TabType left_tab_type_; // Indicates tab-stop assessment + TabType right_tab_type_; // Indicates tab-stop assessment + BlobRegionType region_type_; // Type of region this blob belongs to + BlobTextFlowType flow_; // Quality of text flow. + inT16 left_rule_; // x-coord of nearest but not crossing rule line + inT16 right_rule_; // x-coord of nearest but not crossing rule line + inT16 left_crossing_rule_; // x-coord of nearest or crossing rule line + inT16 right_crossing_rule_; // x-coord of nearest or crossing rule line + inT16 base_char_top_; // y-coord of top/bottom of diacritic base, + inT16 base_char_bottom_; // if it exists else top/bottom of this blob. + inT16 baseline_y_; // Estimate of baseline position. + int line_crossings_; // Number of line intersections touched. + BLOBNBOX* base_char_blob_; // The blob that was the base char. + float horz_stroke_width_; // Median horizontal stroke width + float vert_stroke_width_; // Median vertical stroke width + float area_stroke_width_; // Stroke width from area/perimeter ratio. + tesseract::ColPartition* owner_; // Who will delete me when I am not needed + BlobSpecialTextType spt_type_; // Special text type. + BLOBNBOX* neighbours_[BND_COUNT]; + bool good_stroke_neighbours_[BND_COUNT]; + bool horz_possible_; // Could be part of horizontal flow. + bool vert_possible_; // Could be part of vertical flow. + bool leader_on_left_; // There is a leader to the left. + bool leader_on_right_; // There is a leader to the right. + // Iff true, then the destructor should delete the cblob_ptr. + // TODO(rays) migrate all uses to correctly setting this flag instead of + // deleting the C_BLOB before deleting the BLOBNBOX. + bool owns_cblob_; +}; + +class TO_ROW : public ELIST2_LINK +{ +public: + static const int kErrorWeight = 3; + + TO_ROW() { + clear(); + } //empty + TO_ROW( //constructor + BLOBNBOX *blob, //from first blob + float top, //of row //target height + float bottom, + float row_size); + + void print() const; + float max_y() const { //access function + return y_max; + } + float min_y() const { + return y_min; + } + float mean_y() const { + return (y_min + y_max) / 2.0f; + } + float initial_min_y() const { + return initial_y_min; + } + float line_m() const { //access to line fit + return m; + } + float line_c() const { + return c; + } + float line_error() const { + return error; + } + float parallel_c() const { + return para_c; + } + float parallel_error() const { + return para_error; + } + float believability() const { //baseline goodness + return credibility; + } + float intercept() const { //real parallel_c + return y_origin; + } + void add_blob( //put in row + BLOBNBOX *blob, //blob to add + float top, //of row //target height + float bottom, + float row_size); + void insert_blob( //put in row in order + BLOBNBOX *blob); + + BLOBNBOX_LIST *blob_list() { //get list + return &blobs; + } + + void set_line( //set line spec + float new_m, //line to set + float new_c, + float new_error) { + m = new_m; + c = new_c; + error = new_error; + } + void set_parallel_line( //set fixed gradient line + float gradient, //page gradient + float new_c, + float new_error) { + para_c = new_c; + para_error = new_error; + credibility = + (float)(blobs.length() - kErrorWeight * new_error); + y_origin = (float)(new_c / sqrt(1 + gradient * gradient)); + //real intercept + } + void set_limits( //set min,max + float new_min, //bottom and + float new_max) { //top of row + y_min = new_min; + y_max = new_max; + } + void compute_vertical_projection(); + //get projection + + bool rep_chars_marked() const { + return num_repeated_sets_ != -1; + } + void clear_rep_chars_marked() { + num_repeated_sets_ = -1; + } + int num_repeated_sets() const { + return num_repeated_sets_; + } + void set_num_repeated_sets(int num_sets) { + num_repeated_sets_ = num_sets; + } + + // true when dead + BOOL8 merged; + BOOL8 all_caps; // had no ascenders + BOOL8 used_dm_model; // in guessing pitch + inT16 projection_left; // start of projection + inT16 projection_right; // start of projection + PITCH_TYPE pitch_decision; // how strong is decision + float fixed_pitch; // pitch or 0 + float fp_space; // sp if fixed pitch + float fp_nonsp; // nonsp if fixed pitch + float pr_space; // sp if prop + float pr_nonsp; // non sp if prop + float spacing; // to "next" row + float xheight; // of line + int xheight_evidence; // number of blobs of height xheight + float ascrise; // ascenders + float descdrop; // descenders + float body_size; // of CJK characters. Assumed to be + // xheight+ascrise for non-CJK text. + inT32 min_space; // min size for real space + inT32 max_nonspace; // max size of non-space + inT32 space_threshold; // space vs nonspace + float kern_size; // average non-space + float space_size; // average space + WERD_LIST rep_words; // repeated chars + ICOORDELT_LIST char_cells; // fixed pitch cells + QSPLINE baseline; // curved baseline + STATS projection; // vertical projection + +private: + void clear(); // clear all values to reasonable defaults + + BLOBNBOX_LIST blobs; //blobs in row + float y_min; //coords + float y_max; + float initial_y_min; + float m, c; //line spec + float error; //line error + float para_c; //constrained fit + float para_error; + float y_origin; //rotated para_c; + float credibility; //baseline believability + int num_repeated_sets_; // number of sets of repeated blobs + // set to -1 if we have not searched + // for repeated blobs in this row yet +}; + +ELIST2IZEH(TO_ROW) +class TO_BLOCK :public ELIST_LINK +{ +public: + TO_BLOCK() : pitch_decision(PITCH_DUNNO) { + clear(); + } //empty + TO_BLOCK( //constructor + BLOCK *src_block); //real block + ~TO_BLOCK(); + + void clear(); // clear all scalar members. + + TO_ROW_LIST *get_rows() { //access function + return &row_list; + } + + // Rotate all the blobnbox lists and the underlying block. Then update the + // median size statistic from the blobs list. + void rotate(const FCOORD& rotation) { + BLOBNBOX_LIST* blobnbox_list[] = { &blobs, &underlines, &noise_blobs, + &small_blobs, &large_blobs, NULL }; + for (BLOBNBOX_LIST** list = blobnbox_list; *list != NULL; ++list) { + BLOBNBOX_IT it(*list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->rotate(rotation); + } + } + // Rotate the block + ASSERT_HOST(block->poly_block() != NULL); + block->rotate(rotation); + // Update the median size statistic from the blobs list. + STATS widths(0, block->bounding_box().width()); + STATS heights(0, block->bounding_box().height()); + BLOBNBOX_IT blob_it(&blobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + widths.add(blob_it.data()->bounding_box().width(), 1); + heights.add(blob_it.data()->bounding_box().height(), 1); + } + block->set_median_size(static_cast(widths.median() + 0.5), + static_cast(heights.median() + 0.5)); + } + + void print_rows() { //debug info + TO_ROW_IT row_it = &row_list; + TO_ROW *row; + + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); + row_it.forward()) { + row = row_it.data(); + tprintf("Row range (%g,%g), para_c=%g, blobcount=" INT32FORMAT + "\n", row->min_y(), row->max_y(), row->parallel_c(), + row->blob_list()->length()); + } + } + + // Reorganizes the blob lists with a different definition of small, medium + // and large, compared to the original definition. + // Height is still the primary filter key, but medium width blobs of small + // height become medium, and very wide blobs of small height stay small. + void ReSetAndReFilterBlobs(); + + // Deletes noise blobs from all lists where not owned by a ColPartition. + void DeleteUnownedNoise(); + + // Computes and stores the edge offsets on each blob for use in feature + // extraction, using greyscale if the supplied grey and thresholds pixes + // are 8-bit or otherwise (if NULL or not 8 bit) the original binary + // edge step outlines. + // Thresholds must either be the same size as grey or an integer down-scale + // of grey. + // See coutln.h for an explanation of edge offsets. + void ComputeEdgeOffsets(Pix* thresholds, Pix* grey); + +#ifndef GRAPHICS_DISABLED + // Draw the noise blobs from all lists in red. + void plot_noise_blobs(ScrollView* to_win); + // Draw the blobs on on the various lists in the block in different colors. + void plot_graded_blobs(ScrollView* to_win); +#endif + + BLOBNBOX_LIST blobs; //medium size + BLOBNBOX_LIST underlines; //underline blobs + BLOBNBOX_LIST noise_blobs; //very small + BLOBNBOX_LIST small_blobs; //fairly small + BLOBNBOX_LIST large_blobs; //big blobs + BLOCK *block; //real block + PITCH_TYPE pitch_decision; //how strong is decision + float line_spacing; //estimate + // line_size is a lower-bound estimate of the font size in pixels of + // the text in the block (with ascenders and descenders), being a small + // (1.25) multiple of the median height of filtered blobs. + // In most cases the font size will be bigger, but it will be closer + // if the text is allcaps, or in a no-x-height script. + float line_size; //estimate + float max_blob_size; //line assignment limit + float baseline_offset; //phase shift + float xheight; //median blob size + float fixed_pitch; //pitch or 0 + float kern_size; //average non-space + float space_size; //average space + inT32 min_space; //min definite space + inT32 max_nonspace; //max definite + float fp_space; //sp if fixed pitch + float fp_nonsp; //nonsp if fixed pitch + float pr_space; //sp if prop + float pr_nonsp; //non sp if prop + TO_ROW *key_row; //starting row + +private: + TO_ROW_LIST row_list; //temporary rows +}; + +ELISTIZEH(TO_BLOCK) +extern double_VAR_H(textord_error_weight, 3, + "Weighting for error in believability"); +void find_cblob_limits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax); +void find_cblob_vlimits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + float &ymin, //output y limits + float &ymax); +void find_cblob_hlimits( //get x limits + C_BLOB *blob, //blob to search + float bottomy, //y limits + float topy, + float &xmin, //output x limits + float &xymax); +C_BLOB *crotate_cblob( //rotate it + C_BLOB *blob, //blob to search + FCOORD rotation //for landscape +); +TBOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds +); +TBOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds +); +void vertical_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output +); +void vertical_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output +); +#ifndef GRAPHICS_DISABLED +void plot_blob_list(ScrollView* win, // window to draw in + BLOBNBOX_LIST *list, // blob list + ScrollView::Color body_colour, // colour to draw + ScrollView::Color child_colour); // colour of child +#endif // GRAPHICS_DISABLED +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.cpp new file mode 100644 index 0000000..bab7407 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.cpp @@ -0,0 +1,1027 @@ +/* -*-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 automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "blobs.h" +#include "ccstruct.h" +#include "clst.h" +#include "cutil.h" +#include "emalloc.h" +#include "helpers.h" +#include "linlsq.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); + +// Returns true when the two line segments cross each other. +// (Moved from outlines.cpp). +// Finds where the projected lines would cross and then checks to see if the +// point of intersection lies on both of the line segments. If it does +// then these two segments cross. +/* static */ +bool TPOINT::IsCrossed(const TPOINT& a0, const TPOINT& a1, const TPOINT& b0, + const TPOINT& b1) { + int b0a1xb0b1, b0b1xb0a0; + int a1b1xa1a0, a1a0xa1b0; + + TPOINT b0a1, b0a0, a1b1, b0b1, a1a0; + + b0a1.x = a1.x - b0.x; + b0a0.x = a0.x - b0.x; + a1b1.x = b1.x - a1.x; + b0b1.x = b1.x - b0.x; + a1a0.x = a0.x - a1.x; + b0a1.y = a1.y - b0.y; + b0a0.y = a0.y - b0.y; + a1b1.y = b1.y - a1.y; + b0b1.y = b1.y - b0.y; + a1a0.y = a0.y - a1.y; + + b0a1xb0b1 = CROSS(b0a1, b0b1); + b0b1xb0a0 = CROSS(b0b1, b0a0); + a1b1xa1a0 = CROSS(a1b1, a1a0); + // For clarity, we want CROSS(a1a0,a1b0) here but we have b0a1 instead of a1b0 + // so use -CROSS(a1b0,b0a1) instead, which is the same. + a1a0xa1b0 = -CROSS(a1a0, b0a1); + + return ((b0a1xb0b1 > 0 && b0b1xb0a0 > 0) || + (b0a1xb0b1 < 0 && b0b1xb0a0 < 0)) && + ((a1b1xa1a0 > 0 && a1a0xa1b0 > 0) || (a1b1xa1a0 < 0 && a1a0xa1b0 < 0)); +} + +// Consume the circular list of EDGEPTs to make a TESSLINE. +TESSLINE* TESSLINE::BuildFromOutlineList(EDGEPT* outline) { + TESSLINE* result = new TESSLINE; + result->loop = outline; + if (outline->src_outline != NULL) { + // ASSUMPTION: This function is only ever called from ApproximateOutline + // and therefore either all points have a src_outline or all do not. + // Just as SetupFromPos sets the vectors from the vertices, setup the + // step_count members to indicate the (positive) number of original + // C_OUTLINE steps to the next vertex. + EDGEPT* pt = outline; + do { + pt->step_count = pt->next->start_step - pt->start_step; + if (pt->step_count < 0) + pt->step_count += pt->src_outline->pathlength(); + pt = pt->next; + } while (pt != 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(floor(pt->pos.x * rot.x() - + pt->pos.y * rot.y() + 0.5)); + pt->pos.y = static_cast(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(floor(pt->pos.x * factor + 0.5)); + pt->pos.y = static_cast(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); +} + +#ifndef GRAPHICS_DISABLED +void TESSLINE::plot(ScrollView* window, ScrollView::Color color, + ScrollView::Color child_color) { + 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 + +// Returns the first non-hidden EDGEPT that has a different src_outline to +// its predecessor, or, if all the same, the lowest indexed point. +EDGEPT* TESSLINE::FindBestStartPt() const { + EDGEPT* best_start = loop; + int best_step = loop->start_step; + // Iterate the polygon. + EDGEPT* pt = loop; + do { + if (pt->IsHidden()) continue; + if (pt->prev->IsHidden() || pt->prev->src_outline != pt->src_outline) + return pt; // Qualifies as the best. + if (pt->start_step < best_step) { + best_step = pt->start_step; + best_start = pt; + } + } while ((pt = pt->next) != loop); + return best_start; +} + +// 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(bool allow_detailed_fx, + 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(); + if (outline->pathlength() > 0) { + TESSLINE* tessline = ApproximateOutline(allow_detailed_fx, outline); + tessline->is_hole = children; + *tail = tessline; + tail = &tessline->next; + } + if (!outline->child()->empty()) { + tail = ApproximateOutlineList(allow_detailed_fx, outline->child(), true, + tail); + } + } + return tail; +} + +// Factory to build a TBLOB from a C_BLOB with polygonal approximation along +// the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB +// contain pointers to the input C_OUTLINEs that enable higher-resolution +// feature extraction that does not use the polygonal approximation. +TBLOB* TBLOB::PolygonalCopy(bool allow_detailed_fx, C_BLOB* src) { + TBLOB* tblob = new TBLOB; + ApproximateOutlineList(allow_detailed_fx, src->out_list(), false, + &tblob->outlines); + return tblob; +} + +// Factory builds a blob with no outlines, but copies the other member data. +TBLOB* TBLOB::ShallowCopy(const TBLOB& src) { + TBLOB* blob = new TBLOB; + blob->denorm_ = src.denorm_; + return blob; +} + +// 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 input blob +// can be used directly. Otherwise a new TBLOB is returned which must be +// deleted after use. +TBLOB* TBLOB::ClassifyNormalizeIfNeeded() 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(); + // 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); + rotated_blob->Normalize(NULL, &rotation, &denorm_, x_middle, y_middle, + 1.0f, 1.0f, 0.0f, target_y, + denorm_.inverse(), denorm_.pix()); + } + 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; + } + denorm_ = src.denorm_; +} + +// Deletes owned data. +void TBLOB::Clear() { + for (TESSLINE* next_outline = NULL; outlines != NULL; + outlines = next_outline) { + next_outline = outlines->next; + delete outlines; + } +} + +// Sets up the built-in DENORM and normalizes the blob in-place. +// For parameters see DENORM::SetupNormalization, plus the inverse flag for +// this blob and the Pix for the full image. +void TBLOB::Normalize(const BLOCK* block, + const FCOORD* rotation, + const DENORM* predecessor, + float x_origin, float y_origin, + float x_scale, float y_scale, + float final_xshift, float final_yshift, + bool inverse, Pix* pix) { + denorm_.SetupNormalization(block, rotation, predecessor, x_origin, y_origin, + x_scale, y_scale, final_xshift, final_yshift); + denorm_.set_inverse(inverse); + denorm_.set_pix(pix); + // TODO(rays) outline->Normalize is more accurate, but breaks tests due + // the changes it makes. Reinstate this code with a retraining. + // The reason this change is troublesome is that it normalizes for the + // baseline value computed independently at each x-coord. If the baseline + // is not horizontal, this introduces shear into the normalized blob, which + // is useful on the rare occasions that the baseline is really curved, but + // the baselines need to be stabilized the rest of the time. +#if 0 + 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; +} + +// Finds and deletes any duplicate outlines in this blob, without deleting +// their EDGEPTs. +void TBLOB::EliminateDuplicateOutlines() { + for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) { + TESSLINE* last_outline = outline; + for (TESSLINE* other_outline = outline->next; other_outline != NULL; + last_outline = other_outline, other_outline = other_outline->next) { + if (outline->SameBox(*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; + } + } + } +} + +// Swaps the outlines of *this and next if needed to keep the centers in +// increasing x. +void TBLOB::CorrectBlobOrder(TBLOB* next) { + TBOX box = bounding_box(); + TBOX next_box = next->bounding_box(); + if (box.x_middle() > next_box.x_middle()) { + Swap(&outlines, &next->outlines); + } +} + +#ifndef GRAPHICS_DISABLED +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); +} +#endif // GRAPHICS_DISABLED + +// Computes the center of mass and second moments for the old baseline and +// 2nd moment normalizations. Returns the outline length. +// The input denorm should be the normalizations that have been applied from +// the image to the current state of this TBLOB. +int TBLOB::ComputeMoments(FCOORD* center, FCOORD* second_moments) const { + // Compute 1st and 2nd moments of the original outline. + LLSQ accumulator; + TBOX box = bounding_box(); + // Iterate the outlines, accumulating edges relative the box.botleft(). + CollectEdges(box, NULL, &accumulator, NULL, NULL); + *center = accumulator.mean_point() + box.botleft(); + // The 2nd moments are just the standard deviation of the point positions. + double x2nd = sqrt(accumulator.x_variance()); + double y2nd = sqrt(accumulator.y_variance()); + if (x2nd < 1.0) x2nd = 1.0; + if (y2nd < 1.0) y2nd = 1.0; + second_moments->set_x(x2nd); + second_moments->set_y(y2nd); + return accumulator.count(); +} + +// Computes the precise bounding box of the coords that are generated by +// GetEdgeCoords. This may be different from the bounding box of the polygon. +void TBLOB::GetPreciseBoundingBox(TBOX* precise_box) const { + TBOX box = bounding_box(); + *precise_box = TBOX(); + CollectEdges(box, precise_box, NULL, NULL, NULL); + precise_box->move(box.botleft()); +} + +// Adds edges to the given vectors. +// For all the edge steps in all the outlines, or polygonal approximation +// where there are no edge steps, collects the steps into x_coords/y_coords. +// x_coords is a collection of the x-coords of vertical edges for each +// y-coord starting at box.bottom(). +// y_coords is a collection of the y-coords of horizontal edges for each +// x-coord starting at box.left(). +// Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. +// Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. +void TBLOB::GetEdgeCoords(const TBOX& box, + GenericVector >* x_coords, + GenericVector >* y_coords) const { + GenericVector empty; + x_coords->init_to_size(box.height(), empty); + y_coords->init_to_size(box.width(), empty); + CollectEdges(box, NULL, NULL, x_coords, y_coords); + // Sort the output vectors. + for (int i = 0; i < x_coords->size(); ++i) + (*x_coords)[i].sort(); + for (int i = 0; i < y_coords->size(); ++i) + (*y_coords)[i].sort(); +} + +// Accumulates the segment between pt1 and pt2 in the LLSQ, quantizing over +// the integer coordinate grid to properly weight long vectors. +static void SegmentLLSQ(const FCOORD& pt1, const FCOORD& pt2, + LLSQ* accumulator) { + FCOORD step(pt2); + step -= pt1; + int xstart = IntCastRounded(MIN(pt1.x(), pt2.x())); + int xend = IntCastRounded(MAX(pt1.x(), pt2.x())); + int ystart = IntCastRounded(MIN(pt1.y(), pt2.y())); + int yend = IntCastRounded(MAX(pt1.y(), pt2.y())); + if (xstart == xend && ystart == yend) return; // Nothing to do. + double weight = step.length() / (xend - xstart + yend - ystart); + // Compute and save the y-position at the middle of each x-step. + for (int x = xstart; x < xend; ++x) { + double y = pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x(); + accumulator->add(x + 0.5, y, weight); + } + // Compute and save the x-position at the middle of each y-step. + for (int y = ystart; y < yend; ++y) { + double x = pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y(); + accumulator->add(x, y + 0.5, weight); + } +} + +// Adds any edges from a single segment of outline between pt1 and pt2 to +// the x_coords, y_coords vectors. pt1 and pt2 should be relative to the +// bottom-left of the bounding box, hence indices to x_coords, y_coords +// are clipped to ([0,x_limit], [0,y_limit]). +// See GetEdgeCoords above for a description of x_coords, y_coords. +static void SegmentCoords(const FCOORD& pt1, const FCOORD& pt2, + int x_limit, int y_limit, + GenericVector >* x_coords, + GenericVector >* y_coords) { + FCOORD step(pt2); + step -= pt1; + int start = ClipToRange(IntCastRounded(MIN(pt1.x(), pt2.x())), 0, x_limit); + int end = ClipToRange(IntCastRounded(MAX(pt1.x(), pt2.x())), 0, x_limit); + for (int x = start; x < end; ++x) { + int y = IntCastRounded(pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x()); + (*y_coords)[x].push_back(y); + } + start = ClipToRange(IntCastRounded(MIN(pt1.y(), pt2.y())), 0, y_limit); + end = ClipToRange(IntCastRounded(MAX(pt1.y(), pt2.y())), 0, y_limit); + for (int y = start; y < end; ++y) { + int x = IntCastRounded(pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y()); + (*x_coords)[y].push_back(x); + } +} + +// Adds any edges from a single segment of outline between pt1 and pt2 to +// the bbox such that it guarantees to contain anything produced by +// SegmentCoords. +static void SegmentBBox(const FCOORD& pt1, const FCOORD& pt2, TBOX* bbox) { + FCOORD step(pt2); + step -= pt1; + int x1 = IntCastRounded(MIN(pt1.x(), pt2.x())); + int x2 = IntCastRounded(MAX(pt1.x(), pt2.x())); + if (x2 > x1) { + int y1 = IntCastRounded(pt1.y() + step.y() * (x1 + 0.5 - pt1.x()) / + step.x()); + int y2 = IntCastRounded(pt1.y() + step.y() * (x2 - 0.5 - pt1.x()) / + step.x()); + TBOX point(x1, MIN(y1, y2), x2, MAX(y1, y2)); + *bbox += point; + } + int y1 = IntCastRounded(MIN(pt1.y(), pt2.y())); + int y2 = IntCastRounded(MAX(pt1.y(), pt2.y())); + if (y2 > y1) { + int x1 = IntCastRounded(pt1.x() + step.x() * (y1 + 0.5 - pt1.y()) / + step.y()); + int x2 = IntCastRounded(pt1.x() + step.x() * (y2 - 0.5 - pt1.y()) / + step.y()); + TBOX point(MIN(x1, x2), y1, MAX(x1, x2), y2); + *bbox += point; + } +} + +// Collects edges into the given bounding box, LLSQ accumulator and/or x_coords, +// y_coords vectors. +// For a description of x_coords/y_coords, see GetEdgeCoords above. +// Startpt to lastpt, inclusive, MUST have the same src_outline member, +// which may be NULL. The vector from lastpt to its next is included in +// the accumulation. Hidden edges should be excluded by the caller. +// The input denorm should be the normalizations that have been applied from +// the image to the current state of the TBLOB from which startpt, lastpt come. +// box is the bounding box of the blob from which the EDGEPTs are taken and +// indices into x_coords, y_coords are offset by box.botleft(). +static void CollectEdgesOfRun(const EDGEPT* startpt, const EDGEPT* lastpt, + const DENORM& denorm, const TBOX& box, + TBOX* bounding_box, + LLSQ* accumulator, + GenericVector > *x_coords, + GenericVector > *y_coords) { + const C_OUTLINE* outline = startpt->src_outline; + int x_limit = box.width() - 1; + int y_limit = box.height() - 1; + if (outline != NULL) { + // Use higher-resolution edge points stored on the outline. + // The outline coordinates may not match the binary image because of the + // rotation for vertical text lines, but the root_denorm IS the matching + // start of the DENORM chain. + const DENORM* root_denorm = denorm.RootDenorm(); + int step_length = outline->pathlength(); + int start_index = startpt->start_step; + // Note that if this run straddles the wrap-around point of the outline, + // that lastpt->start_step may have a lower index than startpt->start_step, + // and we want to use an end_index that allows us to use a positive + // increment, so we add step_length if necessary, but that may be beyond the + // bounds of the outline steps/ due to wrap-around, so we use % step_length + // everywhere, except for start_index. + int end_index = lastpt->start_step + lastpt->step_count; + if (end_index <= start_index) + end_index += step_length; + // pos is the integer coordinates of the binary image steps. + ICOORD pos = outline->position_at_index(start_index); + FCOORD origin(box.left(), box.bottom()); + // f_pos is a floating-point version of pos that offers improved edge + // positioning using greyscale information or smoothing of edge steps. + FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, start_index); + // pos_normed is f_pos after the appropriate normalization, and relative + // to origin. + // prev_normed is the previous value of pos_normed. + FCOORD prev_normed; + denorm.NormTransform(root_denorm, f_pos, &prev_normed); + prev_normed -= origin; + for (int index = start_index; index < end_index; ++index) { + ICOORD step = outline->step(index % step_length); + // Only use the point if its edge strength is positive. This excludes + // points that don't provide useful information, eg + // ___________ + // |___________ + // The vertical step provides only noisy, damaging information, as even + // with a greyscale image, the positioning of the edge there may be a + // fictitious extrapolation, so previous processing has eliminated it. + if (outline->edge_strength_at_index(index % step_length) > 0) { + FCOORD f_pos = outline->sub_pixel_pos_at_index(pos, + index % step_length); + FCOORD pos_normed; + denorm.NormTransform(root_denorm, f_pos, &pos_normed); + pos_normed -= origin; + // Accumulate the information that is selected by the caller. + if (bounding_box != NULL) { + SegmentBBox(pos_normed, prev_normed, bounding_box); + } + if (accumulator != NULL) { + SegmentLLSQ(pos_normed, prev_normed, accumulator); + } + if (x_coords != NULL && y_coords != NULL) { + SegmentCoords(pos_normed, prev_normed, x_limit, y_limit, + x_coords, y_coords); + } + prev_normed = pos_normed; + } + pos += step; + } + } + else { + // There is no outline, so we are forced to use the polygonal approximation. + const EDGEPT* endpt = lastpt->next; + const EDGEPT* pt = startpt; + do { + FCOORD next_pos(pt->next->pos.x - box.left(), + pt->next->pos.y - box.bottom()); + FCOORD pos(pt->pos.x - box.left(), pt->pos.y - box.bottom()); + if (bounding_box != NULL) { + SegmentBBox(next_pos, pos, bounding_box); + } + if (accumulator != NULL) { + SegmentLLSQ(next_pos, pos, accumulator); + } + if (x_coords != NULL && y_coords != NULL) { + SegmentCoords(next_pos, pos, x_limit, y_limit, x_coords, y_coords); + } + } while ((pt = pt->next) != endpt); + } +} + +// For all the edge steps in all the outlines, or polygonal approximation +// where there are no edge steps, collects the steps into the bounding_box, +// llsq and/or the x_coords/y_coords. Both are used in different kinds of +// normalization. +// For a description of x_coords, y_coords, see GetEdgeCoords above. +void TBLOB::CollectEdges(const TBOX& box, + TBOX* bounding_box, LLSQ* llsq, + GenericVector >* x_coords, + GenericVector >* y_coords) const { + // Iterate the outlines. + for (const TESSLINE* ol = outlines; ol != NULL; ol = ol->next) { + // Iterate the polygon. + EDGEPT* loop_pt = ol->FindBestStartPt(); + EDGEPT* pt = loop_pt; + if (pt == NULL) continue; + do { + if (pt->IsHidden()) continue; + // Find a run of equal src_outline. + EDGEPT* last_pt = pt; + do { + last_pt = last_pt->next; + } while (last_pt != loop_pt && !last_pt->IsHidden() && + last_pt->src_outline == pt->src_outline); + last_pt = last_pt->prev; + CollectEdgesOfRun(pt, last_pt, denorm_, box, + bounding_box, llsq, x_coords, y_coords); + pt = last_pt; + } while ((pt = pt->next) != loop_pt); + } +} + +// Factory to build a TWERD from a (C_BLOB) WERD, with polygonal +// approximation along the way. +TWERD* TWERD::PolygonalCopy(bool allow_detailed_fx, WERD* src) { + TWERD* tessword = new TWERD; + tessword->latin_script = src->flag(W_SCRIPT_IS_LATIN); + C_BLOB_IT b_it(src->cblob_list()); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + C_BLOB* blob = b_it.data(); + TBLOB* tblob = TBLOB::PolygonalCopy(allow_detailed_fx, blob); + tessword->blobs.push_back(tblob); + } + return tessword; +} + +// Baseline normalizes the blobs in-place, recording the normalization in the +// DENORMs in the blobs. +void TWERD::BLNormalize(const BLOCK* block, const ROW* row, Pix* pix, + bool inverse, float x_height, float baseline_shift, + bool numeric_mode, tesseract::OcrEngineMode hint, + const TBOX* norm_box, + DENORM* word_denorm) { + TBOX word_box = bounding_box(); + if (norm_box != NULL) word_box = *norm_box; + float word_middle = (word_box.left() + word_box.right()) / 2.0f; + float input_y_offset = 0.0f; + float final_y_offset = static_cast(kBlnBaselineOffset); + float scale = kBlnXHeight / x_height; + if (hint == tesseract::OEM_CUBE_ONLY || row == NULL) { + word_middle = word_box.left(); + input_y_offset = word_box.bottom(); + final_y_offset = 0.0f; + if (hint == tesseract::OEM_CUBE_ONLY) + scale = 1.0f; + } + else { + input_y_offset = row->base_line(word_middle) + baseline_shift; + } + for (int b = 0; b < blobs.size(); ++b) { + TBLOB* blob = blobs[b]; + TBOX blob_box = blob->bounding_box(); + float mid_x = (blob_box.left() + blob_box.right()) / 2.0f; + float baseline = input_y_offset; + float blob_scale = scale; + if (numeric_mode) { + baseline = blob_box.bottom(); + blob_scale = ClipToRange(kBlnXHeight * 4.0f / (3 * blob_box.height()), + scale, scale * 1.5f); + } + else if (row != NULL && hint != tesseract::OEM_CUBE_ONLY) { + baseline = row->base_line(mid_x) + baseline_shift; + } + // The image will be 8-bit grey if the input was grey or color. Note that in + // a grey image 0 is black and 255 is white. If the input was binary, then + // the pix will be binary and 0 is white, with 1 being black. + // To tell the difference pixGetDepth() will return 8 or 1. + // The inverse flag will be true iff the word has been determined to be + // white on black, and is independent of whether the pix is 8 bit or 1 bit. + blob->Normalize(block, NULL, NULL, word_middle, baseline, blob_scale, + blob_scale, 0.0f, final_y_offset, inverse, pix); + } + if (word_denorm != NULL) { + word_denorm->SetupNormalization(block, NULL, NULL, word_middle, + input_y_offset, scale, scale, + 0.0f, final_y_offset); + word_denorm->set_inverse(inverse); + word_denorm->set_pix(pix); + } +} + +// Copies the data and the blobs, but leaves next untouched. +void TWERD::CopyFrom(const TWERD& src) { + Clear(); + latin_script = src.latin_script; + for (int b = 0; b < src.blobs.size(); ++b) { + TBLOB* new_blob = new TBLOB(*src.blobs[b]); + blobs.push_back(new_blob); + } +} + +// Deletes owned data. +void TWERD::Clear() { + blobs.delete_data_pointers(); + blobs.clear(); +} + +// Recomputes the bounding boxes of the blobs. +void TWERD::ComputeBoundingBoxes() { + for (int b = 0; b < blobs.size(); ++b) { + blobs[b]->ComputeBoundingBoxes(); + } +} + +TBOX TWERD::bounding_box() const { + TBOX result; + for (int b = 0; b < blobs.size(); ++b) { + TBOX box = blobs[b]->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) { + if (start >= blobs.size() - 1) return; // Nothing to do. + TESSLINE* outline = blobs[start]->outlines; + for (int i = start + 1; i < end && i < blobs.size(); ++i) { + TBLOB* next_blob = blobs[i]; + // Take the outlines from the next blob. + if (outline == NULL) { + blobs[start]->outlines = next_blob->outlines; + outline = blobs[start]->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. + delete next_blob; + blobs[i] = NULL; + } + // Remove dead blobs from the vector. + for (int i = start + 1; i < end && start + 1 < blobs.size(); ++i) { + blobs.remove(start + 1); + } +} + +#ifndef GRAPHICS_DISABLED +void TWERD::plot(ScrollView* window) { + ScrollView::Color color = WERD::NextColor(ScrollView::BLACK); + for (int b = 0; b < blobs.size(); ++b) { + blobs[b]->plot(window, color, ScrollView::BROWN); + color = WERD::NextColor(color); + } +} +#endif // GRAPHICS_DISABLED + +/********************************************************************** + * 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((outline1->topleft.x + outline1->botright.x) / 2), + static_cast((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((outline2->topleft.x + outline2->botright.x) / 2), + static_cast((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((outline->topleft.x + outline->botright.x) / 2), + static_cast((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; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.h new file mode 100644 index 0000000..0119b47 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blobs.h @@ -0,0 +1,449 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: blobs.h (Formerly blobs.h) + * Description: Blob definition + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 27 15:39:52 1989 + * Modified: Thu Mar 28 15:33:38 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef BLOBS_H +#define BLOBS_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "clst.h" +#include "normalis.h" +#include "publictypes.h" +#include "rect.h" +#include "vecfuncs.h" + +class BLOCK; +class C_BLOB; +class C_OUTLINE; +class LLSQ; +class ROW; +class WERD; + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define EDGEPTFLAGS 4 /*concavity,length etc. */ + +struct TPOINT { + TPOINT(): x(0), y(0) {} + TPOINT(inT16 vx, inT16 vy) : x(vx), y(vy) {} + TPOINT(const ICOORD &ic) : x(ic.x()), y(ic.y()) {} + + void operator+=(const TPOINT& other) { + x += other.x; + y += other.y; + } + void operator/=(int divisor) { + x /= divisor; + y /= divisor; + } + bool operator==(const TPOINT& other) const { + return x == other.x && y == other.y; + } + // Returns true when the two line segments cross each other. + // (Moved from outlines.cpp). + static bool IsCrossed(const TPOINT& a0, const TPOINT& a1, const TPOINT& b0, + const TPOINT& b1); + + inT16 x; // absolute x coord. + inT16 y; // absolute y coord. +}; +typedef TPOINT VECTOR; // structure for coordinates. + +struct EDGEPT { + EDGEPT() + : next(NULL), prev(NULL), src_outline(NULL), start_step(0), step_count(0) { + memset(flags, 0, EDGEPTFLAGS * sizeof(flags[0])); + } + EDGEPT(const EDGEPT& src) : next(NULL), prev(NULL) { + CopyFrom(src); + } + EDGEPT& operator=(const EDGEPT& src) { + CopyFrom(src); + return *this; + } + // Copies the data elements, but leaves the pointers untouched. + void CopyFrom(const EDGEPT& src) { + pos = src.pos; + vec = src.vec; + memcpy(flags, src.flags, EDGEPTFLAGS * sizeof(flags[0])); + src_outline = src.src_outline; + start_step = src.start_step; + step_count = src.step_count; + } + // Returns the squared distance between the points, with the x-component + // weighted by x_factor. + int WeightedDistance(const EDGEPT& other, int x_factor) const { + int x_dist = pos.x - other.pos.x; + int y_dist = pos.y - other.pos.y; + return x_dist * x_dist * x_factor + y_dist * y_dist; + } + // Returns true if the positions are equal. + bool EqualPos(const EDGEPT& other) const { return pos == other.pos; } + // Returns the bounding box of the outline segment from *this to *end. + // Ignores hidden edge flags. + TBOX SegmentBox(const EDGEPT* end) const { + TBOX box(pos.x, pos.y, pos.x, pos.y); + const EDGEPT* pt = this; + do { + pt = pt->next; + if (pt->pos.x < box.left()) box.set_left(pt->pos.x); + if (pt->pos.x > box.right()) box.set_right(pt->pos.x); + if (pt->pos.y < box.bottom()) box.set_bottom(pt->pos.y); + if (pt->pos.y > box.top()) box.set_top(pt->pos.y); + } while (pt != end && pt != this); + return box; + } + // Returns the area of the outline segment from *this to *end. + // Ignores hidden edge flags. + int SegmentArea(const EDGEPT* end) const { + int area = 0; + const EDGEPT* pt = this->next; + do { + TPOINT origin_vec(pt->pos.x - pos.x, pt->pos.y - pos.y); + area += CROSS(origin_vec, pt->vec); + pt = pt->next; + } while (pt != end && pt != this); + return area; + } + // Returns true if the number of points in the outline segment from *this to + // *end is less that min_points and false if we get back to *this first. + // Ignores hidden edge flags. + bool ShortNonCircularSegment(int min_points, const EDGEPT* end) const { + int count = 0; + const EDGEPT* pt = this; + do { + if (pt == end) return true; + pt = pt->next; + ++count; + } while (pt != this && count <= min_points); + return false; + } + + // Accessors to hide or reveal a cut edge from feature extractors. + void Hide() { + flags[0] = true; + } + void Reveal() { + flags[0] = false; + } + bool IsHidden() const { + return flags[0] != 0; + } + void MarkChop() { + flags[2] = true; + } + bool IsChopPt() const { + return flags[2] != 0; + } + + TPOINT pos; // position + VECTOR vec; // vector to next point + // TODO(rays) Remove flags and replace with + // is_hidden, runlength, dir, and fixed. The only use + // of the flags other than is_hidden is in polyaprx.cpp. + char flags[EDGEPTFLAGS]; // concavity, length etc + EDGEPT* next; // anticlockwise element + EDGEPT* prev; // clockwise element + C_OUTLINE* src_outline; // Outline it came from. + // The following fields are not used if src_outline is NULL. + int start_step; // Location of pos in src_outline. + int step_count; // Number of steps used (may wrap around). +}; + +// For use in chop and findseam to keep a list of which EDGEPTs were inserted. +CLISTIZEH(EDGEPT); + +struct TESSLINE { + TESSLINE() : is_hole(false), loop(NULL), next(NULL) {} + TESSLINE(const TESSLINE& src) : loop(NULL), next(NULL) { + CopyFrom(src); + } + ~TESSLINE() { + Clear(); + } + TESSLINE& operator=(const TESSLINE& src) { + CopyFrom(src); + return *this; + } + // Consume the circular list of EDGEPTs to make a TESSLINE. + static TESSLINE* BuildFromOutlineList(EDGEPT* outline); + // Copies the data and the outline, but leaves next untouched. + void CopyFrom(const TESSLINE& src); + // Deletes owned data. + void Clear(); + // Normalize in-place using the DENORM. + void Normalize(const DENORM& denorm); + // Rotates by the given rotation in place. + void Rotate(const FCOORD rotation); + // Moves by the given vec in place. + void Move(const ICOORD vec); + // Scales by the given factor in place. + void Scale(float factor); + // Sets up the start and vec members of the loop from the pos members. + void SetupFromPos(); + // Recomputes the bounding box from the points in the loop. + void ComputeBoundingBox(); + // 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 MinMaxCrossProduct(const TPOINT vec, int* min_xp, int* max_xp) const; + + TBOX bounding_box() const; + // Returns true if *this and other have equal bounding boxes. + bool SameBox(const TESSLINE& other) const { + return topleft == other.topleft && botright == other.botright; + } + // Returns true if the given line segment crosses any outline of this blob. + bool SegmentCrosses(const TPOINT& pt1, const TPOINT& pt2) const { + if (Contains(pt1) && Contains(pt2)) { + EDGEPT* pt = loop; + do { + if (TPOINT::IsCrossed(pt1, pt2, pt->pos, pt->next->pos)) return true; + pt = pt->next; + } while (pt != loop); + } + return false; + } + // Returns true if the point is contained within the outline box. + bool Contains(const TPOINT& pt) const { + return topleft.x <= pt.x && pt.x <= botright.x && + botright.y <= pt.y && pt.y <= topleft.y; + } + + #ifndef GRAPHICS_DISABLED + void plot(ScrollView* window, ScrollView::Color color, + ScrollView::Color child_color); + #endif // GRAPHICS_DISABLED + + // Returns the first outline point that has a different src_outline to its + // predecessor, or, if all the same, the lowest indexed point. + EDGEPT* FindBestStartPt() const; + + + int BBArea() const { + return (botright.x - topleft.x) * (topleft.y - botright.y); + } + + TPOINT topleft; // Top left of loop. + TPOINT botright; // Bottom right of loop. + TPOINT start; // Start of loop. + bool is_hole; // True if this is a hole/child outline. + EDGEPT *loop; // Edgeloop. + TESSLINE *next; // Next outline in blob. +}; // Outline structure. + +struct TBLOB { + TBLOB() : outlines(NULL) {} + TBLOB(const TBLOB& src) : outlines(NULL) { + CopyFrom(src); + } + ~TBLOB() { + Clear(); + } + TBLOB& operator=(const TBLOB& src) { + CopyFrom(src); + return *this; + } + // Factory to build a TBLOB from a C_BLOB with polygonal approximation along + // the way. If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB + // contain pointers to the input C_OUTLINEs that enable higher-resolution + // feature extraction that does not use the polygonal approximation. + static TBLOB* PolygonalCopy(bool allow_detailed_fx, C_BLOB* src); + // Factory builds a blob with no outlines, but copies the other member data. + static TBLOB* ShallowCopy(const TBLOB& src); + // 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 input blob + // can be used directly. Otherwise a new TBLOB is returned which must be + // deleted after use. + TBLOB* ClassifyNormalizeIfNeeded() const; + + // Copies the data and the outlines, but leaves next untouched. + void CopyFrom(const TBLOB& src); + // Deletes owned data. + void Clear(); + // Sets up the built-in DENORM and normalizes the blob in-place. + // For parameters see DENORM::SetupNormalization, plus the inverse flag for + // this blob and the Pix for the full image. + void Normalize(const BLOCK* block, + const FCOORD* rotation, + const DENORM* predecessor, + float x_origin, float y_origin, + float x_scale, float y_scale, + float final_xshift, float final_yshift, + bool inverse, Pix* pix); + // Rotates by the given rotation in place. + void Rotate(const FCOORD rotation); + // Moves by the given vec in place. + void Move(const ICOORD vec); + // Scales by the given factor in place. + void Scale(float factor); + // Recomputes the bounding boxes of the outlines. + void ComputeBoundingBoxes(); + + // Returns the number of outlines. + int NumOutlines() const; + + TBOX bounding_box() const; + + // Returns true if the given line segment crosses any outline of this blob. + bool SegmentCrossesOutline(const TPOINT& pt1, const TPOINT& pt2) const { + for (const TESSLINE* outline = outlines; outline != NULL; + outline = outline->next) { + if (outline->SegmentCrosses(pt1, pt2)) return true; + } + return false; + } + // Returns true if the point is contained within any of the outline boxes. + bool Contains(const TPOINT& pt) const { + for (const TESSLINE* outline = outlines; outline != NULL; + outline = outline->next) { + if (outline->Contains(pt)) return true; + } + return false; + } + + // Finds and deletes any duplicate outlines in this blob, without deleting + // their EDGEPTs. + void EliminateDuplicateOutlines(); + + // Swaps the outlines of *this and next if needed to keep the centers in + // increasing x. + void CorrectBlobOrder(TBLOB* next); + + const DENORM& denorm() const { + return denorm_; + } + + #ifndef GRAPHICS_DISABLED + void plot(ScrollView* window, ScrollView::Color color, + ScrollView::Color child_color); + #endif // GRAPHICS_DISABLED + + int BBArea() const { + int total_area = 0; + for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) + total_area += outline->BBArea(); + return total_area; + } + + // Computes the center of mass and second moments for the old baseline and + // 2nd moment normalizations. Returns the outline length. + // The input denorm should be the normalizations that have been applied from + // the image to the current state of this TBLOB. + int ComputeMoments(FCOORD* center, FCOORD* second_moments) const; + // Computes the precise bounding box of the coords that are generated by + // GetEdgeCoords. This may be different from the bounding box of the polygon. + void GetPreciseBoundingBox(TBOX* precise_box) const; + // Adds edges to the given vectors. + // For all the edge steps in all the outlines, or polygonal approximation + // where there are no edge steps, collects the steps into x_coords/y_coords. + // x_coords is a collection of the x-coords of vertical edges for each + // y-coord starting at box.bottom(). + // y_coords is a collection of the y-coords of horizontal edges for each + // x-coord starting at box.left(). + // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. + // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. + void GetEdgeCoords(const TBOX& box, + GenericVector >* x_coords, + GenericVector >* y_coords) const; + + TESSLINE *outlines; // List of outlines in blob. + + private: // TODO(rays) Someday the data members will be private too. + // For all the edge steps in all the outlines, or polygonal approximation + // where there are no edge steps, collects the steps into the bounding_box, + // llsq and/or the x_coords/y_coords. Both are used in different kinds of + // normalization. + // For a description of x_coords, y_coords, see GetEdgeCoords above. + void CollectEdges(const TBOX& box, + TBOX* bounding_box, LLSQ* llsq, + GenericVector >* x_coords, + GenericVector >* y_coords) const; + + private: + // DENORM indicating the transformations that this blob has undergone so far. + DENORM denorm_; +}; // Blob structure. + +struct TWERD { + TWERD() : latin_script(false) {} + TWERD(const TWERD& src) { + CopyFrom(src); + } + ~TWERD() { + Clear(); + } + TWERD& operator=(const TWERD& src) { + CopyFrom(src); + return *this; + } + // Factory to build a TWERD from a (C_BLOB) WERD, with polygonal + // approximation along the way. + static TWERD* PolygonalCopy(bool allow_detailed_fx, WERD* src); + // Baseline normalizes the blobs in-place, recording the normalization in the + // DENORMs in the blobs. + void BLNormalize(const BLOCK* block, const ROW* row, Pix* pix, bool inverse, + float x_height, float baseline_shift, bool numeric_mode, + tesseract::OcrEngineMode hint, + const TBOX* norm_box, + DENORM* word_denorm); + // Copies the data and the blobs, but leaves next untouched. + void CopyFrom(const TWERD& src); + // Deletes owned data. + void Clear(); + // Recomputes the bounding boxes of the blobs. + void ComputeBoundingBoxes(); + + // Returns the number of blobs in the word. + int NumBlobs() const { + return blobs.size(); + } + TBOX bounding_box() const; + + // Merges the blobs from start to end, not including end, and deletes + // the blobs between start and end. + void MergeBlobs(int start, int end); + + void plot(ScrollView* window); + + GenericVector blobs; // Blobs in word. + bool latin_script; // This word is in a latin-based script. +}; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +// TODO(rays) Make divisible_blob and divide_blobs members of TBLOB. +bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location); + +void divide_blobs(TBLOB *blob, TBLOB *other_blob, bool italic_blob, + const TPOINT& location); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.cpp new file mode 100644 index 0000000..3b4d824 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.cpp @@ -0,0 +1,71 @@ +/********************************************************************** + * File: blread.cpp (Formerly pdread.c) + * Description: Friend function of BLOCK to read the uscan pd file. + * Author: Ray Smith + * Created: Mon Mar 18 14:39:00 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#ifdef __UNIX__ +#include +#endif +#include "scanutils.h" +#include "fileerr.h" +#include "blread.h" + +#define UNLV_EXT ".uzn" // unlv zone file + +/********************************************************************** + * read_unlv_file + * + * Read a whole unlv zone file to make a list of blocks. + **********************************************************************/ + +bool read_unlv_file( //print list of sides + STRING name, //basename of file + inT32 xsize, //image size + inT32 ysize, //image size + BLOCK_LIST *blocks //output list + ) { + FILE *pdfp; //file pointer + BLOCK *block; //current block + int x; //current top-down coords + int y; + int width; //of current block + int height; + BLOCK_IT block_it = blocks; //block iterator + + name += UNLV_EXT; //add extension + if ((pdfp = fopen (name.string (), "rb")) == NULL) { + return false; //didn't read one + } else { + while (tfscanf(pdfp, "%d %d %d %d %*s", &x, &y, &width, &height) >= 4) { + //make rect block + block = new BLOCK (name.string (), TRUE, 0, 0, + (inT16) x, (inT16) (ysize - y - height), + (inT16) (x + width), (inT16) (ysize - y)); + //on end of list + block_it.add_to_end (block); + } + fclose(pdfp); + } + return true; +} + +void FullPageBlock(int width, int height, BLOCK_LIST *blocks) { + BLOCK_IT block_it(blocks); + BLOCK* block = new BLOCK("", TRUE, 0, 0, 0, 0, width, height); + block_it.add_to_end(block); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.h new file mode 100644 index 0000000..3969d00 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/blread.h @@ -0,0 +1,33 @@ +/********************************************************************** + * File: blread.h (Formerly pdread.h) + * Description: Friend function of BLOCK to read the uscan pd file. + * Author: Ray Smith + * Created: Mon Mar 18 14:39:00 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLREAD_H +#define BLREAD_H + +#include "params.h" +#include "ocrblock.h" + +bool read_unlv_file( //print list of sides + STRING name, //basename of file + inT32 xsize, //image size + inT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +void FullPageBlock(int width, int height, BLOCK_LIST *blocks); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.cpp new file mode 100644 index 0000000..d6ceebb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.cpp @@ -0,0 +1,235 @@ +/********************************************************************** + * File: boxread.cpp + * Description: Read data from a box file. + * Author: Ray Smith + * Created: Fri Aug 24 17:47:23 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. + * + **********************************************************************/ + +#include "boxread.h" +#include + +#include "fileerr.h" +#include "rect.h" +#include "strngs.h" +#include "tprintf.h" +#include "unichar.h" + +// Special char code used to identify multi-blob labels. +static const char* kMultiBlobLabelCode = "WordStr"; + +// Open the boxfile based on the given image filename. +FILE* OpenBoxFile(const STRING& fname) { + STRING filename = BoxFileName(fname); + FILE* box_file = NULL; + if (!(box_file = fopen(filename.string(), "rb"))) { + CANTOPENFILE.error("read_next_box", TESSEXIT, "Can't open box file %s", + filename.string()); + } + return box_file; +} + +// Reads all boxes from the given filename. +// Reads a specific target_page number if >= 0, or all pages otherwise. +// Skips blanks if skip_blanks is true. +// The UTF-8 label of the box is put in texts, and the full box definition as +// a string is put in box_texts, with the corresponding page number in pages. +// Each of the output vectors is optional (may be NULL). +// Returns false if no boxes are found. +bool ReadAllBoxes(int target_page, bool skip_blanks, const STRING& filename, + GenericVector* boxes, + GenericVector* texts, + GenericVector* box_texts, + GenericVector* pages) { + GenericVector box_data; + if (!tesseract::LoadDataFromFile(BoxFileName(filename), &box_data)) + return false; + // Convert the array of bytes to a string, so it can be used by the parser. + box_data.push_back('\0'); + return ReadMemBoxes(target_page, skip_blanks, &box_data[0], boxes, texts, + box_texts, pages); +} + +// Reads all boxes from the string. Otherwise, as ReadAllBoxes. +bool ReadMemBoxes(int target_page, bool skip_blanks, const char* box_data, + GenericVector* boxes, + GenericVector* texts, + GenericVector* box_texts, + GenericVector* pages) { + STRING box_str(box_data); + GenericVector lines; + box_str.split('\n', &lines); + if (lines.empty()) return false; + int num_boxes = 0; + for (int i = 0; i < lines.size(); ++i) { + int page = 0; + STRING utf8_str; + TBOX box; + if (!ParseBoxFileStr(lines[i].string(), &page, &utf8_str, &box)) { + continue; + } + if (skip_blanks && (utf8_str == " " || utf8_str == "\t")) continue; + if (target_page >= 0 && page != target_page) continue; + if (boxes != NULL) boxes->push_back(box); + if (texts != NULL) texts->push_back(utf8_str); + if (box_texts != NULL) { + STRING full_text; + MakeBoxFileStr(utf8_str.string(), box, target_page, &full_text); + box_texts->push_back(full_text); + } + if (pages != NULL) pages->push_back(page); + ++num_boxes; + } + return num_boxes > 0; +} + +// Returns the box file name corresponding to the given image_filename. +STRING BoxFileName(const STRING& image_filename) { + STRING box_filename = image_filename; + const char *lastdot = strrchr(box_filename.string(), '.'); + if (lastdot != NULL) + box_filename.truncate_at(lastdot - box_filename.string()); + + box_filename += ".box"; + return box_filename; +} + +// TODO(rays) convert all uses of ReadNextBox to use the new ReadAllBoxes. +// Box files are used ONLY DURING TRAINING, but by both processes of +// creating tr files with tesseract, and unicharset_extractor. +// ReadNextBox factors out the code to interpret a line of a box +// file so that applybox and unicharset_extractor interpret the same way. +// This function returns the next valid box file utf8 string and coords +// and returns true, or false on eof (and closes the file). +// It ignores the utf8 file signature ByteOrderMark (U+FEFF=EF BB BF), checks +// for valid utf-8 and allows space or tab between fields. +// utf8_str is set with the unichar string, and bounding box with the box. +// If there are page numbers in the file, it reads them all. +bool ReadNextBox(int *line_number, FILE* box_file, + STRING* utf8_str, TBOX* bounding_box) { + return ReadNextBox(-1, line_number, box_file, utf8_str, bounding_box); +} + +// As ReadNextBox above, but get a specific page number. (0-based) +// Use -1 to read any page number. Files without page number all +// read as if they are page 0. +bool ReadNextBox(int target_page, int *line_number, FILE* box_file, + STRING* utf8_str, TBOX* bounding_box) { + int page = 0; + char buff[kBoxReadBufSize]; // boxfile read buffer + char *buffptr = buff; + + while (fgets(buff, sizeof(buff) - 1, box_file)) { + (*line_number)++; + + buffptr = buff; + const unsigned char *ubuf = reinterpret_cast(buffptr); + if (ubuf[0] == 0xef && ubuf[1] == 0xbb && ubuf[2] == 0xbf) + buffptr += 3; // Skip unicode file designation. + // Check for blank lines in box file + if (*buffptr == '\n' || *buffptr == '\0') continue; + // Skip blank boxes. + if (*buffptr == ' ' || *buffptr == '\t') continue; + if (*buffptr != '\0') { + if (!ParseBoxFileStr(buffptr, &page, utf8_str, bounding_box)) { + tprintf("Box file format error on line %i; ignored\n", *line_number); + continue; + } + if (target_page >= 0 && target_page != page) + continue; // Not on the appropriate page. + return true; // Successfully read a box. + } + } + fclose(box_file); + return false; // EOF +} + +// Parses the given box file string into a page_number, utf8_str, and +// bounding_box. Returns true on a successful parse. +// The box file is assumed to contain box definitions, one per line, of the +// following format for blob-level boxes: +// +// and for word/line-level boxes: +// WordStr # +// See applyybox.cpp for more information. +bool ParseBoxFileStr(const char* boxfile_str, int* page_number, + STRING* utf8_str, TBOX* bounding_box) { + *bounding_box = TBOX(); // Initialize it to empty. + *utf8_str = ""; + char uch[kBoxReadBufSize]; + const char *buffptr = boxfile_str; + // Read the unichar without messing up on Tibetan. + // According to issue 253 the utf-8 surrogates 85 and A0 are treated + // as whitespace by sscanf, so it is more reliable to just find + // ascii space and tab. + int uch_len = 0; + // Skip unicode file designation, if present. + const unsigned char *ubuf = reinterpret_cast(buffptr); + if (ubuf[0] == 0xef && ubuf[1] == 0xbb && ubuf[2] == 0xbf) + buffptr += 3; + // Allow a single blank as the UTF-8 string. Check for empty string and + // then blindly eat the first character. + if (*buffptr == '\0') return false; + do { + uch[uch_len++] = *buffptr++; + } while (*buffptr != '\0' && *buffptr != ' ' && *buffptr != '\t' && + uch_len < kBoxReadBufSize - 1); + uch[uch_len] = '\0'; + if (*buffptr != '\0') ++buffptr; + int x_min, y_min, x_max, y_max; + *page_number = 0; + int count = sscanf(buffptr, "%d %d %d %d %d", + &x_min, &y_min, &x_max, &y_max, page_number); + if (count != 5 && count != 4) { + tprintf("Bad box coordinates in boxfile string! %s\n", ubuf); + return false; + } + // Test for long space-delimited string label. + if (strcmp(uch, kMultiBlobLabelCode) == 0 && + (buffptr = strchr(buffptr, '#')) != NULL) { + strncpy(uch, buffptr + 1, kBoxReadBufSize - 1); + uch[kBoxReadBufSize - 1] = '\0'; // Prevent buffer overrun. + chomp_string(uch); + uch_len = strlen(uch); + } + // Validate UTF8 by making unichars with it. + int used = 0; + while (used < uch_len) { + UNICHAR ch(uch + used, uch_len - used); + int new_used = ch.utf8_len(); + if (new_used == 0) { + tprintf("Bad UTF-8 str %s starts with 0x%02x at col %d\n", + uch + used, uch[used], used + 1); + return false; + } + used += new_used; + } + *utf8_str = uch; + if (x_min > x_max) Swap(&x_min, &x_max); + if (y_min > y_max) Swap(&y_min, &y_max); + bounding_box->set_to_given_coords(x_min, y_min, x_max, y_max); + return true; // Successfully read a box. +} + +// Creates a box file string from a unichar string, TBOX and page number. +void MakeBoxFileStr(const char* unichar_str, const TBOX& box, int page_num, + STRING* box_str) { + *box_str = unichar_str; + box_str->add_str_int(" ", box.left()); + box_str->add_str_int(" ", box.bottom()); + box_str->add_str_int(" ", box.right()); + box_str->add_str_int(" ", box.top()); + box_str->add_str_int(" ", page_num); +} + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.h new file mode 100644 index 0000000..f12853d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxread.h @@ -0,0 +1,85 @@ +/********************************************************************** + * File: boxread.cpp + * Description: Read data from a box file. + * Author: Ray Smith + * Created: Fri Aug 24 17:47:23 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_CCUTIL_BOXREAD_H__ +#define TESSERACT_CCUTIL_BOXREAD_H__ + +#include +#include "genericvector.h" +#include "strngs.h" + +class STRING; +class TBOX; + +// Size of buffer used to read a line from a box file. +const int kBoxReadBufSize = 1024; + +// Open the boxfile based on the given image filename. +// Returns NULL if the box file cannot be opened. +FILE* OpenBoxFile(const STRING& fname); + +// Reads all boxes from the given filename. +// Reads a specific target_page number if >= 0, or all pages otherwise. +// Skips blanks if skip_blanks is true. +// The UTF-8 label of the box is put in texts, and the full box definition as +// a string is put in box_texts, with the corresponding page number in pages. +// Each of the output vectors is optional (may be NULL). +// Returns false if no boxes are found. +bool ReadAllBoxes(int target_page, bool skip_blanks, const STRING& filename, + GenericVector* boxes, + GenericVector* texts, + GenericVector* box_texts, + GenericVector* pages); + +// Reads all boxes from the string. Otherwise, as ReadAllBoxes. +bool ReadMemBoxes(int target_page, bool skip_blanks, const char* box_data, + GenericVector* boxes, + GenericVector* texts, + GenericVector* box_texts, + GenericVector* pages); + +// Returns the box file name corresponding to the given image_filename. +STRING BoxFileName(const STRING& image_filename); + +// ReadNextBox factors out the code to interpret a line of a box +// file so that applybox and unicharset_extractor interpret the same way. +// This function returns the next valid box file utf8 string and coords +// and returns true, or false on eof (and closes the file). +// It ignores the utf8 file signature ByteOrderMark (U+FEFF=EF BB BF), checks +// for valid utf-8 and allows space or tab between fields. +// utf8_str is set with the unichar string, and bounding box with the box. +// If there are page numbers in the file, it reads them all. +bool ReadNextBox(int *line_number, FILE* box_file, + STRING* utf8_str, TBOX* bounding_box); +// As ReadNextBox above, but get a specific page number. (0-based) +// Use -1 to read any page number. Files without page number all +// read as if they are page 0. +bool ReadNextBox(int target_page, int *line_number, FILE* box_file, + STRING* utf8_str, TBOX* bounding_box); + +// Parses the given box file string into a page_number, utf8_str, and +// bounding_box. Returns true on a successful parse. +bool ParseBoxFileStr(const char* boxfile_str, int* page_number, + STRING* utf8_str, TBOX* bounding_box); + +// Creates a box file string from a unichar string, TBOX and page number. +void MakeBoxFileStr(const char* unichar_str, const TBOX& box, int page_num, + STRING* box_str); + +#endif // TESSERACT_CCUTIL_BOXREAD_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.cpp new file mode 100644 index 0000000..9b1ce33 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.cpp @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////// +// File: boxword.h +// Description: Class to represent the bounding boxes of the output. +// Author: Ray Smith +// Created: Tue May 25 14:18:14 PDT 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 "blobs.h" +#include "boxword.h" +#include "normalis.h" +#include "ocrblock.h" +#include "pageres.h" + +namespace tesseract { + +// Clip output boxes to input blob boxes for bounds that are within this +// tolerance. Otherwise, the blob may be chopped and we have to just use +// the word bounding box. +const int kBoxClipTolerance = 2; + +BoxWord::BoxWord() : length_(0) { +} + +BoxWord::BoxWord(const BoxWord& src) { + CopyFrom(src); +} + +BoxWord::~BoxWord() { +} + +BoxWord& BoxWord::operator=(const BoxWord& src) { + CopyFrom(src); + return *this; +} + +void BoxWord::CopyFrom(const BoxWord& src) { + bbox_ = src.bbox_; + length_ = src.length_; + boxes_.clear(); + boxes_.reserve(length_); + for (int i = 0; i < length_; ++i) + boxes_.push_back(src.boxes_[i]); +} + +// Factory to build a BoxWord from a TWERD using the DENORMs on each blob to +// switch back to original image coordinates. +BoxWord* BoxWord::CopyFromNormalized(TWERD* tessword) { + BoxWord* boxword = new BoxWord(); + // Count the blobs. + boxword->length_ = tessword->NumBlobs(); + // Allocate memory. + boxword->boxes_.reserve(boxword->length_); + + for (int b = 0; b < boxword->length_; ++b) { + TBLOB* tblob = tessword->blobs[b]; + TBOX blob_box; + for (TESSLINE* outline = tblob->outlines; outline != NULL; + outline = outline->next) { + EDGEPT* edgept = outline->loop; + // Iterate over the edges. + do { + if (!edgept->IsHidden() || !edgept->prev->IsHidden()) { + ICOORD pos(edgept->pos.x, edgept->pos.y); + TPOINT denormed; + tblob->denorm().DenormTransform(NULL, edgept->pos, &denormed); + pos.set_x(denormed.x); + pos.set_y(denormed.y); + TBOX pt_box(pos, pos); + blob_box += pt_box; + } + edgept = edgept->next; + } while (edgept != outline->loop); + } + boxword->boxes_.push_back(blob_box); + } + boxword->ComputeBoundingBox(); + return boxword; +} + +// Clean up the bounding boxes from the polygonal approximation by +// expanding slightly, then clipping to the blobs from the original_word +// that overlap. If not null, the block provides the inverse rotation. +void BoxWord::ClipToOriginalWord(const BLOCK* block, WERD* original_word) { + for (int i = 0; i < length_; ++i) { + TBOX box = boxes_[i]; + // Expand by a single pixel, as the poly approximation error is 1 pixel. + box = TBOX(box.left() - 1, box.bottom() - 1, + box.right() + 1, box.top() + 1); + // Now find the original box that matches. + TBOX original_box; + C_BLOB_IT b_it(original_word->cblob_list()); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + TBOX blob_box = b_it.data()->bounding_box(); + if (block != NULL) + blob_box.rotate(block->re_rotation()); + if (blob_box.major_overlap(box)) { + original_box += blob_box; + } + } + if (!original_box.null_box()) { + if (NearlyEqual(original_box.left(), box.left(), kBoxClipTolerance)) + box.set_left(original_box.left()); + if (NearlyEqual(original_box.right(), box.right(), + kBoxClipTolerance)) + box.set_right(original_box.right()); + if (NearlyEqual(original_box.top(), box.top(), kBoxClipTolerance)) + box.set_top(original_box.top()); + if (NearlyEqual(original_box.bottom(), box.bottom(), + kBoxClipTolerance)) + box.set_bottom(original_box.bottom()); + } + original_box = original_word->bounding_box(); + if (block != NULL) + original_box.rotate(block->re_rotation()); + boxes_[i] = box.intersection(original_box); + } + ComputeBoundingBox(); +} + +// Merges the boxes from start to end, not including end, and deletes +// the boxes between start and end. +void BoxWord::MergeBoxes(int start, int end) { + start = ClipToRange(start, 0, length_); + end = ClipToRange(end, 0, length_); + if (end <= start + 1) + return; + for (int i = start + 1; i < end; ++i) { + boxes_[start] += boxes_[i]; + } + int shrinkage = end - 1 - start; + length_ -= shrinkage; + for (int i = start + 1; i < length_; ++i) + boxes_[i] = boxes_[i + shrinkage]; + boxes_.truncate(length_); +} + +// Inserts a new box before the given index. +// Recomputes the bounding box. +void BoxWord::InsertBox(int index, const TBOX& box) { + if (index < length_) + boxes_.insert(box, index); + else + boxes_.push_back(box); + length_ = boxes_.size(); + ComputeBoundingBox(); +} + +// Changes the box at the given index to the new box. +// Recomputes the bounding box. +void BoxWord::ChangeBox(int index, const TBOX& box) { + boxes_[index] = box; + ComputeBoundingBox(); +} + +// Deletes the box with the given index, and shuffles up the rest. +// Recomputes the bounding box. +void BoxWord::DeleteBox(int index) { + ASSERT_HOST(0 <= index && index < length_); + boxes_.remove(index); + --length_; + ComputeBoundingBox(); +} + +// Deletes all the boxes stored in BoxWord. +void BoxWord::DeleteAllBoxes() { + length_ = 0; + boxes_.clear(); + bbox_ = TBOX(); +} + +// Computes the bounding box of the word. +void BoxWord::ComputeBoundingBox() { + bbox_ = TBOX(); + for (int i = 0; i < length_; ++i) + bbox_ += boxes_[i]; +} + +// This and other putatively are the same, so call the (permanent) callback +// for each blob index where the bounding boxes match. +// The callback is deleted on completion. +void BoxWord::ProcessMatchedBlobs(const TWERD& other, + TessCallback1* cb) const { + for (int i = 0; i < length_ && i < other.NumBlobs(); ++i) { + TBOX blob_box = other.blobs[i]->bounding_box(); + if (blob_box == boxes_[i]) + cb->Run(i); + } + delete cb; +} + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.h new file mode 100644 index 0000000..c1fab06 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/boxword.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////// +// File: boxword.h +// Description: Class to represent the bounding boxes of the output. +// Author: Ray Smith +// Created: Tue May 25 14:18:14 PDT 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CSTRUCT_BOXWORD_H__ +#define TESSERACT_CSTRUCT_BOXWORD_H__ + +#include "genericvector.h" +#include "rect.h" +#include "unichar.h" + +class BLOCK; +class DENORM; +struct TWERD; +class UNICHARSET; +class WERD; +class WERD_CHOICE; +class WERD_RES; + +namespace tesseract { + +// Class to hold an array of bounding boxes for an output word and +// the bounding box of the whole word. +class BoxWord { + public: + BoxWord(); + explicit BoxWord(const BoxWord& src); + ~BoxWord(); + + BoxWord& operator=(const BoxWord& src); + + void CopyFrom(const BoxWord& src); + + // Factory to build a BoxWord from a TWERD using the DENORMs on each blob to + // switch back to original image coordinates. + static BoxWord* CopyFromNormalized(TWERD* tessword); + + // Clean up the bounding boxes from the polygonal approximation by + // expanding slightly, then clipping to the blobs from the original_word + // that overlap. If not null, the block provides the inverse rotation. + void ClipToOriginalWord(const BLOCK* block, WERD* original_word); + + // Merges the boxes from start to end, not including end, and deletes + // the boxes between start and end. + void MergeBoxes(int start, int end); + + // Inserts a new box before the given index. + // Recomputes the bounding box. + void InsertBox(int index, const TBOX& box); + + // Changes the box at the given index to the new box. + // Recomputes the bounding box. + void ChangeBox(int index, const TBOX& box); + + // Deletes the box with the given index, and shuffles up the rest. + // Recomputes the bounding box. + void DeleteBox(int index); + + // Deletes all the boxes stored in BoxWord. + void DeleteAllBoxes(); + + // This and other putatively are the same, so call the (permanent) callback + // for each blob index where the bounding boxes match. + // The callback is deleted on completion. + void ProcessMatchedBlobs(const TWERD& other, TessCallback1* cb) const; + + const TBOX& bounding_box() const { + return bbox_; + } + int length() const { return length_; } + const TBOX& BlobBox(int index) const { + return boxes_[index]; + } + + private: + void ComputeBoundingBox(); + + TBOX bbox_; + int length_; + GenericVector boxes_; +}; + +} // namespace tesseract. + + +#endif // TESSERACT_CSTRUCT_BOXWORD_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.cpp new file mode 100644 index 0000000..d030f50 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.cpp @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////// +// File: ccstruct.cpp +// Description: ccstruct 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. +// +/////////////////////////////////////////////////////////////////////// + +#include "ccstruct.h" + +namespace tesseract { + +// APPROXIMATIONS of the fractions of the character cell taken by +// the descenders, ascenders, and x-height. +const double CCStruct::kDescenderFraction = 0.25; +const double CCStruct::kXHeightFraction = 0.5; +const double CCStruct::kAscenderFraction = 0.25; +const double CCStruct::kXHeightCapRatio = CCStruct::kXHeightFraction / + (CCStruct::kXHeightFraction + CCStruct::kAscenderFraction); + +CCStruct::CCStruct() {} + +CCStruct::~CCStruct() { +} + +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.h new file mode 100644 index 0000000..3f92122 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ccstruct.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////// +// File: ccstruct.h +// Description: ccstruct 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_CCSTRUCT_CCSTRUCT_H__ +#define TESSERACT_CCSTRUCT_CCSTRUCT_H__ + +#include "cutil.h" + +namespace tesseract { +class CCStruct : public CUtil { + public: + CCStruct(); + ~CCStruct(); + + // Globally accessible constants. + // APPROXIMATIONS of the fractions of the character cell taken by + // the descenders, ascenders, and x-height. + static const double kDescenderFraction; // = 0.25; + static const double kXHeightFraction; // = 0.5; + static const double kAscenderFraction; // = 0.25; + // Derived value giving the x-height as a fraction of cap-height. + static const double kXHeightCapRatio; // = XHeight/(XHeight + Ascender). +}; + +class Tesseract; +} // namespace tesseract + + +#endif // TESSERACT_CCSTRUCT_CCSTRUCT_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.cpp new file mode 100644 index 0000000..5b8792a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.cpp @@ -0,0 +1,1075 @@ +/********************************************************************** + * File: coutln.c (Formerly coutline.c) + * Description: Code for the C_OUTLINE class. + * Author: Ray Smith + * Created: Mon Oct 07 16:01:57 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#ifdef __UNIX__ +#include +#endif + +#include "coutln.h" + +#include "allheaders.h" +#include "blobs.h" +#include "normalis.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +ELISTIZE (C_OUTLINE) +ICOORD C_OUTLINE::step_coords[4] = { + ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1) +}; + +/** + * @name C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP. + * @param startpt outline to convert + * @param bot_left bounding box + * @param top_right bounding box + * @param length length of loop + */ + +C_OUTLINE::C_OUTLINE(CRACKEDGE* startpt, ICOORD bot_left, ICOORD top_right, + inT16 length) + : box(bot_left, top_right), start(startpt->pos), offsets(NULL) { + inT16 stepindex; //index to step + CRACKEDGE *edgept; //current point + + stepcount = length; //no of steps + if (length == 0) { + steps = NULL; + return; + } + //get memory + steps = (uinT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + edgept = startpt; + + for (stepindex = 0; stepindex < length; stepindex++) { + //set compact step + set_step (stepindex, edgept->stepdir); + edgept = edgept->next; + } +} + +/** + * @name C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG. + */ +C_OUTLINE::C_OUTLINE ( +//constructor + //steps to copy +ICOORD startpt, DIR128 * new_steps, +inT16 length //length of loop +):start (startpt), offsets(NULL) { + inT8 dirdiff; //direction difference + DIR128 prevdir; //previous direction + DIR128 dir; //current direction + DIR128 lastdir; //dir of last step + TBOX new_box; //easy bounding + inT16 stepindex; //index to step + inT16 srcindex; //source steps + ICOORD pos; //current position + + pos = startpt; + stepcount = length; // No. of steps. + ASSERT_HOST(length >= 0); + steps = reinterpret_cast(alloc_mem(step_mem())); // Get memory. + memset(steps, 0, step_mem()); + + lastdir = new_steps[length - 1]; + prevdir = lastdir; + for (stepindex = 0, srcindex = 0; srcindex < length; + stepindex++, srcindex++) { + new_box = TBOX (pos, pos); + box += new_box; + //copy steps + dir = new_steps[srcindex]; + set_step(stepindex, dir); + dirdiff = dir - prevdir; + pos += step (stepindex); + if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) { + stepindex -= 2; //cancel there-and-back + prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir; + } + else + prevdir = dir; + } + ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ()); + do { + dirdiff = step_dir (stepindex - 1) - step_dir (0); + if (dirdiff == 64 || dirdiff == -64) { + start += step (0); + stepindex -= 2; //cancel there-and-back + for (int i = 0; i < stepindex; ++i) + set_step(i, step_dir(i + 1)); + } + } + while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64)); + stepcount = stepindex; + ASSERT_HOST (stepcount >= 4); +} + +/** + * @name C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE. + * @param srcline outline to rotate + * @param rotation rotate to coord + */ + +C_OUTLINE::C_OUTLINE(C_OUTLINE* srcline, FCOORD rotation) : offsets(NULL) { + TBOX new_box; //easy bounding + inT16 stepindex; //index to step + inT16 dirdiff; //direction change + ICOORD pos; //current position + ICOORD prevpos; //previous dest point + + ICOORD destpos; //destination point + inT16 destindex; //index to step + DIR128 dir; //coded direction + uinT8 new_step; + + stepcount = srcline->stepcount * 2; + if (stepcount == 0) { + steps = NULL; + box = srcline->box; + box.rotate(rotation); + return; + } + //get memory + steps = (uinT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + + for (int iteration = 0; iteration < 2; ++iteration) { + DIR128 round1 = iteration == 0 ? 32 : 0; + DIR128 round2 = iteration != 0 ? 32 : 0; + pos = srcline->start; + prevpos = pos; + prevpos.rotate (rotation); + start = prevpos; + box = TBOX (start, start); + destindex = 0; + for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { + pos += srcline->step (stepindex); + destpos = pos; + destpos.rotate (rotation); + // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y()); + while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) { + dir = DIR128 (FCOORD (destpos - prevpos)); + dir += 64; //turn to step style + new_step = dir.get_dir (); + // tprintf(" %i\n", new_step); + if (new_step & 31) { + set_step(destindex++, dir + round1); + prevpos += step(destindex - 1); + if (destindex < 2 + || ((dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) != + -64 && dirdiff != 64)) { + set_step(destindex++, dir + round2); + prevpos += step(destindex - 1); + } else { + prevpos -= step(destindex - 1); + destindex--; + prevpos -= step(destindex - 1); + set_step(destindex - 1, dir + round2); + prevpos += step(destindex - 1); + } + } + else { + set_step(destindex++, dir); + prevpos += step(destindex - 1); + } + while (destindex >= 2 && + ((dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 || + dirdiff == 64)) { + prevpos -= step(destindex - 1); + prevpos -= step(destindex - 2); + destindex -= 2; // Forget u turn + } + //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y()); + new_box = TBOX (destpos, destpos); + box += new_box; + } + } + ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); + dirdiff = step_dir (destindex - 1) - step_dir (0); + while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) { + start += step (0); + destindex -= 2; + for (int i = 0; i < destindex; ++i) + set_step(i, step_dir(i + 1)); + dirdiff = step_dir (destindex - 1) - step_dir (0); + } + if (destindex >= 4) + break; + } + ASSERT_HOST(destindex <= stepcount); + stepcount = destindex; + destpos = start; + for (stepindex = 0; stepindex < stepcount; stepindex++) { + destpos += step (stepindex); + } + ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); +} + +// Build a fake outline, given just a bounding box and append to the list. +void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) { + C_OUTLINE_IT ol_it(outlines); + // Make a C_OUTLINE from the bounds. This is a bit of a hack, + // as there is no outline, just a bounding box, but it works nicely. + CRACKEDGE start; + start.pos = box.topleft(); + C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0); + ol_it.add_to_end(outline); +} + +/** + * @name C_OUTLINE::area + * + * Compute the area of the outline. + */ + +inT32 C_OUTLINE::area() const { + int stepindex; //current step + inT32 total_steps; //steps to do + inT32 total; //total area + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + // We aren't going to modify the list, or its contents, but there is + // no const iterator. + C_OUTLINE_IT it(const_cast(&children)); + + pos = start_pos (); + total_steps = pathlength (); + total = 0; + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + if (next_step.x () < 0) + total += pos.y (); + else if (next_step.x () > 0) + total -= pos.y (); + pos += next_step; + } + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + total += it.data ()->area ();//add areas of children + + return total; +} + +/** + * @name C_OUTLINE::perimeter + * + * Compute the perimeter of the outline and its first level children. + */ + +inT32 C_OUTLINE::perimeter() const { + inT32 total_steps; // Return value. + // We aren't going to modify the list, or its contents, but there is + // no const iterator. + C_OUTLINE_IT it(const_cast(&children)); + + total_steps = pathlength(); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) + total_steps += it.data()->pathlength(); // Add perimeters of children. + + return total_steps; +} + +/** + * @name C_OUTLINE::outer_area + * + * Compute the area of the outline. + */ + +inT32 C_OUTLINE::outer_area() const { + int stepindex; //current step + inT32 total_steps; //steps to do + inT32 total; //total area + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + + pos = start_pos (); + total_steps = pathlength (); + if (total_steps == 0) + return box.area(); + total = 0; + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + if (next_step.x () < 0) + total += pos.y (); + else if (next_step.x () > 0) + total -= pos.y (); + pos += next_step; + } + + return total; +} + +/** + * @name C_OUTLINE::count_transitions + * + * Compute the number of x and y maxes and mins in the outline. + * @param threshold winding number on size + */ + +inT32 C_OUTLINE::count_transitions(inT32 threshold) { + BOOL8 first_was_max_x; //what was first + BOOL8 first_was_max_y; + BOOL8 looking_for_max_x; //what is next + BOOL8 looking_for_min_x; + BOOL8 looking_for_max_y; //what is next + BOOL8 looking_for_min_y; + int stepindex; //current step + inT32 total_steps; //steps to do + //current limits + inT32 max_x, min_x, max_y, min_y; + inT32 initial_x, initial_y; //initial limits + inT32 total; //total changes + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + + pos = start_pos (); + total_steps = pathlength (); + total = 0; + max_x = min_x = pos.x (); + max_y = min_y = pos.y (); + looking_for_max_x = TRUE; + looking_for_min_x = TRUE; + looking_for_max_y = TRUE; + looking_for_min_y = TRUE; + first_was_max_x = FALSE; + first_was_max_y = FALSE; + initial_x = pos.x (); + initial_y = pos.y (); //stop uninit warning + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + pos += next_step; + if (next_step.x () < 0) { + if (looking_for_max_x && pos.x () < min_x) + min_x = pos.x (); + if (looking_for_min_x && max_x - pos.x () > threshold) { + if (looking_for_max_x) { + initial_x = max_x; + first_was_max_x = FALSE; + } + total++; + looking_for_max_x = TRUE; + looking_for_min_x = FALSE; + min_x = pos.x (); //reset min + } + } + else if (next_step.x () > 0) { + if (looking_for_min_x && pos.x () > max_x) + max_x = pos.x (); + if (looking_for_max_x && pos.x () - min_x > threshold) { + if (looking_for_min_x) { + initial_x = min_x; //remember first min + first_was_max_x = TRUE; + } + total++; + looking_for_max_x = FALSE; + looking_for_min_x = TRUE; + max_x = pos.x (); + } + } + else if (next_step.y () < 0) { + if (looking_for_max_y && pos.y () < min_y) + min_y = pos.y (); + if (looking_for_min_y && max_y - pos.y () > threshold) { + if (looking_for_max_y) { + initial_y = max_y; //remember first max + first_was_max_y = FALSE; + } + total++; + looking_for_max_y = TRUE; + looking_for_min_y = FALSE; + min_y = pos.y (); //reset min + } + } + else { + if (looking_for_min_y && pos.y () > max_y) + max_y = pos.y (); + if (looking_for_max_y && pos.y () - min_y > threshold) { + if (looking_for_min_y) { + initial_y = min_y; //remember first min + first_was_max_y = TRUE; + } + total++; + looking_for_max_y = FALSE; + looking_for_min_y = TRUE; + max_y = pos.y (); + } + } + + } + if (first_was_max_x && looking_for_min_x) { + if (max_x - initial_x > threshold) + total++; + else + total--; + } + else if (!first_was_max_x && looking_for_max_x) { + if (initial_x - min_x > threshold) + total++; + else + total--; + } + if (first_was_max_y && looking_for_min_y) { + if (max_y - initial_y > threshold) + total++; + else + total--; + } + else if (!first_was_max_y && looking_for_max_y) { + if (initial_y - min_y > threshold) + total++; + else + total--; + } + + return total; +} + +/** + * @name C_OUTLINE::operator< + * + * @return TRUE if the left operand is inside the right one. + * @param other other outline + */ + +BOOL8 +C_OUTLINE::operator<(const C_OUTLINE& other) const { + inT16 count = 0; //winding count + ICOORD pos; //position of point + inT32 stepindex; //index to cstep + + if (!box.overlap (other.box)) + return FALSE; //can't be contained + if (stepcount == 0) + return other.box.contains(this->box); + + pos = start; + for (stepindex = 0; stepindex < stepcount + && (count = other.winding_number (pos)) == INTERSECTING; stepindex++) + pos += step (stepindex); //try all points + if (count == INTERSECTING) { + //all intersected + pos = other.start; + for (stepindex = 0; stepindex < other.stepcount + && (count = winding_number (pos)) == INTERSECTING; stepindex++) + //try other way round + pos += other.step (stepindex); + return count == INTERSECTING || count == 0; + } + return count != 0; +} + +/** + * @name C_OUTLINE::winding_number + * + * @return the winding number of the outline around the given point. + * @param point point to wind around + */ + +inT16 C_OUTLINE::winding_number(ICOORD point) const { + inT16 stepindex; //index to cstep + inT16 count; //winding count + ICOORD vec; //to current point + ICOORD stepvec; //step vector + inT32 cross; //cross product + + vec = start - point; //vector to it + count = 0; + for (stepindex = 0; stepindex < stepcount; stepindex++) { + stepvec = step (stepindex); //get the step + //crossing the line + if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) { + cross = vec * stepvec; //cross product + if (cross > 0) + count++; //crossing right half + else if (cross == 0) + return INTERSECTING; //going through point + } + else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) { + cross = vec * stepvec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + vec += stepvec; //sum vectors + } + return count; //winding number +} + +/** + * C_OUTLINE::turn_direction + * + * @return the sum direction delta of the outline. + */ + +inT16 C_OUTLINE::turn_direction() const { //winding number + DIR128 prevdir; //previous direction + DIR128 dir; //current direction + inT16 stepindex; //index to cstep + inT8 dirdiff; //direction difference + inT16 count; //winding count + + if (stepcount == 0) + return 128; + count = 0; + prevdir = step_dir (stepcount - 1); + for (stepindex = 0; stepindex < stepcount; stepindex++) { + dir = step_dir (stepindex); + dirdiff = dir - prevdir; + ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32); + count += dirdiff; + prevdir = dir; + } + ASSERT_HOST (count == 128 || count == -128); + return count; //winding number +} + +/** + * @name C_OUTLINE::reverse + * + * Reverse the direction of an outline. + */ + +void C_OUTLINE::reverse() { //reverse drection + DIR128 halfturn = MODULUS / 2; //amount to shift + DIR128 stepdir; //direction of step + inT16 stepindex; //index to cstep + inT16 farindex; //index to other side + inT16 halfsteps; //half of stepcount + + halfsteps = (stepcount + 1) / 2; + for (stepindex = 0; stepindex < halfsteps; stepindex++) { + farindex = stepcount - stepindex - 1; + stepdir = step_dir (stepindex); + set_step (stepindex, step_dir (farindex) + halfturn); + set_step (farindex, stepdir + halfturn); + } +} + +/** + * @name C_OUTLINE::move + * + * Move C_OUTLINE by vector + * @param vec vector to reposition OUTLINE by + */ + +void C_OUTLINE::move(const ICOORD vec) { + C_OUTLINE_IT it(&children); // iterator + + box.move (vec); + start += vec; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move child outlines +} + +/** + * Returns true if *this and its children are legally nested. + * The outer area of a child should have the opposite sign to the + * parent. If not, it means we have discarded an outline in between + * (probably due to excessive length). + */ +bool C_OUTLINE::IsLegallyNested() const { + if (stepcount == 0) return true; + int64_t parent_area = outer_area(); + // We aren't going to modify the list, or its contents, but there is + // no const iterator. + C_OUTLINE_IT child_it(const_cast(&children)); + for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { + const C_OUTLINE* child = child_it.data(); + if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested()) + return false; + } + return true; +} + +/** + * If this outline is smaller than the given min_size, delete this and + * remove from its list, via *it, after checking that *it points to this. + * Otherwise, if any children of this are too small, delete them. + * On entry, *it must be an iterator pointing to this. If this gets deleted + * then this is extracted from *it, so an iteration can continue. + * @param min_size minimum size for outline + * @param it outline iterator + */ +void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) { + if (box.width() < min_size || box.height() < min_size) { + ASSERT_HOST(this == it->data()); + delete it->extract(); // Too small so get rid of it and any children. + } else if (!children.empty()) { + // Search the children of this, deleting any that are too small. + C_OUTLINE_IT child_it(&children); + for (child_it.mark_cycle_pt(); !child_it.cycled_list(); + child_it.forward()) { + C_OUTLINE* child = child_it.data(); + child->RemoveSmallRecursive(min_size, &child_it); + } + } +} + +// Factored out helpers below are used only by ComputeEdgeOffsets to operate +// on data from an 8-bit Pix, and assume that any input x and/or y are already +// constrained to be legal Pix coordinates. + +/** + * Helper computes the local 2-D gradient (dx, dy) from the 2x2 cell centered + * on the given (x,y). If the cell would go outside the image, it is padded + * with white. + */ +static void ComputeGradient(const l_uint32* data, int wpl, + int x, int y, int width, int height, + ICOORD* gradient) { + const l_uint32* line = data + y * wpl; + int pix_x_y = + x < width && y < height + ? GET_DATA_BYTE( + const_cast(reinterpret_cast(line)), x) + : 255; + int pix_x_prevy = + x < width && y > 0 + ? GET_DATA_BYTE( + const_cast(reinterpret_cast(line - wpl)), x) + : 255; + int pix_prevx_prevy = + x > 0 && y > 0 + ? GET_DATA_BYTE( + const_cast(reinterpret_cast(line - wpl)), + x - 1) + : 255; + int pix_prevx_y = + x > 0 && y < height + ? GET_DATA_BYTE( + const_cast(reinterpret_cast(line)), x - 1) + : 255; + gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy)); + gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y)); +} + +/** + * Helper evaluates a vertical difference, (x,y) - (x,y-1), returning true if + * the difference, matches diff_sign and updating the best_diff, best_sum, + * best_y if a new max. + */ +static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign, + int x, int y, int height, + int* best_diff, int* best_sum, int* best_y) { + if (y <= 0 || y >= height) + return false; + const l_uint32* line = data + y * wpl; + int pixel1 = GET_DATA_BYTE( + const_cast(reinterpret_cast(line - wpl)), x); + int pixel2 = + GET_DATA_BYTE(const_cast(reinterpret_cast(line)), x); + int diff = (pixel2 - pixel1) * diff_sign; + if (diff > *best_diff) { + *best_diff = diff; + *best_sum = pixel1 + pixel2; + *best_y = y; + } + return diff > 0; +} + +/** + * Helper evaluates a horizontal difference, (x,y) - (x-1,y), where y is implied + * by the input image line, returning true if the difference matches diff_sign + * and updating the best_diff, best_sum, best_x if a new max. + */ +static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign, + int x, int width, + int* best_diff, int* best_sum, int* best_x) { + if (x <= 0 || x >= width) + return false; + int pixel1 = GET_DATA_BYTE( + const_cast(reinterpret_cast(line)), x - 1); + int pixel2 = + GET_DATA_BYTE(const_cast(reinterpret_cast(line)), x); + int diff = (pixel2 - pixel1) * diff_sign; + if (diff > *best_diff) { + *best_diff = diff; + *best_sum = pixel1 + pixel2; + *best_x = x; + } + return diff > 0; +} + +/** + * Adds sub-pixel resolution EdgeOffsets for the outline if the supplied + * pix is 8-bit. Does nothing otherwise. + * Operation: Consider the following near-horizontal line: + * @verbatim + * _________ + * |________ + * |________ + * @endverbatim + * At *every* position along this line, the gradient direction will be close + * to vertical. Extrapoaltion/interpolation of the position of the threshold + * that was used to binarize the image gives a more precise vertical position + * for each horizontal step, and the conflict in step direction and gradient + * direction can be used to ignore the vertical steps. + */ +void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) { + if (pixGetDepth(pix) != 8) return; + const l_uint32* data = pixGetData(pix); + int wpl = pixGetWpl(pix); + int width = pixGetWidth(pix); + int height = pixGetHeight(pix); + bool negative = flag(COUT_INVERSE); + delete [] offsets; + offsets = new EdgeOffset[stepcount]; + ICOORD pos = start; + ICOORD prev_gradient; + ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, + &prev_gradient); + for (int s = 0; s < stepcount; ++s) { + ICOORD step_vec = step(s); + TPOINT pt1(pos); + pos += step_vec; + TPOINT pt2(pos); + ICOORD next_gradient; + ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, + &next_gradient); + // Use the sum of the prev and next as the working gradient. + ICOORD gradient = prev_gradient + next_gradient; + // best_diff will be manipulated to be always positive. + int best_diff = 0; + // offset will be the extrapolation of the location of the greyscale + // threshold from the edge with the largest difference, relative to the + // location of the binary edge. + int offset = 0; + if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) { + // Horizontal step. diff_sign == 1 indicates black above. + int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1; + int x = MIN(pt1.x, pt2.x); + int y = height - pt1.y; + int best_sum = 0; + int best_y = y; + EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, + &best_diff, &best_sum, &best_y); + // Find the strongest edge. + int test_y = y; + do { + ++test_y; + } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, + &best_diff, &best_sum, &best_y)); + test_y = y; + do { + --test_y; + } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, + &best_diff, &best_sum, &best_y)); + offset = diff_sign * (best_sum / 2 - threshold) + + (y - best_y) * best_diff; + } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) { + // Vertical step. diff_sign == 1 indicates black on the left. + int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1; + int x = pt1.x; + int y = height - MAX(pt1.y, pt2.y); + const l_uint32* line = pixGetData(pix) + y * wpl; + int best_sum = 0; + int best_x = x; + EvaluateHorizontalDiff(line, diff_sign, x, width, + &best_diff, &best_sum, &best_x); + // Find the strongest edge. + int test_x = x; + do { + ++test_x; + } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, + &best_diff, &best_sum, &best_x)); + test_x = x; + do { + --test_x; + } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, + &best_diff, &best_sum, &best_x)); + offset = diff_sign * (threshold - best_sum / 2) + + (best_x - x) * best_diff; + } + offsets[s].offset_numerator = + static_cast(ClipToRange(offset, -MAX_INT8, MAX_INT8)); + offsets[s].pixel_diff = static_cast(ClipToRange(best_diff, 0 , + MAX_UINT8)); + if (negative) gradient = -gradient; + // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2) + // to convert from gradient direction to edge direction. + offsets[s].direction = + Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256); + prev_gradient = next_gradient; + } +} + +/** + * Adds sub-pixel resolution EdgeOffsets for the outline using only + * a binary image source. + * + * Runs a sliding window of 5 edge steps over the outline, maintaining a count + * of the number of steps in each of the 4 directions in the window, and a + * sum of the x or y position of each step (as appropriate to its direction.) + * Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the + * perpendicular direction. Eg + * @verbatim + * ___ ___ Chain code from the left: + * |___ ___ ___| 222122212223221232223000 + * |___| |_| Corresponding counts of each direction: + * 0 00000000000000000123 + * 1 11121111001111100000 + * 2 44434443443333343321 + * 3 00000001111111112111 + * Count of direction at center 41434143413313143313 + * Step gets used? YNYYYNYYYNYYNYNYYYyY (y= U-turn exception) + * Path redrawn showing only the used points: + * ___ ___ + * ___ ___ ___| + * ___ _ + * @endverbatim + * Sub-pixel edge position cannot be shown well with ASCII-art, but each + * horizontal step's y position is the mean of the y positions of the steps + * in the same direction in the sliding window, which makes a much smoother + * outline, without losing important detail. + */ +void C_OUTLINE::ComputeBinaryOffsets() { + delete [] offsets; + offsets = new EdgeOffset[stepcount]; + // Count of the number of steps in each direction in the sliding window. + int dir_counts[4]; + // Sum of the positions (y for a horizontal step, x for vertical) in each + // direction in the sliding window. + int pos_totals[4]; + memset(dir_counts, 0, sizeof(dir_counts)); + memset(pos_totals, 0, sizeof(pos_totals)); + ICOORD pos = start; + ICOORD tail_pos = pos; + // tail_pos is the trailing position, with the next point to be lost from + // the window. + tail_pos -= step(stepcount - 1); + tail_pos -= step(stepcount - 2); + // head_pos is the leading position, with the next point to be added to the + // window. + ICOORD head_pos = tail_pos; + // Set up the initial window with 4 points in [-2, 2) + for (int s = -2; s < 2; ++s) { + increment_step(s, 1, &head_pos, dir_counts, pos_totals); + } + for (int s = 0; s < stepcount; pos += step(s++)) { + // At step s, s in in the middle of [s-2, s+2]. + increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals); + int dir_index = chain_code(s); + ICOORD step_vec = step(s); + int best_diff = 0; + int offset = 0; + // Use only steps that have a count of >=2 OR the strong U-turn with a + // single d and 2 at d-1 and 2 at d+1 (mod 4). + if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 && + dir_counts[Modulo(dir_index - 1, 4)] == 2 && + dir_counts[Modulo(dir_index + 1, 4)] == 2)) { + // Valid step direction. + best_diff = dir_counts[dir_index]; + int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y(); + // The offset proposes that the actual step should be positioned at + // the mean position of the steps in the window of the same direction. + // See ASCII art above. + offset = pos_totals[dir_index] - best_diff * edge_pos; + } + offsets[s].offset_numerator = + static_cast(ClipToRange(offset, -MAX_INT8, MAX_INT8)); + offsets[s].pixel_diff = static_cast(ClipToRange(best_diff, 0 , + MAX_UINT8)); + // The direction is just the vector from start to end of the window. + FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y()); + offsets[s].direction = direction.to_direction(); + increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals); + } +} + +/** + * Renders the outline to the given pix, with left and top being + * the coords of the upper-left corner of the pix. + */ +void C_OUTLINE::render(int left, int top, Pix* pix) const { + ICOORD pos = start; + for (int stepindex = 0; stepindex < stepcount; ++stepindex) { + ICOORD next_step = step(stepindex); + if (next_step.y() < 0) { + pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, + PIX_NOT(PIX_DST), NULL, 0, 0); + } else if (next_step.y() > 0) { + pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, + PIX_NOT(PIX_DST), NULL, 0, 0); + } + pos += next_step; + } +} + +/** + * Renders just the outline to the given pix (no fill), with left and top + * being the coords of the upper-left corner of the pix. + * @param left coord + * @param top coord + * @param pix the pix to outline + */ +void C_OUTLINE::render_outline(int left, int top, Pix* pix) const { + ICOORD pos = start; + for (int stepindex = 0; stepindex < stepcount; ++stepindex) { + ICOORD next_step = step(stepindex); + if (next_step.y() < 0) { + pixSetPixel(pix, pos.x() - left, top - pos.y(), 1); + } else if (next_step.y() > 0) { + pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1); + } else if (next_step.x() < 0) { + pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1); + } else if (next_step.x() > 0) { + pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1); + } + pos += next_step; + } +} + +/** + * @name C_OUTLINE::plot + * + * Draw the outline in the given colour. + * @param window window to draw in + * @param colour colour to draw in + */ + +#ifndef GRAPHICS_DISABLED +void C_OUTLINE::plot(ScrollView* window, ScrollView::Color colour) const { + inT16 stepindex; // index to cstep + ICOORD pos; // current position + DIR128 stepdir; // direction of step + + pos = start; // current position + window->Pen(colour); + if (stepcount == 0) { + window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); + return; + } + window->SetCursor(pos.x(), pos.y()); + + stepindex = 0; + while (stepindex < stepcount) { + pos += step(stepindex); // step to next + stepdir = step_dir(stepindex); + stepindex++; // count steps + // merge straight lines + while (stepindex < stepcount && + stepdir.get_dir() == step_dir(stepindex).get_dir()) { + pos += step(stepindex); + stepindex++; + } + window->DrawTo(pos.x(), pos.y()); + } +} + +/** + * Draws the outline in the given colour, normalized using the given denorm, + * making use of sub-pixel accurate information if available. + */ +void C_OUTLINE::plot_normed(const DENORM& denorm, ScrollView::Color colour, + ScrollView* window) const { + window->Pen(colour); + if (stepcount == 0) { + window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); + return; + } + const DENORM* root_denorm = denorm.RootDenorm(); + ICOORD pos = start; // current position + FCOORD f_pos = sub_pixel_pos_at_index(pos, 0); + FCOORD pos_normed; + denorm.NormTransform(root_denorm, f_pos, &pos_normed); + window->SetCursor(IntCastRounded(pos_normed.x()), + IntCastRounded(pos_normed.y())); + for (int s = 0; s < stepcount; pos += step(s++)) { + int edge_weight = edge_strength_at_index(s); + if (edge_weight == 0) { + // This point has conflicting gradient and step direction, so ignore it. + continue; + } + FCOORD f_pos = sub_pixel_pos_at_index(pos, s); + FCOORD pos_normed; + denorm.NormTransform(root_denorm, f_pos, &pos_normed); + window->DrawTo(IntCastRounded(pos_normed.x()), + IntCastRounded(pos_normed.y())); + } +} +#endif + +/** + * @name C_OUTLINE::operator= + * + * Assignment - deep copy data + * @param source assign from this + */ + +C_OUTLINE& C_OUTLINE::operator=(const C_OUTLINE& source) { + box = source.box; + start = source.start; + if (steps != NULL) + free_mem(steps); + stepcount = source.stepcount; + steps = (uinT8 *) alloc_mem (step_mem()); + memmove (steps, source.steps, step_mem()); + if (!children.empty ()) + children.clear (); + children.deep_copy(&source.children, &deep_copy); + delete [] offsets; + if (source.offsets != NULL) { + offsets = new EdgeOffset[stepcount]; + memcpy(offsets, source.offsets, stepcount * sizeof(*offsets)); + } else { + offsets = NULL; + } + return *this; +} + +/** + * Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals + * by the step, increment, and vertical step ? x : y position * increment + * at step s Mod stepcount respectively. Used to add or subtract the + * direction and position to/from accumulators of a small neighbourhood. + */ +void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos, + int* dir_counts, int* pos_totals) const { + int step_index = Modulo(s, stepcount); + int dir_index = chain_code(step_index); + dir_counts[dir_index] += increment; + ICOORD step_vec = step(step_index); + if (step_vec.x() == 0) + pos_totals[dir_index] += pos->x() * increment; + else + pos_totals[dir_index] += pos->y() * increment; + *pos += step_vec; +} + +ICOORD C_OUTLINE::chain_step(int chaindir) { + return step_coords[chaindir % 4]; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.h new file mode 100644 index 0000000..fd0b673 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/coutln.h @@ -0,0 +1,291 @@ +/********************************************************************** + * File: coutln.c (Formerly: coutline.c) + * Description: Code for the C_OUTLINE class. + * Author: Ray Smith + * Created: Mon Oct 07 16:01:57 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef COUTLN_H +#define COUTLN_H + +#include "crakedge.h" +#include "mod128.h" +#include "bits16.h" +#include "rect.h" +#include "blckerr.h" +#include "scrollview.h" + +class DENORM; + +#define INTERSECTING MAX_INT16//no winding number + + //mask to get step +#define STEP_MASK 3 + +enum C_OUTLINE_FLAGS +{ + COUT_INVERSE //White on black blob +}; + +// Simple struct to hold the 3 values needed to compute a more precise edge +// position and direction. The offset_numerator is the difference between the +// grey threshold and the mean pixel value. pixel_diff is the difference between +// the pixels in the edge. Consider the following row of pixels: p1 p2 p3 p4 p5 +// Say the image was thresholded at threshold t, making p1, p2, p3 black +// and p4, p5 white (p1, p2, p3 < t, and p4, p5 >= t), but suppose that +// max(p[i+1] - p[i]) is p3 - p2. Then the extrapolated position of the edge, +// based on the maximum gradient, is at the crack between p2 and p3 plus the +// offset (t - (p2+p3)/2)/(p3 - p2). We store the pixel difference p3-p2 +// denominator in pixel_diff and the offset numerator, relative to the original +// binary edge (t - (p2+p3)/2) - (p3 -p2) in offset_numerator. +// The sign of offset_numerator and pixel_diff are manipulated to ensure +// that the pixel_diff, which will be used as a weight, is always positive. +// The direction stores the quantized feature direction for the given step +// computed from the edge gradient. (Using binary_angle_plus_pi.) +// If the pixel_diff is zero, it means that the direction of the gradient +// is in conflict with the step direction, so this step is to be ignored. +struct EdgeOffset { + inT8 offset_numerator; + uinT8 pixel_diff; + uinT8 direction; +}; + +class DLLSYM C_OUTLINE; //forward declaration +struct Pix; + +ELISTIZEH (C_OUTLINE) +class DLLSYM C_OUTLINE:public ELIST_LINK { + public: + C_OUTLINE() { //empty constructor + steps = NULL; + offsets = NULL; + } + C_OUTLINE( //constructor + CRACKEDGE *startpt, //from edge detector + ICOORD bot_left, //bounding box //length of loop + ICOORD top_right, + inT16 length); + C_OUTLINE(ICOORD startpt, //start of loop + DIR128 *new_steps, //steps in loop + inT16 length); //length of loop + //outline to copy + C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation); //and rotate + + // Build a fake outline, given just a bounding box and append to the list. + static void FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines); + + ~C_OUTLINE () { //destructor + if (steps != NULL) + free_mem(steps); + steps = NULL; + delete [] offsets; + } + + BOOL8 flag( //test flag + C_OUTLINE_FLAGS mask) const { //flag to test + return flags.bit (mask); + } + void set_flag( //set flag value + C_OUTLINE_FLAGS mask, //flag to test + BOOL8 value) { //value to set + flags.set_bit (mask, value); + } + + C_OUTLINE_LIST *child() { //get child list + return &children; + } + + //access function + const TBOX &bounding_box() const { + return box; + } + void set_step( //set a step + inT16 stepindex, //index of step + inT8 stepdir) { //chain code + int shift = stepindex%4 * 2; + uinT8 mask = 3 << shift; + steps[stepindex/4] = ((stepdir << shift) & mask) | + (steps[stepindex/4] & ~mask); + //squeeze 4 into byte + } + void set_step( //set a step + inT16 stepindex, //index of step + DIR128 stepdir) { //direction + //clean it + inT8 chaindir = stepdir.get_dir() >> (DIRBITS - 2); + //difference + set_step(stepindex, chaindir); + //squeeze 4 into byte + } + + inT32 pathlength() const { //get path length + return stepcount; + } + // Return step at a given index as a DIR128. + DIR128 step_dir(int index) const { + return DIR128((inT16)(((steps[index/4] >> (index%4 * 2)) & STEP_MASK) << + (DIRBITS - 2))); + } + // Return the step vector for the given outline position. + ICOORD step(int index) const { // index of step + return step_coords[chain_code(index)]; + } + // get start position + const ICOORD &start_pos() const { + return start; + } + // Returns the position at the given index on the outline. + // NOT to be used lightly, as it has to iterate the outline to find out. + ICOORD position_at_index(int index) const { + ICOORD pos = start; + for (int i = 0; i < index; ++i) + pos += step(i); + return pos; + } + // Returns the sub-pixel accurate position given the integer position pos + // at the given index on the outline. pos may be a return value of + // position_at_index, or computed by repeatedly adding step to the + // start_pos() in the usual way. + FCOORD sub_pixel_pos_at_index(const ICOORD& pos, int index) const { + const ICOORD& step_to_next(step(index)); + FCOORD f_pos(pos.x() + step_to_next.x() / 2.0f, + pos.y() + step_to_next.y() / 2.0f); + if (offsets != NULL && offsets[index].pixel_diff > 0) { + float offset = offsets[index].offset_numerator; + offset /= offsets[index].pixel_diff; + if (step_to_next.x() != 0) + f_pos.set_y(f_pos.y() + offset); + else + f_pos.set_x(f_pos.x() + offset); + } + return f_pos; + } + // Returns the step direction for the given index or -1 if there is none. + int direction_at_index(int index) const { + if (offsets != NULL && offsets[index].pixel_diff > 0) + return offsets[index].direction; + return -1; + } + // Returns the edge strength for the given index. + // If there are no recorded edge strengths, returns 1 (assuming the image + // is binary). Returns 0 if the gradient direction conflicts with the + // step direction, indicating that this position could be skipped. + int edge_strength_at_index(int index) const { + if (offsets != NULL) + return offsets[index].pixel_diff; + return 1; + } + // Return the step as a chain code (0-3) related to the standard feature + // direction of binary_angle_plus_pi by: + // chain_code * 64 = feature direction. + int chain_code(int index) const { // index of step + return (steps[index / 4] >> (index % 4 * 2)) & STEP_MASK; + } + + inT32 area() const; // Returns area of self and 1st level children. + inT32 perimeter() const; // Total perimeter of self and 1st level children. + inT32 outer_area() const; // Returns area of self only. + inT32 count_transitions( //count maxima + inT32 threshold); //size threshold + + BOOL8 operator< ( //containment test + const C_OUTLINE & other) const; + BOOL8 operator> ( //containment test + C_OUTLINE & other) const + { + return other < *this; //use the < to do it + } + inT16 winding_number( //get winding number + ICOORD testpt) const; //around this point + //get direction + inT16 turn_direction() const; + void reverse(); //reverse direction + + void move( // reposition outline + const ICOORD vec); // by vector + + // Returns true if *this and its children are legally nested. + // The outer area of a child should have the opposite sign to the + // parent. If not, it means we have discarded an outline in between + // (probably due to excessive length). + bool IsLegallyNested() const; + + // If this outline is smaller than the given min_size, delete this and + // remove from its list, via *it, after checking that *it points to this. + // Otherwise, if any children of this are too small, delete them. + // On entry, *it must be an iterator pointing to this. If this gets deleted + // then this is extracted from *it, so an iteration can continue. + void RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it); + + // Adds sub-pixel resolution EdgeOffsets for the outline if the supplied + // pix is 8-bit. Does nothing otherwise. + void ComputeEdgeOffsets(int threshold, Pix* pix); + // Adds sub-pixel resolution EdgeOffsets for the outline using only + // a binary image source. + void ComputeBinaryOffsets(); + + // Renders the outline to the given pix, with left and top being + // the coords of the upper-left corner of the pix. + void render(int left, int top, Pix* pix) const; + + // Renders just the outline to the given pix (no fill), with left and top + // being the coords of the upper-left corner of the pix. + void render_outline(int left, int top, Pix* pix) const; + + #ifndef GRAPHICS_DISABLED + void plot( //draw one + ScrollView* window, //window to draw in + ScrollView::Color colour) const; //colour to draw it + // Draws the outline in the given colour, normalized using the given denorm, + // making use of sub-pixel accurate information if available. + void plot_normed(const DENORM& denorm, ScrollView::Color colour, + ScrollView* window) const; + #endif // GRAPHICS_DISABLED + + C_OUTLINE& operator=(const C_OUTLINE& source); + + static C_OUTLINE* deep_copy(const C_OUTLINE* src) { + C_OUTLINE* outline = new C_OUTLINE; + *outline = *src; + return outline; + } + + static ICOORD chain_step(int chaindir); + + // The maximum length of any outline. The stepcount is stored as 16 bits, + // but it is probably not a good idea to increase this constant by much + // and switch to 32 bits, as it plays an important role in keeping huge + // outlines invisible, which prevents bad speed behavior. + static const int kMaxOutlineLength = 16000; + + private: + // Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals + // by the step, increment, and vertical step ? x : y position * increment + // at step s Mod stepcount respectively. Used to add or subtract the + // direction and position to/from accumulators of a small neighbourhood. + void increment_step(int s, int increment, ICOORD* pos, int* dir_counts, + int* pos_totals) const; + int step_mem() const { return (stepcount+3) / 4; } + + TBOX box; // bounding box + ICOORD start; // start coord + inT16 stepcount; // no of steps + BITS16 flags; // flags about outline + uinT8 *steps; // step array + EdgeOffset* offsets; // Higher precision edge. + C_OUTLINE_LIST children; // child elements + static ICOORD step_coords[4]; +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/crakedge.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/crakedge.h new file mode 100644 index 0000000..37729b4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/crakedge.h @@ -0,0 +1,37 @@ +/********************************************************************** + * File: crakedge.h (Formerly: crkedge.h) + * Description: Sturctures for the Crack following edge detector. + * Author: Ray Smith + * Created: Fri Mar 22 16:06:38 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CRAKEDGE_H +#define CRAKEDGE_H + +#include "points.h" +#include "mod128.h" + +class CRACKEDGE { + public: + CRACKEDGE() {} + + ICOORD pos; /*position of crack */ + inT8 stepx; //edge step + inT8 stepy; + inT8 stepdir; //chaincode + CRACKEDGE *prev; /*previous point */ + CRACKEDGE *next; /*next point */ +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.cpp new file mode 100644 index 0000000..2540925 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.cpp @@ -0,0 +1,295 @@ +/////////////////////////////////////////////////////////////////////// +// File: detlinefit.cpp +// Description: Deterministic least median squares line fitting. +// Author: Ray Smith +// Created: Thu Feb 28 14:45: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 "detlinefit.h" +#include "statistc.h" +#include "ndminx.h" +#include "tprintf.h" + +namespace tesseract { + +// The number of points to consider at each end. +const int kNumEndPoints = 3; +// The minimum number of points at which to switch to number of points +// for badly fitted lines. +// To ensure a sensible error metric, kMinPointsForErrorCount should be at +// least kMaxRealDistance / (1 - %ile) where %ile is the fractile used in +// ComputeUpperQuartileError. +const int kMinPointsForErrorCount = 16; +// The maximum real distance to use before switching to number of +// mis-fitted points, which will get square-rooted for true distance. +const int kMaxRealDistance = 2.0; + +DetLineFit::DetLineFit() : square_length_(0.0) { +} + +DetLineFit::~DetLineFit() { +} + +// Delete all Added points. +void DetLineFit::Clear() { + pts_.clear(); + distances_.clear(); +} + +// Add a new point. Takes a copy - the pt doesn't need to stay in scope. +void DetLineFit::Add(const ICOORD& pt) { + pts_.push_back(PointWidth(pt, 0)); +} +// Associates a half-width with the given point if a point overlaps the +// previous point by more than half the width, and its distance is further +// than the previous point, then the more distant point is ignored in the +// distance calculation. Useful for ignoring i dots and other diacritics. +void DetLineFit::Add(const ICOORD& pt, int halfwidth) { + pts_.push_back(PointWidth(pt, halfwidth)); +} + +// Fits a line to the points, ignoring the skip_first initial points and the +// skip_last final points, returning the fitted line as a pair of points, +// and the upper quartile error. +double DetLineFit::Fit(int skip_first, int skip_last, + ICOORD* pt1, ICOORD* pt2) { + // Do something sensible with no points. + if (pts_.empty()) { + pt1->set_x(0); + pt1->set_y(0); + *pt2 = *pt1; + return 0.0; + } + // Count the points and find the first and last kNumEndPoints. + int pt_count = pts_.size(); + ICOORD* starts[kNumEndPoints]; + if (skip_first >= pt_count) skip_first = pt_count - 1; + int start_count = 0; + int end_i = MIN(skip_first + kNumEndPoints, pt_count); + for (int i = skip_first; i < end_i; ++i) { + starts[start_count++] = &pts_[i].pt; + } + ICOORD* ends[kNumEndPoints]; + if (skip_last >= pt_count) skip_last = pt_count - 1; + int end_count = 0; + end_i = MAX(0, pt_count - kNumEndPoints - skip_last); + for (int i = pt_count - 1 - skip_last; i >= end_i; --i) { + ends[end_count++] = &pts_[i].pt; + } + // 1 or 2 points need special treatment. + if (pt_count <= 2) { + *pt1 = *starts[0]; + if (pt_count > 1) + *pt2 = *ends[0]; + else + *pt2 = *pt1; + return 0.0; + } + // Although with between 2 and 2*kNumEndPoints-1 points, there will be + // overlap in the starts, ends sets, this is OK and taken care of by the + // if (*start != *end) test below, which also tests for equal input points. + double best_uq = -1.0; + // Iterate each pair of points and find the best fitting line. + for (int i = 0; i < start_count; ++i) { + ICOORD* start = starts[i]; + for (int j = 0; j < end_count; ++j) { + ICOORD* end = ends[j]; + if (*start != *end) { + ComputeDistances(*start, *end); + // Compute the upper quartile error from the line. + double dist = EvaluateLineFit(); + if (dist < best_uq || best_uq < 0.0) { + best_uq = dist; + *pt1 = *start; + *pt2 = *end; + } + } + } + } + // Finally compute the square root to return the true distance. + return best_uq > 0.0 ? sqrt(best_uq) : best_uq; +} + +// Constrained fit with a supplied direction vector. Finds the best line_pt, +// that is one of the supplied points having the median cross product with +// direction, ignoring points that have a cross product outside of the range +// [min_dist, max_dist]. Returns the resulting error metric using the same +// reduced set of points. +// *Makes use of floating point arithmetic* +double DetLineFit::ConstrainedFit(const FCOORD& direction, + double min_dist, double max_dist, + bool debug, ICOORD* line_pt) { + ComputeConstrainedDistances(direction, min_dist, max_dist); + // Do something sensible with no points or computed distances. + if (pts_.empty() || distances_.empty()) { + line_pt->set_x(0); + line_pt->set_y(0); + return 0.0; + } + int median_index = distances_.choose_nth_item(distances_.size() / 2); + *line_pt = distances_[median_index].data; + if (debug) { + tprintf("Constrained fit to dir %g, %g = %d, %d :%d distances:\n", + direction.x(), direction.y(), + line_pt->x(), line_pt->y(), distances_.size()); + for (int i = 0; i < distances_.size(); ++i) { + tprintf("%d: %d, %d -> %g\n", i, distances_[i].data.x(), + distances_[i].data.y(), distances_[i].key); + } + tprintf("Result = %d\n", median_index); + } + // Center distances on the fitted point. + double dist_origin = direction * *line_pt; + for (int i = 0; i < distances_.size(); ++i) { + distances_[i].key -= dist_origin; + } + return sqrt(EvaluateLineFit()); +} + +// Returns true if there were enough points at the last call to Fit or +// ConstrainedFit for the fitted points to be used on a badly fitted line. +bool DetLineFit::SufficientPointsForIndependentFit() const { + return distances_.size() >= kMinPointsForErrorCount; +} + +// Backwards compatible fit returning a gradient and constant. +// Deprecated. Prefer Fit(ICOORD*, ICOORD*) where possible, but use this +// function in preference to the LMS class. +double DetLineFit::Fit(float* m, float* c) { + ICOORD start, end; + double error = Fit(&start, &end); + if (end.x() != start.x()) { + *m = static_cast(end.y() - start.y()) / (end.x() - start.x()); + *c = start.y() - *m * start.x(); + } else { + *m = 0.0f; + *c = 0.0f; + } + return error; +} + +// Backwards compatible constrained fit with a supplied gradient. +// Deprecated. Use ConstrainedFit(const FCOORD& direction) where possible +// to avoid potential difficulties with infinite gradients. +double DetLineFit::ConstrainedFit(double m, float* c) { + // Do something sensible with no points. + if (pts_.empty()) { + *c = 0.0f; + return 0.0; + } + double cos = 1.0 / sqrt(1.0 + m * m); + FCOORD direction(cos, m * cos); + ICOORD line_pt; + double error = ConstrainedFit(direction, -MAX_FLOAT32, MAX_FLOAT32, false, + &line_pt); + *c = line_pt.y() - line_pt.x() * m; + return error; +} + +// Computes and returns the squared evaluation metric for a line fit. +double DetLineFit::EvaluateLineFit() { + // Compute the upper quartile error from the line. + double dist = ComputeUpperQuartileError(); + if (distances_.size() >= kMinPointsForErrorCount && + dist > kMaxRealDistance * kMaxRealDistance) { + // Use the number of mis-fitted points as the error metric, as this + // gives a better measure of fit for badly fitted lines where more + // than a quarter are badly fitted. + double threshold = kMaxRealDistance * sqrt(square_length_); + dist = NumberOfMisfittedPoints(threshold); + } + return dist; +} + +// Computes the absolute error distances of the points from the line, +// and returns the squared upper-quartile error distance. +double DetLineFit::ComputeUpperQuartileError() { + int num_errors = distances_.size(); + if (num_errors == 0) return 0.0; + // Get the absolute values of the errors. + for (int i = 0; i < num_errors; ++i) { + if (distances_[i].key < 0) distances_[i].key = -distances_[i].key; + } + // Now get the upper quartile distance. + int index = distances_.choose_nth_item(3 * num_errors / 4); + double dist = distances_[index].key; + // The true distance is the square root of the dist squared / square_length. + // Don't bother with the square root. Just return the square distance. + return square_length_ > 0.0 ? dist * dist / square_length_ : 0.0; +} + +// Returns the number of sample points that have an error more than threshold. +int DetLineFit::NumberOfMisfittedPoints(double threshold) const { + int num_misfits = 0; + int num_dists = distances_.size(); + // Get the absolute values of the errors. + for (int i = 0; i < num_dists; ++i) { + if (distances_[i].key > threshold) + ++num_misfits; + } + return num_misfits; +} + +// Computes all the cross product distances of the points from the line, +// storing the actual (signed) cross products in distances. +// Ignores distances of points that are further away than the previous point, +// and overlaps the previous point by at least half. +void DetLineFit::ComputeDistances(const ICOORD& start, const ICOORD& end) { + distances_.truncate(0); + ICOORD line_vector = end; + line_vector -= start; + square_length_ = line_vector.sqlength(); + int line_length = IntCastRounded(sqrt(square_length_)); + // Compute the distance of each point from the line. + int prev_abs_dist = 0; + int prev_dot = 0; + for (int i = 0; i < pts_.size(); ++i) { + ICOORD pt_vector = pts_[i].pt; + pt_vector -= start; + int dot = line_vector % pt_vector; + // Compute |line_vector||pt_vector|sin(angle between) + int dist = line_vector * pt_vector; + int abs_dist = dist < 0 ? -dist : dist; + if (abs_dist > prev_abs_dist && i > 0) { + // Ignore this point if it overlaps the previous one. + int separation = abs(dot - prev_dot); + if (separation < line_length * pts_[i].halfwidth || + separation < line_length * pts_[i - 1].halfwidth) + continue; + } + distances_.push_back(DistPointPair(dist, pts_[i].pt)); + prev_abs_dist = abs_dist; + prev_dot = dot; + } +} + +// Computes all the cross product distances of the points perpendicular to +// the given direction, ignoring distances outside of the give distance range, +// storing the actual (signed) cross products in distances_. +void DetLineFit::ComputeConstrainedDistances(const FCOORD& direction, + double min_dist, double max_dist) { + distances_.truncate(0); + square_length_ = direction.sqlength(); + // Compute the distance of each point from the line. + for (int i = 0; i < pts_.size(); ++i) { + FCOORD pt_vector = pts_[i].pt; + // Compute |line_vector||pt_vector|sin(angle between) + double dist = direction * pt_vector; + if (min_dist <= dist && dist <= max_dist) + distances_.push_back(DistPointPair(dist, pts_[i].pt)); + } +} + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.h new file mode 100644 index 0000000..12afbb7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/detlinefit.h @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////// +// File: detlinefit.h +// Description: Deterministic least upper-quartile squares line fitting. +// Author: Ray Smith +// Created: Thu Feb 28 14:35: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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCSTRUCT_DETLINEFIT_H_ +#define TESSERACT_CCSTRUCT_DETLINEFIT_H_ + +#include "genericvector.h" +#include "kdpair.h" +#include "points.h" + +namespace tesseract { + +// This class fits a line to a set of ICOORD points. +// There is no restriction on the direction of the line, as it +// uses a vector method, ie no concern over infinite gradients. +// The fitted line has the least upper quartile of squares of perpendicular +// distances of all source points from the line, subject to the constraint +// that the line is made from one of the pairs of [{p1,p2,p3},{pn-2, pn-1, pn}] +// i.e. the 9 combinations of one of the first 3 and last 3 points. +// A fundamental assumption of this algorithm is that one of the first 3 and +// one of the last 3 points are near the best line fit. +// The points must be Added in line order for the algorithm to work properly. +// No floating point calculations are needed* to make an accurate fit, +// and no random numbers are needed** so the algorithm is deterministic, +// architecture-stable, and compiler-stable as well as stable to minor +// changes in the input. +// *A single floating point division is used to compute each line's distance. +// This is unlikely to result in choice of a different line, but if it does, +// it would be easy to replace with a 64 bit integer calculation. +// **Random numbers are used in the nth_item function, but the worst +// non-determinism that can result is picking a different result among equals, +// and that wouldn't make any difference to the end-result distance, so the +// randomness does not affect the determinism of the algorithm. The random +// numbers are only there to guarantee average linear time. +// Fitting time is linear, but with a high constant, as it tries 9 different +// lines and computes the distance of all points each time. +// This class is aimed at replacing the LLSQ (linear least squares) and +// LMS (least median of squares) classes that are currently used for most +// of the line fitting in Tesseract. +class DetLineFit { + public: + DetLineFit(); + ~DetLineFit(); + + // Delete all Added points. + void Clear(); + + // Adds a new point. Takes a copy - the pt doesn't need to stay in scope. + // Add must be called on points in sequence along the line. + void Add(const ICOORD& pt); + // Associates a half-width with the given point if a point overlaps the + // previous point by more than half the width, and its distance is further + // than the previous point, then the more distant point is ignored in the + // distance calculation. Useful for ignoring i dots and other diacritics. + void Add(const ICOORD& pt, int halfwidth); + + // Fits a line to the points, returning the fitted line as a pair of + // points, and the upper quartile error. + double Fit(ICOORD* pt1, ICOORD* pt2) { + return Fit(0, 0, pt1, pt2); + } + // Fits a line to the points, ignoring the skip_first initial points and the + // skip_last final points, returning the fitted line as a pair of points, + // and the upper quartile error. + double Fit(int skip_first, int skip_last, ICOORD* pt1, ICOORD* pt2); + + // Constrained fit with a supplied direction vector. Finds the best line_pt, + // that is one of the supplied points having the median cross product with + // direction, ignoring points that have a cross product outside of the range + // [min_dist, max_dist]. Returns the resulting error metric using the same + // reduced set of points. + // *Makes use of floating point arithmetic* + double ConstrainedFit(const FCOORD& direction, + double min_dist, double max_dist, + bool debug, ICOORD* line_pt); + + // Returns true if there were enough points at the last call to Fit or + // ConstrainedFit for the fitted points to be used on a badly fitted line. + bool SufficientPointsForIndependentFit() const; + + // Backwards compatible fit returning a gradient and constant. + // Deprecated. Prefer Fit(ICOORD*, ICOORD*) where possible, but use this + // function in preference to the LMS class. + double Fit(float* m, float* c); + + // Backwards compatible constrained fit with a supplied gradient. + // Deprecated. Use ConstrainedFit(const FCOORD& direction) where possible + // to avoid potential difficulties with infinite gradients. + double ConstrainedFit(double m, float* c); + + private: + // Simple struct to hold an ICOORD point and a halfwidth representing half + // the "width" (supposedly approximately parallel to the direction of the + // line) of each point, such that distant points can be discarded when they + // overlap nearer points. (Think i dot and other diacritics or noise.) + struct PointWidth { + PointWidth() : pt(ICOORD(0, 0)), halfwidth(0) {} + PointWidth(const ICOORD& pt0, int halfwidth0) + : pt(pt0), halfwidth(halfwidth0) {} + + ICOORD pt; + int halfwidth; + }; + // Type holds the distance of each point from the fitted line and the point + // itself. Use of double allows integer distances from ICOORDs to be stored + // exactly, and also the floating point results from ConstrainedFit. + typedef KDPairInc DistPointPair; + + // Computes and returns the squared evaluation metric for a line fit. + double EvaluateLineFit(); + + // Computes the absolute values of the precomputed distances_, + // and returns the squared upper-quartile error distance. + double ComputeUpperQuartileError(); + + // Returns the number of sample points that have an error more than threshold. + int NumberOfMisfittedPoints(double threshold) const; + + // Computes all the cross product distances of the points from the line, + // storing the actual (signed) cross products in distances_. + // Ignores distances of points that are further away than the previous point, + // and overlaps the previous point by at least half. + void ComputeDistances(const ICOORD& start, const ICOORD& end); + + // Computes all the cross product distances of the points perpendicular to + // the given direction, ignoring distances outside of the give distance range, + // storing the actual (signed) cross products in distances_. + void ComputeConstrainedDistances(const FCOORD& direction, + double min_dist, double max_dist); + + // Stores all the source points in the order they were given and their + // halfwidths, if any. + GenericVector pts_; + // Stores the computed perpendicular distances of (some of) the pts_ from a + // given vector (assuming it goes through the origin, making it a line). + // Since the distances may be a subset of the input points, and get + // re-ordered by the nth_item function, the original point is stored + // along side the distance. + GenericVector distances_; // Distances of points. + // The squared length of the vector used to compute distances_. + double square_length_; +}; + +} // namespace tesseract. + +#endif // TESSERACT_CCSTRUCT_DETLINEFIT_H_ + + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.cpp new file mode 100644 index 0000000..7325c9c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.cpp @@ -0,0 +1,98 @@ +/********************************************************************** + * File: dppoint.cpp + * Description: Simple generic dynamic programming class. + * Author: Ray Smith + * Created: Wed Mar 25 19:08:01 PDT 2009 + * + * (C) Copyright 2009, 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 "dppoint.h" +#include "tprintf.h" + +namespace tesseract { + +// Solve the dynamic programming problem for the given array of points, with +// the given size and cost function. +// Steps backwards are limited to being between min_step and max_step +// inclusive. +// The return value is the tail of the best path. +DPPoint* DPPoint::Solve(int min_step, int max_step, bool debug, + CostFunc cost_func, int size, DPPoint* points) { + if (size <= 0 || max_step < min_step || min_step >= size) + return NULL; // Degenerate, but not necessarily an error. + ASSERT_HOST(min_step > 0); // Infinite loop possible if this is not true. + if (debug) + tprintf("min = %d, max=%d\n", + min_step, max_step); + // Evaluate the total cost at each point. + for (int i = 0; i < size; ++i) { + for (int offset = min_step; offset <= max_step; ++offset) { + DPPoint* prev = offset <= i ? points + i - offset : NULL; + inT64 new_cost = (points[i].*cost_func)(prev); + if (points[i].best_prev_ != NULL && offset > min_step * 2 && + new_cost > points[i].total_cost_) + break; // Find only the first minimum if going over twice the min. + } + points[i].total_cost_ += points[i].local_cost_; + if (debug) { + tprintf("At point %d, local cost=%d, total_cost=%d, steps=%d\n", + i, points[i].local_cost_, points[i].total_cost_, + points[i].total_steps_); + } + } + // Now find the end of the best path and return it. + int best_cost = points[size - 1].total_cost_; + int best_end = size - 1; + for (int end = best_end - 1; end >= size - min_step; --end) { + int cost = points[end].total_cost_; + if (cost < best_cost) { + best_cost = cost; + best_end = end; + } + } + return points + best_end; +} + +// A CostFunc that takes the variance of step into account in the cost. +inT64 DPPoint::CostWithVariance(const DPPoint* prev) { + if (prev == NULL || prev == this) { + UpdateIfBetter(0, 1, NULL, 0, 0, 0); + return 0; + } + + int delta = this - prev; + inT32 n = prev->n_ + 1; + inT32 sig_x = prev->sig_x_ + delta; + inT64 sig_xsq = prev->sig_xsq_ + delta * delta; + inT64 cost = (sig_xsq - sig_x * sig_x / n) / n; + cost += prev->total_cost_; + UpdateIfBetter(cost, prev->total_steps_ + 1, prev, n, sig_x, sig_xsq); + return cost; +} + +// Update the other members if the cost is lower. +void DPPoint::UpdateIfBetter(inT64 cost, inT32 steps, const DPPoint* prev, + inT32 n, inT32 sig_x, inT64 sig_xsq) { + if (cost < total_cost_) { + total_cost_ = cost; + total_steps_ = steps; + best_prev_ = prev; + n_ = n; + sig_x_ = sig_x; + sig_xsq_ = sig_xsq; + } +} + +} // namespace tesseract. + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.h new file mode 100644 index 0000000..fd87bb9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/dppoint.h @@ -0,0 +1,102 @@ +/********************************************************************** + * File: dppoint.h + * Description: Simple generic dynamic programming class. + * Author: Ray Smith + * Created: Wed Mar 25 18:57:01 PDT 2009 + * + * (C) Copyright 2009, 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_CCSTRUCT_DPPOINT_H__ +#define TESSERACT_CCSTRUCT_DPPOINT_H__ + +#include "host.h" + +namespace tesseract { + +// A simple class to provide a dynamic programming solution to a class of +// 1st-order problems in which the cost is dependent only on the current +// step and the best cost to that step, with a possible special case +// of using the variance of the steps, and only the top choice is required. +// Useful for problems such as finding the optimal cut points in a fixed-pitch +// (vertical or horizontal) situation. +// Skeletal Example: +// DPPoint* array = new DPPoint[width]; +// for (int i = 0; i < width; i++) { +// array[i].AddLocalCost(cost_at_i) +// } +// DPPoint* best_end = DPPoint::Solve(..., array); +// while (best_end != NULL) { +// int cut_index = best_end - array; +// best_end = best_end->best_prev(); +// } +// delete [] array; +class DPPoint { + public: + // The cost function evaluates the total cost at this (excluding this's + // local_cost) and if it beats this's total_cost, then + // replace the appropriate values in this. + typedef inT64 (DPPoint::*CostFunc)(const DPPoint* prev); + + DPPoint() + : local_cost_(0), total_cost_(MAX_INT32), total_steps_(1), best_prev_(NULL), + n_(0), sig_x_(0), sig_xsq_(0) { + } + + // Solve the dynamic programming problem for the given array of points, with + // the given size and cost function. + // Steps backwards are limited to being between min_step and max_step + // inclusive. + // The return value is the tail of the best path. + static DPPoint* Solve(int min_step, int max_step, bool debug, + CostFunc cost_func, int size, DPPoint* points); + + // A CostFunc that takes the variance of step into account in the cost. + inT64 CostWithVariance(const DPPoint* prev); + + // Accessors. + int total_cost() const { + return total_cost_; + } + int Pathlength() const { + return total_steps_; + } + const DPPoint* best_prev() const { + return best_prev_; + } + void AddLocalCost(int new_cost) { + local_cost_ += new_cost; + } + + private: + // Code common to different cost functions. + + // Update the other members if the cost is lower. + void UpdateIfBetter(inT64 cost, inT32 steps, const DPPoint* prev, + inT32 n, inT32 sig_x, inT64 sig_xsq); + + inT32 local_cost_; // Cost of this point on its own. + inT32 total_cost_; // Sum of all costs in best path to here. + // During cost calculations local_cost is excluded. + inT32 total_steps_; // Number of steps in best path to here. + const DPPoint* best_prev_; // Pointer to prev point in best path from here. + // Information for computing the variance part of the cost. + inT32 n_; // Number of steps in best path to here for variance. + inT32 sig_x_; // Sum of step sizes for computing variance. + inT64 sig_xsq_; // Sum of squares of steps for computing variance. +}; + +} // namespace tesseract. + +#endif // TESSERACT_CCSTRUCT_DPPOINT_H__ + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.cpp new file mode 100644 index 0000000..536ac28 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.cpp @@ -0,0 +1,262 @@ +/////////////////////////////////////////////////////////////////////// +// File: fontinfo.cpp +// Description: Font information classes abstracted from intproto.h/cpp. +// Author: rays@google.com (Ray Smith) +// Created: Wed May 18 10:39:01 PDT 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. +// +/////////////////////////////////////////////////////////////////////// + +#include "fontinfo.h" +#include "bitvector.h" +#include "unicity_table.h" + +namespace tesseract { + +// Writes to the given file. Returns false in case of error. +bool FontInfo::Serialize(FILE* fp) const { + if (!write_info(fp, *this)) return false; + if (!write_spacing_info(fp, *this)) 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 FontInfo::DeSerialize(bool swap, FILE* fp) { + if (!read_info(fp, this, swap)) return false; + if (!read_spacing_info(fp, this, swap)) return false; + return true; +} + +FontInfoTable::FontInfoTable() { + set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); + set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); +} + +FontInfoTable::~FontInfoTable() { +} + +// Writes to the given file. Returns false in case of error. +bool FontInfoTable::Serialize(FILE* fp) const { + return this->SerializeClasses(fp); +} +// Reads from the given file. Returns false in case of error. +// If swap is true, assumes a big/little-endian swap is needed. +bool FontInfoTable::DeSerialize(bool swap, FILE* fp) { + truncate(0); + return this->DeSerializeClasses(swap, fp); +} + +// Returns true if the given set of fonts includes one with the same +// properties as font_id. +bool FontInfoTable::SetContainsFontProperties( + int font_id, const GenericVector& font_set) const { + uinT32 properties = get(font_id).properties; + for (int f = 0; f < font_set.size(); ++f) { + if (get(font_set[f].fontinfo_id).properties == properties) + return true; + } + return false; +} + +// Returns true if the given set of fonts includes multiple properties. +bool FontInfoTable::SetContainsMultipleFontProperties( + const GenericVector& font_set) const { + if (font_set.empty()) return false; + int first_font = font_set[0].fontinfo_id; + uinT32 properties = get(first_font).properties; + for (int f = 1; f < font_set.size(); ++f) { + if (get(font_set[f].fontinfo_id).properties != properties) + return true; + } + return false; +} + +// Moves any non-empty FontSpacingInfo entries from other to this. +void FontInfoTable::MoveSpacingInfoFrom(FontInfoTable* other) { + set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); + set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); + for (int i = 0; i < other->size(); ++i) { + GenericVector* spacing_vec = other->get(i).spacing_vec; + if (spacing_vec != NULL) { + int target_index = get_index(other->get(i)); + if (target_index < 0) { + // Bit copy the FontInfo and steal all the pointers. + push_back(other->get(i)); + other->get(i).name = NULL; + } else { + delete [] get(target_index).spacing_vec; + get(target_index).spacing_vec = other->get(i).spacing_vec; + } + other->get(i).spacing_vec = NULL; + } + } +} + +// Moves this to the target unicity table. +void FontInfoTable::MoveTo(UnicityTable* target) { + target->clear(); + target->set_compare_callback(NewPermanentTessCallback(CompareFontInfo)); + target->set_clear_callback(NewPermanentTessCallback(FontInfoDeleteCallback)); + for (int i = 0; i < size(); ++i) { + // Bit copy the FontInfo and steal all the pointers. + target->push_back(get(i)); + get(i).name = NULL; + get(i).spacing_vec = NULL; + } +} + + +// Compare FontInfo structures. +bool CompareFontInfo(const FontInfo& fi1, const FontInfo& fi2) { + // The font properties are required to be the same for two font with the same + // name, so there is no need to test them. + // Consequently, querying the table with only its font name as information is + // enough to retrieve its properties. + return strcmp(fi1.name, fi2.name) == 0; +} +// Compare FontSet structures. +bool CompareFontSet(const FontSet& fs1, const FontSet& fs2) { + if (fs1.size != fs2.size) + return false; + for (int i = 0; i < fs1.size; ++i) { + if (fs1.configs[i] != fs2.configs[i]) + return false; + } + return true; +} + +// Callbacks for GenericVector. +void FontInfoDeleteCallback(FontInfo f) { + if (f.spacing_vec != NULL) { + f.spacing_vec->delete_data_pointers(); + delete f.spacing_vec; + } + delete[] f.name; +} +void FontSetDeleteCallback(FontSet fs) { + delete[] fs.configs; +} + +/*---------------------------------------------------------------------------*/ +// Callbacks used by UnicityTable to read/write FontInfo/FontSet structures. +bool read_info(FILE* f, FontInfo* fi, bool swap) { + inT32 size; + if (fread(&size, sizeof(size), 1, f) != 1) return false; + if (swap) + Reverse32(&size); + char* font_name = new char[size + 1]; + fi->name = font_name; + if (static_cast(fread(font_name, sizeof(*font_name), size, f)) != size) + return false; + font_name[size] = '\0'; + if (fread(&fi->properties, sizeof(fi->properties), 1, f) != 1) return false; + if (swap) + Reverse32(&fi->properties); + return true; +} + +bool write_info(FILE* f, const FontInfo& fi) { + inT32 size = strlen(fi.name); + if (fwrite(&size, sizeof(size), 1, f) != 1) return false; + if (static_cast(fwrite(fi.name, sizeof(*fi.name), size, f)) != size) + return false; + if (fwrite(&fi.properties, sizeof(fi.properties), 1, f) != 1) return false; + return true; +} + +bool read_spacing_info(FILE *f, FontInfo* fi, bool swap) { + inT32 vec_size, kern_size; + if (fread(&vec_size, sizeof(vec_size), 1, f) != 1) return false; + if (swap) Reverse32(&vec_size); + ASSERT_HOST(vec_size >= 0); + if (vec_size == 0) return true; + fi->init_spacing(vec_size); + for (int i = 0; i < vec_size; ++i) { + FontSpacingInfo *fs = new FontSpacingInfo(); + if (fread(&fs->x_gap_before, sizeof(fs->x_gap_before), 1, f) != 1 || + fread(&fs->x_gap_after, sizeof(fs->x_gap_after), 1, f) != 1 || + fread(&kern_size, sizeof(kern_size), 1, f) != 1) { + delete fs; + return false; + } + if (swap) { + ReverseN(&(fs->x_gap_before), sizeof(fs->x_gap_before)); + ReverseN(&(fs->x_gap_after), sizeof(fs->x_gap_after)); + Reverse32(&kern_size); + } + if (kern_size < 0) { // indication of a NULL entry in fi->spacing_vec + delete fs; + continue; + } + if (kern_size > 0 && (!fs->kerned_unichar_ids.DeSerialize(swap, f) || + !fs->kerned_x_gaps.DeSerialize(swap, f))) { + delete fs; + return false; + } + fi->add_spacing(i, fs); + } + return true; +} + +bool write_spacing_info(FILE* f, const FontInfo& fi) { + inT32 vec_size = (fi.spacing_vec == NULL) ? 0 : fi.spacing_vec->size(); + if (fwrite(&vec_size, sizeof(vec_size), 1, f) != 1) return false; + inT16 x_gap_invalid = -1; + for (int i = 0; i < vec_size; ++i) { + FontSpacingInfo *fs = fi.spacing_vec->get(i); + inT32 kern_size = (fs == NULL) ? -1 : fs->kerned_x_gaps.size(); + if (fs == NULL) { + // Valid to have the identical fwrites. Writing invalid x-gaps. + if (fwrite(&(x_gap_invalid), sizeof(x_gap_invalid), 1, f) != 1 || + fwrite(&(x_gap_invalid), sizeof(x_gap_invalid), 1, f) != 1 || + fwrite(&kern_size, sizeof(kern_size), 1, f) != 1) { + return false; + } + } else { + if (fwrite(&(fs->x_gap_before), sizeof(fs->x_gap_before), 1, f) != 1 || + fwrite(&(fs->x_gap_after), sizeof(fs->x_gap_after), 1, f) != 1 || + fwrite(&kern_size, sizeof(kern_size), 1, f) != 1) { + return false; + } + } + if (kern_size > 0 && (!fs->kerned_unichar_ids.Serialize(f) || + !fs->kerned_x_gaps.Serialize(f))) { + return false; + } + } + return true; +} + +bool read_set(FILE* f, FontSet* fs, bool swap) { + if (fread(&fs->size, sizeof(fs->size), 1, f) != 1) return false; + if (swap) + Reverse32(&fs->size); + fs->configs = new int32_t[fs->size]; + for (int i = 0; i < fs->size; ++i) { + if (fread(&fs->configs[i], sizeof(fs->configs[i]), 1, f) != 1) return false; + if (swap) + Reverse32(&fs->configs[i]); + } + return true; +} + +bool write_set(FILE* f, const FontSet& fs) { + if (fwrite(&fs.size, sizeof(fs.size), 1, f) != 1) return false; + for (int i = 0; i < fs.size; ++i) { + if (fwrite(&fs.configs[i], sizeof(fs.configs[i]), 1, f) != 1) return false; + } + return true; +} + +} // namespace tesseract. + diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.h new file mode 100644 index 0000000..597a179 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/fontinfo.h @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////// +// File: fontinfo.h +// Description: Font information classes abstracted from intproto.h/cpp. +// Author: rays@google.com (Ray Smith) +// Created: Tue May 17 17:08:01 PDT 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_CCSTRUCT_FONTINFO_H_ +#define TESSERACT_CCSTRUCT_FONTINFO_H_ + +#include "genericvector.h" +#include "host.h" +#include "unichar.h" + +#include + +template class UnicityTable; + +namespace tesseract { + +class BitVector; + +// Simple struct to hold a font and a score. The scores come from the low-level +// integer matcher, so they are in the uinT16 range. Fonts are an index to +// fontinfo_table. +// These get copied around a lot, so best to keep them small. +struct ScoredFont { + ScoredFont() : fontinfo_id(-1), score(0) {} + ScoredFont(int font_id, uinT16 classifier_score) + : fontinfo_id(font_id), score(classifier_score) {} + + // Index into fontinfo table, but inside the classifier, may be a shapetable + // index. + inT32 fontinfo_id; + // Raw score from the low-level classifier. + uinT16 score; +}; + +// Struct for information about spacing between characters in a particular font. +struct FontSpacingInfo { + inT16 x_gap_before; + inT16 x_gap_after; + GenericVector kerned_unichar_ids; + GenericVector kerned_x_gaps; +}; + +/* + * font_properties contains properties about boldness, italicness, fixed pitch, + * serif, fraktur + */ +struct FontInfo { + FontInfo() : name(NULL), properties(0), universal_id(0), spacing_vec(NULL) {} + ~FontInfo() {} + + // 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); + + // Reserves unicharset_size spots in spacing_vec. + void init_spacing(int unicharset_size) { + spacing_vec = new GenericVector(); + spacing_vec->init_to_size(unicharset_size, NULL); + } + // Adds the given pointer to FontSpacingInfo to spacing_vec member + // (FontInfo class takes ownership of the pointer). + // Note: init_spacing should be called before calling this function. + void add_spacing(UNICHAR_ID uch_id, FontSpacingInfo *spacing_info) { + ASSERT_HOST(spacing_vec != NULL && spacing_vec->size() > uch_id); + (*spacing_vec)[uch_id] = spacing_info; + } + + // Returns the pointer to FontSpacingInfo for the given UNICHAR_ID. + const FontSpacingInfo *get_spacing(UNICHAR_ID uch_id) const { + return (spacing_vec == NULL || spacing_vec->size() <= uch_id) ? + NULL : (*spacing_vec)[uch_id]; + } + + // Fills spacing with the value of the x gap expected between the two given + // UNICHAR_IDs. Returns true on success. + bool get_spacing(UNICHAR_ID prev_uch_id, + UNICHAR_ID uch_id, + int *spacing) const { + const FontSpacingInfo *prev_fsi = this->get_spacing(prev_uch_id); + const FontSpacingInfo *fsi = this->get_spacing(uch_id); + if (prev_fsi == NULL || fsi == NULL) return false; + int i = 0; + for (; i < prev_fsi->kerned_unichar_ids.size(); ++i) { + if (prev_fsi->kerned_unichar_ids[i] == uch_id) break; + } + if (i < prev_fsi->kerned_unichar_ids.size()) { + *spacing = prev_fsi->kerned_x_gaps[i]; + } else { + *spacing = prev_fsi->x_gap_after + fsi->x_gap_before; + } + return true; + } + + bool is_italic() const { return properties & 1; } + bool is_bold() const { return (properties & 2) != 0; } + bool is_fixed_pitch() const { return (properties & 4) != 0; } + bool is_serif() const { return (properties & 8) != 0; } + bool is_fraktur() const { return (properties & 16) != 0; } + + char* name; + uinT32 properties; + // The universal_id is a field reserved for the initialization process + // to assign a unique id number to all fonts loaded for the current + // combination of languages. This id will then be returned by + // ResultIterator::WordFontAttributes. + inT32 universal_id; + // Horizontal spacing between characters (indexed by UNICHAR_ID). + GenericVector *spacing_vec; +}; + +// Every class (character) owns a FontSet that represents all the fonts that can +// render this character. +// Since almost all the characters from the same script share the same set of +// fonts, the sets are shared over multiple classes (see +// Classify::fontset_table_). Thus, a class only store an id to a set. +// Because some fonts cannot render just one character of a set, there are a +// lot of FontSet that differ only by one font. Rather than storing directly +// the FontInfo in the FontSet structure, it's better to share FontInfos among +// FontSets (Classify::fontinfo_table_). +struct FontSet { + int32_t size; + int32_t* configs; // FontInfo ids +}; + +// Class that adds a bit of functionality on top of GenericVector to +// implement a table of FontInfo that replaces UniCityTable. +// TODO(rays) change all references once all existing traineddata files +// are replaced. +class FontInfoTable : public GenericVector { + public: + FontInfoTable(); + ~FontInfoTable(); + + // 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); + + // Returns true if the given set of fonts includes one with the same + // properties as font_id. + bool SetContainsFontProperties( + int font_id, const GenericVector& font_set) const; + // Returns true if the given set of fonts includes multiple properties. + bool SetContainsMultipleFontProperties( + const GenericVector& font_set) const; + + // Moves any non-empty FontSpacingInfo entries from other to this. + void MoveSpacingInfoFrom(FontInfoTable* other); + // Moves this to the target unicity table. + void MoveTo(UnicityTable* target); +}; + +// Compare FontInfo structures. +bool CompareFontInfo(const FontInfo& fi1, const FontInfo& fi2); +// Compare FontSet structures. +bool CompareFontSet(const FontSet& fs1, const FontSet& fs2); +// Deletion callbacks for GenericVector. +void FontInfoDeleteCallback(FontInfo f); +void FontSetDeleteCallback(FontSet fs); + +// Callbacks used by UnicityTable to read/write FontInfo/FontSet structures. +bool read_info(FILE* f, FontInfo* fi, bool swap); +bool write_info(FILE* f, const FontInfo& fi); +bool read_spacing_info(FILE *f, FontInfo* fi, bool swap); +bool write_spacing_info(FILE* f, const FontInfo& fi); +bool read_set(FILE* f, FontSet* fs, bool swap); +bool write_set(FILE* f, const FontSet& fs); + +} // namespace tesseract. + +#endif /* THIRD_PARTY_TESSERACT_CCSTRUCT_FONTINFO_H_ */ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.cpp new file mode 100644 index 0000000..03ae60b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.cpp @@ -0,0 +1,38 @@ +/********************************************************************** + * File: genblob.cpp (Formerly gblob.c) + * Description: Generic Blob processing routines + * Author: Phil Cheatle + * Created: Mon Nov 25 10:53:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "genblob.h" +#include "stepblob.h" + +/********************************************************************** + * c_blob_comparator() + * + * Blob comparator used to sort a blob list so that blobs are in increasing + * order of left edge. + **********************************************************************/ + +int c_blob_comparator( // sort blobs + const void *blob1p, // ptr to ptr to blob1 + const void *blob2p // ptr to ptr to blob2 + ) { + C_BLOB *blob1 = *(C_BLOB **) blob1p; + C_BLOB *blob2 = *(C_BLOB **) blob2p; + + return blob1->bounding_box ().left () - blob2->bounding_box ().left (); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.h new file mode 100644 index 0000000..2fb55a1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/genblob.h @@ -0,0 +1,27 @@ +/********************************************************************** + * File: genblob.h (Formerly gblob.h) + * Description: Generic Blob processing routines + * Author: Phil Cheatle + * Created: Mon Nov 25 10:53:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GENBLOB_H +#define GENBLOB_H + +// Sort function to sort blobs by ascending left edge. +int c_blob_comparator(const void *blob1p, // ptr to ptr to blob1 + const void *blob2p); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/hpdsizes.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/hpdsizes.h new file mode 100644 index 0000000..f4d886a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/hpdsizes.h @@ -0,0 +1,17 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 HPDSIZES_H +#define HPDSIZES_H + +#define NUM_TEXT_ATTR 10 +#define NUM_BLOCK_ATTR 7 +#define MAXLENGTH 128 +#define NUM_BACKGROUNDS 8 +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.cpp new file mode 100644 index 0000000..7abac13 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.cpp @@ -0,0 +1,699 @@ +/////////////////////////////////////////////////////////////////////// +// File: imagedata.h +// Description: Class to hold information about a single multi-page tiff +// training file and its corresponding boxes or text file. +// Author: Ray Smith +// Created: Tue May 28 08:56:06 PST 2013 +// +// (C) Copyright 2013, 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 automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "imagedata.h" + +#include "allheaders.h" +#include "boxread.h" +#include "callcpp.h" +#include "helpers.h" +#include "tprintf.h" + +#if defined(__MINGW32__) +# include +#elif __cplusplus > 199711L // in C++11 +# include +#endif + +// Number of documents to read ahead while training. Doesn't need to be very +// large. +const int kMaxReadAhead = 8; + +namespace tesseract { + +WordFeature::WordFeature() : x_(0), y_(0), dir_(0) { +} + +WordFeature::WordFeature(const FCOORD& fcoord, uinT8 dir) + : x_(IntCastRounded(fcoord.x())), + y_(ClipToRange(IntCastRounded(fcoord.y()), 0, MAX_UINT8)), + dir_(dir) { +} + +// Computes the maximum x and y value in the features. +void WordFeature::ComputeSize(const GenericVector& features, + int* max_x, int* max_y) { + *max_x = 0; + *max_y = 0; + for (int f = 0; f < features.size(); ++f) { + if (features[f].x_ > *max_x) *max_x = features[f].x_; + if (features[f].y_ > *max_y) *max_y = features[f].y_; + } +} + +// Draws the features in the given window. +void WordFeature::Draw(const GenericVector& features, + ScrollView* window) { +#ifndef GRAPHICS_DISABLED + for (int f = 0; f < features.size(); ++f) { + FCOORD pos(features[f].x_, features[f].y_); + FCOORD dir; + dir.from_direction(features[f].dir_); + dir *= 8.0f; + window->SetCursor(IntCastRounded(pos.x() - dir.x()), + IntCastRounded(pos.y() - dir.y())); + window->DrawTo(IntCastRounded(pos.x() + dir.x()), + IntCastRounded(pos.y() + dir.y())); + } +#endif +} + +// Writes to the given file. Returns false in case of error. +bool WordFeature::Serialize(FILE* fp) const { + if (fwrite(&x_, sizeof(x_), 1, fp) != 1) return false; + if (fwrite(&y_, sizeof(y_), 1, fp) != 1) return false; + if (fwrite(&dir_, sizeof(dir_), 1, fp) != 1) 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 WordFeature::DeSerialize(bool swap, FILE* fp) { + if (fread(&x_, sizeof(x_), 1, fp) != 1) return false; + if (swap) ReverseN(&x_, sizeof(x_)); + if (fread(&y_, sizeof(y_), 1, fp) != 1) return false; + if (fread(&dir_, sizeof(dir_), 1, fp) != 1) return false; + return true; +} + +void FloatWordFeature::FromWordFeatures( + const GenericVector& word_features, + GenericVector* float_features) { + for (int i = 0; i < word_features.size(); ++i) { + FloatWordFeature f; + f.x = word_features[i].x(); + f.y = word_features[i].y(); + f.dir = word_features[i].dir(); + f.x_bucket = 0; // Will set it later. + float_features->push_back(f); + } +} + +// Sort function to sort first by x-bucket, then by y. +/* static */ +int FloatWordFeature::SortByXBucket(const void* v1, const void* v2) { + const FloatWordFeature* f1 = reinterpret_cast(v1); + const FloatWordFeature* f2 = reinterpret_cast(v2); + int x_diff = f1->x_bucket - f2->x_bucket; + if (x_diff == 0) return f1->y - f2->y; + return x_diff; +} + +ImageData::ImageData() : page_number_(-1), vertical_text_(false) { +} +// Takes ownership of the pix and destroys it. +ImageData::ImageData(bool vertical, Pix* pix) + : page_number_(0), vertical_text_(vertical) { + SetPix(pix); +} +ImageData::~ImageData() { +} + +// Builds and returns an ImageData from the basic data. Note that imagedata, +// truth_text, and box_text are all the actual file data, NOT filenames. +ImageData* ImageData::Build(const char* name, int page_number, const char* lang, + const char* imagedata, int imagedatasize, + const char* truth_text, const char* box_text) { + ImageData* image_data = new ImageData(); + image_data->imagefilename_ = name; + image_data->page_number_ = page_number; + image_data->language_ = lang; + // Save the imagedata. + image_data->image_data_.resize_no_init(imagedatasize); + memcpy(&image_data->image_data_[0], imagedata, imagedatasize); + if (!image_data->AddBoxes(box_text)) { + if (truth_text == NULL || truth_text[0] == '\0') { + tprintf("Error: No text corresponding to page %d from image %s!\n", + page_number, name); + delete image_data; + return NULL; + } + image_data->transcription_ = truth_text; + // If we have no boxes, the transcription is in the 0th box_texts_. + image_data->box_texts_.push_back(truth_text); + // We will create a box for the whole image on PreScale, to save unpacking + // the image now. + } else if (truth_text != NULL && truth_text[0] != '\0' && + image_data->transcription_ != truth_text) { + // Save the truth text as it is present and disagrees with the box text. + image_data->transcription_ = truth_text; + } + return image_data; +} + +// Writes to the given file. Returns false in case of error. +bool ImageData::Serialize(TFile* fp) const { + if (!imagefilename_.Serialize(fp)) return false; + if (fp->FWrite(&page_number_, sizeof(page_number_), 1) != 1) return false; + if (!image_data_.Serialize(fp)) return false; + if (!transcription_.Serialize(fp)) return false; + // WARNING: Will not work across different endian machines. + if (!boxes_.Serialize(fp)) return false; + if (!box_texts_.SerializeClasses(fp)) return false; + inT8 vertical = vertical_text_; + if (fp->FWrite(&vertical, sizeof(vertical), 1) != 1) 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 ImageData::DeSerialize(bool swap, TFile* fp) { + if (!imagefilename_.DeSerialize(swap, fp)) return false; + if (fp->FRead(&page_number_, sizeof(page_number_), 1) != 1) return false; + if (swap) ReverseN(&page_number_, sizeof(page_number_)); + if (!image_data_.DeSerialize(swap, fp)) return false; + if (!transcription_.DeSerialize(swap, fp)) return false; + // WARNING: Will not work across different endian machines. + if (!boxes_.DeSerialize(swap, fp)) return false; + if (!box_texts_.DeSerializeClasses(swap, fp)) return false; + inT8 vertical = 0; + if (fp->FRead(&vertical, sizeof(vertical), 1) != 1) return false; + vertical_text_ = vertical != 0; + return true; +} + +// As DeSerialize, but only seeks past the data - hence a static method. +bool ImageData::SkipDeSerialize(bool swap, TFile* fp) { + if (!STRING::SkipDeSerialize(swap, fp)) return false; + inT32 page_number; + if (fp->FRead(&page_number, sizeof(page_number), 1) != 1) return false; + if (!GenericVector::SkipDeSerialize(swap, fp)) return false; + if (!STRING::SkipDeSerialize(swap, fp)) return false; + if (!GenericVector::SkipDeSerialize(swap, fp)) return false; + if (!GenericVector::SkipDeSerializeClasses(swap, fp)) return false; + inT8 vertical = 0; + return fp->FRead(&vertical, sizeof(vertical), 1) == 1; +} + +// Saves the given Pix as a PNG-encoded string and destroys it. +void ImageData::SetPix(Pix* pix) { + SetPixInternal(pix, &image_data_); +} + +// Returns the Pix image for *this. Must be pixDestroyed after use. +Pix* ImageData::GetPix() const { + return GetPixInternal(image_data_); +} + +// Gets anything and everything with a non-NULL pointer, prescaled to a +// given target_height (if 0, then the original image height), and aligned. +// Also returns (if not NULL) the width and height of the scaled image. +// The return value is the scaled Pix, which must be pixDestroyed after use, +// and scale_factor (if not NULL) is set to the scale factor that was applied +// to the image to achieve the target_height. +Pix* ImageData::PreScale(int target_height, int max_height, float* scale_factor, + int* scaled_width, int* scaled_height, + GenericVector* boxes) const { + int input_width = 0; + int input_height = 0; + Pix* src_pix = GetPix(); + ASSERT_HOST(src_pix != NULL); + input_width = pixGetWidth(src_pix); + input_height = pixGetHeight(src_pix); + if (target_height == 0) { + target_height = MIN(input_height, max_height); + } + float im_factor = static_cast(target_height) / input_height; + if (scaled_width != NULL) + *scaled_width = IntCastRounded(im_factor * input_width); + if (scaled_height != NULL) + *scaled_height = target_height; + // Get the scaled image. + Pix* pix = pixScale(src_pix, im_factor, im_factor); + if (pix == NULL) { + tprintf("Scaling pix of size %d, %d by factor %g made null pix!!\n", + input_width, input_height, im_factor); + } + if (scaled_width != NULL) *scaled_width = pixGetWidth(pix); + if (scaled_height != NULL) *scaled_height = pixGetHeight(pix); + pixDestroy(&src_pix); + if (boxes != NULL) { + // Get the boxes. + boxes->truncate(0); + for (int b = 0; b < boxes_.size(); ++b) { + TBOX box = boxes_[b]; + box.scale(im_factor); + boxes->push_back(box); + } + if (boxes->empty()) { + // Make a single box for the whole image. + TBOX box(0, 0, im_factor * input_width, target_height); + boxes->push_back(box); + } + } + if (scale_factor != NULL) *scale_factor = im_factor; + return pix; +} + +int ImageData::MemoryUsed() const { + return image_data_.size(); +} + +// Draws the data in a new window. +void ImageData::Display() const { +#ifndef GRAPHICS_DISABLED + const int kTextSize = 64; + // Draw the image. + Pix* pix = GetPix(); + if (pix == NULL) return; + int width = pixGetWidth(pix); + int height = pixGetHeight(pix); + ScrollView* win = new ScrollView("Imagedata", 100, 100, + 2 * (width + 2 * kTextSize), + 2 * (height + 4 * kTextSize), + width + 10, height + 3 * kTextSize, true); + win->Image(pix, 0, height - 1); + pixDestroy(&pix); + // Draw the boxes. + win->Pen(ScrollView::RED); + win->Brush(ScrollView::NONE); + int text_size = kTextSize; + if (!boxes_.empty() && boxes_[0].height() * 2 < text_size) + text_size = boxes_[0].height() * 2; + win->TextAttributes("Arial", text_size, false, false, false); + if (!boxes_.empty()) { + for (int b = 0; b < boxes_.size(); ++b) { + boxes_[b].plot(win); + win->Text(boxes_[b].left(), height + kTextSize, box_texts_[b].string()); + } + } else { + // The full transcription. + win->Pen(ScrollView::CYAN); + win->Text(0, height + kTextSize * 2, transcription_.string()); + } + win->Update(); + window_wait(win); +#endif +} + +// Adds the supplied boxes and transcriptions that correspond to the correct +// page number. +void ImageData::AddBoxes(const GenericVector& boxes, + const GenericVector& texts, + const GenericVector& box_pages) { + // Copy the boxes and make the transcription. + for (int i = 0; i < box_pages.size(); ++i) { + if (page_number_ >= 0 && box_pages[i] != page_number_) continue; + transcription_ += texts[i]; + boxes_.push_back(boxes[i]); + box_texts_.push_back(texts[i]); + } +} + +// Saves the given Pix as a PNG-encoded string and destroys it. +void ImageData::SetPixInternal(Pix* pix, GenericVector* image_data) { + l_uint8* data; + size_t size; + pixWriteMem(&data, &size, pix, IFF_PNG); + pixDestroy(&pix); + image_data->resize_no_init(size); + memcpy(&(*image_data)[0], data, size); + free(data); +} + +// Returns the Pix image for the image_data. Must be pixDestroyed after use. +Pix* ImageData::GetPixInternal(const GenericVector& image_data) { + Pix* pix = NULL; + if (!image_data.empty()) { + // Convert the array to an image. + const unsigned char* u_data = + reinterpret_cast(&image_data[0]); + pix = pixReadMem(u_data, image_data.size()); + } + return pix; +} + +// Parses the text string as a box file and adds any discovered boxes that +// match the page number. Returns false on error. +bool ImageData::AddBoxes(const char* box_text) { + if (box_text != NULL && box_text[0] != '\0') { + GenericVector boxes; + GenericVector texts; + GenericVector box_pages; + if (ReadMemBoxes(page_number_, false, box_text, &boxes, + &texts, NULL, &box_pages)) { + AddBoxes(boxes, texts, box_pages); + return true; + } else { + tprintf("Error: No boxes for page %d from image %s!\n", + page_number_, imagefilename_.string()); + } + } + return false; +} + +// Thread function to call ReCachePages. +void* ReCachePagesFunc(void* data) { + DocumentData* document_data = reinterpret_cast(data); + document_data->ReCachePages(); + return NULL; +} + +DocumentData::DocumentData(const STRING& name) + : document_name_(name), + pages_offset_(-1), + total_pages_(-1), + memory_used_(0), + max_memory_(0), + reader_(NULL) {} + +DocumentData::~DocumentData() { + SVAutoLock lock_p(&pages_mutex_); + SVAutoLock lock_g(&general_mutex_); +} + +// Reads all the pages in the given lstmf filename to the cache. The reader +// is used to read the file. +bool DocumentData::LoadDocument(const char* filename, const char* lang, + int start_page, inT64 max_memory, + FileReader reader) { + SetDocument(filename, lang, max_memory, reader); + pages_offset_ = start_page; + return ReCachePages(); +} + +// Sets up the document, without actually loading it. +void DocumentData::SetDocument(const char* filename, const char* lang, + inT64 max_memory, FileReader reader) { + SVAutoLock lock_p(&pages_mutex_); + SVAutoLock lock(&general_mutex_); + document_name_ = filename; + lang_ = lang; + pages_offset_ = -1; + max_memory_ = max_memory; + reader_ = reader; +} + +// Writes all the pages to the given filename. Returns false on error. +bool DocumentData::SaveDocument(const char* filename, FileWriter writer) { + SVAutoLock lock(&pages_mutex_); + TFile fp; + fp.OpenWrite(NULL); + if (!pages_.Serialize(&fp) || !fp.CloseWrite(filename, writer)) { + tprintf("Serialize failed: %s\n", filename); + return false; + } + return true; +} +bool DocumentData::SaveToBuffer(GenericVector* buffer) { + SVAutoLock lock(&pages_mutex_); + TFile fp; + fp.OpenWrite(buffer); + return pages_.Serialize(&fp); +} + +// Adds the given page data to this document, counting up memory. +void DocumentData::AddPageToDocument(ImageData* page) { + SVAutoLock lock(&pages_mutex_); + pages_.push_back(page); + set_memory_used(memory_used() + page->MemoryUsed()); +} + +// If the given index is not currently loaded, loads it using a separate +// thread. +void DocumentData::LoadPageInBackground(int index) { + ImageData* page = NULL; + if (IsPageAvailable(index, &page)) return; + SVAutoLock lock(&pages_mutex_); + if (pages_offset_ == index) return; + pages_offset_ = index; + pages_.clear(); + #ifndef GRAPHICS_DISABLED + SVSync::StartThread(ReCachePagesFunc, this); + #endif // GRAPHICS_DISABLED +} + +// Returns a pointer to the page with the given index, modulo the total +// number of pages. Blocks until the background load is completed. +const ImageData* DocumentData::GetPage(int index) { + ImageData* page = NULL; + while (!IsPageAvailable(index, &page)) { + // If there is no background load scheduled, schedule one now. + pages_mutex_.Lock(); + bool needs_loading = pages_offset_ != index; + pages_mutex_.Unlock(); + if (needs_loading) LoadPageInBackground(index); + // We can't directly load the page, or the background load will delete it + // while the caller is using it, so give it a chance to work. +#if __cplusplus > 199711L && !defined(__MINGW32__) + std::this_thread::sleep_for(std::chrono::seconds(1)); +#elif _WIN32 // MSVS + Sleep(1000); +#else + sleep(1); +#endif + } + return page; +} + +// Returns true if the requested page is available, and provides a pointer, +// which may be NULL if the document is empty. May block, even though it +// doesn't guarantee to return true. +bool DocumentData::IsPageAvailable(int index, ImageData** page) { + SVAutoLock lock(&pages_mutex_); + int num_pages = NumPages(); + if (num_pages == 0 || index < 0) { + *page = NULL; // Empty Document. + return true; + } + if (num_pages > 0) { + index = Modulo(index, num_pages); + if (pages_offset_ <= index && index < pages_offset_ + pages_.size()) { + *page = pages_[index - pages_offset_]; // Page is available already. + return true; + } + } + return false; +} + +// Removes all pages from memory and frees the memory, but does not forget +// the document metadata. +inT64 DocumentData::UnCache() { + SVAutoLock lock(&pages_mutex_); + inT64 memory_saved = memory_used(); + pages_.clear(); + pages_offset_ = -1; + set_total_pages(-1); + set_memory_used(0); + tprintf("Unloaded document %s, saving %d memory\n", document_name_.string(), + memory_saved); + return memory_saved; +} + +// Locks the pages_mutex_ and Loads as many pages can fit in max_memory_ +// starting at index pages_offset_. +bool DocumentData::ReCachePages() { + SVAutoLock lock(&pages_mutex_); + // Read the file. + set_total_pages(0); + set_memory_used(0); + int loaded_pages = 0; + pages_.truncate(0); + TFile fp; + if (!fp.Open(document_name_, reader_) || + !PointerVector::DeSerializeSize(false, &fp, &loaded_pages) || + loaded_pages <= 0) { + tprintf("Deserialize header failed: %s\n", document_name_.string()); + return false; + } + pages_offset_ %= loaded_pages; + // Skip pages before the first one we want, and load the rest until max + // memory and skip the rest after that. + int page; + for (page = 0; page < loaded_pages; ++page) { + if (page < pages_offset_ || + (max_memory_ > 0 && memory_used() > max_memory_)) { + if (!PointerVector::DeSerializeSkip(false, &fp)) break; + } else { + if (!pages_.DeSerializeElement(false, &fp)) break; + ImageData* image_data = pages_.back(); + if (image_data->imagefilename().length() == 0) { + image_data->set_imagefilename(document_name_); + image_data->set_page_number(page); + } + image_data->set_language(lang_); + set_memory_used(memory_used() + image_data->MemoryUsed()); + } + } + if (page < loaded_pages) { + tprintf("Deserialize failed: %s read %d/%d pages\n", + document_name_.string(), page, loaded_pages); + pages_.truncate(0); + } else { + tprintf("Loaded %d/%d pages (%d-%d) of document %s\n", pages_.size(), + loaded_pages, pages_offset_, pages_offset_ + pages_.size(), + document_name_.string()); + } + set_total_pages(loaded_pages); + return !pages_.empty(); +} + +// A collection of DocumentData that knows roughly how much memory it is using. +DocumentCache::DocumentCache(inT64 max_memory) + : num_pages_per_doc_(0), max_memory_(max_memory) {} +DocumentCache::~DocumentCache() {} + +// Adds all the documents in the list of filenames, counting memory. +// The reader is used to read the files. +bool DocumentCache::LoadDocuments(const GenericVector& filenames, + const char* lang, + CachingStrategy cache_strategy, + FileReader reader) { + cache_strategy_ = cache_strategy; + inT64 fair_share_memory = 0; + // In the round-robin case, each DocumentData handles restricting its content + // to its fair share of memory. In the sequential case, DocumentCache + // determines which DocumentDatas are held entirely in memory. + if (cache_strategy_ == CS_ROUND_ROBIN) + fair_share_memory = max_memory_ / filenames.size(); + for (int arg = 0; arg < filenames.size(); ++arg) { + STRING filename = filenames[arg]; + DocumentData* document = new DocumentData(filename); + document->SetDocument(filename.string(), lang, fair_share_memory, reader); + AddToCache(document); + } + if (!documents_.empty()) { + // Try to get the first page now to verify the list of filenames. + if (GetPageBySerial(0) != NULL) return true; + tprintf("Load of page 0 failed!\n"); + } + return false; +} + +// Adds document to the cache. +bool DocumentCache::AddToCache(DocumentData* data) { + inT64 new_memory = data->memory_used(); + documents_.push_back(data); + return true; +} + +// Finds and returns a document by name. +DocumentData* DocumentCache::FindDocument(const STRING& document_name) const { + for (int i = 0; i < documents_.size(); ++i) { + if (documents_[i]->document_name() == document_name) + return documents_[i]; + } + return NULL; +} + +// Returns the total number of pages in an epoch. For CS_ROUND_ROBIN cache +// strategy, could take a long time. +int DocumentCache::TotalPages() { + if (cache_strategy_ == CS_SEQUENTIAL) { + // In sequential mode, we assume each doc has the same number of pages + // whether it is true or not. + if (num_pages_per_doc_ == 0) GetPageSequential(0); + return num_pages_per_doc_ * documents_.size(); + } + int total_pages = 0; + int num_docs = documents_.size(); + for (int d = 0; d < num_docs; ++d) { + // We have to load a page to make NumPages() valid. + documents_[d]->GetPage(0); + total_pages += documents_[d]->NumPages(); + } + return total_pages; +} + +// Returns a page by serial number, selecting them in a round-robin fashion +// from all the documents. Highly disk-intensive, but doesn't need samples +// to be shuffled between files to begin with. +const ImageData* DocumentCache::GetPageRoundRobin(int serial) { + int num_docs = documents_.size(); + int doc_index = serial % num_docs; + const ImageData* doc = documents_[doc_index]->GetPage(serial / num_docs); + for (int offset = 1; offset <= kMaxReadAhead && offset < num_docs; ++offset) { + doc_index = (serial + offset) % num_docs; + int page = (serial + offset) / num_docs; + documents_[doc_index]->LoadPageInBackground(page); + } + return doc; +} + +// Returns a page by serial number, selecting them in sequence from each file. +// Requires the samples to be shuffled between the files to give a random or +// uniform distribution of data. Less disk-intensive than GetPageRoundRobin. +const ImageData* DocumentCache::GetPageSequential(int serial) { + int num_docs = documents_.size(); + ASSERT_HOST(num_docs > 0); + if (num_pages_per_doc_ == 0) { + // Use the pages in the first doc as the number of pages in each doc. + documents_[0]->GetPage(0); + num_pages_per_doc_ = documents_[0]->NumPages(); + if (num_pages_per_doc_ == 0) { + tprintf("First document cannot be empty!!\n"); + ASSERT_HOST(num_pages_per_doc_ > 0); + } + // Get rid of zero now if we don't need it. + if (serial / num_pages_per_doc_ % num_docs > 0) documents_[0]->UnCache(); + } + int doc_index = serial / num_pages_per_doc_ % num_docs; + const ImageData* doc = + documents_[doc_index]->GetPage(serial % num_pages_per_doc_); + // Count up total memory. Background loading makes it more complicated to + // keep a running count. + inT64 total_memory = 0; + for (int d = 0; d < num_docs; ++d) { + total_memory += documents_[d]->memory_used(); + } + if (total_memory >= max_memory_) { + // Find something to un-cache. + // If there are more than 3 in front, then serial is from the back reader + // of a pair of readers. If we un-cache from in-front-2 to 2-ahead, then + // we create a hole between them and then un-caching the backmost occupied + // will work for both. + int num_in_front = CountNeighbourDocs(doc_index, 1); + for (int offset = num_in_front - 2; + offset > 1 && total_memory >= max_memory_; --offset) { + int next_index = (doc_index + offset) % num_docs; + total_memory -= documents_[next_index]->UnCache(); + } + // If that didn't work, the best solution is to un-cache from the back. If + // we take away the document that a 2nd reader is using, it will put it + // back and make a hole between. + int num_behind = CountNeighbourDocs(doc_index, -1); + for (int offset = num_behind; offset < 0 && total_memory >= max_memory_; + ++offset) { + int next_index = (doc_index + offset + num_docs) % num_docs; + total_memory -= documents_[next_index]->UnCache(); + } + } + int next_index = (doc_index + 1) % num_docs; + if (!documents_[next_index]->IsCached() && total_memory < max_memory_) { + documents_[next_index]->LoadPageInBackground(0); + } + return doc; +} + +// Helper counts the number of adjacent cached neighbours of index looking in +// direction dir, ie index+dir, index+2*dir etc. +int DocumentCache::CountNeighbourDocs(int index, int dir) { + int num_docs = documents_.size(); + for (int offset = dir; abs(offset) < num_docs; offset += dir) { + int offset_index = (index + offset + num_docs) % num_docs; + if (!documents_[offset_index]->IsCached()) return offset - dir; + } + return num_docs; +} + +} // namespace tesseract. diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.h new file mode 100644 index 0000000..ae67229 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/imagedata.h @@ -0,0 +1,379 @@ +/////////////////////////////////////////////////////////////////////// +// File: imagedata.h +// Description: Class to hold information about a single image and its +// corresponding boxes or text file. +// Author: Ray Smith +// Created: Mon Jul 22 14:17:06 PDT 2013 +// +// (C) Copyright 2013, 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_IMAGE_IMAGEDATA_H_ +#define TESSERACT_IMAGE_IMAGEDATA_H_ + + +#include "genericvector.h" +#include "normalis.h" +#include "rect.h" +#include "strngs.h" +#include "svutil.h" + +struct Pix; + +namespace tesseract { + +// Amount of padding to apply in output pixels in feature mode. +const int kFeaturePadding = 2; +// Number of pixels to pad around text boxes. +const int kImagePadding = 4; + +// Enum to determine the caching and data sequencing strategy. +enum CachingStrategy { + // Reads all of one file before moving on to the next. Requires samples to be + // shuffled across files. Uses the count of samples in the first file as + // the count in all the files to achieve high-speed random access. As a + // consequence, if subsequent files are smaller, they get entries used more + // than once, and if subsequent files are larger, some entries are not used. + // Best for larger data sets that don't fit in memory. + CS_SEQUENTIAL, + // Reads one sample from each file in rotation. Does not require shuffled + // samples, but is extremely disk-intensive. Samples in smaller files also + // get used more often than samples in larger files. + // Best for smaller data sets that mostly fit in memory. + CS_ROUND_ROBIN, +}; + +class WordFeature { + public: + WordFeature(); + WordFeature(const FCOORD& fcoord, uinT8 dir); + + // Computes the maximum x and y value in the features. + static void ComputeSize(const GenericVector& features, + int* max_x, int* max_y); + // Draws the features in the given window. + static void Draw(const GenericVector& features, + ScrollView* window); + + // Accessors. + int x() const { return x_; } + int y() const { return y_; } + int dir() const { return dir_; } + + // 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); + + private: + inT16 x_; + uinT8 y_; + uinT8 dir_; +}; + +// A floating-point version of WordFeature, used as an intermediate during +// scaling. +struct FloatWordFeature { + static void FromWordFeatures(const GenericVector& word_features, + GenericVector* float_features); + // Sort function to sort first by x-bucket, then by y. + static int SortByXBucket(const void*, const void*); + + float x; + float y; + float dir; + int x_bucket; +}; + +// Class to hold information on a single image: +// Filename, cached image as a Pix*, character boxes, text transcription. +// The text transcription is the ground truth UTF-8 text for the image. +// Character boxes are optional and indicate the desired segmentation of +// the text into recognition units. +class ImageData { + public: + ImageData(); + // Takes ownership of the pix. + ImageData(bool vertical, Pix* pix); + ~ImageData(); + + // Builds and returns an ImageData from the basic data. Note that imagedata, + // truth_text, and box_text are all the actual file data, NOT filenames. + static ImageData* Build(const char* name, int page_number, const char* lang, + const char* imagedata, int imagedatasize, + const char* truth_text, const char* box_text); + + // Writes to the given file. Returns false in case of error. + bool Serialize(TFile* 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, TFile* fp); + // As DeSerialize, but only seeks past the data - hence a static method. + static bool SkipDeSerialize(bool swap, tesseract::TFile* fp); + + // Other accessors. + const STRING& imagefilename() const { + return imagefilename_; + } + void set_imagefilename(const STRING& name) { + imagefilename_ = name; + } + int page_number() const { + return page_number_; + } + void set_page_number(int num) { + page_number_ = num; + } + const GenericVector& image_data() const { + return image_data_; + } + const STRING& language() const { + return language_; + } + void set_language(const STRING& lang) { + language_ = lang; + } + const STRING& transcription() const { + return transcription_; + } + const GenericVector& boxes() const { + return boxes_; + } + const GenericVector& box_texts() const { + return box_texts_; + } + const STRING& box_text(int index) const { + return box_texts_[index]; + } + // Saves the given Pix as a PNG-encoded string and destroys it. + void SetPix(Pix* pix); + // Returns the Pix image for *this. Must be pixDestroyed after use. + Pix* GetPix() const; + // Gets anything and everything with a non-NULL pointer, prescaled to a + // given target_height (if 0, then the original image height), and aligned. + // Also returns (if not NULL) the width and height of the scaled image. + // The return value is the scaled Pix, which must be pixDestroyed after use, + // and scale_factor (if not NULL) is set to the scale factor that was applied + // to the image to achieve the target_height. + Pix* PreScale(int target_height, int max_height, float* scale_factor, + int* scaled_width, int* scaled_height, + GenericVector* boxes) const; + + int MemoryUsed() const; + + // Draws the data in a new window. + void Display() const; + + // Adds the supplied boxes and transcriptions that correspond to the correct + // page number. + void AddBoxes(const GenericVector& boxes, + const GenericVector& texts, + const GenericVector& box_pages); + + private: + // Saves the given Pix as a PNG-encoded string and destroys it. + static void SetPixInternal(Pix* pix, GenericVector* image_data); + // Returns the Pix image for the image_data. Must be pixDestroyed after use. + static Pix* GetPixInternal(const GenericVector& image_data); + // Parses the text string as a box file and adds any discovered boxes that + // match the page number. Returns false on error. + bool AddBoxes(const char* box_text); + + private: + STRING imagefilename_; // File to read image from. + inT32 page_number_; // Page number if multi-page tif or -1. + GenericVector image_data_; // PNG file data. + STRING language_; // Language code for image. + STRING transcription_; // UTF-8 ground truth of image. + GenericVector boxes_; // If non-empty boxes of the image. + GenericVector box_texts_; // String for text in each box. + bool vertical_text_; // Image has been rotated from vertical. +}; + +// A collection of ImageData that knows roughly how much memory it is using. +class DocumentData { + friend void* ReCachePagesFunc(void* data); + + public: + explicit DocumentData(const STRING& name); + ~DocumentData(); + + // Reads all the pages in the given lstmf filename to the cache. The reader + // is used to read the file. + bool LoadDocument(const char* filename, const char* lang, int start_page, + inT64 max_memory, FileReader reader); + // Sets up the document, without actually loading it. + void SetDocument(const char* filename, const char* lang, inT64 max_memory, + FileReader reader); + // Writes all the pages to the given filename. Returns false on error. + bool SaveDocument(const char* filename, FileWriter writer); + bool SaveToBuffer(GenericVector* buffer); + + // Adds the given page data to this document, counting up memory. + void AddPageToDocument(ImageData* page); + + const STRING& document_name() const { + SVAutoLock lock(&general_mutex_); + return document_name_; + } + int NumPages() const { + SVAutoLock lock(&general_mutex_); + return total_pages_; + } + inT64 memory_used() const { + SVAutoLock lock(&general_mutex_); + return memory_used_; + } + // If the given index is not currently loaded, loads it using a separate + // thread. Note: there are 4 cases: + // Document uncached: IsCached() returns false, total_pages_ < 0. + // Required page is available: IsPageAvailable returns true. In this case, + // total_pages_ > 0 and + // pages_offset_ <= index%total_pages_ <= pages_offset_+pages_.size() + // Pages are loaded, but the required one is not. + // The requested page is being loaded by LoadPageInBackground. In this case, + // index == pages_offset_. Once the loading starts, the pages lock is held + // until it completes, at which point IsPageAvailable will unblock and return + // true. + void LoadPageInBackground(int index); + // Returns a pointer to the page with the given index, modulo the total + // number of pages. Blocks until the background load is completed. + const ImageData* GetPage(int index); + // Returns true if the requested page is available, and provides a pointer, + // which may be NULL if the document is empty. May block, even though it + // doesn't guarantee to return true. + bool IsPageAvailable(int index, ImageData** page); + // Takes ownership of the given page index. The page is made NULL in *this. + ImageData* TakePage(int index) { + SVAutoLock lock(&pages_mutex_); + ImageData* page = pages_[index]; + pages_[index] = NULL; + return page; + } + // Returns true if the document is currently loaded or in the process of + // loading. + bool IsCached() const { return NumPages() >= 0; } + // Removes all pages from memory and frees the memory, but does not forget + // the document metadata. Returns the memory saved. + inT64 UnCache(); + + private: + // Sets the value of total_pages_ behind a mutex. + void set_total_pages(int total) { + SVAutoLock lock(&general_mutex_); + total_pages_ = total; + } + void set_memory_used(inT64 memory_used) { + SVAutoLock lock(&general_mutex_); + memory_used_ = memory_used; + } + // Locks the pages_mutex_ and Loads as many pages can fit in max_memory_ + // starting at index pages_offset_. + bool ReCachePages(); + + private: + // A name for this document. + STRING document_name_; + // The language of this document. + STRING lang_; + // A group of pages that corresponds in some loose way to a document. + PointerVector pages_; + // Page number of the first index in pages_. + int pages_offset_; + // Total number of pages in document (may exceed size of pages_.) + int total_pages_; + // Total of all pix sizes in the document. + inT64 memory_used_; + // Max memory to use at any time. + inT64 max_memory_; + // Saved reader from LoadDocument to allow re-caching. + FileReader reader_; + // Mutex that protects pages_ and pages_offset_ against multiple parallel + // loads, and provides a wait for page. + SVMutex pages_mutex_; + // Mutex that protects other data members that callers want to access without + // waiting for a load operation. + mutable SVMutex general_mutex_; +}; + +// A collection of DocumentData that knows roughly how much memory it is using. +// Note that while it supports background read-ahead, it assumes that a single +// thread is accessing documents, ie it is not safe for multiple threads to +// access different documents in parallel, as one may de-cache the other's +// content. +class DocumentCache { + public: + explicit DocumentCache(inT64 max_memory); + ~DocumentCache(); + + // Deletes all existing documents from the cache. + void Clear() { + documents_.clear(); + num_pages_per_doc_ = 0; + } + // Adds all the documents in the list of filenames, counting memory. + // The reader is used to read the files. + bool LoadDocuments(const GenericVector& filenames, const char* lang, + CachingStrategy cache_strategy, FileReader reader); + + // Adds document to the cache. + bool AddToCache(DocumentData* data); + + // Finds and returns a document by name. + DocumentData* FindDocument(const STRING& document_name) const; + + // Returns a page by serial number using the current cache_strategy_ to + // determine the mapping from serial number to page. + const ImageData* GetPageBySerial(int serial) { + if (cache_strategy_ == CS_SEQUENTIAL) + return GetPageSequential(serial); + else + return GetPageRoundRobin(serial); + } + + const PointerVector& documents() const { + return documents_; + } + // Returns the total number of pages in an epoch. For CS_ROUND_ROBIN cache + // strategy, could take a long time. + int TotalPages(); + + private: + // Returns a page by serial number, selecting them in a round-robin fashion + // from all the documents. Highly disk-intensive, but doesn't need samples + // to be shuffled between files to begin with. + const ImageData* GetPageRoundRobin(int serial); + // Returns a page by serial number, selecting them in sequence from each file. + // Requires the samples to be shuffled between the files to give a random or + // uniform distribution of data. Less disk-intensive than GetPageRoundRobin. + const ImageData* GetPageSequential(int serial); + + // Helper counts the number of adjacent cached neighbour documents_ of index + // looking in direction dir, ie index+dir, index+2*dir etc. + int CountNeighbourDocs(int index, int dir); + + // A group of pages that corresponds in some loose way to a document. + PointerVector documents_; + // Strategy to use for caching and serializing data samples. + CachingStrategy cache_strategy_; + // Number of pages in the first document, used as a divisor in + // GetPageSequential to determine the document index. + int num_pages_per_doc_; + // Max memory allowed in this cache. + inT64 max_memory_; +}; + +} // namespace tesseract + + +#endif // TESSERACT_IMAGE_IMAGEDATA_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ipoints.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ipoints.h new file mode 100644 index 0000000..0be7635 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ipoints.h @@ -0,0 +1,484 @@ +/********************************************************************** + * File: ipoints.h (Formerly icoords.h) + * Description: Inline functions for coords.h. + * Author: Ray Smith + * Created: Fri Jun 21 15:14:21 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IPOINTS_H +#define IPOINTS_H + +#include + +/********************************************************************** + * operator! + * + * Rotate an ICOORD 90 degrees anticlockwise. + **********************************************************************/ + +inline ICOORD +operator! ( //rotate 90 deg anti +const ICOORD & src //thing to rotate +) { + ICOORD result; //output + + result.xcoord = -src.ycoord; + result.ycoord = src.xcoord; + return result; +} + + +/********************************************************************** + * operator- + * + * Unary minus of an ICOORD. + **********************************************************************/ + +inline ICOORD +operator- ( //unary minus +const ICOORD & src //thing to minus +) { + ICOORD result; //output + + result.xcoord = -src.xcoord; + result.ycoord = -src.ycoord; + return result; +} + + +/********************************************************************** + * operator+ + * + * Add 2 ICOORDS. + **********************************************************************/ + +inline ICOORD +operator+ ( //sum vectors +const ICOORD & op1, //operands +const ICOORD & op2) { + ICOORD sum; //result + + sum.xcoord = op1.xcoord + op2.xcoord; + sum.ycoord = op1.ycoord + op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator+= + * + * Add 2 ICOORDS. + **********************************************************************/ + +inline ICOORD & +operator+= ( //sum vectors +ICOORD & op1, //operands +const ICOORD & op2) { + op1.xcoord += op2.xcoord; + op1.ycoord += op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator- + * + * Subtract 2 ICOORDS. + **********************************************************************/ + +inline ICOORD +operator- ( //subtract vectors +const ICOORD & op1, //operands +const ICOORD & op2) { + ICOORD sum; //result + + sum.xcoord = op1.xcoord - op2.xcoord; + sum.ycoord = op1.ycoord - op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator-= + * + * Subtract 2 ICOORDS. + **********************************************************************/ + +inline ICOORD & +operator-= ( //sum vectors +ICOORD & op1, //operands +const ICOORD & op2) { + op1.xcoord -= op2.xcoord; + op1.ycoord -= op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator% + * + * Scalar product of 2 ICOORDS. + **********************************************************************/ + +inline inT32 +operator% ( //scalar product +const ICOORD & op1, //operands +const ICOORD & op2) { + return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; +} + + +/********************************************************************** + * operator* + * + * Cross product of 2 ICOORDS. + **********************************************************************/ + +inline inT32 operator *( //cross product + const ICOORD &op1, //operands + const ICOORD &op2) { + return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; +} + + +/********************************************************************** + * operator* + * + * Scalar multiply of an ICOORD. + **********************************************************************/ + +inline ICOORD operator *( //scalar multiply + const ICOORD &op1, //operands + inT16 scale) { + ICOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +inline ICOORD operator *( //scalar multiply + inT16 scale, + const ICOORD &op1 //operands + ) { + ICOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +/********************************************************************** + * operator*= + * + * Scalar multiply of an ICOORD. + **********************************************************************/ + +inline ICOORD & +operator*= ( //scalar multiply +ICOORD & op1, //operands +inT16 scale) { + op1.xcoord *= scale; + op1.ycoord *= scale; + return op1; +} + + +/********************************************************************** + * operator/ + * + * Scalar divide of an ICOORD. + **********************************************************************/ + +inline ICOORD +operator/ ( //scalar divide +const ICOORD & op1, //operands +inT16 scale) { + ICOORD result; //output + + result.xcoord = op1.xcoord / scale; + result.ycoord = op1.ycoord / scale; + return result; +} + + +/********************************************************************** + * operator/= + * + * Scalar divide of an ICOORD. + **********************************************************************/ + +inline ICOORD & +operator/= ( //scalar divide +ICOORD & op1, //operands +inT16 scale) { + op1.xcoord /= scale; + op1.ycoord /= scale; + return op1; +} + + +/********************************************************************** + * ICOORD::rotate + * + * Rotate an ICOORD by the given (normalized) (cos,sin) vector. + **********************************************************************/ + +inline void ICOORD::rotate( //rotate by vector + const FCOORD& vec) { + inT16 tmp; + + tmp = (inT16) floor (xcoord * vec.x () - ycoord * vec.y () + 0.5); + ycoord = (inT16) floor (ycoord * vec.x () + xcoord * vec.y () + 0.5); + xcoord = tmp; +} + + +/********************************************************************** + * operator! + * + * Rotate an FCOORD 90 degrees anticlockwise. + **********************************************************************/ + +inline FCOORD +operator! ( //rotate 90 deg anti +const FCOORD & src //thing to rotate +) { + FCOORD result; //output + + result.xcoord = -src.ycoord; + result.ycoord = src.xcoord; + return result; +} + + +/********************************************************************** + * operator- + * + * Unary minus of an FCOORD. + **********************************************************************/ + +inline FCOORD +operator- ( //unary minus +const FCOORD & src //thing to minus +) { + FCOORD result; //output + + result.xcoord = -src.xcoord; + result.ycoord = -src.ycoord; + return result; +} + + +/********************************************************************** + * operator+ + * + * Add 2 FCOORDS. + **********************************************************************/ + +inline FCOORD +operator+ ( //sum vectors +const FCOORD & op1, //operands +const FCOORD & op2) { + FCOORD sum; //result + + sum.xcoord = op1.xcoord + op2.xcoord; + sum.ycoord = op1.ycoord + op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator+= + * + * Add 2 FCOORDS. + **********************************************************************/ + +inline FCOORD & +operator+= ( //sum vectors +FCOORD & op1, //operands +const FCOORD & op2) { + op1.xcoord += op2.xcoord; + op1.ycoord += op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator- + * + * Subtract 2 FCOORDS. + **********************************************************************/ + +inline FCOORD +operator- ( //subtract vectors +const FCOORD & op1, //operands +const FCOORD & op2) { + FCOORD sum; //result + + sum.xcoord = op1.xcoord - op2.xcoord; + sum.ycoord = op1.ycoord - op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator-= + * + * Subtract 2 FCOORDS. + **********************************************************************/ + +inline FCOORD & +operator-= ( //sum vectors +FCOORD & op1, //operands +const FCOORD & op2) { + op1.xcoord -= op2.xcoord; + op1.ycoord -= op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator% + * + * Scalar product of 2 FCOORDS. + **********************************************************************/ + +inline float +operator% ( //scalar product +const FCOORD & op1, //operands +const FCOORD & op2) { + return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; +} + + +/********************************************************************** + * operator* + * + * Cross product of 2 FCOORDS. + **********************************************************************/ + +inline float operator *( //cross product + const FCOORD &op1, //operands + const FCOORD &op2) { + return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; +} + + +/********************************************************************** + * operator* + * + * Scalar multiply of an FCOORD. + **********************************************************************/ + +inline FCOORD operator *( //scalar multiply + const FCOORD &op1, //operands + float scale) { + FCOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +inline FCOORD operator *( //scalar multiply + float scale, + const FCOORD &op1 //operands + ) { + FCOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +/********************************************************************** + * operator*= + * + * Scalar multiply of an FCOORD. + **********************************************************************/ + +inline FCOORD & +operator*= ( //scalar multiply +FCOORD & op1, //operands +float scale) { + op1.xcoord *= scale; + op1.ycoord *= scale; + return op1; +} + + +/********************************************************************** + * operator/ + * + * Scalar divide of an FCOORD. + **********************************************************************/ + +inline FCOORD +operator/ ( //scalar divide +const FCOORD & op1, //operands +float scale) { + FCOORD result; //output + + if (scale != 0) { + result.xcoord = op1.xcoord / scale; + result.ycoord = op1.ycoord / scale; + } + return result; +} + + +/********************************************************************** + * operator/= + * + * Scalar divide of an FCOORD. + **********************************************************************/ + +inline FCOORD & +operator/= ( //scalar divide +FCOORD & op1, //operands +float scale) { + if (scale != 0) { + op1.xcoord /= scale; + op1.ycoord /= scale; + } + return op1; +} + + +/********************************************************************** + * rotate + * + * Rotate an FCOORD by the given (normalized) (cos,sin) vector. + **********************************************************************/ + +inline void FCOORD::rotate( //rotate by vector + const FCOORD vec) { + float tmp; + + tmp = xcoord * vec.x () - ycoord * vec.y (); + ycoord = ycoord * vec.x () + xcoord * vec.y (); + xcoord = tmp; +} + +inline void FCOORD::unrotate(const FCOORD& vec) { + rotate(FCOORD(vec.x(), -vec.y())); +} + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.cpp new file mode 100644 index 0000000..6dffa2d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.cpp @@ -0,0 +1,259 @@ +/********************************************************************** + * File: linlsq.cpp (Formerly llsq.c) + * Description: Linear Least squares fitting code. + * Author: Ray Smith + * Created: Thu Sep 12 08:44:51 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include +#include "errcode.h" +#include "linlsq.h" + +const ERRCODE EMPTY_LLSQ = "Can't delete from an empty LLSQ"; + +/********************************************************************** + * LLSQ::clear + * + * Function to initialize a LLSQ. + **********************************************************************/ + +void LLSQ::clear() { // initialize + total_weight = 0.0; // no elements + sigx = 0.0; // update accumulators + sigy = 0.0; + sigxx = 0.0; + sigxy = 0.0; + sigyy = 0.0; +} + + +/********************************************************************** + * LLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void LLSQ::add(double x, double y) { // add an element + total_weight++; // count elements + sigx += x; // update accumulators + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; +} +// Adds an element with a specified weight. +void LLSQ::add(double x, double y, double weight) { + total_weight += weight; + sigx += x * weight; // update accumulators + sigy += y * weight; + sigxx += x * x * weight; + sigxy += x * y * weight; + sigyy += y * y * weight; +} +// Adds a whole LLSQ. +void LLSQ::add(const LLSQ& other) { + total_weight += other.total_weight; + sigx += other.sigx; // update accumulators + sigy += other.sigy; + sigxx += other.sigxx; + sigxy += other.sigxy; + sigyy += other.sigyy; +} + + +/********************************************************************** + * LLSQ::remove + * + * Delete an element from the acculuator. + **********************************************************************/ + +void LLSQ::remove(double x, double y) { // delete an element + if (total_weight <= 0.0) // illegal + EMPTY_LLSQ.error("LLSQ::remove", ABORT, NULL); + total_weight--; // count elements + sigx -= x; // update accumulators + sigy -= y; + sigxx -= x * x; + sigxy -= x * y; + sigyy -= y * y; +} + + +/********************************************************************** + * LLSQ::m + * + * Return the gradient of the line fit. + **********************************************************************/ + +double LLSQ::m() const { // get gradient + double covar = covariance(); + double x_var = x_variance(); + if (x_var != 0.0) + return covar / x_var; + else + return 0.0; // too little +} + + +/********************************************************************** + * LLSQ::c + * + * Return the constant of the line fit. + **********************************************************************/ + +double LLSQ::c(double m) const { // get constant + if (total_weight > 0.0) + return (sigy - m * sigx) / total_weight; + else + return 0; // too little +} + + +/********************************************************************** + * LLSQ::rms + * + * Return the rms error of the fit. + **********************************************************************/ + +double LLSQ::rms(double m, double c) const { // get error + double error; // total error + + if (total_weight > 0) { + error = sigyy + m * (m * sigxx + 2 * (c * sigx - sigxy)) + c * + (total_weight * c - 2 * sigy); + if (error >= 0) + error = sqrt(error / total_weight); // sqrt of mean + else + error = 0; + } else { + error = 0; // too little + } + return error; +} + + +/********************************************************************** + * LLSQ::pearson + * + * Return the pearson product moment correlation coefficient. + **********************************************************************/ + +double LLSQ::pearson() const { // get correlation + double r = 0.0; // Correlation is 0 if insufficent data. + + double covar = covariance(); + if (covar != 0.0) { + double var_product = x_variance() * y_variance(); + if (var_product > 0.0) + r = covar / sqrt(var_product); + } + return r; +} + +// Returns the x,y means as an FCOORD. +FCOORD LLSQ::mean_point() const { + if (total_weight > 0.0) { + return FCOORD(sigx / total_weight, sigy / total_weight); + } else { + return FCOORD(0.0f, 0.0f); + } +} + +// Returns the sqrt of the mean squared error measured perpendicular from the +// line through mean_point() in the direction dir. +// +// Derivation: +// Lemma: Let v and x_i (i=1..N) be a k-dimensional vectors (1xk matrices). +// Let % be dot product and ' be transpose. Note that: +// Sum[i=1..N] (v % x_i)^2 +// = v * [x_1' x_2' ... x_N'] * [x_1' x_2' .. x_N']' * v' +// If x_i have average 0 we have: +// = v * (N * COVARIANCE_MATRIX(X)) * v' +// Expanded for the case that k = 2, where we treat the dimensions +// as x_i and y_i, this is: +// = v * (N * [VAR(X), COV(X,Y); COV(X,Y) VAR(Y)]) * v' +// Now, we are trying to calculate the mean squared error, where v is +// perpendicular to our line of interest: +// Mean squared error +// = E [ (v % (x_i - x_avg))) ^2 ] +// = Sum (v % (x_i - x_avg))^2 / N +// = v * N * [VAR(X) COV(X,Y); COV(X,Y) VAR(Y)] / N * v' +// = v * [VAR(X) COV(X,Y); COV(X,Y) VAR(Y)] * v' +// = code below +double LLSQ::rms_orth(const FCOORD &dir) const { + FCOORD v = !dir; + v.normalise(); + return sqrt(v.x() * v.x() * x_variance() + + 2 * v.x() * v.y() * covariance() + + v.y() * v.y() * y_variance()); +} + +// Returns the direction of the fitted line as a unit vector, using the +// least mean squared perpendicular distance. The line runs through the +// mean_point, i.e. a point p on the line is given by: +// p = mean_point() + lambda * vector_fit() for some real number lambda. +// Note that the result (0<=x<=1, -1<=y<=-1) is directionally ambiguous +// and may be negated without changing its meaning. +// Fitting a line m + 饾渾v to a set of N points Pi = (xi, yi), where +// m is the mean point (饾潄, 饾潅) and +// v is the direction vector (cos饾渻, sin饾渻) +// The perpendicular distance of each Pi from the line is: +// (Pi - m) x v, where x is the scalar cross product. +// Total squared error is thus: +// E = 鈭((xi - 饾潄)sin饾渻 - (yi - 饾潅)cos饾渻)虏 +// = 鈭(xi - 饾潄)虏sin虏饾渻 - 2鈭(xi - 饾潄)(yi - 饾潅)sin饾渻 cos饾渻 + 鈭(yi - 饾潅)虏cos虏饾渻 +// = NVar(xi)sin虏饾渻 - 2NCovar(xi, yi)sin饾渻 cos饾渻 + NVar(yi)cos虏饾渻 (Eq 1) +// where Var(xi) is the variance of xi, +// and Covar(xi, yi) is the covariance of xi, yi. +// Taking the derivative wrt 饾渻 and setting to 0 to obtain the min/max: +// 0 = 2NVar(xi)sin饾渻 cos饾渻 -2NCovar(xi, yi)(cos虏饾渻 - sin虏饾渻) -2NVar(yi)sin饾渻 cos饾渻 +// => Covar(xi, yi)(cos虏饾渻 - sin虏饾渻) = (Var(xi) - Var(yi))sin饾渻 cos饾渻 +// Using double angles: +// 2Covar(xi, yi)cos2饾渻 = (Var(xi) - Var(yi))sin2饾渻 (Eq 2) +// So 饾渻 = 0.5 atan2(2Covar(xi, yi), Var(xi) - Var(yi)) (Eq 3) + +// Because it involves 2饾渻 , Eq 2 has 2 solutions 90 degrees apart, but which +// is the min and which is the max? From Eq1: +// E/N = Var(xi)sin虏饾渻 - 2Covar(xi, yi)sin饾渻 cos饾渻 + Var(yi)cos虏饾渻 +// and 90 degrees away, using sin/cos equivalences: +// E'/N = Var(xi)cos虏饾渻 + 2Covar(xi, yi)sin饾渻 cos饾渻 + Var(yi)sin虏饾渻 +// The second error is smaller (making it the minimum) iff +// E'/N < E/N ie: +// (Var(xi) - Var(yi))(cos虏饾渻 - sin虏饾渻) < -4Covar(xi, yi)sin饾渻 cos饾渻 +// Using double angles: +// (Var(xi) - Var(yi))cos2饾渻 < -2Covar(xi, yi)sin2饾渻 (InEq 1) +// But atan2(2Covar(xi, yi), Var(xi) - Var(yi)) picks 2饾渻 such that: +// sgn(cos2饾渻) = sgn(Var(xi) - Var(yi)) and sgn(sin2饾渻) = sgn(Covar(xi, yi)) +// so InEq1 can *never* be true, making the atan2 result *always* the min! +// In the degenerate case, where Covar(xi, yi) = 0 AND Var(xi) = Var(yi), +// the 2 solutions have equal error and the inequality is still false. +// Therefore the solution really is as trivial as Eq 3. + +// This is equivalent to returning the Principal Component in PCA, or the +// eigenvector corresponding to the largest eigenvalue in the covariance +// matrix. However, atan2 is much simpler! The one reference I found that +// uses this formula is http://web.mit.edu/18.06/www/Essays/tlsfit.pdf but +// that is still a much more complex derivation. It seems Pearson had already +// found this simple solution in 1901. +// http://books.google.com/books?id=WXwvAQAAIAAJ&pg=PA559 +FCOORD LLSQ::vector_fit() const { + double x_var = x_variance(); + double y_var = y_variance(); + double covar = covariance(); + double theta = 0.5 * atan2(2.0 * covar, x_var - y_var); + FCOORD result(cos(theta), sin(theta)); + return result; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.h new file mode 100644 index 0000000..d1cea80 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/linlsq.h @@ -0,0 +1,134 @@ +/********************************************************************** + * File: linlsq.h (Formerly llsq.h) + * Description: Linear Least squares fitting code. + * Author: Ray Smith + * Created: Thu Sep 12 08:44:51 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSERACT_CCSTRUCT_LINLSQ_H_ +#define TESSERACT_CCSTRUCT_LINLSQ_H_ + +#include "points.h" +#include "params.h" + +class LLSQ { + public: + LLSQ() { // constructor + clear(); // set to zeros + } + void clear(); // initialize + + // Adds an element with a weight of 1. + void add(double x, double y); + // Adds an element with a specified weight. + void add(double x, double y, double weight); + // Adds a whole LLSQ. + void add(const LLSQ& other); + // Deletes an element with a weight of 1. + void remove(double x, double y); + inT32 count() const { // no of elements + return static_cast(total_weight + 0.5); + } + + double m() const; // get gradient + double c(double m) const; // get constant + double rms(double m, double c) const; // get error + double pearson() const; // get correlation coefficient. + + // Returns the x,y means as an FCOORD. + FCOORD mean_point() const; + + // Returns the average sum of squared perpendicular error from a line + // through mean_point() in the direction dir. + double rms_orth(const FCOORD &dir) const; + + // Returns the direction of the fitted line as a unit vector, using the + // least mean squared perpendicular distance. The line runs through the + // mean_point, i.e. a point p on the line is given by: + // p = mean_point() + lambda * vector_fit() for some real number lambda. + // Note that the result (0<=x<=1, -1<=y<=-1) is directionally ambiguous + // and may be negated without changing its meaning, since a line is only + // unique to a range of pi radians. + // Modernists prefer to think of this as an Eigenvalue problem, but + // Pearson had the simple solution in 1901. + // + // Note that this is equivalent to returning the Principal Component in PCA, + // or the eigenvector corresponding to the largest eigenvalue in the + // covariance matrix. + FCOORD vector_fit() const; + + // Returns the covariance. + double covariance() const { + if (total_weight > 0.0) + return (sigxy - sigx * sigy / total_weight) / total_weight; + else + return 0.0; + } + double x_variance() const { + if (total_weight > 0.0) + return (sigxx - sigx * sigx / total_weight) / total_weight; + else + return 0.0; + } + double y_variance() const { + if (total_weight > 0.0) + return (sigyy - sigy * sigy / total_weight) / total_weight; + else + return 0.0; + } + + private: + double total_weight; // no of elements or sum of weights. + double sigx; // sum of x + double sigy; // sum of y + double sigxx; // sum x squared + double sigxy; // sum of xy + double sigyy; // sum y squared +}; + + +// Returns the median value of the vector, given that the values are +// circular, with the given modulus. Values may be signed or unsigned, +// eg range from -pi to pi (modulus 2pi) or from 0 to 2pi (modulus 2pi). +// NOTE that the array is shuffled, but the time taken is linear. +// An assumption is made that most of the values are spread over no more than +// half the range, but wrap-around is accounted for if the median is near +// the wrap-around point. +// Cannot be a member of GenericVector, as it makes heavy used of LLSQ. +// T must be an integer or float/double type. +template T MedianOfCircularValues(T modulus, GenericVector* v) { + LLSQ stats; + T halfrange = static_cast(modulus / 2); + int num_elements = v->size(); + for (int i = 0; i < num_elements; ++i) { + stats.add((*v)[i], (*v)[i] + halfrange); + } + bool offset_needed = stats.y_variance() < stats.x_variance(); + if (offset_needed) { + for (int i = 0; i < num_elements; ++i) { + (*v)[i] += halfrange; + } + } + int median_index = v->choose_nth_item(num_elements / 2); + if (offset_needed) { + for (int i = 0; i < num_elements; ++i) { + (*v)[i] -= halfrange; + } + } + return (*v)[median_index]; +} + + +#endif // TESSERACT_CCSTRUCT_LINLSQ_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.cpp new file mode 100644 index 0000000..88d2340 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.cpp @@ -0,0 +1,159 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: matrix.c (Formerly matrix.c) + * Description: Ratings matrix code. (Used by associator) + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 13:18:47 1990 + * Modified: Wed Mar 20 09:44:47 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "matrix.h" + +#include "callcpp.h" +#include "ratngs.h" +#include "tprintf.h" +#include "unicharset.h" + +// Returns true if there are any real classification results. +bool MATRIX::Classified(int col, int row, int wildcard_id) const { + if (get(col, row) == NOT_CLASSIFIED) return false; + BLOB_CHOICE_IT b_it(get(col, row)); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOB_CHOICE* choice = b_it.data(); + if (choice->IsClassified()) + return true; + } + return false; +} + +// Expands the existing matrix in-place to make the band wider, without +// losing any existing data. +void MATRIX::IncreaseBandSize(int bandwidth) { + ResizeWithCopy(dimension(), bandwidth); +} + +// Returns a bigger MATRIX with a new column and row in the matrix in order +// to split the blob at the given (ind,ind) diagonal location. +// Entries are relocated to the new MATRIX using the transformation defined +// by MATRIX_COORD::MapForSplit. +// Transfers the pointer data to the new MATRIX and deletes *this. +MATRIX* MATRIX::ConsumeAndMakeBigger(int ind) { + int dim = dimension(); + int band_width = bandwidth(); + // Check to see if bandwidth needs expanding. + for (int col = ind; col >= 0 && col > ind - band_width; --col) { + if (array_[col * band_width + band_width - 1] != empty_) { + ++band_width; + break; + } + } + MATRIX* result = new MATRIX(dim + 1, band_width); + + for (int col = 0; col < dim; ++col) { + for (int row = col; row < dim && row < col + bandwidth(); ++row) { + MATRIX_COORD coord(col, row); + coord.MapForSplit(ind); + BLOB_CHOICE_LIST* choices = get(col, row); + if (choices != NULL) { + // Correct matrix location on each choice. + BLOB_CHOICE_IT bc_it(choices); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + BLOB_CHOICE* choice = bc_it.data(); + choice->set_matrix_cell(coord.col, coord.row); + } + ASSERT_HOST(coord.Valid(*result)); + result->put(coord.col, coord.row, choices); + } + } + } + delete this; + return result; +} + +// Makes and returns a deep copy of *this, including all the BLOB_CHOICEs +// on the lists, but not any LanguageModelState that may be attached to the +// BLOB_CHOICEs. +MATRIX* MATRIX::DeepCopy() const { + int dim = dimension(); + int band_width = bandwidth(); + MATRIX* result = new MATRIX(dim, band_width); + for (int col = 0; col < dim; ++col) { + for (int row = col; row < dim && row < col + band_width; ++row) { + BLOB_CHOICE_LIST* choices = get(col, row); + if (choices != NULL) { + BLOB_CHOICE_LIST* copy_choices = new BLOB_CHOICE_LIST; + copy_choices->deep_copy(choices, &BLOB_CHOICE::deep_copy); + result->put(col, row, copy_choices); + } + } + } + return result; +} + +// Print the best guesses out of the match rating matrix. +void MATRIX::print(const UNICHARSET &unicharset) const { + tprintf("Ratings Matrix (top 3 choices)\n"); + int dim = dimension(); + int band_width = bandwidth(); + int row, col; + for (col = 0; col < dim; ++col) { + for (row = col; row < dim && row < col + band_width; ++row) { + BLOB_CHOICE_LIST *rating = this->get(col, row); + if (rating == NOT_CLASSIFIED) continue; + BLOB_CHOICE_IT b_it(rating); + tprintf("col=%d row=%d ", col, row); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + tprintf("%s rat=%g cert=%g " , + unicharset.id_to_unichar(b_it.data()->unichar_id()), + b_it.data()->rating(), b_it.data()->certainty()); + } + tprintf("\n"); + } + tprintf("\n"); + } + tprintf("\n"); + for (col = 0; col < dim; ++col) tprintf("\t%d", col); + tprintf("\n"); + for (row = 0; row < dim; ++row) { + for (col = 0; col <= row; ++col) { + if (col == 0) tprintf("%d\t", row); + if (row >= col + band_width) { + tprintf(" \t"); + continue; + } + BLOB_CHOICE_LIST *rating = this->get(col, row); + if (rating != NOT_CLASSIFIED) { + BLOB_CHOICE_IT b_it(rating); + int counter = 0; + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + tprintf("%s ", + unicharset.id_to_unichar(b_it.data()->unichar_id())); + ++counter; + if (counter == 3) break; + } + tprintf("\t"); + } else { + tprintf(" \t"); + } + } + tprintf("\n"); + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.h new file mode 100644 index 0000000..4b5b242 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/matrix.h @@ -0,0 +1,635 @@ +/* -*-C-*- + ****************************************************************************** + * File: matrix.h (Formerly matrix.h) + * Description: Generic 2-d array/matrix and banded triangular matrix class. + * Author: Ray Smith + * TODO(rays) Separate from ratings matrix, which it also contains: + * + * Descrition: Ratings matrix class (specialization of banded matrix). + * Segmentation search matrix of lists of BLOB_CHOICE. + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 13:22:06 1990 + * Modified: Tue Mar 19 16:00:20 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef TESSERACT_CCSTRUCT_MATRIX_H__ +#define TESSERACT_CCSTRUCT_MATRIX_H__ + +#include +#include "kdpair.h" +#include "points.h" +#include "serialis.h" +#include "unicharset.h" + +class BLOB_CHOICE; +class BLOB_CHOICE_LIST; + +#define NOT_CLASSIFIED reinterpret_cast(0) + +// A generic class to hold a 2-D matrix with entries of type T, but can also +// act as a base class for other implementations, such as a triangular or +// banded matrix. +template +class GENERIC_2D_ARRAY { + public: + // Initializes the array size, and empty element, but cannot allocate memory + // for the subclasses or initialize because calls to the num_elements + // member will be routed to the base class implementation. Subclasses can + // either pass the memory in, or allocate after by calling Resize(). + GENERIC_2D_ARRAY(int dim1, int dim2, const T& empty, T* array) + : empty_(empty), dim1_(dim1), dim2_(dim2), array_(array) { + size_allocated_ = dim1 * dim2; + } + // Original constructor for a full rectangular matrix DOES allocate memory + // and initialize it to empty. + GENERIC_2D_ARRAY(int dim1, int dim2, const T& empty) + : empty_(empty), dim1_(dim1), dim2_(dim2) { + int new_size = dim1 * dim2; + array_ = new T[new_size]; + size_allocated_ = new_size; + for (int i = 0; i < size_allocated_; ++i) + array_[i] = empty_; + } + // Default constructor for array allocation. Use Resize to set the size. + GENERIC_2D_ARRAY() + : array_(NULL), empty_(static_cast(0)), dim1_(0), dim2_(0), + size_allocated_(0) { + } + GENERIC_2D_ARRAY(const GENERIC_2D_ARRAY& src) + : array_(NULL), empty_(static_cast(0)), dim1_(0), dim2_(0), + size_allocated_(0) { + *this = src; + } + virtual ~GENERIC_2D_ARRAY() { delete[] array_; } + + void operator=(const GENERIC_2D_ARRAY& src) { + ResizeNoInit(src.dim1(), src.dim2()); + memcpy(array_, src.array_, num_elements() * sizeof(array_[0])); + } + + // Reallocate the array to the given size. Does not keep old data, but does + // not initialize the array either. + void ResizeNoInit(int size1, int size2) { + int new_size = size1 * size2; + if (new_size > size_allocated_) { + delete [] array_; + array_ = new T[new_size]; + size_allocated_ = new_size; + } + dim1_ = size1; + dim2_ = size2; + } + + // Reallocate the array to the given size. Does not keep old data. + void Resize(int size1, int size2, const T& empty) { + empty_ = empty; + ResizeNoInit(size1, size2); + Clear(); + } + + // Reallocate the array to the given size, keeping old data. + void ResizeWithCopy(int size1, int size2) { + if (size1 != dim1_ || size2 != dim2_) { + int new_size = size1 * size2; + T* new_array = new T[new_size]; + for (int col = 0; col < size1; ++col) { + for (int row = 0; row < size2; ++row) { + int old_index = col * dim2() + row; + int new_index = col * size2 + row; + if (col < dim1_ && row < dim2_) { + new_array[new_index] = array_[old_index]; + } else { + new_array[new_index] = empty_; + } + } + } + delete[] array_; + array_ = new_array; + dim1_ = size1; + dim2_ = size2; + size_allocated_ = new_size; + } + } + + // Sets all the elements of the array to the empty value. + void Clear() { + int total_size = num_elements(); + for (int i = 0; i < total_size; ++i) + array_[i] = empty_; + } + + // Writes to the given file. Returns false in case of error. + // Only works with bitwise-serializeable types! + bool Serialize(FILE* fp) const { + if (!SerializeSize(fp)) return false; + if (fwrite(&empty_, sizeof(empty_), 1, fp) != 1) return false; + int size = num_elements(); + if (fwrite(array_, sizeof(*array_), size, fp) != size) return false; + return true; + } + bool Serialize(tesseract::TFile* fp) const { + if (!SerializeSize(fp)) return false; + if (fp->FWrite(&empty_, sizeof(empty_), 1) != 1) return false; + int size = num_elements(); + if (fp->FWrite(array_, sizeof(*array_), size) != size) return false; + return true; + } + + // Reads from the given file. Returns false in case of error. + // Only works with bitwise-serializeable types! + // If swap is true, assumes a big/little-endian swap is needed. + bool DeSerialize(bool swap, FILE* fp) { + if (!DeSerializeSize(swap, fp)) return false; + if (fread(&empty_, sizeof(empty_), 1, fp) != 1) return false; + if (swap) ReverseN(&empty_, sizeof(empty_)); + int size = num_elements(); + if (fread(array_, sizeof(*array_), size, fp) != size) return false; + if (swap) { + for (int i = 0; i < size; ++i) + ReverseN(&array_[i], sizeof(array_[i])); + } + return true; + } + bool DeSerialize(bool swap, tesseract::TFile* fp) { + if (!DeSerializeSize(swap, fp)) return false; + if (fp->FRead(&empty_, sizeof(empty_), 1) != 1) return false; + if (swap) ReverseN(&empty_, sizeof(empty_)); + int size = num_elements(); + if (fp->FRead(array_, sizeof(*array_), size) != size) return false; + if (swap) { + for (int i = 0; i < size; ++i) + ReverseN(&array_[i], sizeof(array_[i])); + } + return true; + } + + // Writes to the given file. Returns false in case of error. + // Assumes a T::Serialize(FILE*) const function. + bool SerializeClasses(FILE* fp) const { + if (!SerializeSize(fp)) return false; + if (!empty_.Serialize(fp)) return false; + int size = num_elements(); + for (int i = 0; i < size; ++i) { + if (!array_[i].Serialize(fp)) return false; + } + return true; + } + + // Reads from the given file. Returns false in case of error. + // Assumes a T::DeSerialize(bool swap, FILE*) function. + // If swap is true, assumes a big/little-endian swap is needed. + bool DeSerializeClasses(bool swap, FILE* fp) { + if (!DeSerializeSize(swap, fp)) return false; + if (!empty_.DeSerialize(swap, fp)) return false; + int size = num_elements(); + for (int i = 0; i < size; ++i) { + if (!array_[i].DeSerialize(swap, fp)) return false; + } + return true; + } + + // Provide the dimensions of this rectangular matrix. + int dim1() const { return dim1_; } + int dim2() const { return dim2_; } + // Returns the number of elements in the array. + // Banded/triangular matrices may override. + virtual int num_elements() const { return dim1_ * dim2_; } + + // Expression to select a specific location in the matrix. The matrix is + // stored COLUMN-major, so the left-most index is the most significant. + // This allows [][] access to use indices in the same order as (,). + virtual int index(int column, int row) const { + return (column * dim2_ + row); + } + + // Put a list element into the matrix at a specific location. + void put(ICOORD pos, const T& thing) { + array_[this->index(pos.x(), pos.y())] = thing; + } + void put(int column, int row, const T& thing) { + array_[this->index(column, row)] = thing; + } + + // Get the item at a specified location from the matrix. + T get(ICOORD pos) const { + return array_[this->index(pos.x(), pos.y())]; + } + T get(int column, int row) const { + return array_[this->index(column, row)]; + } + // Return a reference to the element at the specified location. + const T& operator()(int column, int row) const { + return array_[this->index(column, row)]; + } + T& operator()(int column, int row) { + return array_[this->index(column, row)]; + } + // Allow access using array[column][row]. NOTE that the indices are + // in the same left-to-right order as the () indexing. + T* operator[](int column) { + return &array_[this->index(column, 0)]; + } + const T* operator[](int column) const { + return &array_[this->index(column, 0)]; + } + + // Adds addend to *this, element-by-element. + void operator+=(const GENERIC_2D_ARRAY& addend) { + if (dim2_ == addend.dim2_) { + // Faster if equal size in the major dimension. + int size = MIN(num_elements(), addend.num_elements()); + for (int i = 0; i < size; ++i) { + array_[i] += addend.array_[i]; + } + } else { + for (int x = 0; x < dim1_; x++) { + for (int y = 0; y < dim2_; y++) { + (*this)(x, y) += addend(x, y); + } + } + } + } + // Subtracts minuend from *this, element-by-element. + void operator-=(const GENERIC_2D_ARRAY& minuend) { + if (dim2_ == minuend.dim2_) { + // Faster if equal size in the major dimension. + int size = MIN(num_elements(), minuend.num_elements()); + for (int i = 0; i < size; ++i) { + array_[i] -= minuend.array_[i]; + } + } else { + for (int x = 0; x < dim1_; x++) { + for (int y = 0; y < dim2_; y++) { + (*this)(x, y) -= minuend(x, y); + } + } + } + } + // Adds addend to all elements. + void operator+=(const T& addend) { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + array_[i] += addend; + } + } + // Multiplies *this by factor, element-by-element. + void operator*=(const T& factor) { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + array_[i] *= factor; + } + } + // Clips *this to the given range. + void Clip(const T& rangemin, const T& rangemax) { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + array_[i] = ClipToRange(array_[i], rangemin, rangemax); + } + } + // Returns true if all elements of *this are within the given range. + // Only uses operator< + bool WithinBounds(const T& rangemin, const T& rangemax) const { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + const T& value = array_[i]; + if (value < rangemin || rangemax < value) + return false; + } + return true; + } + // Normalize the whole array. + double Normalize() { + int size = num_elements(); + if (size <= 0) return 0.0; + // Compute the mean. + double mean = 0.0; + for (int i = 0; i < size; ++i) { + mean += array_[i]; + } + mean /= size; + // Subtract the mean and compute the standard deviation. + double sd = 0.0; + for (int i = 0; i < size; ++i) { + double normed = array_[i] - mean; + array_[i] = normed; + sd += normed * normed; + } + sd = sqrt(sd / size); + if (sd > 0.0) { + // Divide by the sd. + for (int i = 0; i < size; ++i) { + array_[i] /= sd; + } + } + return sd; + } + + // Returns the maximum value of the array. + T Max() const { + int size = num_elements(); + if (size <= 0) return empty_; + // Compute the max. + T max_value = array_[0]; + for (int i = 1; i < size; ++i) { + const T& value = array_[i]; + if (value > max_value) max_value = value; + } + return max_value; + } + + // Returns the maximum absolute value of the array. + T MaxAbs() const { + int size = num_elements(); + if (size <= 0) return empty_; + // Compute the max. + T max_abs = static_cast(0); + for (int i = 0; i < size; ++i) { + T value = static_cast(fabs(array_[i])); + if (value > max_abs) max_abs = value; + } + return max_abs; + } + + // Accumulates the element-wise sums of squares of src into *this. + void SumSquares(const GENERIC_2D_ARRAY& src) { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + array_[i] += src.array_[i] * src.array_[i]; + } + } + + // Scales each element using the ada-grad algorithm, ie array_[i] by + // sqrt(num_samples/max(1,sqsum[i])). + void AdaGradScaling(const GENERIC_2D_ARRAY& sqsum, int num_samples) { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + array_[i] *= sqrt(num_samples / MAX(1.0, sqsum.array_[i])); + } + } + + void AssertFinite() const { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + ASSERT_HOST(isfinite(array_[i])); + } + } + + // REGARDLESS OF THE CURRENT DIMENSIONS, treats the data as a + // num_dims-dimensional array/tensor with dimensions given by dims, (ordered + // from most significant to least significant, the same as standard C arrays) + // and moves src_dim to dest_dim, with the initial dest_dim and any dimensions + // in between shifted towards the hole left by src_dim. Example: + // Current data content: array_=[0, 1, 2, ....119] + // perhaps *this may be of dim[40, 3], with values [[0, 1, 2][3, 4, 5]... + // but the current dimensions are irrelevant. + // num_dims = 4, dims=[5, 4, 3, 2] + // src_dim=3, dest_dim=1 + // tensor=[[[[0, 1][2, 3][4, 5]] + // [[6, 7][8, 9][10, 11]] + // [[12, 13][14, 15][16, 17]] + // [[18, 19][20, 21][22, 23]]] + // [[[24, 25]... + // output dims =[5, 2, 4, 3] + // output tensor=[[[[0, 2, 4][6, 8, 10][12, 14, 16][18, 20, 22]] + // [[1, 3, 5][7, 9, 11][13, 15, 17][19, 21, 23]]] + // [[[24, 26, 28]... + // which is stored in the array_ as: + // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 1, 3, 5, 7, 9, 11, 13...] + // NOTE: the 2 stored matrix dimensions are simply copied from *this. To + // change the dimensions after the transpose, use ResizeNoInit. + // Higher dimensions above 2 are strictly the responsibility of the caller. + void RotatingTranspose(const int* dims, int num_dims, int src_dim, + int dest_dim, GENERIC_2D_ARRAY* result) const { + int max_d = MAX(src_dim, dest_dim); + int min_d = MIN(src_dim, dest_dim); + // In a tensor of shape [d0, d1... min_d, ... max_d, ... dn-2, dn-1], the + // ends outside of min_d and max_d are unaffected, with [max_d +1, dn-1] + // being contiguous blocks of data that will move together, and + // [d0, min_d -1] being replicas of the transpose operation. + // num_replicas represents the large dimensions unchanged by the operation. + // move_size represents the small dimensions unchanged by the operation. + // src_step represents the stride in the src between each adjacent group + // in the destination. + int num_replicas = 1, move_size = 1, src_step = 1; + for (int d = 0; d < min_d; ++d) num_replicas *= dims[d]; + for (int d = max_d + 1; d < num_dims; ++d) move_size *= dims[d]; + for (int d = src_dim + 1; d < num_dims; ++d) src_step *= dims[d]; + if (src_dim > dest_dim) src_step *= dims[src_dim]; + // wrap_size is the size of a single replica, being the amount that is + // handled num_replicas times. + int wrap_size = move_size; + for (int d = min_d; d <= max_d; ++d) wrap_size *= dims[d]; + result->ResizeNoInit(dim1_, dim2_); + result->empty_ = empty_; + const T* src = array_; + T* dest = result->array_; + for (int replica = 0; replica < num_replicas; ++replica) { + for (int start = 0; start < src_step; start += move_size) { + for (int pos = start; pos < wrap_size; pos += src_step) { + memcpy(dest, src + pos, sizeof(*dest) * move_size); + dest += move_size; + } + } + src += wrap_size; + } + } + + // Delete objects pointed to by array_[i]. + void delete_matrix_pointers() { + int size = num_elements(); + for (int i = 0; i < size; ++i) { + T matrix_cell = array_[i]; + if (matrix_cell != empty_) + delete matrix_cell; + } + } + + protected: + // Factored helper to serialize the size. + bool SerializeSize(FILE* fp) const { + inT32 size = dim1_; + if (fwrite(&size, sizeof(size), 1, fp) != 1) return false; + size = dim2_; + if (fwrite(&size, sizeof(size), 1, fp) != 1) return false; + return true; + } + bool SerializeSize(tesseract::TFile* fp) const { + inT32 size = dim1_; + if (fp->FWrite(&size, sizeof(size), 1) != 1) return false; + size = dim2_; + if (fp->FWrite(&size, sizeof(size), 1) != 1) return false; + return true; + } + // Factored helper to deserialize the size. + // If swap is true, assumes a big/little-endian swap is needed. + bool DeSerializeSize(bool swap, FILE* fp) { + inT32 size1, size2; + if (fread(&size1, sizeof(size1), 1, fp) != 1) return false; + if (fread(&size2, sizeof(size2), 1, fp) != 1) return false; + if (swap) { + ReverseN(&size1, sizeof(size1)); + ReverseN(&size2, sizeof(size2)); + } + Resize(size1, size2, empty_); + return true; + } + bool DeSerializeSize(bool swap, tesseract::TFile* fp) { + inT32 size1, size2; + if (fp->FRead(&size1, sizeof(size1), 1) != 1) return false; + if (fp->FRead(&size2, sizeof(size2), 1) != 1) return false; + if (swap) { + ReverseN(&size1, sizeof(size1)); + ReverseN(&size2, sizeof(size2)); + } + Resize(size1, size2, empty_); + return true; + } + + T* array_; + T empty_; // The unused cell. + int dim1_; // Size of the 1st dimension in indexing functions. + int dim2_; // Size of the 2nd dimension in indexing functions. + // The total size to which the array can be expanded before a realloc is + // needed. If Resize is used, memory is retained so it can be re-expanded + // without a further alloc, and this stores the allocated size. + int size_allocated_; +}; + +// A generic class to store a banded triangular matrix with entries of type T. +// In this array, the nominally square matrix is dim1_ x dim1_, and dim2_ is +// the number of bands, INCLUDING the diagonal. The storage is thus of size +// dim1_ * dim2_ and index(col, row) = col * dim2_ + row - col, and an +// assert will fail if row < col or row - col >= dim2. +template +class BandTriMatrix : public GENERIC_2D_ARRAY { + public: + // Allocate a piece of memory to hold a 2d-array of the given dimension. + // Initialize all the elements of the array to empty instead of assuming + // that a default constructor can be used. + BandTriMatrix(int dim1, int dim2, const T& empty) + : GENERIC_2D_ARRAY(dim1, dim2, empty) { + } + // The default destructor will do. + + // Provide the dimensions of this matrix. + // dimension is the size of the nominally square matrix. + int dimension() const { return this->dim1_; } + // bandwidth is the number of bands in the matrix, INCLUDING the diagonal. + int bandwidth() const { return this->dim2_; } + + // Expression to select a specific location in the matrix. The matrix is + // stored COLUMN-major, so the left-most index is the most significant. + // This allows [][] access to use indices in the same order as (,). + virtual int index(int column, int row) const { + ASSERT_HOST(row >= column); + ASSERT_HOST(row - column < this->dim2_); + return column * this->dim2_ + row - column; + } + + // Appends array2 corner-to-corner to *this, making an array of dimension + // equal to the sum of the individual dimensions. + // array2 is not destroyed, but is left empty, as all elements are moved + // to *this. + void AttachOnCorner(BandTriMatrix* array2) { + int new_dim1 = this->dim1_ + array2->dim1_; + int new_dim2 = MAX(this->dim2_, array2->dim2_); + T* new_array = new T[new_dim1 * new_dim2]; + for (int col = 0; col < new_dim1; ++col) { + for (int j = 0; j < new_dim2; ++j) { + int new_index = col * new_dim2 + j; + if (col < this->dim1_ && j < this->dim2_) { + new_array[new_index] = this->get(col, col + j); + } else if (col >= this->dim1_ && j < array2->dim2_) { + new_array[new_index] = array2->get(col - this->dim1_, + col - this->dim1_ + j); + array2->put(col - this->dim1_, col - this->dim1_ + j, NULL); + } else { + new_array[new_index] = this->empty_; + } + } + } + delete[] this->array_; + this->array_ = new_array; + this->dim1_ = new_dim1; + this->dim2_ = new_dim2; + } +}; + +class MATRIX : public BandTriMatrix { + public: + MATRIX(int dimension, int bandwidth) + : BandTriMatrix(dimension, bandwidth, NOT_CLASSIFIED) {} + + // Returns true if there are any real classification results. + bool Classified(int col, int row, int wildcard_id) const; + + // Expands the existing matrix in-place to make the band wider, without + // losing any existing data. + void IncreaseBandSize(int bandwidth); + + // Returns a bigger MATRIX with a new column and row in the matrix in order + // to split the blob at the given (ind,ind) diagonal location. + // Entries are relocated to the new MATRIX using the transformation defined + // by MATRIX_COORD::MapForSplit. + // Transfers the pointer data to the new MATRIX and deletes *this. + MATRIX* ConsumeAndMakeBigger(int ind); + + // Makes and returns a deep copy of *this, including all the BLOB_CHOICEs + // on the lists, but not any LanguageModelState that may be attached to the + // BLOB_CHOICEs. + MATRIX* DeepCopy() const; + + // Print a shortened version of the contents of the matrix. + void print(const UNICHARSET &unicharset) const; +}; + +struct MATRIX_COORD { + static void Delete(void *arg) { + MATRIX_COORD *c = static_cast(arg); + delete c; + } + // Default constructor required by GenericHeap. + MATRIX_COORD() : col(0), row(0) {} + MATRIX_COORD(int c, int r): col(c), row(r) {} + ~MATRIX_COORD() {} + + bool Valid(const MATRIX &m) const { + return 0 <= col && col < m.dimension() && + col <= row && row < col + m.bandwidth() && row < m.dimension(); + } + + // Remaps the col,row pair to split the blob at the given (ind,ind) diagonal + // location. + // Entries at (i,j) for i in [0,ind] and j in [ind,dim) move to (i,j+1), + // making a new row at ind. + // Entries at (i,j) for i in [ind+1,dim) and j in [i,dim) move to (i+i,j+1), + // making a new column at ind+1. + void MapForSplit(int ind) { + ASSERT_HOST(row >= col); + if (col > ind) ++col; + if (row >= ind) ++row; + ASSERT_HOST(row >= col); + } + + int col; + int row; +}; + +// The MatrixCoordPair contains a MATRIX_COORD and its priority. +typedef tesseract::KDPairInc MatrixCoordPair; + +#endif // TESSERACT_CCSTRUCT_MATRIX_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.cpp new file mode 100644 index 0000000..4e5f4bd --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.cpp @@ -0,0 +1,88 @@ +/********************************************************************** + * File: mod128.c (Formerly dir128.c) + * Description: Code to convert a DIR128 to an ICOORD. + * Author: Ray Smith + * Created: Tue Oct 22 11:56:09 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mod128.h" + +const inT16 idirtab[] = { + 1000, 0, 998, 49, 995, 98, 989, 146, + 980, 195, 970, 242, 956, 290, 941, 336, + 923, 382, 903, 427, 881, 471, 857, 514, + 831, 555, 803, 595, 773, 634, 740, 671, + 707, 707, 671, 740, 634, 773, 595, 803, + 555, 831, 514, 857, 471, 881, 427, 903, + 382, 923, 336, 941, 290, 956, 242, 970, + 195, 980, 146, 989, 98, 995, 49, 998, + 0, 1000, -49, 998, -98, 995, -146, 989, + -195, 980, -242, 970, -290, 956, -336, 941, + -382, 923, -427, 903, -471, 881, -514, 857, + -555, 831, -595, 803, -634, 773, -671, 740, + -707, 707, -740, 671, -773, 634, -803, 595, + -831, 555, -857, 514, -881, 471, -903, 427, + -923, 382, -941, 336, -956, 290, -970, 242, + -980, 195, -989, 146, -995, 98, -998, 49, + -1000, 0, -998, -49, -995, -98, -989, -146, + -980, -195, -970, -242, -956, -290, -941, -336, + -923, -382, -903, -427, -881, -471, -857, -514, + -831, -555, -803, -595, -773, -634, -740, -671, + -707, -707, -671, -740, -634, -773, -595, -803, + -555, -831, -514, -857, -471, -881, -427, -903, + -382, -923, -336, -941, -290, -956, -242, -970, + -195, -980, -146, -989, -98, -995, -49, -998, + 0, -1000, 49, -998, 98, -995, 146, -989, + 195, -980, 242, -970, 290, -956, 336, -941, + 382, -923, 427, -903, 471, -881, 514, -857, + 555, -831, 595, -803, 634, -773, 671, -740, + 707, -707, 740, -671, 773, -634, 803, -595, + 831, -555, 857, -514, 881, -471, 903, -427, + 923, -382, 941, -336, 956, -290, 970, -242, + 980, -195, 989, -146, 995, -98, 998, -49 +}; + +const ICOORD *dirtab = (ICOORD *) idirtab; + +/********************************************************************** + * DIR128::DIR128 + * + * Quantize the direction of an FCOORD to make a DIR128. + **********************************************************************/ + +DIR128::DIR128( //from fcoord + const FCOORD fc //vector to quantize + ) { + int high, low, current; //binary search + + low = 0; + if (fc.y () == 0) { + if (fc.x () >= 0) + dir = 0; + else + dir = MODULUS / 2; + return; + } + high = MODULUS; + do { + current = (high + low) / 2; + if (dirtab[current] * fc >= 0) + low = current; + else + high = current; + } + while (high - low > 1); + dir = low; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.h new file mode 100644 index 0000000..c0e71a4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/mod128.h @@ -0,0 +1,84 @@ +/********************************************************************** + * File: mod128.h (Formerly dir128.h) + * Description: Header for class which implements modulo arithmetic. + * Author: Ray Smith + * Created: Tue Mar 26 17:48:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MOD128_H +#define MOD128_H + +#include "points.h" + +#define MODULUS 128 /*range of directions */ +#define DIRBITS 7 //no of bits used +#define DIRSCALE 1000 //length of vector + +class DLLSYM DIR128 +{ + public: + DIR128() { + } //empty constructor + + DIR128( //constructor + inT16 value) { //value to assign + value %= MODULUS; //modulo arithmetic + if (value < 0) + value += MODULUS; //done properly + dir = (inT8) value; + } + DIR128(const FCOORD fc); //quantize vector + + DIR128 & operator= ( //assign of inT16 + inT16 value) { //value to assign + value %= MODULUS; //modulo arithmetic + if (value < 0) + value += MODULUS; //done properly + dir = (inT8) value; + return *this; + } + inT8 operator- ( //subtraction + const DIR128 & minus) const//for signed result + { + //result + inT16 result = dir - minus.dir; + + if (result > MODULUS / 2) + result -= MODULUS; //get in range + else if (result < -MODULUS / 2) + result += MODULUS; + return (inT8) result; + } + DIR128 operator+ ( //addition + const DIR128 & add) const //of itself + { + DIR128 result; //sum + + result = dir + add.dir; //let = do the work + return result; + } + DIR128 & operator+= ( //same as + + const DIR128 & add) { + *this = dir + add.dir; //let = do the work + return *this; + } + inT8 get_dir() const { //access function + return dir; + } + + private: + inT8 dir; //a direction +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.cpp new file mode 100644 index 0000000..ddf6dbf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.cpp @@ -0,0 +1,573 @@ +/********************************************************************** + * File: normalis.cpp (Formerly denorm.c) + * Description: Code for the DENORM class. + * Author: Ray Smith + * Created: Thu Apr 23 09:22:43 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "normalis.h" + +#include + +#include "allheaders.h" +#include "blobs.h" +#include "helpers.h" +#include "matrix.h" +#include "ocrblock.h" +#include "unicharset.h" +#include "werd.h" + +// Tolerance in pixels used for baseline and xheight on non-upper/lower scripts. +const int kSloppyTolerance = 4; +// Final tolerance in pixels added to the computed xheight range. +const float kFinalPixelTolerance = 0.125f; + +DENORM::DENORM() { + Init(); +} + +DENORM::DENORM(const DENORM &src) { + rotation_ = NULL; + *this = src; +} + + +DENORM & DENORM::operator=(const DENORM & src) { + Clear(); + inverse_ = src.inverse_; + predecessor_ = src.predecessor_; + pix_ = src.pix_; + block_ = src.block_; + if (src.rotation_ == NULL) + rotation_ = NULL; + else + rotation_ = new FCOORD(*src.rotation_); + x_origin_ = src.x_origin_; + y_origin_ = src.y_origin_; + x_scale_ = src.x_scale_; + y_scale_ = src.y_scale_; + final_xshift_ = src.final_xshift_; + final_yshift_ = src.final_yshift_; + return *this; +} + +DENORM::~DENORM() { + Clear(); +} + +// Initializes the denorm for a transformation. For details see the large +// comment in normalis.h. +// Arguments: +// block: if not NULL, then this is the first transformation, and +// block->re_rotation() needs to be used after the Denorm +// transformation to get back to the image coords. +// rotation: if not NULL, apply this rotation after translation to the +// origin and scaling. (Usually a classify rotation.) +// predecessor: if not NULL, then predecessor has been applied to the +// input space and needs to be undone to complete the inverse. +// The above pointers are not owned by this DENORM and are assumed to live +// longer than this denorm, except rotation, which is deep copied on input. +// +// x_origin: The x origin which will be mapped to final_xshift in the result. +// y_origin: The y origin which will be mapped to final_yshift in the result. +// Added to result of row->baseline(x) if not NULL. +// +// x_scale: scale factor for the x-coordinate. +// y_scale: scale factor for the y-coordinate. Ignored if segs is given. +// Note that these scale factors apply to the same x and y system as the +// x-origin and y-origin apply, ie after any block rotation, but before +// the rotation argument is applied. +// +// final_xshift: The x component of the final translation. +// final_yshift: The y component of the final translation. +void DENORM::SetupNormalization(const BLOCK* block, + const FCOORD* rotation, + const DENORM* predecessor, + float x_origin, float y_origin, + float x_scale, float y_scale, + float final_xshift, float final_yshift) { + Clear(); + block_ = block; + if (rotation == NULL) + rotation_ = NULL; + else + rotation_ = new FCOORD(*rotation); + predecessor_ = predecessor; + x_origin_ = x_origin; + y_origin_ = y_origin; + x_scale_ = x_scale; + y_scale_ = y_scale; + final_xshift_ = final_xshift; + final_yshift_ = final_yshift; +} + +// Helper for SetupNonLinear computes an image of shortest run-lengths from +// the x/y edges provided. +// Based on "A nonlinear normalization method for handprinted Kanji character +// recognition -- line density equalization" by Hiromitsu Yamada et al. +// Eg below is an O in a 1-pixel margin-ed bounding box and the corresponding +// ______________ input x_coords and y_coords. +// | _________ | +// | | _ | | 1, 6 +// | | | | | | 1, 3, 4, 6 +// | | | | | | 1, 3, 4, 6 +// | | | | | | 1, 3, 4, 6 +// | | |_| | | 1, 3, 4, 6 +// | |_________| | 1, 6 +// |_____________| +// E 1 1 1 1 1 E +// m 7 7 2 7 7 m +// p 6 p +// t 7 t +// y y +// The output image contains the min of the x and y run-length (distance +// between edges) at each coordinate in the image thus: +// ______________ +// |7 1_1_1_1_1 7| +// |1|5 5 1 5 5|1| +// |1|2 2|1|2 2|1| +// |1|2 2|1|2 2|1| +// |1|2 2|1|2 2|1| +// |1|2 2|1|2 2|1| +// |1|5_5_1_5_5|1| +// |7_1_1_1_1_1_7| +// Note that the input coords are all integer, so all partial pixels are dealt +// with elsewhere. Although it is nice for outlines to be properly connected +// and continuous, there is no requirement that they be as such, so they could +// have been derived from a flaky source, such as greyscale. +// This function works only within the provided box, and it is assumed that the +// input x_coords and y_coords have already been translated to have the bottom- +// left of box as the origin. Although an output, the minruns should have been +// pre-initialized to be the same size as box. Each element will contain the +// minimum of x and y run-length as shown above. +static void ComputeRunlengthImage( + const TBOX& box, + const GenericVector >& x_coords, + const GenericVector >& y_coords, + GENERIC_2D_ARRAY* minruns) { + int width = box.width(); + int height = box.height(); + ASSERT_HOST(minruns->dim1() == width); + ASSERT_HOST(minruns->dim2() == height); + // Set a 2-d image array to the run lengths at each pixel. + for (int ix = 0; ix < width; ++ix) { + int y = 0; + for (int i = 0; i < y_coords[ix].size(); ++i) { + int y_edge = ClipToRange(y_coords[ix][i], 0, height); + int gap = y_edge - y; + // Every pixel between the last and current edge get set to the gap. + while (y < y_edge) { + (*minruns)(ix, y) = gap; + ++y; + } + } + // Pretend there is a bounding box of edges all around the image. + int gap = height - y; + while (y < height) { + (*minruns)(ix, y) = gap; + ++y; + } + } + // Now set the image pixels the the MIN of the x and y runlengths. + for (int iy = 0; iy < height; ++iy) { + int x = 0; + for (int i = 0; i < x_coords[iy].size(); ++i) { + int x_edge = ClipToRange(x_coords[iy][i], 0, width); + int gap = x_edge - x; + while (x < x_edge) { + if (gap < (*minruns)(x, iy)) + (*minruns)(x, iy) = gap; + ++x; + } + } + int gap = width - x; + while (x < width) { + if (gap < (*minruns)(x, iy)) + (*minruns)(x, iy) = gap; + ++x; + } + } +} +// Converts the run-length image (see above to the edge density profiles used +// for scaling, thus: +// ______________ +// |7 1_1_1_1_1 7| = 5.28 +// |1|5 5 1 5 5|1| = 3.8 +// |1|2 2|1|2 2|1| = 5 +// |1|2 2|1|2 2|1| = 5 +// |1|2 2|1|2 2|1| = 5 +// |1|2 2|1|2 2|1| = 5 +// |1|5_5_1_5_5|1| = 3.8 +// |7_1_1_1_1_1_7| = 5.28 +// 6 4 4 8 4 4 6 +// . . . . . . . +// 2 4 4 0 4 4 2 +// 8 8 +// Each profile is the sum of the reciprocals of the pixels in the image in +// the appropriate row or column, and these are then normalized to sum to 1. +// On output hx, hy contain an extra element, which will eventually be used +// to guarantee that the top/right edge of the box (and anything beyond) always +// gets mapped to the maximum target coordinate. +static void ComputeEdgeDensityProfiles(const TBOX& box, + const GENERIC_2D_ARRAY& minruns, + GenericVector* hx, + GenericVector* hy) { + int width = box.width(); + int height = box.height(); + hx->init_to_size(width + 1, 0.0); + hy->init_to_size(height + 1, 0.0); + double total = 0.0; + for (int iy = 0; iy < height; ++iy) { + for (int ix = 0; ix < width; ++ix) { + int run = minruns(ix, iy); + if (run == 0) run = 1; + float density = 1.0f / run; + (*hx)[ix] += density; + (*hy)[iy] += density; + } + total += (*hy)[iy]; + } + // Normalize each profile to sum to 1. + if (total > 0.0) { + for (int ix = 0; ix < width; ++ix) { + (*hx)[ix] /= total; + } + for (int iy = 0; iy < height; ++iy) { + (*hy)[iy] /= total; + } + } + // There is an extra element in each array, so initialize to 1. + (*hx)[width] = 1.0f; + (*hy)[height] = 1.0f; +} + +// Sets up the DENORM to execute a non-linear transformation based on +// preserving an even distribution of stroke edges. The transformation +// operates only within the given box. +// x_coords is a collection of the x-coords of vertical edges for each +// y-coord starting at box.bottom(). +// y_coords is a collection of the y-coords of horizontal edges for each +// x-coord starting at box.left(). +// Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. +// Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. +// The second-level vectors must all be sorted in ascending order. +// See comments on the helper functions above for more details. +void DENORM::SetupNonLinear( + const DENORM* predecessor, const TBOX& box, float target_width, + float target_height, float final_xshift, float final_yshift, + const GenericVector >& x_coords, + const GenericVector >& y_coords) { + Clear(); + predecessor_ = predecessor; + // x_map_ and y_map_ store a mapping from input x and y coordinate to output + // x and y coordinate, based on scaling to the supplied target_width and + // target_height. + x_map_ = new GenericVector; + y_map_ = new GenericVector; + // Set a 2-d image array to the run lengths at each pixel. + int width = box.width(); + int height = box.height(); + GENERIC_2D_ARRAY minruns(width, height, 0); + ComputeRunlengthImage(box, x_coords, y_coords, &minruns); + // Edge density is the sum of the inverses of the run lengths. Compute + // edge density projection profiles. + ComputeEdgeDensityProfiles(box, minruns, x_map_, y_map_); + // Convert the edge density profiles to the coordinates by multiplying by + // the desired size and accumulating. + (*x_map_)[width] = target_width; + for (int x = width - 1; x >= 0; --x) { + (*x_map_)[x] = (*x_map_)[x + 1] - (*x_map_)[x] * target_width; + } + (*y_map_)[height] = target_height; + for (int y = height - 1; y >= 0; --y) { + (*y_map_)[y] = (*y_map_)[y + 1] - (*y_map_)[y] * target_height; + } + x_origin_ = box.left(); + y_origin_ = box.bottom(); + final_xshift_ = final_xshift; + final_yshift_ = final_yshift; +} + +// Transforms the given coords one step forward to normalized space, without +// using any block rotation or predecessor. +void DENORM::LocalNormTransform(const TPOINT& pt, TPOINT* transformed) const { + FCOORD src_pt(pt.x, pt.y); + FCOORD float_result; + LocalNormTransform(src_pt, &float_result); + transformed->x = IntCastRounded(float_result.x()); + transformed->y = IntCastRounded(float_result.y()); +} +void DENORM::LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const { + FCOORD translated(pt.x() - x_origin_, pt.y() - y_origin_); + if (x_map_ != NULL && y_map_ != NULL) { + int x = ClipToRange(IntCastRounded(translated.x()), 0, x_map_->size()-1); + translated.set_x((*x_map_)[x]); + int y = ClipToRange(IntCastRounded(translated.y()), 0, y_map_->size()-1); + translated.set_y((*y_map_)[y]); + } else { + translated.set_x(translated.x() * x_scale_); + translated.set_y(translated.y() * y_scale_); + if (rotation_ != NULL) + translated.rotate(*rotation_); + } + transformed->set_x(translated.x() + final_xshift_); + transformed->set_y(translated.y() + final_yshift_); +} + +// Transforms the given coords forward to normalized space using the +// full transformation sequence defined by the block rotation, the +// predecessors, deepest first, and finally this. If first_norm is not NULL, +// then the first and deepest transformation used is first_norm, ending +// with this, and the block rotation will not be applied. +void DENORM::NormTransform(const DENORM* first_norm, const TPOINT& pt, + TPOINT* transformed) const { + FCOORD src_pt(pt.x, pt.y); + FCOORD float_result; + NormTransform(first_norm, src_pt, &float_result); + transformed->x = IntCastRounded(float_result.x()); + transformed->y = IntCastRounded(float_result.y()); +} +void DENORM::NormTransform(const DENORM* first_norm, const FCOORD& pt, + FCOORD* transformed) const { + FCOORD src_pt(pt); + if (first_norm != this) { + if (predecessor_ != NULL) { + predecessor_->NormTransform(first_norm, pt, &src_pt); + } else if (block_ != NULL) { + FCOORD fwd_rotation(block_->re_rotation().x(), + -block_->re_rotation().y()); + src_pt.rotate(fwd_rotation); + } + } + LocalNormTransform(src_pt, transformed); +} + +// Transforms the given coords one step back to source space, without +// using to any block rotation or predecessor. +void DENORM::LocalDenormTransform(const TPOINT& pt, TPOINT* original) const { + FCOORD src_pt(pt.x, pt.y); + FCOORD float_result; + LocalDenormTransform(src_pt, &float_result); + original->x = IntCastRounded(float_result.x()); + original->y = IntCastRounded(float_result.y()); +} +void DENORM::LocalDenormTransform(const FCOORD& pt, FCOORD* original) const { + FCOORD rotated(pt.x() - final_xshift_, pt.y() - final_yshift_); + if (x_map_ != NULL && y_map_ != NULL) { + int x = x_map_->binary_search(rotated.x()); + original->set_x(x + x_origin_); + int y = y_map_->binary_search(rotated.y()); + original->set_y(y + y_origin_); + } else { + if (rotation_ != NULL) { + FCOORD inverse_rotation(rotation_->x(), -rotation_->y()); + rotated.rotate(inverse_rotation); + } + original->set_x(rotated.x() / x_scale_ + x_origin_); + float y_scale = y_scale_; + original->set_y(rotated.y() / y_scale + y_origin_); + } +} + +// Transforms the given coords all the way back to source image space using +// the full transformation sequence defined by this and its predecessors +// recursively, shallowest first, and finally any block re_rotation. +// If last_denorm is not NULL, then the last transformation used will +// be last_denorm, and the block re_rotation will never be executed. +void DENORM::DenormTransform(const DENORM* last_denorm, const TPOINT& pt, + TPOINT* original) const { + FCOORD src_pt(pt.x, pt.y); + FCOORD float_result; + DenormTransform(last_denorm, src_pt, &float_result); + original->x = IntCastRounded(float_result.x()); + original->y = IntCastRounded(float_result.y()); +} +void DENORM::DenormTransform(const DENORM* last_denorm, const FCOORD& pt, + FCOORD* original) const { + LocalDenormTransform(pt, original); + if (last_denorm != this) { + if (predecessor_ != NULL) { + predecessor_->DenormTransform(last_denorm, *original, original); + } else if (block_ != NULL) { + original->rotate(block_->re_rotation()); + } + } +} + +// Normalize a blob using blob transformations. Less accurate, but +// more accurately copies the old way. +void DENORM::LocalNormBlob(TBLOB* blob) const { + TBOX blob_box = blob->bounding_box(); + ICOORD translation(-IntCastRounded(x_origin_), -IntCastRounded(y_origin_)); + blob->Move(translation); + if (y_scale_ != 1.0f) + blob->Scale(y_scale_); + if (rotation_ != NULL) + blob->Rotate(*rotation_); + translation.set_x(IntCastRounded(final_xshift_)); + translation.set_y(IntCastRounded(final_yshift_)); + blob->Move(translation); +} + +// Fills in the x-height range accepted by the given unichar_id, given its +// bounding box in the usual baseline-normalized coordinates, with some +// initial crude x-height estimate (such as word size) and this denoting the +// transformation that was used. +void DENORM::XHeightRange(int unichar_id, const UNICHARSET& unicharset, + const TBOX& bbox, + float* min_xht, float* max_xht, float* yshift) const { + // Default return -- accept anything. + *yshift = 0.0f; + *min_xht = 0.0f; + *max_xht = MAX_FLOAT32; + + if (!unicharset.top_bottom_useful()) + return; + + // Clip the top and bottom to the limit of normalized feature space. + int top = ClipToRange(bbox.top(), 0, kBlnCellHeight - 1); + int bottom = ClipToRange(bbox.bottom(), 0, kBlnCellHeight - 1); + // A tolerance of yscale corresponds to 1 pixel in the image. + double tolerance = y_scale(); + // If the script doesn't have upper and lower-case characters, widen the + // tolerance to allow sloppy baseline/x-height estimates. + if (!unicharset.script_has_upper_lower()) + tolerance = y_scale() * kSloppyTolerance; + + int min_bottom, max_bottom, min_top, max_top; + unicharset.get_top_bottom(unichar_id, &min_bottom, &max_bottom, + &min_top, &max_top); + + // Calculate the scale factor we'll use to get to image y-pixels + double midx = (bbox.left() + bbox.right()) / 2.0; + double ydiff = (bbox.top() - bbox.bottom()) + 2.0; + FCOORD mid_bot(midx, bbox.bottom()), tmid_bot; + FCOORD mid_high(midx, bbox.bottom() + ydiff), tmid_high; + DenormTransform(NULL, mid_bot, &tmid_bot); + DenormTransform(NULL, mid_high, &tmid_high); + + // bln_y_measure * yscale = image_y_measure + double yscale = tmid_high.pt_to_pt_dist(tmid_bot) / ydiff; + + // Calculate y-shift + int bln_yshift = 0, bottom_shift = 0, top_shift = 0; + if (bottom < min_bottom - tolerance) { + bottom_shift = bottom - min_bottom; + } else if (bottom > max_bottom + tolerance) { + bottom_shift = bottom - max_bottom; + } + if (top < min_top - tolerance) { + top_shift = top - min_top; + } else if (top > max_top + tolerance) { + top_shift = top - max_top; + } + if ((top_shift >= 0 && bottom_shift > 0) || + (top_shift < 0 && bottom_shift < 0)) { + bln_yshift = (top_shift + bottom_shift) / 2; + } + *yshift = bln_yshift * yscale; + + // To help very high cap/xheight ratio fonts accept the correct x-height, + // and to allow the large caps in small caps to accept the xheight of the + // small caps, add kBlnBaselineOffset to chars with a maximum max, and have + // a top already at a significantly high position. + if (max_top == kBlnCellHeight - 1 && + top > kBlnCellHeight - kBlnBaselineOffset / 2) + max_top += kBlnBaselineOffset; + top -= bln_yshift; + int height = top - kBlnBaselineOffset; + double min_height = min_top - kBlnBaselineOffset - tolerance; + double max_height = max_top - kBlnBaselineOffset + tolerance; + + // We shouldn't try calculations if the characters are very short (for example + // for punctuation). + if (min_height > kBlnXHeight / 8 && height > 0) { + float result = height * kBlnXHeight * yscale / min_height; + *max_xht = result + kFinalPixelTolerance; + result = height * kBlnXHeight * yscale / max_height; + *min_xht = result - kFinalPixelTolerance; + } +} + +// Prints the content of the DENORM for debug purposes. +void DENORM::Print() const { + if (pix_ != NULL) { + tprintf("Pix dimensions %d x %d x %d\n", + pixGetWidth(pix_), pixGetHeight(pix_), pixGetDepth(pix_)); + } + if (inverse_) + tprintf("Inverse\n"); + if (block_ && block_->re_rotation().x() != 1.0f) { + tprintf("Block rotation %g, %g\n", + block_->re_rotation().x(), block_->re_rotation().y()); + } + tprintf("Input Origin = (%g, %g)\n", x_origin_, y_origin_); + if (x_map_ != NULL && y_map_ != NULL) { + tprintf("x map:\n"); + for (int x = 0; x < x_map_->size(); ++x) { + tprintf("%g ", (*x_map_)[x]); + } + tprintf("\ny map:\n"); + for (int y = 0; y < y_map_->size(); ++y) { + tprintf("%g ", (*y_map_)[y]); + } + tprintf("\n"); + } else { + tprintf("Scale = (%g, %g)\n", x_scale_, y_scale_); + if (rotation_ != NULL) + tprintf("Rotation = (%g, %g)\n", rotation_->x(), rotation_->y()); + } + tprintf("Final Origin = (%g, %g)\n", final_xshift_, final_xshift_); + if (predecessor_ != NULL) { + tprintf("Predecessor:\n"); + predecessor_->Print(); + } +} + + +// ============== Private Code ====================== + +// Free allocated memory and clear pointers. +void DENORM::Clear() { + if (x_map_ != NULL) { + delete x_map_; + x_map_ = NULL; + } + if (y_map_ != NULL) { + delete y_map_; + y_map_ = NULL; + } + if (rotation_ != NULL) { + delete rotation_; + rotation_ = NULL; + } +} + +// Setup default values. +void DENORM::Init() { + inverse_ = false; + pix_ = NULL; + block_ = NULL; + rotation_ = NULL; + predecessor_ = NULL; + x_map_ = NULL; + y_map_ = NULL; + x_origin_ = 0.0f; + y_origin_ = 0.0f; + x_scale_ = 1.0f; + y_scale_ = 1.0f; + final_xshift_ = 0.0f; + final_yshift_ = static_cast(kBlnBaselineOffset); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.h new file mode 100644 index 0000000..2d75412 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/normalis.h @@ -0,0 +1,317 @@ +/********************************************************************** + * File: normalis.h (Formerly denorm.h) + * Description: Code for the DENORM class. + * Author: Ray Smith + * Created: Thu Apr 23 09:22:43 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef NORMALIS_H +#define NORMALIS_H + +#include +#include "genericvector.h" +#include "host.h" + +const int kBlnCellHeight = 256; // Full-height for baseline normalization. +const int kBlnXHeight = 128; // x-height for baseline normalization. +const int kBlnBaselineOffset = 64; // offset for baseline normalization. + +struct Pix; +class ROW; // Forward decl +class BLOCK; +class FCOORD; +struct TBLOB; +class TBOX; +struct TPOINT; +class UNICHARSET; + +namespace tesseract { + +// Possible normalization methods. Use NEGATIVE values as these also +// double up as markers for the last sub-classifier. +enum NormalizationMode { + NM_BASELINE = -3, // The original BL normalization mode. + NM_CHAR_ISOTROPIC = -2, // Character normalization but isotropic. + NM_CHAR_ANISOTROPIC = -1 // The original CN normalization mode. +}; + +} // namespace tesseract. + +class DENORM { + public: + DENORM(); + + // Copying a DENORM is allowed. + DENORM(const DENORM &); + DENORM& operator=(const DENORM&); + ~DENORM(); + + // Setup the normalization transformation parameters. + // The normalizations applied to a blob are as follows: + // 1. An optional block layout rotation that was applied during layout + // analysis to make the textlines horizontal. + // 2. A normalization transformation (LocalNormTransform): + // Subtract the "origin" + // Apply an x,y scaling. + // Apply an optional rotation. + // Add back a final translation. + // The origin is in the block-rotated space, and is usually something like + // the x-middle of the word at the baseline. + // 3. Zero or more further normalization transformations that are applied + // in sequence, with a similar pattern to the first normalization transform. + // + // A DENORM holds the parameters of a single normalization, and can execute + // both the LocalNormTransform (a forwards normalization), and the + // LocalDenormTransform which is an inverse transform or de-normalization. + // A DENORM may point to a predecessor DENORM, which is actually the earlier + // normalization, so the full normalization sequence involves executing all + // predecessors first and then the transform in "this". + // Let x be image co-ordinates and that we have normalization classes A, B, C + // where we first apply A then B then C to get normalized x': + // x' = CBAx + // Then the backwards (to original coordinates) would be: + // x = A^-1 B^-1 C^-1 x' + // and A = B->predecessor_ and B = C->predecessor_ + // NormTransform executes all predecessors recursively, and then this. + // NormTransform would be used to transform an image-based feature to + // normalized space for use in a classifier + // DenormTransform inverts this and then all predecessors. It can be + // used to get back to the original image coordinates from normalized space. + // The LocalNormTransform member executes just the transformation + // in "this" without the layout rotation or any predecessors. It would be + // used to run each successive normalization, eg the word normalization, + // and later the character normalization. + + // Arguments: + // block: if not NULL, then this is the first transformation, and + // block->re_rotation() needs to be used after the Denorm + // transformation to get back to the image coords. + // rotation: if not NULL, apply this rotation after translation to the + // origin and scaling. (Usually a classify rotation.) + // predecessor: if not NULL, then predecessor has been applied to the + // input space and needs to be undone to complete the inverse. + // The above pointers are not owned by this DENORM and are assumed to live + // longer than this denorm, except rotation, which is deep copied on input. + // + // x_origin: The x origin which will be mapped to final_xshift in the result. + // y_origin: The y origin which will be mapped to final_yshift in the result. + // Added to result of row->baseline(x) if not NULL. + // + // x_scale: scale factor for the x-coordinate. + // y_scale: scale factor for the y-coordinate. Ignored if segs is given. + // Note that these scale factors apply to the same x and y system as the + // x-origin and y-origin apply, ie after any block rotation, but before + // the rotation argument is applied. + // + // final_xshift: The x component of the final translation. + // final_yshift: The y component of the final translation. + // + // In theory, any of the commonly used normalizations can be setup here: + // * Traditional baseline normalization on a word: + // SetupNormalization(block, NULL, NULL, + // box.x_middle(), baseline, + // kBlnXHeight / x_height, kBlnXHeight / x_height, + // 0, kBlnBaselineOffset); + // * "Numeric mode" baseline normalization on a word, in which the blobs + // are positioned with the bottom as the baseline is achieved by making + // a separate DENORM for each blob. + // SetupNormalization(block, NULL, NULL, + // box.x_middle(), box.bottom(), + // kBlnXHeight / x_height, kBlnXHeight / x_height, + // 0, kBlnBaselineOffset); + // * Anisotropic character normalization used by IntFx. + // SetupNormalization(NULL, NULL, denorm, + // centroid_x, centroid_y, + // 51.2 / ry, 51.2 / rx, 128, 128); + // * Normalize blob height to x-height (current OSD): + // SetupNormalization(NULL, &rotation, NULL, + // box.rotational_x_middle(rotation), + // box.rotational_y_middle(rotation), + // kBlnXHeight / box.rotational_height(rotation), + // kBlnXHeight / box.rotational_height(rotation), + // 0, kBlnBaselineOffset); + // * Secondary normalization for classification rotation (current): + // FCOORD rotation = block->classify_rotation(); + // float target_height = kBlnXHeight / CCStruct::kXHeightCapRatio; + // SetupNormalization(NULL, &rotation, denorm, + // box.rotational_x_middle(rotation), + // box.rotational_y_middle(rotation), + // target_height / box.rotational_height(rotation), + // target_height / box.rotational_height(rotation), + // 0, kBlnBaselineOffset); + // * Proposed new normalizations for CJK: Between them there is then + // no need for further normalization at all, and the character fills the cell. + // ** Replacement for baseline normalization on a word: + // Scales height and width independently so that modal height and pitch + // fill the cell respectively. + // float cap_height = x_height / CCStruct::kXHeightCapRatio; + // SetupNormalization(block, NULL, NULL, + // box.x_middle(), cap_height / 2.0f, + // kBlnCellHeight / fixed_pitch, + // kBlnCellHeight / cap_height, + // 0, 0); + // ** Secondary normalization for classification (with rotation) (proposed): + // Requires a simple translation to the center of the appropriate character + // cell, no further scaling and a simple rotation (or nothing) about the + // cell center. + // FCOORD rotation = block->classify_rotation(); + // SetupNormalization(NULL, &rotation, denorm, + // fixed_pitch_cell_center, + // 0.0f, + // 1.0f, + // 1.0f, + // 0, 0); + void SetupNormalization(const BLOCK* block, + const FCOORD* rotation, + const DENORM* predecessor, + float x_origin, float y_origin, + float x_scale, float y_scale, + float final_xshift, float final_yshift); + + // Sets up the DENORM to execute a non-linear transformation based on + // preserving an even distribution of stroke edges. The transformation + // operates only within the given box, scaling input coords within the box + // non-linearly to a box of target_width by target_height, with all other + // coords being clipped to the box edge. As with SetupNormalization above, + // final_xshift and final_yshift are applied after scaling, and the bottom- + // left of box is used as a pre-scaling origin. + // x_coords is a collection of the x-coords of vertical edges for each + // y-coord starting at box.bottom(). + // y_coords is a collection of the y-coords of horizontal edges for each + // x-coord starting at box.left(). + // Eg x_coords[0] is a collection of the x-coords of edges at y=bottom. + // Eg x_coords[1] is a collection of the x-coords of edges at y=bottom + 1. + // The second-level vectors must all be sorted in ascending order. + void SetupNonLinear(const DENORM* predecessor, const TBOX& box, + float target_width, float target_height, + float final_xshift, float final_yshift, + const GenericVector >& x_coords, + const GenericVector >& y_coords); + + // Transforms the given coords one step forward to normalized space, without + // using any block rotation or predecessor. + void LocalNormTransform(const TPOINT& pt, TPOINT* transformed) const; + void LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const; + // Transforms the given coords forward to normalized space using the + // full transformation sequence defined by the block rotation, the + // predecessors, deepest first, and finally this. If first_norm is not NULL, + // then the first and deepest transformation used is first_norm, ending + // with this, and the block rotation will not be applied. + void NormTransform(const DENORM* first_norm, const TPOINT& pt, + TPOINT* transformed) const; + void NormTransform(const DENORM* first_norm, const FCOORD& pt, + FCOORD* transformed) const; + // Transforms the given coords one step back to source space, without + // using to any block rotation or predecessor. + void LocalDenormTransform(const TPOINT& pt, TPOINT* original) const; + void LocalDenormTransform(const FCOORD& pt, FCOORD* original) const; + // Transforms the given coords all the way back to source image space using + // the full transformation sequence defined by this and its predecessors + // recursively, shallowest first, and finally any block re_rotation. + // If last_denorm is not NULL, then the last transformation used will + // be last_denorm, and the block re_rotation will never be executed. + void DenormTransform(const DENORM* last_denorm, const TPOINT& pt, + TPOINT* original) const; + void DenormTransform(const DENORM* last_denorm, const FCOORD& pt, + FCOORD* original) const; + + // Normalize a blob using blob transformations. Less accurate, but + // more accurately copies the old way. + void LocalNormBlob(TBLOB* blob) const; + + // Fills in the x-height range accepted by the given unichar_id in blob + // coordinates, given its bounding box in the usual baseline-normalized + // coordinates, with some initial crude x-height estimate (such as word + // size) and this denoting the transformation that was used. + // Also returns the amount the character must have shifted up or down. + void XHeightRange(int unichar_id, const UNICHARSET& unicharset, + const TBOX& bbox, + float* min_xht, + float* max_xht, + float* yshift) const; + + // Prints the content of the DENORM for debug purposes. + void Print() const; + + Pix* pix() const { + return pix_; + } + void set_pix(Pix* pix) { + pix_ = pix; + } + bool inverse() const { + return inverse_; + } + void set_inverse(bool value) { + inverse_ = value; + } + const DENORM* RootDenorm() const { + if (predecessor_ != NULL) + return predecessor_->RootDenorm(); + return this; + } + const DENORM* predecessor() const { + return predecessor_; + } + // Accessors - perhaps should not be needed. + float x_scale() const { + return x_scale_; + } + float y_scale() const { + return y_scale_; + } + const BLOCK* block() const { + return block_; + } + void set_block(const BLOCK* block) { + block_ = block; + } + + private: + // Free allocated memory and clear pointers. + void Clear(); + // Setup default values. + void Init(); + + // Best available image. + Pix* pix_; + // True if the source image is white-on-black. + bool inverse_; + // Block the word came from. If not null, block->re_rotation() takes the + // "untransformed" coordinates even further back to the original image. + // Used only on the first DENORM in a chain. + const BLOCK* block_; + // Rotation to apply between translation to the origin and scaling. + const FCOORD* rotation_; + // Previous transformation in a chain. + const DENORM* predecessor_; + // Non-linear transformation maps directly from each integer offset from the + // origin to the corresponding x-coord. Owned by the DENORM. + GenericVector* x_map_; + // Non-linear transformation maps directly from each integer offset from the + // origin to the corresponding y-coord. Owned by the DENORM. + GenericVector* y_map_; + // x-coordinate to be mapped to final_xshift_ in the result. + float x_origin_; + // y-coordinate to be mapped to final_yshift_ in the result. + float y_origin_; + // Scale factors for x and y coords. Applied to pre-rotation system. + float x_scale_; + float y_scale_; + // Destination coords of the x_origin_ and y_origin_. + float final_xshift_; + float final_yshift_; +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.cpp new file mode 100644 index 0000000..ad7893b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.cpp @@ -0,0 +1,519 @@ +/********************************************************************** + * File: ocrblock.cpp (Formerly block.c) + * Description: BLOCK member functions and iterator functions. + * Author: Ray Smith + * Created: Fri Mar 15 09:41:28 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include "blckerr.h" +#include "ocrblock.h" +#include "stepblob.h" +#include "tprintf.h" + +#define BLOCK_LABEL_HEIGHT 150 //char height of block id + +ELISTIZE (BLOCK) +/** + * BLOCK::BLOCK + * + * Constructor for a simple rectangular block. + */ +BLOCK::BLOCK(const char *name, //< filename + BOOL8 prop, //< proportional + inT16 kern, //< kerning + inT16 space, //< spacing + inT16 xmin, //< bottom left + inT16 ymin, inT16 xmax, //< top right + inT16 ymax) + : PDBLK (xmin, ymin, xmax, ymax), + filename(name), + re_rotation_(1.0f, 0.0f), + classify_rotation_(1.0f, 0.0f), + skew_(1.0f, 0.0f) { + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + proportional = prop; + right_to_left_ = false; + kerning = kern; + spacing = space; + font_class = -1; //not assigned + cell_over_xheight_ = 2.0f; + hand_poly = NULL; + left_it.set_to_list (&leftside); + right_it.set_to_list (&rightside); + //make default box + left_it.add_to_end (new ICOORDELT (xmin, ymin)); + left_it.add_to_end (new ICOORDELT (xmin, ymax)); + right_it.add_to_end (new ICOORDELT (xmax, ymin)); + right_it.add_to_end (new ICOORDELT (xmax, ymax)); +} + +/** + * decreasing_top_order + * + * Sort Comparator: Return <0 if row1 top < row2 top + */ + +int decreasing_top_order( // + const void *row1, + const void *row2) { + return (*(ROW **) row2)->bounding_box ().top () - + (*(ROW **) row1)->bounding_box ().top (); +} + + +/** + * BLOCK::rotate + * + * Rotate the polygon by the given rotation and recompute the bounding_box. + */ +void BLOCK::rotate(const FCOORD& rotation) { + poly_block()->rotate(rotation); + box = *poly_block()->bounding_box(); +} + +// Returns the bounding box including the desired combination of upper and +// lower noise/diacritic elements. +TBOX BLOCK::restricted_bounding_box(bool upper_dots, bool lower_dots) const { + TBOX box; + // This is a read-only iteration of the rows in the block. + ROW_IT it(const_cast(&rows)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + box += it.data()->restricted_bounding_box(upper_dots, lower_dots); + } + return box; +} + +/** + * BLOCK::reflect_polygon_in_y_axis + * + * Reflects the polygon in the y-axis and recompute the bounding_box. + * Does nothing to any contained rows/words/blobs etc. + */ +void BLOCK::reflect_polygon_in_y_axis() { + poly_block()->reflect_in_y_axis(); + box = *poly_block()->bounding_box(); +} + +/** + * BLOCK::sort_rows + * + * Order rows so that they are in order of decreasing Y coordinate + */ + +void BLOCK::sort_rows() { // order on "top" + ROW_IT row_it(&rows); + + row_it.sort (decreasing_top_order); +} + + +/** + * BLOCK::compress + * + * Delete space between the rows. (And maybe one day, compress the rows) + * Fill space of block from top down, left aligning rows. + */ + +void BLOCK::compress() { // squash it up + #define ROW_SPACING 5 + + ROW_IT row_it(&rows); + ROW *row; + ICOORD row_spacing (0, ROW_SPACING); + + ICOORDELT_IT icoordelt_it; + + sort_rows(); + + box = TBOX (box.topleft (), box.topleft ()); + box.move_bottom_edge (ROW_SPACING); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row->move (box.botleft () - row_spacing - + row->bounding_box ().topleft ()); + box += row->bounding_box (); + } + + leftside.clear (); + icoordelt_it.set_to_list (&leftside); + icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.bottom ())); + icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.top ())); + rightside.clear (); + icoordelt_it.set_to_list (&rightside); + icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.bottom ())); + icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.top ())); +} + + +/** + * BLOCK::check_pitch + * + * Check whether the block is fixed or prop, set the flag, and set + * the pitch if it is fixed. + */ + +void BLOCK::check_pitch() { // check prop + // tprintf("Missing FFT fixed pitch stuff!\n"); + pitch = -1; +} + + +/** + * BLOCK::compress + * + * Compress and move in a single operation. + */ + +void BLOCK::compress( // squash it up + const ICOORD vec // and move + ) { + box.move (vec); + compress(); +} + + +/** + * BLOCK::print + * + * Print the info on a block + */ + +void BLOCK::print( //print list of sides + FILE *, //< file to print on + BOOL8 dump //< print full detail + ) { + ICOORDELT_IT it = &leftside; //iterator + + box.print (); + tprintf ("Proportional= %s\n", proportional ? "TRUE" : "FALSE"); + tprintf ("Kerning= %d\n", kerning); + tprintf ("Spacing= %d\n", spacing); + tprintf ("Fixed_pitch=%d\n", pitch); + tprintf ("Filename= %s\n", filename.string ()); + + if (dump) { + tprintf ("Left side coords are:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); + tprintf ("\n"); + tprintf ("Right side coords are:\n"); + it.set_to_list (&rightside); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); + tprintf ("\n"); + } +} + +/** + * BLOCK::operator= + * + * Assignment - duplicate the block structure, but with an EMPTY row list. + */ + +BLOCK & BLOCK::operator= ( //assignment +const BLOCK & source //from this +) { + this->ELIST_LINK::operator= (source); + this->PDBLK::operator= (source); + proportional = source.proportional; + kerning = source.kerning; + spacing = source.spacing; + filename = source.filename; //STRINGs assign ok + if (!rows.empty ()) + rows.clear (); + re_rotation_ = source.re_rotation_; + classify_rotation_ = source.classify_rotation_; + skew_ = source.skew_; + return *this; +} + +// This function is for finding the approximate (horizontal) distance from +// the x-coordinate of the left edge of a symbol to the left edge of the +// text block which contains it. We are passed: +// segments - output of PB_LINE_IT::get_line() which contains x-coordinate +// intervals for the scan line going through the symbol's y-coordinate. +// Each element of segments is of the form (x()=start_x, y()=length). +// x - the x coordinate of the symbol we're interested in. +// margin - return value, the distance from x,y to the left margin of the +// block containing it. +// If all segments were to the right of x, we return false and 0. +bool LeftMargin(ICOORDELT_LIST *segments, int x, int *margin) { + bool found = false; + *margin = 0; + if (segments->empty()) + return found; + ICOORDELT_IT seg_it(segments); + for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) { + int cur_margin = x - seg_it.data()->x(); + if (cur_margin >= 0) { + if (!found) { + *margin = cur_margin; + } else if (cur_margin < *margin) { + *margin = cur_margin; + } + found = true; + } + } + return found; +} + +// This function is for finding the approximate (horizontal) distance from +// the x-coordinate of the right edge of a symbol to the right edge of the +// text block which contains it. We are passed: +// segments - output of PB_LINE_IT::get_line() which contains x-coordinate +// intervals for the scan line going through the symbol's y-coordinate. +// Each element of segments is of the form (x()=start_x, y()=length). +// x - the x coordinate of the symbol we're interested in. +// margin - return value, the distance from x,y to the right margin of the +// block containing it. +// If all segments were to the left of x, we return false and 0. +bool RightMargin(ICOORDELT_LIST *segments, int x, int *margin) { + bool found = false; + *margin = 0; + if (segments->empty()) + return found; + ICOORDELT_IT seg_it(segments); + for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) { + int cur_margin = seg_it.data()->x() + seg_it.data()->y() - x; + if (cur_margin >= 0) { + if (!found) { + *margin = cur_margin; + } else if (cur_margin < *margin) { + *margin = cur_margin; + } + found = true; + } + } + return found; +} + +// Compute the distance from the left and right ends of each row to the +// left and right edges of the block's polyblock. Illustration: +// ____________________________ _______________________ +// | Howdy neighbor! | |rectangular blocks look| +// | This text is written to| |more like stacked pizza| +// |illustrate how useful poly- |boxes. | +// |blobs are in ----------- ------ The polyblob| +// |dealing with| _________ |for a BLOCK rec-| +// |harder layout| /===========\ |ords the possibly| +// |issues. | | _ _ | |skewed pseudo-| +// | You see this| | |_| \|_| | |rectangular | +// |text is flowed| | } | |boundary that| +// |around a mid-| \ ____ | |forms the ideal-| +// |cloumn portrait._____ \ / __|ized text margin| +// | Polyblobs exist| \ / |from which we should| +// |to account for insets| | | |measure paragraph| +// |which make otherwise| ----- |indentation. | +// ----------------------- ---------------------- +// +// If we identify a drop-cap, we measure the left margin for the lines +// below the first line relative to one space past the drop cap. The +// first line's margin and those past the drop cap area are measured +// relative to the enclosing polyblock. +// +// TODO(rays): Before this will work well, we'll need to adjust the +// polyblob tighter around the text near images, as in: +// UNLV_AUTO:mag.3G0 page 2 +// UNLV_AUTO:mag.3G4 page 16 +void BLOCK::compute_row_margins() { + if (row_list()->empty() || row_list()->singleton()) { + return; + } + + // If Layout analysis was not called, default to this. + POLY_BLOCK rect_block(bounding_box(), PT_FLOWING_TEXT); + POLY_BLOCK *pblock = &rect_block; + if (poly_block() != NULL) { + pblock = poly_block(); + } + + // Step One: Determine if there is a drop-cap. + // TODO(eger): Fix up drop cap code for RTL languages. + ROW_IT r_it(row_list()); + ROW *first_row = r_it.data(); + ROW *second_row = r_it.data_relative(1); + + // initialize the bottom of a fictitious drop cap far above the first line. + int drop_cap_bottom = first_row->bounding_box().top() + + first_row->bounding_box().height(); + int drop_cap_right = first_row->bounding_box().left(); + int mid_second_line = second_row->bounding_box().top() - + second_row->bounding_box().height() / 2; + WERD_IT werd_it(r_it.data()->word_list()); // words of line one + if (!werd_it.empty()) { + C_BLOB_IT cblob_it(werd_it.data()->cblob_list()); + for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); + cblob_it.forward()) { + TBOX bbox = cblob_it.data()->bounding_box(); + if (bbox.bottom() <= mid_second_line) { + // we found a real drop cap + first_row->set_has_drop_cap(true); + if (drop_cap_bottom > bbox.bottom()) + drop_cap_bottom = bbox.bottom(); + if (drop_cap_right < bbox.right()) + drop_cap_right = bbox.right(); + } + } + } + + // Step Two: Calculate the margin from the text of each row to the block + // (or drop-cap) boundaries. + PB_LINE_IT lines(pblock); + r_it.set_to_list(row_list()); + for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) { + ROW *row = r_it.data(); + TBOX row_box = row->bounding_box(); + int left_y = row->base_line(row_box.left()) + row->x_height(); + int left_margin; + ICOORDELT_LIST *segments = lines.get_line(left_y); + LeftMargin(segments, row_box.left(), &left_margin); + delete segments; + + if (row_box.top() >= drop_cap_bottom) { + int drop_cap_distance = row_box.left() - row->space() - drop_cap_right; + if (drop_cap_distance < 0) + drop_cap_distance = 0; + if (drop_cap_distance < left_margin) + left_margin = drop_cap_distance; + } + + int right_y = row->base_line(row_box.right()) + row->x_height(); + int right_margin; + segments = lines.get_line(right_y); + RightMargin(segments, row_box.right(), &right_margin); + delete segments; + row->set_lmargin(left_margin); + row->set_rmargin(right_margin); + } +} + +/********************************************************************** + * PrintSegmentationStats + * + * Prints segmentation stats for the given block list. + **********************************************************************/ + +void PrintSegmentationStats(BLOCK_LIST* block_list) { + int num_blocks = 0; + int num_rows = 0; + int num_words = 0; + int num_blobs = 0; + BLOCK_IT block_it(block_list); + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { + BLOCK* block = block_it.data(); + ++num_blocks; + ROW_IT row_it(block->row_list()); + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + ++num_rows; + ROW* row = row_it.data(); + // Iterate over all werds in the row. + WERD_IT werd_it(row->word_list()); + for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { + WERD* werd = werd_it.data(); + ++num_words; + num_blobs += werd->cblob_list()->length(); + } + } + } + tprintf("Block list stats:\nBlocks = %d\nRows = %d\nWords = %d\nBlobs = %d\n", + num_blocks, num_rows, num_words, num_blobs); +} + +/********************************************************************** + * ExtractBlobsFromSegmentation + * + * Extracts blobs from the given block list and adds them to the output list. + * The block list must have been created by performing a page segmentation. + **********************************************************************/ + +void ExtractBlobsFromSegmentation(BLOCK_LIST* blocks, + C_BLOB_LIST* output_blob_list) { + C_BLOB_IT return_list_it(output_blob_list); + BLOCK_IT block_it(blocks); + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { + BLOCK* block = block_it.data(); + 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(); + // Iterate over all werds in the row. + WERD_IT werd_it(row->word_list()); + for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { + WERD* werd = werd_it.data(); + return_list_it.move_to_last(); + return_list_it.add_list_after(werd->cblob_list()); + return_list_it.move_to_last(); + return_list_it.add_list_after(werd->rej_cblob_list()); + } + } + } +} + +/********************************************************************** + * RefreshWordBlobsFromNewBlobs() + * + * Refreshes the words in the block_list by using blobs in the + * new_blobs list. + * Block list must have word segmentation in it. + * It consumes the blobs provided in the new_blobs list. The blobs leftover in + * the new_blobs list after the call weren't matched to any blobs of the words + * in block list. + * The output not_found_blobs is a list of blobs from the original segmentation + * in the block_list for which no corresponding new blobs were found. + **********************************************************************/ + +void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list, + C_BLOB_LIST* new_blobs, + C_BLOB_LIST* not_found_blobs) { + // Now iterate over all the blobs in the segmentation_block_list_, and just + // replace the corresponding c-blobs inside the werds. + BLOCK_IT block_it(block_list); + 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()) + continue; // Don't touch non-text blocks. + // 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(); + // Iterate over all werds in the row. + WERD_IT werd_it(row->word_list()); + WERD_LIST new_words; + WERD_IT new_words_it(&new_words); + for (werd_it.mark_cycle_pt(); !werd_it.cycled_list(); werd_it.forward()) { + WERD* werd = werd_it.extract(); + WERD* new_werd = werd->ConstructWerdWithNewBlobs(new_blobs, + not_found_blobs); + if (new_werd) { + // Insert this new werd into the actual row's werd-list. Remove the + // existing one. + new_words_it.add_after_then_move(new_werd); + delete werd; + } else { + // Reinsert the older word back, for lack of better options. + // This is critical since dropping the words messes up segmentation: + // eg. 1st word in the row might otherwise have W_FUZZY_NON turned on. + new_words_it.add_after_then_move(werd); + } + } + // Get rid of the old word list & replace it with the new one. + row->word_list()->clear(); + werd_it.move_to_first(); + werd_it.add_list_after(&new_words); + } + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.h new file mode 100644 index 0000000..c93aaf8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrblock.h @@ -0,0 +1,237 @@ +/********************************************************************** + * File: ocrblock.h (Formerly block.h) + * Description: Page block class definition. + * Author: Ray Smith + * Created: Thu Mar 14 17:32:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OCRBLOCK_H +#define OCRBLOCK_H + +#include "ocrpara.h" +#include "ocrrow.h" +#include "pdblock.h" + +class BLOCK; //forward decl + +ELISTIZEH (BLOCK) +class BLOCK:public ELIST_LINK, public PDBLK +//page block +{ + friend class BLOCK_RECT_IT; //block iterator + + public: + BLOCK() + : re_rotation_(1.0f, 0.0f), + classify_rotation_(1.0f, 0.0f), + skew_(1.0f, 0.0f) { + right_to_left_ = false; + hand_poly = NULL; + } + BLOCK(const char *name, //< filename + BOOL8 prop, //< proportional + inT16 kern, //< kerning + inT16 space, //< spacing + inT16 xmin, //< bottom left + inT16 ymin, + inT16 xmax, //< top right + inT16 ymax); + + ~BLOCK () { + } + + /** + * set space size etc. + * @param prop proportional + * @param kern inter char size + * @param space inter word size + * @param ch_pitch pitch if fixed + */ + void set_stats(BOOL8 prop, + inT16 kern, + inT16 space, + inT16 ch_pitch) { + proportional = prop; + kerning = (inT8) kern; + spacing = space; + pitch = ch_pitch; + } + /// set char size + void set_xheight(inT32 height) { + xheight = height; + } + /// set font class + void set_font_class(inT16 font) { + font_class = font; + } + /// return proportional + BOOL8 prop() const { + return proportional; + } + bool right_to_left() const { + return right_to_left_; + } + void set_right_to_left(bool value) { + right_to_left_ = value; + } + /// return pitch + inT32 fixed_pitch() const { + return pitch; + } + /// return kerning + inT16 kern() const { + return kerning; + } + /// return font class + inT16 font() const { + return font_class; + } + /// return spacing + inT16 space() const { + return spacing; + } + /// return filename + const char *name() const { + return filename.string (); + } + /// return xheight + inT32 x_height() const { + return xheight; + } + float cell_over_xheight() const { + return cell_over_xheight_; + } + void set_cell_over_xheight(float ratio) { + cell_over_xheight_ = ratio; + } + /// get rows + ROW_LIST *row_list() { + return &rows; + } + // Compute the margins between the edges of each row and this block's + // polyblock, and store the results in the rows. + void compute_row_margins(); + + // get paragraphs + PARA_LIST *para_list() { + return ¶s_; + } + /// get blobs + C_BLOB_LIST *blob_list() { + return &c_blobs; + } + C_BLOB_LIST *reject_blobs() { + return &rej_blobs; + } + FCOORD re_rotation() const { + return re_rotation_; // How to transform coords back to image. + } + void set_re_rotation(const FCOORD& rotation) { + re_rotation_ = rotation; + } + FCOORD classify_rotation() const { + return classify_rotation_; // Apply this before classifying. + } + void set_classify_rotation(const FCOORD& rotation) { + classify_rotation_ = rotation; + } + FCOORD skew() const { + return skew_; // Direction of true horizontal. + } + void set_skew(const FCOORD& skew) { + skew_ = skew; + } + const ICOORD& median_size() const { + return median_size_; + } + void set_median_size(int x, int y) { + median_size_.set_x(x); + median_size_.set_y(y); + } + + Pix* render_mask(TBOX* mask_box) { + return PDBLK::render_mask(re_rotation_, mask_box); + } + + // Returns the bounding box including the desired combination of upper and + // lower noise/diacritic elements. + TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; + + // Reflects the polygon in the y-axis and recomputes the bounding_box. + // Does nothing to any contained rows/words/blobs etc. + void reflect_polygon_in_y_axis(); + + void rotate(const FCOORD& rotation); + + /// decreasing y order + void sort_rows(); + + /// shrink white space + void compress(); + + /// check proportional + void check_pitch(); + + /// shrink white space and move by vector + void compress(const ICOORD vec); + + /// dump whole table + void print(FILE *fp, BOOL8 dump); + + BLOCK& operator=(const BLOCK & source); + + private: + BOOL8 proportional; //< proportional + bool right_to_left_; //< major script is right to left. + inT8 kerning; //< inter blob gap + inT16 spacing; //< inter word gap + inT16 pitch; //< pitch of non-props + inT16 font_class; //< correct font class + inT32 xheight; //< height of chars + float cell_over_xheight_; //< Ratio of cell height to xheight. + STRING filename; //< name of block + ROW_LIST rows; //< rows in block + PARA_LIST paras_; //< paragraphs of block + C_BLOB_LIST c_blobs; //< before textord + C_BLOB_LIST rej_blobs; //< duff stuff + FCOORD re_rotation_; //< How to transform coords back to image. + FCOORD classify_rotation_; //< Apply this before classifying. + FCOORD skew_; //< Direction of true horizontal. + ICOORD median_size_; //< Median size of blobs. +}; + +int decreasing_top_order(const void *row1, const void *row2); + +// A function to print segmentation stats for the given block list. +void PrintSegmentationStats(BLOCK_LIST* block_list); + +// Extracts blobs fromo the given block list and adds them to the output list. +// The block list must have been created by performing a page segmentation. +void ExtractBlobsFromSegmentation(BLOCK_LIST* blocks, + C_BLOB_LIST* output_blob_list); + +// Refreshes the words in the block_list by using blobs in the +// new_blobs list. +// Block list must have word segmentation in it. +// It consumes the blobs provided in the new_blobs list. The blobs leftover in +// the new_blobs list after the call weren't matched to any blobs of the words +// in block list. +// The output not_found_blobs is a list of blobs from the original segmentation +// in the block_list for which no corresponding new blobs were found. +void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list, + C_BLOB_LIST* new_blobs, + C_BLOB_LIST* not_found_blobs); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.cpp new file mode 100644 index 0000000..b244bfa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.cpp @@ -0,0 +1,100 @@ +///////////////////////////////////////////////////////////////////// +// File: ocrpara.h +// Description: OCR Paragraph Output Type +// Author: David Eger +// Created: 2010-11-15 +// +// (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 + +#include "ocrpara.h" +#include "host.h" // For NearlyEqual() + +ELISTIZE(PARA) + +using tesseract::JUSTIFICATION_LEFT; +using tesseract::JUSTIFICATION_RIGHT; +using tesseract::JUSTIFICATION_CENTER; +using tesseract::JUSTIFICATION_UNKNOWN; + +static STRING ParagraphJustificationToString( + tesseract::ParagraphJustification justification) { + switch (justification) { + case JUSTIFICATION_LEFT: + return "LEFT"; + case JUSTIFICATION_RIGHT: + return "RIGHT"; + case JUSTIFICATION_CENTER: + return "CENTER"; + default: + return "UNKNOWN"; + } +} + +bool ParagraphModel::ValidFirstLine(int lmargin, int lindent, + int rindent, int rmargin) const { + switch (justification_) { + case JUSTIFICATION_LEFT: + return NearlyEqual(lmargin + lindent, margin_ + first_indent_, + tolerance_); + case JUSTIFICATION_RIGHT: + return NearlyEqual(rmargin + rindent, margin_ + first_indent_, + tolerance_); + case JUSTIFICATION_CENTER: + return NearlyEqual(lindent, rindent, tolerance_ * 2); + default: + // shouldn't happen + return false; + } +} + +bool ParagraphModel::ValidBodyLine(int lmargin, int lindent, + int rindent, int rmargin) const { + switch (justification_) { + case JUSTIFICATION_LEFT: + return NearlyEqual(lmargin + lindent, margin_ + body_indent_, + tolerance_); + case JUSTIFICATION_RIGHT: + return NearlyEqual(rmargin + rindent, margin_ + body_indent_, + tolerance_); + case JUSTIFICATION_CENTER: + return NearlyEqual(lindent, rindent, tolerance_ * 2); + default: + // shouldn't happen + return false; + } +} + +bool ParagraphModel::Comparable(const ParagraphModel &other) const { + if (justification_ != other.justification_) + return false; + if (justification_ == JUSTIFICATION_CENTER || + justification_ == JUSTIFICATION_UNKNOWN) + return true; + int tolerance = (tolerance_ + other.tolerance_) / 4; + return NearlyEqual(margin_ + first_indent_, + other.margin_ + other.first_indent_, tolerance) && + NearlyEqual(margin_ + body_indent_, + other.margin_ + other.body_indent_, tolerance); +} + +STRING ParagraphModel::ToString() const { + char buffer[200]; + const STRING &alignment = ParagraphJustificationToString(justification_); + snprintf(buffer, sizeof(buffer), + "margin: %d, first_indent: %d, body_indent: %d, alignment: %s", + margin_, first_indent_, body_indent_, alignment.string()); + return STRING(buffer); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.h new file mode 100644 index 0000000..025e2bf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrpara.h @@ -0,0 +1,191 @@ +///////////////////////////////////////////////////////////////////// +// File: ocrpara.h +// Description: OCR Paragraph Output Type +// Author: David Eger +// Created: 2010-11-15 +// +// (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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCSTRUCT_OCRPARA_H_ +#define TESSERACT_CCSTRUCT_OCRPARA_H_ + +#include "publictypes.h" +#include "elst.h" +#include "strngs.h" + +class ParagraphModel; + +struct PARA : public ELIST_LINK { + public: + PARA() : model(NULL), is_list_item(false), + is_very_first_or_continuation(false), has_drop_cap(false) {} + + // We do not own the model, we just reference it. + // model may be NULL if there is not a good model for this paragraph. + const ParagraphModel *model; + + bool is_list_item; + + // The first paragraph on a page often lacks a first line indent, but should + // still be modeled by the same model as other body text paragraphs on the + // page. + bool is_very_first_or_continuation; + + // Does this paragraph begin with a drop cap? + bool has_drop_cap; +}; + +ELISTIZEH(PARA) + +// A geometric model of paragraph indentation and alignment. +// +// Measurements are in pixels. The meaning of the integer arguments changes +// depending upon the value of justification. Distances less than or equal +// to tolerance apart we take as "equivalent" for the purpose of model +// matching, and in the examples below, we assume tolerance is zero. +// +// justification = LEFT: +// margin the "ignored" margin to the left block edge. +// first_indent indent from the left margin to a typical first text line. +// body_indent indent from the left margin of a typical body text line. +// +// justification = RIGHT: +// margin the "ignored" margin to the right block edge. +// first_indent indent from the right margin to a typical first text line. +// body_indent indent from the right margin of a typical body text line. +// +// justification = CENTER: +// margin ignored +// first_indent ignored +// body_indent ignored +// +// ====== Extended example, assuming each letter is ten pixels wide: ======= +// +// +--------------------------------+ +// | Awesome | ParagraphModel(CENTER, 0, 0, 0) +// | Centered Title | +// | Paragraph Detection | +// | OCR TEAM | +// | 10 November 2010 | +// | | +// | Look here, I have a paragraph.| ParagraphModel(LEFT, 0, 20, 0) +// |This paragraph starts at the top| +// |of the page and takes 3 lines. | +// | Here I have a second paragraph| ParagraphModel(LEFT, 0, 20, 0) +// |which indicates that the first | +// |paragraph is not a continuation | +// |from a previous page, as it is | +// |indented just like this second | +// |paragraph. | +// | Here is a block quote. It | ParagraphModel(LEFT, 30, 0, 0) +// | looks like the prior text | +// | but it is indented more | +// | and is fully justified. | +// | So how does one deal with | ParagraphModel(LEFT, 0, 20, 0) +// |centered text, block quotes, | +// |normal paragraphs, and lists | +// |like what follows? | +// |1. Make a plan. | ParagraphModel(LEFT, 0, 0, 30) +// |2. Use a heuristic, for example,| ParagraphModel(LEFT, 0, 0, 30) +// | looking for lines where the | +// | first word of the next line | +// | would fit on the previous | +// | line. | +// |8. Try to implement the plan in | ParagraphModel(LEFT, 0, 0, 30) +// | Python and try it out. | +// |4. Determine how to fix the | ParagraphModel(LEFT, 0, 0, 30) +// | mistakes. | +// |5. Repeat. | ParagraphModel(LEFT, 0, 0, 30) +// | For extra painful penalty work| ParagraphModel(LEFT, 0, 20, 0) +// |you can try to identify source | +// |code. Ouch! | +// +--------------------------------+ +class ParagraphModel { + public: + ParagraphModel(tesseract::ParagraphJustification justification, + int margin, + int first_indent, + int body_indent, + int tolerance) + : justification_(justification), + margin_(margin), + first_indent_(first_indent), + body_indent_(body_indent), + tolerance_(tolerance) { + // Make one of {first_indent, body_indent} is 0. + int added_margin = first_indent; + if (body_indent < added_margin) + added_margin = body_indent; + margin_ += added_margin; + first_indent_ -= added_margin; + body_indent_ -= added_margin; + } + + ParagraphModel() + : justification_(tesseract::JUSTIFICATION_UNKNOWN), + margin_(0), + first_indent_(0), + body_indent_(0), + tolerance_(0) { } + + // ValidFirstLine() and ValidBodyLine() take arguments describing a text line + // in a block of text which we are trying to model: + // lmargin, lindent: these add up to the distance from the leftmost ink + // in the text line to the surrounding text block's left + // edge. + // rmargin, rindent: these add up to the distance from the rightmost ink + // in the text line to the surrounding text block's right + // edge. + // The caller determines the division between "margin" and "indent", which + // only actually affect whether we think the line may be centered. + // + // If the amount of whitespace matches the amount of whitespace expected on + // the relevant side of the line (within tolerance_) we say it matches. + + // Return whether a given text line could be a first paragraph line according + // to this paragraph model. + bool ValidFirstLine(int lmargin, int lindent, int rindent, int rmargin) const; + + // Return whether a given text line could be a first paragraph line according + // to this paragraph model. + bool ValidBodyLine(int lmargin, int lindent, int rindent, int rmargin) const; + + tesseract::ParagraphJustification justification() const { + return justification_; + } + int margin() const { return margin_; } + int first_indent() const { return first_indent_; } + int body_indent() const { return body_indent_; } + int tolerance() const { return tolerance_; } + bool is_flush() const { + return (justification_ == tesseract::JUSTIFICATION_LEFT || + justification_ == tesseract::JUSTIFICATION_RIGHT) && + abs(first_indent_ - body_indent_) <= tolerance_; + } + + // Return whether this model is likely to agree with the other model on most + // paragraphs they are marked. + bool Comparable(const ParagraphModel &other) const; + + STRING ToString() const; + + private: + tesseract::ParagraphJustification justification_; + int margin_; + int first_indent_; + int body_indent_; + int tolerance_; +}; + +#endif // TESSERACT_CCSTRUCT_OCRPARA_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.cpp new file mode 100644 index 0000000..c6f919c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.cpp @@ -0,0 +1,243 @@ +/********************************************************************** + * File: ocrrow.cpp (Formerly row.c) + * Description: Code for the ROW class. + * Author: Ray Smith + * Created: Tue Oct 08 15:58:04 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "ocrrow.h" +#include "blobbox.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +ELISTIZE (ROW) +/********************************************************************** + * ROW::ROW + * + * Constructor to build a ROW. Only the stats stuff are given here. + * The words are added directly. + **********************************************************************/ +ROW::ROW ( //constructor +inT32 spline_size, //no of segments +inT32 * xstarts, //segment boundaries +double *coeffs, //coefficients +float x_height, //line height +float ascenders, //ascender size +float descenders, //descender drop +inT16 kern, //char gap +inT16 space //word gap +) + : baseline(spline_size, xstarts, coeffs), + para_(NULL) { + kerning = kern; //just store stuff + spacing = space; + xheight = x_height; + ascrise = ascenders; + bodysize = 0.0f; + descdrop = descenders; + has_drop_cap_ = false; + lmargin_ = 0; + rmargin_ = 0; +} + + +/********************************************************************** + * ROW::ROW + * + * Constructor to build a ROW. Only the stats stuff are given here. + * The words are added directly. + **********************************************************************/ + +ROW::ROW( //constructor + TO_ROW *to_row, //source row + inT16 kern, //char gap + inT16 space //word gap + ) : para_(NULL) { + kerning = kern; //just store stuff + spacing = space; + xheight = to_row->xheight; + bodysize = to_row->body_size; + ascrise = to_row->ascrise; + descdrop = to_row->descdrop; + baseline = to_row->baseline; + has_drop_cap_ = false; + lmargin_ = 0; + rmargin_ = 0; +} + +// Returns the bounding box including the desired combination of upper and +// lower noise/diacritic elements. +TBOX ROW::restricted_bounding_box(bool upper_dots, bool lower_dots) const { + TBOX box; + // This is a read-only iteration of the words in the row. + WERD_IT it(const_cast(&words)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + box += it.data()->restricted_bounding_box(upper_dots, lower_dots); + } + return box; +} + +/********************************************************************** + * ROW::recalc_bounding_box + * + * Set the bounding box correctly + **********************************************************************/ + +void ROW::recalc_bounding_box() { //recalculate BB + WERD *word; //current word + WERD_IT it = &words; //words of ROW + inT16 left; //of word + inT16 prev_left; //old left + + if (!it.empty ()) { + word = it.data (); + prev_left = word->bounding_box ().left (); + it.forward (); + while (!it.at_first ()) { + word = it.data (); + left = word->bounding_box ().left (); + if (left < prev_left) { + it.move_to_first (); + //words in BB order + it.sort (word_comparator); + break; + } + prev_left = left; + it.forward (); + } + } + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + if (it.at_first ()) + word->set_flag (W_BOL, TRUE); + else + //not start of line + word->set_flag (W_BOL, FALSE); + if (it.at_last ()) + word->set_flag (W_EOL, TRUE); + else + //not end of line + word->set_flag (W_EOL, FALSE); + //extend BB as reqd + bound_box += word->bounding_box (); + } +} + + +/********************************************************************** + * ROW::move + * + * Reposition row by vector + **********************************************************************/ + +void ROW::move( // reposition row + const ICOORD vec // by vector + ) { + WERD_IT it(&words); // word iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); + + bound_box.move (vec); + baseline.move (vec); +} + + +/********************************************************************** + * ROW::print + * + * Display members + **********************************************************************/ + +void ROW::print( //print + FILE *fp //file to print on + ) { + tprintf("Kerning= %d\n", kerning); + tprintf("Spacing= %d\n", spacing); + bound_box.print(); + tprintf("Xheight= %f\n", xheight); + tprintf("Ascrise= %f\n", ascrise); + tprintf("Descdrop= %f\n", descdrop); + tprintf("has_drop_cap= %d\n", has_drop_cap_); + tprintf("lmargin= %d, rmargin= %d\n", lmargin_, rmargin_); +} + + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void ROW::plot( //draw it + ScrollView* window, //window to draw in + ScrollView::Color colour //colour to draw in + ) { + WERD *word; //current word + WERD_IT it = &words; //words of ROW + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + word->plot (window, colour); //all in one colour + } +} + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in rainbow colours. + **********************************************************************/ + +void ROW::plot( //draw it + ScrollView* window //window to draw in + ) { + WERD *word; //current word + WERD_IT it = &words; //words of ROW + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + word->plot (window); //in rainbow colours + } +} +#endif // GRAPHICS_DISABLED + +/********************************************************************** + * ROW::operator= + * + * Assign rows by duplicating the row structure but NOT the WERDLIST + **********************************************************************/ + +ROW & ROW::operator= (const ROW & source) { + this->ELIST_LINK::operator= (source); + kerning = source.kerning; + spacing = source.spacing; + xheight = source.xheight; + bodysize = source.bodysize; + ascrise = source.ascrise; + descdrop = source.descdrop; + if (!words.empty ()) + words.clear (); + baseline = source.baseline; //QSPLINES must do = + bound_box = source.bound_box; + has_drop_cap_ = source.has_drop_cap_; + lmargin_ = source.lmargin_; + rmargin_ = source.rmargin_; + para_ = source.para_; + return *this; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.h new file mode 100644 index 0000000..45384b7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ocrrow.h @@ -0,0 +1,165 @@ +/********************************************************************** + * File: ocrrow.h (Formerly row.h) + * Description: Code for the ROW class. + * Author: Ray Smith + * Created: Tue Oct 08 15:58:04 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OCRROW_H +#define OCRROW_H + +#include + +#include "quspline.h" +#include "werd.h" + +class TO_ROW; + +struct 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; + } + // Returns the bounding box including the desired combination of upper and + // lower noise/diacritic elements. + TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; + + 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 + + #ifndef GRAPHICS_DISABLED + void plot( //draw one + ScrollView* window, //window to draw in + ScrollView::Color colour); //uniform colour + void plot( //draw one + ScrollView* window); //in rainbow colours + + 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 // GRAPHICS_DISABLED + 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 diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.cpp new file mode 100644 index 0000000..8822ce2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.cpp @@ -0,0 +1,216 @@ +/********************************************************************** + * 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 "otsuthr.h" + +#include +#include "allheaders.h" +#include "helpers.h" +#include "openclwrapper.h" + + +namespace tesseract { + +// Computes 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. +// The return value is the number of channels in the input image, being +// the size of the output thresholds and hi_values arrays. +int OtsuThreshold(Pix* src_pix, int left, int top, int width, int height, + int** thresholds, int** hi_values) { + int num_channels = pixGetDepth(src_pix) / 8; + // Of all channels with no good hi_value, keep the best so we can always + // produce at least one answer. + PERF_COUNT_START("OtsuThreshold") + int best_hi_value = 1; + int best_hi_index = 0; + bool any_good_hivalue = false; + double best_hi_dist = 0.0; + *thresholds = new int[num_channels]; + *hi_values = new int[num_channels]; + + // only use opencl if compiled w/ OpenCL and selected device is opencl +#ifdef USE_OPENCL + // all of channel 0 then all of channel 1... + int* histogramAllChannels = new int[kHistogramSize * num_channels]; + + // Calculate Histogram on GPU + OpenclDevice od; + if (od.selectedDeviceIsOpenCL() && (num_channels == 1 || num_channels == 4) && + top == 0 && left == 0) { + od.HistogramRectOCL((unsigned char*)pixGetData(src_pix), num_channels, + pixGetWpl(src_pix) * 4, left, top, width, height, + kHistogramSize, histogramAllChannels); + + // Calculate Threshold from Histogram on cpu + for (int ch = 0; ch < num_channels; ++ch) { + (*thresholds)[ch] = -1; + (*hi_values)[ch] = -1; + int *histogram = &histogramAllChannels[kHistogramSize * ch]; + 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; + } + } + } + } else { +#endif + for (int ch = 0; ch < num_channels; ++ch) { + (*thresholds)[ch] = -1; + (*hi_values)[ch] = -1; + // Compute the histogram of the image rectangle. + int histogram[kHistogramSize]; + HistogramRect(src_pix, ch, 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; + } + } + } +#ifdef USE_OPENCL + } + delete[] histogramAllChannels; +#endif // USE_OPENCL + + if (!any_good_hivalue) { + // Use the best of the ones that were not good enough. + (*hi_values)[best_hi_index] = best_hi_value; + } + PERF_COUNT_END + return num_channels; +} + +// Computes the histogram for the given image rectangle, and the given +// single channel. Each channel is always one byte per pixel. +// Histogram is always a kHistogramSize(256) element array to count +// occurrences of each pixel value. +void HistogramRect(Pix* src_pix, int channel, + int left, int top, int width, int height, + int* histogram) { + PERF_COUNT_START("HistogramRect") + int num_channels = pixGetDepth(src_pix) / 8; + channel = ClipToRange(channel, 0, num_channels - 1); + int bottom = top + height; + memset(histogram, 0, sizeof(*histogram) * kHistogramSize); + int src_wpl = pixGetWpl(src_pix); + l_uint32* srcdata = pixGetData(src_pix); + for (int y = top; y < bottom; ++y) { + const l_uint32* linedata = srcdata + y * src_wpl; + for (int x = 0; x < width; ++x) { + int pixel = GET_DATA_BYTE(const_cast( + reinterpret_cast(linedata)), + (x + left) * num_channels + channel); + ++histogram[pixel]; + } + } + PERF_COUNT_END +} + +// Computes 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(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(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. diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.h new file mode 100644 index 0000000..7e7d281 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/otsuthr.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////// +// File: otsuthr.h +// Description: Simple Otsu thresholding for binarizing images. +// Author: Ray Smith +// Created: Fri Mar 07 12:14: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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCMAIN_OTSUTHR_H__ +#define TESSERACT_CCMAIN_OTSUTHR_H__ + +struct Pix; + +namespace tesseract { + +const int kHistogramSize = 256; // The size of a histogram of pixel values. + +// Computes 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. +// The return value is the number of channels in the input image, being +// the size of the output thresholds and hi_values arrays. +int OtsuThreshold(Pix* src_pix, int left, int top, int width, int height, + int** thresholds, int** hi_values); + +// Computes the histogram for the given image rectangle, and the given +// single channel. Each channel is always one byte per pixel. +// Histogram is always a kHistogramSize(256) element array to count +// occurrences of each pixel value. +void HistogramRect(Pix* src_pix, int channel, + int left, int top, int width, int height, + int* histogram); + +// Computes 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); + +} // namespace tesseract. + +#endif // TESSERACT_CCMAIN_OTSUTHR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.cpp new file mode 100644 index 0000000..3263536 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.cpp @@ -0,0 +1,1690 @@ +/********************************************************************** + * File: pageres.cpp (Formerly page_res.c) + * Description: Hierarchy of results classes from PAGE_RES to WERD_RES + * and an iterator class to iterate over the words. + * Main purposes: + * Easy way to iterate over the words without a 3-nested loop. + * Holds data used during word recognition. + * Holds information about alternative spacing paths. + * Author: Phil Cheatle + * Created: Tue Sep 22 08:42:49 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#include +#ifdef __UNIX__ +#include +#endif +#include "blamer.h" +#include "pageres.h" +#include "blobs.h" + +ELISTIZE (BLOCK_RES) +CLISTIZE (BLOCK_RES) ELISTIZE (ROW_RES) ELISTIZE (WERD_RES) + +// Gain factor for computing thresholds that determine the ambiguity of a word. +static const double kStopperAmbiguityThresholdGain = 8.0; +// Constant offset for computing thresholds that determine the ambiguity of a +// word. +static const double kStopperAmbiguityThresholdOffset = 1.5; +// Max number of broken pieces to associate. +const int kWordrecMaxNumJoinChunks = 4; +// Max ratio of word box height to line size to allow it to be processed as +// a line with other words. +const double kMaxWordSizeRatio = 1.25; +// Max ratio of line box height to line size to allow a new word to be added. +const double kMaxLineSizeRatio = 1.25; +// Max ratio of word gap to line size to allow a new word to be added. +const double kMaxWordGapRatio = 2.0; + +// Computes and returns a threshold of certainty difference used to determine +// which words to keep, based on the adjustment factors of the two words. +// TODO(rays) This is horrible. Replace with an enhance params training model. +static double StopperAmbigThreshold(double f1, double f2) { + return (f2 - f1) * kStopperAmbiguityThresholdGain - + kStopperAmbiguityThresholdOffset; +} + +/************************************************************************* + * PAGE_RES::PAGE_RES + * + * Constructor for page results + *************************************************************************/ +PAGE_RES::PAGE_RES( + bool merge_similar_words, + BLOCK_LIST *the_block_list, + WERD_CHOICE **prev_word_best_choice_ptr) { + Init(); + BLOCK_IT block_it(the_block_list); + BLOCK_RES_IT block_res_it(&block_res_list); + for (block_it.mark_cycle_pt(); + !block_it.cycled_list(); block_it.forward()) { + block_res_it.add_to_end(new BLOCK_RES(merge_similar_words, + block_it.data())); + } + prev_word_best_choice = prev_word_best_choice_ptr; +} + +/************************************************************************* + * BLOCK_RES::BLOCK_RES + * + * Constructor for BLOCK results + *************************************************************************/ + +BLOCK_RES::BLOCK_RES(bool merge_similar_words, BLOCK *the_block) { + ROW_IT row_it (the_block->row_list ()); + ROW_RES_IT row_res_it(&row_res_list); + + char_count = 0; + rej_count = 0; + font_class = -1; //not assigned + x_height = -1.0; + font_assigned = FALSE; + bold = FALSE; + italic = FALSE; + row_count = 0; + + block = the_block; + + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + row_res_it.add_to_end(new ROW_RES(merge_similar_words, row_it.data())); + } +} + +/************************************************************************* + * ROW_RES::ROW_RES + * + * Constructor for ROW results + *************************************************************************/ + +ROW_RES::ROW_RES(bool merge_similar_words, ROW *the_row) { + WERD_IT word_it(the_row->word_list()); + WERD_RES_IT word_res_it(&word_res_list); + WERD_RES *combo = NULL; // current combination of fuzzies + WERD *copy_word; + + char_count = 0; + rej_count = 0; + whole_word_rej_count = 0; + + row = the_row; + bool add_next_word = false; + TBOX union_box; + float line_height = the_row->x_height() + the_row->ascenders() - + the_row->descenders(); + for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { + WERD_RES* word_res = new WERD_RES(word_it.data()); + word_res->x_height = the_row->x_height(); + if (add_next_word) { + ASSERT_HOST(combo != NULL); + // We are adding this word to the combination. + word_res->part_of_combo = TRUE; + combo->copy_on(word_res); + } else if (merge_similar_words) { + union_box = word_res->word->bounding_box(); + add_next_word = !word_res->word->flag(W_REP_CHAR) && + union_box.height() <= line_height * kMaxWordSizeRatio; + word_res->odd_size = !add_next_word; + } + WERD* next_word = word_it.data_relative(1); + if (merge_similar_words) { + if (add_next_word && !next_word->flag(W_REP_CHAR)) { + // Next word will be added on if all of the following are true: + // Not a rep char. + // Box height small enough. + // Union box height small enough. + // Horizontal gap small enough. + TBOX next_box = next_word->bounding_box(); + int prev_right = union_box.right(); + union_box += next_box; + if (next_box.height() > line_height * kMaxWordSizeRatio || + union_box.height() > line_height * kMaxLineSizeRatio || + next_box.left() > prev_right + line_height * kMaxWordGapRatio) { + add_next_word = false; + } + } + next_word->set_flag(W_FUZZY_NON, add_next_word); + } else { + add_next_word = next_word->flag(W_FUZZY_NON); + } + if (add_next_word) { + if (combo == NULL) { + copy_word = new WERD; + *copy_word = *(word_it.data()); // deep copy + combo = new WERD_RES(copy_word); + combo->x_height = the_row->x_height(); + combo->combination = TRUE; + word_res_it.add_to_end(combo); + } + word_res->part_of_combo = TRUE; + } else { + combo = NULL; + } + word_res_it.add_to_end(word_res); + } +} + + +WERD_RES& WERD_RES::operator=(const WERD_RES & source) { + this->ELIST_LINK::operator=(source); + Clear(); + if (source.combination) { + word = new WERD; + *word = *(source.word); // deep copy + } else { + word = source.word; // pt to same word + } + if (source.bln_boxes != NULL) + bln_boxes = new tesseract::BoxWord(*source.bln_boxes); + if (source.chopped_word != NULL) + chopped_word = new TWERD(*source.chopped_word); + if (source.rebuild_word != NULL) + rebuild_word = new TWERD(*source.rebuild_word); + // TODO(rays) Do we ever need to copy the seam_array? + blob_row = source.blob_row; + denorm = source.denorm; + if (source.box_word != NULL) + box_word = new tesseract::BoxWord(*source.box_word); + best_state = source.best_state; + correct_text = source.correct_text; + blob_widths = source.blob_widths; + blob_gaps = source.blob_gaps; + // None of the uses of operator= require the ratings matrix to be copied, + // so don't as it would be really slow. + + // Copy the cooked choices. + WERD_CHOICE_IT wc_it(const_cast(&source.best_choices)); + WERD_CHOICE_IT wc_dest_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) { + const WERD_CHOICE *choice = wc_it.data(); + wc_dest_it.add_after_then_move(new WERD_CHOICE(*choice)); + } + if (!wc_dest_it.empty()) { + wc_dest_it.move_to_first(); + best_choice = wc_dest_it.data(); + } else { + best_choice = NULL; + } + + if (source.raw_choice != NULL) { + raw_choice = new WERD_CHOICE(*source.raw_choice); + } else { + raw_choice = NULL; + } + if (source.ep_choice != NULL) { + ep_choice = new WERD_CHOICE(*source.ep_choice); + } else { + ep_choice = NULL; + } + reject_map = source.reject_map; + combination = source.combination; + part_of_combo = source.part_of_combo; + CopySimpleFields(source); + if (source.blamer_bundle != NULL) { + blamer_bundle = new BlamerBundle(*(source.blamer_bundle)); + } + return *this; +} + +// Copies basic fields that don't involve pointers that might be useful +// to copy when making one WERD_RES from another. +void WERD_RES::CopySimpleFields(const WERD_RES& source) { + tess_failed = source.tess_failed; + tess_accepted = source.tess_accepted; + tess_would_adapt = source.tess_would_adapt; + done = source.done; + unlv_crunch_mode = source.unlv_crunch_mode; + small_caps = source.small_caps; + odd_size = source.odd_size; + italic = source.italic; + bold = source.bold; + fontinfo = source.fontinfo; + fontinfo2 = source.fontinfo2; + fontinfo_id_count = source.fontinfo_id_count; + fontinfo_id2_count = source.fontinfo_id2_count; + x_height = source.x_height; + caps_height = source.caps_height; + baseline_shift = source.baseline_shift; + guessed_x_ht = source.guessed_x_ht; + guessed_caps_ht = source.guessed_caps_ht; + reject_spaces = source.reject_spaces; + uch_set = source.uch_set; + tesseract = source.tesseract; +} + +// Initializes a blank (default constructed) WERD_RES from one that has +// already been recognized. +// Use SetupFor*Recognition afterwards to complete the setup and make +// it ready for a retry recognition. +void WERD_RES::InitForRetryRecognition(const WERD_RES& source) { + word = source.word; + CopySimpleFields(source); + if (source.blamer_bundle != NULL) { + blamer_bundle = new BlamerBundle(); + blamer_bundle->CopyTruth(*source.blamer_bundle); + } +} + +// Sets up the members used in recognition: bln_boxes, chopped_word, +// seam_array, denorm. Returns false if +// the word is empty and sets up fake results. If use_body_size is +// true and row->body_size is set, then body_size will be used for +// blob normalization instead of xheight + ascrise. This flag is for +// those languages that are using CJK pitch model and thus it has to +// be true if and only if tesseract->textord_use_cjk_fp_model is +// true. +// If allow_detailed_fx is true, the feature extractor will receive fine +// precision outline information, allowing smoother features and better +// features on low resolution images. +// The norm_mode_hint sets the default mode for normalization in absence +// of any of the above flags. +// norm_box is used to override the word bounding box to determine the +// normalization scale and offset. +// Returns false if the word is empty and sets up fake results. +bool WERD_RES::SetupForRecognition(const UNICHARSET& unicharset_in, + tesseract::Tesseract* tess, Pix* pix, + int norm_mode, + const TBOX* norm_box, + bool numeric_mode, + bool use_body_size, + bool allow_detailed_fx, + ROW *row, const BLOCK* block) { + tesseract::OcrEngineMode norm_mode_hint = + static_cast(norm_mode); + tesseract = tess; + POLY_BLOCK* pb = block != NULL ? block->poly_block() : NULL; + if ((norm_mode_hint != tesseract::OEM_CUBE_ONLY && + word->cblob_list()->empty()) || (pb != NULL && !pb->IsText())) { + // Empty words occur when all the blobs have been moved to the rej_blobs + // list, which seems to occur frequently in junk. + SetupFake(unicharset_in); + word->set_flag(W_REP_CHAR, false); + return false; + } + ClearResults(); + SetupWordScript(unicharset_in); + chopped_word = TWERD::PolygonalCopy(allow_detailed_fx, word); + float word_xheight = use_body_size && row != NULL && row->body_size() > 0.0f + ? row->body_size() : x_height; + chopped_word->BLNormalize(block, row, pix, word->flag(W_INVERSE), + word_xheight, baseline_shift, numeric_mode, + norm_mode_hint, norm_box, &denorm); + blob_row = row; + SetupBasicsFromChoppedWord(unicharset_in); + SetupBlamerBundle(); + int num_blobs = chopped_word->NumBlobs(); + ratings = new MATRIX(num_blobs, kWordrecMaxNumJoinChunks); + tess_failed = false; + return true; +} + +// Set up the seam array, bln_boxes, best_choice, and raw_choice to empty +// accumulators from a made chopped word. We presume the fields are already +// empty. +void WERD_RES::SetupBasicsFromChoppedWord(const UNICHARSET &unicharset_in) { + bln_boxes = tesseract::BoxWord::CopyFromNormalized(chopped_word); + start_seam_list(chopped_word, &seam_array); + SetupBlobWidthsAndGaps(); + ClearWordChoices(); +} + +// Sets up the members used in recognition for an empty recognition result: +// bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. +void WERD_RES::SetupFake(const UNICHARSET& unicharset_in) { + ClearResults(); + SetupWordScript(unicharset_in); + chopped_word = new TWERD; + rebuild_word = new TWERD; + bln_boxes = new tesseract::BoxWord; + box_word = new tesseract::BoxWord; + int blob_count = word->cblob_list()->length(); + if (blob_count > 0) { + BLOB_CHOICE** fake_choices = new BLOB_CHOICE*[blob_count]; + // For non-text blocks, just pass any blobs through to the box_word + // and call the word failed with a fake classification. + C_BLOB_IT b_it(word->cblob_list()); + int blob_id = 0; + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + TBOX box = b_it.data()->bounding_box(); + box_word->InsertBox(box_word->length(), box); + fake_choices[blob_id++] = new BLOB_CHOICE; + } + FakeClassifyWord(blob_count, fake_choices); + delete [] fake_choices; + } else { + WERD_CHOICE* word = new WERD_CHOICE(&unicharset_in); + word->make_bad(); + LogNewRawChoice(word); + // Ownership of word is taken by *this WERD_RES in LogNewCookedChoice. + LogNewCookedChoice(1, false, word); + } + tess_failed = true; + done = true; +} + +void WERD_RES::SetupWordScript(const UNICHARSET& uch) { + uch_set = &uch; + int script = uch.default_sid(); + word->set_script_id(script); + word->set_flag(W_SCRIPT_HAS_XHEIGHT, uch.script_has_xheight()); + word->set_flag(W_SCRIPT_IS_LATIN, script == uch.latin_sid()); +} + +// Sets up the blamer_bundle if it is not null, using the initialized denorm. +void WERD_RES::SetupBlamerBundle() { + if (blamer_bundle != NULL) { + blamer_bundle->SetupNormTruthWord(denorm); + } +} + +// Computes the blob_widths and blob_gaps from the chopped_word. +void WERD_RES::SetupBlobWidthsAndGaps() { + blob_widths.truncate(0); + blob_gaps.truncate(0); + int num_blobs = chopped_word->NumBlobs(); + for (int b = 0; b < num_blobs; ++b) { + TBLOB *blob = chopped_word->blobs[b]; + TBOX box = blob->bounding_box(); + blob_widths.push_back(box.width()); + if (b + 1 < num_blobs) { + blob_gaps.push_back( + chopped_word->blobs[b + 1]->bounding_box().left() - box.right()); + } + } +} + +// Updates internal data to account for a new SEAM (chop) at the given +// blob_number. Fixes the ratings matrix and states in the choices, as well +// as the blob widths and gaps. +void WERD_RES::InsertSeam(int blob_number, SEAM* seam) { + // Insert the seam into the SEAMS array. + seam->PrepareToInsertSeam(seam_array, chopped_word->blobs, blob_number, true); + seam_array.insert(seam, blob_number); + if (ratings != NULL) { + // Expand the ratings matrix. + ratings = ratings->ConsumeAndMakeBigger(blob_number); + // Fix all the segmentation states. + if (raw_choice != NULL) + raw_choice->UpdateStateForSplit(blob_number); + WERD_CHOICE_IT wc_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) { + WERD_CHOICE* choice = wc_it.data(); + choice->UpdateStateForSplit(blob_number); + } + SetupBlobWidthsAndGaps(); + } +} + +// Returns true if all the word choices except the first have adjust_factors +// worse than the given threshold. +bool WERD_RES::AlternativeChoiceAdjustmentsWorseThan(float threshold) const { + // The choices are not changed by this iteration. + WERD_CHOICE_IT wc_it(const_cast(&best_choices)); + for (wc_it.forward(); !wc_it.at_first(); wc_it.forward()) { + WERD_CHOICE* choice = wc_it.data(); + if (choice->adjust_factor() <= threshold) + return false; + } + return true; +} + +// Returns true if the current word is ambiguous (by number of answers or +// by dangerous ambigs.) +bool WERD_RES::IsAmbiguous() { + return !best_choices.singleton() || best_choice->dangerous_ambig_found(); +} + +// Returns true if the ratings matrix size matches the sum of each of the +// segmentation states. +bool WERD_RES::StatesAllValid() { + int ratings_dim = ratings->dimension(); + if (raw_choice->TotalOfStates() != ratings_dim) { + tprintf("raw_choice has total of states = %d vs ratings dim of %d\n", + raw_choice->TotalOfStates(), ratings_dim); + return false; + } + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + if (choice->TotalOfStates() != ratings_dim) { + tprintf("Cooked #%d has total of states = %d vs ratings dim of %d\n", + index, choice->TotalOfStates(), ratings_dim); + return false; + } + } + return true; +} + +// Prints a list of words found if debug is true or the word result matches +// the word_to_debug. +void WERD_RES::DebugWordChoices(bool debug, const char* word_to_debug) { + if (debug || + (word_to_debug != NULL && *word_to_debug != '\0' && best_choice != NULL && + best_choice->unichar_string() == STRING(word_to_debug))) { + if (raw_choice != NULL) + raw_choice->print("\nBest Raw Choice"); + + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + STRING label; + label.add_str_int("\nCooked Choice #", index); + choice->print(label.string()); + } + } +} + +// Prints the top choice along with the accepted/done flags. +void WERD_RES::DebugTopChoice(const char* msg) const { + tprintf("Best choice: accepted=%d, adaptable=%d, done=%d : ", + tess_accepted, tess_would_adapt, done); + if (best_choice == NULL) + tprintf("\n"); + else + best_choice->print(msg); +} + +// Removes from best_choices all choices which are not within a reasonable +// range of the best choice. +// TODO(rays) incorporate the information used here into the params training +// re-ranker, in place of this heuristic that is based on the previous +// adjustment factor. +void WERD_RES::FilterWordChoices(int debug_level) { + if (best_choice == NULL || best_choices.singleton()) + return; + + if (debug_level >= 2) + best_choice->print("\nFiltering against best choice"); + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.forward(); !it.at_first(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + float threshold = StopperAmbigThreshold(best_choice->adjust_factor(), + choice->adjust_factor()); + // i, j index the blob choice in choice, best_choice. + // chunk is an index into the chopped_word blobs (AKA chunks). + // Since the two words may use different segmentations of the chunks, we + // iterate over the chunks to find out whether a comparable blob + // classification is much worse than the best result. + int i = 0, j = 0, chunk = 0; + // Each iteration of the while deals with 1 chunk. On entry choice_chunk + // and best_chunk are the indices of the first chunk in the NEXT blob, + // i.e. we don't have to increment i, j while chunk < choice_chunk and + // best_chunk respectively. + int choice_chunk = choice->state(0), best_chunk = best_choice->state(0); + while (i < choice->length() && j < best_choice->length()) { + if (choice->unichar_id(i) != best_choice->unichar_id(j) && + choice->certainty(i) - best_choice->certainty(j) < threshold) { + if (debug_level >= 2) { + STRING label; + label.add_str_int("\nDiscarding bad choice #", index); + choice->print(label.string()); + tprintf("i %d j %d Chunk %d Choice->Blob[i].Certainty %.4g" + " BestChoice->ChunkCertainty[Chunk] %g Threshold %g\n", + i, j, chunk, choice->certainty(i), + best_choice->certainty(j), threshold); + } + delete it.extract(); + break; + } + ++chunk; + // If needed, advance choice_chunk to keep up with chunk. + while (choice_chunk < chunk && ++i < choice->length()) + choice_chunk += choice->state(i); + // If needed, advance best_chunk to keep up with chunk. + while (best_chunk < chunk && ++j < best_choice->length()) + best_chunk += best_choice->state(j); + } + } +} + +void WERD_RES::ComputeAdaptionThresholds(float certainty_scale, + float min_rating, + float max_rating, + float rating_margin, + float* thresholds) { + int chunk = 0; + int end_chunk = best_choice->state(0); + int end_raw_chunk = raw_choice->state(0); + int raw_blob = 0; + for (int i = 0; i < best_choice->length(); i++, thresholds++) { + float avg_rating = 0.0f; + int num_error_chunks = 0; + + // For each chunk in best choice blob i, count non-matching raw results. + while (chunk < end_chunk) { + if (chunk >= end_raw_chunk) { + ++raw_blob; + end_raw_chunk += raw_choice->state(raw_blob); + } + if (best_choice->unichar_id(i) != + raw_choice->unichar_id(raw_blob)) { + avg_rating += raw_choice->certainty(raw_blob); + ++num_error_chunks; + } + ++chunk; + } + + if (num_error_chunks > 0) { + avg_rating /= num_error_chunks; + *thresholds = (avg_rating / -certainty_scale) * (1.0 - rating_margin); + } else { + *thresholds = max_rating; + } + + if (*thresholds > max_rating) + *thresholds = max_rating; + if (*thresholds < min_rating) + *thresholds = min_rating; + } +} + +// Saves a copy of the word_choice if it has the best unadjusted rating. +// Returns true if the word_choice was the new best. +bool WERD_RES::LogNewRawChoice(WERD_CHOICE* word_choice) { + if (raw_choice == NULL || word_choice->rating() < raw_choice->rating()) { + delete raw_choice; + raw_choice = new WERD_CHOICE(*word_choice); + raw_choice->set_permuter(TOP_CHOICE_PERM); + return true; + } + return false; +} + +// Consumes word_choice by adding it to best_choices, (taking ownership) if +// the certainty for word_choice is some distance of the best choice in +// best_choices, or by deleting the word_choice and returning false. +// The best_choices list is kept in sorted order by rating. Duplicates are +// removed, and the list is kept no longer than max_num_choices in length. +// Returns true if the word_choice is still a valid pointer. +bool WERD_RES::LogNewCookedChoice(int max_num_choices, bool debug, + WERD_CHOICE* word_choice) { + if (best_choice != NULL) { + // Throw out obviously bad choices to save some work. + // TODO(rays) Get rid of this! This piece of code produces different + // results according to the order in which words are found, which is an + // undesirable behavior. It would be better to keep all the choices and + // prune them later when more information is available. + float max_certainty_delta = + StopperAmbigThreshold(best_choice->adjust_factor(), + word_choice->adjust_factor()); + if (max_certainty_delta > -kStopperAmbiguityThresholdOffset) + max_certainty_delta = -kStopperAmbiguityThresholdOffset; + if (word_choice->certainty() - best_choice->certainty() < + max_certainty_delta) { + if (debug) { + STRING bad_string; + word_choice->string_and_lengths(&bad_string, NULL); + tprintf("Discarding choice \"%s\" with an overly low certainty" + " %.3f vs best choice certainty %.3f (Threshold: %.3f)\n", + bad_string.string(), word_choice->certainty(), + best_choice->certainty(), + max_certainty_delta + best_choice->certainty()); + } + delete word_choice; + return false; + } + } + + // Insert in the list in order of increasing rating, but knock out worse + // string duplicates. + WERD_CHOICE_IT it(&best_choices); + const STRING& new_str = word_choice->unichar_string(); + bool inserted = false; + int num_choices = 0; + if (!it.empty()) { + do { + WERD_CHOICE* choice = it.data(); + if (choice->rating() > word_choice->rating() && !inserted) { + // Time to insert. + it.add_before_stay_put(word_choice); + inserted = true; + if (num_choices == 0) + best_choice = word_choice; // This is the new best. + ++num_choices; + } + if (choice->unichar_string() == new_str) { + if (inserted) { + // New is better. + delete it.extract(); + } else { + // Old is better. + if (debug) { + tprintf("Discarding duplicate choice \"%s\", rating %g vs %g\n", + new_str.string(), word_choice->rating(), choice->rating()); + } + delete word_choice; + return false; + } + } else { + ++num_choices; + if (num_choices > max_num_choices) + delete it.extract(); + } + it.forward(); + } while (!it.at_first()); + } + if (!inserted && num_choices < max_num_choices) { + it.add_to_end(word_choice); + inserted = true; + if (num_choices == 0) + best_choice = word_choice; // This is the new best. + } + if (debug) { + if (inserted) + tprintf("New %s", best_choice == word_choice ? "Best" : "Secondary"); + else + tprintf("Poor"); + word_choice->print(" Word Choice"); + } + if (!inserted) { + delete word_choice; + return false; + } + return true; +} + + +// Simple helper moves the ownership of the pointer data from src to dest, +// first deleting anything in dest, and nulling out src afterwards. +template static void MovePointerData(T** dest, T**src) { + delete *dest; + *dest = *src; + *src = NULL; +} + +// Prints a brief list of all the best choices. +void WERD_RES::PrintBestChoices() const { + STRING alternates_str; + WERD_CHOICE_IT it(const_cast(&best_choices)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + if (!it.at_first()) alternates_str += "\", \""; + alternates_str += it.data()->unichar_string(); + } + tprintf("Alternates for \"%s\": {\"%s\"}\n", + best_choice->unichar_string().string(), alternates_str.string()); +} + +// Returns the sum of the widths of the blob between start_blob and last_blob +// inclusive. +int WERD_RES::GetBlobsWidth(int start_blob, int last_blob) { + int result = 0; + for (int b = start_blob; b <= last_blob; ++b) { + result += blob_widths[b]; + if (b < last_blob) + result += blob_gaps[b]; + } + return result; +} +// Returns the width of a gap between the specified blob and the next one. +int WERD_RES::GetBlobsGap(int blob_index) { + if (blob_index < 0 || blob_index >= blob_gaps.size()) + return 0; + return blob_gaps[blob_index]; +} + +// Returns the BLOB_CHOICE corresponding to the given index in the +// best choice word taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. May return NULL if there is no +// BLOB_CHOICE matching the unichar_id at the given index. +BLOB_CHOICE* WERD_RES::GetBlobChoice(int index) const { + if (index < 0 || index >= best_choice->length()) return NULL; + BLOB_CHOICE_LIST* choices = GetBlobChoices(index); + return FindMatchingChoice(best_choice->unichar_id(index), choices); +} + +// Returns the BLOB_CHOICE_LIST corresponding to the given index in the +// best choice word taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. +BLOB_CHOICE_LIST* WERD_RES::GetBlobChoices(int index) const { + return best_choice->blob_choices(index, ratings); +} + +// Moves the results fields from word to this. This takes ownership of all +// the data, so src can be destructed. +void WERD_RES::ConsumeWordResults(WERD_RES* word) { + denorm = word->denorm; + blob_row = word->blob_row; + MovePointerData(&chopped_word, &word->chopped_word); + MovePointerData(&rebuild_word, &word->rebuild_word); + MovePointerData(&box_word, &word->box_word); + seam_array.delete_data_pointers(); + seam_array = word->seam_array; + word->seam_array.clear(); + best_state.move(&word->best_state); + correct_text.move(&word->correct_text); + blob_widths.move(&word->blob_widths); + blob_gaps.move(&word->blob_gaps); + if (ratings != NULL) ratings->delete_matrix_pointers(); + MovePointerData(&ratings, &word->ratings); + best_choice = word->best_choice; + MovePointerData(&raw_choice, &word->raw_choice); + best_choices.clear(); + WERD_CHOICE_IT wc_it(&best_choices); + wc_it.add_list_after(&word->best_choices); + reject_map = word->reject_map; + if (word->blamer_bundle != NULL) { + assert(blamer_bundle != NULL); + blamer_bundle->CopyResults(*(word->blamer_bundle)); + } + CopySimpleFields(*word); +} + +// Replace the best choice and rebuild box word. +// choice must be from the current best_choices list. +void WERD_RES::ReplaceBestChoice(WERD_CHOICE* choice) { + best_choice = choice; + RebuildBestState(); + SetupBoxWord(); + // Make up a fake reject map of the right length to keep the + // rejection pass happy. + reject_map.initialise(best_state.length()); + done = tess_accepted = tess_would_adapt = true; + SetScriptPositions(); +} + +// Builds the rebuild_word and sets the best_state from the chopped_word and +// the best_choice->state. +void WERD_RES::RebuildBestState() { + ASSERT_HOST(best_choice != NULL); + if (rebuild_word != NULL) + delete rebuild_word; + rebuild_word = new TWERD; + if (seam_array.empty()) + start_seam_list(chopped_word, &seam_array); + best_state.truncate(0); + int start = 0; + for (int i = 0; i < best_choice->length(); ++i) { + int length = best_choice->state(i); + best_state.push_back(length); + if (length > 1) { + SEAM::JoinPieces(seam_array, chopped_word->blobs, start, + start + length - 1); + } + TBLOB* blob = chopped_word->blobs[start]; + rebuild_word->blobs.push_back(new TBLOB(*blob)); + if (length > 1) { + SEAM::BreakPieces(seam_array, chopped_word->blobs, start, + start + length - 1); + } + start += length; + } +} + +// Copies the chopped_word to the rebuild_word, faking a best_state as well. +// Also sets up the output box_word. +void WERD_RES::CloneChoppedToRebuild() { + if (rebuild_word != NULL) + delete rebuild_word; + rebuild_word = new TWERD(*chopped_word); + SetupBoxWord(); + int word_len = box_word->length(); + best_state.reserve(word_len); + correct_text.reserve(word_len); + for (int i = 0; i < word_len; ++i) { + best_state.push_back(1); + correct_text.push_back(STRING("")); + } +} + +// Sets/replaces the box_word with one made from the rebuild_word. +void WERD_RES::SetupBoxWord() { + if (box_word != NULL) + delete box_word; + rebuild_word->ComputeBoundingBoxes(); + box_word = tesseract::BoxWord::CopyFromNormalized(rebuild_word); + box_word->ClipToOriginalWord(denorm.block(), word); +} + +// Sets up the script positions in the output best_choice using the best_choice +// to get the unichars, and the unicharset to get the target positions. +void WERD_RES::SetScriptPositions() { + best_choice->SetScriptPositions(small_caps, chopped_word); +} +// Sets all the blobs in all the words (raw choice and best choices) to be +// the given position. (When a sub/superscript is recognized as a separate +// word, it falls victim to the rule that a whole word cannot be sub or +// superscript, so this function overrides that problem.) +void WERD_RES::SetAllScriptPositions(tesseract::ScriptPos position) { + raw_choice->SetAllScriptPositions(position); + WERD_CHOICE_IT wc_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) + wc_it.data()->SetAllScriptPositions(position); +} + +// Classifies the word with some already-calculated BLOB_CHOICEs. +// The choices are an array of blob_count pointers to BLOB_CHOICE, +// providing a single classifier result for each blob. +// The BLOB_CHOICEs are consumed and the word takes ownership. +// The number of blobs in the box_word must match blob_count. +void WERD_RES::FakeClassifyWord(int blob_count, BLOB_CHOICE** choices) { + // Setup the WERD_RES. + ASSERT_HOST(box_word != NULL); + ASSERT_HOST(blob_count == box_word->length()); + ClearWordChoices(); + ClearRatings(); + ratings = new MATRIX(blob_count, 1); + for (int c = 0; c < blob_count; ++c) { + BLOB_CHOICE_LIST* choice_list = new BLOB_CHOICE_LIST; + BLOB_CHOICE_IT choice_it(choice_list); + choice_it.add_after_then_move(choices[c]); + ratings->put(c, c, choice_list); + } + FakeWordFromRatings(TOP_CHOICE_PERM); + reject_map.initialise(blob_count); + done = true; +} + +// Creates a WERD_CHOICE for the word using the top choices from the leading +// diagonal of the ratings matrix. +void WERD_RES::FakeWordFromRatings(PermuterType permuter) { + int num_blobs = ratings->dimension(); + WERD_CHOICE* word_choice = new WERD_CHOICE(uch_set, num_blobs); + word_choice->set_permuter(permuter); + for (int b = 0; b < num_blobs; ++b) { + UNICHAR_ID unichar_id = UNICHAR_SPACE; + float rating = MAX_INT32; + float certainty = -MAX_INT32; + BLOB_CHOICE_LIST* choices = ratings->get(b, b); + if (choices != NULL && !choices->empty()) { + BLOB_CHOICE_IT bc_it(choices); + BLOB_CHOICE* choice = bc_it.data(); + unichar_id = choice->unichar_id(); + rating = choice->rating(); + certainty = choice->certainty(); + } + word_choice->append_unichar_id_space_allocated(unichar_id, 1, rating, + certainty); + } + LogNewRawChoice(word_choice); + // Ownership of word_choice taken by word here. + LogNewCookedChoice(1, false, word_choice); +} + +// Copies the best_choice strings to the correct_text for adaption/training. +void WERD_RES::BestChoiceToCorrectText() { + correct_text.clear(); + ASSERT_HOST(best_choice != NULL); + for (int i = 0; i < best_choice->length(); ++i) { + UNICHAR_ID choice_id = best_choice->unichar_id(i); + const char* blob_choice = uch_set->id_to_unichar(choice_id); + correct_text.push_back(STRING(blob_choice)); + } +} + +// Merges 2 adjacent blobs in the result if the permanent callback +// class_cb returns other than INVALID_UNICHAR_ID, AND the permanent +// callback box_cb is NULL or returns true, setting the merged blob +// result to the class returned from class_cb. +// Returns true if anything was merged. +bool WERD_RES::ConditionalBlobMerge( + TessResultCallback2* class_cb, + TessResultCallback2* box_cb) { + ASSERT_HOST(best_choice->length() == 0 || ratings != NULL); + bool modified = false; + for (int i = 0; i + 1 < best_choice->length(); ++i) { + UNICHAR_ID new_id = class_cb->Run(best_choice->unichar_id(i), + best_choice->unichar_id(i+1)); + if (new_id != INVALID_UNICHAR_ID && + (box_cb == NULL || box_cb->Run(box_word->BlobBox(i), + box_word->BlobBox(i + 1)))) { + // Raw choice should not be fixed. + best_choice->set_unichar_id(new_id, i); + modified = true; + MergeAdjacentBlobs(i); + const MATRIX_COORD& coord = best_choice->MatrixCoord(i); + if (!coord.Valid(*ratings)) { + ratings->IncreaseBandSize(coord.row + 1 - coord.col); + } + BLOB_CHOICE_LIST* blob_choices = GetBlobChoices(i); + if (FindMatchingChoice(new_id, blob_choices) == NULL) { + // Insert a fake result. + BLOB_CHOICE* blob_choice = new BLOB_CHOICE; + blob_choice->set_unichar_id(new_id); + BLOB_CHOICE_IT bc_it(blob_choices); + bc_it.add_before_then_move(blob_choice); + } + } + } + delete class_cb; + delete box_cb; + return modified; +} + +// Merges 2 adjacent blobs in the result (index and index+1) and corrects +// all the data to account for the change. +void WERD_RES::MergeAdjacentBlobs(int index) { + if (reject_map.length() == best_choice->length()) + reject_map.remove_pos(index); + best_choice->remove_unichar_id(index + 1); + rebuild_word->MergeBlobs(index, index + 2); + box_word->MergeBoxes(index, index + 2); + if (index + 1 < best_state.length()) { + best_state[index] += best_state[index + 1]; + best_state.remove(index + 1); + } +} + +// TODO(tkielbus) Decide between keeping this behavior here or modifying the +// training data. + +// Utility function for fix_quotes +// Return true if the next character in the string (given the UTF8 length in +// bytes) is a quote character. +static int is_simple_quote(const char* signed_str, int length) { + const unsigned char* str = + reinterpret_cast(signed_str); + // Standard 1 byte quotes. + return (length == 1 && (*str == '\'' || *str == '`')) || + // UTF-8 3 bytes curved quotes. + (length == 3 && ((*str == 0xe2 && + *(str + 1) == 0x80 && + *(str + 2) == 0x98) || + (*str == 0xe2 && + *(str + 1) == 0x80 && + *(str + 2) == 0x99))); +} + +// Callback helper for fix_quotes returns a double quote if both +// arguments are quote, otherwise INVALID_UNICHAR_ID. +UNICHAR_ID WERD_RES::BothQuotes(UNICHAR_ID id1, UNICHAR_ID id2) { + const char *ch = uch_set->id_to_unichar(id1); + const char *next_ch = uch_set->id_to_unichar(id2); + if (is_simple_quote(ch, strlen(ch)) && + is_simple_quote(next_ch, strlen(next_ch))) + return uch_set->unichar_to_id("\""); + return INVALID_UNICHAR_ID; +} + +// Change pairs of quotes to double quotes. +void WERD_RES::fix_quotes() { + if (!uch_set->contains_unichar("\"") || + !uch_set->get_enabled(uch_set->unichar_to_id("\""))) + return; // Don't create it if it is disallowed. + + ConditionalBlobMerge( + NewPermanentTessCallback(this, &WERD_RES::BothQuotes), + NULL); +} + +// Callback helper for fix_hyphens returns UNICHAR_ID of - if both +// arguments are hyphen, otherwise INVALID_UNICHAR_ID. +UNICHAR_ID WERD_RES::BothHyphens(UNICHAR_ID id1, UNICHAR_ID id2) { + const char *ch = uch_set->id_to_unichar(id1); + const char *next_ch = uch_set->id_to_unichar(id2); + if (strlen(ch) == 1 && strlen(next_ch) == 1 && + (*ch == '-' || *ch == '~') && (*next_ch == '-' || *next_ch == '~')) + return uch_set->unichar_to_id("-"); + return INVALID_UNICHAR_ID; +} + +// Callback helper for fix_hyphens returns true if box1 and box2 overlap +// (assuming both on the same textline, are in order and a chopped em dash.) +bool WERD_RES::HyphenBoxesOverlap(const TBOX& box1, const TBOX& box2) { + return box1.right() >= box2.left(); +} + +// Change pairs of hyphens to a single hyphen if the bounding boxes touch +// Typically a long dash which has been segmented. +void WERD_RES::fix_hyphens() { + if (!uch_set->contains_unichar("-") || + !uch_set->get_enabled(uch_set->unichar_to_id("-"))) + return; // Don't create it if it is disallowed. + + ConditionalBlobMerge( + NewPermanentTessCallback(this, &WERD_RES::BothHyphens), + NewPermanentTessCallback(this, &WERD_RES::HyphenBoxesOverlap)); +} + +// Callback helper for merge_tess_fails returns a space if both +// arguments are space, otherwise INVALID_UNICHAR_ID. +UNICHAR_ID WERD_RES::BothSpaces(UNICHAR_ID id1, UNICHAR_ID id2) { + if (id1 == id2 && id1 == uch_set->unichar_to_id(" ")) + return id1; + else + return INVALID_UNICHAR_ID; +} + +// Change pairs of tess failures to a single one +void WERD_RES::merge_tess_fails() { + if (ConditionalBlobMerge( + NewPermanentTessCallback(this, &WERD_RES::BothSpaces), NULL)) { + int len = best_choice->length(); + ASSERT_HOST(reject_map.length() == len); + ASSERT_HOST(box_word->length() == len); + } +} + +// Returns true if the collection of count pieces, starting at start, are all +// natural connected components, ie there are no real chops involved. +bool WERD_RES::PiecesAllNatural(int start, int count) const { + // all seams must have no splits. + for (int index = start; index < start + count - 1; ++index) { + if (index >= 0 && index < seam_array.size()) { + SEAM* seam = seam_array[index]; + if (seam != NULL && seam->HasAnySplits()) return false; + } + } + return true; +} + + +WERD_RES::~WERD_RES () { + Clear(); +} + +void WERD_RES::InitNonPointers() { + tess_failed = FALSE; + tess_accepted = FALSE; + tess_would_adapt = FALSE; + done = FALSE; + unlv_crunch_mode = CR_NONE; + small_caps = false; + odd_size = false; + italic = FALSE; + bold = FALSE; + // The fontinfos and tesseract count as non-pointers as they point to + // data owned elsewhere. + fontinfo = NULL; + fontinfo2 = NULL; + tesseract = NULL; + fontinfo_id_count = 0; + fontinfo_id2_count = 0; + x_height = 0.0; + caps_height = 0.0; + baseline_shift = 0.0f; + space_certainty = 0.0f; + guessed_x_ht = TRUE; + guessed_caps_ht = TRUE; + combination = FALSE; + part_of_combo = FALSE; + reject_spaces = FALSE; +} + +void WERD_RES::InitPointers() { + word = NULL; + bln_boxes = NULL; + blob_row = NULL; + uch_set = NULL; + chopped_word = NULL; + rebuild_word = NULL; + box_word = NULL; + ratings = NULL; + best_choice = NULL; + raw_choice = NULL; + ep_choice = NULL; + blamer_bundle = NULL; +} + +void WERD_RES::Clear() { + if (word != NULL && combination) { + delete word; + } + word = NULL; + delete blamer_bundle; + blamer_bundle = NULL; + ClearResults(); +} + +void WERD_RES::ClearResults() { + done = false; + fontinfo = NULL; + fontinfo2 = NULL; + fontinfo_id_count = 0; + fontinfo_id2_count = 0; + if (bln_boxes != NULL) { + delete bln_boxes; + bln_boxes = NULL; + } + blob_row = NULL; + if (chopped_word != NULL) { + delete chopped_word; + chopped_word = NULL; + } + if (rebuild_word != NULL) { + delete rebuild_word; + rebuild_word = NULL; + } + if (box_word != NULL) { + delete box_word; + box_word = NULL; + } + best_state.clear(); + correct_text.clear(); + seam_array.delete_data_pointers(); + seam_array.clear(); + blob_widths.clear(); + blob_gaps.clear(); + ClearRatings(); + ClearWordChoices(); + if (blamer_bundle != NULL) blamer_bundle->ClearResults(); +} +void WERD_RES::ClearWordChoices() { + best_choice = NULL; + if (raw_choice != NULL) { + delete raw_choice; + raw_choice = NULL; + } + best_choices.clear(); + if (ep_choice != NULL) { + delete ep_choice; + ep_choice = NULL; + } +} +void WERD_RES::ClearRatings() { + if (ratings != NULL) { + ratings->delete_matrix_pointers(); + delete ratings; + ratings = NULL; + } +} + + +bool PAGE_RES_IT::operator ==(const PAGE_RES_IT &other) const { + return word_res == other.word_res && + row_res == other.row_res && + block_res == other.block_res; +} + +int PAGE_RES_IT::cmp(const PAGE_RES_IT &other) const { + ASSERT_HOST(page_res == other.page_res); + if (other.block_res == NULL) { + // other points to the end of the page. + if (block_res == NULL) + return 0; + return -1; + } + if (block_res == NULL) { + return 1; // we point to the end of the page. + } + if (block_res == other.block_res) { + if (other.row_res == NULL || row_res == NULL) { + // this should only happen if we hit an image block. + return 0; + } + if (row_res == other.row_res) { + // we point to the same block and row. + ASSERT_HOST(other.word_res != NULL && word_res != NULL); + if (word_res == other.word_res) { + // we point to the same word! + return 0; + } + + WERD_RES_IT word_res_it(&row_res->word_res_list); + for (word_res_it.mark_cycle_pt(); !word_res_it.cycled_list(); + word_res_it.forward()) { + if (word_res_it.data() == word_res) { + return -1; + } else if (word_res_it.data() == other.word_res) { + return 1; + } + } + ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); + } + + // we both point to the same block, but different rows. + ROW_RES_IT row_res_it(&block_res->row_res_list); + for (row_res_it.mark_cycle_pt(); !row_res_it.cycled_list(); + row_res_it.forward()) { + if (row_res_it.data() == row_res) { + return -1; + } else if (row_res_it.data() == other.row_res) { + return 1; + } + } + ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); + } + + // We point to different blocks. + BLOCK_RES_IT block_res_it(&page_res->block_res_list); + for (block_res_it.mark_cycle_pt(); + !block_res_it.cycled_list(); block_res_it.forward()) { + if (block_res_it.data() == block_res) { + return -1; + } else if (block_res_it.data() == other.block_res) { + return 1; + } + } + // Shouldn't happen... + ASSERT_HOST("Error: Incomparable PAGE_RES_ITs" == NULL); + return 0; +} + +// Inserts the new_word as a combination owned by a corresponding WERD_RES +// before the current position. The simple fields of the WERD_RES are copied +// from clone_res and the resulting WERD_RES is returned for further setup +// with best_choice etc. +WERD_RES* PAGE_RES_IT::InsertSimpleCloneWord(const WERD_RES& clone_res, + WERD* new_word) { + // Make a WERD_RES for the new_word. + WERD_RES* new_res = new WERD_RES(new_word); + new_res->CopySimpleFields(clone_res); + new_res->combination = true; + // Insert into the appropriate place in the ROW_RES. + WERD_RES_IT wr_it(&row()->word_res_list); + for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { + WERD_RES* word = wr_it.data(); + if (word == word_res) + break; + } + ASSERT_HOST(!wr_it.cycled_list()); + wr_it.add_before_then_move(new_res); + if (wr_it.at_first()) { + // This is the new first word, so reset the member iterator so it + // detects the cycled_list state correctly. + ResetWordIterator(); + } + return new_res; +} + +// Helper computes the boundaries between blobs in the word. The blob bounds +// are likely very poor, if they come from LSTM, where it only outputs the +// character at one pixel within it, so we find the midpoints between them. +static void ComputeBlobEnds(const WERD_RES& word, C_BLOB_LIST* next_word_blobs, + GenericVector* blob_ends) { + C_BLOB_IT blob_it(word.word->cblob_list()); + for (int i = 0; i < word.best_state.size(); ++i) { + int length = word.best_state[i]; + // Get the bounding box of the fake blobs + TBOX blob_box = blob_it.data()->bounding_box(); + blob_it.forward(); + for (int b = 1; b < length; ++b) { + blob_box += blob_it.data()->bounding_box(); + blob_it.forward(); + } + // This blob_box is crap, so for now we are only looking for the + // boundaries between them. + int blob_end = MAX_INT32; + if (!blob_it.at_first() || next_word_blobs != NULL) { + if (blob_it.at_first()) + blob_it.set_to_list(next_word_blobs); + blob_end = (blob_box.right() + blob_it.data()->bounding_box().left()) / 2; + } + blob_ends->push_back(blob_end); + } +} + +// Replaces the current WERD/WERD_RES with the given words. The given words +// contain fake blobs that indicate the position of the characters. These are +// replaced with real blobs from the current word as much as possible. +void PAGE_RES_IT::ReplaceCurrentWord( + tesseract::PointerVector* words) { + if (words->empty()) { + DeleteCurrentWord(); + return; + } + WERD_RES* input_word = word(); + // Set the BOL/EOL flags on the words from the input word. + if (input_word->word->flag(W_BOL)) { + (*words)[0]->word->set_flag(W_BOL, true); + } else { + (*words)[0]->word->set_blanks(1); + } + words->back()->word->set_flag(W_EOL, input_word->word->flag(W_EOL)); + + // Move the blobs from the input word to the new set of words. + // If the input word_res is a combination, then the replacements will also be + // combinations, and will own their own words. If the input word_res is not a + // combination, then the final replacements will not be either, (although it + // is allowed for the input words to be combinations) and their words + // will get put on the row list. This maintains the ownership rules. + WERD_IT w_it(row()->row->word_list()); + if (!input_word->combination) { + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD* word = w_it.data(); + if (word == input_word->word) + break; + } + // w_it is now set to the input_word's word. + ASSERT_HOST(!w_it.cycled_list()); + } + // Insert into the appropriate place in the ROW_RES. + WERD_RES_IT wr_it(&row()->word_res_list); + for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { + WERD_RES* word = wr_it.data(); + if (word == input_word) + break; + } + ASSERT_HOST(!wr_it.cycled_list()); + // Since we only have an estimate of the bounds between blobs, use the blob + // x-middle as the determiner of where to put the blobs + C_BLOB_IT src_b_it(input_word->word->cblob_list()); + src_b_it.sort(&C_BLOB::SortByXMiddle); + C_BLOB_IT rej_b_it(input_word->word->rej_cblob_list()); + rej_b_it.sort(&C_BLOB::SortByXMiddle); + for (int w = 0; w < words->size(); ++w) { + WERD_RES* word_w = (*words)[w]; + // Compute blob boundaries. + GenericVector blob_ends; + C_BLOB_LIST* next_word_blobs = + w + 1 < words->size() ? (*words)[w + 1]->word->cblob_list() : NULL; + ComputeBlobEnds(*word_w, next_word_blobs, &blob_ends); + // Delete the fake blobs on the current word. + word_w->word->cblob_list()->clear(); + C_BLOB_IT dest_it(word_w->word->cblob_list()); + // Build the box word as we move the blobs. + tesseract::BoxWord* box_word = new tesseract::BoxWord; + for (int i = 0; i < blob_ends.size(); ++i) { + int end_x = blob_ends[i]; + TBOX blob_box; + // Add the blobs up to end_x. + while (!src_b_it.empty() && + src_b_it.data()->bounding_box().x_middle() < end_x) { + blob_box += src_b_it.data()->bounding_box(); + dest_it.add_after_then_move(src_b_it.extract()); + src_b_it.forward(); + } + while (!rej_b_it.empty() && + rej_b_it.data()->bounding_box().x_middle() < end_x) { + blob_box += rej_b_it.data()->bounding_box(); + dest_it.add_after_then_move(rej_b_it.extract()); + rej_b_it.forward(); + } + // Clip to the previously computed bounds. Although imperfectly accurate, + // it is good enough, and much more complicated to determine where else + // to clip. + if (i > 0 && blob_box.left() < blob_ends[i - 1]) + blob_box.set_left(blob_ends[i - 1]); + if (blob_box.right() > end_x) + blob_box.set_right(end_x); + box_word->InsertBox(i, blob_box); + } + // Fix empty boxes. If a very joined blob sits over multiple characters, + // then we will have some empty boxes from using the middle, so look for + // overlaps. + for (int i = 0; i < box_word->length(); ++i) { + TBOX box = box_word->BlobBox(i); + if (box.null_box()) { + // Nothing has its middle in the bounds of this blob, so use anything + // that overlaps. + for (dest_it.mark_cycle_pt(); !dest_it.cycled_list(); + dest_it.forward()) { + TBOX blob_box = dest_it.data()->bounding_box(); + if (blob_box.left() < blob_ends[i] && + (i == 0 || blob_box.right() >= blob_ends[i - 1])) { + if (i > 0 && blob_box.left() < blob_ends[i - 1]) + blob_box.set_left(blob_ends[i - 1]); + if (blob_box.right() > blob_ends[i]) + blob_box.set_right(blob_ends[i]); + box_word->ChangeBox(i, blob_box); + break; + } + } + } + } + delete word_w->box_word; + word_w->box_word = box_word; + if (!input_word->combination) { + // Insert word_w->word into the ROW. It doesn't own its word, so the + // ROW needs to own it. + w_it.add_before_stay_put(word_w->word); + word_w->combination = false; + } + (*words)[w] = NULL; // We are taking ownership. + wr_it.add_before_stay_put(word_w); + } + // We have taken ownership of the words. + words->clear(); + // Delete the current word, which has been replaced. We could just call + // DeleteCurrentWord, but that would iterate both lists again, and we know + // we are already in the right place. + if (!input_word->combination) + delete w_it.extract(); + delete wr_it.extract(); + ResetWordIterator(); +} + +// Deletes the current WERD_RES and its underlying WERD. +void PAGE_RES_IT::DeleteCurrentWord() { + // Check that this word is as we expect. part_of_combos are NEVER iterated + // by the normal iterator, so we should never be trying to delete them. + ASSERT_HOST(!word_res->part_of_combo); + if (!word_res->combination) { + // Combinations own their own word, so we won't find the word on the + // row's word_list, but it is legitimate to try to delete them. + // Delete word from the ROW when not a combination. + WERD_IT w_it(row()->row->word_list()); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + if (w_it.data() == word_res->word) { + break; + } + } + ASSERT_HOST(!w_it.cycled_list()); + delete w_it.extract(); + } + // Remove the WERD_RES for the new_word. + // Remove the WORD_RES from the ROW_RES. + WERD_RES_IT wr_it(&row()->word_res_list); + for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { + if (wr_it.data() == word_res) { + word_res = NULL; + break; + } + } + ASSERT_HOST(!wr_it.cycled_list()); + delete wr_it.extract(); + ResetWordIterator(); +} + +// Makes the current word a fuzzy space if not already fuzzy. Updates +// corresponding part of combo if required. +void PAGE_RES_IT::MakeCurrentWordFuzzy() { + WERD* real_word = word_res->word; + if (!real_word->flag(W_FUZZY_SP) && !real_word->flag(W_FUZZY_NON)) { + real_word->set_flag(W_FUZZY_SP, true); + if (word_res->combination) { + // The next word should be the corresponding part of combo, but we have + // already stepped past it, so find it by search. + WERD_RES_IT wr_it(&row()->word_res_list); + for (wr_it.mark_cycle_pt(); + !wr_it.cycled_list() && wr_it.data() != word_res; wr_it.forward()) { + } + wr_it.forward(); + ASSERT_HOST(wr_it.data()->part_of_combo); + real_word = wr_it.data()->word; + ASSERT_HOST(!real_word->flag(W_FUZZY_SP) && + !real_word->flag(W_FUZZY_NON)); + real_word->set_flag(W_FUZZY_SP, true); + } + } +} + +/************************************************************************* + * PAGE_RES_IT::restart_page + * + * Set things up at the start of the page + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::start_page(bool empty_ok) { + block_res_it.set_to_list(&page_res->block_res_list); + block_res_it.mark_cycle_pt(); + prev_block_res = NULL; + prev_row_res = NULL; + prev_word_res = NULL; + block_res = NULL; + row_res = NULL; + word_res = NULL; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + internal_forward(true, empty_ok); + return internal_forward(false, empty_ok); +} + +// Recovers from operations on the current word, such as in InsertCloneWord +// and DeleteCurrentWord. +// Resets the word_res_it so that it is one past the next_word_res, as +// it should be after internal_forward. If next_row_res != row_res, +// then the next_word_res is in the next row, so there is no need to do +// anything to word_res_it, but it is still a good idea to reset the pointers +// word_res and prev_word_res, which are still in the current row. +void PAGE_RES_IT::ResetWordIterator() { + if (row_res == next_row_res) { + // Reset the member iterator so it can move forward and detect the + // cycled_list state correctly. + word_res_it.move_to_first(); + for (word_res_it.mark_cycle_pt(); + !word_res_it.cycled_list() && word_res_it.data() != next_word_res; + word_res_it.forward()) { + if (!word_res_it.data()->part_of_combo) { + if (prev_row_res == row_res) prev_word_res = word_res; + word_res = word_res_it.data(); + } + } + ASSERT_HOST(!word_res_it.cycled_list()); + word_res_it.forward(); + } else { + // word_res_it is OK, but reset word_res and prev_word_res if needed. + WERD_RES_IT wr_it(&row_res->word_res_list); + for (wr_it.mark_cycle_pt(); !wr_it.cycled_list(); wr_it.forward()) { + if (!wr_it.data()->part_of_combo) { + if (prev_row_res == row_res) prev_word_res = word_res; + word_res = wr_it.data(); + } + } + } +} + +/************************************************************************* + * PAGE_RES_IT::internal_forward + * + * Find the next word on the page. If empty_ok is true, then non-text blocks + * and text blocks with no text are visited as if they contain a single + * imaginary word in a single imaginary row. (word() and row() both return NULL + * in such a block and the return value is NULL.) + * If empty_ok is false, the old behaviour is maintained. Each real word + * is visited and empty and non-text blocks and rows are skipped. + * new_block is used to initialize the iterators for a new block. + * The iterator maintains pointers to block, row and word for the previous, + * current and next words. These are correct, regardless of block/row + * boundaries. NULL values denote start and end of the page. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::internal_forward(bool new_block, bool empty_ok) { + bool new_row = false; + + prev_block_res = block_res; + prev_row_res = row_res; + prev_word_res = word_res; + block_res = next_block_res; + row_res = next_row_res; + word_res = next_word_res; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + + while (!block_res_it.cycled_list()) { + if (new_block) { + new_block = false; + row_res_it.set_to_list(&block_res_it.data()->row_res_list); + row_res_it.mark_cycle_pt(); + if (row_res_it.empty() && empty_ok) { + next_block_res = block_res_it.data(); + break; + } + new_row = true; + } + while (!row_res_it.cycled_list()) { + if (new_row) { + new_row = false; + word_res_it.set_to_list(&row_res_it.data()->word_res_list); + word_res_it.mark_cycle_pt(); + } + // Skip any part_of_combo words. + while (!word_res_it.cycled_list() && word_res_it.data()->part_of_combo) + word_res_it.forward(); + if (!word_res_it.cycled_list()) { + next_block_res = block_res_it.data(); + next_row_res = row_res_it.data(); + next_word_res = word_res_it.data(); + word_res_it.forward(); + goto foundword; + } + // end of row reached + row_res_it.forward(); + new_row = true; + } + // end of block reached + block_res_it.forward(); + new_block = true; + } + foundword: + // Update prev_word_best_choice pointer. + if (page_res != NULL && page_res->prev_word_best_choice != NULL) { + *page_res->prev_word_best_choice = + (new_block || prev_word_res == NULL) ? NULL : prev_word_res->best_choice; + } + return word_res; +} + +/************************************************************************* + * PAGE_RES_IT::restart_row() + * + * Move to the beginning (leftmost word) of the current row. + *************************************************************************/ +WERD_RES *PAGE_RES_IT::restart_row() { + ROW_RES *row = this->row(); + if (!row) return NULL; + for (restart_page(); this->row() != row; forward()) { + // pass + } + return word(); +} + +/************************************************************************* + * PAGE_RES_IT::forward_paragraph + * + * Move to the beginning of the next paragraph, allowing empty blocks. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::forward_paragraph() { + while (block_res == next_block_res && + (next_row_res != NULL && next_row_res->row != NULL && + row_res->row->para() == next_row_res->row->para())) { + internal_forward(false, true); + } + return internal_forward(false, true); +} + +/************************************************************************* + * PAGE_RES_IT::forward_block + * + * Move to the beginning of the next block, allowing empty blocks. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::forward_block() { + while (block_res == next_block_res) { + internal_forward(false, true); + } + return internal_forward(false, true); +} + +void PAGE_RES_IT::rej_stat_word() { + inT16 chars_in_word; + inT16 rejects_in_word = 0; + + chars_in_word = word_res->reject_map.length (); + page_res->char_count += chars_in_word; + block_res->char_count += chars_in_word; + row_res->char_count += chars_in_word; + + rejects_in_word = word_res->reject_map.reject_count (); + + page_res->rej_count += rejects_in_word; + block_res->rej_count += rejects_in_word; + row_res->rej_count += rejects_in_word; + if (chars_in_word == rejects_in_word) + row_res->whole_word_rej_count += rejects_in_word; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.h new file mode 100644 index 0000000..26a8534 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/pageres.h @@ -0,0 +1,776 @@ +/********************************************************************** + * File: pageres.h (Formerly page_res.h) + * Description: Results classes used by control.c + * Author: Phil Cheatle + * Created: Tue Sep 22 08:42:49 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef PAGERES_H +#define PAGERES_H + +#include "blamer.h" +#include "blobs.h" +#include "boxword.h" +#include "elst.h" +#include "genericvector.h" +#include "normalis.h" +#include "ocrblock.h" +#include "ocrrow.h" +#include "params_training_featdef.h" +#include "ratngs.h" +#include "rejctmap.h" +#include "seam.h" +#include "werd.h" + +namespace tesseract { + struct FontInfo; + class Tesseract; +} +using tesseract::FontInfo; + +/* Forward declarations */ + +class BLOCK_RES; + +ELISTIZEH(BLOCK_RES) CLISTIZEH(BLOCK_RES) +class + ROW_RES; + +ELISTIZEH(ROW_RES) +class WERD_RES; + +ELISTIZEH(WERD_RES) + +/************************************************************************* + * PAGE_RES - Page results + *************************************************************************/ + class PAGE_RES { // page result + public: + inT32 char_count; + inT32 rej_count; + BLOCK_RES_LIST block_res_list; + BOOL8 rejected; + // Updated every time PAGE_RES_IT iterating on this PAGE_RES moves to + // the next word. This pointer is not owned by PAGE_RES class. + WERD_CHOICE **prev_word_best_choice; + // Sums of blame reasons computed by the blamer. + GenericVector blame_reasons; + // Debug information about all the misadaptions on this page. + // Each BlamerBundle contains an index into this vector, so that words that + // caused misadaption could be marked. However, since words could be + // deleted/split/merged, the log is stored on the PAGE_RES level. + GenericVector misadaption_log; + + inline void Init() { + char_count = 0; + rej_count = 0; + rejected = FALSE; + prev_word_best_choice = NULL; + blame_reasons.init_to_size(IRR_NUM_REASONS, 0); + } + + PAGE_RES() { Init(); } // empty constructor + + PAGE_RES(bool merge_similar_words, + BLOCK_LIST *block_list, // real blocks + WERD_CHOICE **prev_word_best_choice_ptr); + + ~PAGE_RES() { // destructor + } +}; + +/************************************************************************* + * BLOCK_RES - Block results + *************************************************************************/ + +class BLOCK_RES :public ELIST_LINK { +public: + BLOCK * block; // real block + inT32 char_count; // chars in block + inT32 rej_count; // rejected chars + inT16 font_class; // + inT16 row_count; + float x_height; + BOOL8 font_assigned; // block already + // processed + BOOL8 bold; // all bold + BOOL8 italic; // all italic + + ROW_RES_LIST row_res_list; + + BLOCK_RES() { + } // empty constructor + + BLOCK_RES(bool merge_similar_words, BLOCK *the_block); // real block + + ~BLOCK_RES() { // destructor + } +}; + +/************************************************************************* + * ROW_RES - Row results + *************************************************************************/ + +class ROW_RES :public ELIST_LINK { +public: + ROW * row; // real row + inT32 char_count; // chars in block + inT32 rej_count; // rejected chars + inT32 whole_word_rej_count; // rejs in total rej wds + WERD_RES_LIST word_res_list; + + ROW_RES() { + } // empty constructor + + ROW_RES(bool merge_similar_words, ROW *the_row); // real row + + ~ROW_RES() { // destructor + } +}; + +/************************************************************************* + * WERD_RES - Word results + *************************************************************************/ +enum CRUNCH_MODE +{ + CR_NONE, + CR_KEEP_SPACE, + CR_LOOSE_SPACE, + CR_DELETE +}; + +// WERD_RES is a collection of publicly accessible members that gathers +// information about a word result. +class WERD_RES : public ELIST_LINK { +public: + // Which word is which? + // There are 3 coordinate spaces in use here: a possibly rotated pixel space, + // the original image coordinate space, and the BLN space in which the + // baseline of a word is at kBlnBaselineOffset, the xheight is kBlnXHeight, + // and the x-middle of the word is at 0. + // In the rotated pixel space, coordinates correspond to the input image, + // but may be rotated about the origin by a multiple of 90 degrees, + // and may therefore be negative. + // In any case a rotation by denorm.block()->re_rotation() will take them + // back to the original image. + // The other differences between words all represent different stages of + // processing during recognition. + + // ---------------------------INPUT------------------------------------- + + // The word is the input C_BLOBs in the rotated pixel space. + // word is NOT owned by the WERD_RES unless combination is true. + // All the other word pointers ARE owned by the WERD_RES. + WERD* word; // Input C_BLOB word. + + // -------------SETUP BY SetupFor*Recognition---READONLY-INPUT------------ + + // The bln_boxes contains the bounding boxes (only) of the input word, in the + // BLN space. The lengths of word and bln_boxes + // match as they are both before any chopping. + // TODO(rays) determine if docqual does anything useful and delete bln_boxes + // if it doesn't. + tesseract::BoxWord* bln_boxes; // BLN input bounding boxes. + // The ROW that this word sits in. NOT owned by the WERD_RES. + ROW* blob_row; + // The denorm provides the transformation to get back to the rotated image + // coords from the chopped_word/rebuild_word BLN coords, but each blob also + // has its own denorm. + DENORM denorm; // For use on chopped_word. + // Unicharset used by the classifier output in best_choice and raw_choice. + const UNICHARSET* uch_set; // For converting back to utf8. + + // ----Initialized by SetupFor*Recognition---BUT OUTPUT FROM RECOGNITION---- + // ----Setup to a (different!) state expected by the various classifiers---- + // TODO(rays) Tidy and make more consistent. + + // The chopped_word is also in BLN space, and represents the fully chopped + // character fragments that make up the word. + // The length of chopped_word matches length of seam_array + 1 (if set). + TWERD* chopped_word; // BLN chopped fragments output. + // Vector of SEAM* holding chopping points matching chopped_word. + GenericVector seam_array; + // Widths of blobs in chopped_word. + GenericVector blob_widths; + // Gaps between blobs in chopped_word. blob_gaps[i] is the gap between + // blob i and blob i+1. + GenericVector blob_gaps; + // Ratings matrix contains classifier choices for each classified combination + // of blobs. The dimension is the same as the number of blobs in chopped_word + // and the leading diagonal corresponds to classifier results of the blobs + // in chopped_word. The state_ members of best_choice, raw_choice and + // best_choices all correspond to this ratings matrix and allow extraction + // of the blob choices for any given WERD_CHOICE. + MATRIX* ratings; // Owned pointer. + // Pointer to the first WERD_CHOICE in best_choices. This is the result that + // will be output from Tesseract. Note that this is now a borrowed pointer + // and should NOT be deleted. + WERD_CHOICE* best_choice; // Borrowed pointer. + // The best raw_choice found during segmentation search. Differs from the + // best_choice by being the best result according to just the character + // classifier, not taking any language model information into account. + // Unlike best_choice, the pointer IS owned by this WERD_RES. + WERD_CHOICE* raw_choice; // Owned pointer. + // Alternative results found during chopping/segmentation search stages. + // Note that being an ELIST, best_choices owns the WERD_CHOICEs. + WERD_CHOICE_LIST best_choices; + + // Truth bounding boxes, text and incorrect choice reason. + BlamerBundle *blamer_bundle; + + // --------------OUTPUT FROM RECOGNITION------------------------------- + // --------------Not all fields are necessarily set.------------------- + // ---best_choice, raw_choice *must* end up set, with a box_word------- + // ---In complete output, the number of blobs in rebuild_word matches--- + // ---the number of boxes in box_word, the number of unichar_ids in--- + // ---best_choice, the number of ints in best_state, and the number--- + // ---of strings in correct_text-------------------------------------- + // ---SetupFake Sets everything to appropriate values if the word is--- + // ---known to be bad before recognition.------------------------------ + + // The rebuild_word is also in BLN space, but represents the final best + // segmentation of the word. Its length is therefore the same as box_word. + TWERD* rebuild_word; // BLN best segmented word. + // The box_word is in the original image coordinate space. It is the + // bounding boxes of the rebuild_word, after denormalization. + // The length of box_word matches rebuild_word, best_state (if set) and + // correct_text (if set), as well as best_choice and represents the + // number of classified units in the output. + tesseract::BoxWord* box_word; // Denormalized output boxes. + // The best_state stores the relationship between chopped_word and + // rebuild_word. Each blob[i] in rebuild_word is composed of best_state[i] + // adjacent blobs in chopped_word. The seams in seam_array are hidden + // within a rebuild_word blob and revealed between them. + GenericVector best_state; // Number of blobs in each best blob. + // The correct_text is used during training and adaption to carry the + // text to the training system without the need for a unicharset. There + // is one entry in the vector for each blob in rebuild_word and box_word. + GenericVector correct_text; + // The Tesseract that was used to recognize this word. Just a borrowed + // pointer. Note: Tesseract's class definition is in a higher-level library. + // We avoid introducing a cyclic dependency by not using the Tesseract + // within WERD_RES. We are just storing it to provide access to it + // for the top-level multi-language controller, and maybe for output of + // the recognized language. + tesseract::Tesseract* tesseract; + + // Less-well documented members. + // TODO(rays) Add more documentation here. + WERD_CHOICE *ep_choice; // ep text TODO(rays) delete this. + REJMAP reject_map; // best_choice rejects + BOOL8 tess_failed; + /* + If tess_failed is TRUE, one of the following tests failed when Tess + returned: + - The outword blob list was not the same length as the best_choice string; + - The best_choice string contained ALL blanks; + - The best_choice string was zero length + */ + BOOL8 tess_accepted; // Tess thinks its ok? + BOOL8 tess_would_adapt; // Tess would adapt? + BOOL8 done; // ready for output? + bool small_caps; // word appears to be small caps + bool odd_size; // word is bigger than line or leader dots. + inT8 italic; + inT8 bold; + // The fontinfos are pointers to data owned by the classifier. + const FontInfo* fontinfo; + const FontInfo* fontinfo2; + inT8 fontinfo_id_count; // number of votes + inT8 fontinfo_id2_count; // number of votes + BOOL8 guessed_x_ht; + BOOL8 guessed_caps_ht; + CRUNCH_MODE unlv_crunch_mode; + float x_height; // post match estimate + float caps_height; // post match estimate + float baseline_shift; // post match estimate. + // Certainty score for the spaces either side of this word (LSTM mode). + // MIN this value with the actual word certainty. + float space_certainty; + + /* + To deal with fuzzy spaces we need to be able to combine "words" to form + combinations when we suspect that the gap is a non-space. The (new) text + ord code generates separate words for EVERY fuzzy gap - flags in the word + indicate whether the gap is below the threshold (fuzzy kern) and is thus + NOT a real word break by default, or above the threshold (fuzzy space) and + this is a real word break by default. + + The WERD_RES list contains all these words PLUS "combination" words built + out of (copies of) the words split by fuzzy kerns. The separate parts have + their "part_of_combo" flag set true and should be IGNORED on a default + reading of the list. + + Combination words are FOLLOWED by the sequence of part_of_combo words + which they combine. + */ + BOOL8 combination; //of two fuzzy gap wds + BOOL8 part_of_combo; //part of a combo + BOOL8 reject_spaces; //Reject spacing? + + WERD_RES() { + InitNonPointers(); + InitPointers(); + } + WERD_RES(WERD *the_word) { + InitNonPointers(); + InitPointers(); + word = the_word; + } + // Deep copies everything except the ratings MATRIX. + // To get that use deep_copy below. + WERD_RES(const WERD_RES& source) : ELIST_LINK(source) { + InitPointers(); + *this = source; // see operator= + } + + ~WERD_RES(); + + // Returns the UTF-8 string for the given blob index in the best_choice word, + // given that we know whether we are in a right-to-left reading context. + // This matters for mirrorable characters such as parentheses. We recognize + // characters purely based on their shape on the page, and by default produce + // the corresponding unicode for a left-to-right context. + const char* BestUTF8(int blob_index, bool in_rtl_context) const { + if (blob_index < 0 || best_choice == NULL || + blob_index >= best_choice->length()) + return NULL; + UNICHAR_ID id = best_choice->unichar_id(blob_index); + if (id < 0 || id >= uch_set->size() || id == INVALID_UNICHAR_ID) + return NULL; + UNICHAR_ID mirrored = uch_set->get_mirror(id); + if (in_rtl_context && mirrored > 0 && mirrored != INVALID_UNICHAR_ID) + id = mirrored; + return uch_set->id_to_unichar_ext(id); + } + // Returns the UTF-8 string for the given blob index in the raw_choice word. + const char* RawUTF8(int blob_index) const { + if (blob_index < 0 || blob_index >= raw_choice->length()) + return NULL; + UNICHAR_ID id = raw_choice->unichar_id(blob_index); + if (id < 0 || id >= uch_set->size() || id == INVALID_UNICHAR_ID) + return NULL; + return uch_set->id_to_unichar(id); + } + + UNICHARSET::Direction SymbolDirection(int blob_index) const { + if (best_choice == NULL || + blob_index >= best_choice->length() || + blob_index < 0) + return UNICHARSET::U_OTHER_NEUTRAL; + return uch_set->get_direction(best_choice->unichar_id(blob_index)); + } + + bool AnyRtlCharsInWord() const { + if (uch_set == NULL || best_choice == NULL || best_choice->length() < 1) + return false; + for (int id = 0; id < best_choice->length(); id++) { + int unichar_id = best_choice->unichar_id(id); + if (unichar_id < 0 || unichar_id >= uch_set->size()) + continue; // Ignore illegal chars. + UNICHARSET::Direction dir = + uch_set->get_direction(unichar_id); + if (dir == UNICHARSET::U_RIGHT_TO_LEFT || + dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC || + dir == UNICHARSET::U_ARABIC_NUMBER) + return true; + } + return false; + } + + bool AnyLtrCharsInWord() const { + if (uch_set == NULL || best_choice == NULL || best_choice->length() < 1) + return false; + for (int id = 0; id < best_choice->length(); id++) { + int unichar_id = best_choice->unichar_id(id); + if (unichar_id < 0 || unichar_id >= uch_set->size()) + continue; // Ignore illegal chars. + UNICHARSET::Direction dir = uch_set->get_direction(unichar_id); + if (dir == UNICHARSET::U_LEFT_TO_RIGHT) + return true; + } + return false; + } + + // Return whether the blobs in this WERD_RES 0, 1,... come from an engine + // that gave us the unichars in reading order (as opposed to strict left + // to right). + bool UnicharsInReadingOrder() const { + return best_choice->unichars_in_script_order(); + } + + void InitNonPointers(); + void InitPointers(); + void Clear(); + void ClearResults(); + void ClearWordChoices(); + void ClearRatings(); + + // Deep copies everything except the ratings MATRIX. + // To get that use deep_copy below. + WERD_RES& operator=(const WERD_RES& source); //from this + + void CopySimpleFields(const WERD_RES& source); + + // Initializes a blank (default constructed) WERD_RES from one that has + // already been recognized. + // Use SetupFor*Recognition afterwards to complete the setup and make + // it ready for a retry recognition. + void InitForRetryRecognition(const WERD_RES& source); + + // Sets up the members used in recognition: bln_boxes, chopped_word, + // seam_array, denorm. Returns false if + // the word is empty and sets up fake results. If use_body_size is + // true and row->body_size is set, then body_size will be used for + // blob normalization instead of xheight + ascrise. This flag is for + // those languages that are using CJK pitch model and thus it has to + // be true if and only if tesseract->textord_use_cjk_fp_model is + // true. + // If allow_detailed_fx is true, the feature extractor will receive fine + // precision outline information, allowing smoother features and better + // features on low resolution images. + // The norm_mode sets the default mode for normalization in absence + // of any of the above flags. It should really be a tesseract::OcrEngineMode + // but is declared as int for ease of use with tessedit_ocr_engine_mode. + // Returns false if the word is empty and sets up fake results. + bool SetupForRecognition(const UNICHARSET& unicharset_in, + tesseract::Tesseract* tesseract, Pix* pix, + int norm_mode, + const TBOX* norm_box, bool numeric_mode, + bool use_body_size, bool allow_detailed_fx, + ROW *row, const BLOCK* block); + + // Set up the seam array, bln_boxes, best_choice, and raw_choice to empty + // accumulators from a made chopped word. We presume the fields are already + // empty. + void SetupBasicsFromChoppedWord(const UNICHARSET &unicharset_in); + + // Sets up the members used in recognition for an empty recognition result: + // bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. + void SetupFake(const UNICHARSET& uch); + + // Set the word as having the script of the input unicharset. + void SetupWordScript(const UNICHARSET& unicharset_in); + + // Sets up the blamer_bundle if it is not null, using the initialized denorm. + void SetupBlamerBundle(); + + // Computes the blob_widths and blob_gaps from the chopped_word. + void SetupBlobWidthsAndGaps(); + + // Updates internal data to account for a new SEAM (chop) at the given + // blob_number. Fixes the ratings matrix and states in the choices, as well + // as the blob widths and gaps. + void InsertSeam(int blob_number, SEAM* seam); + + // Returns true if all the word choices except the first have adjust_factors + // worse than the given threshold. + bool AlternativeChoiceAdjustmentsWorseThan(float threshold) const; + + // Returns true if the current word is ambiguous (by number of answers or + // by dangerous ambigs.) + bool IsAmbiguous(); + + // Returns true if the ratings matrix size matches the sum of each of the + // segmentation states. + bool StatesAllValid(); + + // Prints a list of words found if debug is true or the word result matches + // the word_to_debug. + void DebugWordChoices(bool debug, const char* word_to_debug); + + // Prints the top choice along with the accepted/done flags. + void DebugTopChoice(const char* msg) const; + + // Removes from best_choices all choices which are not within a reasonable + // range of the best choice. + void FilterWordChoices(int debug_level); + + // Computes a set of distance thresholds used to control adaption. + // 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, max_rating is placed + // into thresholds. If the classifier was incorrect, the mean 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 min_rating in order to + // prevent trying to make overly tight templates. + // min_rating limits how tight to make a template. + // max_rating limits how loose to make a template. + // rating_margin denotes the amount of margin to put in template. + void ComputeAdaptionThresholds(float certainty_scale, + float min_rating, + float max_rating, + float rating_margin, + float* thresholds); + + // Saves a copy of the word_choice if it has the best unadjusted rating. + // Returns true if the word_choice was the new best. + bool LogNewRawChoice(WERD_CHOICE* word_choice); + // Consumes word_choice by adding it to best_choices, (taking ownership) if + // the certainty for word_choice is some distance of the best choice in + // best_choices, or by deleting the word_choice and returning false. + // The best_choices list is kept in sorted order by rating. Duplicates are + // removed, and the list is kept no longer than max_num_choices in length. + // Returns true if the word_choice is still a valid pointer. + bool LogNewCookedChoice(int max_num_choices, bool debug, + WERD_CHOICE* word_choice); + + // Prints a brief list of all the best choices. + void PrintBestChoices() const; + + // Returns the sum of the widths of the blob between start_blob and last_blob + // inclusive. + int GetBlobsWidth(int start_blob, int last_blob); + // Returns the width of a gap between the specified blob and the next one. + int GetBlobsGap(int blob_index); + + // Returns the BLOB_CHOICE corresponding to the given index in the + // best choice word taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. May return NULL if there is no + // BLOB_CHOICE matching the unichar_id at the given index. + BLOB_CHOICE* GetBlobChoice(int index) const; + + // Returns the BLOB_CHOICE_LIST corresponding to the given index in the + // best choice word taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. + BLOB_CHOICE_LIST* GetBlobChoices(int index) const; + + // Moves the results fields from word to this. This takes ownership of all + // the data, so src can be destructed. + // word1.ConsumeWordResult(word); + // delete word; + // is simpler and faster than: + // word1 = *word; + // delete word; + // as it doesn't need to copy and reallocate anything. + void ConsumeWordResults(WERD_RES* word); + + // Replace the best choice and rebuild box word. + // choice must be from the current best_choices list. + void ReplaceBestChoice(WERD_CHOICE* choice); + + // Builds the rebuild_word and sets the best_state from the chopped_word and + // the best_choice->state. + void RebuildBestState(); + + // Copies the chopped_word to the rebuild_word, faking a best_state as well. + // Also sets up the output box_word. + void CloneChoppedToRebuild(); + + // Sets/replaces the box_word with one made from the rebuild_word. + void SetupBoxWord(); + + // Sets up the script positions in the best_choice using the best_choice + // to get the unichars, and the unicharset to get the target positions. + void SetScriptPositions(); + // Sets all the blobs in all the words (best choice and alternates) to be + // the given position. (When a sub/superscript is recognized as a separate + // word, it falls victim to the rule that a whole word cannot be sub or + // superscript, so this function overrides that problem.) + void SetAllScriptPositions(tesseract::ScriptPos position); + + // Classifies the word with some already-calculated BLOB_CHOICEs. + // The choices are an array of blob_count pointers to BLOB_CHOICE, + // providing a single classifier result for each blob. + // The BLOB_CHOICEs are consumed and the word takes ownership. + // The number of blobs in the box_word must match blob_count. + void FakeClassifyWord(int blob_count, BLOB_CHOICE** choices); + + // Creates a WERD_CHOICE for the word using the top choices from the leading + // diagonal of the ratings matrix. + void FakeWordFromRatings(PermuterType permuter); + + // Copies the best_choice strings to the correct_text for adaption/training. + void BestChoiceToCorrectText(); + + // Merges 2 adjacent blobs in the result if the permanent callback + // class_cb returns other than INVALID_UNICHAR_ID, AND the permanent + // callback box_cb is NULL or returns true, setting the merged blob + // result to the class returned from class_cb. + // Returns true if anything was merged. + bool ConditionalBlobMerge( + TessResultCallback2* class_cb, + TessResultCallback2* box_cb); + + // Merges 2 adjacent blobs in the result (index and index+1) and corrects + // all the data to account for the change. + void MergeAdjacentBlobs(int index); + + // Callback helper for fix_quotes returns a double quote if both + // arguments are quote, otherwise INVALID_UNICHAR_ID. + UNICHAR_ID BothQuotes(UNICHAR_ID id1, UNICHAR_ID id2); + void fix_quotes(); + + // Callback helper for fix_hyphens returns UNICHAR_ID of - if both + // arguments are hyphen, otherwise INVALID_UNICHAR_ID. + UNICHAR_ID BothHyphens(UNICHAR_ID id1, UNICHAR_ID id2); + // Callback helper for fix_hyphens returns true if box1 and box2 overlap + // (assuming both on the same textline, are in order and a chopped em dash.) + bool HyphenBoxesOverlap(const TBOX& box1, const TBOX& box2); + void fix_hyphens(); + + // Callback helper for merge_tess_fails returns a space if both + // arguments are space, otherwise INVALID_UNICHAR_ID. + UNICHAR_ID BothSpaces(UNICHAR_ID id1, UNICHAR_ID id2); + void merge_tess_fails(); + + // Returns a really deep copy of *src, including the ratings MATRIX. + static WERD_RES* deep_copy(const WERD_RES* src) { + WERD_RES* result = new WERD_RES(*src); + // That didn't copy the ratings, but we want a copy if there is one to + // begin with. + if (src->ratings != NULL) + result->ratings = src->ratings->DeepCopy(); + return result; + } + + // Copy blobs from word_res onto this word (eliminating spaces between). + // Since this may be called bidirectionally OR both the BOL and EOL flags. + void copy_on(WERD_RES *word_res) { //from this word + word->set_flag(W_BOL, word->flag(W_BOL) || word_res->word->flag(W_BOL)); + word->set_flag(W_EOL, word->flag(W_EOL) || word_res->word->flag(W_EOL)); + word->copy_on(word_res->word); + } + + // Returns true if the collection of count pieces, starting at start, are all + // natural connected components, ie there are no real chops involved. + bool PiecesAllNatural(int start, int count) const; +}; + +/************************************************************************* + * PAGE_RES_IT - Page results iterator + *************************************************************************/ + +class PAGE_RES_IT { +public: + PAGE_RES * page_res; // page being iterated + + PAGE_RES_IT() { + } // empty contructor + + PAGE_RES_IT(PAGE_RES *the_page_res) { // page result + page_res = the_page_res; + restart_page(); // ready to scan + } + + // Do two PAGE_RES_ITs point at the same word? + // This is much cheaper than cmp(). + bool operator ==(const PAGE_RES_IT &other) const; + + bool operator !=(const PAGE_RES_IT &other) const { return !(*this == other); } + + // Given another PAGE_RES_IT to the same page, + // this before other: -1 + // this equal to other: 0 + // this later than other: 1 + int cmp(const PAGE_RES_IT &other) const; + + WERD_RES *restart_page() { + return start_page(false); // Skip empty blocks. + } + WERD_RES *restart_page_with_empties() { + return start_page(true); // Allow empty blocks. + } + WERD_RES *start_page(bool empty_ok); + + WERD_RES *restart_row(); + + // ============ Methods that mutate the underling structures =========== + // Note that these methods will potentially invalidate other PAGE_RES_ITs + // and are intended to be used only while a single PAGE_RES_IT is active. + // This problem needs to be taken into account if these mutation operators + // are ever provided to PageIterator or its subclasses. + + // Inserts the new_word and a corresponding WERD_RES before the current + // position. The simple fields of the WERD_RES are copied from clone_res and + // the resulting WERD_RES is returned for further setup with best_choice etc. + WERD_RES* InsertSimpleCloneWord(const WERD_RES& clone_res, WERD* new_word); + + // Replaces the current WERD/WERD_RES with the given words. The given words + // contain fake blobs that indicate the position of the characters. These are + // replaced with real blobs from the current word as much as possible. + void ReplaceCurrentWord(tesseract::PointerVector* words); + + // Deletes the current WERD_RES and its underlying WERD. + void DeleteCurrentWord(); + + // Makes the current word a fuzzy space if not already fuzzy. Updates + // corresponding part of combo if required. + void MakeCurrentWordFuzzy(); + + WERD_RES *forward() { // Get next word. + return internal_forward(false, false); + } + // Move forward, but allow empty blocks to show as single NULL words. + WERD_RES *forward_with_empties() { + return internal_forward(false, true); + } + + WERD_RES *forward_paragraph(); // get first word in next non-empty paragraph + WERD_RES *forward_block(); // get first word in next non-empty block + + WERD_RES *prev_word() const { // previous word + return prev_word_res; + } + ROW_RES *prev_row() const { // row of prev word + return prev_row_res; + } + BLOCK_RES *prev_block() const { // block of prev word + return prev_block_res; + } + WERD_RES *word() const { // current word + return word_res; + } + ROW_RES *row() const { // row of current word + return row_res; + } + BLOCK_RES *block() const { // block of cur. word + return block_res; + } + WERD_RES *next_word() const { // next word + return next_word_res; + } + ROW_RES *next_row() const { // row of next word + return next_row_res; + } + BLOCK_RES *next_block() const { // block of next word + return next_block_res; + } + void rej_stat_word(); // for page/block/row + void ResetWordIterator(); + +private: + WERD_RES *internal_forward(bool new_block, bool empty_ok); + + WERD_RES * prev_word_res; // previous word + ROW_RES *prev_row_res; // row of prev word + BLOCK_RES *prev_block_res; // block of prev word + + WERD_RES *word_res; // current word + ROW_RES *row_res; // row of current word + BLOCK_RES *block_res; // block of cur. word + + WERD_RES *next_word_res; // next word + ROW_RES *next_row_res; // row of next word + BLOCK_RES *next_block_res; // block of next word + + BLOCK_RES_IT block_res_it; // iterators + ROW_RES_IT row_res_it; + WERD_RES_IT word_res_it; +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.cpp new file mode 100644 index 0000000..5daa7ae --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.cpp @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////// +// File: params_training_featdef.cpp +// Description: Utility functions for params training features. +// Author: David Eger +// Created: Mon Jun 11 11:26:42 PDT 2012 +// +// (C) Copyright 2012, 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 + +#include "params_training_featdef.h" + +namespace tesseract { + +int ParamsTrainingFeatureByName(const char *name) { + if (name == NULL) + return -1; + int array_size = sizeof(kParamsTrainingFeatureTypeName) / + sizeof(kParamsTrainingFeatureTypeName[0]); + for (int i = 0; i < array_size; i++) { + if (kParamsTrainingFeatureTypeName[i] == NULL) + continue; + if (strcmp(name, kParamsTrainingFeatureTypeName[i]) == 0) + return i; + } + return -1; +} + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.h new file mode 100644 index 0000000..6e021f0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/params_training_featdef.h @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////// +// File: params_training_featdef.h +// Description: Feature definitions for params training. +// Author: Rika Antonova +// Created: Mon Nov 28 11:26:42 PDT 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_WORDREC_PARAMS_TRAINING_FEATDEF_H_ +#define TESSERACT_WORDREC_PARAMS_TRAINING_FEATDEF_H_ + +#include "genericvector.h" +#include "strngs.h" + +namespace tesseract { + +// Maximum number of unichars in the small and medium sized words +static const int kMaxSmallWordUnichars = 3; +static const int kMaxMediumWordUnichars = 6; + +// Raw features extracted from a single OCR hypothesis. +// The features are normalized (by outline length or number of unichars as +// appropriate) real-valued quantities with unbounded range and +// unknown distribution. +// Normalization / binarization of these features is done at a later stage. +// Note: when adding new fields to this enum make sure to modify +// kParamsTrainingFeatureTypeName +enum kParamsTrainingFeatureType { + // Digits + PTRAIN_DIGITS_SHORT, // 0 + PTRAIN_DIGITS_MED, // 1 + PTRAIN_DIGITS_LONG, // 2 + // Number or pattern (NUMBER_PERM, USER_PATTERN_PERM) + PTRAIN_NUM_SHORT, // 3 + PTRAIN_NUM_MED, // 4 + PTRAIN_NUM_LONG, // 5 + // Document word (DOC_DAWG_PERM) + PTRAIN_DOC_SHORT, // 6 + PTRAIN_DOC_MED, // 7 + PTRAIN_DOC_LONG, // 8 + // Word (SYSTEM_DAWG_PERM, USER_DAWG_PERM, COMPOUND_PERM) + PTRAIN_DICT_SHORT, // 9 + PTRAIN_DICT_MED, // 10 + PTRAIN_DICT_LONG, // 11 + // Frequent word (FREQ_DAWG_PERM) + PTRAIN_FREQ_SHORT, // 12 + PTRAIN_FREQ_MED, // 13 + PTRAIN_FREQ_LONG, // 14 + PTRAIN_SHAPE_COST_PER_CHAR, // 15 + PTRAIN_NGRAM_COST_PER_CHAR, // 16 + PTRAIN_NUM_BAD_PUNC, // 17 + PTRAIN_NUM_BAD_CASE, // 18 + PTRAIN_XHEIGHT_CONSISTENCY, // 19 + PTRAIN_NUM_BAD_CHAR_TYPE, // 20 + PTRAIN_NUM_BAD_SPACING, // 21 + PTRAIN_NUM_BAD_FONT, // 22 + PTRAIN_RATING_PER_CHAR, // 23 + + PTRAIN_NUM_FEATURE_TYPES +}; + +static const char * const kParamsTrainingFeatureTypeName[] = { + "PTRAIN_DIGITS_SHORT", // 0 + "PTRAIN_DIGITS_MED", // 1 + "PTRAIN_DIGITS_LONG", // 2 + "PTRAIN_NUM_SHORT", // 3 + "PTRAIN_NUM_MED", // 4 + "PTRAIN_NUM_LONG", // 5 + "PTRAIN_DOC_SHORT", // 6 + "PTRAIN_DOC_MED", // 7 + "PTRAIN_DOC_LONG", // 8 + "PTRAIN_DICT_SHORT", // 9 + "PTRAIN_DICT_MED", // 10 + "PTRAIN_DICT_LONG", // 11 + "PTRAIN_FREQ_SHORT", // 12 + "PTRAIN_FREQ_MED", // 13 + "PTRAIN_FREQ_LONG", // 14 + "PTRAIN_SHAPE_COST_PER_CHAR", // 15 + "PTRAIN_NGRAM_COST_PER_CHAR", // 16 + "PTRAIN_NUM_BAD_PUNC", // 17 + "PTRAIN_NUM_BAD_CASE", // 18 + "PTRAIN_XHEIGHT_CONSISTENCY", // 19 + "PTRAIN_NUM_BAD_CHAR_TYPE", // 20 + "PTRAIN_NUM_BAD_SPACING", // 21 + "PTRAIN_NUM_BAD_FONT", // 22 + "PTRAIN_RATING_PER_CHAR", // 23 +}; + +// Returns the index of the given feature (by name), +// or -1 meaning the feature is unknown. +int ParamsTrainingFeatureByName(const char *name); + + +// Entry with features extracted from a single OCR hypothesis for a word. +struct ParamsTrainingHypothesis { + ParamsTrainingHypothesis() : cost(0.0) { + memset(features, 0, sizeof(float) * PTRAIN_NUM_FEATURE_TYPES); + } + ParamsTrainingHypothesis(const ParamsTrainingHypothesis &other) { + memcpy(features, other.features, + sizeof(float) * PTRAIN_NUM_FEATURE_TYPES); + str = other.str; + cost = other.cost; + } + float features[PTRAIN_NUM_FEATURE_TYPES]; + STRING str; // string corresponding to word hypothesis (for debugging) + float cost; // path cost computed by segsearch +}; + +// A list of hypotheses explored during one run of segmentation search. +typedef GenericVector ParamsTrainingHypothesisList; + +// A bundle that accumulates all of the hypothesis lists explored during all +// of the runs of segmentation search on a word (e.g. a list of hypotheses +// explored on PASS1, PASS2, fix xheight pass, etc). +class ParamsTrainingBundle { + public: + ParamsTrainingBundle() {} + // Starts a new hypothesis list. + // Should be called at the beginning of a new run of the segmentation search. + void StartHypothesisList() { + hyp_list_vec.push_back(ParamsTrainingHypothesisList()); + } + // Adds a new ParamsTrainingHypothesis to the current hypothesis list + // and returns the reference to the newly added entry. + ParamsTrainingHypothesis &AddHypothesis( + const ParamsTrainingHypothesis &other) { + if (hyp_list_vec.empty()) StartHypothesisList(); + hyp_list_vec.back().push_back(ParamsTrainingHypothesis(other)); + return hyp_list_vec.back().back(); + } + + GenericVector hyp_list_vec; +}; + +} // namespace tesseract + +#endif // TESSERACT_WORDREC_PARAMS_TRAINING_FEATDEF_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.cpp new file mode 100644 index 0000000..cf3289f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.cpp @@ -0,0 +1,378 @@ +/********************************************************************** + * File: pdblock.c (Formerly pdblk.c) + * Description: PDBLK member functions and iterator functions. + * Author: Ray Smith + * Created: Fri Mar 15 09:41:28 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include "allheaders.h" +#include "blckerr.h" +#include "pdblock.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#define BLOCK_LABEL_HEIGHT 150 //char height of block id + +CLISTIZE (PDBLK) +/********************************************************************** + * PDBLK::PDBLK + * + * Constructor for a simple rectangular block. + **********************************************************************/ +PDBLK::PDBLK ( //rectangular block +inT16 xmin, //bottom left +inT16 ymin, inT16 xmax, //top right +inT16 ymax): box (ICOORD (xmin, ymin), ICOORD (xmax, ymax)) { + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + hand_poly = NULL; + left_it.set_to_list (&leftside); + right_it.set_to_list (&rightside); + //make default box + left_it.add_to_end (new ICOORDELT (xmin, ymin)); + left_it.add_to_end (new ICOORDELT (xmin, ymax)); + right_it.add_to_end (new ICOORDELT (xmax, ymin)); + right_it.add_to_end (new ICOORDELT (xmax, ymax)); + index_ = 0; +} + + +/********************************************************************** + * PDBLK::set_sides + * + * Sets left and right vertex lists + **********************************************************************/ + +void PDBLK::set_sides( //set vertex lists + ICOORDELT_LIST *left, //left vertices + ICOORDELT_LIST *right //right vertices + ) { + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + leftside.clear (); + left_it.move_to_first (); + left_it.add_list_before (left); + rightside.clear (); + right_it.move_to_first (); + right_it.add_list_before (right); +} + +/********************************************************************** + * PDBLK::contains + * + * Return TRUE if the given point is within the block. + **********************************************************************/ + +BOOL8 PDBLK::contains( //test containment + ICOORD pt //point to test + ) { + BLOCK_RECT_IT it = this; //rectangle iterator + ICOORD bleft, tright; //corners of rectangle + + for (it.start_block (); !it.cycled_rects (); it.forward ()) { + //get rectangle + it.bounding_box (bleft, tright); + //inside rect + if (pt.x () >= bleft.x () && pt.x () <= tright.x () + && pt.y () >= bleft.y () && pt.y () <= tright.y ()) + return TRUE; //is inside + } + return FALSE; //not inside +} + + +/********************************************************************** + * PDBLK::move + * + * Reposition block + **********************************************************************/ + +void PDBLK::move( // reposition block + const ICOORD vec // by vector + ) { + ICOORDELT_IT it(&leftside); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + *(it.data ()) += vec; + + it.set_to_list (&rightside); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + *(it.data ()) += vec; + + box.move (vec); +} + +// Returns a binary Pix mask with a 1 pixel for every pixel within the +// block. Rotates the coordinate system by rerotation prior to rendering. +Pix* PDBLK::render_mask(const FCOORD& rerotation, TBOX* mask_box) { + TBOX rotated_box(box); + rotated_box.rotate(rerotation); + Pix* pix = pixCreate(rotated_box.width(), rotated_box.height(), 1); + if (hand_poly != NULL) { + // We are going to rotate, so get a deep copy of the points and + // make a new POLY_BLOCK with it. + ICOORDELT_LIST polygon; + polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy); + POLY_BLOCK image_block(&polygon, hand_poly->isA()); + image_block.rotate(rerotation); + // Block outline is a polygon, so use a PB_LINE_IT to get the + // rasterized interior. (Runs of interior pixels on a line.) + PB_LINE_IT *lines = new PB_LINE_IT(&image_block); + for (int y = box.bottom(); y < box.top(); ++y) { + ICOORDELT_LIST* segments = lines->get_line(y); + if (!segments->empty()) { + ICOORDELT_IT s_it(segments); + // Each element of segments is a start x and x size of the + // run of interior pixels. + for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) { + int start = s_it.data()->x(); + int xext = s_it.data()->y(); + // Set the run of pixels to 1. + pixRasterop(pix, start - rotated_box.left(), + rotated_box.height() - 1 - (y - rotated_box.bottom()), + xext, 1, PIX_SET, NULL, 0, 0); + } + } + delete segments; + } + delete lines; + } else { + // Just fill the whole block as there is only a bounding box. + pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(), + PIX_SET, NULL, 0, 0); + } + if (mask_box != NULL) *mask_box = rotated_box; + return pix; +} + + +/********************************************************************** + * PDBLK::plot + * + * Plot the outline of a block in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PDBLK::plot( //draw outline + ScrollView* window, //window to draw in + inT32 serial, //serial number + ScrollView::Color colour //colour to draw in + ) { + ICOORD startpt; //start of outline + ICOORD endpt; //end of outline + ICOORD prevpt; //previous point + ICOORDELT_IT it = &leftside; //iterator + + //set the colour + window->Pen(colour); + window->TextAttributes("Times", BLOCK_LABEL_HEIGHT, false, false, false); + + if (hand_poly != NULL) { + hand_poly->plot(window, serial); + } else if (!leftside.empty ()) { + startpt = *(it.data ()); //bottom left corner + // tprintf("Block %d bottom left is (%d,%d)\n", + // serial,startpt.x(),startpt.y()); + char temp_buff[34]; + #if defined(__UNIX__) || defined(MINGW) + sprintf(temp_buff, INT32FORMAT, serial); + #else + ultoa (serial, temp_buff, 10); + #endif + window->Text(startpt.x (), startpt.y (), temp_buff); + + window->SetCursor(startpt.x (), startpt.y ()); + do { + prevpt = *(it.data ()); //previous point + it.forward (); //move to next point + //draw round corner + window->DrawTo(prevpt.x (), it.data ()->y ()); + window->DrawTo(it.data ()->x (), it.data ()->y ()); + } + while (!it.at_last ()); //until end of list + endpt = *(it.data ()); //end point + + //other side of boundary + window->SetCursor(startpt.x (), startpt.y ()); + it.set_to_list (&rightside); + prevpt = startpt; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + //draw round corner + window->DrawTo(prevpt.x (), it.data ()->y ()); + window->DrawTo(it.data ()->x (), it.data ()->y ()); + prevpt = *(it.data ()); //previous point + } + //close boundary + window->DrawTo(endpt.x(), endpt.y()); + } +} +#endif + +/********************************************************************** + * PDBLK::operator= + * + * Assignment - duplicate the block structure, but with an EMPTY row list. + **********************************************************************/ + +PDBLK & PDBLK::operator= ( //assignment +const PDBLK & source //from this +) { + // this->ELIST_LINK::operator=(source); + if (!leftside.empty ()) + leftside.clear (); + if (!rightside.empty ()) + rightside.clear (); + leftside.deep_copy(&source.leftside, &ICOORDELT::deep_copy); + rightside.deep_copy(&source.rightside, &ICOORDELT::deep_copy); + box = source.box; + return *this; +} + + +/********************************************************************** + * BLOCK_RECT_IT::BLOCK_RECT_IT + * + * Construct a block rectangle iterator. + **********************************************************************/ + +BLOCK_RECT_IT::BLOCK_RECT_IT ( +//iterate rectangles +PDBLK * blkptr //from block +):left_it (&blkptr->leftside), right_it (&blkptr->rightside) { + block = blkptr; //remember block + //non empty list + if (!blkptr->leftside.empty ()) { + start_block(); //ready for iteration + } +} + + +/********************************************************************** + * BLOCK_RECT_IT::set_to_block + * + * Start a new block. + **********************************************************************/ + +void BLOCK_RECT_IT::set_to_block( //start (new) block + PDBLK *blkptr) { //block to start + block = blkptr; //remember block + //set iterators + left_it.set_to_list (&blkptr->leftside); + right_it.set_to_list (&blkptr->rightside); + if (!blkptr->leftside.empty ()) + start_block(); //ready for iteration +} + + +/********************************************************************** + * BLOCK_RECT_IT::start_block + * + * Restart a block. + **********************************************************************/ + +void BLOCK_RECT_IT::start_block() { //start (new) block + left_it.move_to_first (); + right_it.move_to_first (); + left_it.mark_cycle_pt (); + right_it.mark_cycle_pt (); + ymin = left_it.data ()->y (); //bottom of first box + ymax = left_it.data_relative (1)->y (); + if (right_it.data_relative (1)->y () < ymax) + //smallest step + ymax = right_it.data_relative (1)->y (); +} + + +/********************************************************************** + * BLOCK_RECT_IT::forward + * + * Move to the next rectangle in the block. + **********************************************************************/ + +void BLOCK_RECT_IT::forward() { //next rectangle + if (!left_it.empty ()) { //non-empty list + if (left_it.data_relative (1)->y () == ymax) + left_it.forward (); //move to meet top + if (right_it.data_relative (1)->y () == ymax) + right_it.forward (); + //last is special + if (left_it.at_last () || right_it.at_last ()) { + left_it.move_to_first (); //restart + right_it.move_to_first (); + //now at bottom + ymin = left_it.data ()->y (); + } + else { + ymin = ymax; //new bottom + } + //next point + ymax = left_it.data_relative (1)->y (); + if (right_it.data_relative (1)->y () < ymax) + //least step forward + ymax = right_it.data_relative (1)->y (); + } +} + + +/********************************************************************** + * BLOCK_LINE_IT::get_line + * + * Get the the start and width of a line in the block. + **********************************************************************/ + +inT16 BLOCK_LINE_IT::get_line( //get a line + inT16 y, //line to get + inT16 &xext //output extent + ) { + ICOORD bleft; //bounding box + ICOORD tright; //of block & rect + + //get block box + block->bounding_box (bleft, tright); + if (y < bleft.y () || y >= tright.y ()) { + // block->print(stderr,FALSE); + BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); + } + + //get rectangle box + rect_it.bounding_box (bleft, tright); + //inside rectangle + if (y >= bleft.y () && y < tright.y ()) { + //width of line + xext = tright.x () - bleft.x (); + return bleft.x (); //start of line + } + for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) { + //get rectangle box + rect_it.bounding_box (bleft, tright); + //inside rectangle + if (y >= bleft.y () && y < tright.y ()) { + //width of line + xext = tright.x () - bleft.x (); + return bleft.x (); //start of line + } + } + LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); + return 0; //dummy to stop warning +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.h new file mode 100644 index 0000000..cf29b78 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/pdblock.h @@ -0,0 +1,175 @@ +/********************************************************************** + * File: pdblock.h (Formerly pdblk.h) + * Description: Page block class definition. + * Author: Ray Smith + * Created: Thu Mar 14 17:32:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PDBLOCK_H +#define PDBLOCK_H + +#include "clst.h" +#include "strngs.h" +#include "polyblk.h" + +class DLLSYM PDBLK; //forward decl +struct Pix; + +CLISTIZEH (PDBLK) +///page block +class PDBLK { + friend class BLOCK_RECT_IT; //< block iterator + + public: + /// empty constructor + PDBLK() { + hand_poly = NULL; + index_ = 0; + } + /// simple constructor + PDBLK(inT16 xmin, //< bottom left + inT16 ymin, + inT16 xmax, //< top right + inT16 ymax); + + /// set vertex lists + ///@param left list of left vertices + ///@param right list of right vertices + void set_sides(ICOORDELT_LIST *left, ICOORDELT_LIST *right); + + /// destructor + ~PDBLK() { delete hand_poly; } + + POLY_BLOCK *poly_block() const { return hand_poly; } + /// set the poly block + void set_poly_block(POLY_BLOCK *blk) { hand_poly = blk; } + /// get box + void bounding_box(ICOORD &bottom_left, // bottom left + ICOORD &top_right) const { // topright + bottom_left = box.botleft(); + top_right = box.topright(); + } + /// get real box + const TBOX &bounding_box() const { return box; } + + int index() const { return index_; } + void set_index(int value) { index_ = value; } + + /// is pt inside block + BOOL8 contains(ICOORD pt); + + /// reposition block + void move(const ICOORD vec); // by vector + + // Returns a binary Pix mask with a 1 pixel for every pixel within the + // block. Rotates the coordinate system by rerotation prior to rendering. + // If not NULL, mask_box is filled with the position box of the returned + // mask image. + Pix *render_mask(const FCOORD &rerotation, TBOX *mask_box); + +#ifndef GRAPHICS_DISABLED + /// draw histogram + ///@param window window to draw in + ///@param serial serial number + ///@param colour colour to draw in + void plot(ScrollView *window, inT32 serial, ScrollView::Color colour); +#endif // GRAPHICS_DISABLED + + /// assignment + ///@param source from this + PDBLK &operator=(const PDBLK &source); + + protected: + POLY_BLOCK *hand_poly; //< weird as well + ICOORDELT_LIST leftside; //< left side vertices + ICOORDELT_LIST rightside; //< right side vertices + TBOX box; //< bounding box + int index_; //< Serial number of this block. +}; + +class DLLSYM BLOCK_RECT_IT //rectangle iterator +{ + public: + ///constructor + ///@param blkptr block to iterate + BLOCK_RECT_IT(PDBLK *blkptr); + + ///start (new) block + void set_to_block ( + PDBLK * blkptr); //block to iterate + + ///start iteration + void start_block(); + + ///next rectangle + void forward(); + + ///test end + BOOL8 cycled_rects() { + return left_it.cycled_list () && right_it.cycled_list (); + } + + ///current rectangle + ///@param bleft bottom left + ///@param tright top right + void bounding_box(ICOORD &bleft, + ICOORD &tright) { + //bottom left + bleft = ICOORD (left_it.data ()->x (), ymin); + //top right + tright = ICOORD (right_it.data ()->x (), ymax); + } + + private: + inT16 ymin; //< bottom of rectangle + inT16 ymax; //< top of rectangle + PDBLK *block; //< block to iterate + ICOORDELT_IT left_it; //< boundary iterators + ICOORDELT_IT right_it; +}; + +///rectangle iterator +class DLLSYM BLOCK_LINE_IT +{ + public: + ///constructor + ///@param blkptr from block + BLOCK_LINE_IT (PDBLK * blkptr) + :rect_it (blkptr) { + block = blkptr; //remember block + } + + ///start (new) block + ///@param blkptr block to start + void set_to_block (PDBLK * blkptr) { + block = blkptr; //remember block + //set iterator + rect_it.set_to_block (blkptr); + } + + ///get a line + ///@param y line to get + ///@param xext output extent + inT16 get_line(inT16 y, + inT16 &xext); + + private: + PDBLK * block; //< block to iterate + BLOCK_RECT_IT rect_it; //< rectangle iterator +}; + +int decreasing_top_order(const void *row1, + const void *row2); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/points.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/points.cpp new file mode 100644 index 0000000..6569fba --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/points.cpp @@ -0,0 +1,145 @@ +/********************************************************************** + * File: points.c (Formerly coords.c) + * Description: Member functions for coordinate classes. + * Author: Ray Smith + * Created: Fri Mar 15 08:58:17 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif // _MSC_VER + +#include +#include "helpers.h" +#include "ndminx.h" +#include "serialis.h" +#include "points.h" + +ELISTIZE (ICOORDELT) //turn to list +bool FCOORD::normalise() { //Convert to unit vec + float len = length (); + + if (len < 0.0000000001) { + return false; + } + xcoord /= len; + ycoord /= len; + return true; +} + +// Set from the given x,y, shrinking the vector to fit if needed. +void ICOORD::set_with_shrink(int x, int y) { + // Fit the vector into an ICOORD, which is 16 bit. + int factor = 1; + int max_extent = MAX(abs(x), abs(y)); + if (max_extent > MAX_INT16) + factor = max_extent / MAX_INT16 + 1; + xcoord = x / factor; + ycoord = y / factor; +} + +// The fortran/basic sgn function returns -1, 0, 1 if x < 0, x == 0, x > 0 +// respectively. +static int sign(int x) { + if (x < 0) + return -1; + else + return x > 0 ? 1 : 0; +} + +// Writes to the given file. Returns false in case of error. +bool ICOORD::Serialize(FILE* fp) const { + if (fwrite(&xcoord, sizeof(xcoord), 1, fp) != 1) return false; + if (fwrite(&ycoord, sizeof(ycoord), 1, fp) != 1) 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 ICOORD::DeSerialize(bool swap, FILE* fp) { + if (fread(&xcoord, sizeof(xcoord), 1, fp) != 1) return false; + if (fread(&ycoord, sizeof(ycoord), 1, fp) != 1) return false; + if (swap) { + ReverseN(&xcoord, sizeof(xcoord)); + ReverseN(&ycoord, sizeof(ycoord)); + } + return true; +} + +// Setup for iterating over the pixels in a vector by the well-known +// Bresenham rendering algorithm. +// Starting with major/2 in the accumulator, on each step add major_step, +// and then add minor to the accumulator. When the accumulator >= major +// subtract major and step a minor step. + +void ICOORD::setup_render(ICOORD* major_step, ICOORD* minor_step, + int* major, int* minor) const { + int abs_x = abs(xcoord); + int abs_y = abs(ycoord); + if (abs_x >= abs_y) { + // X-direction is major. + major_step->xcoord = sign(xcoord); + major_step->ycoord = 0; + minor_step->xcoord = 0; + minor_step->ycoord = sign(ycoord); + *major = abs_x; + *minor = abs_y; + } else { + // Y-direction is major. + major_step->xcoord = 0; + major_step->ycoord = sign(ycoord); + minor_step->xcoord = sign(xcoord); + minor_step->ycoord = 0; + *major = abs_y; + *minor = abs_x; + } +} + +// Returns the standard feature direction corresponding to this. +// See binary_angle_plus_pi below for a description of the direction. +uinT8 FCOORD::to_direction() const { + return binary_angle_plus_pi(angle()); +} +// Sets this with a unit vector in the given standard feature direction. +void FCOORD::from_direction(uinT8 direction) { + double radians = angle_from_direction(direction); + xcoord = cos(radians); + ycoord = sin(radians); +} + +// Converts an angle in radians (from ICOORD::angle or FCOORD::angle) to a +// standard feature direction as an unsigned angle in 256ths of a circle +// measured anticlockwise from (-1, 0). +uinT8 FCOORD::binary_angle_plus_pi(double radians) { + return Modulo(IntCastRounded((radians + M_PI) * 128.0 / M_PI), 256); +} +// Inverse of binary_angle_plus_pi returns an angle in radians for the +// given standard feature direction. +double FCOORD::angle_from_direction(uinT8 direction) { + return direction * M_PI / 128.0 - M_PI; +} + +// Returns the point on the given line nearest to this, ie the point such +// that the vector point->this is perpendicular to the line. +// The line is defined as a line_point and a dir_vector for its direction. +FCOORD FCOORD::nearest_pt_on_line(const FCOORD& line_point, + const FCOORD& dir_vector) const { + FCOORD point_vector(*this - line_point); + // The dot product (%) is |dir_vector||point_vector|cos theta, so dividing by + // the square of the length of dir_vector gives us the fraction of dir_vector + // to add to line1 to get the appropriate point, so + // result = line1 + lambda dir_vector. + double lambda = point_vector % dir_vector / dir_vector.sqlength(); + return line_point + (dir_vector * lambda); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/points.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/points.h new file mode 100644 index 0000000..33e0732 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/points.h @@ -0,0 +1,323 @@ +/********************************************************************** + * File: points.h (Formerly coords.h) + * Description: Coordinate class definitions. + * Author: Ray Smith + * Created: Fri Mar 15 08:32:45 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POINTS_H +#define POINTS_H + +#include +#include +#include "elst.h" + +class FCOORD; + +///integer coordinate +class ICOORD +{ + friend class FCOORD; + + public: + ///empty constructor + ICOORD() { + xcoord = ycoord = 0; //default zero + } + ///constructor + ///@param xin x value + ///@param yin y value + ICOORD(inT16 xin, + inT16 yin) { + xcoord = xin; + ycoord = yin; + } + ///destructor + ~ICOORD () { + } + + ///access function + inT16 x() const { + return xcoord; + } + ///access_function + inT16 y() const { + return ycoord; + } + + ///rewrite function + void set_x(inT16 xin) { + xcoord = xin; //write new value + } + ///rewrite function + void set_y(inT16 yin) { //value to set + ycoord = yin; + } + + /// Set from the given x,y, shrinking the vector to fit if needed. + void set_with_shrink(int x, int y); + + ///find sq length + float sqlength() const { + return (float) (xcoord * xcoord + ycoord * ycoord); + } + + ///find length + float length() const { + return (float) sqrt (sqlength ()); + } + + ///sq dist between pts + float pt_to_pt_sqdist(const ICOORD &pt) const { + ICOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + ///Distance between pts + float pt_to_pt_dist(const ICOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + ///find angle + float angle() const { + return (float) atan2 ((double) ycoord, (double) xcoord); + } + + ///test equality + BOOL8 operator== (const ICOORD & other) const { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + ///test inequality + BOOL8 operator!= (const ICOORD & other) const { + return xcoord != other.xcoord || ycoord != other.ycoord; + } + ///rotate 90 deg anti + friend ICOORD operator! (const ICOORD &); + ///unary minus + friend ICOORD operator- (const ICOORD &); + ///add + friend ICOORD operator+ (const ICOORD &, const ICOORD &); + ///add + friend ICOORD & operator+= (ICOORD &, const ICOORD &); + ///subtract + friend ICOORD operator- (const ICOORD &, const ICOORD &); + ///subtract + friend ICOORD & operator-= (ICOORD &, const ICOORD &); + ///scalar product + friend inT32 operator% (const ICOORD &, const ICOORD &); + ///cross product + friend inT32 operator *(const ICOORD &, + const ICOORD &); + ///multiply + friend ICOORD operator *(const ICOORD &, + inT16); + ///multiply + friend ICOORD operator *(inT16, + const ICOORD &); + ///multiply + friend ICOORD & operator*= (ICOORD &, inT16); + ///divide + friend ICOORD operator/ (const ICOORD &, inT16); + ///divide + friend ICOORD & operator/= (ICOORD &, inT16); + ///rotate + ///@param vec by vector + void rotate(const FCOORD& vec); + + /// Setup for iterating over the pixels in a vector by the well-known + /// Bresenham rendering algorithm. + /// Starting with major/2 in the accumulator, on each step move by + /// major_step, and then add minor to the accumulator. When + /// accumulator >= major subtract major and also move by minor_step. + void setup_render(ICOORD* major_step, ICOORD* minor_step, + int* major, int* minor) const; + + // 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); + + protected: + inT16 xcoord; //< x value + inT16 ycoord; //< y value +}; + +class DLLSYM ICOORDELT:public ELIST_LINK, public ICOORD + //embedded coord list +{ + public: + ///empty constructor + ICOORDELT() { + } + ///constructor from ICOORD + ICOORDELT (ICOORD icoord):ICOORD (icoord) { + } + ///constructor + ///@param xin x value + ///@param yin y value + ICOORDELT(inT16 xin, + inT16 yin) { + xcoord = xin; + ycoord = yin; + } + + static ICOORDELT* deep_copy(const ICOORDELT* src) { + ICOORDELT* elt = new ICOORDELT; + *elt = *src; + return elt; + } + +}; + +ELISTIZEH (ICOORDELT) +class DLLSYM FCOORD +{ + public: + ///empty constructor + FCOORD() { + } + ///constructor + ///@param xvalue x value + ///@param yvalue y value + FCOORD(float xvalue, + float yvalue) { + xcoord = xvalue; //set coords + ycoord = yvalue; + } + FCOORD( //make from ICOORD + ICOORD icoord) { //coords to set + xcoord = icoord.xcoord; + ycoord = icoord.ycoord; + } + + float x() const { //get coords + return xcoord; + } + float y() const { + return ycoord; + } + ///rewrite function + void set_x(float xin) { + xcoord = xin; //write new value + } + ///rewrite function + void set_y(float yin) { //value to set + ycoord = yin; + } + + ///find sq length + float sqlength() const { + return xcoord * xcoord + ycoord * ycoord; + } + + ///find length + float length() const { + return (float) sqrt (sqlength ()); + } + + ///sq dist between pts + float pt_to_pt_sqdist(const FCOORD &pt) const { + FCOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + ///Distance between pts + float pt_to_pt_dist(const FCOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + ///find angle + float angle() const { + return (float) atan2 (ycoord, xcoord); + } + // Returns the standard feature direction corresponding to this. + // See binary_angle_plus_pi below for a description of the direction. + uinT8 to_direction() const; + // Sets this with a unit vector in the given standard feature direction. + void from_direction(uinT8 direction); + + // Converts an angle in radians (from ICOORD::angle or FCOORD::angle) to a + // standard feature direction as an unsigned angle in 256ths of a circle + // measured anticlockwise from (-1, 0). + static uinT8 binary_angle_plus_pi(double angle); + // Inverse of binary_angle_plus_pi returns an angle in radians for the + // given standard feature direction. + static double angle_from_direction(uinT8 direction); + // Returns the point on the given line nearest to this, ie the point such + // that the vector point->this is perpendicular to the line. + // The line is defined as a line_point and a dir_vector for its direction. + // dir_vector need not be a unit vector. + FCOORD nearest_pt_on_line(const FCOORD& line_point, + const FCOORD& dir_vector) const; + + ///Convert to unit vec + bool normalise(); + + ///test equality + BOOL8 operator== (const FCOORD & other) { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + ///test inequality + BOOL8 operator!= (const FCOORD & other) { + return xcoord != other.xcoord || ycoord != other.ycoord; + } + ///rotate 90 deg anti + friend FCOORD operator! (const FCOORD &); + ///unary minus + friend FCOORD operator- (const FCOORD &); + ///add + friend FCOORD operator+ (const FCOORD &, const FCOORD &); + ///add + friend FCOORD & operator+= (FCOORD &, const FCOORD &); + ///subtract + friend FCOORD operator- (const FCOORD &, const FCOORD &); + ///subtract + friend FCOORD & operator-= (FCOORD &, const FCOORD &); + ///scalar product + friend float operator% (const FCOORD &, const FCOORD &); + ///cross product + friend float operator *(const FCOORD &, const FCOORD &); + ///multiply + friend FCOORD operator *(const FCOORD &, float); + ///multiply + friend FCOORD operator *(float, const FCOORD &); + + ///multiply + friend FCOORD & operator*= (FCOORD &, float); + ///divide + friend FCOORD operator/ (const FCOORD &, float); + ///rotate + ///@param vec by vector + void rotate(const FCOORD vec); + // unrotate - undo a rotate(vec) + // @param vec by vector + void unrotate(const FCOORD &vec); + ///divide + friend FCOORD & operator/= (FCOORD &, float); + + private: + float xcoord; //2 floating coords + float ycoord; +}; + +#include "ipoints.h" /*do inline funcs */ +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.cpp new file mode 100644 index 0000000..7597349 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.cpp @@ -0,0 +1,584 @@ +/********************************************************************** + * File: polyaprx.cpp (Formerly polygon.c) + * Description: Code for polygonal approximation from old edgeprog. + * Author: Ray Smith + * Created: Thu Nov 25 11:42:04 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#ifdef __UNIX__ +#include +#endif +#define FASTEDGELENGTH 256 +#include "polyaprx.h" +#include "params.h" +#include "tprintf.h" + +#define EXTERN + +EXTERN BOOL_VAR(poly_debug, FALSE, "Debug old poly"); +EXTERN BOOL_VAR(poly_wide_objects_better, TRUE, + "More accurate approx on wide things"); + +#define FIXED 4 /*OUTLINE point is fixed */ + +#define RUNLENGTH 1 /*length of run */ + +#define DIR 2 /*direction of run */ + +#define FLAGS 0 + +#define fixed_dist 20 //really an int_variable +#define approx_dist 15 //really an int_variable + +const int par1 = 4500 / (approx_dist * approx_dist); +const int par2 = 6750 / (approx_dist * approx_dist); + + +/********************************************************************** + * tesspoly_outline + * + * Approximate an outline from chain codes form using the old tess algorithm. + * If allow_detailed_fx is true, the EDGEPTs in the returned TBLOB + * contain pointers to the input C_OUTLINEs that enable higher-resolution + * feature extraction that does not use the polygonal approximation. + **********************************************************************/ + + +TESSLINE* ApproximateOutline(bool allow_detailed_fx, C_OUTLINE* c_outline) { + TBOX loop_box; // bounding box + inT32 area; // loop area + EDGEPT stack_edgepts[FASTEDGELENGTH]; // converted path + EDGEPT* edgepts = stack_edgepts; + + // Use heap memory if the stack buffer is not big enough. + if (c_outline->pathlength() > FASTEDGELENGTH) + edgepts = new EDGEPT[c_outline->pathlength()]; + + loop_box = c_outline->bounding_box(); + area = loop_box.height(); + if (!poly_wide_objects_better && loop_box.width() > area) + area = loop_box.width(); + area *= area; + edgesteps_to_edgepts(c_outline, edgepts); + fix2(edgepts, area); + EDGEPT* edgept = poly2(edgepts, area); // 2nd approximation. + EDGEPT* startpt = edgept; + EDGEPT* result = NULL; + EDGEPT* prev_result = NULL; + do { + EDGEPT* new_pt = new EDGEPT; + new_pt->pos = edgept->pos; + new_pt->prev = prev_result; + if (prev_result == NULL) { + result = new_pt; + } else { + prev_result->next = new_pt; + new_pt->prev = prev_result; + } + if (allow_detailed_fx) { + new_pt->src_outline = edgept->src_outline; + new_pt->start_step = edgept->start_step; + new_pt->step_count = edgept->step_count; + } + prev_result = new_pt; + edgept = edgept->next; + } + while (edgept != startpt); + prev_result->next = result; + result->prev = prev_result; + if (edgepts != stack_edgepts) + delete [] edgepts; + return TESSLINE::BuildFromOutlineList(result); +} + + +/********************************************************************** + * edgesteps_to_edgepts + * + * Convert a C_OUTLINE to EDGEPTs. + **********************************************************************/ + +EDGEPT * +edgesteps_to_edgepts ( //convert outline +C_OUTLINE * c_outline, //input +EDGEPT edgepts[] //output is array +) { + inT32 length; //steps in path + ICOORD pos; //current coords + inT32 stepindex; //current step + inT32 stepinc; //increment + inT32 epindex; //current EDGEPT + inT32 count; //repeated steps + ICOORD vec; //for this 8 step + ICOORD prev_vec; + inT8 epdir; //of this step + DIR128 prevdir; //prvious dir + DIR128 dir; //of this step + + pos = c_outline->start_pos (); //start of loop + length = c_outline->pathlength (); + stepindex = 0; + epindex = 0; + prevdir = -1; + count = 0; + int prev_stepindex = 0; + do { + dir = c_outline->step_dir (stepindex); + vec = c_outline->step (stepindex); + if (stepindex < length - 1 + && c_outline->step_dir (stepindex + 1) - dir == -32) { + dir += 128 - 16; + vec += c_outline->step (stepindex + 1); + stepinc = 2; + } + else + stepinc = 1; + if (count == 0) { + prevdir = dir; + prev_vec = vec; + } + if (prevdir.get_dir () != dir.get_dir ()) { + edgepts[epindex].pos.x = pos.x (); + edgepts[epindex].pos.y = pos.y (); + prev_vec *= count; + edgepts[epindex].vec.x = prev_vec.x (); + edgepts[epindex].vec.y = prev_vec.y (); + pos += prev_vec; + edgepts[epindex].flags[RUNLENGTH] = count; + edgepts[epindex].prev = &edgepts[epindex - 1]; + edgepts[epindex].flags[FLAGS] = 0; + edgepts[epindex].next = &edgepts[epindex + 1]; + prevdir += 64; + epdir = (DIR128) 0 - prevdir; + epdir >>= 4; + epdir &= 7; + edgepts[epindex].flags[DIR] = epdir; + edgepts[epindex].src_outline = c_outline; + edgepts[epindex].start_step = prev_stepindex; + edgepts[epindex].step_count = stepindex - prev_stepindex; + epindex++; + prevdir = dir; + prev_vec = vec; + count = 1; + prev_stepindex = stepindex; + } + else + count++; + stepindex += stepinc; + } + while (stepindex < length); + edgepts[epindex].pos.x = pos.x (); + edgepts[epindex].pos.y = pos.y (); + prev_vec *= count; + edgepts[epindex].vec.x = prev_vec.x (); + edgepts[epindex].vec.y = prev_vec.y (); + pos += prev_vec; + edgepts[epindex].flags[RUNLENGTH] = count; + edgepts[epindex].flags[FLAGS] = 0; + edgepts[epindex].src_outline = c_outline; + edgepts[epindex].start_step = prev_stepindex; + edgepts[epindex].step_count = stepindex - prev_stepindex; + edgepts[epindex].prev = &edgepts[epindex - 1]; + edgepts[epindex].next = &edgepts[0]; + prevdir += 64; + epdir = (DIR128) 0 - prevdir; + epdir >>= 4; + epdir &= 7; + edgepts[epindex].flags[DIR] = epdir; + edgepts[0].prev = &edgepts[epindex]; + ASSERT_HOST (pos.x () == c_outline->start_pos ().x () + && pos.y () == c_outline->start_pos ().y ()); + return &edgepts[0]; +} + + +/********************************************************************** + *fix2(start,area) fixes points on the outline according to a trial method* + **********************************************************************/ + +//#pragma OPT_LEVEL 1 /*stop compiler bugs*/ + +void fix2( //polygonal approx + EDGEPT *start, /*loop to approimate */ + int area) { + EDGEPT *edgept; /*current point */ + EDGEPT *edgept1; + EDGEPT *loopstart; /*modified start of loop */ + EDGEPT *linestart; /*start of line segment */ + int dir1, dir2; /*directions of line */ + int sum1, sum2; /*lengths in dir1,dir2 */ + int stopped; /*completed flag */ + int fixed_count; //no of fixed points + int d01, d12, d23, gapmin; + TPOINT d01vec, d12vec, d23vec; + EDGEPT *edgefix, *startfix; + EDGEPT *edgefix0, *edgefix1, *edgefix2, *edgefix3; + + edgept = start; /*start of loop */ + while (((edgept->flags[DIR] - edgept->prev->flags[DIR] + 1) & 7) < 3 + && (dir1 = + (edgept->prev->flags[DIR] - edgept->next->flags[DIR]) & 7) != 2 + && dir1 != 6) + edgept = edgept->next; /*find suitable start */ + loopstart = edgept; /*remember start */ + + stopped = 0; /*not finished yet */ + edgept->flags[FLAGS] |= FIXED; /*fix it */ + do { + linestart = edgept; /*possible start of line */ + dir1 = edgept->flags[DIR]; /*first direction */ + /*length of dir1 */ + sum1 = edgept->flags[RUNLENGTH]; + edgept = edgept->next; + dir2 = edgept->flags[DIR]; /*2nd direction */ + /*length in dir2 */ + sum2 = edgept->flags[RUNLENGTH]; + if (((dir1 - dir2 + 1) & 7) < 3) { + while (edgept->prev->flags[DIR] == edgept->next->flags[DIR]) { + edgept = edgept->next; /*look at next */ + if (edgept->flags[DIR] == dir1) + /*sum lengths */ + sum1 += edgept->flags[RUNLENGTH]; + else + sum2 += edgept->flags[RUNLENGTH]; + } + + if (edgept == loopstart) + stopped = 1; /*finished */ + if (sum2 + sum1 > 2 + && linestart->prev->flags[DIR] == dir2 + && (linestart->prev->flags[RUNLENGTH] > + linestart->flags[RUNLENGTH] || sum2 > sum1)) { + /*start is back one */ + linestart = linestart->prev; + linestart->flags[FLAGS] |= FIXED; + } + + if (((edgept->next->flags[DIR] - edgept->flags[DIR] + 1) & 7) >= 3 + || (edgept->flags[DIR] == dir1 && sum1 >= sum2) + || ((edgept->prev->flags[RUNLENGTH] < edgept->flags[RUNLENGTH] + || (edgept->flags[DIR] == dir2 && sum2 >= sum1)) + && linestart->next != edgept)) + edgept = edgept->next; + } + /*sharp bend */ + edgept->flags[FLAGS] |= FIXED; + } + /*do whole loop */ + while (edgept != loopstart && !stopped); + + edgept = start; + do { + if (((edgept->flags[RUNLENGTH] >= 8) && + (edgept->flags[DIR] != 2) && (edgept->flags[DIR] != 6)) || + ((edgept->flags[RUNLENGTH] >= 8) && + ((edgept->flags[DIR] == 2) || (edgept->flags[DIR] == 6)))) { + edgept->flags[FLAGS] |= FIXED; + edgept1 = edgept->next; + edgept1->flags[FLAGS] |= FIXED; + } + edgept = edgept->next; + } + while (edgept != start); + + edgept = start; + do { + /*single fixed step */ + if (edgept->flags[FLAGS] & FIXED && edgept->flags[RUNLENGTH] == 1 + /*and neighours free */ + && edgept->next->flags[FLAGS] & FIXED && (edgept->prev->flags[FLAGS] & FIXED) == 0 + /*same pair of dirs */ + && (edgept->next->next->flags[FLAGS] & FIXED) == 0 && edgept->prev->flags[DIR] == edgept->next->flags[DIR] && edgept->prev->prev->flags[DIR] == edgept->next->next->flags[DIR] + && ((edgept->prev->flags[DIR] - edgept->flags[DIR] + 1) & 7) < 3) { + /*unfix it */ + edgept->flags[FLAGS] &= ~FIXED; + edgept->next->flags[FLAGS] &= ~FIXED; + } + edgept = edgept->next; /*do all points */ + } + while (edgept != start); /*until finished */ + + stopped = 0; + if (area < 450) + area = 450; + + gapmin = area * fixed_dist * fixed_dist / 44000; + + edgept = start; + fixed_count = 0; + do { + if (edgept->flags[FLAGS] & FIXED) + fixed_count++; + edgept = edgept->next; + } + while (edgept != start); + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix0 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix1 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix2 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix3 = edgept; + + startfix = edgefix2; + + do { + if (fixed_count <= 3) + break; //already too few + point_diff (d12vec, edgefix1->pos, edgefix2->pos); + d12 = LENGTH (d12vec); + // TODO(rays) investigate this change: + // Only unfix a point if it is part of a low-curvature section + // of outline and the total angle change of the outlines is + // less than 90 degrees, ie the scalar product is positive. + // if (d12 <= gapmin && SCALAR(edgefix0->vec, edgefix2->vec) > 0) { + if (d12 <= gapmin) { + point_diff (d01vec, edgefix0->pos, edgefix1->pos); + d01 = LENGTH (d01vec); + point_diff (d23vec, edgefix2->pos, edgefix3->pos); + d23 = LENGTH (d23vec); + if (d01 > d23) { + edgefix2->flags[FLAGS] &= ~FIXED; + fixed_count--; + } + else { + edgefix1->flags[FLAGS] &= ~FIXED; + fixed_count--; + edgefix1 = edgefix2; + } + } + else { + edgefix0 = edgefix1; + edgefix1 = edgefix2; + } + edgefix2 = edgefix3; + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) { + if (edgept == startfix) + stopped = 1; + edgept = edgept->next; + } + edgefix3 = edgept; + edgefix = edgefix2; + } + while ((edgefix != startfix) && (!stopped)); +} + + +//#pragma OPT_LEVEL 2 /*stop compiler bugs*/ + +/********************************************************************** + *poly2(startpt,area,path) applies a second approximation to the outline + *using the points which have been fixed by the first approximation* + **********************************************************************/ + +EDGEPT *poly2( //second poly + EDGEPT *startpt, /*start of loop */ + int area /*area of blob box */ + ) { + EDGEPT *edgept; /*current outline point */ + EDGEPT *loopstart; /*starting point */ + EDGEPT *linestart; /*start of line */ + int edgesum; /*correction count */ + + if (area < 1200) + area = 1200; /*minimum value */ + + loopstart = NULL; /*not found it yet */ + edgept = startpt; /*start of loop */ + + do { + /*current point fixed */ + if (edgept->flags[FLAGS] & FIXED + /*and next not */ + && (edgept->next->flags[FLAGS] & FIXED) == 0) { + loopstart = edgept; /*start of repoly */ + break; + } + edgept = edgept->next; /*next point */ + } + while (edgept != startpt); /*until found or finished */ + + if (loopstart == NULL && (startpt->flags[FLAGS] & FIXED) == 0) { + /*fixed start of loop */ + startpt->flags[FLAGS] |= FIXED; + loopstart = startpt; /*or start of loop */ + } + if (loopstart) { + do { + edgept = loopstart; /*first to do */ + do { + linestart = edgept; + edgesum = 0; /*sum of lengths */ + do { + /*sum lengths */ + edgesum += edgept->flags[RUNLENGTH]; + edgept = edgept->next; /*move on */ + } + while ((edgept->flags[FLAGS] & FIXED) == 0 + && edgept != loopstart && edgesum < 126); + if (poly_debug) + tprintf + ("Poly2:starting at (%d,%d)+%d=(%d,%d),%d to (%d,%d)\n", + linestart->pos.x, linestart->pos.y, linestart->flags[DIR], + linestart->vec.x, linestart->vec.y, edgesum, edgept->pos.x, + edgept->pos.y); + /*reapproximate */ + cutline(linestart, edgept, area); + + while ((edgept->next->flags[FLAGS] & FIXED) + && edgept != loopstart) + edgept = edgept->next; /*look for next non-fixed */ + } + /*do all the loop */ + while (edgept != loopstart); + edgesum = 0; + do { + if (edgept->flags[FLAGS] & FIXED) + edgesum++; + edgept = edgept->next; + } + //count fixed pts + while (edgept != loopstart); + if (edgesum < 3) + area /= 2; //must have 3 pts + } + while (edgesum < 3); + do { + linestart = edgept; + do { + edgept = edgept->next; + } + while ((edgept->flags[FLAGS] & FIXED) == 0); + linestart->next = edgept; + edgept->prev = linestart; + linestart->vec.x = edgept->pos.x - linestart->pos.x; + linestart->vec.y = edgept->pos.y - linestart->pos.y; + } + while (edgept != loopstart); + } + else + edgept = startpt; /*start of loop */ + + loopstart = edgept; /*new start */ + return loopstart; /*correct exit */ +} + + +/********************************************************************** + *cutline(first,last,area) straightens out a line by partitioning + *and joining the ends by a straight line* + **********************************************************************/ + +void cutline( //recursive refine + EDGEPT *first, /*ends of line */ + EDGEPT *last, + int area /*area of object */ + ) { + EDGEPT *edge; /*current edge */ + TPOINT vecsum; /*vector sum */ + int vlen; /*approx length of vecsum */ + TPOINT vec; /*accumulated vector */ + EDGEPT *maxpoint; /*worst point */ + int maxperp; /*max deviation */ + int perp; /*perp distance */ + int ptcount; /*no of points */ + int squaresum; /*sum of perps */ + + edge = first; /*start of line */ + if (edge->next == last) + return; /*simple line */ + + /*vector sum */ + vecsum.x = last->pos.x - edge->pos.x; + vecsum.y = last->pos.y - edge->pos.y; + if (vecsum.x == 0 && vecsum.y == 0) { + /*special case */ + vecsum.x = -edge->prev->vec.x; + vecsum.y = -edge->prev->vec.y; + } + /*absolute value */ + vlen = vecsum.x > 0 ? vecsum.x : -vecsum.x; + if (vecsum.y > vlen) + vlen = vecsum.y; /*maximum */ + else if (-vecsum.y > vlen) + vlen = -vecsum.y; /*absolute value */ + + vec.x = edge->vec.x; /*accumulated vector */ + vec.y = edge->vec.y; + maxperp = 0; /*none yet */ + squaresum = ptcount = 0; + edge = edge->next; /*move to actual point */ + maxpoint = edge; /*in case there isn't one */ + do { + perp = CROSS (vec, vecsum); /*get perp distance */ + if (perp != 0) { + perp *= perp; /*squared deviation */ + } + squaresum += perp; /*sum squares */ + ptcount++; /*count points */ + if (poly_debug) + tprintf ("Cutline:Final perp=%d\n", perp); + if (perp > maxperp) { + maxperp = perp; + maxpoint = edge; /*find greatest deviation */ + } + vec.x += edge->vec.x; /*accumulate vectors */ + vec.y += edge->vec.y; + edge = edge->next; + } + while (edge != last); /*test all line */ + + perp = LENGTH (vecsum); + ASSERT_HOST (perp != 0); + + if (maxperp < 256 * MAX_INT16) { + maxperp <<= 8; + maxperp /= perp; /*true max perp */ + } + else { + maxperp /= perp; + maxperp <<= 8; /*avoid overflow */ + } + if (squaresum < 256 * MAX_INT16) + /*mean squared perp */ + perp = (squaresum << 8) / (perp * ptcount); + else + /*avoid overflow */ + perp = (squaresum / perp << 8) / ptcount; + + if (poly_debug) + tprintf ("Cutline:A=%d, max=%.2f(%.2f%%), msd=%.2f(%.2f%%)\n", + area, maxperp / 256.0, maxperp * 200.0 / area, + perp / 256.0, perp * 300.0 / area); + if (maxperp * par1 >= 10 * area || perp * par2 >= 10 * area || vlen >= 126) { + maxpoint->flags[FLAGS] |= FIXED; + /*partitions */ + cutline(first, maxpoint, area); + cutline(maxpoint, last, area); + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.h new file mode 100644 index 0000000..45e3c75 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyaprx.h @@ -0,0 +1,44 @@ +/********************************************************************** + * File: polyaprx.h (Formerly polygon.h) + * Description: Code for polygonal approximation from old edgeprog. + * Author: Ray Smith + * Created: Thu Nov 25 11:42:04 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POLYAPRX_H +#define POLYAPRX_H + +#include "blobs.h" +#include "coutln.h" + +// convert a chain-coded input to the old OUTLINE approximation +TESSLINE* ApproximateOutline(bool allow_detailed_fx, C_OUTLINE *c_outline); +EDGEPT *edgesteps_to_edgepts ( //convert outline +C_OUTLINE * c_outline, //input +EDGEPT edgepts[] //output is array +); +void fix2( //polygonal approx + EDGEPT *start, /*loop to approimate */ + int area); +EDGEPT *poly2( //second poly + EDGEPT *startpt, /*start of loop */ + int area /*area of blob box */ + ); +void cutline( //recursive refine + EDGEPT *first, /*ends of line */ + EDGEPT *last, + int area /*area of object */ + ); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.cpp new file mode 100644 index 0000000..b5ca2e1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.cpp @@ -0,0 +1,421 @@ +/********************************************************************** + * File: polyblk.c (Formerly poly_block.c) + * Description: Polygonal blocks + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include +#include +#include "elst.h" +#include "polyblk.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#define PBLOCK_LABEL_SIZE 150 +#define INTERSECTING MAX_INT16 + +int lessthan(const void *first, const void *second); + +POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) { + ICOORDELT_IT v = &vertices; + + vertices.clear(); + v.move_to_first(); + v.add_list_before(points); + compute_bb(); + type = t; +} + +// Initialize from box coordinates. +POLY_BLOCK::POLY_BLOCK(const TBOX& box, PolyBlockType t) { + vertices.clear(); + ICOORDELT_IT v = &vertices; + v.move_to_first(); + v.add_to_end(new ICOORDELT(box.left(), box.top())); + v.add_to_end(new ICOORDELT(box.left(), box.bottom())); + v.add_to_end(new ICOORDELT(box.right(), box.bottom())); + v.add_to_end(new ICOORDELT(box.right(), box.top())); + compute_bb(); + type = t; +} + +/** + * @name POLY_BLOCK::compute_bb + * + * Compute the bounding box from the outline points. + */ + +void POLY_BLOCK::compute_bb() { //constructor + ICOORD ibl, itr; //integer bb + ICOORD botleft; //bounding box + ICOORD topright; + ICOORD pos; //current pos; + ICOORDELT_IT pts = &vertices; //iterator + + botleft = *pts.data (); + topright = botleft; + do { + pos = *pts.data (); + if (pos.x () < botleft.x ()) + //get bounding box + botleft = ICOORD (pos.x (), botleft.y ()); + if (pos.y () < botleft.y ()) + botleft = ICOORD (botleft.x (), pos.y ()); + if (pos.x () > topright.x ()) + topright = ICOORD (pos.x (), topright.y ()); + if (pos.y () > topright.y ()) + topright = ICOORD (topright.x (), pos.y ()); + pts.forward (); + } + while (!pts.at_first ()); + ibl = ICOORD (botleft.x (), botleft.y ()); + itr = ICOORD (topright.x (), topright.y ()); + box = TBOX (ibl, itr); +} + + +/** + * @name POLY_BLOCK::winding_number + * + * Return the winding number of the outline around the given point. + * @param point point to wind around + */ + +inT16 POLY_BLOCK::winding_number(const ICOORD &point) { + inT16 count; //winding count + ICOORD pt; //current point + ICOORD vec; //point to current point + ICOORD vvec; //current point to next point + inT32 cross; //cross product + ICOORDELT_IT it = &vertices; //iterator + + count = 0; + do { + pt = *it.data (); + vec = pt - point; + vvec = *it.data_relative (1) - pt; + //crossing the line + if (vec.y () <= 0 && vec.y () + vvec.y () > 0) { + cross = vec * vvec; //cross product + if (cross > 0) + count++; //crossing right half + else if (cross == 0) + return INTERSECTING; //going through point + } + else if (vec.y () > 0 && vec.y () + vvec.y () <= 0) { + cross = vec * vvec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + else if (vec.y () == 0 && vec.x () == 0) + return INTERSECTING; + it.forward (); + } + while (!it.at_first ()); + return count; //winding number +} + + +/// @return true if other is inside this. +bool POLY_BLOCK::contains(POLY_BLOCK *other) { + inT16 count; // winding count + ICOORDELT_IT it = &vertices; // iterator + ICOORD vertex; + + if (!box.overlap (*(other->bounding_box ()))) + return false; // can't be contained + + /* check that no vertex of this is inside other */ + + do { + vertex = *it.data (); + // get winding number + count = other->winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return false; + it.forward (); + } + while (!it.at_first ()); + + /* check that all vertices of other are inside this */ + + //switch lists + it.set_to_list (other->points ()); + do { + vertex = *it.data (); + //try other way round + count = winding_number (vertex); + if (count != INTERSECTING) + if (count == 0) + return false; + it.forward (); + } + while (!it.at_first ()); + return true; +} + + +/** + * @name POLY_BLOCK::rotate + * + * Rotate the POLY_BLOCK. + * @param rotation cos, sin of angle + */ + +void POLY_BLOCK::rotate(FCOORD rotation) { + FCOORD pos; //current pos; + ICOORDELT *pt; //current point + ICOORDELT_IT pts = &vertices; //iterator + + do { + pt = pts.data (); + pos.set_x (pt->x ()); + pos.set_y (pt->y ()); + pos.rotate (rotation); + pt->set_x ((inT16) (floor (pos.x () + 0.5))); + pt->set_y ((inT16) (floor (pos.y () + 0.5))); + pts.forward (); + } + while (!pts.at_first ()); + compute_bb(); +} + +/** + * @name POLY_BLOCK::reflect_in_y_axis + * + * Reflect the coords of the polygon in the y-axis. (Flip the sign of x.) + */ + +void POLY_BLOCK::reflect_in_y_axis() { + ICOORDELT *pt; // current point + ICOORDELT_IT pts = &vertices; // Iterator. + + do { + pt = pts.data(); + pt->set_x(-pt->x()); + pts.forward(); + } + while (!pts.at_first()); + compute_bb(); +} + + +/** + * POLY_BLOCK::move + * + * Move the POLY_BLOCK. + * @param shift x,y translation vector + */ + +void POLY_BLOCK::move(ICOORD shift) { + ICOORDELT *pt; //current point + ICOORDELT_IT pts = &vertices; //iterator + + do { + pt = pts.data (); + *pt += shift; + pts.forward (); + } + while (!pts.at_first ()); + compute_bb(); +} + + +#ifndef GRAPHICS_DISABLED +void POLY_BLOCK::plot(ScrollView* window, inT32 num) { + ICOORDELT_IT v = &vertices; + + window->Pen(ColorForPolyBlockType(type)); + + v.move_to_first (); + + if (num > 0) { + window->TextAttributes("Times", 80, false, false, false); + char temp_buff[34]; + #if defined(__UNIX__) || defined(MINGW) + sprintf(temp_buff, INT32FORMAT, num); + #else + ltoa (num, temp_buff, 10); + #endif + window->Text(v.data ()->x (), v.data ()->y (), temp_buff); + } + + window->SetCursor(v.data ()->x (), v.data ()->y ()); + for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) { + window->DrawTo(v.data ()->x (), v.data ()->y ()); + } + v.move_to_first (); + window->DrawTo(v.data ()->x (), v.data ()->y ()); +} + + +void POLY_BLOCK::fill(ScrollView* window, ScrollView::Color colour) { + inT16 y; + inT16 width; + PB_LINE_IT *lines; + ICOORDELT_LIST *segments; + ICOORDELT_IT s_it; + + lines = new PB_LINE_IT (this); + window->Pen(colour); + + for (y = this->bounding_box ()->bottom (); + y <= this->bounding_box ()->top (); y++) { + segments = lines->get_line (y); + if (!segments->empty ()) { + s_it.set_to_list (segments); + for (s_it.mark_cycle_pt (); !s_it.cycled_list (); s_it.forward ()) { + // Note different use of ICOORDELT, x coord is x coord of pixel + // at the start of line segment, y coord is length of line segment + // Last pixel is start pixel + length. + width = s_it.data ()->y (); + window->SetCursor(s_it.data ()->x (), y); + window->DrawTo(s_it.data ()->x () + (float) width, y); + } + } + } + + delete lines; +} +#endif + + +/// @return true if the polygons of other and this overlap. +bool POLY_BLOCK::overlap(POLY_BLOCK *other) { + inT16 count; // winding count + ICOORDELT_IT it = &vertices; // iterator + ICOORD vertex; + + if (!box.overlap(*(other->bounding_box()))) + return false; // can't be any overlap. + + /* see if a vertex of this is inside other */ + + do { + vertex = *it.data (); + // get winding number + count = other->winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return true; + it.forward (); + } + while (!it.at_first ()); + + /* see if a vertex of other is inside this */ + + // switch lists + it.set_to_list (other->points ()); + do { + vertex = *it.data(); + // try other way round + count = winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return true; + it.forward (); + } + while (!it.at_first ()); + return false; +} + + +ICOORDELT_LIST *PB_LINE_IT::get_line(inT16 y) { + ICOORDELT_IT v, r; + ICOORDELT_LIST *result; + ICOORDELT *x, *current, *previous; + float fy, fx; + + fy = (float) (y + 0.5); + result = new ICOORDELT_LIST (); + r.set_to_list (result); + v.set_to_list (block->points ()); + + for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) { + if (((v.data_relative (-1)->y () > y) && (v.data ()->y () <= y)) + || ((v.data_relative (-1)->y () <= y) && (v.data ()->y () > y))) { + previous = v.data_relative (-1); + current = v.data (); + fx = (float) (0.5 + previous->x () + + (current->x () - previous->x ()) * (fy - + previous->y ()) / + (current->y () - previous->y ())); + x = new ICOORDELT ((inT16) fx, 0); + r.add_to_end (x); + } + } + + if (!r.empty ()) { + r.sort (lessthan); + for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) + x = r.data (); + for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) { + r.data ()->set_y (r.data_relative (1)->x () - r.data ()->x ()); + r.forward (); + delete (r.extract ()); + } + } + + return result; +} + + +int lessthan(const void *first, const void *second) { + ICOORDELT *p1 = (*(ICOORDELT **) first); + ICOORDELT *p2 = (*(ICOORDELT **) second); + + if (p1->x () < p2->x ()) + return (-1); + else if (p1->x () > p2->x ()) + return (1); + else + return (0); +} + +#ifndef GRAPHICS_DISABLED +/// Returns a color to draw the given type. +ScrollView::Color POLY_BLOCK::ColorForPolyBlockType(PolyBlockType type) { + // Keep kPBColors in sync with PolyBlockType. + const ScrollView::Color kPBColors[PT_COUNT] = { + ScrollView::WHITE, // Type is not yet known. Keep as the 1st element. + ScrollView::BLUE, // Text that lives inside a column. + ScrollView::CYAN, // Text that spans more than one column. + ScrollView::MEDIUM_BLUE, // Text that is in a cross-column pull-out region. + ScrollView::AQUAMARINE, // Partition belonging to an equation region. + ScrollView::SKY_BLUE, // Partition belonging to an inline equation region. + ScrollView::MAGENTA, // Partition belonging to a table region. + ScrollView::GREEN, // Text-line runs vertically. + ScrollView::LIGHT_BLUE, // Text that belongs to an image. + ScrollView::RED, // Image that lives inside a column. + ScrollView::YELLOW, // Image that spans more than one column. + ScrollView::ORANGE, // Image in a cross-column pull-out region. + ScrollView::BROWN, // Horizontal Line. + ScrollView::DARK_GREEN, // Vertical Line. + ScrollView::GREY // Lies outside of any column. + }; + if (type >= 0 && type < PT_COUNT) { + return kPBColors[type]; + } + return ScrollView::WHITE; +} +#endif // GRAPHICS_DISABLED diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.h new file mode 100644 index 0000000..edfaac7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/polyblk.h @@ -0,0 +1,113 @@ +/********************************************************************** + * File: polyblk.h (Formerly poly_block.h) + * Description: Polygonal blocks + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef POLYBLK_H +#define POLYBLK_H + +#include "publictypes.h" +#include "elst.h" +#include "points.h" +#include "rect.h" +#include "scrollview.h" + +class DLLSYM POLY_BLOCK { + public: + POLY_BLOCK() { + } + // Initialize from box coordinates. + POLY_BLOCK(const TBOX& box, PolyBlockType type); + POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType type); + ~POLY_BLOCK () { + } + + TBOX *bounding_box() { // access function + return &box; + } + + ICOORDELT_LIST *points() { // access function + return &vertices; + } + + void compute_bb(); + + PolyBlockType isA() const { + return type; + } + + bool IsText() const { + return PTIsTextType(type); + } + + // Rotate about the origin by the given rotation. (Analogous to + // multiplying by a complex number. + void rotate(FCOORD rotation); + // Reflect the coords of the polygon in the y-axis. (Flip the sign of x.) + void reflect_in_y_axis(); + // Move by adding shift to all coordinates. + void move(ICOORD shift); + + void plot(ScrollView* window, inT32 num); + + #ifndef GRAPHICS_DISABLED + void fill(ScrollView* window, ScrollView::Color colour); + #endif // GRAPHICS_DISABLED + + // Returns true if other is inside this. + bool contains(POLY_BLOCK *other); + + // Returns true if the polygons of other and this overlap. + bool overlap(POLY_BLOCK *other); + + // Returns the winding number of this around the test_pt. + // Positive for anticlockwise, negative for clockwise, and zero for + // test_pt outside this. + inT16 winding_number(const ICOORD &test_pt); + + #ifndef GRAPHICS_DISABLED + // Static utility functions to handle the PolyBlockType. + // Returns a color to draw the given type. + static ScrollView::Color ColorForPolyBlockType(PolyBlockType type); + #endif // GRAPHICS_DISABLED + + private: + ICOORDELT_LIST vertices; // vertices + TBOX box; // bounding box + PolyBlockType type; // Type of this region. +}; + +// Class to iterate the scanlines of a polygon. +class DLLSYM PB_LINE_IT { + public: + PB_LINE_IT(POLY_BLOCK *blkptr) { + block = blkptr; + } + + void set_to_block(POLY_BLOCK * blkptr) { + block = blkptr; + } + + // Returns a list of runs of pixels for the given y coord. + // Each element of the returned list is the start (x) and extent(y) of + // a run inside the region. + // Delete the returned list after use. + ICOORDELT_LIST *get_line(inT16 y); + + private: + POLY_BLOCK * block; +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.cpp new file mode 100644 index 0000000..47a1800 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.cpp @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////// +// File: publictypes.cpp +// Description: Types used in both the API and internally +// Author: Ray Smith +// Created: Wed Mar 03 11:17: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 "publictypes.h" + +/** String name for each block type. Keep in sync with PolyBlockType. */ +const char* kPolyBlockNames[] = { + "Unknown", + "Flowing Text", + "Heading Text", + "Pullout Text", + "Equation", + "Inline Equation", + "Table", + "Vertical Text", + "Caption Text", + "Flowing Image", + "Heading Image", + "Pullout Image", + "Horizontal Line", + "Vertical Line", + "Noise", + "" // End marker for testing that sizes match. +}; diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.h new file mode 100644 index 0000000..3835a26 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/publictypes.h @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////// +// File: publictypes.h +// Description: Types used in both the API and internally +// Author: Ray Smith +// Created: Wed Mar 03 09:22:53 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCSTRUCT_PUBLICTYPES_H__ +#define TESSERACT_CCSTRUCT_PUBLICTYPES_H__ + +// This file contains types that are used both by the API and internally +// to Tesseract. In order to decouple the API from Tesseract and prevent cyclic +// dependencies, THIS FILE SHOULD NOT DEPEND ON ANY OTHER PART OF TESSERACT. +// Restated: It is OK for low-level Tesseract files to include publictypes.h, +// but not for the low-level tesseract code to include top-level API code. +// This file should not use other Tesseract types, as that would drag +// their includes into the API-level. +// API-level code should include apitypes.h in preference to this file. + +/** Number of printers' points in an inch. The unit of the pointsize return. */ +const int kPointsPerInch = 72; + +/** + * Possible types for a POLY_BLOCK or ColPartition. + * Must be kept in sync with kPBColors in polyblk.cpp and PTIs*Type functions + * below, as well as kPolyBlockNames in publictypes.cpp. + * Used extensively by ColPartition, and POLY_BLOCK. +*/ +enum PolyBlockType { + PT_UNKNOWN, // Type is not yet known. Keep as the first element. + PT_FLOWING_TEXT, // Text that lives inside a column. + PT_HEADING_TEXT, // Text that spans more than one column. + PT_PULLOUT_TEXT, // Text that is in a cross-column pull-out region. + PT_EQUATION, // Partition belonging to an equation region. + PT_INLINE_EQUATION, // Partition has inline equation. + PT_TABLE, // Partition belonging to a table region. + PT_VERTICAL_TEXT, // Text-line runs vertically. + PT_CAPTION_TEXT, // Text that belongs to an image. + PT_FLOWING_IMAGE, // Image that lives inside a column. + PT_HEADING_IMAGE, // Image that spans more than one column. + PT_PULLOUT_IMAGE, // Image that is in a cross-column pull-out region. + PT_HORZ_LINE, // Horizontal Line. + PT_VERT_LINE, // Vertical Line. + PT_NOISE, // Lies outside of any column. + PT_COUNT +}; + +/** Returns true if PolyBlockType is of horizontal line type */ +inline bool PTIsLineType(PolyBlockType type) { + return type == PT_HORZ_LINE || type == PT_VERT_LINE; +} +/** Returns true if PolyBlockType is of image type */ +inline bool PTIsImageType(PolyBlockType type) { + return type == PT_FLOWING_IMAGE || type == PT_HEADING_IMAGE || + type == PT_PULLOUT_IMAGE; +} +/** Returns true if PolyBlockType is of text type */ +inline bool PTIsTextType(PolyBlockType type) { + return type == PT_FLOWING_TEXT || type == PT_HEADING_TEXT || + type == PT_PULLOUT_TEXT || type == PT_TABLE || + type == PT_VERTICAL_TEXT || type == PT_CAPTION_TEXT || + type == PT_INLINE_EQUATION; +} +// Returns true if PolyBlockType is of pullout(inter-column) type +inline bool PTIsPulloutType(PolyBlockType type) { + return type == PT_PULLOUT_IMAGE || type == PT_PULLOUT_TEXT; +} + +/** String name for each block type. Keep in sync with PolyBlockType. */ +extern const char* kPolyBlockNames[]; + +namespace tesseract { + /** + * +------------------+ Orientation Example: + * | 1 Aaaa Aaaa Aaaa | ==================== + * | Aaa aa aaa aa | To left is a diagram of some (1) English and + * | aaaaaa A aa aaa. | (2) Chinese text and a (3) photo credit. + * | 2 | + * | ####### c c C | Upright Latin characters are represented as A and a. + * | ####### c c c | '<' represents a latin character rotated + * | < ####### c c c | anti-clockwise 90 degrees. + * | < ####### c c | + * | < ####### . c | Upright Chinese characters are represented C and c. + * | 3 ####### c | + * +------------------+ NOTA BENE: enum values here should match goodoc.proto + + * If you orient your head so that "up" aligns with Orientation, + * then the characters will appear "right side up" and readable. + * + * In the example above, both the English and Chinese paragraphs are oriented + * so their "up" is the top of the page (page up). The photo credit is read + * with one's head turned leftward ("up" is to page left). + * + * The values of this enum match the convention of Tesseract's osdetect.h + */ + enum Orientation { + ORIENTATION_PAGE_UP = 0, + ORIENTATION_PAGE_RIGHT = 1, + ORIENTATION_PAGE_DOWN = 2, + ORIENTATION_PAGE_LEFT = 3, + }; + + /** + * The grapheme clusters within a line of text are laid out logically + * in this direction, judged when looking at the text line rotated so that + * its Orientation is "page up". + * + * For English text, the writing direction is left-to-right. For the + * Chinese text in the above example, the writing direction is top-to-bottom. + */ + enum WritingDirection { + WRITING_DIRECTION_LEFT_TO_RIGHT = 0, + WRITING_DIRECTION_RIGHT_TO_LEFT = 1, + WRITING_DIRECTION_TOP_TO_BOTTOM = 2, + }; + + /** + * The text lines are read in the given sequence. + * + * In English, the order is top-to-bottom. + * In Chinese, vertical text lines are read right-to-left. Mongolian is + * written in vertical columns top to bottom like Chinese, but the lines + * order left-to right. + * + * Note that only some combinations make sense. For example, + * WRITING_DIRECTION_LEFT_TO_RIGHT implies TEXTLINE_ORDER_TOP_TO_BOTTOM + */ + enum TextlineOrder { + TEXTLINE_ORDER_LEFT_TO_RIGHT = 0, + TEXTLINE_ORDER_RIGHT_TO_LEFT = 1, + TEXTLINE_ORDER_TOP_TO_BOTTOM = 2, + }; + + /** + * Possible modes for page layout analysis. These *must* be kept in order + * of decreasing amount of layout analysis to be done, except for OSD_ONLY, + * so that the inequality test macros below work. + */ + enum PageSegMode { + PSM_OSD_ONLY, ///< Orientation and script detection only. + PSM_AUTO_OSD, ///< Automatic page segmentation with orientation and + ///< script detection. (OSD) + PSM_AUTO_ONLY, ///< Automatic page segmentation, but no OSD, or OCR. + PSM_AUTO, ///< Fully automatic page segmentation, but no OSD. + PSM_SINGLE_COLUMN, ///< Assume a single column of text of variable sizes. + PSM_SINGLE_BLOCK_VERT_TEXT, ///< Assume a single uniform block of vertically + ///< aligned text. + PSM_SINGLE_BLOCK, ///< Assume a single uniform block of text. (Default.) + PSM_SINGLE_LINE, ///< Treat the image as a single text line. + PSM_SINGLE_WORD, ///< Treat the image as a single word. + PSM_CIRCLE_WORD, ///< Treat the image as a single word in a circle. + PSM_SINGLE_CHAR, ///< Treat the image as a single character. + PSM_SPARSE_TEXT, ///< Find as much text as possible in no particular order. + PSM_SPARSE_TEXT_OSD, ///< Sparse text with orientation and script det. + PSM_RAW_LINE, ///< Treat the image as a single text line, bypassing + ///< hacks that are Tesseract-specific. + + PSM_COUNT ///< Number of enum entries. + }; + + /** + * Inline functions that act on a PageSegMode to determine whether components of + * layout analysis are enabled. + * *Depend critically on the order of elements of PageSegMode.* + * NOTE that arg is an int for compatibility with INT_PARAM. + */ + inline bool PSM_OSD_ENABLED(int pageseg_mode) { + return pageseg_mode <= PSM_AUTO_OSD || pageseg_mode == PSM_SPARSE_TEXT_OSD; + } + inline bool PSM_ORIENTATION_ENABLED(int pageseg_mode) { + return pageseg_mode <= PSM_AUTO || pageseg_mode == PSM_SPARSE_TEXT_OSD; + } + inline bool PSM_COL_FIND_ENABLED(int pageseg_mode) { + return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_AUTO; + } + inline bool PSM_SPARSE(int pageseg_mode) { + return pageseg_mode == PSM_SPARSE_TEXT || pageseg_mode == PSM_SPARSE_TEXT_OSD; + } + inline bool PSM_BLOCK_FIND_ENABLED(int pageseg_mode) { + return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_COLUMN; + } + inline bool PSM_LINE_FIND_ENABLED(int pageseg_mode) { + return pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_BLOCK; + } + inline bool PSM_WORD_FIND_ENABLED(int pageseg_mode) { + return (pageseg_mode >= PSM_AUTO_OSD && pageseg_mode <= PSM_SINGLE_LINE) || + pageseg_mode == PSM_SPARSE_TEXT || pageseg_mode == PSM_SPARSE_TEXT_OSD; + } + + /** + * enum of the elements of the page hierarchy, used in ResultIterator + * to provide functions that operate on each level without having to + * have 5x as many functions. + */ + enum PageIteratorLevel { + RIL_BLOCK, // Block of text/image/separator line. + RIL_PARA, // Paragraph within a block. + RIL_TEXTLINE, // Line within a paragraph. + RIL_WORD, // Word within a textline. + RIL_SYMBOL // Symbol/character within a word. + }; + + /** + * JUSTIFICATION_UNKNOWN + * The alignment is not clearly one of the other options. This could happen + * for example if there are only one or two lines of text or the text looks + * like source code or poetry. + * + * NOTA BENE: Fully justified paragraphs (text aligned to both left and right + * margins) are marked by Tesseract with JUSTIFICATION_LEFT if their text + * is written with a left-to-right script and with JUSTIFICATION_RIGHT if + * their text is written in a right-to-left script. + * + * Interpretation for text read in vertical lines: + * "Left" is wherever the starting reading position is. + * + * JUSTIFICATION_LEFT + * Each line, except possibly the first, is flush to the same left tab stop. + * + * JUSTIFICATION_CENTER + * The text lines of the paragraph are centered about a line going + * down through their middle of the text lines. + * + * JUSTIFICATION_RIGHT + * Each line, except possibly the first, is flush to the same right tab stop. + */ + enum ParagraphJustification { + JUSTIFICATION_UNKNOWN, + JUSTIFICATION_LEFT, + JUSTIFICATION_CENTER, + JUSTIFICATION_RIGHT, + }; + + /** + * When Tesseract/Cube is initialized we can choose to instantiate/load/run + * only the Tesseract part, only the Cube part or both along with the combiner. + * The preference of which engine to use is stored in tessedit_ocr_engine_mode. + * + * ATTENTION: When modifying this enum, please make sure to make the + * appropriate changes to all the enums mirroring it (e.g. OCREngine in + * cityblock/workflow/detection/detection_storage.proto). Such enums will + * mention the connection to OcrEngineMode in the comments. + */ + enum OcrEngineMode { + OEM_TESSERACT_ONLY, // Run Tesseract only - fastest + OEM_CUBE_ONLY, // Run Cube only - better accuracy, but slower + OEM_TESSERACT_CUBE_COMBINED, // Run both and combine results - best accuracy + OEM_DEFAULT // Specify this mode when calling init_*(), + // to indicate that any of the above modes + // should be automatically inferred from the + // variables in the language-specific config, + // command-line configs, or if not specified + // in any of the above should be set to the + // default OEM_TESSERACT_ONLY. + }; + +} // namespace tesseract. + +#endif // TESSERACT_CCSTRUCT_PUBLICTYPES_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.cpp new file mode 100644 index 0000000..e0f3169 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.cpp @@ -0,0 +1,143 @@ +/********************************************************************** + * File: quadlsq.cpp (Formerly qlsq.c) + * Description: Code for least squares approximation of quadratics. + * Author: Ray Smith + * Created: Wed Oct 6 15:14:23 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include +#include "quadlsq.h" +#include "tprintf.h" + +// Minimum variance in least squares before backing off to a lower degree. +const double kMinVariance = 1.0 / 1024; + +/********************************************************************** + * QLSQ::clear + * + * Function to initialize a QLSQ. + **********************************************************************/ + +void QLSQ::clear() { // initialize + a = 0.0; + b = 0.0; + c = 0.0; + n = 0; // No elements. + sigx = 0.0; // Zero accumulators. + sigy = 0.0; + sigxx = 0.0; + sigxy = 0.0; + sigyy = 0.0; + sigxxx = 0.0; + sigxxy = 0.0; + sigxxxx = 0.0; +} + + +/********************************************************************** + * QLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void QLSQ::add(double x, double y) { + n++; // Count elements. + sigx += x; // Update accumulators. + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; + sigxxx += static_cast(x) * x * x; + sigxxy += static_cast(x) * x * y; + sigxxxx += static_cast(x) * x * x * x; +} + + +/********************************************************************** + * QLSQ::remove + * + * Delete an element from the accumulator. + **********************************************************************/ + +void QLSQ::remove(double x, double y) { + if (n <= 0) { + tprintf("Can't remove an element from an empty QLSQ accumulator!\n"); + return; + } + n--; // Count elements. + sigx -= x; // Update accumulators. + sigy -= y; + sigxx -= x * x; + sigxy -= x * y; + sigyy -= y * y; + sigxxx -= static_cast(x) * x * x; + sigxxy -= static_cast(x) * x * y; + sigxxxx -= static_cast(x) * x * x * x; +} + + +/********************************************************************** + * QLSQ::fit + * + * Fit the given degree of polynomial and store the result. + * This creates a quadratic of the form axx + bx + c, but limited to + * the given degree. + **********************************************************************/ + +void QLSQ::fit(int degree) { + long double x_variance = static_cast(sigxx) * n - + static_cast(sigx) * sigx; + + // Note: for computational efficiency, we do not normalize the variance, + // covariance and cube variance here as they are in the same order in both + // nominators and denominators. However, we need be careful in value range + // check. + if (x_variance < kMinVariance * n * n || degree < 1 || n < 2) { + // We cannot calculate b reliably so forget a and b, and just work on c. + a = b = 0.0; + if (n >= 1 && degree >= 0) { + c = sigy / n; + } else { + c = 0.0; + } + return; + } + long double top96 = 0.0; // Accurate top. + long double bottom96 = 0.0; // Accurate bottom. + long double cubevar = sigxxx * n - static_cast(sigxx) * sigx; + long double covariance = static_cast(sigxy) * n - + static_cast(sigx) * sigy; + + if (n >= 4 && degree >= 2) { + top96 = cubevar * covariance; + top96 += x_variance * (static_cast(sigxx) * sigy - sigxxy * n); + + bottom96 = cubevar * cubevar; + bottom96 -= x_variance * + (sigxxxx * n - static_cast(sigxx) * sigxx); + } + if (bottom96 >= kMinVariance * n * n * n * n) { + // Denominators looking good + a = top96 / bottom96; + top96 = covariance - cubevar * a; + b = top96 / x_variance; + } else { + // Forget a, and concentrate on b. + a = 0.0; + b = covariance / x_variance; + } + c = (sigy - a * sigxx - b * sigx) / n; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.h new file mode 100644 index 0000000..337fd61 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadlsq.h @@ -0,0 +1,67 @@ +/********************************************************************** + * File: quadlsq.h (Formerly qlsq.h) + * Description: Code for least squares approximation of quadratics. + * Author: Ray Smith + * Created: Wed Oct 6 15:14:23 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUADLSQ_H +#define QUADLSQ_H + +#include "points.h" + +class QLSQ +{ + public: + QLSQ() { //constructor + clear(); //set to zeros + } + void clear(); //initialize + + void add( //add element + double x, //coords to add + double y); + void remove( //delete element + double x, //coords to delete + double y); + inT32 count() { //no of elements + return n; + } + + void fit( //fit the given + int degree); //return actual + double get_a() { //get x squard + return a; + } + double get_b() { //get x squard + return b; + } + double get_c() { //get x squard + return c; + } + + private: + inT32 n; //no of elements + double a, b, c; //result + double sigx; //sum of x + double sigy; //sum of y + double sigxx; //sum x squared + double sigxy; //sum of xy + double sigyy; //sum y squared + long double sigxxx; //sum x cubed + long double sigxxy; //sum xsquared y + long double sigxxxx; //sum x fourth +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/quadratc.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadratc.h new file mode 100644 index 0000000..88047b2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/quadratc.h @@ -0,0 +1,63 @@ +/********************************************************************** + * File: quadratc.h (Formerly quadrtic.h) + * Description: Code for the QUAD_COEFFS class. + * Author: Ray Smith + * Created: Tue Oct 08 17:24:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUADRATC_H +#define QUADRATC_H + +#include "points.h" + +class QUAD_COEFFS +{ + public: + QUAD_COEFFS() { + } //empty constructor + QUAD_COEFFS( //constructor + double xsq, //coefficients + float x, + float constant) { + a = xsq; + b = x; + c = constant; + } + + float y( //evaluate + float x) const { //at x + return (float) ((a * x + b) * x + c); + } + + void move( // reposition word + ICOORD vec) { // by vector + /************************************************************ + y - q = a (x - p)^2 + b (x - p) + c + y - q = ax^2 - 2apx + ap^2 + bx - bp + c + y = ax^2 + (b - 2ap)x + (c - bp + ap^2 + q) + ************************************************************/ + inT16 p = vec.x (); + inT16 q = vec.y (); + + c = (float) (c - b * p + a * p * p + q); + b = (float) (b - 2 * a * p); + } + + double a; //x squared + float b; //x + float c; //constant + private: +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.cpp new file mode 100644 index 0000000..82107e1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.cpp @@ -0,0 +1,425 @@ +/********************************************************************** + * File: quspline.cpp (Formerly qspline.c) + * Description: Code for the QSPLINE class. + * Author: Ray Smith + * Created: Tue Oct 08 17:16:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "allheaders.h" +#include "memry.h" +#include "quadlsq.h" +#include "quspline.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#define QSPLINE_PRECISION 16 //no of steps to draw + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE given the components used in the old code. + **********************************************************************/ + +QSPLINE::QSPLINE( //constructor + inT32 count, //no of segments + inT32 *xstarts, //start coords + double *coeffs //coefficients + ) { + inT32 index; //segment index + + //get memory + xcoords = (inT32 *) alloc_mem ((count + 1) * sizeof (inT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (count * sizeof (QUAD_COEFFS)); + segments = count; + for (index = 0; index < segments; index++) { + //copy them + xcoords[index] = xstarts[index]; + quadratics[index] = QUAD_COEFFS (coeffs[index * 3], + coeffs[index * 3 + 1], + coeffs[index * 3 + 2]); + } + //right edge + xcoords[index] = xstarts[index]; +} + + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE by appproximation of points. + **********************************************************************/ + +QSPLINE::QSPLINE ( //constructor +int xstarts[], //spline boundaries +int segcount, //no of segments +int xpts[], //points to fit +int ypts[], int pointcount, //no of pts +int degree //fit required +) { + int pointindex; /*no along text line */ + int segment; /*segment no */ + inT32 *ptcounts; //no in each segment + QLSQ qlsq; /*accumulator */ + + segments = segcount; + xcoords = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); + ptcounts = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (segcount * sizeof (QUAD_COEFFS)); + memmove (xcoords, xstarts, (segcount + 1) * sizeof (inT32)); + ptcounts[0] = 0; /*none in any yet */ + for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) { + while (segment < segcount && xpts[pointindex] >= xstarts[segment]) { + segment++; /*try next segment */ + /*cumulative counts */ + ptcounts[segment] = ptcounts[segment - 1]; + } + ptcounts[segment]++; /*no in previous partition */ + } + while (segment < segcount) { + segment++; + /*zero the rest */ + ptcounts[segment] = ptcounts[segment - 1]; + } + + for (segment = 0; segment < segcount; segment++) { + qlsq.clear (); + /*first blob */ + pointindex = ptcounts[segment]; + if (pointindex > 0 + && xpts[pointindex] != xpts[pointindex - 1] + && xpts[pointindex] != xstarts[segment]) + qlsq.add (xstarts[segment], + ypts[pointindex - 1] + + (ypts[pointindex] - ypts[pointindex - 1]) + * (xstarts[segment] - xpts[pointindex - 1]) + / (xpts[pointindex] - xpts[pointindex - 1])); + for (; pointindex < ptcounts[segment + 1]; pointindex++) { + qlsq.add (xpts[pointindex], ypts[pointindex]); + } + if (pointindex > 0 && pointindex < pointcount + && xpts[pointindex] != xstarts[segment + 1]) + qlsq.add (xstarts[segment + 1], + ypts[pointindex - 1] + + (ypts[pointindex] - ypts[pointindex - 1]) + * (xstarts[segment + 1] - xpts[pointindex - 1]) + / (xpts[pointindex] - xpts[pointindex - 1])); + qlsq.fit (degree); + quadratics[segment].a = qlsq.get_a (); + quadratics[segment].b = qlsq.get_b (); + quadratics[segment].c = qlsq.get_c (); + } + free_mem(ptcounts); +} + + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE from another. + **********************************************************************/ + +QSPLINE::QSPLINE( //constructor + const QSPLINE &src) { + segments = 0; + xcoords = NULL; + quadratics = NULL; + *this = src; +} + + +/********************************************************************** + * QSPLINE::~QSPLINE + * + * Destroy a QSPLINE. + **********************************************************************/ + +QSPLINE::~QSPLINE ( //constructor +) { + if (xcoords != NULL) { + free_mem(xcoords); + xcoords = NULL; + } + if (quadratics != NULL) { + free_mem(quadratics); + quadratics = NULL; + } +} + + +/********************************************************************** + * QSPLINE::operator= + * + * Copy a QSPLINE + **********************************************************************/ + +QSPLINE & QSPLINE::operator= ( //assignment +const QSPLINE & source) { + if (xcoords != NULL) + free_mem(xcoords); + if (quadratics != NULL) + free_mem(quadratics); + + segments = source.segments; + xcoords = (inT32 *) alloc_mem ((segments + 1) * sizeof (inT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (segments * sizeof (QUAD_COEFFS)); + memmove (xcoords, source.xcoords, (segments + 1) * sizeof (inT32)); + memmove (quadratics, source.quadratics, segments * sizeof (QUAD_COEFFS)); + return *this; +} + + +/********************************************************************** + * QSPLINE::step + * + * Return the total of the step functions between the given coords. + **********************************************************************/ + +double QSPLINE::step( //find step functions + double x1, //between coords + double x2) { + int index1, index2; //indices of coords + double total; /*total steps */ + + index1 = spline_index (x1); + index2 = spline_index (x2); + total = 0; + while (index1 < index2) { + total += + (double) quadratics[index1 + 1].y ((float) xcoords[index1 + 1]); + total -= (double) quadratics[index1].y ((float) xcoords[index1 + 1]); + index1++; /*next segment */ + } + return total; /*total steps */ +} + + +/********************************************************************** + * QSPLINE::y + * + * Return the y value at the given x value. + **********************************************************************/ + +double QSPLINE::y( //evaluate + double x //coord to evaluate at + ) const { + inT32 index; //segment index + + index = spline_index (x); + return quadratics[index].y (x);//in correct segment +} + + +/********************************************************************** + * QSPLINE::spline_index + * + * Return the index to the largest xcoord not greater than x. + **********************************************************************/ + +inT32 QSPLINE::spline_index( //evaluate + double x //coord to evaluate at + ) const { + inT32 index; //segment index + inT32 bottom; //bottom of range + inT32 top; //top of range + + bottom = 0; + top = segments; + while (top - bottom > 1) { + index = (top + bottom) / 2; //centre of range + if (x >= xcoords[index]) + bottom = index; //new min + else + top = index; //new max + } + return bottom; +} + + +/********************************************************************** + * QSPLINE::move + * + * Reposition spline by vector + **********************************************************************/ + +void QSPLINE::move( // reposition spline + ICOORD vec // by vector + ) { + inT32 segment; //index of segment + inT16 x_shift = vec.x (); + + for (segment = 0; segment < segments; segment++) { + xcoords[segment] += x_shift; + quadratics[segment].move (vec); + } + xcoords[segment] += x_shift; +} + + +/********************************************************************** + * QSPLINE::overlap + * + * Return TRUE if spline2 overlaps this by no more than fraction less + * than the bounds of this. + **********************************************************************/ + +BOOL8 QSPLINE::overlap( //test overlap + QSPLINE *spline2, //2 cannot be smaller + double fraction //by more than this + ) { + int leftlimit; /*common left limit */ + int rightlimit; /*common right limit */ + + leftlimit = xcoords[1]; + rightlimit = xcoords[segments - 1]; + /*or too non-overlap */ + if (spline2->segments < 3 || spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) + || spline2->xcoords[spline2->segments - 1] < rightlimit + - fraction * (rightlimit - leftlimit)) + return FALSE; + else + return TRUE; +} + + +/********************************************************************** + * extrapolate_spline + * + * Extrapolates the spline linearly using the same gradient as the + * quadratic has at either end. + **********************************************************************/ + +void QSPLINE::extrapolate( //linear extrapolation + double gradient, //gradient to use + int xmin, //new left edge + int xmax //new right edge + ) { + int segment; /*current segment of spline */ + int dest_segment; //dest index + int *xstarts; //new boundaries + QUAD_COEFFS *quads; //new ones + int increment; //in size + + increment = xmin < xcoords[0] ? 1 : 0; + if (xmax > xcoords[segments]) + increment++; + if (increment == 0) + return; + xstarts = (int *) alloc_mem ((segments + 1 + increment) * sizeof (int)); + quads = + (QUAD_COEFFS *) alloc_mem ((segments + increment) * sizeof (QUAD_COEFFS)); + if (xmin < xcoords[0]) { + xstarts[0] = xmin; + quads[0].a = 0; + quads[0].b = gradient; + quads[0].c = y (xcoords[0]) - quads[0].b * xcoords[0]; + dest_segment = 1; + } + else + dest_segment = 0; + for (segment = 0; segment < segments; segment++) { + xstarts[dest_segment] = xcoords[segment]; + quads[dest_segment] = quadratics[segment]; + dest_segment++; + } + xstarts[dest_segment] = xcoords[segment]; + if (xmax > xcoords[segments]) { + quads[dest_segment].a = 0; + quads[dest_segment].b = gradient; + quads[dest_segment].c = y (xcoords[segments]) + - quads[dest_segment].b * xcoords[segments]; + dest_segment++; + xstarts[dest_segment] = xmax + 1; + } + segments = dest_segment; + free_mem(xcoords); + free_mem(quadratics); + xcoords = (inT32 *) xstarts; + quadratics = quads; +} + + +/********************************************************************** + * QSPLINE::plot + * + * Draw the QSPLINE in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void QSPLINE::plot( //draw it + ScrollView* window, //window to draw in + ScrollView::Color colour //colour to draw in + ) const { + inT32 segment; //index of segment + inT16 step; //index of poly piece + double increment; //x increment + double x; //x coord + + window->Pen(colour); + for (segment = 0; segment < segments; segment++) { + increment = + (double) (xcoords[segment + 1] - + xcoords[segment]) / QSPLINE_PRECISION; + x = xcoords[segment]; + for (step = 0; step <= QSPLINE_PRECISION; step++) { + if (segment == 0 && step == 0) + window->SetCursor(x, quadratics[segment].y (x)); + else + window->DrawTo(x, quadratics[segment].y (x)); + x += increment; + } + } +} +#endif + +void QSPLINE::plot(Pix *pix) const { + if (pix == NULL) { + return; + } + + inT32 segment; // Index of segment + inT16 step; // Index of poly piece + double increment; // x increment + double x; // x coord + double height = static_cast(pixGetHeight(pix)); + Pta* points = ptaCreate(QSPLINE_PRECISION * segments); + const int kLineWidth = 5; + + for (segment = 0; segment < segments; segment++) { + increment = static_cast((xcoords[segment + 1] - + xcoords[segment])) / QSPLINE_PRECISION; + x = xcoords[segment]; + for (step = 0; step <= QSPLINE_PRECISION; step++) { + double y = height - quadratics[segment].y(x); + ptaAddPt(points, x, y); + x += increment; + } + } + + switch (pixGetDepth(pix)) { + case 1: + pixRenderPolyline(pix, points, kLineWidth, L_SET_PIXELS, 1); + break; + case 32: + pixRenderPolylineArb(pix, points, kLineWidth, 255, 0, 0, 1); + break; + default: + pixRenderPolyline(pix, points, kLineWidth, L_CLEAR_PIXELS, 1); + break; + } + ptaDestroy(&points); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.h new file mode 100644 index 0000000..f9e8145 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/quspline.h @@ -0,0 +1,99 @@ +/********************************************************************** + * File: quspline.h (Formerly qspline.h) + * Description: Code for the QSPLINE class. + * Author: Ray Smith + * Created: Tue Oct 08 17:16:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUSPLINE_H +#define QUSPLINE_H + +#include "quadratc.h" +#include "serialis.h" +#include "memry.h" +#include "rect.h" + +class ROW; +struct Pix; + +class QSPLINE +{ + friend void make_first_baseline(TBOX *, + int, + int *, + int *, + QSPLINE *, + QSPLINE *, + float); + friend void make_holed_baseline(TBOX *, int, QSPLINE *, QSPLINE *, float); + friend void tweak_row_baseline(ROW *, double, double); + public: + QSPLINE() { //empty constructor + segments = 0; + xcoords = NULL; //everything empty + quadratics = NULL; + } + QSPLINE( //copy constructor + const QSPLINE &src); + QSPLINE( //constructor + inT32 count, //number of segments + inT32 *xstarts, //segment starts + double *coeffs); //coefficients + ~QSPLINE (); //destructor + QSPLINE ( //least squares fit + int xstarts[], //spline boundaries + int segcount, //no of segments + int xcoords[], //points to fit + int ycoords[], int blobcount,//no of coords + int degree); //function + + double step( //step change + double x1, //between coords + double x2); + double y( //evaluate + double x) const; //at x + + void move( // reposition spline + ICOORD vec); // by vector + BOOL8 overlap( //test overlap + QSPLINE *spline2, //2 cannot be smaller + double fraction); //by more than this + void extrapolate( //linear extrapolation + double gradient, //gradient to use + int left, //new left edge + int right); //new right edge + +#ifndef GRAPHICS_DISABLED + void plot( //draw it + ScrollView* window, //in window + ScrollView::Color colour) const; //in colour +#endif + + // Paint the baseline over pix. If pix has depth of 32, then the line will + // be painted in red. Otherwise it will be painted in black. + void plot(Pix* pix) const; + + QSPLINE & operator= ( + const QSPLINE & source); //from this + + private: + + inT32 spline_index( //binary search + double x) const; //for x + inT32 segments; //no of segments + inT32 *xcoords; //no of coords + QUAD_COEFFS *quadratics; //spline pieces +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.cpp new file mode 100644 index 0000000..cbd7bf0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.cpp @@ -0,0 +1,837 @@ +/********************************************************************** + * File: ratngs.cpp (Formerly ratings.c) + * Description: Code to manipulate the BLOB_CHOICE and WERD_CHOICE classes. + * Author: Ray Smith + * Created: Thu Apr 23 13:23:29 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "ratngs.h" + +#include "blobs.h" +#include "callcpp.h" +#include "genericvector.h" +#include "matrix.h" +#include "normalis.h" // kBlnBaselineOffset. +#include "unicharset.h" + +using tesseract::ScriptPos; + +ELISTIZE(BLOB_CHOICE); +ELISTIZE(WERD_CHOICE); + +const float WERD_CHOICE::kBadRating = 100000.0; +// Min offset in baseline-normalized coords to make a character a subscript. +const int kMinSubscriptOffset = 20; +// Min offset in baseline-normalized coords to make a character a superscript. +const int kMinSuperscriptOffset = 20; +// Max y of bottom of a drop-cap blob. +const int kMaxDropCapBottom = -128; +// Max fraction of x-height to use as denominator in measuring x-height overlap. +const double kMaxOverlapDenominator = 0.125; +// Min fraction of x-height range that should be in agreement for matching +// x-heights. +const double kMinXHeightMatch = 0.5; +// Max tolerance on baseline position as a fraction of x-height for matching +// baselines. +const double kMaxBaselineDrift = 0.0625; + +static const char kPermuterTypeNoPerm[] = "None"; +static const char kPermuterTypePuncPerm[] = "Punctuation"; +static const char kPermuterTypeTopPerm[] = "Top Choice"; +static const char kPermuterTypeLowerPerm[] = "Top Lower Case"; +static const char kPermuterTypeUpperPerm[] = "Top Upper Case"; +static const char kPermuterTypeNgramPerm[] = "Ngram"; +static const char kPermuterTypeNumberPerm[] = "Number"; +static const char kPermuterTypeUserPatPerm[] = "User Pattern"; +static const char kPermuterTypeSysDawgPerm[] = "System Dictionary"; +static const char kPermuterTypeDocDawgPerm[] = "Document Dictionary"; +static const char kPermuterTypeUserDawgPerm[] = "User Dictionary"; +static const char kPermuterTypeFreqDawgPerm[] = "Frequent Words Dictionary"; +static const char kPermuterTypeCompoundPerm[] = "Compound"; + +static const char * const kPermuterTypeNames[] = { + kPermuterTypeNoPerm, // 0 + kPermuterTypePuncPerm, // 1 + kPermuterTypeTopPerm, // 2 + kPermuterTypeLowerPerm, // 3 + kPermuterTypeUpperPerm, // 4 + kPermuterTypeNgramPerm, // 5 + kPermuterTypeNumberPerm, // 6 + kPermuterTypeUserPatPerm, // 7 + kPermuterTypeSysDawgPerm, // 8 + kPermuterTypeDocDawgPerm, // 9 + kPermuterTypeUserDawgPerm, // 10 + kPermuterTypeFreqDawgPerm, // 11 + kPermuterTypeCompoundPerm // 12 +}; + +/** + * BLOB_CHOICE::BLOB_CHOICE + * + * Constructor to build a BLOB_CHOICE from a char, rating and certainty. + */ +BLOB_CHOICE::BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id + float src_rating, // rating + float src_cert, // certainty + int src_script_id, // script + float min_xheight, // min xheight allowed + float max_xheight, // max xheight by this char + float yshift, // yshift out of position + BlobChoiceClassifier c) { // adapted match or other + unichar_id_ = src_unichar_id; + rating_ = src_rating; + certainty_ = src_cert; + fontinfo_id_ = -1; + fontinfo_id2_ = -1; + script_id_ = src_script_id; + min_xheight_ = min_xheight; + max_xheight_ = max_xheight; + yshift_ = yshift; + classifier_ = c; +} + +/** + * BLOB_CHOICE::BLOB_CHOICE + * + * Constructor to build a BLOB_CHOICE from another BLOB_CHOICE. + */ +BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) : ELIST_LINK(other) { + unichar_id_ = other.unichar_id(); + rating_ = other.rating(); + certainty_ = other.certainty(); + fontinfo_id_ = other.fontinfo_id(); + fontinfo_id2_ = other.fontinfo_id2(); + script_id_ = other.script_id(); + matrix_cell_ = other.matrix_cell_; + min_xheight_ = other.min_xheight_; + max_xheight_ = other.max_xheight_; + yshift_ = other.yshift(); + classifier_ = other.classifier_; + fonts_ = other.fonts_; +} + +// Returns true if *this and other agree on the baseline and x-height +// to within some tolerance based on a given estimate of the x-height. +bool BLOB_CHOICE::PosAndSizeAgree(const BLOB_CHOICE& other, float x_height, + bool debug) const { + double baseline_diff = fabs(yshift() - other.yshift()); + if (baseline_diff > kMaxBaselineDrift * x_height) { + if (debug) { + tprintf("Baseline diff %g for %d v %d\n", + baseline_diff, unichar_id_, other.unichar_id_); + } + return false; + } + double this_range = max_xheight() - min_xheight(); + double other_range = other.max_xheight() - other.min_xheight(); + double denominator = ClipToRange(MIN(this_range, other_range), + 1.0, kMaxOverlapDenominator * x_height); + double overlap = MIN(max_xheight(), other.max_xheight()) - + MAX(min_xheight(), other.min_xheight()); + overlap /= denominator; + if (debug) { + tprintf("PosAndSize for %d v %d: bl diff = %g, ranges %g, %g / %g ->%g\n", + unichar_id_, other.unichar_id_, baseline_diff, + this_range, other_range, denominator, overlap); + } + + return overlap >= kMinXHeightMatch; +} + +// Helper to find the BLOB_CHOICE in the bc_list that matches the given +// unichar_id, or NULL if there is no match. +BLOB_CHOICE* FindMatchingChoice(UNICHAR_ID char_id, + BLOB_CHOICE_LIST* bc_list) { + // Find the corresponding best BLOB_CHOICE. + BLOB_CHOICE_IT choice_it(bc_list); + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + BLOB_CHOICE* choice = choice_it.data(); + if (choice->unichar_id() == char_id) { + return choice; + } + } + return NULL; +} + +const char *WERD_CHOICE::permuter_name(uinT8 permuter) { + return kPermuterTypeNames[permuter]; +} + +namespace tesseract { + +const char *ScriptPosToString(enum ScriptPos script_pos) { + switch (script_pos) { + case SP_NORMAL: return "NORM"; + case SP_SUBSCRIPT: return "SUB"; + case SP_SUPERSCRIPT: return "SUPER"; + case SP_DROPCAP: return "DROPC"; + } + return "SP_UNKNOWN"; +} + +} // namespace tesseract. + +/** + * WERD_CHOICE::WERD_CHOICE + * + * Constructor to build a WERD_CHOICE from the given string. + * The function assumes that src_string is not NULL. + */ +WERD_CHOICE::WERD_CHOICE(const char *src_string, + const UNICHARSET &unicharset) + : unicharset_(&unicharset){ + GenericVector encoding; + GenericVector lengths; + if (unicharset.encode_string(src_string, true, &encoding, &lengths, NULL)) { + lengths.push_back('\0'); + STRING src_lengths = &lengths[0]; + this->init(src_string, src_lengths.string(), 0.0, 0.0, NO_PERM); + } else { // There must have been an invalid unichar in the string. + this->init(8); + this->make_bad(); + } +} + +/** + * WERD_CHOICE::init + * + * Helper function to build a WERD_CHOICE from the given string, + * fragment lengths, rating, certainty and permuter. + * + * The function assumes that src_string is not NULL. + * src_lengths argument could be NULL, in which case the unichars + * in src_string are assumed to all be of length 1. + */ +void WERD_CHOICE::init(const char *src_string, + const char *src_lengths, + float src_rating, + float src_certainty, + uinT8 src_permuter) { + int src_string_len = strlen(src_string); + if (src_string_len == 0) { + this->init(8); + } else { + this->init(src_lengths ? strlen(src_lengths): src_string_len); + length_ = reserved_; + int offset = 0; + for (int i = 0; i < length_; ++i) { + int unichar_length = src_lengths ? src_lengths[i] : 1; + unichar_ids_[i] = + unicharset_->unichar_to_id(src_string+offset, unichar_length); + state_[i] = 1; + certainties_[i] = src_certainty; + offset += unichar_length; + } + } + adjust_factor_ = 1.0f; + rating_ = src_rating; + certainty_ = src_certainty; + permuter_ = src_permuter; + dangerous_ambig_found_ = false; +} + +/** + * WERD_CHOICE::~WERD_CHOICE + */ +WERD_CHOICE::~WERD_CHOICE() { + delete[] unichar_ids_; + delete[] script_pos_; + delete[] state_; + delete[] certainties_; +} + +const char *WERD_CHOICE::permuter_name() const { + return kPermuterTypeNames[permuter_]; +} + +// Returns the BLOB_CHOICE_LIST corresponding to the given index in the word, +// taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. +BLOB_CHOICE_LIST* WERD_CHOICE::blob_choices(int index, MATRIX* ratings) const { + MATRIX_COORD coord = MatrixCoord(index); + BLOB_CHOICE_LIST* result = ratings->get(coord.col, coord.row); + if (result == NULL) { + result = new BLOB_CHOICE_LIST; + ratings->put(coord.col, coord.row, result); + } + return result; +} + +// Returns the MATRIX_COORD corresponding to the location in the ratings +// MATRIX for the given index into the word. +MATRIX_COORD WERD_CHOICE::MatrixCoord(int index) const { + int col = 0; + for (int i = 0; i < index; ++i) + col += state_[i]; + int row = col + state_[index] - 1; + return MATRIX_COORD(col, row); +} + +// Sets the entries for the given index from the BLOB_CHOICE, assuming +// unit fragment lengths, but setting the state for this index to blob_count. +void WERD_CHOICE::set_blob_choice(int index, int blob_count, + const BLOB_CHOICE* blob_choice) { + unichar_ids_[index] = blob_choice->unichar_id(); + script_pos_[index] = tesseract::SP_NORMAL; + state_[index] = blob_count; + certainties_[index] = blob_choice->certainty(); +} + + +/** + * contains_unichar_id + * + * Returns true if unichar_ids_ contain the given unichar_id, false otherwise. + */ +bool WERD_CHOICE::contains_unichar_id(UNICHAR_ID unichar_id) const { + for (int i = 0; i < length_; ++i) { + if (unichar_ids_[i] == unichar_id) { + return true; + } + } + return false; +} + +/** + * remove_unichar_ids + * + * Removes num unichar ids starting from index start from unichar_ids_ + * and updates length_ and fragment_lengths_ to reflect this change. + * Note: this function does not modify rating_ and certainty_. + */ +void WERD_CHOICE::remove_unichar_ids(int start, int num) { + ASSERT_HOST(start >= 0 && start + num <= length_); + // Accumulate the states to account for the merged blobs. + for (int i = 0; i < num; ++i) { + if (start > 0) + state_[start - 1] += state_[start + i]; + else if (start + num < length_) + state_[start + num] += state_[start + i]; + } + for (int i = start; i + num < length_; ++i) { + unichar_ids_[i] = unichar_ids_[i + num]; + script_pos_[i] = script_pos_[i + num]; + state_[i] = state_[i + num]; + certainties_[i] = certainties_[i + num]; + } + length_ -= num; +} + +/** + * reverse_and_mirror_unichar_ids + * + * Reverses and mirrors unichars in unichar_ids. + */ +void WERD_CHOICE::reverse_and_mirror_unichar_ids() { + for (int i = 0; i < length_ / 2; ++i) { + UNICHAR_ID tmp_id = unichar_ids_[i]; + unichar_ids_[i] = unicharset_->get_mirror(unichar_ids_[length_-1-i]); + unichar_ids_[length_-1-i] = unicharset_->get_mirror(tmp_id); + } + if (length_ % 2 != 0) { + unichar_ids_[length_/2] = unicharset_->get_mirror(unichar_ids_[length_/2]); + } +} + +/** + * punct_stripped + * + * Returns the half-open interval of unichar_id indices [start, end) which + * enclose the core portion of this word -- the part after stripping + * punctuation from the left and right. + */ +void WERD_CHOICE::punct_stripped(int *start, int *end) const { + *start = 0; + *end = length() - 1; + while (*start < length() && + unicharset()->get_ispunctuation(unichar_id(*start))) { + (*start)++; + } + while (*end > -1 && + unicharset()->get_ispunctuation(unichar_id(*end))) { + (*end)--; + } + (*end)++; +} + +void WERD_CHOICE::GetNonSuperscriptSpan(int *pstart, int *pend) const { + int end = length(); + while (end > 0 && + unicharset_->get_isdigit(unichar_ids_[end - 1]) && + BlobPosition(end - 1) == tesseract::SP_SUPERSCRIPT) { + end--; + } + int start = 0; + while (start < end && + unicharset_->get_isdigit(unichar_ids_[start]) && + BlobPosition(start) == tesseract::SP_SUPERSCRIPT) { + start++; + } + *pstart = start; + *pend = end; +} + +WERD_CHOICE WERD_CHOICE::shallow_copy(int start, int end) const { + ASSERT_HOST(start >= 0 && start <= length_); + ASSERT_HOST(end >= 0 && end <= length_); + if (end < start) { end = start; } + WERD_CHOICE retval(unicharset_, end - start); + for (int i = start; i < end; i++) { + retval.append_unichar_id_space_allocated( + unichar_ids_[i], state_[i], 0.0f, certainties_[i]); + } + return retval; +} + +/** + * has_rtl_unichar_id + * + * Returns true if unichar_ids contain at least one "strongly" RTL unichar. + */ +bool WERD_CHOICE::has_rtl_unichar_id() const { + int i; + for (i = 0; i < length_; ++i) { + UNICHARSET::Direction dir = unicharset_->get_direction(unichar_ids_[i]); + if (dir == UNICHARSET::U_RIGHT_TO_LEFT || + dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC) { + return true; + } + } + return false; +} + +/** + * string_and_lengths + * + * Populates the given word_str with unichars from unichar_ids and + * and word_lengths_str with the corresponding unichar lengths. + */ +void WERD_CHOICE::string_and_lengths(STRING *word_str, + STRING *word_lengths_str) const { + *word_str = ""; + if (word_lengths_str != NULL) *word_lengths_str = ""; + for (int i = 0; i < length_; ++i) { + const char *ch = unicharset_->id_to_unichar_ext(unichar_ids_[i]); + *word_str += ch; + if (word_lengths_str != NULL) { + *word_lengths_str += strlen(ch); + } + } +} + +/** + * append_unichar_id + * + * Make sure there is enough space in the word for the new unichar id + * and call append_unichar_id_space_allocated(). + */ +void WERD_CHOICE::append_unichar_id( + UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty) { + if (length_ == reserved_) { + this->double_the_size(); + } + this->append_unichar_id_space_allocated(unichar_id, blob_count, + rating, certainty); +} + +/** + * WERD_CHOICE::operator+= + * + * Cat a second word rating on the end of this current one. + * The ratings are added and the confidence is the min. + * If the permuters are NOT the same the permuter is set to COMPOUND_PERM + */ +WERD_CHOICE & WERD_CHOICE::operator+= (const WERD_CHOICE & second) { + ASSERT_HOST(unicharset_ == second.unicharset_); + while (reserved_ < length_ + second.length()) { + this->double_the_size(); + } + const UNICHAR_ID *other_unichar_ids = second.unichar_ids(); + for (int i = 0; i < second.length(); ++i) { + unichar_ids_[length_ + i] = other_unichar_ids[i]; + state_[length_ + i] = second.state_[i]; + certainties_[length_ + i] = second.certainties_[i]; + script_pos_[length_ + i] = second.BlobPosition(i); + } + length_ += second.length(); + if (second.adjust_factor_ > adjust_factor_) + adjust_factor_ = second.adjust_factor_; + rating_ += second.rating(); // add ratings + if (second.certainty() < certainty_) // take min + certainty_ = second.certainty(); + if (second.dangerous_ambig_found_) + dangerous_ambig_found_ = true; + if (permuter_ == NO_PERM) { + permuter_ = second.permuter(); + } else if (second.permuter() != NO_PERM && + second.permuter() != permuter_) { + permuter_ = COMPOUND_PERM; + } + return *this; +} + + +/** + * WERD_CHOICE::operator= + * + * Allocate enough memory to hold a copy of source and copy over + * all the information from source to this WERD_CHOICE. + */ +WERD_CHOICE& WERD_CHOICE::operator=(const WERD_CHOICE& source) { + while (reserved_ < source.length()) { + this->double_the_size(); + } + + unicharset_ = source.unicharset_; + const UNICHAR_ID *other_unichar_ids = source.unichar_ids(); + for (int i = 0; i < source.length(); ++i) { + unichar_ids_[i] = other_unichar_ids[i]; + state_[i] = source.state_[i]; + certainties_[i] = source.certainties_[i]; + script_pos_[i] = source.BlobPosition(i); + } + length_ = source.length(); + adjust_factor_ = source.adjust_factor_; + rating_ = source.rating(); + certainty_ = source.certainty(); + min_x_height_ = source.min_x_height(); + max_x_height_ = source.max_x_height(); + permuter_ = source.permuter(); + dangerous_ambig_found_ = source.dangerous_ambig_found_; + return *this; +} + +// Sets up the script_pos_ member using the blobs_list to get the bln +// bounding boxes, *this to get the unichars, and this->unicharset +// to get the target positions. If small_caps is true, sub/super are not +// considered, but dropcaps are. +// NOTE: blobs_list should be the chopped_word blobs. (Fully segemented.) +void WERD_CHOICE::SetScriptPositions(bool small_caps, TWERD* word) { + // Since WERD_CHOICE isn't supposed to depend on a Tesseract, + // we don't have easy access to the flags Tesseract stores. Therefore, debug + // for this module is hard compiled in. + int debug = 0; + + // Initialize to normal. + for (int i = 0; i < length_; ++i) + script_pos_[i] = tesseract::SP_NORMAL; + if (word->blobs.empty() || word->NumBlobs() != TotalOfStates()) { + return; + } + + int position_counts[4]; + for (int i = 0; i < 4; i++) { + position_counts[i] = 0; + } + + int chunk_index = 0; + for (int blob_index = 0; blob_index < length_; ++blob_index, ++chunk_index) { + TBLOB* tblob = word->blobs[chunk_index]; + int uni_id = unichar_id(blob_index); + TBOX blob_box = tblob->bounding_box(); + if (state_ != NULL) { + for (int i = 1; i < state_[blob_index]; ++i) { + ++chunk_index; + tblob = word->blobs[chunk_index]; + blob_box += tblob->bounding_box(); + } + } + script_pos_[blob_index] = ScriptPositionOf(false, *unicharset_, blob_box, + uni_id); + if (small_caps && script_pos_[blob_index] != tesseract::SP_DROPCAP) { + script_pos_[blob_index] = tesseract::SP_NORMAL; + } + position_counts[script_pos_[blob_index]]++; + } + // If almost everything looks like a superscript or subscript, + // we most likely just got the baseline wrong. + if (position_counts[tesseract::SP_SUBSCRIPT] > 0.75 * length_ || + position_counts[tesseract::SP_SUPERSCRIPT] > 0.75 * length_) { + if (debug >= 2) { + tprintf("Most characters of %s are subscript or superscript.\n" + "That seems wrong, so I'll assume we got the baseline wrong\n", + unichar_string().string()); + } + for (int i = 0; i < length_; i++) { + ScriptPos sp = script_pos_[i]; + if (sp == tesseract::SP_SUBSCRIPT || sp == tesseract::SP_SUPERSCRIPT) { + position_counts[sp]--; + position_counts[tesseract::SP_NORMAL]++; + script_pos_[i] = tesseract::SP_NORMAL; + } + } + } + + if ((debug >= 1 && position_counts[tesseract::SP_NORMAL] < length_) || + debug >= 2) { + tprintf("SetScriptPosition on %s\n", unichar_string().string()); + int chunk_index = 0; + for (int blob_index = 0; blob_index < length_; ++blob_index) { + if (debug >= 2 || script_pos_[blob_index] != tesseract::SP_NORMAL) { + TBLOB* tblob = word->blobs[chunk_index]; + ScriptPositionOf(true, *unicharset_, tblob->bounding_box(), + unichar_id(blob_index)); + } + chunk_index += state_ != NULL ? state_[blob_index] : 1; + } + } +} +// Sets the script_pos_ member from some source positions with a given length. +void WERD_CHOICE::SetScriptPositions(const tesseract::ScriptPos* positions, + int length) { + ASSERT_HOST(length == length_); + if (positions != script_pos_) { + delete [] script_pos_; + script_pos_ = new ScriptPos[length]; + memcpy(script_pos_, positions, sizeof(positions[0]) * length); + } +} +// Sets all the script_pos_ positions to the given position. +void WERD_CHOICE::SetAllScriptPositions(tesseract::ScriptPos position) { + for (int i = 0; i < length_; ++i) + script_pos_[i] = position; +} + +/* static */ +ScriptPos WERD_CHOICE::ScriptPositionOf(bool print_debug, + const UNICHARSET& unicharset, + const TBOX& blob_box, + UNICHAR_ID unichar_id) { + ScriptPos retval = tesseract::SP_NORMAL; + int top = blob_box.top(); + int bottom = blob_box.bottom(); + int min_bottom, max_bottom, min_top, max_top; + unicharset.get_top_bottom(unichar_id, + &min_bottom, &max_bottom, + &min_top, &max_top); + + int sub_thresh_top = min_top - kMinSubscriptOffset; + int sub_thresh_bot = kBlnBaselineOffset - kMinSubscriptOffset; + int sup_thresh_bot = max_bottom + kMinSuperscriptOffset; + if (bottom <= kMaxDropCapBottom) { + retval = tesseract::SP_DROPCAP; + } else if (top < sub_thresh_top && bottom < sub_thresh_bot) { + retval = tesseract::SP_SUBSCRIPT; + } else if (bottom > sup_thresh_bot) { + retval = tesseract::SP_SUPERSCRIPT; + } + + if (print_debug) { + const char *pos = ScriptPosToString(retval); + tprintf("%s Character %s[bot:%d top: %d] " + "bot_range[%d,%d] top_range[%d, %d] " + "sub_thresh[bot:%d top:%d] sup_thresh_bot %d\n", + pos, unicharset.id_to_unichar(unichar_id), + bottom, top, + min_bottom, max_bottom, min_top, max_top, + sub_thresh_bot, sub_thresh_top, + sup_thresh_bot); + } + return retval; +} + +// Returns the script-id (eg Han) of the dominant script in the word. +int WERD_CHOICE::GetTopScriptID() const { + int max_script = unicharset_->get_script_table_size(); + int *sid = new int[max_script]; + int x; + for (x = 0; x < max_script; x++) sid[x] = 0; + for (x = 0; x < length_; ++x) { + int script_id = unicharset_->get_script(unichar_id(x)); + sid[script_id]++; + } + if (unicharset_->han_sid() != unicharset_->null_sid()) { + // Add the Hiragana & Katakana counts to Han and zero them out. + if (unicharset_->hiragana_sid() != unicharset_->null_sid()) { + sid[unicharset_->han_sid()] += sid[unicharset_->hiragana_sid()]; + sid[unicharset_->hiragana_sid()] = 0; + } + if (unicharset_->katakana_sid() != unicharset_->null_sid()) { + sid[unicharset_->han_sid()] += sid[unicharset_->katakana_sid()]; + sid[unicharset_->katakana_sid()] = 0; + } + } + // Note that high script ID overrides lower one on a tie, thus biasing + // towards non-Common script (if sorted that way in unicharset file). + int max_sid = 0; + for (x = 1; x < max_script; x++) + if (sid[x] >= sid[max_sid]) max_sid = x; + if (sid[max_sid] < length_ / 2) + max_sid = unicharset_->null_sid(); + delete[] sid; + return max_sid; +} + +// Fixes the state_ for a chop at the given blob_posiiton. +void WERD_CHOICE::UpdateStateForSplit(int blob_position) { + int total_chunks = 0; + for (int i = 0; i < length_; ++i) { + total_chunks += state_[i]; + if (total_chunks > blob_position) { + ++state_[i]; + return; + } + } +} + +// Returns the sum of all the state elements, being the total number of blobs. +int WERD_CHOICE::TotalOfStates() const { + int total_chunks = 0; + for (int i = 0; i < length_; ++i) { + total_chunks += state_[i]; + } + return total_chunks; +} + +/** + * WERD_CHOICE::print + * + * Print WERD_CHOICE to stdout. + */ +void WERD_CHOICE::print(const char *msg) const { + tprintf("%s : ", msg); + for (int i = 0; i < length_; ++i) { + tprintf("%s", unicharset_->id_to_unichar(unichar_ids_[i])); + } + tprintf(" : R=%g, C=%g, F=%g, Perm=%d, xht=[%g,%g], ambig=%d\n", + rating_, certainty_, adjust_factor_, permuter_, + min_x_height_, max_x_height_, dangerous_ambig_found_); + tprintf("pos"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%s", ScriptPosToString(script_pos_[i])); + } + tprintf("\nstr"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%s", unicharset_->id_to_unichar(unichar_ids_[i])); + } + tprintf("\nstate:"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%d ", state_[i]); + } + tprintf("\nC"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%.3f", certainties_[i]); + } + tprintf("\n"); +} + +// Prints the segmentation state with an introductory message. +void WERD_CHOICE::print_state(const char *msg) const { + tprintf("%s", msg); + for (int i = 0; i < length_; ++i) + tprintf(" %d", state_[i]); + tprintf("\n"); +} + +// Displays the segmentation state of *this (if not the same as the last +// one displayed) and waits for a click in the window. +void WERD_CHOICE::DisplaySegmentation(TWERD* word) { +#ifndef GRAPHICS_DISABLED + // Number of different colors to draw with. + const int kNumColors = 6; + static ScrollView *segm_window = NULL; + // Check the state against the static prev_drawn_state. + static GenericVector prev_drawn_state; + bool already_done = prev_drawn_state.size() == length_; + if (!already_done) prev_drawn_state.init_to_size(length_, 0); + for (int i = 0; i < length_; ++i) { + if (prev_drawn_state[i] != state_[i]) { + already_done = false; + } + prev_drawn_state[i] = state_[i]; + } + if (already_done || word->blobs.empty()) return; + + // Create the window if needed. + if (segm_window == NULL) { + segm_window = new ScrollView("Segmentation", 5, 10, 500, 256, + 2000.0, 256.0, true); + } else { + segm_window->Clear(); + } + + TBOX bbox; + int blob_index = 0; + for (int c = 0; c < length_; ++c) { + ScrollView::Color color = + static_cast(c % kNumColors + 3); + for (int i = 0; i < state_[c]; ++i, ++blob_index) { + TBLOB* blob = word->blobs[blob_index]; + bbox += blob->bounding_box(); + blob->plot(segm_window, color, color); + } + } + segm_window->ZoomToRectangle(bbox.left(), bbox.top(), + bbox.right(), bbox.bottom()); + segm_window->Update(); + window_wait(segm_window); +#endif +} + + +bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, + const WERD_CHOICE &word2) { + const UNICHARSET *uchset = word1.unicharset(); + if (word2.unicharset() != uchset) return false; + int w1start, w1end; + word1.punct_stripped(&w1start, &w1end); + int w2start, w2end; + word2.punct_stripped(&w2start, &w2end); + if (w1end - w1start != w2end - w2start) return false; + for (int i = 0; i < w1end - w1start; i++) { + if (uchset->to_lower(word1.unichar_id(w1start + i)) != + uchset->to_lower(word2.unichar_id(w2start + i))) { + return false; + } + } + return true; +} + +/** + * print_ratings_list + * + * Send all the ratings out to the logfile. + * + * @param msg intro message + * @param ratings list of ratings + * @param current_unicharset unicharset that can be used + * for id-to-unichar conversion + */ +void print_ratings_list(const char *msg, + BLOB_CHOICE_LIST *ratings, + const UNICHARSET ¤t_unicharset) { + if (ratings->length() == 0) { + tprintf("%s:\n", msg); + return; + } + if (*msg != '\0') { + tprintf("%s\n", msg); + } + BLOB_CHOICE_IT c_it; + c_it.set_to_list(ratings); + for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { + c_it.data()->print(¤t_unicharset); + if (!c_it.at_last()) tprintf("\n"); + } + tprintf("\n"); + fflush(stdout); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.h new file mode 100644 index 0000000..2ee9c94 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/ratngs.h @@ -0,0 +1,643 @@ +/********************************************************************** + * File: ratngs.h (Formerly ratings.h) + * Description: Definition of the WERD_CHOICE and BLOB_CHOICE classes. + * Author: Ray Smith + * Created: Thu Apr 23 11:40:38 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RATNGS_H +#define RATNGS_H + +#include + +#include "clst.h" +#include "elst.h" +#include "fontinfo.h" +#include "genericvector.h" +#include "matrix.h" +#include "unichar.h" +#include "unicharset.h" +#include "werd.h" + +class MATRIX; +struct TBLOB; +struct TWERD; + +// Enum to describe the source of a BLOB_CHOICE to make it possible to determine +// whether a blob has been classified by inspecting the BLOB_CHOICEs. +enum BlobChoiceClassifier { + BCC_STATIC_CLASSIFIER, // From the char_norm classifier. + BCC_ADAPTED_CLASSIFIER, // From the adaptive classifier. + BCC_SPECKLE_CLASSIFIER, // Backup for failed classification. + BCC_AMBIG, // Generated by ambiguity detection. + BCC_FAKE, // From some other process. +}; + +class BLOB_CHOICE: public ELIST_LINK +{ + public: + BLOB_CHOICE() { + unichar_id_ = UNICHAR_SPACE; + fontinfo_id_ = -1; + fontinfo_id2_ = -1; + rating_ = 10.0; + certainty_ = -1.0; + script_id_ = -1; + xgap_before_ = 0; + xgap_after_ = 0; + min_xheight_ = 0.0f; + max_xheight_ = 0.0f; + yshift_ = 0.0f; + classifier_ = BCC_FAKE; + } + BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id + float src_rating, // rating + float src_cert, // certainty + int script_id, // script + float min_xheight, // min xheight in image pixel units + float max_xheight, // max xheight allowed by this char + float yshift, // the larger of y shift (top or bottom) + BlobChoiceClassifier c); // adapted match or other + BLOB_CHOICE(const BLOB_CHOICE &other); + ~BLOB_CHOICE() {} + + UNICHAR_ID unichar_id() const { + return unichar_id_; + } + float rating() const { + return rating_; + } + float certainty() const { + return certainty_; + } + inT16 fontinfo_id() const { + return fontinfo_id_; + } + inT16 fontinfo_id2() const { + return fontinfo_id2_; + } + const GenericVector& fonts() const { + return fonts_; + } + void set_fonts(const GenericVector& fonts) { + fonts_ = fonts; + int score1 = 0, score2 = 0; + fontinfo_id_ = -1; + fontinfo_id2_ = -1; + for (int f = 0; f < fonts_.size(); ++f) { + if (fonts_[f].score > score1) { + score2 = score1; + fontinfo_id2_ = fontinfo_id_; + score1 = fonts_[f].score; + fontinfo_id_ = fonts_[f].fontinfo_id; + } else if (fonts_[f].score > score2) { + score2 = fonts_[f].score; + fontinfo_id2_ = fonts_[f].fontinfo_id; + } + } + } + int script_id() const { + return script_id_; + } + const MATRIX_COORD& matrix_cell() { + return matrix_cell_; + } + inT16 xgap_before() const { + return xgap_before_; + } + inT16 xgap_after() const { + return xgap_after_; + } + float min_xheight() const { + return min_xheight_; + } + float max_xheight() const { + return max_xheight_; + } + float yshift() const { + return yshift_; + } + BlobChoiceClassifier classifier() const { + return classifier_; + } + bool IsAdapted() const { + return classifier_ == BCC_ADAPTED_CLASSIFIER; + } + bool IsClassified() const { + return classifier_ == BCC_STATIC_CLASSIFIER || + classifier_ == BCC_ADAPTED_CLASSIFIER || + classifier_ == BCC_SPECKLE_CLASSIFIER; + } + + void set_unichar_id(UNICHAR_ID newunichar_id) { + unichar_id_ = newunichar_id; + } + void set_rating(float newrat) { + rating_ = newrat; + } + void set_certainty(float newrat) { + certainty_ = newrat; + } + void set_script(int newscript_id) { + script_id_ = newscript_id; + } + void set_matrix_cell(int col, int row) { + matrix_cell_.col = col; + matrix_cell_.row = row; + } + void set_xgap_before(inT16 gap) { + xgap_before_ = gap; + } + void set_xgap_after(inT16 gap) { + xgap_after_ = gap; + } + void set_classifier(BlobChoiceClassifier classifier) { + classifier_ = classifier; + } + static BLOB_CHOICE* deep_copy(const BLOB_CHOICE* src) { + BLOB_CHOICE* choice = new BLOB_CHOICE; + *choice = *src; + return choice; + } + // Returns true if *this and other agree on the baseline and x-height + // to within some tolerance based on a given estimate of the x-height. + bool PosAndSizeAgree(const BLOB_CHOICE& other, float x_height, + bool debug) const; + + void print(const UNICHARSET *unicharset) const { + tprintf("r%.2f c%.2f x[%g,%g]: %d %s", + rating_, certainty_, + min_xheight_, max_xheight_, unichar_id_, + (unicharset == NULL) ? "" : + unicharset->debug_str(unichar_id_).string()); + } + void print_full() const { + print(NULL); + tprintf(" script=%d, font1=%d, font2=%d, yshift=%g, classifier=%d\n", + script_id_, fontinfo_id_, fontinfo_id2_, yshift_, classifier_); + } + // Sort function for sorting BLOB_CHOICEs in increasing order of rating. + static int SortByRating(const void *p1, const void *p2) { + const BLOB_CHOICE *bc1 = + *reinterpret_cast(p1); + const BLOB_CHOICE *bc2 = + *reinterpret_cast(p2); + return (bc1->rating_ < bc2->rating_) ? -1 : 1; + } + + private: + UNICHAR_ID unichar_id_; // unichar id + // Fonts and scores. Allowed to be empty. + GenericVector fonts_; + inT16 fontinfo_id_; // char font information + inT16 fontinfo_id2_; // 2nd choice font information + // Rating is the classifier distance weighted by the length of the outline + // in the blob. In terms of probability, classifier distance is -klog p such + // that the resulting distance is in the range [0, 1] and then + // rating = w (-k log p) where w is the weight for the length of the outline. + // Sums of ratings may be compared meaningfully for words of different + // segmentation. + float rating_; // size related + // Certainty is a number in [-20, 0] indicating the classifier certainty + // of the choice. In terms of probability, certainty = 20 (k log p) where + // k is defined as above to normalize -klog p to the range [0, 1]. + float certainty_; // absolute + int script_id_; + // Holds the position of this choice in the ratings matrix. + // Used to location position in the matrix during path backtracking. + MATRIX_COORD matrix_cell_; + inT16 xgap_before_; + inT16 xgap_after_; + // X-height range (in image pixels) that this classification supports. + float min_xheight_; + float max_xheight_; + // yshift_ - The vertical distance (in image pixels) the character is + // shifted (up or down) from an acceptable y position. + float yshift_; + BlobChoiceClassifier classifier_; // What generated *this. +}; + +// Make BLOB_CHOICE listable. +ELISTIZEH(BLOB_CHOICE) + +// Return the BLOB_CHOICE in bc_list matching a given unichar_id, +// or NULL if there is no match. +BLOB_CHOICE *FindMatchingChoice(UNICHAR_ID char_id, BLOB_CHOICE_LIST *bc_list); + +// Permuter codes used in WERD_CHOICEs. +enum PermuterType { + NO_PERM, // 0 + PUNC_PERM, // 1 + TOP_CHOICE_PERM, // 2 + LOWER_CASE_PERM, // 3 + UPPER_CASE_PERM, // 4 + NGRAM_PERM, // 5 + NUMBER_PERM, // 6 + USER_PATTERN_PERM, // 7 + SYSTEM_DAWG_PERM, // 8 + DOC_DAWG_PERM, // 9 + USER_DAWG_PERM, // 10 + FREQ_DAWG_PERM, // 11 + COMPOUND_PERM, // 12 + + NUM_PERMUTER_TYPES +}; + +namespace tesseract { +// ScriptPos tells whether a character is subscript, superscript or normal. +enum ScriptPos { + SP_NORMAL, + SP_SUBSCRIPT, + SP_SUPERSCRIPT, + SP_DROPCAP +}; + +const char *ScriptPosToString(tesseract::ScriptPos script_pos); + +} // namespace tesseract. + +class TESS_API WERD_CHOICE : public ELIST_LINK { + public: + static const float kBadRating; + static const char *permuter_name(uinT8 permuter); + + WERD_CHOICE(const UNICHARSET *unicharset) + : unicharset_(unicharset) { this->init(8); } + WERD_CHOICE(const UNICHARSET *unicharset, int reserved) + : unicharset_(unicharset) { this->init(reserved); } + WERD_CHOICE(const char *src_string, + const char *src_lengths, + float src_rating, + float src_certainty, + uinT8 src_permuter, + const UNICHARSET &unicharset) + : unicharset_(&unicharset) { + this->init(src_string, src_lengths, src_rating, + src_certainty, src_permuter); + } + WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset); + WERD_CHOICE(const WERD_CHOICE &word) + : ELIST_LINK(word), unicharset_(word.unicharset_) { + this->init(word.length()); + this->operator=(word); + } + ~WERD_CHOICE(); + + const UNICHARSET *unicharset() const { + return unicharset_; + } + inline int length() const { + return length_; + } + float adjust_factor() const { + return adjust_factor_; + } + void set_adjust_factor(float factor) { + adjust_factor_ = factor; + } + inline const UNICHAR_ID *unichar_ids() const { + return unichar_ids_; + } + inline UNICHAR_ID unichar_id(int index) const { + assert(index < length_); + return unichar_ids_[index]; + } + inline int state(int index) const { + return state_[index]; + } + tesseract::ScriptPos BlobPosition(int index) const { + if (index < 0 || index >= length_) + return tesseract::SP_NORMAL; + return script_pos_[index]; + } + inline float rating() const { + return rating_; + } + inline float certainty() const { + return certainty_; + } + inline float certainty(int index) const { + return certainties_[index]; + } + inline float min_x_height() const { + return min_x_height_; + } + inline float max_x_height() const { + return max_x_height_; + } + inline void set_x_heights(float min_height, float max_height) { + min_x_height_ = min_height; + max_x_height_ = max_height; + } + inline uinT8 permuter() const { + return permuter_; + } + const char *permuter_name() const; + // Returns the BLOB_CHOICE_LIST corresponding to the given index in the word, + // taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. + BLOB_CHOICE_LIST* blob_choices(int index, MATRIX* ratings) const; + + // Returns the MATRIX_COORD corresponding to the location in the ratings + // MATRIX for the given index into the word. + MATRIX_COORD MatrixCoord(int index) const; + + inline void set_unichar_id(UNICHAR_ID unichar_id, int index) { + assert(index < length_); + unichar_ids_[index] = unichar_id; + } + bool dangerous_ambig_found() const { + return dangerous_ambig_found_; + } + void set_dangerous_ambig_found_(bool value) { + dangerous_ambig_found_ = value; + } + inline void set_rating(float new_val) { + rating_ = new_val; + } + inline void set_certainty(float new_val) { + certainty_ = new_val; + } + inline void set_permuter(uinT8 perm) { + permuter_ = perm; + } + // Note: this function should only be used if all the fields + // are populated manually with set_* functions (rather than + // (copy)constructors and append_* functions). + inline void set_length(int len) { + ASSERT_HOST(reserved_ >= len); + length_ = len; + } + + /// Make more space in unichar_id_ and fragment_lengths_ arrays. + inline void double_the_size() { + if (reserved_ > 0) { + unichar_ids_ = GenericVector::double_the_size_memcpy( + reserved_, unichar_ids_); + script_pos_ = GenericVector::double_the_size_memcpy( + reserved_, script_pos_); + state_ = GenericVector::double_the_size_memcpy( + reserved_, state_); + certainties_ = GenericVector::double_the_size_memcpy( + reserved_, certainties_); + reserved_ *= 2; + } else { + unichar_ids_ = new UNICHAR_ID[1]; + script_pos_ = new tesseract::ScriptPos[1]; + state_ = new int[1]; + certainties_ = new float[1]; + reserved_ = 1; + } + } + + /// Initializes WERD_CHOICE - reserves length slots in unichar_ids_ and + /// fragment_length_ arrays. Sets other values to default (blank) values. + inline void init(int reserved) { + reserved_ = reserved; + if (reserved > 0) { + unichar_ids_ = new UNICHAR_ID[reserved]; + script_pos_ = new tesseract::ScriptPos[reserved]; + state_ = new int[reserved]; + certainties_ = new float[reserved]; + } else { + unichar_ids_ = NULL; + script_pos_ = NULL; + state_ = NULL; + certainties_ = NULL; + } + length_ = 0; + adjust_factor_ = 1.0f; + rating_ = 0.0; + certainty_ = MAX_FLOAT32; + min_x_height_ = 0.0f; + max_x_height_ = MAX_FLOAT32; + permuter_ = NO_PERM; + unichars_in_script_order_ = false; // Tesseract is strict left-to-right. + dangerous_ambig_found_ = false; + } + + /// Helper function to build a WERD_CHOICE from the given string, + /// fragment lengths, rating, certainty and permuter. + /// The function assumes that src_string is not NULL. + /// src_lengths argument could be NULL, in which case the unichars + /// in src_string are assumed to all be of length 1. + void init(const char *src_string, const char *src_lengths, + float src_rating, float src_certainty, + uinT8 src_permuter); + + /// Set the fields in this choice to be default (bad) values. + inline void make_bad() { + length_ = 0; + rating_ = kBadRating; + certainty_ = -MAX_FLOAT32; + } + + /// This function assumes that there is enough space reserved + /// in the WERD_CHOICE for adding another unichar. + /// This is an efficient alternative to append_unichar_id(). + inline void append_unichar_id_space_allocated( + UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty) { + assert(reserved_ > length_); + length_++; + this->set_unichar_id(unichar_id, blob_count, + rating, certainty, length_-1); + } + + void append_unichar_id(UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty); + + inline void set_unichar_id(UNICHAR_ID unichar_id, int blob_count, + float rating, float certainty, int index) { + assert(index < length_); + unichar_ids_[index] = unichar_id; + state_[index] = blob_count; + certainties_[index] = certainty; + script_pos_[index] = tesseract::SP_NORMAL; + rating_ += rating; + if (certainty < certainty_) { + certainty_ = certainty; + } + } + // Sets the entries for the given index from the BLOB_CHOICE, assuming + // unit fragment lengths, but setting the state for this index to blob_count. + void set_blob_choice(int index, int blob_count, + const BLOB_CHOICE* blob_choice); + + bool contains_unichar_id(UNICHAR_ID unichar_id) const; + void remove_unichar_ids(int index, int num); + inline void remove_last_unichar_id() { --length_; } + inline void remove_unichar_id(int index) { + this->remove_unichar_ids(index, 1); + } + bool has_rtl_unichar_id() const; + void reverse_and_mirror_unichar_ids(); + + // Returns the half-open interval of unichar_id indices [start, end) which + // enclose the core portion of this word -- the part after stripping + // punctuation from the left and right. + void punct_stripped(int *start_core, int *end_core) const; + + // Returns the indices [start, end) containing the core of the word, stripped + // of any superscript digits on either side. (i.e., the non-footnote part + // of the word). There is no guarantee that the output range is non-empty. + void GetNonSuperscriptSpan(int *start, int *end) const; + + // Return a copy of this WERD_CHOICE with the choices [start, end). + // The result is useful only for checking against a dictionary. + WERD_CHOICE shallow_copy(int start, int end) const; + + void string_and_lengths(STRING *word_str, STRING *word_lengths_str) const; + const STRING debug_string() const { + STRING word_str; + for (int i = 0; i < length_; ++i) { + word_str += unicharset_->debug_str(unichar_ids_[i]); + word_str += " "; + } + return word_str; + } + + // Call this to override the default (strict left to right graphemes) + // with the fact that some engine produces a "reading order" set of + // Graphemes for each word. + bool set_unichars_in_script_order(bool in_script_order) { + return unichars_in_script_order_ = in_script_order; + } + + bool unichars_in_script_order() const { + return unichars_in_script_order_; + } + + // Returns a UTF-8 string equivalent to the current choice + // of UNICHAR IDs. + const STRING &unichar_string() const { + this->string_and_lengths(&unichar_string_, &unichar_lengths_); + return unichar_string_; + } + + // Returns the lengths, one byte each, representing the number of bytes + // required in the unichar_string for each UNICHAR_ID. + const STRING &unichar_lengths() const { + this->string_and_lengths(&unichar_string_, &unichar_lengths_); + return unichar_lengths_; + } + + // Sets up the script_pos_ member using the blobs_list to get the bln + // bounding boxes, *this to get the unichars, and this->unicharset + // to get the target positions. If small_caps is true, sub/super are not + // considered, but dropcaps are. + // NOTE: blobs_list should be the chopped_word blobs. (Fully segemented.) + void SetScriptPositions(bool small_caps, TWERD* word); + // Sets the script_pos_ member from some source positions with a given length. + void SetScriptPositions(const tesseract::ScriptPos* positions, int length); + // Sets all the script_pos_ positions to the given position. + void SetAllScriptPositions(tesseract::ScriptPos position); + + static tesseract::ScriptPos ScriptPositionOf(bool print_debug, + const UNICHARSET& unicharset, + const TBOX& blob_box, + UNICHAR_ID unichar_id); + + // Returns the "dominant" script ID for the word. By "dominant", the script + // must account for at least half the characters. Otherwise, it returns 0. + // Note that for Japanese, Hiragana and Katakana are simply treated as Han. + int GetTopScriptID() const; + + // Fixes the state_ for a chop at the given blob_posiiton. + void UpdateStateForSplit(int blob_position); + + // Returns the sum of all the state elements, being the total number of blobs. + int TotalOfStates() const; + + void print() const { this->print(""); } + void print(const char *msg) const; + // Prints the segmentation state with an introductory message. + void print_state(const char *msg) const; + + // Displays the segmentation state of *this (if not the same as the last + // one displayed) and waits for a click in the window. + void DisplaySegmentation(TWERD* word); + + WERD_CHOICE& operator+= ( // concatanate + const WERD_CHOICE & second);// second on first + + WERD_CHOICE& operator= (const WERD_CHOICE& source); + + private: + const UNICHARSET *unicharset_; + // TODO(rays) Perhaps replace the multiple arrays with an array of structs? + // unichar_ids_ is an array of classifier "results" that make up a word. + // For each unichar_ids_[i], script_pos_[i] has the sub/super/normal position + // of each unichar_id. + // state_[i] indicates the number of blobs in WERD_RES::chopped_word that + // were put together to make the classification results in the ith position + // in unichar_ids_, and certainties_[i] is the certainty of the choice that + // was used in this word. + // == Change from before == + // Previously there was fragment_lengths_ that allowed a word to be + // artificially composed of multiple fragment results. Since the new + // segmentation search doesn't do fragments, treatment of fragments has + // been moved to a lower level, augmenting the ratings matrix with the + // combined fragments, and allowing the language-model/segmentation-search + // to deal with only the combined unichar_ids. + UNICHAR_ID *unichar_ids_; // unichar ids that represent the text of the word + tesseract::ScriptPos* script_pos_; // Normal/Sub/Superscript of each unichar. + int* state_; // Number of blobs in each unichar. + float* certainties_; // Certainty of each unichar. + int reserved_; // size of the above arrays + int length_; // word length + // Factor that was used to adjust the rating. + float adjust_factor_; + // Rating is the sum of the ratings of the individual blobs in the word. + float rating_; // size related + // certainty is the min (worst) certainty of the individual blobs in the word. + float certainty_; // absolute + // xheight computed from the result, or 0 if inconsistent. + float min_x_height_; + float max_x_height_; + uinT8 permuter_; // permuter code + + // Normally, the ratings_ matrix represents the recognition results in order + // from left-to-right. However, some engines (say Cube) may return + // recognition results in the order of the script's major reading direction + // (for Arabic, that is right-to-left). + bool unichars_in_script_order_; + // True if NoDangerousAmbig found an ambiguity. + bool dangerous_ambig_found_; + + // The following variables are populated and passed by reference any + // time unichar_string() or unichar_lengths() are called. + mutable STRING unichar_string_; + mutable STRING unichar_lengths_; +}; + +// Make WERD_CHOICE listable. +ELISTIZEH(WERD_CHOICE) +typedef GenericVector BLOB_CHOICE_LIST_VECTOR; + +// Utilities for comparing WERD_CHOICEs + +bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, + const WERD_CHOICE &word2); + +// Utilities for debug printing. +void print_ratings_list( + const char *msg, // intro message + BLOB_CHOICE_LIST *ratings, // list of results + const UNICHARSET ¤t_unicharset // unicharset that can be used + // for id-to-unichar conversion + ); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.cpp new file mode 100644 index 0000000..4a9fe00 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.cpp @@ -0,0 +1,263 @@ +/********************************************************************** + * File: rect.c (Formerly box.c) + * Description: Bounding box class definition. + * Author: Phil Cheatle + * Created: Wed Oct 16 15:18:45 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "rect.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +/********************************************************************** + * TBOX::TBOX() Constructor from 2 ICOORDS + * + **********************************************************************/ + +TBOX::TBOX( // constructor + const ICOORD pt1, // one corner + const ICOORD pt2 // the other corner + ) { + if (pt1.x () <= pt2.x ()) { + if (pt1.y () <= pt2.y ()) { + bot_left = pt1; + top_right = pt2; + } + else { + bot_left = ICOORD (pt1.x (), pt2.y ()); + top_right = ICOORD (pt2.x (), pt1.y ()); + } + } + else { + if (pt1.y () <= pt2.y ()) { + bot_left = ICOORD (pt2.x (), pt1.y ()); + top_right = ICOORD (pt1.x (), pt2.y ()); + } + else { + bot_left = pt2; + top_right = pt1; + } + } +} + +/********************************************************************** + * TBOX::TBOX() Constructor from 4 integer values. + * Note: It is caller's responsibility to provide values in the right + * order. + **********************************************************************/ + +TBOX::TBOX( //constructor + inT16 left, inT16 bottom, inT16 right, inT16 top) + : bot_left(left, bottom), top_right(right, top) { +} + +// rotate_large constructs the containing bounding box of all 4 +// corners after rotating them. It therefore guarantees that all +// original content is contained within, but also slightly enlarges the box. +void TBOX::rotate_large(const FCOORD& vec) { + ICOORD top_left(bot_left.x(), top_right.y()); + ICOORD bottom_right(top_right.x(), bot_left.y()); + top_left.rotate(vec); + bottom_right.rotate(vec); + rotate(vec); + TBOX box2(top_left, bottom_right); + *this += box2; +} + +/********************************************************************** + * TBOX::intersection() Build the largest box contained in both boxes + * + **********************************************************************/ + +TBOX TBOX::intersection( //shared area box + const TBOX &box) const { + inT16 left; + inT16 bottom; + inT16 right; + inT16 top; + if (overlap (box)) { + if (box.bot_left.x () > bot_left.x ()) + left = box.bot_left.x (); + else + left = bot_left.x (); + + if (box.top_right.x () < top_right.x ()) + right = box.top_right.x (); + else + right = top_right.x (); + + if (box.bot_left.y () > bot_left.y ()) + bottom = box.bot_left.y (); + else + bottom = bot_left.y (); + + if (box.top_right.y () < top_right.y ()) + top = box.top_right.y (); + else + top = top_right.y (); + } + else { + left = MAX_INT16; + bottom = MAX_INT16; + top = -MAX_INT16; + right = -MAX_INT16; + } + return TBOX (left, bottom, right, top); +} + + +/********************************************************************** + * TBOX::bounding_union() Build the smallest box containing both boxes + * + **********************************************************************/ + +TBOX TBOX::bounding_union( //box enclosing both + const TBOX &box) const { + ICOORD bl; //bottom left + ICOORD tr; //top right + + if (box.bot_left.x () < bot_left.x ()) + bl.set_x (box.bot_left.x ()); + else + bl.set_x (bot_left.x ()); + + if (box.top_right.x () > top_right.x ()) + tr.set_x (box.top_right.x ()); + else + tr.set_x (top_right.x ()); + + if (box.bot_left.y () < bot_left.y ()) + bl.set_y (box.bot_left.y ()); + else + bl.set_y (bot_left.y ()); + + if (box.top_right.y () > top_right.y ()) + tr.set_y (box.top_right.y ()); + else + tr.set_y (top_right.y ()); + return TBOX (bl, tr); +} + + +/********************************************************************** + * TBOX::plot() Paint a box using specified settings + * + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void TBOX::plot( //paint box + ScrollView* fd, //where to paint + ScrollView::Color fill_colour, //colour for inside + ScrollView::Color border_colour //colour for border + ) const { + fd->Brush(fill_colour); + fd->Pen(border_colour); + plot(fd); +} +#endif + +// Appends the bounding box as (%d,%d)->(%d,%d) to a STRING. +void TBOX::print_to_str(STRING *str) const { + // "(%d,%d)->(%d,%d)", left(), bottom(), right(), top() + str->add_str_int("(", left()); + str->add_str_int(",", bottom()); + str->add_str_int(")->(", right()); + str->add_str_int(",", top()); + *str += ')'; +} + +// Writes to the given file. Returns false in case of error. +bool TBOX::Serialize(FILE* fp) const { + if (!bot_left.Serialize(fp)) return false; + if (!top_right.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 TBOX::DeSerialize(bool swap, FILE* fp) { + if (!bot_left.DeSerialize(swap, fp)) return false; + if (!top_right.DeSerialize(swap, fp)) return false; + return true; +} + +/********************************************************************** + * operator+= + * + * Extend one box to include the other (In place union) + **********************************************************************/ + +DLLSYM TBOX & +operator+= ( //bounding bounding bx +TBOX & op1, //operands +const TBOX & op2) { + if (op2.bot_left.x () < op1.bot_left.x ()) + op1.bot_left.set_x (op2.bot_left.x ()); + + if (op2.top_right.x () > op1.top_right.x ()) + op1.top_right.set_x (op2.top_right.x ()); + + if (op2.bot_left.y () < op1.bot_left.y ()) + op1.bot_left.set_y (op2.bot_left.y ()); + + if (op2.top_right.y () > op1.top_right.y ()) + op1.top_right.set_y (op2.top_right.y ()); + + return op1; +} + + +/********************************************************************** + * operator&= + * + * Reduce one box to intersection with the other (In place intersection) + **********************************************************************/ + +TBOX& operator&=(TBOX& op1, const TBOX& op2) { + if (op1.overlap (op2)) { + if (op2.bot_left.x () > op1.bot_left.x ()) + op1.bot_left.set_x (op2.bot_left.x ()); + + if (op2.top_right.x () < op1.top_right.x ()) + op1.top_right.set_x (op2.top_right.x ()); + + if (op2.bot_left.y () > op1.bot_left.y ()) + op1.bot_left.set_y (op2.bot_left.y ()); + + if (op2.top_right.y () < op1.top_right.y ()) + op1.top_right.set_y (op2.top_right.y ()); + } + else { + op1.bot_left.set_x (MAX_INT16); + op1.bot_left.set_y (MAX_INT16); + op1.top_right.set_x (-MAX_INT16); + op1.top_right.set_y (-MAX_INT16); + } + return op1; +} + +bool TBOX::x_almost_equal(const TBOX &box, int tolerance) const { + return (abs(left() - box.left()) <= tolerance && + abs(right() - box.right()) <= tolerance); +} + +bool TBOX::almost_equal(const TBOX &box, int tolerance) const { + return (abs(left() - box.left()) <= tolerance && + abs(right() - box.right()) <= tolerance && + abs(top() - box.top()) <= tolerance && + abs(bottom() - box.bottom()) <= tolerance); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.h new file mode 100644 index 0000000..f31247a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/rect.h @@ -0,0 +1,484 @@ +/********************************************************************** + * File: rect.h (Formerly box.h) + * Description: Bounding box class definition. + * Author: Phil Cheatle + * Created: Wed Oct 16 15:18:45 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RECT_H +#define RECT_H + +#include +#include "points.h" +#include "ndminx.h" +#include "scrollview.h" +#include "strngs.h" +#include "tprintf.h" + +class DLLSYM TBOX { // bounding box + public: + TBOX (): // empty constructor making a null box + bot_left (MAX_INT16, MAX_INT16), top_right (-MAX_INT16, -MAX_INT16) { + } + + TBOX( // constructor + const ICOORD pt1, // one corner + const ICOORD pt2); // the other corner + + TBOX( // constructor + inT16 left, inT16 bottom, inT16 right, inT16 top); + + TBOX( // box around FCOORD + const FCOORD pt); + + bool null_box() const { // Is box null + return ((left () >= right ()) || (top () <= bottom ())); + } + + bool operator==(const TBOX& other) const { + return bot_left == other.bot_left && top_right == other.top_right; + } + + inT16 top() const { // coord of top + return top_right.y (); + } + void set_top(int y) { + top_right.set_y(y); + } + + inT16 bottom() const { // coord of bottom + return bot_left.y (); + } + void set_bottom(int y) { + bot_left.set_y(y); + } + + inT16 left() const { // coord of left + return bot_left.x (); + } + void set_left(int x) { + bot_left.set_x(x); + } + + inT16 right() const { // coord of right + return top_right.x (); + } + void set_right(int x) { + top_right.set_x(x); + } + int x_middle() const { + return (bot_left.x() + top_right.x()) / 2; + } + int y_middle() const { + return (bot_left.y() + top_right.y()) / 2; + } + + const ICOORD &botleft() const { // access function + return bot_left; + } + + ICOORD botright() const { // ~ access function + return ICOORD (top_right.x (), bot_left.y ()); + } + + ICOORD topleft() const { // ~ access function + return ICOORD (bot_left.x (), top_right.y ()); + } + + const ICOORD &topright() const { // access function + return top_right; + } + + inT16 height() const { // how high is it? + if (!null_box ()) + return top_right.y () - bot_left.y (); + else + return 0; + } + + inT16 width() const { // how high is it? + if (!null_box ()) + return top_right.x () - bot_left.x (); + else + return 0; + } + + inT32 area() const { // what is the area? + if (!null_box ()) + return width () * height (); + else + return 0; + } + + // Pads the box on either side by the supplied x,y pad amounts. + // NO checks for exceeding any bounds like 0 or an image size. + void pad(int xpad, int ypad) { + ICOORD pad(xpad, ypad); + bot_left -= pad; + top_right += pad; + } + + void move_bottom_edge( // move one edge + const inT16 y) { // by +/- y + bot_left += ICOORD (0, y); + } + + void move_left_edge( // move one edge + const inT16 x) { // by +/- x + bot_left += ICOORD (x, 0); + } + + void move_right_edge( // move one edge + const inT16 x) { // by +/- x + top_right += ICOORD (x, 0); + } + + void move_top_edge( // move one edge + const inT16 y) { // by +/- y + top_right += ICOORD (0, y); + } + + void move( // move box + const ICOORD vec) { // by vector + bot_left += vec; + top_right += vec; + } + + void move( // move box + const FCOORD vec) { // by float vector + bot_left.set_x ((inT16) floor (bot_left.x () + vec.x ())); + // round left + bot_left.set_y ((inT16) floor (bot_left.y () + vec.y ())); + // round down + top_right.set_x ((inT16) ceil (top_right.x () + vec.x ())); + // round right + top_right.set_y ((inT16) ceil (top_right.y () + vec.y ())); + // round up + } + + void scale( // scale box + const float f) { // by multiplier + bot_left.set_x ((inT16) floor (bot_left.x () * f)); // round left + bot_left.set_y ((inT16) floor (bot_left.y () * f)); // round down + top_right.set_x ((inT16) ceil (top_right.x () * f)); // round right + top_right.set_y ((inT16) ceil (top_right.y () * f)); // round up + } + void scale( // scale box + const FCOORD vec) { // by float vector + bot_left.set_x ((inT16) floor (bot_left.x () * vec.x ())); + bot_left.set_y ((inT16) floor (bot_left.y () * vec.y ())); + top_right.set_x ((inT16) ceil (top_right.x () * vec.x ())); + top_right.set_y ((inT16) ceil (top_right.y () * vec.y ())); + } + + // rotate doesn't enlarge the box - it just rotates the bottom-left + // and top-right corners. Use rotate_large if you want to guarantee + // that all content is contained within the rotated box. + void rotate(const FCOORD& vec) { // by vector + bot_left.rotate (vec); + top_right.rotate (vec); + *this = TBOX (bot_left, top_right); + } + // rotate_large constructs the containing bounding box of all 4 + // corners after rotating them. It therefore guarantees that all + // original content is contained within, but also slightly enlarges the box. + void rotate_large(const FCOORD& vec); + + bool contains( // is pt inside box + const FCOORD pt) const; + + bool contains( // is box inside box + const TBOX &box) const; + + bool overlap( // do boxes overlap + const TBOX &box) const; + + bool major_overlap( // do boxes overlap more than half + const TBOX &box) const; + + // Do boxes overlap on x axis. + bool x_overlap(const TBOX &box) const; + + // Return the horizontal gap between the boxes. If the boxes + // overlap horizontally then the return value is negative, indicating + // the amount of the overlap. + int x_gap(const TBOX& box) const { + return MAX(bot_left.x(), box.bot_left.x()) - + MIN(top_right.x(), box.top_right.x()); + } + + // Return the vertical gap between the boxes. If the boxes + // overlap vertically then the return value is negative, indicating + // the amount of the overlap. + int y_gap(const TBOX& box) const { + return MAX(bot_left.y(), box.bot_left.y()) - + MIN(top_right.y(), box.top_right.y()); + } + + // Do boxes overlap on x axis by more than + // half of the width of the narrower box. + bool major_x_overlap(const TBOX &box) const; + + // Do boxes overlap on y axis. + bool y_overlap(const TBOX &box) const; + + // Do boxes overlap on y axis by more than + // half of the height of the shorter box. + bool major_y_overlap(const TBOX &box) const; + + // fraction of current box's area covered by other + double overlap_fraction(const TBOX &box) const; + + // fraction of the current box's projected area covered by the other's + double x_overlap_fraction(const TBOX& box) const; + + // fraction of the current box's projected area covered by the other's + double y_overlap_fraction(const TBOX& box) const; + + // Returns true if the boxes are almost equal on x axis. + bool x_almost_equal(const TBOX &box, int tolerance) const; + + // Returns true if the boxes are almost equal + bool almost_equal(const TBOX &box, int tolerance) const; + + TBOX intersection( // shared area box + const TBOX &box) const; + + TBOX bounding_union( // box enclosing both + const TBOX &box) const; + + // Sets the box boundaries to the given coordinates. + void set_to_given_coords(int x_min, int y_min, int x_max, int y_max) { + bot_left.set_x(x_min); + bot_left.set_y(y_min); + top_right.set_x(x_max); + top_right.set_y(y_max); + } + + void print() const { // print + tprintf("Bounding box=(%d,%d)->(%d,%d)\n", + left(), bottom(), right(), top()); + } + // Appends the bounding box as (%d,%d)->(%d,%d) to a STRING. + void print_to_str(STRING *str) const; + +#ifndef GRAPHICS_DISABLED + void plot( // use current settings + ScrollView* fd) const { // where to paint + fd->Rectangle(bot_left.x (), bot_left.y (), top_right.x (), + top_right.y ()); + } + + void plot( // paint box + ScrollView* fd, // where to paint + ScrollView::Color fill_colour, // colour for inside + ScrollView::Color border_colour) const; // colour for border +#endif + // 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); + + friend TBOX& operator+=(TBOX&, const TBOX&); + // in place union + friend TBOX& operator&=(TBOX&, const TBOX&); + // in place intersection + + private: + ICOORD bot_left; // bottom left corner + ICOORD top_right; // top right corner +}; + +/********************************************************************** + * TBOX::TBOX() Constructor from 1 FCOORD + * + **********************************************************************/ + +inline TBOX::TBOX( // constructor + const FCOORD pt // floating centre + ) { + bot_left = ICOORD ((inT16) floor (pt.x ()), (inT16) floor (pt.y ())); + top_right = ICOORD ((inT16) ceil (pt.x ()), (inT16) ceil (pt.y ())); +} + + +/********************************************************************** + * TBOX::contains() Is point within box + * + **********************************************************************/ + +inline bool TBOX::contains(const FCOORD pt) const { + return ((pt.x () >= bot_left.x ()) && + (pt.x () <= top_right.x ()) && + (pt.y () >= bot_left.y ()) && (pt.y () <= top_right.y ())); +} + + +/********************************************************************** + * TBOX::contains() Is box within box + * + **********************************************************************/ + +inline bool TBOX::contains(const TBOX &box) const { + return (contains (box.bot_left) && contains (box.top_right)); +} + + +/********************************************************************** + * TBOX::overlap() Do two boxes overlap? + * + **********************************************************************/ + +inline bool TBOX::overlap( // do boxes overlap + const TBOX &box) const { + return ((box.bot_left.x () <= top_right.x ()) && + (box.top_right.x () >= bot_left.x ()) && + (box.bot_left.y () <= top_right.y ()) && + (box.top_right.y () >= bot_left.y ())); +} + +/********************************************************************** + * TBOX::major_overlap() Do two boxes overlap by at least half of the smallest? + * + **********************************************************************/ + +inline bool TBOX::major_overlap( // Do boxes overlap more that half. + const TBOX &box) const { + int overlap = MIN(box.top_right.x(), top_right.x()); + overlap -= MAX(box.bot_left.x(), bot_left.x()); + overlap += overlap; + if (overlap < MIN(box.width(), width())) + return false; + overlap = MIN(box.top_right.y(), top_right.y()); + overlap -= MAX(box.bot_left.y(), bot_left.y()); + overlap += overlap; + if (overlap < MIN(box.height(), height())) + return false; + return true; +} + +/********************************************************************** + * TBOX::overlap_fraction() Fraction of area covered by the other box + * + **********************************************************************/ + +inline double TBOX::overlap_fraction(const TBOX &box) const { + double fraction = 0.0; + if (this->area()) { + fraction = this->intersection(box).area() * 1.0 / this->area(); + } + return fraction; +} + +/********************************************************************** + * TBOX::x_overlap() Do two boxes overlap on x-axis + * + **********************************************************************/ + +inline bool TBOX::x_overlap(const TBOX &box) const { + return ((box.bot_left.x() <= top_right.x()) && + (box.top_right.x() >= bot_left.x())); +} + +/********************************************************************** + * TBOX::major_x_overlap() Do two boxes overlap by more than half the + * width of the narrower box on the x-axis + * + **********************************************************************/ + +inline bool TBOX::major_x_overlap(const TBOX &box) const { + inT16 overlap = box.width(); + if (this->left() > box.left()) { + overlap -= this->left() - box.left(); + } + if (this->right() < box.right()) { + overlap -= box.right() - this->right(); + } + return (overlap >= box.width() / 2 || overlap >= this->width() / 2); +} + +/********************************************************************** + * TBOX::y_overlap() Do two boxes overlap on y-axis + * + **********************************************************************/ + +inline bool TBOX::y_overlap(const TBOX &box) const { + return ((box.bot_left.y() <= top_right.y()) && + (box.top_right.y() >= bot_left.y())); +} + +/********************************************************************** + * TBOX::major_y_overlap() Do two boxes overlap by more than half the + * height of the shorter box on the y-axis + * + **********************************************************************/ + +inline bool TBOX::major_y_overlap(const TBOX &box) const { + inT16 overlap = box.height(); + if (this->bottom() > box.bottom()) { + overlap -= this->bottom() - box.bottom(); + } + if (this->top() < box.top()) { + overlap -= box.top() - this->top(); + } + return (overlap >= box.height() / 2 || overlap >= this->height() / 2); +} + +/********************************************************************** + * TBOX::x_overlap_fraction() Calculates the horizontal overlap of the + * given boxes as a fraction of this boxes + * width. + * + **********************************************************************/ + +inline double TBOX::x_overlap_fraction(const TBOX& other) const { + int low = MAX(left(), other.left()); + int high = MIN(right(), other.right()); + int width = right() - left(); + if (width == 0) { + int x = left(); + if (other.left() <= x && x <= other.right()) + return 1.0; + else + return 0.0; + } else { + return MAX(0, static_cast(high - low) / width); + } +} + +/********************************************************************** + * TBOX::y_overlap_fraction() Calculates the vertical overlap of the + * given boxes as a fraction of this boxes + * height. + * + **********************************************************************/ + +inline double TBOX::y_overlap_fraction(const TBOX& other) const { + int low = MAX(bottom(), other.bottom()); + int high = MIN(top(), other.top()); + int height = top() - bottom(); + if (height == 0) { + int y = bottom(); + if (other.bottom() <= y && y <= other.top()) + return 1.0; + else + return 0.0; + } else { + return MAX(0, static_cast(high - low) / height); + } +} + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.cpp new file mode 100644 index 0000000..a291067 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.cpp @@ -0,0 +1,521 @@ +/********************************************************************** + * File: rejctmap.cpp (Formerly rejmap.c) + * Description: REJ and REJMAP class functions. + * Author: Phil Cheatle + * Created: Thu Jun 9 13:46:38 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "host.h" +#include "rejctmap.h" +#include "params.h" + +BOOL8 REJ::perm_rejected() { //Is char perm reject? + return (flag (R_TESS_FAILURE) || + flag (R_SMALL_XHT) || + flag (R_EDGE_CHAR) || + flag (R_1IL_CONFLICT) || + flag (R_POSTNN_1IL) || + flag (R_REJ_CBLOB) || + flag (R_BAD_REPETITION) || flag (R_MM_REJECT)); +} + + +BOOL8 REJ::rej_before_nn_accept() { + return flag (R_POOR_MATCH) || + flag (R_NOT_TESS_ACCEPTED) || + flag (R_CONTAINS_BLANKS) || flag (R_BAD_PERMUTER); +} + + +BOOL8 REJ::rej_between_nn_and_mm() { + return flag (R_HYPHEN) || + flag (R_DUBIOUS) || + flag (R_NO_ALPHANUMS) || flag (R_MOSTLY_REJ) || flag (R_XHT_FIXUP); +} + + +BOOL8 REJ::rej_between_mm_and_quality_accept() { + return flag (R_BAD_QUALITY); +} + + +BOOL8 REJ::rej_between_quality_and_minimal_rej_accept() { + return flag (R_DOC_REJ) || + flag (R_BLOCK_REJ) || flag (R_ROW_REJ) || flag (R_UNLV_REJ); +} + + +BOOL8 REJ::rej_before_mm_accept() { + return rej_between_nn_and_mm () || + (rej_before_nn_accept () && + !flag (R_NN_ACCEPT) && !flag (R_HYPHEN_ACCEPT)); +} + + +BOOL8 REJ::rej_before_quality_accept() { + return rej_between_mm_and_quality_accept () || + (!flag (R_MM_ACCEPT) && rej_before_mm_accept ()); +} + + +BOOL8 REJ::rejected() { //Is char rejected? + if (flag (R_MINIMAL_REJ_ACCEPT)) + return FALSE; + else + return (perm_rejected () || + rej_between_quality_and_minimal_rej_accept () || + (!flag (R_QUALITY_ACCEPT) && rej_before_quality_accept ())); +} + + +BOOL8 REJ::accept_if_good_quality() { //potential rej? + return (rejected () && + !perm_rejected () && + flag (R_BAD_PERMUTER) && + !flag (R_POOR_MATCH) && + !flag (R_NOT_TESS_ACCEPTED) && + !flag (R_CONTAINS_BLANKS) && + (!rej_between_nn_and_mm () && + !rej_between_mm_and_quality_accept () && + !rej_between_quality_and_minimal_rej_accept ())); +} + + +void REJ::setrej_tess_failure() { //Tess generated blank + set_flag(R_TESS_FAILURE); +} + + +void REJ::setrej_small_xht() { //Small xht char/wd + set_flag(R_SMALL_XHT); +} + + +void REJ::setrej_edge_char() { //Close to image edge + set_flag(R_EDGE_CHAR); +} + + +void REJ::setrej_1Il_conflict() { //Initial reject map + set_flag(R_1IL_CONFLICT); +} + + +void REJ::setrej_postNN_1Il() { //1Il after NN + set_flag(R_POSTNN_1IL); +} + + +void REJ::setrej_rej_cblob() { //Insert duff blob + set_flag(R_REJ_CBLOB); +} + + +void REJ::setrej_mm_reject() { //Matrix matcher + set_flag(R_MM_REJECT); +} + + +void REJ::setrej_bad_repetition() { //Odd repeated char + set_flag(R_BAD_REPETITION); +} + + +void REJ::setrej_poor_match() { //Failed Rays heuristic + set_flag(R_POOR_MATCH); +} + + +void REJ::setrej_not_tess_accepted() { + //TEMP reject_word + set_flag(R_NOT_TESS_ACCEPTED); +} + + +void REJ::setrej_contains_blanks() { + //TEMP reject_word + set_flag(R_CONTAINS_BLANKS); +} + + +void REJ::setrej_bad_permuter() { //POTENTIAL reject_word + set_flag(R_BAD_PERMUTER); +} + + +void REJ::setrej_hyphen() { //PostNN dubious hyphen or . + set_flag(R_HYPHEN); +} + + +void REJ::setrej_dubious() { //PostNN dubious limit + set_flag(R_DUBIOUS); +} + + +void REJ::setrej_no_alphanums() { //TEMP reject_word + set_flag(R_NO_ALPHANUMS); +} + + +void REJ::setrej_mostly_rej() { //TEMP reject_word + set_flag(R_MOSTLY_REJ); +} + + +void REJ::setrej_xht_fixup() { //xht fixup + set_flag(R_XHT_FIXUP); +} + + +void REJ::setrej_bad_quality() { //TEMP reject_word + set_flag(R_BAD_QUALITY); +} + + +void REJ::setrej_doc_rej() { //TEMP reject_word + set_flag(R_DOC_REJ); +} + + +void REJ::setrej_block_rej() { //TEMP reject_word + set_flag(R_BLOCK_REJ); +} + + +void REJ::setrej_row_rej() { //TEMP reject_word + set_flag(R_ROW_REJ); +} + + +void REJ::setrej_unlv_rej() { //TEMP reject_word + set_flag(R_UNLV_REJ); +} + + +void REJ::setrej_hyphen_accept() { //NN Flipped a char + set_flag(R_HYPHEN_ACCEPT); +} + + +void REJ::setrej_nn_accept() { //NN Flipped a char + set_flag(R_NN_ACCEPT); +} + + +void REJ::setrej_mm_accept() { //Matrix matcher + set_flag(R_MM_ACCEPT); +} + + +void REJ::setrej_quality_accept() { //Quality flip a char + set_flag(R_QUALITY_ACCEPT); +} + + +void REJ::setrej_minimal_rej_accept() { + //Accept all except blank + set_flag(R_MINIMAL_REJ_ACCEPT); +} + + +void REJ::full_print(FILE *fp) { + fprintf (fp, "R_TESS_FAILURE: %s\n", flag (R_TESS_FAILURE) ? "T" : "F"); + fprintf (fp, "R_SMALL_XHT: %s\n", flag (R_SMALL_XHT) ? "T" : "F"); + fprintf (fp, "R_EDGE_CHAR: %s\n", flag (R_EDGE_CHAR) ? "T" : "F"); + fprintf (fp, "R_1IL_CONFLICT: %s\n", flag (R_1IL_CONFLICT) ? "T" : "F"); + fprintf (fp, "R_POSTNN_1IL: %s\n", flag (R_POSTNN_1IL) ? "T" : "F"); + fprintf (fp, "R_REJ_CBLOB: %s\n", flag (R_REJ_CBLOB) ? "T" : "F"); + fprintf (fp, "R_MM_REJECT: %s\n", flag (R_MM_REJECT) ? "T" : "F"); + fprintf (fp, "R_BAD_REPETITION: %s\n", flag (R_BAD_REPETITION) ? "T" : "F"); + fprintf (fp, "R_POOR_MATCH: %s\n", flag (R_POOR_MATCH) ? "T" : "F"); + fprintf (fp, "R_NOT_TESS_ACCEPTED: %s\n", + flag (R_NOT_TESS_ACCEPTED) ? "T" : "F"); + fprintf (fp, "R_CONTAINS_BLANKS: %s\n", + flag (R_CONTAINS_BLANKS) ? "T" : "F"); + fprintf (fp, "R_BAD_PERMUTER: %s\n", flag (R_BAD_PERMUTER) ? "T" : "F"); + fprintf (fp, "R_HYPHEN: %s\n", flag (R_HYPHEN) ? "T" : "F"); + fprintf (fp, "R_DUBIOUS: %s\n", flag (R_DUBIOUS) ? "T" : "F"); + fprintf (fp, "R_NO_ALPHANUMS: %s\n", flag (R_NO_ALPHANUMS) ? "T" : "F"); + fprintf (fp, "R_MOSTLY_REJ: %s\n", flag (R_MOSTLY_REJ) ? "T" : "F"); + fprintf (fp, "R_XHT_FIXUP: %s\n", flag (R_XHT_FIXUP) ? "T" : "F"); + fprintf (fp, "R_BAD_QUALITY: %s\n", flag (R_BAD_QUALITY) ? "T" : "F"); + fprintf (fp, "R_DOC_REJ: %s\n", flag (R_DOC_REJ) ? "T" : "F"); + fprintf (fp, "R_BLOCK_REJ: %s\n", flag (R_BLOCK_REJ) ? "T" : "F"); + fprintf (fp, "R_ROW_REJ: %s\n", flag (R_ROW_REJ) ? "T" : "F"); + fprintf (fp, "R_UNLV_REJ: %s\n", flag (R_UNLV_REJ) ? "T" : "F"); + fprintf (fp, "R_HYPHEN_ACCEPT: %s\n", flag (R_HYPHEN_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_NN_ACCEPT: %s\n", flag (R_NN_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_MM_ACCEPT: %s\n", flag (R_MM_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_QUALITY_ACCEPT: %s\n", flag (R_QUALITY_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_MINIMAL_REJ_ACCEPT: %s\n", + flag (R_MINIMAL_REJ_ACCEPT) ? "T" : "F"); +} + + +//The REJMAP class has been hacked to use alloc_struct instead of new []. +//This is to reduce memory fragmentation only as it is rather kludgy. +//alloc_struct by-passes the call to the constructor of REJ on each +//array element. Although the constructor is empty, the BITS16 members +//do have a constructor which sets all the flags to 0. The memset +//replaces this functionality. + +REJMAP::REJMAP( //classwise copy + const REJMAP &source) { + REJ *to; + REJ *from = source.ptr; + int i; + + len = source.length (); + + if (len > 0) { + ptr = (REJ *) alloc_struct (len * sizeof (REJ), "REJ"); + to = ptr; + for (i = 0; i < len; i++) { + *to = *from; + to++; + from++; + } + } + else + ptr = NULL; +} + + +REJMAP & REJMAP::operator= ( //assign REJMAP +const REJMAP & source //from this +) { + REJ * + to; + REJ * + from = source.ptr; + int + i; + + initialise (source.len); + to = ptr; + for (i = 0; i < len; i++) { + *to = *from; + to++; + from++; + } + return *this; +} + + +void REJMAP::initialise( //Redefine map + inT16 length) { + if (ptr != NULL) + free_struct (ptr, len * sizeof (REJ), "REJ"); + len = length; + if (len > 0) + ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), + 0, len * sizeof (REJ)); + else + ptr = NULL; +} + + +inT16 REJMAP::accept_count() { //How many accepted? + int i; + inT16 count = 0; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted ()) + count++; + } + return count; +} + + +BOOL8 REJMAP::recoverable_rejects() { //Any non perm rejs? + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].recoverable ()) + return TRUE; + } + return FALSE; +} + + +BOOL8 REJMAP::quality_recoverable_rejects() { //Any potential rejs? + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accept_if_good_quality ()) + return TRUE; + } + return FALSE; +} + + +void REJMAP::remove_pos( //Cut out an element + inT16 pos //element to remove + ) { + REJ *new_ptr; //new, smaller map + int i; + + ASSERT_HOST (pos >= 0); + ASSERT_HOST (pos < len); + ASSERT_HOST (len > 0); + + len--; + if (len > 0) + new_ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), + 0, len * sizeof (REJ)); + else + new_ptr = NULL; + + for (i = 0; i < pos; i++) + new_ptr[i] = ptr[i]; //copy pre pos + + for (; pos < len; pos++) + new_ptr[pos] = ptr[pos + 1]; //copy post pos + + //delete old map + free_struct (ptr, (len + 1) * sizeof (REJ), "REJ"); + ptr = new_ptr; +} + + +void REJMAP::print(FILE *fp) { + int i; + char buff[512]; + + for (i = 0; i < len; i++) { + buff[i] = ptr[i].display_char (); + } + buff[i] = '\0'; + fprintf (fp, "\"%s\"", buff); +} + + +void REJMAP::full_print(FILE *fp) { + int i; + + for (i = 0; i < len; i++) { + ptr[i].full_print (fp); + fprintf (fp, "\n"); + } +} + + +void REJMAP::rej_word_small_xht() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + ptr[i].setrej_small_xht (); + } +} + + +void REJMAP::rej_word_tess_failure() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + ptr[i].setrej_tess_failure (); + } +} + + +void REJMAP::rej_word_not_tess_accepted() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_not_tess_accepted(); + } +} + + +void REJMAP::rej_word_contains_blanks() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_contains_blanks(); + } +} + + +void REJMAP::rej_word_bad_permuter() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_bad_permuter (); + } +} + + +void REJMAP::rej_word_xht_fixup() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_xht_fixup(); + } +} + + +void REJMAP::rej_word_no_alphanums() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_no_alphanums(); + } +} + + +void REJMAP::rej_word_mostly_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_mostly_rej(); + } +} + + +void REJMAP::rej_word_bad_quality() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_bad_quality(); + } +} + + +void REJMAP::rej_word_doc_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_doc_rej(); + } +} + + +void REJMAP::rej_word_block_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_block_rej(); + } +} + + +void REJMAP::rej_word_row_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted()) ptr[i].setrej_row_rej(); + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.h new file mode 100644 index 0000000..009ba58 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/rejctmap.h @@ -0,0 +1,276 @@ +/********************************************************************** + * File: rejctmap.h (Formerly rejmap.h) + * Description: REJ and REJMAP class functions. + * Author: Phil Cheatle + * Created: Thu Jun 9 13:46:38 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + +This module may look unnecessarily verbose, but here's the philosophy... + +ALL processing of the reject map is done in this module. There are lots of +separate calls to set reject/accept flags. These have DELIBERATELY been kept +distinct so that this module can decide what to do. + +Basically, there is a flag for each sort of rejection or acceptance. This +provides a history of what has happened to EACH character. + +Determining whether a character is CURRENTLY rejected depends on implicit +understanding of the SEQUENCE of possible calls. The flags are defined and +grouped in the REJ_FLAGS enum. These groupings are used in determining a +characters CURRENT rejection status. Basically, a character is ACCEPTED if + + none of the permanent rej flags are set + AND ( the character has never been rejected + OR an accept flag is set which is LATER than the latest reject flag ) + +IT IS FUNDAMENTAL THAT ANYONE HACKING THIS CODE UNDERSTANDS THE SIGNIFICANCE +OF THIS IMPLIED TEMPORAL ORDERING OF THE FLAGS!!!! +**********************************************************************/ + +#ifndef REJCTMAP_H +#define REJCTMAP_H + +#ifdef __UNIX__ +#include +#endif +#include "memry.h" +#include "bits16.h" +#include "params.h" + +enum REJ_FLAGS { + /* Reject modes which are NEVER overridden */ + R_TESS_FAILURE, // PERM Tess didn't classify + R_SMALL_XHT, // PERM Xht too small + R_EDGE_CHAR, // PERM Too close to edge of image + R_1IL_CONFLICT, // PERM 1Il confusion + R_POSTNN_1IL, // PERM 1Il unrejected by NN + R_REJ_CBLOB, // PERM Odd blob + R_MM_REJECT, // PERM Matrix match rejection (m's) + R_BAD_REPETITION, // TEMP Repeated char which doesn't match trend + + /* Initial reject modes (pre NN_ACCEPT) */ + R_POOR_MATCH, // TEMP Ray's original heuristic (Not used) + R_NOT_TESS_ACCEPTED, // TEMP Tess didn't accept WERD + R_CONTAINS_BLANKS, // TEMP Tess failed on other chs in WERD + R_BAD_PERMUTER, // POTENTIAL Bad permuter for WERD + + /* Reject modes generated after NN_ACCEPT but before MM_ACCEPT */ + R_HYPHEN, // TEMP Post NN dodgy hyphen or full stop + R_DUBIOUS, // TEMP Post NN dodgy chars + R_NO_ALPHANUMS, // TEMP No alphanumerics in word after NN + R_MOSTLY_REJ, // TEMP Most of word rejected so rej the rest + R_XHT_FIXUP, // TEMP Xht tests unsure + + /* Reject modes generated after MM_ACCEPT but before QUALITY_ACCEPT */ + R_BAD_QUALITY, // TEMP Quality metrics bad for WERD + + /* Reject modes generated after QUALITY_ACCEPT but before MINIMAL_REJ accep*/ + R_DOC_REJ, // TEMP Document rejection + R_BLOCK_REJ, // TEMP Block rejection + R_ROW_REJ, // TEMP Row rejection + R_UNLV_REJ, // TEMP ~ turned to - or ^ turned to space + + /* Accept modes which occur between the above rejection groups */ + R_NN_ACCEPT, // NN acceptance + R_HYPHEN_ACCEPT, // Hyphen acceptance + R_MM_ACCEPT, // Matrix match acceptance + R_QUALITY_ACCEPT, // Accept word in good quality doc + R_MINIMAL_REJ_ACCEPT // Accept EVERYTHING except tess failures +}; + +/* REJECT MAP VALUES */ + +#define MAP_ACCEPT '1' +#define MAP_REJECT_PERM '0' +#define MAP_REJECT_TEMP '2' +#define MAP_REJECT_POTENTIAL '3' + +class REJ +{ + BITS16 flags1; + BITS16 flags2; + + void set_flag(REJ_FLAGS rej_flag) { + if (rej_flag < 16) + flags1.turn_on_bit (rej_flag); + else + flags2.turn_on_bit (rej_flag - 16); + } + + BOOL8 rej_before_nn_accept(); + BOOL8 rej_between_nn_and_mm(); + BOOL8 rej_between_mm_and_quality_accept(); + BOOL8 rej_between_quality_and_minimal_rej_accept(); + BOOL8 rej_before_mm_accept(); + BOOL8 rej_before_quality_accept(); + + public: + REJ() { //constructor + } + + REJ( //classwise copy + const REJ &source) { + flags1 = source.flags1; + flags2 = source.flags2; + } + + REJ & operator= ( //assign REJ + const REJ & source) { //from this + flags1 = source.flags1; + flags2 = source.flags2; + return *this; + } + + BOOL8 flag(REJ_FLAGS rej_flag) { + if (rej_flag < 16) + return flags1.bit (rej_flag); + else + return flags2.bit (rej_flag - 16); + } + + char display_char() { + if (perm_rejected ()) + return MAP_REJECT_PERM; + else if (accept_if_good_quality ()) + return MAP_REJECT_POTENTIAL; + else if (rejected ()) + return MAP_REJECT_TEMP; + else + return MAP_ACCEPT; + } + + BOOL8 perm_rejected(); //Is char perm reject? + + BOOL8 rejected(); //Is char rejected? + + BOOL8 accepted() { //Is char accepted? + return !rejected (); + } + + //potential rej? + BOOL8 accept_if_good_quality(); + + BOOL8 recoverable() { + return (rejected () && !perm_rejected ()); + } + + void setrej_tess_failure(); //Tess generated blank + void setrej_small_xht(); //Small xht char/wd + void setrej_edge_char(); //Close to image edge + void setrej_1Il_conflict(); //Initial reject map + void setrej_postNN_1Il(); //1Il after NN + void setrej_rej_cblob(); //Insert duff blob + void setrej_mm_reject(); //Matrix matcher + //Odd repeated char + void setrej_bad_repetition(); + void setrej_poor_match(); //Failed Rays heuristic + //TEMP reject_word + void setrej_not_tess_accepted(); + //TEMP reject_word + void setrej_contains_blanks(); + void setrej_bad_permuter(); //POTENTIAL reject_word + void setrej_hyphen(); //PostNN dubious hyph or . + void setrej_dubious(); //PostNN dubious limit + void setrej_no_alphanums(); //TEMP reject_word + void setrej_mostly_rej(); //TEMP reject_word + void setrej_xht_fixup(); //xht fixup + void setrej_bad_quality(); //TEMP reject_word + void setrej_doc_rej(); //TEMP reject_word + void setrej_block_rej(); //TEMP reject_word + void setrej_row_rej(); //TEMP reject_word + void setrej_unlv_rej(); //TEMP reject_word + void setrej_nn_accept(); //NN Flipped a char + void setrej_hyphen_accept(); //Good aspect ratio + void setrej_mm_accept(); //Matrix matcher + //Quality flip a char + void setrej_quality_accept(); + //Accept all except blank + void setrej_minimal_rej_accept(); + + void full_print(FILE *fp); +}; + +class REJMAP +{ + REJ *ptr; //ptr to the chars + inT16 len; //Number of chars + + public: + REJMAP() { //constructor + ptr = NULL; + len = 0; + } + + REJMAP( //classwise copy + const REJMAP &rejmap); + + REJMAP & operator= ( //assign REJMAP + const REJMAP & source); //from this + + ~REJMAP () { //destructor + if (ptr != NULL) + free_struct (ptr, len * sizeof (REJ), "REJ"); + } + + void initialise( //Redefine map + inT16 length); + + REJ & operator[]( //access function + inT16 index) const //map index + { + ASSERT_HOST (index < len); + return ptr[index]; //no bounds checks + } + + inT32 length() const { //map length + return len; + } + + inT16 accept_count(); //How many accepted? + + inT16 reject_count() { //How many rejects? + return len - accept_count (); + } + + void remove_pos( //Cut out an element + inT16 pos); //element to remove + + void print(FILE *fp); + + void full_print(FILE *fp); + + BOOL8 recoverable_rejects(); //Any non perm rejs? + + BOOL8 quality_recoverable_rejects(); + //Any potential rejs? + + void rej_word_small_xht(); //Reject whole word + //Reject whole word + void rej_word_tess_failure(); + void rej_word_not_tess_accepted(); + //Reject whole word + //Reject whole word + void rej_word_contains_blanks(); + //Reject whole word + void rej_word_bad_permuter(); + void rej_word_xht_fixup(); //Reject whole word + //Reject whole word + void rej_word_no_alphanums(); + void rej_word_mostly_rej(); //Reject whole word + void rej_word_bad_quality(); //Reject whole word + void rej_word_doc_rej(); //Reject whole word + void rej_word_block_rej(); //Reject whole word + void rej_word_row_rej(); //Reject whole word +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.cpp new file mode 100644 index 0000000..3d70eaf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.cpp @@ -0,0 +1,280 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: seam.c (Formerly seam.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 17 16:30:13 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "seam.h" +#include "blobs.h" +#include "tprintf.h" + +/*---------------------------------------------------------------------- + Public Function Code +----------------------------------------------------------------------*/ + +// Returns the bounding box of all the points in the seam. +TBOX SEAM::bounding_box() const { + TBOX box(location_.x, location_.y, location_.x, location_.y); + for (int s = 0; s < num_splits_; ++s) { + box += splits_[s].bounding_box(); + } + return box; +} + +// Returns true if other can be combined into *this. +bool SEAM::CombineableWith(const SEAM& other, int max_x_dist, + float max_total_priority) const { + int dist = location_.x - other.location_.x; + if (-max_x_dist < dist && dist < max_x_dist && + num_splits_ + other.num_splits_ <= kMaxNumSplits && + priority_ + other.priority_ < max_total_priority && + !OverlappingSplits(other) && !SharesPosition(other)) { + return true; + } else { + return false; + } +} + +// Combines other into *this. Only works if CombinableWith returned true. +void SEAM::CombineWith(const SEAM& other) { + priority_ += other.priority_; + location_ += other.location_; + location_ /= 2; + + for (int s = 0; s < other.num_splits_ && num_splits_ < kMaxNumSplits; ++s) + splits_[num_splits_++] = other.splits_[s]; +} + +// Returns true if the splits in *this SEAM appear OK in the sense that they +// do not cross any outlines and do not chop off any ridiculously small +// pieces. +bool SEAM::IsHealthy(const TBLOB& blob, int min_points, int min_area) const { + // TODO(rays) Try testing all the splits. Duplicating original code for now, + // which tested only the first. + return num_splits_ == 0 || splits_[0].IsHealthy(blob, min_points, min_area); +} + +// Computes the widthp_/widthn_ range for all existing SEAMs and for *this +// seam, which is about to be inserted at insert_index. Returns false if +// any of the computations fails, as this indicates an invalid chop. +// widthn_/widthp_ are only changed if modify is true. +bool SEAM::PrepareToInsertSeam(const GenericVector& seams, + const GenericVector& blobs, + int insert_index, bool modify) { + for (int s = 0; s < insert_index; ++s) { + if (!seams[s]->FindBlobWidth(blobs, s, modify)) return false; + } + if (!FindBlobWidth(blobs, insert_index, modify)) return false; + for (int s = insert_index; s < seams.size(); ++s) { + if (!seams[s]->FindBlobWidth(blobs, s + 1, modify)) return false; + } + return true; +} + +// Computes the widthp_/widthn_ range. Returns false if not all the splits +// are accounted for. widthn_/widthp_ are only changed if modify is true. +bool SEAM::FindBlobWidth(const GenericVector& blobs, int index, + bool modify) { + int num_found = 0; + if (modify) { + widthp_ = 0; + widthn_ = 0; + } + for (int s = 0; s < num_splits_; ++s) { + const SPLIT& split = splits_[s]; + bool found_split = split.ContainedByBlob(*blobs[index]); + // Look right. + for (int b = index + 1; !found_split && b < blobs.size(); ++b) { + found_split = split.ContainedByBlob(*blobs[b]); + if (found_split && b - index > widthp_ && modify) widthp_ = b - index; + } + // Look left. + for (int b = index - 1; !found_split && b >= 0; --b) { + found_split = split.ContainedByBlob(*blobs[b]); + if (found_split && index - b > widthn_ && modify) widthn_ = index - b; + } + if (found_split) ++num_found; + } + return num_found == num_splits_; +} + +// Splits this blob into two blobs by applying the splits included in +// *this SEAM +void SEAM::ApplySeam(bool italic_blob, TBLOB* blob, TBLOB* other_blob) const { + for (int s = 0; s < num_splits_; ++s) { + splits_[s].SplitOutlineList(blob->outlines); + } + blob->ComputeBoundingBoxes(); + + divide_blobs(blob, other_blob, italic_blob, location_); + + blob->EliminateDuplicateOutlines(); + other_blob->EliminateDuplicateOutlines(); + + blob->CorrectBlobOrder(other_blob); +} + +// Undoes ApplySeam by removing the seam between these two blobs. +// Produces one blob as a result, and deletes other_blob. +void SEAM::UndoSeam(TBLOB* blob, TBLOB* other_blob) const { + if (blob->outlines == NULL) { + blob->outlines = other_blob->outlines; + other_blob->outlines = NULL; + } + + TESSLINE* outline = blob->outlines; + while (outline->next) outline = outline->next; + outline->next = other_blob->outlines; + other_blob->outlines = NULL; + delete other_blob; + + for (int s = 0; s < num_splits_; ++s) { + splits_[s].UnsplitOutlineList(blob); + } + blob->ComputeBoundingBoxes(); + blob->EliminateDuplicateOutlines(); +} + +// Prints everything in *this SEAM. +void SEAM::Print(const char* label) const { + tprintf(label); + tprintf(" %6.2f @ (%d,%d), p=%d, n=%d ", priority_, location_.x, location_.y, + widthp_, widthn_); + for (int s = 0; s < num_splits_; ++s) { + splits_[s].Print(); + if (s + 1 < num_splits_) tprintf(", "); + } + tprintf("\n"); +} + +// Prints a collection of SEAMs. +/* static */ +void SEAM::PrintSeams(const char* label, const GenericVector& seams) { + if (!seams.empty()) { + tprintf("%s\n", label); + for (int x = 0; x < seams.size(); ++x) { + tprintf("%2d: ", x); + seams[x]->Print(""); + } + tprintf("\n"); + } +} + +#ifndef GRAPHICS_DISABLED +// Draws the seam in the given window. +void SEAM::Mark(ScrollView* window) const { + for (int s = 0; s < num_splits_; ++s) splits_[s].Mark(window); +} +#endif + +// Break up the blobs in this chain so that they are all independent. +// This operation should undo the affect of join_pieces. +/* static */ +void SEAM::BreakPieces(const GenericVector& seams, + const GenericVector& blobs, int first, + int last) { + for (int x = first; x < last; ++x) seams[x]->Reveal(); + + TESSLINE* outline = blobs[first]->outlines; + int next_blob = first + 1; + + while (outline != NULL && next_blob <= last) { + if (outline->next == blobs[next_blob]->outlines) { + outline->next = NULL; + outline = blobs[next_blob]->outlines; + ++next_blob; + } else { + outline = outline->next; + } + } +} + +// Join a group of base level pieces into a single blob that can then +// be classified. +/* static */ +void SEAM::JoinPieces(const GenericVector& seams, + const GenericVector& blobs, int first, int last) { + TESSLINE* outline = blobs[first]->outlines; + if (!outline) + return; + + for (int x = first; x < last; ++x) { + SEAM *seam = seams[x]; + if (x - seam->widthn_ >= first && x + seam->widthp_ < last) seam->Hide(); + while (outline->next) outline = outline->next; + outline->next = blobs[x + 1]->outlines; + } +} + +// Hides the seam so the outlines appear not to be cut by it. +void SEAM::Hide() const { + for (int s = 0; s < num_splits_; ++s) { + splits_[s].Hide(); + } +} + +// Undoes hide, so the outlines are cut by the seam. +void SEAM::Reveal() const { + for (int s = 0; s < num_splits_; ++s) { + splits_[s].Reveal(); + } +} + +// Computes and returns, but does not set, the full priority of *this SEAM. +float SEAM::FullPriority(int xmin, int xmax, double overlap_knob, + int centered_maxwidth, double center_knob, + double width_change_knob) const { + if (num_splits_ == 0) return 0.0f; + for (int s = 1; s < num_splits_; ++s) { + splits_[s].SplitOutline(); + } + float full_priority = + priority_ + + splits_[0].FullPriority(xmin, xmax, overlap_knob, centered_maxwidth, + center_knob, width_change_knob); + for (int s = num_splits_ - 1; s >= 1; --s) { + splits_[s].UnsplitOutlines(); + } + return full_priority; +} + +/** + * @name start_seam_list + * + * Initialize a list of seams that match the original number of blobs + * present in the starting segmentation. Each of the seams created + * by this routine have location information only. + */ +void start_seam_list(TWERD* word, GenericVector* seam_array) { + seam_array->truncate(0); + TPOINT location; + + for (int b = 1; b < word->NumBlobs(); ++b) { + TBOX bbox = word->blobs[b - 1]->bounding_box(); + TBOX nbox = word->blobs[b]->bounding_box(); + location.x = (bbox.right() + nbox.left()) / 2; + location.y = (bbox.bottom() + bbox.top() + nbox.bottom() + nbox.top()) / 4; + seam_array->push_back(new SEAM(0.0f, location)); + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.h new file mode 100644 index 0000000..4bafbcb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/seam.h @@ -0,0 +1,203 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: seam.h (Formerly seam.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Thu May 16 17:05:52 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef SEAM_H +#define SEAM_H + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "blobs.h" +#include "split.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef float PRIORITY; /* PRIORITY */ + +class SEAM { + public: + // A seam with no splits + SEAM(float priority, const TPOINT& location) + : priority_(priority), + location_(location), + widthp_(0), + widthn_(0), + num_splits_(0) {} + // A seam with a single split point. + SEAM(float priority, const TPOINT& location, const SPLIT& split) + : priority_(priority), + location_(location), + widthp_(0), + widthn_(0), + num_splits_(1) { + splits_[0] = split; + } + // Default copy constructor, operator= and destructor are OK! + + // Accessors. + float priority() const { return priority_; } + void set_priority(float priority) { priority_ = priority; } + bool HasAnySplits() const { return num_splits_ > 0; } + + // Returns the bounding box of all the points in the seam. + TBOX bounding_box() const; + + // Returns true if other can be combined into *this. + bool CombineableWith(const SEAM& other, int max_x_dist, + float max_total_priority) const; + // Combines other into *this. Only works if CombinableWith returned true. + void CombineWith(const SEAM& other); + + // Returns true if the given blob contains all splits of *this SEAM. + bool ContainedByBlob(const TBLOB& blob) const { + for (int s = 0; s < num_splits_; ++s) { + if (!splits_[s].ContainedByBlob(blob)) return false; + } + return true; + } + + // Returns true if the given EDGEPT is used by this SEAM, checking only + // the EDGEPT pointer, not the coordinates. + bool UsesPoint(const EDGEPT* point) const { + for (int s = 0; s < num_splits_; ++s) { + if (splits_[s].UsesPoint(point)) return true; + } + return false; + } + // Returns true if *this and other share any common point, by coordinates. + bool SharesPosition(const SEAM& other) const { + for (int s = 0; s < num_splits_; ++s) { + for (int t = 0; t < other.num_splits_; ++t) + if (splits_[s].SharesPosition(other.splits_[t])) return true; + } + return false; + } + // Returns true if *this and other have any vertically overlapping splits. + bool OverlappingSplits(const SEAM& other) const { + for (int s = 0; s < num_splits_; ++s) { + TBOX split1_box = splits_[s].bounding_box(); + for (int t = 0; t < other.num_splits_; ++t) { + TBOX split2_box = other.splits_[t].bounding_box(); + if (split1_box.y_overlap(split2_box)) return true; + } + } + return false; + } + + // Marks the edgepts used by the seam so the segments made by the cut + // never get split further by another seam in the future. + void Finalize() { + for (int s = 0; s < num_splits_; ++s) { + splits_[s].point1->MarkChop(); + splits_[s].point2->MarkChop(); + } + } + + // Returns true if the splits in *this SEAM appear OK in the sense that they + // do not cross any outlines and do not chop off any ridiculously small + // pieces. + bool IsHealthy(const TBLOB& blob, int min_points, int min_area) const; + + // Computes the widthp_/widthn_ range for all existing SEAMs and for *this + // seam, which is about to be inserted at insert_index. Returns false if + // any of the computations fails, as this indicates an invalid chop. + // widthn_/widthp_ are only changed if modify is true. + bool PrepareToInsertSeam(const GenericVector& seams, + const GenericVector& blobs, int insert_index, + bool modify); + // Computes the widthp_/widthn_ range. Returns false if not all the splits + // are accounted for. widthn_/widthp_ are only changed if modify is true. + bool FindBlobWidth(const GenericVector& blobs, int index, + bool modify); + + // Splits this blob into two blobs by applying the splits included in + // *this SEAM + void ApplySeam(bool italic_blob, TBLOB* blob, TBLOB* other_blob) const; + // Undoes ApplySeam by removing the seam between these two blobs. + // Produces one blob as a result, and deletes other_blob. + void UndoSeam(TBLOB* blob, TBLOB* other_blob) const; + + // Prints everything in *this SEAM. + void Print(const char* label) const; + // Prints a collection of SEAMs. + static void PrintSeams(const char* label, const GenericVector& seams); +#ifndef GRAPHICS_DISABLED + // Draws the seam in the given window. + void Mark(ScrollView* window) const; +#endif + + // Break up the blobs in this chain so that they are all independent. + // This operation should undo the affect of join_pieces. + static void BreakPieces(const GenericVector& seams, + const GenericVector& blobs, int first, + int last); + // Join a group of base level pieces into a single blob that can then + // be classified. + static void JoinPieces(const GenericVector& seams, + const GenericVector& blobs, int first, + int last); + + // Hides the seam so the outlines appear not to be cut by it. + void Hide() const; + // Undoes hide, so the outlines are cut by the seam. + void Reveal() const; + + // Computes and returns, but does not set, the full priority of *this SEAM. + // The arguments here are config parameters defined in Wordrec. Add chop_ + // to the beginning of the name. + float FullPriority(int xmin, int xmax, double overlap_knob, + int centered_maxwidth, double center_knob, + double width_change_knob) const; + + private: + // Maximum number of splits that a SEAM can hold. + static const int kMaxNumSplits = 3; + // Priority of this split. Lower is better. + float priority_; + // Position of the middle of the seam. + TPOINT location_; + // A range such that all splits in *this SEAM are contained within blobs in + // the range [index - widthn_,index + widthp_] where index is the index of + // this SEAM in the seams vector. + inT8 widthp_; + inT8 widthn_; + // Number of splits_ that are used. + inT8 num_splits_; + // Set of pairs of points that are the ends of each split in the SEAM. + SPLIT splits_[kMaxNumSplits]; +}; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +void start_seam_list(TWERD* word, GenericVector* seam_array); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/split.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/split.cpp new file mode 100644 index 0000000..fbbc71f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/split.cpp @@ -0,0 +1,330 @@ +/* -*-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 automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "split.h" +#include "coutln.h" +#include "tprintf.h" + +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +// Limit on the amount of penalty for the chop being off-center. +const int kCenterGradeCap = 25; +// Ridiculously large priority for splits that are no use. +const double kBadPriority = 999.0; + +BOOL_VAR(wordrec_display_splits, 0, "Display splits"); + +// Returns the bounding box of all the points in the split. +TBOX SPLIT::bounding_box() const { + return TBOX( + MIN(point1->pos.x, point2->pos.x), MIN(point1->pos.y, point2->pos.y), + MAX(point1->pos.x, point2->pos.x), MAX(point1->pos.y, point2->pos.y)); +} + +// Hides the SPLIT so the outlines appear not to be cut by it. +void SPLIT::Hide() const { + EDGEPT* edgept = point1; + do { + edgept->Hide(); + edgept = edgept->next; + } while (!edgept->EqualPos(*point2) && edgept != point1); + edgept = point2; + do { + edgept->Hide(); + edgept = edgept->next; + } while (!edgept->EqualPos(*point1) && edgept != point2); +} + +// Undoes hide, so the outlines are cut by the SPLIT. +void SPLIT::Reveal() const { + EDGEPT* edgept = point1; + do { + edgept->Reveal(); + edgept = edgept->next; + } while (!edgept->EqualPos(*point2) && edgept != point1); + edgept = point2; + do { + edgept->Reveal(); + edgept = edgept->next; + } while (!edgept->EqualPos(*point1) && edgept != point2); +} + +// Compute a split priority based on the bounding boxes of the parts. +// The arguments here are config parameters defined in Wordrec. Add chop_ +// to the beginning of the name. +float SPLIT::FullPriority(int xmin, int xmax, double overlap_knob, + int centered_maxwidth, double center_knob, + double width_change_knob) const { + TBOX box1 = Box12(); + TBOX box2 = Box21(); + int min_left = MIN(box1.left(), box2.left()); + int max_right = MAX(box1.right(), box2.right()); + if (xmin < min_left && xmax > max_right) return kBadPriority; + + float grade = 0.0f; + // grade_overlap. + int width1 = box1.width(); + int width2 = box2.width(); + int min_width = MIN(width1, width2); + int overlap = -box1.x_gap(box2); + if (overlap == min_width) { + grade += 100.0f; // Total overlap. + } else { + if (2 * overlap > min_width) overlap += 2 * overlap - min_width; + if (overlap > 0) grade += overlap_knob * overlap; + } + // grade_center_of_blob. + if (width1 <= centered_maxwidth || width2 <= centered_maxwidth) { + grade += MIN(kCenterGradeCap, center_knob * abs(width1 - width2)); + } + // grade_width_change. + float width_change_grade = 20 - (max_right - min_left - MAX(width1, width2)); + if (width_change_grade > 0.0f) + grade += width_change_grade * width_change_knob; + return grade; +} + +// Returns true if *this SPLIT appears OK in the sense that it does not cross +// any outlines and does not chop off any ridiculously small pieces. +bool SPLIT::IsHealthy(const TBLOB& blob, int min_points, int min_area) const { + return !IsLittleChunk(min_points, min_area) && + !blob.SegmentCrossesOutline(point1->pos, point2->pos); +} + +// Returns true if the split generates a small chunk in terms of either area +// or number of points. +bool SPLIT::IsLittleChunk(int min_points, int min_area) const { + if (point1->ShortNonCircularSegment(min_points, point2) && + point1->SegmentArea(point2) < min_area) { + return true; + } + if (point2->ShortNonCircularSegment(min_points, point1) && + point2->SegmentArea(point1) < min_area) { + return true; + } + return false; +} + +/********************************************************************** + * 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; + // Now deal with the src_outline steps. + C_OUTLINE* prev_ol = prev->src_outline; + if (prev_ol != NULL && prev->next == next) { + // Compute the fraction of the segment that is being cut. + FCOORD segment_vec(next->pos.x - prev->pos.x, next->pos.y - prev->pos.y); + FCOORD target_vec(x - prev->pos.x, y - prev->pos.y); + double cut_fraction = target_vec.length() / segment_vec.length(); + // Get the start and end at the step level. + ICOORD step_start = prev_ol->position_at_index(prev->start_step); + int end_step = prev->start_step + prev->step_count; + int step_length = prev_ol->pathlength(); + ICOORD step_end = prev_ol->position_at_index(end_step % step_length); + ICOORD step_vec = step_end - step_start; + double target_length = step_vec.length() * cut_fraction; + // Find the point on the segment that gives the length nearest to target. + int best_step = prev->start_step; + ICOORD total_step(0, 0); + double best_dist = target_length; + for (int s = prev->start_step; s < end_step; ++s) { + total_step += prev_ol->step(s % step_length); + double dist = fabs(target_length - total_step.length()); + if (dist < best_dist) { + best_dist = dist; + best_step = s + 1; + } + } + // The new point is an intermediate point. + this_edgept->src_outline = prev_ol; + this_edgept->step_count = end_step - best_step; + this_edgept->start_step = best_step % step_length; + prev->step_count = best_step - prev->start_step; + } else { + // The new point is poly only. + this_edgept->src_outline = NULL; + this_edgept->step_count = 0; + this_edgept->start_step = 0; + } + /* 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; + // Add point's steps onto prev's steps if they are from the same outline. + if (prev->src_outline == point->src_outline && prev->src_outline != NULL) { + prev->step_count += point->step_count; + } + 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; +} + +/********************************************************************** + * Print + * + * Shows the coordinates of both points in a split. + **********************************************************************/ +void SPLIT::Print() const { + tprintf("(%d,%d)--(%d,%d)", point1->pos.x, point1->pos.y, point2->pos.x, + point2->pos.y); +} + +#ifndef GRAPHICS_DISABLED +// Draws the split in the given window. +void SPLIT::Mark(ScrollView* window) const { + window->Pen(ScrollView::GREEN); + window->Line(point1->pos.x, point1->pos.y, point2->pos.x, point2->pos.y); + window->UpdateWindow(); +} +#endif + +// Creates two outlines out of one by splitting the original one in half. +// Inserts the resulting outlines into the given list. +void SPLIT::SplitOutlineList(TESSLINE* outlines) const { + SplitOutline(); + while (outlines->next != NULL) outlines = outlines->next; + + outlines->next = new TESSLINE; + outlines->next->loop = point1; + outlines->next->ComputeBoundingBox(); + + outlines = outlines->next; + + outlines->next = new TESSLINE; + outlines->next->loop = point2; + outlines->next->ComputeBoundingBox(); + + outlines->next->next = NULL; +} + +// Makes a split between these two edge points, but does not affect the +// outlines to which they belong. +void SPLIT::SplitOutline() const { + EDGEPT* temp2 = point2->next; + EDGEPT* temp1 = point1->next; + /* Create two new points */ + EDGEPT* new_point1 = make_edgept(point1->pos.x, point1->pos.y, temp1, point2); + EDGEPT* new_point2 = make_edgept(point2->pos.x, point2->pos.y, temp2, point1); + // point1 and 2 are now cross-over points, so they must have NULL + // src_outlines and give their src_outline information their new + // replacements. + new_point1->src_outline = point1->src_outline; + new_point1->start_step = point1->start_step; + new_point1->step_count = point1->step_count; + new_point2->src_outline = point2->src_outline; + new_point2->start_step = point2->start_step; + new_point2->step_count = point2->step_count; + point1->src_outline = NULL; + point1->start_step = 0; + point1->step_count = 0; + point2->src_outline = NULL; + point2->start_step = 0; + point2->step_count = 0; +} + +// Undoes the effect of SplitOutlineList, correcting the outlines for undoing +// the split, but possibly leaving some duplicate outlines. +void SPLIT::UnsplitOutlineList(TBLOB* blob) const { + /* Modify edge points */ + UnsplitOutlines(); + + TESSLINE* outline1 = new TESSLINE; + outline1->next = blob->outlines; + blob->outlines = outline1; + outline1->loop = point1; + + TESSLINE* outline2 = new TESSLINE; + outline2->next = blob->outlines; + blob->outlines = outline2; + outline2->loop = point2; +} + +// Removes the split that was put between these two points. +void SPLIT::UnsplitOutlines() const { + EDGEPT* tmp1 = point1->next; + EDGEPT* tmp2 = point2->next; + + tmp1->next->prev = point2; + tmp2->next->prev = point1; + + // tmp2 is coincident with point1. point1 takes tmp2's place as tmp2 is + // deleted. + point1->next = tmp2->next; + point1->src_outline = tmp2->src_outline; + point1->start_step = tmp2->start_step; + point1->step_count = tmp2->step_count; + // Likewise point2 takes tmp1's place. + point2->next = tmp1->next; + point2->src_outline = tmp1->src_outline; + point2->start_step = tmp1->start_step; + point2->step_count = tmp1->step_count; + + delete tmp1; + delete tmp2; + + point1->vec.x = point1->next->pos.x - point1->pos.x; + point1->vec.y = point1->next->pos.y - point1->pos.y; + + point2->vec.x = point2->next->pos.x - point2->pos.x; + point2->vec.y = point2->next->pos.y - point2->pos.y; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/split.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/split.h new file mode 100644 index 0000000..2642474 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/split.h @@ -0,0 +1,120 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: split.h (Formerly split.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 13 10:49:23 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *****************************************************************************/ +#ifndef SPLIT_H +#define SPLIT_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "blobs.h" +#include "scrollview.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +struct SPLIT { + SPLIT() : point1(NULL), point2(NULL) {} + SPLIT(EDGEPT* pt1, EDGEPT* pt2) : point1(pt1), point2(pt2) {} + + // Returns the bounding box of all the points in the split. + TBOX bounding_box() const; + // Returns the bounding box of the outline from point1 to point2. + TBOX Box12() const { return point1->SegmentBox(point2); } + // Returns the bounding box of the outline from point1 to point1. + TBOX Box21() const { return point2->SegmentBox(point1); } + // Returns the bounding box of the out + + // Hides the SPLIT so the outlines appear not to be cut by it. + void Hide() const; + // Undoes hide, so the outlines are cut by the SPLIT. + void Reveal() const; + + // Returns true if the given EDGEPT is used by this SPLIT, checking only + // the EDGEPT pointer, not the coordinates. + bool UsesPoint(const EDGEPT* point) const { + return point1 == point || point2 == point; + } + // Returns true if the other SPLIT has any position shared with *this. + bool SharesPosition(const SPLIT& other) const { + return point1->EqualPos(*other.point1) || point1->EqualPos(*other.point2) || + point2->EqualPos(*other.point1) || point2->EqualPos(*other.point2); + } + // Returns true if both points are contained within the blob. + bool ContainedByBlob(const TBLOB& blob) const { + return blob.Contains(point1->pos) && blob.Contains(point2->pos); + } + // Returns true if both points are contained within the outline. + bool ContainedByOutline(const TESSLINE& outline) const { + return outline.Contains(point1->pos) && outline.Contains(point2->pos); + } + // Compute a split priority based on the bounding boxes of the parts. + // The arguments here are config parameters defined in Wordrec. Add chop_ + // to the beginning of the name. + float FullPriority(int xmin, int xmax, double overlap_knob, + int centered_maxwidth, double center_knob, + double width_change_knob) const; + // Returns true if *this SPLIT appears OK in the sense that it does not cross + // any outlines and does not chop off any ridiculously small pieces. + bool IsHealthy(const TBLOB& blob, int min_points, int min_area) const; + // Returns true if the split generates a small chunk in terms of either area + // or number of points. + bool IsLittleChunk(int min_points, int min_area) const; + + void Print() const; +#ifndef GRAPHICS_DISABLED + // Draws the split in the given window. + void Mark(ScrollView* window) const; +#endif + + // Creates two outlines out of one by splitting the original one in half. + // Inserts the resulting outlines into the given list. + void SplitOutlineList(TESSLINE* outlines) const; + // Makes a split between these two edge points, but does not affect the + // outlines to which they belong. + void SplitOutline() const; + // Undoes the effect of SplitOutlineList, correcting the outlines for undoing + // the split, but possibly leaving some duplicate outlines. + void UnsplitOutlineList(TBLOB* blob) const; + // Removes the split that was put between these two points. + void UnsplitOutlines() const; + + EDGEPT *point1; + EDGEPT *point2; +}; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ + +extern BOOL_VAR_H(wordrec_display_splits, 0, "Display splits"); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev); + +void remove_edgept(EDGEPT *point); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.cpp new file mode 100644 index 0000000..a28217f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.cpp @@ -0,0 +1,782 @@ +/********************************************************************** + * File: statistc.c (Formerly stats.c) + * Description: Simple statistical package for integer values. + * Author: Ray Smith + * Created: Mon Feb 04 16:56:05 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "statistc.h" +#include +#include +#include +#include "helpers.h" +#include "scrollview.h" +#include "tprintf.h" + +using tesseract::KDPairInc; + +/********************************************************************** + * STATS::STATS + * + * Construct a new stats element by allocating and zeroing the memory. + **********************************************************************/ +STATS::STATS(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) { + if (max_bucket_value_plus_1 <= min_bucket_value) { + min_bucket_value = 0; + max_bucket_value_plus_1 = 1; + } + rangemin_ = min_bucket_value; // setup + rangemax_ = max_bucket_value_plus_1; + buckets_ = new inT32[rangemax_ - rangemin_]; + clear(); +} + +STATS::STATS() { + rangemax_ = 0; + rangemin_ = 0; + buckets_ = NULL; +} + +/********************************************************************** + * STATS::set_range + * + * Alter the range on an existing stats element. + **********************************************************************/ +bool STATS::set_range(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) { + if (max_bucket_value_plus_1 <= min_bucket_value) { + return false; + } + if (rangemax_ - rangemin_ != max_bucket_value_plus_1 - min_bucket_value) { + delete [] buckets_; + buckets_ = new inT32[max_bucket_value_plus_1 - min_bucket_value]; + } + rangemin_ = min_bucket_value; // setup + rangemax_ = max_bucket_value_plus_1; + clear(); // zero it + return true; +} + +/********************************************************************** + * STATS::clear + * + * Clear out the STATS class by zeroing all the buckets. + **********************************************************************/ +void STATS::clear() { // clear out buckets + total_count_ = 0; + if (buckets_ != NULL) + memset(buckets_, 0, (rangemax_ - rangemin_) * sizeof(buckets_[0])); +} + +/********************************************************************** + * STATS::~STATS + * + * Destructor for a stats class. + **********************************************************************/ +STATS::~STATS () { + delete [] buckets_; +} + +/********************************************************************** + * STATS::add + * + * Add a set of samples to (or delete from) a pile. + **********************************************************************/ +void STATS::add(inT32 value, inT32 count) { + if (buckets_ == NULL) { + return; + } + value = ClipToRange(value, rangemin_, rangemax_ - 1); + buckets_[value - rangemin_] += count; + total_count_ += count; // keep count of total +} + +/********************************************************************** + * STATS::mode + * + * Find the mode of a stats class. + **********************************************************************/ +inT32 STATS::mode() const { // get mode of samples + if (buckets_ == NULL) { + return rangemin_; + } + inT32 max = buckets_[0]; // max cell count + inT32 maxindex = 0; // index of max + for (int index = rangemax_ - rangemin_ - 1; index > 0; --index) { + if (buckets_[index] > max) { + max = buckets_[index]; // find biggest + maxindex = index; + } + } + return maxindex + rangemin_; // index of biggest +} + +/********************************************************************** + * STATS::mean + * + * Find the mean of a stats class. + **********************************************************************/ +double STATS::mean() const { //get mean of samples + if (buckets_ == NULL || total_count_ <= 0) { + return static_cast(rangemin_); + } + inT64 sum = 0; + for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) { + sum += static_cast(index) * buckets_[index]; + } + return static_cast(sum) / total_count_ + rangemin_; +} + +/********************************************************************** + * STATS::sd + * + * Find the standard deviation of a stats class. + **********************************************************************/ +double STATS::sd() const { //standard deviation + if (buckets_ == NULL || total_count_ <= 0) { + return 0.0; + } + inT64 sum = 0; + double sqsum = 0.0; + for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) { + sum += static_cast(index) * buckets_[index]; + sqsum += static_cast(index) * index * buckets_[index]; + } + double variance = static_cast(sum) / total_count_; + variance = sqsum / total_count_ - variance * variance; + if (variance > 0.0) + return sqrt(variance); + return 0.0; +} + +/********************************************************************** + * STATS::ile + * + * Returns the fractile value such that frac fraction (in [0,1]) of samples + * has a value less than the return value. + **********************************************************************/ +double STATS::ile(double frac) const { + if (buckets_ == NULL || total_count_ == 0) { + return static_cast(rangemin_); + } +#if 0 + // TODO(rays) The existing code doesn't seem to be doing the right thing + // with target a double but this substitute crashes the code that uses it. + // Investigate and fix properly. + int target = IntCastRounded(frac * total_count_); + target = ClipToRange(target, 1, total_count_); +#else + double target = frac * total_count_; + target = ClipToRange(target, 1.0, static_cast(total_count_)); +#endif + int sum = 0; + int index = 0; + for (index = 0; index < rangemax_ - rangemin_ && sum < target; + sum += buckets_[index++]); + if (index > 0) { + ASSERT_HOST(buckets_[index - 1] > 0); + return rangemin_ + index - + static_cast(sum - target) / buckets_[index - 1]; + } else { + return static_cast(rangemin_); + } +} + +/********************************************************************** + * STATS::min_bucket + * + * Find REAL minimum bucket - ile(0.0) isn't necessarily correct + **********************************************************************/ +inT32 STATS::min_bucket() const { // Find min + if (buckets_ == NULL || total_count_ == 0) { + return rangemin_; + } + inT32 min = 0; + for (min = 0; (min < rangemax_ - rangemin_) && (buckets_[min] == 0); min++); + return rangemin_ + min; +} + +/********************************************************************** + * STATS::max_bucket + * + * Find REAL maximum bucket - ile(1.0) isn't necessarily correct + **********************************************************************/ + +inT32 STATS::max_bucket() const { // Find max + if (buckets_ == NULL || total_count_ == 0) { + return rangemin_; + } + inT32 max; + for (max = rangemax_ - rangemin_ - 1; max > 0 && buckets_[max] == 0; max--); + return rangemin_ + max; +} + +/********************************************************************** + * STATS::median + * + * Finds a more useful estimate of median than ile(0.5). + * + * Overcomes a problem with ile() - if the samples are, for example, + * 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway + * between 6 and 13 = 9.5 + **********************************************************************/ +double STATS::median() const { //get median + if (buckets_ == NULL) { + return static_cast(rangemin_); + } + double median = ile(0.5); + int median_pile = static_cast(floor(median)); + if ((total_count_ > 1) && (pile_count(median_pile) == 0)) { + inT32 min_pile; + inT32 max_pile; + /* Find preceding non zero pile */ + for (min_pile = median_pile; pile_count(min_pile) == 0; min_pile--); + /* Find following non zero pile */ + for (max_pile = median_pile; pile_count(max_pile) == 0; max_pile++); + median = (min_pile + max_pile) / 2.0; + } + return median; +} + +/********************************************************************** + * STATS::local_min + * + * Return TRUE if this point is a local min. + **********************************************************************/ +bool STATS::local_min(inT32 x) const { + if (buckets_ == NULL) { + return false; + } + x = ClipToRange(x, rangemin_, rangemax_ - 1) - rangemin_; + if (buckets_[x] == 0) + return true; + inT32 index; // table index + for (index = x - 1; index >= 0 && buckets_[index] == buckets_[x]; --index); + if (index >= 0 && buckets_[index] < buckets_[x]) + return false; + for (index = x + 1; index < rangemax_ - rangemin_ && + buckets_[index] == buckets_[x]; ++index); + if (index < rangemax_ - rangemin_ && buckets_[index] < buckets_[x]) + return false; + else + return true; +} + +/********************************************************************** + * STATS::smooth + * + * Apply a triangular smoothing filter to the stats. + * This makes the modes a bit more useful. + * The factor gives the height of the triangle, i.e. the weight of the + * centre. + **********************************************************************/ +void STATS::smooth(inT32 factor) { + if (buckets_ == NULL || factor < 2) { + return; + } + STATS result(rangemin_, rangemax_); + int entrycount = rangemax_ - rangemin_; + for (int entry = 0; entry < entrycount; entry++) { + //centre weight + int count = buckets_[entry] * factor; + for (int offset = 1; offset < factor; offset++) { + if (entry - offset >= 0) + count += buckets_[entry - offset] * (factor - offset); + if (entry + offset < entrycount) + count += buckets_[entry + offset] * (factor - offset); + } + result.add(entry + rangemin_, count); + } + total_count_ = result.total_count_; + memcpy(buckets_, result.buckets_, entrycount * sizeof(buckets_[0])); +} + +/********************************************************************** + * STATS::cluster + * + * Cluster the samples into max_cluster clusters. + * Each call runs one iteration. The array of clusters must be + * max_clusters+1 in size as cluster 0 is used to indicate which samples + * have been used. + * The return value is the current number of clusters. + **********************************************************************/ + +inT32 STATS::cluster(float lower, // thresholds + float upper, + float multiple, // distance threshold + inT32 max_clusters, // max no to make + STATS *clusters) { // array of clusters + BOOL8 new_cluster; // added one + float *centres; // cluster centres + inT32 entry; // bucket index + inT32 cluster; // cluster index + inT32 best_cluster; // one to assign to + inT32 new_centre = 0; // residual mode + inT32 new_mode; // pile count of new_centre + inT32 count; // pile to place + float dist; // from cluster + float min_dist; // from best_cluster + inT32 cluster_count; // no of clusters + + if (buckets_ == NULL || max_clusters < 1) + return 0; + centres = new float[max_clusters + 1]; + for (cluster_count = 1; cluster_count <= max_clusters + && clusters[cluster_count].buckets_ != NULL + && clusters[cluster_count].total_count_ > 0; + cluster_count++) { + centres[cluster_count] = + static_cast(clusters[cluster_count].ile(0.5)); + new_centre = clusters[cluster_count].mode(); + for (entry = new_centre - 1; centres[cluster_count] - entry < lower + && entry >= rangemin_ + && pile_count(entry) <= pile_count(entry + 1); + entry--) { + count = pile_count(entry) - clusters[0].pile_count(entry); + if (count > 0) { + clusters[cluster_count].add(entry, count); + clusters[0].add (entry, count); + } + } + for (entry = new_centre + 1; entry - centres[cluster_count] < lower + && entry < rangemax_ + && pile_count(entry) <= pile_count(entry - 1); + entry++) { + count = pile_count(entry) - clusters[0].pile_count(entry); + if (count > 0) { + clusters[cluster_count].add(entry, count); + clusters[0].add(entry, count); + } + } + } + cluster_count--; + + if (cluster_count == 0) { + clusters[0].set_range(rangemin_, rangemax_); + } + do { + new_cluster = FALSE; + new_mode = 0; + for (entry = 0; entry < rangemax_ - rangemin_; entry++) { + count = buckets_[entry] - clusters[0].buckets_[entry]; + //remaining pile + if (count > 0) { //any to handle + min_dist = static_cast(MAX_INT32); + best_cluster = 0; + for (cluster = 1; cluster <= cluster_count; cluster++) { + dist = entry + rangemin_ - centres[cluster]; + //find distance + if (dist < 0) + dist = -dist; + if (dist < min_dist) { + min_dist = dist; //find least + best_cluster = cluster; + } + } + if (min_dist > upper //far enough for new + && (best_cluster == 0 + || entry + rangemin_ > centres[best_cluster] * multiple + || entry + rangemin_ < centres[best_cluster] / multiple)) { + if (count > new_mode) { + new_mode = count; + new_centre = entry + rangemin_; + } + } + } + } + // need new and room + if (new_mode > 0 && cluster_count < max_clusters) { + cluster_count++; + new_cluster = TRUE; + if (!clusters[cluster_count].set_range(rangemin_, rangemax_)) { + delete [] centres; + return 0; + } + centres[cluster_count] = static_cast(new_centre); + clusters[cluster_count].add(new_centre, new_mode); + clusters[0].add(new_centre, new_mode); + for (entry = new_centre - 1; centres[cluster_count] - entry < lower + && entry >= rangemin_ + && pile_count (entry) <= pile_count(entry + 1); entry--) { + count = pile_count(entry) - clusters[0].pile_count(entry); + if (count > 0) { + clusters[cluster_count].add(entry, count); + clusters[0].add(entry, count); + } + } + for (entry = new_centre + 1; entry - centres[cluster_count] < lower + && entry < rangemax_ + && pile_count (entry) <= pile_count(entry - 1); entry++) { + count = pile_count(entry) - clusters[0].pile_count(entry); + if (count > 0) { + clusters[cluster_count].add(entry, count); + clusters[0].add (entry, count); + } + } + centres[cluster_count] = + static_cast(clusters[cluster_count].ile(0.5)); + } + } while (new_cluster && cluster_count < max_clusters); + delete [] centres; + return cluster_count; +} + +// Helper tests that the current index is still part of the peak and gathers +// the data into the peak, returning false when the peak is ended. +// src_buckets[index] - used_buckets[index] is the unused part of the histogram. +// prev_count is the histogram count of the previous index on entry and is +// updated to the current index on return. +// total_count and total_value are accumulating the mean of the peak. +static bool GatherPeak(int index, const int* src_buckets, int* used_buckets, + int* prev_count, int* total_count, double* total_value) { + int pile_count = src_buckets[index] - used_buckets[index]; + if (pile_count <= *prev_count && pile_count > 0) { + // Accumulate count and index.count product. + *total_count += pile_count; + *total_value += index * pile_count; + // Mark this index as used + used_buckets[index] = src_buckets[index]; + *prev_count = pile_count; + return true; + } else { + return false; + } +} + +// Finds (at most) the top max_modes modes, well actually the whole peak around +// each mode, returning them in the given modes vector as a pair in order of decreasing total count. +// Since the mean is the key and the count the data in the pair, a single call +// to sort on the output will re-sort by increasing mean of peak if that is +// more useful than decreasing total count. +// Returns the actual number of modes found. +int STATS::top_n_modes(int max_modes, + GenericVector >* modes) const { + if (max_modes <= 0) return 0; + int src_count = rangemax_ - rangemin_; + // Used copies the counts in buckets_ as they get used. + STATS used(rangemin_, rangemax_); + modes->truncate(0); + // Total count of the smallest peak found so far. + int least_count = 1; + // Mode that is used as a seed for each peak + int max_count = 0; + do { + // Find an unused mode. + max_count = 0; + int max_index = 0; + for (int src_index = 0; src_index < src_count; src_index++) { + int pile_count = buckets_[src_index] - used.buckets_[src_index]; + if (pile_count > max_count) { + max_count = pile_count; + max_index = src_index; + } + } + if (max_count > 0) { + // Copy the bucket count to used so it doesn't get found again. + used.buckets_[max_index] = max_count; + // Get the entire peak. + double total_value = max_index * max_count; + int total_count = max_count; + int prev_pile = max_count; + for (int offset = 1; max_index + offset < src_count; ++offset) { + if (!GatherPeak(max_index + offset, buckets_, used.buckets_, + &prev_pile, &total_count, &total_value)) + break; + } + prev_pile = buckets_[max_index]; + for (int offset = 1; max_index - offset >= 0; ++offset) { + if (!GatherPeak(max_index - offset, buckets_, used.buckets_, + &prev_pile, &total_count, &total_value)) + break; + } + if (total_count > least_count || modes->size() < max_modes) { + // We definitely want this mode, so if we have enough discard the least. + if (modes->size() == max_modes) + modes->truncate(max_modes - 1); + int target_index = 0; + // Linear search for the target insertion point. + while (target_index < modes->size() && + (*modes)[target_index].data >= total_count) + ++target_index; + float peak_mean = + static_cast(total_value / total_count + rangemin_); + modes->insert(KDPairInc(peak_mean, total_count), + target_index); + least_count = modes->back().data; + } + } + } while (max_count > 0); + return modes->size(); +} + +/********************************************************************** + * STATS::print + * + * Prints a summary and table of the histogram. + **********************************************************************/ +void STATS::print() const { + if (buckets_ == NULL) { + return; + } + inT32 min = min_bucket() - rangemin_; + inT32 max = max_bucket() - rangemin_; + + int num_printed = 0; + for (int index = min; index <= max; index++) { + if (buckets_[index] != 0) { + tprintf("%4d:%-3d ", rangemin_ + index, buckets_[index]); + if (++num_printed % 8 == 0) + tprintf ("\n"); + } + } + tprintf ("\n"); + print_summary(); +} + + + +/********************************************************************** + * STATS::print_summary + * + * Print a summary of the stats. + **********************************************************************/ +void STATS::print_summary() const { + if (buckets_ == NULL) { + return; + } + inT32 min = min_bucket(); + inT32 max = max_bucket(); + tprintf("Total count=%d\n", total_count_); + tprintf("Min=%.2f Really=%d\n", ile(0.0), min); + tprintf("Lower quartile=%.2f\n", ile(0.25)); + tprintf("Median=%.2f, ile(0.5)=%.2f\n", median(), ile(0.5)); + tprintf("Upper quartile=%.2f\n", ile(0.75)); + tprintf("Max=%.2f Really=%d\n", ile(1.0), max); + tprintf("Range=%d\n", max + 1 - min); + tprintf("Mean= %.2f\n", mean()); + tprintf("SD= %.2f\n", sd()); +} + + +/********************************************************************** + * STATS::plot + * + * Draw a histogram of the stats table. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void STATS::plot(ScrollView* window, // to draw in + float xorigin, // bottom left + float yorigin, + float xscale, // one x unit + float yscale, // one y unit + ScrollView::Color colour) const { // colour to draw in + if (buckets_ == NULL) { + return; + } + window->Pen(colour); + + for (int index = 0; index < rangemax_ - rangemin_; index++) { + window->Rectangle( xorigin + xscale * index, yorigin, + xorigin + xscale * (index + 1), + yorigin + yscale * buckets_[index]); + } +} +#endif + + +/********************************************************************** + * STATS::plotline + * + * Draw a histogram of the stats table. (Line only) + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void STATS::plotline(ScrollView* window, // to draw in + float xorigin, // bottom left + float yorigin, + float xscale, // one x unit + float yscale, // one y unit + ScrollView::Color colour) const { // colour to draw in + if (buckets_ == NULL) { + return; + } + window->Pen(colour); + window->SetCursor(xorigin, yorigin + yscale * buckets_[0]); + for (int index = 0; index < rangemax_ - rangemin_; index++) { + window->DrawTo(xorigin + xscale * index, + yorigin + yscale * buckets_[index]); + } +} +#endif + + +/********************************************************************** + * choose_nth_item + * + * Returns the index of what would b the nth item in the array + * if the members were sorted, without actually sorting. + **********************************************************************/ + +inT32 choose_nth_item(inT32 index, float *array, inT32 count) { + inT32 next_sample; // next one to do + inT32 next_lesser; // space for new + inT32 prev_greater; // last one saved + inT32 equal_count; // no of equal ones + float pivot; // proposed median + float sample; // current sample + + if (count <= 1) + return 0; + if (count == 2) { + if (array[0] < array[1]) { + return index >= 1 ? 1 : 0; + } + else { + return index >= 1 ? 0 : 1; + } + } + else { + if (index < 0) + index = 0; // ensure legal + else if (index >= count) + index = count - 1; + equal_count = (inT32) (rand() % count); + pivot = array[equal_count]; + // fill gap + array[equal_count] = array[0]; + next_lesser = 0; + prev_greater = count; + equal_count = 1; + for (next_sample = 1; next_sample < prev_greater;) { + sample = array[next_sample]; + if (sample < pivot) { + // shuffle + array[next_lesser++] = sample; + next_sample++; + } + else if (sample > pivot) { + prev_greater--; + // juggle + array[next_sample] = array[prev_greater]; + array[prev_greater] = sample; + } + else { + equal_count++; + next_sample++; + } + } + for (next_sample = next_lesser; next_sample < prev_greater;) + array[next_sample++] = pivot; + if (index < next_lesser) + return choose_nth_item (index, array, next_lesser); + else if (index < prev_greater) + return next_lesser; // in equal bracket + else + return choose_nth_item (index - prev_greater, + array + prev_greater, + count - prev_greater) + prev_greater; + } +} + +/********************************************************************** + * choose_nth_item + * + * Returns the index of what would be the nth item in the array + * if the members were sorted, without actually sorting. + **********************************************************************/ +inT32 choose_nth_item(inT32 index, void *array, inT32 count, size_t size, + int (*compar)(const void*, const void*)) { + int result; // of compar + inT32 next_sample; // next one to do + inT32 next_lesser; // space for new + inT32 prev_greater; // last one saved + inT32 equal_count; // no of equal ones + inT32 pivot; // proposed median + + if (count <= 1) + return 0; + if (count == 2) { + if (compar (array, (char *) array + size) < 0) { + return index >= 1 ? 1 : 0; + } + else { + return index >= 1 ? 0 : 1; + } + } + if (index < 0) + index = 0; // ensure legal + else if (index >= count) + index = count - 1; + pivot = (inT32) (rand () % count); + swap_entries (array, size, pivot, 0); + next_lesser = 0; + prev_greater = count; + equal_count = 1; + for (next_sample = 1; next_sample < prev_greater;) { + result = + compar ((char *) array + size * next_sample, + (char *) array + size * next_lesser); + if (result < 0) { + swap_entries (array, size, next_lesser++, next_sample++); + // shuffle + } + else if (result > 0) { + prev_greater--; + swap_entries(array, size, prev_greater, next_sample); + } + else { + equal_count++; + next_sample++; + } + } + if (index < next_lesser) + return choose_nth_item (index, array, next_lesser, size, compar); + else if (index < prev_greater) + return next_lesser; // in equal bracket + else + return choose_nth_item (index - prev_greater, + (char *) array + size * prev_greater, + count - prev_greater, size, + compar) + prev_greater; +} + +/********************************************************************** + * swap_entries + * + * Swap 2 entries of arbitrary size in-place in a table. + **********************************************************************/ +void swap_entries(void *array, // array of entries + size_t size, // size of entry + inT32 index1, // entries to swap + inT32 index2) { + char tmp; + char *ptr1; // to entries + char *ptr2; + size_t count; // of bytes + + ptr1 = reinterpret_cast(array) + index1 * size; + ptr2 = reinterpret_cast(array) + index2 * size; + for (count = 0; count < size; count++) { + tmp = *ptr1; + *ptr1++ = *ptr2; + *ptr2++ = tmp; // tedious! + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.h new file mode 100644 index 0000000..96bd9e3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/statistc.h @@ -0,0 +1,169 @@ +/********************************************************************** + * File: statistc.h (Formerly stats.h) + * Description: Class description for STATS class. + * Author: Ray Smith + * Created: Mon Feb 04 16:19:07 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSERACT_CCSTRUCT_STATISTC_H_ +#define TESSERACT_CCSTRUCT_STATISTC_H_ + +#include +#include "host.h" +#include "kdpair.h" +#include "scrollview.h" + +template class GenericVector; + + +// Simple histogram-based statistics for integer values in a known +// range, such that the range is small compared to the number of samples. +class STATS { + public: + // The histogram buckets are in the range + // [min_bucket_value, max_bucket_value_plus_1 - 1] i.e. + // [min_bucket_value, max_bucket_value]. + // Any data under min_bucket value is silently mapped to min_bucket_value, + // and likewise, any data over max_bucket_value is silently mapped to + // max_bucket_value. + // In the internal array, min_bucket_value maps to 0 and + // max_bucket_value_plus_1 - min_bucket_value to the array size. + // TODO(rays) This is ugly. Convert the second argument to + // max_bucket_value and all the code that uses it. + STATS(inT32 min_bucket_value, inT32 max_bucket_value_plus_1); + STATS(); // empty for arrays + + ~STATS(); + + // (Re)Sets the range and clears the counts. + // See the constructor for info on max and min values. + bool set_range(inT32 min_bucket_value, inT32 max_bucket_value_plus_1); + + void clear(); // empty buckets + + void add(inT32 value, inT32 count); + + // "Accessors" return various statistics on the data. + inT32 mode() const; // get mode of samples + double mean() const; // get mean of samples + double sd() const; // standard deviation + // Returns the fractile value such that frac fraction (in [0,1]) of samples + // has a value less than the return value. + double ile(double frac) const; + // Returns the minimum used entry in the histogram (ie the minimum of the + // data, NOT the minimum of the supplied range, nor is it an index.) + // Would normally be called min(), but that is a reserved word in VC++. + inT32 min_bucket() const; // Find min + // Returns the maximum used entry in the histogram (ie the maximum of the + // data, NOT the maximum of the supplied range, nor is it an index.) + inT32 max_bucket() const; // Find max + // Finds a more useful estimate of median than ile(0.5). + // Overcomes a problem with ile() - if the samples are, for example, + // 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway + // between 6 and 13 = 9.5 + double median() const; // get median of samples + // Returns the count of the given value. + inT32 pile_count(inT32 value ) const { + if (value <= rangemin_) + return buckets_[0]; + if (value >= rangemax_ - 1) + return buckets_[rangemax_ - rangemin_ - 1]; + return buckets_[value - rangemin_]; + } + // Returns the total count of all buckets. + inT32 get_total() const { + return total_count_; // total of all piles + } + // Returns true if x is a local min. + bool local_min(inT32 x) const; + + // Apply a triangular smoothing filter to the stats. + // This makes the modes a bit more useful. + // The factor gives the height of the triangle, i.e. the weight of the + // centre. + void smooth(inT32 factor); + + // Cluster the samples into max_cluster clusters. + // Each call runs one iteration. The array of clusters must be + // max_clusters+1 in size as cluster 0 is used to indicate which samples + // have been used. + // The return value is the current number of clusters. + inT32 cluster(float lower, // thresholds + float upper, + float multiple, // distance threshold + inT32 max_clusters, // max no to make + STATS *clusters); // array of clusters + +// Finds (at most) the top max_modes modes, well actually the whole peak around +// each mode, returning them in the given modes vector as a pair in order of decreasing total count. +// Since the mean is the key and the count the data in the pair, a single call +// to sort on the output will re-sort by increasing mean of peak if that is +// more useful than decreasing total count. +// Returns the actual number of modes found. + int top_n_modes( + int max_modes, + GenericVector >* modes) const; + + // Prints a summary and table of the histogram. + void print() const; + // Prints summary stats only of the histogram. + void print_summary() const; + + #ifndef GRAPHICS_DISABLED + // Draws the histogram as a series of rectangles. + void plot(ScrollView* window, // window to draw in + float xorigin, // origin of histo + float yorigin, // gram + float xscale, // size of one unit + float yscale, // size of one uint + ScrollView::Color colour) const; // colour to draw in + + // Draws a line graph of the histogram. + void plotline(ScrollView* window, // window to draw in + float xorigin, // origin of histo + float yorigin, // gram + float xscale, // size of one unit + float yscale, // size of one uint + ScrollView::Color colour) const; // colour to draw in + #endif // GRAPHICS_DISABLED + + private: + inT32 rangemin_; // min of range + // rangemax_ is not well named as it is really one past the max. + inT32 rangemax_; // max of range + inT32 total_count_; // no of samples + inT32* buckets_; // array of cells +}; + +// Returns the nth ordered item from the array, as if they were +// ordered, but without ordering them, in linear time. +// The array does get shuffled! +inT32 choose_nth_item(inT32 index, // index to choose + float *array, // array of items + inT32 count); // no of items +// Generic version uses a defined comparator (with qsort semantics). +inT32 choose_nth_item(inT32 index, // index to choose + void *array, // array of items + inT32 count, // no of items + size_t size, // element size + int (*compar)(const void*, const void*)); // comparator +// Swaps 2 entries in an array in-place. +void swap_entries(void *array, // array of entries + size_t size, // size of entry + inT32 index1, // entries to swap + inT32 index2); + +#endif // TESSERACT_CCSTRUCT_STATISTC_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.cpp new file mode 100644 index 0000000..f387aa4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.cpp @@ -0,0 +1,547 @@ +/********************************************************************** + * File: stepblob.cpp (Formerly cblob.c) + * Description: Code for C_BLOB class. + * Author: Ray Smith + * Created: Tue Oct 08 10:41:13 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "stepblob.h" +#include "allheaders.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +// Max perimeter to width ratio for a baseline position above box bottom. +const double kMaxPerimeterWidthRatio = 8.0; + +ELISTIZE (C_BLOB) +/********************************************************************** + * position_outline + * + * Position the outline in the given list at the relevant place + * according to its nesting. + **********************************************************************/ +static void position_outline( //put in place + C_OUTLINE *outline, //thing to place + C_OUTLINE_LIST *destlist //desstination list + ) { + C_OUTLINE *dest_outline; //outline from dest list + C_OUTLINE_IT it = destlist; //iterator + //iterator on children + C_OUTLINE_IT child_it = outline->child (); + + if (!it.empty ()) { + do { + dest_outline = it.data (); //get destination + //encloses dest + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + //put this in place + it.add_after_then_move (outline); + //make it a child + child_it.add_to_end (dest_outline); + while (!it.at_last ()) { + it.forward (); //do rest of list + //check for other children + dest_outline = it.data (); + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + child_it.add_to_end (dest_outline); + //make it a child + if (it.empty ()) + break; + } + } + return; //finished + } + //enclosed by dest + else if (*outline < *dest_outline) { + position_outline (outline, dest_outline->child ()); + //place in child list + return; //finished + } + it.forward (); + } + while (!it.at_first ()); + } + it.add_to_end (outline); //at outer level +} + + +/********************************************************************** + * plot_outline_list + * + * Draw a list of outlines in the given colour and their children + * in the child colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +static void plot_outline_list( //draw outlines + C_OUTLINE_LIST *list, //outline to draw + ScrollView* window, //window to draw in + ScrollView::Color colour, //colour to use + ScrollView::Color child_colour //colour of children + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + //draw it + outline->plot (window, colour); + if (!outline->child ()->empty ()) + plot_outline_list (outline->child (), window, + child_colour, child_colour); + } +} +// Draws the outlines in the given colour, and child_colour, normalized +// using the given denorm, making use of sub-pixel accurate information +// if available. +static void plot_normed_outline_list(const DENORM& denorm, + C_OUTLINE_LIST *list, + ScrollView::Color colour, + ScrollView::Color child_colour, + ScrollView* window) { + C_OUTLINE_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + outline->plot_normed(denorm, colour, window); + if (!outline->child()->empty()) + plot_normed_outline_list(denorm, outline->child(), child_colour, + child_colour, window); + } +} +#endif + + +/********************************************************************** + * reverse_outline_list + * + * Reverse a list of outlines and their children. + **********************************************************************/ + +static void reverse_outline_list(C_OUTLINE_LIST *list) { + C_OUTLINE_IT it = list; // iterator + + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + outline->reverse(); // reverse it + outline->set_flag(COUT_INVERSE, TRUE); + if (!outline->child()->empty()) + reverse_outline_list(outline->child()); + } +} + + +/********************************************************************** + * C_BLOB::C_BLOB + * + * Constructor to build a C_BLOB from a list of C_OUTLINEs. + * The C_OUTLINEs are not copied so the source list is emptied. + * The C_OUTLINEs are nested correctly in the blob. + **********************************************************************/ + +C_BLOB::C_BLOB(C_OUTLINE_LIST *outline_list) { + for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) { + C_OUTLINE* outline = ol_it.extract(); + // Position this outline in appropriate position in the hierarchy. + position_outline(outline, &outlines); + } + CheckInverseFlagAndDirection(); +} + +// Simpler constructor to build a blob from a single outline that has +// already been fully initialized. +C_BLOB::C_BLOB(C_OUTLINE* outline) { + C_OUTLINE_IT it(&outlines); + it.add_to_end(outline); +} + +// Builds a set of one or more blobs from a list of outlines. +// Input: one outline on outline_list contains all the others, but the +// nesting and order are undefined. +// If good_blob is true, the blob is added to good_blobs_it, unless +// an illegal (generation-skipping) parent-child relationship is found. +// If so, the parent blob goes to bad_blobs_it, and the immediate children +// are promoted to the top level, recursively being sent to good_blobs_it. +// If good_blob is false, all created blobs will go to the bad_blobs_it. +// Output: outline_list is empty. One or more blobs are added to +// good_blobs_it and/or bad_blobs_it. +void C_BLOB::ConstructBlobsFromOutlines(bool good_blob, + C_OUTLINE_LIST* outline_list, + C_BLOB_IT* good_blobs_it, + C_BLOB_IT* bad_blobs_it) { + // List of top-level outlines with correctly nested children. + C_OUTLINE_LIST nested_outlines; + for (C_OUTLINE_IT ol_it(outline_list); !ol_it.empty(); ol_it.forward()) { + C_OUTLINE* outline = ol_it.extract(); + // Position this outline in appropriate position in the hierarchy. + position_outline(outline, &nested_outlines); + } + // Check for legal nesting and reassign as required. + for (C_OUTLINE_IT ol_it(&nested_outlines); !ol_it.empty(); ol_it.forward()) { + C_OUTLINE* outline = ol_it.extract(); + bool blob_is_good = good_blob; + if (!outline->IsLegallyNested()) { + // The blob is illegally nested. + // Mark it bad, and add all its children to the top-level list. + blob_is_good = false; + ol_it.add_list_after(outline->child()); + } + C_BLOB* blob = new C_BLOB(outline); + // Set inverse flag and reverse if needed. + blob->CheckInverseFlagAndDirection(); + // Put on appropriate list. + if (!blob_is_good && bad_blobs_it != NULL) + bad_blobs_it->add_after_then_move(blob); + else + good_blobs_it->add_after_then_move(blob); + } +} + +// Sets the COUT_INVERSE flag appropriately on the outlines and their +// children recursively, reversing the outlines if needed so that +// everything has an anticlockwise top-level. +void C_BLOB::CheckInverseFlagAndDirection() { + 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(); + if (outline->turn_direction() < 0) { + outline->reverse(); + reverse_outline_list(outline->child()); + outline->set_flag(COUT_INVERSE, TRUE); + } else { + outline->set_flag(COUT_INVERSE, FALSE); + } + } +} + + +// Build and return a fake blob containing a single fake outline with no +// steps. +C_BLOB* C_BLOB::FakeBlob(const TBOX& box) { + C_OUTLINE_LIST outlines; + C_OUTLINE::FakeOutline(box, &outlines); + return new C_BLOB(&outlines); +} + +/********************************************************************** + * C_BLOB::bounding_box + * + * Return the bounding box of the blob. + **********************************************************************/ + +TBOX C_BLOB::bounding_box() const { // bounding box + C_OUTLINE *outline; // current outline + // This is a read-only iteration of the outlines. + C_OUTLINE_IT it = const_cast(&outlines); + TBOX box; // bounding box + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + box += outline->bounding_box (); + } + return box; +} + + +/********************************************************************** + * C_BLOB::area + * + * Return the area of the blob. + **********************************************************************/ + +inT32 C_BLOB::area() { //area + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + inT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->area (); + } + return total; +} + +/********************************************************************** + * C_BLOB::perimeter + * + * Return the perimeter of the top and 2nd level outlines. + **********************************************************************/ + +inT32 C_BLOB::perimeter() { + C_OUTLINE *outline; // current outline + C_OUTLINE_IT it = &outlines; // outlines of blob + inT32 total; // total perimeter + + total = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + outline = it.data(); + total += outline->perimeter(); + } + return total; +} + + +/********************************************************************** + * C_BLOB::outer_area + * + * Return the area of the blob. + **********************************************************************/ + +inT32 C_BLOB::outer_area() { //area + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + inT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->outer_area (); + } + return total; +} + + +/********************************************************************** + * C_BLOB::count_transitions + * + * Return the total x and y maxes and mins in the blob. + * Chlid outlines are not counted. + **********************************************************************/ + +inT32 C_BLOB::count_transitions( //area + inT32 threshold //on size + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + inT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->count_transitions (threshold); + } + return total; +} + + +/********************************************************************** + * C_BLOB::move + * + * Move C_BLOB by vector + **********************************************************************/ + +void C_BLOB::move( // reposition blob + const ICOORD vec // by vector + ) { + C_OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move each outline +} + +// Static helper for C_BLOB::rotate to allow recursion of child outlines. +void RotateOutlineList(const FCOORD& rotation, C_OUTLINE_LIST* outlines) { + C_OUTLINE_LIST new_outlines; + C_OUTLINE_IT src_it(outlines); + C_OUTLINE_IT dest_it(&new_outlines); + while (!src_it.empty()) { + C_OUTLINE* old_outline = src_it.extract(); + src_it.forward(); + C_OUTLINE* new_outline = new C_OUTLINE(old_outline, rotation); + if (!old_outline->child()->empty()) { + RotateOutlineList(rotation, old_outline->child()); + C_OUTLINE_IT child_it(new_outline->child()); + child_it.add_list_after(old_outline->child()); + } + delete old_outline; + dest_it.add_to_end(new_outline); + } + src_it.add_list_after(&new_outlines); +} + +/********************************************************************** + * C_BLOB::rotate + * + * Rotate C_BLOB by rotation. + * Warning! has to rebuild all the C_OUTLINEs. + **********************************************************************/ +void C_BLOB::rotate(const FCOORD& rotation) { + RotateOutlineList(rotation, &outlines); +} + +// Helper calls ComputeEdgeOffsets or ComputeBinaryOffsets recursively on the +// outline list and its children. +static void ComputeEdgeOffsetsOutlineList(int threshold, Pix* pix, + C_OUTLINE_LIST *list) { + C_OUTLINE_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + if (pix != NULL && pixGetDepth(pix) == 8) + outline->ComputeEdgeOffsets(threshold, pix); + else + outline->ComputeBinaryOffsets(); + if (!outline->child()->empty()) + ComputeEdgeOffsetsOutlineList(threshold, pix, outline->child()); + } +} + +// Adds sub-pixel resolution EdgeOffsets for the outlines using greyscale +// if the supplied pix is 8-bit or the binary edges if NULL. +void C_BLOB::ComputeEdgeOffsets(int threshold, Pix* pix) { + ComputeEdgeOffsetsOutlineList(threshold, pix, &outlines); +} + +// Estimates and returns the baseline position based on the shape of the +// outlines. +// We first find the minimum y-coord (y_mins) at each x-coord within the blob. +// If there is a run of some y or y+1 in y_mins that is longer than the total +// number of positions at bottom or bottom+1, subject to the additional +// condition that at least one side of the y/y+1 run is higher than y+1, so it +// is not a local minimum, then y, not the bottom, makes a good candidate +// baseline position for this blob. Eg +// | ---| +// | | +// |- -----------| <= Good candidate baseline position. +// |- -| +// | -| +// |---| <= Bottom of blob +inT16 C_BLOB::EstimateBaselinePosition() { + TBOX box = bounding_box(); + int left = box.left(); + int width = box.width(); + int bottom = box.bottom(); + if (outlines.empty() || perimeter() > width * kMaxPerimeterWidthRatio) + return bottom; // This is only for non-CJK blobs. + // Get the minimum y coordinate at each x-coordinate. + GenericVector y_mins; + y_mins.init_to_size(width + 1, box.top()); + C_OUTLINE_IT it(&outlines); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + ICOORD pos = outline->start_pos(); + for (int s = 0; s < outline->pathlength(); ++s) { + if (pos.y() < y_mins[pos.x() - left]) + y_mins[pos.x() - left] = pos.y(); + pos += outline->step(s); + } + } + // Find the total extent of the bottom or bottom + 1. + int bottom_extent = 0; + for (int x = 0; x <= width; ++x) { + if (y_mins[x] == bottom || y_mins[x] == bottom + 1) + ++bottom_extent; + } + // Find the lowest run longer than the bottom extent that is not the bottom. + int best_min = box.top(); + int prev_run = 0; + int prev_y = box.top(); + int prev_prev_y = box.top(); + for (int x = 0; x < width; x += prev_run) { + // Find the length of the current run. + int y_at_x = y_mins[x]; + int run = 1; + while (x + run <= width && y_mins[x + run] == y_at_x) ++run; + if (y_at_x > bottom + 1) { + // Possible contender. + int total_run = run; + // Find extent of current value or +1 to the right of x. + while (x + total_run <= width && + (y_mins[x + total_run] == y_at_x || + y_mins[x + total_run] == y_at_x + 1)) ++total_run; + // At least one end has to be higher so it is not a local max. + if (prev_prev_y > y_at_x + 1 || x + total_run > width || + y_mins[x + total_run] > y_at_x + 1) { + // If the prev_run is at y + 1, then we can add that too. There cannot + // be a suitable run at y before that or we would have found it already. + if (prev_run > 0 && prev_y == y_at_x + 1) total_run += prev_run; + if (total_run > bottom_extent && y_at_x < best_min) { + best_min = y_at_x; + } + } + } + prev_run = run; + prev_prev_y = prev_y; + prev_y = y_at_x; + } + return best_min == box.top() ? bottom : best_min; +} + +static void render_outline_list(C_OUTLINE_LIST *list, + int left, int top, Pix* pix) { + C_OUTLINE_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + outline->render(left, top, pix); + if (!outline->child()->empty()) + render_outline_list(outline->child(), left, top, pix); + } +} + +static void render_outline_list_outline(C_OUTLINE_LIST *list, + int left, int top, Pix* pix) { + C_OUTLINE_IT it(list); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + C_OUTLINE* outline = it.data(); + outline->render_outline(left, top, pix); + } +} + +// Returns a Pix rendering of the blob. pixDestroy after use. +Pix* C_BLOB::render() { + TBOX box = bounding_box(); + Pix* pix = pixCreate(box.width(), box.height(), 1); + render_outline_list(&outlines, box.left(), box.top(), pix); + return pix; +} + +// Returns a Pix rendering of the outline of the blob. (no fill). +// pixDestroy after use. +Pix* C_BLOB::render_outline() { + TBOX box = bounding_box(); + Pix* pix = pixCreate(box.width(), box.height(), 1); + render_outline_list_outline(&outlines, box.left(), box.top(), pix); + return pix; +} + +/********************************************************************** + * C_BLOB::plot + * + * Draw the C_BLOB in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void C_BLOB::plot(ScrollView* window, // window to draw in + ScrollView::Color blob_colour, // main colour + ScrollView::Color child_colour) { // for holes + plot_outline_list(&outlines, window, blob_colour, child_colour); +} +// Draws the blob in the given colour, and child_colour, normalized +// using the given denorm, making use of sub-pixel accurate information +// if available. +void C_BLOB::plot_normed(const DENORM& denorm, + ScrollView::Color blob_colour, + ScrollView::Color child_colour, + ScrollView* window) { + plot_normed_outline_list(denorm, &outlines, blob_colour, child_colour, + window); +} +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.h new file mode 100644 index 0000000..e1cde2e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/stepblob.h @@ -0,0 +1,131 @@ +/********************************************************************** + * File: stepblob.h (Formerly cblob.h) + * Description: Code for C_BLOB class. + * Author: Ray Smith + * Created: Tue Oct 08 10:41:13 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STEPBLOB_H +#define STEPBLOB_H + +#include "coutln.h" +#include "rect.h" + +class C_BLOB; +struct Pix; +ELISTIZEH(C_BLOB) + +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); + + // Builds a set of one or more blobs from a list of outlines. + // Input: one outline on outline_list contains all the others, but the + // nesting and order are undefined. + // If good_blob is true, the blob is added to good_blobs_it, unless + // an illegal (generation-skipping) parent-child relationship is found. + // If so, the parent blob goes to bad_blobs_it, and the immediate children + // are promoted to the top level, recursively being sent to good_blobs_it. + // If good_blob is false, all created blobs will go to the bad_blobs_it. + // Output: outline_list is empty. One or more blobs are added to + // good_blobs_it and/or bad_blobs_it. + static void ConstructBlobsFromOutlines(bool good_blob, + C_OUTLINE_LIST* outline_list, + C_BLOB_IT* good_blobs_it, + C_BLOB_IT* bad_blobs_it); + + // Sets the COUT_INVERSE flag appropriately on the outlines and their + // children recursively, reversing the outlines if needed so that + // everything has an anticlockwise top-level. + void CheckInverseFlagAndDirection(); + + // 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() const; // 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. + + // Adds sub-pixel resolution EdgeOffsets for the outlines using greyscale + // if the supplied pix is 8-bit or the binary edges if NULL. + void ComputeEdgeOffsets(int threshold, Pix* pix); + + // Estimates and returns the baseline position based on the shape of the + // outlines. + inT16 EstimateBaselinePosition(); + + // 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(); + + #ifndef GRAPHICS_DISABLED + void plot( //draw one + ScrollView* window, //window to draw in + ScrollView::Color blob_colour, //for outer bits + ScrollView::Color child_colour); //for holes + // Draws the blob in the given colour, and child_colour, normalized + // using the given denorm, making use of sub-pixel accurate information + // if available. + void plot_normed(const DENORM& denorm, + ScrollView::Color blob_colour, + ScrollView::Color child_colour, + ScrollView* window); + #endif // GRAPHICS_DISABLED + + 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; + } + + static int SortByXMiddle(const void *v1, const void *v2) { + const C_BLOB* blob1 = *reinterpret_cast(v1); + const C_BLOB* blob2 = *reinterpret_cast(v2); + return blob1->bounding_box().x_middle() - + blob2->bounding_box().x_middle(); + } + + + private: + C_OUTLINE_LIST outlines; //master elements +}; + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.cpp new file mode 100644 index 0000000..bafca55 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.cpp @@ -0,0 +1,63 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: vecfuncs.c (Formerly vecfuncs.c) + * Description: Blob definition + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 27 15:39:52 1989 + * Modified: Tue Jul 9 17:44:12 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + * Revision 5.1 89/07/27 11:47:50 11:47:50 ray () + * Added ratings access methods. + * This version ready for independent development. + */ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "vecfuncs.h" +#include "blobs.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * direction + * + * Show if the line is going in the positive or negative X direction. + **********************************************************************/ +int direction(EDGEPT *point) { + int dir; /** direction to return **/ + EDGEPT *prev; /** prev point **/ + EDGEPT *next; /** next point **/ + + dir = 0; + prev = point->prev; + next = point->next; + + if (((prev->pos.x <= point->pos.x) && + (point->pos.x < next->pos.x)) || + ((prev->pos.x < point->pos.x) && (point->pos.x <= next->pos.x))) + dir = 1; + + if (((prev->pos.x >= point->pos.x) && + (point->pos.x > next->pos.x)) || + ((prev->pos.x > point->pos.x) && (point->pos.x >= next->pos.x))) + dir = -1; + + return dir; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.h new file mode 100644 index 0000000..55cf310 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/vecfuncs.h @@ -0,0 +1,78 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: vecfuncs.h (Formerly vecfuncs.h) + * Description: Vector calculations + * Author: Mark Seaman, OCR Technology + * Created: Wed Dec 20 09:37:18 1989 + * Modified: Tue Jul 9 17:44:37 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef VECFUNCS_H +#define VECFUNCS_H + +#include + +struct EDGEPT; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * point_diff + * + * Return the difference from point (p1) to point (p2). Put the value + * into point (p). + **********************************************************************/ + +#define point_diff(p,p1,p2) \ +((p).x = (p1).x - (p2).x, \ + (p).y = (p1).y - (p2).y) + +/********************************************************************** + * CROSS + * + * cross product + **********************************************************************/ + +#define CROSS(a,b) \ +((a).x * (b).y - (a).y * (b).x) + +/********************************************************************** + * SCALAR + * + * scalar vector product + **********************************************************************/ + +#define SCALAR(a,b) \ +((a).x * (b).x + (a).y * (b).y) + +/********************************************************************** + * LENGTH + * + * length of vector + **********************************************************************/ + +#define LENGTH(a) \ +((a).x * (a).x + (a).y * (a).y) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int direction(EDGEPT *point); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.cpp b/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.cpp new file mode 100644 index 0000000..4410a06 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.cpp @@ -0,0 +1,600 @@ +/********************************************************************** + * File: werd.cpp (Formerly word.c) + * Description: Code for the WERD class. + * Author: Ray Smith + * Created: Tue Oct 08 14:32:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "blckerr.h" +#include "helpers.h" +#include "linlsq.h" +#include "werd.h" + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#define FIRST_COLOUR ScrollView::RED //< first rainbow colour +#define LAST_COLOUR ScrollView::AQUAMARINE //< last rainbow colour +#define CHILD_COLOUR ScrollView::BROWN //< colour of children + +const ERRCODE CANT_SCALE_EDGESTEPS = + "Attempted to scale an edgestep format word"; + +ELIST2IZE(WERD) + +/** + * WERD::WERD + * + * Constructor to build a WERD from a list of C_BLOBs. + * blob_list The C_BLOBs (in word order) are not copied; + * we take its elements and put them in our lists. + * blank_count blanks in front of the word + * text correct text, outlives this WERD + */ +WERD::WERD(C_BLOB_LIST *blob_list, uinT8 blank_count, const char *text) + : blanks(blank_count), + flags(0), + script_id_(0), + correct(text) { + C_BLOB_IT start_it = &cblobs; + C_BLOB_IT rej_cblob_it = &rej_cblobs; + C_OUTLINE_IT c_outline_it; + inT16 inverted_vote = 0; + inT16 non_inverted_vote = 0; + + // Move blob_list's elements into cblobs. + start_it.add_list_after(blob_list); + + /* + Set white on black flag for the WERD, moving any duff blobs onto the + rej_cblobs list. + First, walk the cblobs checking the inverse flag for each outline of each + cblob. If a cblob has inconsistent flag settings for its different + outlines, move the blob to the reject list. Otherwise, increment the + appropriate w-on-b or b-on-w vote for the word. + + Now set the inversion flag for the WERD by maximum vote. + + Walk the blobs again, moving any blob whose inversion flag does not agree + with the concencus onto the reject list. + */ + start_it.set_to_list(&cblobs); + if (start_it.empty()) + return; + for (start_it.mark_cycle_pt(); !start_it.cycled_list(); start_it.forward()) { + BOOL8 reject_blob = FALSE; + BOOL8 blob_inverted; + + c_outline_it.set_to_list(start_it.data()->out_list()); + blob_inverted = c_outline_it.data()->flag(COUT_INVERSE); + for (c_outline_it.mark_cycle_pt(); + !c_outline_it.cycled_list() && !reject_blob; + c_outline_it.forward()) { + reject_blob = c_outline_it.data()->flag(COUT_INVERSE) != blob_inverted; + } + if (reject_blob) { + rej_cblob_it.add_after_then_move(start_it.extract()); + } else { + if (blob_inverted) + inverted_vote++; + else + non_inverted_vote++; + } + } + + flags.set_bit(W_INVERSE, (inverted_vote > non_inverted_vote)); + + start_it.set_to_list(&cblobs); + if (start_it.empty()) + return; + for (start_it.mark_cycle_pt(); !start_it.cycled_list(); start_it.forward()) { + c_outline_it.set_to_list(start_it.data()->out_list()); + if (c_outline_it.data()->flag(COUT_INVERSE) != flags.bit(W_INVERSE)) + rej_cblob_it.add_after_then_move(start_it.extract()); + } +} + + +/** + * WERD::WERD + * + * Constructor to build a WERD from a list of C_BLOBs. + * The C_BLOBs are not copied so the source list is emptied. + */ + +WERD::WERD(C_BLOB_LIST * blob_list, //< In word order + WERD * clone) //< Source of flags + : flags(clone->flags), + script_id_(clone->script_id_), + correct(clone->correct) { + C_BLOB_IT start_it = blob_list; // iterator + C_BLOB_IT end_it = blob_list; // another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((C_BLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + blanks = clone->blanks; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + +// Construct a WERD from a single_blob and clone the flags from this. +// W_BOL and W_EOL flags are set according to the given values. +WERD* WERD::ConstructFromSingleBlob(bool bol, bool eol, C_BLOB* blob) { + C_BLOB_LIST temp_blobs; + C_BLOB_IT temp_it(&temp_blobs); + temp_it.add_after_then_move(blob); + WERD* blob_word = new WERD(&temp_blobs, this); + blob_word->set_flag(W_BOL, bol); + blob_word->set_flag(W_EOL, eol); + return blob_word; +} + +/** + * WERD::bounding_box + * + * Return the bounding box of the WERD. + * This is quite a mess to compute! + * ORIGINALLY, REJECT CBLOBS WERE EXCLUDED, however, this led to bugs when the + * words on the row were re-sorted. The original words were built with reject + * blobs included. The FUZZY SPACE flags were set accordingly. If ALL the + * blobs in a word are rejected the BB for the word is NULL, causing the sort + * to screw up, leading to the erroneous possibility of the first word in a + * row being marked as FUZZY space. + */ + +TBOX WERD::bounding_box() const { return restricted_bounding_box(true, true); } + +// Returns the bounding box including the desired combination of upper and +// lower noise/diacritic elements. +TBOX WERD::restricted_bounding_box(bool upper_dots, bool lower_dots) const { + TBOX box = true_bounding_box(); + int bottom = box.bottom(); + int top = box.top(); + // This is a read-only iteration of the rejected blobs. + C_BLOB_IT it(const_cast(&rej_cblobs)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + TBOX dot_box = it.data()->bounding_box(); + if ((upper_dots || dot_box.bottom() <= top) && + (lower_dots || dot_box.top() >= bottom)) { + box += dot_box; + } + } + return box; +} + +// Returns the bounding box of only the good blobs. +TBOX WERD::true_bounding_box() const { + TBOX box; // box being built + // This is a read-only iteration of the good blobs. + C_BLOB_IT it(const_cast(&cblobs)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + box += it.data()->bounding_box(); + } + return box; +} + +/** + * WERD::move + * + * Reposition WERD by vector + * NOTE!! REJECT CBLOBS ARE NOT MOVED + */ + +void WERD::move(const ICOORD vec) { + C_BLOB_IT cblob_it(&cblobs); // cblob iterator + + for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) + cblob_it.data()->move(vec); +} + +/** + * WERD::join_on + * + * Join other word onto this one. Delete the old word. + */ + +void WERD::join_on(WERD* other) { + C_BLOB_IT blob_it(&cblobs); + C_BLOB_IT src_it(&other->cblobs); + C_BLOB_IT rej_cblob_it(&rej_cblobs); + C_BLOB_IT src_rej_it(&other->rej_cblobs); + + while (!src_it.empty()) { + blob_it.add_to_end(src_it.extract()); + src_it.forward(); + } + while (!src_rej_it.empty()) { + rej_cblob_it.add_to_end(src_rej_it.extract()); + src_rej_it.forward(); + } +} + + +/** + * WERD::copy_on + * + * Copy blobs from other word onto this one. + */ + +void WERD::copy_on(WERD* other) { + bool reversed = other->bounding_box().left() < bounding_box().left(); + C_BLOB_IT c_blob_it(&cblobs); + C_BLOB_LIST c_blobs; + + c_blobs.deep_copy(&other->cblobs, &C_BLOB::deep_copy); + if (reversed) { + c_blob_it.add_list_before(&c_blobs); + } else { + c_blob_it.move_to_last(); + c_blob_it.add_list_after(&c_blobs); + } + if (!other->rej_cblobs.empty()) { + C_BLOB_IT rej_c_blob_it(&rej_cblobs); + C_BLOB_LIST new_rej_c_blobs; + + new_rej_c_blobs.deep_copy(&other->rej_cblobs, &C_BLOB::deep_copy); + if (reversed) { + rej_c_blob_it.add_list_before(&new_rej_c_blobs); + } else { + rej_c_blob_it.move_to_last(); + rej_c_blob_it.add_list_after(&new_rej_c_blobs); + } + } +} + +/** + * WERD::print + * + * Display members + */ + +void WERD::print() { + tprintf("Blanks= %d\n", blanks); + bounding_box().print(); + tprintf("Flags = %d = 0%o\n", flags.val, flags.val); + tprintf(" W_SEGMENTED = %s\n", flags.bit(W_SEGMENTED) ? "TRUE" : "FALSE "); + tprintf(" W_ITALIC = %s\n", flags.bit(W_ITALIC) ? "TRUE" : "FALSE "); + tprintf(" W_BOL = %s\n", flags.bit(W_BOL) ? "TRUE" : "FALSE "); + tprintf(" W_EOL = %s\n", flags.bit(W_EOL) ? "TRUE" : "FALSE "); + tprintf(" W_NORMALIZED = %s\n", + flags.bit(W_NORMALIZED) ? "TRUE" : "FALSE "); + tprintf(" W_SCRIPT_HAS_XHEIGHT = %s\n", + flags.bit(W_SCRIPT_HAS_XHEIGHT) ? "TRUE" : "FALSE "); + tprintf(" W_SCRIPT_IS_LATIN = %s\n", + flags.bit(W_SCRIPT_IS_LATIN) ? "TRUE" : "FALSE "); + tprintf(" W_DONT_CHOP = %s\n", flags.bit(W_DONT_CHOP) ? "TRUE" : "FALSE "); + tprintf(" W_REP_CHAR = %s\n", flags.bit(W_REP_CHAR) ? "TRUE" : "FALSE "); + tprintf(" W_FUZZY_SP = %s\n", flags.bit(W_FUZZY_SP) ? "TRUE" : "FALSE "); + tprintf(" W_FUZZY_NON = %s\n", flags.bit(W_FUZZY_NON) ? "TRUE" : "FALSE "); + tprintf("Correct= %s\n", correct.string()); + tprintf("Rejected cblob count = %d\n", rej_cblobs.length()); + tprintf("Script = %d\n", script_id_); +} + + +/** + * WERD::plot + * + * Draw the WERD in the given colour. + */ + +#ifndef GRAPHICS_DISABLED +void WERD::plot(ScrollView *window, ScrollView::Color colour) { + C_BLOB_IT it = &cblobs; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot(window, colour, colour); + } + plot_rej_blobs(window); +} + +// Get the next color in the (looping) rainbow. +ScrollView::Color WERD::NextColor(ScrollView::Color colour) { + ScrollView::Color next = static_cast(colour + 1); + if (next >= LAST_COLOUR || next < FIRST_COLOUR) + next = FIRST_COLOUR; + return next; +} + +/** + * WERD::plot + * + * Draw the WERD in rainbow colours in window. + */ + +void WERD::plot(ScrollView* window) { + ScrollView::Color colour = FIRST_COLOUR; + C_BLOB_IT it = &cblobs; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot(window, colour, CHILD_COLOUR); + colour = NextColor(colour); + } + plot_rej_blobs(window); +} + + +/** + * WERD::plot_rej_blobs + * + * Draw the WERD rejected blobs in window - ALWAYS GREY + */ + + +void WERD::plot_rej_blobs(ScrollView *window) { + C_BLOB_IT it = &rej_cblobs; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot(window, ScrollView::GREY, ScrollView::GREY); + } +} +#endif // GRAPHICS_DISABLED + + +/** + * WERD::shallow_copy() + * + * Make a shallow copy of a word + */ + +WERD *WERD::shallow_copy() { + WERD *new_word = new WERD; + + new_word->blanks = blanks; + new_word->flags = flags; + new_word->dummy = dummy; + new_word->correct = correct; + return new_word; +} + + +/** + * WERD::operator= + * + * Assign a word, DEEP copying the blob list + */ + +WERD & WERD::operator= (const WERD & source) { + this->ELIST2_LINK::operator= (source); + blanks = source.blanks; + flags = source.flags; + script_id_ = source.script_id_; + dummy = source.dummy; + correct = source.correct; + if (!cblobs.empty()) + cblobs.clear(); + cblobs.deep_copy(&source.cblobs, &C_BLOB::deep_copy); + + if (!rej_cblobs.empty()) + rej_cblobs.clear(); + rej_cblobs.deep_copy(&source.rej_cblobs, &C_BLOB::deep_copy); + return *this; +} + + +/** + * word_comparator() + * + * word comparator used to sort a word list so that words are in increasing + * order of left edge. + */ + +int word_comparator(const void *word1p, const void *word2p) { + WERD *word1 = *(WERD **)word1p; + WERD *word2 = *(WERD **)word2p; + return word1->bounding_box().left() - word2->bounding_box().left(); +} + +/** + * WERD::ConstructWerdWithNewBlobs() + * + * This method returns a new werd constructed using the blobs in the input + * all_blobs list, which correspond to the blobs in this werd object. The + * blobs used to construct the new word are consumed and removed from the + * input all_blobs list. + * Returns NULL if the word couldn't be constructed. + * Returns original blobs for which no matches were found in the output list + * orphan_blobs (appends). + */ + +WERD* WERD::ConstructWerdWithNewBlobs(C_BLOB_LIST* all_blobs, + C_BLOB_LIST* orphan_blobs) { + C_BLOB_LIST current_blob_list; + C_BLOB_IT werd_blobs_it(¤t_blob_list); + // Add the word's c_blobs. + werd_blobs_it.add_list_after(cblob_list()); + + // New blob list. These contain the blobs which will form the new word. + C_BLOB_LIST new_werd_blobs; + C_BLOB_IT new_blobs_it(&new_werd_blobs); + + // not_found_blobs contains the list of current word's blobs for which a + // corresponding blob wasn't found in the input all_blobs list. + C_BLOB_LIST not_found_blobs; + C_BLOB_IT not_found_it(¬_found_blobs); + not_found_it.move_to_last(); + + werd_blobs_it.move_to_first(); + for (werd_blobs_it.mark_cycle_pt(); !werd_blobs_it.cycled_list(); + werd_blobs_it.forward()) { + C_BLOB* werd_blob = werd_blobs_it.extract(); + TBOX werd_blob_box = werd_blob->bounding_box(); + bool found = false; + // Now find the corresponding blob for this blob in the all_blobs + // list. For now, follow the inefficient method of pairwise + // comparisons. Ideally, one can pre-bucket the blobs by row. + C_BLOB_IT all_blobs_it(all_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(); + // Compute the overlap of the two blobs. If major, a_blob should + // be added to the new blobs list. + TBOX a_blob_box = a_blob->bounding_box(); + if (a_blob_box.null_box()) { + tprintf("Bounding box couldn't be ascertained\n"); + } + if (werd_blob_box.contains(a_blob_box) || + werd_blob_box.major_overlap(a_blob_box)) { + // Old blobs are from minimal splits, therefore are expected to be + // bigger. The new small blobs should cover a significant portion. + // This is it. + all_blobs_it.extract(); + new_blobs_it.add_after_then_move(a_blob); + found = true; + } + } + if (!found) { + not_found_it.add_after_then_move(werd_blob); + } else { + delete werd_blob; + } + } + // Iterate over all not found blobs. Some of them may be due to + // under-segmentation (which is OK, since the corresponding blob is already + // in the list in that case. + not_found_it.move_to_first(); + 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(); + C_BLOB_IT existing_blobs_it(new_blobs_it); + for (existing_blobs_it.mark_cycle_pt(); !existing_blobs_it.cycled_list(); + existing_blobs_it.forward()) { + C_BLOB* a_blob = existing_blobs_it.data(); + TBOX a_blob_box = a_blob->bounding_box(); + if ((not_found_box.major_overlap(a_blob_box) || + a_blob_box.major_overlap(not_found_box)) && + not_found_box.y_overlap_fraction(a_blob_box) > 0.8) { + // Already taken care of. + delete not_found_it.extract(); + break; + } + } + } + if (orphan_blobs) { + C_BLOB_IT orphan_blobs_it(orphan_blobs); + orphan_blobs_it.move_to_last(); + orphan_blobs_it.add_list_after(¬_found_blobs); + } + + // New blobs are ready. Create a new werd object with these. + WERD* new_werd = NULL; + if (!new_werd_blobs.empty()) { + new_werd = new WERD(&new_werd_blobs, this); + } else { + // Add the blobs back to this word so that it can be reused. + C_BLOB_IT this_list_it(cblob_list()); + this_list_it.add_list_after(¬_found_blobs); + } + return new_werd; +} + +// Removes noise from the word by moving small outlines to the rej_cblobs +// list, based on the size_threshold. +void WERD::CleanNoise(float size_threshold) { + C_BLOB_IT blob_it(&cblobs); + C_BLOB_IT rej_it(&rej_cblobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { + C_BLOB* blob = blob_it.data(); + C_OUTLINE_IT ol_it(blob->out_list()); + for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) { + C_OUTLINE* outline = ol_it.data(); + TBOX ol_box = outline->bounding_box(); + int ol_size = + ol_box.width() > ol_box.height() ? ol_box.width() : ol_box.height(); + if (ol_size < size_threshold) { + // This outline is too small. Move it to a separate blob in the + // reject blobs list. + C_BLOB* rej_blob = new C_BLOB(ol_it.extract()); + rej_it.add_after_then_move(rej_blob); + } + } + if (blob->out_list()->empty()) delete blob_it.extract(); + } +} + +// Extracts all the noise outlines and stuffs the pointers into the given +// vector of outlines. Afterwards, the outlines vector owns the pointers. +void WERD::GetNoiseOutlines(GenericVector* outlines) { + C_BLOB_IT rej_it(&rej_cblobs); + for (rej_it.mark_cycle_pt(); !rej_it.empty(); rej_it.forward()) { + C_BLOB* blob = rej_it.extract(); + C_OUTLINE_IT ol_it(blob->out_list()); + outlines->push_back(ol_it.extract()); + delete blob; + } +} + +// Adds the selected outlines to the indcated real blobs, and puts the rest +// back in rej_cblobs where they came from. Where the target_blobs entry is +// NULL, a run of wanted outlines is put into a single new blob. +// Ownership of the outlines is transferred back to the word. (Hence +// GenericVector and not PointerVector.) +// Returns true if any new blob was added to the start of the word, which +// suggests that it might need joining to the word before it, and likewise +// sets make_next_word_fuzzy true if any new blob was added to the end. +bool WERD::AddSelectedOutlines(const GenericVector& wanted, + const GenericVector& target_blobs, + const GenericVector& outlines, + bool* make_next_word_fuzzy) { + bool outline_added_to_start = false; + if (make_next_word_fuzzy != NULL) *make_next_word_fuzzy = false; + C_BLOB_IT rej_it(&rej_cblobs); + for (int i = 0; i < outlines.size(); ++i) { + C_OUTLINE* outline = outlines[i]; + if (outline == NULL) continue; // Already used it. + if (wanted[i]) { + C_BLOB* target_blob = target_blobs[i]; + TBOX noise_box = outline->bounding_box(); + if (target_blob == NULL) { + target_blob = new C_BLOB(outline); + // Need to find the insertion point. + C_BLOB_IT blob_it(&cblobs); + for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); + blob_it.forward()) { + C_BLOB* blob = blob_it.data(); + TBOX blob_box = blob->bounding_box(); + if (blob_box.left() > noise_box.left()) { + if (blob_it.at_first() && !flag(W_FUZZY_SP) && !flag(W_FUZZY_NON)) { + // We might want to join this word to its predecessor. + outline_added_to_start = true; + } + blob_it.add_before_stay_put(target_blob); + break; + } + } + if (blob_it.cycled_list()) { + blob_it.add_to_end(target_blob); + if (make_next_word_fuzzy != NULL) *make_next_word_fuzzy = true; + } + // Add all consecutive wanted, but null-blob outlines to same blob. + C_OUTLINE_IT ol_it(target_blob->out_list()); + while (i + 1 < outlines.size() && wanted[i + 1] && + target_blobs[i + 1] == NULL) { + ++i; + ol_it.add_to_end(outlines[i]); + } + } else { + // Insert outline into this blob. + C_OUTLINE_IT ol_it(target_blob->out_list()); + ol_it.add_to_end(outline); + } + } else { + // Put back on noise list. + rej_it.add_to_end(new C_BLOB(outline)); + } + } + return outline_added_to_start; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.h b/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.h new file mode 100644 index 0000000..f9a89fb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccstruct/werd.h @@ -0,0 +1,199 @@ +/********************************************************************** + * File: word.c + * Description: Code for the WERD class. + * Author: Ray Smith + * Created: Tue Oct 08 14:32:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef WERD_H +#define WERD_H + +#include "params.h" +#include "bits16.h" +#include "elst2.h" +#include "strngs.h" +#include "blckerr.h" +#include "stepblob.h" + +enum WERD_FLAGS +{ + W_SEGMENTED, //< correctly segmented + W_ITALIC, //< italic text + W_BOLD, //< bold text + W_BOL, //< start of line + W_EOL, //< end of line + W_NORMALIZED, //< flags + W_SCRIPT_HAS_XHEIGHT, //< x-height concept makes sense. + W_SCRIPT_IS_LATIN, //< Special case latin for y. splitting. + W_DONT_CHOP, //< fixed pitch chopped + W_REP_CHAR, //< repeated character + W_FUZZY_SP, //< fuzzy space + W_FUZZY_NON, //< fuzzy nonspace + W_INVERSE //< white on black +}; + +enum DISPLAY_FLAGS +{ + /* Display flags bit number allocations */ + DF_BOX, //< Bounding box + DF_TEXT, //< Correct ascii + DF_POLYGONAL, //< Polyg approx + DF_EDGE_STEP, //< Edge steps + DF_BN_POLYGONAL, //< BL normalisd polyapx + DF_BLAMER //< Blamer information +}; + +class ROW; //forward decl + +class WERD : public ELIST2_LINK { + public: + WERD() {} + // WERD constructed with: + // blob_list - blobs of the word (we take this list's contents) + // blanks - number of blanks before the word + // text - correct text (outlives WERD) + WERD(C_BLOB_LIST *blob_list, uinT8 blanks, const char *text); + + // WERD constructed from: + // blob_list - blobs in the word + // clone - werd to clone flags, etc from. + WERD(C_BLOB_LIST *blob_list, WERD *clone); + + // Construct a WERD from a single_blob and clone the flags from this. + // W_BOL and W_EOL flags are set according to the given values. + WERD* ConstructFromSingleBlob(bool bol, bool eol, C_BLOB* blob); + + ~WERD() { + } + + // assignment + WERD & operator= (const WERD &source); + + // This method returns a new werd constructed using the blobs in the input + // all_blobs list, which correspond to the blobs in this werd object. The + // blobs used to construct the new word are consumed and removed from the + // input all_blobs list. + // Returns NULL if the word couldn't be constructed. + // Returns original blobs for which no matches were found in the output list + // orphan_blobs (appends). + WERD *ConstructWerdWithNewBlobs(C_BLOB_LIST *all_blobs, + C_BLOB_LIST *orphan_blobs); + + // Accessors for reject / DUFF blobs in various formats + C_BLOB_LIST *rej_cblob_list() { // compact format + return &rej_cblobs; + } + + // Accessors for good blobs in various formats. + C_BLOB_LIST *cblob_list() { // get compact blobs + return &cblobs; + } + + uinT8 space() { // access function + return blanks; + } + void set_blanks(uinT8 new_blanks) { + blanks = new_blanks; + } + int script_id() const { + return script_id_; + } + void set_script_id(int id) { + script_id_ = id; + } + + // Returns the (default) bounding box including all the dots. + TBOX bounding_box() const; // compute bounding box + // Returns the bounding box including the desired combination of upper and + // lower noise/diacritic elements. + TBOX restricted_bounding_box(bool upper_dots, bool lower_dots) const; + // Returns the bounding box of only the good blobs. + TBOX true_bounding_box() const; + + const char *text() const { return correct.string(); } + void set_text(const char *new_text) { correct = new_text; } + + BOOL8 flag(WERD_FLAGS mask) const { return flags.bit(mask); } + void set_flag(WERD_FLAGS mask, BOOL8 value) { flags.set_bit(mask, value); } + + BOOL8 display_flag(uinT8 flag) const { return disp_flags.bit(flag); } + void set_display_flag(uinT8 flag, BOOL8 value) { + disp_flags.set_bit(flag, value); + } + + WERD *shallow_copy(); // shallow copy word + + // reposition word by vector + void move(const ICOORD vec); + + // join other's blobs onto this werd, emptying out other. + void join_on(WERD* other); + + // copy other's blobs onto this word, leaving other intact. + void copy_on(WERD* other); + + // tprintf word metadata (but not blob innards) + void print(); + + #ifndef GRAPHICS_DISABLED + // plot word on window in a uniform colour + void plot(ScrollView *window, ScrollView::Color colour); + + // Get the next color in the (looping) rainbow. + static ScrollView::Color NextColor(ScrollView::Color colour); + + // plot word on window in a rainbow of colours + void plot(ScrollView *window); + + // plot rejected blobs in a rainbow of colours + void plot_rej_blobs(ScrollView *window); + #endif // GRAPHICS_DISABLED + + // Removes noise from the word by moving small outlines to the rej_cblobs + // list, based on the size_threshold. + void CleanNoise(float size_threshold); + + // Extracts all the noise outlines and stuffs the pointers into the given + // vector of outlines. Afterwards, the outlines vector owns the pointers. + void GetNoiseOutlines(GenericVector *outlines); + // Adds the selected outlines to the indcated real blobs, and puts the rest + // back in rej_cblobs where they came from. Where the target_blobs entry is + // NULL, a run of wanted outlines is put into a single new blob. + // Ownership of the outlines is transferred back to the word. (Hence + // GenericVector and not PointerVector.) + // Returns true if any new blob was added to the start of the word, which + // suggests that it might need joining to the word before it, and likewise + // sets make_next_word_fuzzy true if any new blob was added to the end. + bool AddSelectedOutlines(const GenericVector &wanted, + const GenericVector &target_blobs, + const GenericVector &outlines, + bool *make_next_word_fuzzy); + + private: + uinT8 blanks; // no of blanks + uinT8 dummy; // padding + BITS16 flags; // flags about word + BITS16 disp_flags; // display flags + inT16 script_id_; // From unicharset. + STRING correct; // correct text + C_BLOB_LIST cblobs; // compacted blobs + C_BLOB_LIST rej_cblobs; // DUFF blobs +}; + +ELIST2IZEH (WERD) +#include "ocrrow.h" // placed here due to +// compare words by increasing order of left edge, suitable for qsort(3) +int word_comparator(const void *word1p, const void *word2p); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.cpp new file mode 100644 index 0000000..15a755d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.cpp @@ -0,0 +1,398 @@ +/////////////////////////////////////////////////////////////////////// +// File: ambigs.cc +// Description: Functions for dealing with ambiguities +// (training and recognition). +// Author: Daria Antonova +// Created: Mon Feb 5 11:26:43 PDT 2009 +// +// (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 "ambigs.h" + +#include +#include "helpers.h" +#include "universalambigs.h" + +#if defined _WIN32 +#ifndef __GNUC__ +#define strtok_r strtok_s +#else +#include "strtok_r.h" +#endif /* __GNUC__ */ +#endif /* _WIN32 */ + +namespace tesseract { + +// Maximum line size: +// 10 for sizes of ambigs, tabs, abmig type and newline +// UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig +const int kMaxAmbigStringSize = UNICHAR_LEN * (MAX_AMBIG_SIZE + 1); + +AmbigSpec::AmbigSpec() { + wrong_ngram[0] = INVALID_UNICHAR_ID; + correct_fragments[0] = INVALID_UNICHAR_ID; + correct_ngram_id = INVALID_UNICHAR_ID; + type = NOT_AMBIG; + wrong_ngram_size = 0; +} + +ELISTIZE(AmbigSpec); + +// Initializes the ambigs by adding a NULL pointer to each table. +void UnicharAmbigs::InitUnicharAmbigs(const UNICHARSET& unicharset, + bool use_ambigs_for_adaption) { + for (int i = 0; i < unicharset.size(); ++i) { + replace_ambigs_.push_back(NULL); + dang_ambigs_.push_back(NULL); + one_to_one_definite_ambigs_.push_back(NULL); + if (use_ambigs_for_adaption) { + ambigs_for_adaption_.push_back(NULL); + reverse_ambigs_for_adaption_.push_back(NULL); + } + } +} + +// Loads the universal ambigs that are useful for any language. +void UnicharAmbigs::LoadUniversal(const UNICHARSET& encoder_set, + UNICHARSET* unicharset) { + TFile file; + if (!file.Open(kUniversalAmbigsFile, ksizeofUniversalAmbigsFile)) return; + LoadUnicharAmbigs(encoder_set, &file, 0, false, unicharset); +} + +void UnicharAmbigs::LoadUnicharAmbigs(const UNICHARSET& encoder_set, + TFile *ambig_file, + int debug_level, + bool use_ambigs_for_adaption, + UNICHARSET *unicharset) { + int i, j; + UnicharIdVector *adaption_ambigs_entry; + if (debug_level) tprintf("Reading ambiguities\n"); + + int test_ambig_part_size; + int replacement_ambig_part_size; + // The space for buffer is allocated on the heap to avoid + // GCC frame size warning. + const int kBufferSize = 10 + 2 * kMaxAmbigStringSize; + char *buffer = new char[kBufferSize]; + char replacement_string[kMaxAmbigStringSize]; + UNICHAR_ID test_unichar_ids[MAX_AMBIG_SIZE + 1]; + int line_num = 0; + int type = NOT_AMBIG; + + // Determine the version of the ambigs file. + int version = 0; + ASSERT_HOST(ambig_file->FGets(buffer, kBufferSize) != NULL && + strlen(buffer) > 0); + if (*buffer == 'v') { + version = static_cast(strtol(buffer+1, NULL, 10)); + ++line_num; + } else { + ambig_file->Rewind(); + } + while (ambig_file->FGets(buffer, kBufferSize) != NULL) { + chomp_string(buffer); + if (debug_level > 2) tprintf("read line %s\n", buffer); + ++line_num; + if (!ParseAmbiguityLine(line_num, version, debug_level, encoder_set, + buffer, &test_ambig_part_size, test_unichar_ids, + &replacement_ambig_part_size, + replacement_string, &type)) continue; + // Construct AmbigSpec and add it to the appropriate AmbigSpec_LIST. + AmbigSpec *ambig_spec = new AmbigSpec(); + if (!InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_ + : dang_ambigs_, + test_ambig_part_size, test_unichar_ids, + replacement_ambig_part_size, replacement_string, type, + ambig_spec, unicharset)) + continue; + + // Update one_to_one_definite_ambigs_. + if (test_ambig_part_size == 1 && + replacement_ambig_part_size == 1 && type == DEFINITE_AMBIG) { + if (one_to_one_definite_ambigs_[test_unichar_ids[0]] == NULL) { + one_to_one_definite_ambigs_[test_unichar_ids[0]] = new UnicharIdVector(); + } + one_to_one_definite_ambigs_[test_unichar_ids[0]]->push_back( + ambig_spec->correct_ngram_id); + } + // Update ambigs_for_adaption_. + if (use_ambigs_for_adaption) { + GenericVector encoding; + // Silently ignore invalid strings, as before, so it is safe to use a + // universal ambigs file. + if (unicharset->encode_string(replacement_string, true, &encoding, + NULL, NULL)) { + for (i = 0; i < test_ambig_part_size; ++i) { + if (ambigs_for_adaption_[test_unichar_ids[i]] == NULL) { + ambigs_for_adaption_[test_unichar_ids[i]] = new UnicharIdVector(); + } + adaption_ambigs_entry = ambigs_for_adaption_[test_unichar_ids[i]]; + for (int r = 0; r < encoding.size(); ++r) { + UNICHAR_ID id_to_insert = encoding[r]; + ASSERT_HOST(id_to_insert != INVALID_UNICHAR_ID); + // Add the new unichar id to adaption_ambigs_entry (only if the + // vector does not already contain it) keeping it in sorted order. + for (j = 0; j < adaption_ambigs_entry->size() && + (*adaption_ambigs_entry)[j] > id_to_insert; ++j); + if (j < adaption_ambigs_entry->size()) { + if ((*adaption_ambigs_entry)[j] != id_to_insert) { + adaption_ambigs_entry->insert(id_to_insert, j); + } + } else { + adaption_ambigs_entry->push_back(id_to_insert); + } + } + } + } + } + } + delete[] buffer; + + // Fill in reverse_ambigs_for_adaption from ambigs_for_adaption vector. + if (use_ambigs_for_adaption) { + for (i = 0; i < ambigs_for_adaption_.size(); ++i) { + adaption_ambigs_entry = ambigs_for_adaption_[i]; + if (adaption_ambigs_entry == NULL) continue; + for (j = 0; j < adaption_ambigs_entry->size(); ++j) { + UNICHAR_ID ambig_id = (*adaption_ambigs_entry)[j]; + if (reverse_ambigs_for_adaption_[ambig_id] == NULL) { + reverse_ambigs_for_adaption_[ambig_id] = new UnicharIdVector(); + } + reverse_ambigs_for_adaption_[ambig_id]->push_back(i); + } + } + } + + // Print what was read from the input file. + if (debug_level > 1) { + for (int tbl = 0; tbl < 2; ++tbl) { + const UnicharAmbigsVector &print_table = + (tbl == 0) ? replace_ambigs_ : dang_ambigs_; + for (i = 0; i < print_table.size(); ++i) { + AmbigSpec_LIST *lst = print_table[i]; + if (lst == NULL) continue; + if (!lst->empty()) { + tprintf("%s Ambiguities for %s:\n", + (tbl == 0) ? "Replaceable" : "Dangerous", + unicharset->debug_str(i).string()); + } + AmbigSpec_IT lst_it(lst); + for (lst_it.mark_cycle_pt(); !lst_it.cycled_list(); lst_it.forward()) { + AmbigSpec *ambig_spec = lst_it.data(); + tprintf("wrong_ngram:"); + UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, *unicharset); + tprintf("correct_fragments:"); + UnicharIdArrayUtils::print(ambig_spec->correct_fragments, *unicharset); + } + } + } + if (use_ambigs_for_adaption) { + for (int vec_id = 0; vec_id < 2; ++vec_id) { + const GenericVector &vec = (vec_id == 0) ? + ambigs_for_adaption_ : reverse_ambigs_for_adaption_; + for (i = 0; i < vec.size(); ++i) { + adaption_ambigs_entry = vec[i]; + if (adaption_ambigs_entry != NULL) { + tprintf("%sAmbigs for adaption for %s:\n", + (vec_id == 0) ? "" : "Reverse ", + unicharset->debug_str(i).string()); + for (j = 0; j < adaption_ambigs_entry->size(); ++j) { + tprintf("%s ", unicharset->debug_str( + (*adaption_ambigs_entry)[j]).string()); + } + tprintf("\n"); + } + } + } + } + } +} + +bool UnicharAmbigs::ParseAmbiguityLine( + int line_num, int version, int debug_level, const UNICHARSET &unicharset, + char *buffer, int *test_ambig_part_size, UNICHAR_ID *test_unichar_ids, + int *replacement_ambig_part_size, char *replacement_string, int *type) { + if (version > 1) { + // Simpler format is just wrong-string correct-string type\n. + STRING input(buffer); + GenericVector fields; + input.split(' ', &fields); + if (fields.size() != 3) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + // Encode wrong-string. + GenericVector unichars; + if (!unicharset.encode_string(fields[0].string(), true, &unichars, NULL, + NULL)) { + return false; + } + *test_ambig_part_size = unichars.size(); + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + // Copy encoded string to output. + for (int i = 0; i < unichars.size(); ++i) + test_unichar_ids[i] = unichars[i]; + test_unichar_ids[unichars.size()] = INVALID_UNICHAR_ID; + // Encode replacement-string to check validity. + if (!unicharset.encode_string(fields[1].string(), true, &unichars, NULL, + NULL)) { + return false; + } + *replacement_ambig_part_size = unichars.size(); + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + if (sscanf(fields[2].string(), "%d", type) != 1) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + snprintf(replacement_string, kMaxAmbigStringSize, "%s", fields[1].string()); + return true; + } + int i; + char *token; + char *next_token; + if (!(token = strtok_r(buffer, kAmbigDelimiters, &next_token)) || + !sscanf(token, "%d", test_ambig_part_size) || + *test_ambig_part_size <= 0) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + for (i = 0; i < *test_ambig_part_size; ++i) { + if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; + if (!unicharset.contains_unichar(token)) { + if (debug_level) tprintf(kIllegalUnicharMsg, token); + break; + } + test_unichar_ids[i] = unicharset.unichar_to_id(token); + } + test_unichar_ids[i] = INVALID_UNICHAR_ID; + + if (i != *test_ambig_part_size || + !(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || + !sscanf(token, "%d", replacement_ambig_part_size) || + *replacement_ambig_part_size <= 0) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + replacement_string[0] = '\0'; + for (i = 0; i < *replacement_ambig_part_size; ++i) { + if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; + strcat(replacement_string, token); + if (!unicharset.contains_unichar(token)) { + if (debug_level) tprintf(kIllegalUnicharMsg, token); + break; + } + } + if (i != *replacement_ambig_part_size) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + if (version > 0) { + // The next field being true indicates that the abiguity should + // always be substituted (e.g. '' should always be changed to "). + // For such "certain" n -> m ambigs tesseract will insert character + // fragments for the n pieces in the unicharset. AmbigsFound() + // will then replace the incorrect ngram with the character + // fragments of the correct character (or ngram if m > 1). + // Note that if m > 1, an ngram will be inserted into the + // modified word, not the individual unigrams. Tesseract + // has limited support for ngram unichar (e.g. dawg permuter). + if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || + !sscanf(token, "%d", type)) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + } + return true; +} + +bool UnicharAmbigs::InsertIntoTable( + UnicharAmbigsVector &table, int test_ambig_part_size, + UNICHAR_ID *test_unichar_ids, int replacement_ambig_part_size, + const char *replacement_string, int type, + AmbigSpec *ambig_spec, UNICHARSET *unicharset) { + ambig_spec->type = static_cast(type); + if (test_ambig_part_size == 1 && replacement_ambig_part_size == 1 && + unicharset->to_lower(test_unichar_ids[0]) == + unicharset->to_lower(unicharset->unichar_to_id(replacement_string))) { + ambig_spec->type = CASE_AMBIG; + } + + ambig_spec->wrong_ngram_size = + UnicharIdArrayUtils::copy(test_unichar_ids, ambig_spec->wrong_ngram); + + // Since we need to maintain a constant number of unichar positions in + // order to construct ambig_blob_choices vector in NoDangerousAmbig(), for + // each n->m ambiguity we will have to place n character fragments of the + // correct ngram into the corresponding positions in the vector (e.g. given + // "vvvvw" and vvvv->ww we will place v and |ww|0|4 into position 0, v and + // |ww|1|4 into position 1 and so on. The correct ngram is reconstructed + // from fragments by dawg_permute_and_select(). + + // Insert the corresponding correct ngram into the unicharset. + // Unicharset code assumes that the "base" ngram is inserted into + // the unicharset before fragments of this ngram are inserted. + unicharset->unichar_insert(replacement_string); + ambig_spec->correct_ngram_id = + unicharset->unichar_to_id(replacement_string); + if (replacement_ambig_part_size > 1) { + unicharset->set_isngram(ambig_spec->correct_ngram_id, true); + } + // Add the corresponding fragments of the wrong ngram to unicharset. + int i; + for (i = 0; i < test_ambig_part_size; ++i) { + UNICHAR_ID unichar_id; + if (test_ambig_part_size == 1) { + unichar_id = ambig_spec->correct_ngram_id; + } else { + STRING frag_str = CHAR_FRAGMENT::to_string( + replacement_string, i, test_ambig_part_size, false); + unicharset->unichar_insert(frag_str.string()); + unichar_id = unicharset->unichar_to_id(frag_str.string()); + } + ambig_spec->correct_fragments[i] = unichar_id; + } + ambig_spec->correct_fragments[i] = INVALID_UNICHAR_ID; + + // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST. + // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram. + if (table[test_unichar_ids[0]] == NULL) { + table[test_unichar_ids[0]] = new AmbigSpec_LIST(); + } + if (table[test_unichar_ids[0]]->add_sorted( + AmbigSpec::compare_ambig_specs, true, ambig_spec)) + return true; + delete ambig_spec; + return false; +} + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.h b/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.h new file mode 100644 index 0000000..faab219 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ambigs.h @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////// +// File: ambigs.h +// Description: Constants, flags, functions for dealing with +// ambiguities (training and recognition). +// Author: Daria Antonova +// Created: Mon Aug 23 11:26:43 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_AMBIGS_H_ +#define TESSERACT_CCUTIL_AMBIGS_H_ + +#include "elst.h" +#include "tprintf.h" +#include "unichar.h" +#include "unicharset.h" +#include "genericvector.h" + +#define MAX_AMBIG_SIZE 10 + +namespace tesseract { + +typedef GenericVector UnicharIdVector; + +static const int kUnigramAmbigsBufferSize = 1000; +static const char kAmbigNgramSeparator[] = { ' ', '\0' }; +static const char kAmbigDelimiters[] = "\t "; +static const char kIllegalMsg[] = + "Illegal ambiguity specification on line %d\n"; +static const char kIllegalUnicharMsg[] = + "Illegal unichar %s in ambiguity specification\n"; + +enum AmbigType { + NOT_AMBIG, // the ngram pair is not ambiguous + REPLACE_AMBIG, // ocred ngram should always be substituted with correct + DEFINITE_AMBIG, // add correct ngram to the classifier results (1-1) + SIMILAR_AMBIG, // use pairwise classifier for ocred/correct pair (1-1) + CASE_AMBIG, // this is a case ambiguity (1-1) + + AMBIG_TYPE_COUNT // number of enum entries +}; + +// A collection of utility functions for arrays of UNICHAR_IDs that are +// terminated by INVALID_UNICHAR_ID. +class UnicharIdArrayUtils { + public: + // Compares two arrays of unichar ids. Returns -1 if the length of array1 is + // less than length of array2, if any array1[i] is less than array2[i]. + // Returns 0 if the arrays are equal, 1 otherwise. + // The function assumes that the arrays are terminated by INVALID_UNICHAR_ID. + static inline int compare(const UNICHAR_ID *ptr1, const UNICHAR_ID *ptr2) { + for (;;) { + const UNICHAR_ID val1 = *ptr1++; + const UNICHAR_ID val2 = *ptr2++; + if (val1 != val2) { + if (val1 == INVALID_UNICHAR_ID) return -1; + if (val2 == INVALID_UNICHAR_ID) return 1; + if (val1 < val2) return -1; + return 1; + } + if (val1 == INVALID_UNICHAR_ID) return 0; + } + } + + // Look uid in the vector of uids. If found, the index of the matched + // element is returned. Otherwise, it returns -1. + static inline int find_in(const UnicharIdVector& uid_vec, + const UNICHAR_ID uid) { + for (int i = 0; i < uid_vec.size(); ++i) + if (uid_vec[i] == uid) return i; + return -1; + } + + // Copies UNICHAR_IDs from dst to src. Returns the number of ids copied. + // The function assumes that the arrays are terminated by INVALID_UNICHAR_ID + // and that dst has enough space for all the elements from src. + static inline int copy(const UNICHAR_ID src[], UNICHAR_ID dst[]) { + int i = 0; + do { + dst[i] = src[i]; + } while (dst[i++] != INVALID_UNICHAR_ID); + return i - 1; + } + + // Prints unichars corresponding to the unichar_ids in the given array. + // The function assumes that array is terminated by INVALID_UNICHAR_ID. + static inline void print(const UNICHAR_ID array[], + const UNICHARSET &unicharset) { + const UNICHAR_ID *ptr = array; + if (*ptr == INVALID_UNICHAR_ID) tprintf("[Empty]"); + while (*ptr != INVALID_UNICHAR_ID) { + tprintf("%s ", unicharset.id_to_unichar(*ptr++)); + } + tprintf("( "); + ptr = array; + while (*ptr != INVALID_UNICHAR_ID) tprintf("%d ", *ptr++); + tprintf(")\n"); + } +}; + +// AMBIG_SPEC_LIST stores a list of dangerous ambigs that +// start with the same unichar (e.g. r->t rn->m rr1->m). +class AmbigSpec : public ELIST_LINK { + public: + AmbigSpec(); + ~AmbigSpec() {} + + // Comparator function for sorting AmbigSpec_LISTs. The lists will + // be sorted by their wrong_ngram arrays. Example of wrong_ngram vectors + // in a a sorted AmbigSpec_LIST: [9 1 3], [9 3 4], [9 8], [9, 8 1]. + static int compare_ambig_specs(const void *spec1, const void *spec2) { + const AmbigSpec *s1 = + *reinterpret_cast(spec1); + const AmbigSpec *s2 = + *reinterpret_cast(spec2); + int result = UnicharIdArrayUtils::compare(s1->wrong_ngram, s2->wrong_ngram); + if (result != 0) return result; + return UnicharIdArrayUtils::compare(s1->correct_fragments, + s2->correct_fragments); + } + + UNICHAR_ID wrong_ngram[MAX_AMBIG_SIZE + 1]; + UNICHAR_ID correct_fragments[MAX_AMBIG_SIZE + 1]; + UNICHAR_ID correct_ngram_id; + AmbigType type; + int wrong_ngram_size; +}; +ELISTIZEH(AmbigSpec); + +// AMBIG_TABLE[i] stores a set of ambiguities whose +// wrong ngram starts with unichar id i. +typedef GenericVector UnicharAmbigsVector; + +class UnicharAmbigs { + public: + UnicharAmbigs() {} + ~UnicharAmbigs() { + replace_ambigs_.delete_data_pointers(); + dang_ambigs_.delete_data_pointers(); + one_to_one_definite_ambigs_.delete_data_pointers(); + } + + const UnicharAmbigsVector &dang_ambigs() const { return dang_ambigs_; } + const UnicharAmbigsVector &replace_ambigs() const { return replace_ambigs_; } + + // Initializes the ambigs by adding a NULL pointer to each table. + void InitUnicharAmbigs(const UNICHARSET& unicharset, + bool use_ambigs_for_adaption); + + // Loads the universal ambigs that are useful for any language. + void LoadUniversal(const UNICHARSET& encoder_set, UNICHARSET* unicharset); + + // Fills in two ambiguity tables (replaceable and dangerous) with information + // read from the ambigs file. An ambiguity table is an array of lists. + // The array is indexed by a class id. Each entry in the table provides + // a list of potential ambiguities which can start with the corresponding + // character. For example the ambiguity "rn -> m", would be located in the + // table at index of unicharset.unichar_to_id('r'). + // In 1-1 ambiguities (e.g. s -> S, 1 -> I) are recorded in + // one_to_one_definite_ambigs_. This vector is also indexed by the class id + // of the wrong part of the ambiguity and each entry contains a vector of + // unichar ids that are ambiguous to it. + // encoder_set is used to encode the ambiguity strings, undisturbed by new + // unichar_ids that may be created by adding the ambigs. + void LoadUnicharAmbigs(const UNICHARSET& encoder_set, + TFile *ambigs_file, int debug_level, + bool use_ambigs_for_adaption, UNICHARSET *unicharset); + + // Returns definite 1-1 ambigs for the given unichar id. + inline const UnicharIdVector *OneToOneDefiniteAmbigs( + UNICHAR_ID unichar_id) const { + if (one_to_one_definite_ambigs_.empty()) return NULL; + return one_to_one_definite_ambigs_[unichar_id]; + } + + // Returns a pointer to the vector with all unichar ids that appear in the + // 'correct' part of the ambiguity pair when the given unichar id appears + // in the 'wrong' part of the ambiguity. E.g. if DangAmbigs file consist of + // m->rn,rn->m,m->iii, UnicharAmbigsForAdaption() called with unichar id of + // m will return a pointer to a vector with unichar ids of r,n,i. + inline const UnicharIdVector *AmbigsForAdaption( + UNICHAR_ID unichar_id) const { + if (ambigs_for_adaption_.empty()) return NULL; + return ambigs_for_adaption_[unichar_id]; + } + + // Similar to the above, but return the vector of unichar ids for which + // the given unichar_id is an ambiguity (appears in the 'wrong' part of + // some ambiguity pair). + inline const UnicharIdVector *ReverseAmbigsForAdaption( + UNICHAR_ID unichar_id) const { + if (reverse_ambigs_for_adaption_.empty()) return NULL; + return reverse_ambigs_for_adaption_[unichar_id]; + } + + private: + bool ParseAmbiguityLine(int line_num, int version, int debug_level, + const UNICHARSET &unicharset, char *buffer, + int *test_ambig_part_size, + UNICHAR_ID *test_unichar_ids, + int *replacement_ambig_part_size, + char *replacement_string, int *type); + bool InsertIntoTable(UnicharAmbigsVector &table, + int test_ambig_part_size, UNICHAR_ID *test_unichar_ids, + int replacement_ambig_part_size, + const char *replacement_string, int type, + AmbigSpec *ambig_spec, UNICHARSET *unicharset); + + UnicharAmbigsVector dang_ambigs_; + UnicharAmbigsVector replace_ambigs_; + GenericVector one_to_one_definite_ambigs_; + GenericVector ambigs_for_adaption_; + GenericVector reverse_ambigs_for_adaption_; +}; + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_AMBIGS_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.cpp new file mode 100644 index 0000000..69b88c2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.cpp @@ -0,0 +1,45 @@ +/********************************************************************** + * File: basedir.c (Formerly getpath.c) + * Description: Find the directory location of the current executable using PATH. + * Author: Ray Smith + * Created: Mon Jul 09 09:06:39 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "basedir.h" + +#include + +// Assuming that code_path is the name of some file in a desired directory, +// returns the given code_path stripped back to the last slash, leaving +// the last slash in place. If there is no slash, returns ./ assuming that +// the input was the name of something in the current directory. +// Useful for getting to the directory of argv[0], but does not search +// any paths. +TESS_API void truncate_path(const char *code_path, STRING* trunc_path) { + int trunc_index = -1; + if (code_path != NULL) { + const char* last_slash = strrchr(code_path, '/'); + if (last_slash != NULL && last_slash + 1 - code_path > trunc_index) + trunc_index = last_slash + 1 - code_path; + last_slash = strrchr(code_path, '\\'); + if (last_slash != NULL && last_slash + 1 - code_path > trunc_index) + trunc_index = last_slash + 1 - code_path; + } + *trunc_path = code_path; + if (trunc_index >= 0) + trunc_path->truncate_at(trunc_index); + else + *trunc_path = "./"; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.h b/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.h new file mode 100644 index 0000000..60d56ba --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/basedir.h @@ -0,0 +1,31 @@ +/********************************************************************** + * File: basedir.h (Formerly getpath.h) + * Description: Header file for getpath.c. Provides relocatability of data. + * Author: Ray Smith + * Created: Mon Jul 09 09:13:03 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BASEDIR_H +#define BASEDIR_H + +#include "platform.h" +#include "strngs.h" + +// Returns the given code_path truncated to the last slash. +// Useful for getting to the directory of argv[0], but does not search +// any paths. +TESS_API void truncate_path(const char *code_path, STRING* trunc_path); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.cpp new file mode 100644 index 0000000..38c896f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.cpp @@ -0,0 +1,29 @@ +/********************************************************************** + * 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 "bits16.h" + +/********************************************************************** + * Constructor. Something to get it past the compiler as almost all inlined. + * + **********************************************************************/ +BITS16::BITS16( // constructor + uinT16 init) { // initial val + val = init; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.h b/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.h new file mode 100644 index 0000000..6bbec4c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/bits16.h @@ -0,0 +1,61 @@ +/********************************************************************** + * File: bits16.h (Formerly bits8.h) + * Description: Code for 8 bit field class. + * Author: Phil Cheatle + * Created: Thu Oct 17 10:10:05 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BITS16_H +#define BITS16_H + +#include "host.h" + +class DLLSYM BITS16 +{ + public: + uinT16 val; + + BITS16() { + val = 0; + } // constructor + + BITS16( // constructor + uinT16 init); // initial val + + void turn_on_bit( // flip specified bit + uinT8 bit_num) { // bit to flip 0..7 + val = val | 01 << bit_num; + } + + void turn_off_bit( // flip specified bit + uinT8 bit_num) { // bit to flip 0..7 + val = val & ~(01 << bit_num); + } + + void set_bit( // flip specified bit + uinT8 bit_num, // bit to flip 0..7 + BOOL8 value) { // value to flip to + if (value) + val = val | 01 << bit_num; + else + val = val & ~(01 << bit_num); + } + + BOOL8 bit( // access bit + uinT8 bit_num) const { // bit to access + return (val >> bit_num) & 01; + } +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.cpp new file mode 100644 index 0000000..3116686 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.cpp @@ -0,0 +1,269 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: bitvector.cpp +// Description: Class replacement for BITVECTOR. +// Author: Ray Smith +// Created: Mon Jan 10 17:45: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. +// +/////////////////////////////////////////////////////////////////////// + +#include "bitvector.h" +#include +#include "helpers.h" +#include "ndminx.h" + +namespace tesseract { + +// Fast lookup table to get the first least significant set bit in a byte. +// For zero, the table has 255, but since it is a special case, most code +// that uses this table will check for zero before looking up lsb_index_. +const uinT8 BitVector::lsb_index_[256] = { + 255, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +// Fast lookup table to get the residual bits after zeroing the first (lowest) +// set bit in a byte. +const uinT8 BitVector::lsb_eroded_[256] = { + 0, 0, 0, 0x2, 0, 0x4, 0x4, 0x6, + 0, 0x8, 0x8, 0x0a, 0x08, 0x0c, 0x0c, 0x0e, + 0, 0x10, 0x10, 0x12, 0x10, 0x14, 0x14, 0x16, + 0x10, 0x18, 0x18, 0x1a, 0x18, 0x1c, 0x1c, 0x1e, + 0, 0x20, 0x20, 0x22, 0x20, 0x24, 0x24, 0x26, + 0x20, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x2c, 0x2e, + 0x20, 0x30, 0x30, 0x32, 0x30, 0x34, 0x34, 0x36, + 0x30, 0x38, 0x38, 0x3a, 0x38, 0x3c, 0x3c, 0x3e, + 0, 0x40, 0x40, 0x42, 0x40, 0x44, 0x44, 0x46, + 0x40, 0x48, 0x48, 0x4a, 0x48, 0x4c, 0x4c, 0x4e, + 0x40, 0x50, 0x50, 0x52, 0x50, 0x54, 0x54, 0x56, + 0x50, 0x58, 0x58, 0x5a, 0x58, 0x5c, 0x5c, 0x5e, + 0x40, 0x60, 0x60, 0x62, 0x60, 0x64, 0x64, 0x66, + 0x60, 0x68, 0x68, 0x6a, 0x68, 0x6c, 0x6c, 0x6e, + 0x60, 0x70, 0x70, 0x72, 0x70, 0x74, 0x74, 0x76, + 0x70, 0x78, 0x78, 0x7a, 0x78, 0x7c, 0x7c, 0x7e, + 0, 0x80, 0x80, 0x82, 0x80, 0x84, 0x84, 0x86, + 0x80, 0x88, 0x88, 0x8a, 0x88, 0x8c, 0x8c, 0x8e, + 0x80, 0x90, 0x90, 0x92, 0x90, 0x94, 0x94, 0x96, + 0x90, 0x98, 0x98, 0x9a, 0x98, 0x9c, 0x9c, 0x9e, + 0x80, 0xa0, 0xa0, 0xa2, 0xa0, 0xa4, 0xa4, 0xa6, + 0xa0, 0xa8, 0xa8, 0xaa, 0xa8, 0xac, 0xac, 0xae, + 0xa0, 0xb0, 0xb0, 0xb2, 0xb0, 0xb4, 0xb4, 0xb6, + 0xb0, 0xb8, 0xb8, 0xba, 0xb8, 0xbc, 0xbc, 0xbe, + 0x80, 0xc0, 0xc0, 0xc2, 0xc0, 0xc4, 0xc4, 0xc6, + 0xc0, 0xc8, 0xc8, 0xca, 0xc8, 0xcc, 0xcc, 0xce, + 0xc0, 0xd0, 0xd0, 0xd2, 0xd0, 0xd4, 0xd4, 0xd6, + 0xd0, 0xd8, 0xd8, 0xda, 0xd8, 0xdc, 0xdc, 0xde, + 0xc0, 0xe0, 0xe0, 0xe2, 0xe0, 0xe4, 0xe4, 0xe6, + 0xe0, 0xe8, 0xe8, 0xea, 0xe8, 0xec, 0xec, 0xee, + 0xe0, 0xf0, 0xf0, 0xf2, 0xf0, 0xf4, 0xf4, 0xf6, + 0xf0, 0xf8, 0xf8, 0xfa, 0xf8, 0xfc, 0xfc, 0xfe +}; + +// Fast lookup table to give the number of set bits in a byte. +const int BitVector::hamming_table_[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + + +BitVector::BitVector() : bit_size_(0), array_(NULL) {} + +BitVector::BitVector(int length) : bit_size_(length) { + array_ = new uinT32[WordLength()]; + SetAllFalse(); +} + +BitVector::BitVector(const BitVector& src) : bit_size_(src.bit_size_) { + array_ = new uinT32[WordLength()]; + memcpy(array_, src.array_, ByteLength()); +} + +BitVector& BitVector::operator=(const BitVector& src) { + Alloc(src.bit_size_); + memcpy(array_, src.array_, ByteLength()); + return *this; +} + +BitVector::~BitVector() { + delete [] array_; +} + +// Initializes the array to length * false. +void BitVector::Init(int length) { + Alloc(length); + SetAllFalse(); +} + +// Writes to the given file. Returns false in case of error. +bool BitVector::Serialize(FILE* fp) const { + if (fwrite(&bit_size_, sizeof(bit_size_), 1, fp) != 1) return false; + int wordlen = WordLength(); + if (static_cast(fwrite(array_, sizeof(*array_), wordlen, fp)) != wordlen) + 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 BitVector::DeSerialize(bool swap, FILE* fp) { + uinT32 new_bit_size; + if (fread(&new_bit_size, sizeof(new_bit_size), 1, fp) != 1) return false; + if (swap) { + ReverseN(&new_bit_size, sizeof(new_bit_size)); + } + Alloc(new_bit_size); + int wordlen = WordLength(); + if (static_cast(fread(array_, sizeof(*array_), wordlen, fp)) != wordlen) + return false; + if (swap) { + for (int i = 0; i < wordlen; ++i) + ReverseN(&array_[i], sizeof(array_[i])); + } + return true; +} + +void BitVector::SetAllFalse() { + memset(array_, 0, ByteLength()); +} +void BitVector::SetAllTrue() { + memset(array_, ~0, ByteLength()); +} + +// Returns the index of the next set bit after the given index. +// Useful for quickly iterating through the set bits in a sparse vector. +int BitVector::NextSetBit(int prev_bit) const { + // Move on to the next bit. + int next_bit = prev_bit + 1; + if (next_bit >= bit_size_) return -1; + // Check the remains of the word containing the next_bit first. + int next_word = WordIndex(next_bit); + int bit_index = next_word * kBitFactor; + int word_end = bit_index + kBitFactor; + uinT32 word = array_[next_word]; + uinT8 byte = word & 0xff; + while (bit_index < word_end) { + if (bit_index + 8 > next_bit && byte != 0) { + while (bit_index + lsb_index_[byte] < next_bit && byte != 0) + byte = lsb_eroded_[byte]; + if (byte != 0) + return bit_index + lsb_index_[byte]; + } + word >>= 8; + bit_index += 8; + byte = word & 0xff; + } + // next_word didn't contain a 1, so find the next word with set bit. + ++next_word; + int wordlen = WordLength(); + while (next_word < wordlen && (word = array_[next_word]) == 0) { + ++next_word; + bit_index += kBitFactor; + } + if (bit_index >= bit_size_) return -1; + // Find the first non-zero byte within the word. + while ((word & 0xff) == 0) { + word >>= 8; + bit_index += 8; + } + return bit_index + lsb_index_[word & 0xff]; +} + +// Returns the number of set bits in the vector. +int BitVector::NumSetBits() const { + int wordlen = WordLength(); + int total_bits = 0; + for (int w = 0; w < wordlen; ++w) { + uinT32 word = array_[w]; + for (int i = 0; i < 4; ++i) { + total_bits += hamming_table_[word & 0xff]; + word >>= 8; + } + } + return total_bits; +} + +// Logical in-place operations on whole bit vectors. Tries to do something +// sensible if they aren't the same size, but they should be really. +void BitVector::operator|=(const BitVector& other) { + int length = MIN(WordLength(), other.WordLength()); + for (int w = 0; w < length; ++w) + array_[w] |= other.array_[w]; +} +void BitVector::operator&=(const BitVector& other) { + int length = MIN(WordLength(), other.WordLength()); + for (int w = 0; w < length; ++w) + array_[w] &= other.array_[w]; + for (int w = WordLength() - 1; w >= length; --w) + array_[w] = 0; +} +void BitVector::operator^=(const BitVector& other) { + int length = MIN(WordLength(), other.WordLength()); + for (int w = 0; w < length; ++w) + array_[w] ^= other.array_[w]; +} +// Set subtraction *this = v1 - v2. +void BitVector::SetSubtract(const BitVector& v1, const BitVector& v2) { + Alloc(v1.size()); + int length = MIN(v1.WordLength(), v2.WordLength()); + for (int w = 0; w < length; ++w) + array_[w] = v1.array_[w] ^ (v1.array_[w] & v2.array_[w]); + for (int w = WordLength() - 1; w >= length; --w) + array_[w] = v1.array_[w]; +} + +// Allocates memory for a vector of the given length. +// Reallocates if the array is a different size, larger or smaller. +void BitVector::Alloc(int length) { + int initial_wordlength = WordLength(); + bit_size_ = length; + int new_wordlength = WordLength(); + if (new_wordlength != initial_wordlength) { + delete [] array_; + array_ = new uinT32[new_wordlength]; + } +} + + +} // namespace tesseract. + + diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.h b/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.h new file mode 100644 index 0000000..5e74807 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/bitvector.h @@ -0,0 +1,143 @@ +// 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 +#include +#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: + // Fast lookup table to get the first least significant set bit in a byte. + // For zero, the table has 255, but since it is a special case, most code + // that uses this table will check for zero before looking up lsb_index_. + static const uinT8 lsb_index_[256]; + // Fast lookup table to get the residual bits after zeroing the least + // significant set bit in a byte. + static const uinT8 lsb_eroded_[256]; + // Fast lookup table to give the number of set bits in a byte. + static const int hamming_table_[256]; + + 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; + } + + // Returns the index of the next set bit after the given index. + // Useful for quickly iterating through the set bits in a sparse vector. + int NextSetBit(int prev_bit) const; + + // Returns the number of set bits in the vector. + int NumSetBits() const; + + // Logical in-place operations on whole bit vectors. Tries to do something + // sensible if they aren't the same size, but they should be really. + void operator|=(const BitVector& other); + void operator&=(const BitVector& other); + void operator^=(const BitVector& other); + // Set subtraction *this = v1 - v2. + void SetSubtract(const BitVector& v1, const BitVector& v2); + + 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. + inT32 bit_size_; + // Array of words used to pack the bits. + // Bits are stored little-endian by uinT32 word, ie by word first and then + // starting with the least significant bit in each word. + uinT32* array_; + // Number of bits in an array_ element. + static const int kBitFactor = sizeof(uinT32) * 8; +}; + +} // namespace tesseract. + +#endif // TESSERACT_CCUTIL_BITVECTOR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.cpp new file mode 100644 index 0000000..ec8e45b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.cpp @@ -0,0 +1,67 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// Author: scharron@google.com (Samuel Charron) +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "ccutil.h" + +#if defined(_WIN32) +#define WINDLLNAME "HG_OCR.lib" +#else +#define WINDLLNAME "HG_OCR.a" +#endif + +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 diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.h b/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.h new file mode 100644 index 0000000..faba0ae --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ccutil.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////// +// File: ccutil.h +// Description: ccutil 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_CCUTIL_CCUTIL_H__ +#define TESSERACT_CCUTIL_CCUTIL_H__ + +#include "ambigs.h" +#include "errcode.h" +#include "strngs.h" +#include "tessdatamanager.h" +#include "params.h" +#include "unicharset.h" + +#ifndef _WIN32 +#include +#include +#endif + +namespace tesseract { + +class CCUtilMutex { + public: + CCUtilMutex(); + + void Lock(); + + void Unlock(); + private: +#ifdef _WIN32 + HANDLE mutex_; +#else + pthread_mutex_t mutex_; +#endif +}; + + +class TESS_API CCUtil { + public: + CCUtil(); + virtual ~CCUtil(); + + public: + // Read the arguments and set up the data path. + void main_setup( + const char *argv0, // program name + const char *basename // name of image + ); + ParamsVectors *params() { return ¶ms_; } + + STRING datadir; // dir for data files + STRING imagebasename; // name of image + STRING lang; + STRING language_data_path_prefix; + TessdataManager tessdata_manager; + UNICHARSET unicharset; + UnicharAmbigs unichar_ambigs; + STRING imagefile; // image file name + STRING directory; // main directory + + private: + ParamsVectors params_; + + public: + // Member parameters. + // These have to be declared and initialized after params_ member, since + // params_ should be initialized before parameters are added to it. + STRING_VAR_H(m_data_sub_dir, "tessdata/", "Directory for data files"); + #ifdef _WIN32 + STRING_VAR_H(tessedit_module_name, WINDLLNAME, + "Module colocated with tessdata dir"); + #endif + INT_VAR_H(ambigs_debug_level, 0, "Debug level for unichar ambiguities"); + BOOL_VAR_H(use_definite_ambigs_for_classifier, 0, + "Use definite ambiguities when running character classifier"); + BOOL_VAR_H(use_ambigs_for_adaption, 0, + "Use ambigs for deciding whether to adapt to a character"); +}; + +extern CCUtilMutex tprintfMutex; // should remain global +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_CCUTIL_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/clst.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/clst.cpp new file mode 100644 index 0000000..52caadf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/clst.cpp @@ -0,0 +1,509 @@ +/********************************************************************** + * File: clst.c (Formerly clist.c) + * Description: CONS cell list handling code which is not in the include file. + * Author: Phil Cheatle + * Created: Mon Jan 28 08:33:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include "clst.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: CLIST + * ================================ + **********************************************************************/ + +/*********************************************************************** + * CLIST::internal_deep_clear + * + * Used by the "deep_clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each data element of the list, regardless of its class. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +CLIST::internal_deep_clear ( //destroy all links +void (*zapper) (void *)) { //ptr to zapper functn + CLIST_LINK *ptr; + CLIST_LINK *next; + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper (ptr->data); + delete(ptr); + ptr = next; + } + } +} + +/*********************************************************************** + * CLIST::shallow_clear + * + * Used by the destructor and the "shallow_clear" member function of derived + * list classes to destroy the list. + * The data elements are NOT destroyed. + * + **********************************************************************/ + +void CLIST::shallow_clear() { //destroy all links + CLIST_LINK *ptr; + CLIST_LINK *next; + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + delete(ptr); + ptr = next; + } + } +} + +/*********************************************************************** + * CLIST::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void CLIST::assign_to_sublist( //to this list + CLIST_ITERATOR *start_it, //from list start + CLIST_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + if (!empty ()) + LIST_NOT_EMPTY.error ("CLIST.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + +/*********************************************************************** + * CLIST::length + * + * Return count of elements on list + **********************************************************************/ + +inT32 CLIST::length() const { //count elements + CLIST_ITERATOR it(const_cast(this)); + inT32 count = 0; + + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) + count++; + return count; +} + +/*********************************************************************** + * CLIST::sort + * + * Sort elements on list + **********************************************************************/ + +void +CLIST::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + CLIST_ITERATOR it(this); + inT32 count; + void **base; //ptr array to sort + void **current; + inT32 i; + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (void **) malloc (count * sizeof (void *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + +// Assuming list has been sorted already, insert new_data to +// keep the list sorted according to the same comparison function. +// Comparison function is the same as used by sort, i.e. uses double +// indirection. Time is O(1) to add to beginning or end. +// Time is linear to add pre-sorted items to an empty list. +// If unique, then don't add duplicate entries. +// Returns true if the element was added to the list. +bool CLIST::add_sorted(int comparator(const void*, const void*), + bool unique, void* new_data) { + // Check for adding at the end. + if (last == NULL || comparator(&last->data, &new_data) < 0) { + CLIST_LINK* new_element = new CLIST_LINK; + new_element->data = new_data; + if (last == NULL) { + new_element->next = new_element; + } else { + new_element->next = last->next; + last->next = new_element; + } + last = new_element; + return true; + } else if (!unique || last->data != new_data) { + // Need to use an iterator. + CLIST_ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + void* data = it.data(); + if (data == new_data && unique) + return false; + if (comparator(&data, &new_data) > 0) + break; + } + if (it.cycled_list()) + it.add_to_end(new_data); + else + it.add_before_then_move(new_data); + return true; + } + return false; +} + +// Assuming that the minuend and subtrahend are already sorted with +// the same comparison function, shallow clears this and then copies +// the set difference minuend - subtrahend to this, being the elements +// of minuend that do not compare equal to anything in subtrahend. +// If unique is true, any duplicates in minuend are also eliminated. +void CLIST::set_subtract(int comparator(const void*, const void*), + bool unique, + CLIST* minuend, CLIST* subtrahend) { + shallow_clear(); + CLIST_ITERATOR m_it(minuend); + CLIST_ITERATOR s_it(subtrahend); + // Since both lists are sorted, finding the subtras that are not + // minus is a case of a parallel iteration. + for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { + void* minu = m_it.data(); + void* subtra = NULL; + if (!s_it.empty()) { + subtra = s_it.data(); + while (!s_it.at_last() && + comparator(&subtra, &minu) < 0) { + s_it.forward(); + subtra = s_it.data(); + } + } + if (subtra == NULL || comparator(&subtra, &minu) != 0) + add_sorted(comparator, unique, minu); + } +} + + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: CLIST_ITERATOR + * ========================================= + **********************************************************************/ + +/*********************************************************************** + * CLIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +void *CLIST_ITERATOR::forward() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + // In case next is deleted by another iterator, get next from current. + current = current->next; + } else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + current = next; + } + next = current->next; + + #ifndef NDEBUG + if (!current) + NULL_DATA.error ("CLIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("CLIST_ITERATOR::forward", ABORT, + "This is: %p Current is: %p", this, current); + #endif + return current->data; +} + +/*********************************************************************** + * CLIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +void *CLIST_ITERATOR::data_relative( //get data + or - ... + inT8 offset) { //offset from current + CLIST_LINK *ptr; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + if (offset < -1) + BAD_PARAMETER.error ("CLIST_ITERATOR::data_relative", ABORT, + "offset < -l"); + #endif + + if (offset == -1) + ptr = prev; + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifndef NDEBUG + if (!ptr) + NULL_DATA.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr->data; +} + +/*********************************************************************** + * CLIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +void *CLIST_ITERATOR::move_to_last() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::move_to_last", ABORT, NULL); + #endif + + while (current != list->last) + forward(); + + if (current == NULL) + return NULL; + else + return current->data; +} + +/*********************************************************************** + * CLIST_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void CLIST_ITERATOR::exchange( //positions of 2 links + CLIST_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + CLIST_LINK *old_current; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("CLIST_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("CLIST_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + +/*********************************************************************** + * CLIST_ITERATOR::extract_sublist() + * + * This is a private member, used only by CLIST::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +CLIST_LINK *CLIST_ITERATOR::extract_sublist( //from this current + CLIST_ITERATOR *other_it) { //to other current + CLIST_ITERATOR temp_it = *this; + CLIST_LINK *end_of_new_list; + + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + #ifndef NDEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + + if (!other_it) + BAD_PARAMETER.error ("CLIST_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("CLIST_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list()) // can't find end pt + BAD_SUBLIST.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/clst.h b/hgdriver/3rdparty/hgOCR/include/ccutil/clst.h new file mode 100644 index 0000000..f93d75a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/clst.h @@ -0,0 +1,931 @@ +/********************************************************************** + * File: clst.h (Formerly clist.h) + * Description: CONS cell list module include file. + * Author: Phil Cheatle + * Created: Mon Jan 28 08:33:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CLST_H +#define CLST_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class CLIST_ITERATOR; + +/********************************************************************** + * CLASS - CLIST_LINK + * + * Generic link class for singly linked CONS cell lists + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the CLIST destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM CLIST_LINK +{ + friend class CLIST_ITERATOR; + friend class CLIST; + + CLIST_LINK *next; + void *data; + + public: + CLIST_LINK() { //constructor + data = next = NULL; + } + + CLIST_LINK( // copy constructor + const CLIST_LINK &) { // don't copy link + data = next = NULL; + } + + void operator=( // don't copy links + const CLIST_LINK &) { + data = next = NULL; + } +}; + +/********************************************************************** + * CLASS - CLIST + * + * Generic list class for singly linked CONS cell lists + **********************************************************************/ + +class DLLSYM CLIST +{ + friend class CLIST_ITERATOR; + + CLIST_LINK *last; //End of list + //(Points to head) + CLIST_LINK *First() { // return first + return last != NULL ? last->next : NULL; + } + + public: + CLIST() { //constructor + last = NULL; + } + + ~CLIST () { //destructor + shallow_clear(); + } + + void internal_deep_clear ( //destroy all links + void (*zapper) (void *)); //ptr to zapper functn + + void shallow_clear(); // clear list but don't + // delete data elements + + bool empty() const { //is list empty? + return !last; + } + + bool singleton() const { + return last != NULL ? (last == last->next) : false; + } + + void shallow_copy( //dangerous!! + CLIST *from_list) { //beware destructors!! + last = from_list->last; + } + + void assign_to_sublist( //to this list + CLIST_ITERATOR *start_it, //from list start + CLIST_ITERATOR *end_it); //from list end + + inT32 length() const; //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + // Assuming list has been sorted already, insert new_data to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + // If unique, then don't add duplicate entries. + // Returns true if the element was added to the list. + bool add_sorted(int comparator(const void*, const void*), + bool unique, void* new_data); + + // Assuming that the minuend and subtrahend are already sorted with + // the same comparison function, shallow clears this and then copies + // the set difference minuend - subtrahend to this, being the elements + // of minuend that do not compare equal to anything in subtrahend. + // If unique is true, any duplicates in minuend are also eliminated. + void set_subtract(int comparator(const void*, const void*), bool unique, + CLIST* minuend, CLIST* subtrahend); + +}; + +/*********************************************************************** + * CLASS - CLIST_ITERATOR + * + * Generic iterator class for singly linked lists with embedded + *links + **********************************************************************/ + +class DLLSYM CLIST_ITERATOR +{ + friend void CLIST::assign_to_sublist(CLIST_ITERATOR *, CLIST_ITERATOR *); + + CLIST *list; //List being iterated + CLIST_LINK *prev; //prev element + CLIST_LINK *current; //current element + CLIST_LINK *next; //next element + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + CLIST_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 started_cycling; //Have we moved off + //the start? + + CLIST_LINK *extract_sublist( //from this current... + CLIST_ITERATOR *other_it); //to other current + + public: + CLIST_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + CLIST_ITERATOR( //constructor + CLIST *list_to_iterate); + + void set_to_list( //change list + CLIST *list_to_iterate); + + void add_after_then_move( //add after current & + void *new_data); //move to new + + void add_after_stay_put( //add after current & + void *new_data); //stay at current + + void add_before_then_move( //add before current & + void *new_data); //move to new + + void add_before_stay_put( //add before current & + void *new_data); //stay at current + + void add_list_after( //add a list & + CLIST *list_to_add); //stay at current + + void add_list_before( //add a list & + CLIST *list_to_add); //move to it 1st item + + void *data() { //get current data + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::data", ABORT, NULL); + if (!current) + NULL_DATA.error ("CLIST_ITERATOR::data", ABORT, NULL); + #endif + return current->data; + } + + void *data_relative( //get data + or - ... + inT8 offset); //offset from current + + void *forward(); //move to next element + + void *extract(); //remove from list + + void *move_to_first(); //go to start of list + + void *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + BOOL8 empty() { //is list empty? + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::empty", ABORT, NULL); + #endif + return list->empty (); + } + + BOOL8 current_extracted() { //current extracted? + return !current; + } + + BOOL8 at_first(); //Current is first? + + BOOL8 at_last(); //Current is last? + + BOOL8 cycled_list(); //Completed a cycle? + + void add_to_end( // add at end & + void *new_data); // don't move + + void exchange( //positions of 2 links + CLIST_ITERATOR *other_it); //other iterator + + inT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * CLIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void CLIST_ITERATOR::set_to_list( //change list + CLIST *list_to_iterate) { + #ifndef NDEBUG + if (!list_to_iterate) + BAD_PARAMETER.error ("CLIST_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); + #endif + + list = list_to_iterate; + prev = list->last; + current = list->First (); + next = current != NULL ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + +/*********************************************************************** + * CLIST_ITERATOR::CLIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline CLIST_ITERATOR::CLIST_ITERATOR(CLIST *list_to_iterate) { + set_to_list(list_to_iterate); +} + +/*********************************************************************** + * CLIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_after_then_move( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_then_move", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + +/*********************************************************************** + * CLIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_after_stay_put( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + +/*********************************************************************** + * CLIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_before_then_move( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_then_move", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + next = current; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + +/*********************************************************************** + * CLIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_before_stay_put( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + +/*********************************************************************** + * CLIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_list_after(CLIST *list_to_add) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First (); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First (); + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First (); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + +/*********************************************************************** + * CLIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_list_before(CLIST *list_to_add) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First (); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First (); + if (current) { //not extracted + list_to_add->last->next = current; + } + else { //current extracted + list_to_add->last->next = next; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + +/*********************************************************************** + * CLIST_ITERATOR::extract + * + * Do extraction by removing current from the list, deleting the cons cell + * and returning the data to the caller, but NOT updating the iterator. (So + * that any calling loop can do this.) The iterator's current points to + * NULL. If the data is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline void *CLIST_ITERATOR::extract() { + void *extracted_data; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error ("CLIST_ITERATOR::extract", + ABORT, NULL); + #endif + + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = NULL; + } else { + prev->next = next; //remove from list + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } else { + ex_current_was_last = FALSE; + } + } + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + extracted_data = current->data; + delete(current); //destroy CONS cell + current = NULL; + return extracted_data; +} + +/*********************************************************************** + * CLIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline void *CLIST_ITERATOR::move_to_first() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::move_to_first", ABORT, NULL); + #endif + + current = list->First (); + prev = list->last; + next = current != NULL ? current->next : NULL; + return current != NULL ? current->data : NULL; +} + +/*********************************************************************** + * CLIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void CLIST_ITERATOR::mark_cycle_pt() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + #endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + +/*********************************************************************** + * CLIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::at_first() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::at_first", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + +/*********************************************************************** + * CLIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::at_last() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::at_last", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + +/*********************************************************************** + * CLIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::cycled_list() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::cycled_list", ABORT, NULL); + #endif + + return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); + +} + +/*********************************************************************** + * CLIST_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline inT32 CLIST_ITERATOR::length() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::length", ABORT, NULL); + #endif + + return list->length (); +} + +/*********************************************************************** + * CLIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +CLIST_ITERATOR::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::sort", ABORT, NULL); + #endif + + list->sort (comparator); + move_to_first(); +} + +/*********************************************************************** + * CLIST_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void CLIST_ITERATOR::add_to_end( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_to_end", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_to_end", ABORT, + "new_data is NULL"); + #endif + + if (this->at_last ()) { + this->add_after_stay_put (new_data); + } + else { + if (this->at_first ()) { + this->add_before_stay_put (new_data); + list->last = prev; + } + else { //Iteratr is elsewhere + new_element = new CLIST_LINK; + new_element->data = new_data; + + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +/*********************************************************************** + CLISTIZE( CLASSNAME ) MACRO DEFINITION + ====================================== + +CLASSNAME is assumed to be the name of a class to be used in a CONS list + +NOTE: Because we don't use virtual functions in the list code, the list code +will NOT work correctly for classes derived from this. + +The macro generates: + - An element deletion function: CLASSNAME##_c1_zapper + - An element copier function: + CLASSNAME##_c1_copier + - A CLIST subclass: CLASSNAME##_CLIST + - A CLIST_ITERATOR subclass: + CLASSNAME##_C_IT + +NOTE: Generated names do NOT clash with those generated by ELISTIZE, +ELIST2ISE and CLIST2IZE + +Two macros are provided: CLISTIZE and CLISTIZEH +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +***********************************************************************/ + +/*********************************************************************** + CLISTIZEH( CLASSNAME ) MACRO + +CLISTIZEH is a concatenation of 3 fragments CLISTIZEH_A, CLISTIZEH_B and +CLISTIZEH_C. +***********************************************************************/ + +#define CLISTIZEH_A(CLASSNAME) \ + \ + extern DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ + void *link); /*link to delete*/ \ + \ + extern DLLSYM void \ + *CLASSNAME##_c1_copier( /*deep copy a link*/ \ + void *old_element); /*source link */ + +#define CLISTIZEH_B(CLASSNAME) \ + \ + /*********************************************************************** \ + * CLASS - \ + *CLASSNAME##_CLIST \ + * \ + * List class for class \ + *CLASSNAME \ + * \ + **********************************************************************/ \ + \ + class DLLSYM CLASSNAME##_CLIST : public CLIST { \ + public: \ + CLASSNAME##_CLIST() : CLIST() {} \ + /* constructor */ \ + \ + CLASSNAME##_CLIST( /* don't construct */ \ + const CLASSNAME##_CLIST &) /*by initial assign*/ \ + { \ + DONT_CONSTRUCT_LIST_BY_COPY.error(QUOTE_IT(CLASSNAME##_CLIST), ABORT, \ + NULL); \ + } \ + \ + void deep_clear() /* delete elements */ \ + { \ + CLIST::internal_deep_clear(&CLASSNAME##_c1_zapper); \ + } \ + \ + void operator=(/* prevent assign */ \ + const CLASSNAME##_CLIST &) { \ + DONT_ASSIGN_LISTS.error(QUOTE_IT(CLASSNAME##_CLIST), ABORT, NULL); \ + } + +#define CLISTIZEH_C(CLASSNAME) \ + } \ + ; \ + \ + /*********************************************************************** \ + * CLASS - CLASSNAME##_C_IT \ + * \ + * Iterator class for class CLASSNAME##_CLIST \ + * \ + * Note: We don't need to coerce pointers to member functions input \ + * parameters as these are automatically converted to the type of the base \ + * type. ("A ptr to a class may be converted to a pointer to a public base \ + * class of that class") \ + **********************************************************************/ \ + \ + class DLLSYM CLASSNAME##_C_IT : public CLIST_ITERATOR { \ + public: \ + CLASSNAME##_C_IT() : CLIST_ITERATOR() {} \ + \ + CLASSNAME##_C_IT(CLASSNAME##_CLIST *list) : CLIST_ITERATOR(list) {} \ + \ + CLASSNAME *data() { return (CLASSNAME *)CLIST_ITERATOR::data(); } \ + \ + CLASSNAME *data_relative(inT8 offset) { \ + return (CLASSNAME *)CLIST_ITERATOR::data_relative(offset); \ + } \ + \ + CLASSNAME *forward() { return (CLASSNAME *)CLIST_ITERATOR::forward(); } \ + \ + CLASSNAME *extract() { return (CLASSNAME *)CLIST_ITERATOR::extract(); } \ + \ + CLASSNAME *move_to_first() { \ + return (CLASSNAME *)CLIST_ITERATOR::move_to_first(); \ + } \ + \ + CLASSNAME *move_to_last() { \ + return (CLASSNAME *)CLIST_ITERATOR::move_to_last(); \ + } \ + }; + +#define CLISTIZEH(CLASSNAME) \ + \ + CLISTIZEH_A(CLASSNAME) \ + \ + CLISTIZEH_B(CLASSNAME) \ + \ + CLISTIZEH_C(CLASSNAME) + +/*********************************************************************** + CLISTIZE( CLASSNAME ) MACRO +***********************************************************************/ + +#define CLISTIZE(CLASSNAME) \ + \ + /*********************************************************************** \ + * CLASSNAME##_c1_zapper \ + * \ + * A function which can delete a CLASSNAME element. This is passed to the \ + * generic deep_clear list member function so that when a list is cleared \ + *the \ + * elements on the list are properly destroyed from the base class, even \ + * though we don't use a virtual destructor function. \ + **********************************************************************/ \ + \ + DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ + void *link) /*link to delete*/ \ + { \ + delete (CLASSNAME *)link; \ + } + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/doubleptr.h b/hgdriver/3rdparty/hgOCR/include/ccutil/doubleptr.h new file mode 100644 index 0000000..4cef16b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/doubleptr.h @@ -0,0 +1,93 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: doubleptr.h +// Description: Double-ended pointer that keeps pointing correctly even +// when reallocated or copied. +// Author: Ray Smith +// Created: Wed Mar 14 12:22:57 PDT 2012 +// +// (C) Copyright 2012, 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_DOUBLEPTR_H_ +#define TESSERACT_CCUTIL_DOUBLEPTR_H_ + +#include "errcode.h" + +namespace tesseract { + +// A smart pointer class that implements a double-ended pointer. Each end +// points to the other end. The copy constructor and operator= have MOVE +// semantics, meaning that the relationship with the other end moves to the +// destination of the copy, leaving the source unattached. +// For this reason both the copy constructor and the operator= take a non-const +// reference argument, and the const reference versions cannot be used. +// DoublePtr is useful to incorporate into structures that are part of a +// collection such as GenericVector or STL containers, where reallocs can +// relocate the members. DoublePtr is also useful in a GenericHeap, where it +// can correctly maintain the pointer to an element of the heap despite it +// getting moved around on the heap. +class DoublePtr { + public: + DoublePtr() : other_end_(NULL) {} + // Copy constructor steals the partner off src and is therefore a non + // const reference arg. + // Copying a const DoublePtr generates a compiler error. + DoublePtr(DoublePtr& src) { + other_end_ = src.other_end_; + if (other_end_ != NULL) { + other_end_->other_end_ = this; + src.other_end_ = NULL; + } + } + // Operator= steals the partner off src, and therefore needs src to be a non- + // const reference. + // Assigning from a const DoublePtr generates a compiler error. + void operator=(DoublePtr& src) { + Disconnect(); + other_end_ = src.other_end_; + if (other_end_ != NULL) { + other_end_->other_end_ = this; + src.other_end_ = NULL; + } + } + + // Connects this and other, discarding any existing connections. + void Connect(DoublePtr* other) { + other->Disconnect(); + Disconnect(); + other->other_end_ = this; + other_end_ = other; + } + // Disconnects this and other, making OtherEnd() return NULL for both. + void Disconnect() { + if (other_end_ != NULL) { + other_end_->other_end_ = NULL; + other_end_ = NULL; + } + } + // Returns the pointer to the other end of the double pointer. + DoublePtr* OtherEnd() const { + return other_end_; + } + + private: + // Pointer to the other end of the link. It is always true that either + // other_end_ == NULL or other_end_->other_end_ == this. + DoublePtr* other_end_; +}; + +} // namespace tesseract. + +#endif // THIRD_PARTY_TESSERACT_CCUTIL_DOUBLEPTR_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/elst.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/elst.cpp new file mode 100644 index 0000000..2d2c9ad --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/elst.cpp @@ -0,0 +1,457 @@ +/********************************************************************** + * File: elst.c (Formerly elist.c) + * Description: Embedded list handling code which is not in the include file. + * Author: Phil Cheatle + * Created: Fri Jan 04 13:55:49 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include "elst.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST + * ================================ + **********************************************************************/ + +/*********************************************************************** + * ELIST::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +ELIST::internal_clear ( //destroy all links +void (*zapper) (ELIST_LINK *)) { + //ptr to zapper functn + ELIST_LINK *ptr; + ELIST_LINK *next; + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper(ptr); + ptr = next; + } + } +} + +/*********************************************************************** + * ELIST::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void ELIST::assign_to_sublist( //to this list + ELIST_ITERATOR *start_it, //from list start + ELIST_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + if (!empty ()) + LIST_NOT_EMPTY.error ("ELIST.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + +/*********************************************************************** + * ELIST::length + * + * Return count of elements on list + **********************************************************************/ + +inT32 ELIST::length() const { // count elements + ELIST_ITERATOR it(const_cast(this)); + inT32 count = 0; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + +/*********************************************************************** + * ELIST::sort + * + * Sort elements on list + * NB If you don't like the const declarations in the comparator, coerce yours: + * ( int (*)(const void *, const void *) + **********************************************************************/ + +void +ELIST::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + ELIST_ITERATOR it(this); + inT32 count; + ELIST_LINK **base; //ptr array to sort + ELIST_LINK **current; + inT32 i; + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (ELIST_LINK **) malloc (count * sizeof (ELIST_LINK *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + +// Assuming list has been sorted already, insert new_link to +// keep the list sorted according to the same comparison function. +// Comparison function is the same as used by sort, i.e. uses double +// indirection. Time is O(1) to add to beginning or end. +// Time is linear to add pre-sorted items to an empty list. +// If unique is set to true and comparator() returns 0 (an entry with the +// same information as the one contained in new_link is already in the +// list) - new_link is not added to the list and the function returns the +// pointer to the identical entry that already exists in the list +// (otherwise the function returns new_link). +ELIST_LINK *ELIST::add_sorted_and_find( + int comparator(const void*, const void*), + bool unique, ELIST_LINK* new_link) { + // Check for adding at the end. + if (last == NULL || comparator(&last, &new_link) < 0) { + if (last == NULL) { + new_link->next = new_link; + } else { + new_link->next = last->next; + last->next = new_link; + } + last = new_link; + } else { + // Need to use an iterator. + ELIST_ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + ELIST_LINK* link = it.data(); + int compare = comparator(&link, &new_link); + if (compare > 0) { + break; + } else if (unique && compare == 0) { + return link; + } + } + if (it.cycled_list()) + it.add_to_end(new_link); + else + it.add_before_then_move(new_link); + } + return new_link; +} + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST_ITERATOR + * ========================================= + **********************************************************************/ + +/*********************************************************************** + * ELIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::forward() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + // In case next is deleted by another iterator, get next from current. + current = current->next; + } else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + current = next; + } + next = current->next; + + #ifndef NDEBUG + if (!current) + NULL_DATA.error ("ELIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST_ITERATOR::forward", ABORT, + "This is: %p Current is: %p", this, current); + #endif + return current; +} + +/*********************************************************************** + * ELIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::data_relative( //get data + or - ... + inT8 offset) { //offset from current + ELIST_LINK *ptr; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + if (offset < -1) + BAD_PARAMETER.error ("ELIST_ITERATOR::data_relative", ABORT, + "offset < -l"); + #endif + + if (offset == -1) + ptr = prev; + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifndef NDEBUG + if (!ptr) + NULL_DATA.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr; +} + +/*********************************************************************** + * ELIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::move_to_last() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::move_to_last", ABORT, NULL); + #endif + + while (current != list->last) + forward(); + + return current; +} + +/*********************************************************************** + * ELIST_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void ELIST_ITERATOR::exchange( //positions of 2 links + ELIST_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + ELIST_LINK *old_current; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("ELIST_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + +/*********************************************************************** + * ELIST_ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::extract_sublist( //from this current + ELIST_ITERATOR *other_it) { //to other current + #ifndef NDEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + #endif + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + + ELIST_ITERATOR temp_it = *this; + ELIST_LINK *end_of_new_list; + + #ifndef NDEBUG + if (!other_it) + BAD_PARAMETER.error ("ELIST_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("ELIST_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list()) // can't find end pt + BAD_SUBLIST.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/elst.h b/hgdriver/3rdparty/hgOCR/include/ccutil/elst.h new file mode 100644 index 0000000..64f4b52 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/elst.h @@ -0,0 +1,988 @@ +/********************************************************************** + * File: elst.h (Formerly elist.h) + * Description: Embedded list module include file. + * Author: Phil Cheatle + * Created: Mon Jan 07 08:35:34 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ELST_H +#define ELST_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class ELIST_ITERATOR; + +/********************************************************************** +This module implements list classes and iterators. +The following list types and iterators are provided: + + List type List Class Iterator Class Element Class + --------- ---------- -------------- ------------- + + Embedded list ELIST + ELIST_ITERATOR + ELIST_LINK + (Single linked) + + Embedded list ELIST2 + ELIST2_ITERATOR + ELIST2_LINK + (Double linked) + + Cons List CLIST + CLIST_ITERATOR + CLIST_LINK + (Single linked) + + Cons List CLIST2 + CLIST2_ITERATOR + CLIST2_LINK + (Double linked) + +An embedded list is where the list pointers are provided by a generic class. +Data types to be listed inherit from the generic class. Data is thus linked +in only ONE list at any one time. + +A cons list has a separate structure for a "cons cell". This contains the +list pointer(s) AND a pointer to the data structure held on the list. A +structure can be on many cons lists at the same time, and the structure does +not need to inherit from any generic class in order to be on the list. + +The implementation of lists is very careful about space and speed overheads. +This is why many embedded lists are provided. The same concerns mean that +in-line type coercion is done, rather than use virtual functions. This is +cumbersome in that each data type to be listed requires its own iterator and +list class - though macros can gererate these. It also prevents heterogeneous +lists. +**********************************************************************/ + +/********************************************************************** + * CLASS - ELIST_LINK + * + * Generic link class for singly linked lists with embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM ELIST_LINK +{ + friend class ELIST_ITERATOR; + friend class ELIST; + + ELIST_LINK *next; + +public: + ELIST_LINK() { + next = NULL; + } + //constructor + + ELIST_LINK(const ELIST_LINK &) { // don't copy link. + next = NULL; + } + + void operator=( // don't copy links + const ELIST_LINK &) { + next = NULL; + } +}; + +/********************************************************************** + * CLASS - ELIST + * + * Generic list class for singly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST +{ + friend class ELIST_ITERATOR; + + ELIST_LINK *last; //End of list + //(Points to head) + ELIST_LINK *First() { // return first + return last ? last->next : NULL; + } + +public: + ELIST() { //constructor + last = NULL; + } + + void internal_clear( //destroy all links + //ptr to zapper functn + void(*zapper) (ELIST_LINK *)); + + bool empty() const { //is list empty? + return !last; + } + + bool singleton() const { + return last ? (last == last->next) : false; + } + + void shallow_copy( //dangerous!! + ELIST *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy(ELIST_LINK * (*copier) (ELIST_LINK *), + const ELIST * list); //list being copied + + void assign_to_sublist( //to this list + ELIST_ITERATOR *start_it, //from list start + ELIST_ITERATOR *end_it); //from list end + + inT32 length() const; // # elements in list + + void sort( //sort elements + int comparator( //comparison routine + const void *, const void *)); + + // Assuming list has been sorted already, insert new_link to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + // If unique is set to true and comparator() returns 0 (an entry with the + // same information as the one contained in new_link is already in the + // list) - new_link is not added to the list and the function returns the + // pointer to the identical entry that already exists in the list + // (otherwise the function returns new_link). + ELIST_LINK *add_sorted_and_find(int comparator(const void*, const void*), + bool unique, ELIST_LINK* new_link); + + // Same as above, but returns true if the new entry was inserted, false + // if the identical entry already existed in the list. + bool add_sorted(int comparator(const void*, const void*), + bool unique, ELIST_LINK* new_link) { + return (add_sorted_and_find(comparator, unique, new_link) == new_link); + } + +}; + +/*********************************************************************** + * CLASS - ELIST_ITERATOR + * + * Generic iterator class for singly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST_ITERATOR +{ + friend void ELIST::assign_to_sublist(ELIST_ITERATOR *, ELIST_ITERATOR *); + + ELIST *list; //List being iterated + ELIST_LINK *prev; //prev element + ELIST_LINK *current; //current element + ELIST_LINK *next; //next element + bool ex_current_was_last; //current extracted + //was end of list + bool ex_current_was_cycle_pt; //current extracted + //was cycle point + ELIST_LINK *cycle_pt; //point we are cycling + //the list to. + bool started_cycling; //Have we moved off + //the start? + + ELIST_LINK *extract_sublist( //from this current... + ELIST_ITERATOR *other_it); //to other current + +public: + ELIST_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + explicit ELIST_ITERATOR(ELIST *list_to_iterate); + + void set_to_list( //change list + ELIST *list_to_iterate); + + void add_after_then_move( //add after current & + ELIST_LINK *new_link); //move to new + + void add_after_stay_put( //add after current & + ELIST_LINK *new_link); //stay at current + + void add_before_then_move( //add before current & + ELIST_LINK *new_link); //move to new + + void add_before_stay_put( //add before current & + ELIST_LINK *new_link); //stay at current + + void add_list_after( //add a list & + ELIST *list_to_add); //stay at current + + void add_list_before( //add a list & + ELIST *list_to_add); //move to it 1st item + + ELIST_LINK *data() { //get current data +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::data", ABORT, NULL); + if (!current) + NULL_DATA.error("ELIST_ITERATOR::data", ABORT, NULL); +#endif + return current; + } + + ELIST_LINK *data_relative( //get data + or - ... + inT8 offset); //offset from current + + ELIST_LINK *forward(); //move to next element + + ELIST_LINK *extract(); //remove from list + + ELIST_LINK *move_to_first(); //go to start of list + + ELIST_LINK *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + bool empty() { //is list empty? +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::empty", ABORT, NULL); +#endif + return list->empty(); + } + + bool current_extracted() { //current extracted? + return !current; + } + + bool at_first(); //Current is first? + + bool at_last(); //Current is last? + + bool cycled_list(); //Completed a cycle? + + void add_to_end( // add at end & + ELIST_LINK *new_link); // don't move + + void exchange( //positions of 2 links + ELIST_ITERATOR *other_it); //other iterator + + inT32 length(); //# elements in list + + void sort( //sort elements + int comparator( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * ELIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void ELIST_ITERATOR::set_to_list( //change list + ELIST *list_to_iterate) { +#ifndef NDEBUG + if (!list_to_iterate) + BAD_PARAMETER.error("ELIST_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); +#endif + + list = list_to_iterate; + prev = list->last; + current = list->First(); + next = current ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + + +/*********************************************************************** + * ELIST_ITERATOR::ELIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline ELIST_ITERATOR::ELIST_ITERATOR(ELIST *list_to_iterate) { + set_to_list(list_to_iterate); +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_after_then_move( // element to add + ELIST_LINK *new_element) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error("ELIST_ITERATOR::add_after_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); +#endif + + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_after_stay_put( // element to add + ELIST_LINK *new_element) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error("ELIST_ITERATOR::add_after_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); +#endif + + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_before_then_move( // element to add + ELIST_LINK *new_element) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error("ELIST_ITERATOR::add_before_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); +#endif + + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + next = current; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + +/*********************************************************************** + * ELIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_before_stay_put( // element to add + ELIST_LINK *new_element) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error("ELIST_ITERATOR::add_before_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); +#endif + + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + +/*********************************************************************** + * ELIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_list_after(ELIST *list_to_add) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error("ELIST_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); +#endif + + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First(); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First(); + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First(); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_list_before(ELIST *list_to_add) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error("ELIST_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); +#endif + + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First(); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First(); + if (current) { //not extracted + list_to_add->last->next = current; + } + else { //current extracted + list_to_add->last->next = next; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to NULL. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline ELIST_LINK *ELIST_ITERATOR::extract() { + ELIST_LINK *extracted_link; + +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error("ELIST_ITERATOR::extract", + ABORT, NULL); +#endif + + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = NULL; + } + else { + prev->next = next; //remove from list + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } + else { + ex_current_was_last = FALSE; + } + } + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + extracted_link = current; + extracted_link->next = NULL; //for safety + current = NULL; + return extracted_link; +} + + +/*********************************************************************** + * ELIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST_LINK *ELIST_ITERATOR::move_to_first() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::move_to_first", ABORT, NULL); +#endif + + current = list->First(); + prev = list->last; + next = current ? current->next : NULL; + return current; +} + + +/*********************************************************************** + * ELIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void ELIST_ITERATOR::mark_cycle_pt() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::mark_cycle_pt", ABORT, NULL); +#endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + + +/*********************************************************************** + * ELIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline bool ELIST_ITERATOR::at_first() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::at_first", ABORT, NULL); +#endif + + //we're at a deleted + return ((list->empty()) || (current == list->First()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline bool ELIST_ITERATOR::at_last() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::at_last", ABORT, NULL); +#endif + + //we're at a deleted + return ((list->empty()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline bool ELIST_ITERATOR::cycled_list() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::cycled_list", ABORT, NULL); +#endif + + return ((list->empty()) || ((current == cycle_pt) && started_cycling)); + +} + + +/*********************************************************************** + * ELIST_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline inT32 ELIST_ITERATOR::length() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::length", ABORT, NULL); +#endif + + return list->length(); +} + + +/*********************************************************************** + * ELIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +ELIST_ITERATOR::sort( //sort elements + int comparator( //comparison routine + const void *, const void *)) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::sort", ABORT, NULL); +#endif + + list->sort(comparator); + move_to_first(); +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void ELIST_ITERATOR::add_to_end( // element to add + ELIST_LINK *new_element) { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::add_to_end", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error("ELIST_ITERATOR::add_to_end", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error("ELIST_ITERATOR::add_to_end", ABORT, NULL); +#endif + + if (this->at_last()) { + this->add_after_stay_put(new_element); + } + else { + if (this->at_first()) { + this->add_before_stay_put(new_element); + list->last = new_element; + } + else { //Iteratr is elsewhere + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + ******************** MACROS ************************************** + ***********************************************************************/ + + /*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== + Replace with "". may be an arbitrary number of tokens + ***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + + /*********************************************************************** + ELISTIZE( CLASSNAME ) MACRO + ============================ + + CLASSNAME is assumed to be the name of a class which has a baseclass of + ELIST_LINK. + + NOTE: Because we don't use virtual functions in the list code, the list code + will NOT work correctly for classes derived from this. + + The macros generate: + - An element deletion function: CLASSNAME##_zapper + - An E_LIST subclass: CLASSNAME##_LIST + - An E_LIST_ITERATOR subclass: CLASSNAME##_IT + + NOTE: Generated names are DELIBERATELY designed to clash with those for + ELIST2IZE but NOT with those for CLISTIZE and CLIST2IZE + + Two macros are provided: ELISTIZE and ELISTIZEH. + The ...IZEH macros just define the class names for use in .h files + The ...IZE macros define the code use in .c files + ***********************************************************************/ + + /*********************************************************************** + ELISTIZEH( CLASSNAME ) MACRO + + ELISTIZEH is a concatenation of 3 fragments ELISTIZEH_A, ELISTIZEH_B and + ELISTIZEH_C. + ***********************************************************************/ + +#define ELISTIZEH_A(CLASSNAME) \ + \ +extern DLLSYM void CLASSNAME##_zapper(ELIST_LINK* link); + +#define ELISTIZEH_B(CLASSNAME) \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_LIST \ +* \ +* List class for class CLASSNAME \ +* \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_LIST : public ELIST { \ + public: \ + CLASSNAME##_LIST():ELIST() {} \ + \ + void clear() { /* delete elements */\ + ELIST::internal_clear(&CLASSNAME##_zapper); \ + } \ + \ + ~CLASSNAME##_LIST() { \ + clear(); \ + } \ + \ + /* Become a deep copy of src_list*/ \ + void deep_copy(const CLASSNAME##_LIST* src_list, \ + CLASSNAME* (*copier)(const CLASSNAME*)); \ + \ +private: \ + /* Prevent assign and copy construction. */ \ + CLASSNAME##_LIST(const CLASSNAME##_LIST&) { \ + DONT_CONSTRUCT_LIST_BY_COPY.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, NULL);\ + } \ + void operator=(const CLASSNAME##_LIST&) { \ + DONT_ASSIGN_LISTS.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, NULL ); \ + } \ + +#define ELISTIZEH_C( CLASSNAME ) \ +}; \ + \ + \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_IT \ +* \ +* Iterator class for class CLASSNAME##_LIST \ +* \ +* Note: We don't need to coerce pointers to member functions input \ +* parameters as these are automatically converted to the type of the base \ +* type. ("A ptr to a class may be converted to a pointer to a public base \ +* class of that class") \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_IT : public ELIST_ITERATOR { \ + public: \ + CLASSNAME##_IT():ELIST_ITERATOR(){} \ + \ + /* TODO(rays) This constructor should be explicit, but that means changing \ + hundreds of incorrect initializations of iterators that use = over () */ \ + CLASSNAME##_IT(CLASSNAME##_LIST* list) : ELIST_ITERATOR(list) {} \ + \ + CLASSNAME* data() { \ + return reinterpret_cast(ELIST_ITERATOR::data()); \ + } \ + \ + CLASSNAME* data_relative(inT8 offset) { \ + return reinterpret_cast(ELIST_ITERATOR::data_relative(offset));\ + } \ + \ + CLASSNAME* forward() { \ + return reinterpret_cast(ELIST_ITERATOR::forward()); \ + } \ + \ + CLASSNAME* extract() { \ + return reinterpret_cast(ELIST_ITERATOR::extract()); \ + } \ + \ + CLASSNAME* move_to_first() { \ + return reinterpret_cast(ELIST_ITERATOR::move_to_first()); \ + } \ + \ + CLASSNAME* move_to_last() { \ + return reinterpret_cast(ELIST_ITERATOR::move_to_last()); \ + } \ +}; + +#define ELISTIZEH( CLASSNAME ) \ + \ +ELISTIZEH_A( CLASSNAME ) \ + \ +ELISTIZEH_B( CLASSNAME ) \ + \ +ELISTIZEH_C( CLASSNAME ) + + + /*********************************************************************** + ELISTIZE( CLASSNAME ) MACRO + ***********************************************************************/ + +#define ELISTIZE(CLASSNAME) \ + \ + /*********************************************************************** \ + * CLASSNAME##_zapper \ + * \ + * A function which can delete a CLASSNAME element. This is passed to the \ + * generic clear list member function so that when a list is cleared the \ + * elements on the list are properly destroyed from the base class, even \ + * though we don't use a virtual destructor function. \ + **********************************************************************/ \ + \ + DLLSYM void CLASSNAME##_zapper(ELIST_LINK *link) { \ + delete reinterpret_cast(link); \ + } \ + \ + /* Become a deep copy of src_list*/ \ + void CLASSNAME##_LIST::deep_copy(const CLASSNAME##_LIST *src_list, \ + CLASSNAME *(*copier)(const CLASSNAME *)) { \ + CLASSNAME##_IT from_it(const_cast(src_list)); \ + CLASSNAME##_IT to_it(this); \ + \ + for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) \ + to_it.add_after_then_move((*copier)(from_it.data())); \ + } + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.cpp new file mode 100644 index 0000000..0d4960e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.cpp @@ -0,0 +1,482 @@ +/********************************************************************** + * File: elst2.c (Formerly elist2.c) + * Description: Doubly linked embedded list code not in the include file. + * Author: Phil Cheatle + * Created: Wed Jan 23 11:04:47 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include "host.h" +#include "elst2.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST2 + * ================================= + **********************************************************************/ + +/*********************************************************************** + * ELIST2::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +ELIST2::internal_clear ( //destroy all links +void (*zapper) (ELIST2_LINK *)) { + //ptr to zapper functn + ELIST2_LINK *ptr; + ELIST2_LINK *next; + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper(ptr); + ptr = next; + } + } +} + +/*********************************************************************** + * ELIST2::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void ELIST2::assign_to_sublist( //to this list + ELIST2_ITERATOR *start_it, //from list start + ELIST2_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + if (!empty ()) + LIST_NOT_EMPTY.error ("ELIST2.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + +/*********************************************************************** + * ELIST2::length + * + * Return count of elements on list + **********************************************************************/ + +inT32 ELIST2::length() const { // count elements + ELIST2_ITERATOR it(const_cast(this)); + inT32 count = 0; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + +/*********************************************************************** + * ELIST2::sort + * + * Sort elements on list + * NB If you don't like the const declarations in the comparator, coerce yours: + * ( int (*)(const void *, const void *) + **********************************************************************/ + +void +ELIST2::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + ELIST2_ITERATOR it(this); + inT32 count; + ELIST2_LINK **base; //ptr array to sort + ELIST2_LINK **current; + inT32 i; + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (ELIST2_LINK **) malloc (count * sizeof (ELIST2_LINK *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + +// Assuming list has been sorted already, insert new_link to +// keep the list sorted according to the same comparison function. +// Comparison function is the same as used by sort, i.e. uses double +// indirection. Time is O(1) to add to beginning or end. +// Time is linear to add pre-sorted items to an empty list. +void ELIST2::add_sorted(int comparator(const void*, const void*), + ELIST2_LINK* new_link) { + // Check for adding at the end. + if (last == NULL || comparator(&last, &new_link) < 0) { + if (last == NULL) { + new_link->next = new_link; + new_link->prev = new_link; + } else { + new_link->next = last->next; + new_link->prev = last; + last->next = new_link; + new_link->next->prev = new_link; + } + last = new_link; + } else { + // Need to use an iterator. + ELIST2_ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + ELIST2_LINK* link = it.data(); + if (comparator(&link, &new_link) > 0) + break; + } + if (it.cycled_list()) + it.add_to_end(new_link); + else + it.add_before_then_move(new_link); + } +} + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST2_ITERATOR + * ========================================== + **********************************************************************/ + +/*********************************************************************** + * ELIST2_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::forward() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + // In case next is deleted by another iterator, get it from the current. + current = current->next; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + current = next; + } + next = current->next; + + #ifndef NDEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST2_ITERATOR::forward", ABORT, + "This is: %p Current is: %p", this, current); + #endif + return current; +} + +/*********************************************************************** + * ELIST2_ITERATOR::backward + * + * Move the iterator to the previous element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::backward() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + next = current; + started_cycling = TRUE; + // In case prev is deleted by another iterator, get it from current. + current = current->prev; + } else { + if (ex_current_was_cycle_pt) + cycle_pt = prev; + current = prev; + } + prev = current->prev; + + #ifndef NDEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + if (!prev) + NULL_PREV.error ("ELIST2_ITERATOR::backward", ABORT, + "This is: %p Current is: %p", this, current); + #endif + return current; +} + +/*********************************************************************** + * ELIST2_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::data_relative( //get data + or - .. + inT8 offset) { //offset from current + ELIST2_LINK *ptr; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + #endif + + if (offset < 0) + for (ptr = current ? current : next; offset++ < 0; ptr = ptr->prev); + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifndef NDEBUG + if (!ptr) + NULL_DATA.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr; +} + +/*********************************************************************** + * ELIST2_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void ELIST2_ITERATOR::exchange( //positions of 2 links + ELIST2_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + ELIST2_LINK *old_current; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("ELIST2_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = current; + current->next = other_it->current; + current->prev = other_it->prev; + next->prev = other_it->current; + + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->current; + other_it->current->next = current; + other_it->current->prev = prev; + other_it->next->prev = current; + + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->prev; + next->prev = other_it->current; + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = prev; + other_it->next->prev = current; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + +/*********************************************************************** + * ELIST2_ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST2::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::extract_sublist( //from this current + ELIST2_ITERATOR *other_it) { //to other current + #ifndef NDEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + #endif + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + + ELIST2_ITERATOR temp_it = *this; + ELIST2_LINK *end_of_new_list; + + #ifndef NDEBUG + if (!other_it) + BAD_PARAMETER.error ("ELIST2_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("ELIST2_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list()) // can't find end pt + BAD_SUBLIST.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + //do INCLUSIVE list + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + //circularise sublist + current->prev = other_it->current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + other_it->next->prev = prev; + + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.h b/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.h new file mode 100644 index 0000000..bf078fb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/elst2.h @@ -0,0 +1,989 @@ +/********************************************************************** + * File: elst2.h (Formerly elist2.h) + * Description: Double linked embedded list module include file. + * Author: Phil Cheatle + * Created: Wed Jan 23 11:04:47 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ELST2_H +#define ELST2_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class ELIST2_ITERATOR; + +/********************************************************************** +DESIGN NOTE +=========== + +It would probably be possible to implement the ELIST2 classes as derived +classes from ELIST. I haven't done this because: + +a) I think it would be harder to understand the code +(Though the problem with not inheriting is that changes to ELIST must be + reflected in ELIST2 and vice versa) + +b) Most of the code is inline so: +i) The duplication in source does not affect the run time code size - the + code is copied inline anyway! + + ii) The compiler should have a bit less work to do! +**********************************************************************/ + +/********************************************************************** + * CLASS - ELIST2_LINK + * + * Generic link class for doubly linked lists with embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST2 destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM ELIST2_LINK +{ + friend class ELIST2_ITERATOR; + friend class ELIST2; + + ELIST2_LINK *prev; + ELIST2_LINK *next; + + public: + ELIST2_LINK() { //constructor + prev = next = NULL; + } + + ELIST2_LINK( // copy constructor + const ELIST2_LINK &) { // don't copy link + prev = next = NULL; + } + + void operator=( // don't copy links + const ELIST2_LINK &) { + prev = next = NULL; + } +}; + +/********************************************************************** + * CLASS - ELIST2 + * + * Generic list class for doubly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST2 +{ + friend class ELIST2_ITERATOR; + + ELIST2_LINK *last; //End of list + //(Points to head) + ELIST2_LINK *First() { // return first + return last ? last->next : NULL; + } + + public: + ELIST2() { //constructor + last = NULL; + } + + void internal_clear ( //destroy all links + void (*zapper) (ELIST2_LINK *)); + //ptr to zapper functn + + bool empty() const { //is list empty? + return !last; + } + + bool singleton() const { + return last ? (last == last->next) : false; + } + + void shallow_copy( //dangerous!! + ELIST2 *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy (ELIST2_LINK * (*copier) (ELIST2_LINK *), + const ELIST2 * list); //list being copied + + void assign_to_sublist( //to this list + ELIST2_ITERATOR *start_it, //from list start + ELIST2_ITERATOR *end_it); //from list end + + inT32 length() const; // # elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + // Assuming list has been sorted already, insert new_link to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + void add_sorted(int comparator(const void*, const void*), + ELIST2_LINK* new_link); + +}; + +/*********************************************************************** + * CLASS - ELIST2_ITERATOR + * + * Generic iterator class for doubly linked lists with embedded + *links + **********************************************************************/ + +class DLLSYM ELIST2_ITERATOR +{ + friend void ELIST2::assign_to_sublist(ELIST2_ITERATOR *, ELIST2_ITERATOR *); + + ELIST2 *list; //List being iterated + ELIST2_LINK *prev; //prev element + ELIST2_LINK *current; //current element + ELIST2_LINK *next; //next element + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + ELIST2_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 started_cycling; //Have we moved off + //the start? + + ELIST2_LINK *extract_sublist( //from this current... + ELIST2_ITERATOR *other_it); //to other current + + public: + ELIST2_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + ELIST2_ITERATOR( //constructor + ELIST2 *list_to_iterate); + + void set_to_list( //change list + ELIST2 *list_to_iterate); + + void add_after_then_move( //add after current & + ELIST2_LINK *new_link); //move to new + + void add_after_stay_put( //add after current & + ELIST2_LINK *new_link); //stay at current + + void add_before_then_move( //add before current & + ELIST2_LINK *new_link); //move to new + + void add_before_stay_put( //add before current & + ELIST2_LINK *new_link); //stay at current + + void add_list_after( //add a list & + ELIST2 *list_to_add); //stay at current + + void add_list_before( //add a list & + ELIST2 *list_to_add); //move to it 1st item + + ELIST2_LINK *data() { //get current data + #ifndef NDEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::data", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::data", ABORT, NULL); + #endif + return current; + } + + ELIST2_LINK *data_relative( //get data + or - ... + inT8 offset); //offset from current + + ELIST2_LINK *forward(); //move to next element + + ELIST2_LINK *backward(); //move to prev element + + ELIST2_LINK *extract(); //remove from list + + //go to start of list + ELIST2_LINK *move_to_first(); + + ELIST2_LINK *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + BOOL8 empty() { //is list empty? + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::empty", ABORT, NULL); + #endif + return list->empty (); + } + + BOOL8 current_extracted() { //current extracted? + return !current; + } + + BOOL8 at_first(); //Current is first? + + BOOL8 at_last(); //Current is last? + + BOOL8 cycled_list(); //Completed a cycle? + + void add_to_end( // add at end & + ELIST2_LINK *new_link); // don't move + + void exchange( //positions of 2 links + ELIST2_ITERATOR *other_it); //other iterator + + inT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * ELIST2_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void ELIST2_ITERATOR::set_to_list( //change list + ELIST2 *list_to_iterate) { + #ifndef NDEBUG + if (!list_to_iterate) + BAD_PARAMETER.error ("ELIST2_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); + #endif + + list = list_to_iterate; + prev = list->last; + current = list->First (); + next = current ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + +/*********************************************************************** + * ELIST2_ITERATOR::ELIST2_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline ELIST2_ITERATOR::ELIST2_ITERATOR(ELIST2 *list_to_iterate) { + set_to_list(list_to_iterate); +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_after_then_move( // element to add + ELIST2_LINK *new_element) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + next->prev = new_element; + + if (current) { //not extracted + new_element->prev = current; + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_after_stay_put( // element to add + ELIST2_LINK *new_element) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + next->prev = new_element; + + if (current) { //not extracted + new_element->prev = current; + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_then_move( // element to add + ELIST2_LINK *new_element) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { //not extracted + new_element->next = current; + current->prev = new_element; + next = current; + } + else { //current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_stay_put( // element to add + ELIST2_LINK *new_element) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { //not extracted + new_element->next = current; + current->prev = new_element; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_list_after(ELIST2 *list_to_add) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First (); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First (); + current->next->prev = current; + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First (); + prev->next->prev = prev; + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_list_before(ELIST2 *list_to_add) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First (); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First (); + prev->next->prev = prev; + + if (current) { //not extracted + list_to_add->last->next = current; + current->prev = list_to_add->last; + } + else { //current extracted + list_to_add->last->next = next; + next->prev = list_to_add->last; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + +/*********************************************************************** + * ELIST2_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to NULL. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::extract() { + ELIST2_LINK *extracted_link; + + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error ("ELIST2_ITERATOR::extract", + ABORT, NULL); + #endif + + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = NULL; + } else { + prev->next = next; //remove from list + next->prev = prev; + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } else { + ex_current_was_last = FALSE; + } + } + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + extracted_link = current; + extracted_link->next = NULL; //for safety + extracted_link->prev = NULL; //for safety + current = NULL; + return extracted_link; +} + +/*********************************************************************** + * ELIST2_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_first() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::move_to_first", ABORT, NULL); + #endif + + current = list->First (); + prev = list->last; + next = current ? current->next : NULL; + return current; +} + +/*********************************************************************** + * ELIST2_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_last() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::move_to_last", ABORT, NULL); + #endif + + current = list->last; + prev = current ? current->prev : NULL; + next = current ? current->next : NULL; + return current; +} + +/*********************************************************************** + * ELIST2_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void ELIST2_ITERATOR::mark_cycle_pt() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::mark_cycle_pt", ABORT, NULL); + #endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + +/*********************************************************************** + * ELIST2_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::at_first() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::at_first", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + +/*********************************************************************** + * ELIST2_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::at_last() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::at_last", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + +/*********************************************************************** + * ELIST2_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::cycled_list() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::cycled_list", ABORT, NULL); + #endif + + return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); + +} + +/*********************************************************************** + * ELIST2_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline inT32 ELIST2_ITERATOR::length() { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::length", ABORT, NULL); + #endif + + return list->length (); +} + +/*********************************************************************** + * ELIST2_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +ELIST2_ITERATOR::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::sort", ABORT, NULL); + #endif + + list->sort (comparator); + move_to_first(); +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void ELIST2_ITERATOR::add_to_end( // element to add + ELIST2_LINK *new_element) { + #ifndef NDEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_to_end", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + #endif + + if (this->at_last ()) { + this->add_after_stay_put (new_element); + } + else { + if (this->at_first ()) { + this->add_before_stay_put (new_element); + list->last = new_element; + } + else { //Iteratr is elsewhere + new_element->next = list->last->next; + new_element->prev = list->last; + list->last->next->prev = new_element; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +/*********************************************************************** + ELIST2IZE( CLASSNAME ) MACRO DEFINITION + ====================================== + +CLASSNAME is assumed to be the name of a class which has a baseclass of +ELIST2_LINK. + +NOTE: Because we don't use virtual functions in the list code, the list code +will NOT work correctly for classes derived from this. + +The macro generates: + - An element deletion function: CLASSNAME##_zapper + - An E_LIST2 subclass: CLASSNAME##_LIST + - An E_LIST2_ITERATOR subclass: + CLASSNAME##_IT + +NOTE: Generated names are DELIBERATELY designed to clash with those for +ELISTIZE but NOT with those for CLISTIZE and CLIST2IZE + +Two macros are provided: ELIST2IZE and ELIST2IZEH +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +***********************************************************************/ + +/*********************************************************************** + ELIST2IZEH( CLASSNAME ) MACRO + +ELIST2IZEH is a concatenation of 3 fragments ELIST2IZEH_A, ELIST2IZEH_B and +ELIST2IZEH_C. +***********************************************************************/ + +#define ELIST2IZEH_A(CLASSNAME) \ + \ + extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ + ELIST2_LINK *link); /*link to delete*/ + +#define ELIST2IZEH_B(CLASSNAME) \ + \ + /*********************************************************************** \ + * CLASS - \ + *CLASSNAME##_LIST \ + * \ + * List class for class \ + *CLASSNAME \ + * \ + **********************************************************************/ \ + \ + class DLLSYM CLASSNAME##_LIST : public ELIST2 { \ + public: \ + CLASSNAME##_LIST() : ELIST2() {} \ + /* constructor */ \ + \ + CLASSNAME##_LIST( /* don't construct */ \ + const CLASSNAME##_LIST &) /*by initial assign*/ \ + { \ + DONT_CONSTRUCT_LIST_BY_COPY.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, \ + NULL); \ + } \ + \ + void clear() /* delete elements */ \ + { \ + ELIST2::internal_clear(&CLASSNAME##_zapper); \ + } \ + \ + ~CLASSNAME##_LIST() /* destructor */ \ + { \ + clear(); \ + } \ + \ + /* Become a deep copy of src_list*/ \ + void deep_copy(const CLASSNAME##_LIST *src_list, \ + CLASSNAME *(*copier)(const CLASSNAME *)); \ + \ + void operator=(/* prevent assign */ \ + const CLASSNAME##_LIST &) { \ + DONT_ASSIGN_LISTS.error(QUOTE_IT(CLASSNAME##_LIST), ABORT, NULL); \ + } + +#define ELIST2IZEH_C(CLASSNAME) \ + } \ + ; \ + \ + /*********************************************************************** \ + * CLASS - CLASSNAME##_IT \ + * \ + * Iterator class for class CLASSNAME##_LIST \ + * \ + * Note: We don't need to coerce pointers to member functions input \ + * parameters as these are automatically converted to the type of the base \ + * type. ("A ptr to a class may be converted to a pointer to a public base \ + * class of that class") \ + **********************************************************************/ \ + \ + class DLLSYM CLASSNAME##_IT : public ELIST2_ITERATOR { \ + public: \ + CLASSNAME##_IT() : ELIST2_ITERATOR() {} \ + \ + CLASSNAME##_IT(CLASSNAME##_LIST *list) : ELIST2_ITERATOR(list) {} \ + \ + CLASSNAME *data() { return (CLASSNAME *)ELIST2_ITERATOR::data(); } \ + \ + CLASSNAME *data_relative(inT8 offset) { \ + return (CLASSNAME *)ELIST2_ITERATOR::data_relative(offset); \ + } \ + \ + CLASSNAME *forward() { return (CLASSNAME *)ELIST2_ITERATOR::forward(); } \ + \ + CLASSNAME *backward() { return (CLASSNAME *)ELIST2_ITERATOR::backward(); } \ + \ + CLASSNAME *extract() { return (CLASSNAME *)ELIST2_ITERATOR::extract(); } \ + \ + CLASSNAME *move_to_first() { \ + return (CLASSNAME *)ELIST2_ITERATOR::move_to_first(); \ + } \ + \ + CLASSNAME *move_to_last() { \ + return (CLASSNAME *)ELIST2_ITERATOR::move_to_last(); \ + } \ + }; + +#define ELIST2IZEH(CLASSNAME) \ + \ + ELIST2IZEH_A(CLASSNAME) \ + \ + ELIST2IZEH_B(CLASSNAME) \ + \ + ELIST2IZEH_C(CLASSNAME) + +/*********************************************************************** + ELIST2IZE( CLASSNAME ) MACRO +***********************************************************************/ + +#define ELIST2IZE(CLASSNAME) \ + \ + /*********************************************************************** \ + * CLASSNAME##_zapper \ + * \ + * A function which can delete a CLASSNAME element. This is passed to the \ + * generic clear list member function so that when a list is cleared the \ + * elements on the list are properly destroyed from the base class, even \ + * though we don't use a virtual destructor function. \ + **********************************************************************/ \ + \ + DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ + ELIST2_LINK *link) /*link to delete*/ \ + { \ + delete (CLASSNAME *)link; \ + } \ + \ + /* Become a deep copy of src_list*/ \ + void CLASSNAME##_LIST::deep_copy(const CLASSNAME##_LIST *src_list, \ + CLASSNAME *(*copier)(const CLASSNAME *)) { \ + CLASSNAME##_IT from_it(const_cast(src_list)); \ + CLASSNAME##_IT to_it(this); \ + \ + for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) \ + to_it.add_after_then_move((*copier)(from_it.data())); \ + } + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.cpp new file mode 100644 index 0000000..42eaf87 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.cpp @@ -0,0 +1,91 @@ +/********************************************************************** + * 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 +#include +#include +#include +#ifdef __UNIX__ +#include +#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"); + + // %s is needed here so msg is printed correctly! + fprintf(stderr, "%s", 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); + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.h b/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.h new file mode 100644 index 0000000..2f31a7b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/errcode.h @@ -0,0 +1,103 @@ +/********************************************************************** + * File: errcode.h (Formerly error.h) + * Description: Header file for generic error handler class + * Author: Ray Smith + * Created: Tue May 1 16:23:36 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ERRCODE_H +#define ERRCODE_H + +#include "host.h" + +/*Control parameters for error()*/ +enum TessErrorLogCode { + DBG = -1, /*log without alert */ + TESSLOG = 0, /*alert user */ + TESSEXIT = 1, /*exit after erro */ + ABORT = 2 /*abort after error */ +}; + +/* Explicit Error Abort codes */ +#define NO_ABORT_CODE 0 +#define LIST_ABORT 1 +#define MEMORY_ABORT 2 +#define FILE_ABORT 3 + +/* Location of code at error codes Reserve 0..2 (status codes 0..23 for UNLV)*/ +#define LOC_UNUSED0 0 +#define LOC_UNUSED1 1 +#define LOC_UNUSED2 2 +#define LOC_INIT 3 +#define LOC_EDGE_PROG 4 +#define LOC_TEXT_ORD_ROWS 5 +#define LOC_TEXT_ORD_WORDS 6 +#define LOC_PASS1 7 +#define LOC_PASS2 8 +/* Reserve up to 8..13 for adding subloc 0/3 plus subsubloc 0/1/2 */ +#define LOC_FUZZY_SPACE 14 +/* Reserve up to 14..20 for adding subloc 0/3 plus subsubloc 0/1/2 */ +#define LOC_MM_ADAPT 21 +#define LOC_DOC_BLK_REJ 22 +#define LOC_WRITE_RESULTS 23 +#define LOC_ADAPTIVE 24 +/* DON'T DEFINE ANY LOCATION > 31 !!! */ + +/* Sub locatation determines whether pass2 was in normal mode or fix xht mode*/ +#define SUBLOC_NORM 0 +#define SUBLOC_FIX_XHT 3 + +/* Sub Sub locatation determines whether match_word_pass2 was in Tess + matcher, NN matcher or somewhere else */ + +#define SUBSUBLOC_OTHER 0 +#define SUBSUBLOC_TESS 1 +#define SUBSUBLOC_NN 2 + +class TESS_API ERRCODE { // error handler class + const char *message; // error message + public: + void error( // error print function + const char *caller, // function location + TessErrorLogCode action, // action to take + const char *format, ... // fprintf format + ) const; + ERRCODE(const char *string) { + message = string; + } // initialize with string +}; + +const ERRCODE ASSERT_FAILED = "Assert failed"; + +#define ASSERT_HOST(x) if (!(x)) \ + { \ + ASSERT_FAILED.error(#x, ABORT, "in file %s, line %d", \ + __FILE__, __LINE__); \ + } + +#define ASSERT_HOST_MSG(x, ...) \ + if (!(x)) { \ + tprintf(__VA_ARGS__); \ + ASSERT_FAILED.error(#x, ABORT, "in file %s, line %d", __FILE__, __LINE__); \ + } + +void signal_exit(int signal_code); + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/fileerr.h b/hgdriver/3rdparty/hgOCR/include/ccutil/fileerr.h new file mode 100644 index 0000000..d3b6993 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/fileerr.h @@ -0,0 +1,34 @@ +/********************************************************************** + * File: fileerr.h (Formerly filerr.h) + * Description: Errors for file utilities. + * Author: Ray Smith + * Created: Tue Aug 14 15:45:16 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef FILEERR_H +#define FILEERR_H + +#include "errcode.h" + +const ERRCODE CANTOPENFILE = "Can't open file"; +const ERRCODE CANTCREATEFILE = "Can't create file"; +const ERRCODE CANTMAKEPIPE = "Can't create pipe"; +const ERRCODE CANTCONNECTPIPE = "Can't reconnect pipes to stdin/stdout"; +const ERRCODE READFAILED = "Read of file failed"; +const ERRCODE WRITEFAILED = "Write of file failed"; +const ERRCODE SELECTFAILED = "Select failed"; + +const ERRCODE EXECFAILED = "Could not exec new process"; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/genericheap.h b/hgdriver/3rdparty/hgOCR/include/ccutil/genericheap.h new file mode 100644 index 0000000..ccf273b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/genericheap.h @@ -0,0 +1,237 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: genericheap.h +// Description: Template heap class. +// Author: Ray Smith, based on Dan Johnson's original code. +// Created: Wed Mar 14 08:13:00 PDT 2012 +// +// (C) Copyright 2012, 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 "errcode.h" +#include "genericvector.h" + +#ifndef TESSERACT_CCUTIL_GENERICHEAP_H_ +#define TESSERACT_CCUTIL_GENERICHEAP_H_ + +namespace tesseract { + +// GenericHeap requires 1 template argument: +// Pair will normally be either KDPairInc or KDPairDec +// for some arbitrary Key and scalar, smart pointer, or non-ownership pointer +// Data type, according to whether a MIN heap or a MAX heap is desired, +// respectively. Using KDPtrPairInc or KDPtrPairDec, +// GenericHeap can also handle simple Data pointers and own them. +// If no additional data is required, Pair can also be a scalar, since +// GenericHeap doesn't look inside it except for operator<. +// +// The heap is stored as a packed binary tree in an array hosted by a +// GenericVector, with the invariant that the children of each node are +// both NOT Pair::operator< the parent node. KDPairInc defines Pair::operator< +// to use Key::operator< to generate a MIN heap and KDPairDec defines +// Pair::operator< to use Key::operator> to generate a MAX heap by reversing +// all the comparisons. +// See http://en.wikipedia.org/wiki/Heap_(data_structure) for more detail on +// the basic heap implementation. +// +// Insertion and removal are both O(log n) and, unlike the STL heap, an +// explicit Reshuffle function allows a node to be repositioned in time O(log n) +// after changing its value. +// +// Accessing the element for revaluation is a more complex matter, since the +// index and pointer can be changed arbitrarily by heap operations. +// Revaluation can be done by making the Data type in the Pair derived from or +// contain a DoublePtr as its first data element, making it possible to convert +// the pointer to a Pair using KDPairInc::RecastDataPointer. +template +class GenericHeap { + public: + GenericHeap() {} + // The initial size is only a GenericVector::reserve. It is not enforced as + // the size limit of the heap. Caller must implement their own enforcement. + explicit GenericHeap(int initial_size) { + heap_.reserve(initial_size); + } + + // Simple accessors. + bool empty() const { + return heap_.empty(); + } + int size() const { + return heap_.size(); + } + int size_reserved() const { + return heap_.size_reserved(); + } + void clear() { + // Clear truncates to 0 to keep the number reserved in tact. + heap_.truncate(0); + } + // Provides access to the underlying vector. + // Caution! any changes that modify the keys will invalidate the heap! + GenericVector* heap() { + return &heap_; + } + // Provides read-only access to an element of the underlying vector. + const Pair& get(int index) const { + return heap_[index]; + } + + // Add entry to the heap, keeping the smallest item at the top, by operator<. + // Note that *entry is used as the source of operator=, but it is non-const + // to allow for a smart pointer to be contained within. + // Time = O(log n). + void Push(Pair* entry) { + int hole_index = heap_.size(); + // Make a hole in the end of heap_ and sift it up to be the correct + // location for the new *entry. To avoid needing a default constructor + // for primitive types, and to allow for use of DoublePtr in the Pair + // somewhere, we have to incur a double copy here. + heap_.push_back(*entry); + *entry = heap_.back(); + hole_index = SiftUp(hole_index, *entry); + heap_[hole_index] = *entry; + } + + // Get the value of the top (smallest, defined by operator< ) element. + const Pair& PeekTop() const { + return heap_[0]; + } + // Get the value of the worst (largest, defined by operator< ) element. + const Pair& PeekWorst() const { return heap_[IndexOfWorst()]; } + + // Removes the top element of the heap. If entry is not NULL, the element + // is copied into *entry, otherwise it is discarded. + // Returns false if the heap was already empty. + // Time = O(log n). + bool Pop(Pair* entry) { + int new_size = heap_.size() - 1; + if (new_size < 0) + return false; // Already empty. + if (entry != NULL) + *entry = heap_[0]; + if (new_size > 0) { + // Sift the hole at the start of the heap_ downwards to match the last + // element. + Pair hole_pair = heap_[new_size]; + heap_.truncate(new_size); + int hole_index = SiftDown(0, hole_pair); + heap_[hole_index] = hole_pair; + } else { + heap_.truncate(new_size); + } + return true; + } + + // Removes the MAXIMUM element of the heap. (MIN from a MAX heap.) If entry is + // not NULL, the element is copied into *entry, otherwise it is discarded. + // Time = O(n). Returns false if the heap was already empty. + bool PopWorst(Pair* entry) { + int worst_index = IndexOfWorst(); + if (worst_index < 0) return false; // It cannot be empty! + // Extract the worst element from the heap, leaving a hole at worst_index. + if (entry != NULL) + *entry = heap_[worst_index]; + int heap_size = heap_.size() - 1; + if (heap_size > 0) { + // Sift the hole upwards to match the last element of the heap_ + Pair hole_pair = heap_[heap_size]; + int hole_index = SiftUp(worst_index, hole_pair); + heap_[hole_index] = hole_pair; + } + heap_.truncate(heap_size); + return true; + } + + // Returns the index of the worst element. Time = O(n/2). + int IndexOfWorst() const { + int heap_size = heap_.size(); + if (heap_size == 0) return -1; // It cannot be empty! + + // Find the maximum element. Its index is guaranteed to be greater than + // the index of the parent of the last element, since by the heap invariant + // the parent must be less than or equal to the children. + int worst_index = heap_size - 1; + int end_parent = ParentNode(worst_index); + for (int i = worst_index - 1; i > end_parent; --i) { + if (heap_[worst_index] < heap_[i]) worst_index = i; + } + return worst_index; + } + + // The pointed-to Pair has changed its key value, so the location of pair + // is reshuffled to maintain the heap invariant. + // Must be a valid pointer to an element of the heap_! + // Caution! Since GenericHeap is based on GenericVector, reallocs may occur + // whenever the vector is extended and elements may get shuffled by any + // Push or Pop operation. Therefore use this function only if Data in Pair is + // of type DoublePtr, derived (first) from DoublePtr, or has a DoublePtr as + // its first element. Reshuffles the heap to maintain the invariant. + // Time = O(log n). + void Reshuffle(Pair* pair) { + int index = pair - &heap_[0]; + Pair hole_pair = heap_[index]; + index = SiftDown(index, hole_pair); + index = SiftUp(index, hole_pair); + heap_[index] = hole_pair; + } + + private: + // A hole in the heap exists at hole_index, and we want to fill it with the + // given pair. SiftUp sifts the hole upward to the correct position and + // returns the destination index without actually putting pair there. + int SiftUp(int hole_index, const Pair& pair) { + int parent; + while (hole_index > 0 && pair < heap_[parent = ParentNode(hole_index)]) { + heap_[hole_index] = heap_[parent]; + hole_index = parent; + } + return hole_index; + } + + // A hole in the heap exists at hole_index, and we want to fill it with the + // given pair. SiftDown sifts the hole downward to the correct position and + // returns the destination index without actually putting pair there. + int SiftDown(int hole_index, const Pair& pair) { + int heap_size = heap_.size(); + int child; + while ((child = LeftChild(hole_index)) < heap_size) { + if (child + 1 < heap_size && heap_[child + 1] < heap_[child]) + ++child; + if (heap_[child] < pair) { + heap_[hole_index] = heap_[child]; + hole_index = child; + } else { + break; + } + } + return hole_index; + } + + // Functions to navigate the tree. Unlike the original implementation, we + // store the root at index 0. + int ParentNode(int index) const { + return (index + 1) / 2 - 1; + } + int LeftChild(int index) const { + return index * 2 + 1; + } + + private: + GenericVector heap_; +}; + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_GENERICHEAP_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/genericvector.h b/hgdriver/3rdparty/hgOCR/include/ccutil/genericvector.h new file mode 100644 index 0000000..f41fcc3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/genericvector.h @@ -0,0 +1,1109 @@ +/////////////////////////////////////////////////////////////////////// +// File: genericvector.h +// Description: Generic vector class +// Author: Daria Antonova +// Created: Mon Jun 23 11:26:43 PDT 2008 +// +// (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_CCUTIL_GENERICVECTOR_H_ +#define TESSERACT_CCUTIL_GENERICVECTOR_H_ + +#include +#include +#include + +#include "tesscallback.h" +#include "errcode.h" +#include "helpers.h" +#include "ndminx.h" +#include "serialis.h" +#include "strngs.h" + +// Use PointerVector below in preference to GenericVector, as that +// provides automatic deletion of pointers, [De]Serialize that works, and +// sort that works. +template +class GenericVector { + public: + GenericVector() : size_used_(0), size_reserved_(0), data_(NULL), + clear_cb_(NULL), compare_cb_(NULL) {} + + GenericVector(int size, T init_val) { + init(size); + init_to_size(size, init_val); + } + + // Copy + GenericVector(const GenericVector& other) { + this->init(other.size()); + this->operator+=(other); + } + GenericVector &operator+=(const GenericVector& other); + GenericVector &operator=(const GenericVector& other); + + ~GenericVector(); + + // Reserve some memory. + void reserve(int size); + // Double the size of the internal array. + void double_the_size(); + + // Resizes to size and sets all values to t. + void init_to_size(int size, T t); + // Resizes to size without any initialization. + void resize_no_init(int size) { + reserve(size); + size_used_ = size; + } + + // Return the size used. + int size() const { + return size_used_; + } + int size_reserved() const { + return size_reserved_; + } + + int length() const { + return size_used_; + } + + // Return true if empty. + bool empty() const { + return size_used_ == 0; + } + + // Return the object from an index. + T &get(int index) const; + T &back() const; + T &operator[](int index) const; + // Returns the last object and removes it. + T pop_back(); + + // Return the index of the T object. + // This method NEEDS a compare_callback to be passed to + // set_compare_callback. + int get_index(T object) const; + + // Return true if T is in the array + bool contains(T object) const; + + // Return true if the index is valid + T contains_index(int index) const; + + // Push an element in the end of the array + int push_back(T object); + void operator+=(T t); + + // Push an element in the end of the array if the same + // element is not already contained in the array. + int push_back_new(T object); + + // Push an element in the front of the array + // Note: This function is O(n) + int push_front(T object); + + // Set the value at the given index + void set(T t, int index); + + // Insert t at the given index, push other elements to the right. + void insert(T t, int index); + + // Removes an element at the given index and + // shifts the remaining elements to the left. + void remove(int index); + + // Truncates the array to the given size by removing the end. + // If the current size is less, the array is not expanded. + void truncate(int size) { + if (size < size_used_) + size_used_ = size; + } + + // Add a callback to be called to delete the elements when the array took + // their ownership. + void set_clear_callback(TessCallback1* cb); + + // Add a callback to be called to compare the elements when needed (contains, + // get_id, ...) + void set_compare_callback(TessResultCallback2* cb); + + // Clear the array, calling the clear callback function if any. + // All the owned callbacks are also deleted. + // If you don't want the callbacks to be deleted, before calling clear, set + // the callback to NULL. + void clear(); + + // Delete objects pointed to by data_[i] + void delete_data_pointers(); + + // This method clears the current object, then, does a shallow copy of + // its argument, and finally invalidates its argument. + // Callbacks are moved to the current object; + void move(GenericVector* from); + + // Read/Write the array to a file. This does _NOT_ read/write the callbacks. + // The callback given must be permanent since they will be called more than + // once. The given callback will be deleted at the end. + // If the callbacks are NULL, then the data is simply read/written using + // fread (and swapping)/fwrite. + // Returns false on error or if the callback returns false. + // DEPRECATED. Use [De]Serialize[Classes] instead. + bool write(FILE* f, TessResultCallback2* cb) const; + bool read(FILE* f, TessResultCallback3* cb, bool swap); + // Writes a vector of simple types to the given file. Assumes that bitwise + // read/write of T will work. Returns false in case of error. + // TODO(rays) Change all callers to use TFile and remove deprecated methods. + bool Serialize(FILE* fp) const; + bool Serialize(tesseract::TFile* fp) const; + // Reads a vector of simple types from the given file. Assumes that bitwise + // read/write will work with ReverseN according to sizeof(T). + // Returns false in case of error. + // If swap is true, assumes a big/little-endian swap is needed. + bool DeSerialize(bool swap, FILE* fp); + bool DeSerialize(bool swap, tesseract::TFile* fp); + // Skips the deserialization of the vector. + static bool SkipDeSerialize(bool swap, tesseract::TFile* fp); + // Writes a vector of classes to the given file. Assumes the existence of + // bool T::Serialize(FILE* fp) const that returns false in case of error. + // Returns false in case of error. + bool SerializeClasses(FILE* fp) const; + bool SerializeClasses(tesseract::TFile* fp) const; + // Reads a vector of classes from the given file. Assumes the existence of + // bool T::Deserialize(bool swap, FILE* fp) that returns false in case of + // error. Also needs T::T() and T::T(constT&), as init_to_size is used in + // this function. Returns false in case of error. + // If swap is true, assumes a big/little-endian swap is needed. + bool DeSerializeClasses(bool swap, FILE* fp); + bool DeSerializeClasses(bool swap, tesseract::TFile* fp); + // Calls SkipDeSerialize on the elements of the vector. + static bool SkipDeSerializeClasses(bool swap, tesseract::TFile* fp); + + // Allocates a new array of double the current_size, copies over the + // information from data to the new location, deletes data and returns + // the pointed to the new larger array. + // This function uses memcpy to copy the data, instead of invoking + // operator=() for each element like double_the_size() does. + static T *double_the_size_memcpy(int current_size, T *data) { + T *data_new = new T[current_size * 2]; + memcpy(data_new, data, sizeof(T) * current_size); + delete[] data; + return data_new; + } + + // Reverses the elements of the vector. + void reverse() { + for (int i = 0; i < size_used_ / 2; ++i) + Swap(&data_[i], &data_[size_used_ - 1 - i]); + } + + // Sorts the members of this vector using the less than comparator (cmp_lt), + // which compares the values. Useful for GenericVectors to primitive types. + // Will not work so great for pointers (unless you just want to sort some + // pointers). You need to provide a specialization to sort_cmp to use + // your type. + void sort(); + + // Sort the array into the order defined by the qsort function comparator. + // The comparator function is as defined by qsort, ie. it receives pointers + // to two Ts and returns negative if the first element is to appear earlier + // in the result and positive if it is to appear later, with 0 for equal. + void sort(int (*comparator)(const void*, const void*)) { + qsort(data_, size_used_, sizeof(*data_), comparator); + } + + // Searches the array (assuming sorted in ascending order, using sort()) for + // an element equal to target and returns true if it is present. + // Use binary_search to get the index of target, or its nearest candidate. + bool bool_binary_search(const T& target) const { + int index = binary_search(target); + if (index >= size_used_) + return false; + return data_[index] == target; + } + // Searches the array (assuming sorted in ascending order, using sort()) for + // an element equal to target and returns the index of the best candidate. + // The return value is conceptually the largest index i such that + // data_[i] <= target or 0 if target < the whole vector. + // NOTE that this function uses operator> so really the return value is + // the largest index i such that data_[i] > target is false. + int binary_search(const T& target) const { + int bottom = 0; + int top = size_used_; + while (top - bottom > 1) { + int middle = (bottom + top) / 2; + if (data_[middle] > target) + top = middle; + else + bottom = middle; + } + return bottom; + } + + // Compact the vector by deleting elements using operator!= on basic types. + // The vector must be sorted. + void compact_sorted() { + if (size_used_ == 0) + return; + + // First element is in no matter what, hence the i = 1. + int last_write = 0; + for (int i = 1; i < size_used_; ++i) { + // Finds next unique item and writes it. + if (data_[last_write] != data_[i]) + data_[++last_write] = data_[i]; + } + // last_write is the index of a valid data cell, so add 1. + size_used_ = last_write + 1; + } + + // Compact the vector by deleting elements for which delete_cb returns + // true. delete_cb is a permanent callback and will be deleted. + void compact(TessResultCallback1* delete_cb) { + int new_size = 0; + int old_index = 0; + // Until the callback returns true, the elements stay the same. + while (old_index < size_used_ && !delete_cb->Run(old_index++)) + ++new_size; + // Now just copy anything else that gets false from delete_cb. + for (; old_index < size_used_; ++old_index) { + if (!delete_cb->Run(old_index)) { + data_[new_size++] = data_[old_index]; + } + } + size_used_ = new_size; + delete delete_cb; + } + + T dot_product(const GenericVector& other) const { + T result = static_cast(0); + for (int i = MIN(size_used_, other.size_used_) - 1; i >= 0; --i) + result += data_[i] * other.data_[i]; + return result; + } + + // Returns the index of what would be the target_index_th item in the array + // if the members were sorted, without actually sorting. Members are + // shuffled around, but it takes O(n) time. + // NOTE: uses operator< and operator== on the members. + int choose_nth_item(int target_index) { + // Make sure target_index is legal. + if (target_index < 0) + target_index = 0; // ensure legal + else if (target_index >= size_used_) + target_index = size_used_ - 1; + unsigned int seed = 1; + return choose_nth_item(target_index, 0, size_used_, &seed); + } + + // Swaps the elements with the given indices. + void swap(int index1, int index2) { + if (index1 != index2) { + T tmp = data_[index1]; + data_[index1] = data_[index2]; + data_[index2] = tmp; + } + } + // Returns true if all elements of *this are within the given range. + // Only uses operator< + bool WithinBounds(const T& rangemin, const T& rangemax) const { + for (int i = 0; i < size_used_; ++i) { + if (data_[i] < rangemin || rangemax < data_[i]) + return false; + } + return true; + } + + protected: + // Internal recursive version of choose_nth_item. + int choose_nth_item(int target_index, int start, int end, unsigned int* seed); + + // Init the object, allocating size memory. + void init(int size); + + // We are assuming that the object generally placed in thie + // vector are small enough that for efficiency it makes sense + // to start with a larger initial size. + static const int kDefaultVectorSize = 4; + inT32 size_used_; + inT32 size_reserved_; + T* data_; + TessCallback1* clear_cb_; + // Mutable because Run method is not const + mutable TessResultCallback2* compare_cb_; +}; + +namespace tesseract { + +// Function to read a GenericVector from a whole file. +// Returns false on failure. +typedef bool (*FileReader)(const STRING& filename, GenericVector* data); +// Function to write a GenericVector to a whole file. +// Returns false on failure. +typedef bool (*FileWriter)(const GenericVector& data, + const STRING& filename); +// The default FileReader loads the whole file into the vector of char, +// returning false on error. +inline bool LoadDataFromFile(const STRING& filename, + GenericVector* data) { + bool result = false; + FILE* fp = fopen(filename.string(), "rb"); + if (fp != NULL) { + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (size > 0) { + // reserve an extra byte in case caller wants to append a '\0' character + data->reserve(size + 1); + data->resize_no_init(size); + result = fread(&(*data)[0], 1, size, fp) == size; + } + fclose(fp); + } + return result; +} +// The default FileWriter writes the vector of char to the filename file, +// returning false on error. +inline bool SaveDataToFile(const GenericVector& data, + const STRING& filename) { + FILE* fp = fopen(filename.string(), "wb"); + if (fp == NULL) return false; + bool result = + static_cast(fwrite(&data[0], 1, data.size(), fp)) == data.size(); + fclose(fp); + return result; +} + +template +bool cmp_eq(T const & t1, T const & t2) { + return t1 == t2; +} + +// Used by sort() +// return < 0 if t1 < t2 +// return 0 if t1 == t2 +// return > 0 if t1 > t2 +template +int sort_cmp(const void* t1, const void* t2) { + const T* a = static_cast (t1); + const T* b = static_cast (t2); + if (*a < *b) { + return -1; + } else if (*b < *a) { + return 1; + } else { + return 0; + } +} + +// Used by PointerVector::sort() +// return < 0 if t1 < t2 +// return 0 if t1 == t2 +// return > 0 if t1 > t2 +template +int sort_ptr_cmp(const void* t1, const void* t2) { + const T* a = *reinterpret_cast(t1); + const T* b = *reinterpret_cast(t2); + if (*a < *b) { + return -1; + } else if (*b < *a) { + return 1; + } else { + return 0; + } +} + +// Subclass for a vector of pointers. Use in preference to GenericVector +// as it provides automatic deletion and correct serialization, with the +// corollary that all copy operations are deep copies of the pointed-to objects. +template +class PointerVector : public GenericVector { + public: + PointerVector() : GenericVector() { } + explicit PointerVector(int size) : GenericVector(size) { } + ~PointerVector() { + // Clear must be called here, even though it is called again by the base, + // as the base will call the wrong clear. + clear(); + } + // Copy must be deep, as the pointers will be automatically deleted on + // destruction. + PointerVector(const PointerVector& other) : GenericVector(other) { + this->init(other.size()); + this->operator+=(other); + } + PointerVector& operator+=(const PointerVector& other) { + this->reserve(this->size_used_ + other.size_used_); + for (int i = 0; i < other.size(); ++i) { + this->push_back(new T(*other.data_[i])); + } + return *this; + } + + PointerVector& operator=(const PointerVector& other) { + if (&other != this) { + this->truncate(0); + this->operator+=(other); + } + return *this; + } + + // Removes an element at the given index and + // shifts the remaining elements to the left. + void remove(int index) { + delete GenericVector::data_[index]; + GenericVector::remove(index); + } + + // Truncates the array to the given size by removing the end. + // If the current size is less, the array is not expanded. + void truncate(int size) { + for (int i = size; i < GenericVector::size_used_; ++i) + delete GenericVector::data_[i]; + GenericVector::truncate(size); + } + + // Compact the vector by deleting elements for which delete_cb returns + // true. delete_cb is a permanent callback and will be deleted. + void compact(TessResultCallback1* delete_cb) { + int new_size = 0; + int old_index = 0; + // Until the callback returns true, the elements stay the same. + while (old_index < GenericVector::size_used_ && + !delete_cb->Run(GenericVector::data_[old_index++])) + ++new_size; + // Now just copy anything else that gets false from delete_cb. + for (; old_index < GenericVector::size_used_; ++old_index) { + if (!delete_cb->Run(GenericVector::data_[old_index])) { + GenericVector::data_[new_size++] = + GenericVector::data_[old_index]; + } else { + delete GenericVector::data_[old_index]; + } + } + GenericVector::size_used_ = new_size; + delete delete_cb; + } + + // Clear the array, calling the clear callback function if any. + // All the owned callbacks are also deleted. + // If you don't want the callbacks to be deleted, before calling clear, set + // the callback to NULL. + void clear() { + GenericVector::delete_data_pointers(); + GenericVector::clear(); + } + + // Writes a vector of (pointers to) classes to the given file. Assumes the + // existence of bool T::Serialize(FILE*) const that returns false in case of + // error. There is no Serialize for simple types, as you would have a + // normal GenericVector of those. + // Returns false in case of error. + bool Serialize(FILE* fp) const { + inT32 used = GenericVector::size_used_; + if (fwrite(&used, sizeof(used), 1, fp) != 1) return false; + for (int i = 0; i < used; ++i) { + inT8 non_null = GenericVector::data_[i] != NULL; + if (fwrite(&non_null, sizeof(non_null), 1, fp) != 1) return false; + if (non_null && !GenericVector::data_[i]->Serialize(fp)) return false; + } + return true; + } + bool Serialize(TFile* fp) const { + inT32 used = GenericVector::size_used_; + if (fp->FWrite(&used, sizeof(used), 1) != 1) return false; + for (int i = 0; i < used; ++i) { + inT8 non_null = GenericVector::data_[i] != NULL; + if (fp->FWrite(&non_null, sizeof(non_null), 1) != 1) return false; + if (non_null && !GenericVector::data_[i]->Serialize(fp)) return false; + } + return true; + } + // Reads a vector of (pointers to) classes to the given file. Assumes the + // existence of bool T::DeSerialize(bool, Tfile*) const that returns false in + // case of error. There is no Serialize for simple types, as you would have a + // normal GenericVector of those. + // If swap is true, assumes a big/little-endian swap is needed. + // Also needs T::T(), as new T is used in this function. + // Returns false in case of error. + bool DeSerialize(bool swap, FILE* fp) { + inT32 reserved; + if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; + if (swap) Reverse32(&reserved); + GenericVector::reserve(reserved); + truncate(0); + for (int i = 0; i < reserved; ++i) { + inT8 non_null; + if (fread(&non_null, sizeof(non_null), 1, fp) != 1) return false; + T* item = NULL; + if (non_null) { + item = new T; + if (!item->DeSerialize(swap, fp)) { + delete item; + return false; + } + this->push_back(item); + } else { + // Null elements should keep their place in the vector. + this->push_back(NULL); + } + } + return true; + } + bool DeSerialize(bool swap, TFile* fp) { + inT32 reserved; + if (!DeSerializeSize(swap, fp, &reserved)) return false; + GenericVector::reserve(reserved); + truncate(0); + for (int i = 0; i < reserved; ++i) { + if (!DeSerializeElement(swap, fp)) return false; + } + return true; + } + // Enables deserialization of a selection of elements. Note that in order to + // retain the integrity of the stream, the caller must call some combination + // of DeSerializeElement and DeSerializeSkip of the exact number returned in + // *size, assuming a true return. + static bool DeSerializeSize(bool swap, TFile* fp, inT32* size) { + if (fp->FRead(size, sizeof(*size), 1) != 1) return false; + if (swap) Reverse32(size); + return true; + } + // Reads and appends to the vector the next element of the serialization. + bool DeSerializeElement(bool swap, TFile* fp) { + inT8 non_null; + if (fp->FRead(&non_null, sizeof(non_null), 1) != 1) return false; + T* item = NULL; + if (non_null) { + item = new T; + if (!item->DeSerialize(swap, fp)) { + delete item; + return false; + } + this->push_back(item); + } else { + // Null elements should keep their place in the vector. + this->push_back(NULL); + } + return true; + } + // Skips the next element of the serialization. + static bool DeSerializeSkip(bool swap, TFile* fp) { + inT8 non_null; + if (fp->FRead(&non_null, sizeof(non_null), 1) != 1) return false; + if (non_null) { + if (!T::SkipDeSerialize(swap, fp)) return false; + } + return true; + } + + // Sorts the items pointed to by the members of this vector using + // t::operator<(). + void sort() { this->GenericVector::sort(&sort_ptr_cmp); } +}; + +} // namespace tesseract + +// A useful vector that uses operator== to do comparisons. +template +class GenericVectorEqEq : public GenericVector { + public: + GenericVectorEqEq() { + GenericVector::set_compare_callback( + NewPermanentTessCallback(tesseract::cmp_eq)); + } + GenericVectorEqEq(int size) : GenericVector(size) { + GenericVector::set_compare_callback( + NewPermanentTessCallback(tesseract::cmp_eq)); + } +}; + +template +void GenericVector::init(int size) { + size_used_ = 0; + size_reserved_ = 0; + data_ = 0; + clear_cb_ = 0; + compare_cb_ = 0; + reserve(size); +} + +template +GenericVector::~GenericVector() { + clear(); +} + +// Reserve some memory. If the internal array contains elements, they are +// copied. +template +void GenericVector::reserve(int size) { + if (size_reserved_ >= size || size <= 0) + return; + if (size < kDefaultVectorSize) size = kDefaultVectorSize; + T* new_array = new T[size]; + for (int i = 0; i < size_used_; ++i) + new_array[i] = data_[i]; + delete[] data_; + data_ = new_array; + size_reserved_ = size; +} + +template +void GenericVector::double_the_size() { + if (size_reserved_ == 0) { + reserve(kDefaultVectorSize); + } + else { + reserve(2 * size_reserved_); + } +} + +// Resizes to size and sets all values to t. +template +void GenericVector::init_to_size(int size, T t) { + reserve(size); + size_used_ = size; + for (int i = 0; i < size; ++i) + data_[i] = t; +} + + +// Return the object from an index. +template +T &GenericVector::get(int index) const { + ASSERT_HOST(index >= 0 && index < size_used_); + return data_[index]; +} + +template +T &GenericVector::operator[](int index) const { + assert(index >= 0 && index < size_used_); + return data_[index]; +} + +template +T &GenericVector::back() const { + ASSERT_HOST(size_used_ > 0); + return data_[size_used_ - 1]; +} +// Returns the last object and removes it. +template +T GenericVector::pop_back() { + ASSERT_HOST(size_used_ > 0); + return data_[--size_used_]; +} + +// Return the object from an index. +template +void GenericVector::set(T t, int index) { + ASSERT_HOST(index >= 0 && index < size_used_); + data_[index] = t; +} + +// Shifts the rest of the elements to the right to make +// space for the new elements and inserts the given element +// at the specified index. +template +void GenericVector::insert(T t, int index) { + ASSERT_HOST(index >= 0 && index <= size_used_); + if (size_reserved_ == size_used_) + double_the_size(); + for (int i = size_used_; i > index; --i) { + data_[i] = data_[i-1]; + } + data_[index] = t; + size_used_++; +} + +// Removes an element at the given index and +// shifts the remaining elements to the left. +template +void GenericVector::remove(int index) { + ASSERT_HOST(index >= 0 && index < size_used_); + for (int i = index; i < size_used_ - 1; ++i) { + data_[i] = data_[i+1]; + } + size_used_--; +} + +// Return true if the index is valindex +template +T GenericVector::contains_index(int index) const { + return index >= 0 && index < size_used_; +} + +// Return the index of the T object. +template +int GenericVector::get_index(T object) const { + for (int i = 0; i < size_used_; ++i) { + ASSERT_HOST(compare_cb_ != NULL); + if (compare_cb_->Run(object, data_[i])) + return i; + } + return -1; +} + +// Return true if T is in the array +template +bool GenericVector::contains(T object) const { + return get_index(object) != -1; +} + +// Add an element in the array +template +int GenericVector::push_back(T object) { + int index = 0; + if (size_used_ == size_reserved_) + double_the_size(); + index = size_used_++; + data_[index] = object; + return index; +} + +template +int GenericVector::push_back_new(T object) { + int index = get_index(object); + if (index >= 0) + return index; + return push_back(object); +} + +// Add an element in the array (front) +template +int GenericVector::push_front(T object) { + if (size_used_ == size_reserved_) + double_the_size(); + for (int i = size_used_; i > 0; --i) + data_[i] = data_[i-1]; + data_[0] = object; + ++size_used_; + return 0; +} + +template +void GenericVector::operator+=(T t) { + push_back(t); +} + +template +GenericVector &GenericVector::operator+=(const GenericVector& other) { + this->reserve(size_used_ + other.size_used_); + for (int i = 0; i < other.size(); ++i) { + this->operator+=(other.data_[i]); + } + return *this; +} + +template +GenericVector &GenericVector::operator=(const GenericVector& other) { + if (&other != this) { + this->truncate(0); + this->operator+=(other); + } + return *this; +} + +// Add a callback to be called to delete the elements when the array took +// their ownership. +template +void GenericVector::set_clear_callback(TessCallback1* cb) { + clear_cb_ = cb; +} + +// Add a callback to be called to delete the elements when the array took +// their ownership. +template +void GenericVector::set_compare_callback( + TessResultCallback2* cb) { + compare_cb_ = cb; +} + +// Clear the array, calling the callback function if any. +template +void GenericVector::clear() { + if (size_reserved_ > 0) { + if (clear_cb_ != NULL) + for (int i = 0; i < size_used_; ++i) + clear_cb_->Run(data_[i]); + delete[] data_; + data_ = NULL; + size_used_ = 0; + size_reserved_ = 0; + } + if (clear_cb_ != NULL) { + delete clear_cb_; + clear_cb_ = NULL; + } + if (compare_cb_ != NULL) { + delete compare_cb_; + compare_cb_ = NULL; + } +} + +template +void GenericVector::delete_data_pointers() { + for (int i = 0; i < size_used_; ++i) + if (data_[i]) { + delete data_[i]; + } +} + + +template +bool GenericVector::write( + FILE* f, TessResultCallback2* cb) const { + if (fwrite(&size_reserved_, sizeof(size_reserved_), 1, f) != 1) return false; + if (fwrite(&size_used_, sizeof(size_used_), 1, f) != 1) return false; + if (cb != NULL) { + for (int i = 0; i < size_used_; ++i) { + if (!cb->Run(f, data_[i])) { + delete cb; + return false; + } + } + delete cb; + } else { + if (fwrite(data_, sizeof(T), size_used_, f) != size_used_) return false; + } + return true; +} + +template +bool GenericVector::read(FILE* f, + TessResultCallback3* cb, + bool swap) { + inT32 reserved; + if (fread(&reserved, sizeof(reserved), 1, f) != 1) return false; + if (swap) Reverse32(&reserved); + reserve(reserved); + if (fread(&size_used_, sizeof(size_used_), 1, f) != 1) return false; + if (swap) Reverse32(&size_used_); + if (cb != NULL) { + for (int i = 0; i < size_used_; ++i) { + if (!cb->Run(f, data_ + i, swap)) { + delete cb; + return false; + } + } + delete cb; + } else { + if (fread(data_, sizeof(T), size_used_, f) != size_used_) return false; + if (swap) { + for (int i = 0; i < size_used_; ++i) + ReverseN(&data_[i], sizeof(T)); + } + } + return true; +} + +// Writes a vector of simple types to the given file. Assumes that bitwise +// read/write of T will work. Returns false in case of error. +template +bool GenericVector::Serialize(FILE* fp) const { + if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; + if (fwrite(data_, sizeof(*data_), size_used_, fp) != size_used_) return false; + return true; +} +template +bool GenericVector::Serialize(tesseract::TFile* fp) const { + if (fp->FWrite(&size_used_, sizeof(size_used_), 1) != 1) return false; + if (fp->FWrite(data_, sizeof(*data_), size_used_) != size_used_) return false; + return true; +} + +// Reads a vector of simple types from the given file. Assumes that bitwise +// read/write will work with ReverseN according to sizeof(T). +// Returns false in case of error. +// If swap is true, assumes a big/little-endian swap is needed. +template +bool GenericVector::DeSerialize(bool swap, FILE* fp) { + inT32 reserved; + if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; + if (swap) Reverse32(&reserved); + reserve(reserved); + size_used_ = reserved; + if (fread(data_, sizeof(T), size_used_, fp) != size_used_) return false; + if (swap) { + for (int i = 0; i < size_used_; ++i) + ReverseN(&data_[i], sizeof(data_[i])); + } + return true; +} +template +bool GenericVector::DeSerialize(bool swap, tesseract::TFile* fp) { + inT32 reserved; + if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; + if (swap) Reverse32(&reserved); + reserve(reserved); + size_used_ = reserved; + if (fp->FRead(data_, sizeof(T), size_used_) != size_used_) return false; + if (swap) { + for (int i = 0; i < size_used_; ++i) + ReverseN(&data_[i], sizeof(data_[i])); + } + return true; +} +template +bool GenericVector::SkipDeSerialize(bool swap, tesseract::TFile* fp) { + inT32 reserved; + if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; + if (swap) Reverse32(&reserved); + return fp->FRead(NULL, sizeof(T), reserved) == reserved; +} + +// Writes a vector of classes to the given file. Assumes the existence of +// bool T::Serialize(FILE* fp) const that returns false in case of error. +// Returns false in case of error. +template +bool GenericVector::SerializeClasses(FILE* fp) const { + if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; + for (int i = 0; i < size_used_; ++i) { + if (!data_[i].Serialize(fp)) return false; + } + return true; +} +template +bool GenericVector::SerializeClasses(tesseract::TFile* fp) const { + if (fp->FWrite(&size_used_, sizeof(size_used_), 1) != 1) return false; + for (int i = 0; i < size_used_; ++i) { + if (!data_[i].Serialize(fp)) return false; + } + return true; +} + +// Reads a vector of classes from the given file. Assumes the existence of +// bool T::Deserialize(bool swap, FILE* fp) that returns false in case of +// error. Also needs T::T() and T::T(constT&), as init_to_size is used in +// this function. Returns false in case of error. +// If swap is true, assumes a big/little-endian swap is needed. +template +bool GenericVector::DeSerializeClasses(bool swap, FILE* fp) { + uinT32 reserved; + if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; + if (swap) Reverse32(&reserved); + T empty; + init_to_size(reserved, empty); + for (int i = 0; i < reserved; ++i) { + if (!data_[i].DeSerialize(swap, fp)) return false; + } + return true; +} +template +bool GenericVector::DeSerializeClasses(bool swap, tesseract::TFile* fp) { + uinT32 reserved; + if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; + if (swap) Reverse32(&reserved); + T empty; + init_to_size(reserved, empty); + for (int i = 0; i < reserved; ++i) { + if (!data_[i].DeSerialize(swap, fp)) return false; + } + return true; +} +template +bool GenericVector::SkipDeSerializeClasses(bool swap, tesseract::TFile* fp) { + uinT32 reserved; + if (fp->FRead(&reserved, sizeof(reserved), 1) != 1) return false; + if (swap) Reverse32(&reserved); + for (int i = 0; i < reserved; ++i) { + if (!T::SkipDeSerialize(swap, fp)) return false; + } + return true; +} + +// This method clear the current object, then, does a shallow copy of +// its argument, and finally invalidates its argument. +template +void GenericVector::move(GenericVector* from) { + this->clear(); + this->data_ = from->data_; + this->size_reserved_ = from->size_reserved_; + this->size_used_ = from->size_used_; + this->compare_cb_ = from->compare_cb_; + this->clear_cb_ = from->clear_cb_; + from->data_ = NULL; + from->clear_cb_ = NULL; + from->compare_cb_ = NULL; + from->size_used_ = 0; + from->size_reserved_ = 0; +} + +template +void GenericVector::sort() { + sort(&tesseract::sort_cmp); +} + +// Internal recursive version of choose_nth_item. +// The algorithm used comes from "Algorithms" by Sedgewick: +// http://books.google.com/books/about/Algorithms.html?id=idUdqdDXqnAC +// The principle is to choose a random pivot, and move everything less than +// the pivot to its left, and everything greater than the pivot to the end +// of the array, then recurse on the part that contains the desired index, or +// just return the answer if it is in the equal section in the middle. +// The random pivot guarantees average linear time for the same reason that +// n times vector::push_back takes linear time on average. +// target_index, start and and end are all indices into the full array. +// Seed is a seed for rand_r for thread safety purposes. Its value is +// unimportant as the random numbers do not affect the result except +// between equal answers. +template +int GenericVector::choose_nth_item(int target_index, int start, int end, + unsigned int* seed) { + // Number of elements to process. + int num_elements = end - start; + // Trivial cases. + if (num_elements <= 1) + return start; + if (num_elements == 2) { + if (data_[start] < data_[start + 1]) { + return target_index > start ? start + 1 : start; + } else { + return target_index > start ? start : start + 1; + } + } + // Place the pivot at start. + #ifndef rand_r // _MSC_VER, ANDROID + srand(*seed); + #define rand_r(seed) rand() + #endif // _MSC_VER + int pivot = rand_r(seed) % num_elements + start; + swap(pivot, start); + // The invariant condition here is that items [start, next_lesser) are less + // than the pivot (which is at index next_lesser) and items + // [prev_greater, end) are greater than the pivot, with items + // [next_lesser, prev_greater) being equal to the pivot. + int next_lesser = start; + int prev_greater = end; + for (int next_sample = start + 1; next_sample < prev_greater;) { + if (data_[next_sample] < data_[next_lesser]) { + swap(next_lesser++, next_sample++); + } else if (data_[next_sample] == data_[next_lesser]) { + ++next_sample; + } else { + swap(--prev_greater, next_sample); + } + } + // Now the invariant is set up, we recurse on just the section that contains + // the desired index. + if (target_index < next_lesser) + return choose_nth_item(target_index, start, next_lesser, seed); + else if (target_index < prev_greater) + return next_lesser; // In equal bracket. + else + return choose_nth_item(target_index, prev_greater, end, seed); +} + + +#endif // TESSERACT_CCUTIL_GENERICVECTOR_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.cpp new file mode 100644 index 0000000..b785245 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.cpp @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////// +// File: gettimeofday.cpp +// Description: Implementation of gettimeofday based on leptonica +// Author: tomp2010, zdenop +// Created: Tue Feb 21 21:38:00 CET 2012 +// +// (C) Copyright 2012, 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 +#include "gettimeofday.h" + +int gettimeofday(struct timeval *tp, struct timezone *tzp) { + l_int32 sec, usec; + if (tp == NULL) + return -1; + + l_getCurrentTime(&sec, &usec); + tp->tv_sec = sec; + tp->tv_usec = usec; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.h b/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.h new file mode 100644 index 0000000..c34215b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/gettimeofday.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////// +// File: gettimeofday.h +// Description: Header file for gettimeofday.cpp +// Author: tomp2010, zdenop +// Created: Tue Feb 21 21:38:00 CET 2012 +// +// (C) Copyright 2012, 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 VS2008_PORT_GETTIMEOFDAY_H_ +#define VS2008_PORT_GETTIMEOFDAY_H_ + +#ifdef _WIN32 +#include // timeval is defined in here. +#endif + +typedef struct timezone tz; + +int gettimeofday(struct timeval * tp, struct timezone * tzp); + +#endif // VS2008_PORT_GETTIMEOFDAY_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.cpp new file mode 100644 index 0000000..1336613 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.cpp @@ -0,0 +1,94 @@ +/********************************************************************** + * 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 +#ifdef __linux__ +#include // For SYS_gettid. +#include // For syscall itself. +#endif +#include "allheaders.h" +#include "errcode.h" +#include "tprintf.h" + +// Size of thread-id array of pixes to keep in case of crash. +const int kMaxNumThreadPixes = 32768; + +Pix* global_crash_pixes[kMaxNumThreadPixes]; + +void SavePixForCrash(int resolution, Pix* pix) { +#ifdef __linux__ +#ifndef ANDROID + int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; +#else + int thread_id = gettid() % kMaxNumThreadPixes; +#endif + pixDestroy(&global_crash_pixes[thread_id]); + if (pix != NULL) { + Pix* clone = pixClone(pix); + pixSetXRes(clone, resolution); + pixSetYRes(clone, resolution); + global_crash_pixes[thread_id] = clone; + } +#endif +} + +// CALL ONLY from a signal handler! Writes a crash image to stderr. +void signal_exit(int signal_code) { + tprintf("Received signal %d!\n", signal_code); +#ifdef __linux__ +#ifndef ANDROID + int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; +#else + int thread_id = gettid() % kMaxNumThreadPixes; +#endif + if (global_crash_pixes[thread_id] != NULL) { + fprintf(stderr, "Crash caused by image with resolution %d\n", + pixGetYRes(global_crash_pixes[thread_id])); + fprintf(stderr, "\n"); + pixWriteStreamPng(stderr, global_crash_pixes[thread_id], 0.0); + fprintf(stderr, "\n\n"); + } + // Raise an uncaught signal, so as to get a useful stack trace. + raise(SIGILL); +#else + abort(); +#endif +} + +void err_exit() { + ASSERT_HOST("Fatal error encountered!" == NULL); +} + + +void set_global_loc_code(int loc_code) { + // global_loc_code = loc_code; + +} + + +void set_global_subloc_code(int loc_code) { + // global_subloc_code = loc_code; + +} + + +void set_global_subsubloc_code(int loc_code) { + // global_subsubloc_code = loc_code; + +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.h b/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.h new file mode 100644 index 0000000..60d6b73 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/globaloc.h @@ -0,0 +1,39 @@ +/********************************************************************** + * File: errcode.h (Formerly error.h) + * Description: Header file for generic error handler class + * Author: Ray Smith + * Created: Tue May 1 16:23:36 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GLOBALOC_H +#define GLOBALOC_H + +#include "host.h" + +// Saves a clone of the given pix, and notes its resolution in thread-specific +// data, so that the image can be written prior to a crash. +struct Pix; +void SavePixForCrash(int resolution, Pix* pix); + +void signal_exit(int signal_code); + +void TESS_API err_exit(); + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/hashfn.h b/hgdriver/3rdparty/hgOCR/include/ccutil/hashfn.h new file mode 100644 index 0000000..73e15be --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/hashfn.h @@ -0,0 +1,80 @@ +/********************************************************************** + * File: hashfn.h (Formerly hash.h) + * Description: Portability hacks for hash_map, hash_set and unique_ptr. + * Author: Ray Smith + * Created: Wed Jan 08 14:08:25 PST 2014 + * + * (C) Copyright 2014, 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 HASHFN_H +#define HASHFN_H + +#if (__cplusplus >= 201103L) || defined(_MSC_VER) // Visual Studio +#include +#include +#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1600) // VS 2008 +#define TessHashMap std::tr1::unordered_map +#define TessHashSet std::tr1::unordered_set +#else // _MSC_VER +#define TessHashMap std::unordered_map +#define TessHashSet std::unordered_set +#include +#define SmartPtr std::unique_ptr +#define HAVE_UNIQUE_PTR +#endif // _MSC_VER +#elif (defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ > 0)) || \ + __GNUC__ >= 4)) // gcc +// hash_set is deprecated in gcc +#include +#include +using __gnu_cxx::hash_map; +using __gnu_cxx::hash_set; +#define TessHashMap __gnu_cxx::hash_map +#define TessHashSet __gnu_cxx::hash_set +#else +#include +#include +#define TessHashMap hash_map +#define TessHashSet :hash_set +#endif // gcc + +#ifndef HAVE_UNIQUE_PTR +// Trivial smart ptr. Expand to add features of std::unique_ptr as required. +template class SmartPtr { + public: + SmartPtr() : ptr_(NULL) {} + explicit SmartPtr(T* ptr) : ptr_(ptr) {} + ~SmartPtr() { + delete ptr_; + } + + T* get() const { + return ptr_; + } + void reset(T* ptr) { + delete ptr_; + ptr_ = ptr; + } + bool operator==(const T* ptr) const { + return ptr_ == ptr; + } + T* operator->() const { + return ptr_; + } + private: + T* ptr_; +}; +#endif // HAVE_UNIQUE_PTR + +#endif // HASHFN_H diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/helpers.h b/hgdriver/3rdparty/hgOCR/include/ccutil/helpers.h new file mode 100644 index 0000000..a2276bc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/helpers.h @@ -0,0 +1,203 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: helpers.h + * Description: General utility functions + * Author: Daria Antonova + * Created: Wed Apr 8 14:37:00 2009 + * Language: C++ + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 2009, 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_HELPERS_H_ +#define TESSERACT_CCUTIL_HELPERS_H_ + +#include +#include + +#include "host.h" + +// TODO(rays) Put the rest of the helpers in the namespace. +namespace tesseract { + +// A simple linear congruential random number generator, using Knuth's +// constants from: +// http://en.wikipedia.org/wiki/Linear_congruential_generator. +class TRand { + public: + TRand() : seed_(1) {} + // Sets the seed to the given value. + void set_seed(uinT64 seed) { + seed_ = seed; + } + + // Returns an integer in the range 0 to MAX_INT32. + inT32 IntRand() { + Iterate(); + return seed_ >> 33; + } + // Returns a floating point value in the range [-range, range]. + double SignedRand(double range) { + return range * 2.0 * IntRand() / MAX_INT32 - range; + } + // Returns a floating point value in the range [0, range]. + double UnsignedRand(double range) { + return range * IntRand() / MAX_INT32; + } + + private: + // Steps the generator to the next value. + void Iterate() { + seed_ *= 6364136223846793005ULL; + seed_ += 1442695040888963407ULL; + } + + // The current value of the seed. + uinT64 seed_; +}; + +} // namespace tesseract + +// Remove newline (if any) at the end of the string. +inline void chomp_string(char *str) { + int last_index = static_cast(strlen(str)) - 1; + while (last_index >= 0 && + (str[last_index] == '\n' || str[last_index] == '\r')) { + str[last_index--] = '\0'; + } +} + +// Advance the current pointer of the file if it points to a newline character. +inline void SkipNewline(FILE *file) { + if (fgetc(file) != '\n') fseek(file, -1, SEEK_CUR); +} + +// Swaps the two args pointed to by the pointers. +// Operator= and copy constructor must work on T. +template inline void Swap(T* p1, T* p2) { + T tmp(*p2); + *p2 = *p1; + *p1 = tmp; +} + +// qsort function to sort 2 floats. +inline int sort_floats(const void *arg1, const void *arg2) { + float diff = *((float *) arg1) - *((float *) arg2); + if (diff > 0) { + return 1; + } else if (diff < 0) { + return -1; + } else { + return 0; + } +} + +// return the smallest multiple of block_size greater than or equal to n. +inline int RoundUp(int n, int block_size) { + return block_size * ((n + block_size - 1) / block_size); +} + +// Clip a numeric value to the interval [lower_bound, upper_bound]. +template +inline T ClipToRange(const T& x, const T& lower_bound, const T& upper_bound) { + if (x < lower_bound) + return lower_bound; + if (x > upper_bound) + return upper_bound; + return x; +} + +// Extend the range [lower_bound, upper_bound] to include x. +template +inline void UpdateRange(const T1& x, T2* lower_bound, T2* upper_bound) { + if (x < *lower_bound) + *lower_bound = x; + if (x > *upper_bound) + *upper_bound = x; +} + +// Decrease lower_bound to be <= x_lo AND increase upper_bound to be >= x_hi. +template +inline void UpdateRange(const T1& x_lo, const T1& x_hi, + T2* lower_bound, T2* upper_bound) { + if (x_lo < *lower_bound) + *lower_bound = x_lo; + if (x_hi > *upper_bound) + *upper_bound = x_hi; +} + +// Intersect the range [*lower2, *upper2] with the range [lower1, upper1], +// putting the result back in [*lower2, *upper2]. +// If non-intersecting ranges are given, we end up with *lower2 > *upper2. +template +inline void IntersectRange(const T& lower1, const T& upper1, + T* lower2, T* upper2) { + if (lower1 > *lower2) + *lower2 = lower1; + if (upper1 < *upper2) + *upper2 = upper1; +} + +// Proper modulo arithmetic operator. Returns a mod b that works for -ve a. +// For any integer a and positive b, returns r : 0<=r= 0 ? (a + b / 2) / b : (a - b / 2) / b; +} + +// Return a double cast to int with rounding. +inline int IntCastRounded(double x) { + return x >= 0.0 ? static_cast(x + 0.5) : -static_cast(-x + 0.5); +} + +// Reverse the order of bytes in a n byte quantity for big/little-endian switch. +inline void ReverseN(void* ptr, int num_bytes) { + char *cptr = reinterpret_cast(ptr); + int halfsize = num_bytes / 2; + for (int i = 0; i < halfsize; ++i) { + char tmp = cptr[i]; + cptr[i] = cptr[num_bytes - 1 - i]; + cptr[num_bytes - 1 - i] = tmp; + } +} + +// Reverse the order of bytes in a 16 bit quantity for big/little-endian switch. +inline void Reverse16(void *ptr) { + ReverseN(ptr, 2); +} + +// Reverse the order of bytes in a 32 bit quantity for big/little-endian switch. +inline void Reverse32(void *ptr) { + ReverseN(ptr, 4); +} + +// Reverse the order of bytes in a 64 bit quantity for big/little-endian switch. +inline void Reverse64(void* ptr) { + ReverseN(ptr, 8); +} + + +#endif // TESSERACT_CCUTIL_HELPERS_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/host.h b/hgdriver/3rdparty/hgOCR/include/ccutil/host.h new file mode 100644 index 0000000..ff9ca8c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/host.h @@ -0,0 +1,82 @@ +/****************************************************************************** + ** Filename: host.h + ** Purpose: This is the system independent typedefs and defines + ** Author: MN, JG, MD + ** + ** (c) Copyright Hewlett-Packard Company, 1988-1996. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef __HOST__ +#define __HOST__ + +#include "platform.h" +/* _WIN32 */ +#ifdef _WIN32 +#include +#include // winbase.h contains windows.h +#endif + +#include // int32_t, ... + +// definitions of portable data types (numbers and characters) +typedef SIGNED char inT8; +typedef unsigned char uinT8; +typedef short inT16; +typedef unsigned short uinT16; +typedef int inT32; +typedef unsigned int uinT32; +#if (_MSC_VER >= 1200) //%%% vkr for VC 6.0 +typedef INT64 inT64; +typedef UINT64 uinT64; +#else +typedef long long int inT64; +typedef unsigned long long int uinT64; +#endif //%%% vkr for VC 6.0 +typedef float FLOAT32; +typedef double FLOAT64; +typedef unsigned char BOOL8; + +#define INT32FORMAT "%d" +#define INT64FORMAT "%lld" + +#define MAX_INT8 0x7f +#define MAX_INT16 0x7fff +#define MAX_INT32 0x7fffffff +#define MAX_UINT8 0xff +#define MAX_UINT16 0xffff +#define MAX_UINT32 0xffffffff +#define MAX_FLOAT32 ((float)3.40282347e+38) + +#define MIN_INT8 0x80 +#define MIN_INT16 0x8000 +#define MIN_INT32 static_cast(0x80000000) +#define MIN_UINT8 0x00 +#define MIN_UINT16 0x0000 +#define MIN_UINT32 0x00000000 +#define MIN_FLOAT32 ((float)1.17549435e-38) + +// Defines +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +// Return true if x is within tolerance of y +template bool NearlyEqual(T x, T y, T tolerance) { + T diff = x - y; + return diff <= tolerance && -diff <= tolerance; +} + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.cpp new file mode 100644 index 0000000..48bd905 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.cpp @@ -0,0 +1,250 @@ +/////////////////////////////////////////////////////////////////////// +// File: indexmapbidi.cpp +// Description: Bi-directional mapping between a sparse and compact space. +// Author: rays@google.com (Ray Smith) +// Created: Tue Apr 06 11:33:59 PDT 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 "indexmapbidi.h" + +namespace tesseract { + +// SparseToCompact takes a sparse index to an index in the compact space. +// Uses a binary search to find the result. For faster speed use +// IndexMapBiDi, but that takes more memory. +int IndexMap::SparseToCompact(int sparse_index) const { + int result = compact_map_.binary_search(sparse_index); + return compact_map_[result] == sparse_index ? result : -1; +} + +// Copy from the input. +void IndexMap::CopyFrom(const IndexMap& src) { + sparse_size_ = src.sparse_size_; + compact_map_ = src.compact_map_; +} +void IndexMap::CopyFrom(const IndexMapBiDi& src) { + sparse_size_ = src.SparseSize(); + compact_map_ = src.compact_map_; +} + +// Writes to the given file. Returns false in case of error. +bool IndexMap::Serialize(FILE* fp) const { + inT32 sparse_size = sparse_size_; + if (fwrite(&sparse_size, sizeof(sparse_size), 1, fp) != 1) return false; + if (!compact_map_.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 IndexMap::DeSerialize(bool swap, FILE* fp) { + inT32 sparse_size; + if (fread(&sparse_size, sizeof(sparse_size), 1, fp) != 1) return false; + if (swap) + ReverseN(&sparse_size, sizeof(sparse_size)); + sparse_size_ = sparse_size; + if (!compact_map_.DeSerialize(swap, fp)) return false; + return true; +} + + +// Top-level init function in a single call to initialize a map to select +// a single contiguous subrange [start, end) of the sparse space to be mapped +// 1 to 1 to the compact space, with all other elements of the sparse space +// left unmapped. +// No need to call Setup after this. +void IndexMapBiDi::InitAndSetupRange(int sparse_size, int start, int end) { + Init(sparse_size, false); + for (int i = start; i < end; ++i) + SetMap(i, true); + Setup(); +} + +// Initializes just the sparse_map_ to the given size with either all +// forward indices mapped (all_mapped = true) or none (all_mapped = false). +// Call Setup immediately after, or make calls to SetMap first to adjust the +// mapping and then call Setup before using the map. +void IndexMapBiDi::Init(int size, bool all_mapped) { + sparse_map_.init_to_size(size, -1); + if (all_mapped) { + for (int i = 0; i < size; ++i) + sparse_map_[i] = i; + } +} + +// Sets a given index in the sparse_map_ to be mapped or not. +void IndexMapBiDi::SetMap(int sparse_index, bool mapped) { + sparse_map_[sparse_index] = mapped ? 0 : -1; +} + +// Sets up the sparse_map_ and compact_map_ properly after Init and +// some calls to SetMap. Assumes an ordered 1-1 map from set indices +// in the forward map to the compact space. +void IndexMapBiDi::Setup() { + int compact_size = 0; + for (int i = 0; i < sparse_map_.size(); ++i) { + if (sparse_map_[i] >= 0) { + sparse_map_[i] = compact_size++; + } + } + compact_map_.init_to_size(compact_size, -1); + for (int i = 0; i < sparse_map_.size(); ++i) { + if (sparse_map_[i] >= 0) { + compact_map_[sparse_map_[i]] = i; + } + } + sparse_size_ = sparse_map_.size(); +} + +// Copy from the input. +void IndexMapBiDi::CopyFrom(const IndexMapBiDi& src) { + sparse_map_ = src.sparse_map_; + compact_map_ = src.compact_map_; + sparse_size_ = sparse_map_.size(); +} + +// Merges the two compact space indices. May be called many times, but +// the merges must be concluded by a call to CompleteMerges. +// Returns true if a merge was actually performed. +bool IndexMapBiDi::Merge(int compact_index1, int compact_index2) { + // Find the current master index for index1 and index2. + compact_index1 = MasterCompactIndex(compact_index1); + compact_index2 = MasterCompactIndex(compact_index2); + // Be sure that index1 < index2. + if (compact_index1 > compact_index2) { + int tmp = compact_index1; + compact_index1 = compact_index2; + compact_index2 = tmp; + } else if (compact_index1 == compact_index2) { + return false; + } + // To save iterating over all sparse_map_ entries, simply make the master + // entry for index2 point to index1. + // This leaves behind a potential chain of parents that needs to be chased, + // as above. + sparse_map_[compact_map_[compact_index2]] = compact_index1; + if (compact_index1 >= 0) + compact_map_[compact_index2] = compact_map_[compact_index1]; + return true; +} + +// Completes one or more Merge operations by further compacting the +// compact space. Unused compact space indices are removed, and the used +// ones above shuffled down to fill the gaps. +// Example: +// Input sparse_map_: (x indicates -1) +// x x 0 x 2 x x 4 x 0 x 2 x +// Output sparse_map_: +// x x 0 x 1 x x 2 x 0 x 1 x +// Output compact_map_: +// 2 4 7. +void IndexMapBiDi::CompleteMerges() { + // Ensure each sparse_map_entry contains a master compact_map_ index. + int compact_size = 0; + for (int i = 0; i < sparse_map_.size(); ++i) { + int compact_index = MasterCompactIndex(sparse_map_[i]); + sparse_map_[i] = compact_index; + if (compact_index >= compact_size) + compact_size = compact_index + 1; + } + // Re-generate the compact_map leaving holes for unused indices. + compact_map_.init_to_size(compact_size, -1); + for (int i = 0; i < sparse_map_.size(); ++i) { + if (sparse_map_[i] >= 0) { + if (compact_map_[sparse_map_[i]] == -1) + compact_map_[sparse_map_[i]] = i; + } + } + // Compact the compact_map, leaving tmp_compact_map saying where each + // index went to in the compacted map. + GenericVector tmp_compact_map; + tmp_compact_map.init_to_size(compact_size, -1); + compact_size = 0; + for (int i = 0; i < compact_map_.size(); ++i) { + if (compact_map_[i] >= 0) { + tmp_compact_map[i] = compact_size; + compact_map_[compact_size++] = compact_map_[i]; + } + } + compact_map_.truncate(compact_size); + // Now modify the entries in the sparse map to point to the new locations. + for (int i = 0; i < sparse_map_.size(); ++i) { + if (sparse_map_[i] >= 0) { + sparse_map_[i] = tmp_compact_map[sparse_map_[i]]; + } + } +} + +// Writes to the given file. Returns false in case of error. +bool IndexMapBiDi::Serialize(FILE* fp) const { + if (!IndexMap::Serialize(fp)) return false; + // Make a vector containing the rest of the map. If the map is many-to-one + // then each additional sparse entry needs to be stored. + // Normally we store only the compact map to save space. + GenericVector remaining_pairs; + for (int i = 0; i < sparse_map_.size(); ++i) { + if (sparse_map_[i] >= 0 && compact_map_[sparse_map_[i]] != i) { + remaining_pairs.push_back(i); + remaining_pairs.push_back(sparse_map_[i]); + } + } + if (!remaining_pairs.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 IndexMapBiDi::DeSerialize(bool swap, FILE* fp) { + if (!IndexMap::DeSerialize(swap, fp)) return false; + GenericVector remaining_pairs; + if (!remaining_pairs.DeSerialize(swap, fp)) return false; + sparse_map_.init_to_size(sparse_size_, -1); + for (int i = 0; i < compact_map_.size(); ++i) { + sparse_map_[compact_map_[i]] = i; + } + for (int i = 0; i < remaining_pairs.size(); ++i) { + int sparse_index = remaining_pairs[i++]; + sparse_map_[sparse_index] = remaining_pairs[i]; + } + return true; +} + +// Bulk calls to SparseToCompact. +// Maps the given array of sparse indices to an array of compact indices. +// Assumes the input is sorted. The output indices are sorted and uniqued. +// Return value is the number of "missed" features, being features that +// don't map to the compact feature space. +int IndexMapBiDi::MapFeatures(const GenericVector& sparse, + GenericVector* compact) const { + compact->truncate(0); + int num_features = sparse.size(); + int missed_features = 0; + int prev_good_feature = -1; + for (int f = 0; f < num_features; ++f) { + int feature = sparse_map_[sparse[f]]; + if (feature >= 0) { + if (feature != prev_good_feature) { + compact->push_back(feature); + prev_good_feature = feature; + } + } else { + ++missed_features; + } + } + return missed_features; +} + +} // namespace tesseract. + diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.h b/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.h new file mode 100644 index 0000000..5059513 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/indexmapbidi.h @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////// +// File: indexmapbidi.h +// Description: Bi-directional mapping between a sparse and compact space. +// Author: rays@google.com (Ray Smith) +// Created: Tue Apr 06 11:33:59 PDT 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_INDEXMAPBIDI_H_ +#define TESSERACT_CCUTIL_INDEXMAPBIDI_H_ + +#include +#include "genericvector.h" + +namespace tesseract { + +class IndexMapBiDi; + +// Bidirectional one-to-one mapping between a sparse and a compact discrete +// space. Many entries in the sparse space are unmapped, but those that are +// mapped have a 1-1 mapping to (and from) the compact space, where all +// values are used. This is useful for forming subsets of larger collections, +// such as subsets of character sets, or subsets of binary feature spaces. +// +// This base class provides basic functionality with binary search for the +// SparseToCompact mapping to save memory. +// For a faster inverse mapping, or to allow a many-to-one mapping, use +// IndexMapBiDi below. +// NOTE: there are currently no methods to setup an IndexMap on its own! +// It must be initialized by copying from an IndexMapBiDi or by DeSerialize. +class IndexMap { + public: + virtual ~IndexMap() {} + + // SparseToCompact takes a sparse index to an index in the compact space. + // Uses a binary search to find the result. For faster speed use + // IndexMapBiDi, but that takes more memory. + virtual int SparseToCompact(int sparse_index) const; + + // CompactToSparse takes a compact index to the corresponding index in the + // sparse space. + int CompactToSparse(int compact_index) const { + return compact_map_[compact_index]; + } + // The size of the sparse space. + virtual int SparseSize() const { + return sparse_size_; + } + // The size of the compact space. + int CompactSize() const { + return compact_map_.size(); + } + + // Copy from the input. + void CopyFrom(const IndexMap& src); + void CopyFrom(const IndexMapBiDi& src); + + // 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); + + protected: + // The sparse space covers integers in the range [0, sparse_size_-1]. + int sparse_size_; + // The compact space covers integers in the range [0, compact_map_.size()-1]. + // Each element contains the corresponding sparse index. + GenericVector compact_map_; +}; + +// Bidirectional many-to-one mapping between a sparse and a compact discrete +// space. As with IndexMap, many entries may be unmapped, but unlike IndexMap, +// of those that are, many may be mapped to the same compact index. +// If the map is many-to-one, it is not possible to directly obtain all the +// sparse indices that map to a single compact index. +// This map is time- rather than space-efficient. It stores the entire sparse +// space. +// IndexMapBiDi may be initialized in one of 3 ways: +// 1. Init(size, true); +// Setup(); +// Sets a complete 1:1 mapping with no unmapped elements. +// 2. Init(size, false); +// for ... SetMap(index, true); +// Setup(); +// Specifies precisely which sparse indices are mapped. The mapping is 1:1. +// 3. Either of the above, followed by: +// for ... Merge(index1, index2); +// CompleteMerges(); +// Allows a many-to-one mapping by merging compact space indices. +class IndexMapBiDi : public IndexMap { + public: + virtual ~IndexMapBiDi() {} + + // Top-level init function in a single call to initialize a map to select + // a single contiguous subrange [start, end) of the sparse space to be mapped + // 1 to 1 to the compact space, with all other elements of the sparse space + // left unmapped. + // No need to call Setup after this. + void InitAndSetupRange(int sparse_size, int start, int end); + + // Initializes just the sparse_map_ to the given size with either all + // forward indices mapped (all_mapped = true) or none (all_mapped = false). + // Call Setup immediately after, or make calls to SetMap first to adjust the + // mapping and then call Setup before using the map. + void Init(int size, bool all_mapped); + // Sets a given index in the sparse_map_ to be mapped or not. + void SetMap(int sparse_index, bool mapped); + // Sets up the sparse_map_ and compact_map_ properly after Init and + // some calls to SetMap. Assumes an ordered 1-1 map from set indices + // in the sparse space to the compact space. + void Setup(); + + // Merges the two compact space indices. May be called many times, but + // the merges must be concluded by a call to CompleteMerges. + // Returns true if a merge was actually performed. + bool Merge(int compact_index1, int compact_index2); + // Returns true if the given compact index has been deleted. + bool IsCompactDeleted(int index) const { + return MasterCompactIndex(index) < 0; + } + // Completes one or more Merge operations by further compacting the + // compact space. + void CompleteMerges(); + + // SparseToCompact takes a sparse index to an index in the compact space. + virtual int SparseToCompact(int sparse_index) const { + return sparse_map_[sparse_index]; + } + // The size of the sparse space. + virtual int SparseSize() const { + return sparse_map_.size(); + } + + // Copy from the input. + void CopyFrom(const IndexMapBiDi& src); + + // 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); + + // Bulk calls to SparseToCompact. + // Maps the given array of sparse indices to an array of compact indices. + // Assumes the input is sorted. The output indices are sorted and uniqued. + // Return value is the number of "missed" features, being features that + // don't map to the compact feature space. + int MapFeatures(const GenericVector& sparse, + GenericVector* compact) const; + + private: + // Returns the master compact index for a given compact index. + // During a multiple merge operation, several compact indices may be + // combined, so we need to be able to find the master of all. + int MasterCompactIndex(int compact_index) const { + while (compact_index >= 0 && + sparse_map_[compact_map_[compact_index]] != compact_index) + compact_index = sparse_map_[compact_map_[compact_index]]; + return compact_index; + } + + // Direct look-up of the compact index for each element in sparse space. + GenericVector sparse_map_; +}; + +} // namespace tesseract. + +#endif // TESSERACT_CCUTIL_INDEXMAPBIDI_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/kdpair.h b/hgdriver/3rdparty/hgOCR/include/ccutil/kdpair.h new file mode 100644 index 0000000..bb7743e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/kdpair.h @@ -0,0 +1,189 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: kdpair.h +// Description: Template pair class like STL pair but geared towards +// the Key+Data design pattern in which some data needs +// to be sorted or kept in a heap sorted on some separate key. +// Author: Ray Smith. +// Created: Thu Mar 15 14:48:05 PDT 2012 +// +// (C) Copyright 2012, 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_KDPAIR_H_ +#define TESSERACT_CCUTIL_KDPAIR_H_ + +#include "genericvector.h" + +namespace tesseract { + +// A useful base struct to facilitate the common operation of sorting a vector +// of simple or smart-pointer data using a separate key. Similar to STL pair. +template +struct KDPair { + KDPair() {} + KDPair(Key k, Data d) : data(d), key(k) {} + + int operator==(const KDPair& other) const { + return key == other.key; + } + + // WARNING! Keep data as the first element! KDPairInc and KDPairDec depend + // on the order of these elements so they can downcast pointers appropriately + // for use by GenericHeap::Reshuffle. + Data data; + Key key; +}; +// Specialization of KDPair to provide operator< for sorting in increasing order +// and recasting of data pointers for use with DoublePtr. +template +struct KDPairInc : public KDPair { + KDPairInc() {} + KDPairInc(Key k, Data d) : KDPair(k, d) {} + // Operator< facilitates sorting in increasing order. + int operator<(const KDPairInc& other) const { + return this->key < other.key; + } + // Returns the input Data pointer recast to a KDPairInc pointer. + // Just casts a pointer to the first element to a pointer to the whole struct. + static KDPairInc* RecastDataPointer(Data* data_ptr) { + return reinterpret_cast(data_ptr); + } +}; +// Specialization of KDPair to provide operator< for sorting in decreasing order +// and recasting of data pointers for use with DoublePtr. +template +struct KDPairDec : public KDPair { + KDPairDec() {} + KDPairDec(Key k, Data d) : KDPair(k, d) {} + // Operator< facilitates sorting in decreasing order by using operator> on + // the key values. + int operator<(const KDPairDec& other) const { + return this->key > other.key; + } + // Returns the input Data pointer recast to a KDPairDec pointer. + // Just casts a pointer to the first element to a pointer to the whole struct. + static KDPairDec* RecastDataPointer(Data* data_ptr) { + return reinterpret_cast(data_ptr); + } +}; + +// A useful base class to facilitate the common operation of sorting a vector +// of owned pointer data using a separate key. This class owns its data pointer, +// deleting it when it has finished with it, and providing copy constructor and +// operator= that have move semantics so that the data does not get copied and +// only a single instance of KDPtrPair holds a specific data pointer. +template +class KDPtrPair { + public: + KDPtrPair() : data_(NULL) {} + KDPtrPair(Key k, Data* d) : data_(d), key_(k) {} + // Copy constructor steals the pointer from src and NULLs it in src, thereby + // moving the (single) ownership of the data. + KDPtrPair(KDPtrPair& src) : data_(src.data_), key_(src.key_) { + src.data_ = NULL; + } + // Destructor deletes data, assuming it is the sole owner. + ~KDPtrPair() { + delete this->data_; + this->data_ = NULL; + } + // Operator= steals the pointer from src and NULLs it in src, thereby + // moving the (single) ownership of the data. + void operator=(KDPtrPair& src) { + delete this->data_; + this->data_ = src.data_; + src.data_ = NULL; + this->key_ = src.key_; + } + + int operator==(const KDPtrPair& other) const { + return key_ == other.key_; + } + + // Accessors. + const Key& key() const { + return key_; + } + void set_key(const Key& new_key) { + key_ = new_key; + } + const Data* data() const { + return data_; + } + // Sets the data pointer, taking ownership of the data. + void set_data(Data* new_data) { + delete data_; + data_ = new_data; + } + // Relinquishes ownership of the data pointer (setting it to NULL). + Data* extract_data() { + Data* result = data_; + data_ = NULL; + return result; + } + + private: + // Data members are private to keep deletion of data_ encapsulated. + Data* data_; + Key key_; +}; +// Specialization of KDPtrPair to provide operator< for sorting in increasing +// order. +template +struct KDPtrPairInc : public KDPtrPair { + // Since we are doing non-standard stuff we have to duplicate *all* the + // constructors and operator=. + KDPtrPairInc() : KDPtrPair() {} + KDPtrPairInc(Key k, Data* d) : KDPtrPair(k, d) {} + KDPtrPairInc(KDPtrPairInc& src) : KDPtrPair(src) {} + void operator=(KDPtrPairInc& src) { + KDPtrPair::operator=(src); + } + // Operator< facilitates sorting in increasing order. + int operator<(const KDPtrPairInc& other) const { + return this->key() < other.key(); + } +}; +// Specialization of KDPtrPair to provide operator< for sorting in decreasing +// order. +template +struct KDPtrPairDec : public KDPtrPair { + // Since we are doing non-standard stuff we have to duplicate *all* the + // constructors and operator=. + KDPtrPairDec() : KDPtrPair() {} + KDPtrPairDec(Key k, Data* d) : KDPtrPair(k, d) {} + KDPtrPairDec(KDPtrPairDec& src) : KDPtrPair(src) {} + void operator=(KDPtrPairDec& src) { + KDPtrPair::operator=(src); + } + // Operator< facilitates sorting in decreasing order by using operator> on + // the key values. + int operator<(const KDPtrPairDec& other) const { + return this->key() > other.key(); + } +}; + +// Specialization for a pair of ints in increasing order. +typedef KDPairInc IntKDPair; + +// Vector of IntKDPair. +class KDVector : public GenericVector { + // TODO(rays) Add some code to manipulate a KDVector. For now there + // is nothing and this class is effectively a specialization typedef. +}; + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_KDPAIR_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/lsterr.h b/hgdriver/3rdparty/hgOCR/include/ccutil/lsterr.h new file mode 100644 index 0000000..82497ae --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/lsterr.h @@ -0,0 +1,43 @@ +/********************************************************************** + * File: lsterr.h (Formerly listerr.h) + * Description: Errors shared by list modules + * Author: Phil Cheatle + * Created: Wed Jan 23 09:10:35 GMT 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "errcode.h" //must be last include + +#ifndef LSTERR_H +#define LSTERR_H + +const ERRCODE DONT_CONSTRUCT_LIST_BY_COPY = +"Can't create a list by assignment"; +const ERRCODE DONT_ASSIGN_LISTS = "Can't assign to lists"; +const ERRCODE SERIALISE_LINKS = "Attempted to (de)serialise a link element"; + +#ifndef NDEBUG + +const ERRCODE NO_LIST = "Iterator not set to a list"; +const ERRCODE NULL_OBJECT = "List found this = NULL!"; +const ERRCODE NULL_DATA = "List would have returned a NULL data pointer"; +const ERRCODE NULL_CURRENT = "List current position is NULL"; +const ERRCODE NULL_NEXT = "Next element on the list is NULL"; +const ERRCODE NULL_PREV = "Previous element on the list is NULL"; +const ERRCODE EMPTY_LIST = "List is empty"; +const ERRCODE BAD_PARAMETER = "List parameter error"; +const ERRCODE STILL_LINKED = + "Attempting to add an element with non NULL links, to a list"; +#endif +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/mainblk.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/mainblk.cpp new file mode 100644 index 0000000..0de3d05 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/mainblk.cpp @@ -0,0 +1,110 @@ +/********************************************************************** + * 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 "fileerr.h" +#ifdef __UNIX__ +#include +#include +#else +#include +#endif +#include +#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. + **********************************************************************/ + + /** + * @brief CCUtil::main_setup - set location of tessdata and name of image + * + * @param argv0 - paths to the directory with language files and config files. + * An actual value of argv0 is used if not NULL, otherwise TESSDATA_PREFIX is + * used if not NULL, next try to use compiled in -DTESSDATA_PREFIX. If previous + * is not successful - use current directory. + * @param basename - name of image + */ + void CCUtil::main_setup(const char *argv0, const char *basename) { + imagebasename = basename; /**< name of image */ + + char *tessdata_prefix = getenv("TESSDATA_PREFIX"); + + if (argv0 != NULL) { + /* Use tessdata prefix from the command line. */ + datadir = argv0; + } + else if (tessdata_prefix) { + /* Use tessdata prefix from the environment. */ + datadir = tessdata_prefix; +#if defined(_WIN32) + } + else if (datadir == NULL || access(datadir.string(), 0) != 0) { + /* Look for tessdata in directory of executable. */ + static char dir[128]; + static char exe[128]; + DWORD length = GetModuleFileName(NULL, exe, sizeof(exe)); + if (length > 0 && length < sizeof(exe)) { + _splitpath(exe, NULL, dir, NULL, NULL); + datadir = dir; + } +#endif /* _WIN32 */ +#if defined(TESSDATA_PREFIX) + } + else { + /* Use tessdata prefix which was compiled in. */ +#define _STR(a) #a +#define _XSTR(a) _STR(a) + datadir = _XSTR(TESSDATA_PREFIX); +#undef _XSTR +#undef _STR +#endif + } + + // datadir may still be empty: + if (datadir.length() == 0) { + datadir = "./"; + } + else { + // Remove tessdata from the end if present, as we will add it back! + int length = datadir.length(); + if (length >= 8 && strcmp(&datadir[length - 8], "tessdata") == 0) + datadir.truncate_at(length - 8); + else if (length >= 9 && strcmp(&datadir[length - 9], "tessdata/") == 0) + datadir.truncate_at(length - 9); + } + + // check for missing directory separator + const char *lastchar = datadir.string(); + lastchar += datadir.length() - 1; + if ((strcmp(lastchar, "/") != 0) && (strcmp(lastchar, "\\") != 0)) + datadir += "/"; + + datadir += m_data_sub_dir; /**< data directory */ + } +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/memry.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/memry.cpp new file mode 100644 index 0000000..925efda --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/memry.cpp @@ -0,0 +1,61 @@ +/********************************************************************** + * File: memry.c (Formerly memory.c) + * Description: Memory allocation with builtin safety checks. + * Author: Ray Smith + * Created: Wed Jan 22 09:43:33 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "memry.h" +#include + +// With improvements in OS memory allocators, internal memory management +// is no longer required, so all these functions now map to their malloc +// family equivalents. + +// TODO(rays) further cleanup by redirecting calls to new and creating proper +// constructors. + +char *alloc_string(inT32 count) { + // Round up the amount allocated to a multiple of 4 + return static_cast(malloc((count + 3) & ~3)); +} + +void free_string(char *string) { + free(string); +} + +void* alloc_struct(inT32 count, const char *) { + return malloc(count); +} + +void free_struct(void *deadstruct, inT32, const char *) { + free(deadstruct); +} + +void *alloc_mem(inT32 count) { + return malloc(static_cast(count)); +} + +void *alloc_big_zeros(inT32 count) { + return calloc(static_cast(count), 1); +} + +void free_mem(void *oldchunk) { + free(oldchunk); +} + +void free_big_mem(void *oldchunk) { + free(oldchunk); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/memry.h b/hgdriver/3rdparty/hgOCR/include/ccutil/memry.h new file mode 100644 index 0000000..66e146e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/memry.h @@ -0,0 +1,43 @@ +/********************************************************************** + * File: memry.h (Formerly memory.h) + * Description: Header file for basic memory allocation/deallocation. + * Author: Ray Smith + * Created: Tue May 8 16:03:48 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MEMRY_H +#define MEMRY_H + +#include +#include "host.h" + +// allocate string +extern char *alloc_string(inT32 count); +// free a string. +extern void free_string(char *string); +// allocate memory +extern void *alloc_struct(inT32 count, const char *name = NULL); +// free a structure. +extern void free_struct(void *deadstruct, inT32, const char *name = NULL); +// get some memory +extern void *alloc_mem(inT32 count); +// get some memory initialized to 0. +extern void *alloc_big_zeros(inT32 count); +// free mem from alloc_mem +extern void free_mem(void *oldchunk); +// free mem from alloc_big_zeros +extern void free_big_mem(void *oldchunk); + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ndminx.h b/hgdriver/3rdparty/hgOCR/include/ccutil/ndminx.h new file mode 100644 index 0000000..9eee76c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ndminx.h @@ -0,0 +1,31 @@ +/********************************************************************** + * File: ndminx.h (Formerly ndminmax.h) + * Description: Extended ascii chars + * Author: Phil Cheatle + * Created: Mon Mar 29 14:46:01 BST 1993 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef NDMINX_H +#define NDMINX_H + +#ifndef MAX +#define MAX(x,y) (((x) >= (y))?(x):(y)) +#endif + +#ifndef MIN +#define MIN(x,y) (((x) <= (y))?(x):(y)) +#endif + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/nwmain.h b/hgdriver/3rdparty/hgOCR/include/ccutil/nwmain.h new file mode 100644 index 0000000..a9b1926 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/nwmain.h @@ -0,0 +1,175 @@ +/********************************************************************** + * File: nwmain.h + * Description: Tool to declare main, making windows invisible. + * Author: Ray Smith + * Created: Fri Sep 07 13:27:50 MDT 1995 + * + * (C) Copyright 1995, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RUNMAIN_H +#define RUNMAIN_H + +#include "host.h" +#include "params.h" + +#define DECLARE_MAIN(ARGC,ARGV)\ +STRING_VAR(init_config_file,"config","Config file to read on startup");\ +REALLY_DECLARE_MAIN(ARGC,ARGV) + +#define DECLARE_MAIN_CONFIG(ARGC,ARGV,NAME)\ +STRING_VAR(init_config_file,NAME,"Config file to read on startup");\ +REALLY_DECLARE_MAIN(ARGC,ARGV) + +#ifndef __UNIX__ + +#define REALLY_DECLARE_MAIN(ARGC,ARGV)\ +\ +/**********************************************************************\ +* parse_args\ +*\ +* Turn a list of args into a new list of args with each separate\ +* whitespace spaced string being an arg.\ +**********************************************************************/\ +\ +inT32 parse_args( /*refine arg list*/\ +inT32 argc, /*no of input args*/\ +char *argv[], /*input args*/\ +char *arglist[] /*output args*/\ +)\ +{\ + inT32 argcount; /*converted argc*/\ + char *testchar; /*char in option string*/\ + inT32 arg; /*current argument*/\ +\ + argcount=0; /*no of options*/\ + for (arg=0;argm_pszExeName);\ + argsin[1]=strdup(theapp->m_lpCmdLine);\ +/*allocate memory for the args. There can never be more than half*/\ +/*the total number of characters in the arguments.*/\ + argv=(char**)malloc(((strlen(argsin[0])+strlen(argsin[1]))/2+1)*sizeof(char*));\ +\ +/*now construct argv as it should be for C.*/\ + argc=parse_args(2,argsin,argv);\ +\ +/*call main(argc,argv) here*/\ + exit_code=real_main(argc,(const char **)argv);\ +\ +\ +/*now get rid of the main app window*/\ + if (theapp!=NULL && theapp->m_pMainWnd!=NULL)\ + PostMessage(theapp->m_pMainWnd->m_hWnd,WM_QUIT,0,0);\ + free(argsin[0]);\ + free(argsin[1]);\ + free(argv);\ + global_exit_code=exit_code;\ + return exit_code;\ +}\ +\ +inT32 real_main(inT32 ARGC,const char* ARGV[])\ + +#else + +#define REALLY_DECLARE_MAIN(ARGC,ARGV)\ +\ +/**********************************************************************\ +* parse_args\ +*\ +* Turn a list of args into a new list of args with each separate\ +* whitespace spaced string being an arg.\ +**********************************************************************/\ +\ +inT32 parse_args( /*refine arg list*/\ +inT32 argc, /*no of input args*/\ +char *argv[], /*input args*/\ +char *arglist[] /*output args*/\ +)\ +{\ + inT32 argcount; /*converted argc*/\ + char *testchar; /*char in option string*/\ + inT32 arg; /*current argument*/\ +\ + argcount=0; /*no of options*/\ + for (arg=0;arg +class ObjectCache { + public: + ObjectCache() {} + ~ObjectCache() { + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (cache_[i].count > 0) { + tprintf("ObjectCache(%p)::~ObjectCache(): WARNING! LEAK! object %p " + "still has count %d (id %s)\n", + this, cache_[i].object, cache_[i].count, + cache_[i].id.string()); + } else { + delete cache_[i].object; + cache_[i].object = NULL; + } + } + mu_.Unlock(); + } + + // Return a pointer to the object identified by id. + // If we haven't yet loaded the object, use loader to load it. + // If loader fails to load it, record a NULL entry in the cache + // and return NULL -- further attempts to load will fail (even + // with a different loader) until DeleteUnusedObjects() is called. + // We delete the given loader. + T *Get(STRING id, + TessResultCallback *loader) { + T *retval = NULL; + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (id == cache_[i].id) { + retval = cache_[i].object; + if (cache_[i].object != NULL) { + cache_[i].count++; + } + mu_.Unlock(); + delete loader; + return retval; + } + } + cache_.push_back(ReferenceCount()); + ReferenceCount &rc = cache_.back(); + rc.id = id; + retval = rc.object = loader->Run(); + rc.count = (retval != NULL) ? 1 : 0; + mu_.Unlock(); + return retval; + } + + // Decrement the count for t. + // Return whether we knew about the given pointer. + bool Free(T *t) { + if (t == NULL) return false; + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (cache_[i].object == t) { + --cache_[i].count; + mu_.Unlock(); + return true; + } + } + mu_.Unlock(); + return false; + } + + void DeleteUnusedObjects() { + mu_.Lock(); + for (int i = cache_.size() - 1; i >= 0; i--) { + if (cache_[i].count <= 0) { + delete cache_[i].object; + cache_.remove(i); + } + } + mu_.Unlock(); + } + + private: + struct ReferenceCount { + STRING id; // A unique ID to identify the object (think path on disk) + T *object; // A copy of the object in memory. Can be delete'd. + int count; // A count of the number of active users of this object. + }; + + CCUtilMutex mu_; + GenericVector cache_; +}; + +} // namespace tesseract + + +#endif // TESSERACT_CCUTIL_OBJECT_CACHE_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/ocrclass.h b/hgdriver/3rdparty/hgOCR/include/ccutil/ocrclass.h new file mode 100644 index 0000000..cb83c6d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/ocrclass.h @@ -0,0 +1,167 @@ +/********************************************************************** + * File: ocrclass.h + * Description: Class definitions and constants for the OCR API. + * Author: Hewlett-Packard Co + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +/********************************************************************** + * This file contains typedefs for all the structures used by + * the HP OCR interface. + * The code is designed to be used with either a C or C++ compiler. + * The structures are designed to allow them to be used with any + * structure alignment up to 8. + **********************************************************************/ + +#ifndef CCUTIL_OCRCLASS_H_ +#define CCUTIL_OCRCLASS_H_ + +#ifndef __GNUC__ +#ifdef _WIN32 +#include "gettimeofday.h" +#endif +#else +#include +#endif +#include +#include "host.h" + +/*Maximum lengths of various strings*/ +#define MAX_FONT_NAME 34 /*name of font */ +#define MAX_OCR_NAME 32 /*name of engine */ +#define MAX_OCR_VERSION 17 /*version code of engine */ + +/*pitch set definitions are identical to RTF*/ +#define PITCH_DEF 0 /*default */ +#define PITCH_FIXED 1 /*fixed pitch */ +#define PITCH_VAR 2 /*variable pitch */ + +/********************************************************************** + * EANYCODE_CHAR + * Description of a single character. The character code is defined by + * the character set of the current font. + * Output text is sent as an array of these structures. + * Spaces and line endings in the output are represented in the + * structures of the surrounding characters. They are not directly + * represented as characters. + * The first character in a word has a positive value of blanks. + * Missing information should be set to the defaults in the comments. + * If word bounds are known, but not character bounds, then the top and + * bottom of each character should be those of the word. The left of the + * first and right of the last char in each word should be set. All other + * lefts and rights should be set to -1. + * If set, the values of right and bottom are left+width and top+height. + * Most of the members come directly from the parameters to ocr_append_char. + * The formatting member uses the enhancement parameter and combines the + * line direction stuff into the top 3 bits. + * The coding is 0=RL char, 1=LR char, 2=DR NL, 3=UL NL, 4=DR Para, + * 5=UL Para, 6=TB char, 7=BT char. API users do not need to know what + * the coding is, only that it is backwards compatible with the previous + * version. + **********************************************************************/ + +typedef struct { /*single character */ +// It should be noted that the format for char_code for version 2.0 and beyond +// is UTF8 which means that ASCII characters will come out as one structure but +// other characters will be returned in two or more instances of this structure +// with a single byte of the UTF8 code in each, but each will have the same +// bounding box. Programs which want to handle languagues with different +// characters sets will need to handle extended characters appropriately, but +// *all* code needs to be prepared to receive UTF8 coded characters for +// characters such as bullet and fancy quotes. + uinT16 char_code; /*character itself */ + inT16 left; /*of char (-1) */ + inT16 right; /*of char (-1) */ + inT16 top; /*of char (-1) */ + inT16 bottom; /*of char (-1) */ + inT16 font_index; /*what font (0) */ + uinT8 confidence; /*0=perfect, 100=reject (0/100) */ + uinT8 point_size; /*of char, 72=i inch, (10) */ + inT8 blanks; /*no of spaces before this char (1) */ + uinT8 formatting; /*char formatting (0) */ +} EANYCODE_CHAR; /*single character */ + +/********************************************************************** + * ETEXT_DESC + * Description of the output of the OCR engine. + * This structure is used as both a progress monitor and the final + * output header, since it needs to be a valid progress monitor while + * the OCR engine is storing its output to shared memory. + * During progress, all the buffer info is -1. + * Progress starts at 0 and increases to 100 during OCR. No other constraint. + * Additionally the progress callback contains the bounding box of the word that + * is currently being processed. + * Every progress callback, the OCR engine must set ocr_alive to 1. + * The HP side will set ocr_alive to 0. Repeated failure to reset + * to 1 indicates that the OCR engine is dead. + * If the cancel function is not null then it is called with the number of + * user words found. If it returns true then operation is cancelled. + **********************************************************************/ +typedef bool (*CANCEL_FUNC)(void* cancel_this, int words); +typedef bool (*PROGRESS_FUNC)(int progress, int left, int right, int top, + int bottom); + +class ETEXT_DESC { // output header + public: + inT16 count; /// chars in this buffer(0) + inT16 progress; /// percent complete increasing (0-100) + /** Progress monitor covers word recognition and it does not cover layout + * analysis. + * See Ray comment in https://github.com/tesseract-ocr/tesseract/pull/27 */ + inT8 more_to_come; /// true if not last + volatile inT8 ocr_alive; /// ocr sets to 1, HP 0 + inT8 err_code; /// for errcode use + CANCEL_FUNC cancel; /// returns true to cancel + PROGRESS_FUNC progress_callback; /// called whenever progress increases + void* cancel_this; /// this or other data for cancel + struct timeval end_time; /// Time to stop. Expected to be set only + /// by call to set_deadline_msecs(). + EANYCODE_CHAR text[1]; /// character data + + ETEXT_DESC() + : count(0), + progress(0), + more_to_come(0), + ocr_alive(0), + err_code(0), + cancel(NULL), + progress_callback(NULL), + cancel_this(NULL) { + end_time.tv_sec = 0; + end_time.tv_usec = 0; + } + + // Sets the end time to be deadline_msecs milliseconds from now. + void set_deadline_msecs(inT32 deadline_msecs) { + gettimeofday(&end_time, NULL); + inT32 deadline_secs = deadline_msecs / 1000; + end_time.tv_sec += deadline_secs; + end_time.tv_usec += (deadline_msecs - deadline_secs * 1000) * 1000; + if (end_time.tv_usec > 1000000) { + end_time.tv_usec -= 1000000; + ++end_time.tv_sec; + } + } + + // Returns false if we've not passed the end_time, or have not set a deadline. + bool deadline_exceeded() const { + if (end_time.tv_sec == 0 && end_time.tv_usec == 0) return false; + struct timeval now; + gettimeofday(&now, NULL); + return (now.tv_sec > end_time.tv_sec || (now.tv_sec == end_time.tv_sec && + now.tv_usec > end_time.tv_usec)); + } +}; + +#endif // CCUTIL_OCRCLASS_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/params.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/params.cpp new file mode 100644 index 0000000..7ae2653 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/params.cpp @@ -0,0 +1,224 @@ +/********************************************************************** + * File: params.cpp + * Description: Initialization and setting of Tesseract parameters. + * Author: Ray Smith + * Created: Fri Feb 22 16:22:34 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include +#include +#include + +#include "genericvector.h" +#include "scanutils.h" +#include "tprintf.h" +#include "params.h" + +#define PLUS '+' //flag states +#define MINUS '-' +#define EQUAL '=' + +tesseract::ParamsVectors *GlobalParams() { + static tesseract::ParamsVectors global_params = tesseract::ParamsVectors(); + return &global_params; +} + +namespace tesseract { + +bool ParamUtils::ReadParamsFile(const char *file, + SetParamConstraint constraint, + ParamsVectors *member_params) { + inT16 nameoffset; // offset for real name + FILE *fp; // file pointer + // iterators + + if (*file == PLUS) { + nameoffset = 1; + } else if (*file == MINUS) { + nameoffset = 1; + } else { + nameoffset = 0; + } + + fp = fopen(file + nameoffset, "rb"); + if (fp == NULL) { + tprintf("read_params_file: Can't open %s\n", file + nameoffset); + return true; + } + const bool anyerr = ReadParamsFromFp(fp, -1, constraint, member_params); + fclose(fp); + return anyerr; +} + +bool ParamUtils::ReadParamsFromFp(FILE *fp, inT64 end_offset, + SetParamConstraint constraint, + ParamsVectors *member_params) { + char line[MAX_PATH]; // input line + bool anyerr = false; // true if any error + bool foundit; // found parameter + char *valptr; // value field + + while ((end_offset < 0 || ftell(fp) < end_offset) && + fgets(line, MAX_PATH, fp)) { + if (line[0] != '\r' && line[0] != '\n' && line[0] != '#') { + chomp_string(line); // remove newline + for (valptr = line; *valptr && *valptr != ' ' && *valptr != '\t'; + valptr++); + if (*valptr) { // found blank + *valptr = '\0'; // make name a string + do + valptr++; // find end of blanks + while (*valptr == ' ' || *valptr == '\t'); + } + foundit = SetParam(line, valptr, constraint, member_params); + + if (!foundit) { + anyerr = true; // had an error + tprintf("read_params_file: parameter not found: %s\n", line); + exit(1); + } + } + } + return anyerr; +} + +bool ParamUtils::SetParam(const char *name, const char* value, + SetParamConstraint constraint, + ParamsVectors *member_params) { + // Look for the parameter among string parameters. + StringParam *sp = FindParam(name, GlobalParams()->string_params, + member_params->string_params); + if (sp != NULL && sp->constraint_ok(constraint)) sp->set_value(value); + if (*value == '\0') return (sp != NULL); + + // Look for the parameter among int parameters. + int intval; + IntParam *ip = FindParam(name, GlobalParams()->int_params, + member_params->int_params); + if (ip && ip->constraint_ok(constraint) && + sscanf(value, "%d", &intval) == 1) ip->set_value(intval); + + // Look for the parameter among bool parameters. + BoolParam *bp = FindParam(name, GlobalParams()->bool_params, + member_params->bool_params); + if (bp != NULL && bp->constraint_ok(constraint)) { + if (*value == 'T' || *value == 't' || + *value == 'Y' || *value == 'y' || *value == '1') { + bp->set_value(true); + } else if (*value == 'F' || *value == 'f' || + *value == 'N' || *value == 'n' || *value == '0') { + bp->set_value(false); + } + } + + // Look for the parameter among double parameters. + double doubleval; + DoubleParam *dp = FindParam(name, GlobalParams()->double_params, + member_params->double_params); + if (dp != NULL && dp->constraint_ok(constraint)) { +#ifdef EMBEDDED + doubleval = strtofloat(value); +#else + if (sscanf(value, "%lf", &doubleval) == 1) +#endif + dp->set_value(doubleval); + } + return (sp || ip || bp || dp); +} + +bool ParamUtils::GetParamAsString(const char *name, + const ParamsVectors* member_params, + STRING *value) { + // Look for the parameter among string parameters. + StringParam *sp = FindParam(name, GlobalParams()->string_params, + member_params->string_params); + if (sp) { + *value = sp->string(); + return true; + } + // Look for the parameter among int parameters. + IntParam *ip = FindParam(name, GlobalParams()->int_params, + member_params->int_params); + if (ip) { + char buf[128]; + snprintf(buf, sizeof(buf), "%d", inT32(*ip)); + *value = buf; + return true; + } + // Look for the parameter among bool parameters. + BoolParam *bp = FindParam(name, GlobalParams()->bool_params, + member_params->bool_params); + if (bp != NULL) { + *value = BOOL8(*bp) ? "1": "0"; + return true; + } + // Look for the parameter among double parameters. + DoubleParam *dp = FindParam(name, GlobalParams()->double_params, + member_params->double_params); + if (dp != NULL) { + char buf[128]; + snprintf(buf, sizeof(buf), "%g", double(*dp)); + *value = buf; + return true; + } + return false; +} + +void ParamUtils::PrintParams(FILE *fp, const ParamsVectors *member_params) { + int v, i; + int num_iterations = (member_params == NULL) ? 1 : 2; + for (v = 0; v < num_iterations; ++v) { + const ParamsVectors *vec = (v == 0) ? GlobalParams() : member_params; + for (i = 0; i < vec->int_params.size(); ++i) { + fprintf(fp, "%s\t%d\t%s\n", vec->int_params[i]->name_str(), + (inT32)(*vec->int_params[i]), vec->int_params[i]->info_str()); + } + for (i = 0; i < vec->bool_params.size(); ++i) { + fprintf(fp, "%s\t%d\t%s\n", vec->bool_params[i]->name_str(), + (BOOL8)(*vec->bool_params[i]), vec->bool_params[i]->info_str()); + } + for (int i = 0; i < vec->string_params.size(); ++i) { + fprintf(fp, "%s\t%s\t%s\n", vec->string_params[i]->name_str(), + vec->string_params[i]->string(), vec->string_params[i]->info_str()); + } + for (int i = 0; i < vec->double_params.size(); ++i) { + fprintf(fp, "%s\t%g\t%s\n", vec->double_params[i]->name_str(), + (double)(*vec->double_params[i]), vec->double_params[i]->info_str()); + } + } +} + +// Resets all parameters back to default values; +void ParamUtils::ResetToDefaults(ParamsVectors* member_params) { + int v, i; + int num_iterations = (member_params == NULL) ? 1 : 2; + for (v = 0; v < num_iterations; ++v) { + ParamsVectors *vec = (v == 0) ? GlobalParams() : member_params; + for (i = 0; i < vec->int_params.size(); ++i) { + vec->int_params[i]->ResetToDefault(); + } + for (i = 0; i < vec->bool_params.size(); ++i) { + vec->bool_params[i]->ResetToDefault(); + } + for (int i = 0; i < vec->string_params.size(); ++i) { + vec->string_params[i]->ResetToDefault(); + } + for (int i = 0; i < vec->double_params.size(); ++i) { + vec->double_params[i]->ResetToDefault(); + } + } +} + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/params.h b/hgdriver/3rdparty/hgOCR/include/ccutil/params.h new file mode 100644 index 0000000..c168dbe --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/params.h @@ -0,0 +1,325 @@ +/********************************************************************** + * File: params.h + * Description: Class definitions of the *_VAR classes for tunable constants. + * Author: Ray Smith + * Created: Fri Feb 22 11:26:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PARAMS_H +#define PARAMS_H + +#include + +#include "genericvector.h" +#include "strngs.h" + +namespace tesseract { + +class IntParam; +class BoolParam; +class StringParam; +class DoubleParam; + +// Enum for constraints on what kind of params should be set by SetParam(). +enum SetParamConstraint { + SET_PARAM_CONSTRAINT_NONE, + SET_PARAM_CONSTRAINT_DEBUG_ONLY, + SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY, + SET_PARAM_CONSTRAINT_NON_INIT_ONLY, +}; + +struct ParamsVectors { + GenericVector int_params; + GenericVector bool_params; + GenericVector string_params; + GenericVector double_params; +}; + +// Utility functions for working with Tesseract parameters. +class ParamUtils { + public: + // Reads a file of parameter definitions and set/modify the values therein. + // If the filename begins with a + or -, the BoolVariables will be + // ORed or ANDed with any current values. + // Blank lines and lines beginning # are ignored. + // Values may have any whitespace after the name and are the rest of line. + static bool TESS_API ReadParamsFile( + const char *file, // filename to read + SetParamConstraint constraint, + ParamsVectors *member_params); + + // Read parameters from the given file pointer (stop at end_offset). + static bool ReadParamsFromFp(FILE *fp, inT64 end_offset, + SetParamConstraint constraint, + ParamsVectors *member_params); + + // Set a parameters to have the given value. + static bool SetParam(const char *name, const char* value, + SetParamConstraint constraint, + ParamsVectors *member_params); + + // Returns the pointer to the parameter with the given name (of the + // appropriate type) if it was found in the vector obtained from + // GlobalParams() or in the given member_params. + template + static T *FindParam(const char *name, + const GenericVector &global_vec, + const GenericVector &member_vec) { + int i; + for (i = 0; i < global_vec.size(); ++i) { + if (strcmp(global_vec[i]->name_str(), name) == 0) return global_vec[i]; + } + for (i = 0; i < member_vec.size(); ++i) { + if (strcmp(member_vec[i]->name_str(), name) == 0) return member_vec[i]; + } + return NULL; + } + // Removes the given pointer to the param from the given vector. + template + static void RemoveParam(T *param_ptr, GenericVector *vec) { + for (int i = 0; i < vec->size(); ++i) { + if ((*vec)[i] == param_ptr) { + vec->remove(i); + return; + } + } + } + // Fetches the value of the named param as a STRING. Returns false if not + // found. + static bool GetParamAsString(const char *name, + const ParamsVectors* member_params, + STRING *value); + + // Print parameters to the given file. + static void PrintParams(FILE *fp, const ParamsVectors *member_params); + + // Resets all parameters back to default values; + static void ResetToDefaults(ParamsVectors* member_params); +}; + +// Definition of various parameter types. +class Param { + public: + ~Param() {} + + const char *name_str() const { return name_; } + const char *info_str() const { return info_; } + bool is_init() const { return init_; } + bool is_debug() const { return debug_; } + bool constraint_ok(SetParamConstraint constraint) const { + return (constraint == SET_PARAM_CONSTRAINT_NONE || + (constraint == SET_PARAM_CONSTRAINT_DEBUG_ONLY && + this->is_debug()) || + (constraint == SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY && + !this->is_debug()) || + (constraint == SET_PARAM_CONSTRAINT_NON_INIT_ONLY && + !this->is_init())); + } + + protected: + Param(const char *name, const char *comment, bool init) : + name_(name), info_(comment), init_(init) { + debug_ = (strstr(name, "debug") != NULL) || (strstr(name, "display")); + } + + const char *name_; // name of this parameter + const char *info_; // for menus + bool init_; // needs to be set before init + bool debug_; +}; + +class IntParam : public Param { + public: + IntParam(inT32 value, const char *name, const char *comment, bool init, + ParamsVectors *vec) : Param(name, comment, init) { + value_ = value; + default_ = value; + params_vec_ = &(vec->int_params); + vec->int_params.push_back(this); + } + ~IntParam() { ParamUtils::RemoveParam(this, params_vec_); } + operator inT32() const { return value_; } + void operator=(inT32 value) { value_ = value; } + void set_value(inT32 value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } + + private: + inT32 value_; + inT32 default_; + // Pointer to the vector that contains this param (not owened by this class). + GenericVector *params_vec_; +}; + +class BoolParam : public Param { + public: + BoolParam(bool value, const char *name, const char *comment, bool init, + ParamsVectors *vec) : Param(name, comment, init) { + value_ = value; + default_ = value; + params_vec_ = &(vec->bool_params); + vec->bool_params.push_back(this); + } + ~BoolParam() { ParamUtils::RemoveParam(this, params_vec_); } + operator BOOL8() const { return value_; } + void operator=(BOOL8 value) { value_ = value; } + void set_value(BOOL8 value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } + + private: + BOOL8 value_; + BOOL8 default_; + // Pointer to the vector that contains this param (not owned by this class). + GenericVector *params_vec_; +}; + +class StringParam : public Param { + public: + StringParam(const char *value, const char *name, + const char *comment, bool init, + ParamsVectors *vec) : Param(name, comment, init) { + value_ = value; + default_ = value; + params_vec_ = &(vec->string_params); + vec->string_params.push_back(this); + } + ~StringParam() { ParamUtils::RemoveParam(this, params_vec_); } + operator STRING &() { return value_; } + const char *string() const { return value_.string(); } + const char *c_str() const { return value_.string(); } + bool empty() { return value_.length() <= 0; } + bool operator==(const STRING& other) { return value_ == other; } + void operator=(const STRING& value) { value_ = value; } + void set_value(const STRING& value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } + + private: + STRING value_; + STRING default_; + // Pointer to the vector that contains this param (not owened by this class). + GenericVector *params_vec_; +}; + +class DoubleParam : public Param { + public: + DoubleParam(double value, const char *name, const char *comment, + bool init, ParamsVectors *vec) : Param(name, comment, init) { + value_ = value; + default_ = value; + params_vec_ = &(vec->double_params); + vec->double_params.push_back(this); + } + ~DoubleParam() { ParamUtils::RemoveParam(this, params_vec_); } + operator double() const { return value_; } + void operator=(double value) { value_ = value; } + void set_value(double value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } + + private: + double value_; + double default_; + // Pointer to the vector that contains this param (not owned by this class). + GenericVector *params_vec_; +}; + +} // namespace tesseract + +// Global parameter lists. +// +// To avoid the problem of undetermined order of static initialization +// global_params are accessed through the GlobalParams function that +// initializes the static pointer to global_params only on the first +// first time GlobalParams() is called. +// +// TODO(daria): remove GlobalParams() when all global Tesseract +// parameters are converted to members. +tesseract::ParamsVectors TESS_API *GlobalParams(); + +/************************************************************************* + * Note on defining parameters. + * + * The values of the parameters defined with *_INIT_* macros are guaranteed + * to be loaded from config files before Tesseract initialization is done + * (there is no such guarantee for parameters defined with the other macros). + *************************************************************************/ + +#define INT_VAR_H(name,val,comment)\ + tesseract::IntParam name + +#define BOOL_VAR_H(name,val,comment)\ + tesseract::BoolParam name + +#define STRING_VAR_H(name,val,comment)\ + tesseract::StringParam name + +#define double_VAR_H(name,val,comment)\ + tesseract::DoubleParam name + +#define INT_VAR(name,val,comment)\ + tesseract::IntParam name(val,#name,comment,false,GlobalParams()) + +#define BOOL_VAR(name,val,comment)\ + tesseract::BoolParam name(val,#name,comment,false,GlobalParams()) + +#define STRING_VAR(name,val,comment)\ + tesseract::StringParam name(val,#name,comment,false,GlobalParams()) + +#define double_VAR(name,val,comment)\ + tesseract::DoubleParam name(val,#name,comment,false,GlobalParams()) + +#define INT_INIT_VAR(name,val,comment)\ + tesseract::IntParam name(val,#name,comment,true,GlobalParams()) + +#define BOOL_INIT_VAR(name,val,comment)\ + tesseract::BoolParam name(val,#name,comment,true,GlobalParams()) + +#define STRING_INIT_VAR(name,val,comment)\ + tesseract::StringParam name(val,#name,comment,true,GlobalParams()) + +#define double_INIT_VAR(name,val,comment)\ + tesseract::DoubleParam name(val,#name,comment,true,GlobalParams()) + +#define INT_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, false, vec) + +#define BOOL_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, false, vec) + +#define STRING_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, false, vec) + +#define double_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, false, vec) + +#define INT_INIT_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, true, vec) + +#define BOOL_INIT_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, true, vec) + +#define STRING_INIT_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, true, vec) + +#define double_INIT_MEMBER(name, val, comment, vec)\ + name(val, #name, comment, true, vec) + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/platform.h b/hgdriver/3rdparty/hgOCR/include/ccutil/platform.h new file mode 100644 index 0000000..99bff4b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/platform.h @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////// +// File: platform.h +// Description: Place holder +// Author: +// Created: +// +// (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_CCUTIL_PLATFORM_H__ +#define TESSERACT_CCUTIL_PLATFORM_H__ + +#include + +#define DLLSYM +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif /* NOMINMAX */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifdef __GNUC__ +#define ultoa _ultoa +#endif /* __GNUC__ */ +#define SIGNED +#if defined(_MSC_VER) +#if (_MSC_VER < 1900) +#define snprintf _snprintf +#endif +#if (_MSC_VER <= 1400) +#define vsnprintf _vsnprintf +#endif /* (_MSC_VER <= 1400) */ +#endif /* defined(_MSC_VER) */ +#else +#define __UNIX__ +#include +#ifndef PATH_MAX +#define MAX_PATH 4096 +#else +#define MAX_PATH PATH_MAX +#endif +#define SIGNED signed +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) + #if defined(TESS_EXPORTS) + #define TESS_API __declspec(dllexport) + #elif defined(TESS_IMPORTS) + #define TESS_API __declspec(dllimport) + #else + #define TESS_API + #endif + #define TESS_LOCAL +#else + #if __GNUC__ >= 4 + #if defined(TESS_EXPORTS) || defined(TESS_IMPORTS) + #define TESS_API __attribute__ ((visibility ("default"))) + #define TESS_LOCAL __attribute__ ((visibility ("hidden"))) + #else + #define TESS_API + #define TESS_LOCAL + #endif + #else + #define TESS_API + #define TESS_LOCAL + #endif +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) + #define _TESS_FILE_BASENAME_ \ + (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#else // Unices + #define _TESS_FILE_BASENAME_ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#endif // TESSERACT_CCUTIL_PLATFORM_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/qrsequence.h b/hgdriver/3rdparty/hgOCR/include/ccutil/qrsequence.h new file mode 100644 index 0000000..d49d755 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/qrsequence.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////// +// File: qrsequence.h +// Description: Quasi-random sequence generator class. +// Author: Ranjith Unnikrishnan +// Created: Wed May 20 2009 +// +// Class to generate a (deterministic) quasi-random Van der Corput sequence that +// covers the interval [0,N) without repetition. +// +// The sequence is generated by reversing the base-2 representation of the +// sequence of natural numbers {0, 1,... M-1}, where M is 2^{num_bits_} and +// num_bits is the minimum number of bits required to represent N. If a reversed +// numbers is >= N it is rejected and the next natural number is considered +// until a valid output number is found. +// +// (C) Copyright 2009, 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_QRSEQUENCE_H_ +#define TESSERACT_CCUTIL_QRSEQUENCE_H_ + +#include + +class QRSequenceGenerator { + public: + // Object is initalized with the size of the output range. + explicit QRSequenceGenerator(int N) : N_(N), next_num_(0) { + num_bits_ = static_cast(ceil(log(static_cast(N)) / log(2.0))); + } + + // Main worker method that retrieves the next number in the sequence. + // Returns kInvalidVal if called more than N times after object initialization + int GetVal() { + const int kInvalidVal = -1; + const int kMaxNaturalNumberValue = 1 << num_bits_; + if (next_num_ >= kMaxNaturalNumberValue) + return kInvalidVal; + int n = next_num_; + + while (next_num_ < kMaxNaturalNumberValue) { + n = GetBinaryReversedInteger(next_num_++); + if (n < N_) break; + } + return (next_num_ > kMaxNaturalNumberValue) ? kInvalidVal : n; + } + + protected: + // Outputs the integer formed by reversing the bits of the input integer. Only + // the lowest num_bits_ bits of the input integer are reversed. + int GetBinaryReversedInteger(int in_val) const { + int bit_pos = num_bits_; + int out_val = 0; + while(bit_pos--) { + // Set the value of the last bit. + out_val |= (in_val & 0x1); + if (bit_pos > 0) { + // Left-shift output value to prepare for storing the next bit. + out_val <<= 1; + } + // Right-shift input value to prepare for retrieving the next bit. + in_val >>= 1; + } + return out_val; + } + int N_; + // Next number to be considered for reversal and output. + int next_num_; + // number of bits required to represent the numbers of the sequence + int num_bits_; +}; + +#endif // TESSERACT_CCUTIL_QRSEQUENCE_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.cpp new file mode 100644 index 0000000..fac50b2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.cpp @@ -0,0 +1,579 @@ +// Copyright 2006 Google Inc. +// All Rights Reserved. +// Author: renn +// +// The fscanf, vfscanf and creat functions are implemented so that their +// functionality is mostly like their stdio counterparts. However, currently +// these functions do not use any buffering, making them rather slow. +// File streams are thus processed one character at a time. +// Although the implementations of the scanf functions do lack a few minor +// features, they should be sufficient for their use in tesseract. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scanutils.h" +#include "tprintf.h" + +enum Flags { + FL_SPLAT = 0x01, // Drop the value, do not assign + FL_INV = 0x02, // Character-set with inverse + FL_WIDTH = 0x04, // Field width specified + FL_MINUS = 0x08, // Negative number +}; + +enum Ranks { + RANK_CHAR = -2, + RANK_SHORT = -1, + RANK_INT = 0, + RANK_LONG = 1, + RANK_LONGLONG = 2, + RANK_PTR = INT_MAX // Special value used for pointers +}; + +const enum Ranks kMinRank = RANK_CHAR; +const enum Ranks kMaxRank = RANK_LONGLONG; + +const enum Ranks kIntMaxRank = RANK_LONGLONG; +const enum Ranks kSizeTRank = RANK_LONG; +const enum Ranks kPtrDiffRank = RANK_LONG; + +enum Bail { + BAIL_NONE = 0, // No error condition + BAIL_EOF, // Hit EOF + BAIL_ERR // Conversion mismatch +}; + +// Helper functions ------------------------------------------------------------ +inline size_t LongBit() { + return CHAR_BIT * sizeof(long); +} + +static inline int +SkipSpace(FILE *s) { + int p; + while (isspace(p = fgetc(s))); + ungetc(p, s); // Make sure next char is available for reading + return p; +} + +static inline void +SetBit(unsigned long *bitmap, unsigned int bit) { + bitmap[bit/LongBit()] |= 1UL << (bit%LongBit()); +} + +static inline int +TestBit(unsigned long *bitmap, unsigned int bit) { + return static_cast(bitmap[bit/LongBit()] >> (bit%LongBit())) & 1; +} + +static inline int DigitValue(int ch, int base) { + if (ch >= '0' && ch <= '9') { + if (base >= 10 || ch <= '7') + return ch-'0'; + } else if (ch >= 'A' && ch <= 'Z' && base == 16) { + return ch-'A'+10; + } else if (ch >= 'a' && ch <= 'z' && base == 16) { + return ch-'a'+10; + } + return -1; +} + +// IO (re-)implementations ----------------------------------------------------- +uintmax_t streamtoumax(FILE* s, int base) { + int minus = 0; + uintmax_t v = 0; + int d, c = 0; + + for (c = fgetc(s); + isspace(static_cast(c)) && (c != EOF); + c = fgetc(s)) {} + + // Single optional + or - + if (c == '-' || c == '+') { + minus = (c == '-'); + c = fgetc(s); + } + + // Assign correct base + if (base == 0) { + if (c == '0') { + c = fgetc(s); + if (c == 'x' || c == 'X') { + base = 16; + c = fgetc(s); + } else { + base = 8; + } + } + } else if (base == 16) { + if (c == '0') { + c = fgetc(s); + if (c == 'x' || c == 'X') c = fgetc(s); + } + } + + // Actual number parsing + for (; (c != EOF) && (d = DigitValue(c, base)) >= 0; c = fgetc(s)) + v = v*base + d; + + ungetc(c, s); + return minus ? -v : v; +} + +double streamtofloat(FILE* s) { + bool minus = false; + uint64_t v = 0; + int d, c; + uint64_t k = 1; + uint64_t w = 0; + + for (c = fgetc(s); + isspace(static_cast(c)) && (c != EOF); + c = fgetc(s)); + + // Single optional + or - + if (c == '-' || c == '+') { + minus = (c == '-'); + c = fgetc(s); + } + + // Actual number parsing + for (; c != EOF && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) + v = v*10 + d; + if (c == '.') { + for (c = fgetc(s); c != EOF && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) { + w = w*10 + d; + k *= 10; + } + } + double f = v + static_cast(w) / k; + if (c == 'e' || c == 'E') { + c = fgetc(s); + int expsign = 1; + if (c == '-' || c == '+') { + expsign = (c == '-') ? -1 : 1; + c = fgetc(s); + } + int exponent = 0; + for (; (c != EOF) && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) { + exponent = exponent * 10 + d; + } + exponent *= expsign; + f *= pow(10.0, static_cast(exponent)); + } + ungetc(c, s); + + return minus ? -f : f; +} + +double strtofloat(const char* s) { + int minus = 0; + int v = 0; + int d; + int k = 1; + int w = 0; + + while(*s && isspace(static_cast(*s))) s++; + + // Single optional + or - + if (*s == '-' || *s == '+') { + minus = (*s == '-'); + s++; + } + + // Actual number parsing + for (; *s && (d = DigitValue(*s, 10)) >= 0; s++) + v = v*10 + d; + if (*s == '.') { + for (++s; *s && (d = DigitValue(*s, 10)) >= 0; s++) { + w = w*10 + d; + k *= 10; + } + } + if (*s == 'e' || *s == 'E') + tprintf("WARNING: Scientific Notation not supported!"); + + double f = static_cast(v) + + static_cast(w) / static_cast(k); + + return minus ? -f : f; +} + +static int tvfscanf(FILE* stream, const char *format, va_list ap); + +int tfscanf(FILE* stream, const char *format, ...) { + va_list ap; + int rv; + + va_start(ap, format); + rv = tvfscanf(stream, format, ap); + va_end(ap); + + return rv; +} + +#ifdef EMBEDDED + +int fscanf(FILE* stream, const char *format, ...) { + va_list ap; + int rv; + + va_start(ap, format); + rv = tvfscanf(stream, format, ap); + va_end(ap); + + return rv; +} + +int vfscanf(FILE* stream, const char *format, ...) { + va_list ap; + int rv; + + va_start(ap, format); + rv = tvfscanf(stream, format, ap); + va_end(ap); + + return rv; +} +#endif + +static int tvfscanf(FILE* stream, const char *format, va_list ap) { + const char *p = format; + char ch; + int q = 0; + uintmax_t val = 0; + int rank = RANK_INT; // Default rank + unsigned int width = UINT_MAX; + int base; + int flags = 0; + enum { + ST_NORMAL, // Ground state + ST_FLAGS, // Special flags + ST_WIDTH, // Field width + ST_MODIFIERS, // Length or conversion modifiers + ST_MATCH_INIT, // Initial state of %[ sequence + ST_MATCH, // Main state of %[ sequence + ST_MATCH_RANGE, // After - in a %[ sequence + } state = ST_NORMAL; + char *sarg = NULL; // %s %c or %[ string argument + enum Bail bail = BAIL_NONE; + int sign; + int converted = 0; // Successful conversions + unsigned long matchmap[((1 << CHAR_BIT)+(CHAR_BIT * sizeof(long) - 1)) / + (CHAR_BIT * sizeof(long))]; + int matchinv = 0; // Is match map inverted? + unsigned char range_start = 0; + off_t start_off = ftell(stream); + + // Skip leading spaces + SkipSpace(stream); + + while ((ch = *p++) && !bail) { + switch (state) { + case ST_NORMAL: + if (ch == '%') { + state = ST_FLAGS; + flags = 0; rank = RANK_INT; width = UINT_MAX; + } else if (isspace(static_cast(ch))) { + SkipSpace(stream); + } else { + if (fgetc(stream) != ch) + bail = BAIL_ERR; // Match failure + } + break; + + case ST_FLAGS: + if (ch == '*') { + flags |= FL_SPLAT; + } else if ('0' <= ch && ch <= '9') { + width = (ch-'0'); + state = ST_WIDTH; + flags |= FL_WIDTH; + } else { + state = ST_MODIFIERS; + p--; // Process this character again + } + break; + + case ST_WIDTH: + if (ch >= '0' && ch <= '9') { + width = width*10+(ch-'0'); + } else { + state = ST_MODIFIERS; + p--; // Process this character again + } + break; + + case ST_MODIFIERS: + switch (ch) { + // Length modifiers - nonterminal sequences + case 'h': + rank--; // Shorter rank + break; + case 'l': + rank++; // Longer rank + break; + case 'j': + rank = kIntMaxRank; + break; + case 'z': + rank = kSizeTRank; + break; + case 't': + rank = kPtrDiffRank; + break; + case 'L': + case 'q': + rank = RANK_LONGLONG; // long double/long long + break; + + default: + // Output modifiers - terminal sequences + state = ST_NORMAL; // Next state will be normal + if (rank < kMinRank) // Canonicalize rank + rank = kMinRank; + else if (rank > kMaxRank) + rank = kMaxRank; + + switch (ch) { + case 'P': // Upper case pointer + case 'p': // Pointer + rank = RANK_PTR; + base = 0; sign = 0; + goto scan_int; + + case 'i': // Base-independent integer + base = 0; sign = 1; + goto scan_int; + + case 'd': // Decimal integer + base = 10; sign = 1; + goto scan_int; + + case 'o': // Octal integer + base = 8; sign = 0; + goto scan_int; + + case 'u': // Unsigned decimal integer + base = 10; sign = 0; + goto scan_int; + + case 'x': // Hexadecimal integer + case 'X': + base = 16; sign = 0; + goto scan_int; + + case 'n': // Number of characters consumed + val = ftell(stream) - start_off; + goto set_integer; + + scan_int: + q = SkipSpace(stream); + if ( q <= 0 ) { + bail = BAIL_EOF; + break; + } + val = streamtoumax(stream, base); + // fall through + + set_integer: + if (!(flags & FL_SPLAT)) { + converted++; + switch(rank) { + case RANK_CHAR: + *va_arg(ap, unsigned char *) + = static_cast(val); + break; + case RANK_SHORT: + *va_arg(ap, unsigned short *) + = static_cast(val); + break; + case RANK_INT: + *va_arg(ap, unsigned int *) + = static_cast(val); + break; + case RANK_LONG: + *va_arg(ap, unsigned long *) + = static_cast(val); + break; + case RANK_LONGLONG: + *va_arg(ap, unsigned long long *) + = static_cast(val); + break; + case RANK_PTR: + *va_arg(ap, void **) + = reinterpret_cast(static_cast(val)); + break; + } + } + break; + + case 'f': // Preliminary float value parsing + case 'g': + case 'G': + case 'e': + case 'E': + q = SkipSpace(stream); + if (q <= 0) { + bail = BAIL_EOF; + break; + } + + { + double fval = streamtofloat(stream); + if (!(flags & FL_SPLAT)) { + if (rank == RANK_INT) + *va_arg(ap, float *) = static_cast(fval); + else if (rank == RANK_LONG) + *va_arg(ap, double *) = static_cast(fval); + converted++; + } + } + break; + + case 'c': // Character + width = (flags & FL_WIDTH) ? width : 1; // Default width == 1 + sarg = va_arg(ap, char *); + while (width--) { + if ((q = fgetc(stream)) <= 0) { + bail = BAIL_EOF; + break; + } + if (!(flags & FL_SPLAT)) { + *sarg++ = q; + converted++; + } + } + break; + + case 's': // String + { + char *sp; + sp = sarg = va_arg(ap, char *); + while (width--) { + q = fgetc(stream); + if (isspace(static_cast(q)) || q <= 0) { + ungetc(q, stream); + break; + } + if (!(flags & FL_SPLAT)) *sp = q; + sp++; + } + if (sarg == sp) { + bail = BAIL_EOF; + } else if (!(flags & FL_SPLAT)) { + *sp = '\0'; // Terminate output + converted++; + } else { + } + } + break; + + case '[': // Character range + sarg = va_arg(ap, char *); + state = ST_MATCH_INIT; + matchinv = 0; + memset(matchmap, 0, sizeof matchmap); + break; + + case '%': // %% sequence + if (fgetc(stream) != '%' ) + bail = BAIL_ERR; + break; + + default: // Anything else + bail = BAIL_ERR; // Unknown sequence + break; + } + } + break; + + case ST_MATCH_INIT: // Initial state for %[ match + if (ch == '^' && !(flags & FL_INV)) { + matchinv = 1; + } else { + SetBit(matchmap, static_cast(ch)); + state = ST_MATCH; + } + break; + + case ST_MATCH: // Main state for %[ match + if (ch == ']') { + goto match_run; + } else if (ch == '-') { + range_start = static_cast(ch); + state = ST_MATCH_RANGE; + } else { + SetBit(matchmap, static_cast(ch)); + } + break; + + case ST_MATCH_RANGE: // %[ match after - + if (ch == ']') { + SetBit(matchmap, static_cast('-')); + goto match_run; + } else { + int i; + for (i = range_start ; i < (static_cast(ch)) ; i++) + SetBit(matchmap, i); + state = ST_MATCH; + } + break; + + match_run: // Match expression finished + char* oarg = sarg; + while (width) { + q = fgetc(stream); + unsigned char qc = static_cast(q); + if (q <= 0 || !(TestBit(matchmap, qc)^matchinv)) { + ungetc(q, stream); + break; + } + if (!(flags & FL_SPLAT)) *sarg = q; + sarg++; + } + if (oarg == sarg) { + bail = (q <= 0) ? BAIL_EOF : BAIL_ERR; + } else if (!(flags & FL_SPLAT)) { + *sarg = '\0'; + converted++; + } + break; + } + } + + if (bail == BAIL_EOF && !converted) + converted = -1; // Return EOF (-1) + + return converted; +} + +#ifdef EMBEDDED +int creat(const char *pathname, mode_t mode) { + return open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode); +} + +#endif // EMBEDDED diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.h b/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.h new file mode 100644 index 0000000..a2c42f5 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/scanutils.h @@ -0,0 +1,67 @@ +// Copyright 2006 Google Inc. +// All Rights Reserved. +// Author: renn +// +// Contains file io functions (mainly for file parsing), that might not be +// available, on embedded devices, or that have an incomplete implementation +// there. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESSERACT_CCUTIL_SCANUTILS_H_ +#define TESSERACT_CCUTIL_SCANUTILS_H_ + +#include +#include +#include +#include + +/** + * fscanf variant to ensure correct reading regardless of locale. + * + * tfscanf parse a file stream according to the given format. See the fscanf + * manpage for more information, as this function attempts to mimic its + * behavior. + * + * @note Note that scientific floating-point notation is not supported. + * + */ +int tfscanf(FILE* stream, const char *format, ...); + +#ifdef EMBEDDED + +// Attempts to parse the given file stream s as an integer of the base +// 'base'. Returns the first successfully parsed integer as a uintmax_t, or +// 0, if none was found. +uintmax_t streamtoumax(FILE* s, int base); + +// Parse a file stream according to the given format. See the fscanf manpage +// for more information, as this function attempts to mimic its behavior. +// Note that scientific loating-point notation is not supported. +int fscanf(FILE* stream, const char *format, ...); + +// Parse a file stream according to the given format. See the fscanf manpage +// for more information, as this function attempts to mimic its behavior. +// Note that scientific loating-point notation is not supported. +int vfscanf(FILE* stream, const char *format, va_list ap); + +// Create a file at the specified path. See the creat manpage for more +// information, as this function attempts to mimic its behavior. +int creat(const char *pathname, mode_t mode); + +// Convert the specified C-String to a float. Returns the first parsed float, +// or 0.0 if no floating point value could be found. Note that scientific +// floating-point notation is not supported. +double strtofloat(const char* s); + +#endif // EMBEDDED + +#endif // TESSERACT_CCUTIL_SCANUTILS_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.cpp new file mode 100644 index 0000000..0e1dd61 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.cpp @@ -0,0 +1,145 @@ +/********************************************************************** + * File: serialis.h (Formerly serialmac.h) + * Description: Inline routines and macros for serialisation functions + * Author: Phil Cheatle + * Created: Tue Oct 08 08:33:12 BST 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "serialis.h" +#include +#include "genericvector.h" + +namespace tesseract { + +TFile::TFile() + : offset_(0), data_(NULL), data_is_owned_(false), is_writing_(false) { +} + +TFile::~TFile() { + if (data_is_owned_) + delete data_; +} + +bool TFile::Open(const STRING& filename, FileReader reader) { + if (!data_is_owned_) { + data_ = new GenericVector; + data_is_owned_ = true; + } + offset_ = 0; + is_writing_ = false; + if (reader == NULL) + return LoadDataFromFile(filename, data_); + else + return (*reader)(filename, data_); +} + +bool TFile::Open(const char* data, int size) { + offset_ = 0; + if (!data_is_owned_) { + data_ = new GenericVector; + data_is_owned_ = true; + } + is_writing_ = false; + data_->resize_no_init(size); + memcpy(&(*data_)[0], data, size); + return true; +} + +bool TFile::Open(FILE* fp, inT64 end_offset) { + offset_ = 0; + inT64 current_pos = ftell(fp); + if (end_offset < 0) { + if (fseek(fp, 0, SEEK_END)) + return false; + end_offset = ftell(fp); + if (fseek(fp, current_pos, SEEK_SET)) + return false; + } + int size = end_offset - current_pos; + is_writing_ = false; + if (!data_is_owned_) { + data_ = new GenericVector; + data_is_owned_ = true; + } + data_->resize_no_init(size); + return static_cast(fread(&(*data_)[0], 1, size, fp)) == size; +} + +char* TFile::FGets(char* buffer, int buffer_size) { + ASSERT_HOST(!is_writing_); + int size = 0; + while (size + 1 < buffer_size && offset_ < data_->size()) { + buffer[size++] = (*data_)[offset_++]; + if ((*data_)[offset_ - 1] == '\n') break; + } + if (size < buffer_size) buffer[size] = '\0'; + return size > 0 ? buffer : NULL; +} + +int TFile::FRead(void* buffer, int size, int count) { + ASSERT_HOST(!is_writing_); + int required_size = size * count; + if (required_size <= 0) return 0; + char* char_buffer = reinterpret_cast(buffer); + if (data_->size() - offset_ < required_size) + required_size = data_->size() - offset_; + if (required_size > 0 && char_buffer != NULL) + memcpy(char_buffer, &(*data_)[offset_], required_size); + offset_ += required_size; + return required_size / size; +} + +void TFile::Rewind() { + ASSERT_HOST(!is_writing_); + offset_ = 0; +} + +void TFile::OpenWrite(GenericVector* data) { + offset_ = 0; + if (data != NULL) { + if (data_is_owned_) delete data_; + data_ = data; + data_is_owned_ = false; + } else if (!data_is_owned_) { + data_ = new GenericVector; + data_is_owned_ = true; + } + is_writing_ = true; + data_->truncate(0); +} + +bool TFile::CloseWrite(const STRING& filename, FileWriter writer) { + ASSERT_HOST(is_writing_); + if (writer == NULL) + return SaveDataToFile(*data_, filename); + else + return (*writer)(*data_, filename); +} + +int TFile::FWrite(const void* buffer, int size, int count) { + ASSERT_HOST(is_writing_); + int total = size * count; + if (total <= 0) return 0; + const char* buf = reinterpret_cast(buffer); + // This isn't very efficient, but memory is so fast compared to disk + // that it is relatively unimportant, and very simple. + for (int i = 0; i < total; ++i) + data_->push_back(buf[i]); + return count; +} + + +} // namespace tesseract. + diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.h b/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.h new file mode 100644 index 0000000..8dfac1d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/serialis.h @@ -0,0 +1,99 @@ +/********************************************************************** + * File: serialis.h (Formerly serialmac.h) + * Description: Inline routines and macros for serialisation functions + * Author: Phil Cheatle + * Created: Tue Oct 08 08:33:12 BST 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SERIALIS_H +#define SERIALIS_H + +#include +#include +#include +#include "host.h" + +template class GenericVector; +class STRING; + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +namespace tesseract { + +// Function to read a GenericVector from a whole file. +// Returns false on failure. +typedef bool (*FileReader)(const STRING& filename, GenericVector* data); +// Function to write a GenericVector to a whole file. +// Returns false on failure. +typedef bool (*FileWriter)(const GenericVector& data, + const STRING& filename); + +// Simple file class. +// Allows for portable file input from memory and from foreign file systems. +class TFile { + public: + TFile(); + ~TFile(); + + // All the Open methods load the whole file into memory for reading. + // Opens a file with a supplied reader, or NULL to use the default. + // Note that mixed read/write is not supported. + bool Open(const STRING& filename, FileReader reader); + // From an existing memory buffer. + bool Open(const char* data, int size); + // From an open file and an end offset. + bool Open(FILE* fp, inT64 end_offset); + + // Reads a line like fgets. Returns NULL on EOF, otherwise buffer. + // Reads at most buffer_size bytes, including '\0' terminator, even if + // the line is longer. Does nothing if buffer_size <= 0. + // To use fscanf use FGets and sscanf. + char* FGets(char* buffer, int buffer_size); + // Replicates fread, returning the number of items read. + int FRead(void* buffer, int size, int count); + // Resets the TFile as if it has been Opened, but nothing read. + // Only allowed while reading! + void Rewind(); + + // Open for writing. Either supply a non-NULL data with OpenWrite before + // calling FWrite, (no close required), or supply a NULL data to OpenWrite + // and call CloseWrite to write to a file after the FWrites. + void OpenWrite(GenericVector* data); + bool CloseWrite(const STRING& filename, FileWriter writer); + + // Replicates fwrite, returning the number of items written. + // To use fprintf, use snprintf and FWrite. + int FWrite(const void* buffer, int size, int count); + + private: + // The number of bytes used so far. + int offset_; + // The buffered data from the file. + GenericVector* data_; + // True if the data_ pointer is owned by *this. + bool data_is_owned_; + // True if the TFile is open for writing. + bool is_writing_; +}; + +} // namespace tesseract. + +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/sorthelper.h b/hgdriver/3rdparty/hgOCR/include/ccutil/sorthelper.h new file mode 100644 index 0000000..4da13b9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/sorthelper.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////// +// File: sorthelper.h +// Description: Generic sort and maxfinding class. +// Author: Ray Smith +// Created: Thu May 20 17:48:21 PDT 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_SORTHELPER_H_ +#define TESSERACT_CCUTIL_SORTHELPER_H_ + +#include +#include "genericvector.h" + +// Generic class to provide functions based on a pair. +// T is the value type. +// The class keeps a count of each value and can return the most frequent +// value or a sorted array of the values with counts. +// Note that this class uses linear search for adding. It is better +// to use the STATS class to get the mode of a large number of values +// in a small space. SortHelper is better to get the mode of a small number +// of values from a large space. +// T must have a copy constructor. +template +class SortHelper { + public: + // Simple pair class to hold the values and counts. + template struct SortPair { + PairT value; + int count; + }; + // qsort function to sort by decreasing count. + static int SortPairsByCount(const void* v1, const void* v2) { + const SortPair* p1 = reinterpret_cast*>(v1); + const SortPair* p2 = reinterpret_cast*>(v2); + return p2->count - p1->count; + } + // qsort function to sort by decreasing value. + static int SortPairsByValue(const void* v1, const void* v2) { + const SortPair* p1 = reinterpret_cast*>(v1); + const SortPair* p2 = reinterpret_cast*>(v2); + if (p2->value - p1->value < 0) return -1; + if (p2->value - p1->value > 0) return 1; + return 0; + } + + // Constructor takes a hint of the array size, but it need not be accurate. + explicit SortHelper(int sizehint) { + counts_.reserve(sizehint); + } + + // Add a value that may be a duplicate of an existing value. + // Uses a linear search. + void Add(T value, int count) { + // Linear search for value. + for (int i = 0; i < counts_.size(); ++i) { + if (counts_[i].value == value) { + counts_[i].count += count; + return; + } + } + SortPair new_pair = {value, count}; + counts_.push_back(SortPair(new_pair)); + } + + // Returns the frequency of the most frequent value. + // If max_value is not NULL, returns the most frequent value. + // If the array is empty, returns -MAX_INT32 and max_value is unchanged. + int MaxCount(T* max_value) const { + int best_count = -MAX_INT32; + for (int i = 0; i < counts_.size(); ++i) { + if (counts_[i].count > best_count) { + best_count = counts_[i].count; + if (max_value != NULL) + *max_value = counts_[i].value; + } + } + return best_count; + } + + // Returns the data array sorted by decreasing frequency. + const GenericVector >& SortByCount() { + counts_.sort(&SortPairsByCount); + return counts_; + } + // Returns the data array sorted by decreasing value. + const GenericVector >& SortByValue() { + counts_.sort(&SortPairsByValue); + return counts_; + } + + private: + GenericVector > counts_; +}; + + +#endif // TESSERACT_CCUTIL_SORTHELPER_H_. diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/stderr.h b/hgdriver/3rdparty/hgOCR/include/ccutil/stderr.h new file mode 100644 index 0000000..b5e96fb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/stderr.h @@ -0,0 +1,26 @@ +/********************************************************************** + * File: stderr.h (Formerly stderrs.h) + * Description: File defining error messages fundamental of commonly used. + * Author: Ray Smith + * Created: Fri Aug 10 15:23:14 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STDERR_H +#define STDERR_H + +#include "errcode.h" + +const ERRCODE MEMORY_OUT = "Out of memory"; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.cpp new file mode 100644 index 0000000..5a9cfd0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.cpp @@ -0,0 +1,510 @@ +/********************************************************************** + * 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 "strngs.h" + +#include + +#include "genericvector.h" +#include "helpers.h" +#include "serialis.h" +#include "tprintf.h" + +using tesseract::TFile; + +// 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; +// Size of buffer needed to host the decimal representation of the maximum +// possible length of a %.8g being -1.2345678e+999 = 16. +const int kMaxDoubleSize = 16; + +/********************************************************************** + * 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 don't modify 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() { + // Empty STRINGs contain just the "\0". + memcpy(AllocData(1, kMinCapacity), "", 1); +} + +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) { + // Empty STRINGs contain just the "\0". + memcpy(AllocData(1, kMinCapacity), "", 1); + } else { + int len = strlen(cstr) + 1; + char* this_cstr = AllocData(len, len); + memcpy(this_cstr, cstr, len); + } + assert(InvariantOk()); +} + +STRING::STRING(const char *data, int length) { + if (data == NULL) { + // Empty STRINGs contain just the "\0". + memcpy(AllocData(1, kMinCapacity), "", 1); + } else { + char* this_cstr = AllocData(length + 1, length + 1); + memcpy(this_cstr, data, length); + this_cstr[length] = '\0'; + } +} + +STRING::~STRING() { + DiscardData(); +} + +// TODO(rays) Change all callers to use TFile and remove the old functions. +// 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 (static_cast(fwrite(GetCStr(), 1, len, fp)) != len) return false; + return true; +} +// Writes to the given file. Returns false in case of error. +bool STRING::Serialize(TFile* fp) const { + inT32 len = length(); + if (fp->FWrite(&len, sizeof(len), 1) != 1) return false; + if (fp->FWrite(GetCStr(), 1, len) != 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 (static_cast(fread(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, TFile* fp) { + inT32 len; + if (fp->FRead(&len, sizeof(len), 1) != 1) return false; + if (swap) + ReverseN(&len, sizeof(len)); + truncate_at(len); + if (fp->FRead(GetCStr(), 1, len) != len) return false; + return true; +} + +// As DeSerialize, but only seeks past the data - hence a static method. +bool STRING::SkipDeSerialize(bool swap, tesseract::TFile* fp) { + inT32 len; + if (fp->FRead(&len, sizeof(len), 1) != 1) return false; + if (swap) ReverseN(&len, sizeof(len)); + return fp->FRead(NULL, 1, len) == len; +} + +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(); +} + +const char* STRING::c_str() const { + return string(); +} + +/****** + * 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) { + ASSERT_HOST(index >= 0); + FixHeader(); + 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 *splited) { + int start_index = 0; + int len = length(); + for (int i = 0; i < len; i++) { + if ((*this)[i] == c) { + if (i != start_index) { + (*this)[i] = '\0'; + splited->push_back(STRING(GetCStr() + start_index, i - start_index)); + (*this)[i] = c; + } + start_index = i + 1; + } + } + + if (len != start_index) { + splited->push_back(STRING(GetCStr() + start_index, len - start_index)); + } +} + +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 doesn't 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; +} +// Appends the given string and double (as a %.8g) to this. +void STRING::add_str_double(const char* str, double number) { + if (str != NULL) + *this += str; + // Allow space for the maximum possible length of %8g. + char num_buffer[kMaxDoubleSize]; + snprintf(num_buffer, kMaxDoubleSize - 1, "%.8g", number); + num_buffer[kMaxDoubleSize - 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; // don't 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 same state as default constructor. + DiscardData(); + // Empty STRINGs contain just the "\0". + memcpy(AllocData(1, kMinCapacity), "", 1); + } + + assert(InvariantOk()); + return *this; +} + +void STRING::assign(const char *cstr, int len) { + STRING_HEADER* this_header = GetHeader(); + this_header->used_ = 0; // don't bother copying data if need to realloc + char* this_cstr = ensure_cstr(len + 1); // +1 for '\0' + + this_header = GetHeader(); // for realloc + memcpy(this_cstr, cstr, len); + this_cstr[len] = '\0'; + this_header->used_ = len + 1; + + assert(InvariantOk()); +} + +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; +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.h b/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.h new file mode 100644 index 0000000..2e65463 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/strngs.h @@ -0,0 +1,174 @@ +/********************************************************************** + * File: strngs.h (Formerly strings.h) + * Description: STRING class definition. + * Author: Ray Smith + * Created: Fri Feb 15 09:15:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STRNGS_H +#define STRNGS_H + +#include +#include +#include "platform.h" +#include "memry.h" + +namespace tesseract { +class TFile; +} // namespace tesseract. + +// STRING_IS_PROTECTED means that string[index] = X is invalid +// because you have to go through strings interface to modify it. +// This allows the string to ensure internal integrity and maintain +// its own string length. Unfortunately this is not possible because +// STRINGS are used as direct-manipulation data buffers for things +// like length arrays and many places cast away the const on string() +// to mutate the string. Turning this off means that internally we +// cannot assume we know the strlen. +#define STRING_IS_PROTECTED 0 + +template class GenericVector; + +class TESS_API STRING +{ + public: + STRING(); + STRING(const STRING &string); + STRING(const char *string); + STRING(const char *data, int length); + ~STRING (); + + // 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); + // Writes to the given file. Returns false in case of error. + bool Serialize(tesseract::TFile* 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, tesseract::TFile* fp); + // As DeSerialize, but only seeks past the data - hence a static method. + static bool SkipDeSerialize(bool swap, tesseract::TFile* fp); + + BOOL8 contains(const char c) const; + inT32 length() const; + inT32 size() const { return length(); } + const char *string() const; + const char *c_str() const; + + inline char* strdup() const { + inT32 len = length() + 1; + return strncpy(new char[len], GetCStr(), len); + } + +#if STRING_IS_PROTECTED + const char &operator[] (inT32 index) const; + // len is number of chars in s to insert starting at index in this string + void insert_range(inT32 index, const char*s, int len); + void erase_range(inT32 index, int len); +#else + char &operator[] (inT32 index) const; +#endif + void split(const char c, GenericVector *splited); + void truncate_at(inT32 index); + + BOOL8 operator== (const STRING & string) const; + BOOL8 operator!= (const STRING & string) const; + BOOL8 operator!= (const char *string) const; + + STRING & operator= (const char *string); + STRING & operator= (const STRING & string); + + STRING operator+ (const STRING & string) const; + STRING operator+ (const char ch) const; + + STRING & operator+= (const char *string); + STRING & operator+= (const STRING & string); + STRING & operator+= (const char ch); + + // Assignment for strings which are not null-terminated. + void assign(const char *cstr, int len); + + // Appends the given string and int (as a %d) to this. + // += cannot be used for ints as there as a char += operator that would + // be ambiguous, and ints usually need a string before or between them + // anyway. + void add_str_int(const char* str, int number); + // Appends the given string and double (as a %.8g) to this. + void add_str_double(const char* str, double number); + + // ensure capacity but keep pointer encapsulated + inline void ensure(inT32 min_capacity) { ensure_cstr(min_capacity); } + + private: + typedef struct STRING_HEADER { + // How much space was allocated in the string buffer for char data. + int capacity_; + + // used_ is how much of the capacity is currently being used, + // including a '\0' terminator. + // + // If used_ is 0 then string is NULL (not even the '\0') + // else if used_ > 0 then it is strlen() + 1 (because it includes '\0') + // else strlen is >= 0 (not NULL) but needs to be computed. + // this condition is set when encapsulation is violated because + // an API returned a mutable string. + // + // capacity_ - used_ = excess capacity that the string can grow + // without reallocating + mutable int used_; + } STRING_HEADER; + + // To preserve the behavior of the old serialization, we only have space + // for one pointer in this structure. So we are embedding a data structure + // at the start of the storage that will hold additional state variables, + // then storing the actual string contents immediately after. + STRING_HEADER* data_; + + // returns the header part of the storage + inline STRING_HEADER* GetHeader() { + return data_; + } + inline const STRING_HEADER* GetHeader() const { + return data_; + } + + // returns the string data part of storage + inline char* GetCStr() { return ((char*)data_) + sizeof(STRING_HEADER); } + + inline const char* GetCStr() const { + return ((const char *)data_) + sizeof(STRING_HEADER); + } + inline bool InvariantOk() const { +#if STRING_IS_PROTECTED + return (GetHeader()->used_ == 0) ? + (string() == NULL) : (GetHeader()->used_ == (strlen(string()) + 1)); +#else + return true; +#endif + } + + // Ensure string has requested capacity as optimization + // to avoid unnecessary reallocations. + // The return value is a cstr buffer with at least requested capacity + char* ensure_cstr(inT32 min_capacity); + + void FixHeader() const; // make used_ non-negative, even if const + + char* AllocData(int used, int capacity); + void DiscardData(); +}; +#endif diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/tesscallback.h b/hgdriver/3rdparty/hgOCR/include/ccutil/tesscallback.h new file mode 100644 index 0000000..1f20c6b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/tesscallback.h @@ -0,0 +1,9721 @@ +/////////////////////////////////////////////////////////////////////// +// File: tesscallback.h +// Description: classes and functions to replace pointer-to-functions +// 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 _TESS_CALLBACK_SPECIALIZATIONS_H +#define _TESS_CALLBACK_SPECIALIZATIONS_H + +#include "host.h" // For NULL. + +struct TessCallbackUtils_ { + static void FailIsRepeatable(const char* name); +}; + + +class TessClosure { + public: + virtual ~TessClosure() { } + virtual void Run() = 0; +}; + +template +class TessResultCallback { + public: + virtual ~TessResultCallback() { } + virtual R Run() = 0; +}; + +template +class _ConstTessMemberResultCallback_0_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)() const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_0( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(); + return result; + } else { + R result = (object_->*member_)(); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_0 + : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)() const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_0( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run() { + if (!del) { + (object_->*member_)(); + } else { + (object_->*member_)(); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_0::base* +NewTessCallback( + const T1* obj, R (T2::*member)() const) { + return new _ConstTessMemberResultCallback_0_0( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_0::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)() const) { + return new _ConstTessMemberResultCallback_0_0( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)() ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_0( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(); + return result; + } else { + R result = (object_->*member_)(); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_0 + : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)() ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_0( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run() { + if (!del) { + (object_->*member_)(); + } else { + (object_->*member_)(); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_0::base* +NewTessCallback( + T1* obj, R (T2::*member)() ) { + return new _TessMemberResultCallback_0_0( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_0::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)() ) { + return new _TessMemberResultCallback_0_0( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_0( + FunctionSignature function) + : function_(function) { + } + + virtual R Run() { + if (!del) { + R result = (*function_)(); + return result; + } else { + R result = (*function_)(); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_0 + : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_0( + FunctionSignature function) + : function_(function) { + } + + virtual void Run() { + if (!del) { + (*function_)(); + } else { + (*function_)(); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_0::base* +NewTessCallback(R (*function)()) { + return new _TessFunctionResultCallback_0_0(function); +} + +template +inline typename _TessFunctionResultCallback_0_0::base* +NewPermanentTessCallback(R (*function)()) { + return new _TessFunctionResultCallback_0_0(function); +} + + + +// Specified by TR1 [4.7.2] Reference modifications. +template struct remove_reference; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +// Identity::type is a typedef of T. Useful for preventing the +// compiler from inferring the type of an argument in templates. +template +struct Identity { + typedef T type; +}; + +template +class _ConstTessMemberResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_0(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_); + return result; + } else { + R result = (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_0(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_); + } else { + (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_0( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_); + return result; + } else { + R result = (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_0( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_); + } else { + (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_0(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_); + return result; + } else { + R result = (*function_)(p1_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_0(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_); + } else { + (*function_)(p1_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_0::base* +NewTessCallback(R (*function)(P1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_0(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_0::base* +NewPermanentTessCallback(R (*function)(P1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_0(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_0(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_0(const T* object, MemberSignature member, P1 p1, P2 p2) + : + object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_); + } else { + (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_0( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_0( T* object, MemberSignature member, P1 p1, P2 p2) + : + object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_); + } else { + (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_0(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_); + return result; + } else { + R result = (*function_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_0(FunctionSignature function, P1 p1, P2 p2) + : + function_(function), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_); + } else { + (*function_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_0::base* +NewTessCallback(R (*function)(P1,P2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_0(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_0::base* +NewPermanentTessCallback(R (*function)(P1,P2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_0(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : + object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_); + } else { + (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_); + } else { + (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_0(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_0(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_); + } else { + (*function_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_0::base* +NewTessCallback(R (*function)(P1,P2,P3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_0(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_0(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_); + } else { + (*function_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_0(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_0(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_0(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_0(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_0(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_0(function, p1, p2, p3, p4, p5, p6); +} + +template +class TessCallback1 { + public: + virtual ~TessCallback1() { } + virtual void Run(A1) = 0; +}; + +template +class TessResultCallback1 { + public: + virtual ~TessResultCallback1() { } + virtual R Run(A1) = 0; +}; + + +template +class TessCallback2 { + public: + virtual ~TessCallback2() { } + virtual void Run(A1,A2) = 0; +}; + +template +class TessResultCallback2 { + public: + virtual ~TessResultCallback2() { } + virtual R Run(A1,A2) = 0; +}; + +template +class TessCallback3 { + public: + virtual ~TessCallback3() { } + virtual void Run(A1,A2,A3) = 0; +}; + +template +class TessResultCallback3 { + public: + virtual ~TessResultCallback3() { } + virtual R Run(A1,A2,A3) = 0; +}; + +template +class TessCallback4 { + public: + virtual ~TessCallback4() { } + virtual void Run(A1,A2,A3,A4) = 0; +}; + +template +class TessResultCallback4 { + public: + virtual ~TessResultCallback4() { } + virtual R Run(A1,A2,A3,A4) = 0; +}; + +template +class _ConstTessMemberResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(A1) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_1( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(a1); + return result; + } else { + R result = (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(A1) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_1( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(a1); + } else { + (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_1::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1) const) { + return new _ConstTessMemberResultCallback_0_1( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_1::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1) const) { + return new _ConstTessMemberResultCallback_0_1( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(A1) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_1( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(a1); + return result; + } else { + R result = (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(A1) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_1( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(a1); + } else { + (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_1::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1) ) { + return new _TessMemberResultCallback_0_1( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_1::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1) ) { + return new _TessMemberResultCallback_0_1( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(A1); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_1( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(a1); + return result; + } else { + R result = (*function_)(a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(A1); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_1( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(a1); + } else { + (*function_)(a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_1::base* +NewTessCallback(R (*function)(A1)) { + return new _TessFunctionResultCallback_0_1(function); +} + +template +inline typename _TessFunctionResultCallback_0_1::base* +NewPermanentTessCallback(R (*function)(A1)) { + return new _TessFunctionResultCallback_0_1(function); +} + +template +class _ConstTessMemberResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_1(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_1(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,a1); + } else { + (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_1( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_1( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,a1); + } else { + (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_1(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,a1); + return result; + } else { + R result = (*function_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_1(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,a1); + } else { + (*function_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_1::base* +NewTessCallback(R (*function)(P1,A1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_1(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_1::base* +NewPermanentTessCallback(R (*function)(P1,A1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_1(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_1(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_1(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,a1); + } else { + (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_1( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_1( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,a1); + } else { + (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_1(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_1(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,a1); + } else { + (*function_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_1::base* +NewTessCallback(R (*function)(P1,P2,A1), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_1(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_1(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_1(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_1(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1); + } else { + (*function_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_1::base* +NewTessCallback(R (*function)(P1,P2,P3,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_1(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_1(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_1(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_1(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_1(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_1(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_1(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_1(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_2( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(a1,a2); + return result; + } else { + R result = (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_2( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(a1,a2); + } else { + (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_2::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1,A2) const) { + return new _ConstTessMemberResultCallback_0_2( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_2::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1,A2) const) { + return new _ConstTessMemberResultCallback_0_2( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(A1,A2) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_2( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(a1,a2); + return result; + } else { + R result = (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(A1,A2) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_2( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(a1,a2); + } else { + (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_2::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1,A2) ) { + return new _TessMemberResultCallback_0_2( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_2::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1,A2) ) { + return new _TessMemberResultCallback_0_2( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(A1,A2); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_2( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(a1,a2); + return result; + } else { + R result = (*function_)(a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(A1,A2); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_2( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(a1,a2); + } else { + (*function_)(a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_2::base* +NewTessCallback(R (*function)(A1,A2)) { + return new _TessFunctionResultCallback_0_2(function); +} + +template +inline typename _TessFunctionResultCallback_0_2::base* +NewPermanentTessCallback(R (*function)(A1,A2)) { + return new _TessFunctionResultCallback_0_2(function); +} + +template +class _ConstTessMemberResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_2(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_2(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,a1,a2); + } else { + (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_2( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_2( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,a1,a2); + } else { + (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_2(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_2(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,a1,a2); + } else { + (*function_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_2::base* +NewTessCallback(R (*function)(P1,A1,A2), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_2(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_2::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_2(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_2(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_2(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_2( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_2( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_2(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_2(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,a1,a2); + } else { + (*function_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_2::base* +NewTessCallback(R (*function)(P1,P2,A1,A2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_2(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_2(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_2(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_2(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_2::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_2(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_2(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_2(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_2(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_2(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_2(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_2(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_2(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_3( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_3( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(a1,a2,a3); + } else { + (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_3::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1,A2,A3) const) { + return new _ConstTessMemberResultCallback_0_3( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_3::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1,A2,A3) const) { + return new _ConstTessMemberResultCallback_0_3( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_3( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_3( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(a1,a2,a3); + } else { + (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_3::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1,A2,A3) ) { + return new _TessMemberResultCallback_0_3( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_3::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1,A2,A3) ) { + return new _TessMemberResultCallback_0_3( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(A1,A2,A3); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_3( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(a1,a2,a3); + return result; + } else { + R result = (*function_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(A1,A2,A3); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_3( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(a1,a2,a3); + } else { + (*function_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_3::base* +NewTessCallback(R (*function)(A1,A2,A3)) { + return new _TessFunctionResultCallback_0_3(function); +} + +template +inline typename _TessFunctionResultCallback_0_3::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3)) { + return new _TessFunctionResultCallback_0_3(function); +} + +template +class _ConstTessMemberResultCallback_1_3 + : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3) const; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3) const; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3); + } else { + (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3); + } else { + (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (*function_)(p1_,a1,a2,a3); + } else { + (*function_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_3::base* +NewTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_3(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_3::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_3(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_3(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_3(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_3( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_3( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_3(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_3(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_3::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_3(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_3(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_3(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_3(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_3::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_3(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_3(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_3(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_3(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_3(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_3(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_3(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_3(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_4(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_4(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4); + } else { + (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4) const) { + return new _ConstTessMemberResultCallback_0_4(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4) const) { + return new _ConstTessMemberResultCallback_0_4(obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_4( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_4( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4); + } else { + (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_4::base* +NewTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4) ) { + return new _TessMemberResultCallback_0_4(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4) ) { + return new _TessMemberResultCallback_0_4(obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(A1,A2,A3,A4); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_4(FunctionSignature function) + : function_(function) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(A1,A2,A3,A4); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_4(FunctionSignature function) + : function_(function) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(a1,a2,a3,a4); + } else { + (*function_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_4::base* +NewTessCallback(R (*function)(A1,A2,A3,A4)) { + return new _TessFunctionResultCallback_0_4(function); +} + +template +inline typename _TessFunctionResultCallback_0_4::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3,A4)) { + return new _TessFunctionResultCallback_0_4(function); +} + +template +class _ConstTessMemberResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_4(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_4(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_4( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_4( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_4(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_4(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,a1,a2,a3,a4); + } else { + (*function_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_4::base* +NewTessCallback(R (*function)(P1,A1,A2,A3,A4), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_4(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_4::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3,A4), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_4(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_4(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_4(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_4( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_4( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_4(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_4(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_4::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_4(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_4(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_4(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_4(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_4::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_4(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_4(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_4(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_4(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_4(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_4(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_4(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_4(function, p1, p2, p3, p4, p5, p6); +} + +template +class TessCallback5 { + public: + virtual ~TessCallback5() { } + virtual void Run(A1,A2,A3,A4,A5) = 0; +}; + +template +class TessResultCallback5 { + public: + virtual ~TessResultCallback5() { } + virtual R Run(A1,A2,A3,A4,A5) = 0; +}; + +template +class _ConstTessMemberResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_5(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_5(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4,a5); + } else { + (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) const) { + return new _ConstTessMemberResultCallback_0_5(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) const) { + return new _ConstTessMemberResultCallback_0_5(obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_5( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_5( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4,a5); + } else { + (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_5::base* +NewTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) ) { + return new _TessMemberResultCallback_0_5(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) ) { + return new _TessMemberResultCallback_0_5(obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_5(FunctionSignature function) + : function_(function) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_5(FunctionSignature function) + : function_(function) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(a1,a2,a3,a4,a5); + } else { + (*function_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_5::base* +NewTessCallback(R (*function)(A1,A2,A3,A4,A5)) { + return new _TessFunctionResultCallback_0_5(function); +} + +template +inline typename _TessFunctionResultCallback_0_5::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3,A4,A5)) { + return new _TessFunctionResultCallback_0_5(function); +} + +template +class _ConstTessMemberResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_5(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_5(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_5( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_5( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_5(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_5(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_5::base* +NewTessCallback(R (*function)(P1,A1,A2,A3,A4,A5), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_5(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_5::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3,A4,A5), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_5(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_5(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_5(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_5( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_5( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_5(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_5(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_5::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_5(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_5(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_5(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_5(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_5(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_5(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_5(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_5(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_5::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_5(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_5(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_5(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_5(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_5(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_5(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_5(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_5(function, p1, p2, p3, p4, p5, p6); +} + +#endif /* _TESS_CALLBACK_SPECIALIZATIONS_H */ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.cpp new file mode 100644 index 0000000..36b1c5f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.cpp @@ -0,0 +1,280 @@ +/////////////////////////////////////////////////////////////////////// +// File: tessdatamanager.cpp +// Description: Functions to handle loading/combining tesseract data files. +// Author: Daria Antonova +// Created: Wed Jun 03 11:26:43 PST 2009 +// +// (C) Copyright 2009, 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. +// +/////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma warning(disable:4244) // Conversion warnings +#endif + +#include "tessdatamanager.h" + +#include + +#include "helpers.h" +#include "serialis.h" +#include "strngs.h" +#include "tprintf.h" +#include "params.h" + +namespace tesseract { + +bool TessdataManager::Init(const char *data_file_name, int debug_level) { + int i; + debug_level_ = debug_level; + data_file_name_ = data_file_name; + data_file_ = fopen(data_file_name, "rb"); + if (data_file_ == NULL) { + tprintf("Error opening data file %s\n", data_file_name); + tprintf("Please make sure the TESSDATA_PREFIX environment variable is set " + "to the parent directory of your \"tessdata\" directory.\n"); + return false; + } + fread(&actual_tessdata_num_entries_, sizeof(inT32), 1, data_file_); + swap_ = (actual_tessdata_num_entries_ > kMaxNumTessdataEntries); + if (swap_) { + ReverseN(&actual_tessdata_num_entries_, + sizeof(actual_tessdata_num_entries_)); + } + if (actual_tessdata_num_entries_ > TESSDATA_NUM_ENTRIES) { + // For forward compatibility, truncate to the number we can handle. + actual_tessdata_num_entries_ = TESSDATA_NUM_ENTRIES; + } + fread(offset_table_, sizeof(inT64), + actual_tessdata_num_entries_, data_file_); + if (swap_) { + for (i = 0 ; i < actual_tessdata_num_entries_; ++i) { + ReverseN(&offset_table_[i], sizeof(offset_table_[i])); + } + } + if (debug_level_) { + tprintf("TessdataManager loaded %d types of tesseract data files.\n", + actual_tessdata_num_entries_); + for (i = 0; i < actual_tessdata_num_entries_; ++i) { + tprintf("Offset for type %d is %lld\n", i, offset_table_[i]); + } + } + return true; +} + +void TessdataManager::CopyFile(FILE *input_file, FILE *output_file, + bool newline_end, inT64 num_bytes_to_copy) { + if (num_bytes_to_copy == 0) return; + int buffer_size = 1024; + if (num_bytes_to_copy > 0 && buffer_size > num_bytes_to_copy) { + buffer_size = num_bytes_to_copy; + } + inT64 num_bytes_copied = 0; + char *chunk = new char[buffer_size]; + int bytes_read; + char last_char = 0x0; + while ((bytes_read = fread(chunk, sizeof(char), + buffer_size, input_file))) { + fwrite(chunk, sizeof(char), bytes_read, output_file); + last_char = chunk[bytes_read-1]; + if (num_bytes_to_copy > 0) { + num_bytes_copied += bytes_read; + if (num_bytes_copied == num_bytes_to_copy) break; + if (num_bytes_copied + buffer_size > num_bytes_to_copy) { + buffer_size = num_bytes_to_copy - num_bytes_copied; + } + } + } + if (newline_end) ASSERT_HOST(last_char == '\n'); + delete[] chunk; +} + +bool TessdataManager::WriteMetadata(inT64 *offset_table, + const char * language_data_path_prefix, + FILE *output_file) { + inT32 num_entries = TESSDATA_NUM_ENTRIES; + bool result = true; + if (fseek(output_file, 0, SEEK_SET) != 0 || + fwrite(&num_entries, sizeof(inT32), 1, output_file) != 1 || + fwrite(offset_table, sizeof(inT64), TESSDATA_NUM_ENTRIES, + output_file) != TESSDATA_NUM_ENTRIES) { + fclose(output_file); + result = false; + tprintf("WriteMetadata failed in TessdataManager!\n"); + } else if (fclose(output_file)) { + result = false; + tprintf("WriteMetadata failed to close file!\n"); + } else { + tprintf("TessdataManager combined tesseract data files.\n"); + for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + tprintf("Offset for type %2d (%s%-22s) is %lld\n", i, + language_data_path_prefix, kTessdataFileSuffixes[i], + offset_table[i]); + } + } + return result; +} + +bool TessdataManager::CombineDataFiles( + const char *language_data_path_prefix, + const char *output_filename) { + int i; + inT64 offset_table[TESSDATA_NUM_ENTRIES]; + for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) offset_table[i] = -1; + FILE *output_file = fopen(output_filename, "wb"); + if (output_file == NULL) { + tprintf("Error opening %s for writing\n", output_filename); + return false; + } + // Leave some space for recording the offset_table. + if (fseek(output_file, + sizeof(inT32) + sizeof(inT64) * TESSDATA_NUM_ENTRIES, SEEK_SET)) { + tprintf("Error seeking %s\n", output_filename); + fclose(output_file); + return false; + } + + TessdataType type = TESSDATA_NUM_ENTRIES; + bool text_file = false; + FILE *file_ptr[TESSDATA_NUM_ENTRIES]; + + // Load individual tessdata components from files. + for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + ASSERT_HOST(TessdataTypeFromFileSuffix( + kTessdataFileSuffixes[i], &type, &text_file)); + STRING filename = language_data_path_prefix; + filename += kTessdataFileSuffixes[i]; + file_ptr[i] = fopen(filename.string(), "rb"); + if (file_ptr[i] != NULL) { + offset_table[type] = ftell(output_file); + CopyFile(file_ptr[i], output_file, text_file, -1); + fclose(file_ptr[i]); + } + } + + // Make sure that the required components are present. + if (file_ptr[TESSDATA_UNICHARSET] == NULL) { + tprintf("Error opening %sunicharset file\n", language_data_path_prefix); + fclose(output_file); + return false; + } + if (file_ptr[TESSDATA_INTTEMP] != NULL && + (file_ptr[TESSDATA_PFFMTABLE] == NULL || + file_ptr[TESSDATA_NORMPROTO] == NULL)) { + tprintf("Error opening %spffmtable and/or %snormproto files" + " while %sinttemp file was present\n", language_data_path_prefix, + language_data_path_prefix, language_data_path_prefix); + fclose(output_file); + return false; + } + + return WriteMetadata(offset_table, language_data_path_prefix, output_file); +} + +bool TessdataManager::OverwriteComponents( + const char *new_traineddata_filename, + char **component_filenames, + int num_new_components) { + int i; + inT64 offset_table[TESSDATA_NUM_ENTRIES]; + TessdataType type = TESSDATA_NUM_ENTRIES; + bool text_file = false; + FILE *file_ptr[TESSDATA_NUM_ENTRIES]; + for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + offset_table[i] = -1; + file_ptr[i] = NULL; + } + FILE *output_file = fopen(new_traineddata_filename, "wb"); + if (output_file == NULL) { + tprintf("Error opening %s for writing\n", new_traineddata_filename); + return false; + } + + // Leave some space for recording the offset_table. + if (fseek(output_file, + sizeof(inT32) + sizeof(inT64) * TESSDATA_NUM_ENTRIES, SEEK_SET)) { + fclose(output_file); + tprintf("Error seeking %s\n", new_traineddata_filename); + return false; + } + + // Open the files with the new components. + for (i = 0; i < num_new_components; ++i) { + if (TessdataTypeFromFileName(component_filenames[i], &type, &text_file)) + file_ptr[type] = fopen(component_filenames[i], "rb"); + } + + // Write updated data to the output traineddata file. + for (i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + if (file_ptr[i] != NULL) { + // Get the data from the opened component file. + offset_table[i] = ftell(output_file); + CopyFile(file_ptr[i], output_file, kTessdataFileIsText[i], -1); + fclose(file_ptr[i]); + } else { + // Get this data component from the loaded data file. + if (SeekToStart(static_cast(i))) { + offset_table[i] = ftell(output_file); + CopyFile(data_file_, output_file, kTessdataFileIsText[i], + GetEndOffset(static_cast(i)) - + ftell(data_file_) + 1); + } + } + } + const char *language_data_path_prefix = strchr(new_traineddata_filename, '.'); + return WriteMetadata(offset_table, language_data_path_prefix, output_file); +} + +bool TessdataManager::TessdataTypeFromFileSuffix( + const char *suffix, TessdataType *type, bool *text_file) { + for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + if (strcmp(kTessdataFileSuffixes[i], suffix) == 0) { + *type = static_cast(i); + *text_file = kTessdataFileIsText[i]; + return true; + } + } + tprintf("TessdataManager can't determine which tessdata" + " component is represented by %s\n", suffix); + return false; +} + +bool TessdataManager::TessdataTypeFromFileName( + const char *filename, TessdataType *type, bool *text_file) { + // Get the file suffix (extension) + const char *suffix = strrchr(filename, '.'); + if (suffix == NULL || *(++suffix) == '\0') return false; + return TessdataTypeFromFileSuffix(suffix, type, text_file); +} + +bool TessdataManager::ExtractToFile(const char *filename) { + TessdataType type = TESSDATA_NUM_ENTRIES; + bool text_file = false; + ASSERT_HOST(tesseract::TessdataManager::TessdataTypeFromFileName( + filename, &type, &text_file)); + if (!SeekToStart(type)) return false; + + FILE *output_file = fopen(filename, "wb"); + if (output_file == NULL) { + tprintf("Error opening %s\n", filename); + exit(1); + } + inT64 begin_offset = ftell(GetDataFilePtr()); + inT64 end_offset = GetEndOffset(type); + tesseract::TessdataManager::CopyFile( + GetDataFilePtr(), output_file, text_file, + end_offset - begin_offset + 1); + fclose(output_file); + return true; +} + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.h b/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.h new file mode 100644 index 0000000..e583b70 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/tessdatamanager.h @@ -0,0 +1,299 @@ +/////////////////////////////////////////////////////////////////////// +// File: tessdatamanager.h +// Description: Functions to handle loading/combining tesseract data files. +// Author: Daria Antonova +// Created: Wed Jun 03 11:26:43 PST 2009 +// +// (C) Copyright 2009, 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_TESSDATAMANAGER_H_ +#define TESSERACT_CCUTIL_TESSDATAMANAGER_H_ + +#include + +#include "host.h" +#include "strngs.h" +#include "tprintf.h" + +static const char kTrainedDataSuffix[] = "traineddata"; + +// When adding new tessdata types and file suffixes, please make sure to +// update TessdataType enum, kTessdataFileSuffixes and kTessdataFileIsText. +static const char kLangConfigFileSuffix[] = "config"; +static const char kUnicharsetFileSuffix[] = "unicharset"; +static const char kAmbigsFileSuffix[] = "unicharambigs"; +static const char kBuiltInTemplatesFileSuffix[] = "inttemp"; +static const char kBuiltInCutoffsFileSuffix[] = "pffmtable"; +static const char kNormProtoFileSuffix[] = "normproto"; +static const char kPuncDawgFileSuffix[] = "punc-dawg"; +static const char kSystemDawgFileSuffix[] = "word-dawg"; +static const char kNumberDawgFileSuffix[] = "number-dawg"; +static const char kFreqDawgFileSuffix[] = "freq-dawg"; +static const char kFixedLengthDawgsFileSuffix[] = "fixed-length-dawgs"; +static const char kCubeUnicharsetFileSuffix[] = "cube-unicharset"; +static const char kCubeSystemDawgFileSuffix[] = "cube-word-dawg"; +static const char kShapeTableFileSuffix[] = "shapetable"; +static const char kBigramDawgFileSuffix[] = "bigram-dawg"; +static const char kUnambigDawgFileSuffix[] = "unambig-dawg"; +static const char kParamsModelFileSuffix[] = "params-model"; + +namespace tesseract { + +enum TessdataType { + TESSDATA_LANG_CONFIG, // 0 + TESSDATA_UNICHARSET, // 1 + TESSDATA_AMBIGS, // 2 + TESSDATA_INTTEMP, // 3 + TESSDATA_PFFMTABLE, // 4 + TESSDATA_NORMPROTO, // 5 + TESSDATA_PUNC_DAWG, // 6 + TESSDATA_SYSTEM_DAWG, // 7 + TESSDATA_NUMBER_DAWG, // 8 + TESSDATA_FREQ_DAWG, // 9 + TESSDATA_FIXED_LENGTH_DAWGS, // 10 // deprecated + TESSDATA_CUBE_UNICHARSET, // 11 + TESSDATA_CUBE_SYSTEM_DAWG, // 12 + TESSDATA_SHAPE_TABLE, // 13 + TESSDATA_BIGRAM_DAWG, // 14 + TESSDATA_UNAMBIG_DAWG, // 15 + TESSDATA_PARAMS_MODEL, // 16 + + TESSDATA_NUM_ENTRIES +}; + +/** + * kTessdataFileSuffixes[i] indicates the file suffix for + * tessdata of type i (from TessdataType enum). + */ +static const char *const kTessdataFileSuffixes[] = { + kLangConfigFileSuffix, // 0 + kUnicharsetFileSuffix, // 1 + kAmbigsFileSuffix, // 2 + kBuiltInTemplatesFileSuffix, // 3 + kBuiltInCutoffsFileSuffix, // 4 + kNormProtoFileSuffix, // 5 + kPuncDawgFileSuffix, // 6 + kSystemDawgFileSuffix, // 7 + kNumberDawgFileSuffix, // 8 + kFreqDawgFileSuffix, // 9 + kFixedLengthDawgsFileSuffix, // 10 // deprecated + kCubeUnicharsetFileSuffix, // 11 + kCubeSystemDawgFileSuffix, // 12 + kShapeTableFileSuffix, // 13 + kBigramDawgFileSuffix, // 14 + kUnambigDawgFileSuffix, // 15 + kParamsModelFileSuffix, // 16 +}; + +/** + * If kTessdataFileIsText[i] is true - the tessdata component + * of type i (from TessdataType enum) is text, and is binary otherwise. + */ +static const bool kTessdataFileIsText[] = { + true, // 0 + true, // 1 + true, // 2 + false, // 3 + true, // 4 + true, // 5 + false, // 6 + false, // 7 + false, // 8 + false, // 9 + false, // 10 // deprecated + true, // 11 + false, // 12 + false, // 13 + false, // 14 + false, // 15 + true, // 16 +}; + +/** + * TessdataType could be updated to contain more entries, however + * we do not expect that number to be astronomically high. + * In order to automatically detect endianness TessdataManager will + * flip the bits if actual_tessdata_num_entries_ is larger than + * kMaxNumTessdataEntries. + */ +static const int kMaxNumTessdataEntries = 1000; + + +class TessdataManager { + public: + TessdataManager() { + data_file_ = NULL; + actual_tessdata_num_entries_ = 0; + for (int i = 0; i < TESSDATA_NUM_ENTRIES; ++i) { + offset_table_[i] = -1; + } + } + ~TessdataManager() {} + int DebugLevel() { return debug_level_; } + + /** + * Opens the given data file and reads the offset table. + * @return true on success. + */ + bool Init(const char *data_file_name, int debug_level); + + // Return the name of the underlying data file. + const STRING &GetDataFileName() const { return data_file_name_; } + + /** Returns data file pointer. */ + inline FILE *GetDataFilePtr() const { return data_file_; } + + /** + * Returns false if there is no data of the given type. + * Otherwise does a seek on the data_file_ to position the pointer + * at the start of the data of the given type. + */ + inline bool SeekToStart(TessdataType tessdata_type) { + if (debug_level_) { + tprintf("TessdataManager: seek to offset %lld - start of tessdata" + "type %d (%s))\n", offset_table_[tessdata_type], + tessdata_type, kTessdataFileSuffixes[tessdata_type]); + } + if (offset_table_[tessdata_type] < 0) { + return false; + } else { + ASSERT_HOST(fseek(data_file_, + static_cast(offset_table_[tessdata_type]), + SEEK_SET) == 0); + return true; + } + } + /** Returns the end offset for the given tesseract data file type. */ + inline inT64 GetEndOffset(TessdataType tessdata_type) const { + int index = tessdata_type + 1; + while (index < actual_tessdata_num_entries_ && offset_table_[index] == -1) { + ++index; // skip tessdata types not present in the combined file + } + if (debug_level_) { + tprintf("TessdataManager: end offset for type %d is %lld\n", + tessdata_type, + (index == actual_tessdata_num_entries_) ? -1 + : offset_table_[index]); + } + return (index == actual_tessdata_num_entries_) ? -1 : offset_table_[index] - 1; + } + /** Closes data_file_ (if it was opened by Init()). */ + inline void End() { + if (data_file_ != NULL) { + fclose(data_file_); + data_file_ = NULL; + } + } + bool swap() const { + return swap_; + } + + /** Writes the number of entries and the given offset table to output_file. + * Returns false on error. + */ + static bool WriteMetadata(inT64 *offset_table, + const char *language_data_path_prefix, + FILE *output_file); + + /** + * Reads all the standard tesseract config and data files for a language + * at the given path and bundles them up into one binary data file. + * Returns true if the combined traineddata file was successfully written. + */ + static bool CombineDataFiles(const char *language_data_path_prefix, + const char *output_filename); + + /** + * Gets the individual components from the data_file_ with which the class was + * initialized. Overwrites the components specified by component_filenames. + * Writes the updated traineddata file to new_traineddata_filename. + */ + bool OverwriteComponents(const char *new_traineddata_filename, + char **component_filenames, + int num_new_components); + + /** + * Extracts tessdata component implied by the name of the input file from + * the combined traineddata loaded into TessdataManager. + * Writes the extracted component to the file indicated by the file name. + * E.g. if the filename given is somepath/somelang.unicharset, unicharset + * will be extracted from the data loaded into the TessdataManager and will + * be written to somepath/somelang.unicharset. + * @return true if the component was successfully extracted, false if the + * component was not present in the traineddata loaded into TessdataManager. + */ + bool ExtractToFile(const char *filename); + + /** + * Copies data from the given input file to the output_file provided. + * If num_bytes_to_copy is >= 0, only num_bytes_to_copy is copied from + * the input file, otherwise all the data in the input file is copied. + */ + static void CopyFile(FILE *input_file, FILE *output_file, + bool newline_end, inT64 num_bytes_to_copy); + + /** + * Fills type with TessdataType of the tessdata component represented by the + * given file name. E.g. tessdata/eng.unicharset -> TESSDATA_UNICHARSET. + * Sets *text_file to true if the component is in text format (e.g. + * unicharset, unichar ambigs, config, etc). + * @return true if the tessdata component type could be determined + * from the given file name. + */ + static bool TessdataTypeFromFileSuffix(const char *suffix, + TessdataType *type, + bool *text_file); + + /** + * Tries to determine tessdata component file suffix from filename, + * returns true on success. + */ + static bool TessdataTypeFromFileName(const char *filename, + TessdataType *type, + bool *text_file); + + private: + + /** + * Opens the file whose name is a concatenation of language_data_path_prefix + * and file_suffix. Returns a file pointer to the opened file. + */ + static FILE *GetFilePtr(const char *language_data_path_prefix, + const char *file_suffix, bool text_file); + + /** + * Each offset_table_[i] contains a file offset in the combined data file + * where the data of TessdataFileType i is stored. + */ + inT64 offset_table_[TESSDATA_NUM_ENTRIES]; + /** + * Actual number of entries in the tessdata table. This value can only be + * same or smaller than TESSDATA_NUM_ENTRIES, but can never be larger, + * since then it would be impossible to interpret the type of tessdata at + * indices same and higher than TESSDATA_NUM_ENTRIES. + * This parameter is used to allow for backward compatibility + * when new tessdata types are introduced. + */ + inT32 actual_tessdata_num_entries_; + STRING data_file_name_; // name of the data file. + FILE *data_file_; ///< pointer to the data file. + int debug_level_; + // True if the bytes need swapping. + bool swap_; +}; + + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_TESSDATAMANAGER_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.cpp new file mode 100644 index 0000000..926c1bc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.cpp @@ -0,0 +1,71 @@ +/********************************************************************** + * File: tprintf.c + * Description: Trace version of printf - portable between UX and NT + * Author: Phil Cheatle + * Created: Wed Jun 28 15:01:15 BST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include +#include +#include "ccutil.h" +#include "params.h" +#include "strngs.h" +#include "tprintf.h" + +#define MAX_MSG_LEN 65536 + +#define EXTERN +// Since tprintf is protected by a mutex, these parameters can remain global. +DLLSYM STRING_VAR(debug_file, "", "File to send tprintf output to"); + +DLLSYM void +tprintf_internal( // Trace printf + const char *format, ... // Message +) { + tesseract::tprintfMutex.Lock(); + va_list args; // variable args + static FILE *debugfp = NULL; // debug file + // debug window + inT32 offset = 0; // into message + static char msg[MAX_MSG_LEN + 1]; + + va_start(args, format); // variable list + // Format into msg + #ifdef _WIN32 + offset += _vsnprintf(msg + offset, MAX_MSG_LEN - offset, format, args); + if (strcmp(debug_file.string(), "/dev/null") == 0) + debug_file.set_value("nul"); + #else + offset += vsnprintf(msg + offset, MAX_MSG_LEN - offset, format, args); + #endif + va_end(args); + + if (debugfp == NULL && strlen(debug_file.string()) > 0) { + debugfp = fopen(debug_file.string(), "wb"); + } else if (debugfp != NULL && strlen(debug_file.string()) == 0) { + fclose(debugfp); + debugfp = NULL; + } + if (debugfp != NULL) + fprintf(debugfp, "%s", msg); + else + fprintf(stderr, "%s", msg); + tesseract::tprintfMutex.Unlock(); +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.h b/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.h new file mode 100644 index 0000000..27a3382 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/tprintf.h @@ -0,0 +1,36 @@ +/********************************************************************** + * File: tprintf.h + * Description: Trace version of printf - portable between UX and NT + * Author: Phil Cheatle + * Created: Wed Jun 28 15:01:15 BST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSERACT_CCUTIL_TPRINTF_H +#define TESSERACT_CCUTIL_TPRINTF_H + +#include "params.h" + +extern DLLSYM STRING_VAR_H(debug_file, "", + "File to send tprintf output to"); +extern DLLSYM BOOL_VAR_H(debug_window_on, TRUE, + "Send tprintf to window unless file set"); + +// Main logging function. +#define tprintf(...) tprintf_internal(__VA_ARGS__) + +extern TESS_API void tprintf_internal( // Trace printf + const char *format, ...); // Message + +#endif // define TESSERACT_CCUTIL_TPRINTF_H diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.cpp new file mode 100644 index 0000000..0ceced1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.cpp @@ -0,0 +1,225 @@ +/////////////////////////////////////////////////////////////////////// +// File: unichar.cpp +// Description: Unicode character/ligature class. +// Author: Ray Smith +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "unichar.h" +#include "errcode.h" +#include "genericvector.h" +#include "tprintf.h" + +#define UNI_MAX_LEGAL_UTF32 0x0010FFFF + +// Construct from a utf8 string. If len<0 then the string is null terminated. +// If the string is too long to fit in the UNICHAR then it takes only what +// will fit. Checks for illegal input and stops at an illegal sequence. +// The resulting UNICHAR may be empty. +UNICHAR::UNICHAR(const char* utf8_str, int len) { + int total_len = 0; + int step = 0; + if (len < 0) { + for (len = 0; len < UNICHAR_LEN && utf8_str[len] != 0; ++len); + } + for (total_len = 0; total_len < len; total_len += step) { + step = utf8_step(utf8_str + total_len); + if (total_len + step > UNICHAR_LEN) + break; // Too long. + if (step == 0) + break; // Illegal first byte. + int i; + for (i = 1; i < step; ++i) + if ((utf8_str[total_len + i] & 0xc0) != 0x80) + break; + if (i < step) + break; // Illegal surrogate + } + memcpy(chars, utf8_str, total_len); + if (total_len < UNICHAR_LEN) { + chars[UNICHAR_LEN - 1] = total_len; + while (total_len < UNICHAR_LEN - 1) + chars[total_len++] = 0; + } +} + +// Construct from a single UCS4 character. Illegal values are ignored, +// resulting in an empty UNICHAR. +UNICHAR::UNICHAR(int unicode) { + const int bytemask = 0xBF; + const int bytemark = 0x80; + + if (unicode < 0x80) { + chars[UNICHAR_LEN - 1] = 1; + chars[2] = 0; + chars[1] = 0; + chars[0] = static_cast(unicode); + } else if (unicode < 0x800) { + chars[UNICHAR_LEN - 1] = 2; + chars[2] = 0; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xc0); + } else if (unicode < 0x10000) { + chars[UNICHAR_LEN - 1] = 3; + chars[2] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xe0); + } else if (unicode <= UNI_MAX_LEGAL_UTF32) { + chars[UNICHAR_LEN - 1] = 4; + chars[3] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[2] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xf0); + } else { + memset(chars, 0, UNICHAR_LEN); + } +} + +// Get the first character as UCS-4. +int UNICHAR::first_uni() const { + static const int utf8_offsets[5] = { + 0, 0, 0x3080, 0xE2080, 0x3C82080 + }; + int uni = 0; + int len = utf8_step(chars); + const char* src = chars; + + switch (len) { + default: + break; + case 4: + uni += static_cast(*src++); + uni <<= 6; + case 3: + uni += static_cast(*src++); + uni <<= 6; + case 2: + uni += static_cast(*src++); + uni <<= 6; + case 1: + uni += static_cast(*src++); + } + uni -= utf8_offsets[len]; + return uni; +} + +// Get a terminated UTF8 string: Must delete[] it after use. +char* UNICHAR::utf8_str() const { + int len = utf8_len(); + char* str = new char[len + 1]; + memcpy(str, chars, len); + str[len] = 0; + return str; +} + +// Get the number of bytes in the first character of the given utf8 string. +int UNICHAR::utf8_step(const char* utf8_str) { + static const char utf8_bytes[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0 + }; + + return utf8_bytes[static_cast(*utf8_str)]; +} + +UNICHAR::const_iterator& UNICHAR::const_iterator::operator++() { + ASSERT_HOST(it_ != NULL); + int step = utf8_step(it_); + if (step == 0) { + tprintf("ERROR: Illegal UTF8 encountered.\n"); + for (int i = 0; i < 5 && it_[i] != '\0'; ++i) { + tprintf("Index %d char = 0x%x\n", i, it_[i]); + } + step = 1; + } + it_ += step; + return *this; +} + +int UNICHAR::const_iterator::operator*() const { + ASSERT_HOST(it_ != NULL); + const int len = utf8_step(it_); + if (len == 0) { + tprintf("WARNING: Illegal UTF8 encountered\n"); + return ' '; + } + UNICHAR uch(it_, len); + return uch.first_uni(); +} + +int UNICHAR::const_iterator::get_utf8(char* utf8_output) const { + ASSERT_HOST(it_ != NULL); + const int len = utf8_step(it_); + if (len == 0) { + tprintf("WARNING: Illegal UTF8 encountered\n"); + utf8_output[0] = ' '; + return 1; + } + strncpy(utf8_output, it_, len); + return len; +} + +int UNICHAR::const_iterator::utf8_len() const { + ASSERT_HOST(it_ != NULL); + const int len = utf8_step(it_); + if (len == 0) { + tprintf("WARNING: Illegal UTF8 encountered\n"); + return 1; + } + return len; +} + +bool UNICHAR::const_iterator::is_legal() const { + return utf8_step(it_) > 0; +} + +UNICHAR::const_iterator UNICHAR::begin(const char* utf8_str, const int len) { + return UNICHAR::const_iterator(utf8_str); +} + +UNICHAR::const_iterator UNICHAR::end(const char* utf8_str, const int len) { + return UNICHAR::const_iterator(utf8_str + len); +} + +// Converts a utf-8 string to a vector of unicodes. +// Returns false if the input contains invalid UTF-8, and replaces +// the rest of the string with a single space. +bool UNICHAR::UTF8ToUnicode(const char* utf8_str, + GenericVector* unicodes) { + const int utf8_length = strlen(utf8_str); + const_iterator end_it(end(utf8_str, utf8_length)); + for (const_iterator it(begin(utf8_str, utf8_length)); it != end_it; ++it) { + if (it.is_legal()) { + unicodes->push_back(*it); + } else { + unicodes->push_back(' '); + return false; + } + } + return true; +} + diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.h b/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.h new file mode 100644 index 0000000..c5bc0ae --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unichar.h @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////// +// File: unichar.h +// Description: Unicode character/ligature class. +// Author: Ray Smith +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_UNICHAR_H__ +#define TESSERACT_CCUTIL_UNICHAR_H__ + +#include +#include + +template class GenericVector; + +// Maximum number of characters that can be stored in a UNICHAR. Must be +// at least 4. Must not exceed 31 without changing the coding of length. +#define UNICHAR_LEN 30 + +// A UNICHAR_ID is the unique id of a unichar. +typedef int UNICHAR_ID; + +// A variable to indicate an invalid or uninitialized unichar id. +static const int INVALID_UNICHAR_ID = -1; +// A special unichar that corresponds to INVALID_UNICHAR_ID. +static const char INVALID_UNICHAR[] = "__INVALID_UNICHAR__"; + +enum StrongScriptDirection { + DIR_NEUTRAL = 0, // Text contains only neutral characters. + DIR_LEFT_TO_RIGHT = 1, // Text contains no Right-to-Left characters. + DIR_RIGHT_TO_LEFT = 2, // Text contains no Left-to-Right characters. + DIR_MIX = 3, // Text contains a mixture of left-to-right + // and right-to-left characters. +}; + +// The UNICHAR class holds a single classification result. This may be +// a single Unicode character (stored as between 1 and 4 utf8 bytes) or +// multiple Unicode characters representing the NFKC expansion of a ligature +// such as fi, ffl etc. These are also stored as utf8. +class UNICHAR { + public: + UNICHAR() { + memset(chars, 0, UNICHAR_LEN); + } + + // Construct from a utf8 string. If len<0 then the string is null terminated. + // If the string is too long to fit in the UNICHAR then it takes only what + // will fit. + UNICHAR(const char* utf8_str, int len); + + // Construct from a single UCS4 character. + explicit UNICHAR(int unicode); + + // Default copy constructor and operator= are OK. + + // Get the first character as UCS-4. + int first_uni() const; + + // Get the length of the UTF8 string. + int utf8_len() const { + int len = chars[UNICHAR_LEN - 1]; + return len >=0 && len < UNICHAR_LEN ? len : UNICHAR_LEN; + } + + // Get a UTF8 string, but NOT NULL terminated. + const char* utf8() const { + return chars; + } + + // Get a terminated UTF8 string: Must delete[] it after use. + char* utf8_str() const; + + // Get the number of bytes in the first character of the given utf8 string. + static int utf8_step(const char* utf8_str); + + // A class to simplify iterating over and accessing elements of a UTF8 + // string. Note that unlike the UNICHAR class, const_iterator does NOT COPY or + // take ownership of the underlying byte array. It also does not permit + // modification of the array (as the name suggests). + // + // Example: + // for (UNICHAR::const_iterator it = UNICHAR::begin(str, str_len); + // it != UNICHAR::end(str, len); + // ++it) { + // tprintf("UCS-4 symbol code = %d\n", *it); + // char buf[5]; + // int char_len = it.get_utf8(buf); buf[char_len] = '\0'; + // tprintf("Char = %s\n", buf); + // } + class const_iterator { + typedef const_iterator CI; + + public: + // Step to the next UTF8 character. + // If the current position is at an illegal UTF8 character, then print an + // error message and step by one byte. If the current position is at a NULL + // value, don't step past it. + const_iterator& operator++(); + + // Return the UCS-4 value at the current position. + // If the current position is at an illegal UTF8 value, return a single + // space character. + int operator*() const; + + // Store the UTF-8 encoding of the current codepoint into buf, which must be + // at least 4 bytes long. Return the number of bytes written. + // If the current position is at an illegal UTF8 value, writes a single + // space character and returns 1. + // Note that this method does not null-terminate the buffer. + int get_utf8(char* buf) const; + // Returns the number of bytes of the current codepoint. Returns 1 if the + // current position is at an illegal UTF8 value. + int utf8_len() const; + // Returns true if the UTF-8 encoding at the current position is legal. + bool is_legal() const; + + // Return the pointer into the string at the current position. + const char* utf8_data() const { return it_; } + + // Iterator equality operators. + friend bool operator==(const CI& lhs, const CI& rhs) { + return lhs.it_ == rhs.it_; + } + friend bool operator!=(const CI& lhs, const CI& rhs) { + return !(lhs == rhs); + } + + private: + friend class UNICHAR; + explicit const_iterator(const char* it) : it_(it) {} + + const char* it_; // Pointer into the string. + }; + + // Create a start/end iterator pointing to a string. Note that these methods + // are static and do NOT create a copy or take ownership of the underlying + // array. + static const_iterator begin(const char* utf8_str, const int byte_length); + static const_iterator end(const char* utf8_str, const int byte_length); + + // Converts a utf-8 string to a vector of unicodes. + // Returns false if the input contains invalid UTF-8, and replaces + // the rest of the string with a single space. + static bool UTF8ToUnicode(const char* utf8_str, GenericVector* unicodes); + + private: + // A UTF-8 representation of 1 or more Unicode characters. + // The last element (chars[UNICHAR_LEN - 1]) is a length if + // its value < UNICHAR_LEN, otherwise it is a genuine character. + char chars[UNICHAR_LEN]; +}; + +#endif // TESSERACT_CCUTIL_UNICHAR_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.cpp new file mode 100644 index 0000000..6b1bb1d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.cpp @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////// +// File: unicharmap.cpp +// Description: Unicode character/ligature to integer id class. +// Author: Thomas Kielbus +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include +#include "unichar.h" +#include "host.h" +#include "unicharmap.h" + +UNICHARMAP::UNICHARMAP() : +nodes(0) { +} + +UNICHARMAP::~UNICHARMAP() { + if (nodes != 0) + delete[] nodes; +} + +// Search the given unichar representation in the tree. Each character in the +// string is interpreted as an index in an array of nodes. +UNICHAR_ID UNICHARMAP::unichar_to_id(const char* const unichar_repr) const { + const char* current_char = unichar_repr; + UNICHARMAP_NODE* current_nodes = nodes; + + assert(*unichar_repr != '\0'); + + do { + if (*(current_char + 1) == '\0') + return current_nodes[static_cast(*current_char)].id; + current_nodes = + current_nodes[static_cast(*current_char)].children; + ++current_char; + } while (true); +} + +// Search the given unichar representation in the tree, using length characters +// from it maximum. Each character in the string is interpreted as an index in +// an array of nodes. +UNICHAR_ID UNICHARMAP::unichar_to_id(const char* const unichar_repr, + int length) const { + const char* current_char = unichar_repr; + UNICHARMAP_NODE* current_nodes = nodes; + + assert(*unichar_repr != '\0'); + assert(length > 0 && length <= UNICHAR_LEN); + + do { + if (length == 1 || *(current_char + 1) == '\0') + return current_nodes[static_cast(*current_char)].id; + current_nodes = + current_nodes[static_cast(*current_char)].children; + ++current_char; + --length; + } while (true); +} + +// Search the given unichar representation in the tree, creating the possibly +// missing nodes. Once the right place has been found, insert the given id and +// update the inserted flag to keep track of the insert. Each character in the +// string is interpreted as an index in an array of nodes. +void UNICHARMAP::insert(const char* const unichar_repr, UNICHAR_ID id) { + const char* current_char = unichar_repr; + UNICHARMAP_NODE** current_nodes_pointer = &nodes; + + assert(*unichar_repr != '\0'); + assert(id >= 0); + + do { + if (*current_nodes_pointer == 0) + *current_nodes_pointer = new UNICHARMAP_NODE[256]; + if (*(current_char + 1) == '\0') { + (*current_nodes_pointer) + [static_cast(*current_char)].id = id; + return; + } + current_nodes_pointer = + &((*current_nodes_pointer) + [static_cast(*current_char)].children); + ++current_char; + } while (true); +} + +// Search the given unichar representation in the tree. Each character in the +// string is interpreted as an index in an array of nodes. Stop once the tree +// does not have anymore nodes or once we found the right unichar_repr. +bool UNICHARMAP::contains(const char* const unichar_repr) const { + if (unichar_repr == NULL || *unichar_repr == '\0') return false; + + const char* current_char = unichar_repr; + UNICHARMAP_NODE* current_nodes = nodes; + + while (current_nodes != 0 && *(current_char + 1) != '\0') { + current_nodes = + current_nodes[static_cast(*current_char)].children; + ++current_char; + } + return current_nodes != 0 && *(current_char + 1) == '\0' && + current_nodes[static_cast(*current_char)].id >= 0; +} + +// Search the given unichar representation in the tree, using length characters +// from it maximum. Each character in the string is interpreted as an index in +// an array of nodes. Stop once the tree does not have anymore nodes or once we +// found the right unichar_repr. +bool UNICHARMAP::contains(const char* const unichar_repr, + int length) const { + if (unichar_repr == NULL || *unichar_repr == '\0') return false; + if (length <= 0 || length > UNICHAR_LEN) return false; + + const char* current_char = unichar_repr; + UNICHARMAP_NODE* current_nodes = nodes; + + while (current_nodes != 0 && (length > 1 && *(current_char + 1) != '\0')) { + current_nodes = + current_nodes[static_cast(*current_char)].children; + --length; + ++current_char; + } + return current_nodes != 0 && (length == 1 || *(current_char + 1) == '\0') && + current_nodes[static_cast(*current_char)].id >= 0; +} + +// Return the minimum number of characters that must be used from this string +// to obtain a match in the UNICHARMAP. +int UNICHARMAP::minmatch(const char* const unichar_repr) const { + const char* current_char = unichar_repr; + UNICHARMAP_NODE* current_nodes = nodes; + + while (current_nodes != NULL && *current_char != '\0') { + if (current_nodes[static_cast(*current_char)].id >= 0) + return current_char + 1 - unichar_repr; + current_nodes = + current_nodes[static_cast(*current_char)].children; + ++current_char; + } + return 0; +} + +void UNICHARMAP::clear() { + if (nodes != 0) + { + delete[] nodes; + nodes = 0; + } +} + +UNICHARMAP::UNICHARMAP_NODE::UNICHARMAP_NODE() : +children(0), +id(-1) { +} + +// Recursively delete the children +UNICHARMAP::UNICHARMAP_NODE::~UNICHARMAP_NODE() { + if (children != 0) { + delete[] children; + } +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.h b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.h new file mode 100644 index 0000000..ad90158 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharmap.h @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////// +// File: unicharmap.h +// Description: Unicode character/ligature to integer id class. +// Author: Thomas Kielbus +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_UNICHARMAP_H__ +#define TESSERACT_CCUTIL_UNICHARMAP_H__ + +#include "unichar.h" + +// A UNICHARMAP stores unique unichars. Each of them is associated with one +// UNICHAR_ID. +class UNICHARMAP { + public: + + // Create an empty UNICHARMAP + UNICHARMAP(); + + ~UNICHARMAP(); + + // Insert the given unichar represention in the UNICHARMAP and associate it + // with the given id. The length of the representation MUST be non-zero. + void insert(const char* const unichar_repr, UNICHAR_ID id); + + // Return the id associated with the given unichar representation, + // this representation MUST exist within the UNICHARMAP. + // The length of the representation MUST be non-zero. + UNICHAR_ID unichar_to_id(const char* const unichar_repr) const; + + // Return the id associated with the given unichar representation, + // this representation MUST exist within the UNICHARMAP. The first + // length characters (maximum) from unichar_repr are used. The length + // MUST be non-zero. + UNICHAR_ID unichar_to_id(const char* const unichar_repr, int length) const; + + // Return true if the given unichar representation is already present in the + // UNICHARMAP. The length of the representation MUST be non-zero. + bool contains(const char* const unichar_repr) const; + + // Return true if the given unichar representation is already present in the + // UNICHARMAP. The first length characters (maximum) from unichar_repr are + // used. The length MUST be non-zero. + bool contains(const char* const unichar_repr, int length) const; + + // Return the minimum number of characters that must be used from this string + // to obtain a match in the UNICHARMAP. + int minmatch(const char* const unichar_repr) const; + + // Clear the UNICHARMAP. All previous data is lost. + void clear(); + + private: + + // The UNICHARMAP is represented as a tree whose nodes are of type + // UNICHARMAP_NODE. + struct UNICHARMAP_NODE { + + UNICHARMAP_NODE(); + ~UNICHARMAP_NODE(); + + UNICHARMAP_NODE* children; + UNICHAR_ID id; + }; + + UNICHARMAP_NODE* nodes; +}; + +#endif // TESSERACT_CCUTIL_UNICHARMAP_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.cpp new file mode 100644 index 0000000..db3e584 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.cpp @@ -0,0 +1,1086 @@ +/////////////////////////////////////////////////////////////////////// +// File: unicharset.cpp +// Description: Unicode character/ligature set class. +// Author: Thomas Kielbus +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "unicharset.h" + +#include +#include +#include + +#include "params.h" +#include "serialis.h" +#include "tesscallback.h" +#include "tprintf.h" +#include "unichar.h" + +// Special character used in representing character fragments. +static const char kSeparator = '|'; +// Special character used in representing 'natural' character fragments. +static const char kNaturalFlag = 'n'; + +static const int ISALPHA_MASK = 0x1; +static const int ISLOWER_MASK = 0x2; +static const int ISUPPER_MASK = 0x4; +static const int ISDIGIT_MASK = 0x8; +static const int ISPUNCTUATION_MASK = 0x10; + +// Y coordinate threshold for determining cap-height vs x-height. +// TODO(rays) Bring the global definition down to the ccutil library level, +// so this constant is relative to some other constants. +static const int kMeanlineThreshold = 220; +// Let C be the number of alpha chars for which all tops exceed +// kMeanlineThreshold, and X the number of alpha chars for which all +// tops are below kMeanlineThreshold, then if X > C * +// kMinXHeightFraction and C > X * kMinCapHeightFraction or more than +// half the alpha characters have upper or lower case, then the +// unicharset "has x-height". +const double kMinXHeightFraction = 0.25; +const double kMinCapHeightFraction = 0.05; + +/*static */ +const char* UNICHARSET::kCustomLigatures[][2] = { + {"ct", "\uE003"}, // c + t -> U+E003 + {"趴h", "\uE006"}, // long-s + h -> U+E006 + {"趴i", "\uE007"}, // long-s + i -> U+E007 + {"趴l", "\uE008"}, // long-s + l -> U+E008 + {"趴趴", "\uE009"}, // long-s + long-s -> U+E009 + {NULL, NULL} +}; + +// List of strings for the SpecialUnicharCodes. Keep in sync with the enum. +const char* UNICHARSET::kSpecialUnicharCodes[SPECIAL_UNICHAR_CODES_COUNT] = { + " ", + "Joined", + "|Broken|0|1" +}; + +UNICHARSET::UNICHAR_PROPERTIES::UNICHAR_PROPERTIES() { + Init(); +} + +// Initialize all properties to sensible default values. +void UNICHARSET::UNICHAR_PROPERTIES::Init() { + isalpha = false; + islower = false; + isupper = false; + isdigit = false; + ispunctuation = false; + isngram = false; + enabled = false; + SetRangesOpen(); + script_id = 0; + other_case = 0; + mirror = 0; + normed = ""; + direction = UNICHARSET::U_LEFT_TO_RIGHT; + fragment = NULL; +} + +// Sets all ranges wide open. Initialization default in case there are +// no useful values available. +void UNICHARSET::UNICHAR_PROPERTIES::SetRangesOpen() { + min_bottom = 0; + max_bottom = MAX_UINT8; + min_top = 0; + max_top = MAX_UINT8; + width = 0.0f; + width_sd = 0.0f; + bearing = 0.0f; + bearing_sd = 0.0f; + advance = 0.0f; + advance_sd = 0.0f; +} + +// Sets all ranges to empty. Used before expanding with font-based data. +void UNICHARSET::UNICHAR_PROPERTIES::SetRangesEmpty() { + min_bottom = MAX_UINT8; + max_bottom = 0; + min_top = MAX_UINT8; + max_top = 0; + width = 0.0f; + width_sd = 0.0f; + bearing = 0.0f; + bearing_sd = 0.0f; + advance = 0.0f; + advance_sd = 0.0f; +} + +// Returns true if any of the top/bottom/width/bearing/advance ranges/stats +// is emtpy. +bool UNICHARSET::UNICHAR_PROPERTIES::AnyRangeEmpty() const { + return width == 0.0f || advance == 0.0f; +} + +// Expands the ranges with the ranges from the src properties. +void UNICHARSET::UNICHAR_PROPERTIES::ExpandRangesFrom( + const UNICHAR_PROPERTIES& src) { + UpdateRange(src.min_bottom, &min_bottom, &max_bottom); + UpdateRange(src.max_bottom, &min_bottom, &max_bottom); + UpdateRange(src.min_top, &min_top, &max_top); + UpdateRange(src.max_top, &min_top, &max_top); + if (src.width_sd > width_sd) { + width = src.width; + width_sd = src.width_sd; + } + if (src.bearing_sd > bearing_sd) { + bearing = src.bearing; + bearing_sd = src.bearing_sd; + } + if (src.advance_sd > advance_sd) { + advance = src.advance; + advance_sd = src.advance_sd; + } +} + +// Copies the properties from src into this. +void UNICHARSET::UNICHAR_PROPERTIES::CopyFrom(const UNICHAR_PROPERTIES& src) { + // Apart from the fragment, everything else can be done with a default copy. + CHAR_FRAGMENT* saved_fragment = fragment; + *this = src; // Bitwise copy. + fragment = saved_fragment; +} + +UNICHARSET::UNICHARSET() : + unichars(NULL), + ids(), + size_used(0), + size_reserved(0), + script_table(NULL), + script_table_size_used(0), + null_script("NULL") { + clear(); + for (int i = 0; i < SPECIAL_UNICHAR_CODES_COUNT; ++i) { + unichar_insert(kSpecialUnicharCodes[i]); + if (i == UNICHAR_JOINED) + set_isngram(i, true); + } +} + +UNICHARSET::~UNICHARSET() { + clear(); +} + +void UNICHARSET::reserve(int unichars_number) { + if (unichars_number > size_reserved) { + UNICHAR_SLOT* unichars_new = new UNICHAR_SLOT[unichars_number]; + for (int i = 0; i < size_used; ++i) + unichars_new[i] = unichars[i]; + for (int j = size_used; j < unichars_number; ++j) { + unichars_new[j].properties.script_id = add_script(null_script); + } + delete[] unichars; + unichars = unichars_new; + size_reserved = unichars_number; + } +} + +UNICHAR_ID +UNICHARSET::unichar_to_id(const char* const unichar_repr) const { + return ids.contains(unichar_repr) ? + ids.unichar_to_id(unichar_repr) : INVALID_UNICHAR_ID; +} + +UNICHAR_ID UNICHARSET::unichar_to_id(const char* const unichar_repr, + int length) const { + assert(length > 0 && length <= UNICHAR_LEN); + return ids.contains(unichar_repr, length) ? + ids.unichar_to_id(unichar_repr, length) : INVALID_UNICHAR_ID; +} + +// Return the minimum number of bytes that matches a legal UNICHAR_ID, +// while leaving the rest of the string encodable. Returns 0 if the +// beginning of the string is not encodable. +// WARNING: this function now encodes the whole string for precision. +// Use encode_string in preference to repeatedly calling step. +int UNICHARSET::step(const char* str) const { + GenericVector encoding; + GenericVector lengths; + encode_string(str, true, &encoding, &lengths, NULL); + if (encoding.empty() || encoding[0] == INVALID_UNICHAR_ID) return 0; + return lengths[0]; +} + +// Return whether the given UTF-8 string is encodable with this UNICHARSET. +// If not encodable, write the first byte offset which cannot be converted +// into the second (return) argument. +bool UNICHARSET::encodable_string(const char *str, + int *first_bad_position) const { + GenericVector encoding; + return encode_string(str, true, &encoding, NULL, first_bad_position); +} + +// Encodes the given UTF-8 string with this UNICHARSET. +// Returns true if the encoding succeeds completely, false if there is at +// least one INVALID_UNICHAR_ID in the returned encoding, but in this case +// the rest of the string is still encoded. +// If lengths is not NULL, then it is filled with the corresponding +// byte length of each encoded UNICHAR_ID. +bool UNICHARSET::encode_string(const char* str, bool give_up_on_failure, + GenericVector* encoding, + GenericVector* lengths, + int* encoded_length) const { + GenericVector working_encoding; + GenericVector working_lengths; + GenericVector best_lengths; + encoding->truncate(0); // Just in case str is empty. + int str_length = strlen(str); + int str_pos = 0; + bool perfect = true; + while (str_pos < str_length) { + encode_string(str, str_pos, str_length, &working_encoding, &working_lengths, + &str_pos, encoding, &best_lengths); + if (str_pos < str_length) { + // This is a non-match. Skip one utf-8 character. + perfect = false; + if (give_up_on_failure) break; + int step = UNICHAR::utf8_step(str + str_pos); + if (step == 0) step = 1; + encoding->push_back(INVALID_UNICHAR_ID); + best_lengths.push_back(step); + str_pos += step; + working_encoding = *encoding; + working_lengths = best_lengths; + } + } + if (lengths != NULL) *lengths = best_lengths; + if (encoded_length != NULL) *encoded_length = str_pos; + return perfect; +} + +const char* UNICHARSET::id_to_unichar(UNICHAR_ID id) const { + if (id == INVALID_UNICHAR_ID) { + return INVALID_UNICHAR; + } + ASSERT_HOST(id < this->size()); + return unichars[id].representation; +} + +const char* UNICHARSET::id_to_unichar_ext(UNICHAR_ID id) const { + if (id == INVALID_UNICHAR_ID) { + return INVALID_UNICHAR; + } + ASSERT_HOST(id < this->size()); + // Resolve from the kCustomLigatures table if this is a private encoding. + if (get_isprivate(id)) { + const char* ch = id_to_unichar(id); + for (int i = 0; kCustomLigatures[i][0] != NULL; ++i) { + if (!strcmp(ch, kCustomLigatures[i][1])) { + return kCustomLigatures[i][0]; + } + } + } + // Otherwise return the stored representation. + return unichars[id].representation; +} + +// Return a STRING that reformats the utf8 str into the str followed +// by its hex unicodes. +STRING UNICHARSET::debug_utf8_str(const char* str) { + STRING result = str; + result += " ["; + int step = 1; + // Chop into unicodes and code each as hex. + for (int i = 0; str[i] != '\0'; i += step) { + char hex[sizeof(int) * 2 + 1]; + step = UNICHAR::utf8_step(str + i); + if (step == 0) { + step = 1; + sprintf(hex, "%x", str[i]); + } else { + UNICHAR ch(str + i, step); + sprintf(hex, "%x", ch.first_uni()); + } + result += hex; + result += " "; + } + result += "]"; + return result; +} + +// Return a STRING containing debug information on the unichar, including +// the id_to_unichar, its hex unicodes and the properties. +STRING UNICHARSET::debug_str(UNICHAR_ID id) const { + if (id == INVALID_UNICHAR_ID) return STRING(id_to_unichar(id)); + const CHAR_FRAGMENT *fragment = this->get_fragment(id); + if (fragment) { + return fragment->to_string(); + } + const char* str = id_to_unichar(id); + STRING result = debug_utf8_str(str); + // Append a for lower alpha, A for upper alpha, and x if alpha but neither. + if (get_isalpha(id)) { + if (get_islower(id)) + result += "a"; + else if (get_isupper(id)) + result += "A"; + else + result += "x"; + } + // Append 0 if a digit. + if (get_isdigit(id)) { + result += "0"; + } + // Append p is a punctuation symbol. + if (get_ispunctuation(id)) { + result += "p"; + } + return result; +} + +// Sets the normed_ids vector from the normed string. normed_ids is not +// stored in the file, and needs to be set when the UNICHARSET is loaded. +void UNICHARSET::set_normed_ids(UNICHAR_ID unichar_id) { + unichars[unichar_id].properties.normed_ids.truncate(0); + if (unichar_id == UNICHAR_SPACE && id_to_unichar(unichar_id)[0] == ' ') { + unichars[unichar_id].properties.normed_ids.push_back(UNICHAR_SPACE); + } else if (!encode_string(unichars[unichar_id].properties.normed.string(), + true, &unichars[unichar_id].properties.normed_ids, + NULL, NULL)) { + unichars[unichar_id].properties.normed_ids.truncate(0); + unichars[unichar_id].properties.normed_ids.push_back(unichar_id); + } +} + +// Returns whether the unichar id represents a unicode value in the private use +// area. We use this range only internally to represent uncommon ligatures +// (eg. 'ct') that do not have regular unicode values. +bool UNICHARSET::get_isprivate(UNICHAR_ID unichar_id) const { + UNICHAR uc(id_to_unichar(unichar_id), -1); + int uni = uc.first_uni(); + return (uni >= 0xE000 && uni <= 0xF8FF); +} + + +// Sets all ranges to empty, so they can be expanded to set the values. +void UNICHARSET::set_ranges_empty() { + for (int id = 0; id < size_used; ++id) { + unichars[id].properties.SetRangesEmpty(); + } +} + +// Sets all the properties for this unicharset given a src unicharset with +// everything set. The unicharsets don't have to be the same, and graphemes +// are correctly accounted for. +void UNICHARSET::PartialSetPropertiesFromOther(int start_index, + const UNICHARSET& src) { + for (int ch = start_index; ch < size_used; ++ch) { + const char* utf8 = id_to_unichar(ch); + UNICHAR_PROPERTIES properties; + if (src.GetStrProperties(utf8, &properties)) { + // Setup the script_id, other_case, and mirror properly. + const char* script = src.get_script_from_script_id(properties.script_id); + properties.script_id = add_script(script); + const char* other_case = src.id_to_unichar(properties.other_case); + if (contains_unichar(other_case)) { + properties.other_case = unichar_to_id(other_case); + } else { + properties.other_case = ch; + } + const char* mirror_str = src.id_to_unichar(properties.mirror); + if (contains_unichar(mirror_str)) { + properties.mirror = unichar_to_id(mirror_str); + } else { + properties.mirror = ch; + } + unichars[ch].properties.CopyFrom(properties); + set_normed_ids(ch); + } + } +} + +// Expands the tops and bottoms and widths for this unicharset given a +// src unicharset with ranges in it. The unicharsets don't have to be the +// same, and graphemes are correctly accounted for. +void UNICHARSET::ExpandRangesFromOther(const UNICHARSET& src) { + for (int ch = 0; ch < size_used; ++ch) { + const char* utf8 = id_to_unichar(ch); + UNICHAR_PROPERTIES properties; + if (src.GetStrProperties(utf8, &properties)) { + // Expand just the ranges from properties. + unichars[ch].properties.ExpandRangesFrom(properties); + } + } +} + +// Makes this a copy of src. Clears this completely first, so the automatic +// ids will not be present in this if not in src. Does NOT reorder the set! +void UNICHARSET::CopyFrom(const UNICHARSET& src) { + clear(); + for (int ch = 0; ch < src.size_used; ++ch) { + const UNICHAR_PROPERTIES& src_props = src.unichars[ch].properties; + const char* utf8 = src.id_to_unichar(ch); + unichar_insert(utf8); + unichars[ch].properties.ExpandRangesFrom(src_props); + } + // Set properties, including mirror and other_case, WITHOUT reordering + // the unicharset. + PartialSetPropertiesFromOther(0, src); +} + +// For each id in src, if it does not occur in this, add it, as in +// SetPropertiesFromOther, otherwise expand the ranges, as in +// ExpandRangesFromOther. +void UNICHARSET::AppendOtherUnicharset(const UNICHARSET& src) { + int initial_used = size_used; + for (int ch = 0; ch < src.size_used; ++ch) { + const UNICHAR_PROPERTIES& src_props = src.unichars[ch].properties; + const char* utf8 = src.id_to_unichar(ch); + if (ch >= SPECIAL_UNICHAR_CODES_COUNT && src_props.AnyRangeEmpty()) { + // Only use fully valid entries. + tprintf("Bad properties for index %d, char %s: " + "%d,%d %d,%d %g,%g %g,%g %g,%g\n", + ch, utf8, src_props.min_bottom, src_props.max_bottom, + src_props.min_top, src_props.max_top, + src_props.width, src_props.width_sd, + src_props.bearing, src_props.bearing_sd, + src_props.advance, src_props.advance_sd); + continue; + } + int id = size_used; + if (contains_unichar(utf8)) { + id = unichar_to_id(utf8); + // Just expand current ranges. + unichars[id].properties.ExpandRangesFrom(src_props); + } else { + unichar_insert(utf8); + unichars[id].properties.SetRangesEmpty(); + } + } + // Set properties, including mirror and other_case, WITHOUT reordering + // the unicharset. + PartialSetPropertiesFromOther(initial_used, src); +} + +// Returns true if the acceptable ranges of the tops of the characters do +// not overlap, making their x-height calculations distinct. +bool UNICHARSET::SizesDistinct(UNICHAR_ID id1, UNICHAR_ID id2) const { + int overlap = MIN(unichars[id1].properties.max_top, + unichars[id2].properties.max_top) - + MAX(unichars[id1].properties.min_top, + unichars[id2].properties.min_top); + return overlap <= 0; +} + +// Internal recursive version of encode_string above. +// Seeks to encode the given string as a sequence of UNICHAR_IDs such that +// each UNICHAR_ID uses the least possible part of the utf8 str. +// It does this by depth-first tail recursion on increasing length matches +// to the UNICHARSET, saving the first encountered result that encodes the +// maximum total length of str. It stops on a failure to encode to make +// the overall process of encoding a partially failed string more efficient. +// See unicharset.h for definition of the args. +void UNICHARSET::encode_string(const char* str, int str_index, int str_length, + GenericVector* encoding, + GenericVector* lengths, + int* best_total_length, + GenericVector* best_encoding, + GenericVector* best_lengths) const { + if (str_index > *best_total_length) { + // This is the best result so far. + *best_total_length = str_index; + *best_encoding = *encoding; + if (best_lengths != NULL) + *best_lengths = *lengths; + } + if (str_index == str_length) return; + int encoding_index = encoding->size(); + // Find the length of the first matching unicharset member. + int length = ids.minmatch(str + str_index); + if (length == 0 || str_index + length > str_length) return; + do { + if (ids.contains(str + str_index, length)) { + // Successful encoding so far. + UNICHAR_ID id = ids.unichar_to_id(str + str_index, length); + encoding->push_back(id); + lengths->push_back(length); + encode_string(str, str_index + length, str_length, encoding, lengths, + best_total_length, best_encoding, best_lengths); + if (*best_total_length == str_length) + return; // Tail recursion success! + // Failed with that length, truncate back and try again. + encoding->truncate(encoding_index); + lengths->truncate(encoding_index); + } + int step = UNICHAR::utf8_step(str + str_index + length); + if (step == 0) step = 1; + length += step; + } while (length <= UNICHAR_LEN && str_index + length <= str_length); +} + +// Gets the properties for a grapheme string, combining properties for +// multiple characters in a meaningful way where possible. +// Returns false if no valid match was found in the unicharset. +// NOTE that script_id, mirror, and other_case refer to this unicharset on +// return and will need translation if the target unicharset is different. +bool UNICHARSET::GetStrProperties(const char* utf8_str, + UNICHAR_PROPERTIES* props) const { + props->Init(); + props->SetRangesEmpty(); + int total_unicodes = 0; + GenericVector encoding; + if (!encode_string(utf8_str, true, &encoding, NULL, NULL)) + return false; // Some part was invalid. + for (int i = 0; i < encoding.size(); ++i) { + int id = encoding[i]; + const UNICHAR_PROPERTIES& src_props = unichars[id].properties; + // Logical OR all the bools. + if (src_props.isalpha) props->isalpha = true; + if (src_props.islower) props->islower = true; + if (src_props.isupper) props->isupper = true; + if (src_props.isdigit) props->isdigit = true; + if (src_props.ispunctuation) props->ispunctuation = true; + if (src_props.isngram) props->isngram = true; + if (src_props.enabled) props->enabled = true; + // Min/max the tops/bottoms. + UpdateRange(src_props.min_bottom, &props->min_bottom, &props->max_bottom); + UpdateRange(src_props.max_bottom, &props->min_bottom, &props->max_bottom); + UpdateRange(src_props.min_top, &props->min_top, &props->max_top); + UpdateRange(src_props.max_top, &props->min_top, &props->max_top); + float bearing = props->advance + src_props.bearing; + if (total_unicodes == 0 || bearing < props->bearing) { + props->bearing = bearing; + props->bearing_sd = props->advance_sd + src_props.bearing_sd; + } + props->advance += src_props.advance; + props->advance_sd += src_props.advance_sd; + // With a single width, just use the widths stored in the unicharset. + props->width = src_props.width; + props->width_sd = src_props.width_sd; + // Use the first script id, other_case, mirror, direction. + // Note that these will need translation, except direction. + if (total_unicodes == 0) { + props->script_id = src_props.script_id; + props->other_case = src_props.other_case; + props->mirror = src_props.mirror; + props->direction = src_props.direction; + } + // The normed string for the compound character is the concatenation of + // the normed versions of the individual characters. + props->normed += src_props.normed; + ++total_unicodes; + } + if (total_unicodes > 1) { + // Estimate the total widths from the advance - bearing. + props->width = props->advance - props->bearing; + props->width_sd = props->advance_sd + props->bearing_sd; + } + return total_unicodes > 0; +} + +// TODO(rays) clean-up the order of functions to match unicharset.h. + +unsigned int UNICHARSET::get_properties(UNICHAR_ID id) const { + unsigned int properties = 0; + if (this->get_isalpha(id)) + properties |= ISALPHA_MASK; + if (this->get_islower(id)) + properties |= ISLOWER_MASK; + if (this->get_isupper(id)) + properties |= ISUPPER_MASK; + if (this->get_isdigit(id)) + properties |= ISDIGIT_MASK; + if (this->get_ispunctuation(id)) + properties |= ISPUNCTUATION_MASK; + return properties; +} + +char UNICHARSET::get_chartype(UNICHAR_ID id) const { + if (this->get_isupper(id)) return 'A'; + if (this->get_islower(id)) return 'a'; + if (this->get_isalpha(id)) return 'x'; + if (this->get_isdigit(id)) return '0'; + if (this->get_ispunctuation(id)) return 'p'; + return 0; +} + +void UNICHARSET::unichar_insert(const char* const unichar_repr) { + if (!ids.contains(unichar_repr)) { + if (strlen(unichar_repr) > UNICHAR_LEN) { + fprintf(stderr, "Utf8 buffer too big, size=%d for %s\n", + int(strlen(unichar_repr)), unichar_repr); + return; + } + if (size_used == size_reserved) { + if (size_used == 0) + reserve(8); + else + reserve(2 * size_used); + } + + strcpy(unichars[size_used].representation, unichar_repr); + this->set_script(size_used, null_script); + // If the given unichar_repr represents a fragmented character, set + // fragment property to a pointer to CHAR_FRAGMENT class instance with + // information parsed from the unichar representation. Use the script + // of the base unichar for the fragmented character if possible. + CHAR_FRAGMENT *frag = CHAR_FRAGMENT::parse_from_string(unichar_repr); + this->unichars[size_used].properties.fragment = frag; + if (frag != NULL && this->contains_unichar(frag->get_unichar())) { + this->unichars[size_used].properties.script_id = + this->get_script(frag->get_unichar()); + } + this->unichars[size_used].properties.enabled = true; + ids.insert(unichar_repr, size_used); + ++size_used; + } +} + +bool UNICHARSET::contains_unichar(const char* const unichar_repr) const { + return ids.contains(unichar_repr); +} + +bool UNICHARSET::contains_unichar(const char* const unichar_repr, + int length) const { + if (length == 0) { + return false; + } + return ids.contains(unichar_repr, length); +} + +bool UNICHARSET::eq(UNICHAR_ID unichar_id, + const char* const unichar_repr) const { + return strcmp(this->id_to_unichar(unichar_id), unichar_repr) == 0; +} + +bool UNICHARSET::save_to_string(STRING *str) const { + const int kFileBufSize = 1024; + char buffer[kFileBufSize + 1]; + snprintf(buffer, kFileBufSize, "%d\n", this->size()); + *str = buffer; + for (UNICHAR_ID id = 0; id < this->size(); ++id) { + int min_bottom, max_bottom, min_top, max_top; + get_top_bottom(id, &min_bottom, &max_bottom, &min_top, &max_top); + float width, width_sd; + get_width_stats(id, &width, &width_sd); + float bearing, bearing_sd; + get_bearing_stats(id, &bearing, &bearing_sd); + float advance, advance_sd; + get_advance_stats(id, &advance, &advance_sd); + unsigned int properties = this->get_properties(id); + if (strcmp(this->id_to_unichar(id), " ") == 0) { + snprintf(buffer, kFileBufSize, "%s %x %s %d\n", "NULL", properties, + this->get_script_from_script_id(this->get_script(id)), + this->get_other_case(id)); + } else { + snprintf(buffer, kFileBufSize, + "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %s %d %d %d %s\t# %s\n", + this->id_to_unichar(id), properties, + min_bottom, max_bottom, min_top, max_top, width, width_sd, + bearing, bearing_sd, advance, advance_sd, + this->get_script_from_script_id(this->get_script(id)), + this->get_other_case(id), this->get_direction(id), + this->get_mirror(id), this->get_normed_unichar(id), + this->debug_str(id).string()); + } + *str += buffer; + } + return true; +} + +// TODO(rays) Replace with TFile everywhere. +class InMemoryFilePointer { + public: + InMemoryFilePointer(const char *memory, int mem_size) + : memory_(memory), fgets_ptr_(memory), mem_size_(mem_size) { } + + char *fgets(char *orig_dst, int size) { + const char *src_end = memory_ + mem_size_; + char *dst_end = orig_dst + size - 1; + if (size < 1) { + return fgets_ptr_ < src_end ? orig_dst : NULL; + } + + char *dst = orig_dst; + char ch = '^'; + while (fgets_ptr_ < src_end && dst < dst_end && ch != '\n') { + ch = *dst++ = *fgets_ptr_++; + } + *dst = 0; + return (dst == orig_dst) ? NULL : orig_dst; + } + + private: + const char *memory_; + const char *fgets_ptr_; + const int mem_size_; +}; + +bool UNICHARSET::load_from_inmemory_file( + const char *memory, int mem_size, bool skip_fragments) { + InMemoryFilePointer mem_fp(memory, mem_size); + TessResultCallback2 *fgets_cb = + NewPermanentTessCallback(&mem_fp, &InMemoryFilePointer::fgets); + bool success = load_via_fgets(fgets_cb, skip_fragments); + delete fgets_cb; + return success; +} + +class LocalFilePointer { + public: + LocalFilePointer(FILE *stream) : fp_(stream) {} + char *fgets(char *dst, int size) { + return ::fgets(dst, size, fp_); + } + private: + FILE *fp_; +}; + +bool UNICHARSET::load_from_file(FILE *file, bool skip_fragments) { + LocalFilePointer lfp(file); + TessResultCallback2 *fgets_cb = + NewPermanentTessCallback(&lfp, &LocalFilePointer::fgets); + bool success = load_via_fgets(fgets_cb, skip_fragments); + delete fgets_cb; + return success; +} + +bool UNICHARSET::load_from_file(tesseract::TFile *file, bool skip_fragments) { + TessResultCallback2 *fgets_cb = + NewPermanentTessCallback(file, &tesseract::TFile::FGets); + bool success = load_via_fgets(fgets_cb, skip_fragments); + delete fgets_cb; + return success; +} + +bool UNICHARSET::load_via_fgets( + TessResultCallback2 *fgets_cb, + bool skip_fragments) { + int unicharset_size; + char buffer[256]; + + this->clear(); + if (fgets_cb->Run(buffer, sizeof(buffer)) == NULL || + sscanf(buffer, "%d", &unicharset_size) != 1) { + return false; + } + this->reserve(unicharset_size); + for (UNICHAR_ID id = 0; id < unicharset_size; ++id) { + char unichar[256]; + unsigned int properties; + char script[64]; + + strcpy(script, null_script); + int min_bottom = 0; + int max_bottom = MAX_UINT8; + int min_top = 0; + int max_top = MAX_UINT8; + float width = 0.0f; + float width_sd = 0.0f; + float bearing = 0.0f; + float bearing_sd = 0.0f; + float advance = 0.0f; + float advance_sd = 0.0f; + // TODO(eger): check that this default it ok + // after enabling BiDi iterator for Arabic+Cube. + int direction = UNICHARSET::U_LEFT_TO_RIGHT; + UNICHAR_ID other_case = id; + UNICHAR_ID mirror = id; + char normed[64]; + int v = -1; + if (fgets_cb->Run(buffer, sizeof (buffer)) == NULL || + ((v = sscanf(buffer, + "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %63s %d %d %d %63s", + unichar, &properties, + &min_bottom, &max_bottom, &min_top, &max_top, + &width, &width_sd, &bearing, &bearing_sd, + &advance, &advance_sd, script, &other_case, + &direction, &mirror, normed)) != 17 && + (v = sscanf(buffer, + "%s %x %d,%d,%d,%d,%g,%g,%g,%g,%g,%g %63s %d %d %d", + unichar, &properties, + &min_bottom, &max_bottom, &min_top, &max_top, + &width, &width_sd, &bearing, &bearing_sd, + &advance, &advance_sd, script, &other_case, + &direction, &mirror)) != 16 && + (v = sscanf(buffer, "%s %x %d,%d,%d,%d %63s %d %d %d", + unichar, &properties, + &min_bottom, &max_bottom, &min_top, &max_top, + script, &other_case, &direction, &mirror)) != 10 && + (v = sscanf(buffer, "%s %x %d,%d,%d,%d %63s %d", unichar, &properties, + &min_bottom, &max_bottom, &min_top, &max_top, + script, &other_case)) != 8 && + (v = sscanf(buffer, "%s %x %63s %d", unichar, &properties, + script, &other_case)) != 4 && + (v = sscanf(buffer, "%s %x %63s", + unichar, &properties, script)) != 3 && + (v = sscanf(buffer, "%s %x", unichar, &properties)) != 2)) { + return false; + } + + // Skip fragments if needed. + CHAR_FRAGMENT *frag = NULL; + if (skip_fragments && (frag = CHAR_FRAGMENT::parse_from_string(unichar))) { + int num_pieces = frag->get_total(); + delete frag; + // Skip multi-element fragments, but keep singles like UNICHAR_BROKEN in. + if (num_pieces > 1) + continue; + } + // Insert unichar into unicharset and set its properties. + if (strcmp(unichar, "NULL") == 0) + this->unichar_insert(" "); + else + this->unichar_insert(unichar); + + this->set_isalpha(id, properties & ISALPHA_MASK); + this->set_islower(id, properties & ISLOWER_MASK); + this->set_isupper(id, properties & ISUPPER_MASK); + this->set_isdigit(id, properties & ISDIGIT_MASK); + this->set_ispunctuation(id, properties & ISPUNCTUATION_MASK); + this->set_isngram(id, false); + this->set_script(id, script); + this->unichars[id].properties.enabled = true; + this->set_top_bottom(id, min_bottom, max_bottom, min_top, max_top); + this->set_width_stats(id, width, width_sd); + this->set_bearing_stats(id, bearing, bearing_sd); + this->set_advance_stats(id, advance, advance_sd); + this->set_direction(id, static_cast(direction)); + ASSERT_HOST(other_case < unicharset_size); + this->set_other_case(id, (v>3) ? other_case : id); + ASSERT_HOST(mirror < unicharset_size); + this->set_mirror(id, (v>8) ? mirror : id); + this->set_normed(id, (v>16) ? normed : unichar); + } + post_load_setup(); + return true; +} + +// Sets up internal data after loading the file, based on the char +// properties. Called from load_from_file, but also needs to be run +// during set_unicharset_properties. +void UNICHARSET::post_load_setup() { + // Number of alpha chars with the case property minus those without, + // in order to determine that half the alpha chars have case. + int net_case_alphas = 0; + int x_height_alphas = 0; + int cap_height_alphas = 0; + top_bottom_set_ = false; + for (UNICHAR_ID id = 0; id < size_used; ++id) { + int min_bottom = 0; + int max_bottom = MAX_UINT8; + int min_top = 0; + int max_top = MAX_UINT8; + get_top_bottom(id, &min_bottom, &max_bottom, &min_top, &max_top); + if (min_top > 0) + top_bottom_set_ = true; + if (get_isalpha(id)) { + if (get_islower(id) || get_isupper(id)) + ++net_case_alphas; + else + --net_case_alphas; + if (min_top < kMeanlineThreshold && max_top < kMeanlineThreshold) + ++x_height_alphas; + else if (min_top > kMeanlineThreshold && max_top > kMeanlineThreshold) + ++cap_height_alphas; + } + set_normed_ids(id); + } + + script_has_upper_lower_ = net_case_alphas > 0; + script_has_xheight_ = script_has_upper_lower_ || + (x_height_alphas > cap_height_alphas * kMinXHeightFraction && + cap_height_alphas > x_height_alphas * kMinCapHeightFraction); + + null_sid_ = get_script_id_from_name(null_script); + ASSERT_HOST(null_sid_ == 0); + common_sid_ = get_script_id_from_name("Common"); + latin_sid_ = get_script_id_from_name("Latin"); + cyrillic_sid_ = get_script_id_from_name("Cyrillic"); + greek_sid_ = get_script_id_from_name("Greek"); + han_sid_ = get_script_id_from_name("Han"); + hiragana_sid_ = get_script_id_from_name("Hiragana"); + katakana_sid_ = get_script_id_from_name("Katakana"); + + // Compute default script. Use the highest-counting alpha script, that is + // not the common script, as that still contains some "alphas". + int* script_counts = new int[script_table_size_used]; + memset(script_counts, 0, sizeof(*script_counts) * script_table_size_used); + for (int id = 0; id < size_used; ++id) { + if (get_isalpha(id)) { + ++script_counts[get_script(id)]; + } + } + default_sid_ = 0; + for (int s = 1; s < script_table_size_used; ++s) { + if (script_counts[s] > script_counts[default_sid_] && s != common_sid_) + default_sid_ = s; + } + delete [] script_counts; +} + +// Returns true if right_to_left scripts are significant in the unicharset, +// but without being so sensitive that "universal" unicharsets containing +// characters from many scripts, like orientation and script detection, +// look like they are right_to_left. +bool UNICHARSET::major_right_to_left() const { + int ltr_count = 0; + int rtl_count = 0; + for (int id = 0; id < size_used; ++id) { + int dir = get_direction(id); + if (dir == UNICHARSET::U_LEFT_TO_RIGHT) ltr_count++; + if (dir == UNICHARSET::U_RIGHT_TO_LEFT || + dir == UNICHARSET::U_RIGHT_TO_LEFT_ARABIC || + dir == UNICHARSET::U_ARABIC_NUMBER) rtl_count++; + } + return rtl_count > ltr_count; +} + +// Set a whitelist and/or blacklist of characters to recognize. +// An empty or NULL whitelist enables everything (minus any blacklist). +// An empty or NULL blacklist disables nothing. +// An empty or NULL blacklist has no effect. +void UNICHARSET::set_black_and_whitelist(const char* blacklist, + const char* whitelist, + const char* unblacklist) { + bool def_enabled = whitelist == NULL || whitelist[0] == '\0'; + // Set everything to default + for (int ch = 0; ch < size_used; ++ch) + unichars[ch].properties.enabled = def_enabled; + if (!def_enabled) { + // Enable the whitelist. + GenericVector encoding; + encode_string(whitelist, false, &encoding, NULL, NULL); + for (int i = 0; i < encoding.size(); ++i) { + if (encoding[i] != INVALID_UNICHAR_ID) + unichars[encoding[i]].properties.enabled = true; + } + } + if (blacklist != NULL && blacklist[0] != '\0') { + // Disable the blacklist. + GenericVector encoding; + encode_string(blacklist, false, &encoding, NULL, NULL); + for (int i = 0; i < encoding.size(); ++i) { + if (encoding[i] != INVALID_UNICHAR_ID) + unichars[encoding[i]].properties.enabled = false; + } + } + if (unblacklist != NULL && unblacklist[0] != '\0') { + // Re-enable the unblacklist. + GenericVector encoding; + encode_string(unblacklist, false, &encoding, NULL, NULL); + for (int i = 0; i < encoding.size(); ++i) { + if (encoding[i] != INVALID_UNICHAR_ID) + unichars[encoding[i]].properties.enabled = true; + } + } +} + +// Returns true if there are any repeated unicodes in the normalized +// text of any unichar-id in the unicharset. +bool UNICHARSET::AnyRepeatedUnicodes() const { + int start_id = 0; + if (has_special_codes()) start_id = SPECIAL_UNICHAR_CODES_COUNT; + for (int id = start_id; id < size_used; ++id) { + // Convert to unicodes. + GenericVector unicodes; + if (UNICHAR::UTF8ToUnicode(get_normed_unichar(id), &unicodes) && + unicodes.size() > 1) { + for (int u = 1; u < unicodes.size(); ++u) { + if (unicodes[u - 1] == unicodes[u]) return true; + } + } + } + return false; +} + +int UNICHARSET::add_script(const char* script) { + for (int i = 0; i < script_table_size_used; ++i) { + if (strcmp(script, script_table[i]) == 0) + return i; + } + if (script_table_size_reserved == 0) { + script_table_size_reserved = 8; + script_table = new char*[script_table_size_reserved]; + } else if (script_table_size_used >= script_table_size_reserved) { + assert(script_table_size_used == script_table_size_reserved); + script_table_size_reserved += script_table_size_reserved; + char** new_script_table = new char*[script_table_size_reserved]; + memcpy(new_script_table, script_table, script_table_size_used * sizeof(char*)); + delete[] script_table; + script_table = new_script_table; + } + script_table[script_table_size_used] = new char[strlen(script) + 1]; + strcpy(script_table[script_table_size_used], script); + return script_table_size_used++; +} + +// Returns the string that represents a fragment +// with the given unichar, pos and total. +STRING CHAR_FRAGMENT::to_string(const char *unichar, int pos, int total, + bool natural) { + if (total == 1) return STRING(unichar); + STRING result = ""; + result += kSeparator; + result += unichar; + char buffer[kMaxLen]; + snprintf(buffer, kMaxLen, "%c%d%c%d", kSeparator, pos, + natural ? kNaturalFlag : kSeparator, total); + result += buffer; + return result; +} + +CHAR_FRAGMENT *CHAR_FRAGMENT::parse_from_string(const char *string) { + const char *ptr = string; + int len = strlen(string); + if (len < kMinLen || *ptr != kSeparator) { + return NULL; // this string can not represent a fragment + } + ptr++; // move to the next character + int step = 0; + while ((ptr + step) < (string + len) && *(ptr + step) != kSeparator) { + step += UNICHAR::utf8_step(ptr + step); + } + if (step == 0 || step > UNICHAR_LEN) { + return NULL; // no character for unichar or the character is too long + } + char unichar[UNICHAR_LEN + 1]; + strncpy(unichar, ptr, step); + unichar[step] = '\0'; // null terminate unichar + ptr += step; // move to the next fragment separator + int pos = 0; + int total = 0; + bool natural = false; + char *end_ptr = NULL; + for (int i = 0; i < 2; i++) { + if (ptr > string + len || *ptr != kSeparator) { + if (i == 1 && *ptr == kNaturalFlag) + natural = true; + else + return NULL; // Failed to parse fragment representation. + } + ptr++; // move to the next character + i == 0 ? pos = static_cast(strtol(ptr, &end_ptr, 10)) + : total = static_cast(strtol(ptr, &end_ptr, 10)); + ptr = end_ptr; + } + if (ptr != string + len) { + return NULL; // malformed fragment representation + } + CHAR_FRAGMENT *fragment = new CHAR_FRAGMENT(); + fragment->set_all(unichar, pos, total, natural); + return fragment; +} + +int UNICHARSET::get_script_id_from_name(const char* script_name) const { + for (int i = 0; i < script_table_size_used; ++i) { + if (strcmp(script_name, script_table[i]) == 0) + return i; + } + return 0; // 0 is always the null_script +} diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.h b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.h new file mode 100644 index 0000000..ddb31be --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicharset.h @@ -0,0 +1,985 @@ +/////////////////////////////////////////////////////////////////////// +// File: unicharset.h +// Description: Unicode character/ligature set class. +// Author: Thomas Kielbus +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_UNICHARSET_H__ +#define TESSERACT_CCUTIL_UNICHARSET_H__ + +#include "errcode.h" +#include "genericvector.h" +#include "helpers.h" +#include "serialis.h" +#include "strngs.h" +#include "tesscallback.h" +#include "unichar.h" +#include "unicharmap.h" + +// Enum holding special values of unichar_id. Every unicharset has these. +// Warning! Keep in sync with kSpecialUnicharCodes. +enum SpecialUnicharCodes { + UNICHAR_SPACE, + UNICHAR_JOINED, + UNICHAR_BROKEN, + + SPECIAL_UNICHAR_CODES_COUNT +}; + +class CHAR_FRAGMENT { + public: + // Minimum number of characters used for fragment representation. + static const int kMinLen = 6; + // Maximum number of characters used for fragment representation. + static const int kMaxLen = 3 + UNICHAR_LEN + 2; + // Maximum number of fragments per character. + static const int kMaxChunks = 5; + + // Setters and Getters. + inline void set_all(const char *unichar, int pos, int total, bool natural) { + set_unichar(unichar); + set_pos(pos); + set_total(total); + set_natural(natural); + } + inline void set_unichar(const char *uch) { + strncpy(this->unichar, uch, UNICHAR_LEN); + this->unichar[UNICHAR_LEN] = '\0'; + } + inline void set_pos(int p) { this->pos = p; } + inline void set_total(int t) { this->total = t; } + inline const char* get_unichar() const { return this->unichar; } + inline int get_pos() const { return this->pos; } + inline int get_total() const { return this->total; } + + // Returns the string that represents a fragment + // with the given unichar, pos and total. + static STRING to_string(const char *unichar, int pos, int total, + bool natural); + // Returns the string that represents this fragment. + STRING to_string() const { + return to_string(unichar, pos, total, natural); + } + + // Checks whether a fragment has the same unichar, + // position and total as the given inputs. + inline bool equals(const char *other_unichar, + int other_pos, int other_total) const { + return (strcmp(this->unichar, other_unichar) == 0 && + this->pos == other_pos && this->total == other_total); + } + inline bool equals(const CHAR_FRAGMENT *other) const { + return this->equals(other->get_unichar(), + other->get_pos(), + other->get_total()); + } + + // Checks whether a given fragment is a continuation of this fragment. + // Assumes that the given fragment pointer is not NULL. + inline bool is_continuation_of(const CHAR_FRAGMENT *fragment) const { + return (strcmp(this->unichar, fragment->get_unichar()) == 0 && + this->total == fragment->get_total() && + this->pos == fragment->get_pos() + 1); + } + + // Returns true if this fragment is a beginning fragment. + inline bool is_beginning() const { return this->pos == 0; } + + // Returns true if this fragment is an ending fragment. + inline bool is_ending() const { return this->pos == this->total-1; } + + // Returns true if the fragment was a separate component to begin with, + // ie did not need chopping to be isolated, but may have been separated + // out from a multi-outline blob. + inline bool is_natural() const { return natural; } + void set_natural(bool value) { natural = value; } + + // Parses the string to see whether it represents a character fragment + // (rather than a regular character). If so, allocates memory for a new + // CHAR_FRAGMENT instance and fills it in with the corresponding fragment + // information. Fragments are of the form: + // |m|1|2, meaning chunk 1 of 2 of character m, or + // |:|1n2, meaning chunk 1 of 2 of character :, and no chopping was needed + // to divide the parts, as they were already separate connected components. + // + // If parsing succeeded returns the pointer to the allocated CHAR_FRAGMENT + // instance, otherwise (if the string does not represent a fragment or it + // looks like it does, but parsing it as a fragment fails) returns NULL. + // + // Note: The caller is responsible for deallocating memory + // associated with the returned pointer. + static CHAR_FRAGMENT *parse_from_string(const char *str); + + private: + char unichar[UNICHAR_LEN + 1]; + // True if the fragment was a separate component to begin with, + // ie did not need chopping to be isolated, but may have been separated + // out from a multi-outline blob. + bool natural; + inT16 pos; // fragment position in the character + inT16 total; // total number of fragments in the character +}; + +// The UNICHARSET class is an utility class for Tesseract that holds the +// set of characters that are used by the engine. Each character is identified +// by a unique number, from 0 to (size - 1). +class UNICHARSET { + public: + // Custom list of characters and their ligature forms (UTF8) + // These map to unicode values in the private use area (PUC) and are supported + // by only few font families (eg. Wyld, Adobe Caslon Pro). + static TESS_API const char* kCustomLigatures[][2]; + + // List of strings for the SpecialUnicharCodes. Keep in sync with the enum. + static const char* kSpecialUnicharCodes[SPECIAL_UNICHAR_CODES_COUNT]; + + // ICU 2.0 UCharDirection enum (from third_party/icu/include/unicode/uchar.h) + enum Direction { + U_LEFT_TO_RIGHT = 0, + U_RIGHT_TO_LEFT = 1, + U_EUROPEAN_NUMBER = 2, + U_EUROPEAN_NUMBER_SEPARATOR = 3, + U_EUROPEAN_NUMBER_TERMINATOR = 4, + U_ARABIC_NUMBER = 5, + U_COMMON_NUMBER_SEPARATOR = 6, + U_BLOCK_SEPARATOR = 7, + U_SEGMENT_SEPARATOR = 8, + U_WHITE_SPACE_NEUTRAL = 9, + U_OTHER_NEUTRAL = 10, + U_LEFT_TO_RIGHT_EMBEDDING = 11, + U_LEFT_TO_RIGHT_OVERRIDE = 12, + U_RIGHT_TO_LEFT_ARABIC = 13, + U_RIGHT_TO_LEFT_EMBEDDING = 14, + U_RIGHT_TO_LEFT_OVERRIDE = 15, + U_POP_DIRECTIONAL_FORMAT = 16, + U_DIR_NON_SPACING_MARK = 17, + U_BOUNDARY_NEUTRAL = 18, + U_CHAR_DIRECTION_COUNT + }; + + // Create an empty UNICHARSET + UNICHARSET(); + + ~UNICHARSET(); + + // Return the UNICHAR_ID of a given unichar representation within the + // UNICHARSET. + UNICHAR_ID TESS_API unichar_to_id(const char* const unichar_repr) const; + + // Return the UNICHAR_ID of a given unichar representation within the + // UNICHARSET. Only the first length characters from unichar_repr are used. + UNICHAR_ID unichar_to_id(const char* const unichar_repr, int length) const; + + // Return the minimum number of bytes that matches a legal UNICHAR_ID, + // while leaving the rest of the string encodable. Returns 0 if the + // beginning of the string is not encodable. + // WARNING: this function now encodes the whole string for precision. + // Use encode_string in preference to repeatedly calling step. + int step(const char* str) const; + + // Return whether the given UTF-8 string is encodable with this UNICHARSET. + // If not encodable, write the first byte offset which cannot be converted + // into the second (return) argument. + bool encodable_string(const char *str, int *first_bad_position) const; + + // Encodes the given UTF-8 string with this UNICHARSET. + // Any part of the string that cannot be encoded (because the utf8 can't + // be broken up into pieces that are in the unicharset) then: + // if give_up_on_failure, stops and returns a partial encoding, + // else continues and inserts an INVALID_UNICHAR_ID in the returned encoding. + // Returns true if the encoding succeeds completely, false if there is at + // least one failure. + // If lengths is not NULL, then it is filled with the corresponding + // byte length of each encoded UNICHAR_ID. + // If encoded_length is not NULL then on return it contains the length of + // str that was encoded. (if give_up_on_failure the location of the first + // failure, otherwise strlen(str).) + bool encode_string(const char* str, bool give_up_on_failure, + GenericVector* encoding, + GenericVector* lengths, + int* encoded_length) const; + + // Return the unichar representation corresponding to the given UNICHAR_ID + // within the UNICHARSET. + const char* id_to_unichar(UNICHAR_ID id) const; + + // Return the UTF8 representation corresponding to the given UNICHAR_ID after + // resolving any private encodings internal to Tesseract. This method is + // preferable to id_to_unichar for outputting text that will be visible to + // external applications. + const char* id_to_unichar_ext(UNICHAR_ID id) const; + + // Return a STRING that reformats the utf8 str into the str followed + // by its hex unicodes. + static STRING debug_utf8_str(const char* str); + + // Return a STRING containing debug information on the unichar, including + // the id_to_unichar, its hex unicodes and the properties. + STRING debug_str(UNICHAR_ID id) const; + STRING debug_str(const char * unichar_repr) const { + return debug_str(unichar_to_id(unichar_repr)); + } + + // Add a unichar representation to the set. + void TESS_API unichar_insert(const char* const unichar_repr); + + // Return true if the given unichar id exists within the set. + // Relies on the fact that unichar ids are contiguous in the unicharset. + bool contains_unichar_id(UNICHAR_ID unichar_id) const { + return unichar_id != INVALID_UNICHAR_ID && unichar_id < size_used && + unichar_id >= 0; + } + + // Return true if the given unichar representation exists within the set. + bool TESS_API contains_unichar(const char* const unichar_repr) const; + bool contains_unichar(const char* const unichar_repr, int length) const; + + // Return true if the given unichar representation corresponds to the given + // UNICHAR_ID within the set. + bool eq(UNICHAR_ID unichar_id, const char* const unichar_repr) const; + + // Delete CHAR_FRAGMENTs stored in properties of unichars array. + void delete_pointers_in_unichars() { + for (int i = 0; i < size_used; ++i) { + if (unichars[i].properties.fragment != NULL) { + delete unichars[i].properties.fragment; + unichars[i].properties.fragment = NULL; + } + } + } + + // Clear the UNICHARSET (all the previous data is lost). + void clear() { + if (script_table != NULL) { + for (int i = 0; i < script_table_size_used; ++i) + delete[] script_table[i]; + delete[] script_table; + script_table = NULL; + script_table_size_used = 0; + } + if (unichars != NULL) { + delete_pointers_in_unichars(); + delete[] unichars; + unichars = NULL; + } + script_table_size_reserved = 0; + size_reserved = 0; + size_used = 0; + ids.clear(); + top_bottom_set_ = false; + script_has_upper_lower_ = false; + script_has_xheight_ = false; + null_sid_ = 0; + common_sid_ = 0; + latin_sid_ = 0; + cyrillic_sid_ = 0; + greek_sid_ = 0; + han_sid_ = 0; + hiragana_sid_ = 0; + katakana_sid_ = 0; + default_sid_ = 0; + } + + // Return the size of the set (the number of different UNICHAR it holds). + int size() const { + return size_used; + } + + // Reserve enough memory space for the given number of UNICHARS + void reserve(int unichars_number); + + // Opens the file indicated by filename and saves unicharset to that file. + // Returns true if the operation is successful. + bool save_to_file(const char * const filename) const { + FILE* file = fopen(filename, "w+b"); + if (file == NULL) return false; + bool result = save_to_file(file); + fclose(file); + return result; + } + + // Saves the content of the UNICHARSET to the given file. + // Returns true if the operation is successful. + bool save_to_file(FILE *file) const { + STRING str; + if (!save_to_string(&str)) return false; + if (fwrite(&str[0], str.length(), 1, file) != 1) return false; + return true; + } + bool save_to_file(tesseract::TFile *file) const { + STRING str; + if (!save_to_string(&str)) return false; + if (file->FWrite(&str[0], str.length(), 1) != 1) return false; + return true; + } + + // Saves the content of the UNICHARSET to the given STRING. + // Returns true if the operation is successful. + bool TESS_API save_to_string(STRING *str) const; + + // Load a unicharset from a unicharset file that has been loaded into + // the given memory buffer. + // Returns true if the operation is successful. + bool load_from_inmemory_file(const char* const memory, int mem_size, + bool skip_fragments); + // Returns true if the operation is successful. + bool load_from_inmemory_file(const char* const memory, int mem_size) { + return load_from_inmemory_file(memory, mem_size, false); + } + + // Opens the file indicated by filename and loads the UNICHARSET + // from the given file. The previous data is lost. + // Returns true if the operation is successful. + bool load_from_file(const char* const filename, bool skip_fragments) { + FILE* file = fopen(filename, "rb"); + if (file == NULL) return false; + bool result = load_from_file(file, skip_fragments); + fclose(file); + return result; + } + // returns true if the operation is successful. + bool load_from_file(const char* const filename) { + return load_from_file(filename, false); + } + + // Loads the UNICHARSET from the given file. The previous data is lost. + // Returns true if the operation is successful. + bool load_from_file(FILE *file, bool skip_fragments); + bool load_from_file(FILE *file) { return load_from_file(file, false); } + bool load_from_file(tesseract::TFile *file, bool skip_fragments); + + + // Sets up internal data after loading the file, based on the char + // properties. Called from load_from_file, but also needs to be run + // during set_unicharset_properties. + void post_load_setup(); + + // Returns true if right_to_left scripts are significant in the unicharset, + // but without being so sensitive that "universal" unicharsets containing + // characters from many scripts, like orientation and script detection, + // look like they are right_to_left. + bool major_right_to_left() const; + + // Set a whitelist and/or blacklist of characters to recognize. + // An empty or NULL whitelist enables everything (minus any blacklist). + // An empty or NULL blacklist disables nothing. + // An empty or NULL unblacklist has no effect. + // The blacklist overrides the whitelist. + // The unblacklist overrides the blacklist. + // Each list is a string of utf8 character strings. Boundaries between + // unicharset units are worked out automatically, and characters not in + // the unicharset are silently ignored. + void set_black_and_whitelist(const char* blacklist, const char* whitelist, + const char* unblacklist); + + // Set the isalpha property of the given unichar to the given value. + void set_isalpha(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.isalpha = value; + } + + // Set the islower property of the given unichar to the given value. + void set_islower(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.islower = value; + } + + // Set the isupper property of the given unichar to the given value. + void set_isupper(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.isupper = value; + } + + // Set the isdigit property of the given unichar to the given value. + void set_isdigit(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.isdigit = value; + } + + // Set the ispunctuation property of the given unichar to the given value. + void set_ispunctuation(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.ispunctuation = value; + } + + // Set the isngram property of the given unichar to the given value. + void set_isngram(UNICHAR_ID unichar_id, bool value) { + unichars[unichar_id].properties.isngram = value; + } + + // Set the script name of the given unichar to the given value. + // Value is copied and thus can be a temporary; + void set_script(UNICHAR_ID unichar_id, const char* value) { + unichars[unichar_id].properties.script_id = add_script(value); + } + + // Set other_case unichar id in the properties for the given unichar id. + void set_other_case(UNICHAR_ID unichar_id, UNICHAR_ID other_case) { + unichars[unichar_id].properties.other_case = other_case; + } + + // Set the direction property of the given unichar to the given value. + void set_direction(UNICHAR_ID unichar_id, UNICHARSET::Direction value) { + unichars[unichar_id].properties.direction = value; + } + + // Set mirror unichar id in the properties for the given unichar id. + void set_mirror(UNICHAR_ID unichar_id, UNICHAR_ID mirror) { + unichars[unichar_id].properties.mirror = mirror; + } + + // Record normalized version of unichar with the given unichar_id. + void set_normed(UNICHAR_ID unichar_id, const char* normed) { + unichars[unichar_id].properties.normed = normed; + unichars[unichar_id].properties.normed_ids.truncate(0); + } + // Sets the normed_ids vector from the normed string. normed_ids is not + // stored in the file, and needs to be set when the UNICHARSET is loaded. + void set_normed_ids(UNICHAR_ID unichar_id); + + // Return the isalpha property of the given unichar. + bool get_isalpha(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.isalpha; + } + + // Return the islower property of the given unichar. + bool get_islower(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.islower; + } + + // Return the isupper property of the given unichar. + bool get_isupper(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.isupper; + } + + // Return the isdigit property of the given unichar. + bool get_isdigit(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.isdigit; + } + + // Return the ispunctuation property of the given unichar. + bool get_ispunctuation(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.ispunctuation; + } + + // Return the isngram property of the given unichar. + bool get_isngram(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return false; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.isngram; + } + + // Returns whether the unichar id represents a unicode value in the private + // use area. + bool get_isprivate(UNICHAR_ID unichar_id) const; + + // Returns true if the ids have useful min/max top/bottom values. + bool top_bottom_useful() const { + return top_bottom_set_; + } + // Sets all ranges to empty, so they can be expanded to set the values. + void set_ranges_empty(); + // Sets all the properties for this unicharset given a src_unicharset with + // everything set. The unicharsets don't have to be the same, and graphemes + // are correctly accounted for. + void SetPropertiesFromOther(const UNICHARSET& src) { + PartialSetPropertiesFromOther(0, src); + } + // Sets properties from Other, starting only at the given index. + void PartialSetPropertiesFromOther(int start_index, const UNICHARSET& src); + // Expands the tops and bottoms and widths for this unicharset given a + // src_unicharset with ranges in it. The unicharsets don't have to be the + // same, and graphemes are correctly accounted for. + void ExpandRangesFromOther(const UNICHARSET& src); + // Makes this a copy of src. Clears this completely first, so the automattic + // ids will not be present in this if not in src. + void CopyFrom(const UNICHARSET& src); + // For each id in src, if it does not occur in this, add it, as in + // SetPropertiesFromOther, otherwise expand the ranges, as in + // ExpandRangesFromOther. + void AppendOtherUnicharset(const UNICHARSET& src); + // Returns true if the acceptable ranges of the tops of the characters do + // not overlap, making their x-height calculations distinct. + bool SizesDistinct(UNICHAR_ID id1, UNICHAR_ID id2) const; + // Returns the min and max bottom and top of the given unichar in + // baseline-normalized coordinates, ie, where the baseline is + // kBlnBaselineOffset and the meanline is kBlnBaselineOffset + kBlnXHeight + // (See normalis.h for the definitions). + void get_top_bottom(UNICHAR_ID unichar_id, + int* min_bottom, int* max_bottom, + int* min_top, int* max_top) const { + if (INVALID_UNICHAR_ID == unichar_id) { + *min_bottom = *min_top = 0; + *max_bottom = *max_top = 256; // kBlnCellHeight + return; + } + ASSERT_HOST(contains_unichar_id(unichar_id)); + *min_bottom = unichars[unichar_id].properties.min_bottom; + *max_bottom = unichars[unichar_id].properties.max_bottom; + *min_top = unichars[unichar_id].properties.min_top; + *max_top = unichars[unichar_id].properties.max_top; + } + void set_top_bottom(UNICHAR_ID unichar_id, + int min_bottom, int max_bottom, + int min_top, int max_top) { + unichars[unichar_id].properties.min_bottom = + static_cast(ClipToRange(min_bottom, 0, MAX_UINT8)); + unichars[unichar_id].properties.max_bottom = + static_cast(ClipToRange(max_bottom, 0, MAX_UINT8)); + unichars[unichar_id].properties.min_top = + static_cast(ClipToRange(min_top, 0, MAX_UINT8)); + unichars[unichar_id].properties.max_top = + static_cast(ClipToRange(max_top, 0, MAX_UINT8)); + } + // Returns the width stats (as mean, sd) of the given unichar relative to the + // median advance of all characters in the character set. + void get_width_stats(UNICHAR_ID unichar_id, + float* width, float* width_sd) const { + if (INVALID_UNICHAR_ID == unichar_id) { + *width = 0.0f; + *width_sd = 0.0f;; + return; + } + ASSERT_HOST(contains_unichar_id(unichar_id)); + *width = unichars[unichar_id].properties.width; + *width_sd = unichars[unichar_id].properties.width_sd; + } + void set_width_stats(UNICHAR_ID unichar_id, float width, float width_sd) { + unichars[unichar_id].properties.width = width; + unichars[unichar_id].properties.width_sd = width_sd; + } + // Returns the stats of the x-bearing (as mean, sd) of the given unichar + // relative to the median advance of all characters in the character set. + void get_bearing_stats(UNICHAR_ID unichar_id, + float* bearing, float* bearing_sd) const { + if (INVALID_UNICHAR_ID == unichar_id) { + *bearing = *bearing_sd = 0.0f; + return; + } + ASSERT_HOST(contains_unichar_id(unichar_id)); + *bearing = unichars[unichar_id].properties.bearing; + *bearing_sd = unichars[unichar_id].properties.bearing_sd; + } + void set_bearing_stats(UNICHAR_ID unichar_id, + float bearing, float bearing_sd) { + unichars[unichar_id].properties.bearing = bearing; + unichars[unichar_id].properties.bearing_sd = bearing_sd; + } + // Returns the stats of the x-advance of the given unichar (as mean, sd) + // relative to the median advance of all characters in the character set. + void get_advance_stats(UNICHAR_ID unichar_id, + float* advance, float* advance_sd) const { + if (INVALID_UNICHAR_ID == unichar_id) { + *advance = *advance_sd = 0; + return; + } + ASSERT_HOST(contains_unichar_id(unichar_id)); + *advance = unichars[unichar_id].properties.advance; + *advance_sd = unichars[unichar_id].properties.advance_sd; + } + void set_advance_stats(UNICHAR_ID unichar_id, + float advance, float advance_sd) { + unichars[unichar_id].properties.advance = advance; + unichars[unichar_id].properties.advance_sd = advance_sd; + } + // Returns true if the font metrics properties are empty. + bool PropertiesIncomplete(UNICHAR_ID unichar_id) const { + return unichars[unichar_id].properties.AnyRangeEmpty(); + } + + // Return the script name of the given unichar. + // The returned pointer will always be the same for the same script, it's + // managed by unicharset and thus MUST NOT be deleted + int get_script(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return null_sid_; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.script_id; + } + + // Return the character properties, eg. alpha/upper/lower/digit/punct, + // as a bit field of unsigned int. + unsigned int get_properties(UNICHAR_ID unichar_id) const; + + // Return the character property as a single char. If a character has + // multiple attributes, the main property is defined by the following order: + // upper_case : 'A' + // lower_case : 'a' + // alpha : 'x' + // digit : '0' + // punctuation: 'p' + char get_chartype(UNICHAR_ID unichar_id) const; + + // Get other_case unichar id in the properties for the given unichar id. + UNICHAR_ID get_other_case(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.other_case; + } + + // Returns the direction property of the given unichar. + Direction get_direction(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return UNICHARSET::U_OTHER_NEUTRAL; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.direction; + } + + // Get mirror unichar id in the properties for the given unichar id. + UNICHAR_ID get_mirror(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.mirror; + } + + // Returns UNICHAR_ID of the corresponding lower-case unichar. + UNICHAR_ID to_lower(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; + ASSERT_HOST(contains_unichar_id(unichar_id)); + if (unichars[unichar_id].properties.islower) return unichar_id; + return unichars[unichar_id].properties.other_case; + } + + // Returns UNICHAR_ID of the corresponding upper-case unichar. + UNICHAR_ID to_upper(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return INVALID_UNICHAR_ID; + ASSERT_HOST(contains_unichar_id(unichar_id)); + if (unichars[unichar_id].properties.isupper) return unichar_id; + return unichars[unichar_id].properties.other_case; + } + + // Returns true if this UNICHARSET has the special codes in + // SpecialUnicharCodes available. If false then there are normal unichars + // at these codes and they should not be used. + bool has_special_codes() const { + return get_fragment(UNICHAR_BROKEN) != NULL && + strcmp(id_to_unichar(UNICHAR_BROKEN), + kSpecialUnicharCodes[UNICHAR_BROKEN]) == 0; + } + + // Returns true if there are any repeated unicodes in the normalized + // text of any unichar-id in the unicharset. + bool AnyRepeatedUnicodes() const; + + // Return a pointer to the CHAR_FRAGMENT class if the given + // unichar id represents a character fragment. + const CHAR_FRAGMENT *get_fragment(UNICHAR_ID unichar_id) const { + if (INVALID_UNICHAR_ID == unichar_id) return NULL; + ASSERT_HOST(contains_unichar_id(unichar_id)); + return unichars[unichar_id].properties.fragment; + } + + // Return the isalpha property of the given unichar representation. + bool get_isalpha(const char* const unichar_repr) const { + return get_isalpha(unichar_to_id(unichar_repr)); + } + + // Return the islower property of the given unichar representation. + bool get_islower(const char* const unichar_repr) const { + return get_islower(unichar_to_id(unichar_repr)); + } + + // Return the isupper property of the given unichar representation. + bool get_isupper(const char* const unichar_repr) const { + return get_isupper(unichar_to_id(unichar_repr)); + } + + // Return the isdigit property of the given unichar representation. + bool get_isdigit(const char* const unichar_repr) const { + return get_isdigit(unichar_to_id(unichar_repr)); + } + + // Return the ispunctuation property of the given unichar representation. + bool get_ispunctuation(const char* const unichar_repr) const { + return get_ispunctuation(unichar_to_id(unichar_repr)); + } + + // Return the character properties, eg. alpha/upper/lower/digit/punct, + // of the given unichar representation + unsigned int get_properties(const char* const unichar_repr) const { + return get_properties(unichar_to_id(unichar_repr)); + } + + char get_chartype(const char* const unichar_repr) const { + return get_chartype(unichar_to_id(unichar_repr)); + } + + // Return the script name of the given unichar representation. + // The returned pointer will always be the same for the same script, it's + // managed by unicharset and thus MUST NOT be deleted + int get_script(const char* const unichar_repr) const { + return get_script(unichar_to_id(unichar_repr)); + } + + // Return a pointer to the CHAR_FRAGMENT class struct if the given + // unichar representation represents a character fragment. + const CHAR_FRAGMENT *get_fragment(const char* const unichar_repr) const { + if (unichar_repr == NULL || unichar_repr[0] == '\0' || + !ids.contains(unichar_repr)) { + return NULL; + } + return get_fragment(unichar_to_id(unichar_repr)); + } + + // Return the isalpha property of the given unichar representation. + // Only the first length characters from unichar_repr are used. + bool get_isalpha(const char* const unichar_repr, + int length) const { + return get_isalpha(unichar_to_id(unichar_repr, length)); + } + + // Return the islower property of the given unichar representation. + // Only the first length characters from unichar_repr are used. + bool get_islower(const char* const unichar_repr, + int length) const { + return get_islower(unichar_to_id(unichar_repr, length)); + } + + // Return the isupper property of the given unichar representation. + // Only the first length characters from unichar_repr are used. + bool get_isupper(const char* const unichar_repr, + int length) const { + return get_isupper(unichar_to_id(unichar_repr, length)); + } + + // Return the isdigit property of the given unichar representation. + // Only the first length characters from unichar_repr are used. + bool get_isdigit(const char* const unichar_repr, + int length) const { + return get_isdigit(unichar_to_id(unichar_repr, length)); + } + + // Return the ispunctuation property of the given unichar representation. + // Only the first length characters from unichar_repr are used. + bool get_ispunctuation(const char* const unichar_repr, + int length) const { + return get_ispunctuation(unichar_to_id(unichar_repr, length)); + } + + // Returns normalized version of unichar with the given unichar_id. + const char *get_normed_unichar(UNICHAR_ID unichar_id) const { + if (unichar_id == UNICHAR_SPACE && has_special_codes()) return " "; + return unichars[unichar_id].properties.normed.string(); + } + // Returns a vector of UNICHAR_IDs that represent the ids of the normalized + // version of the given id. There may be more than one UNICHAR_ID in the + // vector if unichar_id represents a ligature. + const GenericVector& normed_ids(UNICHAR_ID unichar_id) const { + return unichars[unichar_id].properties.normed_ids; + } + + // Return the script name of the given unichar representation. + // Only the first length characters from unichar_repr are used. + // The returned pointer will always be the same for the same script, it's + // managed by unicharset and thus MUST NOT be deleted + int get_script(const char* const unichar_repr, + int length) const { + return get_script(unichar_to_id(unichar_repr, length)); + } + + // Return the (current) number of scripts in the script table + int get_script_table_size() const { + return script_table_size_used; + } + + // Return the script string from its id + const char* get_script_from_script_id(int id) const { + if (id >= script_table_size_used || id < 0) + return null_script; + return script_table[id]; + } + + // Returns the id from the name of the script, or 0 if script is not found. + // Note that this is an expensive operation since it involves iteratively + // comparing strings in the script table. To avoid dependency on STL, we + // won't use a hash. Instead, the calling function can use this to lookup + // and save the ID for relevant scripts for fast comparisons later. + int get_script_id_from_name(const char* script_name) const; + + // Return true if the given script is the null script + bool is_null_script(const char* script) const { + return script == null_script; + } + + // Uniquify the given script. For two scripts a and b, if strcmp(a, b) == 0, + // then the returned pointer will be the same. + // The script parameter is copied and thus can be a temporary. + int add_script(const char* script); + + // Return the enabled property of the given unichar. + bool get_enabled(UNICHAR_ID unichar_id) const { + return unichars[unichar_id].properties.enabled; + } + + + int null_sid() const { return null_sid_; } + int common_sid() const { return common_sid_; } + int latin_sid() const { return latin_sid_; } + int cyrillic_sid() const { return cyrillic_sid_; } + int greek_sid() const { return greek_sid_; } + int han_sid() const { return han_sid_; } + int hiragana_sid() const { return hiragana_sid_; } + int katakana_sid() const { return katakana_sid_; } + int default_sid() const { return default_sid_; } + + // Returns true if the unicharset has the concept of upper/lower case. + bool script_has_upper_lower() const { + return script_has_upper_lower_; + } + // Returns true if the unicharset has the concept of x-height. + // script_has_xheight can be true even if script_has_upper_lower is not, + // when the script has a sufficiently predominant top line with ascenders, + // such as Devanagari and Thai. + bool script_has_xheight() const { + return script_has_xheight_; + } + + private: + + struct UNICHAR_PROPERTIES { + UNICHAR_PROPERTIES(); + // Initializes all properties to sensible default values. + void Init(); + // Sets all ranges wide open. Initialization default in case there are + // no useful values available. + void SetRangesOpen(); + // Sets all ranges to empty. Used before expanding with font-based data. + void SetRangesEmpty(); + // Returns true if any of the top/bottom/width/bearing/advance ranges/stats + // is emtpy. + bool AnyRangeEmpty() const; + // Expands the ranges with the ranges from the src properties. + void ExpandRangesFrom(const UNICHAR_PROPERTIES& src); + // Copies the properties from src into this. + void CopyFrom(const UNICHAR_PROPERTIES& src); + + bool isalpha; + bool islower; + bool isupper; + bool isdigit; + bool ispunctuation; + bool isngram; + bool enabled; + // Possible limits of the top and bottom of the bounding box in + // baseline-normalized coordinates, ie, where the baseline is + // kBlnBaselineOffset and the meanline is kBlnBaselineOffset + kBlnXHeight + // (See normalis.h for the definitions). + uinT8 min_bottom; + uinT8 max_bottom; + uinT8 min_top; + uinT8 max_top; + // Statstics of the widths of bounding box, relative to the median advance. + float width; + float width_sd; + // Stats of the x-bearing and advance, also relative to the median advance. + float bearing; + float bearing_sd; + float advance; + float advance_sd; + int script_id; + UNICHAR_ID other_case; // id of the corresponding upper/lower case unichar + Direction direction; // direction of this unichar + // Mirror property is useful for reverse DAWG lookup for words in + // right-to-left languages (e.g. "(word)" would be in + // '[open paren]' 'w' 'o' 'r' 'd' '[close paren]' in a UTF8 string. + // However, what we want in our DAWG is + // '[open paren]', 'd', 'r', 'o', 'w', '[close paren]' not + // '[close paren]', 'd', 'r', 'o', 'w', '[open paren]'. + UNICHAR_ID mirror; + // A string of unichar_ids that represent the corresponding normed string. + // For awkward characters like em-dash, this gives hyphen. + // For ligatures, this gives the string of normal unichars. + GenericVector normed_ids; + STRING normed; // normalized version of this unichar + // Contains meta information about the fragment if a unichar represents + // a fragment of a character, otherwise should be set to NULL. + // It is assumed that character fragments are added to the unicharset + // after the corresponding 'base' characters. + CHAR_FRAGMENT *fragment; + }; + + struct UNICHAR_SLOT { + char representation[UNICHAR_LEN + 1]; + UNICHAR_PROPERTIES properties; + }; + + // Internal recursive version of encode_string above. + // str is the start of the whole string. + // str_index is the current position in str. + // str_length is the length of str. + // encoding is a working encoding of str. + // lengths is a working set of lengths of each element of encoding. + // best_total_length is the longest length of str that has been successfully + // encoded so far. + // On return: + // best_encoding contains the encoding that used the longest part of str. + // best_lengths (may be null) contains the lengths of best_encoding. + void encode_string(const char* str, int str_index, int str_length, + GenericVector* encoding, + GenericVector* lengths, + int* best_total_length, + GenericVector* best_encoding, + GenericVector* best_lengths) const; + + // Gets the properties for a grapheme string, combining properties for + // multiple characters in a meaningful way where possible. + // Returns false if no valid match was found in the unicharset. + // NOTE that script_id, mirror, and other_case refer to this unicharset on + // return and will need redirecting if the target unicharset is different. + bool GetStrProperties(const char* utf8_str, + UNICHAR_PROPERTIES* props) const; + + // Load ourselves from a "file" where our only interface to the file is + // an implementation of fgets(). This is the parsing primitive accessed by + // the public routines load_from_file() and load_from_inmemory_file(). + bool load_via_fgets(TessResultCallback2 *fgets_cb, + bool skip_fragments); + + UNICHAR_SLOT* unichars; + UNICHARMAP ids; + int size_used; + int size_reserved; + char** script_table; + int script_table_size_used; + int script_table_size_reserved; + const char* null_script; + // True if the unichars have their tops/bottoms set. + bool top_bottom_set_; + // True if the unicharset has significant upper/lower case chars. + bool script_has_upper_lower_; + // True if the unicharset has a significant mean-line with significant + // ascenders above that. + bool script_has_xheight_; + + // A few convenient script name-to-id mapping without using hash. + // These are initialized when unicharset file is loaded. Anything + // missing from this list can be looked up using get_script_id_from_name. + int null_sid_; + int common_sid_; + int latin_sid_; + int cyrillic_sid_; + int greek_sid_; + int han_sid_; + int hiragana_sid_; + int katakana_sid_; + // The most frequently occurring script in the charset. + int default_sid_; +}; + +#endif // TESSERACT_CCUTIL_UNICHARSET_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicity_table.h b/hgdriver/3rdparty/hgOCR/include/ccutil/unicity_table.h new file mode 100644 index 0000000..d664d46 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicity_table.h @@ -0,0 +1,208 @@ +/////////////////////////////////////////////////////////////////////// +// File: UnicityTable.h +// Description: a class to uniquify objects, manipulating them using integers +// ids. +// 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_CCUTIL_UNICITY_TABLE_H_ +#define TESSERACT_CCUTIL_UNICITY_TABLE_H_ + +#include "tesscallback.h" +#include "errcode.h" +#include "genericvector.h" + +// A class to uniquify objects, manipulating them using integers ids. +// T requirements: +// operator= to add an element +// default-constructible: allocating the internal table will call the default +// constructor. +template +class UnicityTable { + public: + UnicityTable(); + /// Clear the structures and deallocate internal structures. + ~UnicityTable(); + + /// Reserve some memory. If there is size or more elements, the table will + /// then allocate size * 2 elements. + void reserve(int size); + + /// Return the size used. + int size() const; + + /// Return the object from an id. + const T &get(int id) const; + + // Return the pointer to an object with the given id. + T *get_mutable(int id); + + /// Return the id of the T object. + /// This method NEEDS a compare_callback to be passed to + /// set_compare_callback. + int get_id(T object) const; + + /// Return true if T is in the table + bool contains(T object) const; + + /// Return true if the id is valid + T contains_id(int id) const; + + /// Add an element in the table + int push_back(T object); + + /// Add a callback to be called to delete the elements when the table took + /// their ownership. + void set_clear_callback(TessCallback1* cb); + + /// Add a callback to be called to compare the elements when needed (contains, + /// get_id, ...) + void set_compare_callback(TessResultCallback2* cb); + + /// Clear the table, calling the callback function if any. + /// All the owned Callbacks are also deleted. + /// If you don't want the Callbacks to be deleted, before calling clear, set + /// the callback to NULL. + void clear(); + + /// This method clear the current object, then, does a shallow copy of + /// its argument, and finally invalidate its argument. + void move(UnicityTable* from); + + /// Read/Write the table to a file. This does _NOT_ read/write the callbacks. + /// The Callback given must be permanent since they will be called more than + /// once. The given callback will be deleted at the end. + /// Returns false on read/write error. + bool write(FILE* f, TessResultCallback2* cb) const; + /// swap is used to switch the endianness. + bool read(FILE* f, TessResultCallback3* cb, bool swap); + + private: + GenericVector table_; + // Mutable because Run method is not const + mutable TessResultCallback2* compare_cb_; +}; + +template +class UnicityTableEqEq : public UnicityTable { + public: + UnicityTableEqEq() { + UnicityTable::set_compare_callback( + NewPermanentTessCallback(tesseract::cmp_eq)); + } +}; + +template +UnicityTable::UnicityTable() : + compare_cb_(0) { +} + + +template +UnicityTable::~UnicityTable() { + clear(); +} + +template +int UnicityTable::size() const{ + return table_.size(); +} + +// Reserve some memory. If there is size or more elements, the table will +// then allocate size * 2 elements. +template +void UnicityTable::reserve(int size) { + table_.reserve(size); +} + +// Return the object from an id. +template +const T &UnicityTable::get(int id) const { + return table_.get(id); +} +// Returns the pointer to the object with the given id. +template +T *UnicityTable::get_mutable(int id) { + return &(table_.get(id)); +} +// Return true if the id is valid +template +T UnicityTable::contains_id(int id) const { + return table_.contains_index(id); +} + +// Return the id of the T object. +template +int UnicityTable::get_id(T object) const { + return table_.get_index(object); +} + +// Return true if T is in the table +template +bool UnicityTable::contains(T object) const { + return get_id(object) != -1; +} + +// Add an element in the table +template +int UnicityTable::push_back(T object) { + int idx = get_id(object); + if (idx == -1) { + idx = table_.push_back(object); + } + return idx; +} + +// Add a callback to be called to delete the elements when the table took +// their ownership. +template +void UnicityTable::set_clear_callback(TessCallback1* cb) { + table_.set_clear_callback(cb); +} + +// Add a callback to be called to delete the elements when the table took +// their ownership. +template +void UnicityTable::set_compare_callback(TessResultCallback2* cb) { + table_.set_compare_callback(cb); + compare_cb_ = cb; +} + +// Clear the table, calling the callback function if any. +template +void UnicityTable::clear() { + table_.clear(); +} + +template +bool UnicityTable::write( + FILE* f, TessResultCallback2* cb) const { + return table_.write(f, cb); +} + +template +bool UnicityTable::read( + FILE* f, TessResultCallback3* cb, bool swap) { + return table_.read(f, cb, swap); +} + +// This method clear the current object, then, does a shallow copy of +// its argument, and finally invalidate its argument. +template +void UnicityTable::move(UnicityTable* from) { + table_.move(&from->table_); +} + +#endif // TESSERACT_CCUTIL_UNICITY_TABLE_H_ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.cpp new file mode 100644 index 0000000..5d9964d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.cpp @@ -0,0 +1,57 @@ +/********************************************************************** + * File: unicodes.h + * Description: Unicode related machinery + * Author: David Eger + * Created: Wed Jun 15 16:37:50 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. + * + **********************************************************************/ + +#include "unicodes.h" +#include "host.h" // for NULL + +namespace tesseract { + +const char *kUTF8LineSeparator = "\u2028"; // "\xe2\x80\xa8"; +const char *kUTF8ParagraphSeparator = "\u2029"; // "\xe2\x80\xa9"; +const char *kLRM = "\u200E"; // Left-to-Right Mark +const char *kRLM = "\u200F"; // Right-to-Left Mark +const char *kRLE = "\u202A"; // Right-to-Left Embedding +const char *kPDF = "\u202C"; // Pop Directional Formatting + +const char *kHyphenLikeUTF8[] = { + "-", // ASCII hyphen-minus + "\u05BE", // word hyphen in hybrew + "\u2010", // hyphen + "\u2011", // non-breaking hyphen + "\u2012", // a hyphen the same width as digits + "\u2013", // en dash + "\u2014", // em dash + "\u2015", // horizontal bar + "\u2212", // arithmetic minus sign + "\uFE58", // small em dash + "\uFE63", // small hyphen-minus + "\uFF0D", // fullwidth hyphen-minus + NULL, // end of our list +}; + +const char *kApostropheLikeUTF8[] = { + "'", // ASCII apostrophe + "`", // ASCII backtick + "\u2018", // opening single quote + "\u2019", // closing single quote + "\u2032", // mathematical prime mark + NULL, // end of our list. +}; + +} // namespace diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.h b/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.h new file mode 100644 index 0000000..f6d2bd5 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/unicodes.h @@ -0,0 +1,39 @@ +/********************************************************************** + * File: unicodes.h + * Description: Unicode related machinery + * Author: David Eger + * Created: Wed Jun 15 16:37:50 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_UNICODES_H__ +#define TESSERACT_CCUTIL_UNICODES_H__ + +namespace tesseract { + +extern const char *kUTF8LineSeparator; +extern const char *kUTF8ParagraphSeparator; +extern const char *kLRM; //< Left-to-Right Mark +extern const char *kRLM; //< Right-to-Left Mark +extern const char *kRLE; //< Right-to-Left Embedding +extern const char *kPDF; //< Pop Directional Formatting + +/// The following are confusable internal word punctuation symbols +/// which we normalize to the first variant when matching in dawgs. +extern const char *kHyphenLikeUTF8[]; +extern const char *kApostropheLikeUTF8[]; + +} // namespace + +#endif // TESSERACT_CCUTIL_UNICODES_H__ diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.cpp b/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.cpp new file mode 100644 index 0000000..aa03c84 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.cpp @@ -0,0 +1,19021 @@ +/////////////////////////////////////////////////////////////////////// +// File: universalambigs.cpp +// Description: Data for a universal ambigs file that is useful for +// any language. +// Author: Ray Smith +// Created: Mon Mar 18 11:26:00 PDT 2013 +// +// (C) Copyright 2013, 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. +// +/////////////////////////////////////////////////////////////////////// + +namespace tesseract { + +extern const char kUniversalAmbigsFile[] = { + '\166', '\062', '\012', '\047', '\047', '\040', '\042', '\040', '\061', + '\012', '\140', '\047', '\040', '\042', '\040', '\061', '\012', '\047', + '\140', '\040', '\042', '\040', '\061', '\012', '\342', '\200', '\230', + '\047', '\040', '\042', '\040', '\061', '\012', '\047', '\342', '\200', + '\230', '\040', '\042', '\040', '\061', '\012', '\342', '\200', '\231', + '\047', '\040', '\042', '\040', '\061', '\012', '\047', '\342', '\200', + '\231', '\040', '\042', '\040', '\061', '\012', '\140', '\140', '\040', + '\042', '\040', '\061', '\012', '\140', '\342', '\200', '\230', '\040', + '\042', '\040', '\061', '\012', '\342', '\200', '\230', '\140', '\040', + '\042', '\040', '\061', '\012', '\140', '\342', '\200', '\231', '\040', + '\042', '\040', '\061', '\012', '\342', '\200', '\231', '\140', '\040', + '\042', '\040', '\061', '\012', '\342', '\200', '\230', '\342', '\200', + '\230', '\040', '\342', '\200', '\234', '\040', '\061', '\012', '\342', + '\200', '\230', '\342', '\200', '\231', '\040', '\042', '\040', '\061', + '\012', '\342', '\200', '\231', '\342', '\200', '\230', '\040', '\042', + '\040', '\061', '\012', '\342', '\200', '\231', '\342', '\200', '\231', + '\040', '\342', '\200', '\235', '\040', '\061', '\012', '\054', '\054', + '\040', '\342', '\200', '\236', '\040', '\061', '\012', '\155', '\040', + '\162', '\156', '\040', '\060', '\012', '\162', '\156', '\040', '\155', + '\040', '\060', '\012', '\155', '\040', '\151', '\156', '\040', '\060', + '\012', '\151', '\156', '\040', '\155', '\040', '\060', '\012', '\144', + '\040', '\143', '\154', '\040', '\060', '\012', '\143', '\154', '\040', + '\144', '\040', '\060', '\012', '\156', '\156', '\040', '\162', '\155', + '\040', '\060', '\012', '\162', '\155', '\040', '\156', '\156', '\040', + '\060', '\012', '\156', '\040', '\162', '\151', '\040', '\060', '\012', + '\162', '\151', '\040', '\156', '\040', '\060', '\012', '\154', '\151', + '\040', '\150', '\040', '\060', '\012', '\154', '\162', '\040', '\150', + '\040', '\060', '\012', '\151', '\151', '\040', '\165', '\040', '\060', + '\012', '\151', '\151', '\040', '\156', '\040', '\060', '\012', '\156', + '\151', '\040', '\155', '\040', '\060', '\012', '\151', '\151', '\151', + '\040', '\155', '\040', '\060', '\012', '\154', '\154', '\040', '\110', + '\040', '\060', '\012', '\111', '\055', '\111', '\040', '\110', '\040', + '\060', '\012', '\166', '\166', '\040', '\167', '\040', '\060', '\012', + '\126', '\126', '\040', '\127', '\040', '\060', '\012', '\164', '\040', + '\146', '\040', '\060', '\012', '\146', '\040', '\164', '\040', '\060', + '\012', '\141', '\040', '\157', '\040', '\060', '\012', '\157', '\040', + '\141', '\040', '\060', '\012', '\145', '\040', '\143', '\040', '\060', + '\012', '\143', '\040', '\145', '\040', '\060', '\012', '\162', '\162', + '\040', '\156', '\040', '\060', '\012', '\105', '\040', '\146', '\151', + '\040', '\060', '\012', '\154', '\074', '\040', '\153', '\040', '\060', + '\012', '\154', '\144', '\040', '\153', '\151', '\040', '\060', '\012', + '\154', '\170', '\040', '\150', '\040', '\060', '\012', '\170', '\156', + '\040', '\155', '\040', '\060', '\012', '\165', '\170', '\040', '\151', + '\156', '\040', '\060', '\012', '\162', '\040', '\164', '\040', '\060', + '\012', '\144', '\040', '\164', '\154', '\040', '\060', '\012', '\144', + '\151', '\040', '\164', '\150', '\040', '\060', '\012', '\165', '\162', + '\040', '\151', '\156', '\040', '\060', '\012', '\165', '\156', '\040', + '\151', '\155', '\040', '\060', '\012', '\165', '\040', '\141', '\040', + '\060', '\012', '\157', '\040', '\303', '\263', '\040', '\060', '\012', + '\303', '\263', '\040', '\157', '\040', '\060', '\012', '\151', '\040', + '\303', '\255', '\040', '\060', '\012', '\303', '\255', '\040', '\151', + '\040', '\060', '\012', '\141', '\040', '\303', '\241', '\040', '\060', + '\012', '\303', '\241', '\040', '\141', '\040', '\060', '\012', '\145', + '\040', '\303', '\251', '\040', '\060', '\012', '\303', '\251', '\040', + '\145', '\040', '\060', '\012', '\165', '\040', '\303', '\272', '\040', + '\060', '\012', '\303', '\272', '\040', '\165', '\040', '\060', '\012', + '\156', '\040', '\303', '\261', '\040', '\060', '\012', '\303', '\261', + '\040', '\156', '\040', '\060', '\012', '\060', '\040', '\157', '\040', + '\060', '\012', '\144', '\040', '\164', '\162', '\040', '\060', '\012', + '\156', '\040', '\164', '\162', '\040', '\060', '\012', '\303', '\261', + '\040', '\146', '\151', '\040', '\060', '\012', '\165', '\040', '\164', + '\151', '\040', '\060', '\012', '\303', '\261', '\040', '\164', '\151', + '\040', '\060', '\012', '\144', '\040', '\164', '\151', '\040', '\060', + '\012', '\144', '\040', '\164', '\303', '\255', '\040', '\060', '\012', + '\144', '\040', '\162', '\303', '\255', '\040', '\060', '\012', '\141', + '\040', '\303', '\240', '\040', '\060', '\012', '\145', '\040', '\303', + '\250', '\040', '\060', '\012', '\156', '\040', '\151', '\152', '\040', + '\060', '\012', '\147', '\040', '\151', '\152', '\040', '\060', '\012', + '\157', '\040', '\303', '\262', '\040', '\060', '\012', '\105', '\040', + '\303', '\211', '\040', '\060', '\012', '\105', '\040', '\303', '\210', + '\040', '\060', '\012', '\165', '\040', '\303', '\274', '\040', '\060', + '\012', '\170', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\131', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\164', '\105', '\040', '\156', '\164', '\040', '\061', + '\012', '\124', '\154', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\170', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\152', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\160', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\162', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\161', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\142', '\114', '\040', '\142', '\145', '\040', '\061', + '\012', '\116', '\166', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\112', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\170', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\172', '\164', '\040', '\164', '\141', '\040', '\061', + '\012', '\161', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\143', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\130', '\160', '\040', '\160', '\157', '\040', '\061', + '\012', '\126', '\161', '\151', '\040', '\164', '\151', '\040', '\061', + '\012', '\125', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\112', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\153', '\144', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\160', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\102', '\166', '\040', '\164', '\151', '\040', '\061', + '\012', '\172', '\122', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\124', '\155', '\040', '\155', '\151', '\040', '\061', + '\012', '\155', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\164', '\160', '\040', '\164', '\151', '\040', '\061', + '\012', '\155', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\104', '\161', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\170', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\166', '\143', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\103', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\101', '\157', '\040', '\166', '\157', '\040', '\061', + '\012', '\161', '\165', '\102', '\040', '\164', '\165', '\040', '\061', + '\012', '\142', '\164', '\126', '\040', '\164', '\151', '\040', '\061', + '\012', '\114', '\155', '\143', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\126', '\167', '\040', '\164', '\151', '\040', '\061', + '\012', '\131', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\120', '\152', '\040', '\164', '\165', '\040', '\061', + '\012', '\146', '\124', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\122', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\144', '\101', '\040', '\144', '\151', '\040', '\061', + '\012', '\152', '\172', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\170', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\166', '\147', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\165', '\126', '\040', '\164', '\165', '\040', '\061', + '\012', '\163', '\127', '\153', '\040', '\153', '\165', '\040', '\061', + '\012', '\120', '\147', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\153', '\125', '\040', '\153', '\165', '\040', '\061', + '\012', '\147', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\144', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\126', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\147', '\144', '\040', '\144', '\151', '\040', '\061', + '\012', '\172', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\161', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\163', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\146', '\116', '\040', '\144', '\151', '\040', '\061', + '\012', '\144', '\147', '\127', '\040', '\144', '\151', '\040', '\061', + '\012', '\167', '\116', '\162', '\040', '\162', '\151', '\040', '\061', + '\012', '\172', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\131', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\110', '\171', '\040', '\164', '\165', '\040', '\061', + '\012', '\164', '\116', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\170', '\112', '\040', '\154', '\151', '\040', '\061', + '\012', '\110', '\142', '\153', '\040', '\153', '\165', '\040', '\061', + '\012', '\170', '\163', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\123', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\106', '\142', '\040', '\142', '\165', '\040', '\061', + '\012', '\116', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\153', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\132', '\146', '\040', '\164', '\157', '\040', '\061', + '\012', '\153', '\143', '\125', '\040', '\153', '\157', '\040', '\061', + '\012', '\146', '\106', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\161', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\156', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\170', '\115', '\040', '\160', '\157', '\040', '\061', + '\012', '\145', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\112', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\156', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\103', '\161', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\146', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\161', '\156', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\123', '\163', '\040', '\151', '\163', '\040', '\061', + '\012', '\163', '\102', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\106', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\115', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\170', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\144', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\162', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\105', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\126', '\143', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\124', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\101', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\154', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\142', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\126', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\121', '\154', '\040', '\164', '\145', '\040', '\061', + '\012', '\163', '\127', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\102', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\125', '\143', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\117', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\110', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\116', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\106', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\155', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\146', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\170', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\146', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\144', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\116', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\120', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\126', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\112', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\160', '\110', '\040', '\160', '\157', '\040', '\061', + '\012', '\170', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\155', '\102', '\040', '\156', '\164', '\040', '\061', + '\012', '\172', '\143', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\146', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\146', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\132', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\122', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\165', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\142', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\166', '\152', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\103', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\167', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\107', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\142', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\103', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\157', '\121', '\040', '\160', '\157', '\040', '\061', + '\012', '\161', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\166', '\147', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\101', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\146', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\147', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\160', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\121', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\152', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\170', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\120', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\116', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\107', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\165', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\131', '\145', '\040', '\164', '\145', '\040', '\061', + '\012', '\146', '\132', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\131', '\040', '\146', '\157', '\040', '\061', + '\012', '\171', '\120', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\146', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\170', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\172', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\141', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\126', '\144', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\152', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\153', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\121', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\166', '\152', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\160', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\112', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\155', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\103', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\167', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\107', '\146', '\040', '\156', '\164', '\040', '\061', + '\012', '\152', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\127', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\170', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\161', '\156', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\144', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\116', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\117', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\102', '\171', '\040', '\146', '\157', '\040', '\061', + '\012', '\156', '\125', '\152', '\040', '\156', '\164', '\040', '\061', + '\012', '\154', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\154', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\130', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\126', '\167', '\040', '\166', '\145', '\040', '\061', + '\012', '\172', '\127', '\156', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\112', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\115', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\155', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\123', '\163', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\127', '\154', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\152', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\113', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\143', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\153', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\125', '\145', '\040', '\164', '\145', '\040', '\061', + '\012', '\154', '\125', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\102', '\147', '\040', '\156', '\164', '\040', '\061', + '\012', '\144', '\110', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\160', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\102', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\144', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\146', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\147', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\153', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\121', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\160', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\102', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\155', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\127', '\146', '\040', '\166', '\145', '\040', '\061', + '\012', '\152', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\171', '\111', '\040', '\156', '\171', '\040', '\061', + '\012', '\132', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\117', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\153', '\112', '\040', '\153', '\157', '\040', '\061', + '\012', '\144', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\142', '\127', '\040', '\151', '\163', '\040', '\061', + '\012', '\172', '\115', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\112', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\115', '\143', '\040', '\153', '\157', '\040', '\061', + '\012', '\172', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\106', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\132', '\170', '\040', '\170', '\145', '\040', '\061', + '\012', '\161', '\166', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\162', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\152', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\167', '\111', '\040', '\167', '\141', '\040', '\061', + '\012', '\151', '\104', '\167', '\040', '\164', '\151', '\040', '\061', + '\012', '\102', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\120', '\172', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\106', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\121', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\102', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\166', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\152', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\172', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\110', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\142', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\127', '\147', '\040', '\156', '\164', '\040', '\061', + '\012', '\131', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\170', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\127', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\164', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\156', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\152', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\171', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\106', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\157', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\167', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\156', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\142', '\167', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\156', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\172', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\160', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\124', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\144', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\125', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\113', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\152', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\124', '\156', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\155', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\142', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\163', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\170', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\170', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\121', '\164', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\170', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\144', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\167', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\126', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\166', '\154', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\115', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\117', '\141', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\131', '\160', '\040', '\164', '\151', '\040', '\061', + '\012', '\166', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\157', '\126', '\040', '\162', '\157', '\040', '\061', + '\012', '\146', '\132', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\121', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\127', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\167', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\163', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\156', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\171', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\122', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\165', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\167', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\126', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\132', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\162', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\163', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\166', '\163', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\103', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\166', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\167', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\107', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\127', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\113', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\127', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\155', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\160', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\172', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\155', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\116', '\153', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\160', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\167', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\110', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\172', '\103', '\040', '\152', '\157', '\040', '\061', + '\012', '\157', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\130', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\105', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\165', '\127', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\163', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\123', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\113', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\171', '\145', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\155', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\144', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\116', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\172', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\152', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\114', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\142', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\170', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\126', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\120', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\155', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\106', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\141', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\116', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\167', '\131', '\040', '\164', '\151', '\040', '\061', + '\012', '\116', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\172', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\152', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\127', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\114', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\172', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\172', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\125', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\107', '\170', '\040', '\164', '\151', '\040', '\061', + '\012', '\170', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\170', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\152', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\107', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\156', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\146', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\165', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\132', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\163', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\126', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\104', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\101', '\157', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\155', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\127', '\156', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\122', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\166', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\151', '\131', '\040', '\164', '\151', '\040', '\061', + '\012', '\170', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\144', '\156', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\170', '\132', '\040', '\170', '\145', '\040', '\061', + '\012', '\130', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\156', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\127', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\121', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\167', '\107', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\155', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\171', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\172', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\107', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\152', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\126', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\167', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\156', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\143', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\117', '\156', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\156', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\164', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\127', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\101', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\132', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\124', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\131', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\167', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\121', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\126', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\102', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\111', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\121', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\142', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\125', '\157', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\126', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\144', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\161', '\103', '\040', '\166', '\157', '\040', '\061', + '\012', '\152', '\153', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\166', '\172', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\120', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\103', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\146', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\167', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\124', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\154', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\114', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\121', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\166', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\147', '\145', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\112', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\116', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\113', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\131', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\152', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\101', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\155', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\107', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\127', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\160', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\167', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\127', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\111', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\142', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\161', '\143', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\145', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\167', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\110', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\167', '\120', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\123', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\131', '\160', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\104', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\102', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\116', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\122', '\154', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\102', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\154', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\167', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\101', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\104', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\102', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\146', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\157', '\116', '\040', '\157', '\156', '\040', '\061', + '\012', '\131', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\126', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\120', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\170', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\146', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\127', '\144', '\040', '\144', '\157', '\040', '\061', + '\012', '\170', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\117', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\153', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\155', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\101', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\110', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\113', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\110', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\170', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\171', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\157', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\115', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\120', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\167', '\107', '\040', '\167', '\141', '\040', '\061', + '\012', '\103', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\157', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\161', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\120', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\146', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\171', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\152', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\155', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\145', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\171', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\163', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\167', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\103', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\160', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\117', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\106', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\113', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\110', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\162', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\114', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\131', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\103', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\167', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\114', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\113', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\115', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\143', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\170', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\132', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\167', '\104', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\146', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\170', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\120', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\101', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\113', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\103', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\167', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\166', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\125', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\120', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\152', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\160', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\172', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\132', '\163', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\113', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\143', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\126', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\127', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\114', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\113', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\147', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\132', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\112', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\153', '\146', '\115', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\113', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\147', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\107', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\122', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\117', '\141', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\167', '\104', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\130', '\163', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\155', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\127', '\155', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\126', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\152', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\170', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\166', '\163', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\126', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\120', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\146', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\106', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\167', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\117', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\132', '\162', '\040', '\151', '\156', '\040', '\061', + '\012', '\126', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\114', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\106', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\171', '\125', '\040', '\156', '\171', '\040', '\061', + '\012', '\155', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\113', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\115', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\111', '\152', '\040', '\164', '\145', '\040', '\061', + '\012', '\126', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\103', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\125', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\132', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\131', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\163', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\110', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\102', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\117', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\152', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\146', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\117', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\130', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\111', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\120', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\167', '\127', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\115', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\124', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\111', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\152', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\120', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\154', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\131', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\126', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\160', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\144', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\114', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\144', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\102', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\162', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\124', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\131', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\143', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\103', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\120', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\160', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\142', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\123', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\105', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\130', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\153', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\125', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\146', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\160', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\124', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\132', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\102', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\107', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\103', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\116', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\130', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\123', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\130', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\147', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\143', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\155', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\170', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\106', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\162', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\116', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\167', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\114', '\164', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\160', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\111', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\120', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\160', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\104', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\146', '\112', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\102', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\110', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\101', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\116', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\152', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\123', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\115', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\113', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\155', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\172', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\110', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\112', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\127', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\153', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\105', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\125', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\114', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\107', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\107', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\106', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\106', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\123', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\130', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\110', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\165', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\127', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\160', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\161', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\162', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\147', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\171', '\120', '\040', '\156', '\171', '\040', '\061', + '\012', '\132', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\106', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\156', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\120', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\106', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\146', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\146', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\107', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\122', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\127', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\131', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\105', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\110', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\106', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\104', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\114', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\113', '\163', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\167', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\126', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\165', '\111', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\167', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\122', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\146', '\104', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\103', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\112', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\103', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\142', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\103', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\155', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\156', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\167', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\111', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\167', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\112', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\156', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\106', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\147', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\104', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\110', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\171', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\114', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\170', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\115', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\167', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\146', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\164', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\103', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\103', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\146', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\111', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\167', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\167', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\132', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\126', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\124', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\115', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\110', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\152', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\167', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\113', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\146', '\107', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\146', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\167', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\125', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\153', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\155', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\155', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\171', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\171', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\122', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\115', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\167', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\120', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\167', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\171', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\163', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\124', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\155', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\160', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\102', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\165', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\114', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\146', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\125', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\103', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\125', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\102', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\155', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\121', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\172', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\162', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\170', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\131', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\120', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\165', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\120', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\167', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\167', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\170', '\120', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\155', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\141', '\167', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\126', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\167', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\131', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\103', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\146', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\172', '\107', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\166', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\103', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\120', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\150', '\166', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\146', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\131', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\116', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\101', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\170', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\117', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\167', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\121', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\127', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\170', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\130', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\166', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\130', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\103', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\122', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\141', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\120', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\132', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\156', '\161', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\126', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\110', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\112', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\124', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\167', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\155', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\146', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\155', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\117', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\165', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\104', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\122', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\155', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\170', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\106', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\126', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\144', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\115', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\167', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\153', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\122', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\166', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\167', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\121', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\162', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\125', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\167', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\147', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\110', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\121', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\132', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\171', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\105', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\114', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\170', '\125', '\040', '\156', '\171', '\040', '\061', + '\012', '\147', '\166', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\163', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\142', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\115', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\130', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\162', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\117', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\107', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\103', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\161', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\104', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\105', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\132', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\131', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\120', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\147', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\170', '\113', '\040', '\146', '\157', '\040', '\061', + '\012', '\110', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\122', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\155', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\170', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\114', '\163', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\122', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\146', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\167', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\106', '\143', '\040', '\164', '\151', '\040', '\061', + '\012', '\167', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\142', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\146', '\123', '\040', '\146', '\157', '\040', '\061', + '\012', '\120', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\131', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\104', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\103', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\170', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\115', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\147', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\161', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\164', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\125', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\156', '\116', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\124', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\163', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\112', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\155', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\152', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\170', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\144', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\101', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\125', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\130', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\102', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\147', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\132', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\112', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\131', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\167', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\131', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\110', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\154', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\103', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\104', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\103', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\170', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\145', '\130', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\153', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\122', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\121', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\106', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\145', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\167', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\167', '\104', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\160', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\165', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\162', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\163', '\156', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\120', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\122', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\152', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\143', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\145', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\106', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\142', '\127', '\040', '\142', '\145', '\040', '\061', + '\012', '\165', '\125', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\160', '\124', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\167', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\130', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\114', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\106', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\114', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\142', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\155', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\156', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\116', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\160', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\154', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\110', '\161', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\167', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\152', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\120', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\122', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\112', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\132', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\166', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\120', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\116', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\155', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\151', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\154', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\167', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\144', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\132', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\146', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\121', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\161', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\114', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\152', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\132', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\131', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\126', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\167', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\167', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\145', '\107', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\123', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\163', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\146', '\121', '\040', '\146', '\157', '\040', '\061', + '\012', '\126', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\155', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\101', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\142', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\112', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\170', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\142', '\103', '\040', '\142', '\145', '\040', '\061', + '\012', '\122', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\112', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\171', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\153', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\157', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\132', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\120', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\107', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\122', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\114', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\170', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\143', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\167', '\160', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\124', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\146', '\107', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\152', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\144', '\147', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\152', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\124', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\122', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\131', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\154', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\165', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\117', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\102', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\122', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\127', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\110', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\146', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\152', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\170', '\107', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\160', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\170', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\111', '\152', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\105', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\106', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\111', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\106', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\172', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\111', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\153', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\146', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\153', '\111', '\040', '\153', '\165', '\040', '\061', + '\012', '\146', '\111', '\157', '\040', '\162', '\157', '\040', '\061', + '\012', '\154', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\101', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\104', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\110', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\112', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\103', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\132', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\124', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\126', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\106', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\167', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\107', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\165', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\106', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\110', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\121', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\104', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\153', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\102', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\155', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\144', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\153', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\123', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\144', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\147', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\116', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\146', '\101', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\170', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\170', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\121', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\167', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\170', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\111', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\172', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\161', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\115', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\160', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\117', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\153', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\152', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\165', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\155', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\165', '\112', '\040', '\157', '\165', '\040', '\061', + '\012', '\171', '\127', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\125', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\172', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\123', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\147', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\152', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\146', '\101', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\153', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\144', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\142', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\170', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\111', '\161', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\147', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\122', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\106', '\167', '\040', '\166', '\157', '\040', '\061', + '\012', '\146', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\167', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\112', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\170', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\132', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\110', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\165', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\160', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\121', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\107', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\163', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\167', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\171', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\113', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\155', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\111', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\171', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\104', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\111', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\152', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\102', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\167', '\142', '\040', '\157', '\167', '\040', '\061', + '\012', '\126', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\152', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\107', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\102', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\160', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\131', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\170', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\150', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\104', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\116', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\122', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\115', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\160', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\104', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\171', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\113', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\153', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\167', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\104', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\144', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\112', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\125', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\124', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\155', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\106', '\150', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\163', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\151', '\127', '\147', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\172', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\126', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\104', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\146', '\125', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\157', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\143', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\127', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\121', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\145', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\152', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\124', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\122', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\142', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\111', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\170', '\116', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\103', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\113', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\124', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\155', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\124', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\114', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\111', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\143', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\165', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\103', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\161', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\152', '\172', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\151', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\156', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\155', '\116', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\142', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\153', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\172', '\103', '\040', '\160', '\157', '\040', '\061', + '\012', '\154', '\146', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\102', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\155', '\114', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\152', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\111', '\165', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\146', '\104', '\040', '\156', '\171', '\040', '\061', + '\012', '\143', '\154', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\144', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\124', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\130', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\160', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\155', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\153', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\125', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\170', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\124', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\132', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\126', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\146', '\104', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\120', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\130', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\105', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\142', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\157', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\142', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\160', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\146', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\104', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\115', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\172', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\155', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\153', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\144', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\142', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\170', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\131', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\112', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\114', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\102', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\145', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\167', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\120', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\111', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\142', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\144', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\115', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\170', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\106', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\167', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\162', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\131', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\126', '\143', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\122', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\160', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\116', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\113', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\166', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\167', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\171', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\102', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\123', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\115', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\152', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\131', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\106', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\167', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\152', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\121', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\115', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\112', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\124', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\155', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\131', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\112', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\124', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\146', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\170', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\142', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\170', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\167', '\117', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\166', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\166', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\153', '\106', '\040', '\153', '\165', '\040', '\061', + '\012', '\151', '\171', '\124', '\040', '\151', '\156', '\040', '\061', + '\012', '\125', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\156', '\172', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\142', '\110', '\040', '\142', '\165', '\040', '\061', + '\012', '\154', '\123', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\125', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\171', '\106', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\170', '\120', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\131', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\167', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\125', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\163', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\144', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\131', '\163', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\146', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\122', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\160', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\170', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\142', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\153', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\113', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\121', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\127', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\167', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\143', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\146', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\130', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\147', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\152', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\107', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\142', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\160', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\115', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\167', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\116', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\154', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\102', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\106', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\146', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\106', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\170', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\147', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\106', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\170', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\152', '\172', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\166', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\171', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\106', '\150', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\107', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\114', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\114', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\113', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\112', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\121', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\160', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\151', '\117', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\117', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\152', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\126', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\166', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\164', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\111', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\160', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\170', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\166', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\160', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\146', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\110', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\144', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\124', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\170', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\120', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\170', '\127', '\040', '\156', '\171', '\040', '\061', + '\012', '\110', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\110', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\166', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\147', '\156', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\142', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\122', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\146', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\130', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\155', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\121', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\163', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\111', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\154', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\160', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\155', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\153', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\104', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\131', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\153', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\106', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\171', '\112', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\111', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\131', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\144', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\142', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\167', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\121', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\112', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\170', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\115', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\146', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\153', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\110', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\141', '\112', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\127', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\102', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\116', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\101', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\110', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\122', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\127', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\124', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\170', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\113', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\124', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\157', '\145', '\117', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\103', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\130', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\101', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\107', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\170', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\122', '\172', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\124', '\163', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\110', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\114', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\116', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\107', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\171', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\172', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\105', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\142', '\106', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\163', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\156', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\113', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\165', '\121', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\170', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\113', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\143', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\146', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\102', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\112', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\131', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\121', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\160', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\146', '\113', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\130', '\162', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\155', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\112', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\111', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\130', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\146', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\126', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\112', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\114', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\167', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\167', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\106', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\127', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\114', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\170', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\146', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\120', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\144', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\167', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\115', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\120', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\172', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\106', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\113', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\153', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\107', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\132', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\153', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\122', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\154', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\116', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\131', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\132', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\120', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\127', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\146', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\152', '\172', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\170', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\154', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\131', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\115', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\122', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\114', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\106', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\116', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\106', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\113', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\112', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\113', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\120', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\172', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\110', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\106', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\166', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\130', '\145', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\111', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\142', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\132', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\113', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\160', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\113', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\121', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\106', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\127', '\166', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\152', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\146', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\167', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\127', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\162', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\166', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\161', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\142', '\107', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\172', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\106', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\170', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\141', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\124', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\102', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\126', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\146', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\170', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\110', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\126', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\121', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\167', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\110', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\172', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\146', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\127', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\161', '\105', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\160', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\127', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\154', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\131', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\166', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\142', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\166', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\110', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\160', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\146', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\145', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\142', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\127', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\144', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\166', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\167', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\115', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\143', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\160', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\153', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\165', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\142', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\123', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\155', '\107', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\141', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\167', '\110', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\104', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\107', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\150', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\153', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\146', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\155', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\131', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\167', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\170', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\142', '\123', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\162', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\112', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\130', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\104', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\160', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\147', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\141', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\162', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\112', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\115', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\116', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\132', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\126', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\142', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\123', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\121', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\167', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\124', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\162', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\112', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\114', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\160', '\105', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\132', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\167', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\103', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\162', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\155', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\146', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\102', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\142', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\144', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\102', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\121', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\154', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\122', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\162', '\161', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\127', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\172', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\107', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\127', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\126', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\165', '\120', '\040', '\165', '\156', '\040', '\061', + '\012', '\144', '\110', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\147', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\115', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\153', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\116', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\160', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\152', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\170', '\126', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\147', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\115', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\107', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\142', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\162', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\165', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\153', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\170', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\126', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\113', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\102', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\121', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\131', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\107', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\164', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\166', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\152', '\172', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\165', '\040', '\153', '\165', '\040', '\061', + '\012', '\121', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\155', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\111', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\132', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\114', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\115', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\120', '\155', '\040', '\165', '\155', '\040', '\061', + '\012', '\160', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\172', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\142', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\143', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\155', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\106', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\170', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\102', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\157', '\110', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\127', '\147', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\104', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\167', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\104', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\113', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\166', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\165', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\103', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\144', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\142', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\142', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\142', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\146', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\142', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\142', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\122', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\132', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\112', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\132', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\142', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\112', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\126', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\167', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\167', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\146', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\164', '\130', '\040', '\164', '\151', '\040', '\061', + '\012', '\144', '\113', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\121', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\104', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\160', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\144', '\104', '\040', '\144', '\151', '\040', '\061', + '\012', '\146', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\102', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\105', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\146', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\132', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\170', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\102', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\122', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\126', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\102', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\117', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\107', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\166', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\147', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\162', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\106', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\126', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\146', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\144', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\156', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\172', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\101', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\101', '\167', '\040', '\153', '\157', '\040', '\061', + '\012', '\170', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\116', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\160', '\144', '\040', '\144', '\151', '\040', '\061', + '\012', '\157', '\125', '\171', '\040', '\153', '\157', '\040', '\061', + '\012', '\146', '\160', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\122', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\154', '\130', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\127', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\127', '\166', '\040', '\166', '\151', '\040', '\061', + '\012', '\106', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\127', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\126', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\120', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\167', '\101', '\040', '\144', '\145', '\040', '\061', + '\012', '\117', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\151', '\132', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\172', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\114', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\127', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\124', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\142', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\165', '\117', '\040', '\151', '\156', '\040', '\061', + '\012', '\121', '\144', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\165', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\163', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\111', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\147', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\123', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\170', '\121', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\143', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\142', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\122', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\132', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\103', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\155', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\132', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\104', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\142', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\126', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\121', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\115', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\103', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\156', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\166', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\146', '\112', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\103', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\121', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\165', '\103', '\040', '\165', '\156', '\040', '\061', + '\012', '\103', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\152', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\155', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\130', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\163', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\142', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\167', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\150', '\151', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\113', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\151', '\161', '\116', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\153', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\116', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\167', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\107', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\162', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\122', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\112', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\161', '\106', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\170', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\163', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\146', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\162', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\130', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\115', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\110', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\113', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\111', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\116', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\167', '\114', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\132', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\117', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\162', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\125', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\155', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\172', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\166', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\170', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\123', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\110', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\116', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\123', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\111', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\146', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\172', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\112', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\152', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\141', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\104', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\121', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\112', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\122', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\112', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\153', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\142', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\102', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\126', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\131', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\126', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\172', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\113', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\120', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\170', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\142', '\124', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\166', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\170', '\114', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\123', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\116', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\124', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\114', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\166', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\106', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\147', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\104', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\117', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\146', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\160', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\170', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\126', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\106', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\170', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\167', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\144', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\127', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\124', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\155', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\166', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\103', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\110', '\150', '\040', '\154', '\157', '\040', '\061', + '\012', '\131', '\166', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\126', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\127', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\115', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\131', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\106', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\121', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\113', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\114', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\111', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\147', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\106', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\170', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\152', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\120', '\147', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\156', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\146', '\120', '\040', '\160', '\162', '\040', '\061', + '\012', '\104', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\172', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\167', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\142', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\167', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\146', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\126', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\125', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\172', '\124', '\040', '\152', '\157', '\040', '\061', + '\012', '\153', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\127', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\142', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\141', '\102', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\101', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\144', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\130', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\155', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\171', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\107', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\112', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\155', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\142', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\167', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\101', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\121', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\157', '\132', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\120', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\171', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\130', '\152', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\167', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\156', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\113', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\142', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\107', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\127', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\131', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\104', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\121', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\121', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\142', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\165', '\122', '\040', '\165', '\156', '\040', '\061', + '\012', '\143', '\115', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\130', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\116', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\112', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\103', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\117', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\155', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\160', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\131', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\162', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\155', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\167', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\130', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\131', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\103', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\115', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\126', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\117', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\114', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\131', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\160', '\106', '\040', '\157', '\156', '\040', '\061', + '\012', '\162', '\127', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\132', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\131', '\142', '\040', '\146', '\157', '\040', '\061', + '\012', '\172', '\142', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\102', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\111', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\167', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\164', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\122', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\171', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\155', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\111', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\111', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\142', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\146', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\127', '\160', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\115', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\123', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\103', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\172', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\146', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\117', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\146', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\107', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\101', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\146', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\106', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\124', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\116', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\113', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\155', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\146', '\122', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\151', '\125', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\163', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\164', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\120', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\170', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\151', '\125', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\104', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\170', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\141', '\107', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\142', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\126', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\170', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\126', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\110', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\101', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\111', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\155', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\155', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\154', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\153', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\166', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\161', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\114', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\121', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\167', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\171', '\122', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\171', '\111', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\147', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\121', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\160', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\172', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\111', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\130', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\160', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\153', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\155', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\162', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\142', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\160', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\103', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\106', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\121', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\152', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\126', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\105', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\161', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\154', '\131', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\107', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\104', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\117', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\162', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\146', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\120', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\126', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\126', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\142', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\122', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\170', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\104', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\147', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\120', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\162', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\126', '\161', '\040', '\144', '\151', '\040', '\061', + '\012', '\170', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\147', '\103', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\117', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\116', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\130', '\161', '\040', '\151', '\156', '\040', '\061', + '\012', '\121', '\156', '\154', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\120', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\111', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\120', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\166', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\161', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\167', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\167', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\155', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\122', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\170', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\142', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\146', '\116', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\160', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\155', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\113', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\142', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\116', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\154', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\102', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\141', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\146', '\115', '\040', '\146', '\157', '\040', '\061', + '\012', '\162', '\132', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\121', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\166', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\125', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\160', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\120', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\107', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\106', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\165', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\102', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\111', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\102', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\125', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\172', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\132', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\126', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\170', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\111', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\102', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\113', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\171', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\157', '\102', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\127', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\146', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\121', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\146', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\130', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\112', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\141', '\123', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\122', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\170', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\107', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\163', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\142', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\156', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\142', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\132', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\171', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\102', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\152', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\130', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\124', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\116', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\124', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\124', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\170', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\126', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\122', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\117', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\110', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\152', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\116', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\167', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\160', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\106', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\105', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\167', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\127', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\146', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\160', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\166', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\130', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\105', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\116', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\115', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\171', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\160', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\157', '\101', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\154', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\170', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\125', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\106', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\163', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\166', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\106', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\156', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\142', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\120', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\163', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\146', '\120', '\040', '\156', '\171', '\040', '\061', + '\012', '\147', '\131', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\146', '\103', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\115', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\154', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\152', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\121', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\124', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\125', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\167', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\115', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\172', '\153', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\154', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\141', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\113', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\126', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\170', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\172', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\170', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\166', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\167', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\172', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\120', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\101', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\172', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\146', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\147', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\121', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\153', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\117', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\115', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\152', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\105', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\145', '\121', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\123', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\125', '\170', '\040', '\155', '\142', '\040', '\061', + '\012', '\172', '\144', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\160', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\147', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\146', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\142', '\126', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\131', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\116', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\166', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\166', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\161', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\170', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\161', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\110', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\146', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\163', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\142', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\125', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\143', '\131', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\144', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\153', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\142', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\166', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\142', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\120', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\160', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\117', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\120', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\101', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\153', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\156', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\116', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\130', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\155', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\125', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\163', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\160', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\145', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\122', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\112', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\163', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\167', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\120', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\101', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\167', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\152', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\132', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\104', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\123', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\121', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\102', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\114', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\162', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\120', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\130', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\142', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\116', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\112', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\112', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\122', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\166', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\166', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\110', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\124', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\104', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\117', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\121', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\166', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\146', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\167', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\154', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\170', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\167', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\166', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\146', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\101', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\115', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\110', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\120', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\112', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\127', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\146', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\106', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\127', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\166', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\111', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\106', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\170', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\114', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\122', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\170', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\152', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\112', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\106', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\105', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\122', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\146', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\167', '\126', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\146', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\127', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\152', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\106', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\165', '\107', '\040', '\165', '\156', '\040', '\061', + '\012', '\154', '\103', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\170', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\107', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\166', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\131', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\160', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\160', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\114', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\127', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\162', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\126', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\116', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\120', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\144', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\125', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\107', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\104', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\146', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\146', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\115', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\152', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\143', '\161', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\127', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\165', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\146', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\171', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\162', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\110', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\171', '\116', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\162', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\113', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\146', '\125', '\040', '\142', '\145', '\040', '\061', + '\012', '\121', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\117', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\130', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\142', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\127', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\124', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\132', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\106', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\160', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\146', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\167', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\160', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\147', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\114', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\170', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\112', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\132', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\116', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\115', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\146', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\125', '\165', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\107', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\160', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\127', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\152', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\165', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\163', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\162', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\127', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\111', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\155', '\116', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\112', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\115', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\123', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\172', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\166', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\155', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\156', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\160', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\114', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\165', '\171', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\145', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\105', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\155', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\152', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\115', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\167', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\161', '\115', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\166', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\142', '\120', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\115', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\165', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\170', '\130', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\166', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\120', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\163', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\146', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\121', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\167', '\146', '\040', '\157', '\167', '\040', '\061', + '\012', '\154', '\152', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\110', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\122', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\154', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\132', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\123', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\127', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\103', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\167', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\105', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\125', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\143', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\104', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\152', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\142', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\117', '\141', '\145', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\142', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\172', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\131', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\102', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\152', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\142', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\107', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\116', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\152', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\155', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\105', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\110', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\107', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\146', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\132', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\171', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\144', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\141', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\123', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\101', '\157', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\114', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\103', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\153', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\131', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\153', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\147', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\127', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\114', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\160', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\167', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\154', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\147', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\161', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\106', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\102', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\127', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\116', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\146', '\103', '\040', '\156', '\171', '\040', '\061', + '\012', '\105', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\152', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\147', '\157', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\102', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\146', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\170', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\126', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\126', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\127', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\123', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\167', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\126', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\170', '\120', '\040', '\156', '\171', '\040', '\061', + '\012', '\131', '\171', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\120', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\117', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\121', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\106', '\040', '\146', '\157', '\040', '\061', + '\012', '\144', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\160', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\147', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\167', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\131', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\123', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\101', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\105', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\113', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\155', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\130', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\121', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\157', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\122', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\171', '\114', '\040', '\156', '\171', '\040', '\061', + '\012', '\153', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\171', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\155', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\111', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\130', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\121', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\115', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\127', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\127', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\142', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\124', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\113', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\146', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\146', '\132', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\126', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\124', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\166', '\166', '\040', '\166', '\151', '\040', '\061', + '\012', '\120', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\110', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\142', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\143', '\106', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\167', '\120', '\040', '\167', '\141', '\040', '\061', + '\012', '\126', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\106', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\102', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\142', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\162', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\153', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\120', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\114', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\162', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\166', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\127', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\127', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\161', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\160', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\120', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\155', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\166', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\144', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\131', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\167', '\142', '\040', '\157', '\167', '\040', '\061', + '\012', '\127', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\104', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\167', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\124', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\103', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\167', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\160', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\103', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\170', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\144', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\166', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\166', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\106', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\126', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\112', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\111', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\167', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\113', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\121', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\142', '\126', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\160', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\125', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\141', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\147', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\127', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\155', '\123', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\132', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\152', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\154', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\167', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\105', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\160', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\112', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\117', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\160', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\124', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\146', '\161', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\132', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\143', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\116', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\131', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\106', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\167', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\167', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\130', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\153', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\147', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\110', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\166', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\151', '\101', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\171', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\171', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\157', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\121', '\153', '\172', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\122', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\106', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\132', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\103', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\113', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\142', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\142', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\146', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\126', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\132', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\132', '\142', '\040', '\147', '\141', '\040', '\061', + '\012', '\167', '\164', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\106', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\104', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\113', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\153', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\123', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\171', '\103', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\126', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\110', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\105', '\167', '\040', '\150', '\141', '\040', '\061', + '\012', '\172', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\111', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\172', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\163', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\142', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\171', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\121', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\154', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\126', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\117', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\153', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\103', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\107', '\155', '\040', '\155', '\141', '\040', '\061', + '\012', '\160', '\141', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\125', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\114', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\146', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\167', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\120', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\170', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\143', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\161', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\172', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\161', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\132', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\110', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\107', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\110', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\106', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\144', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\103', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\152', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\111', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\130', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\142', '\105', '\040', '\142', '\145', '\040', '\061', + '\012', '\110', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\142', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\125', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\153', '\145', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\157', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\103', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\114', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\147', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\117', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\117', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\127', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\116', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\146', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\126', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\146', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\162', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\104', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\117', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\167', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\121', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\161', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\166', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\121', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\115', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\142', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\152', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\142', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\116', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\132', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\103', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\110', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\115', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\111', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\110', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\105', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\112', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\122', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\112', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\106', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\110', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\167', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\121', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\112', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\144', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\117', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\114', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\172', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\162', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\155', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\170', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\142', '\125', '\040', '\142', '\145', '\040', '\061', + '\012', '\141', '\145', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\166', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\122', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\170', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\121', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\130', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\112', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\166', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\126', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\127', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\130', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\102', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\153', '\115', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\110', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\142', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\122', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\166', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\102', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\157', '\120', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\164', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\167', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\106', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\153', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\157', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\166', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\131', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\152', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\127', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\154', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\142', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\131', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\144', '\124', '\040', '\144', '\141', '\040', '\061', + '\012', '\166', '\124', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\116', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\142', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\125', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\130', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\122', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\161', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\117', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\104', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\112', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\163', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\145', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\105', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\111', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\132', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\167', '\122', '\040', '\151', '\156', '\040', '\061', + '\012', '\157', '\112', '\166', '\040', '\153', '\157', '\040', '\061', + '\012', '\165', '\146', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\127', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\165', '\126', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\130', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\112', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\105', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\112', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\105', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\161', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\120', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\127', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\153', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\141', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\126', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\123', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\122', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\142', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\167', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\146', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\147', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\167', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\172', '\112', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\131', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\121', '\154', '\040', '\151', '\156', '\040', '\061', + '\012', '\121', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\106', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\156', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\131', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\111', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\130', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\162', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\152', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\151', '\171', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\132', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\130', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\112', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\142', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\165', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\157', '\125', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\127', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\167', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\171', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\157', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\160', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\112', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\144', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\126', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\152', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\113', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\153', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\142', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\114', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\113', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\132', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\162', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\122', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\115', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\125', '\170', '\040', '\141', '\162', '\040', '\061', + '\012', '\161', '\110', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\153', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\170', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\172', '\127', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\155', '\102', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\172', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\111', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\157', '\113', '\040', '\157', '\156', '\040', '\061', + '\012', '\107', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\107', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\132', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\167', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\146', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\111', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\105', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\114', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\122', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\153', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\166', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\161', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\167', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\126', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\103', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\171', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\147', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\114', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\116', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\112', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\130', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\114', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\167', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\166', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\160', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\142', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\121', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\144', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\111', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\110', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\166', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\146', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\114', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\172', '\153', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\152', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\151', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\114', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\112', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\117', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\127', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\101', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\111', '\171', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\112', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\122', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\146', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\111', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\107', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\143', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\115', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\147', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\155', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\157', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\102', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\127', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\131', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\165', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\114', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\147', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\112', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\115', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\146', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\117', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\153', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\161', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\112', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\155', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\163', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\144', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\156', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\155', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\115', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\142', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\104', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\152', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\144', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\165', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\103', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\164', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\112', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\146', '\130', '\040', '\146', '\157', '\040', '\061', + '\012', '\116', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\102', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\142', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\171', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\146', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\111', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\113', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\124', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\121', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\155', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\160', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\123', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\147', '\151', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\111', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\167', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\117', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\167', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\163', '\120', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\132', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\154', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\170', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\132', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\171', '\123', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\132', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\154', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\171', '\106', '\040', '\151', '\156', '\040', '\061', + '\012', '\103', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\167', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\146', '\126', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\142', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\165', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\114', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\155', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\131', '\157', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\162', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\171', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\165', '\171', '\127', '\040', '\165', '\156', '\040', '\061', + '\012', '\153', '\107', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\167', '\113', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\153', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\130', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\103', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\121', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\120', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\102', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\102', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\105', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\105', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\110', '\167', '\040', '\165', '\163', '\040', '\061', + '\012', '\106', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\151', '\131', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\120', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\121', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\125', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\114', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\122', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\153', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\131', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\167', '\131', '\040', '\151', '\163', '\040', '\061', + '\012', '\162', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\110', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\127', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\157', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\146', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\104', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\142', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\101', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\160', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\160', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\157', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\122', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\125', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\104', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\104', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\107', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\146', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\122', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\146', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\132', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\127', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\111', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\113', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\122', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\163', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\142', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\103', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\162', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\166', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\146', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\161', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\155', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\127', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\111', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\155', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\171', '\116', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\132', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\125', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\103', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\153', '\162', '\040', '\162', '\151', '\040', '\061', + '\012', '\146', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\122', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\103', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\142', '\103', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\115', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\120', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\171', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\145', '\130', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\126', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\103', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\143', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\146', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\142', '\132', '\040', '\151', '\156', '\040', '\061', + '\012', '\116', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\130', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\126', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\170', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\170', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\115', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\160', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\132', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\130', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\166', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\126', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\103', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\146', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\121', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\146', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\121', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\126', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\107', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\115', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\170', '\166', '\167', '\040', '\167', '\151', '\040', '\061', + '\012', '\172', '\111', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\146', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\127', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\155', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\132', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\147', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\141', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\123', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\124', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\123', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\104', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\167', '\166', '\040', '\167', '\151', '\040', '\061', + '\012', '\162', '\160', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\120', '\163', '\040', '\151', '\163', '\040', '\061', + '\012', '\113', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\104', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\162', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\121', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\131', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\170', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\163', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\130', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\167', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\163', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\146', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\111', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\144', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\103', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\172', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\152', '\130', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\127', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\112', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\122', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\130', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\145', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\131', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\170', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\132', '\162', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\146', '\126', '\040', '\165', '\163', '\040', '\061', + '\012', '\162', '\130', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\132', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\124', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\161', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\127', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\146', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\170', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\152', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\115', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\153', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\131', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\166', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\167', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\167', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\130', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\117', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\105', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\110', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\110', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\170', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\111', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\155', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\123', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\146', '\130', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\106', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\156', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\153', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\152', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\127', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\115', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\115', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\142', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\112', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\104', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\152', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\151', '\107', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\154', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\106', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\155', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\104', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\162', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\114', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\142', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\163', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\161', '\154', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\112', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\107', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\157', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\131', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\105', '\157', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\143', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\167', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\153', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\110', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\145', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\165', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\167', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\131', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\166', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\102', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\163', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\113', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\123', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\142', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\141', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\142', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\110', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\131', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\160', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\152', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\107', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\121', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\154', '\112', '\040', '\154', '\151', '\040', '\061', + '\012', '\143', '\161', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\121', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\145', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\142', '\124', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\121', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\104', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\146', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\123', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\112', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\170', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\113', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\142', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\116', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\145', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\130', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\113', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\103', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\113', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\161', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\172', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\152', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\157', '\111', '\040', '\157', '\156', '\040', '\061', + '\012', '\131', '\171', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\146', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\162', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\121', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\142', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\155', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\142', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\127', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\170', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\116', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\146', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\157', '\130', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\114', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\152', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\147', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\113', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\151', '\132', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\130', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\132', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\126', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\123', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\172', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\161', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\121', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\167', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\116', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\105', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\163', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\114', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\162', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\111', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\111', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\114', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\161', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\127', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\131', '\143', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\130', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\161', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\170', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\154', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\151', '\104', '\170', '\040', '\154', '\151', '\040', '\061', + '\012', '\132', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\142', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\122', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\123', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\145', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\113', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\127', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\160', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\105', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\163', '\113', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\115', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\132', '\040', '\153', '\165', '\040', '\061', + '\012', '\127', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\155', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\102', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\103', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\167', '\116', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\146', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\110', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\167', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\152', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\155', '\126', '\040', '\144', '\151', '\040', '\061', + '\012', '\143', '\103', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\130', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\155', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\170', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\162', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\170', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\154', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\103', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\146', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\107', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\142', '\105', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\127', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\170', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\157', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\122', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\147', '\151', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\104', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\127', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\127', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\122', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\164', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\167', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\162', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\146', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\146', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\113', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\127', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\165', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\153', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\131', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\101', '\157', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\122', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\127', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\107', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\143', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\103', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\155', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\121', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\127', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\124', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\141', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\160', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\114', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\163', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\106', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\155', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\154', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\170', '\101', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\106', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\146', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\160', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\127', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\123', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\160', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\124', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\102', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\156', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\161', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\154', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\126', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\165', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\162', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\120', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\106', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\120', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\112', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\162', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\102', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\167', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\127', '\147', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\117', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\142', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\166', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\142', '\106', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\127', '\165', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\152', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\145', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\130', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\110', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\146', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\167', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\110', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\107', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\112', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\107', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\142', '\113', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\127', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\172', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\167', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\110', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\161', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\124', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\120', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\144', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\141', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\167', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\122', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\114', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\171', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\161', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\160', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\153', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\146', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\127', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\130', '\145', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\130', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\170', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\170', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\142', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\120', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\103', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\151', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\107', '\160', '\040', '\160', '\157', '\040', '\061', + '\012', '\150', '\120', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\103', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\172', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\110', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\167', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\103', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\121', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\142', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\130', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\171', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\107', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\166', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\130', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\130', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\171', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\142', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\171', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\155', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\116', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\161', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\131', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\160', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\171', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\101', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\102', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\160', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\130', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\115', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\114', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\106', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\166', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\170', '\113', '\040', '\142', '\145', '\040', '\061', + '\012', '\102', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\160', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\112', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\113', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\112', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\132', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\156', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\170', '\131', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\125', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\111', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\132', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\160', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\155', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\146', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\127', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\142', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\104', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\144', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\167', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\172', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\105', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\170', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\130', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\103', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\147', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\125', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\123', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\153', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\112', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\162', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\126', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\171', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\112', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\170', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\146', '\131', '\040', '\146', '\157', '\040', '\061', + '\012', '\130', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\147', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\171', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\142', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\163', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\154', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\160', '\121', '\040', '\160', '\157', '\040', '\061', + '\012', '\161', '\112', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\150', '\131', '\151', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\116', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\113', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\110', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\125', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\170', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\111', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\124', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\146', '\120', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\170', '\111', '\040', '\146', '\157', '\040', '\061', + '\012', '\166', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\167', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\141', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\170', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\160', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\160', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\116', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\125', '\171', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\146', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\163', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\130', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\155', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\121', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\117', '\141', '\171', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\125', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\103', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\111', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\121', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\112', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\102', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\126', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\106', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\165', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\167', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\144', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\161', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\127', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\163', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\114', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\144', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\147', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\131', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\132', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\146', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\166', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\126', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\115', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\121', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\125', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\107', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\166', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\162', '\101', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\171', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\114', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\105', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\110', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\120', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\111', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\103', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\104', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\107', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\162', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\104', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\121', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\144', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\121', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\121', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\167', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\144', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\156', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\151', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\113', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\153', '\105', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\132', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\142', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\142', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\160', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\141', '\112', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\146', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\103', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\170', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\107', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\152', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\146', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\160', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\132', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\105', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\131', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\122', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\123', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\112', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\103', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\166', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\167', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\155', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\124', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\153', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\111', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\163', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\123', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\155', '\111', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\127', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\113', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\157', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\167', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\153', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\167', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\121', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\125', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\113', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\121', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\126', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\115', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\142', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\132', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\167', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\162', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\167', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\167', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\107', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\132', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\106', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\166', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\111', '\171', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\104', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\131', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\162', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\105', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\107', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\107', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\104', '\155', '\040', '\160', '\157', '\040', '\061', + '\012', '\161', '\155', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\126', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\105', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\110', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\141', '\161', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\120', '\166', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\104', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\124', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\132', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\146', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\153', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\123', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\104', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\112', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\114', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\106', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\166', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\131', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\116', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\112', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\146', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\155', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\107', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\111', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\102', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\171', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\167', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\120', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\124', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\160', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\132', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\142', '\101', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\124', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\170', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\116', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\115', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\117', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\170', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\163', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\152', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\105', '\157', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\167', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\152', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\124', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\146', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\143', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\167', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\151', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\121', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\106', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\131', '\145', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\167', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\110', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\170', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\111', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\146', '\105', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\122', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\170', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\103', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\170', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\103', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\114', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\170', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\120', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\131', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\124', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\126', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\167', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\105', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\160', '\120', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\152', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\171', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\117', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\147', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\115', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\167', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\111', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\146', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\163', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\125', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\171', '\110', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\167', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\103', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\144', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\124', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\171', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\127', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\115', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\110', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\126', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\172', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\115', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\155', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\160', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\102', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\160', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\130', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\121', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\142', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\160', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\130', '\163', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\153', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\155', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\167', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\142', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\160', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\122', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\171', '\104', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\120', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\142', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\170', '\121', '\040', '\170', '\145', '\040', '\061', + '\012', '\104', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\153', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\155', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\162', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\167', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\121', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\155', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\172', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\131', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\170', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\125', '\170', '\040', '\170', '\145', '\040', '\061', + '\012', '\167', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\131', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\126', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\131', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\164', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\114', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\153', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\127', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\147', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\120', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\161', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\157', '\125', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\167', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\155', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\167', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\113', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\154', '\122', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\110', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\112', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\155', '\111', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\103', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\146', '\131', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\142', '\131', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\110', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\152', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\131', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\170', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\152', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\106', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\123', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\104', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\162', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\131', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\144', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\153', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\126', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\106', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\127', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\160', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\116', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\113', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\132', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\152', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\152', '\131', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\164', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\110', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\147', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\117', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\113', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\123', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\111', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\113', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\146', '\103', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\113', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\123', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\121', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\151', '\107', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\120', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\153', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\110', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\130', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\126', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\170', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\121', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\166', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\110', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\170', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\112', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\115', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\103', '\171', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\130', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\130', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\111', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\152', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\170', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\104', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\130', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\152', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\172', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\147', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\156', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\126', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\142', '\111', '\040', '\142', '\145', '\040', '\061', + '\012', '\132', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\146', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\123', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\141', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\130', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\123', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\144', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\124', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\164', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\157', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\113', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\121', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\114', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\107', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\155', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\144', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\130', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\104', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\115', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\112', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\144', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\103', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\130', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\161', '\124', '\040', '\165', '\156', '\040', '\061', + '\012', '\102', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\102', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\130', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\142', '\106', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\164', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\126', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\120', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\157', '\110', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\130', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\167', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\132', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\125', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\103', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\142', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\107', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\167', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\172', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\172', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\170', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\155', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\152', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\104', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\146', '\121', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\170', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\170', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\121', '\156', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\104', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\106', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\142', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\110', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\126', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\117', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\127', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\167', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\156', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\121', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\122', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\113', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\152', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\160', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\153', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\142', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\161', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\120', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\107', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\111', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\125', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\161', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\146', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\146', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\106', '\143', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\130', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\161', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\102', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\124', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\144', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\152', '\112', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\132', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\172', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\146', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\125', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\170', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\154', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\104', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\166', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\162', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\171', '\131', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\120', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\167', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\171', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\124', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\102', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\113', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\126', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\101', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\166', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\150', '\155', '\040', '\155', '\141', '\040', '\061', + '\012', '\132', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\166', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\144', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\131', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\130', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\126', '\170', '\040', '\166', '\151', '\040', '\061', + '\012', '\122', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\104', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\171', '\166', '\040', '\166', '\151', '\040', '\061', + '\012', '\103', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\122', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\121', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\142', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\167', '\123', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\166', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\107', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\102', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\131', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\105', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\163', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\166', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\153', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\151', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\101', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\126', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\170', '\124', '\040', '\142', '\145', '\040', '\061', + '\012', '\121', '\150', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\154', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\142', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\145', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\124', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\167', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\106', '\161', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\114', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\163', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\104', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\170', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\112', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\103', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\104', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\121', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\110', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\115', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\117', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\146', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\115', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\131', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\153', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\131', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\161', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\107', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\113', '\147', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\111', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\104', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\146', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\146', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\124', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\125', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\171', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\124', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\170', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\131', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\115', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\115', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\110', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\146', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\147', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\167', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\150', '\143', '\040', '\151', '\143', '\040', '\061', + '\012', '\170', '\111', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\147', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\170', '\116', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\101', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\103', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\157', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\130', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\121', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\127', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\130', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\127', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\145', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\112', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\155', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\154', '\122', '\040', '\151', '\163', '\040', '\061', + '\012', '\125', '\141', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\142', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\126', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\167', '\125', '\040', '\151', '\156', '\040', '\061', + '\012', '\103', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\130', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\107', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\142', '\131', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\172', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\127', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\170', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\121', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\166', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\124', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\167', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\146', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\145', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\102', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\123', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\171', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\152', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\164', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\127', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\170', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\110', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\102', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\166', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\155', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\121', '\167', '\040', '\167', '\151', '\040', '\061', + '\012', '\170', '\141', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\146', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\107', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\166', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\167', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\155', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\112', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\110', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\121', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\102', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\160', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\144', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\101', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\104', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\132', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\102', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\121', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\157', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\131', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\163', '\104', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\122', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\104', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\146', '\170', '\101', '\040', '\146', '\157', '\040', '\061', + '\012', '\170', '\120', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\112', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\160', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\170', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\114', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\123', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\125', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\127', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\131', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\152', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\167', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\120', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\101', '\157', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\166', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\117', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\155', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\153', '\111', '\040', '\153', '\165', '\040', '\061', + '\012', '\160', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\104', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\122', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\165', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\105', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\146', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\165', '\112', '\040', '\165', '\156', '\040', '\061', + '\012', '\156', '\122', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\170', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\146', '\132', '\040', '\156', '\171', '\040', '\061', + '\012', '\157', '\161', '\124', '\040', '\150', '\157', '\040', '\061', + '\012', '\143', '\147', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\142', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\130', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\146', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\167', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\103', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\172', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\147', '\152', '\040', '\152', '\157', '\040', '\061', + '\012', '\107', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\107', '\040', '\146', '\157', '\040', '\061', + '\012', '\155', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\144', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\110', '\153', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\104', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\106', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\142', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\166', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\144', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\155', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\130', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\117', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\172', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\160', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\132', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\106', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\156', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\130', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\142', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\120', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\132', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\160', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\153', '\117', '\040', '\153', '\157', '\040', '\061', + '\012', '\147', '\163', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\167', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\165', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\127', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\170', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\126', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\152', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\110', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\101', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\146', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\115', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\167', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\142', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\152', '\132', '\040', '\157', '\156', '\040', '\061', + '\012', '\163', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\170', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\171', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\105', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\117', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\124', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\127', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\110', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\167', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\103', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\167', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\111', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\107', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\152', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\171', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\170', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\166', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\154', '\162', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\103', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\114', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\166', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\142', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\131', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\146', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\146', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\102', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\171', '\112', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\155', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\130', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\131', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\170', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\116', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\172', '\125', '\040', '\146', '\157', '\040', '\061', + '\012', '\122', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\115', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\167', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\155', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\125', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\123', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\153', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\102', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\112', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\124', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\130', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\102', '\040', '\157', '\167', '\040', '\061', + '\012', '\153', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\146', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\132', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\155', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\114', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\113', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\161', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\146', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\122', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\116', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\144', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\160', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\127', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\101', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\162', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\155', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\114', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\167', '\102', '\040', '\151', '\156', '\040', '\061', + '\012', '\145', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\157', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\142', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\160', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\172', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\127', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\152', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\167', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\127', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\162', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\171', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\123', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\153', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\167', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\107', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\102', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\127', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\155', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\107', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\112', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\152', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\116', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\104', '\144', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\162', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\113', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\161', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\102', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\165', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\104', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\147', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\144', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\171', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\117', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\121', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\110', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\121', '\153', '\040', '\153', '\157', '\040', '\061', + '\012', '\107', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\132', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\166', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\167', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\115', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\152', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\102', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\102', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\143', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\121', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\155', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\146', '\110', '\040', '\146', '\157', '\040', '\061', + '\012', '\160', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\127', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\132', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\116', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\142', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\126', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\112', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\147', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\170', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\166', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\155', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\116', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\113', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\122', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\154', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\121', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\155', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\142', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\126', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\146', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\126', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\113', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\124', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\166', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\107', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\166', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\160', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\172', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\170', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\115', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\152', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\146', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\152', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\125', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\127', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\107', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\167', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\171', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\155', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\130', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\167', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\111', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\107', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\156', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\155', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\121', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\113', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\125', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\123', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\146', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\116', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\124', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\102', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\167', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\121', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\104', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\127', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\152', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\103', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\171', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\127', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\156', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\102', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\111', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\132', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\116', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\106', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\127', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\142', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\163', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\146', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\126', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\124', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\147', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\165', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\146', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\142', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\132', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\103', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\147', '\165', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\110', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\145', '\121', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\154', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\107', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\170', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\121', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\117', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\142', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\112', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\155', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\143', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\167', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\132', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\162', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\130', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\167', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\111', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\165', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\104', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\121', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\110', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\170', '\111', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\117', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\160', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\155', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\127', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\107', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\123', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\122', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\167', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\160', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\155', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\110', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\146', '\113', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\147', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\124', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\172', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\164', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\130', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\172', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\146', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\163', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\103', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\106', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\151', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\123', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\104', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\110', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\144', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\156', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\146', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\171', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\112', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\103', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\113', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\167', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\110', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\142', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\125', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\171', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\160', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\152', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\167', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\126', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\163', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\156', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\160', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\104', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\120', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\151', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\153', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\165', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\157', '\110', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\167', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\102', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\157', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\170', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\112', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\115', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\170', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\172', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\160', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\124', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\163', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\167', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\130', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\146', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\123', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\113', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\127', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\162', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\155', '\107', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\143', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\123', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\145', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\107', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\154', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\155', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\154', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\167', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\102', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\102', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\130', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\107', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\130', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\157', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\151', '\126', '\164', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\103', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\121', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\103', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\166', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\120', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\146', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\142', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\123', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\122', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\143', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\170', '\112', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\131', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\127', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\107', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\172', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\147', '\126', '\040', '\147', '\151', '\040', '\061', + '\012', '\122', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\106', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\121', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\116', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\146', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\102', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\147', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\123', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\103', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\166', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\141', '\104', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\107', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\152', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\124', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\115', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\142', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\132', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\121', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\153', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\171', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\146', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\160', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\113', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\154', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\132', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\122', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\160', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\167', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\107', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\122', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\153', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\125', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\152', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\113', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\160', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\125', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\152', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\150', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\152', '\161', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\171', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\106', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\105', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\120', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\146', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\115', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\113', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\110', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\106', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\160', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\116', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\104', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\160', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\156', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\146', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\172', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\102', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\143', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\115', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\112', '\152', '\040', '\162', '\151', '\040', '\061', + '\012', '\141', '\130', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\163', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\121', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\113', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\166', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\107', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\103', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\122', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\151', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\106', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\105', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\172', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\104', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\170', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\107', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\166', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\160', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\170', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\132', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\157', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\115', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\107', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\116', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\143', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\111', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\122', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\162', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\171', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\143', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\105', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\123', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\126', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\166', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\144', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\114', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\167', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\144', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\132', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\167', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\142', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\147', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\113', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\112', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\160', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\161', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\125', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\124', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\126', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\110', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\126', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\154', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\162', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\166', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\145', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\116', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\172', '\142', '\040', '\142', '\151', '\040', '\061', + '\012', '\127', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\143', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\126', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\123', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\161', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\104', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\171', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\145', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\161', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\124', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\155', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\106', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\156', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\127', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\172', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\121', '\145', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\154', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\107', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\147', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\115', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\123', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\147', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\114', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\166', '\132', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\146', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\103', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\132', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\111', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\132', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\170', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\144', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\131', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\171', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\107', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\106', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\166', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\167', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\166', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\122', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\172', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\160', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\120', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\171', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\155', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\154', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\105', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\101', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\172', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\117', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\144', '\163', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\166', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\120', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\132', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\146', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\146', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\154', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\126', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\155', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\144', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\155', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\121', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\150', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\104', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\144', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\166', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\171', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\112', '\146', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\141', '\102', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\127', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\107', '\142', '\040', '\142', '\151', '\040', '\061', + '\012', '\147', '\152', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\152', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\166', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\124', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\127', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\127', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\113', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\115', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\167', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\167', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\115', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\160', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\166', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\152', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\126', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\126', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\172', '\156', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\171', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\105', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\160', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\104', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\165', '\113', '\040', '\165', '\156', '\040', '\061', + '\012', '\166', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\127', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\114', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\160', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\114', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\160', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\104', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\167', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\102', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\107', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\146', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\154', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\146', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\121', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\107', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\102', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\104', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\115', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\146', '\115', '\040', '\156', '\171', '\040', '\061', + '\012', '\107', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\116', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\162', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\146', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\124', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\166', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\120', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\115', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\170', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\132', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\106', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\163', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\122', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\163', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\102', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\153', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\161', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\153', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\144', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\170', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\130', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\152', '\113', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\106', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\120', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\144', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\164', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\171', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\156', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\112', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\127', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\164', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\172', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\170', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\155', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\160', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\152', '\170', '\040', '\172', '\152', '\040', '\061', + '\012', '\164', '\166', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\104', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\171', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\166', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\120', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\167', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\160', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\104', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\105', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\143', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\104', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\127', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\151', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\106', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\146', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\123', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\155', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\106', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\120', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\161', '\121', '\040', '\164', '\151', '\040', '\061', + '\012', '\155', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\170', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\166', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\154', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\106', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\146', '\132', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\103', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\143', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\104', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\115', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\103', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\132', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\121', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\106', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\167', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\157', '\111', '\040', '\157', '\156', '\040', '\061', + '\012', '\163', '\112', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\164', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\126', '\146', '\040', '\147', '\151', '\040', '\061', + '\012', '\160', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\107', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\172', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\130', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\115', '\154', '\040', '\154', '\151', '\040', '\061', + '\012', '\142', '\131', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\154', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\120', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\164', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\121', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\144', '\143', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\125', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\112', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\115', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\167', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\164', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\172', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\172', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\102', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\163', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\125', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\163', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\130', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\154', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\126', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\101', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\155', '\153', '\040', '\155', '\107', '\040', '\061', + '\012', '\152', '\113', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\120', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\120', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\131', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\170', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\153', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\143', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\132', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\142', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\121', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\153', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\153', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\114', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\116', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\145', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\132', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\166', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\103', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\172', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\154', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\106', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\115', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\146', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\110', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\170', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\112', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\172', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\127', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\162', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\170', '\132', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\130', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\151', '\107', '\153', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\105', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\121', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\112', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\156', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\145', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\144', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\127', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\154', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\102', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\146', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\115', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\117', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\104', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\155', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\163', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\163', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\130', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\145', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\106', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\130', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\101', '\157', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\153', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\157', '\103', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\131', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\116', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\156', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\167', '\112', '\040', '\143', '\155', '\040', '\061', + '\012', '\161', '\112', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\116', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\167', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\102', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\114', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\162', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\102', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\172', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\152', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\170', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\146', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\121', '\155', '\040', '\121', '\117', '\040', '\061', + '\012', '\172', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\142', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\125', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\131', '\151', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\116', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\132', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\107', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\103', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\167', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\142', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\105', '\161', '\164', '\040', '\145', '\161', '\040', '\061', + '\012', '\130', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\125', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\170', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\117', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\103', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\147', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\163', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\107', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\153', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\122', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\167', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\112', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\126', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\131', '\154', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\104', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\113', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\120', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\146', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\146', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\113', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\111', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\165', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\113', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\132', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\160', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\146', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\152', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\130', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\167', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\131', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\161', '\131', '\040', '\145', '\161', '\040', '\061', + '\012', '\165', '\111', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\107', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\167', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\127', '\040', '\150', '\127', '\040', '\061', + '\012', '\151', '\171', '\107', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\145', '\106', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\152', '\151', '\040', '\152', '\123', '\040', '\061', + '\012', '\155', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\110', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\127', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\123', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\172', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\160', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\166', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\131', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\107', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\122', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\124', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\132', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\117', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\152', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\120', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\167', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\167', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\160', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\124', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\117', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\160', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\131', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\152', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\172', '\156', '\040', '\114', '\107', '\040', '\061', + '\012', '\131', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\131', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\130', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\111', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\112', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\170', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\104', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\163', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\152', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\125', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\102', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\142', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\154', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\103', '\160', '\040', '\107', '\103', '\040', '\061', + '\012', '\156', '\126', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\142', '\107', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\144', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\125', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\132', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\122', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\167', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\167', '\123', '\040', '\163', '\164', '\040', '\061', + '\012', '\105', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\105', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\153', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\152', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\130', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\166', '\131', '\040', '\166', '\113', '\040', '\061', + '\012', '\154', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\170', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\126', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\113', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\167', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\160', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\147', '\145', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\104', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\160', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\155', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\125', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\127', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\162', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\132', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\107', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\167', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\160', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\115', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\160', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\104', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\113', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\143', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\170', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\120', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\155', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\170', '\103', '\040', '\107', '\103', '\040', '\061', + '\012', '\154', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\153', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\154', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\120', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\166', '\121', '\040', '\121', '\117', '\040', '\061', + '\012', '\152', '\107', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\115', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\117', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\144', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\166', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\142', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\146', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\160', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\170', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\132', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\107', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\120', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\107', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\142', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\152', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\123', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\111', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\111', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\160', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\125', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\125', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\167', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\110', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\152', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\120', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\162', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\131', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\117', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\126', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\106', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\166', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\142', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\120', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\121', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\105', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\163', '\102', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\155', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\164', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\167', '\111', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\160', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\130', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\131', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\106', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\102', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\103', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\162', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\113', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\116', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\122', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\141', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\141', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\170', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\125', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\117', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\132', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\105', '\161', '\040', '\166', '\113', '\040', '\061', + '\012', '\130', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\120', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\143', '\132', '\040', '\143', '\155', '\040', '\061', + '\012', '\154', '\104', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\167', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\162', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\115', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\146', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\144', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\120', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\172', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\106', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\110', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\145', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\153', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\132', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\124', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\110', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\154', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\142', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\142', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\165', '\106', '\040', '\165', '\156', '\040', '\061', + '\012', '\161', '\172', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\141', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\141', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\157', '\103', '\161', '\040', '\107', '\103', '\040', '\061', + '\012', '\161', '\121', '\150', '\040', '\121', '\117', '\040', '\061', + '\012', '\143', '\167', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\115', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\113', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\113', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\161', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\107', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\115', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\132', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\171', '\105', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\125', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\126', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\115', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\127', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\132', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\157', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\122', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\120', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\145', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\161', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\166', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\162', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\104', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\145', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\172', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\172', '\143', '\040', '\143', '\155', '\040', '\061', + '\012', '\170', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\167', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\170', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\126', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\147', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\152', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\132', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\154', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\160', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\121', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\122', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\160', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\126', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\121', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\121', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\162', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\161', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\112', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\170', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\146', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\126', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\102', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\130', '\154', '\040', '\130', '\155', '\040', '\061', + '\012', '\150', '\167', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\162', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\154', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\117', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\103', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\106', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\127', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\123', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\102', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\166', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\146', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\113', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\153', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\111', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\142', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\110', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\126', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\162', '\113', '\040', '\110', '\113', '\040', '\061', + '\012', '\127', '\170', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\142', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\152', '\143', '\040', '\152', '\123', '\040', '\061', + '\012', '\152', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\166', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\171', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\146', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\142', '\114', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\166', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\114', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\120', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\170', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\146', '\125', '\040', '\146', '\157', '\040', '\061', + '\012', '\151', '\116', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\120', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\151', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\122', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\142', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\170', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\115', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\170', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\142', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\141', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\155', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\151', '\117', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\117', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\146', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\121', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\131', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\164', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\123', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\146', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\123', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\123', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\103', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\167', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\102', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\163', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\132', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\167', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\115', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\163', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\170', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\126', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\162', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\142', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\124', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\162', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\112', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\132', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\125', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\106', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\142', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\115', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\123', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\113', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\170', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\152', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\162', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\166', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\170', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\166', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\106', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\163', '\146', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\111', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\170', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\110', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\130', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\127', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\145', '\113', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\142', '\126', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\130', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\166', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\142', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\125', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\155', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\170', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\152', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\155', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\161', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\112', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\122', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\126', '\143', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\155', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\121', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\146', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\142', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\106', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\127', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\110', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\170', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\155', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\172', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\155', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\166', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\121', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\110', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\110', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\115', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\167', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\146', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\106', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\130', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\126', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\110', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\107', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\172', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\164', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\167', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\162', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\155', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\155', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\151', '\131', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\124', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\160', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\150', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\117', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\146', '\106', '\040', '\146', '\157', '\040', '\061', + '\012', '\143', '\124', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\160', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\147', '\167', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\103', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\124', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\155', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\170', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\101', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\110', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\157', '\111', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\104', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\151', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\170', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\155', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\155', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\126', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\146', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\171', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\155', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\121', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\106', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\167', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\127', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\112', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\157', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\171', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\152', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\142', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\131', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\120', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\103', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\170', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\132', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\142', '\111', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\101', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\167', '\146', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\146', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\165', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\161', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\154', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\162', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\170', '\127', '\040', '\146', '\157', '\040', '\061', + '\012', '\123', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\166', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\116', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\130', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\104', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\113', '\172', '\040', '\165', '\163', '\040', '\061', + '\012', '\161', '\152', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\153', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\106', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\115', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\121', '\145', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\131', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\142', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\126', '\153', '\040', '\156', '\144', '\040', '\061', + '\012', '\142', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\161', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\123', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\130', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\112', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\127', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\142', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\167', '\120', '\040', '\151', '\156', '\040', '\061', + '\012', '\154', '\127', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\110', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\143', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\115', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\171', '\131', '\040', '\156', '\171', '\040', '\061', + '\012', '\141', '\106', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\171', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\142', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\152', '\101', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\155', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\157', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\170', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\122', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\116', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\151', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\131', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\170', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\152', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\103', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\102', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\131', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\110', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\166', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\121', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\142', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\170', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\170', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\155', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\102', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\110', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\126', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\146', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\166', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\115', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\156', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\121', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\157', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\153', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\110', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\167', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\160', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\125', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\170', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\126', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\112', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\102', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\155', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\113', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\103', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\125', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\115', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\107', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\110', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\132', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\166', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\154', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\155', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\106', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\166', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\161', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\166', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\143', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\153', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\105', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\167', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\147', '\130', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\123', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\167', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\172', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\162', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\110', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\142', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\146', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\167', '\102', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\155', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\172', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\126', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\167', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\111', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\160', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\167', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\153', '\101', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\113', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\170', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\141', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\146', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\132', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\130', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\104', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\146', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\141', '\121', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\152', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\113', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\160', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\115', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\167', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\102', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\101', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\123', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\142', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\157', '\124', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\111', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\172', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\172', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\121', '\040', '\153', '\165', '\040', '\061', + '\012', '\154', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\167', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\122', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\117', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\165', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\132', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\142', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\104', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\167', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\117', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\167', '\111', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\152', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\117', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\146', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\161', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\104', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\155', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\165', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\146', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\146', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\142', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\170', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\130', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\125', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\166', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\112', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\142', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\146', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\107', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\117', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\152', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\120', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\160', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\163', '\102', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\170', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\106', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\157', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\155', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\142', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\106', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\171', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\152', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\131', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\165', '\112', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\145', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\162', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\155', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\166', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\172', '\117', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\121', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\120', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\127', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\103', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\145', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\132', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\103', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\166', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\172', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\127', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\113', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\143', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\103', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\101', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\125', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\115', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\125', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\132', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\164', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\122', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\102', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\147', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\127', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\167', '\120', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\111', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\114', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\151', '\130', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\163', '\114', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\105', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\107', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\125', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\155', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\153', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\167', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\155', '\123', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\171', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\142', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\127', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\167', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\104', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\123', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\162', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\107', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\162', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\154', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\142', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\116', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\121', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\126', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\160', '\120', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\153', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\106', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\121', '\157', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\154', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\155', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\127', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\155', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\114', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\154', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\161', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\147', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\107', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\167', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\155', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\142', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\153', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\117', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\146', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\146', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\116', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\104', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\142', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\153', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\101', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\106', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\167', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\125', '\157', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\104', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\107', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\122', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\142', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\146', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\104', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\106', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\152', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\126', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\166', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\116', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\122', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\107', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\132', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\115', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\120', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\172', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\172', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\103', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\167', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\153', '\115', '\040', '\153', '\157', '\040', '\061', + '\012', '\166', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\120', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\112', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\115', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\116', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\162', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\163', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\144', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\127', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\167', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\152', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\113', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\160', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\164', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\171', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\113', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\126', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\120', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\106', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\170', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\131', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\167', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\121', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\142', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\131', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\170', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\160', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\142', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\121', '\171', '\040', '\164', '\157', '\040', '\061', + '\012', '\166', '\170', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\160', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\120', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\110', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\146', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\103', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\147', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\106', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\104', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\111', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\164', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\122', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\152', '\124', '\040', '\162', '\157', '\040', '\061', + '\012', '\162', '\152', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\101', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\114', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\123', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\126', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\105', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\107', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\117', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\120', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\163', '\113', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\103', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\116', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\124', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\106', '\166', '\040', '\162', '\157', '\040', '\061', + '\012', '\122', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\161', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\155', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\165', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\147', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\144', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\101', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\167', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\145', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\141', '\106', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\146', '\102', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\160', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\107', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\127', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\172', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\163', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\121', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\146', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\142', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\113', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\130', '\141', '\040', '\141', '\162', '\040', '\061', + '\012', '\167', '\152', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\127', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\152', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\122', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\147', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\172', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\145', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\155', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\107', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\113', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\127', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\113', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\171', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\112', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\107', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\123', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\106', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\144', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\110', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\122', '\156', '\040', '\141', '\162', '\040', '\061', + '\012', '\130', '\154', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\116', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\106', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\116', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\120', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\172', '\112', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\123', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\127', '\147', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\114', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\111', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\157', '\113', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\114', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\110', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\170', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\127', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\107', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\152', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\146', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\132', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\157', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\170', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\131', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\132', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\154', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\121', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\142', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\105', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\116', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\162', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\170', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\121', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\142', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\147', '\115', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\145', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\126', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\126', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\115', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\124', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\120', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\103', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\166', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\115', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\152', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\155', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\154', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\167', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\120', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\122', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\170', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\110', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\113', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\131', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\170', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\157', '\153', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\160', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\104', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\147', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\113', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\167', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\104', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\147', '\146', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\125', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\153', '\106', '\040', '\153', '\157', '\040', '\061', + '\012', '\120', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\123', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\146', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\167', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\114', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\112', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\122', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\130', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\114', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\115', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\166', '\107', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\164', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\170', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\143', '\117', '\040', '\152', '\141', '\040', '\061', + '\012', '\163', '\103', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\102', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\117', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\112', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\152', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\103', '\167', '\040', '\155', '\141', '\040', '\061', + '\012', '\150', '\170', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\124', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\144', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\166', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\151', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\167', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\164', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\111', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\131', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\146', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\121', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\130', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\105', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\120', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\142', '\127', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\110', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\130', '\157', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\170', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\127', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\132', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\117', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\124', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\162', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\116', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\105', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\112', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\110', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\160', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\170', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\154', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\113', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\166', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\170', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\126', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\121', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\130', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\150', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\153', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\150', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\104', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\114', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\124', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\144', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\110', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\104', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\120', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\110', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\160', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\120', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\116', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\171', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\123', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\121', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\146', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\107', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\106', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\113', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\121', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\116', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\165', '\130', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\146', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\111', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\153', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\165', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\120', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\113', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\114', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\120', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\160', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\170', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\143', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\126', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\155', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\152', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\172', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\164', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\113', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\130', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\147', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\160', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\127', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\170', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\172', '\115', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\142', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\160', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\145', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\146', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\170', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\102', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\164', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\102', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\127', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\123', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\162', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\112', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\130', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\156', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\126', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\111', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\126', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\122', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\150', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\124', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\160', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\130', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\106', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\111', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\167', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\106', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\127', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\105', '\141', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\146', '\105', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\162', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\120', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\120', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\103', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\132', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\127', '\154', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\125', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\142', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\142', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\170', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\142', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\152', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\130', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\116', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\112', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\104', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\125', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\127', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\120', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\116', '\142', '\040', '\153', '\157', '\040', '\061', + '\012', '\127', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\112', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\155', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\124', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\155', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\121', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\120', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\170', '\172', '\040', '\172', '\145', '\040', '\061', + '\012', '\104', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\115', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\115', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\112', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\146', '\116', '\040', '\146', '\157', '\040', '\061', + '\012', '\144', '\121', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\165', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\120', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\146', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\167', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\141', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\155', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\146', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\152', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\162', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\170', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\170', '\110', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\122', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\152', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\123', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\155', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\146', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\112', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\167', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\122', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\161', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\172', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\154', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\142', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\160', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\124', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\120', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\171', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\102', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\111', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\104', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\112', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\101', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\106', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\104', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\144', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\167', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\131', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\155', '\107', '\040', '\155', '\145', '\040', '\061', + '\012', '\113', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\164', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\112', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\167', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\126', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\115', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\170', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\112', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\146', '\123', '\040', '\156', '\171', '\040', '\061', + '\012', '\115', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\163', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\112', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\167', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\170', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\112', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\107', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\123', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\105', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\161', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\112', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\127', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\166', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\152', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\153', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\166', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\171', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\116', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\114', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\126', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\117', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\170', '\114', '\040', '\146', '\157', '\040', '\061', + '\012', '\163', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\124', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\126', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\132', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\126', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\123', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\116', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\146', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\143', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\124', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\141', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\152', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\115', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\164', '\102', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\146', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\170', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\132', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\103', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\160', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\153', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\113', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\160', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\116', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\160', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\130', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\146', '\113', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\160', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\165', '\167', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\156', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\166', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\107', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\132', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\142', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\166', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\167', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\146', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\103', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\127', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\166', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\167', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\107', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\166', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\122', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\121', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\104', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\125', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\130', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\130', '\171', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\142', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\131', '\144', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\157', '\130', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\147', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\121', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\126', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\171', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\102', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\170', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\120', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\127', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\142', '\120', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\101', '\157', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\146', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\163', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\115', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\122', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\152', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\117', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\167', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\114', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\120', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\103', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\104', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\106', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\160', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\155', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\103', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\106', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\160', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\146', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\132', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\122', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\104', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\120', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\166', '\120', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\153', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\121', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\170', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\107', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\152', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\151', '\106', '\040', '\164', '\151', '\040', '\061', + '\012', '\132', '\172', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\131', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\152', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\167', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\153', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\171', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\151', '\171', '\102', '\040', '\151', '\156', '\040', '\061', + '\012', '\150', '\121', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\160', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\125', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\153', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\160', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\167', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\121', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\104', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\144', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\105', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\141', '\166', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\172', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\110', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\106', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\102', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\122', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\154', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\172', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\131', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\155', '\122', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\130', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\171', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\130', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\112', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\172', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\152', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\161', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\167', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\152', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\122', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\144', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\101', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\116', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\116', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\155', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\107', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\110', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\167', '\106', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\120', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\104', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\121', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\111', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\103', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\106', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\112', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\151', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\122', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\116', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\120', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\113', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\104', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\106', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\126', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\123', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\167', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\157', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\102', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\107', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\132', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\142', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\132', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\172', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\104', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\146', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\161', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\123', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\170', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\130', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\142', '\124', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\113', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\124', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\125', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\104', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\125', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\171', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\103', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\130', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\120', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\146', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\126', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\104', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\146', '\107', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\130', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\147', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\170', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\141', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\130', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\166', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\121', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\166', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\170', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\112', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\123', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\103', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\107', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\116', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\110', '\153', '\040', '\157', '\156', '\040', '\061', + '\012', '\127', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\166', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\153', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\157', '\132', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\107', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\103', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\130', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\113', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\105', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\123', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\171', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\121', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\127', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\155', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\144', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\162', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\102', '\160', '\040', '\160', '\157', '\040', '\061', + '\012', '\146', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\145', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\107', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\146', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\123', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\121', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\160', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\115', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\141', '\161', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\127', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\171', '\112', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\131', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\162', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\113', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\113', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\172', '\163', '\124', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\111', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\146', '\125', '\040', '\146', '\157', '\040', '\061', + '\012', '\127', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\127', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\116', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\146', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\162', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\126', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\171', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\163', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\103', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\167', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\146', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\105', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\117', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\160', '\123', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\111', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\166', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\146', '\113', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\115', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\102', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\163', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\155', '\111', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\155', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\116', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\165', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\152', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\117', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\107', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\110', '\160', '\040', '\141', '\154', '\040', '\061', + '\012', '\161', '\147', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\142', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\161', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\153', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\104', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\121', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\112', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\115', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\152', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\153', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\157', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\152', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\106', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\152', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\156', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\112', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\132', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\167', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\111', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\102', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\167', '\106', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\110', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\123', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\113', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\102', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\155', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\131', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\107', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\170', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\154', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\154', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\142', '\104', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\101', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\110', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\170', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\126', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\160', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\151', '\171', '\120', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\155', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\155', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\102', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\105', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\103', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\172', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\111', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\131', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\161', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\155', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\132', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\123', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\113', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\127', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\166', '\102', '\040', '\160', '\157', '\040', '\061', + '\012', '\164', '\147', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\125', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\132', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\144', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\157', '\111', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\112', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\170', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\111', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\171', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\142', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\153', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\145', '\157', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\130', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\106', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\112', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\115', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\156', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\161', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\103', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\117', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\154', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\125', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\126', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\155', '\115', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\142', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\155', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\106', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\123', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\155', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\156', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\171', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\172', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\147', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\126', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\127', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\164', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\111', '\157', '\040', '\150', '\157', '\040', '\061', + '\012', '\153', '\146', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\112', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\145', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\165', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\130', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\172', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\153', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\105', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\167', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\104', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\167', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\130', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\146', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\116', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\154', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\125', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\124', '\144', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\131', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\143', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\102', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\131', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\114', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\156', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\106', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\120', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\116', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\154', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\114', '\170', '\040', '\152', '\157', '\040', '\061', + '\012', '\152', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\167', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\107', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\164', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\156', '\127', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\111', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\117', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\142', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\172', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\127', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\170', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\160', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\163', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\111', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\107', '\153', '\040', '\157', '\156', '\040', '\061', + '\012', '\110', '\156', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\122', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\107', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\131', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\121', '\157', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\116', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\144', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\107', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\162', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\166', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\104', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\120', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\147', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\127', '\167', '\040', '\157', '\167', '\040', '\061', + '\012', '\155', '\112', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\130', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\131', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\110', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\106', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\163', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\101', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\154', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\171', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\126', '\162', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\102', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\161', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\153', '\163', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\120', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\146', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\112', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\127', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\120', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\170', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\120', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\162', '\152', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\167', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\102', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\160', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\155', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\131', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\142', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\171', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\102', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\107', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\170', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\142', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\153', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\127', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\117', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\163', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\157', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\167', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\164', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\162', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\126', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\164', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\110', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\147', '\163', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\154', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\152', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\166', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\111', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\131', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\126', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\155', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\152', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\121', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\116', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\146', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\102', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\112', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\107', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\132', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\127', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\163', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\170', '\123', '\040', '\156', '\171', '\040', '\061', + '\012', '\162', '\170', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\116', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\116', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\160', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\130', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\154', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\152', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\112', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\156', '\126', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\172', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\167', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\161', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\116', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\165', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\111', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\115', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\127', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\115', '\143', '\040', '\156', '\144', '\040', '\061', + '\012', '\132', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\154', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\125', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\124', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\152', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\170', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\116', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\144', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\112', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\130', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\167', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\145', '\166', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\160', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\101', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\142', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\146', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\130', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\101', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\155', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\172', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\147', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\112', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\132', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\146', '\101', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\155', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\116', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\122', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\152', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\152', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\111', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\115', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\153', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\101', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\170', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\166', '\101', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\143', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\123', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\102', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\127', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\167', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\116', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\125', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\146', '\120', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\170', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\166', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\125', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\170', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\120', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\170', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\105', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\142', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\167', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\115', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\106', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\146', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\122', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\161', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\160', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\110', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\124', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\141', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\154', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\172', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\102', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\170', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\166', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\103', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\122', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\107', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\114', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\107', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\107', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\144', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\152', '\110', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\115', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\127', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\142', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\107', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\145', '\170', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\127', '\152', '\040', '\152', '\157', '\040', '\061', + '\012', '\160', '\121', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\117', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\162', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\130', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\127', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\161', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\112', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\112', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\166', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\120', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\111', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\146', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\132', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\155', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\103', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\106', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\142', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\121', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\121', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\131', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\120', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\117', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\116', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\110', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\144', '\105', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\126', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\120', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\122', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\157', '\105', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\156', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\166', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\157', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\155', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\104', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\104', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\153', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\170', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\124', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\163', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\146', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\142', '\107', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\127', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\104', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\127', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\130', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\166', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\166', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\104', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\155', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\101', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\146', '\123', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\171', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\121', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\132', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\105', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\121', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\160', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\132', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\122', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\151', '\127', '\040', '\151', '\156', '\040', '\061', + '\012', '\150', '\161', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\102', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\160', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\110', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\115', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\127', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\152', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\166', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\166', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\153', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\116', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\147', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\167', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\131', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\124', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\112', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\172', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\171', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\171', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\102', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\166', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\142', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\142', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\167', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\112', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\111', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\166', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\122', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\117', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\170', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\111', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\102', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\124', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\103', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\147', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\160', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\146', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\170', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\126', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\166', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\131', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\116', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\121', '\151', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\120', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\166', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\153', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\144', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\163', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\166', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\127', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\131', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\131', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\110', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\142', '\115', '\040', '\160', '\162', '\040', '\061', + '\012', '\110', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\161', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\127', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\156', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\115', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\113', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\157', '\126', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\117', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\154', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\146', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\113', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\151', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\123', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\143', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\120', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\155', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\170', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\162', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\120', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\161', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\103', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\155', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\167', '\120', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\166', '\107', '\040', '\166', '\145', '\040', '\061', + '\012', '\126', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\154', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\121', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\114', '\146', '\040', '\160', '\157', '\040', '\061', + '\012', '\170', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\146', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\151', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\163', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\162', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\122', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\172', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\142', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\153', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\106', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\110', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\162', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\116', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\107', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\105', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\126', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\167', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\111', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\152', '\166', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\123', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\170', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\110', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\126', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\125', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\170', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\144', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\146', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\142', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\172', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\110', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\170', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\155', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\155', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\172', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\155', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\130', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\142', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\160', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\155', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\131', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\113', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\144', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\130', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\166', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\151', '\103', '\040', '\151', '\156', '\040', '\061', + '\012', '\147', '\153', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\112', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\160', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\120', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\122', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\115', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\111', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\161', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\157', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\106', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\110', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\103', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\142', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\151', '\110', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\107', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\167', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\104', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\166', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\116', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\131', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\110', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\106', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\155', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\102', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\147', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\167', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\167', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\105', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\107', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\153', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\112', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\146', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\126', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\167', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\156', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\125', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\102', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\126', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\166', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\131', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\102', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\117', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\104', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\124', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\112', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\167', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\144', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\152', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\127', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\142', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\144', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\167', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\101', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\171', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\120', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\125', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\120', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\157', '\106', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\131', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\131', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\167', '\127', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\102', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\113', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\146', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\115', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\167', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\172', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\111', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\144', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\147', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\167', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\102', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\117', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\114', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\144', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\146', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\161', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\110', '\142', '\040', '\160', '\157', '\040', '\061', + '\012', '\166', '\122', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\160', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\127', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\142', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\132', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\102', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\152', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\166', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\167', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\102', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\170', '\110', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\170', '\110', '\040', '\146', '\157', '\040', '\061', + '\012', '\164', '\130', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\102', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\112', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\152', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\126', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\104', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\155', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\141', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\111', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\155', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\155', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\126', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\132', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\125', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\144', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\157', '\125', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\112', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\125', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\166', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\121', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\111', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\107', '\147', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\116', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\166', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\155', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\160', '\117', '\040', '\160', '\157', '\040', '\061', + '\012', '\164', '\105', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\146', '\114', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\131', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\127', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\124', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\126', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\157', '\112', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\104', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\106', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\154', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\142', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\102', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\160', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\116', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\123', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\157', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\113', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\154', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\144', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\147', '\161', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\150', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\165', '\131', '\040', '\165', '\156', '\040', '\061', + '\012', '\152', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\165', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\172', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\106', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\146', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\111', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\131', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\124', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\165', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\110', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\122', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\120', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\113', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\160', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\153', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\170', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\151', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\171', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\104', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\171', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\127', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\101', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\132', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\121', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\142', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\170', '\104', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\170', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\110', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\167', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\142', '\101', '\040', '\142', '\145', '\040', '\061', + '\012', '\122', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\123', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\121', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\155', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\156', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\163', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\130', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\124', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\160', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\131', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\105', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\111', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\116', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\117', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\130', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\106', '\155', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\153', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\146', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\142', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\166', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\155', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\106', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\106', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\106', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\142', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\155', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\106', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\104', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\106', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\107', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\164', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\172', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\115', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\167', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\142', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\125', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\152', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\104', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\142', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\123', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\167', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\115', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\152', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\120', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\170', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\131', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\162', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\143', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\112', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\125', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\130', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\104', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\170', '\107', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\117', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\147', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\160', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\116', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\121', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\106', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\104', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\163', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\132', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\170', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\121', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\113', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\170', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\167', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\107', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\166', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\126', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\155', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\144', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\112', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\150', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\164', '\121', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\171', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\132', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\113', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\152', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\116', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\112', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\160', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\167', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\145', '\112', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\162', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\154', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\103', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\121', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\114', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\167', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\121', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\146', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\122', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\125', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\160', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\127', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\102', '\161', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\127', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\146', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\127', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\162', '\166', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\142', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\156', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\106', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\155', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\156', '\111', '\040', '\156', '\164', '\040', '\061', + '\012', '\161', '\117', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\171', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\116', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\105', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\160', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\170', '\114', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\145', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\125', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\167', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\162', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\124', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\170', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\112', '\163', '\040', '\157', '\156', '\040', '\061', + '\012', '\163', '\122', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\121', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\144', '\116', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\170', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\160', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\132', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\132', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\127', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\125', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\162', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\155', '\116', '\040', '\155', '\145', '\040', '\061', + '\012', '\117', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\114', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\112', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\107', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\115', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\124', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\110', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\127', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\170', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\170', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\166', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\103', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\146', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\160', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\154', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\170', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\132', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\153', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\127', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\161', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\113', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\107', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\103', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\171', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\117', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\117', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\127', '\164', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\170', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\116', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\124', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\104', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\122', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\125', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\102', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\111', '\171', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\111', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\117', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\162', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\105', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\153', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\152', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\155', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\124', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\114', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\167', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\144', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\163', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\125', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\162', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\142', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\170', '\111', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\103', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\130', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\104', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\144', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\130', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\165', '\121', '\040', '\165', '\156', '\040', '\061', + '\012', '\153', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\127', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\103', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\131', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\110', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\126', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\141', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\170', '\127', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\156', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\106', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\167', '\111', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\154', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\103', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\162', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\131', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\154', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\114', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\143', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\162', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\152', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\112', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\114', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\121', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\150', '\153', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\147', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\113', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\112', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\156', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\153', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\127', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\164', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\145', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\162', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\104', '\160', '\040', '\160', '\157', '\040', '\061', + '\012', '\103', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\170', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\102', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\115', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\170', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\113', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\167', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\102', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\156', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\155', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\113', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\107', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\114', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\120', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\115', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\105', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\155', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\103', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\166', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\126', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\121', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\112', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\172', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\162', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\102', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\122', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\111', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\113', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\102', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\104', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\106', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\166', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\122', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\152', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\104', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\146', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\107', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\107', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\170', '\126', '\040', '\146', '\157', '\040', '\061', + '\012', '\151', '\120', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\147', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\111', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\150', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\104', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\164', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\106', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\152', '\123', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\160', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\104', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\113', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\132', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\170', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\124', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\105', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\110', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\161', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\170', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\124', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\121', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\163', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\107', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\113', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\167', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\167', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\162', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\160', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\106', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\171', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\167', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\147', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\164', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\161', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\155', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\121', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\105', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\166', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\166', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\112', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\171', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\164', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\132', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\105', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\146', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\113', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\170', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\124', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\107', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\127', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\162', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\160', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\121', '\163', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\142', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\126', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\114', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\162', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\163', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\167', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\120', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\162', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\146', '\104', '\040', '\146', '\157', '\040', '\061', + '\012', '\167', '\171', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\102', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\163', '\171', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\160', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\147', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\126', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\113', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\116', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\111', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\147', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\146', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\153', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\125', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\103', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\102', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\122', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\107', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\102', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\170', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\115', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\127', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\127', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\165', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\166', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\155', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\143', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\102', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\127', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\130', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\172', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\170', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\157', '\130', '\040', '\160', '\157', '\040', '\061', + '\012', '\170', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\127', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\127', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\123', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\146', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\152', '\144', '\040', '\144', '\157', '\040', '\061', + '\012', '\121', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\171', '\113', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\157', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\114', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\115', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\103', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\130', '\157', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\153', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\124', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\126', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\167', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\157', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\104', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\116', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\131', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\123', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\111', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\141', '\152', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\131', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\113', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\111', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\146', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\146', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\103', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\161', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\126', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\103', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\145', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\110', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\116', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\155', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\106', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\131', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\147', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\167', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\171', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\172', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\121', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\142', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\123', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\131', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\147', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\131', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\160', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\126', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\166', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\146', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\167', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\127', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\161', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\143', '\112', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\167', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\160', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\143', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\111', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\144', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\106', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\114', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\160', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\112', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\112', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\113', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\145', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\170', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\103', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\122', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\115', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\113', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\153', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\122', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\170', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\103', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\131', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\153', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\121', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\104', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\152', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\155', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\112', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\172', '\126', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\132', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\147', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\104', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\110', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\111', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\144', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\103', '\155', '\040', '\157', '\167', '\040', '\061', + '\012', '\166', '\126', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\142', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\170', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\147', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\127', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\131', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\130', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\151', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\102', '\160', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\130', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\172', '\160', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\143', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\167', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\117', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\117', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\163', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\122', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\104', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\146', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\166', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\127', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\172', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\160', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\121', '\145', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\131', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\121', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\166', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\125', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\142', '\110', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\166', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\106', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\160', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\114', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\167', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\171', '\113', '\040', '\156', '\171', '\040', '\061', + '\012', '\123', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\170', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\112', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\164', '\152', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\120', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\162', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\170', '\131', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\163', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\155', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\155', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\106', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\126', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\144', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\127', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\131', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\157', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\152', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\104', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\127', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\154', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\167', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\132', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\121', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\167', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\131', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\101', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\157', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\167', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\167', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\115', '\152', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\162', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\130', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\105', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\121', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\152', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\115', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\126', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\166', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\110', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\142', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\124', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\170', '\117', '\040', '\156', '\171', '\040', '\061', + '\012', '\106', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\106', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\104', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\110', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\110', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\106', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\171', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\105', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\144', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\161', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\110', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\126', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\110', '\167', '\040', '\165', '\156', '\040', '\061', + '\012', '\132', '\143', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\104', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\164', '\154', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\161', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\162', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\103', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\165', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\115', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\127', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\160', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\121', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\170', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\146', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\132', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\103', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\162', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\157', '\111', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\162', '\146', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\102', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\171', '\126', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\151', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\104', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\147', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\116', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\144', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\166', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\142', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\151', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\142', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\115', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\110', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\127', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\120', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\114', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\157', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\160', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\124', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\143', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\120', '\171', '\040', '\153', '\165', '\040', '\061', + '\012', '\146', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\131', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\123', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\104', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\142', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\146', '\117', '\040', '\156', '\171', '\040', '\061', + '\012', '\165', '\121', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\167', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\124', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\112', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\127', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\143', '\125', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\125', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\116', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\132', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\163', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\131', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\131', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\107', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\170', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\102', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\112', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\146', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\142', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\110', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\113', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\102', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\160', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\144', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\115', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\132', '\146', '\040', '\160', '\151', '\040', '\061', + '\012', '\145', '\131', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\124', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\104', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\146', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\160', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\103', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\120', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\103', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\116', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\122', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\141', '\145', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\102', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\117', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\142', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\130', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\147', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\130', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\116', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\141', '\117', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\146', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\167', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\152', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\115', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\106', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\146', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\132', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\102', '\142', '\040', '\142', '\151', '\040', '\061', + '\012', '\163', '\152', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\104', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\152', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\162', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\106', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\121', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\142', '\105', '\040', '\166', '\151', '\040', '\061', + '\012', '\125', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\123', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\105', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\146', '\110', '\040', '\142', '\145', '\040', '\061', + '\012', '\116', '\162', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\166', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\151', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\142', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\170', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\166', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\162', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\131', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\153', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\170', '\040', '\166', '\151', '\040', '\061', + '\012', '\132', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\127', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\104', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\166', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\157', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\142', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\116', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\124', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\152', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\112', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\153', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\120', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\143', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\152', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\171', '\105', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\102', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\120', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\147', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\166', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\162', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\166', '\146', '\040', '\166', '\151', '\040', '\061', + '\012', '\171', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\167', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\113', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\112', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\144', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\116', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\112', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\141', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\113', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\116', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\120', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\122', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\156', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\132', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\170', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\120', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\167', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\120', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\146', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\167', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\142', '\107', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\132', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\103', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\124', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\123', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\154', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\127', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\124', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\106', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\112', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\143', '\122', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\142', '\124', '\040', '\142', '\145', '\040', '\061', + '\012', '\106', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\167', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\142', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\123', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\154', '\142', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\117', '\143', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\142', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\163', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\117', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\155', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\103', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\152', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\106', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\123', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\142', '\113', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\106', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\103', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\110', '\172', '\040', '\164', '\172', '\040', '\061', + '\012', '\150', '\152', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\155', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\103', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\111', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\116', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\142', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\106', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\122', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\162', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\167', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\126', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\120', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\160', '\120', '\040', '\151', '\156', '\040', '\061', + '\012', '\112', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\112', '\142', '\040', '\142', '\151', '\040', '\061', + '\012', '\152', '\170', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\167', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\122', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\146', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\124', '\144', '\160', '\040', '\160', '\157', '\040', '\061', + '\012', '\167', '\105', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\161', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\170', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\125', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\121', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\124', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\121', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\167', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\153', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\121', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\114', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\101', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\155', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\113', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\125', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\172', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\116', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\146', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\132', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\130', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\104', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\144', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\170', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\146', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\117', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\116', '\166', '\170', '\040', '\166', '\151', '\040', '\061', + '\012', '\161', '\141', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\107', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\106', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\161', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\171', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\155', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\103', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\126', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\121', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\110', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\172', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\131', '\171', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\170', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\127', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\103', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\106', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\145', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\152', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\150', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\103', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\124', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\170', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\166', '\113', '\040', '\166', '\151', '\040', '\061', + '\012', '\114', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\167', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\124', '\153', '\040', '\144', '\151', '\040', '\061', + '\012', '\146', '\163', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\152', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\121', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\155', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\163', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\104', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\110', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\106', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\131', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\113', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\116', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\157', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\160', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\127', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\154', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\146', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\155', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\107', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\162', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\110', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\114', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\142', '\102', '\040', '\142', '\151', '\040', '\061', + '\012', '\151', '\131', '\162', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\104', '\172', '\040', '\164', '\172', '\040', '\061', + '\012', '\170', '\163', '\112', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\115', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\125', '\165', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\167', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\120', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\104', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\126', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\113', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\157', '\132', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\101', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\115', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\106', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\126', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\130', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\150', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\154', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\142', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\120', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\104', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\152', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\152', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\106', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\157', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\125', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\112', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\113', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\132', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\103', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\165', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\123', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\123', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\104', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\142', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\166', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\130', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\106', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\160', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\124', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\120', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\151', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\106', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\170', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\103', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\126', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\126', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\103', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\167', '\126', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\142', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\131', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\123', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\103', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\167', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\130', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\102', '\167', '\040', '\155', '\142', '\040', '\061', + '\012', '\167', '\155', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\112', '\170', '\040', '\170', '\145', '\040', '\061', + '\012', '\144', '\130', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\102', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\117', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\121', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\166', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\103', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\113', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\126', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\132', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\102', '\166', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\170', '\122', '\040', '\146', '\157', '\040', '\061', + '\012', '\166', '\155', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\120', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\106', '\153', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\171', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\125', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\155', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\142', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\131', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\106', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\160', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\170', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\115', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\162', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\107', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\143', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\113', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\142', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\131', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\113', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\160', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\103', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\122', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\111', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\116', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\166', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\107', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\102', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\115', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\103', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\166', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\170', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\103', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\124', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\142', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\152', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\131', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\166', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\157', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\126', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\110', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\142', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\110', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\153', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\155', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\142', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\122', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\102', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\130', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\162', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\166', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\104', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\146', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\132', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\145', '\116', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\170', '\123', '\040', '\146', '\157', '\040', '\061', + '\012', '\163', '\116', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\165', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\172', '\127', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\172', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\146', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\110', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\166', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\157', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\170', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\115', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\152', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\152', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\155', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\111', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\153', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\124', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\126', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\121', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\113', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\125', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\165', '\124', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\144', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\170', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\147', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\161', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\107', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\166', '\155', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\113', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\166', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\110', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\115', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\163', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\113', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\120', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\147', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\167', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\131', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\172', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\152', '\040', '\152', '\157', '\040', '\061', + '\012', '\113', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\160', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\144', '\101', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\123', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\142', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\162', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\125', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\105', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\152', '\110', '\040', '\152', '\157', '\040', '\061', + '\012', '\163', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\117', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\142', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\120', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\122', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\171', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\167', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\112', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\166', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\106', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\116', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\160', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\131', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\107', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\124', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\146', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\172', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\144', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\124', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\113', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\115', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\155', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\117', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\142', '\111', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\160', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\105', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\127', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\166', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\131', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\170', '\115', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\120', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\143', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\112', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\170', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\156', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\171', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\123', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\161', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\131', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\162', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\110', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\142', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\121', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\170', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\147', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\144', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\144', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\120', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\121', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\167', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\170', '\114', '\040', '\156', '\171', '\040', '\061', + '\012', '\131', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\111', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\121', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\161', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\146', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\124', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\146', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\113', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\130', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\131', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\157', '\143', '\040', '\162', '\157', '\040', '\061', + '\012', '\166', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\132', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\144', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\147', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\156', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\146', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\142', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\142', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\160', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\157', '\127', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\112', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\121', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\166', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\104', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\167', '\106', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\113', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\111', '\165', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\112', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\147', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\150', '\040', '\150', '\157', '\040', '\061', + '\012', '\143', '\166', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\147', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\116', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\132', '\156', '\040', '\157', '\156', '\040', '\061', + '\012', '\165', '\125', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\132', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\107', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\123', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\143', '\107', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\142', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\145', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\167', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\120', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\123', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\120', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\172', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\143', '\110', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\166', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\166', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\122', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\116', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\113', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\112', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\117', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\103', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\141', '\117', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\153', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\130', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\170', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\132', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\154', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\167', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\110', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\121', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\145', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\121', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\102', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\170', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\115', '\166', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\143', '\122', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\102', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\127', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\171', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\103', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\162', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\171', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\163', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\166', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\162', '\114', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\166', '\130', '\040', '\157', '\156', '\040', '\061', + '\012', '\125', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\126', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\160', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\171', '\127', '\040', '\156', '\171', '\040', '\061', + '\012', '\162', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\130', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\143', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\155', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\152', '\102', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\144', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\153', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\116', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\127', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\164', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\146', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\132', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\167', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\125', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\107', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\167', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\142', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\121', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\132', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\106', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\104', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\110', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\124', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\106', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\166', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\111', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\114', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\145', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\154', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\143', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\164', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\112', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\121', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\120', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\155', '\117', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\102', '\153', '\040', '\151', '\156', '\040', '\061', + '\012', '\165', '\172', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\126', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\107', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\160', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\104', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\126', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\155', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\166', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\146', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\121', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\120', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\170', '\112', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\112', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\122', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\143', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\126', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\161', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\152', '\113', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\153', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\115', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\142', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\114', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\121', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\155', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\113', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\161', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\126', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\130', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\117', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\110', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\106', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\131', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\106', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\111', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\115', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\161', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\142', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\163', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\102', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\142', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\170', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\171', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\115', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\117', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\111', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\131', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\146', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\153', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\131', '\153', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\122', '\147', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\117', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\101', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\113', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\103', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\151', '\126', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\162', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\105', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\122', '\162', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\142', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\115', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\132', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\106', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\153', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\113', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\142', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\110', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\172', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\115', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\113', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\160', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\143', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\127', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\155', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\167', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\144', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\132', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\113', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\172', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\171', '\124', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\145', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\160', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\171', '\107', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\114', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\154', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\113', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\122', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\120', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\153', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\170', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\127', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\143', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\112', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\167', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\122', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\167', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\114', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\130', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\146', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\152', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\172', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\125', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\123', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\170', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\126', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\125', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\141', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\110', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\151', '\171', '\103', '\040', '\151', '\156', '\040', '\061', + '\012', '\124', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\112', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\112', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\112', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\116', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\144', '\101', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\161', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\107', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\121', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\114', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\126', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\141', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\111', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\130', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\106', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\167', '\115', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\116', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\127', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\156', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\123', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\103', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\124', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\104', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\102', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\172', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\160', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\103', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\122', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\153', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\104', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\142', '\104', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\154', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\106', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\120', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\152', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\123', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\165', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\120', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\112', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\160', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\107', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\130', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\121', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\131', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\166', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\167', '\132', '\040', '\151', '\156', '\040', '\061', + '\012', '\172', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\155', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\113', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\146', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\125', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\167', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\130', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\120', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\146', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\160', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\116', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\130', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\172', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\155', '\102', '\040', '\155', '\145', '\040', '\061', + '\012', '\127', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\130', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\121', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\144', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\162', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\146', '\114', '\040', '\156', '\171', '\040', '\061', + '\012', '\171', '\131', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\142', '\110', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\113', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\146', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\172', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\141', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\112', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\141', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\150', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\131', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\155', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\150', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\155', '\171', '\040', '\155', '\145', '\040', '\061', + '\012', '\122', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\163', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\150', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\111', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\111', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\143', '\106', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\161', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\153', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\115', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\167', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\101', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\155', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\167', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\146', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\170', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\143', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\146', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\172', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\162', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\147', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\167', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\107', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\144', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\104', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\114', '\163', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\116', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\127', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\152', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\131', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\145', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\166', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\153', '\101', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\152', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\147', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\130', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\124', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\150', '\172', '\040', '\150', '\141', '\040', '\061', + '\012', '\167', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\170', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\120', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\106', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\115', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\142', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\160', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\142', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\115', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\146', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\121', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\105', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\155', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\106', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\120', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\115', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\172', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\106', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\112', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\126', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\157', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\143', '\116', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\111', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\132', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\131', '\154', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\110', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\163', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\102', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\124', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\113', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\142', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\166', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\146', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\103', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\142', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\166', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\124', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\107', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\124', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\160', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\167', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\161', '\124', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\115', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\126', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\115', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\167', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\152', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\124', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\162', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\167', '\132', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\156', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\112', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\153', '\112', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\101', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\150', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\122', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\154', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\106', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\167', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\103', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\130', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\124', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\142', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\166', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\102', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\116', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\121', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\114', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\126', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\125', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\132', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\111', '\171', '\040', '\145', '\147', '\040', '\061', + '\012', '\150', '\126', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\121', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\113', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\142', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\131', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\166', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\110', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\130', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\126', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\160', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\127', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\146', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\111', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\146', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\147', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\105', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\117', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\110', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\132', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\114', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\153', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\167', '\121', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\162', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\142', '\110', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\166', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\122', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\160', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\106', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\123', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\144', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\126', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\166', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\167', '\117', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\127', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\121', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\156', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\146', '\111', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\123', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\160', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\131', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\102', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\112', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\144', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\121', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\146', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\113', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\172', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\142', '\167', '\111', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\154', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\142', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\156', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\167', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\117', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\122', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\132', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\153', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\107', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\122', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\166', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\107', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\151', '\111', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\172', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\114', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\125', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\156', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\130', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\103', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\114', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\146', '\110', '\040', '\146', '\157', '\040', '\061', + '\012', '\151', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\110', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\131', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\104', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\123', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\141', '\132', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\142', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\172', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\161', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\105', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\146', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\166', '\123', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\144', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\122', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\154', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\154', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\141', '\157', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\154', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\120', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\111', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\155', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\164', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\164', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\172', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\170', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\162', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\143', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\165', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\120', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\106', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\107', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\131', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\114', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\115', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\126', '\154', '\040', '\157', '\156', '\040', '\061', + '\012', '\143', '\167', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\142', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\167', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\145', '\121', '\157', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\121', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\113', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\166', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\126', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\120', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\121', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\132', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\127', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\160', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\105', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\163', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\154', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\172', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\165', '\112', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\126', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\132', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\142', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\157', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\143', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\167', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\120', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\112', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\155', '\116', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\144', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\101', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\170', '\113', '\040', '\156', '\171', '\040', '\061', + '\012', '\110', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\111', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\147', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\114', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\115', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\166', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\147', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\154', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\115', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\123', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\105', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\146', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\103', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\153', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\111', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\120', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\160', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\121', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\154', '\103', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\150', '\147', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\160', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\111', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\120', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\162', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\110', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\146', '\127', '\040', '\156', '\171', '\040', '\061', + '\012', '\131', '\171', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\122', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\113', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\170', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\144', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\142', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\145', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\111', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\160', '\120', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\121', '\171', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\120', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\101', '\157', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\131', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\146', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\166', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\145', '\110', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\121', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\160', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\146', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\144', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\102', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\105', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\146', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\145', '\106', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\120', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\104', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\132', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\125', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\121', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\152', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\113', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\115', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\115', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\167', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\163', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\125', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\102', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\146', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\146', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\144', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\172', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\126', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\114', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\124', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\155', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\166', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\121', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\111', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\107', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\110', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\112', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\127', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\130', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\152', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\154', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\162', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\153', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\110', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\146', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\114', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\125', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\104', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\114', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\131', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\111', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\162', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\142', '\103', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\130', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\103', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\102', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\111', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\144', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\121', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\160', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\153', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\153', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\122', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\115', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\145', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\110', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\170', '\125', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\131', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\154', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\122', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\107', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\106', '\172', '\040', '\172', '\145', '\040', '\061', + '\012', '\161', '\117', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\107', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\165', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\103', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\121', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\167', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\127', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\146', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\130', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\125', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\144', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\167', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\166', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\142', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\144', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\112', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\127', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\130', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\130', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\115', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\116', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\106', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\160', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\150', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\131', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\166', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\111', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\142', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\115', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\150', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\127', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\114', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\163', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\155', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\101', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\172', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\113', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\171', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\146', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\160', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\101', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\164', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\122', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\124', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\125', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\151', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\112', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\103', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\117', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\146', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\112', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\153', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\171', '\104', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\162', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\160', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\125', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\144', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\121', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\155', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\153', '\152', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\124', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\131', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\132', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\153', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\104', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\123', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\130', '\162', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\132', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\161', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\112', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\167', '\105', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\170', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\170', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\115', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\122', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\131', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\126', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\155', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\171', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\170', '\145', '\111', '\040', '\145', '\162', '\040', '\061', + '\012', '\127', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\104', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\172', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\147', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\163', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\130', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\103', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\162', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\122', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\146', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\164', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\124', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\146', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\154', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\150', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\106', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\121', '\165', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\130', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\106', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\111', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\124', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\113', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\122', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\130', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\124', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\122', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\112', '\162', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\122', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\160', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\116', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\165', '\152', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\110', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\110', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\161', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\160', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\103', '\147', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\127', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\165', '\126', '\040', '\165', '\156', '\040', '\061', + '\012', '\142', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\170', '\105', '\040', '\142', '\145', '\040', '\061', + '\012', '\165', '\126', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\162', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\111', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\141', '\161', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\107', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\147', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\163', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\126', '\170', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\123', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\160', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\113', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\167', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\131', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\112', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\166', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\106', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\160', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\116', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\166', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\170', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\160', '\165', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\170', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\101', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\155', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\103', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\106', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\114', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\143', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\126', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\153', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\170', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\152', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\152', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\167', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\150', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\172', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\166', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\120', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\171', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\103', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\152', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\146', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\160', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\160', '\146', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\164', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\157', '\124', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\123', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\170', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\127', '\162', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\117', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\144', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\130', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\130', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\142', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\167', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\126', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\167', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\163', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\120', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\167', '\107', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\155', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\166', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\146', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\160', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\166', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\114', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\143', '\145', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\102', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\144', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\115', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\146', '\117', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\141', '\112', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\146', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\144', '\112', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\105', '\141', '\171', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\123', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\166', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\160', '\110', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\170', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\120', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\142', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\157', '\105', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\146', '\106', '\040', '\142', '\145', '\040', '\061', + '\012', '\155', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\163', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\103', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\114', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\153', '\130', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\103', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\125', '\153', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\115', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\146', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\130', '\141', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\115', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\146', '\103', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\107', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\111', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\166', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\160', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\116', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\153', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\111', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\146', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\126', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\153', '\121', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\161', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\170', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\146', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\131', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\147', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\130', '\141', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\131', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\152', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\152', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\130', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\150', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\107', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\120', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\117', '\147', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\160', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\103', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\146', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\167', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\114', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\126', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\155', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\107', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\112', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\106', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\103', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\166', '\127', '\040', '\154', '\145', '\040', '\061', + '\012', '\123', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\132', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\111', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\104', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\103', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\153', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\160', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\121', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\151', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\125', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\152', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\130', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\147', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\167', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\164', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\110', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\110', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\142', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\155', '\131', '\040', '\155', '\145', '\040', '\061', + '\012', '\167', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\106', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\147', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\155', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\146', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\147', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\163', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\157', '\127', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\120', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\155', '\160', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\103', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\164', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\142', '\121', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\165', '\115', '\040', '\165', '\156', '\040', '\061', + '\012', '\146', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\156', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\144', '\123', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\106', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\106', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\161', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\155', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\153', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\121', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\171', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\170', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\172', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\146', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\153', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\125', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\165', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\125', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\102', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\116', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\127', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\166', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\122', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\156', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\146', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\152', '\123', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\146', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\130', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\122', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\105', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\107', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\110', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\102', '\146', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\126', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\151', '\113', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\141', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\147', '\101', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\167', '\112', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\107', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\164', '\146', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\152', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\107', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\155', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\156', '\125', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\107', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\126', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\123', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\161', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\156', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\126', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\126', '\163', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\116', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\156', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\126', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\127', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\110', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\117', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\103', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\164', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\143', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\156', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\155', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\126', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\144', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\144', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\110', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\153', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\170', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\162', '\101', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\162', '\124', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\152', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\142', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\155', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\104', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\126', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\107', '\163', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\141', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\162', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\112', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\170', '\105', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\166', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\160', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\112', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\106', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\167', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\141', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\155', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\122', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\101', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\104', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\107', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\164', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\163', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\171', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\170', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\132', '\040', '\146', '\157', '\040', '\061', + '\012', '\163', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\155', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\110', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\155', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\112', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\120', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\130', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\106', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\116', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\123', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\122', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\167', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\155', '\102', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\123', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\115', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\153', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\144', '\163', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\142', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\160', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\170', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\147', '\120', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\160', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\160', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\160', '\120', '\040', '\160', '\162', '\040', '\061', + '\012', '\116', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\147', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\161', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\122', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\125', '\142', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\112', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\160', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\153', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\115', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\163', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\117', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\156', '\142', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\166', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\106', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\160', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\164', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\105', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\150', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\130', '\171', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\104', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\126', '\153', '\040', '\151', '\156', '\040', '\061', + '\012', '\110', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\160', '\132', '\040', '\160', '\157', '\040', '\061', + '\012', '\141', '\145', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\152', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\107', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\161', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\120', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\130', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\152', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\150', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\161', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\131', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\150', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\154', '\105', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\110', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\110', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\161', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\142', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\123', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\110', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\160', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\121', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\151', '\125', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\152', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\164', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\147', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\121', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\127', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\126', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\121', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\114', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\110', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\110', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\166', '\125', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\126', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\132', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\154', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\144', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\127', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\151', '\117', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\104', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\146', '\114', '\040', '\146', '\157', '\040', '\061', + '\012', '\171', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\102', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\103', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\114', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\162', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\170', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\155', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\102', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\165', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\171', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\106', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\155', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\126', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\113', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\150', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\123', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\162', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\102', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\171', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\146', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\130', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\152', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\147', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\167', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\132', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\112', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\104', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\125', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\170', '\105', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\170', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\143', '\167', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\122', '\167', '\040', '\163', '\164', '\040', '\061', + '\012', '\113', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\170', '\101', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\121', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\120', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\110', '\167', '\165', '\040', '\153', '\165', '\040', '\061', + '\012', '\163', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\170', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\141', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\142', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\115', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\107', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\162', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\117', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\113', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\162', '\101', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\127', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\170', '\121', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\130', '\157', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\155', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\144', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\160', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\115', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\110', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\143', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\141', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\144', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\142', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\126', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\114', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\160', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\146', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\152', '\122', '\040', '\151', '\156', '\040', '\061', + '\012', '\151', '\112', '\171', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\146', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\162', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\147', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\117', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\127', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\131', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\162', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\110', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\105', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\150', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\131', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\146', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\142', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\103', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\155', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\172', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\113', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\123', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\144', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\127', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\167', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\142', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\163', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\164', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\160', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\164', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\117', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\111', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\166', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\152', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\166', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\170', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\121', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\112', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\142', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\122', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\105', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\141', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\123', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\112', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\127', '\162', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\113', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\141', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\103', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\142', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\144', '\111', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\146', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\142', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\167', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\167', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\114', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\156', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\165', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\111', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\124', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\113', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\102', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\117', '\172', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\117', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\172', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\115', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\146', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\164', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\172', '\146', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\167', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\121', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\163', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\114', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\155', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\116', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\115', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\103', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\160', '\166', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\116', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\160', '\120', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\130', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\114', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\172', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\166', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\105', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\170', '\113', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\102', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\103', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\152', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\164', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\102', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\152', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\147', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\162', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\142', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\167', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\121', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\115', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\103', '\156', '\040', '\156', '\144', '\040', '\061', + '\012', '\145', '\107', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\165', '\120', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\151', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\152', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\167', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\170', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\114', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\122', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\171', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\122', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\113', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\150', '\142', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\155', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\161', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\147', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\110', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\126', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\154', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\104', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\121', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\112', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\120', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\143', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\103', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\103', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\141', '\102', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\142', '\127', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\142', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\127', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\170', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\121', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\116', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\144', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\131', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\167', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\132', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\142', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\141', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\126', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\146', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\170', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\102', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\145', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\102', '\145', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\160', '\103', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\160', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\162', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\157', '\171', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\113', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\123', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\102', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\132', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\120', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\102', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\170', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\114', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\107', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\166', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\104', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\122', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\116', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\101', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\146', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\107', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\166', '\102', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\126', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\142', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\106', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\146', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\166', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\106', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\157', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\162', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\112', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\146', '\105', '\040', '\156', '\171', '\040', '\061', + '\012', '\153', '\155', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\107', '\155', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\170', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\165', '\125', '\040', '\165', '\156', '\040', '\061', + '\012', '\161', '\131', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\113', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\146', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\162', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\121', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\127', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\172', '\103', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\131', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\141', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\123', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\121', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\153', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\156', '\154', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\115', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\171', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\106', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\153', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\143', '\153', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\115', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\112', '\172', '\040', '\157', '\156', '\040', '\061', + '\012', '\170', '\166', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\121', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\131', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\170', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\104', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\120', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\171', '\115', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\170', '\104', '\040', '\156', '\171', '\040', '\061', + '\012', '\153', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\130', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\153', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\106', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\155', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\143', '\171', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\161', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\162', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\101', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\170', '\106', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\120', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\103', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\102', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\142', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\146', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\155', '\142', '\112', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\131', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\104', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\155', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\165', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\130', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\120', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\117', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\116', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\152', '\120', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\162', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\147', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\125', '\151', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\101', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\132', '\166', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\160', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\116', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\102', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\152', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\114', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\131', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\142', '\117', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\130', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\113', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\165', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\146', '\102', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\163', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\125', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\155', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\130', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\111', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\121', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\164', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\143', '\145', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\164', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\104', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\114', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\145', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\166', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\142', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\124', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\124', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\144', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\170', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\104', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\146', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\110', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\126', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\172', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\166', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\132', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\120', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\116', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\162', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\132', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\116', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\106', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\106', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\126', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\132', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\143', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\171', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\164', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\122', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\103', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\112', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\167', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\170', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\114', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\107', '\161', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\142', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\123', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\160', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\147', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\156', '\152', '\111', '\040', '\156', '\144', '\040', '\061', + '\012', '\131', '\160', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\170', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\126', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\112', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\170', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\104', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\105', '\141', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\143', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\107', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\153', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\142', '\113', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\116', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\162', '\125', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\142', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\166', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\151', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\115', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\142', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\147', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\155', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\113', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\170', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\146', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\162', '\120', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\105', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\170', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\132', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\130', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\143', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\153', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\116', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\130', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\153', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\106', '\170', '\040', '\170', '\145', '\040', '\061', + '\012', '\144', '\123', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\120', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\157', '\106', '\160', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\101', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\172', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\111', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\160', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\101', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\146', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\127', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\144', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\154', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\105', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\161', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\130', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\116', '\040', '\146', '\157', '\040', '\061', + '\012', '\142', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\157', '\107', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\132', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\146', '\110', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\143', '\105', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\162', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\127', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\167', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\166', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\147', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\160', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\152', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\161', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\106', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\121', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\120', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\160', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\167', '\105', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\155', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\171', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\126', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\164', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\160', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\160', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\150', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\152', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\147', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\155', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\103', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\131', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\166', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\154', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\116', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\142', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\142', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\170', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\165', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\106', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\126', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\127', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\102', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\172', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\125', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\131', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\126', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\146', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\166', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\172', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\131', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\164', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\160', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\114', '\154', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\107', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\163', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\116', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\152', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\126', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\156', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\125', '\141', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\172', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\170', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\152', '\115', '\040', '\141', '\156', '\040', '\061', + '\012', '\107', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\154', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\113', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\160', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\152', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\152', '\107', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\170', '\111', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\130', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\166', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\170', '\102', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\171', '\126', '\040', '\156', '\171', '\040', '\061', + '\012', '\143', '\130', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\142', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\123', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\160', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\142', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\113', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\162', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\146', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\157', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\154', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\111', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\162', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\141', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\143', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\144', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\102', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\124', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\146', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\160', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\132', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\123', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\120', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\145', '\121', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\111', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\111', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\116', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\117', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\115', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\106', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\146', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\152', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\153', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\112', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\172', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\104', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\130', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\164', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\127', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\121', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\124', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\165', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\165', '\127', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\115', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\132', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\102', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\131', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\103', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\125', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\153', '\122', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\144', '\156', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\152', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\120', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\142', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\146', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\142', '\126', '\040', '\144', '\145', '\040', '\061', + '\012', '\106', '\155', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\146', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\142', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\104', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\120', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\142', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\106', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\105', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\105', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\115', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\132', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\152', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\113', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\144', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\107', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\123', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\130', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\103', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\163', '\121', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\120', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\155', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\152', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\167', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\114', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\110', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\164', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\127', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\122', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\160', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\156', '\104', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\160', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\172', '\123', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\132', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\170', '\103', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\146', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\155', '\102', '\040', '\155', '\145', '\040', '\061', + '\012', '\154', '\102', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\112', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\153', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\132', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\167', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\143', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\146', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\157', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\151', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\102', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\152', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\166', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\151', '\126', '\040', '\151', '\156', '\040', '\061', + '\012', '\153', '\142', '\120', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\126', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\132', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\131', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\167', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\166', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\111', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\155', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\166', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\167', '\132', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\146', '\114', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\153', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\126', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\104', '\153', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\110', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\123', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\172', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\106', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\105', '\157', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\157', '\131', '\040', '\157', '\156', '\040', '\061', + '\012', '\101', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\111', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\125', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\122', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\120', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\145', '\152', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\164', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\163', '\126', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\115', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\105', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\170', '\131', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\125', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\147', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\115', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\112', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\144', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\116', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\107', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\170', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\127', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\120', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\122', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\113', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\166', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\160', '\113', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\146', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\125', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\127', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\120', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\120', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\112', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\150', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\121', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\105', '\157', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\106', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\130', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\142', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\152', '\117', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\154', '\132', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\152', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\126', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\152', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\130', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\156', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\171', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\153', '\166', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\171', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\147', '\120', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\115', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\142', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\117', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\101', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\114', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\170', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\114', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\104', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\160', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\111', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\102', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\130', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\144', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\152', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\171', '\127', '\040', '\151', '\156', '\040', '\061', + '\012', '\112', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\127', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\144', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\163', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\121', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\167', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\142', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\146', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\145', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\125', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\110', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\106', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\156', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\166', '\111', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\103', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\161', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\124', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\146', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\163', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\104', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\112', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\110', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\104', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\163', '\152', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\117', '\171', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\167', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\142', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\160', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\160', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\141', '\130', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\110', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\171', '\116', '\040', '\156', '\171', '\040', '\061', + '\012', '\155', '\142', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\126', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\122', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\172', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\142', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\123', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\115', '\167', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\160', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\152', '\130', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\103', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\121', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\111', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\157', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\126', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\142', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\130', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\163', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\142', '\106', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\102', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\151', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\166', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\127', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\152', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\150', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\150', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\112', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\115', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\125', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\143', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\157', '\111', '\040', '\157', '\156', '\040', '\061', + '\012', '\132', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\172', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\152', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\160', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\130', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\153', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\110', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\120', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\125', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\170', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\113', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\153', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\122', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\143', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\126', '\144', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\102', '\160', '\040', '\142', '\145', '\040', '\061', + '\012', '\117', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\132', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\111', '\171', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\167', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\161', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\150', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\116', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\127', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\162', '\126', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\171', '\123', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\166', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\126', '\146', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\113', '\147', '\145', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\145', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\162', '\166', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\172', '\111', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\167', '\123', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\103', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\126', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\102', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\121', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\170', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\104', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\106', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\121', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\170', '\142', '\104', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\155', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\102', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\125', '\040', '\146', '\157', '\040', '\061', + '\012', '\130', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\114', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\143', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\102', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\112', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\122', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\150', '\161', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\126', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\105', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\146', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\126', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\155', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\120', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\132', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\130', '\154', '\040', '\151', '\156', '\040', '\061', + '\012', +}; + +extern const int ksizeofUniversalAmbigsFile = sizeof(kUniversalAmbigsFile); + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.h b/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.h new file mode 100644 index 0000000..bcc633e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/include/ccutil/universalambigs.h @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////// +// File: universalambigs.h +// Description: Data for a universal ambigs file that is useful for +// any language. +// Author: Ray Smith +// Created: Mon Mar 18 11:26:00 PDT 2013 +// +// (C) Copyright 2013, 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. +// +/////////////////////////////////////////////////////////////////////// + +namespace tesseract { + +extern const char kUniversalAmbigsFile[]; +extern const int ksizeofUniversalAmbigsFile; + +} // namespace tesseract diff --git a/hgdriver/3rdparty/hgOCR/leptonica/adaptmap.c b/hgdriver/3rdparty/hgOCR/leptonica/adaptmap.c new file mode 100644 index 0000000..9a782fd --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/adaptmap.c @@ -0,0 +1,2947 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file adaptmap.c + *
+ *
+ *  -------------------------------------------------------------------
+ *
+ *  Image binarization algorithms are found in:
+ *     grayquant.c:   standard, simple, general grayscale quantization
+ *     adaptmap.c:    local adaptive; mostly gray-to-gray in preparation
+ *                    for binarization
+ *     binarize.c:    special binarization methods, locally adaptive.
+ *
+ *  -------------------------------------------------------------------
+ *
+ *      Clean background to white using background normalization
+ *          PIX       *pixCleanBackgroundToWhite()
+ *
+ *      Adaptive background normalization (top-level functions)
+ *          PIX       *pixBackgroundNormSimple()     8 and 32 bpp
+ *          PIX       *pixBackgroundNorm()           8 and 32 bpp
+ *          PIX       *pixBackgroundNormMorph()      8 and 32 bpp
+ *
+ *      Arrays of inverted background values for normalization (16 bpp)
+ *          l_int32    pixBackgroundNormGrayArray()   8 bpp input
+ *          l_int32    pixBackgroundNormRGBArrays()   32 bpp input
+ *          l_int32    pixBackgroundNormGrayArrayMorph()   8 bpp input
+ *          l_int32    pixBackgroundNormRGBArraysMorph()   32 bpp input
+ *
+ *      Measurement of local background
+ *          l_int32    pixGetBackgroundGrayMap()        8 bpp
+ *          l_int32    pixGetBackgroundRGBMap()         32 bpp
+ *          l_int32    pixGetBackgroundGrayMapMorph()   8 bpp
+ *          l_int32    pixGetBackgroundRGBMapMorph()    32 bpp
+ *          l_int32    pixFillMapHoles()
+ *          PIX       *pixExtendByReplication()         8 bpp
+ *          l_int32    pixSmoothConnectedRegions()      8 bpp
+ *
+ *      Measurement of local foreground
+ *          l_int32    pixGetForegroundGrayMap()        8 bpp
+ *
+ *      Generate inverted background map for each component
+ *          PIX       *pixGetInvBackgroundMap()   16 bpp
+ *
+ *      Apply inverse background map to image
+ *          PIX       *pixApplyInvBackgroundGrayMap()   8 bpp
+ *          PIX       *pixApplyInvBackgroundRGBMap()    32 bpp
+ *
+ *      Apply variable map
+ *          PIX       *pixApplyVariableGrayMap()        8 bpp
+ *
+ *      Non-adaptive (global) mapping
+ *          PIX       *pixGlobalNormRGB()               32 bpp or cmapped
+ *          PIX       *pixGlobalNormNoSatRGB()          32 bpp
+ *
+ *      Adaptive threshold spread normalization
+ *          l_int32    pixThresholdSpreadNorm()         8 bpp
+ *
+ *      Adaptive background normalization (flexible adaptaption)
+ *          PIX       *pixBackgroundNormFlex()          8 bpp
+ *
+ *      Adaptive contrast normalization
+ *          PIX             *pixContrastNorm()          8 bpp
+ *          l_int32          pixMinMaxTiles()
+ *          l_int32          pixSetLowContrast()
+ *          PIX             *pixLinearTRCTiled()
+ *          static l_int32  *iaaGetLinearTRC()
+ *
+ *  Background normalization is done by generating a reduced map (or set
+ *  of maps) representing the estimated background value of the
+ *  input image, and using this to shift the pixel values so that
+ *  this background value is set to some constant value.
+ *
+ *  Specifically, normalization has 3 steps:
+ *    (1) Generate a background map at a reduced scale.
+ *    (2) Make the array of inverted background values by inverting
+ *        the map.  The result is an array of local multiplicative factors.
+ *    (3) Apply this inverse background map to the image
+ *
+ *  The inverse background arrays can be generated in two different ways here:
+ *    (1) Remove the 'foreground' pixels and average over the remaining
+ *        pixels in each tile.  Propagate values into tiles where
+ *        values have not been assigned, either because there was not
+ *        enough background in the tile or because the tile is covered
+ *        by a foreground region described by an image mask.
+ *        After the background map is made, the inverse map is generated by
+ *        smoothing over some number of adjacent tiles
+ *        (block convolution) and then inverting.
+ *    (2) Remove the foreground pixels using a morphological closing
+ *        on a subsampled version of the image.  Propagate values
+ *        into pixels covered by an optional image mask.  Invert the
+ *        background map without preconditioning by convolutional smoothing.
+ *
+ *  Other methods for adaptively normalizing the image are also given here.
+ *
+ *  (1) pixThresholdSpreadNorm() computes a local threshold over the image
+ *      and normalizes the input pixel values so that this computed threshold
+ *      is a constant across the entire image.
+ *
+ *  (2) pixContrastNorm() computes and applies a local TRC so that the
+ *      local dynamic range is expanded to the full 8 bits, where the
+ *      darkest pixels are mapped to 0 and the lightest to 255.  This is
+ *      useful for improving the appearance of pages with very light
+ *      foreground or very dark background, and where the local TRC
+ *      function doesn't change rapidly with position.
+ * 
+ */ + +#include "allheaders.h" + + /* Default input parameters for pixBackgroundNormSimple() + * Notes: + * (1) mincount must never exceed the tile area (width * height) + * (2) bgval must be sufficiently below 255 to avoid accidental + * saturation; otherwise it should be large to avoid + * shrinking the dynamic range + * (3) results should otherwise not be sensitive to these values + */ +static const l_int32 DefaultTileWidth = 10; /*!< default tile width */ +static const l_int32 DefaultTileHeight = 15; /*!< default tile height */ +static const l_int32 DefaultFgThreshold = 60; /*!< default fg threshold */ +static const l_int32 DefaultMinCount = 40; /*!< default minimum count */ +static const l_int32 DefaultBgVal = 200; /*!< default bg value */ +static const l_int32 DefaultXSmoothSize = 2; /*!< default x smooth size */ +static const l_int32 DefaultYSmoothSize = 1; /*!< default y smooth size */ + +static l_int32 *iaaGetLinearTRC(l_int32 **iaa, l_int32 diff); + +#ifndef NO_CONSOLE_IO +#define DEBUG_GLOBAL 0 /*!< set to 1 to debug pixGlobalNormNoSatRGB() */ +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------* + * Clean background to white using background normalization * + *------------------------------------------------------------------*/ +/*! + * \brief pixCleanBackgroundToWhite() + * + * \param[in] pixs 8 bpp grayscale or 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] pixg [optional] 8 bpp grayscale version; can be null + * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 + * \param[in] blackval dark value to set to black (0) + * \param[in] whiteval light value to set to white (255) + * \return pixd 8 bpp or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *    (1) This is a simplified interface for cleaning an image.
+ *        For comparison, see pixAdaptThresholdToBinaryGen().
+ *    (2) The suggested default values for the input parameters are:
+ *          gamma:    1.0  (reduce this to increase the contrast; e.g.,
+ *                          for light text)
+ *          blackval   70  (a bit more than 60)
+ *          whiteval  190  (a bit less than 200)
+ * 
+ */ +PIX * +pixCleanBackgroundToWhite(PIX *pixs, + PIX *pixim, + PIX *pixg, + l_float32 gamma, + l_int32 blackval, + l_int32 whiteval) +{ +l_int32 d; +PIX *pixd; + + PROCNAME("pixCleanBackgroundToWhite"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 8 or 32", procName, NULL); + + pixd = pixBackgroundNormSimple(pixs, pixim, pixg); + if (!pixd) + return (PIX *)ERROR_PTR("background norm failedd", procName, NULL); + pixGammaTRC(pixd, pixd, gamma, blackval, whiteval); + return pixd; +} + + +/*------------------------------------------------------------------* + * Adaptive background normalization * + *------------------------------------------------------------------*/ +/*! + * \brief pixBackgroundNormSimple() + * + * \param[in] pixs 8 bpp grayscale or 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] pixg [optional] 8 bpp grayscale version; can be null + * \return pixd 8 bpp or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *    (1) This is a simplified interface to pixBackgroundNorm(),
+ *        where seven parameters are defaulted.
+ *    (2) The input image is either grayscale or rgb.
+ *    (3) See pixBackgroundNorm() for usage and function.
+ * 
+ */ +PIX * +pixBackgroundNormSimple(PIX *pixs, + PIX *pixim, + PIX *pixg) +{ + return pixBackgroundNorm(pixs, pixim, pixg, + DefaultTileWidth, DefaultTileHeight, + DefaultFgThreshold, DefaultMinCount, + DefaultBgVal, DefaultXSmoothSize, + DefaultYSmoothSize); +} + + +/*! + * \brief pixBackgroundNorm() + * + * \param[in] pixs 8 bpp grayscale or 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] pixg [optional] 8 bpp grayscale version; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[in] bgval target bg val; typ. > 128 + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \return pixd 8 bpp or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *    (1) This is a top-level interface for normalizing the image intensity
+ *        by mapping the image so that the background is near the input
+ *        value 'bgval'.
+ *    (2) The input image is either grayscale or rgb.
+ *    (3) For each component in the input image, the background value
+ *        in each tile is estimated using the values in the tile that
+ *        are not part of the foreground, where the foreground is
+ *        determined by the input 'thresh' argument.
+ *    (4) An optional binary mask can be specified, with the foreground
+ *        pixels typically over image regions.  The resulting background
+ *        map values will be determined by surrounding pixels that are
+ *        not under the mask foreground.  The origin (0,0) of this mask
+ *        is assumed to be aligned with the origin of the input image.
+ *        This binary mask must not fully cover pixs, because then there
+ *        will be no pixels in the input image available to compute
+ *        the background.
+ *    (5) An optional grayscale version of the input pixs can be supplied.
+ *        The only reason to do this is if the input is RGB and this
+ *        grayscale version can be used elsewhere.  If the input is RGB
+ *        and this is not supplied, it is made internally using only
+ *        the green component, and destroyed after use.
+ *    (6) The dimensions of the pixel tile (sx, sy) give the amount by
+ *        by which the map is reduced in size from the input image.
+ *    (7) The threshold is used to binarize the input image, in order to
+ *        locate the foreground components.  If this is set too low,
+ *        some actual foreground may be used to determine the maps;
+ *        if set too high, there may not be enough background
+ *        to determine the map values accurately.  Typically, it's
+ *        better to err by setting the threshold too high.
+ *    (8) A 'mincount' threshold is a minimum count of pixels in a
+ *        tile for which a background reading is made, in order for that
+ *        pixel in the map to be valid.  This number should perhaps be
+ *        at least 1/3 the size of the tile.
+ *    (9) A 'bgval' target background value for the normalized image.  This
+ *        should be at least 128.  If set too close to 255, some
+ *        clipping will occur in the result.
+ *    (10) Two factors, 'smoothx' and 'smoothy', are input for smoothing
+ *        the map.  Each low-pass filter kernel dimension is
+ *        is 2 * (smoothing factor) + 1, so a
+ *        value of 0 means no smoothing. A value of 1 or 2 is recommended.
+ * 
+ */ +PIX * +pixBackgroundNorm(PIX *pixs, + PIX *pixim, + PIX *pixg, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + l_int32 bgval, + l_int32 smoothx, + l_int32 smoothy) +{ +l_int32 d, allfg; +PIX *pixm, *pixmi, *pixd; +PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; + + PROCNAME("pixBackgroundNorm"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (sx < 4 || sy < 4) + return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); + } + + pixd = NULL; + if (d == 8) { + pixm = NULL; + pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); + if (!pixm) { + L_WARNING("map not made; return a copy of the source\n", procName); + return pixCopy(NULL, pixs); + } + + pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); + if (!pixmi) { + L_WARNING("pixmi not made; return a copy of source\n", procName); + pixDestroy(&pixm); + return pixCopy(NULL, pixs); + } else { + pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy); + } + + pixDestroy(&pixm); + pixDestroy(&pixmi); + } + else { + pixmr = pixmg = pixmb = NULL; + pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, + mincount, &pixmr, &pixmg, &pixmb); + if (!pixmr || !pixmg || !pixmb) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + L_WARNING("map not made; return a copy of the source\n", procName); + return pixCopy(NULL, pixs); + } + + pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); + pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); + pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); + if (!pixmri || !pixmgi || !pixmbi) { + L_WARNING("not all pixm*i are made; return src copy\n", procName); + pixd = pixCopy(NULL, pixs); + } else { + pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, + sx, sy); + } + + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + pixDestroy(&pixmri); + pixDestroy(&pixmgi); + pixDestroy(&pixmbi); + } + + if (!pixd) + ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixBackgroundNormMorph() + * + * \param[in] pixs 8 bpp grayscale or 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] reduction at which morph closings are done; between 2 and 16 + * \param[in] size of square Sel for the closing; use an odd number + * \param[in] bgval target bg val; typ. > 128 + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *    (1) This is a top-level interface for normalizing the image intensity
+ *        by mapping the image so that the background is near the input
+ *        value 'bgval'.
+ *    (2) The input image is either grayscale or rgb.
+ *    (3) For each component in the input image, the background value
+ *        is estimated using a grayscale closing; hence the 'Morph'
+ *        in the function name.
+ *    (4) An optional binary mask can be specified, with the foreground
+ *        pixels typically over image regions.  The resulting background
+ *        map values will be determined by surrounding pixels that are
+ *        not under the mask foreground.  The origin (0,0) of this mask
+ *        is assumed to be aligned with the origin of the input image.
+ *        This binary mask must not fully cover pixs, because then there
+ *        will be no pixels in the input image available to compute
+ *        the background.
+ *    (5) The map is computed at reduced size (given by 'reduction')
+ *        from the input pixs and optional pixim.  At this scale,
+ *        pixs is closed to remove the background, using a square Sel
+ *        of odd dimension.  The product of reduction * size should be
+ *        large enough to remove most of the text foreground.
+ *    (6) No convolutional smoothing needs to be done on the map before
+ *        inverting it.
+ *    (7) A 'bgval' target background value for the normalized image.  This
+ *        should be at least 128.  If set too close to 255, some
+ *        clipping will occur in the result.
+ * 
+ */ +PIX * +pixBackgroundNormMorph(PIX *pixs, + PIX *pixim, + l_int32 reduction, + l_int32 size, + l_int32 bgval) +{ +l_int32 d, allfg; +PIX *pixm, *pixmi, *pixd; +PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; + + PROCNAME("pixBackgroundNormMorph"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (reduction < 2 || reduction > 16) + return (PIX *)ERROR_PTR("reduction must be between 2 and 16", + procName, NULL); + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); + } + + pixd = NULL; + if (d == 8) { + pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); + if (!pixm) + return (PIX *)ERROR_PTR("pixm not made", procName, NULL); + pixmi = pixGetInvBackgroundMap(pixm, bgval, 0, 0); + if (!pixmi) + ERROR_PTR("pixmi not made", procName, NULL); + else + pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, + reduction, reduction); + pixDestroy(&pixm); + pixDestroy(&pixmi); + } + else { /* d == 32 */ + pixmr = pixmg = pixmb = NULL; + pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, + &pixmr, &pixmg, &pixmb); + if (!pixmr || !pixmg || !pixmb) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + return (PIX *)ERROR_PTR("not all pixm*", procName, NULL); + } + + pixmri = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); + pixmgi = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); + pixmbi = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); + if (!pixmri || !pixmgi || !pixmbi) + ERROR_PTR("not all pixm*i are made", procName, NULL); + else + pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, + reduction, reduction); + + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + pixDestroy(&pixmri); + pixDestroy(&pixmgi); + pixDestroy(&pixmbi); + } + + if (!pixd) + ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Arrays of inverted background values for normalization * + *-------------------------------------------------------------------------* + * Notes for these four functions: * + * (1) They are useful if you need to save the actual mapping array. * + * (2) They could be used in the top-level functions but are * + * not because their use makes those functions less clear. * + * (3) Each component in the input pixs generates a 16 bpp pix array. * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixBackgroundNormGrayArray() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[in] bgval target bg val; typ. > 128 + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \param[out] ppixd 16 bpp array of inverted background value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *    (1) See notes in pixBackgroundNorm().
+ *    (2) This returns a 16 bpp pix that can be used by
+ *        pixApplyInvBackgroundGrayMap() to generate a normalized version
+ *        of the input pixs.
+ * 
+ */ +l_ok +pixBackgroundNormGrayArray(PIX *pixs, + PIX *pixim, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + l_int32 bgval, + l_int32 smoothx, + l_int32 smoothy, + PIX **ppixd) +{ +l_int32 allfg; +PIX *pixm; + + PROCNAME("pixBackgroundNormGrayArray"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (sx < 4 || sy < 4) + return ERROR_INT("sx and sy must be >= 4", procName, 1); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return ERROR_INT("pixim all foreground", procName, 1); + } + + pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); + if (!pixm) + return ERROR_INT("pixm not made", procName, 1); + *ppixd = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); + pixCopyResolution(*ppixd, pixs); + pixDestroy(&pixm); + return 0; +} + + +/*! + * \brief pixBackgroundNormRGBArrays() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] pixg [optional] 8 bpp grayscale version; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[in] bgval target bg val; typ. > 128 + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \param[out] ppixr 16 bpp array of inverted R background value + * \param[out] ppixg 16 bpp array of inverted G background value + * \param[out] ppixb 16 bpp array of inverted B background value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *    (1) See notes in pixBackgroundNorm().
+ *    (2) This returns a set of three 16 bpp pix that can be used by
+ *        pixApplyInvBackgroundGrayMap() to generate a normalized version
+ *        of each component of the input pixs.
+ * 
+ */ +l_ok +pixBackgroundNormRGBArrays(PIX *pixs, + PIX *pixim, + PIX *pixg, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + l_int32 bgval, + l_int32 smoothx, + l_int32 smoothy, + PIX **ppixr, + PIX **ppixg, + PIX **ppixb) +{ +l_int32 allfg; +PIX *pixmr, *pixmg, *pixmb; + + PROCNAME("pixBackgroundNormRGBArrays"); + + if (!ppixr || !ppixg || !ppixb) + return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); + *ppixr = *ppixg = *ppixb = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (sx < 4 || sy < 4) + return ERROR_INT("sx and sy must be >= 4", procName, 1); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return ERROR_INT("pixim all foreground", procName, 1); + } + + pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, mincount, + &pixmr, &pixmg, &pixmb); + if (!pixmr || !pixmg || !pixmb) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + return ERROR_INT("not all pixm* made", procName, 1); + } + + *ppixr = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); + *ppixg = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); + *ppixb = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + return 0; +} + + +/*! + * \brief pixBackgroundNormGrayArrayMorph() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] reduction at which morph closings are done; between 2 and 16 + * \param[in] size of square Sel for the closing; use an odd number + * \param[in] bgval target bg val; typ. > 128 + * \param[out] ppixd 16 bpp array of inverted background value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *    (1) See notes in pixBackgroundNormMorph().
+ *    (2) This returns a 16 bpp pix that can be used by
+ *        pixApplyInvBackgroundGrayMap() to generate a normalized version
+ *        of the input pixs.
+ * 
+ */ +l_ok +pixBackgroundNormGrayArrayMorph(PIX *pixs, + PIX *pixim, + l_int32 reduction, + l_int32 size, + l_int32 bgval, + PIX **ppixd) +{ +l_int32 allfg; +PIX *pixm; + + PROCNAME("pixBackgroundNormGrayArrayMorph"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not 8 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (reduction < 2 || reduction > 16) + return ERROR_INT("reduction must be between 2 and 16", procName, 1); + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return ERROR_INT("pixim all foreground", procName, 1); + } + + pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); + if (!pixm) + return ERROR_INT("pixm not made", procName, 1); + *ppixd = pixGetInvBackgroundMap(pixm, bgval, 0, 0); + pixCopyResolution(*ppixd, pixs); + pixDestroy(&pixm); + return 0; +} + + +/*! + * \brief pixBackgroundNormRGBArraysMorph() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] reduction at which morph closings are done; between 2 and 16 + * \param[in] size of square Sel for the closing; use an odd number + * \param[in] bgval target bg val; typ. > 128 + * \param[out] ppixr 16 bpp array of inverted R background value + * \param[out] ppixg 16 bpp array of inverted G background value + * \param[out] ppixb 16 bpp array of inverted B background value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *    (1) See notes in pixBackgroundNormMorph().
+ *    (2) This returns a set of three 16 bpp pix that can be used by
+ *        pixApplyInvBackgroundGrayMap() to generate a normalized version
+ *        of each component of the input pixs.
+ * 
+ */ +l_ok +pixBackgroundNormRGBArraysMorph(PIX *pixs, + PIX *pixim, + l_int32 reduction, + l_int32 size, + l_int32 bgval, + PIX **ppixr, + PIX **ppixg, + PIX **ppixb) +{ +l_int32 allfg; +PIX *pixmr, *pixmg, *pixmb; + + PROCNAME("pixBackgroundNormRGBArraysMorph"); + + if (!ppixr || !ppixg || !ppixb) + return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); + *ppixr = *ppixg = *ppixb = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (reduction < 2 || reduction > 16) + return ERROR_INT("reduction must be between 2 and 16", procName, 1); + + /* If pixim exists, verify that it is not all foreground. */ + if (pixim) { + pixInvert(pixim, pixim); + pixZero(pixim, &allfg); + pixInvert(pixim, pixim); + if (allfg) + return ERROR_INT("pixim all foreground", procName, 1); + } + + pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, + &pixmr, &pixmg, &pixmb); + if (!pixmr || !pixmg || !pixmb) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + return ERROR_INT("not all pixm* made", procName, 1); + } + + *ppixr = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); + *ppixg = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); + *ppixb = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + return 0; +} + + +/*------------------------------------------------------------------* + * Measurement of local background * + *------------------------------------------------------------------*/ +/*! + * \brief pixGetBackgroundGrayMap() + * + * \param[in] pixs 8 bpp grayscale; not cmapped + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; + * it should not have only foreground pixels + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[out] ppixd 8 bpp grayscale map + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The background is measured in regions that don't have
+ *          images.  It is then propagated into the image regions,
+ *          and finally smoothed in each image region.
+ * 
+ */ +l_ok +pixGetBackgroundGrayMap(PIX *pixs, + PIX *pixim, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + PIX **ppixd) +{ +l_int32 w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf; +l_int32 xim, yim, delx, nx, ny, i, j, k, m; +l_int32 count, sum, val8; +l_int32 empty, fgpixels; +l_uint32 *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef; +l_float32 scalex, scaley; +PIX *pixd, *piximi, *pixb, *pixf, *pixims; + + PROCNAME("pixGetBackgroundGrayMap"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (sx < 4 || sy < 4) + return ERROR_INT("sx and sy must be >= 4", procName, 1); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* Evaluate the 'image' mask, pixim, and make sure + * it is not all fg. */ + fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ + if (pixim) { + piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ + pixZero(piximi, &empty); + pixDestroy(&piximi); + if (empty) + return ERROR_INT("pixim all fg; no background", procName, 1); + pixZero(pixim, &empty); + if (!empty) /* there are fg pixels in pixim */ + fgpixels = 1; + } + + /* Generate the foreground mask, pixf, which is at + * full resolution. These pixels will be ignored when + * computing the background values. */ + pixb = pixThresholdToBinary(pixs, thresh); + pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); + pixDestroy(&pixb); + + + /* ------------- Set up the output map pixd --------------- */ + /* Generate pixd, which is reduced by the factors (sx, sy). */ + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + wd = (w + sx - 1) / sx; + hd = (h + sy - 1) / sy; + pixd = pixCreate(wd, hd, 8); + + /* Note: we only compute map values in tiles that are complete. + * In general, tiles at right and bottom edges will not be + * complete, and we must fill them in later. */ + nx = w / sx; + ny = h / sy; + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + wplf = pixGetWpl(pixf); + dataf = pixGetData(pixf); + for (i = 0; i < ny; i++) { + lines = datas + sy * i * wpls; + linef = dataf + sy * i * wplf; + lined = datad + i * wpld; + for (j = 0; j < nx; j++) { + delx = j * sx; + sum = 0; + count = 0; + for (k = 0; k < sy; k++) { + for (m = 0; m < sx; m++) { + if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { + sum += GET_DATA_BYTE(lines + k * wpls, delx + m); + count++; + } + } + } + if (count >= mincount) { + val8 = sum / count; + SET_DATA_BYTE(lined, j, val8); + } + } + } + pixDestroy(&pixf); + + /* If there is an optional mask with fg pixels, erase the previous + * calculation for the corresponding map pixels, setting the + * map values to 0. Then, when all the map holes are filled, + * these erased pixels will be set by the surrounding map values. + * + * The calculation here is relatively efficient: for each pixel + * in pixd (which corresponds to a tile of mask pixels in pixim) + * we look only at the pixel in pixim that is at the center + * of the tile. If the mask pixel is ON, we reset the map + * pixel in pixd to 0, so that it can later be filled in. */ + pixims = NULL; + if (pixim && fgpixels) { + wim = pixGetWidth(pixim); + him = pixGetHeight(pixim); + dataim = pixGetData(pixim); + wplim = pixGetWpl(pixim); + for (i = 0; i < ny; i++) { + yim = i * sy + sy / 2; + if (yim >= him) + break; + lineim = dataim + yim * wplim; + for (j = 0; j < nx; j++) { + xim = j * sx + sx / 2; + if (xim >= wim) + break; + if (GET_DATA_BIT(lineim, xim)) + pixSetPixel(pixd, j, i, 0); + } + } + } + + /* Fill all the holes in the map. */ + if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) { + pixDestroy(&pixd); + L_WARNING("can't make the map\n", procName); + return 1; + } + + /* Finally, for each connected region corresponding to the + * 'image' mask, reset all pixels to their average value. + * Each of these components represents an image (or part of one) + * in the input, and this smooths the background values + * in each of these regions. */ + if (pixim && fgpixels) { + scalex = 1. / (l_float32)sx; + scaley = 1. / (l_float32)sy; + pixims = pixScaleBySampling(pixim, scalex, scaley); + pixSmoothConnectedRegions(pixd, pixims, 2); + pixDestroy(&pixims); + } + + *ppixd = pixd; + pixCopyResolution(*ppixd, pixs); + return 0; +} + + +/*! + * \brief pixGetBackgroundRGBMap() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it + * should not have all foreground pixels + * \param[in] pixg [optional] 8 bpp grayscale version; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[out] ppixmr red component map + * \param[out] ppixmg green component map + * \param[out] ppixmb blue component map + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If pixg, which is a grayscale version of pixs, is provided,
+ *          use this internally to generate the foreground mask.
+ *          Otherwise, a grayscale version of pixs will be generated
+ *          from the green component only, used, and destroyed.
+ * 
+ */ +l_ok +pixGetBackgroundRGBMap(PIX *pixs, + PIX *pixim, + PIX *pixg, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + PIX **ppixmr, + PIX **ppixmg, + PIX **ppixmb) +{ +l_int32 w, h, wm, hm, wim, him, wpls, wplim, wplf; +l_int32 xim, yim, delx, nx, ny, i, j, k, m; +l_int32 count, rsum, gsum, bsum, rval, gval, bval; +l_int32 empty, fgpixels; +l_uint32 pixel; +l_uint32 *datas, *dataim, *dataf, *lines, *lineim, *linef; +l_float32 scalex, scaley; +PIX *piximi, *pixgc, *pixb, *pixf, *pixims; +PIX *pixmr, *pixmg, *pixmb; + + PROCNAME("pixGetBackgroundRGBMap"); + + if (!ppixmr || !ppixmg || !ppixmb) + return ERROR_INT("&pixm* not all defined", procName, 1); + *ppixmr = *ppixmg = *ppixmb = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (sx < 4 || sy < 4) + return ERROR_INT("sx and sy must be >= 4", procName, 1); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* Evaluate the mask pixim and make sure it is not all foreground */ + fgpixels = 0; /* boolean for existence of fg mask pixels */ + if (pixim) { + piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ + pixZero(piximi, &empty); + pixDestroy(&piximi); + if (empty) + return ERROR_INT("pixim all fg; no background", procName, 1); + pixZero(pixim, &empty); + if (!empty) /* there are fg pixels in pixim */ + fgpixels = 1; + } + + /* Generate the foreground mask. These pixels will be + * ignored when computing the background values. */ + if (pixg) /* use the input grayscale version if it is provided */ + pixgc = pixClone(pixg); + else + pixgc = pixConvertRGBToGrayFast(pixs); + pixb = pixThresholdToBinary(pixgc, thresh); + pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); + pixDestroy(&pixgc); + pixDestroy(&pixb); + + /* Generate the output mask images */ + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + wm = (w + sx - 1) / sx; + hm = (h + sy - 1) / sy; + pixmr = pixCreate(wm, hm, 8); + pixmg = pixCreate(wm, hm, 8); + pixmb = pixCreate(wm, hm, 8); + + /* ------------- Set up the mapping images --------------- */ + /* Note: we only compute map values in tiles that are complete. + * In general, tiles at right and bottom edges will not be + * complete, and we must fill them in later. */ + nx = w / sx; + ny = h / sy; + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wplf = pixGetWpl(pixf); + dataf = pixGetData(pixf); + for (i = 0; i < ny; i++) { + lines = datas + sy * i * wpls; + linef = dataf + sy * i * wplf; + for (j = 0; j < nx; j++) { + delx = j * sx; + rsum = gsum = bsum = 0; + count = 0; + for (k = 0; k < sy; k++) { + for (m = 0; m < sx; m++) { + if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { + pixel = *(lines + k * wpls + delx + m); + rsum += (pixel >> 24); + gsum += ((pixel >> 16) & 0xff); + bsum += ((pixel >> 8) & 0xff); + count++; + } + } + } + if (count >= mincount) { + rval = rsum / count; + gval = gsum / count; + bval = bsum / count; + pixSetPixel(pixmr, j, i, rval); + pixSetPixel(pixmg, j, i, gval); + pixSetPixel(pixmb, j, i, bval); + } + } + } + pixDestroy(&pixf); + + /* If there is an optional mask with fg pixels, erase the previous + * calculation for the corresponding map pixels, setting the + * map values in each of the 3 color maps to 0. Then, when + * all the map holes are filled, these erased pixels will + * be set by the surrounding map values. */ + if (pixim) { + wim = pixGetWidth(pixim); + him = pixGetHeight(pixim); + dataim = pixGetData(pixim); + wplim = pixGetWpl(pixim); + for (i = 0; i < ny; i++) { + yim = i * sy + sy / 2; + if (yim >= him) + break; + lineim = dataim + yim * wplim; + for (j = 0; j < nx; j++) { + xim = j * sx + sx / 2; + if (xim >= wim) + break; + if (GET_DATA_BIT(lineim, xim)) { + pixSetPixel(pixmr, j, i, 0); + pixSetPixel(pixmg, j, i, 0); + pixSetPixel(pixmb, j, i, 0); + } + } + } + } + + /* ----------------- Now fill in the holes ----------------------- */ + if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || + pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || + pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + L_WARNING("can't make the maps\n", procName); + return 1; + } + + /* Finally, for each connected region corresponding to the + * fg mask, reset all pixels to their average value. */ + if (pixim && fgpixels) { + scalex = 1. / (l_float32)sx; + scaley = 1. / (l_float32)sy; + pixims = pixScaleBySampling(pixim, scalex, scaley); + pixSmoothConnectedRegions(pixmr, pixims, 2); + pixSmoothConnectedRegions(pixmg, pixims, 2); + pixSmoothConnectedRegions(pixmb, pixims, 2); + pixDestroy(&pixims); + } + + *ppixmr = pixmr; + *ppixmg = pixmg; + *ppixmb = pixmb; + pixCopyResolution(*ppixmr, pixs); + pixCopyResolution(*ppixmg, pixs); + pixCopyResolution(*ppixmb, pixs); + return 0; +} + + +/*! + * \brief pixGetBackgroundGrayMapMorph() + * + * \param[in] pixs 8 bpp grayscale; not cmapped + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it + * should not have all foreground pixels + * \param[in] reduction factor at which closing is performed + * \param[in] size of square Sel for the closing; use an odd number + * \param[out] ppixm grayscale map + * \return 0 if OK, 1 on error + */ +l_ok +pixGetBackgroundGrayMapMorph(PIX *pixs, + PIX *pixim, + l_int32 reduction, + l_int32 size, + PIX **ppixm) +{ +l_int32 nx, ny, empty, fgpixels; +l_float32 scale; +PIX *pixm, *pix1, *pix2, *pix3, *pixims; + + PROCNAME("pixGetBackgroundGrayMapMorph"); + + if (!ppixm) + return ERROR_INT("&pixm not defined", procName, 1); + *ppixm = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + + /* Evaluate the mask pixim and make sure it is not all foreground. */ + fgpixels = 0; /* boolean for existence of fg mask pixels */ + if (pixim) { + pixInvert(pixim, pixim); /* set background pixels to 1 */ + pixZero(pixim, &empty); + if (empty) + return ERROR_INT("pixim all fg; no background", procName, 1); + pixInvert(pixim, pixim); /* revert to original mask */ + pixZero(pixim, &empty); + if (!empty) /* there are fg pixels in pixim */ + fgpixels = 1; + } + + /* Downscale as requested and do the closing to get the background. */ + scale = 1. / (l_float32)reduction; + pix1 = pixScaleBySampling(pixs, scale, scale); + pix2 = pixCloseGray(pix1, size, size); + pix3 = pixExtendByReplication(pix2, 1, 1); + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* Downscale the image mask, if any, and remove it from the + * background. These pixels will be filled in (twice). */ + pixims = NULL; + if (pixim) { + pixims = pixScale(pixim, scale, scale); + pixm = pixConvertTo8(pixims, FALSE); + pixAnd(pixm, pixm, pix3); + } + else + pixm = pixClone(pix3); + pixDestroy(&pix3); + + /* Fill all the holes in the map. */ + nx = pixGetWidth(pixs) / reduction; + ny = pixGetHeight(pixs) / reduction; + if (pixFillMapHoles(pixm, nx, ny, L_FILL_BLACK)) { + pixDestroy(&pixm); + pixDestroy(&pixims); + L_WARNING("can't make the map\n", procName); + return 1; + } + + /* Finally, for each connected region corresponding to the + * fg mask, reset all pixels to their average value. */ + if (pixim && fgpixels) + pixSmoothConnectedRegions(pixm, pixims, 2); + pixDestroy(&pixims); + + *ppixm = pixm; + pixCopyResolution(*ppixm, pixs); + return 0; +} + + +/*! + * \brief pixGetBackgroundRGBMapMorph() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it + * should not have all foreground pixels + * \param[in] reduction factor at which closing is performed + * \param[in] size of square Sel for the closing; use an odd number + * \param[out] ppixmr red component map + * \param[out] ppixmg green component map + * \param[out] ppixmb blue component map + * \return 0 if OK, 1 on error + */ +l_ok +pixGetBackgroundRGBMapMorph(PIX *pixs, + PIX *pixim, + l_int32 reduction, + l_int32 size, + PIX **ppixmr, + PIX **ppixmg, + PIX **ppixmb) +{ +l_int32 nx, ny, empty, fgpixels; +l_float32 scale; +PIX *pixm, *pixmr, *pixmg, *pixmb, *pix1, *pix2, *pix3, *pixims; + + PROCNAME("pixGetBackgroundRGBMapMorph"); + + if (!ppixmr || !ppixmg || !ppixmb) + return ERROR_INT("&pixm* not all defined", procName, 1); + *ppixmr = *ppixmg = *ppixmb = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + + /* Evaluate the mask pixim and make sure it is not all foreground. */ + fgpixels = 0; /* boolean for existence of fg mask pixels */ + if (pixim) { + pixInvert(pixim, pixim); /* set background pixels to 1 */ + pixZero(pixim, &empty); + if (empty) + return ERROR_INT("pixim all fg; no background", procName, 1); + pixInvert(pixim, pixim); /* revert to original mask */ + pixZero(pixim, &empty); + if (!empty) /* there are fg pixels in pixim */ + fgpixels = 1; + } + + /* Generate an 8 bpp version of the image mask, if it exists */ + scale = 1. / (l_float32)reduction; + pixims = NULL; + pixm = NULL; + if (pixim) { + pixims = pixScale(pixim, scale, scale); + pixm = pixConvertTo8(pixims, FALSE); + } + + /* Downscale as requested and do the closing to get the background. + * Then remove the image mask pixels from the background. They + * will be filled in (twice) later. Do this for all 3 components. */ + pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_RED); + pix2 = pixCloseGray(pix1, size, size); + pix3 = pixExtendByReplication(pix2, 1, 1); + if (pixim) + pixmr = pixAnd(NULL, pixm, pix3); + else + pixmr = pixClone(pix3); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_GREEN); + pix2 = pixCloseGray(pix1, size, size); + pix3 = pixExtendByReplication(pix2, 1, 1); + if (pixim) + pixmg = pixAnd(NULL, pixm, pix3); + else + pixmg = pixClone(pix3); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_BLUE); + pix2 = pixCloseGray(pix1, size, size); + pix3 = pixExtendByReplication(pix2, 1, 1); + if (pixim) + pixmb = pixAnd(NULL, pixm, pix3); + else + pixmb = pixClone(pix3); + pixDestroy(&pixm); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + /* Fill all the holes in the three maps. */ + nx = pixGetWidth(pixs) / reduction; + ny = pixGetHeight(pixs) / reduction; + if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || + pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || + pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { + pixDestroy(&pixmr); + pixDestroy(&pixmg); + pixDestroy(&pixmb); + pixDestroy(&pixims); + L_WARNING("can't make the maps\n", procName); + return 1; + } + + /* Finally, for each connected region corresponding to the + * fg mask in each component, reset all pixels to their + * average value. */ + if (pixim && fgpixels) { + pixSmoothConnectedRegions(pixmr, pixims, 2); + pixSmoothConnectedRegions(pixmg, pixims, 2); + pixSmoothConnectedRegions(pixmb, pixims, 2); + pixDestroy(&pixims); + } + + *ppixmr = pixmr; + *ppixmg = pixmg; + *ppixmb = pixmb; + pixCopyResolution(*ppixmr, pixs); + pixCopyResolution(*ppixmg, pixs); + pixCopyResolution(*ppixmb, pixs); + return 0; +} + + +/*! + * \brief pixFillMapHoles() + * + * \param[in] pix 8 bpp; a map, with one pixel for each tile in + * a larger image + * \param[in] nx number of horizontal pixel tiles that are entirely + * covered with pixels in the original source image + * \param[in] ny ditto for the number of vertical pixel tiles + * \param[in] filltype L_FILL_WHITE or L_FILL_BLACK + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation on pix (the map).  pix is
+ *          typically a low-resolution version of some other image
+ *          from which it was derived, where each pixel in pix
+ *          corresponds to a rectangular tile (say, m x n) of pixels
+ *          in the larger image.  All we need to know about the larger
+ *          image is whether or not the rightmost column and bottommost
+ *          row of pixels in pix correspond to tiles that are
+ *          only partially covered by pixels in the larger image.
+ *      (2) Typically, some number of pixels in the input map are
+ *          not known, and their values must be determined by near
+ *          pixels that are known.  These unknown pixels are the 'holes'.
+ *          They can take on only two values, 0 and 255, and the
+ *          instruction about which to fill is given by the filltype flag.
+ *      (3) The "holes" can come from two sources.  The first is when there
+ *          are not enough foreground or background pixels in a tile;
+ *          the second is when a tile is at least partially covered
+ *          by an image mask.  If we're filling holes in a fg mask,
+ *          the holes are initialized to black (0) and use L_FILL_BLACK.
+ *          For filling holes in a bg mask, initialize the holes to
+ *          white (255) and use L_FILL_WHITE.
+ *      (4) If w is the map width, nx = w or nx = w - 1; ditto for h and ny.
+ * 
+ */ +l_ok +pixFillMapHoles(PIX *pix, + l_int32 nx, + l_int32 ny, + l_int32 filltype) +{ +l_int32 w, h, y, nmiss, goodcol, i, j, found, ival, valtest; +l_uint32 val, lastval; +NUMA *na; /* indicates if there is any data in the column */ +PIX *pixt; + + PROCNAME("pixFillMapHoles"); + + if (!pix || pixGetDepth(pix) != 8) + return ERROR_INT("pix not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pix)) + return ERROR_INT("pix is colormapped", procName, 1); + + /* ------------- Fill holes in the mapping image columns ----------- */ + pixGetDimensions(pix, &w, &h, NULL); + na = numaCreate(0); /* holds flag for which columns have data */ + nmiss = 0; + valtest = (filltype == L_FILL_WHITE) ? 255 : 0; + for (j = 0; j < nx; j++) { /* do it by columns */ + found = FALSE; + for (i = 0; i < ny; i++) { + pixGetPixel(pix, j, i, &val); + if (val != valtest) { + y = i; + found = TRUE; + break; + } + } + if (found == FALSE) { + numaAddNumber(na, 0); /* no data in the column */ + nmiss++; + } + else { + numaAddNumber(na, 1); /* data in the column */ + for (i = y - 1; i >= 0; i--) /* replicate upwards to top */ + pixSetPixel(pix, j, i, val); + pixGetPixel(pix, j, 0, &lastval); + for (i = 1; i < h; i++) { /* set going down to bottom */ + pixGetPixel(pix, j, i, &val); + if (val == valtest) + pixSetPixel(pix, j, i, lastval); + else + lastval = val; + } + } + } + numaAddNumber(na, 0); /* last column */ + + if (nmiss == nx) { /* no data in any column! */ + numaDestroy(&na); + L_WARNING("no bg found; no data in any column\n", procName); + return 1; + } + + /* ---------- Fill in missing columns by replication ----------- */ + if (nmiss > 0) { /* replicate columns */ + pixt = pixCopy(NULL, pix); + /* Find the first good column */ + goodcol = 0; + for (j = 0; j < w; j++) { + numaGetIValue(na, j, &ival); + if (ival == 1) { + goodcol = j; + break; + } + } + if (goodcol > 0) { /* copy cols backward */ + for (j = goodcol - 1; j >= 0; j--) { + pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j + 1, 0); + pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); + } + } + for (j = goodcol + 1; j < w; j++) { /* copy cols forward */ + numaGetIValue(na, j, &ival); + if (ival == 0) { + /* Copy the column to the left of j */ + pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j - 1, 0); + pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); + } + } + pixDestroy(&pixt); + } + if (w > nx) { /* replicate the last column */ + for (i = 0; i < h; i++) { + pixGetPixel(pix, w - 2, i, &val); + pixSetPixel(pix, w - 1, i, val); + } + } + + numaDestroy(&na); + return 0; +} + + +/*! + * \brief pixExtendByReplication() + * + * \param[in] pixs 8 bpp + * \param[in] addw number of extra pixels horizontally to add + * \param[in] addh number of extra pixels vertically to add + * \return pixd extended with replicated pixel values, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixel values are extended to the left and down, as required.
+ * 
+ */ +PIX * +pixExtendByReplication(PIX *pixs, + l_int32 addw, + l_int32 addh) +{ +l_int32 w, h, i, j; +l_uint32 val; +PIX *pixd; + + PROCNAME("pixExtendByReplication"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + if (addw == 0 && addh == 0) + return pixCopy(NULL, pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w + addw, h + addh, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); + + if (addw > 0) { + for (i = 0; i < h; i++) { + pixGetPixel(pixd, w - 1, i, &val); + for (j = 0; j < addw; j++) + pixSetPixel(pixd, w + j, i, val); + } + } + + if (addh > 0) { + for (j = 0; j < w + addw; j++) { + pixGetPixel(pixd, j, h - 1, &val); + for (i = 0; i < addh; i++) + pixSetPixel(pixd, j, h + i, val); + } + } + + pixCopyResolution(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixSmoothConnectedRegions() + * + * \param[in] pixs 8 bpp grayscale; no colormap + * \param[in] pixm [optional] 1 bpp; if null, this is a no-op + * \param[in] factor subsampling factor for getting average; >= 1 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pixels in pixs corresponding to those in each
+ *          8-connected region in the mask are set to the average value.
+ *      (2) This is required for adaptive mapping to avoid the
+ *          generation of stripes in the background map, due to
+ *          variations in the pixel values near the edges of mask regions.
+ *      (3) This function is optimized for background smoothing, where
+ *          there are a relatively small number of components.  It will
+ *          be inefficient if used where there are many small components.
+ * 
+ */ +l_ok +pixSmoothConnectedRegions(PIX *pixs, + PIX *pixm, + l_int32 factor) +{ +l_int32 empty, i, n, x, y; +l_float32 aveval; +BOXA *boxa; +PIX *pixmc; +PIXA *pixa; + + PROCNAME("pixSmoothConnectedRegions"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs has colormap", procName, 1); + if (!pixm) { + L_INFO("pixm not defined\n", procName); + return 0; + } + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + pixZero(pixm, &empty); + if (empty) { + L_INFO("pixm has no fg pixels; nothing to do\n", procName); + return 0; + } + + boxa = pixConnComp(pixm, &pixa, 8); + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + if ((pixmc = pixaGetPix(pixa, i, L_CLONE)) == NULL) { + L_WARNING("missing pixmc!\n", procName); + continue; + } + boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); + pixGetAverageMasked(pixs, pixmc, x, y, factor, L_MEAN_ABSVAL, &aveval); + pixPaintThroughMask(pixs, pixmc, x, y, (l_int32)aveval); + pixDestroy(&pixmc); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return 0; +} + + +/*------------------------------------------------------------------* + * Measurement of local foreground * + *------------------------------------------------------------------*/ +#if 0 /* Not working properly: do not use */ + +/*! + * \brief pixGetForegroundGrayMap() + * + * \param[in] pixs 8 bpp + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] sx, sy src tile size, in pixels + * \param[in] thresh threshold for determining foreground + * \param[out] ppixd 8 bpp grayscale map + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Each (sx, sy) tile of pixs gets mapped to one pixel in pixd.
+ *      (2) pixd is the estimate of the fg (darkest) value within each tile.
+ *      (3) All pixels in pixd that are in 'image' regions, as specified
+ *          by pixim, are given the background value 0.
+ *      (4) For pixels in pixd that can't directly be given a fg value,
+ *          the value is inferred by propagating from neighboring pixels.
+ *      (5) In practice, pixd can be used to normalize the fg, and
+ *          it can be done after background normalization.
+ *      (6) The overall procedure is:
+ *            ~ reduce 2x by sampling
+ *            ~ paint all 'image' pixels white, so that they don't
+ *            ~ participate in the Min reduction
+ *            ~ do a further (sx, sy) Min reduction -- think of
+ *              it as a large opening followed by subsampling by the
+ *              reduction factors
+ *            ~ threshold the result to identify fg, and set the
+ *              bg pixels to 255 (these are 'holes')
+ *            ~ fill holes by propagation from fg values
+ *            ~ replicatively expand by 2x, arriving at the final
+ *              resolution of pixd
+ *            ~ smooth with a 17x17 kernel
+ *            ~ paint the 'image' regions black
+ * 
+ */ +l_ok +pixGetForegroundGrayMap(PIX *pixs, + PIX *pixim, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + PIX **ppixd) +{ +l_int32 w, h, d, wd, hd; +l_int32 empty, fgpixels; +PIX *pixd, *piximi, *pixim2, *pixims, *pixs2, *pixb, *pixt1, *pixt2, *pixt3; + + PROCNAME("pixGetForegroundGrayMap"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return ERROR_INT("pixs not 8 bpp", procName, 1); + if (pixim && pixGetDepth(pixim) != 1) + return ERROR_INT("pixim not 1 bpp", procName, 1); + if (sx < 2 || sy < 2) + return ERROR_INT("sx and sy must be >= 2", procName, 1); + + /* Generate pixd, which is reduced by the factors (sx, sy). */ + wd = (w + sx - 1) / sx; + hd = (h + sy - 1) / sy; + pixd = pixCreate(wd, hd, 8); + *ppixd = pixd; + + /* Evaluate the 'image' mask, pixim. If it is all fg, + * the output pixd has all pixels with value 0. */ + fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ + if (pixim) { + piximi = pixInvert(NULL, pixim); /* set non-image pixels to 1 */ + pixZero(piximi, &empty); + pixDestroy(&piximi); + if (empty) /* all 'image'; return with all pixels set to 0 */ + return 0; + pixZero(pixim, &empty); + if (!empty) /* there are fg pixels in pixim */ + fgpixels = 1; + } + + /* 2x subsampling; paint white through 'image' mask. */ + pixs2 = pixScaleBySampling(pixs, 0.5, 0.5); + if (pixim && fgpixels) { + pixim2 = pixReduceBinary2(pixim, NULL); + pixPaintThroughMask(pixs2, pixim2, 0, 0, 255); + pixDestroy(&pixim2); + } + + /* Min (erosion) downscaling; total reduction (4 sx, 4 sy). */ + pixt1 = pixScaleGrayMinMax(pixs2, sx, sy, L_CHOOSE_MIN); + +/* pixDisplay(pixt1, 300, 200); */ + + /* Threshold to identify fg; paint bg pixels to white. */ + pixb = pixThresholdToBinary(pixt1, thresh); /* fg pixels */ + pixInvert(pixb, pixb); + pixPaintThroughMask(pixt1, pixb, 0, 0, 255); + pixDestroy(&pixb); + + /* Replicative expansion by 2x to (sx, sy). */ + pixt2 = pixExpandReplicate(pixt1, 2); + +/* pixDisplay(pixt2, 500, 200); */ + + /* Fill holes in the fg by propagation */ + pixFillMapHoles(pixt2, w / sx, h / sy, L_FILL_WHITE); + +/* pixDisplay(pixt2, 700, 200); */ + + /* Smooth with 17x17 kernel. */ + pixt3 = pixBlockconv(pixt2, 8, 8); + pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixt3, 0, 0); + + /* Paint the image parts black. */ + pixims = pixScaleBySampling(pixim, 1. / sx, 1. / sy); + pixPaintThroughMask(pixd, pixims, 0, 0, 0); + + pixDestroy(&pixs2); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + return 0; +} +#endif /* Not working properly: do not use */ + + +/*------------------------------------------------------------------* + * Generate inverted background map * + *------------------------------------------------------------------*/ +/*! + * \brief pixGetInvBackgroundMap() + * + * \param[in] pixs 8 bpp grayscale; no colormap + * \param[in] bgval target bg val; typ. > 128 + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \return pixd 16 bpp, or NULL on error + * + *
+ * Notes:
+ *     (1) bgval should typically be > 120 and < 240
+ *     (2) pixd is a normalization image; the original image is
+ *       multiplied by pixd and the result is divided by 256.
+ * 
+ */ +PIX * +pixGetInvBackgroundMap(PIX *pixs, + l_int32 bgval, + l_int32 smoothx, + l_int32 smoothy) +{ +l_int32 w, h, wplsm, wpld, i, j; +l_int32 val, val16; +l_uint32 *datasm, *datad, *linesm, *lined; +PIX *pixsm, *pixd; + + PROCNAME("pixGetInvBackgroundMap"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < 5 || h < 5) + return (PIX *)ERROR_PTR("w and h must be >= 5", procName, NULL); + + /* smooth the map image */ + pixsm = pixBlockconv(pixs, smoothx, smoothy); + datasm = pixGetData(pixsm); + wplsm = pixGetWpl(pixsm); + + /* invert the map image, scaling up to preserve dynamic range */ + pixd = pixCreate(w, h, 16); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linesm = datasm + i * wplsm; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linesm, j); + if (val > 0) + val16 = (256 * bgval) / val; + else { /* shouldn't happen */ + L_WARNING("smoothed bg has 0 pixel!\n", procName); + val16 = bgval / 2; + } + SET_DATA_TWO_BYTES(lined, j, val16); + } + } + + pixDestroy(&pixsm); + pixCopyResolution(pixd, pixs); + return pixd; +} + + +/*------------------------------------------------------------------* + * Apply background map to image * + *------------------------------------------------------------------*/ +/*! + * \brief pixApplyInvBackgroundGrayMap() + * + * \param[in] pixs 8 bpp grayscale; no colormap + * \param[in] pixm 16 bpp, inverse background map + * \param[in] sx tile width in pixels + * \param[in] sy tile height in pixels + * \return pixd 8 bpp, or NULL on error + */ +PIX * +pixApplyInvBackgroundGrayMap(PIX *pixs, + PIX *pixm, + l_int32 sx, + l_int32 sy) +{ +l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; +l_int32 vals, vald; +l_uint32 val16; +l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; +PIX *pixd; + + PROCNAME("pixApplyInvBackgroundGrayMap"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 16) + return (PIX *)ERROR_PTR("pixm undefined or not 16 bpp", procName, NULL); + if (sx == 0 || sy == 0) + return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + pixGetDimensions(pixm, &wm, &hm, NULL); + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < hm; i++) { + lines = datas + sy * i * wpls; + lined = datad + sy * i * wpld; + yoff = sy * i; + for (j = 0; j < wm; j++) { + pixGetPixel(pixm, j, i, &val16); + xoff = sx * j; + for (k = 0; k < sy && yoff + k < h; k++) { + flines = lines + k * wpls; + flined = lined + k * wpld; + for (m = 0; m < sx && xoff + m < w; m++) { + vals = GET_DATA_BYTE(flines, xoff + m); + vald = (vals * val16) / 256; + vald = L_MIN(vald, 255); + SET_DATA_BYTE(flined, xoff + m, vald); + } + } + } + } + + return pixd; +} + + +/*! + * \brief pixApplyInvBackgroundRGBMap() + * + * \param[in] pixs 32 bpp rbg + * \param[in] pixmr 16 bpp, red inverse background map + * \param[in] pixmg 16 bpp, green inverse background map + * \param[in] pixmb 16 bpp, blue inverse background map + * \param[in] sx tile width in pixels + * \param[in] sy tile height in pixels + * \return pixd 32 bpp rbg, or NULL on error + */ +PIX * +pixApplyInvBackgroundRGBMap(PIX *pixs, + PIX *pixmr, + PIX *pixmg, + PIX *pixmb, + l_int32 sx, + l_int32 sy) +{ +l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; +l_int32 rvald, gvald, bvald; +l_uint32 vals; +l_uint32 rval16, gval16, bval16; +l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; +PIX *pixd; + + PROCNAME("pixApplyInvBackgroundRGBMap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (!pixmr || !pixmg || !pixmb) + return (PIX *)ERROR_PTR("pix maps not all defined", procName, NULL); + if (pixGetDepth(pixmr) != 16 || pixGetDepth(pixmg) != 16 || + pixGetDepth(pixmb) != 16) + return (PIX *)ERROR_PTR("pix maps not all 16 bpp", procName, NULL); + if (sx == 0 || sy == 0) + return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + wm = pixGetWidth(pixmr); + hm = pixGetHeight(pixmr); + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < hm; i++) { + lines = datas + sy * i * wpls; + lined = datad + sy * i * wpld; + yoff = sy * i; + for (j = 0; j < wm; j++) { + pixGetPixel(pixmr, j, i, &rval16); + pixGetPixel(pixmg, j, i, &gval16); + pixGetPixel(pixmb, j, i, &bval16); + xoff = sx * j; + for (k = 0; k < sy && yoff + k < h; k++) { + flines = lines + k * wpls; + flined = lined + k * wpld; + for (m = 0; m < sx && xoff + m < w; m++) { + vals = *(flines + xoff + m); + rvald = ((vals >> 24) * rval16) / 256; + rvald = L_MIN(rvald, 255); + gvald = (((vals >> 16) & 0xff) * gval16) / 256; + gvald = L_MIN(gvald, 255); + bvald = (((vals >> 8) & 0xff) * bval16) / 256; + bvald = L_MIN(bvald, 255); + composeRGBPixel(rvald, gvald, bvald, flined + xoff + m); + } + } + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Apply variable map * + *------------------------------------------------------------------*/ +/*! + * \brief pixApplyVariableGrayMap() + * + * \param[in] pixs 8 bpp + * \param[in] pixg 8 bpp, variable map + * \param[in] target typ. 128 for threshold + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Suppose you have an image that you want to transform based
+ *          on some photometric measurement at each point, such as the
+ *          threshold value for binarization.  Representing the photometric
+ *          measurement as an image pixg, you can threshold in input image
+ *          using pixVarThresholdToBinary().  Alternatively, you can map
+ *          the input image pointwise so that the threshold over the
+ *          entire image becomes a constant, such as 128.  For example,
+ *          if a pixel in pixg is 150 and the target is 128, the
+ *          corresponding pixel in pixs is mapped linearly to a value
+ *          (128/150) of the input value.  If the resulting mapped image
+ *          pixd were then thresholded at 128, you would obtain the
+ *          same result as a direct binarization using pixg with
+ *          pixVarThresholdToBinary().
+ *      (2) The sizes of pixs and pixg must be equal.
+ * 
+ */ +PIX * +pixApplyVariableGrayMap(PIX *pixs, + PIX *pixg, + l_int32 target) +{ +l_int32 i, j, w, h, d, wpls, wplg, wpld, vals, valg, vald; +l_uint8 *lut; +l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; +l_float32 fval; +PIX *pixd; + + PROCNAME("pixApplyVariableGrayMap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixg) + return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); + if (!pixSizesEqual(pixs, pixg)) + return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL); + + /* Generate a LUT for the mapping if the image is large enough + * to warrant the overhead. The LUT is of size 2^16. For the + * index to the table, get the MSB from pixs and the LSB from pixg. + * Note: this LUT is bigger than the typical 32K L1 cache, so + * we expect cache misses. L2 latencies are about 5ns. But + * division is slooooow. For large images, this function is about + * 4x faster when using the LUT. C'est la vie. */ + lut = NULL; + if (w * h > 100000) { /* more pixels than 2^16 */ + if ((lut = (l_uint8 *)LEPT_CALLOC(0x10000, sizeof(l_uint8))) == NULL) + return (PIX *)ERROR_PTR("lut not made", procName, NULL); + for (i = 0; i < 256; i++) { + for (j = 0; j < 256; j++) { + fval = (l_float32)(i * target) / (j + 0.5); + lut[(i << 8) + j] = L_MIN(255, (l_int32)(fval + 0.5)); + } + } + } + + if ((pixd = pixCreateNoInit(w, h, 8)) == NULL) { + LEPT_FREE(lut); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lineg = datag + i * wplg; + lined = datad + i * wpld; + if (lut) { + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valg = GET_DATA_BYTE(lineg, j); + vald = lut[(vals << 8) + valg]; + SET_DATA_BYTE(lined, j, vald); + } + } + else { + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valg = GET_DATA_BYTE(lineg, j); + fval = (l_float32)(vals * target) / (valg + 0.5); + vald = L_MIN(255, (l_int32)(fval + 0.5)); + SET_DATA_BYTE(lined, j, vald); + } + } + } + + LEPT_FREE(lut); + return pixd; +} + + +/*------------------------------------------------------------------* + * Non-adaptive (global) mapping * + *------------------------------------------------------------------*/ +/*! + * \brief pixGlobalNormRGB() + * + * \param[in] pixd [optional] null, existing or equal to pixs + * \param[in] pixs 32 bpp rgb, or colormapped + * \param[in] rval, gval, bval pixel values in pixs that are + * linearly mapped to mapval + * \param[in] mapval use 255 for mapping to white + * \return pixd 32 bpp rgb or colormapped, or NULL on error + * + *
+ * Notes:
+ *    (1) The value of pixd determines if the results are written to a
+ *        new pix (use NULL), in-place to pixs (use pixs), or to some
+ *        other existing pix.
+ *    (2) This does a global normalization of an image where the
+ *        r,g,b color components are not balanced.  Thus, white in pixs is
+ *        represented by a set of r,g,b values that are not all 255.
+ *    (3) The input values (rval, gval, bval) should be chosen to
+ *        represent the gray color (mapval, mapval, mapval) in src.
+ *        Thus, this function will map (rval, gval, bval) to that gray color.
+ *    (4) Typically, mapval = 255, so that (rval, gval, bval)
+ *        corresponds to the white point of src.  In that case, these
+ *        parameters should be chosen so that few pixels have higher values.
+ *    (5) In all cases, we do a linear TRC separately on each of the
+ *        components, saturating at 255.
+ *    (6) If the input pix is 8 bpp without a colormap, you can get
+ *        this functionality with mapval = 255 by calling:
+ *            pixGammaTRC(pixd, pixs, 1.0, 0, bgval);
+ *        where bgval is the value you want to be mapped to 255.
+ *        Or more generally, if you want bgval to be mapped to mapval:
+ *            pixGammaTRC(pixd, pixs, 1.0, 0, 255 * bgval / mapval);
+ * 
+ */ +PIX * +pixGlobalNormRGB(PIX *pixd, + PIX *pixs, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 mapval) +{ +l_int32 w, h, d, i, j, ncolors, rv, gv, bv, wpl; +l_int32 *rarray, *garray, *barray; +l_uint32 *data, *line; +NUMA *nar, *nag, *nab; +PIXCMAP *cmap; + + PROCNAME("pixGlobalNormRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + cmap = pixGetColormap(pixs); + pixGetDimensions(pixs, &w, &h, &d); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (mapval <= 0) { + L_WARNING("mapval must be > 0; setting to 255\n", procName); + mapval = 255; + } + + /* Prepare pixd to be a copy of pixs */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + /* Generate the TRC maps for each component. Make sure the + * upper range for each color is greater than zero. */ + nar = numaGammaTRC(1.0, 0, L_MAX(1, 255 * rval / mapval)); + nag = numaGammaTRC(1.0, 0, L_MAX(1, 255 * gval / mapval)); + nab = numaGammaTRC(1.0, 0, L_MAX(1, 255 * bval / mapval)); + + /* Extract copies of the internal arrays */ + rarray = numaGetIArray(nar); + garray = numaGetIArray(nag); + barray = numaGetIArray(nab); + if (!nar || !nag || !nab || !rarray || !garray || !barray) { + L_ERROR("allocation failure in arrays\n", procName); + goto cleanup_arrays; + } + + if (cmap) { + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rv, &gv, &bv); + pixcmapResetColor(cmap, i, rarray[rv], garray[gv], barray[bv]); + } + } + else { + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rv, &gv, &bv); + composeRGBPixel(rarray[rv], garray[gv], barray[bv], line + j); + } + } + } + +cleanup_arrays: + numaDestroy(&nar); + numaDestroy(&nag); + numaDestroy(&nab); + LEPT_FREE(rarray); + LEPT_FREE(garray); + LEPT_FREE(barray); + return pixd; +} + + +/*! + * \brief pixGlobalNormNoSatRGB() + * + * \param[in] pixd [optional] null, existing or equal to pixs + * \param[in] pixs 32 bpp rgb + * \param[in] rval, gval, bval pixel values in pixs that are + * linearly mapped to mapval; but see below + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] rank between 0.0 and 1.0; typ. use a value near 1.0 + * \return pixd 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *    (1) This is a version of pixGlobalNormRGB(), where the output
+ *        intensity is scaled back so that a controlled fraction of
+ *        pixel components is allowed to saturate.  See comments in
+ *        pixGlobalNormRGB().
+ *    (2) The value of pixd determines if the results are written to a
+ *        new pix (use NULL), in-place to pixs (use pixs), or to some
+ *        other existing pix.
+ *    (3) This does a global normalization of an image where the
+ *        r,g,b color components are not balanced.  Thus, white in pixs is
+ *        represented by a set of r,g,b values that are not all 255.
+ *    (4) The input values (rval, gval, bval) can be chosen to be the
+ *        color that, after normalization, becomes white background.
+ *        For images that are mostly background, the closer these values
+ *        are to the median component values, the closer the resulting
+ *        background will be to gray, becoming white at the brightest places.
+ *    (5) The mapval used in pixGlobalNormRGB() is computed here to
+ *        avoid saturation of any component in the image (save for a
+ *        fraction of the pixels given by the input rank value).
+ * 
+ */ +PIX * +pixGlobalNormNoSatRGB(PIX *pixd, + PIX *pixs, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 factor, + l_float32 rank) +{ +l_int32 mapval; +l_float32 rankrval, rankgval, rankbval; +l_float32 rfract, gfract, bfract, maxfract; + + PROCNAME("pixGlobalNormNoSatRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank not in [0.0 ... 1.0]", procName, NULL); + if (rval <= 0 || gval <= 0 || bval <= 0) + return (PIX *)ERROR_PTR("invalid estim. color values", procName, NULL); + + /* The max value for each component may be larger than the + * input estimated background value. In that case, mapping + * for those pixels would saturate. To prevent saturation, + * we compute the fraction for each component by which we + * would oversaturate. Then take the max of these, and + * reduce, uniformly over all components, the output intensity + * by this value. Then no component will saturate. + * In practice, if rank < 1.0, a fraction of pixels + * may have a component saturate. By keeping rank close to 1.0, + * that fraction can be made arbitrarily small. */ + pixGetRankValueMaskedRGB(pixs, NULL, 0, 0, factor, rank, &rankrval, + &rankgval, &rankbval); + rfract = rankrval / (l_float32)rval; + gfract = rankgval / (l_float32)gval; + bfract = rankbval / (l_float32)bval; + maxfract = L_MAX(rfract, gfract); + maxfract = L_MAX(maxfract, bfract); +#if DEBUG_GLOBAL + fprintf(stderr, "rankrval = %7.2f, rankgval = %7.2f, rankbval = %7.2f\n", + rankrval, rankgval, rankbval); + fprintf(stderr, "rfract = %7.4f, gfract = %7.4f, bfract = %7.4f\n", + rfract, gfract, bfract); +#endif /* DEBUG_GLOBAL */ + + mapval = (l_int32)(255. / maxfract); + pixd = pixGlobalNormRGB(pixd, pixs, rval, gval, bval, mapval); + return pixd; +} + + +/*------------------------------------------------------------------* + * Adaptive threshold spread normalization * + *------------------------------------------------------------------*/ +/*! + * \brief pixThresholdSpreadNorm() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] filtertype L_SOBEL_EDGE or L_TWO_SIDED_EDGE; + * \param[in] edgethresh threshold on magnitude of edge filter; + * typ 10-20 + * \param[in] smoothx, smoothy half-width of convolution kernel applied to + * spread threshold: use 0 for no smoothing + * \param[in] gamma gamma correction; typ. about 0.7 + * \param[in] minval input value that gives 0 for output; typ. -25 + * \param[in] maxval input value that gives 255 for output; + * typ. 255 + * \param[in] targetthresh target threshold for normalization + * \param[out] ppixth [optional] computed local threshold value + * \param[out] ppixb [optional] thresholded normalized image + * \param[out] ppixd [optional] normalized image + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The basis of this approach is the use of seed spreading
+ *          on a (possibly) sparse set of estimates for the local threshold.
+ *          The resulting dense estimates are smoothed by convolution
+ *          and used to either threshold the input image or normalize it
+ *          with a local transformation that linearly maps the pixels so
+ *          that the local threshold estimate becomes constant over the
+ *          resulting image.  This approach is one of several that
+ *          have been suggested (and implemented) by Ray Smith.
+ *      (2) You can use either the Sobel or TwoSided edge filters.
+ *          The results appear to be similar, using typical values
+ *          of edgethresh in the rang 10-20.
+ *      (3) To skip the trc enhancement, use gamma = 1.0, minval = 0
+ *          and maxval = 255.
+ *      (4) For the normalized image pixd, each pixel is linearly mapped
+ *          in such a way that the local threshold is equal to targetthresh.
+ *      (5) The full width and height of the convolution kernel
+ *          are (2 * smoothx + 1) and (2 * smoothy + 1).
+ *      (6) This function can be used with the pixtiling utility if the
+ *          images are too large.  See pixOtsuAdaptiveThreshold() for
+ *          an example of this.
+ * 
+ */ +l_ok +pixThresholdSpreadNorm(PIX *pixs, + l_int32 filtertype, + l_int32 edgethresh, + l_int32 smoothx, + l_int32 smoothy, + l_float32 gamma, + l_int32 minval, + l_int32 maxval, + l_int32 targetthresh, + PIX **ppixth, + PIX **ppixb, + PIX **ppixd) +{ +PIX *pixe, *pixet, *pixsd, *pixg1, *pixg2, *pixth; + + PROCNAME("pixThresholdSpreadNorm"); + + if (ppixth) *ppixth = NULL; + if (ppixb) *ppixb = NULL; + if (ppixd) *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (!ppixth && !ppixb && !ppixd) + return ERROR_INT("no output requested", procName, 1); + if (filtertype != L_SOBEL_EDGE && filtertype != L_TWO_SIDED_EDGE) + return ERROR_INT("invalid filter type", procName, 1); + + /* Get the thresholded edge pixels. These are the ones + * that have values in pixs near the local optimal fg/bg threshold. */ + if (filtertype == L_SOBEL_EDGE) + pixe = pixSobelEdgeFilter(pixs, L_VERTICAL_EDGES); + else /* L_TWO_SIDED_EDGE */ + pixe = pixTwoSidedEdgeFilter(pixs, L_VERTICAL_EDGES); + pixet = pixThresholdToBinary(pixe, edgethresh); + pixInvert(pixet, pixet); + + /* Build a seed image whose only nonzero values are those + * values of pixs corresponding to pixels in the fg of pixet. */ + pixsd = pixCreateTemplate(pixs); + pixCombineMasked(pixsd, pixs, pixet); + + /* Spread the seed and optionally smooth to reduce noise */ + pixg1 = pixSeedspread(pixsd, 4); + pixg2 = pixBlockconv(pixg1, smoothx, smoothy); + + /* Optionally do a gamma enhancement */ + pixth = pixGammaTRC(NULL, pixg2, gamma, minval, maxval); + + /* Do the mapping and thresholding */ + if (ppixd) { + *ppixd = pixApplyVariableGrayMap(pixs, pixth, targetthresh); + if (ppixb) + *ppixb = pixThresholdToBinary(*ppixd, targetthresh); + } + else if (ppixb) + *ppixb = pixVarThresholdToBinary(pixs, pixth); + + if (ppixth) + *ppixth = pixth; + else + pixDestroy(&pixth); + + pixDestroy(&pixe); + pixDestroy(&pixet); + pixDestroy(&pixsd); + pixDestroy(&pixg1); + pixDestroy(&pixg2); + return 0; +} + + +/*------------------------------------------------------------------* + * Adaptive background normalization (flexible adaptaption) * + *------------------------------------------------------------------*/ +/*! + * \brief pixBackgroundNormFlex() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] sx, sy desired tile dimensions; size may vary; + * use values between 3 and 10 + * \param[in] smoothx, smoothy half-width of convolution kernel applied to + * threshold array: use values between 1 and 3 + * \param[in] delta difference parameter in basin filling; + * use 0 to skip + * \return pixd 8 bpp, background-normalized), or NULL on error + * + *
+ * Notes:
+ *      (1) This does adaptation flexibly to a quickly varying background.
+ *          For that reason, all input parameters should be small.
+ *      (2) sx and sy give the tile size; they should be in [5 - 7].
+ *      (3) The full width and height of the convolution kernel
+ *          are (2 * smoothx + 1) and (2 * smoothy + 1).  They
+ *          should be in [1 - 2].
+ *      (4) Basin filling is used to fill the large fg regions.  The
+ *          parameter %delta measures the height that the black
+ *          background is raised from the local minima.  By raising
+ *          the background, it is possible to threshold the large
+ *          fg regions to foreground.  If %delta is too large,
+ *          bg regions will be lifted, causing thickening of
+ *          the fg regions.  Use 0 to skip.
+ * 
+ */ +PIX * +pixBackgroundNormFlex(PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 smoothx, + l_int32 smoothy, + l_int32 delta) +{ +l_float32 scalex, scaley; +PIX *pixt, *pixsd, *pixmin, *pixbg, *pixbgi, *pixd; + + PROCNAME("pixBackgroundNormFlex"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); + if (sx < 3 || sy < 3) + return (PIX *)ERROR_PTR("sx and/or sy less than 3", procName, NULL); + if (sx > 10 || sy > 10) + return (PIX *)ERROR_PTR("sx and/or sy exceed 10", procName, NULL); + if (smoothx < 1 || smoothy < 1) + return (PIX *)ERROR_PTR("smooth params less than 1", procName, NULL); + if (smoothx > 3 || smoothy > 3) + return (PIX *)ERROR_PTR("smooth params exceed 3", procName, NULL); + + /* Generate the bg estimate using smoothed average with subsampling */ + scalex = 1. / (l_float32)sx; + scaley = 1. / (l_float32)sy; + pixt = pixScaleSmooth(pixs, scalex, scaley); + + /* Do basin filling on the bg estimate if requested */ + if (delta <= 0) + pixsd = pixClone(pixt); + else { + pixLocalExtrema(pixt, 0, 0, &pixmin, NULL); + pixsd = pixSeedfillGrayBasin(pixmin, pixt, delta, 4); + pixDestroy(&pixmin); + } + pixbg = pixExtendByReplication(pixsd, 1, 1); + + /* Map the bg to 200 */ + pixbgi = pixGetInvBackgroundMap(pixbg, 200, smoothx, smoothy); + pixd = pixApplyInvBackgroundGrayMap(pixs, pixbgi, sx, sy); + + pixDestroy(&pixt); + pixDestroy(&pixsd); + pixDestroy(&pixbg); + pixDestroy(&pixbgi); + return pixd; +} + + +/*------------------------------------------------------------------* + * Adaptive contrast normalization * + *------------------------------------------------------------------*/ +/*! + * \brief pixContrastNorm() + * + * \param[in] pixd [optional] 8 bpp; null or equal to pixs + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] sx, sy tile dimensions + * \param[in] mindiff minimum difference to accept as valid + * \param[in] smoothx, smoothy half-width of convolution kernel applied to + * min and max arrays: use 0 for no smoothing + * \return pixd always + * + *
+ * Notes:
+ *      (1) This function adaptively attempts to expand the contrast
+ *          to the full dynamic range in each tile.  If the contrast in
+ *          a tile is smaller than %mindiff, it uses the min and max
+ *          pixel values from neighboring tiles.  It also can use
+ *          convolution to smooth the min and max values from
+ *          neighboring tiles.  After all that processing, it is
+ *          possible that the actual pixel values in the tile are outside
+ *          the computed [min ... max] range for local contrast
+ *          normalization.  Such pixels are taken to be at either 0
+ *          (if below the min) or 255 (if above the max).
+ *      (2) pixd can be equal to pixs (in-place operation) or
+ *          null (makes a new pixd).
+ *      (3) sx and sy give the tile size; they are typically at least 20.
+ *      (4) mindiff is used to eliminate results for tiles where it is
+ *          likely that either fg or bg is missing.  A value around 50
+ *          or more is reasonable.
+ *      (5) The full width and height of the convolution kernel
+ *          are (2 * smoothx + 1) and (2 * smoothy + 1).  Some smoothing
+ *          is typically useful, and we limit the smoothing half-widths
+ *          to the range from 0 to 8.
+ *      (6) A linear TRC (gamma = 1.0) is applied to increase the contrast
+ *          in each tile.  The result can subsequently be globally corrected,
+ *          by applying pixGammaTRC() with arbitrary values of gamma
+ *          and the 0 and 255 points of the mapping.
+ * 
+ */ +PIX * +pixContrastNorm(PIX *pixd, + PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 mindiff, + l_int32 smoothx, + l_int32 smoothy) +{ +PIX *pixmin, *pixmax; + + PROCNAME("pixContrastNorm"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); + if (sx < 5 || sy < 5) + return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); + if (smoothx < 0 || smoothy < 0) + return (PIX *)ERROR_PTR("smooth params less than 0", procName, pixd); + if (smoothx > 8 || smoothy > 8) + return (PIX *)ERROR_PTR("smooth params exceed 8", procName, pixd); + + /* Get the min and max pixel values in each tile, and represent + * each value as a pixel in pixmin and pixmax, respectively. */ + pixMinMaxTiles(pixs, sx, sy, mindiff, smoothx, smoothy, &pixmin, &pixmax); + + /* For each tile, do a linear expansion of the dynamic range + * of pixels so that the min value is mapped to 0 and the + * max value is mapped to 255. */ + pixd = pixLinearTRCTiled(pixd, pixs, sx, sy, pixmin, pixmax); + + pixDestroy(&pixmin); + pixDestroy(&pixmax); + return pixd; +} + + +/*! + * \brief pixMinMaxTiles() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] sx, sy tile dimensions + * \param[in] mindiff minimum difference to accept as valid + * \param[in] smoothx, smoothy half-width of convolution kernel applied to + * min and max arrays: use 0 for no smoothing + * \param[out] ppixmin tiled minima + * \param[out] ppixmax tiled maxima + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes filtered and smoothed values for the min and
+ *          max pixel values in each tile of the image.
+ *      (2) See pixContrastNorm() for usage.
+ * 
+ */ +l_ok +pixMinMaxTiles(PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 mindiff, + l_int32 smoothx, + l_int32 smoothy, + PIX **ppixmin, + PIX **ppixmax) +{ +l_int32 w, h; +PIX *pixmin1, *pixmax1, *pixmin2, *pixmax2; + + PROCNAME("pixMinMaxTiles"); + + if (ppixmin) *ppixmin = NULL; + if (ppixmax) *ppixmax = NULL; + if (!ppixmin || !ppixmax) + return ERROR_INT("&pixmin or &pixmax undefined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (sx < 5 || sy < 5) + return ERROR_INT("sx and/or sy less than 3", procName, 1); + if (smoothx < 0 || smoothy < 0) + return ERROR_INT("smooth params less than 0", procName, 1); + if (smoothx > 5 || smoothy > 5) + return ERROR_INT("smooth params exceed 5", procName, 1); + + /* Get the min and max values in each tile */ + pixmin1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MIN); + pixmax1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MAX); + + pixmin2 = pixExtendByReplication(pixmin1, 1, 1); + pixmax2 = pixExtendByReplication(pixmax1, 1, 1); + pixDestroy(&pixmin1); + pixDestroy(&pixmax1); + + /* Make sure no value is 0 */ + pixAddConstantGray(pixmin2, 1); + pixAddConstantGray(pixmax2, 1); + + /* Generate holes where the contrast is too small */ + pixSetLowContrast(pixmin2, pixmax2, mindiff); + + /* Fill the holes (0 values) */ + pixGetDimensions(pixmin2, &w, &h, NULL); + pixFillMapHoles(pixmin2, w, h, L_FILL_BLACK); + pixFillMapHoles(pixmax2, w, h, L_FILL_BLACK); + + /* Smooth if requested */ + if (smoothx > 0 || smoothy > 0) { + smoothx = L_MIN(smoothx, (w - 1) / 2); + smoothy = L_MIN(smoothy, (h - 1) / 2); + *ppixmin = pixBlockconv(pixmin2, smoothx, smoothy); + *ppixmax = pixBlockconv(pixmax2, smoothx, smoothy); + } + else { + *ppixmin = pixClone(pixmin2); + *ppixmax = pixClone(pixmax2); + } + pixCopyResolution(*ppixmin, pixs); + pixCopyResolution(*ppixmax, pixs); + pixDestroy(&pixmin2); + pixDestroy(&pixmax2); + + return 0; +} + + +/*! + * \brief pixSetLowContrast() + * + * \param[in] pixs1 8 bpp + * \param[in] pixs2 8 bpp + * \param[in] mindiff minimum difference to accept as valid + * \return 0 if OK; 1 if no pixel diffs are large enough, or on error + * + *
+ * Notes:
+ *      (1) This compares corresponding pixels in pixs1 and pixs2.
+ *          When they differ by less than %mindiff, set the pixel
+ *          values to 0 in each.  Each pixel typically represents a tile
+ *          in a larger image, and a very small difference between
+ *          the min and max in the tile indicates that the min and max
+ *          values are not to be trusted.
+ *      (2) If contrast (pixel difference) detection is expected to fail,
+ *          caller should check return value.
+ * 
+ */ +l_ok +pixSetLowContrast(PIX *pixs1, + PIX *pixs2, + l_int32 mindiff) +{ +l_int32 i, j, w, h, d, wpl, val1, val2, found; +l_uint32 *data1, *data2, *line1, *line2; + + PROCNAME("pixSetLowContrast"); + + if (!pixs1 || !pixs2) + return ERROR_INT("pixs1 and pixs2 not both defined", procName, 1); + if (pixSizesEqual(pixs1, pixs2) == 0) + return ERROR_INT("pixs1 and pixs2 not equal size", procName, 1); + pixGetDimensions(pixs1, &w, &h, &d); + if (d != 8) + return ERROR_INT("depth not 8 bpp", procName, 1); + if (mindiff > 254) return 0; + + data1 = pixGetData(pixs1); + data2 = pixGetData(pixs2); + wpl = pixGetWpl(pixs1); + found = 0; /* init to not finding any diffs >= mindiff */ + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl; + line2 = data2 + i * wpl; + for (j = 0; j < w; j++) { + val1 = GET_DATA_BYTE(line1, j); + val2 = GET_DATA_BYTE(line2, j); + if (L_ABS(val1 - val2) >= mindiff) { + found = 1; + break; + } + } + if (found) break; + } + if (!found) { + L_WARNING("no pixel pair diffs as large as mindiff\n", procName); + pixClearAll(pixs1); + pixClearAll(pixs2); + return 1; + } + + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl; + line2 = data2 + i * wpl; + for (j = 0; j < w; j++) { + val1 = GET_DATA_BYTE(line1, j); + val2 = GET_DATA_BYTE(line2, j); + if (L_ABS(val1 - val2) < mindiff) { + SET_DATA_BYTE(line1, j, 0); + SET_DATA_BYTE(line2, j, 0); + } + } + } + + return 0; +} + + +/*! + * \brief pixLinearTRCTiled() + * + * \param[in] pixd [optional] 8 bpp + * \param[in] pixs 8 bpp, not colormapped + * \param[in] sx, sy tile dimensions + * \param[in] pixmin pix of min values in tiles + * \param[in] pixmax pix of max values in tiles + * \return pixd always + * + *
+ * Notes:
+ *      (1) pixd can be equal to pixs (in-place operation) or
+ *          null (makes a new pixd).
+ *      (2) sx and sy give the tile size; they are typically at least 20.
+ *      (3) pixmin and pixmax are generated by pixMinMaxTiles()
+ *      (4) For each tile, this does a linear expansion of the dynamic
+ *          range so that the min value in the tile becomes 0 and the
+ *          max value in the tile becomes 255.
+ *      (5) The LUTs that do the mapping are generated as needed
+ *          and stored for reuse in an integer array within the ptr array iaa[].
+ * 
+ */ +PIX * +pixLinearTRCTiled(PIX *pixd, + PIX *pixs, + l_int32 sx, + l_int32 sy, + PIX *pixmin, + PIX *pixmax) +{ +l_int32 i, j, k, m, w, h, wt, ht, wpl, wplt, xoff, yoff; +l_int32 minval, maxval, val, sval; +l_int32 *ia; +l_int32 **iaa; +l_uint32 *data, *datamin, *datamax, *line, *tline, *linemin, *linemax; + + PROCNAME("pixLinearTRCTiled"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); + if (!pixmin || !pixmax) + return (PIX *)ERROR_PTR("pixmin & pixmax not defined", procName, pixd); + if (sx < 5 || sy < 5) + return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); + + if ((iaa = (l_int32 **)LEPT_CALLOC(256, sizeof(l_int32 *))) == NULL) + return (PIX *)ERROR_PTR("iaa not made", procName, NULL); + if ((pixd = pixCopy(pixd, pixs)) == NULL) { + LEPT_FREE(iaa); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixGetDimensions(pixd, &w, &h, NULL); + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + datamin = pixGetData(pixmin); + datamax = pixGetData(pixmax); + wplt = pixGetWpl(pixmin); + pixGetDimensions(pixmin, &wt, &ht, NULL); + for (i = 0; i < ht; i++) { + line = data + sy * i * wpl; + linemin = datamin + i * wplt; + linemax = datamax + i * wplt; + yoff = sy * i; + for (j = 0; j < wt; j++) { + xoff = sx * j; + minval = GET_DATA_BYTE(linemin, j); + maxval = GET_DATA_BYTE(linemax, j); + if (maxval == minval) { + L_ERROR("shouldn't happen! i,j = %d,%d, minval = %d\n", + procName, i, j, minval); + continue; + } + if ((ia = iaaGetLinearTRC(iaa, maxval - minval)) == NULL) { + L_ERROR("failure to make ia for j = %d!\n", procName, j); + continue; + } + for (k = 0; k < sy && yoff + k < h; k++) { + tline = line + k * wpl; + for (m = 0; m < sx && xoff + m < w; m++) { + val = GET_DATA_BYTE(tline, xoff + m); + sval = val - minval; + sval = L_MAX(0, sval); + SET_DATA_BYTE(tline, xoff + m, ia[sval]); + } + } + } + } + + for (i = 0; i < 256; i++) + LEPT_FREE(iaa[i]); + LEPT_FREE(iaa); + return pixd; +} + + +/*! + * \brief iaaGetLinearTRC() + * + * \param[in] iaa bare array of ptrs to l_int32 + * \param[in] diff between min and max pixel values that are + * to be mapped to 0 and 255 + * \return ia LUT with input (val - minval) and output a + * value between 0 and 255) + */ +static l_int32 * +iaaGetLinearTRC(l_int32 **iaa, + l_int32 diff) +{ +l_int32 i; +l_int32 *ia; +l_float32 factor; + + PROCNAME("iaaGetLinearTRC"); + + if (!iaa) + return (l_int32 *)ERROR_PTR("iaa not defined", procName, NULL); + + if (iaa[diff] != NULL) /* already have it */ + return iaa[diff]; + + if ((ia = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) + return (l_int32 *)ERROR_PTR("ia not made", procName, NULL); + iaa[diff] = ia; + if (diff == 0) { /* shouldn't happen */ + for (i = 0; i < 256; i++) + ia[i] = 128; + } + else { + factor = 255. / (l_float32)diff; + for (i = 0; i < diff + 1; i++) + ia[i] = (l_int32)(factor * i + 0.5); + for (i = diff + 1; i < 256; i++) + ia[i] = 255; + } + + return ia; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/affine.c b/hgdriver/3rdparty/hgOCR/leptonica/affine.c new file mode 100644 index 0000000..10cb873 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/affine.c @@ -0,0 +1,1622 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file affine.c + *
+ *
+ *      Affine (3 pt) image transformation using a sampled
+ *      (to nearest integer) transform on each dest point
+ *           PIX        *pixAffineSampledPta()
+ *           PIX        *pixAffineSampled()
+ *
+ *      Affine (3 pt) image transformation using interpolation
+ *      (or area mapping) for anti-aliasing images that are
+ *      2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB
+ *           PIX        *pixAffinePta()
+ *           PIX        *pixAffine()
+ *           PIX        *pixAffinePtaColor()
+ *           PIX        *pixAffineColor()
+ *           PIX        *pixAffinePtaGray()
+ *           PIX        *pixAffineGray()
+ *
+ *      Affine transform including alpha (blend) component
+ *           PIX        *pixAffinePtaWithAlpha()
+ *
+ *      Affine coordinate transformation
+ *           l_int32     getAffineXformCoeffs()
+ *           l_int32     affineInvertXform()
+ *           l_int32     affineXformSampledPt()
+ *           l_int32     affineXformPt()
+ *
+ *      Interpolation helper functions
+ *           l_int32     linearInterpolatePixelGray()
+ *           l_int32     linearInterpolatePixelColor()
+ *
+ *      Gauss-jordan linear equation solver
+ *           l_int32     gaussjordan()
+ *
+ *      Affine image transformation using a sequence of
+ *      shear/scale/translation operations
+ *           PIX        *pixAffineSequential()
+ *
+ *      One can define a coordinate space by the location of the origin,
+ *      the orientation of x and y axes, and the unit scaling along
+ *      each axis.  An affine transform is a general linear
+ *      transformation from one coordinate space to another.
+ *
+ *      For the general case, we can define the affine transform using
+ *      two sets of three (noncollinear) points in a plane.  One set
+ *      corresponds to the input (src) coordinate space; the other to the
+ *      transformed (dest) coordinate space.  Each point in the
+ *      src corresponds to one of the points in the dest.  With two
+ *      sets of three points, we get a set of 6 equations in 6 unknowns
+ *      that specifies the mapping between the coordinate spaces.
+ *      The interface here allows you to specify either the corresponding
+ *      sets of 3 points, or the transform itself (as a vector of 6
+ *      coefficients).
+ *
+ *      Given the transform as a vector of 6 coefficients, we can compute
+ *      both a a pointwise affine coordinate transformation and an
+ *      affine image transformation.
+ *
+ *      To compute the coordinate transform, we need the coordinate
+ *      value (x',y') in the transformed space for any point (x,y)
+ *      in the original space.  To derive this transform from the
+ *      three corresponding points, it is convenient to express the affine
+ *      coordinate transformation using an LU decomposition of
+ *      a set of six linear equations that express the six coordinates
+ *      of the three points in the transformed space as a function of
+ *      the six coordinates in the original space.  Once we have
+ *      this transform matrix , we can transform an image by
+ *      finding, for each destination pixel, the pixel (or pixels)
+ *      in the source that give rise to it.
+ *
+ *      This 'pointwise' transformation can be done either by sampling
+ *      and picking a single pixel in the src to replicate into the dest,
+ *      or by interpolating (or averaging) over four src pixels to
+ *      determine the value of the dest pixel.  The first method is
+ *      implemented by pixAffineSampled() and the second method by
+ *      pixAffine().  The interpolated method can only be used for
+ *      images with more than 1 bpp, but for these, the image quality
+ *      is significantly better than the sampled method, due to
+ *      the 'antialiasing' effect of weighting the src pixels.
+ *
+ *      Interpolation works well when there is relatively little scaling,
+ *      or if there is image expansion in general.  However, if there
+ *      is significant image reduction, one should apply a low-pass
+ *      filter before subsampling to avoid aliasing the high frequencies.
+ *
+ *      A typical application might be to align two images, which
+ *      may be scaled, rotated and translated versions of each other.
+ *      Through some pre-processing, three corresponding points are
+ *      located in each of the two images.  One of the images is
+ *      then to be (affine) transformed to align with the other.
+ *      As mentioned, the standard way to do this is to use three
+ *      sets of points, compute the 6 transformation coefficients
+ *      from these points that describe the linear transformation,
+ *
+ *          x' = ax + by + c
+ *          y' = dx + ey + f
+ *
+ *      and use this in a pointwise manner to transform the image.
+ *
+ *      N.B.  Be sure to see the comment in getAffineXformCoeffs(),
+ *      regarding using the inverse of the affine transform for points
+ *      to transform images.
+ *
+ *      There is another way to do this transformation; namely,
+ *      by doing a sequence of simple affine transforms, without
+ *      computing directly the affine coordinate transformation.
+ *      We have at our disposal (1) translations (using rasterop),
+ *      (2) horizontal and vertical shear about any horizontal and vertical
+ *      line, respectively, and (3) non-isotropic scaling by two
+ *      arbitrary x and y scaling factors.  We also have rotation
+ *      about an arbitrary point, but this is equivalent to a set
+ *      of three shears so we do not need to use it.
+ *
+ *      Why might we do this?  For binary images, it is usually
+ *      more efficient to do such transformations by a sequence
+ *      of word parallel operations.  Shear and translation can be
+ *      done in-place and word parallel; arbitrary scaling is
+ *      mostly pixel-wise.
+ *
+ *      Suppose that we are transforming image 1 to correspond to image 2.
+ *      We have a set of three points, describing the coordinate space
+ *      embedded in image 1, and we need to transform image 1 until
+ *      those three points exactly correspond to the new coordinate space
+ *      defined by the second set of three points.  In our image
+ *      matching application, the latter set of three points was
+ *      found to be the corresponding points in image 2.
+ *
+ *      The most elegant way I can think of to do such a sequential
+ *      implementation is to imagine that we're going to transform
+ *      BOTH images until they're aligned.  (We don't really want
+ *      to transform both, because in fact we may only have one image
+ *      that is undergoing a general affine transformation.)
+ *
+ *      Choose the 3 corresponding points as follows:
+ *         ~ The 1st point is an origin
+ *         ~ The 2nd point gives the orientation and scaling of the
+ *           "x" axis with respect to the origin
+ *         ~ The 3rd point does likewise for the "y" axis.
+ *      These "axes" must not be collinear; otherwise they are
+ *      arbitrary (although some strange things will happen if
+ *      the handedness sweeping through the minimum angle between
+ *      the axes is opposite).
+ *
+ *      An important constraint is that we have shear operations
+ *      about an arbitrary horizontal or vertical line, but always
+ *      parallel to the x or y axis.  If we continue to pretend that
+ *      we have an unprimed coordinate space embedded in image 1 and
+ *      a primed coordinate space embedded in image 2, we imagine
+ *      (a) transforming image 1 by horizontal and vertical shears about
+ *      point 1 to align points 3 and 2 along the y and x axes,
+ *      respectively, and (b) transforming image 2 by horizontal and
+ *      vertical shears about point 1' to align points 3' and 2' along
+ *      the y and x axes.  Then we scale image 1 so that the distances
+ *      from 1 to 2 and from 1 to 3 are equal to the distances in
+ *      image 2 from 1' to 2' and from 1' to 3'.  This scaling operation
+ *      leaves the true image origin, at (0,0) invariant, and will in
+ *      general translate point 1.  The original points 1 and 1' will
+ *      typically not coincide in any event, so we must translate
+ *      the origin of image 1, at its current point 1, to the origin
+ *      of image 2 at 1'.  The images should now be aligned.  But
+ *      because we never really transformed image 2 (and image 2 may
+ *      not even exist), we now perform  on image 1 the reverse of
+ *      the shear transforms that we imagined doing on image 2;
+ *      namely, the negative vertical shear followed by the negative
+ *      horizontal shear.  Image 1 should now have its transformed
+ *      unprimed coordinates aligned with the original primed
+ *      coordinates.  In all this, it is only necessary to keep track
+ *      of the shear angles and translations of points during the shears.
+ *      What has been accomplished is a general affine transformation
+ *      on image 1.
+ *
+ *      Having described all this, if you are going to use an
+ *      affine transformation in an application, this is what you
+ *      need to know:
+ *
+ *          (1) You should NEVER use the sequential method, because
+ *              the image quality for 1 bpp text is much poorer
+ *              (even though it is about 2x faster than the pointwise sampled
+ *              method), and for images with depth greater than 1, it is
+ *              nearly 20x slower than the pointwise sampled method
+ *              and over 10x slower than the pointwise interpolated method!
+ *              The sequential method is given here for purely
+ *              pedagogical reasons.
+ *
+ *          (2) For 1 bpp images, use the pointwise sampled function
+ *              pixAffineSampled().  For all other images, the best
+ *              quality results result from using the pointwise
+ *              interpolated function pixAffinePta() or pixAffine();
+ *              the cost is less than a doubling of the computation time
+ *              with respect to the sampled function.  If you use
+ *              interpolation on colormapped images, the colormap will
+ *              be removed, resulting in either a grayscale or color
+ *              image, depending on the values in the colormap.
+ *              If you want to retain the colormap, use pixAffineSampled().
+ *
+ *      Typical relative timing of pointwise transforms (sampled = 1.0):
+ *      8 bpp:   sampled        1.0
+ *               interpolated   1.6
+ *      32 bpp:  sampled        1.0
+ *               interpolated   1.8
+ *      Additionally, the computation time/pixel is nearly the same
+ *      for 8 bpp and 32 bpp, for both sampled and interpolated.
+ * 
+ */ + + +#include +#include +#include "allheaders.h" + +extern l_float32 AlphaMaskBorderVals[2]; + +#ifndef NO_CONSOLE_IO +#define DEBUG 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Sampled affine image transformation * + *-------------------------------------------------------------*/ +/*! + * \brief pixAffineSampledPta() + * + * \param[in] pixs all depths + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) The 3 points must not be collinear.
+ *      (4) The order of the 3 points is arbitrary; however, to compare
+ *          with the sequential transform they must be in these locations
+ *          and in this order: origin, x-axis, y-axis.
+ *      (5) For 1 bpp images, this has much better quality results
+ *          than pixAffineSequential(), particularly for text.
+ *          It is about 3x slower, but does not require additional
+ *          border pixels.  The poor quality of pixAffineSequential()
+ *          is due to repeated quantized transforms.  It is strongly
+ *          recommended that pixAffineSampled() be used for 1 bpp images.
+ *      (6) For 8 or 32 bpp, much better quality is obtained by the
+ *          somewhat slower pixAffinePta().  See that function
+ *          for relative timings between sampled and interpolated.
+ *      (7) To repeat, use of the sequential transform,
+ *          pixAffineSequential(), for any images, is discouraged.
+ * 
+ */ +PIX * +pixAffineSampledPta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixAffineSampledPta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 3) + return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); + if (ptaGetCount(ptad) != 3) + return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getAffineXformCoeffs(ptad, ptas, &vc); + pixd = pixAffineSampled(pixs, vc, incolor); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixAffineSampled() + * + * \param[in] pixs all depths + * \param[in] vc vector of 6 coefficients for affine transformation + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) For 8 or 32 bpp, much better quality is obtained by the
+ *          somewhat slower pixAffine().  See that function
+ *          for relative timings between sampled and interpolated.
+ * 
+ */ +PIX * +pixAffineSampled(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; +l_uint32 val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixAffineSampled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); + + /* Init all dest pixels to color to be brought in from outside */ + pixd = pixCreateTemplate(pixs); + if ((cmap = pixGetColormap(pixs)) != NULL) { + if (incolor == L_BRING_IN_WHITE) + color = 1; + else + color = 0; + pixcmapAddBlackOrWhite(cmap, color, &cmapindex); + pixSetAllArbitrary(pixd, cmapindex); + } else { + if ((d == 1 && incolor == L_BRING_IN_WHITE) || + (d > 1 && incolor == L_BRING_IN_BLACK)) { + pixClearAll(pixd); + } else { + pixSetAll(pixd); + } + } + + /* Scan over the dest pixels */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + affineXformSampledPt(vc, j, i, &x, &y); + if (x < 0 || y < 0 || x >=w || y >= h) + continue; + lines = datas + y * wpls; + if (d == 1) { + val = GET_DATA_BIT(lines, x); + SET_DATA_BIT_VAL(lined, j, val); + } else if (d == 8) { + val = GET_DATA_BYTE(lines, x); + SET_DATA_BYTE(lined, j, val); + } else if (d == 32) { + lined[j] = lines[x]; + } else if (d == 2) { + val = GET_DATA_DIBIT(lines, x); + SET_DATA_DIBIT(lined, j, val); + } else if (d == 4) { + val = GET_DATA_QBIT(lines, x); + SET_DATA_QBIT(lined, j, val); + } + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Interpolated affine image transformation * + *---------------------------------------------------------------------*/ +/*! + * \brief pixAffinePta() + * + * \param[in] pixs all depths; colormap ok + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixAffinePta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixAffinePta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 3) + return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); + if (ptaGetCount(ptad) != 3) + return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixAffineSampledPta(pixs, ptad, ptas, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixAffinePtaGray(pixt2, ptad, ptas, colorval); + else /* d == 32 */ + pixd = pixAffinePtaColor(pixt2, ptad, ptas, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixAffine() + * + * \param[in] pixs all depths; colormap ok + * \param[in] vc vector of 6 coefficients for affine transformation + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixAffine(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixAffine"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixAffineSampled(pixs, vc, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixAffineGray(pixt2, vc, colorval); + else /* d == 32 */ + pixd = pixAffineColor(pixt2, vc, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixAffinePtaColor() + * + * \param[in] pixs 32 bpp + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixAffinePtaColor(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint32 colorval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixAffinePtaColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (ptaGetCount(ptas) != 3) + return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); + if (ptaGetCount(ptad) != 3) + return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getAffineXformCoeffs(ptad, ptas, &vc); + pixd = pixAffineColor(pixs, vc, colorval); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixAffineColor() + * + * \param[in] pixs 32 bpp + * \param[in] vc vector of 6 coefficients for affine transformation + * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixAffineColor(PIX *pixs, + l_float32 *vc, + l_uint32 colorval) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_uint32 val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixAffineColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, colorval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + affineXformPt(vc, j, i, &x, &y); + linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, + &val); + *(lined + j) = val; + } + } + + /* If rgba, transform the pixs alpha channel and insert in pixd */ + if (pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixAffineGray(pix1, vc, 255); /* bring in opaque */ + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return pixd; +} + + +/*! + * \brief pixAffinePtaGray() + * + * \param[in] pixs 8 bpp + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixAffinePtaGray(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint8 grayval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixAffinePtaGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (ptaGetCount(ptas) != 3) + return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); + if (ptaGetCount(ptad) != 3) + return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getAffineXformCoeffs(ptad, ptas, &vc); + pixd = pixAffineGray(pixs, vc, grayval); + LEPT_FREE(vc); + + return pixd; +} + + + +/*! + * \brief pixAffineGray() + * + * \param[in] pixs 8 bpp + * \param[in] vc vector of 6 coefficients for affine transformation + * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixAffineGray(PIX *pixs, + l_float32 *vc, + l_uint8 grayval) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pixd; + + PROCNAME("pixAffineGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, grayval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + affineXformPt(vc, j, i, &x, &y); + linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Affine transform including alpha (blend) component * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixAffinePtaWithAlpha() + * + * \param[in] pixs 32 bpp rgb + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] pixg [optional] 8 bpp, can be null + * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent + * and 1.0 fully opaque + * \param[in] border of pixels added to capture transformed source pixels + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The alpha channel is transformed separately from pixs,
+ *          and aligns with it, being fully transparent outside the
+ *          boundary of the transformed pixs.  For pixels that are fully
+ *          transparent, a blending function like pixBlendWithGrayMask()
+ *          will give zero weight to corresponding pixels in pixs.
+ *      (2) If pixg is NULL, it is generated as an alpha layer that is
+ *          partially opaque, using %fract.  Otherwise, it is cropped
+ *          to pixs if required and %fract is ignored.  The alpha channel
+ *          in pixs is never used.
+ *      (3) Colormaps are removed.
+ *      (4) When pixs is transformed, it doesn't matter what color is brought
+ *          in because the alpha channel will be transparent (0) there.
+ *      (5) To avoid losing source pixels in the destination, it may be
+ *          necessary to add a border to the source pix before doing
+ *          the affine transformation.  This can be any non-negative number.
+ *      (6) The input %ptad and %ptas are in a coordinate space before
+ *          the border is added.  Internally, we compensate for this
+ *          before doing the affine transform on the image after the border
+ *          is added.
+ *      (7) The default setting for the border values in the alpha channel
+ *          is 0 (transparent) for the outermost ring of pixels and
+ *          (0.5 * fract * 255) for the second ring.  When blended over
+ *          a second image, this
+ *          (a) shrinks the visible image to make a clean overlap edge
+ *              with an image below, and
+ *          (b) softens the edges by weakening the aliasing there.
+ *          Use l_setAlphaMaskBorder() to change these values.
+ * 
+ */ +PIX * +pixAffinePtaWithAlpha(PIX *pixs, + PTA *ptad, + PTA *ptas, + PIX *pixg, + l_float32 fract, + l_int32 border) +{ +l_int32 ws, hs, d; +PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; +PTA *ptad2, *ptas2; + + PROCNAME("pixAffinePtaWithAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (d != 32 && pixGetColormap(pixs) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (pixg && pixGetDepth(pixg) != 8) { + L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", + procName); + pixg = NULL; + } + if (!pixg && (fract < 0.0 || fract > 1.0)) { + L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); + fract = 1.0; + } + if (!pixg && fract == 0.0) + L_WARNING("fully opaque alpha; image will not be blended\n", procName); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + + /* Add border; the color doesn't matter */ + pixb1 = pixAddBorder(pixs, border, 0); + + /* Transform the ptr arrays to work on the bordered image */ + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + + /* Do separate affine transform of rgb channels of pixs and of pixg */ + pixd = pixAffinePtaColor(pixb1, ptad2, ptas2, 0); + if (!pixg) { + pixg2 = pixCreate(ws, hs, 8); + if (fract == 1.0) + pixSetAll(pixg2); + else + pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); + } else { + pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); + } + if (ws > 10 && hs > 10) { /* see note 7 */ + pixSetBorderRingVal(pixg2, 1, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); + pixSetBorderRingVal(pixg2, 2, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); + + } + pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ + pixga = pixAffinePtaGray(pixb2, ptad2, ptas2, 0); + pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); + pixSetSpp(pixd, 4); + + pixDestroy(&pixg2); + pixDestroy(&pixb1); + pixDestroy(&pixb2); + pixDestroy(&pixga); + ptaDestroy(&ptad2); + ptaDestroy(&ptas2); + return pixd; +} + + +/*-------------------------------------------------------------* + * Affine coordinate transformation * + *-------------------------------------------------------------*/ +/*! + * \brief getAffineXformCoeffs() + * + * \param[in] ptas source 3 points; unprimed + * \param[in] ptad transformed 3 points; primed + * \param[out] pvc vector of coefficients of transform + * \return 0 if OK; 1 on error + * + *
+ *  We have a set of six equations, describing the affine
+ *  transformation that takes 3 points ptas into 3 other
+ *  points ptad.  These equations are:
+ *
+ *          x1' = c[0]*x1 + c[1]*y1 + c[2]
+ *          y1' = c[3]*x1 + c[4]*y1 + c[5]
+ *          x2' = c[0]*x2 + c[1]*y2 + c[2]
+ *          y2' = c[3]*x2 + c[4]*y2 + c[5]
+ *          x3' = c[0]*x3 + c[1]*y3 + c[2]
+ *          y3' = c[3]*x3 + c[4]*y3 + c[5]
+ *
+ *  This can be represented as
+ *
+ *          AC = B
+ *
+ *  where B and C are column vectors
+ *
+ *          B = [ x1' y1' x2' y2' x3' y3' ]
+ *          C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] ]
+ *
+ *  and A is the 6x6 matrix
+ *
+ *          x1   y1   1   0    0    0
+ *           0    0   0   x1   y1   1
+ *          x2   y2   1   0    0    0
+ *           0    0   0   x2   y2   1
+ *          x3   y3   1   0    0    0
+ *           0    0   0   x3   y3   1
+ *
+ *  These six equations are solved here for the coefficients C.
+ *
+ *  These six coefficients can then be used to find the dest
+ *  point x',y') corresponding to any src point (x,y, according
+ *  to the equations
+ *
+ *           x' = c[0]x + c[1]y + c[2]
+ *           y' = c[3]x + c[4]y + c[5]
+ *
+ *  that are implemented in affineXformPt.
+ *
+ *  !!!!!!!!!!!!!!!!!!   Very important   !!!!!!!!!!!!!!!!!!!!!!
+ *
+ *  When the affine transform is composed from a set of simple
+ *  operations such as translation, scaling and rotation,
+ *  it is built in a form to convert from the un-transformed src
+ *  point to the transformed dest point.  However, when an
+ *  affine transform is used on images, it is used in an inverted
+ *  way: it converts from the transformed dest point to the
+ *  un-transformed src point.  So, for example, if you transform
+ *  a boxa using transform A, to transform an image in the same
+ *  way you must use the inverse of A.
+ *
+ *  For example, if you transform a boxa with a 3x3 affine matrix
+ *  'mat', the analogous image transformation must use 'matinv':
+ * \code
+ *     boxad = boxaAffineTransform(boxas, mat);
+ *     affineInvertXform(mat, &matinv);
+ *     pixd = pixAffine(pixs, matinv, L_BRING_IN_WHITE);
+ * \endcode
+ *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * 
+ */ +l_ok +getAffineXformCoeffs(PTA *ptas, + PTA *ptad, + l_float32 **pvc) +{ +l_int32 i; +l_float32 x1, y1, x2, y2, x3, y3; +l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ +l_float32 *a[6]; /* 6x6 matrix A */ + + PROCNAME("getAffineXformCoeffs"); + + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (!ptad) + return ERROR_INT("ptad not defined", procName, 1); + if (!pvc) + return ERROR_INT("&vc not defined", procName, 1); + + if ((b = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32))) == NULL) + return ERROR_INT("b not made", procName, 1); + *pvc = b; + + ptaGetPt(ptas, 0, &x1, &y1); + ptaGetPt(ptas, 1, &x2, &y2); + ptaGetPt(ptas, 2, &x3, &y3); + ptaGetPt(ptad, 0, &b[0], &b[1]); + ptaGetPt(ptad, 1, &b[2], &b[3]); + ptaGetPt(ptad, 2, &b[4], &b[5]); + + for (i = 0; i < 6; i++) + if ((a[i] = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32))) == NULL) + return ERROR_INT("a[i] not made", procName, 1); + + a[0][0] = x1; + a[0][1] = y1; + a[0][2] = 1.; + a[1][3] = x1; + a[1][4] = y1; + a[1][5] = 1.; + a[2][0] = x2; + a[2][1] = y2; + a[2][2] = 1.; + a[3][3] = x2; + a[3][4] = y2; + a[3][5] = 1.; + a[4][0] = x3; + a[4][1] = y3; + a[4][2] = 1.; + a[5][3] = x3; + a[5][4] = y3; + a[5][5] = 1.; + + gaussjordan(a, b, 6); + + for (i = 0; i < 6; i++) + LEPT_FREE(a[i]); + + return 0; +} + + +/*! + * \brief affineInvertXform() + * + * \param[in] vc vector of 6 coefficients + * \param[out] pvci inverted transform + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The 6 affine transform coefficients are the first
+ *          two rows of a 3x3 matrix where the last row has
+ *          only a 1 in the third column.  We invert this
+ *          using gaussjordan(), and select the first 2 rows
+ *          as the coefficients of the inverse affine transform.
+ *      (2) Alternatively, we can find the inverse transform
+ *          coefficients by inverting the 2x2 submatrix,
+ *          and treating the top 2 coefficients in the 3rd column as
+ *          a RHS vector for that 2x2 submatrix.  Then the
+ *          6 inverted transform coefficients are composed of
+ *          the inverted 2x2 submatrix and the negative of the
+ *          transformed RHS vector.  Why is this so?  We have
+ *             Y = AX + R  (2 equations in 6 unknowns)
+ *          Then
+ *             X = A'Y - A'R
+ *          Gauss-jordan solves
+ *             AF = R
+ *          and puts the solution for F, which is A'R,
+ *          into the input R vector.
+ *
+ * 
+ */ +l_ok +affineInvertXform(l_float32 *vc, + l_float32 **pvci) +{ +l_int32 i; +l_float32 *vci; +l_float32 *a[3]; +l_float32 b[3] = {1.0, 1.0, 1.0}; /* anything; results ignored */ + + PROCNAME("affineInvertXform"); + + if (!pvci) + return ERROR_INT("&vci not defined", procName, 1); + *pvci = NULL; + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + +#if 1 + for (i = 0; i < 3; i++) + a[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); + a[0][0] = vc[0]; + a[0][1] = vc[1]; + a[0][2] = vc[2]; + a[1][0] = vc[3]; + a[1][1] = vc[4]; + a[1][2] = vc[5]; + a[2][2] = 1.0; + gaussjordan(a, b, 3); /* this inverts matrix a */ + vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); + *pvci = vci; + vci[0] = a[0][0]; + vci[1] = a[0][1]; + vci[2] = a[0][2]; + vci[3] = a[1][0]; + vci[4] = a[1][1]; + vci[5] = a[1][2]; + for (i = 0; i < 3; i++) + LEPT_FREE(a[i]); + +#else + + /* Alternative version, inverting a 2x2 matrix */ + { l_float32 *a2[2]; + for (i = 0; i < 2; i++) + a2[i] = (l_float32 *)LEPT_CALLOC(2, sizeof(l_float32)); + a2[0][0] = vc[0]; + a2[0][1] = vc[1]; + a2[1][0] = vc[3]; + a2[1][1] = vc[4]; + b[0] = vc[2]; + b[1] = vc[5]; + gaussjordan(a2, b, 2); /* this inverts matrix a2 */ + vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); + *pvci = vci; + vci[0] = a2[0][0]; + vci[1] = a2[0][1]; + vci[2] = -b[0]; /* note sign */ + vci[3] = a2[1][0]; + vci[4] = a2[1][1]; + vci[5] = -b[1]; /* note sign */ + for (i = 0; i < 2; i++) + LEPT_FREE(a2[i]); + } +#endif + + return 0; +} + + +/*! + * \brief affineXformSampledPt() + * + * \param[in] vc vector of 6 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds the nearest pixel coordinates of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +affineXformSampledPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_int32 *pxp, + l_int32 *pyp) +{ + PROCNAME("affineXformSampledPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] + 0.5); + *pyp = (l_int32)(vc[3] * x + vc[4] * y + vc[5] + 0.5); + return 0; +} + + +/*! + * \brief affineXformPt() + * + * \param[in] vc vector of 6 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This computes the floating point location of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +affineXformPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_float32 *pxp, + l_float32 *pyp) +{ + PROCNAME("affineXformPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + *pxp = vc[0] * x + vc[1] * y + vc[2]; + *pyp = vc[3] * x + vc[4] * y + vc[5]; + return 0; +} + + +/*-------------------------------------------------------------* + * Interpolation helper functions * + *-------------------------------------------------------------*/ +/*! + * \brief linearInterpolatePixelColor() + * + * \param[in] datas ptr to beginning of image data + * \param[in] wpls 32-bit word/line for this data array + * \param[in] w, h of image + * \param[in] x, y floating pt location for evaluation + * \param[in] colorval color brought in from the outside when the + * input x,y location is outside the image; + * in 0xrrggbb00 format) + * \param[out] pval interpolated color value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a standard linear interpolation function.  It is
+ *          equivalent to area weighting on each component, and
+ *          avoids "jaggies" when rendering sharp edges.
+ * 
+ */ +l_ok +linearInterpolatePixelColor(l_uint32 *datas, + l_int32 wpls, + l_int32 w, + l_int32 h, + l_float32 x, + l_float32 y, + l_uint32 colorval, + l_uint32 *pval) +{ +l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf; +l_int32 rval, gval, bval; +l_uint32 word00, word01, word10, word11; +l_uint32 *lines; + + PROCNAME("linearInterpolatePixelColor"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = colorval; + if (!datas) + return ERROR_INT("datas not defined", procName, 1); + + /* Skip if x or y are invalid. (x,y) must be in the source image. + * Failure to detect an invalid point will cause a mem address fault. + * Occasionally, x or y will be a nan, and relational checks always + * fail for nans. Therefore we check if the point is inside the pix */ + valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); + if (!valid) return 0; + + xpm = (l_int32)(16.0 * x); + ypm = (l_int32)(16.0 * y); + xp = xpm >> 4; + xp2 = xp + 1 < w ? xp + 1 : xp; + yp = ypm >> 4; + if (yp + 1 >= h) wpls = 0; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + +#if DEBUG + if (xf < 0 || yf < 0) + fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); +#endif /* DEBUG */ + + /* Do area weighting (eqiv. to linear interpolation) */ + lines = datas + yp * wpls; + word00 = *(lines + xp); + word10 = *(lines + xp2); + word01 = *(lines + wpls + xp); + word11 = *(lines + wpls + xp2); + rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_RED_SHIFT) & 0xff)) / 256; + gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff)) / 256; + bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff)) / 256; + composeRGBPixel(rval, gval, bval, pval); + return 0; +} + + +/*! + * \brief linearInterpolatePixelGray() + * + * \param[in] datas ptr to beginning of image data + * \param[in] wpls 32-bit word/line for this data array + * \param[in] w, h of image + * \param[in] x, y floating pt location for evaluation + * \param[in] grayval color brought in from the outside when the + * input x,y location is outside the image + * \param[out] pval interpolated gray value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a standard linear interpolation function.  It is
+ *          equivalent to area weighting on each component, and
+ *          avoids "jaggies" when rendering sharp edges.
+ * 
+ */ +l_ok +linearInterpolatePixelGray(l_uint32 *datas, + l_int32 wpls, + l_int32 w, + l_int32 h, + l_float32 x, + l_float32 y, + l_int32 grayval, + l_int32 *pval) +{ +l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf, v00, v10, v01, v11; +l_uint32 *lines; + + PROCNAME("linearInterpolatePixelGray"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = grayval; + if (!datas) + return ERROR_INT("datas not defined", procName, 1); + + /* Skip if x or y is invalid. (x,y) must be in the source image. + * Failure to detect an invalid point will cause a mem address fault. + * Occasionally, x or y will be a nan, and relational checks always + * fail for nans. Therefore we check if the point is inside the pix */ + valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); + if (!valid) return 0; + + xpm = (l_int32)(16.0 * x); + ypm = (l_int32)(16.0 * y); + xp = xpm >> 4; + xp2 = xp + 1 < w ? xp + 1 : xp; + yp = ypm >> 4; + if (yp + 1 >= h) wpls = 0; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + +#if DEBUG + if (xf < 0 || yf < 0) + fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); +#endif /* DEBUG */ + + /* Interpolate by area weighting. */ + lines = datas + yp * wpls; + v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); + v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp2); + v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); + v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp2); + *pval = (v00 + v01 + v10 + v11) / 256; + return 0; +} + + + +/*-------------------------------------------------------------* + * Gauss-jordan linear equation solver * + *-------------------------------------------------------------*/ +#define SWAP(a,b) {temp = (a); (a) = (b); (b) = temp;} + +/*! + * \brief gaussjordan() + * + * \param[in] a n x n matrix + * \param[in] b n x 1 right-hand side column vector + * \param[in] n dimension + * \return 0 if ok, 1 on error + * + *
+ * Notes:
+ *      (1) There are two side-effects:
+ *          * The matrix a is transformed to its inverse A
+ *          * The rhs vector b is transformed to the solution x
+ *            of the linear equation ax = b
+ *      (2) The inverse A can then be used to solve the same equation with
+ *          different rhs vectors c by multiplication: x = Ac
+ *      (3) Adapted from "Numerical Recipes in C, Second Edition", 1992,
+ *          pp. 36-41 (gauss-jordan elimination)
+ * 
+ */ +l_int32 +gaussjordan(l_float32 **a, + l_float32 *b, + l_int32 n) +{ +l_int32 i, icol, irow, j, k, col, row, success; +l_int32 *indexc, *indexr, *ipiv; +l_float32 maxval, val, pivinv, temp; + + PROCNAME("gaussjordan"); + + if (!a) + return ERROR_INT("a not defined", procName, 1); + if (!b) + return ERROR_INT("b not defined", procName, 1); + + success = TRUE; + indexc = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); + indexr = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); + ipiv = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); + if (!indexc || !indexr || !ipiv) { + L_ERROR("array not made\n", procName); + success = FALSE; + goto cleanup_arrays; + } + + icol = irow = 0; /* silence static checker */ + for (i = 0; i < n; i++) { + maxval = 0.0; + for (j = 0; j < n; j++) { + if (ipiv[j] != 1) { + for (k = 0; k < n; k++) { + if (ipiv[k] == 0) { + if (fabs(a[j][k]) >= maxval) { + maxval = fabs(a[j][k]); + irow = j; + icol = k; + } + } else if (ipiv[k] > 1) { + L_ERROR("singular matrix\n", procName); + success = FALSE; + goto cleanup_arrays; + } + } + } + } + ++(ipiv[icol]); + + if (irow != icol) { + for (col = 0; col < n; col++) + SWAP(a[irow][col], a[icol][col]); + SWAP(b[irow], b[icol]); + } + + indexr[i] = irow; + indexc[i] = icol; + if (a[icol][icol] == 0.0) { + L_ERROR("singular matrix\n", procName); + success = FALSE; + goto cleanup_arrays; + } + pivinv = 1.0 / a[icol][icol]; + a[icol][icol] = 1.0; + for (col = 0; col < n; col++) + a[icol][col] *= pivinv; + b[icol] *= pivinv; + + for (row = 0; row < n; row++) { + if (row != icol) { + val = a[row][icol]; + a[row][icol] = 0.0; + for (col = 0; col < n; col++) + a[row][col] -= a[icol][col] * val; + b[row] -= b[icol] * val; + } + } + } + + for (col = n - 1; col >= 0; col--) { + if (indexr[col] != indexc[col]) { + for (k = 0; k < n; k++) + SWAP(a[k][indexr[col]], a[k][indexc[col]]); + } + } + +cleanup_arrays: + LEPT_FREE(indexr); + LEPT_FREE(indexc); + LEPT_FREE(ipiv); + return (success) ? 0 : 1; +} + + +/*-------------------------------------------------------------* + * Sequential affine image transformation * + *-------------------------------------------------------------*/ +/*! + * \brief pixAffineSequential() + * + * \param[in] pixs + * \param[in] ptad 3 pts of final coordinate space + * \param[in] ptas 3 pts of initial coordinate space + * \param[in] bw pixels of additional border width during computation + * \param[in] bh pixels of additional border height during computation + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The 3 pts must not be collinear.
+ *      (2) The 3 pts must be given in this order:
+ *           ~ origin
+ *           ~ a location along the x-axis
+ *           ~ a location along the y-axis.
+ *      (3) You must guess how much border must be added so that no
+ *          pixels are lost in the transformations from src to
+ *          dest coordinate space.  (This can be calculated but it
+ *          is a lot of work!)  For coordinate spaces that are nearly
+ *          at right angles, on a 300 ppi scanned page, the addition
+ *          of 1000 pixels on each side is usually sufficient.
+ *      (4) This is here for pedagogical reasons.  It is about 3x faster
+ *          on 1 bpp images than pixAffineSampled(), but the results
+ *          on text are much inferior.
+ * 
+ */ +PIX * +pixAffineSequential(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 bw, + l_int32 bh) +{ +l_int32 x1, y1, x2, y2, x3, y3; /* ptas */ +l_int32 x1p, y1p, x2p, y2p, x3p, y3p; /* ptad */ +l_int32 x1sc, y1sc; /* scaled origin */ +l_float32 x2s, x2sp, scalex, scaley; +l_float32 th3, th3p, ph2, ph2p; +#if DEBUG +l_float32 rad2deg; +#endif /* DEBUG */ +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixAffineSequential"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + + if (ptaGetCount(ptas) != 3) + return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); + if (ptaGetCount(ptad) != 3) + return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); + ptaGetIPt(ptas, 0, &x1, &y1); + ptaGetIPt(ptas, 1, &x2, &y2); + ptaGetIPt(ptas, 2, &x3, &y3); + ptaGetIPt(ptad, 0, &x1p, &y1p); + ptaGetIPt(ptad, 1, &x2p, &y2p); + ptaGetIPt(ptad, 2, &x3p, &y3p); + + pix1 = pix2 = pixd = NULL; + + if (y1 == y3) + return (PIX *)ERROR_PTR("y1 == y3!", procName, NULL); + if (y1p == y3p) + return (PIX *)ERROR_PTR("y1p == y3p!", procName, NULL); + + if (bw != 0 || bh != 0) { + /* resize all points and add border to pixs */ + x1 = x1 + bw; + y1 = y1 + bh; + x2 = x2 + bw; + y2 = y2 + bh; + x3 = x3 + bw; + y3 = y3 + bh; + x1p = x1p + bw; + y1p = y1p + bh; + x2p = x2p + bw; + y2p = y2p + bh; + x3p = x3p + bw; + y3p = y3p + bh; + + if ((pix1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + } else { + pix1 = pixCopy(NULL, pixs); + } + + /*-------------------------------------------------------------* + The horizontal shear is done to move the 3rd point to the + y axis. This moves the 2nd point either towards or away + from the y axis, depending on whether it is above or below + the x axis. That motion must be computed so that we know + the angle of vertical shear to use to get the 2nd point + on the x axis. We must also know the x coordinate of the + 2nd point in order to compute how much scaling is required + to match points on the axis. + *-------------------------------------------------------------*/ + + /* Shear angles required to put src points on x and y axes */ + th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3)); + x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3)); + if (x2s == (l_float32)x1) { + L_ERROR("x2s == x1!\n", procName); + goto cleanup_pix; + } + ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1)); + + /* Shear angles required to put dest points on x and y axes. + * Use the negative of these values to instead move the + * src points from the axes to the actual dest position. + * These values are also needed to scale the image. */ + th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p)); + x2sp = (l_float32)(x2p - + ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p)); + if (x2sp == (l_float32)x1p) { + L_ERROR("x2sp == x1p!\n", procName); + goto cleanup_pix; + } + ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p)); + + /* Shear image to first put src point 3 on the y axis, + * and then to put src point 2 on the x axis */ + pixHShearIP(pix1, y1, th3, L_BRING_IN_WHITE); + pixVShearIP(pix1, x1, ph2, L_BRING_IN_WHITE); + + /* Scale image to match dest scale. The dest scale + * is calculated above from the angles th3p and ph2p + * that would be required to move the dest points to + * the x and y axes. */ + scalex = (l_float32)(x2sp - x1p) / (x2s - x1); + scaley = (l_float32)(y3p - y1p) / (y3 - y1); + if ((pix2 = pixScale(pix1, scalex, scaley)) == NULL) { + L_ERROR("pix2 not made\n", procName); + goto cleanup_pix; + } + +#if DEBUG + rad2deg = 180. / 3.1415926535; + fprintf(stderr, "th3 = %5.1f deg, ph2 = %5.1f deg\n", + rad2deg * th3, rad2deg * ph2); + fprintf(stderr, "th3' = %5.1f deg, ph2' = %5.1f deg\n", + rad2deg * th3p, rad2deg * ph2p); + fprintf(stderr, "scalex = %6.3f, scaley = %6.3f\n", scalex, scaley); +#endif /* DEBUG */ + + /*-------------------------------------------------------------* + Scaling moves the 1st src point, which is the origin. + It must now be moved again to coincide with the origin + (1st point) of the dest. After this is done, the 2nd + and 3rd points must be sheared back to the original + positions of the 2nd and 3rd dest points. We use the + negative of the angles that were previously computed + for shearing those points in the dest image to x and y + axes, and take the shears in reverse order as well. + *-------------------------------------------------------------*/ + /* Shift image to match dest origin. */ + x1sc = (l_int32)(scalex * x1 + 0.5); /* x comp of origin after scaling */ + y1sc = (l_int32)(scaley * y1 + 0.5); /* y comp of origin after scaling */ + pixRasteropIP(pix2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE); + + /* Shear image to take points 2 and 3 off the axis and + * put them in the original dest position */ + pixVShearIP(pix2, x1p, -ph2p, L_BRING_IN_WHITE); + pixHShearIP(pix2, y1p, -th3p, L_BRING_IN_WHITE); + + if (bw != 0 || bh != 0) { + if ((pixd = pixRemoveBorderGeneral(pix2, bw, bw, bh, bh)) == NULL) + L_ERROR("pixd not made\n", procName); + } else { + pixd = pixClone(pix2); + } + +cleanup_pix: + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/affinecompose.c b/hgdriver/3rdparty/hgOCR/leptonica/affinecompose.c new file mode 100644 index 0000000..28be7b9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/affinecompose.c @@ -0,0 +1,662 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file affinecompose.c + *
+ *
+ *      Composable coordinate transforms
+ *           l_float32   *createMatrix2dTranslate()
+ *           l_float32   *createMatrix2dScale()
+ *           l_float32   *createMatrix2dRotate()
+ *
+ *      Special coordinate transforms on pta
+ *           PTA         *ptaTranslate()
+ *           PTA         *ptaScale()
+ *           PTA         *ptaRotate()
+ *
+ *      Special coordinate transforms on boxa
+ *           BOXA        *boxaTranslate()
+ *           BOXA        *boxaScale()
+ *           BOXA        *boxaRotate()
+ *
+ *      General coordinate transform on pta and boxa
+ *           PTA         *ptaAffineTransform()
+ *           BOXA        *boxaAffineTransform()
+ *
+ *      Matrix operations
+ *           l_int32      l_productMatVec()
+ *           l_int32      l_productMat2()
+ *           l_int32      l_productMat3()
+ *           l_int32      l_productMat4()
+ * 
+ */ + +#include +#include "allheaders.h" + + +/*-------------------------------------------------------------* + * Composable coordinate transforms * + *-------------------------------------------------------------*/ +/*! + * \brief createMatrix2dTranslate() + * + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return 3x3 transform matrix, or NULL on error + * + *
+ * Notes:
+ *      (1) The translation is equivalent to:
+ *             v' = Av
+ *          where v and v' are 1x3 column vectors in the form
+ *             v = [x, y, 1]^    ^ denotes transpose
+ *          and the affine translation matrix is
+ *             A = [ 1   0   tx
+ *                   0   1   ty
+ *                   0   0    1  ]
+ *
+ *      (2) We consider translation as with respect to a fixed origin.
+ *          In a clipping operation, the origin moves and the points
+ *          are fixed, and you use (-tx, -ty) where (tx, ty) is the
+ *          translation vector of the origin.
+ * 
+ */ +l_float32 * +createMatrix2dTranslate(l_float32 transx, + l_float32 transy) +{ +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + mat[0] = mat[4] = mat[8] = 1; + mat[2] = transx; + mat[5] = transy; + return mat; +} + + +/*! + * \brief createMatrix2dScale() + * + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return 3x3 transform matrix, or NULL on error + * + *
+ * Notes:
+ *      (1) The scaling is equivalent to:
+ *             v' = Av
+ *         where v and v' are 1x3 column vectors in the form
+ *              v = [x, y, 1]^    ^ denotes transpose
+ *         and the affine scaling matrix is
+ *             A = [ sx  0    0
+ *                   0   sy   0
+ *                   0   0    1  ]
+ *
+ *      (2) We consider scaling as with respect to a fixed origin.
+ *          In other words, the origin is the only point that doesn't
+ *          move in the scaling transform.
+ * 
+ */ +l_float32 * +createMatrix2dScale(l_float32 scalex, + l_float32 scaley) +{ +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + mat[0] = scalex; + mat[4] = scaley; + mat[8] = 1; + return mat; +} + + +/*! + * \brief createMatrix2dRotate() + * + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return 3x3 transform matrix, or NULL on error + * + *
+ * Notes:
+ *      (1) The rotation is equivalent to:
+ *             v' = Av
+ *          where v and v' are 1x3 column vectors in the form
+ *             v = [x, y, 1]^    ^ denotes transpose
+ *          and the affine rotation matrix is
+ *             A = [ cosa   -sina    xc*1-cosa + yc*sina
+ *                   sina    cosa    yc*1-cosa - xc*sina
+ *                     0       0                 1         ]
+ *
+ *          If the rotation is about the origin, xc, yc) = (0, 0 and
+ *          this simplifies to
+ *             A = [ cosa   -sina    0
+ *                   sina    cosa    0
+ *                     0       0     1 ]
+ *
+ *          These relations follow from the following equations, which
+ *          you can convince yourself are correct as follows.  Draw a
+ *          circle centered on xc,yc) and passing through (x,y), with
+ *          (x',y') on the arc at an angle 'a' clockwise from (x,y).
+ *           [ Hint: cosa + b = cosa * cosb - sina * sinb
+ *                   sina + b = sina * cosb + cosa * sinb ]
+ *
+ *            x' - xc =  x - xc) * cosa - (y - yc * sina
+ *            y' - yc =  x - xc) * sina + (y - yc * cosa
+ * 
+ */ +l_float32 * +createMatrix2dRotate(l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +l_float32 sina, cosa; +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + sina = sin(angle); + cosa = cos(angle); + mat[0] = mat[4] = cosa; + mat[1] = -sina; + mat[2] = xc * (1.0 - cosa) + yc * sina; + mat[3] = sina; + mat[5] = yc * (1.0 - cosa) - xc * sina; + mat[8] = 1; + return mat; +} + + + +/*-------------------------------------------------------------* + * Special coordinate transforms on pta * + *-------------------------------------------------------------*/ +/*! + * \brief ptaTranslate() + * + * \param[in] ptas for initial points + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return ptad translated points, or NULL on error + * + *
+ * Notes:
+ *      (1) See createMatrix2dTranslate() for details of transform.
+ * 
+ */ +PTA * +ptaTranslate(PTA *ptas, + l_float32 transx, + l_float32 transy) +{ +l_int32 i, npts; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaTranslate"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, x + transx, y + transy); + } + + return ptad; +} + + +/*! + * \brief ptaScale() + * + * \param[in] ptas for initial points + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See createMatrix2dScale() for details of transform.
+ * 
+ */ +PTA * +ptaScale(PTA *ptas, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 i, npts; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaScale"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, scalex * x, scaley * y); + } + + return ptad; +} + + +/*! + * \brief ptaRotate() + * + * \param[in] ptas for initial points + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return 0 if OK; 1 on error + * + *
+ * Notes;
+ *      (1) See createMatrix2dScale() for details of transform.
+ *      (2) This transform can be thought of as composed of the
+ *          sum of two parts:
+ *           a) an (x,y)-dependent rotation about the origin:
+ *              xr = x * cosa - y * sina
+ *              yr = x * sina + y * cosa
+ *           b) an (x,y)-independent translation that depends on the
+ *              rotation center and the angle:
+ *              xt = xc - xc * cosa + yc * sina
+ *              yt = yc - xc * sina - yc * cosa
+ *          The translation part (xt,yt) is equal to the difference
+ *          between the center (xc,yc) and the location of the
+ *          center after it is rotated about the origin.
+ * 
+ */ +PTA * +ptaRotate(PTA *ptas, + l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +l_int32 i, npts; +l_float32 x, y, xp, yp, sina, cosa; +PTA *ptad; + + PROCNAME("ptaRotate"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + sina = sin(angle); + cosa = cos(angle); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + xp = xc + (x - xc) * cosa - (y - yc) * sina; + yp = yc + (x - xc) * sina + (y - yc) * cosa; + ptaAddPt(ptad, xp, yp); + } + + return ptad; +} + + +/*-------------------------------------------------------------* + * Special coordinate transforms on boxa * + *-------------------------------------------------------------*/ +/*! + * \brief boxaTranslate() + * + * \param[in] boxas + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return boxad translated boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dTranslate() for details of transform. + */ +BOXA * +boxaTranslate(BOXA *boxas, + l_float32 transx, + l_float32 transy) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + PROCNAME("boxaTranslate"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaTranslate(ptas, transx, transy); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*! + * \brief boxaScale() + * + * \param[in] boxas + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return boxad scaled boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dScale() for details of transform. + */ +BOXA * +boxaScale(BOXA *boxas, + l_float32 scalex, + l_float32 scaley) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + PROCNAME("boxaScale"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaScale(ptas, scalex, scaley); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*! + * \brief boxaRotate() + * + * \param[in] boxas + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return boxad scaled boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dRotate() for details of transform. + */ +BOXA * +boxaRotate(BOXA *boxas, + l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + PROCNAME("boxaRotate"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaRotate(ptas, xc, yc, angle); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*-------------------------------------------------------------* + * General affine coordinate transform * + *-------------------------------------------------------------*/ +/*! + * \brief ptaAffineTransform() + * + * \param[in] ptas for initial points + * \param[in] mat 3x3 transform matrix; canonical form + * \return ptad transformed points, or NULL on error + */ +PTA * +ptaAffineTransform(PTA *ptas, + l_float32 *mat) +{ +l_int32 i, npts; +l_float32 vecs[3], vecd[3]; +PTA *ptad; + + PROCNAME("ptaAffineTransform"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (!mat) + return (PTA *)ERROR_PTR("transform not defined", procName, NULL); + + vecs[2] = 1; + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &vecs[0], &vecs[1]); + l_productMatVec(mat, vecs, vecd, 3); + ptaAddPt(ptad, vecd[0], vecd[1]); + } + + return ptad; +} + + +/*! + * \brief boxaAffineTransform() + * + * \param[in] boxas + * \param[in] mat 3x3 transform matrix; canonical form + * \return boxad transformed boxas, or NULL on error + */ +BOXA * +boxaAffineTransform(BOXA *boxas, + l_float32 *mat) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + PROCNAME("boxaAffineTransform"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!mat) + return (BOXA *)ERROR_PTR("transform not defined", procName, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaAffineTransform(ptas, mat); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*-------------------------------------------------------------* + * Matrix operations * + *-------------------------------------------------------------*/ +/*! + * \brief l_productMatVec() + * + * \param[in] mat square matrix, as a 1-dimensional %size^2 array + * \param[in] vecs input column vector of length %size + * \param[in] vecd result column vector + * \param[in] size matrix is %size x %size; vectors are length %size + * \return 0 if OK, 1 on error + */ +l_ok +l_productMatVec(l_float32 *mat, + l_float32 *vecs, + l_float32 *vecd, + l_int32 size) +{ +l_int32 i, j; + + PROCNAME("l_productMatVec"); + + if (!mat) + return ERROR_INT("matrix not defined", procName, 1); + if (!vecs) + return ERROR_INT("input vector not defined", procName, 1); + if (!vecd) + return ERROR_INT("result vector not defined", procName, 1); + + for (i = 0; i < size; i++) { + vecd[i] = 0; + for (j = 0; j < size; j++) { + vecd[i] += mat[size * i + j] * vecs[j]; + } + } + return 0; +} + + +/*! + * \brief l_productMat2() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat2(l_float32 *mat1, + l_float32 *mat2, + l_float32 *matd, + l_int32 size) +{ +l_int32 i, j, k, index; + + PROCNAME("l_productMat2"); + + if (!mat1) + return ERROR_INT("matrix 1 not defined", procName, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", procName, 1); + if (!matd) + return ERROR_INT("result matrix not defined", procName, 1); + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + index = size * i + j; + matd[index] = 0; + for (k = 0; k < size; k++) + matd[index] += mat1[size * i + k] * mat2[size * k + j]; + } + } + return 0; +} + + +/*! + * \brief l_productMat3() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] mat3 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat3(l_float32 *mat1, + l_float32 *mat2, + l_float32 *mat3, + l_float32 *matd, + l_int32 size) +{ +l_float32 *matt; + + PROCNAME("l_productMat3"); + + if (!mat1) + return ERROR_INT("matrix 1 not defined", procName, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", procName, 1); + if (!mat3) + return ERROR_INT("matrix 3 not defined", procName, 1); + if (!matd) + return ERROR_INT("result matrix not defined", procName, 1); + + if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, + sizeof(l_float32))) == NULL) + return ERROR_INT("matt not made", procName, 1); + l_productMat2(mat1, mat2, matt, size); + l_productMat2(matt, mat3, matd, size); + LEPT_FREE(matt); + return 0; +} + + +/*! + * \brief l_productMat4() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] mat3 square matrix, as a 1-dimensional size^2 array + * \param[in] mat4 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat4(l_float32 *mat1, + l_float32 *mat2, + l_float32 *mat3, + l_float32 *mat4, + l_float32 *matd, + l_int32 size) +{ +l_float32 *matt; + + PROCNAME("l_productMat4"); + + if (!mat1) + return ERROR_INT("matrix 1 not defined", procName, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", procName, 1); + if (!mat3) + return ERROR_INT("matrix 3 not defined", procName, 1); + if (!matd) + return ERROR_INT("result matrix not defined", procName, 1); + + if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, + sizeof(l_float32))) == NULL) + return ERROR_INT("matt not made", procName, 1); + l_productMat3(mat1, mat2, mat3, matt, size); + l_productMat2(matt, mat4, matd, size); + LEPT_FREE(matt); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/allheaders.h b/hgdriver/3rdparty/hgOCR/leptonica/allheaders.h new file mode 100644 index 0000000..70c45a6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/allheaders.h @@ -0,0 +1,2741 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_ALLHEADERS_H +#define LEPTONICA_ALLHEADERS_H + + +#define LIBLEPT_MAJOR_VERSION 1 +#define LIBLEPT_MINOR_VERSION 79 +#define LIBLEPT_PATCH_VERSION 0 + +#include "alltypes.h" + +#ifndef NO_PROTOS +/* + * These prototypes were autogen'd by xtractprotos, v. 1.5 + */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +LEPT_DLL extern PIX * pixCleanBackgroundToWhite ( PIX *pixs, PIX *pixim, PIX *pixg, l_float32 gamma, l_int32 blackval, l_int32 whiteval ); +LEPT_DLL extern PIX * pixBackgroundNormSimple ( PIX *pixs, PIX *pixim, PIX *pixg ); +LEPT_DLL extern PIX * pixBackgroundNorm ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy ); +LEPT_DLL extern PIX * pixBackgroundNormMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval ); +LEPT_DLL extern l_ok pixBackgroundNormGrayArray ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, PIX **ppixd ); +LEPT_DLL extern l_ok pixBackgroundNormRGBArrays ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, PIX **ppixr, PIX **ppixg, PIX **ppixb ); +LEPT_DLL extern l_ok pixBackgroundNormGrayArrayMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixd ); +LEPT_DLL extern l_ok pixBackgroundNormRGBArraysMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixr, PIX **ppixg, PIX **ppixb ); +LEPT_DLL extern l_ok pixGetBackgroundGrayMap ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixd ); +LEPT_DLL extern l_ok pixGetBackgroundRGBMap ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb ); +LEPT_DLL extern l_ok pixGetBackgroundGrayMapMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixm ); +LEPT_DLL extern l_ok pixGetBackgroundRGBMapMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb ); +LEPT_DLL extern l_ok pixFillMapHoles ( PIX *pix, l_int32 nx, l_int32 ny, l_int32 filltype ); +LEPT_DLL extern PIX * pixExtendByReplication ( PIX *pixs, l_int32 addw, l_int32 addh ); +LEPT_DLL extern l_ok pixSmoothConnectedRegions ( PIX *pixs, PIX *pixm, l_int32 factor ); +LEPT_DLL extern PIX * pixGetInvBackgroundMap ( PIX *pixs, l_int32 bgval, l_int32 smoothx, l_int32 smoothy ); +LEPT_DLL extern PIX * pixApplyInvBackgroundGrayMap ( PIX *pixs, PIX *pixm, l_int32 sx, l_int32 sy ); +LEPT_DLL extern PIX * pixApplyInvBackgroundRGBMap ( PIX *pixs, PIX *pixmr, PIX *pixmg, PIX *pixmb, l_int32 sx, l_int32 sy ); +LEPT_DLL extern PIX * pixApplyVariableGrayMap ( PIX *pixs, PIX *pixg, l_int32 target ); +LEPT_DLL extern PIX * pixGlobalNormRGB ( PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 mapval ); +LEPT_DLL extern PIX * pixGlobalNormNoSatRGB ( PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 factor, l_float32 rank ); +LEPT_DLL extern l_ok pixThresholdSpreadNorm ( PIX *pixs, l_int32 filtertype, l_int32 edgethresh, l_int32 smoothx, l_int32 smoothy, l_float32 gamma, l_int32 minval, l_int32 maxval, l_int32 targetthresh, PIX **ppixth, PIX **ppixb, PIX **ppixd ); +LEPT_DLL extern PIX * pixBackgroundNormFlex ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta ); +LEPT_DLL extern PIX * pixContrastNorm ( PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy ); +LEPT_DLL extern l_ok pixMinMaxTiles ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy, PIX **ppixmin, PIX **ppixmax ); +LEPT_DLL extern l_ok pixSetLowContrast ( PIX *pixs1, PIX *pixs2, l_int32 mindiff ); +LEPT_DLL extern PIX * pixLinearTRCTiled ( PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, PIX *pixmin, PIX *pixmax ); +LEPT_DLL extern PIX * pixAffineSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixAffineSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixAffinePta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixAffine ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixAffinePtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); +LEPT_DLL extern PIX * pixAffineColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); +LEPT_DLL extern PIX * pixAffinePtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); +LEPT_DLL extern PIX * pixAffineGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); +LEPT_DLL extern PIX * pixAffinePtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); +LEPT_DLL extern l_ok getAffineXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); +LEPT_DLL extern l_ok affineInvertXform ( l_float32 *vc, l_float32 **pvci ); +LEPT_DLL extern l_ok affineXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); +LEPT_DLL extern l_ok affineXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); +LEPT_DLL extern l_ok linearInterpolatePixelColor ( l_uint32 *datas, l_int32 wpls, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_uint32 colorval, l_uint32 *pval ); +LEPT_DLL extern l_ok linearInterpolatePixelGray ( l_uint32 *datas, l_int32 wpls, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_int32 grayval, l_int32 *pval ); +LEPT_DLL extern l_int32 gaussjordan ( l_float32 **a, l_float32 *b, l_int32 n ); +LEPT_DLL extern PIX * pixAffineSequential ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 bw, l_int32 bh ); +LEPT_DLL extern l_float32 * createMatrix2dTranslate ( l_float32 transx, l_float32 transy ); +LEPT_DLL extern l_float32 * createMatrix2dScale ( l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern l_float32 * createMatrix2dRotate ( l_float32 xc, l_float32 yc, l_float32 angle ); +LEPT_DLL extern PTA * ptaTranslate ( PTA *ptas, l_float32 transx, l_float32 transy ); +LEPT_DLL extern PTA * ptaScale ( PTA *ptas, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PTA * ptaRotate ( PTA *ptas, l_float32 xc, l_float32 yc, l_float32 angle ); +LEPT_DLL extern BOXA * boxaTranslate ( BOXA *boxas, l_float32 transx, l_float32 transy ); +LEPT_DLL extern BOXA * boxaScale ( BOXA *boxas, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern BOXA * boxaRotate ( BOXA *boxas, l_float32 xc, l_float32 yc, l_float32 angle ); +LEPT_DLL extern PTA * ptaAffineTransform ( PTA *ptas, l_float32 *mat ); +LEPT_DLL extern BOXA * boxaAffineTransform ( BOXA *boxas, l_float32 *mat ); +LEPT_DLL extern l_ok l_productMatVec ( l_float32 *mat, l_float32 *vecs, l_float32 *vecd, l_int32 size ); +LEPT_DLL extern l_ok l_productMat2 ( l_float32 *mat1, l_float32 *mat2, l_float32 *matd, l_int32 size ); +LEPT_DLL extern l_ok l_productMat3 ( l_float32 *mat1, l_float32 *mat2, l_float32 *mat3, l_float32 *matd, l_int32 size ); +LEPT_DLL extern l_ok l_productMat4 ( l_float32 *mat1, l_float32 *mat2, l_float32 *mat3, l_float32 *mat4, l_float32 *matd, l_int32 size ); +LEPT_DLL extern l_int32 l_getDataBit ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataBit ( void *line, l_int32 n ); +LEPT_DLL extern void l_clearDataBit ( void *line, l_int32 n ); +LEPT_DLL extern void l_setDataBitVal ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern l_int32 l_getDataDibit ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataDibit ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern void l_clearDataDibit ( void *line, l_int32 n ); +LEPT_DLL extern l_int32 l_getDataQbit ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataQbit ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern void l_clearDataQbit ( void *line, l_int32 n ); +LEPT_DLL extern l_int32 l_getDataByte ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataByte ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern l_int32 l_getDataTwoBytes ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataTwoBytes ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern l_int32 l_getDataFourBytes ( const void *line, l_int32 n ); +LEPT_DLL extern void l_setDataFourBytes ( void *line, l_int32 n, l_int32 val ); +LEPT_DLL extern char * barcodeDispatchDecoder ( char *barstr, l_int32 format, l_int32 debugflag ); +LEPT_DLL extern l_int32 barcodeFormatIsSupported ( l_int32 format ); +LEPT_DLL extern NUMA * pixFindBaselines ( PIX *pixs, PTA **ppta, PIXA *pixadb ); +LEPT_DLL extern PIX * pixDeskewLocal ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); +LEPT_DLL extern l_ok pixGetLocalSkewTransform ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, PTA **pptas, PTA **pptad ); +LEPT_DLL extern NUMA * pixGetLocalSkewAngles ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_float32 *pa, l_float32 *pb, l_int32 debug ); +LEPT_DLL extern L_BBUFFER * bbufferCreate ( const l_uint8 *indata, l_int32 nalloc ); +LEPT_DLL extern void bbufferDestroy ( L_BBUFFER **pbb ); +LEPT_DLL extern l_uint8 * bbufferDestroyAndSaveData ( L_BBUFFER **pbb, size_t *pnbytes ); +LEPT_DLL extern l_ok bbufferRead ( L_BBUFFER *bb, l_uint8 *src, l_int32 nbytes ); +LEPT_DLL extern l_ok bbufferReadStream ( L_BBUFFER *bb, FILE *fp, l_int32 nbytes ); +LEPT_DLL extern l_ok bbufferExtendArray ( L_BBUFFER *bb, l_int32 nbytes ); +LEPT_DLL extern l_ok bbufferWrite ( L_BBUFFER *bb, l_uint8 *dest, size_t nbytes, size_t *pnout ); +LEPT_DLL extern l_ok bbufferWriteStream ( L_BBUFFER *bb, FILE *fp, size_t nbytes, size_t *pnout ); +LEPT_DLL extern PIX * pixBilateral ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction ); +LEPT_DLL extern PIX * pixBilateralGray ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction ); +LEPT_DLL extern PIX * pixBilateralExact ( PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel ); +LEPT_DLL extern PIX * pixBilateralGrayExact ( PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel ); +LEPT_DLL extern PIX* pixBlockBilateralExact ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev ); +LEPT_DLL extern L_KERNEL * makeRangeKernel ( l_float32 range_stdev ); +LEPT_DLL extern PIX * pixBilinearSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixBilinearSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixBilinearPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixBilinear ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixBilinearPtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); +LEPT_DLL extern PIX * pixBilinearColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); +LEPT_DLL extern PIX * pixBilinearPtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); +LEPT_DLL extern PIX * pixBilinearGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); +LEPT_DLL extern PIX * pixBilinearPtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); +LEPT_DLL extern l_ok getBilinearXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); +LEPT_DLL extern l_ok bilinearXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); +LEPT_DLL extern l_ok bilinearXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); +LEPT_DLL extern l_ok pixOtsuAdaptiveThreshold ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, PIX **ppixth, PIX **ppixd ); +LEPT_DLL extern PIX * pixOtsuThreshOnBackgroundNorm ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh ); +LEPT_DLL extern PIX * pixMaskedThreshOnBackgroundNorm ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh ); +LEPT_DLL extern l_ok pixSauvolaBinarizeTiled ( PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 nx, l_int32 ny, PIX **ppixth, PIX **ppixd ); +LEPT_DLL extern l_ok pixSauvolaBinarize ( PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 addborder, PIX **ppixm, PIX **ppixsd, PIX **ppixth, PIX **ppixd ); +LEPT_DLL extern l_ok pixThresholdByConnComp ( PIX *pixs, PIX *pixm, l_int32 start, l_int32 end, l_int32 incr, l_float32 thresh48, l_float32 threshdiff, l_int32 *pglobthresh, PIX **ppixd, l_int32 debugflag ); +LEPT_DLL extern PIX * pixExpandBinaryReplicate ( PIX *pixs, l_int32 xfact, l_int32 yfact ); +LEPT_DLL extern PIX * pixExpandBinaryPower2 ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern PIX * pixReduceBinary2 ( PIX *pixs, l_uint8 *intab ); +LEPT_DLL extern PIX * pixReduceRankBinaryCascade ( PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4 ); +LEPT_DLL extern PIX * pixReduceRankBinary2 ( PIX *pixs, l_int32 level, l_uint8 *intab ); +LEPT_DLL extern l_uint8 * makeSubsampleTab2x ( void ); +LEPT_DLL extern PIX * pixBlend ( PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); +LEPT_DLL extern PIX * pixBlendMask ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type ); +LEPT_DLL extern PIX * pixBlendGray ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type, l_int32 transparent, l_uint32 transpix ); +LEPT_DLL extern PIX * pixBlendGrayInverse ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); +LEPT_DLL extern PIX * pixBlendColor ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 transparent, l_uint32 transpix ); +LEPT_DLL extern PIX * pixBlendColorByChannel ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 rfract, l_float32 gfract, l_float32 bfract, l_int32 transparent, l_uint32 transpix ); +LEPT_DLL extern PIX * pixBlendGrayAdapt ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 shift ); +LEPT_DLL extern PIX * pixFadeWithGray ( PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type ); +LEPT_DLL extern PIX * pixBlendHardLight ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); +LEPT_DLL extern l_ok pixBlendCmap ( PIX *pixs, PIX *pixb, l_int32 x, l_int32 y, l_int32 sindex ); +LEPT_DLL extern PIX * pixBlendWithGrayMask ( PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y ); +LEPT_DLL extern PIX * pixBlendBackgroundToColor ( PIX *pixd, PIX *pixs, BOX *box, l_uint32 color, l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern PIX * pixMultiplyByColor ( PIX *pixd, PIX *pixs, BOX *box, l_uint32 color ); +LEPT_DLL extern PIX * pixAlphaBlendUniform ( PIX *pixs, l_uint32 color ); +LEPT_DLL extern PIX * pixAddAlphaToBlend ( PIX *pixs, l_float32 fract, l_int32 invert ); +LEPT_DLL extern PIX * pixSetAlphaOverWhite ( PIX *pixs ); +LEPT_DLL extern l_ok pixLinearEdgeFade ( PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade ); +LEPT_DLL extern L_BMF * bmfCreate ( const char *dir, l_int32 fontsize ); +LEPT_DLL extern void bmfDestroy ( L_BMF **pbmf ); +LEPT_DLL extern PIX * bmfGetPix ( L_BMF *bmf, char chr ); +LEPT_DLL extern l_ok bmfGetWidth ( L_BMF *bmf, char chr, l_int32 *pw ); +LEPT_DLL extern l_ok bmfGetBaseline ( L_BMF *bmf, char chr, l_int32 *pbaseline ); +LEPT_DLL extern PIXA * pixaGetFont ( const char *dir, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); +LEPT_DLL extern l_ok pixaSaveFont ( const char *indir, const char *outdir, l_int32 fontsize ); +LEPT_DLL extern PIX * pixReadStreamBmp ( FILE *fp ); +LEPT_DLL extern PIX * pixReadMemBmp ( const l_uint8 *cdata, size_t size ); +LEPT_DLL extern l_ok pixWriteStreamBmp ( FILE *fp, PIX *pix ); +LEPT_DLL extern l_ok pixWriteMemBmp ( l_uint8 **pfdata, size_t *pfsize, PIX *pixs ); +LEPT_DLL extern PIXA * l_bootnum_gen1 ( void ); +LEPT_DLL extern PIXA * l_bootnum_gen2 ( void ); +LEPT_DLL extern PIXA * l_bootnum_gen3 ( void ); +LEPT_DLL extern PIXA * l_bootnum_gen4 ( l_int32 nsamp ); +LEPT_DLL extern BOX * boxCreate ( l_int32 x, l_int32 y, l_int32 w, l_int32 h ); +LEPT_DLL extern BOX * boxCreateValid ( l_int32 x, l_int32 y, l_int32 w, l_int32 h ); +LEPT_DLL extern BOX * boxCopy ( BOX *box ); +LEPT_DLL extern BOX * boxClone ( BOX *box ); +LEPT_DLL extern void boxDestroy ( BOX **pbox ); +LEPT_DLL extern l_ok boxGetGeometry ( BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok boxSetGeometry ( BOX *box, l_int32 x, l_int32 y, l_int32 w, l_int32 h ); +LEPT_DLL extern l_ok boxGetSideLocations ( BOX *box, l_int32 *pl, l_int32 *pr, l_int32 *pt, l_int32 *pb ); +LEPT_DLL extern l_ok boxSetSideLocations ( BOX *box, l_int32 l, l_int32 r, l_int32 t, l_int32 b ); +LEPT_DLL extern l_int32 boxGetRefcount ( BOX *box ); +LEPT_DLL extern l_ok boxChangeRefcount ( BOX *box, l_int32 delta ); +LEPT_DLL extern l_ok boxIsValid ( BOX *box, l_int32 *pvalid ); +LEPT_DLL extern BOXA * boxaCreate ( l_int32 n ); +LEPT_DLL extern BOXA * boxaCopy ( BOXA *boxa, l_int32 copyflag ); +LEPT_DLL extern void boxaDestroy ( BOXA **pboxa ); +LEPT_DLL extern l_ok boxaAddBox ( BOXA *boxa, BOX *box, l_int32 copyflag ); +LEPT_DLL extern l_ok boxaExtendArray ( BOXA *boxa ); +LEPT_DLL extern l_ok boxaExtendArrayToSize ( BOXA *boxa, l_int32 size ); +LEPT_DLL extern l_int32 boxaGetCount ( BOXA *boxa ); +LEPT_DLL extern l_int32 boxaGetValidCount ( BOXA *boxa ); +LEPT_DLL extern BOX * boxaGetBox ( BOXA *boxa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern BOX * boxaGetValidBox ( BOXA *boxa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern NUMA * boxaFindInvalidBoxes ( BOXA *boxa ); +LEPT_DLL extern l_ok boxaGetBoxGeometry ( BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok boxaIsFull ( BOXA *boxa, l_int32 *pfull ); +LEPT_DLL extern l_ok boxaReplaceBox ( BOXA *boxa, l_int32 index, BOX *box ); +LEPT_DLL extern l_ok boxaInsertBox ( BOXA *boxa, l_int32 index, BOX *box ); +LEPT_DLL extern l_ok boxaRemoveBox ( BOXA *boxa, l_int32 index ); +LEPT_DLL extern l_ok boxaRemoveBoxAndSave ( BOXA *boxa, l_int32 index, BOX **pbox ); +LEPT_DLL extern BOXA * boxaSaveValid ( BOXA *boxas, l_int32 copyflag ); +LEPT_DLL extern l_ok boxaInitFull ( BOXA *boxa, BOX *box ); +LEPT_DLL extern l_ok boxaClear ( BOXA *boxa ); +LEPT_DLL extern BOXAA * boxaaCreate ( l_int32 n ); +LEPT_DLL extern BOXAA * boxaaCopy ( BOXAA *baas, l_int32 copyflag ); +LEPT_DLL extern void boxaaDestroy ( BOXAA **pbaa ); +LEPT_DLL extern l_ok boxaaAddBoxa ( BOXAA *baa, BOXA *ba, l_int32 copyflag ); +LEPT_DLL extern l_ok boxaaExtendArray ( BOXAA *baa ); +LEPT_DLL extern l_ok boxaaExtendArrayToSize ( BOXAA *baa, l_int32 size ); +LEPT_DLL extern l_int32 boxaaGetCount ( BOXAA *baa ); +LEPT_DLL extern l_int32 boxaaGetBoxCount ( BOXAA *baa ); +LEPT_DLL extern BOXA * boxaaGetBoxa ( BOXAA *baa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern BOX * boxaaGetBox ( BOXAA *baa, l_int32 iboxa, l_int32 ibox, l_int32 accessflag ); +LEPT_DLL extern l_ok boxaaInitFull ( BOXAA *baa, BOXA *boxa ); +LEPT_DLL extern l_ok boxaaExtendWithInit ( BOXAA *baa, l_int32 maxindex, BOXA *boxa ); +LEPT_DLL extern l_ok boxaaReplaceBoxa ( BOXAA *baa, l_int32 index, BOXA *boxa ); +LEPT_DLL extern l_ok boxaaInsertBoxa ( BOXAA *baa, l_int32 index, BOXA *boxa ); +LEPT_DLL extern l_ok boxaaRemoveBoxa ( BOXAA *baa, l_int32 index ); +LEPT_DLL extern l_ok boxaaAddBox ( BOXAA *baa, l_int32 index, BOX *box, l_int32 accessflag ); +LEPT_DLL extern BOXAA * boxaaReadFromFiles ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); +LEPT_DLL extern BOXAA * boxaaRead ( const char *filename ); +LEPT_DLL extern BOXAA * boxaaReadStream ( FILE *fp ); +LEPT_DLL extern BOXAA * boxaaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok boxaaWrite ( const char *filename, BOXAA *baa ); +LEPT_DLL extern l_ok boxaaWriteStream ( FILE *fp, BOXAA *baa ); +LEPT_DLL extern l_ok boxaaWriteMem ( l_uint8 **pdata, size_t *psize, BOXAA *baa ); +LEPT_DLL extern BOXA * boxaRead ( const char *filename ); +LEPT_DLL extern BOXA * boxaReadStream ( FILE *fp ); +LEPT_DLL extern BOXA * boxaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok boxaWriteDebug ( const char *filename, BOXA *boxa ); +LEPT_DLL extern l_ok boxaWrite ( const char *filename, BOXA *boxa ); +LEPT_DLL extern l_ok boxaWriteStream ( FILE *fp, BOXA *boxa ); +LEPT_DLL extern l_ok boxaWriteMem ( l_uint8 **pdata, size_t *psize, BOXA *boxa ); +LEPT_DLL extern l_ok boxPrintStreamInfo ( FILE *fp, BOX *box ); +LEPT_DLL extern l_ok boxContains ( BOX *box1, BOX *box2, l_int32 *presult ); +LEPT_DLL extern l_ok boxIntersects ( BOX *box1, BOX *box2, l_int32 *presult ); +LEPT_DLL extern BOXA * boxaContainedInBox ( BOXA *boxas, BOX *box ); +LEPT_DLL extern l_ok boxaContainedInBoxCount ( BOXA *boxa, BOX *box, l_int32 *pcount ); +LEPT_DLL extern l_ok boxaContainedInBoxa ( BOXA *boxa1, BOXA *boxa2, l_int32 *pcontained ); +LEPT_DLL extern BOXA * boxaIntersectsBox ( BOXA *boxas, BOX *box ); +LEPT_DLL extern l_ok boxaIntersectsBoxCount ( BOXA *boxa, BOX *box, l_int32 *pcount ); +LEPT_DLL extern BOXA * boxaClipToBox ( BOXA *boxas, BOX *box ); +LEPT_DLL extern BOXA * boxaCombineOverlaps ( BOXA *boxas, PIXA *pixadb ); +LEPT_DLL extern l_ok boxaCombineOverlapsInPair ( BOXA *boxas1, BOXA *boxas2, BOXA **pboxad1, BOXA **pboxad2, PIXA *pixadb ); +LEPT_DLL extern BOX * boxOverlapRegion ( BOX *box1, BOX *box2 ); +LEPT_DLL extern BOX * boxBoundingRegion ( BOX *box1, BOX *box2 ); +LEPT_DLL extern l_ok boxOverlapFraction ( BOX *box1, BOX *box2, l_float32 *pfract ); +LEPT_DLL extern l_ok boxOverlapArea ( BOX *box1, BOX *box2, l_int32 *parea ); +LEPT_DLL extern BOXA * boxaHandleOverlaps ( BOXA *boxas, l_int32 op, l_int32 range, l_float32 min_overlap, l_float32 max_ratio, NUMA **pnamap ); +LEPT_DLL extern l_ok boxOverlapDistance ( BOX *box1, BOX *box2, l_int32 *ph_ovl, l_int32 *pv_ovl ); +LEPT_DLL extern l_ok boxSeparationDistance ( BOX *box1, BOX *box2, l_int32 *ph_sep, l_int32 *pv_sep ); +LEPT_DLL extern l_ok boxCompareSize ( BOX *box1, BOX *box2, l_int32 type, l_int32 *prel ); +LEPT_DLL extern l_ok boxContainsPt ( BOX *box, l_float32 x, l_float32 y, l_int32 *pcontains ); +LEPT_DLL extern BOX * boxaGetNearestToPt ( BOXA *boxa, l_int32 x, l_int32 y ); +LEPT_DLL extern BOX * boxaGetNearestToLine ( BOXA *boxa, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok boxaFindNearestBoxes ( BOXA *boxa, l_int32 dist_select, l_int32 range, NUMAA **pnaaindex, NUMAA **pnaadist ); +LEPT_DLL extern l_ok boxaGetNearestByDirection ( BOXA *boxa, l_int32 i, l_int32 dir, l_int32 dist_select, l_int32 range, l_int32 *pindex, l_int32 *pdist ); +LEPT_DLL extern l_ok boxGetCenter ( BOX *box, l_float32 *pcx, l_float32 *pcy ); +LEPT_DLL extern l_ok boxIntersectByLine ( BOX *box, l_int32 x, l_int32 y, l_float32 slope, l_int32 *px1, l_int32 *py1, l_int32 *px2, l_int32 *py2, l_int32 *pn ); +LEPT_DLL extern BOX * boxClipToRectangle ( BOX *box, l_int32 wi, l_int32 hi ); +LEPT_DLL extern l_ok boxClipToRectangleParams ( BOX *box, l_int32 w, l_int32 h, l_int32 *pxstart, l_int32 *pystart, l_int32 *pxend, l_int32 *pyend, l_int32 *pbw, l_int32 *pbh ); +LEPT_DLL extern BOX * boxRelocateOneSide ( BOX *boxd, BOX *boxs, l_int32 loc, l_int32 sideflag ); +LEPT_DLL extern BOXA * boxaAdjustSides ( BOXA *boxas, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); +LEPT_DLL extern l_ok boxaAdjustBoxSides ( BOXA *boxa, l_int32 index, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); +LEPT_DLL extern BOX * boxAdjustSides ( BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); +LEPT_DLL extern BOXA * boxaSetSide ( BOXA *boxad, BOXA *boxas, l_int32 side, l_int32 val, l_int32 thresh ); +LEPT_DLL extern l_ok boxSetSide ( BOX *boxs, l_int32 side, l_int32 val, l_int32 thresh ); +LEPT_DLL extern BOXA * boxaAdjustWidthToTarget ( BOXA *boxad, BOXA *boxas, l_int32 sides, l_int32 target, l_int32 thresh ); +LEPT_DLL extern BOXA * boxaAdjustHeightToTarget ( BOXA *boxad, BOXA *boxas, l_int32 sides, l_int32 target, l_int32 thresh ); +LEPT_DLL extern l_ok boxEqual ( BOX *box1, BOX *box2, l_int32 *psame ); +LEPT_DLL extern l_ok boxaEqual ( BOXA *boxa1, BOXA *boxa2, l_int32 maxdist, NUMA **pnaindex, l_int32 *psame ); +LEPT_DLL extern l_ok boxSimilar ( BOX *box1, BOX *box2, l_int32 leftdiff, l_int32 rightdiff, l_int32 topdiff, l_int32 botdiff, l_int32 *psimilar ); +LEPT_DLL extern l_ok boxaSimilar ( BOXA *boxa1, BOXA *boxa2, l_int32 leftdiff, l_int32 rightdiff, l_int32 topdiff, l_int32 botdiff, l_int32 debug, l_int32 *psimilar, NUMA **pnasim ); +LEPT_DLL extern l_ok boxaJoin ( BOXA *boxad, BOXA *boxas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern l_ok boxaaJoin ( BOXAA *baad, BOXAA *baas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern l_ok boxaSplitEvenOdd ( BOXA *boxa, l_int32 fillflag, BOXA **pboxae, BOXA **pboxao ); +LEPT_DLL extern BOXA * boxaMergeEvenOdd ( BOXA *boxae, BOXA *boxao, l_int32 fillflag ); +LEPT_DLL extern BOXA * boxaTransform ( BOXA *boxas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern BOX * boxTransform ( BOX *box, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern BOXA * boxaTransformOrdered ( BOXA *boxas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 order ); +LEPT_DLL extern BOX * boxTransformOrdered ( BOX *boxs, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 order ); +LEPT_DLL extern BOXA * boxaRotateOrth ( BOXA *boxas, l_int32 w, l_int32 h, l_int32 rotation ); +LEPT_DLL extern BOX * boxRotateOrth ( BOX *box, l_int32 w, l_int32 h, l_int32 rotation ); +LEPT_DLL extern BOXA * boxaShiftWithPta ( BOXA *boxas, PTA *pta, l_int32 dir ); +LEPT_DLL extern BOXA * boxaSort ( BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); +LEPT_DLL extern BOXA * boxaBinSort ( BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); +LEPT_DLL extern BOXA * boxaSortByIndex ( BOXA *boxas, NUMA *naindex ); +LEPT_DLL extern BOXAA * boxaSort2d ( BOXA *boxas, NUMAA **pnaad, l_int32 delta1, l_int32 delta2, l_int32 minh1 ); +LEPT_DLL extern BOXAA * boxaSort2dByIndex ( BOXA *boxas, NUMAA *naa ); +LEPT_DLL extern l_ok boxaExtractAsNuma ( BOXA *boxa, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid ); +LEPT_DLL extern l_ok boxaExtractAsPta ( BOXA *boxa, PTA **pptal, PTA **pptat, PTA **pptar, PTA **pptab, PTA **pptaw, PTA **pptah, l_int32 keepinvalid ); +LEPT_DLL extern PTA * boxaExtractCorners ( BOXA *boxa, l_int32 corner ); +LEPT_DLL extern l_ok boxaGetRankVals ( BOXA *boxa, l_float32 fract, l_int32 *px, l_int32 *py, l_int32 *pr, l_int32 *pb, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok boxaGetMedianVals ( BOXA *boxa, l_int32 *px, l_int32 *py, l_int32 *pr, l_int32 *pb, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok boxaGetAverageSize ( BOXA *boxa, l_float32 *pw, l_float32 *ph ); +LEPT_DLL extern l_ok boxaaGetExtent ( BOXAA *baa, l_int32 *pw, l_int32 *ph, BOX **pbox, BOXA **pboxa ); +LEPT_DLL extern BOXA * boxaaFlattenToBoxa ( BOXAA *baa, NUMA **pnaindex, l_int32 copyflag ); +LEPT_DLL extern BOXA * boxaaFlattenAligned ( BOXAA *baa, l_int32 num, BOX *fillerbox, l_int32 copyflag ); +LEPT_DLL extern BOXAA * boxaEncapsulateAligned ( BOXA *boxa, l_int32 num, l_int32 copyflag ); +LEPT_DLL extern BOXAA * boxaaTranspose ( BOXAA *baas ); +LEPT_DLL extern l_ok boxaaAlignBox ( BOXAA *baa, BOX *box, l_int32 delta, l_int32 *pindex ); +LEPT_DLL extern PIX * pixMaskConnComp ( PIX *pixs, l_int32 connectivity, BOXA **pboxa ); +LEPT_DLL extern PIX * pixMaskBoxa ( PIX *pixd, PIX *pixs, BOXA *boxa, l_int32 op ); +LEPT_DLL extern PIX * pixPaintBoxa ( PIX *pixs, BOXA *boxa, l_uint32 val ); +LEPT_DLL extern PIX * pixSetBlackOrWhiteBoxa ( PIX *pixs, BOXA *boxa, l_int32 op ); +LEPT_DLL extern PIX * pixPaintBoxaRandom ( PIX *pixs, BOXA *boxa ); +LEPT_DLL extern PIX * pixBlendBoxaRandom ( PIX *pixs, BOXA *boxa, l_float32 fract ); +LEPT_DLL extern PIX * pixDrawBoxa ( PIX *pixs, BOXA *boxa, l_int32 width, l_uint32 val ); +LEPT_DLL extern PIX * pixDrawBoxaRandom ( PIX *pixs, BOXA *boxa, l_int32 width ); +LEPT_DLL extern PIX * boxaaDisplay ( PIX *pixs, BOXAA *baa, l_int32 linewba, l_int32 linewb, l_uint32 colorba, l_uint32 colorb, l_int32 w, l_int32 h ); +LEPT_DLL extern PIXA * pixaDisplayBoxaa ( PIXA *pixas, BOXAA *baa, l_int32 colorflag, l_int32 width ); +LEPT_DLL extern BOXA * pixSplitIntoBoxa ( PIX *pixs, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder ); +LEPT_DLL extern BOXA * pixSplitComponentIntoBoxa ( PIX *pix, BOX *box, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder ); +LEPT_DLL extern BOXA * makeMosaicStrips ( l_int32 w, l_int32 h, l_int32 direction, l_int32 size ); +LEPT_DLL extern l_ok boxaCompareRegions ( BOXA *boxa1, BOXA *boxa2, l_int32 areathresh, l_int32 *pnsame, l_float32 *pdiffarea, l_float32 *pdiffxor, PIX **ppixdb ); +LEPT_DLL extern BOX * pixSelectLargeULComp ( PIX *pixs, l_float32 areaslop, l_int32 yslop, l_int32 connectivity ); +LEPT_DLL extern BOX * boxaSelectLargeULBox ( BOXA *boxas, l_float32 areaslop, l_int32 yslop ); +LEPT_DLL extern BOXA * boxaSelectRange ( BOXA *boxas, l_int32 first, l_int32 last, l_int32 copyflag ); +LEPT_DLL extern BOXAA * boxaaSelectRange ( BOXAA *baas, l_int32 first, l_int32 last, l_int32 copyflag ); +LEPT_DLL extern BOXA * boxaSelectBySize ( BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged ); +LEPT_DLL extern NUMA * boxaMakeSizeIndicator ( BOXA *boxa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation ); +LEPT_DLL extern BOXA * boxaSelectByArea ( BOXA *boxas, l_int32 area, l_int32 relation, l_int32 *pchanged ); +LEPT_DLL extern NUMA * boxaMakeAreaIndicator ( BOXA *boxa, l_int32 area, l_int32 relation ); +LEPT_DLL extern BOXA * boxaSelectByWHRatio ( BOXA *boxas, l_float32 ratio, l_int32 relation, l_int32 *pchanged ); +LEPT_DLL extern NUMA * boxaMakeWHRatioIndicator ( BOXA *boxa, l_float32 ratio, l_int32 relation ); +LEPT_DLL extern BOXA * boxaSelectWithIndicator ( BOXA *boxas, NUMA *na, l_int32 *pchanged ); +LEPT_DLL extern BOXA * boxaPermutePseudorandom ( BOXA *boxas ); +LEPT_DLL extern BOXA * boxaPermuteRandom ( BOXA *boxad, BOXA *boxas ); +LEPT_DLL extern l_ok boxaSwapBoxes ( BOXA *boxa, l_int32 i, l_int32 j ); +LEPT_DLL extern PTA * boxaConvertToPta ( BOXA *boxa, l_int32 ncorners ); +LEPT_DLL extern BOXA * ptaConvertToBoxa ( PTA *pta, l_int32 ncorners ); +LEPT_DLL extern PTA * boxConvertToPta ( BOX *box, l_int32 ncorners ); +LEPT_DLL extern BOX * ptaConvertToBox ( PTA *pta ); +LEPT_DLL extern l_ok boxaGetExtent ( BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox ); +LEPT_DLL extern l_ok boxaGetCoverage ( BOXA *boxa, l_int32 wc, l_int32 hc, l_int32 exactflag, l_float32 *pfract ); +LEPT_DLL extern l_ok boxaaSizeRange ( BOXAA *baa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern l_ok boxaSizeRange ( BOXA *boxa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern l_ok boxaLocationRange ( BOXA *boxa, l_int32 *pminx, l_int32 *pminy, l_int32 *pmaxx, l_int32 *pmaxy ); +LEPT_DLL extern l_ok boxaGetSizes ( BOXA *boxa, NUMA **pnaw, NUMA **pnah ); +LEPT_DLL extern l_ok boxaGetArea ( BOXA *boxa, l_int32 *parea ); +LEPT_DLL extern PIX * boxaDisplayTiled ( BOXA *boxas, PIXA *pixa, l_int32 first, l_int32 last, l_int32 maxwidth, l_int32 linewidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border ); +LEPT_DLL extern BOXA * boxaSmoothSequenceLS ( BOXA *boxas, l_float32 factor, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels, l_int32 debug ); +LEPT_DLL extern BOXA * boxaSmoothSequenceMedian ( BOXA *boxas, l_int32 halfwin, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels, l_int32 debug ); +LEPT_DLL extern BOXA * boxaLinearFit ( BOXA *boxas, l_float32 factor, l_int32 debug ); +LEPT_DLL extern BOXA * boxaWindowedMedian ( BOXA *boxas, l_int32 halfwin, l_int32 debug ); +LEPT_DLL extern BOXA * boxaModifyWithBoxa ( BOXA *boxas, BOXA *boxam, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels ); +LEPT_DLL extern BOXA * boxaConstrainSize ( BOXA *boxas, l_int32 width, l_int32 widthflag, l_int32 height, l_int32 heightflag ); +LEPT_DLL extern BOXA * boxaReconcileEvenOddHeight ( BOXA *boxas, l_int32 sides, l_int32 delh, l_int32 op, l_float32 factor, l_int32 start ); +LEPT_DLL extern BOXA * boxaReconcilePairWidth ( BOXA *boxas, l_int32 delw, l_int32 op, l_float32 factor, NUMA *na ); +LEPT_DLL extern l_ok boxaSizeConsistency1 ( BOXA *boxas, l_int32 type, l_float32 threshp, l_float32 threshm, l_float32 *pfvarp, l_float32 *pfvarm, l_int32 *psame ); +LEPT_DLL extern l_ok boxaSizeConsistency2 ( BOXA *boxas, l_float32 *pfdevw, l_float32 *pfdevh, l_int32 debug ); +LEPT_DLL extern BOXA * boxaReconcileSizeByMedian ( BOXA *boxas, l_int32 type, l_float32 dfract, l_float32 sfract, l_float32 factor, NUMA **pnadelw, NUMA **pnadelh, l_float32 *pratiowh ); +LEPT_DLL extern l_ok boxaPlotSides ( BOXA *boxa, const char *plotname, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, PIX **ppixd ); +LEPT_DLL extern l_ok boxaPlotSizes ( BOXA *boxa, const char *plotname, NUMA **pnaw, NUMA **pnah, PIX **ppixd ); +LEPT_DLL extern BOXA * boxaFillSequence ( BOXA *boxas, l_int32 useflag, l_int32 debug ); +LEPT_DLL extern l_ok boxaSizeVariation ( BOXA *boxa, l_int32 type, l_float32 *pdel_evenodd, l_float32 *prms_even, l_float32 *prms_odd, l_float32 *prms_all ); +LEPT_DLL extern l_ok boxaMedianDimensions ( BOXA *boxas, l_int32 *pmedw, l_int32 *pmedh, l_int32 *pmedwe, l_int32 *pmedwo, l_int32 *pmedhe, l_int32 *pmedho, NUMA **pnadelw, NUMA **pnadelh ); +LEPT_DLL extern L_BYTEA * l_byteaCreate ( size_t nbytes ); +LEPT_DLL extern L_BYTEA * l_byteaInitFromMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern L_BYTEA * l_byteaInitFromFile ( const char *fname ); +LEPT_DLL extern L_BYTEA * l_byteaInitFromStream ( FILE *fp ); +LEPT_DLL extern L_BYTEA * l_byteaCopy ( L_BYTEA *bas, l_int32 copyflag ); +LEPT_DLL extern void l_byteaDestroy ( L_BYTEA **pba ); +LEPT_DLL extern size_t l_byteaGetSize ( L_BYTEA *ba ); +LEPT_DLL extern l_uint8 * l_byteaGetData ( L_BYTEA *ba, size_t *psize ); +LEPT_DLL extern l_uint8 * l_byteaCopyData ( L_BYTEA *ba, size_t *psize ); +LEPT_DLL extern l_ok l_byteaAppendData ( L_BYTEA *ba, const l_uint8 *newdata, size_t newbytes ); +LEPT_DLL extern l_ok l_byteaAppendString ( L_BYTEA *ba, const char *str ); +LEPT_DLL extern l_ok l_byteaJoin ( L_BYTEA *ba1, L_BYTEA **pba2 ); +LEPT_DLL extern l_ok l_byteaSplit ( L_BYTEA *ba1, size_t splitloc, L_BYTEA **pba2 ); +LEPT_DLL extern l_ok l_byteaFindEachSequence ( L_BYTEA *ba, const l_uint8 *sequence, size_t seqlen, L_DNA **pda ); +LEPT_DLL extern l_ok l_byteaWrite ( const char *fname, L_BYTEA *ba, size_t startloc, size_t nbytes ); +LEPT_DLL extern l_ok l_byteaWriteStream ( FILE *fp, L_BYTEA *ba, size_t startloc, size_t nbytes ); +LEPT_DLL extern CCBORDA * ccbaCreate ( PIX *pixs, l_int32 n ); +LEPT_DLL extern void ccbaDestroy ( CCBORDA **pccba ); +LEPT_DLL extern CCBORD * ccbCreate ( PIX *pixs ); +LEPT_DLL extern void ccbDestroy ( CCBORD **pccb ); +LEPT_DLL extern l_ok ccbaAddCcb ( CCBORDA *ccba, CCBORD *ccb ); +LEPT_DLL extern l_int32 ccbaGetCount ( CCBORDA *ccba ); +LEPT_DLL extern CCBORD * ccbaGetCcb ( CCBORDA *ccba, l_int32 index ); +LEPT_DLL extern CCBORDA * pixGetAllCCBorders ( PIX *pixs ); +LEPT_DLL extern PTAA * pixGetOuterBordersPtaa ( PIX *pixs ); +LEPT_DLL extern l_ok pixGetOuterBorder ( CCBORD *ccb, PIX *pixs, BOX *box ); +LEPT_DLL extern l_ok ccbaGenerateGlobalLocs ( CCBORDA *ccba ); +LEPT_DLL extern l_ok ccbaGenerateStepChains ( CCBORDA *ccba ); +LEPT_DLL extern l_ok ccbaStepChainsToPixCoords ( CCBORDA *ccba, l_int32 coordtype ); +LEPT_DLL extern l_ok ccbaGenerateSPGlobalLocs ( CCBORDA *ccba, l_int32 ptsflag ); +LEPT_DLL extern l_ok ccbaGenerateSinglePath ( CCBORDA *ccba ); +LEPT_DLL extern PTA * getCutPathForHole ( PIX *pix, PTA *pta, BOX *boxinner, l_int32 *pdir, l_int32 *plen ); +LEPT_DLL extern PIX * ccbaDisplayBorder ( CCBORDA *ccba ); +LEPT_DLL extern PIX * ccbaDisplaySPBorder ( CCBORDA *ccba ); +LEPT_DLL extern PIX * ccbaDisplayImage1 ( CCBORDA *ccba ); +LEPT_DLL extern PIX * ccbaDisplayImage2 ( CCBORDA *ccba ); +LEPT_DLL extern l_ok ccbaWrite ( const char *filename, CCBORDA *ccba ); +LEPT_DLL extern l_ok ccbaWriteStream ( FILE *fp, CCBORDA *ccba ); +LEPT_DLL extern CCBORDA * ccbaRead ( const char *filename ); +LEPT_DLL extern CCBORDA * ccbaReadStream ( FILE *fp ); +LEPT_DLL extern l_ok ccbaWriteSVG ( const char *filename, CCBORDA *ccba ); +LEPT_DLL extern char * ccbaWriteSVGString ( const char *filename, CCBORDA *ccba ); +LEPT_DLL extern PIXA * pixaThinConnected ( PIXA *pixas, l_int32 type, l_int32 connectivity, l_int32 maxiters ); +LEPT_DLL extern PIX * pixThinConnected ( PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters ); +LEPT_DLL extern PIX * pixThinConnectedBySet ( PIX *pixs, l_int32 type, SELA *sela, l_int32 maxiters ); +LEPT_DLL extern SELA * selaMakeThinSets ( l_int32 index, l_int32 debug ); +LEPT_DLL extern l_ok jbCorrelation ( const char *dirin, l_float32 thresh, l_float32 weight, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag ); +LEPT_DLL extern l_ok jbRankHaus ( const char *dirin, l_int32 size, l_float32 rank, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag ); +LEPT_DLL extern JBCLASSER * jbWordsInTextlines ( const char *dirin, l_int32 reduction, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weight, NUMA **pnatl, l_int32 firstpage, l_int32 npages ); +LEPT_DLL extern l_ok pixGetWordsInTextlines ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad, NUMA **pnai ); +LEPT_DLL extern l_ok pixGetWordBoxesInTextlines ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, NUMA **pnai ); +LEPT_DLL extern l_ok pixFindWordAndCharacterBoxes ( PIX *pixs, BOX *boxs, l_int32 thresh, BOXA **pboxaw, BOXAA **pboxaac, const char *debugdir ); +LEPT_DLL extern NUMAA * boxaExtractSortedPattern ( BOXA *boxa, NUMA *na ); +LEPT_DLL extern l_ok numaaCompareImagesByBoxes ( NUMAA *naa1, NUMAA *naa2, l_int32 nperline, l_int32 nreq, l_int32 maxshiftx, l_int32 maxshifty, l_int32 delx, l_int32 dely, l_int32 *psame, l_int32 debugflag ); +LEPT_DLL extern l_ok pixColorContent ( PIX *pixs, l_int32 rwhite, l_int32 gwhite, l_int32 bwhite, l_int32 mingray, PIX **ppixr, PIX **ppixg, PIX **ppixb ); +LEPT_DLL extern PIX * pixColorMagnitude ( PIX *pixs, l_int32 rwhite, l_int32 gwhite, l_int32 bwhite, l_int32 type ); +LEPT_DLL extern PIX * pixMaskOverColorPixels ( PIX *pixs, l_int32 threshdiff, l_int32 mindist ); +LEPT_DLL extern PIX * pixMaskOverGrayPixels ( PIX *pixs, l_int32 maxlimit, l_int32 satlimit ); +LEPT_DLL extern PIX * pixMaskOverColorRange ( PIX *pixs, l_int32 rmin, l_int32 rmax, l_int32 gmin, l_int32 gmax, l_int32 bmin, l_int32 bmax ); +LEPT_DLL extern l_ok pixColorFraction ( PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh, l_int32 factor, l_float32 *ppixfract, l_float32 *pcolorfract ); +LEPT_DLL extern l_ok pixFindColorRegions ( PIX *pixs, PIX *pixm, l_int32 factor, l_int32 lightthresh, l_int32 darkthresh, l_int32 mindiff, l_int32 colordiff, l_float32 edgefract, l_float32 *pcolorfract, PIX **pcolormask1, PIX **pcolormask2, PIXA *pixadb ); +LEPT_DLL extern l_ok pixNumSignificantGrayColors ( PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_float32 minfract, l_int32 factor, l_int32 *pncolors ); +LEPT_DLL extern l_ok pixColorsForQuantization ( PIX *pixs, l_int32 thresh, l_int32 *pncolors, l_int32 *piscolor, l_int32 debug ); +LEPT_DLL extern l_ok pixNumColors ( PIX *pixs, l_int32 factor, l_int32 *pncolors ); +LEPT_DLL extern l_ok pixGetMostPopulatedColors ( PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors, l_uint32 **parray, PIXCMAP **pcmap ); +LEPT_DLL extern PIX * pixSimpleColorQuantize ( PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors ); +LEPT_DLL extern NUMA * pixGetRGBHistogram ( PIX *pixs, l_int32 sigbits, l_int32 factor ); +LEPT_DLL extern l_ok makeRGBIndexTables ( l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab, l_int32 sigbits ); +LEPT_DLL extern l_ok getRGBFromIndex ( l_uint32 index, l_int32 sigbits, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixHasHighlightRed ( PIX *pixs, l_int32 factor, l_float32 fract, l_float32 fthresh, l_int32 *phasred, l_float32 *pratio, PIX **ppixdb ); +LEPT_DLL extern PIX * pixColorGrayRegions ( PIX *pixs, BOXA *boxa, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixColorGray ( PIX *pixs, BOX *box, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern PIX * pixColorGrayMasked ( PIX *pixs, PIX *pixm, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern PIX * pixSnapColor ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval, l_int32 diff ); +LEPT_DLL extern PIX * pixSnapColorCmap ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval, l_int32 diff ); +LEPT_DLL extern PIX * pixLinearMapToTargetColor ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval ); +LEPT_DLL extern l_ok pixelLinearMapToTargetColor ( l_uint32 scolor, l_uint32 srcmap, l_uint32 dstmap, l_uint32 *pdcolor ); +LEPT_DLL extern PIX * pixShiftByComponent ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval ); +LEPT_DLL extern l_ok pixelShiftByComponent ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 srcval, l_uint32 dstval, l_uint32 *ppixel ); +LEPT_DLL extern l_ok pixelFractionalShift ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fraction, l_uint32 *ppixel ); +LEPT_DLL extern PIXCMAP * pixcmapCreate ( l_int32 depth ); +LEPT_DLL extern PIXCMAP * pixcmapCreateRandom ( l_int32 depth, l_int32 hasblack, l_int32 haswhite ); +LEPT_DLL extern PIXCMAP * pixcmapCreateLinear ( l_int32 d, l_int32 nlevels ); +LEPT_DLL extern PIXCMAP * pixcmapCopy ( const PIXCMAP *cmaps ); +LEPT_DLL extern void pixcmapDestroy ( PIXCMAP **pcmap ); +LEPT_DLL extern l_ok pixcmapIsValid ( const PIXCMAP *cmap, l_int32 *pvalid ); +LEPT_DLL extern l_ok pixcmapAddColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixcmapAddRGBA ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval ); +LEPT_DLL extern l_ok pixcmapAddNewColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapAddNearestColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapUsableColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pusable ); +LEPT_DLL extern l_ok pixcmapAddBlackOrWhite ( PIXCMAP *cmap, l_int32 color, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapSetBlackAndWhite ( PIXCMAP *cmap, l_int32 setblack, l_int32 setwhite ); +LEPT_DLL extern l_int32 pixcmapGetCount ( const PIXCMAP *cmap ); +LEPT_DLL extern l_int32 pixcmapGetFreeCount ( PIXCMAP *cmap ); +LEPT_DLL extern l_int32 pixcmapGetDepth ( PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapGetMinDepth ( PIXCMAP *cmap, l_int32 *pmindepth ); +LEPT_DLL extern l_ok pixcmapClear ( PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapGetColor ( PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixcmapGetColor32 ( PIXCMAP *cmap, l_int32 index, l_uint32 *pval32 ); +LEPT_DLL extern l_ok pixcmapGetRGBA ( PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval ); +LEPT_DLL extern l_ok pixcmapGetRGBA32 ( PIXCMAP *cmap, l_int32 index, l_uint32 *pval32 ); +LEPT_DLL extern l_ok pixcmapResetColor ( PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixcmapSetAlpha ( PIXCMAP *cmap, l_int32 index, l_int32 aval ); +LEPT_DLL extern l_int32 pixcmapGetIndex ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapHasColor ( PIXCMAP *cmap, l_int32 *pcolor ); +LEPT_DLL extern l_ok pixcmapIsOpaque ( PIXCMAP *cmap, l_int32 *popaque ); +LEPT_DLL extern l_ok pixcmapIsBlackAndWhite ( PIXCMAP *cmap, l_int32 *pblackwhite ); +LEPT_DLL extern l_ok pixcmapCountGrayColors ( PIXCMAP *cmap, l_int32 *pngray ); +LEPT_DLL extern l_ok pixcmapGetRankIntensity ( PIXCMAP *cmap, l_float32 rankval, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapGetNearestIndex ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapGetNearestGrayIndex ( PIXCMAP *cmap, l_int32 val, l_int32 *pindex ); +LEPT_DLL extern l_ok pixcmapGetDistanceToColor ( PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pdist ); +LEPT_DLL extern l_ok pixcmapGetRangeValues ( PIXCMAP *cmap, l_int32 select, l_int32 *pminval, l_int32 *pmaxval, l_int32 *pminindex, l_int32 *pmaxindex ); +LEPT_DLL extern PIXCMAP * pixcmapGrayToColor ( l_uint32 color ); +LEPT_DLL extern PIXCMAP * pixcmapColorToGray ( PIXCMAP *cmaps, l_float32 rwt, l_float32 gwt, l_float32 bwt ); +LEPT_DLL extern PIXCMAP * pixcmapConvertTo4 ( PIXCMAP *cmaps ); +LEPT_DLL extern PIXCMAP * pixcmapConvertTo8 ( PIXCMAP *cmaps ); +LEPT_DLL extern PIXCMAP * pixcmapRead ( const char *filename ); +LEPT_DLL extern PIXCMAP * pixcmapReadStream ( FILE *fp ); +LEPT_DLL extern PIXCMAP * pixcmapReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixcmapWrite ( const char *filename, const PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapWriteStream ( FILE *fp, const PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapWriteMem ( l_uint8 **pdata, size_t *psize, const PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapToArrays ( const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap ); +LEPT_DLL extern l_ok pixcmapToRGBTable ( PIXCMAP *cmap, l_uint32 **ptab, l_int32 *pncolors ); +LEPT_DLL extern l_ok pixcmapSerializeToMemory ( PIXCMAP *cmap, l_int32 cpc, l_int32 *pncolors, l_uint8 **pdata ); +LEPT_DLL extern PIXCMAP * pixcmapDeserializeFromMemory ( l_uint8 *data, l_int32 cpc, l_int32 ncolors ); +LEPT_DLL extern char * pixcmapConvertToHex ( l_uint8 *data, l_int32 ncolors ); +LEPT_DLL extern l_ok pixcmapGammaTRC ( PIXCMAP *cmap, l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern l_ok pixcmapContrastTRC ( PIXCMAP *cmap, l_float32 factor ); +LEPT_DLL extern l_ok pixcmapShiftIntensity ( PIXCMAP *cmap, l_float32 fraction ); +LEPT_DLL extern l_ok pixcmapShiftByComponent ( PIXCMAP *cmap, l_uint32 srcval, l_uint32 dstval ); +LEPT_DLL extern PIX * pixColorMorph ( PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOctreeColorQuant ( PIX *pixs, l_int32 colors, l_int32 ditherflag ); +LEPT_DLL extern PIX * pixOctreeColorQuantGeneral ( PIX *pixs, l_int32 colors, l_int32 ditherflag, l_float32 validthresh, l_float32 colorthresh ); +LEPT_DLL extern l_ok makeRGBToIndexTables ( l_int32 cqlevels, l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab ); +LEPT_DLL extern void getOctcubeIndexFromRGB ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *rtab, l_uint32 *gtab, l_uint32 *btab, l_uint32 *pindex ); +LEPT_DLL extern PIX * pixOctreeQuantByPopulation ( PIX *pixs, l_int32 level, l_int32 ditherflag ); +LEPT_DLL extern PIX * pixOctreeQuantNumColors ( PIX *pixs, l_int32 maxcolors, l_int32 subsample ); +LEPT_DLL extern PIX * pixOctcubeQuantMixedWithGray ( PIX *pixs, l_int32 depth, l_int32 graylevels, l_int32 delta ); +LEPT_DLL extern PIX * pixFixedOctcubeQuant256 ( PIX *pixs, l_int32 ditherflag ); +LEPT_DLL extern PIX * pixFewColorsOctcubeQuant1 ( PIX *pixs, l_int32 level ); +LEPT_DLL extern PIX * pixFewColorsOctcubeQuant2 ( PIX *pixs, l_int32 level, NUMA *na, l_int32 ncolors, l_int32 *pnerrors ); +LEPT_DLL extern PIX * pixFewColorsOctcubeQuantMixed ( PIX *pixs, l_int32 level, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh, l_float32 minfract, l_int32 maxspan ); +LEPT_DLL extern PIX * pixFixedOctcubeQuantGenRGB ( PIX *pixs, l_int32 level ); +LEPT_DLL extern PIX * pixQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth, l_int32 level, l_int32 metric ); +LEPT_DLL extern PIX * pixOctcubeQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth, l_int32 level, l_int32 metric ); +LEPT_DLL extern NUMA * pixOctcubeHistogram ( PIX *pixs, l_int32 level, l_int32 *pncolors ); +LEPT_DLL extern l_int32 * pixcmapToOctcubeLUT ( PIXCMAP *cmap, l_int32 level, l_int32 metric ); +LEPT_DLL extern l_ok pixRemoveUnusedColors ( PIX *pixs ); +LEPT_DLL extern l_ok pixNumberOccupiedOctcubes ( PIX *pix, l_int32 level, l_int32 mincount, l_float32 minfract, l_int32 *pncolors ); +LEPT_DLL extern PIX * pixMedianCutQuant ( PIX *pixs, l_int32 ditherflag ); +LEPT_DLL extern PIX * pixMedianCutQuantGeneral ( PIX *pixs, l_int32 ditherflag, l_int32 outdepth, l_int32 maxcolors, l_int32 sigbits, l_int32 maxsub, l_int32 checkbw ); +LEPT_DLL extern PIX * pixMedianCutQuantMixed ( PIX *pixs, l_int32 ncolor, l_int32 ngray, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh ); +LEPT_DLL extern PIX * pixFewColorsMedianCutQuantMixed ( PIX *pixs, l_int32 ncolor, l_int32 ngray, l_int32 maxncolors, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh ); +LEPT_DLL extern l_int32 * pixMedianCutHisto ( PIX *pixs, l_int32 sigbits, l_int32 subsample ); +LEPT_DLL extern PIX * pixColorSegment ( PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 selsize, l_int32 finalcolors, l_int32 debugflag ); +LEPT_DLL extern PIX * pixColorSegmentCluster ( PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 debugflag ); +LEPT_DLL extern l_ok pixAssignToNearestColor ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 level, l_int32 *countarray ); +LEPT_DLL extern l_ok pixColorSegmentClean ( PIX *pixs, l_int32 selsize, l_int32 *countarray ); +LEPT_DLL extern l_ok pixColorSegmentRemoveColors ( PIX *pixd, PIX *pixs, l_int32 finalcolors ); +LEPT_DLL extern PIX * pixConvertRGBToHSV ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixConvertHSVToRGB ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern l_ok convertRGBToHSV ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *phval, l_int32 *psval, l_int32 *pvval ); +LEPT_DLL extern l_ok convertHSVToRGB ( l_int32 hval, l_int32 sval, l_int32 vval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixcmapConvertRGBToHSV ( PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapConvertHSVToRGB ( PIXCMAP *cmap ); +LEPT_DLL extern PIX * pixConvertRGBToHue ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertRGBToSaturation ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertRGBToValue ( PIX *pixs ); +LEPT_DLL extern PIX * pixMakeRangeMaskHS ( PIX *pixs, l_int32 huecenter, l_int32 huehw, l_int32 satcenter, l_int32 sathw, l_int32 regionflag ); +LEPT_DLL extern PIX * pixMakeRangeMaskHV ( PIX *pixs, l_int32 huecenter, l_int32 huehw, l_int32 valcenter, l_int32 valhw, l_int32 regionflag ); +LEPT_DLL extern PIX * pixMakeRangeMaskSV ( PIX *pixs, l_int32 satcenter, l_int32 sathw, l_int32 valcenter, l_int32 valhw, l_int32 regionflag ); +LEPT_DLL extern PIX * pixMakeHistoHS ( PIX *pixs, l_int32 factor, NUMA **pnahue, NUMA **pnasat ); +LEPT_DLL extern PIX * pixMakeHistoHV ( PIX *pixs, l_int32 factor, NUMA **pnahue, NUMA **pnaval ); +LEPT_DLL extern PIX * pixMakeHistoSV ( PIX *pixs, l_int32 factor, NUMA **pnasat, NUMA **pnaval ); +LEPT_DLL extern l_ok pixFindHistoPeaksHSV ( PIX *pixs, l_int32 type, l_int32 width, l_int32 height, l_int32 npeaks, l_float32 erasefactor, PTA **ppta, NUMA **pnatot, PIXA **ppixa ); +LEPT_DLL extern PIX * displayHSVColorRange ( l_int32 hval, l_int32 sval, l_int32 vval, l_int32 huehw, l_int32 sathw, l_int32 nsamp, l_int32 factor ); +LEPT_DLL extern PIX * pixConvertRGBToYUV ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixConvertYUVToRGB ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern l_ok convertRGBToYUV ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pyval, l_int32 *puval, l_int32 *pvval ); +LEPT_DLL extern l_ok convertYUVToRGB ( l_int32 yval, l_int32 uval, l_int32 vval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixcmapConvertRGBToYUV ( PIXCMAP *cmap ); +LEPT_DLL extern l_ok pixcmapConvertYUVToRGB ( PIXCMAP *cmap ); +LEPT_DLL extern FPIXA * pixConvertRGBToXYZ ( PIX *pixs ); +LEPT_DLL extern PIX * fpixaConvertXYZToRGB ( FPIXA *fpixa ); +LEPT_DLL extern l_ok convertRGBToXYZ ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pfxval, l_float32 *pfyval, l_float32 *pfzval ); +LEPT_DLL extern l_ok convertXYZToRGB ( l_float32 fxval, l_float32 fyval, l_float32 fzval, l_int32 blackout, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern FPIXA * fpixaConvertXYZToLAB ( FPIXA *fpixas ); +LEPT_DLL extern FPIXA * fpixaConvertLABToXYZ ( FPIXA *fpixas ); +LEPT_DLL extern l_ok convertXYZToLAB ( l_float32 xval, l_float32 yval, l_float32 zval, l_float32 *plval, l_float32 *paval, l_float32 *pbval ); +LEPT_DLL extern l_ok convertLABToXYZ ( l_float32 lval, l_float32 aval, l_float32 bval, l_float32 *pxval, l_float32 *pyval, l_float32 *pzval ); +LEPT_DLL extern FPIXA * pixConvertRGBToLAB ( PIX *pixs ); +LEPT_DLL extern PIX * fpixaConvertLABToRGB ( FPIXA *fpixa ); +LEPT_DLL extern l_ok convertRGBToLAB ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pflval, l_float32 *pfaval, l_float32 *pfbval ); +LEPT_DLL extern l_ok convertLABToRGB ( l_float32 flval, l_float32 faval, l_float32 fbval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixEqual ( PIX *pix1, PIX *pix2, l_int32 *psame ); +LEPT_DLL extern l_ok pixEqualWithAlpha ( PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame ); +LEPT_DLL extern l_ok pixEqualWithCmap ( PIX *pix1, PIX *pix2, l_int32 *psame ); +LEPT_DLL extern l_ok cmapEqual ( PIXCMAP *cmap1, PIXCMAP *cmap2, l_int32 ncomps, l_int32 *psame ); +LEPT_DLL extern l_ok pixUsesCmapColor ( PIX *pixs, l_int32 *pcolor ); +LEPT_DLL extern l_ok pixCorrelationBinary ( PIX *pix1, PIX *pix2, l_float32 *pval ); +LEPT_DLL extern PIX * pixDisplayDiffBinary ( PIX *pix1, PIX *pix2 ); +LEPT_DLL extern l_ok pixCompareBinary ( PIX *pix1, PIX *pix2, l_int32 comptype, l_float32 *pfract, PIX **ppixdiff ); +LEPT_DLL extern l_ok pixCompareGrayOrRGB ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); +LEPT_DLL extern l_ok pixCompareGray ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); +LEPT_DLL extern l_ok pixCompareRGB ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); +LEPT_DLL extern l_ok pixCompareTiled ( PIX *pix1, PIX *pix2, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixdiff ); +LEPT_DLL extern NUMA * pixCompareRankDifference ( PIX *pix1, PIX *pix2, l_int32 factor ); +LEPT_DLL extern l_ok pixTestForSimilarity ( PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 maxfract, l_float32 maxave, l_int32 *psimilar, l_int32 details ); +LEPT_DLL extern l_ok pixGetDifferenceStats ( PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 *pfractdiff, l_float32 *pavediff, l_int32 details ); +LEPT_DLL extern NUMA * pixGetDifferenceHistogram ( PIX *pix1, PIX *pix2, l_int32 factor ); +LEPT_DLL extern l_ok pixGetPerceptualDiff ( PIX *pixs1, PIX *pixs2, l_int32 sampling, l_int32 dilation, l_int32 mindiff, l_float32 *pfract, PIX **ppixdiff1, PIX **ppixdiff2 ); +LEPT_DLL extern l_ok pixGetPSNR ( PIX *pix1, PIX *pix2, l_int32 factor, l_float32 *ppsnr ); +LEPT_DLL extern l_ok pixaComparePhotoRegionsByHisto ( PIXA *pixa, l_float32 minratio, l_float32 textthresh, l_int32 factor, l_int32 n, l_float32 simthresh, NUMA **pnai, l_float32 **pscores, PIX **ppixd, l_int32 debug ); +LEPT_DLL extern l_ok pixComparePhotoRegionsByHisto ( PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag ); +LEPT_DLL extern l_ok pixGenPhotoHistos ( PIX *pixs, BOX *box, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, l_int32 *pw, l_int32 *ph, l_int32 debugindex ); +LEPT_DLL extern PIX * pixPadToCenterCentroid ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern l_ok pixCentroid8 ( PIX *pixs, l_int32 factor, l_float32 *pcx, l_float32 *pcy ); +LEPT_DLL extern l_ok pixDecideIfPhotoImage ( PIX *pix, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, PIXA *pixadebug ); +LEPT_DLL extern l_ok compareTilesByHisto ( NUMAA *naa1, NUMAA *naa2, l_float32 minratio, l_int32 w1, l_int32 h1, l_int32 w2, l_int32 h2, l_float32 *pscore, PIXA *pixadebug ); +LEPT_DLL extern l_ok pixCompareGrayByHisto ( PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 maxgray, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag ); +LEPT_DLL extern l_ok pixCropAlignedToCentroid ( PIX *pix1, PIX *pix2, l_int32 factor, BOX **pbox1, BOX **pbox2 ); +LEPT_DLL extern l_uint8 * l_compressGrayHistograms ( NUMAA *naa, l_int32 w, l_int32 h, size_t *psize ); +LEPT_DLL extern NUMAA * l_uncompressGrayHistograms ( l_uint8 *bytea, size_t size, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok pixCompareWithTranslation ( PIX *pix1, PIX *pix2, l_int32 thresh, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag ); +LEPT_DLL extern l_ok pixBestCorrelation ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_int32 etransx, l_int32 etransy, l_int32 maxshift, l_int32 *tab8, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag ); +LEPT_DLL extern BOXA * pixConnComp ( PIX *pixs, PIXA **ppixa, l_int32 connectivity ); +LEPT_DLL extern BOXA * pixConnCompPixa ( PIX *pixs, PIXA **ppixa, l_int32 connectivity ); +LEPT_DLL extern BOXA * pixConnCompBB ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern l_ok pixCountConnComp ( PIX *pixs, l_int32 connectivity, l_int32 *pcount ); +LEPT_DLL extern l_int32 nextOnPixelInRaster ( PIX *pixs, l_int32 xstart, l_int32 ystart, l_int32 *px, l_int32 *py ); +LEPT_DLL extern BOX * pixSeedfillBB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y, l_int32 connectivity ); +LEPT_DLL extern BOX * pixSeedfill4BB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); +LEPT_DLL extern BOX * pixSeedfill8BB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixSeedfill ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y, l_int32 connectivity ); +LEPT_DLL extern l_ok pixSeedfill4 ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixSeedfill8 ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok convertFilesTo1bpp ( const char *dirin, const char *substr, l_int32 upscaling, l_int32 thresh, l_int32 firstpage, l_int32 npages, const char *dirout, l_int32 outformat ); +LEPT_DLL extern PIX * pixBlockconv ( PIX *pix, l_int32 wc, l_int32 hc ); +LEPT_DLL extern PIX * pixBlockconvGray ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); +LEPT_DLL extern PIX * pixBlockconvAccum ( PIX *pixs ); +LEPT_DLL extern PIX * pixBlockconvGrayUnnormalized ( PIX *pixs, l_int32 wc, l_int32 hc ); +LEPT_DLL extern PIX * pixBlockconvTiled ( PIX *pix, l_int32 wc, l_int32 hc, l_int32 nx, l_int32 ny ); +LEPT_DLL extern PIX * pixBlockconvGrayTile ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); +LEPT_DLL extern l_ok pixWindowedStats ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, PIX **ppixm, PIX **ppixms, FPIX **pfpixv, FPIX **pfpixrv ); +LEPT_DLL extern PIX * pixWindowedMean ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag ); +LEPT_DLL extern PIX * pixWindowedMeanSquare ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder ); +LEPT_DLL extern l_ok pixWindowedVariance ( PIX *pixm, PIX *pixms, FPIX **pfpixv, FPIX **pfpixrv ); +LEPT_DLL extern DPIX * pixMeanSquareAccum ( PIX *pixs ); +LEPT_DLL extern PIX * pixBlockrank ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc, l_float32 rank ); +LEPT_DLL extern PIX * pixBlocksum ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); +LEPT_DLL extern PIX * pixCensusTransform ( PIX *pixs, l_int32 halfsize, PIX *pixacc ); +LEPT_DLL extern PIX * pixConvolve ( PIX *pixs, L_KERNEL *kel, l_int32 outdepth, l_int32 normflag ); +LEPT_DLL extern PIX * pixConvolveSep ( PIX *pixs, L_KERNEL *kelx, L_KERNEL *kely, l_int32 outdepth, l_int32 normflag ); +LEPT_DLL extern PIX * pixConvolveRGB ( PIX *pixs, L_KERNEL *kel ); +LEPT_DLL extern PIX * pixConvolveRGBSep ( PIX *pixs, L_KERNEL *kelx, L_KERNEL *kely ); +LEPT_DLL extern FPIX * fpixConvolve ( FPIX *fpixs, L_KERNEL *kel, l_int32 normflag ); +LEPT_DLL extern FPIX * fpixConvolveSep ( FPIX *fpixs, L_KERNEL *kelx, L_KERNEL *kely, l_int32 normflag ); +LEPT_DLL extern PIX * pixConvolveWithBias ( PIX *pixs, L_KERNEL *kel1, L_KERNEL *kel2, l_int32 force8, l_int32 *pbias ); +LEPT_DLL extern void l_setConvolveSampling ( l_int32 xfact, l_int32 yfact ); +LEPT_DLL extern PIX * pixAddGaussianNoise ( PIX *pixs, l_float32 stdev ); +LEPT_DLL extern l_float32 gaussDistribSampling ( ); +LEPT_DLL extern l_ok pixCorrelationScore ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_float32 *pscore ); +LEPT_DLL extern l_int32 pixCorrelationScoreThresholded ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_int32 *downcount, l_float32 score_threshold ); +LEPT_DLL extern l_ok pixCorrelationScoreSimple ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_float32 *pscore ); +LEPT_DLL extern l_ok pixCorrelationScoreShifted ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_int32 delx, l_int32 dely, l_int32 *tab, l_float32 *pscore ); +LEPT_DLL extern L_DEWARP * dewarpCreate ( PIX *pixs, l_int32 pageno ); +LEPT_DLL extern L_DEWARP * dewarpCreateRef ( l_int32 pageno, l_int32 refpage ); +LEPT_DLL extern void dewarpDestroy ( L_DEWARP **pdew ); +LEPT_DLL extern L_DEWARPA * dewarpaCreate ( l_int32 nptrs, l_int32 sampling, l_int32 redfactor, l_int32 minlines, l_int32 maxdist ); +LEPT_DLL extern L_DEWARPA * dewarpaCreateFromPixacomp ( PIXAC *pixac, l_int32 useboth, l_int32 sampling, l_int32 minlines, l_int32 maxdist ); +LEPT_DLL extern void dewarpaDestroy ( L_DEWARPA **pdewa ); +LEPT_DLL extern l_ok dewarpaDestroyDewarp ( L_DEWARPA *dewa, l_int32 pageno ); +LEPT_DLL extern l_ok dewarpaInsertDewarp ( L_DEWARPA *dewa, L_DEWARP *dew ); +LEPT_DLL extern L_DEWARP * dewarpaGetDewarp ( L_DEWARPA *dewa, l_int32 index ); +LEPT_DLL extern l_ok dewarpaSetCurvatures ( L_DEWARPA *dewa, l_int32 max_linecurv, l_int32 min_diff_linecurv, l_int32 max_diff_linecurv, l_int32 max_edgecurv, l_int32 max_diff_edgecurv, l_int32 max_edgeslope ); +LEPT_DLL extern l_ok dewarpaUseBothArrays ( L_DEWARPA *dewa, l_int32 useboth ); +LEPT_DLL extern l_ok dewarpaSetCheckColumns ( L_DEWARPA *dewa, l_int32 check_columns ); +LEPT_DLL extern l_ok dewarpaSetMaxDistance ( L_DEWARPA *dewa, l_int32 maxdist ); +LEPT_DLL extern L_DEWARP * dewarpRead ( const char *filename ); +LEPT_DLL extern L_DEWARP * dewarpReadStream ( FILE *fp ); +LEPT_DLL extern L_DEWARP * dewarpReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok dewarpWrite ( const char *filename, L_DEWARP *dew ); +LEPT_DLL extern l_ok dewarpWriteStream ( FILE *fp, L_DEWARP *dew ); +LEPT_DLL extern l_ok dewarpWriteMem ( l_uint8 **pdata, size_t *psize, L_DEWARP *dew ); +LEPT_DLL extern L_DEWARPA * dewarpaRead ( const char *filename ); +LEPT_DLL extern L_DEWARPA * dewarpaReadStream ( FILE *fp ); +LEPT_DLL extern L_DEWARPA * dewarpaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok dewarpaWrite ( const char *filename, L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaWriteStream ( FILE *fp, L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaWriteMem ( l_uint8 **pdata, size_t *psize, L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpBuildPageModel ( L_DEWARP *dew, const char *debugfile ); +LEPT_DLL extern l_ok dewarpFindVertDisparity ( L_DEWARP *dew, PTAA *ptaa, l_int32 rotflag ); +LEPT_DLL extern l_ok dewarpFindHorizDisparity ( L_DEWARP *dew, PTAA *ptaa ); +LEPT_DLL extern PTAA * dewarpGetTextlineCenters ( PIX *pixs, l_int32 debugflag ); +LEPT_DLL extern PTAA * dewarpRemoveShortLines ( PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag ); +LEPT_DLL extern l_ok dewarpFindHorizSlopeDisparity ( L_DEWARP *dew, PIX *pixb, l_float32 fractthresh, l_int32 parity ); +LEPT_DLL extern l_ok dewarpBuildLineModel ( L_DEWARP *dew, l_int32 opensize, const char *debugfile ); +LEPT_DLL extern l_ok dewarpaModelStatus ( L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess ); +LEPT_DLL extern l_ok dewarpaApplyDisparity ( L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, l_int32 grayin, l_int32 x, l_int32 y, PIX **ppixd, const char *debugfile ); +LEPT_DLL extern l_ok dewarpaApplyDisparityBoxa ( L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, BOXA *boxas, l_int32 mapdir, l_int32 x, l_int32 y, BOXA **pboxad, const char *debugfile ); +LEPT_DLL extern l_ok dewarpMinimize ( L_DEWARP *dew ); +LEPT_DLL extern l_ok dewarpPopulateFullRes ( L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok dewarpSinglePage ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 useboth, l_int32 check_columns, PIX **ppixd, L_DEWARPA **pdewa, l_int32 debug ); +LEPT_DLL extern l_ok dewarpSinglePageInit ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 useboth, l_int32 check_columns, PIX **ppixb, L_DEWARPA **pdewa ); +LEPT_DLL extern l_ok dewarpSinglePageRun ( PIX *pixs, PIX *pixb, L_DEWARPA *dewa, PIX **ppixd, l_int32 debug ); +LEPT_DLL extern l_ok dewarpaListPages ( L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaSetValidModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); +LEPT_DLL extern l_ok dewarpaInsertRefModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); +LEPT_DLL extern l_ok dewarpaStripRefModels ( L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaRestoreModels ( L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaInfo ( FILE *fp, L_DEWARPA *dewa ); +LEPT_DLL extern l_ok dewarpaModelStats ( L_DEWARPA *dewa, l_int32 *pnnone, l_int32 *pnvsuccess, l_int32 *pnvvalid, l_int32 *pnhsuccess, l_int32 *pnhvalid, l_int32 *pnref ); +LEPT_DLL extern l_ok dewarpaShowArrays ( L_DEWARPA *dewa, l_float32 scalefact, l_int32 first, l_int32 last ); +LEPT_DLL extern l_ok dewarpDebug ( L_DEWARP *dew, const char *subdirs, l_int32 index ); +LEPT_DLL extern l_ok dewarpShowResults ( L_DEWARPA *dewa, SARRAY *sa, BOXA *boxa, l_int32 firstpage, l_int32 lastpage, const char *pdfout ); +LEPT_DLL extern L_DNA * l_dnaCreate ( l_int32 n ); +LEPT_DLL extern L_DNA * l_dnaCreateFromIArray ( l_int32 *iarray, l_int32 size ); +LEPT_DLL extern L_DNA * l_dnaCreateFromDArray ( l_float64 *darray, l_int32 size, l_int32 copyflag ); +LEPT_DLL extern L_DNA * l_dnaMakeSequence ( l_float64 startval, l_float64 increment, l_int32 size ); +LEPT_DLL extern void l_dnaDestroy ( L_DNA **pda ); +LEPT_DLL extern L_DNA * l_dnaCopy ( L_DNA *da ); +LEPT_DLL extern L_DNA * l_dnaClone ( L_DNA *da ); +LEPT_DLL extern l_ok l_dnaEmpty ( L_DNA *da ); +LEPT_DLL extern l_ok l_dnaAddNumber ( L_DNA *da, l_float64 val ); +LEPT_DLL extern l_ok l_dnaInsertNumber ( L_DNA *da, l_int32 index, l_float64 val ); +LEPT_DLL extern l_ok l_dnaRemoveNumber ( L_DNA *da, l_int32 index ); +LEPT_DLL extern l_ok l_dnaReplaceNumber ( L_DNA *da, l_int32 index, l_float64 val ); +LEPT_DLL extern l_int32 l_dnaGetCount ( L_DNA *da ); +LEPT_DLL extern l_ok l_dnaSetCount ( L_DNA *da, l_int32 newcount ); +LEPT_DLL extern l_ok l_dnaGetDValue ( L_DNA *da, l_int32 index, l_float64 *pval ); +LEPT_DLL extern l_ok l_dnaGetIValue ( L_DNA *da, l_int32 index, l_int32 *pival ); +LEPT_DLL extern l_ok l_dnaSetValue ( L_DNA *da, l_int32 index, l_float64 val ); +LEPT_DLL extern l_ok l_dnaShiftValue ( L_DNA *da, l_int32 index, l_float64 diff ); +LEPT_DLL extern l_int32 * l_dnaGetIArray ( L_DNA *da ); +LEPT_DLL extern l_float64 * l_dnaGetDArray ( L_DNA *da, l_int32 copyflag ); +LEPT_DLL extern l_int32 l_dnaGetRefcount ( L_DNA *da ); +LEPT_DLL extern l_ok l_dnaChangeRefcount ( L_DNA *da, l_int32 delta ); +LEPT_DLL extern l_ok l_dnaGetParameters ( L_DNA *da, l_float64 *pstartx, l_float64 *pdelx ); +LEPT_DLL extern l_ok l_dnaSetParameters ( L_DNA *da, l_float64 startx, l_float64 delx ); +LEPT_DLL extern l_ok l_dnaCopyParameters ( L_DNA *dad, L_DNA *das ); +LEPT_DLL extern L_DNA * l_dnaRead ( const char *filename ); +LEPT_DLL extern L_DNA * l_dnaReadStream ( FILE *fp ); +LEPT_DLL extern l_ok l_dnaWrite ( const char *filename, L_DNA *da ); +LEPT_DLL extern l_ok l_dnaWriteStream ( FILE *fp, L_DNA *da ); +LEPT_DLL extern L_DNAA * l_dnaaCreate ( l_int32 n ); +LEPT_DLL extern L_DNAA * l_dnaaCreateFull ( l_int32 nptr, l_int32 n ); +LEPT_DLL extern l_ok l_dnaaTruncate ( L_DNAA *daa ); +LEPT_DLL extern void l_dnaaDestroy ( L_DNAA **pdaa ); +LEPT_DLL extern l_ok l_dnaaAddDna ( L_DNAA *daa, L_DNA *da, l_int32 copyflag ); +LEPT_DLL extern l_int32 l_dnaaGetCount ( L_DNAA *daa ); +LEPT_DLL extern l_int32 l_dnaaGetDnaCount ( L_DNAA *daa, l_int32 index ); +LEPT_DLL extern l_int32 l_dnaaGetNumberCount ( L_DNAA *daa ); +LEPT_DLL extern L_DNA * l_dnaaGetDna ( L_DNAA *daa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern l_ok l_dnaaReplaceDna ( L_DNAA *daa, l_int32 index, L_DNA *da ); +LEPT_DLL extern l_ok l_dnaaGetValue ( L_DNAA *daa, l_int32 i, l_int32 j, l_float64 *pval ); +LEPT_DLL extern l_ok l_dnaaAddNumber ( L_DNAA *daa, l_int32 index, l_float64 val ); +LEPT_DLL extern L_DNAA * l_dnaaRead ( const char *filename ); +LEPT_DLL extern L_DNAA * l_dnaaReadStream ( FILE *fp ); +LEPT_DLL extern l_ok l_dnaaWrite ( const char *filename, L_DNAA *daa ); +LEPT_DLL extern l_ok l_dnaaWriteStream ( FILE *fp, L_DNAA *daa ); +LEPT_DLL extern l_ok l_dnaJoin ( L_DNA *dad, L_DNA *das, l_int32 istart, l_int32 iend ); +LEPT_DLL extern L_DNA * l_dnaaFlattenToDna ( L_DNAA *daa ); +LEPT_DLL extern NUMA * l_dnaConvertToNuma ( L_DNA *da ); +LEPT_DLL extern L_DNA * numaConvertToDna ( NUMA *na ); +LEPT_DLL extern L_DNA * l_dnaUnionByAset ( L_DNA *da1, L_DNA *da2 ); +LEPT_DLL extern L_DNA * l_dnaRemoveDupsByAset ( L_DNA *das ); +LEPT_DLL extern L_DNA * l_dnaIntersectionByAset ( L_DNA *da1, L_DNA *da2 ); +LEPT_DLL extern L_ASET * l_asetCreateFromDna ( L_DNA *da ); +LEPT_DLL extern L_DNA * l_dnaDiffAdjValues ( L_DNA *das ); +LEPT_DLL extern L_DNAHASH * l_dnaHashCreate ( l_int32 nbuckets, l_int32 initsize ); +LEPT_DLL extern void l_dnaHashDestroy ( L_DNAHASH **pdahash ); +LEPT_DLL extern l_int32 l_dnaHashGetCount ( L_DNAHASH *dahash ); +LEPT_DLL extern l_int32 l_dnaHashGetTotalCount ( L_DNAHASH *dahash ); +LEPT_DLL extern L_DNA * l_dnaHashGetDna ( L_DNAHASH *dahash, l_uint64 key, l_int32 copyflag ); +LEPT_DLL extern l_ok l_dnaHashAdd ( L_DNAHASH *dahash, l_uint64 key, l_float64 value ); +LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromDna ( L_DNA *da ); +LEPT_DLL extern l_ok l_dnaRemoveDupsByHash ( L_DNA *das, L_DNA **pdad, L_DNAHASH **pdahash ); +LEPT_DLL extern l_ok l_dnaMakeHistoByHash ( L_DNA *das, L_DNAHASH **pdahash, L_DNA **pdav, L_DNA **pdac ); +LEPT_DLL extern L_DNA * l_dnaIntersectionByHash ( L_DNA *da1, L_DNA *da2 ); +LEPT_DLL extern l_ok l_dnaFindValByHash ( L_DNA *da, L_DNAHASH *dahash, l_float64 val, l_int32 *pindex ); +LEPT_DLL extern PIX * pixMorphDwa_2 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); +LEPT_DLL extern PIX * pixFMorphopGen_2 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); +LEPT_DLL extern l_int32 fmorphopgen_low_2 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); +LEPT_DLL extern PIX * pixSobelEdgeFilter ( PIX *pixs, l_int32 orientflag ); +LEPT_DLL extern PIX * pixTwoSidedEdgeFilter ( PIX *pixs, l_int32 orientflag ); +LEPT_DLL extern l_ok pixMeasureEdgeSmoothness ( PIX *pixs, l_int32 side, l_int32 minjump, l_int32 minreversal, l_float32 *pjpl, l_float32 *pjspl, l_float32 *prpl, const char *debugfile ); +LEPT_DLL extern NUMA * pixGetEdgeProfile ( PIX *pixs, l_int32 side, const char *debugfile ); +LEPT_DLL extern l_ok pixGetLastOffPixelInRun ( PIX *pixs, l_int32 x, l_int32 y, l_int32 direction, l_int32 *ploc ); +LEPT_DLL extern l_int32 pixGetLastOnPixelInRun ( PIX *pixs, l_int32 x, l_int32 y, l_int32 direction, l_int32 *ploc ); +LEPT_DLL extern char * encodeBase64 ( const l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern l_uint8 * decodeBase64 ( const char *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern char * encodeAscii85 ( const l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern l_uint8 * decodeAscii85 ( const char *inarray, l_int32 insize, l_int32 *poutsize ); +LEPT_DLL extern char * reformatPacked64 ( const char *inarray, l_int32 insize, l_int32 leadspace, l_int32 linechars, l_int32 addquotes, l_int32 *poutsize ); +LEPT_DLL extern PIX * pixGammaTRC ( PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern PIX * pixGammaTRCMasked ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern PIX * pixGammaTRCWithAlpha ( PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern NUMA * numaGammaTRC ( l_float32 gamma, l_int32 minval, l_int32 maxval ); +LEPT_DLL extern PIX * pixContrastTRC ( PIX *pixd, PIX *pixs, l_float32 factor ); +LEPT_DLL extern PIX * pixContrastTRCMasked ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 factor ); +LEPT_DLL extern NUMA * numaContrastTRC ( l_float32 factor ); +LEPT_DLL extern PIX * pixEqualizeTRC ( PIX *pixd, PIX *pixs, l_float32 fract, l_int32 factor ); +LEPT_DLL extern NUMA * numaEqualizeTRC ( PIX *pix, l_float32 fract, l_int32 factor ); +LEPT_DLL extern l_int32 pixTRCMap ( PIX *pixs, PIX *pixm, NUMA *na ); +LEPT_DLL extern l_int32 pixTRCMapGeneral ( PIX *pixs, PIX *pixm, NUMA *nar, NUMA *nag, NUMA *nab ); +LEPT_DLL extern PIX * pixUnsharpMasking ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); +LEPT_DLL extern PIX * pixUnsharpMaskingGray ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); +LEPT_DLL extern PIX * pixUnsharpMaskingFast ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); +LEPT_DLL extern PIX * pixUnsharpMaskingGrayFast ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); +LEPT_DLL extern PIX * pixUnsharpMaskingGray1D ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); +LEPT_DLL extern PIX * pixUnsharpMaskingGray2D ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); +LEPT_DLL extern PIX * pixModifyHue ( PIX *pixd, PIX *pixs, l_float32 fract ); +LEPT_DLL extern PIX * pixModifySaturation ( PIX *pixd, PIX *pixs, l_float32 fract ); +LEPT_DLL extern l_int32 pixMeasureSaturation ( PIX *pixs, l_int32 factor, l_float32 *psat ); +LEPT_DLL extern PIX * pixModifyBrightness ( PIX *pixd, PIX *pixs, l_float32 fract ); +LEPT_DLL extern PIX * pixMosaicColorShiftRGB ( PIX *pixs, l_float32 roff, l_float32 goff, l_float32 boff, l_float32 delta, l_int32 nincr ); +LEPT_DLL extern PIX * pixColorShiftRGB ( PIX *pixs, l_float32 rfract, l_float32 gfract, l_float32 bfract ); +LEPT_DLL extern PIX * pixDarkenGray ( PIX *pixd, PIX *pixs, l_int32 thresh, l_int32 satlimit ); +LEPT_DLL extern PIX * pixMultConstantColor ( PIX *pixs, l_float32 rfact, l_float32 gfact, l_float32 bfact ); +LEPT_DLL extern PIX * pixMultMatrixColor ( PIX *pixs, L_KERNEL *kel ); +LEPT_DLL extern PIX * pixHalfEdgeByBandpass ( PIX *pixs, l_int32 sm1h, l_int32 sm1v, l_int32 sm2h, l_int32 sm2v ); +LEPT_DLL extern l_ok fhmtautogen ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern l_ok fhmtautogen1 ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern l_ok fhmtautogen2 ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern PIX * pixHMTDwa_1 ( PIX *pixd, PIX *pixs, const char *selname ); +LEPT_DLL extern PIX * pixFHMTGen_1 ( PIX *pixd, PIX *pixs, const char *selname ); +LEPT_DLL extern l_int32 fhmtgen_low_1 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); +LEPT_DLL extern l_ok pixItalicWords ( PIX *pixs, BOXA *boxaw, PIX *pixw, BOXA **pboxa, l_int32 debugflag ); +LEPT_DLL extern PIX * pixOrientCorrect ( PIX *pixs, l_float32 minupconf, l_float32 minratio, l_float32 *pupconf, l_float32 *pleftconf, l_int32 *protation, l_int32 debug ); +LEPT_DLL extern l_ok pixOrientDetect ( PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern l_ok makeOrientDecision ( l_float32 upconf, l_float32 leftconf, l_float32 minupconf, l_float32 minratio, l_int32 *porient, l_int32 debug ); +LEPT_DLL extern l_ok pixUpDownDetect ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern l_ok pixUpDownDetectGeneral ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug ); +LEPT_DLL extern l_ok pixOrientDetectDwa ( PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern l_ok pixUpDownDetectDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern l_ok pixUpDownDetectGeneralDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug ); +LEPT_DLL extern l_ok pixMirrorDetect ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern l_ok pixMirrorDetectDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); +LEPT_DLL extern PIX * pixFlipFHMTGen ( PIX *pixd, PIX *pixs, const char *selname ); +LEPT_DLL extern l_ok fmorphautogen ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern l_ok fmorphautogen1 ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern l_int32 fmorphautogen2 ( SELA *sela, l_int32 fileindex, const char *filename ); +LEPT_DLL extern PIX * pixMorphDwa_1 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); +LEPT_DLL extern PIX * pixFMorphopGen_1 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); +LEPT_DLL extern l_int32 fmorphopgen_low_1 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); +LEPT_DLL extern FPIX * fpixCreate ( l_int32 width, l_int32 height ); +LEPT_DLL extern FPIX * fpixCreateTemplate ( FPIX *fpixs ); +LEPT_DLL extern FPIX * fpixClone ( FPIX *fpix ); +LEPT_DLL extern FPIX * fpixCopy ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern l_ok fpixResizeImageData ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern void fpixDestroy ( FPIX **pfpix ); +LEPT_DLL extern l_ok fpixGetDimensions ( FPIX *fpix, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok fpixSetDimensions ( FPIX *fpix, l_int32 w, l_int32 h ); +LEPT_DLL extern l_int32 fpixGetWpl ( FPIX *fpix ); +LEPT_DLL extern l_ok fpixSetWpl ( FPIX *fpix, l_int32 wpl ); +LEPT_DLL extern l_int32 fpixGetRefcount ( FPIX *fpix ); +LEPT_DLL extern l_ok fpixChangeRefcount ( FPIX *fpix, l_int32 delta ); +LEPT_DLL extern l_ok fpixGetResolution ( FPIX *fpix, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok fpixSetResolution ( FPIX *fpix, l_int32 xres, l_int32 yres ); +LEPT_DLL extern l_ok fpixCopyResolution ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern l_float32 * fpixGetData ( FPIX *fpix ); +LEPT_DLL extern l_ok fpixSetData ( FPIX *fpix, l_float32 *data ); +LEPT_DLL extern l_ok fpixGetPixel ( FPIX *fpix, l_int32 x, l_int32 y, l_float32 *pval ); +LEPT_DLL extern l_ok fpixSetPixel ( FPIX *fpix, l_int32 x, l_int32 y, l_float32 val ); +LEPT_DLL extern FPIXA * fpixaCreate ( l_int32 n ); +LEPT_DLL extern FPIXA * fpixaCopy ( FPIXA *fpixa, l_int32 copyflag ); +LEPT_DLL extern void fpixaDestroy ( FPIXA **pfpixa ); +LEPT_DLL extern l_ok fpixaAddFPix ( FPIXA *fpixa, FPIX *fpix, l_int32 copyflag ); +LEPT_DLL extern l_int32 fpixaGetCount ( FPIXA *fpixa ); +LEPT_DLL extern l_ok fpixaChangeRefcount ( FPIXA *fpixa, l_int32 delta ); +LEPT_DLL extern FPIX * fpixaGetFPix ( FPIXA *fpixa, l_int32 index, l_int32 accesstype ); +LEPT_DLL extern l_ok fpixaGetFPixDimensions ( FPIXA *fpixa, l_int32 index, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_float32 * fpixaGetData ( FPIXA *fpixa, l_int32 index ); +LEPT_DLL extern l_ok fpixaGetPixel ( FPIXA *fpixa, l_int32 index, l_int32 x, l_int32 y, l_float32 *pval ); +LEPT_DLL extern l_ok fpixaSetPixel ( FPIXA *fpixa, l_int32 index, l_int32 x, l_int32 y, l_float32 val ); +LEPT_DLL extern DPIX * dpixCreate ( l_int32 width, l_int32 height ); +LEPT_DLL extern DPIX * dpixCreateTemplate ( DPIX *dpixs ); +LEPT_DLL extern DPIX * dpixClone ( DPIX *dpix ); +LEPT_DLL extern DPIX * dpixCopy ( DPIX *dpixd, DPIX *dpixs ); +LEPT_DLL extern l_ok dpixResizeImageData ( DPIX *dpixd, DPIX *dpixs ); +LEPT_DLL extern void dpixDestroy ( DPIX **pdpix ); +LEPT_DLL extern l_ok dpixGetDimensions ( DPIX *dpix, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok dpixSetDimensions ( DPIX *dpix, l_int32 w, l_int32 h ); +LEPT_DLL extern l_int32 dpixGetWpl ( DPIX *dpix ); +LEPT_DLL extern l_ok dpixSetWpl ( DPIX *dpix, l_int32 wpl ); +LEPT_DLL extern l_int32 dpixGetRefcount ( DPIX *dpix ); +LEPT_DLL extern l_ok dpixChangeRefcount ( DPIX *dpix, l_int32 delta ); +LEPT_DLL extern l_ok dpixGetResolution ( DPIX *dpix, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok dpixSetResolution ( DPIX *dpix, l_int32 xres, l_int32 yres ); +LEPT_DLL extern l_ok dpixCopyResolution ( DPIX *dpixd, DPIX *dpixs ); +LEPT_DLL extern l_float64 * dpixGetData ( DPIX *dpix ); +LEPT_DLL extern l_ok dpixSetData ( DPIX *dpix, l_float64 *data ); +LEPT_DLL extern l_ok dpixGetPixel ( DPIX *dpix, l_int32 x, l_int32 y, l_float64 *pval ); +LEPT_DLL extern l_ok dpixSetPixel ( DPIX *dpix, l_int32 x, l_int32 y, l_float64 val ); +LEPT_DLL extern FPIX * fpixRead ( const char *filename ); +LEPT_DLL extern FPIX * fpixReadStream ( FILE *fp ); +LEPT_DLL extern FPIX * fpixReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok fpixWrite ( const char *filename, FPIX *fpix ); +LEPT_DLL extern l_ok fpixWriteStream ( FILE *fp, FPIX *fpix ); +LEPT_DLL extern l_ok fpixWriteMem ( l_uint8 **pdata, size_t *psize, FPIX *fpix ); +LEPT_DLL extern FPIX * fpixEndianByteSwap ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern DPIX * dpixRead ( const char *filename ); +LEPT_DLL extern DPIX * dpixReadStream ( FILE *fp ); +LEPT_DLL extern DPIX * dpixReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok dpixWrite ( const char *filename, DPIX *dpix ); +LEPT_DLL extern l_ok dpixWriteStream ( FILE *fp, DPIX *dpix ); +LEPT_DLL extern l_ok dpixWriteMem ( l_uint8 **pdata, size_t *psize, DPIX *dpix ); +LEPT_DLL extern DPIX * dpixEndianByteSwap ( DPIX *dpixd, DPIX *dpixs ); +LEPT_DLL extern l_ok fpixPrintStream ( FILE *fp, FPIX *fpix, l_int32 factor ); +LEPT_DLL extern FPIX * pixConvertToFPix ( PIX *pixs, l_int32 ncomps ); +LEPT_DLL extern DPIX * pixConvertToDPix ( PIX *pixs, l_int32 ncomps ); +LEPT_DLL extern PIX * fpixConvertToPix ( FPIX *fpixs, l_int32 outdepth, l_int32 negvals, l_int32 errorflag ); +LEPT_DLL extern PIX * fpixDisplayMaxDynamicRange ( FPIX *fpixs ); +LEPT_DLL extern DPIX * fpixConvertToDPix ( FPIX *fpix ); +LEPT_DLL extern PIX * dpixConvertToPix ( DPIX *dpixs, l_int32 outdepth, l_int32 negvals, l_int32 errorflag ); +LEPT_DLL extern FPIX * dpixConvertToFPix ( DPIX *dpix ); +LEPT_DLL extern l_ok fpixGetMin ( FPIX *fpix, l_float32 *pminval, l_int32 *pxminloc, l_int32 *pyminloc ); +LEPT_DLL extern l_ok fpixGetMax ( FPIX *fpix, l_float32 *pmaxval, l_int32 *pxmaxloc, l_int32 *pymaxloc ); +LEPT_DLL extern l_ok dpixGetMin ( DPIX *dpix, l_float64 *pminval, l_int32 *pxminloc, l_int32 *pyminloc ); +LEPT_DLL extern l_ok dpixGetMax ( DPIX *dpix, l_float64 *pmaxval, l_int32 *pxmaxloc, l_int32 *pymaxloc ); +LEPT_DLL extern FPIX * fpixScaleByInteger ( FPIX *fpixs, l_int32 factor ); +LEPT_DLL extern DPIX * dpixScaleByInteger ( DPIX *dpixs, l_int32 factor ); +LEPT_DLL extern FPIX * fpixLinearCombination ( FPIX *fpixd, FPIX *fpixs1, FPIX *fpixs2, l_float32 a, l_float32 b ); +LEPT_DLL extern l_ok fpixAddMultConstant ( FPIX *fpix, l_float32 addc, l_float32 multc ); +LEPT_DLL extern DPIX * dpixLinearCombination ( DPIX *dpixd, DPIX *dpixs1, DPIX *dpixs2, l_float32 a, l_float32 b ); +LEPT_DLL extern l_ok dpixAddMultConstant ( DPIX *dpix, l_float64 addc, l_float64 multc ); +LEPT_DLL extern l_ok fpixSetAllArbitrary ( FPIX *fpix, l_float32 inval ); +LEPT_DLL extern l_ok dpixSetAllArbitrary ( DPIX *dpix, l_float64 inval ); +LEPT_DLL extern FPIX * fpixAddBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern FPIX * fpixRemoveBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern FPIX * fpixAddMirroredBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern FPIX * fpixAddContinuedBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern FPIX * fpixAddSlopeBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern l_ok fpixRasterop ( FPIX *fpixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, FPIX *fpixs, l_int32 sx, l_int32 sy ); +LEPT_DLL extern FPIX * fpixRotateOrth ( FPIX *fpixs, l_int32 quads ); +LEPT_DLL extern FPIX * fpixRotate180 ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern FPIX * fpixRotate90 ( FPIX *fpixs, l_int32 direction ); +LEPT_DLL extern FPIX * fpixFlipLR ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern FPIX * fpixFlipTB ( FPIX *fpixd, FPIX *fpixs ); +LEPT_DLL extern FPIX * fpixAffinePta ( FPIX *fpixs, PTA *ptad, PTA *ptas, l_int32 border, l_float32 inval ); +LEPT_DLL extern FPIX * fpixAffine ( FPIX *fpixs, l_float32 *vc, l_float32 inval ); +LEPT_DLL extern FPIX * fpixProjectivePta ( FPIX *fpixs, PTA *ptad, PTA *ptas, l_int32 border, l_float32 inval ); +LEPT_DLL extern FPIX * fpixProjective ( FPIX *fpixs, l_float32 *vc, l_float32 inval ); +LEPT_DLL extern l_ok linearInterpolatePixelFloat ( l_float32 *datas, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_float32 inval, l_float32 *pval ); +LEPT_DLL extern PIX * fpixThresholdToPix ( FPIX *fpix, l_float32 thresh ); +LEPT_DLL extern FPIX * pixComponentFunction ( PIX *pix, l_float32 rnum, l_float32 gnum, l_float32 bnum, l_float32 rdenom, l_float32 gdenom, l_float32 bdenom ); +LEPT_DLL extern PIX * pixReadStreamGif ( FILE *fp ); +LEPT_DLL extern PIX * pixReadMemGif ( const l_uint8 *cdata, size_t size ); +LEPT_DLL extern l_ok pixWriteStreamGif ( FILE *fp, PIX *pix ); +LEPT_DLL extern l_ok pixWriteMemGif ( l_uint8 **pdata, size_t *psize, PIX *pix ); +LEPT_DLL extern GPLOT * gplotCreate ( const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel ); +LEPT_DLL extern void gplotDestroy ( GPLOT **pgplot ); +LEPT_DLL extern l_ok gplotAddPlot ( GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle ); +LEPT_DLL extern l_ok gplotSetScaling ( GPLOT *gplot, l_int32 scaling ); +LEPT_DLL extern l_ok gplotMakeOutput ( GPLOT *gplot ); +LEPT_DLL extern l_ok gplotGenCommandFile ( GPLOT *gplot ); +LEPT_DLL extern l_ok gplotGenDataFiles ( GPLOT *gplot ); +LEPT_DLL extern l_ok gplotSimple1 ( NUMA *na, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern l_ok gplotSimple2 ( NUMA *na1, NUMA *na2, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern l_ok gplotSimpleN ( NUMAA *naa, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern l_ok gplotSimpleXY1 ( NUMA *nax, NUMA *nay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern l_ok gplotSimpleXY2 ( NUMA *nax, NUMA *nay1, NUMA *nay2, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern l_ok gplotSimpleXYN ( NUMA *nax, NUMAA *naay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); +LEPT_DLL extern GPLOT * gplotRead ( const char *filename ); +LEPT_DLL extern l_ok gplotWrite ( const char *filename, GPLOT *gplot ); +LEPT_DLL extern PTA * generatePtaLine ( l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2 ); +LEPT_DLL extern PTA * generatePtaWideLine ( l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width ); +LEPT_DLL extern PTA * generatePtaBox ( BOX *box, l_int32 width ); +LEPT_DLL extern PTA * generatePtaBoxa ( BOXA *boxa, l_int32 width, l_int32 removedups ); +LEPT_DLL extern PTA * generatePtaHashBox ( BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline ); +LEPT_DLL extern PTA * generatePtaHashBoxa ( BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 removedups ); +LEPT_DLL extern PTAA * generatePtaaBoxa ( BOXA *boxa ); +LEPT_DLL extern PTAA * generatePtaaHashBoxa ( BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline ); +LEPT_DLL extern PTA * generatePtaPolyline ( PTA *ptas, l_int32 width, l_int32 closeflag, l_int32 removedups ); +LEPT_DLL extern PTA * generatePtaGrid ( l_int32 w, l_int32 h, l_int32 nx, l_int32 ny, l_int32 width ); +LEPT_DLL extern PTA * convertPtaLineTo4cc ( PTA *ptas ); +LEPT_DLL extern PTA * generatePtaFilledCircle ( l_int32 radius ); +LEPT_DLL extern PTA * generatePtaFilledSquare ( l_int32 side ); +LEPT_DLL extern PTA * generatePtaLineFromPt ( l_int32 x, l_int32 y, l_float64 length, l_float64 radang ); +LEPT_DLL extern l_ok locatePtRadially ( l_int32 xr, l_int32 yr, l_float64 dist, l_float64 radang, l_float64 *px, l_float64 *py ); +LEPT_DLL extern l_ok pixRenderPlotFromNuma ( PIX **ppix, NUMA *na, l_int32 plotloc, l_int32 linewidth, l_int32 max, l_uint32 color ); +LEPT_DLL extern PTA * makePlotPtaFromNuma ( NUMA *na, l_int32 size, l_int32 plotloc, l_int32 linewidth, l_int32 max ); +LEPT_DLL extern l_ok pixRenderPlotFromNumaGen ( PIX **ppix, NUMA *na, l_int32 orient, l_int32 linewidth, l_int32 refpos, l_int32 max, l_int32 drawref, l_uint32 color ); +LEPT_DLL extern PTA * makePlotPtaFromNumaGen ( NUMA *na, l_int32 orient, l_int32 linewidth, l_int32 refpos, l_int32 max, l_int32 drawref ); +LEPT_DLL extern l_ok pixRenderPta ( PIX *pix, PTA *pta, l_int32 op ); +LEPT_DLL extern l_ok pixRenderPtaArb ( PIX *pix, PTA *pta, l_uint8 rval, l_uint8 gval, l_uint8 bval ); +LEPT_DLL extern l_ok pixRenderPtaBlend ( PIX *pix, PTA *pta, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); +LEPT_DLL extern l_ok pixRenderLine ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_int32 op ); +LEPT_DLL extern l_ok pixRenderLineArb ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); +LEPT_DLL extern l_ok pixRenderLineBlend ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); +LEPT_DLL extern l_ok pixRenderBox ( PIX *pix, BOX *box, l_int32 width, l_int32 op ); +LEPT_DLL extern l_ok pixRenderBoxArb ( PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); +LEPT_DLL extern l_ok pixRenderBoxBlend ( PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); +LEPT_DLL extern l_ok pixRenderBoxa ( PIX *pix, BOXA *boxa, l_int32 width, l_int32 op ); +LEPT_DLL extern l_ok pixRenderBoxaArb ( PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); +LEPT_DLL extern l_ok pixRenderBoxaBlend ( PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract, l_int32 removedups ); +LEPT_DLL extern l_ok pixRenderHashBox ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 op ); +LEPT_DLL extern l_ok pixRenderHashBoxArb ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixRenderHashBoxBlend ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fract ); +LEPT_DLL extern l_ok pixRenderHashMaskArb ( PIX *pix, PIX *pixm, l_int32 x, l_int32 y, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixRenderHashBoxa ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 op ); +LEPT_DLL extern l_ok pixRenderHashBoxaArb ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixRenderHashBoxaBlend ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fract ); +LEPT_DLL extern l_ok pixRenderPolyline ( PIX *pix, PTA *ptas, l_int32 width, l_int32 op, l_int32 closeflag ); +LEPT_DLL extern l_ok pixRenderPolylineArb ( PIX *pix, PTA *ptas, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_int32 closeflag ); +LEPT_DLL extern l_ok pixRenderPolylineBlend ( PIX *pix, PTA *ptas, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract, l_int32 closeflag, l_int32 removedups ); +LEPT_DLL extern l_ok pixRenderGridArb ( PIX *pix, l_int32 nx, l_int32 ny, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); +LEPT_DLL extern PIX * pixRenderRandomCmapPtaa ( PIX *pix, PTAA *ptaa, l_int32 polyflag, l_int32 width, l_int32 closeflag ); +LEPT_DLL extern PIX * pixRenderPolygon ( PTA *ptas, l_int32 width, l_int32 *pxmin, l_int32 *pymin ); +LEPT_DLL extern PIX * pixFillPolygon ( PIX *pixs, PTA *pta, l_int32 xmin, l_int32 ymin ); +LEPT_DLL extern PIX * pixRenderContours ( PIX *pixs, l_int32 startval, l_int32 incr, l_int32 outdepth ); +LEPT_DLL extern PIX * fpixAutoRenderContours ( FPIX *fpix, l_int32 ncontours ); +LEPT_DLL extern PIX * fpixRenderContours ( FPIX *fpixs, l_float32 incr, l_float32 proxim ); +LEPT_DLL extern PTA * pixGeneratePtaBoundary ( PIX *pixs, l_int32 width ); +LEPT_DLL extern PIX * pixErodeGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixDilateGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixDilateGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixDitherToBinary ( PIX *pixs ); +LEPT_DLL extern PIX * pixDitherToBinarySpec ( PIX *pixs, l_int32 lowerclip, l_int32 upperclip ); +LEPT_DLL extern void ditherToBinaryLineLow ( l_uint32 *lined, l_int32 w, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 lowerclip, l_int32 upperclip, l_int32 lastlineflag ); +LEPT_DLL extern PIX * pixThresholdToBinary ( PIX *pixs, l_int32 thresh ); +LEPT_DLL extern void thresholdToBinaryLineLow ( l_uint32 *lined, l_int32 w, l_uint32 *lines, l_int32 d, l_int32 thresh ); +LEPT_DLL extern PIX * pixVarThresholdToBinary ( PIX *pixs, PIX *pixg ); +LEPT_DLL extern PIX * pixAdaptThresholdToBinary ( PIX *pixs, PIX *pixm, l_float32 gamma ); +LEPT_DLL extern PIX * pixAdaptThresholdToBinaryGen ( PIX *pixs, PIX *pixm, l_float32 gamma, l_int32 blackval, l_int32 whiteval, l_int32 thresh ); +LEPT_DLL extern PIX * pixGenerateMaskByValue ( PIX *pixs, l_int32 val, l_int32 usecmap ); +LEPT_DLL extern PIX * pixGenerateMaskByBand ( PIX *pixs, l_int32 lower, l_int32 upper, l_int32 inband, l_int32 usecmap ); +LEPT_DLL extern PIX * pixDitherTo2bpp ( PIX *pixs, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixDitherTo2bppSpec ( PIX *pixs, l_int32 lowerclip, l_int32 upperclip, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixThresholdTo2bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixThresholdTo4bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixThresholdOn8bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixThresholdGrayArb ( PIX *pixs, const char *edgevals, l_int32 outdepth, l_int32 use_average, l_int32 setblack, l_int32 setwhite ); +LEPT_DLL extern l_int32 * makeGrayQuantIndexTable ( l_int32 nlevels ); +LEPT_DLL extern l_ok makeGrayQuantTableArb ( NUMA *na, l_int32 outdepth, l_int32 **ptab, PIXCMAP **pcmap ); +LEPT_DLL extern PIX * pixGenerateMaskByBand32 ( PIX *pixs, l_uint32 refval, l_int32 delm, l_int32 delp, l_float32 fractm, l_float32 fractp ); +LEPT_DLL extern PIX * pixGenerateMaskByDiscr32 ( PIX *pixs, l_uint32 refval1, l_uint32 refval2, l_int32 distflag ); +LEPT_DLL extern PIX * pixGrayQuantFromHisto ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 minfract, l_int32 maxsize ); +LEPT_DLL extern PIX * pixGrayQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth ); +LEPT_DLL extern L_HEAP * lheapCreate ( l_int32 n, l_int32 direction ); +LEPT_DLL extern void lheapDestroy ( L_HEAP **plh, l_int32 freeflag ); +LEPT_DLL extern l_ok lheapAdd ( L_HEAP *lh, void *item ); +LEPT_DLL extern void * lheapRemove ( L_HEAP *lh ); +LEPT_DLL extern l_int32 lheapGetCount ( L_HEAP *lh ); +LEPT_DLL extern l_ok lheapSwapUp ( L_HEAP *lh, l_int32 index ); +LEPT_DLL extern l_ok lheapSwapDown ( L_HEAP *lh ); +LEPT_DLL extern l_ok lheapSort ( L_HEAP *lh ); +LEPT_DLL extern l_ok lheapSortStrictOrder ( L_HEAP *lh ); +LEPT_DLL extern l_ok lheapPrint ( FILE *fp, L_HEAP *lh ); +LEPT_DLL extern JBCLASSER * jbRankHausInit ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_int32 size, l_float32 rank ); +LEPT_DLL extern JBCLASSER * jbCorrelationInit ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weightfactor ); +LEPT_DLL extern JBCLASSER * jbCorrelationInitWithoutComponents ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weightfactor ); +LEPT_DLL extern l_ok jbAddPages ( JBCLASSER *classer, SARRAY *safiles ); +LEPT_DLL extern l_ok jbAddPage ( JBCLASSER *classer, PIX *pixs ); +LEPT_DLL extern l_ok jbAddPageComponents ( JBCLASSER *classer, PIX *pixs, BOXA *boxas, PIXA *pixas ); +LEPT_DLL extern l_ok jbClassifyRankHaus ( JBCLASSER *classer, BOXA *boxa, PIXA *pixas ); +LEPT_DLL extern l_int32 pixHaustest ( PIX *pix1, PIX *pix2, PIX *pix3, PIX *pix4, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh ); +LEPT_DLL extern l_int32 pixRankHaustest ( PIX *pix1, PIX *pix2, PIX *pix3, PIX *pix4, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 area1, l_int32 area3, l_float32 rank, l_int32 *tab8 ); +LEPT_DLL extern l_ok jbClassifyCorrelation ( JBCLASSER *classer, BOXA *boxa, PIXA *pixas ); +LEPT_DLL extern l_ok jbGetComponents ( PIX *pixs, l_int32 components, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad ); +LEPT_DLL extern l_ok pixWordMaskByDilation ( PIX *pixs, PIX **ppixm, l_int32 *psize, PIXA *pixadb ); +LEPT_DLL extern l_ok pixWordBoxesByDilation ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxa, l_int32 *psize, PIXA *pixadb ); +LEPT_DLL extern PIXA * jbAccumulateComposites ( PIXAA *pixaa, NUMA **pna, PTA **pptat ); +LEPT_DLL extern PIXA * jbTemplatesFromComposites ( PIXA *pixac, NUMA *na ); +LEPT_DLL extern JBCLASSER * jbClasserCreate ( l_int32 method, l_int32 components ); +LEPT_DLL extern void jbClasserDestroy ( JBCLASSER **pclasser ); +LEPT_DLL extern JBDATA * jbDataSave ( JBCLASSER *classer ); +LEPT_DLL extern void jbDataDestroy ( JBDATA **pdata ); +LEPT_DLL extern l_ok jbDataWrite ( const char *rootout, JBDATA *jbdata ); +LEPT_DLL extern JBDATA * jbDataRead ( const char *rootname ); +LEPT_DLL extern PIXA * jbDataRender ( JBDATA *data, l_int32 debugflag ); +LEPT_DLL extern l_ok jbGetULCorners ( JBCLASSER *classer, PIX *pixs, BOXA *boxa ); +LEPT_DLL extern l_ok jbGetLLCorners ( JBCLASSER *classer ); +LEPT_DLL extern l_ok readHeaderJp2k ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_ok freadHeaderJp2k ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_ok readHeaderMemJp2k ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_int32 fgetJp2kResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern PIX * pixReadJp2k ( const char *filename, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); +LEPT_DLL extern PIX * pixReadStreamJp2k ( FILE *fp, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); +LEPT_DLL extern l_ok pixWriteJp2k ( const char *filename, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); +LEPT_DLL extern l_ok pixWriteStreamJp2k ( FILE *fp, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); +LEPT_DLL extern PIX * pixReadMemJp2k ( const l_uint8 *data, size_t size, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); +LEPT_DLL extern l_ok pixWriteMemJp2k ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); +LEPT_DLL extern PIX * pixReadJpeg ( const char *filename, l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); +LEPT_DLL extern PIX * pixReadStreamJpeg ( FILE *fp, l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); +LEPT_DLL extern l_ok readHeaderJpeg ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); +LEPT_DLL extern l_ok freadHeaderJpeg ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); +LEPT_DLL extern l_int32 fgetJpegResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_int32 fgetJpegComment ( FILE *fp, l_uint8 **pcomment ); +LEPT_DLL extern l_ok pixWriteJpeg ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive ); +LEPT_DLL extern l_ok pixWriteStreamJpeg ( FILE *fp, PIX *pixs, l_int32 quality, l_int32 progressive ); +LEPT_DLL extern PIX * pixReadMemJpeg ( const l_uint8 *data, size_t size, l_int32 cmflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); +LEPT_DLL extern l_ok readHeaderMemJpeg ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); +LEPT_DLL extern l_ok readResolutionMemJpeg ( const l_uint8 *data, size_t size, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok pixWriteMemJpeg ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 quality, l_int32 progressive ); +LEPT_DLL extern l_ok pixSetChromaSampling ( PIX *pix, l_int32 sampling ); +LEPT_DLL extern L_KERNEL * kernelCreate ( l_int32 height, l_int32 width ); +LEPT_DLL extern void kernelDestroy ( L_KERNEL **pkel ); +LEPT_DLL extern L_KERNEL * kernelCopy ( L_KERNEL *kels ); +LEPT_DLL extern l_ok kernelGetElement ( L_KERNEL *kel, l_int32 row, l_int32 col, l_float32 *pval ); +LEPT_DLL extern l_ok kernelSetElement ( L_KERNEL *kel, l_int32 row, l_int32 col, l_float32 val ); +LEPT_DLL extern l_ok kernelGetParameters ( L_KERNEL *kel, l_int32 *psy, l_int32 *psx, l_int32 *pcy, l_int32 *pcx ); +LEPT_DLL extern l_ok kernelSetOrigin ( L_KERNEL *kel, l_int32 cy, l_int32 cx ); +LEPT_DLL extern l_ok kernelGetSum ( L_KERNEL *kel, l_float32 *psum ); +LEPT_DLL extern l_ok kernelGetMinMax ( L_KERNEL *kel, l_float32 *pmin, l_float32 *pmax ); +LEPT_DLL extern L_KERNEL * kernelNormalize ( L_KERNEL *kels, l_float32 normsum ); +LEPT_DLL extern L_KERNEL * kernelInvert ( L_KERNEL *kels ); +LEPT_DLL extern l_float32 ** create2dFloatArray ( l_int32 sy, l_int32 sx ); +LEPT_DLL extern L_KERNEL * kernelRead ( const char *fname ); +LEPT_DLL extern L_KERNEL * kernelReadStream ( FILE *fp ); +LEPT_DLL extern l_ok kernelWrite ( const char *fname, L_KERNEL *kel ); +LEPT_DLL extern l_ok kernelWriteStream ( FILE *fp, L_KERNEL *kel ); +LEPT_DLL extern L_KERNEL * kernelCreateFromString ( l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, const char *kdata ); +LEPT_DLL extern L_KERNEL * kernelCreateFromFile ( const char *filename ); +LEPT_DLL extern L_KERNEL * kernelCreateFromPix ( PIX *pix, l_int32 cy, l_int32 cx ); +LEPT_DLL extern PIX * kernelDisplayInPix ( L_KERNEL *kel, l_int32 size, l_int32 gthick ); +LEPT_DLL extern NUMA * parseStringForNumbers ( const char *str, const char *seps ); +LEPT_DLL extern L_KERNEL * makeFlatKernel ( l_int32 height, l_int32 width, l_int32 cy, l_int32 cx ); +LEPT_DLL extern L_KERNEL * makeGaussianKernel ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 max ); +LEPT_DLL extern l_ok makeGaussianKernelSep ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 max, L_KERNEL **pkelx, L_KERNEL **pkely ); +LEPT_DLL extern L_KERNEL * makeDoGKernel ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 ratio ); +LEPT_DLL extern char * getImagelibVersions ( ); +LEPT_DLL extern void listDestroy ( DLLIST **phead ); +LEPT_DLL extern l_ok listAddToHead ( DLLIST **phead, void *data ); +LEPT_DLL extern l_ok listAddToTail ( DLLIST **phead, DLLIST **ptail, void *data ); +LEPT_DLL extern l_ok listInsertBefore ( DLLIST **phead, DLLIST *elem, void *data ); +LEPT_DLL extern l_ok listInsertAfter ( DLLIST **phead, DLLIST *elem, void *data ); +LEPT_DLL extern void * listRemoveElement ( DLLIST **phead, DLLIST *elem ); +LEPT_DLL extern void * listRemoveFromHead ( DLLIST **phead ); +LEPT_DLL extern void * listRemoveFromTail ( DLLIST **phead, DLLIST **ptail ); +LEPT_DLL extern DLLIST * listFindElement ( DLLIST *head, void *data ); +LEPT_DLL extern DLLIST * listFindTail ( DLLIST *head ); +LEPT_DLL extern l_int32 listGetCount ( DLLIST *head ); +LEPT_DLL extern l_ok listReverse ( DLLIST **phead ); +LEPT_DLL extern l_ok listJoin ( DLLIST **phead1, DLLIST **phead2 ); +LEPT_DLL extern L_AMAP * l_amapCreate ( l_int32 keytype ); +LEPT_DLL extern RB_TYPE * l_amapFind ( L_AMAP *m, RB_TYPE key ); +LEPT_DLL extern void l_amapInsert ( L_AMAP *m, RB_TYPE key, RB_TYPE value ); +LEPT_DLL extern void l_amapDelete ( L_AMAP *m, RB_TYPE key ); +LEPT_DLL extern void l_amapDestroy ( L_AMAP **pm ); +LEPT_DLL extern L_AMAP_NODE * l_amapGetFirst ( L_AMAP *m ); +LEPT_DLL extern L_AMAP_NODE * l_amapGetNext ( L_AMAP_NODE *n ); +LEPT_DLL extern L_AMAP_NODE * l_amapGetLast ( L_AMAP *m ); +LEPT_DLL extern L_AMAP_NODE * l_amapGetPrev ( L_AMAP_NODE *n ); +LEPT_DLL extern l_int32 l_amapSize ( L_AMAP *m ); +LEPT_DLL extern L_ASET * l_asetCreate ( l_int32 keytype ); +LEPT_DLL extern RB_TYPE * l_asetFind ( L_ASET *s, RB_TYPE key ); +LEPT_DLL extern void l_asetInsert ( L_ASET *s, RB_TYPE key ); +LEPT_DLL extern void l_asetDelete ( L_ASET *s, RB_TYPE key ); +LEPT_DLL extern void l_asetDestroy ( L_ASET **ps ); +LEPT_DLL extern L_ASET_NODE * l_asetGetFirst ( L_ASET *s ); +LEPT_DLL extern L_ASET_NODE * l_asetGetNext ( L_ASET_NODE *n ); +LEPT_DLL extern L_ASET_NODE * l_asetGetLast ( L_ASET *s ); +LEPT_DLL extern L_ASET_NODE * l_asetGetPrev ( L_ASET_NODE *n ); +LEPT_DLL extern l_int32 l_asetSize ( L_ASET *s ); +LEPT_DLL extern PIX * generateBinaryMaze ( l_int32 w, l_int32 h, l_int32 xi, l_int32 yi, l_float32 wallps, l_float32 ranis ); +LEPT_DLL extern PTA * pixSearchBinaryMaze ( PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd ); +LEPT_DLL extern PTA * pixSearchGrayMaze ( PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd ); +LEPT_DLL extern PIX * pixDilate ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixErode ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixHMT ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixOpen ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixClose ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixCloseSafe ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixOpenGeneralized ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixCloseGeneralized ( PIX *pixd, PIX *pixs, SEL *sel ); +LEPT_DLL extern PIX * pixDilateBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseSafeBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern l_int32 selectComposableSels ( l_int32 size, l_int32 direction, SEL **psel1, SEL **psel2 ); +LEPT_DLL extern l_ok selectComposableSizes ( l_int32 size, l_int32 *pfactor1, l_int32 *pfactor2 ); +LEPT_DLL extern PIX * pixDilateCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseSafeCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern void resetMorphBoundaryCondition ( l_int32 bc ); +LEPT_DLL extern l_uint32 getMorphBorderPixelColor ( l_int32 type, l_int32 depth ); +LEPT_DLL extern PIX * pixExtractBoundary ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixMorphSequenceMasked ( PIX *pixs, PIX *pixm, const char *sequence, l_int32 dispsep ); +LEPT_DLL extern PIX * pixMorphSequenceByComponent ( PIX *pixs, const char *sequence, l_int32 connectivity, l_int32 minw, l_int32 minh, BOXA **pboxa ); +LEPT_DLL extern PIXA * pixaMorphSequenceByComponent ( PIXA *pixas, const char *sequence, l_int32 minw, l_int32 minh ); +LEPT_DLL extern PIX * pixMorphSequenceByRegion ( PIX *pixs, PIX *pixm, const char *sequence, l_int32 connectivity, l_int32 minw, l_int32 minh, BOXA **pboxa ); +LEPT_DLL extern PIXA * pixaMorphSequenceByRegion ( PIX *pixs, PIXA *pixam, const char *sequence, l_int32 minw, l_int32 minh ); +LEPT_DLL extern PIX * pixUnionOfMorphOps ( PIX *pixs, SELA *sela, l_int32 type ); +LEPT_DLL extern PIX * pixIntersectionOfMorphOps ( PIX *pixs, SELA *sela, l_int32 type ); +LEPT_DLL extern PIX * pixSelectiveConnCompFill ( PIX *pixs, l_int32 connectivity, l_int32 minw, l_int32 minh ); +LEPT_DLL extern l_ok pixRemoveMatchedPattern ( PIX *pixs, PIX *pixp, PIX *pixe, l_int32 x0, l_int32 y0, l_int32 dsize ); +LEPT_DLL extern PIX * pixDisplayMatchedPattern ( PIX *pixs, PIX *pixp, PIX *pixe, l_int32 x0, l_int32 y0, l_uint32 color, l_float32 scale, l_int32 nlevels ); +LEPT_DLL extern PIXA * pixaExtendByMorph ( PIXA *pixas, l_int32 type, l_int32 niters, SEL *sel, l_int32 include ); +LEPT_DLL extern PIXA * pixaExtendByScaling ( PIXA *pixas, NUMA *nasc, l_int32 type, l_int32 include ); +LEPT_DLL extern PIX * pixSeedfillMorph ( PIX *pixs, PIX *pixm, l_int32 maxiters, l_int32 connectivity ); +LEPT_DLL extern NUMA * pixRunHistogramMorph ( PIX *pixs, l_int32 runtype, l_int32 direction, l_int32 maxsize ); +LEPT_DLL extern PIX * pixTophat ( PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 type ); +LEPT_DLL extern PIX * pixHDome ( PIX *pixs, l_int32 height, l_int32 connectivity ); +LEPT_DLL extern PIX * pixFastTophat ( PIX *pixs, l_int32 xsize, l_int32 ysize, l_int32 type ); +LEPT_DLL extern PIX * pixMorphGradient ( PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 smoothing ); +LEPT_DLL extern PTA * pixaCentroids ( PIXA *pixa ); +LEPT_DLL extern l_ok pixCentroid ( PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave ); +LEPT_DLL extern PIX * pixDilateBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixDilateCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixDilateCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixErodeCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixOpenCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern PIX * pixCloseCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern l_ok getExtendedCompositeParameters ( l_int32 size, l_int32 *pn, l_int32 *pextra, l_int32 *pactualsize ); +LEPT_DLL extern PIX * pixMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep ); +LEPT_DLL extern PIX * pixMorphCompSequence ( PIX *pixs, const char *sequence, l_int32 dispsep ); +LEPT_DLL extern PIX * pixMorphSequenceDwa ( PIX *pixs, const char *sequence, l_int32 dispsep ); +LEPT_DLL extern PIX * pixMorphCompSequenceDwa ( PIX *pixs, const char *sequence, l_int32 dispsep ); +LEPT_DLL extern l_int32 morphSequenceVerify ( SARRAY *sa ); +LEPT_DLL extern PIX * pixGrayMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy ); +LEPT_DLL extern PIX * pixColorMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy ); +LEPT_DLL extern NUMA * numaCreate ( l_int32 n ); +LEPT_DLL extern NUMA * numaCreateFromIArray ( l_int32 *iarray, l_int32 size ); +LEPT_DLL extern NUMA * numaCreateFromFArray ( l_float32 *farray, l_int32 size, l_int32 copyflag ); +LEPT_DLL extern NUMA * numaCreateFromString ( const char *str ); +LEPT_DLL extern void numaDestroy ( NUMA **pna ); +LEPT_DLL extern NUMA * numaCopy ( NUMA *na ); +LEPT_DLL extern NUMA * numaClone ( NUMA *na ); +LEPT_DLL extern l_ok numaEmpty ( NUMA *na ); +LEPT_DLL extern l_ok numaAddNumber ( NUMA *na, l_float32 val ); +LEPT_DLL extern l_ok numaInsertNumber ( NUMA *na, l_int32 index, l_float32 val ); +LEPT_DLL extern l_ok numaRemoveNumber ( NUMA *na, l_int32 index ); +LEPT_DLL extern l_ok numaReplaceNumber ( NUMA *na, l_int32 index, l_float32 val ); +LEPT_DLL extern l_int32 numaGetCount ( NUMA *na ); +LEPT_DLL extern l_ok numaSetCount ( NUMA *na, l_int32 newcount ); +LEPT_DLL extern l_ok numaGetFValue ( NUMA *na, l_int32 index, l_float32 *pval ); +LEPT_DLL extern l_ok numaGetIValue ( NUMA *na, l_int32 index, l_int32 *pival ); +LEPT_DLL extern l_ok numaSetValue ( NUMA *na, l_int32 index, l_float32 val ); +LEPT_DLL extern l_ok numaShiftValue ( NUMA *na, l_int32 index, l_float32 diff ); +LEPT_DLL extern l_int32 * numaGetIArray ( NUMA *na ); +LEPT_DLL extern l_float32 * numaGetFArray ( NUMA *na, l_int32 copyflag ); +LEPT_DLL extern l_int32 numaGetRefcount ( NUMA *na ); +LEPT_DLL extern l_ok numaChangeRefcount ( NUMA *na, l_int32 delta ); +LEPT_DLL extern l_ok numaGetParameters ( NUMA *na, l_float32 *pstartx, l_float32 *pdelx ); +LEPT_DLL extern l_ok numaSetParameters ( NUMA *na, l_float32 startx, l_float32 delx ); +LEPT_DLL extern l_ok numaCopyParameters ( NUMA *nad, NUMA *nas ); +LEPT_DLL extern SARRAY * numaConvertToSarray ( NUMA *na, l_int32 size1, l_int32 size2, l_int32 addzeros, l_int32 type ); +LEPT_DLL extern NUMA * numaRead ( const char *filename ); +LEPT_DLL extern NUMA * numaReadStream ( FILE *fp ); +LEPT_DLL extern NUMA * numaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok numaWriteDebug ( const char *filename, NUMA *na ); +LEPT_DLL extern l_ok numaWrite ( const char *filename, NUMA *na ); +LEPT_DLL extern l_ok numaWriteStream ( FILE *fp, NUMA *na ); +LEPT_DLL extern l_ok numaWriteMem ( l_uint8 **pdata, size_t *psize, NUMA *na ); +LEPT_DLL extern NUMAA * numaaCreate ( l_int32 n ); +LEPT_DLL extern NUMAA * numaaCreateFull ( l_int32 nptr, l_int32 n ); +LEPT_DLL extern l_ok numaaTruncate ( NUMAA *naa ); +LEPT_DLL extern void numaaDestroy ( NUMAA **pnaa ); +LEPT_DLL extern l_ok numaaAddNuma ( NUMAA *naa, NUMA *na, l_int32 copyflag ); +LEPT_DLL extern l_int32 numaaGetCount ( NUMAA *naa ); +LEPT_DLL extern l_int32 numaaGetNumaCount ( NUMAA *naa, l_int32 index ); +LEPT_DLL extern l_int32 numaaGetNumberCount ( NUMAA *naa ); +LEPT_DLL extern NUMA ** numaaGetPtrArray ( NUMAA *naa ); +LEPT_DLL extern NUMA * numaaGetNuma ( NUMAA *naa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern l_ok numaaReplaceNuma ( NUMAA *naa, l_int32 index, NUMA *na ); +LEPT_DLL extern l_ok numaaGetValue ( NUMAA *naa, l_int32 i, l_int32 j, l_float32 *pfval, l_int32 *pival ); +LEPT_DLL extern l_ok numaaAddNumber ( NUMAA *naa, l_int32 index, l_float32 val ); +LEPT_DLL extern NUMAA * numaaRead ( const char *filename ); +LEPT_DLL extern NUMAA * numaaReadStream ( FILE *fp ); +LEPT_DLL extern NUMAA * numaaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok numaaWrite ( const char *filename, NUMAA *naa ); +LEPT_DLL extern l_ok numaaWriteStream ( FILE *fp, NUMAA *naa ); +LEPT_DLL extern l_ok numaaWriteMem ( l_uint8 **pdata, size_t *psize, NUMAA *naa ); +LEPT_DLL extern NUMA * numaArithOp ( NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op ); +LEPT_DLL extern NUMA * numaLogicalOp ( NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op ); +LEPT_DLL extern NUMA * numaInvert ( NUMA *nad, NUMA *nas ); +LEPT_DLL extern l_int32 numaSimilar ( NUMA *na1, NUMA *na2, l_float32 maxdiff, l_int32 *psimilar ); +LEPT_DLL extern l_ok numaAddToNumber ( NUMA *na, l_int32 index, l_float32 val ); +LEPT_DLL extern l_ok numaGetMin ( NUMA *na, l_float32 *pminval, l_int32 *piminloc ); +LEPT_DLL extern l_ok numaGetMax ( NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc ); +LEPT_DLL extern l_ok numaGetSum ( NUMA *na, l_float32 *psum ); +LEPT_DLL extern NUMA * numaGetPartialSums ( NUMA *na ); +LEPT_DLL extern l_ok numaGetSumOnInterval ( NUMA *na, l_int32 first, l_int32 last, l_float32 *psum ); +LEPT_DLL extern l_ok numaHasOnlyIntegers ( NUMA *na, l_int32 maxsamples, l_int32 *pallints ); +LEPT_DLL extern NUMA * numaSubsample ( NUMA *nas, l_int32 subfactor ); +LEPT_DLL extern NUMA * numaMakeDelta ( NUMA *nas ); +LEPT_DLL extern NUMA * numaMakeSequence ( l_float32 startval, l_float32 increment, l_int32 size ); +LEPT_DLL extern NUMA * numaMakeConstant ( l_float32 val, l_int32 size ); +LEPT_DLL extern NUMA * numaMakeAbsValue ( NUMA *nad, NUMA *nas ); +LEPT_DLL extern NUMA * numaAddBorder ( NUMA *nas, l_int32 left, l_int32 right, l_float32 val ); +LEPT_DLL extern NUMA * numaAddSpecifiedBorder ( NUMA *nas, l_int32 left, l_int32 right, l_int32 type ); +LEPT_DLL extern NUMA * numaRemoveBorder ( NUMA *nas, l_int32 left, l_int32 right ); +LEPT_DLL extern l_ok numaCountNonzeroRuns ( NUMA *na, l_int32 *pcount ); +LEPT_DLL extern l_ok numaGetNonzeroRange ( NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast ); +LEPT_DLL extern l_ok numaGetCountRelativeToZero ( NUMA *na, l_int32 type, l_int32 *pcount ); +LEPT_DLL extern NUMA * numaClipToInterval ( NUMA *nas, l_int32 first, l_int32 last ); +LEPT_DLL extern NUMA * numaMakeThresholdIndicator ( NUMA *nas, l_float32 thresh, l_int32 type ); +LEPT_DLL extern NUMA * numaUniformSampling ( NUMA *nas, l_int32 nsamp ); +LEPT_DLL extern NUMA * numaReverse ( NUMA *nad, NUMA *nas ); +LEPT_DLL extern NUMA * numaLowPassIntervals ( NUMA *nas, l_float32 thresh, l_float32 maxn ); +LEPT_DLL extern NUMA * numaThresholdEdges ( NUMA *nas, l_float32 thresh1, l_float32 thresh2, l_float32 maxn ); +LEPT_DLL extern l_int32 numaGetSpanValues ( NUMA *na, l_int32 span, l_int32 *pstart, l_int32 *pend ); +LEPT_DLL extern l_int32 numaGetEdgeValues ( NUMA *na, l_int32 edge, l_int32 *pstart, l_int32 *pend, l_int32 *psign ); +LEPT_DLL extern l_ok numaInterpolateEqxVal ( l_float32 startx, l_float32 deltax, NUMA *nay, l_int32 type, l_float32 xval, l_float32 *pyval ); +LEPT_DLL extern l_ok numaInterpolateArbxVal ( NUMA *nax, NUMA *nay, l_int32 type, l_float32 xval, l_float32 *pyval ); +LEPT_DLL extern l_ok numaInterpolateEqxInterval ( l_float32 startx, l_float32 deltax, NUMA *nasy, l_int32 type, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnax, NUMA **pnay ); +LEPT_DLL extern l_ok numaInterpolateArbxInterval ( NUMA *nax, NUMA *nay, l_int32 type, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnadx, NUMA **pnady ); +LEPT_DLL extern l_ok numaFitMax ( NUMA *na, l_float32 *pmaxval, NUMA *naloc, l_float32 *pmaxloc ); +LEPT_DLL extern l_ok numaDifferentiateInterval ( NUMA *nax, NUMA *nay, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnadx, NUMA **pnady ); +LEPT_DLL extern l_ok numaIntegrateInterval ( NUMA *nax, NUMA *nay, l_float32 x0, l_float32 x1, l_int32 npts, l_float32 *psum ); +LEPT_DLL extern l_ok numaSortGeneral ( NUMA *na, NUMA **pnasort, NUMA **pnaindex, NUMA **pnainvert, l_int32 sortorder, l_int32 sorttype ); +LEPT_DLL extern NUMA * numaSortAutoSelect ( NUMA *nas, l_int32 sortorder ); +LEPT_DLL extern NUMA * numaSortIndexAutoSelect ( NUMA *nas, l_int32 sortorder ); +LEPT_DLL extern l_int32 numaChooseSortType ( NUMA *nas ); +LEPT_DLL extern NUMA * numaSort ( NUMA *naout, NUMA *nain, l_int32 sortorder ); +LEPT_DLL extern NUMA * numaBinSort ( NUMA *nas, l_int32 sortorder ); +LEPT_DLL extern NUMA * numaGetSortIndex ( NUMA *na, l_int32 sortorder ); +LEPT_DLL extern NUMA * numaGetBinSortIndex ( NUMA *nas, l_int32 sortorder ); +LEPT_DLL extern NUMA * numaSortByIndex ( NUMA *nas, NUMA *naindex ); +LEPT_DLL extern l_int32 numaIsSorted ( NUMA *nas, l_int32 sortorder, l_int32 *psorted ); +LEPT_DLL extern l_ok numaSortPair ( NUMA *nax, NUMA *nay, l_int32 sortorder, NUMA **pnasx, NUMA **pnasy ); +LEPT_DLL extern NUMA * numaInvertMap ( NUMA *nas ); +LEPT_DLL extern NUMA * numaPseudorandomSequence ( l_int32 size, l_int32 seed ); +LEPT_DLL extern NUMA * numaRandomPermutation ( NUMA *nas, l_int32 seed ); +LEPT_DLL extern l_ok numaGetRankValue ( NUMA *na, l_float32 fract, NUMA *nasort, l_int32 usebins, l_float32 *pval ); +LEPT_DLL extern l_ok numaGetMedian ( NUMA *na, l_float32 *pval ); +LEPT_DLL extern l_ok numaGetBinnedMedian ( NUMA *na, l_int32 *pval ); +LEPT_DLL extern l_ok numaGetMeanDevFromMedian ( NUMA *na, l_float32 med, l_float32 *pdev ); +LEPT_DLL extern l_ok numaGetMedianDevFromMedian ( NUMA *na, l_float32 *pmed, l_float32 *pdev ); +LEPT_DLL extern l_ok numaGetMode ( NUMA *na, l_float32 *pval, l_int32 *pcount ); +LEPT_DLL extern l_ok numaJoin ( NUMA *nad, NUMA *nas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern l_ok numaaJoin ( NUMAA *naad, NUMAA *naas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern NUMA * numaaFlattenToNuma ( NUMAA *naa ); +LEPT_DLL extern NUMA * numaErode ( NUMA *nas, l_int32 size ); +LEPT_DLL extern NUMA * numaDilate ( NUMA *nas, l_int32 size ); +LEPT_DLL extern NUMA * numaOpen ( NUMA *nas, l_int32 size ); +LEPT_DLL extern NUMA * numaClose ( NUMA *nas, l_int32 size ); +LEPT_DLL extern NUMA * numaTransform ( NUMA *nas, l_float32 shift, l_float32 scale ); +LEPT_DLL extern l_ok numaSimpleStats ( NUMA *na, l_int32 first, l_int32 last, l_float32 *pmean, l_float32 *pvar, l_float32 *prvar ); +LEPT_DLL extern l_ok numaWindowedStats ( NUMA *nas, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv ); +LEPT_DLL extern NUMA * numaWindowedMean ( NUMA *nas, l_int32 wc ); +LEPT_DLL extern NUMA * numaWindowedMeanSquare ( NUMA *nas, l_int32 wc ); +LEPT_DLL extern l_ok numaWindowedVariance ( NUMA *nam, NUMA *nams, NUMA **pnav, NUMA **pnarv ); +LEPT_DLL extern NUMA * numaWindowedMedian ( NUMA *nas, l_int32 halfwin ); +LEPT_DLL extern NUMA * numaConvertToInt ( NUMA *nas ); +LEPT_DLL extern NUMA * numaMakeHistogram ( NUMA *na, l_int32 maxbins, l_int32 *pbinsize, l_int32 *pbinstart ); +LEPT_DLL extern NUMA * numaMakeHistogramAuto ( NUMA *na, l_int32 maxbins ); +LEPT_DLL extern NUMA * numaMakeHistogramClipped ( NUMA *na, l_float32 binsize, l_float32 maxsize ); +LEPT_DLL extern NUMA * numaRebinHistogram ( NUMA *nas, l_int32 newsize ); +LEPT_DLL extern NUMA * numaNormalizeHistogram ( NUMA *nas, l_float32 tsum ); +LEPT_DLL extern l_ok numaGetStatsUsingHistogram ( NUMA *na, l_int32 maxbins, l_float32 *pmin, l_float32 *pmax, l_float32 *pmean, l_float32 *pvariance, l_float32 *pmedian, l_float32 rank, l_float32 *prval, NUMA **phisto ); +LEPT_DLL extern l_ok numaGetHistogramStats ( NUMA *nahisto, l_float32 startx, l_float32 deltax, l_float32 *pxmean, l_float32 *pxmedian, l_float32 *pxmode, l_float32 *pxvariance ); +LEPT_DLL extern l_ok numaGetHistogramStatsOnInterval ( NUMA *nahisto, l_float32 startx, l_float32 deltax, l_int32 ifirst, l_int32 ilast, l_float32 *pxmean, l_float32 *pxmedian, l_float32 *pxmode, l_float32 *pxvariance ); +LEPT_DLL extern l_ok numaMakeRankFromHistogram ( l_float32 startx, l_float32 deltax, NUMA *nasy, l_int32 npts, NUMA **pnax, NUMA **pnay ); +LEPT_DLL extern l_ok numaHistogramGetRankFromVal ( NUMA *na, l_float32 rval, l_float32 *prank ); +LEPT_DLL extern l_ok numaHistogramGetValFromRank ( NUMA *na, l_float32 rank, l_float32 *prval ); +LEPT_DLL extern l_ok numaDiscretizeRankAndIntensity ( NUMA *na, l_int32 nbins, NUMA **pnarbin, NUMA **pnam, NUMA **pnar, NUMA **pnabb ); +LEPT_DLL extern l_ok numaGetRankBinValues ( NUMA *na, l_int32 nbins, NUMA **pnarbin, NUMA **pnam ); +LEPT_DLL extern l_ok numaSplitDistribution ( NUMA *na, l_float32 scorefract, l_int32 *psplitindex, l_float32 *pave1, l_float32 *pave2, l_float32 *pnum1, l_float32 *pnum2, NUMA **pnascore ); +LEPT_DLL extern l_ok grayHistogramsToEMD ( NUMAA *naa1, NUMAA *naa2, NUMA **pnad ); +LEPT_DLL extern l_ok numaEarthMoverDistance ( NUMA *na1, NUMA *na2, l_float32 *pdist ); +LEPT_DLL extern l_ok grayInterHistogramStats ( NUMAA *naa, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv ); +LEPT_DLL extern NUMA * numaFindPeaks ( NUMA *nas, l_int32 nmax, l_float32 fract1, l_float32 fract2 ); +LEPT_DLL extern NUMA * numaFindExtrema ( NUMA *nas, l_float32 delta, NUMA **pnav ); +LEPT_DLL extern l_ok numaCountReversals ( NUMA *nas, l_float32 minreversal, l_int32 *pnr, l_float32 *prd ); +LEPT_DLL extern l_ok numaSelectCrossingThreshold ( NUMA *nax, NUMA *nay, l_float32 estthresh, l_float32 *pbestthresh ); +LEPT_DLL extern NUMA * numaCrossingsByThreshold ( NUMA *nax, NUMA *nay, l_float32 thresh ); +LEPT_DLL extern NUMA * numaCrossingsByPeaks ( NUMA *nax, NUMA *nay, l_float32 delta ); +LEPT_DLL extern l_ok numaEvalBestHaarParameters ( NUMA *nas, l_float32 relweight, l_int32 nwidth, l_int32 nshift, l_float32 minwidth, l_float32 maxwidth, l_float32 *pbestwidth, l_float32 *pbestshift, l_float32 *pbestscore ); +LEPT_DLL extern l_ok numaEvalHaarSum ( NUMA *nas, l_float32 width, l_float32 shift, l_float32 relweight, l_float32 *pscore ); +LEPT_DLL extern NUMA * genConstrainedNumaInRange ( l_int32 first, l_int32 last, l_int32 nmax, l_int32 use_pairs ); +LEPT_DLL extern l_ok pixGetRegionsBinary ( PIX *pixs, PIX **ppixhm, PIX **ppixtm, PIX **ppixtb, PIXA *pixadb ); +LEPT_DLL extern PIX * pixGenHalftoneMask ( PIX *pixs, PIX **ppixtext, l_int32 *phtfound, l_int32 debug ); +LEPT_DLL extern PIX * pixGenerateHalftoneMask ( PIX *pixs, PIX **ppixtext, l_int32 *phtfound, PIXA *pixadb ); +LEPT_DLL extern PIX * pixGenTextlineMask ( PIX *pixs, PIX **ppixvws, l_int32 *ptlfound, PIXA *pixadb ); +LEPT_DLL extern PIX * pixGenTextblockMask ( PIX *pixs, PIX *pixvws, PIXA *pixadb ); +LEPT_DLL extern BOX * pixFindPageForeground ( PIX *pixs, l_int32 threshold, l_int32 mindist, l_int32 erasedist, l_int32 showmorph, PIXAC *pixac ); +LEPT_DLL extern l_ok pixSplitIntoCharacters ( PIX *pixs, l_int32 minw, l_int32 minh, BOXA **pboxa, PIXA **ppixa, PIX **ppixdebug ); +LEPT_DLL extern BOXA * pixSplitComponentWithProfile ( PIX *pixs, l_int32 delta, l_int32 mindel, PIX **ppixdebug ); +LEPT_DLL extern PIXA * pixExtractTextlines ( PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 minw, l_int32 minh, l_int32 adjw, l_int32 adjh, PIXA *pixadb ); +LEPT_DLL extern PIXA * pixExtractRawTextlines ( PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 adjw, l_int32 adjh, PIXA *pixadb ); +LEPT_DLL extern l_ok pixCountTextColumns ( PIX *pixs, l_float32 deltafract, l_float32 peakfract, l_float32 clipfract, l_int32 *pncols, PIXA *pixadb ); +LEPT_DLL extern l_ok pixDecideIfText ( PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb ); +LEPT_DLL extern l_ok pixFindThreshFgExtent ( PIX *pixs, l_int32 thresh, l_int32 *ptop, l_int32 *pbot ); +LEPT_DLL extern l_ok pixDecideIfTable ( PIX *pixs, BOX *box, l_int32 orient, l_int32 *pscore, PIXA *pixadb ); +LEPT_DLL extern PIX * pixPrepare1bpp ( PIX *pixs, BOX *box, l_float32 cropfract, l_int32 outres ); +LEPT_DLL extern l_ok pixEstimateBackground ( PIX *pixs, l_int32 darkthresh, l_float32 edgecrop, l_int32 *pbg ); +LEPT_DLL extern l_ok pixFindLargeRectangles ( PIX *pixs, l_int32 polarity, l_int32 nrect, BOXA **pboxa, PIX **ppixdb ); +LEPT_DLL extern l_ok pixFindLargestRectangle ( PIX *pixs, l_int32 polarity, BOX **pbox, PIX **ppixdb ); +LEPT_DLL extern BOX * pixFindRectangleInCC ( PIX *pixs, BOX *boxs, l_float32 fract, l_int32 dir, l_int32 select, l_int32 debug ); +LEPT_DLL extern l_ok pixSetSelectCmap ( PIX *pixs, BOX *box, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixColorGrayRegionsCmap ( PIX *pixs, BOXA *boxa, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixColorGrayCmap ( PIX *pixs, BOX *box, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixColorGrayMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok addColorizedGrayToCmap ( PIXCMAP *cmap, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval, NUMA **pna ); +LEPT_DLL extern l_ok pixSetSelectMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixSetMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern char * parseForProtos ( const char *filein, const char *prestring ); +LEPT_DLL extern l_ok partifyFiles ( const char *dirname, const char *substr, l_int32 nparts, const char *outroot, const char *debugfile ); +LEPT_DLL extern l_ok partifyPixac ( PIXAC *pixac, l_int32 nparts, const char *outroot, PIXA *pixadb ); +LEPT_DLL extern BOXA * boxaGetWhiteblocks ( BOXA *boxas, BOX *box, l_int32 sortflag, l_int32 maxboxes, l_float32 maxoverlap, l_int32 maxperim, l_float32 fract, l_int32 maxpops ); +LEPT_DLL extern BOXA * boxaPruneSortedOnOverlap ( BOXA *boxas, l_float32 maxoverlap ); +LEPT_DLL extern l_ok convertFilesToPdf ( const char *dirname, const char *substr, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); +LEPT_DLL extern l_ok saConvertFilesToPdf ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); +LEPT_DLL extern l_ok saConvertFilesToPdfData ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok selectDefaultPdfEncoding ( PIX *pix, l_int32 *ptype ); +LEPT_DLL extern l_ok convertUnscaledFilesToPdf ( const char *dirname, const char *substr, const char *title, const char *fileout ); +LEPT_DLL extern l_ok saConvertUnscaledFilesToPdf ( SARRAY *sa, const char *title, const char *fileout ); +LEPT_DLL extern l_ok saConvertUnscaledFilesToPdfData ( SARRAY *sa, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok convertUnscaledToPdfData ( const char *fname, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixaConvertToPdf ( PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); +LEPT_DLL extern l_ok pixaConvertToPdfData ( PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok convertToPdf ( const char *filein, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok convertImageDataToPdf ( l_uint8 *imdata, size_t size, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok convertToPdfData ( const char *filein, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok convertImageDataToPdfData ( l_uint8 *imdata, size_t size, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok pixConvertToPdf ( PIX *pix, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok pixWriteStreamPdf ( FILE *fp, PIX *pix, l_int32 res, const char *title ); +LEPT_DLL extern l_ok pixWriteMemPdf ( l_uint8 **pdata, size_t *pnbytes, PIX *pix, l_int32 res, const char *title ); +LEPT_DLL extern l_ok convertSegmentedFilesToPdf ( const char *dirname, const char *substr, l_int32 res, l_int32 type, l_int32 thresh, BOXAA *baa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); +LEPT_DLL extern BOXAA * convertNumberedMasksToBoxaa ( const char *dirname, const char *substr, l_int32 numpre, l_int32 numpost ); +LEPT_DLL extern l_ok convertToPdfSegmented ( const char *filein, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); +LEPT_DLL extern l_ok pixConvertToPdfSegmented ( PIX *pixs, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); +LEPT_DLL extern l_ok convertToPdfDataSegmented ( const char *filein, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixConvertToPdfDataSegmented ( PIX *pixs, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok concatenatePdf ( const char *dirname, const char *substr, const char *fileout ); +LEPT_DLL extern l_ok saConcatenatePdf ( SARRAY *sa, const char *fileout ); +LEPT_DLL extern l_ok ptraConcatenatePdf ( L_PTRA *pa, const char *fileout ); +LEPT_DLL extern l_ok concatenatePdfToData ( const char *dirname, const char *substr, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok saConcatenatePdfToData ( SARRAY *sa, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixConvertToPdfData ( PIX *pix, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); +LEPT_DLL extern l_ok ptraConcatenatePdfToData ( L_PTRA *pa_data, SARRAY *sa, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok convertTiffMultipageToPdf ( const char *filein, const char *fileout ); +LEPT_DLL extern l_ok l_generateCIDataForPdf ( const char *fname, PIX *pix, l_int32 quality, L_COMP_DATA **pcid ); +LEPT_DLL extern L_COMP_DATA * l_generateFlateDataPdf ( const char *fname, PIX *pixs ); +LEPT_DLL extern L_COMP_DATA * l_generateJpegData ( const char *fname, l_int32 ascii85flag ); +LEPT_DLL extern L_COMP_DATA * l_generateJpegDataMem ( l_uint8 *data, size_t nbytes, l_int32 ascii85flag ); +LEPT_DLL extern l_ok l_generateCIData ( const char *fname, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); +LEPT_DLL extern l_ok pixGenerateCIData ( PIX *pixs, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); +LEPT_DLL extern L_COMP_DATA * l_generateFlateData ( const char *fname, l_int32 ascii85flag ); +LEPT_DLL extern L_COMP_DATA * l_generateG4Data ( const char *fname, l_int32 ascii85flag ); +LEPT_DLL extern l_ok cidConvertToPdfData ( L_COMP_DATA *cid, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern void l_CIDataDestroy ( L_COMP_DATA **pcid ); +LEPT_DLL extern void l_pdfSetG4ImageMask ( l_int32 flag ); +LEPT_DLL extern void l_pdfSetDateAndVersion ( l_int32 flag ); +LEPT_DLL extern void setPixMemoryManager ( alloc_fn allocator, dealloc_fn deallocator ); +LEPT_DLL extern PIX * pixCreate ( l_int32 width, l_int32 height, l_int32 depth ); +LEPT_DLL extern PIX * pixCreateNoInit ( l_int32 width, l_int32 height, l_int32 depth ); +LEPT_DLL extern PIX * pixCreateTemplate ( const PIX *pixs ); +LEPT_DLL extern PIX * pixCreateTemplateNoInit ( const PIX *pixs ); +LEPT_DLL extern PIX * pixCreateHeader ( l_int32 width, l_int32 height, l_int32 depth ); +LEPT_DLL extern PIX * pixClone ( PIX *pixs ); +LEPT_DLL extern void pixDestroy ( PIX **ppix ); +LEPT_DLL extern PIX * pixCopy ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_ok pixResizeImageData ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_ok pixCopyColormap ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_int32 pixSizesEqual ( const PIX *pix1, const PIX *pix2 ); +LEPT_DLL extern l_ok pixTransferAllData ( PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat ); +LEPT_DLL extern l_ok pixSwapAndDestroy ( PIX **ppixd, PIX **ppixs ); +LEPT_DLL extern l_int32 pixGetWidth ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetWidth ( PIX *pix, l_int32 width ); +LEPT_DLL extern l_int32 pixGetHeight ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetHeight ( PIX *pix, l_int32 height ); +LEPT_DLL extern l_int32 pixGetDepth ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetDepth ( PIX *pix, l_int32 depth ); +LEPT_DLL extern l_ok pixGetDimensions ( const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd ); +LEPT_DLL extern l_ok pixSetDimensions ( PIX *pix, l_int32 w, l_int32 h, l_int32 d ); +LEPT_DLL extern l_ok pixCopyDimensions ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_int32 pixGetSpp ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetSpp ( PIX *pix, l_int32 spp ); +LEPT_DLL extern l_ok pixCopySpp ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_int32 pixGetWpl ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetWpl ( PIX *pix, l_int32 wpl ); +LEPT_DLL extern l_int32 pixGetRefcount ( const PIX *pix ); +LEPT_DLL extern l_int32 pixChangeRefcount ( PIX *pix, l_int32 delta ); +LEPT_DLL extern l_int32 pixGetXRes ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetXRes ( PIX *pix, l_int32 res ); +LEPT_DLL extern l_int32 pixGetYRes ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetYRes ( PIX *pix, l_int32 res ); +LEPT_DLL extern l_ok pixGetResolution ( const PIX *pix, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok pixSetResolution ( PIX *pix, l_int32 xres, l_int32 yres ); +LEPT_DLL extern l_int32 pixCopyResolution ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_int32 pixScaleResolution ( PIX *pix, l_float32 xscale, l_float32 yscale ); +LEPT_DLL extern l_int32 pixGetInputFormat ( const PIX *pix ); +LEPT_DLL extern l_int32 pixSetInputFormat ( PIX *pix, l_int32 informat ); +LEPT_DLL extern l_int32 pixCopyInputFormat ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern l_int32 pixSetSpecial ( PIX *pix, l_int32 special ); +LEPT_DLL extern char * pixGetText ( PIX *pix ); +LEPT_DLL extern l_ok pixSetText ( PIX *pix, const char *textstring ); +LEPT_DLL extern l_ok pixAddText ( PIX *pix, const char *textstring ); +LEPT_DLL extern l_int32 pixCopyText ( PIX *pixd, const PIX *pixs ); +LEPT_DLL extern PIXCMAP * pixGetColormap ( PIX *pix ); +LEPT_DLL extern l_ok pixSetColormap ( PIX *pix, PIXCMAP *colormap ); +LEPT_DLL extern l_ok pixDestroyColormap ( PIX *pix ); +LEPT_DLL extern l_uint32 * pixGetData ( PIX *pix ); +LEPT_DLL extern l_int32 pixSetData ( PIX *pix, l_uint32 *data ); +LEPT_DLL extern l_uint32 * pixExtractData ( PIX *pixs ); +LEPT_DLL extern l_int32 pixFreeData ( PIX *pix ); +LEPT_DLL extern void ** pixGetLinePtrs ( PIX *pix, l_int32 *psize ); +LEPT_DLL extern l_ok pixPrintStreamInfo ( FILE *fp, const PIX *pix, const char *text ); +LEPT_DLL extern l_ok pixGetPixel ( PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval ); +LEPT_DLL extern l_ok pixSetPixel ( PIX *pix, l_int32 x, l_int32 y, l_uint32 val ); +LEPT_DLL extern l_ok pixGetRGBPixel ( PIX *pix, l_int32 x, l_int32 y, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern l_ok pixSetRGBPixel ( PIX *pix, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); +LEPT_DLL extern l_ok pixGetRandomPixel ( PIX *pix, l_uint32 *pval, l_int32 *px, l_int32 *py ); +LEPT_DLL extern l_ok pixClearPixel ( PIX *pix, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixFlipPixel ( PIX *pix, l_int32 x, l_int32 y ); +LEPT_DLL extern void setPixelLow ( l_uint32 *line, l_int32 x, l_int32 depth, l_uint32 val ); +LEPT_DLL extern l_ok pixGetBlackOrWhiteVal ( PIX *pixs, l_int32 op, l_uint32 *pval ); +LEPT_DLL extern l_ok pixClearAll ( PIX *pix ); +LEPT_DLL extern l_ok pixSetAll ( PIX *pix ); +LEPT_DLL extern l_ok pixSetAllGray ( PIX *pix, l_int32 grayval ); +LEPT_DLL extern l_ok pixSetAllArbitrary ( PIX *pix, l_uint32 val ); +LEPT_DLL extern l_ok pixSetBlackOrWhite ( PIX *pixs, l_int32 op ); +LEPT_DLL extern l_ok pixSetComponentArbitrary ( PIX *pix, l_int32 comp, l_int32 val ); +LEPT_DLL extern l_ok pixClearInRect ( PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixSetInRect ( PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixSetInRectArbitrary ( PIX *pix, BOX *box, l_uint32 val ); +LEPT_DLL extern l_ok pixBlendInRect ( PIX *pixs, BOX *box, l_uint32 val, l_float32 fract ); +LEPT_DLL extern l_ok pixSetPadBits ( PIX *pix, l_int32 val ); +LEPT_DLL extern l_ok pixSetPadBitsBand ( PIX *pix, l_int32 by, l_int32 bh, l_int32 val ); +LEPT_DLL extern l_ok pixSetOrClearBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op ); +LEPT_DLL extern l_ok pixSetBorderVal ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); +LEPT_DLL extern l_ok pixSetBorderRingVal ( PIX *pixs, l_int32 dist, l_uint32 val ); +LEPT_DLL extern l_ok pixSetMirroredBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixCopyBorder ( PIX *pixd, PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixAddBorder ( PIX *pixs, l_int32 npix, l_uint32 val ); +LEPT_DLL extern PIX * pixAddBlackOrWhiteBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op ); +LEPT_DLL extern PIX * pixAddBorderGeneral ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); +LEPT_DLL extern PIX * pixRemoveBorder ( PIX *pixs, l_int32 npix ); +LEPT_DLL extern PIX * pixRemoveBorderGeneral ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixRemoveBorderToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIX * pixAddMirroredBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixAddRepeatedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixAddMixedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern PIX * pixAddContinuedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); +LEPT_DLL extern l_ok pixShiftAndTransferAlpha ( PIX *pixd, PIX *pixs, l_float32 shiftx, l_float32 shifty ); +LEPT_DLL extern PIX * pixDisplayLayersRGBA ( PIX *pixs, l_uint32 val, l_int32 maxw ); +LEPT_DLL extern PIX * pixCreateRGBImage ( PIX *pixr, PIX *pixg, PIX *pixb ); +LEPT_DLL extern PIX * pixGetRGBComponent ( PIX *pixs, l_int32 comp ); +LEPT_DLL extern l_ok pixSetRGBComponent ( PIX *pixd, PIX *pixs, l_int32 comp ); +LEPT_DLL extern PIX * pixGetRGBComponentCmap ( PIX *pixs, l_int32 comp ); +LEPT_DLL extern l_ok pixCopyRGBComponent ( PIX *pixd, PIX *pixs, l_int32 comp ); +LEPT_DLL extern l_ok composeRGBPixel ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel ); +LEPT_DLL extern l_ok composeRGBAPixel ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval, l_uint32 *ppixel ); +LEPT_DLL extern void extractRGBValues ( l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); +LEPT_DLL extern void extractRGBAValues ( l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval ); +LEPT_DLL extern l_int32 extractMinMaxComponent ( l_uint32 pixel, l_int32 type ); +LEPT_DLL extern l_ok pixGetRGBLine ( PIX *pixs, l_int32 row, l_uint8 *bufr, l_uint8 *bufg, l_uint8 *bufb ); +LEPT_DLL extern PIX * pixEndianByteSwapNew ( PIX *pixs ); +LEPT_DLL extern l_ok pixEndianByteSwap ( PIX *pixs ); +LEPT_DLL extern l_int32 lineEndianByteSwap ( l_uint32 *datad, l_uint32 *datas, l_int32 wpl ); +LEPT_DLL extern PIX * pixEndianTwoByteSwapNew ( PIX *pixs ); +LEPT_DLL extern l_ok pixEndianTwoByteSwap ( PIX *pixs ); +LEPT_DLL extern l_ok pixGetRasterData ( PIX *pixs, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixAlphaIsOpaque ( PIX *pix, l_int32 *popaque ); +LEPT_DLL extern l_uint8 ** pixSetupByteProcessing ( PIX *pix, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok pixCleanupByteProcessing ( PIX *pix, l_uint8 **lineptrs ); +LEPT_DLL extern void l_setAlphaMaskBorder ( l_float32 val1, l_float32 val2 ); +LEPT_DLL extern l_ok pixSetMasked ( PIX *pixd, PIX *pixm, l_uint32 val ); +LEPT_DLL extern l_ok pixSetMaskedGeneral ( PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixCombineMasked ( PIX *pixd, PIX *pixs, PIX *pixm ); +LEPT_DLL extern l_ok pixCombineMaskedGeneral ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixPaintThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val ); +LEPT_DLL extern PIX * pixCopyWithBoxa ( PIX *pixs, BOXA *boxa, l_int32 background ); +LEPT_DLL extern l_ok pixPaintSelfThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_int32 searchdir, l_int32 mindist, l_int32 tilesize, l_int32 ntiles, l_int32 distblend ); +LEPT_DLL extern PIX * pixMakeMaskFromVal ( PIX *pixs, l_int32 val ); +LEPT_DLL extern PIX * pixMakeMaskFromLUT ( PIX *pixs, l_int32 *tab ); +LEPT_DLL extern PIX * pixMakeArbMaskFromRGB ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_float32 thresh ); +LEPT_DLL extern PIX * pixSetUnderTransparency ( PIX *pixs, l_uint32 val, l_int32 debug ); +LEPT_DLL extern PIX * pixMakeAlphaFromMask ( PIX *pixs, l_int32 dist, BOX **pbox ); +LEPT_DLL extern l_ok pixGetColorNearMaskBoundary ( PIX *pixs, PIX *pixm, BOX *box, l_int32 dist, l_uint32 *pval, l_int32 debug ); +LEPT_DLL extern PIX * pixInvert ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixOr ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixAnd ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixXor ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixSubtract ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern l_ok pixZero ( PIX *pix, l_int32 *pempty ); +LEPT_DLL extern l_ok pixForegroundFraction ( PIX *pix, l_float32 *pfract ); +LEPT_DLL extern NUMA * pixaCountPixels ( PIXA *pixa ); +LEPT_DLL extern l_ok pixCountPixels ( PIX *pixs, l_int32 *pcount, l_int32 *tab8 ); +LEPT_DLL extern l_ok pixCountPixelsInRect ( PIX *pixs, BOX *box, l_int32 *pcount, l_int32 *tab8 ); +LEPT_DLL extern NUMA * pixCountByRow ( PIX *pix, BOX *box ); +LEPT_DLL extern NUMA * pixCountByColumn ( PIX *pix, BOX *box ); +LEPT_DLL extern NUMA * pixCountPixelsByRow ( PIX *pix, l_int32 *tab8 ); +LEPT_DLL extern NUMA * pixCountPixelsByColumn ( PIX *pix ); +LEPT_DLL extern l_ok pixCountPixelsInRow ( PIX *pix, l_int32 row, l_int32 *pcount, l_int32 *tab8 ); +LEPT_DLL extern NUMA * pixGetMomentByColumn ( PIX *pix, l_int32 order ); +LEPT_DLL extern l_ok pixThresholdPixelSum ( PIX *pix, l_int32 thresh, l_int32 *pabove, l_int32 *tab8 ); +LEPT_DLL extern l_int32 * makePixelSumTab8 ( void ); +LEPT_DLL extern l_int32 * makePixelCentroidTab8 ( void ); +LEPT_DLL extern NUMA * pixAverageByRow ( PIX *pix, BOX *box, l_int32 type ); +LEPT_DLL extern NUMA * pixAverageByColumn ( PIX *pix, BOX *box, l_int32 type ); +LEPT_DLL extern l_ok pixAverageInRect ( PIX *pix, BOX *box, l_float32 *pave ); +LEPT_DLL extern NUMA * pixVarianceByRow ( PIX *pix, BOX *box ); +LEPT_DLL extern NUMA * pixVarianceByColumn ( PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixVarianceInRect ( PIX *pix, BOX *box, l_float32 *prootvar ); +LEPT_DLL extern NUMA * pixAbsDiffByRow ( PIX *pix, BOX *box ); +LEPT_DLL extern NUMA * pixAbsDiffByColumn ( PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixAbsDiffInRect ( PIX *pix, BOX *box, l_int32 dir, l_float32 *pabsdiff ); +LEPT_DLL extern l_ok pixAbsDiffOnLine ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_float32 *pabsdiff ); +LEPT_DLL extern l_int32 pixCountArbInRect ( PIX *pixs, BOX *box, l_int32 val, l_int32 factor, l_int32 *pcount ); +LEPT_DLL extern PIX * pixMirroredTiling ( PIX *pixs, l_int32 w, l_int32 h ); +LEPT_DLL extern l_ok pixFindRepCloseTile ( PIX *pixs, BOX *box, l_int32 searchdir, l_int32 mindist, l_int32 tsize, l_int32 ntiles, BOX **pboxtile, l_int32 debug ); +LEPT_DLL extern NUMA * pixGetGrayHistogram ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern NUMA * pixGetGrayHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor ); +LEPT_DLL extern NUMA * pixGetGrayHistogramInRect ( PIX *pixs, BOX *box, l_int32 factor ); +LEPT_DLL extern NUMAA * pixGetGrayHistogramTiled ( PIX *pixs, l_int32 factor, l_int32 nx, l_int32 ny ); +LEPT_DLL extern l_ok pixGetColorHistogram ( PIX *pixs, l_int32 factor, NUMA **pnar, NUMA **pnag, NUMA **pnab ); +LEPT_DLL extern l_ok pixGetColorHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, NUMA **pnar, NUMA **pnag, NUMA **pnab ); +LEPT_DLL extern NUMA * pixGetCmapHistogram ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern NUMA * pixGetCmapHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor ); +LEPT_DLL extern NUMA * pixGetCmapHistogramInRect ( PIX *pixs, BOX *box, l_int32 factor ); +LEPT_DLL extern l_int32 pixCountRGBColors ( PIX *pixs ); +LEPT_DLL extern L_AMAP * pixGetColorAmapHistogram ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern l_int32 amapGetCountForColor ( L_AMAP *amap, l_uint32 val ); +LEPT_DLL extern l_ok pixGetRankValue ( PIX *pixs, l_int32 factor, l_float32 rank, l_uint32 *pvalue ); +LEPT_DLL extern l_ok pixGetRankValueMaskedRGB ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_float32 rank, l_float32 *prval, l_float32 *pgval, l_float32 *pbval ); +LEPT_DLL extern l_ok pixGetRankValueMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_float32 rank, l_float32 *pval, NUMA **pna ); +LEPT_DLL extern l_ok pixGetPixelAverage ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_uint32 *pval ); +LEPT_DLL extern l_ok pixGetPixelStats ( PIX *pixs, l_int32 factor, l_int32 type, l_uint32 *pvalue ); +LEPT_DLL extern l_ok pixGetAverageMaskedRGB ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *prval, l_float32 *pgval, l_float32 *pbval ); +LEPT_DLL extern l_ok pixGetAverageMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *pval ); +LEPT_DLL extern l_ok pixGetAverageTiledRGB ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixr, PIX **ppixg, PIX **ppixb ); +LEPT_DLL extern PIX * pixGetAverageTiled ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type ); +LEPT_DLL extern l_int32 pixRowStats ( PIX *pixs, BOX *box, NUMA **pnamean, NUMA **pnamedian, NUMA **pnamode, NUMA **pnamodecount, NUMA **pnavar, NUMA **pnarootvar ); +LEPT_DLL extern l_int32 pixColumnStats ( PIX *pixs, BOX *box, NUMA **pnamean, NUMA **pnamedian, NUMA **pnamode, NUMA **pnamodecount, NUMA **pnavar, NUMA **pnarootvar ); +LEPT_DLL extern l_ok pixGetRangeValues ( PIX *pixs, l_int32 factor, l_int32 color, l_int32 *pminval, l_int32 *pmaxval ); +LEPT_DLL extern l_ok pixGetExtremeValue ( PIX *pixs, l_int32 factor, l_int32 type, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *pgrayval ); +LEPT_DLL extern l_ok pixGetMaxValueInRect ( PIX *pixs, BOX *box, l_uint32 *pmaxval, l_int32 *pxmax, l_int32 *pymax ); +LEPT_DLL extern l_ok pixGetBinnedComponentRange ( PIX *pixs, l_int32 nbins, l_int32 factor, l_int32 color, l_int32 *pminval, l_int32 *pmaxval, l_uint32 **pcarray, l_int32 fontsize ); +LEPT_DLL extern l_ok pixGetRankColorArray ( PIX *pixs, l_int32 nbins, l_int32 type, l_int32 factor, l_uint32 **pcarray, l_int32 debugflag, l_int32 fontsize ); +LEPT_DLL extern l_ok pixGetBinnedColor ( PIX *pixs, PIX *pixg, l_int32 factor, l_int32 nbins, NUMA *nalut, l_uint32 **pcarray, l_int32 debugflag ); +LEPT_DLL extern PIX * pixDisplayColorArray ( l_uint32 *carray, l_int32 ncolors, l_int32 side, l_int32 ncols, l_int32 fontsize ); +LEPT_DLL extern PIX * pixRankBinByStrip ( PIX *pixs, l_int32 direction, l_int32 size, l_int32 nbins, l_int32 type ); +LEPT_DLL extern PIX * pixaGetAlignedStats ( PIXA *pixa, l_int32 type, l_int32 nbins, l_int32 thresh ); +LEPT_DLL extern l_ok pixaExtractColumnFromEachPix ( PIXA *pixa, l_int32 col, PIX *pixd ); +LEPT_DLL extern l_ok pixGetRowStats ( PIX *pixs, l_int32 type, l_int32 nbins, l_int32 thresh, l_float32 *colvect ); +LEPT_DLL extern l_ok pixGetColumnStats ( PIX *pixs, l_int32 type, l_int32 nbins, l_int32 thresh, l_float32 *rowvect ); +LEPT_DLL extern l_ok pixSetPixelColumn ( PIX *pix, l_int32 col, l_float32 *colvect ); +LEPT_DLL extern l_ok pixThresholdForFgBg ( PIX *pixs, l_int32 factor, l_int32 thresh, l_int32 *pfgval, l_int32 *pbgval ); +LEPT_DLL extern l_ok pixSplitDistributionFgBg ( PIX *pixs, l_float32 scorefract, l_int32 factor, l_int32 *pthresh, l_int32 *pfgval, l_int32 *pbgval, PIX **ppixdb ); +LEPT_DLL extern l_ok pixaFindDimensions ( PIXA *pixa, NUMA **pnaw, NUMA **pnah ); +LEPT_DLL extern l_ok pixFindAreaPerimRatio ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); +LEPT_DLL extern NUMA * pixaFindPerimToAreaRatio ( PIXA *pixa ); +LEPT_DLL extern l_ok pixFindPerimToAreaRatio ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); +LEPT_DLL extern NUMA * pixaFindPerimSizeRatio ( PIXA *pixa ); +LEPT_DLL extern l_ok pixFindPerimSizeRatio ( PIX *pixs, l_int32 *tab, l_float32 *pratio ); +LEPT_DLL extern NUMA * pixaFindAreaFraction ( PIXA *pixa ); +LEPT_DLL extern l_ok pixFindAreaFraction ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); +LEPT_DLL extern NUMA * pixaFindAreaFractionMasked ( PIXA *pixa, PIX *pixm, l_int32 debug ); +LEPT_DLL extern l_ok pixFindAreaFractionMasked ( PIX *pixs, BOX *box, PIX *pixm, l_int32 *tab, l_float32 *pfract ); +LEPT_DLL extern NUMA * pixaFindWidthHeightRatio ( PIXA *pixa ); +LEPT_DLL extern NUMA * pixaFindWidthHeightProduct ( PIXA *pixa ); +LEPT_DLL extern l_ok pixFindOverlapFraction ( PIX *pixs1, PIX *pixs2, l_int32 x2, l_int32 y2, l_int32 *tab, l_float32 *pratio, l_int32 *pnoverlap ); +LEPT_DLL extern BOXA * pixFindRectangleComps ( PIX *pixs, l_int32 dist, l_int32 minw, l_int32 minh ); +LEPT_DLL extern l_ok pixConformsToRectangle ( PIX *pixs, BOX *box, l_int32 dist, l_int32 *pconforms ); +LEPT_DLL extern PIXA * pixClipRectangles ( PIX *pixs, BOXA *boxa ); +LEPT_DLL extern PIX * pixClipRectangle ( PIX *pixs, BOX *box, BOX **pboxc ); +LEPT_DLL extern PIX * pixClipMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_uint32 outval ); +LEPT_DLL extern l_ok pixCropToMatch ( PIX *pixs1, PIX *pixs2, PIX **ppixd1, PIX **ppixd2 ); +LEPT_DLL extern PIX * pixCropToSize ( PIX *pixs, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixResizeToMatch ( PIX *pixs, PIX *pixt, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixSelectComponentBySize ( PIX *pixs, l_int32 rankorder, l_int32 type, l_int32 connectivity, BOX **pbox ); +LEPT_DLL extern PIX * pixFilterComponentBySize ( PIX *pixs, l_int32 rankorder, l_int32 type, l_int32 connectivity, BOX **pbox ); +LEPT_DLL extern PIX * pixMakeSymmetricMask ( l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type ); +LEPT_DLL extern PIX * pixMakeFrameMask ( l_int32 w, l_int32 h, l_float32 hf1, l_float32 hf2, l_float32 vf1, l_float32 vf2 ); +LEPT_DLL extern PIX * pixMakeCoveringOfRectangles ( PIX *pixs, l_int32 maxiters ); +LEPT_DLL extern l_ok pixFractionFgInMask ( PIX *pix1, PIX *pix2, l_float32 *pfract ); +LEPT_DLL extern l_ok pixClipToForeground ( PIX *pixs, PIX **ppixd, BOX **pbox ); +LEPT_DLL extern l_ok pixTestClipToForeground ( PIX *pixs, l_int32 *pcanclip ); +LEPT_DLL extern l_ok pixClipBoxToForeground ( PIX *pixs, BOX *boxs, PIX **ppixd, BOX **pboxd ); +LEPT_DLL extern l_ok pixScanForForeground ( PIX *pixs, BOX *box, l_int32 scanflag, l_int32 *ploc ); +LEPT_DLL extern l_ok pixClipBoxToEdges ( PIX *pixs, BOX *boxs, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, PIX **ppixd, BOX **pboxd ); +LEPT_DLL extern l_ok pixScanForEdge ( PIX *pixs, BOX *box, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, l_int32 scanflag, l_int32 *ploc ); +LEPT_DLL extern NUMA * pixExtractOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor ); +LEPT_DLL extern l_float32 pixAverageOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor ); +LEPT_DLL extern NUMA * pixAverageIntensityProfile ( PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 factor1, l_int32 factor2 ); +LEPT_DLL extern NUMA * pixReversalProfile ( PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 minreversal, l_int32 factor1, l_int32 factor2 ); +LEPT_DLL extern l_ok pixWindowedVarianceOnLine ( PIX *pixs, l_int32 dir, l_int32 loc, l_int32 c1, l_int32 c2, l_int32 size, NUMA **pnad ); +LEPT_DLL extern l_ok pixMinMaxNearLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 dist, l_int32 direction, NUMA **pnamin, NUMA **pnamax, l_float32 *pminave, l_float32 *pmaxave ); +LEPT_DLL extern PIX * pixRankRowTransform ( PIX *pixs ); +LEPT_DLL extern PIX * pixRankColumnTransform ( PIX *pixs ); +LEPT_DLL extern PIXA * pixaCreate ( l_int32 n ); +LEPT_DLL extern PIXA * pixaCreateFromPix ( PIX *pixs, l_int32 n, l_int32 cellw, l_int32 cellh ); +LEPT_DLL extern PIXA * pixaCreateFromBoxa ( PIX *pixs, BOXA *boxa, l_int32 start, l_int32 num, l_int32 *pcropwarn ); +LEPT_DLL extern PIXA * pixaSplitPix ( PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor ); +LEPT_DLL extern void pixaDestroy ( PIXA **ppixa ); +LEPT_DLL extern PIXA * pixaCopy ( PIXA *pixa, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaAddPix ( PIXA *pixa, PIX *pix, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaAddBox ( PIXA *pixa, BOX *box, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaExtendArrayToSize ( PIXA *pixa, l_int32 size ); +LEPT_DLL extern l_int32 pixaGetCount ( PIXA *pixa ); +LEPT_DLL extern l_ok pixaChangeRefcount ( PIXA *pixa, l_int32 delta ); +LEPT_DLL extern PIX * pixaGetPix ( PIXA *pixa, l_int32 index, l_int32 accesstype ); +LEPT_DLL extern l_ok pixaGetPixDimensions ( PIXA *pixa, l_int32 index, l_int32 *pw, l_int32 *ph, l_int32 *pd ); +LEPT_DLL extern BOXA * pixaGetBoxa ( PIXA *pixa, l_int32 accesstype ); +LEPT_DLL extern l_int32 pixaGetBoxaCount ( PIXA *pixa ); +LEPT_DLL extern BOX * pixaGetBox ( PIXA *pixa, l_int32 index, l_int32 accesstype ); +LEPT_DLL extern l_ok pixaGetBoxGeometry ( PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_ok pixaSetBoxa ( PIXA *pixa, BOXA *boxa, l_int32 accesstype ); +LEPT_DLL extern PIX ** pixaGetPixArray ( PIXA *pixa ); +LEPT_DLL extern l_ok pixaVerifyDepth ( PIXA *pixa, l_int32 *psame, l_int32 *pmaxd ); +LEPT_DLL extern l_ok pixaVerifyDimensions ( PIXA *pixa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern l_ok pixaIsFull ( PIXA *pixa, l_int32 *pfullpa, l_int32 *pfullba ); +LEPT_DLL extern l_ok pixaCountText ( PIXA *pixa, l_int32 *pntext ); +LEPT_DLL extern l_ok pixaSetText ( PIXA *pixa, const char *text, SARRAY *sa ); +LEPT_DLL extern void *** pixaGetLinePtrs ( PIXA *pixa, l_int32 *psize ); +LEPT_DLL extern l_ok pixaWriteStreamInfo ( FILE *fp, PIXA *pixa ); +LEPT_DLL extern l_ok pixaReplacePix ( PIXA *pixa, l_int32 index, PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixaInsertPix ( PIXA *pixa, l_int32 index, PIX *pixs, BOX *box ); +LEPT_DLL extern l_ok pixaRemovePix ( PIXA *pixa, l_int32 index ); +LEPT_DLL extern l_ok pixaRemovePixAndSave ( PIXA *pixa, l_int32 index, PIX **ppix, BOX **pbox ); +LEPT_DLL extern l_ok pixaRemoveSelected ( PIXA *pixa, NUMA *naindex ); +LEPT_DLL extern l_ok pixaInitFull ( PIXA *pixa, PIX *pix, BOX *box ); +LEPT_DLL extern l_ok pixaClear ( PIXA *pixa ); +LEPT_DLL extern l_ok pixaJoin ( PIXA *pixad, PIXA *pixas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern PIXA * pixaInterleave ( PIXA *pixa1, PIXA *pixa2, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaaJoin ( PIXAA *paad, PIXAA *paas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern PIXAA * pixaaCreate ( l_int32 n ); +LEPT_DLL extern PIXAA * pixaaCreateFromPixa ( PIXA *pixa, l_int32 n, l_int32 type, l_int32 copyflag ); +LEPT_DLL extern void pixaaDestroy ( PIXAA **ppaa ); +LEPT_DLL extern l_ok pixaaAddPixa ( PIXAA *paa, PIXA *pixa, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaaExtendArray ( PIXAA *paa ); +LEPT_DLL extern l_ok pixaaAddPix ( PIXAA *paa, l_int32 index, PIX *pix, BOX *box, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaaAddBox ( PIXAA *paa, BOX *box, l_int32 copyflag ); +LEPT_DLL extern l_int32 pixaaGetCount ( PIXAA *paa, NUMA **pna ); +LEPT_DLL extern PIXA * pixaaGetPixa ( PIXAA *paa, l_int32 index, l_int32 accesstype ); +LEPT_DLL extern BOXA * pixaaGetBoxa ( PIXAA *paa, l_int32 accesstype ); +LEPT_DLL extern PIX * pixaaGetPix ( PIXAA *paa, l_int32 index, l_int32 ipix, l_int32 accessflag ); +LEPT_DLL extern l_ok pixaaVerifyDepth ( PIXAA *paa, l_int32 *psame, l_int32 *pmaxd ); +LEPT_DLL extern l_ok pixaaVerifyDimensions ( PIXAA *paa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern l_int32 pixaaIsFull ( PIXAA *paa, l_int32 *pfull ); +LEPT_DLL extern l_ok pixaaInitFull ( PIXAA *paa, PIXA *pixa ); +LEPT_DLL extern l_ok pixaaReplacePixa ( PIXAA *paa, l_int32 index, PIXA *pixa ); +LEPT_DLL extern l_ok pixaaClear ( PIXAA *paa ); +LEPT_DLL extern l_ok pixaaTruncate ( PIXAA *paa ); +LEPT_DLL extern PIXA * pixaRead ( const char *filename ); +LEPT_DLL extern PIXA * pixaReadStream ( FILE *fp ); +LEPT_DLL extern PIXA * pixaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixaWriteDebug ( const char *fname, PIXA *pixa ); +LEPT_DLL extern l_ok pixaWrite ( const char *filename, PIXA *pixa ); +LEPT_DLL extern l_ok pixaWriteStream ( FILE *fp, PIXA *pixa ); +LEPT_DLL extern l_ok pixaWriteMem ( l_uint8 **pdata, size_t *psize, PIXA *pixa ); +LEPT_DLL extern PIXA * pixaReadBoth ( const char *filename ); +LEPT_DLL extern PIXAA * pixaaReadFromFiles ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); +LEPT_DLL extern PIXAA * pixaaRead ( const char *filename ); +LEPT_DLL extern PIXAA * pixaaReadStream ( FILE *fp ); +LEPT_DLL extern PIXAA * pixaaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixaaWrite ( const char *filename, PIXAA *paa ); +LEPT_DLL extern l_ok pixaaWriteStream ( FILE *fp, PIXAA *paa ); +LEPT_DLL extern l_ok pixaaWriteMem ( l_uint8 **pdata, size_t *psize, PIXAA *paa ); +LEPT_DLL extern PIXACC * pixaccCreate ( l_int32 w, l_int32 h, l_int32 negflag ); +LEPT_DLL extern PIXACC * pixaccCreateFromPix ( PIX *pix, l_int32 negflag ); +LEPT_DLL extern void pixaccDestroy ( PIXACC **ppixacc ); +LEPT_DLL extern PIX * pixaccFinal ( PIXACC *pixacc, l_int32 outdepth ); +LEPT_DLL extern PIX * pixaccGetPix ( PIXACC *pixacc ); +LEPT_DLL extern l_int32 pixaccGetOffset ( PIXACC *pixacc ); +LEPT_DLL extern l_ok pixaccAdd ( PIXACC *pixacc, PIX *pix ); +LEPT_DLL extern l_ok pixaccSubtract ( PIXACC *pixacc, PIX *pix ); +LEPT_DLL extern l_ok pixaccMultConst ( PIXACC *pixacc, l_float32 factor ); +LEPT_DLL extern l_ok pixaccMultConstAccumulate ( PIXACC *pixacc, PIX *pix, l_float32 factor ); +LEPT_DLL extern PIX * pixSelectBySize ( PIX *pixs, l_int32 width, l_int32 height, l_int32 connectivity, l_int32 type, l_int32 relation, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectBySize ( PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged ); +LEPT_DLL extern NUMA * pixaMakeSizeIndicator ( PIXA *pixa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation ); +LEPT_DLL extern PIX * pixSelectByPerimToAreaRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectByPerimToAreaRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIX * pixSelectByPerimSizeRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectByPerimSizeRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIX * pixSelectByAreaFraction ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectByAreaFraction ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIX * pixSelectByWidthHeightRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectByWidthHeightRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectByNumConnComp ( PIXA *pixas, l_int32 nmin, l_int32 nmax, l_int32 connectivity, l_int32 *pchanged ); +LEPT_DLL extern PIXA * pixaSelectWithIndicator ( PIXA *pixas, NUMA *na, l_int32 *pchanged ); +LEPT_DLL extern l_ok pixRemoveWithIndicator ( PIX *pixs, PIXA *pixa, NUMA *na ); +LEPT_DLL extern l_ok pixAddWithIndicator ( PIX *pixs, PIXA *pixa, NUMA *na ); +LEPT_DLL extern PIXA * pixaSelectWithString ( PIXA *pixas, const char *str, l_int32 *perror ); +LEPT_DLL extern PIX * pixaRenderComponent ( PIX *pixs, PIXA *pixa, l_int32 index ); +LEPT_DLL extern PIXA * pixaSort ( PIXA *pixas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex, l_int32 copyflag ); +LEPT_DLL extern PIXA * pixaBinSort ( PIXA *pixas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex, l_int32 copyflag ); +LEPT_DLL extern PIXA * pixaSortByIndex ( PIXA *pixas, NUMA *naindex, l_int32 copyflag ); +LEPT_DLL extern PIXAA * pixaSort2dByIndex ( PIXA *pixas, NUMAA *naa, l_int32 copyflag ); +LEPT_DLL extern PIXA * pixaSelectRange ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 copyflag ); +LEPT_DLL extern PIXAA * pixaaSelectRange ( PIXAA *paas, l_int32 first, l_int32 last, l_int32 copyflag ); +LEPT_DLL extern PIXAA * pixaaScaleToSize ( PIXAA *paas, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIXAA * pixaaScaleToSizeVar ( PIXAA *paas, NUMA *nawd, NUMA *nahd ); +LEPT_DLL extern PIXA * pixaScaleToSize ( PIXA *pixas, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIXA * pixaScaleToSizeRel ( PIXA *pixas, l_int32 delw, l_int32 delh ); +LEPT_DLL extern PIXA * pixaScale ( PIXA *pixas, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIXA * pixaScaleBySampling ( PIXA *pixas, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIXA * pixaRotate ( PIXA *pixas, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height ); +LEPT_DLL extern PIXA * pixaRotateOrth ( PIXA *pixas, l_int32 rotation ); +LEPT_DLL extern PIXA * pixaTranslate ( PIXA *pixas, l_int32 hshift, l_int32 vshift, l_int32 incolor ); +LEPT_DLL extern PIXA * pixaAddBorderGeneral ( PIXA *pixad, PIXA *pixas, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); +LEPT_DLL extern PIXA * pixaaFlattenToPixa ( PIXAA *paa, NUMA **pnaindex, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaaSizeRange ( PIXAA *paa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern l_ok pixaSizeRange ( PIXA *pixa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); +LEPT_DLL extern PIXA * pixaClipToPix ( PIXA *pixas, PIX *pixs ); +LEPT_DLL extern l_ok pixaClipToForeground ( PIXA *pixas, PIXA **ppixad, BOXA **pboxa ); +LEPT_DLL extern l_ok pixaGetRenderingDepth ( PIXA *pixa, l_int32 *pdepth ); +LEPT_DLL extern l_ok pixaHasColor ( PIXA *pixa, l_int32 *phascolor ); +LEPT_DLL extern l_ok pixaAnyColormaps ( PIXA *pixa, l_int32 *phascmap ); +LEPT_DLL extern l_ok pixaGetDepthInfo ( PIXA *pixa, l_int32 *pmaxdepth, l_int32 *psame ); +LEPT_DLL extern PIXA * pixaConvertToSameDepth ( PIXA *pixas ); +LEPT_DLL extern l_ok pixaEqual ( PIXA *pixa1, PIXA *pixa2, l_int32 maxdist, NUMA **pnaindex, l_int32 *psame ); +LEPT_DLL extern l_ok pixaSetFullSizeBoxa ( PIXA *pixa ); +LEPT_DLL extern PIX * pixaDisplay ( PIXA *pixa, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixaDisplayOnColor ( PIXA *pixa, l_int32 w, l_int32 h, l_uint32 bgcolor ); +LEPT_DLL extern PIX * pixaDisplayRandomCmap ( PIXA *pixa, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixaDisplayLinearly ( PIXA *pixas, l_int32 direction, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border, BOXA **pboxa ); +LEPT_DLL extern PIX * pixaDisplayOnLattice ( PIXA *pixa, l_int32 cellw, l_int32 cellh, l_int32 *pncols, BOXA **pboxa ); +LEPT_DLL extern PIX * pixaDisplayUnsplit ( PIXA *pixa, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor ); +LEPT_DLL extern PIX * pixaDisplayTiled ( PIXA *pixa, l_int32 maxwidth, l_int32 background, l_int32 spacing ); +LEPT_DLL extern PIX * pixaDisplayTiledInRows ( PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border ); +LEPT_DLL extern PIX * pixaDisplayTiledInColumns ( PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border ); +LEPT_DLL extern PIX * pixaDisplayTiledAndScaled ( PIXA *pixa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); +LEPT_DLL extern PIX * pixaDisplayTiledWithText ( PIXA *pixa, l_int32 maxwidth, l_float32 scalefactor, l_int32 spacing, l_int32 border, l_int32 fontsize, l_uint32 textcolor ); +LEPT_DLL extern PIX * pixaDisplayTiledByIndex ( PIXA *pixa, NUMA *na, l_int32 width, l_int32 spacing, l_int32 border, l_int32 fontsize, l_uint32 textcolor ); +LEPT_DLL extern PIX * pixaaDisplay ( PIXAA *paa, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixaaDisplayByPixa ( PIXAA *paa, l_int32 xspace, l_int32 yspace, l_int32 maxw ); +LEPT_DLL extern PIXA * pixaaDisplayTiledAndScaled ( PIXAA *paa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); +LEPT_DLL extern PIXA * pixaConvertTo1 ( PIXA *pixas, l_int32 thresh ); +LEPT_DLL extern PIXA * pixaConvertTo8 ( PIXA *pixas, l_int32 cmapflag ); +LEPT_DLL extern PIXA * pixaConvertTo8Colormap ( PIXA *pixas, l_int32 dither ); +LEPT_DLL extern PIXA * pixaConvertTo32 ( PIXA *pixas ); +LEPT_DLL extern PIXA * pixaConstrainedSelect ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 nmax, l_int32 use_pairs, l_int32 copyflag ); +LEPT_DLL extern l_ok pixaSelectToPdf ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, l_uint32 color, l_int32 fontsize, const char *fileout ); +LEPT_DLL extern PIXA * pixaMakeFromTiledPixa ( PIXA *pixas, l_int32 w, l_int32 h, l_int32 nsamp ); +LEPT_DLL extern PIXA * pixaMakeFromTiledPix ( PIX *pixs, l_int32 w, l_int32 h, l_int32 start, l_int32 num, BOXA *boxa ); +LEPT_DLL extern l_ok pixGetTileCount ( PIX *pix, l_int32 *pn ); +LEPT_DLL extern PIXA * pixaDisplayMultiTiled ( PIXA *pixas, l_int32 nx, l_int32 ny, l_int32 maxw, l_int32 maxh, l_float32 scalefactor, l_int32 spacing, l_int32 border ); +LEPT_DLL extern l_ok pixaSplitIntoFiles ( PIXA *pixas, l_int32 nsplit, l_float32 scale, l_int32 outwidth, l_int32 write_pixa, l_int32 write_pix, l_int32 write_pdf ); +LEPT_DLL extern l_ok convertToNUpFiles ( const char *dir, const char *substr, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize, const char *outdir ); +LEPT_DLL extern PIXA * convertToNUpPixa ( const char *dir, const char *substr, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize ); +LEPT_DLL extern PIXA * pixaConvertToNUpPixa ( PIXA *pixas, SARRAY *sa, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize ); +LEPT_DLL extern l_ok pixaCompareInPdf ( PIXA *pixa1, PIXA *pixa2, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize, const char *fileout ); +LEPT_DLL extern l_ok pmsCreate ( size_t minsize, size_t smallest, NUMA *numalloc, const char *logfile ); +LEPT_DLL extern void pmsDestroy ( ); +LEPT_DLL extern void * pmsCustomAlloc ( size_t nbytes ); +LEPT_DLL extern void pmsCustomDealloc ( void *data ); +LEPT_DLL extern void * pmsGetAlloc ( size_t nbytes ); +LEPT_DLL extern l_ok pmsGetLevelForAlloc ( size_t nbytes, l_int32 *plevel ); +LEPT_DLL extern l_ok pmsGetLevelForDealloc ( void *data, l_int32 *plevel ); +LEPT_DLL extern void pmsLogInfo ( ); +LEPT_DLL extern l_ok pixAddConstantGray ( PIX *pixs, l_int32 val ); +LEPT_DLL extern l_ok pixMultConstantGray ( PIX *pixs, l_float32 val ); +LEPT_DLL extern PIX * pixAddGray ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixSubtractGray ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixThresholdToValue ( PIX *pixd, PIX *pixs, l_int32 threshval, l_int32 setval ); +LEPT_DLL extern PIX * pixInitAccumulate ( l_int32 w, l_int32 h, l_uint32 offset ); +LEPT_DLL extern PIX * pixFinalAccumulate ( PIX *pixs, l_uint32 offset, l_int32 depth ); +LEPT_DLL extern PIX * pixFinalAccumulateThreshold ( PIX *pixs, l_uint32 offset, l_uint32 threshold ); +LEPT_DLL extern l_ok pixAccumulate ( PIX *pixd, PIX *pixs, l_int32 op ); +LEPT_DLL extern l_ok pixMultConstAccumulate ( PIX *pixs, l_float32 factor, l_uint32 offset ); +LEPT_DLL extern PIX * pixAbsDifference ( PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixAddRGB ( PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern PIX * pixMinOrMax ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 type ); +LEPT_DLL extern PIX * pixMaxDynamicRange ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixMaxDynamicRangeRGB ( PIX *pixs, l_int32 type ); +LEPT_DLL extern l_uint32 linearScaleRGBVal ( l_uint32 sval, l_float32 factor ); +LEPT_DLL extern l_uint32 logScaleRGBVal ( l_uint32 sval, l_float32 *tab, l_float32 factor ); +LEPT_DLL extern l_float32 * makeLogBase2Tab ( void ); +LEPT_DLL extern l_float32 getLogBase2 ( l_int32 val, l_float32 *logtab ); +LEPT_DLL extern PIXC * pixcompCreateFromPix ( PIX *pix, l_int32 comptype ); +LEPT_DLL extern PIXC * pixcompCreateFromString ( l_uint8 *data, size_t size, l_int32 copyflag ); +LEPT_DLL extern PIXC * pixcompCreateFromFile ( const char *filename, l_int32 comptype ); +LEPT_DLL extern void pixcompDestroy ( PIXC **ppixc ); +LEPT_DLL extern PIXC * pixcompCopy ( PIXC *pixcs ); +LEPT_DLL extern l_ok pixcompGetDimensions ( PIXC *pixc, l_int32 *pw, l_int32 *ph, l_int32 *pd ); +LEPT_DLL extern l_ok pixcompGetParameters ( PIXC *pixc, l_int32 *pxres, l_int32 *pyres, l_int32 *pcomptype, l_int32 *pcmapflag ); +LEPT_DLL extern l_ok pixcompDetermineFormat ( l_int32 comptype, l_int32 d, l_int32 cmapflag, l_int32 *pformat ); +LEPT_DLL extern PIX * pixCreateFromPixcomp ( PIXC *pixc ); +LEPT_DLL extern PIXAC * pixacompCreate ( l_int32 n ); +LEPT_DLL extern PIXAC * pixacompCreateWithInit ( l_int32 n, l_int32 offset, PIX *pix, l_int32 comptype ); +LEPT_DLL extern PIXAC * pixacompCreateFromPixa ( PIXA *pixa, l_int32 comptype, l_int32 accesstype ); +LEPT_DLL extern PIXAC * pixacompCreateFromFiles ( const char *dirname, const char *substr, l_int32 comptype ); +LEPT_DLL extern PIXAC * pixacompCreateFromSA ( SARRAY *sa, l_int32 comptype ); +LEPT_DLL extern void pixacompDestroy ( PIXAC **ppixac ); +LEPT_DLL extern l_ok pixacompAddPix ( PIXAC *pixac, PIX *pix, l_int32 comptype ); +LEPT_DLL extern l_ok pixacompAddPixcomp ( PIXAC *pixac, PIXC *pixc, l_int32 copyflag ); +LEPT_DLL extern l_ok pixacompReplacePix ( PIXAC *pixac, l_int32 index, PIX *pix, l_int32 comptype ); +LEPT_DLL extern l_ok pixacompReplacePixcomp ( PIXAC *pixac, l_int32 index, PIXC *pixc ); +LEPT_DLL extern l_ok pixacompAddBox ( PIXAC *pixac, BOX *box, l_int32 copyflag ); +LEPT_DLL extern l_int32 pixacompGetCount ( PIXAC *pixac ); +LEPT_DLL extern PIXC * pixacompGetPixcomp ( PIXAC *pixac, l_int32 index, l_int32 copyflag ); +LEPT_DLL extern PIX * pixacompGetPix ( PIXAC *pixac, l_int32 index ); +LEPT_DLL extern l_ok pixacompGetPixDimensions ( PIXAC *pixac, l_int32 index, l_int32 *pw, l_int32 *ph, l_int32 *pd ); +LEPT_DLL extern BOXA * pixacompGetBoxa ( PIXAC *pixac, l_int32 accesstype ); +LEPT_DLL extern l_int32 pixacompGetBoxaCount ( PIXAC *pixac ); +LEPT_DLL extern BOX * pixacompGetBox ( PIXAC *pixac, l_int32 index, l_int32 accesstype ); +LEPT_DLL extern l_ok pixacompGetBoxGeometry ( PIXAC *pixac, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern l_int32 pixacompGetOffset ( PIXAC *pixac ); +LEPT_DLL extern l_ok pixacompSetOffset ( PIXAC *pixac, l_int32 offset ); +LEPT_DLL extern PIXA * pixaCreateFromPixacomp ( PIXAC *pixac, l_int32 accesstype ); +LEPT_DLL extern l_ok pixacompJoin ( PIXAC *pixacd, PIXAC *pixacs, l_int32 istart, l_int32 iend ); +LEPT_DLL extern PIXAC * pixacompInterleave ( PIXAC *pixac1, PIXAC *pixac2 ); +LEPT_DLL extern PIXAC * pixacompRead ( const char *filename ); +LEPT_DLL extern PIXAC * pixacompReadStream ( FILE *fp ); +LEPT_DLL extern PIXAC * pixacompReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixacompWrite ( const char *filename, PIXAC *pixac ); +LEPT_DLL extern l_ok pixacompWriteStream ( FILE *fp, PIXAC *pixac ); +LEPT_DLL extern l_ok pixacompWriteMem ( l_uint8 **pdata, size_t *psize, PIXAC *pixac ); +LEPT_DLL extern l_ok pixacompConvertToPdf ( PIXAC *pixac, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); +LEPT_DLL extern l_ok pixacompConvertToPdfData ( PIXAC *pixac, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixacompFastConvertToPdfData ( PIXAC *pixac, const char *title, l_uint8 **pdata, size_t *pnbytes ); +LEPT_DLL extern l_ok pixacompWriteStreamInfo ( FILE *fp, PIXAC *pixac, const char *text ); +LEPT_DLL extern l_ok pixcompWriteStreamInfo ( FILE *fp, PIXC *pixc, const char *text ); +LEPT_DLL extern PIX * pixacompDisplayTiledAndScaled ( PIXAC *pixac, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); +LEPT_DLL extern l_ok pixacompWriteFiles ( PIXAC *pixac, const char *subdir ); +LEPT_DLL extern l_ok pixcompWriteFile ( const char *rootname, PIXC *pixc ); +LEPT_DLL extern PIX * pixThreshold8 ( PIX *pixs, l_int32 d, l_int32 nlevels, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixRemoveColormapGeneral ( PIX *pixs, l_int32 type, l_int32 ifnocmap ); +LEPT_DLL extern PIX * pixRemoveColormap ( PIX *pixs, l_int32 type ); +LEPT_DLL extern l_ok pixAddGrayColormap8 ( PIX *pixs ); +LEPT_DLL extern PIX * pixAddMinimalGrayColormap8 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertRGBToLuminance ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertRGBToGray ( PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt ); +LEPT_DLL extern PIX * pixConvertRGBToGrayFast ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertRGBToGrayMinMax ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixConvertRGBToGraySatBoost ( PIX *pixs, l_int32 refval ); +LEPT_DLL extern PIX * pixConvertRGBToGrayArb ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc ); +LEPT_DLL extern PIX * pixConvertRGBToBinaryArb ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_int32 thresh, l_int32 relation ); +LEPT_DLL extern PIX * pixConvertGrayToColormap ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertGrayToColormap8 ( PIX *pixs, l_int32 mindepth ); +LEPT_DLL extern PIX * pixColorizeGray ( PIX *pixs, l_uint32 color, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixConvertRGBToColormap ( PIX *pixs, l_int32 ditherflag ); +LEPT_DLL extern PIX * pixConvertCmapTo1 ( PIX *pixs ); +LEPT_DLL extern l_ok pixQuantizeIfFewColors ( PIX *pixs, l_int32 maxcolors, l_int32 mingraycolors, l_int32 octlevel, PIX **ppixd ); +LEPT_DLL extern PIX * pixConvert16To8 ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixConvertGrayToFalseColor ( PIX *pixs, l_float32 gamma ); +LEPT_DLL extern PIX * pixUnpackBinary ( PIX *pixs, l_int32 depth, l_int32 invert ); +LEPT_DLL extern PIX * pixConvert1To16 ( PIX *pixd, PIX *pixs, l_uint16 val0, l_uint16 val1 ); +LEPT_DLL extern PIX * pixConvert1To32 ( PIX *pixd, PIX *pixs, l_uint32 val0, l_uint32 val1 ); +LEPT_DLL extern PIX * pixConvert1To2Cmap ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert1To2 ( PIX *pixd, PIX *pixs, l_int32 val0, l_int32 val1 ); +LEPT_DLL extern PIX * pixConvert1To4Cmap ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert1To4 ( PIX *pixd, PIX *pixs, l_int32 val0, l_int32 val1 ); +LEPT_DLL extern PIX * pixConvert1To8Cmap ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert1To8 ( PIX *pixd, PIX *pixs, l_uint8 val0, l_uint8 val1 ); +LEPT_DLL extern PIX * pixConvert2To8 ( PIX *pixs, l_uint8 val0, l_uint8 val1, l_uint8 val2, l_uint8 val3, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixConvert4To8 ( PIX *pixs, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixConvert8To16 ( PIX *pixs, l_int32 leftshift ); +LEPT_DLL extern PIX * pixConvertTo2 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert8To2 ( PIX *pix ); +LEPT_DLL extern PIX * pixConvertTo4 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert8To4 ( PIX *pix ); +LEPT_DLL extern PIX * pixConvertTo1Adaptive ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertTo1 ( PIX *pixs, l_int32 threshold ); +LEPT_DLL extern PIX * pixConvertTo1BySampling ( PIX *pixs, l_int32 factor, l_int32 threshold ); +LEPT_DLL extern PIX * pixConvertTo8 ( PIX *pixs, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixConvertTo8BySampling ( PIX *pixs, l_int32 factor, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixConvertTo8Colormap ( PIX *pixs, l_int32 dither ); +LEPT_DLL extern PIX * pixConvertTo16 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertTo32 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertTo32BySampling ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern PIX * pixConvert8To32 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertTo8Or32 ( PIX *pixs, l_int32 copyflag, l_int32 warnflag ); +LEPT_DLL extern PIX * pixConvert24To32 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert32To24 ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvert32To16 ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixConvert32To8 ( PIX *pixs, l_int32 type16, l_int32 type8 ); +LEPT_DLL extern PIX * pixRemoveAlpha ( PIX *pixs ); +LEPT_DLL extern PIX * pixAddAlphaTo1bpp ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixConvertLossless ( PIX *pixs, l_int32 d ); +LEPT_DLL extern PIX * pixConvertForPSWrap ( PIX *pixs ); +LEPT_DLL extern PIX * pixConvertToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); +LEPT_DLL extern PIX * pixConvertGrayToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); +LEPT_DLL extern PIX * pixConvertColorToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); +LEPT_DLL extern void l_setNeutralBoostVal ( l_int32 val ); +LEPT_DLL extern PIX * pixConnCompTransform ( PIX *pixs, l_int32 connect, l_int32 depth ); +LEPT_DLL extern PIX * pixConnCompAreaTransform ( PIX *pixs, l_int32 connect ); +LEPT_DLL extern l_ok pixConnCompIncrInit ( PIX *pixs, l_int32 conn, PIX **ppixd, PTAA **pptaa, l_int32 *pncc ); +LEPT_DLL extern l_int32 pixConnCompIncrAdd ( PIX *pixs, PTAA *ptaa, l_int32 *pncc, l_float32 x, l_float32 y, l_int32 debug ); +LEPT_DLL extern l_ok pixGetSortedNeighborValues ( PIX *pixs, l_int32 x, l_int32 y, l_int32 conn, l_int32 **pneigh, l_int32 *pnvals ); +LEPT_DLL extern PIX * pixLocToColorTransform ( PIX *pixs ); +LEPT_DLL extern PIXTILING * pixTilingCreate ( PIX *pixs, l_int32 nx, l_int32 ny, l_int32 w, l_int32 h, l_int32 xoverlap, l_int32 yoverlap ); +LEPT_DLL extern void pixTilingDestroy ( PIXTILING **ppt ); +LEPT_DLL extern l_ok pixTilingGetCount ( PIXTILING *pt, l_int32 *pnx, l_int32 *pny ); +LEPT_DLL extern l_ok pixTilingGetSize ( PIXTILING *pt, l_int32 *pw, l_int32 *ph ); +LEPT_DLL extern PIX * pixTilingGetTile ( PIXTILING *pt, l_int32 i, l_int32 j ); +LEPT_DLL extern l_ok pixTilingNoStripOnPaint ( PIXTILING *pt ); +LEPT_DLL extern l_ok pixTilingPaintTile ( PIX *pixd, l_int32 i, l_int32 j, PIX *pixs, PIXTILING *pt ); +LEPT_DLL extern PIX * pixReadStreamPng ( FILE *fp ); +LEPT_DLL extern l_ok readHeaderPng ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok freadHeaderPng ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok readHeaderMemPng ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_int32 fgetPngResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok isPngInterlaced ( const char *filename, l_int32 *pinterlaced ); +LEPT_DLL extern l_ok fgetPngColormapInfo ( FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency ); +LEPT_DLL extern l_ok pixWritePng ( const char *filename, PIX *pix, l_float32 gamma ); +LEPT_DLL extern l_ok pixWriteStreamPng ( FILE *fp, PIX *pix, l_float32 gamma ); +LEPT_DLL extern l_ok pixSetZlibCompression ( PIX *pix, l_int32 compval ); +LEPT_DLL extern void l_pngSetReadStrip16To8 ( l_int32 flag ); +LEPT_DLL extern PIX * pixReadMemPng ( const l_uint8 *filedata, size_t filesize ); +LEPT_DLL extern l_ok pixWriteMemPng ( l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, l_float32 gamma ); +LEPT_DLL extern PIX * pixReadStreamPnm ( FILE *fp ); +LEPT_DLL extern l_ok readHeaderPnm ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_ok freadHeaderPnm ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_ok pixWriteStreamPnm ( FILE *fp, PIX *pix ); +LEPT_DLL extern l_ok pixWriteStreamAsciiPnm ( FILE *fp, PIX *pix ); +LEPT_DLL extern l_ok pixWriteStreamPam ( FILE *fp, PIX *pix ); +LEPT_DLL extern PIX * pixReadMemPnm ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok readHeaderMemPnm ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); +LEPT_DLL extern l_ok pixWriteMemPnm ( l_uint8 **pdata, size_t *psize, PIX *pix ); +LEPT_DLL extern l_ok pixWriteMemPam ( l_uint8 **pdata, size_t *psize, PIX *pix ); +LEPT_DLL extern PIX * pixProjectiveSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixProjectiveSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixProjectivePta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); +LEPT_DLL extern PIX * pixProjective ( PIX *pixs, l_float32 *vc, l_int32 incolor ); +LEPT_DLL extern PIX * pixProjectivePtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); +LEPT_DLL extern PIX * pixProjectiveColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); +LEPT_DLL extern PIX * pixProjectivePtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); +LEPT_DLL extern PIX * pixProjectiveGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); +LEPT_DLL extern PIX * pixProjectivePtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); +LEPT_DLL extern l_ok getProjectiveXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); +LEPT_DLL extern l_ok projectiveXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); +LEPT_DLL extern l_ok projectiveXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); +LEPT_DLL extern l_ok convertFilesToPS ( const char *dirin, const char *substr, l_int32 res, const char *fileout ); +LEPT_DLL extern l_ok sarrayConvertFilesToPS ( SARRAY *sa, l_int32 res, const char *fileout ); +LEPT_DLL extern l_ok convertFilesFittedToPS ( const char *dirin, const char *substr, l_float32 xpts, l_float32 ypts, const char *fileout ); +LEPT_DLL extern l_ok sarrayConvertFilesFittedToPS ( SARRAY *sa, l_float32 xpts, l_float32 ypts, const char *fileout ); +LEPT_DLL extern l_ok writeImageCompressedToPSFile ( const char *filein, const char *fileout, l_int32 res, l_int32 *pindex ); +LEPT_DLL extern l_ok convertSegmentedPagesToPS ( const char *pagedir, const char *pagestr, l_int32 page_numpre, const char *maskdir, const char *maskstr, l_int32 mask_numpre, l_int32 numpost, l_int32 maxnum, l_float32 textscale, l_float32 imagescale, l_int32 threshold, const char *fileout ); +LEPT_DLL extern l_ok pixWriteSegmentedPageToPS ( PIX *pixs, PIX *pixm, l_float32 textscale, l_float32 imagescale, l_int32 threshold, l_int32 pageno, const char *fileout ); +LEPT_DLL extern l_ok pixWriteMixedToPS ( PIX *pixb, PIX *pixc, l_float32 scale, l_int32 pageno, const char *fileout ); +LEPT_DLL extern l_ok convertToPSEmbed ( const char *filein, const char *fileout, l_int32 level ); +LEPT_DLL extern l_ok pixaWriteCompressedToPS ( PIXA *pixa, const char *fileout, l_int32 res, l_int32 level ); +LEPT_DLL extern l_ok pixWriteCompressedToPS ( PIX *pix, const char *fileout, l_int32 res, l_int32 level, l_int32 *pindex ); +LEPT_DLL extern l_ok pixWritePSEmbed ( const char *filein, const char *fileout ); +LEPT_DLL extern l_ok pixWriteStreamPS ( FILE *fp, PIX *pix, BOX *box, l_int32 res, l_float32 scale ); +LEPT_DLL extern char * pixWriteStringPS ( PIX *pixs, BOX *box, l_int32 res, l_float32 scale ); +LEPT_DLL extern char * generateUncompressedPS ( char *hexdata, l_int32 w, l_int32 h, l_int32 d, l_int32 psbpl, l_int32 bps, l_float32 xpt, l_float32 ypt, l_float32 wpt, l_float32 hpt, l_int32 boxflag ); +LEPT_DLL extern l_ok convertJpegToPSEmbed ( const char *filein, const char *fileout ); +LEPT_DLL extern l_ok convertJpegToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 endpage ); +LEPT_DLL extern l_ok convertG4ToPSEmbed ( const char *filein, const char *fileout ); +LEPT_DLL extern l_ok convertG4ToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 maskflag, l_int32 endpage ); +LEPT_DLL extern l_ok convertTiffMultipageToPS ( const char *filein, const char *fileout, l_float32 fillfract ); +LEPT_DLL extern l_ok convertFlateToPSEmbed ( const char *filein, const char *fileout ); +LEPT_DLL extern l_ok convertFlateToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 endpage ); +LEPT_DLL extern l_ok pixWriteMemPS ( l_uint8 **pdata, size_t *psize, PIX *pix, BOX *box, l_int32 res, l_float32 scale ); +LEPT_DLL extern l_int32 getResLetterPage ( l_int32 w, l_int32 h, l_float32 fillfract ); +LEPT_DLL extern l_int32 getResA4Page ( l_int32 w, l_int32 h, l_float32 fillfract ); +LEPT_DLL extern void l_psWriteBoundingBox ( l_int32 flag ); +LEPT_DLL extern PTA * ptaCreate ( l_int32 n ); +LEPT_DLL extern PTA * ptaCreateFromNuma ( NUMA *nax, NUMA *nay ); +LEPT_DLL extern void ptaDestroy ( PTA **ppta ); +LEPT_DLL extern PTA * ptaCopy ( PTA *pta ); +LEPT_DLL extern PTA * ptaCopyRange ( PTA *ptas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern PTA * ptaClone ( PTA *pta ); +LEPT_DLL extern l_ok ptaEmpty ( PTA *pta ); +LEPT_DLL extern l_ok ptaAddPt ( PTA *pta, l_float32 x, l_float32 y ); +LEPT_DLL extern l_ok ptaInsertPt ( PTA *pta, l_int32 index, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok ptaRemovePt ( PTA *pta, l_int32 index ); +LEPT_DLL extern l_int32 ptaGetRefcount ( PTA *pta ); +LEPT_DLL extern l_int32 ptaChangeRefcount ( PTA *pta, l_int32 delta ); +LEPT_DLL extern l_int32 ptaGetCount ( PTA *pta ); +LEPT_DLL extern l_ok ptaGetPt ( PTA *pta, l_int32 index, l_float32 *px, l_float32 *py ); +LEPT_DLL extern l_ok ptaGetIPt ( PTA *pta, l_int32 index, l_int32 *px, l_int32 *py ); +LEPT_DLL extern l_ok ptaSetPt ( PTA *pta, l_int32 index, l_float32 x, l_float32 y ); +LEPT_DLL extern l_ok ptaGetArrays ( PTA *pta, NUMA **pnax, NUMA **pnay ); +LEPT_DLL extern PTA * ptaRead ( const char *filename ); +LEPT_DLL extern PTA * ptaReadStream ( FILE *fp ); +LEPT_DLL extern PTA * ptaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok ptaWriteDebug ( const char *filename, PTA *pta, l_int32 type ); +LEPT_DLL extern l_ok ptaWrite ( const char *filename, PTA *pta, l_int32 type ); +LEPT_DLL extern l_ok ptaWriteStream ( FILE *fp, PTA *pta, l_int32 type ); +LEPT_DLL extern l_ok ptaWriteMem ( l_uint8 **pdata, size_t *psize, PTA *pta, l_int32 type ); +LEPT_DLL extern PTAA * ptaaCreate ( l_int32 n ); +LEPT_DLL extern void ptaaDestroy ( PTAA **pptaa ); +LEPT_DLL extern l_ok ptaaAddPta ( PTAA *ptaa, PTA *pta, l_int32 copyflag ); +LEPT_DLL extern l_int32 ptaaGetCount ( PTAA *ptaa ); +LEPT_DLL extern PTA * ptaaGetPta ( PTAA *ptaa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern l_ok ptaaGetPt ( PTAA *ptaa, l_int32 ipta, l_int32 jpt, l_float32 *px, l_float32 *py ); +LEPT_DLL extern l_ok ptaaInitFull ( PTAA *ptaa, PTA *pta ); +LEPT_DLL extern l_ok ptaaReplacePta ( PTAA *ptaa, l_int32 index, PTA *pta ); +LEPT_DLL extern l_ok ptaaAddPt ( PTAA *ptaa, l_int32 ipta, l_float32 x, l_float32 y ); +LEPT_DLL extern l_ok ptaaTruncate ( PTAA *ptaa ); +LEPT_DLL extern PTAA * ptaaRead ( const char *filename ); +LEPT_DLL extern PTAA * ptaaReadStream ( FILE *fp ); +LEPT_DLL extern PTAA * ptaaReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok ptaaWriteDebug ( const char *filename, PTAA *ptaa, l_int32 type ); +LEPT_DLL extern l_ok ptaaWrite ( const char *filename, PTAA *ptaa, l_int32 type ); +LEPT_DLL extern l_ok ptaaWriteStream ( FILE *fp, PTAA *ptaa, l_int32 type ); +LEPT_DLL extern l_ok ptaaWriteMem ( l_uint8 **pdata, size_t *psize, PTAA *ptaa, l_int32 type ); +LEPT_DLL extern PTA * ptaSubsample ( PTA *ptas, l_int32 subfactor ); +LEPT_DLL extern l_ok ptaJoin ( PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern l_ok ptaaJoin ( PTAA *ptaad, PTAA *ptaas, l_int32 istart, l_int32 iend ); +LEPT_DLL extern PTA * ptaReverse ( PTA *ptas, l_int32 type ); +LEPT_DLL extern PTA * ptaTranspose ( PTA *ptas ); +LEPT_DLL extern PTA * ptaCyclicPerm ( PTA *ptas, l_int32 xs, l_int32 ys ); +LEPT_DLL extern PTA * ptaSelectRange ( PTA *ptas, l_int32 first, l_int32 last ); +LEPT_DLL extern BOX * ptaGetBoundingRegion ( PTA *pta ); +LEPT_DLL extern l_ok ptaGetRange ( PTA *pta, l_float32 *pminx, l_float32 *pmaxx, l_float32 *pminy, l_float32 *pmaxy ); +LEPT_DLL extern PTA * ptaGetInsideBox ( PTA *ptas, BOX *box ); +LEPT_DLL extern PTA * pixFindCornerPixels ( PIX *pixs ); +LEPT_DLL extern l_int32 ptaContainsPt ( PTA *pta, l_int32 x, l_int32 y ); +LEPT_DLL extern l_int32 ptaTestIntersection ( PTA *pta1, PTA *pta2 ); +LEPT_DLL extern PTA * ptaTransform ( PTA *ptas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern l_int32 ptaPtInsidePolygon ( PTA *pta, l_float32 x, l_float32 y, l_int32 *pinside ); +LEPT_DLL extern l_float32 l_angleBetweenVectors ( l_float32 x1, l_float32 y1, l_float32 x2, l_float32 y2 ); +LEPT_DLL extern l_ok ptaGetMinMax ( PTA *pta, l_float32 *pxmin, l_float32 *pymin, l_float32 *pxmax, l_float32 *pymax ); +LEPT_DLL extern PTA * ptaSelectByValue ( PTA *ptas, l_float32 xth, l_float32 yth, l_int32 type, l_int32 relation ); +LEPT_DLL extern PTA * ptaCropToMask ( PTA *ptas, PIX *pixm ); +LEPT_DLL extern l_ok ptaGetLinearLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit ); +LEPT_DLL extern l_ok ptaGetQuadraticLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, NUMA **pnafit ); +LEPT_DLL extern l_ok ptaGetCubicLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pd, NUMA **pnafit ); +LEPT_DLL extern l_ok ptaGetQuarticLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pd, l_float32 *pe, NUMA **pnafit ); +LEPT_DLL extern l_ok ptaNoisyLinearLSF ( PTA *pta, l_float32 factor, PTA **pptad, l_float32 *pa, l_float32 *pb, l_float32 *pmederr, NUMA **pnafit ); +LEPT_DLL extern l_ok ptaNoisyQuadraticLSF ( PTA *pta, l_float32 factor, PTA **pptad, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pmederr, NUMA **pnafit ); +LEPT_DLL extern l_ok applyLinearFit ( l_float32 a, l_float32 b, l_float32 x, l_float32 *py ); +LEPT_DLL extern l_ok applyQuadraticFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 x, l_float32 *py ); +LEPT_DLL extern l_ok applyCubicFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 d, l_float32 x, l_float32 *py ); +LEPT_DLL extern l_ok applyQuarticFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 d, l_float32 e, l_float32 x, l_float32 *py ); +LEPT_DLL extern l_ok pixPlotAlongPta ( PIX *pixs, PTA *pta, l_int32 outformat, const char *title ); +LEPT_DLL extern PTA * ptaGetPixelsFromPix ( PIX *pixs, BOX *box ); +LEPT_DLL extern PIX * pixGenerateFromPta ( PTA *pta, l_int32 w, l_int32 h ); +LEPT_DLL extern PTA * ptaGetBoundaryPixels ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PTAA * ptaaGetBoundaryPixels ( PIX *pixs, l_int32 type, l_int32 connectivity, BOXA **pboxa, PIXA **ppixa ); +LEPT_DLL extern PTAA * ptaaIndexLabeledPixels ( PIX *pixs, l_int32 *pncc ); +LEPT_DLL extern PTA * ptaGetNeighborPixLocs ( PIX *pixs, l_int32 x, l_int32 y, l_int32 conn ); +LEPT_DLL extern PTA * numaConvertToPta1 ( NUMA *na ); +LEPT_DLL extern PTA * numaConvertToPta2 ( NUMA *nax, NUMA *nay ); +LEPT_DLL extern l_ok ptaConvertToNuma ( PTA *pta, NUMA **pnax, NUMA **pnay ); +LEPT_DLL extern PIX * pixDisplayPta ( PIX *pixd, PIX *pixs, PTA *pta ); +LEPT_DLL extern PIX * pixDisplayPtaaPattern ( PIX *pixd, PIX *pixs, PTAA *ptaa, PIX *pixp, l_int32 cx, l_int32 cy ); +LEPT_DLL extern PIX * pixDisplayPtaPattern ( PIX *pixd, PIX *pixs, PTA *pta, PIX *pixp, l_int32 cx, l_int32 cy, l_uint32 color ); +LEPT_DLL extern PTA * ptaReplicatePattern ( PTA *ptas, PIX *pixp, PTA *ptap, l_int32 cx, l_int32 cy, l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * pixDisplayPtaa ( PIX *pixs, PTAA *ptaa ); +LEPT_DLL extern PTA * ptaSort ( PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); +LEPT_DLL extern l_ok ptaGetSortIndex ( PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); +LEPT_DLL extern PTA * ptaSortByIndex ( PTA *ptas, NUMA *naindex ); +LEPT_DLL extern PTAA * ptaaSortByIndex ( PTAA *ptaas, NUMA *naindex ); +LEPT_DLL extern l_ok ptaGetRankValue ( PTA *pta, l_float32 fract, PTA *ptasort, l_int32 sorttype, l_float32 *pval ); +LEPT_DLL extern PTA * ptaUnionByAset ( PTA *pta1, PTA *pta2 ); +LEPT_DLL extern PTA * ptaRemoveDupsByAset ( PTA *ptas ); +LEPT_DLL extern PTA * ptaIntersectionByAset ( PTA *pta1, PTA *pta2 ); +LEPT_DLL extern L_ASET * l_asetCreateFromPta ( PTA *pta ); +LEPT_DLL extern PTA * ptaUnionByHash ( PTA *pta1, PTA *pta2 ); +LEPT_DLL extern l_ok ptaRemoveDupsByHash ( PTA *ptas, PTA **pptad, L_DNAHASH **pdahash ); +LEPT_DLL extern PTA * ptaIntersectionByHash ( PTA *pta1, PTA *pta2 ); +LEPT_DLL extern l_ok ptaFindPtByHash ( PTA *pta, L_DNAHASH *dahash, l_int32 x, l_int32 y, l_int32 *pindex ); +LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromPta ( PTA *pta ); +LEPT_DLL extern L_PTRA * ptraCreate ( l_int32 n ); +LEPT_DLL extern void ptraDestroy ( L_PTRA **ppa, l_int32 freeflag, l_int32 warnflag ); +LEPT_DLL extern l_ok ptraAdd ( L_PTRA *pa, void *item ); +LEPT_DLL extern l_ok ptraInsert ( L_PTRA *pa, l_int32 index, void *item, l_int32 shiftflag ); +LEPT_DLL extern void * ptraRemove ( L_PTRA *pa, l_int32 index, l_int32 flag ); +LEPT_DLL extern void * ptraRemoveLast ( L_PTRA *pa ); +LEPT_DLL extern void * ptraReplace ( L_PTRA *pa, l_int32 index, void *item, l_int32 freeflag ); +LEPT_DLL extern l_ok ptraSwap ( L_PTRA *pa, l_int32 index1, l_int32 index2 ); +LEPT_DLL extern l_ok ptraCompactArray ( L_PTRA *pa ); +LEPT_DLL extern l_ok ptraReverse ( L_PTRA *pa ); +LEPT_DLL extern l_ok ptraJoin ( L_PTRA *pa1, L_PTRA *pa2 ); +LEPT_DLL extern l_ok ptraGetMaxIndex ( L_PTRA *pa, l_int32 *pmaxindex ); +LEPT_DLL extern l_ok ptraGetActualCount ( L_PTRA *pa, l_int32 *pcount ); +LEPT_DLL extern void * ptraGetPtrToItem ( L_PTRA *pa, l_int32 index ); +LEPT_DLL extern L_PTRAA * ptraaCreate ( l_int32 n ); +LEPT_DLL extern void ptraaDestroy ( L_PTRAA **ppaa, l_int32 freeflag, l_int32 warnflag ); +LEPT_DLL extern l_ok ptraaGetSize ( L_PTRAA *paa, l_int32 *psize ); +LEPT_DLL extern l_ok ptraaInsertPtra ( L_PTRAA *paa, l_int32 index, L_PTRA *pa ); +LEPT_DLL extern L_PTRA * ptraaGetPtra ( L_PTRAA *paa, l_int32 index, l_int32 accessflag ); +LEPT_DLL extern L_PTRA * ptraaFlattenToPtra ( L_PTRAA *paa ); +LEPT_DLL extern l_ok pixQuadtreeMean ( PIX *pixs, l_int32 nlevels, PIX *pix_ma, FPIXA **pfpixa ); +LEPT_DLL extern l_ok pixQuadtreeVariance ( PIX *pixs, l_int32 nlevels, PIX *pix_ma, DPIX *dpix_msa, FPIXA **pfpixa_v, FPIXA **pfpixa_rv ); +LEPT_DLL extern l_ok pixMeanInRectangle ( PIX *pixs, BOX *box, PIX *pixma, l_float32 *pval ); +LEPT_DLL extern l_ok pixVarianceInRectangle ( PIX *pixs, BOX *box, PIX *pix_ma, DPIX *dpix_msa, l_float32 *pvar, l_float32 *prvar ); +LEPT_DLL extern BOXAA * boxaaQuadtreeRegions ( l_int32 w, l_int32 h, l_int32 nlevels ); +LEPT_DLL extern l_ok quadtreeGetParent ( FPIXA *fpixa, l_int32 level, l_int32 x, l_int32 y, l_float32 *pval ); +LEPT_DLL extern l_ok quadtreeGetChildren ( FPIXA *fpixa, l_int32 level, l_int32 x, l_int32 y, l_float32 *pval00, l_float32 *pval10, l_float32 *pval01, l_float32 *pval11 ); +LEPT_DLL extern l_int32 quadtreeMaxLevels ( l_int32 w, l_int32 h ); +LEPT_DLL extern PIX * fpixaDisplayQuadtree ( FPIXA *fpixa, l_int32 factor, l_int32 fontsize ); +LEPT_DLL extern L_QUEUE * lqueueCreate ( l_int32 nalloc ); +LEPT_DLL extern void lqueueDestroy ( L_QUEUE **plq, l_int32 freeflag ); +LEPT_DLL extern l_ok lqueueAdd ( L_QUEUE *lq, void *item ); +LEPT_DLL extern void * lqueueRemove ( L_QUEUE *lq ); +LEPT_DLL extern l_int32 lqueueGetCount ( L_QUEUE *lq ); +LEPT_DLL extern l_ok lqueuePrint ( FILE *fp, L_QUEUE *lq ); +LEPT_DLL extern PIX * pixRankFilter ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); +LEPT_DLL extern PIX * pixRankFilterRGB ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); +LEPT_DLL extern PIX * pixRankFilterGray ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); +LEPT_DLL extern PIX * pixMedianFilter ( PIX *pixs, l_int32 wf, l_int32 hf ); +LEPT_DLL extern PIX * pixRankFilterWithScaling ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank, l_float32 scalefactor ); +LEPT_DLL extern L_RBTREE * l_rbtreeCreate ( l_int32 keytype ); +LEPT_DLL extern RB_TYPE * l_rbtreeLookup ( L_RBTREE *t, RB_TYPE key ); +LEPT_DLL extern void l_rbtreeInsert ( L_RBTREE *t, RB_TYPE key, RB_TYPE value ); +LEPT_DLL extern void l_rbtreeDelete ( L_RBTREE *t, RB_TYPE key ); +LEPT_DLL extern void l_rbtreeDestroy ( L_RBTREE **pt ); +LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetFirst ( L_RBTREE *t ); +LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetNext ( L_RBTREE_NODE *n ); +LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetLast ( L_RBTREE *t ); +LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetPrev ( L_RBTREE_NODE *n ); +LEPT_DLL extern l_int32 l_rbtreeGetCount ( L_RBTREE *t ); +LEPT_DLL extern void l_rbtreePrint ( FILE *fp, L_RBTREE *t ); +LEPT_DLL extern SARRAY * pixProcessBarcodes ( PIX *pixs, l_int32 format, l_int32 method, SARRAY **psaw, l_int32 debugflag ); +LEPT_DLL extern PIXA * pixExtractBarcodes ( PIX *pixs, l_int32 debugflag ); +LEPT_DLL extern SARRAY * pixReadBarcodes ( PIXA *pixa, l_int32 format, l_int32 method, SARRAY **psaw, l_int32 debugflag ); +LEPT_DLL extern NUMA * pixReadBarcodeWidths ( PIX *pixs, l_int32 method, l_int32 debugflag ); +LEPT_DLL extern BOXA * pixLocateBarcodes ( PIX *pixs, l_int32 thresh, PIX **ppixb, PIX **ppixm ); +LEPT_DLL extern PIX * pixDeskewBarcode ( PIX *pixs, PIX *pixb, BOX *box, l_int32 margin, l_int32 threshold, l_float32 *pangle, l_float32 *pconf ); +LEPT_DLL extern NUMA * pixExtractBarcodeWidths1 ( PIX *pixs, l_float32 thresh, l_float32 binfract, NUMA **pnaehist, NUMA **pnaohist, l_int32 debugflag ); +LEPT_DLL extern NUMA * pixExtractBarcodeWidths2 ( PIX *pixs, l_float32 thresh, l_float32 *pwidth, NUMA **pnac, l_int32 debugflag ); +LEPT_DLL extern NUMA * pixExtractBarcodeCrossings ( PIX *pixs, l_float32 thresh, l_int32 debugflag ); +LEPT_DLL extern NUMA * numaQuantizeCrossingsByWidth ( NUMA *nas, l_float32 binfract, NUMA **pnaehist, NUMA **pnaohist, l_int32 debugflag ); +LEPT_DLL extern NUMA * numaQuantizeCrossingsByWindow ( NUMA *nas, l_float32 ratio, l_float32 *pwidth, l_float32 *pfirstloc, NUMA **pnac, l_int32 debugflag ); +LEPT_DLL extern PIXA * pixaReadFiles ( const char *dirname, const char *substr ); +LEPT_DLL extern PIXA * pixaReadFilesSA ( SARRAY *sa ); +LEPT_DLL extern PIX * pixRead ( const char *filename ); +LEPT_DLL extern PIX * pixReadWithHint ( const char *filename, l_int32 hint ); +LEPT_DLL extern PIX * pixReadIndexed ( SARRAY *sa, l_int32 index ); +LEPT_DLL extern PIX * pixReadStream ( FILE *fp, l_int32 hint ); +LEPT_DLL extern l_ok pixReadHeader ( const char *filename, l_int32 *pformat, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok findFileFormat ( const char *filename, l_int32 *pformat ); +LEPT_DLL extern l_ok findFileFormatStream ( FILE *fp, l_int32 *pformat ); +LEPT_DLL extern l_ok findFileFormatBuffer ( const l_uint8 *buf, l_int32 *pformat ); +LEPT_DLL extern l_int32 fileFormatIsTiff ( FILE *fp ); +LEPT_DLL extern PIX * pixReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixReadHeaderMem ( const l_uint8 *data, size_t size, l_int32 *pformat, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok writeImageFileInfo ( const char *filename, FILE *fpout, l_int32 headeronly ); +LEPT_DLL extern l_ok ioFormatTest ( const char *filename ); +LEPT_DLL extern L_RECOG * recogCreateFromRecog ( L_RECOG *recs, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern L_RECOG * recogCreateFromPixa ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern L_RECOG * recogCreateFromPixaNoFinish ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern L_RECOG * recogCreate ( l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); +LEPT_DLL extern void recogDestroy ( L_RECOG **precog ); +LEPT_DLL extern l_int32 recogGetCount ( L_RECOG *recog ); +LEPT_DLL extern l_ok recogSetParams ( L_RECOG *recog, l_int32 type, l_int32 min_nopad, l_float32 max_wh_ratio, l_float32 max_ht_ratio ); +LEPT_DLL extern l_int32 recogGetClassIndex ( L_RECOG *recog, l_int32 val, char *text, l_int32 *pindex ); +LEPT_DLL extern l_ok recogStringToIndex ( L_RECOG *recog, char *text, l_int32 *pindex ); +LEPT_DLL extern l_int32 recogGetClassString ( L_RECOG *recog, l_int32 index, char **pcharstr ); +LEPT_DLL extern l_ok l_convertCharstrToInt ( const char *str, l_int32 *pval ); +LEPT_DLL extern L_RECOG * recogRead ( const char *filename ); +LEPT_DLL extern L_RECOG * recogReadStream ( FILE *fp ); +LEPT_DLL extern L_RECOG * recogReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok recogWrite ( const char *filename, L_RECOG *recog ); +LEPT_DLL extern l_ok recogWriteStream ( FILE *fp, L_RECOG *recog ); +LEPT_DLL extern l_ok recogWriteMem ( l_uint8 **pdata, size_t *psize, L_RECOG *recog ); +LEPT_DLL extern PIXA * recogExtractPixa ( L_RECOG *recog ); +LEPT_DLL extern BOXA * recogDecode ( L_RECOG *recog, PIX *pixs, l_int32 nlevels, PIX **ppixdb ); +LEPT_DLL extern l_ok recogCreateDid ( L_RECOG *recog, PIX *pixs ); +LEPT_DLL extern l_ok recogDestroyDid ( L_RECOG *recog ); +LEPT_DLL extern l_int32 recogDidExists ( L_RECOG *recog ); +LEPT_DLL extern L_RDID * recogGetDid ( L_RECOG *recog ); +LEPT_DLL extern l_ok recogSetChannelParams ( L_RECOG *recog, l_int32 nlevels ); +LEPT_DLL extern l_ok recogIdentifyMultiple ( L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, PIX **ppixdb, l_int32 debugsplit ); +LEPT_DLL extern l_ok recogSplitIntoCharacters ( L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, l_int32 debug ); +LEPT_DLL extern l_ok recogCorrelationBestRow ( L_RECOG *recog, PIX *pixs, BOXA **pboxa, NUMA **pnascore, NUMA **pnaindex, SARRAY **psachar, l_int32 debug ); +LEPT_DLL extern l_ok recogCorrelationBestChar ( L_RECOG *recog, PIX *pixs, BOX **pbox, l_float32 *pscore, l_int32 *pindex, char **pcharstr, PIX **ppixdb ); +LEPT_DLL extern l_ok recogIdentifyPixa ( L_RECOG *recog, PIXA *pixa, PIX **ppixdb ); +LEPT_DLL extern l_ok recogIdentifyPix ( L_RECOG *recog, PIX *pixs, PIX **ppixdb ); +LEPT_DLL extern l_ok recogSkipIdentify ( L_RECOG *recog ); +LEPT_DLL extern void rchaDestroy ( L_RCHA **prcha ); +LEPT_DLL extern void rchDestroy ( L_RCH **prch ); +LEPT_DLL extern l_ok rchaExtract ( L_RCHA *rcha, NUMA **pnaindex, NUMA **pnascore, SARRAY **psatext, NUMA **pnasample, NUMA **pnaxloc, NUMA **pnayloc, NUMA **pnawidth ); +LEPT_DLL extern l_ok rchExtract ( L_RCH *rch, l_int32 *pindex, l_float32 *pscore, char **ptext, l_int32 *psample, l_int32 *pxloc, l_int32 *pyloc, l_int32 *pwidth ); +LEPT_DLL extern PIX * recogProcessToIdentify ( L_RECOG *recog, PIX *pixs, l_int32 pad ); +LEPT_DLL extern SARRAY * recogExtractNumbers ( L_RECOG *recog, BOXA *boxas, l_float32 scorethresh, l_int32 spacethresh, BOXAA **pbaa, NUMAA **pnaa ); +LEPT_DLL extern PIXA * showExtractNumbers ( PIX *pixs, SARRAY *sa, BOXAA *baa, NUMAA *naa, PIX **ppixdb ); +LEPT_DLL extern l_ok recogTrainLabeled ( L_RECOG *recog, PIX *pixs, BOX *box, char *text, l_int32 debug ); +LEPT_DLL extern l_ok recogProcessLabeled ( L_RECOG *recog, PIX *pixs, BOX *box, char *text, PIX **ppix ); +LEPT_DLL extern l_ok recogAddSample ( L_RECOG *recog, PIX *pix, l_int32 debug ); +LEPT_DLL extern PIX * recogModifyTemplate ( L_RECOG *recog, PIX *pixs ); +LEPT_DLL extern l_int32 recogAverageSamples ( L_RECOG **precog, l_int32 debug ); +LEPT_DLL extern l_int32 pixaAccumulateSamples ( PIXA *pixa, PTA *pta, PIX **ppixd, l_float32 *px, l_float32 *py ); +LEPT_DLL extern l_ok recogTrainingFinished ( L_RECOG **precog, l_int32 modifyflag, l_int32 minsize, l_float32 minfract ); +LEPT_DLL extern PIXA * recogFilterPixaBySize ( PIXA *pixas, l_int32 setsize, l_int32 maxkeep, l_float32 max_ht_ratio, NUMA **pna ); +LEPT_DLL extern PIXAA * recogSortPixaByClass ( PIXA *pixa, l_int32 setsize ); +LEPT_DLL extern l_ok recogRemoveOutliers1 ( L_RECOG **precog, l_float32 minscore, l_int32 mintarget, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); +LEPT_DLL extern PIXA * pixaRemoveOutliers1 ( PIXA *pixas, l_float32 minscore, l_int32 mintarget, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); +LEPT_DLL extern l_ok recogRemoveOutliers2 ( L_RECOG **precog, l_float32 minscore, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); +LEPT_DLL extern PIXA * pixaRemoveOutliers2 ( PIXA *pixas, l_float32 minscore, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); +LEPT_DLL extern PIXA * recogTrainFromBoot ( L_RECOG *recogboot, PIXA *pixas, l_float32 minscore, l_int32 threshold, l_int32 debug ); +LEPT_DLL extern l_ok recogPadDigitTrainingSet ( L_RECOG **precog, l_int32 scaleh, l_int32 linew ); +LEPT_DLL extern l_int32 recogIsPaddingNeeded ( L_RECOG *recog, SARRAY **psa ); +LEPT_DLL extern PIXA * recogAddDigitPadTemplates ( L_RECOG *recog, SARRAY *sa ); +LEPT_DLL extern L_RECOG * recogMakeBootDigitRecog ( l_int32 nsamp, l_int32 scaleh, l_int32 linew, l_int32 maxyshift, l_int32 debug ); +LEPT_DLL extern PIXA * recogMakeBootDigitTemplates ( l_int32 nsamp, l_int32 debug ); +LEPT_DLL extern l_ok recogShowContent ( FILE *fp, L_RECOG *recog, l_int32 index, l_int32 display ); +LEPT_DLL extern l_ok recogDebugAverages ( L_RECOG **precog, l_int32 debug ); +LEPT_DLL extern l_int32 recogShowAverageTemplates ( L_RECOG *recog ); +LEPT_DLL extern l_ok recogShowMatchesInRange ( L_RECOG *recog, PIXA *pixa, l_float32 minscore, l_float32 maxscore, l_int32 display ); +LEPT_DLL extern PIX * recogShowMatch ( L_RECOG *recog, PIX *pix1, PIX *pix2, BOX *box, l_int32 index, l_float32 score ); +LEPT_DLL extern l_ok regTestSetup ( l_int32 argc, char **argv, L_REGPARAMS **prp ); +LEPT_DLL extern l_ok regTestCleanup ( L_REGPARAMS *rp ); +LEPT_DLL extern l_ok regTestCompareValues ( L_REGPARAMS *rp, l_float32 val1, l_float32 val2, l_float32 delta ); +LEPT_DLL extern l_ok regTestCompareStrings ( L_REGPARAMS *rp, l_uint8 *string1, size_t bytes1, l_uint8 *string2, size_t bytes2 ); +LEPT_DLL extern l_ok regTestComparePix ( L_REGPARAMS *rp, PIX *pix1, PIX *pix2 ); +LEPT_DLL extern l_ok regTestCompareSimilarPix ( L_REGPARAMS *rp, PIX *pix1, PIX *pix2, l_int32 mindiff, l_float32 maxfract, l_int32 printstats ); +LEPT_DLL extern l_ok regTestCheckFile ( L_REGPARAMS *rp, const char *localname ); +LEPT_DLL extern l_ok regTestCompareFiles ( L_REGPARAMS *rp, l_int32 index1, l_int32 index2 ); +LEPT_DLL extern l_ok regTestWritePixAndCheck ( L_REGPARAMS *rp, PIX *pix, l_int32 format ); +LEPT_DLL extern l_ok regTestWriteDataAndCheck ( L_REGPARAMS *rp, void *data, size_t nbytes, const char *ext ); +LEPT_DLL extern char * regTestGenLocalFilename ( L_REGPARAMS *rp, l_int32 index, l_int32 format ); +LEPT_DLL extern l_ok pixRasterop ( PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy ); +LEPT_DLL extern l_ok pixRasteropVip ( PIX *pixd, l_int32 bx, l_int32 bw, l_int32 vshift, l_int32 incolor ); +LEPT_DLL extern l_ok pixRasteropHip ( PIX *pixd, l_int32 by, l_int32 bh, l_int32 hshift, l_int32 incolor ); +LEPT_DLL extern PIX * pixTranslate ( PIX *pixd, PIX *pixs, l_int32 hshift, l_int32 vshift, l_int32 incolor ); +LEPT_DLL extern l_ok pixRasteropIP ( PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor ); +LEPT_DLL extern l_ok pixRasteropFullImage ( PIX *pixd, PIX *pixs, l_int32 op ); +LEPT_DLL extern void rasteropUniLow ( l_uint32 *datad, l_int32 dpixw, l_int32 dpixh, l_int32 depth, l_int32 dwpl, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op ); +LEPT_DLL extern void rasteropLow ( l_uint32 *datad, l_int32 dpixw, l_int32 dpixh, l_int32 depth, l_int32 dwpl, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, l_uint32 *datas, l_int32 spixw, l_int32 spixh, l_int32 swpl, l_int32 sx, l_int32 sy ); +LEPT_DLL extern void rasteropVipLow ( l_uint32 *data, l_int32 pixw, l_int32 pixh, l_int32 depth, l_int32 wpl, l_int32 x, l_int32 w, l_int32 shift ); +LEPT_DLL extern void rasteropHipLow ( l_uint32 *data, l_int32 pixh, l_int32 depth, l_int32 wpl, l_int32 y, l_int32 h, l_int32 shift ); +LEPT_DLL extern PIX * pixRotate ( PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height ); +LEPT_DLL extern PIX * pixEmbedForRotation ( PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height ); +LEPT_DLL extern PIX * pixRotateBySampling ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotateBinaryNice ( PIX *pixs, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotateWithAlpha ( PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract ); +LEPT_DLL extern PIX * pixRotateAM ( PIX *pixs, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotateAMColor ( PIX *pixs, l_float32 angle, l_uint32 colorval ); +LEPT_DLL extern PIX * pixRotateAMGray ( PIX *pixs, l_float32 angle, l_uint8 grayval ); +LEPT_DLL extern PIX * pixRotateAMCorner ( PIX *pixs, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotateAMColorCorner ( PIX *pixs, l_float32 angle, l_uint32 fillval ); +LEPT_DLL extern PIX * pixRotateAMGrayCorner ( PIX *pixs, l_float32 angle, l_uint8 grayval ); +LEPT_DLL extern PIX * pixRotateAMColorFast ( PIX *pixs, l_float32 angle, l_uint32 colorval ); +LEPT_DLL extern PIX * pixRotateOrth ( PIX *pixs, l_int32 quads ); +LEPT_DLL extern PIX * pixRotate180 ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixRotate90 ( PIX *pixs, l_int32 direction ); +LEPT_DLL extern PIX * pixFlipLR ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixFlipTB ( PIX *pixd, PIX *pixs ); +LEPT_DLL extern PIX * pixRotateShear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotate2Shear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotate3Shear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern l_ok pixRotateShearIP ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixRotateShearCenter ( PIX *pixs, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern l_ok pixRotateShearCenterIP ( PIX *pixs, l_float32 angle, l_int32 incolor ); +LEPT_DLL extern PIX * pixStrokeWidthTransform ( PIX *pixs, l_int32 color, l_int32 depth, l_int32 nangles ); +LEPT_DLL extern PIX * pixRunlengthTransform ( PIX *pixs, l_int32 color, l_int32 direction, l_int32 depth ); +LEPT_DLL extern l_ok pixFindHorizontalRuns ( PIX *pix, l_int32 y, l_int32 *xstart, l_int32 *xend, l_int32 *pn ); +LEPT_DLL extern l_ok pixFindVerticalRuns ( PIX *pix, l_int32 x, l_int32 *ystart, l_int32 *yend, l_int32 *pn ); +LEPT_DLL extern NUMA * pixFindMaxRuns ( PIX *pix, l_int32 direction, NUMA **pnastart ); +LEPT_DLL extern l_ok pixFindMaxHorizontalRunOnLine ( PIX *pix, l_int32 y, l_int32 *pxstart, l_int32 *psize ); +LEPT_DLL extern l_ok pixFindMaxVerticalRunOnLine ( PIX *pix, l_int32 x, l_int32 *pystart, l_int32 *psize ); +LEPT_DLL extern l_ok runlengthMembershipOnLine ( l_int32 *buffer, l_int32 size, l_int32 depth, l_int32 *start, l_int32 *end, l_int32 n ); +LEPT_DLL extern l_int32 * makeMSBitLocTab ( l_int32 bitval ); +LEPT_DLL extern SARRAY * sarrayCreate ( l_int32 n ); +LEPT_DLL extern SARRAY * sarrayCreateInitialized ( l_int32 n, const char *initstr ); +LEPT_DLL extern SARRAY * sarrayCreateWordsFromString ( const char *string ); +LEPT_DLL extern SARRAY * sarrayCreateLinesFromString ( const char *string, l_int32 blankflag ); +LEPT_DLL extern void sarrayDestroy ( SARRAY **psa ); +LEPT_DLL extern SARRAY * sarrayCopy ( SARRAY *sa ); +LEPT_DLL extern SARRAY * sarrayClone ( SARRAY *sa ); +LEPT_DLL extern l_ok sarrayAddString ( SARRAY *sa, const char *string, l_int32 copyflag ); +LEPT_DLL extern char * sarrayRemoveString ( SARRAY *sa, l_int32 index ); +LEPT_DLL extern l_ok sarrayReplaceString ( SARRAY *sa, l_int32 index, char *newstr, l_int32 copyflag ); +LEPT_DLL extern l_ok sarrayClear ( SARRAY *sa ); +LEPT_DLL extern l_int32 sarrayGetCount ( SARRAY *sa ); +LEPT_DLL extern char ** sarrayGetArray ( SARRAY *sa, l_int32 *pnalloc, l_int32 *pn ); +LEPT_DLL extern char * sarrayGetString ( SARRAY *sa, l_int32 index, l_int32 copyflag ); +LEPT_DLL extern l_int32 sarrayGetRefcount ( SARRAY *sa ); +LEPT_DLL extern l_ok sarrayChangeRefcount ( SARRAY *sa, l_int32 delta ); +LEPT_DLL extern char * sarrayToString ( SARRAY *sa, l_int32 addnlflag ); +LEPT_DLL extern char * sarrayToStringRange ( SARRAY *sa, l_int32 first, l_int32 nstrings, l_int32 addnlflag ); +LEPT_DLL extern l_ok sarrayJoin ( SARRAY *sa1, SARRAY *sa2 ); +LEPT_DLL extern l_ok sarrayAppendRange ( SARRAY *sa1, SARRAY *sa2, l_int32 start, l_int32 end ); +LEPT_DLL extern l_ok sarrayPadToSameSize ( SARRAY *sa1, SARRAY *sa2, const char *padstring ); +LEPT_DLL extern SARRAY * sarrayConvertWordsToLines ( SARRAY *sa, l_int32 linesize ); +LEPT_DLL extern l_int32 sarraySplitString ( SARRAY *sa, const char *str, const char *separators ); +LEPT_DLL extern SARRAY * sarraySelectBySubstring ( SARRAY *sain, const char *substr ); +LEPT_DLL extern SARRAY * sarraySelectByRange ( SARRAY *sain, l_int32 first, l_int32 last ); +LEPT_DLL extern l_int32 sarrayParseRange ( SARRAY *sa, l_int32 start, l_int32 *pactualstart, l_int32 *pend, l_int32 *pnewstart, const char *substr, l_int32 loc ); +LEPT_DLL extern SARRAY * sarrayRead ( const char *filename ); +LEPT_DLL extern SARRAY * sarrayReadStream ( FILE *fp ); +LEPT_DLL extern SARRAY * sarrayReadMem ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok sarrayWrite ( const char *filename, SARRAY *sa ); +LEPT_DLL extern l_ok sarrayWriteStream ( FILE *fp, SARRAY *sa ); +LEPT_DLL extern l_ok sarrayWriteMem ( l_uint8 **pdata, size_t *psize, SARRAY *sa ); +LEPT_DLL extern l_ok sarrayAppend ( const char *filename, SARRAY *sa ); +LEPT_DLL extern SARRAY * getNumberedPathnamesInDirectory ( const char *dirname, const char *substr, l_int32 numpre, l_int32 numpost, l_int32 maxnum ); +LEPT_DLL extern SARRAY * getSortedPathnamesInDirectory ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); +LEPT_DLL extern SARRAY * convertSortedToNumberedPathnames ( SARRAY *sa, l_int32 numpre, l_int32 numpost, l_int32 maxnum ); +LEPT_DLL extern SARRAY * getFilenamesInDirectory ( const char *dirname ); +LEPT_DLL extern SARRAY * sarraySort ( SARRAY *saout, SARRAY *sain, l_int32 sortorder ); +LEPT_DLL extern SARRAY * sarraySortByIndex ( SARRAY *sain, NUMA *naindex ); +LEPT_DLL extern l_int32 stringCompareLexical ( const char *str1, const char *str2 ); +LEPT_DLL extern SARRAY * sarrayUnionByAset ( SARRAY *sa1, SARRAY *sa2 ); +LEPT_DLL extern SARRAY * sarrayRemoveDupsByAset ( SARRAY *sas ); +LEPT_DLL extern SARRAY * sarrayIntersectionByAset ( SARRAY *sa1, SARRAY *sa2 ); +LEPT_DLL extern L_ASET * l_asetCreateFromSarray ( SARRAY *sa ); +LEPT_DLL extern l_ok sarrayRemoveDupsByHash ( SARRAY *sas, SARRAY **psad, L_DNAHASH **pdahash ); +LEPT_DLL extern SARRAY * sarrayIntersectionByHash ( SARRAY *sa1, SARRAY *sa2 ); +LEPT_DLL extern l_ok sarrayFindStringByHash ( SARRAY *sa, L_DNAHASH *dahash, const char *str, l_int32 *pindex ); +LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromSarray ( SARRAY *sa ); +LEPT_DLL extern SARRAY * sarrayGenerateIntegers ( l_int32 n ); +LEPT_DLL extern l_ok sarrayLookupCSKV ( SARRAY *sa, const char *keystring, char **pvalstring ); +LEPT_DLL extern PIX * pixScale ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleToSizeRel ( PIX *pixs, l_int32 delw, l_int32 delh ); +LEPT_DLL extern PIX * pixScaleToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIX * pixScaleToResolution ( PIX *pixs, l_float32 target, l_float32 assumed, l_float32 *pscalefact ); +LEPT_DLL extern PIX * pixScaleGeneral ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_float32 sharpfract, l_int32 sharpwidth ); +LEPT_DLL extern PIX * pixScaleLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleColorLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleColor2xLI ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleColor4xLI ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleGrayLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleGray2xLI ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleGray4xLI ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleGray2xLIThresh ( PIX *pixs, l_int32 thresh ); +LEPT_DLL extern PIX * pixScaleGray2xLIDither ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleGray4xLIThresh ( PIX *pixs, l_int32 thresh ); +LEPT_DLL extern PIX * pixScaleGray4xLIDither ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleBySampling ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleBySamplingToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIX * pixScaleByIntSampling ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern PIX * pixScaleRGBToGrayFast ( PIX *pixs, l_int32 factor, l_int32 color ); +LEPT_DLL extern PIX * pixScaleRGBToBinaryFast ( PIX *pixs, l_int32 factor, l_int32 thresh ); +LEPT_DLL extern PIX * pixScaleGrayToBinaryFast ( PIX *pixs, l_int32 factor, l_int32 thresh ); +LEPT_DLL extern PIX * pixScaleSmooth ( PIX *pix, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleSmoothToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIX * pixScaleRGBToGray2 ( PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt ); +LEPT_DLL extern PIX * pixScaleAreaMap ( PIX *pix, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleAreaMap2 ( PIX *pix ); +LEPT_DLL extern PIX * pixScaleAreaMapToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); +LEPT_DLL extern PIX * pixScaleBinary ( PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleToGray ( PIX *pixs, l_float32 scalefactor ); +LEPT_DLL extern PIX * pixScaleToGrayFast ( PIX *pixs, l_float32 scalefactor ); +LEPT_DLL extern PIX * pixScaleToGray2 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGray3 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGray4 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGray6 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGray8 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGray16 ( PIX *pixs ); +LEPT_DLL extern PIX * pixScaleToGrayMipmap ( PIX *pixs, l_float32 scalefactor ); +LEPT_DLL extern PIX * pixScaleMipmap ( PIX *pixs1, PIX *pixs2, l_float32 scale ); +LEPT_DLL extern PIX * pixExpandReplicate ( PIX *pixs, l_int32 factor ); +LEPT_DLL extern PIX * pixScaleGrayMinMax ( PIX *pixs, l_int32 xfact, l_int32 yfact, l_int32 type ); +LEPT_DLL extern PIX * pixScaleGrayMinMax2 ( PIX *pixs, l_int32 type ); +LEPT_DLL extern PIX * pixScaleGrayRankCascade ( PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4 ); +LEPT_DLL extern PIX * pixScaleGrayRank2 ( PIX *pixs, l_int32 rank ); +LEPT_DLL extern l_ok pixScaleAndTransferAlpha ( PIX *pixd, PIX *pixs, l_float32 scalex, l_float32 scaley ); +LEPT_DLL extern PIX * pixScaleWithAlpha ( PIX *pixs, l_float32 scalex, l_float32 scaley, PIX *pixg, l_float32 fract ); +LEPT_DLL extern PIX * pixSeedfillBinary ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity ); +LEPT_DLL extern PIX * pixSeedfillBinaryRestricted ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 xmax, l_int32 ymax ); +LEPT_DLL extern PIX * pixHolesByFilling ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern PIX * pixFillClosedBorders ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern PIX * pixExtractBorderConnComps ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern PIX * pixRemoveBorderConnComps ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern PIX * pixFillBgFromBorder ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern PIX * pixFillHolesToBoundingRect ( PIX *pixs, l_int32 minsize, l_float32 maxhfract, l_float32 minfgfract ); +LEPT_DLL extern l_ok pixSeedfillGray ( PIX *pixs, PIX *pixm, l_int32 connectivity ); +LEPT_DLL extern l_ok pixSeedfillGrayInv ( PIX *pixs, PIX *pixm, l_int32 connectivity ); +LEPT_DLL extern l_ok pixSeedfillGraySimple ( PIX *pixs, PIX *pixm, l_int32 connectivity ); +LEPT_DLL extern l_ok pixSeedfillGrayInvSimple ( PIX *pixs, PIX *pixm, l_int32 connectivity ); +LEPT_DLL extern PIX * pixSeedfillGrayBasin ( PIX *pixb, PIX *pixm, l_int32 delta, l_int32 connectivity ); +LEPT_DLL extern PIX * pixDistanceFunction ( PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond ); +LEPT_DLL extern PIX * pixSeedspread ( PIX *pixs, l_int32 connectivity ); +LEPT_DLL extern l_ok pixLocalExtrema ( PIX *pixs, l_int32 maxmin, l_int32 minmax, PIX **ppixmin, PIX **ppixmax ); +LEPT_DLL extern l_ok pixSelectedLocalExtrema ( PIX *pixs, l_int32 mindist, PIX **ppixmin, PIX **ppixmax ); +LEPT_DLL extern PIX * pixFindEqualValues ( PIX *pixs1, PIX *pixs2 ); +LEPT_DLL extern l_ok pixSelectMinInConnComp ( PIX *pixs, PIX *pixm, PTA **ppta, NUMA **pnav ); +LEPT_DLL extern PIX * pixRemoveSeededComponents ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 bordersize ); +LEPT_DLL extern SELA * selaCreate ( l_int32 n ); +LEPT_DLL extern void selaDestroy ( SELA **psela ); +LEPT_DLL extern SEL * selCreate ( l_int32 height, l_int32 width, const char *name ); +LEPT_DLL extern void selDestroy ( SEL **psel ); +LEPT_DLL extern SEL * selCopy ( SEL *sel ); +LEPT_DLL extern SEL * selCreateBrick ( l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, l_int32 type ); +LEPT_DLL extern SEL * selCreateComb ( l_int32 factor1, l_int32 factor2, l_int32 direction ); +LEPT_DLL extern l_int32 ** create2dIntArray ( l_int32 sy, l_int32 sx ); +LEPT_DLL extern l_ok selaAddSel ( SELA *sela, SEL *sel, const char *selname, l_int32 copyflag ); +LEPT_DLL extern l_int32 selaGetCount ( SELA *sela ); +LEPT_DLL extern SEL * selaGetSel ( SELA *sela, l_int32 i ); +LEPT_DLL extern char * selGetName ( SEL *sel ); +LEPT_DLL extern l_ok selSetName ( SEL *sel, const char *name ); +LEPT_DLL extern l_ok selaFindSelByName ( SELA *sela, const char *name, l_int32 *pindex, SEL **psel ); +LEPT_DLL extern l_ok selGetElement ( SEL *sel, l_int32 row, l_int32 col, l_int32 *ptype ); +LEPT_DLL extern l_ok selSetElement ( SEL *sel, l_int32 row, l_int32 col, l_int32 type ); +LEPT_DLL extern l_ok selGetParameters ( SEL *sel, l_int32 *psy, l_int32 *psx, l_int32 *pcy, l_int32 *pcx ); +LEPT_DLL extern l_ok selSetOrigin ( SEL *sel, l_int32 cy, l_int32 cx ); +LEPT_DLL extern l_ok selGetTypeAtOrigin ( SEL *sel, l_int32 *ptype ); +LEPT_DLL extern char * selaGetBrickName ( SELA *sela, l_int32 hsize, l_int32 vsize ); +LEPT_DLL extern char * selaGetCombName ( SELA *sela, l_int32 size, l_int32 direction ); +LEPT_DLL extern l_ok getCompositeParameters ( l_int32 size, l_int32 *psize1, l_int32 *psize2, char **pnameh1, char **pnameh2, char **pnamev1, char **pnamev2 ); +LEPT_DLL extern SARRAY * selaGetSelnames ( SELA *sela ); +LEPT_DLL extern l_ok selFindMaxTranslations ( SEL *sel, l_int32 *pxp, l_int32 *pyp, l_int32 *pxn, l_int32 *pyn ); +LEPT_DLL extern SEL * selRotateOrth ( SEL *sel, l_int32 quads ); +LEPT_DLL extern SELA * selaRead ( const char *fname ); +LEPT_DLL extern SELA * selaReadStream ( FILE *fp ); +LEPT_DLL extern SEL * selRead ( const char *fname ); +LEPT_DLL extern SEL * selReadStream ( FILE *fp ); +LEPT_DLL extern l_ok selaWrite ( const char *fname, SELA *sela ); +LEPT_DLL extern l_ok selaWriteStream ( FILE *fp, SELA *sela ); +LEPT_DLL extern l_ok selWrite ( const char *fname, SEL *sel ); +LEPT_DLL extern l_ok selWriteStream ( FILE *fp, SEL *sel ); +LEPT_DLL extern SEL * selCreateFromString ( const char *text, l_int32 h, l_int32 w, const char *name ); +LEPT_DLL extern char * selPrintToString ( SEL *sel ); +LEPT_DLL extern SELA * selaCreateFromFile ( const char *filename ); +LEPT_DLL extern SEL * selCreateFromPta ( PTA *pta, l_int32 cy, l_int32 cx, const char *name ); +LEPT_DLL extern SEL * selCreateFromPix ( PIX *pix, l_int32 cy, l_int32 cx, const char *name ); +LEPT_DLL extern SEL * selReadFromColorImage ( const char *pathname ); +LEPT_DLL extern SEL * selCreateFromColorPix ( PIX *pixs, const char *selname ); +LEPT_DLL extern PIX * selDisplayInPix ( SEL *sel, l_int32 size, l_int32 gthick ); +LEPT_DLL extern PIX * selaDisplayInPix ( SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols ); +LEPT_DLL extern SELA * selaAddBasic ( SELA *sela ); +LEPT_DLL extern SELA * selaAddHitMiss ( SELA *sela ); +LEPT_DLL extern SELA * selaAddDwaLinear ( SELA *sela ); +LEPT_DLL extern SELA * selaAddDwaCombs ( SELA *sela ); +LEPT_DLL extern SELA * selaAddCrossJunctions ( SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag ); +LEPT_DLL extern SELA * selaAddTJunctions ( SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag ); +LEPT_DLL extern SELA * sela4ccThin ( SELA *sela ); +LEPT_DLL extern SELA * sela8ccThin ( SELA *sela ); +LEPT_DLL extern SELA * sela4and8ccThin ( SELA *sela ); +LEPT_DLL extern SEL * pixGenerateSelWithRuns ( PIX *pixs, l_int32 nhlines, l_int32 nvlines, l_int32 distance, l_int32 minlength, l_int32 toppix, l_int32 botpix, l_int32 leftpix, l_int32 rightpix, PIX **ppixe ); +LEPT_DLL extern SEL * pixGenerateSelRandom ( PIX *pixs, l_float32 hitfract, l_float32 missfract, l_int32 distance, l_int32 toppix, l_int32 botpix, l_int32 leftpix, l_int32 rightpix, PIX **ppixe ); +LEPT_DLL extern SEL * pixGenerateSelBoundary ( PIX *pixs, l_int32 hitdist, l_int32 missdist, l_int32 hitskip, l_int32 missskip, l_int32 topflag, l_int32 botflag, l_int32 leftflag, l_int32 rightflag, PIX **ppixe ); +LEPT_DLL extern NUMA * pixGetRunCentersOnLine ( PIX *pixs, l_int32 x, l_int32 y, l_int32 minlength ); +LEPT_DLL extern NUMA * pixGetRunsOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2 ); +LEPT_DLL extern PTA * pixSubsampleBoundaryPixels ( PIX *pixs, l_int32 skip ); +LEPT_DLL extern l_int32 adjacentOnPixelInRaster ( PIX *pixs, l_int32 x, l_int32 y, l_int32 *pxa, l_int32 *pya ); +LEPT_DLL extern PIX * pixDisplayHitMissSel ( PIX *pixs, SEL *sel, l_int32 scalefactor, l_uint32 hitcolor, l_uint32 misscolor ); +LEPT_DLL extern PIX * pixHShear ( PIX *pixd, PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixVShear ( PIX *pixd, PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixHShearCorner ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixVShearCorner ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixHShearCenter ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixVShearCenter ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern l_ok pixHShearIP ( PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern l_ok pixVShearIP ( PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixHShearLI ( PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixVShearLI ( PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); +LEPT_DLL extern PIX * pixDeskewBoth ( PIX *pixs, l_int32 redsearch ); +LEPT_DLL extern PIX * pixDeskew ( PIX *pixs, l_int32 redsearch ); +LEPT_DLL extern PIX * pixFindSkewAndDeskew ( PIX *pixs, l_int32 redsearch, l_float32 *pangle, l_float32 *pconf ); +LEPT_DLL extern PIX * pixDeskewGeneral ( PIX *pixs, l_int32 redsweep, l_float32 sweeprange, l_float32 sweepdelta, l_int32 redsearch, l_int32 thresh, l_float32 *pangle, l_float32 *pconf ); +LEPT_DLL extern l_ok pixFindSkew ( PIX *pixs, l_float32 *pangle, l_float32 *pconf ); +LEPT_DLL extern l_ok pixFindSkewSweep ( PIX *pixs, l_float32 *pangle, l_int32 reduction, l_float32 sweeprange, l_float32 sweepdelta ); +LEPT_DLL extern l_ok pixFindSkewSweepAndSearch ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); +LEPT_DLL extern l_ok pixFindSkewSweepAndSearchScore ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); +LEPT_DLL extern l_ok pixFindSkewSweepAndSearchScorePivot ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_int32 pivot ); +LEPT_DLL extern l_int32 pixFindSkewOrthogonalRange ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_float32 confprior ); +LEPT_DLL extern l_ok pixFindDifferentialSquareSum ( PIX *pixs, l_float32 *psum ); +LEPT_DLL extern l_ok pixFindNormalizedSquareSum ( PIX *pixs, l_float32 *phratio, l_float32 *pvratio, l_float32 *pfract ); +LEPT_DLL extern PIX * pixReadStreamSpix ( FILE *fp ); +LEPT_DLL extern l_ok readHeaderSpix ( const char *filename, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok freadHeaderSpix ( FILE *fp, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok sreadHeaderSpix ( const l_uint32 *data, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); +LEPT_DLL extern l_ok pixWriteStreamSpix ( FILE *fp, PIX *pix ); +LEPT_DLL extern PIX * pixReadMemSpix ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixWriteMemSpix ( l_uint8 **pdata, size_t *psize, PIX *pix ); +LEPT_DLL extern l_ok pixSerializeToMemory ( PIX *pixs, l_uint32 **pdata, size_t *pnbytes ); +LEPT_DLL extern PIX * pixDeserializeFromMemory ( const l_uint32 *data, size_t nbytes ); +LEPT_DLL extern L_STACK * lstackCreate ( l_int32 n ); +LEPT_DLL extern void lstackDestroy ( L_STACK **plstack, l_int32 freeflag ); +LEPT_DLL extern l_ok lstackAdd ( L_STACK *lstack, void *item ); +LEPT_DLL extern void * lstackRemove ( L_STACK *lstack ); +LEPT_DLL extern l_int32 lstackGetCount ( L_STACK *lstack ); +LEPT_DLL extern l_ok lstackPrint ( FILE *fp, L_STACK *lstack ); +LEPT_DLL extern L_STRCODE * strcodeCreate ( l_int32 fileno ); +LEPT_DLL extern l_ok strcodeCreateFromFile ( const char *filein, l_int32 fileno, const char *outdir ); +LEPT_DLL extern l_ok strcodeGenerate ( L_STRCODE *strcode, const char *filein, const char *type ); +LEPT_DLL extern l_int32 strcodeFinalize ( L_STRCODE **pstrcode, const char *outdir ); +LEPT_DLL extern l_int32 l_getStructStrFromFile ( const char *filename, l_int32 field, char **pstr ); +LEPT_DLL extern l_ok pixFindStrokeLength ( PIX *pixs, l_int32 *tab8, l_int32 *plength ); +LEPT_DLL extern l_ok pixFindStrokeWidth ( PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto ); +LEPT_DLL extern NUMA * pixaFindStrokeWidth ( PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug ); +LEPT_DLL extern PIXA * pixaModifyStrokeWidth ( PIXA *pixas, l_float32 targetw ); +LEPT_DLL extern PIX * pixModifyStrokeWidth ( PIX *pixs, l_float32 width, l_float32 targetw ); +LEPT_DLL extern PIXA * pixaSetStrokeWidth ( PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity ); +LEPT_DLL extern PIX * pixSetStrokeWidth ( PIX *pixs, l_int32 width, l_int32 thinfirst, l_int32 connectivity ); +LEPT_DLL extern l_int32 * sudokuReadFile ( const char *filename ); +LEPT_DLL extern l_int32 * sudokuReadString ( const char *str ); +LEPT_DLL extern L_SUDOKU * sudokuCreate ( l_int32 *array ); +LEPT_DLL extern void sudokuDestroy ( L_SUDOKU **psud ); +LEPT_DLL extern l_int32 sudokuSolve ( L_SUDOKU *sud ); +LEPT_DLL extern l_ok sudokuTestUniqueness ( l_int32 *array, l_int32 *punique ); +LEPT_DLL extern L_SUDOKU * sudokuGenerate ( l_int32 *array, l_int32 seed, l_int32 minelems, l_int32 maxtries ); +LEPT_DLL extern l_int32 sudokuOutput ( L_SUDOKU *sud, l_int32 arraytype ); +LEPT_DLL extern PIX * pixAddSingleTextblock ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location, l_int32 *poverflow ); +LEPT_DLL extern PIX * pixAddTextlines ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); +LEPT_DLL extern l_ok pixSetTextblock ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 x0, l_int32 y0, l_int32 wtext, l_int32 firstindent, l_int32 *poverflow ); +LEPT_DLL extern l_ok pixSetTextline ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 x0, l_int32 y0, l_int32 *pwidth, l_int32 *poverflow ); +LEPT_DLL extern PIXA * pixaAddTextNumber ( PIXA *pixas, L_BMF *bmf, NUMA *na, l_uint32 val, l_int32 location ); +LEPT_DLL extern PIXA * pixaAddTextlines ( PIXA *pixas, L_BMF *bmf, SARRAY *sa, l_uint32 val, l_int32 location ); +LEPT_DLL extern l_ok pixaAddPixWithText ( PIXA *pixa, PIX *pixs, l_int32 reduction, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); +LEPT_DLL extern SARRAY * bmfGetLineStrings ( L_BMF *bmf, const char *textstr, l_int32 maxw, l_int32 firstindent, l_int32 *ph ); +LEPT_DLL extern NUMA * bmfGetWordWidths ( L_BMF *bmf, const char *textstr, SARRAY *sa ); +LEPT_DLL extern l_ok bmfGetStringWidth ( L_BMF *bmf, const char *textstr, l_int32 *pw ); +LEPT_DLL extern SARRAY * splitStringToParagraphs ( char *textstr, l_int32 splitflag ); +LEPT_DLL extern PIX * pixReadTiff ( const char *filename, l_int32 n ); +LEPT_DLL extern PIX * pixReadStreamTiff ( FILE *fp, l_int32 n ); +LEPT_DLL extern l_ok pixWriteTiff ( const char *filename, PIX *pix, l_int32 comptype, const char *modestr ); +LEPT_DLL extern l_ok pixWriteTiffCustom ( const char *filename, PIX *pix, l_int32 comptype, const char *modestr, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes ); +LEPT_DLL extern l_ok pixWriteStreamTiff ( FILE *fp, PIX *pix, l_int32 comptype ); +LEPT_DLL extern l_ok pixWriteStreamTiffWA ( FILE *fp, PIX *pix, l_int32 comptype, const char *modestr ); +LEPT_DLL extern PIX * pixReadFromMultipageTiff ( const char *fname, size_t *poffset ); +LEPT_DLL extern PIXA * pixaReadMultipageTiff ( const char *filename ); +LEPT_DLL extern l_ok pixaWriteMultipageTiff ( const char *fname, PIXA *pixa ); +LEPT_DLL extern l_ok writeMultipageTiff ( const char *dirin, const char *substr, const char *fileout ); +LEPT_DLL extern l_ok writeMultipageTiffSA ( SARRAY *sa, const char *fileout ); +LEPT_DLL extern l_ok fprintTiffInfo ( FILE *fpout, const char *tiffile ); +LEPT_DLL extern l_ok tiffGetCount ( FILE *fp, l_int32 *pn ); +LEPT_DLL extern l_ok getTiffResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); +LEPT_DLL extern l_ok readHeaderTiff ( const char *filename, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); +LEPT_DLL extern l_ok freadHeaderTiff ( FILE *fp, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); +LEPT_DLL extern l_ok readHeaderMemTiff ( const l_uint8 *cdata, size_t size, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); +LEPT_DLL extern l_ok findTiffCompression ( FILE *fp, l_int32 *pcomptype ); +LEPT_DLL extern l_ok extractG4DataFromFile ( const char *filein, l_uint8 **pdata, size_t *pnbytes, l_int32 *pw, l_int32 *ph, l_int32 *pminisblack ); +LEPT_DLL extern PIX * pixReadMemTiff ( const l_uint8 *cdata, size_t size, l_int32 n ); +LEPT_DLL extern PIX * pixReadMemFromMultipageTiff ( const l_uint8 *cdata, size_t size, size_t *poffset ); +LEPT_DLL extern PIXA * pixaReadMemMultipageTiff ( const l_uint8 *data, size_t size ); +LEPT_DLL extern l_ok pixaWriteMemMultipageTiff ( l_uint8 **pdata, size_t *psize, PIXA *pixa ); +LEPT_DLL extern l_ok pixWriteMemTiff ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype ); +LEPT_DLL extern l_ok pixWriteMemTiffCustom ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes ); +LEPT_DLL extern l_int32 setMsgSeverity ( l_int32 newsev ); +LEPT_DLL extern l_int32 returnErrorInt ( const char *msg, const char *procname, l_int32 ival ); +LEPT_DLL extern l_float32 returnErrorFloat ( const char *msg, const char *procname, l_float32 fval ); +LEPT_DLL extern void * returnErrorPtr ( const char *msg, const char *procname, void *pval ); +LEPT_DLL extern l_ok filesAreIdentical ( const char *fname1, const char *fname2, l_int32 *psame ); +LEPT_DLL extern l_uint16 convertOnLittleEnd16 ( l_uint16 shortin ); +LEPT_DLL extern l_uint16 convertOnBigEnd16 ( l_uint16 shortin ); +LEPT_DLL extern l_uint32 convertOnLittleEnd32 ( l_uint32 wordin ); +LEPT_DLL extern l_uint32 convertOnBigEnd32 ( l_uint32 wordin ); +LEPT_DLL extern l_ok fileCorruptByDeletion ( const char *filein, l_float32 loc, l_float32 size, const char *fileout ); +LEPT_DLL extern l_ok fileCorruptByMutation ( const char *filein, l_float32 loc, l_float32 size, const char *fileout ); +LEPT_DLL extern l_ok fileReplaceBytes ( const char *filein, l_int32 start, l_int32 nbytes, l_uint8 *newdata, size_t newsize, const char *fileout ); +LEPT_DLL extern l_ok genRandomIntegerInRange ( l_int32 range, l_int32 seed, l_int32 *pval ); +LEPT_DLL extern l_int32 lept_roundftoi ( l_float32 fval ); +LEPT_DLL extern l_ok l_hashStringToUint64 ( const char *str, l_uint64 *phash ); +LEPT_DLL extern l_ok l_hashPtToUint64 ( l_int32 x, l_int32 y, l_uint64 *phash ); +LEPT_DLL extern l_ok l_hashFloat64ToUint64 ( l_int32 nbuckets, l_float64 val, l_uint64 *phash ); +LEPT_DLL extern l_ok findNextLargerPrime ( l_int32 start, l_uint32 *pprime ); +LEPT_DLL extern l_ok lept_isPrime ( l_uint64 n, l_int32 *pis_prime, l_uint32 *pfactor ); +LEPT_DLL extern l_uint32 convertIntToGrayCode ( l_uint32 val ); +LEPT_DLL extern l_uint32 convertGrayCodeToInt ( l_uint32 val ); +LEPT_DLL extern char * getLeptonicaVersion ( ); +LEPT_DLL extern void startTimer ( void ); +LEPT_DLL extern l_float32 stopTimer ( void ); +LEPT_DLL extern L_TIMER startTimerNested ( void ); +LEPT_DLL extern l_float32 stopTimerNested ( L_TIMER rusage_start ); +LEPT_DLL extern void l_getCurrentTime ( l_int32 *sec, l_int32 *usec ); +LEPT_DLL extern L_WALLTIMER * startWallTimer ( void ); +LEPT_DLL extern l_float32 stopWallTimer ( L_WALLTIMER **ptimer ); +LEPT_DLL extern char * l_getFormattedDate ( ); +LEPT_DLL extern char * stringNew ( const char *src ); +LEPT_DLL extern l_ok stringCopy ( char *dest, const char *src, l_int32 n ); +LEPT_DLL extern char * stringCopySegment ( const char *src, l_int32 start, l_int32 nbytes ); +LEPT_DLL extern l_ok stringReplace ( char **pdest, const char *src ); +LEPT_DLL extern l_int32 stringLength ( const char *src, size_t size ); +LEPT_DLL extern l_int32 stringCat ( char *dest, size_t size, const char *src ); +LEPT_DLL extern char * stringConcatNew ( const char *first, ... ); +LEPT_DLL extern char * stringJoin ( const char *src1, const char *src2 ); +LEPT_DLL extern l_ok stringJoinIP ( char **psrc1, const char *src2 ); +LEPT_DLL extern char * stringReverse ( const char *src ); +LEPT_DLL extern char * strtokSafe ( char *cstr, const char *seps, char **psaveptr ); +LEPT_DLL extern l_ok stringSplitOnToken ( char *cstr, const char *seps, char **phead, char **ptail ); +LEPT_DLL extern l_ok stringCheckForChars ( const char *src, const char *chars, l_int32 *pfound ); +LEPT_DLL extern char * stringRemoveChars ( const char *src, const char *remchars ); +LEPT_DLL extern char * stringReplaceEachSubstr ( const char *src, const char *sub1, const char *sub2, l_int32 *pcount ); +LEPT_DLL extern char * stringReplaceSubstr ( const char *src, const char *sub1, const char *sub2, l_int32 *ploc, l_int32 *pfound ); +LEPT_DLL extern L_DNA * stringFindEachSubstr ( const char *src, const char *sub ); +LEPT_DLL extern l_int32 stringFindSubstr ( const char *src, const char *sub, l_int32 *ploc ); +LEPT_DLL extern l_uint8 * arrayReplaceEachSequence ( const l_uint8 *datas, size_t dataslen, const l_uint8 *seq, size_t seqlen, const l_uint8 *newseq, size_t newseqlen, size_t *pdatadlen, l_int32 *pcount ); +LEPT_DLL extern L_DNA * arrayFindEachSequence ( const l_uint8 *data, size_t datalen, const l_uint8 *sequence, size_t seqlen ); +LEPT_DLL extern l_ok arrayFindSequence ( const l_uint8 *data, size_t datalen, const l_uint8 *sequence, size_t seqlen, l_int32 *poffset, l_int32 *pfound ); +LEPT_DLL extern void * reallocNew ( void **pindata, l_int32 oldsize, l_int32 newsize ); +LEPT_DLL extern l_uint8 * l_binaryRead ( const char *filename, size_t *pnbytes ); +LEPT_DLL extern l_uint8 * l_binaryReadStream ( FILE *fp, size_t *pnbytes ); +LEPT_DLL extern l_uint8 * l_binaryReadSelect ( const char *filename, size_t start, size_t nbytes, size_t *pnread ); +LEPT_DLL extern l_uint8 * l_binaryReadSelectStream ( FILE *fp, size_t start, size_t nbytes, size_t *pnread ); +LEPT_DLL extern l_ok l_binaryWrite ( const char *filename, const char *operation, const void *data, size_t nbytes ); +LEPT_DLL extern size_t nbytesInFile ( const char *filename ); +LEPT_DLL extern size_t fnbytesInFile ( FILE *fp ); +LEPT_DLL extern l_uint8 * l_binaryCopy ( const l_uint8 *datas, size_t size ); +LEPT_DLL extern l_ok l_binaryCompare ( const l_uint8 *data1, size_t size1, const l_uint8 *data2, size_t size2, l_int32 *psame ); +LEPT_DLL extern l_ok fileCopy ( const char *srcfile, const char *newfile ); +LEPT_DLL extern l_ok fileConcatenate ( const char *srcfile, const char *destfile ); +LEPT_DLL extern l_ok fileAppendString ( const char *filename, const char *str ); +LEPT_DLL extern FILE * fopenReadStream ( const char *filename ); +LEPT_DLL extern FILE * fopenWriteStream ( const char *filename, const char *modestring ); +LEPT_DLL extern FILE * fopenReadFromMemory ( const l_uint8 *data, size_t size ); +LEPT_DLL extern FILE * fopenWriteWinTempfile ( ); +LEPT_DLL extern FILE * lept_fopen ( const char *filename, const char *mode ); +LEPT_DLL extern l_ok lept_fclose ( FILE *fp ); +LEPT_DLL extern void * lept_calloc ( size_t nmemb, size_t size ); +LEPT_DLL extern void lept_free ( void *ptr ); +LEPT_DLL extern l_int32 lept_mkdir ( const char *subdir ); +LEPT_DLL extern l_int32 lept_rmdir ( const char *subdir ); +LEPT_DLL extern void lept_direxists ( const char *dir, l_int32 *pexists ); +LEPT_DLL extern l_int32 lept_rm_match ( const char *subdir, const char *substr ); +LEPT_DLL extern l_int32 lept_rm ( const char *subdir, const char *tail ); +LEPT_DLL extern l_int32 lept_rmfile ( const char *filepath ); +LEPT_DLL extern l_int32 lept_mv ( const char *srcfile, const char *newdir, const char *newtail, char **pnewpath ); +LEPT_DLL extern l_int32 lept_cp ( const char *srcfile, const char *newdir, const char *newtail, char **pnewpath ); +LEPT_DLL extern void callSystemDebug ( const char *cmd ); +LEPT_DLL extern l_ok splitPathAtDirectory ( const char *pathname, char **pdir, char **ptail ); +LEPT_DLL extern l_ok splitPathAtExtension ( const char *pathname, char **pbasename, char **pextension ); +LEPT_DLL extern char * pathJoin ( const char *dir, const char *fname ); +LEPT_DLL extern char * appendSubdirs ( const char *basedir, const char *subdirs ); +LEPT_DLL extern l_ok convertSepCharsInPath ( char *path, l_int32 type ); +LEPT_DLL extern char * genPathname ( const char *dir, const char *fname ); +LEPT_DLL extern l_ok makeTempDirname ( char *result, size_t nbytes, const char *subdir ); +LEPT_DLL extern l_ok modifyTrailingSlash ( char *path, size_t nbytes, l_int32 flag ); +LEPT_DLL extern char * l_makeTempFilename ( ); +LEPT_DLL extern l_int32 extractNumberFromFilename ( const char *fname, l_int32 numpre, l_int32 numpost ); +LEPT_DLL extern PIX * pixSimpleCaptcha ( PIX *pixs, l_int32 border, l_int32 nterms, l_uint32 seed, l_uint32 color, l_int32 cmapflag ); +LEPT_DLL extern PIX * pixRandomHarmonicWarp ( PIX *pixs, l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_int32 nx, l_int32 ny, l_uint32 seed, l_int32 grayval ); +LEPT_DLL extern PIX * pixWarpStereoscopic ( PIX *pixs, l_int32 zbend, l_int32 zshiftt, l_int32 zshiftb, l_int32 ybendt, l_int32 ybendb, l_int32 redleft ); +LEPT_DLL extern PIX * pixStretchHorizontal ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 operation, l_int32 incolor ); +LEPT_DLL extern PIX * pixStretchHorizontalSampled ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 incolor ); +LEPT_DLL extern PIX * pixStretchHorizontalLI ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 incolor ); +LEPT_DLL extern PIX * pixQuadraticVShear ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 operation, l_int32 incolor ); +LEPT_DLL extern PIX * pixQuadraticVShearSampled ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 incolor ); +LEPT_DLL extern PIX * pixQuadraticVShearLI ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 incolor ); +LEPT_DLL extern PIX * pixStereoFromPair ( PIX *pix1, PIX *pix2, l_float32 rwt, l_float32 gwt, l_float32 bwt ); +LEPT_DLL extern L_WSHED * wshedCreate ( PIX *pixs, PIX *pixm, l_int32 mindepth, l_int32 debugflag ); +LEPT_DLL extern void wshedDestroy ( L_WSHED **pwshed ); +LEPT_DLL extern l_ok wshedApply ( L_WSHED *wshed ); +LEPT_DLL extern l_ok wshedBasins ( L_WSHED *wshed, PIXA **ppixa, NUMA **pnalevels ); +LEPT_DLL extern PIX * wshedRenderFill ( L_WSHED *wshed ); +LEPT_DLL extern PIX * wshedRenderColors ( L_WSHED *wshed ); +LEPT_DLL extern l_ok pixaWriteWebPAnim ( const char *filename, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern l_ok pixaWriteStreamWebPAnim ( FILE *fp, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern l_ok pixaWriteMemWebPAnim ( l_uint8 **pencdata, size_t *pencsize, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern PIX * pixReadStreamWebP ( FILE *fp ); +LEPT_DLL extern PIX * pixReadMemWebP ( const l_uint8 *filedata, size_t filesize ); +LEPT_DLL extern l_ok readHeaderWebP ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pspp ); +LEPT_DLL extern l_ok readHeaderMemWebP ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pspp ); +LEPT_DLL extern l_ok pixWriteWebP ( const char *filename, PIX *pixs, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern l_ok pixWriteStreamWebP ( FILE *fp, PIX *pixs, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern l_ok pixWriteMemWebP ( l_uint8 **pencdata, size_t *pencsize, PIX *pixs, l_int32 quality, l_int32 lossless ); +LEPT_DLL extern l_int32 l_jpegSetQuality ( l_int32 new_quality ); +LEPT_DLL extern void setLeptDebugOK ( l_int32 allow ); +LEPT_DLL extern l_ok pixaWriteFiles ( const char *rootname, PIXA *pixa, l_int32 format ); +LEPT_DLL extern l_ok pixWriteDebug ( const char *fname, PIX *pix, l_int32 format ); +LEPT_DLL extern l_ok pixWrite ( const char *fname, PIX *pix, l_int32 format ); +LEPT_DLL extern l_ok pixWriteAutoFormat ( const char *filename, PIX *pix ); +LEPT_DLL extern l_ok pixWriteStream ( FILE *fp, PIX *pix, l_int32 format ); +LEPT_DLL extern l_ok pixWriteImpliedFormat ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive ); +LEPT_DLL extern l_int32 pixChooseOutputFormat ( PIX *pix ); +LEPT_DLL extern l_int32 getImpliedFileFormat ( const char *filename ); +LEPT_DLL extern l_ok pixGetAutoFormat ( PIX *pix, l_int32 *pformat ); +LEPT_DLL extern const char * getFormatExtension ( l_int32 format ); +LEPT_DLL extern l_ok pixWriteMem ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 format ); +LEPT_DLL extern l_ok l_fileDisplay ( const char *fname, l_int32 x, l_int32 y, l_float32 scale ); +LEPT_DLL extern l_ok pixDisplay ( PIX *pixs, l_int32 x, l_int32 y ); +LEPT_DLL extern l_ok pixDisplayWithTitle ( PIX *pixs, l_int32 x, l_int32 y, const char *title, l_int32 dispflag ); +LEPT_DLL extern l_ok pixSaveTiled ( PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 dp ); +LEPT_DLL extern l_ok pixSaveTiledOutline ( PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp ); +LEPT_DLL extern l_ok pixSaveTiledWithText ( PIX *pixs, PIXA *pixa, l_int32 outwidth, l_int32 newrow, l_int32 space, l_int32 linewidth, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); +LEPT_DLL extern void l_chooseDisplayProg ( l_int32 selection ); +LEPT_DLL extern l_ok pixDisplayWrite ( PIX *pixs, l_int32 reduction ); +LEPT_DLL extern l_uint8 * zlibCompress ( l_uint8 *datain, size_t nin, size_t *pnout ); +LEPT_DLL extern l_uint8 * zlibUncompress ( l_uint8 *datain, size_t nin, size_t *pnout ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NO_PROTOS */ + + +#endif /* LEPTONICA_ALLHEADERS_H */ + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/alltypes.h b/hgdriver/3rdparty/hgOCR/leptonica/alltypes.h new file mode 100644 index 0000000..a84c0bf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/alltypes.h @@ -0,0 +1,66 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_ALLTYPES_H +#define LEPTONICA_ALLTYPES_H + + /* Standard */ +#include +#include +#include + + /* General and configuration defs */ +#include "endianness.h" +#include "environ.h" + + /* Generic and non-image-specific containers */ +#include "array.h" +#include "bbuffer.h" +#include "heap.h" +#include "list.h" +#include "ptra.h" +#include "queue.h" +#include "rbtree.h" +#include "stack.h" + + /* Imaging */ +#include "arrayaccess.h" +#include "bmf.h" +#include "ccbord.h" +#include "dewarp.h" +#include "gplot.h" +#include "imageio.h" +#include "jbclass.h" +#include "morph.h" +#include "pix.h" +#include "recog.h" +#include "regutils.h" +#include "stringcode.h" +#include "sudoku.h" +#include "watershed.h" + + +#endif /* LEPTONICA_ALLTYPES_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/array.h b/hgdriver/3rdparty/hgOCR/leptonica/array.h new file mode 100644 index 0000000..5c13977 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/array.h @@ -0,0 +1,158 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_ARRAY_H +#define LEPTONICA_ARRAY_H + +/*! + * \file array.h + * + *
+ *  Contains the following structs:
+ *      struct Numa
+ *      struct Numaa
+ *      struct L_Dna
+ *      struct L_Dnaa
+ *      struct L_DnaHash
+ *      struct Sarray
+ *      struct L_Bytea
+ *
+ *  Contains definitions for:
+ *      Numa interpolation flags
+ *      Numa and FPix border flags
+ *      Numa data type conversion to string
+ * 
+ */ + + +/*------------------------------------------------------------------------* + * Array Structs * + *------------------------------------------------------------------------*/ + +/*! Numa version for serialization */ +#define NUMA_VERSION_NUMBER 1 + + /*! Number array: an array of floats */ +struct Numa +{ + l_int32 nalloc; /*!< size of allocated number array */ + l_int32 n; /*!< number of numbers saved */ + l_int32 refcount; /*!< reference count (1 if no clones) */ + l_float32 startx; /*!< x value assigned to array[0] */ + l_float32 delx; /*!< change in x value as i --> i + 1 */ + l_float32 *array; /*!< number array */ +}; +typedef struct Numa NUMA; + + /*! Array of number arrays */ +struct Numaa +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 n; /*!< number of Numa saved */ + struct Numa **numa; /*!< array of Numa */ +}; +typedef struct Numaa NUMAA; + +/*! Dna version for serialization */ +#define DNA_VERSION_NUMBER 1 + + /*! Double number array: an array of doubles */ +struct L_Dna +{ + l_int32 nalloc; /*!< size of allocated number array */ + l_int32 n; /*!< number of numbers saved */ + l_int32 refcount; /*!< reference count (1 if no clones) */ + l_float64 startx; /*!< x value assigned to array[0] */ + l_float64 delx; /*!< change in x value as i --> i + 1 */ + l_float64 *array; /*!< number array */ +}; +typedef struct L_Dna L_DNA; + + /*! Array of double number arrays */ +struct L_Dnaa +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 n; /*!< number of L_Dna saved */ + struct L_Dna **dna; /*!< array of L_Dna */ +}; +typedef struct L_Dnaa L_DNAA; + + /*! A hash table of Dnas */ +struct L_DnaHash +{ + l_int32 nbuckets; + l_int32 initsize; /*!< initial size of each dna that is made */ + struct L_Dna **dna; /*!< array of L_Dna */ +}; +typedef struct L_DnaHash L_DNAHASH; + +/*! Sarray version for serialization */ +#define SARRAY_VERSION_NUMBER 1 + + /*! String array: an array of C strings */ +struct Sarray +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 n; /*!< number of strings allocated */ + l_int32 refcount; /*!< reference count (1 if no clones) */ + char **array; /*!< string array */ +}; +typedef struct Sarray SARRAY; + + /*! Byte array (analogous to C++ "string") */ +struct L_Bytea +{ + size_t nalloc; /*!< number of bytes allocated in data array */ + size_t size; /*!< number of bytes presently used */ + l_int32 refcount; /*!< reference count (1 if no clones) */ + l_uint8 *data; /*!< data array */ +}; +typedef struct L_Bytea L_BYTEA; + + +/*------------------------------------------------------------------------* + * Array flags * + *------------------------------------------------------------------------*/ +/*! Numa Interpolation */ +enum { + L_LINEAR_INTERP = 1, /*!< linear */ + L_QUADRATIC_INTERP = 2 /*!< quadratic */ +}; + +/*! Border Adding */ +enum { + L_CONTINUED_BORDER = 1, /*!< extended with same value */ + L_SLOPE_BORDER = 2, /*!< extended with constant normal derivative */ + L_MIRRORED_BORDER = 3 /*!< mirrored */ +}; + +/*! Numa Data Conversion */ +enum { + L_INTEGER_VALUE = 1, /*!< convert to integer */ + L_FLOAT_VALUE = 2 /*!< convert to float */ +}; + +#endif /* LEPTONICA_ARRAY_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.c b/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.c new file mode 100644 index 0000000..69cdb6f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.c @@ -0,0 +1,364 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file arrayaccess.c + *
+ *
+ *     Access within an array of 32-bit words
+ *
+ *           l_int32     l_getDataBit()
+ *           void        l_setDataBit()
+ *           void        l_clearDataBit()
+ *           void        l_setDataBitVal()
+ *           l_int32     l_getDataDibit()
+ *           void        l_setDataDibit()
+ *           void        l_clearDataDibit()
+ *           l_int32     l_getDataQbit()
+ *           void        l_setDataQbit()
+ *           void        l_clearDataQbit()
+ *           l_int32     l_getDataByte()
+ *           void        l_setDataByte()
+ *           l_int32     l_getDataTwoBytes()
+ *           void        l_setDataTwoBytes()
+ *           l_int32     l_getDataFourBytes()
+ *           void        l_setDataFourBytes()
+ *
+ *     Note that these all require 32-bit alignment, and hence an input
+ *     ptr to l_uint32.  However, this is not enforced by the compiler.
+ *     Instead, we allow the use of a void* ptr, because the line ptrs
+ *     are an efficient way to get random access (see pixGetLinePtrs()).
+ *     It is then necessary to cast internally within each function
+ *     because ptr arithmetic requires knowing the size of the units
+ *     being referenced.
+ * 
+ */ + +#include "allheaders.h" + + +/*----------------------------------------------------------------------* + * Access within an array of 32-bit words * + *----------------------------------------------------------------------*/ +/*! + * \brief l_getDataBit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return val of the nth 1-bit pixel. + */ +l_int32 +l_getDataBit(const void *line, + l_int32 n) +{ + return (*((const l_uint32 *)line + (n >> 5)) >> (31 - (n & 31))) & 1; +} + + +/*! + * \brief l_setDataBit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return void + * + * Action: sets the pixel to 1 + */ +void +l_setDataBit(void *line, + l_int32 n) +{ + *((l_uint32 *)line + (n >> 5)) |= (0x80000000 >> (n & 31)); +} + + +/*! + * \brief l_clearDataBit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return void + * + * Action: sets the 1-bit pixel to 0 + */ +void +l_clearDataBit(void *line, + l_int32 n) +{ + *((l_uint32 *)line + (n >> 5)) &= ~(0x80000000 >> (n & 31)); +} + + +/*! + * \brief l_setDataBitVal() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 or 1 + * \return void + * + *
+ * Notes:
+ *      (1) This is an accessor for a 1 bpp pix.
+ *      (2) It is actually a little slower than using:
+ *            if (val == 0)
+ *                l_ClearDataBit(line, n);
+ *            else
+ *                l_SetDataBit(line, n);
+ * 
+ */ +void +l_setDataBitVal(void *line, + l_int32 n, + l_int32 val) +{ +l_uint32 *pword; + + pword = (l_uint32 *)line + (n >> 5); + *pword &= ~(0x80000000 >> (n & 31)); /* clear */ + *pword |= (l_uint32)val << (31 - (n & 31)); /* set */ + return; +} + + +/*! + * \brief l_getDataDibit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return val of the nth 2-bit pixel. + */ +l_int32 +l_getDataDibit(const void *line, + l_int32 n) +{ + return (*((const l_uint32 *)line + (n >> 4)) >> (2 * (15 - (n & 15)))) & 3; +} + + +/*! + * \brief l_setDataDibit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 - 3 + * \return void + */ +void +l_setDataDibit(void *line, + l_int32 n, + l_int32 val) +{ +l_uint32 *pword; + + pword = (l_uint32 *)line + (n >> 4); + *pword &= ~(0xc0000000 >> (2 * (n & 15))); /* clear */ + *pword |= (l_uint32)(val & 3) << (30 - 2 * (n & 15)); /* set */ + return; +} + + +/*! + * \brief l_clearDataDibit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return void + * + * Action: sets the 2-bit pixel to 0 + */ +void +l_clearDataDibit(void *line, + l_int32 n) +{ + *((l_uint32 *)line + (n >> 4)) &= ~(0xc0000000 >> (2 * (n & 15))); +} + + +/*! + * \brief l_getDataQbit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return val of the nth 4-bit pixel. + */ +l_int32 +l_getDataQbit(const void *line, + l_int32 n) +{ + return (*((const l_uint32 *)line + (n >> 3)) >> (4 * (7 - (n & 7)))) & 0xf; +} + + +/*! + * \brief l_setDataQbit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 - 0xf + * \return void + */ +void +l_setDataQbit(void *line, + l_int32 n, + l_int32 val) +{ +l_uint32 *pword; + + pword = (l_uint32 *)line + (n >> 3); + *pword &= ~(0xf0000000 >> (4 * (n & 7))); /* clear */ + *pword |= (l_uint32)(val & 15) << (28 - 4 * (n & 7)); /* set */ + return; +} + + +/*! + * \brief l_clearDataQbit() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return void + * + * Action: sets the 4-bit pixel to 0 + */ +void +l_clearDataQbit(void *line, + l_int32 n) +{ + *((l_uint32 *)line + (n >> 3)) &= ~(0xf0000000 >> (4 * (n & 7))); +} + + +/*! + * \brief l_getDataByte() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return value of the n-th byte pixel + */ +l_int32 +l_getDataByte(const void *line, + l_int32 n) +{ +#ifdef L_BIG_ENDIAN + return *((const l_uint8 *)line + n); +#else /* L_LITTLE_ENDIAN */ + return *(l_uint8 *)((l_uintptr_t)((const l_uint8 *)line + n) ^ 3); +#endif /* L_BIG_ENDIAN */ +} + + +/*! + * \brief l_setDataByte() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 - 0xff + * \return void + */ +void +l_setDataByte(void *line, + l_int32 n, + l_int32 val) +{ +#ifdef L_BIG_ENDIAN + *((l_uint8 *)line + n) = val; +#else /* L_LITTLE_ENDIAN */ + *(l_uint8 *)((l_uintptr_t)((l_uint8 *)line + n) ^ 3) = val; +#endif /* L_BIG_ENDIAN */ +} + + +/*! + * \brief l_getDataTwoBytes() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return value of the n-th 2-byte pixel + */ +l_int32 +l_getDataTwoBytes(const void *line, + l_int32 n) +{ +#ifdef L_BIG_ENDIAN + return *((const l_uint16 *)line + n); +#else /* L_LITTLE_ENDIAN */ + return *(l_uint16 *)((l_uintptr_t)((const l_uint16 *)line + n) ^ 2); +#endif /* L_BIG_ENDIAN */ +} + + +/*! + * \brief l_setDataTwoBytes() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 - 0xffff + * \return void + */ +void +l_setDataTwoBytes(void *line, + l_int32 n, + l_int32 val) +{ +#ifdef L_BIG_ENDIAN + *((l_uint16 *)line + n) = val; +#else /* L_LITTLE_ENDIAN */ + *(l_uint16 *)((l_uintptr_t)((l_uint16 *)line + n) ^ 2) = val; +#endif /* L_BIG_ENDIAN */ +} + + +/*! + * \brief l_getDataFourBytes() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \return value of the n-th 4-byte pixel + */ +l_int32 +l_getDataFourBytes(const void *line, + l_int32 n) +{ + return *((const l_uint32 *)line + n); +} + + +/*! + * \brief l_setDataFourBytes() + * + * \param[in] line ptr to beginning of data line + * \param[in] n pixel index + * \param[in] val val to be inserted: 0 - 0xffffffff + * \return void + */ +void +l_setDataFourBytes(void *line, + l_int32 n, + l_int32 val) +{ + *((l_uint32 *)line + n) = val; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.h b/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.h new file mode 100644 index 0000000..1a831bc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/arrayaccess.h @@ -0,0 +1,270 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_ARRAY_ACCESS_H +#define LEPTONICA_ARRAY_ACCESS_H + +/*! + * \file arrayaccess.h + * + *
+ *  1, 2, 4, 8, 16 and 32 bit data access within an array of 32-bit words
+ *
+ *  This is used primarily to access 1, 2, 4, 8, 16 and 32 bit pixels
+ *  in a line of image data, represented as an array of 32-bit words.
+ *
+ *     pdata:  pointer to first 32-bit word in the array
+ *     n:      index of the pixel in the array
+ *
+ *  Function calls for these accessors are defined in arrayaccess.c.
+ *
+ *  However, for efficiency we use the inline macros for all accesses.
+ *  Even though the 2 and 4 bit set* accessors are more complicated,
+ *  they are about 10% faster than the function calls.
+ *
+ *  The 32 bit access is just a cast and ptr arithmetic.  We include
+ *  it so that the input ptr can be void*.
+ *
+ *  At the end of this file is code for invoking the function calls
+ *  instead of inlining.
+ *
+ *  The macro SET_DATA_BIT_VAL(pdata, n, val) is a bit slower than
+ *      if (val == 0)
+ *          CLEAR_DATA_BIT(pdata, n);
+ *      else
+ *          SET_DATA_BIT(pdata, n);
+ *
+ *  Some compilers complain when the SET macros are surrounded by
+ *  parentheses, because parens require an evaluation and it is not
+ *  defined for SET macros.  If SET_DATA_QBIT were defined as a
+ *  compound macro, in analogy to l_setDataQbit(), it requires
+ *  surrounding braces:
+ * \code
+ *     #define  SET_DATA_QBIT(pdata, n, val) \
+ *        {l_uint32 *_TEMP_WORD_PTR_; \
+ *         _TEMP_WORD_PTR_ = (l_uint32 *)(pdata) + ((n) >> 3); \
+ *         *_TEMP_WORD_PTR_ &= ~(0xf0000000 >> (4 * ((n) & 7))); \
+ *         *_TEMP_WORD_PTR_ |= (((val) & 15) << (28 - 4 * ((n) & 7)));}
+ * \endcode
+ *  but if used in an if/else
+ * \code
+ *      if (x)
+ *         SET_DATA_QBIT(...);
+ *      else
+ *         ...
+ * \endcode
+ *  the compiler sees
+ * \code
+ *      if (x)
+ *         {......};
+ *      else
+ *         ...
+ * \endcode
+ *  The semicolon comes after the brace and will not compile.
+ *  This can be fixed in the call by either omitting the semicolon
+ *  or requiring another set of braces around SET_DATA_QBIT(), but
+ *  both these options break compatibility with current code, and
+ *  require special attention by anyone using the macros.
+ *
+ *  There are (at least) two ways to fix this in the macro definitions,
+ *  suggested by Dave Bryan.
+ *  (1) Surround the braces in the macro above with
+ *         do {....} while(0)
+ *      Then the semicolon just terminates the expression.
+ *  (2) Reduce the blocks to a single expression; e.g,
+ *         *((l_uint32 *)(pdata) + ((n) >> 3)) = \
+ *           *((l_uint32 *)(pdata) + ((n) >> 3)) \
+ *           & ~(0xf0000000 >> (4 * ((n) & 7))) \
+ *           | (((val) & 15) << (28 - 4 * ((n) & 7)))
+ *      This appears to cause redundant computation, but the compiler
+ *      should evaluate the common subexpression only once.
+ *  All these methods have the same performance, giving about 300M
+ *  SET_DATA_QBIT operations per second on a fast 64 bit system.
+ *  Using the function calls instead of the macros results in about 250M
+ *  SET_DATA_QBIT operations per second, a performance hit of nearly 20%.
+ * 
+ */ + +#define USE_INLINE_ACCESSORS 1 + +#if USE_INLINE_ACCESSORS + + /*=============================================================*/ + /* Faster: use in line accessors */ + /*=============================================================*/ + + /*--------------------------------------------------* + * 1 bit access * + *--------------------------------------------------*/ +/*! 1 bit access - get */ +#define GET_DATA_BIT(pdata, n) \ + ((*((const l_uint32 *)(pdata) + ((n) >> 5)) >> (31 - ((n) & 31))) & 1) + +/*! 1 bit access - set */ +#define SET_DATA_BIT(pdata, n) \ + *((l_uint32 *)(pdata) + ((n) >> 5)) |= (0x80000000 >> ((n) & 31)) + +/*! 1 bit access - clear */ +#define CLEAR_DATA_BIT(pdata, n) \ + *((l_uint32 *)(pdata) + ((n) >> 5)) &= ~(0x80000000 >> ((n) & 31)) + +/*! 1 bit access - set value (0 or 1) */ +#define SET_DATA_BIT_VAL(pdata, n, val) \ + *((l_uint32 *)(pdata) + ((n) >> 5)) = \ + ((*((l_uint32 *)(pdata) + ((n) >> 5)) \ + & (~(0x80000000 >> ((n) & 31)))) \ + | ((l_uint32)(val) << (31 - ((n) & 31)))) + + /*--------------------------------------------------* + * 2 bit access * + *--------------------------------------------------*/ +/*! 2 bit access - get */ +#define GET_DATA_DIBIT(pdata, n) \ + ((*((const l_uint32 *)(pdata) + ((n) >> 4)) >> (2 * (15 - ((n) & 15)))) & 3) + +/*! 2 bit access - set value (0 ... 3) */ +#define SET_DATA_DIBIT(pdata, n, val) \ + *((l_uint32 *)(pdata) + ((n) >> 4)) = \ + ((*((l_uint32 *)(pdata) + ((n) >> 4)) \ + & (~(0xc0000000 >> (2 * ((n) & 15))))) \ + | ((l_uint32)((val) & 3) << (30 - 2 * ((n) & 15)))) + +/*! 2 bit access - clear */ +#define CLEAR_DATA_DIBIT(pdata, n) \ + *((l_uint32 *)(pdata) + ((n) >> 4)) &= ~(0xc0000000 >> (2 * ((n) & 15))) + + + /*--------------------------------------------------* + * 4 bit access * + *--------------------------------------------------*/ +/*! 4 bit access - get */ +#define GET_DATA_QBIT(pdata, n) \ + ((*((const l_uint32 *)(pdata) + ((n) >> 3)) >> (4 * (7 - ((n) & 7)))) & 0xf) + +/*! 4 bit access - set value (0 ... 15) */ +#define SET_DATA_QBIT(pdata, n, val) \ + *((l_uint32 *)(pdata) + ((n) >> 3)) = \ + ((*((l_uint32 *)(pdata) + ((n) >> 3)) \ + & (~(0xf0000000 >> (4 * ((n) & 7))))) \ + | ((l_uint32)((val) & 15) << (28 - 4 * ((n) & 7)))) + +/*! 4 bit access - clear */ +#define CLEAR_DATA_QBIT(pdata, n) \ + *((l_uint32 *)(pdata) + ((n) >> 3)) &= ~(0xf0000000 >> (4 * ((n) & 7))) + + + /*--------------------------------------------------* + * 8 bit access * + *--------------------------------------------------*/ +#ifdef L_BIG_ENDIAN +/*! 8 bit access - get */ +#define GET_DATA_BYTE(pdata, n) \ + (*((const l_uint8 *)(pdata) + (n))) +#else /* L_LITTLE_ENDIAN */ +/*! 8 bit access - get */ +#define GET_DATA_BYTE(pdata, n) \ + (*(l_uint8 *)((l_uintptr_t)((const l_uint8 *)(pdata) + (n)) ^ 3)) +#endif /* L_BIG_ENDIAN */ + +#ifdef L_BIG_ENDIAN +/*! 8 bit access - set value (0 ... 255) */ +#define SET_DATA_BYTE(pdata, n, val) \ + *((l_uint8 *)(pdata) + (n)) = (val) +#else /* L_LITTLE_ENDIAN */ +/*! 8 bit access - set value (0 ... 255) */ +#define SET_DATA_BYTE(pdata, n, val) \ + *(l_uint8 *)((l_uintptr_t)((l_uint8 *)(pdata) + (n)) ^ 3) = (val) +#endif /* L_BIG_ENDIAN */ + + + /*--------------------------------------------------* + * 16 bit access * + *--------------------------------------------------*/ +#ifdef L_BIG_ENDIAN +/*! 16 bit access - get */ +#define GET_DATA_TWO_BYTES(pdata, n) \ + (*((const l_uint16 *)(pdata) + (n))) +#else /* L_LITTLE_ENDIAN */ +/*! 16 bit access - get */ +#define GET_DATA_TWO_BYTES(pdata, n) \ + (*(l_uint16 *)((l_uintptr_t)((const l_uint16 *)(pdata) + (n)) ^ 2)) +#endif /* L_BIG_ENDIAN */ + +#ifdef L_BIG_ENDIAN +/*! 16 bit access - set value (0 ... 65535) */ +#define SET_DATA_TWO_BYTES(pdata, n, val) \ + *((l_uint16 *)(pdata) + (n)) = (val) +#else /* L_LITTLE_ENDIAN */ +/*! 16 bit access - set value (0 ... 65535) */ +#define SET_DATA_TWO_BYTES(pdata, n, val) \ + *(l_uint16 *)((l_uintptr_t)((l_uint16 *)(pdata) + (n)) ^ 2) = (val) +#endif /* L_BIG_ENDIAN */ + + + /*--------------------------------------------------* + * 32 bit access * + *--------------------------------------------------*/ +/*! 32 bit access - get */ +#define GET_DATA_FOUR_BYTES(pdata, n) \ + (*((const l_uint32 *)(pdata) + (n))) + +/*! 32 bit access - set (0 ... 4294967295) */ +#define SET_DATA_FOUR_BYTES(pdata, n, val) \ + *((l_uint32 *)(pdata) + (n)) = (val) + + +#else + + /*=============================================================*/ + /* Slower: use function calls for all accessors */ + /*=============================================================*/ + +#define GET_DATA_BIT(pdata, n) l_getDataBit(pdata, n) +#define SET_DATA_BIT(pdata, n) l_setDataBit(pdata, n) +#define CLEAR_DATA_BIT(pdata, n) l_clearDataBit(pdata, n) +#define SET_DATA_BIT_VAL(pdata, n, val) l_setDataBitVal(pdata, n, val) + +#define GET_DATA_DIBIT(pdata, n) l_getDataDibit(pdata, n) +#define SET_DATA_DIBIT(pdata, n, val) l_setDataDibit(pdata, n, val) +#define CLEAR_DATA_DIBIT(pdata, n) l_clearDataDibit(pdata, n) + +#define GET_DATA_QBIT(pdata, n) l_getDataQbit(pdata, n) +#define SET_DATA_QBIT(pdata, n, val) l_setDataQbit(pdata, n, val) +#define CLEAR_DATA_QBIT(pdata, n) l_clearDataQbit(pdata, n) + +#define GET_DATA_BYTE(pdata, n) l_getDataByte(pdata, n) +#define SET_DATA_BYTE(pdata, n, val) l_setDataByte(pdata, n, val) + +#define GET_DATA_TWO_BYTES(pdata, n) l_getDataTwoBytes(pdata, n) +#define SET_DATA_TWO_BYTES(pdata, n, val) l_setDataTwoBytes(pdata, n, val) + +#define GET_DATA_FOUR_BYTES(pdata, n) l_getDataFourBytes(pdata, n) +#define SET_DATA_FOUR_BYTES(pdata, n, val) l_setDataFourBytes(pdata, n, val) + +#endif /* USE_INLINE_ACCESSORS */ + + +#endif /* LEPTONICA_ARRAY_ACCESS_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bardecode.c b/hgdriver/3rdparty/hgOCR/leptonica/bardecode.c new file mode 100644 index 0000000..5008bbc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bardecode.c @@ -0,0 +1,1033 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bardecode.c + *
+ *
+ *      Dispatcher
+ *          char            *barcodeDispatchDecoder()
+ *
+ *      Format Determination
+ *          static l_int32   barcodeFindFormat()
+ *          l_int32          barcodeFormatIsSupported()
+ *          static l_int32   barcodeVerifyFormat()
+ *
+ *      Decode 2 of 5
+ *          static char     *barcodeDecode2of5()
+ *
+ *      Decode Interleaved 2 of 5
+ *          static char     *barcodeDecodeI2of5()
+ *
+ *      Decode Code 93
+ *          static char     *barcodeDecode93()
+ *
+ *      Decode Code 39
+ *          static char     *barcodeDecode39()
+ *
+ *      Decode Codabar
+ *          static char     *barcodeDecodeCodabar()
+ *
+ *      Decode UPC-A
+ *          static char     *barcodeDecodeUpca()
+ *
+ *      Decode EAN 13
+ *          static char     *barcodeDecodeEan13()
+ * 
+ */ + +#include +#include "allheaders.h" +#include "readbarcode.h" + + +static l_int32 barcodeFindFormat(char *barstr); +static l_int32 barcodeVerifyFormat(char *barstr, l_int32 format, + l_int32 *pvalid, l_int32 *preverse); +static char *barcodeDecode2of5(char *barstr, l_int32 debugflag); +static char *barcodeDecodeI2of5(char *barstr, l_int32 debugflag); +static char *barcodeDecode93(char *barstr, l_int32 debugflag); +static char *barcodeDecode39(char *barstr, l_int32 debugflag); +static char *barcodeDecodeCodabar(char *barstr, l_int32 debugflag); +static char *barcodeDecodeUpca(char *barstr, l_int32 debugflag); +static char *barcodeDecodeEan13(char *barstr, l_int32 first, l_int32 debugflag); + + +#ifndef NO_CONSOLE_IO +#define DEBUG_CODES 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------------* + * Decoding dispatcher * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDispatchDecoder() + * + * \param[in] barstr string of integers in set {1,2,3,4} of bar widths + * \param[in] format L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ... + * \param[in] debugflag use 1 to generate debug output + * \return data string of decoded barcode data, or NULL on error + */ +char * +barcodeDispatchDecoder(char *barstr, + l_int32 format, + l_int32 debugflag) +{ +char *data = NULL; + + PROCNAME("barcodeDispatchDecoder"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + debugflag = FALSE; /* not used yet */ + + if (format == L_BF_ANY) + format = barcodeFindFormat(barstr); + + if (format == L_BF_CODE2OF5) + data = barcodeDecode2of5(barstr, debugflag); + else if (format == L_BF_CODEI2OF5) + data = barcodeDecodeI2of5(barstr, debugflag); + else if (format == L_BF_CODE93) + data = barcodeDecode93(barstr, debugflag); + else if (format == L_BF_CODE39) + data = barcodeDecode39(barstr, debugflag); + else if (format == L_BF_CODABAR) + data = barcodeDecodeCodabar(barstr, debugflag); + else if (format == L_BF_UPCA) + data = barcodeDecodeUpca(barstr, debugflag); + else if (format == L_BF_EAN13) + data = barcodeDecodeEan13(barstr, 0, debugflag); + else + return (char *)ERROR_PTR("format not implemented", procName, NULL); + + return data; +} + + +/*------------------------------------------------------------------------* + * Barcode format determination * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeFindFormat() + * + * \param[in] barstr of barcode widths, in set {1,2,3,4} + * \return format for barcode, or L_BF_UNKNOWN if not recognized + */ +static l_int32 +barcodeFindFormat(char *barstr) +{ +l_int32 i, format, valid; + + PROCNAME("barcodeFindFormat"); + + if (!barstr) + return ERROR_INT("barstr not defined", procName, L_BF_UNKNOWN); + + for (i = 0; i < NumSupportedBarcodeFormats; i++) { + format = SupportedBarcodeFormat[i]; + barcodeVerifyFormat(barstr, format, &valid, NULL); + if (valid) { + L_INFO("Barcode format: %s\n", procName, + SupportedBarcodeFormatName[i]); + return format; + } + } + return L_BF_UNKNOWN; +} + + +/*! + * \brief barcodeFormatIsSupported() + * + * \param[in] format + * \return 1 if format is one of those supported; 0 otherwise + * + */ +l_int32 +barcodeFormatIsSupported(l_int32 format) +{ +l_int32 i; + + for (i = 0; i < NumSupportedBarcodeFormats; i++) { + if (format == SupportedBarcodeFormat[i]) + return 1; + } + return 0; +} + + +/*! + * \brief barcodeVerifyFormat() + * + * \param[in] barstr of barcode widths, in set {1,2,3,4} + * \param[in] format L_BF_CODEI2OF5, L_BF_CODE93, ... + * \param[out] pvalid 0 if not valid, 1 and 2 if valid + * \param[out] preverse [optional] 1 if reversed; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If valid == 1, the barcode is of the given format in the
+ *          forward order; if valid == 2, it is backwards.
+ *      (2) If the barcode needs to be reversed to read it, and &reverse
+ *          is provided, a 1 is put into %reverse.
+ *      (3) Add to this as more formats are supported.
+ * 
+ */ +static l_int32 +barcodeVerifyFormat(char *barstr, + l_int32 format, + l_int32 *pvalid, + l_int32 *preverse) +{ +char *revbarstr; +l_int32 i, start, len, stop, mid; + + PROCNAME("barcodeVerifyFormat"); + + if (!pvalid) + return ERROR_INT("barstr not defined", procName, 1); + *pvalid = 0; + if (preverse) *preverse = 0; + if (!barstr) + return ERROR_INT("barstr not defined", procName, 1); + + switch (format) + { + case L_BF_CODE2OF5: + start = !strncmp(barstr, Code2of5[C25_START], 3); + len = strlen(barstr); + stop = !strncmp(&barstr[len - 5], Code2of5[C25_STOP], 5); + if (start && stop) { + *pvalid = 1; + } else { + revbarstr = stringReverse(barstr); + start = !strncmp(revbarstr, Code2of5[C25_START], 3); + stop = !strncmp(&revbarstr[len - 5], Code2of5[C25_STOP], 5); + LEPT_FREE(revbarstr); + if (start && stop) { + *pvalid = 1; + if (preverse) *preverse = 1; + } + } + break; + case L_BF_CODEI2OF5: + start = !strncmp(barstr, CodeI2of5[CI25_START], 4); + len = strlen(barstr); + stop = !strncmp(&barstr[len - 3], CodeI2of5[CI25_STOP], 3); + if (start && stop) { + *pvalid = 1; + } else { + revbarstr = stringReverse(barstr); + start = !strncmp(revbarstr, CodeI2of5[CI25_START], 4); + stop = !strncmp(&revbarstr[len - 3], CodeI2of5[CI25_STOP], 3); + LEPT_FREE(revbarstr); + if (start && stop) { + *pvalid = 1; + if (preverse) *preverse = 1; + } + } + break; + case L_BF_CODE93: + start = !strncmp(barstr, Code93[C93_START], 6); + len = strlen(barstr); + stop = !strncmp(&barstr[len - 7], Code93[C93_STOP], 6); + if (start && stop) { + *pvalid = 1; + } else { + revbarstr = stringReverse(barstr); + start = !strncmp(revbarstr, Code93[C93_START], 6); + stop = !strncmp(&revbarstr[len - 7], Code93[C93_STOP], 6); + LEPT_FREE(revbarstr); + if (start && stop) { + *pvalid = 1; + if (preverse) *preverse = 1; + } + } + break; + case L_BF_CODE39: + start = !strncmp(barstr, Code39[C39_START], 9); + len = strlen(barstr); + stop = !strncmp(&barstr[len - 9], Code39[C39_STOP], 9); + if (start && stop) { + *pvalid = 1; + } else { + revbarstr = stringReverse(barstr); + start = !strncmp(revbarstr, Code39[C39_START], 9); + stop = !strncmp(&revbarstr[len - 9], Code39[C39_STOP], 9); + LEPT_FREE(revbarstr); + if (start && stop) { + *pvalid = 1; + if (preverse) *preverse = 1; + } + } + break; + case L_BF_CODABAR: + start = stop = 0; + len = strlen(barstr); + for (i = 16; i <= 19; i++) /* any of these will do */ + start += !strncmp(barstr, Codabar[i], 7); + for (i = 16; i <= 19; i++) /* ditto */ + stop += !strncmp(&barstr[len - 7], Codabar[i], 7); + if (start && stop) { + *pvalid = 1; + } else { + start = stop = 0; + revbarstr = stringReverse(barstr); + for (i = 16; i <= 19; i++) + start += !strncmp(revbarstr, Codabar[i], 7); + for (i = 16; i <= 19; i++) + stop += !strncmp(&revbarstr[len - 7], Codabar[i], 7); + LEPT_FREE(revbarstr); + if (start && stop) { + *pvalid = 1; + if (preverse) *preverse = 1; + } + } + break; + case L_BF_UPCA: + case L_BF_EAN13: + len = strlen(barstr); + if (len == 59) { + start = !strncmp(barstr, Upca[UPCA_START], 3); + mid = !strncmp(&barstr[27], Upca[UPCA_MID], 5); + stop = !strncmp(&barstr[len - 3], Upca[UPCA_STOP], 3); + if (start && mid && stop) + *pvalid = 1; + } + break; + default: + return ERROR_INT("format not supported", procName, 1); + } + + return 0; +} + + +/*------------------------------------------------------------------------* + * Code 2 of 5 * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecode2of5() + * + * \param[in] barstr of widths, in set {1, 2} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref: http://en.wikipedia.org/wiki/Two-out-of-five_code (Note:
+ *                 the codes given here are wrong!)
+ *               http://morovia.com/education/symbology/code25.asp
+ *      (2) This is a very low density encoding for the 10 digits.
+ *          Each digit is encoded with 5 black bars, of which 2 are wide
+ *          and 3 are narrow.  No information is carried in the spaces
+ *          between the bars, which are all equal in width, represented by
+ *          a "1" in our encoding.
+ *      (3) The mapping from the sequence of five bar widths to the
+ *          digit is identical to the mapping used by the interleaved
+ *          2 of 5 code.  The start code is 21211, representing two
+ *          wide bars and a narrow bar, and the interleaved "1" spaces
+ *          are explicit.  The stop code is 21112.  For all codes
+ *          (including start and stop), the trailing space "1" is
+ *          implicit -- there is no reason to represent it in the
+ *          Code2of5[] array.
+ * 
+ */ +static char * +barcodeDecode2of5(char *barstr, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code[10]; +l_int32 valid, reverse, i, j, len, error, ndigits, start, found; + + PROCNAME("barcodeDecodeI2of5"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse if necessary */ + barcodeVerifyFormat(barstr, L_BF_CODE2OF5, &valid, &reverse); + if (!valid) + return (char *)ERROR_PTR("barstr not in 2of5 format", procName, NULL); + if (reverse) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Verify size */ + len = strlen(vbarstr); + if ((len - 11) % 10 != 0) { + LEPT_FREE(vbarstr); + return (char *)ERROR_PTR("size not divisible by 10: invalid 2of5 code", + procName, NULL); + } + + error = FALSE; + ndigits = (len - 11) / 10; + data = (char *)LEPT_CALLOC(ndigits + 1, sizeof(char)); + memset(code, 0, 10); + for (i = 0; i < ndigits; i++) { + start = 6 + 10 * i; + for (j = 0; j < 9; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < 10; j++) { + if (!strcmp(code, Code2of5[j])) { + data[i] = 0x30 + j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + return data; +} + + +/*------------------------------------------------------------------------* + * Interleaved Code 2 of 5 * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecodeI2of5() + * + * \param[in] barstr of widths, in set {1, 2} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref: http://en.wikipedia.org/wiki/Interleaved_2_of_5
+ *      (2) This always encodes an even number of digits.
+ *          The start code is 1111; the stop code is 211.
+ * 
+ */ +static char * +barcodeDecodeI2of5(char *barstr, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code1[6], code2[6]; +l_int32 valid, reverse, i, j, len, error, npairs, start, found; + + PROCNAME("barcodeDecodeI2of5"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse if necessary */ + barcodeVerifyFormat(barstr, L_BF_CODEI2OF5, &valid, &reverse); + if (!valid) + return (char *)ERROR_PTR("barstr not in i2of5 format", procName, NULL); + if (reverse) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Verify size */ + len = strlen(vbarstr); + if ((len - 7) % 10 != 0) { + LEPT_FREE(vbarstr); + return (char *)ERROR_PTR("size not divisible by 10: invalid I2of5 code", + procName, NULL); + } + + error = FALSE; + npairs = (len - 7) / 10; + data = (char *)LEPT_CALLOC(2 * npairs + 1, sizeof(char)); + memset(code1, 0, 6); + memset(code2, 0, 6); + for (i = 0; i < npairs; i++) { + start = 4 + 10 * i; + for (j = 0; j < 5; j++) { + code1[j] = vbarstr[start + 2 * j]; + code2[j] = vbarstr[start + 2 * j + 1]; + } + + if (debugflag) + fprintf(stderr, "code1: %s, code2: %s\n", code1, code2); + + found = FALSE; + for (j = 0; j < 10; j++) { + if (!strcmp(code1, CodeI2of5[j])) { + data[2 * i] = 0x30 + j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + found = FALSE; + for (j = 0; j < 10; j++) { + if (!strcmp(code2, CodeI2of5[j])) { + data[2 * i + 1] = 0x30 + j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + return data; +} + + +/*------------------------------------------------------------------------* + * Code 93 * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecode93() + * + * \param[in] barstr of widths, in set {1, 2, 3, 4} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Code93
+ *                http://morovia.com/education/symbology/code93.asp
+ *      (2) Each symbol has 3 black and 3 white bars.
+ *          The start and stop codes are 111141; the stop code then is
+ *          terminated with a final (1) bar.
+ *      (3) The last two codes are check codes.  We are checking them
+ *          for correctness, and issuing a warning on failure.  Should
+ *          probably not return any data on failure.
+ * 
+ */ +static char * +barcodeDecode93(char *barstr, + l_int32 debugflag) +{ +const char *checkc, *checkk; +char *data, *vbarstr; +char code[7]; +l_int32 valid, reverse, i, j, len, error, nsymb, start, found, sum; +l_int32 *index; + + PROCNAME("barcodeDecode93"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse if necessary */ + barcodeVerifyFormat(barstr, L_BF_CODE93, &valid, &reverse); + if (!valid) + return (char *)ERROR_PTR("barstr not in code93 format", procName, NULL); + if (reverse) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Verify size; skip the first 6 and last 7 bars. */ + len = strlen(vbarstr); + if ((len - 13) % 6 != 0) { + LEPT_FREE(vbarstr); + return (char *)ERROR_PTR("size not divisible by 6: invalid code 93", + procName, NULL); + } + + /* Decode the symbols */ + nsymb = (len - 13) / 6; + data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); + index = (l_int32 *)LEPT_CALLOC(nsymb, sizeof(l_int32)); + memset(code, 0, 7); + error = FALSE; + for (i = 0; i < nsymb; i++) { + start = 6 + 6 * i; + for (j = 0; j < 6; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < C93_START; j++) { + if (!strcmp(code, Code93[j])) { + data[i] = Code93Val[j]; + index[i] = j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(index); + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + /* Do check sums. For character "C", use only the + * actual data in computing the sum. For character "K", + * use the actual data plus the check character "C". */ + sum = 0; + for (i = 0; i < nsymb - 2; i++) /* skip the "C" and "K" */ + sum += ((i % 20) + 1) * index[nsymb - 3 - i]; + if (data[nsymb - 2] != Code93Val[sum % 47]) + L_WARNING("Error for check C\n", procName); + + if (debugflag) { + checkc = Code93[sum % 47]; + fprintf(stderr, "checkc = %s\n", checkc); + } + + sum = 0; + for (i = 0; i < nsymb - 1; i++) /* skip the "K" */ + sum += ((i % 15) + 1) * index[nsymb - 2 - i]; + if (data[nsymb - 1] != Code93Val[sum % 47]) + L_WARNING("Error for check K\n", procName); + + if (debugflag) { + checkk = Code93[sum % 47]; + fprintf(stderr, "checkk = %s\n", checkk); + } + + /* Remove the two check codes from the output */ + data[nsymb - 2] = '\0'; + + LEPT_FREE(index); + return data; +} + + +/*------------------------------------------------------------------------* + * Code 39 * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecode39() + * + * \param[in] barstr of widths, in set {1, 2} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Code39
+ *                http://morovia.com/education/symbology/code39.asp
+ *      (2) Each symbol has 5 black and 4 white bars.
+ *          The start and stop codes are 121121211 (the asterisk)
+ *      (3) This decoder was contributed by Roger Hyde.
+ * 
+ */ +static char * +barcodeDecode39(char *barstr, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code[10]; +l_int32 valid, reverse, i, j, len, error, nsymb, start, found; + + PROCNAME("barcodeDecode39"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse if necessary */ + barcodeVerifyFormat(barstr, L_BF_CODE39, &valid, &reverse); + if (!valid) + return (char *)ERROR_PTR("barstr not in code39 format", procName, NULL); + if (reverse) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Verify size */ + len = strlen(vbarstr); + if ((len + 1) % 10 != 0) { + LEPT_FREE(vbarstr); + return (char *)ERROR_PTR("size+1 not divisible by 10: invalid code 39", + procName, NULL); + } + + /* Decode the symbols */ + nsymb = (len - 19) / 10; + data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); + memset(code, 0, 10); + error = FALSE; + for (i = 0; i < nsymb; i++) { + start = 10 + 10 * i; + for (j = 0; j < 9; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < C39_START; j++) { + if (!strcmp(code, Code39[j])) { + data[i] = Code39Val[j]; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + return data; +} + + +/*------------------------------------------------------------------------* + * Codabar * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecodeCodabar() + * + * \param[in] barstr of widths, in set {1, 2} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Codabar
+ *                http://morovia.com/education/symbology/codabar.asp
+ *      (2) Each symbol has 4 black and 3 white bars.  They represent the
+ *          10 digits, and optionally 6 other characters.  The start and
+ *          stop codes can be any of four (typically denoted A,B,C,D).
+ * 
+ */ +static char * +barcodeDecodeCodabar(char *barstr, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code[8]; +l_int32 valid, reverse, i, j, len, error, nsymb, start, found; + + PROCNAME("barcodeDecodeCodabar"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse if necessary */ + barcodeVerifyFormat(barstr, L_BF_CODABAR, &valid, &reverse); + if (!valid) + return (char *)ERROR_PTR("barstr not in codabar format", + procName, NULL); + if (reverse) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Verify size */ + len = strlen(vbarstr); + if ((len + 1) % 8 != 0) { + LEPT_FREE(vbarstr); + return (char *)ERROR_PTR("size+1 not divisible by 8: invalid codabar", + procName, NULL); + } + + /* Decode the symbols */ + nsymb = (len - 15) / 8; + data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); + memset(code, 0, 8); + error = FALSE; + for (i = 0; i < nsymb; i++) { + start = 8 + 8 * i; + for (j = 0; j < 7; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < 16; j++) { + if (!strcmp(code, Codabar[j])) { + data[i] = CodabarVal[j]; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + return data; +} + + +/*------------------------------------------------------------------------* + * Code UPC-A * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecodeUpca() + * + * \param[in] barstr of widths, in set {1, 2, 3, 4} + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
+ *                http://morovia.com/education/symbology/upc-a.asp
+ *      (2) Each symbol has 2 black and 2 white bars, and encodes a digit.
+ *          The start and stop codes are 111 and 111.  There are a total of
+ *          30 black bars, encoding 12 digits in two sets of 6, with
+ *          2 black bars separating the sets.
+ *      (3) The last digit is a check digit.  We check for correctness, and
+ *          issue a warning on failure.  Should probably not return any
+ *          data on failure.
+ * 
+ */ +static char * +barcodeDecodeUpca(char *barstr, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code[5]; +l_int32 valid, i, j, len, error, start, found, sum, checkdigit; + + PROCNAME("barcodeDecodeUpca"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format; reverse has no meaning here -- we must test both */ + barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL); + if (!valid) + return (char *)ERROR_PTR("barstr not in UPC-A format", procName, NULL); + + /* Verify size */ + len = strlen(barstr); + if (len != 59) + return (char *)ERROR_PTR("size not 59; invalid UPC-A barcode", + procName, NULL); + + /* Check the first digit. If invalid, reverse the string. */ + memset(code, 0, 5); + for (i = 0; i < 4; i++) + code[i] = barstr[i + 3]; + found = FALSE; + for (i = 0; i < 10; i++) { + if (!strcmp(code, Upca[i])) { + found = TRUE; + break; + } + } + if (found == FALSE) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Decode the 12 symbols */ + data = (char *)LEPT_CALLOC(13, sizeof(char)); + memset(code, 0, 5); + error = FALSE; + for (i = 0; i < 12; i++) { + if (i < 6) + start = 3 + 4 * i; + else + start = 32 + 4 * (i - 6); + for (j = 0; j < 4; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < 10; j++) { + if (!strcmp(code, Upca[j])) { + data[i] = 0x30 + j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + /* Calculate the check digit (data[11]). */ + sum = 0; + for (i = 0; i < 12; i += 2) /* "even" digits */ + sum += 3 * (data[i] - 0x30); + for (i = 1; i < 11; i += 2) /* "odd" digits */ + sum += (data[i] - 0x30); + checkdigit = sum % 10; + if (checkdigit) /* not 0 */ + checkdigit = 10 - checkdigit; + if (checkdigit + 0x30 != data[11]) + L_WARNING("Error for UPC-A check character\n", procName); + + return data; +} + + +/*------------------------------------------------------------------------* + * Code EAN-13 * + *------------------------------------------------------------------------*/ +/*! + * \brief barcodeDecodeEan13() + * + * \param[in] barstr of widths, in set {1, 2, 3, 4} + * \param[in] first first digit: 0 - 9 + * \param[in] debugflag + * \return data string of digits, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
+ *                http://morovia.com/education/symbology/ean-13.asp
+ *      (2) The encoding is essentially the same as UPC-A, except
+ *          there are 13 digits in total, of which 12 are encoded
+ *          by bars (as with UPC-A) and the 13th is a leading digit
+ *          that determines the encoding of the next 6 digits,
+ *          selecting each digit from one of two tables.
+ *          encoded in the bars (as with UPC-A).  If the first digit
+ *          is 0, the encoding is identical to UPC-A.
+ *      (3) As with UPC-A, the last digit is a check digit.
+ *      (4) For now, we assume the first digit is input to this function.
+ *          Eventually, we will read it by pattern matching.
+ *
+ *    TODO: fix this for multiple tables, depending on the value of %first
+ * 
+ */ +static char * +barcodeDecodeEan13(char *barstr, + l_int32 first, + l_int32 debugflag) +{ +char *data, *vbarstr; +char code[5]; +l_int32 valid, i, j, len, error, start, found, sum, checkdigit; + + PROCNAME("barcodeDecodeEan13"); + + if (!barstr) + return (char *)ERROR_PTR("barstr not defined", procName, NULL); + + /* Verify format. You can't tell the orientation by the start + * and stop codes, but you can by the location of the digits. + * Use the UPCA verifier for EAN 13 -- it is identical. */ + barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL); + if (!valid) + return (char *)ERROR_PTR("barstr not in EAN 13 format", procName, NULL); + + /* Verify size */ + len = strlen(barstr); + if (len != 59) + return (char *)ERROR_PTR("size not 59; invalid EAN 13 barcode", + procName, NULL); + + /* Check the first digit. If invalid, reverse the string. */ + memset(code, 0, 5); + for (i = 0; i < 4; i++) + code[i] = barstr[i + 3]; + found = FALSE; + for (i = 0; i < 10; i++) { + if (!strcmp(code, Upca[i])) { + found = TRUE; + break; + } + } + if (found == FALSE) + vbarstr = stringReverse(barstr); + else + vbarstr = stringNew(barstr); + + /* Decode the 12 symbols */ + data = (char *)LEPT_CALLOC(13, sizeof(char)); + memset(code, 0, 5); + error = FALSE; + for (i = 0; i < 12; i++) { + if (i < 6) + start = 3 + 4 * i; + else + start = 32 + 4 * (i - 6); + for (j = 0; j < 4; j++) + code[j] = vbarstr[start + j]; + + if (debugflag) + fprintf(stderr, "code: %s\n", code); + + found = FALSE; + for (j = 0; j < 10; j++) { + if (!strcmp(code, Upca[j])) { + data[i] = 0x30 + j; + found = TRUE; + break; + } + } + if (!found) error = TRUE; + } + LEPT_FREE(vbarstr); + + if (error) { + LEPT_FREE(data); + return (char *)ERROR_PTR("error in decoding", procName, NULL); + } + + /* Calculate the check digit (data[11]). */ + sum = 0; + for (i = 0; i < 12; i += 2) /* "even" digits */ + sum += 3 * (data[i] - 0x30); + for (i = 1; i < 12; i += 2) /* "odd" digits */ + sum += (data[i] - 0x30); + checkdigit = sum % 10; + if (checkdigit) /* not 0 */ + checkdigit = 10 - checkdigit; + if (checkdigit + 0x30 != data[11]) + L_WARNING("Error for EAN-13 check character\n", procName); + + return data; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/baseline.c b/hgdriver/3rdparty/hgOCR/leptonica/baseline.c new file mode 100644 index 0000000..b8c74b2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/baseline.c @@ -0,0 +1,596 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file baseline.c + *
+ *
+ *      Locate text baselines in an image
+ *           NUMA     *pixFindBaselines()
+ *
+ *      Projective transform to remove local skew
+ *           PIX      *pixDeskewLocal()
+ *
+ *      Determine local skew
+ *           l_int32   pixGetLocalSkewTransform()
+ *           NUMA     *pixGetLocalSkewAngles()
+ *
+ *  We have two apparently different functions here:
+ *    ~ finding baselines
+ *    ~ finding a projective transform to remove keystone warping
+ *  The function pixGetLocalSkewAngles() returns an array of angles,
+ *  one for each raster line, and the baselines of the text lines
+ *  should intersect the left edge of the image with that angle.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Min to travel after finding max before abandoning peak */ +static const l_int32 MinDistInPeak = 35; + + /* Thresholds for peaks and zeros, relative to the max peak */ +static const l_int32 PeakThresholdRatio = 20; +static const l_int32 ZeroThresholdRatio = 100; + + /* Default values for determining local skew */ +static const l_int32 DefaultSlices = 10; +static const l_int32 DefaultSweepReduction = 2; +static const l_int32 DefaultBsReduction = 1; +static const l_float32 DefaultSweepRange = 5.; /* degrees */ +static const l_float32 DefaultSweepDelta = 1.; /* degrees */ +static const l_float32 DefaultMinbsDelta = 0.01; /* degrees */ + + /* Overlap slice fraction added to top and bottom of each slice */ +static const l_float32 OverlapFraction = 0.5; + + /* Minimum allowed confidence (ratio) for accepting a value */ +static const l_float32 MinAllowedConfidence = 3.0; + + +/*---------------------------------------------------------------------* + * Locate text baselines in an image * + *---------------------------------------------------------------------*/ +/*! + * \brief pixFindBaselines() + * + * \param[in] pixs 1 bpp, 300 ppi + * \param[out] ppta [optional] pairs of pts corresponding to + * approx. ends of each text line + * \param[in] pixadb for debug output; use NULL to skip + * \return na of baseline y values, or NULL on error + * + *
+ * Notes:
+ *      (1) Input binary image must have text lines already aligned
+ *          horizontally.  This can be done by either rotating the
+ *          image with pixDeskew(), or, if a projective transform
+ *          is required, by doing pixDeskewLocal() first.
+ *      (2) Input null for &pta if you don't want this returned.
+ *          The pta will come in pairs of points (left and right end
+ *          of each baseline).
+ *      (3) Caution: this will not work properly on text with multiple
+ *          columns, where the lines are not aligned between columns.
+ *          If there are multiple columns, they should be extracted
+ *          separately before finding the baselines.
+ *      (4) This function constructs different types of output
+ *          for baselines; namely, a set of raster line values and
+ *          a set of end points of each baseline.
+ *      (5) This function was designed to handle short and long text lines
+ *          without using dangerous thresholds on the peak heights.  It does
+ *          this by combining the differential signal with a morphological
+ *          analysis of the locations of the text lines.  One can also
+ *          combine this data to normalize the peak heights, by weighting
+ *          the differential signal in the region of each baseline
+ *          by the inverse of the width of the text line found there.
+ * 
+ */ +NUMA * +pixFindBaselines(PIX *pixs, + PTA **ppta, + PIXA *pixadb) +{ +l_int32 h, i, j, nbox, val1, val2, ndiff, bx, by, bw, bh; +l_int32 imaxloc, peakthresh, zerothresh, inpeak; +l_int32 mintosearch, max, maxloc, nloc, locval; +l_int32 *array; +l_float32 maxval; +BOXA *boxa1, *boxa2, *boxa3; +GPLOT *gplot; +NUMA *nasum, *nadiff, *naloc, *naval; +PIX *pix1, *pix2; +PTA *pta; + + PROCNAME("pixFindBaselines"); + + if (ppta) *ppta = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + /* Close up the text characters, removing noise */ + pix1 = pixMorphSequence(pixs, "c25.1 + e15.1", 0); + + /* Estimate the resolution */ + if (pixadb) pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); + + /* Save the difference of adjacent row sums. + * The high positive-going peaks are the baselines */ + if ((nasum = pixCountPixelsByRow(pix1, NULL)) == NULL) { + pixDestroy(&pix1); + return (NUMA *)ERROR_PTR("nasum not made", procName, NULL); + } + h = pixGetHeight(pixs); + nadiff = numaCreate(h); + numaGetIValue(nasum, 0, &val2); + for (i = 0; i < h - 1; i++) { + val1 = val2; + numaGetIValue(nasum, i + 1, &val2); + numaAddNumber(nadiff, val1 - val2); + } + numaDestroy(&nasum); + + if (pixadb) { /* show the difference signal */ + lept_mkdir("lept/baseline"); + gplotSimple1(nadiff, GPLOT_PNG, "/tmp/lept/baseline/diff", "Diff Sig"); + pix2 = pixRead("/tmp/lept/baseline/diff.png"); + pixaAddPix(pixadb, pix2, L_INSERT); + } + + /* Use the zeroes of the profile to locate each baseline. */ + array = numaGetIArray(nadiff); + ndiff = numaGetCount(nadiff); + numaGetMax(nadiff, &maxval, &imaxloc); + numaDestroy(&nadiff); + + /* Use this to begin locating a new peak: */ + peakthresh = (l_int32)maxval / PeakThresholdRatio; + /* Use this to begin a region between peaks: */ + zerothresh = (l_int32)maxval / ZeroThresholdRatio; + + naloc = numaCreate(0); + naval = numaCreate(0); + inpeak = FALSE; + for (i = 0; i < ndiff; i++) { + if (inpeak == FALSE) { + if (array[i] > peakthresh) { /* transition to in-peak */ + inpeak = TRUE; + mintosearch = i + MinDistInPeak; /* accept no zeros + * between i and mintosearch */ + max = array[i]; + maxloc = i; + } + } else { /* inpeak == TRUE; look for max */ + if (array[i] > max) { + max = array[i]; + maxloc = i; + mintosearch = i + MinDistInPeak; + } else if (i > mintosearch && array[i] <= zerothresh) { /* leave */ + inpeak = FALSE; + numaAddNumber(naval, max); + numaAddNumber(naloc, maxloc); + } + } + } + LEPT_FREE(array); + + /* If array[ndiff-1] is max, eg. no descenders, baseline at bottom */ + if (inpeak) { + numaAddNumber(naval, max); + numaAddNumber(naloc, maxloc); + } + + if (pixadb) { /* show the raster locations for the peaks */ + gplot = gplotCreate("/tmp/lept/baseline/loc", GPLOT_PNG, "Peak locs", + "rasterline", "height"); + gplotAddPlot(gplot, naloc, naval, GPLOT_POINTS, "locs"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + pix2 = pixRead("/tmp/lept/baseline/loc.png"); + pixaAddPix(pixadb, pix2, L_INSERT); + } + numaDestroy(&naval); + + /* Generate an approximate profile of text line width. + * First, filter the boxes of text, where there may be + * more than one box for a given textline. */ + pix2 = pixMorphSequence(pix1, "r11 + c20.1 + o30.1 +c1.3", 0); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + boxa1 = pixConnComp(pix2, NULL, 4); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (boxaGetCount(boxa1) == 0) { + numaDestroy(&naloc); + boxaDestroy(&boxa1); + L_INFO("no compnents after filtering\n", procName); + return NULL; + } + boxa2 = boxaTransform(boxa1, 0, 0, 4., 4.); + boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + + /* Optionally, find the baseline segments */ + pta = NULL; + if (ppta) { + pta = ptaCreate(0); + *ppta = pta; + } + if (pta) { + nloc = numaGetCount(naloc); + nbox = boxaGetCount(boxa3); + for (i = 0; i < nbox; i++) { + boxaGetBoxGeometry(boxa3, i, &bx, &by, &bw, &bh); + for (j = 0; j < nloc; j++) { + numaGetIValue(naloc, j, &locval); + if (L_ABS(locval - (by + bh)) > 25) + continue; + ptaAddPt(pta, bx, locval); + ptaAddPt(pta, bx + bw, locval); + break; + } + } + } + boxaDestroy(&boxa3); + + if (pixadb && pta) { /* display baselines */ + l_int32 npts, x1, y1, x2, y2; + pix1 = pixConvertTo32(pixs); + npts = ptaGetCount(pta); + for (i = 0; i < npts; i += 2) { + ptaGetIPt(pta, i, &x1, &y1); + ptaGetIPt(pta, i + 1, &x2, &y2); + pixRenderLineArb(pix1, x1, y1, x2, y2, 2, 255, 0, 0); + } + pixWriteDebug("/tmp/lept/baseline/baselines.png", pix1, IFF_PNG); + pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); + pixDestroy(&pix1); + } + + return naloc; +} + + +/*---------------------------------------------------------------------* + * Projective transform to remove local skew * + *---------------------------------------------------------------------*/ +/*! + * \brief pixDeskewLocal() + * + * \param[in] pixs 1 bpp + * \param[in] nslices the number of horizontal overlapping slices; + * must be larger than 1 and not exceed 20; + * use 0 for default + * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; + * use 0 for default value + * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and + * not larger than redsweep; use 0 for default value + * \param[in] sweeprange half the full range, assumed about 0; in degrees; + * use 0.0 for default value + * \param[in] sweepdelta angle increment of sweep; in degrees; + * use 0.0 for default value + * \param[in] minbsdelta min binary search increment angle; in degrees; + * use 0.0 for default value + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function allows deskew of a page whose skew changes
+ *          approximately linearly with vertical position.  It uses
+ *          a projective transform that in effect does a differential
+ *          shear about the LHS of the page, and makes all text lines
+ *          horizontal.
+ *      (2) The origin of the keystoning can be either a cheap document
+ *          feeder that rotates the page as it is passed through, or a
+ *          camera image taken from either the left or right side
+ *          of the vertical.
+ *      (3) The image transformation is a projective warping,
+ *          not a rotation.  Apart from this function, the text lines
+ *          must be properly aligned vertically with respect to each
+ *          other.  This can be done by pre-processing the page; e.g.,
+ *          by rotating or horizontally shearing it.
+ *          Typically, this can be achieved by vertically aligning
+ *          the page edge.
+ * 
+ */ +PIX * +pixDeskewLocal(PIX *pixs, + l_int32 nslices, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta) +{ +l_int32 ret; +PIX *pixd; +PTA *ptas, *ptad; + + PROCNAME("pixDeskewLocal"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + /* Skew array gives skew angle (deg) as fctn of raster line + * where it intersects the LHS of the image */ + ret = pixGetLocalSkewTransform(pixs, nslices, redsweep, redsearch, + sweeprange, sweepdelta, minbsdelta, + &ptas, &ptad); + if (ret != 0) + return (PIX *)ERROR_PTR("transform pts not found", procName, NULL); + + /* Use a projective transform */ + pixd = pixProjectiveSampledPta(pixs, ptad, ptas, L_BRING_IN_WHITE); + + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Determine the local skew * + *---------------------------------------------------------------------*/ +/*! + * \brief pixGetLocalSkewTransform() + * + * \param[in] pixs + * \param[in] nslices the number of horizontal overlapping slices; + * must be larger than 1 and not exceed 20; + * use 0 for default + * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; + * use 0 for default value + * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and not + * larger than redsweep; use 0 for default value + * \param[in] sweeprange half the full range, assumed about 0; + * in degrees; use 0.0 for default value + * \param[in] sweepdelta angle increment of sweep; in degrees; + * use 0.0 for default value + * \param[in] minbsdelta min binary search increment angle; in degrees; + * use 0.0 for default value + * \param[out] pptas 4 points in the source + * \param[out] pptad the corresponding 4 pts in the dest + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates two pairs of points in the src, each pair
+ *          corresponding to a pair of points that would lie along
+ *          the same raster line in a transformed (dewarped) image.
+ *      (2) The sets of 4 src and 4 dest points returned by this function
+ *          can then be used, in a projective or bilinear transform,
+ *          to remove keystoning in the src.
+ * 
+ */ +l_ok +pixGetLocalSkewTransform(PIX *pixs, + l_int32 nslices, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta, + PTA **pptas, + PTA **pptad) +{ +l_int32 w, h, i; +l_float32 deg2rad, angr, angd, dely; +NUMA *naskew; +PTA *ptas, *ptad; + + PROCNAME("pixGetLocalSkewTransform"); + + if (!pptas || !pptad) + return ERROR_INT("&ptas and &ptad not defined", procName, 1); + *pptas = *pptad = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (nslices < 2 || nslices > 20) + nslices = DefaultSlices; + if (redsweep < 1 || redsweep > 8) + redsweep = DefaultSweepReduction; + if (redsearch < 1 || redsearch > redsweep) + redsearch = DefaultBsReduction; + if (sweeprange == 0.0) + sweeprange = DefaultSweepRange; + if (sweepdelta == 0.0) + sweepdelta = DefaultSweepDelta; + if (minbsdelta == 0.0) + minbsdelta = DefaultMinbsDelta; + + naskew = pixGetLocalSkewAngles(pixs, nslices, redsweep, redsearch, + sweeprange, sweepdelta, minbsdelta, + NULL, NULL, 0); + if (!naskew) + return ERROR_INT("naskew not made", procName, 1); + + deg2rad = 3.14159265 / 180.; + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + ptas = ptaCreate(4); + ptad = ptaCreate(4); + *pptas = ptas; + *pptad = ptad; + + /* Find i for skew line that intersects LHS at i and RHS at h / 20 */ + for (i = 0; i < h; i++) { + numaGetFValue(naskew, i, &angd); + angr = angd * deg2rad; + dely = w * tan(angr); + if (i - dely > 0.05 * h) + break; + } + ptaAddPt(ptas, 0, i); + ptaAddPt(ptas, w - 1, i - dely); + ptaAddPt(ptad, 0, i); + ptaAddPt(ptad, w - 1, i); + + /* Find i for skew line that intersects LHS at i and RHS at 19h / 20 */ + for (i = h - 1; i > 0; i--) { + numaGetFValue(naskew, i, &angd); + angr = angd * deg2rad; + dely = w * tan(angr); + if (i - dely < 0.95 * h) + break; + } + ptaAddPt(ptas, 0, i); + ptaAddPt(ptas, w - 1, i - dely); + ptaAddPt(ptad, 0, i); + ptaAddPt(ptad, w - 1, i); + + numaDestroy(&naskew); + return 0; +} + + +/*! + * \brief pixGetLocalSkewAngles() + * + * \param[in] pixs 1 bpp + * \param[in] nslices the number of horizontal overlapping slices; + * must be larger than 1 and not exceed 20; + * use 0 for default + * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; + * use 0 for default value + * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and not + * larger than redsweep; use 0 for default value + * \param[in] sweeprange half the full range, assumed about 0; + * in degrees; use 0.0 for default value + * \param[in] sweepdelta angle increment of sweep; in degrees; + * use 0.0 for default value + * \param[in] minbsdelta min binary search increment angle; in degrees; + * use 0.0 for default value + * \param[out] pa [optional] slope of skew as fctn of y + * \param[out] pb [optional] intercept at y = 0 of skew, + 8 as a function of y + * \param[in] debug 1 for generating plot of skew angle vs. y; + * 0 otherwise + * \return naskew, or NULL on error + * + *
+ * Notes:
+ *      (1) The local skew is measured in a set of overlapping strips.
+ *          We then do a least square linear fit parameters to get
+ *          the slope and intercept parameters a and b in
+ *              skew-angle = a * y + b  (degrees)
+ *          for the local skew as a function of raster line y.
+ *          This is then used to make naskew, which can be interpreted
+ *          as the computed skew angle (in degrees) at the left edge
+ *          of each raster line.
+ *      (2) naskew can then be used to find the baselines of text, because
+ *          each text line has a baseline that should intersect
+ *          the left edge of the image with the angle given by this
+ *          array, evaluated at the raster line of intersection.
+ * 
+ */ +NUMA * +pixGetLocalSkewAngles(PIX *pixs, + l_int32 nslices, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta, + l_float32 *pa, + l_float32 *pb, + l_int32 debug) +{ +l_int32 w, h, hs, i, ystart, yend, ovlap, npts; +l_float32 angle, conf, ycenter, a, b; +BOX *box; +GPLOT *gplot; +NUMA *naskew, *nax, *nay; +PIX *pix; +PTA *pta; + + PROCNAME("pixGetLocalSkewAngles"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (nslices < 2 || nslices > 20) + nslices = DefaultSlices; + if (redsweep < 1 || redsweep > 8) + redsweep = DefaultSweepReduction; + if (redsearch < 1 || redsearch > redsweep) + redsearch = DefaultBsReduction; + if (sweeprange == 0.0) + sweeprange = DefaultSweepRange; + if (sweepdelta == 0.0) + sweepdelta = DefaultSweepDelta; + if (minbsdelta == 0.0) + minbsdelta = DefaultMinbsDelta; + + pixGetDimensions(pixs, &w, &h, NULL); + hs = h / nslices; + ovlap = (l_int32)(OverlapFraction * hs); + pta = ptaCreate(nslices); + for (i = 0; i < nslices; i++) { + ystart = L_MAX(0, hs * i - ovlap); + yend = L_MIN(h - 1, hs * (i + 1) + ovlap); + ycenter = (l_float32)(ystart + yend) / 2; + box = boxCreate(0, ystart, w, yend - ystart + 1); + pix = pixClipRectangle(pixs, box, NULL); + pixFindSkewSweepAndSearch(pix, &angle, &conf, redsweep, redsearch, + sweeprange, sweepdelta, minbsdelta); + if (conf > MinAllowedConfidence) + ptaAddPt(pta, ycenter, angle); + pixDestroy(&pix); + boxDestroy(&box); + } + + /* Do linear least squares fit */ + if ((npts = ptaGetCount(pta)) < 2) { + ptaDestroy(&pta); + return (NUMA *)ERROR_PTR("can't fit skew", procName, NULL); + } + ptaGetLinearLSF(pta, &a, &b, NULL); + if (pa) *pa = a; + if (pb) *pb = b; + + /* Make skew angle array as function of raster line */ + naskew = numaCreate(h); + for (i = 0; i < h; i++) { + angle = a * i + b; + numaAddNumber(naskew, angle); + } + + if (debug) { + lept_mkdir("lept/baseline"); + ptaGetArrays(pta, &nax, &nay); + gplot = gplotCreate("/tmp/lept/baseline/skew", GPLOT_PNG, + "skew as fctn of y", "y (in raster lines from top)", + "angle (in degrees)"); + gplotAddPlot(gplot, NULL, naskew, GPLOT_POINTS, "linear lsf"); + gplotAddPlot(gplot, nax, nay, GPLOT_POINTS, "actual data pts"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + numaDestroy(&nax); + numaDestroy(&nay); + } + + ptaDestroy(&pta); + return naskew; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.c b/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.c new file mode 100644 index 0000000..8fffd1d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.c @@ -0,0 +1,482 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bbuffer.c + *
+ *
+ *      Create/Destroy BBuffer
+ *          L_BBUFFER      *bbufferCreate()
+ *          void           *bbufferDestroy()
+ *          l_uint8        *bbufferDestroyAndSaveData()
+ *
+ *      Operations to read data TO a BBuffer
+ *          l_int32         bbufferRead()
+ *          l_int32         bbufferReadStream()
+ *          l_int32         bbufferExtendArray()
+ *
+ *      Operations to write data FROM a BBuffer
+ *          l_int32         bbufferWrite()
+ *          l_int32         bbufferWriteStream()
+ *
+ *    The bbuffer is an implementation of a byte queue.
+ *    The bbuffer holds a byte array from which bytes are
+ *    processed in a first-in/first-out fashion.  As with
+ *    any queue, bbuffer maintains two "pointers," one to the
+ *    tail of the queue (where you read new bytes onto it)
+ *    and one to the head of the queue (where you start from
+ *    when writing bytes out of it.
+ *
+ *    The queue can be visualized:
+ *
+ * \code
+ *  byte 0                                           byte (nalloc - 1)
+ *       |                                                |
+ *       --------------------------------------------------
+ *                 H                             T
+ *       [   aw   ][  bytes currently on queue  ][  anr   ]
+ *
+ *       ---:  all allocated data in bbuffer
+ *       H:    queue head (ptr to next byte to be written out)
+ *       T:    queue tail (ptr to first byte to be written to)
+ *       aw:   already written from queue
+ *       anr:  allocated but not yet read to
+ * \endcode
+ *    The purpose of bbuffer is to allow you to safely read
+ *    bytes in, and to sequentially write them out as well.
+ *    In the process of writing bytes out, you don't actually
+ *    remove the bytes in the array; you just move the pointer
+ *    (nwritten) which points to the head of the queue.  In
+ *    the process of reading bytes in, you sometimes need to
+ *    expand the array size.  If a read is performed after a
+ *    write, so that the head of the queue is not at the
+ *    beginning of the array, the bytes already written are
+ *    first removed by copying the others over them; then the
+ *    new bytes are read onto the tail of the queue.
+ *
+ *    Note that the meaning of "read into" and "write from"
+ *    the bbuffer is OPPOSITE to that for a stream, where
+ *    you read "from" a stream and write "into" a stream.
+ *    As a mnemonic for remembering the direction:
+ *        ~ to read bytes from a stream into the bbuffer,
+ *          you call fread on the stream
+ *        ~ to write bytes from the bbuffer into a stream,
+ *          you call fwrite on the stream
+ *
+ *    See zlibmem.c for an example use of bbuffer, where we
+ *    compress and decompress an array of bytes in memory.
+ *
+ *    We can also use the bbuffer trivially to read from stdin
+ *    into memory; e.g., to capture bytes piped from the stdout
+ *    of another program.  This is equivalent to repeatedly
+ *    calling bbufferReadStream() until the input queue is empty.
+ *    This is implemented in l_binaryReadStream().
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on array size */ +static const l_uint32 MaxArraySize = 1000000000; /* 10^9 bytes */ +static const l_int32 InitialArraySize = 1024; /*!< n'importe quoi */ + +/*--------------------------------------------------------------------------* + * BBuffer create/destroy * + *--------------------------------------------------------------------------*/ +/*! + * \brief bbufferCreate() + * + * \param[in] indata address in memory [optional] + * \param[in] nalloc size of byte array to be alloc'd 0 for default + * \return bbuffer, or NULL on error + * + *
+ * Notes:
+ *      (1) If a buffer address is given, you should read all the data in.
+ *      (2) Allocates a bbuffer with associated byte array of
+ *          the given size.  If a buffer address is given,
+ *          it then reads the number of bytes into the byte array.
+ * 
+ */ +L_BBUFFER * +bbufferCreate(const l_uint8 *indata, + l_int32 nalloc) +{ +L_BBUFFER *bb; + + PROCNAME("bbufferCreate"); + + if (nalloc <= 0 || nalloc > MaxArraySize) + nalloc = InitialArraySize; + + bb = (L_BBUFFER *)LEPT_CALLOC(1, sizeof(L_BBUFFER)); + if ((bb->array = (l_uint8 *)LEPT_CALLOC(nalloc, sizeof(l_uint8))) == NULL) { + LEPT_FREE(bb); + return (L_BBUFFER *)ERROR_PTR("byte array not made", procName, NULL); + } + bb->nalloc = nalloc; + bb->nwritten = 0; + + if (indata) { + memcpy(bb->array, indata, nalloc); + bb->n = nalloc; + } else { + bb->n = 0; + } + + return bb; +} + + +/*! + * \brief bbufferDestroy() + * + * \param[in,out] pbb will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Destroys the byte array in the bbuffer and then the bbuffer;
+ *          then nulls the contents of the input ptr.
+ * 
+ */ +void +bbufferDestroy(L_BBUFFER **pbb) +{ +L_BBUFFER *bb; + + PROCNAME("bbufferDestroy"); + + if (pbb == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + + if ((bb = *pbb) == NULL) + return; + + if (bb->array) + LEPT_FREE(bb->array); + LEPT_FREE(bb); + *pbb = NULL; + + return; +} + + +/*! + * \brief bbufferDestroyAndSaveData() + * + * \param[in,out] pbb input data buffer; will be nulled + * \param[out] pnbytes number of bytes saved in array + * \return barray newly allocated array of data + * + *
+ * Notes:
+ *      (1) Copies data to newly allocated array; then destroys the bbuffer.
+ * 
+ */ +l_uint8 * +bbufferDestroyAndSaveData(L_BBUFFER **pbb, + size_t *pnbytes) +{ +l_uint8 *array; +size_t nbytes; +L_BBUFFER *bb; + + PROCNAME("bbufferDestroyAndSaveData"); + + if (pbb == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return NULL; + } + if (pnbytes == NULL) { + L_WARNING("&nbytes is NULL\n", procName); + bbufferDestroy(pbb); + return NULL; + } + + if ((bb = *pbb) == NULL) + return NULL; + + /* write all unwritten bytes out to a new array */ + nbytes = bb->n - bb->nwritten; + *pnbytes = nbytes; + if ((array = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) { + L_WARNING("calloc failure for array\n", procName); + return NULL; + } + memcpy(array, bb->array + bb->nwritten, nbytes); + + bbufferDestroy(pbb); + return array; +} + + +/*--------------------------------------------------------------------------* + * Operations to read data INTO a BBuffer * + *--------------------------------------------------------------------------*/ +/*! + * \brief bbufferRead() + * + * \param[in] bb bbuffer + * \param[in] src source memory buffer from which bytes are read + * \param[in] nbytes bytes to be read + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For a read after write, first remove the written
+ *          bytes by shifting the unwritten bytes in the array,
+ *          then check if there is enough room to add the new bytes.
+ *          If not, realloc with bbufferExpandArray(), resulting
+ *          in a second writing of the unwritten bytes.  While less
+ *          efficient, this is simpler than making a special case
+ *          of reallocNew().
+ * 
+ */ +l_ok +bbufferRead(L_BBUFFER *bb, + l_uint8 *src, + l_int32 nbytes) +{ +l_int32 navail, nadd, nwritten; + + PROCNAME("bbufferRead"); + + if (!bb) + return ERROR_INT("bb not defined", procName, 1); + if (!src) + return ERROR_INT("src not defined", procName, 1); + if (nbytes == 0) + return ERROR_INT("no bytes to read", procName, 1); + + if ((nwritten = bb->nwritten)) { /* move the unwritten bytes over */ + memmove(bb->array, bb->array + nwritten, bb->n - nwritten); + bb->nwritten = 0; + bb->n -= nwritten; + } + + /* If necessary, expand the allocated array. Do so by + * by at least a factor of two. */ + navail = bb->nalloc - bb->n; + if (nbytes > navail) { + nadd = L_MAX(bb->nalloc, nbytes); + bbufferExtendArray(bb, nadd); + } + + /* Read in the new bytes */ + memcpy(bb->array + bb->n, src, nbytes); + bb->n += nbytes; + + return 0; +} + + +/*! + * \brief bbufferReadStream() + * + * \param[in] bb bbuffer + * \param[in] fp source stream from which bytes are read + * \param[in] nbytes bytes to be read + * \return 0 if OK, 1 on error + */ +l_ok +bbufferReadStream(L_BBUFFER *bb, + FILE *fp, + l_int32 nbytes) +{ +l_int32 navail, nadd, nread, nwritten; + + PROCNAME("bbufferReadStream"); + + if (!bb) + return ERROR_INT("bb not defined", procName, 1); + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (nbytes == 0) + return ERROR_INT("no bytes to read", procName, 1); + + if ((nwritten = bb->nwritten)) { /* move any unwritten bytes over */ + memmove(bb->array, bb->array + nwritten, bb->n - nwritten); + bb->nwritten = 0; + bb->n -= nwritten; + } + + /* If necessary, expand the allocated array. Do so by + * by at least a factor of two. */ + navail = bb->nalloc - bb->n; + if (nbytes > navail) { + nadd = L_MAX(bb->nalloc, nbytes); + bbufferExtendArray(bb, nadd); + } + + /* Read in the new bytes */ + nread = fread(bb->array + bb->n, 1, nbytes, fp); + bb->n += nread; + + return 0; +} + + +/*! + * \brief bbufferExtendArray() + * + * \param[in] bb bbuffer + * \param[in] nbytes number of bytes to extend array size + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) reallocNew() copies all bb->nalloc bytes, even though
+ *          only bb->n are data.
+ * 
+ */ +l_ok +bbufferExtendArray(L_BBUFFER *bb, + l_int32 nbytes) +{ + PROCNAME("bbufferExtendArray"); + + if (!bb) + return ERROR_INT("bb not defined", procName, 1); + + if ((bb->array = (l_uint8 *)reallocNew((void **)&bb->array, + bb->nalloc, + bb->nalloc + nbytes)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + bb->nalloc += nbytes; + return 0; +} + + +/*--------------------------------------------------------------------------* + * Operations to write data FROM a BBuffer * + *--------------------------------------------------------------------------*/ +/*! + * \brief bbufferWrite() + * + * \param[in] bb bbuffer + * \param[in] dest dest memory buffer to which bytes are written + * \param[in] nbytes bytes requested to be written + * \param[out] pnout bytes actually written + * \return 0 if OK, 1 on error + */ +l_ok +bbufferWrite(L_BBUFFER *bb, + l_uint8 *dest, + size_t nbytes, + size_t *pnout) +{ +size_t nleft, nout; + + PROCNAME("bbufferWrite"); + + if (!bb) + return ERROR_INT("bb not defined", procName, 1); + if (!dest) + return ERROR_INT("dest not defined", procName, 1); + if (nbytes <= 0) + return ERROR_INT("no bytes requested to write", procName, 1); + if (!pnout) + return ERROR_INT("&nout not defined", procName, 1); + + nleft = bb->n - bb->nwritten; + nout = L_MIN(nleft, nbytes); + *pnout = nout; + + if (nleft == 0) { /* nothing to write; reinitialize the buffer */ + bb->n = 0; + bb->nwritten = 0; + return 0; + } + + /* nout > 0; transfer the data out */ + memcpy(dest, bb->array + bb->nwritten, nout); + bb->nwritten += nout; + + /* If all written; "empty" the buffer */ + if (nout == nleft) { + bb->n = 0; + bb->nwritten = 0; + } + + return 0; +} + + +/*! + * \brief bbufferWriteStream() + * + * \param[in] bb bbuffer + * \param[in] fp dest stream to which bytes are written + * \param[in] nbytes bytes requested to be written + * \param[out] pnout bytes actually written + * \return 0 if OK, 1 on error + */ +l_ok +bbufferWriteStream(L_BBUFFER *bb, + FILE *fp, + size_t nbytes, + size_t *pnout) +{ +size_t nleft, nout; + + PROCNAME("bbufferWriteStream"); + + if (!bb) + return ERROR_INT("bb not defined", procName, 1); + if (!fp) + return ERROR_INT("output stream not defined", procName, 1); + if (nbytes <= 0) + return ERROR_INT("no bytes requested to write", procName, 1); + if (!pnout) + return ERROR_INT("&nout not defined", procName, 1); + + nleft = bb->n - bb->nwritten; + nout = L_MIN(nleft, nbytes); + *pnout = nout; + + if (nleft == 0) { /* nothing to write; reinitialize the buffer */ + bb->n = 0; + bb->nwritten = 0; + return 0; + } + + /* nout > 0; transfer the data out */ + fwrite(bb->array + bb->nwritten, 1, nout, fp); + bb->nwritten += nout; + + /* If all written; "empty" the buffer */ + if (nout == nleft) { + bb->n = 0; + bb->nwritten = 0; + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.h b/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.h new file mode 100644 index 0000000..945cbb0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bbuffer.h @@ -0,0 +1,60 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_BBUFFER_H +#define LEPTONICA_BBUFFER_H + +/*! + * \file bbuffer.h + * + *
+ *      Expandable byte buffer for reading data in from memory and
+ *      writing data out to other memory.
+ *
+ *      This implements a queue of bytes, so data read in is put
+ *      on the "back" of the queue (i.e., the end of the byte array)
+ *      and data written out is taken from the "front" of the queue
+ *      (i.e., from an index marker "nwritten" that is initially set at
+ *      the beginning of the array.)  As usual with expandable
+ *      arrays, we keep the size of the allocated array and the
+ *      number of bytes that have been read into the array.
+ *
+ *      For implementation details, see bbuffer.c.
+ * 
+ */ + +/*! Expandable byte buffer for memory read/write operations */ +struct L_ByteBuffer +{ + l_int32 nalloc; /*!< size of allocated byte array */ + l_int32 n; /*!< number of bytes read into to the array */ + l_int32 nwritten; /*!< number of bytes written from the array */ + l_uint8 *array; /*!< byte array */ +}; +typedef struct L_ByteBuffer L_BBUFFER; + + +#endif /* LEPTONICA_BBUFFER_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bilateral.c b/hgdriver/3rdparty/hgOCR/leptonica/bilateral.c new file mode 100644 index 0000000..2cc7eab --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bilateral.c @@ -0,0 +1,810 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bilateral.c + *
+ *
+ *     Top level approximate separable grayscale or color bilateral filtering
+ *          PIX                 *pixBilateral()
+ *          PIX                 *pixBilateralGray()
+ *
+ *     Implementation of approximate separable bilateral filter
+ *          static L_BILATERAL  *bilateralCreate()
+ *          static void         *bilateralDestroy()
+ *          static PIX          *bilateralApply()
+ *
+ *     Slow, exact implementation of grayscale or color bilateral filtering
+ *          PIX                 *pixBilateralExact()
+ *          PIX                 *pixBilateralGrayExact()
+ *          PIX                 *pixBlockBilateralExact()
+ *
+ *     Kernel helper function
+ *          L_KERNEL            *makeRangeKernel()
+ *
+ *  This includes both a slow, exact implementation of the bilateral
+ *  filter algorithm (given by Sylvain Paris and Fr茅do Durand),
+ *  and a fast, approximate and separable implementation (following
+ *  Yang, Tan and Ahuja).  See bilateral.h for algorithmic details.
+ *
+ *  The bilateral filter has the nice property of applying a gaussian
+ *  filter to smooth parts of the image that don't vary too quickly,
+ *  while at the same time preserving edges.  The filter is nonlinear
+ *  and cannot be decomposed into two separable filters; however,
+ *  there exists an approximate method that is separable.  To further
+ *  speed up the separable implementation, you can generate the
+ *  intermediate data at reduced resolution.
+ *
+ *  The full kernel is composed of two parts: a spatial gaussian filter
+ *  and a nonlinear "range" filter that depends on the intensity difference
+ *  between the reference pixel at the spatial kernel origin and any other
+ *  pixel within the kernel support.
+ *
+ *  In our implementations, the range filter is a parameterized,
+ *  one-sided, 256-element, monotonically decreasing gaussian function
+ *  of the absolute value of the difference between pixel values; namely,
+ *  abs(I2 - I1).  In general, any decreasing function can be used,
+ *  and more generally,  any two-dimensional kernel can be used if
+ *  you wish to relax the 'abs' condition.  (In that case, the range
+ *  filter can be 256 x 256).
+ * 
+ */ + +#include +#include "allheaders.h" +#include "bilateral.h" + +static L_BILATERAL *bilateralCreate(PIX *pixs, l_float32 spatial_stdev, + l_float32 range_stdev, l_int32 ncomps, + l_int32 reduction); +static PIX *bilateralApply(L_BILATERAL *bil); +static void bilateralDestroy(L_BILATERAL **pbil); + + +#ifndef NO_CONSOLE_IO +#define DEBUG_BILATERAL 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*--------------------------------------------------------------------------* + * Top level approximate separable grayscale or color bilateral filtering * + *--------------------------------------------------------------------------*/ +/*! + * \brief pixBilateral() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb, no colormap + * \param[in] spatial_stdev of gaussian kernel; in pixels, > 0.5 + * \param[in] range_stdev of gaussian range kernel; > 5.0; typ. 50.0 + * \param[in] ncomps number of intermediate sums J(k,x); + * in [4 ... 30] + * \param[in] reduction 1, 2 or 4 + * \return pixd bilateral filtered image, or NULL on error + * + *
+ * Notes:
+ *      (1) This performs a relatively fast, separable bilateral
+ *          filtering operation.  The time is proportional to ncomps
+ *          and varies inversely approximately as the cube of the
+ *          reduction factor.  See bilateral.h for algorithm details.
+ *      (2) We impose minimum values for range_stdev and ncomps to
+ *          avoid nasty artifacts when either are too small.  We also
+ *          impose a constraint on their product:
+ *               ncomps * range_stdev >= 100.
+ *          So for values of range_stdev >= 25, ncomps can be as small as 4.
+ *          Here is a qualitative, intuitive explanation for this constraint.
+ *          Call the difference in k values between the J(k) == 'delta', where
+ *              'delta' ~ 200 / ncomps
+ *          Then this constraint is roughly equivalent to the condition:
+ *              'delta' < 2 * range_stdev
+ *          Note that at an intensity difference of (2 * range_stdev), the
+ *          range part of the kernel reduces the effect by the factor 0.14.
+ *          This constraint requires that we have a sufficient number of
+ *          PCBs (i.e, a small enough 'delta'), so that for any value of
+ *          image intensity I, there exists a k (and a PCB, J(k), such that
+ *              |I - k| < range_stdev
+ *          Any fewer PCBs and we don't have enough to support this condition.
+ *      (3) The upper limit of 30 on ncomps is imposed because the
+ *          gain in accuracy is not worth the extra computation.
+ *      (4) The size of the gaussian kernel is twice the spatial_stdev
+ *          on each side of the origin.  The minimum value of
+ *          spatial_stdev, 0.5, is required to have a finite sized
+ *          spatial kernel.  In practice, a much larger value is used.
+ *      (5) Computation of the intermediate images goes inversely
+ *          as the cube of the reduction factor.  If you can use a
+ *          reduction of 2 or 4, it is well-advised.
+ *      (6) The range kernel is defined over the absolute value of pixel
+ *          grayscale differences, and hence must have size 256 x 1.
+ *          Values in the array represent the multiplying weight
+ *          depending on the absolute gray value difference between
+ *          the source pixel and the neighboring pixel, and should
+ *          be monotonically decreasing.
+ *      (7) Interesting observation.  Run this on prog/fish24.jpg, with
+ *          range_stdev = 60, ncomps = 6, and spatial_dev = {10, 30, 50}.
+ *          As spatial_dev gets larger, we get the counter-intuitive
+ *          result that the body of the red fish becomes less blurry.
+ * 
+ */ +PIX * +pixBilateral(PIX *pixs, + l_float32 spatial_stdev, + l_float32 range_stdev, + l_int32 ncomps, + l_int32 reduction) +{ +l_int32 d; +l_float32 sstdev; /* scaled spatial stdev */ +PIX *pixt, *pixr, *pixg, *pixb, *pixd; + + PROCNAME("pixBilateral"); + + if (!pixs || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not defined or cmapped", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (reduction != 1 && reduction != 2 && reduction != 4) + return (PIX *)ERROR_PTR("reduction invalid", procName, NULL); + sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ + if (sstdev < 0.5) + return (PIX *)ERROR_PTR("sstdev < 0.5", procName, NULL); + if (range_stdev <= 5.0) + return (PIX *)ERROR_PTR("range_stdev <= 5.0", procName, NULL); + if (ncomps < 4 || ncomps > 30) + return (PIX *)ERROR_PTR("ncomps not in [4 ... 30]", procName, NULL); + if (ncomps * range_stdev < 100.0) + return (PIX *)ERROR_PTR("ncomps * range_stdev < 100.0", procName, NULL); + + if (d == 8) + return pixBilateralGray(pixs, spatial_stdev, range_stdev, + ncomps, reduction); + + pixt = pixGetRGBComponent(pixs, COLOR_RED); + pixr = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, + reduction); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + pixg = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, + reduction); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + pixb = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, + reduction); + pixDestroy(&pixt); + pixd = pixCreateRGBImage(pixr, pixg, pixb); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; +} + + +/*! + * \brief pixBilateralGray() + * + * \param[in] pixs 8 bpp gray + * \param[in] spatial_stdev of gaussian kernel; in pixels, > 0.5 + * \param[in] range_stdev of gaussian range kernel; > 5.0; typ. 50.0 + * \param[in] ncomps number of intermediate sums J(k,x); + * in [4 ... 30] + * \param[in] reduction 1, 2 or 4 + * \return pixd 8 bpp bilateral filtered image, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixBilateral() for constraints on the input parameters.
+ *      (2) See pixBilateral() for algorithm details.
+ * 
+ */ +PIX * +pixBilateralGray(PIX *pixs, + l_float32 spatial_stdev, + l_float32 range_stdev, + l_int32 ncomps, + l_int32 reduction) +{ +l_float32 sstdev; /* scaled spatial stdev */ +PIX *pixd; +L_BILATERAL *bil; + + PROCNAME("pixBilateralGray"); + + if (!pixs || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not defined or cmapped", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp gray", procName, NULL); + if (reduction != 1 && reduction != 2 && reduction != 4) + return (PIX *)ERROR_PTR("reduction invalid", procName, NULL); + sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ + if (sstdev < 0.5) + return (PIX *)ERROR_PTR("sstdev < 0.5", procName, NULL); + if (range_stdev <= 5.0) + return (PIX *)ERROR_PTR("range_stdev <= 5.0", procName, NULL); + if (ncomps < 4 || ncomps > 30) + return (PIX *)ERROR_PTR("ncomps not in [4 ... 30]", procName, NULL); + if (ncomps * range_stdev < 100.0) + return (PIX *)ERROR_PTR("ncomps * range_stdev < 100.0", procName, NULL); + + bil = bilateralCreate(pixs, spatial_stdev, range_stdev, ncomps, reduction); + if (!bil) return (PIX *)ERROR_PTR("bil not made", procName, NULL); + pixd = bilateralApply(bil); + bilateralDestroy(&bil); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Implementation of approximate separable bilateral filter * + *----------------------------------------------------------------------*/ +/*! + * \brief bilateralCreate() + * + * \param[in] pixs 8 bpp gray, no colormap + * \param[in] spatial_stdev of gaussian kernel; in pixels, > 0.5 + * \param[in] range_stdev of gaussian range kernel; > 5.0; typ. 50.0 + * \param[in] ncomps number of intermediate sums J(k,x); + * in [4 ... 30] + * \param[in] reduction 1, 2 or 4 + * \return bil, or NULL on error + * + *
+ * Notes:
+ *      (1) This initializes a bilateral filtering operation, generating all
+ *          the data required.  It takes most of the time in the bilateral
+ *          filtering operation.
+ *      (2) See bilateral.h for details of the algorithm.
+ *      (3) See pixBilateral() for constraints on input parameters, which
+ *          are not checked here.
+ * 
+ */ +static L_BILATERAL * +bilateralCreate(PIX *pixs, + l_float32 spatial_stdev, + l_float32 range_stdev, + l_int32 ncomps, + l_int32 reduction) +{ +l_int32 w, ws, wd, h, hs, hd, i, j, k, index; +l_int32 border, minval, maxval, spatial_size; +l_int32 halfwidth, wpls, wplt, wpld, kval, nval, dval; +l_float32 sstdev, fval1, fval2, denom, sum, norm, kern; +l_int32 *nc, *kindex; +l_float32 *kfract, *range, *spatial; +l_uint32 *datas, *datat, *datad, *lines, *linet, *lined; +L_BILATERAL *bil; +PIX *pixt, *pixt2, *pixsc, *pixd; +PIXA *pixac; + + PROCNAME("bilateralCreate"); + + sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ + if ((bil = (L_BILATERAL *)LEPT_CALLOC(1, sizeof(L_BILATERAL))) == NULL) + return (L_BILATERAL *)ERROR_PTR("bil not made", procName, NULL); + bil->spatial_stdev = sstdev; + bil->range_stdev = range_stdev; + bil->reduction = reduction; + bil->ncomps = ncomps; + + if (reduction == 1) { + pixt = pixClone(pixs); + } else if (reduction == 2) { + pixt = pixScaleAreaMap2(pixs); + } else { /* reduction == 4) */ + pixt2 = pixScaleAreaMap2(pixs); + pixt = pixScaleAreaMap2(pixt2); + pixDestroy(&pixt2); + } + + pixGetExtremeValue(pixt, 1, L_SELECT_MIN, NULL, NULL, NULL, &minval); + pixGetExtremeValue(pixt, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxval); + bil->minval = minval; + bil->maxval = maxval; + + border = (l_int32)(2 * sstdev + 1); + pixsc = pixAddMirroredBorder(pixt, border, border, border, border); + bil->pixsc = pixsc; + pixDestroy(&pixt); + bil->pixs = pixClone(pixs); + + + /* -------------------------------------------------------------------- * + * Generate arrays for interpolation of J(k,x): + * (1.0 - kfract[.]) * J(kindex[.], x) + kfract[.] * J(kindex[.] + 1, x), + * where I(x) is the index into kfract[] and kindex[], + * and x is an index into the 2D image array. + * -------------------------------------------------------------------- */ + /* nc is the set of k values to be used in J(k,x) */ + nc = (l_int32 *)LEPT_CALLOC(ncomps, sizeof(l_int32)); + for (i = 0; i < ncomps; i++) + nc[i] = minval + i * (maxval - minval) / (ncomps - 1); + bil->nc = nc; + + /* kindex maps from intensity I(x) to the lower k index for J(k,x) */ + kindex = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { + fval2 = nc[k + 1]; + while (i < fval2) { + kindex[i] = k; + i++; + } + } + kindex[maxval] = ncomps - 2; + bil->kindex = kindex; + + /* kfract maps from intensity I(x) to the fraction of J(k+1,x) used */ + kfract = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); /* from lower */ + for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { + fval1 = nc[k]; + fval2 = nc[k + 1]; + while (i < fval2) { + kfract[i] = (l_float32)(i - fval1) / (l_float32)(fval2 - fval1); + i++; + } + } + kfract[maxval] = 1.0; + bil->kfract = kfract; + +#if DEBUG_BILATERAL + for (i = minval; i <= maxval; i++) + fprintf(stderr, "kindex[%d] = %d; kfract[%d] = %5.3f\n", + i, kindex[i], i, kfract[i]); + for (i = 0; i < ncomps; i++) + fprintf(stderr, "nc[%d] = %d\n", i, nc[i]); +#endif /* DEBUG_BILATERAL */ + + + /* -------------------------------------------------------------------- * + * Generate 1-D kernel arrays (spatial and range) * + * -------------------------------------------------------------------- */ + spatial_size = 2 * sstdev + 1; + spatial = (l_float32 *)LEPT_CALLOC(spatial_size, sizeof(l_float32)); + denom = 2. * sstdev * sstdev; + for (i = 0; i < spatial_size; i++) + spatial[i] = expf(-(l_float32)(i * i) / denom); + bil->spatial = spatial; + + range = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); + denom = 2. * range_stdev * range_stdev; + for (i = 0; i < 256; i++) + range[i] = expf(-(l_float32)(i * i) / denom); + bil->range = range; + + + /* -------------------------------------------------------------------- * + * Generate principal bilateral component images * + * -------------------------------------------------------------------- */ + pixac = pixaCreate(ncomps); + pixGetDimensions(pixsc, &ws, &hs, NULL); + datas = pixGetData(pixsc); + wpls = pixGetWpl(pixsc); + pixGetDimensions(pixs, &w, &h, NULL); + wd = (w + reduction - 1) / reduction; + hd = (h + reduction - 1) / reduction; + halfwidth = (l_int32)(2.0 * sstdev); + for (index = 0; index < ncomps; index++) { + pixt = pixCopy(NULL, pixsc); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + kval = nc[index]; + /* Separable convolutions: horizontal first */ + for (i = 0; i < hd; i++) { + lines = datas + (border + i) * wpls; + linet = datat + (border + i) * wplt; + for (j = 0; j < wd; j++) { + sum = 0.0; + norm = 0.0; + for (k = -halfwidth; k <= halfwidth; k++) { + nval = GET_DATA_BYTE(lines, border + j + k); + kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; + sum += kern * nval; + norm += kern; + } + dval = (l_int32)((sum / norm) + 0.5); + SET_DATA_BYTE(linet, border + j, dval); + } + } + /* Vertical convolution */ + pixd = pixCreate(wd, hd, 8); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < hd; i++) { + linet = datat + (border + i) * wplt; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + sum = 0.0; + norm = 0.0; + for (k = -halfwidth; k <= halfwidth; k++) { + nval = GET_DATA_BYTE(linet + k * wplt, border + j); + kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; + sum += kern * nval; + norm += kern; + } + dval = (l_int32)((sum / norm) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + pixDestroy(&pixt); + pixaAddPix(pixac, pixd, L_INSERT); + } + bil->pixac = pixac; + bil->lineset = (l_uint32 ***)pixaGetLinePtrs(pixac, NULL); + + return bil; +} + + +/*! + * \brief bilateralApply() + * + * \param[in] bil + * \return pixd + */ +static PIX * +bilateralApply(L_BILATERAL *bil) +{ +l_int32 i, j, k, ired, jred, w, h, wpls, wpld, ncomps, reduction; +l_int32 vals, vald, lowval, hival; +l_int32 *kindex; +l_float32 fract; +l_float32 *kfract; +l_uint32 *lines, *lined, *datas, *datad; +l_uint32 ***lineset = NULL; /* for set of PBC */ +PIX *pixs, *pixd; +PIXA *pixac; + + PROCNAME("bilateralApply"); + + if (!bil) + return (PIX *)ERROR_PTR("bil not defined", procName, NULL); + pixs = bil->pixs; + ncomps = bil->ncomps; + kindex = bil->kindex; + kfract = bil->kfract; + reduction = bil->reduction; + pixac = bil->pixac; + lineset = bil->lineset; + if (pixaGetCount(pixac) != ncomps) + return (PIX *)ERROR_PTR("PBC images do not exist", procName, NULL); + + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs, &w, &h, NULL); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + ired = i / reduction; + for (j = 0; j < w; j++) { + jred = j / reduction; + vals = GET_DATA_BYTE(lines, j); + k = kindex[vals]; + lowval = GET_DATA_BYTE(lineset[k][ired], jred); + hival = GET_DATA_BYTE(lineset[k + 1][ired], jred); + fract = kfract[vals]; + vald = (l_int32)((1.0 - fract) * lowval + fract * hival + 0.5); + SET_DATA_BYTE(lined, j, vald); + } + } + + return pixd; +} + + +/*! + * \brief bilateralDestroy() + * + * \param[in,out] pbil will be set to null before returning + */ +static void +bilateralDestroy(L_BILATERAL **pbil) +{ +l_int32 i; +L_BILATERAL *bil; + + PROCNAME("bilateralDestroy"); + + if (pbil == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((bil = *pbil) == NULL) + return; + + pixDestroy(&bil->pixs); + pixDestroy(&bil->pixsc); + pixaDestroy(&bil->pixac); + LEPT_FREE(bil->spatial); + LEPT_FREE(bil->range); + LEPT_FREE(bil->nc); + LEPT_FREE(bil->kindex); + LEPT_FREE(bil->kfract); + for (i = 0; i < bil->ncomps; i++) + LEPT_FREE(bil->lineset[i]); + LEPT_FREE(bil->lineset); + LEPT_FREE(bil); + *pbil = NULL; + return; +} + + +/*----------------------------------------------------------------------* + * Exact implementation of grayscale or color bilateral filtering * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBilateralExact() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb + * \param[in] spatial_kel gaussian kernel + * \param[in] range_kel [optional] 256 x 1, monotonically decreasing + * \return pixd 8 bpp bilateral filtered image + * + *
+ * Notes:
+ *      (1) The spatial_kel is a conventional smoothing kernel, typically a
+ *          2-d Gaussian kernel or other block kernel.  It can be either
+ *          normalized or not, but must be everywhere positive.
+ *      (2) The range_kel is defined over the absolute value of pixel
+ *          grayscale differences, and hence must have size 256 x 1.
+ *          Values in the array represent the multiplying weight for each
+ *          gray value difference between the target pixel and center of the
+ *          kernel, and should be monotonically decreasing.
+ *      (3) If range_kel == NULL, a constant weight is applied regardless
+ *          of the range value difference.  This degenerates to a regular
+ *          pixConvolve() with a normalized kernel.
+ * 
+ */ +PIX * +pixBilateralExact(PIX *pixs, + L_KERNEL *spatial_kel, + L_KERNEL *range_kel) +{ +l_int32 d; +PIX *pixt, *pixr, *pixg, *pixb, *pixd; + + PROCNAME("pixBilateralExact"); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (!spatial_kel) + return (PIX *)ERROR_PTR("spatial_ke not defined", procName, NULL); + + if (d == 8) { + return pixBilateralGrayExact(pixs, spatial_kel, range_kel); + } else { /* d == 32 */ + pixt = pixGetRGBComponent(pixs, COLOR_RED); + pixr = pixBilateralGrayExact(pixt, spatial_kel, range_kel); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + pixg = pixBilateralGrayExact(pixt, spatial_kel, range_kel); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + pixb = pixBilateralGrayExact(pixt, spatial_kel, range_kel); + pixDestroy(&pixt); + pixd = pixCreateRGBImage(pixr, pixg, pixb); + + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; + } +} + + +/*! + * \brief pixBilateralGrayExact() + * + * \param[in] pixs 8 bpp gray + * \param[in] spatial_kel gaussian kernel + * \param[in] range_kel [optional] 256 x 1, monotonically decreasing + * \return pixd 8 bpp bilateral filtered image + * + *
+ * Notes:
+ *      (1) See pixBilateralExact().
+ * 
+ */ +PIX * +pixBilateralGrayExact(PIX *pixs, + L_KERNEL *spatial_kel, + L_KERNEL *range_kel) +{ +l_int32 i, j, id, jd, k, m, w, h, d, sx, sy, cx, cy, wplt, wpld; +l_int32 val, center_val; +l_uint32 *datat, *datad, *linet, *lined; +l_float32 sum, weight_sum, weight; +L_KERNEL *keli; +PIX *pixt, *pixd; + + PROCNAME("pixBilateralGrayExact"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be gray", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (!spatial_kel) + return (PIX *)ERROR_PTR("spatial kel not defined", procName, NULL); + + if (!range_kel) + return pixConvolve(pixs, spatial_kel, 8, 1); + if (range_kel->sx != 256 || range_kel->sy != 1) + return (PIX *)ERROR_PTR("range kel not {256 x 1", procName, NULL); + + keli = kernelInvert(spatial_kel); + kernelGetParameters(keli, &sy, &sx, &cy, &cx); + if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) { + kernelDestroy(&keli); + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + + pixd = pixCreate(w, h, 8); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0, id = 0; id < h; i++, id++) { + lined = datad + id * wpld; + for (j = 0, jd = 0; jd < w; j++, jd++) { + center_val = GET_DATA_BYTE(datat + (i + cy) * wplt, j + cx); + weight_sum = 0.0; + sum = 0.0; + for (k = 0; k < sy; k++) { + linet = datat + (i + k) * wplt; + for (m = 0; m < sx; m++) { + val = GET_DATA_BYTE(linet, j + m); + weight = keli->data[k][m] * + range_kel->data[0][L_ABS(center_val - val)]; + weight_sum += weight; + sum += val * weight; + } + } + SET_DATA_BYTE(lined, jd, (l_int32)(sum / weight_sum + 0.5)); + } + } + + kernelDestroy(&keli); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixBlockBilateralExact() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb + * \param[in] spatial_stdev must be > 0.0 + * \param[in] range_stdev must be > 0.0 + * \return pixd 8 bpp or 32 bpp bilateral filtered image + * + *
+ * Notes:
+ *      (1) See pixBilateralExact().  This provides an interface using
+ *          the standard deviations of the spatial and range filters.
+ *      (2) The convolution window halfwidth is 2 * spatial_stdev,
+ *          and the square filter size is 4 * spatial_stdev + 1.
+ *          The kernel captures 95% of total energy.  This is compensated
+ *          by normalization.
+ *      (3) The range_stdev is analogous to spatial_halfwidth in the
+ *          grayscale domain [0...255], and determines how much damping of the
+ *          smoothing operation is applied across edges.  The larger this
+ *          value is, the smaller the damping.  The smaller the value, the
+ *          more edge details are preserved.  These approximations are useful
+ *          for deciding the appropriate cutoff.
+ *              kernel[1 * stdev] ~= 0.6  * kernel[0]
+ *              kernel[2 * stdev] ~= 0.14 * kernel[0]
+ *              kernel[3 * stdev] ~= 0.01 * kernel[0]
+ *          If range_stdev is infinite there is no damping, and this
+ *          becomes a conventional gaussian smoothing.
+ *          This value does not affect the run time.
+ *      (4) If range_stdev is negative or zero, the range kernel is
+ *          ignored and this degenerates to a straight gaussian convolution.
+ *      (5) This is very slow for large spatial filters.  The time
+ *          on a 3GHz pentium is roughly
+ *             T = 1.2 * 10^-8 * (A * sh^2)  sec
+ *          where A = # of pixels, sh = spatial halfwidth of filter.
+ * 
+ */ +PIX* +pixBlockBilateralExact(PIX *pixs, + l_float32 spatial_stdev, + l_float32 range_stdev) +{ +l_int32 d, halfwidth; +L_KERNEL *spatial_kel, *range_kel; +PIX *pixd; + + PROCNAME("pixBlockBilateralExact"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); + if (spatial_stdev <= 0.0) + return (PIX *)ERROR_PTR("invalid spatial stdev", procName, NULL); + if (range_stdev <= 0.0) + return (PIX *)ERROR_PTR("invalid range stdev", procName, NULL); + + halfwidth = 2 * spatial_stdev; + spatial_kel = makeGaussianKernel(halfwidth, halfwidth, spatial_stdev, 1.0); + range_kel = makeRangeKernel(range_stdev); + pixd = pixBilateralExact(pixs, spatial_kel, range_kel); + kernelDestroy(&spatial_kel); + kernelDestroy(&range_kel); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Kernel helper function * + *----------------------------------------------------------------------*/ +/*! + * \brief makeRangeKernel() + * + * \param[in] range_stdev must be > 0.0 + * \return kel, or NULL on error + * + *
+ * Notes:
+ *      (1) Creates a one-sided Gaussian kernel with the given
+ *          standard deviation.  At grayscale difference of one stdev,
+ *          the kernel falls to 0.6, and to 0.01 at three stdev.
+ *      (2) A typical input number might be 20.  Then pixels whose
+ *          value differs by 60 from the center pixel have their
+ *          weight in the convolution reduced by a factor of about 0.01.
+ * 
+ */ +L_KERNEL * +makeRangeKernel(l_float32 range_stdev) +{ +l_int32 x; +l_float32 val, denom; +L_KERNEL *kel; + + PROCNAME("makeRangeKernel"); + + if (range_stdev <= 0.0) + return (L_KERNEL *)ERROR_PTR("invalid stdev <= 0", procName, NULL); + + denom = 2. * range_stdev * range_stdev; + if ((kel = kernelCreate(1, 256)) == NULL) + return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); + kernelSetOrigin(kel, 0, 0); + for (x = 0; x < 256; x++) { + val = expf(-(l_float32)(x * x) / denom); + kernelSetElement(kel, 0, x, val); + } + return kel; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bilateral.h b/hgdriver/3rdparty/hgOCR/leptonica/bilateral.h new file mode 100644 index 0000000..e5b5bbd --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bilateral.h @@ -0,0 +1,136 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_BILATERAL_H +#define LEPTONICA_BILATERAL_H + +/*! + * \file bilateral.h + * + *
+ *  Contains the following struct
+ *      struct L_Bilateral
+ *
+ *
+ *  For a tutorial introduction to bilateral filters, which apply a
+ *  gaussian blur to smooth parts of the image while preserving edges, see
+ *    http://people.csail.mit.edu/sparis/bf_course/slides/03_definition_bf.pdf
+ *
+ *  We give an implementation of a bilateral filtering algorithm given in:
+ *    "Real-Time O(1) Bilateral Filtering," by Yang, Tan and Ahuja, CVPR 2009
+ *  which is at:
+ *    http://vision.ai.uiuc.edu/~qyang6/publications/cvpr-09-qingxiong-yang.pdf
+ *  This is based on an earlier algorithm by Sylvain Paris and Fr茅do Durand:
+ *    http://people.csail.mit.edu/sparis/publi/2006/eccv/
+ *               Paris_06_Fast_Approximation.pdf
+ *
+ *  The kernel of the filter is a product of a spatial gaussian and a
+ *  monotonically decreasing function of the difference in intensity
+ *  between the source pixel and the neighboring pixel.  The intensity
+ *  part of the filter gives higher influence for pixels with intensities
+ *  that are near to the source pixel, and the spatial part of the
+ *  filter gives higher weight to pixels that are near the source pixel.
+ *  This combination smooths in relatively uniform regions, while
+ *  maintaining edges.
+ *
+ *  The advantage of the appoach of Yang et al is that it is separable,
+ *  so the computation time is linear in the gaussian filter size.
+ *  Furthermore, it is possible to do much of the computation as a reduced
+ *  scale, which gives a good approximation to the full resolution version
+ *  but greatly speeds it up.
+ *
+ *  The bilateral filtered value at x is:
+ *
+ *            sum[y in N(x)]: spatial(|y - x|) * range(|I(x) - I(y)|) * I(y)
+ *    I'(x) = --------------------------------------------------------------
+ *            sum[y in N(x)]: spatial(|y - x|) * range(|I(x) - I(y)|)
+ *
+ *  where I() is the input image, I'() is the filtered image, N(x) is the
+ *  set of pixels around x in the filter support, and spatial() and range()
+ *  are gaussian functions:
+ *          spatial(x) = exp(-x^2 / (2 * s_s^2))
+ *          range(x) = exp(-x^2 / (2 * s_r^2))
+ *  and s_s and s_r and the standard deviations of the two gaussians.
+ *
+ *  Yang et al use a separable approximation to this, by defining a set
+ *  of related but separable functions J(k,x), that we call Principal
+ *  Bilateral Components (PBC):
+ *
+ *             sum[y in N(x)]: spatial(|y - x|) * range(|k - I(y)|) * I(y)
+ *    J(k,x) = -----------------------------------------------------------
+ *             sum[y in N(x)]: spatial(|y - x|) * range(|k - I(y)|)
+ *
+ *  which are computed quickly for a set of n values k[p], p = 0 ... n-1.
+ *  Then each output pixel is found using a linear interpolation:
+ *
+ *    I'(x) = (1 - q) * J(k[p],x) + q * J(k[p+1],x)
+ *
+ *  where J(k[p],x) and J(k[p+1],x) are PBC for which
+ *    k[p] <= I(x) and k[p+1] >= I(x), and
+ *    q = (I(x) - k[p]) / (k[p+1] - k[p]).
+ *
+ *  We can also subsample I(x), create subsampled versions of J(k,x),
+ *  which are then interpolated between for I'(x).
+ *
+ *  We generate 'pixsc', by optionally downscaling the input image
+ *  (using area mapping by the factor 'reduction'), and then adding
+ *  a mirrored border to avoid boundary cases.  This is then used
+ *  to compute 'ncomps' PBCs.
+ *
+ *  The 'spatial_stdev' is also downscaled by 'reduction'.  The size
+ *  of the 'spatial' array is 4 * (reduced 'spatial_stdev') + 1.
+ *  The size of the 'range' array is 256.
+ * 
+ */ + + +/*------------------------------------------------------------------------* + * Bilateral filter * + *------------------------------------------------------------------------*/ + +/*! Bilateral filter */ +struct L_Bilateral +{ + struct Pix *pixs; /*!< clone of source pix */ + struct Pix *pixsc; /*!< downscaled pix with mirrored border */ + l_int32 reduction; /*!< 1, 2 or 4x for intermediates */ + l_float32 spatial_stdev; /*!< stdev of spatial gaussian */ + l_float32 range_stdev; /*!< stdev of range gaussian */ + l_float32 *spatial; /*!< 1D gaussian spatial kernel */ + l_float32 *range; /*!< one-sided gaussian range kernel */ + l_int32 minval; /*!< min value in 8 bpp pix */ + l_int32 maxval; /*!< max value in 8 bpp pix */ + l_int32 ncomps; /*!< number of intermediate results */ + l_int32 *nc; /*!< set of k values (size ncomps) */ + l_int32 *kindex; /*!< mapping from intensity to lower k */ + l_float32 *kfract; /*!< mapping from intensity to fract k */ + struct Pixa *pixac; /*!< intermediate result images (PBC) */ + l_uint32 ***lineset; /*!< lineptrs for pixac */ +}; +typedef struct L_Bilateral L_BILATERAL; + + +#endif /* LEPTONICA_BILATERAL_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bilinear.c b/hgdriver/3rdparty/hgOCR/leptonica/bilinear.c new file mode 100644 index 0000000..e03a840 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bilinear.c @@ -0,0 +1,910 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file bilinear.c + *
+ *
+ *      Bilinear (4 pt) image transformation using a sampled
+ *      (to nearest integer) transform on each dest point
+ *           PIX      *pixBilinearSampledPta()
+ *           PIX      *pixBilinearSampled()
+ *
+ *      Bilinear (4 pt) image transformation using interpolation
+ *      (or area mapping) for anti-aliasing images that are
+ *      2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB
+ *           PIX      *pixBilinearPta()
+ *           PIX      *pixBilinear()
+ *           PIX      *pixBilinearPtaColor()
+ *           PIX      *pixBilinearColor()
+ *           PIX      *pixBilinearPtaGray()
+ *           PIX      *pixBilinearGray()
+ *
+ *      Bilinear transform including alpha (blend) component
+ *           PIX      *pixBilinearPtaWithAlpha()
+ *
+ *      Bilinear coordinate transformation
+ *           l_int32   getBilinearXformCoeffs()
+ *           l_int32   bilinearXformSampledPt()
+ *           l_int32   bilinearXformPt()
+ *
+ *      A bilinear transform can be specified as a specific functional
+ *      mapping between 4 points in the source and 4 points in the dest.
+ *      It can be used as an approximation to a (nonlinear) projective
+ *      transform, because for small warps it is very similar and
+ *      it is more stable.  (Projective transforms have a division
+ *      by a quantity that can get arbitrarily small.)
+ *
+ *      We give both a bilinear coordinate transformation and
+ *      a bilinear image transformation.
+ *
+ *      For the former, we ask for the coordinate value (x',y')
+ *      in the transformed space for any point (x,y) in the original
+ *      space.  The coefficients of the transformation are found by
+ *      solving 8 simultaneous equations for the 8 coordinates of
+ *      the 4 points in src and dest.  The transformation can then
+ *      be used to compute the associated image transform, by
+ *      computing, for each dest pixel, the relevant pixel(s) in
+ *      the source.  This can be done either by taking the closest
+ *      src pixel to each transformed dest pixel ("sampling") or
+ *      by doing an interpolation and averaging over 4 source
+ *      pixels with appropriate weightings ("interpolated").
+ *
+ *      A typical application would be to remove some of the
+ *      keystoning due to a projective transform in the imaging system.
+ *
+ *      The bilinear transform is given by specifying two equations:
+ *
+ *          x' = ax + by + cxy + d
+ *          y' = ex + fy + gxy + h
+ *
+ *      where the eight coefficients have been computed from four
+ *      sets of these equations, each for two corresponding data pts.
+ *      In practice, once the coefficients are known, we use the
+ *      equations "backwards": for each point (x,y) in the dest image,
+ *      these two equations are used to compute the corresponding point
+ *      (x',y') in the src.  That computed point in the src is then used
+ *      to determine the corresponding dest pixel value in one of two ways:
+ *
+ *       ~ sampling: simply take the value of the src pixel in which this
+ *                   point falls
+ *       ~ interpolation: take appropriate linear combinations of the
+ *                        four src pixels that this dest pixel would
+ *                        overlap, with the coefficients proportional
+ *                        to the amount of overlap
+ *
+ *      For small warp, like rotation, area mapping in the
+ *      interpolation is equivalent to linear interpolation.
+ *
+ *      Typical relative timing of transforms (sampled = 1.0):
+ *      8 bpp:   sampled        1.0
+ *               interpolated   1.6
+ *      32 bpp:  sampled        1.0
+ *               interpolated   1.8
+ *      Additionally, the computation time/pixel is nearly the same
+ *      for 8 bpp and 32 bpp, for both sampled and interpolated.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +extern l_float32 AlphaMaskBorderVals[2]; + + +/*-------------------------------------------------------------* + * Sampled bilinear image transformation * + *-------------------------------------------------------------*/ +/*! + * \brief pixBilinearSampledPta() + * + * \param[in] pixs all depths + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) No 3 of the 4 points may be collinear.
+ *      (4) For 8 and 32 bpp pix, better quality is obtained by the
+ *          somewhat slower pixBilinearPta().  See that
+ *          function for relative timings between sampled and interpolated.
+ * 
+ */ +PIX * +pixBilinearSampledPta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixBilinearSampledPta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getBilinearXformCoeffs(ptad, ptas, &vc); + pixd = pixBilinearSampled(pixs, vc, incolor); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixBilinearSampled() + * + * \param[in] pixs all depths + * \param[in] vc vector of 8 coefficients for bilinear transformation + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) For 8 or 32 bpp, much better quality is obtained by the
+ *          somewhat slower pixBilinear().  See that function
+ *          for relative timings between sampled and interpolated.
+ * 
+ */ +PIX * +pixBilinearSampled(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; +l_uint32 val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixBilinearSampled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); + + /* Init all dest pixels to color to be brought in from outside */ + pixd = pixCreateTemplate(pixs); + if ((cmap = pixGetColormap(pixs)) != NULL) { + if (incolor == L_BRING_IN_WHITE) + color = 1; + else + color = 0; + pixcmapAddBlackOrWhite(cmap, color, &cmapindex); + pixSetAllArbitrary(pixd, cmapindex); + } else { + if ((d == 1 && incolor == L_BRING_IN_WHITE) || + (d > 1 && incolor == L_BRING_IN_BLACK)) { + pixClearAll(pixd); + } else { + pixSetAll(pixd); + } + } + + /* Scan over the dest pixels */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + bilinearXformSampledPt(vc, j, i, &x, &y); + if (x < 0 || y < 0 || x >=w || y >= h) + continue; + lines = datas + y * wpls; + if (d == 1) { + val = GET_DATA_BIT(lines, x); + SET_DATA_BIT_VAL(lined, j, val); + } else if (d == 8) { + val = GET_DATA_BYTE(lines, x); + SET_DATA_BYTE(lined, j, val); + } else if (d == 32) { + lined[j] = lines[x]; + } else if (d == 2) { + val = GET_DATA_DIBIT(lines, x); + SET_DATA_DIBIT(lined, j, val); + } else if (d == 4) { + val = GET_DATA_QBIT(lines, x); + SET_DATA_QBIT(lined, j, val); + } + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Interpolated bilinear image transformation * + *---------------------------------------------------------------------*/ +/*! + * \brief pixBilinearPta() + * + * \param[in] pixs all depths; colormap ok + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixBilinearPta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixBilinearPta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixBilinearSampledPta(pixs, ptad, ptas, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixBilinearPtaGray(pixt2, ptad, ptas, colorval); + else /* d == 32 */ + pixd = pixBilinearPtaColor(pixt2, ptad, ptas, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixBilinear() + * + * \param[in] pixs all depths; colormap ok + * \param[in] vc vector of 8 coefficients for bilinear transformation + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixBilinear(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixBilinear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixBilinearSampled(pixs, vc, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixBilinearGray(pixt2, vc, colorval); + else /* d == 32 */ + pixd = pixBilinearColor(pixt2, vc, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixBilinearPtaColor() + * + * \param[in] pixs 32 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixBilinearPtaColor(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint32 colorval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixBilinearPtaColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getBilinearXformCoeffs(ptad, ptas, &vc); + pixd = pixBilinearColor(pixs, vc, colorval); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixBilinearColor() + * + * \param[in] pixs 32 bpp + * \param[in] vc vector of 8 coefficients for bilinear transformation + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixBilinearColor(PIX *pixs, + l_float32 *vc, + l_uint32 colorval) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_uint32 val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixBilinearColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, colorval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + bilinearXformPt(vc, j, i, &x, &y); + linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, + &val); + *(lined + j) = val; + } + } + + /* If rgba, transform the pixs alpha channel and insert in pixd */ + if (pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixBilinearGray(pix1, vc, 255); /* bring in opaque */ + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return pixd; +} + + +/*! + * \brief pixBilinearPtaGray() + * + * \param[in] pixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixBilinearPtaGray(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint8 grayval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixBilinearPtaGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getBilinearXformCoeffs(ptad, ptas, &vc); + pixd = pixBilinearGray(pixs, vc, grayval); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixBilinearGray() + * + * \param[in] pixs 8 bpp + * \param[in] vc vector of 8 coefficients for bilinear transformation + * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixBilinearGray(PIX *pixs, + l_float32 *vc, + l_uint8 grayval) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pixd; + + PROCNAME("pixBilinearGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, grayval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + bilinearXformPt(vc, j, i, &x, &y); + linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Bilinear transform including alpha (blend) component * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixBilinearPtaWithAlpha() + * + * \param[in] pixs 32 bpp rgb + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] pixg [optional] 8 bpp, can be null + * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent + * and 1.0 fully opaque + * \param[in] border of pixels added to capture transformed source pixels + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The alpha channel is transformed separately from pixs,
+ *          and aligns with it, being fully transparent outside the
+ *          boundary of the transformed pixs.  For pixels that are fully
+ *          transparent, a blending function like pixBlendWithGrayMask()
+ *          will give zero weight to corresponding pixels in pixs.
+ *      (2) If %pixg is NULL, it is generated as an alpha layer that is
+ *          partially opaque, using %fract.  Otherwise, it is cropped
+ *          to %pixs if required and %fract is ignored.  The alpha channel
+ *          in %pixs is never used.
+ *      (3) Colormaps are removed.
+ *      (4) When pixs is transformed, it doesn't matter what color is brought
+ *          in because the alpha channel will be transparent (0) there.
+ *      (5) To avoid losing source pixels in the destination, it may be
+ *          necessary to add a border to the source pix before doing
+ *          the bilinear transformation.  This can be any non-negative number.
+ *      (6) The input %ptad and %ptas are in a coordinate space before
+ *          the border is added.  Internally, we compensate for this
+ *          before doing the bilinear transform on the image after
+ *          the border is added.
+ *      (7) The default setting for the border values in the alpha channel
+ *          is 0 (transparent) for the outermost ring of pixels and
+ *          (0.5 * fract * 255) for the second ring.  When blended over
+ *          a second image, this
+ *          (a) shrinks the visible image to make a clean overlap edge
+ *              with an image below, and
+ *          (b) softens the edges by weakening the aliasing there.
+ *          Use l_setAlphaMaskBorder() to change these values.
+ * 
+ */ +PIX * +pixBilinearPtaWithAlpha(PIX *pixs, + PTA *ptad, + PTA *ptas, + PIX *pixg, + l_float32 fract, + l_int32 border) +{ +l_int32 ws, hs, d; +PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; +PTA *ptad2, *ptas2; + + PROCNAME("pixBilinearPtaWithAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (d != 32 && pixGetColormap(pixs) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (pixg && pixGetDepth(pixg) != 8) { + L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", + procName); + pixg = NULL; + } + if (!pixg && (fract < 0.0 || fract > 1.0)) { + L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); + fract = 1.0; + } + if (!pixg && fract == 0.0) + L_WARNING("fully opaque alpha; image cannot be blended\n", procName); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + + /* Add border; the color doesn't matter */ + pixb1 = pixAddBorder(pixs, border, 0); + + /* Transform the ptr arrays to work on the bordered image */ + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + + /* Do separate bilinear transform of rgb channels of pixs and of pixg */ + pixd = pixBilinearPtaColor(pixb1, ptad2, ptas2, 0); + if (!pixg) { + pixg2 = pixCreate(ws, hs, 8); + if (fract == 1.0) + pixSetAll(pixg2); + else + pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); + } else { + pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); + } + if (ws > 10 && hs > 10) { /* see note 7 */ + pixSetBorderRingVal(pixg2, 1, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); + pixSetBorderRingVal(pixg2, 2, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); + + } + pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ + pixga = pixBilinearPtaGray(pixb2, ptad2, ptas2, 0); + pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); + pixSetSpp(pixd, 4); + + pixDestroy(&pixg2); + pixDestroy(&pixb1); + pixDestroy(&pixb2); + pixDestroy(&pixga); + ptaDestroy(&ptad2); + ptaDestroy(&ptas2); + return pixd; +} + + +/*-------------------------------------------------------------* + * Bilinear coordinate transformation * + *-------------------------------------------------------------*/ +/*! + * \brief getBilinearXformCoeffs() + * + * \param[in] ptas source 4 points; unprimed + * \param[in] ptad transformed 4 points; primed + * \param[out] pvc vector of coefficients of transform + * \return 0 if OK; 1 on error + * + *
+ * We have a set of 8 equations, describing the bilinear
+ * transformation that takes 4 points ptas into 4 other
+ * points ptad.  These equations are:
+ *
+ *          x1' = c[0]*x1 + c[1]*y1 + c[2]*x1*y1 + c[3]
+ *          y1' = c[4]*x1 + c[5]*y1 + c[6]*x1*y1 + c[7]
+ *          x2' = c[0]*x2 + c[1]*y2 + c[2]*x2*y2 + c[3]
+ *          y2' = c[4]*x2 + c[5]*y2 + c[6]*x2*y2 + c[7]
+ *          x3' = c[0]*x3 + c[1]*y3 + c[2]*x3*y3 + c[3]
+ *          y3' = c[4]*x3 + c[5]*y3 + c[6]*x3*y3 + c[7]
+ *          x4' = c[0]*x4 + c[1]*y4 + c[2]*x4*y4 + c[3]
+ *          y4' = c[4]*x4 + c[5]*y4 + c[6]*x4*y4 + c[7]
+ *
+ * This can be represented as
+ *
+ *           AC = B
+ *
+ * where B and C are column vectors
+ *
+ *         B = [ x1' y1' x2' y2' x3' y3' x4' y4' ]
+ *         C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ]
+ *
+ * and A is the 8x8 matrix
+ *
+ *             x1   y1   x1*y1   1   0    0      0     0
+ *              0    0     0     0   x1   y1   x1*y1   1
+ *             x2   y2   x2*y2   1   0    0      0     0
+ *              0    0     0     0   x2   y2   x2*y2   1
+ *             x3   y3   x3*y3   1   0    0      0     0
+ *              0    0     0     0   x3   y3   x3*y3   1
+ *             x4   y4   x4*y4   1   0    0      0     0
+ *              0    0     0     0   x4   y4   x4*y4   1
+ *
+ * These eight equations are solved here for the coefficients C.
+ *
+ * These eight coefficients can then be used to find the mapping
+ * x,y) --> (x',y':
+ *
+ *           x' = c[0]x + c[1]y + c[2]xy + c[3]
+ *           y' = c[4]x + c[5]y + c[6]xy + c[7]
+ *
+ * that are implemented in bilinearXformSampledPt and
+ * bilinearXFormPt.
+ * 
+ */ +l_ok +getBilinearXformCoeffs(PTA *ptas, + PTA *ptad, + l_float32 **pvc) +{ +l_int32 i; +l_float32 x1, y1, x2, y2, x3, y3, x4, y4; +l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ +l_float32 *a[8]; /* 8x8 matrix A */ + + PROCNAME("getBilinearXformCoeffs"); + + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (!ptad) + return ERROR_INT("ptad not defined", procName, 1); + if (!pvc) + return ERROR_INT("&vc not defined", procName, 1); + + b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); + *pvc = b; + ptaGetPt(ptas, 0, &x1, &y1); + ptaGetPt(ptas, 1, &x2, &y2); + ptaGetPt(ptas, 2, &x3, &y3); + ptaGetPt(ptas, 3, &x4, &y4); + ptaGetPt(ptad, 0, &b[0], &b[1]); + ptaGetPt(ptad, 1, &b[2], &b[3]); + ptaGetPt(ptad, 2, &b[4], &b[5]); + ptaGetPt(ptad, 3, &b[6], &b[7]); + + for (i = 0; i < 8; i++) + a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); + a[0][0] = x1; + a[0][1] = y1; + a[0][2] = x1 * y1; + a[0][3] = 1.; + a[1][4] = x1; + a[1][5] = y1; + a[1][6] = x1 * y1; + a[1][7] = 1.; + a[2][0] = x2; + a[2][1] = y2; + a[2][2] = x2 * y2; + a[2][3] = 1.; + a[3][4] = x2; + a[3][5] = y2; + a[3][6] = x2 * y2; + a[3][7] = 1.; + a[4][0] = x3; + a[4][1] = y3; + a[4][2] = x3 * y3; + a[4][3] = 1.; + a[5][4] = x3; + a[5][5] = y3; + a[5][6] = x3 * y3; + a[5][7] = 1.; + a[6][0] = x4; + a[6][1] = y4; + a[6][2] = x4 * y4; + a[6][3] = 1.; + a[7][4] = x4; + a[7][5] = y4; + a[7][6] = x4 * y4; + a[7][7] = 1.; + + gaussjordan(a, b, 8); + + for (i = 0; i < 8; i++) + LEPT_FREE(a[i]); + return 0; +} + + +/*! + * \brief bilinearXformSampledPt() + * + * \param[in] vc vector of 8 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds the nearest pixel coordinates of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +bilinearXformSampledPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_int32 *pxp, + l_int32 *pyp) +{ + + PROCNAME("bilinearXformSampledPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3] + 0.5); + *pyp = (l_int32)(vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7] + 0.5); + return 0; +} + + +/*! + * \brief bilinearXformPt() + * + * \param[in] vc vector of 8 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This computes the floating point location of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +bilinearXformPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_float32 *pxp, + l_float32 *pyp) +{ + PROCNAME("bilinearXformPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + *pxp = vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3]; + *pyp = vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7]; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/binarize.c b/hgdriver/3rdparty/hgOCR/leptonica/binarize.c new file mode 100644 index 0000000..0bec396 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/binarize.c @@ -0,0 +1,1013 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file binarize.c + *
+ *
+ *  ===================================================================
+ *  Image binarization algorithms are found in:
+ *    grayquant.c:   standard, simple, general grayscale quantization
+ *    adaptmap.c:    local adaptive; mostly gray-to-gray in preparation
+ *                   for binarization
+ *    binarize.c:    special binarization methods, locally adaptive and
+ *                   global.
+ *  ===================================================================
+ *
+ *      Adaptive Otsu-based thresholding
+ *          l_int32       pixOtsuAdaptiveThreshold()       8 bpp
+ *
+ *      Otsu thresholding on adaptive background normalization
+ *          PIX          *pixOtsuThreshOnBackgroundNorm()  8 bpp
+ *
+ *      Masking and Otsu estimate on adaptive background normalization
+ *          PIX          *pixMaskedThreshOnBackgroundNorm()  8 bpp
+ *
+ *      Sauvola local thresholding
+ *          l_int32       pixSauvolaBinarizeTiled()
+ *          l_int32       pixSauvolaBinarize()
+ *          static PIX   *pixSauvolaGetThreshold()
+ *          static PIX   *pixApplyLocalThreshold();
+ *
+ *      Thresholding using connected components
+ *          PIX          *pixThresholdByConnComp()
+ *
+ *  Notes:
+ *      (1) pixOtsuAdaptiveThreshold() computes a global threshold over each
+ *          tile and performs the threshold operation, resulting in a
+ *          binary image for each tile.  These are stitched into the
+ *          final result.
+ *      (2) pixOtsuThreshOnBackgroundNorm() and
+ *          pixMaskedThreshOnBackgroundNorm() are binarization functions
+ *          that use background normalization with other techniques.
+ *      (3) Sauvola binarization computes a local threshold based on
+ *          the local average and square average.  It takes two constants:
+ *          the window size for the measurement at each pixel and a
+ *          parameter that determines the amount of normalized local
+ *          standard deviation to subtract from the local average value.
+ *      (4) pixThresholdByCC() uses the numbers of 4 and 8 connected
+ *          components at different thresholding to determine if a
+ *          global threshold can be used (for text or line-art) and the
+ *          value it should have.
+ * 
+ */ + +#include +#include "allheaders.h" + +static PIX *pixSauvolaGetThreshold(PIX *pixm, PIX *pixms, l_float32 factor, + PIX **ppixsd); +static PIX *pixApplyLocalThreshold(PIX *pixs, PIX *pixth); + +/*------------------------------------------------------------------* + * Adaptive Otsu-based thresholding * + *------------------------------------------------------------------*/ +/*! + * \brief pixOtsuAdaptiveThreshold() + * + * \param[in] pixs 8 bpp + * \param[in] sx, sy desired tile dimensions; actual size may vary + * \param[in] smoothx, smoothy half-width of convolution kernel applied to + * threshold array: use 0 for no smoothing + * \param[in] scorefract fraction of the max Otsu score; typ. 0.1; + * use 0.0 for standard Otsu + * \param[out] ppixth [optional] array of threshold values + * found for each tile + * \param[out] ppixd [optional] thresholded input pixs, + * based on the threshold array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The Otsu method finds a single global threshold for an image.
+ *          This function allows a locally adapted threshold to be
+ *          found for each tile into which the image is broken up.
+ *      (2) The array of threshold values, one for each tile, constitutes
+ *          a highly downscaled image.  This array is optionally
+ *          smoothed using a convolution.  The full width and height of the
+ *          convolution kernel are (2 * %smoothx + 1) and (2 * %smoothy + 1).
+ *      (3) The minimum tile dimension allowed is 16.  If such small
+ *          tiles are used, it is recommended to use smoothing, because
+ *          without smoothing, each small tile determines the splitting
+ *          threshold independently.  A tile that is entirely in the
+ *          image bg will then hallucinate fg, resulting in a very noisy
+ *          binarization.  The smoothing should be large enough that no
+ *          tile is only influenced by one type (fg or bg) of pixels,
+ *          because it will force a split of its pixels.
+ *      (4) To get a single global threshold for the entire image, use
+ *          input values of %sx and %sy that are larger than the image.
+ *          For this situation, the smoothing parameters are ignored.
+ *      (5) The threshold values partition the image pixels into two classes:
+ *          one whose values are less than the threshold and another
+ *          whose values are greater than or equal to the threshold.
+ *          This is the same use of 'threshold' as in pixThresholdToBinary().
+ *      (6) The scorefract is the fraction of the maximum Otsu score, which
+ *          is used to determine the range over which the histogram minimum
+ *          is searched.  See numaSplitDistribution() for details on the
+ *          underlying method of choosing a threshold.
+ *      (7) This uses enables a modified version of the Otsu criterion for
+ *          splitting the distribution of pixels in each tile into a
+ *          fg and bg part.  The modification consists of searching for
+ *          a minimum in the histogram over a range of pixel values where
+ *          the Otsu score is within a defined fraction, %scorefract,
+ *          of the max score.  To get the original Otsu algorithm, set
+ *          %scorefract == 0.
+ *      (8) N.B. This method is NOT recommended for images with weak text
+ *          and significant background noise, such as bleedthrough, because
+ *          of the problem noted in (3) above for tiling.  Use Sauvola.
+ * 
+ */ +l_ok +pixOtsuAdaptiveThreshold(PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 smoothx, + l_int32 smoothy, + l_float32 scorefract, + PIX **ppixth, + PIX **ppixd) +{ +l_int32 w, h, nx, ny, i, j, thresh; +l_uint32 val; +PIX *pixt, *pixb, *pixthresh, *pixth, *pixd; +PIXTILING *pt; + + PROCNAME("pixOtsuAdaptiveThreshold"); + + if (!ppixth && !ppixd) + return ERROR_INT("neither &pixth nor &pixd defined", procName, 1); + if (ppixth) *ppixth = NULL; + if (ppixd) *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (sx < 16 || sy < 16) + return ERROR_INT("sx and sy must be >= 16", procName, 1); + + /* Compute the threshold array for the tiles */ + pixGetDimensions(pixs, &w, &h, NULL); + nx = L_MAX(1, w / sx); + ny = L_MAX(1, h / sy); + smoothx = L_MIN(smoothx, (nx - 1) / 2); + smoothy = L_MIN(smoothy, (ny - 1) / 2); + pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0); + pixthresh = pixCreate(nx, ny, 8); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + pixt = pixTilingGetTile(pt, i, j); + pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh, + NULL, NULL, NULL); + pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */ + pixDestroy(&pixt); + } + } + + /* Optionally smooth the threshold array */ + if (smoothx > 0 || smoothy > 0) + pixth = pixBlockconv(pixthresh, smoothx, smoothy); + else + pixth = pixClone(pixthresh); + pixDestroy(&pixthresh); + + /* Optionally apply the threshold array to binarize pixs */ + if (ppixd) { + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + pixt = pixTilingGetTile(pt, i, j); + pixGetPixel(pixth, j, i, &val); + pixb = pixThresholdToBinary(pixt, val); + pixTilingPaintTile(pixd, i, j, pixb, pt); + pixDestroy(&pixt); + pixDestroy(&pixb); + } + } + *ppixd = pixd; + } + + if (ppixth) + *ppixth = pixth; + else + pixDestroy(&pixth); + + pixTilingDestroy(&pt); + return 0; +} + + +/*------------------------------------------------------------------* + * Otsu thresholding on adaptive background normalization * + *------------------------------------------------------------------*/ +/*! + * \brief pixOtsuThreshOnBackgroundNorm() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[in] bgval target bg val; typ. > 128 + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \param[in] scorefract fraction of the max Otsu score; typ. 0.1 + * \param[out] pthresh [optional] threshold value that was + * used on the normalized image + * \return pixd 1 bpp thresholded image, or NULL on error + * + *
+ * Notes:
+ *      (1) This does background normalization followed by Otsu
+ *          thresholding.  Otsu binarization attempts to split the
+ *          image into two roughly equal sets of pixels, and it does
+ *          a very poor job when there are large amounts of dark
+ *          background.  By doing a background normalization first,
+ *          to get the background near 255, we remove this problem.
+ *          Then we use a modified Otsu to estimate the best global
+ *          threshold on the normalized image.
+ *      (2) See pixBackgroundNorm() for meaning and typical values
+ *          of input parameters.  For a start, you can try:
+ *            sx, sy = 10, 15
+ *            thresh = 100
+ *            mincount = 50
+ *            bgval = 255
+ *            smoothx, smoothy = 2
+ * 
+ */ +PIX * +pixOtsuThreshOnBackgroundNorm(PIX *pixs, + PIX *pixim, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + l_int32 bgval, + l_int32 smoothx, + l_int32 smoothy, + l_float32 scorefract, + l_int32 *pthresh) +{ +l_int32 w, h; +l_uint32 val; +PIX *pixn, *pixt, *pixd; + + PROCNAME("pixOtsuThreshOnBackgroundNorm"); + + if (pthresh) *pthresh = 0; + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); + if (sx < 4 || sy < 4) + return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh, + mincount, bgval, smoothx, smoothy); + if (!pixn) + return (PIX *)ERROR_PTR("pixn not made", procName, NULL); + + /* Just use 1 tile for a global threshold, which is stored + * as a single pixel in pixt. */ + pixGetDimensions(pixn, &w, &h, NULL); + pixOtsuAdaptiveThreshold(pixn, w, h, 0, 0, scorefract, &pixt, &pixd); + pixDestroy(&pixn); + + if (pixt && pthresh) { + pixGetPixel(pixt, 0, 0, &val); + *pthresh = val; + } + pixDestroy(&pixt); + + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + else + return pixd; +} + + + +/*----------------------------------------------------------------------* + * Masking and Otsu estimate on adaptive background normalization * + *----------------------------------------------------------------------*/ +/*! + * \brief pixMaskedThreshOnBackgroundNorm() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] pixim [optional] 1 bpp 'image' mask; can be null + * \param[in] sx, sy tile size in pixels + * \param[in] thresh threshold for determining foreground + * \param[in] mincount min threshold on counts in a tile + * \param[in] smoothx half-width of block convolution kernel width + * \param[in] smoothy half-width of block convolution kernel height + * \param[in] scorefract fraction of the max Otsu score; typ. ~ 0.1 + * \param[out] pthresh [optional] threshold value that was + * used on the normalized image + * \return pixd 1 bpp thresholded image, or NULL on error + * + *
+ * Notes:
+ *      (1) This begins with a standard background normalization.
+ *          Additionally, there is a flexible background norm, that
+ *          will adapt to a rapidly varying background, and this
+ *          puts white pixels in the background near regions with
+ *          significant foreground.  The white pixels are turned into
+ *          a 1 bpp selection mask by binarization followed by dilation.
+ *          Otsu thresholding is performed on the input image to get an
+ *          estimate of the threshold in the non-mask regions.
+ *          The background normalized image is thresholded with two
+ *          different values, and the result is combined using
+ *          the selection mask.
+ *      (2) Note that the numbers 255 (for bgval target) and 190 (for
+ *          thresholding on pixn) are tied together, and explicitly
+ *          defined in this function.
+ *      (3) See pixBackgroundNorm() for meaning and typical values
+ *          of input parameters.  For a start, you can try:
+ *            sx, sy = 10, 15
+ *            thresh = 100
+ *            mincount = 50
+ *            smoothx, smoothy = 2
+ * 
+ */ +PIX * +pixMaskedThreshOnBackgroundNorm(PIX *pixs, + PIX *pixim, + l_int32 sx, + l_int32 sy, + l_int32 thresh, + l_int32 mincount, + l_int32 smoothx, + l_int32 smoothy, + l_float32 scorefract, + l_int32 *pthresh) +{ +l_int32 w, h, highthresh; +l_uint32 val; +PIX *pixn, *pixm, *pixd, *pix1, *pix2, *pix3, *pix4; + + PROCNAME("pixMaskedThreshOnBackgroundNorm"); + + if (pthresh) *pthresh = 0; + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); + if (sx < 4 || sy < 4) + return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); + if (mincount > sx * sy) { + L_WARNING("mincount too large for tile size\n", procName); + mincount = (sx * sy) / 3; + } + + /* Standard background normalization */ + pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh, + mincount, 255, smoothx, smoothy); + if (!pixn) + return (PIX *)ERROR_PTR("pixn not made", procName, NULL); + + /* Special background normalization for adaptation to quickly + * varying background. Threshold on the very light parts, + * which tend to be near significant edges, and dilate to + * form a mask over regions that are typically text. The + * dilation size is chosen to cover the text completely, + * except for very thick fonts. */ + pix1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20); + pix2 = pixThresholdToBinary(pix1, 240); + pixInvert(pix2, pix2); + pixm = pixMorphSequence(pix2, "d21.21", 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* Use Otsu to get a global threshold estimate for the image, + * which is stored as a single pixel in pix3. */ + pixGetDimensions(pixs, &w, &h, NULL); + pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pix3, NULL); + pixGetPixel(pix3, 0, 0, &val); + if (pthresh) *pthresh = val; + pixDestroy(&pix3); + + /* Threshold the background normalized images differentially, + * using a high value correlated with the background normalization + * for the part of the image under the mask (i.e., near the + * darker, thicker foreground), and a value that depends on the Otsu + * threshold for the rest of the image. This gives a solid + * (high) thresholding for the foreground parts of the image, + * while allowing the background and light foreground to be + * reasonably well cleaned using a threshold adapted to the + * input image. */ + highthresh = L_MIN(256, val + 30); + pixd = pixThresholdToBinary(pixn, highthresh); /* for bg and light fg */ + pix4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */ + pixCombineMasked(pixd, pix4, pixm); + pixDestroy(&pix4); + pixDestroy(&pixm); + pixDestroy(&pixn); + + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + else + return pixd; +} + + +/*----------------------------------------------------------------------* + * Sauvola binarization * + *----------------------------------------------------------------------*/ +/*! + * \brief pixSauvolaBinarizeTiled() + * + * \param[in] pixs 8 bpp grayscale, not colormapped + * \param[in] whsize window half-width for measuring local statistics + * \param[in] factor factor for reducing threshold due to variance; >= 0 + * \param[in] nx, ny subdivision into tiles; >= 1 + * \param[out] ppixth [optional] Sauvola threshold values + * \param[out] ppixd [optional] thresholded image + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The window width and height are 2 * %whsize + 1.  The minimum
+ *          value for %whsize is 2; typically it is >= 7..
+ *      (2) For nx == ny == 1, this defaults to pixSauvolaBinarize().
+ *      (3) Why a tiled version?
+ *          (a) Because the mean value accumulator is a uint32, overflow
+ *              can occur for an image with more than 16M pixels.
+ *          (b) The mean value accumulator array for 16M pixels is 64 MB.
+ *              The mean square accumulator array for 16M pixels is 128 MB.
+ *              Using tiles reduces the size of these arrays.
+ *          (c) Each tile can be processed independently, in parallel,
+ *              on a multicore processor.
+ *      (4) The Sauvola threshold is determined from the formula:
+ *              t = m * (1 - k * (1 - s / 128))
+ *          See pixSauvolaBinarize() for details.
+ * 
+ */ +l_ok +pixSauvolaBinarizeTiled(PIX *pixs, + l_int32 whsize, + l_float32 factor, + l_int32 nx, + l_int32 ny, + PIX **ppixth, + PIX **ppixd) +{ +l_int32 i, j, w, h, xrat, yrat; +PIX *pixth, *pixd, *tileth, *tiled, *pixt; +PIX **ptileth, **ptiled; +PIXTILING *pt; + + PROCNAME("pixSauvolaBinarizeTiled"); + + if (!ppixth && !ppixd) + return ERROR_INT("no outputs", procName, 1); + if (ppixth) *ppixth = NULL; + if (ppixd) *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is cmapped", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (whsize < 2) + return ERROR_INT("whsize must be >= 2", procName, 1); + if (w < 2 * whsize + 3 || h < 2 * whsize + 3) + return ERROR_INT("whsize too large for image", procName, 1); + if (factor < 0.0) + return ERROR_INT("factor must be >= 0", procName, 1); + + if (nx <= 1 && ny <= 1) + return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL, + ppixth, ppixd); + + /* Test to see if the tiles are too small. The required + * condition is that the tile dimensions must be at least + * (whsize + 2) x (whsize + 2). */ + xrat = w / nx; + yrat = h / ny; + if (xrat < whsize + 2) { + nx = w / (whsize + 2); + L_WARNING("tile width too small; nx reduced to %d\n", procName, nx); + } + if (yrat < whsize + 2) { + ny = h / (whsize + 2); + L_WARNING("tile height too small; ny reduced to %d\n", procName, ny); + } + if (nx <= 1 && ny <= 1) + return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL, + ppixth, ppixd); + + /* We can use pixtiling for painting both outputs, if requested */ + if (ppixth) { + pixth = pixCreateNoInit(w, h, 8); + *ppixth = pixth; + } + if (ppixd) { + pixd = pixCreateNoInit(w, h, 1); + *ppixd = pixd; + } + pt = pixTilingCreate(pixs, nx, ny, 0, 0, whsize + 1, whsize + 1); + pixTilingNoStripOnPaint(pt); /* pixSauvolaBinarize() does the stripping */ + + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + pixt = pixTilingGetTile(pt, i, j); + ptileth = (ppixth) ? &tileth : NULL; + ptiled = (ppixd) ? &tiled : NULL; + pixSauvolaBinarize(pixt, whsize, factor, 0, NULL, NULL, + ptileth, ptiled); + if (ppixth) { /* do not strip */ + pixTilingPaintTile(pixth, i, j, tileth, pt); + pixDestroy(&tileth); + } + if (ppixd) { + pixTilingPaintTile(pixd, i, j, tiled, pt); + pixDestroy(&tiled); + } + pixDestroy(&pixt); + } + } + + pixTilingDestroy(&pt); + return 0; +} + + +/*! + * \brief pixSauvolaBinarize() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] whsize window half-width for measuring local statistics + * \param[in] factor factor for reducing threshold due to variance; >= 0 + * \param[in] addborder 1 to add border of width (%whsize + 1) on all sides + * \param[out] ppixm [optional] local mean values + * \param[out] ppixsd [optional] local standard deviation values + * \param[out] ppixth [optional] threshold values + * \param[out] ppixd [optional] thresholded image + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The window width and height are 2 * %whsize + 1.  The minimum
+ *          value for %whsize is 2; typically it is >= 7..
+ *      (2) The local statistics, measured over the window, are the
+ *          average and standard deviation.
+ *      (3) The measurements of the mean and standard deviation are
+ *          performed inside a border of (%whsize + 1) pixels.  If pixs does
+ *          not have these added border pixels, use %addborder = 1 to add
+ *          it here; otherwise use %addborder = 0.
+ *      (4) The Sauvola threshold is determined from the formula:
+ *            t = m * (1 - k * (1 - s / 128))
+ *          where:
+ *            t = local threshold
+ *            m = local mean
+ *            k = %factor (>= 0)   [ typ. 0.35 ]
+ *            s = local standard deviation, which is maximized at
+ *                127.5 when half the samples are 0 and half are 255.
+ *      (5) The basic idea of Niblack and Sauvola binarization is that
+ *          the local threshold should be less than the median value,
+ *          and the larger the variance, the closer to the median
+ *          it should be chosen.  Typical values for k are between
+ *          0.2 and 0.5.
+ * 
+ */ +l_ok +pixSauvolaBinarize(PIX *pixs, + l_int32 whsize, + l_float32 factor, + l_int32 addborder, + PIX **ppixm, + PIX **ppixsd, + PIX **ppixth, + PIX **ppixd) +{ +l_int32 w, h; +PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd; + + PROCNAME("pixSauvolaBinarize"); + + if (ppixm) *ppixm = NULL; + if (ppixsd) *ppixsd = NULL; + if (ppixth) *ppixth = NULL; + if (ppixd) *ppixd = NULL; + if (!ppixm && !ppixsd && !ppixth && !ppixd) + return ERROR_INT("no outputs", procName, 1); + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is cmapped", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (whsize < 2) + return ERROR_INT("whsize must be >= 2", procName, 1); + if (w < 2 * whsize + 3 || h < 2 * whsize + 3) + return ERROR_INT("whsize too large for image", procName, 1); + if (factor < 0.0) + return ERROR_INT("factor must be >= 0", procName, 1); + + if (addborder) { + pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1, + whsize + 1, whsize + 1); + pixsc = pixClone(pixs); + } else { + pixg = pixClone(pixs); + pixsc = pixRemoveBorder(pixs, whsize + 1); + } + if (!pixg || !pixsc) + return ERROR_INT("pixg and pixsc not made", procName, 1); + + /* All these functions strip off the border pixels. */ + if (ppixm || ppixth || ppixd) + pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1); + if (ppixsd || ppixth || ppixd) + pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1); + if (ppixth || ppixd) + pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd); + if (ppixd) { + pixd = pixApplyLocalThreshold(pixsc, pixth); + pixCopyResolution(pixd, pixs); + } + + if (ppixm) + *ppixm = pixm; + else + pixDestroy(&pixm); + pixDestroy(&pixms); + if (ppixth) + *ppixth = pixth; + else + pixDestroy(&pixth); + if (ppixd) + *ppixd = pixd; + pixDestroy(&pixg); + pixDestroy(&pixsc); + return 0; +} + + +/*! + * \brief pixSauvolaGetThreshold() + * + * \param[in] pixm 8 bpp grayscale; not colormapped + * \param[in] pixms 32 bpp + * \param[in] factor factor for reducing threshold due to variance; >= 0 + * \param[out] ppixsd [optional] local standard deviation + * \return pixd 8 bpp, sauvola threshold values, or NULL on error + * + *
+ * Notes:
+ *      (1) The Sauvola threshold is determined from the formula:
+ *            t = m * (1 - k * (1 - s / 128))
+ *          where:
+ *            t = local threshold
+ *            m = local mean
+ *            k = %factor (>= 0)   [ typ. 0.35 ]
+ *            s = local standard deviation, which is maximized at
+ *                127.5 when half the samples are 0 and half are 255.
+ *      (2) See pixSauvolaBinarize() for other details.
+ *      (3) Important definitions and relations for computing averages:
+ *            v == pixel value
+ *            E(p) == expected value of p == average of p over some pixel set
+ *            S(v) == square of v == v * v
+ *            mv == E(v) == expected pixel value == mean value
+ *            ms == E(S(v)) == expected square of pixel values
+ *               == mean square value
+ *            var == variance == expected square of deviation from mean
+ *                == E(S(v - mv)) = E(S(v) - 2 * S(v * mv) + S(mv))
+ *                                = E(S(v)) - S(mv)
+ *                                = ms - mv * mv
+ *            s == standard deviation = sqrt(var)
+ *          So for evaluating the standard deviation in the Sauvola
+ *          threshold, we take
+ *            s = sqrt(ms - mv * mv)
+ * 
+ */ +static PIX * +pixSauvolaGetThreshold(PIX *pixm, + PIX *pixms, + l_float32 factor, + PIX **ppixsd) +{ +l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab; +l_int32 mv, ms, var, thresh; +l_uint32 *datam, *datams, *datasd, *datad; +l_uint32 *linem, *linems, *linesd, *lined; +l_float32 sd; +l_float32 *tab; /* of 2^16 square roots */ +PIX *pixsd, *pixd; + + PROCNAME("pixSauvolaGetThreshold"); + + if (ppixsd) *ppixsd = NULL; + if (!pixm || pixGetDepth(pixm) != 8) + return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixm)) + return (PIX *)ERROR_PTR("pixm is colormapped", procName, NULL); + if (!pixms || pixGetDepth(pixms) != 32) + return (PIX *)ERROR_PTR("pixms undefined or not 32 bpp", + procName, NULL); + if (factor < 0.0) + return (PIX *)ERROR_PTR("factor must be >= 0", procName, NULL); + + /* Only make a table of 2^16 square roots if there + * are enough pixels to justify it. */ + pixGetDimensions(pixm, &w, &h, NULL); + usetab = (w * h > 100000) ? 1 : 0; + if (usetab) { + tabsize = 1 << 16; + tab = (l_float32 *)LEPT_CALLOC(tabsize, sizeof(l_float32)); + for (i = 0; i < tabsize; i++) + tab[i] = sqrtf((l_float32)i); + } + + pixd = pixCreate(w, h, 8); + if (ppixsd) { + pixsd = pixCreate(w, h, 8); + *ppixsd = pixsd; + } + datam = pixGetData(pixm); + datams = pixGetData(pixms); + if (ppixsd) datasd = pixGetData(pixsd); + datad = pixGetData(pixd); + wplm = pixGetWpl(pixm); + wplms = pixGetWpl(pixms); + if (ppixsd) wplsd = pixGetWpl(pixsd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linem = datam + i * wplm; + linems = datams + i * wplms; + if (ppixsd) linesd = datasd + i * wplsd; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + mv = GET_DATA_BYTE(linem, j); + ms = linems[j]; + var = ms - mv * mv; + if (usetab) + sd = tab[var]; + else + sd = sqrtf((l_float32)var); + if (ppixsd) SET_DATA_BYTE(linesd, j, (l_int32)sd); + thresh = (l_int32)(mv * (1.0 - factor * (1.0 - sd / 128.))); + SET_DATA_BYTE(lined, j, thresh); + } + } + + if (usetab) LEPT_FREE(tab); + return pixd; +} + + +/*! + * \brief pixApplyLocalThreshold() + * + * \param[in] pixs 8 bpp grayscale; not colormapped + * \param[in] pixth 8 bpp array of local thresholds + * \return pixd 1 bpp, thresholded image, or NULL on error + */ +static PIX * +pixApplyLocalThreshold(PIX *pixs, + PIX *pixth) +{ +l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt; +l_uint32 *datas, *datat, *datad, *lines, *linet, *lined; +PIX *pixd; + + PROCNAME("pixApplyLocalThreshold"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); + if (!pixth || pixGetDepth(pixth) != 8) + return (PIX *)ERROR_PTR("pixth undefined or not 8 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, 1); + datas = pixGetData(pixs); + datat = pixGetData(pixth); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wplt = pixGetWpl(pixth); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valt = GET_DATA_BYTE(linet, j); + if (vals < valt) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*----------------------------------------------------------------------* + * Thresholding using connected components * + *----------------------------------------------------------------------*/ +/*! + * \brief pixThresholdByConnComp() + * + * \param[in] pixs depth > 1, colormap OK + * \param[in] pixm [optional] 1 bpp mask giving region to ignore + * by setting pixels to white; use NULL if no mask + * \param[in] start, end, incr binarization threshold levels to test + * \param[in] thresh48 threshold on normalized difference between the + * numbers of 4 and 8 connected components + * \param[in] threshdiff threshold on normalized difference between the + * number of 4 cc at successive iterations + * \param[out] pglobthresh [optional] best global threshold; 0 + * if no threshold is found + * \param[out] ppixd [optional] image thresholded to binary, or + * null if no threshold is found + * \param[in] debugflag 1 for plotted results + * \return 0 if OK, 1 on error or if no threshold is found + * + *
+ * Notes:
+ *      (1) This finds a global threshold based on connected components.
+ *          Although slow, it is reasonable to use it in a situation where
+ *          (a) the background in the image is relatively uniform, and
+ *          (b) the result will be fed to an OCR program that accepts 1 bpp
+ *              images and works best with easily segmented characters.
+ *          The reason for (b) is that this selects a threshold with a
+ *          minimum number of both broken characters and merged characters.
+ *      (2) If the pix has color, it is converted to gray using the
+ *          max component.
+ *      (3) Input 0 to use default values for any of these inputs:
+ *          %start, %end, %incr, %thresh48, %threshdiff.
+ *      (4) This approach can be understood as follows.  When the
+ *          binarization threshold is varied, the numbers of c.c. identify
+ *          four regimes:
+ *          (a) For low thresholds, text is broken into small pieces, and
+ *              the number of c.c. is large, with the 4 c.c. significantly
+ *              exceeding the 8 c.c.
+ *          (b) As the threshold rises toward the optimum value, the text
+ *              characters coalesce and there is very little difference
+ *              between the numbers of 4 and 8 c.c, which both go
+ *              through a minimum.
+ *          (c) Above this, the image background gets noisy because some
+ *              pixels are(thresholded to foreground, and the numbers
+ *              of c.c. quickly increase, with the 4 c.c. significantly
+ *              larger than the 8 c.c.
+ *          (d) At even higher thresholds, the image background noise
+ *              coalesces as it becomes mostly foreground, and the
+ *              number of c.c. drops quickly.
+ *      (5) If there is no global threshold that distinguishes foreground
+ *          text from background (e.g., weak text over a background that
+ *          has significant variation and/or bleedthrough), this returns 1,
+ *          which the caller should check.
+ * 
+ */ +l_ok +pixThresholdByConnComp(PIX *pixs, + PIX *pixm, + l_int32 start, + l_int32 end, + l_int32 incr, + l_float32 thresh48, + l_float32 threshdiff, + l_int32 *pglobthresh, + PIX **ppixd, + l_int32 debugflag) +{ +l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh; +l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4; +GPLOT *gplot; +NUMA *na4, *na8; +PIX *pix1, *pix2, *pix3; + + PROCNAME("pixThresholdByConnComp"); + + if (pglobthresh) *pglobthresh = 0; + if (ppixd) *ppixd = NULL; + if (!pixs || pixGetDepth(pixs) == 1) + return ERROR_INT("pixs undefined or 1 bpp", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm must be 1 bpp", procName, 1); + + /* Assign default values if requested */ + if (start <= 0) start = 80; + if (end <= 0) end = 200; + if (incr <= 0) incr = 10; + if (thresh48 <= 0.0) thresh48 = 0.01; + if (threshdiff <= 0.0) threshdiff = 0.01; + if (start > end) + return ERROR_INT("invalid start,end", procName, 1); + + /* Make 8 bpp, using the max component if color. */ + if (pixGetColormap(pixs)) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix1 = pixClone(pixs); + if (pixGetDepth(pix1) == 32) + pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); + else + pix2 = pixConvertTo8(pix1, 0); + pixDestroy(&pix1); + + /* Mask out any non-text regions. Do this in-place, because pix2 + * can never be the same pix as pixs. */ + if (pixm) + pixSetMasked(pix2, pixm, 255); + + /* Make sure there are enough components to get a valid signal */ + pix3 = pixConvertTo1(pix2, start); + pixCountConnComp(pix3, 4, &n4); + pixDestroy(&pix3); + mincounts = 500; + if (n4 < mincounts) { + L_INFO("Insufficient component count: %d\n", procName, n4); + pixDestroy(&pix2); + return 1; + } + + /* Compute the c.c. data */ + na4 = numaCreate(0); + na8 = numaCreate(0); + numaSetParameters(na4, start, incr); + numaSetParameters(na8, start, incr); + for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) { + pix3 = pixConvertTo1(pix2, thresh); + pixCountConnComp(pix3, 4, &n4); + pixCountConnComp(pix3, 8, &n8); + numaAddNumber(na4, n4); + numaAddNumber(na8, n8); + pixDestroy(&pix3); + } + if (debugflag) { + gplot = gplotCreate("/tmp/threshroot", GPLOT_PNG, + "number of cc vs. threshold", + "threshold", "number of cc"); + gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc"); + gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } + + n = numaGetCount(na4); + found = FALSE; + for (i = 0; i < n; i++) { + if (i == 0) { + numaGetFValue(na4, i, &firstcount4); + prevcount4 = firstcount4; + } else { + numaGetFValue(na4, i, &count4); + numaGetFValue(na8, i, &count8); + diff48 = (count4 - count8) / firstcount4; + diff4 = L_ABS(prevcount4 - count4) / firstcount4; + if (debugflag) { + fprintf(stderr, "diff48 = %7.3f, diff4 = %7.3f\n", + diff48, diff4); + } + if (diff48 < thresh48 && diff4 < threshdiff) { + found = TRUE; + break; + } + prevcount4 = count4; + } + } + numaDestroy(&na4); + numaDestroy(&na8); + + if (found) { + globthresh = start + i * incr; + if (pglobthresh) *pglobthresh = globthresh; + if (ppixd) { + *ppixd = pixConvertTo1(pix2, globthresh); + pixCopyResolution(*ppixd, pixs); + } + if (debugflag) fprintf(stderr, "global threshold = %d\n", globthresh); + pixDestroy(&pix2); + return 0; + } + + if (debugflag) fprintf(stderr, "no global threshold found\n"); + pixDestroy(&pix2); + return 1; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/binexpand.c b/hgdriver/3rdparty/hgOCR/leptonica/binexpand.c new file mode 100644 index 0000000..bb5a888 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/binexpand.c @@ -0,0 +1,303 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file binexpand.c + *
+ *
+ *      Replicated expansion (integer scaling)
+ *         PIX     *pixExpandBinaryReplicate()
+ *
+ *      Special case: power of 2 replicated expansion
+ *         PIX     *pixExpandBinaryPower2()
+ *
+ *      Expansion tables for power of 2 expansion
+ *         static l_uint16    *makeExpandTab2x()
+ *         static l_uint32    *makeExpandTab4x()
+ *         static l_uint32    *makeExpandTab8x()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Static table functions and tables */ +static l_uint16 * makeExpandTab2x(void); +static l_uint32 * makeExpandTab4x(void); +static l_uint32 * makeExpandTab8x(void); +static l_uint32 expandtab16[] = { + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff}; + + +/*------------------------------------------------------------------* + * Replicated expansion (integer scaling) * + *------------------------------------------------------------------*/ +/*! + * \brief pixExpandBinaryReplicate() + * + * \param[in] pixs 1 bpp + * \param[in] xfact integer scale factor for horiz. replicative expansion + * \param[in] yfact integer scale factor for vertical replicative expansion + * \return pixd scaled up, or NULL on error + */ +PIX * +pixExpandBinaryReplicate(PIX *pixs, + l_int32 xfact, + l_int32 yfact) +{ +l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixExpandBinaryReplicate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); + if (xfact <= 0 || yfact <= 0) + return (PIX *)ERROR_PTR("invalid scale factor: <= 0", procName, NULL); + + if (xfact == yfact) { + if (xfact == 1) + return pixCopy(NULL, pixs); + if (xfact == 2 || xfact == 4 || xfact == 8 || xfact == 16) + return pixExpandBinaryPower2(pixs, xfact); + } + + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wd = xfact * w; + hd = yfact * h; + if ((pixd = pixCreate(wd, hd, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, (l_float32)xfact, (l_float32)yfact); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + yfact * i * wpld; + for (j = 0; j < w; j++) { /* replicate pixels on a single line */ + if (GET_DATA_BIT(lines, j)) { + start = xfact * j; + for (k = 0; k < xfact; k++) + SET_DATA_BIT(lined, start + k); + } + } + for (k = 1; k < yfact; k++) /* replicate the line */ + memcpy(lined + k * wpld, lined, 4 * wpld); + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Power of 2 expansion * + *------------------------------------------------------------------*/ +/*! + * \brief pixExpandBinaryPower2() + * + * \param[in] pixs 1 bpp + * \param[in] factor expansion factor: 1, 2, 4, 8, 16 + * \return pixd expanded 1 bpp by replication, or NULL on error + */ +PIX * +pixExpandBinaryPower2(PIX *pixs, + l_int32 factor) +{ +l_uint8 sval; +l_uint16 *tab2; +l_int32 i, j, k, w, h, d, wd, hd, wpls, wpld, sdibits, sqbits, sbytes; +l_uint32 *datas, *datad, *lines, *lined, *tab4, *tab8; +PIX *pixd; + + PROCNAME("pixExpandBinaryPower2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); + if (factor == 1) + return pixCopy(NULL, pixs); + if (factor != 2 && factor != 4 && factor != 8 && factor != 16) + return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL); + + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wd = factor * w; + hd = factor * h; + if ((pixd = pixCreate(wd, hd, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + if (factor == 2) { + tab2 = makeExpandTab2x(); + sbytes = (w + 7) / 8; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + 2 * i * wpld; + for (j = 0; j < sbytes; j++) { + sval = GET_DATA_BYTE(lines, j); + SET_DATA_TWO_BYTES(lined, j, tab2[sval]); + } + memcpy(lined + wpld, lined, 4 * wpld); + } + LEPT_FREE(tab2); + } else if (factor == 4) { + tab4 = makeExpandTab4x(); + sbytes = (w + 7) / 8; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + 4 * i * wpld; + for (j = 0; j < sbytes; j++) { + sval = GET_DATA_BYTE(lines, j); + lined[j] = tab4[sval]; + } + for (k = 1; k < 4; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + LEPT_FREE(tab4); + } else if (factor == 8) { + tab8 = makeExpandTab8x(); + sqbits = (w + 3) / 4; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + 8 * i * wpld; + for (j = 0; j < sqbits; j++) { + sval = GET_DATA_QBIT(lines, j); + if (sval > 15) + L_WARNING("sval = %d; should be < 16\n", procName, sval); + lined[j] = tab8[sval]; + } + for (k = 1; k < 8; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + LEPT_FREE(tab8); + } else { /* factor == 16 */ + sdibits = (w + 1) / 2; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + 16 * i * wpld; + for (j = 0; j < sdibits; j++) { + sval = GET_DATA_DIBIT(lines, j); + lined[j] = expandtab16[sval]; + } + for (k = 1; k < 16; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + } + + return pixd; +} + + +/*-------------------------------------------------------------------* + * Expansion tables for 2x, 4x and 8x expansion * + *-------------------------------------------------------------------*/ +static l_uint16 * +makeExpandTab2x(void) +{ +l_uint16 *tab; +l_int32 i; + + tab = (l_uint16 *) LEPT_CALLOC(256, sizeof(l_uint16)); + for (i = 0; i < 256; i++) { + if (i & 0x01) + tab[i] = 0x3; + if (i & 0x02) + tab[i] |= 0xc; + if (i & 0x04) + tab[i] |= 0x30; + if (i & 0x08) + tab[i] |= 0xc0; + if (i & 0x10) + tab[i] |= 0x300; + if (i & 0x20) + tab[i] |= 0xc00; + if (i & 0x40) + tab[i] |= 0x3000; + if (i & 0x80) + tab[i] |= 0xc000; + } + return tab; +} + + +static l_uint32 * +makeExpandTab4x(void) +{ +l_uint32 *tab; +l_int32 i; + + tab = (l_uint32 *) LEPT_CALLOC(256, sizeof(l_uint32)); + for (i = 0; i < 256; i++) { + if (i & 0x01) + tab[i] = 0xf; + if (i & 0x02) + tab[i] |= 0xf0; + if (i & 0x04) + tab[i] |= 0xf00; + if (i & 0x08) + tab[i] |= 0xf000; + if (i & 0x10) + tab[i] |= 0xf0000; + if (i & 0x20) + tab[i] |= 0xf00000; + if (i & 0x40) + tab[i] |= 0xf000000; + if (i & 0x80) + tab[i] |= 0xf0000000; + } + return tab; +} + + +static l_uint32 * +makeExpandTab8x(void) +{ +l_uint32 *tab; +l_int32 i; + + tab = (l_uint32 *) LEPT_CALLOC(16, sizeof(l_uint32)); + for (i = 0; i < 16; i++) { + if (i & 0x01) + tab[i] = 0xff; + if (i & 0x02) + tab[i] |= 0xff00; + if (i & 0x04) + tab[i] |= 0xff0000; + if (i & 0x08) + tab[i] |= 0xff000000; + } + return tab; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/binreduce.c b/hgdriver/3rdparty/hgOCR/leptonica/binreduce.c new file mode 100644 index 0000000..2f813d3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/binreduce.c @@ -0,0 +1,407 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + /*! + * \file binreduce.c + *
+  *
+  *      Subsampled 2x reduction
+  *           PIX      *pixReduceBinary2()
+  *
+  *      Rank filtered 2x reductions
+  *           PIX      *pixReduceRankBinaryCascade()
+  *           PIX      *pixReduceRankBinary2()
+  *
+  *      Permutation table for 2x rank binary reduction
+  *           l_uint8  *makeSubsampleTab2x(void)
+  * 
+ */ + +#include +#include "allheaders.h" + + + /*------------------------------------------------------------------* + * Subsampled reduction * + *------------------------------------------------------------------*/ + /*! + * \brief pixReduceBinary2() + * + * \param[in] pixs + * \param[in] intab [optional]; if null, a table is made here + * and destroyed before exit + * \return pixd 2x subsampled, or NULL on error + * + *
+	* Notes:
+	*      (1) After folding, the data is in bytes 0 and 2 of the word,
+	*          and the bits in each byte are in the following order
+	*          (with 0 being the leftmost originating pair and 7 being
+	*          the rightmost originating pair):
+	*               0 4 1 5 2 6 3 7
+	*          These need to be permuted to
+	*               0 1 2 3 4 5 6 7
+	*          which is done with an 8-bit table generated by makeSubsampleTab2x().
+	* 
+ */ +PIX * +pixReduceBinary2(PIX *pixs, + l_uint8 *intab) +{ + l_uint8 byte0, byte1; + l_uint8 *tab; + l_uint16 shortd; + l_int32 i, id, j, ws, hs, wpls, wpld, wplsi; + l_uint32 word; + l_uint32 *datas, *datad, *lines, *lined; + PIX *pixd; + + PROCNAME("pixReduceBinary2"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + if (hs <= 1) + return (PIX *)ERROR_PTR("hs must be at least 2", procName, NULL); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + + if ((pixd = pixCreate(ws / 2, hs / 2, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.5, 0.5); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + tab = (intab) ? intab : makeSubsampleTab2x(); + if (!tab) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("tab not made", procName, NULL); + } + + /* e.g., if ws = 65: wd = 32, wpls = 3, wpld = 1 --> trouble */ + wplsi = L_MIN(wpls, 2 * wpld); /* iterate over this number of words */ + + for (i = 0, id = 0; i < hs - 1; i += 2, id++) { + lines = datas + i * wpls; + lined = datad + id * wpld; + for (j = 0; j < wplsi; j++) { + word = *(lines + j); + word = word & 0xaaaaaaaa; /* mask */ + word = word | (word << 7); /* fold; data in bytes 0 & 2 */ + byte0 = word >> 24; + byte1 = (word >> 8) & 0xff; + shortd = (tab[byte0] << 8) | tab[byte1]; + SET_DATA_TWO_BYTES(lined, j, shortd); + } + } + + if (!intab) LEPT_FREE(tab); + return pixd; +} + + +/*------------------------------------------------------------------* + * Rank filtered binary reductions * + *------------------------------------------------------------------*/ + /*! + * \brief pixReduceRankBinaryCascade() + * + * \param[in] pixs 1 bpp + * \param[in] level1 threshold, in the set {0, 1, 2, 3, 4} + * \param[in] level2 threshold, in the set {0, 1, 2, 3, 4} + * \param[in] level3 threshold, in the set {0, 1, 2, 3, 4} + * \param[in] level4 threshold, in the set {0, 1, 2, 3, 4} + * \return pixd, or NULL on error + * + *
+  * Notes:
+  *      (1) This performs up to four cascaded 2x rank reductions.
+  *      (2) Use level = 0 to truncate the cascade.
+  * 
+ */ +PIX * +pixReduceRankBinaryCascade(PIX *pixs, + l_int32 level1, + l_int32 level2, + l_int32 level3, + l_int32 level4) +{ + PIX *pix1, *pix2, *pix3, *pix4; + l_uint8 *tab; + + PROCNAME("pixReduceRankBinaryCascade"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be binary", procName, NULL); + if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4) + return (PIX *)ERROR_PTR("levels must not exceed 4", procName, NULL); + + if (level1 <= 0) { + L_WARNING("no reduction because level1 not > 0\n", procName); + return pixCopy(NULL, pixs); + } + + if ((tab = makeSubsampleTab2x()) == NULL) + return (PIX *)ERROR_PTR("tab not made", procName, NULL); + + pix1 = pixReduceRankBinary2(pixs, level1, tab); + if (level2 <= 0) { + LEPT_FREE(tab); + return pix1; + } + + pix2 = pixReduceRankBinary2(pix1, level2, tab); + pixDestroy(&pix1); + if (level3 <= 0) { + LEPT_FREE(tab); + return pix2; + } + + pix3 = pixReduceRankBinary2(pix2, level3, tab); + pixDestroy(&pix2); + if (level4 <= 0) { + LEPT_FREE(tab); + return pix3; + } + + pix4 = pixReduceRankBinary2(pix3, level4, tab); + pixDestroy(&pix3); + LEPT_FREE(tab); + return pix4; +} + + +/*! + * \brief pixReduceRankBinary2() + * + * \param[in] pixs 1 bpp + * \param[in] level rank threshold: 1, 2, 3, 4 + * \param[in] intab [optional]; if null, a table is made here + * and destroyed before exit + * \return pixd 1 bpp, 2x rank threshold reduced, or NULL on error + * + *
+ * Notes:
+ *      (1) pixd is downscaled by 2x from pixs.
+ *      (2) The rank threshold specifies the minimum number of ON
+ *          pixels in each 2x2 region of pixs that are required to
+ *          set the corresponding pixel ON in pixd.
+ *      (3) Rank filtering is done to the UL corner of each 2x2 pixel block,
+ *          using only logical operations.  Then these pixels are chosen
+ *          in the 2x subsampling process, subsampled, as described
+ *          above in pixReduceBinary2().
+ * 
+ */ +PIX * +pixReduceRankBinary2(PIX *pixs, + l_int32 level, + l_uint8 *intab) +{ + l_uint8 byte0, byte1; + l_uint8 *tab; + l_uint16 shortd; + l_int32 i, id, j, ws, hs, wpls, wpld, wplsi; + l_uint32 word1, word2, word3, word4; + l_uint32 *datas, *datad, *lines, *lined; + PIX *pixd; + + PROCNAME("pixReduceRankBinary2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); + if (level < 1 || level > 4) + return (PIX *)ERROR_PTR("level must be in set {1,2,3,4}", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + if (hs <= 1) + return (PIX *)ERROR_PTR("hs must be at least 2", procName, NULL); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + + if ((pixd = pixCreate(ws / 2, hs / 2, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.5, 0.5); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + tab = (intab) ? intab : makeSubsampleTab2x(); + if (!tab) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("tab not made", procName, NULL); + } + + /* e.g., if ws = 65: wd = 32, wpls = 3, wpld = 1 --> trouble */ + wplsi = L_MIN(wpls, 2 * wpld); /* iterate over this number of words */ + + switch (level) + { + + case 1: + for (i = 0, id = 0; i < hs - 1; i += 2, id++) { + lines = datas + i * wpls; + lined = datad + id * wpld; + for (j = 0; j < wplsi; j++) { + word1 = *(lines + j); + word2 = *(lines + wpls + j); + + /* OR/OR */ + word2 = word1 | word2; + word2 = word2 | (word2 << 1); + + word2 = word2 & 0xaaaaaaaa; /* mask */ + word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ + byte0 = word1 >> 24; + byte1 = (word1 >> 8) & 0xff; + shortd = (tab[byte0] << 8) | tab[byte1]; + SET_DATA_TWO_BYTES(lined, j, shortd); + } + } + break; + + case 2: + for (i = 0, id = 0; i < hs - 1; i += 2, id++) { + lines = datas + i * wpls; + lined = datad + id * wpld; + for (j = 0; j < wplsi; j++) { + word1 = *(lines + j); + word2 = *(lines + wpls + j); + + /* (AND/OR) OR (OR/AND) */ + word3 = word1 & word2; + word3 = word3 | (word3 << 1); + word4 = word1 | word2; + word4 = word4 & (word4 << 1); + word2 = word3 | word4; + + word2 = word2 & 0xaaaaaaaa; /* mask */ + word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ + byte0 = word1 >> 24; + byte1 = (word1 >> 8) & 0xff; + shortd = (tab[byte0] << 8) | tab[byte1]; + SET_DATA_TWO_BYTES(lined, j, shortd); + } + } + break; + + case 3: + for (i = 0, id = 0; i < hs - 1; i += 2, id++) { + lines = datas + i * wpls; + lined = datad + id * wpld; + for (j = 0; j < wplsi; j++) { + word1 = *(lines + j); + word2 = *(lines + wpls + j); + + /* (AND/OR) AND (OR/AND) */ + word3 = word1 & word2; + word3 = word3 | (word3 << 1); + word4 = word1 | word2; + word4 = word4 & (word4 << 1); + word2 = word3 & word4; + + word2 = word2 & 0xaaaaaaaa; /* mask */ + word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ + byte0 = word1 >> 24; + byte1 = (word1 >> 8) & 0xff; + shortd = (tab[byte0] << 8) | tab[byte1]; + SET_DATA_TWO_BYTES(lined, j, shortd); + } + } + break; + + case 4: + for (i = 0, id = 0; i < hs - 1; i += 2, id++) { + lines = datas + i * wpls; + lined = datad + id * wpld; + for (j = 0; j < wplsi; j++) { + word1 = *(lines + j); + word2 = *(lines + wpls + j); + + /* AND/AND */ + word2 = word1 & word2; + word2 = word2 & (word2 << 1); + + word2 = word2 & 0xaaaaaaaa; /* mask */ + word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ + byte0 = word1 >> 24; + byte1 = (word1 >> 8) & 0xff; + shortd = (tab[byte0] << 8) | tab[byte1]; + SET_DATA_TWO_BYTES(lined, j, shortd); + } + } + break; + } + + if (!intab) LEPT_FREE(tab); + return pixd; +} + + +/*! + * \brief makeSubsampleTab2x() + * + * \return tab table of 256 permutations, or NULL on error + * + *
+ * Notes:
+ *      Permutation table for 2x rank binary reduction
+ *      This table permutes the bits in a byte, from
+ *          0 4 1 5 2 6 3 7
+ *      to
+ *          0 1 2 3 4 5 6 7
+ * 
+ */ +l_uint8 * +makeSubsampleTab2x(void) +{ + l_uint8 *tab; + l_int32 i; + + PROCNAME("makeSubsampleTab2x"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + + for (i = 0; i < 256; i++) + tab[i] = ((i & 0x01)) | /* 7 */ + ((i & 0x04) >> 1) | /* 6 */ + ((i & 0x10) >> 2) | /* 5 */ + ((i & 0x40) >> 3) | /* 4 */ + ((i & 0x02) << 3) | /* 3 */ + ((i & 0x08) << 2) | /* 2 */ + ((i & 0x20) << 1) | /* 1 */ + ((i & 0x80)); /* 0 */ + + return tab; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/blend.c b/hgdriver/3rdparty/hgOCR/leptonica/blend.c new file mode 100644 index 0000000..a8e5273 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/blend.c @@ -0,0 +1,2293 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file blend.c + *
+ *
+ *      Blending two images that are not colormapped
+ *           PIX             *pixBlend()
+ *           PIX             *pixBlendMask()
+ *           PIX             *pixBlendGray()
+ *           PIX             *pixBlendGrayInverse()
+ *           PIX             *pixBlendColor()
+ *           PIX             *pixBlendColorByChannel()
+ *           PIX             *pixBlendGrayAdapt()
+ *           static l_int32   blendComponents()
+ *           PIX             *pixFadeWithGray()
+ *           PIX             *pixBlendHardLight()
+ *           static l_int32   blendHardLightComponents()
+ *
+ *      Blending two colormapped images
+ *           l_int32          pixBlendCmap()
+ *
+ *      Blending two images using a third (alpha mask)
+ *           PIX             *pixBlendWithGrayMask()
+ *
+ *      Blending background to a specific color
+ *           PIX             *pixBlendBackgroundToColor()
+ *
+ *      Multiplying by a specific color
+ *           PIX             *pixMultiplyByColor()
+ *
+ *      Rendering with alpha blending over a uniform background
+ *           PIX             *pixAlphaBlendUniform()
+ *
+ *      Adding an alpha layer for blending
+ *           PIX             *pixAddAlphaToBlend()
+ *
+ *      Setting a transparent alpha component over a white background
+ *           PIX             *pixSetAlphaOverWhite()
+ *
+ *      Fading from the edge
+ *           l_int32          pixLinearEdgeFade()
+ *
+ *  In blending operations a new pix is produced where typically
+ *  a subset of pixels in src1 are changed by the set of pixels
+ *  in src2, when src2 is located in a given position relative
+ *  to src1.  This is similar to rasterop, except that the
+ *  blending operations we allow are more complex, and typically
+ *  result in dest pixels that are a linear combination of two
+ *  pixels, such as src1 and its inverse.  I find it convenient
+ *  to think of src2 as the "blender" (the one that takes the action)
+ *  and src1 as the "blendee" (the one that changes).
+ *
+ *  Blending works best when src1 is 8 or 32 bpp.  We also allow
+ *  src1 to be colormapped, but the colormap is removed before blending,
+ *  so if src1 is colormapped, we can't allow in-place blending.
+ *
+ *  Because src2 is typically smaller than src1, we can implement by
+ *  clipping src2 to src1 and then transforming some of the dest
+ *  pixels that are under the support of src2.  In practice, we
+ *  do the clipping in the inner pixel loop.  For grayscale and
+ *  color src2, we also allow a simple form of transparency, where
+ *  pixels of a particular value in src2 are transparent; for those pixels,
+ *  no blending is done.
+ *
+ *  The blending functions are categorized by the depth of src2,
+ *  the blender, and not that of src1, the blendee.
+ *
+ *   ~ If src2 is 1 bpp, we can do one of three things:
+ *     (1) L_BLEND_WITH_INVERSE: Blend a given fraction of src1 with its
+ *         inverse color for those pixels in src2 that are fg (ON),
+ *         and leave the dest pixels unchanged for pixels in src2 that
+ *         are bg (OFF).
+ *     (2) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by a
+ *         given fraction for those pixels in src2 that are fg (ON),
+ *         and leave the dest pixels unchanged for pixels in src2 that
+ *         are bg (OFF).
+ *     (3) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by a
+ *         given fraction for those pixels in src2 that are fg (ON),
+ *         and leave the dest pixels unchanged for pixels in src2 that
+ *         are bg (OFF).
+ *     The blending function is pixBlendMask().
+ *
+ *   ~ If src2 is 8 bpp grayscale, we can do one of two things
+ *     (but see pixFadeWithGray() below):
+ *     (1) L_BLEND_GRAY: If src1 is 8 bpp, mix the two values, using
+ *         a fraction of src2 and (1 - fraction) of src1.
+ *         If src1 is 32 bpp (rgb), mix the fraction of src2 with
+ *         each of the color components in src1.
+ *     (2) L_BLEND_GRAY_WITH_INVERSE: Use the grayscale value in src2
+ *         to determine how much of the inverse of a src1 pixel is
+ *         to be combined with the pixel value.  The input fraction
+ *         further acts to scale the change in the src1 pixel.
+ *     The blending function is pixBlendGray().
+ *
+ *   ~ If src2 is color, we blend a given fraction of src2 with
+ *     src1.  If src1 is 8 bpp, the resulting image is 32 bpp.
+ *     The blending function is pixBlendColor().
+ *
+ *   ~ For all three blending functions -- pixBlendMask(), pixBlendGray()
+ *     and pixBlendColor() -- you can apply the blender to the blendee
+ *     either in-place or generating a new pix.  For the in-place
+ *     operation, this requires that the depth of the resulting pix
+ *     must equal that of the input pixs1.
+ *
+ *   ~ We remove colormaps from src1 and src2 before blending.
+ *     Any quantization would have to be done after blending.
+ *
+ *  We include another function, pixFadeWithGray(), that blends
+ *  a gray or color src1 with a gray src2.  It does one of these things:
+ *     (1) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by
+ *         a number times the value in src2.
+ *     (2) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by
+ *         a number times the value in src2.
+ *
+ *  Also included is a generalization of the so-called "hard light"
+ *  blending: pixBlendHardLight().  We generalize by allowing a fraction < 1.0
+ *  of the blender to be admixed with the blendee.  The standard function
+ *  does full mixing.
+ * 
+ */ + + +#include "allheaders.h" + +static l_int32 blendComponents(l_int32 a, l_int32 b, l_float32 fract); +static l_int32 blendHardLightComponents(l_int32 a, l_int32 b, l_float32 fract); + + +/*-------------------------------------------------------------* + * Blending two images that are not colormapped * + *-------------------------------------------------------------*/ +/*! + * \brief pixBlend() + * + * \param[in] pixs1 blendee + * \param[in] pixs2 blender; typ. smaller + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1; can be < 0 + * \param[in] fract blending fraction + * \return pixd blended image, or null on error + * + *
+ * Notes:
+ *      (1) This is a simple top-level interface.  For more flexibility,
+ *          call directly into pixBlendMask(), etc.
+ * 
+ */ +PIX * +pixBlend(PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract) +{ +l_int32 w1, h1, d1, d2; +BOX *box; +PIX *pixc, *pixt, *pixd; + + PROCNAME("pixBlend"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + + /* check relative depths */ + d1 = pixGetDepth(pixs1); + d2 = pixGetDepth(pixs2); + if (d1 == 1 && d2 > 1) + return (PIX *)ERROR_PTR("mixing gray or color with 1 bpp", + procName, NULL); + + /* remove colormap from pixs2 if necessary */ + pixt = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); + d2 = pixGetDepth(pixt); + + /* Check if pixs2 is clipped by its position with respect + * to pixs1; if so, clip it and redefine x and y if necessary. + * This actually isn't necessary, as the specific blending + * functions do the clipping directly in the pixel loop + * over pixs2, but it's included here to show how it can + * easily be done on pixs2 first. */ + pixGetDimensions(pixs1, &w1, &h1, NULL); + box = boxCreate(-x, -y, w1, h1); /* box of pixs1 relative to pixs2 */ + pixc = pixClipRectangle(pixt, box, NULL); + boxDestroy(&box); + if (!pixc) { + L_WARNING("box doesn't overlap pix\n", procName); + pixDestroy(&pixt); + return NULL; + } + x = L_MAX(0, x); + y = L_MAX(0, y); + + if (d2 == 1) { + pixd = pixBlendMask(NULL, pixs1, pixc, x, y, fract, + L_BLEND_WITH_INVERSE); + } else if (d2 == 8) { + pixd = pixBlendGray(NULL, pixs1, pixc, x, y, fract, + L_BLEND_GRAY, 0, 0); + } else { /* d2 == 32 */ + pixd = pixBlendColor(NULL, pixs1, pixc, x, y, fract, 0, 0); + } + + pixDestroy(&pixc); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixBlendMask() + * + * \param[in] pixd [optional]; either NULL or equal to pixs1 for in-place + * \param[in] pixs1 blendee, depth > 1 + * \param[in] pixs2 blender, 1 bpp; typ. smaller in size than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1; can be < 0 + * \param[in] fract blending fraction + * \param[in] type L_BLEND_WITH_INVERSE, L_BLEND_TO_WHITE, + * L_BLEND_TO_BLACK + * \return pixd if OK; null on error + * + *
+ * Notes:
+ *      (1) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (2) If pixs1 has a colormap, it is removed.
+ *      (3) For inplace operation (pixs1 not cmapped), call it this way:
+ *            pixBlendMask(pixs1, pixs1, pixs2, ...)
+ *      (4) For generating a new pixd:
+ *            pixd = pixBlendMask(NULL, pixs1, pixs2, ...)
+ *      (5) Only call in-place if pixs1 does not have a colormap.
+ *      (6) Invalid %fract defaults to 0.5 with a warning.
+ *          Invalid %type defaults to L_BLEND_WITH_INVERSE with a warning.
+ * 
+ */ +PIX * +pixBlendMask(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract, + l_int32 type) +{ +l_int32 i, j, d, wc, hc, w, h, wplc; +l_int32 val, rval, gval, bval; +l_uint32 pixval; +l_uint32 *linec, *datac; +PIX *pixc, *pix1, *pix2; + + PROCNAME("pixBlendMask"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL); + if (pixGetDepth(pixs2) != 1) + return (PIX *)ERROR_PTR("pixs2 not 1 bpp", procName, NULL); + if (pixd == pixs1 && pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", procName, NULL); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE && + type != L_BLEND_TO_BLACK) { + L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE\n", + procName); + type = L_BLEND_WITH_INVERSE; + } + + /* If pixd != NULL, we know that it is equal to pixs1 and + * that pixs1 does not have a colormap, so that an in-place operation + * can be done. Otherwise, remove colormap from pixs1 if + * it exists and unpack to at least 8 bpp if necessary, + * to do the blending on a new pix. */ + if (!pixd) { + pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pix1) < 8) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixd = pixCopy(NULL, pix2); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */ + pixc = pixClone(pixs2); + wc = pixGetWidth(pixc); + hc = pixGetHeight(pixc); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + + /* Check limits for src1, in case clipping was not done. */ + switch (type) + { + case L_BLEND_WITH_INVERSE: + /* + * The basic logic for this blending is: + * p --> (1 - f) * p + f * (1 - p) + * where p is a normalized value: p = pixval / 255. + * Thus, + * p --> p + f * (1 - 2 * p) + */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + bval = GET_DATA_BIT(linec, j); + if (bval) { + switch (d) + { + case 8: + pixGetPixel(pixd, x + j, y + i, &pixval); + val = (l_int32)(pixval + fract * (255 - 2 * pixval)); + pixSetPixel(pixd, x + j, y + i, val); + break; + case 32: + pixGetPixel(pixd, x + j, y + i, &pixval); + extractRGBValues(pixval, &rval, &gval, &bval); + rval = (l_int32)(rval + fract * (255 - 2 * rval)); + gval = (l_int32)(gval + fract * (255 - 2 * gval)); + bval = (l_int32)(bval + fract * (255 - 2 * bval)); + composeRGBPixel(rval, gval, bval, &pixval); + pixSetPixel(pixd, x + j, y + i, pixval); + break; + default: + L_WARNING("d neither 8 nor 32 bpp; no blend\n", + procName); + } + } + } + } + break; + case L_BLEND_TO_WHITE: + /* + * The basic logic for this blending is: + * p --> p + f * (1 - p) (p normalized to [0...1]) + */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + bval = GET_DATA_BIT(linec, j); + if (bval) { + switch (d) + { + case 8: + pixGetPixel(pixd, x + j, y + i, &pixval); + val = (l_int32)(pixval + fract * (255 - pixval)); + pixSetPixel(pixd, x + j, y + i, val); + break; + case 32: + pixGetPixel(pixd, x + j, y + i, &pixval); + extractRGBValues(pixval, &rval, &gval, &bval); + rval = (l_int32)(rval + fract * (255 - rval)); + gval = (l_int32)(gval + fract * (255 - gval)); + bval = (l_int32)(bval + fract * (255 - bval)); + composeRGBPixel(rval, gval, bval, &pixval); + pixSetPixel(pixd, x + j, y + i, pixval); + break; + default: + L_WARNING("d neither 8 nor 32 bpp; no blend\n", + procName); + } + } + } + } + break; + case L_BLEND_TO_BLACK: + /* + * The basic logic for this blending is: + * p --> (1 - f) * p (p normalized to [0...1]) + */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + bval = GET_DATA_BIT(linec, j); + if (bval) { + switch (d) + { + case 8: + pixGetPixel(pixd, x + j, y + i, &pixval); + val = (l_int32)((1. - fract) * pixval); + pixSetPixel(pixd, x + j, y + i, val); + break; + case 32: + pixGetPixel(pixd, x + j, y + i, &pixval); + extractRGBValues(pixval, &rval, &gval, &bval); + rval = (l_int32)((1. - fract) * rval); + gval = (l_int32)((1. - fract) * gval); + bval = (l_int32)((1. - fract) * bval); + composeRGBPixel(rval, gval, bval, &pixval); + pixSetPixel(pixd, x + j, y + i, pixval); + break; + default: + L_WARNING("d neither 8 nor 32 bpp; no blend\n", + procName); + } + } + } + } + break; + default: + L_WARNING("invalid binary mask blend type\n", procName); + break; + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixBlendGray() + * + * \param[in] pixd [optional] either equal to pixs1 for in-place, + * or NULL + * \param[in] pixs1 blendee, depth > 1 + * \param[in] pixs2 blender, any depth; typically, the area of + * pixs2 is smaller than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1; can be < 0 + * \param[in] fract blending fraction + * \param[in] type L_BLEND_GRAY, L_BLEND_GRAY_WITH_INVERSE + * \param[in] transparent 1 to use transparency; 0 otherwise + * \param[in] transpix pixel grayval in pixs2 that is to be transparent + * \return pixd if OK; pixs1 on error + * + *
+ * Notes:
+ *      (1) For inplace operation (pixs1 not cmapped), call it this way:
+ *            pixBlendGray(pixs1, pixs1, pixs2, ...)
+ *      (2) For generating a new pixd:
+ *            pixd = pixBlendGray(NULL, pixs1, pixs2, ...)
+ *      (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (4) If pixs1 has a colormap, it is removed; otherwise, if pixs1
+ *          has depth < 8, it is unpacked to generate a 8 bpp pix.
+ *      (5) If transparent = 0, the blending fraction (fract) is
+ *          applied equally to all pixels.
+ *      (6) If transparent = 1, all pixels of value transpix (typically
+ *          either 0 or 0xff) in pixs2 are transparent in the blend.
+ *      (7) After processing pixs1, it is either 8 bpp or 32 bpp:
+ *          ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1.
+ *          ~ if 32 bpp, each component of pixs1 is mixed with
+ *            the same fraction of pixs2.
+ *      (8) For L_BLEND_GRAY_WITH_INVERSE, the white values of the blendee
+ *          (cval == 255 in the code below) result in a delta of 0.
+ *          Thus, these pixels are intrinsically transparent!
+ *          The "pivot" value of the src, at which no blending occurs, is
+ *          128.  Compare with the adaptive pivot in pixBlendGrayAdapt().
+ *      (9) Invalid %fract defaults to 0.5 with a warning.
+ *          Invalid %type defaults to L_BLEND_GRAY with a warning.
+ * 
+ */ +PIX * +pixBlendGray(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract, + l_int32 type, + l_int32 transparent, + l_uint32 transpix) +{ +l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta; +l_int32 ival, irval, igval, ibval, cval, dval; +l_uint32 val32; +l_uint32 *linec, *lined, *datac, *datad; +PIX *pixc, *pix1, *pix2; + + PROCNAME("pixBlendGray"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); + if (pixd == pixs1 && pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) { + L_WARNING("invalid blend type; setting to L_BLEND_GRAY\n", procName); + type = L_BLEND_GRAY; + } + + /* If pixd != NULL, we know that it is equal to pixs1 and + * that pixs1 does not have a colormap, so that an in-place operation + * can be done. Otherwise, remove colormap from pixs1 if + * it exists and unpack to at least 8 bpp if necessary, + * to do the blending on a new pix. */ + if (!pixd) { + pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pix1) < 8) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixd = pixCopy(NULL, pix2); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + pixc = pixConvertTo8(pixs2, 0); + pixGetDimensions(pixc, &wc, &hc, NULL); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + + /* Check limits for src1, in case clipping was not done */ + if (type == L_BLEND_GRAY) { + /* + * The basic logic for this blending is: + * p --> (1 - f) * p + f * c + * where c is the 8 bpp blender. All values are normalized to [0...1]. + */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + switch (d) + { + case 8: + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + if (transparent == 0 || cval != transpix) { + dval = GET_DATA_BYTE(lined, j + x); + ival = (l_int32)((1. - fract) * dval + fract * cval); + SET_DATA_BYTE(lined, j + x, ival); + } + } + break; + case 32: + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + if (transparent == 0 || cval != transpix) { + val32 = *(lined + j + x); + extractRGBValues(val32, &irval, &igval, &ibval); + irval = (l_int32)((1. - fract) * irval + fract * cval); + igval = (l_int32)((1. - fract) * igval + fract * cval); + ibval = (l_int32)((1. - fract) * ibval + fract * cval); + composeRGBPixel(irval, igval, ibval, &val32); + *(lined + j + x) = val32; + } + } + break; + default: + break; /* shouldn't happen */ + } + } + } else { /* L_BLEND_GRAY_WITH_INVERSE */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + switch (d) + { + case 8: + /* + * For 8 bpp, the dest pix is shifted by a signed amount + * proportional to the distance from 128 (the pivot value), + * and to the darkness of src2. If the dest is darker + * than 128, it becomes lighter, and v.v. + * The basic logic is: + * d --> d + f * (0.5 - d) * (1 - c) + * where d and c are normalized pixel values for src1 and + * src2, respectively, with 8 bit normalization to [0...1]. + */ + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + if (transparent == 0 || cval != transpix) { + ival = GET_DATA_BYTE(lined, j + x); + delta = (128 - ival) * (255 - cval) / 256; + ival += (l_int32)(fract * delta + 0.5); + SET_DATA_BYTE(lined, j + x, ival); + } + } + break; + case 32: + /* Each component is shifted by the same formula for 8 bpp */ + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + if (transparent == 0 || cval != transpix) { + val32 = *(lined + j + x); + extractRGBValues(val32, &irval, &igval, &ibval); + delta = (128 - irval) * (255 - cval) / 256; + irval += (l_int32)(fract * delta + 0.5); + delta = (128 - igval) * (255 - cval) / 256; + igval += (l_int32)(fract * delta + 0.5); + delta = (128 - ibval) * (255 - cval) / 256; + ibval += (l_int32)(fract * delta + 0.5); + composeRGBPixel(irval, igval, ibval, &val32); + *(lined + j + x) = val32; + } + } + break; + default: + break; /* shouldn't happen */ + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixBlendGrayInverse() + * + * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL + * \param[in] pixd [optional] either NULL or equal to pixs1 for in-place + * \param[in] pixs1 blendee, depth > 1 + * \param[in] pixs2 blender, any depth; typ. smaller in size than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1; can be < 0 + * \param[in] fract blending fraction + * \return pixd if OK; pixs1 on error + * + *
+ * Notes:
+ *      (1) For inplace operation (pixs1 not cmapped), call it this way:
+ *            pixBlendGrayInverse(pixs1, pixs1, pixs2, ...)
+ *      (2) For generating a new pixd:
+ *            pixd = pixBlendGrayInverse(NULL, pixs1, pixs2, ...)
+ *      (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (4) If pixs1 has a colormap, it is removed; otherwise if pixs1
+ *          has depth < 8, it is unpacked to generate a 8 bpp pix.
+ *      (5) This is a no-nonsense blender.  It changes the src1 pixel except
+ *          when the src1 pixel is midlevel gray.  Use fract == 1 for the most
+ *          aggressive blending, where, if the gray pixel in pixs2 is 0,
+ *          we get a complete inversion of the color of the src pixel in pixs1.
+ *      (6) The basic logic is that each component transforms by:
+                 d  -->  c * d + (1 - c ) * (f * (1 - d) + d * (1 - f))
+ *          where c is the blender pixel from pixs2,
+ *                f is %fract,
+ *                c and d are normalized to [0...1]
+ *          This has the property that for f == 0 (no blend) or c == 1 (white):
+ *               d  -->  d
+ *          For c == 0 (black) we get maximum inversion:
+ *               d  -->  f * (1 - d) + d * (1 - f)   [inversion by fraction f]
+ * 
+ */ +PIX * +pixBlendGrayInverse(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract) +{ +l_int32 i, j, d, wc, hc, w, h, wplc, wpld; +l_int32 irval, igval, ibval, cval, dval; +l_float32 a; +l_uint32 val32; +l_uint32 *linec, *lined, *datac, *datad; +PIX *pixc, *pix1, *pix2; + + PROCNAME("pixBlendGrayInverse"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); + if (pixd == pixs1 && pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + + /* If pixd != NULL, we know that it is equal to pixs1 and + * that pixs1 does not have a colormap, so that an in-place operation + * can be done. Otherwise, remove colormap from pixs1 if + * it exists and unpack to at least 8 bpp if necessary, + * to do the blending on a new pix. */ + if (!pixd) { + pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pix1) < 8) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixd = pixCopy(NULL, pix2); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + pixc = pixConvertTo8(pixs2, 0); + pixGetDimensions(pixc, &wc, &hc, NULL); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + + /* Check limits for src1, in case clipping was not done */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + switch (d) + { + case 8: + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + dval = GET_DATA_BYTE(lined, j + x); + a = (1.0 - fract) * dval + fract * (255.0 - dval); + dval = (l_int32)(cval * dval / 255.0 + + a * (255.0 - cval) / 255.0); + SET_DATA_BYTE(lined, j + x, dval); + } + break; + case 32: + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + val32 = *(lined + j + x); + extractRGBValues(val32, &irval, &igval, &ibval); + a = (1.0 - fract) * irval + fract * (255.0 - irval); + irval = (l_int32)(cval * irval / 255.0 + + a * (255.0 - cval) / 255.0); + a = (1.0 - fract) * igval + fract * (255.0 - igval); + igval = (l_int32)(cval * igval / 255.0 + + a * (255.0 - cval) / 255.0); + a = (1.0 - fract) * ibval + fract * (255.0 - ibval); + ibval = (l_int32)(cval * ibval / 255.0 + + a * (255.0 - cval) / 255.0); + composeRGBPixel(irval, igval, ibval, &val32); + *(lined + j + x) = val32; + } + break; + default: + break; /* shouldn't happen */ + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixBlendColor() + * + * \param[in] pixd [optional] either equal to pixs1 for in-place, + * or NULL + * \param[in] pixs1 blendee; depth > 1 + * \param[in] pixs2 blender, any depth; typically, the area of + * pixs2 is smaller than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1 + * \param[in] fract blending fraction + * \param[in] transparent 1 to use transparency; 0 otherwise + * \param[in] transpix pixel color in pixs2 that is to be transparent + * \return pixd, or null on error + * + *
+ * Notes:
+ *      (1) For inplace operation (pixs1 must be 32 bpp), call it this way:
+ *            pixBlendColor(pixs1, pixs1, pixs2, ...)
+ *      (2) For generating a new pixd:
+ *            pixd = pixBlendColor(NULL, pixs1, pixs2, ...)
+ *      (3) If pixs2 is not 32 bpp rgb, it is converted.
+ *      (4) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (5) If pixs1 has a colormap, it is removed to generate a 32 bpp pix.
+ *      (6) If pixs1 has depth < 32, it is unpacked to generate a 32 bpp pix.
+ *      (7) If transparent = 0, the blending fraction (fract) is
+ *          applied equally to all pixels.
+ *      (8) If transparent = 1, all pixels of value transpix (typically
+ *          either 0 or 0xffffff00) in pixs2 are transparent in the blend.
+ * 
+ */ +PIX * +pixBlendColor(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract, + l_int32 transparent, + l_uint32 transpix) +{ +l_int32 i, j, wc, hc, w, h, wplc, wpld; +l_int32 rval, gval, bval, rcval, gcval, bcval; +l_uint32 cval32, val32; +l_uint32 *linec, *lined, *datac, *datad; +PIX *pixc; + + PROCNAME("pixBlendColor"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL); + if (pixd == pixs1 && pixGetDepth(pixs1) != 32) + return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, NULL); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + + /* If pixd != null, we know that it is equal to pixs1 and + * that pixs1 is 32 bpp rgb, so that an in-place operation + * can be done. Otherwise, pixConvertTo32() will remove a + * colormap from pixs1 if it exists and unpack to 32 bpp + * (if necessary) to do the blending on a new 32 bpp Pix. */ + if (!pixd) + pixd = pixConvertTo32(pixs1); + pixGetDimensions(pixd, &w, &h, NULL); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + pixc = pixConvertTo32(pixs2); /* blend with 32 bpp rgb */ + pixGetDimensions(pixc, &wc, &hc, NULL); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + + /* Check limits for src1, in case clipping was not done */ + for (i = 0; i < hc; i++) { + /* + * The basic logic for this blending is: + * p --> (1 - f) * p + f * c + * for each color channel. c is a color component of the blender. + * All values are normalized to [0...1]. + */ + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval32 = *(linec + j); + if (transparent == 0 || + ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { + val32 = *(lined + j + x); + extractRGBValues(cval32, &rcval, &gcval, &bcval); + extractRGBValues(val32, &rval, &gval, &bval); + rval = (l_int32)((1. - fract) * rval + fract * rcval); + gval = (l_int32)((1. - fract) * gval + fract * gcval); + bval = (l_int32)((1. - fract) * bval + fract * bcval); + composeRGBPixel(rval, gval, bval, &val32); + *(lined + j + x) = val32; + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/* + * \brief pixBlendColorByChannel() + * + * \param[in] pixd [optional] either equal to pixs1 for in-place, + * or NULL + * \param[in] pixs1 blendee; depth > 1 + * \param[in] pixs2 blender, any depth; typically, the area of + * pixs2 is smaller than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1 + * \param[in] rfract blending fraction in red channel + * \param[in] gfract blending fraction in green channel + * \param[in] bfract blending fraction in blue channel + * \param[in] transparent 1 to use transparency; 0 otherwise + * \param[in] transpix pixel color in pixs2 that is to be transparent + * \return pixd if OK; pixd on error + * + *
+ * Notes:
+ *      (1) This generalizes pixBlendColor() in two ways:
+ *          (a) The mixing fraction is specified per channel.
+ *          (b) The mixing fraction may be < 0 or > 1, in which case,
+ *              the min or max of two images are taken, respectively.
+ *      (2) Specifically,
+ *          for p = pixs1[i], c = pixs2[i], f = fract[i], i = 1, 2, 3:
+ *              f < 0.0:          p --> min(p, c)
+ *              0.0 <= f <= 1.0:  p --> (1 - f) * p + f * c
+ *              f > 1.0:          p --> max(a, c)
+ *          Special cases:
+ *              f = 0:   p --> p
+ *              f = 1:   p --> c
+ *      (3) See usage notes in pixBlendColor()
+ *      (4) pixBlendColor() would be equivalent to
+ *            pixBlendColorChannel(..., fract, fract, fract, ...);
+ *          at a small cost of efficiency.
+ * 
+ */ +PIX * +pixBlendColorByChannel(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 rfract, + l_float32 gfract, + l_float32 bfract, + l_int32 transparent, + l_uint32 transpix) +{ +l_int32 i, j, wc, hc, w, h, wplc, wpld; +l_int32 rval, gval, bval, rcval, gcval, bcval; +l_uint32 cval32, val32; +l_uint32 *linec, *lined, *datac, *datad; +PIX *pixc; + + PROCNAME("pixBlendColorByChannel"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); + if (pixd == pixs1 && pixGetDepth(pixs1) != 32) + return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); + + /* If pixd != NULL, we know that it is equal to pixs1 and + * that pixs1 is 32 bpp rgb, so that an in-place operation + * can be done. Otherwise, pixConvertTo32() will remove a + * colormap from pixs1 if it exists and unpack to 32 bpp + * (if necessary) to do the blending on a new 32 bpp Pix. */ + if (!pixd) + pixd = pixConvertTo32(pixs1); + pixGetDimensions(pixd, &w, &h, NULL); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + pixc = pixConvertTo32(pixs2); + pixGetDimensions(pixc, &wc, &hc, NULL); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + + /* Check limits for src1, in case clipping was not done */ + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval32 = *(linec + j); + if (transparent == 0 || + ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { + val32 = *(lined + j + x); + extractRGBValues(cval32, &rcval, &gcval, &bcval); + extractRGBValues(val32, &rval, &gval, &bval); + rval = blendComponents(rval, rcval, rfract); + gval = blendComponents(gval, gcval, gfract); + bval = blendComponents(bval, bcval, bfract); + composeRGBPixel(rval, gval, bval, &val32); + *(lined + j + x) = val32; + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +static l_int32 +blendComponents(l_int32 a, + l_int32 b, + l_float32 fract) +{ + if (fract < 0.) + return ((a < b) ? a : b); + if (fract > 1.) + return ((a > b) ? a : b); + return (l_int32)((1. - fract) * a + fract * b); +} + + +/*! + * \brief pixBlendGrayAdapt() + * + * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL + * \param[in] pixs1 blendee; depth > 1 + * \param[in] pixs2 blender, any depth; typically, the area of + * pixs2 is smaller than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1; can be < 0 + * \param[in] fract blending fraction + * \param[in] shift >= 0 but <= 128: shift of zero blend value from + * median source; use -1 for default value; + * \return pixd if OK; pixs1 on error + * + *
+ * Notes:
+ *      (1) For inplace operation (pixs1 not cmapped), call it this way:
+ *            pixBlendGrayAdapt(pixs1, pixs1, pixs2, ...)
+ *          For generating a new pixd:
+ *            pixd = pixBlendGrayAdapt(NULL, pixs1, pixs2, ...)
+ *      (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (3) If pixs1 has a colormap, it is removed.
+ *      (4) If pixs1 has depth < 8, it is unpacked to generate a 8 bpp pix.
+ *      (5) This does a blend with inverse.  Whereas in pixGlendGray(), the
+ *          zero blend point is where the blendee pixel is 128, here
+ *          the zero blend point is found adaptively, with respect to the
+ *          median of the blendee region.  If the median is < 128,
+ *          the zero blend point is found from
+ *              median + shift.
+ *          Otherwise, if the median >= 128, the zero blend point is
+ *              median - shift.
+ *          The purpose of shifting the zero blend point away from the
+ *          median is to prevent a situation in pixBlendGray() where
+ *          the median is 128 and the blender is not visible.
+ *          The default value of shift is 64.
+ *      (6) After processing pixs1, it is either 8 bpp or 32 bpp:
+ *          ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1.
+ *          ~ if 32 bpp, each component of pixs1 is mixed with
+ *            the same fraction of pixs2.
+ *      (7) The darker the blender, the more it mixes with the blendee.
+ *          A blender value of 0 has maximum mixing; a value of 255
+ *          has no mixing and hence is transparent.
+ * 
+ */ +PIX * +pixBlendGrayAdapt(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract, + l_int32 shift) +{ +l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap; +l_int32 rval, gval, bval, cval, dval, mval, median, pivot; +l_uint32 val32; +l_uint32 *linec, *lined, *datac, *datad; +l_float32 fmedian, factor; +BOX *box, *boxt; +PIX *pixc, *pix1, *pix2; + + PROCNAME("pixBlendGrayAdapt"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixGetDepth(pixs1) == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); + if (pixd == pixs1 && pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + if (shift == -1) shift = 64; /* default value */ + if (shift < 0 || shift > 127) { + L_WARNING("invalid shift; setting to 64\n", procName); + shift = 64; + } + + /* Test for overlap */ + pixGetDimensions(pixs1, &w, &h, NULL); + pixGetDimensions(pixs2, &wc, &hc, NULL); + box = boxCreate(x, y, wc, hc); + boxt = boxCreate(0, 0, w, h); + boxIntersects(box, boxt, &overlap); + boxDestroy(&boxt); + if (!overlap) { + boxDestroy(&box); + return (PIX *)ERROR_PTR("no image overlap", procName, pixd); + } + + /* If pixd != NULL, we know that it is equal to pixs1 and + * that pixs1 does not have a colormap, so that an in-place operation + * can be done. Otherwise, remove colormap from pixs1 if + * it exists and unpack to at least 8 bpp if necessary, + * to do the blending on a new pix. */ + if (!pixd) { + pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pix1) < 8) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixd = pixCopy(NULL, pix2); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + /* Get the median value in the region of blending */ + pix1 = pixClipRectangle(pixd, box, NULL); + pix2 = pixConvertTo8(pix1, 0); + pixGetRankValueMasked(pix2, NULL, 0, 0, 1, 0.5, &fmedian, NULL); + median = (l_int32)(fmedian + 0.5); + if (median < 128) + pivot = median + shift; + else + pivot = median - shift; + pixDestroy(&pix1); + pixDestroy(&pix2); + boxDestroy(&box); + + /* Process over src2; clip to src1. */ + d = pixGetDepth(pixd); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + pixc = pixConvertTo8(pixs2, 0); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + switch (d) + { + case 8: + /* + * For 8 bpp, the dest pix is shifted by an amount + * proportional to the distance from the pivot value, + * and to the darkness of src2. In no situation will it + * pass the pivot value in intensity. + * The basic logic is: + * d --> d + f * (np - d) * (1 - c) + * where np, d and c are normalized pixel values for + * the pivot, src1 and src2, respectively, with normalization + * to 255. + */ + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + dval = GET_DATA_BYTE(lined, j + x); + cval = GET_DATA_BYTE(linec, j); + delta = (pivot - dval) * (255 - cval) / 256; + dval += (l_int32)(fract * delta + 0.5); + SET_DATA_BYTE(lined, j + x, dval); + } + break; + case 32: + /* + * For 32 bpp, the dest pix is shifted by an amount + * proportional to the max component distance from the + * pivot value, and to the darkness of src2. Each component + * is shifted by the same fraction, either up or down, + * depending on the shift direction (which is toward the + * pivot). The basic logic for the red component is: + * r --> r + f * (np - m) * (1 - c) * (r / m) + * where np, r, m and c are normalized pixel values for + * the pivot, the r component of src1, the max component + * of src1, and src2, respectively, again with normalization + * to 255. Likewise for the green and blue components. + */ + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + cval = GET_DATA_BYTE(linec, j); + val32 = *(lined + j + x); + extractRGBValues(val32, &rval, &gval, &bval); + mval = L_MAX(rval, gval); + mval = L_MAX(mval, bval); + mval = L_MAX(mval, 1); + delta = (pivot - mval) * (255 - cval) / 256; + factor = fract * delta / mval; + rval += (l_int32)(factor * rval + 0.5); + gval += (l_int32)(factor * gval + 0.5); + bval += (l_int32)(factor * bval + 0.5); + composeRGBPixel(rval, gval, bval, &val32); + *(lined + j + x) = val32; + } + break; + default: + break; /* shouldn't happen */ + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixFadeWithGray() + * + * \param[in] pixs colormapped or 8 bpp or 32 bpp + * \param[in] pixb 8 bpp blender + * \param[in] factor multiplicative factor to apply to blender value + * \param[in] type L_BLEND_TO_WHITE, L_BLEND_TO_BLACK + * \return pixd, or null on error + * + *
+ * Notes:
+ *      (1) This function combines two pix aligned to the UL corner; they
+ *          need not be the same size.
+ *      (2) Each pixel in pixb is multiplied by 'factor' divided by 255, and
+ *          clipped to the range [0 ... 1].  This gives the fade fraction
+ *          to be applied to pixs.  Fade either to white (L_BLEND_TO_WHITE)
+ *          or to black (L_BLEND_TO_BLACK).
+ * 
+ */ +PIX * +pixFadeWithGray(PIX *pixs, + PIX *pixb, + l_float32 factor, + l_int32 type) +{ +l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld; +l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval; +l_float32 nfactor, fract; +l_uint32 val32, nval32; +l_uint32 *lined, *datad, *lineb, *datab; +PIX *pixd; + + PROCNAME("pixFadeWithGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixb) + return (PIX *)ERROR_PTR("pixb not defined", procName, NULL); + if (pixGetDepth(pixs) == 1) + return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); + pixGetDimensions(pixb, &wb, &hb, &db); + if (db != 8) + return (PIX *)ERROR_PTR("pixb not 8 bpp", procName, NULL); + if (factor < 0.0 || factor > 255.0) + return (PIX *)ERROR_PTR("factor not in [0.0...255.0]", procName, NULL); + if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK) + return (PIX *)ERROR_PTR("invalid fade type", procName, NULL); + + /* Remove colormap if it exists; otherwise copy */ + pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); + pixGetDimensions(pixd, &wd, &hd, &d); + w = L_MIN(wb, wd); + h = L_MIN(hb, hd); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datab = pixGetData(pixb); + wplb = pixGetWpl(pixb); + + /* The basic logic for this blending is, for each component p of pixs: + * fade-to-white: p --> p + (f * c) * (1 - p) + * fade-to-black: p --> p - (f * c) * p + * with c being the 8 bpp blender pixel of pixb, and with both + * p and c normalized to [0...1]. */ + nfactor = factor / 255.; + for (i = 0; i < h; i++) { + lineb = datab + i * wplb; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + valb = GET_DATA_BYTE(lineb, j); + fract = nfactor * (l_float32)valb; + fract = L_MIN(fract, 1.0); + if (d == 8) { + vald = GET_DATA_BYTE(lined, j); + if (type == L_BLEND_TO_WHITE) + nvald = vald + (l_int32)(fract * (255. - (l_float32)vald)); + else /* L_BLEND_TO_BLACK */ + nvald = vald - (l_int32)(fract * (l_float32)vald); + SET_DATA_BYTE(lined, j, nvald); + } else { /* d == 32 */ + val32 = lined[j]; + extractRGBValues(val32, &rval, &gval, &bval); + if (type == L_BLEND_TO_WHITE) { + nrval = rval + (l_int32)(fract * (255. - (l_float32)rval)); + ngval = gval + (l_int32)(fract * (255. - (l_float32)gval)); + nbval = bval + (l_int32)(fract * (255. - (l_float32)bval)); + } else { + nrval = rval - (l_int32)(fract * (l_float32)rval); + ngval = gval - (l_int32)(fract * (l_float32)gval); + nbval = bval - (l_int32)(fract * (l_float32)bval); + } + composeRGBPixel(nrval, ngval, nbval, &nval32); + lined[j] = nval32; + } + } + } + + return pixd; +} + + +/* + * \brief pixBlendHardLight() + * + * \param[in] pixd either NULL or equal to pixs1 for in-place + * \param[in] pixs1 blendee; depth > 1, may be cmapped + * \param[in] pixs2 blender, 8 or 32 bpp; may be colormapped; + * typ. smaller in size than pixs1 + * \param[in] x,y origin [UL corner] of pixs2 relative to + * the origin of pixs1 + * \param[in] fract blending fraction, or 'opacity factor' + * \return pixd if OK; pixs1 on error + * + *
+ * Notes:
+ *      (1) pixs2 must be 8 or 32 bpp; either may have a colormap.
+ *      (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
+ *      (3) Only call in-place if pixs1 is not colormapped.
+ *      (4) If pixs1 has a colormap, it is removed to generate either an
+ *          8 or 32 bpp pix, depending on the colormap.
+ *      (5) For inplace operation, call it this way:
+ *            pixBlendHardLight(pixs1, pixs1, pixs2, ...)
+ *      (6) For generating a new pixd:
+ *            pixd = pixBlendHardLight(NULL, pixs1, pixs2, ...)
+ *      (7) This is a generalization of the usual hard light blending,
+ *          where fract == 1.0.
+ *      (8) "Overlay" blending is the same as hard light blending, with
+ *          fract == 1.0, except that the components are switched
+ *          in the test.  (Note that the result is symmetric in the
+ *          two components.)
+ *      (9) See, e.g.:
+ *           http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm
+ *           http://www.digitalartform.com/imageArithmetic.htm
+ *      (10) This function was built by Paco Galanes.
+ * 
+ */ +PIX * +pixBlendHardLight(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 x, + l_int32 y, + l_float32 fract) +{ +l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld; +l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval; +l_uint32 cval32, dval32; +l_uint32 *linec, *lined, *datac, *datad; +PIX *pixc, *pixt; + + PROCNAME("pixBlendHardLight"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + pixGetDimensions(pixs1, &w, &h, &d); + pixGetDimensions(pixs2, &wc, &hc, &dc); + if (d == 1) + return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); + if (dc != 8 && dc != 32) + return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", procName, pixd); + if (pixd && (pixd != pixs1)) + return (PIX *)ERROR_PTR("inplace and pixd != pixs1", procName, pixd); + if (pixd == pixs1 && pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", procName, pixd); + if (pixd && d != 8 && d != 32) + return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", procName, pixd); + + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + + /* If pixs2 has a colormap, remove it */ + pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); /* clone ok */ + dc = pixGetDepth(pixc); + + /* There are 4 cases: + * * pixs1 has or doesn't have a colormap + * * pixc is either 8 or 32 bpp + * In all situations, if pixs has a colormap it must be removed, + * and pixd must have a depth that is equal to or greater than pixc. */ + if (dc == 32) { + if (pixGetColormap(pixs1)) { /* pixd == NULL */ + pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); + } else { + if (!pixd) { + pixd = pixConvertTo32(pixs1); + } else { + pixt = pixConvertTo32(pixs1); + pixCopy(pixd, pixt); + pixDestroy(&pixt); + } + } + d = 32; + } else { /* dc == 8 */ + if (pixGetColormap(pixs1)) /* pixd == NULL */ + pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + else + pixd = pixCopy(pixd, pixs1); + d = pixGetDepth(pixd); + } + + if (!(d == 8 && dc == 8) && /* 3 cases only */ + !(d == 32 && dc == 8) && + !(d == 32 && dc == 32)) { + pixDestroy(&pixc); + return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", procName, pixd); + } + + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + for (i = 0; i < hc; i++) { + if (i + y < 0 || i + y >= h) continue; + linec = datac + i * wplc; + lined = datad + (i + y) * wpld; + for (j = 0; j < wc; j++) { + if (j + x < 0 || j + x >= w) continue; + if (d == 8 && dc == 8) { + dval = GET_DATA_BYTE(lined, x + j); + cval = GET_DATA_BYTE(linec, j); + dval = blendHardLightComponents(dval, cval, fract); + SET_DATA_BYTE(lined, x + j, dval); + } else if (d == 32 && dc == 8) { + dval32 = *(lined + x + j); + extractRGBValues(dval32, &rdval, &gdval, &bdval); + cval = GET_DATA_BYTE(linec, j); + rdval = blendHardLightComponents(rdval, cval, fract); + gdval = blendHardLightComponents(gdval, cval, fract); + bdval = blendHardLightComponents(bdval, cval, fract); + composeRGBPixel(rdval, gdval, bdval, &dval32); + *(lined + x + j) = dval32; + } else if (d == 32 && dc == 32) { + dval32 = *(lined + x + j); + extractRGBValues(dval32, &rdval, &gdval, &bdval); + cval32 = *(linec + j); + extractRGBValues(cval32, &rcval, &gcval, &bcval); + rdval = blendHardLightComponents(rdval, rcval, fract); + gdval = blendHardLightComponents(gdval, gcval, fract); + bdval = blendHardLightComponents(bdval, bcval, fract); + composeRGBPixel(rdval, gdval, bdval, &dval32); + *(lined + x + j) = dval32; + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/* + * \brief blendHardLightComponents() + * + * \param[in] a 8 bpp blendee component + * \param[in] b 8 bpp blender component + * \param[in] fract fraction of blending; use 1.0 for usual definition + * \return blended 8 bpp component + * + *
+ * Notes:
+ *
+ *    The basic logic for this blending is:
+ *      b < 0.5:
+ *          a --> 2 * a * (0.5 - f * (0.5 - b))
+ *      b >= 0.5:
+ *          a --> 1 - 2 * (1 - a) * (1 - (0.5 - f * (0.5 - b)))
+ *
+ *    In the limit that f == 1 (standard hardlight blending):
+ *      b < 0.5:   a --> 2 * a * b
+ *                     or
+ *                 a --> a - a * (1 - 2 * b)
+ *      b >= 0.5:  a --> 1 - 2 * (1 - a) * (1 - b)
+ *                     or
+ *                 a --> a + (1 - a) * (2 * b - 1)
+ *
+ *    You can see that for standard hardlight blending:
+ *      b < 0.5:   a is pushed linearly with b down to 0
+ *      b >= 0.5:  a is pushed linearly with b up to 1
+ *    a is unchanged if b = 0.5
+ *
+ *    Our opacity factor f reduces the deviation of b from 0.5:
+ *      f == 0:  b -->  0.5, so no blending occurs
+ *      f == 1:  b -->  b, so we get full conventional blending
+ *
+ *    There is a variant of hardlight blending called "softlight" blending:
+ *    (e.g., http://jswidget.com/blog/tag/hard-light/)
+ *      b < 0.5:
+ *          a --> a - a * (0.5 - b) * (1 - Abs(2 * a - 1))
+ *      b >= 0.5:
+ *          a --> a + (1 - a) * (b - 0.5) * (1 - Abs(2 * a - 1))
+ *    which limits the amount that 'a' can be moved to a maximum of
+ *    halfway toward 0 or 1, and further reduces it as 'a' moves
+ *    away from 0.5.
+ *    As you can see, there are a nearly infinite number of different
+ *    blending formulas that can be conjured up.
+ * 
+ */ +static l_int32 blendHardLightComponents(l_int32 a, + l_int32 b, + l_float32 fract) +{ + if (b < 0x80) { + b = 0x80 - (l_int32)(fract * (0x80 - b)); + return (a * b) >> 7; + } else { + b = 0x80 + (l_int32)(fract * (b - 0x80)); + return 0xff - (((0xff - b) * (0xff - a)) >> 7); + } +} + + +/*-------------------------------------------------------------* + * Blending two colormapped images * + *-------------------------------------------------------------*/ +/*! + * \brief pixBlendCmap() + * + * \param[in] pixs 2, 4 or 8 bpp, with colormap + * \param[in] pixb colormapped blender + * \param[in] x, y UL corner of blender relative to pixs + * \param[in] sindex colormap index of pixels in pixs to be changed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function combines two colormaps, and replaces the pixels
+ *          in pixs that have a specified color value with those in pixb.
+ *      (2) sindex must be in the existing colormap; otherwise an
+ *          error is returned.  In use, sindex will typically be the index
+ *          for white (255, 255, 255).
+ *      (3) Blender colors that already exist in the colormap are used;
+ *          others are added.  If any blender colors cannot be
+ *          stored in the colormap, an error is returned.
+ *      (4) In the implementation, a mapping is generated from each
+ *          original blender colormap index to the corresponding index
+ *          in the expanded colormap for pixs.  Then for each pixel in
+ *          pixs with value sindex, and which is covered by a blender pixel,
+ *          the new index corresponding to the blender pixel is substituted
+ *          for sindex.
+ * 
+ */ +l_ok +pixBlendCmap(PIX *pixs, + PIX *pixb, + l_int32 x, + l_int32 y, + l_int32 sindex) +{ +l_int32 rval, gval, bval; +l_int32 i, j, w, h, d, ncb, wb, hb, wpls; +l_int32 index, val, nadded; +l_int32 lut[256]; +l_uint32 pval; +l_uint32 *lines, *datas; +PIXCMAP *cmaps, *cmapb, *cmapsc; + + PROCNAME("pixBlendCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixb) + return ERROR_INT("pixb not defined", procName, 1); + if ((cmaps = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap in pixs", procName, 1); + if ((cmapb = pixGetColormap(pixb)) == NULL) + return ERROR_INT("no colormap in pixb", procName, 1); + ncb = pixcmapGetCount(cmapb); + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("depth not in {2,4,8}", procName, 1); + + /* Make a copy of cmaps; we'll add to this if necessary + * and substitute at the end if we found there was enough room + * to hold all the new colors. */ + cmapsc = pixcmapCopy(cmaps); + + /* Add new colors if necessary; get mapping array between + * cmaps and cmapb. */ + for (i = 0, nadded = 0; i < ncb; i++) { + pixcmapGetColor(cmapb, i, &rval, &gval, &bval); + if (pixcmapGetIndex(cmapsc, rval, gval, bval, &index)) { /* not found */ + if (pixcmapAddColor(cmapsc, rval, gval, bval)) { + pixcmapDestroy(&cmapsc); + return ERROR_INT("not enough room in cmaps", procName, 1); + } + lut[i] = pixcmapGetCount(cmapsc) - 1; + nadded++; + } else { + lut[i] = index; + } + } + + /* Replace cmaps if colors have been added. */ + if (nadded == 0) + pixcmapDestroy(&cmapsc); + else + pixSetColormap(pixs, cmapsc); + + /* Replace each pixel value sindex by mapped colormap index when + * a blender pixel in pixbc overlays it. */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixGetDimensions(pixb, &wb, &hb, NULL); + for (i = 0; i < hb; i++) { + if (i + y < 0 || i + y >= h) continue; + lines = datas + (y + i) * wpls; + for (j = 0; j < wb; j++) { + if (j + x < 0 || j + x >= w) continue; + switch (d) { + case 2: + val = GET_DATA_DIBIT(lines, x + j); + if (val == sindex) { + pixGetPixel(pixb, j, i, &pval); + SET_DATA_DIBIT(lines, x + j, lut[pval]); + } + break; + case 4: + val = GET_DATA_QBIT(lines, x + j); + if (val == sindex) { + pixGetPixel(pixb, j, i, &pval); + SET_DATA_QBIT(lines, x + j, lut[pval]); + } + break; + case 8: + val = GET_DATA_BYTE(lines, x + j); + if (val == sindex) { + pixGetPixel(pixb, j, i, &pval); + SET_DATA_BYTE(lines, x + j, lut[pval]); + } + break; + default: + return ERROR_INT("depth not in {2,4,8}", procName, 1); + } + } + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Blending two images using a third * + *---------------------------------------------------------------------*/ +/*! + * \brief pixBlendWithGrayMask() + * + * \param[in] pixs1 8 bpp gray, rgb, rgba or colormapped + * \param[in] pixs2 8 bpp gray, rgb, rgba or colormapped + * \param[in] pixg [optional] 8 bpp gray, for transparency of pixs2; + * can be null + * \param[in] x, y UL corner of pixs2 and pixg with respect to pixs1 + * \return pixd blended image, or null on error + * + *
+ * Notes:
+ *      (1) The result is 8 bpp grayscale if both pixs1 and pixs2 are
+ *          8 bpp gray.  Otherwise, the result is 32 bpp rgb.
+ *      (2) pixg is an 8 bpp transparency image, where 0 is transparent
+ *          and 255 is opaque.  It determines the transparency of pixs2
+ *          when applied over pixs1.  It can be null if pixs2 is rgba,
+ *          in which case we use the alpha component of pixs2.
+ *      (3) If pixg exists, it need not be the same size as pixs2.
+ *          However, we assume their UL corners are aligned with each other,
+ *          and placed at the location (x, y) in pixs1.
+ *      (4) The pixels in pixd are a combination of those in pixs1
+ *          and pixs2, where the amount from pixs2 is proportional to
+ *          the value of the pixel (p) in pixg, and the amount from pixs1
+ *          is proportional to (255 - p).  Thus pixg is a transparency
+ *          image (usually called an alpha blender) where each pixel
+ *          can be associated with a pixel in pixs2, and determines
+ *          the amount of the pixs2 pixel in the final result.
+ *          For example, if pixg is all 0, pixs2 is transparent and
+ *          the result in pixd is simply pixs1.
+ *      (5) A typical use is for the pixs2/pixg combination to be
+ *          a small watermark that is applied to pixs1.
+ * 
+ */ +PIX * +pixBlendWithGrayMask(PIX *pixs1, + PIX *pixs2, + PIX *pixg, + l_int32 x, + l_int32 y) +{ +l_int32 w1, h1, d1, w2, h2, d2, spp, wg, hg, wmin, hmin, wpld, wpls, wplg; +l_int32 i, j, val, dval, sval; +l_int32 drval, dgval, dbval, srval, sgval, sbval; +l_uint32 dval32, sval32; +l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg; +l_float32 fract; +PIX *pixr1, *pixr2, *pix1, *pix2, *pixg2, *pixd; + + PROCNAME("pixBlendWithGrayMask"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + pixGetDimensions(pixs1, &w1, &h1, &d1); + pixGetDimensions(pixs2, &w2, &h2, &d2); + if (d1 == 1 || d2 == 1) + return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", procName, NULL); + if (pixg) { + if (pixGetDepth(pixg) != 8) + return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL); + pixGetDimensions(pixg, &wg, &hg, NULL); + wmin = L_MIN(w2, wg); + hmin = L_MIN(h2, hg); + pixg2 = pixClone(pixg); + } else { /* use the alpha component of pixs2 */ + spp = pixGetSpp(pixs2); + if (d2 != 32 || spp != 4) + return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", procName, NULL); + wmin = w2; + hmin = h2; + pixg2 = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL); + } + + /* Remove colormaps if they exist; clones are OK */ + pixr1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); + pixr2 = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); + + /* Regularize to the same depth if necessary */ + d1 = pixGetDepth(pixr1); + d2 = pixGetDepth(pixr2); + if (d1 == 32) { /* convert d2 to rgb if necessary */ + pix1 = pixClone(pixr1); + if (d2 != 32) + pix2 = pixConvertTo32(pixr2); + else + pix2 = pixClone(pixr2); + } else if (d2 == 32) { /* and d1 != 32; convert to 32 */ + pix2 = pixClone(pixr2); + pix1 = pixConvertTo32(pixr1); + } else { /* both are 8 bpp or less */ + pix1 = pixConvertTo8(pixr1, FALSE); + pix2 = pixConvertTo8(pixr2, FALSE); + } + pixDestroy(&pixr1); + pixDestroy(&pixr2); + + /* Sanity check: both either 8 or 32 bpp */ + d1 = pixGetDepth(pix1); + d2 = pixGetDepth(pix2); + if (d1 != d2 || (d1 != 8 && d1 != 32)) { + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pixg2); + return (PIX *)ERROR_PTR("depths not regularized! bad!", procName, NULL); + } + + /* Start with a copy of pix1 */ + pixd = pixCopy(NULL, pix1); + pixDestroy(&pix1); + + /* Blend pix2 onto pixd, using pixg2. + * Let the normalized pixel value of pixg2 be f = pixval / 255, + * and the pixel values of pixd and pix2 be p1 and p2, rsp. + * Then the blended value is: + * p = (1.0 - f) * p1 + f * p2 + * Blending is done component-wise if rgb. + * Scan over pix2 and pixg2, clipping to pixd where necessary. */ + datad = pixGetData(pixd); + datas = pixGetData(pix2); + datag = pixGetData(pixg2); + wpld = pixGetWpl(pixd); + wpls = pixGetWpl(pix2); + wplg = pixGetWpl(pixg2); + for (i = 0; i < hmin; i++) { + if (i + y < 0 || i + y >= h1) continue; + lined = datad + (i + y) * wpld; + lines = datas + i * wpls; + lineg = datag + i * wplg; + for (j = 0; j < wmin; j++) { + if (j + x < 0 || j + x >= w1) continue; + val = GET_DATA_BYTE(lineg, j); + if (val == 0) continue; /* pix2 is transparent */ + fract = (l_float32)val / 255.; + if (d1 == 8) { + dval = GET_DATA_BYTE(lined, j + x); + sval = GET_DATA_BYTE(lines, j); + dval = (l_int32)((1.0 - fract) * dval + fract * sval); + SET_DATA_BYTE(lined, j + x, dval); + } else { /* 32 */ + dval32 = *(lined + j + x); + sval32 = *(lines + j); + extractRGBValues(dval32, &drval, &dgval, &dbval); + extractRGBValues(sval32, &srval, &sgval, &sbval); + drval = (l_int32)((1.0 - fract) * drval + fract * srval); + dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval); + dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval); + composeRGBPixel(drval, dgval, dbval, &dval32); + *(lined + j + x) = dval32; + } + } + } + + pixDestroy(&pixg2); + pixDestroy(&pix2); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Blending background to a specific color * + *---------------------------------------------------------------------*/ +/*! + * \brief pixBlendBackgroundToColor() + * + * \param[in] pixd can be NULL or pixs + * \param[in] pixs 32 bpp rgb + * \param[in] box region for blending; can be NULL) + * \param[in] color 32 bit color in 0xrrggbb00 format + * \param[in] gamma, minval, maxval args for grayscale TRC mapping + * \return pixd always + * + *
+ * Notes:
+ *      (1) This in effect replaces light background pixels in pixs
+ *          by the input color.  It does it by alpha blending so that
+ *          there are no visible artifacts from hard cutoffs.
+ *      (2) If pixd == pixs, this is done in-place.
+ *      (3) If box == NULL, this is performed on all of pixs.
+ *      (4) The alpha component for blending is derived from pixs,
+ *          by converting to grayscale and enhancing with a TRC.
+ *      (5) The last three arguments specify the TRC operation.
+ *          Suggested values are: %gamma = 0.3, %minval = 50, %maxval = 200.
+ *          To skip the TRC, use %gamma == 1, %minval = 0, %maxval = 255.
+ *          See pixGammaTRC() for details.
+ * 
+ */ +PIX * +pixBlendBackgroundToColor(PIX *pixd, + PIX *pixs, + BOX *box, + l_uint32 color, + l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +l_int32 x, y, w, h; +BOX *boxt; +PIX *pixt, *pixc, *pixr, *pixg; + + PROCNAME("pixBlendBackgroundToColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd); + + /* Extract the (optionally cropped) region, pixr, and generate + * an identically sized pixc with the uniform color. */ + if (!pixd) + pixd = pixCopy(NULL, pixs); + if (box) { + pixr = pixClipRectangle(pixd, box, &boxt); + boxGetGeometry(boxt, &x, &y, &w, &h); + pixc = pixCreate(w, h, 32); + boxDestroy(&boxt); + } else { + pixc = pixCreateTemplate(pixs); + pixr = pixClone(pixd); + } + pixSetAllArbitrary(pixc, color); + + /* Set up the alpha channel */ + pixg = pixConvertTo8(pixr, 0); + pixGammaTRC(pixg, pixg, gamma, minval, maxval); + pixSetRGBComponent(pixc, pixg, L_ALPHA_CHANNEL); + + /* Blend and replace in pixd */ + pixt = pixBlendWithGrayMask(pixr, pixc, NULL, 0, 0); + if (box) { + pixRasterop(pixd, x, y, w, h, PIX_SRC, pixt, 0, 0); + pixDestroy(&pixt); + } else { + pixTransferAllData(pixd, &pixt, 0, 0); + } + + pixDestroy(&pixc); + pixDestroy(&pixr); + pixDestroy(&pixg); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Multiplying by a specific color * + *---------------------------------------------------------------------*/ +/*! + * \brief pixMultiplyByColor() + * + * \param[in] pixd can be NULL or pixs + * \param[in] pixs 32 bpp rgb + * \param[in] box region for filtering; can be NULL) + * \param[in] color 32 bit color in 0xrrggbb00 format + * \return pixd always + * + *
+ * Notes:
+ *      (1) This filters all pixels in the specified region by
+ *          multiplying each component by the input color.
+ *          This leaves black invariant and transforms white to the
+ *          input color.
+ *      (2) If pixd == pixs, this is done in-place.
+ *      (3) If box == NULL, this is performed on all of pixs.
+ * 
+ */ +PIX * +pixMultiplyByColor(PIX *pixd, + PIX *pixs, + BOX *box, + l_uint32 color) +{ +l_int32 i, j, bx, by, w, h, wpl; +l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval; +l_float32 frval, fgval, fbval; +l_uint32 *data, *line; +PIX *pixt; + + PROCNAME("pixMultiplyByColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + if (box) { + boxGetGeometry(box, &bx, &by, NULL, NULL); + pixt = pixClipRectangle(pixd, box, NULL); + } else { + pixt = pixClone(pixd); + } + + /* Multiply each pixel in pixt by the color */ + extractRGBValues(color, &red, &green, &blue); + frval = (1. / 255.) * red; + fgval = (1. / 255.) * green; + fbval = (1. / 255.) * blue; + data = pixGetData(pixt); + wpl = pixGetWpl(pixt); + pixGetDimensions(pixt, &w, &h, NULL); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + nrval = (l_int32)(frval * rval + 0.5); + ngval = (l_int32)(fgval * gval + 0.5); + nbval = (l_int32)(fbval * bval + 0.5); + composeRGBPixel(nrval, ngval, nbval, line + j); + } + } + + /* Replace */ + if (box) + pixRasterop(pixd, bx, by, w, h, PIX_SRC, pixt, 0, 0); + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Rendering with alpha blending over a uniform background * + *---------------------------------------------------------------------*/ +/*! + * \brief pixAlphaBlendUniform() + * + * \param[in] pixs 32 bpp rgba, with alpha + * \param[in] color 32 bit color in 0xrrggbb00 format + * \return pixd 32 bpp rgb: pixs blended over uniform color %color, + * a clone of pixs if no alpha, and null on error + * + *
+ * Notes:
+ *      (1) This is a convenience function that renders 32 bpp RGBA images
+ *          (with an alpha channel) over a uniform background of
+ *          value %color.  To render over a white background,
+ *          use %color = 0xffffff00.  The result is an RGB image.
+ *      (2) If pixs does not have an alpha channel, it returns a clone
+ *          of pixs.
+ * 
+ */ +PIX * +pixAlphaBlendUniform(PIX *pixs, + l_uint32 color) +{ +PIX *pixt, *pixd; + + PROCNAME("pixAlphaBlendUniform"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (pixGetSpp(pixs) != 4) { + L_WARNING("no alpha channel; returning clone\n", procName); + return pixClone(pixs); + } + + pixt = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixt, color); + pixSetSpp(pixt, 3); /* not required */ + pixd = pixBlendWithGrayMask(pixt, pixs, NULL, 0, 0); + + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Adding an alpha layer for blending * + *---------------------------------------------------------------------*/ +/*! + * \brief pixAddAlphaToBlend() + * + * \param[in] pixs any depth + * \param[in] fract fade fraction in the alpha component + * \param[in] invert 1 to photometrically invert pixs + * \return pixd 32 bpp with alpha, or null on error + * + *
+ * Notes:
+ *      (1) This is a simple alpha layer generator, where typically white has
+ *          maximum transparency and black has minimum.
+ *      (2) If %invert == 1, generate the same alpha layer but invert
+ *          the input image photometrically.  This is useful for blending
+ *          over dark images, where you want dark regions in pixs, such
+ *          as text, to be lighter in the blended image.
+ *      (3) The fade %fract gives the minimum transparency (i.e.,
+ *          maximum opacity).  A small fraction is useful for adding
+ *          a watermark to an image.
+ *      (4) If pixs has a colormap, it is removed to rgb.
+ *      (5) If pixs already has an alpha layer, it is overwritten.
+ * 
+ */ +PIX * +pixAddAlphaToBlend(PIX *pixs, + l_float32 fract, + l_int32 invert) +{ +PIX *pixd, *pix1, *pix2; + + PROCNAME("pixAddAlphaToBlend"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (fract < 0.0 || fract > 1.0) + return (PIX *)ERROR_PTR("invalid fract", procName, NULL); + + /* Convert to 32 bpp */ + if (pixGetColormap(pixs)) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pix1 = pixClone(pixs); + pixd = pixConvertTo32(pix1); /* new */ + + /* Use an inverted image if this will be blended with a dark image */ + if (invert) pixInvert(pixd, pixd); + + /* Generate alpha layer */ + pix2 = pixConvertTo8(pix1, 0); /* new */ + pixInvert(pix2, pix2); + pixMultConstantGray(pix2, fract); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + + +/*---------------------------------------------------------------------* + * Setting a transparent alpha component over a white background * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSetAlphaOverWhite() + * + * \param[in] pixs colormapped or 32 bpp rgb; no alpha + * \return pixd new pix with meaningful alpha component, + * or null on error + * + *
+ * Notes:
+ *      (1) The generated alpha component is transparent over white
+ *          (background) pixels in pixs, and quickly grades to opaque
+ *          away from the transparent parts.  This is a cheap and
+ *          dirty alpha generator.  The 2 pixel gradation is useful
+ *          to blur the boundary between the transparent region
+ *          (that will render entirely from a backing image) and
+ *          the remainder which renders from pixs.
+ *      (2) All alpha component bits in pixs are overwritten.
+ * 
+ */ +PIX * +pixSetAlphaOverWhite(PIX *pixs) +{ +PIX *pixd, *pix1, *pix2, *pix3, *pix4; + + PROCNAME("pixSetAlphaOverWhite"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!(pixGetDepth(pixs) == 32 || pixGetColormap(pixs))) + return (PIX *)ERROR_PTR("pixs not 32 bpp or cmapped", procName, NULL); + + /* Remove colormap if it exists; otherwise copy */ + pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_TO_FULL_COLOR, L_COPY); + + /* Generate a 1 bpp image where a white pixel in pixd is 0. + * In the comments below, a "white" pixel refers to pixd. + * pix1 is rgb, pix2 is 8 bpp gray, pix3 is 1 bpp. */ + pix1 = pixInvert(NULL, pixd); /* send white (255) to 0 for each sample */ + pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); /* 0 if white */ + pix3 = pixThresholdToBinary(pix2, 1); /* sets white pixels to 1 */ + pixInvert(pix3, pix3); /* sets white pixels to 0 */ + + /* Generate the alpha component using the distance transform, + * which measures the distance to the nearest bg (0) pixel in pix3. + * After multiplying by 128, its value is 0 (transparent) + * over white pixels, and goes to opaque (255) two pixels away + * from the nearest white pixel. */ + pix4 = pixDistanceFunction(pix3, 8, 8, L_BOUNDARY_FG); + pixMultConstantGray(pix4, 128.0); + pixSetRGBComponent(pixd, pix4, L_ALPHA_CHANNEL); + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Fading from the edge * + *---------------------------------------------------------------------*/ +/*! + * \brief pixLinearEdgeFade() + * + * \param[in] pixs 8 or 32 bpp; no colormap + * \param[in] dir L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT + * \param[in] fadeto L_BLEND_TO_WHITE, L_BLEND_TO_BLACK + * \param[in] distfract fraction of width or height over which fading occurs + * \param[in] maxfade fraction of fading at the edge, <= 1.0 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation.
+ *      (2) Maximum fading fraction %maxfade occurs at the edge of the image,
+ *          and the fraction goes to 0 at the fractional distance %distfract
+ *          from the edge.  %maxfade must be in [0, 1].
+ *      (3) %distrfact must be in [0, 1], and typically it would be <= 0.5.
+ * 
+ */ +l_ok +pixLinearEdgeFade(PIX *pixs, + l_int32 dir, + l_int32 fadeto, + l_float32 distfract, + l_float32 maxfade) +{ +l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval; +l_float32 slope, limit, del; +l_uint32 *data, *line; + + PROCNAME("pixLinearEdgeFade"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetColormap(pixs) != NULL) + return ERROR_INT("pixs has a colormap", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32) + return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); + if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && + dir != L_FROM_TOP && dir != L_FROM_BOT) + return ERROR_INT("invalid fade direction from edge", procName, 1); + if (fadeto != L_BLEND_TO_WHITE && fadeto != L_BLEND_TO_BLACK) + return ERROR_INT("invalid fadeto photometry", procName, 1); + if (maxfade <= 0) return 0; + if (maxfade > 1.0) + return ERROR_INT("invalid maxfade", procName, 1); + if (distfract <= 0 || distfract * L_MIN(w, h) < 1.0) { + L_INFO("distfract is too small\n", procName); + return 0; + } + if (distfract > 1.0) + return ERROR_INT("invalid distfract", procName, 1); + + /* Set up parameters */ + if (dir == L_FROM_LEFT) { + range = (l_int32)(distfract * w); + xmin = 0; + slope = maxfade / (l_float32)range; + } else if (dir == L_FROM_RIGHT) { + range = (l_int32)(distfract * w); + xmin = w - range; + slope = maxfade / (l_float32)range; + } else if (dir == L_FROM_TOP) { + range = (l_int32)(distfract * h); + ymin = 0; + slope = maxfade / (l_float32)range; + } else if (dir == L_FROM_BOT) { + range = (l_int32)(distfract * h); + ymin = h - range; + slope = maxfade / (l_float32)range; + } + + limit = (fadeto == L_BLEND_TO_WHITE) ? 255.0 : 0.0; + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { + for (j = 0; j < range; j++) { + del = (dir == L_FROM_LEFT) ? maxfade - slope * j + : maxfade - slope * (range - j); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 8) { + val = GET_DATA_BYTE(line, xmin + j); + val += (limit - val) * del + 0.5; + SET_DATA_BYTE(line, xmin + j, val); + } else { /* rgb */ + extractRGBValues(*(line + xmin + j), &rval, &gval, &bval); + rval += (limit - rval) * del + 0.5; + gval += (limit - gval) * del + 0.5; + bval += (limit - bval) * del + 0.5; + composeRGBPixel(rval, gval, bval, line + xmin + j); + } + } + } + } else { /* dir == L_FROM_TOP || L_FROM_BOT */ + for (i = 0; i < range; i++) { + del = (dir == L_FROM_TOP) ? maxfade - slope * i + : maxfade - slope * (range - i); + line = data + (ymin + i) * wpl; + for (j = 0; j < w; j++) { + if (d == 8) { + val = GET_DATA_BYTE(line, j); + val += (limit - val) * del + 0.5; + SET_DATA_BYTE(line, j, val); + } else { /* rgb */ + extractRGBValues(*(line + j), &rval, &gval, &bval); + rval += (limit - rval) * del + 0.5; + gval += (limit - gval) * del + 0.5; + bval += (limit - bval) * del + 0.5; + composeRGBPixel(rval, gval, bval, line + j); + } + } + } + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmf.c b/hgdriver/3rdparty/hgOCR/leptonica/bmf.c new file mode 100644 index 0000000..5c522dc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmf.c @@ -0,0 +1,874 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bmf.c + *
+ *
+ *   Acquisition and generation of bitmap fonts.
+ *
+ *       L_BMF           *bmfCreate()
+ *       L_BMF           *bmfDestroy()
+ *
+ *       PIX             *bmfGetPix()
+ *       l_int32          bmfGetWidth()
+ *       l_int32          bmfGetBaseline()
+ *
+ *       PIXA            *pixaGetFont()
+ *       l_int32          pixaSaveFont()
+ *       static PIXA     *pixaGenerateFontFromFile()
+ *       static PIXA     *pixaGenerateFontFromString()
+ *       static PIXA     *pixaGenerateFont()
+ *       static l_int32   pixGetTextBaseline()
+ *       static l_int32   bmfMakeAsciiTables()
+ *
+ *   This is not a very general utility, because it only uses bitmap
+ *   representations of a single font, Palatino-Roman, with the
+ *   normal style.  It uses bitmaps generated for nine sizes, from
+ *   4 to 20 pts, rendered at 300 ppi.  Generalization to different
+ *   fonts, styles and sizes is straightforward.
+ *
+ *   I chose Palatino-Roman is because I like it.
+ *   The input font images were generated from a set of small
+ *   PostScript files, such as chars-12.ps, which were rendered
+ *   into the inputfont[] bitmap files using GhostScript.  See, for
+ *   example, the bash script prog/ps2tiff, which will "rip" a
+ *   PostScript file into a set of ccitt-g4 compressed tiff files.
+ *
+ *   The set of ascii characters from 32 through 126 are the 95
+ *   printable ascii chars.  Palatino-Roman is missing char 92, '\'.
+ *   I have substituted an LR flip of '/', char 47, for 92, so that
+ *   there are no missing printable chars in this set.  The space is
+ *   char 32, and I have given it a width equal to twice the width of '!'.
+ * 
+ */ + +#include +#include "allheaders.h" +#include "bmfdata.h" + +static const l_float32 VertFractSep = 0.3; + +#ifndef NO_CONSOLE_IO +#define DEBUG_BASELINE 0 +#define DEBUG_CHARS 0 +#define DEBUG_FONT_GEN 0 +#endif /* ~NO_CONSOLE_IO */ + +static PIXA *pixaGenerateFontFromFile(const char *dir, l_int32 fontsize, + l_int32 *pbl0, l_int32 *pbl1, + l_int32 *pbl2); +static PIXA *pixaGenerateFontFromString(l_int32 fontsize, l_int32 *pbl0, + l_int32 *pbl1, l_int32 *pbl2); +static PIXA *pixaGenerateFont(PIX *pixs, l_int32 fontsize, l_int32 *pbl0, + l_int32 *pbl1, l_int32 *pbl2); +static l_int32 pixGetTextBaseline(PIX *pixs, l_int32 *tab8, l_int32 *py); +static l_int32 bmfMakeAsciiTables(L_BMF *bmf); + + +/*---------------------------------------------------------------------*/ +/* Bmf create/destroy */ +/*---------------------------------------------------------------------*/ +/*! + * \brief bmfCreate() + * + * \param[in] dir [optional] directory holding pixa of character set + * \param[in] fontsize 4, 6, 8, ... , 20 + * \return bmf holding the bitmap font and associated information + * + *
+ * Notes:
+ *      (1) If %dir == null, this generates the font bitmaps from a
+ *          compiled string.
+ *      (2) Otherwise, this tries to read a pre-computed pixa file with the
+ *          95 ascii chars in it.  If the file is not found, it then
+ *          attempts to generate the pixa and associated baseline
+ *          data from a tiff image containing all the characters.  If
+ *          that fails, it uses the compiled string.
+ * 
+ */ +L_BMF * +bmfCreate(const char *dir, + l_int32 fontsize) +{ +L_BMF *bmf; +PIXA *pixa; + + PROCNAME("bmfCreate"); + + if (fontsize < 4 || fontsize > 20 || (fontsize % 2)) + return (L_BMF *)ERROR_PTR("fontsize must be in {4, 6, ..., 20}", + procName, NULL); + + bmf = (L_BMF *)LEPT_CALLOC(1, sizeof(L_BMF)); + + if (!dir) { /* Generate from a string */ + pixa = pixaGenerateFontFromString(fontsize, &bmf->baseline1, + &bmf->baseline2, &bmf->baseline3); + } else { /* Look for the pixa in a directory */ + pixa = pixaGetFont(dir, fontsize, &bmf->baseline1, &bmf->baseline2, + &bmf->baseline3); + if (!pixa) { /* Not found; make it from a file */ + L_INFO("Generating pixa of bitmap fonts from file\n", procName); + pixa = pixaGenerateFontFromFile(dir, fontsize, &bmf->baseline1, + &bmf->baseline2, &bmf->baseline3); + if (!pixa) { /* Not made; make it from a string after all */ + L_ERROR("Failed to make font; use string\n", procName); + pixa = pixaGenerateFontFromString(fontsize, &bmf->baseline1, + &bmf->baseline2, &bmf->baseline3); + } + } + } + + if (!pixa) { + bmfDestroy(&bmf); + return (L_BMF *)ERROR_PTR("font pixa not made", procName, NULL); + } + + bmf->pixa = pixa; + bmf->size = fontsize; + if (dir) bmf->directory = stringNew(dir); + bmfMakeAsciiTables(bmf); + return bmf; +} + + +/*! + * \brief bmfDestroy() + * + * \param[in,out] pbmf will be set to null before returning + * \return void + */ +void +bmfDestroy(L_BMF **pbmf) +{ +L_BMF *bmf; + + PROCNAME("bmfDestroy"); + + if (pbmf == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((bmf = *pbmf) == NULL) + return; + + pixaDestroy(&bmf->pixa); + LEPT_FREE(bmf->directory); + LEPT_FREE(bmf->fonttab); + LEPT_FREE(bmf->baselinetab); + LEPT_FREE(bmf->widthtab); + LEPT_FREE(bmf); + *pbmf = NULL; + return; +} + + +/*---------------------------------------------------------------------*/ +/* Bmf accessors */ +/*---------------------------------------------------------------------*/ +/*! + * \brief bmfGetPix() + * + * \param[in] bmf + * \param[in] chr should be one of the 95 supported printable bitmaps + * \return pix clone of pix in bmf, or NULL on error + */ +PIX * +bmfGetPix(L_BMF *bmf, + char chr) +{ +l_int32 i, index; +PIXA *pixa; + + PROCNAME("bmfGetPix"); + + if ((index = (l_int32)chr) == 10) /* NL */ + return NULL; + if (!bmf) + return (PIX *)ERROR_PTR("bmf not defined", procName, NULL); + + i = bmf->fonttab[index]; + if (i == UNDEF) { + L_ERROR("no bitmap representation for %d\n", procName, index); + return NULL; + } + + if ((pixa = bmf->pixa) == NULL) + return (PIX *)ERROR_PTR("pixa not found", procName, NULL); + + return pixaGetPix(pixa, i, L_CLONE); +} + + +/*! + * \brief bmfGetWidth() + * + * \param[in] bmf + * \param[in] chr should be one of the 95 supported bitmaps + * \param[out] pw character width; -1 if not printable + * \return 0 if OK, 1 on error + */ +l_ok +bmfGetWidth(L_BMF *bmf, + char chr, + l_int32 *pw) +{ +l_int32 i, index; +PIXA *pixa; + + PROCNAME("bmfGetWidth"); + + if (!pw) + return ERROR_INT("&w not defined", procName, 1); + *pw = -1; + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + if ((index = (l_int32)chr) == 10) /* NL */ + return 0; + + i = bmf->fonttab[index]; + if (i == UNDEF) { + L_ERROR("no bitmap representation for %d\n", procName, index); + return 1; + } + + if ((pixa = bmf->pixa) == NULL) + return ERROR_INT("pixa not found", procName, 1); + + return pixaGetPixDimensions(pixa, i, pw, NULL, NULL); +} + + +/*! + * \brief bmfGetBaseline() + * + * \param[in] bmf + * \param[in] chr should be one of the 95 supported bitmaps + * \param[out] pbaseline distance below UL corner of bitmap char + * \return 0 if OK, 1 on error + */ +l_ok +bmfGetBaseline(L_BMF *bmf, + char chr, + l_int32 *pbaseline) +{ +l_int32 bl, index; + + PROCNAME("bmfGetBaseline"); + + if (!pbaseline) + return ERROR_INT("&baseline not defined", procName, 1); + *pbaseline = 0; + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + if ((index = (l_int32)chr) == 10) /* NL */ + return 0; + + bl = bmf->baselinetab[index]; + if (bl == UNDEF) { + L_ERROR("no bitmap representation for %d\n", procName, index); + return 1; + } + + *pbaseline = bl; + return 0; +} + + +/*---------------------------------------------------------------------*/ +/* Font bitmap acquisition and generation */ +/*---------------------------------------------------------------------*/ +/*! + * \brief pixaGetFont() + * + * \param[in] dir directory holding pixa of character set + * \param[in] fontsize 4, 6, 8, ... , 20 + * \param[out] pbl0 baseline of row 1 + * \param[out] pbl1 baseline of row 2 + * \param[out] pbl2 baseline of row 3 + * \return pixa of font bitmaps for 95 characters, or NULL on error + * + *
+ * Notes:
+ *      (1) This reads a pre-computed pixa file with the 95 ascii chars.
+ * 
+ */ +PIXA * +pixaGetFont(const char *dir, + l_int32 fontsize, + l_int32 *pbl0, + l_int32 *pbl1, + l_int32 *pbl2) +{ +char *pathname; +l_int32 fileno; +PIXA *pixa; + + PROCNAME("pixaGetFont"); + + fileno = (fontsize / 2) - 2; + if (fileno < 0 || fileno >= NUM_FONTS) + return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); + if (!pbl0 || !pbl1 || !pbl2) + return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); + *pbl0 = baselines[fileno][0]; + *pbl1 = baselines[fileno][1]; + *pbl2 = baselines[fileno][2]; + + pathname = pathJoin(dir, outputfonts[fileno]); + pixa = pixaRead(pathname); + LEPT_FREE(pathname); + + if (!pixa) + L_WARNING("pixa of char bitmaps not found\n", procName); + return pixa; +} + + +/*! + * \brief pixaSaveFont() + * + * \param[in] indir [optional] directory holding image of character set + * \param[in] outdir directory into which the output pixa file + * will be written + * \param[in] fontsize in pts, at 300 ppi + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This saves a font of a particular size.
+ *      (2) If %indir == null, this generates the font bitmaps from a
+ *          compiled string.
+ *      (3) prog/genfonts calls this function for each of the
+ *          nine font sizes, to generate all the font pixa files.
+ * 
+ */ +l_ok +pixaSaveFont(const char *indir, + const char *outdir, + l_int32 fontsize) +{ +char *pathname; +l_int32 bl1, bl2, bl3; +PIXA *pixa; + + PROCNAME("pixaSaveFont"); + + if (fontsize < 4 || fontsize > 20 || (fontsize % 2)) + return ERROR_INT("fontsize must be in {4, 6, ..., 20}", procName, 1); + + if (!indir) /* Generate from a string */ + pixa = pixaGenerateFontFromString(fontsize, &bl1, &bl2, &bl3); + else /* Generate from an image file */ + pixa = pixaGenerateFontFromFile(indir, fontsize, &bl1, &bl2, &bl3); + if (!pixa) + return ERROR_INT("pixa not made", procName, 1); + + pathname = pathJoin(outdir, outputfonts[(fontsize - 4) / 2]); + pixaWrite(pathname, pixa); + +#if DEBUG_FONT_GEN + L_INFO("Found %d chars in font size %d\n", procName, pixaGetCount(pixa), + fontsize); + L_INFO("Baselines are at: %d, %d, %d\n", procName, bl1, bl2, bl3); +#endif /* DEBUG_FONT_GEN */ + + LEPT_FREE(pathname); + pixaDestroy(&pixa); + return 0; +} + + +/*! + * \brief pixaGenerateFontFromFile() + * + * \param[in] dir directory holding image of character set + * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi + * \param[out] pbl0 baseline of row 1 + * \param[out] pbl1 baseline of row 2 + * \param[out] pbl2 baseline of row 3 + * \return pixa of font bitmaps for 95 characters, or NULL on error + * + * These font generation functions use 9 sets, each with bitmaps + * of 94 ascii characters, all in Palatino-Roman font. + * Each input bitmap has 3 rows of characters. The range of + * ascii values in each row is as follows: + * row 0: 32-57 32 is a space + * row 1: 58-91 92, '\', is not represented in this font + * row 2: 93-126 + * We LR flip the '/' char to generate a bitmap for the missing + * '\' character, so that we have representations of all 95 + * printable chars. + * + * Typically, use pixaGetFont() to generate the character bitmaps + * in memory for a bmf. This will simply access the bitmap files + * in a serialized pixa that were produced in prog/genfonts.c using + * this function. + */ +static PIXA * +pixaGenerateFontFromFile(const char *dir, + l_int32 fontsize, + l_int32 *pbl0, + l_int32 *pbl1, + l_int32 *pbl2) +{ +char *pathname; +l_int32 fileno; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixaGenerateFontFromFile"); + + if (!pbl0 || !pbl1 || !pbl2) + return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); + *pbl0 = *pbl1 = *pbl2 = 0; + if (!dir) + return (PIXA *)ERROR_PTR("dir not defined", procName, NULL); + fileno = (fontsize / 2) - 2; + if (fileno < 0 || fileno >= NUM_FONTS) + return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); + + pathname = pathJoin(dir, inputfonts[fileno]); + pix = pixRead(pathname); + LEPT_FREE(pathname); + if (!pix) { + L_ERROR("pix not found for font size %d\n", procName, fontsize); + return NULL; + } + + pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); + pixDestroy(&pix); + return pixa; +} + + +/*! + * \brief pixaGenerateFontFromString() + * + * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi + * \param[out] pbl0 baseline of row 1 + * \param[out] pbl1 baseline of row 2 + * \param[out] pbl2 baseline of row 3 + * \return pixa of font bitmaps for 95 characters, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixaGenerateFontFromFile() for details.
+ * 
+ */ +static PIXA * +pixaGenerateFontFromString(l_int32 fontsize, + l_int32 *pbl0, + l_int32 *pbl1, + l_int32 *pbl2) +{ +l_uint8 *data; +l_int32 redsize, nbytes; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixaGenerateFontFromString"); + + if (!pbl0 || !pbl1 || !pbl2) + return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); + *pbl0 = *pbl1 = *pbl2 = 0; + redsize = (fontsize / 2) - 2; + if (redsize < 0 || redsize >= NUM_FONTS) + return (PIXA *)ERROR_PTR("invalid font size", procName, NULL); + + if (fontsize == 4) { + data = decodeBase64(fontdata_4, strlen(fontdata_4), &nbytes); + } else if (fontsize == 6) { + data = decodeBase64(fontdata_6, strlen(fontdata_6), &nbytes); + } else if (fontsize == 8) { + data = decodeBase64(fontdata_8, strlen(fontdata_8), &nbytes); + } else if (fontsize == 10) { + data = decodeBase64(fontdata_10, strlen(fontdata_10), &nbytes); + } else if (fontsize == 12) { + data = decodeBase64(fontdata_12, strlen(fontdata_12), &nbytes); + } else if (fontsize == 14) { + data = decodeBase64(fontdata_14, strlen(fontdata_14), &nbytes); + } else if (fontsize == 16) { + data = decodeBase64(fontdata_16, strlen(fontdata_16), &nbytes); + } else if (fontsize == 18) { + data = decodeBase64(fontdata_18, strlen(fontdata_18), &nbytes); + } else { /* fontsize == 20 */ + data = decodeBase64(fontdata_20, strlen(fontdata_20), &nbytes); + } + if (!data) + return (PIXA *)ERROR_PTR("data not made", procName, NULL); + + pix = pixReadMem(data, nbytes); + LEPT_FREE(data); + if (!pix) + return (PIXA *)ERROR_PTR("pix not made", procName, NULL); + + pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); + pixDestroy(&pix); + return pixa; +} + + +/*! + * \brief pixaGenerateFont() + * + * \param[in] pixs of 95 characters in 3 rows + * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi + * \param[out] pbl0 baseline of row 1 + * \param[out] pbl1 baseline of row 2 + * \param[out] pbl2 baseline of row 3 + * \return pixa of font bitmaps for 95 characters, or NULL on error + * + *
+ * Notes:
+ *      (1) This does all the work.  See pixaGenerateFontFromFile()
+ *          for an overview.
+ *      (2) The pix is for one of the 9 fonts.  %fontsize is only
+ *          used here for debugging.
+ * 
+ */ +static PIXA * +pixaGenerateFont(PIX *pixs, + l_int32 fontsize, + l_int32 *pbl0, + l_int32 *pbl1, + l_int32 *pbl2) +{ +l_int32 i, j, nrows, nrowchars, nchars, h, yval; +l_int32 width, height; +l_int32 baseline[3]; +l_int32 *tab = NULL; +BOX *box, *box1, *box2; +BOXA *boxar, *boxac, *boxacs; +PIX *pix1, *pix2, *pixr, *pixrc, *pixc; +PIXA *pixa; +l_int32 n, w, inrow, top; +l_int32 *ia; +NUMA *na; + + PROCNAME("pixaGenerateFont"); + + if (!pbl0 || !pbl1 || !pbl2) + return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); + *pbl0 = *pbl1 = *pbl2 = 0; + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Locate the 3 rows of characters */ + w = pixGetWidth(pixs); + na = pixCountPixelsByRow(pixs, NULL); + boxar = boxaCreate(0); + n = numaGetCount(na); + ia = numaGetIArray(na); + inrow = 0; + for (i = 0; i < n; i++) { + if (!inrow && ia[i] > 0) { + inrow = 1; + top = i; + } else if (inrow && ia[i] == 0) { + inrow = 0; + box = boxCreate(0, top, w, i - top); + boxaAddBox(boxar, box, L_INSERT); + } + } + LEPT_FREE(ia); + numaDestroy(&na); + nrows = boxaGetCount(boxar); +#if DEBUG_FONT_GEN + L_INFO("For fontsize %s, have %d rows\n", procName, fontsize, nrows); +#endif /* DEBUG_FONT_GEN */ + if (nrows != 3) { + L_INFO("nrows = %d; skipping fontsize %d\n", procName, nrows, fontsize); + boxaDestroy(&boxar); + return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL); + } + + /* Grab the character images and baseline data */ +#if DEBUG_BASELINE + lept_rmdir("baseline"); + lept_mkdir("baseline"); +#endif /* DEBUG_BASELINE */ + tab = makePixelSumTab8(); + pixa = pixaCreate(95); + for (i = 0; i < nrows; i++) { + box = boxaGetBox(boxar, i, L_CLONE); + pixr = pixClipRectangle(pixs, box, NULL); /* row of chars */ + pixGetTextBaseline(pixr, tab, &yval); + baseline[i] = yval; + +#if DEBUG_BASELINE + L_INFO("Baseline info: row %d, yval = %d, h = %d\n", procName, + i, yval, pixGetHeight(pixr)); + pix1 = pixCopy(NULL, pixr); + pixRenderLine(pix1, 0, yval, pixGetWidth(pix1), yval, 1, + L_FLIP_PIXELS); + if (i == 0 ) + pixWriteDebug("/tmp/baseline/row0.png", pix1, IFF_PNG); + else if (i == 1) + pixWriteDebug("/tmp/baseline/row1.png", pix1, IFF_PNG); + else + pixWriteDebug("/tmp/baseline/row2.png", pix1, IFF_PNG); + pixDestroy(&pix1); +#endif /* DEBUG_BASELINE */ + + boxDestroy(&box); + pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35); + boxac = pixConnComp(pixrc, NULL, 8); + boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL); + if (i == 0) { /* consolidate the two components of '"' */ + box1 = boxaGetBox(boxacs, 1, L_CLONE); + box2 = boxaGetBox(boxacs, 2, L_CLONE); + box1->w = box2->x + box2->w - box1->x; /* increase width */ + boxDestroy(&box1); + boxDestroy(&box2); + boxaRemoveBox(boxacs, 2); + } + h = pixGetHeight(pixr); + nrowchars = boxaGetCount(boxacs); + for (j = 0; j < nrowchars; j++) { + box = boxaGetBox(boxacs, j, L_COPY); + if (box->w <= 2 && box->h == 1) { /* skip 1x1, 2x1 components */ + boxDestroy(&box); + continue; + } + box->y = 0; + box->h = h - 1; + pixc = pixClipRectangle(pixr, box, NULL); + boxDestroy(&box); + if (i == 0 && j == 0) /* add a pix for the space; change later */ + pixaAddPix(pixa, pixc, L_COPY); + if (i == 2 && j == 0) /* add a pix for the '\'; change later */ + pixaAddPix(pixa, pixc, L_COPY); + pixaAddPix(pixa, pixc, L_INSERT); + } + pixDestroy(&pixr); + pixDestroy(&pixrc); + boxaDestroy(&boxac); + boxaDestroy(&boxacs); + } + LEPT_FREE(tab); + + nchars = pixaGetCount(pixa); + if (nchars != 95) + return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL); + + *pbl0 = baseline[0]; + *pbl1 = baseline[1]; + *pbl2 = baseline[2]; + + /* Fix the space character up; it should have no ON pixels, + * and be about twice as wide as the '!' character. */ + pix1 = pixaGetPix(pixa, 0, L_CLONE); + width = 2 * pixGetWidth(pix1); + height = pixGetHeight(pix1); + pixDestroy(&pix1); + pix1 = pixCreate(width, height, 1); + pixaReplacePix(pixa, 0, pix1, NULL); + + /* Fix up the '\' character; use a LR flip of the '/' char */ + pix1 = pixaGetPix(pixa, 15, L_CLONE); + pix2 = pixFlipLR(NULL, pix1); + pixDestroy(&pix1); + pixaReplacePix(pixa, 60, pix2, NULL); + +#if DEBUG_CHARS + pix1 = pixaDisplayTiled(pixa, 1500, 0, 10); + pixDisplay(pix1, 100 * i, 200); + pixDestroy(&pix1); +#endif /* DEBUG_CHARS */ + + boxaDestroy(&boxar); + return pixa; +} + + +/*! + * \brief pixGetTextBaseline() + * + * \param[in] pixs 1 bpp, one textline character set + * \param[in] tab8 [optional] pixel sum table + * \param[out] py baseline value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Method: find the largest difference in pixel sums from one
+ *          raster line to the next one below it.  The baseline is the
+ *          upper raster line for the pair of raster lines that
+ *          maximizes this function.
+ * 
+ */ +static l_int32 +pixGetTextBaseline(PIX *pixs, + l_int32 *tab8, + l_int32 *py) +{ +l_int32 i, h, val1, val2, diff, diffmax, ymax; +l_int32 *tab; +NUMA *na; + + PROCNAME("pixGetTextBaseline"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!py) + return ERROR_INT("&y not defined", procName, 1); + *py = 0; + if (!tab8) + tab = makePixelSumTab8(); + else + tab = tab8; + + na = pixCountPixelsByRow(pixs, tab); + h = numaGetCount(na); + diffmax = 0; + ymax = 0; + for (i = 1; i < h; i++) { + numaGetIValue(na, i - 1, &val1); + numaGetIValue(na, i, &val2); + diff = L_MAX(0, val1 - val2); + if (diff > diffmax) { + diffmax = diff; + ymax = i - 1; /* upper raster line */ + } + } + *py = ymax; + + if (!tab8) + LEPT_FREE(tab); + numaDestroy(&na); + return 0; +} + + +/*! + * \brief bmfMakeAsciiTables + * + * \param[in] bmf + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This makes three tables, each of size 128, as follows:
+ *          ~ fonttab is a table containing the index of the Pix
+ *            that corresponds to each input ascii character;
+ *            it maps (ascii-index) --> Pixa index
+ *          ~ baselinetab is a table containing the baseline offset
+ *            for the Pix that corresponds to each input ascii character;
+ *            it maps (ascii-index) --> baseline offset
+ *          ~ widthtab is a table containing the character width in
+ *            pixels for the Pix that corresponds to that character;
+ *            it maps (ascii-index) --> bitmap width
+ *     (2) This also computes
+ *          ~ lineheight (sum of maximum character extensions above and
+ *                        below the baseline)
+ *          ~ kernwidth (spacing between characters within a word)
+ *          ~ spacewidth (space between words)
+ *          ~ vertlinesep (extra vertical spacing between textlines)
+ *     (3) The baselines apply as follows:
+ *          baseline1   (ascii 32 - 57), ascii 92
+ *          baseline2   (ascii 58 - 91)
+ *          baseline3   (ascii 93 - 126)
+ *     (4) The only array in bmf that is not ascii-based is the
+ *         array of bitmaps in the pixa, which starts at ascii 32.
+ * 
+ */ +static l_int32 +bmfMakeAsciiTables(L_BMF *bmf) +{ +l_int32 i, maxh, height, charwidth, xwidth, kernwidth; +l_int32 *fonttab, *baselinetab, *widthtab; +PIX *pix; + + PROCNAME("bmfMakeAsciiTables"); + + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + + /* First get the fonttab; we use this later for the char widths */ + fonttab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); + bmf->fonttab = fonttab; + for (i = 0; i < 128; i++) + fonttab[i] = UNDEF; + for (i = 32; i < 127; i++) + fonttab[i] = i - 32; + + baselinetab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); + bmf->baselinetab = baselinetab; + for (i = 0; i < 128; i++) + baselinetab[i] = UNDEF; + for (i = 32; i <= 57; i++) + baselinetab[i] = bmf->baseline1; + for (i = 58; i <= 91; i++) + baselinetab[i] = bmf->baseline2; + baselinetab[92] = bmf->baseline1; /* the '\' char */ + for (i = 93; i < 127; i++) + baselinetab[i] = bmf->baseline3; + + /* Generate array of character widths; req's fonttab to exist */ + widthtab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); + bmf->widthtab = widthtab; + for (i = 0; i < 128; i++) + widthtab[i] = UNDEF; + for (i = 32; i < 127; i++) { + bmfGetWidth(bmf, i, &charwidth); + widthtab[i] = charwidth; + } + + /* Get the line height of text characters, from the highest + * ascender to the lowest descender; req's fonttab to exist. */ + pix = bmfGetPix(bmf, 32); + maxh = pixGetHeight(pix); + pixDestroy(&pix); + pix = bmfGetPix(bmf, 58); + height = pixGetHeight(pix); + pixDestroy(&pix); + maxh = L_MAX(maxh, height); + pix = bmfGetPix(bmf, 93); + height = pixGetHeight(pix); + pixDestroy(&pix); + maxh = L_MAX(maxh, height); + bmf->lineheight = maxh; + + /* Get the kern width (distance between characters). + * We let it be the same for all characters in a given + * font size, and scale it linearly with the size; + * req's fonttab to be built first. */ + bmfGetWidth(bmf, 120, &xwidth); + kernwidth = (l_int32)(0.08 * (l_float32)xwidth + 0.5); + bmf->kernwidth = L_MAX(1, kernwidth); + + /* Save the space width (between words) */ + bmfGetWidth(bmf, 32, &charwidth); + bmf->spacewidth = charwidth; + + /* Save the extra vertical space between lines */ + bmf->vertlinesep = (l_int32)(VertFractSep * bmf->lineheight + 0.5); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmf.h b/hgdriver/3rdparty/hgOCR/leptonica/bmf.h new file mode 100644 index 0000000..328e2c0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmf.h @@ -0,0 +1,64 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_BMF_H +#define LEPTONICA_BMF_H + +/*! + * \file bmf.h + * + * Simple data structure to hold bitmap fonts and related data + */ + + /*! Constants for deciding when text block is divided into paragraphs */ +/*! Split Text */ +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 */ +}; + + +/*! Data structure to hold bitmap fonts and related data */ +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 */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmfdata.h b/hgdriver/3rdparty/hgOCR/leptonica/bmfdata.h new file mode 100644 index 0000000..30e2b5a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmfdata.h @@ -0,0 +1,636 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bmfdata.h + * + *
+ *  This file contains data for constructing the bitmap fonts.
+ *
+ *  The fontdata string holds all 9 sets of bitmap fonts in a base64
+ *  encoding of a pixacomp representation of the tiff compressed images.
+ *  It was generated by prog/genfonts and pasted in.  This allows
+ *  the use of the bitmap fonts for image labelling without accessing
+ *  stored versions of either the tiff images for each set, or the pixa
+ *  of the 95 printable character images that was derived from the tiff image.
+ *
+ *  In use, to get the bmf for a specific font size, from the encoded
+ *  string in this file, call
+ *      bmfCreate(NULL, fontsize);
+ * 
+ */ + +#ifndef LEPTONICA_BMFDATA_H +#define LEPTONICA_BMFDATA_H + +#define NUM_FONTS 9 +static const char *inputfonts[] = {"chars-4.tif", "chars-6.tif", + "chars-8.tif", "chars-10.tif", + "chars-12.tif", "chars-14.tif", + "chars-16.tif", "chars-18.tif", + "chars-20.tif"}; +static const char *outputfonts[] = {"chars-4.pa", "chars-6.pa", + "chars-8.pa", "chars-10.pa", + "chars-12.pa", "chars-14.pa", + "chars-16.pa", "chars-18.pa", + "chars-20.pa"}; +static const l_int32 baselines[NUM_FONTS][3] = {{11, 12, 12}, {18, 18, 18}, + {24, 24, 24}, {30, 30, 30}, + {36, 36, 36}, {42, 42, 42}, + {48, 48, 48}, {54, 54, 54}, + {60, 60, 60}}; + +static const char fontdata_4[] = + "SUkqACYFAAAmoHICP///////////////////////kFcchgc45Bgc45AgcgxBY5DY5DY5Agcg" + "jkM45A8GocgxBA8M45BfCGgchhzOQxZBiNe/CDQRT6RQ+k4QV6BHcgvBBjCC+KoSjQI7wjj/" + "16I+EUPTpV0rI4LilVtAjjyPuR58jg3CRd6dJkcDMCj+v//qlVsMgQPVY6vugih9Lr/8RCF+" + "OqUUK6C/fHFV9RStf8MulG10fKcN6X+lXOBg+GexX71wxSPCf4/+kE0uR5zE0rtfCFg3oIp0" + "R+GF5DSmQaMS/oG1xen0X2wyh8WXwoI46VPt/kNYcf9J4h/pUHB///2H+t+lkCByDj/r9ZBX" + "H1BAtUr7u/IEOQanrS0eByO16tpVaSWtaEVsNiG66WrBgg05wM4bCYNWDCWIiDCER6HGhERE" + "RER3ZHBfXjaSQ7iOP/////////////////////////////////////////////////////+Q" + "JgK95DIDRZAjCDccgRMhn4g5yC9CD0IL+QxhuIfCCYQTC4IJhBiyLBB7J4QX4gvQgxxBehBi" + "yGDkPhdkEw1kPZY5cEHck5BIJOQc9aI+wjE7DL7RdsMu2GXoZehGDYaDCDQaDSCDQdIOGEEX" + "bDLzCLthl5ojzkeL0NMJhNNbVoJ6kclXuggyOGfugnw3vugv/0u+9IN7pBvdJ//brT3VtdLy" + "B4NxyGsOPRnv9R7xx3/9L+EU/3/f4jj/t+3TdDvkFZyC7hYdKkCCKHQI76SW/pD/6XCKdAin" + "29L9L6/9eEUOrD0kv8IIMNKkq/j/zD5h+P4r//99LfBKcDR9utK62NLxEIIhnmGGlpek3Lz/" + "jj5cv/ul7f+EvimH///0l6CENpfrHt/y9l7kr/4RT/f7f+PwRTkG7/tpav26XtrxoVI5/vSx" + "xsP/7ful7fdd1tv/7FRoj//DLgQZgQCFhlYlfv1kx9//28mPx/7ruu3/t9K3pEh/IKzkF3DL" + "g2BENDtBr9Jh4S12H/+3+17GwwltpbZBx0u0unr0v9IMjhrBYYpO0KZmDikMJsYTCDCeE2Gh" + "p6DTdiEE2KCdo8GcNj3pJsJofjiIiIiIiIiI4iIiIiIhhCIiIiIiIr1SMwyQbOkEiGQCvd4i" + "I//////////////////////////////////////////////////////+QVo7IEDkGwchpOQV" + "nIa0ENKCGhyC7kHchocgZschnHIMPtKk7oIP7ulv6f9Yj5DIDaH/3gjjr///+rI4aiIEXngg" + "RZBfCBEWQXsofKggu5DD5Y+Qw5UHghiCoIEYQw5VkCMIO5TkF7shhzOQxZ4IJZxy3IO5nIJZ" + "4IP//1iiPOGd0R+iPQgR3TQIIXZ3/S7BBnezui87MOiPbKHRHqftNNXvTTUjy/9JkcFjTpOk" + "9NsKmFTu+Etppw06VtMjhhO0OLCd3S+rSdIUvyDD+Iha8fQ//+K//3/+D/vbQRT7d9LsjhgI" + "7nH8Ivf/lw0bS/4RT////7f//pfq+lhr6/v/Yf/t//3/+D/sO2NNhpfiP66Xat8L/2//3S0r" + "XIMD/rvUEd9Isf/4Mp5wCDgYBlOzgO0fB3aem2mmnYTtipwCAZQ6DnAXDgynapwk20h/+IiI" + "iIy9ERxEREREZHDLiIiIiIjjj6kNWdP//qP/pMjhq8bSXwojsGkEwmliIiP/////////////" + "/////////////////////////wAQAQ4AAAEDAAEAAACSAwAAAQEDAAEAAAA2AgAAAgEDAAEA" + "AAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAA" + "FQEDAAEAAAABAAAAFgEDAAEAAAA2AgAAFwEEAAEAAAAeBQAAGgEFAAEAAADUBQAAGwEFAAEA" + "AADcBQAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; + +static const char fontdata_6[] = + "SUkqAMoGAAAmoHVf///////////////////////////////IZAUfsgeBdyGdyDjkMgI+QPKC" + "GIO5AhzOgyGiCMcgYtUrIKHohowhschs4hnwgXcgRQhsgguQQXwhov6/QYQI7qgRUUk2QIfV" + "F5hQmmugqCMTCBHj/9F8j9JuknWm7rSbCBFPLtou2sjhlBSOKkE3Qf3+kv9fpcMQaXY9PTwR" + "T6WvpX/0v19aVbeQ0D6X7+v/X//QIQfj6xSS4QLS3xx69IVtL/EQy8CvbSqhq4I7//pJeVnT" + "Dr/+Niloufj9fpJLxalYrDtdr2DGk/etf6CDrkduzQkw21/w2prRfYZcNbj1+kQMQuL03hF5" + "sQRT+CEMMj7pAjuk/5DVDINfr+k9b06Stj+GXgW6pN9/kNsdL/XQg/+nSx/0v20vxSv0v/S3" + "/yDA/19sV/6WkQ0D5DY/6+lkDyf/SX9h65BRBDTdJ/StLILuk2lWkl399U2kw0Thpa0r7S0U" + "A7S20rSVtJL/iGrFMSPJv+qYoEaA+KBA4pikmKCWIiDVCINaQ0KiIiIiIoFhoRfSodbS1xbp" + "Id0hx8f///////////////////////////////////////////////////IHMFnMgTA0hyGQ" + "G45DLcg0jkQfyGQDNxBv5DLcg3QQ2EEHDIEaEHDIaDkMTJzIeZBJkEmTwh5kNmEPhB7ITCGi" + "ZDOghsmQ0IIbJhHUEMzPAh8jYOeIuRsEZFHCZEHBDhdoww1DLm0bOGXGwZccGXHCMDgwQMED" + "BAwQMEi4ZwQdAg2GEEbYYZc2EbYYZcwwjB5dmDgwQMIMJoNbQNqHuRxF6I7YQIN+6BBrDf+E" + "E//pf3oEG9tAg3vC9//126bQWlXh0gyODd+l7fXwv/0u1gio0m90m916x9uu60nXXyB4G7kN" + "tx6JwU9oEU/4944qP/pcEU8EU+37f7f4j/q6q2tpDXhYaShBBDer1XfJD5IdL/0vtf9L9L//" + "ergin9JukvIHk5BiAggw+kn1fSr///9L3r2/fS30of9r1exWqXp4QQYaWl9XH/a2vH+l9/t/" + "6X58mgN//r07dJe04QRDYGGGgvpVeXb/jj5gT8X7r7f+CX6CDD/bp6bXY/xEIIQw16Xq8N/y" + "5ZcvT/Lp/de3/j+2QMd/r/p0l6CDdf0h73//ZF7/w37r99/fuD/vVq9SP3S9hpd+lLj/6444" + "a/9v7r39L0tt/7Xq9b0vDDIbAwQQu2ElKHq/fr3f/2/dfb39/b/V6jjSb1Io/hhiEFbEECFK" + "r/euRR+//28ivxXt913XZBcf/jaevr8geTkCHDDCCIF3bEk9XpN6X7f/7f7+xtpbaW+l2l9K" + "3pfpqGGEErBhJfCTBk4wl+wf/7f9fsMJba7cMJbDSa9JvSX2sPCwxCQYQaFBikIQQwQMMYIG" + "CBggeCBsNCgg3CBhBuGKBA2KBA24hAgbFdOlYIGh+NCIiIiIiIiI4iIiIhxEGCERERERER9L" + "GHfVBF0Tgtg0dSBoDTYk+h40PiP/////////////////////////////////////////////" + "//////5A887IHkOQbLIE8EFaCGvBBmsgosgaDcg3HIbHwaIbIvVVIZTkGHVUtv9IOHRHBU+D" + "g5DJBx//QRTr69fr/+3X+I+v/pa//v/9N0Q2XnshsshsjIaMyGjMhlOQIHycZAhyDUOQy+IZ" + "xzWQUWUOQYc7kGMyGdyTkH41kH4scnZB4JwQxhrIYp/64hF56DCLzBF4aLzQNF8+DyuCguuF" + "Kw/ApXIvMFTCI7FhU0XmgYUL/ap0tow3/6TdN2XCTpB0rVJqJHmHD6BYbNhoDEjzSbDDLhJo" + "NnHSdQ4cMJoMJQ0DpBphVC//x9v/ScMEkwqf9Lpp6dJum18cQwX3V9XXWv/pN9OkKX/9f6X1" + "1/TpdX+6umrDdRSS2yBGFv4iQZu/9D//4r//f/58CP3XI/p7pL9F9peEYv/zAF8NL/hFP///" + "/t/utrrutN6SQYr0F//7Ff+3////g3/11dJ+l+I/+ld7ey4KP+3//fpX5DOOD/3sb8j+6X/9" + "en1+v/b//dLr//Vuo0rY0ib//aphKGYdtAinbLfROC//Yf/8NKGEmwvaUOwvtK3SX/7DPcUG" + "NjhsUEHhBwwg8JuEGEGEHDCDhhiopiCKcIOKeJHTd8JNuh/+IiIiIsubERxEREREZcNKIiIi" + "IiNDj+En/X/IbQdf/+Cj/9Npd6SXq3WLDSrwSEdigkEGCDrEREf/////////////////////" + "///////4AIAIAA4AAAEDAAEAAABBBAAAAQEDAAEAAAA6AgAAAgEDAAEAAAABAAAAAwEDAAEA" + "AAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAAFQEDAAEAAAABAAAA" + "FgEDAAEAAAA6AgAAFwEEAAEAAADBBgAAGgEFAAEAAAB4BwAAGwEFAAEAAACABwAAHAEDAAEA" + "AAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; + +static const char fontdata_8[] = + "SUkqALIIAAAmoHcGf/////////////////////////////////kMgMsfUgeDaOQLjkHHIZAN" + "T5A8K5AiDQQ0OW7kMqCEHIZthNJkcMwuGQG8g34gYcgo8go4hmwQIDIGIIL1EGOIKO1/wRmG" + "cvBqEX3S3dBGJhUwmlQSpGINF2/9cIxkfa9U+k2Q2OlpNgqaNzWwgWk2k33Veluk2q6STadJ" + "U2jHlzcJtZcGlS4RJOt9f9f9L62GMw+vC0np5HXS/0n/6Vf9dapwxpdj7rr6Wl/f//v9dJLa" + "kG76X/XXpf//v/j62kl4I2i4ZVd8caX8UrS/xEgvV7aVMUP19f615+S7/6BmGXBh70tK21ev" + "60lxefkmGla/8WxVZM9Y31/RDYOEl5uappMV/1sGKhNfYX/1EOuEHiR57DbXfUMOieIxwZgN" + "vjpfrI7a9XQdJF9sSOv+QL+qLzSt//9IW6x6tUg21+Q2qpHnS3Tf5BtTkNSi/06710rYpeDM" + "MuBi6pNq3+QZX6/S0J8DHdUn8f+v3S/Fb9L/63r8hnH9f26/rS0sgXj9fXpV+vuP9X9Igofy" + "DD1el6WQPCR/pL+w7XIZUEGx660nS3V0vSrv/qm0m2UBr61T7S0dAd13XSTdBL+r0l6YYX+t" + "JtK1hhK7CTDCSthJLpeIpIMUGJHaf9rYohsQsQiBhDEIMQtiECCxESCjKESKPdDQqIiIiIig" + "sGhF1Wh16pfbSSrFtKh3odkcHWI/////////////////////////////////////////////" + "////5A7AyfkDqG265DJBRxDKmQanIZWpDKDIOnIaBhB05BQGQwgkcgiCCIIIglxBEEG/kGPI" + "J5DzIN6EG+pDKoQ2akDFCGBBBDkdCCUI5kE8iuRfIPxCwCZBHIYGMFhMI2w8M42COFBnCDIN" + "7JWQz2SsEcKQzwDBENEENkENkQRDRANwQNgwQRthhnDYRthgzZhhGG5cjZQYIGXDOCBhNYYW" + "k2rMBNcu2ECBhptBtAgdoGHQPQdFwTv+l6T4QIGG0Gwi4UOg2gg0777dNXg2gg9Qq+m0g37p" + "eG/8Jf/pd96Cb7Sb9f//1pvbS0vV0rT9L3/0v/0vWCKjV91fdJ//dK/0n1Xx6eXX0vvHGv/0" + "uXTkde9Jv0m//6+/T20rSevIZCggrxpErPFpX+O36j/6C/X2//7/Ecf95dUnSdIUvCsNLCCC" + "I6vvpL+RR8ij//pe3++lfpev+2l1ffdJeQPCOQ0OEEw9Un6+q3/0v/S/S9v/S/q//tfYp1S9" + "NMIIMNKkq1uwS////0vb/b9+t9KZg0fdL3Wm0v/CCDBpdfvF/wwsMLx/pfpff+Evz+ygMr9+" + "ldPdJe00EEQbpww0tV0rmDf8cfNhfxD9/2/8/foEw//f/Y0vEQQQgw6+l3wb/mB5gfoP8wn9" + "pe/+P4bBv90vfvS9Ag2l10lff++//7fv+3/3+Qau/vtK0kXTaX6bq9ePe9L/shZ/+39pfff/" + "th/3S9/+vhhL/SkcJ//HHBr/2/f9v0vS23/vdL0m9LwwwgmRwb20R1SW/f/d//b+0vff2/b/" + "3r70m9LwwyDdOEENsHpHH3+9LIUfv/9vIUff9vuvryGcf9dY2KX1IUfwYMQgnFik0r1b0v2/" + "/2++K+9tLbXbuu+Oum9L8geEchogMMEEQzXbFBb9N6Wvf/7f7+xvX1t6+k0+k/X6ahhhAk2G" + "kt6TZDj4S/b//b0v92GEttLb0tgwvTS3pL/QbQWGDBL7CQYMFTCVhbDBrffbaYW2r3YYSthh" + "K7gwguKr0m9Jfaw8JoMQgQYIMIQgxCQhAhkHQGIRBhBI5BEZBhAYaGCB4IGQSmGIRBugMQiG" + "hDDiiCg4YT+EoZDOhD8aERERERERERxERERDiIMIRERERERH1xb+qQfpJBF2UAZhn9EDUFTK" + "B7xoQYSB7Qjj/////////////////////////////////////////////////kDxf7IHgQOQ" + "VbIH1kCSyCrZA8cEMyCBqHcgYcgYfIHh7IF4TChVCkM1yGhwoVe+loHBwi8gdNMOHS2/tL6H" + "/yGSCkP/6BFOvrtNeE//Sv9cR+v/p1////W6////p1zZkNnZAv2bCDcchsHyLGQ2DmwnZAuO" + "bCBfiBcc3EGochoHNBAjsg3HIQcguOSHLHLHIJMm5LiC7kMocmOWOWOQXciv/62JDZPQZBv5" + "DYhF5z4Zy8yr0yDGEGM1yDGJoMgxyYRiDIEYmQboIYxNF2HPg8lkaH6hMjhDjQ//p0Xb0XmE" + "YmEYcJNhNJj0Xn+gtUXqL3ReaQbVF5ou1qk4TVQwgYQYWDCDoIMIMKXH/9bSbig6CDoIOlyO" + "jAbFVthw+gsG4qwbbSsGKDYQQcMSPJRSBwd6dPbSfpL/6f6tdXqx1YVf6XTCevem168GYDR9" + "fSutLS/9WxeuqrV/9/wl/7pXXXQ/91p7pXjSW5DRhFH+sLuor///6C//33X4P91bl1pjdJKt" + "hovBr4iQPKn/x/X/F////7NAz/v0tavW9aYaXhG3/+YDM2l/zCf///+3+9e3TvSTeglDFegv" + "//bS/9v//+vw3/q3Wt6pf0PpfV3+xX/t//3635DNv9utb0R9t1X4/+vreyOGZ/2//+uvyGx3" + "/16elvVIjH//Xp3/X/2//3X3//WKjjSeNb/+10rtWyMfX/2//7q0rX6u1d2kraSr/3RdYaTD" + "LdsIv2GvJAZ/+w//2GErCCbCLr2EoNiR161b0l/9g0HI6FBimKg2KCB2CBwwQPBA2wQMEDBA" + "4MEDhhiFFBisETgwITTCg2vCTDaQ//ERERERZg2IjiIiIiIzAa8REREREccfwgg/9f6X+v+Q" + "ZK///0x/+m0sF0q9W0sW6XyGSGkOkI7YSr4rYhAkEGCDrFhCI4//////////////////////" + "///////////8AEAEDgAAAQMAAQAAAP8EAAABAQMAAQAAAFUCAAACAQMAAQAAAAEAAAADAQMA" + "AQAAAAQAAAAGAQMAAQAAAAEAAAARAQQAAQAAAAgAAAASAQMAAQAAAAEAAAAVAQMAAQAAAAEA" + "AAAWAQMAAQAAAFUCAAAXAQQAAQAAAKoIAAAaAQUAAQAAAGAJAAAbAQUAAQAAAGgJAAAcAQMA" + "AQAAAAEAAAAoAQMAAQAAAAIAAAAAAAAAAADAEgAABAAAAMASAAAEAA=="; + +static const char fontdata_10[] = + "SUkqAGwKAAAmoFQGz///////////////////////////5DIBocgZg0PkDwy3JvkFdyB4Qchl" + "DkGB7yB5OnZBQ5J8hmckQ0rBNUyDSOkQWnIZXkMqZBrghs0INDkM/kdkDfsLqqhGYKDEHp0k" + "G0HkFEwoQaaqCcWQzzCMMPXfwg0m0gi89KyCgekkYmCpppYQKgjc0m//0Yy8/16VtP0EGwqN" + "to22ugtBBtJv2vpLdJtJJ1SbTpJKwjnoOgg2swGmFLgiStb3+lXf/69v1bYLpuuR1pLVX//X" + "r/S60mwYorKXH/dfS69J/2vX/9UvYyGU699PXXpa/3//4+l1S2EcXqvXHX1qr/8RIMCP17SS" + "pwggnqvj1XpClpf1+3SWlS2l/v6S+btbr/IKbknv62KH2Fel/VJeEGlTDS/1W9tJKiGL8f/1" + "Sri83qxVr/sQ2K1JBpXel/RAuOFXm29On//YMUk/dhf+qEOuEHQtWG2v+w9GEwZuXj1/Uuw1" + "6bnzaSDtF1/wbSI+Sdx/X9IQ6WPCb0YbYr38MvvCMTVv8gqlyGsR/pX/ukkHaS8gqiMOkk2l" + "f/pfpOlvXSTYa/9/b2/yBO9f9cTQMzuu4/RBSgnHpJe2l+KX6Wv6ST1j//7f/2lpdf/pfkM8" + "el+xVr0/pEMofIZV16+v//9tda/pdZAh1vS+sge4/0kv3fyGbBBVeutK126dLtJLuq+ttJuH" + "+FTV/SOR19dJPSWqr6SX2gyx+ur7S0LbS20n/oJf8PS20mwjeNtf0noINYMJBBwwk2kk2kEF" + "texFJBiExCYXXTWwwkCBrEIEDimGEErDCQILERBgsQwgafFRSDEIRDCEMIMUIYhQWQyAaHER" + "bSrERER/0q90tfukqxbWh3odtLbSxH//////////////////////////////////////////" + "////yBTDMpkFsFhyB4YOQyAboILYFByB4hyB4vkMgCIK4iOQsFWQ07IZxyBEeQyQ1PINNLIZ" + "icEDIMeWcgoBkFy4IGQIIIoZByCDhkHIInkMEEDFCGyhBJkFzggyDcYCDINxgQMgwoIIGRDk" + "EIIp0O0MhjrIPyZDCj0GCD4aOEHEN3CPDDaDTQaapp6bwjxByc2EeIOTmGEcbw1TTT7ppJ1U" + "4B46aPGGmQabJeECIJZDPZEmDNhIM2JQIHBggwQMEDBAwSBAwQNo4DdkCHQIGyCiw2gQNkFF" + "htBB5cZwWGCIMOGCBhBglBggdBA6U2Ca5c2EbDvwbSayCZh8Ogg+/6C329JvbSb3SD777/q3" + "TdQq9INoIN/oL2/9J//S7W9IN9pBvv//tJ720m0tL/SbT3X2/9L/9L+XXSvdK90v//1p0nrS" + "+npuXX0vb66X/9Ll0176b/b///eu++1/yGQxyBwOOk63+++ONV/6X8uu3r+l/iOP2t6uk9Cl" + "4WHqR8e7r6SH/Uf/S+19v3/f/96dGF7q0kvCw0qCBAn6vpff//pe9e39/3pX/a9XTaTql5A9" + "wQ2QEmHWgmKer6X8iPkR1/9L7X30vSS///991bpL1TCCDBpKv76Vb/9f+l719+/W+lD/erXW" + "K0v7wggw0qS9K4YIL////QX3+3/pfpMoBq/a9XTTapfWCCIFy4MNL694g/44+P9fdL2/8Jfn" + "mzoGZ96dX+6S92ggsMNLS9bmyD///i/v9v/P/6BMP+/r22KS8RCBCGGl+teDf84POD82DH79" + "1//5HDL+Gw3+6/a/XhBBhpddK+/9PT//N7/r2/8b9yGpT/q1ek2l9BBuvS6vu9f+yDuRj/+3" + "9r7ff/2D/2r16MLpfT9+kh7/X/xf/t+9e39fW2/71q2qV6XsML+qV//jjkCM/9h/a+36+u2/" + "/9dU3peGDCCbdtalw/2/93/9v3r/f2/b/20r71frwwyGWXBBVbaL8JK/+l9//t/a+33X1//7" + "G+levhh4QIXYqKNFX7fWQR9v/9vIO+9e3uu2ltkND/rHUaTekQw/hhiEE2IpK+l6///7elx+" + "33X+313TXX6X5A9uQUQGGEEQa4tKr9vS/b//b/a9jbS20tvX16dJvS/TChgwgk2Gkr6TDILj" + "4S/Yf/7f/+2ltpfdbaX6Tfr90GwgtsJd4JNhcEtLb//b/r3YaWw0tu0uDBJp9fSX/B4WGeNB" + "NNCEGZkghCCGEGGZlCDCDCDwg2GhhN0GE3YYJBBsMEEEGw4YJBBsV00kw0Gh+1QeE0xCCDBB" + "hBMQkCChBsQggwQYQeEG2FBA8IGCBuGIQQYYoINuIQINr8JWCBr4qIiDCERBhCIgygDw1IiI" + "tCLhghBghEGEIMJrxER+hEaERDiIiPpaB/0g/SIGwCcdJFzOgGgr6jEGvGgamgH2EL4j////" + "//////////////////////////////////////////+QP6EDob+QPBoHIElkDw9kCyyBJBA8" + "F7INVkDYDEZDLjyGVCZBXmCqQZPIaUENEAoKlt5A8sTSfV00/S2/6BwdF3D+Dg//pr6Q/+QW" + "wbj//MKvrtNeC/9JN1/iP//+vr//+k3////9r///+k9ZeECzPy+IZY5BuP5AuOXhHhDKHL4g" + "tOXxBowscg3HLjIGByHHIG9CMci+Qzv/+3BEMyeEGQMUCGQLzyBimgwUgRmRewVNBgqDIZXg" + "qYQsFTIEUyGzAUgucuippgmRLIOcuhDFX/pYhPTChGHCNzROBBuKAXpgoLoLBU0wVMIwwwVN" + "Fzgqow2icEgoYIGCDBYMK0EGEDClxP/7YRtvl20YOgg6CDYVBNaMXfQXovNGK6MUIJt0XbCT" + "WqCDhX336B6apJL/0ug3bpB0nSsGbDZZsNghBsHB9BYNhiE2GIQbSbBsNoJwYkergzYN4P1p" + "9pXXX/q3vTaWrr6V1/pf9at02vTX/t7fTaT+l/9Y/rr0370/6XTT0/fr44/6WnuukKpdkFFk" + "K/pN+9DWv//6C//S/rq/7+XVJum9Kt0DXxEF9V///9f/991+ZgY+6Tf8VrQSww0YwaXkDwOE" + "f/H3X/H////sH/+k2k1dJN6SQYrwjj//Ng1dL/m0////9h/t1/tvpN6SQa9Av//ev/b////w" + "3/rpN6ekrelQ+v//sMJf+3///X4N/3t+lt6X4+l6V33hiF/7f/9+t+D/ulr6L70q////+XBp" + "/7f//XX5BQO/9/TdJNvpER//16d1fS/9v/919//1emONK71r//0rtb1/9h//3Wla/XrHWrxS" + "S//YRdbpsijtourZFfT/9v/9+0E2vrZ3hourW0k26X/7aWgwgmGFYaVsMJJzWBDtPTYaaYTt" + "O20oaTYRhUGnUUxV76V0kF/9ioOXQpigxUNiggbYQOGEDwg3CBggwg4MIHDYaCimIWEHDCCa" + "ah9OrDeP/2ENBoNMIQwhbERxkcMgYqbQTCxDEJpoX8RocfxEREUYE4jiOIiIj/2En/r/IG5d" + "J/1/////H69JtLIH9NJf3S6uq9ISh0CxdL8gt46iO2kl6FbYSCQIMIHWGISCTCbWIiI/////" + "/////////////////////////wAQAQ4AAAEDAAEAAACoBQAAAQEDAAEAAABCAgAAAgEDAAEA" + "AAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAA" + "FQEDAAEAAAABAAAAFgEDAAEAAABCAgAAFwEEAAEAAABkCgAAGgEFAAEAAAAaCwAAGwEFAAEA" + "AAAiCwAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; + +static const char fontdata_12[] = + "SUkqAFAMAAAmoFsNP/////////////////////////////////kMgNpyBoLGQPBocjfIEkED" + "wU3ILjrkDxwmnkGmKIa+ENfFshpj0Qy5kNIcg0UIHhxyCjCLhDSHIa9kG8yGZPCqpAvBK4YR" + "oCU0km4PTChBkMqgJxhMhnCBBhB6u/QIoBubbpPSb0gjbYKmEH4S0bNo43/rhBpNqjHpKyBh" + "/SDYVNNLCBUkG0EG//0Yi7fdJOqt3S02CzjaPNroLSdJv6qtLDS2qT1TaaVLo5UEDwQb5gGx" + "TAYXdf/ql9PS+t3rVwurp0XXS6SdW+v9f9fpJwxRcUrj7/9JUv/7v1X/Wkl2DGv9aTpel16X" + "v66/6/pbkMyK79/S+tf2///H6tJLbBHv6/4/66Vpf4iQYUfqulXhAioHSrx6S9If//9uq0kk" + "tL/f0v9K0v/v62KHbq9f60vNNdhpX+QJ4JXe6pV7X1+qSXhB0kw0tf6Ye2l0RNFxb1/oEF8W" + "pf0xC/14gwxCSTXv6/yBiiXON4Qattr/sGOmtcL/0oNeEDappMO1+thpIxyIRuOl+kjDdcJ4" + "lzemwwjC/4byL6TbNgp//6ENpY3CDpBG5sV/qQaCEgjc0rfyDKTIbWiX6T+9WqCDbVbkGRRL" + "t6Tav/1/pWl9PShsNL14dJK6b/1X9LXLHf1Scf//bVv8gtRVfpPEX71vXRAnslG6SX2l+K39" + "a/qlrjX/+3/1paX/pb1+Qbj+l+2la/+lkM26/9L1T/+26/Sf1IZg9f6X//0l+xT1/6VrkNDp" + "N0vSWQPOOvX+2/yGlBBkdetLr/WrVLTX+km0m2H+Cp1a6RB3b+0n1eku/9L+0DLHtLpNXrQu" + "0t6tKrUJfXD0knpgwQt/+rSTW0EnYSbpW0kF/weEtsJMTcF/Tqw0iBepYYSIZurDCTDCSsMJ" + "BLa1DEQkgxCYQa0taoMV8QriExVMQiCjsREGFiGEGm8aHaEQYQsIMIQwoWQyA2nER6pIRERH" + "3Vf26pf0kq9v1xbSSHdKFtpDt11WI///////////////////////////////////////////" + "/kC0GD5AzAxBA8DCCGQCoQQMw0yCB4EEEDwYoQyA1YNxDuQ8Hwg2YQ24vIZILHkNQ+QaS4IG" + "QzqyGWkILkwQMhs1ITUg+pB9SD6kJQhjUhmHIGDkMUIZyAgyBgGEGQMBAgZDPQhaEEqIQggm" + "hCoQ1QyBFqQX5MgwGQl1hBgg7hhHyBw/CPkD///vCPEHDCPEHDRxhx/r+CeE6i5wDwxTCPkG" + "pDSmT9GwSQ0TIzkMuZF8homR+EcB2Q2eQI8g38g38g3+cBQfDUaPgoZDZYQIGGQMTJTCBAwy" + "BiZKaBA+QI4hnsGfAgEDBWQe00CbWvRttGwR7CDYQQdhEE9hA0wgaQQdpppppBNPTtIINsIN" + "oINsINpPLhDgmmnaaVyGzkgepgCPwg2EEGHe2k+GHvuk//pdrek3uk3uk//6/t02lSX7aTa+" + "l4f/Sf/0v70m9tJvbX/967SbV60vS0nvdL2/9Kv/S9b0n9J//3+9td0m0tL90m5dfX2/9L/9" + "Ll0+XT9vfb3Sr/3S/ur9J8erX9L7xxX/9L+XXb1/X/f6/+6dJ0q/IZAdyBY+pCQ9X+O/0P/o" + "L7X36v6v8Rx+/RhVbW0hS8LD6BBny1fpL/X/0vevb1f1f/90r/un0vCw0lRyddXr9//+l9r9" + "/f96V/3ule6TaSXkDzggogJMHVIJjdX6/yFfIV//0vf9vS9JL//dL3Suuv00wggw1Vf7wku/" + "+l/6X2l7f//pQ//691bVL1sEEGGlpVpeEFX///6Xv+/vpb6TB/36t7FaSX+EEDDqkv3iv//h" + "hf0vtL2/9L8IKdQ0/uk39U3SXvhBEMomGGgv+rg/44+P9ff+/8JfnOynBp/f1q+qXtMIIFhh" + "paXq84Qf//8X9pe3/nP/BBv961b7Yr8RCCww0vSXvITv58efH5wNH79/2/9hfuG/9ev3S8II" + "QwaX9Je3/CDwg//zif2l7/4/tkNQP9vbXpPS8IINpdfvvf///7fv+339/kNqf+l7a20l8IN1" + "fpJX36/9kGCP/Df6Xt//7Yf+/r0Y//v+lx7/X/3/7f3/fpeltv+9at0lel8MEt/ST9/33chs" + "//2/evb39/b/9f1pvS8MMIJvbRHWpgMfv8cbD/+39r79/f7/t02l6vpeGGQaSYQT3YXX/9L/" + "/9v3r2/r62//X29K9Lww8IIXYrCR4Sv2/9v/9h5Bgftfb3XbXbINx/1/rpX8gw/hg8IKwwmI" + "S76V6WQXf//29divvuvrbuu9uo46vS/DDEIJsWkkr9vS12//2//29tLbrtV+o3dJvS/IHnBA" + "vYMMEEQ04bFLfpvS62//2/39jettLfrdWqpX0v0woYYQSbaS3pNkM4+l+3/+3/Xu2l2lt69p" + "fpXr+tBhhArbCVPhJhhcJft//t67+7DS20tu62GvT030v+G0FsMJLagkygWmRaYLsNdf21BV" + "q12GEsMMJd2EtgwSafX0gv9B4WGfMIEUAgNCgxSEIhlkyC+oZoOQY0IXQhjXIZ9GDQyGEOCI" + "YYKAIsGCRAvoydogX0YcGEiGXoxX0CTBkC+iH7Sh4TQYhJqgQYSBLhiCu/t1vTtwxCsMQrbY" + "hWwunSbv8aERDCERBghEQZIA8GWIiNCLhghBghEGCEGF+IiP0IjQiJA8C+CIiK64QP6pB+kk" + "gf+i4zUBoDN0iBKb0INfCigak4HhI0QMw1IvYQjj////////////////////////////////" + "////////////kD9BA6hrjkM2CGYP5DIDUggeBiyB9hBYsgeGVBDVggbQ2ZiVHkGiCB4rkDfy" + "B4bJqQN5kNdyCiCBEyDVNBbeQPHyqqqqaf/e6aRBYsgeBfEXcgUYnZDRZDUtLb/90hf//9NL" + "1/8gtgsP/8xtfS2mvBf/X/8R//6ptfX+v/Xr///+m1////V////9K0iGb/kMz8g0fkD4fyB4" + "ZxyG3MhmjkDwUp5DMHIYHIHgTj//uwQTycyDTMhl0wnhPLmQy4BcheyBeC5kfgpcwQYKXMg1" + "0M5DZBPAg8FBSBBBM5DCCK5EoQx5C4QcgmcguI/9KxT0wQYQ0bmiQGgwyGBFMhsmQInpZDPN" + "NBkNk00cYZAiaDCGQXmFRttEgHkWbuune7//7hGDeEGEbOEbOEEGwqQfT10C9NNU0EG1QYRs" + "uqQcL4YIGCBgkyFsG0CDBAwUwFX/pXQfRt0EGggg6V6TWjDZBRZDZmlkFFow2jDkFGIw2k5D" + "RiMG0EGiGy1p1Bwd6fp0n6S/+n24hBtXSDpNgzYF84CgQg3voLiEGIQbYhBtJtiEDaTxLuuQ" + "0W76991paX/rdPCdLp/0un/S6rp+6dLhP//WtNq36//TY+366X71/pdNPWr02vjtft72rpdV" + "SXZAxhBx/X66f9v/f8Jf+9X/1Y/62i602lqKXug0/pv9RS1///QX/6/pfD/br3WKbpJBbaDS" + "8RIHgYPv/DC//+v//7/ygDH/dbprVIJYbRuBhLwRmv/x9pf8X//v/7B/6V17vShh4QVBj8I8" + "f/4L6/5tP////Yf7fq2vfTeqQa9Av/5wNS2l/7f///+G/9J66vVK9KgYXpf/+w0v/b///r8G" + "/2+9+26Sf8fX6u/2K/9v/+/W/Iav/6WlaSL71S/H69f7wwv/b//66/D///pb0v//16vouGp/" + "2//3X/yGU7+rdOrGrd9EKP/+vttr6/+3//daTf/36xVJNukkv/66Xe3pf+3///Wv16sfpXGl" + "//aLraTbYRhYZCPp/+3/+2laTYX1u0XWmnV9L/+wl3CbIjsMJbDCXIwG//Yf/7aVoKGEbXus" + "zthLfqm2kl/9iFMwXBhJhhJiFMwzjIMEWQYRBkMEZBghhkEIIYIMRMwwDg2GlDCTELIMaQwS" + "ioqZgY7glB6H/7XL4pimlYVtp3fbV3dp2xCimF6EJ2uq92v/2hoMIMINCGEIbERxDBCIiIhh" + "TeEGsQwmgwhd6EccfsREREIwE4jiOIiIjX+Egf//1f9f8gVq6/6////S1H0vSb8gfo0v90vu" + "v0m4WLrXkFsGsdRHtJL7S2GCCr4rDFEDwUYQyQ0yCCqGlhgqXaxERH//////////////////" + "/////////////////////4AIAIAOAAABAwABAAAAYwYAAAEBAwABAAAAeAIAAAIBAwABAAAA" + "AQAAAAMBAwABAAAABAAAAAYBAwABAAAAAQAAABEBBAABAAAACAAAABIBAwABAAAAAQAAABUB" + "AwABAAAAAQAAABYBAwABAAAAeAIAABcBBAABAAAASAwAABoBBQABAAAA/gwAABsBBQABAAAA" + "Bg0AABwBAwABAAAAAQAAACgBAwABAAAAAgAAAAAAAAAAAMASAAAEAAAAwBIAAAQA"; + +static const char fontdata_14[] = + "SUkqAKINAAAmoCAz/////////////////////////yGQBw/kMgGYcgw5DJBpvIHg1wR3kCuC" + "B4NFhbrIHiwnZAxZFjIafUQ2+BJJshrRkGnyGtBBqmQ05kNqyBcQQ1YINyZBRMhpfhf1CMwz" + "S5hqg9W4aggwoIGCDCWC4QYIPXrwR1BQm6Wkm6pGzYKmn2EFQRsgwjhB/9UjeXg0m1RifVkM" + "t1VBNhUGE1pAtBBtBN//hBYdboJOkk2nVJNgj3R4s8b8JUk6TftfpYfdafV09VbQXCDcEHWX" + "BWCmAIraTf/9eldL0ld1VcLp6bRddKkqff91Vf9fXbDeqtwum0v9L11v/+v+uqSwxR+rx/3S" + "9LS+vfqtf9da7DHr+/pel/79f1/9dKr5Boha9Lr/9L1/a/8fXSqsI/ev/HS9Kkrrv/IZ0n9V" + "aSXYIEU467ePX6j2v+I/tqulSulfX+qX0ldf/e9U6Q9wr1X6pfJ+u2l/kFqyO/tJYr2vr/qv" + "BA9JhpX/XeG0qqtq9f1SS9NIl3DS1/pg8MQlyJWuP/9JfF4QaTFN//EMaVd36/SIZrhNLnCe" + "EGob1/2U4bUJ/cLX/iDXQQb06Ydr0uw6RvZCaePX6V106EwdK2GF38NqQnJOzgE/1/SkcbS2" + "nhBtQjc2JfX6kGrSgjDDW3/r+hDfi3CekEG2v62XmoQTdN/kDgCIKtS/pOl+2qQba/IHCTD0" + "rat//X6Ta/XSuGEl/htaur/0v9et91SbH/+l1evIH0a/pOhJAaf0t/ogtWRY3Wm9v/GutLX/" + "S0sdfpfbS/X9L/0t/r9L9v/pv63r19L8gXH//tL9ddKiDVn9fX19JfbFPXXWkQan+npekv//" + "99df0tLIbHW+vXIHjj11S6bf8hrWQJHp/Sb/rVfS01/rddu/BUH2lpaW2k9JNpJa63pJX3D6" + "6TX9IoZddrf+gvrvS3psIMk7/9N1odpbpOkraQS/70km0mGEcxWvWrpJqwwknDCCbSStJL+o" + "PCW2EmKDXWtUwwkQy06xCINQyKYaWGGEECC2vDEQkgxBMINN/TSsV9bCYhJMUCBYiJBppiGC" + "DC0hxoMIRBghYIMIQwULIZAHDiIvpKIiIj91X7qtfdUvuklXtrS4t0o+lC20h263SxH/////" + "////////////////////////////////////yBlyPyBmCy5A8NUMhkrQgaA6CB4NKCB4ZhyG" + "QBxZCDkHcg8EUcg3cgr35BbB5kGw6kNRQQ1QZAgwQaBogwBkGgGQ0VkPWQxWQxWQxWQShBes" + "g0oINBBDCCDcMhmJyGWrIaichmKwQMhoEyD1kEDIPUQQiPjIMTIaOIL0IKMIEDc8B4WCBggd" + "sMIMMgYZkOCDDQYQaDCDShoNwg7QQMMGEDYYQeGE0GEGg0mGk1uutMIPBnthGYRAzwIGQaMO" + "nIKMPWEZhiQL8DBEMrgYIhldOBlngbcEDZDKgIzEYM8EYRmIyGbhCURwJwZ4C5gFAIGEGCwY" + "QNoEHSr7CMxA03ISYQIgxjkGJ5BiMgvCBB6apqkqtK9AgYbg2gQMPBsIINTAU8FT70/T0G1m" + "A2L5gbRwF34dBB8N/4QT/+gv70E3toJveuv/XT20m6pfSDhBBhp7aT4b/pBV/6Xa3oIN7oIN" + "7aT/+3X7aTpaX02k/ul7f+k//pf+k/aT+v1/+qT1daX/TaML6Xt/6X/6XMJowswnre63vX/7" + "ave2rpaXi6Tffpff///hL/9vSb9Jv1//6/0m168hkA3H0np/r3xxS//S9tL2/f9/xHH/tGF2" + "6ehXwpA/foh7bW/Ue/Uf/S//b0r9K//20vtK0rSS8LDpIEzZ19Vv9f+l9pf+/7//+9e6vpeF" + "hrhHmR/at6r/r/6Xv+3r9L9X+2lq3t1aSXkDyggYgJMHSSCjf+vvIO+Qd//0v0vb6/q9f/79" + "LSbSr00wggw10mtJ9Kt/+v/QXt/t/ev6V//pPtpevqmEEGGlr/eEl//X/0v0vb1fpX6Yf7aT" + "98baSX3ggQYaSSXpPhAv///9L2/2/9L8JSQCr/+vadJL/CCDDS6r7j//+P9L9L//S/CTNYa/" + "3S1dJq+vpoIIg0AQYaWv1yXDZ+OP/0/b/b/wl+ZDIgNP999+6S+00EFhh116vOCB///xf6Xt" + "/5Z/4Jh//pe3el4iEFhhpaql3g3//OAX/ft/t/8L9wb/bSferYpLwghBg0F9aT7f84D5wH//" + "Ob/S9v/H9shr1f/1arpeEEGGvX97f1///t+3///7kFU/7pWr6MJtV4QINpeqST7////7f6Xt" + "9/f2Df9//7r8IPX1xfd6/9kNGn/t+3+39fW2//ulaSTel9+36Xu//7//t/17e/v7b/tpe+k3" + "pfDBf1pf+scchld/7ftr7fr2u7//1ev14MMIJvdUpgGH96/b/+3//919d/71a9U3peGGEE7d" + "yOqSX79e//7ftpe3v/7f/avuqV6+GDINYEEEO2EnCW39/9//t//t91t1t/09aV6vpeGHhArY" + "qKLtL6fSyGd9//28hoftL2/X12yDd69bX/Sb0iGx/DDwQTYaYSW3rel/f/7f7/t7dbdf/f8b" + "1V9fhhiEE2IpL9N6/t//hv+K9vbXtdv/V6qNX0vyB5QQy7DDCCINsWtPq3pft//sPXf/tLet" + "vS26jd0r1/TBIGGEEm2l3pN6X7f/7f9extpbaW3a9r1Svpfrhgwgk20l9JhkNj4S12H/+3+/" + "u2lsNL+uwk19N6S/dBhhBbDCVN4JMMJYIL9h//t6XXuw0ttLbhhLYYS/Svpf8PBYYMIJO0KY" + "MFQhIUmwYVNNPTbQ03TTdhhBJsMJJtwwkmxVNOraaH9JB4TTFEFAZDGqCDEIIIg0AZBisMUQ" + "z1kPWQxXkNlbBhSC+mQlRDGmGKIZVYZQwiGVWwcQiDTW0/QJQZDKrX2sPCaBgvRTg2BIhA0u" + "GS4KP+/te4YLDEL2Fhr+n/xoREGCERIKgYiJBVDERxERxEODBCDBCIMEIMF04iI+oiNCIkDw" + "1bEREfrCB/WEH60gf0qMMH6VIIGU4GoKfSIEsGKCDV9UQNA9IeNA1JAHnhD4j///////////" + "//////////////////////////////+QPkEDMFW+yGQBPBA8NSAmQZ4IHhqQQ2oIEoDFkGuC" + "GlHkDwN4ILMyB4NM1ILMyB4NMyGrNLYeQPF4g14kFC4UgqQQLwFCpbe9pEGbiB4NfIu5As5N" + "Mg34hr9X+qu6Qd1t3Xb+0vUf//9G1/S+vIGYZj//tr67TXhf/S6/xH1//bX///9L/X///bX/" + "//9Lr///9Jtf/////8l/kNTiHwg2f/+k3LhpGgZclMhqeQaJ5Bp/INU9BkGiCBeMgnZDLgIM" + "IMhmwgyDXMg1QSmQ1KE3IF4JYQUHyGbBBdyBGhJBDXchrcQfCC4ZGggwE//xCDwgwQMIYIPJ" + "OCD0wUF1yCj00wVMEDBUGEMFCgg8gY8h+8hjRSEQE1//9JsJ6YUKEcMMIYRsjqBFMhsOC6BY" + "KmmQ0HTRsgwUINSDB1RgcI6BiCgz4OCBnwSDBBtAgz4OCmARf/thGxvTCOFCODoINhJJrRg3" + "+gvRt0YN6MGwgg3phGxVqkGgvvvbh6dqkv/S6D6MDaCDoIHS9J9BByBjCDfNLIGJhtJyBfEE" + "HSbIKMRgVoIHIKMVJ1IaMIJnTrTaTpaX/7e8Qm0mknSbIN8VnAMCn/S6YpuKem4hB0uJdpcg" + "oz3+9tb//9Lq6DpaTr9XV/hBdV1avTaXQff+61S66pL/9t3r/6b1en/S6aenW/Xof/dW/bSd" + "dL8gpD+lj7aTrr//+l//T02vVj/1ownTaV0KSW2QzMv6b/xr///0F//39ff9r1r060luEDXx" + "ELuq///+l/+vv/B/vTa3TFeqWw0DS8hkBoI/+Gv1/xf/+/r7JAZn7+n2m6Sr0bMMJeQyAXmb" + "P/j7X/v////Z1Av90v19UmHhBJBj8I8P/8iAMXr/nE////9h/3tpN03dJN/QYXoL/+cBs2l/" + "7f///+G/3S/W3XfSSBr0vr/2GEv/b///r8H//W6+kr9ofS//9iF/7f///+Q16f39Poum3pfj" + "6X93+GC/9v/+61vwf90m10lb1S//9L+9mA1v+3///X7/39N6T3SX//07r6X/t//v+/kMt3/d" + "LX0rdVId//11u9vS/9v/+0tK//19jikm+q//16bbX1/9v/9/rX69YqnVtvS//tdL0XWyDj6/" + "+3//aVpNr39our/XFJf/6L+GgmGQo7aW2vf/t//t1DSsIwvpWW8NL6pJt0l/9sJcMJMMKwwl" + "sMLyXAv/2H/+2lDCCYaX2lFMVbTurdKl/7EKDiExTFScNAogRrDIMazQMHUGJAjVsg+pDGpt" + "JOCHUQ0DQGEopiFkCKoYSdqThlfBKD0P/60Y07WGFt/+wuv9iFCDXxCaa3pqnf/8MIWgYQME" + "DCEMEIcRHFghEREQwU5BBhYhhNBhDT4jQ4/iIiIhGw7xHEcRERH/0g/9f4Sf//yB+Bf+l/6X" + "/9f/+ra+PVfXWCf/q2uC6r9NoLpuq9RHHS/IGeOltpV9rtpJehWwwSIHg08EDCDrDEKECDIM" + "tVYYIfaxER/////////////////////////////+ACACAA4AAAEDAAEAAAATBwAAAQEDAAEA" + "AABKAgAAAgEDAAEAAAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAA" + "EgEDAAEAAAABAAAAFQEDAAEAAAABAAAAFgEDAAEAAABKAgAAFwEEAAEAAACZDQAAGgEFAAEA" + "AABQDgAAGwEFAAEAAABYDgAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQA" + "AADAEgAABAA="; + +static const char fontdata_16[] = + "SUkqAHAPAAAmoCQP/////////////////////////////////IZJx0QyQzjkM45DJA3vIHhr" + "2RbyB9BA8Gy00/IHg8XZDMsiXkGzqIK/Akk2Q2nSINUyG25DVoQ1aEGSCGUoINjkFEyGPIZU" + "yGrPBVXqwQahNUm4PCBhQQYQMFwQcYIGED131IZoaNsOk6SbVII4bBQgwmlhAtHDDCOEH79Y" + "QNINqnrZBoHrQQbCpp+EFSCDYQQb/1wjkXbSekbfSbT9JsFTR82uEFpOk3/+gsOtqk6STadJ" + "LYR9Z4bhBv0FSTdX9fpYf6SeltP6cILhBtBOswCkpsNFdX666S+m1/p7pJbgtJ6bRddBVVNp" + "X++v69LpK2G164XT1/pa/v79a/69dWGKJ2krY+3ul6XS6V/69f9a0uGP/rX/Wkv//9f9fSps" + "Ol/vWl6Wv7/X//1pa6kGu9f/0vS69f+v8fW6S8Izf6/xr/1uu99yGga/qtaSbBH1HS28fS9I" + "atf8R/dVdJLwlf/6S+q9f/fdVpD9PpL9VXkvqmGl//uqxCW2r//18EDVbSv8gerIl3tpVW7C" + "vS/VKvQekw0tevb7SVrx//pBJcXRH9MNBf/yhQxCrIUZXf/0kvahA1Ypv/qIMMJQmv+l+pBp" + "cIOueG8J0w9f1ZLgyJNVuC/9JCDXhB9NWG2v1sNQjnIWvx0v6uug3EwTSu19cMNIh/SsGcF/" + "/6UuNpdaBB8I5hsMI2lv4N4QaTeP6X6iG1xbptJBBtiF/5DU1SCON07//9But61SDtfkFgal" + "29INrf5BZEyDInS/S1/bpINtJf4dJK1b/0v9JuvrVXBhf+303Tf//6Wu+9U2P/ukv3X6pdaT" + "oSGDZ9JXrogerIl79Orf5A8S6/0v/Wtev9Jb3S/FJ/S1/pXrH//2//v0t69fX/0v20v0tdKi" + "Gl36/X0Qyn/+20nr+tIhpj/v16XS/SX8f6X9L5BQ9dL0lr//7Vr7+k2l6V9euQPDx/pJdNv+" + "Q2o7rS62/VdUsJ//trbD/BSBPiWulf6T0k3SXfrpJdWw3rVPetIhiel3V/0gv+9LdWwgyKP/" + "qlfobaW2k6STapa9XpJXTDCH/XulrDSuwk3S6QS3pYelthJibabS10m0kGsMIJOwk2ltpBBd" + "LyjggkgxCaDX9PtpEMwGsUQ0xDEJsJJMNBBBbXgxFYYTCYT/tbFfC4TELDFEMueIiQa0JCGC" + "Bq6FIUgwhEGCEMIMIQwUFkMk3ERdaxEREf60vbVL/qkvbSX9+ku7SS8W0qHekttIdtLbS3ax" + "H//////////////////////////////////////8gMBZD1yBoDQ5A8GXQhkg31IGgFAZA8G0" + "MgeGsQQyQ2oIG45AkvyC2GvMgqoTIa6QhtBCGgbINQqQYFCDWoIbBBBBBDAghgQQwIIOgguI" + "INYZDTIIYIIGKgREA0EwDYRANBMBqgyGgoIYGEMVEHrIY0IYqyC+hAiZBvMhg5DL4gQLMzA8" + "PBAyGsn4MIHIqGZoED//9bwQcGCDgwgf/64J9pcLCYQOyG0kBGgeQboIQgg1AZBQYCMweQLz" + "IGJkMuZDLmQy5o+GWZgqOZgYZDNxHwoZBpORaI+FDINJyKdHhNENlCBjAZoBgEDNAzyGzNHA" + "zuv7CNBA1Z8I0CB2CIMHZ4GEzwLwgQO00001CadJtoIIGHBA2EEDDYIG0EDzYc+HtNNU1dEC" + "9EgdJmwUL5smEfBh24NhIO4N4fDoIP/6Xe+gg27aCDe2k01u+364eg3wkvQQbQQYfugnww9/" + "Sa/9L1vSb20m90n//p/tJ0v+nQTa7aTW3/pP/6Xfek3uk3tpfX7/dNq3Wl+2kG79L2/9L/9L" + "1vSb7Sb///tK1V6tJUvS0nRhd0vf/S//CXcwswnpPuk+6X///tpOlpfugm+/r2/9L/9LmFX3" + "6b+m/3/9unutJv68dbS/X28cV//S+69vW/W/X//XRhdv0tfIZAaQ5A8Ufp9/r/6//QXuv30/" + "q/4jj7/2raTdCl4WHpEH5tb6Ue/Uf/S+69vX+v/+6tf0nVLwsOlBM3dP9b/X/pe6+39/3//q" + "9PTdWkl4WDWkeb/vSX/X/0vuvb1fpX0r/br79XqvIHhYIZdhWHWEE6TaT//kF3yC7//pe6+/" + "XrX//rpatpWkvhNMIIMNUko/vS9v/r/0vuvv7670r/er3punVL7UEEGHXvpegq////S9/2//" + "/ph/3ut+k3SX9hAgw0tKvfCS//r/6X2l7er6SvpQ/9enVjtKvXCCDDSSS9bhggX///+l7/t/" + "6X4SZ1BW+3X/T6++EEQaBMMNL/p4h/668f6C+0vf/S/CTIgGz+ut01aSX00EFhg0tV+4P+OP" + "/0/f+/8JfmIYP96un23SS9poILDDS6rSeeCB///xf6Xt/5ZH8Ew/73XXvS8RCCwYaX6XeDf/" + "88GX+H7f7f+wX7hh/69XVsVXgghDDrX0vb/ngfPA//57f6+3/j+2Q2hH717+6+EEGGl0l77f" + "++//7ft17f/+2QV9f7W19PS8IEGHX6S3v9b7//t/r//9bkFNH709NqjabSXwg2v/T93///+3" + "7de33Xf2G/7/6S9L4Qer1SQvf1/7IN6v/b/X2//9sP+66V9N9fa79V+/X+9/+37de3v7+2//" + "Xvrevwwv6pX/+OOQzJ/+3+vt+l6W//e2ukk+l4YMIJ7fVGwz/vX7D/+37df9/f2/7pdXpN6X" + "hhhArfRdUqf36///b/X29//b/7/f768MMhqiYIJrbS0Et/f+//7ft17fpb1t/7paWqT6+GHh" + "BC22lpU/vpff/7f6+339r/6X33SV6RBRHhh4QVsUxCJ2t9XrkG77f/7eQUPt17e9b1tkC8V/" + "exv76VeDB4QThhMJa9W+v//9vX/77S7S2/73Sr0m9L8MMQgmxGlf70tdv/9v+K99v39vS3X9" + "ikr6/IHhYINEBhhBEFS7S70m9L9v/9v9/b3S20v/umKrV9fwmChhggSbaVP03hLrv/9v+vY7" + "S7S29L136b0v7UMMILYYSW9WGQLvpft//sPS3/bS20tu67S90r0l/oMMIJNtL8JMMJYS1ww/" + "/2//3YaW2lt2lthWqpX0v1w2gsMMElbwSYMElIOfW2Gt3fbarbXuGwgrYaCu7CVsGEv0r6C+" + "6QPC2DCSpoQgxoQkNWDCqq6txrppuwYSUMMElbgwknFe6tpof1h4TQYhEDGpBisIIMIIIg1C" + "hBgQGIRDQIIIIIYEZBuIDBhSC9TRDjCD1OxCIZohiEQzRDBxCINYwNNNUCTBkMsQvtUHhNBh" + "eiXBVClWGrwZCAX/7r/4YWGFuGFhhf1/44iIMEIiDOoZIaDUGQEQiIuIhwYISCmGIgwQhgvx" + "ER9IRHERIHgrwIiI11hGgGwCzroO+qCB+loP9JGCNQGwGXpECYGYPSCBkuBsBt9Q0qBr0ooS" + "GciHjQMJHQDx6IGobv8IRx///////////////////////////////////////yB49PIZIsED" + "wZIIHgxxA8rIHgqWQVrIEsM2yGnZDUvyGQoIM8yB4KnhSB/MgeDZMhtTCWw8geCTIamBIFIH" + "g2IUgzEEFeCGXAKC1t7rXrpp+v9WpA+4geCryMHIHvk0yBfiCp1b7ql6Q/+vf2vr///o4tel" + "015AzBmj/6tf9prwv/q/64j4X/0rS//r/vf9f//0rX///+m////9df///6b////1dL///+rg" + "iGpTIvkG2ZDS/IaX5DUpkpkNOCGXGQf8hmOR+QTyGnBKZDXoQ04I5kNqhJyGVBLiBc+QanIZ" + "4IZ4ISCOCOCDa5BUwgvxBeCJBFciuQz8Qxf/q4gg8EDBAwgeCB4IPCBgoLrkC/BBhBgoQMED" + "BQgYIGFBQoI1gokMzgWOMg9VkKGQwdY44//qwnphQhhHDDR1BQbJnnpkFCCGdGlkM6EGgyDc" + "hMI4QZBuhNDIEIVGx0ageQqAZoGAQMEoMJuCDBBhL/6unphHChGxwgg2FCCY9P9AvCp6aCDe" + "gwjg1qEg0F9pphbCB0mg1MBhf/Vo2K9GyYQQcIIHScKrWjZMgY8go/QWQUejhsI4bIGPRsmk" + "2QL+jZNAg5BR/ThSBHkMe9PbtpPX/+r0H0EG0g6TpXLx4MtPEJ3fS2IJiE7EJ0m3QINhIO6p" + "Pu/6039aX/pXvEJtLSDpNj8+GWn/S6aenVuKDpcS5pXIF+9tJu1dJ1pL/6em6etf9J6b/QXT" + "TdNpPTa9P//61aXWl/63/q6Wl/1/pdV19/XQ//dft039Vv9Nj03/7evv+l7vuk9Nr9j+6ujC" + "aTpaQqvZBp4gQ/q/6Qpa///hL///S6v/q/7SvSrcINP6t9////9Bf/97/3+2vTdN06SSWw0D" + "S8RIHgrU9f///0v/1//ZQDX/3XVj9IILDDQa+QyAatP15OJ+v+L//39fmoMz91dfTdbegpsg" + "0vIZAZlDd/8ff/3////sH+66tpPqkw8JJBivCPj//KgDF0v+eT////2/+nr3fSb0kga8IF//" + "PBt7S/9v//+vwb/bW1bq7SSfqg16X1/7df+3////hv/r9b9K/wwvS//9hhL/2////8g2Eft+" + "l0rdfq0P/93+GIX/t//39X5BUn/pN7ejG9Uvx9L0v/Bgv/b//61/D/39apNvSX//7v3y4bf/" + "b//f6/IZkP+6Wr0t2qX//1d74S/9v//X2//19+1Sf0iGH//XX1vS/9v/+60v//bWK6Stuv//" + "+m219f/b//f1tfrtetjpvVJf/sLpdq3r/7f/9pXTa/+sbWk2xSX/9owtoJttGFhkHfT/9v/9" + "urQTa9pWSHbRftbS+lX/thBcNJhkOOwwlsMJcqwyv+w//20rCUMI2v9pwwl9aTbS//xXDCCb" + "CsQrY1hra6sGtrrbaUNBMQtbSYpit/VvSBf/ak4ZzCFMbUkBsRDPU2QYrlAOawzyBFbkHrIP" + "WauUA5rCFAbGlEINZAhPDCCpqUBmp2gSg9D/9hdNNBrDC2//YXX+xCnkmF8U01vXTtf/hoaB" + "hAwQaEMEIOIjiyOGQCwCwwgYWIMEGgwh/EccfoREREI2CPEcRxEREa/wgg///hJ/6/0n/X/I" + "HiiX///pdfT+n/+tpePX9fhfX1bSyB49NKvptL7/1IHg1wEYA1CxdKvSEdtJLyBmDU/2l/YS" + "2wkl8eGGEEQPDXcJBBhBpYYhMQgQMgUVwsGaAeCsF7WIiI//////////////////////////" + "////////wAQAQA4AAAEDAAEAAADOBwAAAQEDAAEAAAB3AgAAAgEDAAEAAAABAAAAAwEDAAEA" + "AAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAAFQEDAAEAAAABAAAA" + "FgEDAAEAAAB3AgAAFwEEAAEAAABoDwAAGgEFAAEAAAAeEAAAGwEFAAEAAAAmEAAAHAEDAAEA" + "AAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; + +static const char fontdata_18[] = + "SUkqAEARAAAmoCq/////////////////////////////////+QyXe5DJDVchncgthMyB4NFk" + "TMgeJBA8FKE06yB9ad5DbxIgScCpNkFYdSGnQgrOQbKENqhA3ghmWQz2QVRyBxZDMoQbJ4XU" + "g0YQl4IHhBhUm4OggwoIGCBhYwQZBuJggYIHhf1CJwazjaSdJNpqEGFQaYWgSwmg9d6yGanQ" + "Qb10m+gjxMKEGEGlhAtHhhhHyf/4QaVpIw3rZBpelQQbCpp+EFSCDaCDe/XSOMwbSfSDpJN3" + "1TYKqMyraC0nQTfT/pYaW0gv06dKk4Iz8+K4Qb9BUk2k/+ugt9+npbTXVtBcINwnWYAnTNg3" + "77f1+v1aS+k3dVXC6em0YXSqkrSv99UvX/S8N6q3C6dJ/0tLff/r9/S6pJsN0RB6rH2/S9JL" + "XX/r0v//WwxRfqt6XvXpaX1fv9f+lqlThjrf+tfS//////6pbdf910vrS9X+tdf9LSWsKQ1L" + "pfr/9fuv/f/H+1rcIzH+v8aS9LS17/yGwU96S0klsEf2OvePpfj3X+I/2v0l4Svr/S/SStf3" + "/9JwkwqevXf9L+m6/rvdLihW6vpfpL8jmktpf5A8WyEu+6She16/rSXggekwwgr/XvbXr2E3" + "1/SSXgmpHPYaWv+3tpJNEGt/H1/0viHhBpMU3/SyGoYhaZCg/v/0gSS7oINWtf9ifDVCSC6T" + "/X+Qa9Pnx1Tph3/0yEAkpr3Ba/pQaXBBvTUMNtf9hpI4mD+PX9JpdBvRwnV2F/2HhBOQxhFj" + "9f1mBh/TxBB0gjiDYYRxL1wbSIP6Tdj/X9QgbSxdJtQgg2xX/kNVPhA6t/1/SF/unpINwvel" + "DMbSCOG1b/IM4vX6TaW9OqQOGvogzCmD6TaT/5BSEL+k6X7aSTbSX+G0km+/9f+r/r0nDBf+" + "/TpN/9V1paWRjv6VNj/+kt7deQPBe9f0nQj99XS8geWEJe/6t/61fpf+lv8Kv0qb7S/FX9LX" + "9a6x1/X2/+v0v/pXX/0v20v110t69fr6IZcf/vv9daVENXt+vX/9L7bS6/9SGo//S9Ja/1/s" + "Va6t+ldZAu6V6X1/+l9tf11069//pZA9RX6/T/5BWhddK63p0krSSwnr9JOtsP8hteQLMpv0" + "tLtL1fS7/177Yb1qmvWk3X7aXaSWv/SXTcHr0nTa6IGBDXbSvSeku+r0ttJhhFIBO/XXS1tL" + "tJ0u1CXroPSSdWwj3f/ptJNbQSbaTaSTaQQXXw8JbDCCiE1117DSIZsBBlusMJENSAg4YQTD" + "CSsMIIILetkNQgkmKDQYVr00rFRGrEKJrDOKYqmKCC2FqDEVgwmEGnodqmvhbCYShhAgsREh" + "phpCDBBr0hUMIRBhCGCDCEMKCyGScCItpdCIiI/6S+9VX2uvvSX9qlXvWuraSVYtpUO9Jdqw" + "ttIdtLbS2mFiP///////////////////////////////////+QEwate8gaApwQPArqIZINtZ" + "A1DU1IHgpaEDwZCCC3wQPA4jyC2DJMgpIyG0BkFdQQUCZDUKSBAuQ1CZAuDIYBkFwZDAMguD" + "IYDIMAyGpqQ19CGNCGYnINYMg1DRBUBkGsbBEYDchgQQwwQYLlOGCIBc1A5GAxIEVENnkMqZ" + "BihBp4hoK5OB4KJBbBU1IbYxODBEaDORcGpYIH//63hA4MIHBhBxX/+QXDUJrwsIMIPDYRoC" + "MMGHDhGgOGGCDBBggYQYLDBBuEHDCCMw4YYYRmHYYaPBA3DBBhMEGEwSYYV9112EGpBXoQiA" + "gQNEDFCD6EGs5B6EDByCUCBA5AxQhmUIZtCGbQhmUR8Gg0BSonDMZBrwIEDZBqOQiAgQMMg1" + "nIQgIINZBvQg0YDNAzQgYLIKMdHAb0p8C69MIoCB3QdBB6IaEMIhsthEM9oIGmmmmmqaurpB" + "NsIG0E2wgbSDzAOZg9qnp9yGUdIN1BV84NozBh7hsJB3BvfdBP/6W6tukGHhtIMPDaT/7v6a" + "Qbw2lST8INhAgbT3QT7f+En/9LvvSb2wk3vXX/dfuk/pekHSb+0vDf+k//pevpP9P6T9P/uv" + "aVpf/aCDae6Xt/6X/4QX96Te2k3t//7pd09OlpfTpN/r7f/X/6XazCek36Tfpf/3+rat1per" + "03ML9Vv/r/+lzCza7et7re//+62vaTrpeOk636X3/pf/pfaXt9P9P///dPe9XX/pXT+vt44/" + "/6Xv/et+t+uOP/zCpNpNpCl5DIBocgeDj9Ot/Q/6Q/+l917+/7/j+6Wt+rpJeFh6RDH3T9Lt" + "+v/pe6+3pfpf/9/vt1el4WDrCDNzrev/1/6X3Xt/f99f7q2l1pOlXhYapBH2//qv//9L3X2/" + "fpX1v+6+1bStJfCw60E6t0/r+QIfIEOv/oL7r719f6//6fb30vIM9kM2wQQYaSQSf1vS9v//" + "/S919vr+vW/3S1/SbSS9BoMIIMNLqK6fSr/+v/S+/3++u+r/vvum0ukvVMIEGGuvreEF////" + "pe6Xt++kr6TD/enp16bX+8IIMOtL6fBBL+uq/+l9/t6+v6UH/a6+x2kl/hBBg0kl9eIL////" + "S/S+/9L8JMpwZf/03tPSX1hBEGsXDDS6r7lQGX/XXj/S9v9v/S/BBSoDb+6WvVWqXu0EFgw0" + "Fr6Twf8cf/p/pe/+EvzaMH/e36tulX1ggsMNLr+58IH///F+3+3/k9P4IMP966b9ul9oMIIL" + "DDS+kqfIPT/+fBo/f6Xt/8L9oN/669NxpeIhBCGGlqut7f8+GM+GP/z6fv/f+wX7YN/39Wqb" + "SXhAgw6/6fb/w+H//b+69v/H9sgyDP3SbV/0vCCDBpdaS+39f//7fuvt//7kDjT+//Ta/CCD" + "aXpfe////+w/uvfuu67B/3rq6Rvel8IP/0k/d6773/4b919v//bDf+urql6Xwgem/WL3f/9y" + "BhP/7f3X3v7+2/7f1+3pfDC/qkv/X/3/7fuvt+v+7/9Nq3SX18ML71V/f445Bqp/7f3Xt/Xp" + "dv/a/6Tevhhggm71WbBv/1+w//t+6+/f37f+9daq3peGGEFvouqSX79e//7f3+3v7+2/+urf" + "XpeGGEE3bfpd/f///t/pff19bf+3TddJN6XgwZDXFwghd0nCST++l9//t+3+32va/+l+uq3/" + "hh4QVthKIRPO+r/3//byBiPS9vet62yGU9f2vt6T6RDKjww8IJtimkkn1b0sgXjv/9v9v9vt" + "Lutv+9666pvS/DDwQLDCYKv/0v2//2/4r32/ddv911Y2Nb0vwwxCCbEV76b0utv/9v9/vdLt" + "L7S7pivSfr8gz2Qa4DDCCIMsNpJPq3r///t6XXt91t1t/69aV6/pkMzwwYQSDYaXfTelrt//" + "t/v7G2ltpbeltpe1vpV9qDDCCVsJK+kwyGaPpft//t/17tpbaW312rVaTekvrQYYIJNhpVeE" + "mwuEF+w//2/3920ttL7S20t9K9L+8MNBbDCS+kwYSUJa7f/7D16/bS20tu0uDBfaV9Jf0g8F" + "sGEltQSYYLZB0KFTDDVNNPTbUFTdNN2GEk2GEk24YSTYpqtK01C/WHhYYhINNCEGKCEJDTDB" + "gqaaem2hp6abhhhBJsGEEmw0GEk2v1YacfvQeE0GEQzKyGcQEEGEECIahQQIBhiCINwZDAMg" + "uDyCgGwYUgwnaIOIIYTuxCINQMMUQagcOIRDUBhhbuCTBkMwGvsLDwg0GF8hAZQUgQNnDJMM" + "r9b1/uGFhgtwwthNVVf+KQiIMEIiDBCIgynDIBRERGhFwYISBPqIkNGogwX4iI/QiOIiQPBZ" + "cRER9cIzA2gb+qCB/WEH60g/rQNdJJGxlOMjgbQaOpAmg2D1CBj+oaQg1egoogahpaXjQMKa" + "gPBjRA1Dbv4Q1sIcf////////////////////////////////////IFmpA0AkvUhkhlQQPAr" + "gIMgeFsgeBxBAkggTA0rIa9kNe/IZAJBA8vIHgT+FIHjmQPBS8gyeEth5A9HIa2BIgfkEDwU" + "iFIHxBA8FUghtkJbeHtL/IElkMu/S291UgeL5A8Cf0YOQPHNNMhleQZP/+6d0g4dNN3rfpV9" + "If/Xv00vX/yBoGoP/84n+l7+v/q6/7VeC//V167CDXhf/V1/xH//6tf/3/9J69f//9tf///0" + "v////2////9LX////br///9LQIg2UyE8gqTIa08hqzyDZTIsyGu5BofyDU5F8hiZDVgizIbZ" + "yGs5EmQVqEVyGa5F4hlnyGlBDZBA8G1yDJhA8NQC/9W2CBB4IGCBhA8EDwQPCBgpDK7ILzwU" + "IGEGChAwQMFCBhA1BQoI1hlQUFIaE5F0IOnUgmpBc5BjyDFZE0//rEIPCYQYQwnNQGHpkMs5" + "BufXIFzhNMhlnTCDIGDhMIZDYdQuSsgwcg5/77///VtPQYUI2OEeGwoR4iIDRTChdAsKmmFT" + "CPjYVBhHBYVUcFhDBQUgXoQI4hsopBjg2jYFzQCDwNzYLr/6sI4L0wjwsIIHQQbBUEGsJ/oL" + "008JoIMPTQQa1QQcF9qmug6TtV/+k9BvRwdBBoJB0m9J9HBshl+QMfoLIZfo4VHCZDL9HBtB" + "NkC/o4NhBByBj1UOkQ2eQY+6bvbV/S//bp9Ag2gg6TpWQL8k1EJ3fS2IQYhOxCdJ3QIG0ndG" + "3rmI8DTd/Wm60qS/9Lp4p10nSfVngzQg/6XCYTwg6txTpcQnVx/6e90m66//b7ptL3W+rq/0" + "F003V03Ta9Nf+2utWlrpf+l709Ol/77/hL/3Xp66f/+r1bS61//bHq1/SfXr/S6p69utfHH9" + "906em8UktshpYQ2P6X7+v/+/6Xv7pe2vV/2lzH7S1S/CBw/2/0hr7//9Bf/7/X3/9+k2r1SW" + "2g0vS//1///CX//e/8H+2lpXvVqkltoNLxEgeBORvr/9f+v/6//shhp/991iulBAsGDRww18" + "hkArV+vx9//F//7+vynBo+3WldNN6QMPBAkgwYS8ETv/9el/3////sP/W999JJh6SQYrwjMX" + "/8pAy7f/zyf///+w//dLSt+m9Kg16Bf/z4K+0v/b////w3+3X3vapfpA16X1/7aX/t///1+D" + "f+tq9JvSvpYYXpf/+wwgv/b////wb/bp6tbvSv2h9L+7/Yr/2//7/vyCuR/r/oxvSS/H/1/8" + "ML/2//+tL8H/erSel36/9L6/vBgv/b//f/8P/a/apN2kl//+22r6MArf9v//+n8g1O//TpPS" + "Tb6//+l/4S/9v/+0tb//Vtb7VK70iC8f/r1drel/7f//9f/69R/vpJf//q2+3pf+3/+3Wtr9" + "er7T0k231//YXS7X1/9v//StJtf+1j40rikq//RhbQTDbRhWyBA+n/7f/7faCte0rtG1qnSb" + "df/20uGk3YS4YXv/2//20oaTYRtf7Juwwgt3pXpJf/sILgwgmGQsMMILYYXlIGZ/2H/+2lDB" + "BMMJfDSjiuvSt0l/9irMPFMUxVsUnDTtPTYaaYTtO2GEopiFoM0WEmqTenVukP/6jmEnakgC" + "6IaE7DIMDJIDBThokNCdyC6cgunLGSGCEYHJANxChMLIaBEQqakMGn0CTB6/+wtIMINBrYLf" + "/2F/+wp5INegmmFtNPW//4NDQMIMEDQgwhBsRHEMjhkhAsGEDCxBhBoGEP4jQ4/iIiIhHATi" + "OI4iIiP/hBA3/X+Eg///pf9f6T/r/ZA8OLf//+l16T1///a6/S+k9aj//a5Arq0q9JtL//wY" + "YQLdtKvSEcdV8gaIddtKvTS20kvj2GEkvrYYIIgeCsOEggyB6sIOtiFFEFsGRPE6AZgsLDCY" + "XsFkDYDScREcf////////////////////////////////////4AIAIAADgAAAQMAAQAAAIEI" + "AAABAQMAAQAAAIsCAAACAQMAAQAAAAEAAAADAQMAAQAAAAQAAAAGAQMAAQAAAAEAAAARAQQA" + "AQAAAAgAAAASAQMAAQAAAAEAAAAVAQMAAQAAAAEAAAAWAQMAAQAAAIsCAAAXAQQAAQAAADcR" + "AAAaAQUAAQAAAO4RAAAbAQUAAQAAAPYRAAAcAQMAAQAAAAEAAAAoAQMAAQAAAAIAAAAAAAAA" + "AADAEgAABAAAAMASAAAEAA=="; + +static const char fontdata_20[] = + "SUkqABATAAAmoDgf////////////////////////////+QyQy7IGwGXPIZILLkNA/kDwVrIW" + "3IHgvBA8FqE00sgeC9pp5BWhIFSvIHhpOQPDToQK3ILYb01TTINOELmCJwypBY8FVsgy2kQ1" + "6BSCocEDBSDQBEFfCBcWINJwQeF/qDCDSCD0m4eCBhSDZWEGFwTwQMIPC1VKQa6keMPTpJu8" + "IMKEGmuECwg0fIP3dcIGgg2kE9JukkeGwqDQaWECwj42EEG//wiRhpN6ON0lZDSetBBsFTXw" + "gqQQNoJv/9HnJetpIJ1201SSbCpo0JroLQTdP/+EFh6b1ekm060mwRp5mNwQb8JUrat//1uu" + "kk+laeklhBaBA6QdZsCsKcAwqdK/qukv3/pXuvbgum4TaMLpUq3T7u9KltVaS61bfpcLp6/p" + "Kqp1vr1/1/qlcNpJK2wvfdL0tf3//X/S+qsGMjvrHpuuvS6XS//6//SVWw0c6X/q6+lr/f//" + "/pfXhj1b/9L0uv3+mvX/9JJLyGtiX9PX+uvr+/VePpf7YR9f/XGkvqquv/1X+u0klwUi3pL/" + "/66Wvf+Qbi/uqWklVhGaY/Xj6XpD3X+I/37pVuCT/36SX6Vpf1/0lpQl6vS/qv0lbr/720u0" + "h7hX+/6XkWfVpfv+6pRVbXpL+kvggaqw0r/IHgmELd7aWwldq//SSrwg6qGEtfpW9hoJKu2v" + "S/0kvCakWisNL/Xg8MV5DNp43/9IL4h4QaTEJv/4wwSSkEUf6X6gklrhB0+v+yXBmprW//0Q" + "06l5mK0k1DDv/5JgUIJrvBf+lB1wgbwnTDbX/Yejyh1sdL/tLhPo8TW9fqgw0kmyC/Eu//0k" + "cGH9NxBA2kEGwwjaX6hvIYmleP6X6UEG0tp0n0cbYYS+lyGqESCDSd//+hbXF4TaQSBuK/8h" + "tJ8I8w0m/9L9J673VINsL3+YbSQQbSv8gflZA4Cf6TaW9dJBsNJeQPyjD6Te3//+k/+2lTtf" + "+G0ltW/+kv0rS+vScGC/9+nr/1+utcijv6STY1/6S3t1/SX6vEfuu6/7/q3+QPDZi7fpa/0r" + "/RA8PhC56VN7a/il+lr+tdY/+vt1//pf+krr/6X9/6WulvX/1//+2k9f1pUQ1/30vS6ohmv6" + "X22tr/6RBsH//9dV+v9uv0m/SC110vVf/pfsV/9aTrkDELel6X//9tfpfpXX06/pZA8Hj/SS" + "6d+sgy3uul+39JWklhB//utsN/BSB4b4lddNpeler6X1rpJX3DtcKn/pdbaTpVpJaa1f/TcP" + "+k0m0tItXaX16t0l+vpJOkw0GQg71r7+h2l2k2lVqEv+Hpba2EeRrn/S9patpJthJulbSQX/" + "D0km0mdWmKtfdJtJNYYSuGEmwkraQS/4eEttBMQg09de2kQaUiygwiGuTk5ptKmGEEEFtexE" + "JJimgwv+kmDBLWK2ITBgkrBgkCC2qwYWDBBhBp0hSDWGIXwuExCoMQgQLERIauiQhggwr8Ug" + "whEGCEMEGEIYUFkMkMyMgeC/EMu+qwZwMgMo4B4axwDg8B4axwG0LxEW1SxEREfetL9Uv26p" + "L9JfVvpL20kv+qSxbSUd1S26YW2kO2ltpb1tNYj/////////////////////////////////" + "5AWBqr/IGoNMEMgk5DJBaGEDYGKyB4FlZA8CjQgtgYghkJ/IGYMUIHCLIKgkCKsC5QCHQNcl" + "oaCWBqlIGYVAMFWGCoBcqwXKgCDoDBUBrmoFQ1Bg6g1EQGmVYaREwZCrDSIoCqDIGKEDGhDC" + "ghnGEMAyBCchgvZDRWQUYQzaENEyGlMhsBfBAyCuMWRYFYjYaBKcNPQIiYF//9reEHIuG0HB" + "hA4r//IMEZBcOuaAeGnCDBA+wg4Pwgf//94QcMIOGEH/+uE1tetMIOyDIKBFAPIKCwhpi5DK" + "FQigGEQzCCGaQQzCCGaQQzCEaBmkgFNokBlshpzCJwcMhpaEQgInBwyGloRA0fD8g3IINPkD" + "CCDTMgoRkDE7R8NDqvTCJAQNQzMEYQIhsoGZgQ2aBns0BiEaAXtNNNNIJp6baQIG2aAu0CBt" + "mgLsIIPtNO01YaIGEUQMISnwy9do0Ah3g6CD5BQG5BRbkG9oIO00000gmm0naCCDDcNhBBhu" + "G6DzYYNAxap91oNpN1BV84Kwggw120EHwb38JB//S/vSb20m90nrf+vVtJtKkvQQbQQbvcJN" + "Yb/0E//pdrekG9tIN7aT7/v970/pfToIN+2gvb/pL/9L+9Jv0m/S//tLWm1bS/90m19L2/9J" + "//S770n3Sb2+v/fb/SetL0m0E97r7f+l/+l6za9X3X6//3Xat02lpf6TaNr9e3/1/+lzac2u" + "3pN7pN71//rrTaT1pfT0336Xv/r/+l+v3q/q///71362vXjq6T+vt44pf/pe3Xt9X9X6v/9q" + "2u3Tpa/pX39ff//6C/X29X9X/Ecff5tV0m6FLyGQCm5A8ND9Pv0kO/pD/6XvXv9/3//1902l" + "apeFg9SGKE/tL////0vtfb0n9J//7XSfb3pLwsOqCe+3qvfr/6XvXt/fXfX+9P/pWlXhYapB" + "GZv0vqt/r/0vtfv//1v+1vbSbSdV8Fh1oJtb76X///S969vV9Vfr/f0v6bpLyB4eyDVYSDDS" + "0unSfS/yGeMhnhf/S+19+vqvW/+v9tdJL0GEwggw9JJ//S+///9L3r2/v++r/tdJtXSbWvWw" + "gQYaWsfTelW//X/pfa+3/pfpMP96b+rrpL7TCCDBpaXr4QS////oL+vvV/V9KH/f/sU2kv+E" + "EGGlpV7eCC//1/9L2/2/9L9JmoFn7paTf6pf4QQYaSX6XEwGn//4/0v0vf/S/CCkICn//0mm" + "6S/wQRDTKwYaWl/cgQZn8Lhf/X2/2/8JfkKdlICt+1dJvabSr6aCCww6+tJ4P+OP/0/0vb/y" + "xwQX5tWD/er/v196CCww0tf+Zh3///F+3/f+1/BMP+//bbSS9hBoILDDS6VJJvBh//zMGv9/" + "pe3/wX7Qb/dLSbS40vEQgWGDS//w3///37f7/8F+2Df/v/bVeEEIYaWte3hv6mYEZmBH/5nv" + "9fb/x/bIKYT9tbX09Lwggw0v0kvf/ff/9v3r2//9yCwn/66bSV18EEDDrpf+////9v7X77ru" + "tsgtB///zadJfCDaX9JN73r/3/7fvXt//9h/20tL0ndL4Qer0lj7fr/yGZZBI/9v7X3//22/" + "/fb1vr4Qff6T3//3i//b969vf39sP+2trpJN6XwwX/X/////2/tfb9L0t//rp+vX4YX3SSv7" + "6xxshpI/9h+9e339/b//96t6XhhhBPeqSNgY++v4f/2/v+//9v/bSdLpJvrwwYQVu3LrX/fX" + "u//t+6XvvW9bb//e2qXpeGGEE3elqrf+v3/+39/t9r37/7df+3peGGQ2ysIJp20sIKv2+v//" + "2/0vb+vS7/9dL0vXww8ECG2wk4SW+vX2//w37f7e37+3Xpe+2kk3peGHhBOGKiieU/t9ZDLj" + "//28hmR6X36XaW2Qy4+9tLS9W+iDRHhg8IKwwmEq3revW3/+3+3+3t+3W///G/Xpfgw8IJsW" + "Cqn6b0tdv/9vXivfdL/vrdW640m9fwwxCCbFL9X0v7//b639vuttLb17qOqW9L8geHshp2GG" + "CCIG92q31D0v2//2/69vbS20tvr13dJ9L9MFDDCCCbaVPq3pft//t/v8baXpbeu3tV031+mE" + "gwwgVtpLek3hL9v/9v/920tuvtLtL6W9JfvDBhBK2Et9JhkM2NLXb//b12ve0thpbd1tha3S" + "fX/wwwgrYYSSvCTDCWCX7D//b//dhpbYS27CXaTtaV6S/pA6Cwwwl+CQYMElIEB6Ww17/bVd" + "rtw2Ethpd2lsMJf7ekF/w8LYMIJO0ITIwXEJDTDBqmqemw409Ndgwgkwwwgk24MJJs1DTW0k" + "2mh/0HhUGQyQIGCDoMUEECkNCgM1iyGxQQYVkMKMgXUMMJAgbQIiAzg3ZqJEGpQwYJEGpQw5" + "0JENRWxXWCwyDWo/aw8JoMQgQMhsBggQYSBAuGJIAxrrYYUgQIwUhgQ4YhYYhbDQhbX4SYev" + "sJIPBNBgvkmBwCkaBU4ZAgzf+/v7hgsMLcGFhhU71/40IiDCERILIgREgsCBEcREaEQ4MEJA" + "sTiIMEIME9UIiP5BbBq8hkhnmQWy/EgeDIOQUuIZAuEDwZuIHgz0ER9IRHEWZgSgVf0ED9LQ" + "f1QQP0tB/pI5g/SpBEQuSe+iByA3HqEDH6UMKINfUUQNgJXfCoGFNYHhlxhAwvogbAzB/hCO" + "P/////////////////////////////////+QPFrIGoF8IHYG3PIZIbdkMg4CZA8CIIZAb2QJ" + "7IE0NbMg255DIAw5A8PMhkLwpA8H8geBZMgpzCCww/CkDy0IHgVhqQPFDIHgcaEFXRLbyB4L" + "0INvL/kFiyDRnpbe6aXrhNP17+0iB4PMhkL0bKQPB/tMhlTIKc//XfSD67vW/2l6Q/+vfqvr" + "///zyf6W7XkDUo/9LS/6/C//Ta+uGgwvBf+lev8R//+rX/+v/V/1///q1////V////9K0v//" + "/9v////S/////bX///9LnA2ycNKQkyDLMg2/kG2eQ26ZEoQ2oINT+Q0nIkyDEyDa5EoQVzkN" + "qCEmQZTkRyDTBF4hmoyGq5Aw5A8FNyBxf/03BAg8EDIbWoIHggeQ19cIGFBcgwfBQgYQMFCB" + "ggYKCBhA1ChMEU4ZkFBSGwQRKiC4yyD1EGEENHkFxhEqIaBv/1cQg8JhBhDCDwg9Mg1UIGKa" + "4KmmQaKJhAwVBhDIGKKE8g0UIEU9V7X//6sJ6DChQjw2ERAFzxEGGygyBjQho00CyCgMJpkD" + "AaDCPjZAvog1IaNFR4WiMB5BNCBhBDP5BQjIEUtHAUEgGAX/6ujYr0wjwsI8LCCDYSQQfX9B" + "emE9UEG9MI8L1QQcL7TTSYaIGDpMINTYEL/6sJA+jg2gg4QQOk2FSawjxP9BejxtHieEeJpB" + "h6ODaBA16QcF96fp96SS/9XpvhB0EHQQdJvSfQINkM2ZDKnoLIZvoEHQTZDLmgQbSchlzQIN" + "hIOQy5qr5DRMhnn/e2ldVX/1e+kG0nSdLIZXqzMGgU7vpcUxCdinSbdIOk7o2eshl+7uk602" + "k/pf+r08QnWldW9XhP+lwmnhOtxCbS4hPv/ff9aWv/pX9Wk6T19XTf6XTTdN03TpdOv/06aT" + "aT+l/+33ul1rf//oL/9fbX6/9tf20rS6/9LF61169Xr/S6rr2666HH/rTp0nxSS2yGpxAu/7" + "ft/6v/v+l7vul7a/f77c2q33SX4Qafpf9Cl///4QX/+//V/2vXulqqW6DX6b/X///9L///S+" + "H/1q2k2k9JJbaBpeIgu///X/pf/97/sH+2rdfFWtILDDQa+QyQ31/XX//9P/9f/5qDU/1rdN" + "PpJbaPEQzXkMgCsR6/H3/8f/+/r7IgGn7/Tq1dJBh4QVBivBFB///S/7////2H/aVr16qw8J" + "JBrwjQT/8gYZt6/59P////Yf7703tvST6SQNegX/8zBT2l/7f//9fhv+6/Sbtav6DXhBfX/t" + "pf+3////hv/XSvTekn6UGF6X//tpf+3////g3+9N7/6V6tD//d/sMJf+3///35Bk0/tddJNt" + "V/H0v//Yhf+3//daX5AkR+/03o3vpL//0v28ML/2////8H/tK10km7Wv/S/d/wzCBf+3/+/1" + "+/771elvSX//1d/pD/7f//WrfkGuP+0tX1b9IgRH//r63hL/2//7Xr/f1+/SpttJf/9em730" + "v/b//f1v/7V6er0r9L//XSttb1/9v//rptfr+1jY0ntUq/+197X1/9v/+0rS1/9XqqVtiqX/" + "9bQVtowrZDYPr/7D//b7SYa9pW2jCxu6V6//thG/aTdhLbC9/+3/+6VoK1/yKPYS3rSbaS//" + "hhLhhJhkMOwwlw15AgaP+3//aVpMMEc/aVhOGEuulfS//iFoMIJhhWKthhJYd2urBra922lB" + "ggmK1tJimK/eraSBf/asqIpgwVqGwYJBA2GCDhhA8IG2EDBAwQODBBw2GlFMLCKcDDCVVBu1" + "hJh6H/7Cjm0ExQanQMoUQ2F7IEF50DBLhokNghyGC5DBcEDnQHIgEOgFwYIKE1kNgOUGE0wo" + "OtoKw9f/a6DQaDC2Cw//sF/+xCn0gwvimg1vXW0//hhDQMIMEDQgwhDYiOLLhkhlBYMEDCxD" + "BBoMEO+I44/iIkCThILGBHgTiQyQaOxHIHh+EREa/xFBEM58fH/sIJ/6/wk///pP+l/kDwIK" + "////X/3r+uvStf///8ev0nrC+v9pZA8WvSr0g2l//7aXtpfqJTgi4GwGeP16QjtpJeQNAanX" + "tL+0tsJJehWwwgklX2GEgkmg6wxCBIgZiCCB+DrDChfCyBtDOdkcDMMcMLEREf//////////" + "////////////////////4AIAIAAOAAABAwABAAAATAkAAAEBAwABAAAAcwIAAAIBAwABAAAA" + "AQAAAAMBAwABAAAABAAAAAYBAwABAAAAAQAAABEBBAABAAAACAAAABIBAwABAAAAAQAAABUB" + "AwABAAAAAQAAABYBAwABAAAAcwIAABcBBAABAAAABxMAABoBBQABAAAAvhMAABsBBQABAAAA" + "xhMAABwBAwABAAAAAQAAACgBAwABAAAAAgAAAAAAAAAAAMASAAAEAAAAwBIAAAQA"; + +#endif /* LEPTONICA_BMFDATA_H */ + + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmp.h b/hgdriver/3rdparty/hgOCR/leptonica/bmp.h new file mode 100644 index 0000000..568c990 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmp.h @@ -0,0 +1,124 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_BMP_H +#define LEPTONICA_BMP_H + +/*! + * \file bmp.h + * + *
+ * This file is here to describe the fields in the header of
+ * the BMP file.  These fields are not used directly in Leptonica.
+ * The only thing we use are the sizes of these two headers.
+ * Furthermore, because of potential namespace conflicts with
+ * the typedefs and defined sizes, we have changed the names
+ * to protect anyone who may also need to use the original definitions.
+ * Thanks to J. D. Bryan for pointing out the potential problems when
+ * developing on Win32 compatible systems.
+ * 
+ */ + +/*-------------------------------------------------------------* + * BMP file header * + *-------------------------------------------------------------*/ + +/*! BMP file header + * + * Notes: + * (1) The bfSize field is stored as a 32 bit integer and includes + * the size of the BMP_FileHeader, BMP_InfoHeader, the color + * table (if any), and the size of the DIB bits. + * (2) The bfOffBits field is also stored as a 32 bit integer and + * contains the absolute offset in bytes of the image data + * in this file. Some bmp files have additional data after the + * BMP_InfoHeader and before the color table (if it exists). + * However, enabling reading of these files makes the reader + * vulnerable to various malware attacks. Therefore we do not + * read bmp files with extra data, and require that the size + * of the color table in bytes is + * offset - sizeof(BMP_FileHeader) - sizeof(BMP_InfoHeader) + * (3) Use arrays of l_uint8[] to make an endianness agnostic + * access to the BMP_FileHeader easier. + */ +struct BMP_FileHeader +{ + l_uint8 bfType[2]; /*!< file type; must be "BM" */ + l_uint8 bfSize[4]; /*!< length of the file; + sizeof(BMP_FileHeader) + + sizeof(BMP_InfoHeader) + + size of optional extra data + + size of color table + + size of DIB bits */ + l_uint8 bfReserved1[2]; /*!< don't care (set to 0) */ + l_uint8 bfReserved2[2]; /*!< don't care (set to 0) */ + l_uint8 bfOffBits[4]; /*!< offset from beginning of file */ +}; +typedef struct BMP_FileHeader BMP_FH; + +/*! Number of bytes in a BMP file header */ +#define BMP_FHBYTES sizeof(BMP_FH) + + +/*-------------------------------------------------------------* + * BMP info header * + *-------------------------------------------------------------*/ + +/*! BMP info header */ +struct BMP_InfoHeader +{ + l_int32 biSize; /*!< size of the BMP_InfoHeader struct */ + l_int32 biWidth; /*!< bitmap width in pixels */ + l_int32 biHeight; /*!< bitmap height in pixels */ + l_int16 biPlanes; /*!< number of bitmap planes */ + l_int16 biBitCount; /*!< number of bits per pixel */ + l_int32 biCompression; /*!< compress format (0 == uncompressed) */ + l_int32 biSizeImage; /*!< size of image in bytes */ + l_int32 biXPelsPerMeter; /*!< pixels per meter in x direction */ + l_int32 biYPelsPerMeter; /*!< pixels per meter in y direction */ + l_int32 biClrUsed; /*!< number of colors used */ + l_int32 biClrImportant; /*!< number of important colors used */ +}; +typedef struct BMP_InfoHeader BMP_IH; + +/*! Number of bytes in a BMP info header */ +#define BMP_IHBYTES sizeof(BMP_IH) + + +/*-------------------------------------------------------------* + * Align BMP headers on 4 byte boundaries * + *-------------------------------------------------------------*/ + +/*! BMP_IH is misaligned, causing crashes on some big-endians. + * A packed struct forces alignment. */ +#if defined(__GNUC__) +typedef struct __attribute__((__packed__)) { + BMP_FH bmpfh; + BMP_IH bmpih; +} BMP_HEADER; +#endif + +#endif /* LEPTONICA_BMP_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmpio.c b/hgdriver/3rdparty/hgOCR/leptonica/bmpio.c new file mode 100644 index 0000000..04efabb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmpio.c @@ -0,0 +1,602 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bmpio.c + *
+ *
+ *      Read bmp
+ *           PIX          *pixReadStreamBmp()
+ *           PIX          *pixReadMemBmp()
+ *
+ *      Write bmp
+ *           l_int32       pixWriteStreamBmp()
+ *           l_int32       pixWriteMemBmp()
+ *
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" +#include "bmp.h" + +/* --------------------------------------------*/ +#if USE_BMPIO /* defined in environ.h */ +/* --------------------------------------------*/ + + /* Here we're setting the pixel value 0 to white (255) and the + * value 1 to black (0). This is the convention for grayscale, but + * the opposite of the convention for 1 bpp, where 0 is white + * and 1 is black. Both colormap entries are opaque (alpha = 255) */ +RGBA_QUAD bwmap[2] = { {255,255,255,255}, {0,0,0,255} }; + + /* Colormap size limit */ +static const l_int32 L_MAX_ALLOWED_NUM_COLORS = 256; + + /* Image dimension limits */ +static const l_int32 L_MAX_ALLOWED_WIDTH = 1000000; +static const l_int32 L_MAX_ALLOWED_HEIGHT = 1000000; +static const l_int64 L_MAX_ALLOWED_PIXELS = 400000000LL; +static const l_int32 L_MAX_ALLOWED_RES = 10000000; /* pixels/meter */ + +#ifndef NO_CONSOLE_IO +#define DEBUG 0 +#endif /* ~NO_CONSOLE_IO */ + +/*--------------------------------------------------------------* + * Read bmp * + *--------------------------------------------------------------*/ +/*! + * \brief pixReadStreamBmp() + * + * \param[in] fp file stream opened for read + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) Here are references on the bmp file format:
+ *          http://en.wikipedia.org/wiki/BMP_file_format
+ *          http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
+ * 
+ */ +PIX * +pixReadStreamBmp(FILE *fp) +{ +l_uint8 *data; +size_t size; +PIX *pix; + + PROCNAME("pixReadStreamBmp"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + + /* Read data from file and decode into Y,U,V arrays */ + rewind(fp); + if ((data = l_binaryReadStream(fp, &size)) == NULL) + return (PIX *)ERROR_PTR("data not read", procName, NULL); + + pix = pixReadMemBmp(data, size); + LEPT_FREE(data); + return pix; +} + + +/*! + * \brief pixReadMemBmp() + * + * \param[in] cdata bmp data + * \param[in] size number of bytes of bmp-formatted data + * \return pix, or NULL on error + */ +PIX * +pixReadMemBmp(const l_uint8 *cdata, + size_t size) +{ +l_uint8 pel[4]; +l_uint8 *cmapBuf, *fdata, *data; +l_int16 bftype, depth, d; +l_int32 offset, width, height, height_neg, xres, yres, compression, imagebytes; +l_int32 cmapbytes, cmapEntries; +l_int32 fdatabpl, extrabytes, pixWpl, pixBpl, i, j, k; +l_uint32 *line, *pixdata, *pword; +l_int64 npixels; +BMP_FH *bmpfh; +#if defined(__GNUC__) +BMP_HEADER *bmph; +#define bmpih (&bmph->bmpih) +#else +BMP_IH *bmpih; +#endif +PIX *pix, *pix1; +PIXCMAP *cmap; + + PROCNAME("pixReadMemBmp"); + + if (!cdata) + return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); + if (size < sizeof(BMP_FH) + sizeof(BMP_IH)) + return (PIX *)ERROR_PTR("bmf size error", procName, NULL); + + /* Verify this is an uncompressed bmp */ + bmpfh = (BMP_FH *)cdata; + bftype = bmpfh->bfType[0] + ((l_int32)bmpfh->bfType[1] << 8); + if (bftype != BMP_ID) + return (PIX *)ERROR_PTR("not bmf format", procName, NULL); +#if defined(__GNUC__) + bmph = (BMP_HEADER *)bmpfh; +#else + bmpih = (BMP_IH *)(cdata + BMP_FHBYTES); +#endif + compression = convertOnBigEnd32(bmpih->biCompression); + if (compression != 0) + return (PIX *)ERROR_PTR("cannot read compressed BMP files", + procName, NULL); + + /* Read the rest of the useful header information */ + offset = bmpfh->bfOffBits[0]; + offset += (l_int32)bmpfh->bfOffBits[1] << 8; + offset += (l_int32)bmpfh->bfOffBits[2] << 16; + offset += (l_uint32)bmpfh->bfOffBits[3] << 24; + width = convertOnBigEnd32(bmpih->biWidth); + height = convertOnBigEnd32(bmpih->biHeight); + depth = convertOnBigEnd16(bmpih->biBitCount); + imagebytes = convertOnBigEnd32(bmpih->biSizeImage); + xres = convertOnBigEnd32(bmpih->biXPelsPerMeter); + yres = convertOnBigEnd32(bmpih->biYPelsPerMeter); + + /* Some sanity checking. We impose limits on the image + * dimensions, resolution and number of pixels. We make sure the + * file is the correct size to hold the amount of uncompressed data + * that is specified in the header. The number of colormap + * entries is checked: it can be either 0 (no cmap) or some + * number between 2 and 256. + * Note that the imagebytes for uncompressed images is either + * 0 or the size of the file data. (The fact that it can + * be 0 is perhaps some legacy glitch). */ + if (width < 1) + return (PIX *)ERROR_PTR("width < 1", procName, NULL); + if (width > L_MAX_ALLOWED_WIDTH) + return (PIX *)ERROR_PTR("width too large", procName, NULL); + if (height == 0 || height < -L_MAX_ALLOWED_HEIGHT || + height > L_MAX_ALLOWED_HEIGHT) + return (PIX *)ERROR_PTR("invalid height", procName, NULL); + if (xres < 0 || xres > L_MAX_ALLOWED_RES || + yres < 0 || yres > L_MAX_ALLOWED_RES) + return (PIX *)ERROR_PTR("invalid resolution", procName, NULL); + height_neg = 0; + if (height < 0) { + height_neg = 1; + height = -height; + } + npixels = 1LL * width * height; + if (npixels > L_MAX_ALLOWED_PIXELS) + return (PIX *)ERROR_PTR("npixels too large", procName, NULL); + if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && + depth != 16 && depth != 24 && depth != 32) + return (PIX *)ERROR_PTR("depth not in {1, 2, 4, 8, 16, 24, 32}", + procName,NULL); + fdatabpl = 4 * ((1LL * width * depth + 31)/32); + if (imagebytes != 0 && imagebytes != fdatabpl * height) + return (PIX *)ERROR_PTR("invalid imagebytes", procName, NULL); + cmapbytes = offset - BMP_FHBYTES - BMP_IHBYTES; + cmapEntries = cmapbytes / sizeof(RGBA_QUAD); + if (cmapEntries < 0 || cmapEntries == 1) + return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", procName, NULL); + if (cmapEntries > L_MAX_ALLOWED_NUM_COLORS) + return (PIX *)ERROR_PTR("invalid cmap: too large", procName,NULL); + if (size != 1LL * offset + 1LL * fdatabpl * height) + return (PIX *)ERROR_PTR("size incommensurate with image data", + procName,NULL); + + /* Handle the colormap */ + cmapBuf = NULL; + if (cmapEntries > 0) { + if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(cmapEntries, sizeof(RGBA_QUAD))) + == NULL) + return (PIX *)ERROR_PTR("cmapBuf alloc fail", procName, NULL ); + + /* Read the colormap entry data from bmp. The RGBA_QUAD colormap + * entries are used for both bmp and leptonica colormaps. */ + memcpy(cmapBuf, cdata + BMP_FHBYTES + BMP_IHBYTES, + sizeof(RGBA_QUAD) * cmapEntries); + } + + /* Make a 32 bpp pix if depth is 24 bpp */ + d = (depth == 24) ? 32 : depth; + if ((pix = pixCreate(width, height, d)) == NULL) { + LEPT_FREE(cmapBuf); + return (PIX *)ERROR_PTR( "pix not made", procName, NULL); + } + pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ + pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ + pixSetInputFormat(pix, IFF_BMP); + pixWpl = pixGetWpl(pix); + pixBpl = 4 * pixWpl; + + /* Convert the bmp colormap to a pixcmap */ + cmap = NULL; + if (cmapEntries > 0) { /* import the colormap to the pix cmap */ + cmap = pixcmapCreate(L_MIN(d, 8)); + LEPT_FREE(cmap->array); /* remove generated cmap array */ + cmap->array = (void *)cmapBuf; /* and replace */ + cmap->n = L_MIN(cmapEntries, 256); + for (i = 0; i < cmap->n; i++) /* set all colors opaque */ + pixcmapSetAlpha (cmap, i, 255); + } + pixSetColormap(pix, cmap); + + /* Acquire the image data. Image origin for bmp is at lower right. */ + fdata = (l_uint8 *)cdata + offset; /* start of the bmp image data */ + pixdata = pixGetData(pix); + if (depth != 24) { /* typ. 1 or 8 bpp */ + data = (l_uint8 *)pixdata + pixBpl * (height - 1); + for (i = 0; i < height; i++) { + memcpy(data, fdata, fdatabpl); + fdata += fdatabpl; + data -= pixBpl; + } + } else { /* 24 bpp file; 32 bpp pix + * Note: for bmp files, pel[0] is blue, pel[1] is green, + * and pel[2] is red. This is opposite to the storage + * in the pix, which puts the red pixel in the 0 byte, + * the green in the 1 byte and the blue in the 2 byte. + * Note also that all words are endian flipped after + * assignment on L_LITTLE_ENDIAN platforms. + * + * We can then make these assignments for little endians: + * SET_DATA_BYTE(pword, 1, pel[0]); blue + * SET_DATA_BYTE(pword, 2, pel[1]); green + * SET_DATA_BYTE(pword, 3, pel[2]); red + * This looks like: + * 3 (R) 2 (G) 1 (B) 0 + * |-----------|------------|-----------|-----------| + * and after byte flipping: + * 3 2 (B) 1 (G) 0 (R) + * |-----------|------------|-----------|-----------| + * + * For big endians we set: + * SET_DATA_BYTE(pword, 2, pel[0]); blue + * SET_DATA_BYTE(pword, 1, pel[1]); green + * SET_DATA_BYTE(pword, 0, pel[2]); red + * This looks like: + * 0 (R) 1 (G) 2 (B) 3 + * |-----------|------------|-----------|-----------| + * so in both cases we get the correct assignment in the PIX. + * + * Can we do a platform-independent assignment? + * Yes, set the bytes without using macros: + * *((l_uint8 *)pword) = pel[2]; red + * *((l_uint8 *)pword + 1) = pel[1]; green + * *((l_uint8 *)pword + 2) = pel[0]; blue + * For little endians, before flipping, this looks again like: + * 3 (R) 2 (G) 1 (B) 0 + * |-----------|------------|-----------|-----------| + */ + extrabytes = fdatabpl - 3 * width; + line = pixdata + pixWpl * (height - 1); + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + pword = line + j; + memcpy(&pel, fdata, 3); + fdata += 3; + *((l_uint8 *)pword + COLOR_RED) = pel[2]; + *((l_uint8 *)pword + COLOR_GREEN) = pel[1]; + *((l_uint8 *)pword + COLOR_BLUE) = pel[0]; + /* should not use alpha byte, but for buggy readers, + * set it to opaque */ + *((l_uint8 *)pword + L_ALPHA_CHANNEL) = 255; + } + if (extrabytes) { + for (k = 0; k < extrabytes; k++) { + memcpy(&pel, fdata, 1); + fdata++; + } + } + line -= pixWpl; + } + } + + pixEndianByteSwap(pix); + if (height_neg) + pixFlipTB(pix, pix); + + /* ---------------------------------------------- + * The bmp colormap determines the values of black + * and white pixels for binary in the following way: + * (a) white = 0 [255], black = 1 [0] + * 255, 255, 255, 255, 0, 0, 0, 255 + * (b) black = 0 [0], white = 1 [255] + * 0, 0, 0, 255, 255, 255, 255, 255 + * We have no need for a 1 bpp pix with a colormap! + * Note: the alpha component here is 255 (opaque) + * ---------------------------------------------- */ + if (depth == 1 && cmap) { + pix1 = pixRemoveColormap(pix, REMOVE_CMAP_TO_BINARY); + pixDestroy(&pix); + pix = pix1; /* rename */ + } + + return pix; +} + + +/*--------------------------------------------------------------* + * Write bmp * + *--------------------------------------------------------------*/ +/*! + * \brief pixWriteStreamBmp() + * + * \param[in] fp file stream + * \param[in] pix all depths + * \return 0 if OK, 1 on error + */ +l_ok +pixWriteStreamBmp(FILE *fp, + PIX *pix) +{ +l_uint8 *data; +size_t size, nbytes; + + PROCNAME("pixWriteStreamBmp"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixWriteMemBmp(&data, &size, pix); + rewind(fp); + nbytes = fwrite(data, 1, size, fp); + free(data); + if (nbytes != size) + return ERROR_INT("Write error", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteMemBmp() + * + * \param[out] pfdata data of bmp formatted image + * \param[out] pfsize size of returned data + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) 2 bpp bmp files are not valid in the spec, and are
+ *          written as 8 bpp.
+ *      (2) pix with depth <= 8 bpp are written with a colormap.
+ *          16 bpp gray and 32 bpp rgb pix are written without a colormap.
+ *      (3) The transparency component in an rgb pix is ignored.
+ *          All 32 bpp pix have the bmp alpha component set to 255 (opaque).
+ *      (4) The bmp colormap entries, RGBA_QUAD, are the same as
+ *          the ones used for colormaps in leptonica.  This allows
+ *          a simple memcpy for bmp output.
+ * 
+ */ +l_ok +pixWriteMemBmp(l_uint8 **pfdata, + size_t *pfsize, + PIX *pixs) +{ +l_uint8 pel[4]; +l_uint8 *cta = NULL; /* address of the bmp color table array */ +l_uint8 *fdata, *data, *fmdata; +l_int32 cmaplen; /* number of bytes in the bmp colormap */ +l_int32 ncolors, val, stepsize; +l_int32 w, h, d, fdepth, xres, yres; +l_int32 pixWpl, pixBpl, extrabytes, fBpl, fWpl, i, j, k; +l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ +l_uint32 offbytes, fimagebytes; +l_uint32 *line, *pword; +size_t fsize; +BMP_FH *bmpfh; +#if defined(__GNUC__) +BMP_HEADER *bmph; +#define bmpih (&bmph->bmpih) +#else +BMP_IH *bmpih; +#endif +PIX *pix; +PIXCMAP *cmap; +RGBA_QUAD *pquad; + + PROCNAME("pixWriteMemBmp"); + + if (pfdata) *pfdata = NULL; + if (pfsize) *pfsize = 0; + if (!pfdata) + return ERROR_INT("&fdata not defined", procName, 1 ); + if (!pfsize) + return ERROR_INT("&fsize not defined", procName, 1 ); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pixGetDimensions(pixs, &w, &h, &d); + if (d == 2) { + L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", procName); + pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); + d = 8; + } else { + pix = pixCopy(NULL, pixs); + } + fdepth = (d == 32) ? 24 : d; + + /* Resolution is given in pixels/meter */ + xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); + yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); + + pixWpl = pixGetWpl(pix); + pixBpl = 4 * pixWpl; + fWpl = (w * fdepth + 31) / 32; + fBpl = 4 * fWpl; + fimagebytes = h * fBpl; + if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { + pixDestroy(&pix); + return ERROR_INT("image data is too large", procName, 1); + } + + /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ + heapcm = 0; + if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ + ncolors = 0; + cmaplen = 0; + } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ + ncolors = pixcmapGetCount(cmap); + cmaplen = ncolors * sizeof(RGBA_QUAD); + cta = (l_uint8 *)cmap->array; + } else { /* no existing colormap; d <= 8; make a binary or gray one */ + if (d == 1) { + cmaplen = sizeof(bwmap); + ncolors = 2; + cta = (l_uint8 *)bwmap; + } else { /* d = 2,4,8; use a grayscale output colormap */ + ncolors = 1 << fdepth; + cmaplen = ncolors * sizeof(RGBA_QUAD); + heapcm = 1; + cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); + stepsize = 255 / (ncolors - 1); + for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; + i < ncolors; + i++, val += stepsize, pquad++) { + pquad->blue = pquad->green = pquad->red = val; + pquad->alpha = 255; /* opaque */ + } + } + } + +#if DEBUG + {l_uint8 *pcmptr; + pcmptr = (l_uint8 *)pixGetColormap(pix)->array; + fprintf(stderr, "Pix colormap[0] = %c%c%c%d\n", + pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); + fprintf(stderr, "Pix colormap[1] = %c%c%c%d\n", + pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); + } +#endif /* DEBUG */ + + offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; + fsize = offbytes + fimagebytes; + fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); + *pfdata = fdata; + *pfsize = fsize; + + /* Write little-endian file header data */ + bmpfh = (BMP_FH *)fdata; + bmpfh->bfType[0] = (l_uint8)(BMP_ID >> 0); + bmpfh->bfType[1] = (l_uint8)(BMP_ID >> 8); + bmpfh->bfSize[0] = (l_uint8)(fsize >> 0); + bmpfh->bfSize[1] = (l_uint8)(fsize >> 8); + bmpfh->bfSize[2] = (l_uint8)(fsize >> 16); + bmpfh->bfSize[3] = (l_uint8)(fsize >> 24); + bmpfh->bfOffBits[0] = (l_uint8)(offbytes >> 0); + bmpfh->bfOffBits[1] = (l_uint8)(offbytes >> 8); + bmpfh->bfOffBits[2] = (l_uint8)(offbytes >> 16); + bmpfh->bfOffBits[3] = (l_uint8)(offbytes >> 24); + + /* Convert to little-endian and write the info header data */ +#if defined(__GNUC__) + bmph = (BMP_HEADER *)bmpfh; +#else + bmpih = (BMP_IH *)(fdata + BMP_FHBYTES); +#endif + bmpih->biSize = convertOnBigEnd32(BMP_IHBYTES); + bmpih->biWidth = convertOnBigEnd32(w); + bmpih->biHeight = convertOnBigEnd32(h); + bmpih->biPlanes = convertOnBigEnd16(1); + bmpih->biBitCount = convertOnBigEnd16(fdepth); + bmpih->biSizeImage = convertOnBigEnd32(fimagebytes); + bmpih->biXPelsPerMeter = convertOnBigEnd32(xres); + bmpih->biYPelsPerMeter = convertOnBigEnd32(yres); + bmpih->biClrUsed = convertOnBigEnd32(ncolors); + bmpih->biClrImportant = convertOnBigEnd32(ncolors); + + /* Copy the colormap data and free the cta if necessary */ + if (ncolors > 0) { + memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); + if (heapcm) LEPT_FREE(cta); + } + + /* When you write a binary image with a colormap + * that sets BLACK to 0, you must invert the data */ + if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { + pixInvert(pix, pix); + } + + /* An endian byte swap is also required */ + pixEndianByteSwap(pix); + + /* Transfer the image data. Image origin for bmp is at lower right. */ + fmdata = fdata + offbytes; + if (fdepth != 24) { /* typ 1 or 8 bpp */ + data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); + for (i = 0; i < h; i++) { + memcpy(fmdata, data, fBpl); + data -= pixBpl; + fmdata += fBpl; + } + } else { /* 32 bpp pix; 24 bpp file + * See the comments in pixReadStreamBmp() to + * understand the logic behind the pixel ordering below. + * Note that we have again done an endian swap on + * little endian machines before arriving here, so that + * the bytes are ordered on both platforms as: + Red Green Blue -- + |-----------|------------|-----------|-----------| + */ + extrabytes = fBpl - 3 * w; + line = pixGetData(pix) + pixWpl * (h - 1); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pword = line + j; + pel[2] = *((l_uint8 *)pword + COLOR_RED); + pel[1] = *((l_uint8 *)pword + COLOR_GREEN); + pel[0] = *((l_uint8 *)pword + COLOR_BLUE); + memcpy(fmdata, &pel, 3); + fmdata += 3; + } + if (extrabytes) { + for (k = 0; k < extrabytes; k++) { + memcpy(fmdata, &pel, 1); + fmdata++; + } + } + line -= pixWpl; + } + } + + pixDestroy(&pix); + return 0; +} + +/* --------------------------------------------*/ +#endif /* USE_BMPIO */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bmpiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/bmpiostub.c new file mode 100644 index 0000000..9a9584c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bmpiostub.c @@ -0,0 +1,68 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bmpiostub.c + *
+ *
+ *      Stubs for bmpio.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_BMPIO /* defined in environ.h */ +/* --------------------------------------------*/ + +PIX * pixReadStreamBmp(FILE *fp) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamBmp", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamBmp(FILE *fp, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteStreamBmp", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemBmp(const l_uint8 *cdata, size_t size) +{ + return (PIX *)ERROR_PTR("function not present", "pixReadMemBmp", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemBmp(l_uint8 **pdata, size_t *psize, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteMemBmp", 1); +} + +/* --------------------------------------------*/ +#endif /* !USE_BMPIO */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen1.c b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen1.c new file mode 100644 index 0000000..f722645 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen1.c @@ -0,0 +1,304 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bootnumgen1.c + *
+ *
+ *   Function for generating prog/recog/digits/bootnum1.pa from an
+ *   encoded, gzipped and serialized string.
+ *
+ *   This was generated using the stringcode utility, slightly edited,
+ *   and then merged into a single file.
+ *
+ *   The code and encoded strings were made using the stringcode utility:
+ *
+ *       L_STRCODE  *strc;
+ *       strc = strcodeCreate(101);   // arbitrary integer
+ *       strcodeGenerate(strc, "recog/digits/bootnum1.pa", "PIXA");
+ *       strcodeFinalize(&strc, ".");
+ *
+ *   The two output files, autogen.101.c and autogen.101.h, were
+ *   then slightly edited and merged into this file.
+ *
+ *   Call this way:
+ *       PIXA  *pixa = l_bootnum_gen1();   (C)
+ *       Pixa  *pixa = l_bootnum_gen1();   (C++)
+ * 
+ */ + +#include +#include "allheaders.h" + +/*---------------------------------------------------------------------*/ +/* Serialized string */ +/*---------------------------------------------------------------------*/ +static const char *l_bootnum1 = + "eJy9nAdUU1kbrs8hkFACCQgYakKTiIChgyAJvdgAG44tVFERQRFR0SSU0AWsgChE0MGxgV1s" + "CaGpIOBYQFEJomNBDaIYNJAbymHmTvz/deJd/3URyTorS98ne+/ve/e3i2LA2sQQwpKITZvX" + "bowhWCvO37IhNGITYWMkIXZtImE2wcraRlHRfeN/+EzoxsSIzeJPkRRHP76ctHIWIXHT+BNz" + "wraJd4qZAfN9lBW1FQEAUPbz9QwS/8aKXzhQ/Bfw9CmFJf6lEOu7bDMA/TkcOs169GG8V3C8" + "x8YNGyJi4gHS2YEbf4gfOvp5ui2SLwoTiEp5GDIXk8DEpGvpu4YnCug8EnYnNQp5FEX9Auox" + "1F3CHeUXJeXzEDa+n/DbAO0HRjh7wNV49D/w85rvecadmjwm2wq2bKVJ2bL7Z92EJduq40uu" + "i/ihCST7G52XIsNFyDGRxl+AHFcCCxWsipDXo1IThQCw/PTUz6tXbNOR0GgNW6Oy+DV1TKPg" + "mcAclkZrt+Eupvjh7AmNjlROLCWZxUhh5IKpHVq4Qk6GXs5HG5LwJVVgxOFzk3k+ctx0PBqz" + "gKQbQ42i0kEVggxwWU155/GZHs8ltNvA1q4y+f32XO//Dku7jf33vtHe5AZ9vwu5fAqTxUhn" + "5aaxDjNYN8CskfMk4V8hAgMu343JQ2EK77JJtxiMGwyGVjKOUsZI4wEYLkoHQK2ewpybuIoh" + "od9WKv3j3/2Udx4zf03/Gi7fh8nKHdOvl3Pv8ph2lKc1dU8aj4wF5XRwux1IFxxIVQhVJQ+y" + "kgcew8VoMfH2wKetGhsKjhpNl9BvJ1Xf0RzTb8OYkQlLv23lkW3nxQ9nQvpfcPilDB4eNxxC" + "FYDeeBxOn95uhWRuRqMHcLhwHLYbFWyCBoFUB9XFelbmdRJq7aUKIuPftlnu53hYau1eFPmZ" + "iB/6QGrZzXwgo0NLn2/jUrUmUVjKG6Ix00Va+k02LjGRghdsvghQ1yOz25LorH4AYZMfl8+6" + "VcBzseGSdzFFXwH1MH11wpytiRIUDrApVCf7vK3WjFJYFPbe+bjRT/pBFI1ioSk3GDzEZgSX" + "m6KMTg8RWGFFG6lRqB5kWXEDVxndgmEIwAA0k0zh96OwSTiiCFf50cO5bW7ZEZFIB1i+xFLm" + "TeS5IxIcjv/Lvu9gdZCoIH5oD8UdNruNTQcU0Wh8MG5kgCQ8GCIw5fJRGdG4XR2kJHqZNhNl" + "h0Z1v8HIzxYigKWJU901PsoMSIh2kqoLjXd4XrtBOyzRjiyiXaH4odO4aI071coAQR7gOGge" + "OIWujrXokL1cpb7n7tLWQ72E5MMtltqC6GV6RN0Ip6Bzp2chzu86kjSE0+jtOPjVTEK3lXQJ" + "VMq+7/RoptxoI7lCvYbQGksv5ZFsPDbbIH64IQcRaGHsF6Bug7jfrKEKmkf7Pk4vPIrcxO5C" + "u7f0oZWFaEwf+NxZozLqTKm7pHr4eVRd/DIYU9/vdQleHoXS/3xIfesIg4d6iUKS5grYfL4H" + "yOXgkeh0xhWtHDlch5sAg7bpbgi4+CEA/UgERmli8UkBbD4dTGUMAh9+gBe+YbCiUhEIWEyz" + "Sa0hakyRpIGfcdXEL/wYTe2MMmNYNJArsJyMmjQ6D/8DZOIpXfRUPNCPT8jQPSbA2LS5eJdd" + "RWOBoyIbEgBcdiQxt5V+vyupFn6OVZ9U22rXFgZLLeQPlo+rtbjLUaBT0J7tMzWp/p0q0bdV" + "Frl0LOkL/0BonJ0Rp2nsgouU3yD/3UnpY5pRzvktpDuEpuP3Xj06EthUk6/faxnGn8+x0dlg" + "Z5hr9vgkPudD49m/ol8hjEBLMwuW4xtJNPjpd7QV9cfQXl/E3YKFBqXfxeNoEXerlekE9VRe" + "aXBwjUy5VRaz3td8xcsSwF9RLYl6yvxm76ar5RuOOsZ96pnLIC4Uti6WWZnyxm/RD9tvrzuf" + "Tmu1qFO821jDyzdVbROc3g5Ym9kRm7fg2iSh4Odkjcmxop/+jA4LCsrJ/pNWmYfiilABSUCW" + "Dr0QTBaB/XgC00Ps4PD0dfoy/S9TyLjdiDgydgBhZR1F7aYXbsYqONDnUqkCoJ4wKBLZALlf" + "KQ+YJ8qcJEng5+spkz3P4u1dbVgkUL5eMkGSyM5nxwK78SRdqmzoEArdKPKIdVZ34COsAs6A" + "lceHKDIeP8TWehfhiIjB4JHV8SMcdhu6oqjAKkB5AJRDIsuGlAMG8CSy2MiKAGDOi5k/nFTc" + "LSSp4OfvXxj9UP6eoNrQIu50JKwsx0vWMmXJTXV/N8NY3znpUb1v2QZg7fUI92S/+b/pbcE6" + "HKUKoktUdjh2Nc1TvZqwdkqOgfZRrdmXQm99XvN1fjZ/cMqH/XU2b+VO9JNkUSv3SSZzK/jZ" + "fDRKEMaoZmlF6MCigrL5pJN9EcCOpTMYLFBOiR73A4VAjmDk0J/xrqRmGlCHIZOEZPn6loAu" + "lbo+rBCjLOKkiPoRX5toAPAY6RQz2HTbUhIAfmb/hc4GZfbgcYDZY7EAS2lfEPFW0eFJbrc8" + "TulRbcCVhlyKn9uDKWrZj4dbz+PsHNWnvDrlv7b24e+9KpcdTDaq/Oj8hna/nf/H+6qaVwk7" + "FZVDNY9u3fbFW2lQ5r2cxTqf5hBJt2gNP/GrTcY4ha2bGbC4oMS/YJxrsbi7sQmj4TswIDL1" + "VG2Up/qpLZ11RhUVRulBZeoLEuJcbVRU/ZqemNR47j4W3Zr8rSXOjKDIjRR+FWy//+7a7EOY" + "c5Ujvy+cyUs2SznB27deEge+E9CcjG5fqyJTYOFATmDepPsVpoOyNNIDUpIghMvvzkNz3bAj" + "jYiGFmpzLRtdK9LKqB192yZfS9mJ4hSm8wywKc2iAPF8kMtGRq1X5tIayEDnKZJQ2UATJQkj" + "3dR7HMZsRLcEFgxkBIyhABfAaaNy2FTQEB9MSipDyhRvpqewGGCqSiwA1D8zXJYS7DgiqRF+" + "+h9NJ0ZjGhcuSMqGpRFK/xET6b/ZSjGFgvPkmWo+6Yw5uL6PlyqvOf3Gnj3Zpxw64heaD9mo" + "ZWNWWaGrFbYcNdy9Z9n6HpP1Rn9uiGpbvuauy7nlRVN4Kx8Il3qWDF5ySzLdkTH4hZaRd/jS" + "Xotq3OWTRvcxZURn5SeaH1dKQkpnBMajV0f9rlBYkJARmPA4EeMx2SOwXH3fqfz2GFnvDE+3" + "2/KnZO4Y6vPeBBGSjcrcf1/63OG60ZNXLaQ/qxI9RcVd+QJCZs0cguX073G4pBxHxQDyXy/M" + "PALPB9pUoj4cZ+vYvbRP/atp7i1JNPh2QHMyrt1lA6tgoUF2YNGkHShn8kQoHQLyPoYuAPKf" + "AXVoe7SIEsxh8hYkK3PzUrWZGIodniEwwOKRPZGEjzQql42pa+oWgVply+wDNrMEoHpKr3Yw" + "BQeUf7Z7Tvo8lS7JBN8YjPZJwzGmwilnLWExQcbgt3GmlS0scUxT92qfg42XbagMv+Bv/8Y/" + "xz2FHfTNCzFsuzij/OMc/a2Y3pVxwtTofVPOGDyof9y+84txv3onsVX2Ijd47aH5r+I/FTde" + "ovuhAml7NI4YzGR5U2TPWZzOlCSDbw5+IbxB5iAYMgcsVXFH9GxvxRapBfnLRhgY4u5rTjuc" + "ygKNe17XqqJl+rtKVz7ZE2Do3l2Sm5igvO+SlgFNr9ZfZn2RS7Bz5fuGhrNdVw9u8Zq9+MjZ" + "pojlyV2lB6zJi/fML5Dkgm8PfqHFIHsQONFiTSxl8SRCrq2UwD58+r22g/aeVLAwk1D40p6y" + "ZMX36axkwpFtjmuDCeDa7ZnU8rIq9xvJrimzO7nsGRcvpBudDpEv59C2D7U5XtSZmuYRVNFX" + "+UwSCL5d+AWXDdmFZf9wcaNThxf+3tR9Sx1xx+pqL5Isa+X3qcvOtk3QXN2/Z+vWCyeCKntD" + "SxYXr/l09rRzv+ey9HpfXFFZ85quI0ZvL/umXL37tXH24UCd6R17H48oD2bORL+4p3FBAswG" + "vl/QnGypzcbqFrDAIL+wDBpbkA8qM44D1R5XUYlzlHR6QmsUYih+cU6p8y1WDQfMWJR6rq9X" + "+6px/oPN8Zeu4l13uxkolMwIL8O13W5/cuj3td0fz6ELrn/deN07f5l+5EE7u8tHfH4CBt85" + "TJ1sMe1NyjdggUnUEBoFeTyMhx4Xo+TMxNB6cfrCl4iXKdTmhlg+WcfjJW2aY6QnBx2VJO9d" + "uQYRlq9FGNAf2Yt42s/y3s3zYei1PaehKT9AWSfr9GEcHidJA986oCdp6l22HoVFA1kHLYiG" + "L/baQJaKOsmlTE1GWw4B7C4xUT3Gdq6UFCZduWC8/2xtr8PCEgb5BeqEj75TrZxCwsnxNntl" + "OjQpPqfvt6YYZwRVaUaH2ieapNWXrjE5KqiYoWISqLrDImnfstbmjlkxKSbLV5EPlb5Z1Oe3" + "SPXajQ3Yx8QnHiV726INVJ4SDyzLufVcdUipOsbeO6Ymu1CSD75V+AU+yCqEjPNtEY+P1NEI" + "TTMkuGylKFlumNPtSfGdc3zKQRKrwHwIuSnZdMO5Aa29UfhA1TXK9hGDy++2Dp+3szZJNrPK" + "Ovy6dH5bE1FtXeZJbmLcl7rHkZ/mO1xe4pD947cvqIxYF4OCqHYVSUDpDIOUAQAyDMsnDcMt" + "sWHAE0ZCEBw8XQBi8fQeJCGyKkSAqkXao2mK63C7kipDBOJZHWMdBtEfR4hBhSaUuSKRmkh5" + "KvjuL0ah3KLsZLdQTDg9MoUU8CyFDPBrXT0XVsrLSaL9f/ENS/8Z29SBdiP1++3WiuWBs7T9" + "OdrTLClDSoafp4QdrX9v6XRvRlyFgkn4AN9dy32482LRhmnnLlJUfd4qvrN17br7jW1oFrq8" + "NjzkYZ7G93yd3BZyTe2pbfsksaQzDVK2GGQaVk5gNVcriE1DGm/13KJaUFf88yErY71jTkyo" + "vW9RD99Fw7O3f0sefrlcuU+6737fwK2e5yyij599u2el2h7RwqluMsFlVlG013lvNa8PdNxY" + "XfG79VHX9V9lLS7bbzvqr/q7JJx0hQXjMThWhqkyLDjIOSyE5kgiEYIbR3CVr1NGo3GFw3kA" + "QkQGkUM0JFUowmBFRHoPBi0iU/o9xJ+pHy0JZxSxCocRXDJxhJvHJlOC9ZNFHYhX78kAkNnv" + "/WcWh+0oiQTfO/xCNftfpYbx2YZ4Sr7A61CgC+Bhlawqt94ktCCn+76comPgrCzTlXE13Knc" + "jhPT1BeULIiKnTcb77bc3cHA5/JJDjKzhKi4Nfcm98nxbxv76n0StHR275xFs0iKigx5KMFl" + "+//FOkxGjhcjk3NzI/HcHI/hckQu6BbRsrJCBkML4UB/640baWTHdmMQXBRSfqTxHpsvIsuf" + "J5Daul5QhN34tt3ahDNVIYnAhW8Io7aEETIQFG7fmqhwd4kkGnzzMBo5xmdRhG/XVsJCg8yD" + "9ySaKIWHeYlh4tEa6NGomCLHZeDV0CiRL8l25CU1CskT6aL7RFq+tS0BNwg2lEhGcxKbAF5C" + "hE9tmw/cxJgNOl3cXSZJAd80jL7XG6O4Vsy2gUUBmQZtiILLBjzJOJwrqYyOoMZZFyAAbhOu" + "4JvriS5JZdJVGcarVO9PRglgKYNcwwqoyjAxJPKiutoj98Wf8kIR5UxmcKZpMe7LZTmBl1bF" + "XYq3XXHoRMXjOJ01Nv4palXDCQrPPER8z0VO1hzP9w/anR93PwiM/Au9Zr9h9naOoGHfB7D8" + "qGVvYJgCW5JNOscwzia7FtEAiw1yDAv/ZmOT5GXZeSsqLjjwFfUtjDzvhGqlqRKnm8tjs/Zi" + "XTBOONmGbCUHTf1ljt98b8xdo6bZbPNuWT1ZZ8j1Y5btnJntfyaGbyWI9jfgP0whfrjBfHtZ" + "Ekm6NQYpmwvyCBNr0PPGkNAIt+N35fW9yqvj7Tfb98ubLZavVvBMU1jh47JEbUZdxB3iSkqS" + "iY7qNOMVoukbt9XGXuWtaDx9/MQGmmhfl89x+107HHt/ErKkcwRSjmvIEUDZRcCmcvh0t2Qe" + "oLucpCsQD3ICFu9dq9xMaQZqNSnmYEML2RG3W8SjCkR5YLLIiOSc36qyLwzZw8Rjv6LYCYtJ" + "QqqQw88bRgEv35KAxz2x7yWR4LsBjPilO4Z04FPHfamQHCaQoqihdMQgDS2k1ql45IhwuD4c" + "ToBTqbIShoQKMJ7KaBGGWoZEOpc5RRP2oxOAShPc3SYtxV2SsqXL81KOEsjEBP1dpxZHALn2" + "88SgnkCX35ZQzDXtQ2QPTj9Kn2cWfuPj+2as6afjyPKrccqvSvz98mbWMxalyHnVnMu9+NSU" + "bBJhau9ju/zSLfz8qTQQTzfJBBfLWksSSbeiMB5tqQbrVGERQc4F2rNGpSDEmULsUZaIO48B" + "gydKQWGTsFmHGIV6DFG/Bx+/jXiJxuYjssgPynkiKhqIf+EwxbX72msJ3Xbw0/gvLPhC9mSy" + "xM4dYfAQRlgbdZIulQSEgswGsgy6hSDEZLGEDXiZADskGlNmGzyMQSJf09DoviYBOblFmM5g" + "gZWXwT9Q4Y/xrzHAu4fTijbbFyhLwsBP3L+wxAt5kokl3nljyx9Yj/a8oCpkWEWYhrfbndD4" + "ZS+9y67LuOkGGtfPMD+8szk60dYcp1x+56FS+59rrnckOc+q8dJ7AHg1FAWPlB+OSLu2ziF6" + "3q3tl1CPBmY8p2X7EySh4Ofx0VKG1hjU5+UZO2FBQW5kopo2L2esXF13yjPZMlTGO8TyTqr5" + "YDlJxkTu+19YdXXL+Z+L3txL21xVZROG7iiOOvKmAIcIpHU0Xtscbbtt8c1534MeTC95N/tZ" + "0y6ERbaxsaB20bAkEPz0P1rNGN/T9NrPoBEWEGRM9KGhQgdAJlOGiZBDYzK8UWhKDaG7FQWY" + "t0zdeXbQtUNSnXQJXHtMXanArQ2WOsicTKzYmjRXq9IJ4in/JyzWH3WBKvfIE0T+VnSwHWmy" + "Iqo7I/tey92PM2/M2hp75rbWY2Jhn7nsDM/bm4r/TPf48vShS+CHU/amSWmXWPYnhlnKNy1o" + "4CvOtLUVm8MlFwbs4Ofw0Yg0vrDzW0VXPywqyJZMTIZ3NrPEnQiQazfS8PWmLrp5sk/+VFAy" + "5Zx7Z5maG5Esm5v4ebdzhlLg6evU29hsX/vlztEXW/Yox3QdPrxzwxTbxtMjqtdo7be68Tfy" + "V2t7HfkCbFninJfla39IEgt+Rh8dGyZjWIyXC07CwoKsyZzJ8sVLPFeEoQjLVDIYX0BCDD2U" + "SYsVoDJo9CoueXSpA8MQWI0udSTgYqhhVCo1lAJMYw4oJ2FtFEIKD6NEIJCrHXCe3U8RSKJI" + "t9Nv2hiK4tp57rBQoEw+sXS7pfmaKoWq7skbyKNUyu7XXq6tEcV7RyGSznzNQHMO9jXrb5p6" + "tbY2b75n1tCrrqPVT8xmNGwYmInKbvxsEF/kHSisV+46qvFuC3DDdrY97+TueEkc+Blec7Jl" + "YgyM98PCgTJ85D8qZ5TRUFyIzfwiY6zNIpo5tmQ2HJh/KVVZ4ZQes9s9eWrwgWjDEqznnIcZ" + "57AZrvK5PcVeO04dHYmw7JxqXyInmit4zCftAsNibjqdI88qeUa0fufanhg9ZBbapvy7o0tO" + "MVuoL0kp3cLAOGWaZukBWJT/qld4ca1U6AS0XNuttw13Guy6rhw4NM1SX6nWq2OFcwJpSZRf" + "4m2uUY6hgxp94VltfmyvauKftc6p25kP7POO3Wio3/XhfaTKgaOnh0P2driItn+cw7HdvLhc" + "AskeviEYzaHj9YopRafhRWfIEEzUlyJyx6yZ7Ez/AjuX37ypqQRF/yWKhxLF5iyIz1BbZjrn" + "UWlpwWH220UfSbtDShBnhgKPB8bfNfRKa1nrt23bmRvTepHeGxs+3mFer+4ivxe9638c90AE" + "mG6xeCs73bdPEk66ZQEp4wVkECL+uR1ntBiTH1SLDNnb7MWQyaivnaZlEJHV1BCvrn7W6f29" + "Nybxarm+JgGPMrtmX06cvwVpORtHuz60gyz7GSkXze084MGPEkXtVYiyMznqxFrygDOS534o" + "raZwpb7BVbKbg6fSMklI+IZBC4Dyq+m99ethQUKGYQEEeW0U0qt9xlXf2/LRtdGtd0L1TYwq" + "KiiFwe5HWU4Pf+RXOHbWV2QyMq9MPWK53f9CDkbzJf6k78zr6/5gOPbpHY7u+aO8JgnYHEJK" + "rWg7Hy2JA98ujB6rGPc/rbffwttsBNkFs8mtw3RAGY1GqeFUXKiJgAeKhF3kAqZq030ZDDfQ" + "c1cAALwT6KoetLpxWFIpfOvwC+dWIOtAgbIRp5VPL+IBRh4IhHqVA4mPp+aT2Xx8hjZuxMFa" + "uCZc0NjMTy/lpc/mpm9nIkSqWVo4t4x0PeAeqGYi6CiQPBtiD98k/EL1FTIJE3UZr+aJZU4b" + "9X2krHIrmfk9Zjmfc5RW+IUk6jPret6POCcaN3jHqwf0nS+/HeEcEzdDiTdvt9c1xonuwE92" + "J8GSOaLDe7Z7h3u3mf4RtdR0OJhQKJDRbHXZc8il45wkm3TbC6UMaxBb8P814/Rs91Bfxi3f" + "pm6MpJrMelS1T87dR1X1Juto4p7sa+cbtGs03m26eHB68YnQiruqXY6BcYQdCxP0eg0HDLNH" + "LrPif1Q5ODyubT28a18i8Gb77GHKpeuSK3D28G3DaEQbL8OWvKZNgcUl6YBYu3kilDNTNLrh" + "Q92BYuWhxyVjNSvDBGEBjfzyst08Lkp+l3xdOuuKXkYno7AI1CK5NScFpMs9NeilDIoEKGBL" + "PVmWwa6XLAvaw7cMapPd79SBpf6wUP61A3RLs4HK2HaItWZmTjJL3z50BJcWl5u7UR9fPHrb" + "uuI4sr+r1Scni6/IzaryXOF2od7ocse0bfZn7CvTd+IJNTm0NWeJew475lCLZ870fDEk89zT" + "a1q/kSNbkko6iyDlMhRkhFZDRmiCagH2wcNBouOZtNRFhKtBTgqxKXc0DlnOSfZycRqw0qsu" + "ruqRt1V7oqAtV5S6rvbD7tbtikpni8wz503xUKprOPtsg+IyRPrGrMuW3n+Irlu8fwXq3HK8" + "73jPUrKm5gDfLvzCxAJyQPP+Oa6wshwP2VRLzrErtqtfXzJvl4+nWsvUDfAPVt2vy46UZbSe" + "nd/rti+l8l7CZf+96h/nrth0gLSuY9XIG3eH2+bZcxpQp4Mc4oOnn1slCSPdfsNxD/7m4hJn" + "WDCQ94memPvtHl3uRXvhDU3kEOHyd2ZY47yyPMyRh8Jl78w4JXCd32+8fJBm/5G5gBk4VLAt" + "OHnQb8XiL3W8lKc+tKLXO45v8Lq28Yz3zYWeDfZOg542V5zv1E6PxG8htH7bcnpT/TO1I2dW" + "f/kTr6bphH13rAAvyQrfJWhONtyVxMYMWKyQFQqZbDiFVIo42IsCOjyrQJ626pljM9ie0X37" + "DVcgmDroHzGxRikzfxSxNAI7rwQrHHiqOuVYsfdr74MLjm1J+fCxZ33O86L8iJU1d+/tz2nx" + "vJmKjPvuELnjqY8wiEYpsiUx81fclwSE7xtGAce3+bn2XZwGCxCyQdCGcgFNiOGSG8hogiCF" + "xeT5pClz08n2aMxIE8lFyOXG8vHy5+lxRuoOojB6WCQhUrbHm9giv1iJ44LGMHDhmk3NGA07" + "rDOOkDDylwgBAOvs//J9tr9Vkgq+xxhNy+Mlrhf3T/TCooLc0AyIaimH35jMo8t5IHA7gXK8" + "ZxnGV96MyOG7JfNAFAJrGwsAgkOEGSj3Ej1JqfDtxC9U436yzJNKwsq1u03F3gef+GOPXZFV" + "JNDfuP92ztPolngWiI3coHo92rYrhV8aYPrZfP4C4bNio27bp0HL9kaom/hrRmBuJLrvfFYe" + "N495lml2FKPztvydXAzHoivVau0lSbb/6WmFf+0hHV/CGttDOt1GJifhWkBF+DGC4iOSTqql" + "nKGjadbjEiHm09wHvvsyHZUK/E7d21UzB2/plOHzQ/OBVtz6P98nP0deMedYWNp7/MHcIHzy" + "MMe+H7FjwOVlonGc5Bq8A3xH8QvNBjkK37/HDcgVRRo3ozkiSuEwyMVHyo+AtZQhzAcMzhXR" + "H0bYhan7i5Xoir1rY0ZiUxDYlBZ36294DxkuqswRiwBUCuz2UdfHfJTk+J+uMUAck6u4Ij6C" + "K0L4XgGztAgCPFbkVkvWdSUlOQoYPJuAD2IMlbpGhtgU6WgRysAM3mvd1GKlRzPZfITxtz/F" + "NqZ+9ltVp3WdkhTw7YMaIPUBEsgUBUxSNDJ46WmjS+hYV4CTQ2mm0XkDNNkRAqauGTuUh1Eh" + "uYx0Aw0iGonazaV0oeua8ZQOMKeTsV2A0adGkZPoPKxzhwvg5eh4PO2ZfpYEj6N0fmGcZ7qu" + "jiksnn+t/EzaIYzG/hufr6w6WZG8nnKFEqchW4hVU+/JvP3x3ibvOPRRo0HDSzIPI+/e26bR" + "/z3HD7vqSFq9dh1+iKf/6aGf+c31I517Y9IemkXPun9C8lyZI3zToAZIfeYCckDOfx9tZQO1" + "ytQ+oB6F+052r3KRJ7uyq0R8kCkidKnU9401mbj/cZJpZSHhV2mfUYAIbf2wObcNKSkdvgf4" + "hcaA/A60WA0dF8kmErUYixf4F+zZn3Eu4zwYbxnUmz991auNetfVjKYcyBgwKb4dTD8Xka2z" + "8M1dFv4tQR9YunR9+9IXN1syDTf8cSNsziEy7blZ3pw/U3wkkeBn/V8YL5InYFTpBMCjPT6H" + "R6zR1lhfXx6r7KhJ3z3/VtrvmmebXlm2rzilf+rA6RWnU9vwT9NXefk3Vfa60Uaq0hJ1qDb2" + "68nmA4sFJSdnjlx3dAqZVnxPEucf6d6a9DeQ+P1/aiXs+HkF/xOf/0bSrx19gTFtW1z/jQQZ" + "GejstID7gs1nXGXwwDAEl4NQRqfjSSQSjUql+nA/ULqEYOVrMGCDERbxVdi9EQAeEqZOL3Fq" + "dJNUbieV8tH3qmPKm4MBX1jK/71vNZFGZzHAHF2cvi47nb6ODQBmq9VJRSGIDElx9lKJQ0+K" + "43jHYmCJg+yJFfS1BrBjKUCqNg732ookTCGA2O9hbP7vdJ4PyE1HoTEYkm40cCKECgA1LNW1" + "A/MoJEnRDlKJ/k93YPwX0ZDvcP072LQBHvPM0X8l4kb6ScKCkNGDUWCOvtjUdocI2LX8PAaP" + "jLWjBJIR8rZVJN3ukEQqAhiga5SV3rldIUngKBUBZpJAeVdxHSwCKC3Pmog5d64pAAQ00M41" + "3HEyv172WyxYfSHfU6h71uX6S42lK6rWKyzEuS/M2jOloDTt/I0heVwrsgunBTz6oiW5C9vR" + "Serv/2f3GPwX9VA6dvp7UxUfkeM6uukN4Y4ZIbmkslJoOJUOkm5+SCKbyy9N49GU1EJDBI21" + "bWxuGwEBRDXrh6A6ciSne04kqcT/p2sB/ot4KPdO1Ce9asfCPcDJWxFg7e4dYhtdXdhZZxRQ" + "Q9l0p2R3zb6gWIO0JKeUQgHb+UkAJ+fisv7LueTUWO1O8iGSVkXPB4RkJdjJSuruM84gJBJV" + "YTH8e5/F+AAYwFgJUUftST1hApTnJsKXxt08FPahVRJQjhJR3nSTmVwRrlhkdUE8b6N36NUu" + "vd9rI6ndWurOozGmfZVhjzEs7VC6nQV1HvFQpafzYpW4/Qj0B6psuK5LLZ8uZ6ziiQrGPdGi" + "9AxjcAI8vWkQhU7H0OvwfHGrU3CuVlUMyXUTJ5v/ddeHMqvH5PVG6bwUZS5Cn4kwR7eg0R9Q" + "lAy9HELfayQTgUZjRjcakoRrQgStXH5eOi8PzcWUndFmiufffTRgaTfucXYnUXJty0m6k3/j" + "u6VO7rx6AZY5gDLp3wV6AR2UmYJTofNRcswGvCK6hdIsTGcw9LRE3OSCXrd3h9xwld8IbSUI" + "BUXqZrfjNODJQPowCIgMTJBfStsOSOqX7gaeX5z8G07ejMEGPEU43AiJZEsiUBDBvfgTGYis" + "EfG/YvVN6+MrnO9WSYX/0033UFINHVdo12ylyB219xvvV25+f+9x7MH1V897qPScfEN8d/+P" + "ktoom6Cm2pSIEHJcpr1C9cJOonn4/djn6LODA4Mj1QEHM9ZFGJwbunw17lDXSUxgZMHSHVkD" + "Vc9TeeRhTdZMcJ9VrNn9vZI1QifpTvSPb/TKewlKd4tT4GQf2kXnIQZAJioHhaNU9TMQHjbY" + "yzSgB6+CHqZ8J8uTHdn8PFBDtAa4bUXhY5DIXcRIVEgZzqatBFmmnX+VMOA2KNxLFneGw5a0" + "37D9DZJA0p3W+3+7QmLszDhJ3rNdJyXQZYn/Qf3cQBwnSFsxfJPaFd/+J6ICni++6VnOviVc" + "9lq354NE++xzrDwqSPY/q4APKp3xZm74d5RWtb0B5frXB5Ik0u3Bk3J4Q8kZugxj/AoJrfNk" + "qqNABHrYqDuQEFYIeVMSiV4tI55ByyFf49HoYZp4AJWH7CJEisB9qD9CksqARyjrb6C8tbhN" + "Lh0zVXs9u3qLBIkVSbqD/DpjKMtqeqxhoUCpeu7EKvxEfakdf6wJR7rz4UDyxeZpObERWb6c" + "imOJuRE7HxlEamQmyu/jbNgp+/j+o5zIEY4pceQBb9vIlk+zh12ePV35LXtV8FITwH2wVfUn" + "MNJd6POL10VAh/EcCSIGg+FGB5HadHHDiOfFZLogBcROJQlpQB3KlSTcKNsTSYgcEQ8cvWQR" + "38OqrQQ/iBWCqe8NBKC8qHE3TSQCAO5ht7PMF1+9foLzP62cQ2l8ova3+N7oFBOdxitN2QOa" + "69ZzzM2pNl11qubTN3HoWxM1V++9vkPhtMK7tKzY83HVQTWzg4w3vVt9gum4qXTrgd2U+xT7" + "m1ZOe/dXz5lTFV2oN3x8FmckEegcnnm1SYciGdysSNJNn8eHkOet+1mw2KAkP3fS39LoPFAc" + "3RDi6KaNo3QwGEMgAvEDNbcZXZdDccJGAT0ICrpHJk1rRETv3kX4Xa9ikIFYJNc2t6wTRU9I" + "oooH0Z3D+JxoJ3XJk1JWpH+ke5t/uBabn7uW0ZQ5Pt1QyPYf+hvI0mj0BSYeIyf8p3BNhoAE" + "KTy6HBdUYNqrMBNUmANotIiRkcLDo7gYBSZKkyAbjvAasSHku+D640ikWJJtMClJCAKuXuqC" + "+1y5lz9hsJOKATs5fmw+14fAYoAitPtkRfYIj+bMpSUwaZS6Vj4qo1drpMNGWIliYURub8Kc" + "uWEJxFkq3sOkB+xmQFYZTfgg6gx5zW6jJQACrsGKR/o53j/BsP9fN8W/96onCkaXKHB+JBcB" + "J1afqSKiOlI5bdM4pNIUHg0xq/gFm93KZtdy2thg0OBLFJDA0FueaLqY/xPtDlJp/0/38P0X" + "7VA8nrxnqbWLzU+vZvBQHuJEQtiVD9RhtHEqIzFUR+EtBks1Q0fAwFWeGQKwiFcdZBI7zlnd" + "waOfh6EMYxiRjObwjWKEP6c5z4i4eewnOI7/66b4Sc1y9Do+oS76sx56UA8tMvCVyRoBC+S4" + "eCUmXhONN6O84WCYCDJOm0ayFTfUCAIo8cJ1aeKcv/5Ev5PUI2J8TxA2fa8sLP1QCPaAiggT" + "M1kfyxWRxsxyqoXPKtk6GcuFacHE+29Prlmqb5L6ViB8tjYkeboHTg7h8FsbYva8DyVOdfXL" + "yefN8Tm2T74Dkhz/vKQPJsfPdgz9Fw4o3E6M7MW1Y7tSvDimx8qa5XP2tMf29px9Fd0TTfFj" + "7GvbNOuIv6Z3I6pYd+hk3S03tvLZ6uSHBUasrMPBRmeVa/y1yTPiwn5yk9I/b+uDgSEPQBVK" + "RD+iDRYGNC/RnOhOb9ZQZWu9mZG2HU2ATC4QdgdpkZdOS/+JNGupezpmTNrU4MQnsKRBExIL" + "6IBMFZWaT+1GGKogt5G+o7xpxrhdvqSkbmpiB7utSzwKxD8gcPGTwiev491FP1FsI5Xi0QrH" + "eF3SLO4uGpZiaJJKmpxn5/FSdLmgKxMRjf6wAf1hHrovnf0GyOhNZw16oNM7cfp8K5d8BHDp" + "Itb32CUe6yeipUuzo9Z7fO+OxyKXeKl6wOQ9paKZDJ4II8cUodRxI1ZEkhAMK8PJO7D5iEXN" + "QH2OvBCTWhTYj8dG+nTjmwpFoDiIDgOVTLDyIBi1CvEDATw74abm0ux58ic00iXcUY86vg9E" + "b86zbFg00LCMnbjW4F61choJiwjwUZ+uxYj+vDRE1l7gZq5p7/R2ZmyEojrNByxeOtSHGLJq" + "SyFitWbgzFnzjsR6od5lt868m6Lc14A69te5DN/KxCg9/WU6uh3dj53scg7vjTJcHfhgWfQ6" + "i7bfmWdZyEGbJ/jnzgtPRDanXPsJtHTpWReA9sZuQ1xYIdVI2TIRi5o5CqmjB8tNnXrfJORr" + "9hEsOt1kNCPrvA3tSY267g0OSpZ9r1K0EsmLtjKyjORkg61WLGvbOVzWWrFTIcHbOmhl2W+7" + "evd3JOB2bt+x5VIUwUkBeevNPX6FnGHBg3Ci0xfN6O1avwOxTFF6e9dRl4cjKwfZmYs8f0Iu" + "XXLXmuy8ixJO3YBFDhmTiVssttybWAbzsCRqMbwMq5dqu5gZEY6xKI6oDEOv4qVXBK6FW9jq" + "qGj1MNVEvYIvX1ivbs92W7QoNdP0YL7bEZ76YJ7urH4dZIVFx94752as/Xjnjp1wSd0XOcxF" + "/4ET7bMlj0VZWUmX8kdL3+OzluPV35xhQUIpfxG0Vf2aMp0AeLXPIRK1OIG6yjlpYVcUx84R" + "8TI5rX4qWr6rPerO3d0Un77xyHmLyt0RXk7NfsTdT6M8Lx+9evX7VfPNN88a3kjJWPDF4b4c" + "2Em5JjodcfMnYNJ5Ae3J1rvlNvUmLDDI4W+YOB91j6M8dmDHQNcxKPmY7/QEQ72O+wfcFfSd" + "sI+0ctvvKul04LuNdLpkQLRNGdWY69Gaax768MqUrTMqFGMEDz5mVJgce3Aye772jqRSAWpE" + "1/Ct4T2dGM+v7msIJX0Pc28o7mJZ7QDtmgP/lE3N9JcktpbONWhNhietm8qXYBFD84GJsxUR" + "90avgFCXbcOr35e3JFzJSEtZlhRbLTu1XXlXkWHvjjUFDa/jWvRMP57pOZKpBp5eiCDqGYlN" + "OIq0R+GC3IM8iz/2IYJBzO3ec8te2RoXtLkZfSi4+77Uc598TW5SZuNqrFdA5fKWq5J3r1hZ" + "S+cstCeDUrxenQ4sVMh3r4f2st8Y3ZLsxZtDjJqLY2iUUA7PpZ53UDQKCMIeOVhX2vB6Rivj" + "nWVbqJ8MqLVEJd6neKZxvvY7htofZX3bjfojvsrOz2cv+k3e3uOeXwnRCv8yPQavvfCqVfHJ" + "0nbSQMFq/qPDd+TMh/zrtxevkby70cpaOruiM9mZA1KS6LB4/7UDcfSsdR0B7c1LJxLP1+bG" + "rVxy21i13CrLJ9aPQbi1vze+xTT/Q+Pn8FNMOsbmEFHF6M3TbBFLY6ZbDErvWcI5KyJmYOQ1" + "jvipOnR1+/zAqSjZt8d/u77rjWELlRhwbXvuPHDQ1+Ia0DfkTV9Xvvr/OvH0fwCqtDFT"; + +/*---------------------------------------------------------------------*/ +/* Auto-generated deserializer */ +/*---------------------------------------------------------------------*/ +/*! + * \brief l_bootnum_gen1() + * + * \return pixa of labeled digits + * + *
+ * Call this way:
+ *      PIXA  *pixa = l_bootnum_gen1();   (C)
+ *      Pixa  *pixa = l_bootnum_gen1();   (C++)
+ * 
+ */ +PIXA * +l_bootnum_gen1(void) +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +PIXA *pixa; + + /* Unencode selected string, write to file, and read it */ + data1 = decodeBase64(l_bootnum1, strlen(l_bootnum1), &size1); + data2 = zlibUncompress(data1, size1, &size2); + pixa = pixaReadMem(data2, size2); + lept_free(data1); + lept_free(data2); + return pixa; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen2.c b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen2.c new file mode 100644 index 0000000..c8032ec --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen2.c @@ -0,0 +1,287 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bootnumgen2.c + *
+ *
+ *   Function for generating prog/recog/digits/bootnum2.pa from an
+ *   encoded, gzipped and serialized string.
+ *
+ *   This was generated using the stringcode utility, slightly edited,
+ *   and then merged into a single file.
+ *
+ *   The code and encoded strings were made using the stringcode utility:
+ *
+ *       L_STRCODE  *strc;
+ *       strc = strcodeCreate(102);   // arbitrary integer
+ *       strcodeGenerate(strc, "recog/digits/bootnum2.pa", "PIXA");
+ *       strcodeFinalize(&strc, ".");
+ *
+ *   The two output files, autogen.102.c and autogen.102.h, were
+ *   then slightly edited and merged into this file.
+ *
+ *   Call this way:
+ *       PIXA  *pixa = l_bootnum_gen2();   (C)
+ *       Pixa  *pixa = l_bootnum_gen2();   (C++)
+ * 
+ */ + +#include +#include "allheaders.h" + +/*---------------------------------------------------------------------*/ +/* Serialized string */ +/*---------------------------------------------------------------------*/ +static const char *l_bootnum2 = + "eJy1nAlUUun//y9eBNOrF3dUBFxKsw00t1xA0dRWc6xsR22x3XanTEANtExtmdJqUlu+U00z" + "ZautglczS1NbZqysRMvMagbTihS5f1DxO+d3+58Dzvl20jgcq/eL+zyf9+f5PJ/nMYxcnhRL" + "n7Vk/Ybla9fQ3Q2nbVodt2Q9fe1SeuLyJHoAnenuaWgYvPb/8zNxa5OWbFD9FMNQ/ePzGAsm" + "0JPW978zmv7jwCvDzMhpYcaGtoYAABhHhIdEqf9UfZFxqm/AsF2Tvqn/SAyfswHQ/Po5boS7" + "+s2NoTEbOWtXr16yZiPg+So/YrjqzYCIkKBog/x4uYNEJsClU8MZ/kpJIiXLlscYH82gnAeG" + "KaVcORwCQaiduA3YX1yaFrQwVSDRO/GpAwbcnlI6fgVdDNT/S0TotJDfg7mpfdqZWms3V33R" + "+7Q3VKTE6aR9lkY7Ok4gRXLwQiJEh9KzIqtRvvQbSoT+rpeZLz3vJqyEzchHeUAzzRBCUUZh" + "Uw+70aT8Q3gemqMHsiI/JhcueMvy5yYlyXAI/IUILPcLXHf3+YdMDJS71lDq1/Z9UDcOiT10" + "ggruh1pyp8iYz7BgS64Gze4+7EAKn9gerzfL1dBIEm06d9U+80o7iwlpobMiSr/M8fI9cfcJ" + "ftPiLfedfvmwd8kn0DxhxJalK11uYgA8tAawUH1R+wDwy8E7OgHEDDyVBFQhKPrWQRQSWmGI" + "yM2jZXxkMShozk74PMhsIQoRGkTORk25cjQHp09DXcR10B0Iqo7c7Be5AKWBQiVNH6pkJXOT" + "cmUCKbGTCIy6OKGMpfdyKQZrvNZYkBqtD2vPLulNnbAmDGAlccV1dHGd6jsqkKJEYS93/DeQ" + "w8GFk1w/5wikLJInv05BIHwjEFJwws2gChO4/NLK7HUYZQxGuKdOM9yyT/hlyzkuOgn30ggX" + "S+qAIDOol8VOCMxbfIrLzeVym7htquldK5aV4tJbvRky0JQI2sX0CgyBiGjTzPL5tHKMaC+t" + "RZsODqJm9t1UnURP7xe94P4NUz4dCJFsMDAYI4la9XFCseUH/JQpVXSk0mcE4XBj2KbiTy4x" + "J+esvrnG4oSxaZf1jT25M1yWdxXk19uvcixbnlnw7ZcnZ27Q8KWe091K17VgaLx1mhKOfTRb" + "6stJOtHM7qdZXV2smtMQIMlxfbh0/yrxJIifngd58U3JkpO77t4pEqQ2nzlI/Wr+1f6VuO1e" + "Zfru1DHn/njDL6zf9PspS4XknNNrxqXg9ksXJi3Jqfyped8bwt2x/o9la2RrMVQ+WlOZqL7s" + "+qgWu6ZFDyVSxfsoRdImmpBgQz4Tm4AidagoNpD8mXMiifxsDrn1KkNBPAYaPObxBagByDMC" + "txlx/GAEZyckegKua2mFytDUexgAX50GWT/Ahd1zmDoBzOwHsK9mGqpCbZp00QSfrenUJ+Hj" + "ZiW2tVnOpcw3ohbeMaFJ9T+Rz7X9ckoya1ezffOVt7UvXoyY++lFyOnm9rgpUyqidjAycz+S" + "3ju+Ln3aQ/2Me6E/dvm8F3fGY5iY2hu6Gsq2D+rH+p37dYKa1g/leV8NRdKvHzZydMDe2T1x" + "0stWUa/JY1/rpe7eUWiXWpG2XPje9K3xg+lJhnMucfbPbqA8jJwSndaOUK/k+r0vv+QW7Jrz" + "p/V2wyrnPMmoIAGWRnuLNxuMA8O2bBDoRBM5GLyS+VIA6qRxfZQogGyjB+akw8XgGzLMoPgn" + "8qUykCh8S6OQlSiXXcErJE9NRmEiIaVw6xcikXwIpnITgGedAlT1Lzb4fjy38PwVLI/27q6e" + "M/3pVvPNjm6teHyKXD3zVG/6auYMHUnMExXd2qGaFHrFVxnjOxnAycIMUdHuHUUFO4JuC4KU" + "AGQf28Yqq3ugFyWTwsCl9RaLZk77BcQK183V+3OtCTZL7HQSvnRgWNX0pSXp0gJ67nncqP01" + "E/RcnbNCUtkXgs3xX6p2/icRTyoouXk/YP3q8nsG5JVdWVXMJWG9N176L63N9d/ufmBa70NG" + "pv9Nnwd15+ch+wtrCt7X3x99ffnaRy+LFhWft6uq9rK4s4z/HUrtTX4IIU1DOVGTUa5FZDdF" + "UoWpUOmiGnT8SBY+IDaBV5YohxHQTggzuiKE0gAY+WYl/BbGdnsjSVCak9pZSKLCBHnLAp7J" + "nHtPQ3Y+WArtHV9N0e/4Cx2bnXWi8NBQcJFEvqgoc4d6nN3CpR+aR7aNYafCi9kGd4H1T23Y" + "UQoufsYGvr6rEdBRCzOplNpGrGTt/R4elFzclGmrk+RAjWRxpUxJRr6F0R+bTKzJIdseZVK4" + "8T6qd/m7pThzBDdC2GkM9cJkOY0fTtdz81NmSHE0YGan7XzcnI/1WPXa+7vZ4OTwJlrO10l9" + "/EC2kvWranIAITn0XPui1KC2NQbOTht/cjJwHnXJLvpAZGBY1I6k1k9/SakZqzIem61z+CNk" + "VX5e4M5fE0oz3tCTzIoOvprZtc02Dk6fbhLo1wA1fOL0nG0lP8mldY0RjnuS5zchzMMpAouo" + "vdkPIRBrEGdqHpBEFYhBBCcksihkEyUuliBEwuV0sh//DSsaeKL3JK1LEYt/yN5Oo/DX0/jd" + "KQzGePCJO8j0I11FBbLDklYfdg1wZja+fWYgAGTPH//n8mkPsCk9U/sEQJ2K9XvlAXPpGZ2g" + "BrzS/p46qJHY9QXR0ZudhbFmj6t+x/vWe5bQz8/egsfveP3m19ofOtLHUIS2H89t/iN26fSk" + "EZ++rhJuLB/JiQSHPdvxsiHTOm4rLvOss97MX479gaFx1835dUxnNDTRmkcU/0osE1wTSDsQ" + "gsoU7VSrLKCZBUEfWfQSm7sswIIFRHfybY59AZ1ya+rEiXIgsgYkEDYXTkghKdPEH9jbP7KT" + "ZU4hr2AlAcie65YVEmb2Acukm//3M8253uyuE9PUfqap9/sy55B62hJmThl5f2ZM9ZTUsMR3" + "jmZ56anN7048sv96MutMUoTrlWHtZTZTup9d5Jz/9Lz4+ssmxqmjcUvQS9fmFJzqBqjDXe6I" + "toQ2YGG0N3/LwTn0/tcEuU4wMzQPCJGlSTkCfSSNZgYR0XAGRVmJu1MTaZs6XCyX0oyEMC2F" + "IW4SwVaJAfTPH1EyuYHJiPwLJnKcIl8IYDMINnMjpzCUAJDX7S6zqWxagAXSPilQR22r/ri3" + "seeSVkDjTx398SLw3zWxnJct5dEQXqCQlwzxxG1EGlLpJaxdDeU8J2dfZR5ZG99GdPQiGCRw" + "4+S1hD/0gt4Cc1/bjG06scYDq1x7o1enM7Q+5bWedfE6KZ8yWDrqBBGUGJlM2mknyDMVoL4p" + "XDlYxr5WALMYyYTmt5HJ1Iy/wY5k+gFWrhgAURj4HJh+qIeUub2QL4UjlTSECBjkhoFzLHFv" + "sSza2/0QMn4Ni88gi2rOo/H0QKi8JrI6S8zjN7MEcu/Imi30XkpGLdjRkWYMGUMkIE7wuaWs" + "1g6Yv9j9bPnG+yuxurX3fPPB6dB6mVyqk27OoG4FDkEr2cnknYcFeTapaDFHRqMrcaqnALM9" + "aQI5k0RbK2xKcW0lxvlw5wNMI2M0NpIrlxSq0nDlBFn5k+DtWATtjV9tIP1py7LQH3g6IfgO" + "ToACKY+F8FKEPG6XqEBKZCHEFGGlAqqUnBL8LBDswRnYkoPu0dtofghtqZD1ATj7h3lKNX0r" + "duXrrr2d/4uZ66kRzsqRllIQFqPbqVLGyZaCeQrQHH/sx2SIMgWii8tlOfBO5XmmQjVxURD4" + "W2qZvjGRPAorWnu7HsIaRCN68uCA6QBBMgvfnBbZaFCeRVLAGSg/sZkopPUNl84WGMqBeVwf" + "uLyXvZkVCYNpOBBvQK/+EaqA66tFVdtVkbP042R75JdtczEoHtp7teVg/KF/vbFAJ5SIQZQW" + "IoLCkQpgJwysA0kebBlrBw3iGaYwFHJAQmMpuUlQea1A7pP7KkWcKhAI0nB4W34XKgpfKi2j" + "qUbr5vOT18zDz7iEBdHeoK0HJ/HfC0sqhvJM+ktbJM6MXY6d7kH769tinKIvhR3U81U4j3EJ" + "uHZ+U/uVrfcnFzazBReytnx9fMayy/rGYf+nlsNX2XpfCJvLG4FuU+B6FwVFXDhz1Q+Lor09" + "qwul/ZX3OROfT9IJJWQwf5LIkFQpCAUAxwxVeS67rQkWIixyNlhvIgwcJgw0Ef7IWEycuMkM" + "2kShL5YAei5ksjKRodgHOALHt1L8RJHlllgG7R15CDkghoF3W6CKSBadHBnLVQlWsPhyFxJP" + "5WdsBVVlBetYpFpinM0XAoGUCzajHww2sD/w2ArQVdnJA4HHb0aUm0fU/Y5l0N6b1Tlff43k" + "1djcIzoxDHjzmGoHE9WQCqmfHnz99/mnr3LW27++PilbMEZCtz0QcSrpW1WGwxnzX/M6J9Go" + "1y69ah850yRtDLJX3Kpn4RhB/YC+2/co9wUrcLIzdX88FIhl0d6bLQbHVGrvuoShPQ/0myrP" + "EKniVUUWjawM6qCRAuUC6TgSSmiuZW+qFXT9SP+LGJdCFwoIBFCIEvndcJyE3iHUIxEAY0v2" + "Ausp5Swsg/Y+PYSaldfEXHKB6s3QwTEllL6iCWmeEE3J8Fcg6mJIBwv6yC2PRMRiMPjDGEhB" + "JL8WtOEMOtJmoObCL06QaBuZepeRfAoHrCulnD5Ea07FQmjv1OpZ5dAHQRW94OsEMX9gIVFT" + "bMqnqwZVqJ6rJGr0/dxGfplriL0dvZGP+8pgJm2wVlxDZCGgJKBoPTIGnm0xdZ/dnkqXUwse" + "N09KXJHssc7QPMVxPUp5tmtu/NeVU7+YvjBteW3Ee+G9xsBmrCmWTXszV/9lSh/buYzlmTqx" + "/Xcf9FZ/AmhSrjCG0Nd2ZGWDN0Nxai1XLisVSB0saOnNrSn0pYS4OwRCq8gY+kijl6BEkNOT" + "Q8ATdnryVnF9FKocEhf5iphxDSDbe7691uTyAkulvdsPYemnoYrWhIEBZzlukZ821d3ML3Lx" + "mOMW1YKYplMljnrJhhUrR79vqQopMFhksKE5t8bwkOnKkozue1MXb2eaV4k+vCE+rhMtmLfP" + "Y+uKmC5z1psmZldy9YVoDNN47W1/CGUuDVPQPxZMLBpC8xXSNqtmE9NfXpkoo3HS7DmgHWhu" + "Dm4zB1EcYysQ/IG9kV/0jYRIvITEZIj2Afi602qO9QG9eCyA9navTnit+wDcdn/aOBSAMWVF" + "pmI6KVRy0FdwIv3SvZq1UGuI8xg2MRzv6Ph0zrSlLgeYP5MMu5p+3isWX7P1UnZPOzajgjGr" + "tXHcyLHW7fMMdoZhAbQ3+SEsOjQAmq24moFRFbYnvMqgMeFpuvtwDwfDPxK9wn8MDWFk7G3e" + "tMiLkDGKFGB2m7R6uE+G2w1KgeM+W7Kpv9Ok9i+H/FLIV0R2O59dXBK9YtGio0kgecX4XRt/" + "IoZjsbT3/X+BtagfK0A9WegW+LqLxxbjybunOhef34ifXR9Zcuca7s24jKTy8mbPLQc5a01/" + "Zte6m+Y4HXIZTdpk9dP++MiDl+eEn/0p0VMyL5jc1XlRvuTCyovLt70I8f18Eu5MxiU99nEf" + "vTwKu386XvuUgDQYCxZMd4d1whtIl+3LJCo8iF2/dmsbOftMOuftCp+SicOMcpOk3BDkfrxb" + "DiN97OOQDZNarhdHzJqGi93g/Ee2Epy34aTPQgOn/cWdVNpv06hLLlpFAVgQ3dbqQwzVA8Mv" + "tHqgngVOLlmMF8bvOX5vbvjIPMTO+ViEQC8MrFh5ljdxS7P7MeOQF5m5CkPHn9ej/8knPzk8" + "93nIz5ZlRk3G4bTmuoDJ0cfOdp9bvUgOvGWO0edwqrGNOON1W8r3lyC2O889rRPWwHZ9wH3J" + "MDED4tTTJrBny1ztTzptTGeP6BxhK6mkBEc9CjaB8XtjZk7JOqqXQdwvuSFFEiY//tRyfeSe" + "C35eCYe3+0NvvEgBvJbHRlWnRzR+3udZg8XRbb9ex+oQxlBfRYplORK+NGwHDhGxCBCstFZl" + "PqhAyrC46hT7pRWGIIg+XyyWI3zBtxyAfBEFYwlQo0mFSLDCRK+DU3gUNtSHWPylirWqNWeA" + "nsfBRsuYI1gq3fbrbfqoRjY5LtKKyj2ot1EI/He/Xv5NJBUYIzgbIYEqtLIS9o6G0HmRM2CE" + "RxWi7hDKvyuUTjdGclgQDWX4K1V5nhFHtd4+rxr3oneUupQ91WZYgv9pAUBDsEQT5ZiGaWyL" + "dCkNOrExp9n45l7vjLhYm5xLXKlns/HC62OsKiMMYuWHYsmPiTsSCdltDZOa53y7XlCnmOJ1" + "4/nKYNrLqm0V/IUCr3lnjafx/gj7j2vnFc6o/PIExaasqyXAxINeNg+fZ9IxkJ665Qj9j+mO" + "Y0SsTpADsc5TE+toY1/GSKYmejbjsrOd7TbuEA5nx881N19E9SgyHUn5+eLFkb9eUKzk2N5u" + "0c+JaWaOjW+8e8LNV+wtsj4otT9iYH54ExZE+1xBPd7M+0AytuF8dQJhaMbbV81446lG1549" + "xgiL3o3IaMLbaKyPXFQkUP3aIf1GBZ662pyP2ie9hdWrfWowhA4vjd7xGr0ygZSP46QBCAUn" + "RCGGMxCKQmSlBUPxTSzrBJG3MFTLYlBYYkBfD7gYYPm1ddz7aqxm7X1f/Rn352Pm7ZxxOmme" + "+M/PGEJwVsJAK+E3CFLl/x+MoWpSubjuQZksRyQtgBCWnZDlCbGUjGSlWCYHESkRMh4NQaRy" + "EHiw3GJG8q16bKrvqZu991cjpfUO9UOLTJIHMkG+FHDicJw4nRyCUgR94i7m5Up5Hghvs5Cn" + "gHgfbFIeeyTL78tkMCKFIeMsCGKMR1mAxV6rv0vf/OaLJdDe14dQs9AQDGztjLnPNGGzLQhS" + "0x0PjUsyt1dGuTo7TwpL/OntiG34CTs+dGc2H14TLvF1czkQ5TP2/cotXsuHTymtqLo8fmJ7" + "EzXrV29H4f1TjheVySnUmTdHlIxmvMEu+j21d3T1ZDDtA5JMTNQu49IAuWsyriB1Ax5evDtx" + "xm4xmT2qPt0w8EeG64WL3UXlVRW3vBe22jpt9Y8uvdAKchOgN39dm9OLVaxbLV7HzVyN4qmD" + "OeIwVWrFrl+0VdZmYD/8+MiuJvLvxzYCszNK2LdGpk981zJD397H8H1EVEjET6fSA0ctqfS4" + "3eQ0ph5/OGn38xLqFXFjZw6tus5+xrayUicsjG5L+n4TQEPlE3SCGeh7mqrJE2knVcuU0ZPW" + "ueqvPBlv3CBestOgZOPdrbtrl2013RPKeTwi2+gAP+7pw9cLVidO4G3p7G15GRP6qpGWCKMG" + "TFHpCbj2scN69NSLyVge7Z17CK3aGp65GudWJYh0izQpm5iYfj1i9rr9L7duPJm6Cz+fLYsL" + "6rZd+U6alovA8zKvkPRDlrJHU4t/LClqsa7oujUp7+7IKofskifvDx+aOD9q+OS95tSeZasu" + "ygCX9QGCisLdzzFkXtrb9b8gWzDQr11dPIzPJoXUw1aJwxNcrlw4aWDFHXblGDlGaOSVyF0v" + "KHkwfAQhJ0I4vXra8iNZF68VL4t3s39HNFkdfc09GI05ErNk/2v/3zZ8XSE8tL5q5GEob2NB" + "yUd9j93+fz4zf/obFk57Cx9CLuL7xzh9tSuFaQIzvVLGz5Em2nF6HEjtPLEY5rA394DCVhAi" + "kslUBiOZy5W/wk9cDaFPycpOpqIlXh5bmViYLW1iCVtQgPMf6twKxxCsS3pp7+zqvLG/+NcR" + "euW2ThhzNYt+dZ8HOUQaRrr8gq9Psv9tVO7OB6HXw2c07LNM/2S9g53c+mZvy8KQw6E/ly8M" + "jXK+yrcbi0rONdLzHX3cEnasueWNTpjKjJB47YkizvByWPZqF9Ur3SuYN4yO3YD00m0Hvj/o" + "sa/41epEFj7o/znSNHsE9BW2bIc+roY+iMhyWFBFhBTEwlP2WSbeDH/VE5KIZQqA0+lAiiPs" + "rSQ/+0Y2ucv0Px8v74SB2W8pt8Zdy8Lux3v9Txf4Go6Bmv/Usr72wtD6GxKagDHsuAPpbnbd" + "zEmkn86yO61dL0RIY0lHKGta5q5zsQ7uffb3+gvDH4aPuluf7W54N4nQHVJwuqzC4djCLj8X" + "B/MJz3FYFu1zAXWpub/mv5plO0snltmaUvPA/sW6GyY3sld2JXw2MJlV/JYdO5nhdOiAfnBq" + "UruLR1twVvAHovSnmHxOah1jBphi8XD09cBJDWeOHSr6JV0yXSjakfzi+bJkn/qlC08t9OIY" + "lZ0/isXSrUWvP9VUuLqa6oQ1mKR1qoYaBekIFLashj6KyA0ObYDFZXCGKqEkCEXGUCnMULDK" + "ExUkjgfJPj5hbbxPY6VMnC2tZAE3xlEeziwlpGEJtM8Q1KG6f1k/5t197aqv/ycMzKwpVlcp" + "8GJ4ZNRW/d171t3cOergnJG5jitvekeeDi7MIOtJr/31TWRdZtVsnDl51PVh142yowPl77xe" + "LHctnNl0g37BbpbLcEfv+BMT5ZOOzLs3nUCr8vBZzE+fehVLpn26MISFsYZs4UBZqeaacV/T" + "dxA+vQScm7h8Pz+EPY+u94xhr6gTvFni65z6yzDRw12Nhp6OS9Yd2HOrvDJOOhtcUPEeav30" + "bD13qiDE2e+Ry+5pDe1L/8xsP9fzS9CUESaP9EuZAVx/6uccLJ32ycO/cKHBIDejbyABNE4n" + "U9jK6Rt8gjZA73JafQdBSJwC0fLISgZTNfrUPaQd8ZColkn5K95HZV7sbOlXFmD6yZ4/avfU" + "AxgOb91W9v1Bbld72wqdOEL/GawpSKdqBlWSu2kCEgpufMzjF/UCSAcIsUwYCh4Q+pbGvESL" + "S2DdqUvJLtpO5dywR3B+whwUuCB3XnNyfdtULIRuHXn9D8NiPUe7Vb0G4gfNtsxvai9Nl3qf" + "PrUY/4e4Y/fEVO5vK8l8y3uEnza9oWeEvYDjrcbsOhRyreLl2V2NLZVvkzyPIMtPBg9vm8xo" + "vHHz/gHFnp1i3nPnS+04u1xXH75+JwGLpFtfno7NGBqkeZredgczPt0iXfrKuWbSJwrlfrgL" + "OcC8JVRGKLcmH51eL6t5sKohFC6aGvzCMdTxmn7UV1rl79lzwgL3xvXm2ln8kMZJbFvRXQtf" + "+Xrh40XDb2b3VvF2NbK6fI69O+OGPUblrX1+oF7G9a9LWyMcKrVC8/Dq/qC248HDX5HiRDFf" + "KgIQnsEhYCJbiENYoJDFdhbL4LtLgOMEi0uL+AIJXyAFOT1EAH+LYpUbScf2FXrr1p2nY76m" + "UT2wWbugpthYTFcF6hzXqDLXrFZLx51n9Wf+wU0fNzLyLifqpE/VhV6vNdMTndyg4WHO03Bh" + "PV1854PtzOWBCeT8tuyEfRd+/bP5xqMiqCllc83NI9dnnHn39h7rwxwfy7Nu+l1YNu2zA/UT" + "6d8B2N4Qr12tQ8MWMHg+xxigQ6ESEXgieu7w87FGI8PlThzQebjzVeoP0DLyfYP4Hpfg0WEH" + "3HB2Dx7dPuQ+yaJ+E85a5JxJyi5pxorXPgcwGxQfUvpwp07ipw2uB3h8Ka4HJ+wUwYwuFr4Z" + "JpKfSRpwk2hcOVzGvobmwKolgegUKoYswft+/HWs8FqYQPhCM4EUNHoSi8x5QvUHgLpzvjeX" + "m2+mYmm0zweGUPDT0Aw2TvZNjqIgfuxR1fwgd88Wy47zpQKcQS9uvQ1ZqUpt0raDackgWAqA" + "dpH3icAKvvmCwt5XCqxu3Qr5Ohb9/s8Qik+iI3Vi1e8yAJ/FfiyuaxTXPRDLCnZIYSMEpgph" + "dnmZjOaqZ7GWyxU6jCgkWOW+JHoB9HK4umrqqO8chdRtW78/2JaNKtTucIpG/GDnba1qCNFU" + "Q4hVTSSnMKhcuaIJQDpVA0kZj8ACOSNyjB8eXZa+U/A6xacwluCzjUXqBJ+cB2uWAu3LgWeb" + "+CgA1BYEZVZtMh2LYfH5n679NSwDXrj6frEpnwFxZry4Fl5lSK05vhhcGme+bjT3ugVlYsTU" + "DrftReyb7qTmu9KSN/tXZp7c63qv6Pbpq9/21P3nd0JkbemCbYcfxkIxLteVjXLc6Qq/6lXz" + "YwKwSLrZe3/oVcy7o919ABqkfxRq+AyAUz+OSkFmTtjp5JPaBPy+f+ZOo8fzJHG7owJucsIN" + "NllbR46ed0gaTup0If1QOGLG8d+8Mhptjk2ZvuylctKjT60pSZkLj3VWNsO7m89ex4l8/R2X" + "+OBHY8m0d/kh1Ac1ZIPtxpJaGd8m3WS8O8XfIBQVtDEQdLMQVUA0vmGDDdXDQ7EWv4sFFH0h" + "QjXccsiZ5j7ey4PCXZYkhgHmUofj4FTb41gE3br7dNw91iAMHK0Pvc80FNNJePGOUaO37130" + "nBubP84man7+ODl+slGQNymcSbm6ZVd+hmnGmnRa+QbTk2bXetcsmXz1ZOlI5Pat2/wnwEHH" + "P0I9Tlm/if7To/7Y1ky7ovyxT+56npiJ5dLe74dQ7cQ8GlapKgHZBiJgK0GIEAVyHN47kH0e" + "BStQD6fH2wOB12BFLYvL5U4s+0CH9r8nNo+YWDi3sBv+ZAyUWLi22m5+GYVF0G0DYIhh7QcN" + "QmUKX+rRghO2wCaMI2iTuC4n/TmLwm0j7kR5BuUiQYlNRr5qhYw7g9YC5TR2Ay6Vx+BKSL4/" + "qo/fEA4h0PjjQUoA+RslAgGnAw0uDHd8iEXSbVNfx+WyBmmgjrHg3kANmmMx0gY3sdPNNHTS" + "rNSdntPicAdHOu7weOd6dPG3wGdTfDJEwufGodnXix6vs3bZWrLY+vN2szzlsS+P9FLuXMFf" + "zgx5UJ6fcf2T/pg7/u3Mtw/LsFjau/4Q6hiMc5231BHDffB0ZKVMQUXQDdDXAmY18VgY9Cmm" + "HmToXRSsaKXFtbHuJCop6oIavAkAaiiP5h+mY5tIfbT3+yH0LmoUTx9sR0DTpLROWEjLYpED" + "GQpukqwoVdqRYyhEeKPJ2egeboJIyqNAHxKr2YDT4ytpxXgyjRBvJBTx3MiBCuDZHINLN+RE" + "4CHOc2fhBIoxFke3DKB/X/jyO652fQganMFCkosqAwA7cUJiFpFswvMR16FvAUTBM4F6BA1K" + "Fmj4EqU5muxloZUCKTOyl1h4m/ZBlR100gDAXuK4Ge4ZiTVJX918v7/Cx3VYodsQGtx1RZQq" + "AieShzPD3zWcmyTnCKR0Ek15XFynEOGynu9h+JlAszvBU8RTpfaLCSSWEhjfCZZns9iKAhQH" + "jL881uawvl4ulkK3Xj4dD69oKJgaimW1MjQA6XwLiWgeFN7dOpMQWmGGvaUAz0p9TOFVy+S+" + "yNsWiCXHXT1ifwlf8W4YVrBuDq5j86FG8DiNYKdaWVqBVBIrRNhduUU2GVQ+yYOQmuGd9gSY" + "GehfLRPYn/iaEwA8f0zZNv0cD7u88NWtJ69/V9uw9dBDneQObp5UonwpsRMUwhBMpiaHc30U" + "HEFRL2zgwTiPxnObUGJR7CoG4d5nkBk+1S+8Fy4MLFxYOEyVjyztrVU90tebHHcd3+JahcXQ" + "3pwtB4eJYFWe39CmK6JIk4IILCRClhCR7Ey2DQxnjFe2cBNQuMweUnAb+ak9ArLzKyh6iwwm" + "Rp7Dk+bxa74hchBIljhwM91P3scS6NZ017+XbW61dbZOBIOHnZapD8aqUgzcBhCRVhpDH1Xr" + "DBB/kcdlS7gdRPYmtidsI+jmb1U2eAPAiRY7w/CNLdhOW9//6VJbI3qw6FmpipJE1TopR7XU" + "9k9O5ks7KkFIpEopElBcmUl4nkBgP054pkeVaShwcXfSRESVNYDMOgWLvU35XBUrZYaMHGXD" + "b9grFXx166XrT7sL5EF1Qxs+lQqBlNhCJBiMVvsWX9oEg4w1CiegAoJqwvNshnmDbyRyEYDE" + "nrqdxvagAjV6+QbuPbQeIvDAw619b3ZyPpZAe/u1AjS9tcCRRz06EUwdJJCJpDm7jZGcCTZC" + "GF1Cpirf4pqthGnsZDkP5HhQaPz4pc7V2c4vs04oGTEsozoK9Xw+TVnFUJwqxj2bhchKP8OA" + "kadfiYXlcmz51ld78yUMxlAgAHyuFQyzoWu3v3ryaG7niuXiyqxSzwtwBsDrB8Pq5582DMVI" + "YjK0t1N1PaM/0USvSYJ10kT/x60btUKpCALTjA08gaf7nWyEBAJdD9A3dpi07UwW+h2B2jul" + "Ot3pX5+U/Zmv3f0OGoGOg1tfaVIEJhAY3cBElkERbEnHL/M/zwVwJsCiky4/r0kzV35Hovbe" + "qH6uZn0SI2V7tTto9H+fKxdIJQidfGKNOEbA9Taj7c/Sqg2/o0l7A/wXmqwGNOUh4rK6NP2j" + "uH1j2HcB4LhsmL7J08uHvyNLe0MzGhxuNrmGhKE9zQ6+VAISCOQJMlUWt6QtSI+TpscBQRIO" + "GOHMqNoDuXl+R6L2jqV+3e+5s6uCF+okkaqZpYHiuiRVqMQJRSFOhc0gkcxvEuOAb3NoM/Vz" + "w+jfkae9Nw3hSjmNPOfBwZZGIAhBM4g2mpwSzQ2jcdtwpDmM8eHsjTggf4GlpOfNrLzviNTe" + "eIZwCw7mM0SROrG4TjUE6YgMEcZ0sYU4PAW4TbP08f12re078rR3FfVrUp88Iv7RxyHJU32G" + "qsdK5RaKGPjF3j4Mhg/DPxEAqveavxZOArFblUyG9j6h/vT6TW/8G7O3OskbPThJ+pt37YQE" + "g72xSUCYV0M9cVUbAIN4CjdW7oLIXhGAn2yoJqcntT7FitXhzrMh7HJhZvQPYjHAgQ3Jgdxj" + "MDf93vZIIBTSg+iqiWLZZtd7ZlTsd2KhDheZqV/reK2nRuIITc1elcDJcISJ5BX+4sRciy5S" + "Fv2D6vnb8u8KigQ44HKFS8rnEfn7vyPzf3o/mTfzoKt6WacpjiSJyxL5KkWConxBkNIA4dHb" + "BQ42rt3L0ti8Few2IqlXcCyHR05RrWLfKelTJkSle39HsvYuM4S6rkayn8Zl0IKiAkFfN/ke" + "gSBf8CI/SOmbpthG6OmEvnTaKME9/ga9F5fJwYms9qA20KurhQVci7HfEvqaNeU72nVrexqi" + "du9/ascRyDbkPJvXOWm5AaTPHoqCWtmio1Ia6amHAnecxu3CZah04yBBCYqbu9cu+v3ZJdiL" + "GJg63DA2hGPmGt0DBZAxd/pKg/r1pTgnpu1L55YTXQYWIxOCTkfqSR03PNw37W2ce2jnnc+/" + "uvrkSsNjEL/V/wmcAq8ZH22x2GfzCRnuwF9eH+9dLxz3HQzd1lg6XmqlwfjvDSRH1LMPx/7Q" + "iVLID9At3Dy4jP1FwSKS/wI70AK2wjSDxelQn6yt+BhZncKX0sZWwxL0DQwA86/7/HQ46gv7" + "Owy6lTN1PFKqYdBsBvqIy+vEQDBbzMuIRR2E6GoI7elrxdwHFrE207uOqzuwVMsW1aol3kee" + "I2WQ7VEicNXUjna+qxF7Kpupw7Vi6kyiv0biIRil3Y6GRr3mjEjbV24Cl48zIfSYCLtpECpI" + "ICC8YULUCkLFbTC59xS7EOZ2HdcDDrmRp+EDr2PPiDB1uDJMXdTpr8Wa5/+mndVoBMcMniM3" + "7uuLH/eRgvOKtfRmzjqQ3eRGtv6RE7uruIjebD5y9O+BjTsM1i82ghUxU36u2Es4G2cmdZwx" + "dtgF7w2PeEfvkebs7Slf4Rzz7lPNiGVSo6iFIXXRMwrasWA63B6mDkX9WfGdl4k2OoFN1pzs" + "LR7W14H53tlm+PHQRCAs0cLJqnQjISJ2VENMxnReVyJjT8fHGx1SutWdVK/FCUEBtpGyLYpF" + "mZ2jvmy13JcXOvsw1XrUqGMi+6Ujv8PyP21b1rBoNvYTuPFcbhxfbwRBOR36VED+wouXwyG1" + "3DayuuyP9ogz4HRllaCOxuiOJQbz2oEr6+xNfB59xB7kZ+pwQ9i/CEia6yJ8FGnqgJRqQ6YU" + "ygGwUwRCClqRvANH+JH+kicHEHQpfamoiUevzhHzcmXw4tGwhMduoJ66olqAft7BmHWSCGLv" + "L2fqcCvYEHbzNBBsjanFImIxXQIQjCN9CopVacQ6MK13g/p2grd2ZOVXsNgfj15cq75u67Mx" + "xCqUg1aQGdpTigPCERez2i3vg74DoJsrD3EqhGqmgvoEnvqQewx/eOTOk1W7y/wTTxDiwaR5" + "Zi3Ws0ZwTNc5uO/YCXnyjpsvULJthpc+WEsuSLH77dVpXmw3MN3SbXYy1wX5J8X/A4d2+ho="; + + +/*---------------------------------------------------------------------*/ +/* Auto-generated deserializer */ +/*---------------------------------------------------------------------*/ +/*! + * \brief l_bootnum_gen2() + * + * \return pixa of labeled digits + * + *
+ * Call this way:
+ *      PIXA  *pixa = l_bootnum_gen2();   (C)
+ *      Pixa  *pixa = l_bootnum_gen2();   (C++)
+ * 
+ */ +PIXA * +l_bootnum_gen2(void) +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +PIXA *pixa; + + /* Unencode selected string, write to file, and read it */ + data1 = decodeBase64(l_bootnum2, strlen(l_bootnum2), &size1); + data2 = zlibUncompress(data1, size1, &size2); + pixa = pixaReadMem(data2, size2); + lept_free(data1); + lept_free(data2); + return pixa; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen3.c b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen3.c new file mode 100644 index 0000000..a77f791 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen3.c @@ -0,0 +1,364 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * \file bootnumgen3.c + *
+ *
+ *   Function for generating prog/recog/digits/bootnum3.pa from an
+ *   encoded, gzipped and serialized string.
+ *
+ *   This was generated using the stringcode utility, slightly edited,
+ *   and then merged into a single file.
+ *
+ *   The code and encoded strings were made using the stringcode utility:
+ *
+ *       L_STRCODE  *strc;
+ *       strc = strcodeCreate(103);   // arbitrary integer
+ *       strcodeGenerate(strc, "recog/digits/bootnum3.pa", "PIXA");
+ *       strcodeFinalize(&strc, ".");
+ *
+ *   The two output files, autogen.103.c and autogen.103.h, were
+ *   then slightly edited and merged into this file.
+ *
+ *   Call this way:
+ *       PIXA  *pixa = l_bootnum_gen3();   (C)
+ *       Pixa  *pixa = l_bootnum_gen3();   (C++)
+ * 
+ */ + +#include +#include "allheaders.h" + +/*---------------------------------------------------------------------*/ +/* Serialized string */ +/*---------------------------------------------------------------------*/ +static const char *l_strdata_0 = + "eJy9nXk01P37/2cMM4NhZqxjnbGPsoxdhRn7WpGSiowlqSSyJmXGTgiplJSt0o42oYx9iUIq" + "FWXQolRoG4X5jmXcy/id07v7fH5/uO/7zHHu8354vd7X83ldr+t1DZ/jjghPnMu2vcE79gTg" + "tPnWhO722rYXt8cXF7gjAmeM09I15OMz2/P/+B2vPRHbglm/ReCb/fUtBPeVuIi9c5/oEAhq" + "uH2L/82X7LjGWoBPkg8EAgnY2lisY/1bkvWjBGb9AxQi0yDF+hdvoM2mYBBIQ2H2Bxxxlhg2" + "+2GIpWuI+Z7du7cFhIAMC/B6OawPt9lamK6Hn9jwoIOXQkDwDGw0ingk4Ha2/3LQdYOUAS5Z" + "YxGv02fz5aJWDCfS1qpIm5orBfgM1tlJ5/DKJ9sEFPIZ1GJDn9p0P7//NT7Pr1focR9flrM0" + "xhETaaEun/ALNuT3aFfACeQE2mql12jY+9lHtLVcY3HVjBwzx6kFiBPD+lGY4zxX9mPVb3ES" + "rn25e5H14Xo2Z5kAi9Oi67pbsQ/VRiK+Q15B7D4+lWRryUCdFLm2ojX8yZWr9pbnxWT87M5k" + "IJ+JiLrDd9fImOwQVtjvRFz/Scfnq9SWB75mntGi0UboD8srdnaYenBgaQNePsU5rBpT8Xu/" + "haXV+zXNiPWh1TyWN0OByQTVg6G4Lkq8IQ2uKEtJwuIGoxWS/EBoGL0BPJEEK7EwN26RQOD2" + "S0hjMPnckFM8WWQj2hiD+AsM2p/gIeq8Z/VHDhAdQCBSiyCOsVGU3wLRNp3uS2B96DcPsu1B" + "GZqGQ5g7BYmo6pj1Kl4q5ZVNczLiUuJJVFDa5la97hW8SloqsT6De+t7a/GhGzH7Vtyxrg2A" + "JDHwDZbiLrwwr+BlLmJu6apNtqdulirH9Fu4KOtcu+3SM+0uftVD+UwUSO2RTf3+gmE7DlRd" + "QKjii6h2NacEfwtVR//nKIr14eZ/bcVruLM+XA3S61MTjCQdaHysvUjV6LWVTHl6ao1JkamR" + "f1/7aKrr0QI7gpJDcr3o6ybtiZcvLqS0r0oId+P9HhzZZFb1otFldOvlmUQTfpvlAWYdlfc4" + "4PQAwUkvwpV7bJ/8LTjdktP7rrM+9JyH291ey4onKG7aUaOJVFE//hHaMTU6LXG3u+rGOuMD" + "cQ2r4lG1BxBq/IdcjhQe9StREAxvMcVcQNYmCK1pFM3zeffePzHGw8aUf0Y7loKAjt5Yg23w" + "HM0zctkRCXmMsx+wvKNE5WDUB8Qosci4Puzy3d9i1Ht1wlaJ9aH7PGMoa69SCHCezodn80O4" + "rVv9PHX7LG7b0CUm0R8dxs00sIIhJZ7WWoxYTOqG8gy0rvbLtuVOJl33TgnZvikZ8tnfwMVU" + "OHibP2nYqK4K1dGaftTexfywe0DFPuOA97fThTn4DP7XfPpWGZgzrA/d/r5BUeZO1qrdtfDU" + "rK7BLm07q/xi0soJtKrfVs+8KJev/XyiHxL413T4kzbmFef0xL9AXqgF82grdsrbVKzu8oyC" + "O/jaVScf1+TV1nmSn6tOH4Os2GlIuOIZ94wDzxAw3rzknVkWjfktPAOt43he1odb5vHc2+fe" + "P27a4XWO2lxFpRihMyk35cvgCsXFJK1bSoeSNNd8rjuccgJXngnvsPZ5dB2e7MwnB2nt9Bh5" + "UGyUnt427c17MuiKqV4FLNCneL+9lb4gnX9Id22eZG5oIgfdiv+10LEF3WmebvWDKgEaAQ7q" + "MtfI0pBSKqr1Sx1ErzuX5OM9cfOrRTA8uFDaf1RtvfIpIW6pJ52jzcGVdvobf7XmSSieFNq9" + "45sSWO/08Y93DT41YVfu9lDZbOeSuJFTvIG5lD8ImSueaPII/rUj9R7MRhWEFX17vKxMra2F" + "uLBqpat1K9w4Ep8WbQl5pT7MUyN51IpOc7V1EKLf5vaj1lY5T6gku4S9tD2jpmz5HvrJnRZa" + "v8tRda892vl+ztnb3+p7Zb/WqK7K8Rrl5+QD7k7mt2TgOxFlQO5k06L6zUvCnrM2tvbN8nB9" + "hQe+qXVwVVX51CPyQoURj1vDmwqdGitsRb5/8hpUDFsJCwk+nK/so3LOnGEY3bDt4fMrsgGh" + "cV2vbvEdsl+PYL7lOU7QjjyS3rKdEw6YRxFf3JEbKy/fAuRRNBY8ih+Z2UyjpSZIyDFAKCZk" + "kInrs5ELYDSPJRakF1Cph8FSfmQyCGQMXqlSeFeCh/OJgZmRP4gQbDOyY0HEOsoE4gki5k4K" + "fG3+oCKCpO/1VCF9Srp8XW+2BzFlzZHqcLmkd67JwvZ4i3qL9amecEJwr430xbiDz2KJH8Zz" + "IivuWp7/cWHfdaPRY7s8JiS6ZTqMT7g4qIu7t05HT7y38nC55zVgnadffMSUExaYHcEsvltV" + "N5pjAdmRhb3nnjYX7SGadplZaNfY9K4WP09hq3wH0SEYn0VQ5MjuyXdJsUebHLZxpbQi7c+l" + "N5hoZyW/4N/Z1D9u2kCcGA5wjI7o2vI1/Nf3OM3q3OlBtUrQrtfq53a1ns/khANuR+b33rXN" + "G00B2RGfv71YOBHuTh2R7ErqckPq1QZohmuFWBGhTlMVHnHcr6gmotF97ySmX8pK+HGmfLnN" + "DNJGvIM3WuK9mosXTFW++VGG/MZS79q3WS3k2tNF/Xd7bLH4+3Cv/fg6VMCjnbbcazabHeXk" + "BGZJ/iCAsC3J6r927Kxkd2HP5lejMUpFr8s3WXmi4u5YbJRL2bjpVqaBMux0r9qpznj7VORt" + "EvLY3Up3ka/c6ClYaovnhcmbN+4aHKpYrdnzmLBhxBzkxgkEzIOIsH5k5oBipoP8AC2cHTux" + "YY5D6pkwxyhQihQlhw6pJ6LEGLSxIlASMYrMADUgqQwtFBY6GIb3PehIo9FwNBBEgPSdCYGS" + "dPhxbw6SakEKu4k58asOx3PSALMcQqwf7BxN3fJ8RUCOasOCKM9aDhyGh64jlp2qmHuhIPBK" + "szJGPL8YN4D1Lmzc+FJLQ6vw3RpdzffybTsTDFdHfvTN7VAkta8Y4Kvgu3eg76HnoELGTpz4" + "r80eHx/dDchn6Op+eqDbxgkGzG0IL4Kpj9yXBCTMC7mM8fy+g5DsVddpc9XwpmY5kVTLXXqH" + "n+FeizvFYBV93HdIHXC6kT/mdjJji5mWCt4d5cHgaXtAiX0vqM9jFatsYJKyaUTx0+ESAnPz" + "5A3VTxeQjBjdV64DLTkcdNrAbIfIIt1DvU5vQLJsw96E9VNUOqweloDkxWJkp/DkCEY9hT6A" + "hSS8IXIhOpi4nGksxDZHIqn2JxXj88Q0/wTuOlGENPqznkGlE5kwUPxpzaK6VXJgThZgFmN2" + "C8rO/V94w4OpgDTNcSEStpfxUkgi3F1o/lJu+/GVX2g0bbVGUalJJTc/3OnlGLhgdQg+L8ml" + "BC2htb3Mwks9+afCzmr9F7vLn03diQwSHMRcSfHyHNwS2cenov9p86d0V04mYM4Cvbg+qtJS" + "KoCka8HrurOj3mH8ulknf/ICfpNqkmWWAAZnF8i9gZVI+2O3Ce9Aa8E7vM407Es+BvvxGfX4" + "TveKLeOrxdRN91v9jFCIEP/xrsblbu5y/Z7dh61WcUIBMx/Ci5GPLLcTDSjyWf4V+cDzkS9p" + "NvKB64n4GUgdDxJzMCqCNoZOwnKNe+I/wbwO5ptY5ZuEjDFJXJ0OOOhF01oslpXk+BhdsN+k" + "yMcJAsxY/EFsYAc9l4U0cnZ1SAgISSEl+/JYbilf7pjQofjVRmZtmvKyNSgbkY2NYZ3e5uHg" + "IIlM/i4X+e+wVXTN/gPyF8s35K3gu2wryFhhwvM2X/rb8p7dz22GBMR09JPMJSR3c5IBcxXo" + "xSXy6alU+y0ydgZptOho/cgkHgHEBFYSMxM7Zo0/CG88TGUQRLAwliDNrg18RgXZ+F0AEZ3P" + "gMGfmo8zoSD1/dp8u+6+LOJ8fmBu4Q9iATtHdJ5/fsv22tl0A0IqMil3sfIUuWjiej/J8rnA" + "RLuwuanRmhT5FzYKkXLpa07Re4N3nsFdDMTfRhX49364900qt3L9rUtSlNfXjN2kBK8GK/Br" + "Vjno3Ggn+jdwYgHzDH+gsmwxWqjxrp4LBwieLnMRVR2uoowOkFPpE2u1QbTbOgtuZ9zZiI8y" + "oUImfFINSkGuY2aVxIxVF3QS+k2XYwZEdvp9F/6I2JI0c8SiY/xE/LHOr1w2w1pvL4QORnBy" + "AXMP/0GGrNkRoZlJpSO/sGQolTTFskL1WB5EIhNDGR+PhiI+ktVoNMFmhAUZXHraFwU9GYyK" + "o74xYTjSQDfi6TCF3jUgy0Yt5zeoHeKcKMD8wn94c1YtoBjSOkFxCDICipTEDBN1CVOJ9MM2" + "DDkRbMLgEKlPsHHaJgeb9FoKM0MtPQCpx6KuJnqDNnboOWJ763ZyPL4OMEMAX3z8mj2o36tD" + "sFNZWfbjt3S20OANuAiUgCzUfL2LgjkEwnLvYpvQviIeL5ZxPiEwmUcvvtqDpJYYQH9gncU/" + "MI3WSaPwzf1xrZuQmWOe+IA5m8yKStwzKodp0aNyszGpXgtU0qT1roW4bgfnYwNT8v/gTtYt" + "FHjaF0qOzk7as29uhYZ3aYoS3xMbfYqGZTY5u757l1tUcfyAQ6+uhYKgkPGdVYaCr473qx63" + "cDp/pRwdqmKnIttax4WZ4aHz3z2z8p3pWUk5TipgUv4fFoO92xkLux3yHTknFDDcglAImQg2" + "fHTsQzZMwafWylKZveZBxtxMUBdTCrR+u268F++0NefjAxNw1GLcaXoZKAHIXjksxNM6lmUk" + "iFh2SR0ct7cOzL4sVlYacpb70GW0QQuo9jw8JtH8sK6KVGuvgHpJFHJ9V8KpcV2rAT58Qerz" + "KuZpB54ttZ4JERDnNSoiYzx3Qjl5gMv2Hy7HikVnVUYFc6FJM6YQ8+lEaAKTMIVKwULGPRWj" + "pFNeFORgY5it5mXGXEwwPRI+86sGDOL9pB++99Y+zjMHHWCa/R9soc0/bSFpISGes4VFYxS6" + "NRcK8jiKe7CZNCWddBgStAr1BaK135wLAonlgiCkQeW1voVDh8kI0KbTxul5ZZZJnCzAhPoP" + "FoJdrXD8ewV+zrfjJahOQc2bPY/4xHaeTQoxxIs27ooyERaS6bosmBhhlq2a90Ztt/y1Txry" + "fV+SW5xvftcpf7+1bIvbzFGD3poO8QM7dR1Wh6Y7czIBT/Gl55gsarpTAIn0om1XiKbQIb/A" + "CbB2GEaQIEv2C2SJHWMAZP4FAiP4zrDi8hpd6EXYh9icrkCUn3JnH4z0hTiGJTM8o0GgNILK" + "rV898NucIMAkWoz1ozwH8u6my+8dILDf+oXjkQPsWrSm0jYrcjaiESRiqtbEWqduS59gcNe6" + "6vdRK4d2kBErpVSONsnznNOL3fY9c4WiiUuSxpWByvA2BccnwzGBydi2Izus8p72nXoROrMX" + "EuWtvcp/aqqUA08XmITPxrFlc3gBUR0dgPbewkHz7vnDLQjNWiSbtfcw6tgyV9QhiitFW7Qp" + "Ac5rEXz01UFz3oBnm7sqJo8/UAcfHV/9PN9G5C78W2HdOmJLapeAjVdkC9EN/clsO95ru6i2" + "6kWPTwWmgX2S6K9WistrVoLP8WzZsKe6O5iTFZgZmF1K/Bxrd42B/W+xsjMwz7+XnUTi6Gf8" + "zOrhylcHQMoavvIBTfhUsq0pzTxtc9S1feHW6umSjav40ttJ3avd35Rq+w5m8b7YfQ2bTd3g" + "G3RY9tHWbw89Rov2U9HXqs/gSqRudinuFnsi3OGjNwFLeoPghARmHf5DZ8fGhcJGx8LZkIrx" + "8Dt4+qBXjLq7WkA3OvIrLm03N9ooCrWXgRVLO5R35GibmacVOkS4kCfrPnf4VNKjgx/eHnc7" + "aLzqecxnsYZVRQaVV37sLpd606cvX1L7o5oTDZh/EFtEq4hoTvotNLbd1Fr0D1OJBdRxJAQi" + "BB1HgsajUQHF8tCTPyhU1sdUniSJAQYIFU0eAIOmEk3D5NzFsjgfGphrmH3o+fOEswd+EABZ" + "ua2LWdisa4ijE1GlrXBVaYtArUOU+F3bslMzuuWH90mikfc+9Ru3663RX8Yfb6OcpFKgGwit" + "kj5WijQX73f9lhM5jW4+HXa/p2WXwrOBre2mQ6ejP0W7DPJMrtYWL6hmqnASAvMRmMUQQr13" + "7gugCOnFPv8vQLNCiEXXmeSkYm67Ah3DBC4/1bM0XGkm/pg9lGpqFFp1PTFMDqoRqb9S+0KZ" + "VtbNwJU7ZuAahYNqQfvf2r98/Ob2qupnz1uJ7V3lzaJRBl6hVRdqECLKN96DNw3YoQ4Fojiz" + "aV3gXQ7/LVAeeGDMx3qvEujZgjLGZocHuxzGybcMxqGVI7imajiP+PFE7ntTSScuXM7acFEx" + "SeZ5dYmu/Ov7fcuP8lphcKZxA644t/X6hF2O0U5jWlX0rxtc8g51rDJY9vleOASriMmrT8Q6" + "n/Ko0B+mreZkBd7xAJCVHSj3/KMlh3tWFFJpeAmB7TrXk63zHdCeMzzowhXPNGbS2hszVjc4" + "uGGe8ucWBFudsdnSciclzQJdZTOIxXuJOmi1x2qP39q4ZiThJyphiIfotu+qoLOUDhfCUPSx" + "RXOE9oYTtRPDMAP4M7Kc7onkDZzIwPyK+KI2GB9wC/0tZHYNyJ0dW6KnsBD4DLnUO4KGaw6k" + "UJFJzzCSM2BWkPH18rOoh1H2wnb2Ut5PYkxI4xA89MMV0EhiybaZV6CmUcf21DgJpg4lyFwG" + "ZU+gfjNrz4ZUvSXWE0G8Ajqd6+W3KnECAm+EWD7fMzAY6vJbgOxqkP/C0QRrTetnTeaeEscL" + "PFwRIruPtYx5HV99SmB9Jaqd4JQxqJeoYKNYYIlYphIfADvBqFA5KZR768zbp1efvqqOZ0qO" + "UyroUz27c57HnDoeZv/oaFfpo0jTsr1lK5371ety5beUvU/rlrrx1t7D7hBWjINXD5ixEV2M" + "u2vsHHQAiYXholgwY+mgX8gE6BssAuG3cNbuh8ntjQ5T8+r9qs3VsS2DTIIJIKQRTKYNCPSZ" + "adChcieUUzP0/ufdmWzNcP3bMdLs+ZiCSHb6AD79zUNfRY06+DGRhrc6koeu3fUu13EdUdPl" + "Mz34Mdnx7MWgnJ4rB+ouKulqvxeLv5828iTwS3vhtVvJ5zZGBWK7P0+ubhPg2WoQ+fOdA2cZ" + "XA94i+Z8bIm3D3sASC2CFgznw6o5w+lYNBtb+FJpPqgGr0TXODRetTAnmJEU82Yke1gNpigg" + "UeRpvau3EyO/Ey2b0FZwfa92xiH8I7Up3utJpk73crMnGh+W4GXNnMJJIi7voiP20BUfv7t4" + "e7PqyHiY6A/NYtIq8EdBH3dBh03fOKGBOZfZ6KIyB92+/FP+b0Gzs1Vz9mZ0Yh6mxxqL3Ffw" + "lFiBf+PNwIEb9EsYVB6Rp7gxKrGkAhyn3ovBYHLyMdAcjAlhTGuKCW6MLh2IjgKd7zIoyr64" + "hNLrAfMyEosx5CxO9vdOodka6Lug9A8XcqFD861iJxXE+HMJXCHrMdGkT36nRVrvXH/96Zy3" + "lIN7o4+mXYR2N5l6MtvipMRpss176y82QXtKzZj1QgfdAq0onzPfxn2SfPZVtbDc4s4GTOwD" + "DbcQCA966+Oa6yvXcpIC8zR/IAdsBdzGPh7UEqyb9TR71HavfLo+8Eig+jLXgGNFDLiGs0VV" + "quXKlI3TXRn7FaoyNTCwtLyjTjFl3NOy/sh9sNO1b9fz6OW43LH9tU9SKVVS/NipqbXGoqFP" + "H1xraup591Ny79hPri0telY+pYMvOUGB2Zo/AGUXgSz+KpA2d9IogjkYTHSpFiFWCwv5hVyR" + "MIMMQzBNGaYyXFgGrDbNpkIi6QSagUVcVxjDuuVgqUxq6RQRdFTSaiX/MxiGk+N/blnY+r37" + "H02aEBIrrPiDcml+UQ1eOLHkBi5VZwtt5hjF72qpz32cKnn9suyYTRlamDxcwQblbXnHiV22" + "WO/SzWMB+1oN9B8nbqdUkYeUkxweTAw+ew+3lPIuyi/bWPvj6uTO8B3N+xzX/HDa9EFP7AmE" + "Exi4YZlvcaEOrb0ESM+3/K3FZTaZxfKPYsBPS/dqe2PSnDDUrJjqt0pWyw99rqzy3tvZqit0" + "FM/Pgz9fJxcxWXs+Z7Tkh1/3Ta3Gj6Ljqgp0981PQnzfVQVd2N6Du9RlUAXOVdYyrTy6XZ4T" + "D7hd+cMWwIUWkfgHl1jrKRJPr7nDxStVCk1vhO8Q3RUqYkWSPykbkEuxjJow6pRbVkgI1tqr" + "baiw1qrPLZQvbu2ow6T1VPWJ4p6qk9VVjT+3vrjr7vlNJRK0XV1P51DkTAUHmD5wXzKv7c5r" + "ow4B8iXs9j/GQ5YvAbN9CaEdTmeCBpk9dR2OffAbh6mxYBldAivTvGxjqOzgwHea84mBu5H5" + "Ctj3KN/fS7vZbsST7UYuoSkkEfOuqljSOq6znRJYyy1VeUYiYndi5LhzTKVPSCkWKK6BLjOG" + "yZjan5M7fC6+cOK56Lvbm8oahS4KpIjti77jfOZ0lbWKF2rfz/afA0iba8/K44lnH4dASGbm" + "x2VfvDrICQnMlvxBbYhtSxbSdHd2mQ9L1xbiJ/CZa5FSQi434lNJJ+pSgmxQQo3L1c68HuJr" + "M5W/znXoUKllDF+w6OhaBZdT2x+L1uy/+Sy97GmmQYLR2IX+rQ1MO+Z0iJ+70NtigzPvDTMj" + "eG5wVk/0gV8pmX+jcl9dUwLkQdgBo41d6ZvtTfLnykUvs3Ox059Er4uJ8zqleRoqrmJ/ayCB" + "b6/KikHEGlxH0rCW1V6JR13JPrzLQhV2UnwC8Xc/CurcPz7ccxQiqUlBRxt+47mA3EzozDA6" + "zIkH3Jv8Yc66iy0Atax4iODpFBKUfkay8tS5kKvFpxD4lVTCvKQYuW/Hke+3vXyX6Ru/KxYC" + "O8u3Lqv3HEYoxpwm3S9WW+a8bLn/EQt53gbNMJjDBsFL0Sczbj9GtvmUe95uFShceaT5i8/g" + "Y66xJp7OK27Ot0xvc7bF6AMvugB8Kdl65/GvFmnsnXyfuMuk1kB4VkedsgQV7NjOw5eB3+r9" + "atMbnyHcy2UK8VRtiRHdvToxNX6xb54/LPR/iMXeDNdqE2qRJTzMOhgTdlnb/uGlH30putND" + "VR5Z8stqX53Q2MWJCNyazCPCwo15ACncQl3Jkt3OtFb+bJxATiyfmDnev4m7PQONd7YIx/Dh" + "Tw836d+TSjvR1mK5NjPWYivkGZdhyLWpZI+EKcIJR+S3LsHn/DkM1WMHrzRfcit8+Laq9MIu" + "aSdokVoYpB+qR1vDvWIbJyUw4/IHV9bYQufNFjotQda+jadvx7g5VFOSrWtzweNer5WlazOp" + "rh6Q5AANj8ob0wUXY15kQjVLennEfcsNB215W1u4nCazblRLE095C/m86Ji2U5yestD/BTXg" + "u9hVWv6wWxda1weTy3XL/OkvaMSJCcyuCCwupsEvtd9rRmHLnjpb9jRrxxJj6TBIPQyaAEG8" + "A+G5sn9CTKUj0BAhs6G6TqJgAlMIlPkDl3m+5+wpzgcGZkD+wF+xVc/9H/7Ksmu54rHnm+nK" + "lVnCKKuiYAGr/GIc89fRJ8dPfXf3EZLMtMsvzTDRcTu34pxJRLDUxy+dW+87QjZvhZ1UOMl9" + "0+NbTM69zfU97vjzGaufM9zrjLNvan/6Wpk6wQFoAMyIzEaQ+ZzUGHdTHZDiefx1MyZuVtZX" + "nSSb2eULaD9/Tj57QCgtzUKhEyyH3vpFtNbWH2ZV1ItabswvcGRD28iRCx4vtK+POHXuPBIf" + "xBt+/6WKoprGt9cPussOaYCfpehxX4me6swnNpwi8VpYCTpxIgJzLtKLot6Yd/73Dlb/1e6+" + "oWPx5hbeE+JChjwhI/Rt1pW5WLXCN67DtcusE6QPbvDQtuHDW9nbTR95RkVnr1pp/jZSAlm7" + "wnrbhoYdF5yfqSKg3PvqYxD91hDlVX2m9n5am069k4a+Bnf07eh8F/5FhJMT+JUSgHuVnZdv" + "YN8ynKupcHdlZjpY8WrEby7hdjtpH8QbJ9gpc3/tXtJYYZWMZOays862PR34u04V7Vk+leFV" + "DHQg5MfamfGXakZM9azj8Pf5su99XdrIKqs3G9ZhgmsCOcGA100ASsC/0nC9jlre+tk0/DAk" + "ee0y0xGLHa2ivbt6eo8NCkX1yPnp2AyPYTCYBL8TYxauhUcYdkU2bw+2VgUfUCYqv4hTsz0N" + "USuN+SjUtMJwg6wCs98HFrEp8KPGx8/jud8qhW8yQaRVhll9YyZpnKDAb50AlHN2Gm67sFOb" + "ywRoOJacm2vMtgBob57N5eSU4hupr3FZmncHY1EiU9evwQcIe/2mbMgGElFbP658f7fzqFKF" + "Z8OOkAP2a3RUTPXN70G+DDM5YYB5k1krpjZ/Tqek0QnIm+xdgHm4eHygKkG1RPlDsWU5122t" + "8mO5WoflzDrdvmBfUKuEkVUkeoOZdE6SgHFml2gCqVe+Winrmugvxxgj/j5ugYqRrWarVxt/" + "sWlItLgcIC/80V6hkOui2dHdPS+Y6eZDa/tHy2becW32IEd+Vj5gz0kN/IAIYHGMbVdC2IXN" + "2VtSKIijtUh2NKQcLlTUmNQ468iy5AfvTXcdx/ttDS7/LIg2bVDWSEp3OIVJDtNBheorodUu" + "JWFXlVkdOP+yU6tLGQUbeEPs9yuoSLU/o4i8YVD5IOnRjcPCz541Hmj8caFQSeekmuCBrzxj" + "4d56GfdyJDi5gRsYgCMT2AbG6+8dBLOrrYrfcYGcXbopyXWUtdo2J+TrerOkUB16x5hYeTMb" + "pwALL7JKZRdhVSi/3clUB0cbkQtYwWGDp8Qblw50Fp0aHNfbuWVL7PFwb3LZgUq7B8Xv3sDE" + "fMklyXcstTkp/+flFrZ/MV3sN2Ji64nfoxHIHKaWLqFwEEKagiUxYwMV0AycSGxvh2GDlNyb" + "A+SB2YpaQyclDkso1ZqKpsFAQih7eI++eQgnBDBP8x8u8vn966q9+fy5XtqLuA3aKdY2J7iC" + "GHCzwn17h/aYC8mNNiQffefa7qDrYGEkIhR62MIWDxJ4biCgfXZnxte+DXl5D9/pTTQO37z1" + "pr/XIfML8R0SXr1e+NwvuZEBgaqnTideMQQHOVgNgdsbgBGVbW/c/7Ytcah4+hnH2Vp1hVg8" + "L0a8frWDGyUr5Qu8e1PU46rq8Ba5EJbhsRtocg7MLgmzHzlmb2x37E3GxRTricFz+gEIefOZ" + "2uMSaz7KL+vPbQrrJHbzd98zyu24GcLZfGsIvCyjOq+Ny3rcAZmbhdseu1tmE16WNtoaBfZB" + "EWFceQVnnVKUIoZLPd24vXVltx6cMBWSTO5Wi+Zu5A+59XIKJWsd6J51/17YnZOs34lsTXhP" + "e97yDvbxiIrkQ9KMGq8/mhMKmJP5D6fsSwWTEUx8l1eDl5ValqmjLDEpSb5D73uNQBa0GZ9V" + "e9CBem574l3PFq9poYuR/G+Kj9aSAgZRGnawPUb3UzYEWfLbZ7s8XnUsbPVhlTJuJWet5wIe" + "cO9DqEdenAMFDIE3sgAsNrHVnn0c5EemcacSXTE51JxpGBJ1n3AjOsOTwayFJu3CzBBBdCKC" + "iSQFQVAG8p6+3DOwulHUVOL6dmSPChMGMjtsHdLb1lbHiQG8tWU+g1AYiT8NSOcXCoMbUudM" + "CzdRSCjtrBornqjzoCXai3y47cThE1aqZkFpB34QdkUEkjLzj2XkDryP6Ya0uY5XTVxT9u8e" + "nWEk/IjfWoxtns4sO1IBiszH9rb6H7i+tzmwcM9Hnoj7+m+VNo9wc0ICMzNii6+ZxNDwJ0Cy" + "7rk43YKXQmK9ZpqyOdK9lLPwW9tw5WFhYV68CeJqV+slpKr9eh8O5gVfD7L1rGvpPUm3Dcl8" + "BynzsIbcKtrfM7l+n+KNxJVpJ+nrrjs0KFnmvEDyNEdK+GQ3rqj3QN4yTmvWaOZUN0Pg92X/" + "sGlsy9+uiuBEQF06Itk3wtS8QnTiSzENloEQe/6hH4klZRGvX2dsMNxkODZacc7rQqzcQ7uV" + "KRaF2dbK05gLZ3r8L+ruEb7xMK0g/aLBz7KfIT+/uznYZEhJu5kkZvRKaHLiAbMo/6Hmvtg4" + "VsuMLaBOwyAQuI7CGBg3czj9WXJD71ezcSyUPwEKhRISwKhfIAgMpLJa3/HrA3nOK7+GwBzH" + "H1xEZ4s1eTF55W2YzX00RQxDeD81aKze8pzPoFBoeDQmdhyDaOZBQsVmCu2TYjWlsC7K/LF2" + "61ruoq4I21VCz4+s3Xw75pxl6K59W9I2NfY1GFro3/kS+5mOpX+zkcqW2vrWuA409HKJcA/8" + "lGc+ELaHwRiARHrhvmxo+ny4X1tocwKNUbpELfezyi9OmqhebW4eVD5M/JCmqOzVadhL3cnX" + "XndLv1iTR33Ioah89GFxUG5O2aMNHip91R/eTNXe2V85Pdoxw1CY6YJUNZi0H6gxGeegWwHc" + "ggCk+5dCh/5lt1Svg7dy1/rii7aJWAWOcDW6EWwcHasHw7GfBLfIPG89nkw6j/jwJn/P/d38" + "v4ri7V7bgKwkNeKXvcgc+LhWGfxqTS+3yeqYAh7OS6YrgNsOgN1+bIVmTzR6UIVm2Q7uzm0a" + "uGJuuzJ1HU9/xPJSnjZCoKu43F7E1r1n7kJsRSwUI8paFS7dYxKowmonP+z3HL167h69GHLi" + "/FDR077OYf2xCTKvX9uFPW+U2wzkLbuMpxmgfnlb43CNn+84IYEXVP7w+H/nwrvXXiZQR8Dw" + "0A9lntKYPU72ygzMJ6sfzkK7nSudaba3ODL4YU3iz7jNVo0t3MifKH/vzqthyCfn78ZUOpdO" + "iYY3Gvf5lyOuW3o5Dhb+8FxvPHlxrOx1Rkmw17bjO2b8Z0KjLadAG3uMlYKC8mM5aYHZkT9w" + "kmw74vBXbkOsB0OtZiOhImKYSfbxm6pv75xKlEmalEhnZmxnIOuwpF5ZKrbHfIzKREWhkqQK" + "KiR4dYoGownt6VjmWyYSVOzvyHVle/8eTh5gvkR00Ze0vDTJA+RL3P86G5n1JSwTWQdPrXNV" + "VTVV89VvhWdryLd4W+7ICE37eDt1k+illULc6L08SdAbzUWp7gr0UStGw6u27d+lv6Tvh+UF" + "1waAjvLt2fbtmHDVkdEz9K88x7x0LO2/Ru7nBATmSWZT7vlSwxrxs7sAeZJ9fxVY6kgisZ4s" + "l3wdtM6KN7c/z7X92XBaRpZ8Sll8Rv7kxfPMqAwL+Js6u+pSFCzAwlM/vF5fQsBrI1KxKfHn" + "2Uuy95YfHLfVU4y3yKxeEVtZ1D3zEP5wIDx0X8IDx61KVyXunC1Xet4+TD+1Wr3gtc5Ml9S3" + "9qA3JU8q0jnhgXmVP+gCZ6/u+sVOzTFkPREqlYBFaCOQmAiMIKGUoJs/hCRNQewgkzBaDuyy" + "bAKS1Gg6gRBDIBDR+RXQJAEMLR+cYvqeFGkCGsfm32VSUmaYTAgIsQ+nIriPh/NEbwUwk4IE" + "sa+XofZfjAJkUjQXGwOaxs4k07HC9UjlBIgDQsABgSD0gMQmIaYIx6/ImBxHtP44MwoUdFEy" + "5k1ZOWd9fQUwiyK2+Mj3aaCtgCyK89+uKc6ekexSLPPktvIk8B8iSgbUwbNFiLUqg/ZrvmUd" + "GgrRTrNQWgc/Ag5rU5q5s+3rWahVMmKr7dHL6Y/0z3/aa2jp8MpfnZnKAN2jKb40nLTo48QC" + "fuVnfsLEt1JfYCOANrMPHueuUMTTmZ1FCoZJMNgX16CylEMhcdL78Wma6zLGTIXMZD/llTSl" + "l63bhC9ea1Jsu7Zv8H1/cdKng8Hb88ONIGtaTYNf+syoVV5r03ptedVjmmfDfY2EqQ1vPTno" + "tAjAbMks3vx1M9CpR78A2RLbxXBPS6AzwRIJTAHHdlAtk8JAijBBg0jHKVAcP45Bg0kkQHH9" + "GbR6Go1WR0FIUhggx1EmRDaBSQoNnILE7Qbd1lDi3+y3a/MSNMAv9eDmaExGbwIb9bOefXlu" + "YbGwo2UKIiEZqcqugWWp3QKjXeYkD0ujCEZExnB8ehfecLJ5TF02NCd+g0PK8MbC7w2hdohA" + "fLipzg1v21d3L734el64QcQg9IFyeAbshcESYMA9yPwulNwrAGzs4AKYesecivF0HeG3syIL" + "NfigPXUrL9fCVZ1P9K5JedrbTfx+Fp+X2LTaM3O1V+VJTP8pSZqes4P+iEpgy4+ZNByi6uGy" + "sttm6ek/qqtv3+uDy/ePtRskLwEGvPoBMGqw7YbNYvWj1JMEE3CcYoL5UV9IZYh6JgTnD2nQ" + "QzClZghT20FeTUQEk5QziazHYnyZ9WMQiS9MTwYlLroF8riFCQY11WiNeSV8oiwBA3x66fz2" + "69J4+XslVbYa2f2jLpywkqVGYQgshoGRJZRSx8GoY2QvQ3IsDenfOzDRwgBBhLlfEAi6PbUE" + "Ye6nIC3YetmriUi/4zKdL6NBfRJyxkV2rZNL4ADve51fG0WuXWRAzsLpHz0X3LWNYDklR95C" + "DS7+O7rHB4VMpmjaffdtuIPp6gckDA2abL3Plzu5nldwKk+lafI7T+tqBe3TfSK8oU2vZtDA" + "or4nuIl/TZf8cd8CkyWmMhGAd5IAXCR2eeMfg3FYr5JzSnbPeocY7c11fr7CsyM8BYkJKYO2" + "x9TOjFQNrdcQe0AIyuC6LnKhG6ob/XzXr0cPzOpCdl2y0LyUV0Q82C+ycga0r0x2PdXDgLAE" + "FTDD8Af30dmGgbBohE7Ro3Xqo60SolMRRIyEIEFbl6BNoLRCCT+w+l/PoC/yiHOp+kFBjiMy" + "d6Ikng4t8dDAz1EAbrB/WYaFO5hQurCwnVWg0KBfQlfg7EpE0CeL0hqfugu+UmmyfZufqbjO" + "g5oFcYtS7hu8xR1lczhidQTa2IHuesv26W1b96/mNTVmUaDIVMXtbq1uS4kqMM8gshitxy1v" + "/V4bD1tUHdkVtNlqPMi8S1mDBk9/A08lDXphxKkCkcOkyxu6eofzIhmOStka6LohBuqA33V1" + "7kOZJbcEqq6k5thxo8Y8yv3bwBOa9/r8bwtjeowtEm7Ic9Z3tQDOGZ29xD1vtcuObJ8ApK1r" + "Fy1pBIUOjoXUg3EBlCEI4uNH0hSW+qwghyqTdIKaA06QGahgUiH1TCQCwYwm15KCZpCCiHby" + "TdRdHAPkx4Tl74seZlnjV6qaJTwT7l+WYALmF2YXSn6OKbyrAQVIVjew78cWsGQVY0E/jitN" + "rfNL7TwXc9OvEq6AQ3NHEi9cYuwdbPCWSbbtq9S9oPh+Z99EVHkE+Iji3iR1pcHI3qkBlcO0" + "s5+zY8oSbmadTBt5zq+4VsPvnJXtySXIgI//mt+C4v4HfgLS1QVfZ9k0axhQ5k7h2XMtBKl4" + "V/7ZrFfVUcOEJ67mtA/q5WmmhsvkrizFtdSI47EYDaGHe4X63Rt2bzmqvKNNqBhDXMmvX2Tt" + "m8TZEKEFcOio6CKNbGI/BZCwbmIrkRbf3K1STbFz17/hZTu0M+p2qW5VfInbllK60pf/UHMd" + "od5rvaRU/NH27jy9QL0yknZNXnlV5NsPv5rCnaAfrWJHJjvK7dfJSknHadh3WdV4NDcvI1je" + "Ob9+CTrgw8Dm6YRPXGkGpLOufxXc5/o9sE6OvnEEySLFwpbNso0KjutwmeYWTj53B/3zHJBr" + "GvUkctWcdg4GJPfivm9jnvglv9Eyr9ESnSfx/uBO34ocnTXTz6T5r5VEMSCNtVp5qbZoztMt" + "LYBTRkUXX7Ec4WsagOR2NTtsvJqKpSPrkbjLZAaNXDtGMY2hU2O562uxQoi0aEPMSaYp4Us0" + "FBpmVCEjYKAwFI0gXxecAnlB4dBnX8BJpt9nvKfAoPMoDWUdIo3zkowWwHmi6MVAuLm4bxyQ" + "1C5nE3m+oo2R7lELwALMAugt66EvBoQpcgDZkAa6EQsGk/zzMSCdEALK+GpG8xLPC3z01/wK" + "RD68sRGQym78x+gvi67D64p9uF1afbm1QMv78RJy2/gUg1UzvD7c+SKzR+iAsMA+/sdRNjIT" + "lObBp+fNdI8k7ZzJk1BY5R3e5Hm+RE6jSl2TvLdZ+9M0aH2JttOjYDjnPWwtgMND0YtsmQzk" + "+t9iY2fni7urlrUWlDtUOsgbAkHoY8lkBhOURCTXzg4VMtAnTEEuMuvhBaYMJhWaJG3TgdRF" + "iFyfnQ94BwUqg/motHwiTgiAuj31tkNsTS4sQQTMO8y+L/P29PELQWCd+faLOcQAjJUDibLy" + "n/wccAwTPI7F6JeQGeAGLF8+w5ynPhYmikBSv5riA6BeTTAxxLt8cBKWYEOYYpLBDaTr0flk" + "UNtOPQ0bO7ElppMDnBOKWgxujKtiDYBsg/FfNw1AODioVkXkWCrtYmoTPnUAWpJ8VOvO4KHt" + "fLxlvOdcawIx+bjmNfaV7ybp8Y3G5q2mk1ya32S2OAZvqFmC4P+bSdj0dzfHEp+1/pR17xzT" + "YC9ieuFhpdbIuFCCtFG1T1Ze6+CxMfVBi0rdfTa9O3OkNgSo1u/wSdBtfDhDe4kLcT0oeOdc" + "4X6XaxeJOd+H1jpPkDVpJ3R3xCaF3FqCDphR+IP4zJbWhZZz9ftzd6us6EMVCiEiTWbycnDF" + "5UfXl1oJi4E2dt2Y4mlQ+TTk+NbCV7FLOKihtvc6uu1tyRnc+TerVl3trWF2fAs3kTxUeZW0" + "MXfk5rTLx7B+o3evek5EWjt5mEeCHLbovL2dffLmEpzAR4f+ochu/IfIzn2zQTlXro+oVZlF" + "5k3ttsMkW2rg6TibzNDGLRNSB1QkS1orGjIc8XpyukaDPdHZa1VFK/cFdfGZy2wlSjxIDlTw" + "3xJjMW0cAUGiNE4Zgu8uMZId4DTRPzjS/ldK680gMmH1xCYsAos5SDAil7CkiAxuSsMqYk5G" + "2xBKmeDBaFx7Le2wsA5JK2zfd6I0OTiH+kbLKIPsRwZ5JhljKWEzoLrodiJo76VlTOsLVpwT" + "1rT+YJIowNj+74P6CNwrWiclDgkax7r13qPSsULQcc9BjH8IIvtXLUL3TAEVLEGJwGIkQXe3" + "ab4y3JY8ssRTA59FNv/URwVe7QCktn8f8TLXIgJNODYQ6rX82F6ydwp1d6qMm19gRXr+QF3s" + "NtewkqQjyWYT0Ps+KpdF9V+omzwoUjR7U98iLxl0szBs5tEhbXia2GBpx5uNsmcSGqLL7/yC" + "0o4ZeOEZzseXQARuKOZfpaktTb/3PTb/mn3HEt0oCp2VB+JDQEP1SAxmJho0iMWNUmhYCqMW" + "nBCdaoMpGYqGoIiUC7Hvf0EaSfrETrWw/BOoAVAT7oUs5et1LAgkU6t/xp+/pGwJIODzyf5b" + "arutvYAVA1lpoPWyDPcRrMDGVps+vEyBkCne2g6Fu3cqsnlFsh1uy9bCr57Trr72XOGEvtUX" + "TF9hYS9r+mKKA4WLviROPS5/l6txR1ZfFHzgSfESTMCnj88zbXVCtANSLdfF4h0vbbblEaum" + "dj2maEWKe1PLMjOxO/6BZxvRwSlZg7HN4xHW1+rsm2x0z59uNHavOB5cFSd0yiBnIgt+Msk8" + "1vs68fB+R9/baqEzQj0G6m2wFUEazuAaoyWyW4CzRv8gNLCzW/ZAuYjZOZ6CgqBxJgzXRxtj" + "yqGwcYO+eN/E2enCsLqJueHCicbc0SD6T2gCzDFqgMhlYBakg8IyX4FAVWcMToa7ungtQfI/" + "Nxcco2aaGVRWegRLwCJ4SN2zBRZQPRhnwoilFkwiRZixnYlUxgA2fwoJzl8JEYkCJeUS4YQb" + "kNfPqO8nTRlErtiGHOpPqpiWLqhjD+gFpHsLSOQnK7eRXfbt9t7P00sQAjMYfxAQ/yW8G9Kq" + "5rJbj+yYLFEjyaKMZbu0Uqzzi0lyKqeUy0+r9Vs3B6+UeSr6vKniZFC4goD+vhlbna/XzXlC" + "ucSzMjvWRQsc+qxUcvXUuubEW7u6w76Lm2ur7bzgFmK8BBswUyGy+JJVp4nfBCS8fx0/MyD1" + "xAQkIhrDx5JeDEt8UWRDUAOMSSBITzXTOhENH5noHBl+qxaD/BIe+hApioHljm2PGjMH+cPg" + "0aDnPyB+mi0zzB9EEGiViG7/RwqDtAQYMEfxBxGRrb26bDDHaAqdMg1OgOJ8KV6oX2AUk1Qf" + "nUHQ14rCnJuBrOo0gcKgUIIVBYaAgX6FGfYK+bn4L/HgwEzDH4Q9tvxu+uctr67DeEysf90y" + "CSX0ITmn0qYy0zYRJ0FrXfKInoZ/lPbsVb0kcGIx9E69xnFG/oNL2WPlSdjCNJNd1u/lLznp" + "Bw0d+6LwK4Khkiwsy/vO8OKxfU5LWCKAg07/IFr8q7C8rW1h6uz2o92pg3wBw3C0+K9nii63" + "rq1H1WhiHMfqRjSiFKKOpVecdP6e8Bh/Ap/1tHmdqHzvpdu8cpZR9utQdH3aN6aj4MsyD1Pm" + "zlf3vZeA+p/XJ9jq68BW3yoBiiM8nv6qFFPU/6DbyY3qhKntruNG3ml6kxUlkgizzKhYmXqm" + "XsL1CuFZZNWkdVDdwdogc+NLD/ab7HopMA2ftjskHh6Ah4kEFF5eAgi4nQAIxJZeZ/Zt0dkb" + "eAjLLvGzga12zxAoSGmW9Tg8WxVF31xcbHhs6qnjI/ULQ2YZv94/b36b835knSV1XS4+0UdN" + "yvoYdtO1r/zBuUJEVMODjuYpnvpryqmjhamcc4u0AE4//QMutuoufKHm6vqFfvzPs9fSWny5" + "t5VtP1LKerHg3vc9ArupeMalgxEfLE/sid7xSKkmSOl0xWuJcK+G2Hd29x+feZ6KTaWZPGvT" + "GeacB6YFcNIpajFM3PS9/RBQfFNjG4ixMwUFp6m1simSoxImvTpTlKLI9XHWfLa1sLAEqBXU" + "ZyxWAgT/oEjZEmlbtcQDA/+KEoB/e7bnZo8FTptPXzVnS8NpAq6t7k/G4KqqqGHw2dpuvc/R" + "MsYrRFXivA8hjDRTIwnXUQG6etGVmGdfr/evCC/E19zsN5C5lRrUemYK8vqY3gN0Ta7vElDA" + "aw8Ay/rscLb4Da3Rs7WuWYkZiHacgsWJ4hjWXNzREWOxdDkhE0Tjx0C3WhoNB26QJveBFJGg" + "ESapjEm6w8QeJA8eJoK+QSx6znTTdJZAAeYE/kMKsXqxSRQ997URQSIaOmYu434vKUd1WTbH" + "wbJQF5OyJyRrTNqizjRZPKij26RzWOQjxavg08ecF71j56vP17vmdsK+HbVKfcDTcFAnYUos" + "rGMJIuCnEgB9GzuKrWOfjS30c9mrucZ2YSyHvDD8TyMCSLamDcjeTad+dq4rRpecy/G3sDhU" + "kWvYPnmn2jWv4JAyDaRRv07Zg/lde3eMbvpBy1WNQ1+5XpWq75bWtpFaAgt4VeEPD8aMF2dc" + "UXglKQwmGBVAGYh17EM0jqKmkHGTUpgZSqkRNxPk+QuagCS4vaKNgeKY5mOmeJNUGsi9Rs/j" + "VbSI5BIEwEzAH1R7/jXtY0Na2VzT9UO5AftSkbYybnNzGcn6ArQdYYpCN7aAxAp88v2M1Vdd" + "pu8Gx+dgu+UNL+EMTKNa9fd+vr+y+p5EFaWh2nwgu/OLwNh3lfJVNj+EJgPwzNxHUmVtyztl" + "NT8scWoEcOjof6hobVqsgk/B6rEJWJJr7Ri1NoaVFnHPpUXWMQXjSJj5FyLKl+w3g6z96Ngu" + "2CxA7hM8K+cPXY/oicb4PTDFmTjKgW9BfMThTPDzX0i/X8xmIuhavt7gzdTSf7xa/wfeUnmO"; + + +/*---------------------------------------------------------------------*/ +/* Auto-generated deserializer */ +/*---------------------------------------------------------------------*/ +/*! + * \brief l_bootnum_gen3() + * + * \return pixa of labeled digits + * + *
+ * Call this way:
+ *      PIXA  *pixa = l_bootnum_gen3();   (C)
+ *      Pixa  *pixa = l_bootnum_gen3();   (C++)
+ * 
+ */ +PIXA * +l_bootnum_gen3(void) +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +PIXA *pixa; + + /* Unencode selected string, uncompress it, and read it */ + data1 = decodeBase64(l_strdata_0, strlen(l_strdata_0), &size1); + data2 = zlibUncompress(data1, size1, &size2); + pixa = pixaReadMem(data2, size2); + lept_free(data1); + lept_free(data2); + return pixa; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen4.c b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen4.c new file mode 100644 index 0000000..cd42166 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bootnumgen4.c @@ -0,0 +1,819 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bootnumgen4.c + *
+ *
+ *   Function for re-generating prog/recog/digits/bootnum4.pa from an
+ *   encoded, gzipped and serialized string.
+ *
+ *   Call this way:
+ *       PIXA  *pixa = l_bootnum_gen4(nsamp);
+ *   where nsamp is the number of digit templates requested for each
+ *   of the 10 digits.  nsamp can be anything from 1 to 100.
+
+ *   This file was generated using the stringcode utility, in recog_bootnum3.c,
+ *   slightly edited, and then merged into a single file.  That program
+ *   generated a pixa of 100 mosaic'd samples of each digit,
+ *   which was copied to recog/digits/bootnum4.pa.
+ *
+ *       L_STRCODE  *strc;
+ *       strc = strcodeCreate(212);   // arbitrary integer
+ *       strcodeGenerate(strc, "recog/digits/bootnum4.pa", "PIXA");
+ *       strcodeFinalize(&strc, ".");
+ *
+ *   The two output files, autogen.212.c and autogen.212.h, were
+ *   then slightly edited and merged into this file, and the code
+ *   to generate the pixa of sample templates was added.
+ *
+ * 
+ */ + +#include +#include "allheaders.h" + +/*---------------------------------------------------------------------*/ +/* Serialized string */ +/*---------------------------------------------------------------------*/ +static const char *l_bootnum4 = + "eJy8uFVQHUzQrou7u7OAhbu7LNzdJbi7uy3cPbi7S/AgwSG4BAvuENyd8+39n3O169zuuemu" + "qa6prpmamfd5kVRtfUwB2pZu7rbOTgAOJGVPRzNLN4CzFcDF1gcgDGBnQ0ISd/7/KTFz9rF0" + "/6+IDel/VRuwGQkAfNz+Z4YJ4Pv/ZkgxqsoyqEhESBAQEKhyspLq/8VxCAhIJsj/EoiG0AHs" + "/wKii6yeO8T/N/LNaDj+C+geUroeEs6OjpZOHhBO/7sZtkc89o7/1mGRkwRp6kbUZzc7HUhj" + "ReZW7BaY7JBZY6MsBYWj4qJIbuY+b4nN8BBEA21C0NDmIjI3Sw2xUXBecXPmoA2R0RLhsR2+" + "grn5iWzbOogNxV3uUelb/jXW9PstZY3UZN2eKJCI53ncey3fbKvB4mY8MGjjbbtK/cJ/yBrj" + "YQjWYfinWa3YqbCtRqSrdzhayuHZOXDMhj+a8MTwJLrpnHBWYV9OvLMRpTHvBchG54Wvf/UN" + "zmhbHDhiIZnsy07U9txvfpbmBvqt47cl45WdjUpQRiTPRASu3dWdVUioVXtthUz9bKmFQhM/" + "s9XgisnSy0ADehGR13qt62Jp0WEZJwD4sMH294x74Se25iIy6Q0RnRFV+GZbqU7UPlCkotQX" + "ZbEK6sZCfwUlWcsUndfry4D0rDFBFM/mH7Hn8o2jgWuHi0HUuvbILf3Ah1bGuwiTiHyixkkR" + "8zI7Erfhe5O24UpxYK5fNwuHNEsUWuK2rfA3g9FWm3830Z37gvovmMjr8bTQERsGF04qBhe4" + "uOK9IC1au+NvhI0LkEDVgku8f301WnoM83RVvtEiuRmhVrXiaO7KCTNw4YXV7uNbkjqj20Uu" + "uNyJfCFs+C4KxsNMn1a4P8KDhzezkEXSM/I1A3x2sG04979sfL8XsDanLrTuJz21E46qnwZ1" + "73lfOibw9jYrdQriTrPtKhEnkzgjcSiOlaDvdp8UAB4r8tv5xwe8Ys8uhb77nrhqd9wtL8cA" + "gmUfD+fcBqr/zQQPg8Z3MG36Uxd+xoS/7GMPkONLIdgeG+1uysOliu2lF4Tv5L5TyaE8MMGs" + "dHLeiBPZfTCzs0omGktVIN+68fbEfjL5eip1GwR05c7it9DqbeOUwejpjG0W6ERZFHXit9TR" + "rSQowN1iRfxrUbCfvnUVRAIpysWiWeO1nZm4RY1zT8H+9Yk2RigJxCDNgueGURtPWe9a8VW4" + "AEJtEpNeXrahOWn7SvT6HnRk+Cn5Mgi3AJ6KxQ4raKR6rSr2aLX9gw26oayLcUb+2rjEcqq9" + "X7GnVIfMZAMc0Fgk+ny+7SvgeqSXuUUeMg9xJ37BvwwNH4g2Vrlrulc4Jlvva/A+H3X49Ptf" + "wAm1kYmbGvzhr0PE7vPukNkXLJNOlLip6nZpJSUGRb1CQK3xzgWt1foNI/3IQ+Hj7ANJnREl" + "01J0wrvtKsLxVzls4NzyH+xLI3iEEKRWiStDo+FZv43/uPWlji1UM7zlz88AHV6l+FIcGpQY" + "yz7iSgZzT/S7TD/nWOBb/MDyLXRIhQqZJZQT7KVBZQHudrOnYe3qpwmx2vemtLGIx4qjppbM" + "cVLIRnK7EadKUHomLQG6aLpYCvIEU0nQ5gaqfUrwmx4g2O+VN1UFJyRWaQ+HMGvcaMM2OmcZ" + "RQt0Ta3fog0xqGeWjOs4GNSokJNFFTEkKBtoKl0uhplMxkL2QpE9opvLVn2P4iSXw1pkM0Qp" + "N1PV9mfHmQSoGL8cDrh7LBsKaFthyEtAXgXq7smG40uLMBrhre31OmOOM0eRrHg53AdVLQWo" + "i9X2rQOYIr8joLxlw6chg8ZKMrZOKogbxdZ9GznPusfW9XXzw0A/e1vEUdAto5yMEE5nhrWB" + "0OLKYA4tlb9gyTX53/4S4Cx9rgALtN5J6gkdrHA+ihfGPeNo4xjgOu1wPQwe9Mght9pVGgNm" + "kOZFpQXA9VbEucBkXEShicjyaeatDqZ3XoBkxqJQlpXxLMc3+Bsv1z/9jcX9AMcaqxhwPahz" + "+aekSZAruqTTWFbLRmjrOYFCK+MwVyaMC0JzpJdyjCoF8TrF74NuLjsCHS1+T4sfdPMUl8rw" + "9xEhIpiXVmnxGnbYcxbF6j+KXNEVo+UEYGbKWIvMbRLhhm9CuyBIcEUE2xFkBA/M39FbzCyg" + "1ZjecfrLIKPgNjjYbDV//VgLaYxWGML+hfqzfxgeud/dfAoi/3g2FO/M65XnKqAV/TwhwrTB" + "WqVTXzSBPK7izn1+wQDPF3562TChp1AEL5D8Mk4Es6HKPydZNAlb0TbOtjSzz4hTIQ+n1CtV" + "ZDpxvmhoJT4pE22rJbNIqpS11TXMvgoDsW7YSIE1dAtjX1KT5CTHlFy2aL4FS62lR3SIY96t" + "R2LqXgIH/B6TKwluhRkadAfdG7RgtjBjzTdGByehMK7hNF4+Qk9cCreoXO1boi784D6t3sbS" + "FWcJf29WgGFksFcC4wbu6PfTeO3om/d0VqgJZORPISuFFQ0gXihtUa6TUmX+d6eXUEmaf5OP" + "YfdllQhvOrCblnSK3M0lpU0Q05XJsISMV4kdZhdL2HoxtLDOdU6ms22pxyzRTUioO4+n7tUC" + "DenPWf8UkiESSCMij/RB7GvqoQyLlDjuFO2vuR7epcKlWcDmJq5SjHzGcdFlh7HZsLlWjNyZ" + "fXeVRW4OXg9Or8tyGNJcqVEvndlI4h2/OP2dRds6UhBUqab1C/AnUFI4RLilYiz28UIxSTMC" + "PaLU+iybYdoX4kSTVGRuNdZavFoEW4CiapfiE1rLYWt8ihbIclB4o6PWk0EYRs3h15lgBFPt" + "o4ChnL8J21xF+Glov2lQIkoROxwYWtb1xF39+4s3G0vOWSCv8BXsYa0Ofs/HXkUbzJ+13yzz" + "xG7p17WXhBwWLkt0lTtx8pZ6jXfRn0iFYx8Znt0yHsIr3XL9ZmNhIytpVLvhm0zLFRdFplfy" + "F1Fbv5l43GUNzbSi5rJDFvcg6kSJCjq+wIVtAFRKGnv/JD9qRVhKcdo/IWbkQnVStNxM2PKF" + "/wNqtLosSYBdQZz+TdYGrzh5GngwSUD9w8EO8UWAIb6adRwPqwLKVXoh2+HZPgfoxEOAMUyF" + "K+1NkJYyd8iKZ++SJkD5SmIFTB50sanSYoUelE0KP0Vq6WdYlwloFq8shywfqBuFzj0vDjiI" + "xPaOS+VmjrJL/wHdkW1Bjti2ElOJyzvJuWZVo/dB8V1ug9k61pcQxt5Sew+/RPdFlZZVdlS/" + "J32qw8KR7ipVCPNBr+Xb/oPZEbap8a/Wt/4YA7kb8W5vWb8zZWHD5GlGt2+kKA62npRv1XV/" + "I3/KIJvG7MaSWVgrx2JupBN3aMPxxIhTkZIe5HACKMcwni6yrI6p1/N/ChIQhEDzpjnV8TOW" + "nfR2cyglqRVf8KFIaJXL1yBlAe9/VNK62Xopv1KF0hQGrsu47aUY1my7t59knX1I5CU75HRD" + "xZL7PvrHfw//KXPYeRCJgrkAdZLSwsU657rL5AssmLhv+GD+q0WXkhC7P8RcoLObNOcrZiy2" + "RZmwdi6gT6M4zkt3pZv4mGKlRgQE1rmviV40qVa73Qsr2iS2fWfir0gokW7970Vf01+qsV0T" + "r0H9OSAxXFS8GIQQc35EOIxBama8DPw2QcAcGkxxxO1a0gO/NB3xS3XRfEczmqK5x486OYe6" + "OWZc8Q2NHbnYPHu7V7rcfgM/2jfK4yiw5SfgzyvMT4XULQ9f8k6+/Oi6GeXlovvChhNlbQ9q" + "nGBnSqw9XoyYwZCsvX8EuY6N2bgE5A7LMwbDUebus5SrCq2gzuKZLC8OHnN5m1xCaxu6bXPd" + "HrB4z8/jVZTmIZl5cMC7iPtGp4o8zj/OeGqFu3E48auW2H79YLwQ05QL8ojv1ziW5LaKeuyT" + "JpAjX/i2JmLqP/i+0kOm/BrMy/ndx9HqLff0JiQefsZxMBBcstk6Obvad/z6IcyvyK/MHuZs" + "mickQ0rbNfeTOKN4zVX8yCpVICh3JSu5+DmEokQdPhUbUZqBcGguH72iJuw9FqJRDzFbgwUI" + "7vs1vXpWSY3fg+HkRw0O86Mg3m8iK6fdlQ6iVB1/j7UAxM5/chBGBNDpntRPYRgrLFkjKJDm" + "JhvdVhbzfkJVs4wfoYZmHnxXmyakrPLT/5xNLlD6b+OI1ZmHUx3kVN1IxlU3EdcGM3UAWjPk" + "pHmSkDe2t75xWX6aXUSFCsM45j+GuQrDSZwJLvb518eYxzN+49RKKke0hiKFjqh2FzC1Dqmd" + "1jHoo2nu+g2v0yu255XvL0eTlPPz3WOVFh0xWCIuvcUp6bnGcQytrcX9kTOQ5nyDaDgqqOGK" + "w5OTtG71QMkGHnRAJ65LHzk+EmdLxN8zxaP5/MPUMy2UuTR0jY0MltCHn4J65R8PF9pTYy92" + "T+2k1/9MnzPTfKYJzhGpSARRQkMemXHFObMxbhsVa5E1wdExnWPr2uMgaogqk4c7uI8Y53YX" + "cLJYcf4jO0a31FA5H7ifMeK8Kuk0WCO0JVqj5X13lO9RVLBXWpjkcaRkFoZDRPu5Wkm6zguj" + "4XnwOCs/XFAYbIxx25P8jIhYSJVysKIi6w7imwyM3GkNjPLItKCVLHAc0jOhcKIg9i2MuH5Y" + "POu0WtNbTT1nkf3D1C/k4Mhqn18BJV2ckvhtiJPCjREUYBOHTqPzh/QDfV2DV7HyqlPDLWPy" + "kd7DZcS4rAh6jRcrwkc2LtzgbkMj9ERsNOJEKmbOgNGqQH5pf1aTcgHcF9QiXQHhyt2SXXbQ" + "AZOIvyE+SJhaEU6a81sUkn00YR4tz5Dmf8GVnJSyZIO4Cfh/Ixv7/zVkQ9j8H2Qbye5NPOjE" + "jWgUM/kDKuWPxIYfvcUlUW7SfUkZ1YOXjkuAXJ4Y/Om8g78W8osgy0XdGm9nmCIBUoiLKVrk" + "+JsctVb1q7XjL1RFJa1A6tvYHtFjdXXz+kIFDfcq0fS+5fYKtva2e3VqTTl186qruPu0IfyJ" + "P6sTQFOMFLXBA7HPveuIHaOoxj645KlZwsK/lN9y6g6mCyywvmnEByjzSNuy69sxjItxyJI6" + "bFVHMHdVV4fpOfPhIfRzgXMJBGKwfmuUrWGJMmBOxdq/1pb9TfRKKHNhz/mbZlP2LTWlzMFR" + "4VOAnpXSLaGudtF5vNUgLLvzWDZ/wifn+DzH1R70baLbCQ3IqPYXLnWeMCYo+RuiF0uoz7mp" + "5jmsMpZYwADY3zUAM/Rva8AKy4G2VzDvcyXeyd/wbYGPoGTn61j6Ps2UPo8HhQhys9ZY7zb4" + "gR4CH4Q/f/bg0KCEFrD7MVMJrvk77n+jWaVD3CYeaC99aUNdTaPmk+OFQxYmSCakmsHwPvXt" + "proWCKWpTThL2Li8rW4u3Qimxp6OVKq+OAHZt9U4CLLjNZm+Fcgtv/mC03lXZIN4047AB/YH" + "5Dpy0z/+4N8lc8kMTx+4teKnXq/bL4EDHnLsgUKeCeey/EZp7RY7TgrWwN9hRzMDYcCeIZnX" + "TN5MuJjpcOERDO8Fmodnun5MUxq4Ty+0sKpwETuLX7MXOaAg0rwlmYO9+VKMpSQ9Q9K3A26I" + "8IIzDYOoPso0hWn0ni1OkSX/lH41Jo5/Q4ICR2F1MBIcaeYcyRsLjWn70ycGG/RTv5F3CRyt" + "UlVLvIh2exxvABu4poR6iFsF4+SEDMDdPEXp5BleC8pqIp/Z5NMQi4q77FbkbCssY5m9f+PX" + "+m59cNjfmwIjz5lkpb+Z5opSXSxKxiO4C3WsMJdayqLIRNnuMjnvCAKnvM96lYXXP1WdzPGz" + "IeWWWY/F5xCoFBsxR6Pm0oC9/oy3pmecjGofaZhTBhjR2tqn/dxPWc1ixTsz0KTcwN1DhHgR" + "HkPSZ6bh2Ek8TwFz69uoVxmpOXFVVzhqSwqQnPjFyaRiVbqsdJ0APvBBEQkom4SZ9WBtsqaB" + "lYGyqlxFz1SRGgokDP3zv1pPkl1P83NRurtD6MDW8qDeh3TVafQlnLh0I3t/lVPPOjWWWJCv" + "kOAgSVuDOp9kB/12QJcrjkv6+XEDpJdMW81PfUb2k6US5dnWwLFtfwJqitx64WPQmhHZqnSW" + "cNPiasQ/fCsnBuM+mN6bG7sz//ECkWbURHazeHI/1PjT3n2rylNjW8+fCm/YqD631hrQB8jP" + "peTJGk3kMzRE2CzFpWD8lDIOs6mY8yUgkAdrrytLwP7hI6JHjpGuoOK4lGs34qNqTtosVCk7" + "n/COurvef5ru1164HFfJBPKbbcxmgrhHIKsZXVUwz9QwvBPi4cs4F77lVjLlz1Pf0FIKpgb2" + "rha6yKkGL3XN24I9iMGbMt+VQxMPI7GLe1xSUi2KIOR+wMre1xH43DDE7co5ckeYBSeakeG3" + "6LZS/RvcNGQyfNG0SpB8KIctyniF94Bw+cRUCvvyric/Q7eBrLkHH4aUx4wC7Ezw/Cj+hZXi" + "cbTPkRAWiyst6RplEXT50pxbaqeu7N3gsRqaUCm+dzrXi6JzThC4zQhyJ56z0zhyyfJd2MDo" + "ZQvicn17BBLRS6/GXMl10L5QW5DVCwP2E1EaAUqb+pvz/YnRUh+EE5kW66tWZYI0LdSV81Pg" + "RSIHZoKGZISFxtILhIUX6tMMBMm5x6Cs5nSf1/MYn98EZbt8zTAbsSvLo1zqFvb65nrE4IIP" + "cAoxjZ48uWDuWnr4z9cOPXEKzYOCeh42s12JclqviD8QzELpyALwXvwPwuHYNYZoWkEqMhdu" + "xtzGOhanv6ygtLcXuwRb3ww9N+hJ8ZtPbWWLUCTe49f/U3y+jCnHUCjRWzMpBHpZmUpIHxAP" + "YH2mux476jQy63moyRvO00jsbPdx8ds4uQ6KfX7bQqowzMe3D1uuYEpZRoCTSgUpYiPluhZr" + "6U1dJAyij8ogYfYjsO6ODtFgQUjlyHedNBttwqlA2SCqdB7KAP4Geff707+ZgUlddjjJX73X" + "O0Nn+mykM7G7S10yjHDofgJl9YP5XPKxLyIZqj39lj82mvMztH5vN69IEm+X8u18qQT4SroM" + "ZiNH6yIbrpEDvtVrmVzlYfpOvWSXjReBsYO9EmFAQ3hZvTJLFjUwCWFaSiihmHrsTcqc81OQ" + "ah0UvMTgdLHBUjBbNexSiuHGq+yCJh4B1b44UXJoOHIfFRqMwlqACI3ylYfEKPPaNwlvUgOM" + "pXIZz0iFBRFQDumukKXEYzuuyl5jyi44XjSpSKpHmc4RbVnsI4Dm3wdQAUJG0js36v7GBjWV" + "bBu5NGjzQhmd5vgH0fssl6T4x+LCGrEwr9lc066mm7lRaMoU/qIk0Eme4efRAkdmgpy/GA7s" + "MCCV+KqusUG5Ti8CFv5VaVZEC78F10XcERq5r6IoFBvtvCeBbbF0511e8y2qtXXELPy24a1Z" + "TTgr0x7pyPHll5i/9T8JaepxpGbmW8GCwaH2xvRWfVs5LltVXq/QwHHC03TNDlnIadn6fO1P" + "fODHVYyQt5JP4n9f13krNx+NztEadpd07HLpkVuBdtsX//xHTACfGNZMjK7OtZj6QSZVOwkZ" + "JUPS/fkdWTUO2hLBPPS81PucOyAp9SU3poSXM+eHaAkEpisneVdQ0BRKsgEeRupr8zXwzxCZ" + "NoLI4E5a5HBQqHXdiPUjhFounLGouqc0tR7jYsdj6+CKCgoCWeXe6P37Wf3aXu0sMiF7JCae" + "oOjNtdY/28YOLDkOcoZ4om+06rKxP6xNBAdHX8HDGSfu2buSJp7rya0UI4kXR9b8tAvCSvrU" + "dgOijamnAuyN+kw+9F2t68smvRVvoilpfz4l6Ypmoy/A7dh+oqrijmrBcXXor+MooktjncEr" + "dqqX9/zZmD3X0cac/swdQOeL0NwfEHPnjRZUSUdBu5JOR1Eesi8PjV+QdEIi4FvnI5//Q2Fx" + "/F9TWBjJ/6OwLPUGnO5k/Rwiw1rh1JaIpiiI4s2Ovpu3VtLpjpD+dyz4z6Ew0fjsU0cY0Y9N" + "BIeQ4HlkpmR8hOuOojA3hNEOQ6WhKNHKTZ4AW3O/1epX/RbrFpG51FgHnomaxjP7GjXL7TIG" + "f+Lcc/e9FV1ray3Veg7QpqmGtWVYt7afqh633Y5MI08NWU1ZvM5k+9OG2ktna5sOstYLg1uh" + "nAjfeZVWJp7jiuuUEa76RQjKDHPIPZXXWelvp2CfiTb1GtIaNpW2dTc3A+XT/eskWEStd0rk" + "hdfNaNuOIdFjsmMG2UYv261OIQaDZAHl6IBAaSEMhwJ3kTM+R4mouMs/gYYT7BUA/hwSNLH2" + "M0hZneTMPH9KCTxgFLHBIRf8bfrd9/F9WLnOQtsYaFFMRDgJOIWDZI38Usd/Ab8JaQcmXGHX" + "6NDq3W3SZp4ke6YFfuYRR3zdmrD1ARqUlEavE4jYxeLyIa7cW94tZNAary2f8c/D04Kmqg8r" + "OXpiHUX6zGhJbNynzB/8s/XHGUxqGgmw8Bf39DUCg39LvF2n4zpNXPJnv9uScB6RPgg7D16m" + "4z5yz84fGfmNiPCfODue3To9lb6m9xdq8slhF94Fm3gL24dm0dRofCbURHpxDRXx8PhzT6tV" + "62Gna2hodf2rVv9WX69jIqv6jjobOHBxgKp2yJjyQuh6Fqb+hV4N+1K43UeF1H/oirrsWvn+" + "E30jSpSBAzOJ+RYLPm6sjEgIZQMhIQqS+aAYcjMPQRz+DNbQawlKU1zPxN0ABgqkWzWMn7Ej" + "c7U8qQ+zt4SruReyRBnnqNuGm1H2j28IhaFf05N8pWZr1PfvA1qhR049F5zRzhgmPixZx+Yy" + "NHtpFyQWXjuHb5ABr6SX6eYyTW7ZwLDU5SOhOtSCx1vnxAchpG13HkW5IkzZCz8HUosA1bRv" + "K5xoonHqYK306p/jeaA7iOlSq+eFn1JSWXJwWtqnY3pCkhLobzNgX+h0E3OQqASzT/oIAQah" + "l/WyghHwOtptQB5UTQhg2ZEr0gIS5SjUo80iH5Q5cVQicy+y9MlPHTeTA+dYlfaRy88ozL0Y" + "oSZUofzUtESBJ/Z2c1IfAPxpMdY3woNy6OFSuKkfx++/8Pjl7GSEhUqja8Zj7gk9NV68IgWX" + "abSED7+0GFq3iU1KKXNpayK5tDx/92sTmNmRcR9CRNIFodecOYMML6viAeIW8kVuZ8hRqDBF" + "9DgvtYGmd/gSF7FcS3rIJiUIe/8gc3FFAxNVwl61WqjQxryJ26i6XGmEHL9/065dgtugFD67" + "1S2KgBnl3XkpgkcoScSNvYAYYyNwLJ0a0IM3Hu/kcCHXH0+AgwrMV82RhPtTBABFCWhVSxgm" + "L+j0FEyv/GE3+a602grvOewI45pZ4TY0qo0RXu9Q9im6e/SHfLHV1Un3r7K5tBExvtivbgSL" + "hVcGJn0d2uouZ1kJGGhLnZEYQAKgumERiKyeUfmlcaByZ4rsXL2UANm8oC5tr765H0Ti9ky9" + "wYTU0gwf8TvPAz2HrDy+U9Y2hB23E5kwL/xnQ8KrnGSYn8T0mk/1S5kJBvXueSyULR0u9BCy" + "EiYqgvSHKrIPjVLduI9MNc4wbqiJ6qs5ni/ZKTJfIQKwNWxmTJbcxsZILwH4ZfMEeVnYYboE" + "J7gwr4nF3PqTqgJunfbH3X2iecWk2KFvx8d86KPIOqlQawT+vKWd4eqMpAxz+PIO3q+pEw4f" + "D/5ZxRPZN1PuxRfcjmM9VEmyMdtNek+JSBVucozVfxctsrCSn2I5itHv+H0Cnz5W78Z5E5cz" + "f4xFaswgH2YDQJoc9LCdIgbI96Hzv+aFWMYV7d8wF6ueKFjIdCV6CA8RB5rXo+fMqUjBfRC8" + "gBfzaYtiyvLQA+jRbSX6sFmuwIG5gh2Me+Jn8EidMzPHooD9kzflDG6jpuM62qJdSnbfb+GU" + "nLcmMiaD46Vu0u5rSxGvobw7ePJorq0XWDL6Co+TQaFltCTPX55QA97EXPEXmGuMWlOCYTWD" + "CzgwOgt7WQI6uKUJNZTJGlm8kj6tE1rM0Yk6CMqmoZPQbj4U/jE+gARhhEFzARekEgr7XUX1" + "NIo4FmUHUBm4tjfzBJrMM9qUaUxtfDW0F48LdqUdfo20wyCuH0OOiGP8yHMnqaiLxiiUDgHG" + "QuXx4VdnhLvdWq4WE0/oIX46IRNJQJH5r2BVQrjfJiZ1gi0LpnQ2U1W6FIu+NBRCt6SYtN8W" + "drpyPmXO28PeB45WzzMyUGIrHT2Gy4itmh6jTKyzsFihZz3gCOuuHAZP79ALELrSJr6Rbrwu" + "0xR+kY6T2gXMYVhWfxaVesFb7OOPvWPRRbtnwBtFvAUbO0Dt9/TAk3hW9PphoyaEOBW9E+/N" + "/PW2+MSGUexZE9Mr3KRDvHvsZ2Wv1c76uaKWGT88Fk78jQOrhtJeZGpgCAvjQd94L7mkXhMr" + "lqQAIShcwqeZqNKp8g8iXINgFMaBBPTYXYmCT8j2CKOdS/4Z2k3VgmlzQ4ayhhAKH1yAWUYt" + "mQgEGssqbpGm+bdFsIcYG+x1iU5rA3i1005Q239+knBecr2zhe/oTmips68UkLR6l+g75mX0" + "ij1u4viJwe+C0OEU/espyVO/paXx72eExROvsyxrM+DbMWUYhWx/CHmHHkY4YFpls8tYzNAM" + "i0hhupJV8zgoNKIwossqxSLGCtNM5xczfCeAlJD1A0rld7GLxAnclOQCa6skVKIfRMshP6iV" + "aTee8BTx0yXpeHxONIejH8SxgPijWwd3YZEdIMWFGDWI37S5W5VfM+fnYExJKUmyikCBCAIs" + "QPwinUJuoIcHXPOUcW6FG9eesQdv9xSlue0saqWl21ey8cJTkt8+PU5wwlBIGVgVVzvoIVZu" + "yFIbI8TRHvMdyBcX4pf5rSQdPb1uiEyxJhZr3nVnzoASCaVw0dYlxCYlteX7K/71T/H/jTJ8" + "hTUE1v04GjKNKQmqNxOyc9mfV2DMgWYGtLjs1Zz3E+pQ4YDg4Pz01Lh4z/NdtV55z2YeoRt6" + "8OXQhEoFwmvbjWv+PoZoE48szWKDsSQ2eMm/v7BFmAt1szUHEX8y6Tyl9N7xkuO9FIWgGOWs" + "mtLCaqhP8TLSaVR7ct5fNxdLnt9T2131N+D+rG0j/A8w9TCfuwEJjR9LKCgGYBJdmNCNKeQ9" + "bqkH7OFAXa65lzr+67HQHkjwHITFYlOYmd/eZaivOMbVRT1grAXRceJvr5+HeukE1tgORT7M" + "b4EFjbGjZfZGujOZV9gDR3cQICsFyWTMfNyjFPg6njSSAiwRVriqIxe3LU2zoJxOIyn3brf6" + "1Uxebk0omXSP5oE1lO/7e3cG7ttEjARqHkKrC8E0vXDio61Fgbway5IqsnL2G3lh6mbiCYkD" + "zjywCw7wijvOAir8DmXr04aawlj+6uFYam18/nUa+/TwN7KvCRjKe5yDvp67c1WUp3DV8zl0" + "9jX2fINVzfIfIs1gvXxI2Aqd3ZY6WORTpodY3dIilfgxfWpaVrfdupCpSXQdAdBk0atWeTSQ" + "WyGeTyHIi/Cc1pBlc5kSxvIDWjziuXdH4J9hBMwueBUViJckxzcoEZndtmmnpsMt/U+GCsMo" + "skz/kXa/UhH0V6Fg9V3t61T1coLAAIz6RPWPfrBIXS5n464e/3UDlhsnM8XYz64bFCkxAjAd" + "rtOOW5CTIe046VktDaVtNWBpcX4bicM2dApWD3JXfu7H0gECcu3eIODqmRtj1i4n5werB6yr" + "N9Ox4sluPEmi69qvHyQrHulSy3juCOW7GcB07Dd2BJcd8ZTKoFXlJgnlrGB/UF5rn6I48Cfo" + "AupNKArLX4J8gW4ezl1LQcYsZUhuqcsbbUpr9jr7A2BleogdaLoK0BE2S3fjdBHoLWXN2nRh" + "7GLgiXy4JYNf3/xAXtQoVCXwfnbVrizurdyZuoTFMWlQr5C79KsolelIeW+z/fYrjHAhLQdS" + "rcXCAkVk+hw7MNQjPyN+mJQYhd1oBd/CTfrak/J39Z8RXiT1fNRwe3Hr235vrDRKWqtM4k+n" + "2zD7fuhcZ/rW72vkTxf07qEJ3Duh2kIEpyMWSlrqzEjn49x/zj2S44HI9enypJMC6pNnSSo1" + "TBSuE3RVbdK7/e2kyZb1ry6C1W9NxdFH7X9bHI+Q9OAPKqq9XfW8mzDjHj1ThP9GQacuJNSj" + "5ljeE4gVReAd+lPzf4560TDrE8VfhJ8LvIu7vcPOg5Ll9pXepaQLcZzWglXOB1x6z+Lr7Ci9" + "z/tpj5qSJtv/7Zshchpig0K9AvUJnqECUNO3DDEy78jz58D3logx+jaD9ZcwkG9bGKY9gHv4" + "pquG+YjzPqZHLm4dZQ36J0O5P+XejwvO9rfqeykw+Zct6D+NTvVi+d9xJakmTV1QEJNR5Cmh" + "Y2mvGUmoWskt22w6GRNNYx1RxFpSpNLMY4/IlO2eJPb4YqLjW5RUYVlaNwu/pX30Jrn8sv8Y" + "HGmC0XIwlnCaYtT/53hQUyzNIT4ziF1dGHVT44uqYR7tcI9NJTgxE2ky8Sr0Fn9vmGmhiYG3" + "+Dt/RXPFOSNwdHPlqvfuDNV2tboBCXxAOt8vAncqco0TPb4jieFyOy5sqlVpilvujsvMJs9G" + "OBHJMw/n2y/mVcrVbml+Ao1q+I2g/2hwnvvvkZLvooly8T1ytNMM81ppIe+DiDhRjQzOkmeI" + "hX1/J/3AbAUsigY0SEKgq77LCTlS0YkRUC9BCxl+hQ+IzlH5uEOxcANdmsIQGH2/UmtNuUrO" + "taOMp+wV0sOqfrMH1WjuD1rIXaPSN8oYvnO0jP9etwah4RqT0pHnghHWkjbujYC3JA4goEuh" + "hVxl3DVSQ/V3K73MGUghrkIwILFjmbclIGeWOFhm7GSVafIntJ7Nw4JIyO/UD88+JgTkta46" + "Lrs+Y4KpKvnaZAsJYjAroAY1wBpnvaNg0O3K2O0QXl8EvvKXIv66xdVmzhW+IdVkLcFVzIYM" + "pcPVz/EOBGX0xwKOnQgOJ+Fj4Zptdqr+Zy9apYlETtHduSsX70RDZvDHGd2Nzc6/KI0+jhYy" + "ynTpT9Nu8zfUXN1+b9jFEgjPrbo/f7f9dwtxDg3VZ6nJtI4bpwRTyJN240OcS7AExGfcnYaF" + "6jpUWNU+JJT3kD2U8BAbMhmoFGSv9x2ohQlHqZKFbz7E0y3LHcpq8XghsGgfSvtTGmXFpxS5" + "N8SUC+alGpfnDOPOKRkN/nbmQYEytkZeuj/xYoWrHHUDtC9SAU1vpH+PaMb1lQ4Mwrm6amDY" + "EQcmkBbKpC5h+HfnX2JxvmtNcN4Wpg2igwJAbq/EB4V6ntLd4m9gtmkVxur8roMq77JIavwy" + "QVzQNW7KkkFHtmyhfkliT/tZNPDCeRZBQop+ykRorxIxhYE/3YonMwGqC1y3yCVVnaR3xvPT" + "zlZKnDGaiyPG2uxqwxXd7UWor/XueIuwYO+wPCSeUqE7oHQUHyLlx8ffeRGhZtq9VKcg3/IW" + "D5N/ENTCeUWp8KeJIh9Vc9IrEsvk8F7ET/ewXbBPjbGsGCbi/vczy4Cnw15Zy68auPZhua0u" + "yq8dt0f026Ak+xqbKirM5F7EqHfS8lvBN7peCI4XcQELHE096HVFSyUtdmWmLf2IFwMaht77" + "vXot3WqFlTqX3E6SFw+88Rq6KMG9lvZo8StZ34/rYOgoDxQKZRyr/8MU4fy/Z4rI/48pYqSn" + "lEgl6AccgLUs2qgOxW+I2AjgbepZ/CXu6iUyTmJi4VMaUZEVAyM9KSBubOrENoW5o7+MKRDR" + "cWL5gz1uLKpYSiZA8c8/XmKQOt3X+HNf9HOZ01GFIQf3i5ndPa+Rv/e/mtuzY11N3jv3Ba2u" + "upqzlokajeDcHy0phdYPdd3pBen5WuraRgu78JwPtINv+b3fe3nVSJWlEw51H+yFXjiTzVgP" + "53MVNuBQh/982q9HsDOoWBxGsTv22skq/RR4nIDswpww2K5FTo72b9i/ZrR4hm3WuJSP+sHv" + "PiMIaAsXuOb/mdN9zmK5f27viyRpN4IEQ5IjQKJfoNxuZdxWU201dLdbxtKdIzE6X0yk1a9V" + "l1Ojtvx7meAIjXWNgSTZQa2tQ+sCNwi7ZMR8GI0x3DzkerJ3QeqFjJHYr7JvmgIaEbbwCR6g" + "vGstov+g4frM4bfR7db57YyEJdZ61GjyjA8QGOXt06D7Y79383sHdlCiN3hnJQzAmfJMfzf5" + "l1S70dtiQbIP4xSG8O9at4BNwcVUUnuduOZR0ETxl5FOpbsgXMcHspkf7PBvdl/m8f49eg6e" + "bp3VPnc98+KQ+9/f29QLU2dJfVRnzfXgA5X/FS11kaQ3s0vy1pq3rZ4I67aNfOYh60VFoMqV" + "58editGQxaplfkFr+CcI/xagF7UPUPRpPvt7TKyD92YQsjPgxLAhlGnIAFH70bVn1MBt4ul7" + "rBz6HpiZ/i/w6Myc/YillKsuR0uVpxrVnqKKJWd9f6uAHCWZNqID/bfija05vNHUam0aJ8Pe" + "sS4BB+dFGOGrWQW74y81AFn07Sv11VCR/5KqxPVdkX/b41Bl/KwA0q0s8MOdtAKq7CifOPLA" + "OCZkBfrS3TC5N/cy2ijZeTA/wK2z8wucKcNZg39+0zWjzjCS0Ni8PMlXKHCN+v5DrImnOrIZ" + "64W+gCd/0M8KuWW6zJRmTU05cBMb8qUZHjPufF6kaeOEl2KCvfcFDTTQDGEFPSxFGmNS7p/2" + "sBIfOOQtaRWogatEj3xhn38b2gOn7g8o+s6GPr7hs8kAMRsGpfcSjRpkgEGFG2ImTVhnKGQu" + "FZzwnoLA4rD3PZqtjyIKwiIh1x6BkLWNvSKMdg9+6Ec+7OkNYJEUsR/QUhm0wJnSUiv2eGS3" + "oI/xJ2GH++c6e8ibSfSAU5qgAYeIYOlXwC1e9IKfiBNTMilBI1JVBBx46EU5f67RusePpedf" + "fd0lquRhTsqKtIBhFqMx08R0r8bGaIjWdzifH8CftcEUqbecZ6eG/uijszsiM99RvG/UH38r" + "GSPNXmYcWaolRHSrPiZtXjP/APDIq9iOectYErkVJBV9QI5Ti2Kt0aMemJrP3AWX/jyAL49q" + "YXnl7qUK/BIV+x0Gf5sL9lvbtTWeDIOGehy9xbgPr6gUPRT2mzbw1U9G9Hegx/a5tWbSZe7L" + "gUQhXKP6yaqXvONnq3HEmIx//R97GsUFQgZywbo8MXjcGJaHeG/jlXm/yLTiOrhaRcFZVsgz" + "78aas1vv8u8IaNC0YJZN9sM3hjaFLSPkP3DasdwKbPfRMHNLZ+2JAbHlgiH2KVaJuiOZY8E4" + "KgfbBtBW1dcMnRytMMzJCf0377UICNqK0CDv0zmsY+QUBY14LV6xMJ1kXkrofhLiEhi1r2pY" + "tWesJ5ZjI4xG+jIxbdQBdnsAnPVPKiYvPVGQdBc0QCd/xXTPk8a4HS22wHvA45SAhnRlqwAS" + "nYVcoJ4yJdPe7aT2bxKpRygpd6gWu4RvpYKZjkgADGkxch0ScrWY3tTJYYHdjo2BQDVSxNHy" + "EH5Rr2mmHuuzEpoC89wrVxREfbCEaNfuirIr67Si/zApRx1WHpFACNIMk6jhbWMny4yWHnIn" + "I+U3+z7hIRnbaFghdTBlyBrds4qt8ZSCQIn83laSxpYYe5FDwdZh5LHgpR5c0Fmz5A4axo2g" + "mDKo/3AsWNJnKkbIBqG1ObErf7pg/jMI9IvUAOwDfFvAMKZdUmhBsgdDyd0Tcx5i6uSpMrjQ" + "+djgTP36dHHshYFKzS+BErd2xbOUU+2mZjIKGHSXbqXkCkIATifA5dlHZmrq5lsKcjUMKWtV" + "d80SS1nmuKKRRCZEl/XmUTYpC66XJj7STUm6dLQ6lMCl+XAFfOf4JXHgBBr+E52UmmmrhqJN" + "uM2ChnzVcCy4BzvvbMmunPCIDMYzndkTBVmBGjoPSJsvHBi/s10iebceeW5M628w5kTajcMK" + "dlhuX4+sveCEq6VJiQ7NPFTbxRmFJIjwdZ7LAHzboihPWqHBaRC7SEO3aHdEijDME8xZz6Kh" + "5zYN/lziVQhMNPpK8E/gBnRaD+97mWudCIYO0lUrWadz8ZadHjCcpkw+gu7+2jagq+A3pqTc" + "AQkdg4OvlUbtGVVDSKmVYcc1VhB9GHF8Ska+JCyb8sE0PGdz0WQU7uVTnkniVybWfKZMZQo+" + "lOIkUh8SM3xDmSiGrwt0ICWmZkzK8nLlouDe2zuo64Y0lHwEKYkK2M2qUutt8bycfBcxlo6d" + "gjmOi94GwI2xBFBJP/xu6YQE/XLllYEk1GWINOpKXZgiINs+wGNiKMSpBsSR7i5vhbeuBmni" + "ScDoLpT2hj2/I4jLp6qKI+cAWPRStFVkYGEYhpBCxoM8NvL9+kiF24Bykqcln/4vO3ilmjt2" + "UtPRXElcTNFlUNEZjt5e5r0WKRB5xinH4GE5jBZvJnSxGTCbHErreCMThFLD0Ng2ZktXJUF0" + "Xi+VrhBIERN9taff0BoERxnWS1XXRsWEK8woLk7gaAEXQC9YcLgzFc1dwoJT+wev74ma3OtS" + "pYzLrWxo0gk1XxTunAQKbM17uXd18B1Q4cMPwo+/vwDiYU9GzkAHljw1fmkR7GXNrI5d72YI" + "oo2qSORnx/lHwgH5qrEiubnBDi7Ss3SkNPCjFobwetkIdmyXesizGNRsJKzP0QIB2layb+WS" + "s5xCqVgjTYj+QgfT8z3qWN2+yFRVy2CTL8VP3mT2i48DjpROGuxQRbj2ETZcE5UOsttbVhNW" + "dzbM5X8F45CulF2CHCzR8G73paMVeWr9J5+N2ZCA/Og8MDtCufZHYBlLyBwX6IESVkzAC+yi" + "Uk6xx6sTJDkog8ZjljQ4Nco4ARIn0DyqHHaQM5igceiCT7yKB5hgPoa/0TzsheudL3ApX4tu" + "KCqEbzHS/8nxcduLdzEYd+Aa0Or1Dr68H/R5UWId30CbUkgiL4PQ96JRM8Bu5E2H9B/ewHDH" + "hvHW1Ss9EiwjhZbQcVan7hdXDbPfFh/2jjbCYIBJkQynyokZf8b3K9Nvh0R8KmxyJzXj8mmK" + "VBNHJo1GwVg0iTmvl0NA5aGUK+KC6voBlvA1E4/E8JEqTcRoDotXxW8yq0NFAKkMWB5jIuOG" + "uIC4U2+mNmBYJSN/iBShBtf++TKEIqgE2CnG1iFJ3biQkOy6m0bspmdmk9HLm1mthODvF51k" + "4yrQAtsG1395JplmUXqYMBG7qrzJ4Ew5WbMgovMfkE4JDkPGYj5Bqtz4cOTBhI7110OngAKV" + "BV34EFu8UlBoKZ0+RKOF8qbEwhNIQ18w80uQrmIMbZJW25LRLxHcROOXItzLYdeaxsKOSffI" + "ZgrpEWOUo+fF2l4UPPjStRNED6AGqCdk4ZHhVmDkyrtCy66J5FsgctS3rUSnkG8X6DHjrPgS" + "AHlsA8yE7Zwemq8MhCvspYQC5EqnJnF3hR4DiGsRzYJy3f3xkCVpuwT2vvMRdLhsFHW8zVl9" + "T7J6YHQWiCHC74iLF4m/UjeIg4n5Ni8TFSIVDtIKt53utZD3dvxMzC1oeBbFjcYOHSTRGNVE" + "cQQxz/6VbS1gYzRj1WE9/phoQprbVPmc4op3aTmaszOQmUkR888g/1XzoF14czg3NkSG5sN8" + "GcNRq8+A6hOKFg8grYFBpd4g6CfQswjHyaLY+WCERyvQ4KQKTJiAVMHH+cmeYKXiDqHEjRJy" + "KSIjrjK8DJwgSCWZuoA/Si2aMggqYclTXvWeUfVc6+BPDQK1RtoL+nVW5rfQIYhCvayiHlNS" + "ldZ3LPa8RdPu192l5hXylREL2hxG9+xo5oJbYsbuHLVYnWigXKNRCfGBhnZQ6otqyXPJ1Vva" + "ZONOoG+3fW6JwHQfj0yBK2iB8xQwOLm+QhvPH7Dw3IgrFQ4m+DOsQxctKsHCMBGxFbdN8ED1" + "MbdS6PzWMM2Fq4HXaT0C6h63nM7Da04Am2qiwMHwNjO55bJR4CbnCNGY1BOmrd1dXIgBYxTi" + "SHrcoAAIdH+TQW3DTL3elz5fRqx4DIOOfITwru+LyTUGGT1ctz9ujqOapkzL5aOnColabh0h" + "Di0SJVU4/0bW/Vzer46eZP8uwUKZjtoxpXkogNzy4A2zoeO8AUtI7HJswrqiKUvZaIsdiZZ5" + "Pns3kMTapHkg8uih/RfV7MiqdOBD+PXoUq5wn+UXkI2pXzoLE4/Ubn9c02VwTHErrst6/wll" + "yYJVpOWYk+fpuwZICOJlsF3a+82dmSOiaelV5Xr4mdsztJ6ZkwggNutgqaZ8qfpTHa7K2yOk" + "KruRuf/fZZqGIdG6a8zKZzY5byd0X8mgGkZI/4zznS30gRqO+QOm1WFp7onu0ewp/w6vIhaH" + "BixYfVcWfJ/32uo2VckIZ8f0z3pdq5kHtQvskxrEaqYDSwhLwcon1sA66J4YpsqAnhjwj4+l" + "yB449C/JLB7AHC+LVeOSUjBmHM7Hwom7kyEOeFvXZzsuPP9MNIcvpKuuw5o1xeqcw1DXj+f7" + "GZzebgtr2lgtcRTMVFW6yqPS9uWTHq9hGYfxeG20LEkw83PU5+9g0ysgra6BJewPU/Q7HEr/" + "GPELI4gDe9vVKbHvx7vh1B+w7GJvKzYFOytC3gEA4UswmllsfxIf9GyzTUEpT2rMNxGapvbe" + "vwtVEGfhbXhnT058q8pP57baeIGI9WFw3q4vbJd30Qyt54Vm+tJ0x0J6Rqdov6h9Bk4TJTzX" + "MuPYFHDn2MOX17UlvGTj34HewF4k9PtD/LW6k16lyp6coYfMIdOCiwllwUuHKqRdC3tgVoNS" + "DqdTRrXTGJqXl9K/phsoXad/EGcmTxAZNKlQKHABZNQYxIFCT+4UFrv21kWwsNycVMS96CP2" + "MCSfzvPSvfZxIXJCDO2nsO2d1wGw/Y0KuWw8jf7bj1SzaLxAXmwkzOEffM9hIEOPartpyyES" + "6qWVLJc3R1rNPWC1QlCNgPqSg3GqB9BBS8pRK+EpMdlbLBgqEzCVO5koxjCcnSWRYURLq0lZ" + "y0+WYweB464TUJZoJoyzhV+TAMmX2JgnMNZLPgSf/YBgT6s6vExB9k0OcrGtvyTVdFwD3/Hd" + "3gzqq6iRy2TavTuAFphv1HLJe1cTphU1D18hZd7hQfDibFAp7gfauN7Drnh3iSeRU6vk+paU" + "1oTilZ4JrG4oj40UH13vcCsxBs/SIEWB8bUeHjCAsbRi3i6E5EPSa+5puSvEykXhnff74oRt" + "/D9yB3Tm7JO/CQnwy1ALsaaaFxZusxXmMSb1Hwka+cdHKUxRMlVbw5dXL7Ps+N/qEqsnCYDv" + "oh84Wke1pbbK/4edwfV/zc5Ah/gfO8MxWyVpX9ZtJO3IRx0Zh+YvbWao+OEM79yWhYSBg7lo" + "NDvtNgrhNQi+WJaFWIHYRldkxjT0k6hP6bdd3aOJSeB9PT2yy5Wjus2vmdEDjo9vQK2anDYi" + "E433FJyvnmgfzHJ12VNSfJ4Ioq8xHKVXCXlpamPz0mPvOl+OhC/ZjYwyruTTg2ut8opEvNoB" + "E8g9DkRW2bTr4LSeIyjJPwO/GeTCgsNSRtxLZHFi5P1LpbX8c/tZ1QdXDlDw4CnCRs78ORs0" + "mEal3ARqWrkZRGiLo//dS4+tnOargf7MmFV2wBEHYop3zw9QK6j3doB2l0XgMZguFV3MLdN1" + "/hAT+yweq6L380W52s441UX0tFmnalRtfkq6uWFauKB746d2f1AMo8r+wt7sOswX7p4eDUCp" + "vhIr4e/EiFNUBkYle6xEASTp/3RZWj451f+uD+YKJF+ef1efBsl36K5L96rb/eNpH/uuo39T" + "y7B0PlP13oU5OKsYWFwQbFVjQ3zZ4MBxm1+rxiskgUQ80sGfUuKpxYVmDV9Ntc0jW4oyvGvH" + "YFH+2xq4K4mN+CneUpPDWNxagGkZmJ1zJj8oKW0ZSVkdI2b8oHq0jcnxOwlRbbmaFuu3mpam" + "kJy5UYrXrWCzPPkoh6kgZZH8cXq7nHmxetUCD/j51LvJf3DlW3RMRDgItq0dIBQh/b7TImvo" + "lkEbY7WEQGkI/3iwBni0a0JUmRlWzJHXbuakNKHshKb4huDEyy+IFx0bU2XH5gPAECykQOjT" + "RmikpexgHDYar3Hj2SGx1MQlI0cEB8zZGaqtRNdhrRdqWVMQw6TEknU/Zgsx1WjkCnDCpM6K" + "bCb3rAz9O1wPctBwhjN+Er+U3bI7xrN/+8lD+OOubf7sABNPIu3yfmZa4qFYqvMCFC/wfLhA" + "stm5VJ/7dmYkqL/4IsMIA7kYj7zYXehr0NHM0Zu8xVqyxuvqWKSms+RYFf9IPGxb/U1qkZfC" + "Yk1n+kIl4/FLVUnNJIct63i98jx30q466F4uqEAHSSqQ33OyLRujTxSOfBsHF8v1flqyyH6Y" + "frEQt9pTiZrQa8HBk3qqjcb+WPzAYa64sbQqttmkEUb8to31+TKKkiHsQBHqwr9nx+8X7rU+" + "K3D1pg2Tcowgb1ov70vxgZYszEy15p6+nIhYxuGdJTDkvIkUdy9s0FL6k48lUDJItiQQQwGL" + "BqqaeBSqoYzMVZRZTgK8jd+iMCQphOhkfEmoNi+HpcrHLjvJJtaT3PRdEabDOJnt27WNWA5R" + "Y9ZeY9UdLfgNtCXzyOq2FMLG4HNrlieKyfSJmoQSnqIV7vuxPf4JW0CP1y/pWoUuLt9fEwy9" + "sHcTyfOBVKz+FH9bfF9woOjmtJiJd9xJa/u7tHE7BbCc6NjdsXoXeqByrzweAxPn7/94MwAD" + "0yLyPB2m9oan1ukcFOjsLSKSM7uq5vxBdCDWYm+Fnp1He2uz9B2m278pH730ZSmo9ncQgF1K" + "Fa+7j7McOenI/K/qbiMZIUQH/k3tH5U4CcGIkJkvVHXtztX2kg/bAfFf6mV4NXNIjVXZe7RF" + "RFxcMDWDgjTZS3HnBrI097ECUV4sMPBN50mKfhMOf7m+p3ktYQ9I28nImnnyoC7ujt+ZF59N" + "sn8LLH/ec1Wc0E+axCgBHWQdS1IqNC/zUiMTqwubEcKMIqhRYOYBBRxfmSjkacPb0IF8PeUN" + "syhq3vV6iYLVirrvZ7UyJYZaaD6YDXzFyISFmH7AjE1sbVBZ/PgldEwnbd1oDIo+njqJTSzc" + "tOIMLZK6Ba3kiIok1F7RjU1BinSUFo1Bwr+gIdLtfb1+uBtGvFNKQtJ+SWV0VKa0WxaKUj+K" + "0j+u2ts2opgILydq/hSnSLQ1l/eY9OTNYi13tpEBnMuAJG+iGNKDk72Erbeb5SA7UI+f4Vlg" + "nH7xTvqlx/FYhHBbk3jYOpPn9qB4NXTMo2voQtqENFVz2P3Mtu3+CrK/TeCCcn12mbXP8uhA" + "4MkR3mW1sEyyVtEqyONHjhmk9xxOQk5/HRqrKHlR1Rd55WMMjlMQYhaj6yWuXEYg69ZpnqRH" + "CJPRgp1sSZk4pbHvq3mJHWa2TxmS8klMPJCLP6DyGixehICl4GoQU4g8sI4Mpp55JJTHEV+s" + "GAP2/4i6dAdQCnrjdEjVR4tLx2goqbco6jqU+gk6hQ8je6Fp9JyPRHnQK5dVU6cWfVzKp5uk" + "UqYVAyfUODaDQBeZ42+xCkh6siYEv64lNnQ4rgdgEgrfWInYdGpv+G+A/ZSnfbRrcukQKzKp" + "jUxkQduMTE/A/oESEVtNBIUWKa+B/34Z4qHuokDBrb9zxfmAU7nL+GAj+0CHMRl/lkWUMoXx" + "R/GTeFV4sotqEuc2Aa95skiaQbSBQwDKoI2KEQz/ubknn9qeP1a0f+sbS13xM4tmFLTcsVg7" + "cBXO1RnkobLwZonqRvPCLKv23L3KExTW7YJ8XIMxZrkWngojBRHOQntqHfM1QtvHJc86WCRD" + "D8LY3Ijthm9WeCGpfuOi1L2pelaOKsElv7oMniVNKQRJCtJXyvTI9GBRjPBEuyevIqivedJQ" + "P0Rai6pPA2K/GxzW7EVI4oGaJSDtrcIHGVk9d+WF+hPZGrAJUaH2PEyOfzXGhAcdoyfTNiB6" + "0fHO9QCWIE9QjcQJI57tWrsU86jlMZyAXPIZXFIAwRis9nlW2R6MJ8kduwNb2SpFTwr5XwOe" + "Z+elqhR1S7A8CEOSZTU4+GacauciuoJAzTIzXUMOpvR7ZHywfsugdBcbxTx7S+y9Qe1ebLlj" + "/meb+ED46c0pM+OwqH6E1lPHwEoc0segThPPaaHj87/7ei1dR/mrM08aWnQVGDfGMsZZPlPu" + "qsPejxNM0SEWZqbXiLJJ7IkGPZH1MHKZn3+LCaqSRLJvqcjU/vAgy65bthT8P7ydVVAcDLCl" + "g7u7DO7u7gwwuLu7E9zdHQZ3d3cIHpzgEggSPLi7s3/V7j7d9/var93Vdb7Tp6qLjfEVfSYs" + "SMBn5zY9hsic7dRroUw50g4iRv1bMOQu6v1uX9keCu2o0aFk1cSYY7IVyh0C2vXbhzzZgBY/" + "LcwDypEd5i7vKPX+7n+7jwELkn2i1o0KpKJyQWxYnT+Xsa+BtCWNwDzgqJl9YZubm3DkhcOK" + "viz7Zqo3TxLbN9f4uQCzlY3BlUHbV+OUwd4gcvZUuehMBjWVVar0pR9ephHBEwAKvj5zfI3o" + "GNI/DHwdKHQE2yEOa+5aPqVHmhvVxtHSDqI8DDwYP0Sr9iP4QOARyyHjMqrRR/NS9PSNm2SE" + "0QK9lR88b1UKsGd0j9CpQn/749OZBG8iU5RgpqTBAWmYixaYnBik3Cz41HwLndh+YqTTWuIT" + "hHMqCXGnHYhnv74gZ23ETi2YJy1G2XS0h0k+rengZDCvpTV0HFeCTiTbr8Qf/y4U/8qD2h/3" + "Szaveg3cLuvf6FGFLzk4Xt21Jqu4IrxPUXkP8ECMTxlmD38REdKh8rXp87rpf8XHp94oJdUg" + "zWCCWLALgy0SeB6YAoKXu4OTomhzknBuH9YZJNVeR9WUMbWI8icz7bhjPiKmxXeeo16ALM0L" + "B6Ahyp6Uauhnsggy8PQ8eSt01eFcdVh0QDgXE7TPRGo75VWb8jBFkoGrS1dKYrUjBUc0MHQ6" + "vJ4FwbiOluCXAjpz7AWNnoolAEp1ejkIGwshu8gtGFcWHylnxqVKYiZvAmOxUhgy9GCEZXc6" + "5RpqlWLdisU3PbF3pThx6RnbtOmfhLw280dYvrRJ+jRkCbqtv4/osQtraYhCr8ZgjDPmOLeL" + "skRLjqJTEWt4vaSwwr66beqJP8dkAT2a3k00n+Lv8xrAZIjk4lMQcGj1pE8K4G1fFjIm4oci" + "fz1wTNUvzi0m6BKW8NsPe+68QOydlxiZP6uAr/Ez34IROn/E5SAZO5H05tbeu8TCz+PyE+6/" + "ksE65W0rQnEJrUSLdqGAMiYv7sdP2J6wIsoGX2+qZCgE2uo/HyEFNoaiYHRVpneB1KGZQAWN" + "5qNBBMh6+d8hacKBf2OdBQf+HMd1f0UcCIG3RM/wDjiDCKjjY1t8YlGeeB1SrZEOKMY01mVv" + "nn/hlKBr0WSg4LORJ9RQbVtSqD+KYMkxpt6vl9EOQUykW1wYgOh9S1UxS2gpPuijmlUh0QBy" + "4Bgu+E3D8ff9JQVurOGYVLkvC3K/cW5eRSU4F2lhv8erWwWcuJyy5k06rG+5uWS49rNdhJE/" + "s1jZ3vLzJvMtTxv3Nc6Xl2nk+7W4BcvVIM1O0frnXnKChMls3bDuU4rI6Szn5QbCNt4Atx4a" + "aJZyt/n9LtOKj094FHukA4fwGRbDZ6GdvazbxZroa2iwLdtnEeiNl9xDRYGSa0gnD8xHHhM3" + "T8bBQluSj9F1P8OmjffFa35u77v4Ju9GT7iy8fv+A3l+OqHyRpA88WHUwnWK01y9AxwYJk29" + "eyXxi062fnwr+VBb6zNOet0RE4CO3hL8qpkuo7WdV0ld7saCMt+8sE8eqPwxYGnfpPNlS0v4" + "ewZZWflDt0gYU8LUKvvLokn7r7LlPi2HGU7o1aVukInQE0teFUGS6+c0s+KRrRbXE1Sgl8bT" + "OwLj9zecOBlhsEN81mXTKx4XLdXlUX9eZGMJ+nNYDNdKLq3EgPm/rInAXdd460bIxTQO4EyQ" + "bVY0Vsmtd3qQXldKSjeuxMDw/C4fvMcu5YF74CkmRY8N1aeRlbz6te+sK5R9xlwQq+mAM8IW" + "x1e24l4bxldHLjIqQp+lE4qr9cHoDoh86YftxFIJ0m6Bkrw9WYv2jwQtnwboUIWCDbN0uHof" + "czs+Db4H8QopWwbcxCMVqreC4VKhKMtmsateiaS48+0nYoxiqDeBj4Iy+n1EYZaJjqizL8gD" + "okQiE5ipnXGYSincF9x2SXIeyLgIhjmRhocfPk0fW256XMS7jrMstswHy0IaAbTQo2YJKWx6" + "AbfHgcozKR982A2e410dfniOPmT8XEieh/az4GfBPiTNhbemRX1yJ9QW4U90h9U6nFeo5Yj6" + "HNqO3lzMg/PWhnZjl50vKqel3uOwICEhO5YPic9wRk+7ahbH2/iLEUL77X1EulXp8UPT0URq" + "RcQfmKrwXzarorYy2HucxVCXlQivwCFgskb7e4ijmuYwO4qgpq8zAbJLgQ3UVt60q64mhzhA" + "JbNDd4IsiORSnI/5lTENPqUgX/IGeP4FkYxSHwpB/t74PyCc+38vU4Dw/yF8xomSPyPiOzZW" + "EdF8FuG9dBgu8QfsvrijRrApLhurWlJROV4kATuEAtMDJMxBrrsa27zRKwYHNGuvQncfHU0K" + "IM8NNDl7SsgKzS9CHyp6tOXHQs36om3r2uFWcLCydWRbjLpvvzKHeam1KES7Qc+qXWKlG+1u" + "hUstLtZrcGdJqchQVm+R2popaPvoSsmAaxakTOKKDQghwkRxleT4KgKtW5RwFXJMxgHRuc8U" + "zDgHQIu/7GF9ptO7/Cl4vhgRePxl/PUntrljEHm+ZqZzmbBga0jdFtd4VfO+i31qP+fJllTJ" + "pI544jsT5cwfsm+aOqRutoWNbz6IOADhEFAm9SZ/fj//KY/5gK5tv+m7cWDY1SpXcH2Bx9qp" + "E9blyuDFm+J1MvR4S7xBPOCN9489/4eI36pu6kpD5jYmfMWN8Av0oiwLlRkKJwhuz1HpKAIf" + "M1UQ/exOQ/bygLxgwnr6JGFHUPK9k9iLKstxAzAJeaflEvM9+ZiXo+I3GBqLebLl4i6t7zcZ" + "BYPDZghQzCFVOFJty7Sc53HyG68AS6PGpbjCuhG32Xs9HZYT7gw+i9QcA69ulHO1RVSfCumH" + "1nJ0O09/smRwjX8YrZDnn37UqXVPGobkiY+ftx3ziW3qnHFYcTifpA6Z4HO2adn9H1lLwrDf" + "UvSElIS4IlklCfn/Nfj8CsJ6iO1TX4xpfyPTJKWQelwqx0hh3G9+Iomlgy5mn/Qh/3O8Gk8E" + "XlbJRufSi8xjELeYgTVOoEIoJBS/noiXtcNMaAa4u3Z9KzEmDzwmhN7reCEYnv4nWJEMmDSn" + "/oJeCwgFGoX8BTWL7aIeMEbT9Qa8SBhqc6SSufLZ5ULh1uBILlbWw6hSae8ZQUaGJH/nhBpS" + "UERHasl4pGabpk5xACqOcIS3H3+fVEQkXf2RpED8CdmJj7VCN+c4VMqDbyKQekjCoa5y9VK/" + "p5CzWV/2+053Q1hsoW85Lps1kHls+mj2br0/uaAsiixsa3c7ZrXCa6/5r2zQZwHT7Sew5TPa" + "a0kYvsNHxO1t+/qrYF0lp4x1JBk5/Y+QcxIZK+ZI5BkjQkSe9q3aRJEY7DGq1/DAd2hdkzxt" + "XDP/yRyIPZnpe4QXF4/ScR96Ad5drdwujploqSDcf40j8baMOgxCmh4qv4ULnoMNu1I9FXij" + "7an6Mm7D+g22xNN0zwO6cJiIeVYoyZKbku9h5IJmWeGQZ02uABxFbX/ODpLT8/fVnGNIqusE" + "2UFmJ0gTOCTizeaAHkxv/JiH6ZvOUA1XFByTK24cSFfmdIuUQxDFpCF51rUggSs5NgFwYke8" + "P9E1JJt9HA5mUm554hnFi/3APlX8k1PU0mDU+UZGN2TH0gLBm+GUuayaao031JZqhm7xn1PM" + "NkI6fuLOkRZ+CUSPVZSyTlolefp50Jn0T2i5qmNU03kJ1XzIH7/IVs34rSdIcsOfkxLOh3HS" + "YqtNLlbpAkP9PaN5YjbHPQoinWdDbdU4X8u54JClDvhD4n+6I3ZLjyGSKVv+5FtGGAqgrHVc" + "8J35xDI6jLwSMIiPeOZVW02kAkwGx0VF7Y8nHciTozBMFLNSkTYjpxdRkeZg0fNinnbD6XZX" + "VmhkY9bhy5Kfgth2Aqenf8ukYNvH+KF/ZeruBlFdtfVq2kkzSY5kvhSn/tPjRlgbCb8oV6u+" + "0KvbpbkSEG8+3qO+Ff8TK23JQEC/RZiE0+HvkjA+hPqM7CoUOSOQSIZ9GprtdKtvQ2sDcrRq" + "ouUe9jIRYIG3z5LhAIZKGwGpJwN5BY3tLhkncjbsaQur3smDp11pniTmgIuy5AAHGGDQWA6M" + "YZYEhXnJPhIgPWVPRyUcXXQiQDEKzwbmcg/IYSb5D40wd8vW6g/uWiFJ9aQGKy+IUou2C2aq" + "O1mVtmWGlZ+Skaj37mJVjx1kElsAGnsH/NwWbn8Y+dhMv7cBXYN82U/LMgix85UCHhKxioLk" + "GZTpOj3dqQwsTcmbptEsNZ3ZHdtYPYrvMlMJhEsOdq7kbjyjX4miutEzySA2cqE4xucNF+gc" + "b8UeaPqQrEIjbHsOOVNISF7Uqd2gIEfZ7eQWZ5eClmMhYmDCTPEM8SDxPKnmyLqB3zrV/6n1" + "gppSQTEZFlETcLoblCegdbU8xmIsQh9eH1rVhuSODLIc6FjletPy0GlZFnCBJkOZ4gWoRLms" + "sUySpoPSTuP7nmvYYF8b1KlEbpMeT5epUbuzfKaOyUBEJ3GooqVLqzIdkTnchTxWA9f9odpZ" + "9ukHrKqENSJgFKcVvybLmk9N3tMu+cT2C1k0fhq4MAo7S7eopeBMjuvVI11dKTiYeJjEb9AU" + "YCIFt8CKDL/1sw0hGzVJD4/WfUZNYyI05gbURtaXarDLT5YYg6GqCi/4LT5dCr69NLmx7Xgx" + "LEu4D/mW2xMrZKU3LQHuBel3bvY/DueyGQpbukc5prwFpLjRgmx0pWNg72siGSv5jX6/hw38" + "p+6VlbA+hKnv+OgIttuZ4aCnvTET1pKg9vOKiQ6pnjtMnhG7dMtzkW7iuO4KrmmfJWRf7Qhq" + "W8HTGusmevLczncDJ3QkpM5tvxTnjKtSJlW34i3YWOPBV7EGbJaZCVdVBgntO34ERDKERa9M" + "+9EqAd504udZUkZpEczCO9169O/hH5LUh8bRGq1qk8JADyobaH3RXrDwnbGLaVo8M/0ByZOH" + "uvH50bSNhe5TzfONz2RNaDu0uMJC73nus/XlwiJFYHjuGk3Dhi7Ja9P0Pn7nZb+p4/QdShsl" + "OnbJQUoEX1fUGOUPOx0aQyH+x6Mxgrr+DdmosQ6E+yYs4cXlsvowZrfI/BorYc/QJu8iiD/d" + "MWH7cNn50lQ5gTBhNPZhm2uB7xzaUUmb0Ul1x2+4dNyuxVE6RFaCQloSzcOsBvOndeF/pSrF" + "eajskaFeJSL+zFP9mU1mcvyz1WJleBd3nq/K0ySdBQPol4Q2d6HlSkZ06mzlqJVznfnlN3Y9" + "6MDaGvaTP1mIPRUNe2oQ81sfnMKiJiT7Gm+KjXOTh+iGBNvlaEzTu9r74VWbnbvEPRVVE3vs" + "WqTpNOEEis3hQI4E2SdjrQ4Se6F8JITgZ2PVVJUvEe8g8800RDPZqHjo/GbhPxKnSVSC5m+u" + "C6HPounSvFek841DgVMlqiGG3Pbe5P0p0Upm8eWp0TLkb3PRZQHccjH5I+OpznHnT5o3/hDi" + "83i3157NgdZMEpFl5G+YrEG/Ps0lDqWh8dQsjOGdim8aKtsY1tTPC/YuVSARdSlMOLZSTKCn" + "NNUn0VmaBe6o12MWvw/Pm1Sc1/gF/VBNIiOMyZeynfqtq9eN4olwc6iV5GKeHikTSv9X59De" + "18AhgWAowVLMucfbcS9MUHHt2X2FQ4A7NzLDCEARjlNdI7Eokb5Yh5SVGLhHjlITma/xHOkQ" + "MiHO6uaqrNrvoQPnnY6wwNPjd7zVpAoUvzYgv3+gQvBA1T0AyTkoGAQlymX2nPCmMKrW6XIp" + "ce1iASaTg8IqbVHPvUZ6Xn6RHwl9wcpzH5hZHJupzNgZThib/E0awQ6S3AORyAqksJOS+CQe" + "XMR4H/8NPf9zWixgBpn4keLVZIiPlEQmFhvW+8b4ysIVTPXK9gNVlf0L5DoTfyCwpwSmI23k" + "tzuEm4MOxsb/7SYmHBSLV/5m49h6tT5rguL6RVwPq2veWe19PPRuB1tG95W1jtt9yK92gWlS" + "PG09N0venra/x4ybQq91lbYft7ddMmJj3E25/ymmQbfN3/kd2nSQZ50rnYnukhvAeUa7cdyR" + "HOWalbbifCyI6Y5ZfXFD3KtPOHJyivZfr/agoFs6nLMdWwKejjZTyXBZmAWYjvuvhhm6i9+e" + "qtx7KdbEvkP+pUQxdroL7iCAUCXk8kflpuYgcNS8FLSJAUpqRmOS+N64JzWgYlw/S2yEASJk" + "9uw1JsBMfriTuLkQCwuUaxi3Ysz++Xe8VGV3uPzpSmOkqQJRV6fi4F7p10hK25dLEKExsZT2" + "0gfM19z4MLVdGFXKNXGGiF2KWbls1jKpb+xG6bGdhqbv9ZkccRrqD5F5ReFrQ/5tYbBGND9Y" + "X5FVC0EUdVwTwV5UZoeOL2QPvHEpW9M4VGXI4vjt1HeSJgL+LWLGkV948XwUm9lk46rGT6/Y" + "u4lwU5lKTSBG4/SRwLr7QFdGN0Ffr0L/UYGk7ErFmFSCqFmA6MhHDSYJexb4hxV+6Bt5As1Q" + "ko59cldXafgS+JnbMRDJXtxw6uYg7UrCRed8xF4nTwKGsaWqeLEv1bbOEtrjOT+t0xntX9wE" + "QuGSH23iFbTmZEv+vb5FPnwextlQWWzrUr8wZgFRiIGABo5VEJj07hMj5Cn0fqtGwl2PJdHi" + "Wv4t9PDZoP1m7EEg3ZWRfIkyu3KhVSL8oO3wB6MZuUEpiwM4I0dHjocBeh3BgPP8NOvUB3X/" + "OEAvI/B+I/sTjTovJcwrktQlpJJletXnMAZJPVFi4EV1WH6jeBkGl1YSh3Lj6L22SZ4Z4Lox" + "eUOMsCbCxYjOn1K6xxhiYsOL1CxzmzBe6ZDr1j2Gv4BjgTssKFLSdp+b4Tv2AdLgz/Y+7DL2" + "roD7Hv4GbosQAvcuUhafp5NANf57tzADVIUnUMTMtOdqRJisZnHmaYhqdMvSe8mvNwW2SDaU" + "CQhOteoBfISiPlw8Lrj16x5JBT6C/WasN/mnPvb91fgDun7r3PR0VGu21kEsXbIVI/w9uiRE" + "FCfnqOo5cp84ZlcRM2EaygmPYmcqpaKzz0KdGOIeJyZNihJ5e00RuX6KeGKjw1ugf6OUvTpA" + "7mcoPKHw86H9BBUd3arPNmdWhw/I+svoyCd6vSXie4pbew7kMiHUiJ/gYDCk2XQ6iH8yehWi" + "E304GTHUjZWbMjnM60+tcDuGR0jgv88feN+FMPqaY+NsH6OnroCPQCIBOyxUdXQG3Xav3qV+" + "QYjw9I2AID6zCLZ38qG0YHQS4nYsC9sBlz+ps+ZT5pTj2/FVayvxDtDGEZ7UNT+qOomzjW06" + "oh9l8QnvBal4qVYJifzGVUtOPwrw0RlgKVXSz0kpiEIFw5nnDLkUz0xBRAVXyQe6ZyHmnFx3" + "lcYYFEGQWUHslKyEvz4TLFKIsLmPu/Oa3wuK+PxrgKtjkM3OFABzcc4byoT/esucmP3p9zlq" + "5pDJjOa15skedQ2dFZB5InenaTfOqk0tp8MU2F/RlX1nW7JGeuw6VPZ4s4lq3rV3uV70BLZG" + "pWTo9Q1lDbkYKGFki6nkY2QIzO85ymnDOz6mbC+cvZuk/M3jYzpg/+2AGVkoiDk3sVklUKpK" + "qQr0/EWedNccUvCLJs1MBbVKKGiVTuoeLTz759mzjOuXdOzTK4Aexb2JWwktEu5b1lwSgYAM" + "esz2MsuRicoi6q3LutXx+/SDzdRm0/Con/AHdOg5Wij0PCW/YN2D8ov4e8ZlTOV72IBN8UKX" + "VYQz5XqfVma3yiwzyhnkrZXOcg3ruxSW/C4NIZkDUU/BoPrRF5RCOdQ9wi0W5/9wI3j+99wI" + "xP/rRqTryidugjL8HRD3m0ET5gRxee4A8gwId4o+wgAMAHnw2ghFMtVPmEwBCVIz1Hkl52JL" + "QHHePMQvLL00vFhJ0tZxEL3mX3wTTHSpRfz3l1ZrKyV61vJ46h6OTFZq7lquhfT5Oq2mHg3q" + "ZzVTa7uWZxOkHTk57YzuNupVnyuJ1JEOKzCHZssKNQHLprxrAAKe/ZtBpmmag5x73Gh1yM7A" + "+9bDGi4e1dqib12HpRxaQ7xYzUEcEfUrBUxthcLU73SR7RcBnliuLngtx5MpSIu1FxGeuMKY" + "HTnL0dmUgOqp0SBUlBUqrl5/3Y9IfJZQ1fCjQQlwRRaLsZw+hu2LlZ5uzuKXssCrlyvu6i8C" + "UIQ1tKudXP41JDDRTI+fJC8676mjj3hk5cQBGX1Dfn3bXDFxvHsKjt1jvccotKthiLEu+HxO" + "sy96+aHl6Q994EgFqVSKQbnRqEsPQiMSGR98be3ziwxVtC5PmezD2ToRlVlPmeMRiu1e5L33" + "9stXyA5PyHnO8vfVNe5iPU9eqvtaih5HZncxyEvfo6Eukmhif7guAkmRHZ919yZTg8sPIdOF" + "JJ+jcbIksSbsmKnS+vH781P0SwPSf4hUv1I1ZAvKlUi1m1Uv638iuRVSa5aPGqatuO5ioh7k" + "iBvmYhLjFXBrwL+lv5X5M9RVVnBZfmNcqfFn93ZCdnOJgDnHC8uXum6TTOI1BqqQMkF/ETQW" + "MXVl0goy02ayO5OIfgsTecimDNH3oGPCE7ZL87CJc8QyKh03LN1rRYqAj3Nrxpztdxz611Bt" + "1JIbjL3sahdNhaa316fp5gl1loOTtPdd8trjpz3LR6+3X+1zdLdTz/U36H2jilBrDOQi6ouA" + "kyFAckp0MFdo91ciaEkr5sqCGu+AooNITb27URD3jQA7SFcJNxr36we9/3i7a7HkGmH1HskG" + "aZJ1HAZH1V1ftTjhOaftQK3K+5VY3IEA27c4/0H2uvxfTH4b2rvTn2R1+WEA3Zh+Tu8rsUQW" + "Oojuhx0rqVAYzBpg+L8JiNjt5EOZsheMNd6v0KxOkKpFBup5fOy9CZsTkv0SCj/dnuT4mb/k" + "GArBtSQZLd46MuG0GA0CXZoqGJ2lAQmSZZrPNzmL9C8fC61o8awjzkEDARY6441/u7CJPGy0" + "lmbhwQ0g2Y6bJrArh7zYHMqnwzt9bg8GgA91103GOMKMw5Dr8bc7HcXbst2MlmE+bTu4SIUB" + "24+jSoAY1k6niSJU9Ess3/oRgziVyUjJ059SGnEWAvY5UBIzl1f6Sp2Vz89ZNe/EJcMGKtCw" + "xJsX4dUx7veTCYrObne02bjkC2uKRuCKoIw0LJoF3e4+FoNLxbsCvneG44Z7CMgXmiNjtMhg" + "IT0EtDQOJ7ThZWHiJ5Mvf7IvTCK8K0b2wqyGBXaHCM3fi2RwjK5diogfyMunoNu8k7lflnKQ" + "6v/2s383kwqHYGcis/vMGREcdjIayYmBHgqC6Q+GPYJanh6jtDT/Unl6CTdPtNZG0mGLiziG" + "Pv0eJR0+wDgq7tc4Q5f7GonPrCBVJMUkfO4gzeQgb2GhDTt0aiVDea7Hc/GMcgMklOtFis0K" + "inLfBpv+7RP3zueNRhqOPI8O/mYj/xmZLBfsJV3N7pXsQTaVT1QNC0CvroKah4mGSParJsNo" + "OJuiBNeeSVJwsBgpYB9LGBfMm8xqnw5h1dh9AhIK90yG1MpmWyRcy5vDykRV5KsU5osqJDQ0" + "4FmovtrfjZoIhdGzigpthZlwj89QPIvnum+V9WDOv3AeUgYjwnlBtm3uDIlOMbi0oUjMFlDv" + "pqTFqG0ogX8yVEqH2NLoxpQ2kN+hYuHklr26JtudXGoQsT3qGZSgRsWP9ItUHG+ngnK8XbgV" + "ZQe5tdFjzBb22J74D2mH9c+adeZ5OT0ji/4bJd2t4cG7Yn2avJ0zFB+NcA7wB2msw9+gO2Bg" + "XwwEnwztiOYDUMybDJ7qLs2zv9xyYwdRlHgBlnj5RcEaeg56TdQnTfgSNRc2NpAqyB3cxZfH" + "ViWDTpFMtkhiT43Rtn8NgYvZQr7b0gmIx6Lz2hqoNakOrCHHSk7AmFpIRg+ViuIsoCf7Qyy+" + "MfgUL6Cd5S9eDJv9pwZnQQulLP4016Zjiww8sXL77+pfBVIrBXMyFx1HgOTYFxSE+HqcQ4HN" + "CIAu7Akolsw70UtynG0SKy1Q6sx/neUnpXRw8TM36xKAIRpGfZ2C8FhFlDmegB4gJCpgM451" + "wG1YhEVuXD3DuFubP1/GztF5oYiihzRFlatfkoS3SJE2RBH+M1ghGXKo95oxAdpQ0gJOCSbJ" + "QKC4spiF6VybeUPBwkaH4VShYXHPRqaP4B5YleQQvtnEeYq9GtD0IEFV4ODecr3n1uGYyvLU" + "MFuGswd9SXb3Sx32haKg1+ovNwRciS3R5u+07dWIq3qaM7lM5gJqPSGZASF8qUjQwknXS2ww" + "rLmIxAAZXfleBXRT1XIXTWL4PtpXRG592kmIwSeadM4lL1swCmHIt1yJzpOt4j5O6nksw3IM" + "S/E4GgvLv7+2cjoCyliahmwPFDV1HsdLz3ruu0YRKiCPoWFhavG4bkgmIaJah6SUGmrWPCN0" + "v+GEmGMCRNDnHi9t9Z2iCanv5z9Qiv1k8viWc8gpXvdXJgP8UTCa7HmvvlhBb9bADX7sYYyQ" + "/ZoBSrtqsdlO0MCHVSWImUjtH4o4lwlZVrSGUE71YIL6GjthMvqUatlU7CBP0p3v9x6fH1Eu" + "Re4UXEUD5KxZiD4Qj1gfKAmkjexlcCNN0bts857RMHkYxHIVqrfe9bZxiIrykkuh6N5FEwK4" + "i4OlqTKWYj8hXdJHmZNpsYe0f9nexODEQhZfZL53yZY2adeiWZCElKNnYUT+gzMFkRg5s8+Y" + "TyDdHFyk/TMS47k6nAa11Hx+czEcSv3s1qotdIExLUmQ4tIYcb115gUNVNnLqZkaBSt4UvK2" + "GOboXhEtRzBQ9vYElojLkVTIJCwoeNESxuBAuCHpRJptq4WYKl36QLJlhohhCAlhnkVCj+CR" + "gWz04IFZqcVIdNkpyb5/jjcKnSZupJNT9Jg9+QqREMDZVLN/nDYsYLvL7XROalWqd8ZKTF/g" + "I8aNLVdTee9bnVGPSyl/3ETAdeA+/K5NIn3xpLQb8DE1/17vezuza95GYaNr1Nb3JUjl+Gn1" + "N/MaV9cJPmec12LnjzbvCBQ/9ko7RW+YKHlOcqV2uNfOPq0UrAYvuuK02qGvpFKXEhyGpeej" + "IVLG9IfnGtKlAOzBQRexAU7Gm9VptMIZDAHUxZapAgk5xKofb5srmXFDgYG3JxZUaIxMuHmk" + "Q5NZX+v0LDetOyg5co3ABZJp8DnNWK6JsGitkIhbSaA6DFF61gLaCl/aCL+KmDI2qjt5t0sM" + "XgVRtZgB6QiMK2MIioeIpUph4N/00frIvhm87JvUlidw0SEW9ghRFxGve5MxcKBrkqzqsa6w" + "y1+yxUp2iZBBXHugZmlwpKoO/geH/XaKjJFn1EhKMLzRUjNcCV0l0dvoFM2Gc9+7zb7NaGCb" + "8pzIFw313danzoMEoc9CA5oEAbjB/eBbz635/WLrPk87lAB7IoUhsgJ2baDRr10FO+z1ao2P" + "jFafUe1oH4NpwWZGSopzekTo573JWwlcyaRp1iMKBnSw5dzYHFQm1uMyEkIKb/iSElxFaB/F" + "baxNcEVSzYowi43TLAgD31DpabApuDuX9DXSxvyI6jcavEeJX1hTKyPHcfyil4WOp5NDkJhV" + "Vaxj+XayIeeDuzI+TFTLNmPo5XZBiNhCLgiLS3EKImUqrILs6i96cYT5Tvx0b94nBUclrSC0" + "D1moP2Bk9ZBatMadp70YveiTS7BUteyOc4tZPo9cy60ZHwcKZV5gda2XxipfVW66vAqolFmE" + "9xl/8279Inmkscay61WFShaBkiEj0Duv9TZBpQGnEc7tIS3RTO+H3PNse+hhMwq+O8KjyiCw" + "IplZRImBDae+i/csiK2u/EfA0cwnBC8Fb9b27CtXe1GJEy4zKrnQ8aOSEFSotUo5sUc4BYWR" + "/95CgHqBQ4+FfsB08SzyZgybFyj5+nDaLyEv5p6OcefnuZFnNxejcTJZ+KfMauMFDwow76Mt" + "jme9CSLtpf+FeCwhuhujEpfkrcJFro7mLh+CkN2q5M79RJdm8eY77eUtE5tXdOwh0kSc6FSn" + "7to3KaPfPg1VslTNTEWOedJ2LpjPL2VJEWSsM0MgupmseSSjrTi9jbG254X9L/JxLwdaNRyr" + "wKX2mcj1etdzh3fEAd5fGGibi9iXP+OcO+AT57wSWwtU6fNVGHgnEO+Ey0eHyfopVTrUQcYx" + "j6q4if2gAOgcnwbrXzpy26bsIKvPmfyZI76gpxQbYJqeRYAo1ipNGzebhh+xoHEsbu20S+P7" + "+O4Tbzg7PVsFOV+yTixv0Q9Lzic5Bx8f+5XO3ZwC6xgm8AI4lBi0a4IjhySBE4njcPxD8FN6" + "mfAIIKJqfpqtpr50u8xhneQdeFsoXVVfLLPHth8FJIPOhOOlkNqY2F71lTTpi0L/T5i3/RmV" + "UuR8Jr8dCtdjzbMYRMTgeQQGQNw64Pyst4R7GrE1uvjGy83nrTjNaqhKKV2Pr4HirbN8ssQs" + "hXXyJbiNbnFS+reFY2RxQZj9pBINteMsLNIUaPt69Thh8N45Yjs/YrbKiqZ98cn0iWa9TflA" + "DrfEQW6kTyHzp3QKo0Jm9t3kGynMyI3+zy1OBq7W3XjMHO96Y0Jpzfgmcr3VjcxI4b/dVmGO" + "/VHqgu+LRaO95L/1LcK+g55Qmr+DP3iBgWKvvPCnmyzfk0sZJvQtcn9fLE2Oi8EQ6Ox6iktE" + "wriD3oFj/5RK0XQkfIv3mMFxuZSxpAIQF4P0Bkl7Y/Lj6hRMrajSmBLGbkKi4niu7a4lDz/B" + "6GPQsIdAOwe3GgUDIvK93PPppum9p/jWAhEjWp0ipEL5l7Z2eVOEwL/9KunbZe91FUO7BMqv" + "9/oMdNyfxY/LPoeMeJ3K/LrgiSurCuU4GbzPmG2FndI6ivNeJ7l3VPRUXjchQQ9btv13Aemh" + "0kTyaLf3yZszpaZ7g7grWvcQjkiZtI81gnzqilSo22zj3Qp3D2OJ9eSN+/kjcdEC6lNsV61d" + "hQwGkqO8jjk7sm9HOeHETMORYtvBgyXHw+omXXfHkWKtc/Ras2B+G1wPqHbL6GTRdmltuhFF" + "cWS9jdEbonBANRJs9Cxj/l8uJtptt9ugNMpI4n65l5HHxJ3lGyKrg14M8Me4nG3IzwcQeyz8" + "LY/osrvVX0gumW+gclBndpF2+FMtiPFVHAIiZEJJ5oI5tKFTPdyF2GMaetP18iQS2dL+vTE6" + "MLspD59ikWGIeDNARprCMGl9gPyvNEfb29w/MJTfAHnffEYIWdLBWCqRicNfvlMhNbFo/GEC" + "iWuc6HLLU4nAQSIppPaAZGtB/whCNsJgattcGd/1Q7CDHiHK+EYlb/4oRi2XivUImhPyt+Z3" + "VvbMwpy9yLro/2FH8P7vvWZS+//hCNGkzRWcUdLhb2kYUt9RcMCmUgqZnii89kC2D9VbDC0V" + "UwgEBdSKLGzICkXeF3iW1F/EyMn0T5RZccxpKXHM0xF0W3lul/vTdQfMXXkvLs9HfW5Xwk/s" + "BtkRJQjKdvZDpSqOjsg+WAVR6I884Uzh4wuQXznOkzOU5x3rZqnUrs+Uc3v3E6KW5EICKTvQ" + "xtL6zmGNs3l/zDhhaHvC52QD334w8y5J/ZjYWHwcRXfUTcDjc3YsZ3bsbrJcyOxgGF+8tfOH" + "UlPCmdABa3Q0gByadsXs9MFAIgZan53MCJ6zjjG0VzuG85XIPbGKxRxCWsUyOILE8Eo7xvOL" + "PHxPOJKCm6qMp6Y2Edgz2UvFMl5RS1PARIbx1j5Q3QlQEV7JVMeM9WPsiL2TiHZJUi0JpkFo" + "N+0B3+XHTpJP2JykaS2ULTASWFLvOO3Mfs7tsaOT7N6liqpEUYzaRXjpETV+zoD8lrwBue8s" + "h+vvi5yYSq9D4SdkNWV9nmiBTS9GuGWhvTMEKGTeQf0uzvYtITLBT8mZdNWK2pPbwXHkRBwR" + "TIRZgCbqxM8FKHwNP6B4wP6ohro8AsiOGG5mz00WWQdYB8CbP/McPN7ht7BoeMLw8xBgo5V7" + "arT+E6Ym8ouMNDNFjZbNGc2UfXxNmJANprMhxPz1qVdGLKQhl3LXbTuk3vP7529v3JZe4Vov" + "U+G+FiniwnVAf5TJWOtd3TEGqSlUS/w+6TlsmdEf788nIbPQDLUrVyhqcBYWXZ7FDnFwL9Iu" + "UeweEcM4U6W0DFiY5WpcjFQ9Qn6LEHUoBiOa+L2guCVLrtpr53j9t28Vd4LG872Q3HfqyZwl" + "CYkI5Tk5TCCLk9WwcWbWTiANDGtoLr24L23Wa1AmhPofgGyEoszFF1/qWDw4ou9vyI6SMxjm" + "ig4vG3ICu4aNimMZXS/uhiKGAhQpExoOEVqKIZoPUlo42GRMHPF+gcoGVT0LtCHrGeUV3wUz" + "gYr+5GabLAmTCcJqXqN7tChYXJUdtd0s2EdnUFHsRMGNDMVmFuJfx8dDrel0YYoXcFFrTA8X" + "3AUfrwDa6KCl5PMpQRIdMi4723dUFpphiTqxqKYNSP2y60HuVP3Wkn7P3KLaACQIih3Ot3mK" + "aDHnKHKDgjVuJIgJ9R5j1KPM/0hXko6s98yVL6iA0vv8w8rfcrTGK5RYCzXzIdZjt4L4iokD" + "t6M4GN6L2spTU/VRM8iaWhYDUeFvw6m62Hm21lTChDLALcsiifSSnQi/CDU7PHYVHDYRaVZB" + "jKULgDSTZN8f4qOGQi8UIuPbnNaYXlYqgYMGcdNjsyf8WlWie6s2wzySFcCiN1zUT/mFBhPy" + "OaZGgHmUb5f+4wTCljSlPlXuVf3imN4/OWJiUBAxrQ/001qfZigYw3RwNClmDFvbUy36HpFD" + "znuQM8ZEQNYb1mgVIfIEK5Q9fzIZfjb4gO2TgT4Zko2tE9kSfHqdewKqAaJD8wqp/it/Bljj" + "Qk3xiXGbVKXlEa9E4JFzCkH//qAh6w2ufFYOBDIp/JCJcX1vRf/0gg/rXLvDhso+VzkbO15j" + "lwNybHRcFoh0XihwXJZUnET9e6YWjXQNIZtyGG/601rkQB9en4h6jdOhT57xVHsNQz2RQuZm" + "CRDCmmToZ0YrE0KHXugeyWREDYFngL0zAa7REYhyVuuwi+29FgxzplJ0xJP+4tOEGktFDBpZ" + "oKBM5QsMvo5GjG75uQy3DrmFNUsBuZaZG38yfmQ3ad7Ir5wcQC9FW2IngqWFjM7QrBVJTW+Z" + "7Wv3N71K6NRUce4PJ9R81L07Em3ZpRUFM6W+nGrWczdM9QmPRabcnd8uXZuQ8qAp73QXWXbS" + "kMmMgkpFloPlvBLIGAC6fFpMdafw0yvMUXWViLeACUXAJLDNxqyWtUPOvVQraNY0BHm8PIKc" + "QA3MvbzJBWMIh8csP2SHSdJe/dQNxS4TnG1339j+RHXawSRqHbFvxKBvtY2fbxN/oH1+Lqs9" + "kmJ85crGplCK986/yfyhNnhPOtY3SYIMY91Wxnvg/vWUjcNGyyVHR3cNCmJuqJD/Q0okmulY" + "rCpQaf25/uiu4be8UumJU9whhGrsMjRgLXjzSysErp6SVtpSkuWw8mZlheOIDMQmau+cPit+" + "fG0KXq1vEU0t7P0lLKeYbLg+cUrHkLKrXOZa21reTVWkf9JtmZixkbHl2khdh8Q8MC00Lv+W" + "n4+5llHeyUihgmO1GeMgV8PhB47XZFaGjdS0sl1E7gkFpkVosqOWUU8qksg+XSdjK2FbEsfm" + "sEd1zWX7SZ2DEMcCEv0IfdgPL6MssFXn1K+tyRgizRNdCHyIppRlHxBUAwjpoblha7GGljnA" + "CjFAdc7dkWMmi+JAGtVfKU9zuMIjShvGvPyxUpm9A0idHWX7mQ8diqeemFehR8/Wbt/ajNiV" + "4hfPax/nPfw4SWJ+wfag+PJSdVhXY63Sr+5rw4sk3an2ny74ZJRmFE0GJgRbcpnjzxlfZa3d" + "tQTZTWFuMy/jJP9R3CrdUCthvVHYYj1HgWiJsk55konABzppHmDIla8rJqdsF7Ocg14H1mqc" + "Tj7Sb324+pB+j58vLfcR2tgVlrGtUiPD2YNHZqrICHwOFOiWDwooZKXtsfDRZTiqJ62PyYYr" + "tpJhkdcGKCZt1K0kLjjHV+/4y1QHiDCoQpQQfhCCltlOKO9saJuTuZA/862dPGGYuSPnJwPy" + "TU7AwBIbGXyqFszmpnzZ28/4OlULayemXEltMlfzc8RlDO7Lj6hjcJtkIpcyGRq8ow0F9wAT" + "SUrWxGIpEJgAg3Ae+gL9i3MRC1wHjoqjeU8c/U9ZKW4FHUvqYZVQN5d29QRe4uURtwZ5MdxO" + "ST6KCgITvQgzo/pasfmfe8TgAL48y9EJdl2D2tspwJGhUKpaMlPgKFqmLgSChXg379mAAceG" + "PTP1W0y9mUnCaNUWg5er8w8dDhMVKPu6iV9LLnNg2BDfK6wYpQ6lrsQyupd/W3Kdj/qJL37x" + "DkW2EMXh+pf0CwMy7CbvJs+rJBC4AYRBGLrdSOAuZl+8wVNSoe9Sjfh6u8caxAxjc2Kh8BNq" + "QSqkHEws9CWfwX0VQ+o36jUhSokFttwVyBtxE47z7jwVCaDRf0TqOTsSqhSSFIoUakYBZAEU" + "hvX67ot3He+g/xaiW3eqJw1xT3yclpIerfD3rvI+AUx1wa1WVCbQWBIo8z+1HwR8TkY50jSv" + "ux51cNWM9axmJ+B4QKUJ1Vkn8g3r/G3lhYZGYIbFQIxl9Bhv0pVO80m5WaHjPbR3LdQLTOuF" + "Dmn8oVBx1Cwza86K8H57dx2cV8doqceasNSqoaIE0mCKNvDOVpHle0MNPCIe5jLU2Kji1vS3" + "jds2gcoNkr0wWhZMMfh4ZPnEw605JUDfzyXMjdudi60zzOsv0bcQi0Y+emRx6Jr/pdbW8TjL" + "OJICc5DfmOcUuWqT2Cexebvabghj/IvYrl5KZbRzi7CunWH0b7N7xHesmJ89Ij/oT7zZSekG" + "1F9/KYP9e0nxhdJcletvuaoJyE/yQOPqAuVYv2OwSjjA76jUM75rmojwtHoIApjcuHZvw2mn" + "QO2HGPeY+AGJlNobpgRWfRYGt0alhQmmv6Jk9EpwLCUiz4q1NmVYr2nIhSkNatxEQqG6WDh1" + "nAuurlPcmo/63D0kbqJTW9EPlTH29/vYvH7KI0OpiLi/zmXwbl3ao/vcOuwcM7oGIOGrg03u" + "qIhRC2WzZE9F5Fh2KrqLcCrwbgj5P+NLazMJrP3hXLSGXnqrvoupoNhtTkM6nSqqKim/vO5u" + "vAJavozzzus+fCHFB1QSRYuMVcIcsQrrfYRx3rvM6rsQpx17gJc68rvfdRhXfn0K9vk0XU+V" + "UZeCZGAejHK9pmtl7b0kL0iqFQ4KkPamV9PuN3HOQcGl3cbJxXJbQeNV5LtC1BQDYaRVR99U" + "Acm6OrJ9MfH/VLzFwdLg2k2Xf3AtB3Q1pXmTO2qUuzNXbZdBVBxKensuHN7L95UWHlR9WLEB" + "RswU4EoNGm1GMs6SJyTXXCdH6RQjC1DZXbuyl7w/VCroxGJXXjNagTa/WjVIjMGwkvP7+Bm/" + "/X38dJEUTN/+ce+S61ALlmr51b/MvrtF5CcufmxLUqPwpvVEG+ubrjVs0TLN3/O+VkGpr0ZX" + "YbpyIoFxO4YhineX9KbsRRe0L7OypHKfcxcZ5V3UN7xo2UYGLuX3SlLdjkOMtL4Eyzi+HmIt" + "Orv0jZLP58auzxvtRgAQDlUVlvE0dG7fa8TvmtUhuxPDnxz/vAWK6RbuucTrr3zS4l2Y1+aA" + "uYrOor0ea0/uElnO/ml76UWq4lZDCbI3UJlRVJdGORpX2m9vMLSCV1QaxJHZQG0H7dls/+S9" + "H5dn8uoX7OP2HemR4iv+v/H6TPs5H/H7+33kqnIod0dz7BRagqzPRagCadsh9eM0xD3dQfLq" + "FA7aF4in7VHhvrk2xJJeZJQ9NDA8E9JUnT+UoI0RhrMyKEYV/iiXi3SpGat1rQlQ+2sXcYhI" + "MxcK0MvagY/P5LDgXhOhfabOv1LiYrdMBPP7/wfW8f3vXZmL/h/W6Qo37st5OuSY80IgLenx" + "gSF0+4qh4BBhdLqIB4MOLJW+V4mh5xZBCLDVeRwODr7Rb0BD8bNdf/4MHUIhdjLuNdnQybmo" + "Werl3I+CgQ0/xKVOOSqtmS2OtVR7RCNXfDCg+b4oPErzwT66XaPCJCdqpmbFkLH4zRP3q1c1" + "NUdzbU67vC/nAv/L9CWOAxDPmK6dOSfG8rCz3B4osu+wUd5fCzmxYFX2UPn60mFQlzQzAHky" + "3AzhWhNAGiKUoQ51MJCmFvAudq9AdUFIuiiqX34fbzxc/BgJKuwgcKnJC/r4cX64+iAYsK5L" + "MVZNQaJ5chgHePXuMM/0HIvaUEL79RK32BATVNHjzOOVOesLv2q4xiAU0fJMbZk3QH/mZK05" + "tiS1KxuXDR/KW31g1/hN5l2RESi/pmI1sspH15MGhNyrgNoHjcD+5vOHU9XCWKUPM6u7kcHS" + "SPgK/LErkDpEfdWgOlabEz0DKob1gtyzLxcMahrNFdQYdeuGJBjWwqHJm41I9NsQGfosItmN" + "j/ZUBUQvRxgxQPs3EAJf2rQROxPtgdLu1TnVyl1JfkIUK4V4FK01tGHKWCT8L4J7V7+Pcu3W" + "G0GDXosWMOyyyebCJ99BQnsE10EbHNzqoldZpOJGU+IeUytklbSCbS6FbogHoUcupyO+kod5" + "AqtzLhBmW5Stg+9FmNwMGPwBs3icm9v49o24mkmtQHdVGn/x4CiSQg0mTFfHZEL/24fQr9NJ" + "mnAhjtRqhY8A22uEanyszzc3CVw7aBnu7sIbm96sGmv/BUj+vuMio+mJQPr8dXrhvv4JwLmd" + "cHVfgFKsLkI6Q2FVr9KP/Q6CyKZGWn99HCmNGmKMBFV+A6iJhgLYAc6q9niuOZwYMe8KGWH6" + "tBdckPjIeE2BgNAtEOhxp5p8EXsPdN6jJTuOxfEZBdwjeKiEX0i3w39kYXRKQXaXt4SpjSOu" + "Jh4mssQ8DNeGAYLTItTHlam7xfQPrANNffQujoTNnSWi5agI2ZN5bIRQWfmdTLDG9JIHF8WJ" + "0JurmMVVbnAx2qutD6/tpX+D68kxADQeVTKHfegH1aj8K94koz2JlShkpdPHPEeYyhHbHv12" + "oUaeK+kToZcuk6gKIRwMkOjDxzYjevvzbYyhrLArN2UE9sQ5plIZaOHtrBNpYM+XS0HFTDBP" + "IJlewx2KOHzIN9R/Le0ka59vBfwB5qO8khhefPSX83m9Z7ge15G0CUXzX0AA23SEWd+Lt4Qm" + "+51AVyuASgCxmUZU2yRzmybqtM92JtqONl8plIKjXCrsWfZqwhRRsYYcluEU+w8ZBjmENnWu" + "//AAdxfqLHSIIrVskii0qtgR/iQ9y9pzSYJJ/UlU7s9R3JOl3BP/KkG1RGZ/Pl5zvIaM8yAp" + "cSn3X4CFG0mhSlp0dAadbpLutZxpQcf33PuGYnQxqyaxF5TsLwFKLPfdaVS+Y9mW4OTyZDDm" + "7w+mQ7yPNCY+MtALjIduEW2hoz89XMb5DkvlPbSZyttNDRze9xo3J3s9r26u75ZdiJQz/+iS" + "zrDboBxNSFFB1cQveRHxJlZl72CDNmla7CHQPCouPtTnF/ojZPfeGFtoxjwSyt6EP/gZxRYe" + "xSU1ur8qFO7AvVTucU7bIohF2mvrlF2rxiYEGxBbPGh+brca8Vf3Sd5DrU9UTGiI4Lxxgcwo" + "zJDrGHDB54ifX+MZCyDn1iecp5mtxld1hqUmpxTGCEwxE6+LafozQKEmrQkILsJDP5T+wgyZ" + "5lUWv/wRFJhOUYYLQvrh23H36ECG0H7A1e0scahy5GlB9MdF+gv5Jm0LgL1pHTUEvTzURLZz" + "aG1RzIR9tqUSrY4+w5zx+oRtX48LCpUies/CfZRerPP+VLxhwVo34XFFFVLbiYFnoibujK60" + "fMWH/L6HR6WFDVUNccbxNvqGrBuElsntVp60Ijr+Yre8cWs2eNPoUR0J/0i8UwU5Oiu6+NMT" + "LdoQaWUr21PHAa8q57PynrSjVAwVyIQOZSHufnli3C/jz7hNvoi1ENo2AHx8NKLouBYEvpsZ" + "Bke09Mv7udHy7jYkWS+96l8kB2vvb9yHjgf5cJf8JMSIjO1a1RHwCsNS2+iyc5atlcw3EZk3" + "RmWQ/90bs31jk+yfiMVjCjSOjkYw095Rx4AuxmIARkuxO++RgKy++hhgA9N4vLuhMEbU2hsW" + "pa6eiJxFZn1GEhxljB0XkIpbGpJ+E7z3bVQbnasMUqvcnEf8qJE/De2urCtEMaTlJyUVpJil" + "wqe+jf8iCQn3oXcyJXsFiGP2h+lg+SAiS9eYVAzInJJTStU5yJfRQqa+EAkS6hn86y8qdfku" + "i7RYWdr3dIITwMB+XISf075PIi+tAD5zvLbWmMfWvAFirAeaUssRZyMy6kYfx/pmnRSo3KjP" + "QHPdC8Ii7i5+Jd4LVr3RQxRgMWzp21ZaLC7TNTeS2/e9X5a8TTSoiBiV4+VdWq18Mtbl0IEJ" + "rZ/JSVEroCrPIHDLAkmtawgcRQmhnYaZ/JDsoRiOMwyXLCuOrWyE0SustEsTvWbgoRsq9Xm4" + "+QKl9mLLKC9U0zHmWjKNpybmh07FVILdo4kw+IoTtdzF9sV5Zo8zXyyCr7fYgGIzLG+u7jd2" + "O8OshNAer8iPA9Nr/8TwrP7uFkl7gY2CBYgIxZXUqT6lZC9K9+T/DwAAAP//vdzXMxwAowVw" + "ZVl9EZ3V++qd6Fa0jR4RFtEj0TtZvZcoq3dCtISIEmz0XlcJUaML0VeJzvc93bkz9/3+Aeft" + "zJyZ38PxUE1IxF+ofZvD1pds5oQbaMKBSkk98dEulnuUzcOfgVBc9Dw1sesttfonH2MxHeMV" + "JSfDG6v2Idhj9lP2MpnP641yIVTdwnrd1ymGllO66jwcq5fPdcKFAtNpbvCCltUo8t3bHD2e" + "8keK25ReN38hZh/zTNL68oTqDIj8FZ0dC4OWK0UBRouGIoDqhBa40N5k4p7Cpx5tlbkViZz3" + "p5+hir3tF6mlOHL+dv9EdRHq+Z2G3Jcm4j8Oy4T+BiWO5XOG1tiUv8zptwkclWTYT2bT4wz8" + "5XW2Crk1knb3L4/aYaV+TGNlTKO+5+NOovVqtaPhOKi6mJLZ+dsFLVV1CVEtl5sxx9pRSwqQ" + "Ky/E3r4qrgr2JCtX+s0iMYJJVHHSMw+pXE31ltGM8NBK7gGORqv1MMeW7M+sslGPz8SfzPle" + "lHJMB6l5d5ClrqroTeGHm6q8NmUdTP7nZ/77+5thDdrLP3FxlG+VZYPXVmSsippcOY6oUEF8" + "Ty/foR0c5UqevF2NyfLxLRe3YRz7YsJ3y+joCB2F2ZtRULwI4/rFzeLPzYjDpDfHx7OtJuTL" + "mv/Rx4SerbTbmjd0rZMXVC8wG2G2cR/w9bZqsboipnKjVvqkNLj55WHj++7sxLQBLJAluqjE" + "XJPucPo7n37py6RpqNTnJ6fOEWFcB4HflBquCuzV63WHRMaECPdsg1NQrmVJeHEhlHydbHfr" + "60ljrUlNVqo1ofoq523f+oNaszOpcXSIbdKfEzK7uCUAfw9iRXwp6NWvNguZqjGvALySF9lD" + "Fh0UKpYDomW95Mpx0NOxQe2mmrPsA7G4WPSEuD6EFqEKpz57ooru5V3y0nOnBZPuc9Ym/La6" + "MdvFXEqZfaa6C+OWl7lcaLavf1wKRITvsHgkrJlDXSuen/W9KT6nWzNlhlMXn+5ZqyvP2ipx" + "yClQ25iK9SiHCXUBJ1GFU/Qv7QaPX8+bpYLPgUBozOAxuRBEbIMVUDqUZilCvSZH44dVo/8Q" + "+As0qaWQeVabbrl7h+Bnqa9YU34tR6sXe0i9mdoJf4WRUC9OMHwqG+Ot8k/kY4VJJq2Y0hV8" + "fZr4lRWXq3Crj/Y1o0UF5GVy4w7LhOG/5U42DXtadVpfqAjnlTx15Dh94UaxyJTxjw3RsF3r" + "atBy5wtJRGydkm57WrcZNKjG/4ex4XTTjagnE+emZr+e1Iuyt8uJn6bQP2vVL/BV7JBDBIzs" + "HBDMD4b8ax1oR2kHW75oaFsprd0Le8y7sqeEXC4LaboE+FjX3+BFr6ujL3fQ++OfjX6TYSqJ" + "arF/sFFaU74vSjgZWczC3dlnnv1R3WAXzzWGZS4ZOSBNhbNY2sWXW6p8GsfMV871qbWkRxIu" + "LlkHC4Fpm2nBDMYC9PZYcmSP1FwN72Djs8sx7g2bS38SnZNbxguljRRxLzMxi/KrmT+ffR5M" + "FEWRaljU7eKTdBrOKR8RR88rbmDWy19Dwupq84LVRYTCFvygunyJo6LdLwlxGkCMmancJMrF" + "sFW2CuqcWuginiXJJn60rS9W6o534CejKGIwm4iISkXaevvshFa756BPfPnlaBXgGD5q6EVP" + "3CoJyuBaB4TwJ85Ue8XJdWClWue0p9DWCGXdV8E+LqnySL5j0Rwfn0h6r4j+b3Nyvogn1YSZ" + "ZSk415TpCOzETOPNlwQPUNXgNytRt0dYMMyU82vX6ZRkjZP0d0Uur2y64hLjNdsVN1aedMK8" + "GQUmAt7zDvXj/bBRJxnNIFX5CgKmDGFERLINu/LreT2ZAkQNN/oobXdi44EVH0mRj2Q5j/z3" + "Apopv/7G1lmFE0FFGMv86MlNAOIoozHQvd5KEkgZ/837s6PXgX2/pSxAzUuBu1BzfzsywOxW" + "AX/iQBRb+FJ/yHPpqnl32vt1vd6Y1E/kIdP51Ty1MfnuiigAsEOvUlar0myksGa5bLXipFQi" + "cqRTZTE3tUjuoe4078BYOrD4RfZfeIil68Gzhb2Hn9/DfDZpHhIR9wAWnAlt5qhRLfCSf6xw" + "hmCohAtLfe9qucEM68epxaJpoabs6+niFgSzpBqgjP5IWZ6SUGkR83Q1hVksjPIhOtRoZTMB" + "eUnMEquW/XSeCwEcEikvCMJRsUD9eXObuf8loKGp1E0ovHhEAaZO8/sV2IfWmXfAZuUvRTu5" + "+G0piVCle/rbFcmqNgV2sTOjtbge5ni0pkOi4XwUvtHtgTVfDbwQtJ/uu3m623VJTCZnIFf3" + "ye8EFpL9D/7lKImLJ9So21omkZnQP/IeTzwShXxytqIURebb1DIxy95UK3NYDs9BfFaOPI/1" + "huim2BzZs8yt0AZKs2anGOVxV3kJfmWREUawqo18pTGxu8fwkBf9mqPw7roEbR1uymX/8UHU" + "iOMUuK5kyy5+KuCLlbeXGHpiT4yhM0uu3kM0Cr5lodGu1l4xE22RgaIqzC1t9SOEPzOl5UU6" + "tgi9yE+heE+fNnUQS2wl7e6WLjOhHsjteOpZVazBH+seZGNj6T80NmdnaCvMBKVUpDYyIQgs" + "829oaa6TPpmHj56AUwWSmYmpsCcaqmLiIg9E9xpZb/TwRjrY0mwB/+CnLaG1O/GaIw+H7wF8" + "LH23j5RO4uEiOCpQWSmQsgjOzzvP/JRorMHeeLIHVER35LtoZ+WnxcL/SvIXu6heOCZZj9jI" + "KOZ+yxO+IdvqsiRXQo32W6J+CrhRCKwOLBMUdQia2HuZjb7PvZgN139ZuAglxZENsUAWnvHq" + "V18oLIb5BA/VGknBlveYv189OgpNgJxT2pFBVPm4Mp0XdOzpp1e2+5BPK7xkrF3PN6P16RM5" + "qJeps8oFBIUbxNUcFkaanNPlcwKRSdl29xHPxvMMr/JWy+rYN8Rt/kGhi9SELJom1NlVIToB" + "A11+/KzW6r+s0lgckNzLzefEjBurYSdSbITUYhUibwjo2bx2BS7tgVrZl2uAi2Ybu3WhsmoE" + "WUnbwPXTLWANSMNTTVeWiTHHQ42Z3dWwPX9cAkZ+3BxnjLsX3ubo+gCSMLlJrhK1/vx/kET2" + "/w9JNP7nGCCJUcs3yk2XCAwnp27ifI2yucPfthCMJcJeuRJFrSqveKjwivGEraMDsXE3gaKT" + "qUPqgrcU+VDBxnzywBI6PRlbdOswv1ZNNBZ/jhUyQ2ln+b2rfnyVRc33+O7Meed359S3bWUO" + "FEWH7ILNsLefm9RpvomNasF7HMR1PHqNzJaNMn+uDGUz98G27Qb5wPfnJVRKXLL+HGor7U4x" + "nudnskpXU1lpIYX2tPhOZzUuE9tsZV4dl7RWzHf2mUuTOuZ9UqySeZ77jinucezW8Di+S+Ey" + "xUjZDg5RzhYmSrZWIB8le9xt0PRDpmb27XYUwZHOc+5HMtnNe23FtQSH5XKR/dYwZ4SzlebB" + "SbtBbn0rG8Xyhw9FPG3Q/LB9TWg5ipr+bKB/3hVBa2+rjmrLcKiWZjcItf3hwxk+7ic9XRZu" + "i8RyPCsK52nIwv9qkgt31qW+GjRTGsrfIuNMGBX0/aUjWItmlVznDHGyk5UjwFJtubtRdYDr" + "ungcFvbN6SDYshYttEzEfPrnvv3SABompDKoSRXpVO4cAU5LmJVzc2tCeaS39Z/7Ic43nwyd" + "Tb8uWhPyCx6R+XHkH1490v1cx9yRRydBHFtLaoX2nWahdkJnWIJlmW7VM1rG5X/Zeg+4PnCu" + "QKZU1q2UEydQk9xLgQ49FMq00HvwlhU7xu1edZ3YdRxO+9/1MGUSV6P0qRR7rIlPuKorwPib" + "M8V+da7+5sl3jPHD8MCcxUiwSVsc6TYEjGSdx9OtU0S5moh0FYqW00haQtfhZMF7ZwrMAfKo" + "dVw7peG6A8210DC7IY25pW0pmI3yMxSAx/0dhvrSmjNsI+sDC1hKCx6TlaGMaTl5U5eTnn1M" + "MDvqxhFn4nNPmnqsOer2g9JgY/YVqzphJKEaQg3flMqxjpL2eVjLt7i5koM7EmFNr/IDUqtd" + "0rkPAW6b2cUnXovztRPvnxhYfCfb6Eh7sW4UPs+xgr5q3tYFMnax1MSD3WJeTZO2fXu9s7S9" + "xYs3J3kdygNJUeFp7I6l5cUzrxvQhn8g9wNzIyfmCSn38LbQH7RvNtLA8VawcTa4nRkZZ1Ou" + "4tzCP64UKr/UDqYDial41gJGMa0RXxzZhZJH4wZcijkHhTznIkut016e/NMTrUduQoD9xL3+" + "jsh9fN575m8vnn/PtHvnSNyq27KwruffvSrQYj9xl6T6aSK6AzMpjFZtG4smI0h5mtQ4T8iI" + "qJ6PycXZX4M+zYzass4vgihGd7gPYweF+/bevmmo493oJ0p0Okxkt92lFR/UkHcNaHyGHkUX" + "Cf/1n0ukJ+X6A+weV+riyrRTXQd2gxnOjuxQze/9w4jCaV2Dfu4Apf9d01xKHd4ROMYXWcZW" + "nO1PblnoU3ITZQDw6Mt2xk21jMOD1eqrLD7PPmbUPYk6J/1bW9aJxMIg2F7EIdT/HMdd1Rr1" + "mP+mP8f7+3OgQjeusoG/vc6ZucSdLhwqp+KlrNC13ovj4bgg20jRpn4RE4JqbKKcYiobWvHF" + "tPz+pibzj/BW3Axw01NvFlUU7NXbcQL0vuSsTU6BJzT0iB6Qf0i/ZK3Uztj6SttmjLQ2FoYh" + "9AyttPDCAmJyg5RXzy9yhMexYJ4gJFltTkaZWv9Z1+XakN8D3OA4l+CUwrykHXmO3mSqF8cM" + "0UQYuqL8XwLPkYDpNAlnff/6SfRK0afTzDlfLhGCG8GhiNvQpNRXC6OMxJSt6lwOO5wcvXVd" + "CXnm/z52Ff5OF2wj33sSSMVJ1s/4XRwuL/JgO2hrT+Ce7DtJN6mG4o9LWkso/SUgThEPa3fg" + "fE+/ILoyTe65uBgbhD+xs8u2dTl/TZNsDdNY8j3DTCIjy/V4utG3IiQpQUsVwJSjwVs5Mnmk" + "NpM5OdVAgdvU3TGE/yBiP+aj3Q5DhG2xAB3+1kov9nx4i8UEoy+X4NCVNgrSfZvDWR94sh01" + "qQ1xqu0Lfo5Pc1PIpkkonoo80b7yk7rJWUWSG5wK3tUbhEeLczZiofCDXtsW+yDIEbSASMki" + "DK/qXAE3W8sfYM6q66szPxGsuHKutiP1ByUF7YblVYsIw48HCwLsRJ3Hi9uWS/gZ4fwLyDqu" + "N8HKNMrPhUgNCTQ7kDdRrKL+3CUFEd5EGWmZIuX18ZFavOK67yIZGlh2nrBLagOQk5pinJnL" + "oj/4ChGekL/TcUSE/iAPEIbqRKPY1jjqkdePFzkPh/nuZOMqYl3jAXrr/bK0OrHavdAwxZQg" + "6qB4VoYRXxrCqJJ4qKxzcYX2Ok08Qw257tRoVWfXrQjBIrxSQdjMXVRkxHGo0Gn4kTTY+lZ5" + "X51ZtUH54dsi6Vv7g40tVgQI+NrriPXWbBS8bEpCxcLmy8nB/8AqSNCW2Km4nlPozGhwfsMH" + "Gbbe3QJXK/1QWR1GX4rESlzsMJUHP3uu4aksfeUV3jXJlPIhD6BWtpRruvAVsLA97reFuCfh" + "37tLAmSYZ6+UEl2PQZW4Fz8jNBK/ZN5lQlOktTek7UqhtXb3T3fxGYLIY1I+owVeqNkMki+u" + "wdxgeKhfiTMT2XzFtzWHhSkhxR/BnS62MtTFtpUbRZTEj0sKw3t4PO/cUVANoLwNT28GA84x" + "RbK+Kvssg/3DlC1CP0vguDaqE3gtHMuFcM2ZVfdRsBx20lN92RLANd8KIh/zgAiUHX5Nl9JU" + "llNZ2DNYwWzhl32iKBtM+5m82d1DzErjUgF0moTkmCszZPqNFadoglq/8+IS7IRogW59w5Ks" + "gXfrHcQhEprR2xrEXvLZdnyo0IYEuVapIgwdPiI1z2DBxcoJ8sy3m16HFTPkDkbjClnDSU4T" + "w/BlY1SqevyTwB4YzjEPXUfsfJM/Jg7qvCcgfvyCcvq/E/Ph42L3YRXkqMxx7OhKNExrld1g" + "PawYZk0z/91WXibCfFrS1iawp42VtLrzHVDihZdQrDSuqkB5eP1SxPJPYs1nO/1NqSU3OWc4" + "SLUOUJCOTebTiJPmM9pxDLiUDNp8RrDnR2PXe3BK3cqlUtjn/Q3pzrVNnAqJ+wp4aqD50sak" + "x72/KpBLXasVeZ1xnk/kgIpUgC0RWHxbbnz+uSxC/VbxbzE2YyPxzGiSivDrwGP29rA/MbuJ" + "gyQhMoHWDS7eps8slTfVnVx2o3Sn88D7SYG9pIgcczRaXz6//Ay3uv1xqNEY4yv8GG7cQDvw" + "N+l63bZvzWzJ8/wwKj6kvmjU5w3dHDKZ9g/YarmBEsCcEjLrUBzAQyWdiQuf3twkeGb8BN9h" + "akDsY3oYZ3LB8cl1LXIyiuO90nDutn7qZ+p4yT7v5dJsx3KuZV7YLH1AzIXcAMec32m3k8j0" + "iKC4xmsq85l3xDiJSuODfKkbjsUeB8SWP5UoQZEPZRB8psqK+Ia4fRssNLz41rG8wbjEK7/O" + "stzaasjdUh9jW579HHkZEgmJdyi7FHAIA+Pyq8ijJM8QiwzGTb2Fpbi5LHsPAIhSo+3AuAOX" + "fLEEE9McEzqL+eP+xFie+7BQY/nB/bwwOtimbleJqpbDIK4mKkWYc2yLLdi0I4VoWkBa6J0S" + "Iqf6q91D7hcncN/fSSNX+E3hiXgnW4tPyZTbfkhM8SvVof8mvrdRkl16jLEG/oKsE1EJ+Y2K" + "Liz2qZQ2oHqoEpj0NSUI6c+qRheVW3qooqCNf/KOeFye2C6+uVg9qHsyRfJC4kFi/+Auusz5" + "EvaWie9xcIYwWtmqZZ0zjEd+V266a0NGFMDGO5/hbuoLkW0QJoG7OzUKm2YM4PXVC+e/0mlN" + "8g53oJAWeJmdnm1Fwy2EivZpN5U/9DFudhwQ8Gc7BkOLLLKCHDjVmHsyqWWwzoJIhv34xvKt" + "d9zRBEaVc+4v7L9+jIHMye3/5AnEzomTjIl+k+1O97Ch7vVirUHsLlPqFYKqKNdcF29i5v7A" + "w2VYdZ/WkqUDyB5q9GApiWXzplCWHBpj+0gbt88zQ+aNi5EARBjGGKAKU1MdhrBKCDJineIO" + "iUyqot9VD8H21kSVcit0yRMnlZetphhBzIIqWXCWQ066oGc0ScmsoPyC40/gkRVLgNtmZNwV" + "x08jOkE18rsNIVKie9hOW8r2rwRk5vYdiSd26044WpDWmdW49nwsezfDPaDy7Qj2+tvUf1HE" + "kR+hv+5KoxRTE600L7X91Ur2ua3mCjvY1V4NM+TPT0tZ25mFW83niOhwCNl/MGYkkeAEP6GO" + "s8jS5shVW+Uc1zT/oIgyO1e1O8TVDm4rVYmVNvemHuAQgkiOaZ70gA6IQebfKWCcrPdRkApN" + "CuJb1dYSzIFXrwFDUBh2IeWOoe3gTFjuKxPVfqHHtg6W7FLvw7/stGDqEbXdifkAS0LAfSpg" + "Ua3qnaw9Y16rWFryES1cL1Ti7MfzUcc3H5jpki21zvVmdIvOsL24HYnoBghnxhkT9kA9hLzu" + "/cbDUEzDzWVRy5AgStGQOdWCZGa8SVf3+U16C1afFg5ZzbnRYHHkirVPdyikos5r8IFNfql1" + "mpja69/q5zuWnS3EuGaBENt1nxK3kYB57GOf5GxUxO17b4KWruHIYSJxYbFVOtFlP3n8dtVE" + "h0c25HlcAqNnOk18pxNF5XEv3ervI3OF8igrM/622f5xCrDdqhgHKf5liexXcIQq9/pbiukn" + "c7noUQh9pnnFvqY35Q1opBvS/aojZdIjwOylSifXfvhPN4I26w5yUhWFH1sMJaabswwthhjI" + "Ok1M7P4m+QcNurgPDx3kbFZCzC4St8dwY+FKiPsYvkqcpOJ+3xfLXZPwji4ntczeYAuFt7Uq" + "hG6bneIcKg1vGOGGI59VZiT8IDkDnrYG2sn+rid/Im2L+c5ddPGICy+MLLPgkuNr2hQh8wL6" + "sgbTvVfrUe7Tvbsn9BRLdYUX3Er4/uuxFViGY3RTwk3T6zmBnKxIbGwAyr2Bw+n2qi5iy/ON" + "Pd364JCVx40xWTU5estviIpjz78PTBIE/Fhbiavx9DsADJ4hH8JwwmGqtRs3yr3+s3sfQb42" + "sAGw8N0Fa7MIgwXd1RP1IaY0ewtBkYuXZKr51nUvAkhemvtYVP2cLQ0xZLSr1Rj69P4xwiCr" + "2MVZVlHEv4v8pGeY9LPOn8tEHFCdtpzphSqdki9wcGwLPxZsiYaLboIINoVTx7FFKcE2lzH/" + "8q8hTA4RgdejiQuJnT2kQ2DWyrp8UdeFvje4/iWD2/NxZz1X2j0ZMz4+jxltMEqrWP6HqOL8" + "tF0xl5bA7o4x4gLkQ8aXqRio8YFFyYYPjYiw2qX2RiEEpi+dVSJViNuoYbfY+LPtEy2qPqQ3" + "ekNE3w0kZjNAN/3g06Tp8ECU1ZIfaPwRUnGznxN/XzNbxEKDn+RkZ9WS2GOd8yrhRh/nQigf" + "DyUtJLWfDc/G/lUVmnDt8VXOyir7fqFbFptkruC9HqPyM3/0k7OR3CrgzNMYvtwMxJ+vWryo" + "N6KQgDrcLUDqh36PIOOXgmytZHpLxrWVKnOcd9hrPfnUGBcHC0113+9yPz0Djl/byoGd1487" + "Ym3gq99gdwc2GKOFHwvcIznxj27RXyqTj1bn5yhi/VEkVU0fKdy/xSn1e1w1x6pc2N4DZTb0" + "mn/BeBL/t2X8B2rxLZ8="; + +/*---------------------------------------------------------------------*/ +/* Deserializer with added processing */ +/*---------------------------------------------------------------------*/ +/*! + * \brief l_bootnum_gen4() + * + * \param[in] nsamp number of samples to retain for each digit + * \return pixa of labeled digits + * + *
+ * Notes:
+ *      (1) The encoded string and the code to generate pixa1 was
+ *          automatically generated.
+ *      (2) pixa1 is further processed to make the pixa of labelled digits.
+ * 
+ */ +PIXA * +l_bootnum_gen4(l_int32 nsamp) +{ +l_uint8 *data1, *data2; +l_int32 size1; +size_t size2; +PIXA *pixa1, *pixa2; + + PROCNAME("l_bootnum_gen4"); + + if (nsamp <= 0) + return (PIXA *)ERROR_PTR("invalid nsamp\n", procName, NULL); + + /* Unencode selected string, write to file, and read it */ + data1 = decodeBase64(l_bootnum4, strlen(l_bootnum4), &size1); + data2 = zlibUncompress(data1, size1, &size2); + pixa1 = pixaReadMem(data2, size2); + lept_free(data1); + lept_free(data2); + + /* pixa1 has 10 images of mosaic'd digits. Each of these images + * must be extracted into a pixa of templates, where each template + * is labeled with the digit value, and then selectively + * concatenated into an output pixa. */ + pixa2 = pixaMakeFromTiledPixa(pixa1, 20, 30, nsamp); + pixaDestroy(&pixa1); + return pixa2; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxbasic.c b/hgdriver/3rdparty/hgOCR/leptonica/boxbasic.c new file mode 100644 index 0000000..d51bd3d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxbasic.c @@ -0,0 +1,2352 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxbasic.c + *
+ *
+ *   Basic 'class' functions for box, boxa and boxaa,
+ *   including accessors and serialization.
+ *
+ *      Box creation, copy, clone, destruction
+ *           BOX      *boxCreate()
+ *           BOX      *boxCreateValid()
+ *           BOX      *boxCopy()
+ *           BOX      *boxClone()
+ *           void      boxDestroy()
+ *
+ *      Box accessors
+ *           l_int32   boxGetGeometry()
+ *           l_int32   boxSetGeometry()
+ *           l_int32   boxGetSideLocations()
+ *           l_int32   boxSetSideLocations()
+ *           l_int32   boxGetRefcount()
+ *           l_int32   boxChangeRefcount()
+ *           l_int32   boxIsValid()
+ *
+ *      Boxa creation, copy, destruction
+ *           BOXA     *boxaCreate()
+ *           BOXA     *boxaCopy()
+ *           void      boxaDestroy()
+ *
+ *      Boxa array extension
+ *           l_int32   boxaAddBox()
+ *           l_int32   boxaExtendArray()
+ *           l_int32   boxaExtendArrayToSize()
+ *
+ *      Boxa accessors
+ *           l_int32   boxaGetCount()
+ *           l_int32   boxaGetValidCount()
+ *           BOX      *boxaGetBox()
+ *           BOX      *boxaGetValidBox()
+ *           NUMA     *boxaFindInvalidBoxes()
+ *           l_int32   boxaGetBoxGeometry()
+ *           l_int32   boxaIsFull()
+ *
+ *      Boxa array modifiers
+ *           l_int32   boxaReplaceBox()
+ *           l_int32   boxaInsertBox()
+ *           l_int32   boxaRemoveBox()
+ *           l_int32   boxaRemoveBoxAndSave()
+ *           BOXA     *boxaSaveValid()
+ *           l_int32   boxaInitFull()
+ *           l_int32   boxaClear()
+ *
+ *      Boxaa creation, copy, destruction
+ *           BOXAA    *boxaaCreate()
+ *           BOXAA    *boxaaCopy()
+ *           void      boxaaDestroy()
+ *
+ *      Boxaa array extension
+ *           l_int32   boxaaAddBoxa()
+ *           l_int32   boxaaExtendArray()
+ *           l_int32   boxaaExtendArrayToSize()
+ *
+ *      Boxaa accessors
+ *           l_int32   boxaaGetCount()
+ *           l_int32   boxaaGetBoxCount()
+ *           BOXA     *boxaaGetBoxa()
+ *           BOX      *boxaaGetBox()
+ *
+ *      Boxaa array modifiers
+ *           l_int32   boxaaInitFull()
+ *           l_int32   boxaaExtendWithInit()
+ *           l_int32   boxaaReplaceBoxa()
+ *           l_int32   boxaaInsertBoxa()
+ *           l_int32   boxaaRemoveBoxa()
+ *           l_int32   boxaaAddBox()
+ *
+ *      Boxaa serialized I/O
+ *           BOXAA    *boxaaReadFromFiles()
+ *           BOXAA    *boxaaRead()
+ *           BOXAA    *boxaaReadStream()
+ *           BOXAA    *boxaaReadMem()
+ *           l_int32   boxaaWrite()
+ *           l_int32   boxaaWriteStream()
+ *           l_int32   boxaaWriteMem()
+ *
+ *      Boxa serialized I/O
+ *           BOXA     *boxaRead()
+ *           BOXA     *boxaReadStream()
+ *           BOXA     *boxaReadMem()
+ *           l_int32   boxaWriteDebug()
+ *           l_int32   boxaWrite()
+ *           l_int32   boxaWriteStream()
+ *           l_int32   boxaWriteMem()
+ *
+ *      Box print (for debug)
+ *           l_int32   boxPrintStreamInfo()
+ *
+ *   Most functions use only valid boxes, which are boxes that have both
+ *   width and height > 0.  However, a few functions, such as
+ *   boxaGetMedianVals() do not assume that all boxes are valid.  For any
+ *   function that can use a boxa with invalid boxes, it is convenient
+ *   to use these accessors:
+ *       boxaGetValidCount()   :  count of valid boxes
+ *       boxaGetValidBox()     :  returns NULL for invalid boxes
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 1000000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + +/*---------------------------------------------------------------------* + * Box creation, destruction and copy * + *---------------------------------------------------------------------*/ +/*! + * \brief boxCreate() + * + * \param[in] x, y, w, h + * \return box, or NULL on error + * + *
+ * Notes:
+ *      (1) This clips the box to the +quad.  If no part of the
+ *          box is in the +quad, this returns NULL.
+ *      (2) We allow you to make a box with w = 0 and/or h = 0.
+ *          This does not represent a valid region, but it is useful
+ *          as a placeholder in a boxa for which the index of the
+ *          box in the boxa is important.  This is an atypical
+ *          situation; usually you want to put only valid boxes with
+ *          nonzero width and height in a boxa.  If you have a boxa
+ *          with invalid boxes, the accessor boxaGetValidBox()
+ *          will return NULL on each invalid box.
+ *      (3) If you want to create only valid boxes, use boxCreateValid(),
+ *          which returns NULL if either w or h is 0.
+ * 
+ */ +BOX * +boxCreate(l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ +BOX *box; + + PROCNAME("boxCreate"); + + if (w < 0 || h < 0) + return (BOX *)ERROR_PTR("w and h not both >= 0", procName, NULL); + if (x < 0) { /* take part in +quad */ + w = w + x; + x = 0; + if (w <= 0) + return (BOX *)ERROR_PTR("x < 0 and box off +quad", procName, NULL); + } + if (y < 0) { /* take part in +quad */ + h = h + y; + y = 0; + if (h <= 0) + return (BOX *)ERROR_PTR("y < 0 and box off +quad", procName, NULL); + } + + box = (BOX *)LEPT_CALLOC(1, sizeof(BOX)); + boxSetGeometry(box, x, y, w, h); + box->refcount = 1; + return box; +} + + +/*! + * \brief boxCreateValid() + * + * \param[in] x, y, w, h + * \return box, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns NULL if either w = 0 or h = 0.
+ * 
+ */ +BOX * +boxCreateValid(l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ + PROCNAME("boxCreateValid"); + + if (w <= 0 || h <= 0) + return (BOX *)ERROR_PTR("w and h not both > 0", procName, NULL); + return boxCreate(x, y, w, h); +} + + +/*! + * \brief boxCopy() + * + * \param[in] box + * \return copy of box, or NULL on error + */ +BOX * +boxCopy(BOX *box) +{ +BOX *boxc; + + PROCNAME("boxCopy"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + + boxc = boxCreate(box->x, box->y, box->w, box->h); + return boxc; +} + + +/*! + * \brief boxClone() + * + * \param[in] box + * \return ptr to same box, or NULL on error + */ +BOX * +boxClone(BOX *box) +{ + + PROCNAME("boxClone"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + + boxChangeRefcount(box, 1); + return box; +} + + +/*! + * \brief boxDestroy() + * + * \param[in,out] pbox will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the box.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +boxDestroy(BOX **pbox) +{ +BOX *box; + + PROCNAME("boxDestroy"); + + if (pbox == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + if ((box = *pbox) == NULL) + return; + + boxChangeRefcount(box, -1); + if (boxGetRefcount(box) <= 0) + LEPT_FREE(box); + *pbox = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Box accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief boxGetGeometry() + * + * \param[in] box + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +boxGetGeometry(BOX *box, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ + PROCNAME("boxGetGeometry"); + + if (px) *px = 0; + if (py) *py = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (px) *px = box->x; + if (py) *py = box->y; + if (pw) *pw = box->w; + if (ph) *ph = box->h; + return 0; +} + + +/*! + * \brief boxSetGeometry() + * + * \param[in] box + * \param[in] x, y, w, h [optional] use -1 to leave unchanged + * \return 0 if OK, 1 on error + */ +l_ok +boxSetGeometry(BOX *box, + l_int32 x, + l_int32 y, + l_int32 w, + l_int32 h) +{ + PROCNAME("boxSetGeometry"); + + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (x != -1) box->x = x; + if (y != -1) box->y = y; + if (w != -1) box->w = w; + if (h != -1) box->h = h; + return 0; +} + + +/*! + * \brief boxGetSideLocations() + * + * \param[in] box + * \param[out] pl, pt, pr, pb [optional] each can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) All returned values are within the box.
+ * 
+ */ +l_ok +boxGetSideLocations(BOX *box, + l_int32 *pl, + l_int32 *pr, + l_int32 *pt, + l_int32 *pb) +{ +l_int32 x, y, w, h; + + PROCNAME("boxGetSideLocations"); + + if (pl) *pl = 0; + if (pr) *pr = 0; + if (pt) *pt = 0; + if (pb) *pb = 0; + if (!box) + return ERROR_INT("box not defined", procName, 1); + + boxGetGeometry(box, &x, &y, &w, &h); + if (pl) *pl = x; + if (pr) *pr = x + w - 1; + if (pt) *pt = y; + if (pb) *pb = y + h - 1; + return 0; +} + + +/*! + * \brief boxSetSideLocations() + * + * \param[in] box + * \param[in] l, r, t, b [optional] use -1 to leave unchanged + * \return 0 if OK, 1 on error + */ +l_ok +boxSetSideLocations(BOX *box, + l_int32 l, + l_int32 r, + l_int32 t, + l_int32 b) +{ +l_int32 x, y, w, h; + + PROCNAME("boxSetSideLocations"); + + if (!box) + return ERROR_INT("box not defined", procName, 1); + x = (l != -1) ? l : box->x; + w = (r != -1) ? r - x + 1 : box->x + box->w - x; + y = (t != -1) ? t : box->y; + h = (b != -1) ? b - y + 1 : box->y + box->h - y; + boxSetGeometry(box, x, y, w, h); + return 0; +} + + +/*! + * \brief Return the current reference count of %box + * + * \param[in] box + * \return refcount + */ +l_int32 +boxGetRefcount(BOX *box) +{ + PROCNAME("boxGetRefcount"); + + if (!box) + return ERROR_INT("box not defined", procName, UNDEF); + + return box->refcount; +} + +/*! + * \brief Adjust the current references count of %box by %delta + * + * \param[in] box ptr to box + * \param[in] delta adjustment, usually -1 or 1 + * \return 0 if OK, 1 on error + */ +l_ok +boxChangeRefcount(BOX *box, + l_int32 delta) +{ + PROCNAME("boxChangeRefcount"); + + if (!box) + return ERROR_INT("box not defined", procName, 1); + + box->refcount += delta; + return 0; +} + + +/*! + * \brief boxIsValid() + * + * \param[in] box + * \param[out] pvalid 1 if valid; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxIsValid(BOX *box, + l_int32 *pvalid) +{ + PROCNAME("boxIsValid"); + + if (!pvalid) + return ERROR_INT("&valid not defined", procName, 1); + *pvalid = 0; + if (!box) + return ERROR_INT("box not defined", procName, 1); + + if (box->w > 0 && box->h > 0) + *pvalid = 1; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa creation, destruction, copy, extension * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaCreate() + * + * \param[in] n initial number of ptrs; 0 for default + * \return boxa, or NULL on error + */ +BOXA * +boxaCreate(l_int32 n) +{ +BOXA *boxa; + + PROCNAME("boxaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + boxa = (BOXA *)LEPT_CALLOC(1, sizeof(BOXA)); + boxa->n = 0; + boxa->nalloc = n; + boxa->refcount = 1; + if ((boxa->box = (BOX **)LEPT_CALLOC(n, sizeof(BOX *))) == NULL) { + boxaDestroy(&boxa); + return (BOXA *)ERROR_PTR("boxa ptrs not made", procName, NULL); + } + return boxa; +} + + +/*! + * \brief boxaCopy() + * + * \param[in] boxa + * \param[in] copyflag L_COPY, L_CLONE, L_COPY_CLONE + * \return new boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) See pix.h for description of the copyflag.
+ *      (2) The copy-clone makes a new boxa that holds clones of each box.
+ * 
+ */ +BOXA * +boxaCopy(BOXA *boxa, + l_int32 copyflag) +{ +l_int32 i; +BOX *boxc; +BOXA *boxac; + + PROCNAME("boxaCopy"); + + if (!boxa) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + + if (copyflag == L_CLONE) { + boxa->refcount++; + return boxa; + } + + if (copyflag != L_COPY && copyflag != L_COPY_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + if ((boxac = boxaCreate(boxa->nalloc)) == NULL) + return (BOXA *)ERROR_PTR("boxac not made", procName, NULL); + for (i = 0; i < boxa->n; i++) { + if (copyflag == L_COPY) + boxc = boxaGetBox(boxa, i, L_COPY); + else /* copy-clone */ + boxc = boxaGetBox(boxa, i, L_CLONE); + boxaAddBox(boxac, boxc, L_INSERT); + } + return boxac; +} + + +/*! + * \brief boxaDestroy() + * + * \param[in,out] pboxa will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the boxa.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +boxaDestroy(BOXA **pboxa) +{ +l_int32 i; +BOXA *boxa; + + PROCNAME("boxaDestroy"); + + if (pboxa == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((boxa = *pboxa) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the boxa. */ + boxa->refcount--; + if (boxa->refcount <= 0) { + for (i = 0; i < boxa->n; i++) + boxDestroy(&boxa->box[i]); + LEPT_FREE(boxa->box); + LEPT_FREE(boxa); + } + + *pboxa = NULL; + return; +} + + +/*! + * \brief boxaAddBox() + * + * \param[in] boxa + * \param[in] box to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +boxaAddBox(BOXA *boxa, + BOX *box, + l_int32 copyflag) +{ +l_int32 n; +BOX *boxc; + + PROCNAME("boxaAddBox"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + if (copyflag == L_INSERT) + boxc = box; + else if (copyflag == L_COPY) + boxc = boxCopy(box); + else if (copyflag == L_CLONE) + boxc = boxClone(box); + else + return ERROR_INT("invalid copyflag", procName, 1); + if (!boxc) + return ERROR_INT("boxc not made", procName, 1); + + n = boxaGetCount(boxa); + if (n >= boxa->nalloc) + boxaExtendArray(boxa); + boxa->box[n] = boxc; + boxa->n++; + + return 0; +} + + +/*! + * \brief boxaExtendArray() + * + * \param[in] boxa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Reallocs with doubled size of ptr array.
+ * 
+ */ +l_ok +boxaExtendArray(BOXA *boxa) +{ + PROCNAME("boxaExtendArray"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + return boxaExtendArrayToSize(boxa, 2 * boxa->nalloc); +} + + +/*! + * \brief boxaExtendArrayToSize() + * + * \param[in] boxa + * \param[in] size new size of boxa array + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If necessary, reallocs new boxa ptr array to %size.
+ * 
+ */ +l_ok +boxaExtendArrayToSize(BOXA *boxa, + l_int32 size) +{ + PROCNAME("boxaExtendArrayToSize"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + if (size > boxa->nalloc) { + if ((boxa->box = (BOX **)reallocNew((void **)&boxa->box, + sizeof(BOX *) * boxa->nalloc, + size * sizeof(BOX *))) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + boxa->nalloc = size; + } + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaGetCount() + * + * \param[in] boxa + * \return count of all boxes; 0 if no boxes or on error + */ +l_int32 +boxaGetCount(BOXA *boxa) +{ + PROCNAME("boxaGetCount"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 0); + return boxa->n; +} + + +/*! + * \brief boxaGetValidCount() + * + * \param[in] boxa + * \return count of valid boxes; 0 if no valid boxes or on error + */ +l_int32 +boxaGetValidCount(BOXA *boxa) +{ +l_int32 n, i, w, h, count; + + PROCNAME("boxaGetValidCount"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 0); + + n = boxaGetCount(boxa); + for (i = 0, count = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + if (w > 0 && h > 0) + count++; + } + return count; +} + + +/*! + * \brief boxaGetBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL on error + */ +BOX * +boxaGetBox(BOXA *boxa, + l_int32 index, + l_int32 accessflag) +{ + PROCNAME("boxaGetBox"); + + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + if (index < 0 || index >= boxa->n) + return (BOX *)ERROR_PTR("index not valid", procName, NULL); + + if (accessflag == L_COPY) + return boxCopy(boxa->box[index]); + else if (accessflag == L_CLONE) + return boxClone(boxa->box[index]); + else + return (BOX *)ERROR_PTR("invalid accessflag", procName, NULL); +} + + +/*! + * \brief boxaGetValidBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL if box is not valid or on error + * + *
+ * Notes:
+ *      (1) This returns NULL for an invalid box in a boxa.
+ *          For a box to be valid, both the width and height must be > 0.
+ *      (2) We allow invalid boxes, with w = 0 or h = 0, as placeholders
+ *          in boxa for which the index of the box in the boxa is important.
+ *          This is an atypical situation; usually you want to put only
+ *          valid boxes in a boxa.
+ * 
+ */ +BOX * +boxaGetValidBox(BOXA *boxa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 w, h; +BOX *box; + + PROCNAME("boxaGetValidBox"); + + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + + if ((box = boxaGetBox(boxa, index, accessflag)) == NULL) + return (BOX *)ERROR_PTR("box not returned", procName, NULL); + boxGetGeometry(box, NULL, NULL, &w, &h); + if (w <= 0 || h <= 0) /* not valid, but not necessarily an error */ + boxDestroy(&box); + return box; +} + + +/*! + * \brief boxaFindInvalidBoxes() + * + * \param[in] boxa + * \return na numa of invalid boxes; NULL if there are none or on error + */ +NUMA * +boxaFindInvalidBoxes(BOXA *boxa) +{ +l_int32 i, n, w, h; +NUMA *na; + + PROCNAME("boxaFindInvalidBoxes"); + + if (!boxa) + return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); + + n = boxaGetCount(boxa); + if (boxaGetValidCount(boxa) == n) + return NULL; + + na = numaMakeConstant(0, n); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + if (w == 0 || h == 0) + numaSetValue(na, i, 1); + } + return na; +} + + +/*! + * \brief boxaGetBoxGeometry() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +boxaGetBoxGeometry(BOXA *boxa, + l_int32 index, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ +BOX *box; + + PROCNAME("boxaGetBoxGeometry"); + + if (px) *px = 0; + if (py) *py = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (index < 0 || index >= boxa->n) + return ERROR_INT("index not valid", procName, 1); + + if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) + return ERROR_INT("box not found!", procName, 1); + boxGetGeometry(box, px, py, pw, ph); + boxDestroy(&box); + return 0; +} + + +/*! + * \brief boxaIsFull() + * + * \param[in] boxa + * \param[out] pfull 1 if boxa is full; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxaIsFull(BOXA *boxa, + l_int32 *pfull) +{ +l_int32 i, n, full; +BOX *box; + + PROCNAME("boxaIsFull"); + + if (!pfull) + return ERROR_INT("&full not defined", procName, 1); + *pfull = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + full = 1; + for (i = 0; i < n; i++) { + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) { + full = 0; + break; + } + boxDestroy(&box); + } + *pfull = full; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa array modifiers * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaReplaceBox() + * + * \param[in] boxa + * \param[in] index to the index-th box + * \param[in] box insert this box to replace existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place replacement of one box; the input %box is now
+ *          owned by the boxa.
+ *      (2) The previous box at that location, if any, is destroyed.
+ * 
+ */ +l_ok +boxaReplaceBox(BOXA *boxa, + l_int32 index, + BOX *box) +{ + PROCNAME("boxaReplaceBox"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (index < 0 || index >= boxa->n) + return ERROR_INT("index not valid", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + boxDestroy(&(boxa->box[index])); + boxa->box[index] = box; + return 0; +} + + +/*! + * \brief boxaInsertBox() + * + * \param[in] boxa + * \param[in] index location in boxa to insert new value + * \param[in] box new box to be inserted; the boxa now owns it + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts box[i] --> box[i + 1] for all i >= index,
+ *          and then inserts box as box[index].
+ *      (2) To insert at the beginning of the array, set index = 0.
+ *      (3) To append to the array, it's easier to use boxaAddBox().
+ *      (4) This should not be used repeatedly to insert into large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +boxaInsertBox(BOXA *boxa, + l_int32 index, + BOX *box) +{ +l_int32 i, n; +BOX **array; + + PROCNAME("boxaInsertBox"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + n = boxaGetCount(boxa); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + if (n >= boxa->nalloc) + boxaExtendArray(boxa); + array = boxa->box; + boxa->n++; + for (i = n; i > index; i--) + array[i] = array[i - 1]; + array[index] = box; + + return 0; +} + + +/*! + * \brief boxaRemoveBox() + * + * \param[in] boxa + * \param[in] index of box to be removed and destroyed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes box[index] and then shifts
+ *          box[i] --> box[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly to remove boxes from
+ *          large arrays, because the function is O(n).
+ * 
+ */ +l_ok +boxaRemoveBox(BOXA *boxa, + l_int32 index) +{ + return boxaRemoveBoxAndSave(boxa, index, NULL); +} + + +/*! + * \brief boxaRemoveBoxAndSave() + * + * \param[in] boxa + * \param[in] index of box to be removed + * \param[out] pbox [optional] removed box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes box[index] and then shifts
+ *          box[i] --> box[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly to remove boxes from
+ *          large arrays, because the function is O(n).
+ * 
+ */ +l_ok +boxaRemoveBoxAndSave(BOXA *boxa, + l_int32 index, + BOX **pbox) +{ +l_int32 i, n; +BOX **array; + + PROCNAME("boxaRemoveBoxAndSave"); + + if (pbox) *pbox = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + n = boxaGetCount(boxa); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + if (pbox) + *pbox = boxaGetBox(boxa, index, L_CLONE); + array = boxa->box; + boxDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + boxa->n--; + + return 0; +} + + +/*! + * \brief boxaSaveValid() + * + * \param[in] boxas + * \param[in] copyflag L_COPY or L_CLONE + * \return boxad if OK, NULL on error + * + *
+ * Notes:
+ *      (1) This makes a copy/clone of each valid box.
+ * 
+ */ +BOXA * +boxaSaveValid(BOXA *boxas, + l_int32 copyflag) +{ +l_int32 i, n; +BOX *box; +BOXA *boxad; + + PROCNAME("boxaSaveValid"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + n = boxaGetCount(boxas); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxas, i, copyflag)) != NULL) + boxaAddBox(boxad, box, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxaInitFull() + * + * \param[in] boxa typically empty + * \param[in] box [optional] to be replicated into the entire ptr array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This initializes a boxa by filling up the entire box ptr array
+ *          with copies of %box.  If %box == NULL, use a placeholder box
+ *          of zero size.  Any existing boxes are destroyed.
+ *          After this opepration, the number of boxes is equal to
+ *          the number of allocated ptrs.
+ *      (2) Note that we use boxaReplaceBox() instead of boxaInsertBox().
+ *          They both have the same effect when inserting into a NULL ptr
+ *          in the boxa ptr array:
+ *      (3) Example usage.  This function is useful to prepare for a
+ *          random insertion (or replacement) of boxes into a boxa.
+ *          To randomly insert boxes into a boxa, up to some index "max":
+ *             Boxa *boxa = boxaCreate(max);
+ *             boxaInitFull(boxa, NULL);
+ *          If you want placeholder boxes of non-zero size:
+ *             Boxa *boxa = boxaCreate(max);
+ *             Box *box = boxCreate(...);
+ *             boxaInitFull(boxa, box);
+ *             boxDestroy(&box);
+ *          If we have an existing boxa with a smaller ptr array, it can
+ *          be reused for up to max boxes:
+ *             boxaExtendArrayToSize(boxa, max);
+ *             boxaInitFull(boxa, NULL);
+ *          The initialization allows the boxa to always be properly
+ *          filled, even if all the boxes are not later replaced.
+ *          If you want to know which boxes have been replaced,
+ *          and you initialized with invalid zero-sized boxes,
+ *          use boxaGetValidBox() to return NULL for the invalid boxes.
+ * 
+ */ +l_ok +boxaInitFull(BOXA *boxa, + BOX *box) +{ +l_int32 i, n; +BOX *boxt; + + PROCNAME("boxaInitFull"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxa->nalloc; + boxa->n = n; + for (i = 0; i < n; i++) { + if (box) + boxt = boxCopy(box); + else + boxt = boxCreate(0, 0, 0, 0); + boxaReplaceBox(boxa, i, boxt); + } + return 0; +} + + +/*! + * \brief boxaClear() + * + * \param[in] boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys all boxes in the boxa, setting the ptrs
+ *          to null.  The number of allocated boxes, n, is set to 0.
+ * 
+ */ +l_ok +boxaClear(BOXA *boxa) +{ +l_int32 i, n; + + PROCNAME("boxaClear"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) + boxDestroy(&boxa->box[i]); + boxa->n = 0; + return 0; +} + + +/*--------------------------------------------------------------------------* + * Boxaa creation, destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief boxaaCreate() + * + * \param[in] n size of boxa ptr array to be alloc'd; 0 for default + * \return baa, or NULL on error + */ +BOXAA * +boxaaCreate(l_int32 n) +{ +BOXAA *baa; + + PROCNAME("boxaaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + baa = (BOXAA *)LEPT_CALLOC(1, sizeof(BOXAA)); + if ((baa->boxa = (BOXA **)LEPT_CALLOC(n, sizeof(BOXA *))) == NULL) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa ptr array not made", procName, NULL); + } + baa->nalloc = n; + baa->n = 0; + return baa; +} + + +/*! + * \brief boxaaCopy() + * + * \param[in] baas input boxaa to be copied + * \param[in] copyflag L_COPY, L_CLONE + * \return baad new boxaa, composed of copies or clones of the boxa + * in baas, or NULL on error + * + *
+ * Notes:
+ *      (1) L_COPY makes a copy of each boxa in baas.
+ *          L_CLONE makes a clone of each boxa in baas.
+ * 
+ */ +BOXAA * +boxaaCopy(BOXAA *baas, + l_int32 copyflag) +{ +l_int32 i, n; +BOXA *boxa; +BOXAA *baad; + + PROCNAME("boxaaCopy"); + + if (!baas) + return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); + + n = boxaaGetCount(baas); + baad = boxaaCreate(n); + for (i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baas, i, copyflag); + boxaaAddBoxa(baad, boxa, L_INSERT); + } + + return baad; +} + + +/*! + * \brief boxaaDestroy() + * + * \param[in,out] pbaa will be set to null before returning + */ +void +boxaaDestroy(BOXAA **pbaa) +{ +l_int32 i; +BOXAA *baa; + + PROCNAME("boxaaDestroy"); + + if (pbaa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((baa = *pbaa) == NULL) + return; + + for (i = 0; i < baa->n; i++) + boxaDestroy(&baa->boxa[i]); + LEPT_FREE(baa->boxa); + LEPT_FREE(baa); + *pbaa = NULL; + + return; +} + + + +/*--------------------------------------------------------------------------* + * Add Boxa to Boxaa * + *--------------------------------------------------------------------------*/ +/*! + * \brief boxaaAddBoxa() + * + * \param[in] baa + * \param[in] ba to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +boxaaAddBoxa(BOXAA *baa, + BOXA *ba, + l_int32 copyflag) +{ +l_int32 n; +BOXA *bac; + + PROCNAME("boxaaAddBoxa"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) + return ERROR_INT("invalid copyflag", procName, 1); + + if (copyflag == L_INSERT) + bac = ba; + else + bac = boxaCopy(ba, copyflag); + + n = boxaaGetCount(baa); + if (n >= baa->nalloc) + boxaaExtendArray(baa); + baa->boxa[n] = bac; + baa->n++; + return 0; +} + + +/*! + * \brief boxaaExtendArray() + * + * \param[in] baa + * \return 0 if OK, 1 on error + */ +l_ok +boxaaExtendArray(BOXAA *baa) +{ + + PROCNAME("boxaaExtendArray"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + if ((baa->boxa = (BOXA **)reallocNew((void **)&baa->boxa, + sizeof(BOXA *) * baa->nalloc, + 2 * sizeof(BOXA *) * baa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + baa->nalloc *= 2; + return 0; +} + + +/*! + * \brief boxaaExtendArrayToSize() + * + * \param[in] baa + * \param[in] size new size of boxa array + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If necessary, reallocs the boxa ptr array to %size.
+ * 
+ */ +l_ok +boxaaExtendArrayToSize(BOXAA *baa, + l_int32 size) +{ + PROCNAME("boxaaExtendArrayToSize"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + if (size > baa->nalloc) { + if ((baa->boxa = (BOXA **)reallocNew((void **)&baa->boxa, + sizeof(BOXA *) * baa->nalloc, + size * sizeof(BOXA *))) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + baa->nalloc = size; + } + return 0; +} + + +/*----------------------------------------------------------------------* + * Boxaa accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaGetCount() + * + * \param[in] baa + * \return count number of boxa, or 0 if no boxa or on error + */ +l_int32 +boxaaGetCount(BOXAA *baa) +{ + PROCNAME("boxaaGetCount"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 0); + return baa->n; +} + + +/*! + * \brief boxaaGetBoxCount() + * + * \param[in] baa + * \return count number of boxes, or 0 if no boxes or on error + */ +l_int32 +boxaaGetBoxCount(BOXAA *baa) +{ +BOXA *boxa; +l_int32 n, sum, i; + + PROCNAME("boxaaGetBoxCount"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 0); + + n = boxaaGetCount(baa); + for (sum = 0, i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + sum += boxaGetCount(boxa); + boxaDestroy(&boxa); + } + + return sum; +} + + +/*! + * \brief boxaaGetBoxa() + * + * \param[in] baa + * \param[in] index to the index-th boxa + * \param[in] accessflag L_COPY or L_CLONE + * \return boxa, or NULL on error + */ +BOXA * +boxaaGetBoxa(BOXAA *baa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 n; + + PROCNAME("boxaaGetBoxa"); + + if (!baa) + return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return (BOXA *)ERROR_PTR("index not valid", procName, NULL); + if (accessflag != L_COPY && accessflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid accessflag", procName, NULL); + + return boxaCopy(baa->boxa[index], accessflag); +} + + +/*! + * \brief boxaaGetBox() + * + * \param[in] baa + * \param[in] iboxa index into the boxa array in the boxaa + * \param[in] ibox index into the box array in the boxa + * \param[in] accessflag L_COPY or L_CLONE + * \return box, or NULL on error + */ +BOX * +boxaaGetBox(BOXAA *baa, + l_int32 iboxa, + l_int32 ibox, + l_int32 accessflag) +{ +BOX *box; +BOXA *boxa; + + PROCNAME("boxaaGetBox"); + + if ((boxa = boxaaGetBoxa(baa, iboxa, L_CLONE)) == NULL) + return (BOX *)ERROR_PTR("boxa not retrieved", procName, NULL); + if ((box = boxaGetBox(boxa, ibox, accessflag)) == NULL) + L_ERROR("box not retrieved\n", procName); + boxaDestroy(&boxa); + return box; +} + + +/*----------------------------------------------------------------------* + * Boxaa array modifiers * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaInitFull() + * + * \param[in] baa typically empty + * \param[in] boxa to be replicated into the entire ptr array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This initializes a boxaa by filling up the entire boxa ptr array
+ *          with copies of %boxa.  Any existing boxa are destroyed.
+ *          After this operation, the number of boxa is equal to
+ *          the number of allocated ptrs.
+ *      (2) Note that we use boxaaReplaceBox() instead of boxaInsertBox().
+ *          They both have the same effect when inserting into a NULL ptr
+ *          in the boxa ptr array
+ *      (3) Example usage.  This function is useful to prepare for a
+ *          random insertion (or replacement) of boxa into a boxaa.
+ *          To randomly insert boxa into a boxaa, up to some index "max":
+ *             Boxaa *baa = boxaaCreate(max);
+ *               // initialize the boxa
+ *             Boxa *boxa = boxaCreate(...);
+ *             ...  [optionally fix with boxes]
+ *             boxaaInitFull(baa, boxa);
+ *          A typical use is to initialize the array with empty boxa,
+ *          and to replace only a subset that must be aligned with
+ *          something else, such as a pixa.
+ * 
+ */ +l_ok +boxaaInitFull(BOXAA *baa, + BOXA *boxa) +{ +l_int32 i, n; +BOXA *boxat; + + PROCNAME("boxaaInitFull"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = baa->nalloc; + baa->n = n; + for (i = 0; i < n; i++) { + boxat = boxaCopy(boxa, L_COPY); + boxaaReplaceBoxa(baa, i, boxat); + } + return 0; +} + + +/*! + * \brief boxaaExtendWithInit() + * + * \param[in] baa + * \param[in] maxindex + * \param[in] boxa to be replicated into the extended ptr array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This should be used on an existing boxaa that has been
+ *          fully loaded with boxa.  It then extends the boxaa,
+ *          loading all the additional ptrs with copies of boxa.
+ *          Typically, boxa will be empty.
+ * 
+ */ +l_ok +boxaaExtendWithInit(BOXAA *baa, + l_int32 maxindex, + BOXA *boxa) +{ +l_int32 i, n; + + PROCNAME("boxaaExtendWithInit"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + /* Extend the ptr array if necessary */ + n = boxaaGetCount(baa); + if (maxindex < n) return 0; + boxaaExtendArrayToSize(baa, maxindex + 1); + + /* Fill the new entries with copies of boxa */ + for (i = n; i <= maxindex; i++) + boxaaAddBoxa(baa, boxa, L_COPY); + return 0; +} + + +/*! + * \brief boxaaReplaceBoxa() + * + * \param[in] baa + * \param[in] index to the index-th boxa + * \param[in] boxa insert and replace any existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Any existing boxa is destroyed, and the input one
+ *          is inserted in its place.
+ *      (2) If the index is invalid, return 1 (error)
+ * 
+ */ +l_ok +boxaaReplaceBoxa(BOXAA *baa, + l_int32 index, + BOXA *boxa) +{ +l_int32 n; + + PROCNAME("boxaaReplaceBoxa"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + + boxaDestroy(&baa->boxa[index]); + baa->boxa[index] = boxa; + return 0; +} + + +/*! + * \brief boxaaInsertBoxa() + * + * \param[in] baa + * \param[in] index location in boxaa to insert new boxa + * \param[in] boxa new boxa to be inserted + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts boxa[i] --> boxa[i + 1] for all i >= index,
+ *          and then inserts boxa as boxa[index].
+ *      (2) To insert at the beginning of the array, set index = 0.
+ *      (3) To append to the array, it's easier to use boxaaAddBoxa().
+ *      (4) This should not be used repeatedly to insert into large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +boxaaInsertBoxa(BOXAA *baa, + l_int32 index, + BOXA *boxa) +{ +l_int32 i, n; +BOXA **array; + + PROCNAME("boxaaInsertBoxa"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + n = boxaaGetCount(baa); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + if (n >= baa->nalloc) + boxaaExtendArray(baa); + array = baa->boxa; + baa->n++; + for (i = n; i > index; i--) + array[i] = array[i - 1]; + array[index] = boxa; + + return 0; +} + + +/*! + * \brief boxaaRemoveBoxa() + * + * \param[in] baa + * \param[in] index of the boxa to be removed and destroyed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes boxa[index] and then shifts
+ *          boxa[i] --> boxa[i - 1] for all i > index.
+ *      (2) The removed boxaa is destroyed.
+ *      (2) This should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +boxaaRemoveBoxa(BOXAA *baa, + l_int32 index) +{ +l_int32 i, n; +BOXA **array; + + PROCNAME("boxaaRemoveBox"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + + array = baa->boxa; + boxaDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + baa->n--; + + return 0; +} + + +/*! + * \brief boxaaAddBox() + * + * \param[in] baa + * \param[in] index of boxa with boxaa + * \param[in] box to be added + * \param[in] accessflag L_INSERT, L_COPY or L_CLONE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Adds to an existing boxa only.
+ * 
+ */ +l_ok +boxaaAddBox(BOXAA *baa, + l_int32 index, + BOX *box, + l_int32 accessflag) +{ +l_int32 n; +BOXA *boxa; + PROCNAME("boxaaAddBox"); + + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + n = boxaaGetCount(baa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + if (accessflag != L_INSERT && accessflag != L_COPY && accessflag != L_CLONE) + return ERROR_INT("invalid accessflag", procName, 1); + + boxa = boxaaGetBoxa(baa, index, L_CLONE); + boxaAddBox(boxa, box, accessflag); + boxaDestroy(&boxa); + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxaa serialized I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaaReadFromFiles() + * + * \param[in] dirname directory + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] first 0-based + * \param[in] nfiles use 0 for everything from %first to the end + * \return baa, or NULL on error or if no boxa files are found. + * + *
+ * Notes:
+ *      (1) The files must be serialized boxa files (e.g., *.ba).
+ *          If some files cannot be read, warnings are issued.
+ *      (2) Use %substr to filter filenames in the directory.  If
+ *          %substr == NULL, this takes all files.
+ *      (3) After filtering, use %first and %nfiles to select
+ *          a contiguous set of files, that have been lexically
+ *          sorted in increasing order.
+ * 
+ */ +BOXAA * +boxaaReadFromFiles(const char *dirname, + const char *substr, + l_int32 first, + l_int32 nfiles) +{ +char *fname; +l_int32 i, n; +BOXA *boxa; +BOXAA *baa; +SARRAY *sa; + + PROCNAME("boxaaReadFromFiles"); + + if (!dirname) + return (BOXAA *)ERROR_PTR("dirname not defined", procName, NULL); + + sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); + if (!sa || ((n = sarrayGetCount(sa)) == 0)) { + sarrayDestroy(&sa); + return (BOXAA *)ERROR_PTR("no pixa files found", procName, NULL); + } + + baa = boxaaCreate(n); + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if ((boxa = boxaRead(fname)) == NULL) { + L_ERROR("boxa not read for %d-th file", procName, i); + continue; + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + + sarrayDestroy(&sa); + return baa; +} + + +/*! + * \brief boxaaRead() + * + * \param[in] filename + * \return boxaa, or NULL on error + */ +BOXAA * +boxaaRead(const char *filename) +{ +FILE *fp; +BOXAA *baa; + + PROCNAME("boxaaRead"); + + if (!filename) + return (BOXAA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (BOXAA *)ERROR_PTR("stream not opened", procName, NULL); + baa = boxaaReadStream(fp); + fclose(fp); + if (!baa) + return (BOXAA *)ERROR_PTR("boxaa not read", procName, NULL); + return baa; +} + + +/*! + * \brief boxaaReadStream() + * + * \param[in] fp input file stream + * \return boxaa, or NULL on error + */ +BOXAA * +boxaaReadStream(FILE *fp) +{ +l_int32 n, i, x, y, w, h, version; +l_int32 ignore; +BOXA *boxa; +BOXAA *baa; + + PROCNAME("boxaaReadStream"); + + if (!fp) + return (BOXAA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nBoxaa Version %d\n", &version) != 1) + return (BOXAA *)ERROR_PTR("not a boxaa file", procName, NULL); + if (version != BOXAA_VERSION_NUMBER) + return (BOXAA *)ERROR_PTR("invalid boxa version", procName, NULL); + if (fscanf(fp, "Number of boxa = %d\n", &n) != 1) + return (BOXAA *)ERROR_PTR("not a boxaa file", procName, NULL); + + if ((baa = boxaaCreate(n)) == NULL) + return (BOXAA *)ERROR_PTR("boxaa not made", procName, NULL); + for (i = 0; i < n; i++) { + if (fscanf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", + &ignore, &x, &y, &w, &h) != 5) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa descr not valid", procName, NULL); + } + if ((boxa = boxaReadStream(fp)) == NULL) { + boxaaDestroy(&baa); + return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + return baa; +} + + +/*! + * \brief boxaaReadMem() + * + * \param[in] data serialization of boxaa; in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return baa, or NULL on error + */ +BOXAA * +boxaaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +BOXAA *baa; + + PROCNAME("boxaaReadMem"); + + if (!data) + return (BOXAA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (BOXAA *)ERROR_PTR("stream not opened", procName, NULL); + + baa = boxaaReadStream(fp); + fclose(fp); + if (!baa) L_ERROR("baa not read\n", procName); + return baa; +} + + +/*! + * \brief boxaaWrite() + * + * \param[in] filename + * \param[in] baa + * \return 0 if OK, 1 on error + */ +l_ok +boxaaWrite(const char *filename, + BOXAA *baa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("boxaaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = boxaaWriteStream(fp, baa); + fclose(fp); + if (ret) + return ERROR_INT("baa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief boxaaWriteStream() + * + * \param[in] fp output file stream + * \param[in] baa + * \return 0 if OK, 1 on error + */ +l_ok +boxaaWriteStream(FILE *fp, + BOXAA *baa) +{ +l_int32 n, i, x, y, w, h; +BOX *box; +BOXA *boxa; + + PROCNAME("boxaaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + n = boxaaGetCount(baa); + fprintf(fp, "\nBoxaa Version %d\n", BOXAA_VERSION_NUMBER); + fprintf(fp, "Number of boxa = %d\n", n); + + for (i = 0; i < n; i++) { + if ((boxa = boxaaGetBoxa(baa, i, L_CLONE)) == NULL) + return ERROR_INT("boxa not found", procName, 1); + boxaGetExtent(boxa, NULL, NULL, &box); + boxGetGeometry(box, &x, &y, &w, &h); + fprintf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", + i, x, y, w, h); + boxaWriteStream(fp, boxa); + boxDestroy(&box); + boxaDestroy(&boxa); + } + return 0; +} + + +/*! + * \brief boxaaWriteMem() + * + * \param[out] pdata data of serialized boxaa; ascii + * \param[out] psize size of returned data + * \param[in] baa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a boxaa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +boxaaWriteMem(l_uint8 **pdata, + size_t *psize, + BOXAA *baa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("boxaaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = boxaaWriteStream(fp, baa); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = boxaaWriteStream(fp, baa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*---------------------------------------------------------------------* + * Boxa serialized I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaRead() + * + * \param[in] filename + * \return boxa, or NULL on error + */ +BOXA * +boxaRead(const char *filename) +{ +FILE *fp; +BOXA *boxa; + + PROCNAME("boxaRead"); + + if (!filename) + return (BOXA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (BOXA *)ERROR_PTR("stream not opened", procName, NULL); + boxa = boxaReadStream(fp); + fclose(fp); + if (!boxa) + return (BOXA *)ERROR_PTR("boxa not read", procName, NULL); + return boxa; +} + + +/*! + * \brief boxaReadStream() + * + * \param[in] fp input file stream + * \return boxa, or NULL on error + */ +BOXA * +boxaReadStream(FILE *fp) +{ +l_int32 n, i, x, y, w, h, version; +l_int32 ignore; +BOX *box; +BOXA *boxa; + + PROCNAME("boxaReadStream"); + + if (!fp) + return (BOXA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nBoxa Version %d\n", &version) != 1) + return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); + if (version != BOXA_VERSION_NUMBER) + return (BOXA *)ERROR_PTR("invalid boxa version", procName, NULL); + if (fscanf(fp, "Number of boxes = %d\n", &n) != 1) + return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); + + if ((boxa = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); + for (i = 0; i < n; i++) { + if (fscanf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", + &ignore, &x, &y, &w, &h) != 5) { + boxaDestroy(&boxa); + return (BOXA *)ERROR_PTR("box descr not valid", procName, NULL); + } + box = boxCreate(x, y, w, h); + boxaAddBox(boxa, box, L_INSERT); + } + + return boxa; +} + + +/*! + * \brief boxaReadMem() + * + * \param[in] data serialization of boxa; in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return boxa, or NULL on error + */ +BOXA * +boxaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +BOXA *boxa; + + PROCNAME("boxaReadMem"); + + if (!data) + return (BOXA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (BOXA *)ERROR_PTR("stream not opened", procName, NULL); + + boxa = boxaReadStream(fp); + fclose(fp); + if (!boxa) L_ERROR("boxa not read\n", procName); + return boxa; +} + + +/*! + * \brief boxaWriteDebug() + * + * \param[in] filename + * \param[in] boxa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of boxaWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +boxaWriteDebug(const char *filename, + BOXA *boxa) +{ + PROCNAME("boxaWriteDebug"); + + if (LeptDebugOK) { + return boxaWrite(filename, boxa); + } else { + L_INFO("write to named temp file %s is disabled\n", procName, filename); + return 0; + } +} + + +/*! + * \brief boxaWrite() + * + * \param[in] filename + * \param[in] boxa + * \return 0 if OK, 1 on error + */ +l_ok +boxaWrite(const char *filename, + BOXA *boxa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("boxaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = boxaWriteStream(fp, boxa); + fclose(fp); + if (ret) + return ERROR_INT("boxa not written to stream", procName, 1); + + return 0; +} + + +/*! + * \brief boxaWriteStream() + * + * \param[in] fp output file stream + * \param[in] boxa + * \return 0 if OK, 1 on error + */ +l_ok +boxaWriteStream(FILE *fp, + BOXA *boxa) +{ +l_int32 n, i; +BOX *box; + + PROCNAME("boxaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + fprintf(fp, "\nBoxa Version %d\n", BOXA_VERSION_NUMBER); + fprintf(fp, "Number of boxes = %d\n", n); + for (i = 0; i < n; i++) { + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) + return ERROR_INT("box not found", procName, 1); + fprintf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", + i, box->x, box->y, box->w, box->h); + boxDestroy(&box); + } + return 0; +} + + +/*! + * \brief boxaWriteMem() + * + * \param[out] pdata data of serialized boxa; ascii + * \param[out] psize size of returned data + * \param[in] boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a boxa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +boxaWriteMem(l_uint8 **pdata, + size_t *psize, + BOXA *boxa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("boxaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = boxaWriteStream(fp, boxa); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = boxaWriteStream(fp, boxa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*---------------------------------------------------------------------* + * Debug printing * + *---------------------------------------------------------------------*/ +/*! + * \brief boxPrintStreamInfo() + * + * \param[in] fp output file stream + * \param[in] box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This outputs debug info.  Use serialization functions to
+ *          write to file if you want to read the data back.
+ * 
+ */ +l_ok +boxPrintStreamInfo(FILE *fp, + BOX *box) +{ + PROCNAME("boxPrintStreamInfo"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + fprintf(fp, " Box: x = %d, y = %d, w = %d, h = %d\n", + box->x, box->y, box->w, box->h); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxfunc1.c b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc1.c new file mode 100644 index 0000000..76d87e6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc1.c @@ -0,0 +1,2733 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxfunc1.c + *
+ *
+ *      Box geometry
+ *           l_int32   boxContains()
+ *           l_int32   boxIntersects()
+ *           BOXA     *boxaContainedInBox()
+ *           l_int32   boxaContainedInBoxCount()
+ *           l_int32   boxaContainedInBoxa()
+ *           BOXA     *boxaIntersectsBox()
+ *           l_int32   boxaIntersectsBoxCount()
+ *           BOXA     *boxaClipToBox()
+ *           BOXA     *boxaCombineOverlaps()
+ *           l_int32   boxaCombineOverlapsInPair()
+ *           BOX      *boxOverlapRegion()
+ *           BOX      *boxBoundingRegion()
+ *           l_int32   boxOverlapFraction()
+ *           l_int32   boxOverlapArea()
+ *           BOXA     *boxaHandleOverlaps()
+ *           l_int32   boxOverlapDistance()
+ *           l_int32   boxSeparationDistance()
+ *           l_int32   boxCompareSize()
+ *           l_int32   boxContainsPt()
+ *           BOX      *boxaGetNearestToPt()
+ *           BOX      *boxaGetNearestToLine()
+ *           l_int32   boxaFindNearestBoxes()
+ *           l_int32   boxaGetNearestByDirection()
+ *    static l_int32   boxHasOverlapInXorY()
+ *    static l_int32   boxGetDistanceInXorY()
+ *           l_int32   boxIntersectByLine()
+ *           l_int32   boxGetCenter()
+ *           BOX      *boxClipToRectangle()
+ *           l_int32   boxClipToRectangleParams()
+ *           BOX      *boxRelocateOneSide()
+ *           BOXA     *boxaAdjustSides()
+ *           BOXA     *boxaAdjustBoxSides()
+ *           BOX      *boxAdjustSides()
+ *           BOXA     *boxaSetSide()
+ *           l_int32   boxSetSide()
+ *           BOXA     *boxaAdjustWidthToTarget()
+ *           BOXA     *boxaAdjustHeightToTarget()
+ *           l_int32   boxEqual()
+ *           l_int32   boxaEqual()
+ *           l_int32   boxSimilar()
+ *           l_int32   boxaSimilar()
+ *
+ *      Boxa combine and split
+ *           l_int32   boxaJoin()
+ *           l_int32   boxaaJoin()
+ *           l_int32   boxaSplitEvenOdd()
+ *           BOXA     *boxaMergeEvenOdd()
+ * 
+ */ + +#include "allheaders.h" + +static l_int32 boxHasOverlapInXorY(l_int32 c1, l_int32 s1, l_int32 c2, + l_int32 s2); +static l_int32 boxGetDistanceInXorY(l_int32 c1, l_int32 s1, l_int32 c2, + l_int32 s2); + + +/*---------------------------------------------------------------------* + * Box geometry * + *---------------------------------------------------------------------*/ +/*! + * \brief boxContains() + * + * \param[in] box1, box2 + * \param[out] presult 1 if box2 is entirely contained within box1; + * 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxContains(BOX *box1, + BOX *box2, + l_int32 *presult) +{ +l_int32 x1, y1, w1, h1, x2, y2, w2, h2, valid1, valid2; + + PROCNAME("boxContains"); + + if (!presult) + return ERROR_INT("&result not defined", procName, 1); + *presult = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + boxGetGeometry(box1, &x1, &y1, &w1, &h1); + boxGetGeometry(box2, &x2, &y2, &w2, &h2); + if (x1 <= x2 && y1 <= y2 && (x1 + w1 >= x2 + w2) && (y1 + h1 >= y2 + h2)) + *presult = 1; + return 0; +} + + +/*! + * \brief boxIntersects() + * + * \param[in] box1, box2 + * \param[out] presult 1 if any part of box2 is contained in box1; + * 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxIntersects(BOX *box1, + BOX *box2, + l_int32 *presult) +{ +l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, valid1, valid2; + + PROCNAME("boxIntersects"); + + if (!presult) + return ERROR_INT("&result not defined", procName, 1); + *presult = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + boxGetGeometry(box1, &l1, &t1, &w1, &h1); + boxGetGeometry(box2, &l2, &t2, &w2, &h2); + r1 = l1 + w1 - 1; + r2 = l2 + w2 - 1; + b1 = t1 + h1 - 1; + b2 = t2 + h2 - 1; + if (b2 < t1 || b1 < t2 || r1 < l2 || r2 < l1) + *presult = 0; + else + *presult = 1; + return 0; +} + + +/*! + * \brief boxaContainedInBox() + * + * \param[in] boxas + * \param[in] box for containment + * \return boxad boxa with all boxes in boxas that are entirely + * contained in box, or NULL on error + * + *
+ * Notes:
+ *      (1) All boxes in %boxas that are entirely outside box are removed.
+ *      (2) If %box is not valid, returns an empty boxa.
+ * 
+ */ +BOXA * +boxaContainedInBox(BOXA *boxas, + BOX *box) +{ +l_int32 i, n, val, valid; +BOX *box1; +BOXA *boxad; + + PROCNAME("boxaContainedInBox"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!box) + return (BOXA *)ERROR_PTR("box not defined", procName, NULL); + n = boxaGetCount(boxas); + boxIsValid(box, &valid); + if (n == 0 || !valid) + return boxaCreate(1); /* empty */ + + boxad = boxaCreate(0); + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) + continue; + boxContains(box, box1, &val); + if (val == 1) + boxaAddBox(boxad, box1, L_COPY); + boxDestroy(&box1); /* destroy the clone */ + } + + return boxad; +} + + +/*! + * \brief boxaContainedInBoxCount() + * + * \param[in] boxa + * \param[in] box for selecting contained boxes in %boxa + * \param[out] pcount number of boxes intersecting the box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %box is not valid, returns a zero count.
+ * 
+ */ +l_ok +boxaContainedInBoxCount(BOXA *boxa, + BOX *box, + l_int32 *pcount) +{ +l_int32 i, n, val, valid; +BOX *box1; + + PROCNAME("boxaContainedInBoxCount"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + n = boxaGetCount(boxa); + boxIsValid(box, &valid); + if (n == 0 || !valid) + return 0; + + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) + continue; + boxContains(box, box1, &val); + if (val == 1) + (*pcount)++; + boxDestroy(&box1); + } + return 0; +} + + +/*! + * \brief boxaContainedInBoxa() + * + * \param[in] boxa1, boxa2 + * \param[out] pcontained 1 if every box in boxa2 is contained in + * some box in boxa1; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxaContainedInBoxa(BOXA *boxa1, + BOXA *boxa2, + l_int32 *pcontained) +{ +l_int32 i, j, n1, n2, cont, result; +BOX *box1, *box2; + + PROCNAME("boxaContainedInBoxa"); + + if (!pcontained) + return ERROR_INT("&contained not defined", procName, 1); + *pcontained = 0; + if (!boxa1 || !boxa2) + return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); + + n1 = boxaGetCount(boxa1); + n2 = boxaGetCount(boxa2); + for (i = 0; i < n2; i++) { + if ((box2 = boxaGetValidBox(boxa2, i, L_CLONE)) == NULL) + continue; + cont = 0; + for (j = 0; j < n1; j++) { + if ((box1 = boxaGetValidBox(boxa1, j, L_CLONE)) == NULL) + continue; + boxContains(box1, box2, &result); + boxDestroy(&box1); + if (result) { + cont = 1; + break; + } + } + boxDestroy(&box2); + if (!cont) return 0; + } + + *pcontained = 1; + return 0; +} + + +/*! + * \brief boxaIntersectsBox() + * + * \param[in] boxas + * \param[in] box for intersecting + * \return boxad boxa with all boxes in boxas that intersect box, + * or NULL on error + * + *
+ * Notes:
+ *      (1) All boxes in boxa that intersect with box (i.e., are completely
+ *          or partially contained in box) are retained.
+ * 
+ */ +BOXA * +boxaIntersectsBox(BOXA *boxas, + BOX *box) +{ +l_int32 i, n, val, valid; +BOX *box1; +BOXA *boxad; + + PROCNAME("boxaIntersectsBox"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!box) + return (BOXA *)ERROR_PTR("box not defined", procName, NULL); + n = boxaGetCount(boxas); + boxIsValid(box, &valid); + if (n == 0 || !valid) + return boxaCreate(1); /* empty */ + + boxad = boxaCreate(0); + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) + continue; + boxIntersects(box, box1, &val); + if (val == 1) + boxaAddBox(boxad, box1, L_COPY); + boxDestroy(&box1); /* destroy the clone */ + } + + return boxad; +} + + +/*! + * \brief boxaIntersectsBoxCount() + * + * \param[in] boxa + * \param[in] box for selecting intersecting boxes in %boxa + * \param[out] pcount number of boxes intersecting the box + * \return 0 if OK, 1 on error + */ +l_ok +boxaIntersectsBoxCount(BOXA *boxa, + BOX *box, + l_int32 *pcount) +{ +l_int32 i, n, val, valid; +BOX *box1; + + PROCNAME("boxaIntersectsBoxCount"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + n = boxaGetCount(boxa); + boxIsValid(box, &valid); + if (n == 0 || !valid) + return 0; + + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) + continue; + boxIntersects(box, box1, &val); + if (val == 1) + (*pcount)++; + boxDestroy(&box1); + } + return 0; +} + + +/*! + * \brief boxaClipToBox() + * + * \param[in] boxas + * \param[in] box for clipping + * \return boxad boxa with boxes in boxas clipped to box, or NULL on error + * + *
+ * Notes:
+ *      (1) All boxes in boxa not intersecting with box are removed, and
+ *          the remaining boxes are clipped to box.
+ * 
+ */ +BOXA * +boxaClipToBox(BOXA *boxas, + BOX *box) +{ +l_int32 i, n, valid; +BOX *box1, *boxo; +BOXA *boxad; + + PROCNAME("boxaClipToBox"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!box) + return (BOXA *)ERROR_PTR("box not defined", procName, NULL); + n = boxaGetCount(boxas); + boxIsValid(box, &valid); + if (n == 0 || !valid) + return boxaCreate(1); /* empty */ + + boxad = boxaCreate(0); + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) + continue; + if ((boxo = boxOverlapRegion(box, box1)) != NULL) + boxaAddBox(boxad, boxo, L_INSERT); + boxDestroy(&box1); + } + + return boxad; +} + + +/*! + * \brief boxaCombineOverlaps() + * + * \param[in] boxas + * \param[in,out] pixadb debug output + * \return boxad where each set of boxes in boxas that overlap are combined + * into a single bounding box in boxad, or NULL on error. + * + *
+ * Notes:
+ *      (1) If there are no overlapping boxes, it simply returns a copy
+ *          of %boxas.
+ *      (2) Input an empty %pixadb, using pixaCreate(0), for debug output.
+ *          The output gives 2 visualizations of the boxes per iteration;
+ *          boxes in red before, and added boxes in green after. Note that
+ *          all pixels in the red boxes are contained in the green ones.
+ *      (3) The alternative method of painting each rectangle and finding
+ *          the 4-connected components gives a different result in
+ *          general, because two non-overlapping (but touching)
+ *          rectangles, when rendered, are 4-connected and will be joined.
+ *      (4) A bad case computationally is to have n boxes, none of which
+ *          overlap.  Then you have one iteration with O(n^2) compares.
+ *          This is still faster than painting each rectangle and finding
+ *          the bounding boxes of the connected components, even for
+ *          thousands of rectangles.
+ * 
+ */ +BOXA * +boxaCombineOverlaps(BOXA *boxas, + PIXA *pixadb) +{ +l_int32 i, j, w, h, n1, n2, overlap, niters; +BOX *box1, *box2, *box3; +BOXA *boxa1, *boxa2; +PIX *pix1; + + PROCNAME("boxaCombineOverlaps"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + if (pixadb) boxaGetExtent(boxas, &w, &h, NULL); + + boxa1 = boxaCopy(boxas, L_COPY); + n1 = boxaGetCount(boxa1); + niters = 0; + while (1) { /* loop until no change from previous iteration */ + niters++; + if (pixadb) { + pix1 = pixCreate(w + 5, h + 5, 32); + pixSetAll(pix1); + pixRenderBoxaArb(pix1, boxa1, 2, 255, 0, 0); + pixaAddPix(pixadb, pix1, L_COPY); + } + + /* Combine overlaps for this iteration */ + for (i = 0; i < n1; i++) { + if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) + continue; + for (j = i + 1; j < n1; j++) { + if ((box2 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) + continue; + boxIntersects(box1, box2, &overlap); + if (overlap) { + box3 = boxBoundingRegion(box1, box2); + boxaReplaceBox(boxa1, i, box3); + boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); + boxDestroy(&box1); + box1 = boxCopy(box3); + } + boxDestroy(&box2); + } + boxDestroy(&box1); + } + boxa2 = boxaSaveValid(boxa1, L_COPY); + n2 = boxaGetCount(boxa2); + boxaDestroy(&boxa1); + boxa1 = boxa2; + if (n1 == n2) { + if (pixadb) pixDestroy(&pix1); + break; + } + n1 = n2; + if (pixadb) { + pixRenderBoxaArb(pix1, boxa1, 2, 0, 255, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + } + } + + if (pixadb) + L_INFO("number of iterations: %d\n", procName, niters); + return boxa1; +} + + +/*! + * \brief boxaCombineOverlapsInPair() + * + * \param[in] boxas1 input boxa1 + * \param[in] boxas2 input boxa2 + * \param[out] pboxad1 output boxa1 + * \param[out] pboxad2 output boxa2 + * \param[in,out] pixadb debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) One of three things happens to each box in %boxa1 and %boxa2:
+ *           * it gets absorbed into a larger box that it overlaps with
+ *           * it absorbs a smaller (by area) box that it overlaps with
+ *             and gets larger, using the bounding region of the 2 boxes
+ *           * it is unchanged (including absorbing smaller boxes that
+ *             are contained within it).
+ *      (2) If all the boxes from one of the input boxa are absorbed, this
+ *          returns an empty boxa.
+ *      (3) Input an empty %pixadb, using pixaCreate(0), for debug output
+ *      (4) This is useful if different operations are to be carried out
+ *          on possibly overlapping rectangular regions, and it is desired
+ *          to have only one operation on any rectangular region.
+ * 
+ */ +l_ok +boxaCombineOverlapsInPair(BOXA *boxas1, + BOXA *boxas2, + BOXA **pboxad1, + BOXA **pboxad2, + PIXA *pixadb) +{ +l_int32 i, j, w, h, w2, h2, n1, n2, n1i, n2i, niters; +l_int32 overlap, bigger, area1, area2; +BOX *box1, *box2, *box3; +BOXA *boxa1, *boxa2, *boxac1, *boxac2; +PIX *pix1; + + PROCNAME("boxaCombineOverlapsInPair"); + + if (pboxad1) *pboxad1 = NULL; + if (pboxad2) *pboxad2 = NULL; + if (!boxas1 || !boxas2) + return ERROR_INT("boxas1 and boxas2 not both defined", procName, 1); + if (!pboxad1 || !pboxad2) + return ERROR_INT("&boxad1 and &boxad2 not both defined", procName, 1); + + if (pixadb) { + boxaGetExtent(boxas1, &w, &h, NULL); + boxaGetExtent(boxas2, &w2, &h2, NULL); + w = L_MAX(w, w2); + h = L_MAX(h, w2); + } + + /* Let the boxa with the largest area have first crack at the other */ + boxaGetArea(boxas1, &area1); + boxaGetArea(boxas2, &area2); + if (area1 >= area2) { + boxac1 = boxaCopy(boxas1, L_COPY); + boxac2 = boxaCopy(boxas2, L_COPY); + } else { + boxac1 = boxaCopy(boxas2, L_COPY); + boxac2 = boxaCopy(boxas1, L_COPY); + } + + n1i = boxaGetCount(boxac1); + n2i = boxaGetCount(boxac2); + niters = 0; + while (1) { + niters++; + if (pixadb) { + pix1 = pixCreate(w + 5, h + 5, 32); + pixSetAll(pix1); + pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); + pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + /* First combine boxes in each set */ + boxa1 = boxaCombineOverlaps(boxac1, NULL); + boxa2 = boxaCombineOverlaps(boxac2, NULL); + + /* Now combine boxes between sets */ + n1 = boxaGetCount(boxa1); + n2 = boxaGetCount(boxa2); + for (i = 0; i < n1; i++) { /* 1 eats 2 */ + if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) + continue; + for (j = 0; j < n2; j++) { + if ((box2 = boxaGetValidBox(boxa2, j, L_COPY)) == NULL) + continue; + boxIntersects(box1, box2, &overlap); + boxCompareSize(box1, box2, L_SORT_BY_AREA, &bigger); + if (overlap && (bigger == 1)) { + box3 = boxBoundingRegion(box1, box2); + boxaReplaceBox(boxa1, i, box3); + boxaReplaceBox(boxa2, j, boxCreate(0, 0, 0, 0)); + boxDestroy(&box1); + box1 = boxCopy(box3); + } + boxDestroy(&box2); + } + boxDestroy(&box1); + } + for (i = 0; i < n2; i++) { /* 2 eats 1 */ + if ((box2 = boxaGetValidBox(boxa2, i, L_COPY)) == NULL) + continue; + for (j = 0; j < n1; j++) { + if ((box1 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) + continue; + boxIntersects(box1, box2, &overlap); + boxCompareSize(box2, box1, L_SORT_BY_AREA, &bigger); + if (overlap && (bigger == 1)) { + box3 = boxBoundingRegion(box1, box2); + boxaReplaceBox(boxa2, i, box3); + boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); + boxDestroy(&box2); + box2 = boxCopy(box3); + } + boxDestroy(&box1); + } + boxDestroy(&box2); + } + boxaDestroy(&boxac1); + boxaDestroy(&boxac2); + boxac1 = boxaSaveValid(boxa1, L_COPY); /* remove invalid boxes */ + boxac2 = boxaSaveValid(boxa2, L_COPY); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + n1 = boxaGetCount(boxac1); + n2 = boxaGetCount(boxac2); + if (n1 == n1i && n2 == n2i) break; + n1i = n1; + n2i = n2; + if (pixadb) { + pix1 = pixCreate(w + 5, h + 5, 32); + pixSetAll(pix1); + pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); + pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + } + } + + if (pixadb) + L_INFO("number of iterations: %d\n", procName, niters); + *pboxad1 = boxac1; + *pboxad2 = boxac2; + return 0; +} + + +/*! + * \brief boxOverlapRegion() + * + * \param[in] box1, box2 + * \return box of overlap region between input boxes; + * NULL if no overlap or on error + * + *
+ * Notes:
+ *      (1) This is the geometric intersection of the two rectangles.
+ * 
+ */ +BOX * +boxOverlapRegion(BOX *box1, + BOX *box2) +{ +l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; +l_int32 valid1, valid2; + + PROCNAME("boxOverlapRegion"); + + if (!box1 || !box2) + return (BOX *)ERROR_PTR("boxes not both defined", procName, NULL); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) { + L_WARNING("at least one box is invalid\n", procName); + return NULL; + } + + boxGetGeometry(box1, &l1, &t1, &w1, &h1); + boxGetGeometry(box2, &l2, &t2, &w2, &h2); + r1 = l1 + w1 - 1; + r2 = l2 + w2 - 1; + b1 = t1 + h1 - 1; + b2 = t2 + h2 - 1; + if (b2 < t1 || b1 < t2 || r1 < l2 || r2 < l1) + return NULL; + + ld = L_MAX(l1, l2); + td = L_MAX(t1, t2); + rd = L_MIN(r1, r2); + bd = L_MIN(b1, b2); + return boxCreate(ld, td, rd - ld + 1, bd - td + 1); +} + + +/*! + * \brief boxBoundingRegion() + * + * \param[in] box1, box2 + * \return box of bounding region containing the input boxes; + * NULL on error + * + *
+ * Notes:
+ *      (1) This is the geometric union of the two rectangles.
+ *      (2) Invalid boxes are ignored.  This returns an invalid box
+ *          if both input boxes are invalid.
+ *      (3) For the geometric union of a boxa, use boxaGetExtent().
+ * 
+ */ +BOX * +boxBoundingRegion(BOX *box1, + BOX *box2) +{ +l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; +l_int32 valid1, valid2; + + PROCNAME("boxBoundingRegion"); + + if (!box1 || !box2) + return (BOX *)ERROR_PTR("boxes not both defined", procName, NULL); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 && !valid2) { + L_WARNING("both boxes are invalid\n", procName); + return boxCreate(0, 0, 0, 0); + } + if (valid1 && !valid2) + return boxCopy(box1); + if (!valid1 && valid2) + return boxCopy(box2); + + boxGetGeometry(box1, &l1, &t1, &w1, &h1); + boxGetGeometry(box2, &l2, &t2, &w2, &h2); + r1 = l1 + w1 - 1; + r2 = l2 + w2 - 1; + b1 = t1 + h1 - 1; + b2 = t2 + h2 - 1; + ld = L_MIN(l1, l2); + td = L_MIN(t1, t2); + rd = L_MAX(r1, r2); + bd = L_MAX(b1, b2); + return boxCreate(ld, td, rd - ld + 1, bd - td + 1); +} + + +/*! + * \brief boxOverlapFraction() + * + * \param[in] box1, box2 + * \param[out] pfract the fraction of box2 overlapped by box1 + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) The result depends on the order of the input boxes,
+ *          because the overlap is taken as a fraction of box2.
+ *      (2) If at least one box is not valid, there is no overlap.
+ * 
+ */ +l_ok +boxOverlapFraction(BOX *box1, + BOX *box2, + l_float32 *pfract) +{ +l_int32 w2, h2, w, h, valid1, valid2; +BOX *boxo; + + PROCNAME("boxOverlapFraction"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) { + L_WARNING("boxes not both valid\n", procName); + return 0; + } + + if ((boxo = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ + return 0; + + boxGetGeometry(box2, NULL, NULL, &w2, &h2); + boxGetGeometry(boxo, NULL, NULL, &w, &h); + *pfract = (l_float32)(w * h) / (l_float32)(w2 * h2); + boxDestroy(&boxo); + return 0; +} + + +/*! + * \brief boxOverlapArea() + * + * \param[in] box1, box2 + * \param[out] parea the number of pixels in the overlap + * \return 0 if OK, 1 on error. + */ +l_ok +boxOverlapArea(BOX *box1, + BOX *box2, + l_int32 *parea) +{ +l_int32 w, h, valid1, valid2; +BOX *box; + + PROCNAME("boxOverlapArea"); + + if (!parea) + return ERROR_INT("&area not defined", procName, 1); + *parea = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + if ((box = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ + return 0; + + boxGetGeometry(box, NULL, NULL, &w, &h); + *parea = w * h; + boxDestroy(&box); + return 0; +} + + +/*! + * \brief boxaHandleOverlaps() + * + * \param[in] boxas + * \param[in] op L_COMBINE, L_REMOVE_SMALL + * \param[in] range forward distance over which overlaps + * are checked; > 0 + * \param[in] min_overlap minimum fraction of smaller box required for + * overlap to count; 0.0 to ignore + * \param[in] max_ratio maximum fraction of small/large areas for + * overlap to count; 1.0 to ignore + * \param[out] pnamap [optional] combining map + * \return boxad, or NULL on error. + * + *
+ * Notes:
+ *      (1) For all n(n-1)/2 box pairings, if two boxes overlap, either:
+ *          (a) op == L_COMBINE: get the bounding region for the two,
+ *              replace the larger with the bounding region, and remove
+ *              the smaller of the two, or
+ *          (b) op == L_REMOVE_SMALL: just remove the smaller.
+ *      (2) If boxas is 2D sorted, range can be small, but if it is
+ *          not spatially sorted, range should be large to allow all
+ *          pairwise comparisons to be made.
+ *      (3) The %min_overlap parameter allows ignoring small overlaps.
+ *          If %min_overlap == 1.0, only boxes fully contained in larger
+ *          boxes can be considered for removal; if %min_overlap == 0.0,
+ *          this constraint is ignored.
+ *      (4) The %max_ratio parameter allows ignoring overlaps between
+ *          boxes that are not too different in size.  If %max_ratio == 0.0,
+ *          no boxes can be removed; if %max_ratio == 1.0, this constraint
+ *          is ignored.
+ * 
+ */ +BOXA * +boxaHandleOverlaps(BOXA *boxas, + l_int32 op, + l_int32 range, + l_float32 min_overlap, + l_float32 max_ratio, + NUMA **pnamap) +{ +l_int32 i, j, n, w, h, area1, area2, val; +l_int32 overlap_area; +l_float32 overlap_ratio, area_ratio; +BOX *box1, *box2, *box3; +BOXA *boxat, *boxad; +NUMA *namap; + + PROCNAME("boxaHandleOverlaps"); + + if (pnamap) *pnamap = NULL; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (op != L_COMBINE && op != L_REMOVE_SMALL) + return (BOXA *)ERROR_PTR("invalid op", procName, NULL); + + n = boxaGetCount(boxas); + if (n == 0) + return boxaCreate(1); /* empty */ + if (range == 0) { + L_WARNING("range is 0\n", procName); + return boxaCopy(boxas, L_COPY); + } + + /* Identify smaller boxes in overlap pairs, and mark to eliminate. */ + namap = numaMakeConstant(-1, n); + for (i = 0; i < n; i++) { + if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) + continue; + boxGetGeometry(box1, NULL, NULL, &w, &h); + area1 = w * h; + if (area1 == 0) { + boxDestroy(&box1); + continue; + } + for (j = i + 1; j < i + 1 + range && j < n; j++) { + if ((box2 = boxaGetValidBox(boxas, j, L_CLONE)) == NULL) + continue; + boxOverlapArea(box1, box2, &overlap_area); + if (overlap_area > 0) { + boxGetGeometry(box2, NULL, NULL, &w, &h); + area2 = w * h; + if (area2 == 0) { + /* do nothing */ + } else if (area1 >= area2) { + overlap_ratio = (l_float32)overlap_area / (l_float32)area2; + area_ratio = (l_float32)area2 / (l_float32)area1; + if (overlap_ratio >= min_overlap && + area_ratio <= max_ratio) { + numaSetValue(namap, j, i); + } + } else { + overlap_ratio = (l_float32)overlap_area / (l_float32)area1; + area_ratio = (l_float32)area1 / (l_float32)area2; + if (overlap_ratio >= min_overlap && + area_ratio <= max_ratio) { + numaSetValue(namap, i, j); + } + } + } + boxDestroy(&box2); + } + boxDestroy(&box1); + } + + boxat = boxaCopy(boxas, L_COPY); + if (op == L_COMBINE) { + /* Resize the larger of the pair to the bounding region */ + for (i = 0; i < n; i++) { + numaGetIValue(namap, i, &val); + if (val >= 0) { + box1 = boxaGetBox(boxas, i, L_CLONE); /* smaller */ + box2 = boxaGetBox(boxas, val, L_CLONE); /* larger */ + box3 = boxBoundingRegion(box1, box2); + boxaReplaceBox(boxat, val, box3); + boxDestroy(&box1); + boxDestroy(&box2); + } + } + } + + /* Remove the smaller of the pairs */ + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(namap, i, &val); + if (val == -1) { + box1 = boxaGetBox(boxat, i, L_COPY); + boxaAddBox(boxad, box1, L_INSERT); + } + } + boxaDestroy(&boxat); + if (pnamap) + *pnamap = namap; + else + numaDestroy(&namap); + return boxad; +} + + +/*! + * \brief boxOverlapDistance() + * + * \param[in] box1, box2 two boxes, in any order + * \param[out] ph_ovl [optional] horizontal overlap + * \param[out] pv_ovl [optional] vertical overlap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This measures horizontal and vertical overlap of the
+ *          two boxes.  Horizontal and vertical overlap are measured
+ *          independently.  We need to consider several cases to clarify.
+ *      (2) A positive horizontal overlap means that there is at least
+ *          one point on the the %box1 boundary with the same x-component
+ *          as some point on the %box2 boundary.  Conversely, with a zero
+ *          or negative horizontal overlap, there are no boundary pixels
+ *          in %box1 that share an x-component with a boundary pixel in %box2.
+ *      (3) For a zero or negative horizontal overlap, o <= 0, the minimum
+ *          difference in the x-component between pixels on the boundaries
+ *          of the two boxes is d = -o + 1.
+ *      (4) Likewise for vertical overlaps.
+ * 
+ */ +l_ok +boxOverlapDistance(BOX *box1, + BOX *box2, + l_int32 *ph_ovl, + l_int32 *pv_ovl) +{ +l_int32 l1, t1, w1, h1, r1, b1, l2, t2, w2, h2, r2, b2, valid1, valid2; + + PROCNAME("boxOverlapDistance"); + + if (!ph_ovl && !pv_ovl) + return ERROR_INT("nothing to do", procName, 1); + if (ph_ovl) *ph_ovl = 0; + if (pv_ovl) *pv_ovl = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + if (ph_ovl) { + boxGetGeometry(box1, &l1, NULL, &w1, NULL); + boxGetGeometry(box2, &l2, NULL, &w2, NULL); + r1 = l1 + w1; /* 1 pixel to the right of box 1 */ + r2 = l2 + w2; + if (l2 >= l1) + *ph_ovl = r1 - l2; + else + *ph_ovl = r2 - l1; + } + if (pv_ovl) { + boxGetGeometry(box1, NULL, &t1, NULL, &h1); + boxGetGeometry(box2, NULL, &t2, NULL, &h2); + b1 = t1 + h1; /* 1 pixel below box 1 */ + b2 = t2 + h2; + if (t2 >= t1) + *pv_ovl = b1 - t2; + else + *pv_ovl = b2 - t1; + } + return 0; +} + + +/*! + * \brief boxSeparationDistance() + * + * \param[in] box1, box2 two boxes, in any order + * \param[out] ph_sep horizontal separation + * \param[out] pv_sep vertical separation + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This measures the Manhattan distance between the closest points
+ *          on the boundaries of the two boxes.  When the boxes overlap
+ *          (including touching along a line or at a corner), the
+ *          horizontal and vertical distances are 0.
+ *      (2) The distances represent the horizontal and vertical separation
+ *          of the two boxes.  The boxes have a nonzero intersection when
+ *          both the horizontal and vertical overlaps are positive, and
+ *          for that case both horizontal and vertical separation
+ *          distances are 0.
+ *      (3) If the horizontal overlap of the boxes is positive, the
+ *          horizontal separation between nearest points on respective
+ *          boundaries is 0, and likewise for the vertical overlap.
+ *      (4) If the horizontal overlap ho <= 0, the horizontal
+ *          separation between nearest points is d = -ho + 1.
+ *          Likewise, if the vertical overlap vo <= 0, the vertical
+ *          separation between nearest points is d = -vo + 1.
+ * 
+ */ +l_ok +boxSeparationDistance(BOX *box1, + BOX *box2, + l_int32 *ph_sep, + l_int32 *pv_sep) +{ +l_int32 h_ovl, v_ovl, valid1, valid2; + + PROCNAME("boxSeparationDistance"); + + if (ph_sep) *ph_sep = 0; + if (pv_sep) *pv_sep = 0; + if (!ph_sep || !pv_sep) + return ERROR_INT("&h_sep and &v_sep not both defined", procName, 1); + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + boxOverlapDistance(box1, box2, &h_ovl, &v_ovl); + if (h_ovl <= 0) + *ph_sep = -h_ovl + 1; + if (v_ovl <= 0) + *pv_sep = -v_ovl + 1; + return 0; +} + + +/*! + * \brief boxCompareSize() + * + * \param[in] box1, box2 + * \param[in] type L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, + * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, + * L_SORT_BY_AREA, + * \param[out] prel 1 if box1 > box2, 0 if the same, -1 if box1 < box2 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We're re-using the SORT enum for these comparisons.
+ * 
+ */ +l_ok +boxCompareSize(BOX *box1, + BOX *box2, + l_int32 type, + l_int32 *prel) +{ +l_int32 w1, h1, w2, h2, size1, size2, valid1, valid2; + + PROCNAME("boxCompareSize"); + + if (!prel) + return ERROR_INT("&rel not defined", procName, 1); + *prel = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + if (type != L_SORT_BY_WIDTH && type != L_SORT_BY_HEIGHT && + type != L_SORT_BY_MAX_DIMENSION && type != L_SORT_BY_PERIMETER && + type != L_SORT_BY_AREA) + return ERROR_INT("invalid compare type", procName, 1); + + boxGetGeometry(box1, NULL, NULL, &w1, &h1); + boxGetGeometry(box2, NULL, NULL, &w2, &h2); + if (type == L_SORT_BY_WIDTH) { + *prel = (w1 > w2) ? 1 : ((w1 == w2) ? 0 : -1); + } else if (type == L_SORT_BY_HEIGHT) { + *prel = (h1 > h2) ? 1 : ((h1 == h2) ? 0 : -1); + } else if (type == L_SORT_BY_MAX_DIMENSION) { + size1 = L_MAX(w1, h1); + size2 = L_MAX(w2, h2); + *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); + } else if (type == L_SORT_BY_PERIMETER) { + size1 = w1 + h1; + size2 = w2 + h2; + *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); + } else if (type == L_SORT_BY_AREA) { + size1 = w1 * h1; + size2 = w2 * h2; + *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); + } + return 0; +} + + +/*! + * \brief boxContainsPt() + * + * \param[in] box + * \param[in] x, y a point + * \param[out] pcontains 1 if box contains point; 0 otherwise + * \return 0 if OK, 1 on error. + */ +l_ok +boxContainsPt(BOX *box, + l_float32 x, + l_float32 y, + l_int32 *pcontains) +{ +l_int32 bx, by, bw, bh; + + PROCNAME("boxContainsPt"); + + if (!pcontains) + return ERROR_INT("&contains not defined", procName, 1); + *pcontains = 0; + if (!box) + return ERROR_INT("&box not defined", procName, 1); + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (x >= bx && x < bx + bw && y >= by && y < by + bh) + *pcontains = 1; + return 0; +} + + +/*! + * \brief boxaGetNearestToPt() + * + * \param[in] boxa + * \param[in] x, y point + * \return box with centroid closest to the given point [x,y], + * or NULL if no boxes in boxa + * + *
+ * Notes:
+ *      (1) Uses euclidean distance between centroid and point.
+ * 
+ */ +BOX * +boxaGetNearestToPt(BOXA *boxa, + l_int32 x, + l_int32 y) +{ +l_int32 i, n, minindex; +l_float32 delx, dely, dist, mindist, cx, cy; +BOX *box; + + PROCNAME("boxaGetNearestToPt"); + + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + if ((n = boxaGetCount(boxa)) == 0) + return (BOX *)ERROR_PTR("n = 0", procName, NULL); + + mindist = 1000000000.; + minindex = 0; + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) + continue; + boxGetCenter(box, &cx, &cy); + delx = (l_float32)(cx - x); + dely = (l_float32)(cy - y); + dist = delx * delx + dely * dely; + if (dist < mindist) { + minindex = i; + mindist = dist; + } + boxDestroy(&box); + } + + return boxaGetBox(boxa, minindex, L_COPY); +} + + +/*! + * \brief boxaGetNearestToLine() + * + * \param[in] boxa + * \param[in] x, y (y = -1 for vertical line; x = -1 for horiz line) + * \return box with centroid closest to the given line, + * or NULL if no boxes in boxa + * + *
+ * Notes:
+ *      (1) For a horizontal line at some value y, get the minimum of the
+ *          distance |yc - y| from the box centroid yc value to y;
+ *          likewise minimize |xc - x| for a vertical line at x.
+ *      (2) Input y < 0, x >= 0 to indicate a vertical line at x, and
+ *          x < 0, y >= 0 for a horizontal line at y.
+ * 
+ */ +BOX * +boxaGetNearestToLine(BOXA *boxa, + l_int32 x, + l_int32 y) +{ +l_int32 i, n, minindex; +l_float32 dist, mindist, cx, cy; +BOX *box; + + PROCNAME("boxaGetNearestToLine"); + + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + if ((n = boxaGetCount(boxa)) == 0) + return (BOX *)ERROR_PTR("n = 0", procName, NULL); + if (y >= 0 && x >= 0) + return (BOX *)ERROR_PTR("either x or y must be < 0", procName, NULL); + if (y < 0 && x < 0) + return (BOX *)ERROR_PTR("either x or y must be >= 0", procName, NULL); + + mindist = 1000000000.; + minindex = 0; + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) + continue; + boxGetCenter(box, &cx, &cy); + if (x >= 0) + dist = L_ABS(cx - (l_float32)x); + else /* y >= 0 */ + dist = L_ABS(cy - (l_float32)y); + if (dist < mindist) { + minindex = i; + mindist = dist; + } + boxDestroy(&box); + } + + return boxaGetBox(boxa, minindex, L_COPY); +} + + +/*! + * \brief boxaFindNearestBoxes() + * + * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order + * \param[in] dist_select L_NON_NEGATIVE, L_ALL + * \param[in] range search distance from box i; use 0 to search + * entire boxa (e.g., if it's not 2D sorted) + * \param[out] pnaaindex for each box in %boxa, contains a numa of 4 + * box indices (per direction) of the nearest box + * \param[out] pnaadist for each box in %boxa, this contains a numa + * \return 0 if OK, 1 on error + *
+ * Notes:
+ *      (1) See boxaGetNearestByDirection() for usage of %dist_select
+ *          and %range.
+ * 
+ */ +l_ok +boxaFindNearestBoxes(BOXA *boxa, + l_int32 dist_select, + l_int32 range, + NUMAA **pnaaindex, + NUMAA **pnaadist) +{ +l_int32 i, n, index, dist; +NUMA *nai, *nad; +NUMAA *naai, *naad; + + PROCNAME("boxaFindNearestBoxes"); + + if (pnaaindex) *pnaaindex = NULL; + if (pnaadist) *pnaadist = NULL; + if (!pnaaindex) + return ERROR_INT("&naaindex not defined", procName, 1); + if (!pnaadist) + return ERROR_INT("&naadist not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + naai = numaaCreate(n); + naad = numaaCreate(n); + *pnaaindex = naai; + *pnaadist = naad; + for (i = 0; i < n; i++) { + nai = numaCreate(4); + nad = numaCreate(4); + boxaGetNearestByDirection(boxa, i, L_FROM_LEFT, dist_select, + range, &index, &dist); + numaAddNumber(nai, index); + numaAddNumber(nad, dist); + boxaGetNearestByDirection(boxa, i, L_FROM_RIGHT, dist_select, + range, &index, &dist); + numaAddNumber(nai, index); + numaAddNumber(nad, dist); + boxaGetNearestByDirection(boxa, i, L_FROM_TOP, dist_select, + range, &index, &dist); + numaAddNumber(nai, index); + numaAddNumber(nad, dist); + boxaGetNearestByDirection(boxa, i, L_FROM_BOT, dist_select, + range, &index, &dist); + numaAddNumber(nai, index); + numaAddNumber(nad, dist); + numaaAddNuma(naai, nai, L_INSERT); + numaaAddNuma(naad, nad, L_INSERT); + } + return 0; +} + + +/*! + * \brief boxaGetNearestByDirection() + * + * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order + * \param[in] i box we test against + * \param[in] dir direction to look: L_FROM_LEFT, L_FROM_RIGHT, + * L_FROM_TOP, L_FROM_BOT + * \param[in] dist_select L_NON_NEGATIVE, L_ALL + * \param[in] range search distance from box i; use 0 to search + * entire boxa (e.g., if it's not 2D sorted) + * \param[out] pindex index in boxa of nearest box with overlapping + * coordinates in the indicated direction; + * -1 if there is no box + * \param[out] pdist distance of the nearest box in the indicated + * direction; 100000 if no box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For efficiency, use a LR/TD sorted %boxa, which can be
+ *          made by flattening a 2D sorted boxaa.  In that case,
+ *          %range can be some positive integer like 50.
+ *      (2) If boxes overlap, the distance will be < 0.  Use %dist_select
+ *          to determine if these should count or not.  If L_ALL, then
+ *          one box will match as the nearest to another in 2 or more
+ *          directions.
+ * 
+ */ +l_ok +boxaGetNearestByDirection(BOXA *boxa, + l_int32 i, + l_int32 dir, + l_int32 dist_select, + l_int32 range, + l_int32 *pindex, + l_int32 *pdist) +{ +l_int32 j, jmin, jmax, n, mindist, dist, index; +l_int32 x, y, w, h, bx, by, bw, bh; + + PROCNAME("boxaGetNearestByDirection"); + + if (pindex) *pindex = -1; + if (pdist) *pdist = 100000; + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + if (!pdist) + return ERROR_INT("&dist not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && + dir != L_FROM_TOP && dir != L_FROM_BOT) + return ERROR_INT("invalid dir", procName, 1); + if (dist_select != L_NON_NEGATIVE && dist_select != L_ALL) + return ERROR_INT("invalid dist_select", procName, 1); + n = boxaGetCount(boxa); + if (i < 0 || i >= n) + return ERROR_INT("invalid box index", procName, 1); + + jmin = (range <= 0) ? 0 : L_MAX(0, i - range); + jmax = (range <= 0) ? n - 1 : L_MIN(n -1, i + range); + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + mindist = 100000; + index = -1; + if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { + for (j = jmin; j <= jmax; j++) { + if (j == i) continue; + boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); + if ((bx >= x && dir == L_FROM_LEFT) || /* not to the left */ + (x >= bx && dir == L_FROM_RIGHT)) /* not to the right */ + continue; + if (boxHasOverlapInXorY(y, h, by, bh) == 1) { + dist = boxGetDistanceInXorY(x, w, bx, bw); + if (dist_select == L_NON_NEGATIVE && dist < 0) continue; + if (dist < mindist) { + mindist = dist; + index = j; + } + } + } + } else if (dir == L_FROM_TOP || dir == L_FROM_BOT) { + for (j = jmin; j <= jmax; j++) { + if (j == i) continue; + boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); + if ((by >= y && dir == L_FROM_TOP) || /* not above */ + (y >= by && dir == L_FROM_BOT)) /* not below */ + continue; + if (boxHasOverlapInXorY(x, w, bx, bw) == 1) { + dist = boxGetDistanceInXorY(y, h, by, bh); + if (dist_select == L_NON_NEGATIVE && dist < 0) continue; + if (dist < mindist) { + mindist = dist; + index = j; + } + } + } + } + *pindex = index; + *pdist = mindist; + return 0; +} + + +/*! + * \brief boxHasOverlapInXorY() + * + * \param[in] c1 left or top coordinate of box1 + * \param[in] s1 width or height of box1 + * \param[in] c2 left or top coordinate of box2 + * \param[in] s2 width or height of box2 + * \return 0 if no overlap; 1 if any overlap + * + *
+ * Notes:
+ *      (1) Like boxGetDistanceInXorY(), this is used for overlaps both in
+ *          x (which projected vertically) and in y (projected horizontally)
+ * 
+ */ +static l_int32 +boxHasOverlapInXorY(l_int32 c1, + l_int32 s1, + l_int32 c2, + l_int32 s2) +{ +l_int32 ovlp; + + if (c1 > c2) + ovlp = c2 + s2 - 1 - c1; + else + ovlp = c1 + s1 - 1 - c2; + return (ovlp < 0) ? 0 : 1; +} + + +/*! + * \brief boxGetDistanceInXorY() + * + * \param[in] c1 left or top coordinate of box1 + * \param[in] s1 width or height of box1 + * \param[in] c2 left or top coordinate of box2 + * \param[in] s2 width or height of box2 + * \return distance between them (if < 0, box2 overlaps box1 in the + * dimension considered) + */ +static l_int32 +boxGetDistanceInXorY(l_int32 c1, + l_int32 s1, + l_int32 c2, + l_int32 s2) +{ +l_int32 dist; + + if (c1 > c2) + dist = c1 - (c2 + s2 - 1); + else + dist = c2 - (c1 + s1 - 1); + return dist; +} + + +/*! + * \brief boxGetCenter() + * + * \param[in] box + * \param[out] pcx, pcy location of center of box + * \return 0 if OK, 1 on error or if box is not valid + */ +l_ok +boxGetCenter(BOX *box, + l_float32 *pcx, + l_float32 *pcy) +{ +l_int32 x, y, w, h; + + PROCNAME("boxGetCenter"); + + if (pcx) *pcx = 0; + if (pcy) *pcy = 0; + if (!pcx || !pcy) + return ERROR_INT("&cx, &cy not both defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + boxGetGeometry(box, &x, &y, &w, &h); + if (w == 0 || h == 0) return 1; + *pcx = (l_float32)(x + 0.5 * w); + *pcy = (l_float32)(y + 0.5 * h); + + return 0; +} + + +/*! + * \brief boxIntersectByLine() + * + * \param[in] box + * \param[in] x, y point that line goes through + * \param[in] slope of line + * \param[out] px1, py1 1st point of intersection with box + * \param[out] px2, py2 2nd point of intersection with box + * \param[out] pn number of points of intersection + * \return 0 if OK, 1 on error or if box is not valid + * + *
+ * Notes:
+ *      (1) If the intersection is at only one point (a corner), the
+ *          coordinates are returned in (x1, y1).
+ *      (2) Represent a vertical line by one with a large but finite slope.
+ * 
+ */ +l_ok +boxIntersectByLine(BOX *box, + l_int32 x, + l_int32 y, + l_float32 slope, + l_int32 *px1, + l_int32 *py1, + l_int32 *px2, + l_int32 *py2, + l_int32 *pn) +{ +l_int32 bx, by, bw, bh, xp, yp, xt, yt, i, n; +l_float32 invslope; +PTA *pta; + + PROCNAME("boxIntersectByLine"); + + if (px1) *px1 = 0; + if (px2) *px2 = 0; + if (py1) *py1 = 0; + if (py2) *py2 = 0; + if (pn) *pn = 0; + if (!px1 || !py1 || !px2 || !py2) + return ERROR_INT("&x1, &y1, &x2, &y2 not all defined", procName, 1); + if (!pn) + return ERROR_INT("&n not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (bw == 0 || bh == 0) return 1; + + if (slope == 0.0) { + if (y >= by && y < by + bh) { + *py1 = *py2 = y; + *px1 = bx; + *px2 = bx + bw - 1; + } + return 0; + } + + if (slope > 1000000.0) { + if (x >= bx && x < bx + bw) { + *px1 = *px2 = x; + *py1 = by; + *py2 = by + bh - 1; + } + return 0; + } + + /* Intersection with top and bottom lines of box */ + pta = ptaCreate(2); + invslope = 1.0 / slope; + xp = (l_int32)(x + invslope * (y - by)); + if (xp >= bx && xp < bx + bw) + ptaAddPt(pta, xp, by); + xp = (l_int32)(x + invslope * (y - by - bh + 1)); + if (xp >= bx && xp < bx + bw) + ptaAddPt(pta, xp, by + bh - 1); + + /* Intersection with left and right lines of box */ + yp = (l_int32)(y + slope * (x - bx)); + if (yp >= by && yp < by + bh) + ptaAddPt(pta, bx, yp); + yp = (l_int32)(y + slope * (x - bx - bw + 1)); + if (yp >= by && yp < by + bh) + ptaAddPt(pta, bx + bw - 1, yp); + + /* There is a maximum of 2 unique points; remove duplicates. */ + n = ptaGetCount(pta); + if (n > 0) { + ptaGetIPt(pta, 0, px1, py1); /* accept the first one */ + *pn = 1; + } + for (i = 1; i < n; i++) { + ptaGetIPt(pta, i, &xt, &yt); + if ((*px1 != xt) || (*py1 != yt)) { + *px2 = xt; + *py2 = yt; + *pn = 2; + break; + } + } + + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief boxClipToRectangle() + * + * \param[in] box + * \param[in] wi, hi rectangle representing image + * \return part of box within given rectangle, or NULL on error + * or if box is entirely outside the rectangle + * + *
+ * Notes:
+ *      (1) This can be used to clip a rectangle to an image.
+ *          The clipping rectangle is assumed to have a UL corner at (0, 0),
+ *          and a LR corner at (wi - 1, hi - 1).
+ * 
+ */ +BOX * +boxClipToRectangle(BOX *box, + l_int32 wi, + l_int32 hi) +{ +BOX *boxd; + + PROCNAME("boxClipToRectangle"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + if (box->x >= wi || box->y >= hi || + box->x + box->w <= 0 || box->y + box->h <= 0) + return (BOX *)ERROR_PTR("box outside rectangle", procName, NULL); + + boxd = boxCopy(box); + if (boxd->x < 0) { + boxd->w += boxd->x; + boxd->x = 0; + } + if (boxd->y < 0) { + boxd->h += boxd->y; + boxd->y = 0; + } + if (boxd->x + boxd->w > wi) + boxd->w = wi - boxd->x; + if (boxd->y + boxd->h > hi) + boxd->h = hi - boxd->y; + return boxd; +} + + +/*! + * \brief boxClipToRectangleParams() + * + * \param[in] box [optional] requested box; can be null + * \param[in] w, h clipping box size; typ. the size of an image + * \param[out] pxstart start x coordinate + * \param[out] pystart start y coordinate + * \param[out] pxend one pixel beyond clipping box + * \param[out] pyend one pixel beyond clipping box + * \param[out] pbw [optional] clipped width + * \param[out] pbh [optional] clipped height + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The return value should be checked.  If it is 1, the
+ *          returned parameter values are bogus.
+ *      (2) This simplifies the selection of pixel locations within
+ *          a given rectangle:
+ *             for (i = ystart; i < yend; i++ {
+ *                 ...
+ *                 for (j = xstart; j < xend; j++ {
+ *                     ....
+ * 
+ */ +l_ok +boxClipToRectangleParams(BOX *box, + l_int32 w, + l_int32 h, + l_int32 *pxstart, + l_int32 *pystart, + l_int32 *pxend, + l_int32 *pyend, + l_int32 *pbw, + l_int32 *pbh) +{ +l_int32 bw, bh; +BOX *boxc; + + PROCNAME("boxClipToRectangleParams"); + + if (pxstart) *pxstart = 0; + if (pystart) *pystart = 0; + if (pxend) *pxend = w; + if (pyend) *pyend = h; + if (pbw) *pbw = w; + if (pbh) *pbh = h; + if (!pxstart || !pystart || !pxend || !pyend) + return ERROR_INT("invalid ptr input", procName, 1); + if (!box) return 0; + + if ((boxc = boxClipToRectangle(box, w, h)) == NULL) + return ERROR_INT("box outside image", procName, 1); + boxGetGeometry(boxc, pxstart, pystart, &bw, &bh); + boxDestroy(&boxc); + + if (pbw) *pbw = bw; + if (pbh) *pbh = bh; + if (bw == 0 || bh == 0) + return ERROR_INT("invalid clipping box", procName, 1); + *pxend = *pxstart + bw; /* 1 past the end */ + *pyend = *pystart + bh; /* 1 past the end */ + return 0; +} + + +/*! + * \brief boxRelocateOneSide() + * + * \param[in] boxd [optional]; this can be null, equal to boxs, + * or different from boxs; + * \param[in] boxs starting box; to have one side relocated + * \param[in] loc new location of the side that is changing + * \param[in] sideflag L_FROM_LEFT, etc., indicating the side that moves + * \return boxd, or NULL on error or if the computed boxd has + * width or height <= 0. + * + *
+ * Notes:
+ *      (1) Set boxd == NULL to get new box; boxd == boxs for in-place;
+ *          or otherwise to resize existing boxd.
+ *      (2) For usage, suggest one of these:
+ *               boxd = boxRelocateOneSide(NULL, boxs, ...);   // new
+ *               boxRelocateOneSide(boxs, boxs, ...);          // in-place
+ *               boxRelocateOneSide(boxd, boxs, ...);          // other
+ * 
+ */ +BOX * +boxRelocateOneSide(BOX *boxd, + BOX *boxs, + l_int32 loc, + l_int32 sideflag) +{ +l_int32 x, y, w, h; + + PROCNAME("boxRelocateOneSide"); + + if (!boxs) + return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); + if (!boxd) + boxd = boxCopy(boxs); + + boxGetGeometry(boxs, &x, &y, &w, &h); + if (w == 0 || h == 0) + return boxd; + if (sideflag == L_FROM_LEFT) + boxSetGeometry(boxd, loc, -1, w + x - loc, -1); + else if (sideflag == L_FROM_RIGHT) + boxSetGeometry(boxd, -1, -1, loc - x + 1, -1); + else if (sideflag == L_FROM_TOP) + boxSetGeometry(boxd, -1, loc, -1, h + y - loc); + else if (sideflag == L_FROM_BOT) + boxSetGeometry(boxd, -1, -1, -1, loc - y + 1); + return boxd; +} + + +/*! + * \brief boxaAdjustSides() + * + * \param[in] boxas + * \param[in] delleft, delright, deltop, delbot changes in location of + * each side for each box + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) New box dimensions are cropped at left and top to x >= 0 and y >= 0.
+ *      (2) If the width or height of a box goes to 0, we generate a box with
+ *          w == 1 and h == 1, as a placeholder.
+ *      (3) See boxAdjustSides().
+ * 
+ */ +BOXA * +boxaAdjustSides(BOXA *boxas, + l_int32 delleft, + l_int32 delright, + l_int32 deltop, + l_int32 delbot) +{ +l_int32 n, i, x, y; +BOX *box1, *box2; +BOXA *boxad; + + PROCNAME("boxaAdjustSides"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + n = boxaGetCount(boxas); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + box1 = boxaGetBox(boxas, i, L_COPY); + box2 = boxAdjustSides(NULL, box1, delleft, delright, deltop, delbot); + if (!box2) { + boxGetGeometry(box1, &x, &y, NULL, NULL); + box2 = boxCreate(x, y, 1, 1); + } + boxaAddBox(boxad, box2, L_INSERT); + boxDestroy(&box1); + } + + return boxad; +} + + +/*! + * \brief boxaAdjustBoxSides() + * + * \param[in] boxas + * \param[in] index + * \param[in] delleft, delright, deltop, delbot changes to box side locs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation on a box in a boxa.
+ *      (2) New box dimensions are cropped at left and top to x >= 0 and y >= 0.
+ *      (3) If a box ends up with no area, an error message is emitted,
+ *          but the box dimensions are not changed.
+ *      (4) See boxaAdjustSides().
+ * 
+ */ +l_ok +boxaAdjustBoxSides(BOXA *boxa, + l_int32 index, + l_int32 delleft, + l_int32 delright, + l_int32 deltop, + l_int32 delbot) +{ +BOX *box; + + PROCNAME("boxaAdjustBoxSides"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) + return ERROR_INT("invalid index", procName, 1); + + boxAdjustSides(box, box, delleft, delright, deltop, delbot); + boxDestroy(&box); /* the clone */ + return 0; +} + + +/*! + * \brief boxAdjustSides() + * + * \param[in] boxd [optional]; this can be null, equal to boxs, + * or different from boxs + * \param[in] boxs starting box; to have sides adjusted + * \param[in] delleft, delright, deltop, delbot changes in location + * of each side + * \return boxd, or NULL on error or if the computed boxd has + * width or height <= 0. + * + *
+ * Notes:
+ *      (1) Set boxd == NULL to get new box; boxd == boxs for in-place;
+ *          or otherwise to resize existing boxd.
+ *      (2) For usage, suggest one of these:
+ *               boxd = boxAdjustSides(NULL, boxs, ...);   // new
+ *               boxAdjustSides(boxs, boxs, ...);          // in-place
+ *               boxAdjustSides(boxd, boxs, ...);          // other
+ *      (3) New box dimensions are cropped at left and top to x >= 0 and y >= 0.
+ *      (4) For example, to expand in-place by 20 pixels on each side, use
+ *             boxAdjustSides(box, box, -20, 20, -20, 20);
+ * 
+ */ +BOX * +boxAdjustSides(BOX *boxd, + BOX *boxs, + l_int32 delleft, + l_int32 delright, + l_int32 deltop, + l_int32 delbot) +{ +l_int32 x, y, w, h, xl, xr, yt, yb, wnew, hnew; + + PROCNAME("boxAdjustSides"); + + if (!boxs) + return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); + + boxGetGeometry(boxs, &x, &y, &w, &h); + xl = L_MAX(0, x + delleft); + yt = L_MAX(0, y + deltop); + xr = x + w + delright; /* one pixel beyond right edge */ + yb = y + h + delbot; /* one pixel below bottom edge */ + wnew = xr - xl; + hnew = yb - yt; + + if (wnew < 1 || hnew < 1) + return (BOX *)ERROR_PTR("boxd has 0 area", procName, NULL); + if (!boxd) + return boxCreate(xl, yt, wnew, hnew); + + boxSetGeometry(boxd, xl, yt, wnew, hnew); + return boxd; +} + + +/*! + * \brief boxaSetSide() + * + * \param[in] boxad use NULL to get a new one; same as boxas for in-place + * \param[in] boxas + * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT + * \param[in] val location to set for given side, for each box + * \param[in] thresh min abs difference to cause resetting to %val + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) Sets the given side of each box.  Use boxad == NULL for a new
+ *          boxa, and boxad == boxas for in-place.
+ *      (2) Use one of these:
+ *               boxad = boxaSetSide(NULL, boxas, ...);   // new
+ *               boxaSetSide(boxas, boxas, ...);  // in-place
+ * 
+ */ +BOXA * +boxaSetSide(BOXA *boxad, + BOXA *boxas, + l_int32 side, + l_int32 val, + l_int32 thresh) +{ +l_int32 n, i; +BOX *box; + + PROCNAME("boxaSetSide"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxad && (boxas != boxad)) + return (BOXA *)ERROR_PTR("not in-place", procName, NULL); + if (side != L_SET_LEFT && side != L_SET_RIGHT && + side != L_SET_TOP && side != L_SET_BOT) + return (BOXA *)ERROR_PTR("invalid side", procName, NULL); + if (val < 0) + return (BOXA *)ERROR_PTR("val < 0", procName, NULL); + + if (!boxad) + boxad = boxaCopy(boxas, L_COPY); + n = boxaGetCount(boxad); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxad, i, L_CLONE); + boxSetSide(box, side, val, thresh); + boxDestroy(&box); /* the clone */ + } + + return boxad; +} + + +/*! + * \brief boxSetSide() + * + * \param[in] boxs + * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT + * \param[in] val location to set for given side, for each box + * \param[in] thresh min abs difference to cause resetting to %val + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation.
+ *      (2) Use %thresh = 0 to definitely set the side to %val.
+ * 
+ */ +l_ok +boxSetSide(BOX *boxs, + l_int32 side, + l_int32 val, + l_int32 thresh) +{ +l_int32 x, y, w, h, diff; + + PROCNAME("boxSetSide"); + + if (!boxs) + return ERROR_INT("box not defined", procName, 1); + if (side != L_SET_LEFT && side != L_SET_RIGHT && + side != L_SET_TOP && side != L_SET_BOT) + return ERROR_INT("invalid side", procName, 1); + if (val < 0) + return ERROR_INT("val < 0", procName, 1); + + boxGetGeometry(boxs, &x, &y, &w, &h); + if (side == L_SET_LEFT) { + diff = x - val; + if (L_ABS(diff) >= thresh) + boxSetGeometry(boxs, val, y, w + diff, h); + } else if (side == L_SET_RIGHT) { + diff = x + w -1 - val; + if (L_ABS(diff) >= thresh) + boxSetGeometry(boxs, x, y, val - x + 1, h); + } else if (side == L_SET_TOP) { + diff = y - val; + if (L_ABS(diff) >= thresh) + boxSetGeometry(boxs, x, val, w, h + diff); + } else { /* side == L_SET_BOT */ + diff = y + h - 1 - val; + if (L_ABS(diff) >= thresh) + boxSetGeometry(boxs, x, y, w, val - y + 1); + } + + return 0; +} + + +/*! + * \brief boxaAdjustWidthToTarget() + * + * \param[in] boxad use NULL to get a new one; same as boxas for in-place + * \param[in] boxas + * \param[in] sides L_ADJUST_LEFT, L_ADJUST_RIGHT, L_ADJUST_LEFT_AND_RIGHT + * \param[in] target target width if differs by more than thresh + * \param[in] thresh min abs difference in width to cause adjustment + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) Conditionally adjusts the width of each box, by moving
+ *          the indicated edges (left and/or right) if the width differs
+ *          by %thresh or more from %target.
+ *      (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place.
+ *          Use one of these:
+ *               boxad = boxaAdjustWidthToTarget(NULL, boxas, ...);   // new
+ *               boxaAdjustWidthToTarget(boxas, boxas, ...);  // in-place
+ * 
+ */ +BOXA * +boxaAdjustWidthToTarget(BOXA *boxad, + BOXA *boxas, + l_int32 sides, + l_int32 target, + l_int32 thresh) +{ +l_int32 x, y, w, h, n, i, diff; +BOX *box; + + PROCNAME("boxaAdjustWidthToTarget"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxad && (boxas != boxad)) + return (BOXA *)ERROR_PTR("not in-place", procName, NULL); + if (sides != L_ADJUST_LEFT && sides != L_ADJUST_RIGHT && + sides != L_ADJUST_LEFT_AND_RIGHT) + return (BOXA *)ERROR_PTR("invalid sides", procName, NULL); + if (target < 1) + return (BOXA *)ERROR_PTR("target < 1", procName, NULL); + + if (!boxad) + boxad = boxaCopy(boxas, L_COPY); + n = boxaGetCount(boxad); + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) + continue; + boxGetGeometry(box, &x, &y, &w, &h); + diff = w - target; + if (sides == L_ADJUST_LEFT) { + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, L_MAX(0, x + diff), y, target, h); + } else if (sides == L_ADJUST_RIGHT) { + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, x, y, target, h); + } else { /* sides == L_ADJUST_LEFT_AND_RIGHT */ + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, L_MAX(0, x + diff/2), y, target, h); + } + boxDestroy(&box); + } + + return boxad; +} + + +/*! + * \brief boxaAdjustHeightToTarget() + * + * \param[in] boxad use NULL to get a new one + * \param[in] boxas + * \param[in] sides L_ADJUST_TOP, L_ADJUST_BOT, L_ADJUST_TOP_AND_BOT + * \param[in] target target height if differs by more than thresh + * \param[in] thresh min abs difference in height to cause adjustment + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) Conditionally adjusts the height of each box, by moving
+ *          the indicated edges (top and/or bot) if the height differs
+ *          by %thresh or more from %target.
+ *      (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place.
+ *          Use one of these:
+ *               boxad = boxaAdjustHeightToTarget(NULL, boxas, ...);   // new
+ *               boxaAdjustHeightToTarget(boxas, boxas, ...);  // in-place
+ * 
+ */ +BOXA * +boxaAdjustHeightToTarget(BOXA *boxad, + BOXA *boxas, + l_int32 sides, + l_int32 target, + l_int32 thresh) +{ +l_int32 x, y, w, h, n, i, diff; +BOX *box; + + PROCNAME("boxaAdjustHeightToTarget"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxad && (boxas != boxad)) + return (BOXA *)ERROR_PTR("not in-place", procName, NULL); + if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT && + sides != L_ADJUST_TOP_AND_BOT) + return (BOXA *)ERROR_PTR("invalid sides", procName, NULL); + if (target < 1) + return (BOXA *)ERROR_PTR("target < 1", procName, NULL); + + if (!boxad) + boxad = boxaCopy(boxas, L_COPY); + n = boxaGetCount(boxad); + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) + continue; + boxGetGeometry(box, &x, &y, &w, &h); + diff = h - target; + if (sides == L_ADJUST_TOP) { + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, x, L_MAX(0, y + diff), w, target); + } else if (sides == L_ADJUST_BOT) { + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, x, y, w, target); + } else { /* sides == L_ADJUST_TOP_AND_BOT */ + if (L_ABS(diff) >= thresh) + boxSetGeometry(box, x, L_MAX(0, y + diff/2), w, target); + } + boxDestroy(&box); + } + + return boxad; +} + + +/*! + * \brief boxEqual() + * + * \param[in] box1 + * \param[in] box2 + * \param[out] psame 1 if equal; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +boxEqual(BOX *box1, + BOX *box2, + l_int32 *psame) +{ + PROCNAME("boxEqual"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + if (box1->x == box2->x && box1->y == box2->y && + box1->w == box2->w && box1->h == box2->h) + *psame = 1; + return 0; +} + + +/*! + * \brief boxaEqual() + * + * \param[in] boxa1 + * \param[in] boxa2 + * \param[in] maxdist + * \param[out] pnaindex [optional] index array of correspondences + * \param[out] psame 1 if equal; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The two boxa are the "same" if they contain the same
+ *          boxes and each box is within %maxdist of its counterpart
+ *          in their positions within the boxa.  This allows for
+ *          small rearrangements.  Use 0 for maxdist if the boxa
+ *          must be identical.
+ *      (2) This applies only to geometry and ordering; refcounts
+ *          are not considered.
+ *      (3) %maxdist allows some latitude in the ordering of the boxes.
+ *          For the boxa to be the "same", corresponding boxes must
+ *          be within %maxdist of each other.  Note that for large
+ *          %maxdist, we should use a hash function for efficiency.
+ *      (4) naindex[i] gives the position of the box in boxa2 that
+ *          corresponds to box i in boxa1.  It is only returned if the
+ *          boxa are equal.
+ * 
+ */ +l_ok +boxaEqual(BOXA *boxa1, + BOXA *boxa2, + l_int32 maxdist, + NUMA **pnaindex, + l_int32 *psame) +{ +l_int32 i, j, n, jstart, jend, found, samebox; +l_int32 *countarray; +BOX *box1, *box2; +NUMA *na; + + PROCNAME("boxaEqual"); + + if (pnaindex) *pnaindex = NULL; + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!boxa1 || !boxa2) + return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); + n = boxaGetCount(boxa1); + if (n != boxaGetCount(boxa2)) + return 0; + + if ((countarray = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) + return ERROR_INT("calloc fail for countarray", procName, 1); + na = numaMakeConstant(0.0, n); + + for (i = 0; i < n; i++) { + box1 = boxaGetBox(boxa1, i, L_CLONE); + jstart = L_MAX(0, i - maxdist); + jend = L_MIN(n-1, i + maxdist); + found = FALSE; + for (j = jstart; j <= jend; j++) { + box2 = boxaGetBox(boxa2, j, L_CLONE); + boxEqual(box1, box2, &samebox); + if (samebox && countarray[j] == 0) { + countarray[j] = 1; + numaReplaceNumber(na, i, j); + found = TRUE; + boxDestroy(&box2); + break; + } + boxDestroy(&box2); + } + boxDestroy(&box1); + if (!found) { + numaDestroy(&na); + LEPT_FREE(countarray); + return 0; + } + } + + *psame = 1; + if (pnaindex) + *pnaindex = na; + else + numaDestroy(&na); + LEPT_FREE(countarray); + return 0; +} + + +/*! + * \brief boxSimilar() + * + * \param[in] box1 + * \param[in] box2 + * \param[in] leftdiff, rightdiff, topdiff, botdiff + * \param[out] psimilar 1 if similar; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The values of leftdiff (etc) are the maximum allowed deviations
+ *          between the locations of the left (etc) sides.  If any side
+ *          pairs differ by more than this amount, the boxes are not similar.
+ * 
+ */ +l_ok +boxSimilar(BOX *box1, + BOX *box2, + l_int32 leftdiff, + l_int32 rightdiff, + l_int32 topdiff, + l_int32 botdiff, + l_int32 *psimilar) +{ +l_int32 l1, l2, r1, r2, t1, t2, b1, b2, valid1, valid2; + + PROCNAME("boxSimilar"); + + if (!psimilar) + return ERROR_INT("&similar not defined", procName, 1); + *psimilar = 0; + if (!box1 || !box2) + return ERROR_INT("boxes not both defined", procName, 1); + boxIsValid(box1, &valid1); + boxIsValid(box2, &valid2); + if (!valid1 || !valid2) + return ERROR_INT("boxes not both valid", procName, 1); + + boxGetSideLocations(box1, &l1, &r1, &t1, &b1); + boxGetSideLocations(box2, &l2, &r2, &t2, &b2); + if (L_ABS(l1 - l2) > leftdiff) + return 0; + if (L_ABS(r1 - r2) > rightdiff) + return 0; + if (L_ABS(t1 - t2) > topdiff) + return 0; + if (L_ABS(b1 - b2) > botdiff) + return 0; + + *psimilar = 1; + return 0; +} + + +/*! + * \brief boxaSimilar() + * + * \param[in] boxa1 + * \param[in] boxa2 + * \param[in] leftdiff, rightdiff, topdiff, botdiff + * \param[in] debug output details of non-similar boxes + * \param[out] psimilar 1 if similar; 0 otherwise + * \param[out] pnasim [optional] na containing 1 if similar; else 0 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See boxSimilar() for parameter usage.
+ *      (2) Corresponding boxes are taken in order in the two boxa.
+ *      (3) %nasim is an indicator array with a (0/1) for each box pair.
+ *      (4) With %nasim or debug == 1, boxes continue to be tested
+ *          after failure.
+ * 
+ */ +l_ok +boxaSimilar(BOXA *boxa1, + BOXA *boxa2, + l_int32 leftdiff, + l_int32 rightdiff, + l_int32 topdiff, + l_int32 botdiff, + l_int32 debug, + l_int32 *psimilar, + NUMA **pnasim) +{ +l_int32 i, n1, n2, match, mismatch; +BOX *box1, *box2; + + PROCNAME("boxaSimilar"); + + if (psimilar) *psimilar = 0; + if (pnasim) *pnasim = NULL; + if (!boxa1 || !boxa2) + return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); + if (!psimilar) + return ERROR_INT("&similar not defined", procName, 1); + n1 = boxaGetCount(boxa1); + n2 = boxaGetCount(boxa2); + if (n1 != n2) { + L_ERROR("boxa counts differ: %d vs %d\n", procName, n1, n2); + return 1; + } + if (pnasim) *pnasim = numaCreate(n1); + + mismatch = FALSE; + for (i = 0; i < n1; i++) { + box1 = boxaGetBox(boxa1, i, L_CLONE); + box2 = boxaGetBox(boxa2, i, L_CLONE); + boxSimilar(box1, box2, leftdiff, rightdiff, topdiff, botdiff, + &match); + boxDestroy(&box1); + boxDestroy(&box2); + if (pnasim) + numaAddNumber(*pnasim, match); + if (!match) { + mismatch = TRUE; + if (!debug && pnasim == NULL) + return 0; + else if (debug) + L_INFO("box %d not similar\n", procName, i); + } + } + + if (!mismatch) *psimilar = 1; + return 0; +} + + +/*----------------------------------------------------------------------* + * Boxa combine and split * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaJoin() + * + * \param[in] boxad dest boxa; add to this one + * \param[in] boxas source boxa; add from this one + * \param[in] istart starting index in boxas + * \param[in] iend ending index in boxas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This appends a clone of each indicated box in boxas to boxad
+ *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (3) iend < 0 means 'read to the end'
+ *      (4) if boxas == NULL or has no boxes, this is a no-op.
+ * 
+ */ +l_ok +boxaJoin(BOXA *boxad, + BOXA *boxas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +BOX *box; + + PROCNAME("boxaJoin"); + + if (!boxad) + return ERROR_INT("boxad not defined", procName, 1); + if (!boxas || ((n = boxaGetCount(boxas)) == 0)) + return 0; + + if (istart < 0) + istart = 0; + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + box = boxaGetBox(boxas, i, L_CLONE); + boxaAddBox(boxad, box, L_INSERT); + } + + return 0; +} + + +/*! + * \brief boxaaJoin() + * + * \param[in] baad dest boxaa; add to this one + * \param[in] baas source boxaa; add from this one + * \param[in] istart starting index in baas + * \param[in] iend ending index in baas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This appends a clone of each indicated boxa in baas to baad
+ *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (3) iend < 0 means 'read to the end'
+ *      (4) if baas == NULL, this is a no-op.
+ * 
+ */ +l_ok +boxaaJoin(BOXAA *baad, + BOXAA *baas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +BOXA *boxa; + + PROCNAME("boxaaJoin"); + + if (!baad) + return ERROR_INT("baad not defined", procName, 1); + if (!baas) + return 0; + + if (istart < 0) + istart = 0; + n = boxaaGetCount(baas); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + boxa = boxaaGetBoxa(baas, i, L_CLONE); + boxaaAddBoxa(baad, boxa, L_INSERT); + } + + return 0; +} + + +/*! + * \brief boxaSplitEvenOdd() + * + * \param[in] boxa + * \param[in] fillflag 1 to put invalid boxes in place; 0 to omit + * \param[out] pboxae, pboxao save even and odd boxes in their separate + * boxa, setting the other type to invalid boxes. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %fillflag == 1, boxae has copies of the even boxes
+ *          in their original location, and nvalid boxes are placed
+ *          in the odd array locations.  And v.v.
+ *      (2) If %fillflag == 0, boxae has only copies of the even boxes.
+ * 
+ */ +l_ok +boxaSplitEvenOdd(BOXA *boxa, + l_int32 fillflag, + BOXA **pboxae, + BOXA **pboxao) +{ +l_int32 i, n; +BOX *box, *box1; + + PROCNAME("boxaSplitEvenOdd"); + + if (pboxae) *pboxae = NULL; + if (pboxao) *pboxao = NULL; + if (!pboxae || !pboxao) + return ERROR_INT("&boxae and &boxao not both defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + *pboxae = boxaCreate(n); + *pboxao = boxaCreate(n); + if (fillflag == 0) { + /* don't fill with invalid boxes; end up with half-size boxa */ + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_COPY); + if ((i & 1) == 0) + boxaAddBox(*pboxae, box, L_INSERT); + else + boxaAddBox(*pboxao, box, L_INSERT); + } + } else { + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_COPY); + box1 = boxCreate(0, 0, 0, 0); /* empty placeholder */ + if ((i & 1) == 0) { + boxaAddBox(*pboxae, box, L_INSERT); + boxaAddBox(*pboxao, box1, L_INSERT); + } else { + boxaAddBox(*pboxae, box1, L_INSERT); + boxaAddBox(*pboxao, box, L_INSERT); + } + } + } + return 0; +} + + +/*! + * \brief boxaMergeEvenOdd() + * + * \param[in] boxae boxes to go in even positions in merged boxa + * \param[in] boxao boxes to go in odd positions in merged boxa + * \param[in] fillflag 1 if there are invalid boxes in placeholders + * \return boxad merged, or NULL on error + * + *
+ * Notes:
+ *      (1) This is essentially the inverse of boxaSplitEvenOdd().
+ *          Typically, boxae and boxao were generated by boxaSplitEvenOdd(),
+ *          and the value of %fillflag needs to be the same in both calls.
+ *      (2) If %fillflag == 1, both boxae and boxao are of the same size;
+ *          otherwise boxae may have one more box than boxao.
+ * 
+ */ +BOXA * +boxaMergeEvenOdd(BOXA *boxae, + BOXA *boxao, + l_int32 fillflag) +{ +l_int32 i, n, ne, no; +BOX *box; +BOXA *boxad; + + PROCNAME("boxaMergeEvenOdd"); + + if (!boxae || !boxao) + return (BOXA *)ERROR_PTR("boxae and boxao not defined", procName, NULL); + ne = boxaGetCount(boxae); + no = boxaGetCount(boxao); + if (ne < no || ne > no + 1) + return (BOXA *)ERROR_PTR("boxa sizes invalid", procName, NULL); + + boxad = boxaCreate(ne); + if (fillflag == 0) { /* both are approx. half-sized; all valid boxes */ + n = ne + no; + for (i = 0; i < n; i++) { + if ((i & 1) == 0) + box = boxaGetBox(boxae, i / 2, L_COPY); + else + box = boxaGetBox(boxao, i / 2, L_COPY); + boxaAddBox(boxad, box, L_INSERT); + } + } else { /* both are full size and have invalid placeholders */ + for (i = 0; i < ne; i++) { + if ((i & 1) == 0) + box = boxaGetBox(boxae, i, L_COPY); + else + box = boxaGetBox(boxao, i, L_COPY); + boxaAddBox(boxad, box, L_INSERT); + } + } + return boxad; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxfunc2.c b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc2.c new file mode 100644 index 0000000..f51ad64 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc2.c @@ -0,0 +1,1928 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxfunc2.c + *
+ *
+ *      Boxa/Box transform (shift, scale) and orthogonal rotation
+ *           BOXA            *boxaTransform()
+ *           BOX             *boxTransform()
+ *           BOXA            *boxaTransformOrdered()
+ *           BOX             *boxTransformOrdered()
+ *           BOXA            *boxaRotateOrth()
+ *           BOX             *boxRotateOrth()
+ *           BOXA            *boxaShiftWithPta()
+ *
+ *      Boxa sort
+ *           BOXA            *boxaSort()
+ *           BOXA            *boxaBinSort()
+ *           BOXA            *boxaSortByIndex()
+ *           BOXAA           *boxaSort2d()
+ *           BOXAA           *boxaSort2dByIndex()
+ *
+ *      Boxa statistics
+ *           l_int32          boxaGetRankVals()
+ *           l_int32          boxaGetMedianVals()
+ *           l_int32          boxaGetAverageSize()
+ *
+ *      Boxa array extraction
+ *           l_int32          boxaExtractAsNuma()
+ *           l_int32          boxaExtractAsPta()
+ *           PTA             *boxaExtractCorners()
+ *
+ *      Other Boxaa functions
+ *           l_int32          boxaaGetExtent()
+ *           BOXA            *boxaaFlattenToBoxa()
+ *           BOXA            *boxaaFlattenAligned()
+ *           BOXAA           *boxaEncapsulateAligned()
+ *           BOXAA           *boxaaTranspose()
+ *           l_int32          boxaaAlignBox()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* For more than this number of c.c. in a binarized image of + * semi-perimeter (w + h) about 5000 or less, the O(n) binsort + * is faster than the O(nlogn) shellsort. */ +static const l_int32 MinCompsForBinSort = 200; + + +/*---------------------------------------------------------------------* + * Boxa/Box transform (shift, scale) and orthogonal rotation * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaTransform() + * + * \param[in] boxas + * \param[in] shiftx + * \param[in] shifty + * \param[in] scalex + * \param[in] scaley + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a very simple function that first shifts, then scales.
+ *      (2) The UL corner coordinates of all boxes in the output %boxad
+ *      (3) For the boxes in the output %boxad, the UL corner coordinates
+ *          must be non-negative, and the width and height of valid
+ *          boxes must be at least 1.
+ * 
+ */ +BOXA * +boxaTransform(BOXA *boxas, + l_int32 shiftx, + l_int32 shifty, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 i, n; +BOX *boxs, *boxd; +BOXA *boxad; + + PROCNAME("boxaTransform"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + n = boxaGetCount(boxas); + if ((boxad = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { + boxaDestroy(&boxad); + return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); + } + boxd = boxTransform(boxs, shiftx, shifty, scalex, scaley); + boxDestroy(&boxs); + boxaAddBox(boxad, boxd, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxTransform() + * + * \param[in] box + * \param[in] shiftx + * \param[in] shifty + * \param[in] scalex + * \param[in] scaley + * \return boxd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a very simple function that first shifts, then scales.
+ *      (2) If the box is invalid, a new invalid box is returned.
+ *      (3) The UL corner coordinates must be non-negative, and the
+ *          width and height of valid boxes must be at least 1.
+ * 
+ */ +BOX * +boxTransform(BOX *box, + l_int32 shiftx, + l_int32 shifty, + l_float32 scalex, + l_float32 scaley) +{ + PROCNAME("boxTransform"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + if (box->w <= 0 || box->h <= 0) + return boxCreate(0, 0, 0, 0); + else + return boxCreate((l_int32)(L_MAX(0, scalex * (box->x + shiftx) + 0.5)), + (l_int32)(L_MAX(0, scaley * (box->y + shifty) + 0.5)), + (l_int32)(L_MAX(1.0, scalex * box->w + 0.5)), + (l_int32)(L_MAX(1.0, scaley * box->h + 0.5))); +} + + +/*! + * \brief boxaTransformOrdered() + * + * \param[in] boxas + * \param[in] shiftx + * \param[in] shifty + * \param[in] scalex + * \param[in] scaley + * \param[in] xcen, ycen center of rotation + * \param[in] angle in radians; clockwise is positive + * \param[in] order one of 6 combinations: L_TR_SC_RO, ... + * \return boxd, or NULL on error + * + *
+ *          shift, scaling and rotation, and the order of the
+ *          transforms is specified.
+ *      (2) Although these operations appear to be on an infinite
+ *          2D plane, in practice the region of interest is clipped
+ *          to a finite image.  The center of rotation is usually taken
+ *          with respect to the image (either the UL corner or the
+ *          center).  A translation can have two very different effects:
+ *            (a) Moves the boxes across the fixed image region.
+ *            (b) Moves the image origin, causing a change in the image
+ *                region and an opposite effective translation of the boxes.
+ *          This function should only be used for (a), where the image
+ *          region is fixed on translation.  If the image region is
+ *          changed by the translation, use instead the functions
+ *          in affinecompose.c, where the image region and rotation
+ *          center can be computed from the actual clipping due to
+ *          translation of the image origin.
+ *      (3) See boxTransformOrdered() for usage and implementation details.
+ * 
+ */ +BOXA * +boxaTransformOrdered(BOXA *boxas, + l_int32 shiftx, + l_int32 shifty, + l_float32 scalex, + l_float32 scaley, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 order) +{ +l_int32 i, n; +BOX *boxs, *boxd; +BOXA *boxad; + + PROCNAME("boxaTransformOrdered"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + n = boxaGetCount(boxas); + if ((boxad = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { + boxaDestroy(&boxad); + return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); + } + boxd = boxTransformOrdered(boxs, shiftx, shifty, scalex, scaley, + xcen, ycen, angle, order); + boxDestroy(&boxs); + boxaAddBox(boxad, boxd, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxTransformOrdered() + * + * \param[in] boxs + * \param[in] shiftx + * \param[in] shifty + * \param[in] scalex + * \param[in] scaley + * \param[in] xcen, ycen center of rotation + * \param[in] angle in radians; clockwise is positive + * \param[in] order one of 6 combinations: L_TR_SC_RO, ... + * \return boxd, or NULL on error + * + *
+ * Notes:
+ *      (1) This allows a sequence of linear transforms, composed of
+ *          shift, scaling and rotation, where the order of the
+ *          transforms is specified.
+ *      (2) The rotation is taken about a point specified by (xcen, ycen).
+ *          Let the components of the vector from the center of rotation
+ *          to the box center be (xdif, ydif):
+ *            xdif = (bx + 0.5 * bw) - xcen
+ *            ydif = (by + 0.5 * bh) - ycen
+ *          Then the box center after rotation has new components:
+ *            bxcen = xcen + xdif * cosa + ydif * sina
+ *            bycen = ycen + ydif * cosa - xdif * sina
+ *          where cosa and sina are the cos and sin of the angle,
+ *          and the enclosing box for the rotated box has size:
+ *            rw = |bw * cosa| + |bh * sina|
+ *            rh = |bh * cosa| + |bw * sina|
+ *          where bw and bh are the unrotated width and height.
+ *          Then the box UL corner (rx, ry) is
+ *            rx = bxcen - 0.5 * rw
+ *            ry = bycen - 0.5 * rh
+ *      (3) The center of rotation specified by args %xcen and %ycen
+ *          is the point BEFORE any translation or scaling.  If the
+ *          rotation is not the first operation, this function finds
+ *          the actual center at the time of rotation.  It does this
+ *          by making the following assumptions:
+ *             (1) Any scaling is with respect to the UL corner, so
+ *                 that the center location scales accordingly.
+ *             (2) A translation does not affect the center of
+ *                 the image; it just moves the boxes.
+ *          We always use assumption (1).  However, assumption (2)
+ *          will be incorrect if the apparent translation is due
+ *          to a clipping operation that, in effect, moves the
+ *          origin of the image.  In that case, you should NOT use
+ *          these simple functions.  Instead, use the functions
+ *          in affinecompose.c, where the rotation center can be
+ *          computed from the actual clipping due to translation
+ *          of the image origin.
+ * 
+ */ +BOX * +boxTransformOrdered(BOX *boxs, + l_int32 shiftx, + l_int32 shifty, + l_float32 scalex, + l_float32 scaley, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 order) +{ +l_int32 bx, by, bw, bh, tx, ty, tw, th; +l_int32 xcent, ycent; /* transformed center of rotation due to scaling */ +l_float32 sina, cosa, xdif, ydif, rx, ry, rw, rh; +BOX *boxd; + + PROCNAME("boxTransformOrdered"); + + if (!boxs) + return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); + if (order != L_TR_SC_RO && order != L_SC_RO_TR && order != L_RO_TR_SC && + order != L_TR_RO_SC && order != L_RO_SC_TR && order != L_SC_TR_RO) + return (BOX *)ERROR_PTR("order invalid", procName, NULL); + + boxGetGeometry(boxs, &bx, &by, &bw, &bh); + if (bw <= 0 || bh <= 0) /* invalid */ + return boxCreate(0, 0, 0, 0); + if (angle != 0.0) { + sina = sin(angle); + cosa = cos(angle); + } + + if (order == L_TR_SC_RO) { + tx = (l_int32)(scalex * (bx + shiftx) + 0.5); + ty = (l_int32)(scaley * (by + shifty) + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); + xcent = (l_int32)(scalex * xcen + 0.5); + ycent = (l_int32)(scaley * ycen + 0.5); + if (angle == 0.0) { + boxd = boxCreate(tx, ty, tw, th); + } else { + xdif = tx + 0.5 * tw - xcent; + ydif = ty + 0.5 * th - ycent; + rw = L_ABS(tw * cosa) + L_ABS(th * sina); + rh = L_ABS(th * cosa) + L_ABS(tw * sina); + rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; + boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, + (l_int32)rh); + } + } else if (order == L_SC_TR_RO) { + tx = (l_int32)(scalex * bx + shiftx + 0.5); + ty = (l_int32)(scaley * by + shifty + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); + xcent = (l_int32)(scalex * xcen + 0.5); + ycent = (l_int32)(scaley * ycen + 0.5); + if (angle == 0.0) { + boxd = boxCreate(tx, ty, tw, th); + } else { + xdif = tx + 0.5 * tw - xcent; + ydif = ty + 0.5 * th - ycent; + rw = L_ABS(tw * cosa) + L_ABS(th * sina); + rh = L_ABS(th * cosa) + L_ABS(tw * sina); + rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; + boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, + (l_int32)rh); + } + } else if (order == L_RO_TR_SC) { + if (angle == 0.0) { + rx = bx; + ry = by; + rw = bw; + rh = bh; + } else { + xdif = bx + 0.5 * bw - xcen; + ydif = by + 0.5 * bh - ycen; + rw = L_ABS(bw * cosa) + L_ABS(bh * sina); + rh = L_ABS(bh * cosa) + L_ABS(bw * sina); + rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; + } + tx = (l_int32)(scalex * (rx + shiftx) + 0.5); + ty = (l_int32)(scaley * (ry + shifty) + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); + boxd = boxCreate(tx, ty, tw, th); + } else if (order == L_RO_SC_TR) { + if (angle == 0.0) { + rx = bx; + ry = by; + rw = bw; + rh = bh; + } else { + xdif = bx + 0.5 * bw - xcen; + ydif = by + 0.5 * bh - ycen; + rw = L_ABS(bw * cosa) + L_ABS(bh * sina); + rh = L_ABS(bh * cosa) + L_ABS(bw * sina); + rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; + } + tx = (l_int32)(scalex * rx + shiftx + 0.5); + ty = (l_int32)(scaley * ry + shifty + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); + boxd = boxCreate(tx, ty, tw, th); + } else if (order == L_TR_RO_SC) { + tx = bx + shiftx; + ty = by + shifty; + if (angle == 0.0) { + rx = tx; + ry = ty; + rw = bw; + rh = bh; + } else { + xdif = tx + 0.5 * bw - xcen; + ydif = ty + 0.5 * bh - ycen; + rw = L_ABS(bw * cosa) + L_ABS(bh * sina); + rh = L_ABS(bh * cosa) + L_ABS(bw * sina); + rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; + } + tx = (l_int32)(scalex * rx + 0.5); + ty = (l_int32)(scaley * ry + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); + boxd = boxCreate(tx, ty, tw, th); + } else { /* order == L_SC_RO_TR) */ + tx = (l_int32)(scalex * bx + 0.5); + ty = (l_int32)(scaley * by + 0.5); + tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); + th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); + xcent = (l_int32)(scalex * xcen + 0.5); + ycent = (l_int32)(scaley * ycen + 0.5); + if (angle == 0.0) { + rx = tx; + ry = ty; + rw = tw; + rh = th; + } else { + xdif = tx + 0.5 * tw - xcent; + ydif = ty + 0.5 * th - ycent; + rw = L_ABS(tw * cosa) + L_ABS(th * sina); + rh = L_ABS(th * cosa) + L_ABS(tw * sina); + rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; + ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; + } + tx = (l_int32)(rx + shiftx + 0.5); + ty = (l_int32)(ry + shifty + 0.5); + tw = (l_int32)(rw + 0.5); + th = (l_int32)(rh + 0.5); + boxd = boxCreate(tx, ty, tw, th); + } + + return boxd; +} + + +/*! + * \brief boxaRotateOrth() + * + * \param[in] boxas + * \param[in] w, h of image in which the boxa is embedded + * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; + * all rotations are clockwise + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) See boxRotateOrth() for details.
+ * 
+ */ +BOXA * +boxaRotateOrth(BOXA *boxas, + l_int32 w, + l_int32 h, + l_int32 rotation) +{ +l_int32 i, n; +BOX *boxs, *boxd; +BOXA *boxad; + + PROCNAME("boxaRotateOrth"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (rotation < 0 || rotation > 3) + return (BOXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); + if (rotation == 0) + return boxaCopy(boxas, L_COPY); + + n = boxaGetCount(boxas); + if ((boxad = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { + boxaDestroy(&boxad); + return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); + } + boxd = boxRotateOrth(boxs, w, h, rotation); + boxDestroy(&boxs); + boxaAddBox(boxad, boxd, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxRotateOrth() + * + * \param[in] box + * \param[in] w, h of image in which the box is embedded + * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; + * all rotations are clockwise + * \return boxd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotate the image with the embedded box by the specified amount.
+ *      (2) After rotation, the rotated box is always measured with
+ *          respect to the UL corner of the image.
+ * 
+ */ +BOX * +boxRotateOrth(BOX *box, + l_int32 w, + l_int32 h, + l_int32 rotation) +{ +l_int32 bx, by, bw, bh, xdist, ydist; + + PROCNAME("boxRotateOrth"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + if (rotation < 0 || rotation > 3) + return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); + if (rotation == 0) + return boxCopy(box); + + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (bw <= 0 || bh <= 0) /* invalid */ + return boxCreate(0, 0, 0, 0); + ydist = h - by - bh; /* below box */ + xdist = w - bx - bw; /* to right of box */ + if (rotation == 1) /* 90 deg cw */ + return boxCreate(ydist, bx, bh, bw); + else if (rotation == 2) /* 180 deg cw */ + return boxCreate(xdist, ydist, bw, bh); + else /* rotation == 3, 270 deg cw */ + return boxCreate(by, xdist, bh, bw); +} + + +/*! + * \brief boxaShiftWithPta() + * + * \param[in] boxas + * \param[in] pta aligned with the boxes; determines shift amount + * \param[in] dir +1 to shift by the values in pta; -1 to shift + * by the negative of the values in the pta. + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) In use, %pta may come from the UL corners of of a boxa, each
+ *          of whose boxes contains the corresponding box of %boxas
+ *          within it.  The output %boxad is then a boxa in the (global)
+ *          coordinates of the containing boxa.  So the input %pta
+ *          could come from boxaExtractCorners().
+ *      (2) The operations with %dir == 1 and %dir == -1 are inverses if
+ *          called in order (1, -1).  Starting with an input boxa and
+ *          calling twice with these values of %dir results in a boxa
+ *          identical to the input.  However, because box parameters can
+ *          never be negative, calling in the order (-1, 1) may result
+ *          in clipping at the left side and the top.
+ * 
+ */ +BOXA * +boxaShiftWithPta(BOXA *boxas, + PTA *pta, + l_int32 dir) +{ +l_int32 i, n, x, y, full; +BOX *box1, *box2; +BOXA *boxad; + + PROCNAME("boxaShiftWithPta"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + boxaIsFull(boxas, &full); + if (!full) + return (BOXA *)ERROR_PTR("boxas not full", procName, NULL); + if (!pta) + return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); + if (dir != 1 && dir != -1) + return (BOXA *)ERROR_PTR("invalid dir", procName, NULL); + n = boxaGetCount(boxas); + if (n != ptaGetCount(pta)) + return (BOXA *)ERROR_PTR("boxas and pta not same size", procName, NULL); + + if ((boxad = boxaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); + for (i = 0; i < n; i++) { + box1 = boxaGetBox(boxas, i, L_COPY); + ptaGetIPt(pta, i, &x, &y); + box2 = boxTransform(box1, dir * x, dir * y, 1.0, 1.0); + boxaAddBox(boxad, box2, L_INSERT); + boxDestroy(&box1); + } + return boxad; +} + + +/*---------------------------------------------------------------------* + * Boxa sort * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaSort() + * + * \param[in] boxas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, + * L_SORT_BY_RIGHT, L_SORT_BY_BOT, + * L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, + * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, + * L_SORT_BY_PERIMETER, L_SORT_BY_AREA, + * L_SORT_BY_ASPECT_RATIO + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex [optional] index of sorted order into + * original array + * \return boxad sorted version of boxas, or NULL on error + * + *
+ * Notes:
+ *      (1) An empty boxa returns a copy, with a warning.
+ * 
+ */ +BOXA * +boxaSort(BOXA *boxas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex) +{ +l_int32 i, n, x, y, w, h, size; +BOXA *boxad; +NUMA *na, *naindex; + + PROCNAME("boxaSort"); + + if (pnaindex) *pnaindex = NULL; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((n = boxaGetCount(boxas)) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && + sorttype != L_SORT_BY_RIGHT && sorttype != L_SORT_BY_BOT && + sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && + sorttype != L_SORT_BY_MIN_DIMENSION && + sorttype != L_SORT_BY_MAX_DIMENSION && + sorttype != L_SORT_BY_PERIMETER && + sorttype != L_SORT_BY_AREA && + sorttype != L_SORT_BY_ASPECT_RATIO) + return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); + + /* Use O(n) binsort if possible */ + if (n > MinCompsForBinSort && + ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || + (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || + (sorttype == L_SORT_BY_PERIMETER))) + return boxaBinSort(boxas, sorttype, sortorder, pnaindex); + + /* Build up numa of specific data */ + if ((na = numaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("na not made", procName, NULL); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); + switch (sorttype) + { + case L_SORT_BY_X: + numaAddNumber(na, x); + break; + case L_SORT_BY_Y: + numaAddNumber(na, y); + break; + case L_SORT_BY_RIGHT: + numaAddNumber(na, x + w - 1); + break; + case L_SORT_BY_BOT: + numaAddNumber(na, y + h - 1); + break; + case L_SORT_BY_WIDTH: + numaAddNumber(na, w); + break; + case L_SORT_BY_HEIGHT: + numaAddNumber(na, h); + break; + case L_SORT_BY_MIN_DIMENSION: + size = L_MIN(w, h); + numaAddNumber(na, size); + break; + case L_SORT_BY_MAX_DIMENSION: + size = L_MAX(w, h); + numaAddNumber(na, size); + break; + case L_SORT_BY_PERIMETER: + size = w + h; + numaAddNumber(na, size); + break; + case L_SORT_BY_AREA: + size = w * h; + numaAddNumber(na, size); + break; + case L_SORT_BY_ASPECT_RATIO: + numaAddNumber(na, (l_float32)w / (l_float32)h); + break; + default: + L_WARNING("invalid sort type\n", procName); + } + } + + /* Get the sort index for data array */ + naindex = numaGetSortIndex(na, sortorder); + numaDestroy(&na); + if (!naindex) + return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); + + /* Build up sorted boxa using sort index */ + boxad = boxaSortByIndex(boxas, naindex); + + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + return boxad; +} + + +/*! + * \brief boxaBinSort() + * + * \param[in] boxas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, + * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex [optional] index of sorted order into + * original array + * \return boxad sorted version of boxas, or NULL on error + * + *
+ * Notes:
+ *      (1) For a large number of boxes (say, greater than 1000), this
+ *          O(n) binsort is much faster than the O(nlogn) shellsort.
+ *          For 5000 components, this is over 20x faster than boxaSort().
+ *      (2) Consequently, boxaSort() calls this function if it will
+ *          likely go much faster.
+ * 
+ */ +BOXA * +boxaBinSort(BOXA *boxas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex) +{ +l_int32 i, n, x, y, w, h; +BOXA *boxad; +NUMA *na, *naindex; + + PROCNAME("boxaBinSort"); + + if (pnaindex) *pnaindex = NULL; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((n = boxaGetCount(boxas)) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && + sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && + sorttype != L_SORT_BY_PERIMETER) + return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); + + /* Generate Numa of appropriate box dimensions */ + if ((na = numaCreate(n)) == NULL) + return (BOXA *)ERROR_PTR("na not made", procName, NULL); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); + switch (sorttype) + { + case L_SORT_BY_X: + numaAddNumber(na, x); + break; + case L_SORT_BY_Y: + numaAddNumber(na, y); + break; + case L_SORT_BY_WIDTH: + numaAddNumber(na, w); + break; + case L_SORT_BY_HEIGHT: + numaAddNumber(na, h); + break; + case L_SORT_BY_PERIMETER: + numaAddNumber(na, w + h); + break; + default: + L_WARNING("invalid sort type\n", procName); + } + } + + /* Get the sort index for data array */ + naindex = numaGetBinSortIndex(na, sortorder); + numaDestroy(&na); + if (!naindex) + return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); + + /* Build up sorted boxa using the sort index */ + boxad = boxaSortByIndex(boxas, naindex); + + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + return boxad; +} + + +/*! + * \brief boxaSortByIndex() + * + * \param[in] boxas + * \param[in] naindex na that maps from the new boxa to the input boxa + * \return boxad sorted, or NULL on error + */ +BOXA * +boxaSortByIndex(BOXA *boxas, + NUMA *naindex) +{ +l_int32 i, n, index; +BOX *box; +BOXA *boxad; + + PROCNAME("boxaSortByIndex"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((n = boxaGetCount(boxas)) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (!naindex) + return (BOXA *)ERROR_PTR("naindex not defined", procName, NULL); + + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + box = boxaGetBox(boxas, index, L_COPY); + boxaAddBox(boxad, box, L_INSERT); + } + + return boxad; +} + + +/*! + * \brief boxaSort2d() + * + * \param[in] boxas + * \param[out] pnaad [optional] numaa with sorted indices + * whose values are the indices of the input array + * \param[in] delta1 min separation that permits aggregation of a box + * onto a boxa of horizontally-aligned boxes; pass 1 + * \param[in] delta2 min separation that permits aggregation of a box + * onto a boxa of horizontally-aligned boxes; pass 2 + * \param[in] minh1 components less than this height either join an + * existing boxa or are set aside for pass 2 + * \return baa 2d sorted version of boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) The final result is a sort where the 'fast scan' direction is
+ *          left to right, and the 'slow scan' direction is from top
+ *          to bottom.  Each boxa in the baa represents a sorted set
+ *          of boxes from left to right.
+ *      (2) Three passes are used to aggregate the boxas, which can correspond
+ *          to characters or words in a line of text.  In pass 1, only
+ *          taller components, which correspond to xheight or larger,
+ *          are permitted to start a new boxa.  In pass 2, the remaining
+ *          vertically-challenged components are allowed to join an
+ *          existing boxa or start a new one.  In pass 3, boxa whose extent
+ *          is overlapping are joined.  After that, the boxes in each
+ *          boxa are sorted horizontally, and finally the boxa are
+ *          sorted vertically.
+ *      (3) If %delta1 > 0, the first pass allows aggregation when
+ *          boxes in the same boxa do not overlap vertically.  In fact,
+ *          %delta1 is the max distance by which they can miss and still
+ *          be aggregated.  If %delta1 < 0, the box must have vertical
+ *          overlap of at least abs(%delta1) with the boxa before it
+ *          can be merged.  Similar for delta2 on the second pass.
+ *      (4) On the first pass, any component of height less than minh1
+ *          cannot start a new boxa; it's put aside for later insertion.
+ *      (5) On the second pass, any small component that doesn't align
+ *          with an existing boxa can start a new one.
+ *      (6) This can be used to identify lines of text from
+ *          character or word bounding boxes.
+ *      (7) Typical values for the input parameters on 300 ppi text are:
+ *                 delta1 ~ 0
+ *                 delta2 ~ 0
+ *                 minh1 ~ 5
+ * 
+ */ +BOXAA * +boxaSort2d(BOXA *boxas, + NUMAA **pnaad, + l_int32 delta1, + l_int32 delta2, + l_int32 minh1) +{ +l_int32 i, index, h, nt, ne, n, m, ival; +BOX *box; +BOXA *boxa, *boxae, *boxan, *boxa1, *boxa2, *boxa3, *boxav, *boxavs; +BOXAA *baa, *baa1, *baad; +NUMA *naindex, *nae, *nan, *nah, *nav, *na1, *na2, *nad, *namap; +NUMAA *naa, *naa1, *naad; + + PROCNAME("boxaSort2d"); + + if (pnaad) *pnaad = NULL; + if (!boxas) + return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxaGetCount(boxas) == 0) + return (BOXAA *)ERROR_PTR("boxas is empty", procName, NULL); + + /* Sort from left to right */ + if ((boxa = boxaSort(boxas, L_SORT_BY_X, L_SORT_INCREASING, &naindex)) + == NULL) + return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); + + /* First pass: assign taller boxes to boxa by row */ + nt = boxaGetCount(boxa); + baa = boxaaCreate(0); + naa = numaaCreate(0); + boxae = boxaCreate(0); /* save small height boxes here */ + nae = numaCreate(0); /* keep track of small height boxes */ + for (i = 0; i < nt; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, NULL, NULL, NULL, &h); + if (h < minh1) { /* save for 2nd pass */ + boxaAddBox(boxae, box, L_INSERT); + numaAddNumber(nae, i); + } else { + n = boxaaGetCount(baa); + boxaaAlignBox(baa, box, delta1, &index); + if (index < n) { /* append to an existing boxa */ + boxaaAddBox(baa, index, box, L_INSERT); + } else { /* doesn't align, need new boxa */ + boxan = boxaCreate(0); + boxaAddBox(boxan, box, L_INSERT); + boxaaAddBoxa(baa, boxan, L_INSERT); + nan = numaCreate(0); + numaaAddNuma(naa, nan, L_INSERT); + } + numaGetIValue(naindex, i, &ival); + numaaAddNumber(naa, index, ival); + } + } + boxaDestroy(&boxa); + numaDestroy(&naindex); + + /* Second pass: feed in small height boxes */ + ne = boxaGetCount(boxae); + for (i = 0; i < ne; i++) { + box = boxaGetBox(boxae, i, L_CLONE); + n = boxaaGetCount(baa); + boxaaAlignBox(baa, box, delta2, &index); + if (index < n) { /* append to an existing boxa */ + boxaaAddBox(baa, index, box, L_INSERT); + } else { /* doesn't align, need new boxa */ + boxan = boxaCreate(0); + boxaAddBox(boxan, box, L_INSERT); + boxaaAddBoxa(baa, boxan, L_INSERT); + nan = numaCreate(0); + numaaAddNuma(naa, nan, L_INSERT); + } + numaGetIValue(nae, i, &ival); /* location in original boxas */ + numaaAddNumber(naa, index, ival); + } + + /* Third pass: merge some boxa whose extent is overlapping. + * Think of these boxa as text lines, where the bounding boxes + * of the text lines can overlap, but likely won't have + * a huge overlap. + * First do a greedy find of pairs of overlapping boxa, where + * the two boxa overlap by at least 50% of the smaller, and + * the smaller is not more than half the area of the larger. + * For such pairs, call the larger one the primary boxa. The + * boxes in the smaller one are appended to those in the primary + * in pass 3a, and the primaries are extracted in pass 3b. + * In this way, all boxes in the original baa are saved. */ + n = boxaaGetCount(baa); + boxaaGetExtent(baa, NULL, NULL, NULL, &boxa3); + boxa1 = boxaHandleOverlaps(boxa3, L_REMOVE_SMALL, 1000, 0.5, 0.5, &namap); + boxaDestroy(&boxa1); + boxaDestroy(&boxa3); + for (i = 0; i < n; i++) { /* Pass 3a: join selected copies of boxa */ + numaGetIValue(namap, i, &ival); + if (ival >= 0) { /* join current to primary boxa[ival] */ + boxa1 = boxaaGetBoxa(baa, i, L_COPY); + boxa2 = boxaaGetBoxa(baa, ival, L_CLONE); + boxaJoin(boxa2, boxa1, 0, -1); + boxaDestroy(&boxa2); + boxaDestroy(&boxa1); + na1 = numaaGetNuma(naa, i, L_COPY); + na2 = numaaGetNuma(naa, ival, L_CLONE); + numaJoin(na2, na1, 0, -1); + numaDestroy(&na1); + numaDestroy(&na2); + } + } + baa1 = boxaaCreate(n); + naa1 = numaaCreate(n); + for (i = 0; i < n; i++) { /* Pass 3b: save primary boxa */ + numaGetIValue(namap, i, &ival); + if (ival == -1) { + boxa1 = boxaaGetBoxa(baa, i, L_CLONE); + boxaaAddBoxa(baa1, boxa1, L_INSERT); + na1 = numaaGetNuma(naa, i, L_CLONE); + numaaAddNuma(naa1, na1, L_INSERT); + } + } + numaDestroy(&namap); + boxaaDestroy(&baa); + baa = baa1; + numaaDestroy(&naa); + naa = naa1; + + /* Sort the boxes in each boxa horizontally */ + m = boxaaGetCount(baa); + for (i = 0; i < m; i++) { + boxa1 = boxaaGetBoxa(baa, i, L_CLONE); + boxa2 = boxaSort(boxa1, L_SORT_BY_X, L_SORT_INCREASING, &nah); + boxaaReplaceBoxa(baa, i, boxa2); + na1 = numaaGetNuma(naa, i, L_CLONE); + na2 = numaSortByIndex(na1, nah); + numaaReplaceNuma(naa, i, na2); + boxaDestroy(&boxa1); + numaDestroy(&na1); + numaDestroy(&nah); + } + + /* Sort the boxa vertically within boxaa, using the first box + * in each boxa. */ + m = boxaaGetCount(baa); + boxav = boxaCreate(m); /* holds first box in each boxa in baa */ + naad = numaaCreate(m); + if (pnaad) + *pnaad = naad; + baad = boxaaCreate(m); + for (i = 0; i < m; i++) { + boxa1 = boxaaGetBoxa(baa, i, L_CLONE); + box = boxaGetBox(boxa1, 0, L_CLONE); + boxaAddBox(boxav, box, L_INSERT); + boxaDestroy(&boxa1); + } + boxavs = boxaSort(boxav, L_SORT_BY_Y, L_SORT_INCREASING, &nav); + for (i = 0; i < m; i++) { + numaGetIValue(nav, i, &index); + boxa = boxaaGetBoxa(baa, index, L_CLONE); + boxaaAddBoxa(baad, boxa, L_INSERT); + nad = numaaGetNuma(naa, index, L_CLONE); + numaaAddNuma(naad, nad, L_INSERT); + } + + +/* fprintf(stderr, "box count = %d, numaa count = %d\n", nt, + numaaGetNumberCount(naad)); */ + + boxaaDestroy(&baa); + boxaDestroy(&boxav); + boxaDestroy(&boxavs); + boxaDestroy(&boxae); + numaDestroy(&nav); + numaDestroy(&nae); + numaaDestroy(&naa); + if (!pnaad) + numaaDestroy(&naad); + + return baad; +} + + +/*! + * \brief boxaSort2dByIndex() + * + * \param[in] boxas + * \param[in] naa numaa that maps from the new baa to the input boxa + * \return baa sorted boxaa, or NULL on error + */ +BOXAA * +boxaSort2dByIndex(BOXA *boxas, + NUMAA *naa) +{ +l_int32 ntot, boxtot, i, j, n, nn, index; +BOX *box; +BOXA *boxa; +BOXAA *baa; +NUMA *na; + + PROCNAME("boxaSort2dByIndex"); + + if (!boxas) + return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((boxtot = boxaGetCount(boxas)) == 0) + return (BOXAA *)ERROR_PTR("boxas is empty", procName, NULL); + if (!naa) + return (BOXAA *)ERROR_PTR("naindex not defined", procName, NULL); + + /* Check counts */ + ntot = numaaGetNumberCount(naa); + if (ntot != boxtot) + return (BOXAA *)ERROR_PTR("element count mismatch", procName, NULL); + + n = numaaGetCount(naa); + baa = boxaaCreate(n); + for (i = 0; i < n; i++) { + na = numaaGetNuma(naa, i, L_CLONE); + nn = numaGetCount(na); + boxa = boxaCreate(nn); + for (j = 0; j < nn; j++) { + numaGetIValue(na, i, &index); + box = boxaGetBox(boxas, index, L_COPY); + boxaAddBox(boxa, box, L_INSERT); + } + boxaaAddBoxa(baa, boxa, L_INSERT); + numaDestroy(&na); + } + + return baa; +} + + +/*---------------------------------------------------------------------* + * Boxa array extraction * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaExtractAsNuma() + * + * \param[in] boxa + * \param[out] pnal [optional] array of left locations + * \param[out] pnat [optional] array of top locations + * \param[out] pnar [optional] array of right locations + * \param[out] pnab [optional] array of bottom locations + * \param[out] pnaw [optional] array of widths + * \param[out] pnah [optional] array of heights + * \param[in] keepinvalid 1 to keep invalid boxes; 0 to remove them + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If you are counting or sorting values, such as determining
+ *          rank order, you must remove invalid boxes.
+ *      (2) If you are parametrizing the values, or doing an evaluation
+ *          where the position in the boxa sequence is important, you
+ *          must replace the invalid boxes with valid ones before
+ *          doing the extraction. This is easily done with boxaFillSequence().
+ * 
+ */ +l_ok +boxaExtractAsNuma(BOXA *boxa, + NUMA **pnal, + NUMA **pnat, + NUMA **pnar, + NUMA **pnab, + NUMA **pnaw, + NUMA **pnah, + l_int32 keepinvalid) +{ +l_int32 i, n, left, top, right, bot, w, h; + + PROCNAME("boxaExtractAsNuma"); + + if (!pnal && !pnat && !pnar && !pnab && !pnaw && !pnah) + return ERROR_INT("no output requested", procName, 1); + if (pnal) *pnal = NULL; + if (pnat) *pnat = NULL; + if (pnar) *pnar = NULL; + if (pnab) *pnab = NULL; + if (pnaw) *pnaw = NULL; + if (pnah) *pnah = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!keepinvalid && boxaGetValidCount(boxa) == 0) + return ERROR_INT("no valid boxes", procName, 1); + + n = boxaGetCount(boxa); + if (pnal) *pnal = numaCreate(n); + if (pnat) *pnat = numaCreate(n); + if (pnar) *pnar = numaCreate(n); + if (pnab) *pnab = numaCreate(n); + if (pnaw) *pnaw = numaCreate(n); + if (pnah) *pnah = numaCreate(n); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); + if (!keepinvalid && (w <= 0 || h <= 0)) + continue; + right = left + w - 1; + bot = top + h - 1; + if (pnal) numaAddNumber(*pnal, left); + if (pnat) numaAddNumber(*pnat, top); + if (pnar) numaAddNumber(*pnar, right); + if (pnab) numaAddNumber(*pnab, bot); + if (pnaw) numaAddNumber(*pnaw, w); + if (pnah) numaAddNumber(*pnah, h); + } + + return 0; +} + + +/*! + * \brief boxaExtractAsPta() + * + * \param[in] boxa + * \param[out] pptal [optional] array of left locations vs. index + * \param[out] pptat [optional] array of top locations vs. index + * \param[out] pptar [optional] array of right locations vs. index + * \param[out] pptab [optional] array of bottom locations vs. index + * \param[out] pptaw [optional] array of widths vs. index + * \param[out] pptah [optional] array of heights vs. index + * \param[in] keepinvalid 1 to keep invalid boxes; 0 to remove them + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For most applications, such as counting, sorting, fitting
+ *          to some parametrized form, plotting or filtering in general,
+ *          you should remove the invalid boxes.  Each pta saves the
+ *          box index in the x array, so replacing invalid boxes by
+ *          filling with boxaFillSequence(), which is required for
+ *          boxaExtractAsNuma(), is not necessary.
+ *      (2) If invalid boxes are retained, each one will result in
+ *          entries (typically 0) in all selected output pta.
+ *      (3) Other boxa --> pta functions are:
+ *          * boxaExtractCorners(): extracts any of the four corners as a pta.
+ *          * boxaConvertToPta(): extracts sufficient number of corners
+ *            to allow reconstruction of the original boxa from the pta.
+ * 
+ */ +l_ok +boxaExtractAsPta(BOXA *boxa, + PTA **pptal, + PTA **pptat, + PTA **pptar, + PTA **pptab, + PTA **pptaw, + PTA **pptah, + l_int32 keepinvalid) +{ +l_int32 i, n, left, top, right, bot, w, h; + + PROCNAME("boxaExtractAsPta"); + + if (!pptal && !pptar && !pptat && !pptab && !pptaw && !pptah) + return ERROR_INT("no output requested", procName, 1); + if (pptal) *pptal = NULL; + if (pptat) *pptat = NULL; + if (pptar) *pptar = NULL; + if (pptab) *pptab = NULL; + if (pptaw) *pptaw = NULL; + if (pptah) *pptah = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!keepinvalid && boxaGetValidCount(boxa) == 0) + return ERROR_INT("no valid boxes", procName, 1); + + n = boxaGetCount(boxa); + if (pptal) *pptal = ptaCreate(n); + if (pptat) *pptat = ptaCreate(n); + if (pptar) *pptar = ptaCreate(n); + if (pptab) *pptab = ptaCreate(n); + if (pptaw) *pptaw = ptaCreate(n); + if (pptah) *pptah = ptaCreate(n); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); + if (!keepinvalid && (w <= 0 || h <= 0)) + continue; + right = left + w - 1; + bot = top + h - 1; + if (pptal) ptaAddPt(*pptal, i, left); + if (pptat) ptaAddPt(*pptat, i, top); + if (pptar) ptaAddPt(*pptar, i, right); + if (pptab) ptaAddPt(*pptab, i, bot); + if (pptaw) ptaAddPt(*pptaw, i, w); + if (pptah) ptaAddPt(*pptah, i, h); + } + + return 0; +} + + +/*! + * \brief boxaExtractCorners() + * + * \param[in] boxa + * \param[in] corner L_UPPER_LEFT, L_UPPER_RIGHT, L_LOWER_LEFT, + * L_LOWER_RIGHT + * \return pta of corner coordinates, or NULL on error + * + *
+ * Notes:
+ *      (1) Extracts (0,0) for invalid boxes.
+ *      (2) Other boxa --> pta functions are:
+ *          * boxaExtractAsPta(): allows extraction of any dimension
+ *            and/or side location, with each in a separate pta.
+ *          * boxaConvertToPta(): extracts sufficient number of corners
+ *            to allow reconstruction of the original boxa from the pta.
+ * 
+ */ +PTA * +boxaExtractCorners(BOXA *boxa, + l_int32 corner) +{ +l_int32 i, n, left, top, right, bot, w, h; +PTA *pta; + + PROCNAME("boxaExtractCorners"); + + if (!boxa) + return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); + if (corner != L_UPPER_LEFT && corner != L_UPPER_RIGHT && + corner != L_LOWER_LEFT && corner != L_LOWER_RIGHT) + return (PTA *)ERROR_PTR("invalid corner", procName, NULL); + + n = boxaGetCount(boxa); + if ((pta = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); + right = left + w - 1; + bot = top + h - 1; + if (w == 0 || h == 0) { /* invalid */ + left = 0; + top = 0; + right = 0; + bot = 0; + } + if (corner == L_UPPER_LEFT) + ptaAddPt(pta, left, top); + else if (corner == L_UPPER_RIGHT) + ptaAddPt(pta, right, top); + else if (corner == L_LOWER_LEFT) + ptaAddPt(pta, left, bot); + else if (corner == L_LOWER_RIGHT) + ptaAddPt(pta, right, bot); + } + + return pta; +} + + +/*---------------------------------------------------------------------* + * Boxa statistics * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaGetRankVals() + * + * \param[in] boxa + * \param[in] fract use 0.0 for smallest, 1.0 for largest width and height + * \param[out] px [optional] rank value of x (left side) + * \param[out] py [optional] rank value of y (top side) + * \param[out] pr [optional] rank value of right side + * \param[out] pb [optional] rank value of bottom side + * \param[out] pw [optional] rank value of width + * \param[out] ph [optional] rank value of height + * \return 0 if OK, 1 on error or if the boxa is empty or has no valid boxes + * + *
+ * Notes:
+ *      (1) This function does not assume that all boxes in the boxa are valid
+ *      (2) The six box parameters are sorted independently.
+ *          For rank order, the width and height are sorted in increasing
+ *          order.  But what does it mean to sort x and y in "rank order"?
+ *          If the boxes are of comparable size and somewhat
+ *          aligned (e.g., from multiple images), it makes some sense
+ *          to give a "rank order" for x and y by sorting them in
+ *          decreasing order.  (By the same argument, we choose to sort
+ *          the r and b sides in increasing order.)  In general, the
+ *          interpretation of a rank order on x and y (or on r and b)
+ *          is highly application dependent.  In summary:
+ *             ~ x and y are sorted in decreasing order
+ *             ~ r and b are sorted in increasing order
+ *             ~ w and h are sorted in increasing order
+ * 
+ */ +l_ok +boxaGetRankVals(BOXA *boxa, + l_float32 fract, + l_int32 *px, + l_int32 *py, + l_int32 *pr, + l_int32 *pb, + l_int32 *pw, + l_int32 *ph) +{ +l_float32 xval, yval, rval, bval, wval, hval; +NUMA *nax, *nay, *nar, *nab, *naw, *nah; + + PROCNAME("boxaGetRankVals"); + + if (px) *px = 0; + if (py) *py = 0; + if (pr) *pr = 0; + if (pb) *pb = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (fract < 0.0 || fract > 1.0) + return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); + if (boxaGetValidCount(boxa) == 0) + return ERROR_INT("no valid boxes in boxa", procName, 1); + + /* Use only the valid boxes */ + boxaExtractAsNuma(boxa, &nax, &nay, &nar, &nab, &naw, &nah, 0); + + if (px) { + numaGetRankValue(nax, 1.0 - fract, NULL, 1, &xval); + *px = (l_int32)xval; + } + if (py) { + numaGetRankValue(nay, 1.0 - fract, NULL, 1, &yval); + *py = (l_int32)yval; + } + if (pr) { + numaGetRankValue(nar, fract, NULL, 1, &rval); + *pr = (l_int32)rval; + } + if (pb) { + numaGetRankValue(nab, fract, NULL, 1, &bval); + *pb = (l_int32)bval; + } + if (pw) { + numaGetRankValue(naw, fract, NULL, 1, &wval); + *pw = (l_int32)wval; + } + if (ph) { + numaGetRankValue(nah, fract, NULL, 1, &hval); + *ph = (l_int32)hval; + } + numaDestroy(&nax); + numaDestroy(&nay); + numaDestroy(&nar); + numaDestroy(&nab); + numaDestroy(&naw); + numaDestroy(&nah); + return 0; +} + + +/*! + * \brief boxaGetMedianVals() + * + * \param[in] boxa + * \param[out] px [optional] median value of x (left side) + * \param[out] py [optional] median value of y (top side) + * \param[out] pr [optional] median value of right side + * \param[out] pb [optional] median value of bottom side + * \param[out] pw [optional] median value of width + * \param[out] ph [optional] median value of height + * \return 0 if OK, 1 on error or if the boxa is empty or has no valid boxes + * + *
+ * Notes:
+ *      (1) See boxaGetRankVals()
+ * 
+ */ +l_ok +boxaGetMedianVals(BOXA *boxa, + l_int32 *px, + l_int32 *py, + l_int32 *pr, + l_int32 *pb, + l_int32 *pw, + l_int32 *ph) +{ + PROCNAME("boxaGetMedianVals"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (boxaGetValidCount(boxa) == 0) + return ERROR_INT("no valid boxes in boxa", procName, 1); + + return boxaGetRankVals(boxa, 0.5, px, py, pr, pb, pw, ph); +} + + +/*! + * \brief boxaGetAverageSize() + * + * \param[in] boxa + * \param[out] pw [optional] average width + * \param[out] ph [optional] average height + * \return 0 if OK, 1 on error or if the boxa is empty + */ +l_ok +boxaGetAverageSize(BOXA *boxa, + l_float32 *pw, + l_float32 *ph) +{ +l_int32 i, n, bw, bh; +l_float32 sumw, sumh; + + PROCNAME("boxaGetAverageSize"); + + if (pw) *pw = 0.0; + if (ph) *ph = 0.0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if ((n = boxaGetCount(boxa)) == 0) + return ERROR_INT("boxa is empty", procName, 1); + + sumw = sumh = 0.0; + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &bw, &bh); + sumw += bw; + sumh += bh; + } + + if (pw) *pw = sumw / n; + if (ph) *ph = sumh / n; + return 0; +} + + +/*---------------------------------------------------------------------* + * Other Boxaa functions * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaaGetExtent() + * + * \param[in] baa + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbox [optional] minimum box containing all boxa + * in boxaa + * \param[out] pboxa [optional] boxa containing all boxes in each + * boxa in the boxaa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned w and h are the minimum size image
+ *          that would contain all boxes untranslated.
+ *      (2) Each box in the returned boxa is the minimum box required to
+ *          hold all the boxes in the respective boxa of baa.
+ *      (3) If there are no valid boxes in a boxa, the box corresponding
+ *          to its extent has all fields set to 0 (an invalid box).
+ * 
+ */ +l_ok +boxaaGetExtent(BOXAA *baa, + l_int32 *pw, + l_int32 *ph, + BOX **pbox, + BOXA **pboxa) +{ +l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found; +BOX *box1; +BOXA *boxa, *boxa1; + + PROCNAME("boxaaGetExtent"); + + if (!pw && !ph && !pbox && !pboxa) + return ERROR_INT("no ptrs defined", procName, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbox) *pbox = NULL; + if (pboxa) *pboxa = NULL; + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + n = boxaaGetCount(baa); + if (n == 0) + return ERROR_INT("no boxa in baa", procName, 1); + + boxa = boxaCreate(n); + xmax = ymax = 0; + xmin = ymin = 100000000; + found = FALSE; + for (i = 0; i < n; i++) { + boxa1 = boxaaGetBoxa(baa, i, L_CLONE); + boxaGetExtent(boxa1, NULL, NULL, &box1); + boxaDestroy(&boxa1); + boxGetGeometry(box1, &x, &y, &w, &h); + if (w > 0 && h > 0) { /* a valid extent box */ + found = TRUE; /* found at least one valid extent box */ + xmin = L_MIN(xmin, x); + ymin = L_MIN(ymin, y); + xmax = L_MAX(xmax, x + w); + ymax = L_MAX(ymax, y + h); + } + boxaAddBox(boxa, box1, L_INSERT); + } + if (found == FALSE) /* no valid extent boxes */ + xmin = ymin = 0; + + if (pw) *pw = xmax; + if (ph) *ph = ymax; + if (pbox) + *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); + if (pboxa) + *pboxa = boxa; + else + boxaDestroy(&boxa); + return 0; +} + + +/*! + * \brief boxaaFlattenToBoxa() + * + * \param[in] baa + * \param[out] pnaindex [optional] the boxa index in the baa + * \param[in] copyflag L_COPY or L_CLONE + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the baa to a boxa, taking the boxes in
+ *          order in the first boxa, then the second, etc.
+ *      (2) If a boxa is empty, we generate an invalid, placeholder box
+ *          of zero size.  This is useful when converting from a baa
+ *          where each boxa has either 0 or 1 boxes, and it is necessary
+ *          to maintain a 1:1 correspondence between the initial
+ *          boxa array and the resulting box array.
+ *      (3) If &naindex is defined, we generate a Numa that gives, for
+ *          each box in the baa, the index of the boxa to which it belongs.
+ * 
+ */ +BOXA * +boxaaFlattenToBoxa(BOXAA *baa, + NUMA **pnaindex, + l_int32 copyflag) +{ +l_int32 i, j, m, n; +BOXA *boxa, *boxat; +BOX *box; +NUMA *naindex; + + PROCNAME("boxaaFlattenToBoxa"); + + if (pnaindex) *pnaindex = NULL; + if (!baa) + return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); + if (pnaindex) { + naindex = numaCreate(0); + *pnaindex = naindex; + } + + n = boxaaGetCount(baa); + boxa = boxaCreate(n); + for (i = 0; i < n; i++) { + boxat = boxaaGetBoxa(baa, i, L_CLONE); + m = boxaGetCount(boxat); + if (m == 0) { /* placeholder box */ + box = boxCreate(0, 0, 0, 0); + boxaAddBox(boxa, box, L_INSERT); + if (pnaindex) + numaAddNumber(naindex, i); /* save 'row' number */ + } else { + for (j = 0; j < m; j++) { + box = boxaGetBox(boxat, j, copyflag); + boxaAddBox(boxa, box, L_INSERT); + if (pnaindex) + numaAddNumber(naindex, i); /* save 'row' number */ + } + } + boxaDestroy(&boxat); + } + + return boxa; +} + + +/*! + * \brief boxaaFlattenAligned() + * + * \param[in] baa + * \param[in] num number extracted from each + * \param[in] fillerbox [optional] that fills if necessary + * \param[in] copyflag L_COPY or L_CLONE + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the baa to a boxa, taking the first %num
+ *          boxes from each boxa.
+ *      (2) In each boxa, if there are less than %num boxes, we preserve
+ *          the alignment between the input baa and the output boxa
+ *          by inserting one or more fillerbox(es) or, if %fillerbox == NULL,
+ *          one or more invalid placeholder boxes.
+ * 
+ */ +BOXA * +boxaaFlattenAligned(BOXAA *baa, + l_int32 num, + BOX *fillerbox, + l_int32 copyflag) +{ +l_int32 i, j, m, n, mval, nshort; +BOXA *boxat, *boxad; +BOX *box; + + PROCNAME("boxaaFlattenAligned"); + + if (!baa) + return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + n = boxaaGetCount(baa); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + boxat = boxaaGetBoxa(baa, i, L_CLONE); + m = boxaGetCount(boxat); + mval = L_MIN(m, num); + nshort = num - mval; + for (j = 0; j < mval; j++) { /* take the first %num if possible */ + box = boxaGetBox(boxat, j, copyflag); + boxaAddBox(boxad, box, L_INSERT); + } + for (j = 0; j < nshort; j++) { /* add fillers if necessary */ + if (fillerbox) { + boxaAddBox(boxad, fillerbox, L_COPY); + } else { + box = boxCreate(0, 0, 0, 0); /* invalid placeholder box */ + boxaAddBox(boxad, box, L_INSERT); + } + } + boxaDestroy(&boxat); + } + + return boxad; +} + + +/*! + * \brief boxaEncapsulateAligned() + * + * \param[in] boxa + * \param[in] num number put into each boxa in the baa + * \param[in] copyflag L_COPY or L_CLONE + * \return baa, or NULL on error + * + *
+ * Notes:
+ *      (1) This puts %num boxes from the input %boxa into each of a
+ *          set of boxa within an output baa.
+ *      (2) This assumes that the boxes in %boxa are in sets of %num each.
+ * 
+ */ +BOXAA * +boxaEncapsulateAligned(BOXA *boxa, + l_int32 num, + l_int32 copyflag) +{ +l_int32 i, j, n, nbaa, index; +BOX *box; +BOXA *boxat; +BOXAA *baa; + + PROCNAME("boxaEncapsulateAligned"); + + if (!boxa) + return (BOXAA *)ERROR_PTR("boxa not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); + + n = boxaGetCount(boxa); + nbaa = n / num; + if (num * nbaa != n) + L_ERROR("inconsistent alignment: num doesn't divide n\n", procName); + baa = boxaaCreate(nbaa); + for (i = 0, index = 0; i < nbaa; i++) { + boxat = boxaCreate(num); + for (j = 0; j < num; j++, index++) { + box = boxaGetBox(boxa, index, copyflag); + boxaAddBox(boxat, box, L_INSERT); + } + boxaaAddBoxa(baa, boxat, L_INSERT); + } + + return baa; +} + + +/*! + * \brief boxaaTranspose() + * + * \param[in] baas + * \return baad, or NULL on error + * + *
+ * Notes:
+ *      (1) If you think of a boxaa as a 2D array of boxes that is accessed
+ *          row major, then each row is represented by one of the boxa.
+ *          This function creates a new boxaa related to the input boxaa
+ *          as a column major traversal of the input boxaa.
+ *      (2) For example, if %baas has 2 boxa, each with 10 boxes, then
+ *          %baad will have 10 boxa, each with 2 boxes.
+ *      (3) Require for this transpose operation that each boxa in
+ *          %baas has the same number of boxes.  This operation is useful
+ *          when the i-th boxes in each boxa are meaningfully related.
+ * 
+ */ +BOXAA * +boxaaTranspose(BOXAA *baas) +{ +l_int32 i, j, ny, nb, nbox; +BOX *box; +BOXA *boxa; +BOXAA *baad; + + PROCNAME("boxaaTranspose"); + + if (!baas) + return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); + if ((ny = boxaaGetCount(baas)) == 0) + return (BOXAA *)ERROR_PTR("baas empty", procName, NULL); + + /* Make sure that each boxa in baas has the same number of boxes */ + for (i = 0; i < ny; i++) { + if ((boxa = boxaaGetBoxa(baas, i, L_CLONE)) == NULL) + return (BOXAA *)ERROR_PTR("baas is missing a boxa", procName, NULL); + nb = boxaGetCount(boxa); + boxaDestroy(&boxa); + if (i == 0) + nbox = nb; + else if (nb != nbox) + return (BOXAA *)ERROR_PTR("boxa are not all the same size", + procName, NULL); + } + + /* baad[i][j] = baas[j][i] */ + baad = boxaaCreate(nbox); + for (i = 0; i < nbox; i++) { + boxa = boxaCreate(ny); + for (j = 0; j < ny; j++) { + box = boxaaGetBox(baas, j, i, L_COPY); + boxaAddBox(boxa, box, L_INSERT); + } + boxaaAddBoxa(baad, boxa, L_INSERT); + } + return baad; +} + + +/*! + * \brief boxaaAlignBox() + * + * \param[in] baa + * \param[in] box to be aligned with bext boxa in the baa, if possible + * \param[in] delta amount by which consecutive components can miss + * in overlap and still be included in the array + * \param[out] pindex index of boxa with best overlap, or if none match, + * this is the index of the next boxa to be generated + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is not greedy.  It finds the boxa whose vertical
+ *          extent has the closest overlap with the input box.
+ * 
+ */ +l_ok +boxaaAlignBox(BOXAA *baa, + BOX *box, + l_int32 delta, + l_int32 *pindex) +{ +l_int32 i, n, m, y, yt, h, ht, ovlp, maxovlp, maxindex; +BOX *boxt; +BOXA *boxa; + + PROCNAME("boxaaAlignBox"); + + if (pindex) *pindex = 0; + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + + n = boxaaGetCount(baa); + boxGetGeometry(box, NULL, &y, NULL, &h); + maxovlp = -10000000; + for (i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + if ((m = boxaGetCount(boxa)) == 0) { + boxaDestroy(&boxa); + L_WARNING("no boxes in boxa\n", procName); + continue; + } + boxaGetExtent(boxa, NULL, NULL, &boxt); + boxGetGeometry(boxt, NULL, &yt, NULL, &ht); + boxDestroy(&boxt); + boxaDestroy(&boxa); + + /* Overlap < 0 means the components do not overlap vertically */ + if (yt >= y) + ovlp = y + h - 1 - yt; + else + ovlp = yt + ht - 1 - y; + if (ovlp > maxovlp) { + maxovlp = ovlp; + maxindex = i; + } + } + + if (maxovlp + delta >= 0) + *pindex = maxindex; + else + *pindex = n; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxfunc3.c b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc3.c new file mode 100644 index 0000000..692aa48 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc3.c @@ -0,0 +1,1626 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxfunc3.c + *
+ *
+ *      Boxa/Boxaa painting into pix
+ *           PIX             *pixMaskConnComp()
+ *           PIX             *pixMaskBoxa()
+ *           PIX             *pixPaintBoxa()
+ *           PIX             *pixSetBlackOrWhiteBoxa()
+ *           PIX             *pixPaintBoxaRandom()
+ *           PIX             *pixBlendBoxaRandom()
+ *           PIX             *pixDrawBoxa()
+ *           PIX             *pixDrawBoxaRandom()
+ *           PIX             *boxaaDisplay()
+ *           PIXA            *pixaDisplayBoxaa()
+ *
+ *      Split mask components into Boxa
+ *           BOXA            *pixSplitIntoBoxa()
+ *           BOXA            *pixSplitComponentIntoBoxa()
+ *           static l_int32   pixSearchForRectangle()
+ *
+ *      Represent horizontal or vertical mosaic strips
+ *           BOXA            *makeMosaicStrips()
+ *
+ *      Comparison between boxa
+ *           l_int32          boxaCompareRegions()
+ *
+ *      Reliable selection of a single large box
+ *           BOX             *pixSelectLargeULComp()
+ *           BOX             *boxaSelectLargeULBox()
+ *
+ *  See summary in pixPaintBoxa() of various ways to paint and draw
+ *  boxes on images.
+ * 
+ */ + +#include "allheaders.h" + +static l_int32 pixSearchForRectangle(PIX *pixs, BOX *boxs, l_int32 minsum, + l_int32 skipdist, l_int32 delta, + l_int32 maxbg, l_int32 sideflag, + BOXA *boxat, NUMA *nascore); + +#ifndef NO_CONSOLE_IO +#define DEBUG_SPLIT 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * Boxa/Boxaa painting into Pix * + *---------------------------------------------------------------------*/ +/*! + * \brief pixMaskConnComp() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \param[out] pboxa [optional] bounding boxes of c.c. + * \return pixd 1 bpp mask over the c.c., or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a mask image with ON pixels over the
+ *          b.b. of the c.c. in pixs.  If there are no ON pixels in pixs,
+ *          pixd will also have no ON pixels.
+ * 
+ */ +PIX * +pixMaskConnComp(PIX *pixs, + l_int32 connectivity, + BOXA **pboxa) +{ +BOXA *boxa; +PIX *pixd; + + PROCNAME("pixMaskConnComp"); + + if (pboxa) *pboxa = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + boxa = pixConnComp(pixs, NULL, connectivity); + pixd = pixCreateTemplate(pixs); + if (boxaGetCount(boxa) != 0) + pixMaskBoxa(pixd, pixd, boxa, L_SET_PIXELS); + if (pboxa) + *pboxa = boxa; + else + boxaDestroy(&boxa); + return pixd; +} + + +/*! + * \brief pixMaskBoxa() + * + * \param[in] pixd [optional] may be NULL + * \param[in] pixs any depth; not cmapped + * \param[in] boxa of boxes, to paint + * \param[in] op L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return pixd with masking op over the boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) This can be used with:
+ *              pixd = NULL  (makes a new pixd)
+ *              pixd = pixs  (in-place)
+ *      (2) If pixd == NULL, this first makes a copy of pixs, and then
+ *          bit-twiddles over the boxes.  Otherwise, it operates directly
+ *          on pixs.
+ *      (3) This simple function is typically used with 1 bpp images.
+ *          It uses the 1-image rasterop function, rasteropUniLow(),
+ *          to set, clear or flip the pixels in pixd.
+ *      (4) If you want to generate a 1 bpp mask of ON pixels from the boxes
+ *          in a Boxa, in a pix of size (w,h):
+ *              pix = pixCreate(w, h, 1);
+ *              pixMaskBoxa(pix, pix, boxa, L_SET_PIXELS);
+ * 
+ */ +PIX * +pixMaskBoxa(PIX *pixd, + PIX *pixs, + BOXA *boxa, + l_int32 op) +{ +l_int32 i, n, x, y, w, h; +BOX *box; + + PROCNAME("pixMaskBoxa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("if pixd, must be in-place", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return (PIX *)ERROR_PTR("invalid op", procName, NULL); + + pixd = pixCopy(pixd, pixs); + if ((n = boxaGetCount(boxa)) == 0) { + L_WARNING("no boxes to mask\n", procName); + return pixd; + } + + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + if (op == L_SET_PIXELS) + pixRasterop(pixd, x, y, w, h, PIX_SET, NULL, 0, 0); + else if (op == L_CLEAR_PIXELS) + pixRasterop(pixd, x, y, w, h, PIX_CLR, NULL, 0, 0); + else /* op == L_FLIP_PIXELS */ + pixRasterop(pixd, x, y, w, h, PIX_NOT(PIX_DST), NULL, 0, 0); + boxDestroy(&box); + } + + return pixd; +} + + +/*! + * \brief pixPaintBoxa() + * + * \param[in] pixs any depth, can be cmapped + * \param[in] boxa of boxes, to paint + * \param[in] val rgba color to paint + * \return pixd with painted boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp
+ *          and the boxa is painted using a colormap; otherwise,
+ *          it is converted to 32 bpp rgb.
+ *      (2) There are several ways to display a box on an image:
+ *            * Paint it as a solid color
+ *            * Draw the outline
+ *            * Blend the outline or region with the existing image
+ *          We provide painting and drawing here; blending is in blend.c.
+ *          When painting or drawing, the result can be either a
+ *          cmapped image or an rgb image.  The dest will be cmapped
+ *          if the src is either 1 bpp or has a cmap that is not full.
+ *          To force RGB output, use pixConvertTo8(pixs, FALSE)
+ *          before calling any of these paint and draw functions.
+ * 
+ */ +PIX * +pixPaintBoxa(PIX *pixs, + BOXA *boxa, + l_uint32 val) +{ +l_int32 i, n, d, rval, gval, bval, newindex; +l_int32 mapvacancy; /* true only if cmap and not full */ +BOX *box; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixPaintBoxa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + + if ((n = boxaGetCount(boxa)) == 0) { + L_WARNING("no boxes to paint; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + mapvacancy = FALSE; + if ((cmap = pixGetColormap(pixs)) != NULL) { + if (pixcmapGetCount(cmap) < 256) + mapvacancy = TRUE; + } + if (pixGetDepth(pixs) == 1 || mapvacancy) + pixd = pixConvertTo8(pixs, TRUE); + else + pixd = pixConvertTo32(pixs); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + d = pixGetDepth(pixd); + if (d == 8) { /* colormapped */ + cmap = pixGetColormap(pixd); + extractRGBValues(val, &rval, &gval, &bval); + if (pixcmapAddNewColor(cmap, rval, gval, bval, &newindex)) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("cmap full; can't add", procName, NULL); + } + } + + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + if (d == 8) + pixSetInRectArbitrary(pixd, box, newindex); + else + pixSetInRectArbitrary(pixd, box, val); + boxDestroy(&box); + } + + return pixd; +} + + +/*! + * \brief pixSetBlackOrWhiteBoxa() + * + * \param[in] pixs any depth, can be cmapped + * \param[in] boxa [optional] of boxes, to clear or set + * \param[in] op L_SET_BLACK, L_SET_WHITE + * \return pixd with boxes filled with white or black, or NULL on error + */ +PIX * +pixSetBlackOrWhiteBoxa(PIX *pixs, + BOXA *boxa, + l_int32 op) +{ +l_int32 i, n, d, index; +l_uint32 color; +BOX *box; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixSetBlackOrWhiteBoxa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return pixCopy(NULL, pixs); + if ((n = boxaGetCount(boxa)) == 0) + return pixCopy(NULL, pixs); + + pixd = pixCopy(NULL, pixs); + d = pixGetDepth(pixd); + if (d == 1) { + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + if (op == L_SET_WHITE) + pixClearInRect(pixd, box); + else + pixSetInRect(pixd, box); + boxDestroy(&box); + } + return pixd; + } + + cmap = pixGetColormap(pixs); + if (cmap) { + color = (op == L_SET_WHITE) ? 1 : 0; + pixcmapAddBlackOrWhite(cmap, color, &index); + } else if (d == 8) { + color = (op == L_SET_WHITE) ? 0xff : 0x0; + } else if (d == 32) { + color = (op == L_SET_WHITE) ? 0xffffff00 : 0x0; + } else if (d == 2) { + color = (op == L_SET_WHITE) ? 0x3 : 0x0; + } else if (d == 4) { + color = (op == L_SET_WHITE) ? 0xf : 0x0; + } else if (d == 16) { + color = (op == L_SET_WHITE) ? 0xffff : 0x0; + } else { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + if (cmap) + pixSetInRectArbitrary(pixd, box, index); + else + pixSetInRectArbitrary(pixd, box, color); + boxDestroy(&box); + } + + return pixd; +} + + +/*! + * \brief pixPaintBoxaRandom() + * + * \param[in] pixs any depth, can be cmapped + * \param[in] boxa of boxes, to paint + * \return pixd with painted boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is 1 bpp, we paint the boxa using a colormap;
+ *          otherwise, we convert to 32 bpp.
+ *      (2) We use up to 254 different colors for painting the regions.
+ *      (3) If boxes overlap, the later ones paint over earlier ones.
+ * 
+ */ +PIX * +pixPaintBoxaRandom(PIX *pixs, + BOXA *boxa) +{ +l_int32 i, n, d, rval, gval, bval, index; +l_uint32 val; +BOX *box; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixPaintBoxaRandom"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + + if ((n = boxaGetCount(boxa)) == 0) { + L_WARNING("no boxes to paint; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + if (pixGetDepth(pixs) == 1) + pixd = pixConvert1To8(NULL, pixs, 255, 0); + else + pixd = pixConvertTo32(pixs); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + cmap = pixcmapCreateRandom(8, 1, 1); + d = pixGetDepth(pixd); /* either 8 or 32 */ + if (d == 8) /* colormapped */ + pixSetColormap(pixd, cmap); + + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + index = 1 + (i % 254); + if (d == 8) { + pixSetInRectArbitrary(pixd, box, index); + } else { /* d == 32 */ + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &val); + pixSetInRectArbitrary(pixd, box, val); + } + boxDestroy(&box); + } + + if (d == 32) + pixcmapDestroy(&cmap); + return pixd; +} + + +/*! + * \brief pixBlendBoxaRandom() + * + * \param[in] pixs any depth; can be cmapped + * \param[in] boxa of boxes, to blend/paint + * \param[in] fract of box color to use + * \return pixd 32 bpp, with blend/painted boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) pixs is converted to 32 bpp.
+ *      (2) This differs from pixPaintBoxaRandom(), in that the
+ *          colors here are blended with the color of pixs.
+ *      (3) We use up to 254 different colors for painting the regions.
+ *      (4) If boxes overlap, the final color depends only on the last
+ *          rect that is used.
+ * 
+ */ +PIX * +pixBlendBoxaRandom(PIX *pixs, + BOXA *boxa, + l_float32 fract) +{ +l_int32 i, n, rval, gval, bval, index; +l_uint32 val; +BOX *box; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixBlendBoxaRandom"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + + if ((n = boxaGetCount(boxa)) == 0) { + L_WARNING("no boxes to paint; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + if ((pixd = pixConvertTo32(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not defined", procName, NULL); + + cmap = pixcmapCreateRandom(8, 1, 1); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + index = 1 + (i % 254); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &val); + pixBlendInRect(pixd, box, val, fract); + boxDestroy(&box); + } + + pixcmapDestroy(&cmap); + return pixd; +} + + +/*! + * \brief pixDrawBoxa() + * + * \param[in] pixs any depth; can be cmapped + * \param[in] boxa of boxes, to draw + * \param[in] width of lines + * \param[in] val rgba color to draw + * \return pixd with outlines of boxes added, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp
+ *          and the boxa is drawn using a colormap; otherwise,
+ *          it is converted to 32 bpp rgb.
+ * 
+ */ +PIX * +pixDrawBoxa(PIX *pixs, + BOXA *boxa, + l_int32 width, + l_uint32 val) +{ +l_int32 rval, gval, bval, newindex; +l_int32 mapvacancy; /* true only if cmap and not full */ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixDrawBoxa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (width < 1) + return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); + + if (boxaGetCount(boxa) == 0) { + L_WARNING("no boxes to draw; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + mapvacancy = FALSE; + if ((cmap = pixGetColormap(pixs)) != NULL) { + if (pixcmapGetCount(cmap) < 256) + mapvacancy = TRUE; + } + if (pixGetDepth(pixs) == 1 || mapvacancy) + pixd = pixConvertTo8(pixs, TRUE); + else + pixd = pixConvertTo32(pixs); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + extractRGBValues(val, &rval, &gval, &bval); + if (pixGetDepth(pixd) == 8) { /* colormapped */ + cmap = pixGetColormap(pixd); + pixcmapAddNewColor(cmap, rval, gval, bval, &newindex); + } + + pixRenderBoxaArb(pixd, boxa, width, rval, gval, bval); + return pixd; +} + + +/*! + * \brief pixDrawBoxaRandom() + * + * \param[in] pixs any depth, can be cmapped + * \param[in] boxa of boxes, to draw + * \param[in] width thickness of line + * \return pixd with box outlines drawn, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is 1 bpp, we draw the boxa using a colormap;
+ *          otherwise, we convert to 32 bpp.
+ *      (2) We use up to 254 different colors for drawing the boxes.
+ *      (3) If boxes overlap, the later ones draw over earlier ones.
+ * 
+ */ +PIX * +pixDrawBoxaRandom(PIX *pixs, + BOXA *boxa, + l_int32 width) +{ +l_int32 i, n, rval, gval, bval, index; +BOX *box; +PIX *pixd; +PIXCMAP *cmap; +PTAA *ptaa; + + PROCNAME("pixDrawBoxaRandom"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (width < 1) + return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); + + if ((n = boxaGetCount(boxa)) == 0) { + L_WARNING("no boxes to draw; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + /* Input depth = 1 bpp; generate cmapped output */ + if (pixGetDepth(pixs) == 1) { + ptaa = generatePtaaBoxa(boxa); + pixd = pixRenderRandomCmapPtaa(pixs, ptaa, 1, width, 1); + ptaaDestroy(&ptaa); + return pixd; + } + + /* Generate rgb output */ + pixd = pixConvertTo32(pixs); + cmap = pixcmapCreateRandom(8, 1, 1); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + index = 1 + (i % 254); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + pixRenderBoxArb(pixd, box, width, rval, gval, bval); + boxDestroy(&box); + } + pixcmapDestroy(&cmap); + return pixd; +} + + +/*! + * \brief boxaaDisplay() + * + * \param[in] pixs [optional] 1 bpp + * \param[in] baa boxaa, typically from a 2d sort + * \param[in] linewba line width to display outline of each boxa + * \param[in] linewb line width to display outline of each box + * \param[in] colorba color to display boxa + * \param[in] colorb color to display box + * \param[in] w width of outupt pix; use 0 if determined by %pixs or %baa + * \param[in] h height of outupt pix; use 0 if determined by %pixs or %baa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %pixs exists, this renders the boxes over an 8 bpp version
+ *          of it.  Otherwise, it renders the boxes over an empty image
+ *          with a white background.
+ *      (2) If %pixs exists, the dimensions of %pixd are the same,
+ *          and input values of %w and %h are ignored.
+ *          If %pixs is NULL, the dimensions of %pixd are determined by
+ *            - %w and %h if both are > 0, or
+ *            - the minimum size required using all boxes in %baa.
+ *
+ * 
+ */ +PIX * +boxaaDisplay(PIX *pixs, + BOXAA *baa, + l_int32 linewba, + l_int32 linewb, + l_uint32 colorba, + l_uint32 colorb, + l_int32 w, + l_int32 h) +{ +l_int32 i, j, n, m, rbox, gbox, bbox, rboxa, gboxa, bboxa; +BOX *box; +BOXA *boxa; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("boxaaDisplay"); + + if (!baa) + return (PIX *)ERROR_PTR("baa not defined", procName, NULL); + + if (w <= 0 || h <= 0) { + if (pixs) + pixGetDimensions(pixs, &w, &h, NULL); + else + boxaaGetExtent(baa, &w, &h, NULL, NULL); + } + + if (pixs) { + pixd = pixConvertTo8(pixs, 1); + cmap = pixGetColormap(pixd); + } else { + pixd = pixCreate(w, h, 8); + cmap = pixcmapCreate(8); + pixSetColormap(pixd, cmap); + pixcmapAddColor(cmap, 255, 255, 255); + } + extractRGBValues(colorb, &rbox, &gbox, &bbox); + extractRGBValues(colorba, &rboxa, &gboxa, &bboxa); + pixcmapAddColor(cmap, rbox, gbox, bbox); + pixcmapAddColor(cmap, rboxa, gboxa, bboxa); + + n = boxaaGetCount(baa); + for (i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + boxaGetExtent(boxa, NULL, NULL, &box); + pixRenderBoxArb(pixd, box, linewba, rboxa, gboxa, bboxa); + boxDestroy(&box); + m = boxaGetCount(boxa); + for (j = 0; j < m; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + pixRenderBoxArb(pixd, box, linewb, rbox, gbox, bbox); + boxDestroy(&box); + } + boxaDestroy(&boxa); + } + + return pixd; +} + + +/*! + * \brief pixaDisplayBoxaa() + * + * \param[in] pixas any depth, can be cmapped + * \param[in] baa boxes to draw on input pixa + * \param[in] colorflag L_DRAW_RED, L_DRAW_GREEN, etc + * \param[in] width thickness of lines + * \return pixa with box outlines drawn on each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) All pix in %pixas that are not rgb are converted to rgb.
+ *      (2) Each boxa in %baa contains boxes that will be drawn on
+ *          the corresponding pix in %pixas.
+ *      (3) The color of the boxes drawn on each pix are selected with
+ *          %colorflag:
+ *            * For red, green or blue: use L_DRAW_RED, etc.
+ *            * For sequential r, g, b: use L_DRAW_RGB
+ *            * For random colors: use L_DRAW_RANDOM
+ * 
+ */ +PIXA * +pixaDisplayBoxaa(PIXA *pixas, + BOXAA *baa, + l_int32 colorflag, + l_int32 width) +{ +l_int32 i, j, nba, n, nbox, rval, gval, bval; +l_uint32 color; +l_uint32 colors[255]; +BOXA *boxa; +BOX *box; +PIX *pix; +PIXA *pixad; + + PROCNAME("pixaDisplayBoxaa"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!baa) + return (PIXA *)ERROR_PTR("baa not defined", procName, NULL); + if (width < 1) + return (PIXA *)ERROR_PTR("width must be >= 1", procName, NULL); + if ((nba = boxaaGetCount(baa)) < 1) + return (PIXA *)ERROR_PTR("no boxa in baa", procName, NULL); + if ((n = pixaGetCount(pixas)) == 0) + return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL); + if (n != nba) + return (PIXA *)ERROR_PTR("num pix != num boxa", procName, NULL); + if (colorflag == L_DRAW_RED) + color = 0xff000000; + else if (colorflag == L_DRAW_GREEN) + color = 0x00ff0000; + else if (colorflag == L_DRAW_BLUE) + color = 0x0000ff00; + else if (colorflag == L_DRAW_RGB) + color = 0x000000ff; + else if (colorflag == L_DRAW_RANDOM) + color = 0x00000000; + else + return (PIXA *)ERROR_PTR("invalid colorflag", procName, NULL); + + if (colorflag == L_DRAW_RED || colorflag == L_DRAW_GREEN || + colorflag == L_DRAW_BLUE) { + for (i = 0; i < 255; i++) + colors[i] = color; + } else if (colorflag == L_DRAW_RGB) { + for (i = 0; i < 255; i++) { + if (i % 3 == L_DRAW_RED) + colors[i] = 0xff000000; + else if (i % 3 == L_DRAW_GREEN) + colors[i] = 0x00ff0000; + else /* i % 3 == L_DRAW_BLUE) */ + colors[i] = 0x0000ff00; + } + } else if (colorflag == L_DRAW_RANDOM) { + for (i = 0; i < 255; i++) { + rval = (l_uint32)rand() & 0xff; + gval = (l_uint32)rand() & 0xff; + bval = (l_uint32)rand() & 0xff; + composeRGBPixel(rval, gval, bval, &colors[i]); + } + } + + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_COPY); + boxa = boxaaGetBoxa(baa, i, L_CLONE); + nbox = boxaGetCount(boxa); + for (j = 0; j < nbox; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + extractRGBValues(colors[j % 255], &rval, &gval, &bval); + pixRenderBoxArb(pix, box, width, rval, gval, bval); + boxDestroy(&box); + } + boxaDestroy(&boxa); + pixaAddPix(pixad, pix, L_INSERT); + } + + return pixad; +} + + +/*---------------------------------------------------------------------* + * Split mask components into Boxa * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSplitIntoBoxa() + * + * \param[in] pixs 1 bpp + * \param[in] minsum minimum pixels to trigger propagation + * \param[in] skipdist distance before computing sum for propagation + * \param[in] delta difference required to stop propagation + * \param[in] maxbg maximum number of allowed bg pixels in ref scan + * \param[in] maxcomps use 0 for unlimited number of subdivided components + * \param[in] remainder set to 1 to get b.b. of remaining stuff + * \return boxa of rectangles covering the fg of pixs, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a boxa of rectangles that covers
+ *          the fg of a mask.  For each 8-connected component in pixs,
+ *          it does a greedy partitioning, choosing the largest
+ *          rectangle found from each of the four directions at each iter.
+ *          See pixSplitComponentIntoBoxa() for details.
+ *      (2) The input parameters give some flexibility for boundary
+ *          noise.  The resulting set of rectangles may cover some
+ *          bg pixels.
+ *      (3) This should be used when there are a small number of
+ *          mask components, each of which has sides that are close
+ *          to horizontal and vertical.  The input parameters %delta
+ *          and %maxbg determine whether or not holes in the mask are covered.
+ *      (4) The parameter %maxcomps gives the maximum number of allowed
+ *          rectangles extracted from any single connected component.
+ *          Use 0 if no limit is to be applied.
+ *      (5) The flag %remainder specifies whether we take a final bounding
+ *          box for anything left after the maximum number of allowed
+ *          rectangle is extracted.
+ * 
+ */ +BOXA * +pixSplitIntoBoxa(PIX *pixs, + l_int32 minsum, + l_int32 skipdist, + l_int32 delta, + l_int32 maxbg, + l_int32 maxcomps, + l_int32 remainder) +{ +l_int32 i, n; +BOX *box; +BOXA *boxa, *boxas, *boxad; +PIX *pix; +PIXA *pixas; + + PROCNAME("pixSplitIntoBoxa"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + boxas = pixConnComp(pixs, &pixas, 8); + n = boxaGetCount(boxas); + boxad = boxaCreate(0); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + box = boxaGetBox(boxas, i, L_CLONE); + boxa = pixSplitComponentIntoBoxa(pix, box, minsum, skipdist, + delta, maxbg, maxcomps, remainder); + boxaJoin(boxad, boxa, 0, -1); + pixDestroy(&pix); + boxDestroy(&box); + boxaDestroy(&boxa); + } + + pixaDestroy(&pixas); + boxaDestroy(&boxas); + return boxad; +} + + +/*! + * \brief pixSplitComponentIntoBoxa() + * + * \param[in] pix 1 bpp + * \param[in] box [optional] location of pix w/rt an origin + * \param[in] minsum minimum pixels to trigger propagation + * \param[in] skipdist distance before computing sum for propagation + * \param[in] delta difference required to stop propagation + * \param[in] maxbg maximum number of allowed bg pixels in ref scan + * \param[in] maxcomps use 0 for unlimited number of subdivided components + * \param[in] remainder set to 1 to get b.b. of remaining stuff + * \return boxa of rectangles covering the fg of pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a boxa of rectangles that covers
+ *          the fg of a mask.  It does so by a greedy partitioning of
+ *          the mask, choosing the largest rectangle found from
+ *          each of the four directions at each step.
+ *      (2) The input parameters give some flexibility for boundary
+ *          noise.  The resulting set of rectangles must cover all
+ *          the fg pixels and, in addition, may cover some bg pixels.
+ *          Using small input parameters on a noiseless mask (i.e., one
+ *          that has only large vertical and horizontal edges) will
+ *          result in a proper covering of only the fg pixels of the mask.
+ *      (3) The input is assumed to be a single connected component, that
+ *          may have holes.  From each side, sweep inward, counting
+ *          the pixels.  If the count becomes greater than %minsum,
+ *          and we have moved forward a further amount %skipdist,
+ *          record that count ('countref'), but don't accept if the scan
+ *          contains more than %maxbg bg pixels.  Continue the scan
+ *          until we reach a count that differs from countref by at
+ *          least %delta, at which point the propagation stops.  The box
+ *          swept out gets a score, which is the sum of fg pixels
+ *          minus a penalty.  The penalty is the number of bg pixels
+ *          in the box.  This is done from all four sides, and the
+ *          side with the largest score is saved as a rectangle.
+ *          The process repeats until there is either no rectangle
+ *          left, or there is one that can't be captured from any
+ *          direction.  For the latter case, we simply accept the
+ *          last rectangle.
+ *      (4) The input box is only used to specify the location of
+ *          the UL corner of pix, with respect to an origin that
+ *          typically represents the UL corner of an underlying image,
+ *          of which pix is one component.  If %box is null,
+ *          the UL corner is taken to be (0, 0).
+ *      (5) The parameter %maxcomps gives the maximum number of allowed
+ *          rectangles extracted from any single connected component.
+ *          Use 0 if no limit is to be applied.
+ *      (6) The flag %remainder specifies whether we take a final bounding
+ *          box for anything left after the maximum number of allowed
+ *          rectangle is extracted.
+ *      (7) So if %maxcomps > 0, it specifies that we want no more than
+ *          the first %maxcomps rectangles that satisfy the input
+ *          criteria.  After this, we can get a final rectangle that
+ *          bounds everything left over by setting %remainder == 1.
+ *          If %remainder == 0, we only get rectangles that satisfy
+ *          the input criteria.
+ *      (8) It should be noted that the removal of rectangles can
+ *          break the original c.c. into several c.c.
+ *      (9) Summing up:
+ *            * If %maxcomp == 0, the splitting proceeds as far as possible.
+ *            * If %maxcomp > 0, the splitting stops when %maxcomps are
+ *                found, or earlier if no more components can be selected.
+ *            * If %remainder == 1 and components remain that cannot be
+ *                selected, they are returned as a single final rectangle;
+ *                otherwise, they are ignored.
+ * 
+ */ +BOXA * +pixSplitComponentIntoBoxa(PIX *pix, + BOX *box, + l_int32 minsum, + l_int32 skipdist, + l_int32 delta, + l_int32 maxbg, + l_int32 maxcomps, + l_int32 remainder) +{ +l_int32 i, w, h, boxx, boxy, bx, by, bw, bh, maxdir, maxscore; +l_int32 iter; +BOX *boxs; /* shrinks as rectangular regions are removed */ +BOX *boxt1, *boxt2, *boxt3; +BOXA *boxat; /* stores rectangle data for each side in an iteration */ +BOXA *boxad; +NUMA *nascore, *nas; +PIX *pixs; + + PROCNAME("pixSplitComponentIntoBoxa"); + + if (!pix || pixGetDepth(pix) != 1) + return (BOXA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + + pixs = pixCopy(NULL, pix); + pixGetDimensions(pixs, &w, &h, NULL); + if (box) + boxGetGeometry(box, &boxx, &boxy, NULL, NULL); + else + boxx = boxy = 0; + boxs = boxCreate(0, 0, w, h); + boxad = boxaCreate(0); + + iter = 0; + while (boxs != NULL) { + boxGetGeometry(boxs, &bx, &by, &bw, &bh); + boxat = boxaCreate(4); /* potential rectangular regions */ + nascore = numaCreate(4); + for (i = 0; i < 4; i++) { + pixSearchForRectangle(pixs, boxs, minsum, skipdist, delta, maxbg, + i, boxat, nascore); + } + nas = numaGetSortIndex(nascore, L_SORT_DECREASING); + numaGetIValue(nas, 0, &maxdir); + numaGetIValue(nascore, maxdir, &maxscore); +#if DEBUG_SPLIT + fprintf(stderr, "Iteration: %d\n", iter); + boxPrintStreamInfo(stderr, boxs); + boxaWriteStream(stderr, boxat); + fprintf(stderr, "\nmaxdir = %d, maxscore = %d\n\n", maxdir, maxscore); +#endif /* DEBUG_SPLIT */ + if (maxscore > 0) { /* accept this */ + boxt1 = boxaGetBox(boxat, maxdir, L_CLONE); + boxt2 = boxTransform(boxt1, boxx, boxy, 1.0, 1.0); + boxaAddBox(boxad, boxt2, L_INSERT); + pixClearInRect(pixs, boxt1); + boxDestroy(&boxt1); + pixClipBoxToForeground(pixs, boxs, NULL, &boxt3); + boxDestroy(&boxs); + boxs = boxt3; + if (boxs) { + boxGetGeometry(boxs, NULL, NULL, &bw, &bh); + if (bw < 2 || bh < 2) + boxDestroy(&boxs); /* we're done */ + } + } else { /* no more valid rectangles can be found */ + if (remainder == 1) { /* save the last box */ + boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); + boxaAddBox(boxad, boxt1, L_INSERT); + } + boxDestroy(&boxs); /* we're done */ + } + boxaDestroy(&boxat); + numaDestroy(&nascore); + numaDestroy(&nas); + + iter++; + if ((iter == maxcomps) && boxs) { + if (remainder == 1) { /* save the last box */ + boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); + boxaAddBox(boxad, boxt1, L_INSERT); + } + boxDestroy(&boxs); /* we're done */ + } + } + + pixDestroy(&pixs); + return boxad; +} + + +/*! + * \brief pixSearchForRectangle() + * + * \param[in] pixs 1 bpp + * \param[in] boxs current region to investigate + * \param[in] minsum minimum pixels to trigger propagation + * \param[in] skipdist distance before computing sum for propagation + * \param[in] delta difference required to stop propagation + * \param[in] maxbg maximum number of allowed bg pixels in ref scan + * \param[in] sideflag side to search from + * \param[in] boxat add result of rectangular region found here + * \param[in] nascore add score for this rectangle here + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixSplitComponentIntoBoxa() for an explanation of the algorithm.
+ *          This does the sweep from a single side.  For each iteration
+ *          in pixSplitComponentIntoBoxa(), this will be called 4 times,
+ *          for %sideflag = {0, 1, 2, 3}.
+ *      (2) If a valid rectangle is not found, add a score of 0 and
+ *          input a minimum box.
+ * 
+ */ +static l_int32 +pixSearchForRectangle(PIX *pixs, + BOX *boxs, + l_int32 minsum, + l_int32 skipdist, + l_int32 delta, + l_int32 maxbg, + l_int32 sideflag, + BOXA *boxat, + NUMA *nascore) +{ +l_int32 bx, by, bw, bh, width, height, setref, atref; +l_int32 minincol, maxincol, mininrow, maxinrow, minval, maxval, bgref; +l_int32 x, y, x0, y0, xref, yref, colsum, rowsum, score, countref, diff; +void **lines1; +BOX *boxr; + + PROCNAME("pixSearchForRectangle"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (!boxs) + return ERROR_INT("boxs not defined", procName, 1); + if (!boxat) + return ERROR_INT("boxat not defined", procName, 1); + if (!nascore) + return ERROR_INT("nascore not defined", procName, 1); + + lines1 = pixGetLinePtrs(pixs, NULL); + boxGetGeometry(boxs, &bx, &by, &bw, &bh); + boxr = NULL; + setref = 0; + atref = 0; + maxval = 0; + minval = 100000; + score = 0; /* sum of all (fg - bg) pixels seen in the scan */ + xref = yref = 100000; /* init to impossibly big number */ + if (sideflag == L_FROM_LEFT) { + for (x = bx; x < bx + bw; x++) { + colsum = 0; + maxincol = 0; + minincol = 100000; + for (y = by; y < by + bh; y++) { + if (GET_DATA_BIT(lines1[y], x)) { + colsum++; + if (y > maxincol) maxincol = y; + if (y < minincol) minincol = y; + } + } + score += colsum; + + /* Enough fg to sweep out a rectangle? */ + if (!setref && colsum >= minsum) { + setref = 1; + xref = x + 10; + if (xref >= bx + bw) + goto failure; + } + + /* Reached the reference line; save the count; + * if there is too much bg, the rectangle is invalid. */ + if (setref && x == xref) { + atref = 1; + countref = colsum; + bgref = maxincol - minincol + 1 - countref; + if (bgref > maxbg) + goto failure; + } + + /* Have we left the rectangle? If so, save it along + * with the score. */ + if (atref) { + diff = L_ABS(colsum - countref); + if (diff >= delta || x == bx + bw - 1) { + height = maxval - minval + 1; + width = x - bx; + if (x == bx + bw - 1) width = x - bx + 1; + boxr = boxCreate(bx, minval, width, height); + score = 2 * score - width * height; + goto success; + } + } + maxval = L_MAX(maxval, maxincol); + minval = L_MIN(minval, minincol); + } + goto failure; + } else if (sideflag == L_FROM_RIGHT) { + for (x = bx + bw - 1; x >= bx; x--) { + colsum = 0; + maxincol = 0; + minincol = 100000; + for (y = by; y < by + bh; y++) { + if (GET_DATA_BIT(lines1[y], x)) { + colsum++; + if (y > maxincol) maxincol = y; + if (y < minincol) minincol = y; + } + } + score += colsum; + if (!setref && colsum >= minsum) { + setref = 1; + xref = x - 10; + if (xref < bx) + goto failure; + } + if (setref && x == xref) { + atref = 1; + countref = colsum; + bgref = maxincol - minincol + 1 - countref; + if (bgref > maxbg) + goto failure; + } + if (atref) { + diff = L_ABS(colsum - countref); + if (diff >= delta || x == bx) { + height = maxval - minval + 1; + x0 = x + 1; + if (x == bx) x0 = x; + width = bx + bw - x0; + boxr = boxCreate(x0, minval, width, height); + score = 2 * score - width * height; + goto success; + } + } + maxval = L_MAX(maxval, maxincol); + minval = L_MIN(minval, minincol); + } + goto failure; + } else if (sideflag == L_FROM_TOP) { + for (y = by; y < by + bh; y++) { + rowsum = 0; + maxinrow = 0; + mininrow = 100000; + for (x = bx; x < bx + bw; x++) { + if (GET_DATA_BIT(lines1[y], x)) { + rowsum++; + if (x > maxinrow) maxinrow = x; + if (x < mininrow) mininrow = x; + } + } + score += rowsum; + if (!setref && rowsum >= minsum) { + setref = 1; + yref = y + 10; + if (yref >= by + bh) + goto failure; + } + if (setref && y == yref) { + atref = 1; + countref = rowsum; + bgref = maxinrow - mininrow + 1 - countref; + if (bgref > maxbg) + goto failure; + } + if (atref) { + diff = L_ABS(rowsum - countref); + if (diff >= delta || y == by + bh - 1) { + width = maxval - minval + 1; + height = y - by; + if (y == by + bh - 1) height = y - by + 1; + boxr = boxCreate(minval, by, width, height); + score = 2 * score - width * height; + goto success; + } + } + maxval = L_MAX(maxval, maxinrow); + minval = L_MIN(minval, mininrow); + } + goto failure; + } else if (sideflag == L_FROM_BOT) { + for (y = by + bh - 1; y >= by; y--) { + rowsum = 0; + maxinrow = 0; + mininrow = 100000; + for (x = bx; x < bx + bw; x++) { + if (GET_DATA_BIT(lines1[y], x)) { + rowsum++; + if (x > maxinrow) maxinrow = x; + if (x < mininrow) mininrow = x; + } + } + score += rowsum; + if (!setref && rowsum >= minsum) { + setref = 1; + yref = y - 10; + if (yref < by) + goto failure; + } + if (setref && y == yref) { + atref = 1; + countref = rowsum; + bgref = maxinrow - mininrow + 1 - countref; + if (bgref > maxbg) + goto failure; + } + if (atref) { + diff = L_ABS(rowsum - countref); + if (diff >= delta || y == by) { + width = maxval - minval + 1; + y0 = y + 1; + if (y == by) y0 = y; + height = by + bh - y0; + boxr = boxCreate(minval, y0, width, height); + score = 2 * score - width * height; + goto success; + } + } + maxval = L_MAX(maxval, maxinrow); + minval = L_MIN(minval, mininrow); + } + goto failure; + } + +failure: + numaAddNumber(nascore, 0); + boxaAddBox(boxat, boxCreate(0, 0, 1, 1), L_INSERT); /* min box */ + LEPT_FREE(lines1); + return 0; + +success: + numaAddNumber(nascore, score); + boxaAddBox(boxat, boxr, L_INSERT); + LEPT_FREE(lines1); + return 0; +} + + +/*---------------------------------------------------------------------* + * Represent horizontal or vertical mosaic strips * + *---------------------------------------------------------------------*/ +/*! + * \brief makeMosaicStrips() + * + * \param[in] w, h + * \param[in] direction L_SCAN_HORIZONTAL or L_SCAN_VERTICAL + * \param[in] size of strips in the scan direction + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) For example, this can be used to generate a pixa of
+ *          vertical strips of width 10 from an image, using:
+ *             pixGetDimensions(pix, &w, &h, NULL);
+ *             boxa = makeMosaicStrips(w, h, L_SCAN_HORIZONTAL, 10);
+ *             pixa = pixClipRectangles(pix, boxa);
+ *          All strips except the last will be the same width.  The
+ *          last strip will have width w % 10.
+ * 
+ */ +BOXA * +makeMosaicStrips(l_int32 w, + l_int32 h, + l_int32 direction, + l_int32 size) +{ +l_int32 i, nstrips, extra; +BOX *box; +BOXA *boxa; + + PROCNAME("makeMosaicStrips"); + + if (w < 1 || h < 1) + return (BOXA *)ERROR_PTR("invalid w or h", procName, NULL); + if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL) + return (BOXA *)ERROR_PTR("invalid direction", procName, NULL); + if (size < 1) + return (BOXA *)ERROR_PTR("size < 1", procName, NULL); + + boxa = boxaCreate(0); + if (direction == L_SCAN_HORIZONTAL) { + nstrips = w / size; + for (i = 0; i < nstrips; i++) { + box = boxCreate(i * size, 0, size, h); + boxaAddBox(boxa, box, L_INSERT); + } + if ((extra = w % size) > 0) { + box = boxCreate(nstrips * size, 0, extra, h); + boxaAddBox(boxa, box, L_INSERT); + } + } else { + nstrips = h / size; + for (i = 0; i < nstrips; i++) { + box = boxCreate(0, i * size, w, size); + boxaAddBox(boxa, box, L_INSERT); + } + if ((extra = h % size) > 0) { + box = boxCreate(0, nstrips * size, w, extra); + boxaAddBox(boxa, box, L_INSERT); + } + } + return boxa; +} + + +/*---------------------------------------------------------------------* + * Comparison between boxa * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaCompareRegions() + * + * \param[in] boxa1, boxa2 + * \param[in] areathresh minimum area of boxes to be considered + * \param[out] pnsame true if same number of boxes + * \param[out] pdiffarea fractional difference in total area + * \param[out] pdiffxor [optional] fractional difference in xor of regions + * \param[out] ppixdb [optional] debug pix showing two boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes 2 boxa, removes all boxes smaller than a given area,
+ *          and compares the remaining boxes between the boxa.
+ *      (2) The area threshold is introduced to help remove noise from
+ *          small components.  Any box with a smaller value of w * h
+ *          will be removed from consideration.
+ *      (3) The xor difference is the most stringent test, requiring alignment
+ *          of the corresponding boxes.  It is also more computationally
+ *          intensive and is optionally returned.  Alignment is to the
+ *          UL corner of each region containing all boxes, as given by
+ *          boxaGetExtent().
+ *      (4) Both fractional differences are with respect to the total
+ *          area in the two boxa.  They range from 0.0 to 1.0.
+ *          A perfect match has value 0.0.  If both boxa are empty,
+ *          we return 0.0; if one is empty we return 1.0.
+ *      (5) An example input might be the rectangular regions of a
+ *          segmentation mask for text or images from two pages.
+ * 
+ */ +l_ok +boxaCompareRegions(BOXA *boxa1, + BOXA *boxa2, + l_int32 areathresh, + l_int32 *pnsame, + l_float32 *pdiffarea, + l_float32 *pdiffxor, + PIX **ppixdb) +{ +l_int32 w, h, x3, y3, w3, h3, x4, y4, w4, h4, n3, n4, area1, area2; +l_int32 count3, count4, countxor; +l_int32 *tab; +BOX *box3, *box4; +BOXA *boxa3, *boxa4, *boxa3t, *boxa4t; +PIX *pix1, *pix2, *pix3, *pix4, *pix5; +PIXA *pixa; + + PROCNAME("boxaCompareRegions"); + + if (pdiffxor) *pdiffxor = 1.0; + if (ppixdb) *ppixdb = NULL; + if (pnsame) *pnsame = FALSE; + if (pdiffarea) *pdiffarea = 1.0; + if (!boxa1 || !boxa2) + return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); + if (!pnsame) + return ERROR_INT("&nsame not defined", procName, 1); + if (!pdiffarea) + return ERROR_INT("&diffarea not defined", procName, 1); + + boxa3 = boxaSelectByArea(boxa1, areathresh, L_SELECT_IF_GTE, NULL); + boxa4 = boxaSelectByArea(boxa2, areathresh, L_SELECT_IF_GTE, NULL); + n3 = boxaGetCount(boxa3); + n4 = boxaGetCount(boxa4); + if (n3 == n4) + *pnsame = TRUE; + + /* There are no boxes in one or both */ + if (n3 == 0 || n4 == 0) { + boxaDestroy(&boxa3); + boxaDestroy(&boxa4); + if (n3 == 0 && n4 == 0) { /* they are both empty: we say they are the + * same; otherwise, they differ maximally + * and retain the default value. */ + *pdiffarea = 0.0; + if (pdiffxor) *pdiffxor = 0.0; + } + return 0; + } + + /* There are boxes in both */ + boxaGetArea(boxa3, &area1); + boxaGetArea(boxa4, &area2); + *pdiffarea = (l_float32)L_ABS(area1 - area2) / (l_float32)(area1 + area2); + if (!pdiffxor) { + boxaDestroy(&boxa3); + boxaDestroy(&boxa4); + return 0; + } + + /* The easiest way to get the xor of aligned boxes is to work + * with images of each boxa. This is done by translating each + * boxa so that the UL corner of the region that includes all + * boxes in the boxa is placed at the origin of each pix. */ + boxaGetExtent(boxa3, &w, &h, &box3); + boxaGetExtent(boxa4, &w, &h, &box4); + boxGetGeometry(box3, &x3, &y3, &w3, &h3); + boxGetGeometry(box4, &x4, &y4, &w4, &h4); + boxa3t = boxaTransform(boxa3, -x3, -y3, 1.0, 1.0); + boxa4t = boxaTransform(boxa4, -x4, -y4, 1.0, 1.0); + w = L_MAX(x3 + w3, x4 + w4); + h = L_MAX(y3 + h3, y4 + h4); + pix3 = pixCreate(w, h, 1); /* use the max to keep everything in the xor */ + pix4 = pixCreate(w, h, 1); + pixMaskBoxa(pix3, pix3, boxa3t, L_SET_PIXELS); + pixMaskBoxa(pix4, pix4, boxa4t, L_SET_PIXELS); + tab = makePixelSumTab8(); + pixCountPixels(pix3, &count3, tab); + pixCountPixels(pix4, &count4, tab); + pix5 = pixXor(NULL, pix3, pix4); + pixCountPixels(pix5, &countxor, tab); + LEPT_FREE(tab); + *pdiffxor = (l_float32)countxor / (l_float32)(count3 + count4); + + if (ppixdb) { + pixa = pixaCreate(2); + pix1 = pixCreate(w, h, 32); + pixSetAll(pix1); + pixRenderHashBoxaBlend(pix1, boxa3, 5, 1, L_POS_SLOPE_LINE, 2, + 255, 0, 0, 0.5); + pixRenderHashBoxaBlend(pix1, boxa4, 5, 1, L_NEG_SLOPE_LINE, 2, + 0, 255, 0, 0.5); + pixaAddPix(pixa, pix1, L_INSERT); + pix2 = pixCreate(w, h, 32); + pixPaintThroughMask(pix2, pix3, x3, y3, 0xff000000); + pixPaintThroughMask(pix2, pix4, x4, y4, 0x00ff0000); + pixAnd(pix3, pix3, pix4); + pixPaintThroughMask(pix2, pix3, x3, y3, 0x0000ff00); + pixaAddPix(pixa, pix2, L_INSERT); + *ppixdb = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2); + pixaDestroy(&pixa); + } + + boxDestroy(&box3); + boxDestroy(&box4); + boxaDestroy(&boxa3); + boxaDestroy(&boxa3t); + boxaDestroy(&boxa4); + boxaDestroy(&boxa4t); + pixDestroy(&pix3); + pixDestroy(&pix4); + pixDestroy(&pix5); + return 0; +} + + +/*---------------------------------------------------------------------* + * Reliable selection of a single large box * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSelectLargeULComp() + * + * \param[in] pixs 1 bpp + * \param[in] areaslop fraction near but less than 1.0 + * \param[in] yslop number of pixels in y direction + * \param[in] connectivity 4 or 8 + * \return box, or NULL on error + * + *
+ * Notes:
+ *      (1) This selects a box near the top (first) and left (second)
+ *          of the image, from the set of all boxes that have
+ *                area >= %areaslop * (area of biggest box),
+ *          where %areaslop is some fraction; say ~ 0.9.
+ *      (2) For all boxes satisfying the above condition, select
+ *          the left-most box that is within %yslop (say, 20) pixels
+ *          of the box nearest the top.
+ *      (3) This can be used to reliably select a specific one of
+ *          the largest regions in an image, for applications where
+ *          there are expected to be small variations in region size
+ *          and location.
+ *      (4) See boxSelectLargeULBox() for implementation details.
+ * 
+ */ +BOX * +pixSelectLargeULComp(PIX *pixs, + l_float32 areaslop, + l_int32 yslop, + l_int32 connectivity) +{ +BOX *box; +BOXA *boxa1; + + PROCNAME("pixSelectLargeULComp"); + + if (!pixs) + return (BOX *)ERROR_PTR("pixs not defined", procName, NULL); + if (areaslop < 0.0 || areaslop > 1.0) + return (BOX *)ERROR_PTR("invalid value for areaslop", procName, NULL); + yslop = L_MAX(0, yslop); + + boxa1 = pixConnCompBB(pixs, connectivity); + if (boxaGetCount(boxa1) == 0) { + boxaDestroy(&boxa1); + return NULL; + } + box = boxaSelectLargeULBox(boxa1, areaslop, yslop); + boxaDestroy(&boxa1); + return box; +} + + +/*! + * \brief boxaSelectLargeULBox() + * + * \param[in] boxas 1 bpp + * \param[in] areaslop fraction near but less than 1.0 + * \param[in] yslop number of pixels in y direction + * \return box, or NULL on error + * + *
+ * Notes:
+ *      (1) See usage notes in pixSelectLargeULComp().
+ * 
+ */ +BOX * +boxaSelectLargeULBox(BOXA *boxas, + l_float32 areaslop, + l_int32 yslop) +{ +l_int32 w, h, i, n, x1, y1, x2, y2, select; +l_float32 area, max_area; +BOX *box; +BOXA *boxa1, *boxa2, *boxa3; + + PROCNAME("boxaSelectLargeULBox"); + + if (!boxas) + return (BOX *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxaGetCount(boxas) == 0) + return (BOX *)ERROR_PTR("no boxes in boxas", procName, NULL); + if (areaslop < 0.0 || areaslop > 1.0) + return (BOX *)ERROR_PTR("invalid value for areaslop", procName, NULL); + yslop = L_MAX(0, yslop); + + boxa1 = boxaSort(boxas, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); + boxa2 = boxaSort(boxa1, L_SORT_BY_Y, L_SORT_INCREASING, NULL); + n = boxaGetCount(boxa2); + boxaGetBoxGeometry(boxa1, 0, NULL, NULL, &w, &h); /* biggest box by area */ + max_area = (l_float32)(w * h); + + /* boxa3 collects all boxes eligible by area, sorted top-down */ + boxa3 = boxaCreate(4); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa2, i, NULL, NULL, &w, &h); + area = (l_float32)(w * h); + if (area / max_area >= areaslop) { + box = boxaGetBox(boxa2, i, L_COPY); + boxaAddBox(boxa3, box, L_INSERT); + } + } + + /* Take the first (top-most box) unless the second (etc) has + * nearly the same y value but a smaller x value. */ + n = boxaGetCount(boxa3); + boxaGetBoxGeometry(boxa3, 0, &x1, &y1, NULL, NULL); + select = 0; + for (i = 1; i < n; i++) { + boxaGetBoxGeometry(boxa3, i, &x2, &y2, NULL, NULL); + if (y2 - y1 < yslop && x2 < x1) { + select = i; + x1 = x2; /* but always compare against y1 */ + } + } + + box = boxaGetBox(boxa3, select, L_COPY); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + return box; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxfunc4.c b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc4.c new file mode 100644 index 0000000..22a8048 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc4.c @@ -0,0 +1,1423 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxfunc4.c + *
+ *
+ *      Boxa and Boxaa range selection
+ *           BOXA     *boxaSelectRange()
+ *           BOXAA    *boxaaSelectRange()
+ *
+ *      Boxa size selection
+ *           BOXA     *boxaSelectBySize()
+ *           NUMA     *boxaMakeSizeIndicator()
+ *           BOXA     *boxaSelectByArea()
+ *           NUMA     *boxaMakeAreaIndicator()
+ *           BOXA     *boxaSelectByWHRatio()
+ *           NUMA     *boxaMakeWHRatioIndicator()
+ *           BOXA     *boxaSelectWithIndicator()
+ *
+ *      Boxa permutation
+ *           BOXA     *boxaPermutePseudorandom()
+ *           BOXA     *boxaPermuteRandom()
+ *           l_int32   boxaSwapBoxes()
+ *
+ *      Boxa and box conversions
+ *           PTA      *boxaConvertToPta()
+ *           BOXA     *ptaConvertToBoxa()
+ *           PTA      *boxConvertToPta()
+ *           BOX      *ptaConvertToBox()
+ *
+ *      Miscellaneous boxa functions
+ *           l_int32   boxaGetExtent()
+ *           l_int32   boxaGetCoverage()
+ *           l_int32   boxaaSizeRange()
+ *           l_int32   boxaSizeRange()
+ *           l_int32   boxaLocationRange()
+ *           NUMA     *boxaGetSizes()
+ *           l_int32   boxaGetArea()
+ *           PIX      *boxaDisplayTiled()
+ * 
+ */ + +#include +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Boxa and boxaa range selection * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaSelectRange() + * + * \param[in] boxas + * \param[in] first use 0 to select from the beginning + * \param[in] last use -1 to select to the end + * \param[in] copyflag L_COPY, L_CLONE + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) The copyflag specifies what we do with each box from boxas.
+ *          Specifically, L_CLONE inserts a clone into boxad of each
+ *          selected box from boxas.
+ * 
+ */ +BOXA * +boxaSelectRange(BOXA *boxas, + l_int32 first, + l_int32 last, + l_int32 copyflag) +{ +l_int32 n, nbox, i; +BOX *box; +BOXA *boxad; + + PROCNAME("boxaSelectRange"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); + if ((n = boxaGetCount(boxas)) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, copyflag); + } + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return (BOXA *)ERROR_PTR("invalid first", procName, NULL); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return (BOXA *)ERROR_PTR("first > last", procName, NULL); + + nbox = last - first + 1; + boxad = boxaCreate(nbox); + for (i = first; i <= last; i++) { + box = boxaGetBox(boxas, i, copyflag); + boxaAddBox(boxad, box, L_INSERT); + } + return boxad; +} + + +/*! + * \brief boxaaSelectRange() + * + * \param[in] baas + * \param[in] first use 0 to select from the beginning + * \param[in] last use -1 to select to the end + * \param[in] copyflag L_COPY, L_CLONE + * \return baad, or NULL on error + * + *
+ * Notes:
+ *      (1) The copyflag specifies what we do with each boxa from baas.
+ *          Specifically, L_CLONE inserts a clone into baad of each
+ *          selected boxa from baas.
+ * 
+ */ +BOXAA * +boxaaSelectRange(BOXAA *baas, + l_int32 first, + l_int32 last, + l_int32 copyflag) +{ +l_int32 n, nboxa, i; +BOXA *boxa; +BOXAA *baad; + + PROCNAME("boxaaSelectRange"); + + if (!baas) + return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); + if ((n = boxaaGetCount(baas)) == 0) + return (BOXAA *)ERROR_PTR("empty baas", procName, NULL); + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return (BOXAA *)ERROR_PTR("invalid first", procName, NULL); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return (BOXAA *)ERROR_PTR("first > last", procName, NULL); + + nboxa = last - first + 1; + baad = boxaaCreate(nboxa); + for (i = first; i <= last; i++) { + boxa = boxaaGetBoxa(baas, i, copyflag); + boxaaAddBoxa(baad, boxa, L_INSERT); + } + return baad; +} + + +/*---------------------------------------------------------------------* + * Boxa size selection * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaSelectBySize() + * + * \param[in] boxas + * \param[in] width, height threshold dimensions + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return boxad filtered set, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) Uses box copies in the new boxa.
+ *      (3) If the selection type is L_SELECT_WIDTH, the input
+ *          height is ignored, and v.v.
+ *      (4) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +BOXA * +boxaSelectBySize(BOXA *boxas, + l_int32 width, + l_int32 height, + l_int32 type, + l_int32 relation, + l_int32 *pchanged) +{ +BOXA *boxad; +NUMA *na; + + PROCNAME("boxaSelectBySize"); + + if (pchanged) *pchanged = FALSE; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxaGetCount(boxas) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (BOXA *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); + + /* Compute the indicator array for saving components */ + if ((na = + boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL) + return (BOXA *)ERROR_PTR("na not made", procName, NULL); + + /* Filter to get output */ + boxad = boxaSelectWithIndicator(boxas, na, pchanged); + + numaDestroy(&na); + return boxad; +} + + +/*! + * \brief boxaMakeSizeIndicator() + * + * \param[in] boxa + * \param[in] width, height threshold dimensions + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return na indicator array, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) If the selection type is L_SELECT_WIDTH, the input
+ *          height is ignored, and v.v.
+ *      (3) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +NUMA * +boxaMakeSizeIndicator(BOXA *boxa, + l_int32 width, + l_int32 height, + l_int32 type, + l_int32 relation) +{ +l_int32 i, n, w, h, ival; +NUMA *na; + + PROCNAME("boxaMakeSizeIndicator"); + + if (!boxa) + return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); + if ((n = boxaGetCount(boxa)) == 0) + return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); + + na = numaCreate(n); + for (i = 0; i < n; i++) { + ival = 0; + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + switch (type) + { + case L_SELECT_WIDTH: + if ((relation == L_SELECT_IF_LT && w < width) || + (relation == L_SELECT_IF_GT && w > width) || + (relation == L_SELECT_IF_LTE && w <= width) || + (relation == L_SELECT_IF_GTE && w >= width)) + ival = 1; + break; + case L_SELECT_HEIGHT: + if ((relation == L_SELECT_IF_LT && h < height) || + (relation == L_SELECT_IF_GT && h > height) || + (relation == L_SELECT_IF_LTE && h <= height) || + (relation == L_SELECT_IF_GTE && h >= height)) + ival = 1; + break; + case L_SELECT_IF_EITHER: + if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || + ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || + ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || + ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) + ival = 1; + break; + case L_SELECT_IF_BOTH: + if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || + ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || + ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || + ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) + ival = 1; + break; + default: + L_WARNING("can't get here!\n", procName); + break; + } + numaAddNumber(na, ival); + } + + return na; +} + + +/*! + * \brief boxaSelectByArea() + * + * \param[in] boxas + * \param[in] area threshold value of width * height + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return boxad filtered set, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses box copies in the new boxa.
+ *      (2) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +BOXA * +boxaSelectByArea(BOXA *boxas, + l_int32 area, + l_int32 relation, + l_int32 *pchanged) +{ +BOXA *boxad; +NUMA *na; + + PROCNAME("boxaSelectByArea"); + + if (pchanged) *pchanged = FALSE; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxaGetCount(boxas) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); + + /* Compute the indicator array for saving components */ + na = boxaMakeAreaIndicator(boxas, area, relation); + + /* Filter to get output */ + boxad = boxaSelectWithIndicator(boxas, na, pchanged); + + numaDestroy(&na); + return boxad; +} + + +/*! + * \brief boxaMakeAreaIndicator() + * + * \param[in] boxa + * \param[in] area threshold value of width * height + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return na indicator array, or NULL on error + * + *
+ * Notes:
+ *      (1) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +NUMA * +boxaMakeAreaIndicator(BOXA *boxa, + l_int32 area, + l_int32 relation) +{ +l_int32 i, n, w, h, ival; +NUMA *na; + + PROCNAME("boxaMakeAreaIndicator"); + + if (!boxa) + return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); + if ((n = boxaGetCount(boxa)) == 0) + return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); + + na = numaCreate(n); + for (i = 0; i < n; i++) { + ival = 0; + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + + if ((relation == L_SELECT_IF_LT && w * h < area) || + (relation == L_SELECT_IF_GT && w * h > area) || + (relation == L_SELECT_IF_LTE && w * h <= area) || + (relation == L_SELECT_IF_GTE && w * h >= area)) + ival = 1; + numaAddNumber(na, ival); + } + + return na; +} + + +/*! + * \brief boxaSelectByWHRatio() + * + * \param[in] boxas + * \param[in] ratio width/height threshold value + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return boxad filtered set, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses box copies in the new boxa.
+ *      (2) To keep narrow components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep wide components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +BOXA * +boxaSelectByWHRatio(BOXA *boxas, + l_float32 ratio, + l_int32 relation, + l_int32 *pchanged) +{ +BOXA *boxad; +NUMA *na; + + PROCNAME("boxaSelectByWHRatio"); + + if (pchanged) *pchanged = FALSE; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (boxaGetCount(boxas) == 0) { + L_WARNING("boxas is empty\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); + + /* Compute the indicator array for saving components */ + na = boxaMakeWHRatioIndicator(boxas, ratio, relation); + + /* Filter to get output */ + boxad = boxaSelectWithIndicator(boxas, na, pchanged); + + numaDestroy(&na); + return boxad; +} + + +/*! + * \brief boxaMakeWHRatioIndicator() + * + * \param[in] boxa + * \param[in] ratio width/height threshold value + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return na indicator array, or NULL on error + * + *
+ * Notes:
+ *      (1) To keep narrow components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep wide components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +NUMA * +boxaMakeWHRatioIndicator(BOXA *boxa, + l_float32 ratio, + l_int32 relation) +{ +l_int32 i, n, w, h, ival; +l_float32 whratio; +NUMA *na; + + PROCNAME("boxaMakeWHRatioIndicator"); + + if (!boxa) + return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); + if ((n = boxaGetCount(boxa)) == 0) + return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); + + na = numaCreate(n); + for (i = 0; i < n; i++) { + ival = 0; + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + whratio = (l_float32)w / (l_float32)h; + + if ((relation == L_SELECT_IF_LT && whratio < ratio) || + (relation == L_SELECT_IF_GT && whratio > ratio) || + (relation == L_SELECT_IF_LTE && whratio <= ratio) || + (relation == L_SELECT_IF_GTE && whratio >= ratio)) + ival = 1; + numaAddNumber(na, ival); + } + + return na; +} + + +/*! + * \brief boxaSelectWithIndicator() + * + * \param[in] boxas + * \param[in] na indicator numa + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return boxad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a copy of the boxa if no components are removed.
+ *      (2) Uses box copies in the new boxa.
+ *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
+ *      (4) If all indicator values are 0, the returned boxa is empty.
+ * 
+ */ +BOXA * +boxaSelectWithIndicator(BOXA *boxas, + NUMA *na, + l_int32 *pchanged) +{ +l_int32 i, n, ival, nsave; +BOX *box; +BOXA *boxad; + + PROCNAME("boxaSelectWithIndicator"); + + if (pchanged) *pchanged = FALSE; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!na) + return (BOXA *)ERROR_PTR("na not defined", procName, NULL); + + nsave = 0; + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 1) nsave++; + } + + if (nsave == n) { + if (pchanged) *pchanged = FALSE; + return boxaCopy(boxas, L_COPY); + } + if (pchanged) *pchanged = TRUE; + boxad = boxaCreate(nsave); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 0) continue; + box = boxaGetBox(boxas, i, L_COPY); + boxaAddBox(boxad, box, L_INSERT); + } + + return boxad; +} + + +/*---------------------------------------------------------------------* + * Boxa Permutation * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaPermutePseudorandom() + * + * \param[in] boxas input boxa + * \return boxad with boxes permuted, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a pseudorandom in-place permutation of the boxes.
+ *      (2) The result is guaranteed not to have any boxes in their
+ *          original position, but it is not very random.  If you
+ *          need randomness, use boxaPermuteRandom().
+ * 
+ */ +BOXA * +boxaPermutePseudorandom(BOXA *boxas) +{ +l_int32 n; +NUMA *na; +BOXA *boxad; + + PROCNAME("boxaPermutePseudorandom"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + + n = boxaGetCount(boxas); + na = numaPseudorandomSequence(n, 0); + boxad = boxaSortByIndex(boxas, na); + numaDestroy(&na); + return boxad; +} + + +/*! + * \brief boxaPermuteRandom() + * + * \param[in] boxad [optional] can be null or equal to boxas + * \param[in] boxas input boxa + * \return boxad with boxes permuted, or NULL on error + * + *
+ * Notes:
+ *      (1) If boxad is null, make a copy of boxas and permute the copy.
+ *          Otherwise, boxad must be equal to boxas, and the operation
+ *          is done in-place.
+ *      (2) If boxas is empty, return an empty boxad.
+ *      (3) This does a random in-place permutation of the boxes,
+ *          by swapping each box in turn with a random box.  The
+ *          result is almost guaranteed not to have any boxes in their
+ *          original position.
+ *      (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do
+ *          a proper permutation is the number of boxes exceeds this.
+ * 
+ */ +BOXA * +boxaPermuteRandom(BOXA *boxad, + BOXA *boxas) +{ +l_int32 i, n, index; + + PROCNAME("boxaPermuteRandom"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + if (boxad && (boxad != boxas)) + return (BOXA *)ERROR_PTR("boxad defined but in-place", procName, NULL); + + if (!boxad) + boxad = boxaCopy(boxas, L_COPY); + if ((n = boxaGetCount(boxad)) == 0) + return boxad; + index = (l_uint32)rand() % n; + index = L_MAX(1, index); + boxaSwapBoxes(boxad, 0, index); + for (i = 1; i < n; i++) { + index = (l_uint32)rand() % n; + if (index == i) index--; + boxaSwapBoxes(boxad, i, index); + } + + return boxad; +} + + +/*! + * \brief boxaSwapBoxes() + * + * \param[in] boxa + * \param[in] i, j two indices of boxes, that are to be swapped + * \return 0 if OK, 1 on error + */ +l_ok +boxaSwapBoxes(BOXA *boxa, + l_int32 i, + l_int32 j) +{ +l_int32 n; +BOX *box; + + PROCNAME("boxaSwapBoxes"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + n = boxaGetCount(boxa); + if (i < 0 || i >= n) + return ERROR_INT("i invalid", procName, 1); + if (j < 0 || j >= n) + return ERROR_INT("j invalid", procName, 1); + if (i == j) + return ERROR_INT("i == j", procName, 1); + + box = boxa->box[i]; + boxa->box[i] = boxa->box[j]; + boxa->box[j] = box; + return 0; +} + + +/*---------------------------------------------------------------------* + * Boxa and Box Conversions * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaConvertToPta() + * + * \param[in] boxa + * \param[in] ncorners 2 or 4 for the representation of each box + * \return pta with %ncorners points for each box in the boxa, + * or NULL on error + * + *
+ * Notes:
+ *      (1) If ncorners == 2, we select the UL and LR corners.
+ *          Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
+ *      (2) Other boxa --> pta functions are:
+ *          * boxaExtractAsPta(): allows extraction of any dimension
+ *            and/or side location, with each in a separate pta.
+ *          * boxaExtractCorners(): extracts any of the four corners as a pta.
+ * 
+ */ +PTA * +boxaConvertToPta(BOXA *boxa, + l_int32 ncorners) +{ +l_int32 i, n; +BOX *box; +PTA *pta, *pta1; + + PROCNAME("boxaConvertToPta"); + + if (!boxa) + return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); + if (ncorners != 2 && ncorners != 4) + return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); + + n = boxaGetCount(boxa); + if ((pta = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_COPY); + pta1 = boxConvertToPta(box, ncorners); + ptaJoin(pta, pta1, 0, -1); + boxDestroy(&box); + ptaDestroy(&pta1); + } + + return pta; +} + + +/*! + * \brief ptaConvertToBoxa() + * + * \param[in] pta + * \param[in] ncorners 2 or 4 for the representation of each box + * \return boxa with one box for each 2 or 4 points in the pta, + * or NULL on error + * + *
+ * Notes:
+ *      (1) For 2 corners, the order of the 2 points is UL, LR.
+ *          For 4 corners, the order of points is UL, UR, LL, LR.
+ *      (2) Each derived box is the minimum size containing all corners.
+ * 
+ */ +BOXA * +ptaConvertToBoxa(PTA *pta, + l_int32 ncorners) +{ +l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; +BOX *box; +BOXA *boxa; + + PROCNAME("ptaConvertToBoxa"); + + if (!pta) + return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); + if (ncorners != 2 && ncorners != 4) + return (BOXA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); + n = ptaGetCount(pta); + if (n % ncorners != 0) + return (BOXA *)ERROR_PTR("size % ncorners != 0", procName, NULL); + nbox = n / ncorners; + if ((boxa = boxaCreate(nbox)) == NULL) + return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); + for (i = 0; i < n; i += ncorners) { + ptaGetIPt(pta, i, &x1, &y1); + ptaGetIPt(pta, i + 1, &x2, &y2); + if (ncorners == 2) { + box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + boxaAddBox(boxa, box, L_INSERT); + continue; + } + ptaGetIPt(pta, i + 2, &x3, &y3); + ptaGetIPt(pta, i + 3, &x4, &y4); + x = L_MIN(x1, x3); + y = L_MIN(y1, y2); + xmax = L_MAX(x2, x4); + ymax = L_MAX(y3, y4); + box = boxCreate(x, y, xmax - x + 1, ymax - y + 1); + boxaAddBox(boxa, box, L_INSERT); + } + + return boxa; +} + + +/*! + * \brief boxConvertToPta() + * + * \param[in] box + * \param[in] ncorners 2 or 4 for the representation of the box + * \return pta with %ncorners points, or NULL on error + * + *
+ * Notes:
+ *      (1) If ncorners == 2, we select the UL and LR corners.
+ *          Otherwise we save all 4 corners in this order: UL, UR, LL, LR.
+ * 
+ */ +PTA * +boxConvertToPta(BOX *box, + l_int32 ncorners) +{ +l_int32 x, y, w, h; +PTA *pta; + + PROCNAME("boxConvertToPta"); + + if (!box) + return (PTA *)ERROR_PTR("box not defined", procName, NULL); + if (ncorners != 2 && ncorners != 4) + return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); + + if ((pta = ptaCreate(ncorners)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + boxGetGeometry(box, &x, &y, &w, &h); + ptaAddPt(pta, x, y); + if (ncorners == 2) { + ptaAddPt(pta, x + w - 1, y + h - 1); + } else { + ptaAddPt(pta, x + w - 1, y); + ptaAddPt(pta, x, y + h - 1); + ptaAddPt(pta, x + w - 1, y + h - 1); + } + + return pta; +} + + +/*! + * \brief ptaConvertToBox() + * + * \param[in] pta + * \return box minimum containing all points in the pta, or NULL on error + * + *
+ * Notes:
+ *      (1) For 2 corners, the order of the 2 points is UL, LR.
+ *          For 4 corners, the order of points is UL, UR, LL, LR.
+ * 
+ */ +BOX * +ptaConvertToBox(PTA *pta) +{ +l_int32 n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; + + PROCNAME("ptaConvertToBox"); + + if (!pta) + return (BOX *)ERROR_PTR("pta not defined", procName, NULL); + n = ptaGetCount(pta); + ptaGetIPt(pta, 0, &x1, &y1); + ptaGetIPt(pta, 1, &x2, &y2); + if (n == 2) + return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + + /* 4 corners */ + ptaGetIPt(pta, 2, &x3, &y3); + ptaGetIPt(pta, 3, &x4, &y4); + x = L_MIN(x1, x3); + y = L_MIN(y1, y2); + xmax = L_MAX(x2, x4); + ymax = L_MAX(y3, y4); + return boxCreate(x, y, xmax - x + 1, ymax - y + 1); +} + + +/*---------------------------------------------------------------------* + * Miscellaneous Boxa functions * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaGetExtent() + * + * \param[in] boxa + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbox [optional] minimum box containing all boxes in boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes the minimum rectangular bounding region
+ *          that contains all valid boxes in a boxa.
+ *      (2) The returned w and h are the minimum size image
+ *          that would contain all boxes untranslated.
+ *      (3) If there are no valid boxes, returned w and h are 0 and
+ *          all parameters in the returned box are 0.  This
+ *          is not an error, because an empty boxa is valid and
+ *          boxaGetExtent() is required for serialization.
+ * 
+ */ +l_ok +boxaGetExtent(BOXA *boxa, + l_int32 *pw, + l_int32 *ph, + BOX **pbox) +{ +l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found; + + PROCNAME("boxaGetExtent"); + + if (!pw && !ph && !pbox) + return ERROR_INT("no ptrs defined", procName, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbox) *pbox = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + xmax = ymax = 0; + xmin = ymin = 100000000; + found = FALSE; + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + if (w <= 0 || h <= 0) + continue; + found = TRUE; + xmin = L_MIN(xmin, x); + ymin = L_MIN(ymin, y); + xmax = L_MAX(xmax, x + w); + ymax = L_MAX(ymax, y + h); + } + if (found == FALSE) /* no valid boxes in boxa */ + xmin = ymin = 0; + if (pw) *pw = xmax; + if (ph) *ph = ymax; + if (pbox) + *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); + + return 0; +} + + +/*! + * \brief boxaGetCoverage() + * + * \param[in] boxa + * \param[in] wc, hc dimensions of overall clipping rectangle with UL + * corner at (0, 0 that is covered by the boxes. + * \param[in] exactflag 1 for guaranteeing an exact result; 0 for getting + * an exact result only if the boxes do not overlap + * \param[out] pfract sum of box area as fraction of w * h + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The boxes in boxa are clipped to the input rectangle.
+ *      (2) * When %exactflag == 1, we generate a 1 bpp pix of size
+ *            wc x hc, paint all the boxes black, and count the fg pixels.
+ *            This can take 1 msec on a large page with many boxes.
+ *          * When %exactflag == 0, we clip each box to the wc x hc region
+ *            and sum the resulting areas.  This is faster.
+ *          * The results are the same when none of the boxes overlap
+ *            within the wc x hc region.
+ * 
+ */ +l_ok +boxaGetCoverage(BOXA *boxa, + l_int32 wc, + l_int32 hc, + l_int32 exactflag, + l_float32 *pfract) +{ +l_int32 i, n, x, y, w, h, sum; +BOX *box, *boxc; +PIX *pixt; + + PROCNAME("boxaGetCoverage"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + if (n == 0) + return ERROR_INT("no boxes in boxa", procName, 1); + + if (exactflag == 0) { /* quick and dirty */ + sum = 0; + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) { + boxGetGeometry(boxc, NULL, NULL, &w, &h); + sum += w * h; + boxDestroy(&boxc); + } + boxDestroy(&box); + } + } else { /* slower and exact */ + pixt = pixCreate(wc, hc, 1); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0); + boxDestroy(&box); + } + pixCountPixels(pixt, &sum, NULL); + pixDestroy(&pixt); + } + + *pfract = (l_float32)sum / (l_float32)(wc * hc); + return 0; +} + + +/*! + * \brief boxaaSizeRange() + * + * \param[in] baa + * \param[out] pminw [optional] min width of all boxes + * \param[out] pmaxw [optional] max width of all boxes + * \param[out] pminh [optional] min height of all boxes + * \param[out] pmaxh [optional] max height of all boxes + * \return 0 if OK, 1 on error + */ +l_ok +boxaaSizeRange(BOXAA *baa, + l_int32 *pminw, + l_int32 *pminh, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ +l_int32 minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n; +BOXA *boxa; + + PROCNAME("boxaaSizeRange"); + + if (!pminw && !pmaxw && !pminh && !pmaxh) + return ERROR_INT("no data can be returned", procName, 1); + if (pminw) *pminw = 0; + if (pminh) *pminh = 0; + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!baa) + return ERROR_INT("baa not defined", procName, 1); + + minw = minh = 100000000; + maxw = maxh = 0; + n = boxaaGetCount(baa); + for (i = 0; i < n; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh); + if (minbw < minw) + minw = minbw; + if (minbh < minh) + minh = minbh; + if (maxbw > maxw) + maxw = maxbw; + if (maxbh > maxh) + maxh = maxbh; + boxaDestroy(&boxa); + } + + if (pminw) *pminw = minw; + if (pminh) *pminh = minh; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + return 0; +} + + +/*! + * \brief boxaSizeRange() + * + * \param[in] boxa + * \param[out] pminw [optional] min width of all boxes + * \param[out] pmaxw [optional] max width of all boxes + * \param[out] pminh [optional] min height of all boxes + * \param[out] pmaxh [optional] max height of all boxes + * \return 0 if OK, 1 on error + */ +l_ok +boxaSizeRange(BOXA *boxa, + l_int32 *pminw, + l_int32 *pminh, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ +l_int32 minw, minh, maxw, maxh, i, n, w, h; + + PROCNAME("boxaSizeRange"); + + if (!pminw && !pmaxw && !pminh && !pmaxh) + return ERROR_INT("no data can be returned", procName, 1); + if (pminw) *pminw = 0; + if (pminh) *pminh = 0; + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + minw = minh = 100000000; + maxw = maxh = 0; + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + if (w < minw) + minw = w; + if (h < minh) + minh = h; + if (w > maxw) + maxw = w; + if (h > maxh) + maxh = h; + } + + if (pminw) *pminw = minw; + if (pminh) *pminh = minh; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + return 0; +} + + +/*! + * \brief boxaLocationRange() + * + * \param[in] boxa + * \param[out] pminx [optional] min (UL corner) x value of all boxes + * \param[out] pminy [optional] min (UL corner) y value of all boxes + * \param[out] pmaxx [optional] max (UL corner) x value of all boxes + * \param[out] pmaxy [optional] max (UL corner) y value of all boxes + * \return 0 if OK, 1 on error + */ +l_ok +boxaLocationRange(BOXA *boxa, + l_int32 *pminx, + l_int32 *pminy, + l_int32 *pmaxx, + l_int32 *pmaxy) +{ +l_int32 minx, miny, maxx, maxy, i, n, x, y; + + PROCNAME("boxaLocationRange"); + + if (!pminx && !pminy && !pmaxx && !pmaxy) + return ERROR_INT("no data can be returned", procName, 1); + if (pminx) *pminx = 0; + if (pminy) *pminy = 0; + if (pmaxx) *pmaxx = 0; + if (pmaxy) *pmaxy = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + minx = miny = 100000000; + maxx = maxy = 0; + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); + if (x < minx) + minx = x; + if (y < miny) + miny = y; + if (x > maxx) + maxx = x; + if (y > maxy) + maxy = y; + } + + if (pminx) *pminx = minx; + if (pminy) *pminy = miny; + if (pmaxx) *pmaxx = maxx; + if (pmaxy) *pmaxy = maxy; + + return 0; +} + + +/*! + * \brief boxaGetSizes() + * + * \param[in] boxa + * \param[out] pnaw [optional] widths of valid boxes + * \param[out] pnah [optional] heights of valid boxes + * \return 0 if OK, 1 on error + */ +l_ok +boxaGetSizes(BOXA *boxa, + NUMA **pnaw, + NUMA **pnah) +{ +l_int32 i, n, w, h; +BOX *box; + + PROCNAME("boxaGetSizes"); + + if (pnaw) *pnaw = NULL; + if (pnah) *pnah = NULL; + if (!pnaw && !pnah) + return ERROR_INT("no output requested", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetValidCount(boxa); + if (pnaw) *pnaw = numaCreate(n); + if (pnah) *pnah = numaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetValidBox(boxa, i, L_COPY); + if (box) { + boxGetGeometry(box, NULL, NULL, &w, &h); + if (pnaw) numaAddNumber(*pnaw, w); + if (pnah) numaAddNumber(*pnah, h); + boxDestroy(&box); + } + } + + return 0; +} + + +/*! + * \brief boxaGetArea() + * + * \param[in] boxa + * \param[out] parea total area of all boxes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Measures the total area of the boxes, without regard to overlaps.
+ * 
+ */ +l_ok +boxaGetArea(BOXA *boxa, + l_int32 *parea) +{ +l_int32 i, n, w, h; + + PROCNAME("boxaGetArea"); + + if (!parea) + return ERROR_INT("&area not defined", procName, 1); + *parea = 0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); + *parea += w * h; + } + return 0; +} + + +/*! + * \brief boxaDisplayTiled() + * + * \param[in] boxas + * \param[in] pixa [optional] background for each box + * \param[in] first index of first box + * \param[in] last index of last box; use -1 to go to end + * \param[in] maxwidth of output image + * \param[in] linewidth width of box outlines, before scaling + * \param[in] scalefactor applied to every box; use 1.0 for no scaling + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \return pixd of tiled images of boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) Displays each box separately in a tiled 32 bpp image.
+ *      (2) If pixa is defined, it must have the same count as the boxa,
+ *          and it will be a background over with each box is rendered.
+ *          If pixa is not defined, the boxes will be rendered over
+ *          blank images of identical size.
+ *      (3) See pixaDisplayTiledInRows() for other parameters.
+ * 
+ */ +PIX * +boxaDisplayTiled(BOXA *boxas, + PIXA *pixa, + l_int32 first, + l_int32 last, + l_int32 maxwidth, + l_int32 linewidth, + l_float32 scalefactor, + l_int32 background, + l_int32 spacing, + l_int32 border) +{ +char buf[32]; +l_int32 i, n, npix, w, h, fontsize; +L_BMF *bmf; +BOX *box; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixat; + + PROCNAME("boxaDisplayTiled"); + + if (!boxas) + return (PIX *)ERROR_PTR("boxas not defined", procName, NULL); + + boxa = boxaSaveValid(boxas, L_COPY); + n = boxaGetCount(boxa); + if (pixa) { + npix = pixaGetCount(pixa); + if (n != npix) { + boxaDestroy(&boxa); + return (PIX *)ERROR_PTR("boxa and pixa counts differ", + procName, NULL); + } + } + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) { + boxaDestroy(&boxa); + return (PIX *)ERROR_PTR("invalid first", procName, NULL); + } + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) { + boxaDestroy(&boxa); + return (PIX *)ERROR_PTR("first > last", procName, NULL); + } + + /* Because the bitmap font will be reduced when tiled, choose the + * font size inversely with the scale factor. */ + if (scalefactor > 0.8) + fontsize = 6; + else if (scalefactor > 0.6) + fontsize = 10; + else if (scalefactor > 0.4) + fontsize = 14; + else if (scalefactor > 0.3) + fontsize = 18; + else fontsize = 20; + bmf = bmfCreate(NULL, fontsize); + + pixat = pixaCreate(n); + boxaGetExtent(boxa, &w, &h, NULL); + for (i = first; i <= last; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + if (!pixa) { + pix1 = pixCreate(w, h, 32); + pixSetAll(pix1); + } else { + pix1 = pixaGetPix(pixa, i, L_COPY); + } + pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00); + snprintf(buf, sizeof(buf), "%d", i); + pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000, + L_ADD_BELOW, NULL); + pixDestroy(&pix1); + pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0); + pixaAddPix(pixat, pix2, L_INSERT); + boxDestroy(&box); + } + bmfDestroy(&bmf); + boxaDestroy(&boxa); + + pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background, + spacing, border); + pixaDestroy(&pixat); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/boxfunc5.c b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc5.c new file mode 100644 index 0000000..0e79e06 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/boxfunc5.c @@ -0,0 +1,2192 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file boxfunc5.c + *
+ *
+ *      Boxa sequence fitting
+ *           BOXA     *boxaSmoothSequenceLS()
+ *           BOXA     *boxaSmoothSequenceMedian()
+ *           BOXA     *boxaLinearFit()
+ *           BOXA     *boxaWindowedMedian()
+ *           BOXA     *boxaModifyWithBoxa()
+ *           BOXA     *boxaConstrainSize()
+ *           BOXA     *boxaReconcileEvenOddHeight()
+ *    static l_int32   boxaTestEvenOddHeight()
+ *           BOXA     *boxaReconcilePairWidth()
+ *           l_int32   boxaSizeConsistency1()
+ *           l_int32   boxaSizeConsistency2()
+ *           BOXA     *boxaReconcileSizeByMedian()
+ *           l_int32   boxaPlotSides()   [for debugging]
+ *           l_int32   boxaPlotSizes()   [for debugging]
+ *           BOXA     *boxaFillSequence()
+ *    static l_int32   boxaFillAll()
+ *           l_int32   boxaSizeVariation()
+ *           l_int32   boxaMedianDimensions()
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_int32 boxaTestEvenOddHeight(BOXA *boxa1, BOXA *boxa2, l_int32 start, + l_float32 *pdel1, l_float32 *pdel2); +static l_int32 boxaFillAll(BOXA *boxa); + + +/*---------------------------------------------------------------------* + * Boxa sequence fitting * + *---------------------------------------------------------------------*/ +/*! + * \brief boxaSmoothSequenceLS() + * + * \param[in] boxas source boxa + * \param[in] factor reject outliers with widths and heights deviating + * from the median by more than %factor times + * the median variation from the median; typically ~3 + * \param[in] subflag L_USE_MINSIZE, L_USE_MAXSIZE, + * L_SUB_ON_LOC_DIFF, L_SUB_ON_SIZE_DIFF, + * L_USE_CAPPED_MIN, L_USE_CAPPED_MAX + * \param[in] maxdiff parameter used with L_SUB_ON_LOC_DIFF, + * L_SUB_ON_SIZE_DIFF, L_USE_CAPPED_MIN, + * L_USE_CAPPED_MAX + * \param[in] extrapixels pixels added on all sides (or subtracted + * if %extrapixels < 0) when using + * L_SUB_ON_LOC_DIFF and L_SUB_ON_SIZE_DIFF + * \param[in] debug 1 for debug output + * \return boxad fitted boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a modified version of %boxas by constructing
+ *          for each input box a box that has been linear least square fit
+ *          (LSF) to the entire set.  The linear fitting is done to each of
+ *          the box sides independently, after outliers are rejected,
+ *          and it is computed separately for sequences of even and
+ *          odd boxes.  Once the linear LSF box is found, the output box
+ *          (in %boxad) is constructed from the input box and the LSF
+ *          box, depending on %subflag.  See boxaModifyWithBoxa() for
+ *          details on the use of %subflag and %maxdiff.
+ *      (2) This is useful if, in both the even and odd sets, the box
+ *          edges vary roughly linearly with its index in the set.
+ * 
+ */ +BOXA * +boxaSmoothSequenceLS(BOXA *boxas, + l_float32 factor, + l_int32 subflag, + l_int32 maxdiff, + l_int32 extrapixels, + l_int32 debug) +{ +l_int32 n; +BOXA *boxae, *boxao, *boxalfe, *boxalfo, *boxame, *boxamo, *boxad; + + PROCNAME("boxaSmoothSequenceLS"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (factor <= 0.0) { + L_WARNING("factor must be > 0.0; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (maxdiff < 0) { + L_WARNING("maxdiff must be >= 0; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && + subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF && + subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) { + L_WARNING("invalid subflag; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if ((n = boxaGetCount(boxas)) < 4) { + L_WARNING("need at least 4 boxes; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + + boxaSplitEvenOdd(boxas, 1, &boxae, &boxao); + if (debug) { + lept_mkdir("lept/smooth"); + boxaWriteDebug("/tmp/lept/smooth/boxae.ba", boxae); + boxaWriteDebug("/tmp/lept/smooth/boxao.ba", boxao); + } + + boxalfe = boxaLinearFit(boxae, factor, debug); + boxalfo = boxaLinearFit(boxao, factor, debug); + if (debug) { + boxaWriteDebug("/tmp/lept/smooth/boxalfe.ba", boxalfe); + boxaWriteDebug("/tmp/lept/smooth/boxalfo.ba", boxalfo); + } + + boxame = boxaModifyWithBoxa(boxae, boxalfe, subflag, maxdiff, extrapixels); + boxamo = boxaModifyWithBoxa(boxao, boxalfo, subflag, maxdiff, extrapixels); + if (debug) { + boxaWriteDebug("/tmp/lept/smooth/boxame.ba", boxame); + boxaWriteDebug("/tmp/lept/smooth/boxamo.ba", boxamo); + } + + boxad = boxaMergeEvenOdd(boxame, boxamo, 1); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + boxaDestroy(&boxalfe); + boxaDestroy(&boxalfo); + boxaDestroy(&boxame); + boxaDestroy(&boxamo); + return boxad; +} + + +/*! + * \brief boxaSmoothSequenceMedian() + * + * \param[in] boxas source boxa + * \param[in] halfwin half-width of sliding window; used to find median + * \param[in] subflag L_USE_MINSIZE, L_USE_MAXSIZE, + * L_SUB_ON_LOC_DIFF, L_SUB_ON_SIZE_DIFF, + * L_USE_CAPPED_MIN, L_USE_CAPPED_MAX + * \param[in] maxdiff parameter used with L_SUB_ON_LOC_DIFF, + * L_SUB_ON_SIZE_DIFF, L_USE_CAPPED_MIN, + * L_USE_CAPPED_MAX + * \param[in] extrapixels pixels added on all sides (or subtracted + * if %extrapixels < 0) when using + * L_SUB_ON_LOC_DIFF and L_SUB_ON_SIZE_DIFF + * \param[in] debug 1 for debug output + * \return boxad fitted boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) The target width of the sliding window is 2 * %halfwin + 1.
+ *          If necessary, this will be reduced by boxaWindowedMedian().
+ *      (2) This returns a modified version of %boxas by constructing
+ *          for each input box a box that has been smoothed with windowed
+ *          median filtering.  The filtering is done to each of the
+ *          box sides independently, and it is computed separately for
+ *          sequences of even and odd boxes.  The output %boxad is
+ *          constructed from the input boxa and the filtered boxa,
+ *          depending on %subflag.  See boxaModifyWithBoxa() for
+ *          details on the use of %subflag, %maxdiff and %extrapixels.
+ *      (3) This is useful for removing noise separately in the even
+ *          and odd sets, where the box edge locations can have
+ *          discontinuities but otherwise vary roughly linearly within
+ *          intervals of size %halfwin or larger.
+ *      (4) If you don't need to handle even and odd sets separately,
+ *          just do this:
+ *              boxam = boxaWindowedMedian(boxas, halfwin, debug);
+ *              boxad = boxaModifyWithBoxa(boxas, boxam, subflag, maxdiff,
+ *                                         extrapixels);
+ *              boxaDestroy(&boxam);
+ * 
+ */ +BOXA * +boxaSmoothSequenceMedian(BOXA *boxas, + l_int32 halfwin, + l_int32 subflag, + l_int32 maxdiff, + l_int32 extrapixels, + l_int32 debug) +{ +l_int32 n; +BOXA *boxae, *boxao, *boxamede, *boxamedo, *boxame, *boxamo, *boxad; + + PROCNAME("boxaSmoothSequenceMedian"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (halfwin <= 0) { + L_WARNING("halfwin must be > 0; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (maxdiff < 0) { + L_WARNING("maxdiff must be >= 0; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && + subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF && + subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) { + L_WARNING("invalid subflag; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if ((n = boxaGetCount(boxas)) < 6) { + L_WARNING("need at least 6 boxes; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + + boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); + if (debug) { + lept_mkdir("lept/smooth"); + boxaWriteDebug("/tmp/lept/smooth/boxae.ba", boxae); + boxaWriteDebug("/tmp/lept/smooth/boxao.ba", boxao); + } + + boxamede = boxaWindowedMedian(boxae, halfwin, debug); + boxamedo = boxaWindowedMedian(boxao, halfwin, debug); + if (debug) { + boxaWriteDebug("/tmp/lept/smooth/boxamede.ba", boxamede); + boxaWriteDebug("/tmp/lept/smooth/boxamedo.ba", boxamedo); + } + + boxame = boxaModifyWithBoxa(boxae, boxamede, subflag, maxdiff, extrapixels); + boxamo = boxaModifyWithBoxa(boxao, boxamedo, subflag, maxdiff, extrapixels); + if (debug) { + boxaWriteDebug("/tmp/lept/smooth/boxame.ba", boxame); + boxaWriteDebug("/tmp/lept/smooth/boxamo.ba", boxamo); + } + + boxad = boxaMergeEvenOdd(boxame, boxamo, 0); + if (debug) { + boxaPlotSides(boxas, NULL, NULL, NULL, NULL, NULL, NULL); + boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL); + boxaPlotSizes(boxas, NULL, NULL, NULL, NULL); + boxaPlotSizes(boxad, NULL, NULL, NULL, NULL); + } + + boxaDestroy(&boxae); + boxaDestroy(&boxao); + boxaDestroy(&boxamede); + boxaDestroy(&boxamedo); + boxaDestroy(&boxame); + boxaDestroy(&boxamo); + return boxad; +} + + +/*! + * \brief boxaLinearFit() + * + * \param[in] boxas source boxa + * \param[in] factor reject outliers with widths and heights deviating + * from the median by more than %factor times + * the median deviation from the median; typically ~3 + * \param[in] debug 1 for debug output + * \return boxad fitted boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds a set of boxes (boxad) where each edge of each box is
+ *          a linear least square fit (LSF) to the edges of the
+ *          input set of boxes (boxas).  Before fitting, outliers in
+ *          the boxes in boxas are removed (see below).
+ *      (2) This is useful when each of the box edges in boxas are expected
+ *          to vary linearly with box index in the set.  These could
+ *          be, for example, noisy measurements of similar regions
+ *          on successive scanned pages.
+ *      (3) Method: there are 2 steps:
+ *          (a) Find and remove outliers, separately based on the deviation
+ *              from the median of the width and height of the box.
+ *              Use %factor to specify tolerance to outliers; use a very
+ *              large value of %factor to avoid rejecting any box sides
+ *              in the linear LSF.
+ *          (b) On the remaining boxes, do a linear LSF independently
+ *              for each of the four sides.
+ *      (4) Invalid input boxes are not used in computation of the LSF.
+ *      (5) The returned boxad can then be used in boxaModifyWithBoxa()
+ *          to selectively change the boxes in boxas.
+ * 
+ */ +BOXA * +boxaLinearFit(BOXA *boxas, + l_float32 factor, + l_int32 debug) +{ +l_int32 n, i, w, h, lval, tval, rval, bval, rejectlr, rejecttb; +l_float32 al, bl, at, bt, ar, br, ab, bb; /* LSF coefficients */ +l_float32 medw, medh, medvarw, medvarh; +BOX *box, *boxempty; +BOXA *boxalr, *boxatb, *boxad; +NUMA *naw, *nah; +PTA *ptal, *ptat, *ptar, *ptab; + + PROCNAME("boxaLinearFit"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((n = boxaGetCount(boxas)) < 2) + return (BOXA *)ERROR_PTR("need at least 2 boxes", procName, NULL); + + /* Remove outliers based on width and height. + * First find the median width and the median deviation from + * the median width. Ditto for the height. */ + boxaExtractAsNuma(boxas, NULL, NULL, NULL, NULL, &naw, &nah, 0); + numaGetMedianDevFromMedian(naw, &medw, &medvarw); + numaGetMedianDevFromMedian(nah, &medh, &medvarh); + numaDestroy(&naw); + numaDestroy(&nah); + + if (debug) { + fprintf(stderr, "medw = %7.3f, medvarw = %7.3f\n", medw, medvarw); + fprintf(stderr, "medh = %7.3f, medvarh = %7.3f\n", medh, medvarh); + } + + /* To fit the left and right sides, only use boxes whose + * width is within (factor * medvarw) of the median width. + * Ditto for the top and bottom sides. Add empty boxes + * in as placeholders so that the index remains the same + * as in boxas. */ + boxalr = boxaCreate(n); + boxatb = boxaCreate(n); + boxempty = boxCreate(0, 0, 0, 0); /* placeholders */ + rejectlr = rejecttb = 0; + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) { + boxaAddBox(boxalr, boxempty, L_COPY); + boxaAddBox(boxatb, boxempty, L_COPY); + continue; + } + boxGetGeometry(box, NULL, NULL, &w, &h); + if (L_ABS(w - medw) <= factor * medvarw) { + boxaAddBox(boxalr, box, L_COPY); + } else { + rejectlr++; + boxaAddBox(boxalr, boxempty, L_COPY); + } + if (L_ABS(h - medh) <= factor * medvarh) { + boxaAddBox(boxatb, box, L_COPY); + } else { + rejecttb++; + boxaAddBox(boxatb, boxempty, L_COPY); + } + boxDestroy(&box); + } + boxDestroy(&boxempty); + if (boxaGetCount(boxalr) < 2 || boxaGetCount(boxatb) < 2) { + boxaDestroy(&boxalr); + boxaDestroy(&boxatb); + return (BOXA *)ERROR_PTR("need at least 2 valid boxes", procName, NULL); + } + + if (debug) { + L_INFO("# lr reject = %d, # tb reject = %d\n", procName, + rejectlr, rejecttb); + lept_mkdir("linfit"); + boxaWriteDebug("/tmp/linfit/boxalr.ba", boxalr); + boxaWriteDebug("/tmp/linfit/boxatb.ba", boxatb); + } + + /* Extract the valid left and right box sides, along with the box + * index, from boxalr. This only extracts pts corresponding to + * valid boxes. Ditto: top and bottom sides from boxatb. */ + boxaExtractAsPta(boxalr, &ptal, NULL, &ptar, NULL, NULL, NULL, 0); + boxaExtractAsPta(boxatb, NULL, &ptat, NULL, &ptab, NULL, NULL, 0); + boxaDestroy(&boxalr); + boxaDestroy(&boxatb); + + if (debug) { + ptaWriteDebug("/tmp/linfit/ptal.pta", ptal, 1); + ptaWriteDebug("/tmp/linfit/ptar.pta", ptar, 1); + ptaWriteDebug("/tmp/linfit/ptat.pta", ptat, 1); + ptaWriteDebug("/tmp/linfit/ptab.pta", ptab, 1); + } + + /* Do a linear LSF fit to the points that are width and height + * validated. Because we've eliminated the outliers, there is no + * need to use ptaNoisyLinearLSF(ptal, factor, NULL, &al, &bl, ...) */ + ptaGetLinearLSF(ptal, &al, &bl, NULL); + ptaGetLinearLSF(ptat, &at, &bt, NULL); + ptaGetLinearLSF(ptar, &ar, &br, NULL); + ptaGetLinearLSF(ptab, &ab, &bb, NULL); + + /* Return the LSF smoothed values, interleaved with invalid + * boxes when the corresponding box in boxas is invalid. */ + boxad = boxaCreate(n); + boxempty = boxCreate(0, 0, 0, 0); /* use for placeholders */ + for (i = 0; i < n; i++) { + lval = (l_int32)(al * i + bl + 0.5); + tval = (l_int32)(at * i + bt + 0.5); + rval = (l_int32)(ar * i + br + 0.5); + bval = (l_int32)(ab * i + bb + 0.5); + if ((box = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) { + boxaAddBox(boxad, boxempty, L_COPY); + } else { + boxDestroy(&box); + box = boxCreate(lval, tval, rval - lval + 1, bval - tval + 1); + boxaAddBox(boxad, box, L_INSERT); + } + } + boxDestroy(&boxempty); + + if (debug) { + boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL); + boxaPlotSizes(boxad, NULL, NULL, NULL, NULL); + } + + ptaDestroy(&ptal); + ptaDestroy(&ptat); + ptaDestroy(&ptar); + ptaDestroy(&ptab); + return boxad; +} + + +/*! + * \brief boxaWindowedMedian() + * + * \param[in] boxas source boxa + * \param[in] halfwin half width of window over which the median is found + * \param[in] debug 1 for debug output + * \return boxad smoothed boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds a set of boxes (boxad) where each edge of each box is
+ *          a windowed median smoothed value to the edges of the
+ *          input set of boxes (boxas).
+ *      (2) Invalid input boxes are filled from nearby ones.
+ *      (3) The returned boxad can then be used in boxaModifyWithBoxa()
+ *          to selectively change the boxes in the source boxa.
+ * 
+ */ +BOXA * +boxaWindowedMedian(BOXA *boxas, + l_int32 halfwin, + l_int32 debug) +{ +l_int32 n, i, left, top, right, bot; +BOX *box; +BOXA *boxaf, *boxad; +NUMA *nal, *nat, *nar, *nab, *naml, *namt, *namr, *namb; + + PROCNAME("boxaWindowedMedian"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if ((n = boxaGetCount(boxas)) < 3) { + L_WARNING("less than 3 boxes; returning a copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (halfwin <= 0) { + L_WARNING("halfwin must be > 0; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + + /* Fill invalid boxes in the input sequence */ + if ((boxaf = boxaFillSequence(boxas, L_USE_ALL_BOXES, debug)) == NULL) + return (BOXA *)ERROR_PTR("filled boxa not made", procName, NULL); + + /* Get the windowed median output from each of the sides */ + boxaExtractAsNuma(boxaf, &nal, &nat, &nar, &nab, NULL, NULL, 0); + naml = numaWindowedMedian(nal, halfwin); + namt = numaWindowedMedian(nat, halfwin); + namr = numaWindowedMedian(nar, halfwin); + namb = numaWindowedMedian(nab, halfwin); + + n = boxaGetCount(boxaf); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naml, i, &left); + numaGetIValue(namt, i, &top); + numaGetIValue(namr, i, &right); + numaGetIValue(namb, i, &bot); + box = boxCreate(left, top, right - left + 1, bot - top + 1); + boxaAddBox(boxad, box, L_INSERT); + } + + if (debug) { + boxaPlotSides(boxaf, NULL, NULL, NULL, NULL, NULL, NULL); + boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL); + boxaPlotSizes(boxaf, NULL, NULL, NULL, NULL); + boxaPlotSizes(boxad, NULL, NULL, NULL, NULL); + } + + boxaDestroy(&boxaf); + numaDestroy(&nal); + numaDestroy(&nat); + numaDestroy(&nar); + numaDestroy(&nab); + numaDestroy(&naml); + numaDestroy(&namt); + numaDestroy(&namr); + numaDestroy(&namb); + return boxad; +} + + +/*! + * \brief boxaModifyWithBoxa() + * + * \param[in] boxas + * \param[in] boxam boxa with boxes used to modify those in boxas + * \param[in] subflag L_USE_MINSIZE, L_USE_MAXSIZE, + * L_SUB_ON_LOC_DIFF, L_SUB_ON_SIZE_DIFF, + * L_USE_CAPPED_MIN, L_USE_CAPPED_MAX + * \param[in] maxdiff parameter used with L_SUB_ON_LOC_DIFF, + * L_SUB_ON_SIZE_DIFF, L_USE_CAPPED_MIN, + * L_USE_CAPPED_MAX + * \param[in] extrapixels pixels added on all sides (or subtracted + * if %extrapixels < 0) when using + * L_SUB_ON_LOC_DIFF and L_SUB_ON_SIZE_DIFF + * \return boxad result after adjusting boxes in boxas, or NULL on error. + * + *
+ * Notes:
+ *      (1) This takes two input boxa (boxas, boxam) and constructs boxad,
+ *          where each box in boxad is generated from the corresponding
+ *          boxes in boxas and boxam.  The rule for constructing each
+ *          output box depends on %subflag and %maxdiff.  Let boxs be
+ *          a box from %boxas and boxm be a box from %boxam.
+ *          * If %subflag == L_USE_MINSIZE: the output box is the intersection
+ *            of the two input boxes.
+ *          * If %subflag == L_USE_MAXSIZE: the output box is the union of the
+ *            two input boxes; i.e., the minimum bounding rectangle for the
+ *            two input boxes.
+ *          * If %subflag == L_SUB_ON_LOC_DIFF: each side of the output box
+ *            is found separately from the corresponding side of boxs and boxm.
+ *            Use the boxm side, expanded by %extrapixels, if greater than
+ *            %maxdiff pixels from the boxs side.
+ *          * If %subflag == L_SUB_ON_SIZE_DIFF: the sides of the output box
+ *            are determined in pairs from the width and height of boxs
+ *            and boxm.  If the boxm width differs by more than %maxdiff
+ *            pixels from boxs, use the boxm left and right sides,
+ *            expanded by %extrapixels.  Ditto for the height difference.
+ *          For the last two flags, each side of the output box is found
+ *          separately from the corresponding side of boxs and boxm,
+ *          according to these rules, where "smaller"("bigger") mean in a
+ *          direction that decreases(increases) the size of the output box:
+ *          * If %subflag == L_USE_CAPPED_MIN: use the Min of boxm
+ *            with the Max of (boxs, boxm +- %maxdiff), where the sign
+ *            is adjusted to make the box smaller (e.g., use "+" on left side).
+ *          * If %subflag == L_USE_CAPPED_MAX: use the Max of boxm
+ *            with the Min of (boxs, boxm +- %maxdiff), where the sign
+ *            is adjusted to make the box bigger (e.g., use "-" on left side).
+ *          Use of the last 2 flags is further explained in (3) and (4).
+ *      (2) boxas and boxam must be the same size.  If boxam == NULL,
+ *          this returns a copy of boxas with a warning.
+ *      (3) If %subflag == L_SUB_ON_LOC_DIFF, use boxm for each side
+ *          where the corresponding sides differ by more than %maxdiff.
+ *          Two extreme cases:
+ *          (a) set %maxdiff == 0 to use only values from boxam in boxad.
+ *          (b) set %maxdiff == 10000 to ignore all values from boxam;
+ *              then boxad will be the same as boxas.
+ *      (4) If %subflag == L_USE_CAPPED_MAX: use boxm if boxs is smaller;
+ *          use boxs if boxs is bigger than boxm by an amount up to %maxdiff;
+ *          and use boxm +- %maxdiff (the 'capped' value) if boxs is
+ *          bigger than boxm by an amount larger than %maxdiff.
+ *          Similarly, with interchange of Min/Max and sign of %maxdiff,
+ *          for %subflag == L_USE_CAPPED_MIN.
+ *      (5) If either of corresponding boxes in boxas and boxam is invalid,
+ *          an invalid box is copied to the result.
+ *      (6) Typical input for boxam may be the output of boxaLinearFit().
+ *          where outliers have been removed and each side is LS fit to a line.
+ *      (7) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(),
+ *          this uses two boxes and does not specify target dimensions.
+ *          Additional constraints on the size of each box can be enforced
+ *          by following this operation with boxaConstrainSize(), taking
+ *          boxad as input.
+ * 
+ */ +BOXA * +boxaModifyWithBoxa(BOXA *boxas, + BOXA *boxam, + l_int32 subflag, + l_int32 maxdiff, + l_int32 extrapixels) +{ +l_int32 n, i, ls, ts, rs, bs, ws, hs, lm, tm, rm, bm, wm, hm, ld, td, rd, bd; +BOX *boxs, *boxm, *boxd, *boxempty; +BOXA *boxad; + + PROCNAME("boxaModifyWithBoxa"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (!boxam) { + L_WARNING("boxam not defined; returning copy", procName); + return boxaCopy(boxas, L_COPY); + } + if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && + subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF && + subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) { + L_WARNING("invalid subflag; returning copy", procName); + return boxaCopy(boxas, L_COPY); + } + n = boxaGetCount(boxas); + if (n != boxaGetCount(boxam)) { + L_WARNING("boxas and boxam sizes differ; returning copy", procName); + return boxaCopy(boxas, L_COPY); + } + + boxad = boxaCreate(n); + boxempty = boxCreate(0, 0, 0, 0); /* placeholders */ + for (i = 0; i < n; i++) { + boxs = boxaGetValidBox(boxas, i, L_CLONE); + boxm = boxaGetValidBox(boxam, i, L_CLONE); + if (!boxs || !boxm) { + boxaAddBox(boxad, boxempty, L_COPY); + } else { + boxGetGeometry(boxs, &ls, &ts, &ws, &hs); + boxGetGeometry(boxm, &lm, &tm, &wm, &hm); + rs = ls + ws - 1; + bs = ts + hs - 1; + rm = lm + wm - 1; + bm = tm + hm - 1; + if (subflag == L_USE_MINSIZE) { + ld = L_MAX(ls, lm); + rd = L_MIN(rs, rm); + td = L_MAX(ts, tm); + bd = L_MIN(bs, bm); + } else if (subflag == L_USE_MAXSIZE) { + ld = L_MIN(ls, lm); + rd = L_MAX(rs, rm); + td = L_MIN(ts, tm); + bd = L_MAX(bs, bm); + } else if (subflag == L_SUB_ON_LOC_DIFF) { + ld = (L_ABS(lm - ls) <= maxdiff) ? ls : lm - extrapixels; + td = (L_ABS(tm - ts) <= maxdiff) ? ts : tm - extrapixels; + rd = (L_ABS(rm - rs) <= maxdiff) ? rs : rm + extrapixels; + bd = (L_ABS(bm - bs) <= maxdiff) ? bs : bm + extrapixels; + } else if (subflag == L_SUB_ON_SIZE_DIFF) { + ld = (L_ABS(wm - ws) <= maxdiff) ? ls : lm - extrapixels; + td = (L_ABS(hm - hs) <= maxdiff) ? ts : tm - extrapixels; + rd = (L_ABS(wm - ws) <= maxdiff) ? rs : rm + extrapixels; + bd = (L_ABS(hm - hs) <= maxdiff) ? bs : bm + extrapixels; + } else if (subflag == L_USE_CAPPED_MIN) { + ld = L_MAX(lm, L_MIN(ls, lm + maxdiff)); + td = L_MAX(tm, L_MIN(ts, tm + maxdiff)); + rd = L_MIN(rm, L_MAX(rs, rm - maxdiff)); + bd = L_MIN(bm, L_MAX(bs, bm - maxdiff)); + } else { /* subflag == L_USE_CAPPED_MAX */ + ld = L_MIN(lm, L_MAX(ls, lm - maxdiff)); + td = L_MIN(tm, L_MAX(ts, tm - maxdiff)); + rd = L_MAX(rm, L_MIN(rs, rm + maxdiff)); + bd = L_MAX(bm, L_MIN(bs, bm + maxdiff)); + } + boxd = boxCreate(ld, td, rd - ld + 1, bd - td + 1); + boxaAddBox(boxad, boxd, L_INSERT); + } + boxDestroy(&boxs); + boxDestroy(&boxm); + } + boxDestroy(&boxempty); + + return boxad; +} + + +/*! + * \brief boxaConstrainSize() + * + * \param[in] boxas + * \param[in] width force width of all boxes to this size; + * input 0 to use the median width + * \param[in] widthflag L_ADJUST_SKIP, L_ADJUST_LEFT, L_ADJUST_RIGHT, + * or L_ADJUST_LEFT_AND_RIGHT + * \param[in] height force height of all boxes to this size; + * input 0 to use the median height + * \param[in] heightflag L_ADJUST_SKIP, L_ADJUST_TOP, L_ADJUST_BOT, + * or L_ADJUST_TOP_AND_BOT + * \return boxad adjusted so all boxes are the same size + * + *
+ * Notes:
+ *      (1) Forces either width or height (or both) of every box in
+ *          the boxa to a specified size, by moving the indicated sides.
+ *      (2) Not all input boxes need to be valid.  Median values will be
+ *          used with invalid boxes.
+ *      (3) Typical input might be the output of boxaLinearFit(),
+ *          where each side has been fit.
+ *      (4) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(),
+ *          this is not dependent on a difference threshold to change the size.
+ *      (5) On error, a message is issued and a copy of the input boxa
+ *          is returned.
+ * 
+ */ +BOXA * +boxaConstrainSize(BOXA *boxas, + l_int32 width, + l_int32 widthflag, + l_int32 height, + l_int32 heightflag) +{ +l_int32 n, i, x, y, w, h, invalid; +l_int32 delw, delh, del_left, del_right, del_top, del_bot; +BOX *medbox, *boxs, *boxd; +BOXA *boxad; + + PROCNAME("boxaConstrainSize"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + + /* Need median values if requested or if there are invalid boxes */ + invalid = boxaGetCount(boxas) - boxaGetValidCount(boxas); + medbox = NULL; + if (width == 0 || height == 0 || invalid > 0) { + if (boxaGetMedianVals(boxas, &x, &y, NULL, NULL, &w, &h)) { + L_ERROR("median vals not returned", procName); + return boxaCopy(boxas, L_COPY); + } + medbox = boxCreate(x, y, w, h); + if (width == 0) width = w; + if (height == 0) height = h; + } + + n = boxaGetCount(boxas); + boxad = boxaCreate(n); + for (i = 0; i < n; i++) { + if ((boxs = boxaGetValidBox(boxas, i, L_COPY)) == NULL) + boxs = boxCopy(medbox); + boxGetGeometry(boxs, NULL, NULL, &w, &h); + delw = width - w; + delh = height - h; + del_left = del_right = del_top = del_bot = 0; + if (widthflag == L_ADJUST_LEFT) { + del_left = -delw; + } else if (widthflag == L_ADJUST_RIGHT) { + del_right = delw; + } else { + del_left = -delw / 2; + del_right = delw / 2 + L_SIGN(delw) * (delw & 1); + } + if (heightflag == L_ADJUST_TOP) { + del_top = -delh; + } else if (heightflag == L_ADJUST_BOT) { + del_bot = delh; + } else { + del_top = -delh / 2; + del_bot = delh / 2 + L_SIGN(delh) * (delh & 1); + } + boxd = boxAdjustSides(NULL, boxs, del_left, del_right, + del_top, del_bot); + boxaAddBox(boxad, boxd, L_INSERT); + boxDestroy(&boxs); + } + + boxDestroy(&medbox); + return boxad; +} + + +/*! + * \brief boxaReconcileEvenOddHeight() + * + * \param[in] boxas containing at least 3 valid boxes in even and odd + * \param[in] sides L_ADJUST_TOP, L_ADJUST_BOT, L_ADJUST_TOP_AND_BOT + * \param[in] delh threshold on median height difference + * \param[in] op L_ADJUST_CHOOSE_MIN, L_ADJUST_CHOOSE_MAX + * \param[in] factor > 0.0, typically near 1.0 + * \param[in] start 0 if pairing (0,1), etc; 1 if pairing (1,2), etc + * \return boxad adjusted, or a copy of boxas on error + * + *
+ * Notes:
+ *      (1) The basic idea is to reconcile differences in box height
+ *          in the even and odd boxes, by moving the top and/or bottom
+ *          edges in the even and odd boxes.  Choose the edge or edges
+ *          to be moved, whether to adjust the boxes with the min
+ *          or the max of the medians, and the threshold on the median
+ *          difference between even and odd box heights for the operations
+ *          to take place.  The same threshold is also used to
+ *          determine if each individual box edge is to be adjusted.
+ *      (2) Boxes are conditionally reset with either the same top (y)
+ *          value or the same bottom value, or both.  The value is
+ *          determined by the greater or lesser of the medians of the
+ *          even and odd boxes, with the choice depending on the value
+ *          of %op, which selects for either min or max median height.
+ *          If the median difference between even and odd boxes is
+ *          greater than %dely, then any individual box edge that differs
+ *          from the selected median by more than %dely is set to
+ *          the selected median times a factor typically near 1.0.
+ *      (3) Note that if selecting for minimum height, you will choose
+ *          the largest y-value for the top and the smallest y-value for
+ *          the bottom of the box.
+ *      (4) Typical input might be the output of boxaSmoothSequence(),
+ *          where even and odd boxa have been independently regulated.
+ *      (5) Require at least 3 valid even boxes and 3 valid odd boxes.
+ *          Median values will be used for invalid boxes.
+ *      (6) If the median height is not representative of the boxes
+ *          in %boxas, this can make things much worse.  In that case,
+ *          ignore the value of %op, and force pairwise equality of the
+ *          heights, with pairwise maximal vertical extension.
+ * 
+ */ +BOXA * +boxaReconcileEvenOddHeight(BOXA *boxas, + l_int32 sides, + l_int32 delh, + l_int32 op, + l_float32 factor, + l_int32 start) +{ +l_int32 n, he, ho, hmed, doeven; +l_float32 del1, del2; +BOXA *boxae, *boxao, *boxa1e, *boxa1o, *boxad; + + PROCNAME("boxaReconcileEvenOddHeight"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT && + sides != L_ADJUST_TOP_AND_BOT) { + L_WARNING("no action requested; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if ((n = boxaGetValidCount(boxas)) < 6) { + L_WARNING("need at least 6 valid boxes; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (factor <= 0.0) { + L_WARNING("invalid factor; setting to 1.0\n", procName); + factor = 1.0; + } + + /* Require at least 3 valid boxes of both types */ + boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); + if (boxaGetValidCount(boxae) < 3 || boxaGetValidCount(boxao) < 3) { + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return boxaCopy(boxas, L_COPY); + } + + /* Get the median heights for each set */ + boxaGetMedianVals(boxae, NULL, NULL, NULL, NULL, NULL, &he); + boxaGetMedianVals(boxao, NULL, NULL, NULL, NULL, NULL, &ho); + L_INFO("median he = %d, median ho = %d\n", procName, he, ho); + + /* If the difference in median height reaches the threshold %delh, + * only adjust the side(s) of one of the sets. If we choose + * the minimum median height as the target, allow the target + * to be scaled by a factor, typically near 1.0, of the + * minimum median height. And similarly if the target is + * the maximum median height. */ + if (L_ABS(he - ho) > delh) { + if (op == L_ADJUST_CHOOSE_MIN) { + doeven = (ho < he) ? TRUE : FALSE; + hmed = (l_int32)(factor * L_MIN(he, ho)); + hmed = L_MIN(hmed, L_MAX(he, ho)); /* don't make it bigger! */ + } else { /* max height */ + doeven = (ho > he) ? TRUE : FALSE; + hmed = (l_int32)(factor * L_MAX(he, ho)); + hmed = L_MAX(hmed, L_MIN(he, ho)); /* don't make it smaller! */ + } + if (doeven) { + boxa1e = boxaAdjustHeightToTarget(NULL, boxae, sides, hmed, delh); + boxa1o = boxaCopy(boxao, L_COPY); + } else { /* !doeven */ + boxa1e = boxaCopy(boxae, L_COPY); + boxa1o = boxaAdjustHeightToTarget(NULL, boxao, sides, hmed, delh); + } + } else { + boxa1e = boxaCopy(boxae, L_CLONE); + boxa1o = boxaCopy(boxao, L_CLONE); + } + boxaDestroy(&boxae); + boxaDestroy(&boxao); + + /* It can happen that the median is not a good measure for an + * entire book. In that case, the reconciliation above can do + * more harm than good. Sanity check by comparing height and y + * differences of adjacent even/odd boxes, before and after + * reconciliation. */ + boxad = boxaMergeEvenOdd(boxa1e, boxa1o, 0); + boxaTestEvenOddHeight(boxas, boxad, start, &del1, &del2); + boxaDestroy(&boxa1e); + boxaDestroy(&boxa1o); + if (del2 < del1 + 10.) + return boxad; + + /* Using the median made it worse. Skip reconciliation: + * forcing all pairs of top and bottom values to have + * maximum extent does not improve the situation either. */ + L_INFO("Got worse: del2 = %f > del1 = %f\n", procName, del2, del1); + boxaDestroy(&boxad); + return boxaCopy(boxas, L_COPY); +} + + +/*! + * \brief boxaTestEvenOddHeight() + * + * \param[in] boxa1 input boxa 1 + * \param[in] boxa2 input boxa 2 + * \param[in] start 0 if pairing (0,1), etc; 1 if pairing (1,2), etc + * \param[out] pdel1 root mean of (dely^2 + delh^2 for boxa1 + * \param[out] pdel2 root mean of (dely^2 + delh^2 for boxa2 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This compares differences in the y location and height of
+ *          adjacent boxes, in each of the input boxa.
+ * 
+ */ +static l_int32 +boxaTestEvenOddHeight(BOXA *boxa1, + BOXA *boxa2, + l_int32 start, + l_float32 *pdel1, + l_float32 *pdel2) +{ +l_int32 i, n, npairs, y1a, y1b, y2a, y2b, h1a, h1b, h2a, h2b; +l_float32 del1, del2; + + PROCNAME("boxaTestEvenOddHeight"); + + if (pdel1) *pdel1 = 0.0; + if (pdel2) *pdel2 = 0.0; + if (!pdel1 || !pdel2) + return ERROR_INT("&del1 and &del2 not both defined", procName, 1); + if (!boxa1 || !boxa2) + return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); + n = L_MIN(boxaGetCount(boxa1), boxaGetCount(boxa2)); + + /* For boxa1 and boxa2 separately, we expect the y and h values + * to be similar for adjacent boxes. Get a measure of similarity + * by finding the sum of squares of differences between + * y values and between h values, and adding them. */ + del1 = del2 = 0.0; + npairs = (n - start) / 2; + for (i = start; i < 2 * npairs; i += 2) { + boxaGetBoxGeometry(boxa1, i, NULL, &y1a, NULL, &h1a); + boxaGetBoxGeometry(boxa1, i + 1, NULL, &y1b, NULL, &h1b); + del1 += (l_float32)(y1a - y1b) * (y1a - y1b) + + (h1a - h1b) * (h1a - h1b); + boxaGetBoxGeometry(boxa2, i, NULL, &y2a, NULL, &h2a); + boxaGetBoxGeometry(boxa2, i + 1, NULL, &y2b, NULL, &h2b); + del2 += (l_float32)(y2a - y2b) * (y2a - y2b) + + (h2a - h2b) * (h2a - h2b); + } + + /* Get the root of the average of the sum of square differences */ + *pdel1 = (l_float32)sqrt((l_float64)del1 / (0.5 * n)); + *pdel2 = (l_float32)sqrt((l_float64)del2 / (0.5 * n)); + return 0; +} + + +/*! + * \brief boxaReconcilePairWidth() + * + * \param[in] boxas + * \param[in] delw threshold on adjacent width difference + * \param[in] op L_ADJUST_CHOOSE_MIN, L_ADJUST_CHOOSE_MAX + * \param[in] factor > 0.0, typically near 1.0 + * \param[in] na [optional] indicator array allowing change + * \return boxad adjusted, or a copy of boxas on error + * + *
+ * Notes:
+ *      (1) This reconciles differences in the width of adjacent boxes,
+ *          by moving one side of one of the boxes in each pair.
+ *          If the widths in the pair differ by more than some
+ *          threshold, move either the left side for even boxes or
+ *          the right side for odd boxes, depending on if we're choosing
+ *          the min or max.  If choosing min, the width of the max is
+ *          set to factor * (width of min).  If choosing max, the width
+ *          of the min is set to factor * (width of max).
+ *      (2) If %na exists, it is an indicator array corresponding to the
+ *          boxes in %boxas.  If %na != NULL, only boxes with an
+ *          indicator value of 1 are allowed to adjust; otherwise,
+ *          all boxes can adjust.
+ *      (3) Typical input might be the output of boxaSmoothSequence(),
+ *          where even and odd boxa have been independently regulated.
+ * 
+ */ +BOXA * +boxaReconcilePairWidth(BOXA *boxas, + l_int32 delw, + l_int32 op, + l_float32 factor, + NUMA *na) +{ +l_int32 i, ne, no, nmin, xe, we, xo, wo, inde, indo, x, w; +BOX *boxe, *boxo; +BOXA *boxae, *boxao, *boxad; + + PROCNAME("boxaReconcilePairWidth"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (factor <= 0.0) { + L_WARNING("invalid factor; setting to 1.0\n", procName); + factor = 1.0; + } + + /* Taking the boxes in pairs, if the difference in width reaches + * the threshold %delw, adjust the left or right side of one + * of the pair. */ + boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); + ne = boxaGetCount(boxae); + no = boxaGetCount(boxao); + nmin = L_MIN(ne, no); + for (i = 0; i < nmin; i++) { + /* Set indicator values */ + if (na) { + numaGetIValue(na, 2 * i, &inde); + numaGetIValue(na, 2 * i + 1, &indo); + } else { + inde = indo = 1; + } + if (inde == 0 && indo == 0) continue; + + boxe = boxaGetBox(boxae, i, L_CLONE); + boxo = boxaGetBox(boxao, i, L_CLONE); + boxGetGeometry(boxe, &xe, NULL, &we, NULL); + boxGetGeometry(boxo, &xo, NULL, &wo, NULL); + if (we == 0 || wo == 0) { /* if either is invalid; skip */ + boxDestroy(&boxe); + boxDestroy(&boxo); + continue; + } else if (L_ABS(we - wo) > delw) { + if (op == L_ADJUST_CHOOSE_MIN) { + if (we > wo && inde == 1) { + /* move left side of even to the right */ + w = factor * wo; + x = xe + (we - w); + boxSetGeometry(boxe, x, -1, w, -1); + } else if (we < wo && indo == 1) { + /* move right side of odd to the left */ + w = factor * we; + boxSetGeometry(boxo, -1, -1, w, -1); + } + } else { /* maximize width */ + if (we < wo && inde == 1) { + /* move left side of even to the left */ + w = factor * wo; + x = L_MAX(0, xe + (we - w)); + w = we + (xe - x); /* covers both cases for the max */ + boxSetGeometry(boxe, x, -1, w, -1); + } else if (we > wo && indo == 1) { + /* move right side of odd to the right */ + w = factor * we; + boxSetGeometry(boxo, -1, -1, w, -1); + } + } + } + boxDestroy(&boxe); + boxDestroy(&boxo); + } + + boxad = boxaMergeEvenOdd(boxae, boxao, 0); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return boxad; +} + + +/*! + * \brief boxaSizeConsistency1() + * + * \param[in] boxas of size >= 10 + * \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT + * \param[in] threshp threshold for pairwise fractional variation + * \param[in] threshm threshold for fractional variation from median + * \param[out] pfvarp [optional] average fractional pairwise variation + * \param[out] pfvarm [optional] average fractional median variation + * \param[out] psame decision for uniformity of page size (1, 0, -1) + * + *
+ * Notes:
+ *      (1) This evaluates a boxa for particular types of dimensional
+ *          variation.  Select either width or height variation.  Then
+ *          it returns two numbers: one is based on pairwise (even/odd)
+ *          variation; the other is based on the average variation
+ *          from the boxa median.
+ *      (2) For the pairwise variation, get the fraction of the absolute
+ *          difference in dimension of each pair of boxes, and take
+ *          the average value.  The median variation is simply the
+ *          the average of the fractional deviation from the median
+ *          of all the boxes.
+ *      (3) Use 0 for default values of %threshp and %threshm.  They are
+ *            threshp:  0.02
+ *            threshm:  0.015
+ *      (4) The intended application is that the boxes are a sequence of
+ *          page regions in a book scan, and we calculate two numbers
+ *          that can give an indication if the pages are approximately
+ *          the same size.  The pairwise variation should be small if
+ *          the boxes are correctly calculated.  If there are a
+ *          significant number of random or systematic outliers, the
+ *          pairwise variation will be large, and no decision will be made
+ *          (i.e., return same == -1).  Here are the possible outcomes:
+ *            Pairwise Var    Median Var    Decision
+ *            ------------    ----------    --------
+ *            small           small         same size  (1)
+ *            small           large         different size  (0)
+ *            large           small/large   unknown   (-1)
+ * 
+ */ +l_ok +boxaSizeConsistency1(BOXA *boxas, + l_int32 type, + l_float32 threshp, + l_float32 threshm, + l_float32 *pfvarp, + l_float32 *pfvarm, + l_int32 *psame) +{ +l_int32 i, n, bw1, bh1, bw2, bh2, npairs; +l_float32 ave, fdiff, sumdiff, med, fvarp, fvarm; +NUMA *na1; + + PROCNAME("boxaSizeConsistency1"); + + if (pfvarp) *pfvarp = 0.0; + if (pfvarm) *pfvarm = 0.0; + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = -1; + if (!boxas) + return ERROR_INT("boxas not defined", procName, 1); + if (boxaGetValidCount(boxas) < 6) + return ERROR_INT("need a least 6 valid boxes", procName, 1); + if (type != L_CHECK_WIDTH && type != L_CHECK_HEIGHT) + return ERROR_INT("invalid type", procName, 1); + if (threshp < 0.0 || threshp >= 0.5) + return ERROR_INT("invalid threshp", procName, 1); + if (threshm < 0.0 || threshm >= 0.5) + return ERROR_INT("invalid threshm", procName, 1); + if (threshp == 0.0) threshp = 0.02; + if (threshm == 0.0) threshm = 0.015; + + /* Evaluate pairwise variation */ + n = boxaGetCount(boxas); + na1 = numaCreate(0); + for (i = 0, npairs = 0, sumdiff = 0; i < n - 1; i += 2) { + boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw1, &bh1); + boxaGetBoxGeometry(boxas, i + 1, NULL, NULL, &bw2, &bh2); + if (bw1 == 0 || bh1 == 0 || bw2 == 0 || bh2 == 0) + continue; + npairs++; + if (type == L_CHECK_WIDTH) { + ave = (bw1 + bw2) / 2.0; + fdiff = L_ABS(bw1 - bw2) / ave; + numaAddNumber(na1, bw1); + numaAddNumber(na1, bw2); + } else { /* type == L_CHECK_HEIGHT) */ + ave = (bh1 + bh2) / 2.0; + fdiff = L_ABS(bh1 - bh2) / ave; + numaAddNumber(na1, bh1); + numaAddNumber(na1, bh2); + } + sumdiff += fdiff; + } + fvarp = sumdiff / npairs; + if (pfvarp) *pfvarp = fvarp; + + /* Evaluate the average abs fractional deviation from the median */ + numaGetMedian(na1, &med); + if (med == 0.0) { + L_WARNING("median value is 0\n", procName); + } else { + numaGetMeanDevFromMedian(na1, med, &fvarm); + fvarm /= med; + if (pfvarm) *pfvarm = fvarm; + } + numaDestroy(&na1); + + /* Make decision */ + if (fvarp < threshp && fvarm < threshm) + *psame = 1; + else if (fvarp < threshp && fvarm > threshm) + *psame = 0; + else + *psame = -1; /* unknown */ + return 0; +} + + +/*! + * \brief boxaSizeConsistency2() + * + * \param[in] boxas of size >= 10 + * \param[out] pfdevw average fractional deviation from median width + * \param[out] pfdevh average fractional deviation from median height + * \param[in] debug 1 for debug plot output of input and regularized + * width and height + * + *
+ * Notes:
+ *      (1) This evaluates a boxa for consistency of the box sizes.
+ *          The intended application is that the boxes are a sequence of
+ *          page regions in a book scan, and the output is a decision
+ *          about whether the pages should be approximately the same size.
+ *          The determination should be robust to outliers, both random
+ *          and (for many cases) systematic.
+ *      (2) This differs from boxaSizeConsistency1() in that it attempts
+ *          to correct for box dimensional errors before doing the
+ *          evaluation.  For this reason, it may be less robust.
+ *      (3) Adjacent even and odd boxes are expected to be the same size.
+ *          Take them pairwise, and assume the minimum height, hmin,
+ *          is correct.  Then for (the usual case) wmin/hmin > 0.5, assume
+ *          the minimum width is correct.  If wmin/hmin <= 0.5, assume
+ *          the maximum width is correct.
+ *      (4) After correcting each pair so that they are the same size,
+ *          compute the average fractional deviation, from median width and
+ *          height.  A deviation of width or height by more than about
+ *          0.02 is evidence that the boxes may be from a non-homogeneous
+ *          source, such as a book with significantly different page sizes.
+ * 
+ */ +l_ok +boxaSizeConsistency2(BOXA *boxas, + l_float32 *pfdevw, + l_float32 *pfdevh, + l_int32 debug) +{ +l_int32 i, n, bw1, bh1, bw2, bh2, npairs; +l_float32 medw, medh, devw, devh, minw, maxw, minh, w; +BOX *box; +BOXA *boxa1; +NUMA *naw, *nah; +PIX *pix1, *pix2, *pix3; +PIXA *pixa; + + PROCNAME("boxaSizeConsistency2"); + + if (pfdevw) *pfdevw = 0.0; + if (pfdevh) *pfdevh = 0.0; + if (!boxas) + return ERROR_INT("boxas not defined", procName, 1); + if (!pfdevw || !pfdevh) + return ERROR_INT("&fdevw and &fdevh not both defined", procName, 1); + n = boxaGetCount(boxas); + if (n < 10) { + L_WARNING("small boxa; assuming OK", procName); + return 0; + } + + /* Regularize w and h in pairs; skip last box if n is odd */ + boxa1 = (debug) ? boxaCreate(n) : NULL; + naw = numaCreate(0); + nah = numaCreate(0); + for (i = 0, npairs = 0; i < n - 1; i += 2) { + boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw1, &bh1); + boxaGetBoxGeometry(boxas, i + 1, NULL, NULL, &bw2, &bh2); + if (bw1 == 0 || bh1 == 0 || bw2 == 0 || bh2 == 0) + continue; + npairs++; + minw = (l_float32)L_MIN(bw1, bw2); + maxw = (l_float32)L_MAX(bw1, bw2); + minh = (l_float32)L_MIN(bh1, bh2); + w = (minw / minh > 0.5) ? minw : maxw; + numaAddNumber(naw, w); + numaAddNumber(nah, minh); + if (debug) { + box = boxCreate(0, 0, w, minh); + boxaAddBox(boxa1, box, L_COPY); + boxaAddBox(boxa1, box, L_INSERT); + } + } + if (npairs == 0) { + L_WARNING("no valid box pairs\n", procName); + numaDestroy(&naw); + numaDestroy(&nah); + boxaDestroy(&boxa1); + } + + /* Get the median value of the regularized sizes, and find + * the average absolute fractional deviation from the median. */ + numaGetMedian(naw, &medw); + numaGetMedian(nah, &medh); + numaGetMeanDevFromMedian(naw, medw, &devw); + numaGetMeanDevFromMedian(nah, medh, &devh); + *pfdevw = devw / medw; + *pfdevh = devh / medh; + if (debug) { + fprintf(stderr, "medw = %5.1f, medh = %5.1f\n", medw, medh); + fprintf(stderr, "fdevw = %6.3f, fdevh = %6.3f\n", *pfdevw, *pfdevh); + boxaPlotSizes(boxas, "input_boxa", NULL, NULL, &pix1); + boxaPlotSizes(boxa1, "regularized_boxa", NULL, NULL, &pix2); + pixDisplay(pix1, 500, 0); + pixDisplay(pix2, 500, 1000); + pixa = pixaCreate(2); + pixaAddPix(pixa, pix1, L_INSERT); + pixaAddPix(pixa, pix2, L_INSERT); + pix3 = pixaDisplayTiledInColumns(pixa, 2, 1.0, 3, 2); + lept_mkdir("lept/boxa"); + pixWrite("/tmp/lept/boxa/eval.png", pix3, IFF_PNG); + pixDisplay(pix3, 100, 100); + pixDestroy(&pix3); + pixaDestroy(&pixa); + boxaDestroy(&boxa1); + } + + numaDestroy(&naw); + numaDestroy(&nah); + return 0; +} + + +/*! + * \brief boxaReconcileSizeByMedian() + * + * \param[in] boxas containing at least 6 valid boxes + * \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT, L_CHECK_BOTH + * \param[in] dfract threshold fraction of dimensional variation from + * median; in range (0 ... 1); typ. about 0.05. + * \param[in] sfract threshold fraction of side variation from median; + * in range (0 ... 1); typ. about 0.04. + * \param[in] factor expansion for fixed box beyond median width; + * should be near 1.0. + * \param[out] pnadelw [optional] diff from median width for boxes + * above threshold + * \param[out] pnadelh [optional] diff from median height for boxes + * above threshold + * \param[out] ratiowh [optional] ratio of median width/height of boxas + * \return boxad possibly adjusted from boxas; a copy of boxas on error + * + *
+ * Notes:
+ *      (1) The basic idea is to identify significant differences in box
+ *          dimension (either width or height) and modify the outlier boxes.
+ *      (2) %type specifies if we are reconciling the width, height or both.
+ *      (3) %dfract specifies the tolerance for different dimensions. Any
+ *          box with a fractional difference from the median size that
+ *          exceeds %dfract will be altered.
+ *      (4) %sfract specifies the tolerance for different side locations.
+ *          If a box has been marked by (3) for alteration, any side
+ *          location that differs from the median side location by
+ *          more than %sfract of the median dimension (medw or medh)
+ *          will be moved.
+ *      (5) Median width and height are found for all valid boxes (i.e.,
+ *          for all boxes with width and height > 0.
+ *          Median side locations are found separately for even and odd boxes,
+ *          using only boxes that are "inliers"; i.e., that have been
+ *          found by (3) to be within tolerance for width or height.
+ *      (6) If all box dimensions are within threshold of the median size,
+ *          just return a copy.  Otherwise, box sides of the outliers
+ *          will be adjusted.
+ *      (7) Using %sfract, sides that are sufficiently far from the median
+ *          are first moved to the median value.  Then they are moved
+ *          together (in or out) so that the final box dimension
+ *          is %factor times the median dimension.
+ *      (8) The arrays that are the initial deviation from median size
+ *          (width and height) are optionally returned.  Also optionally
+ *          returned is the median w/h asperity ratio of the input %boxas.
+ * 
+ */ +BOXA * +boxaReconcileSizeByMedian(BOXA *boxas, + l_int32 type, + l_float32 dfract, + l_float32 sfract, + l_float32 factor, + NUMA **pnadelw, + NUMA **pnadelh, + l_float32 *pratiowh) +{ +l_int32 i, n, ne, no, outfound, isvalid, ind, del, maxdel; +l_int32 medw, medh, bw, bh, left, right, top, bot; +l_int32 medleft, medlefte, medlefto, medright, medrighte, medrighto; +l_int32 medtop, medtope, medtopo, medbot, medbote, medboto; +l_float32 brat; +BOX *box; +BOXA *boxa1, *boxae, *boxao, *boxad; +NUMA *naind, *nadelw, *nadelh; + + PROCNAME("boxaReconcileSizeByMedian"); + + if (pnadelw) *pnadelw = NULL; + if (pnadelh) *pnadelh = NULL; + if (pratiowh) *pratiowh = 0.0; + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (type != L_CHECK_WIDTH && type != L_CHECK_HEIGHT && + type != L_CHECK_BOTH) { + L_WARNING("invalid type; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (dfract <= 0.0 || dfract >= 0.5) { + L_WARNING("invalid dimensional fract; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (sfract <= 0.0 || sfract >= 0.5) { + L_WARNING("invalid side fract; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + if (factor < 0.8 || factor > 1.25) + L_WARNING("factor %5.3f is typ. closer to 1.0\n", procName, factor); + if (boxaGetValidCount(boxas) < 6) { + L_WARNING("need at least 6 valid boxes; returning copy\n", procName); + return boxaCopy(boxas, L_COPY); + } + + /* If reconciling both width and height, optionally return array of + * median deviations and even/odd ratio for width measurements */ + if (type == L_CHECK_BOTH) { + boxa1 = boxaReconcileSizeByMedian(boxas, L_CHECK_WIDTH, dfract, sfract, + factor, pnadelw, NULL, pratiowh); + boxad = boxaReconcileSizeByMedian(boxa1, L_CHECK_HEIGHT, dfract, sfract, + factor, NULL, pnadelh, NULL); + boxaDestroy(&boxa1); + return boxad; + } + + n = boxaGetCount(boxas); + naind = numaCreate(n); /* outlier indicator array */ + boxae = boxaCreate(0); /* even inliers */ + boxao = boxaCreate(0); /* odd inliers */ + outfound = FALSE; + if (type == L_CHECK_WIDTH) { + boxaMedianDimensions(boxas, &medw, &medh, NULL, NULL, NULL, NULL, + &nadelw, NULL); + if (pratiowh) { + *pratiowh = (l_float32)medw / (l_float32)medh; + L_INFO("median ratio w/h = %5.3f\n", procName, *pratiowh); + } + if (pnadelw) + *pnadelw = nadelw; + else + numaDestroy(&nadelw); + + /* Check for outliers; assemble inliers */ + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxas, i, L_COPY)) == NULL) { + numaAddNumber(naind, 0); + continue; + } + boxGetGeometry(box, NULL, NULL, &bw, NULL); + brat = (l_float32)bw / (l_float32)medw; + if (brat < 1.0 - dfract || brat > 1.0 + dfract) { + outfound = TRUE; + numaAddNumber(naind, 1); + boxDestroy(&box); + } else { /* add to inliers */ + numaAddNumber(naind, 0); + if (i % 2 == 0) + boxaAddBox(boxae, box, L_INSERT); + else + boxaAddBox(boxao, box, L_INSERT); + } + } + if (!outfound) { /* nothing to do */ + numaDestroy(&naind); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + L_INFO("no width outlier boxes found\n", procName); + return boxaCopy(boxas, L_COPY); + } + + /* Get left/right parameters from inliers. Handle the case + * where there are no inliers for one of the sets. For example, + * when all the even boxes have a different dimension from + * the odd boxes, and the median arbitrarily gets assigned + * to the even boxes, there are no odd inliers; in that case, + * use the even inliers sides to decide whether to adjust + * the left or the right sides of individual outliers. */ + L_INFO("fixing width of outlier boxes\n", procName); + medlefte = medrighte = medlefto = medrighto = 0; + if ((ne = boxaGetValidCount(boxae)) > 0) + boxaGetMedianVals(boxae, &medlefte, NULL, &medrighte, NULL, + NULL, NULL); + if ((no = boxaGetValidCount(boxao)) > 0) + boxaGetMedianVals(boxao, &medlefto, NULL, &medrighto, NULL, + NULL, NULL); + if (ne == 0) { /* use odd inliers values for both */ + medlefte = medlefto; + medrighte = medrighto; + } else if (no == 0) { /* use even inliers values for both */ + medlefto = medlefte; + medrighto = medrighte; + } + + /* Adjust the left and/or right sides of outliers. + * For each box that is a dimensional outlier, consider each side. + * Any side that differs fractionally from the median value + * by more than %sfract times the median width (medw) is set to + * the median value for that side. Then both sides are moved + * an equal distance in or out to make w = %factor * medw. */ + boxad = boxaCreate(n); + maxdel = (l_int32)(sfract * medw + 0.5); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxas, i, L_COPY); + boxIsValid(box, &isvalid); + numaGetIValue(naind, i, &ind); + medleft = (i % 2 == 0) ? medlefte : medlefto; + medright = (i % 2 == 0) ? medrighte : medrighto; + if (ind == 1 && isvalid) { /* adjust sides */ + boxGetSideLocations(box, &left, &right, NULL, NULL); + if (L_ABS(left - medleft) > maxdel) left = medleft; + if (L_ABS(right - medright) > maxdel) right = medright; + del = (l_int32)(factor * medw - (right - left)) / 2; + boxSetSide(box, L_SET_LEFT, left - del, 0); + boxSetSide(box, L_SET_RIGHT, right + del, 0); + } + boxaAddBox(boxad, box, L_INSERT); + } + } else { /* L_CHECK_HEIGHT */ + boxaMedianDimensions(boxas, &medw, &medh, NULL, NULL, NULL, NULL, + NULL, &nadelh); + if (pratiowh) { + *pratiowh = (l_float32)medw / (l_float32)medh; + L_INFO("median ratio w/h = %5.3f\n", procName, *pratiowh); + } + if (pnadelh) + *pnadelh = nadelh; + else + numaDestroy(&nadelh); + + /* Check for outliers; assemble inliers */ + for (i = 0; i < n; i++) { + if ((box = boxaGetValidBox(boxas, i, L_COPY)) == NULL) { + numaAddNumber(naind, 0); + continue; + } + boxGetGeometry(box, NULL, NULL, NULL, &bh); + brat = (l_float32)bh / (l_float32)medh; + if (brat < 1.0 - dfract || brat > 1.0 + dfract) { + outfound = TRUE; + numaAddNumber(naind, 1); + boxDestroy(&box); + } else { /* add to inliers */ + numaAddNumber(naind, 0); + if (i % 2 == 0) + boxaAddBox(boxae, box, L_INSERT); + else + boxaAddBox(boxao, box, L_INSERT); + } + } + if (!outfound) { /* nothing to do */ + numaDestroy(&naind); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + L_INFO("no height outlier boxes found\n", procName); + return boxaCopy(boxas, L_COPY); + } + + /* Get top/bot parameters from inliers. Handle the case + * where there are no inliers for one of the sets. For example, + * when all the even boxes have a different dimension from + * the odd boxes, and the median arbitrarily gets assigned + * to the even boxes, there are no odd inliers; in that case, + * use the even inlier sides to decide whether to adjust + * the top or the bottom sides of individual outliers. */ + L_INFO("fixing height of outlier boxes\n", procName); + medlefte = medtope = medbote = medtopo = medboto = 0; + if ((ne = boxaGetValidCount(boxae)) > 0) + boxaGetMedianVals(boxae, NULL, &medtope, NULL, &medbote, + NULL, NULL); + if ((no = boxaGetValidCount(boxao)) > 0) + boxaGetMedianVals(boxao, NULL, &medtopo, NULL, &medboto, + NULL, NULL); + if (ne == 0) { /* use odd inliers values for both */ + medtope = medtopo; + medbote = medboto; + } else if (no == 0) { /* use even inliers values for both */ + medtopo = medtope; + medboto = medbote; + } + + /* Adjust the top and/or bottom sides of outliers. + * For each box that is a dimensional outlier, consider each side. + * Any side that differs fractionally from the median value + * by more than %sfract times the median height (medh) is + * set to the median value for that that side. Then both + * sides are moved an equal distance in or out to make + * h = %factor * medh). */ + boxad = boxaCreate(n); + maxdel = (l_int32)(sfract * medh + 0.5); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxas, i, L_COPY); + boxIsValid(box, &isvalid); + numaGetIValue(naind, i, &ind); + medtop = (i % 2 == 0) ? medtope : medtopo; + medbot = (i % 2 == 0) ? medbote : medboto; + if (ind == 1 && isvalid) { /* adjust sides */ + boxGetSideLocations(box, NULL, NULL, &top, &bot); + if (L_ABS(top - medtop) > maxdel) top = medtop; + if (L_ABS(bot - medbot) > maxdel) bot = medbot; + del = (l_int32)(factor * medh - (bot - top)) / 2; /* typ > 0 */ + boxSetSide(box, L_SET_TOP, L_MAX(0, top - del), 0); + boxSetSide(box, L_SET_BOT, bot + del, 0); + } + boxaAddBox(boxad, box, L_INSERT); + } + } + numaDestroy(&naind); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return boxad; +} + + +/*! + * \brief boxaPlotSides() + * + * \param[in] boxa source boxa + * \param[in] plotname [optional], can be NULL + * \param[out] pnal [optional] na of left sides + * \param[out] pnat [optional] na of top sides + * \param[out] pnar [optional] na of right sides + * \param[out] pnab [optional] na of bottom sides + * \param[out] ppixd [optional] pix of the output plot + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This debugging function shows the progression of the four
+ *          sides in the boxa.  There must be at least 2 boxes.
+ *      (2) If there are invalid boxes (e.g., if only even or odd
+ *          indices have valid boxes), this will fill them with the
+ *          nearest valid box before plotting.
+ *      (3) The plotfiles are put in /tmp/lept/plots/, and are named
+ *          either with %plotname or, if NULL, a default name.  If
+ *          %plotname is used, make sure is has no whitespace characters.
+ * 
+ */ +l_ok +boxaPlotSides(BOXA *boxa, + const char *plotname, + NUMA **pnal, + NUMA **pnat, + NUMA **pnar, + NUMA **pnab, + PIX **ppixd) +{ +char buf[128], titlebuf[128]; +char *dataname; +static l_int32 plotid = 0; +l_int32 n, i, w, h, left, top, right, bot; +l_float32 med, dev; +BOXA *boxat; +GPLOT *gplot; +NUMA *nal, *nat, *nar, *nab; + + PROCNAME("boxaPlotSides"); + + if (pnal) *pnal = NULL; + if (pnat) *pnat = NULL; + if (pnar) *pnar = NULL; + if (pnab) *pnab = NULL; + if (ppixd) *ppixd = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if ((n = boxaGetCount(boxa)) < 2) + return ERROR_INT("less than 2 boxes", procName, 1); + + boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0); + + /* Build the numas for each side */ + nal = numaCreate(n); + nat = numaCreate(n); + nar = numaCreate(n); + nab = numaCreate(n); + + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxat, i, &left, &top, &w, &h); + right = left + w - 1; + bot = top + h - 1; + numaAddNumber(nal, left); + numaAddNumber(nat, top); + numaAddNumber(nar, right); + numaAddNumber(nab, bot); + } + boxaDestroy(&boxat); + + lept_mkdir("lept/plots"); + if (plotname) { + snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%s", plotname); + snprintf(titlebuf, sizeof(titlebuf), "%s: Box sides vs. box index", + plotname); + } else { + snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%d", plotid++); + snprintf(titlebuf, sizeof(titlebuf), "Box sides vs. box index"); + } + gplot = gplotCreate(buf, GPLOT_PNG, titlebuf, + "box index", "side location"); + gplotAddPlot(gplot, NULL, nal, GPLOT_LINES, "left side"); + gplotAddPlot(gplot, NULL, nat, GPLOT_LINES, "top side"); + gplotAddPlot(gplot, NULL, nar, GPLOT_LINES, "right side"); + gplotAddPlot(gplot, NULL, nab, GPLOT_LINES, "bottom side"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + + if (ppixd) { + stringCat(buf, sizeof(buf), ".png"); + *ppixd = pixRead(buf); + dataname = (plotname) ? stringNew(plotname) : stringNew("no_name"); + numaGetMedian(nal, &med); + numaGetMeanDevFromMedian(nal, med, &dev); + fprintf(stderr, "%s left: med = %7.3f, meandev = %7.3f\n", + dataname, med, dev); + numaGetMedian(nat, &med); + numaGetMeanDevFromMedian(nat, med, &dev); + fprintf(stderr, "%s top: med = %7.3f, meandev = %7.3f\n", + dataname, med, dev); + numaGetMedian(nar, &med); + numaGetMeanDevFromMedian(nar, med, &dev); + fprintf(stderr, "%s right: med = %7.3f, meandev = %7.3f\n", + dataname, med, dev); + numaGetMedian(nab, &med); + numaGetMeanDevFromMedian(nab, med, &dev); + fprintf(stderr, "%s bot: med = %7.3f, meandev = %7.3f\n", + dataname, med, dev); + LEPT_FREE(dataname); + } + + if (pnal) + *pnal = nal; + else + numaDestroy(&nal); + if (pnat) + *pnat = nat; + else + numaDestroy(&nat); + if (pnar) + *pnar = nar; + else + numaDestroy(&nar); + if (pnab) + *pnab = nab; + else + numaDestroy(&nab); + return 0; +} + + +/*! + * \brief boxaPlotSizes() + * + * \param[in] boxa source boxa + * \param[in] plotname [optional], can be NULL + * \param[out] pnaw [optional] na of widths + * \param[out] pnah [optional] na of heights + * \param[out] ppixd [optional] pix of the output plot + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This debugging function shows the progression of box width
+ *          and height in the boxa.  There must be at least 2 boxes.
+ *      (2) If there are invalid boxes (e.g., if only even or odd
+ *          indices have valid boxes), this will fill them with the
+ *          nearest valid box before plotting.
+ *      (3) The plotfiles are put in /tmp/lept/plots/, and are named
+ *          either with %plotname or, if NULL, a default name.  If
+ *          %plotname is used, make sure is has no whitespace characters.
+ * 
+ */ +l_ok +boxaPlotSizes(BOXA *boxa, + const char *plotname, + NUMA **pnaw, + NUMA **pnah, + PIX **ppixd) +{ +char buf[128], titlebuf[128]; +static l_int32 plotid = 0; +l_int32 n, i, w, h; +BOXA *boxat; +GPLOT *gplot; +NUMA *naw, *nah; + + PROCNAME("boxaPlotSizes"); + + if (pnaw) *pnaw = NULL; + if (pnah) *pnah = NULL; + if (ppixd) *ppixd = NULL; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if ((n = boxaGetCount(boxa)) < 2) + return ERROR_INT("less than 2 boxes", procName, 1); + + boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0); + + /* Build the numas for the width and height */ + naw = numaCreate(n); + nah = numaCreate(n); + + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxat, i, NULL, NULL, &w, &h); + numaAddNumber(naw, w); + numaAddNumber(nah, h); + } + boxaDestroy(&boxat); + + lept_mkdir("lept/plots"); + if (plotname) { + snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%s", plotname); + snprintf(titlebuf, sizeof(titlebuf), "%s: Box size vs. box index", + plotname); + } else { + snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%d", plotid++); + snprintf(titlebuf, sizeof(titlebuf), "Box size vs. box index"); + } + gplot = gplotCreate(buf, GPLOT_PNG, titlebuf, + "box index", "box dimension"); + gplotAddPlot(gplot, NULL, naw, GPLOT_LINES, "width"); + gplotAddPlot(gplot, NULL, nah, GPLOT_LINES, "height"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + + if (ppixd) { + stringCat(buf, sizeof(buf), ".png"); + *ppixd = pixRead(buf); + } + + if (pnaw) + *pnaw = naw; + else + numaDestroy(&naw); + if (pnah) + *pnah = nah; + else + numaDestroy(&nah); + return 0; +} + + +/*! + * \brief boxaFillSequence() + * + * \param[in] boxas with at least 3 boxes + * \param[in] useflag L_USE_ALL_BOXES, L_USE_SAME_PARITY_BOXES + * \param[in] debug 1 for debug output + * \return boxad filled boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This simple function replaces invalid boxes with a copy of
+ *          the nearest valid box, selected from either the entire
+ *          sequence (L_USE_ALL_BOXES) or from the boxes with the
+ *          same parity (L_USE_SAME_PARITY_BOXES).  It returns a new boxa.
+ *      (2) This is useful if you expect boxes in the sequence to
+ *          vary slowly with index.
+ * 
+ */ +BOXA * +boxaFillSequence(BOXA *boxas, + l_int32 useflag, + l_int32 debug) +{ +l_int32 n, nv; +BOXA *boxae, *boxao, *boxad; + + PROCNAME("boxaFillSequence"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (useflag != L_USE_ALL_BOXES && useflag != L_USE_SAME_PARITY_BOXES) + return (BOXA *)ERROR_PTR("invalid useflag", procName, NULL); + + n = boxaGetCount(boxas); + nv = boxaGetValidCount(boxas); + if (n == nv) + return boxaCopy(boxas, L_COPY); /* all valid */ + if (debug) + L_INFO("%d valid boxes, %d invalid boxes\n", procName, nv, n - nv); + if (useflag == L_USE_SAME_PARITY_BOXES && n < 3) { + L_WARNING("n < 3; some invalid\n", procName); + return boxaCopy(boxas, L_COPY); + } + + if (useflag == L_USE_ALL_BOXES) { + boxad = boxaCopy(boxas, L_COPY); + boxaFillAll(boxad); + } else { + boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); + boxaFillAll(boxae); + boxaFillAll(boxao); + boxad = boxaMergeEvenOdd(boxae, boxao, 0); + boxaDestroy(&boxae); + boxaDestroy(&boxao); + } + + nv = boxaGetValidCount(boxad); + if (n != nv) + L_WARNING("there are still %d invalid boxes\n", procName, n - nv); + + return boxad; +} + + +/*! + * \brief boxaFillAll() + * + * \param[in] boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This static function replaces every invalid box with the
+ *          nearest valid box.  If there are no valid boxes, it
+ *          issues a warning.
+ * 
+ */ +static l_int32 +boxaFillAll(BOXA *boxa) +{ +l_int32 n, nv, i, j, spandown, spanup; +l_int32 *indic; +BOX *box, *boxt; + + PROCNAME("boxaFillAll"); + + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + n = boxaGetCount(boxa); + nv = boxaGetValidCount(boxa); + if (n == nv) return 0; + if (nv == 0) { + L_WARNING("no valid boxes out of %d boxes\n", procName, n); + return 0; + } + + /* Make indicator array for valid boxes */ + if ((indic = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) + return ERROR_INT("indic not made", procName, 1); + for (i = 0; i < n; i++) { + box = boxaGetValidBox(boxa, i, L_CLONE); + if (box) + indic[i] = 1; + boxDestroy(&box); + } + + /* Replace invalid boxes with the nearest valid one */ + for (i = 0; i < n; i++) { + box = boxaGetValidBox(boxa, i, L_CLONE); + if (!box) { + spandown = spanup = 10000000; + for (j = i - 1; j >= 0; j--) { + if (indic[j] == 1) { + spandown = i - j; + break; + } + } + for (j = i + 1; j < n; j++) { + if (indic[j] == 1) { + spanup = j - i; + break; + } + } + if (spandown < spanup) + boxt = boxaGetBox(boxa, i - spandown, L_COPY); + else + boxt = boxaGetBox(boxa, i + spanup, L_COPY); + boxaReplaceBox(boxa, i, boxt); + } + boxDestroy(&box); + } + + LEPT_FREE(indic); + return 0; +} + + +/*! + * \brief boxaSizeVariation() + * + * \param[in] boxa at least 4 boxes + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT + * \param[out] pdel_evenodd [optional] average absolute value of + * (even - odd) size pairs + * \param[out] prms_even [optional] rms deviation of even boxes + * \param[out] prms_odd [optional] rms deviation of odd boxes + * \param[out] prms_all [optional] rms deviation of all boxes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives several measures of the smoothness of either the
+ *          width or height of a sequence of boxes.
+ *          See boxaMedianDimensions() for some other measures.
+ *      (2) Statistics can be found separately for even and odd boxes.
+ *          Additionally, the average pair-wise difference between
+ *          adjacent even and odd boxes can be returned.
+ *      (3) The use case is bounding boxes for scanned page images,
+ *          where ideally the sizes should have little variance.
+ * 
+ */ +l_ok +boxaSizeVariation(BOXA *boxa, + l_int32 type, + l_float32 *pdel_evenodd, + l_float32 *prms_even, + l_float32 *prms_odd, + l_float32 *prms_all) +{ +l_int32 n, ne, no, nmin, vale, valo, i; +l_float32 sum; +BOXA *boxae, *boxao; +NUMA *nae, *nao, *na_all; + + PROCNAME("boxaSizeVariation"); + + if (pdel_evenodd) *pdel_evenodd = 0.0; + if (prms_even) *prms_even = 0.0; + if (prms_odd) *prms_odd = 0.0; + if (prms_all) *prms_all = 0.0; + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT) + return ERROR_INT("invalid type", procName, 1); + if (!pdel_evenodd && !prms_even && !prms_odd && !prms_all) + return ERROR_INT("nothing to do", procName, 1); + n = boxaGetCount(boxa); + if (n < 4) + return ERROR_INT("too few boxes", procName, 1); + + boxaSplitEvenOdd(boxa, 0, &boxae, &boxao); + ne = boxaGetCount(boxae); + no = boxaGetCount(boxao); + nmin = L_MIN(ne, no); + if (nmin == 0) { + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return ERROR_INT("either no even or no odd boxes", procName, 1); + } + + if (type == L_SELECT_WIDTH) { + boxaGetSizes(boxae, &nae, NULL); + boxaGetSizes(boxao, &nao, NULL); + boxaGetSizes(boxa, &na_all, NULL); + } else { /* L_SELECT_HEIGHT) */ + boxaGetSizes(boxae, NULL, &nae); + boxaGetSizes(boxao, NULL, &nao); + boxaGetSizes(boxa, NULL, &na_all); + } + + if (pdel_evenodd) { + sum = 0.0; + for (i = 0; i < nmin; i++) { + numaGetIValue(nae, i, &vale); + numaGetIValue(nao, i, &valo); + sum += L_ABS(vale - valo); + } + *pdel_evenodd = sum / nmin; + } + if (prms_even) + numaSimpleStats(nae, 0, -1, NULL, NULL, prms_even); + if (prms_odd) + numaSimpleStats(nao, 0, -1, NULL, NULL, prms_odd); + if (prms_all) + numaSimpleStats(na_all, 0, -1, NULL, NULL, prms_all); + + boxaDestroy(&boxae); + boxaDestroy(&boxao); + numaDestroy(&nae); + numaDestroy(&nao); + numaDestroy(&na_all); + return 0; +} + + +/*! + * \brief boxaMedianDimensions() + * + * \param[in] boxas containing at least 3 valid boxes in even and odd + * \param[out] pmedw [optional] median width of all boxes + * \param[out] pmedh [optional] median height of all boxes + * \param[out] pmedwe [optional] median width of even boxes + * \param[out] pmedwo [optional] median width of odd boxes + * \param[out] pmedhe [optional] median height of even boxes + * \param[out] pmedho [optional] median height of odd boxes + * \param[out] pnadelw [optional] width diff of each box from median + * \param[out] pnadelh [optional] height diff of each box from median + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This provides information that (1) allows identification of
+ *          boxes that have unusual (outlier) width or height, and (2) can
+ *          be used to regularize the sizes of the outlier boxes, assuming
+ *          that the boxes satisfy a fairly regular sequence and should
+ *          mostly have the same width and height.
+ *      (2) This finds the median width and height, as well as separate
+ *          median widths and heights of even and odd boxes.  It also
+ *          generates arrays that give the difference in width and height
+ *          of each box from the median, which can be used to correct
+ *          individual boxes.
+ *      (3) All return values are optional.
+ * 
+ */ +l_ok +boxaMedianDimensions(BOXA *boxas, + l_int32 *pmedw, + l_int32 *pmedh, + l_int32 *pmedwe, + l_int32 *pmedwo, + l_int32 *pmedhe, + l_int32 *pmedho, + NUMA **pnadelw, + NUMA **pnadelh) +{ +l_int32 i, n, bw, bh, medw, medh, medwe, medwo, medhe, medho; +BOXA *boxae, *boxao; +NUMA *nadelw, *nadelh; + + PROCNAME("boxaMedianDimensions"); + + if (pmedw) *pmedw = 0; + if (pmedh) *pmedh = 0; + if (pmedwe) *pmedwe= 0; + if (pmedwo) *pmedwo= 0; + if (pmedhe) *pmedhe= 0; + if (pmedho) *pmedho= 0; + if (pnadelw) *pnadelw = NULL; + if (pnadelh) *pnadelh = NULL; + if (!boxas) + return ERROR_INT("boxas not defined", procName, 1); + if (boxaGetValidCount(boxas) < 6) + return ERROR_INT("need at least 6 valid boxes", procName, 1); + + /* Require at least 3 valid boxes of both types */ + boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); + if (boxaGetValidCount(boxae) < 3 || boxaGetValidCount(boxao) < 3) { + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return ERROR_INT("don't have 3+ valid boxes of each type", procName, 1); + } + + /* Get the relevant median widths and heights */ + boxaGetMedianVals(boxas, NULL, NULL, NULL, NULL, &medw, &medh); + boxaGetMedianVals(boxae, NULL, NULL, NULL, NULL, &medwe, &medhe); + boxaGetMedianVals(boxao, NULL, NULL, NULL, NULL, &medwo, &medho); + if (pmedw) *pmedw = medw; + if (pmedh) *pmedh = medh; + if (pmedwe) *pmedwe = medwe; + if (pmedwo) *pmedwo = medwo; + if (pmedhe) *pmedhe = medhe; + if (pmedho) *pmedho = medho; + + /* Find the variation from median dimension for each box */ + n = boxaGetCount(boxas); + nadelw = numaCreate(n); + nadelh = numaCreate(n); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw, &bh); + if (bw == 0 || bh == 0) { /* invalid box */ + numaAddNumber(nadelw, 0); + numaAddNumber(nadelh, 0); + } else { + numaAddNumber(nadelw, bw - medw); + numaAddNumber(nadelh, bh - medh); + } + } + if (pnadelw) + *pnadelw = nadelw; + else + numaDestroy(&nadelw); + if (pnadelh) + *pnadelh = nadelh; + else + numaDestroy(&nadelh); + + boxaDestroy(&boxae); + boxaDestroy(&boxao); + return 0; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/bytearray.c b/hgdriver/3rdparty/hgOCR/leptonica/bytearray.c new file mode 100644 index 0000000..62a9184 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/bytearray.c @@ -0,0 +1,637 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file bytearray.c + *
+ *
+ *   Functions for handling byte arrays, in analogy with C++ 'strings'
+ *
+ *      Creation, copy, clone, destruction
+ *           L_BYTEA      *l_byteaCreate()
+ *           L_BYTEA      *l_byteaInitFromMem()
+ *           L_BYTEA      *l_byteaInitFromFile()
+ *           L_BYTEA      *l_byteaInitFromStream()
+ *           L_BYTEA      *l_byteaCopy()
+ *           void          l_byteaDestroy()
+ *
+ *      Accessors
+ *           size_t        l_byteaGetSize()
+ *           l_uint8      *l_byteaGetData()
+ *           l_uint8      *l_byteaCopyData()
+ *
+ *      Appending
+ *           l_int32       l_byteaAppendData()
+ *           l_int32       l_byteaAppendString()
+ *           static l_int32  l_byteaExtendArrayToSize()
+ *
+ *      Join/Split
+ *           l_int32       l_byteaJoin()
+ *           l_int32       l_byteaSplit()
+ *
+ *      Search
+ *           l_int32       l_byteaFindEachSequence()
+ *
+ *      Output to file
+ *           l_int32       l_byteaWrite()
+ *           l_int32       l_byteaWriteStream()
+ *
+ *   The internal data array is always null-terminated, for ease of use
+ *   in the event that it is an ascii string without null bytes.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on array size */ +static const l_uint32 MaxArraySize = 1000000000; /* 10^9 bytes */ +static const l_int32 InitialArraySize = 200; /*!< n'importe quoi */ + + /* Static function */ +static l_int32 l_byteaExtendArrayToSize(L_BYTEA *ba, size_t size); + + +/*---------------------------------------------------------------------* + * Creation, copy, clone, destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaCreate() + * + * \param[in] nbytes determines initial size of data array + * \return l_bytea, or NULL on error + * + *
+ * Notes:
+ *      (1) The allocated array is n + 1 bytes.  This allows room
+ *          for null termination.
+ * 
+ */ +L_BYTEA * +l_byteaCreate(size_t nbytes) +{ +L_BYTEA *ba; + + PROCNAME("l_byteaCreate"); + + if (nbytes <= 0 || nbytes > MaxArraySize) + nbytes = InitialArraySize; + ba = (L_BYTEA *)LEPT_CALLOC(1, sizeof(L_BYTEA)); + ba->data = (l_uint8 *)LEPT_CALLOC(nbytes + 1, sizeof(l_uint8)); + if (!ba->data) { + l_byteaDestroy(&ba); + return (L_BYTEA *)ERROR_PTR("ba array not made", procName, NULL); + } + ba->nalloc = nbytes + 1; + ba->refcount = 1; + return ba; +} + + +/*! + * \brief l_byteaInitFromMem() + * + * \param[in] data to be copied to the array + * \param[in] size amount of data + * \return l_bytea, or NULL on error + */ +L_BYTEA * +l_byteaInitFromMem(const l_uint8 *data, + size_t size) +{ +L_BYTEA *ba; + + PROCNAME("l_byteaInitFromMem"); + + if (!data) + return (L_BYTEA *)ERROR_PTR("data not defined", procName, NULL); + if (size <= 0) + return (L_BYTEA *)ERROR_PTR("no bytes to initialize", procName, NULL); + if (size > MaxArraySize) + return (L_BYTEA *)ERROR_PTR("size is too big", procName, NULL); + + if ((ba = l_byteaCreate(size)) == NULL) + return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); + memcpy(ba->data, data, size); + ba->size = size; + return ba; +} + + +/*! + * \brief l_byteaInitFromFile() + * + * \param[in] fname + * \return l_bytea, or NULL on error + */ +L_BYTEA * +l_byteaInitFromFile(const char *fname) +{ +FILE *fp; +L_BYTEA *ba; + + PROCNAME("l_byteaInitFromFile"); + + if (!fname) + return (L_BYTEA *)ERROR_PTR("fname not defined", procName, NULL); + + if ((fp = fopenReadStream(fname)) == NULL) + return (L_BYTEA *)ERROR_PTR("file stream not opened", procName, NULL); + ba = l_byteaInitFromStream(fp); + fclose(fp); + if (!ba) + return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); + return ba; +} + + +/*! + * \brief l_byteaInitFromStream() + * + * \param[in] fp file stream + * \return l_bytea, or NULL on error + */ +L_BYTEA * +l_byteaInitFromStream(FILE *fp) +{ +l_uint8 *data; +size_t nbytes; +L_BYTEA *ba; + + PROCNAME("l_byteaInitFromStream"); + + if (!fp) + return (L_BYTEA *)ERROR_PTR("stream not defined", procName, NULL); + + if ((data = l_binaryReadStream(fp, &nbytes)) == NULL) + return (L_BYTEA *)ERROR_PTR("data not read", procName, NULL); + if ((ba = l_byteaCreate(nbytes)) == NULL) { + LEPT_FREE(data); + return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); + } + memcpy(ba->data, data, nbytes); + ba->size = nbytes; + LEPT_FREE(data); + return ba; +} + + +/*! + * \brief l_byteaCopy() + * + * \param[in] bas source lba + * \param[in] copyflag L_COPY, L_CLONE + * \return clone or copy of bas, or NULL on error + * + *
+ * Notes:
+ *      (1) If cloning, up the refcount and return a ptr to %bas.
+ * 
+ */ +L_BYTEA * +l_byteaCopy(L_BYTEA *bas, + l_int32 copyflag) +{ + PROCNAME("l_byteaCopy"); + + if (!bas) + return (L_BYTEA *)ERROR_PTR("bas not defined", procName, NULL); + + if (copyflag == L_CLONE) { + bas->refcount++; + return bas; + } + + return l_byteaInitFromMem(bas->data, bas->size); +} + + +/*! + * \brief l_byteaDestroy() + * + * \param[in,out] pba will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the lba.
+ *      (2) Always nulls the input ptr.
+ *      (3) If the data has been previously removed, the lba will
+ *          have been nulled, so this will do nothing.
+ * 
+ */ +void +l_byteaDestroy(L_BYTEA **pba) +{ +L_BYTEA *ba; + + PROCNAME("l_byteaDestroy"); + + if (pba == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((ba = *pba) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the lba. */ + ba->refcount--; + if (ba->refcount <= 0) { + if (ba->data) LEPT_FREE(ba->data); + LEPT_FREE(ba); + } + + *pba = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaGetSize() + * + * \param[in] ba + * \return size of stored byte array, or 0 on error + */ +size_t +l_byteaGetSize(L_BYTEA *ba) +{ + PROCNAME("l_byteaGetSize"); + + if (!ba) + return ERROR_INT("ba not defined", procName, 0); + return ba->size; +} + + +/*! + * \brief l_byteaGetData() + * + * \param[in] ba + * \param[out] psize size of data in lba + * \return ptr to existing data array, or NULL on error + * + *
+ * Notes:
+ *      (1) The returned ptr is owned by %ba.  Do not free it!
+ * 
+ */ +l_uint8 * +l_byteaGetData(L_BYTEA *ba, + size_t *psize) +{ + PROCNAME("l_byteaGetData"); + + if (!ba) + return (l_uint8 *)ERROR_PTR("ba not defined", procName, NULL); + if (!psize) + return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); + + *psize = ba->size; + return ba->data; +} + + +/*! + * \brief l_byteaCopyData() + * + * \param[in] ba + * \param[out] psize size of data in lba + * \return copy of data in use in the data array, or NULL on error. + * + *
+ * Notes:
+ *      (1) The returned data is owned by the caller.  The input %ba
+ *          still owns the original data array.
+ * 
+ */ +l_uint8 * +l_byteaCopyData(L_BYTEA *ba, + size_t *psize) +{ +l_uint8 *data; + + PROCNAME("l_byteaCopyData"); + + if (!psize) + return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); + *psize = 0; + if (!ba) + return (l_uint8 *)ERROR_PTR("ba not defined", procName, NULL); + + data = l_byteaGetData(ba, psize); + return l_binaryCopy(data, *psize); +} + + +/*---------------------------------------------------------------------* + * Appending * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaAppendData() + * + * \param[in] ba + * \param[in] newdata byte array to be appended + * \param[in] newbytes size of data array + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaAppendData(L_BYTEA *ba, + const l_uint8 *newdata, + size_t newbytes) +{ +size_t size, nalloc, reqsize; + + PROCNAME("l_byteaAppendData"); + + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + if (!newdata) + return ERROR_INT("newdata not defined", procName, 1); + + size = l_byteaGetSize(ba); + reqsize = size + newbytes + 1; + nalloc = ba->nalloc; + if (nalloc < reqsize) + l_byteaExtendArrayToSize(ba, 2 * reqsize); + + memcpy(ba->data + size, newdata, newbytes); + ba->size += newbytes; + return 0; +} + + +/*! + * \brief l_byteaAppendString() + * + * \param[in] ba + * \param[in] str null-terminated string to be appended + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaAppendString(L_BYTEA *ba, + const char *str) +{ +size_t size, len, nalloc, reqsize; + + PROCNAME("l_byteaAppendString"); + + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + if (!str) + return ERROR_INT("str not defined", procName, 1); + + size = l_byteaGetSize(ba); + len = strlen(str); + reqsize = size + len + 1; + nalloc = ba->nalloc; + if (nalloc < reqsize) + l_byteaExtendArrayToSize(ba, 2 * reqsize); + + memcpy(ba->data + size, str, len); + ba->size += len; + return 0; +} + + +/*! + * \brief l_byteaExtendArrayToSize() + * + * \param[in] ba + * \param[in] size new size of lba data array + * \return 0 if OK; 1 on error + */ +static l_int32 +l_byteaExtendArrayToSize(L_BYTEA *ba, + size_t size) +{ + PROCNAME("l_byteaExtendArrayToSize"); + + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + + if (size > ba->nalloc) { + if ((ba->data = + (l_uint8 *)reallocNew((void **)&ba->data, ba->nalloc, size)) + == NULL) + return ERROR_INT("new array not returned", procName, 1); + ba->nalloc = size; + } + return 0; +} + + +/*---------------------------------------------------------------------* + * String join/split * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaJoin() + * + * \param[in] ba1 + * \param[in,out] pba2 data array is added to the one in ba1; + * then ba2 is destroyed and its pointer is nulled. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is a no-op, not an error, for %ba2 to be null.
+ * 
+ */ +l_ok +l_byteaJoin(L_BYTEA *ba1, + L_BYTEA **pba2) +{ +l_uint8 *data2; +size_t nbytes2; +L_BYTEA *ba2; + + PROCNAME("l_byteaJoin"); + + if (!ba1) + return ERROR_INT("ba1 not defined", procName, 1); + if (!pba2) + return ERROR_INT("&ba2 not defined", procName, 1); + if ((ba2 = *pba2) == NULL) return 0; + + data2 = l_byteaGetData(ba2, &nbytes2); + l_byteaAppendData(ba1, data2, nbytes2); + + l_byteaDestroy(pba2); + return 0; +} + + +/*! + * \brief l_byteaSplit() + * + * \param[in] ba1 lba to split; array bytes nulled beyond the split loc + * \param[in] splitloc location in ba1 to split; ba2 begins there + * \param[out] pba2 with data starting at splitloc + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaSplit(L_BYTEA *ba1, + size_t splitloc, + L_BYTEA **pba2) +{ +l_uint8 *data1; +size_t nbytes1, nbytes2; + + PROCNAME("l_byteaSplit"); + + if (!pba2) + return ERROR_INT("&ba2 not defined", procName, 1); + *pba2 = NULL; + if (!ba1) + return ERROR_INT("ba1 not defined", procName, 1); + + data1 = l_byteaGetData(ba1, &nbytes1); + if (splitloc >= nbytes1) + return ERROR_INT("splitloc invalid", procName, 1); + nbytes2 = nbytes1 - splitloc; + + /* Make the new lba */ + *pba2 = l_byteaInitFromMem(data1 + splitloc, nbytes2); + + /* Null the removed bytes in the input lba */ + memset(data1 + splitloc, 0, nbytes2); + ba1->size = splitloc; + return 0; +} + + +/*---------------------------------------------------------------------* + * Search * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaFindEachSequence() + * + * \param[in] ba + * \param[in] sequence subarray of bytes to find in data + * \param[in] seqlen length of sequence, in bytes + * \param[out] pda byte positions of each occurrence of %sequence + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaFindEachSequence(L_BYTEA *ba, + const l_uint8 *sequence, + size_t seqlen, + L_DNA **pda) +{ +l_uint8 *data; +size_t size; + + PROCNAME("l_byteaFindEachSequence"); + + if (!pda) + return ERROR_INT("&da not defined", procName, 1); + *pda = NULL; + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + if (!sequence) + return ERROR_INT("sequence not defined", procName, 1); + + data = l_byteaGetData(ba, &size); + *pda = arrayFindEachSequence(data, size, sequence, seqlen); + return 0; +} + + +/*---------------------------------------------------------------------* + * Output to file * + *---------------------------------------------------------------------*/ +/*! + * \brief l_byteaWrite() + * + * \param[in] fname output file + * \param[in] ba + * \param[in] startloc first byte to output + * \param[in] nbytes number of bytes to write; use 0 to write to + * the end of the data array + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaWrite(const char *fname, + L_BYTEA *ba, + size_t startloc, + size_t nbytes) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("l_byteaWrite"); + + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + + if ((fp = fopenWriteStream(fname, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = l_byteaWriteStream(fp, ba, startloc, nbytes); + fclose(fp); + return ret; +} + + +/*! + * \brief l_byteaWriteStream() + * + * \param[in] fp file stream opened for binary write + * \param[in] ba + * \param[in] startloc first byte to output + * \param[in] nbytes number of bytes to write; use 0 to write to + * the end of the data array + * \return 0 if OK, 1 on error + */ +l_ok +l_byteaWriteStream(FILE *fp, + L_BYTEA *ba, + size_t startloc, + size_t nbytes) +{ +l_uint8 *data; +size_t size, maxbytes; + + PROCNAME("l_byteaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!ba) + return ERROR_INT("ba not defined", procName, 1); + + data = l_byteaGetData(ba, &size); + if (startloc >= size) + return ERROR_INT("invalid startloc", procName, 1); + maxbytes = size - startloc; + nbytes = (nbytes == 0) ? maxbytes : L_MIN(nbytes, maxbytes); + + fwrite(data + startloc, 1, nbytes, fp); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ccbord.c b/hgdriver/3rdparty/hgOCR/leptonica/ccbord.c new file mode 100644 index 0000000..525d922 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ccbord.c @@ -0,0 +1,2617 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file ccbord.c + *
+ *
+ *     CCBORDA and CCBORD creation and destruction
+ *         CCBORDA         *ccbaCreate()
+ *         void            *ccbaDestroy()
+ *         CCBORD          *ccbCreate()
+ *         void            *ccbDestroy()
+ *
+ *     CCBORDA addition
+ *         l_int32          ccbaAddCcb()
+ *         static l_int32   ccbaExtendArray()
+ *
+ *     CCBORDA accessors
+ *         l_int32          ccbaGetCount()
+ *         l_int32          ccbaGetCcb()
+ *
+ *     Top-level border-finding routines
+ *         CCBORDA         *pixGetAllCCBorders()
+ *         static CCBORD   *pixGetCCBorders()
+ *         PTAA            *pixGetOuterBordersPtaa()
+ *         static PTA      *pixGetOuterBorderPta()
+ *
+ *     Lower-level border location routines
+ *         PTAA            *pixGetOuterBorder()
+ *         static l_int32   pixGetHoleBorder()
+ *         static l_int32   findNextBorderPixel()
+ *         static void      locateOutsideSeedPixel()
+ *
+ *     Border conversions
+ *         l_int32          ccbaGenerateGlobalLocs()
+ *         l_int32          ccbaGenerateStepChains()
+ *         l_int32          ccbaStepChainsToPixCoords()
+ *         l_int32          ccbaGenerateSPGlobalLocs()
+ *
+ *     Conversion to single path
+ *         l_int32          ccbaGenerateSinglePath()
+ *         PTA             *getCutPathForHole()
+ *
+ *     Border and full image rendering
+ *         PIX             *ccbaDisplayBorder()
+ *         PIX             *ccbaDisplaySPBorder()
+ *         PIX             *ccbaDisplayImage1()
+ *         PIX             *ccbaDisplayImage2()
+ *
+ *     Serialize for I/O
+ *         l_int32          ccbaWrite()
+ *         l_int32          ccbaWriteStream()
+ *         l_int32          ccbaRead()
+ *         l_int32          ccbaReadStream()
+ *
+ *     SVG output
+ *         l_int32          ccbaWriteSVG()
+ *         char            *ccbaWriteSVGString()
+ *
+ *
+ *     Border finding is tricky because components can have
+ *     holes, which also need to be traced out.  The outer
+ *     border can be connected with all the hole borders,
+ *     so that there is a single border for each component.
+ *     [Alternatively, the connecting paths can be eliminated if
+ *     you're willing to have a set of borders for each
+ *     component (an exterior border and some number of
+ *     interior ones), with "line to" operations tracing
+ *     out each border and "move to" operations going from
+ *     one border to the next.]
+ *
+ *     Here's the plan.  We get the pix for each connected
+ *     component, and trace its exterior border.  We then
+ *     find the holes (if any) in the pix, and separately
+ *     trace out their borders, all using the same
+ *     border-following rule that has ON pixels on the right
+ *     side of the path.
+ *
+ *     [For svg, we may want to turn each set of borders for a c.c.
+ *     into a closed path.  This can be done by tunnelling
+ *     through the component from the outer border to each of the
+ *     holes, going in and coming out along the same path so
+ *     the connection will be invisible in any rendering
+ *     (display or print) from the outline.  The result is a
+ *     closed path, where the outside border is traversed
+ *     cw and each hole is traversed ccw.  The svg renderer
+ *     is assumed to handle these closed borders properly.]
+ *
+ *     Each border is a closed path that is traversed in such
+ *     a way that the stuff inside the c.c. is on the right
+ *     side of the traveller.  The border of a singly-connected
+ *     component is thus traversed cw, and the border of the
+ *     holes inside a c.c. are traversed ccw.  Suppose we have
+ *     a list of all the borders of each c.c., both the cw and ccw
+ *     traversals.  How do we reconstruct the image?
+ *
+ *   Reconstruction:
+ *
+ *     Method 1.  Topological method using connected components.
+ *     We have closed borders composed of cw border pixels for the
+ *     exterior of c.c. and ccw border pixels for the interior (holes)
+ *     in the c.c.
+ *         (a) Initialize the destination to be OFF.  Then,
+ *             in any order:
+ *         (b) Fill the components within and including the cw borders,
+ *             and sequentially XOR them onto the destination.
+ *         (c) Fill the components within but not including the ccw
+ *             borders and sequentially XOR them onto the destination.
+ *     The components that are XOR'd together can be generated as follows:
+ *         (a) For each closed cw path, use pixFillClosedBorders():
+ *               (1) Turn on the path pixels in a subimage that
+ *                   minimally supports the border.
+ *               (2) Do a 4-connected fill from a seed of 1 pixel width
+ *                   on the border, using the inverted image in (1) as
+ *                   a filling mask.
+ *               (3) Invert the fill result: this gives the component
+ *                   including the exterior cw path, with all holes
+ *                   filled.
+ *         (b) For each closed ccw path (hole):
+ *               (1) Turn on the path pixels in a subimage that minimally
+ *                   supports the path.
+ *               (2) Find a seed pixel on the inside of this path.
+ *               (3) Do a 4-connected fill from this seed pixel, using
+ *                   the inverted image of the path in (1) as a filling
+ *                   mask.
+ *
+ *     ------------------------------------------------------
+ *
+ *     Method 2.  A variant of Method 1.  Topological.
+ *     In Method 1, we treat the exterior border differently from
+ *     the interior (hole) borders.  Here, all borders in a c.c.
+ *     are treated equally:
+ *         (1) Start with a pix with a 1 pixel OFF boundary
+ *             enclosing all the border pixels of the c.c.
+ *             This is the filling mask.
+ *         (2) Make a seed image of the same size as follows:  for
+ *             each border, put one seed pixel OUTSIDE the border
+ *             (where OUTSIDE is determined by the inside/outside
+ *             convention for borders).
+ *         (3) Seedfill into the seed image, filling in the regions
+ *             determined by the filling mask.  The fills are clipped
+ *             by the border pixels.
+ *         (4) Inverting this, we get the c.c. properly filled,
+ *             with the holes empty!
+ *         (5) Rasterop using XOR the filled c.c. (but not the 1
+ *             pixel boundary) into the full dest image.
+ *
+ *     Method 2 is about 1.2x faster than Method 1 on text images,
+ *     and about 2x faster on complex images (e.g., with halftones).
+ *
+ *     ------------------------------------------------------
+ *
+ *     Method 3.  The traditional way to fill components delineated
+ *     by boundaries is through scan line conversion.  It's a bit
+ *     tricky, and I have not yet tried to implement it.
+ *
+ *     ------------------------------------------------------
+ *
+ *     Method 4.  [Nota Bene: this method probably doesn't work, and
+ *     won't be implemented.  If I get a more traditional scan line
+ *     conversion algorithm working, I'll erase these notes.]
+ *     Render all border pixels on a destination image,
+ *     which will be the final result after scan conversion.  Assign
+ *     a value 1 to pixels on cw paths, 2 to pixels on ccw paths,
+ *     and 3 to pixels that are on both paths.  Each of the paths
+ *     is an 8-connected component.  Now scan across each raster
+ *     line.  The attempt is to make rules for each scan line
+ *     that are independent of neighboring scanlines.  Here are
+ *     a set of rules for writing ON pixels on a destination raster image:
+ *
+ *         (a) The rasterizer will be in one of two states: ON and OFF.
+ *         (b) Start each line in the OFF state.  In the OFF state,
+ *             skip pixels until you hit a path of any type.  Turn
+ *             the path pixel ON.
+ *         (c) If the state is ON, each pixel you encounter will
+ *             be turned on, until and including hitting a path pixel.
+ *         (d) When you hit a path pixel, if the path does NOT cut
+ *             through the line, so that there is not an 8-cc path
+ *             pixel (of any type) both above and below, the state
+ *             is unchanged (it stays either ON or OFF).
+ *         (e) If the path does cut through, but with a possible change
+ *             of pixel type, then we decide whether or
+ *             not to toggle the state based on the values of the
+ *             path pixel and the path pixels above and below:
+ *               (1) if a 1 path cuts through, toggle;
+ *               (1) if a 2 path cuts through, toggle;
+ *               (3) if a 3 path cuts through, do not toggle;
+ *               (4) if on one side a 3 touches both a 1 and a 2, use the 2
+ *               (5) if a 3 has any 1 neighbors, toggle; else if it has
+ *                   no 1 neighbors, do not toggle;
+ *               (6) if a 2 has any neighbors that are 1 or 3,
+ *                   do not toggle
+ *               (7) if a 1 has neighbors 1 and x (x = 2 or 3),
+ *                   toggle
+ *
+ *
+ *     To visualize how these rules work, consider the following
+ *     component with border pixels labeled according to the scheme
+ *     above.  We also show the values of the interior pixels
+ *     (w=OFF, b=ON), but these of course must be inferred properly
+ *     from the rules above:
+ *
+ *                     3
+ *                  3  w  3             1  1  1
+ *                  1  2  1          1  b  2  b  1
+ *                  1  b  1             3  w  2  1
+ *                  3  b  1          1  b  2  b  1
+ *               3  w  3                1  1  1
+ *               3  w  3
+ *            1  b  2  b  1
+ *            1  2  w  2  1
+ *         1  b  2  w  2  b  1
+ *            1  2  w  2  1
+ *               1  2  b  1
+ *               1  b  1
+ *                  1
+ *
+ *
+ *     Even if this works, which is unlikely, it will certainly be
+ *     slow because decisions have to be made on a pixel-by-pixel
+ *     basis when encountering borders.
+ *
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + +static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */ + + /* In ccbaGenerateSinglePath(): don't save holes + * in c.c. with ridiculously many small holes */ +static const l_int32 NMAX_HOLES = 150; + + /* Tables used to trace the border. + * - The 8 pixel positions of neighbors Q are labeled clockwise + * starting from the west: + * 1 2 3 + * 0 P 4 + * 7 6 5 + * where the labels are the index offset [0, ... 7] of Q relative to P. + * - xpostab[] and ypostab[] give the actual x and y pixel offsets + * of Q relative to P, indexed by the index offset. + * - qpostab[pos] gives the new index offset of Q relative to P, at + * the time that a new P has been chosen to be in index offset + * position 'pos' relative to the previous P. The relation + * between P and Q is always 4-connected. */ +static const l_int32 xpostab[] = {-1, -1, 0, 1, 1, 1, 0, -1}; +static const l_int32 ypostab[] = {0, -1, -1, -1, 0, 1, 1, 1}; +static const l_int32 qpostab[] = {6, 6, 0, 0, 2, 2, 4, 4}; + + /* Static functions */ +static l_int32 ccbaExtendArray(CCBORDA *ccba); +static CCBORD *pixGetCCBorders(PIX *pixs, BOX *box); +static PTA *pixGetOuterBorderPta(PIX *pixs, BOX *box); +static l_ok pixGetHoleBorder(CCBORD *ccb, PIX *pixs, BOX *box, + l_int32 xs, l_int32 ys); +static l_int32 findNextBorderPixel(l_int32 w, l_int32 h, l_uint32 *data, + l_int32 wpl, l_int32 px, l_int32 py, + l_int32 *pqpos, l_int32 *pnpx, + l_int32 *pnpy); +static void locateOutsideSeedPixel(l_int32 fpx, l_int32 fpy, l_int32 spx, + l_int32 spy, l_int32 *pxs, l_int32 *pys); + +#ifndef NO_CONSOLE_IO +#define DEBUG_PRINT 0 +#endif /* NO CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * ccba and ccb creation and destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaCreate() + * + * \param[in] pixs 1 bpp; can be null + * \param[in] n initial number of ptrs + * \return ccba, or NULL on error + */ +CCBORDA * +ccbaCreate(PIX *pixs, + l_int32 n) +{ +CCBORDA *ccba; + + PROCNAME("ccbaCreate"); + + if (n <= 0) + n = INITIAL_PTR_ARRAYSIZE; + + ccba = (CCBORDA *)LEPT_CALLOC(1, sizeof(CCBORDA)); + if (pixs) { + ccba->pix = pixClone(pixs); + ccba->w = pixGetWidth(pixs); + ccba->h = pixGetHeight(pixs); + } + ccba->n = 0; + ccba->nalloc = n; + if ((ccba->ccb = (CCBORD **)LEPT_CALLOC(n, sizeof(CCBORD *))) == NULL) { + ccbaDestroy(&ccba); + return (CCBORDA *)ERROR_PTR("ccba ptrs not made", procName, NULL); + } + return ccba; +} + + +/*! + * \brief ccbaDestroy() + * + * \param[in,out] pccba will be set to null befoe returning + * \return void + */ +void +ccbaDestroy(CCBORDA **pccba) +{ +l_int32 i; +CCBORDA *ccba; + + PROCNAME("ccbaDestroy"); + + if (pccba == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((ccba = *pccba) == NULL) + return; + + pixDestroy(&ccba->pix); + for (i = 0; i < ccba->n; i++) + ccbDestroy(&ccba->ccb[i]); + LEPT_FREE(ccba->ccb); + LEPT_FREE(ccba); + *pccba = NULL; + return; +} + + +/*! + * \brief ccbCreate() + * + * \param[in] pixs [optional]; can be null + * \return ccb or NULL on error + */ +CCBORD * +ccbCreate(PIX *pixs) +{ +BOXA *boxa; +CCBORD *ccb; +PTA *start; +PTAA *local; + + PROCNAME("ccbCreate"); + + if (pixs) { + if (pixGetDepth(pixs) != 1) + return (CCBORD *)ERROR_PTR("pixs not binary", procName, NULL); + } + + if ((ccb = (CCBORD *)LEPT_CALLOC(1, sizeof(CCBORD))) == NULL) + return (CCBORD *)ERROR_PTR("ccb not made", procName, NULL); + ccb->refcount++; + if (pixs) + ccb->pix = pixClone(pixs); + if ((boxa = boxaCreate(1)) == NULL) + return (CCBORD *)ERROR_PTR("boxa not made", procName, NULL); + ccb->boxa = boxa; + if ((start = ptaCreate(1)) == NULL) + return (CCBORD *)ERROR_PTR("start pta not made", procName, NULL); + ccb->start = start; + if ((local = ptaaCreate(1)) == NULL) + return (CCBORD *)ERROR_PTR("local ptaa not made", procName, NULL); + ccb->local = local; + + return ccb; +} + + +/*! + * \brief ccbDestroy() + * + * \param[in,out] pccb will be set to null before returning + * \return void + */ +void +ccbDestroy(CCBORD **pccb) +{ +CCBORD *ccb; + + PROCNAME("ccbDestroy"); + + if (pccb == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((ccb = *pccb) == NULL) + return; + + ccb->refcount--; + if (ccb->refcount == 0) { + if (ccb->pix) + pixDestroy(&ccb->pix); + if (ccb->boxa) + boxaDestroy(&ccb->boxa); + if (ccb->start) + ptaDestroy(&ccb->start); + if (ccb->local) + ptaaDestroy(&ccb->local); + if (ccb->global) + ptaaDestroy(&ccb->global); + if (ccb->step) + numaaDestroy(&ccb->step); + if (ccb->splocal) + ptaDestroy(&ccb->splocal); + if (ccb->spglobal) + ptaDestroy(&ccb->spglobal); + LEPT_FREE(ccb); + *pccb = NULL; + } + return; +} + + +/*---------------------------------------------------------------------* + * ccba addition * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaAddCcb() + * + * \param[in] ccba + * \param[in] ccb to be added by insertion + * \return 0 if OK; 1 on error + */ +l_ok +ccbaAddCcb(CCBORDA *ccba, + CCBORD *ccb) +{ +l_int32 n; + + PROCNAME("ccbaAddCcb"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + if (!ccb) + return ERROR_INT("ccb not defined", procName, 1); + + n = ccbaGetCount(ccba); + if (n >= ccba->nalloc) + ccbaExtendArray(ccba); + ccba->ccb[n] = ccb; + ccba->n++; + return 0; +} + + +/*! + * \brief ccbaExtendArray() + * + * \param[in] ccba + * \return 0 if OK; 1 on error + */ +static l_int32 +ccbaExtendArray(CCBORDA *ccba) +{ + PROCNAME("ccbaExtendArray"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + if ((ccba->ccb = (CCBORD **)reallocNew((void **)&ccba->ccb, + sizeof(CCBORD *) * ccba->nalloc, + 2 * sizeof(CCBORD *) * ccba->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + ccba->nalloc = 2 * ccba->nalloc; + return 0; +} + + + +/*---------------------------------------------------------------------* + * ccba accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaGetCount() + * + * \param[in] ccba + * \return count, with 0 on error + */ +l_int32 +ccbaGetCount(CCBORDA *ccba) +{ + + PROCNAME("ccbaGetCount"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 0); + + return ccba->n; +} + + +/*! + * \brief ccbaGetCcb() + * + * \param[in] ccba + * \param[in] index + * \return ccb, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a clone of the ccb; it must be destroyed
+ * 
+ */ +CCBORD * +ccbaGetCcb(CCBORDA *ccba, + l_int32 index) +{ +CCBORD *ccb; + + PROCNAME("ccbaGetCcb"); + + if (!ccba) + return (CCBORD *)ERROR_PTR("ccba not defined", procName, NULL); + if (index < 0 || index >= ccba->n) + return (CCBORD *)ERROR_PTR("index out of bounds", procName, NULL); + + ccb = ccba->ccb[index]; + ccb->refcount++; + return ccb; +} + + + +/*---------------------------------------------------------------------* + * Top-level border-finding routines * + *---------------------------------------------------------------------*/ +/*! + * \brief pixGetAllCCBorders() + * + * \param[in] pixs 1 bpp + * \return ccborda, or NULL on error + */ +CCBORDA * +pixGetAllCCBorders(PIX *pixs) +{ +l_int32 n, i; +BOX *box; +BOXA *boxa; +CCBORDA *ccba; +CCBORD *ccb; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixGetAllCCBorders"); + + if (!pixs) + return (CCBORDA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (CCBORDA *)ERROR_PTR("pixs not binary", procName, NULL); + + if ((boxa = pixConnComp(pixs, &pixa, 8)) == NULL) + return (CCBORDA *)ERROR_PTR("boxa not made", procName, NULL); + n = boxaGetCount(boxa); + + if ((ccba = ccbaCreate(pixs, n)) == NULL) { + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return (CCBORDA *)ERROR_PTR("ccba not made", procName, NULL); + } + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { + ccbaDestroy(&ccba); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + return (CCBORDA *)ERROR_PTR("pix not found", procName, NULL); + } + if ((box = pixaGetBox(pixa, i, L_CLONE)) == NULL) { + ccbaDestroy(&ccba); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + pixDestroy(&pix); + return (CCBORDA *)ERROR_PTR("box not found", procName, NULL); + } + ccb = pixGetCCBorders(pix, box); + pixDestroy(&pix); + boxDestroy(&box); + if (!ccb) { + ccbaDestroy(&ccba); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + return (CCBORDA *)ERROR_PTR("ccb not made", procName, NULL); + } +/* ptaWriteStream(stderr, ccb->local, 1); */ + ccbaAddCcb(ccba, ccb); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return ccba; +} + + +/*! + * \brief pixGetCCBorders() + * + * \param[in] pixs 1 bpp, one 8-connected component + * \param[in] box of %pixs, in global coords + * \return ccbord, or NULL on error + * + *
+ * Notes:
+ *      (1) We are finding the exterior and interior borders
+ *          of an 8-connected component.   This should be used
+ *          on a pix that has exactly one 8-connected component.
+ *      (2) Typically, pixs is a c.c. in some larger pix.  The
+ *          input box gives its location in global coordinates.
+ *          This box is saved, as well as the boxes for the
+ *          borders of any holes within the c.c., but the latter
+ *          are given in relative coords within the c.c.
+ *      (3) The calculations for the exterior border are done
+ *          on a pix with a 1-pixel
+ *          added border, but the saved pixel coordinates
+ *          are the correct (relative) ones for the input pix
+ *          (without a 1-pixel border)
+ *      (4) For the definition of the three tables -- xpostab[], ypostab[]
+ *          and qpostab[] -- see above where they are defined.
+ * 
+ */ +static CCBORD * +pixGetCCBorders(PIX *pixs, + BOX *box) +{ +l_int32 allzero, i, x, xh, w, nh; +l_int32 xs, ys; /* starting hole border pixel, relative in pixs */ +l_uint32 val; +BOX *boxt, *boxe; +BOXA *boxa; +CCBORD *ccb; +PIX *pixh; /* for hole components */ +PIX *pixt; +PIXA *pixa; + + PROCNAME("pixGetCCBorders"); + + if (!pixs) + return (CCBORD *)ERROR_PTR("pixs not defined", procName, NULL); + if (!box) + return (CCBORD *)ERROR_PTR("box not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (CCBORD *)ERROR_PTR("pixs not binary", procName, NULL); + + pixZero(pixs, &allzero); + if (allzero) + return (CCBORD *)ERROR_PTR("pixs all 0", procName, NULL); + + if ((ccb = ccbCreate(pixs)) == NULL) + return (CCBORD *)ERROR_PTR("ccb not made", procName, NULL); + + /* Get the exterior border */ + pixGetOuterBorder(ccb, pixs, box); + + /* Find the holes, if any */ + if ((pixh = pixHolesByFilling(pixs, 4)) == NULL) { + ccbDestroy(&ccb); + return (CCBORD *)ERROR_PTR("pixh not made", procName, NULL); + } + pixZero(pixh, &allzero); + if (allzero) { /* no holes */ + pixDestroy(&pixh); + return ccb; + } + + /* Get c.c. and locations of the holes */ + if ((boxa = pixConnComp(pixh, &pixa, 4)) == NULL) { + ccbDestroy(&ccb); + pixDestroy(&pixh); + return (CCBORD *)ERROR_PTR("boxa not made", procName, NULL); + } + nh = boxaGetCount(boxa); +/* fprintf(stderr, "%d holes\n", nh); */ + + /* For each hole, find an interior pixel within the hole, + * then march to the right and stop at the first border + * pixel. Save the bounding box of the border, which + * is 1 pixel bigger on each side than the bounding box + * of the hole itself. Note that we use a pix of the + * c.c. of the hole itself to be sure that we start + * with a pixel in the hole of the proper component. + * If we did everything from the parent component, it is + * possible to start in a different hole that is within + * the b.b. of a larger hole. */ + w = pixGetWidth(pixs); + for (i = 0; i < nh; i++) { + boxt = boxaGetBox(boxa, i, L_CLONE); + pixt = pixaGetPix(pixa, i, L_CLONE); + ys = boxt->y; /* there must be a hole pixel on this raster line */ + for (x = 0; x < boxt->w; x++) { /* look for (fg) hole pixel */ + pixGetPixel(pixt, x, 0, &val); + if (val == 1) { + xh = x; + break; + } + } + if (x == boxt->w) { + L_WARNING("no hole pixel found!\n", procName); + continue; + } + for (x = xh + boxt->x; x < w; x++) { /* look for (fg) border pixel */ + pixGetPixel(pixs, x, ys, &val); + if (val == 1) { + xs = x; + break; + } + } + boxe = boxCreate(boxt->x - 1, boxt->y - 1, boxt->w + 2, boxt->h + 2); +#if DEBUG_PRINT + boxPrintStreamInfo(stderr, box); + boxPrintStreamInfo(stderr, boxe); + fprintf(stderr, "xs = %d, ys = %d\n", xs, ys); +#endif /* DEBUG_PRINT */ + pixGetHoleBorder(ccb, pixs, boxe, xs, ys); + boxDestroy(&boxt); + boxDestroy(&boxe); + pixDestroy(&pixt); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + pixDestroy(&pixh); + return ccb; +} + + +/*! + * \brief pixGetOuterBordersPtaa() + * + * \param[in] pixs 1 bpp + * \return ptaa of outer borders, in global coords, or NULL on error + */ +PTAA * +pixGetOuterBordersPtaa(PIX *pixs) +{ +l_int32 i, n; +BOX *box; +BOXA *boxa; +PIX *pix; +PIXA *pixa; +PTA *pta; +PTAA *ptaa; + + PROCNAME("pixGetOuterBordersPtaa"); + + if (!pixs) + return (PTAA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PTAA *)ERROR_PTR("pixs not binary", procName, NULL); + + boxa = pixConnComp(pixs, &pixa, 8); + n = boxaGetCount(boxa); + if (n == 0) { + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return (PTAA *)ERROR_PTR("pixs empty", procName, NULL); + } + + ptaa = ptaaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pix = pixaGetPix(pixa, i, L_CLONE); + pta = pixGetOuterBorderPta(pix, box); + if (pta) + ptaaAddPta(ptaa, pta, L_INSERT); + boxDestroy(&box); + pixDestroy(&pix); + } + + pixaDestroy(&pixa); + boxaDestroy(&boxa); + return ptaa; +} + + +/*! + * \brief pixGetOuterBorderPta() + * + * \param[in] pixs 1 bpp, one 8-connected component + * \param[in] box [optional] of %pixs, in global coordinates + * \return pta of outer border, in global coords, or NULL on error + * + *
+ * Notes:
+ *      (1) We are finding the exterior border of a single 8-connected
+ *          component.
+ *      (2) If box is NULL, the outline returned is in the local coords
+ *          of the input pix.  Otherwise, box is assumed to give the
+ *          location of the pix in global coordinates, and the returned
+ *          pta will be in those global coordinates.
+ * 
+ */ +static PTA * +pixGetOuterBorderPta(PIX *pixs, + BOX *box) +{ +l_int32 allzero, x, y; +BOX *boxt; +CCBORD *ccb; +PTA *ptaloc, *ptad; + + PROCNAME("pixGetOuterBorderPta"); + + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PTA *)ERROR_PTR("pixs not binary", procName, NULL); + + pixZero(pixs, &allzero); + if (allzero) + return (PTA *)ERROR_PTR("pixs all 0", procName, NULL); + + if ((ccb = ccbCreate(pixs)) == NULL) + return (PTA *)ERROR_PTR("ccb not made", procName, NULL); + if (!box) + boxt = boxCreate(0, 0, pixGetWidth(pixs), pixGetHeight(pixs)); + else + boxt = boxClone(box); + + /* Get the exterior border in local coords */ + pixGetOuterBorder(ccb, pixs, boxt); + if ((ptaloc = ptaaGetPta(ccb->local, 0, L_CLONE)) == NULL) { + ccbDestroy(&ccb); + boxDestroy(&boxt); + return (PTA *)ERROR_PTR("ptaloc not made", procName, NULL); + } + + /* Transform to global coordinates, if they are given */ + if (box) { + boxGetGeometry(box, &x, &y, NULL, NULL); + ptad = ptaTransform(ptaloc, x, y, 1.0, 1.0); + } else { + ptad = ptaClone(ptaloc); + } + + ptaDestroy(&ptaloc); + boxDestroy(&boxt); + ccbDestroy(&ccb); + return ptad; +} + + +/*---------------------------------------------------------------------* + * Lower-level border-finding routines * + *---------------------------------------------------------------------*/ +/*! + * \brief pixGetOuterBorder() + * + * \param[in] ccb unfilled + * \param[in] pixs for the component at hand + * \param[in] box for the component, in global coords + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) the border is saved in relative coordinates within
+ *          the c.c. (pixs).  Because the calculation is done
+ *          in pixb with added 1 pixel border, we must subtract
+ *          1 from each pixel value before storing it.
+ *      (2) the stopping condition is that after the first pixel is
+ *          returned to, the next pixel is the second pixel.  Having
+ *          these 2 pixels recur in sequence proves the path is closed,
+ *          and we do not store the second pixel again.
+ * 
+ */ +l_ok +pixGetOuterBorder(CCBORD *ccb, + PIX *pixs, + BOX *box) +{ +l_int32 fpx, fpy, spx, spy, qpos; +l_int32 px, py, npx, npy; +l_int32 w, h, wpl; +l_uint32 *data; +PTA *pta; +PIX *pixb; /* with 1 pixel border */ + + PROCNAME("pixGetOuterBorder"); + + if (!ccb) + return ERROR_INT("ccb not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + /* Add 1-pixel border all around, and find start pixel */ + if ((pixb = pixAddBorder(pixs, 1, 0)) == NULL) + return ERROR_INT("pixs not made", procName, 1); + if (!nextOnPixelInRaster(pixb, 1, 1, &px, &py)) { + pixDestroy(&pixb); + return ERROR_INT("no start pixel found", procName, 1); + } + qpos = 0; /* relative to p */ + fpx = px; /* save location of first pixel on border */ + fpy = py; + + /* Save box and start pixel in relative coords */ + boxaAddBox(ccb->boxa, box, L_COPY); + ptaAddPt(ccb->start, px - 1, py - 1); + + pta = ptaCreate(0); + ptaaAddPta(ccb->local, pta, L_INSERT); + ptaAddPt(pta, px - 1, py - 1); /* initial point */ + pixGetDimensions(pixb, &w, &h, NULL); + data = pixGetData(pixb); + wpl = pixGetWpl(pixb); + + /* Get the second point; if there is none, return */ + if (findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy)) { + pixDestroy(&pixb); + return 0; + } + + spx = npx; /* save location of second pixel on border */ + spy = npy; + ptaAddPt(pta, npx - 1, npy - 1); /* second point */ + px = npx; + py = npy; + + while (1) { + findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy); + if (px == fpx && py == fpy && npx == spx && npy == spy) + break; + ptaAddPt(pta, npx - 1, npy - 1); + px = npx; + py = npy; + } + + pixDestroy(&pixb); + return 0; +} + + +/*! + * \brief pixGetHoleBorder() + * + * \param[in] ccb the exterior border is already made + * \param[in] pixs for the connected component at hand + * \param[in] box for the specific hole border, in relative + * coordinates to the c.c. + * \param[in] xs, ys first pixel on hole border, relative to c.c. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) we trace out hole border on pixs without addition
+ *          of single pixel added border to pixs
+ *      (2) therefore all coordinates are relative within the c.c. (pixs)
+ *      (3) same position tables and stopping condition as for
+ *          exterior borders
+ * 
+ */ +static l_ok +pixGetHoleBorder(CCBORD *ccb, + PIX *pixs, + BOX *box, + l_int32 xs, + l_int32 ys) +{ +l_int32 fpx, fpy, spx, spy, qpos; +l_int32 px, py, npx, npy; +l_int32 w, h, wpl; +l_uint32 *data; +PTA *pta; + + PROCNAME("pixGetHoleBorder"); + + if (!ccb) + return ERROR_INT("ccb not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + /* Add border and find start pixel */ + qpos = 0; /* orientation of Q relative to P */ + fpx = xs; /* save location of first pixel on border */ + fpy = ys; + + /* Save box and start pixel */ + boxaAddBox(ccb->boxa, box, L_COPY); + ptaAddPt(ccb->start, xs, ys); + + if ((pta = ptaCreate(0)) == NULL) + return ERROR_INT("pta not made", procName, 1); + ptaaAddPta(ccb->local, pta, L_INSERT); + ptaAddPt(pta, xs, ys); /* initial pixel */ + + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + + /* Get the second point; there should always be at least 4 pts + * in a minimal hole border! */ + if (findNextBorderPixel(w, h, data, wpl, xs, ys, &qpos, &npx, &npy)) + return ERROR_INT("isolated hole border point!", procName, 1); + + spx = npx; /* save location of second pixel on border */ + spy = npy; + ptaAddPt(pta, npx, npy); /* second pixel */ + px = npx; + py = npy; + + while (1) { + findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy); + if (px == fpx && py == fpy && npx == spx && npy == spy) + break; + ptaAddPt(pta, npx, npy); + px = npx; + py = npy; + } + + return 0; +} + + +/*! + * \brief findNextBorderPixel() + * + * \param[in] w, h + * \param[in] data, wpl + * \param[in] px, py current P + * \param[in,out] pqpos input current Q; new Q + * \param[out] pnpx, pnpy new P + * \return 0 if next pixel found; 1 otherwise + * + *
+ * Notes:
+ *      (1) qpos increases clockwise from 0 to 7, with 0 at
+ *          location with Q to left of P:   Q P
+ *      (2) this is a low-level function that does not check input
+ *          parameters.  All calling functions should check them.
+ * 
+ */ +static l_int32 +findNextBorderPixel(l_int32 w, + l_int32 h, + l_uint32 *data, + l_int32 wpl, + l_int32 px, + l_int32 py, + l_int32 *pqpos, + l_int32 *pnpx, + l_int32 *pnpy) +{ +l_int32 qpos, i, pos, npx, npy, val; +l_uint32 *line; + + qpos = *pqpos; + for (i = 1; i < 8; i++) { + pos = (qpos + i) % 8; + npx = px + xpostab[pos]; + npy = py + ypostab[pos]; + line = data + npy * wpl; + val = GET_DATA_BIT(line, npx); + if (val) { + *pnpx = npx; + *pnpy = npy; + *pqpos = qpostab[pos]; + return 0; + } + } + + return 1; +} + + +/*! + * \brief locateOutsideSeedPixel() + * + * \param[in] fpx, fpy location of first pixel + * \param[in] spx, spy location of second pixel + * \param[out] pxs, pys seed pixel to be returned + * + *
+ * Notes:
+ *      (1) The first and second pixels must be 8-adjacent,
+ *          so |dx| <= 1 and |dy| <= 1 and both dx and dy
+ *          cannot be 0.  There are 8 possible cases.
+ *      (2) The seed pixel is OUTSIDE the foreground of the c.c.
+ *      (3) These rules are for the situation where the INSIDE
+ *          of the c.c. is on the right as you follow the border:
+ *          cw for an exterior border and ccw for a hole border.
+ * 
+ */ +static void +locateOutsideSeedPixel(l_int32 fpx, + l_int32 fpy, + l_int32 spx, + l_int32 spy, + l_int32 *pxs, + l_int32 *pys) +{ +l_int32 dx, dy; + + dx = spx - fpx; + dy = spy - fpy; + + if (dx * dy == 1) { + *pxs = fpx + dx; + *pys = fpy; + } else if (dx * dy == -1) { + *pxs = fpx; + *pys = fpy + dy; + } else if (dx == 0) { + *pxs = fpx + dy; + *pys = fpy + dy; + } else /* dy == 0 */ { + *pxs = fpx + dx; + *pys = fpy - dx; + } + + return; +} + + + +/*---------------------------------------------------------------------* + * Border conversions * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaGenerateGlobalLocs() + * + * \param[in] ccba with local chain ptaa of borders computed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This uses the pixel locs in the local ptaa, which are all
+ *          relative to each c.c., to find the global pixel locations,
+ *          and stores them in the global ptaa.
+ * 
+ */ +l_ok +ccbaGenerateGlobalLocs(CCBORDA *ccba) +{ +l_int32 ncc, nb, n, i, j, k, xul, yul, x, y; +CCBORD *ccb; +PTAA *ptaal, *ptaag; +PTA *ptal, *ptag; + + PROCNAME("ccbaGenerateGlobalLocs"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + + /* Get the UL corner in global coords, (xul, yul), of the c.c. */ + boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL); + + /* Make a new global ptaa, removing any old one */ + ptaal = ccb->local; + nb = ptaaGetCount(ptaal); /* number of borders */ + if (ccb->global) /* remove old one */ + ptaaDestroy(&ccb->global); + if ((ptaag = ptaaCreate(nb)) == NULL) + return ERROR_INT("ptaag not made", procName, 1); + ccb->global = ptaag; /* save new one */ + + /* Iterate through the borders for this c.c. */ + for (j = 0; j < nb; j++) { + ptal = ptaaGetPta(ptaal, j, L_CLONE); + n = ptaGetCount(ptal); /* number of pixels in border */ + if ((ptag = ptaCreate(n)) == NULL) + return ERROR_INT("ptag not made", procName, 1); + ptaaAddPta(ptaag, ptag, L_INSERT); + for (k = 0; k < n; k++) { + ptaGetIPt(ptal, k, &x, &y); + ptaAddPt(ptag, x + xul, y + yul); + } + ptaDestroy(&ptal); + } + ccbDestroy(&ccb); + } + + return 0; +} + + +/*! + * \brief ccbaGenerateStepChains() + * + * \param[in] ccba with local chain ptaa of borders computed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This uses the pixel locs in the local ptaa,
+ *          which are all relative to each c.c., to find
+ *          the step directions for successive pixels in
+ *          the chain, and stores them in the step numaa.
+ *      (2) To get the step direction, use
+ *              1   2   3
+ *              0   P   4
+ *              7   6   5
+ *          where P is the previous pixel at (px, py).  The step direction
+ *          is the number (from 0 through 7) for each relative location
+ *          of the current pixel at (cx, cy).  It is easily found by
+ *          indexing into a 2-d 3x3 array (dirtab).
+ * 
+ */ +l_ok +ccbaGenerateStepChains(CCBORDA *ccba) +{ +l_int32 ncc, nb, n, i, j, k; +l_int32 px, py, cx, cy, stepdir; +l_int32 dirtab[][3] = {{1, 2, 3}, {0, -1, 4}, {7, 6, 5}}; +CCBORD *ccb; +NUMA *na; +NUMAA *naa; /* step chain code; to be made */ +PTA *ptal; +PTAA *ptaal; /* local chain code */ + + PROCNAME("ccbaGenerateStepChains"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + + /* Make a new step numaa, removing any old one */ + ptaal = ccb->local; + nb = ptaaGetCount(ptaal); /* number of borders */ + if (ccb->step) /* remove old one */ + numaaDestroy(&ccb->step); + if ((naa = numaaCreate(nb)) == NULL) + return ERROR_INT("naa not made", procName, 1); + ccb->step = naa; /* save new one */ + + /* Iterate through the borders for this c.c. */ + for (j = 0; j < nb; j++) { + ptal = ptaaGetPta(ptaal, j, L_CLONE); + n = ptaGetCount(ptal); /* number of pixels in border */ + if (n == 1) { /* isolated pixel */ + na = numaCreate(1); /* but leave it empty */ + } else { /* trace out the boundary */ + if ((na = numaCreate(n)) == NULL) + return ERROR_INT("na not made", procName, 1); + ptaGetIPt(ptal, 0, &px, &py); + for (k = 1; k < n; k++) { + ptaGetIPt(ptal, k, &cx, &cy); + stepdir = dirtab[1 + cy - py][1 + cx - px]; + numaAddNumber(na, stepdir); + px = cx; + py = cy; + } + } + numaaAddNuma(naa, na, L_INSERT); + ptaDestroy(&ptal); + } + ccbDestroy(&ccb); /* just decrement refcount */ + } + + return 0; +} + + +/*! + * \brief ccbaStepChainsToPixCoords() + * + * \param[in] ccba with step chains numaa of borders + * \param[in] coordtype CCB_GLOBAL_COORDS or CCB_LOCAL_COORDS + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This uses the step chain data in each ccb to determine
+ *          the pixel locations, either global or local,
+ *          and stores them in the appropriate ptaa,
+ *          either global or local.  For the latter, the
+ *          pixel locations are relative to the c.c.
+ * 
+ */ +l_ok +ccbaStepChainsToPixCoords(CCBORDA *ccba, + l_int32 coordtype) +{ +l_int32 ncc, nb, n, i, j, k; +l_int32 xul, yul, xstart, ystart, x, y, stepdir; +BOXA *boxa; +CCBORD *ccb; +NUMA *na; +NUMAA *naa; +PTAA *ptaan; /* new pix coord ptaa */ +PTA *ptas, *ptan; + + PROCNAME("ccbaStepChainsToPixCoords"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + if (coordtype != CCB_GLOBAL_COORDS && coordtype != CCB_LOCAL_COORDS) + return ERROR_INT("coordtype not valid", procName, 1); + + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if ((naa = ccb->step) == NULL) + return ERROR_INT("step numaa not found", procName, 1); + if ((boxa = ccb->boxa) == NULL) + return ERROR_INT("boxa not found", procName, 1); + if ((ptas = ccb->start) == NULL) + return ERROR_INT("start pta not found", procName, 1); + + /* For global coords, get the (xul, yul) of the c.c.; + * otherwise, use relative coords. */ + if (coordtype == CCB_LOCAL_COORDS) { + xul = 0; + yul = 0; + } else { /* coordtype == CCB_GLOBAL_COORDS */ + /* Get UL corner in global coords */ + if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, NULL, NULL)) + return ERROR_INT("bounding rectangle not found", procName, 1); + } + + /* Make a new ptaa, removing any old one */ + nb = numaaGetCount(naa); /* number of borders */ + if ((ptaan = ptaaCreate(nb)) == NULL) + return ERROR_INT("ptaan not made", procName, 1); + if (coordtype == CCB_LOCAL_COORDS) { + if (ccb->local) /* remove old one */ + ptaaDestroy(&ccb->local); + ccb->local = ptaan; /* save new local chain */ + } else { /* coordtype == CCB_GLOBAL_COORDS */ + if (ccb->global) /* remove old one */ + ptaaDestroy(&ccb->global); + ccb->global = ptaan; /* save new global chain */ + } + + /* Iterate through the borders for this c.c. */ + for (j = 0; j < nb; j++) { + na = numaaGetNuma(naa, j, L_CLONE); + n = numaGetCount(na); /* number of steps in border */ + if ((ptan = ptaCreate(n + 1)) == NULL) + return ERROR_INT("ptan not made", procName, 1); + ptaaAddPta(ptaan, ptan, L_INSERT); + ptaGetIPt(ptas, j, &xstart, &ystart); + x = xul + xstart; + y = yul + ystart; + ptaAddPt(ptan, x, y); + for (k = 0; k < n; k++) { + numaGetIValue(na, k, &stepdir); + x += xpostab[stepdir]; + y += ypostab[stepdir]; + ptaAddPt(ptan, x, y); + } + numaDestroy(&na); + } + ccbDestroy(&ccb); + } + + return 0; +} + + +/*! + * \brief ccbaGenerateSPGlobalLocs() + * + * \param[in] ccba + * \param[in] ptsflag CCB_SAVE_ALL_PTS or CCB_SAVE_TURNING_PTS + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This calculates the splocal rep if not yet made.
+ *      (2) It uses the local pixel values in splocal, the single
+ *          path pta, which are all relative to each c.c., to find
+ *          the corresponding global pixel locations, and stores
+ *          them in the spglobal pta.
+ *      (3) This lists only the turning points: it both makes a
+ *          valid svg file and is typically about half the size
+ *          when all border points are listed.
+ * 
+ */ +l_ok +ccbaGenerateSPGlobalLocs(CCBORDA *ccba, + l_int32 ptsflag) +{ +l_int32 ncc, npt, i, j, xul, yul, x, y, delx, dely; +l_int32 xp, yp, delxp, delyp; /* prev point and increments */ +CCBORD *ccb; +PTA *ptal, *ptag; + + PROCNAME("ccbaGenerateSPGlobalLocs"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + /* Make sure we have a local single path representation */ + if ((ccb = ccbaGetCcb(ccba, 0)) == NULL) + return ERROR_INT("no ccb", procName, 1); + if (!ccb->splocal) + ccbaGenerateSinglePath(ccba); + ccbDestroy(&ccb); /* clone ref */ + + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + + /* Get the UL corner in global coords, (xul, yul), of the c.c. */ + if (boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL)) + return ERROR_INT("bounding rectangle not found", procName, 1); + + /* Make a new spglobal pta, removing any old one */ + ptal = ccb->splocal; + npt = ptaGetCount(ptal); /* number of points */ + if (ccb->spglobal) /* remove old one */ + ptaDestroy(&ccb->spglobal); + if ((ptag = ptaCreate(npt)) == NULL) + return ERROR_INT("ptag not made", procName, 1); + ccb->spglobal = ptag; /* save new one */ + + /* Convert local to global */ + if (ptsflag == CCB_SAVE_ALL_PTS) { + for (j = 0; j < npt; j++) { + ptaGetIPt(ptal, j, &x, &y); + ptaAddPt(ptag, x + xul, y + yul); + } + } else { /* ptsflag = CCB_SAVE_TURNING_PTS */ + ptaGetIPt(ptal, 0, &xp, &yp); /* get the 1st pt */ + ptaAddPt(ptag, xp + xul, yp + yul); /* save the 1st pt */ + if (npt == 2) { /* get and save the 2nd pt */ + ptaGetIPt(ptal, 1, &x, &y); + ptaAddPt(ptag, x + xul, y + yul); + } else if (npt > 2) { + ptaGetIPt(ptal, 1, &x, &y); + delxp = x - xp; + delyp = y - yp; + xp = x; + yp = y; + for (j = 2; j < npt; j++) { + ptaGetIPt(ptal, j, &x, &y); + delx = x - xp; + dely = y - yp; + if (delx != delxp || dely != delyp) + ptaAddPt(ptag, xp + xul, yp + yul); + xp = x; + yp = y; + delxp = delx; + delyp = dely; + } + ptaAddPt(ptag, xp + xul, yp + yul); + } + } + + ccbDestroy(&ccb); /* clone ref */ + } + + return 0; +} + + + +/*---------------------------------------------------------------------* + * Conversion to single path * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaGenerateSinglePath() + * + * \param[in] ccba + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates a single border in local pixel coordinates.
+ *          For each c.c., if there is just an outer border, copy it.
+ *          If there are also hole borders, for each hole border,
+ *          determine the smallest horizontal or vertical
+ *          distance from the border to the outside of the c.c.,
+ *          and find a path through the c.c. for this cut.
+ *          We do this in a way that guarantees a pixel from the
+ *          hole border is the starting point of the path, and
+ *          we must verify that the path intersects the outer
+ *          border (if it intersects it, then it ends on it).
+ *          One can imagine pathological cases, but they may not
+ *          occur in images of text characters and un-textured
+ *          line graphics.
+ *      (2) Once it is verified that the path through the c.c.
+ *          intersects both the hole and outer borders, we
+ *          generate the full single path for all borders in the
+ *          c.c.  Starting at the start point on the outer
+ *          border, when we hit a line on a cut, we take
+ *          the cut, do the hold border, and return on the cut
+ *          to the outer border.  We compose a pta of the
+ *          outer border pts that are on cut paths, and for
+ *          every point on the outer border (as we go around),
+ *          we check against this pta.  When we find a matching
+ *          point in the pta, we do its cut path and hole border.
+ *          The single path is saved in the ccb.
+ * 
+ */ +l_ok +ccbaGenerateSinglePath(CCBORDA *ccba) +{ +l_int32 i, j, k, ncc, nb, ncut, npt, dir, len, state, lostholes; +l_int32 x, y, xl, yl, xf, yf; +BOX *boxinner; +BOXA *boxa; +CCBORD *ccb; +PTA *pta, *ptac, *ptah; +PTA *ptahc; /* cyclic permutation of hole border, with end pts at cut */ +PTA *ptas; /* output result: new single path for c.c. */ +PTA *ptaf; /* points on the hole borders that intersect with cuts */ +PTA *ptal; /* points on outer border that intersect with cuts */ +PTA *ptap, *ptarp; /* path and reverse path between borders */ +PTAA *ptaa; +PTAA *ptaap; /* ptaa for all paths between borders */ + + PROCNAME("ccbaGenerateSinglePath"); + + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + ncc = ccbaGetCount(ccba); /* number of c.c. */ + lostholes = 0; + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if ((ptaa = ccb->local) == NULL) { + L_WARNING("local pixel loc array not found\n", procName); + continue; + } + nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ + + /* Prepare the output pta */ + if (ccb->splocal) + ptaDestroy(&ccb->splocal); + ptas = ptaCreate(0); + ccb->splocal = ptas; + + /* If no holes, just concat the outer border */ + pta = ptaaGetPta(ptaa, 0, L_CLONE); + if (nb == 1 || nb > NMAX_HOLES + 1) { + ptaJoin(ptas, pta, 0, -1); + ptaDestroy(&pta); /* remove clone */ + ccbDestroy(&ccb); /* remove clone */ + continue; + } + + /* Find the (nb - 1) cut paths that connect holes + * with outer border */ + boxa = ccb->boxa; + ptaap = ptaaCreate(nb - 1); + ptaf = ptaCreate(nb - 1); + ptal = ptaCreate(nb - 1); + for (j = 1; j < nb; j++) { + boxinner = boxaGetBox(boxa, j, L_CLONE); + + /* Find a short path and store it */ + ptac = getCutPathForHole(ccb->pix, pta, boxinner, &dir, &len); + if (len == 0) { /* bad: we lose the hole! */ + lostholes++; +/* boxPrintStreamInfo(stderr, boxa->box[0]); */ + } + ptaaAddPta(ptaap, ptac, L_INSERT); +/* fprintf(stderr, "dir = %d, length = %d\n", dir, len); */ +/* ptaWriteStream(stderr, ptac, 1); */ + + /* Store the first and last points in the cut path, + * which must be on a hole border and the outer + * border, respectively */ + ncut = ptaGetCount(ptac); + if (ncut == 0) { /* missed hole; neg coords won't match */ + ptaAddPt(ptaf, -1, -1); + ptaAddPt(ptal, -1, -1); + } else { + ptaGetIPt(ptac, 0, &x, &y); + ptaAddPt(ptaf, x, y); + ptaGetIPt(ptac, ncut - 1, &x, &y); + ptaAddPt(ptal, x, y); + } + boxDestroy(&boxinner); + } + + /* Make a single path for the c.c. using these connections */ + npt = ptaGetCount(pta); /* outer border pts */ + for (k = 0; k < npt; k++) { + ptaGetIPt(pta, k, &x, &y); + if (k == 0) { /* if there is a cut at the first point, + * we can wait until the end to take it */ + ptaAddPt(ptas, x, y); + continue; + } + state = L_NOT_FOUND; + for (j = 0; j < nb - 1; j++) { /* iterate over cut end pts */ + ptaGetIPt(ptal, j, &xl, &yl); /* cut point on outer border */ + if (x == xl && y == yl) { /* take this cut to the hole */ + state = L_FOUND; + ptap = ptaaGetPta(ptaap, j, L_CLONE); + ptarp = ptaReverse(ptap, 1); + /* Cut point on hole border: */ + ptaGetIPt(ptaf, j, &xf, &yf); + /* Hole border: */ + ptah = ptaaGetPta(ptaa, j + 1, L_CLONE); + ptahc = ptaCyclicPerm(ptah, xf, yf); +/* ptaWriteStream(stderr, ptahc, 1); */ + ptaJoin(ptas, ptarp, 0, -1); + ptaJoin(ptas, ptahc, 0, -1); + ptaJoin(ptas, ptap, 0, -1); + ptaDestroy(&ptap); + ptaDestroy(&ptarp); + ptaDestroy(&ptah); + ptaDestroy(&ptahc); + break; + } + } + if (state == L_NOT_FOUND) + ptaAddPt(ptas, x, y); + } + +/* ptaWriteStream(stderr, ptas, 1); */ + ptaaDestroy(&ptaap); + ptaDestroy(&ptaf); + ptaDestroy(&ptal); + ptaDestroy(&pta); /* remove clone */ + ccbDestroy(&ccb); /* remove clone */ + } + + if (lostholes > 0) + L_WARNING("***** %d lost holes *****\n", procName, lostholes); + + return 0; +} + + +/*! + * \brief getCutPathForHole() + * + * \param[in] pix 1 bpp, of c.c. + * \param[in] pta of outer border + * \param[in] boxinner bounding box of hole path + * \param[out] pdir direction (0-3), returned; only needed for debug + * \param[out] plen length of path, returned + * \return pta of pts on cut path from the hole border + * to the outer border, including end points on + * both borders; or NULL on error + * + *
+ * Notes:
+ *      (1) If we don't find a path, we return a pta with no pts
+ *          in it and len = 0.
+ *      (2) The goal is to get a reasonably short path between the
+ *          inner and outer borders, that goes entirely within the fg of
+ *          the pix.  This function is cheap-and-dirty, may fail for some
+ *          holes in complex topologies such as those you might find in a
+ *          moderately dark scanned halftone.  If it fails to find a
+ *          path to any particular hole, it gives a warning, and because
+ *          that hole path is not included, the hole will not be rendered.
+ * 
+ */ +PTA * +getCutPathForHole(PIX *pix, + PTA *pta, + BOX *boxinner, + l_int32 *pdir, + l_int32 *plen) +{ +l_int32 w, h, nc, x, y, xl, yl, xmid, ymid; +l_uint32 val; +PTA *ptac; + + PROCNAME("getCutPathForHole"); + + if (!pix) + return (PTA *)ERROR_PTR("pix not defined", procName, NULL); + if (!pta) + return (PTA *)ERROR_PTR("pta not defined", procName, NULL); + if (!boxinner) + return (PTA *)ERROR_PTR("boxinner not defined", procName, NULL); + + w = pixGetWidth(pix); + h = pixGetHeight(pix); + + if ((ptac = ptaCreate(4)) == NULL) + return (PTA *)ERROR_PTR("ptac not made", procName, NULL); + xmid = boxinner->x + boxinner->w / 2; + ymid = boxinner->y + boxinner->h / 2; + + /* try top first */ + for (y = ymid; y >= 0; y--) { + pixGetPixel(pix, xmid, y, &val); + if (val == 1) { + ptaAddPt(ptac, xmid, y); + break; + } + } + for (y = y - 1; y >= 0; y--) { + pixGetPixel(pix, xmid, y, &val); + if (val == 1) + ptaAddPt(ptac, xmid, y); + else + break; + } + nc = ptaGetCount(ptac); + ptaGetIPt(ptac, nc - 1, &xl, &yl); + if (ptaContainsPt(pta, xl, yl)) { + *pdir = 1; + *plen = nc; + return ptac; + } + + /* Next try bottom */ + ptaEmpty(ptac); + for (y = ymid; y < h; y++) { + pixGetPixel(pix, xmid, y, &val); + if (val == 1) { + ptaAddPt(ptac, xmid, y); + break; + } + } + for (y = y + 1; y < h; y++) { + pixGetPixel(pix, xmid, y, &val); + if (val == 1) + ptaAddPt(ptac, xmid, y); + else + break; + } + nc = ptaGetCount(ptac); + ptaGetIPt(ptac, nc - 1, &xl, &yl); + if (ptaContainsPt(pta, xl, yl)) { + *pdir = 3; + *plen = nc; + return ptac; + } + + /* Next try left */ + ptaEmpty(ptac); + for (x = xmid; x >= 0; x--) { + pixGetPixel(pix, x, ymid, &val); + if (val == 1) { + ptaAddPt(ptac, x, ymid); + break; + } + } + for (x = x - 1; x >= 0; x--) { + pixGetPixel(pix, x, ymid, &val); + if (val == 1) + ptaAddPt(ptac, x, ymid); + else + break; + } + nc = ptaGetCount(ptac); + ptaGetIPt(ptac, nc - 1, &xl, &yl); + if (ptaContainsPt(pta, xl, yl)) { + *pdir = 0; + *plen = nc; + return ptac; + } + + /* Finally try right */ + ptaEmpty(ptac); + for (x = xmid; x < w; x++) { + pixGetPixel(pix, x, ymid, &val); + if (val == 1) { + ptaAddPt(ptac, x, ymid); + break; + } + } + for (x = x + 1; x < w; x++) { + pixGetPixel(pix, x, ymid, &val); + if (val == 1) + ptaAddPt(ptac, x, ymid); + else + break; + } + nc = ptaGetCount(ptac); + ptaGetIPt(ptac, nc - 1, &xl, &yl); + if (ptaContainsPt(pta, xl, yl)) { + *pdir = 2; + *plen = nc; + return ptac; + } + + /* If we get here, we've failed! */ + ptaEmpty(ptac); + L_WARNING("no path found\n", procName); + *plen = 0; + return ptac; +} + + + +/*---------------------------------------------------------------------* + * Border rendering * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaDisplayBorder() + * + * \param[in] ccba + * \return pix of border pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses global ptaa, which gives each border pixel in
+ *          global coordinates, and must be computed in advance
+ *          by calling ccbaGenerateGlobalLocs().
+ * 
+ */ +PIX * +ccbaDisplayBorder(CCBORDA *ccba) +{ +l_int32 ncc, nb, n, i, j, k, x, y; +CCBORD *ccb; +PIX *pixd; +PTAA *ptaa; +PTA *pta; + + PROCNAME("ccbaDisplayBorder"); + + if (!ccba) + return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); + + if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if ((ptaa = ccb->global) == NULL) { + L_WARNING("global pixel loc array not found", procName); + continue; + } + nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ + for (j = 0; j < nb; j++) { + pta = ptaaGetPta(ptaa, j, L_CLONE); + n = ptaGetCount(pta); /* number of pixels in the border */ + for (k = 0; k < n; k++) { + ptaGetIPt(pta, k, &x, &y); + pixSetPixel(pixd, x, y, 1); + } + ptaDestroy(&pta); + } + ccbDestroy(&ccb); + } + + return pixd; +} + + +/*! + * \brief ccbaDisplaySPBorder() + * + * \param[in] ccba + * \return pix of border pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses spglobal pta, which gives each border pixel in
+ *          global coordinates, one path per c.c., and must
+ *          be computed in advance by calling ccbaGenerateSPGlobalLocs().
+ * 
+ */ +PIX * +ccbaDisplaySPBorder(CCBORDA *ccba) +{ +l_int32 ncc, npt, i, j, x, y; +CCBORD *ccb; +PIX *pixd; +PTA *ptag; + + PROCNAME("ccbaDisplaySPBorder"); + + if (!ccba) + return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); + + if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + ncc = ccbaGetCount(ccba); /* number of c.c. */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if ((ptag = ccb->spglobal) == NULL) { + L_WARNING("spglobal pixel loc array not found\n", procName); + continue; + } + npt = ptaGetCount(ptag); /* number of pixels on path */ + for (j = 0; j < npt; j++) { + ptaGetIPt(ptag, j, &x, &y); + pixSetPixel(pixd, x, y, 1); + } + ccbDestroy(&ccb); /* clone ref */ + } + + return pixd; +} + + +/*! + * \brief ccbaDisplayImage1() + * + * \param[in] ccba + * \return pix of image, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses local ptaa, which gives each border pixel in
+ *          local coordinates, so the actual pixel positions must
+ *          be computed using all offsets.
+ *      (2) For the holes, use coordinates relative to the c.c.
+ *      (3) This is slower than Method 2.
+ *      (4) This uses topological properties (Method 1) to do scan
+ *          conversion to raster
+ *
+ *  This algorithm deserves some commentary.
+ *
+ *  I first tried the following:
+ *    ~ outer borders: 4-fill from outside, stopping at the
+ *         border, using pixFillClosedBorders()
+ *    ~ inner borders: 4-fill from outside, stopping again
+ *         at the border, XOR with the border, and invert
+ *         to get the hole.  This did not work, because if
+ *         you have a hole border that looks like:
+ *
+ *                x x x x x x
+ *                x          x
+ *                x   x x x   x
+ *                  x x o x   x
+ *                      x     x
+ *                      x     x
+ *                        x x x
+ *
+ *         if you 4-fill from the outside, the pixel 'o' will
+ *         not be filled!  XORing with the border leaves it OFF.
+ *         Inverting then gives a single bad ON pixel that is not
+ *         actually part of the hole.
+ *
+ *  So what you must do instead is 4-fill the holes from inside.
+ *  You can do this from a seedfill, using a pix with the hole
+ *  border as the filling mask.  But you need to start with a
+ *  pixel inside the hole.  How is this determined?  The best
+ *  way is from the contour.  We have a right-hand shoulder
+ *  rule for inside (i.e., the filled region).   Take the
+ *  first 2 pixels of the hole border, and compute dx and dy
+ *  (second coord minus first coord:  dx = sx - fx, dy = sy - fy).
+ *  There are 8 possibilities, depending on the values of dx and
+ *  dy (which can each be -1, 0, and +1, but not both 0).
+ *  These 8 cases can be broken into 4; see the simple algorithm below.
+ *  Once you have an interior seed pixel, you fill from the seed,
+ *  clipping with the hole border pix by filling into its invert.
+ *
+ *  You then successively XOR these interior filled components, in any order.
+ * 
+ */ +PIX * +ccbaDisplayImage1(CCBORDA *ccba) +{ +l_int32 ncc, i, nb, n, j, k, x, y, xul, yul, xoff, yoff, w, h; +l_int32 fpx, fpy, spx, spy, xs, ys; +BOX *box; +BOXA *boxa; +CCBORD *ccb; +PIX *pixd, *pixt, *pixh; +PTAA *ptaa; +PTA *pta; + + PROCNAME("ccbaDisplayImage1"); + + if (!ccba) + return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); + + if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + ncc = ccbaGetCount(ccba); + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if ((boxa = ccb->boxa) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("boxa not found", procName, NULL); + } + + /* Render border in pixt */ + if ((ptaa = ccb->local) == NULL) { + L_WARNING("local chain array not found\n", procName); + continue; + } + + nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ + for (j = 0; j < nb; j++) { + if ((box = boxaGetBox(boxa, j, L_CLONE)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("b. box not found", procName, NULL); + } + if (j == 0) { + boxGetGeometry(box, &xul, &yul, &w, &h); + xoff = yoff = 0; + } else { + boxGetGeometry(box, &xoff, &yoff, &w, &h); + } + boxDestroy(&box); + + /* Render the border in a minimum-sized pix; + * subtract xoff and yoff because the pixel + * location is stored relative to the c.c., but + * we need it relative to just the hole border. */ + if ((pixt = pixCreate(w, h, 1)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + pta = ptaaGetPta(ptaa, j, L_CLONE); + n = ptaGetCount(pta); /* number of pixels in the border */ + for (k = 0; k < n; k++) { + ptaGetIPt(pta, k, &x, &y); + pixSetPixel(pixt, x - xoff, y - yoff, 1); + if (j > 0) { /* need this for finding hole border pixel */ + if (k == 0) { + fpx = x - xoff; + fpy = y - yoff; + } + if (k == 1) { + spx = x - xoff; + spy = y - yoff; + } + } + } + ptaDestroy(&pta); + + /* Get the filled component */ + if (j == 0) { /* if outer border, fill from outer boundary */ + if ((pixh = pixFillClosedBorders(pixt, 4)) == NULL) { + pixDestroy(&pixd); + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("pixh not made", procName, NULL); + } + } else { /* fill the hole from inside */ + /* get the location of a seed pixel in the hole */ + locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys); + + /* Put seed in hole and fill interior of hole, + * using pixt as clipping mask */ + pixh = pixCreateTemplate(pixt); + pixSetPixel(pixh, xs, ys, 1); /* put seed pixel in hole */ + pixInvert(pixt, pixt); /* to make filling mask */ + pixSeedfillBinary(pixh, pixh, pixt, 4); /* 4-fill hole */ + } + + /* XOR into the dest */ + pixRasterop(pixd, xul + xoff, yul + yoff, w, h, PIX_XOR, + pixh, 0, 0); + pixDestroy(&pixt); + pixDestroy(&pixh); + } + ccbDestroy(&ccb); + } + return pixd; +} + + + +/*! + * \brief ccbaDisplayImage2() + * + * \param[in] ccba + * \return pix of image, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses local chain ptaa, which gives each border pixel in
+ *          local coordinates, so the actual pixel positions must
+ *          be computed using all offsets.
+ *      (2) Treats exterior and hole borders on equivalent
+ *          footing, and does all calculations on a pix
+ *          that spans the c.c. with a 1 pixel added boundary.
+ *      (3) This uses topological properties (Method 2) to do scan
+ *          conversion to raster
+ *      (4) The algorithm is described at the top of this file (Method 2).
+ *          It is preferred to Method 1 because it is between 1.2x and 2x
+ *          faster than Method 1.
+ * 
+ */ +PIX * +ccbaDisplayImage2(CCBORDA *ccba) +{ +l_int32 ncc, nb, n, i, j, k, x, y, xul, yul, w, h; +l_int32 fpx, fpy, spx, spy, xs, ys; +BOXA *boxa; +CCBORD *ccb; +PIX *pixd, *pixc, *pixs; +PTAA *ptaa; +PTA *pta; + + PROCNAME("ccbaDisplayImage2"); + + if (!ccba) + return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); + + if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + ncc = ccbaGetCount(ccba); + for (i = 0; i < ncc; i++) { + /* Generate clipping mask from border pixels and seed image + * from one seed for each closed border. */ + ccb = ccbaGetCcb(ccba, i); + if ((boxa = ccb->boxa) == NULL) { + pixDestroy(&pixd); + ccbDestroy(&ccb); + return (PIX *)ERROR_PTR("boxa not found", procName, NULL); + } + if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, &w, &h)) { + pixDestroy(&pixd); + ccbDestroy(&ccb); + return (PIX *)ERROR_PTR("b. box not found", procName, NULL); + } + pixc = pixCreate(w + 2, h + 2, 1); + pixs = pixCreateTemplate(pixc); + + if ((ptaa = ccb->local) == NULL) { + pixDestroy(&pixc); + pixDestroy(&pixs); + ccbDestroy(&ccb); + L_WARNING("local chain array not found\n", procName); + continue; + } + nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ + for (j = 0; j < nb; j++) { + pta = ptaaGetPta(ptaa, j, L_CLONE); + n = ptaGetCount(pta); /* number of pixels in the border */ + + /* Render border pixels in pixc */ + for (k = 0; k < n; k++) { + ptaGetIPt(pta, k, &x, &y); + pixSetPixel(pixc, x + 1, y + 1, 1); + if (k == 0) { + fpx = x + 1; + fpy = y + 1; + } else if (k == 1) { + spx = x + 1; + spy = y + 1; + } + } + + /* Get and set seed pixel for this border in pixs */ + if (n > 1) + locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys); + else /* isolated c.c. */ + xs = ys = 0; + pixSetPixel(pixs, xs, ys, 1); + ptaDestroy(&pta); + } + + /* Fill from seeds in pixs, using pixc as the clipping mask, + * to reconstruct the c.c. */ + pixInvert(pixc, pixc); /* to convert clipping -> filling mask */ + pixSeedfillBinary(pixs, pixs, pixc, 4); /* 4-fill */ + pixInvert(pixs, pixs); /* to make the c.c. */ + + /* XOR into the dest */ + pixRasterop(pixd, xul, yul, w, h, PIX_XOR, pixs, 1, 1); + + pixDestroy(&pixc); + pixDestroy(&pixs); + ccbDestroy(&ccb); /* ref-counted */ + } + return pixd; +} + + + +/*---------------------------------------------------------------------* + * Serialize for I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaWrite() + * + * \param[in] filename + * \param[in] ccba + * \return 0 if OK, 1 on error + */ +l_ok +ccbaWrite(const char *filename, + CCBORDA *ccba) +{ +FILE *fp; + + PROCNAME("ccbaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + if (ccbaWriteStream(fp, ccba)) { + fclose(fp); + return ERROR_INT("ccba not written to stream", procName, 1); + } + + fclose(fp); + return 0; +} + + + +/*! + * \brief ccbaWriteStream() + * + * \param[in] fp file stream + * \param[in] ccba + * \return 0 if OK; 1 on error + * + * Format: + * \code + * ccba: %7d cc\n num. c.c.) (ascii) (18B + * pix width 4B + * pix height 4B + * [for i = 1, ncc] + * ulx 4B + * uly 4B + * w 4B -- not req'd for reconstruction + * h 4B -- not req'd for reconstruction + * number of borders 4B + * [for j = 1, nb] + * startx 4B + * starty 4B + * [for k = 1, nb] + * 2 steps 1B + * end in z8 or 88 1B + * \endcode + */ +l_ok +ccbaWriteStream(FILE *fp, + CCBORDA *ccba) +{ +char strbuf[256]; +l_uint8 bval; +l_uint8 *datain, *dataout; +l_int32 i, j, k, bx, by, bw, bh, val, startx, starty; +l_int32 ncc, nb, n; +l_uint32 w, h; +size_t inbytes, outbytes; +L_BBUFFER *bbuf; +CCBORD *ccb; +NUMA *na; +NUMAA *naa; +PTA *pta; + + PROCNAME("ccbaWriteStream"); + +#if !HAVE_LIBZ /* defined in environ.h */ + return ERROR_INT("no libz: can't write data", procName, 1); +#else + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + if ((bbuf = bbufferCreate(NULL, 1000)) == NULL) + return ERROR_INT("bbuf not made", procName, 1); + + ncc = ccbaGetCount(ccba); + snprintf(strbuf, sizeof(strbuf), "ccba: %7d cc\n", ncc); + bbufferRead(bbuf, (l_uint8 *)strbuf, 18); + w = pixGetWidth(ccba->pix); + h = pixGetHeight(ccba->pix); + bbufferRead(bbuf, (l_uint8 *)&w, 4); /* width */ + bbufferRead(bbuf, (l_uint8 *)&h, 4); /* height */ + for (i = 0; i < ncc; i++) { + ccb = ccbaGetCcb(ccba, i); + if (boxaGetBoxGeometry(ccb->boxa, 0, &bx, &by, &bw, &bh)) { + bbufferDestroy(&bbuf); + return ERROR_INT("bounding box not found", procName, 1); + } + bbufferRead(bbuf, (l_uint8 *)&bx, 4); /* ulx of c.c. */ + bbufferRead(bbuf, (l_uint8 *)&by, 4); /* uly of c.c. */ + bbufferRead(bbuf, (l_uint8 *)&bw, 4); /* w of c.c. */ + bbufferRead(bbuf, (l_uint8 *)&bh, 4); /* h of c.c. */ + if ((naa = ccb->step) == NULL) { + ccbaGenerateStepChains(ccba); + naa = ccb->step; + } + nb = numaaGetCount(naa); + bbufferRead(bbuf, (l_uint8 *)&nb, 4); /* number of borders in c.c. */ + pta = ccb->start; + for (j = 0; j < nb; j++) { + ptaGetIPt(pta, j, &startx, &starty); + bbufferRead(bbuf, (l_uint8 *)&startx, 4); /* starting x in border */ + bbufferRead(bbuf, (l_uint8 *)&starty, 4); /* starting y in border */ + na = numaaGetNuma(naa, j, L_CLONE); + n = numaGetCount(na); + for (k = 0; k < n; k++) { + numaGetIValue(na, k, &val); + if (k % 2 == 0) + bval = (l_uint8)val << 4; + else + bval |= (l_uint8)val; + if (k % 2 == 1) + bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* 2 border steps */ + } + if (n % 2 == 1) { + bval |= 0x8; + bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0xz8, */ + /* where z = {0..7} */ + } else { /* n % 2 == 0 */ + bval = 0x88; + bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0x88 */ + } + numaDestroy(&na); + } + ccbDestroy(&ccb); + } + + datain = bbufferDestroyAndSaveData(&bbuf, &inbytes); + dataout = zlibCompress(datain, inbytes, &outbytes); + fwrite(dataout, 1, outbytes, fp); + + LEPT_FREE(datain); + LEPT_FREE(dataout); + return 0; + +#endif /* !HAVE_LIBZ */ +} + + +/*! + * \brief ccbaRead() + * + * \param[in] filename + * \return ccba, or NULL on error + */ +CCBORDA * +ccbaRead(const char *filename) +{ +FILE *fp; +CCBORDA *ccba; + + PROCNAME("ccbaRead"); + + if (!filename) + return (CCBORDA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (CCBORDA *)ERROR_PTR("stream not opened", procName, NULL); + ccba = ccbaReadStream(fp); + fclose(fp); + + if (!ccba) + return (CCBORDA *)ERROR_PTR("ccba not returned", procName, NULL); + return ccba; +} + + +/*! + * \brief ccbaReadStream() + * + * \param[in] fp file stream + * \return ccba, or NULL on error + * + * \code + * Format: ccba: %7d cc\n num. c.c.) (ascii) (17B + * pix width 4B + * pix height 4B + * [for i = 1, ncc] + * ulx 4B + * uly 4B + * w 4B -- not req'd for reconstruction + * h 4B -- not req'd for reconstruction + * number of borders 4B + * [for j = 1, nb] + * startx 4B + * starty 4B + * [for k = 1, nb] + * 2 steps 1B + * end in z8 or 88 1B + * \endcode + */ +CCBORDA * +ccbaReadStream(FILE *fp) +{ +char strbuf[256]; +l_uint8 bval; +l_uint8 *datain, *dataout; +l_int32 i, j, startx, starty; +l_int32 offset, nib1, nib2; +l_int32 ncc, nb; +l_uint32 width, height, w, h, xoff, yoff; +size_t inbytes, outbytes; +BOX *box; +CCBORD *ccb; +CCBORDA *ccba; +NUMA *na; +NUMAA *step; + + PROCNAME("ccbaReadStream"); + +#if !HAVE_LIBZ /* defined in environ.h */ + return (CCBORDA *)ERROR_PTR("no libz: can't read data", procName, NULL); +#else + + if (!fp) + return (CCBORDA *)ERROR_PTR("stream not open", procName, NULL); + + if ((datain = l_binaryReadStream(fp, &inbytes)) == NULL) + return (CCBORDA *)ERROR_PTR("data not read from file", procName, NULL); + dataout = zlibUncompress(datain, inbytes, &outbytes); + LEPT_FREE(datain); + if (!dataout) + return (CCBORDA *)ERROR_PTR("dataout not made", procName, NULL); + + offset = 18; + memcpy(strbuf, dataout, offset); + strbuf[17] = '\0'; + if (memcmp(strbuf, "ccba:", 5) != 0) { + LEPT_FREE(dataout); + return (CCBORDA *)ERROR_PTR("file not type ccba", procName, NULL); + } + sscanf(strbuf, "ccba: %7d cc\n", &ncc); +/* fprintf(stderr, "ncc = %d\n", ncc); */ + if ((ccba = ccbaCreate(NULL, ncc)) == NULL) { + LEPT_FREE(dataout); + return (CCBORDA *)ERROR_PTR("ccba not made", procName, NULL); + } + + memcpy(&width, dataout + offset, 4); + offset += 4; + memcpy(&height, dataout + offset, 4); + offset += 4; + ccba->w = width; + ccba->h = height; +/* fprintf(stderr, "width = %d, height = %d\n", width, height); */ + + for (i = 0; i < ncc; i++) { /* should be ncc */ + ccb = ccbCreate(NULL); + ccbaAddCcb(ccba, ccb); + + memcpy(&xoff, dataout + offset, 4); + offset += 4; + memcpy(&yoff, dataout + offset, 4); + offset += 4; + memcpy(&w, dataout + offset, 4); + offset += 4; + memcpy(&h, dataout + offset, 4); + offset += 4; + box = boxCreate(xoff, yoff, w, h); + boxaAddBox(ccb->boxa, box, L_INSERT); +/* fprintf(stderr, "xoff = %d, yoff = %d, w = %d, h = %d\n", + xoff, yoff, w, h); */ + + memcpy(&nb, dataout + offset, 4); + offset += 4; +/* fprintf(stderr, "num borders = %d\n", nb); */ + step = numaaCreate(nb); + ccb->step = step; + + for (j = 0; j < nb; j++) { /* should be nb */ + memcpy(&startx, dataout + offset, 4); + offset += 4; + memcpy(&starty, dataout + offset, 4); + offset += 4; + ptaAddPt(ccb->start, startx, starty); +/* fprintf(stderr, "startx = %d, starty = %d\n", startx, starty); */ + na = numaCreate(0); + numaaAddNuma(step, na, L_INSERT); + + while(1) { + bval = *(dataout + offset); + offset++; + nib1 = (bval >> 4); + nib2 = bval & 0xf; + if (nib1 != 8) + numaAddNumber(na, nib1); + else + break; + if (nib2 != 8) + numaAddNumber(na, nib2); + else + break; + } + } + } + LEPT_FREE(dataout); + return ccba; + +#endif /* !HAVE_LIBZ */ +} + + +/*---------------------------------------------------------------------* + * SVG Output * + *---------------------------------------------------------------------*/ +/*! + * \brief ccbaWriteSVG() + * + * \param[in] filename + * \param[in] ccba + * \return 0 if OK, 1 on error + */ +l_ok +ccbaWriteSVG(const char *filename, + CCBORDA *ccba) +{ +char *svgstr; + + PROCNAME("ccbaWriteSVG"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!ccba) + return ERROR_INT("ccba not defined", procName, 1); + + if ((svgstr = ccbaWriteSVGString(filename, ccba)) == NULL) + return ERROR_INT("svgstr not made", procName, 1); + + l_binaryWrite(filename, "w", svgstr, strlen(svgstr)); + LEPT_FREE(svgstr); + + return 0; +} + + +/*! + * \brief ccbaWriteSVGString() + * + * \param[in] filename + * \param[in] ccba + * \return string in svg-formatted, that can be written to file, + * or NULL on error. + */ +char * +ccbaWriteSVGString(const char *filename, + CCBORDA *ccba) +{ +char *svgstr; +char smallbuf[256]; +char line0[] = ""; +char line1[] = ""; +char line2[] = ""; +char line3[] = ""; +char line5[] = ""; +char space[] = " "; +l_int32 i, j, ncc, npt, x, y; +CCBORD *ccb; +PTA *pta; +SARRAY *sa; + + PROCNAME("ccbaWriteSVGString"); + + if (!filename) + return (char *)ERROR_PTR("filename not defined", procName, NULL); + if (!ccba) + return (char *)ERROR_PTR("ccba not defined", procName, NULL); + + sa = sarrayCreate(0); + sarrayAddString(sa, line0, L_COPY); + sarrayAddString(sa, line1, L_COPY); + sarrayAddString(sa, line2, L_COPY); + ncc = ccbaGetCount(ccba); + for (i = 0; i < ncc; i++) { + if ((ccb = ccbaGetCcb(ccba, i)) == NULL) { + sarrayDestroy(&sa); + return (char *)ERROR_PTR("ccb not found", procName, NULL); + } + if ((pta = ccb->spglobal) == NULL) { + sarrayDestroy(&sa); + ccbDestroy(&ccb); + return (char *)ERROR_PTR("spglobal not made", procName, NULL); + } + sarrayAddString(sa, line3, L_COPY); + npt = ptaGetCount(pta); + for (j = 0; j < npt; j++) { + ptaGetIPt(pta, j, &x, &y); + snprintf(smallbuf, sizeof(smallbuf), "%0d,%0d", x, y); + sarrayAddString(sa, smallbuf, L_COPY); + } + sarrayAddString(sa, line4, L_COPY); + ccbDestroy(&ccb); + } + sarrayAddString(sa, line5, L_COPY); + sarrayAddString(sa, space, L_COPY); + + svgstr = sarrayToString(sa, 1); +/* fprintf(stderr, "%s", svgstr); */ + + sarrayDestroy(&sa); + return svgstr; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ccbord.h b/hgdriver/3rdparty/hgOCR/leptonica/ccbord.h new file mode 100644 index 0000000..cccef6e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ccbord.h @@ -0,0 +1,121 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_CCBORD_H +#define LEPTONICA_CCBORD_H + +/*! + * \file ccbord.h + * + *
+ *           CCBord:   represents a single connected component
+ *           CCBorda:  an array of CCBord
+ * 
+ */ + + /*! Use in ccbaStepChainsToPixCoords() */ +/*! CCB Coords */ +enum { + CCB_LOCAL_COORDS = 1, + CCB_GLOBAL_COORDS = 2 +}; + + /*! Use in ccbaGenerateSPGlobalLocs() */ +/*! CCB Points */ +enum { + CCB_SAVE_ALL_PTS = 1, + CCB_SAVE_TURNING_PTS = 2 +}; + + + /*! + *
+     * CCBord contains:
+     *
+     *    (1) a minimally-clipped bitmap of the component (pix),
+     *    (2) a boxa consisting of:
+     *          for the primary component:
+     *                (xul, yul) pixel location in global coords
+     *                (w, h) of the bitmap
+     *          for the hole components:
+     *                (x, y) in relative coordinates in primary component
+     *                (w, h) of the hole border (which is 2 pixels
+     *                       larger in each direction than the hole itself)
+     *    (3) a pta ('start') of the initial border pixel location for each
+     *        closed curve, all in relative coordinates of the primary
+     *        component.  This is given for the primary component,
+     *        followed by the hole components, if any.
+     *    (4) a refcount of the ccbord; used internally when a ccbord
+     *        is accessed from a ccborda (array of ccbord)
+     *    (5) a ptaa for the chain code for the border in relative
+     *        coordinates, where the first pta is the exterior border
+     *        and all other pta are for interior borders (holes)
+     *    (6) a ptaa for the global pixel loc rendition of the border,
+     *        where the first pta is the exterior border and all other
+     *        pta are for interior borders (holes).
+     *        This is derived from the local or step chain code.
+     *    (7) a numaa for the chain code for the border as orientation
+     *        directions between successive border pixels, where
+     *        the first numa is the exterior border and all other
+     *        numa are for interior borders (holes).  This is derived
+     *        from the local chain code.  The 8 directions are 0 - 7.
+     *    (8) a pta for a single chain for each c.c., comprised of outer
+     *        and hole borders, plus cut paths between them, all in
+     *        local coords.
+     *    (9) a pta for a single chain for each c.c., comprised of outer
+     *        and hole borders, plus cut paths between them, all in
+     *        global coords.
+     * 
+ */ +struct CCBord +{ + struct Pix *pix; /*!< component bitmap (min size) */ + struct Boxa *boxa; /*!< regions of each closed curve */ + struct Pta *start; /*!< initial border pixel locations */ + l_int32 refcount; /*!< number of handles; start at 1 */ + struct Ptaa *local; /*!< ptaa of chain pixels (local) */ + struct Ptaa *global; /*!< ptaa of chain pixels (global) */ + struct Numaa *step; /*!< numaa of chain code (step dir) */ + struct Pta *splocal; /*!< pta of single chain (local) */ + struct Pta *spglobal; /*!< pta of single chain (global) */ +}; +typedef struct CCBord CCBORD; + +/*! Array of CCBord */ +struct CCBorda +{ + struct Pix *pix; /*!< input pix (may be null) */ + l_int32 w; /*!< width of pix */ + l_int32 h; /*!< height of pix */ + l_int32 n; /*!< number of ccbord in ptr array */ + l_int32 nalloc; /*!< number of ccbord ptrs allocated */ + struct CCBord **ccb; /*!< ccb ptr array */ +}; +typedef struct CCBorda CCBORDA; + + +#endif /* LEPTONICA_CCBORD_H */ + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ccthin.c b/hgdriver/3rdparty/hgOCR/leptonica/ccthin.c new file mode 100644 index 0000000..f622034 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ccthin.c @@ -0,0 +1,472 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file ccthin.c + *
+ *
+ *     PIXA   *pixaThinConnected()
+ *     PIX    *pixThinConnected()
+ *     PIX    *pixThinConnectedBySet()
+ *     SELA   *selaMakeThinSets()
+ * 
+ */ + +#include "allheaders.h" + + /* ------------------------------------------------------------ + * The sels used here (and their rotated counterparts) are the + * useful 3x3 Sels for thinning. They are defined in sel2.c, + * and the sets are constructed in selaMakeThinSets(). + * The notation is based on "Connectivity-preserving morphological + * image transformations", a version of which can be found at + * http://www.leptonica.com/papers/conn.pdf + * ------------------------------------------------------------ */ + +/*----------------------------------------------------------------* + * CC-preserving thinning * + *----------------------------------------------------------------*/ +/*! + * \brief pixaThinConnected() + * + * \param[in] pixas of 1 bpp pix + * \param[in] type L_THIN_FG, L_THIN_BG + * \param[in] connectivity 4 or 8 + * \param[in] maxiters max number of iters allowed; + * use 0 to iterate until completion + * \return pixds, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes in pixThinConnected().
+ * 
+ */ +PIXA * +pixaThinConnected(PIXA *pixas, + l_int32 type, + l_int32 connectivity, + l_int32 maxiters) +{ +l_int32 i, n, d, same; +PIX *pix1, *pix2; +PIXA *pixad; +SELA *sela; + + PROCNAME("pixaThinConnected"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_THIN_FG && type != L_THIN_BG) + return (PIXA *)ERROR_PTR("invalid fg/bg type", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (maxiters == 0) maxiters = 10000; + + pixaVerifyDepth(pixas, &same, &d); + if (d != 1) + return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL); + + if (connectivity == 4) + sela = selaMakeThinSets(1, 0); + else /* connectivity == 8 */ + sela = selaMakeThinSets(5, 0); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixThinConnectedBySet(pix1, type, sela, maxiters); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + selaDestroy(&sela); + return pixad; +} + + +/*! + * \brief pixThinConnected() + * + * \param[in] pixs 1 bpp + * \param[in] type L_THIN_FG, L_THIN_BG + * \param[in] connectivity 4 or 8 + * \param[in] maxiters max number of iters allowed; + * use 0 to iterate until completion + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See "Connectivity-preserving morphological image transformations,"
+ *          Dan S. Bloomberg, in SPIE Visual Communications and Image
+ *          Processing, Conference 1606, pp. 320-334, November 1991,
+ *          Boston, MA.   A web version is available at
+ *              http://www.leptonica.com/papers/conn.pdf
+ *      (2) This is a simple interface for two of the best iterative
+ *          morphological thinning algorithms, for 4-c.c and 8-c.c.
+ *          Each iteration uses a mixture of parallel operations
+ *          (using several different 3x3 Sels) and serial operations.
+ *          Specifically, each thinning iteration consists of
+ *          four sequential thinnings from each of four directions.
+ *          Each of these thinnings is a parallel composite
+ *          operation, where the union of a set of HMTs are set
+ *          subtracted from the input.  For 4-cc thinning, we
+ *          use 3 HMTs in parallel, and for 8-cc thinning we use 4 HMTs.
+ *      (3) A "good" thinning algorithm is one that generates a skeleton
+ *          that is near the medial axis and has neither pruned
+ *          real branches nor left extra dendritic branches.
+ *      (4) Duality between operations on fg and bg require switching
+ *          the connectivity.  To thin the foreground, which is the usual
+ *          situation, use type == L_THIN_FG.  Thickening the foreground
+ *          is equivalent to thinning the background (type == L_THIN_BG),
+ *          where the alternate connectivity gets preserved.
+ *          For example, to thicken the fg with 2 rounds of iterations
+ *          using 4-c.c., thin the bg using Sels that preserve 8-connectivity:
+ *             Pix *pix = pixThinConnected(pixs, L_THIN_BG, 8, 2);
+ *      (5) This makes and destroys the sela set each time. It's not a large
+ *          overhead, but if you are calling this thousands of times on
+ *          very small images, you can avoid the overhead; e.g.
+ *             Sela *sela = selaMakeThinSets(1, 0);  // for 4-c.c.
+ *             Pix *pix = pixThinConnectedBySet(pixs, L_THIN_FG, sela, 0);
+ *          using set 1 for 4-c.c. and set 5 for 8-c.c operations.
+ * 
+ */ +PIX * +pixThinConnected(PIX *pixs, + l_int32 type, + l_int32 connectivity, + l_int32 maxiters) +{ +PIX *pixd; +SELA *sela; + + PROCNAME("pixThinConnected"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (type != L_THIN_FG && type != L_THIN_BG) + return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (maxiters == 0) maxiters = 10000; + + if (connectivity == 4) + sela = selaMakeThinSets(1, 0); + else /* connectivity == 8 */ + sela = selaMakeThinSets(5, 0); + + pixd = pixThinConnectedBySet(pixs, type, sela, maxiters); + + selaDestroy(&sela); + return pixd; +} + + +/*! + * \brief pixThinConnectedBySet() + * + * \param[in] pixs 1 bpp + * \param[in] type L_THIN_FG, L_THIN_BG + * \param[in] sela of Sels for parallel composite HMTs + * \param[in] maxiters max number of iters allowed; + * use 0 to iterate until completion + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes in pixThinConnected().
+ *      (2) This takes a sela representing one of 11 sets of HMT Sels.
+ *          The HMTs from this set are run in parallel and the result
+ *          is OR'd before being subtracted from the source.  For each
+ *          iteration, this "parallel" thin is performed four times
+ *          sequentially, for sels rotated by 90 degrees in all four
+ *          directions.
+ *      (3) The "parallel" and "sequential" nomenclature is standard
+ *          in digital filtering.  Here, "parallel" operations work on the
+ *          same source (pixd), and accumulate the results in a temp
+ *          image before actually applying them to the source (in this
+ *          case, using an in-place subtraction).  "Sequential" operations
+ *          operate directly on the source (pixd) to produce the result
+ *          (in this case, with four sequential thinning operations, one
+ *          from each of four directions).
+ * 
+ */ +PIX * +pixThinConnectedBySet(PIX *pixs, + l_int32 type, + SELA *sela, + l_int32 maxiters) +{ +l_int32 i, j, r, nsels, same; +PIXA *pixahmt; +PIX **pixhmt; /* array owned by pixahmt; do not destroy! */ +PIX *pix1, *pix2, *pixd; +SEL *sel, *selr; + + PROCNAME("pixThinConnectedBySet"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (type != L_THIN_FG && type != L_THIN_BG) + return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL); + if (!sela) + return (PIX *)ERROR_PTR("sela not defined", procName, NULL); + if (maxiters == 0) maxiters = 10000; + + /* Set up array of temp pix to hold hmts */ + nsels = selaGetCount(sela); + pixahmt = pixaCreate(nsels); + for (i = 0; i < nsels; i++) { + pix1 = pixCreateTemplate(pixs); + pixaAddPix(pixahmt, pix1, L_INSERT); + } + pixhmt = pixaGetPixArray(pixahmt); + if (!pixhmt) { + pixaDestroy(&pixahmt); + return (PIX *)ERROR_PTR("pixhmt array not made", procName, NULL); + } + + /* Set up initial image for fg thinning */ + if (type == L_THIN_FG) + pixd = pixCopy(NULL, pixs); + else /* bg thinning */ + pixd = pixInvert(NULL, pixs); + + /* Thin the fg, with up to maxiters iterations */ + for (i = 0; i < maxiters; i++) { + pix1 = pixCopy(NULL, pixd); /* test for completion */ + for (r = 0; r < 4; r++) { /* over 90 degree rotations of Sels */ + for (j = 0; j < nsels; j++) { /* over individual sels in sela */ + sel = selaGetSel(sela, j); /* not a copy */ + selr = selRotateOrth(sel, r); + pixHMT(pixhmt[j], pixd, selr); + selDestroy(&selr); + if (j > 0) + pixOr(pixhmt[0], pixhmt[0], pixhmt[j]); /* accum result */ + } + pixSubtract(pixd, pixd, pixhmt[0]); /* remove result */ + } + pixEqual(pixd, pix1, &same); + pixDestroy(&pix1); + if (same) { +/* L_INFO("%d iterations to completion\n", procName, i); */ + break; + } + } + + /* This is a bit tricky. If we're thickening the foreground, then + * we get a fg border of thickness equal to the number of + * iterations. This border is connected to all components that + * were initially touching the border, but as it grows, it does + * not touch other growing components -- it leaves a 1 pixel wide + * background between it and the growing components, and that + * thin background prevents the components from growing further. + * This border can be entirely removed as follows: + * (1) Subtract the original (unthickened) image pixs from the + * thickened image. This removes the pixels that were originally + * touching the border. + * (2) Get all remaining pixels that are connected to the border. + * (3) Remove those pixels from the thickened image. */ + if (type == L_THIN_BG) { + pixInvert(pixd, pixd); /* finish with duality */ + pix1 = pixSubtract(NULL, pixd, pixs); + pix2 = pixExtractBorderConnComps(pix1, 4); + pixSubtract(pixd, pixd, pix2); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixaDestroy(&pixahmt); + return pixd; +} + + +/*! + * \brief selaMakeThinSets() + * + * \param[in] index into specific sets + * \param[in] debug 1 to output display of sela + * \return sela, or NULL on error + * + *
+ * Notes:
+ *      (1) These are specific sets of HMTs to be used in parallel for
+ *          for thinning from each of four directions.
+ *      (2) The sets are indexed as follows:
+ *          For thinning (e.g., run to completion):
+ *              index = 1     sel_4_1, sel_4_2, sel_4_3
+ *              index = 2     sel_4_1, sel_4_5, sel_4_6
+ *              index = 3     sel_4_1, sel_4_7, sel_4_7_rot
+ *              index = 4     sel_48_1, sel_48_1_rot, sel_48_2
+ *              index = 5     sel_8_2, sel_8_3, sel_8_5, sel_8_6
+ *              index = 6     sel_8_2, sel_8_3, sel_48_2
+ *              index = 7     sel_8_1, sel_8_5, sel_8_6
+ *              index = 8     sel_8_2, sel_8_3, sel_8_8, sel_8_9
+ *              index = 9     sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot
+ *          For thickening (e.g., just a few iterations):
+ *              index = 10    sel_4_2, sel_4_3
+ *              index = 11    sel_8_4
+ *      (3) For a very smooth skeleton, use set 1 for 4 connected and
+ *          set 5 for 8 connected thins.
+ * 
+ */ +SELA * +selaMakeThinSets(l_int32 index, + l_int32 debug) +{ +SEL *sel; +SELA *sela1, *sela2, *sela3; + + PROCNAME("selaMakeThinSets"); + + if (index < 1 || index > 11) + return (SELA *)ERROR_PTR("invalid index", procName, NULL); + + sela2 = selaCreate(4); + switch(index) + { + case 1: + sela1 = sela4ccThin(NULL); + selaFindSelByName(sela1, "sel_4_1", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_3", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 2: + sela1 = sela4ccThin(NULL); + selaFindSelByName(sela1, "sel_4_1", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_5", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_6", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 3: + sela1 = sela4ccThin(NULL); + selaFindSelByName(sela1, "sel_4_1", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_7", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + sel = selRotateOrth(sel, 1); + selaAddSel(sela2, sel, "sel_4_7_rot", L_INSERT); + break; + case 4: + sela1 = sela4and8ccThin(NULL); + selaFindSelByName(sela1, "sel_48_1", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + sel = selRotateOrth(sel, 1); + selaAddSel(sela2, sel, "sel_48_1_rot", L_INSERT); + selaFindSelByName(sela1, "sel_48_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 5: + sela1 = sela8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_3", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_5", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_6", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 6: + sela1 = sela8ccThin(NULL); + sela3 = sela4and8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_3", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela3, "sel_48_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaDestroy(&sela3); + break; + case 7: + sela1 = sela8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_1", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_5", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_6", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 8: + sela1 = sela8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_3", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_8", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_9", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 9: + sela1 = sela8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_5", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_6", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_8_7", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + sel = selRotateOrth(sel, 1); + selaAddSel(sela2, sel, "sel_8_7_rot", L_INSERT); + break; + case 10: /* thicken for this one; use just a few iterations */ + sela1 = sela4ccThin(NULL); + selaFindSelByName(sela1, "sel_4_2", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + selaFindSelByName(sela1, "sel_4_3", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + case 11: /* thicken for this one; use just a few iterations */ + sela1 = sela8ccThin(NULL); + selaFindSelByName(sela1, "sel_8_4", NULL, &sel); + selaAddSel(sela2, sel, NULL, L_COPY); + break; + } + + /* Optionally display the sel set */ + if (debug) { + PIX *pix1; + char buf[32]; + lept_mkdir("/lept/sels"); + pix1 = selaDisplayInPix(sela2, 35, 3, 15, 4); + snprintf(buf, sizeof(buf), "/tmp/lept/sels/set%d.png", index); + pixWrite(buf, pix1, IFF_PNG); + pixDisplay(pix1, 100, 100); + pixDestroy(&pix1); + } + + selaDestroy(&sela1); + return sela2; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/classapp.c b/hgdriver/3rdparty/hgOCR/leptonica/classapp.c new file mode 100644 index 0000000..be17961 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/classapp.c @@ -0,0 +1,1048 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file classapp.c + *
+ *
+ *      Top-level jb2 correlation and rank-hausdorff
+ *         l_int32         jbCorrelation()
+ *         l_int32         jbRankHaus()
+ *
+ *      Extract and classify words in textline order
+ *         JBCLASSER      *jbWordsInTextlines()
+ *         l_int32         pixGetWordsInTextlines()
+ *         l_int32         pixGetWordBoxesInTextlines()
+ *
+ *      Extract word and character bounding boxes
+ *         l_int32         pixFindWordAndCharacterBoxes()
+ *
+ *      Use word bounding boxes to compare page images
+ *         NUMAA          *boxaExtractSortedPattern()
+ *         l_int32         numaaCompareImagesByBoxes()
+ *         static l_int32  testLineAlignmentX()
+ *         static l_int32  countAlignedMatches()
+ *         static void     printRowIndices()
+ * 
+ */ + +#include +#include "allheaders.h" + +//static const l_int32 L_BUF_SIZE = 512; /*!< size of filename buffer */ +#define L_BUF_SIZE 512 +static const l_int32 JB_WORDS_MIN_WIDTH = 5; /*!< min. word width in pixels */ +static const l_int32 JB_WORDS_MIN_HEIGHT = 3; /*!< min. word height in pixels */ + + /* Static comparison functions */ +static l_int32 testLineAlignmentX(NUMA *na1, NUMA *na2, l_int32 shiftx, + l_int32 delx, l_int32 nperline); +static l_int32 countAlignedMatches(NUMA *nai1, NUMA *nai2, NUMA *nasx, + NUMA *nasy, l_int32 n1, l_int32 n2, + l_int32 delx, l_int32 dely, + l_int32 nreq, l_int32 *psame, + l_int32 debugflag); +static void printRowIndices(l_int32 *index1, l_int32 n1, + l_int32 *index2, l_int32 n2); + + +/*------------------------------------------------------------------* + * Top-level jb2 correlation and rank-hausdorff * + *------------------------------------------------------------------*/ +/*! + * \brief jbCorrelation() + * + * \param[in] dirin directory of input images + * \param[in] thresh typically ~0.8 + * \param[in] weight typically ~0.6 + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] rootname for output files + * \param[in] firstpage 0-based + * \param[in] npages use 0 for all pages in dirin + * \param[in] renderflag 1 to render from templates; 0 to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The images must be 1 bpp.  If they are not, you can convert
+ *          them using convertFilesTo1bpp().
+ *      (2) See prog/jbcorrelation for generating more output (e.g.,
+ *          for debugging)
+ * 
+ */ +l_ok +jbCorrelation(const char *dirin, + l_float32 thresh, + l_float32 weight, + l_int32 components, + const char *rootname, + l_int32 firstpage, + l_int32 npages, + l_int32 renderflag) +{ +char filename[L_BUF_SIZE]; +l_int32 nfiles, i, numpages; +JBDATA *data; +JBCLASSER *classer; +PIX *pix; +PIXA *pixa; +SARRAY *safiles; + + PROCNAME("jbCorrelation"); + + if (!dirin) + return ERROR_INT("dirin not defined", procName, 1); + if (!rootname) + return ERROR_INT("rootname not defined", procName, 1); + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return ERROR_INT("components invalid", procName, 1); + + safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages); + nfiles = sarrayGetCount(safiles); + + /* Classify components */ + classer = jbCorrelationInit(components, 0, 0, thresh, weight); + jbAddPages(classer, safiles); + + /* Save data */ + data = jbDataSave(classer); + jbDataWrite(rootname, data); + + /* Optionally, render pages using class templates */ + if (renderflag) { + pixa = jbDataRender(data, FALSE); + numpages = pixaGetCount(pixa); + if (numpages != nfiles) + fprintf(stderr, "numpages = %d, nfiles = %d, not equal!\n", + numpages, nfiles); + for (i = 0; i < numpages; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + snprintf(filename, L_BUF_SIZE, "%s.%04d", rootname, i); + fprintf(stderr, "filename: %s\n", filename); + pixWrite(filename, pix, IFF_PNG); + pixDestroy(&pix); + } + pixaDestroy(&pixa); + } + + sarrayDestroy(&safiles); + jbClasserDestroy(&classer); + jbDataDestroy(&data); + return 0; +} + + +/*! + * \brief jbRankHaus() + * + * \param[in] dirin directory of input images + * \param[in] size of Sel used for dilation; typ. 2 + * \param[in] rank rank value of match; typ. 0.97 + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] rootname for output files + * \param[in] firstpage 0-based + * \param[in] npages use 0 for all pages in dirin + * \param[in] renderflag 1 to render from templates; 0 to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See prog/jbrankhaus for generating more output (e.g.,
+ *          for debugging)
+ * 
+ */ +l_ok +jbRankHaus(const char *dirin, + l_int32 size, + l_float32 rank, + l_int32 components, + const char *rootname, + l_int32 firstpage, + l_int32 npages, + l_int32 renderflag) +{ +char filename[L_BUF_SIZE]; +l_int32 nfiles, i, numpages; +JBDATA *data; +JBCLASSER *classer; +PIX *pix; +PIXA *pixa; +SARRAY *safiles; + + PROCNAME("jbRankHaus"); + + if (!dirin) + return ERROR_INT("dirin not defined", procName, 1); + if (!rootname) + return ERROR_INT("rootname not defined", procName, 1); + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return ERROR_INT("components invalid", procName, 1); + + safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages); + nfiles = sarrayGetCount(safiles); + + /* Classify components */ + classer = jbRankHausInit(components, 0, 0, size, rank); + jbAddPages(classer, safiles); + + /* Save data */ + data = jbDataSave(classer); + jbDataWrite(rootname, data); + + /* Optionally, render pages using class templates */ + if (renderflag) { + pixa = jbDataRender(data, FALSE); + numpages = pixaGetCount(pixa); + if (numpages != nfiles) + fprintf(stderr, "numpages = %d, nfiles = %d, not equal!\n", + numpages, nfiles); + for (i = 0; i < numpages; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + snprintf(filename, L_BUF_SIZE, "%s.%04d", rootname, i); + fprintf(stderr, "filename: %s\n", filename); + pixWrite(filename, pix, IFF_PNG); + pixDestroy(&pix); + } + pixaDestroy(&pixa); + } + + sarrayDestroy(&safiles); + jbClasserDestroy(&classer); + jbDataDestroy(&data); + return 0; +} + + + +/*------------------------------------------------------------------* + * Extract and classify words in textline order * + *------------------------------------------------------------------*/ +/*! + * \brief jbWordsInTextlines() + * + * \param[in] dirin directory of input pages + * \param[in] reduction 1 for full res; 2 for half-res + * \param[in] maxwidth of word mask components, to be kept + * \param[in] maxheight of word mask components, to be kept + * \param[in] thresh on correlation; 0.80 is reasonable + * \param[in] weight for handling thick text; 0.6 is reasonable + * \param[out] pnatl numa with textline index for each component + * \param[in] firstpage 0-based + * \param[in] npages use 0 for all pages in dirin + * \return classer for the set of pages + * + *
+ * Notes:
+ *      (1) This is a high-level function.  See prog/jbwords for example
+ *          of usage.
+ *      (2) Typically, use input of 75 - 150 ppi for finding words.
+ * 
+ */ +JBCLASSER * +jbWordsInTextlines(const char *dirin, + l_int32 reduction, + l_int32 maxwidth, + l_int32 maxheight, + l_float32 thresh, + l_float32 weight, + NUMA **pnatl, + l_int32 firstpage, + l_int32 npages) +{ +char *fname; +l_int32 nfiles, i, w, h; +BOXA *boxa; +JBCLASSER *classer; +NUMA *nai, *natl; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *safiles; + + PROCNAME("jbWordsInTextlines"); + + if (!pnatl) + return (JBCLASSER *)ERROR_PTR("&natl not defined", procName, NULL); + *pnatl = NULL; + if (!dirin) + return (JBCLASSER *)ERROR_PTR("dirin not defined", procName, NULL); + if (reduction != 1 && reduction != 2) + return (JBCLASSER *)ERROR_PTR("reduction not in {1,2}", procName, NULL); + + safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages); + nfiles = sarrayGetCount(safiles); + + /* Classify components */ + classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight); + classer->safiles = sarrayCopy(safiles); + natl = numaCreate(0); + *pnatl = natl; + for (i = 0; i < nfiles; i++) { + fname = sarrayGetString(safiles, i, L_NOCOPY); + if ((pix1 = pixRead(fname)) == NULL) { + L_WARNING("image file %d not read\n", procName, i); + continue; + } + if (reduction == 1) + pix2 = pixClone(pix1); + else /* reduction == 2 */ + pix2 = pixReduceRankBinaryCascade(pix1, 1, 0, 0, 0); + pixGetWordsInTextlines(pix2, JB_WORDS_MIN_WIDTH, + JB_WORDS_MIN_HEIGHT, maxwidth, maxheight, + &boxa, &pixa, &nai); + pixGetDimensions(pix2, &w, &h, NULL); + classer->w = w; + classer->h = h; + jbAddPageComponents(classer, pix2, boxa, pixa); + numaJoin(natl, nai, 0, -1); + pixDestroy(&pix1); + pixDestroy(&pix2); + numaDestroy(&nai); + boxaDestroy(&boxa); + pixaDestroy(&pixa); + } + + sarrayDestroy(&safiles); + return classer; +} + + +/*! + * \brief pixGetWordsInTextlines() + * + * \param[in] pixs 1 bpp, typ. 75 - 150 ppi + * \param[in] minwidth of saved components; smaller are discarded + * \param[in] minheight of saved components; smaller are discarded + * \param[in] maxwidth of saved components; larger are discarded + * \param[in] maxheight of saved components; larger are discarded + * \param[out] pboxad word boxes sorted in textline line order + * \param[out] ppixad word images sorted in textline line order + * \param[out] pnai index of textline for each word + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input should be at a resolution of between 75 and 150 ppi.
+ *      (2) The four size constraints on saved components are all
+ *          scaled by %reduction.
+ *      (3) The result are word images (and their b.b.), extracted in
+ *          textline order, at either full res or 2x reduction,
+ *          and with a numa giving the textline index for each word.
+ *      (4) The pixa and boxa interfaces should make this type of
+ *          application simple to put together.  The steps are:
+ *           ~ generate first estimate of word masks
+ *           ~ get b.b. of these, and remove the small and big ones
+ *           ~ extract pixa of the word images, using the b.b.
+ *           ~ sort actual word images in textline order (2d)
+ *           ~ flatten them to a pixa (1d), saving the textline index
+ *             for each pix
+ *      (5) In an actual application, it may be desirable to pre-filter
+ *          the input image to remove large components, to extract
+ *          single columns of text, and to deskew them.  For example,
+ *          to remove both large components and small noisy components
+ *          that can interfere with the statistics used to estimate
+ *          parameters for segmenting by words, but still retain text lines,
+ *          the following image preprocessing can be done:
+ *                Pix *pixt = pixMorphSequence(pixs, "c40.1", 0);
+ *                Pix *pixf = pixSelectBySize(pixt, 0, 60, 8,
+ *                                     L_SELECT_HEIGHT, L_SELECT_IF_LT, NULL);
+ *                pixAnd(pixf, pixf, pixs);  // the filtered image
+ *          The closing turns text lines into long blobs, but does not
+ *          significantly increase their height.  But if there are many
+ *          small connected components in a dense texture, this is likely
+ *          to generate tall components that will be eliminated in pixf.
+ * 
+ */ +l_ok +pixGetWordsInTextlines(PIX *pixs, + l_int32 minwidth, + l_int32 minheight, + l_int32 maxwidth, + l_int32 maxheight, + BOXA **pboxad, + PIXA **ppixad, + NUMA **pnai) +{ +BOXA *boxa1, *boxad; +BOXAA *baa; +NUMA *nai; +NUMAA *naa; +PIXA *pixa1, *pixad; +PIXAA *paa; + + PROCNAME("pixGetWordsInTextlines"); + + if (!pboxad || !ppixad || !pnai) + return ERROR_INT("&boxad, &pixad, &nai not all defined", procName, 1); + *pboxad = NULL; + *ppixad = NULL; + *pnai = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Get the bounding boxes of the words from the word mask. */ + pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight, + &boxa1, NULL, NULL); + + /* Generate a pixa of the word images */ + pixa1 = pixaCreateFromBoxa(pixs, boxa1, 0, 0, NULL); + + /* Sort the bounding boxes of these words by line. We use the + * index mapping to allow identical sorting of the pixa. */ + baa = boxaSort2d(boxa1, &naa, -1, -1, 4); + paa = pixaSort2dByIndex(pixa1, naa, L_CLONE); + + /* Flatten the word paa */ + pixad = pixaaFlattenToPixa(paa, &nai, L_CLONE); + boxad = pixaGetBoxa(pixad, L_COPY); + + *pnai = nai; + *pboxad = boxad; + *ppixad = pixad; + + pixaDestroy(&pixa1); + boxaDestroy(&boxa1); + boxaaDestroy(&baa); + pixaaDestroy(&paa); + numaaDestroy(&naa); + return 0; +} + + +/*! + * \brief pixGetWordBoxesInTextlines() + * + * \param[in] pixs 1 bpp, typ. 75 - 150 ppi + * \param[in] minwidth of saved components; smaller are discarded + * \param[in] minheight of saved components; smaller are discarded + * \param[in] maxwidth of saved components; larger are discarded + * \param[in] maxheight of saved components; larger are discarded + * \param[out] pboxad word boxes sorted in textline line order + * \param[out] pnai [optional] index of textline for each word + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input should be at a resolution of between 75 and 150 ppi.
+ *      (2) This is a special version of pixGetWordsInTextlines(), that
+ *          just finds the word boxes in line order, with a numa
+ *          giving the textline index for each word.
+ *          See pixGetWordsInTextlines() for more details.
+ * 
+ */ +l_ok +pixGetWordBoxesInTextlines(PIX *pixs, + l_int32 minwidth, + l_int32 minheight, + l_int32 maxwidth, + l_int32 maxheight, + BOXA **pboxad, + NUMA **pnai) +{ +BOXA *boxa1; +BOXAA *baa; +NUMA *nai; + + PROCNAME("pixGetWordBoxesInTextlines"); + + if (pnai) *pnai = NULL; + if (!pboxad) + return ERROR_INT("&boxad and &nai not both defined", procName, 1); + *pboxad = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Get the bounding boxes of the words from the word mask. */ + pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight, + &boxa1, NULL, NULL); + + /* 2D sort the bounding boxes of these words. */ + baa = boxaSort2d(boxa1, NULL, 3, -5, 5); + + /* Flatten the boxaa, saving the boxa index for each box */ + *pboxad = boxaaFlattenToBoxa(baa, &nai, L_CLONE); + + if (pnai) + *pnai = nai; + else + numaDestroy(&nai); + boxaDestroy(&boxa1); + boxaaDestroy(&baa); + return 0; +} + + +/*------------------------------------------------------------------* + * Extract word and character bounding boxes * + *------------------------------------------------------------------*/ +/*! + * \brief pixFindWordAndCharacterBoxes() + * + * \param[in] pixs 2, 4, 8 or 32 bpp; colormap OK; typ. 300 ppi + * \param[in] boxs [optional] region to select in pixs + * \param[in] thresh binarization threshold (typ. 100 - 150) + * \param[out] pboxaw return the word boxes + * \param[out] pboxaac return the character boxes + * \param[in] debugdir [optional] for debug images; use NULL to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %boxs == NULL, the entire input image is used.
+ *      (2) Having an input pix that is not 1bpp is necessary to reduce
+ *          touching characters by using a low binarization threshold.
+ *          Suggested thresholds are between 100 and 150.
+ *      (3) The coordinates in the output boxes are global, with respect
+ *          to the input image.
+ * 
+ */ +l_ok +pixFindWordAndCharacterBoxes(PIX *pixs, + BOX *boxs, + l_int32 thresh, + BOXA **pboxaw, + BOXAA **pboxaac, + const char *debugdir) +{ +char *debugfile, *subdir; +l_int32 i, xs, ys, xb, yb, nb, loc; +l_float32 scalefact; +BOX *box1, *box2; +BOXA *boxa1, *boxa1a, *boxa2, *boxa3, *boxa4, *boxa5, *boxaw; +BOXAA *boxaac; +PIX *pix1, *pix2, *pix3, *pix3a, *pix4, *pix5; + + PROCNAME("pixFindWordAndCharacterBoxes"); + + if (pboxaw) *pboxaw = NULL; + if (pboxaac) *pboxaac = NULL; + if (!pboxaw || !pboxaac) + return ERROR_INT("&boxaw and &boxaac not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) == 1) + return ERROR_INT("pixs not defined or 1 bpp", procName, 1); + if (thresh > 150) + L_WARNING("threshold is %d; may be too high\n", procName, thresh); + + if (boxs) { + if ((pix1 = pixClipRectangle(pixs, boxs, NULL)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + boxGetGeometry(boxs, &xs, &ys, NULL, NULL); + } else { + pix1 = pixClone(pixs); + xs = ys = 0; + } + + /* Convert pix1 to 8 bpp gray if necessary */ + pix2 = pixConvertTo8(pix1, FALSE); + + /* To find the words and letters, work with 1 bpp images and use + * a low threshold to reduce the number of touching characters. */ + pix3 = pixConvertTo1(pix2, thresh); + + /* Work at about 120 ppi to find the word bounding boxes. */ + pix3a = pixScaleToResolution(pix3, 120.0, 300.0, &scalefact); + + /* First find the words, removing the very small things like + * dots over the 'i' that weren't included in word boxes. */ + pixGetWordBoxesInTextlines(pix3a, 1, 4, 150, 40, &boxa1a, NULL); + boxa1 = boxaTransform(boxa1a, 0, 0, 1.0 / scalefact, 1.0 / scalefact); + if (debugdir) { + loc = 0; + subdir = stringReplaceSubstr(debugdir, "/tmp/", "", &loc, NULL); + lept_mkdir(subdir); + LEPT_FREE(subdir); + pix4 = pixConvertTo32(pix2); + pixRenderBoxaArb(pix4, boxa1, 2, 255, 0, 0); + debugfile = stringJoin(debugdir, "/words.png"); + pixWrite(debugfile, pix4, IFF_PNG); + pixDestroy(&pix4); + LEPT_FREE(debugfile); + } + + /* Now find the letters at 300 ppi */ + nb = boxaGetCount(boxa1); + boxaw = boxaCreate(nb); + boxaac = boxaaCreate(nb); + *pboxaw = boxaw; + *pboxaac = boxaac; + for (i = 0; i < nb; i++) { + box1 = boxaGetBox(boxa1, i, L_COPY); + boxGetGeometry(box1, &xb, &yb, NULL, NULL); + pix4 = pixClipRectangle(pix3, box1, NULL); + /* Join detached parts of characters vertically */ + pix5 = pixMorphSequence(pix4, "c1.10", 0); + /* The connected components should mostly be characters */ + boxa2 = pixConnCompBB(pix5, 4); + /* Remove very small pieces */ + boxa3 = boxaSelectBySize(boxa2, 2, 5, L_SELECT_IF_BOTH, + L_SELECT_IF_GTE, NULL); + /* Order left to right */ + boxa4 = boxaSort(boxa3, L_SORT_BY_X, L_SORT_INCREASING, NULL); + /* Express locations with reference to the full input image */ + boxa5 = boxaTransform(boxa4, xs + xb, ys + yb, 1.0, 1.0); + box2 = boxTransform(box1, xs, ys, 1.0, 1.0); + + /* Ignore any boxa with no boxes after size filtering */ + if (boxaGetCount(boxa5) > 0) { + boxaAddBox(boxaw, box2, L_INSERT); + boxaaAddBoxa(boxaac, boxa5, L_INSERT); + } else { + boxDestroy(&box2); + boxaDestroy(&boxa5); + } + boxDestroy(&box1); + pixDestroy(&pix4); + pixDestroy(&pix5); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + boxaDestroy(&boxa4); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix3a); + boxaDestroy(&boxa1); + boxaDestroy(&boxa1a); + if (debugdir) { + pix4 = pixConvertTo32(pixs); + boxa2 = boxaaFlattenToBoxa(boxaac, NULL, L_COPY); + pixRenderBoxaArb(pix4, boxa2, 2, 255, 0, 0); + boxa3 = boxaAdjustSides(boxaw, -2, 2, -2, 2); + pixRenderBoxaArb(pix4, boxa3, 2, 0, 255, 0); + debugfile = stringJoin(debugdir, "/chars.png"); + pixWrite(debugfile, pix4, IFF_PNG); + pixDestroy(&pix4); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + LEPT_FREE(debugfile); + } + return 0; +} + + +/*------------------------------------------------------------------* + * Use word bounding boxes to compare page images * + *------------------------------------------------------------------*/ +/*! + * \brief boxaExtractSortedPattern() + * + * \param[in] boxa typ. of word bounding boxes, in textline order + * \param[in] na index of textline for each box in boxa + * \return naa NUMAA, where each numa represents one textline, + * or NULL on error + * + *
+ * Notes:
+ *      (1) The input is expected to come from pixGetWordBoxesInTextlines().
+ *      (2) Each numa in the output consists of an average y coordinate
+ *          of the first box in the textline, followed by pairs of
+ *          x coordinates representing the left and right edges of each
+ *          of the boxes in the textline.
+ * 
+ */ +NUMAA * +boxaExtractSortedPattern(BOXA *boxa, + NUMA *na) +{ +l_int32 index, nbox, row, prevrow, x, y, w, h; +BOX *box; +NUMA *nad; +NUMAA *naa; + + PROCNAME("boxaExtractSortedPattern"); + + if (!boxa) + return (NUMAA *)ERROR_PTR("boxa not defined", procName, NULL); + if (!na) + return (NUMAA *)ERROR_PTR("na not defined", procName, NULL); + + naa = numaaCreate(0); + nbox = boxaGetCount(boxa); + if (nbox == 0) + return naa; + + prevrow = -1; + for (index = 0; index < nbox; index++) { + box = boxaGetBox(boxa, index, L_CLONE); + numaGetIValue(na, index, &row); + if (row > prevrow) { + if (index > 0) + numaaAddNuma(naa, nad, L_INSERT); + nad = numaCreate(0); + prevrow = row; + boxGetGeometry(box, NULL, &y, NULL, &h); + numaAddNumber(nad, y + h / 2); + } + boxGetGeometry(box, &x, NULL, &w, NULL); + numaAddNumber(nad, x); + numaAddNumber(nad, x + w - 1); + boxDestroy(&box); + } + numaaAddNuma(naa, nad, L_INSERT); + + return naa; +} + + +/*! + * \brief numaaCompareImagesByBoxes() + * + * \param[in] naa1 for image 1, formatted by boxaExtractSortedPattern() + * \param[in] naa2 for image 2, formatted by boxaExtractSortedPattern() + * \param[in] nperline number of box regions to be used in each textline + * \param[in] nreq number of complete row matches required + * \param[in] maxshiftx max allowed x shift between two patterns, in pixels + * \param[in] maxshifty max allowed y shift between two patterns, in pixels + * \param[in] delx max allowed difference in x data, after alignment + * \param[in] dely max allowed difference in y data, after alignment + * \param[out] psame 1 if %nreq row matches are found; 0 otherwise + * \param[in] debugflag 1 for debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Each input numaa describes a set of sorted bounding boxes
+ *          (sorted by textline and, within each textline, from
+ *          left to right) in the images from which they are derived.
+ *          See boxaExtractSortedPattern() for a description of the data
+ *          format in each of the input numaa.
+ *      (2) This function does an alignment between the input
+ *          descriptions of bounding boxes for two images. The
+ *          input parameter %nperline specifies the number of boxes
+ *          to consider in each line when testing for a match, and
+ *          %nreq is the required number of lines that must be well-aligned
+ *          to get a match.
+ *      (3) Testing by alignment has 3 steps:
+ *          (a) Generating the location of word bounding boxes from the
+ *              images (prior to calling this function).
+ *          (b) Listing all possible pairs of aligned rows, based on
+ *              tolerances in horizontal and vertical positions of
+ *              the boxes.  Specifically, all pairs of rows are enumerated
+ *              whose first %nperline boxes can be brought into close
+ *              alignment, based on the delx parameter for boxes in the
+ *              line and within the overall the %maxshiftx and %maxshifty
+ *              constraints.
+ *          (c) Each pair, starting with the first, is used to search
+ *              for a set of %nreq - 1 other pairs that can all be aligned
+ *              with a difference in global translation of not more
+ *              than (%delx, %dely).
+ * 
+ */ +l_ok +numaaCompareImagesByBoxes(NUMAA *naa1, + NUMAA *naa2, + l_int32 nperline, + l_int32 nreq, + l_int32 maxshiftx, + l_int32 maxshifty, + l_int32 delx, + l_int32 dely, + l_int32 *psame, + l_int32 debugflag) +{ +l_int32 n1, n2, i, j, nbox, y1, y2, xl1, xl2; +l_int32 shiftx, shifty, match; +l_int32 *line1, *line2; /* indicator for sufficient boxes in a line */ +l_int32 *yloc1, *yloc2; /* arrays of y value for first box in a line */ +l_int32 *xleft1, *xleft2; /* arrays of x value for left side of first box */ +NUMA *na1, *na2, *nai1, *nai2, *nasx, *nasy; + + PROCNAME("numaaCompareImagesByBoxes"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!naa1) + return ERROR_INT("naa1 not defined", procName, 1); + if (!naa2) + return ERROR_INT("naa2 not defined", procName, 1); + if (nperline < 1) + return ERROR_INT("nperline < 1", procName, 1); + if (nreq < 1) + return ERROR_INT("nreq < 1", procName, 1); + + n1 = numaaGetCount(naa1); + n2 = numaaGetCount(naa2); + if (n1 < nreq || n2 < nreq) + return 0; + + /* Find the lines in naa1 and naa2 with sufficient boxes. + * Also, find the y-values for each of the lines, and the + * LH x-values of the first box in each line. */ + line1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); + line2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); + yloc1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); + yloc2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); + xleft1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); + xleft2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); + if (!line1 || !line2 || !yloc1 || !yloc2 || !xleft1 || !xleft2) + return ERROR_INT("callof failure for an array", procName, 1); + for (i = 0; i < n1; i++) { + na1 = numaaGetNuma(naa1, i, L_CLONE); + numaGetIValue(na1, 0, yloc1 + i); + numaGetIValue(na1, 1, xleft1 + i); + nbox = (numaGetCount(na1) - 1) / 2; + if (nbox >= nperline) + line1[i] = 1; + numaDestroy(&na1); + } + for (i = 0; i < n2; i++) { + na2 = numaaGetNuma(naa2, i, L_CLONE); + numaGetIValue(na2, 0, yloc2 + i); + numaGetIValue(na2, 1, xleft2 + i); + nbox = (numaGetCount(na2) - 1) / 2; + if (nbox >= nperline) + line2[i] = 1; + numaDestroy(&na2); + } + + /* Enumerate all possible line matches. A 'possible' line + * match is one where the x and y shifts for the first box + * in each line are within the maxshiftx and maxshifty + * constraints, and the left and right sides of the remaining + * (nperline - 1) successive boxes are within delx of each other. + * The result is a set of four numas giving parameters of + * each set of matching lines. */ + nai1 = numaCreate(0); /* line index 1 of match */ + nai2 = numaCreate(0); /* line index 2 of match */ + nasx = numaCreate(0); /* shiftx for match */ + nasy = numaCreate(0); /* shifty for match */ + for (i = 0; i < n1; i++) { + if (line1[i] == 0) continue; + y1 = yloc1[i]; + xl1 = xleft1[i]; + na1 = numaaGetNuma(naa1, i, L_CLONE); + for (j = 0; j < n2; j++) { + if (line2[j] == 0) continue; + y2 = yloc2[j]; + if (L_ABS(y1 - y2) > maxshifty) continue; + xl2 = xleft2[j]; + if (L_ABS(xl1 - xl2) > maxshiftx) continue; + shiftx = xl1 - xl2; /* shift to add to x2 values */ + shifty = y1 - y2; /* shift to add to y2 values */ + na2 = numaaGetNuma(naa2, j, L_CLONE); + + /* Now check if 'nperline' boxes in the two lines match */ + match = testLineAlignmentX(na1, na2, shiftx, delx, nperline); + if (match) { + numaAddNumber(nai1, i); + numaAddNumber(nai2, j); + numaAddNumber(nasx, shiftx); + numaAddNumber(nasy, shifty); + } + numaDestroy(&na2); + } + numaDestroy(&na1); + } + + /* Determine if there are a sufficient number of mutually + * aligned matches. Mutually aligned matches place an additional + * constraint on the 'possible' matches, where the relative + * shifts must not exceed the (delx, dely) distances. */ + countAlignedMatches(nai1, nai2, nasx, nasy, n1, n2, delx, dely, + nreq, psame, debugflag); + + LEPT_FREE(line1); + LEPT_FREE(line2); + LEPT_FREE(yloc1); + LEPT_FREE(yloc2); + LEPT_FREE(xleft1); + LEPT_FREE(xleft2); + numaDestroy(&nai1); + numaDestroy(&nai2); + numaDestroy(&nasx); + numaDestroy(&nasy); + return 0; +} + + +static l_int32 +testLineAlignmentX(NUMA *na1, + NUMA *na2, + l_int32 shiftx, + l_int32 delx, + l_int32 nperline) +{ +l_int32 i, xl1, xr1, xl2, xr2, diffl, diffr; + + PROCNAME("testLineAlignmentX"); + + if (!na1) + return ERROR_INT("na1 not defined", procName, 1); + if (!na2) + return ERROR_INT("na2 not defined", procName, 1); + + for (i = 0; i < nperline; i++) { + numaGetIValue(na1, i + 1, &xl1); + numaGetIValue(na1, i + 2, &xr1); + numaGetIValue(na2, i + 1, &xl2); + numaGetIValue(na2, i + 2, &xr2); + diffl = L_ABS(xl1 - xl2 - shiftx); + diffr = L_ABS(xr1 - xr2 - shiftx); + if (diffl > delx || diffr > delx) + return 0; + } + + return 1; +} + + +/* + * \brief countAlignedMatches() + * + * \param[in] nai1, nai2 numas of row pairs for matches + * \param[in] nasx, nasy numas of x and y shifts for the matches + * \param[in] n1, n2 number of rows in images 1 and 2 + * \param[in] delx, dely allowed difference in shifts of the match, + * compared to the reference match + * \param[in] nre1 number of required aligned matches + * \param[out] psame return 1 if %nreq row matches are found; + * 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes 4 input arrays giving parameters of all the
+ *          line matches.  It looks for the maximum set of aligned
+ *          matches (matches with approximately the same overall shifts)
+ *          that do not use rows from either image more than once.
+ * 
+ */ +static l_ok +countAlignedMatches(NUMA *nai1, + NUMA *nai2, + NUMA *nasx, + NUMA *nasy, + l_int32 n1, + l_int32 n2, + l_int32 delx, + l_int32 dely, + l_int32 nreq, + l_int32 *psame, + l_int32 debugflag) +{ +l_int32 i, j, nm, shiftx, shifty, nmatch, diffx, diffy; +l_int32 *ia1, *ia2, *iasx, *iasy, *index1, *index2; + + PROCNAME("countAlignedMatches"); + + if (!nai1 || !nai2 || !nasx || !nasy) + return ERROR_INT("4 input numas not defined", procName, 1); + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + + /* Check for sufficient aligned matches, doing a double iteration + * over the set of raw matches. The row index arrays + * are used to verify that the same rows in either image + * are not used in more than one match. Whenever there + * is a match that is properly aligned, those rows are + * marked in the index arrays. */ + nm = numaGetCount(nai1); /* number of matches */ + if (nm < nreq) + return 0; + + ia1 = numaGetIArray(nai1); + ia2 = numaGetIArray(nai2); + iasx = numaGetIArray(nasx); + iasy = numaGetIArray(nasy); + index1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); /* watch rows */ + index2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); + if (!index1 || !index2) + return ERROR_INT("calloc fail for array", procName, 1); + for (i = 0; i < nm; i++) { + if (*psame == 1) + break; + + /* Reset row index arrays */ + memset(index1, 0, 4 * n1); + memset(index2, 0, 4 * n2); + nmatch = 1; + index1[ia1[i]] = nmatch; /* mark these rows as taken */ + index2[ia2[i]] = nmatch; + shiftx = iasx[i]; /* reference shift between two rows */ + shifty = iasy[i]; /* ditto */ + if (nreq == 1) { + *psame = 1; + break; + } + for (j = 0; j < nm; j++) { + if (j == i) continue; + /* Rows must both be different from any previously seen */ + if (index1[ia1[j]] > 0 || index2[ia2[j]] > 0) continue; + /* Check the shift for this match */ + diffx = L_ABS(shiftx - iasx[j]); + diffy = L_ABS(shifty - iasy[j]); + if (diffx > delx || diffy > dely) continue; + /* We have a match */ + nmatch++; + index1[ia1[j]] = nmatch; /* mark the rows */ + index2[ia2[j]] = nmatch; + if (nmatch >= nreq) { + *psame = 1; + if (debugflag) + printRowIndices(index1, n1, index2, n2); + break; + } + } + } + + LEPT_FREE(ia1); + LEPT_FREE(ia2); + LEPT_FREE(iasx); + LEPT_FREE(iasy); + LEPT_FREE(index1); + LEPT_FREE(index2); + return 0; +} + + +static void +printRowIndices(l_int32 *index1, + l_int32 n1, + l_int32 *index2, + l_int32 n2) +{ +l_int32 i; + + fprintf(stderr, "Index1: "); + for (i = 0; i < n1; i++) { + if (i && (i % 20 == 0)) + fprintf(stderr, "\n "); + fprintf(stderr, "%3d", index1[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Index2: "); + for (i = 0; i < n2; i++) { + if (i && (i % 20 == 0)) + fprintf(stderr, "\n "); + fprintf(stderr, "%3d", index2[i]); + } + fprintf(stderr, "\n"); + return; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colorcontent.c b/hgdriver/3rdparty/hgOCR/leptonica/colorcontent.c new file mode 100644 index 0000000..cbd7f0a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colorcontent.c @@ -0,0 +1,1891 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colorcontent.c + *
+ *
+ *      Builds an image of the color content, on a per-pixel basis,
+ *      as a measure of the amount of divergence of each color
+ *      component (R,G,B) from gray.
+ *         l_int32    pixColorContent()
+ *
+ *      Finds the 'amount' of color in an image, on a per-pixel basis,
+ *      as a measure of the difference of the pixel color from gray.
+ *         PIX       *pixColorMagnitude()
+ *
+ *      Generates a mask over pixels that have sufficient color and
+ *      are not too close to gray pixels.
+ *         PIX       *pixMaskOverColorPixels()
+ *
+ *      Generates a mask over pixels that have little color and
+ *      are not too bright.
+ *         PIX       *pixMaskOverGrayPixels()
+ *
+ *      Generates mask over pixels within a prescribed cube in RGB space
+ *         PIX       *pixMaskOverColorRange()
+ *
+ *      Finds the fraction of pixels with "color" that are not close to black
+ *         l_int32    pixColorFraction()
+ *
+ *      Determine if there are significant color regions that are
+ *      not background in a page image
+ *         l_int32    pixFindColorRegions()
+ *
+ *      Finds the number of perceptually significant gray intensities
+ *      in a grayscale image.
+ *         l_int32    pixNumSignificantGrayColors()
+ *
+ *      Identifies images where color quantization will cause posterization
+ *      due to the existence of many colors in low-gradient regions.
+ *         l_int32    pixColorsForQuantization()
+ *
+ *      Finds the number of unique colors in an image
+ *         l_int32    pixNumColors()
+ *
+ *      Find the most "populated" colors in the image (and quantize)
+ *         l_int32    pixGetMostPopulatedColors()
+ *         PIX       *pixSimpleColorQuantize()
+ *
+ *      Constructs a color histogram based on rgb indices
+ *         NUMA      *pixGetRGBHistogram()
+ *         l_int32    makeRGBIndexTables()
+ *         l_int32    getRGBFromIndex()
+ *
+ *      Identify images that have highlight (red) color
+ *         l_int32    pixHasHighlightRed()
+ *
+ *  Color is tricky.  If we consider gray (r = g = b) to have no color
+ *  content, how should we define the color content in each component
+ *  of an arbitrary pixel, as well as the overall color magnitude?
+ *
+ *  I can think of three ways to define the color content in each component:
+ *
+ *  (1) Linear.  For each component, take the difference from the average
+ *      of all three.
+ *  (2) Linear.  For each component, take the difference from the average
+ *      of the other two.
+ *  (3) Nonlinear.  For each component, take the minimum of the differences
+ *      from the other two.
+ *
+ *  How might one choose from among these?  Consider two different situations:
+ *  (a) r = g = 0, b = 255            {255}   /255/
+ *  (b) r = 0, g = 127, b = 255       {191}   /128/
+ *  How much g is in each of these?  The three methods above give:
+ *  (a)  1: 85   2: 127   3: 0        [85]
+ *  (b)  1: 0    2: 0     3: 127      [0]
+ *  How much b is in each of these?
+ *  (a)  1: 170  2: 255   3: 255      [255]
+ *  (b)  1: 127  2: 191   3: 127      [191]
+ *  The number I'd "like" to give is in [].  (Please don't ask why, it's
+ *  just a feeling.
+ *
+ *  So my preferences seem to be somewhere between (1) and (2).
+ *  (3) is just too "decisive!"  Let's pick (2).
+ *
+ *  We also allow compensation for white imbalance.  For each
+ *  component, we do a linear TRC (gamma = 1.0), where the black
+ *  point remains at 0 and the white point is given by the input
+ *  parameter.  This is equivalent to doing a global remapping,
+ *  as with pixGlobalNormRGB(), followed by color content (or magnitude)
+ *  computation, but without the overhead of first creating the
+ *  white point normalized image.
+ *
+ *  Another useful property is the overall color magnitude in the pixel.
+ *  For this there are again several choices, such as:
+ *      (a) rms deviation from the mean
+ *      (b) the average L1 deviation from the mean
+ *      (c) the maximum (over components) of one of the color
+ *          content measures given above.
+ *
+ *  For now, we will choose two of the methods in (c):
+ *     L_MAX_DIFF_FROM_AVERAGE_2
+ *        Define the color magnitude as the maximum over components
+ *        of the difference between the component value and the
+ *        average of the other two.  It is easy to show that
+ *        this is equivalent to selecting the two component values
+ *        that are closest to each other, averaging them, and
+ *        using the distance from that average to the third component.
+ *        For (a) and (b) above, this value is in {..}.
+ *    L_MAX_MIN_DIFF_FROM_2
+ *        Define the color magnitude as the maximum over components
+ *        of the minimum difference between the component value and the
+ *        other two values.  It is easy to show that this is equivalent
+ *        to selecting the intermediate value of the three differences
+ *        between the three components.  For (a) and (b) above,
+ *        this value is in /../.
+ * 
+ */ + +#include "allheaders.h" + +/* ----------------------------------------------------------------------- * + * Builds an image of the color content, on a per-pixel basis, * + * as a measure of the amount of divergence of each color * + * component (R,G,B) from gray. * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixColorContent() + * + * \param[in] pixs 32 bpp rgb or 8 bpp colormapped + * \param[in] rwhite, gwhite, bwhite color value associated with white point + * \param[in] mingray min gray value for which color is measured + * \param[out] ppixr [optional] 8 bpp red 'content' + * \param[out] ppixg [optional] 8 bpp green 'content' + * \param[out] ppixb [optional] 8 bpp blue 'content' + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns the color content in each component, which is
+ *          a measure of the deviation from gray, and is defined
+ *          as the difference between the component and the average of
+ *          the other two components.  See the discussion at the
+ *          top of this file.
+ *      (2) The three numbers (rwhite, gwhite and bwhite) can be thought
+ *          of as the values in the image corresponding to white.
+ *          They are used to compensate for an unbalanced color white point.
+ *          They must either be all 0 or all non-zero.  To turn this
+ *          off, set them all to 0.
+ *      (3) If the maximum component after white point correction,
+ *          max(r,g,b), is less than mingray, all color components
+ *          for that pixel are set to zero.
+ *          Use mingray = 0 to turn off this filtering of dark pixels.
+ *      (4) Therefore, use 0 for all four input parameters if the color
+ *          magnitude is to be calculated without either white balance
+ *          correction or dark filtering.
+ * 
+ */ +l_ok +pixColorContent(PIX *pixs, + l_int32 rwhite, + l_int32 gwhite, + l_int32 bwhite, + l_int32 mingray, + PIX **ppixr, + PIX **ppixg, + PIX **ppixb) +{ +l_int32 w, h, d, i, j, wplc, wplr, wplg, wplb; +l_int32 rval, gval, bval, rgdiff, rbdiff, gbdiff, maxval, colorval; +l_int32 *rtab, *gtab, *btab; +l_uint32 pixel; +l_uint32 *datac, *datar, *datag, *datab, *linec, *liner, *lineg, *lineb; +NUMA *nar, *nag, *nab; +PIX *pixc; /* rgb */ +PIX *pixr, *pixg, *pixb; /* 8 bpp grayscale */ +PIXCMAP *cmap; + + PROCNAME("pixColorContent"); + + if (!ppixr && !ppixg && !ppixb) + return ERROR_INT("no return val requested", procName, 1); + if (ppixr) *ppixr = NULL; + if (ppixg) *ppixg = NULL; + if (ppixb) *ppixb = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (mingray < 0) mingray = 0; + pixGetDimensions(pixs, &w, &h, &d); + if (mingray > 255) + return ERROR_INT("mingray > 255", procName, 1); + if (rwhite < 0 || gwhite < 0 || bwhite < 0) + return ERROR_INT("some white vals are negative", procName, 1); + if ((rwhite || gwhite || bwhite) && (rwhite * gwhite * bwhite == 0)) + return ERROR_INT("white vals not all zero or all nonzero", procName, 1); + + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return ERROR_INT("pixs neither cmapped nor 32 bpp", procName, 1); + if (cmap) + pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc = pixClone(pixs); + + pixr = pixg = pixb = NULL; + pixGetDimensions(pixc, &w, &h, NULL); + if (ppixr) { + pixr = pixCreate(w, h, 8); + datar = pixGetData(pixr); + wplr = pixGetWpl(pixr); + *ppixr = pixr; + } + if (ppixg) { + pixg = pixCreate(w, h, 8); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + *ppixg = pixg; + } + if (ppixb) { + pixb = pixCreate(w, h, 8); + datab = pixGetData(pixb); + wplb = pixGetWpl(pixb); + *ppixb = pixb; + } + + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + if (rwhite) { /* all white pt vals are nonzero */ + nar = numaGammaTRC(1.0, 0, rwhite); + rtab = numaGetIArray(nar); + nag = numaGammaTRC(1.0, 0, gwhite); + gtab = numaGetIArray(nag); + nab = numaGammaTRC(1.0, 0, bwhite); + btab = numaGetIArray(nab); + } + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + if (pixr) + liner = datar + i * wplr; + if (pixg) + lineg = datag + i * wplg; + if (pixb) + lineb = datab + i * wplb; + for (j = 0; j < w; j++) { + pixel = linec[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + if (rwhite) { /* color correct for white point */ + rval = rtab[rval]; + gval = gtab[gval]; + bval = btab[bval]; + } + if (mingray > 0) { /* dark pixels have no color value */ + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + if (maxval < mingray) + continue; /* colorval = 0 for each component */ + } + rgdiff = L_ABS(rval - gval); + rbdiff = L_ABS(rval - bval); + gbdiff = L_ABS(gval - bval); + if (pixr) { + colorval = (rgdiff + rbdiff) / 2; + SET_DATA_BYTE(liner, j, colorval); + } + if (pixg) { + colorval = (rgdiff + gbdiff) / 2; + SET_DATA_BYTE(lineg, j, colorval); + } + if (pixb) { + colorval = (rbdiff + gbdiff) / 2; + SET_DATA_BYTE(lineb, j, colorval); + } + } + } + + if (rwhite) { + numaDestroy(&nar); + numaDestroy(&nag); + numaDestroy(&nab); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + } + pixDestroy(&pixc); + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Finds the 'amount' of color in an image, on a per-pixel basis, * + * as a measure of the difference of the pixel color from gray. * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixColorMagnitude() + * + * \param[in] pixs 32 bpp rgb or 8 bpp colormapped + * \param[in] rwhite, gwhite, bwhite color value associated with white point + * \param[in] type chooses the method for calculating the color magnitude: + * L_MAX_DIFF_FROM_AVERAGE_2, L_MAX_MIN_DIFF_FROM_2, + * L_MAX_DIFF + * \return pixd 8 bpp, amount of color in each source pixel, + * or NULL on error + * + *
+ * Notes:
+ *      (1) For an RGB image, a gray pixel is one where all three components
+ *          are equal.  We define the amount of color in an RGB pixel as
+ *          a function depending on the absolute value of the differences
+ *          between the three color components.  Consider the two largest
+ *          of these differences.  The pixel component in common to these
+ *          two differences is the color farthest from the other two.
+ *          The color magnitude in an RGB pixel can be taken as one
+ *          of these three definitions:
+ *            (a) The average of these two differences.  This is the
+ *                average distance from the two components that are
+ *                nearest to each other to the third component.
+ *            (b) The minimum value of these two differences.  This is
+ *                the intermediate value of the three distances between
+ *                component values.  Stated otherwise, it is the
+ *                maximum over all components of the minimum distance
+ *                from that component to the other two components.
+ *            (c) The maximum difference between component values.
+ *      (2) As an example, suppose that R and G are the closest in
+ *          magnitude.  Then the color is determined as either:
+ *            (a) The average distance of B from these two:
+ *                   (|B - R| + |B - G|) / 2
+ *            (b) The minimum distance of B from these two:
+ *                   min(|B - R|, |B - G|).
+ *            (c) The maximum distance of B from these two:
+ *                   max(|B - R|, |B - G|)
+ *      (3) The three methods for choosing the color magnitude from
+ *          the components are selected with these flags:
+ *            (a) L_MAX_DIFF_FROM_AVERAGE_2
+ *            (b) L_MAX_MIN_DIFF_FROM_2
+ *            (c) L_MAX_DIFF
+ *      (4) The three numbers (rwhite, gwhite and bwhite) can be thought
+ *          of as the values in the image corresponding to white.
+ *          They are used to compensate for an unbalanced color white point.
+ *          They must either be all 0 or all non-zero.  To turn this
+ *          off, set them all to 0.
+ * 
+ */ +PIX * +pixColorMagnitude(PIX *pixs, + l_int32 rwhite, + l_int32 gwhite, + l_int32 bwhite, + l_int32 type) +{ +l_int32 w, h, d, i, j, wplc, wpld; +l_int32 rval, gval, bval, rdist, gdist, bdist, colorval; +l_int32 rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval; +l_int32 *rtab, *gtab, *btab; +l_uint32 pixel; +l_uint32 *datac, *datad, *linec, *lined; +NUMA *nar, *nag, *nab; +PIX *pixc, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixColorMagnitude"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (type != L_MAX_DIFF_FROM_AVERAGE_2 && type != L_MAX_MIN_DIFF_FROM_2 && + type != L_MAX_DIFF) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (rwhite < 0 || gwhite < 0 || bwhite < 0) + return (PIX *)ERROR_PTR("some white vals are negative", procName, NULL); + if ((rwhite || gwhite || bwhite) && (rwhite * gwhite * bwhite == 0)) + return (PIX *)ERROR_PTR("white vals not all zero or all nonzero", + procName, NULL); + + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (cmap) + pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc = pixClone(pixs); + + pixd = pixCreate(w, h, 8); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datac = pixGetData(pixc); + wplc = pixGetWpl(pixc); + if (rwhite) { /* all white pt vals are nonzero */ + nar = numaGammaTRC(1.0, 0, rwhite); + rtab = numaGetIArray(nar); + nag = numaGammaTRC(1.0, 0, gwhite); + gtab = numaGetIArray(nag); + nab = numaGammaTRC(1.0, 0, bwhite); + btab = numaGetIArray(nab); + } + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linec[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + if (rwhite) { /* color correct for white point */ + rval = rtab[rval]; + gval = gtab[gval]; + bval = btab[bval]; + } + if (type == L_MAX_DIFF_FROM_AVERAGE_2) { + rdist = ((gval + bval ) / 2 - rval); + rdist = L_ABS(rdist); + gdist = ((rval + bval ) / 2 - gval); + gdist = L_ABS(gdist); + bdist = ((rval + gval ) / 2 - bval); + bdist = L_ABS(bdist); + colorval = L_MAX(rdist, gdist); + colorval = L_MAX(colorval, bdist); + } else if (type == L_MAX_MIN_DIFF_FROM_2) { /* intermediate dist */ + rgdist = L_ABS(rval - gval); + rbdist = L_ABS(rval - bval); + gbdist = L_ABS(gval - bval); + maxdist = L_MAX(rgdist, rbdist); + if (gbdist >= maxdist) { + colorval = maxdist; + } else { /* gbdist is smallest or intermediate */ + mindist = L_MIN(rgdist, rbdist); + colorval = L_MAX(mindist, gbdist); + } + } else { /* type == L_MAX_DIFF */ + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + colorval = maxval - minval; + } + SET_DATA_BYTE(lined, j, colorval); + } + } + + if (rwhite) { + numaDestroy(&nar); + numaDestroy(&nag); + numaDestroy(&nab); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + } + pixDestroy(&pixc); + return pixd; +} + + +/* ----------------------------------------------------------------------- * + * Generates a mask over pixels that have sufficient color and * + * are not too close to gray pixels. * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixMaskOverColorPixels() + * + * \param[in] pixs 32 bpp rgb or 8 bpp colormapped + * \param[in] threshdiff threshold for minimum of the max difference + * between components + * \param[in] mindist min allowed distance from nearest non-color pixel + * \return pixd 1 bpp, mask over color pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) The generated mask identifies each pixel as either color or
+ *          non-color.  For a pixel to be color, it must satisfy two
+ *          constraints:
+ *            (a) The max difference between the r,g and b components must
+ *                equal or exceed a threshold %threshdiff.
+ *            (b) It must be at least %mindist (in an 8-connected way)
+ *                from the nearest non-color pixel.
+ *      (2) The distance constraint (b) is only applied if %mindist > 1.
+ *          For example, if %mindist == 2, the color pixels identified
+ *          by (a) are eroded by a 3x3 Sel.  In general, the Sel size
+ *          for erosion is 2 * (%mindist - 1) + 1.
+ *          Why have this constraint?  In scanned images that are
+ *          essentially gray, color artifacts are typically introduced
+ *          in transition regions near sharp edges that go from dark
+ *          to light, so this allows these transition regions to be removed.
+ * 
+ */ +PIX * +pixMaskOverColorPixels(PIX *pixs, + l_int32 threshdiff, + l_int32 mindist) +{ +l_int32 w, h, d, i, j, wpls, wpld, size; +l_int32 rval, gval, bval, minval, maxval; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixc, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMaskOverColorPixels"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (cmap) + pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc = pixClone(pixs); + + pixd = pixCreate(w, h, 1); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixc); + wpls = pixGetWpl(pixc); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + if (maxval - minval >= threshdiff) + SET_DATA_BIT(lined, j); + } + } + + if (mindist > 1) { + size = 2 * (mindist - 1) + 1; + pixErodeBrick(pixd, pixd, size, size); + } + + pixDestroy(&pixc); + return pixd; +} + + +/* ----------------------------------------------------------------------- * + * Generates a mask over pixels that have little color and * + * are not too bright * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixMaskOverGrayPixels() + * + * \param[in] pixs 32 bpp rgb + * \param[in] maxlimit only consider pixels with max component <= %maxlimit + * \param[in] satlimit only consider pixels with saturation <= %satlimit + * \return pixd (1 bpp), or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a mask over rgb pixels that are gray (i.e.,
+ *          have low saturation) and are not too bright.  For example, if
+ *          we know that the gray pixels in %pixs have saturation
+ *          (max - min) less than 10, and brightness (max) less than 200,
+ *             pixMaskOverGrayPixels(pixs, 220, 10)
+ *          will generate a mask over the gray pixels.  Other pixels that
+ *          are not too dark and have a relatively large saturation will
+ *          be little affected.
+ *      (2) The algorithm is related to pixDarkenGray().
+ * 
+ */ +PIX * +pixMaskOverGrayPixels(PIX *pixs, + l_int32 maxlimit, + l_int32 satlimit) +{ +l_int32 w, h, i, j, wpls, wpld; +l_int32 rval, gval, bval, minrg, min, maxrg, max, sat; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixMaskOverGrayPixels"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (maxlimit < 0 || maxlimit > 255) + return (PIX *)ERROR_PTR("invalid maxlimit", procName, NULL); + if (satlimit < 1) + return (PIX *)ERROR_PTR("invalid satlimit", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + sat = max - min; + if (max <= maxlimit && sat <= satlimit) + SET_DATA_BIT(lined, j); + } + } + return pixd; +} + + +/* ----------------------------------------------------------------------- * + * Generates a mask over pixels that have RGB color components * + * within the prescribed range (a cube in RGB color space) * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixMaskOverColorRange() + * + * \param[in] pixs 32 bpp rgb or 8 bpp colormapped + * \param[in] rmin, rmax min and max allowed values for red component + * \param[in] gmin, gmax ditto for green + * \param[in] bmin, bmax ditto for blue + * \return pixd 1 bpp, mask over color pixels, or NULL on error + */ +PIX * +pixMaskOverColorRange(PIX *pixs, + l_int32 rmin, + l_int32 rmax, + l_int32 gmin, + l_int32 gmax, + l_int32 bmin, + l_int32 bmax) +{ +l_int32 w, h, d, i, j, wpls, wpld; +l_int32 rval, gval, bval; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixc, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMaskOverColorRange"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (cmap) + pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc = pixClone(pixs); + + pixd = pixCreate(w, h, 1); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixc); + wpls = pixGetWpl(pixc); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + if (rval < rmin || rval > rmax) continue; + if (gval < gmin || gval > gmax) continue; + if (bval < bmin || bval > bmax) continue; + SET_DATA_BIT(lined, j); + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/* ----------------------------------------------------------------------- * + * Finds the fraction of pixels with "color" that are not close to black * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixColorFraction() + * + * \param[in] pixs 32 bpp rgb + * \param[in] darkthresh threshold near black; if the lightest component + * is below this, the pixel is not considered in + * the statistics; typ. 20 + * \param[in] lightthresh threshold near white; if the darkest component + * is above this, the pixel is not considered in + * the statistics; typ. 244 + * \param[in] diffthresh thresh for the maximum difference between + * component value; below this the pixel is not + * considered to have sufficient color + * \param[in] factor subsampling factor + * \param[out] ppixfract fraction of pixels in intermediate + * brightness range that were considered + * for color content + * \param[out] pcolorfract fraction of pixels that meet the + * criterion for sufficient color; 0.0 on error + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function is asking the question: to what extent does the
+ *          image appear to have color?   The amount of color a pixel
+ *          appears to have depends on both the deviation of the
+ *          individual components from their average and on the average
+ *          intensity itself.  For example, the color will be much more
+ *          obvious with a small deviation from white than the same
+ *          deviation from black.
+ *      (2) Any pixel that meets these three tests is considered a
+ *          colorful pixel:
+ *            (a) the lightest component must equal or exceed %darkthresh
+ *            (b) the darkest component must not exceed %lightthresh
+ *            (c) the max difference between components must equal or
+ *                exceed %diffthresh.
+ *      (3) The dark pixels are removed from consideration because
+ *          they don't appear to have color.
+ *      (4) The very lightest pixels are removed because if an image
+ *          has a lot of "white", the color fraction will be artificially
+ *          low, even if all the other pixels are colorful.
+ *      (5) If pixfract is very small, there are few pixels that are neither
+ *          black nor white.  If colorfract is very small, the pixels
+ *          that are neither black nor white have very little color
+ *          content.  The product 'pixfract * colorfract' gives the
+ *          fraction of pixels with significant color content.
+ *      (6) One use of this function is as a preprocessing step for median
+ *          cut quantization (colorquant2.c), which does a very poor job
+ *          splitting the color space into rectangular volume elements when
+ *          all the pixels are near the diagonal of the color cube.  For
+ *          octree quantization of an image with only gray values, the
+ *          2^(level) octcubes on the diagonal are the only ones
+ *          that can be occupied.
+ * 
+ */ +l_ok +pixColorFraction(PIX *pixs, + l_int32 darkthresh, + l_int32 lightthresh, + l_int32 diffthresh, + l_int32 factor, + l_float32 *ppixfract, + l_float32 *pcolorfract) +{ +l_int32 i, j, w, h, wpl, rval, gval, bval, minval, maxval; +l_int32 total, npix, ncolor; +l_uint32 pixel; +l_uint32 *data, *line; + + PROCNAME("pixColorFraction"); + + if (ppixfract) *ppixfract = 0.0; + if (pcolorfract) *pcolorfract = 0.0; + if (!ppixfract || !pcolorfract) + return ERROR_INT("&pixfract and &colorfract not defined", + procName, 1); + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + npix = ncolor = total = 0; + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + total++; + pixel = line[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + if (minval > lightthresh) /* near white */ + continue; + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + if (maxval < darkthresh) /* near black */ + continue; + + npix++; + if (maxval - minval >= diffthresh) + ncolor++; + } + } + + if (npix == 0) { + L_WARNING("No pixels found for consideration\n", procName); + return 0; + } + *ppixfract = (l_float32)npix / (l_float32)total; + *pcolorfract = (l_float32)ncolor / (l_float32)npix; + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Determine if there are significant color regions in a page image * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixFindColorRegions() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixm [optional] 1 bpp mask image + * \param[in] factor subsample factor; integer >= 1 + * \param[in] lightthresh threshold for component average in lightest + * of 10 buckets; typ. 210; -1 for default + * \param[in] darkthresh threshold to eliminate dark pixels (e.g., text) + * from consideration; typ. 70; -1 for default. + * \param[in] mindiff minimum difference (b - r) and (g - r), used to + * find blue or green pixels; typ. 10; -1 for default + * \param[in] colordiff minimum difference in (max - min) component to + * qualify as a color pixel; typ. 90; -1 for default + * \param[in] edgefract fraction of image half-width and half-height + * for which color pixels are ignored; typ. 0.05. + * \param[out] pcolorfract fraction of 'color' pixels found + * \param[out] pcolormask1 [optional] mask over background color, if any + * \param[out] pcolormask2 [optional] filtered mask over background color + * \param[out] pixadb [optional] debug intermediate results + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function tries to determine if there is a significant
+ *          color or darker region on a scanned page image, where part
+ *          of the image is background that is either white or reddish.
+ *          This also allows extraction of regions of colored pixels that
+ *          have a smaller red component than blue or green components.
+ *      (2) If %pixm exists, pixels under its fg are combined with
+ *          dark pixels to make a mask of pixels not to be considered
+ *          as color candidates.
+ *      (3) There are four thresholds.
+ *          * %lightthresh: compute the average value of each rgb pixel,
+ *            and make 10 buckets by value.  If the lightest bucket gray
+ *            value is below %lightthresh, the image is not considered
+ *            to have a light bg, and this returns 0.0 for %colorfract.
+ *          * %darkthresh: ignore pixels darker than this (typ. fg text).
+ *            We make a 1 bpp mask of these pixels, and then dilate it to
+ *            remove all vestiges of fg from their vicinity.
+ *          * %mindiff: consider pixels with either (b - r) or (g - r)
+ *            being at least this value, as having color.
+ *          * %colordiff: consider pixels where the (max - min) difference
+ *            of the pixel components exceeds this value, as having color.
+ *      (4) All components of color pixels that are touching the image
+ *          border are removed.  Additionally, all pixels within some
+ *          normalized distance %edgefract from the image border can
+ *          be removed.  This insures that dark pixels near the edge
+ *          of the image are not included.
+ *      (5) This returns in %pcolorfract the fraction of pixels that have
+ *          color and are not in the set consisting of an OR between
+ *          %pixm and the dilated dark pixel mask.
+ *      (6) No masks are returned unless light color pixels are found.
+ *          If colorfract > 0.0 and %pcolormask1 is defined, this returns
+ *          a 1 bpp mask with fg pixels over the color background.
+ *          This mask may have some holes in it.
+ *      (7) If colorfract > 0.0 and %pcolormask2 is defined, this returns
+ *          a version of colormask1 where small holes have been filled.
+ *      (8) To generate a boxa of rectangular regions from the overlap
+ *          of components in the filtered mask:
+ *                boxa1 = pixConnCompBB(colormask2, 8);
+ *                boxa2 = boxaCombineOverlaps(boxa1, NULL);
+ *          This is done here in debug mode.
+ * 
+ */ +l_ok +pixFindColorRegions(PIX *pixs, + PIX *pixm, + l_int32 factor, + l_int32 lightthresh, + l_int32 darkthresh, + l_int32 mindiff, + l_int32 colordiff, + l_float32 edgefract, + l_float32 *pcolorfract, + PIX **pcolormask1, + PIX **pcolormask2, + PIXA *pixadb) +{ +l_int32 w, h, count, rval, gval, bval, aveval, proceed; +l_float32 ratio; +l_uint32 *carray; +BOXA *boxa1, *boxa2; +PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3; + + PROCNAME("pixFindColorRegions"); + + if (pcolormask1) *pcolormask1 = NULL; + if (pcolormask2) *pcolormask2 = NULL; + if (!pcolorfract) + return ERROR_INT("&colorfract not defined", procName, 1); + *pcolorfract = 0.0; + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (factor < 1) factor = 1; + if (lightthresh < 0) lightthresh = 210; /* defaults */ + if (darkthresh < 0) darkthresh = 70; + if (mindiff < 0) mindiff = 10; + if (colordiff < 0) colordiff = 90; + if (edgefract < 0.0 || edgefract > 1.0) edgefract = 0.05; + + /* Check if pixm covers most of the image. If so, just return. */ + pixGetDimensions(pixs, &w, &h, NULL); + if (pixm) { + pixCountPixels(pixm, &count, NULL); + ratio = (l_float32)count / ((l_float32)(w) * h); + if (ratio > 0.7) { + if (pixadb) L_INFO("pixm has big fg: %f5.2\n", procName, ratio); + return 0; + } + } + + /* Get the light background color. Use the average component value + * and select the lightest of 10 buckets. Require that it is + * reddish and, using lightthresh, not too dark. */ + pixGetRankColorArray(pixs, 10, L_SELECT_AVERAGE, factor, &carray, 0, 0); + if (!carray) + return ERROR_INT("rank color array not made", procName, 1); + extractRGBValues(carray[9], &rval, &gval, &bval); + if (pixadb) L_INFO("lightest background color: (r,g,b) = (%d,%d,%d)\n", + procName, rval, gval, bval); + proceed = TRUE; + if ((rval < bval - 2) || (rval < gval - 2)) { + if (pixadb) L_INFO("background not reddish\n", procName); + proceed = FALSE; + } + aveval = (rval + gval + bval) / 3; + if (aveval < lightthresh) { + if (pixadb) L_INFO("background too dark\n", procName); + proceed = FALSE; + } + if (pixadb) { + pix1 = pixDisplayColorArray(carray, 10, 120, 3, 6); + pixaAddPix(pixadb, pix1, L_INSERT); + } + LEPT_FREE(carray); + if (proceed == FALSE) return 0; + + /* Make a mask pixm1 over the dark pixels in the image: + * convert to gray using the average of the components; + * threshold using darkthresh; do a small dilation; + * combine with pixm. */ + pix1 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33); + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + pixm1 = pixThresholdToBinary(pix1, darkthresh); + pixDilateBrick(pixm1, pixm1, 7, 7); + if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY); + if (pixm) { + pixOr(pixm1, pixm1, pixm); + if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY); + } + pixDestroy(&pix1); + + /* Make masks over pixels that are bluish, or greenish, or + have a very large color saturation (max - min) value. */ + pixm2 = pixConvertRGBToBinaryArb(pixs, -1.0, 0.0, 1.0, mindiff, + L_SELECT_IF_GTE); /* b - r */ + if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); + pix1 = pixConvertRGBToBinaryArb(pixs, -1.0, 1.0, 0.0, mindiff, + L_SELECT_IF_GTE); /* g - r */ + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + pixOr(pixm2, pixm2, pix1); + pixDestroy(&pix1); + pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF); + pix2 = pixThresholdToBinary(pix1, colordiff); + pixInvert(pix2, pix2); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + pixOr(pixm2, pixm2, pix2); + if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* Subtract the dark pixels represented by pixm1. + * pixm2 now holds all the color pixels of interest */ + pixSubtract(pixm2, pixm2, pixm1); + pixDestroy(&pixm1); + if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); + + /* But we're not quite finished. Remove pixels from any component + * that is touching the image border. False color pixels can + * sometimes be found there if the image is much darker near + * the border, due to oxidation or reduced illumination. Also + * remove any pixels within the normalized fraction %distfract + * of the image border. */ + pixm3 = pixRemoveBorderConnComps(pixm2, 8); + pixDestroy(&pixm2); + if (edgefract > 0.0) { + pix2 = pixMakeSymmetricMask(w, h, edgefract, edgefract, L_USE_INNER); + pixAnd(pixm3, pixm3, pix2); + pixDestroy(&pix2); + } + if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY); + + /* Get the fraction of light color pixels */ + pixCountPixels(pixm3, &count, NULL); + *pcolorfract = (l_float32)count / ((l_float32)(w) * h); + if (pixadb) { + if (count == 0) + L_INFO("no light color pixels found\n", procName); + else + L_INFO("fraction of light color pixels = %5.3f\n", procName, + *pcolorfract); + } + + /* Debug: extract the color pixels from pixs */ + if (pixadb && count > 0) { + /* Use pixm3 to extract the color pixels */ + pix3 = pixCreateTemplate(pixs); + pixSetAll(pix3); + pixCombineMasked(pix3, pixs, pixm3); + pixaAddPix(pixadb, pix3, L_INSERT); + + /* Use additional filtering to extract the color pixels */ + pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15); + pixaAddPix(pixadb, pix3, L_INSERT); + pix5 = pixCreateTemplate(pixs); + pixSetAll(pix5); + pixCombineMasked(pix5, pixs, pix3); + pixaAddPix(pixadb, pix5, L_INSERT); + + /* Get the combined bounding boxes of the mask components + * in pix3, and extract those pixels from pixs. */ + boxa1 = pixConnCompBB(pix3, 8); + boxa2 = boxaCombineOverlaps(boxa1, NULL); + pix4 = pixCreateTemplate(pix3); + pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS); + pixaAddPix(pixadb, pix4, L_INSERT); + pix5 = pixCreateTemplate(pixs); + pixSetAll(pix5); + pixCombineMasked(pix5, pixs, pix4); + pixaAddPix(pixadb, pix5, L_INSERT); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + } + pixaAddPix(pixadb, pixs, L_COPY); + + /* Optional colormask returns */ + if (pcolormask2 && count > 0) + *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15); + if (pcolormask1 && count > 0) + *pcolormask1 = pixm3; + else + pixDestroy(&pixm3); + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Finds the number of perceptually significant gray intensities * + * in a grayscale image. * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixNumSignificantGrayColors() + * + * \param[in] pixs 8 bpp gray + * \param[in] darkthresh dark threshold for minimum intensity to be + * considered; typ. 20 + * \param[in] lightthresh threshold near white, for maximum intensity + * to be considered; typ. 236 + * \param[in] minfract minimum fraction of all pixels to include a level + * as significant; typ. 0.0001; should be < 0.001 + * \param[in] factor subsample factor; integer >= 1 + * \param[out] pncolors number of significant colors; 0 on error + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function is asking the question: how many perceptually
+ *          significant gray color levels is in this pix?
+ *          A color level must meet 3 criteria to be significant:
+ *            ~ it can't be too close to black
+ *            ~ it can't be too close to white
+ *            ~ it must have at least some minimum fractional population
+ *      (2) Use -1 for default values for darkthresh, lightthresh and minfract.
+ *      (3) Choose default of darkthresh = 20, because variations in very
+ *          dark pixels are not visually significant.
+ *      (4) Choose default of lightthresh = 236, because document images
+ *          that have been jpeg'd typically have near-white pixels in the
+ *          8x8 jpeg blocks, and these should not be counted.  It is desirable
+ *          to obtain a clean image by quantizing this noise away.
+ * 
+ */ +l_ok +pixNumSignificantGrayColors(PIX *pixs, + l_int32 darkthresh, + l_int32 lightthresh, + l_float32 minfract, + l_int32 factor, + l_int32 *pncolors) +{ +l_int32 i, w, h, count, mincount, ncolors; +NUMA *na; + + PROCNAME("pixNumSignificantGrayColors"); + + if (!pncolors) + return ERROR_INT("&ncolors not defined", procName, 1); + *pncolors = 0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (darkthresh < 0) darkthresh = 20; /* defaults */ + if (lightthresh < 0) lightthresh = 236; + if (minfract < 0.0) minfract = 0.0001; + if (minfract > 1.0) + return ERROR_INT("minfract > 1.0", procName, 1); + if (minfract >= 0.001) + L_WARNING("minfract too big; likely to underestimate ncolors\n", + procName); + if (lightthresh > 255 || darkthresh >= lightthresh) + return ERROR_INT("invalid thresholds", procName, 1); + if (factor < 1) factor = 1; + + pixGetDimensions(pixs, &w, &h, NULL); + mincount = (l_int32)(minfract * w * h * factor * factor); + if ((na = pixGetGrayHistogram(pixs, factor)) == NULL) + return ERROR_INT("na not made", procName, 1); + ncolors = 2; /* add in black and white */ + for (i = darkthresh; i <= lightthresh; i++) { + numaGetIValue(na, i, &count); + if (count >= mincount) + ncolors++; + } + + *pncolors = ncolors; + numaDestroy(&na); + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Identifies images where color quantization will cause posterization * + * due to the existence of many colors in low-gradient regions. * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixColorsForQuantization() + * \param[in] pixs 8 bpp gray or 32 bpp rgb; with or without colormap + * \param[in] thresh binary threshold on edge gradient; 0 for default + * \param[out] pncolors the number of colors found + * \param[out] piscolor [optional] 1 if significant color is found; + * 0 otherwise. If pixs is 8 bpp, and does not have + * a colormap with color entries, this is 0 + * \param[in] debug 1 to output masked image that is tested for colors; + * 0 otherwise + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This function finds a measure of the number of colors that are
+ *          found in low-gradient regions of an image.  By its
+ *          magnitude relative to some threshold (not specified in
+ *          this function), it gives a good indication of whether
+ *          quantization will generate posterization.   This number
+ *          is larger for images with regions of slowly varying
+ *          intensity (if 8 bpp) or color (if rgb). Such images, if
+ *          quantized, may require dithering to avoid posterization,
+ *          and lossless compression is then expected to be poor.
+ *      (2) If pixs has a colormap, the number of colors returned is
+ *          the number in the colormap.
+ *      (3) It is recommended that document images be reduced to a width
+ *          of 800 pixels before applying this function.  Then it can
+ *          be expected that color detection will be fairly accurate
+ *          and the number of colors will reflect both the content and
+ *          the type of compression to be used.  For less than 15 colors,
+ *          there is unlikely to be a halftone image, and lossless
+ *          quantization should give both a good visual result and
+ *          better compression.
+ *      (4) When using the default threshold on the gradient (15),
+ *          images (both gray and rgb) where ncolors is greater than
+ *          about 15 will compress poorly with either lossless
+ *          compression or dithered quantization, and they may be
+ *          posterized with non-dithered quantization.
+ *      (5) For grayscale images, or images without significant color,
+ *          this returns the number of significant gray levels in
+ *          the low-gradient regions.  The actual number of gray levels
+ *          can be large due to jpeg compression noise in the background.
+ *      (6) Similarly, for color images, the actual number of different
+ *          (r,g,b) colors in the low-gradient regions (rather than the
+ *          number of occupied level 4 octcubes) can be quite large, e.g.,
+ *          due to jpeg compression noise, even for regions that appear
+ *          to be of a single color.  By quantizing to level 4 octcubes,
+ *          most of these superfluous colors are removed from the counting.
+ *      (7) The image is tested for color.  If there is very little color,
+ *          it is thresholded to gray and the number of gray levels in
+ *          the low gradient regions is found.  If the image has color,
+ *          the number of occupied level 4 octcubes is found.
+ *      (8) The number of colors in the low-gradient regions increases
+ *          monotonically with the threshold %thresh on the edge gradient.
+ *      (9) Background: grayscale and color quantization is often useful
+ *          to achieve highly compressed images with little visible
+ *          distortion.  However, gray or color washes (regions of
+ *          low gradient) can defeat this approach to high compression.
+ *          How can one determine if an image is expected to compress
+ *          well using gray or color quantization?  We use the fact that
+ *            * gray washes, when quantized with less than 50 intensities,
+ *              have posterization (visible boundaries between regions
+ *              of uniform 'color') and poor lossless compression
+ *            * color washes, when quantized with level 4 octcubes,
+ *              typically result in both posterization and the occupancy
+ *              of many level 4 octcubes.
+ *          Images can have colors either intrinsically or as jpeg
+ *          compression artifacts.  This function reduces but does not
+ *          completely eliminate measurement of jpeg quantization noise
+ *          in the white background of grayscale or color images.
+ * 
+ */ +l_ok +pixColorsForQuantization(PIX *pixs, + l_int32 thresh, + l_int32 *pncolors, + l_int32 *piscolor, + l_int32 debug) +{ +l_int32 w, h, d, minside, factor; +l_float32 pixfract, colorfract; +PIX *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm; +PIXCMAP *cmap; + + PROCNAME("pixColorsForQuantization"); + + if (piscolor) *piscolor = 0; + if (!pncolors) + return ERROR_INT("&ncolors not defined", procName, 1); + *pncolors = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) != NULL) { + *pncolors = pixcmapGetCount(cmap); + if (piscolor) + pixcmapHasColor(cmap, piscolor); + return 0; + } + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32) + return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); + if (thresh <= 0) + thresh = 15; + + /* First test if 32 bpp has any significant color; if not, + * convert it to gray. Colors whose average values are within + * 20 of black or 8 of white are ignored because they're not + * very 'colorful'. If less than 2.5/10000 of the pixels have + * significant color, consider the image to be gray. */ + minside = L_MIN(w, h); + if (d == 8) { + pixt = pixClone(pixs); + } else { /* d == 32 */ + factor = L_MAX(1, minside / 400); + pixColorFraction(pixs, 20, 248, 30, factor, &pixfract, &colorfract); + if (pixfract * colorfract < 0.00025) { + pixt = pixGetRGBComponent(pixs, COLOR_RED); + d = 8; + } else { /* d == 32 */ + pixt = pixClone(pixs); + if (piscolor) + *piscolor = 1; + } + } + + /* If the smallest side is less than 1000, do not downscale. + * If it is in [1000 ... 2000), downscale by 2x. If it is >= 2000, + * downscale by 4x. Factors of 2 are chosen for speed. The + * actual resolution at which subsequent calculations take place + * is not strongly dependent on downscaling. */ + factor = L_MAX(1, minside / 500); + if (factor == 1) + pixsc = pixCopy(NULL, pixt); /* to be sure pixs is unchanged */ + else if (factor == 2 || factor == 3) + pixsc = pixScaleAreaMap2(pixt); + else + pixsc = pixScaleAreaMap(pixt, 0.25, 0.25); + + /* Basic edge mask generation procedure: + * ~ work on a grayscale image + * ~ get a 1 bpp edge mask by using an edge filter and + * thresholding to get fg pixels at the edges + * ~ for gray, dilate with a 3x3 brick Sel to get mask over + * all pixels within a distance of 1 pixel from the nearest + * edge pixel + * ~ for color, dilate with a 7x7 brick Sel to get mask over + * all pixels within a distance of 3 pixels from the nearest + * edge pixel */ + if (d == 8) + pixg = pixClone(pixsc); + else /* d == 32 */ + pixg = pixConvertRGBToLuminance(pixsc); + pixe = pixSobelEdgeFilter(pixg, L_ALL_EDGES); + pixb = pixThresholdToBinary(pixe, thresh); + pixInvert(pixb, pixb); + if (d == 8) + pixm = pixMorphSequence(pixb, "d3.3", 0); + else + pixm = pixMorphSequence(pixb, "d7.7", 0); + + /* Mask the near-edge pixels to white, and count the colors. + * If grayscale, don't count colors within 20 levels of + * black or white, and only count colors with a fraction + * of at least 1/10000 of the image pixels. + * If color, count the number of level 4 octcubes that + * contain at least 20 pixels. These magic numbers are guesses + * as to what might work, based on a small data set. Results + * should not be overly sensitive to their actual values. */ + if (d == 8) { + pixSetMasked(pixg, pixm, 0xff); + if (debug) pixWrite("junkpix8.png", pixg, IFF_PNG); + pixNumSignificantGrayColors(pixg, 20, 236, 0.0001, 1, pncolors); + } else { /* d == 32 */ + pixSetMasked(pixsc, pixm, 0xffffffff); + if (debug) pixWrite("junkpix32.png", pixsc, IFF_PNG); + pixNumberOccupiedOctcubes(pixsc, 4, 20, -1, pncolors); + } + + pixDestroy(&pixt); + pixDestroy(&pixsc); + pixDestroy(&pixg); + pixDestroy(&pixe); + pixDestroy(&pixb); + pixDestroy(&pixm); + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Finds the number of unique colors in an image * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixNumColors() + * \param[in] pixs 2, 4, 8, 32 bpp + * \param[in] factor subsampling factor; integer + * \param[out] pncolors the number of colors found, or 0 if + * there are more than 256 + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This returns the actual number of colors found in the image,
+ *          even if there is a colormap.  If %factor == 1 and the
+ *          number of colors differs from the number of entries
+ *          in the colormap, a warning is issued.
+ *      (2) Use %factor == 1 to find the actual number of colors.
+ *          Use %factor > 1 to quickly find the approximate number of colors.
+ *      (3) For d = 2, 4 or 8 bpp grayscale, this returns the number
+ *          of colors found in the image in 'ncolors'.
+ *      (4) For d = 32 bpp (rgb), if the number of colors is
+ *          greater than 256, this returns 0 in 'ncolors'.
+ * 
+ */ +l_ok +pixNumColors(PIX *pixs, + l_int32 factor, + l_int32 *pncolors) +{ +l_int32 w, h, d, i, j, wpl, hashsize, sum, count; +l_int32 rval, gval, bval, val; +l_int32 *inta; +l_uint32 pixel; +l_uint32 *data, *line; +PIXCMAP *cmap; + + PROCNAME("pixNumColors"); + + if (!pncolors) + return ERROR_INT("&ncolors not defined", procName, 1); + *pncolors = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8 && d != 32) + return ERROR_INT("d not in {2, 4, 8, 32}", procName, 1); + if (factor < 1) factor = 1; + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + sum = 0; + if (d != 32) { /* grayscale */ + if ((inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) + return ERROR_INT("calloc failure for inta", procName, 1); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + if (d == 8) + val = GET_DATA_BYTE(line, j); + else if (d == 4) + val = GET_DATA_QBIT(line, j); + else /* d == 2 */ + val = GET_DATA_DIBIT(line, j); + inta[val] = 1; + } + } + for (i = 0; i < 256; i++) + if (inta[i]) sum++; + *pncolors = sum; + LEPT_FREE(inta); + + cmap = pixGetColormap(pixs); + if (cmap && factor == 1) { + count = pixcmapGetCount(cmap); + if (sum != count) + L_WARNING("colormap size %d differs from actual colors\n", + procName, count); + } + return 0; + } + + /* 32 bpp rgb; quit if we get above 256 colors */ + hashsize = 5507; /* big and prime; collisions are not likely */ + if ((inta = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32))) == NULL) + return ERROR_INT("calloc failure with hashsize", procName, 1); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + pixel = line[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + val = (137 * rval + 269 * gval + 353 * bval) % hashsize; + if (inta[val] == 0) { + inta[val] = 1; + sum++; + if (sum > 256) { + LEPT_FREE(inta); + return 0; + } + } + } + } + + *pncolors = sum; + LEPT_FREE(inta); + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Find the most "populated" colors in the image (and quantize) * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixGetMostPopulatedColors() + * \param[in] pixs 32 bpp rgb + * \param[in] sigbits 2-6, significant bits retained in the quantizer + * for each component of the input image + * \param[in] factor subsampling factor; use 1 for no subsampling + * \param[in] ncolors the number of most populated colors to select + * \param[out] parray [optional] array of colors, each as 0xrrggbb00 + * \param[out] pcmap [optional] colormap of the colors + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This finds the %ncolors most populated cubes in rgb colorspace,
+ *          where the cube size depends on %sigbits as
+ *               cube side = (256 >> sigbits)
+ *      (2) The rgb color components are found at the center of the cube.
+ *      (3) The output array of colors can be displayed using
+ *               pixDisplayColorArray(array, ncolors, ...);
+ * 
+ */ +l_ok +pixGetMostPopulatedColors(PIX *pixs, + l_int32 sigbits, + l_int32 factor, + l_int32 ncolors, + l_uint32 **parray, + PIXCMAP **pcmap) +{ +l_int32 n, i, rgbindex, rval, gval, bval; +NUMA *nahisto, *naindex; + + PROCNAME("pixGetMostPopulatedColors"); + + if (!parray && !pcmap) + return ERROR_INT("no return val requested", procName, 1); + if (parray) *parray = NULL; + if (pcmap) *pcmap = NULL; + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined", procName, 1); + if (sigbits < 2 || sigbits > 6) + return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); + if (factor < 1 || ncolors < 1) + return ERROR_INT("factor < 1 or ncolors < 1", procName, 1); + + if ((nahisto = pixGetRGBHistogram(pixs, sigbits, factor)) == NULL) + return ERROR_INT("nahisto not made", procName, 1); + + /* naindex contains the index into nahisto, which is the rgbindex */ + naindex = numaSortIndexAutoSelect(nahisto, L_SORT_DECREASING); + numaDestroy(&nahisto); + if (!naindex) + return ERROR_INT("naindex not made", procName, 1); + + n = numaGetCount(naindex); + ncolors = L_MIN(n, ncolors); + if (parray) *parray = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32)); + if (pcmap) *pcmap = pixcmapCreate(8); + for (i = 0; i < ncolors; i++) { + numaGetIValue(naindex, i, &rgbindex); /* rgb index */ + getRGBFromIndex(rgbindex, sigbits, &rval, &gval, &bval); + if (parray) composeRGBPixel(rval, gval, bval, *parray + i); + if (pcmap) pixcmapAddColor(*pcmap, rval, gval, bval); + } + + numaDestroy(&naindex); + return 0; +} + + +/*! + * \brief pixSimpleColorQuantize() + * \param[in] pixs 32 bpp rgb + * \param[in] sigbits 2-4, significant bits retained in the quantizer + * for each component of the input image + * \param[in] factor subsampling factor; use 1 for no subsampling + * \param[in] ncolors the number of most populated colors to select + * \return pixd 8 bpp cmapped or NULL on error + * + *
+ * Notes:
+ *      (1) If you want to do color quantization for real, use octcube
+ *          or modified median cut.  This function shows that it is
+ *          easy to make a simple quantizer based solely on the population
+ *          in cells of a given size in rgb color space.
+ *      (2) The %ncolors most populated cells at the %sigbits level form
+ *          the colormap for quantizing, and this uses octcube indexing
+ *          under the covers to assign each pixel to the nearest color.
+ *      (3) %sigbits is restricted to 2, 3 and 4.  At the low end, the
+ *          color discrimination is very crude; at the upper end, a set of
+ *          similar colors can dominate the result.  Interesting results
+ *          are generally found for %sigbits = 3 and ncolors ~ 20.
+ *      (4) See also pixColorSegment() for a method of quantizing the
+ *          colors to generate regions of similar color.
+ * 
+ */ +PIX * +pixSimpleColorQuantize(PIX *pixs, + l_int32 sigbits, + l_int32 factor, + l_int32 ncolors) +{ +l_int32 w, h; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixSimpleColorQuantize"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (sigbits < 2 || sigbits > 4) + return (PIX *)ERROR_PTR("sigbits not in {2,3,4}", procName, NULL); + + pixGetMostPopulatedColors(pixs, sigbits, factor, ncolors, NULL, &cmap); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, 8); + pixSetColormap(pixd, cmap); + pixAssignToNearestColor(pixd, pixs, NULL, 4, NULL); + return pixd; +} + + +/* ----------------------------------------------------------------------- * + * Constructs a color histogram based on rgb indices * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixGetRGBHistogram() + * \param[in] pixs 32 bpp rgb + * \param[in] sigbits 2-6, significant bits retained in the quantizer + * for each component of the input image + * \param[in] factor subsampling factor; use 1 for no subsampling + * \return numa histogram of colors, indexed by RGB + * components, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses a simple, fast method of indexing into an rgb image.
+ *      (2) The output is a 1D histogram of count vs. rgb-index, which
+ *          uses red sigbits as the most significant and blue as the least.
+ *      (3) This function produces the same result as pixMedianCutHisto().
+ * 
+ */ +NUMA * +pixGetRGBHistogram(PIX *pixs, + l_int32 sigbits, + l_int32 factor) +{ +l_int32 w, h, i, j, size, wpl, rval, gval, bval, npts; +l_uint32 val32, rgbindex; +l_float32 *array; +l_uint32 *data, *line, *rtab, *gtab, *btab; +NUMA *na; + + PROCNAME("pixGetRGBHistogram"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (sigbits < 2 || sigbits > 6) + return (NUMA *)ERROR_PTR("sigbits not in [2 ... 6]", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("factor < 1", procName, NULL); + + /* Get histogram size: 2^(3 * sigbits) */ + size = 1 << (3 * sigbits); /* 64, 512, 4096, 32768, 262144 */ + na = numaMakeConstant(0, size); /* init to all 0 */ + array = numaGetFArray(na, L_NOCOPY); + + makeRGBIndexTables(&rtab, >ab, &btab, sigbits); + + /* Check the number of sampled pixels */ + pixGetDimensions(pixs, &w, &h, NULL); + npts = ((w + factor - 1) / factor) * ((h + factor - 1) / factor); + if (npts < 1000) + L_WARNING("only sampling %d pixels\n", procName, npts); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + val32 = *(line + j); + extractRGBValues(val32, &rval, &gval, &bval); + rgbindex = rtab[rval] | gtab[gval] | btab[bval]; + array[rgbindex]++; + } + } + + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return na; +} + + +/*! + * \brief makeRGBIndexTables() + * + * \param[out] prtab, pgtab, pbtab 256-entry rgb index tables + * \param[in] sigbits 2-6, significant bits retained in the quantizer + * for each component of the input image + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) These tables are used to map from rgb sample values to
+ *          an rgb index, using
+ *             rgbindex = rtab[rval] | gtab[gval] | btab[bval]
+ *          where, e.g., if sigbits = 3, the index is a 9 bit integer:
+ *             r7 r6 r5 g7 g6 g5 b7 b6 b5
+ * 
+ */ +l_ok +makeRGBIndexTables(l_uint32 **prtab, + l_uint32 **pgtab, + l_uint32 **pbtab, + l_int32 sigbits) +{ +l_int32 i; +l_uint32 *rtab, *gtab, *btab; + + PROCNAME("makeRGBIndexTables"); + + if (prtab) *prtab = NULL; + if (pgtab) *pgtab = NULL; + if (pbtab) *pbtab = NULL; + if (!prtab || !pgtab || !pbtab) + return ERROR_INT("not all table ptrs defined", procName, 1); + if (sigbits < 2 || sigbits > 6) + return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); + + rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + if (!rtab || !gtab || !btab) + return ERROR_INT("calloc fail for tab", procName, 1); + *prtab = rtab; + *pgtab = gtab; + *pbtab = btab; + switch (sigbits) { + case 2: + for (i = 0; i < 256; i++) { + rtab[i] = (i & 0xc0) >> 2; + gtab[i] = (i & 0xc0) >> 4; + btab[i] = (i & 0xc0) >> 6; + } + break; + case 3: + for (i = 0; i < 256; i++) { + rtab[i] = (i & 0xe0) << 1; + gtab[i] = (i & 0xe0) >> 2; + btab[i] = (i & 0xe0) >> 5; + } + break; + case 4: + for (i = 0; i < 256; i++) { + rtab[i] = (i & 0xf0) << 4; + gtab[i] = (i & 0xf0); + btab[i] = (i & 0xf0) >> 4; + } + break; + case 5: + for (i = 0; i < 256; i++) { + rtab[i] = (i & 0xf8) << 7; + gtab[i] = (i & 0xf8) << 2; + btab[i] = (i & 0xf8) >> 3; + } + break; + case 6: + for (i = 0; i < 256; i++) { + rtab[i] = (i & 0xfc) << 10; + gtab[i] = (i & 0xfc) << 4; + btab[i] = (i & 0xfc) >> 2; + } + break; + default: + L_ERROR("Illegal sigbits = %d\n", procName, sigbits); + return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); + } + + return 0; +} + + +/*! + * \brief getRGBFromIndex() + * + * \param[in] index rgbindex + * \param[in] sigbits 2-6, significant bits retained in the quantizer + * for each component of the input image + * \param[out] prval, pgval, pbval rgb values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The %index is expressed in bits, based on the the
+ *          %sigbits of the r, g and b components, as
+ *             r7 r6 ... g7 g6 ... b7 b6 ...
+ *      (2) The computed rgb values are in the center of the quantized cube.
+ *          The extra bit that is OR'd accomplishes this.
+ * 
+ */ +l_ok +getRGBFromIndex(l_uint32 index, + l_int32 sigbits, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ + PROCNAME("getRGBFromIndex"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval || !pgval || !pbval) + return ERROR_INT("not all component ptrs defined", procName, 1); + if (sigbits < 2 || sigbits > 6) + return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); + + switch (sigbits) { + case 2: + *prval = ((index << 2) & 0xc0) | 0x20; + *pgval = ((index << 4) & 0xc0) | 0x20; + *pbval = ((index << 6) & 0xc0) | 0x20; + break; + case 3: + *prval = ((index >> 1) & 0xe0) | 0x10; + *pgval = ((index << 2) & 0xe0) | 0x10; + *pbval = ((index << 5) & 0xe0) | 0x10; + break; + case 4: + *prval = ((index >> 4) & 0xf0) | 0x08; + *pgval = (index & 0xf0) | 0x08; + *pbval = ((index << 4) & 0xf0) | 0x08; + break; + case 5: + *prval = ((index >> 7) & 0xf8) | 0x04; + *pgval = ((index >> 2) & 0xf8) | 0x04; + *pbval = ((index << 3) & 0xf8) | 0x04; + break; + case 6: + *prval = ((index >> 10) & 0xfc) | 0x02; + *pgval = ((index >> 4) & 0xfc) | 0x02; + *pbval = ((index << 2) & 0xfc) | 0x02; + break; + default: + L_ERROR("Illegal sigbits = %d\n", procName, sigbits); + return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); + } + + return 0; +} + + +/* ----------------------------------------------------------------------- * + * Identify images that have highlight (red) color * + * ----------------------------------------------------------------------- */ +/*! + * \brief pixHasHighlightRed() + * + * \param[in] pixs 32 bpp rgb + * \param[in] factor subsampling; an integer >= 1; use 1 for all pixels + * \param[in] fract threshold fraction of all image pixels + * \param[in] fthresh threshold on a function of the components; typ. ~2.5 + * \param[out] phasred 1 if red pixels are above threshold + * \param[out] pratio [optional] normalized fraction of threshold + * red pixels that is actually observed + * \param[out] ppixdb [optional] seed pixel mask + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Pixels are identified as red if they satisfy two conditions:
+ *          (a) The components satisfy (R-B)/B > %fthresh   (red or dark fg)
+ *          (b) The red component satisfied R > 128  (red or light bg)
+ *          Masks are generated for (a) and (b), and the intersection
+ *          gives the pixels that are red but not either light bg or
+ *          dark fg.
+ *      (2) A typical value for fract = 0.0001, which gives sensitivity
+ *          to an image where a small fraction of the pixels are printed
+ *          in red.
+ *      (3) A typical value for fthresh = 2.5.  Higher values give less
+ *          sensitivity to red, and fewer false positives.
+ * 
+ */ +l_ok +pixHasHighlightRed(PIX *pixs, + l_int32 factor, + l_float32 fract, + l_float32 fthresh, + l_int32 *phasred, + l_float32 *pratio, + PIX **ppixdb) +{ +l_int32 w, h, count; +l_float32 ratio; +PIX *pix1, *pix2, *pix3, *pix4; +FPIX *fpix; + + PROCNAME("pixHasHighlightRed"); + + if (pratio) *pratio = 0.0; + if (ppixdb) *ppixdb = NULL; + if (phasred) *phasred = 0; + if (!pratio && !ppixdb) + return ERROR_INT("no return val requested", procName, 1); + if (!phasred) + return ERROR_INT("&hasred not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (fthresh < 1.5 || fthresh > 3.5) + L_WARNING("fthresh = %f is out of normal bounds\n", procName, fthresh); + + if (factor > 1) + pix1 = pixScaleByIntSampling(pixs, factor); + else + pix1 = pixClone(pixs); + + /* Identify pixels that are either red or dark foreground */ + fpix = pixComponentFunction(pix1, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0); + pix2 = fpixThresholdToPix(fpix, fthresh); + pixInvert(pix2, pix2); + + /* Identify pixels that are either red or light background */ + pix3 = pixGetRGBComponent(pix1, COLOR_RED); + pix4 = pixThresholdToBinary(pix3, 130); + pixInvert(pix4, pix4); + + pixAnd(pix4, pix4, pix2); + pixCountPixels(pix4, &count, NULL); + pixGetDimensions(pix4, &w, &h, NULL); + L_INFO("count = %d, thresh = %d\n", procName, count, + (l_int32)(fract * w * h)); + ratio = (l_float32)count / (fract * w * h); + if (pratio) *pratio = ratio; + if (ratio >= 1.0) + *phasred = 1; + if (ppixdb) + *ppixdb = pix4; + else + pixDestroy(&pix4); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + fpixDestroy(&fpix); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/coloring.c b/hgdriver/3rdparty/hgOCR/leptonica/coloring.c new file mode 100644 index 0000000..cdac702 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/coloring.c @@ -0,0 +1,1046 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file coloring.c + *
+ *
+ *      Coloring "gray" pixels
+ *           PIX             *pixColorGrayRegions()
+ *           l_int32          pixColorGray()
+ *           PIX             *pixColorGrayMasked()
+ *
+ *      Adjusting one or more colors to a target color
+ *           PIX             *pixSnapColor()
+ *           PIX             *pixSnapColorCmap()
+ *
+ *      Piecewise linear color mapping based on a source/target pair
+ *           PIX             *pixLinearMapToTargetColor()
+ *           l_int32          pixelLinearMapToTargetColor()
+ *
+ *      Fractional shift of RGB towards black or white
+ *           PIX             *pixShiftByComponent()
+ *           l_int32          pixelShiftByComponent()
+ *           l_int32          pixelFractionalShift()
+ *
+ *  There are several "coloring" functions in leptonica.
+ *  You can find them in these files:
+ *       coloring.c
+ *       paintcmap.c
+ *       pix2.c
+ *       blend.c
+ *       enhance.c
+ *
+ *  They fall into the following categories:
+ *
+ *  (1) Moving either the light or dark pixels toward a
+ *      specified color. (pixColorGray, pixColorGrayMasked)
+ *  (2) Forcing all pixels whose color is within some delta of a
+ *      specified color to move to that color. (pixSnapColor)
+ *  (3) Doing a piecewise linear color shift specified by a source
+ *      and a target color.  Each component shifts independently.
+ *      (pixLinearMapToTargetColor)
+ *  (4) Shifting all colors by a given fraction of their distance
+ *      from 0 (if shifting down) or from 255 (if shifting up).
+ *      This is useful for colorizing either the background or
+ *      the foreground of a grayscale image. (pixShiftByComponent)
+ *  (5) Shifting all colors by a component-dependent fraction of
+ *      their distance from 0 (if shifting down) or from 255 (if
+ *      shifting up).  This is useful for modifying the color to
+ *      compensate for color shifts in acquisition or printing.
+ *      (enhance.c: pixColorShiftRGB, pixMosaicColorShiftRGB).
+ *  (6) Repainting selected pixels. (paintcmap.c: pixSetSelectMaskedCmap)
+ *  (7) Blending a fraction of a specific color with the existing RGB
+ *      color.  (pix2.c: pixBlendInRect())
+ *  (8) Changing selected colors in a colormap.
+ *      (paintcmap.c: pixSetSelectCmap, pixSetSelectMaskedCmap)
+ *  (9) Shifting all the pixels towards black or white depending on
+ *      the gray value of a second image.  (blend.c: pixFadeWithGray)
+ *  (10) Changing the hue, saturation or brightness, by changing the
+ *      appropriate parameter in HSV color space by a fraction of
+ *      the distance toward its end-point.  For example, you can change
+ *      the brightness by moving each pixel's v-parameter a specified
+ *      fraction of the distance toward 0 (darkening) or toward 255
+ *      (brightening).  (enhance.c: pixModifySaturation,
+ *      pixModifyHue, pixModifyBrightness)
+ * 
+ */ + +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Coloring "gray" pixels * + *---------------------------------------------------------------------*/ +/*! + * \brief pixColorGrayRegions() + * + * \param[in] pixs 2, 4 or 8 bpp gray, rgb, or colormapped + * \param[in] boxa of regions in which to apply color + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] thresh average value below/above which pixel is unchanged + * \param[in] rval, gval, bval new color to paint + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a new image, where some of the pixels in each
+ *          box in the boxa are colorized.  See pixColorGray() for usage
+ *          with %type and %thresh.  Note that %thresh is only used for
+ *          rgb; it is ignored for colormapped images.
+ *      (2) If the input image is colormapped, the new image will be 8 bpp
+ *          colormapped if possible; otherwise, it will be converted
+ *          to 32 bpp rgb.  Only pixels that are strictly gray will be
+ *          colorized.
+ *      (3) If the input image is not colormapped, it is converted to rgb.
+ *          A "gray" value for a pixel is determined by averaging the
+ *          components, and the output rgb value is determined from this.
+ *      (4) This can be used in conjunction with pixHasHighlightRed() to
+ *          add highlight color to a grayscale image.
+ * 
+ */ +PIX * +pixColorGrayRegions(PIX *pixs, + BOXA *boxa, + l_int32 type, + l_int32 thresh, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, n, ncolors, ngray; +BOX *box; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixColorGrayRegions"); + + if (!pixs || pixGetDepth(pixs) == 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + /* If cmapped and there is room in an 8 bpp colormap for + * expansion, convert pixs to 8 bpp, and colorize. */ + cmap = pixGetColormap(pixs); + if (cmap) { + ncolors = pixcmapGetCount(cmap); + pixcmapCountGrayColors(cmap, &ngray); + if (ncolors + ngray < 255) { + pixd = pixConvertTo8(pixs, 1); /* always new image */ + pixColorGrayRegionsCmap(pixd, boxa, type, rval, gval, bval); + return pixd; + } + } + + /* The output will be rgb. Make sure the thresholds are valid */ + if (type == L_PAINT_LIGHT) { /* thresh should be low */ + if (thresh >= 255) + return (PIX *)ERROR_PTR("thresh must be < 255", procName, NULL); + if (thresh > 127) + L_WARNING("threshold set very high\n", procName); + } else { /* type == L_PAINT_DARK; thresh should be high */ + if (thresh <= 0) + return (PIX *)ERROR_PTR("thresh must be > 0", procName, NULL); + if (thresh < 128) + L_WARNING("threshold set very low\n", procName); + } + + pixd = pixConvertTo32(pixs); /* always new image */ + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pixColorGray(pixd, box, type, thresh, rval, gval, bval); + boxDestroy(&box); + } + + return pixd; +} + + +/*! + * \brief pixColorGray() + * + * \param[in] pixs 8 bpp gray, rgb or colormapped image + * \param[in] box [optional] region in which to apply color; can be NULL + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] thresh average value below/above which pixel is unchanged + * \param[in] rval, gval, bval new color to paint + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation; pixs is modified.
+ *          If pixs is colormapped, the operation will add colors to the
+ *          colormap.  Otherwise, pixs will be converted to 32 bpp rgb if
+ *          it is initially 8 bpp gray.
+ *      (2) If type == L_PAINT_LIGHT, it colorizes non-black pixels,
+ *          preserving antialiasing.
+ *          If type == L_PAINT_DARK, it colorizes non-white pixels,
+ *          preserving antialiasing.
+ *      (3) If box is NULL, applies function to the entire image; otherwise,
+ *          clips the operation to the intersection of the box and pix.
+ *      (4) If colormapped, calls pixColorGrayCmap(), which applies the
+ *          coloring algorithm only to pixels that are strictly gray.
+ *      (5) For RGB, determines a "gray" value by averaging; then uses this
+ *          value, plus the input rgb target, to generate the output
+ *          pixel values.
+ *      (6) thresh is only used for rgb; it is ignored for colormapped pix.
+ *          If type == L_PAINT_LIGHT, use thresh = 0 if all pixels are to
+ *          be colored (black pixels will be unaltered).
+ *          In situations where there are a lot of black pixels,
+ *          setting thresh > 0 will make the function considerably
+ *          more efficient without affecting the final result.
+ *          If type == L_PAINT_DARK, use thresh = 255 if all pixels
+ *          are to be colored (white pixels will be unaltered).
+ *          In situations where there are a lot of white pixels,
+ *          setting thresh < 255 will make the function considerably
+ *          more efficient without affecting the final result.
+ * 
+ */ +l_ok +pixColorGray(PIX *pixs, + BOX *box, + l_int32 type, + l_int32 thresh, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, w, h, d, wpl, x1, x2, y1, y2, bw, bh; +l_int32 nrval, ngval, nbval, aveval; +l_float32 factor; +l_uint32 val32; +l_uint32 *line, *data; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixColorGray"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) + return ERROR_INT("invalid type", procName, 1); + + cmap = pixGetColormap(pixs); + pixGetDimensions(pixs, &w, &h, &d); + if (!cmap && d != 8 && d != 32) + return ERROR_INT("pixs not cmapped, 8 bpp or rgb", procName, 1); + if (cmap) + return pixColorGrayCmap(pixs, box, type, rval, gval, bval); + + /* rgb or 8 bpp gray image; check the thresh */ + if (type == L_PAINT_LIGHT) { /* thresh should be low */ + if (thresh >= 255) + return ERROR_INT("thresh must be < 255; else this is a no-op", + procName, 1); + if (thresh > 127) + L_WARNING("threshold set very high\n", procName); + } else { /* type == L_PAINT_DARK; thresh should be high */ + if (thresh <= 0) + return ERROR_INT("thresh must be > 0; else this is a no-op", + procName, 1); + if (thresh < 128) + L_WARNING("threshold set very low\n", procName); + } + + /* In-place conversion to 32 bpp if necessary */ + if (d == 8) { + pixt = pixConvertTo32(pixs); + pixTransferAllData(pixs, &pixt, 1, 0); + } + + if (!box) { + x1 = y1 = 0; + x2 = w; + y2 = h; + } else { + boxGetGeometry(box, &x1, &y1, &bw, &bh); + x2 = x1 + bw - 1; + y2 = y1 + bh - 1; + } + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + factor = 1. / 255.; + for (i = y1; i <= y2; i++) { + if (i < 0 || i >= h) + continue; + line = data + i * wpl; + for (j = x1; j <= x2; j++) { + if (j < 0 || j >= w) + continue; + val32 = *(line + j); + aveval = ((val32 >> 24) + ((val32 >> 16) & 0xff) + + ((val32 >> 8) & 0xff)) / 3; + if (type == L_PAINT_LIGHT) { + if (aveval < thresh) /* skip sufficiently dark pixels */ + continue; + nrval = (l_int32)(rval * aveval * factor); + ngval = (l_int32)(gval * aveval * factor); + nbval = (l_int32)(bval * aveval * factor); + } else { /* type == L_PAINT_DARK */ + if (aveval > thresh) /* skip sufficiently light pixels */ + continue; + nrval = rval + (l_int32)((255. - rval) * aveval * factor); + ngval = gval + (l_int32)((255. - gval) * aveval * factor); + nbval = bval + (l_int32)((255. - bval) * aveval * factor); + } + composeRGBPixel(nrval, ngval, nbval, &val32); + *(line + j) = val32; + } + } + + return 0; +} + + +/*! + * \brief pixColorGrayMasked() + * + * \param[in] pixs 8 bpp gray, rgb or colormapped image + * \param[in] pixm 1 bpp mask, through which to apply color + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] thresh average value below/above which pixel is unchanged + * \param[in] rval, gval, bval new color to paint + * \return pixd colorized, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a new image, where some of the pixels under
+ *          FG in the mask are colorized.
+ *      (2) See pixColorGray() for usage with %type and %thresh.  Note
+ *          that %thresh is only used for rgb; it is ignored for
+ *          colormapped images.  In most cases, the mask will be over
+ *          the darker parts and %type == L_PAINT_DARK.
+ *      (3) If pixs is colormapped this calls pixColorMaskedCmap(),
+ *          which adds colors to the colormap for pixd; it only adds
+ *          colors corresponding to strictly gray colors in the colormap.
+ *          Otherwise, if pixs is 8 bpp gray, pixd will be 32 bpp rgb.
+ *      (4) If pixs is 32 bpp rgb, for each pixel a "gray" value is
+ *          found by averaging.  This average is then used with the
+ *          input rgb target to generate the output pixel values.
+ *      (5) This can be used in conjunction with pixHasHighlightRed() to
+ *          add highlight color to a grayscale image.
+ * 
+ */ +PIX * +pixColorGrayMasked(PIX *pixs, + PIX *pixm, + l_int32 type, + l_int32 thresh, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, w, h, d, wm, hm, wmin, hmin, wpl, wplm; +l_int32 nrval, ngval, nbval, aveval; +l_float32 factor; +l_uint32 val32; +l_uint32 *line, *data, *linem, *datam; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixColorGrayMasked"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); + if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + cmap = pixGetColormap(pixs); + pixGetDimensions(pixs, &w, &h, &d); + if (!cmap && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped, 8 bpp gray or 32 bpp", + procName, NULL); + if (cmap) { + pixd = pixCopy(NULL, pixs); + pixColorGrayMaskedCmap(pixd, pixm, type, rval, gval, bval); + return pixd; + } + + /* rgb or 8 bpp gray image; check the thresh */ + if (type == L_PAINT_LIGHT) { /* thresh should be low */ + if (thresh >= 255) + return (PIX *)ERROR_PTR( + "thresh must be < 255; else this is a no-op", procName, NULL); + if (thresh > 127) + L_WARNING("threshold set very high\n", procName); + } else { /* type == L_PAINT_DARK; thresh should be high */ + if (thresh <= 0) + return (PIX *)ERROR_PTR( + "thresh must be > 0; else this is a no-op", procName, NULL); + if (thresh < 128) + L_WARNING("threshold set very low\n", procName); + } + + pixGetDimensions(pixm, &wm, &hm, NULL); + if (wm != w) + L_WARNING("wm = %d differs from w = %d\n", procName, wm, w); + if (hm != h) + L_WARNING("hm = %d differs from h = %d\n", procName, hm, h); + wmin = L_MIN(w, wm); + hmin = L_MIN(h, hm); + if (d == 8) + pixd = pixConvertTo32(pixs); + else + pixd = pixCopy(NULL, pixs); + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + factor = 1. / 255.; + for (i = 0; i < hmin; i++) { + line = data + i * wpl; + linem = datam + i * wplm; + for (j = 0; j < wmin; j++) { + if (GET_DATA_BIT(linem, j) == 0) + continue; + val32 = *(line + j); + aveval = ((val32 >> 24) + ((val32 >> 16) & 0xff) + + ((val32 >> 8) & 0xff)) / 3; + if (type == L_PAINT_LIGHT) { + if (aveval < thresh) /* skip sufficiently dark pixels */ + continue; + nrval = (l_int32)(rval * aveval * factor); + ngval = (l_int32)(gval * aveval * factor); + nbval = (l_int32)(bval * aveval * factor); + } else { /* type == L_PAINT_DARK */ + if (aveval > thresh) /* skip sufficiently light pixels */ + continue; + nrval = rval + (l_int32)((255. - rval) * aveval * factor); + ngval = gval + (l_int32)((255. - gval) * aveval * factor); + nbval = bval + (l_int32)((255. - bval) * aveval * factor); + } + composeRGBPixel(nrval, ngval, nbval, &val32); + *(line + j) = val32; + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Adjusting one or more colors to a target color * + *------------------------------------------------------------------*/ +/*! + * \brief pixSnapColor() + * + * \param[in] pixd [optional]; either NULL or equal to pixs for in-place + * \param[in] pixs colormapped or 8 bpp gray or 32 bpp rgb + * \param[in] srcval color center to be selected for change: 0xrrggbb00 + * \param[in] dstval target color for pixels: 0xrrggbb00 + * \param[in] diff max absolute difference, applied to all components + * \return pixd with all pixels within diff of pixval set to pixval, + * or pixd on error + * + *
+ * Notes:
+ *      (1) For inplace operation, call it this way:
+ *           pixSnapColor(pixs, pixs, ... )
+ *      (2) For generating a new pixd:
+ *           pixd = pixSnapColor(NULL, pixs, ...)
+ *      (3) If pixs has a colormap, it is handled by pixSnapColorCmap().
+ *      (4) All pixels within 'diff' of 'srcval', componentwise,
+ *          will be changed to 'dstval'.
+ * 
+ */ +PIX * +pixSnapColor(PIX *pixd, + PIX *pixs, + l_uint32 srcval, + l_uint32 dstval, + l_int32 diff) +{ +l_int32 val, sval, dval; +l_int32 rval, gval, bval, rsval, gsval, bsval; +l_int32 i, j, w, h, d, wpl; +l_uint32 pixel; +l_uint32 *line, *data; + + PROCNAME("pixSnapColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + + if (pixGetColormap(pixs)) + return pixSnapColorCmap(pixd, pixs, srcval, dstval, diff); + + /* pixs does not have a colormap; it must be 8 bpp gray or + * 32 bpp rgb. */ + if (pixGetDepth(pixs) < 8) + return (PIX *)ERROR_PTR("pixs is < 8 bpp", procName, pixd); + + /* Do the work on pixd */ + if (!pixd) + pixd = pixCopy(NULL, pixs); + + pixGetDimensions(pixd, &w, &h, &d); + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + if (d == 8) { + sval = srcval & 0xff; + dval = dstval & 0xff; + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(line, j); + if (L_ABS(val - sval) <= diff) + SET_DATA_BYTE(line, j, dval); + } + } + } else { /* d == 32 */ + extractRGBValues(srcval, &rsval, &gsval, &bsval); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + pixel = *(line + j); + extractRGBValues(pixel, &rval, &gval, &bval); + if ((L_ABS(rval - rsval) <= diff) && + (L_ABS(gval - gsval) <= diff) && + (L_ABS(bval - bsval) <= diff)) + *(line + j) = dstval; /* replace */ + } + } + } + + return pixd; +} + + +/*! + * \brief pixSnapColorCmap() + * + * \param[in] pixd [optional]; either NULL or equal to pixs for in-place + * \param[in] pixs colormapped + * \param[in] srcval color center to be selected for change: 0xrrggbb00 + * \param[in] dstval target color for pixels: 0xrrggbb00 + * \param[in] diff max absolute difference, applied to all components + * \return pixd with all pixels within diff of srcval set to dstval, + * or pixd on error + * + *
+ * Notes:
+ *      (1) For inplace operation, call it this way:
+ *           pixSnapCcmap(pixs, pixs, ... )
+ *      (2) For generating a new pixd:
+ *           pixd = pixSnapCmap(NULL, pixs, ...)
+ *      (3) pixs must have a colormap.
+ *      (4) All colors within 'diff' of 'srcval', componentwise,
+ *          will be changed to 'dstval'.
+ * 
+ */ +PIX * +pixSnapColorCmap(PIX *pixd, + PIX *pixs, + l_uint32 srcval, + l_uint32 dstval, + l_int32 diff) +{ +l_int32 i, ncolors, index, found; +l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; +l_int32 *tab; +PIX *pixm; +PIXCMAP *cmap; + + PROCNAME("pixSnapColorCmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("cmap not found", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + + /* If no free colors, look for one close to the target + * that can be commandeered. */ + cmap = pixGetColormap(pixd); + ncolors = pixcmapGetCount(cmap); + extractRGBValues(srcval, &rsval, &gsval, &bsval); + extractRGBValues(dstval, &rdval, &gdval, &bdval); + found = FALSE; + if (pixcmapGetFreeCount(cmap) == 0) { + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if ((L_ABS(rval - rsval) <= diff) && + (L_ABS(gval - gsval) <= diff) && + (L_ABS(bval - bsval) <= diff)) { + index = i; + pixcmapResetColor(cmap, index, rdval, gdval, bdval); + found = TRUE; + break; + } + } + } else { /* just add the new color */ + pixcmapAddColor(cmap, rdval, gdval, bdval); + ncolors = pixcmapGetCount(cmap); + index = ncolors - 1; /* index of new destination color */ + found = TRUE; + } + + if (!found) { + L_INFO("nothing to do\n", procName); + return pixd; + } + + /* For each color in cmap that is close enough to srcval, + * set the tab value to 1. Then generate a 1 bpp mask with + * fg pixels for every pixel in pixd that is close enough + * to srcval (i.e., has value 1 in tab). */ + if ((tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) + return (PIX *)ERROR_PTR("tab not made", procName, pixd); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if ((L_ABS(rval - rsval) <= diff) && + (L_ABS(gval - gsval) <= diff) && + (L_ABS(bval - bsval) <= diff)) + tab[i] = 1; + } + pixm = pixMakeMaskFromLUT(pixd, tab); + LEPT_FREE(tab); + + /* Use the binary mask to set all selected pixels to + * the dest color index. */ + pixSetMasked(pixd, pixm, dstval); + pixDestroy(&pixm); + + /* Remove all unused colors from the colormap. */ + pixRemoveUnusedColors(pixd); + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Piecewise linear color mapping based on a source/target pair * + *---------------------------------------------------------------------*/ +/*! + * \brief pixLinearMapToTargetColor() + * + * \param[in] pixd [optional]; either NULL or equal to pixs for in-place + * \param[in] pixs 32 bpp rgb + * \param[in] srcval source color: 0xrrggbb00 + * \param[in] dstval target color: 0xrrggbb00 + * \return pixd with all pixels mapped based on the srcval/destval mapping, + * or pixd on error + * + *
+ * Notes:
+ *      (1) For each component (r, b, g) separately, this does a piecewise
+ *          linear mapping of the colors in pixs to colors in pixd.
+ *          If rs and rd are the red src and dest components in %srcval and
+ *          %dstval, then the range [0 ... rs] in pixs is mapped to
+ *          [0 ... rd] in pixd.  Likewise, the range [rs ... 255] in pixs
+ *          is mapped to [rd ... 255] in pixd.  And similarly for green
+ *          and blue.
+ *      (2) The mapping will in general change the hue of the pixels.
+ *          However, if the src and dst targets are related by
+ *          a transformation given by pixelFractionalShift(), the hue
+ *          is invariant.
+ *      (3) For inplace operation, call it this way:
+ *            pixLinearMapToTargetColor(pixs, pixs, ... )
+ *      (4) For generating a new pixd:
+ *            pixd = pixLinearMapToTargetColor(NULL, pixs, ...)
+ * 
+ */ +PIX * +pixLinearMapToTargetColor(PIX *pixd, + PIX *pixs, + l_uint32 srcval, + l_uint32 dstval) +{ +l_int32 i, j, w, h, wpl; +l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; +l_int32 *rtab, *gtab, *btab; +l_uint32 pixel; +l_uint32 *line, *data; + + PROCNAME("pixLinearMapToTargetColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, pixd); + + /* Do the work on pixd */ + if (!pixd) + pixd = pixCopy(NULL, pixs); + + extractRGBValues(srcval, &rsval, &gsval, &bsval); + extractRGBValues(dstval, &rdval, &gdval, &bdval); + rsval = L_MIN(254, L_MAX(1, rsval)); + gsval = L_MIN(254, L_MAX(1, gsval)); + bsval = L_MIN(254, L_MAX(1, bsval)); + rtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + gtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + btab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (!rtab || !gtab || !btab) + return (PIX *)ERROR_PTR("calloc fail for tab", procName, pixd); + for (i = 0; i < 256; i++) { + if (i <= rsval) + rtab[i] = (i * rdval) / rsval; + else + rtab[i] = rdval + ((255 - rdval) * (i - rsval)) / (255 - rsval); + if (i <= gsval) + gtab[i] = (i * gdval) / gsval; + else + gtab[i] = gdval + ((255 - gdval) * (i - gsval)) / (255 - gsval); + if (i <= bsval) + btab[i] = (i * bdval) / bsval; + else + btab[i] = bdval + ((255 - bdval) * (i - bsval)) / (255 - bsval); + } + pixGetDimensions(pixd, &w, &h, NULL); + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + pixel = line[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + composeRGBPixel(rtab[rval], gtab[gval], btab[bval], &pixel); + line[j] = pixel; + } + } + + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*! + * \brief pixelLinearMapToTargetColor() + * + * \param[in] scolor rgb source color: 0xrrggbb00 + * \param[in] srcmap source mapping color: 0xrrggbb00 + * \param[in] dstmap target mapping color: 0xrrggbb00 + * \param[out] pdcolor rgb dest color: 0xrrggbb00 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does this does a piecewise linear mapping of each
+ *          component of %scolor to %dcolor, based on the relation
+ *          between the components of %srcmap and %dstmap.  It is the
+ *          same transformation, performed on a single color, as mapped
+ *          on every pixel in a pix by pixLinearMapToTargetColor().
+ *      (2) For each component, if the sval is larger than the smap,
+ *          the dval will be pushed up from dmap towards white.
+ *          Otherwise, dval will be pushed down from dmap towards black.
+ *          This is because you can visualize the transformation as
+ *          a linear stretching where smap moves to dmap, and everything
+ *          else follows linearly with 0 and 255 fixed.
+ *      (3) The mapping will in general change the hue of %scolor.
+ *          However, if the %srcmap and %dstmap targets are related by
+ *          a transformation given by pixelFractionalShift(), the hue
+ *          will be invariant.
+ * 
+ */ +l_ok +pixelLinearMapToTargetColor(l_uint32 scolor, + l_uint32 srcmap, + l_uint32 dstmap, + l_uint32 *pdcolor) +{ +l_int32 srval, sgval, sbval, drval, dgval, dbval; +l_int32 srmap, sgmap, sbmap, drmap, dgmap, dbmap; + + PROCNAME("pixelLinearMapToTargetColor"); + + if (!pdcolor) + return ERROR_INT("&dcolor not defined", procName, 1); + *pdcolor = 0; + + extractRGBValues(scolor, &srval, &sgval, &sbval); + extractRGBValues(srcmap, &srmap, &sgmap, &sbmap); + extractRGBValues(dstmap, &drmap, &dgmap, &dbmap); + srmap = L_MIN(254, L_MAX(1, srmap)); + sgmap = L_MIN(254, L_MAX(1, sgmap)); + sbmap = L_MIN(254, L_MAX(1, sbmap)); + + if (srval <= srmap) + drval = (srval * drmap) / srmap; + else + drval = drmap + ((255 - drmap) * (srval - srmap)) / (255 - srmap); + if (sgval <= sgmap) + dgval = (sgval * dgmap) / sgmap; + else + dgval = dgmap + ((255 - dgmap) * (sgval - sgmap)) / (255 - sgmap); + if (sbval <= sbmap) + dbval = (sbval * dbmap) / sbmap; + else + dbval = dbmap + ((255 - dbmap) * (sbval - sbmap)) / (255 - sbmap); + + composeRGBPixel(drval, dgval, dbval, pdcolor); + return 0; +} + + +/*------------------------------------------------------------------* + * Fractional shift of RGB towards black or white * + *------------------------------------------------------------------*/ +/*! + * \brief pixShiftByComponent() + * + * \param[in] pixd [optional]; either NULL or equal to pixs for in-place + * \param[in] pixs 32 bpp rgb + * \param[in] srcval source color: 0xrrggbb00 + * \param[in] dstval target color: 0xrrggbb00 + * \return pixd with all pixels mapped based on the srcval/destval mapping, + * or pixd on error + * + *
+ * Notes:
+ *      (1) For each component (r, b, g) separately, this does a linear
+ *          mapping of the colors in pixs to colors in pixd.
+ *          Let rs and rd be the red src and dest components in %srcval and
+ *          %dstval, and rval is the red component of the src pixel.
+ *          Then for all pixels in pixs, the mapping for the red
+ *          component from pixs to pixd is:
+ *             if (rd <= rs)   (shift toward black)
+ *                 rval --> (rd/rs) * rval
+ *             if (rd > rs)    (shift toward white)
+ *                (255 - rval) --> ((255 - rs)/(255 - rd)) * (255 - rval)
+ *          Thus if rd <= rs, the red component of all pixels is
+ *          mapped by the same fraction toward white, and if rd > rs,
+ *          they are mapped by the same fraction toward black.
+ *          This is essentially a different linear TRC (gamma = 1)
+ *          for each component.  The source and target color inputs are
+ *          just used to generate the three fractions.
+ *      (2) Note that this mapping differs from that in
+ *          pixLinearMapToTargetColor(), which maps rs --> rd and does
+ *          a piecewise stretching in between.
+ *      (3) For inplace operation, call it this way:
+ *            pixFractionalShiftByComponent(pixs, pixs, ... )
+ *      (4) For generating a new pixd:
+ *            pixd = pixLinearMapToTargetColor(NULL, pixs, ...)
+ *      (5) A simple application is to color a grayscale image.
+ *          A light background can be colored using srcval = 0xffffff00
+ *          and picking a target background color for dstval.
+ *          A dark foreground can be colored by using srcval = 0x0
+ *          and choosing a target foreground color for dstval.
+ * 
+ */ +PIX * +pixShiftByComponent(PIX *pixd, + PIX *pixs, + l_uint32 srcval, + l_uint32 dstval) +{ +l_int32 i, j, w, h, wpl; +l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; +l_int32 *rtab, *gtab, *btab; +l_uint32 pixel; +l_uint32 *line, *data; +PIXCMAP *cmap; + + PROCNAME("pixShiftByComponent"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); + if (pixGetDepth(pixs) != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, pixd); + + /* Do the work on pixd */ + if (!pixd) + pixd = pixCopy(NULL, pixs); + + /* If colormapped, just modify it */ + if ((cmap = pixGetColormap(pixd)) != NULL) { + pixcmapShiftByComponent(cmap, srcval, dstval); + return pixd; + } + + extractRGBValues(srcval, &rsval, &gsval, &bsval); + extractRGBValues(dstval, &rdval, &gdval, &bdval); + rtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + gtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + btab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (!rtab || !gtab || !btab) { + L_ERROR("calloc fail for tab\n", procName); + goto cleanup; + } + for (i = 0; i < 256; i++) { + if (rdval == rsval) + rtab[i] = i; + else if (rdval < rsval) + rtab[i] = (i * rdval) / rsval; + else + rtab[i] = 255 - (255 - rdval) * (255 - i) / (255 - rsval); + if (gdval == gsval) + gtab[i] = i; + else if (gdval < gsval) + gtab[i] = (i * gdval) / gsval; + else + gtab[i] = 255 - (255 - gdval) * (255 - i) / (255 - gsval); + if (bdval == bsval) + btab[i] = i; + else if (bdval < bsval) + btab[i] = (i * bdval) / bsval; + else + btab[i] = 255 - (255 - bdval) * (255 - i) / (255 - bsval); + } + pixGetDimensions(pixd, &w, &h, NULL); + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + pixel = line[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + composeRGBPixel(rtab[rval], gtab[gval], btab[bval], &pixel); + line[j] = pixel; + } + } + +cleanup: + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*! + * \brief pixelShiftByComponent() + * + * \param[in] rval, gval, bval + * \param[in] srcval source color: 0xrrggbb00 + * \param[in] dstval target color: 0xrrggbb00 + * \param[out] ppixel rgb value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a linear transformation that gives the same result
+ *          on a single pixel as pixShiftByComponent() gives
+ *          on a pix.  Each component is handled separately.  If
+ *          the dest component is larger than the src, then the
+ *          component is pushed toward 255 by the same fraction as
+ *          the src --> dest shift.
+ * 
+ */ +l_ok +pixelShiftByComponent(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_uint32 srcval, + l_uint32 dstval, + l_uint32 *ppixel) +{ +l_int32 rsval, rdval, gsval, gdval, bsval, bdval, rs, gs, bs; + + PROCNAME("pixelShiftByComponent"); + + if (!ppixel) + return ERROR_INT("&pixel defined", procName, 1); + + extractRGBValues(srcval, &rsval, &gsval, &bsval); + extractRGBValues(dstval, &rdval, &gdval, &bdval); + if (rdval == rsval) + rs = rval; + else if (rdval < rsval) + rs = (rval * rdval) / rsval; + else + rs = 255 - (255 - rdval) * (255 - rval) / (255 - rsval); + if (gdval == gsval) + gs = gval; + else if (gdval < gsval) + gs = (gval * gdval) / gsval; + else + gs = 255 - (255 - gdval) * (255 - gval) / (255 - gsval); + if (bdval == bsval) + bs = bval; + else if (bdval < bsval) + bs = (bval * bdval) / bsval; + else + bs = 255 - (255 - bdval) * (255 - bval) / (255 - bsval); + composeRGBPixel(rs, gs, bs, ppixel); + return 0; +} + + +/*! + * \brief pixelFractionalShift() + * + * \param[in] rval, gval, bval + * \param[in] fraction negative toward black; positive toward white + * \param[out] ppixel rgb value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This transformation leaves the hue invariant, while changing
+ *          the saturation and intensity.  It can be used for that
+ *          purpose in pixLinearMapToTargetColor().
+ *      (2) %fraction is in the range [-1 .... +1].  If %fraction < 0,
+ *          saturation is increased and brightness is reduced.  The
+ *          opposite results if %fraction > 0.  If %fraction == -1,
+ *          the resulting pixel is black; %fraction == 1 results in white.
+ * 
+ */ +l_ok +pixelFractionalShift(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_float32 fraction, + l_uint32 *ppixel) +{ +l_int32 nrval, ngval, nbval; + + PROCNAME("pixelFractionalShift"); + + if (!ppixel) + return ERROR_INT("&pixel defined", procName, 1); + if (fraction < -1.0 || fraction > 1.0) + return ERROR_INT("fraction not in [-1 ... +1]", procName, 1); + + nrval = (fraction < 0) ? (l_int32)((1.0 + fraction) * rval + 0.5) : + rval + (l_int32)(fraction * (255 - rval) + 0.5); + ngval = (fraction < 0) ? (l_int32)((1.0 + fraction) * gval + 0.5) : + gval + (l_int32)(fraction * (255 - gval) + 0.5); + nbval = (fraction < 0) ? (l_int32)((1.0 + fraction) * bval + 0.5) : + bval + (l_int32)(fraction * (255 - bval) + 0.5); + composeRGBPixel(nrval, ngval, nbval, ppixel); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colormap.c b/hgdriver/3rdparty/hgOCR/leptonica/colormap.c new file mode 100644 index 0000000..853feaa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colormap.c @@ -0,0 +1,2303 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colormap.c + *
+ *
+ *      Colormap creation, copy, destruction, addition
+ *           PIXCMAP    *pixcmapCreate()
+ *           PIXCMAP    *pixcmapCreateRandom()
+ *           PIXCMAP    *pixcmapCreateLinear()
+ *           PIXCMAP    *pixcmapCopy()
+ *           void        pixcmapDestroy()
+ *           l_int32     pixcmapIsValid()
+ *           l_int32     pixcmapAddColor()
+ *           l_int32     pixcmapAddRGBA()
+ *           l_int32     pixcmapAddNewColor()
+ *           l_int32     pixcmapAddNearestColor()
+ *           l_int32     pixcmapUsableColor()
+ *           l_int32     pixcmapAddBlackOrWhite()
+ *           l_int32     pixcmapSetBlackAndWhite()
+ *           l_int32     pixcmapGetCount()
+ *           l_int32     pixcmapGetDepth()
+ *           l_int32     pixcmapGetMinDepth()
+ *           l_int32     pixcmapGetFreeCount()
+ *           l_int32     pixcmapClear()
+ *
+ *      Colormap random access and test
+ *           l_int32     pixcmapGetColor()
+ *           l_int32     pixcmapGetColor32()
+ *           l_int32     pixcmapGetRGBA()
+ *           l_int32     pixcmapGetRGBA32()
+ *           l_int32     pixcmapResetColor()
+ *           l_int32     pixcmapSetAlpha()
+ *           l_int32     pixcmapGetIndex()
+ *           l_int32     pixcmapHasColor()
+ *           l_int32     pixcmapIsOpaque()
+ *           l_int32     pixcmapIsBlackAndWhite()
+ *           l_int32     pixcmapCountGrayColors()
+ *           l_int32     pixcmapGetRankIntensity()
+ *           l_int32     pixcmapGetNearestIndex()
+ *           l_int32     pixcmapGetNearestGrayIndex()
+ *           l_int32     pixcmapGetDistanceToColor()
+ *           l_int32     pixcmapGetRangeValues()
+ *
+ *      Colormap conversion
+ *           PIXCMAP    *pixcmapGrayToColor()
+ *           PIXCMAP    *pixcmapColorToGray()
+ *           PIXCMAP    *pixcmapConvertTo4()
+ *           PIXCMAP    *pixcmapConvertTo8()
+ *
+ *      Colormap I/O
+ *           l_int32     pixcmapRead()
+ *           l_int32     pixcmapReadStream()
+ *           l_int32     pixcmapReadMem()
+ *           l_int32     pixcmapWrite()
+ *           l_int32     pixcmapWriteStream()
+ *           l_int32     pixcmapWriteMem()
+ *
+ *      Extract colormap arrays and serialization
+ *           l_int32     pixcmapToArrays()
+ *           l_int32     pixcmapToRGBTable()
+ *           l_int32     pixcmapSerializeToMemory()
+ *           PIXCMAP    *pixcmapDeserializeFromMemory()
+ *           char       *pixcmapConvertToHex()
+ *
+ *      Colormap transforms
+ *           l_int32     pixcmapGammaTRC()
+ *           l_int32     pixcmapContrastTRC()
+ *           l_int32     pixcmapShiftIntensity()
+ *           l_int32     pixcmapShiftByComponent()
+ *
+ *  Note:
+ *      (1) colormaps in leptonica have a maximum of 256 entries.
+ *      (2) nalloc, the allocated size of the palette array, is related
+ *          to the depth d of the pixels by:
+ *                 nalloc = 2^(d)
+ *       
+ * 
+ */ + +#include +#include "allheaders.h" + +/*-------------------------------------------------------------* + * Colormap creation and addition * + *-------------------------------------------------------------*/ +/*! + * \brief pixcmapCreate() + * + * \param[in] depth of pix, in bpp + * \return cmap, or NULL on error + */ +PIXCMAP * +pixcmapCreate(l_int32 depth) +{ +RGBA_QUAD *cta; +PIXCMAP *cmap; + + PROCNAME("pixcmapCreate"); + + if (depth != 1 && depth != 2 && depth !=4 && depth != 8) + return (PIXCMAP *)ERROR_PTR("depth not in {1,2,4,8}", procName, NULL); + + cmap = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP)); + cmap->depth = depth; + cmap->nalloc = 1 << depth; + cta = (RGBA_QUAD *)LEPT_CALLOC(cmap->nalloc, sizeof(RGBA_QUAD)); + cmap->array = cta; + cmap->n = 0; + return cmap; +} + + +/*! + * \brief pixcmapCreateRandom() + * + * \param[in] depth of pix, in bpp: 2, 4 or 8 + * \param[in] hasblack 1 if the first color is black; 0 if no black + * \param[in] haswhite 1 if the last color is white; 0 if no white + * \return cmap, or NULL on error + * + *
+ * Notes:
+ *      (1) This sets up a colormap with random colors,
+ *          where the first color is optionally black, the last color
+ *          is optionally white, and the remaining colors are
+ *          chosen randomly.
+ *      (2) The number of randomly chosen colors is:
+ *               2^(depth) - haswhite - hasblack
+ *      (3) Because rand() is seeded, it might disrupt otherwise
+ *          deterministic results if also used elsewhere in a program.
+ *      (4) rand() is not threadsafe, and will generate garbage if run
+ *          on multiple threads at once -- though garbage is generally
+ *          what you want from a random number generator!
+ *      (5) Modern rand()s have equal randomness in low and high order
+ *          bits, but older ones don't.  Here, we're just using rand()
+ *          to choose colors for output.
+ * 
+ */ +PIXCMAP * +pixcmapCreateRandom(l_int32 depth, + l_int32 hasblack, + l_int32 haswhite) +{ +l_int32 ncolors, i; +l_int32 red[256], green[256], blue[256]; +PIXCMAP *cmap; + + PROCNAME("pixcmapCreateRandom"); + + if (depth != 2 && depth != 4 && depth != 8) + return (PIXCMAP *)ERROR_PTR("depth not in {2, 4, 8}", procName, NULL); + if (hasblack != 0) hasblack = 1; + if (haswhite != 0) haswhite = 1; + + cmap = pixcmapCreate(depth); + ncolors = 1 << depth; + if (hasblack) /* first color is optionally black */ + pixcmapAddColor(cmap, 0, 0, 0); + for (i = hasblack; i < ncolors - haswhite; i++) { + red[i] = (l_uint32)rand() & 0xff; + green[i] = (l_uint32)rand() & 0xff; + blue[i] = (l_uint32)rand() & 0xff; + pixcmapAddColor(cmap, red[i], green[i], blue[i]); + } + if (haswhite) /* last color is optionally white */ + pixcmapAddColor(cmap, 255, 255, 255); + + return cmap; +} + + +/*! + * \brief pixcmapCreateLinear() + * + * \param[in] d depth of pix for this colormap; 1, 2, 4 or 8 + * \param[in] nlevels valid in range [2, 2^d] + * \return cmap, or NULL on error + * + *
+ * Notes:
+ *      (1) Colormap has equally spaced gray color values
+ *          from black (0, 0, 0) to white (255, 255, 255).
+ * 
+ */ +PIXCMAP * +pixcmapCreateLinear(l_int32 d, + l_int32 nlevels) +{ +l_int32 maxlevels, i, val; +PIXCMAP *cmap; + + PROCNAME("pixcmapCreateLinear"); + + if (d != 1 && d != 2 && d !=4 && d != 8) + return (PIXCMAP *)ERROR_PTR("d not in {1, 2, 4, 8}", procName, NULL); + maxlevels = 1 << d; + if (nlevels < 2 || nlevels > maxlevels) + return (PIXCMAP *)ERROR_PTR("invalid nlevels", procName, NULL); + + cmap = pixcmapCreate(d); + for (i = 0; i < nlevels; i++) { + val = (255 * i) / (nlevels - 1); + pixcmapAddColor(cmap, val, val, val); + } + return cmap; +} + + +/*! + * \brief pixcmapCopy() + * + * \param[in] cmaps + * \return cmapd, or NULL on error + */ +PIXCMAP * +pixcmapCopy(const PIXCMAP *cmaps) +{ +l_int32 nbytes, valid; +PIXCMAP *cmapd; + + PROCNAME("pixcmapCopy"); + + if (!cmaps) + return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); + pixcmapIsValid(cmaps, &valid); + if (!valid) + return (PIXCMAP *)ERROR_PTR("invalid cmap", procName, NULL); + + cmapd = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP)); + nbytes = cmaps->nalloc * sizeof(RGBA_QUAD); + cmapd->array = (void *)LEPT_CALLOC(1, nbytes); + memcpy(cmapd->array, cmaps->array, cmaps->n * sizeof(RGBA_QUAD)); + cmapd->n = cmaps->n; + cmapd->nalloc = cmaps->nalloc; + cmapd->depth = cmaps->depth; + return cmapd; +} + + +/*! + * \brief pixcmapDestroy() + * + * \param[in,out] pcmap set to null on return + * \return void + */ +void +pixcmapDestroy(PIXCMAP **pcmap) +{ +PIXCMAP *cmap; + + PROCNAME("pixcmapDestroy"); + + if (pcmap == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((cmap = *pcmap) == NULL) + return; + + LEPT_FREE(cmap->array); + LEPT_FREE(cmap); + *pcmap = NULL; + return; +} + +/*! + * \brief pixcmapIsValid() + * + * \param[in] cmap + * \param[out] pvalid return 1 if valid; 0 if not + * \return 0 if OK, 1 on error or if cmap is not valid + */ +l_ok +pixcmapIsValid(const PIXCMAP *cmap, + l_int32 *pvalid) +{ +l_int32 d; + + PROCNAME("pixcmapIsValid"); + + if (!pvalid) + return ERROR_INT("&valid not defined", procName, 1); + *pvalid = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (!cmap->array) + return ERROR_INT("cmap array not defined", procName, 1); + d = cmap->depth; + if (d !=1 && d != 2 && d != 4 && d != 8) { + L_ERROR("invalid cmap depth: %d\n", procName, d); + return 1; + } + if (cmap->nalloc < 2 || cmap->nalloc > 256) { + L_ERROR("invalid cmap nalloc: %d\n", procName, cmap->nalloc); + return 1; + } + if (cmap->n < 0 || cmap->n > 256 || cmap->n > cmap->nalloc) { + L_ERROR("invalid cmap n: %d (nalloc = %d)\n", procName, + cmap->n, cmap->nalloc); + return 1; + } + *pvalid = 1; + return 0; +} + + +/*! + * \brief pixcmapAddColor() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap entry to be added; each number + * is in range [0, ... 255] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This always adds the color if there is room.
+ *      (2) The alpha component is 255 (opaque)
+ * 
+ */ +l_ok +pixcmapAddColor(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapAddColor"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (cmap->n >= cmap->nalloc) + return ERROR_INT("no free color entries", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + cta[cmap->n].red = rval; + cta[cmap->n].green = gval; + cta[cmap->n].blue = bval; + cta[cmap->n].alpha = 255; + cmap->n++; + return 0; +} + + +/*! + * \brief pixcmapAddRGBA() + * + * \param[in] cmap + * \param[in] rval, gval, bval, aval colormap entry to be added; + * each number is in range [0, ... 255] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This always adds the color if there is room.
+ * 
+ */ +l_ok +pixcmapAddRGBA(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 aval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapAddRGBA"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (cmap->n >= cmap->nalloc) + return ERROR_INT("no free color entries", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + cta[cmap->n].red = rval; + cta[cmap->n].green = gval; + cta[cmap->n].blue = bval; + cta[cmap->n].alpha = aval; + cmap->n++; + return 0; +} + + +/*! + * \brief pixcmapAddNewColor() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap entry to be added; each number + * is in range [0, ... 255] + * \param[out] pindex index of color + * \return 0 if OK, 1 on error; 2 if unable to add color + * + *
+ * Notes:
+ *      (1) This only adds color if not already there.
+ *      (2) The alpha component is 255 (opaque)
+ *      (3) This returns the index of the new (or existing) color.
+ *      (4) Returns 2 with a warning if unable to add this color;
+ *          the caller should check the return value.
+ * 
+ */ +l_ok +pixcmapAddNewColor(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pindex) +{ + PROCNAME("pixcmapAddNewColor"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + /* Check if the color is already present. */ + if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */ + return 0; + + /* We need to add the color. Is there room? */ + if (cmap->n >= cmap->nalloc) { + L_WARNING("no free color entries\n", procName); + return 2; + } + + /* There's room. Add it. */ + pixcmapAddColor(cmap, rval, gval, bval); + *pindex = pixcmapGetCount(cmap) - 1; + return 0; +} + + +/*! + * \brief pixcmapAddNearestColor() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap entry to be added; each number + * is in range [0, ... 255] + * \param[out] pindex index of color + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only adds color if not already there.
+ *      (2) The alpha component is 255 (opaque)
+ *      (3) If it's not in the colormap and there is no room to add
+ *          another color, this returns the index of the nearest color.
+ * 
+ */ +l_ok +pixcmapAddNearestColor(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pindex) +{ + PROCNAME("pixcmapAddNearestColor"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + /* Check if the color is already present. */ + if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */ + return 0; + + /* We need to add the color. Is there room? */ + if (cmap->n < cmap->nalloc) { + pixcmapAddColor(cmap, rval, gval, bval); + *pindex = pixcmapGetCount(cmap) - 1; + return 0; + } + + /* There's no room. Return the index of the nearest color */ + pixcmapGetNearestIndex(cmap, rval, gval, bval, pindex); + return 0; +} + + +/*! + * \brief pixcmapUsableColor() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap entry to be added; each number + * is in range [0, ... 255] + * \param[out] pusable 1 if usable; 0 if not + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This checks if the color already exists or if there is
+ *          room to add it.  It makes no change in the colormap.
+ * 
+ */ +l_ok +pixcmapUsableColor(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pusable) +{ +l_int32 index; + + PROCNAME("pixcmapUsableColor"); + + if (!pusable) + return ERROR_INT("&usable not defined", procName, 1); + *pusable = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + /* Is there room to add it? */ + if (cmap->n < cmap->nalloc) { + *pusable = 1; + return 0; + } + + /* No room; check if the color is already present. */ + if (!pixcmapGetIndex(cmap, rval, gval, bval, &index)) /* found */ + *pusable = 1; + return 0; +} + + +/*! + * \brief pixcmapAddBlackOrWhite() + * + * \param[in] cmap + * \param[in] color 0 for black, 1 for white + * \param[out] pindex [optional] index of color; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only adds color if not already there.
+ *      (2) The alpha component is 255 (opaque)
+ *      (3) This sets index to the requested color.
+ *      (4) If there is no room in the colormap, returns the index
+ *          of the closest color.
+ * 
+ */ +l_ok +pixcmapAddBlackOrWhite(PIXCMAP *cmap, + l_int32 color, + l_int32 *pindex) +{ +l_int32 index; + + PROCNAME("pixcmapAddBlackOrWhite"); + + if (pindex) *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if (color == 0) { /* black */ + if (pixcmapGetFreeCount(cmap) > 0) + pixcmapAddNewColor(cmap, 0, 0, 0, &index); + else + pixcmapGetRankIntensity(cmap, 0.0, &index); + } else { /* white */ + if (pixcmapGetFreeCount(cmap) > 0) + pixcmapAddNewColor(cmap, 255, 255, 255, &index); + else + pixcmapGetRankIntensity(cmap, 1.0, &index); + } + + if (pindex) + *pindex = index; + return 0; +} + + +/*! + * \brief pixcmapSetBlackAndWhite() + * + * \param[in] cmap + * \param[in] setblack 0 for no operation; 1 to set darkest color to black + * \param[in] setwhite 0 for no operation; 1 to set lightest color to white + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapSetBlackAndWhite(PIXCMAP *cmap, + l_int32 setblack, + l_int32 setwhite) +{ +l_int32 index; + + PROCNAME("pixcmapSetBlackAndWhite"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if (setblack) { + pixcmapGetRankIntensity(cmap, 0.0, &index); + pixcmapResetColor(cmap, index, 0, 0, 0); + } + if (setwhite) { + pixcmapGetRankIntensity(cmap, 1.0, &index); + pixcmapResetColor(cmap, index, 255, 255, 255); + } + return 0; +} + + +/*! + * \brief pixcmapGetCount() + * + * \param[in] cmap + * \return count, or 0 on error + */ +l_int32 +pixcmapGetCount(const PIXCMAP *cmap) +{ + PROCNAME("pixcmapGetCount"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 0); + return cmap->n; +} + + +/*! + * \brief pixcmapGetFreeCount() + * + * \param[in] cmap + * \return free entries, or 0 on error + */ +l_int32 +pixcmapGetFreeCount(PIXCMAP *cmap) +{ + PROCNAME("pixcmapGetFreeCount"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 0); + return (cmap->nalloc - cmap->n); +} + + +/*! + * \brief pixcmapGetDepth() + * + * \param[in] cmap + * \return depth, or 0 on error + */ +l_int32 +pixcmapGetDepth(PIXCMAP *cmap) +{ + PROCNAME("pixcmapGetDepth"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 0); + return cmap->depth; +} + + +/*! + * \brief pixcmapGetMinDepth() + * + * \param[in] cmap + * \param[out] pmindepth minimum depth to support the colormap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) On error, &mindepth is returned as 0.
+ * 
+ */ +l_ok +pixcmapGetMinDepth(PIXCMAP *cmap, + l_int32 *pmindepth) +{ +l_int32 ncolors; + + PROCNAME("pixcmapGetMinDepth"); + + if (!pmindepth) + return ERROR_INT("&mindepth not defined", procName, 1); + *pmindepth = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + if (ncolors <= 4) + *pmindepth = 2; + else if (ncolors <= 16) + *pmindepth = 4; + else /* ncolors > 16 */ + *pmindepth = 8; + return 0; +} + + +/*! + * \brief pixcmapClear() + * + * \param[in] cmap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes the colors by setting the count to 0.
+ * 
+ */ +l_ok +pixcmapClear(PIXCMAP *cmap) +{ + PROCNAME("pixcmapClear"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + cmap->n = 0; + return 0; +} + + +/*-------------------------------------------------------------* + * Colormap random access * + *-------------------------------------------------------------*/ +/*! + * \brief pixcmapGetColor() + * + * \param[in] cmap + * \param[in] index + * \param[out] prval, pgval, pbval each color value + * \return 0 if OK, 1 if not accessible caller should check + */ +l_ok +pixcmapGetColor(PIXCMAP *cmap, + l_int32 index, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetColor"); + + if (!prval || !pgval || !pbval) + return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); + *prval = *pgval = *pbval = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (index < 0 || index >= cmap->n) + return ERROR_INT("index out of bounds", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + *prval = cta[index].red; + *pgval = cta[index].green; + *pbval = cta[index].blue; + return 0; +} + + +/*! + * \brief pixcmapGetColor32() + * + * \param[in] cmap + * \param[in] index + * \param[out] pval32 32-bit rgb color value + * \return 0 if OK, 1 if not accessible caller should check + * + *
+ * Notes:
+ *      (1) The returned alpha channel value is 255.
+ * 
+ */ +l_ok +pixcmapGetColor32(PIXCMAP *cmap, + l_int32 index, + l_uint32 *pval32) +{ +l_int32 rval, gval, bval; + + PROCNAME("pixcmapGetColor32"); + + if (!pval32) + return ERROR_INT("&val32 not defined", procName, 1); + *pval32 = 0; + + if (pixcmapGetColor(cmap, index, &rval, &gval, &bval) != 0) + return ERROR_INT("rgb values not found", procName, 1); + composeRGBAPixel(rval, gval, bval, 255, pval32); + return 0; +} + + +/*! + * \brief pixcmapGetRGBA() + * + * \param[in] cmap + * \param[in] index + * \param[out] prval, pgval, pbval, paval each color value + * \return 0 if OK, 1 if not accessible caller should check + */ +l_ok +pixcmapGetRGBA(PIXCMAP *cmap, + l_int32 index, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval, + l_int32 *paval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetRGBA"); + + if (!prval || !pgval || !pbval || !paval) + return ERROR_INT("&rval, &gval, &bval, &aval not all defined", + procName, 1); + *prval = *pgval = *pbval = *paval = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (index < 0 || index >= cmap->n) + return ERROR_INT("index out of bounds", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + *prval = cta[index].red; + *pgval = cta[index].green; + *pbval = cta[index].blue; + *paval = cta[index].alpha; + return 0; +} + + +/*! + * \brief pixcmapGetRGBA32() + * + * \param[in] cmap + * \param[in] index + * \param[out] pval32 32-bit rgba color value + * \return 0 if OK, 1 if not accessible caller should check + */ +l_ok +pixcmapGetRGBA32(PIXCMAP *cmap, + l_int32 index, + l_uint32 *pval32) +{ +l_int32 rval, gval, bval, aval; + + PROCNAME("pixcmapGetRGBA32"); + + if (!pval32) + return ERROR_INT("&val32 not defined", procName, 1); + *pval32 = 0; + + if (pixcmapGetRGBA(cmap, index, &rval, &gval, &bval, &aval) != 0) + return ERROR_INT("rgba values not found", procName, 1); + composeRGBAPixel(rval, gval, bval, aval, pval32); + return 0; +} + + +/*! + * \brief pixcmapResetColor() + * + * \param[in] cmap + * \param[in] index + * \param[in] rval, gval, bval colormap entry to be reset; each number + * is in range [0, ... 255] + * \return 0 if OK, 1 if not accessible caller should check + * + *
+ * Notes:
+ *      (1) This resets sets the color of an entry that has already
+ *          been set and included in the count of colors.
+ *      (2) The alpha component is 255 (opaque)
+ * 
+ */ +l_ok +pixcmapResetColor(PIXCMAP *cmap, + l_int32 index, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapResetColor"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (index < 0 || index >= cmap->n) + return ERROR_INT("index out of bounds", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + cta[index].red = rval; + cta[index].green = gval; + cta[index].blue = bval; + cta[index].alpha = 255; + return 0; +} + + +/*! + * \brief pixcmapSetAlpha() + * + * \param[in] cmap + * \param[in] index + * \param[in] aval in range [0, ... 255] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This modifies the transparency of one entry in a colormap.
+ *          The alpha component by default is 255 (opaque).
+ *          This is used when extracting the colormap from a PNG file
+ *          without decoding the image.
+ * 
+ */ +l_ok +pixcmapSetAlpha(PIXCMAP *cmap, + l_int32 index, + l_int32 aval) +{ +RGBA_QUAD *cta; + + PROCNAME("pixcmapSetAlpha"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (index < 0 || index >= cmap->n) + return ERROR_INT("index out of bounds", procName, 1); + + cta = (RGBA_QUAD *)cmap->array; + cta[index].alpha = aval; + return 0; +} + + +/*! + * \brief pixcmapGetIndex() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap colors to search for; each number + * is in range [0, ... 255] + * \param[out] pindex value of index found + * \return 0 if found, 1 if not found caller must check + */ +l_int32 +pixcmapGetIndex(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pindex) +{ +l_int32 n, i; +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetIndex"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + n = pixcmapGetCount(cmap); + + cta = (RGBA_QUAD *)cmap->array; + for (i = 0; i < n; i++) { + if (rval == cta[i].red && + gval == cta[i].green && + bval == cta[i].blue) { + *pindex = i; + return 0; + } + } + return 1; +} + + +/*! + * \brief pixcmapHasColor() + * + * \param[in] cmap + * \param[out] pcolor TRUE if cmap has color; FALSE otherwise + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapHasColor(PIXCMAP *cmap, + l_int32 *pcolor) +{ +l_int32 n, i; +l_int32 *rmap, *gmap, *bmap; + + PROCNAME("pixcmapHasColor"); + + if (!pcolor) + return ERROR_INT("&color not defined", procName, 1); + *pcolor = FALSE; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL)) + return ERROR_INT("colormap arrays not made", procName, 1); + n = pixcmapGetCount(cmap); + for (i = 0; i < n; i++) { + if ((rmap[i] != gmap[i]) || (rmap[i] != bmap[i])) { + *pcolor = TRUE; + break; + } + } + + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + return 0; +} + + +/*! + * \brief pixcmapIsOpaque() + * + * \param[in] cmap + * \param[out] popaque TRUE if fully opaque: all entries are 255 + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapIsOpaque(PIXCMAP *cmap, + l_int32 *popaque) +{ +l_int32 i, n; +RGBA_QUAD *cta; + + PROCNAME("pixcmapIsOpaque"); + + if (!popaque) + return ERROR_INT("&opaque not defined", procName, 1); + *popaque = TRUE; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + n = pixcmapGetCount(cmap); + cta = (RGBA_QUAD *)cmap->array; + for (i = 0; i < n; i++) { + if (cta[i].alpha != 255) { + *popaque = FALSE; + break; + } + } + return 0; +} + + +/*! + * \brief pixcmapIsBlackAndWhite() + * + * \param[in] cmap + * \param[out] pblackwhite TRUE if the cmap has only two colors: + * black (0,0,0) and white (255,255,255) + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapIsBlackAndWhite(PIXCMAP *cmap, + l_int32 *pblackwhite) +{ +l_int32 val0, val1, hascolor; +RGBA_QUAD *cta; + + PROCNAME("pixcmapIsBlackAndWhite"); + + if (!pblackwhite) + return ERROR_INT("&blackwhite not defined", procName, 1); + *pblackwhite = FALSE; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (pixcmapGetCount(cmap) != 2) + return 0; + + pixcmapHasColor(cmap, &hascolor); + if (hascolor) return 0; + + cta = (RGBA_QUAD *)cmap->array; + val0 = cta[0].red; + val1 = cta[1].red; + if ((val0 == 0 && val1 == 255) || (val0 == 255 && val1 == 0)) + *pblackwhite = TRUE; + return 0; +} + + +/*! + * \brief pixcmapCountGrayColors() + * + * \param[in] cmap + * \param[out] pngray number of gray colors + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This counts the unique gray colors, including black and white.
+ * 
+ */ +l_ok +pixcmapCountGrayColors(PIXCMAP *cmap, + l_int32 *pngray) +{ +l_int32 n, i, rval, gval, bval, count; +l_int32 *array; + + PROCNAME("pixcmapCountGrayColors"); + + if (!pngray) + return ERROR_INT("&ngray not defined", procName, 1); + *pngray = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + array = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + n = pixcmapGetCount(cmap); + count = 0; + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if ((rval == gval) && (rval == bval) && (array[rval] == 0)) { + array[rval] = 1; + count++; + } + } + + LEPT_FREE(array); + *pngray = count; + return 0; +} + + +/*! + * \brief pixcmapGetRankIntensity() + * + * \param[in] cmap + * \param[in] rankval 0.0 for darkest, 1.0 for lightest color + * \param[out] pindex the index into the colormap that corresponds + * to the rank intensity color + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapGetRankIntensity(PIXCMAP *cmap, + l_float32 rankval, + l_int32 *pindex) +{ +l_int32 n, i, rval, gval, bval, rankindex; +NUMA *na, *nasort; + + PROCNAME("pixcmapGetRankIntensity"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (rankval < 0.0 || rankval > 1.0) + return ERROR_INT("rankval not in [0.0 ... 1.0]", procName, 1); + + n = pixcmapGetCount(cmap); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + numaAddNumber(na, rval + gval + bval); + } + nasort = numaGetSortIndex(na, L_SORT_INCREASING); + rankindex = (l_int32)(rankval * (n - 1) + 0.5); + numaGetIValue(nasort, rankindex, pindex); + + numaDestroy(&na); + numaDestroy(&nasort); + return 0; +} + + +/*! + * \brief pixcmapGetNearestIndex() + * + * \param[in] cmap + * \param[in] rval, gval, bval colormap colors to search for; each number + * is in range [0, ... 255] + * \param[out] pindex the index of the nearest color + * \return 0 if OK, 1 on error caller must check + * + *
+ * Notes:
+ *      (1) Returns the index of the exact color if possible, otherwise the
+ *          index of the color closest to the target color.
+ *      (2) Nearest color is that which is the least sum-of-squares distance
+ *          from the target color.
+ * 
+ */ +l_ok +pixcmapGetNearestIndex(PIXCMAP *cmap, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pindex) +{ +l_int32 i, n, delta, dist, mindist; +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetNearestIndex"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = UNDEF; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if ((cta = (RGBA_QUAD *)cmap->array) == NULL) + return ERROR_INT("cta not defined(!)", procName, 1); + n = pixcmapGetCount(cmap); + + mindist = 3 * 255 * 255 + 1; + for (i = 0; i < n; i++) { + delta = cta[i].red - rval; + dist = delta * delta; + delta = cta[i].green - gval; + dist += delta * delta; + delta = cta[i].blue - bval; + dist += delta * delta; + if (dist < mindist) { + *pindex = i; + if (dist == 0) + break; + mindist = dist; + } + } + + return 0; +} + + +/*! + * \brief pixcmapGetNearestGrayIndex() + * + * \param[in] cmap + * \param[in] val gray value to search for; in range [0, ... 255] + * \param[out] pindex the index of the nearest color + * \return 0 if OK, 1 on error caller must check + * + *
+ * Notes:
+ *      (1) This should be used on gray colormaps.  It uses only the
+ *          green value of the colormap.
+ *      (2) Returns the index of the exact color if possible, otherwise the
+ *          index of the color closest to the target color.
+ * 
+ */ +l_ok +pixcmapGetNearestGrayIndex(PIXCMAP *cmap, + l_int32 val, + l_int32 *pindex) +{ +l_int32 i, n, dist, mindist; +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetNearestGrayIndex"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (val < 0 || val > 255) + return ERROR_INT("val not in [0 ... 255]", procName, 1); + + if ((cta = (RGBA_QUAD *)cmap->array) == NULL) + return ERROR_INT("cta not defined(!)", procName, 1); + n = pixcmapGetCount(cmap); + + mindist = 256; + for (i = 0; i < n; i++) { + dist = cta[i].green - val; + dist = L_ABS(dist); + if (dist < mindist) { + *pindex = i; + if (dist == 0) + break; + mindist = dist; + } + } + + return 0; +} + + +/*! + * \brief pixcmapGetDistanceToColor() + * + * \param[in] cmap + * \param[in] index + * \param[in] rval, gval, bval target color + * \param[out] pdist the distance from the cmap entry to target + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Returns the L2 distance (squared) between the color at index i
+ *          and the target color.
+ * 
+ */ +l_ok +pixcmapGetDistanceToColor(PIXCMAP *cmap, + l_int32 index, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pdist) +{ +l_int32 n, delta, dist; +RGBA_QUAD *cta; + + PROCNAME("pixcmapGetDistanceToColor"); + + if (!pdist) + return ERROR_INT("&dist not defined", procName, 1); + *pdist = UNDEF; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + n = pixcmapGetCount(cmap); + if (index >= n) + return ERROR_INT("invalid index", procName, 1); + + if ((cta = (RGBA_QUAD *)cmap->array) == NULL) + return ERROR_INT("cta not defined(!)", procName, 1); + + delta = cta[index].red - rval; + dist = delta * delta; + delta = cta[index].green - gval; + dist += delta * delta; + delta = cta[index].blue - bval; + dist += delta * delta; + *pdist = dist; + + return 0; +} + + +/*! + * \brief pixcmapGetRangeValues() + * + * \param[in] cmap + * \param[in] select L_SELECT_RED, L_SELECT_GREEN, L_SELECT_BLUE or + * L_SELECT_AVERAGE + * \param[out] pminval [optional] minimum value of component + * \param[out] pmaxval [optional] maximum value of component + * \param[out] pminindex [optional] index of minimum value + * \param[out] pmaxindex [optional] index of maximum value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Returns, for selected components (or the average), the
+ *          the extreme values (min and/or max) and their indices
+ *          that are found in the cmap.
+ * 
+ */ +l_ok +pixcmapGetRangeValues(PIXCMAP *cmap, + l_int32 select, + l_int32 *pminval, + l_int32 *pmaxval, + l_int32 *pminindex, + l_int32 *pmaxindex) +{ +l_int32 i, n, imin, imax, minval, maxval, rval, gval, bval, aveval; + + PROCNAME("pixcmapGetRangeValues"); + + if (pminval) *pminval = UNDEF; + if (pmaxval) *pmaxval = UNDEF; + if (pminindex) *pminindex = UNDEF; + if (pmaxindex) *pmaxindex = UNDEF; + if (!pminval && !pmaxval && !pminindex && !pmaxindex) + return ERROR_INT("no result requested", procName, 1); + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + imin = UNDEF; + imax = UNDEF; + minval = 100000; + maxval = -1; + n = pixcmapGetCount(cmap); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if (select == L_SELECT_RED) { + if (rval < minval) { + minval = rval; + imin = i; + } + if (rval > maxval) { + maxval = rval; + imax = i; + } + } else if (select == L_SELECT_GREEN) { + if (gval < minval) { + minval = gval; + imin = i; + } + if (gval > maxval) { + maxval = gval; + imax = i; + } + } else if (select == L_SELECT_BLUE) { + if (bval < minval) { + minval = bval; + imin = i; + } + if (bval > maxval) { + maxval = bval; + imax = i; + } + } else if (select == L_SELECT_AVERAGE) { + aveval = (rval + gval + bval) / 3; + if (aveval < minval) { + minval = aveval; + imin = i; + } + if (aveval > maxval) { + maxval = aveval; + imax = i; + } + } else { + return ERROR_INT("invalid selection", procName, 1); + } + } + + if (pminval) *pminval = minval; + if (pmaxval) *pmaxval = maxval; + if (pminindex) *pminindex = imin; + if (pmaxindex) *pmaxindex = imax; + return 0; +} + + +/*-------------------------------------------------------------* + * Colormap conversion * + *-------------------------------------------------------------*/ +/*! + * \brief pixcmapGrayToColor() + * + * \param[in] color + * \return cmap, or NULL on error + * + *
+ * Notes:
+ *      (1) This creates a colormap that maps from gray to
+ *          a specific color.  In the mapping, each component
+ *          is faded to white, depending on the gray value.
+ *      (2) In use, this is simply attached to a grayscale pix
+ *          to give it the input color.
+ * 
+ */ +PIXCMAP * +pixcmapGrayToColor(l_uint32 color) +{ +l_int32 i, rval, gval, bval; +PIXCMAP *cmap; + + extractRGBValues(color, &rval, &gval, &bval); + cmap = pixcmapCreate(8); + for (i = 0; i < 256; i++) { + pixcmapAddColor(cmap, rval + (i * (255 - rval)) / 255, + gval + (i * (255 - gval)) / 255, + bval + (i * (255 - bval)) / 255); + } + + return cmap; +} + + +/*! + * \brief pixcmapColorToGray() + * + * \param[in] cmaps + * \param[in] rwt, gwt, bwt non-negative; these should add to 1.0 + * \return cmap gray, or NULL on error + * + *
+ * Notes:
+ *      (1) This creates a gray colormap from an arbitrary colormap.
+ *      (2) In use, attach the output gray colormap to the pix
+ *          (or a copy of it) that provided the input colormap.
+ * 
+ */ +PIXCMAP * +pixcmapColorToGray(PIXCMAP *cmaps, + l_float32 rwt, + l_float32 gwt, + l_float32 bwt) +{ +l_int32 i, n, rval, gval, bval, val; +l_float32 sum; +PIXCMAP *cmapd; + + PROCNAME("pixcmapColorToGray"); + + if (!cmaps) + return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); + if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) + return (PIXCMAP *)ERROR_PTR("weights not all >= 0.0", procName, NULL); + + /* Make sure the sum of weights is 1.0; otherwise, you can get + * overflow in the gray value. */ + sum = rwt + gwt + bwt; + if (sum == 0.0) { + L_WARNING("all weights zero; setting equal to 1/3\n", procName); + rwt = gwt = bwt = 0.33333; + sum = 1.0; + } + if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ + L_WARNING("weights don't sum to 1; maintaining ratios\n", procName); + rwt = rwt / sum; + gwt = gwt / sum; + bwt = bwt / sum; + } + + if ((cmapd = pixcmapCopy(cmaps)) == NULL) + return (PIXCMAP *)ERROR_PTR("cmapd not made", procName, NULL); + n = pixcmapGetCount(cmapd); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmapd, i, &rval, &gval, &bval); + val = (l_int32)(rwt * rval + gwt * gval + bwt * bval + 0.5); + pixcmapResetColor(cmapd, i, val, val, val); + } + + return cmapd; +} + + +/*! + * \brief pixcmapConvertTo4() + * + * \param[in] cmaps colormap for 2 bpp pix + * \return cmapd (4 bpp) + * + *
+ * Notes:
+ *      (1) This converts a 2 bpp colormap to 4 bpp.  The colors
+ *          are the same; the output colormap entry array has size 16.
+ * 
+ */ +PIXCMAP * +pixcmapConvertTo4(PIXCMAP *cmaps) +{ +l_int32 i, n, rval, gval, bval; +PIXCMAP *cmapd; + + PROCNAME("pixcmapConvertTo4"); + + if (!cmaps) + return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); + if (pixcmapGetDepth(cmaps) != 2) + return (PIXCMAP *)ERROR_PTR("cmaps not for 2 bpp pix", procName, NULL); + + cmapd = pixcmapCreate(4); + n = pixcmapGetCount(cmaps); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmaps, i, &rval, &gval, &bval); + pixcmapAddColor(cmapd, rval, gval, bval); + } + return cmapd; +} + + +/*! + * \brief pixcmapConvertTo8() + * + * \param[in] cmaps colormap for 2 bpp or 4 bpp pix + * \return cmapd (8 bpp) + * + *
+ * Notes:
+ *      (1) This converts a 2 bpp or 4 bpp colormap to 8 bpp.  The colors
+ *          are the same; the output colormap entry array has size 256.
+ * 
+ */ +PIXCMAP * +pixcmapConvertTo8(PIXCMAP *cmaps) +{ +l_int32 i, n, depth, rval, gval, bval; +PIXCMAP *cmapd; + + PROCNAME("pixcmapConvertTo8"); + + if (!cmaps) + return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); + depth = pixcmapGetDepth(cmaps); + if (depth == 8) return pixcmapCopy(cmaps); + if (depth != 2 && depth != 4) + return (PIXCMAP *)ERROR_PTR("cmaps not 2 or 4 bpp", procName, NULL); + + cmapd = pixcmapCreate(8); + n = pixcmapGetCount(cmaps); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmaps, i, &rval, &gval, &bval); + pixcmapAddColor(cmapd, rval, gval, bval); + } + return cmapd; +} + + +/*-------------------------------------------------------------* + * Colormap I/O * + *-------------------------------------------------------------*/ +/*! + * \brief pixcmapRead() + * + * \param[in] filename + * \return cmap, or NULL on error + */ +PIXCMAP * +pixcmapRead(const char *filename) +{ +FILE *fp; +PIXCMAP *cmap; + + PROCNAME("pixcmapRead"); + + if (!filename) + return (PIXCMAP *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL); + cmap = pixcmapReadStream(fp); + fclose(fp); + if (!cmap) + return (PIXCMAP *)ERROR_PTR("cmap not read", procName, NULL); + return cmap; +} + + +/*! + * \brief pixcmapReadStream() + * + * \param[in] fp file stream + * \return cmap, or NULL on error + */ +PIXCMAP * +pixcmapReadStream(FILE *fp) +{ +l_int32 rval, gval, bval, aval, ignore; +l_int32 i, index, ret, depth, ncolors; +PIXCMAP *cmap; + + PROCNAME("pixcmapReadStream"); + + if (!fp) + return (PIXCMAP *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", + &depth, &ncolors); + if (ret != 2 || + (depth != 1 && depth != 2 && depth != 4 && depth != 8) || + (ncolors < 2 || ncolors > 256)) + return (PIXCMAP *)ERROR_PTR("invalid cmap size", procName, NULL); + ignore = fscanf(fp, "Color R-val G-val B-val Alpha\n"); + ignore = fscanf(fp, "----------------------------------------\n"); + + cmap = pixcmapCreate(depth); + for (i = 0; i < ncolors; i++) { + if (fscanf(fp, "%3d %3d %3d %3d %3d\n", + &index, &rval, &gval, &bval, &aval) != 5) { + pixcmapDestroy(&cmap); + return (PIXCMAP *)ERROR_PTR("invalid entry", procName, NULL); + } + pixcmapAddRGBA(cmap, rval, gval, bval, aval); + } + return cmap; +} + + +/*! + * \brief pixcmapReadMem() + * + * \param[in] data serialization of pixcmap; in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return cmap, or NULL on error + */ +PIXCMAP * +pixcmapReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +PIXCMAP *cmap; + + PROCNAME("pixcmapReadMem"); + + if (!data) + return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL); + + cmap = pixcmapReadStream(fp); + fclose(fp); + if (!cmap) L_ERROR("cmap not read\n", procName); + return cmap; +} + + +/*! + * \brief pixcmapWrite() + * + * \param[in] filename + * \param[in] cmap + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapWrite(const char *filename, + const PIXCMAP *cmap) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixcmapWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixcmapWriteStream(fp, cmap); + fclose(fp); + if (ret) + return ERROR_INT("cmap not written to stream", procName, 1); + return 0; +} + + + +/*! + * \brief pixcmapWriteStream() + * + * \param[in] fp file stream + \param[in] cmap + * \return 0 if OK, 1 on error + */ +l_ok +pixcmapWriteStream(FILE *fp, + const PIXCMAP *cmap) +{ +l_int32 *rmap, *gmap, *bmap, *amap; +l_int32 i; + + PROCNAME("pixcmapWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap)) + return ERROR_INT("colormap arrays not made", procName, 1); + + fprintf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", cmap->depth, cmap->n); + fprintf(fp, "Color R-val G-val B-val Alpha\n"); + fprintf(fp, "----------------------------------------\n"); + for (i = 0; i < cmap->n; i++) + fprintf(fp, "%3d %3d %3d %3d %3d\n", + i, rmap[i], gmap[i], bmap[i], amap[i]); + fprintf(fp, "\n"); + + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + LEPT_FREE(amap); + return 0; +} + + +/*! + * \brief pixcmapWriteMem() + * + * \param[out] pdata data of serialized pixcmap; ascii + * \param[out] psize size of returned data + * \param[in] cmap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a pixcmap in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +pixcmapWriteMem(l_uint8 **pdata, + size_t *psize, + const PIXCMAP *cmap) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixcmapWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixcmapWriteStream(fp, cmap); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = pixcmapWriteStream(fp, cmap); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*----------------------------------------------------------------------* + * Extract colormap arrays and serialization * + *----------------------------------------------------------------------*/ +/*! + * \brief pixcmapToArrays() + * + * \param[in] cmap colormap + * \param[out] prmap, pgmap, pbmap colormap arrays + * \param[out] pamap [optional] alpha array + * \return 0 if OK; 1 on error + */ +l_ok +pixcmapToArrays(const PIXCMAP *cmap, + l_int32 **prmap, + l_int32 **pgmap, + l_int32 **pbmap, + l_int32 **pamap) +{ +l_int32 *rmap, *gmap, *bmap, *amap; +l_int32 i, ncolors; +RGBA_QUAD *cta; + + PROCNAME("pixcmapToArrays"); + + if (!prmap || !pgmap || !pbmap) + return ERROR_INT("&rmap, &gmap, &bmap not all defined", procName, 1); + *prmap = *pgmap = *pbmap = NULL; + if (pamap) *pamap = NULL; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + rmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); + gmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); + bmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); + *prmap = rmap; + *pgmap = gmap; + *pbmap = bmap; + if (pamap) { + amap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); + *pamap = amap; + } + + cta = (RGBA_QUAD *)cmap->array; + for (i = 0; i < ncolors; i++) { + rmap[i] = cta[i].red; + gmap[i] = cta[i].green; + bmap[i] = cta[i].blue; + if (pamap) + amap[i] = cta[i].alpha; + } + + return 0; +} + + +/*! + * \brief pixcmapToRGBTable() + * + * \param[in] cmap colormap + * \param[out] ptab table of rgba values for the colormap + * \param[out] pncolors [optional] size of table + * \return 0 if OK; 1 on error + */ +l_ok +pixcmapToRGBTable(PIXCMAP *cmap, + l_uint32 **ptab, + l_int32 *pncolors) +{ +l_int32 i, ncolors, rval, gval, bval, aval; +l_uint32 *tab; + + PROCNAME("pixcmapToRGBTable"); + + if (!ptab) + return ERROR_INT("&tab not defined", procName, 1); + *ptab = NULL; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + if (pncolors) *pncolors = ncolors; + tab = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32)); + *ptab = tab; + + for (i = 0; i < ncolors; i++) { + pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval); + composeRGBAPixel(rval, gval, bval, aval, &tab[i]); + } + return 0; +} + + +/*! + * \brief pixcmapSerializeToMemory() + * + * \param[in] cmap colormap + * \param[in] cpc components/color: 3 for rgb, 4 for rgba + * \param[out] pncolors number of colors in table + * \param[out] pdata binary string, cpc bytes per color + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) When serializing to store in a pdf, use %cpc = 3.
+ * 
+ */ +l_ok +pixcmapSerializeToMemory(PIXCMAP *cmap, + l_int32 cpc, + l_int32 *pncolors, + l_uint8 **pdata) +{ +l_int32 i, ncolors, rval, gval, bval, aval; +l_uint8 *data; + + PROCNAME("pixcmapSerializeToMemory"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pncolors) + return ERROR_INT("&ncolors not defined", procName, 1); + *pncolors = 0; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (cpc != 3 && cpc != 4) + return ERROR_INT("cpc not 3 or 4", procName, 1); + + ncolors = pixcmapGetCount(cmap); + *pncolors = ncolors; + data = (l_uint8 *)LEPT_CALLOC((size_t)cpc * ncolors, sizeof(l_uint8)); + *pdata = data; + + for (i = 0; i < ncolors; i++) { + pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval); + data[cpc * i] = rval; + data[cpc * i + 1] = gval; + data[cpc * i + 2] = bval; + if (cpc == 4) + data[cpc * i + 3] = aval; + } + return 0; +} + + +/*! + * \brief pixcmapDeserializeFromMemory() + * + * \param[in] data binary string, 3 or 4 bytes per color + * \param[in] cpc components/color: 3 for rgb, 4 for rgba + * \param[in] ncolors + * \return cmap, or NULL on error + */ +PIXCMAP * +pixcmapDeserializeFromMemory(l_uint8 *data, + l_int32 cpc, + l_int32 ncolors) +{ +l_int32 i, d, rval, gval, bval, aval; +PIXCMAP *cmap; + + PROCNAME("pixcmapDeserializeFromMemory"); + + if (!data) + return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL); + if (cpc != 3 && cpc != 4) + return (PIXCMAP *)ERROR_PTR("cpc not 3 or 4", procName, NULL); + if (ncolors == 0) + return (PIXCMAP *)ERROR_PTR("no entries", procName, NULL); + if (ncolors > 256) + return (PIXCMAP *)ERROR_PTR("ncolors > 256", procName, NULL); + + if (ncolors > 16) + d = 8; + else if (ncolors > 4) + d = 4; + else if (ncolors > 2) + d = 2; + else + d = 1; + cmap = pixcmapCreate(d); + for (i = 0; i < ncolors; i++) { + rval = data[cpc * i]; + gval = data[cpc * i + 1]; + bval = data[cpc * i + 2]; + if (cpc == 4) + aval = data[cpc * i + 3]; + else + aval = 255; /* opaque */ + pixcmapAddRGBA(cmap, rval, gval, bval, aval); + } + + return cmap; +} + + +/*! + * \brief pixcmapConvertToHex() + * + * \param[in] data binary serialized data + * \param[in] ncolors in colormap + * \return hexdata bracketed, space-separated ascii hex string, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) The number of bytes in %data is 3 * ncolors.
+ *      (2) Output is in form:
+ *             < r0g0b0 r1g1b1 ... rngnbn >
+ *          where r0, g0, b0 ... are each 2 bytes of hex ascii
+ *      (3) This is used in pdf files to express the colormap as an
+ *          array in ascii (human-readable) format.
+ * 
+ */ +char * +pixcmapConvertToHex(l_uint8 *data, + l_int32 ncolors) +{ +l_int32 i, j, hexbytes; +char *hexdata = NULL; +char buf[4]; + + PROCNAME("pixcmapConvertToHex"); + + if (!data) + return (char *)ERROR_PTR("data not defined", procName, NULL); + if (ncolors < 1) + return (char *)ERROR_PTR("no colors", procName, NULL); + + hexbytes = 2 + (2 * 3 + 1) * ncolors + 2; + hexdata = (char *)LEPT_CALLOC(hexbytes, sizeof(char)); + hexdata[0] = '<'; + hexdata[1] = ' '; + + for (i = 0; i < ncolors; i++) { + j = 2 + (2 * 3 + 1) * i; + snprintf(buf, sizeof(buf), "%02x", data[3 * i]); + hexdata[j] = buf[0]; + hexdata[j + 1] = buf[1]; + snprintf(buf, sizeof(buf), "%02x", data[3 * i + 1]); + hexdata[j + 2] = buf[0]; + hexdata[j + 3] = buf[1]; + snprintf(buf, sizeof(buf), "%02x", data[3 * i + 2]); + hexdata[j + 4] = buf[0]; + hexdata[j + 5] = buf[1]; + hexdata[j + 6] = ' '; + } + hexdata[j + 7] = '>'; + hexdata[j + 8] = '\0'; + return hexdata; +} + + +/*-------------------------------------------------------------* + * Colormap transforms * + *-------------------------------------------------------------*/ +/*! + * \brief pixcmapGammaTRC() + * + * \param[in] cmap colormap + * \param[in] gamma gamma correction; must be > 0.0 + * \param[in] minval input value that gives 0 for output; can be < 0 + * \param[in] maxval input value that gives 255 for output; can be > 255 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place transform
+ *      (2) See pixGammaTRC() and numaGammaTRC() in enhance.c
+ *          for description and use of transform
+ * 
+ */ +l_ok +pixcmapGammaTRC(PIXCMAP *cmap, + l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +l_int32 rval, gval, bval, trval, tgval, tbval, i, ncolors; +NUMA *nag; + + PROCNAME("pixcmapGammaTRC"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (gamma <= 0.0) { + L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); + gamma = 1.0; + } + if (minval >= maxval) + return ERROR_INT("minval not < maxval", procName, 1); + + if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */ + return 0; + + if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) + return ERROR_INT("nag not made", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + numaGetIValue(nag, rval, &trval); + numaGetIValue(nag, gval, &tgval); + numaGetIValue(nag, bval, &tbval); + pixcmapResetColor(cmap, i, trval, tgval, tbval); + } + + numaDestroy(&nag); + return 0; +} + + +/*! + * \brief pixcmapContrastTRC() + * + * \param[in] cmap colormap + * \param[in] factor generally between 0.0 [no enhancement] + * and 1.0, but can be larger than 1.0 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place transform
+ *      (2) See pixContrastTRC() and numaContrastTRC() in enhance.c
+ *          for description and use of transform
+ * 
+ */ +l_ok +pixcmapContrastTRC(PIXCMAP *cmap, + l_float32 factor) +{ +l_int32 i, ncolors, rval, gval, bval, trval, tgval, tbval; +NUMA *nac; + + PROCNAME("pixcmapContrastTRC"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (factor < 0.0) { + L_WARNING("factor must be >= 0.0; setting to 0.0\n", procName); + factor = 0.0; + } + + if ((nac = numaContrastTRC(factor)) == NULL) + return ERROR_INT("nac not made", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + numaGetIValue(nac, rval, &trval); + numaGetIValue(nac, gval, &tgval); + numaGetIValue(nac, bval, &tbval); + pixcmapResetColor(cmap, i, trval, tgval, tbval); + } + + numaDestroy(&nac); + return 0; +} + + +/*! + * \brief pixcmapShiftIntensity() + * + * \param[in] cmap colormap + * \param[in] fraction between -1.0 and +1.0 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place transform
+ *      (2) It does a proportional shift of the intensity for each color.
+ *      (3) If fraction < 0.0, it moves all colors towards (0,0,0).
+ *          This darkens the image.
+ *          If fraction > 0.0, it moves all colors towards (255,255,255)
+ *          This fades the image.
+ *      (4) The equivalent transform can be accomplished with pixcmapGammaTRC(),
+ *          but it is considerably more difficult (see numaGammaTRC()).
+ * 
+ */ +l_ok +pixcmapShiftIntensity(PIXCMAP *cmap, + l_float32 fraction) +{ +l_int32 i, ncolors, rval, gval, bval; + + PROCNAME("pixcmapShiftIntensity"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (fraction < -1.0 || fraction > 1.0) + return ERROR_INT("fraction not in [-1.0, 1.0]", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if (fraction < 0.0) + pixcmapResetColor(cmap, i, + (l_int32)((1.0 + fraction) * rval), + (l_int32)((1.0 + fraction) * gval), + (l_int32)((1.0 + fraction) * bval)); + else + pixcmapResetColor(cmap, i, + rval + (l_int32)(fraction * (255 - rval)), + gval + (l_int32)(fraction * (255 - gval)), + bval + (l_int32)(fraction * (255 - bval))); + } + + return 0; +} + + +/*! + * \brief pixcmapShiftByComponent() + * + * \param[in] cmap colormap + * \param[in] srcval source color: 0xrrggbb00 + * \param[in] dstval target color: 0xrrggbb00 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place transform
+ *      (2) It implements pixelShiftByComponent() for each color.
+ *          The mapping is specified by srcval and dstval.
+ *      (3) If a component decreases, the component in the colormap
+ *          decreases by the same ratio.  Likewise for increasing, except
+ *          all ratios are taken with respect to the distance from 255.
+ * 
+ */ +l_ok +pixcmapShiftByComponent(PIXCMAP *cmap, + l_uint32 srcval, + l_uint32 dstval) +{ +l_int32 i, ncolors, rval, gval, bval; +l_uint32 newval; + + PROCNAME("pixcmapShiftByComponent"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + pixelShiftByComponent(rval, gval, bval, srcval, dstval, &newval); + extractRGBValues(newval, &rval, &gval, &bval); + pixcmapResetColor(cmap, i, rval, gval, bval); + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colormorph.c b/hgdriver/3rdparty/hgOCR/leptonica/colormorph.c new file mode 100644 index 0000000..3a92bfa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colormorph.c @@ -0,0 +1,125 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colormorph.c + *
+ *
+ *      Top-level color morphological operations
+ *
+ *            PIX     *pixColorMorph()
+ *
+ *      Method: Algorithm by van Herk and Gil and Werman, 1992
+ *              Apply grayscale morphological operations separately
+ *              to each component.
+ * 
+ */ + +#include "allheaders.h" + + +/*-----------------------------------------------------------------* + * Top-level color morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixColorMorph() + * + * \param[in] pixs + * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE, L_MORPH_OPEN, + * or L_MORPH_CLOSE + * \param[in] hsize width of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto for height of Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) This does the morph operation on each component separately,
+ *          and recombines the result.
+ *      (2) Sel is a brick with all elements being hits.
+ *      (3) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixColorMorph(PIX *pixs, + l_int32 type, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixr, *pixg, *pixb, *pixrm, *pixgm, *pixbm, *pixd; + + PROCNAME("pixColorMorph"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && + type != L_MORPH_OPEN && type != L_MORPH_CLOSE) + return (PIX *)ERROR_PTR("invalid morph type", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + if (type == L_MORPH_DILATE) { + pixrm = pixDilateGray(pixr, hsize, vsize); + pixgm = pixDilateGray(pixg, hsize, vsize); + pixbm = pixDilateGray(pixb, hsize, vsize); + } else if (type == L_MORPH_ERODE) { + pixrm = pixErodeGray(pixr, hsize, vsize); + pixgm = pixErodeGray(pixg, hsize, vsize); + pixbm = pixErodeGray(pixb, hsize, vsize); + } else if (type == L_MORPH_OPEN) { + pixrm = pixOpenGray(pixr, hsize, vsize); + pixgm = pixOpenGray(pixg, hsize, vsize); + pixbm = pixOpenGray(pixb, hsize, vsize); + } else { /* type == L_MORPH_CLOSE */ + pixrm = pixCloseGray(pixr, hsize, vsize); + pixgm = pixCloseGray(pixg, hsize, vsize); + pixbm = pixCloseGray(pixb, hsize, vsize); + } + pixd = pixCreateRGBImage(pixrm, pixgm, pixbm); + pixDestroy(&pixr); + pixDestroy(&pixrm); + pixDestroy(&pixg); + pixDestroy(&pixgm); + pixDestroy(&pixb); + pixDestroy(&pixbm); + + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colorquant1.c b/hgdriver/3rdparty/hgOCR/leptonica/colorquant1.c new file mode 100644 index 0000000..ab63e9e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colorquant1.c @@ -0,0 +1,4155 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colorquant1.c + *
+ *
+ *  Octcube color quantization
+ *
+ *  There are several different octcube/octree based quantizations.
+ *  These can be classified, in the order in which they appear in this
+ *  file, as follows:
+ *
+ *  -----------------------------------------------------------------
+ *  (1) General adaptive octree
+ *  (2) Adaptive octree by population at fixed level
+ *  (3) Adaptive octree using population and with specified number
+ *      of output colors
+ *  (4) Octcube with colormap representation of mixed color/gray
+ *  (5) 256 fixed octcubes covering color space
+ *  (6) Octcubes at fixed level for ncolors <= 256
+ *  (7) Octcubes at fixed level with RGB output
+ *  (8) Quantizing an rgb image using a specified colormap
+ *  -----------------------------------------------------------------
+ *
+ *  (1) Two-pass adaptive octree color quantization
+ *          PIX              *pixOctreeColorQuant()
+ *          PIX              *pixOctreeColorQuantGeneral()
+ *
+ *        which calls
+ *          static CQCELL  ***octreeGenerateAndPrune()
+ *          static PIX       *pixOctreeQuantizePixels()
+ *
+ *        which calls
+ *          static l_int32    octreeFindColorCell()
+ *
+ *      Helper cqcell functions
+ *          static CQCELL  ***cqcellTreeCreate()
+ *          static void       cqcellTreeDestroy()
+ *
+ *      Helper index functions
+ *          l_int32           makeRGBToIndexTables()
+ *          void              getOctcubeIndexFromRGB()
+ *          static void       getRGBFromOctcube()
+ *          static l_int32    getOctcubeIndices()
+ *          static l_int32    octcubeGetCount()
+ *
+ *  (2) Adaptive octree quantization based on population at a fixed level
+ *          PIX              *pixOctreeQuantByPopulation()
+ *          static l_int32    pixDitherOctindexWithCmap()
+ *
+ *  (3) Adaptive octree quantization to 4 and 8 bpp with specified
+ *      number of output colors in colormap
+ *          PIX              *pixOctreeQuantNumColors()
+ *
+ *  (4) Mixed color/gray quantization with specified number of colors
+ *          PIX              *pixOctcubeQuantMixedWithGray()
+ *
+ *  (5) Fixed partition octcube quantization with 256 cells
+ *          PIX              *pixFixedOctcubeQuant256()
+ *
+ *  (6) Fixed partition quantization for images with few colors
+ *          PIX              *pixFewColorsOctcubeQuant1()
+ *          PIX              *pixFewColorsOctcubeQuant2()
+ *          PIX              *pixFewColorsOctcubeQuantMixed()
+ *
+ *  (7) Fixed partition octcube quantization at specified level
+ *      with quantized output to RGB
+ *          PIX              *pixFixedOctcubeQuantGenRGB()
+ *
+ *  (8) Color quantize RGB image using existing colormap
+ *          PIX              *pixQuantFromCmap()  [high-level wrapper]
+ *          PIX              *pixOctcubeQuantFromCmap()
+ *          static PIX       *pixOctcubeQuantFromCmapLUT()
+ *
+ *      Generation of octcube histogram
+ *          NUMA             *pixOctcubeHistogram()
+ *
+ *      Get filled octcube table from colormap
+ *          l_int32          *pixcmapToOctcubeLUT()
+ *
+ *      Strip out unused elements in colormap
+ *          l_int32           pixRemoveUnusedColors()
+ *
+ *      Find number of occupied octcubes at the specified level
+ *          l_int32           pixNumberOccupiedOctcubes()
+ *
+ *  Notes:
+ *        Leptonica also provides color quantization using a modified
+ *        form of median cut.  See colorquant2.c for details.
+ * 
+ */ + +#include +#include "allheaders.h" + + +/* + *
+ *   This data structure is used for pixOctreeColorQuant(),
+ *   a color octree that adjusts to the color distribution
+ *   in the image that is being quantized.  The best settings
+ *   are with CqNLevels = 6 and DITHERING set on.
+ *
+ * Notes:
+ *      (1) the CTE (color table entry) index is sequentially
+ *          assigned as the tree is pruned back
+ *      (2) if 'bleaf' == 1, all pixels in that cube have been
+ *          assigned to one or more CTEs.  But note that if
+ *          all 8 subcubes have 'bleaf' == 1, it will have no
+ *          pixels left for assignment and will not be a CTE.
+ *      (3) 'nleaves', the number of leaves contained at the next
+ *          lower level is some number between 0 and 8, inclusive.
+ *          If it is zero, it means that all colors within this cube
+ *          are part of a single growing cluster that has not yet
+ *          been set aside as a leaf.  If 'nleaves' > 0, 'bleaf'
+ *          will be set to 1 and all pixels not assigned to leaves
+ *          at lower levels will be assigned to a CTE here.
+ *          (However, as described above, if all pixels are already
+ *          assigned, we set 'bleaf' = 1 but do not create a CTE
+ *          at this level.)
+ *      (4) To keep the maximum color error to a minimum, we
+ *          prune the tree back to level 2, and require that
+ *          all 64 level 2 cells are CTEs.
+ *      (5) We reserve an extra set of colors to prevent running out
+ *          of colors during the assignment of the final 64 level 2 cells.
+ *          This is more likely to happen with small images.
+ *      (6) When we run out of colors, the dithered image can be very
+ *          poor, so we additionally prevent dithering if the image
+ *          is small.
+ *      (7) The color content of the image is measured, and if there
+ *          is very little color, it is quantized in grayscale.
+ * 
+ */ +struct ColorQuantCell +{ + l_int32 rc, gc, bc; /* center values */ + l_int32 n; /* number of samples in this cell */ + l_int32 index; /* CTE (color table entry) index */ + l_int32 nleaves; /* # of leaves contained at next lower level */ + l_int32 bleaf; /* boolean: 0 if not a leaf, 1 if so */ +}; +typedef struct ColorQuantCell CQCELL; + + /* Constants for pixOctreeColorQuant() */ +static const l_int32 CqNLevels = 5; /* only 4, 5 and 6 are allowed */ +static const l_int32 CqReservedColors = 64; /* to allow for level 2 */ + /* remainder CTEs */ +static const l_int32 ExtraReservedColors = 25; /* to avoid running out */ +static const l_int32 TreeGenWidth = 350; /* big enough for good stats */ +static const l_int32 MinDitherSize = 250; /* don't dither if smaller */ + + +/* + *
+ *   This data structure is used for pixOctreeQuantNumColors(),
+ *   a color octree that adjusts in a simple way to the to the color
+ *   distribution in the image that is being quantized.  It outputs
+ *   colormapped images, either 4 bpp or 8 bpp, depending on the
+ *   max number of colors and the compression desired.
+ *
+ *   The number of samples is saved as a float in the first location,
+ *   because this is required to use it as the key that orders the
+ *   cells in the priority queue.
+ * 
+ * */ +struct OctcubeQuantCell +{ + l_float32 n; /* number of samples in this cell */ + l_int32 octindex; /* octcube index */ + l_int32 rcum, gcum, bcum; /* cumulative values */ + l_int32 rval, gval, bval; /* average values */ +}; +typedef struct OctcubeQuantCell OQCELL; + + +/* + *
+ *   This data structure is using for heap sorting octcubes
+ *   by population.  Sort order is decreasing.
+ * 
+ */ +struct L_OctcubePop +{ + l_float32 npix; /* parameter on which to sort */ + l_int32 index; /* octcube index at assigned level */ + l_int32 rval; /* mean red value of pixels in octcube */ + l_int32 gval; /* mean green value of pixels in octcube */ + l_int32 bval; /* mean blue value of pixels in octcube */ +}; +typedef struct L_OctcubePop L_OCTCUBE_POP; + +/* + *
+ *   In pixDitherOctindexWithCmap(), we use these default values.
+     To get the max value of 'dif' in the dithering color transfer,
+     divide these "DIF_CAP" values by 8.  However, a value of
+     0 means that there is no cap (infinite cap).  A very small
+     value is used for POP_DIF_CAP because dithering on the population
+     generated colormap can be unstable without a tight cap.
+ * 
+ */ + +static const l_int32 FIXED_DIF_CAP = 0; +static const l_int32 POP_DIF_CAP = 40; + + + /* Static octree helper function */ +static l_int32 octreeFindColorCell(l_int32 octindex, CQCELL ***cqcaa, + l_int32 *pindex, l_int32 *prval, + l_int32 *pgval, l_int32 *pbval); + + /* Static cqcell functions */ +static CQCELL ***octreeGenerateAndPrune(PIX *pixs, l_int32 colors, + l_int32 reservedcolors, + PIXCMAP **pcmap); +static PIX *pixOctreeQuantizePixels(PIX *pixs, CQCELL ***cqcaa, + l_int32 ditherflag); +static CQCELL ***cqcellTreeCreate(void); +static void cqcellTreeDestroy(CQCELL ****pcqcaa); + + /* Static helper octcube index functions */ +static void getRGBFromOctcube(l_int32 cubeindex, l_int32 level, + l_int32 *prval, l_int32 *pgval, l_int32 *pbval); +static l_int32 getOctcubeIndices(l_int32 rgbindex, l_int32 level, + l_int32 *pbindex, l_int32 *psindex); +static l_int32 octcubeGetCount(l_int32 level, l_int32 *psize); + + /* Static function to perform octcube-indexed dithering */ +static l_int32 pixDitherOctindexWithCmap(PIX *pixs, PIX *pixd, l_uint32 *rtab, + l_uint32 *gtab, l_uint32 *btab, + l_int32 *carray, l_int32 difcap); + + /* Static function to perform octcube-based quantizing from colormap */ +static PIX *pixOctcubeQuantFromCmapLUT(PIX *pixs, PIXCMAP *cmap, + l_int32 mindepth, l_int32 *cmaptab, + l_uint32 *rtab, l_uint32 *gtab, + l_uint32 *btab); + +#ifndef NO_CONSOLE_IO +#define DEBUG_COLORQUANT 0 +#define DEBUG_OCTINDEX 0 +#define DEBUG_OCTCUBE_CMAP 0 +#define DEBUG_POP 0 +#define DEBUG_FEW_COLORS 0 +#define PRINT_OCTCUBE_STATS 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------------------* + * Two-pass adaptive octree color quantization * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixOctreeColorQuant() + * + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] colors in colormap; some number in range [128 ... 256]; + * the actual number of colors used will be smaller + * \param[in] ditherflag 1 to dither, 0 otherwise + * \return pixd 8 bpp with colormap, or NULL on error + * + *
+ *  I found one description in the literature of octree color
+ *  quantization, using progressive truncation of the octree,
+ *  by M. Gervautz and W. Purgathofer in Graphics Gems, pp.
+ *  287-293, ed. A. Glassner, Academic Press, 1990.
+ *  Rather than setting up a fixed partitioning of the color
+ *  space ab initio, as we do here, they allow the octree to be
+ *  progressively truncated as new pixels are added.  They
+ *  need to set up some data structures that are traversed
+ *  with the addition of each 24 bit pixel, in order to decide
+ *  either 1) in which cluster (sub-branch of the octree to put
+ *  the pixel, or 2 whether to truncate the octree further
+ *  to place the pixel in an existing cluster, or 3 which
+ *  two existing clusters should be merged so that the pixel
+ *  can be left to start a truncated leaf of the octree.  Such dynamic
+ *  truncation is considerably more complicated, and Gervautz et
+ *  al. did not explain how they did it in anywhere near the
+ *  detail required to check their implementation.
+ *
+ *  The simple method in pixFixedOctcubeQuant256 is very
+ *  fast, and with dithering the results are good, but you
+ *  can do better if the color clusters are selected adaptively
+ *  from the image.  We want a method that makes much better
+ *  use of color samples in regions of color space with high
+ *  pixel density, while also fairly representing small numbers
+ *  of color pixels in low density regions.  Such adaptation
+ *  requires two passes through the image: the first for generating
+ *  the pruned tree of color cubes and the second for computing the index
+ *  into the color table for each pixel.
+ *
+ *  A relatively simple adaptive method is pixOctreeQuantByPopulation.
+ *  That function first determines if the image has very few colors,
+ *  and, if so, quantizes to those colors.  If there are more than
+ *  256 colors, it generates a histogram of octcube leaf occupancy
+ *  at level 4, chooses the 192 most populated such leaves as
+ *  the first 192 colors, and sets the remaining 64 colors to the
+ *  residual average pixel values in each of the 64 level 2 octcubes.
+ *  This is a bit faster than pixOctreeColorQuant, and does very
+ *  well without dithering, but for most images with dithering it
+ *  is clearly inferior.
+ *
+ *  We now describe pixOctreeColorQuant.  The first pass is done
+ *  on a subsampled image, because we do not need to use all the
+ *  pixels in the image to generate the tree.  Subsampling
+ *  down to 0.25 1/16 of the pixels makes the program run
+ *  about 1.3 times faster.
+ *
+ *  Instead of dividing the color space into 256 equal-sized
+ *  regions, we initially divide it into 2^12 or 2^15 or 2^18
+ *  equal-sized octcubes.  Suppose we choose to use 2^18 octcubes.
+ *  This gives us 6 octree levels.  We then prune back,
+ *  starting from level 6.  For every cube at level 6, there
+ *  are 8 cubes at level 5.  Call the operation of putting a
+ *  cube aside as a color table entry CTE a "saving."
+ *  We use a in general level-dependent threshold, and save
+ *  those level 6 cubes that are above threshold.
+ *  The rest are combined into the containing level 5 cube.
+ *  If between 1 and 7 level 6 cubes within a level 5
+ *  cube have been saved by thresholding, then the remaining
+ *  level 6 cubes in that level 5 cube are automatically
+ *  saved as well, without applying a threshold.  This greatly
+ *  simplifies both the description of the CTEs and the later
+ *  classification of each pixel as belonging to a CTE.
+ *  This procedure is iterated through every cube, starting at
+ *  level 5, and then 4, 3, and 2, successively.  The result is that
+ *  each CTE contains the entirety of a set of from 1 to 7 cubes
+ *  from a given level that all belong to a single cube at the
+ *  level above.   We classify the CTEs in terms of the
+ *  condition in which they are made as either being "threshold"
+ *  or "residual."  They are "threshold" CTEs if no subcubes
+ *  are CTEs that is, they contain every pixel within the cube
+ *  and the number of pixels exceeds the threshold for making
+ *  a CTE.  They are "residual" CTEs if at least one but not more
+ *  than 7 of the subcubes have already been determined to be CTEs;
+ *  this happens automatically -- no threshold is applied.
+ *  If all 8 subcubes are determined to be CTEs, the cube is
+ *  marked as having all pixels accounted for 'bleaf' = 1 but
+ *  is not saved as a CTE.
+ *
+ *  We stop the pruning at level 2, at which there are 64
+ *  sub-cubes.  Any pixels not already claimed in a CTE are
+ *  put in these cubes.
+ *
+ *  As the cubes are saved as color samples in the color table,
+ *  the number of remaining pixels P and the number of
+ *  remaining colors in the color table N are recomputed,
+ *  along with the average number of pixels P/N ppc to go in
+ *  each of the remaining colors.  This running average number is
+ *  used to set the threshold at the current level.
+ *
+ *  Because we are going to very small cubes at levels 6 or 5,
+ *  and will dither the colors for errors, it is not necessary
+ *  to compute the color center of each cluster; we can simply
+ *  use the center of the cube.  This gives us a minimax error
+ *  condition: the maximum error is half the width of the
+ *  level 2 cubes -- 32 color values out of 256 -- for each color
+ *  sample.  In practice, most of the pixels will be very much
+ *  closer to the center of their cells.  And with dithering,
+ *  the average pixel color in a small region will be closer still.
+ *  Thus with the octree quantizer, we are able to capture
+ *  regions of high color pdf probability density function in small
+ *  but accurate CTEs, and to have only a small number of pixels
+ *  that end up a significant distance with a guaranteed maximum
+ *  from their true color.
+ *
+ *  How should the threshold factor vary?  Threshold factors
+ *  are required for levels 2, 3, 4 and 5 in the pruning stage.
+ *  The threshold for level 5 is actually applied to cubes at
+ *  level 6, etc.  From various experiments, it appears that
+ *  the results do not vary appreciably for threshold values near 1.0.
+ *  If you want more colors in smaller cubes, the threshold
+ *  factors can be set lower than 1.0 for cubes at levels 4 and 5.
+ *  However, if the factor is set much lower than 1.0 for
+ *  levels 2 and 3, we can easily run out of colors.
+ *  We put aside 64 colors in the calculation of the threshold
+ *  values, because we must have 64 color centers at level 2,
+ *  that will have very few pixels in most of them.
+ *  If we reduce the factor for level 5 to 0.4, this will
+ *  generate many level 6 CTEs, and consequently
+ *  many residual cells will be formed up from those leaves,
+ *  resulting in the possibility of running out of colors.
+ *  Remember, the residual CTEs are mandatory, and are formed
+ *  without using the threshold, regardless of the number of
+ *  pixels that are absorbed.
+ *
+ *  The implementation logically has four parts:
+ *
+ *       1 accumulation into small, fixed cells
+ *       2 pruning back into selected CTE cubes
+ *       3 organizing the CTEs for fast search to find
+ *           the CTE to which any image pixel belongs
+ *       4 doing a second scan to code the image pixels by CTE
+ *
+ *  Step 1 is straightforward; we use 2^15 cells.
+ *
+ *  We've already discussed how the pruning step 2 will be performed.
+ *
+ *  Steps 3) and (4 are related, in that the organization
+ *  used by step 3 determines how the search actually
+ *  takes place for each pixel in step 4.
+ *
+ *  There are many ways to do step 3.  Let's explore a few.
+ *
+ *  a The simplest is to order the cubes from highest occupancy
+ *      to lowest, and traverse the list looking for the deepest
+ *      match.  To make this more efficient, so that we know when
+ *      to stop looking, any cube that has separate CTE subcubes
+ *      would be marked as such, so that we know when we hit a
+ *      true leaf.
+ *
+ *  b Alternatively, we can order the cubes by highest
+ *      occupancy separately each level, and work upward,
+ *      starting at level 5, so that when we find a match we
+ *      know that it will be correct.
+ *
+ *  c Another approach would be to order the cubes by
+ *      "address" and use a hash table to find the cube
+ *      corresponding to a pixel color.  I don't know how to
+ *      do this with a variable length address, as each CTE
+ *      will have 3*n bits, where n is the level.
+ *
+ *  d Another approach entirely is to put the CTE cubes into
+ *      a tree, in such a way that starting from the root, and
+ *      using 3 bits of address at a time, the correct branch of
+ *      each octree can be taken until a leaf is found.  Because
+ *      a given cube can be both a leaf and also have branches
+ *      going to sub-cubes, the search stops only when no
+ *      marked subcubes have addresses that match the given pixel.
+ *
+ *      In the tree method, we can start with a dense infrastructure,
+ *      and place the leaves corresponding to the N colors
+ *      in the tree, or we can grow from the root only those
+ *      branches that end directly on leaves.
+ *
+ *  What we do here is to take approach d, and implement the tree
+ *  "virtually", as a set of arrays, one array for each level
+ *  of the tree.   Initially we start at level 5, an array with
+ *  2^15 cubes, each with 8 subcubes.  We then build nodes at
+ *  levels closer to the root; at level 4 there are 2^12 nodes
+ *  each with 8 subcubes; etc.  Using these arrays has
+ *  several advantages:
+ *
+ *     ~  We don't need to keep track of links between cubes
+ *        and subcubes, because we can use the canonical
+ *        addressing on the cell arrays directly to determine
+ *        which nodes are parent cubes and which are sub-cubes.
+ *
+ *     ~  We can prune directly on this tree
+ *
+ *     ~  We can navigate the pruned tree quickly to classify
+ *        each pixel in the image.
+ *
+ *  Canonical addressing guarantees that the i-th node at level k
+ *  has 8 subnodes given by the 8*i ... 8*i+7 nodes at level k+1.
+ *
+ *  The pruning step works as follows.  We go from the lowest
+ *  level up.  At each level, the threshold is found from the
+ *  product of a factor near 1.0 and the ratio of unmarked pixels
+ *  to remaining colors minus the 64.  We march through
+ *  the space, sequentially considering a cube and its 8 subcubes.
+ *  We first check those subcubes that are not already
+ *  marked as CTE to see if any are above threshold, and if so,
+ *  generate a CTE and mark them as such.
+ *  We then determine if any of the subcubes have been marked.
+ *  If so, and there are subcubes that are not marked,
+ *  we generate a CTE for the cube from the remaining unmarked
+ *  subcubes; this is mandatory and does not depend on how many
+ *  pixels are in the set of subcubes.  If none of the subcubes
+ *  are marked, we aggregate their pixels into the cube
+ *  containing them, but do not mark it as a CTE; that
+ *  will be determined when iterating through the next level up.
+ *
+ *  When all the pixels in a cube are accounted for in one or more
+ *  colors, we set the boolean 'bleaf' to true.  This is the
+ *  flag used to mark the cubes in the pruning step.  If a cube
+ *  is marked, and all 8 subcubes are marked, then it is not
+ *  itself given a CTE because all pixels have already been
+ *  accounted for.
+ *
+ *  Note that the pruning of the tree and labelling of the CTEs
+ *  step 2 accomplishes step 3 implicitly, because the marked
+ *  and pruned tree is ready for use in labelling each pixel
+ *  in step 4.  We now, for every pixel in the image, traverse
+ *  the tree from the root, looking for the lowest cube that is a leaf.
+ *  At each level we have a cube and subcube.  If we reach a subcube
+ *  leaf that is marked 0, we know that the color is stored in the
+ *  cube above, and we've found the CTE.  Otherwise, the subcube
+ *  leaf is marked 1.  If we're at the last level, we've reached
+ *  the final leaf and must use it.  Otherwise, continue the
+ *  process at the next level down.
+ *
+ *  For robustness, efficiency and high quality output, we do the following:
+ *
+ *  (1) Measure the color content of the image.  If there is very little
+ *      color, quantize in grayscale.
+ *  (2) For efficiency, build the octree with a subsampled image if the
+ *      image is larger than some threshold size.
+ *  (3) Reserve an extra set of colors to prevent running out of colors
+ *      when pruning the octree; specifically, during the assignment
+ *      of those level 2 cells out of the 64 that have unassigned
+ *      pixels.  The problem of running out is more likely to happen
+ *      with small images, because the estimation we use for the
+ *      number of pixels available is not accurate.
+ *  (4) In the unlikely event that we run out of colors, the dithered
+ *      image can be very poor.  As this would only happen with very
+ *      small images, and dithering is not particularly noticeable with
+ *      such images, turn it off.
+ * 
+ */ +PIX * +pixOctreeColorQuant(PIX *pixs, + l_int32 colors, + l_int32 ditherflag) +{ + PROCNAME("pixOctreeColorQuant"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (colors < 128 || colors > 240) /* further restricted */ + return (PIX *)ERROR_PTR("colors must be in [128, 240]", procName, NULL); + + return pixOctreeColorQuantGeneral(pixs, colors, ditherflag, 0.01, 0.01); +} + + +/*! + * \brief pixOctreeColorQuantGeneral() + * + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] colors in colormap; some number in range [128 ... 240]; + * the actual number of colors used will be smaller + * \param[in] ditherflag 1 to dither, 0 otherwise + * \param[in] validthresh minimum fraction of pixels neither near white + * nor black, required for color quantization; + * typically ~0.01, but smaller for images that have + * color but are nearly all white + * \param[in] colorthresh minimum fraction of pixels with color that are + * not near white or black, that are required + * for color quantization; typ. ~0.01, but smaller + * for images that have color along with a + * significant fraction of gray + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) The parameters %validthresh and %colorthresh are used to
+ *          determine if color quantization should be used on an image,
+ *          or whether, instead, it should be quantized in grayscale.
+ *          If the image has very few non-white and non-black pixels, or
+ *          if those pixels that are non-white and non-black are all
+ *          very close to either white or black, it is usually better
+ *          to treat the color as accidental and to quantize the image
+ *          to gray only.  These parameters are useful if you know
+ *          something a priori about the image.  Perhaps you know that
+ *          there is only a very small fraction of color pixels, but they're
+ *          important to preserve; then you want to use a smaller value for
+ *          these parameters.  To disable conversion to gray and force
+ *          color quantization, use %validthresh = 0.0 and %colorthresh = 0.0.
+ *      (2) See pixOctreeColorQuant() for algorithmic and implementation
+ *          details.  This function has a more general interface.
+ *      (3) See pixColorFraction() for computing the fraction of pixels
+ *          that are neither white nor black, and the fraction of those
+ *          pixels that have little color.  From the documentation there:
+ *             If pixfract is very small, there are few pixels that are
+ *             neither black nor white.  If colorfract is very small,
+ *             the pixels that are neither black nor white have very
+ *             little color content.  The product 'pixfract * colorfract'
+ *             gives the fraction of pixels with significant color content.
+ *          We test against the product %validthresh * %colorthresh
+ *          to find color in images that have either very few
+ *          intermediate gray pixels or that have many such gray pixels.
+ * 
+ */ +PIX * +pixOctreeColorQuantGeneral(PIX *pixs, + l_int32 colors, + l_int32 ditherflag, + l_float32 validthresh, + l_float32 colorthresh) +{ +l_int32 w, h, minside, factor, index, rval, gval, bval; +l_float32 scalefactor; +l_float32 pixfract; /* fraction neither near white nor black */ +l_float32 colorfract; /* fraction with color of the pixfract population */ +CQCELL ***cqcaa; +PIX *pixd, *pixsub; +PIXCMAP *cmap; + + PROCNAME("pixOctreeColorQuantGeneral"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (colors < 128 || colors > 240) + return (PIX *)ERROR_PTR("colors must be in [128, 240]", procName, NULL); + + /* Determine if the image has sufficient color content for + * octree quantization, based on the input thresholds. + * If pixfract << 1, most pixels are close to black or white. + * If colorfract << 1, the pixels that are not near + * black or white have very little color. + * If with insufficient color, quantize with a grayscale colormap. */ + pixGetDimensions(pixs, &w, &h, NULL); + if (validthresh > 0.0 && colorthresh > 0.0) { + minside = L_MIN(w, h); + factor = L_MAX(1, minside / 400); + pixColorFraction(pixs, 20, 244, 20, factor, &pixfract, &colorfract); + if (pixfract * colorfract < validthresh * colorthresh) { + L_INFO("\n Pixel fraction neither white nor black = %6.3f" + "\n Color fraction of those pixels = %6.3f" + "\n Quantizing to 8 bpp gray\n", + procName, pixfract, colorfract); + return pixConvertTo8(pixs, 1); + } + } else { + L_INFO("\n Process in color by default\n", procName); + } + + /* Conditionally subsample to speed up the first pass */ + if (w > TreeGenWidth) { + scalefactor = (l_float32)TreeGenWidth / (l_float32)w; + pixsub = pixScaleBySampling(pixs, scalefactor, scalefactor); + } else { + pixsub = pixClone(pixs); + } + + /* Drop the number of requested colors if image is very small */ + if (w < MinDitherSize && h < MinDitherSize) + colors = L_MIN(colors, 220); + + /* Make the pruned octree */ + cqcaa = octreeGenerateAndPrune(pixsub, colors, CqReservedColors, &cmap); + if (!cqcaa) { + pixDestroy(&pixsub); + return (PIX *)ERROR_PTR("tree not made", procName, NULL); + } +#if DEBUG_COLORQUANT + L_INFO(" Colors requested = %d\n", procName, colors); + L_INFO(" Actual colors = %d\n", procName, cmap->n); +#endif /* DEBUG_COLORQUANT */ + + /* Do not dither if image is very small */ + if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { + L_INFO("Small image: dithering turned off\n", procName); + ditherflag = 0; + } + + /* Traverse tree from root, looking for lowest cube + * that is a leaf, and set dest pix value to its + * colortable index */ + if ((pixd = pixOctreeQuantizePixels(pixs, cqcaa, ditherflag)) == NULL) { + pixDestroy(&pixsub); + cqcellTreeDestroy(&cqcaa); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + /* Attach colormap and copy res */ + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Force darkest color to black if each component <= 4 */ + pixcmapGetRankIntensity(cmap, 0.0, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval < 5 && gval < 5 && bval < 5) + pixcmapResetColor(cmap, index, 0, 0, 0); + + /* Force lightest color to white if each component >= 252 */ + pixcmapGetRankIntensity(cmap, 1.0, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval > 251 && gval > 251 && bval > 251) + pixcmapResetColor(cmap, index, 255, 255, 255); + + cqcellTreeDestroy(&cqcaa); + pixDestroy(&pixsub); + return pixd; +} + + +/*! + * \brief octreeGenerateAndPrune() + * + * \param[in] pixs + * \param[in] colors number of colors to use between 128 and 256 + * \param[in] reservedcolors number of reserved colors + * \param[out] pcmap colormap returned + * \return octree, colormap and number of colors used, or NULL + * on error + * + *
+ * Notes:
+ *      (1) The number of colors in the cmap may differ from the number
+ *          of colors requested, but it will not be larger than 256
+ * 
+ */ +static CQCELL *** +octreeGenerateAndPrune(PIX *pixs, + l_int32 colors, + l_int32 reservedcolors, + PIXCMAP **pcmap) +{ +l_int32 rval, gval, bval, cindex; +l_int32 level, ncells, octindex; +l_int32 w, h, wpls; +l_int32 i, j, isub; +l_int32 npix; /* number of remaining pixels to be assigned */ +l_int32 ncolor; /* number of remaining color cells to be used */ +l_int32 ppc; /* ave number of pixels left for each color cell */ +l_int32 rv, gv, bv; +l_float32 thresholdFactor[] = {0.01f, 0.01f, 1.0f, 1.0f, 1.0f, 1.0f}; +l_float32 thresh; /* factor of ppc for this level */ +l_uint32 *datas, *lines; +l_uint32 *rtab, *gtab, *btab; +CQCELL ***cqcaa; /* one array for each octree level */ +CQCELL **cqca, **cqcasub; +CQCELL *cqc, *cqcsub; +PIXCMAP *cmap; +NUMA *nat; /* accumulates levels for threshold cells */ +NUMA *nar; /* accumulates levels for residual cells */ + + PROCNAME("octreeGenerateAndPrune"); + + if (!pixs) + return (CQCELL ***)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (CQCELL ***)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (colors < 128 || colors > 256) + return (CQCELL ***)ERROR_PTR("colors not in [128,256]", procName, NULL); + if (!pcmap) + return (CQCELL ***)ERROR_PTR("&cmap not defined", procName, NULL); + + if ((cqcaa = cqcellTreeCreate()) == NULL) + return (CQCELL ***)ERROR_PTR("cqcaa not made", procName, NULL); + + /* Make the canonical index tables */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(CqNLevels, &rtab, >ab, &btab); + + /* Generate an 8 bpp cmap (max size 256) */ + cmap = pixcmapCreate(8); + *pcmap = cmap; + + pixGetDimensions(pixs, &w, &h, NULL); + npix = w * h; /* initialize to all pixels */ + ncolor = colors - reservedcolors - ExtraReservedColors; + ppc = npix / ncolor; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Accumulate the centers of each cluster at level CqNLevels */ + ncells = 1 << (3 * CqNLevels); + cqca = cqcaa[CqNLevels]; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + cqc = cqca[octindex]; + cqc->n++; + } + } + + /* Arrays for storing statistics */ + nat = numaCreate(0); + nar = numaCreate(0); + + /* Prune back from the lowest level and generate the colormap */ + for (level = CqNLevels - 1; level >= 2; level--) { + thresh = thresholdFactor[level]; + cqca = cqcaa[level]; + cqcasub = cqcaa[level + 1]; + ncells = 1 << (3 * level); + for (i = 0; i < ncells; i++) { /* i is octindex at level */ + cqc = cqca[i]; + for (j = 0; j < 8; j++) { /* check all subnodes */ + isub = 8 * i + j; /* isub is octindex at level+1 */ + cqcsub = cqcasub[isub]; + if (cqcsub->bleaf == 1) { /* already a leaf? */ + cqc->nleaves++; /* count the subcube leaves */ + continue; + } + if (cqcsub->n >= thresh * ppc) { /* make it a true leaf? */ + cqcsub->bleaf = 1; + if (cmap->n < 256) { + cqcsub->index = cmap->n; /* assign the color index */ + getRGBFromOctcube(isub, level + 1, &rv, &gv, &bv); + pixcmapAddColor(cmap, rv, gv, bv); +#if 1 /* save values */ + cqcsub->rc = rv; + cqcsub->gc = gv; + cqcsub->bc = bv; +#endif + } else { + /* This doesn't seem to happen. Do something. */ + L_ERROR("assigning pixels to wrong color\n", procName); + pixcmapGetNearestIndex(cmap, 128, 128, 128, &cindex); + cqcsub->index = cindex; /* assign to the nearest */ + pixcmapGetColor(cmap, cindex, &rval, &gval, &bval); + cqcsub->rc = rval; + cqcsub->gc = gval; + cqcsub->bc = bval; + } + cqc->nleaves++; + npix -= cqcsub->n; + ncolor--; + if (ncolor > 0) + ppc = npix / ncolor; + else if (ncolor + reservedcolors > 0) + ppc = npix / (ncolor + reservedcolors); + else + ppc = 1000000; /* make it big */ + numaAddNumber(nat, level + 1); + +#if DEBUG_OCTCUBE_CMAP + fprintf(stderr, "Exceeds threshold: colors used = %d, colors remaining = %d\n", + cmap->n, ncolor + reservedcolors); + fprintf(stderr, " cell with %d pixels, npix = %d, ppc = %d\n", + cqcsub->n, npix, ppc); + fprintf(stderr, " index = %d, level = %d, subindex = %d\n", + i, level, j); + fprintf(stderr, " rv = %d, gv = %d, bv = %d\n", rv, gv, bv); +#endif /* DEBUG_OCTCUBE_CMAP */ + + } + } + if (cqc->nleaves > 0 || level == 2) { /* make the cube a leaf now */ + cqc->bleaf = 1; + if (cqc->nleaves < 8) { /* residual CTE cube: acquire the + * remaining pixels */ + for (j = 0; j < 8; j++) { /* check all subnodes */ + isub = 8 * i + j; + cqcsub = cqcasub[isub]; + if (cqcsub->bleaf == 0) /* absorb */ + cqc->n += cqcsub->n; + } + if (cmap->n < 256) { + cqc->index = cmap->n; /* assign the color index */ + getRGBFromOctcube(i, level, &rv, &gv, &bv); + pixcmapAddColor(cmap, rv, gv, bv); +#if 1 /* save values */ + cqc->rc = rv; + cqc->gc = gv; + cqc->bc = bv; +#endif + } else { + L_WARNING("possibly assigned pixels to wrong color\n", + procName); + /* This is very bad. It will only cause trouble + * with dithering, and we try to avoid it with + * ExtraReservedColors. */ + pixcmapGetNearestIndex(cmap, rv, gv, bv, &cindex); + cqc->index = cindex; /* assign to the nearest */ + pixcmapGetColor(cmap, cindex, &rval, &gval, &bval); + cqc->rc = rval; + cqc->gc = gval; + cqc->bc = bval; + } + npix -= cqc->n; + ncolor--; + if (ncolor > 0) + ppc = npix / ncolor; + else if (ncolor + reservedcolors > 0) + ppc = npix / (ncolor + reservedcolors); + else + ppc = 1000000; /* make it big */ + numaAddNumber(nar, level); + +#if DEBUG_OCTCUBE_CMAP + fprintf(stderr, "By remainder: colors used = %d, colors remaining = %d\n", + cmap->n, ncolor + reservedcolors); + fprintf(stderr, " cell with %d pixels, npix = %d, ppc = %d\n", + cqc->n, npix, ppc); + fprintf(stderr, " index = %d, level = %d\n", i, level); + fprintf(stderr, " rv = %d, gv = %d, bv = %d\n", rv, gv, bv); +#endif /* DEBUG_OCTCUBE_CMAP */ + + } + } else { /* absorb all the subpixels but don't make it a leaf */ + for (j = 0; j < 8; j++) { /* absorb from all subnodes */ + isub = 8 * i + j; + cqcsub = cqcasub[isub]; + cqc->n += cqcsub->n; + } + } + } + } + +#if PRINT_OCTCUBE_STATS +{ +l_int32 tc[] = {0, 0, 0, 0, 0, 0, 0}; +l_int32 rc[] = {0, 0, 0, 0, 0, 0, 0}; +l_int32 nt, nr, ival; + + nt = numaGetCount(nat); + nr = numaGetCount(nar); + for (i = 0; i < nt; i++) { + numaGetIValue(nat, i, &ival); + tc[ival]++; + } + for (i = 0; i < nr; i++) { + numaGetIValue(nar, i, &ival); + rc[ival]++; + } + fprintf(stderr, " Threshold cells formed: %d\n", nt); + for (i = 1; i < CqNLevels + 1; i++) + fprintf(stderr, " level %d: %d\n", i, tc[i]); + fprintf(stderr, "\n Residual cells formed: %d\n", nr); + for (i = 0; i < CqNLevels ; i++) + fprintf(stderr, " level %d: %d\n", i, rc[i]); +} +#endif /* PRINT_OCTCUBE_STATS */ + + numaDestroy(&nat); + numaDestroy(&nar); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + + return cqcaa; +} + + +/*! + * \brief pixOctreeQuantizePixels() + * + * \param[in] pixs 32 bpp + * \param[in] cqcaa octree in array format + * \param[in] ditherflag 1 for dithering, 0 for no dithering + * \return pixd or NULL on error + * + *
+ * Notes:
+ *      (1) This routine doesn't need to use the CTEs (colormap
+ *          table entries) because the color indices are embedded
+ *          in the octree.  Thus, the calling program must make
+ *          and attach the colormap to pixd after it is returned.
+ *      (2) Dithering is performed in integers, effectively rounding
+ *          to 1/8 sample increment.  The data in the integer buffers is
+ *          64 times the sample values.  The 'dif' is 8 times the
+ *          sample values, and this spread, multiplied by 8, to the
+ *          integer buffers.  Because the dif is truncated to an
+ *          integer, the dither is accurate to 1/8 of a sample increment,
+ *          or 1/2048 of the color range.
+ * 
+ */ +static PIX * +pixOctreeQuantizePixels(PIX *pixs, + CQCELL ***cqcaa, + l_int32 ditherflag) +{ +l_uint8 *bufu8r, *bufu8g, *bufu8b; +l_int32 rval, gval, bval; +l_int32 octindex, index; +l_int32 val1, val2, val3, dif; +l_int32 w, h, wpls, wpld, i, j, success; +l_int32 rc, gc, bc; +l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixOctreeQuantizePixels"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (!cqcaa) + return (PIX *)ERROR_PTR("cqcaa not defined", procName, NULL); + + /* Make output 8 bpp palette image */ + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Make the canonical index tables */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(CqNLevels, &rtab, >ab, &btab); + + /* Traverse tree from root, looking for lowest cube + * that is a leaf, and set dest pix to its + * colortable index value. The results are far + * better when dithering to get a more accurate + * average color. */ + if (ditherflag == 0) { /* no dithering */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); + SET_DATA_BYTE(lined, j, index); + } + } + } else { /* Dither */ + success = TRUE; + bufu8r = bufu8g = bufu8b = NULL; + buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; + bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || + !buf1b || !buf2r || !buf2g || !buf2b) { + L_ERROR("buffer not made\n", procName); + success = FALSE; + goto buffer_cleanup; + } + + /* Start by priming buf2; line 1 is above line 2 */ + pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + for (i = 0; i < h - 1; i++) { + /* Swap data 2 --> 1, and read in new line 2 */ + memcpy(buf1r, buf2r, 4 * w); + memcpy(buf1g, buf2g, 4 * w); + memcpy(buf1b, buf2b, 4 * w); + pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + /* Dither */ + lined = datad + i * wpld; + for (j = 0; j < w - 1; j++) { + rval = buf1r[j] / 64; + gval = buf1g[j] / 64; + bval = buf1b[j] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); + SET_DATA_BYTE(lined, j, index); + + dif = buf1r[j] / 8 - 8 * rc; + if (dif != 0) { + val1 = buf1r[j + 1] + 3 * dif; + val2 = buf2r[j] + 3 * dif; + val3 = buf2r[j + 1] + 2 * dif; + if (dif > 0) { + buf1r[j + 1] = L_MIN(16383, val1); + buf2r[j] = L_MIN(16383, val2); + buf2r[j + 1] = L_MIN(16383, val3); + } else { + buf1r[j + 1] = L_MAX(0, val1); + buf2r[j] = L_MAX(0, val2); + buf2r[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1g[j] / 8 - 8 * gc; + if (dif != 0) { + val1 = buf1g[j + 1] + 3 * dif; + val2 = buf2g[j] + 3 * dif; + val3 = buf2g[j + 1] + 2 * dif; + if (dif > 0) { + buf1g[j + 1] = L_MIN(16383, val1); + buf2g[j] = L_MIN(16383, val2); + buf2g[j + 1] = L_MIN(16383, val3); + } else { + buf1g[j + 1] = L_MAX(0, val1); + buf2g[j] = L_MAX(0, val2); + buf2g[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1b[j] / 8 - 8 * bc; + if (dif != 0) { + val1 = buf1b[j + 1] + 3 * dif; + val2 = buf2b[j] + 3 * dif; + val3 = buf2b[j + 1] + 2 * dif; + if (dif > 0) { + buf1b[j + 1] = L_MIN(16383, val1); + buf2b[j] = L_MIN(16383, val2); + buf2b[j + 1] = L_MIN(16383, val3); + } else { + buf1b[j + 1] = L_MAX(0, val1); + buf2b[j] = L_MAX(0, val2); + buf2b[j + 1] = L_MAX(0, val3); + } + } + } + + /* Get last pixel in row; no downward propagation */ + rval = buf1r[w - 1] / 64; + gval = buf1g[w - 1] / 64; + bval = buf1b[w - 1] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); + SET_DATA_BYTE(lined, w - 1, index); + } + + /* Get last row of pixels; no leftward propagation */ + lined = datad + (h - 1) * wpld; + for (j = 0; j < w; j++) { + rval = buf2r[j] / 64; + gval = buf2g[j] / 64; + bval = buf2b[j] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); + SET_DATA_BYTE(lined, j, index); + } + +buffer_cleanup: + LEPT_FREE(bufu8r); + LEPT_FREE(bufu8g); + LEPT_FREE(bufu8b); + LEPT_FREE(buf1r); + LEPT_FREE(buf1g); + LEPT_FREE(buf1b); + LEPT_FREE(buf2r); + LEPT_FREE(buf2g); + LEPT_FREE(buf2b); + if (!success) pixDestroy(&pixd); + } + + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*! + * \brief octreeFindColorCell() + * + * \param[in] octindex + * \param[in] cqcaa + * \param[out] pindex index of CTE; returned to set pixel value + * \param[out] prval of CTE + * \param[out] pgval of CTE + * \param[out] pbval of CTE + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) As this is in inner loop, we don't check input pointers!
+ *      (2) This traverses from the root (well, actually from level 2,
+ *          because the level 2 cubes are the largest CTE cubes),
+ *          and finds the index number of the cell and the color values,
+ *          which can be used either directly or in a (Floyd-Steinberg)
+ *          error-diffusion dithering algorithm.
+ * 
+ */ +static l_int32 +octreeFindColorCell(l_int32 octindex, + CQCELL ***cqcaa, + l_int32 *pindex, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 level; +l_int32 baseindex, subindex; +CQCELL *cqc, *cqcsub; + + /* Use rgb values stored in the cubes; a little faster */ + for (level = 2; level < CqNLevels; level++) { + getOctcubeIndices(octindex, level, &baseindex, &subindex); + cqc = cqcaa[level][baseindex]; + cqcsub = cqcaa[level + 1][subindex]; + if (cqcsub->bleaf == 0) { /* use cell at level above */ + *pindex = cqc->index; + *prval = cqc->rc; + *pgval = cqc->gc; + *pbval = cqc->bc; + break; + } else if (level == CqNLevels - 1) { /* reached the bottom */ + *pindex = cqcsub->index; + *prval = cqcsub->rc; + *pgval = cqcsub->gc; + *pbval = cqcsub->bc; + break; + } + } + +#if 0 + /* Generate rgb values for each cube on the fly; slower */ + for (level = 2; level < CqNLevels; level++) { + l_int32 rv, gv, bv; + getOctcubeIndices(octindex, level, &baseindex, &subindex); + cqc = cqcaa[level][baseindex]; + cqcsub = cqcaa[level + 1][subindex]; + if (cqcsub->bleaf == 0) { /* use cell at level above */ + getRGBFromOctcube(baseindex, level, &rv, &gv, &bv); + *pindex = cqc->index; + *prval = rv; + *pgval = gv; + *pbval = bv; + break; + } else if (level == CqNLevels - 1) { /* reached the bottom */ + getRGBFromOctcube(subindex, level + 1, &rv, &gv, &bv); + *pindex = cqcsub->index; + *prval = rv; + *pgval = gv; + *pbval = bv; + break; + } + } +#endif + + return 0; +} + + + +/*------------------------------------------------------------------* + * Helper cqcell functions * + *------------------------------------------------------------------*/ +/*! + * \brief cqcellTreeCreate() + * + * \return cqcell array tree + */ +static CQCELL *** +cqcellTreeCreate(void) +{ +l_int32 level, ncells, i; +CQCELL ***cqcaa; +CQCELL **cqca; /* one array for each octree level */ + + PROCNAME("cqcellTreeCreate"); + + /* Make array of accumulation cell arrays from levels 1 to 5 */ + if ((cqcaa = (CQCELL ***)LEPT_CALLOC(CqNLevels + 1, sizeof(CQCELL **))) + == NULL) + return (CQCELL ***)ERROR_PTR("cqcaa not made", procName, NULL); + for (level = 0; level <= CqNLevels; level++) { + ncells = 1 << (3 * level); + if ((cqca = (CQCELL **)LEPT_CALLOC(ncells, sizeof(CQCELL *))) == NULL) { + cqcellTreeDestroy(&cqcaa); + return (CQCELL ***)ERROR_PTR("cqca not made", procName, NULL); + } + cqcaa[level] = cqca; + for (i = 0; i < ncells; i++) { + if ((cqca[i] = (CQCELL *)LEPT_CALLOC(1, sizeof(CQCELL))) == NULL) { + cqcellTreeDestroy(&cqcaa); + return (CQCELL ***)ERROR_PTR("cqc not made", procName, NULL); + } + } + } + + return cqcaa; +} + + +/*! + * \brief cqcellTreeDestroy() + * + * \param[in,out] pcqcaa will be set to null before returning + */ +static void +cqcellTreeDestroy(CQCELL ****pcqcaa) +{ +l_int32 level, ncells, i; +CQCELL ***cqcaa; +CQCELL **cqca; + + PROCNAME("cqcellTreeDestroy"); + + if (pcqcaa == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + + if ((cqcaa = *pcqcaa) == NULL) + return; + + for (level = 0; level <= CqNLevels; level++) { + cqca = cqcaa[level]; + ncells = 1 << (3 * level); + for (i = 0; i < ncells; i++) + LEPT_FREE(cqca[i]); + LEPT_FREE(cqca); + } + LEPT_FREE(cqcaa); + *pcqcaa = NULL; + + return; +} + + + +/*------------------------------------------------------------------* + * Helper index functions * + *------------------------------------------------------------------*/ +/*! + * \brief makeRGBToIndexTables() + * + * \param[in] cqlevels can be 1, 2, 3, 4, 5 or 6 + * \param[out] prtab, pgtab, pbtab tables + * \return 0 if OK; 1 on error + * + *
+ *  Set up tables.  e.g., for cqlevels = 5, we need an integer 0 < i < 2^15:
+ *      rtab = 0  i7  0   0  i6  0   0  i5  0   0   i4  0   0   i3  0   0
+ *      gtab = 0  0   i7  0   0  i6  0   0  i5  0   0   i4  0   0   i3  0
+ *      btab = 0  0   0   i7  0  0   i6  0  0   i5  0   0   i4  0   0   i3
+ *
+ *  The tables are then used to map from rbg --> index as follows:
+ *      index = 0  r7  g7  b7  r6  g6  b6  r5  g5  b5  r4  g4  b4  r3  g3  b3
+ *
+ *    e.g., for cqlevels = 4, we map to
+ *      index = 0  0   0   0   r7  g7  b7  r6  g6  b6  r5  g5  b5  r4  g4  b4
+ *
+ *  This may look a bit strange.  The notation 'r7' means the MSBit of
+ *  the r value which has 8 bits, going down from r7 to r0.
+ *  Keep in mind that r7 is actually the r component bit for level 1 of
+ *  the octtree.  Level 1 is composed of 8 octcubes, represented by
+ *  the bits r7 g7 b7, which divide the entire color space into
+ *  8 cubes.  At level 2, each of these 8 octcubes is further divided into
+ *  8 cubes, each labeled by the second most significant bits r6 g6 b6
+ *  of the rgb color.
+ * 
+ */ +l_ok +makeRGBToIndexTables(l_int32 cqlevels, + l_uint32 **prtab, + l_uint32 **pgtab, + l_uint32 **pbtab) +{ +l_int32 i; +l_uint32 *rtab, *gtab, *btab; + + PROCNAME("makeRGBToIndexTables"); + + if (cqlevels < 1 || cqlevels > 6) + return ERROR_INT("cqlevels must be in {1,...6}", procName, 1); + if (!prtab || !pgtab || !pbtab) + return ERROR_INT("not all &tabs defined", procName, 1); + + rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + if (!rtab || !gtab || !btab) + return ERROR_INT("calloc fail for tab", procName, 1); + *prtab = rtab; + *pgtab = gtab; + *pbtab = btab; + + switch (cqlevels) + { + case 1: + for (i = 0; i < 256; i++) { + rtab[i] = (i >> 5) & 0x0004; + gtab[i] = (i >> 6) & 0x0002; + btab[i] = (i >> 7); + } + break; + case 2: + for (i = 0; i < 256; i++) { + rtab[i] = ((i >> 2) & 0x0020) | ((i >> 4) & 0x0004); + gtab[i] = ((i >> 3) & 0x0010) | ((i >> 5) & 0x0002); + btab[i] = ((i >> 4) & 0x0008) | ((i >> 6) & 0x0001); + } + break; + case 3: + for (i = 0; i < 256; i++) { + rtab[i] = ((i << 1) & 0x0100) | ((i >> 1) & 0x0020) | + ((i >> 3) & 0x0004); + gtab[i] = (i & 0x0080) | ((i >> 2) & 0x0010) | + ((i >> 4) & 0x0002); + btab[i] = ((i >> 1) & 0x0040) | ((i >> 3) & 0x0008) | + ((i >> 5) & 0x0001); + } + break; + case 4: + for (i = 0; i < 256; i++) { + rtab[i] = ((i << 4) & 0x0800) | ((i << 2) & 0x0100) | + (i & 0x0020) | ((i >> 2) & 0x0004); + gtab[i] = ((i << 3) & 0x0400) | ((i << 1) & 0x0080) | + ((i >> 1) & 0x0010) | ((i >> 3) & 0x0002); + btab[i] = ((i << 2) & 0x0200) | (i & 0x0040) | + ((i >> 2) & 0x0008) | ((i >> 4) & 0x0001); + } + break; + case 5: + for (i = 0; i < 256; i++) { + rtab[i] = ((i << 7) & 0x4000) | ((i << 5) & 0x0800) | + ((i << 3) & 0x0100) | ((i << 1) & 0x0020) | + ((i >> 1) & 0x0004); + gtab[i] = ((i << 6) & 0x2000) | ((i << 4) & 0x0400) | + ((i << 2) & 0x0080) | (i & 0x0010) | + ((i >> 2) & 0x0002); + btab[i] = ((i << 5) & 0x1000) | ((i << 3) & 0x0200) | + ((i << 1) & 0x0040) | ((i >> 1) & 0x0008) | + ((i >> 3) & 0x0001); + } + break; + case 6: + for (i = 0; i < 256; i++) { + rtab[i] = ((i << 10) & 0x20000) | ((i << 8) & 0x4000) | + ((i << 6) & 0x0800) | ((i << 4) & 0x0100) | + ((i << 2) & 0x0020) | (i & 0x0004); + gtab[i] = ((i << 9) & 0x10000) | ((i << 7) & 0x2000) | + ((i << 5) & 0x0400) | ((i << 3) & 0x0080) | + ((i << 1) & 0x0010) | ((i >> 1) & 0x0002); + btab[i] = ((i << 8) & 0x8000) | ((i << 6) & 0x1000) | + ((i << 4) & 0x0200) | ((i << 2) & 0x0040) | + (i & 0x0008) | ((i >> 2) & 0x0001); + } + break; + default: + ERROR_INT("cqlevels not in [1...6]", procName, 1); + break; + } + + return 0; +} + + +/*! + * \brief getOctcubeIndexFromRGB() + * + * \param[in] rval, gval, bval + * \param[in] rtab, gtab, btab generated with makeRGBToIndexTables() + * \param[out] pindex found index + * \return void + * + *
+ * Notes:
+ *      No error checking!
+ * 
+ */ +void +getOctcubeIndexFromRGB(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_uint32 *rtab, + l_uint32 *gtab, + l_uint32 *btab, + l_uint32 *pindex) +{ + *pindex = rtab[rval] | gtab[gval] | btab[bval]; + return; +} + + +/*! + * \brief getRGBFromOctcube() + * + * \param[in] cubeindex octcube index + * \param[in] level at which index is expressed + * \param[out] prval r val of this cube + * \param[out] pgval g val of this cube + * \param[out] pbval b val of this cube + * \return void + * + *
+ * Notes:
+ *      (1) We can consider all octcube indices to represent a
+ *          specific point in color space: namely, the location
+ *          of the 'upper-left' corner of the cube, where indices
+ *          increase down and to the right.  The upper left corner
+ *          of the color space is then 00000....
+ *      (2) The 'rgbindex' is a 24-bit representation of the location,
+ *          in octcube notation, at the center of the octcube.
+ *          To get to the center of an octcube, you choose the 111
+ *          octcube at the next lower level.
+ *      (3) For example, if the octcube index = 110101 (binary),
+ *          which is a level 2 expression, then the rgbindex
+ *          is the 24-bit representation of 110101111 (at level 3);
+ *          namely, 000110101111000000000000.  The number is padded
+ *          with 3 leading 0s (because the representation uses
+ *          only 21 bits) and 12 trailing 0s (the default for
+ *          levels 4-7, which are contained within each of the level3
+ *          octcubes.  Then the rgb values for the center of the
+ *          octcube are: rval = 11100000, gval = 10100000, bval = 01100000
+ * 
+ */ +static void +getRGBFromOctcube(l_int32 cubeindex, + l_int32 level, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 rgbindex; + + /* Bring to format in 21 bits: (r7 g7 b7 r6 g6 b6 ...) */ + /* This is valid for levels from 0 to 6 */ + rgbindex = cubeindex << (3 * (7 - level)); /* upper corner of cube */ + rgbindex |= (0x7 << (3 * (6 - level))); /* index to center of cube */ + + /* Extract separate pieces */ + *prval = ((rgbindex >> 13) & 0x80) | + ((rgbindex >> 11) & 0x40) | + ((rgbindex >> 9) & 0x20) | + ((rgbindex >> 7) & 0x10) | + ((rgbindex >> 5) & 0x08) | + ((rgbindex >> 3) & 0x04) | + ((rgbindex >> 1) & 0x02); + *pgval = ((rgbindex >> 12) & 0x80) | + ((rgbindex >> 10) & 0x40) | + ((rgbindex >> 8) & 0x20) | + ((rgbindex >> 6) & 0x10) | + ((rgbindex >> 4) & 0x08) | + ((rgbindex >> 2) & 0x04) | + (rgbindex & 0x02); + *pbval = ((rgbindex >> 11) & 0x80) | + ((rgbindex >> 9) & 0x40) | + ((rgbindex >> 7) & 0x20) | + ((rgbindex >> 5) & 0x10) | + ((rgbindex >> 3) & 0x08) | + ((rgbindex >> 1) & 0x04) | + ((rgbindex << 1) & 0x02); + + return; +} + + +/*! + * \brief getOctcubeIndices() + * + * \param[in] rgbindex + * \param[in] level octree level 0, 1, 2, 3, 4, 5 + * \param[out] pbindex base index index at the octree level + * \param[out] psindex sub index index at the next lower level + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *  for CqNLevels = 6, the full RGB index is in the form:
+ *     index = (0[13] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3 r2 g2 b2)
+ *  for CqNLevels = 5, the full RGB index is in the form:
+ *     index = (0[16] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3)
+ *  for CqNLevels = 4, the full RGB index is in the form:
+ *     index = (0[19] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4)
+ *
+ *  The base index is the index of the octcube at the level given,
+ *  whereas the sub index is the index at the next level down.
+ *
+ *  For level 0: base index = 0
+ *               sub index is the 3 bit number (r7 g7 b7)
+ *  For level 1: base index = (r7 g7 b7)
+ *               sub index = (r7 g7 b7 r6 g6 b6)
+ *  For level 2: base index = (r7 g7 b7 r6 g6 b6)
+ *               sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5)
+ *  For level 3: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5)
+ *               sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4)
+ *  For level 4: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4)
+ *               sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3)
+ *  For level 5: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3)
+ *               sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3
+ *                            r2 g2 b2)
+ * 
+ */ +static l_int32 +getOctcubeIndices(l_int32 rgbindex, + l_int32 level, + l_int32 *pbindex, + l_int32 *psindex) +{ + PROCNAME("getOctcubeIndex"); + + if (level < 0 || level > CqNLevels - 1) + return ERROR_INT("level must be in e.g., [0 ... 5]", procName, 1); + if (!pbindex) + return ERROR_INT("&bindex not defined", procName, 1); + if (!psindex) + return ERROR_INT("&sindex not defined", procName, 1); + + *pbindex = rgbindex >> (3 * (CqNLevels - level)); + *psindex = rgbindex >> (3 * (CqNLevels - 1 - level)); + return 0; +} + + +/*! + * \brief octcubeGetCount() + * + * \param[in] level valid values are in [1,...6]; there are 2^level + * cubes along each side of the rgb cube + * \param[out] psize 2^(3 * level) cubes in the entire rgb cube + * \return 0 if OK, 1 on error. Caller must check! + * + *
+ *     level:   1        2        3        4        5        6
+ *     size:    8       64       512     4098     32784   262272
+ * 
+ */ +static l_int32 +octcubeGetCount(l_int32 level, + l_int32 *psize) +{ + PROCNAME("octcubeGetCount"); + + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (level < 1 || level > 6) + return ERROR_INT("invalid level", procName, 1); + + *psize = 1 << (3 * level); + return 0; +} + + +/*---------------------------------------------------------------------------* + * Adaptive octree quantization based on population at a fixed level * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixOctreeQuantByPopulation() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level significant bits for each of RGB; valid for {3,4}. + * Use 0 for default (level 4; recommended + * \param[in] ditherflag 1 to dither, 0 otherwise + * \return pixd quantized to octcubes or NULL on error + * + *
+ * Notes:
+ *      (1) This color quantization method works very well without
+ *          dithering, using octcubes at two different levels:
+ *            (a) the input %level, which is either 3 or 4
+ *            (b) level 2 (64 octcubes to cover the entire color space)
+ *      (2) For best results, using %level = 4 is recommended.
+ *          Why do we provide an option for using level 3?  Because
+ *          there are 512 octcubes at level 3, and for many images
+ *          not more than 256 are filled.  As a result, on some images
+ *          a very accurate quantized representation is possible using
+ *          %level = 3.
+ *      (3) This first breaks up the color space into octcubes at the
+ *          input %level, and computes, for each octcube, the average
+ *          value of the pixels that are in it.
+ *      (4) Then there are two possible situations:
+ *            (a) If there are not more than 256 populated octcubes,
+ *                it returns a cmapped pix with those values assigned.
+ *            (b) Otherwise, it selects 192 octcubes containing the largest
+ *                number of pixels and quantizes pixels within those octcubes
+ *                to their average.  Then, to handle the residual pixels
+ *                that are not in those 192 octcubes, it generates a
+ *                level 2 octree consisting of 64 octcubes, and within
+ *                each octcube it quantizes the residual pixels to their
+ *                average within each of those level 2 octcubes.
+ *      (5) Unpopulated level 2 octcubes are represented in the colormap
+ *          by their centers.  This, of course, has no effect unless
+ *          dithering is used for the output image.
+ *      (6) The depth of pixd is the minimum required to support the
+ *          number of colors found at %level; namely, 2, 4 or 8.
+ *      (7) This function works particularly well on images such as maps,
+ *          where there are a relatively small number of well-populated
+ *          colors, but due to antialiasing and compression artifacts
+ *          there may be a large number of different colors.  This will
+ *          pull out and represent accurately the highly populated colors,
+ *          while still making a reasonable approximation for the others.
+ *      (8) The highest level of octcubes allowed is 4.  Use of higher
+ *          levels typically results in having a small fraction of
+ *          pixels in the most populated 192 octcubes.  As a result,
+ *          most of the pixels are represented at level 2, which is
+ *          not sufficiently accurate.
+ *      (9) Dithering shows artifacts on some images.  If you plan to
+ *          dither, pixOctreeColorQuant() and pixFixedOctcubeQuant256()
+ *          usually give better results.
+ * 
+ */ +PIX * +pixOctreeQuantByPopulation(PIX *pixs, + l_int32 level, + l_int32 ditherflag) +{ +l_int32 w, h, wpls, wpld, i, j, depth, size, ncolors, index; +l_int32 rval, gval, bval; +l_int32 *rarray, *garray, *barray, *narray, *iarray; +l_uint32 octindex, octindex2; +l_uint32 *rtab, *gtab, *btab, *rtab2, *gtab2, *btab2; +l_uint32 *lines, *lined, *datas, *datad; +L_OCTCUBE_POP *opop; +L_HEAP *lh; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixOctreeQuantByPopulation"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (level == 0) level = 4; + if (level < 3 || level > 4) + return (PIX *)ERROR_PTR("level not in {3,4}", procName, NULL); + + /* Do not dither if image is very small */ + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { + L_INFO("Small image: dithering turned off\n", procName); + ditherflag = 0; + } + + if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ + return (PIX *)ERROR_PTR("size not returned", procName, NULL); + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + + pixd = NULL; + narray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + if (!narray || !rarray || !garray || !barray) + goto array_cleanup; + + /* Place the pixels in octcube leaves. */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + narray[octindex]++; + rarray[octindex] += rval; + garray[octindex] += gval; + barray[octindex] += bval; + } + } + + /* Find the number of different colors */ + for (i = 0, ncolors = 0; i < size; i++) { + if (narray[i] > 0) + ncolors++; + } + if (ncolors <= 4) + depth = 2; + else if (ncolors <= 16) + depth = 4; + else + depth = 8; + pixd = pixCreate(w, h, depth); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + cmap = pixcmapCreate(depth); + pixSetColormap(pixd, cmap); + + /* Average the colors in each octcube leaf. */ + for (i = 0; i < size; i++) { + if (narray[i] > 0) { + rarray[i] /= narray[i]; + garray[i] /= narray[i]; + barray[i] /= narray[i]; + } + } + + /* If ncolors <= 256, finish immediately. Do not dither. + * Re-use narray to hold the colormap index + 1 */ + if (ncolors <= 256) { + for (i = 0, index = 0; i < size; i++) { + if (narray[i] > 0) { + pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); + narray[i] = index + 1; /* to avoid storing 0 */ + index++; + } + } + + /* Set the cmap indices for each pixel */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + switch (depth) + { + case 8: + SET_DATA_BYTE(lined, j, narray[octindex] - 1); + break; + case 4: + SET_DATA_QBIT(lined, j, narray[octindex] - 1); + break; + case 2: + SET_DATA_DIBIT(lined, j, narray[octindex] - 1); + break; + default: + L_WARNING("shouldn't get here\n", procName); + } + } + } + goto array_cleanup; + } + + /* More complicated. Sort by decreasing population */ + lh = lheapCreate(500, L_SORT_DECREASING); + for (i = 0; i < size; i++) { + if (narray[i] > 0) { + opop = (L_OCTCUBE_POP *)LEPT_CALLOC(1, sizeof(L_OCTCUBE_POP)); + opop->npix = (l_float32)narray[i]; + opop->index = i; + opop->rval = rarray[i]; + opop->gval = garray[i]; + opop->bval = barray[i]; + lheapAdd(lh, opop); + } + } + + /* Take the top 192. These will form the first 192 colors + * in the cmap. iarray[i] holds the index into the cmap. */ + iarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + for (i = 0; i < 192; i++) { + opop = (L_OCTCUBE_POP*)lheapRemove(lh); + if (!opop) break; + pixcmapAddColor(cmap, opop->rval, opop->gval, opop->bval); + iarray[opop->index] = i + 1; /* +1 to avoid storing 0 */ + +#if DEBUG_POP + fprintf(stderr, "i = %d, n = %6.0f, (r,g,b) = (%d %d %d)\n", + i, opop->npix, opop->rval, opop->gval, opop->bval); +#endif /* DEBUG_POP */ + + LEPT_FREE(opop); + } + + /* Make the octindex tables for level 2, and reuse rarray, etc. */ + rtab2 = gtab2 = btab2 = NULL; + makeRGBToIndexTables(2, &rtab2, >ab2, &btab2); + for (i = 0; i < 64; i++) { + narray[i] = 0; + rarray[i] = 0; + garray[i] = 0; + barray[i] = 0; + } + + /* Take the rest of the occupied octcubes, assigning the pixels + * to these new colormap indices. iarray[] is addressed + * by %level octcube indices, and it now holds the + * colormap indices for all pixels in pixs. */ + for (i = 192; i < size; i++) { + opop = (L_OCTCUBE_POP*)lheapRemove(lh); + if (!opop) break; + rval = opop->rval; + gval = opop->gval; + bval = opop->bval; + octindex2 = rtab2[rval] | gtab2[gval] | btab2[bval]; + narray[octindex2] += (l_int32)opop->npix; + rarray[octindex2] += (l_int32)opop->npix * rval; + garray[octindex2] += (l_int32)opop->npix * gval; + barray[octindex2] += (l_int32)opop->npix * bval; + iarray[opop->index] = 192 + octindex2 + 1; /* +1 to avoid storing 0 */ + LEPT_FREE(opop); + } + lheapDestroy(&lh, TRUE); + + /* To span the full color space, which is necessary for dithering, + * set each iarray element whose value is still 0 at the input + * level octcube leaves (because there were no pixels in those + * octcubes) to the colormap index corresponding to its level 2 + * octcube. */ + if (ditherflag) { + for (i = 0; i < size; i++) { + if (iarray[i] == 0) { + getRGBFromOctcube(i, level, &rval, &gval, &bval); + octindex2 = rtab2[rval] | gtab2[gval] | btab2[bval]; + iarray[i] = 192 + octindex2 + 1; + } + } + } + LEPT_FREE(rtab2); + LEPT_FREE(gtab2); + LEPT_FREE(btab2); + + /* Average the colors from the residuals in each level 2 octcube, + * and add these 64 values to the colormap. */ + for (i = 0; i < 64; i++) { + if (narray[i] > 0) { + rarray[i] /= narray[i]; + garray[i] /= narray[i]; + barray[i] /= narray[i]; + } else { /* no pixels in this octcube; use center value */ + getRGBFromOctcube(i, 2, &rarray[i], &garray[i], &barray[i]); + } + pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); + } + + /* Set the cmap indices for each pixel. Subtract 1 from + * the value in iarray[] because we added 1 earlier. */ + if (ditherflag == 0) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + SET_DATA_BYTE(lined, j, iarray[octindex] - 1); + } + } + } else { /* dither */ + pixDitherOctindexWithCmap(pixs, pixd, rtab, gtab, btab, + iarray, POP_DIF_CAP); + } + +#if DEBUG_POP + for (i = 0; i < size / 16; i++) { + l_int32 j; + for (j = 0; j < 16; j++) + fprintf(stderr, "%d ", iarray[16 * i + j]); + fprintf(stderr, "\n"); + } +#endif /* DEBUG_POP */ + + LEPT_FREE(iarray); + +array_cleanup: + LEPT_FREE(narray); + LEPT_FREE(rarray); + LEPT_FREE(garray); + LEPT_FREE(barray); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + + return pixd; +} + + +/*! + * \brief pixDitherOctindexWithCmap() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixd 8 bpp cmapped + * \param[in] rtab, gtab, btab tables from rval to octindex + * \param[in] indexmap array mapping octindex to cmap index + * \param[in] difcap max allowed dither transfer; + * use 0 for infinite cap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This performs dithering to generate the colormap indices
+ *          in pixd.  The colormap has been calculated, along with
+ *          four input LUTs that together give the inverse colormapping
+ *          from RGB to colormap index.
+ *      (2) For pixOctreeQuantByPopulation(), %indexmap maps from the
+ *          standard octindex to colormap index (after subtracting 1).
+ *          The basic pixel-level function, without dithering, is:
+ *             extractRGBValues(lines[j], &rval, &gval, &bval);
+ *             octindex = rtab[rval] | gtab[gval] | btab[bval];
+ *             SET_DATA_BYTE(lined, j, indexmap[octindex] - 1);
+ *      (3) This can be used in any situation where the general
+ *          prescription for finding the colormap index from the rgb
+ *          value is precisely this:
+ *             cmapindex = indexmap[rtab[rval] | gtab[gval] | btab[bval]] - 1
+ *          For example, in pixFixedOctcubeQuant256(), we don't use
+ *          standard octcube indexing, the rtab (etc) LUTs map directly
+ *          to the colormap index, and %indexmap just compensates for
+ *          the 1-off indexing assumed to be in that table.
+ * 
+ */ +static l_int32 +pixDitherOctindexWithCmap(PIX *pixs, + PIX *pixd, + l_uint32 *rtab, + l_uint32 *gtab, + l_uint32 *btab, + l_int32 *indexmap, + l_int32 difcap) +{ +l_uint8 *bufu8r, *bufu8g, *bufu8b; +l_int32 i, j, w, h, wpld, octindex, cmapindex, success; +l_int32 rval, gval, bval, rc, gc, bc; +l_int32 dif, val1, val2, val3; +l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; +l_uint32 *datad, *lined; +PIXCMAP *cmap; + + PROCNAME("pixDitherOctindexWithCmap"); + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); + if (!pixd || pixGetDepth(pixd) != 8) + return ERROR_INT("pixd undefined or not 8 bpp", procName, 1); + if ((cmap = pixGetColormap(pixd)) == NULL) + return ERROR_INT("pixd not cmapped", procName, 1); + if (!rtab || !gtab || !btab || !indexmap) + return ERROR_INT("not all 4 tables defined", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixGetWidth(pixd) != w || pixGetHeight(pixd) != h) + return ERROR_INT("pixs and pixd not same size", procName, 1); + + success = TRUE; + bufu8r = bufu8g = bufu8b = NULL; + buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; + bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || + !buf1b || !buf2r || !buf2g || !buf2b) { + L_ERROR("buffer not made\n", procName); + success = FALSE; + goto buffer_cleanup; + } + + /* Start by priming buf2; line 1 is above line 2 */ + pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h - 1; i++) { + /* Swap data 2 --> 1, and read in new line 2 */ + memcpy(buf1r, buf2r, 4 * w); + memcpy(buf1g, buf2g, 4 * w); + memcpy(buf1b, buf2b, 4 * w); + pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + /* Dither */ + lined = datad + i * wpld; + for (j = 0; j < w - 1; j++) { + rval = buf1r[j] / 64; + gval = buf1g[j] / 64; + bval = buf1b[j] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + cmapindex = indexmap[octindex] - 1; + SET_DATA_BYTE(lined, j, cmapindex); + pixcmapGetColor(cmap, cmapindex, &rc, &gc, &bc); + + dif = buf1r[j] / 8 - 8 * rc; + if (difcap > 0) { + if (dif > difcap) dif = difcap; + if (dif < -difcap) dif = -difcap; + } + if (dif != 0) { + val1 = buf1r[j + 1] + 3 * dif; + val2 = buf2r[j] + 3 * dif; + val3 = buf2r[j + 1] + 2 * dif; + if (dif > 0) { + buf1r[j + 1] = L_MIN(16383, val1); + buf2r[j] = L_MIN(16383, val2); + buf2r[j + 1] = L_MIN(16383, val3); + } else { + buf1r[j + 1] = L_MAX(0, val1); + buf2r[j] = L_MAX(0, val2); + buf2r[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1g[j] / 8 - 8 * gc; + if (difcap > 0) { + if (dif > difcap) dif = difcap; + if (dif < -difcap) dif = -difcap; + } + if (dif != 0) { + val1 = buf1g[j + 1] + 3 * dif; + val2 = buf2g[j] + 3 * dif; + val3 = buf2g[j + 1] + 2 * dif; + if (dif > 0) { + buf1g[j + 1] = L_MIN(16383, val1); + buf2g[j] = L_MIN(16383, val2); + buf2g[j + 1] = L_MIN(16383, val3); + } else { + buf1g[j + 1] = L_MAX(0, val1); + buf2g[j] = L_MAX(0, val2); + buf2g[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1b[j] / 8 - 8 * bc; + if (difcap > 0) { + if (dif > difcap) dif = difcap; + if (dif < -difcap) dif = -difcap; + } + if (dif != 0) { + val1 = buf1b[j + 1] + 3 * dif; + val2 = buf2b[j] + 3 * dif; + val3 = buf2b[j + 1] + 2 * dif; + if (dif > 0) { + buf1b[j + 1] = L_MIN(16383, val1); + buf2b[j] = L_MIN(16383, val2); + buf2b[j + 1] = L_MIN(16383, val3); + } else { + buf1b[j + 1] = L_MAX(0, val1); + buf2b[j] = L_MAX(0, val2); + buf2b[j + 1] = L_MAX(0, val3); + } + } + } + + /* Get last pixel in row; no downward propagation */ + rval = buf1r[w - 1] / 64; + gval = buf1g[w - 1] / 64; + bval = buf1b[w - 1] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + cmapindex = indexmap[octindex] - 1; + SET_DATA_BYTE(lined, w - 1, cmapindex); + } + + /* Get last row of pixels; no leftward propagation */ + lined = datad + (h - 1) * wpld; + for (j = 0; j < w; j++) { + rval = buf2r[j] / 64; + gval = buf2g[j] / 64; + bval = buf2b[j] / 64; + octindex = rtab[rval] | gtab[gval] | btab[bval]; + cmapindex = indexmap[octindex] - 1; + SET_DATA_BYTE(lined, j, cmapindex); + } + +buffer_cleanup: + LEPT_FREE(bufu8r); + LEPT_FREE(bufu8g); + LEPT_FREE(bufu8b); + LEPT_FREE(buf1r); + LEPT_FREE(buf1g); + LEPT_FREE(buf1b); + LEPT_FREE(buf2r); + LEPT_FREE(buf2g); + LEPT_FREE(buf2b); + + return (success) ? 0 : 1; +} + + +/*---------------------------------------------------------------------------* + * Adaptive octree quantization to 4 and 8 bpp with max colors * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixOctreeQuantNumColors() + * + * \param[in] pixs 32 bpp rgb + * \param[in] maxcolors 8 to 256; the actual number of colors used + * may be less than this + * \param[in] subsample factor for computing color distribution; + * use 0 for default + * \return pixd 4 or 8 bpp, colormapped, or NULL on error + * + *
+ *  pixOctreeColorQuant is very flexible in terms of the relative
+ *  depth of different cubes of the octree.   By contrast, this function,
+ *  pixOctreeQuantNumColors is also adaptive, but it supports octcube
+ *  leaves at only two depths: a smaller depth that guarantees
+ *  full coverage of the color space and octcubes at one level
+ *  deeper for more accurate colors.  Its main virutes are simplicity
+ *  and speed, which are both derived from the natural indexing of
+ *  the octcubes from the RGB values.
+ *
+ *  Before describing pixOctreeQuantNumColors, consider an even simpler
+ *  approach for 4 bpp with either 8 or 16 colors.  With 8 colors,
+ *  you simply go to level 1 octcubes and use the average color
+ *  found in each cube.  For 16 colors, you find which of the three
+ *  colors has the largest variance at the second level, and use two
+ *  indices for that color.  The result is quite poor, because 1 some
+ *  of the cubes are nearly empty and 2 you don't get much color
+ *  differentiation for the extra 8 colors.  Trust me, this method may
+ *  be simple, but it isn't worth anything.
+ *
+ *  In pixOctreeQuantNumColors, we generate colormapped images at
+ *  either 4 bpp or 8 bpp.  For 4 bpp, we have a minimum of 8 colors
+ *  for the level 1 octcubes, plus up to 8 additional colors that
+ *  are determined from the level 2 popularity.  If the number of colors
+ *  is between 8 and 16, the output is a 4 bpp image.  If the number of
+ *  colors is greater than 16, the output is a 8 bpp image.
+ *
+ *  We use a priority queue, implemented with a heap, to select the
+ *  requisite number of most populated octcubes at the deepest level
+ *  level 2 for 64 or fewer colors; level 3 for more than 64 colors.
+ *  These are combined with one color for each octcube one level above,
+ *  which is used to span the color space of octcubes that were not
+ *  included at the deeper level.
+ *
+ *  If the deepest level is 2, we combine the popular level 2 octcubes
+ *  out of a total of 64 with the 8 level 1 octcubes.  If the deepest
+ *  level is 3, we combine the popular level 3 octcubes out of a
+ *  total 512 with the 64 level 2 octcubes that span the color space.
+ *  In the latter case, we require a minimum of 64 colors for the level 2
+ *  octcubes, plus up to 192 additional colors determined from level 3
+ *  popularity.
+ *
+ *  The parameter 'maxlevel' is the deepest octcube level that is used.
+ *  The implementation also uses two LUTs, which are employed in
+ *  two successive traversals of the dest image.  The first maps
+ *  from the src octindex at 'maxlevel' to the color table index,
+ *  which is the value that is stored in the 4 or 8 bpp dest pixel.
+ *  The second LUT maps from that colormap value in the dest to a
+ *  new colormap value for a minimum sized colormap, stored back in
+ *  the dest.  It is used to remove any color map entries that
+ *  correspond to color space regions that have no pixels in the
+ *  source image.  These regions can be either from the higher level
+ *  e.g., level 1 for 4 bpp, or from octcubes at 'maxlevel' that
+ *  are unoccupied.  This remapping results in the minimum number
+ *  of colors used according to the constraints induced by the
+ *  input 'maxcolors'.  We also compute the average R, G and B color
+ *  values in each region of the color space represented by a
+ *  colormap entry, and store them in the colormap.
+ *
+ *  The maximum number of colors is input, which determines the
+ *  following properties of the dest image and octcube regions used:
+ *
+ *     Number of colors      dest image depth      maxlevel
+ *     ----------------      ----------------      --------
+ *       8 to 16                  4 bpp               2
+ *       17 to 64                 8 bpp               2
+ *       65 to 256                8 bpp               3
+ *
+ *  It may turn out that the number of extra colors, beyond the
+ *  minimum 8 and 64 for maxlevel 2 and 3, respectively, is larger
+ *  than the actual number of occupied cubes at these levels
+ *  In that case, all the pixels are contained in this
+ *  subset of cubes at maxlevel, and no colormap colors are needed
+ *  to represent the remainder pixels one level above.  Thus, for
+ *  example, in use one often finds that the pixels in an image
+ *  occupy less than 192 octcubes at level 3, so they can be represented
+ *  by a colormap for octcubes at level 3 only.
+ * 
+ */ +PIX * +pixOctreeQuantNumColors(PIX *pixs, + l_int32 maxcolors, + l_int32 subsample) +{ +l_int32 w, h, minside, bpp, wpls, wpld, i, j, actualcolors; +l_int32 rval, gval, bval, nbase, nextra, maxlevel, ncubes, val; +l_int32 *lut1, *lut2; +l_uint32 index; +l_uint32 *lines, *lined, *datas, *datad, *pspixel; +l_uint32 *rtab, *gtab, *btab; +OQCELL *oqc; +OQCELL **oqca; +L_HEAP *lh; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixOctreeQuantNumColors"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (maxcolors < 8) { + L_WARNING("max colors < 8; setting to 8\n", procName); + maxcolors = 8; + } + if (maxcolors > 256) { + L_WARNING("max colors > 256; setting to 256\n", procName); + maxcolors = 256; + } + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + minside = L_MIN(w, h); + if (subsample <= 0) { + subsample = L_MAX(1, minside / 200); + } + + if (maxcolors <= 16) { + bpp = 4; + pixd = pixCreate(w, h, bpp); + maxlevel = 2; + ncubes = 64; /* 2^6 */ + nbase = 8; + nextra = maxcolors - nbase; + } else if (maxcolors <= 64) { + bpp = 8; + pixd = pixCreate(w, h, bpp); + maxlevel = 2; + ncubes = 64; /* 2^6 */ + nbase = 8; + nextra = maxcolors - nbase; + } else { /* maxcolors <= 256 */ + bpp = 8; + pixd = pixCreate(w, h, bpp); + maxlevel = 3; + ncubes = 512; /* 2^9 */ + nbase = 64; + nextra = maxcolors - nbase; + } + + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /*----------------------------------------------------------* + * If we're using the minimum number of colors, it is * + * much simpler. We just use 'nbase' octcubes. * + * For this case, we don't eliminate any extra colors. * + *----------------------------------------------------------*/ + if (nextra == 0) { + /* prepare the OctcubeQuantCell array */ + if ((oqca = (OQCELL **)LEPT_CALLOC(nbase, sizeof(OQCELL *))) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("oqca not made", procName, NULL); + } + for (i = 0; i < nbase; i++) { + oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); + oqca[i]->n = 0.0; + } + + rtab = gtab = btab = NULL; + makeRGBToIndexTables(maxlevel - 1, &rtab, >ab, &btab); + + /* Go through the entire image, gathering statistics and + * assigning pixels to their quantized value */ + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pspixel = lines + j; + extractRGBValues(*pspixel, &rval, &gval, &bval); + getOctcubeIndexFromRGB(rval, gval, bval, + rtab, gtab, btab, &index); +/* fprintf(stderr, "rval = %d, gval = %d, bval = %d," + " index = %d\n", rval, gval, bval, index); */ + if (bpp == 4) + SET_DATA_QBIT(lined, j, index); + else /* bpp == 8 */ + SET_DATA_BYTE(lined, j, index); + oqca[index]->n += 1.0; + oqca[index]->rcum += rval; + oqca[index]->gcum += gval; + oqca[index]->bcum += bval; + } + } + + /* Compute average color values in each octcube, and + * generate colormap */ + cmap = pixcmapCreate(bpp); + pixSetColormap(pixd, cmap); + for (i = 0; i < nbase; i++) { + oqc = oqca[i]; + if (oqc->n != 0) { + oqc->rval = (l_int32)(oqc->rcum / oqc->n); + oqc->gval = (l_int32)(oqc->gcum / oqc->n); + oqc->bval = (l_int32)(oqc->bcum / oqc->n); + } else { + getRGBFromOctcube(i, maxlevel - 1, &oqc->rval, + &oqc->gval, &oqc->bval); + } + pixcmapAddColor(cmap, oqc->rval, oqc->gval, oqc->bval); + } + + for (i = 0; i < nbase; i++) + LEPT_FREE(oqca[i]); + LEPT_FREE(oqca); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; + } + + /*------------------------------------------------------------* + * General case: we will use colors in octcubes at maxlevel. * + * We also remove any colors that are not populated from * + * the colormap. * + *------------------------------------------------------------*/ + /* Prepare the OctcubeQuantCell array */ + if ((oqca = (OQCELL **)LEPT_CALLOC(ncubes, sizeof(OQCELL *))) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("oqca not made", procName, NULL); + } + for (i = 0; i < ncubes; i++) { + oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); + oqca[i]->n = 0.0; + } + + /* Make the tables to map color to the octindex, + * of which there are 'ncubes' at 'maxlevel' */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(maxlevel, &rtab, >ab, &btab); + + /* Estimate the color distribution; we want to find the + * most popular nextra colors at 'maxlevel' */ + for (i = 0; i < h; i += subsample) { + lines = datas + i * wpls; + for (j = 0; j < w; j += subsample) { + pspixel = lines + j; + extractRGBValues(*pspixel, &rval, &gval, &bval); + getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, &index); + oqca[index]->n += 1.0; + oqca[index]->octindex = index; + oqca[index]->rcum += rval; + oqca[index]->gcum += gval; + oqca[index]->bcum += bval; + } + } + + /* Transfer the OQCELL from the array, and order in a heap */ + lh = lheapCreate(512, L_SORT_DECREASING); + for (i = 0; i < ncubes; i++) + lheapAdd(lh, oqca[i]); + LEPT_FREE(oqca); /* don't need this array */ + + /* Prepare a new OctcubeQuantCell array, with maxcolors cells */ + oqca = (OQCELL **)LEPT_CALLOC(maxcolors, sizeof(OQCELL *)); + for (i = 0; i < nbase; i++) { /* make nbase cells */ + oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); + oqca[i]->n = 0.0; + } + + /* Remove the nextra most populated ones, and put them in the array */ + for (i = 0; i < nextra; i++) { + oqc = (OQCELL *)lheapRemove(lh); + oqc->n = 0.0; /* reinit */ + oqc->rcum = 0; + oqc->gcum = 0; + oqc->bcum = 0; + oqca[nbase + i] = oqc; /* store it in the array */ + } + + /* Destroy the heap and its remaining contents */ + lheapDestroy(&lh, TRUE); + + /* Generate a lookup table from octindex at maxlevel + * to color table index */ + lut1 = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); + for (i = 0; i < nextra; i++) + lut1[oqca[nbase + i]->octindex] = nbase + i; + for (index = 0; index < ncubes; index++) { + if (lut1[index] == 0) /* not one of the extras; need to assign */ + lut1[index] = index >> 3; /* remove the least significant bits */ +/* fprintf(stderr, "lut1[%d] = %d\n", index, lut1[index]); */ + } + + /* Go through the entire image, gathering statistics and + * assigning pixels to their quantized value */ + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pspixel = lines + j; + extractRGBValues(*pspixel, &rval, &gval, &bval); + getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, &index); +/* fprintf(stderr, "rval = %d, gval = %d, bval = %d, index = %d\n", + rval, gval, bval, index); */ + val = lut1[index]; + switch (bpp) { + case 4: + SET_DATA_QBIT(lined, j, val); + break; + case 8: + SET_DATA_BYTE(lined, j, val); + break; + default: + LEPT_FREE(oqca); + LEPT_FREE(lut1); + return (PIX *)ERROR_PTR("bpp not 4 or 8!", procName, NULL); + break; + } + oqca[val]->n += 1.0; + oqca[val]->rcum += rval; + oqca[val]->gcum += gval; + oqca[val]->bcum += bval; + } + } + + /* Compute averages, set up a colormap, and make a second + * lut that converts from the color values currently in + * the image to a minimal set */ + lut2 = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); + cmap = pixcmapCreate(bpp); + pixSetColormap(pixd, cmap); + for (i = 0, index = 0; i < maxcolors; i++) { + oqc = oqca[i]; + lut2[i] = index; + if (oqc->n == 0) /* no occupancy; don't bump up index */ + continue; + oqc->rval = (l_int32)(oqc->rcum / oqc->n); + oqc->gval = (l_int32)(oqc->gcum / oqc->n); + oqc->bval = (l_int32)(oqc->bcum / oqc->n); + pixcmapAddColor(cmap, oqc->rval, oqc->gval, oqc->bval); + index++; + } +/* pixcmapWriteStream(stderr, cmap); */ + actualcolors = pixcmapGetCount(cmap); +/* fprintf(stderr, "Number of different colors = %d\n", actualcolors); */ + + /* Last time through the image; use the lookup table to + * remap the pixel value to the minimal colormap */ + if (actualcolors < maxcolors) { + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + switch (bpp) { + case 4: + val = GET_DATA_QBIT(lined, j); + SET_DATA_QBIT(lined, j, lut2[val]); + break; + case 8: + val = GET_DATA_BYTE(lined, j); + SET_DATA_BYTE(lined, j, lut2[val]); + break; + } + } + } + } + + if (oqca) { + for (i = 0; i < maxcolors; i++) + LEPT_FREE(oqca[i]); + } + LEPT_FREE(oqca); + LEPT_FREE(lut1); + LEPT_FREE(lut2); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Mixed color/gray quantization with specified number of colors * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixOctcubeQuantMixedWithGray() + * + * \param[in] pixs 32 bpp rgb + * \param[in] depth of output pix + * \param[in] graylevels graylevels (must be > 1) + * \param[in] delta threshold for deciding if a pix is color or gray + * \return pixd quantized to octcube and gray levels or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a colormapped image, where the colormap table values
+ *          have two components: octcube values representing pixels with
+ *          color content, and grayscale values for the rest.
+ *      (2) The threshold (delta) is the maximum allowable difference of
+ *          the max abs value of | r - g |, | r - b | and | g - b |.
+ *      (3) The octcube values are the averages of all pixels that are
+ *          found in the octcube, and that are far enough from gray to
+ *          be considered color.  This can roughly be visualized as all
+ *          the points in the rgb color cube that are not within a "cylinder"
+ *          of diameter approximately 'delta' along the main diagonal.
+ *      (4) We want to guarantee full coverage of the rgb color space; thus,
+ *          if the output depth is 4, the octlevel is 1 (2 x 2 x 2 = 8 cubes)
+ *          and if the output depth is 8, the octlevel is 2 (4 x 4 x 4
+ *          = 64 cubes).
+ *      (5) Consequently, we have the following constraint on the number
+ *          of allowed gray levels: for 4 bpp, 8; for 8 bpp, 192.
+ * 
+ */ +PIX * +pixOctcubeQuantMixedWithGray(PIX *pixs, + l_int32 depth, + l_int32 graylevels, + l_int32 delta) +{ +l_int32 w, h, wpls, wpld, i, j, size, octlevels; +l_int32 rval, gval, bval, del, val, midval; +l_int32 *carray, *rarray, *garray, *barray; +l_int32 *tabval; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *lines, *lined, *datas, *datad; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixOctcubeQuantMixedWithGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (graylevels < 2) + return (PIX *)ERROR_PTR("invalid graylevels", procName, NULL); + if (depth == 4) { + octlevels = 1; + size = 8; /* 2 ** 3 */ + if (graylevels > 8) + return (PIX *)ERROR_PTR("max 8 gray levels", procName, NULL); + } else if (depth == 8) { + octlevels = 2; + size = 64; /* 2 ** 6 */ + if (graylevels > 192) + return (PIX *)ERROR_PTR("max 192 gray levels", procName, NULL); + } else { + return (PIX *)ERROR_PTR("output depth not 4 or 8 bpp", procName, NULL); + } + + pixd = NULL; + + /* Make octcube index tables */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(octlevels, &rtab, >ab, &btab); + + /* Make octcube arrays for storing points in each cube */ + carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + + /* Make lookup table, using computed thresholds */ + tabval = makeGrayQuantIndexTable(graylevels); + if (!rtab || !gtab || !btab || + !carray || !rarray || !garray || !barray || !tabval) { + L_ERROR("calloc fail for an array\n", procName); + goto array_cleanup; + } + + /* Make colormapped output pixd */ + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, depth)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto array_cleanup; + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + cmap = pixcmapCreate(depth); + for (j = 0; j < size; j++) /* reserve octcube colors */ + pixcmapAddColor(cmap, 1, 1, 1); /* a color that won't be used */ + for (j = 0; j < graylevels; j++) { /* set grayscale colors */ + val = (255 * j) / (graylevels - 1); + pixcmapAddColor(cmap, val, val, val); + } + pixSetColormap(pixd, cmap); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + /* Go through src image: assign dest pixels to colormap values + * and compute average colors in each occupied octcube */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + if (rval > gval) { + if (gval > bval) { /* r > g > b */ + del = rval - bval; + midval = gval; + } else if (rval > bval) { /* r > b > g */ + del = rval - gval; + midval = bval; + } else { /* b > r > g */ + del = bval - gval; + midval = rval; + } + } else { /* gval >= rval */ + if (rval > bval) { /* g > r > b */ + del = gval - bval; + midval = rval; + } else if (gval > bval) { /* g > b > r */ + del = gval - rval; + midval = bval; + } else { /* b > g > r */ + del = bval - rval; + midval = gval; + } + } + if (del > delta) { /* assign to color */ + octindex = rtab[rval] | gtab[gval] | btab[bval]; + carray[octindex]++; + rarray[octindex] += rval; + garray[octindex] += gval; + barray[octindex] += bval; + if (depth == 4) + SET_DATA_QBIT(lined, j, octindex); + else /* depth == 8 */ + SET_DATA_BYTE(lined, j, octindex); + } else { /* assign to grayscale */ + val = size + tabval[midval]; + if (depth == 4) + SET_DATA_QBIT(lined, j, val); + else /* depth == 8 */ + SET_DATA_BYTE(lined, j, val); + } + } + } + + /* Average the colors in each bin and reset the colormap */ + for (i = 0; i < size; i++) { + if (carray[i] > 0) { + rarray[i] /= carray[i]; + garray[i] /= carray[i]; + barray[i] /= carray[i]; + pixcmapResetColor(cmap, i, rarray[i], garray[i], barray[i]); + } + } + +array_cleanup: + LEPT_FREE(carray); + LEPT_FREE(rarray); + LEPT_FREE(garray); + LEPT_FREE(barray); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + LEPT_FREE(tabval); + + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Fixed partition octcube quantization with 256 cells * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixFixedOctcubeQuant256() + * + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] ditherflag 1 for dithering; 0 for no dithering + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ * Notes:
+ *  This simple 1-pass color quantization works by breaking the
+ *  color space into 256 pieces, with 3 bits quantized for each of
+ *  red and green, and 2 bits quantized for blue.  We shortchange
+ *  blue because the eye is least sensitive to blue.  This
+ *  division of the color space is into two levels of octrees,
+ *  followed by a further division by 4 not 8, where both
+ *  blue octrees have been combined in the third level.
+ *
+ *  The color map is generated from the 256 color centers by
+ *  taking the representative color to be the center of the
+ *  cell volume.  This gives a maximum error in the red and
+ *  green values of 16 levels, and a maximum error in the
+ *  blue sample of 32 levels.
+ *
+ *  Each pixel in the 24-bit color image is placed in its containing
+ *  cell, given by the relevant MSbits of the red, green and blue
+ *  samples.  An error-diffusion dithering is performed on each
+ *  color sample to give the appearance of good average local color.
+ *  Dithering is required; without it, the contouring and visible
+ *  color errors are very bad.
+ *
+ *  I originally implemented this algorithm in two passes,
+ *  where the first pass was used to compute the weighted average
+ *  of each sample in each pre-allocated region of color space.
+ *  The idea was to use these centroids in the dithering algorithm
+ *  of the second pass, to reduce the average error that was
+ *  being dithered.  However, with dithering, there is
+ *  virtually no difference, so there is no reason to make the
+ *  first pass.  Consequently, this 1-pass version just assigns
+ *  the pixels to the centers of the pre-allocated cells.
+ *  We use dithering to spread the difference between the sample
+ *  value and the location of the center of the cell.  For speed
+ *  and simplicity, we use integer dithering and propagate only
+ *  to the right, down, and diagonally down-right, with ratios
+ *  3/8, 3/8 and 1/4, respectively.  The results should be nearly
+ *  as good, and a bit faster, with propagation only to the right
+ *  and down.
+ *
+ *  The algorithm is very fast, because there is no search,
+ *  only fast generation of the cell index for each pixel.
+ *  We use a simple mapping from the three 8 bit rgb samples
+ *  to the 8 bit cell index; namely, r7 r6 r5 g7 g6 g5 b7 b6.
+ *  This is not in an octcube format, but it doesn't matter.
+ *  There are no storage requirements.  We could keep a
+ *  running average of the center of each sample in each
+ *  cluster, rather than using the center of the cell, but
+ *  this is just extra work, esp. with dithering.
+ *
+ *  This method gives surprisingly good results with dithering.
+ *  However, without dithering, the loss of color accuracy is
+ *  evident in regions that are very light or that have subtle
+ *  blending of colors.
+ * 
+ */ +PIX * +pixFixedOctcubeQuant256(PIX *pixs, + l_int32 ditherflag) +{ +l_uint8 index; +l_int32 rval, gval, bval; +l_int32 w, h, wpls, wpld, i, j, cindex; +l_uint32 *rtab, *gtab, *btab; +l_int32 *itab; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixFixedOctcubeQuant256"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + + /* Do not dither if image is very small */ + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { + L_INFO("Small image: dithering turned off\n", procName); + ditherflag = 0; + } + + /* Find the centers of the 256 cells, each of which represents + * the 3 MSBits of the red and green components, and the + * 2 MSBits of the blue component. This gives a mapping + * from a "cube index" to the rgb values. Save all 256 + * rgb values of these centers in a colormap. + * For example, to get the red color of the cell center, + * you take the 3 MSBits of to the index and add the + * offset to the center of the cell, which is 0x10. */ + cmap = pixcmapCreate(8); + for (cindex = 0; cindex < 256; cindex++) { + rval = (cindex & 0xe0) | 0x10; + gval = ((cindex << 3) & 0xe0) | 0x10; + bval = ((cindex << 6) & 0xc0) | 0x20; + pixcmapAddColor(cmap, rval, gval, bval); + } + + /* Make output 8 bpp palette image */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) { + pixcmapDestroy(&cmap); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Set dest pix values to colortable indices */ + if (ditherflag == 0) { /* no dithering */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + index = (rval & 0xe0) | ((gval >> 3) & 0x1c) | (bval >> 6); + SET_DATA_BYTE(lined, j, index); + } + } + } else { /* ditherflag == 1 */ + /* Set up conversion tables from rgb directly to the colormap + * index. However, the dithering function expects these tables + * to generate an octcube index (+1), and the table itab[] to + * convert to the colormap index. So we make a trivial + * itab[], that simply compensates for the -1 in + * pixDitherOctindexWithCmap(). No cap is required on + * the propagated difference. */ + rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + itab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (!rtab || !gtab || !btab || !itab) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("calloc fail for table", procName, NULL); + } + for (i = 0; i < 256; i++) { + rtab[i] = i & 0xe0; + gtab[i] = (i >> 3) & 0x1c; + btab[i] = i >> 6; + itab[i] = i + 1; + } + pixDitherOctindexWithCmap(pixs, pixd, rtab, gtab, btab, itab, + FIXED_DIF_CAP); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + LEPT_FREE(itab); + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Nearly exact quantization for images with few colors * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixFewColorsOctcubeQuant1() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level significant bits for each of RGB; valid in [1...6] + * \return pixd quantized to octcube or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a colormapped image, where the colormap table values
+ *          are the averages of all pixels that are found in the octcube.
+ *      (2) This fails if there are more than 256 colors (i.e., more
+ *          than 256 occupied octcubes).
+ *      (3) Often level 3 (512 octcubes) will succeed because not more
+ *          than half of them are occupied with 1 or more pixels.
+ *      (4) The depth of the result, which is either 2, 4 or 8 bpp,
+ *          is the minimum required to hold the number of colors that
+ *          are found.
+ *      (5) This can be useful for quantizing orthographically generated
+ *          images such as color maps, where there may be more than 256 colors
+ *          because of aliasing or jpeg artifacts on text or lines, but
+ *          there are a relatively small number of solid colors.  Then,
+ *          use with level = 3 can often generate a compact and accurate
+ *          representation of the original RGB image.  For this purpose,
+ *          it is better than pixFewColorsOctcubeQuant2(), because it
+ *          uses the average value of pixels in the octcube rather
+ *          than the first found pixel.  It is also simpler to use,
+ *          because it generates the histogram internally.
+ * 
+ */ +PIX * +pixFewColorsOctcubeQuant1(PIX *pixs, + l_int32 level) +{ +l_int32 w, h, wpls, wpld, i, j, depth, size, ncolors, index; +l_int32 rval, gval, bval; +l_int32 *carray, *rarray, *garray, *barray; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *lines, *lined, *datas, *datad, *pspixel; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixFewColorsOctcubeQuant1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (level < 1 || level > 6) + return (PIX *)ERROR_PTR("invalid level", procName, NULL); + + pixd = NULL; + + if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ + return (PIX *)ERROR_PTR("size not returned", procName, NULL); + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + + carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); + if (!carray || !rarray || !garray || !barray) { + L_ERROR("calloc fail for an array\n", procName); + goto array_cleanup; + } + + /* Place the pixels in octcube leaves. */ + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + pspixel = lines + j; + extractRGBValues(*pspixel, &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + carray[octindex]++; + rarray[octindex] += rval; + garray[octindex] += gval; + barray[octindex] += bval; + } + } + + /* Find the number of different colors */ + for (i = 0, ncolors = 0; i < size; i++) { + if (carray[i] > 0) + ncolors++; + } + if (ncolors > 256) { + L_WARNING("%d colors found; more than 256\n", procName, ncolors); + goto array_cleanup; + } + if (ncolors <= 4) + depth = 2; + else if (ncolors <= 16) + depth = 4; + else + depth = 8; + + /* Average the colors in each octcube leaf and add to colormap table; + * then use carray to hold the colormap index + 1 */ + cmap = pixcmapCreate(depth); + for (i = 0, index = 0; i < size; i++) { + if (carray[i] > 0) { + rarray[i] /= carray[i]; + garray[i] /= carray[i]; + barray[i] /= carray[i]; + pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); + carray[i] = index + 1; /* to avoid storing 0 */ + index++; + } + } + + pixd = pixCreate(w, h, depth); + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pspixel = lines + j; + extractRGBValues(*pspixel, &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + switch (depth) + { + case 2: + SET_DATA_DIBIT(lined, j, carray[octindex] - 1); + break; + case 4: + SET_DATA_QBIT(lined, j, carray[octindex] - 1); + break; + case 8: + SET_DATA_BYTE(lined, j, carray[octindex] - 1); + break; + default: + L_WARNING("shouldn't get here\n", procName); + } + } + } + +array_cleanup: + LEPT_FREE(carray); + LEPT_FREE(rarray); + LEPT_FREE(garray); + LEPT_FREE(barray); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*! + * \brief pixFewColorsOctcubeQuant2() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level of octcube indexing, for histogram: 3, 4, 5, 6 + * \param[in] na histogram of pixel occupation in octree leaves + * at given level + * \param[in] ncolors number of occupied octree leaves at given level + * \param[out] pnerrors [optional] num of pixels not exactly + * represented in the colormap + * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a colormapped image, where the colormap table values
+ *          are the averages of all pixels that are found in the octcube.
+ *      (2) This fails if there are more than 256 colors (i.e., more
+ *          than 256 occupied octcubes).
+ *      (3) Often level 3 (512 octcubes) will succeed because not more
+ *          than half of them are occupied with 1 or more pixels.
+ *      (4) For an image with not more than 256 colors, it is unlikely
+ *          that two pixels of different color will fall in the same
+ *          octcube at level = 4.   However it is possible, and this
+ *          function optionally returns %nerrors, the number of pixels
+ *          where, because more than one color is in the same octcube,
+ *          the pixel color is not exactly reproduced in the colormap.
+ *          The colormap for an occupied leaf of the octree contains
+ *          the color of the first pixel encountered in that octcube.
+ *      (5) This differs from pixFewColorsOctcubeQuant1(), which also
+ *          requires not more than 256 occupied leaves, but represents
+ *          the color of each leaf by an average over the pixels in
+ *          that leaf.  This also requires precomputing the histogram
+ *          of occupied octree leaves, which is generated using
+ *          pixOctcubeHistogram().
+ *      (6) This is used in pixConvertRGBToColormap() for images that
+ *          are determined, by their histogram, to have relatively few
+ *          colors.  This typically happens with orthographically
+ *          produced images (as oppopsed to natural images), where
+ *          it is expected that most of the pixels within a leaf
+ *          octcube have exactly the same color, and quantization to
+ *          that color is lossless.
+ * 
+ */ +PIX * +pixFewColorsOctcubeQuant2(PIX *pixs, + l_int32 level, + NUMA *na, + l_int32 ncolors, + l_int32 *pnerrors) +{ +l_int32 w, h, wpls, wpld, i, j, nerrors; +l_int32 ncubes, depth, cindex, oval; +l_int32 rval, gval, bval; +l_int32 *octarray; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *lines, *lined, *datas, *datad, *ppixel; +l_uint32 *colorarray; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixFewColorsOctcubeQuant2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (level < 3 || level > 6) + return (PIX *)ERROR_PTR("level not in {4, 5, 6}", procName, NULL); + if (ncolors > 256) + return (PIX *)ERROR_PTR("ncolors > 256", procName, NULL); + if (pnerrors) + *pnerrors = UNDEF; + + pixd = NULL; + + /* Represent the image with a set of leaf octcubes + * at 'level', one for each color. */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + + /* The octarray will give a ptr from the octcube to the colorarray */ + ncubes = numaGetCount(na); + octarray = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); + + /* The colorarray will hold the colors of the first pixel + * that lands in the leaf octcube. After filling, it is + * used to generate the colormap. */ + colorarray = (l_uint32 *)LEPT_CALLOC(ncolors + 1, sizeof(l_uint32)); + if (!octarray || !colorarray) { + L_ERROR("octarray or colorarray not made\n", procName); + goto cleanup_arrays; + } + + /* Determine the output depth from the number of colors */ + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (ncolors <= 4) + depth = 2; + else if (ncolors <= 16) + depth = 4; + else /* ncolors <= 256 */ + depth = 8; + + if ((pixd = pixCreate(w, h, depth)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup_arrays; + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* For each pixel, get the octree index for its leaf octcube. + * Check if a pixel has already been found in this octcube. + * ~ If not yet found, save that color in the colorarray + * and save the cindex in the octarray. + * ~ If already found, compare the pixel color with the + * color in the colorarray, and note if it differs. + * Then set the dest pixel value to the cindex - 1, which + * will be the cmap index for this color. */ + cindex = 1; /* start with 1 */ + nerrors = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + ppixel = lines + j; + extractRGBValues(*ppixel, &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + oval = octarray[octindex]; + if (oval == 0) { + octarray[octindex] = cindex; + colorarray[cindex] = *ppixel; + setPixelLow(lined, j, depth, cindex - 1); + cindex++; + } else { /* already have seen this color; is it unique? */ + setPixelLow(lined, j, depth, oval - 1); + if (colorarray[oval] != *ppixel) + nerrors++; + } + } + } + if (pnerrors) + *pnerrors = nerrors; + +#if DEBUG_FEW_COLORS + fprintf(stderr, "ncubes = %d, ncolors = %d\n", ncubes, ncolors); + for (i = 0; i < ncolors; i++) + fprintf(stderr, "color[%d] = %x\n", i, colorarray[i + 1]); +#endif /* DEBUG_FEW_COLORS */ + + /* Make the colormap. */ + cmap = pixcmapCreate(depth); + for (i = 0; i < ncolors; i++) { + ppixel = colorarray + i + 1; + extractRGBValues(*ppixel, &rval, &gval, &bval); + pixcmapAddColor(cmap, rval, gval, bval); + } + pixSetColormap(pixd, cmap); + +cleanup_arrays: + LEPT_FREE(octarray); + LEPT_FREE(colorarray); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + + return pixd; +} + + +/*! + * \brief pixFewColorsOctcubeQuantMixed() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level significant octcube bits for each of RGB; + * valid in [1...6]; use 0 for default + * \param[in] darkthresh threshold near black; if the lightest component + * is below this, the pixel is not considered to + * be gray or color; uses 0 for default + * \param[in] lightthresh threshold near white; if the darkest component + * is above this, the pixel is not considered to + * be gray or color; use 0 for default + * \param[in] diffthresh thresh for the max difference between component + * values; for differences below this, the pixel + * is considered to be gray; use 0 for default + * \param[in] minfract min fraction of pixels for gray histo bin; + * use 0.0 for default + * \param[in] maxspan max size of gray histo bin; use 0 for default + * \return pixd 8 bpp, quantized to octcube for pixels that are + * not gray; gray pixels are quantized separately + * over the full gray range, or NULL on error + * + *
+ * Notes:
+ *      (1) First runs pixFewColorsOctcubeQuant1().  If this succeeds,
+ *          it separates the color from gray(ish) entries in the cmap,
+ *          and re-quantizes the gray pixels.  The result has some pixels
+ *          in color and others in gray.
+ *      (2) This fails if there are more than 256 colors (i.e., more
+ *          than 256 occupied octcubes in the color quantization).
+ *      (3) Level 3 (512 octcubes) will usually succeed because not more
+ *          than half of them are occupied with 1 or more pixels.
+ *      (4) This uses the criterion from pixColorFraction() for deciding
+ *          if a colormap entry is color; namely, if the color components
+ *          are not too close to either black or white, and the maximum
+ *          difference between component values equals or exceeds a threshold.
+ *      (5) For quantizing the gray pixels, it uses a histogram-based
+ *          method where input parameters determining the buckets are
+ *          the minimum population fraction and the maximum allowed size.
+ *      (6) Recommended input parameters are:
+ *              %level:  3 or 4  (3 is default)
+ *              %darkthresh:  20
+ *              %lightthresh: 244
+ *              %diffthresh: 20
+ *              %minfract: 0.05
+ *              %maxspan: 15
+ *          These numbers are intended to be conservative (somewhat over-
+ *          sensitive) in color detection,  It's usually better to pay
+ *          extra with octcube quantization of a grayscale image than
+ *          to use grayscale quantization on an image that has some
+ *          actual color.  Input 0 on any of these to get the default.
+ *      (7) This can be useful for quantizing orthographically generated
+ *          images such as color maps, where there may be more than 256 colors
+ *          because of aliasing or jpeg artifacts on text or lines, but
+ *          there are a relatively small number of solid colors.  It usually
+ *          gives results that are better than pixOctcubeQuantMixedWithGray(),
+ *          both in size and appearance.  But it is a bit slower.
+ * 
+ */ +PIX * +pixFewColorsOctcubeQuantMixed(PIX *pixs, + l_int32 level, + l_int32 darkthresh, + l_int32 lightthresh, + l_int32 diffthresh, + l_float32 minfract, + l_int32 maxspan) +{ +l_int32 i, j, w, h, wplc, wplm, wpld, ncolors, index; +l_int32 rval, gval, bval, val, minval, maxval; +l_int32 *lut; +l_uint32 *datac, *datam, *datad, *linec, *linem, *lined; +PIX *pixc, *pixm, *pixg, *pixd; +PIXCMAP *cmap, *cmapd; + + PROCNAME("pixFewColorsOctcubeQuantMixed"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (level <= 0) level = 3; + if (level > 6) + return (PIX *)ERROR_PTR("invalid level", procName, NULL); + if (darkthresh <= 0) darkthresh = 20; + if (lightthresh <= 0) lightthresh = 244; + if (diffthresh <= 0) diffthresh = 20; + if (minfract <= 0.0) minfract = 0.05; + if (maxspan <= 2) maxspan = 15; + + /* Start with a simple fixed octcube quantizer. */ + if ((pixc = pixFewColorsOctcubeQuant1(pixs, level)) == NULL) + return (PIX *)ERROR_PTR("too many colors", procName, NULL); + + /* Identify and save color entries in the colormap. Set up a LUT + * that returns -1 for any gray pixel. */ + cmap = pixGetColormap(pixc); + ncolors = pixcmapGetCount(cmap); + cmapd = pixcmapCreate(8); + lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) + lut[i] = -1; + for (i = 0, index = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + if (minval > lightthresh) /* near white */ + continue; + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + if (maxval < darkthresh) /* near black */ + continue; + + /* Use the max diff between components to test for color */ + if (maxval - minval >= diffthresh) { + pixcmapAddColor(cmapd, rval, gval, bval); + lut[i] = index; + index++; + } + } + + /* Generate dest pix with just the color pixels set to their + * colormap indices. At the same time, make a 1 bpp mask + * of the non-color pixels */ + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, 8); + pixSetColormap(pixd, cmapd); + pixm = pixCreate(w, h, 1); + datac = pixGetData(pixc); + datam = pixGetData(pixm); + datad = pixGetData(pixd); + wplc = pixGetWpl(pixc); + wplm = pixGetWpl(pixm); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + linem = datam + i * wplm; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linec, j); + if (lut[val] == -1) + SET_DATA_BIT(linem, j); + else + SET_DATA_BYTE(lined, j, lut[val]); + } + } + + /* Fill in the gray values. Use a grayscale version of pixs + * as input, along with the mask over the actual gray pixels. */ + pixg = pixConvertTo8(pixs, 0); + pixGrayQuantFromHisto(pixd, pixg, pixm, minfract, maxspan); + + LEPT_FREE(lut); + pixDestroy(&pixc); + pixDestroy(&pixm); + pixDestroy(&pixg); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Fixed partition octcube quantization with RGB output * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixFixedOctcubeQuantGenRGB() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level significant bits for each of r,g,b + * \return pixd rgb; quantized to octcube centers, or NULL on error + * + *
+ * Notes:
+ *      (1) Unlike the other color quantization functions, this one
+ *          generates an rgb image.
+ *      (2) The pixel values are quantized to the center of each octcube
+ *          (at the specified level) containing the pixel.  They are
+ *          not quantized to the average of the pixels in that octcube.
+ * 
+ */ +PIX * +pixFixedOctcubeQuantGenRGB(PIX *pixs, + l_int32 level) +{ +l_int32 w, h, wpls, wpld, i, j; +l_int32 rval, gval, bval; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *lines, *lined, *datas, *datad; +PIX *pixd; + + PROCNAME("pixFixedOctcubeQuantGenRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (level < 1 || level > 6) + return (PIX *)ERROR_PTR("level not in {1,...6}", procName, NULL); + + if (makeRGBToIndexTables(level, &rtab, >ab, &btab)) + return (PIX *)ERROR_PTR("tables not made", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, 32); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + getRGBFromOctcube(octindex, level, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*------------------------------------------------------------------* + * Color quantize RGB image using existing colormap * + *------------------------------------------------------------------*/ +/*! + * \brief pixQuantFromCmap() + * + * \param[in] pixs 8 bpp grayscale without cmap, or 32 bpp rgb + * \param[in] cmap to quantize to; insert copy into dest pix + * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp + * \param[in] level of octcube used for finding nearest color in cmap + * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE + * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level wrapper for quantizing either grayscale
+ *          or rgb images to a specified colormap.
+ *      (2) The actual output depth is constrained by %mindepth and
+ *          by the number of colors in %cmap.
+ *      (3) For grayscale, %level and %metric are ignored.
+ *      (4) If the cmap has color and pixs is grayscale, the color is
+ *          removed from the cmap before quantizing pixs.
+ * 
+ */ +PIX * +pixQuantFromCmap(PIX *pixs, + PIXCMAP *cmap, + l_int32 mindepth, + l_int32 level, + l_int32 metric) +{ +l_int32 d; + + PROCNAME("pixQuantFromCmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (mindepth != 2 && mindepth != 4 && mindepth != 8) + return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); + d = pixGetDepth(pixs); + if (d == 8) + return pixGrayQuantFromCmap(pixs, cmap, mindepth); + else if (d == 32) + return pixOctcubeQuantFromCmap(pixs, cmap, mindepth, + level, metric); + else + return (PIX *)ERROR_PTR("d not 8 or 32 bpp", procName, NULL); +} + + + +/*! + * \brief pixOctcubeQuantFromCmap() + * + * \param[in] pixs 32 bpp rgb + * \param[in] cmap to quantize to; insert copy into dest pix + * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp + * \param[in] level of octcube used for finding nearest color in cmap + * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE + * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error + * + *
+ * Notes:
+ *      (1) In typical use, we are doing an operation, such as
+ *          interpolative scaling, on a colormapped pix, where it is
+ *          necessary to remove the colormap before the operation.
+ *          We then want to re-quantize the RGB result using the same
+ *          colormap.
+ *      (2) The level is used to divide the color space into octcubes.
+ *          Each input pixel is, in effect, placed at the center of an
+ *          octcube at the given level, and it is mapped into the
+ *          exact color (given in the colormap) that is the closest
+ *          to that location.  We need to know that distance, for each color
+ *          in the colormap.  The higher the level of the octtree, the smaller
+ *          the octcubes in the color space, and hence the more accurately
+ *          we can determine the closest color in the colormap; however,
+ *          the size of the LUT, which is the total number of octcubes,
+ *          increases by a factor of 8 for each increase of 1 level.
+ *          The time required to acquire a level 4 mapping table, which has
+ *          about 4K entries, is less than 1 msec, so that is the
+ *          recommended minimum size to be used.  At that size, the
+ *          octcubes have their centers 16 units apart in each (r,g,b)
+ *          direction.  If two colors are in the same octcube, the one
+ *          closest to the center will always be chosen.  The maximum
+ *          error for any component occurs when the correct color is
+ *          at a cube corner and there is an incorrect color just inside
+ *          the cube next to the opposite corner, giving an error of
+ *          14 units (out of 256) for each component.   Using a level 5
+ *          mapping table reduces the maximum error to 6 units.
+ *      (3) Typically you should use the Euclidean metric, because the
+ *          resulting voronoi cells (which are generated using the actual
+ *          colormap values as seeds) are convex for Euclidean distance
+ *          but not for Manhattan distance.  In terms of the octcubes,
+ *          convexity of the voronoi cells means that if the 8 corners
+ *          of any cube (of which the octcubes are special cases)
+ *          are all within a cell, then every point in the cube will
+ *          lie within the cell.
+ *      (4) The depth of the output pixd is equal to the maximum of
+ *          (a) %mindepth and (b) the minimum (2, 4 or 8 bpp) necessary
+ *          to hold the indices in the colormap.
+ *      (5) We build a mapping table from octcube to colormap index so
+ *          that this function can run in a time (otherwise) independent
+ *          of the number of colors in the colormap.  This avoids a
+ *          brute-force search for the closest colormap color to each
+ *          pixel in the image.
+ *      (6) This is similar to the function pixAssignToNearestColor()
+ *          used for color segmentation.
+ *      (7) Except for very small images or when using level > 4,
+ *          it takes very little time to generate the tables,
+ *          compared to the generation of the colormapped dest pix,
+ *          so one would not typically use the low-level version.
+ * 
+ */ +PIX * +pixOctcubeQuantFromCmap(PIX *pixs, + PIXCMAP *cmap, + l_int32 mindepth, + l_int32 level, + l_int32 metric) +{ +l_int32 *cmaptab; +l_uint32 *rtab, *gtab, *btab; +PIX *pixd; + + PROCNAME("pixOctcubeQuantFromCmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (!cmap) + return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); + if (mindepth != 2 && mindepth != 4 && mindepth != 8) + return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); + if (level < 1 || level > 6) + return (PIX *)ERROR_PTR("level not in {1...6}", procName, NULL); + if (metric != L_MANHATTAN_DISTANCE && metric != L_EUCLIDEAN_DISTANCE) + return (PIX *)ERROR_PTR("invalid metric", procName, NULL); + + /* Set up the tables to map rgb to the nearest colormap index */ + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + cmaptab = pixcmapToOctcubeLUT(cmap, level, metric); + + pixd = pixOctcubeQuantFromCmapLUT(pixs, cmap, mindepth, + cmaptab, rtab, gtab, btab); + + LEPT_FREE(cmaptab); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return pixd; +} + + +/*! + * \brief pixOctcubeQuantFromCmapLUT() + * + * \param[in] pixs 32 bpp rgb + * \param[in] cmap to quantize to; insert copy into dest pix + * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp + * \param[in] cmaptab table mapping from octindex to colormap index + * \param[in] rtab, gtab, btab tables mapping from RGB to octindex + * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error + * + *
+ * Notes:
+ *      (1) See the notes in the higher-level function
+ *          pixOctcubeQuantFromCmap().  The octcube level for
+ *          the generated octree is specified there, along with
+ *          the distance metric for determining the closest
+ *          color in the colormap to each octcube.
+ *      (2) If the colormap, level and metric information have already
+ *          been used to construct the set of mapping tables,
+ *          this low-level function can be used directly (i.e.,
+ *          independently of pixOctcubeQuantFromCmap()) to build
+ *          a colormapped pix that uses the specified colormap.
+ * 
+ */ +static PIX * +pixOctcubeQuantFromCmapLUT(PIX *pixs, + PIXCMAP *cmap, + l_int32 mindepth, + l_int32 *cmaptab, + l_uint32 *rtab, + l_uint32 *gtab, + l_uint32 *btab) +{ +l_int32 i, j, w, h, depth, wpls, wpld; +l_int32 rval, gval, bval, index; +l_uint32 octindex; +l_uint32 *lines, *lined, *datas, *datad; +PIX *pixd; +PIXCMAP *cmapc; + + PROCNAME("pixOctcubeQuantFromCmapLUT"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (!cmap) + return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); + if (mindepth != 2 && mindepth != 4 && mindepth != 8) + return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); + if (!rtab || !gtab || !btab || !cmaptab) + return (PIX *)ERROR_PTR("tables not all defined", procName, NULL); + + /* Init dest pix (with minimum bpp depending on cmap) */ + pixcmapGetMinDepth(cmap, &depth); + depth = L_MAX(depth, mindepth); + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmapc = pixcmapCopy(cmap); + pixSetColormap(pixd, cmapc); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Insert the colormap index of the color nearest to the input pixel */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + /* Map from rgb to octcube index */ + getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, + &octindex); + /* Map from octcube index to nearest colormap index */ + index = cmaptab[octindex]; + if (depth == 2) + SET_DATA_DIBIT(lined, j, index); + else if (depth == 4) + SET_DATA_QBIT(lined, j, index); + else /* depth == 8 */ + SET_DATA_BYTE(lined, j, index); + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Generation of octcube histogram * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixOctcubeHistogram() + * + * \param[in] pixs 32 bpp rgb + * \param[in] level significant bits for each of RGB; valid in [1...6] + * \param[out] pncolors [optional] number of occupied cubes + * \return numa histogram of color pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) Input NULL for &ncolors to prevent computation and return value.
+ * 
+ */ +NUMA * +pixOctcubeHistogram(PIX *pixs, + l_int32 level, + l_int32 *pncolors) +{ +l_int32 size, i, j, w, h, wpl, ncolors, val; +l_int32 rval, gval, bval; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *data, *line; +l_float32 *array; +NUMA *na; + + PROCNAME("pixOctcubeHistogram"); + + if (pncolors) *pncolors = 0; + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (NUMA *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + + if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ + return (NUMA *)ERROR_PTR("size not returned", procName, NULL); + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + + if ((na = numaCreate(size)) == NULL) { + L_ERROR("na not made\n", procName); + goto cleanup_arrays; + } + numaSetCount(na, size); + array = numaGetFArray(na, L_NOCOPY); + + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; +#if DEBUG_OCTINDEX + if ((level == 1 && octindex > 7) || + (level == 2 && octindex > 63) || + (level == 3 && octindex > 511) || + (level == 4 && octindex > 4097) || + (level == 5 && octindex > 32783) || + (level == 6 && octindex > 262271)) { + fprintf(stderr, "level = %d, octindex = %d, index error!\n", + level, octindex); + continue; + } +#endif /* DEBUG_OCTINDEX */ + array[octindex] += 1.0; + } + } + + if (pncolors) { + for (i = 0, ncolors = 0; i < size; i++) { + numaGetIValue(na, i, &val); + if (val > 0) + ncolors++; + } + *pncolors = ncolors; + } + +cleanup_arrays: + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return na; +} + + +/*------------------------------------------------------------------* + * Get filled octcube table from colormap * + *------------------------------------------------------------------*/ +/*! + * \brief pixcmapToOctcubeLUT() + * + * \param[in] cmap + * \param[in] level significant bits for each of RGB; valid in [1...6] + * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE + * \return tab[2**3 * level] + * + *
+ * Notes:
+ *      (1) This function is used to quickly find the colormap color
+ *          that is closest to any rgb color.  It is used to assign
+ *          rgb colors to an existing colormap.  It can be very expensive
+ *          to search through the entire colormap for the closest color
+ *          to each pixel.  Instead, we first set up this table, which is
+ *          populated by the colormap index nearest to each octcube
+ *          color.  Then we go through the image; for each pixel,
+ *          do two table lookups: first to generate the octcube index
+ *          from rgb and second to use this table to read out the
+ *          colormap index.
+ *      (2) Do a slight modification for white and black.  For level = 4,
+ *          each octcube size is 16.  The center of the whitest octcube
+ *          is at (248, 248, 248), which is closer to 242 than 255.
+ *          Consequently, any gray color between 242 and 254 will
+ *          be selected, even if white (255, 255, 255) exists.  This is
+ *          typically not optimal, because the original color was
+ *          likely white.  Therefore, if white exists in the colormap,
+ *          use it for any rgb color that falls into the most white octcube.
+ *          Do the similar thing for black.
+ *      (3) Here are the actual function calls for quantizing to a
+ *          specified colormap:
+ *            ~ first make the tables that map from rgb --> octcube index
+ *                     makeRGBToIndexTables()
+ *            ~ then for each pixel:
+ *                * use the tables to get the octcube index
+ *                     getOctcubeIndexFromRGB()
+ *                * use this table to get the nearest color in the colormap
+ *                     cmap_index = tab[index]
+ *      (4) Distance can be either manhattan or euclidean.
+ *      (5) In typical use, level = 4 gives reasonable results, and
+ *          level = 5 is slightly better.  When this function is used
+ *          for color segmentation, there are typically a small number
+ *          of colors and the number of levels can be small (e.g., level = 3).
+ * 
+ */ +l_int32 * +pixcmapToOctcubeLUT(PIXCMAP *cmap, + l_int32 level, + l_int32 metric) +{ +l_int32 i, k, size, ncolors, mindist, dist, mincolor, index; +l_int32 rval, gval, bval; /* color at center of the octcube */ +l_int32 *rmap, *gmap, *bmap, *tab; + + PROCNAME("pixcmapToOctcubeLUT"); + + if (!cmap) + return (l_int32 *)ERROR_PTR("cmap not defined", procName, NULL); + if (level < 1 || level > 6) + return (l_int32 *)ERROR_PTR("level not in {1...6}", procName, NULL); + if (metric != L_MANHATTAN_DISTANCE && metric != L_EUCLIDEAN_DISTANCE) + return (l_int32 *)ERROR_PTR("invalid metric", procName, NULL); + + if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ + return (l_int32 *)ERROR_PTR("size not returned", procName, NULL); + if ((tab = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) + return (l_int32 *)ERROR_PTR("tab not allocated", procName, NULL); + + ncolors = pixcmapGetCount(cmap); + pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL); + + /* Assign based on the closest octcube center to the cmap color */ + for (i = 0; i < size; i++) { + getRGBFromOctcube(i, level, &rval, &gval, &bval); + mindist = 1000000; + mincolor = 0; /* irrelevant init */ + for (k = 0; k < ncolors; k++) { + if (metric == L_MANHATTAN_DISTANCE) { + dist = L_ABS(rval - rmap[k]) + L_ABS(gval - gmap[k]) + + L_ABS(bval - bmap[k]); + } else { /* L_EUCLIDEAN_DISTANCE */ + dist = (rval - rmap[k]) * (rval - rmap[k]) + + (gval - gmap[k]) * (gval - gmap[k]) + + (bval - bmap[k]) * (bval - bmap[k]); + } + if (dist < mindist) { + mindist = dist; + mincolor = k; + } + } + tab[i] = mincolor; + } + + /* Reset black and white if available in the colormap. + * The darkest octcube is at octindex 0. + * The lightest octcube is at the max octindex. */ + pixcmapGetNearestIndex(cmap, 0, 0, 0, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval < 7 && gval < 7 && bval < 7) { + tab[0] = index; + } + pixcmapGetNearestIndex(cmap, 255, 255, 255, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval > 248 && gval > 248 && bval > 248) { + tab[(1 << (3 * level)) - 1] = index; + } + + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + return tab; +} + + +/*------------------------------------------------------------------* + * Strip out unused elements in colormap * + *------------------------------------------------------------------*/ +/*! + * \brief pixRemoveUnusedColors() + * + * \param[in] pixs colormapped + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) If the image doesn't have a colormap, returns without error.
+ *      (3) Unusued colors are removed from the colormap, and the
+ *          image pixels are re-numbered.
+ * 
+ */ +l_ok +pixRemoveUnusedColors(PIX *pixs) +{ +l_int32 i, j, w, h, d, nc, wpls, val, newval, index, zerofound; +l_int32 rval, gval, bval; +l_uint32 *datas, *lines; +l_int32 *histo, *map1, *map2; +PIXCMAP *cmap, *cmapd; + + PROCNAME("pixRemoveUnusedColors"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return 0; + + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("d not in {2, 4, 8}", procName, 1); + + /* Find which indices are actually used */ + nc = pixcmapGetCount(cmap); + if ((histo = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL) + return ERROR_INT("histo not made", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + switch (d) + { + case 2: + val = GET_DATA_DIBIT(lines, j); + break; + case 4: + val = GET_DATA_QBIT(lines, j); + break; + case 8: + val = GET_DATA_BYTE(lines, j); + break; + default: + LEPT_FREE(histo); + return ERROR_INT("switch ran off end!", procName, 1); + } + if (val >= nc) { + L_WARNING("cmap index out of bounds!\n", procName); + continue; + } + histo[val]++; + } + } + + /* Check if there are any zeroes. If none, quit. */ + zerofound = FALSE; + for (i = 0; i < nc; i++) { + if (histo[i] == 0) { + zerofound = TRUE; + break; + } + } + if (!zerofound) { + LEPT_FREE(histo); + return 0; + } + + /* Generate mapping tables between indices */ + map1 = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32)); + map2 = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32)); + index = 0; + for (i = 0; i < nc; i++) { + if (histo[i] != 0) { + map1[index] = i; /* get old index from new */ + map2[i] = index; /* get new index from old */ + index++; + } + } + + /* Generate new colormap and attach to pixs */ + cmapd = pixcmapCreate(d); + for (i = 0; i < index; i++) { + pixcmapGetColor(cmap, map1[i], &rval, &gval, &bval); + pixcmapAddColor(cmapd, rval, gval, bval); + } + pixSetColormap(pixs, cmapd); + + /* Map pixel (index) values to new cmap */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + switch (d) + { + case 2: + val = GET_DATA_DIBIT(lines, j); + newval = map2[val]; + SET_DATA_DIBIT(lines, j, newval); + break; + case 4: + val = GET_DATA_QBIT(lines, j); + newval = map2[val]; + SET_DATA_QBIT(lines, j, newval); + break; + case 8: + val = GET_DATA_BYTE(lines, j); + newval = map2[val]; + SET_DATA_BYTE(lines, j, newval); + break; + default: + LEPT_FREE(histo); + LEPT_FREE(map1); + LEPT_FREE(map2); + return ERROR_INT("switch ran off end!", procName, 1); + } + } + } + + LEPT_FREE(histo); + LEPT_FREE(map1); + LEPT_FREE(map2); + return 0; +} + + +/*------------------------------------------------------------------* + * Find number of occupied octcubes at the specified level * + *------------------------------------------------------------------*/ +/*! + * \brief pixNumberOccupiedOctcubes() + * + * \param[in] pix 32 bpp + * \param[in] level of octcube + * \param[in] mincount minimum num pixels in an octcube to be counted; + * -1 to not use + * \param[in] minfract minimum fract of pixels in an octcube to be + * counted; -1 to not use + * \param[out] pncolors number of occupied octcubes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Exactly one of (%mincount, %minfract) must be -1, so, e.g.,
+ *          if %mincount == -1, then we use %minfract.
+ *      (2) If all occupied octcubes are to count, set %mincount == 1.
+ *          Setting %minfract == 0.0 is taken to mean the same thing.
+ * 
+ */ +l_ok +pixNumberOccupiedOctcubes(PIX *pix, + l_int32 level, + l_int32 mincount, + l_float32 minfract, + l_int32 *pncolors) +{ +l_int32 i, j, w, h, d, wpl, ncolors, size, octindex; +l_int32 rval, gval, bval; +l_int32 *carray; +l_uint32 *data, *line, *rtab, *gtab, *btab; + + PROCNAME("pixNumberOccupiedOctcubes"); + + if (!pncolors) + return ERROR_INT("&ncolors not defined", procName, 1); + *pncolors = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 32) + return ERROR_INT("pix not 32 bpp", procName, 1); + if (level < 1 || level > 6) + return ERROR_INT("invalid level", procName, 1); + if ((mincount < 0 && minfract < 0) || (mincount >= 0.0 && minfract >= 0.0)) + return ERROR_INT("invalid mincount/minfract", procName, 1); + if (mincount == 0 || minfract == 0.0) + mincount = 1; + else if (minfract > 0.0) + mincount = L_MIN(1, (l_int32)(minfract * w * h)); + + if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ + return ERROR_INT("size not returned", procName, 1); + rtab = gtab = btab = NULL; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + if ((carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) { + L_ERROR("carray not made\n", procName); + goto cleanup_arrays; + } + + /* Mark the occupied octcube leaves */ + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + octindex = rtab[rval] | gtab[gval] | btab[bval]; + carray[octindex]++; + } + } + + /* Count them */ + for (i = 0, ncolors = 0; i < size; i++) { + if (carray[i] >= mincount) + ncolors++; + } + *pncolors = ncolors; + +cleanup_arrays: + LEPT_FREE(carray); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colorquant2.c b/hgdriver/3rdparty/hgOCR/leptonica/colorquant2.c new file mode 100644 index 0000000..0650b79 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colorquant2.c @@ -0,0 +1,1674 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colorquant2.c + *
+ *
+ *  Modified median cut color quantization
+ *
+ *      High level
+ *          PIX              *pixMedianCutQuant()
+ *          PIX              *pixMedianCutQuantGeneral()
+ *          PIX              *pixMedianCutQuantMixed()
+ *          PIX              *pixFewColorsMedianCutQuantMixed()
+ *
+ *      Median cut indexed histogram
+ *          l_int32          *pixMedianCutHisto()
+ *
+ *      Static helpers
+ *          static PIXCMAP   *pixcmapGenerateFromHisto()
+ *          static PIX       *pixQuantizeWithColormap()
+ *          static void       getColorIndexMedianCut()
+ *          static L_BOX3D   *pixGetColorRegion()
+ *          static l_int32    medianCutApply()
+ *          static PIXCMAP   *pixcmapGenerateFromMedianCuts()
+ *          static l_int32    vboxGetAverageColor()
+ *          static l_int32    vboxGetCount()
+ *          static l_int32    vboxGetVolume()
+ *          static L_BOX3D   *box3dCreate();
+ *          static L_BOX3D   *box3dCopy();
+ *
+ *   Paul Heckbert published the median cut algorithm, "Color Image
+ *   Quantization for Frame Buffer Display," in Proc. SIGGRAPH '82,
+ *   Boston, July 1982, pp. 297-307.  See:
+ *   http://delivery.acm.org/10.1145/810000/801294/p297-heckbert.pdf
+ *
+ *   Median cut starts with either the full color space or the occupied
+ *   region of color space.  If you're not dithering, the occupied region
+ *   can be used, but with dithering, pixels can end up in any place
+ *   in the color space, so you must represent the entire color space in
+ *   the final colormap.
+ *
+ *   Color components are quantized to typically 5 or 6 significant
+ *   bits (for each of r, g and b).   Call a 3D region of color
+ *   space a 'vbox'.  Any color in this quantized space is represented
+ *   by an element of a linear histogram array, indexed by rgb value.
+ *   The initial region is then divided into two regions that have roughly
+ *   equal pixel occupancy (hence the name "median cut").  Subdivision
+ *   continues until the requisite number of vboxes has been generated.
+ *
+ *   But the devil is in the details of the subdivision process.
+ *   Here are some choices that you must make:
+ *     (1) Along which axis to subdivide?
+ *     (2) Which box to put the bin with the median pixel?
+ *     (3) How to order the boxes for subdivision?
+ *     (4) How to adequately handle boxes with very small numbers of pixels?
+ *     (5) How to prevent a little-represented but highly visible color
+ *         from being masked out by other colors in its vbox.
+ *
+ *   Taking these in order:
+ *     (1) Heckbert suggests using either the largest vbox side, or the vbox
+ *         side with the largest variance in pixel occupancy.  We choose
+ *         to divide based on the largest vbox side.
+ *     (2) Suppose you've chosen a side.  Then you have a histogram
+ *         of pixel occupancy in 2D slices of the vbox.  One of those
+ *         slices includes the median pixel.  Suppose there are L bins
+ *         to the left (smaller index) and R bins to the right.  Then
+ *         this slice (or bin) should be assigned to the box containing
+ *         the smaller of L and R.  This both shortens the larger
+ *         of the subdivided dimensions and helps a low-count color
+ *         far from the subdivision boundary to better express itself.
+ *     (2a) One can also ask if the boundary should be moved even
+ *         farther into the longer side.  This is feasible if we have
+ *         a method for doing extra subdivisions on the high count
+ *         vboxes.  And we do (see (3)).
+ *     (3) To make sure that the boxes are subdivided toward equal
+ *         occupancy, use an occupancy-sorted priority queue, rather
+ *         than a simple queue.
+ *     (4) With a priority queue, boxes with small number of pixels
+ *         won't be repeatedly subdivided.  This is good.
+ *     (5) Use of a priority queue allows tricks such as in (2a) to let
+ *         small occupancy clusters be better expressed.  In addition,
+ *         rather than splitting near the median, small occupancy colors
+ *         are best reproduced by cutting half-way into the longer side.
+ *
+ *   However, serious problems can arise with dithering if a priority
+ *   queue is used based on population alone.  If the picture has
+ *   large regions of nearly constant color, some vboxes can be very
+ *   large and have a sizeable population (but not big enough to get to
+ *   the head of the queue).  If one of these large, occupied vboxes
+ *   is near in color to a nearly constant color region of the
+ *   image, dithering can inject pixels from the large vbox into
+ *   the nearly uniform region.  These pixels can be very far away
+ *   in color, and the oscillations are highly visible.  To prevent
+ *   this, we can take either or both of these actions:
+ *
+ *     (1) Subdivide a fraction (< 1.0) based on population, and
+ *         do the rest of the subdivision based on the product of
+ *         the vbox volume and its population.  By using the product,
+ *         we avoid further subdivision of nearly empty vboxes, and
+ *         directly target large vboxes with significant population.
+ *
+ *     (2) Threshold the excess color transferred in dithering to
+ *         neighboring pixels.
+ *
+ *   Doing either of these will stop the most annoying oscillations
+ *   in dithering.  Furthermore, by doing (1), we also improve the
+ *   rendering of regions of nearly constant color, both with and
+ *   without dithering.  It turns out that the image quality is
+ *   not sensitive to the value of the parameter in (1); values
+ *   between 0.3 and 0.9 give very good results.
+ *
+ *   Here's the lesson: subdivide the color space into vboxes such
+ *   that (1) the most populated vboxes that can be further
+ *   subdivided (i.e., that occupy more than one quantum volume
+ *   in color space) all have approximately the same population,
+ *   and (2) all large vboxes have no significant population.
+ *   If these conditions are met, the quantization will be excellent.
+ *
+ *   Once the subdivision has been made, the colormap is generated,
+ *   with one color for each vbox and using the average color in the vbox.
+ *   At the same time, the histogram array is converted to an inverse
+ *   colormap table, storing the colormap index in every cell in the
+ *   vbox.  Finally, using both the colormap and the inverse colormap,
+ *   a colormapped pix is quickly generated from the original rgb pix.
+ *
+ *   In the present implementation, subdivided regions of colorspace
+ *   that are not occupied are retained, but not further subdivided.
+ *   This is required for our inverse colormap lookup table for
+ *   dithering, because dithered pixels may fall into these unoccupied
+ *   regions.  For such empty regions, we use the center as the rgb
+ *   colormap value.
+ *
+ *   This variation on median cut can be referred to as "Modified Median
+ *   Cut" quantization, or MMCQ.  Overall, the undithered MMCQ gives
+ *   comparable results to the two-pass Octcube Quantizer (OQ).
+ *   Comparing the two methods on the test24.jpg painting, we see:
+ *
+ *     (1) For rendering spot color (the various reds and pinks in
+ *         the image), MMCQ is not as good as OQ.
+ *
+ *     (2) For rendering majority color regions, MMCQ does a better
+ *         job of avoiding posterization.  That is, it does better
+ *         dividing the color space up in the most heavily populated regions.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Median cut 3-d volume element. Sort on first element, which + * can be the number of pixels, the volume or a combination + * of these. */ +struct L_Box3d +{ + l_float32 sortparam; /* parameter on which to sort the vbox */ + l_int32 npix; /* number of pixels in the vbox */ + l_int32 vol; /* quantized volume of vbox */ + l_int32 r1; /* min r index in the vbox */ + l_int32 r2; /* max r index in the vbox */ + l_int32 g1; /* min g index in the vbox */ + l_int32 g2; /* max g index in the vbox */ + l_int32 b1; /* min b index in the vbox */ + l_int32 b2; /* max b index in the vbox */ +}; +typedef struct L_Box3d L_BOX3D; + + /* Static median cut helper functions */ +static PIXCMAP *pixcmapGenerateFromHisto(PIX *pixs, l_int32 depth, + l_int32 *histo, l_int32 histosize, + l_int32 sigbits); +static PIX *pixQuantizeWithColormap(PIX *pixs, l_int32 ditherflag, + l_int32 outdepth, + PIXCMAP *cmap, l_int32 *indexmap, + l_int32 mapsize, l_int32 sigbits); +static void getColorIndexMedianCut(l_uint32 pixel, l_int32 rshift, + l_uint32 mask, l_int32 sigbits, + l_int32 *pindex); +static L_BOX3D *pixGetColorRegion(PIX *pixs, l_int32 sigbits, + l_int32 subsample); +static l_int32 medianCutApply(l_int32 *histo, l_int32 sigbits, + L_BOX3D *vbox, L_BOX3D **pvbox1, + L_BOX3D **pvbox2); +static PIXCMAP *pixcmapGenerateFromMedianCuts(L_HEAP *lh, l_int32 *histo, + l_int32 sigbits); +static l_int32 vboxGetAverageColor(L_BOX3D *vbox, l_int32 *histo, + l_int32 sigbits, l_int32 index, + l_int32 *prval, l_int32 *pgval, + l_int32 *pbval); +static l_int32 vboxGetCount(L_BOX3D *vbox, l_int32 *histo, l_int32 sigbits); +static l_int32 vboxGetVolume(L_BOX3D *vbox); +static L_BOX3D *box3dCreate(l_int32 r1, l_int32 r2, l_int32 g1, + l_int32 g2, l_int32 b1, l_int32 b2); +static L_BOX3D *box3dCopy(L_BOX3D *vbox); + + + /* 5 significant bits for each component is generally satisfactory */ +static const l_int32 DefaultSigBits = 5; +static const l_int32 MaxItersAllowed = 5000; /* prevents infinite looping */ + + /* Specify fraction of vboxes made that are sorted on population alone. + * The remaining vboxes are sorted on (population * vbox-volume). */ +static const l_float32 FractByPopulation = 0.85; + + /* To get the max value of 'dif' in the dithering color transfer, + * divide DifCap by 8. */ +static const l_int32 DifCap = 100; + + +#ifndef NO_CONSOLE_IO +#define DEBUG_MC_COLORS 0 +#define DEBUG_SPLIT_AXES 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------------* + * High level * + *------------------------------------------------------------------------*/ +/*! + * \brief pixMedianCutQuant() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] ditherflag 1 for dither; 0 for no dither + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Simple interface.  See pixMedianCutQuantGeneral() for
+ *          use of defaulted parameters.
+ * 
+ */ +PIX * +pixMedianCutQuant(PIX *pixs, + l_int32 ditherflag) +{ + return pixMedianCutQuantGeneral(pixs, ditherflag, + 0, 256, DefaultSigBits, 1, 1); +} + + +/*! + * \brief pixMedianCutQuantGeneral() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] ditherflag 1 for dither; 0 for no dither + * \param[in] outdepth output depth; valid: 0, 1, 2, 4, 8 + * \param[in] maxcolors between 2 and 256 + * \param[in] sigbits valid: 5 or 6; use 0 for default + * \param[in] maxsub max subsampling, integer; use 0 for default; + * 1 for no subsampling + * \param[in] checkbw 1 to check if color content is very small, + * 0 to assume there is sufficient color + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) %maxcolors must be in the range [2 ... 256].
+ *      (2) Use %outdepth = 0 to have the output depth computed as the
+ *          minimum required to hold the actual colors found, given
+ *          the %maxcolors constraint.
+ *      (3) Use %outdepth = 1, 2, 4 or 8 to specify the output depth.
+ *          In that case, %maxcolors must not exceed 2^(outdepth).
+ *      (4) If there are fewer quantized colors in the image than %maxcolors,
+ *          the colormap is simply generated from those colors.
+ *      (5) %maxsub is the maximum allowed subsampling to be used in the
+ *          computation of the color histogram and region of occupied
+ *          color space.  The subsampling is chosen internally for
+ *          efficiency, based on the image size, but this parameter
+ *          limits it.  Use %maxsub = 0 for the internal default, which is the
+ *          maximum allowed subsampling.  Use %maxsub = 1 to prevent
+ *          subsampling.  In general use %maxsub >= 1 to specify the
+ *          maximum subsampling to be allowed, where the actual subsampling
+ *          will be the minimum of this value and the internally
+ *          determined default value.
+ *      (6) If the image appears gray because either most of the pixels
+ *          are gray or most of the pixels are essentially black or white,
+ *          the image is trivially quantized with a grayscale colormap.  The
+ *          reason is that median cut divides the color space into rectangular
+ *          regions, and it does a very poor job if all the pixels are
+ *          near the diagonal of the color space cube.
+ * 
+ */ +PIX * +pixMedianCutQuantGeneral(PIX *pixs, + l_int32 ditherflag, + l_int32 outdepth, + l_int32 maxcolors, + l_int32 sigbits, + l_int32 maxsub, + l_int32 checkbw) +{ +l_int32 i, subsample, histosize, smalln, ncolors, niters, popcolors; +l_int32 w, h, minside, factor, index, rval, gval, bval; +l_int32 *histo; +l_float32 pixfract, colorfract; +L_BOX3D *vbox, *vbox1, *vbox2; +L_HEAP *lh, *lhs; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMedianCutQuantGeneral"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (maxcolors < 2 || maxcolors > 256) + return (PIX *)ERROR_PTR("maxcolors not in [2...256]", procName, NULL); + if (outdepth != 0 && outdepth != 1 && outdepth != 2 && outdepth != 4 && + outdepth != 8) + return (PIX *)ERROR_PTR("outdepth not in {0,1,2,4,8}", procName, NULL); + if (outdepth > 0 && (maxcolors > (1 << outdepth))) + return (PIX *)ERROR_PTR("maxcolors > 2^(outdepth)", procName, NULL); + if (sigbits == 0) + sigbits = DefaultSigBits; + else if (sigbits < 5 || sigbits > 6) + return (PIX *)ERROR_PTR("sigbits not 5 or 6", procName, NULL); + if (maxsub <= 0) + maxsub = 10; /* default will prevail for 10^7 pixels or less */ + + /* Determine if the image has sufficient color content. + * If pixfract << 1, most pixels are close to black or white. + * If colorfract << 1, the pixels that are not near + * black or white have very little color. + * If with little color, quantize with a grayscale colormap. */ + pixGetDimensions(pixs, &w, &h, NULL); + if (checkbw) { + minside = L_MIN(w, h); + factor = L_MAX(1, minside / 400); + pixColorFraction(pixs, 20, 244, 20, factor, &pixfract, &colorfract); + if (pixfract * colorfract < 0.00025) { + L_INFO("\n Pixel fraction neither white nor black = %6.3f" + "\n Color fraction of those pixels = %6.3f" + "\n Quantizing in gray\n", + procName, pixfract, colorfract); + return pixConvertTo8(pixs, 1); + } + } + + /* Compute the color space histogram. Default sampling + * is about 10^5 pixels. */ + if (maxsub == 1) { + subsample = 1; + } else { + subsample = (l_int32)(sqrt((l_float64)(w * h) / 100000.)); + subsample = L_MAX(1, L_MIN(maxsub, subsample)); + } + histo = pixMedianCutHisto(pixs, sigbits, subsample); + histosize = 1 << (3 * sigbits); + + /* See if the number of quantized colors is less than maxcolors */ + ncolors = 0; + smalln = TRUE; + for (i = 0; i < histosize; i++) { + if (histo[i]) + ncolors++; + if (ncolors > maxcolors) { + smalln = FALSE; + break; + } + } + if (smalln) { /* finish up now */ + if (outdepth == 0) { + if (ncolors <= 2) + outdepth = 1; + else if (ncolors <= 4) + outdepth = 2; + else if (ncolors <= 16) + outdepth = 4; + else + outdepth = 8; + } + cmap = pixcmapGenerateFromHisto(pixs, outdepth, + histo, histosize, sigbits); + pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap, + histo, histosize, sigbits); + LEPT_FREE(histo); + return pixd; + } + + /* Initial vbox: minimum region in colorspace occupied by pixels */ + if (ditherflag || subsample > 1) /* use full color space */ + vbox = box3dCreate(0, (1 << sigbits) - 1, + 0, (1 << sigbits) - 1, + 0, (1 << sigbits) - 1); + else + vbox = pixGetColorRegion(pixs, sigbits, subsample); + vbox->npix = vboxGetCount(vbox, histo, sigbits); + vbox->vol = vboxGetVolume(vbox); + + /* For a fraction 'popcolors' of the desired 'maxcolors', + * generate median cuts based on population, putting + * everything on a priority queue sorted by population. */ + lh = lheapCreate(0, L_SORT_DECREASING); + lheapAdd(lh, vbox); + ncolors = 1; + niters = 0; + popcolors = (l_int32)(FractByPopulation * maxcolors); + while (1) { + vbox = (L_BOX3D *)lheapRemove(lh); + if (vboxGetCount(vbox, histo, sigbits) == 0) { /* just put it back */ + lheapAdd(lh, vbox); + continue; + } + medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2); + if (!vbox1) { + L_WARNING("vbox1 not defined; shouldn't happen!\n", procName); + break; + } + if (vbox1->vol > 1) + vbox1->sortparam = vbox1->npix; + LEPT_FREE(vbox); + lheapAdd(lh, vbox1); + if (vbox2) { /* vbox2 can be NULL */ + if (vbox2->vol > 1) + vbox2->sortparam = vbox2->npix; + lheapAdd(lh, vbox2); + ncolors++; + } + if (ncolors >= popcolors) + break; + if (niters++ > MaxItersAllowed) { + L_WARNING("infinite loop; perhaps too few pixels!\n", procName); + break; + } + } + + /* Re-sort by the product of pixel occupancy times the size + * in color space. */ + lhs = lheapCreate(0, L_SORT_DECREASING); + while ((vbox = (L_BOX3D *)lheapRemove(lh))) { + vbox->sortparam = vbox->npix * vbox->vol; + lheapAdd(lhs, vbox); + } + lheapDestroy(&lh, TRUE); + + /* For the remaining (maxcolors - popcolors), generate the + * median cuts using the (npix * vol) sorting. */ + while (1) { + vbox = (L_BOX3D *)lheapRemove(lhs); + if (vboxGetCount(vbox, histo, sigbits) == 0) { /* just put it back */ + lheapAdd(lhs, vbox); + continue; + } + medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2); + if (!vbox1) { + L_WARNING("vbox1 not defined; shouldn't happen!\n", procName); + break; + } + if (vbox1->vol > 1) + vbox1->sortparam = vbox1->npix * vbox1->vol; + LEPT_FREE(vbox); + lheapAdd(lhs, vbox1); + if (vbox2) { /* vbox2 can be NULL */ + if (vbox2->vol > 1) + vbox2->sortparam = vbox2->npix * vbox2->vol; + lheapAdd(lhs, vbox2); + ncolors++; + } + if (ncolors >= maxcolors) + break; + if (niters++ > MaxItersAllowed) { + L_WARNING("infinite loop; perhaps too few pixels!\n", procName); + break; + } + } + + /* Re-sort by pixel occupancy. This is not necessary, + * but it makes a more useful listing. */ + lh = lheapCreate(0, L_SORT_DECREASING); + while ((vbox = (L_BOX3D *)lheapRemove(lhs))) { + vbox->sortparam = vbox->npix; +/* vbox->sortparam = vbox->npix * vbox->vol; */ + lheapAdd(lh, vbox); + } + lheapDestroy(&lhs, TRUE); + + /* Generate colormap from median cuts and quantize pixd */ + cmap = pixcmapGenerateFromMedianCuts(lh, histo, sigbits); + if (outdepth == 0) { + ncolors = pixcmapGetCount(cmap); + if (ncolors <= 2) + outdepth = 1; + else if (ncolors <= 4) + outdepth = 2; + else if (ncolors <= 16) + outdepth = 4; + else + outdepth = 8; + } + pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap, + histo, histosize, sigbits); + + /* Force darkest color to black if each component <= 4 */ + pixcmapGetRankIntensity(cmap, 0.0, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval < 5 && gval < 5 && bval < 5) + pixcmapResetColor(cmap, index, 0, 0, 0); + + /* Force lightest color to white if each component >= 252 */ + pixcmapGetRankIntensity(cmap, 1.0, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + if (rval > 251 && gval > 251 && bval > 251) + pixcmapResetColor(cmap, index, 255, 255, 255); + + lheapDestroy(&lh, TRUE); + LEPT_FREE(histo); + return pixd; +} + + +/*! + * \brief pixMedianCutQuantMixed() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] ncolor maximum number of colors assigned to + * pixels with significant color + * \param[in] ngray number of gray colors to be used; must be >= 2 + * \param[in] darkthresh threshold near black; if the lightest component + * is below this, the pixel is not considered to + * be gray or color; uses 0 for default + * \param[in] lightthresh threshold near white; if the darkest component + * is above this, the pixel is not considered to + * be gray or color; use 0 for default + * \param[in] diffthresh thresh for the max difference between component + * values; for differences below this, the pixel + * is considered to be gray; use 0 for default + * \return pixd 8 bpp cmapped, or NULL on error + * + *
+ * Notes:
+ *      (1) ncolor + ngray must not exceed 255.
+ *      (2) The method makes use of pixMedianCutQuantGeneral() with
+ *          minimal addition.
+ *          (a) Preprocess the image, setting all pixels with little color
+ *              to black, and populating an auxiliary 8 bpp image with the
+ *              expected colormap values corresponding to the set of
+ *              quantized gray values.
+ *          (b) Color quantize the altered input image to n + 1 colors.
+ *          (c) Augment the colormap with the gray indices, and
+ *              substitute the gray quantized values from the auxiliary
+ *              image for those in the color quantized output that had
+ *              been quantized as black.
+ *      (3) Median cut color quantization is relatively poor for grayscale
+ *          images with many colors, when compared to octcube quantization.
+ *          Thus, for images with both gray and color, it is important
+ *          to quantize the gray pixels by another method.  Here, we
+ *          are conservative in detecting color, preferring to use
+ *          a few extra bits to encode colorful pixels that push them
+ *          to gray.  This is particularly reasonable with this function,
+ *          because it handles the gray and color pixels separately,
+ *          using median cut color quantization for the color pixels
+ *          and equal-bin grayscale quantization for the non-color pixels.
+ * 
+ */ +PIX * +pixMedianCutQuantMixed(PIX *pixs, + l_int32 ncolor, + l_int32 ngray, + l_int32 darkthresh, + l_int32 lightthresh, + l_int32 diffthresh) +{ +l_int32 i, j, w, h, wplc, wplg, wpld, nc, unused, iscolor, factor, minside; +l_int32 rval, gval, bval, minval, maxval, val, grayval; +l_float32 pixfract, colorfract; +l_int32 *lut; +l_uint32 *datac, *datag, *datad, *linec, *lineg, *lined; +PIX *pixc, *pixg, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMedianCutQuantMixed"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (ngray < 2) + return (PIX *)ERROR_PTR("ngray < 2", procName, NULL); + if (ncolor + ngray > 255) + return (PIX *)ERROR_PTR("ncolor + ngray > 255", procName, NULL); + if (darkthresh <= 0) darkthresh = 20; + if (lightthresh <= 0) lightthresh = 244; + if (diffthresh <= 0) diffthresh = 20; + + /* First check if this should be quantized in gray. + * Use a more sensitive parameter for detecting color than with + * pixMedianCutQuantGeneral(), because this function can handle + * gray pixels well. */ + pixGetDimensions(pixs, &w, &h, NULL); + minside = L_MIN(w, h); + factor = L_MAX(1, minside / 400); + pixColorFraction(pixs, darkthresh, lightthresh, diffthresh, factor, + &pixfract, &colorfract); + if (pixfract * colorfract < 0.0001) { + L_INFO("\n Pixel fraction neither white nor black = %6.3f" + "\n Color fraction of those pixels = %6.3f" + "\n Quantizing in gray\n", + procName, pixfract, colorfract); + pixg = pixConvertTo8(pixs, 0); + pixd = pixThresholdOn8bpp(pixg, ngray, 1); + pixDestroy(&pixg); + return pixd; + } + + /* OK, there is color in the image. + * Preprocess to handle the gray pixels. Set the color pixels in pixc + * to black, and store their (eventual) colormap indices in pixg.*/ + pixc = pixCopy(NULL, pixs); + pixg = pixCreate(w, h, 8); /* color pixels will remain 0 here */ + datac = pixGetData(pixc); + datag = pixGetData(pixg); + wplc = pixGetWpl(pixc); + wplg = pixGetWpl(pixg); + lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) + lut[i] = ncolor + 1 + (i * (ngray - 1) + 128) / 255; + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + lineg = datag + i * wplg; + for (j = 0; j < w; j++) { + iscolor = FALSE; + extractRGBValues(linec[j], &rval, &gval, &bval); + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + if (maxval >= darkthresh && + minval <= lightthresh && + maxval - minval >= diffthresh) { + iscolor = TRUE; + } + if (!iscolor) { + linec[j] = 0x0; /* set to black */ + grayval = (maxval + minval) / 2; + SET_DATA_BYTE(lineg, j, lut[grayval]); + } + } + } + + /* Median cut on color pixels plus black */ + pixd = pixMedianCutQuantGeneral(pixc, FALSE, 8, ncolor + 1, + DefaultSigBits, 1, 0); + + /* Augment the colormap with gray values. The new cmap + * indices should agree with the values previously stored in pixg. */ + cmap = pixGetColormap(pixd); + nc = pixcmapGetCount(cmap); + unused = ncolor + 1 - nc; + if (unused < 0) + L_ERROR("Too many colors: extra = %d\n", procName, -unused); + if (unused > 0) { /* fill in with black; these won't be used */ + L_INFO("%d unused colors\n", procName, unused); + for (i = 0; i < unused; i++) + pixcmapAddColor(cmap, 0, 0, 0); + } + for (i = 0; i < ngray; i++) { + grayval = (255 * i) / (ngray - 1); + pixcmapAddColor(cmap, grayval, grayval, grayval); + } + + /* Substitute cmap indices for the gray pixels into pixd */ + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + lineg = datag + i * wplg; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lineg, j); /* if 0, it's a color pixel */ + if (val) + SET_DATA_BYTE(lined, j, val); + } + } + + pixDestroy(&pixc); + pixDestroy(&pixg); + LEPT_FREE(lut); + return pixd; +} + + +/*! + * \brief pixFewColorsMedianCutQuantMixed() + * + * \param[in] pixs 32 bpp rgb + * \param[in] ncolor number of colors to be assigned to pixels + * with significant color + * \param[in] ngray number of gray colors to be used; must be >= 2 + * \param[in] maxncolors maximum number of colors to be returned from + * pixColorsForQuantization(); use 0 for default + * \param[in] darkthresh threshold near black; if the lightest component + * is below this, the pixel is not considered to + * be gray or color; use 0 for default + * \param[in] lightthresh threshold near white; if the darkest component + * is above this, the pixel is not considered to + * be gray or color; use 0 for default + * \param[in] diffthresh thresh for the max difference between component + * values; for differences below this, the pixel + * is considered to be gray; use 0 for default + * \return pixd 8 bpp, median cut quantized for pixels that are + * not gray; gray pixels are quantized separately over + * the full gray range; null if too many colors or on error + * + *
+ * Notes:
+ *      (1) This is the "few colors" version of pixMedianCutQuantMixed().
+ *          It fails (returns NULL) if it finds more than maxncolors, but
+ *          otherwise it gives the same result.
+ *      (2) Recommended input parameters are:
+ *              %maxncolors:  20
+ *              %darkthresh:  20
+ *              %lightthresh: 244
+ *              %diffthresh:  15  (any higher can miss colors differing
+ *                                 slightly from gray)
+ *      (3) Both ncolor and ngray should be at least equal to maxncolors.
+ *          If they're not, they are automatically increased, and a
+ *          warning is given.
+ *      (4) If very little color content is found, the input is
+ *          converted to gray and quantized in equal intervals.
+ *      (5) This can be useful for quantizing orthographically generated
+ *          images such as color maps, where there may be more than 256 colors
+ *          because of aliasing or jpeg artifacts on text or lines, but
+ *          there are a relatively small number of solid colors.
+ *      (6) Example of usage:
+ *             // Try to quantize, using default values for mixed med cut
+ *             Pix *pixq = pixFewColorsMedianCutQuantMixed(pixs, 100, 20,
+ *                             0, 0, 0, 0);
+ *             if (!pixq)  // too many colors; don't quantize
+ *                 pixq = pixClone(pixs);
+ * 
+ */ +PIX * +pixFewColorsMedianCutQuantMixed(PIX *pixs, + l_int32 ncolor, + l_int32 ngray, + l_int32 maxncolors, + l_int32 darkthresh, + l_int32 lightthresh, + l_int32 diffthresh) +{ +l_int32 ncolors, iscolor; +PIX *pixg, *pixd; + + PROCNAME("pixFewColorsMedianCutQuantMixed"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (maxncolors <= 0) maxncolors = 20; + if (darkthresh <= 0) darkthresh = 20; + if (lightthresh <= 0) lightthresh = 244; + if (diffthresh <= 0) diffthresh = 15; + if (ncolor < maxncolors) { + L_WARNING("ncolor too small; setting to %d\n", procName, maxncolors); + ncolor = maxncolors; + } + if (ngray < maxncolors) { + L_WARNING("ngray too small; setting to %d\n", procName, maxncolors); + ngray = maxncolors; + } + + /* Estimate the color content and the number of colors required */ + pixColorsForQuantization(pixs, 15, &ncolors, &iscolor, 0); + + /* Note that maxncolors applies to all colors required to quantize, + * both gray and colorful */ + if (ncolors > maxncolors) + return (PIX *)ERROR_PTR("too many colors", procName, NULL); + + /* If no color, return quantized gray pix */ + if (!iscolor) { + pixg = pixConvertTo8(pixs, 0); + pixd = pixThresholdOn8bpp(pixg, ngray, 1); + pixDestroy(&pixg); + return pixd; + } + + /* Use the mixed gray/color quantizer */ + return pixMedianCutQuantMixed(pixs, ncolor, ngray, darkthresh, + lightthresh, diffthresh); +} + + + +/*------------------------------------------------------------------------* + * Median cut indexed histogram * + *------------------------------------------------------------------------*/ +/*! + * \brief pixMedianCutHisto() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] sigbits valid: 5 or 6 + * \param[in] subsample integer > 0 + * \return histo 1-d array, giving the number of pixels in each + * quantized region of color space, or NULL on error + * + *
+ * Notes:
+ *      (1) Array is indexed by (3 * sigbits) bits.  The array size
+ *          is 2^(3 * sigbits).
+ *      (2) Indexing into the array from rgb uses red sigbits as
+ *          most significant and blue as least.
+ * 
+ */ +l_int32 * +pixMedianCutHisto(PIX *pixs, + l_int32 sigbits, + l_int32 subsample) +{ +l_int32 i, j, w, h, wpl, rshift, index, histosize; +l_int32 *histo; +l_uint32 mask, pixel; +l_uint32 *data, *line; + + PROCNAME("pixMedianCutHisto"); + + if (!pixs) + return (l_int32 *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (l_int32 *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (sigbits < 5 || sigbits > 6) + return (l_int32 *)ERROR_PTR("sigbits not 5 or 6", procName, NULL); + if (subsample <= 0) + return (l_int32 *)ERROR_PTR("subsample not > 0", procName, NULL); + + histosize = 1 << (3 * sigbits); + if ((histo = (l_int32 *)LEPT_CALLOC(histosize, sizeof(l_int32))) == NULL) + return (l_int32 *)ERROR_PTR("histo not made", procName, NULL); + + rshift = 8 - sigbits; + mask = 0xff >> rshift; + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i += subsample) { + line = data + i * wpl; + for (j = 0; j < w; j += subsample) { + pixel = line[j]; + getColorIndexMedianCut(pixel, rshift, mask, sigbits, &index); + histo[index]++; + } + } + + return histo; +} + + +/*------------------------------------------------------------------------* + * Static helpers * + *------------------------------------------------------------------------*/ +/*! + * \brief pixcmapGenerateFromHisto() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] depth of colormap + * \param[in] histo + * \param[in] histosize + * \param[in] sigbits + * \return colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) This is used when the number of colors in the histo
+ *          is not greater than maxcolors.
+ *      (2) As a side-effect, the histo becomes an inverse colormap,
+ *          labeling the cmap indices for each existing color.
+ * 
+ */ +static PIXCMAP * +pixcmapGenerateFromHisto(PIX *pixs, + l_int32 depth, + l_int32 *histo, + l_int32 histosize, + l_int32 sigbits) +{ +l_int32 i, index, shift, rval, gval, bval; +l_uint32 mask; +PIXCMAP *cmap; + + PROCNAME("pixcmapGenerateFromHisto"); + + if (!pixs) + return (PIXCMAP *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIXCMAP *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (!histo) + return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL); + + /* Capture the rgb values of each occupied cube in the histo, + * and re-label the histo value with the colormap index. */ + cmap = pixcmapCreate(depth); + shift = 8 - sigbits; + mask = 0xff >> shift; + for (i = 0, index = 0; i < histosize; i++) { + if (histo[i]) { + rval = (i >> (2 * sigbits)) << shift; + gval = ((i >> sigbits) & mask) << shift; + bval = (i & mask) << shift; + pixcmapAddColor(cmap, rval, gval, bval); + histo[i] = index++; + } + } + + return cmap; +} + + +/*! + * \brief pixQuantizeWithColormap() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] ditherflag 1 for dither; 0 for no dither + * \param[in] outdepth depth of the returned pixd + * \param[in] cmap colormap + * \param[in] indexmap lookup table + * \param[in] mapsize size of the lookup table + * \param[in] sigbits significant bits in output + * \return pixd quantized to colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) The indexmap is a LUT that takes the rgb indices of the
+ *          pixel and returns the index into the colormap.
+ *      (2) If ditherflag is 1, %outdepth is ignored and the output
+ *          depth is set to 8.
+ * 
+ */ +static PIX * +pixQuantizeWithColormap(PIX *pixs, + l_int32 ditherflag, + l_int32 outdepth, + PIXCMAP *cmap, + l_int32 *indexmap, + l_int32 mapsize, + l_int32 sigbits) +{ +l_uint8 *bufu8r, *bufu8g, *bufu8b; +l_int32 i, j, w, h, wpls, wpld, rshift, index, cmapindex, success; +l_int32 rval, gval, bval, rc, gc, bc; +l_int32 dif, val1, val2, val3; +l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; +l_uint32 *datas, *datad, *lines, *lined; +l_uint32 mask, pixel; +PIX *pixd; + + PROCNAME("pixQuantizeWithColormap"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (!cmap) + return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); + if (!indexmap) + return (PIX *)ERROR_PTR("indexmap not defined", procName, NULL); + if (ditherflag) + outdepth = 8; + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, outdepth); + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + rshift = 8 - sigbits; + mask = 0xff >> rshift; + if (ditherflag == 0) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (outdepth == 1) { + for (j = 0; j < w; j++) { + pixel = lines[j]; + getColorIndexMedianCut(pixel, rshift, mask, + sigbits, &index); + if (indexmap[index]) + SET_DATA_BIT(lined, j); + } + } else if (outdepth == 2) { + for (j = 0; j < w; j++) { + pixel = lines[j]; + getColorIndexMedianCut(pixel, rshift, mask, + sigbits, &index); + SET_DATA_DIBIT(lined, j, indexmap[index]); + } + } else if (outdepth == 4) { + for (j = 0; j < w; j++) { + pixel = lines[j]; + getColorIndexMedianCut(pixel, rshift, mask, + sigbits, &index); + SET_DATA_QBIT(lined, j, indexmap[index]); + } + } else { /* outdepth == 8 */ + for (j = 0; j < w; j++) { + pixel = lines[j]; + getColorIndexMedianCut(pixel, rshift, mask, + sigbits, &index); + SET_DATA_BYTE(lined, j, indexmap[index]); + } + } + } + } else { /* ditherflag == 1 */ + success = TRUE; + bufu8r = bufu8g = bufu8b = NULL; + buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; + bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); + buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || + !buf1b || !buf2r || !buf2g || !buf2b) { + L_ERROR("buffer not made\n", procName); + success = FALSE; + goto buffer_cleanup; + } + + /* Start by priming buf2; line 1 is above line 2 */ + pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + for (i = 0; i < h - 1; i++) { + /* Swap data 2 --> 1, and read in new line 2 */ + memcpy(buf1r, buf2r, 4 * w); + memcpy(buf1g, buf2g, 4 * w); + memcpy(buf1b, buf2b, 4 * w); + pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); + for (j = 0; j < w; j++) { + buf2r[j] = 64 * bufu8r[j]; + buf2g[j] = 64 * bufu8g[j]; + buf2b[j] = 64 * bufu8b[j]; + } + + /* Dither */ + lined = datad + i * wpld; + for (j = 0; j < w - 1; j++) { + rval = buf1r[j] / 64; + gval = buf1g[j] / 64; + bval = buf1b[j] / 64; + index = ((rval >> rshift) << (2 * sigbits)) + + ((gval >> rshift) << sigbits) + (bval >> rshift); + cmapindex = indexmap[index]; + SET_DATA_BYTE(lined, j, cmapindex); + pixcmapGetColor(cmap, cmapindex, &rc, &gc, &bc); + + dif = buf1r[j] / 8 - 8 * rc; + if (dif > DifCap) dif = DifCap; + if (dif < -DifCap) dif = -DifCap; + if (dif != 0) { + val1 = buf1r[j + 1] + 3 * dif; + val2 = buf2r[j] + 3 * dif; + val3 = buf2r[j + 1] + 2 * dif; + if (dif > 0) { + buf1r[j + 1] = L_MIN(16383, val1); + buf2r[j] = L_MIN(16383, val2); + buf2r[j + 1] = L_MIN(16383, val3); + } else { + buf1r[j + 1] = L_MAX(0, val1); + buf2r[j] = L_MAX(0, val2); + buf2r[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1g[j] / 8 - 8 * gc; + if (dif > DifCap) dif = DifCap; + if (dif < -DifCap) dif = -DifCap; + if (dif != 0) { + val1 = buf1g[j + 1] + 3 * dif; + val2 = buf2g[j] + 3 * dif; + val3 = buf2g[j + 1] + 2 * dif; + if (dif > 0) { + buf1g[j + 1] = L_MIN(16383, val1); + buf2g[j] = L_MIN(16383, val2); + buf2g[j + 1] = L_MIN(16383, val3); + } else { + buf1g[j + 1] = L_MAX(0, val1); + buf2g[j] = L_MAX(0, val2); + buf2g[j + 1] = L_MAX(0, val3); + } + } + + dif = buf1b[j] / 8 - 8 * bc; + if (dif > DifCap) dif = DifCap; + if (dif < -DifCap) dif = -DifCap; + if (dif != 0) { + val1 = buf1b[j + 1] + 3 * dif; + val2 = buf2b[j] + 3 * dif; + val3 = buf2b[j + 1] + 2 * dif; + if (dif > 0) { + buf1b[j + 1] = L_MIN(16383, val1); + buf2b[j] = L_MIN(16383, val2); + buf2b[j + 1] = L_MIN(16383, val3); + } else { + buf1b[j + 1] = L_MAX(0, val1); + buf2b[j] = L_MAX(0, val2); + buf2b[j + 1] = L_MAX(0, val3); + } + } + } + + /* Get last pixel in row; no downward propagation */ + rval = buf1r[w - 1] / 64; + gval = buf1g[w - 1] / 64; + bval = buf1b[w - 1] / 64; + index = ((rval >> rshift) << (2 * sigbits)) + + ((gval >> rshift) << sigbits) + (bval >> rshift); + SET_DATA_BYTE(lined, w - 1, indexmap[index]); + } + + /* Get last row of pixels; no leftward propagation */ + lined = datad + (h - 1) * wpld; + for (j = 0; j < w; j++) { + rval = buf2r[j] / 64; + gval = buf2g[j] / 64; + bval = buf2b[j] / 64; + index = ((rval >> rshift) << (2 * sigbits)) + + ((gval >> rshift) << sigbits) + (bval >> rshift); + SET_DATA_BYTE(lined, j, indexmap[index]); + } + +buffer_cleanup: + LEPT_FREE(bufu8r); + LEPT_FREE(bufu8g); + LEPT_FREE(bufu8b); + LEPT_FREE(buf1r); + LEPT_FREE(buf1g); + LEPT_FREE(buf1b); + LEPT_FREE(buf2r); + LEPT_FREE(buf2g); + LEPT_FREE(buf2b); + if (!success) pixDestroy(&pixd); + } + + return pixd; +} + + +/*! + * \brief getColorIndexMedianCut() + * + * \param[in] pixel 32 bit rgb + * \param[in] rshift of component: 8 - sigbits + * \param[in] mask over sigbits + * \param[in] sigbits + * \param[out] pindex rgb index value + * \return void + * + *
+ * Notes:
+ *      (1) This is used on each pixel in the source image.  No checking
+ *          is done on input values.
+ * 
+ */ +static void +getColorIndexMedianCut(l_uint32 pixel, + l_int32 rshift, + l_uint32 mask, + l_int32 sigbits, + l_int32 *pindex) +{ +l_int32 rval, gval, bval; + + rval = pixel >> (24 + rshift); + gval = (pixel >> (16 + rshift)) & mask; + bval = (pixel >> (8 + rshift)) & mask; + *pindex = (rval << (2 * sigbits)) + (gval << sigbits) + bval; + return; +} + + +/*! + * \brief pixGetColorRegion() + * + * \param[in] pixs 32 bpp; rgb color + * \param[in] sigbits valid: 5, 6 + * \param[in] subsample integer > 0 + * \return vbox minimum 3D box in color space enclosing all pixels, + * or NULL on error + * + *
+ * Notes:
+ *      (1) Computes the minimum 3D box in color space enclosing all
+ *          pixels in the image.
+ * 
+ */ +static L_BOX3D * +pixGetColorRegion(PIX *pixs, + l_int32 sigbits, + l_int32 subsample) +{ +l_int32 rmin, rmax, gmin, gmax, bmin, bmax, rval, gval, bval; +l_int32 w, h, wpl, i, j, rshift; +l_uint32 mask, pixel; +l_uint32 *data, *line; + + PROCNAME("pixGetColorRegion"); + + if (!pixs) + return (L_BOX3D *)ERROR_PTR("pixs not defined", procName, NULL); + + rmin = gmin = bmin = 1000000; + rmax = gmax = bmax = 0; + rshift = 8 - sigbits; + mask = 0xff >> rshift; + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i += subsample) { + line = data + i * wpl; + for (j = 0; j < w; j += subsample) { + pixel = line[j]; + rval = pixel >> (24 + rshift); + gval = (pixel >> (16 + rshift)) & mask; + bval = (pixel >> (8 + rshift)) & mask; + if (rval < rmin) + rmin = rval; + else if (rval > rmax) + rmax = rval; + if (gval < gmin) + gmin = gval; + else if (gval > gmax) + gmax = gval; + if (bval < bmin) + bmin = bval; + else if (bval > bmax) + bmax = bval; + } + } + + return box3dCreate(rmin, rmax, gmin, gmax, bmin, bmax); +} + + +/*! + * \brief medianCutApply() + * + * \param[in] histo array; in rgb colorspace + * \param[in] sigbits + * \param[in] vbox input 3D box + * \param[out] pvbox1, pvbox2 vbox split in two parts + * \return 0 if OK, 1 on error + */ +static l_int32 +medianCutApply(l_int32 *histo, + l_int32 sigbits, + L_BOX3D *vbox, + L_BOX3D **pvbox1, + L_BOX3D **pvbox2) +{ +l_int32 i, j, k, sum, rw, gw, bw, maxw, index; +l_int32 total, left, right; +l_int32 partialsum[128]; +L_BOX3D *vbox1, *vbox2; + + PROCNAME("medianCutApply"); + + if (pvbox1) *pvbox1 = NULL; + if (pvbox2) *pvbox2 = NULL; + if (!histo) + return ERROR_INT("histo not defined", procName, 1); + if (!vbox) + return ERROR_INT("vbox not defined", procName, 1); + if (!pvbox1 || !pvbox2) + return ERROR_INT("&vbox1 and &vbox2 not both defined", procName, 1); + + if (vboxGetCount(vbox, histo, sigbits) == 0) + return ERROR_INT("no pixels in vbox", procName, 1); + + /* If the vbox occupies just one element in color space, it can't + * be split. Leave the 'sortparam' field at 0, so that it goes to + * the tail of the priority queue and stays there, thereby avoiding + * an infinite loop (take off, put back on the head) if it + * happens to be the most populous box! */ + rw = vbox->r2 - vbox->r1 + 1; + gw = vbox->g2 - vbox->g1 + 1; + bw = vbox->b2 - vbox->b1 + 1; + if (rw == 1 && gw == 1 && bw == 1) { + *pvbox1 = box3dCopy(vbox); + return 0; + } + + /* Select the longest axis for splitting */ + maxw = L_MAX(rw, gw); + maxw = L_MAX(maxw, bw); +#if DEBUG_SPLIT_AXES + if (rw == maxw) + fprintf(stderr, "red split\n"); + else if (gw == maxw) + fprintf(stderr, "green split\n"); + else + fprintf(stderr, "blue split\n"); +#endif /* DEBUG_SPLIT_AXES */ + + /* Find the partial sum arrays along the selected axis. */ + total = 0; + if (maxw == rw) { + for (i = vbox->r1; i <= vbox->r2; i++) { + sum = 0; + for (j = vbox->g1; j <= vbox->g2; j++) { + for (k = vbox->b1; k <= vbox->b2; k++) { + index = (i << (2 * sigbits)) + (j << sigbits) + k; + sum += histo[index]; + } + } + total += sum; + partialsum[i] = total; + } + } else if (maxw == gw) { + for (i = vbox->g1; i <= vbox->g2; i++) { + sum = 0; + for (j = vbox->r1; j <= vbox->r2; j++) { + for (k = vbox->b1; k <= vbox->b2; k++) { + index = (i << sigbits) + (j << (2 * sigbits)) + k; + sum += histo[index]; + } + } + total += sum; + partialsum[i] = total; + } + } else { /* maxw == bw */ + for (i = vbox->b1; i <= vbox->b2; i++) { + sum = 0; + for (j = vbox->r1; j <= vbox->r2; j++) { + for (k = vbox->g1; k <= vbox->g2; k++) { + index = i + (j << (2 * sigbits)) + (k << sigbits); + sum += histo[index]; + } + } + total += sum; + partialsum[i] = total; + } + } + + /* Determine the cut planes, making sure that two vboxes + * are always produced. Generate the two vboxes and compute + * the sum in each of them. Choose the cut plane within + * the greater of the (left, right) sides of the bin in which + * the median pixel resides. Here's the surprise: go halfway + * into that side. By doing that, you technically move away + * from "median cut," but in the process a significant number + * of low-count vboxes are produced, allowing much better + * reproduction of low-count spot colors. */ + vbox1 = vbox2 = NULL; + if (maxw == rw) { + for (i = vbox->r1; i <= vbox->r2; i++) { + if (partialsum[i] > total / 2) { + vbox1 = box3dCopy(vbox); + vbox2 = box3dCopy(vbox); + left = i - vbox->r1; + right = vbox->r2 - i; + if (left <= right) + vbox1->r2 = L_MIN(vbox->r2 - 1, i + right / 2); + else /* left > right */ + vbox1->r2 = L_MAX(vbox->r1, i - 1 - left / 2); + vbox2->r1 = vbox1->r2 + 1; + break; + } + } + } else if (maxw == gw) { + for (i = vbox->g1; i <= vbox->g2; i++) { + if (partialsum[i] > total / 2) { + vbox1 = box3dCopy(vbox); + vbox2 = box3dCopy(vbox); + left = i - vbox->g1; + right = vbox->g2 - i; + if (left <= right) + vbox1->g2 = L_MIN(vbox->g2 - 1, i + right / 2); + else /* left > right */ + vbox1->g2 = L_MAX(vbox->g1, i - 1 - left / 2); + vbox2->g1 = vbox1->g2 + 1; + break; + } + } + } else { /* maxw == bw */ + for (i = vbox->b1; i <= vbox->b2; i++) { + if (partialsum[i] > total / 2) { + vbox1 = box3dCopy(vbox); + vbox2 = box3dCopy(vbox); + left = i - vbox->b1; + right = vbox->b2 - i; + if (left <= right) + vbox1->b2 = L_MIN(vbox->b2 - 1, i + right / 2); + else /* left > right */ + vbox1->b2 = L_MAX(vbox->b1, i - 1 - left / 2); + vbox2->b1 = vbox1->b2 + 1; + break; + } + } + } + *pvbox1 = vbox1; + *pvbox2 = vbox2; + if (!vbox1) + return ERROR_INT("vbox1 not made; shouldn't happen", procName, 1); + if (!vbox2) + return ERROR_INT("vbox2 not made; shouldn't happen", procName, 1); + vbox1->npix = vboxGetCount(vbox1, histo, sigbits); + vbox2->npix = vboxGetCount(vbox2, histo, sigbits); + vbox1->vol = vboxGetVolume(vbox1); + vbox2->vol = vboxGetVolume(vbox2); + + return 0; +} + + +/*! + * \brief pixcmapGenerateFromMedianCuts() + * + * \param[in] lh priority queue of pointers to vboxes + * \param[in] histo + * \param[in] sigbits valid: 5 or 6 + * \return cmap, or NULL on error + * + *
+ * Notes:
+ *      (1) Each vbox in the heap represents a color in the colormap.
+ *      (2) As a side-effect, the histo becomes an inverse colormap,
+ *          where the part of the array correpsonding to each vbox
+ *          is labeled with the cmap index for that vbox.  Then
+ *          for each rgb pixel, the colormap index is found directly
+ *          by mapping the rgb value to the histo array index.
+ * 
+ */ +static PIXCMAP * +pixcmapGenerateFromMedianCuts(L_HEAP *lh, + l_int32 *histo, + l_int32 sigbits) +{ +l_int32 index, rval, gval, bval; +L_BOX3D *vbox; +PIXCMAP *cmap; + + PROCNAME("pixcmapGenerateFromMedianCuts"); + + if (!lh) + return (PIXCMAP *)ERROR_PTR("lh not defined", procName, NULL); + if (!histo) + return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL); + + rval = gval = bval = 0; /* make compiler happy */ + cmap = pixcmapCreate(8); + index = 0; + while (lheapGetCount(lh) > 0) { + vbox = (L_BOX3D *)lheapRemove(lh); + vboxGetAverageColor(vbox, histo, sigbits, index, &rval, &gval, &bval); + pixcmapAddColor(cmap, rval, gval, bval); + LEPT_FREE(vbox); + index++; + } + + return cmap; +} + + +/*! + * \brief vboxGetAverageColor() + * + * \param[in] vbox 3d region of color space for one quantized color + * \param[in] histo + * \param[in] sigbits valid: 5 or 6 + * \param[in] index if >= 0, assign to all colors in histo in this vbox + * \param[out] prval, pgval, pbval average color + * \return cmap, or NULL on error + * + *
+ * Notes:
+ *      (1) The vbox represents one color in the colormap.
+ *      (2) If index >= 0, as a side-effect, all array elements in
+ *          the histo corresponding to the vbox are labeled with this
+ *          cmap index for that vbox.  Otherwise, the histo array
+ *          is not changed.
+ *      (3) The vbox is quantized in sigbits.  So the actual 8-bit color
+ *          components are found by multiplying the quantized value
+ *          by either 4 or 8.  We must add 0.5 to the quantized index
+ *          before multiplying to get the approximate 8-bit color in
+ *          the center of the vbox; otherwise we get values on
+ *          the lower corner.
+ * 
+ */ +static l_int32 +vboxGetAverageColor(L_BOX3D *vbox, + l_int32 *histo, + l_int32 sigbits, + l_int32 index, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 i, j, k, ntot, mult, histoindex, rsum, gsum, bsum; + + PROCNAME("vboxGetAverageColor"); + + if (!vbox) + return ERROR_INT("vbox not defined", procName, 1); + if (!histo) + return ERROR_INT("histo not defined", procName, 1); + if (!prval || !pgval || !pbval) + return ERROR_INT("&p*val not all defined", procName, 1); + + *prval = *pgval = *pbval = 0; + ntot = 0; + mult = 1 << (8 - sigbits); + rsum = gsum = bsum = 0; + for (i = vbox->r1; i <= vbox->r2; i++) { + for (j = vbox->g1; j <= vbox->g2; j++) { + for (k = vbox->b1; k <= vbox->b2; k++) { + histoindex = (i << (2 * sigbits)) + (j << sigbits) + k; + ntot += histo[histoindex]; + rsum += (l_int32)(histo[histoindex] * (i + 0.5) * mult); + gsum += (l_int32)(histo[histoindex] * (j + 0.5) * mult); + bsum += (l_int32)(histo[histoindex] * (k + 0.5) * mult); + if (index >= 0) + histo[histoindex] = index; + } + } + } + + if (ntot == 0) { + *prval = mult * (vbox->r1 + vbox->r2 + 1) / 2; + *pgval = mult * (vbox->g1 + vbox->g2 + 1) / 2; + *pbval = mult * (vbox->b1 + vbox->b2 + 1) / 2; + } else { + *prval = rsum / ntot; + *pgval = gsum / ntot; + *pbval = bsum / ntot; + } + +#if DEBUG_MC_COLORS + fprintf(stderr, "ntot[%d] = %d: [%d, %d, %d], (%d, %d, %d)\n", + index, ntot, vbox->r2 - vbox->r1 + 1, + vbox->g2 - vbox->g1 + 1, vbox->b2 - vbox->b1 + 1, + *prval, *pgval, *pbval); +#endif /* DEBUG_MC_COLORS */ + + return 0; +} + + +/*! + * \brief vboxGetCount() + * + * \param[in] vbox 3d region of color space for one quantized color + * \param[in] histo + * \param[in] sigbits valid: 5 or 6 + * \return number of image pixels in this region, or 0 on error + */ +static l_int32 +vboxGetCount(L_BOX3D *vbox, + l_int32 *histo, + l_int32 sigbits) +{ +l_int32 i, j, k, npix, index; + + PROCNAME("vboxGetCount"); + + if (!vbox) + return ERROR_INT("vbox not defined", procName, 0); + if (!histo) + return ERROR_INT("histo not defined", procName, 0); + + npix = 0; + for (i = vbox->r1; i <= vbox->r2; i++) { + for (j = vbox->g1; j <= vbox->g2; j++) { + for (k = vbox->b1; k <= vbox->b2; k++) { + index = (i << (2 * sigbits)) + (j << sigbits) + k; + npix += histo[index]; + } + } + } + + return npix; +} + + +/*! + * \brief vboxGetVolume() + * + * \param[in] vbox 3d region of color space for one quantized color + * \return quantized volume of vbox, or 0 on error + */ +static l_int32 +vboxGetVolume(L_BOX3D *vbox) +{ + PROCNAME("vboxGetVolume"); + + if (!vbox) + return ERROR_INT("vbox not defined", procName, 0); + + return ((vbox->r2 - vbox->r1 + 1) * (vbox->g2 - vbox->g1 + 1) * + (vbox->b2 - vbox->b1 + 1)); +} + +/*! + * \brief box3dCreate() + * + * \param[in] r1, r2, g1, g2, b1, b2 initial values + * \return vbox + */ +static L_BOX3D * +box3dCreate(l_int32 r1, + l_int32 r2, + l_int32 g1, + l_int32 g2, + l_int32 b1, + l_int32 b2) +{ +L_BOX3D *vbox; + + vbox = (L_BOX3D *)LEPT_CALLOC(1, sizeof(L_BOX3D)); + vbox->r1 = r1; + vbox->r2 = r2; + vbox->g1 = g1; + vbox->g2 = g2; + vbox->b1 = b1; + vbox->b2 = b2; + return vbox; +} + + +/*! + * \brief box3dCopy() + * + * \param[in] vbox + * \return vboxc copy of vbox + * + *
+ * Notes:
+ *      Don't copy the sortparam.
+ * 
+ */ +static L_BOX3D * +box3dCopy(L_BOX3D *vbox) +{ +L_BOX3D *vboxc; + + PROCNAME("box3dCopy"); + + if (!vbox) + return (L_BOX3D *)ERROR_PTR("vbox not defined", procName, NULL); + + vboxc = box3dCreate(vbox->r1, vbox->r2, vbox->g1, vbox->g2, + vbox->b1, vbox->b2); + vboxc->npix = vbox->npix; + vboxc->vol = vbox->vol; + return vboxc; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colorseg.c b/hgdriver/3rdparty/hgOCR/leptonica/colorseg.c new file mode 100644 index 0000000..f7372d2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colorseg.c @@ -0,0 +1,654 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colorseg.c + *
+ *
+ *    Unsupervised color segmentation
+ *
+ *               PIX     *pixColorSegment()
+ *               PIX     *pixColorSegmentCluster()
+ *       static  l_int32  pixColorSegmentTryCluster()
+ *               l_int32  pixAssignToNearestColor()
+ *               l_int32  pixColorSegmentClean()
+ *               l_int32  pixColorSegmentRemoveColors()
+ * 
+ */ + +#include "allheaders.h" + + /* Maximum allowed iterations in Phase 1. */ +static const l_int32 MAX_ALLOWED_ITERATIONS = 20; + + /* Factor by which max dist is increased on each iteration */ +static const l_float32 DIST_EXPAND_FACT = 1.3; + + /* Octcube division level for computing nearest colormap color using LUT. + * Using 4 should suffice for up to 50 - 100 colors, and it is + * very fast. Using 5 takes 8 times as long to set up the LUT + * for little perceptual gain, even with 100 colors. */ +static const l_int32 LEVEL_IN_OCTCUBE = 4; + + +static l_int32 pixColorSegmentTryCluster(PIX *pixd, PIX *pixs, + l_int32 maxdist, l_int32 maxcolors, + l_int32 debugflag); + +/*------------------------------------------------------------------* + * Unsupervised color segmentation * + *------------------------------------------------------------------*/ +/*! + * \brief pixColorSegment() + * + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] maxdist max euclidean dist to existing cluster + * \param[in] maxcolors max number of colors allowed in first pass + * \param[in] selsize linear size of sel for closing to remove noise + * \param[in] finalcolors max number of final colors allowed after 4th pass + * \param[in] debugflag 1 for debug output; 0 otherwise + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ *  Color segmentation proceeds in four phases:
+ *
+ *  Phase 1:  pixColorSegmentCluster()
+ *  The image is traversed in raster order.  Each pixel either
+ *  becomes the representative for a new cluster or is assigned to an
+ *  existing cluster.  Assignment is greedy.  The data is stored in
+ *  a colormapped image.  Three auxiliary arrays are used to hold
+ *  the colors of the representative pixels, for fast lookup.
+ *  The average color in each cluster is computed.
+ *
+ *  Phase 2.  pixAssignToNearestColor()
+ *  A second non-greedy clustering pass is performed, where each pixel
+ *  is assigned to the nearest cluster average.  We also keep track
+ *  of how many pixels are assigned to each cluster.
+ *
+ *  Phase 3.  pixColorSegmentClean()
+ *  For each cluster, starting with the largest, do a morphological
+ *  closing to eliminate small components within larger ones.
+ *
+ *  Phase 4.  pixColorSegmentRemoveColors()
+ *  Eliminate all colors except the most populated 'finalcolors'.
+ *  Then remove unused colors from the colormap, and reassign those
+ *  pixels to the nearest remaining cluster, using the original pixel values.
+ *
+ * Notes:
+ *      (1) The goal is to generate a small number of colors.
+ *          Typically this would be specified by 'finalcolors',
+ *          a number that would be somewhere between 3 and 6.
+ *          The parameter 'maxcolors' specifies the maximum number of
+ *          colors generated in the first phase.  This should be
+ *          larger than finalcolors, perhaps twice as large.
+ *          If more than 'maxcolors' are generated in the first phase
+ *          using the input 'maxdist', the distance is repeatedly
+ *          increased by a multiplicative factor until the condition
+ *          is satisfied.  The implicit relation between 'maxdist'
+ *          and 'maxcolors' is thus adjusted programmatically.
+ *      (2) As a very rough guideline, given a target value of 'finalcolors',
+ *          here are approximate values of 'maxdist' and 'maxcolors'
+ *          to start with:
+ *
+ *               finalcolors    maxcolors    maxdist
+ *               -----------    ---------    -------
+ *                   3             6          100
+ *                   4             8           90
+ *                   5            10           75
+ *                   6            12           60
+ *
+ *          For a given number of finalcolors, if you use too many
+ *          maxcolors, the result will be noisy.  If you use too few,
+ *          the result will be a relatively poor assignment of colors.
+ * 
+ */ +PIX * +pixColorSegment(PIX *pixs, + l_int32 maxdist, + l_int32 maxcolors, + l_int32 selsize, + l_int32 finalcolors, + l_int32 debugflag) +{ +l_int32 *countarray; +PIX *pixd; + + PROCNAME("pixColorSegment"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); + + /* Phase 1; original segmentation */ + pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors, debugflag); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + if (debugflag) { + lept_mkdir("lept/segment"); + pixWriteDebug("/tmp/lept/segment/colorseg1.png", pixd, IFF_PNG); + } + + /* Phase 2; refinement in pixel assignment */ + if ((countarray = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("countarray not made", procName, NULL); + } + pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray); + if (debugflag) + pixWriteDebug("/tmp/lept/segment/colorseg2.png", pixd, IFF_PNG); + + /* Phase 3: noise removal by separately closing each color */ + pixColorSegmentClean(pixd, selsize, countarray); + LEPT_FREE(countarray); + if (debugflag) + pixWriteDebug("/tmp/lept/segment/colorseg3.png", pixd, IFF_PNG); + + /* Phase 4: removal of colors with small population and + * reassignment of pixels to remaining colors */ + pixColorSegmentRemoveColors(pixd, pixs, finalcolors); + return pixd; +} + + +/*! + * \brief pixColorSegmentCluster() + * + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] maxdist max euclidean dist to existing cluster + * \param[in] maxcolors max number of colors allowed in first pass + * \param[in] debugflag 1 for debug output; 0 otherwise + * \return pixd 8 bit with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) This is phase 1.  See description in pixColorSegment().
+ *      (2) Greedy unsupervised classification.  If the limit 'maxcolors'
+ *          is exceeded, the computation is repeated with a larger
+ *          allowed cluster size.
+ *      (3) On each successive iteration, 'maxdist' is increased by a
+ *          constant factor.  See comments in pixColorSegment() for
+ *          a guideline on parameter selection.
+ *          Note that the diagonal of the 8-bit rgb color cube is about
+ *          440, so for 'maxdist' = 440, you are guaranteed to get 1 color!
+ * 
+ */ +PIX * +pixColorSegmentCluster(PIX *pixs, + l_int32 maxdist, + l_int32 maxcolors, + l_int32 debugflag) +{ +l_int32 w, h, newmaxdist, ret, niters, ncolors, success; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixColorSegmentCluster"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(8); + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + + newmaxdist = maxdist; + niters = 0; + success = TRUE; + while (1) { + ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist, + maxcolors, debugflag); + niters++; + if (!ret) { + ncolors = pixcmapGetCount(cmap); + if (debugflag) + L_INFO("Success with %d colors after %d iters\n", procName, + ncolors, niters); + break; + } + if (niters == MAX_ALLOWED_ITERATIONS) { + L_WARNING("too many iters; newmaxdist = %d\n", + procName, newmaxdist); + success = FALSE; + break; + } + newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist); + } + + if (!success) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL); + } + + return pixd; +} + + +/*! + * \brief pixColorSegmentTryCluster() + * + * \param[in] pixd + * \param[in] pixs + * \param[in] maxdist + * \param[in] maxcolors + * \param[in] debugflag 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      This function should only be called from pixColorSegCluster()
+ * 
+ */ +static l_int32 +pixColorSegmentTryCluster(PIX *pixd, + PIX *pixs, + l_int32 maxdist, + l_int32 maxcolors, + l_int32 debugflag) +{ +l_int32 rmap[256], gmap[256], bmap[256]; +l_int32 w, h, wpls, wpld, i, j, k, found, ret, index, ncolors; +l_int32 rval, gval, bval, dist2, maxdist2; +l_int32 countarray[256]; +l_int32 rsum[256], gsum[256], bsum[256]; +l_uint32 *ppixel; +l_uint32 *datas, *datad, *lines, *lined; +PIXCMAP *cmap; + + PROCNAME("pixColorSegmentTryCluster"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + maxdist2 = maxdist * maxdist; + cmap = pixGetColormap(pixd); + pixcmapClear(cmap); + for (k = 0; k < 256; k++) { + rsum[k] = gsum[k] = bsum[k] = 0; + rmap[k] = gmap[k] = bmap[k] = 0; + } + + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + ncolors = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + ppixel = lines + j; + rval = GET_DATA_BYTE(ppixel, COLOR_RED); + gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); + bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); + ncolors = pixcmapGetCount(cmap); + found = FALSE; + for (k = 0; k < ncolors; k++) { + dist2 = (rval - rmap[k]) * (rval - rmap[k]) + + (gval - gmap[k]) * (gval - gmap[k]) + + (bval - bmap[k]) * (bval - bmap[k]); + if (dist2 <= maxdist2) { /* take it; greedy */ + found = TRUE; + SET_DATA_BYTE(lined, j, k); + countarray[k]++; + rsum[k] += rval; + gsum[k] += gval; + bsum[k] += bval; + break; + } + } + if (!found) { /* Add a new color */ + ret = pixcmapAddNewColor(cmap, rval, gval, bval, &index); +/* fprintf(stderr, + "index = %d, (i,j) = (%d,%d), rgb = (%d, %d, %d)\n", + index, i, j, rval, gval, bval); */ + if (ret == 0 && index < maxcolors) { + countarray[index] = 1; + SET_DATA_BYTE(lined, j, index); + rmap[index] = rval; + gmap[index] = gval; + bmap[index] = bval; + rsum[index] = rval; + gsum[index] = gval; + bsum[index] = bval; + } else { + if (debugflag) { + L_INFO("maxcolors exceeded for maxdist = %d\n", + procName, maxdist); + } + return 1; + } + } + } + } + + /* Replace the colors in the colormap by the averages */ + for (k = 0; k < ncolors; k++) { + rval = rsum[k] / countarray[k]; + gval = gsum[k] / countarray[k]; + bval = bsum[k] / countarray[k]; + pixcmapResetColor(cmap, k, rval, gval, bval); + } + + return 0; +} + + +/*! + * \brief pixAssignToNearestColor() + * + * \param[in] pixd 8 bpp, colormapped + * \param[in] pixs 32 bpp; 24-bit color + * \param[in] pixm [optional] 1 bpp + * \param[in] level of octcube used for finding nearest color in cmap + * \param[in] countarray [optional] ptr to array, in which we can store + * the number of pixels found in each color in + * the colormap in pixd + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used in phase 2 of color segmentation, where pixs
+ *          is the original input image to pixColorSegment(), and
+ *          pixd is the colormapped image returned from
+ *          pixColorSegmentCluster().  It is also used, with a mask,
+ *          in phase 4.
+ *      (2) This is an in-place operation.
+ *      (3) The colormap in pixd is unchanged.
+ *      (4) pixs and pixd must be the same size (w, h).
+ *      (5) The selection mask pixm can be null.  If it exists, it must
+ *          be the same size as pixs and pixd, and only pixels
+ *          corresponding to fg in pixm are assigned.  Set to
+ *          NULL if all pixels in pixd are to be assigned.
+ *      (6) The countarray can be null.  If it exists, it is pre-allocated
+ *          and of a size at least equal to the size of the colormap in pixd.
+ *      (7) This does a best-fit (non-greedy) assignment of pixels to
+ *          existing clusters.  Specifically, it assigns each pixel
+ *          in pixd to the color index in the pixd colormap that has a
+ *          color closest to the corresponding rgb pixel in pixs.
+ *      (8) 'level' is the octcube level used to quickly find the nearest
+ *          color in the colormap for each pixel.  For color segmentation,
+ *          this parameter is set to LEVEL_IN_OCTCUBE.
+ *      (9) We build a mapping table from octcube to colormap index so
+ *          that this function can run in a time (otherwise) independent
+ *          of the number of colors in the colormap.  This avoids a
+ *          brute-force search for the closest colormap color to each
+ *          pixel in the image.
+ * 
+ */ +l_ok +pixAssignToNearestColor(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_int32 level, + l_int32 *countarray) +{ +l_int32 w, h, wpls, wpld, wplm, i, j, success; +l_int32 rval, gval, bval, index; +l_int32 *cmaptab; +l_uint32 octindex; +l_uint32 *rtab, *gtab, *btab; +l_uint32 *ppixel; +l_uint32 *datas, *datad, *datam, *lines, *lined, *linem; +PIXCMAP *cmap; + + PROCNAME("pixAssignToNearestColor"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if ((cmap = pixGetColormap(pixd)) == NULL) + return ERROR_INT("cmap not found", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (level < 1 || level > 6) + return ERROR_INT("level not in [1 ... 6]", procName, 1); + + /* Set up the tables to map rgb to the nearest colormap index */ + success = TRUE; + makeRGBToIndexTables(level, &rtab, >ab, &btab); + cmaptab = pixcmapToOctcubeLUT(cmap, level, L_MANHATTAN_DISTANCE); + if (!rtab || !gtab || !btab || !cmaptab) { + L_ERROR("failure to make a table\n", procName); + success = FALSE; + goto cleanup_arrays; + } + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + if (pixm) { + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + } + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (pixm) + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if (pixm) { + if (!GET_DATA_BIT(linem, j)) + continue; + } + ppixel = lines + j; + rval = GET_DATA_BYTE(ppixel, COLOR_RED); + gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); + bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); + /* Map from rgb to octcube index */ + getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, + &octindex); + /* Map from octcube index to nearest colormap index */ + index = cmaptab[octindex]; + if (countarray) + countarray[index]++; + SET_DATA_BYTE(lined, j, index); + } + } + +cleanup_arrays: + LEPT_FREE(cmaptab); + LEPT_FREE(rtab); + LEPT_FREE(gtab); + LEPT_FREE(btab); + return (success) ? 0 : 1; +} + + +/*! + * \brief pixColorSegmentClean() + * + * \param[in] pixs 8 bpp, colormapped + * \param[in] selsize for closing + * \param[in] countarray ptr to array containing the number of pixels + * found in each color in the colormap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This operation is in-place.
+ *      (2) This is phase 3 of color segmentation.  It is the first
+ *          part of a two-step noise removal process.  Colors with a
+ *          large population are closed first; this operation absorbs
+ *          small sets of intercolated pixels of a different color.
+ * 
+ */ +l_ok +pixColorSegmentClean(PIX *pixs, + l_int32 selsize, + l_int32 *countarray) +{ +l_int32 i, ncolors, val; +l_uint32 val32; +NUMA *na, *nasi; +PIX *pixt1, *pixt2; +PIXCMAP *cmap; + + PROCNAME("pixColorSegmentClean"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not 8 bpp", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("cmap not found", procName, 1); + if (!countarray) + return ERROR_INT("countarray not defined", procName, 1); + if (selsize <= 1) + return 0; /* nothing to do */ + + /* Sort colormap indices in decreasing order of pixel population */ + ncolors = pixcmapGetCount(cmap); + na = numaCreate(ncolors); + for (i = 0; i < ncolors; i++) + numaAddNumber(na, countarray[i]); + nasi = numaGetSortIndex(na, L_SORT_DECREASING); + numaDestroy(&na); + if (!nasi) + return ERROR_INT("nasi not made", procName, 1); + + /* For each color, in order of decreasing population, + * do a closing and absorb the added pixels. Note that + * if the closing removes pixels at the border, they'll + * still appear in the xor and will be properly (re)set. */ + for (i = 0; i < ncolors; i++) { + numaGetIValue(nasi, i, &val); + pixt1 = pixGenerateMaskByValue(pixs, val, 1); + pixt2 = pixCloseSafeCompBrick(NULL, pixt1, selsize, selsize); + pixXor(pixt2, pixt2, pixt1); /* pixels to be added to type 'val' */ + pixcmapGetColor32(cmap, val, &val32); + pixSetMasked(pixs, pixt2, val32); /* add them */ + pixDestroy(&pixt1); + pixDestroy(&pixt2); + } + numaDestroy(&nasi); + return 0; +} + + +/*! + * \brief pixColorSegmentRemoveColors() + * + * \param[in] pixd 8 bpp, colormapped + * \param[in] pixs 32 bpp rgb, with initial pixel values + * \param[in] finalcolors max number of colors to retain + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This operation is in-place.
+ *      (2) This is phase 4 of color segmentation, and the second part
+ *          of the 2-step noise removal.  Only 'finalcolors' different
+ *          colors are retained, with colors with smaller populations
+ *          being replaced by the nearest color of the remaining colors.
+ *          For highest accuracy, for pixels that are being replaced,
+ *          we find the nearest colormap color  to the original rgb color.
+ * 
+ */ +l_ok +pixColorSegmentRemoveColors(PIX *pixd, + PIX *pixs, + l_int32 finalcolors) +{ +l_int32 i, ncolors, index, tempindex; +l_int32 *tab; +l_uint32 tempcolor; +NUMA *na, *nasi; +PIX *pixm; +PIXCMAP *cmap; + + PROCNAME("pixColorSegmentRemoveColors"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixGetDepth(pixd) != 8) + return ERROR_INT("pixd not 8 bpp", procName, 1); + if ((cmap = pixGetColormap(pixd)) == NULL) + return ERROR_INT("cmap not found", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + ncolors = pixcmapGetCount(cmap); + if (finalcolors >= ncolors) /* few enough colors already; nothing to do */ + return 0; + + /* Generate a mask over all pixels that are not in the + * 'finalcolors' most populated colors. Save the colormap + * index of any one of the retained colors in 'tempindex'. + * The LUT has values 0 for the 'finalcolors' most populated colors, + * which will be retained; and 1 for the rest, which are marked + * by fg pixels in pixm and will be removed. */ + na = pixGetCmapHistogram(pixd, 1); + if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) { + numaDestroy(&na); + return ERROR_INT("nasi not made", procName, 1); + } + numaGetIValue(nasi, finalcolors - 1, &tempindex); /* retain down to this */ + pixcmapGetColor32(cmap, tempindex, &tempcolor); /* use this color */ + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = finalcolors; i < ncolors; i++) { + numaGetIValue(nasi, i, &index); + tab[index] = 1; + } + + pixm = pixMakeMaskFromLUT(pixd, tab); + LEPT_FREE(tab); + + /* Reassign the masked pixels temporarily to the saved index + * (tempindex). This guarantees that no pixels are labeled by + * a colormap index of any colors that will be removed. + * The actual value doesn't matter, as long as it's one + * of the retained colors, because these pixels will later + * be reassigned based on the full set of colors retained + * in the colormap. */ + pixSetMasked(pixd, pixm, tempcolor); + + /* Now remove unused colors from the colormap. This reassigns + * image pixels as required. */ + pixRemoveUnusedColors(pixd); + + /* Finally, reassign the pixels under the mask (those that were + * given a 'tempindex' value) to the nearest color in the colormap. + * This is the function used in phase 2 on all image pixels; here + * it is only used on the masked pixels given by pixm. */ + pixAssignToNearestColor(pixd, pixs, pixm, LEVEL_IN_OCTCUBE, NULL); + + pixDestroy(&pixm); + numaDestroy(&na); + numaDestroy(&nasi); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/colorspace.c b/hgdriver/3rdparty/hgOCR/leptonica/colorspace.c new file mode 100644 index 0000000..d44bcd7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/colorspace.c @@ -0,0 +1,2414 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file colorspace.c + *
+ *
+ *      Colorspace conversion between RGB and HSV
+ *           PIX        *pixConvertRGBToHSV()
+ *           PIX        *pixConvertHSVToRGB()
+ *           l_int32     convertRGBToHSV()
+ *           l_int32     convertHSVToRGB()
+ *           l_int32     pixcmapConvertRGBToHSV()
+ *           l_int32     pixcmapConvertHSVToRGB()
+ *           PIX        *pixConvertRGBToHue()
+ *           PIX        *pixConvertRGBToSaturation()
+ *           PIX        *pixConvertRGBToValue()
+ *
+ *      Selection and display of range of colors in HSV space
+ *           PIX        *pixMakeRangeMaskHS()
+ *           PIX        *pixMakeRangeMaskHV()
+ *           PIX        *pixMakeRangeMaskSV()
+ *           PIX        *pixMakeHistoHS()
+ *           PIX        *pixMakeHistoHV()
+ *           PIX        *pixMakeHistoSV()
+ *           PIX        *pixFindHistoPeaksHSV()
+ *           PIX        *displayHSVColorRange()
+ *
+ *      Colorspace conversion between RGB and YUV
+ *           PIX        *pixConvertRGBToYUV()
+ *           PIX        *pixConvertYUVToRGB()
+ *           l_int32     convertRGBToYUV()
+ *           l_int32     convertYUVToRGB()
+ *           l_int32     pixcmapConvertRGBToYUV()
+ *           l_int32     pixcmapConvertYUVToRGB()
+ *
+ *      Colorspace conversion between RGB and XYZ
+ *           FPIXA      *pixConvertRGBToXYZ()
+ *           PIX        *fpixaConvertXYZToRGB()
+ *           l_int32     convertRGBToXYZ()
+ *           l_int32     convertXYZToRGB()
+ *
+ *      Colorspace conversion between XYZ and LAB
+ *           FPIXA      *fpixaConvertXYZToLAB()
+ *           PIX        *fpixaConvertLABToXYZ()
+ *           l_int32     convertXYZToLAB()
+ *           l_int32     convertLABToXYZ()
+ *           static l_float32  lab_forward()
+ *           static l_float32  lab_reverse()
+ *
+ *      Colorspace conversion between RGB and LAB
+ *           FPIXA      *pixConvertRGBToLAB()
+ *           PIX        *fpixaConvertLABToRGB()
+ *           l_int32     convertRGBToLAB()
+ *           l_int32     convertLABToRGB()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_HISTO 0 +#define SLOW_CUBE_ROOT 0 +#endif /* ~NO_CONSOLE_IO */ + + /* Functions used in xyz <--> lab conversions */ +static l_float32 lab_forward(l_float32 v); +static l_float32 lab_reverse(l_float32 v); + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between RGB and HSB * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToHSV() + * + * \param[in] pixd can be NULL; if not NULL, must == pixs + * \param[in] pixs + * \return pixd always + * + *
+ * Notes:
+ *      (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
+ *      (2) The definition of our HSV space is given in convertRGBToHSV().
+ *      (3) The h, s and v values are stored in the same places as
+ *          the r, g and b values, respectively.  Here, they are explicitly
+ *          placed in the 3 MS bytes in the pixel.
+ *      (4) Normalizing to 1 and considering the r,g,b components,
+ *          a simple way to understand the HSV space is:
+ *           ~ v = max(r,g,b)
+ *           ~ s = (max - min) / max
+ *           ~ h ~ (mid - min) / (max - min)  [apart from signs and constants]
+ *      (5) Normalizing to 1, some properties of the HSV space are:
+ *           ~ For gray values (r = g = b) along the continuum between
+ *             black and white:
+ *                s = 0  (becoming undefined as you approach black)
+ *                h is undefined everywhere
+ *           ~ Where one component is saturated and the others are zero:
+ *                v = 1
+ *                s = 1
+ *                h = 0 (r = max), 1/3 (g = max), 2/3 (b = max)
+ *           ~ Where two components are saturated and the other is zero:
+ *                v = 1
+ *                s = 1
+ *                h = 1/2 (if r = 0), 5/6 (if g = 0), 1/6 (if b = 0)
+ * 
+ */ +PIX * +pixConvertRGBToHSV(PIX *pixd, + PIX *pixs) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; +l_uint32 *line, *data; +PIXCMAP *cmap; + + PROCNAME("pixConvertRGBToHSV"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); + + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + + cmap = pixGetColormap(pixd); + if (cmap) { /* just convert the colormap */ + pixcmapConvertRGBToHSV(cmap); + return pixd; + } + + /* Convert RGB image */ + pixGetDimensions(pixd, &w, &h, NULL); + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + line[j] = (hval << 24) | (sval << 16) | (vval << 8); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertHSVToRGB() + * + * \param[in] pixd can be NULL; if not NULL, must == pixs + * \param[in] pixs + * \return pixd always + * + *
+ * Notes:
+ *      (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
+ *      (2) The user takes responsibility for making sure that pixs is
+ *          in our HSV space.  The definition of our HSV space is given
+ *          in convertRGBToHSV().
+ *      (3) The h, s and v values are stored in the same places as
+ *          the r, g and b values, respectively.  Here, they are explicitly
+ *          placed in the 3 MS bytes in the pixel.
+ * 
+ */ +PIX * +pixConvertHSVToRGB(PIX *pixd, + PIX *pixs) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; +l_uint32 pixel; +l_uint32 *line, *data; +PIXCMAP *cmap; + + PROCNAME("pixConvertHSVToRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); + + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + + cmap = pixGetColormap(pixd); + if (cmap) { /* just convert the colormap */ + pixcmapConvertHSVToRGB(cmap); + return pixd; + } + + /* Convert HSV image */ + pixGetDimensions(pixd, &w, &h, NULL); + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + pixel = line[j]; + hval = pixel >> 24; + sval = (pixel >> 16) & 0xff; + vval = (pixel >> 8) & 0xff; + convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, line + j); + } + } + + return pixd; +} + + +/*! + * \brief convertRGBToHSV() + * + * \param[in] rval, gval, bval RGB input + * \param[out] phval, psval, pvval comparable HSV values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The range of returned values is:
+ *            h [0 ... 239]
+ *            s [0 ... 255]
+ *            v [0 ... 255]
+ *      (2) If r = g = b, the pixel is gray (s = 0), and we define h = 0.
+ *      (3) h wraps around, so that h = 0 and h = 240 are equivalent
+ *          in hue space.
+ *      (4) h has the following correspondence to color:
+ *            h = 0         magenta
+ *            h = 40        red
+ *            h = 80        yellow
+ *            h = 120       green
+ *            h = 160       cyan
+ *            h = 200       blue
+ * 
+ */ +l_ok +convertRGBToHSV(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *phval, + l_int32 *psval, + l_int32 *pvval) +{ +l_int32 minrg, maxrg, min, max, delta; +l_float32 h; + + PROCNAME("convertRGBToHSV"); + + if (phval) *phval = 0; + if (psval) *psval = 0; + if (pvval) *pvval = 0; + if (!phval || !psval || !pvval) + return ERROR_INT("&hval, &sval, &vval not all defined", procName, 1); + + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + delta = max - min; + + *pvval = max; + if (delta == 0) { /* gray; no chroma */ + *phval = 0; + *psval = 0; + } else { + *psval = (l_int32)(255. * (l_float32)delta / (l_float32)max + 0.5); + if (rval == max) /* between magenta and yellow */ + h = (l_float32)(gval - bval) / (l_float32)delta; + else if (gval == max) /* between yellow and cyan */ + h = 2. + (l_float32)(bval - rval) / (l_float32)delta; + else /* between cyan and magenta */ + h = 4. + (l_float32)(rval - gval) / (l_float32)delta; + h *= 40.0; + if (h < 0.0) + h += 240.0; + if (h >= 239.5) + h = 0.0; + *phval = (l_int32)(h + 0.5); + } + + return 0; +} + + +/*! + * \brief convertHSVToRGB() + * + * \param[in] hval, sval, vval HSV input + * \param[out] prval, pgval, pbval comparable RGB values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertRGBToHSV() for valid input range of HSV values
+ *          and their interpretation in color space.
+ * 
+ */ +l_ok +convertHSVToRGB(l_int32 hval, + l_int32 sval, + l_int32 vval, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 i, x, y, z; +l_float32 h, f, s; + + PROCNAME("convertHSVToRGB"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval || !pgval || !pbval) + return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); + + if (sval == 0) { /* gray */ + *prval = vval; + *pgval = vval; + *pbval = vval; + } else { + if (hval < 0 || hval > 240) + return ERROR_INT("invalid hval", procName, 1); + if (hval == 240) + hval = 0; + h = (l_float32)hval / 40.; + i = (l_int32)h; + f = h - i; + s = (l_float32)sval / 255.; + x = (l_int32)(vval * (1. - s) + 0.5); + y = (l_int32)(vval * (1. - s * f) + 0.5); + z = (l_int32)(vval * (1. - s * (1. - f)) + 0.5); + switch (i) + { + case 0: + *prval = vval; + *pgval = z; + *pbval = x; + break; + case 1: + *prval = y; + *pgval = vval; + *pbval = x; + break; + case 2: + *prval = x; + *pgval = vval; + *pbval = z; + break; + case 3: + *prval = x; + *pgval = y; + *pbval = vval; + break; + case 4: + *prval = z; + *pgval = x; + *pbval = vval; + break; + case 5: + *prval = vval; + *pgval = x; + *pbval = y; + break; + default: /* none possible */ + return 1; + } + } + + return 0; +} + + +/*! + * \brief pixcmapConvertRGBToHSV() + * + * \param[in] cmap + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      ~ in-place transform
+ *      ~ See convertRGBToHSV() for def'n of HSV space.
+ *      ~ replaces: r --> h, g --> s, b --> v
+ * 
+ */ +l_ok +pixcmapConvertRGBToHSV(PIXCMAP *cmap) +{ +l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; + + PROCNAME("pixcmapConvertRGBToHSV"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + pixcmapResetColor(cmap, i, hval, sval, vval); + } + return 0; +} + + +/*! + * \brief pixcmapConvertHSVToRGB() + * + * \param[in] cmap + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      ~ in-place transform
+ *      ~ See convertRGBToHSV() for def'n of HSV space.
+ *      ~ replaces: h --> r, s --> g, v --> b
+ * 
+ */ +l_ok +pixcmapConvertHSVToRGB(PIXCMAP *cmap) +{ +l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; + + PROCNAME("pixcmapConvertHSVToRGB"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &hval, &sval, &vval); + convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); + pixcmapResetColor(cmap, i, rval, gval, bval); + } + return 0; +} + + +/*! + * \brief pixConvertRGBToHue() + * + * \param[in] pixs 32 bpp RGB, or 8 bpp with colormap + * \return pixd 8 bpp hue of HSV, or NULL on error + * + *
+ * Notes:
+ *      (1) The conversion to HSV hue is in-lined here.
+ *      (2) If there is a colormap, it is removed.
+ *      (3) If you just want the hue component, this does it
+ *          at about 10 Mpixels/sec/GHz, which is about
+ *          2x faster than using pixConvertRGBToHSV()
+ * 
+ */ +PIX * +pixConvertRGBToHue(PIX *pixs) +{ +l_int32 w, h, d, wplt, wpld; +l_int32 i, j, rval, gval, bval, hval, minrg, min, maxrg, max, delta; +l_float32 fh; +l_uint32 pixel; +l_uint32 *linet, *lined, *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixConvertRGBToHue"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + + /* Convert RGB image */ + pixd = pixCreate(w, h, 8); + pixCopyResolution(pixd, pixs); + wplt = pixGetWpl(pixt); + datat = pixGetData(pixt); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + delta = max - min; + if (delta == 0) { /* gray; no chroma */ + hval = 0; + } else { + if (rval == max) /* between magenta and yellow */ + fh = (l_float32)(gval - bval) / (l_float32)delta; + else if (gval == max) /* between yellow and cyan */ + fh = 2. + (l_float32)(bval - rval) / (l_float32)delta; + else /* between cyan and magenta */ + fh = 4. + (l_float32)(rval - gval) / (l_float32)delta; + fh *= 40.0; + if (fh < 0.0) + fh += 240.0; + hval = (l_int32)(fh + 0.5); + } + SET_DATA_BYTE(lined, j, hval); + } + } + pixDestroy(&pixt); + + return pixd; +} + + + +/*! + * \brief pixConvertRGBToSaturation() + * + * \param[in] pixs 32 bpp RGB, or 8 bpp with colormap + * \return pixd 8 bpp sat of HSV, or NULL on error + * + *
+ * Notes:
+ *      (1) The conversion to HSV sat is in-lined here.
+ *      (2) If there is a colormap, it is removed.
+ *      (3) If you just want the saturation component, this does it
+ *          at about 12 Mpixels/sec/GHz.
+ * 
+ */ +PIX * +pixConvertRGBToSaturation(PIX *pixs) +{ +l_int32 w, h, d, wplt, wpld; +l_int32 i, j, rval, gval, bval, sval, minrg, min, maxrg, max, delta; +l_uint32 pixel; +l_uint32 *linet, *lined, *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixConvertRGBToSaturation"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + + /* Convert RGB image */ + pixd = pixCreate(w, h, 8); + pixCopyResolution(pixd, pixs); + wplt = pixGetWpl(pixt); + datat = pixGetData(pixt); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + delta = max - min; + if (delta == 0) /* gray; no chroma */ + sval = 0; + else + sval = (l_int32)(255. * + (l_float32)delta / (l_float32)max + 0.5); + SET_DATA_BYTE(lined, j, sval); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixConvertRGBToValue() + * + * \param[in] pixs 32 bpp RGB,or 8 bpp with colormap + * \return pixd 8 bpp max component intensity of HSV, or NULL on error + * + *
+ * Notes:
+ *      (1) The conversion to HSV sat is in-lined here.
+ *      (2) If there is a colormap, it is removed.
+ *      (3) If you just want the value component, this does it
+ *          at about 35 Mpixels/sec/GHz.
+ * 
+ */ +PIX * +pixConvertRGBToValue(PIX *pixs) +{ +l_int32 w, h, d, wplt, wpld; +l_int32 i, j, rval, gval, bval, maxrg, max; +l_uint32 pixel; +l_uint32 *linet, *lined, *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixConvertRGBToValue"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + + /* Convert RGB image */ + pixd = pixCreate(w, h, 8); + pixCopyResolution(pixd, pixs); + wplt = pixGetWpl(pixt); + datat = pixGetData(pixt); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + SET_DATA_BYTE(lined, j, max); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Selection and display of range of colors in HSV space * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixMakeRangeMaskHS() + * + * \param[in] pixs 32 bpp rgb + * \param[in] huecenter center value of hue range + * \param[in] huehw half-width of hue range + * \param[in] satcenter center value of saturation range + * \param[in] sathw half-width of saturation range + * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION + * \return pixd 1 bpp mask over selected pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixels are selected based on the specified ranges of
+ *          hue and saturation.  For selection or exclusion, the pixel
+ *          HS component values must be within both ranges.  Care must
+ *          be taken in finding the hue range because of wrap-around.
+ *      (2) Use %regionflag == L_INCLUDE_REGION to take only those
+ *          pixels within the rectangular region specified in HS space.
+ *          Use %regionflag == L_EXCLUDE_REGION to take all pixels except
+ *          those within the rectangular region specified in HS space.
+ * 
+ */ +PIX * +pixMakeRangeMaskHS(PIX *pixs, + l_int32 huecenter, + l_int32 huehw, + l_int32 satcenter, + l_int32 sathw, + l_int32 regionflag) +{ +l_int32 i, j, w, h, wplt, wpld, hstart, hend, sstart, send, hval, sval; +l_int32 *hlut, *slut; +l_uint32 pixel; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixMakeRangeMaskHS"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) + return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); + + /* Set up LUTs for hue and saturation. These have the value 1 + * within the specified intervals of hue and saturation. */ + hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32)); + slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + sstart = L_MAX(0, satcenter - sathw); + send = L_MIN(255, satcenter + sathw); + for (i = sstart; i <= send; i++) + slut[i] = 1; + hstart = (huecenter - huehw + 240) % 240; + hend = (huecenter + huehw + 240) % 240; + if (hstart < hend) { + for (i = hstart; i <= hend; i++) + hlut[i] = 1; + } else { /* wrap */ + for (i = hstart; i < 240; i++) + hlut[i] = 1; + for (i = 0; i <= hend; i++) + hlut[i] = 1; + } + + /* Generate the mask */ + pixt = pixConvertRGBToHSV(NULL, pixs); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreateNoInit(w, h, 1); + if (regionflag == L_INCLUDE_REGION) + pixClearAll(pixd); + else /* L_EXCLUDE_REGION */ + pixSetAll(pixd); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + hval = (pixel >> L_RED_SHIFT) & 0xff; + sval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (hlut[hval] == 1 && slut[sval] == 1) { + if (regionflag == L_INCLUDE_REGION) + SET_DATA_BIT(lined, j); + else /* L_EXCLUDE_REGION */ + CLEAR_DATA_BIT(lined, j); + } + } + } + + LEPT_FREE(hlut); + LEPT_FREE(slut); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixMakeRangeMaskHV() + * + * \param[in] pixs 32 bpp rgb + * \param[in] huecenter center value of hue range + * \param[in] huehw half-width of hue range + * \param[in] valcenter center value of max intensity range + * \param[in] valhw half-width of max intensity range + * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION + * \return pixd 1 bpp mask over selected pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixels are selected based on the specified ranges of
+ *          hue and max intensity values.  For selection or exclusion,
+ *          the pixel HV component values must be within both ranges.
+ *          Care must be taken in finding the hue range because of wrap-around.
+ *      (2) Use %regionflag == L_INCLUDE_REGION to take only those
+ *          pixels within the rectangular region specified in HV space.
+ *          Use %regionflag == L_EXCLUDE_REGION to take all pixels except
+ *          those within the rectangular region specified in HV space.
+ * 
+ */ +PIX * +pixMakeRangeMaskHV(PIX *pixs, + l_int32 huecenter, + l_int32 huehw, + l_int32 valcenter, + l_int32 valhw, + l_int32 regionflag) +{ +l_int32 i, j, w, h, wplt, wpld, hstart, hend, vstart, vend, hval, vval; +l_int32 *hlut, *vlut; +l_uint32 pixel; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixMakeRangeMaskHV"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) + return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); + + /* Set up LUTs for hue and maximum intensity (val). These have + * the value 1 within the specified intervals of hue and value. */ + hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32)); + vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + vstart = L_MAX(0, valcenter - valhw); + vend = L_MIN(255, valcenter + valhw); + for (i = vstart; i <= vend; i++) + vlut[i] = 1; + hstart = (huecenter - huehw + 240) % 240; + hend = (huecenter + huehw + 240) % 240; + if (hstart < hend) { + for (i = hstart; i <= hend; i++) + hlut[i] = 1; + } else { + for (i = hstart; i < 240; i++) + hlut[i] = 1; + for (i = 0; i <= hend; i++) + hlut[i] = 1; + } + + /* Generate the mask */ + pixt = pixConvertRGBToHSV(NULL, pixs); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreateNoInit(w, h, 1); + if (regionflag == L_INCLUDE_REGION) + pixClearAll(pixd); + else /* L_EXCLUDE_REGION */ + pixSetAll(pixd); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + hval = (pixel >> L_RED_SHIFT) & 0xff; + vval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (hlut[hval] == 1 && vlut[vval] == 1) { + if (regionflag == L_INCLUDE_REGION) + SET_DATA_BIT(lined, j); + else /* L_EXCLUDE_REGION */ + CLEAR_DATA_BIT(lined, j); + } + } + } + + LEPT_FREE(hlut); + LEPT_FREE(vlut); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixMakeRangeMaskSV() + * + * \param[in] pixs 32 bpp rgb + * \param[in] satcenter center value of saturation range + * \param[in] sathw half-width of saturation range + * \param[in] valcenter center value of max intensity range + * \param[in] valhw half-width of max intensity range + * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION + * \return pixd 1 bpp mask over selected pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixels are selected based on the specified ranges of
+ *          saturation and max intensity (val).  For selection or
+ *          exclusion, the pixel SV component values must be within both ranges.
+ *      (2) Use %regionflag == L_INCLUDE_REGION to take only those
+ *          pixels within the rectangular region specified in SV space.
+ *          Use %regionflag == L_EXCLUDE_REGION to take all pixels except
+ *          those within the rectangular region specified in SV space.
+ * 
+ */ +PIX * +pixMakeRangeMaskSV(PIX *pixs, + l_int32 satcenter, + l_int32 sathw, + l_int32 valcenter, + l_int32 valhw, + l_int32 regionflag) +{ +l_int32 i, j, w, h, wplt, wpld, sval, vval, sstart, send, vstart, vend; +l_int32 *slut, *vlut; +l_uint32 pixel; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixMakeRangeMaskSV"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) + return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); + + /* Set up LUTs for saturation and max intensity (val). + * These have the value 1 within the specified intervals of + * saturation and max intensity. */ + slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + sstart = L_MAX(0, satcenter - sathw); + send = L_MIN(255, satcenter + sathw); + vstart = L_MAX(0, valcenter - valhw); + vend = L_MIN(255, valcenter + valhw); + for (i = sstart; i <= send; i++) + slut[i] = 1; + for (i = vstart; i <= vend; i++) + vlut[i] = 1; + + /* Generate the mask */ + pixt = pixConvertRGBToHSV(NULL, pixs); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreateNoInit(w, h, 1); + if (regionflag == L_INCLUDE_REGION) + pixClearAll(pixd); + else /* L_EXCLUDE_REGION */ + pixSetAll(pixd); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = linet[j]; + sval = (pixel >> L_GREEN_SHIFT) & 0xff; + vval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (slut[sval] == 1 && vlut[vval] == 1) { + if (regionflag == L_INCLUDE_REGION) + SET_DATA_BIT(lined, j); + else /* L_EXCLUDE_REGION */ + CLEAR_DATA_BIT(lined, j); + } + } + } + + LEPT_FREE(slut); + LEPT_FREE(vlut); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixMakeHistoHS() + * + * \param[in] pixs HSV colorspace + * \param[in] factor subsampling factor; integer + * \param[out] pnahue [optional] hue histogram + * \param[out] pnasat [optional] saturation histogram + * \return pixd 32 bpp histogram in hue and saturation, or NULL on error + * + *
+ * Notes:
+ *      (1) pixs is a 32 bpp image in HSV colorspace; hue is in the "red"
+ *          byte, saturation is in the "green" byte.
+ *      (2) In pixd, hue is displayed vertically; saturation horizontally.
+ *          The dimensions of pixd are w = 256, h = 240, and the depth
+ *          is 32 bpp.  The value at each point is simply the number
+ *          of pixels found at that value of hue and saturation.
+ * 
+ */ +PIX * +pixMakeHistoHS(PIX *pixs, + l_int32 factor, + NUMA **pnahue, + NUMA **pnasat) +{ +l_int32 i, j, w, h, wplt, hval, sval, nd; +l_uint32 pixel; +l_uint32 *datat, *linet; +void **lined32; +NUMA *nahue, *nasat; +PIX *pixt, *pixd; + + PROCNAME("pixMakeHistoHS"); + + if (pnahue) *pnahue = NULL; + if (pnasat) *pnasat = NULL; + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + + if (pnahue) { + nahue = numaCreate(240); + numaSetCount(nahue, 240); + *pnahue = nahue; + } + if (pnasat) { + nasat = numaCreate(256); + numaSetCount(nasat, 256); + *pnasat = nasat; + } + + if (factor <= 1) + pixt = pixClone(pixs); + else + pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, + 1.0 / (l_float32)factor); + + /* Create the hue-saturation histogram */ + pixd = pixCreate(256, 240, 32); + lined32 = pixGetLinePtrs(pixd, NULL); + pixGetDimensions(pixt, &w, &h, NULL); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + for (j = 0; j < w; j++) { + pixel = linet[j]; + hval = (pixel >> L_RED_SHIFT) & 0xff; + +#if DEBUG_HISTO + if (hval > 239) { + fprintf(stderr, "hval = %d for (%d,%d)\n", hval, i, j); + continue; + } +#endif /* DEBUG_HISTO */ + + sval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (pnahue) + numaShiftValue(nahue, hval, 1.0); + if (pnasat) + numaShiftValue(nasat, sval, 1.0); + nd = GET_DATA_FOUR_BYTES(lined32[hval], sval); + SET_DATA_FOUR_BYTES(lined32[hval], sval, nd + 1); + } + } + + LEPT_FREE(lined32); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixMakeHistoHV() + * + * \param[in] pixs HSV colorspace + * \param[in] factor subsampling factor; integer + * \param[out] pnahue [optional] hue histogram + * \param[out] pnaval [optional] max intensity (value) histogram + * \return pixd 32 bpp histogram in hue and value, or NULL on error + * + *
+ * Notes:
+ *      (1) %pixs is a 32 bpp image in HSV colorspace; hue is in the "red"
+ *          byte, max intensity ("value") is in the "blue" byte.
+ *      (2) In %pixd, hue is displayed vertically; intensity horizontally.
+ *          The dimensions of %pixd are w = 256, h = 240, and the depth
+ *          is 32 bpp.  The value at each point is simply the number
+ *          of pixels found at that value of hue and intensity.
+ * 
+ */ +PIX * +pixMakeHistoHV(PIX *pixs, + l_int32 factor, + NUMA **pnahue, + NUMA **pnaval) +{ +l_int32 i, j, w, h, wplt, hval, vval, nd; +l_uint32 pixel; +l_uint32 *datat, *linet; +void **lined32; +NUMA *nahue, *naval; +PIX *pixt, *pixd; + + PROCNAME("pixMakeHistoHV"); + + if (pnahue) *pnahue = NULL; + if (pnaval) *pnaval = NULL; + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + + if (pnahue) { + nahue = numaCreate(240); + numaSetCount(nahue, 240); + *pnahue = nahue; + } + if (pnaval) { + naval = numaCreate(256); + numaSetCount(naval, 256); + *pnaval = naval; + } + + if (factor <= 1) + pixt = pixClone(pixs); + else + pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, + 1.0 / (l_float32)factor); + + /* Create the hue-value histogram */ + pixd = pixCreate(256, 240, 32); + lined32 = pixGetLinePtrs(pixd, NULL); + pixGetDimensions(pixt, &w, &h, NULL); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + for (j = 0; j < w; j++) { + pixel = linet[j]; + hval = (pixel >> L_RED_SHIFT) & 0xff; + vval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (pnahue) + numaShiftValue(nahue, hval, 1.0); + if (pnaval) + numaShiftValue(naval, vval, 1.0); + nd = GET_DATA_FOUR_BYTES(lined32[hval], vval); + SET_DATA_FOUR_BYTES(lined32[hval], vval, nd + 1); + } + } + + LEPT_FREE(lined32); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixMakeHistoSV() + * + * \param[in] pixs HSV colorspace + * \param[in] factor subsampling factor; integer + * \param[out] pnasat [optional] sat histogram + * \param[out] pnaval [optional] max intensity (value) histogram + * \return pixd 32 bpp histogram in sat and value, or NULL on error + * + *
+ * Notes:
+ *      (1) %pixs is a 32 bpp image in HSV colorspace; sat is in the "green"
+ *          byte, max intensity ("value") is in the "blue" byte.
+ *      (2) In %pixd, sat is displayed vertically; intensity horizontally.
+ *          The dimensions of %pixd are w = 256, h = 256, and the depth
+ *          is 32 bpp.  The value at each point is simply the number
+ *          of pixels found at that value of saturation and intensity.
+ * 
+ */ +PIX * +pixMakeHistoSV(PIX *pixs, + l_int32 factor, + NUMA **pnasat, + NUMA **pnaval) +{ +l_int32 i, j, w, h, wplt, sval, vval, nd; +l_uint32 pixel; +l_uint32 *datat, *linet; +void **lined32; +NUMA *nasat, *naval; +PIX *pixt, *pixd; + + PROCNAME("pixMakeHistoSV"); + + if (pnasat) *pnasat = NULL; + if (pnaval) *pnaval = NULL; + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + + if (pnasat) { + nasat = numaCreate(256); + numaSetCount(nasat, 256); + *pnasat = nasat; + } + if (pnaval) { + naval = numaCreate(256); + numaSetCount(naval, 256); + *pnaval = naval; + } + + if (factor <= 1) + pixt = pixClone(pixs); + else + pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, + 1.0 / (l_float32)factor); + + /* Create the hue-value histogram */ + pixd = pixCreate(256, 256, 32); + lined32 = pixGetLinePtrs(pixd, NULL); + pixGetDimensions(pixt, &w, &h, NULL); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + for (j = 0; j < w; j++) { + pixel = linet[j]; + sval = (pixel >> L_GREEN_SHIFT) & 0xff; + vval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (pnasat) + numaShiftValue(nasat, sval, 1.0); + if (pnaval) + numaShiftValue(naval, vval, 1.0); + nd = GET_DATA_FOUR_BYTES(lined32[sval], vval); + SET_DATA_FOUR_BYTES(lined32[sval], vval, nd + 1); + } + } + + LEPT_FREE(lined32); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixFindHistoPeaksHSV() + * + * \param[in] pixs 32 bpp; HS, HV or SV histogram; not changed + * \param[in] type L_HS_HISTO, L_HV_HISTO or L_SV_HISTO + * \param[in] width half width of sliding window + * \param[in] height half height of sliding window + * \param[in] npeaks number of peaks to look for + * \param[in] erasefactor ratio of erase window size to sliding window size + * \param[out] ppta locations of max for each integrated peak area + * \param[out] pnatot integrated peak areas + * \param[out] ppixa [optional] pixa for debugging; NULL to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) %pixs is a 32 bpp histogram in a pair of HSV colorspace.  It
+ *          should be thought of as a single sample with 32 bps (bits/sample).
+ *      (2) After each peak is found, the peak is erased with a window
+ *          that is centered on the peak and scaled from the sliding
+ *          window by %erasefactor.  Typically, %erasefactor is chosen
+ *          to be > 1.0.
+ *      (3) Data for a maximum of %npeaks is returned in %pta and %natot.
+ *      (4) For debugging, after the pixa is returned, display with:
+ *          pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2);
+ * 
+ */ +l_ok +pixFindHistoPeaksHSV(PIX *pixs, + l_int32 type, + l_int32 width, + l_int32 height, + l_int32 npeaks, + l_float32 erasefactor, + PTA **ppta, + NUMA **pnatot, + PIXA **ppixa) +{ +l_int32 i, xmax, ymax, ewidth, eheight; +l_uint32 maxval; +BOX *box; +NUMA *natot; +PIX *pixh, *pixw, *pix1, *pix2, *pix3; +PTA *pta; + + PROCNAME("pixFindHistoPeaksHSV"); + + if (ppixa) *ppixa = NULL; + if (ppta) *ppta = NULL; + if (pnatot) *pnatot = NULL; + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); + if (!ppta || !pnatot) + return ERROR_INT("&pta and &natot not both defined", procName, 1); + if (type != L_HS_HISTO && type != L_HV_HISTO && type != L_SV_HISTO) + return ERROR_INT("invalid HSV histo type", procName, 1); + + if ((pta = ptaCreate(npeaks)) == NULL) + return ERROR_INT("pta not made", procName, 1); + *ppta = pta; + if ((natot = numaCreate(npeaks)) == NULL) + return ERROR_INT("natot not made", procName, 1); + *pnatot = natot; + + *ppta = pta; + if (type == L_SV_HISTO) + pixh = pixAddMirroredBorder(pixs, width + 1, width + 1, height + 1, + height + 1); + else /* type == L_HS_HISTO or type == L_HV_HISTO */ + pixh = pixAddMixedBorder(pixs, width + 1, width + 1, height + 1, + height + 1); + + /* Get the total count in the sliding window. If the window + * fully covers the peak, this will be the integrated + * volume under the peak. */ + pixw = pixWindowedMean(pixh, width, height, 1, 0); + pixDestroy(&pixh); + + /* Sequentially identify and erase peaks in the histogram. + * If requested for debugging, save a pixa of the sequence of + * false color histograms. */ + if (ppixa) + *ppixa = pixaCreate(0); + for (i = 0; i < npeaks; i++) { + pixGetMaxValueInRect(pixw, NULL, &maxval, &xmax, &ymax); + if (maxval == 0) break; + numaAddNumber(natot, maxval); + ptaAddPt(pta, xmax, ymax); + ewidth = (l_int32)(width * erasefactor); + eheight = (l_int32)(height * erasefactor); + box = boxCreate(xmax - ewidth, ymax - eheight, 2 * ewidth + 1, + 2 * eheight + 1); + + if (ppixa) { + pix1 = pixMaxDynamicRange(pixw, L_LINEAR_SCALE); + pixaAddPix(*ppixa, pix1, L_INSERT); + pix2 = pixConvertGrayToFalseColor(pix1, 1.0); + pixaAddPix(*ppixa, pix2, L_INSERT); + pix1 = pixMaxDynamicRange(pixw, L_LOG_SCALE); + pix2 = pixConvertGrayToFalseColor(pix1, 1.0); + pixaAddPix(*ppixa, pix2, L_INSERT); + pix3 = pixConvertTo32(pix1); + pixRenderHashBoxArb(pix3, box, 6, 2, L_NEG_SLOPE_LINE, + 1, 255, 100, 100); + pixaAddPix(*ppixa, pix3, L_INSERT); + pixDestroy(&pix1); + } + + pixClearInRect(pixw, box); + boxDestroy(&box); + if (type == L_HS_HISTO || type == L_HV_HISTO) { + /* clear wraps at bottom and top */ + if (ymax - eheight < 0) { /* overlap to bottom */ + box = boxCreate(xmax - ewidth, 240 + ymax - eheight, + 2 * ewidth + 1, eheight - ymax); + } else if (ymax + eheight > 239) { /* overlap to top */ + box = boxCreate(xmax - ewidth, 0, 2 * ewidth + 1, + ymax + eheight - 239); + } else { + box = NULL; + } + if (box) { + pixClearInRect(pixw, box); + boxDestroy(&box); + } + } + } + + pixDestroy(&pixw); + return 0; +} + + +/*! + * \brief displayHSVColorRange() + * + * \param[in] hval hue center value; in range [0 ... 240] + * \param[in] sval saturation center value; in range [0 ... 255] + * \param[in] vval max intensity value; in range [0 ... 255] + * \param[in] huehw half-width of hue range; > 0 + * \param[in] sathw half-width of saturation range; > 0 + * \param[in] nsamp number of samplings in each half-width in hue and sat + * \param[in] factor linear size of each color square, in pixels; > 3 + * \return pixd 32 bpp set of color squares over input range; NULL on error + * + *
+ * Notes:
+ *      (1) The total number of color samplings in each of the hue
+ *          and saturation directions is 2 * nsamp + 1.
+ * 
+ */ +PIX * +displayHSVColorRange(l_int32 hval, + l_int32 sval, + l_int32 vval, + l_int32 huehw, + l_int32 sathw, + l_int32 nsamp, + l_int32 factor) +{ +l_int32 i, j, w, huedelta, satdelta, hue, sat, rval, gval, bval; +PIX *pixt, *pixd; + + PROCNAME("displayHSVColorRange"); + + if (hval < 0 || hval > 240) + return (PIX *)ERROR_PTR("invalid hval", procName, NULL); + if (huehw < 5 || huehw > 120) + return (PIX *)ERROR_PTR("invalid huehw", procName, NULL); + if (sval - sathw < 0 || sval + sathw > 255) + return (PIX *)ERROR_PTR("invalid sval/sathw", procName, NULL); + if (nsamp < 1 || factor < 3) + return (PIX *)ERROR_PTR("invalid nsamp or rep. factor", procName, NULL); + if (vval < 0 || vval > 255) + return (PIX *)ERROR_PTR("invalid vval", procName, NULL); + + w = (2 * nsamp + 1); + huedelta = (l_int32)((l_float32)huehw / (l_float32)nsamp); + satdelta = (l_int32)((l_float32)sathw / (l_float32)nsamp); + pixt = pixCreate(w, w, 32); + for (i = 0; i < w; i++) { + hue = hval + huedelta * (i - nsamp); + if (hue < 0) hue += 240; + if (hue >= 240) hue -= 240; + for (j = 0; j < w; j++) { + sat = sval + satdelta * (j - nsamp); + convertHSVToRGB(hue, sat, vval, &rval, &gval, &bval); + pixSetRGBPixel(pixt, j, i, rval, gval, bval); + } + } + + pixd = pixExpandReplicate(pixt, factor); + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between RGB and YUV * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToYUV() + * + * \param[in] pixd can be NULL; if not NULL, must == pixs + * \param[in] pixs + * \return pixd always + * + *
+ * Notes:
+ *      (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
+ *      (2) The Y, U and V values are stored in the same places as
+ *          the r, g and b values, respectively.  Here, they are explicitly
+ *          placed in the 3 MS bytes in the pixel.
+ *      (3) Normalizing to 1 and considering the r,g,b components,
+ *          a simple way to understand the YUV space is:
+ *           ~ Y = weighted sum of (r,g,b)
+ *           ~ U = weighted difference between Y and B
+ *           ~ V = weighted difference between Y and R
+ *      (4) Following video conventions, Y, U and V are in the range:
+ *             Y: [16, 235]
+ *             U: [16, 240]
+ *             V: [16, 240]
+ *      (5) For the coefficients in the transform matrices, see eq. 4 in
+ *          "Frequently Asked Questions about Color" by Charles Poynton,
+ *          //http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html
+ * 
+ */ +PIX * +pixConvertRGBToYUV(PIX *pixd, + PIX *pixs) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval; +l_uint32 *line, *data; +PIXCMAP *cmap; + + PROCNAME("pixConvertRGBToYUV"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); + + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + + cmap = pixGetColormap(pixd); + if (cmap) { /* just convert the colormap */ + pixcmapConvertRGBToYUV(cmap); + return pixd; + } + + /* Convert RGB image */ + pixGetDimensions(pixd, &w, &h, NULL); + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval); + line[j] = (yval << 24) | (uval << 16) | (vval << 8); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertYUVToRGB() + * + * \param[in] pixd can be NULL; if not NULL, must == pixs + * \param[in] pixs + * \return pixd always + * + *
+ * Notes:
+ *      (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL.
+ *      (2) The user takes responsibility for making sure that pixs is
+ *          in YUV space.
+ *      (3) The Y, U and V values are stored in the same places as
+ *          the r, g and b values, respectively.  Here, they are explicitly
+ *          placed in the 3 MS bytes in the pixel.
+ * 
+ */ +PIX * +pixConvertYUVToRGB(PIX *pixd, + PIX *pixs) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval; +l_uint32 pixel; +l_uint32 *line, *data; +PIXCMAP *cmap; + + PROCNAME("pixConvertYUVToRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && pixd != pixs) + return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); + + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + + cmap = pixGetColormap(pixd); + if (cmap) { /* just convert the colormap */ + pixcmapConvertYUVToRGB(cmap); + return pixd; + } + + /* Convert YUV image */ + pixGetDimensions(pixd, &w, &h, NULL); + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + pixel = line[j]; + yval = pixel >> 24; + uval = (pixel >> 16) & 0xff; + vval = (pixel >> 8) & 0xff; + convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, line + j); + } + } + + return pixd; +} + + +/*! + * \brief convertRGBToYUV() + * + * \param[in] rval, gval, bval RGB input + * \param[out] pyval, puval, pvval equivalent YUV values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The range of returned values is:
+ *            Y [16 ... 235]
+ *            U [16 ... 240]
+ *            V [16 ... 240]
+ * 
+ */ +l_ok +convertRGBToYUV(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 *pyval, + l_int32 *puval, + l_int32 *pvval) +{ +l_float32 norm; + + PROCNAME("convertRGBToYUV"); + + if (pyval) *pyval = 0; + if (puval) *puval = 0; + if (pvval) *pvval = 0; + if (!pyval || !puval || !pvval) + return ERROR_INT("&yval, &uval, &vval not all defined", procName, 1); + + norm = 1.0 / 256.; + *pyval = (l_int32)(16.0 + + norm * (65.738 * rval + 129.057 * gval + 25.064 * bval) + 0.5); + *puval = (l_int32)(128.0 + + norm * (-37.945 * rval -74.494 * gval + 112.439 * bval) + 0.5); + *pvval = (l_int32)(128.0 + + norm * (112.439 * rval - 94.154 * gval - 18.285 * bval) + 0.5); + return 0; +} + + +/*! + * \brief convertYUVToRGB() + * + * \param[in] yval, uval, vval YUV input + * \param[out] prval, pgval, pbval equivalent RGB values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The range of valid input values is:
+ *            Y [16 ... 235]
+ *            U [16 ... 240]
+ *            V [16 ... 240]
+ *      (2) Conversion of RGB --> YUV --> RGB leaves the image unchanged.
+ *      (3) The YUV gamut is larger than the RBG gamut; many YUV values
+ *          will result in an invalid RGB value.  We clip individual
+ *          r,g,b components to the range [0, 255], and do not test input.
+ * 
+ */ +l_ok +convertYUVToRGB(l_int32 yval, + l_int32 uval, + l_int32 vval, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 rval, gval, bval; +l_float32 norm, ym, um, vm; + + PROCNAME("convertYUVToRGB"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval || !pgval || !pbval) + return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); + + norm = 1.0 / 256.; + ym = yval - 16.0; + um = uval - 128.0; + vm = vval - 128.0; + rval = (l_int32)(norm * (298.082 * ym + 408.583 * vm) + 0.5); + gval = (l_int32)(norm * (298.082 * ym - 100.291 * um - 208.120 * vm) + + 0.5); + bval = (l_int32)(norm * (298.082 * ym + 516.411 * um) + 0.5); + *prval = L_MIN(255, L_MAX(0, rval)); + *pgval = L_MIN(255, L_MAX(0, gval)); + *pbval = L_MIN(255, L_MAX(0, bval)); + + return 0; +} + + +/*! + * \brief pixcmapConvertRGBToYUV() + * + * \param[in] cmap + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      ~ in-place transform
+ *      ~ See convertRGBToYUV() for def'n of YUV space.
+ *      ~ replaces: r --> y, g --> u, b --> v
+ * 
+ */ +l_ok +pixcmapConvertRGBToYUV(PIXCMAP *cmap) +{ +l_int32 i, ncolors, rval, gval, bval, yval, uval, vval; + + PROCNAME("pixcmapConvertRGBToYUV"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval); + pixcmapResetColor(cmap, i, yval, uval, vval); + } + return 0; +} + + +/*! + * \brief pixcmapConvertYUVToRGB() + * + * \param[in] cmap + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      ~ in-place transform
+ *      ~ See convertRGBToYUV() for def'n of YUV space.
+ *      ~ replaces: y --> r, u --> g, v --> b
+ * 
+ */ +l_ok +pixcmapConvertYUVToRGB(PIXCMAP *cmap) +{ +l_int32 i, ncolors, rval, gval, bval, yval, uval, vval; + + PROCNAME("pixcmapConvertYUVToRGB"); + + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &yval, &uval, &vval); + convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval); + pixcmapResetColor(cmap, i, rval, gval, bval); + } + return 0; +} + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between RGB and XYZ * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToXYZ() + * + * \param[in] pixs 32 bpp rgb + * \return fpixa xyz + * + *
+ * Notes:
+ *      (1) The [x,y,z] values are stored as float values in three fpix
+ *          that are returned in a fpixa.
+ *      (2) The XYZ color space was defined in 1931 as a reference model that
+ *          simulates human color perception.  When Y is taken as luminance,
+ *          the values of X and Z constitute a color plane representing
+ *          all the hues that can be perceived.  This gamut of colors
+ *          is larger than the gamuts that can be displayed or printed.
+ *          For example, although all rgb values map to XYZ, the converse
+ *          is not true.
+ *      (3) The value of the coefficients depends on the illuminant.  We use
+ *          coefficients for converting sRGB under D65 (the spectrum from
+ *          a 6500 degree K black body; an approximation to daylight color).
+ *          See, e.g.,
+ *             http://www.cs.rit.edu/~ncs/color/t_convert.html
+ *          For more general information on color transforms, see:
+ *             http://www.brucelindbloom.com/
+ *             http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html
+ *             http://en.wikipedia.org/wiki/CIE_1931_color_space
+ * 
+ */ +FPIXA * +pixConvertRGBToXYZ(PIX *pixs) +{ +l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; +l_uint32 *lines, *datas; +l_float32 fxval, fyval, fzval; +l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; +FPIX *fpix; +FPIXA *fpixa; + + PROCNAME("pixConvertRGBToXYZ"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); + + /* Convert RGB image */ + pixGetDimensions(pixs, &w, &h, NULL); + fpixa = fpixaCreate(3); + for (i = 0; i < 3; i++) { + fpix = fpixCreate(w, h); + fpixaAddFPix(fpixa, fpix, L_INSERT); + } + wpls = pixGetWpl(pixs); + wpld = fpixGetWpl(fpix); + datas = pixGetData(pixs); + datax = fpixaGetData(fpixa, 0); + datay = fpixaGetData(fpixa, 1); + dataz = fpixaGetData(fpixa, 2); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linex = datax + i * wpld; + liney = datay + i * wpld; + linez = dataz + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval); + *(linex + j) = fxval; + *(liney + j) = fyval; + *(linez + j) = fzval; + } + } + + return fpixa; +} + + +/*! + * \brief fpixaConvertXYZToRGB() + * + * \param[in] fpixa three fpix: x,y,z + * \return pixd 32 bpp rgb + * + *
+ * Notes:
+ *      (1) The xyz image is stored in three fpix.
+ *      (2) For values of xyz that are out of gamut for rgb, the rgb
+ *          components are set to the closest valid color.
+ * 
+ */ +PIX * +fpixaConvertXYZToRGB(FPIXA *fpixa) +{ +l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; +l_float32 fxval, fyval, fzval; +l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; +l_uint32 *lined, *datad; +PIX *pixd; +FPIX *fpix; + + PROCNAME("fpixaConvertXYZToRGB"); + + if (!fpixa || fpixaGetCount(fpixa) != 3) + return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL); + + /* Convert XYZ image */ + if (fpixaGetFPixDimensions(fpixa, 0, &w, &h)) + return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL); + pixd = pixCreate(w, h, 32); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + datax = fpixaGetData(fpixa, 0); + datay = fpixaGetData(fpixa, 1); + dataz = fpixaGetData(fpixa, 2); + fpix = fpixaGetFPix(fpixa, 0, L_CLONE); + wpls = fpixGetWpl(fpix); + fpixDestroy(&fpix); + for (i = 0; i < h; i++) { + linex = datax + i * wpls; + liney = datay + i * wpls; + linez = dataz + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + fxval = linex[j]; + fyval = liney[j]; + fzval = linez[j]; + convertXYZToRGB(fxval, fyval, fzval, 0, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + return pixd; +} + + +/*! + * \brief convertRGBToXYZ() + * + * \param[in] rval, gval, bval rgb input + * \param[out] pfxval, pfyval, pfzval equivalent xyz values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) These conversions are for illuminant D65 acting on linear sRGB
+ *          values.
+ * 
+ */ +l_ok +convertRGBToXYZ(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_float32 *pfxval, + l_float32 *pfyval, + l_float32 *pfzval) +{ + PROCNAME("convertRGBToXYZ"); + + if (pfxval) *pfxval = 0.0; + if (pfyval) *pfyval = 0.0; + if (pfzval) *pfzval = 0.0; + if (!pfxval || !pfyval || !pfzval) + return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1); + + *pfxval = 0.4125 * rval + 0.3576 * gval + 0.1804 * bval; + *pfyval = 0.2127 * rval + 0.7152 * gval + 0.0722 * bval; + *pfzval = 0.0193 * rval + 0.1192 * gval + 0.9502 * bval; + return 0; +} + + +/*! + * \brief convertXYZToRGB() + * + * \param[in] fxval, fyval, fzval + * \param[in] blackout 0 to output nearest color if out of gamut; + * 1 to output black + * \param[out] prval, pgval, pbval 32 bpp rgb values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For values of xyz that are out of gamut for rgb, at least
+ *          one of the r, g or b components will be either less than 0
+ *          or greater than 255.  For that situation:
+ *            * if %blackout == 0, the individual component(s) that are out
+ *              of gamut will be set to 0 or 255, respectively.
+ *            * if %blackout == 1, the output color will be set to black
+ * 
+ */ +l_ok +convertXYZToRGB(l_float32 fxval, + l_float32 fyval, + l_float32 fzval, + l_int32 blackout, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 rval, gval, bval; + + PROCNAME("convertXYZToRGB"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval || !pgval ||!pbval) + return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); + *prval = *pgval = *pbval = 0; + + rval = (l_int32)(3.2405 * fxval - 1.5372 * fyval - 0.4985 * fzval + 0.5); + gval = (l_int32)(-0.9693 * fxval + 1.8760 * fyval + 0.0416 * fzval + 0.5); + bval = (l_int32)(0.0556 * fxval - 0.2040 * fyval + 1.0573 * fzval + 0.5); + if (blackout == 0) { /* the usual situation; use nearest rgb color */ + *prval = L_MAX(0, L_MIN(rval, 255)); + *pgval = L_MAX(0, L_MIN(gval, 255)); + *pbval = L_MAX(0, L_MIN(bval, 255)); + } else { /* use black for out of gamut */ + if (rval >= 0 && rval < 256 && gval >= 0 && gval < 256 && + bval >= 0 && bval < 256) { /* in gamut */ + *prval = rval; + *pgval = gval; + *pbval = bval; + } + } + return 0; +} + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between XYZ and LAB * + *---------------------------------------------------------------------------*/ +/*! + * \brief fpixaConvertXYZToLAB() + * + * \param[in] fpixas xyz + * \return fpixa lab + * + *
+ * Notes:
+ *      (1) The input [x,y,z] and output [l,a,b] values are stored as
+ *          float values, each set in three fpix.
+ *      (2) The CIE LAB color space was invented in 1976, as an
+ *          absolute reference for specifying colors that we can
+ *          perceive, independently of the rendering device.  It was
+ *          invented to align color display and print images.
+ *          For information, see:
+ *             http://www.brucelindbloom.com/
+ *             http://en.wikipedia.org/wiki/Lab_color_space
+ * 
+ */ +FPIXA * +fpixaConvertXYZToLAB(FPIXA *fpixas) +{ +l_int32 w, h, wpl, i, j; +l_float32 fxval, fyval, fzval, flval, faval, fbval; +l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; +l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; +FPIX *fpix; +FPIXA *fpixad; + + PROCNAME("fpixaConvertXYZToLAB"); + + if (!fpixas || fpixaGetCount(fpixas) != 3) + return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL); + + /* Convert XYZ image */ + if (fpixaGetFPixDimensions(fpixas, 0, &w, &h)) + return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL); + fpixad = fpixaCreate(3); + for (i = 0; i < 3; i++) { + fpix = fpixCreate(w, h); + fpixaAddFPix(fpixad, fpix, L_INSERT); + } + wpl = fpixGetWpl(fpix); + datax = fpixaGetData(fpixas, 0); + datay = fpixaGetData(fpixas, 1); + dataz = fpixaGetData(fpixas, 2); + datal = fpixaGetData(fpixad, 0); + dataa = fpixaGetData(fpixad, 1); + datab = fpixaGetData(fpixad, 2); + + /* Convert XYZ image */ + for (i = 0; i < h; i++) { + linex = datax + i * wpl; + liney = datay + i * wpl; + linez = dataz + i * wpl; + linel = datal + i * wpl; + linea = dataa + i * wpl; + lineb = datab + i * wpl; + for (j = 0; j < w; j++) { + fxval = *(linex + j); + fyval = *(liney + j); + fzval = *(linez + j); + convertXYZToLAB(fxval, fyval, fzval, &flval, &faval, &fbval); + *(linel + j) = flval; + *(linea + j) = faval; + *(lineb + j) = fbval; + } + } + + return fpixad; +} + + +/*! + * \brief fpixaConvertLABToXYZ() + * + * \param[in] fpixas lab + * \return fpixa xyz + * + *
+ * Notes:
+ *      (1) The input [l,a,b] and output [x,y,z] values are stored as
+ *          float values, each set in three fpix.
+ * 
+ */ +FPIXA * +fpixaConvertLABToXYZ(FPIXA *fpixas) +{ +l_int32 w, h, wpl, i, j; +l_float32 fxval, fyval, fzval, flval, faval, fbval; +l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; +l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; +FPIX *fpix; +FPIXA *fpixad; + + PROCNAME("fpixaConvertLABToXYZ"); + + if (!fpixas || fpixaGetCount(fpixas) != 3) + return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL); + + /* Convert LAB image */ + if (fpixaGetFPixDimensions(fpixas, 0, &w, &h)) + return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL); + fpixad = fpixaCreate(3); + for (i = 0; i < 3; i++) { + fpix = fpixCreate(w, h); + fpixaAddFPix(fpixad, fpix, L_INSERT); + } + wpl = fpixGetWpl(fpix); + datal = fpixaGetData(fpixas, 0); + dataa = fpixaGetData(fpixas, 1); + datab = fpixaGetData(fpixas, 2); + datax = fpixaGetData(fpixad, 0); + datay = fpixaGetData(fpixad, 1); + dataz = fpixaGetData(fpixad, 2); + + /* Convert XYZ image */ + for (i = 0; i < h; i++) { + linel = datal + i * wpl; + linea = dataa + i * wpl; + lineb = datab + i * wpl; + linex = datax + i * wpl; + liney = datay + i * wpl; + linez = dataz + i * wpl; + for (j = 0; j < w; j++) { + flval = *(linel + j); + faval = *(linea + j); + fbval = *(lineb + j); + convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval); + *(linex + j) = fxval; + *(liney + j) = fyval; + *(linez + j) = fzval; + } + } + + return fpixad; +} + + +/*! + * \brief convertXYZToLAB() + * + * \param[in] xval, yval, zval input xyz + * \param[out] plval, paval, pbval equivalent lab values + * \return 0 if OK, 1 on error + */ +l_ok +convertXYZToLAB(l_float32 xval, + l_float32 yval, + l_float32 zval, + l_float32 *plval, + l_float32 *paval, + l_float32 *pbval) +{ +l_float32 xn, yn, zn, fx, fy, fz; + + PROCNAME("convertXYZToLAB"); + + if (plval) *plval = 0.0; + if (paval) *paval = 0.0; + if (pbval) *pbval = 0.0; + if (!plval || !paval || !pbval) + return ERROR_INT("&lval, &aval, &bval not all defined", procName, 1); + + /* First normalize to the corresponding white values */ + xn = 0.0041259 * xval; + yn = 0.0039216 * yval; + zn = 0.0036012 * zval; + /* Then apply the lab_forward function */ + fx = lab_forward(xn); + fy = lab_forward(yn); + fz = lab_forward(zn); + *plval = 116.0 * fy - 16.0; + *paval = 500.0 * (fx - fy); + *pbval = 200.0 * (fy - fz); + return 0; +} + + +/*! + * \brief convertLABToXYZ() + * + * \param[in] lval, aval, bval input lab + * \param[out] pxval, pyval, pzval equivalent xyz values + * \return 0 if OK, 1 on error + */ +l_ok +convertLABToXYZ(l_float32 lval, + l_float32 aval, + l_float32 bval, + l_float32 *pxval, + l_float32 *pyval, + l_float32 *pzval) +{ +l_float32 fx, fy, fz; +l_float32 xw = 242.37; /* x component corresponding to rgb white */ +l_float32 yw = 255.0; /* y component corresponding to rgb white */ +l_float32 zw = 277.69; /* z component corresponding to rgb white */ + + PROCNAME("convertLABToXYZ"); + + if (pxval) *pxval = 0.0; + if (pyval) *pyval = 0.0; + if (pzval) *pzval = 0.0; + if (!pxval || !pyval || !pzval) + return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1); + + fy = 0.0086207 * (16.0 + lval); + fx = fy + 0.002 * aval; + fz = fy - 0.005 * bval; + *pxval = xw * lab_reverse(fx); + *pyval = yw * lab_reverse(fy); + *pzval = zw * lab_reverse(fz); + return 0; +} + + +/* + * See http://en.wikipedia.org/wiki/Lab_color_space for formulas. + * This is the forward function: from xyz to lab. It includes a rational + * function approximation over [0.008856 ... 1] to the cube root, from + * "Fast Color Space Transformations Using Minimax Approximations", + * M. Celebi et al, http://arxiv.org/pdf/1009.0854v1.pdf. + */ +static l_float32 +lab_forward(l_float32 v) +{ +const l_float32 f_thresh = 0.008856; /* (6/29)^3 */ +const l_float32 f_factor = 7.787; /* (1/3) * (29/6)^2) */ +const l_float32 f_offset = 0.13793; /* 4/29 */ + + if (v > f_thresh) { +#if SLOW_CUBE_ROOT + return powf(v, 0.333333); +#else + l_float32 num, den; + num = 4.37089e-04 + v * (9.52695e-02 + v * (1.25201 + v * 1.30273)); + den = 3.91236e-03 + v * (2.95408e-01 + v * (1.71714 + v * 6.34341e-01)); + return num / den; +#endif + } else { + return f_factor * v + f_offset; + } +} + + +/* + * See http://en.wikipedia.org/wiki/Lab_color_space for formulas. + * This is the reverse (inverse) function: from lab to xyz. + */ +static l_float32 +lab_reverse(l_float32 v) +{ +const l_float32 r_thresh = 0.20690; /* 6/29 */ +const l_float32 r_factor = 0.12842; /* 3 * (6/29)^2 */ +const l_float32 r_offset = 0.13793; /* 4/29 */ + + if (v > r_thresh) { + return v * v * v; + } else { + return r_factor * (v - r_offset); + } +} + + +/*---------------------------------------------------------------------------* + * Colorspace conversion between RGB and LAB * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToLAB() + * + * \param[in] pixs 32 bpp rgb + * \return fpixa lab + * + *
+ * Notes:
+ *      (1) The [l,a,b] values are stored as float values in three fpix
+ *          that are returned in a fpixa.
+ * 
+ */ +FPIXA * +pixConvertRGBToLAB(PIX *pixs) +{ +l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; +l_uint32 *lines, *datas; +l_float32 flval, faval, fbval; +l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; +FPIX *fpix; +FPIXA *fpixa; + + PROCNAME("pixConvertRGBToLAB"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); + + /* Convert RGB image */ + pixGetDimensions(pixs, &w, &h, NULL); + fpixa = fpixaCreate(3); + for (i = 0; i < 3; i++) { + fpix = fpixCreate(w, h); + fpixaAddFPix(fpixa, fpix, L_INSERT); + } + wpls = pixGetWpl(pixs); + wpld = fpixGetWpl(fpix); + datas = pixGetData(pixs); + datal = fpixaGetData(fpixa, 0); + dataa = fpixaGetData(fpixa, 1); + datab = fpixaGetData(fpixa, 2); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linel = datal + i * wpld; + linea = dataa + i * wpld; + lineb = datab + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + convertRGBToLAB(rval, gval, bval, &flval, &faval, &fbval); + *(linel + j) = flval; + *(linea + j) = faval; + *(lineb + j) = fbval; + } + } + + return fpixa; +} + + +/*! + * \brief fpixaConvertLABToRGB() + * + * \param[in] fpixa three fpix: l,a,b + * \return pixd 32 bpp rgb + * + *
+ * Notes:
+ *      (1) The lab image is stored in three fpix.
+ * 
+ */ +PIX * +fpixaConvertLABToRGB(FPIXA *fpixa) +{ +l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; +l_float32 flval, faval, fbval; +l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; +l_uint32 *lined, *datad; +PIX *pixd; +FPIX *fpix; + + PROCNAME("fpixaConvertLABToRGB"); + + if (!fpixa || fpixaGetCount(fpixa) != 3) + return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL); + + /* Convert LAB image */ + if (fpixaGetFPixDimensions(fpixa, 0, &w, &h)) + return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL); + pixd = pixCreate(w, h, 32); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + datal = fpixaGetData(fpixa, 0); + dataa = fpixaGetData(fpixa, 1); + datab = fpixaGetData(fpixa, 2); + fpix = fpixaGetFPix(fpixa, 0, L_CLONE); + wpls = fpixGetWpl(fpix); + fpixDestroy(&fpix); + for (i = 0; i < h; i++) { + linel = datal + i * wpls; + linea = dataa + i * wpls; + lineb = datab + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + flval = linel[j]; + faval = linea[j]; + fbval = lineb[j]; + convertLABToRGB(flval, faval, fbval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + return pixd; +} + + +/*! + * \brief convertRGBToLAB() + * + * \param[in] rval, gval, bval rgb input + * \param[out] pflval, pfaval, pfbval equivalent lab values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) These conversions are for illuminant D65 acting on linear sRGB
+ *          values.
+ * 
+ */ +l_ok +convertRGBToLAB(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_float32 *pflval, + l_float32 *pfaval, + l_float32 *pfbval) +{ +l_float32 fxval, fyval, fzval; + + PROCNAME("convertRGBToLAB"); + + if (pflval) *pflval = 0.0; + if (pfaval) *pfaval = 0.0; + if (pfbval) *pfbval = 0.0; + if (!pflval || !pfaval || !pfbval) + return ERROR_INT("&flval, &faval, &fbval not all defined", procName, 1); + + convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval); + convertXYZToLAB(fxval, fyval, fzval, pflval, pfaval, pfbval); + return 0; +} + + +/*! + * \brief convertLABToRGB() + * + * \param[in] flval, faval, fbval input lab + * \param[out] prval, pgval, pbval equivalent rgb values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For values of lab that are out of gamut for rgb, the rgb
+ *          components are set to the closest valid color.
+ * 
+ */ +l_ok +convertLABToRGB(l_float32 flval, + l_float32 faval, + l_float32 fbval, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_float32 fxval, fyval, fzval; + + PROCNAME("convertLABToRGB"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval || !pgval || !pbval) + return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); + + convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval); + convertXYZToRGB(fxval, fyval, fzval, 0, prval, pgval, pbval); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/compare.c b/hgdriver/3rdparty/hgOCR/leptonica/compare.c new file mode 100644 index 0000000..b3952ec --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/compare.c @@ -0,0 +1,3604 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file compare.c + *
+ *
+ *      Test for pix equality
+ *           l_int32     pixEqual()
+ *           l_int32     pixEqualWithAlpha()
+ *           l_int32     pixEqualWithCmap()
+ *           l_int32     cmapEqual()
+ *           l_int32     pixUsesCmapColor()
+ *
+ *      Binary correlation
+ *           l_int32     pixCorrelationBinary()
+ *
+ *      Difference of two images of same size
+ *           l_int32     pixDisplayDiffBinary()
+ *           l_int32     pixCompareBinary()
+ *           l_int32     pixCompareGrayOrRGB()
+ *           l_int32     pixCompareGray()
+ *           l_int32     pixCompareRGB()
+ *           l_int32     pixCompareTiled()
+ *
+ *      Other measures of the difference of two images of the same size
+ *           NUMA       *pixCompareRankDifference()
+ *           l_int32     pixTestForSimilarity()
+ *           l_int32     pixGetDifferenceStats()
+ *           NUMA       *pixGetDifferenceHistogram()
+ *           l_int32     pixGetPerceptualDiff()
+ *           l_int32     pixGetPSNR()
+ *
+ *      Comparison of photo regions by histogram
+ *           l_int32     pixaComparePhotoRegionsByHisto()  -- top-level
+ *           l_int32     pixComparePhotoRegionsByHisto()  -- top-level for 2
+ *           l_int32     pixGenPhotoHistos()
+ *           PIX        *pixPadToCenterCentroid()
+ *           l_int32     pixCentroid8()
+ *           l_int32     pixDecideIfPhotoImage()
+ *       static l_int32  findHistoGridDimensions()
+ *           l_int32     compareTilesByHisto()
+ *
+ *           l_int32     pixCompareGrayByHisto()  -- top-level for 2
+ *       static l_int32  pixCompareTilesByHisto()
+ *           l_int32     pixCropAlignedToCentroid()
+ *
+ *           l_uint8    *l_compressGrayHistograms()
+ *           NUMAA      *l_uncompressGrayHistograms()
+ *
+ *      Translated images at the same resolution
+ *           l_int32     pixCompareWithTranslation()
+ *           l_int32     pixBestCorrelation()
+ *
+ *  For comparing images using tiled histograms, essentially all the
+ *  computation goes into deciding if a region of an image is a photo,
+ *  whether that photo region is amenable to similarity measurements
+ *  using histograms, and finally the calculation of the gray histograms
+ *  for each of the tiled regions.  The actual comparison is essentially
+ *  instantaneous.  Therefore, with a large number of images to compare
+ *  with each other, it is important to first calculate the histograms
+ *  for each image.  Then the comparisons, which go as the square of the
+ *  number of images, actually takes no time.
+ *
+ *  A high level function that takes a pixa of images and does
+ *  all comparisons, pixaComparePhotosByHisto(), uses this split
+ *  approach.  It pads the images so that the centroid is in the center,
+ *  which will allow the tiles to be better aligned.
+ *
+ *  For testing purposes, two functions are given that do all the work
+ *  to compare just two photo regions:
+ *    *  pixComparePhotoRegionsByHisto() uses the split approach, qualifying
+ *       the images first with pixGenPhotoHistos(), and then comparing
+ *       with compareTilesByHisto().
+ *    *  pixCompareGrayByHisto() aligns the two images by centroid
+ *       and calls pixCompareTilesByHisto() to generate the histograms
+ *       and do the comparison.
+ *
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Small enough to consider equal to 0.0, for plot output */ +static const l_float32 TINY = 0.00001; + +static l_ok findHistoGridDimensions(l_int32 n, l_int32 w, l_int32 h, + l_int32 *pnx, l_int32 *pny, l_int32 debug); +static l_ok pixCompareTilesByHisto(PIX *pix1, PIX *pix2, l_int32 maxgray, + l_int32 factor, l_int32 n, + l_float32 *pscore, PIXA *pixadebug); + + +/*------------------------------------------------------------------* + * Test for pix equality * + *------------------------------------------------------------------*/ +/*! + * \brief pixEqual() + * + * \param[in] pix1 + * \param[in] pix2 + * \param[out] psame 1 if same; 0 if different + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Equality is defined as having the same pixel values for
+ *          each respective image pixel.
+ *      (2) This works on two pix of any depth.  If one or both pix
+ *          have a colormap, the depths can be different and the
+ *          two pix can still be equal.
+ *      (3) This ignores the alpha component for 32 bpp images.
+ *      (4) If both pix have colormaps and the depths are equal,
+ *          use the pixEqualWithCmap() function, which does a fast
+ *          comparison if the colormaps are identical and a relatively
+ *          slow comparison otherwise.
+ *      (5) In all other cases, any existing colormaps must first be
+ *          removed before doing pixel comparison.  After the colormaps
+ *          are removed, the resulting two images must have the same depth.
+ *          The "lowest common denominator" is RGB, but this is only
+ *          chosen when necessary, or when both have colormaps but
+ *          different depths.
+ *      (6) For images without colormaps that are not 32 bpp, all bits
+ *          in the image part of the data array must be identical.
+ * 
+ */ +l_ok +pixEqual(PIX *pix1, + PIX *pix2, + l_int32 *psame) +{ + return pixEqualWithAlpha(pix1, pix2, 0, psame); +} + + +/*! + * \brief pixEqualWithAlpha() + * + * \param[in] pix1 + * \param[in] pix2 + * \param[in] use_alpha 1 to compare alpha in RGBA; 0 to ignore + * \param[out] psame 1 if same; 0 if different + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See notes in pixEqual().
+ *      (2) This is more general than pixEqual(), in that for 32 bpp
+ *          RGBA images, where spp = 4, you can optionally include
+ *          the alpha component in the comparison.
+ * 
+ */ +l_ok +pixEqualWithAlpha(PIX *pix1, + PIX *pix2, + l_int32 use_alpha, + l_int32 *psame) +{ +l_int32 w1, h1, d1, w2, h2, d2, wpl1, wpl2; +l_int32 spp1, spp2, i, j, color, mismatch, opaque; +l_int32 fullwords, linebits, endbits; +l_uint32 endmask, wordmask; +l_uint32 *data1, *data2, *line1, *line2; +PIX *pixs1, *pixs2, *pixt1, *pixt2, *pixalpha; +PIXCMAP *cmap1, *cmap2; + + PROCNAME("pixEqualWithAlpha"); + + if (!psame) + return ERROR_INT("psame not defined", procName, 1); + *psame = 0; /* init to not equal */ + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + pixGetDimensions(pix1, &w1, &h1, &d1); + pixGetDimensions(pix2, &w2, &h2, &d2); + if (w1 != w2 || h1 != h2) { + L_INFO("pix sizes differ\n", procName); + return 0; + } + + /* Suppose the use_alpha flag is true. + * If only one of two 32 bpp images has spp == 4, we call that + * a "mismatch" of the alpha component. In the case of a mismatch, + * if the 4 bpp pix does not have all alpha components opaque (255), + * the images are not-equal. However if they are all opaque, + * this image is equivalent to spp == 3, so we allow the + * comparison to go forward, testing only for the RGB equality. */ + spp1 = pixGetSpp(pix1); + spp2 = pixGetSpp(pix2); + mismatch = 0; + if (use_alpha && d1 == 32 && d2 == 32) { + mismatch = ((spp1 == 4 && spp2 != 4) || (spp1 != 4 && spp2 == 4)); + if (mismatch) { + pixalpha = (spp1 == 4) ? pix1 : pix2; + pixAlphaIsOpaque(pixalpha, &opaque); + if (!opaque) { + L_INFO("just one pix has a non-opaque alpha layer\n", procName); + return 0; + } + } + } + + cmap1 = pixGetColormap(pix1); + cmap2 = pixGetColormap(pix2); + if (!cmap1 && !cmap2 && (d1 != d2) && (d1 == 32 || d2 == 32)) { + L_INFO("no colormaps, pix depths unequal, and one of them is RGB\n", + procName); + return 0; + } + + if (cmap1 && cmap2 && (d1 == d2)) /* use special function */ + return pixEqualWithCmap(pix1, pix2, psame); + + /* Must remove colormaps if they exist, and in the process + * end up with the resulting images having the same depth. */ + if (cmap1 && !cmap2) { + pixUsesCmapColor(pix1, &color); + if (color && d2 <= 8) /* can't be equal */ + return 0; + if (d2 < 8) + pixs2 = pixConvertTo8(pix2, FALSE); + else + pixs2 = pixClone(pix2); + if (d2 <= 8) + pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_GRAYSCALE); + else + pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); + } else if (!cmap1 && cmap2) { + pixUsesCmapColor(pix2, &color); + if (color && d1 <= 8) /* can't be equal */ + return 0; + if (d1 < 8) + pixs1 = pixConvertTo8(pix1, FALSE); + else + pixs1 = pixClone(pix1); + if (d1 <= 8) + pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_GRAYSCALE); + else + pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); + } else if (cmap1 && cmap2) { /* depths not equal; use rgb */ + pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); + pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); + } else { /* no colormaps */ + pixs1 = pixClone(pix1); + pixs2 = pixClone(pix2); + } + + /* OK, we have no colormaps, but the depths may still be different */ + d1 = pixGetDepth(pixs1); + d2 = pixGetDepth(pixs2); + if (d1 != d2) { + if (d1 == 16 || d2 == 16) { + L_INFO("one pix is 16 bpp\n", procName); + pixDestroy(&pixs1); + pixDestroy(&pixs2); + return 0; + } + pixt1 = pixConvertLossless(pixs1, 8); + pixt2 = pixConvertLossless(pixs2, 8); + if (!pixt1 || !pixt2) { + L_INFO("failure to convert to 8 bpp\n", procName); + pixDestroy(&pixs1); + pixDestroy(&pixs2); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return 0; + } + } else { + pixt1 = pixClone(pixs1); + pixt2 = pixClone(pixs2); + } + pixDestroy(&pixs1); + pixDestroy(&pixs2); + + /* No colormaps, equal depths; do pixel comparisons */ + d1 = pixGetDepth(pixt1); + d2 = pixGetDepth(pixt2); + wpl1 = pixGetWpl(pixt1); + wpl2 = pixGetWpl(pixt2); + data1 = pixGetData(pixt1); + data2 = pixGetData(pixt2); + + if (d1 == 32) { /* test either RGB or RGBA pixels */ + if (use_alpha && !mismatch) + wordmask = (spp1 == 3) ? 0xffffff00 : 0xffffffff; + else + wordmask = 0xffffff00; + for (i = 0; i < h1; i++) { + line1 = data1 + wpl1 * i; + line2 = data2 + wpl2 * i; + for (j = 0; j < wpl1; j++) { + if ((*line1 ^ *line2) & wordmask) { + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return 0; + } + line1++; + line2++; + } + } + } else { /* all bits count */ + linebits = d1 * w1; + fullwords = linebits / 32; + endbits = linebits & 31; + endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits)); + for (i = 0; i < h1; i++) { + line1 = data1 + wpl1 * i; + line2 = data2 + wpl2 * i; + for (j = 0; j < fullwords; j++) { + if (*line1 ^ *line2) { + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return 0; + } + line1++; + line2++; + } + if (endbits) { + if ((*line1 ^ *line2) & endmask) { + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return 0; + } + } + } + } + + pixDestroy(&pixt1); + pixDestroy(&pixt2); + *psame = 1; + return 0; +} + + +/*! + * \brief pixEqualWithCmap() + * + * \param[in] pix1 + * \param[in] pix2 + * \param[out] psame + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns same = TRUE if the images have identical content.
+ *      (2) Both pix must have a colormap, and be of equal size and depth.
+ *          If these conditions are not satisfied, it is not an error;
+ *          the returned result is same = FALSE.
+ *      (3) We then check whether the colormaps are the same; if so,
+ *          the comparison proceeds 32 bits at a time.
+ *      (4) If the colormaps are different, the comparison is done by
+ *          slow brute force.
+ * 
+ */ +l_ok +pixEqualWithCmap(PIX *pix1, + PIX *pix2, + l_int32 *psame) +{ +l_int32 d, w, h, wpl1, wpl2, i, j, linebits, fullwords, endbits; +l_int32 rval1, rval2, gval1, gval2, bval1, bval2, samecmaps; +l_uint32 endmask, val1, val2; +l_uint32 *data1, *data2, *line1, *line2; +PIXCMAP *cmap1, *cmap2; + + PROCNAME("pixEqualWithCmap"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + + if (pixSizesEqual(pix1, pix2) == 0) + return 0; + cmap1 = pixGetColormap(pix1); + cmap2 = pixGetColormap(pix2); + if (!cmap1 || !cmap2) { + L_INFO("both images don't have colormap\n", procName); + return 0; + } + pixGetDimensions(pix1, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8) { + L_INFO("pix depth not in {1, 2, 4, 8}\n", procName); + return 0; + } + + cmapEqual(cmap1, cmap2, 3, &samecmaps); + if (samecmaps == TRUE) { /* colormaps are identical; compare by words */ + linebits = d * w; + wpl1 = pixGetWpl(pix1); + wpl2 = pixGetWpl(pix2); + data1 = pixGetData(pix1); + data2 = pixGetData(pix2); + fullwords = linebits / 32; + endbits = linebits & 31; + endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits)); + for (i = 0; i < h; i++) { + line1 = data1 + wpl1 * i; + line2 = data2 + wpl2 * i; + for (j = 0; j < fullwords; j++) { + if (*line1 ^ *line2) + return 0; + line1++; + line2++; + } + if (endbits) { + if ((*line1 ^ *line2) & endmask) + return 0; + } + } + *psame = 1; + return 0; + } + + /* Colormaps aren't identical; compare pixel by pixel */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pix1, j, i, &val1); + pixGetPixel(pix2, j, i, &val2); + pixcmapGetColor(cmap1, val1, &rval1, &gval1, &bval1); + pixcmapGetColor(cmap2, val2, &rval2, &gval2, &bval2); + if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) + return 0; + } + } + + *psame = 1; + return 0; +} + + +/*! + * \brief cmapEqual() + * + * \param[in] cmap1 + * \param[in] cmap2 + * \param[in] ncomps 3 for RGB, 4 for RGBA + * \param[out] psame + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns %same = TRUE if the colormaps have identical entries.
+ *      (2) If %ncomps == 4, the alpha components of the colormaps are also
+ *          compared.
+ * 
+ */ +l_ok +cmapEqual(PIXCMAP *cmap1, + PIXCMAP *cmap2, + l_int32 ncomps, + l_int32 *psame) +{ +l_int32 n1, n2, i, rval1, rval2, gval1, gval2, bval1, bval2, aval1, aval2; + + PROCNAME("cmapEqual"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = FALSE; + if (!cmap1) + return ERROR_INT("cmap1 not defined", procName, 1); + if (!cmap2) + return ERROR_INT("cmap2 not defined", procName, 1); + if (ncomps != 3 && ncomps != 4) + return ERROR_INT("ncomps not 3 or 4", procName, 1); + + n1 = pixcmapGetCount(cmap1); + n2 = pixcmapGetCount(cmap2); + if (n1 != n2) { + L_INFO("colormap sizes are different\n", procName); + return 0; + } + + for (i = 0; i < n1; i++) { + pixcmapGetRGBA(cmap1, i, &rval1, &gval1, &bval1, &aval1); + pixcmapGetRGBA(cmap2, i, &rval2, &gval2, &bval2, &aval2); + if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) + return 0; + if (ncomps == 4 && aval1 != aval2) + return 0; + } + *psame = TRUE; + return 0; +} + + +/*! + * \brief pixUsesCmapColor() + * + * \param[in] pixs any depth, colormap + * \param[out] pcolor TRUE if color found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns color = TRUE if three things are obtained:
+ *          (a) the pix has a colormap
+ *          (b) the colormap has at least one color entry
+ *          (c) a color entry is actually used
+ *      (2) It is used in pixEqual() for comparing two images, in a
+ *          situation where it is required to know if the colormap
+ *          has color entries that are actually used in the image.
+ * 
+ */ +l_ok +pixUsesCmapColor(PIX *pixs, + l_int32 *pcolor) +{ +l_int32 n, i, rval, gval, bval, numpix; +NUMA *na; +PIXCMAP *cmap; + + PROCNAME("pixUsesCmapColor"); + + if (!pcolor) + return ERROR_INT("&color not defined", procName, 1); + *pcolor = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + if ((cmap = pixGetColormap(pixs)) == NULL) + return 0; + + pixcmapHasColor(cmap, pcolor); + if (*pcolor == 0) /* no color */ + return 0; + + /* The cmap has color entries. Are they used? */ + na = pixGetGrayHistogram(pixs, 1); + n = pixcmapGetCount(cmap); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + numaGetIValue(na, i, &numpix); + if ((rval != gval || rval != bval) && numpix) { /* color found! */ + *pcolor = 1; + break; + } + } + numaDestroy(&na); + + return 0; +} + + +/*------------------------------------------------------------------* + * Binary correlation * + *------------------------------------------------------------------*/ +/*! + * \brief pixCorrelationBinary() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \param[out] pval correlation + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The correlation is a number between 0.0 and 1.0,
+ *          based on foreground similarity:
+ *                           (|1 AND 2|)**2
+ *            correlation =  --------------
+ *                             |1| * |2|
+ *          where |x| is the count of foreground pixels in image x.
+ *          If the images are identical, this is 1.0.
+ *          If they have no fg pixels in common, this is 0.0.
+ *          If one or both images have no fg pixels, the correlation is 0.0.
+ *      (2) Typically the two images are of equal size, but this
+ *          is not enforced.  Instead, the UL corners are aligned.
+ * 
+ */ +l_ok +pixCorrelationBinary(PIX *pix1, + PIX *pix2, + l_float32 *pval) +{ +l_int32 count1, count2, countn; +l_int32 *tab8; +PIX *pixn; + + PROCNAME("pixCorrelationBinary"); + + if (!pval) + return ERROR_INT("&pval not defined", procName, 1); + *pval = 0.0; + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + + tab8 = makePixelSumTab8(); + pixCountPixels(pix1, &count1, tab8); + pixCountPixels(pix2, &count2, tab8); + if (count1 == 0 || count2 == 0) { + LEPT_FREE(tab8); + return 0; + } + pixn = pixAnd(NULL, pix1, pix2); + pixCountPixels(pixn, &countn, tab8); + *pval = (l_float32)countn * (l_float32)countn / + ((l_float32)count1 * (l_float32)count2); + LEPT_FREE(tab8); + pixDestroy(&pixn); + return 0; +} + + +/*------------------------------------------------------------------* + * Difference of two images * + *------------------------------------------------------------------*/ +/*! + * \brief pixDisplayDiffBinary() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \return pixd 4 bpp cmapped, or NULL on error + * + *
+ * Notes:
+ *      (1) This gives a color representation of the difference between
+ *          pix1 and pix2.  The color difference depends on the order.
+ *          The pixels in pixd have 4 colors:
+ *           * unchanged:  black (on), white (off)
+ *           * on in pix1, off in pix2: red
+ *           * on in pix2, off in pix1: green
+ *      (2) This aligns the UL corners of pix1 and pix2, and crops
+ *          to the overlapping pixels.
+ * 
+ */ +PIX * +pixDisplayDiffBinary(PIX *pix1, + PIX *pix2) +{ +l_int32 w1, h1, d1, w2, h2, d2, minw, minh; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixDisplayDiffBinary"); + + if (!pix1 || !pix2) + return (PIX *)ERROR_PTR("pix1, pix2 not both defined", procName, NULL); + pixGetDimensions(pix1, &w1, &h1, &d1); + pixGetDimensions(pix2, &w2, &h2, &d2); + if (d1 != 1 || d2 != 1) + return (PIX *)ERROR_PTR("pix1 and pix2 not 1 bpp", procName, NULL); + minw = L_MIN(w1, w2); + minh = L_MIN(h1, h2); + + pixd = pixCreate(minw, minh, 4); + cmap = pixcmapCreate(4); + pixcmapAddColor(cmap, 255, 255, 255); /* initialized to white */ + pixcmapAddColor(cmap, 0, 0, 0); + pixcmapAddColor(cmap, 255, 0, 0); + pixcmapAddColor(cmap, 0, 255, 0); + pixSetColormap(pixd, cmap); + + pixt = pixAnd(NULL, pix1, pix2); + pixPaintThroughMask(pixd, pixt, 0, 0, 0x0); /* black */ + pixSubtract(pixt, pix1, pix2); + pixPaintThroughMask(pixd, pixt, 0, 0, 0xff000000); /* red */ + pixSubtract(pixt, pix2, pix1); + pixPaintThroughMask(pixd, pixt, 0, 0, 0x00ff0000); /* green */ + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixCompareBinary() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \param[in] comptype L_COMPARE_XOR, L_COMPARE_SUBTRACT + * \param[out] pfract fraction of pixels that are different + * \param[out] ppixdiff [optional] pix of difference + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The two images are aligned at the UL corner, and do not
+ *          need to be the same size.
+ *      (2) If using L_COMPARE_SUBTRACT, pix2 is subtracted from pix1.
+ *      (3) The total number of pixels is determined by pix1.
+ *      (4) On error, the returned fraction is 1.0.
+ * 
+ */ +l_ok +pixCompareBinary(PIX *pix1, + PIX *pix2, + l_int32 comptype, + l_float32 *pfract, + PIX **ppixdiff) +{ +l_int32 w, h, count; +PIX *pixt; + + PROCNAME("pixCompareBinary"); + + if (ppixdiff) *ppixdiff = NULL; + if (!pfract) + return ERROR_INT("&pfract not defined", procName, 1); + *pfract = 1.0; /* initialize to max difference */ + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); + if (comptype != L_COMPARE_XOR && comptype != L_COMPARE_SUBTRACT) + return ERROR_INT("invalid comptype", procName, 1); + + if (comptype == L_COMPARE_XOR) + pixt = pixXor(NULL, pix1, pix2); + else /* comptype == L_COMPARE_SUBTRACT) */ + pixt = pixSubtract(NULL, pix1, pix2); + pixCountPixels(pixt, &count, NULL); + pixGetDimensions(pix1, &w, &h, NULL); + *pfract = (l_float32)(count) / (l_float32)(w * h); + + if (ppixdiff) + *ppixdiff = pixt; + else + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixCompareGrayOrRGB() + * + * \param[in] pix1 2,4,8,16 bpp gray, 32 bpp rgb, or colormapped + * \param[in] pix2 2,4,8,16 bpp gray, 32 bpp rgb, or colormapped + * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF + * \param[in] plottype gplot plot output type, or 0 for no plot + * \param[out] psame [optional] 1 if pixel values are identical + * \param[out] pdiff [optional] average difference + * \param[out] prmsdiff [optional] rms of difference + * \param[out] ppixdiff [optional] pix of difference + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The two images are aligned at the UL corner, and do not
+ *          need to be the same size.  If they are not the same size,
+ *          the comparison will be made over overlapping pixels.
+ *      (2) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (3) If RGB, each component is compared separately.
+ *      (4) If type is L_COMPARE_ABS_DIFF, pix2 is subtracted from pix1
+ *          and the absolute value is taken.
+ *      (5) If type is L_COMPARE_SUBTRACT, pix2 is subtracted from pix1
+ *          and the result is clipped to 0.
+ *      (6) The plot output types are specified in gplot.h.
+ *          Use 0 if no difference plot is to be made.
+ *      (7) If the images are pixelwise identical, no difference
+ *          plot is made, even if requested.  The result (TRUE or FALSE)
+ *          is optionally returned in the parameter 'same'.
+ *      (8) The average difference (either subtracting or absolute value)
+ *          is optionally returned in the parameter 'diff'.
+ *      (9) The RMS difference is optionally returned in the
+ *          parameter 'rmsdiff'.  For RGB, we return the average of
+ *          the RMS differences for each of the components.
+ *     (10) Because pixel values are compared, pix1 and pix2 can be equal when:
+ *          * they are both gray with different depth
+ *          * one is colormapped and the other is not
+ *          * they are both colormapped and have different size colormaps
+ * 
+ */ +l_ok +pixCompareGrayOrRGB(PIX *pix1, + PIX *pix2, + l_int32 comptype, + l_int32 plottype, + l_int32 *psame, + l_float32 *pdiff, + l_float32 *prmsdiff, + PIX **ppixdiff) +{ +l_int32 retval, d1, d2; +PIX *pixt1, *pixt2, *pixs1, *pixs2; + + PROCNAME("pixCompareGrayOrRGB"); + + if (psame) *psame = 0; + if (pdiff) *pdiff = 255.0; + if (prmsdiff) *prmsdiff = 255.0; + if (ppixdiff) *ppixdiff = NULL; + if (!pix1 || pixGetDepth(pix1) == 1) + return ERROR_INT("pix1 not defined or 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) == 1) + return ERROR_INT("pix2 not defined or 1 bpp", procName, 1); + if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) + return ERROR_INT("invalid comptype", procName, 1); + if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) + return ERROR_INT("invalid plottype", procName, 1); + + pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); + pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); + d1 = pixGetDepth(pixt1); + d2 = pixGetDepth(pixt2); + if (d1 < 8) + pixs1 = pixConvertTo8(pixt1, FALSE); + else + pixs1 = pixClone(pixt1); + if (d2 < 8) + pixs2 = pixConvertTo8(pixt2, FALSE); + else + pixs2 = pixClone(pixt2); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + d1 = pixGetDepth(pixs1); + d2 = pixGetDepth(pixs2); + if (d1 != d2) { + pixDestroy(&pixs1); + pixDestroy(&pixs2); + return ERROR_INT("intrinsic depths are not equal", procName, 1); + } + + if (d1 == 8 || d1 == 16) + retval = pixCompareGray(pixs1, pixs2, comptype, plottype, psame, + pdiff, prmsdiff, ppixdiff); + else /* d1 == 32 */ + retval = pixCompareRGB(pixs1, pixs2, comptype, plottype, psame, + pdiff, prmsdiff, ppixdiff); + pixDestroy(&pixs1); + pixDestroy(&pixs2); + return retval; +} + + +/*! + * \brief pixCompareGray() + * + * \param[in] pix1 8 or 16 bpp, not cmapped + * \param[in] pix2 8 or 16 bpp, not cmapped + * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF + * \param[in] plottype gplot plot output type, or 0 for no plot + * \param[out] psame [optional] 1 if pixel values are identical + * \param[out] pdiff [optional] average difference + * \param[out] prmsdiff [optional] rms of difference + * \param[out] ppixdiff [optional] pix of difference + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See pixCompareGrayOrRGB() for details.
+ *      (2) Use pixCompareGrayOrRGB() if the input pix are colormapped.
+ *      (3) Note: setting %plottype > 0 can result in writing named
+ *                output files.
+ * 
+ */ +l_ok +pixCompareGray(PIX *pix1, + PIX *pix2, + l_int32 comptype, + l_int32 plottype, + l_int32 *psame, + l_float32 *pdiff, + l_float32 *prmsdiff, + PIX **ppixdiff) +{ +char buf[64]; +static l_int32 index = 0; +l_int32 d1, d2, same, first, last; +GPLOT *gplot; +NUMA *na, *nac; +PIX *pixt; + + PROCNAME("pixCompareGray"); + + if (psame) *psame = 0; + if (pdiff) *pdiff = 255.0; + if (prmsdiff) *prmsdiff = 255.0; + if (ppixdiff) *ppixdiff = NULL; + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + d1 = pixGetDepth(pix1); + d2 = pixGetDepth(pix2); + if ((d1 != d2) || (d1 != 8 && d1 != 16)) + return ERROR_INT("depths unequal or not 8 or 16 bpp", procName, 1); + if (pixGetColormap(pix1) || pixGetColormap(pix2)) + return ERROR_INT("pix1 and/or pix2 are colormapped", procName, 1); + if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) + return ERROR_INT("invalid comptype", procName, 1); + if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) + return ERROR_INT("invalid plottype", procName, 1); + + lept_mkdir("lept/comp"); + + if (comptype == L_COMPARE_SUBTRACT) + pixt = pixSubtractGray(NULL, pix1, pix2); + else /* comptype == L_COMPARE_ABS_DIFF) */ + pixt = pixAbsDifference(pix1, pix2); + + pixZero(pixt, &same); + if (same) + L_INFO("Images are pixel-wise identical\n", procName); + if (psame) *psame = same; + + if (pdiff) + pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_MEAN_ABSVAL, pdiff); + + /* Don't bother to plot if the images are the same */ + if (plottype && !same) { + L_INFO("Images differ: output plots will be generated\n", procName); + na = pixGetGrayHistogram(pixt, 1); + numaGetNonzeroRange(na, TINY, &first, &last); + nac = numaClipToInterval(na, 0, last); + snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d", index); + gplot = gplotCreate(buf, plottype, + "Pixel Difference Histogram", "diff val", + "number of pixels"); + gplotAddPlot(gplot, NULL, nac, GPLOT_LINES, "gray"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d.png", + index++); + l_fileDisplay(buf, 100, 100, 1.0); + numaDestroy(&na); + numaDestroy(&nac); + } + + if (ppixdiff) + *ppixdiff = pixCopy(NULL, pixt); + + if (prmsdiff) { + if (comptype == L_COMPARE_SUBTRACT) { /* wrong type for rms diff */ + pixDestroy(&pixt); + pixt = pixAbsDifference(pix1, pix2); + } + pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, prmsdiff); + } + + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixCompareRGB() + * + * \param[in] pix1 32 bpp rgb + * \param[in] pix2 32 bpp rgb + * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF + * \param[in] plottype gplot plot output type, or 0 for no plot + * \param[out] psame [optional] 1 if pixel values are identical + * \param[out] pdiff [optional] average difference + * \param[out] prmsdiff [optional] rms of difference + * \param[out] ppixdiff [optional] pix of difference + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See pixCompareGrayOrRGB() for details.
+ *      (2) Note: setting %plottype > 0 can result in writing named
+ *                output files.
+ * 
+ */ +l_ok +pixCompareRGB(PIX *pix1, + PIX *pix2, + l_int32 comptype, + l_int32 plottype, + l_int32 *psame, + l_float32 *pdiff, + l_float32 *prmsdiff, + PIX **ppixdiff) +{ +char buf[64]; +static l_int32 index = 0; +l_int32 rsame, gsame, bsame, same, first, rlast, glast, blast, last; +l_float32 rdiff, gdiff, bdiff; +GPLOT *gplot; +NUMA *nar, *nag, *nab, *narc, *nagc, *nabc; +PIX *pixr1, *pixr2, *pixg1, *pixg2, *pixb1, *pixb2; +PIX *pixr, *pixg, *pixb; + + PROCNAME("pixCompareRGB"); + + if (psame) *psame = 0; + if (pdiff) *pdiff = 0.0; + if (prmsdiff) *prmsdiff = 0.0; + if (ppixdiff) *ppixdiff = NULL; + if (!pix1 || pixGetDepth(pix1) != 32) + return ERROR_INT("pix1 not defined or not 32 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 32) + return ERROR_INT("pix2 not defined or not ew bpp", procName, 1); + if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) + return ERROR_INT("invalid comptype", procName, 1); + if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) + return ERROR_INT("invalid plottype", procName, 1); + + lept_mkdir("lept/comp"); + + pixr1 = pixGetRGBComponent(pix1, COLOR_RED); + pixr2 = pixGetRGBComponent(pix2, COLOR_RED); + pixg1 = pixGetRGBComponent(pix1, COLOR_GREEN); + pixg2 = pixGetRGBComponent(pix2, COLOR_GREEN); + pixb1 = pixGetRGBComponent(pix1, COLOR_BLUE); + pixb2 = pixGetRGBComponent(pix2, COLOR_BLUE); + if (comptype == L_COMPARE_SUBTRACT) { + pixr = pixSubtractGray(NULL, pixr1, pixr2); + pixg = pixSubtractGray(NULL, pixg1, pixg2); + pixb = pixSubtractGray(NULL, pixb1, pixb2); + } else { /* comptype == L_COMPARE_ABS_DIFF) */ + pixr = pixAbsDifference(pixr1, pixr2); + pixg = pixAbsDifference(pixg1, pixg2); + pixb = pixAbsDifference(pixb1, pixb2); + } + + pixZero(pixr, &rsame); + pixZero(pixg, &gsame); + pixZero(pixb, &bsame); + same = rsame && gsame && bsame; + if (same) + L_INFO("Images are pixel-wise identical\n", procName); + if (psame) *psame = same; + + if (pdiff) { + pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_MEAN_ABSVAL, &rdiff); + pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &gdiff); + pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_MEAN_ABSVAL, &bdiff); + *pdiff = (rdiff + gdiff + bdiff) / 3.0; + } + + /* Don't bother to plot if the images are the same */ + if (plottype && !same) { + L_INFO("Images differ: output plots will be generated\n", procName); + nar = pixGetGrayHistogram(pixr, 1); + nag = pixGetGrayHistogram(pixg, 1); + nab = pixGetGrayHistogram(pixb, 1); + numaGetNonzeroRange(nar, TINY, &first, &rlast); + numaGetNonzeroRange(nag, TINY, &first, &glast); + numaGetNonzeroRange(nab, TINY, &first, &blast); + last = L_MAX(rlast, glast); + last = L_MAX(last, blast); + narc = numaClipToInterval(nar, 0, last); + nagc = numaClipToInterval(nag, 0, last); + nabc = numaClipToInterval(nab, 0, last); + snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d", index); + gplot = gplotCreate(buf, plottype, + "Pixel Difference Histogram", "diff val", + "number of pixels"); + gplotAddPlot(gplot, NULL, narc, GPLOT_LINES, "red"); + gplotAddPlot(gplot, NULL, nagc, GPLOT_LINES, "green"); + gplotAddPlot(gplot, NULL, nabc, GPLOT_LINES, "blue"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d.png", + index++); + l_fileDisplay(buf, 100, 100, 1.0); + numaDestroy(&nar); + numaDestroy(&nag); + numaDestroy(&nab); + numaDestroy(&narc); + numaDestroy(&nagc); + numaDestroy(&nabc); + } + + if (ppixdiff) + *ppixdiff = pixCreateRGBImage(pixr, pixg, pixb); + + if (prmsdiff) { + if (comptype == L_COMPARE_SUBTRACT) { + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + pixr = pixAbsDifference(pixr1, pixr2); + pixg = pixAbsDifference(pixg1, pixg2); + pixb = pixAbsDifference(pixb1, pixb2); + } + pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &rdiff); + pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &gdiff); + pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &bdiff); + *prmsdiff = (rdiff + gdiff + bdiff) / 3.0; + } + + pixDestroy(&pixr1); + pixDestroy(&pixr2); + pixDestroy(&pixg1); + pixDestroy(&pixg2); + pixDestroy(&pixb1); + pixDestroy(&pixb2); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return 0; +} + + +/*! + * \brief pixCompareTiled() + * + * \param[in] pix1 8 bpp or 32 bpp rgb + * \param[in] pix2 8 bpp 32 bpp rgb + * \param[in] sx, sy tile size; must be > 1 in each dimension + * \param[in] type L_MEAN_ABSVAL or L_ROOT_MEAN_SQUARE + * \param[out] ppixdiff pix of difference + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) With L_MEAN_ABSVAL, we compute for each tile the
+ *          average abs value of the pixel component difference between
+ *          the two (aligned) images.  With L_ROOT_MEAN_SQUARE, we
+ *          compute instead the rms difference over all components.
+ *      (2) The two input pix must be the same depth.  Comparison is made
+ *          using UL corner alignment.
+ *      (3) For 32 bpp, the distance between corresponding tiles
+ *          is found by averaging the measured difference over all three
+ *          components of each pixel in the tile.
+ *      (4) The result, pixdiff, contains one pixel for each source tile.
+ * 
+ */ +l_ok +pixCompareTiled(PIX *pix1, + PIX *pix2, + l_int32 sx, + l_int32 sy, + l_int32 type, + PIX **ppixdiff) +{ +l_int32 d1, d2, w, h; +PIX *pixt, *pixr, *pixg, *pixb; +PIX *pixrdiff, *pixgdiff, *pixbdiff; +PIXACC *pixacc; + + PROCNAME("pixCompareTiled"); + + if (!ppixdiff) + return ERROR_INT("&pixdiff not defined", procName, 1); + *ppixdiff = NULL; + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + d1 = pixGetDepth(pix1); + d2 = pixGetDepth(pix2); + if (d1 != d2) + return ERROR_INT("depths not equal", procName, 1); + if (d1 != 8 && d1 != 32) + return ERROR_INT("pix1 not 8 or 32 bpp", procName, 1); + if (d2 != 8 && d2 != 32) + return ERROR_INT("pix2 not 8 or 32 bpp", procName, 1); + if (sx < 2 || sy < 2) + return ERROR_INT("sx and sy not both > 1", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE) + return ERROR_INT("invalid type", procName, 1); + + pixt = pixAbsDifference(pix1, pix2); + if (d1 == 8) { + *ppixdiff = pixGetAverageTiled(pixt, sx, sy, type); + } else { /* d1 == 32 */ + pixr = pixGetRGBComponent(pixt, COLOR_RED); + pixg = pixGetRGBComponent(pixt, COLOR_GREEN); + pixb = pixGetRGBComponent(pixt, COLOR_BLUE); + pixrdiff = pixGetAverageTiled(pixr, sx, sy, type); + pixgdiff = pixGetAverageTiled(pixg, sx, sy, type); + pixbdiff = pixGetAverageTiled(pixb, sx, sy, type); + pixGetDimensions(pixrdiff, &w, &h, NULL); + pixacc = pixaccCreate(w, h, 0); + pixaccAdd(pixacc, pixrdiff); + pixaccAdd(pixacc, pixgdiff); + pixaccAdd(pixacc, pixbdiff); + pixaccMultConst(pixacc, 1. / 3.); + *ppixdiff = pixaccFinal(pixacc, 8); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + pixDestroy(&pixrdiff); + pixDestroy(&pixgdiff); + pixDestroy(&pixbdiff); + pixaccDestroy(&pixacc); + } + pixDestroy(&pixt); + return 0; +} + + +/*------------------------------------------------------------------* + * Other measures of the difference of two images * + *------------------------------------------------------------------*/ +/*! + * \brief pixCompareRankDifference() + * + * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; use 0 or 1 for no subsampling + * \return narank numa of rank difference, or NULL on error + * + *
+ * Notes:
+ *      (1) This answers the question: if the pixel values in each
+ *          component are compared by absolute difference, for
+ *          any value of difference, what is the fraction of
+ *          pixel pairs that have a difference of this magnitude
+ *          or greater.  For a difference of 0, the fraction is 1.0.
+ *          In this sense, it is a mapping from pixel difference to
+ *          rank order of difference.
+ *      (2) The two images are aligned at the UL corner, and do not
+ *          need to be the same size.  If they are not the same size,
+ *          the comparison will be made over overlapping pixels.
+ *      (3) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (4) If RGB, pixel differences for each component are aggregated
+ *          into a single histogram.
+ * 
+ */ +NUMA * +pixCompareRankDifference(PIX *pix1, + PIX *pix2, + l_int32 factor) +{ +l_int32 i; +l_float32 *array1, *array2; +NUMA *nah, *nan, *nad; + + PROCNAME("pixCompareRankDifference"); + + if (!pix1) + return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL); + if (!pix2) + return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL); + + if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + + nan = numaNormalizeHistogram(nah, 1.0); + array1 = numaGetFArray(nan, L_NOCOPY); + + nad = numaCreate(256); + numaSetCount(nad, 256); /* all initialized to 0.0 */ + array2 = numaGetFArray(nad, L_NOCOPY); + + /* Do rank accumulation on normalized histo of diffs */ + array2[0] = 1.0; + for (i = 1; i < 256; i++) + array2[i] = array2[i - 1] - array1[i - 1]; + + numaDestroy(&nah); + numaDestroy(&nan); + return nad; +} + + +/*! + * \brief pixTestForSimilarity() + * + * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; use 0 or 1 for no subsampling + * \param[in] mindiff minimum pixel difference to be counted; > 0 + * \param[in] maxfract maximum fraction of pixels allowed to have + * diff greater than or equal to mindiff + * \param[in] maxave maximum average difference of pixels allowed for + * pixels with diff greater than or equal to + * mindiff, after subtracting mindiff + * \param[out] psimilar 1 if similar, 0 otherwise + * \param[in] details use 1 to give normalized histogram and other data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes 2 pix that are the same size and determines using
+ *          3 input parameters if they are "similar".  The first parameter
+ *          %mindiff establishes a criterion of pixel-to-pixel similarity:
+ *          two pixels are not similar if their difference in value is
+ *          at least mindiff.  Then %maxfract and %maxave are thresholds
+ *          on the number and distribution of dissimilar pixels
+ *          allowed for the two pix to be similar.   If the pix are
+ *          to be similar, neither threshold can be exceeded.
+ *      (2) In setting the %maxfract and %maxave thresholds, you have
+ *          these options:
+ *            (a) Base the comparison only on %maxfract.  Then set
+ *                %maxave = 0.0 or 256.0.  (If 0, we always ignore it.)
+ *            (b) Base the comparison only on %maxave.  Then set
+ *                %maxfract = 1.0.
+ *            (c) Base the comparison on both thresholds.
+ *      (3) Example of values that can be expected at mindiff = 15 when
+ *          comparing lossless png encoding with jpeg encoding, q=75:
+ *             (smoothish bg)       fractdiff = 0.01, avediff = 2.5
+ *             (natural scene)      fractdiff = 0.13, avediff = 3.5
+ *          To identify these images as 'similar', select maxfract
+ *          and maxave to be upper bounds of what you expect.
+ *      (4) See pixGetDifferenceStats() for a discussion of why we subtract
+ *          mindiff from the computed average diff of the nonsimilar pixels
+ *          to get the 'avediff' returned by that function.
+ *      (5) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (6) If RGB, the maximum difference between pixel components is
+ *          saved in the histogram.
+ * 
+ */ +l_ok +pixTestForSimilarity(PIX *pix1, + PIX *pix2, + l_int32 factor, + l_int32 mindiff, + l_float32 maxfract, + l_float32 maxave, + l_int32 *psimilar, + l_int32 details) +{ +l_float32 fractdiff, avediff; + + PROCNAME("pixTestForSimilarity"); + + if (!psimilar) + return ERROR_INT("&similar not defined", procName, 1); + *psimilar = 0; + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + if (pixSizesEqual(pix1, pix2) == 0) + return ERROR_INT("pix sizes not equal", procName, 1); + if (mindiff <= 0) + return ERROR_INT("mindiff must be > 0", procName, 1); + + if (pixGetDifferenceStats(pix1, pix2, factor, mindiff, + &fractdiff, &avediff, details)) + return ERROR_INT("diff stats not found", procName, 1); + + if (maxave <= 0.0) maxave = 256.0; + if (fractdiff <= maxfract && avediff <= maxave) + *psimilar = 1; + return 0; +} + + +/*! + * \brief pixGetDifferenceStats() + * + * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; use 0 or 1 for no subsampling + * \param[in] mindiff minimum pixel difference to be counted; > 0 + * \param[out] pfractdiff fraction of pixels with diff greater than or + * equal to mindiff + * \param[out] pavediff average difference of pixels with diff greater + * than or equal to mindiff, less mindiff + * \param[in] details use 1 to give normalized histogram and other data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes a threshold %mindiff and describes the difference
+ *          between two images in terms of two numbers:
+ *            (a) the fraction of pixels, %fractdiff, whose difference
+ *                equals or exceeds the threshold %mindiff, and
+ *            (b) the average value %avediff of the difference in pixel value
+ *                for the pixels in the set given by (a), after you subtract
+ *                %mindiff.  The reason for subtracting %mindiff is that
+ *                you then get a useful measure for the rate of falloff
+ *                of the distribution for larger differences.  For example,
+ *                if %mindiff = 10 and you find that %avediff = 2.5, it
+ *                says that of the pixels with diff > 10, the average of
+ *                their diffs is just mindiff + 2.5 = 12.5.  This is a
+ *                fast falloff in the histogram with increasing difference.
+ *      (2) The two images are aligned at the UL corner, and do not
+ *          need to be the same size.  If they are not the same size,
+ *          the comparison will be made over overlapping pixels.
+ *      (3) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (4) If RGB, the maximum difference between pixel components is
+ *          saved in the histogram.
+ *      (5) Set %details == 1 to see the difference histogram and get
+ *          an output that shows for each value of %mindiff, what are the
+ *          minimum values required for fractdiff and avediff in order
+ *          that the two pix will be considered similar.
+ * 
+ */ +l_ok +pixGetDifferenceStats(PIX *pix1, + PIX *pix2, + l_int32 factor, + l_int32 mindiff, + l_float32 *pfractdiff, + l_float32 *pavediff, + l_int32 details) +{ +l_int32 i, first, last, diff; +l_float32 fract, ave; +l_float32 *array; +NUMA *nah, *nan, *nac; + + PROCNAME("pixGetDifferenceStats"); + + if (pfractdiff) *pfractdiff = 0.0; + if (pavediff) *pavediff = 0.0; + if (!pfractdiff) + return ERROR_INT("&fractdiff not defined", procName, 1); + if (!pavediff) + return ERROR_INT("&avediff not defined", procName, 1); + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + if (mindiff <= 0) + return ERROR_INT("mindiff must be > 0", procName, 1); + + if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL) + return ERROR_INT("na not made", procName, 1); + + if ((nan = numaNormalizeHistogram(nah, 1.0)) == NULL) { + numaDestroy(&nah); + return ERROR_INT("nan not made", procName, 1); + } + array = numaGetFArray(nan, L_NOCOPY); + + if (details) { + lept_mkdir("lept/comp"); + numaGetNonzeroRange(nan, 0.0, &first, &last); + nac = numaClipToInterval(nan, first, last); + gplotSimple1(nac, GPLOT_PNG, "/tmp/lept/comp/histo", + "Difference histogram"); + l_fileDisplay("/tmp/lept/comp/histo.png", 500, 0, 1.0); + fprintf(stderr, "\nNonzero values in normalized histogram:"); + numaWriteStream(stderr, nac); + numaDestroy(&nac); + fprintf(stderr, " Mindiff fractdiff avediff\n"); + fprintf(stderr, " -----------------------------------\n"); + for (diff = 1; diff < L_MIN(2 * mindiff, last); diff++) { + fract = 0.0; + ave = 0.0; + for (i = diff; i <= last; i++) { + fract += array[i]; + ave += (l_float32)i * array[i]; + } + ave = (fract == 0.0) ? 0.0 : ave / fract; + ave -= diff; + fprintf(stderr, "%5d %7.4f %7.4f\n", + diff, fract, ave); + } + fprintf(stderr, " -----------------------------------\n"); + } + + fract = 0.0; + ave = 0.0; + for (i = mindiff; i < 256; i++) { + fract += array[i]; + ave += (l_float32)i * array[i]; + } + ave = (fract == 0.0) ? 0.0 : ave / fract; + ave -= mindiff; + + *pfractdiff = fract; + *pavediff = ave; + + numaDestroy(&nah); + numaDestroy(&nan); + return 0; +} + + +/*! + * \brief pixGetDifferenceHistogram() + * + * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; use 0 or 1 for no subsampling + * \return na Numa of histogram of differences, or NULL on error + * + *
+ * Notes:
+ *      (1) The two images are aligned at the UL corner, and do not
+ *          need to be the same size.  If they are not the same size,
+ *          the comparison will be made over overlapping pixels.
+ *      (2) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (3) If RGB, the maximum difference between pixel components is
+ *          saved in the histogram.
+ * 
+ */ +NUMA * +pixGetDifferenceHistogram(PIX *pix1, + PIX *pix2, + l_int32 factor) +{ +l_int32 w1, h1, d1, w2, h2, d2, w, h, wpl1, wpl2; +l_int32 i, j, val, val1, val2; +l_int32 rval1, rval2, gval1, gval2, bval1, bval2; +l_int32 rdiff, gdiff, bdiff, maxdiff; +l_uint32 *data1, *data2, *line1, *line2; +l_float32 *array; +NUMA *na; +PIX *pixt1, *pixt2; + + PROCNAME("pixGetDifferenceHistogram"); + + if (!pix1) + return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL); + if (!pix2) + return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL); + d1 = pixGetDepth(pix1); + d2 = pixGetDepth(pix2); + if (d1 == 16 || d2 == 16) + return (NUMA *)ERROR_PTR("d == 16 not supported", procName, NULL); + if (d1 < 8 && !pixGetColormap(pix1)) + return (NUMA *)ERROR_PTR("pix1 depth < 8 bpp and not cmapped", + procName, NULL); + if (d2 < 8 && !pixGetColormap(pix2)) + return (NUMA *)ERROR_PTR("pix2 depth < 8 bpp and not cmapped", + procName, NULL); + pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); + pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); + pixGetDimensions(pixt1, &w1, &h1, &d1); + pixGetDimensions(pixt2, &w2, &h2, &d2); + if (d1 != d2) { + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return (NUMA *)ERROR_PTR("pix depths not equal", procName, NULL); + } + if (factor < 1) factor = 1; + + na = numaCreate(256); + numaSetCount(na, 256); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + w = L_MIN(w1, w2); + h = L_MIN(h1, h2); + data1 = pixGetData(pixt1); + data2 = pixGetData(pixt2); + wpl1 = pixGetWpl(pixt1); + wpl2 = pixGetWpl(pixt2); + if (d1 == 8) { + for (i = 0; i < h; i += factor) { + line1 = data1 + i * wpl1; + line2 = data2 + i * wpl2; + for (j = 0; j < w; j += factor) { + val1 = GET_DATA_BYTE(line1, j); + val2 = GET_DATA_BYTE(line2, j); + val = L_ABS(val1 - val2); + array[val]++; + } + } + } else { /* d1 == 32 */ + for (i = 0; i < h; i += factor) { + line1 = data1 + i * wpl1; + line2 = data2 + i * wpl2; + for (j = 0; j < w; j += factor) { + extractRGBValues(line1[j], &rval1, &gval1, &bval1); + extractRGBValues(line2[j], &rval2, &gval2, &bval2); + rdiff = L_ABS(rval1 - rval2); + gdiff = L_ABS(gval1 - gval2); + bdiff = L_ABS(bval1 - bval2); + maxdiff = L_MAX(rdiff, gdiff); + maxdiff = L_MAX(maxdiff, bdiff); + array[maxdiff]++; + } + } + } + + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return na; +} + + +/*! + * \brief pixGetPerceptualDiff() + * + * \param[in] pixs1 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] pixs2 8 bpp gray or 32 bpp rgb, or colormapped + * \param[in] sampling subsampling factor; use 0 or 1 for no subsampling + * \param[in] dilation size of grayscale or color Sel; odd + * \param[in] mindiff minimum pixel difference to be counted; > 0 + * \param[out] pfract fraction of pixels with diff greater than mindiff + * \param[out] ppixdiff1 [optional] showing difference (gray or color) + * \param[out] ppixdiff2 [optional] showing pixels of sufficient diff + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes 2 pix and determines, using 2 input parameters:
+ *           * %dilation specifies the amount of grayscale or color
+ *             dilation to apply to the images, to compensate for
+ *             a small amount of misregistration.  A typical number might
+ *             be 5, which uses a 5x5 Sel.  Grayscale dilation expands
+ *             lighter pixels into darker pixel regions.
+ *           * %mindiff determines the threshold on the difference in
+ *             pixel values to be counted -- two pixels are not similar
+ *             if their difference in value is at least %mindiff.  For
+ *             color pixels, we use the maximum component difference.
+ *      (2) The pixelwise comparison is always done with the UL corners
+ *          aligned.  The sizes of pix1 and pix2 need not be the same,
+ *          although in practice it can be useful to scale to the same size.
+ *      (3) If there is a colormap, it is removed and the result
+ *          is either gray or RGB depending on the colormap.
+ *      (4) Two optional diff images can be retrieved (typ. for debugging):
+ *           pixdiff1: the gray or color difference
+ *           pixdiff2: thresholded to 1 bpp for pixels exceeding %mindiff
+ *      (5) The returned value of fract can be compared to some threshold,
+ *          which is application dependent.
+ *      (6) This method is in analogy to the two-sided hausdorff transform,
+ *          except here it is for d > 1.  For d == 1 (see pixRankHaustest()),
+ *          we verify that when one pix1 is dilated, it covers at least a
+ *          given fraction of the pixels in pix2, and v.v.; in that
+ *          case, the two pix are sufficiently similar.  Here, we
+ *          do an analogous thing: subtract the dilated pix1 from pix2 to
+ *          get a 1-sided hausdorff-like transform.  Then do it the
+ *          other way.  Take the component-wise max of the two results,
+ *          and threshold to get the fraction of pixels with a difference
+ *          below the threshold.
+ * 
+ */ +l_ok +pixGetPerceptualDiff(PIX *pixs1, + PIX *pixs2, + l_int32 sampling, + l_int32 dilation, + l_int32 mindiff, + l_float32 *pfract, + PIX **ppixdiff1, + PIX **ppixdiff2) +{ +l_int32 d1, d2, w, h, count; +PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; +PIX *pix10, *pix11; + + PROCNAME("pixGetPerceptualDiff"); + + if (ppixdiff1) *ppixdiff1 = NULL; + if (ppixdiff2) *ppixdiff2 = NULL; + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 1.0; /* init to completely different */ + if ((dilation & 1) == 0) + return ERROR_INT("dilation must be odd", procName, 1); + if (!pixs1) + return ERROR_INT("pixs1 not defined", procName, 1); + if (!pixs2) + return ERROR_INT("pixs2 not defined", procName, 1); + d1 = pixGetDepth(pixs1); + d2 = pixGetDepth(pixs2); + if (!pixGetColormap(pixs1) && d1 < 8) + return ERROR_INT("pixs1 not cmapped or >=8 bpp", procName, 1); + if (!pixGetColormap(pixs2) && d2 < 8) + return ERROR_INT("pixs2 not cmapped or >=8 bpp", procName, 1); + + /* Integer downsample if requested */ + if (sampling > 1) { + pix1 = pixScaleByIntSampling(pixs1, sampling); + pix2 = pixScaleByIntSampling(pixs2, sampling); + } else { + pix1 = pixClone(pixs1); + pix2 = pixClone(pixs2); + } + + /* Remove colormaps */ + if (pixGetColormap(pix1)) { + pix3 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); + d1 = pixGetDepth(pix3); + } else { + pix3 = pixClone(pix1); + } + if (pixGetColormap(pix2)) { + pix4 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); + d2 = pixGetDepth(pix4); + } else { + pix4 = pixClone(pix2); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + if (d1 != d2) { + pixDestroy(&pix3); + pixDestroy(&pix4); + return ERROR_INT("pix3 and pix4 depths not equal", procName, 1); + } + + /* In each direction, do a small dilation and subtract the dilated + * image from the other image to get a one-sided difference. + * Then take the max of the differences for each direction + * and clipping each component to 255 if necessary. Note that + * for RGB images, the dilations and max selection are done + * component-wise, and the conversion to grayscale also uses the + * maximum component. The resulting grayscale images are + * thresholded using %mindiff. */ + if (d1 == 8) { + pix5 = pixDilateGray(pix3, dilation, dilation); + pixCompareGray(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, + &pix7); + pix6 = pixDilateGray(pix4, dilation, dilation); + pixCompareGray(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, + &pix8); + pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX); + pix10 = pixThresholdToBinary(pix9, mindiff); + pixInvert(pix10, pix10); + pixCountPixels(pix10, &count, NULL); + pixGetDimensions(pix10, &w, &h, NULL); + *pfract = (l_float32)count / (l_float32)(w * h); + pixDestroy(&pix5); + pixDestroy(&pix6); + pixDestroy(&pix7); + pixDestroy(&pix8); + if (ppixdiff1) + *ppixdiff1 = pix9; + else + pixDestroy(&pix9); + if (ppixdiff2) + *ppixdiff2 = pix10; + else + pixDestroy(&pix10); + } else { /* d1 == 32 */ + pix5 = pixColorMorph(pix3, L_MORPH_DILATE, dilation, dilation); + pixCompareRGB(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, + &pix7); + pix6 = pixColorMorph(pix4, L_MORPH_DILATE, dilation, dilation); + pixCompareRGB(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, + &pix8); + pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX); + pix10 = pixConvertRGBToGrayMinMax(pix9, L_CHOOSE_MAX); + pix11 = pixThresholdToBinary(pix10, mindiff); + pixInvert(pix11, pix11); + pixCountPixels(pix11, &count, NULL); + pixGetDimensions(pix11, &w, &h, NULL); + *pfract = (l_float32)count / (l_float32)(w * h); + pixDestroy(&pix5); + pixDestroy(&pix6); + pixDestroy(&pix7); + pixDestroy(&pix8); + pixDestroy(&pix10); + if (ppixdiff1) + *ppixdiff1 = pix9; + else + pixDestroy(&pix9); + if (ppixdiff2) + *ppixdiff2 = pix11; + else + pixDestroy(&pix11); + + } + pixDestroy(&pix3); + pixDestroy(&pix4); + return 0; +} + + +/*! + * \brief pixGetPSNR() + * + * \param[in] pix1, pix2 8 or 32 bpp; no colormap + * \param[in] factor sampling factor; >= 1 + * \param[out] ppsnr power signal/noise ratio difference + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes the power S/N ratio, in dB, for the difference
+ *          between two images.  By convention, the power S/N
+ *          for a grayscale image is ('log' == log base 10,
+ *          and 'ln == log base e):
+ *            PSNR = 10 * log((255/MSE)^2)
+ *                 = 4.3429 * ln((255/MSE)^2)
+ *                 = -4.3429 * ln((MSE/255)^2)
+ *          where MSE is the mean squared error.
+ *          Here are some examples:
+ *             MSE             PSNR
+ *             ---             ----
+ *             10              28.1
+ *             3               38.6
+ *             1               48.1
+ *             0.1             68.1
+ *      (2) If pix1 and pix2 have the same pixel values, the MSE = 0.0
+ *          and the PSNR is infinity.  For that case, this returns
+ *          PSNR = 1000, which corresponds to the very small MSE of
+ *          about 10^(-48).
+ * 
+ */ +l_ok +pixGetPSNR(PIX *pix1, + PIX *pix2, + l_int32 factor, + l_float32 *ppsnr) +{ +l_int32 same, i, j, w, h, d, wpl1, wpl2, v1, v2, r1, g1, b1, r2, g2, b2; +l_uint32 *data1, *data2, *line1, *line2; +l_float32 mse; /* mean squared error */ + + PROCNAME("pixGetPSNR"); + + if (!ppsnr) + return ERROR_INT("&psnr not defined", procName, 1); + *ppsnr = 0.0; + if (!pix1 || !pix2) + return ERROR_INT("empty input pix", procName, 1); + if (!pixSizesEqual(pix1, pix2)) + return ERROR_INT("pix sizes unequal", procName, 1); + if (pixGetColormap(pix1)) + return ERROR_INT("pix1 has colormap", procName, 1); + if (pixGetColormap(pix2)) + return ERROR_INT("pix2 has colormap", procName, 1); + pixGetDimensions(pix1, &w, &h, &d); + if (d != 8 && d != 32) + return ERROR_INT("pix not 8 or 32 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("invalid sampling factor", procName, 1); + + pixEqual(pix1, pix2, &same); + if (same) { + *ppsnr = 1000.0; /* crazy big exponent */ + return 0; + } + + data1 = pixGetData(pix1); + data2 = pixGetData(pix2); + wpl1 = pixGetWpl(pix1); + wpl2 = pixGetWpl(pix2); + mse = 0.0; + if (d == 8) { + for (i = 0; i < h; i += factor) { + line1 = data1 + i * wpl1; + line2 = data2 + i * wpl2; + for (j = 0; j < w; j += factor) { + v1 = GET_DATA_BYTE(line1, j); + v2 = GET_DATA_BYTE(line2, j); + mse += (l_float32)(v1 - v2) * (v1 - v2); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i += factor) { + line1 = data1 + i * wpl1; + line2 = data2 + i * wpl2; + for (j = 0; j < w; j += factor) { + extractRGBValues(line1[j], &r1, &g1, &b1); + extractRGBValues(line2[j], &r2, &g2, &b2); + mse += ((l_float32)(r1 - r2) * (r1 - r2) + + (g1 - g2) * (g1 - g2) + + (b1 - b2) * (b1 - b2)) / 3.0; + } + } + } + mse = mse / ((l_float32)(w) * h); + + *ppsnr = -4.3429448 * log(mse / (255 * 255)); + return 0; +} + + +/*------------------------------------------------------------------* + * Comparison of photo regions by histogram * + *------------------------------------------------------------------*/ +/*! + * \brief pixaComparePhotoRegionsByHisto() + * + * \param[in] pixa any depth; colormap OK + * \param[in] minratio requiring sizes be compatible; < 1.0 + * \param[in] textthresh threshold for text/photo; use 0 for default + * \param[in] factor subsampling; >= 1 + * \param[in] n in range {1, ... 7}. n^2 is the maximum number + * of subregions for histograms; typ. n = 3. + * \param[in] simthresh threshold for similarity; use 0 for default + * \param[out] pnai array giving similarity class indices + * \param[out] pscores [optional] score matrix as 1-D array of size N^2 + * \param[out] ppixd [optional] pix of similarity classes + * \param[in] debug 1 to output histograms; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function takes a pixa of cropped photo images and
+ *          compares each one to the others for similarity.
+ *          Each image is first tested to see if it is a photo that can
+ *          be compared by tiled histograms.  If so, it is padded to put
+ *          the centroid in the center of the image, and the histograms
+ *          are generated.  The final step of comparing each histogram
+ *          with all the others is very fast.
+ *      (2) To make the histograms, each image is subdivided in a maximum
+ *          of n^2 subimages.  The parameter %n specifies the "side" of
+ *          an n x n grid of such subimages.  If the subimages have an
+ *          aspect ratio larger than 2, the grid will change, again using n^2
+ *          as a maximum for the number of subimages.  For example,
+ *          if n == 3, but the image is 600 x 200 pixels, a 3x3 grid
+ *          would have subimages of 200 x 67 pixels, which is more
+ *          than 2:1, so we change to a 4x2 grid where each subimage
+ *          has 150 x 100 pixels.
+ *      (3) An initial filter gives %score = 0 if the ratio of widths
+ *          and heights (smallest / largest) does not exceed a
+ *          threshold %minratio.  If set at 1.0, both images must be
+ *          exactly the same size.  A typical value for %minratio is 0.9.
+ *      (4) The comparison score between two images is a value in [0.0 .. 1.0].
+ *          If the comparison score >= %simthresh, the images are placed in
+ *          the same similarity class.  Default value for %simthresh is 0.25.
+ *      (5) An array %nai of similarity class indices for pix in the
+ *          input pixa is returned.
+ *      (6) There are two debugging options:
+ *          * An optional 2D matrix of scores is returned as a 1D array.
+ *            A visualization of this is written to a temp file.
+ *          * An optional pix showing the similarity classes can be
+ *            returned.  Text in each input pix is reproduced.
+ *      (7) See the notes in pixComparePhotoRegionsByHisto() for details
+ *          on the implementation.
+ * 
+ */ +l_ok +pixaComparePhotoRegionsByHisto(PIXA *pixa, + l_float32 minratio, + l_float32 textthresh, + l_int32 factor, + l_int32 n, + l_float32 simthresh, + NUMA **pnai, + l_float32 **pscores, + PIX **ppixd, + l_int32 debug) +{ +char *text; +l_int32 i, j, nim, w, h, w1, h1, w2, h2, ival, index, classid; +l_float32 score; +l_float32 *scores; +NUMA *nai, *naw, *nah; +NUMAA *naa; +NUMAA **n3a; /* array of naa */ +PIX *pix; + + PROCNAME("pixaComparePhotoRegionsByHisto"); + + if (pscores) *pscores = NULL; + if (ppixd) *ppixd = NULL; + if (!pnai) + return ERROR_INT("&na not defined", procName, 1); + *pnai = NULL; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (minratio < 0.0 || minratio > 1.0) + return ERROR_INT("minratio not in [0.0 ... 1.0]", procName, 1); + if (textthresh <= 0.0) textthresh = 1.3; + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (n < 1 || n > 7) { + L_WARNING("n = %d is invalid; setting to 4\n", procName, n); + n = 4; + } + if (simthresh <= 0.0) simthresh = 0.25; + if (simthresh > 1.0) + return ERROR_INT("simthresh invalid; should be near 0.25", procName, 1); + + /* Prepare the histograms */ + nim = pixaGetCount(pixa); + if ((n3a = (NUMAA **)LEPT_CALLOC(nim, sizeof(NUMAA *))) == NULL) + return ERROR_INT("calloc fail for n3a", procName, 1); + naw = numaCreate(0); + nah = numaCreate(0); + for (i = 0; i < nim; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + text = pixGetText(pix); + pixSetResolution(pix, 150, 150); + index = (debug) ? i : 0; + pixGenPhotoHistos(pix, NULL, factor, textthresh, n, + &naa, &w, &h, index); + n3a[i] = naa; + numaAddNumber(naw, w); + numaAddNumber(nah, h); + if (naa) + fprintf(stderr, "Image %s is photo\n", text); + else + fprintf(stderr, "Image %s is NOT photo\n", text); + pixDestroy(&pix); + } + + /* Do the comparisons. We are making a set of classes, where + * all similar images are placed in the same class. There are + * 'nim' input images. The classes are labeled by 'classid' (all + * similar images get the same 'classid' value), and 'nai' maps + * the classid of the image in the input array to the classid + * of the similarity class. */ + if ((scores = + (l_float32 *)LEPT_CALLOC((size_t)nim * nim, sizeof(l_float32))) + == NULL) { + L_ERROR("calloc fail for scores\n", procName); + goto cleanup; + } + nai = numaMakeConstant(-1, nim); /* classid array */ + for (i = 0, classid = 0; i < nim; i++) { + scores[nim * i + i] = 1.0; + numaGetIValue(nai, i, &ival); + if (ival != -1) /* already set */ + continue; + numaSetValue(nai, i, classid); + if (n3a[i] == NULL) { /* not a photo */ + classid++; + continue; + } + numaGetIValue(naw, i, &w1); + numaGetIValue(nah, i, &h1); + for (j = i + 1; j < nim; j++) { + numaGetIValue(nai, j, &ival); + if (ival != -1) /* already set */ + continue; + if (n3a[j] == NULL) /* not a photo */ + continue; + numaGetIValue(naw, j, &w2); + numaGetIValue(nah, j, &h2); + compareTilesByHisto(n3a[i], n3a[j], minratio, w1, h1, w2, h2, + &score, NULL); + scores[nim * i + j] = score; + scores[nim * j + i] = score; /* the score array is symmetric */ +/* fprintf(stderr, "score = %5.3f\n", score); */ + if (score > simthresh) { + numaSetValue(nai, j, classid); + fprintf(stderr, + "Setting %d similar to %d, in class %d; score %5.3f\n", + j, i, classid, score); + } + } + classid++; + } + *pnai = nai; + + /* Debug: optionally save and display the score array. + * All images that are photos are represented by a point on + * the diagonal. Other images in the same similarity class + * are on the same horizontal raster line to the right. + * The array has been symmetrized, so images in the same + * same similarity class also appear on the same column below. */ + if (pscores) { + l_int32 wpl, fact; + l_uint32 *line, *data; + PIX *pix2, *pix3; + pix2 = pixCreate(nim, nim, 8); + data = pixGetData(pix2); + wpl = pixGetWpl(pix2); + for (i = 0; i < nim; i++) { + line = data + i * wpl; + for (j = 0; j < nim; j++) { + SET_DATA_BYTE(line, j, + L_MIN(255, 4.0 * 255 * scores[nim * i + j])); + } + } + fact = L_MAX(2, 1000 / nim); + pix3 = pixExpandReplicate(pix2, fact); + fprintf(stderr, "Writing to /tmp/lept/comp/scorearray.png\n"); + lept_mkdir("lept/comp"); + pixWrite("/tmp/lept/comp/scorearray.png", pix3, IFF_PNG); + pixDestroy(&pix2); + pixDestroy(&pix3); + *pscores = scores; + } else { + LEPT_FREE(scores); + } + + /* Debug: optionally display and save the image comparisons. + * Image similarity classes are displayed by column; similar + * images are displayed in the same column. */ + if (ppixd) + *ppixd = pixaDisplayTiledByIndex(pixa, nai, 200, 20, 2, 6, 0x0000ff00); + +cleanup: + numaDestroy(&naw); + numaDestroy(&nah); + for (i = 0; i < nim; i++) + numaaDestroy(&n3a[i]); + LEPT_FREE(n3a); + return 0; +} + + +/*! + * \brief pixComparePhotoRegionsByHisto() + * + * \param[in] pix1, pix2 any depth; colormap OK + * \param[in] box1, box2 [optional] photo regions from each; can be null + * \param[in] minratio requiring sizes be compatible; < 1.0 + * \param[in] factor subsampling factor; >= 1 + * \param[in] n in range {1, ... 7}. n^2 is the maximum number + * of subregions for histograms; typ. n = 3. + * \param[out] pscore similarity score of histograms + * \param[in] debugflag 1 for debug output; 0 for no debugging + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function compares two grayscale photo regions.  If a
+ *          box is given, the region is clipped; otherwise assume
+ *          the entire images are photo regions.  This is done with a
+ *          set of not more than n^2 spatially aligned histograms, which are
+ *          aligned using the centroid of the inverse image.
+ *      (2) The parameter %n specifies the "side" of an n x n grid
+ *          of subimages.  If the subimages have an aspect ratio larger
+ *          than 2, the grid will change, using n^2 as a maximum for
+ *          the number of subimages.  For example, if n == 3, but the
+ *          image is 600 x 200 pixels, a 3x3 grid would have subimages
+ *          of 200 x 67 pixels, which is more than 2:1, so we change
+ *          to a 4x2 grid where each subimage has 150 x 100 pixels.
+ *      (3) An initial filter gives %score = 0 if the ratio of widths
+ *          and heights (smallest / largest) does not exceed a
+ *          threshold %minratio.  This must be between 0.5 and 1.0.
+ *          If set at 1.0, both images must be exactly the same size.
+ *          A typical value for %minratio is 0.9.
+ *      (4) Because this function should not be used on text or
+ *          line graphics, which can give false positive results
+ *          (i.e., high scores for different images), filter the images
+ *          using pixGenPhotoHistos(), which returns tiled histograms
+ *          only if an image is not text and comparison is expected
+ *          to work with histograms.  If either image fails the test,
+ *          the comparison returns a score of 0.0.
+ *      (5) The white value counts in the histograms are removed; they
+ *          are typically pixels that were padded to achieve alignment.
+ *      (6) For an efficient representation of the histogram, normalize
+ *          using a multiplicative factor so that the number in the
+ *          maximum bucket is 255.  It then takes 256 bytes to store.
+ *      (7) When comparing the histograms of two regions, use the
+ *          Earth Mover distance (EMD), with the histograms normalized
+ *          so that the sum over bins is the same.  Further normalize
+ *          by dividing by 255, so that the result is in [0.0 ... 1.0].
+ *      (8) Get a similarity score S = 1.0 - k * D, where
+ *            k is a constant, say in the range 5-10
+ *            D = normalized EMD
+ *          and for multiple tiles, take the Min(S) to be the final score.
+ *          Using aligned tiles gives protection against accidental
+ *          similarity of the overall grayscale histograms.
+ *          A small number of aligned tiles works well.
+ *      (9) With debug on, you get a pdf that shows, for each tile,
+ *          the images, histograms and score.
+ * 
+ */ +l_ok +pixComparePhotoRegionsByHisto(PIX *pix1, + PIX *pix2, + BOX *box1, + BOX *box2, + l_float32 minratio, + l_int32 factor, + l_int32 n, + l_float32 *pscore, + l_int32 debugflag) +{ +l_int32 w1, h1, w2, h2, w1c, h1c, w2c, h2c, debugindex; +l_float32 wratio, hratio; +NUMAA *naa1, *naa2; +PIX *pix3, *pix4; +PIXA *pixa; + + PROCNAME("pixComparePhotoRegionsByHisto"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + if (minratio < 0.5 || minratio > 1.0) + return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1); + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (n < 1 || n > 7) { + L_WARNING("n = %d is invalid; setting to 4\n", procName, n); + n = 4; + } + + debugindex = 0; + if (debugflag) { + lept_mkdir("lept/comp"); + debugindex = 666; /* arbitrary number used for naming output */ + } + + /* Initial filter by size */ + if (box1) + boxGetGeometry(box1, NULL, NULL, &w1, &h1); + else + pixGetDimensions(pix1, &w1, &h1, NULL); + if (box2) + boxGetGeometry(box2, NULL, NULL, &w2, &h2); + else + pixGetDimensions(pix1, &w2, &h2, NULL); + wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : + (l_float32)w2 / (l_float32)w1; + hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : + (l_float32)h2 / (l_float32)h1; + if (wratio < minratio || hratio < minratio) + return 0; + + /* Initial crop, if necessary, and make histos */ + if (box1) + pix3 = pixClipRectangle(pix1, box1, NULL); + else + pix3 = pixClone(pix1); + pixGenPhotoHistos(pix3, NULL, factor, 0, n, &naa1, &w1c, &h1c, debugindex); + pixDestroy(&pix3); + if (!naa1) return 0; + if (box2) + pix4 = pixClipRectangle(pix2, box2, NULL); + else + pix4 = pixClone(pix2); + pixGenPhotoHistos(pix4, NULL, factor, 0, n, &naa2, &w2c, &h2c, debugindex); + pixDestroy(&pix4); + if (!naa2) return 0; + + /* Compare histograms */ + pixa = (debugflag) ? pixaCreate(0) : NULL; + compareTilesByHisto(naa1, naa2, minratio, w1c, h1c, w2c, h2c, pscore, pixa); + pixaDestroy(&pixa); + return 0; +} + + +/*! + * \brief pixGenPhotoHistos() + * + * \param[in] pixs depth > 1 bpp; colormap OK + * \param[in] box [optional] region to be selected; can be null + * \param[in] factor subsampling; >= 1 + * \param[in] thresh threshold for photo/text; use 0 for default + * \param[in] n in range {1, ... 7}. n^2 is the maximum number + * of subregions for histograms; typ. n = 3. + * \param[out] pnaa nx * ny 256-entry gray histograms + * \param[out] pw width of image used to make histograms + * \param[out] ph height of image used to make histograms + * \param[in] debugindex 0 for no debugging; positive integer otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This crops and converts to 8 bpp if necessary.  It adds a
+ *          minimal white boundary such that the centroid of the
+ *          photo-inverted image is in the center. This allows
+ *          automatic alignment with histograms of other image regions.
+ *      (2) The parameter %n specifies the "side" of the n x n grid
+ *          of subimages.  If the subimages have an aspect ratio larger
+ *          than 2, the grid will change, using n^2 as a maximum for
+ *          the number of subimages.  For example, if n == 3, but the
+ *          image is 600 x 200 pixels, a 3x3 grid would have subimages
+ *          of 200 x 67 pixels, which is more than 2:1, so we change
+ *          to a 4x2 grid where each subimage has 150 x 100 pixels.
+ *      (3) The white value in the histogram is removed, because of
+ *          the padding.
+ *      (4) Use 0 for conservative default (1.3) for thresh.
+ *      (5) For an efficient representation of the histogram, normalize
+ *          using a multiplicative factor so that the number in the
+ *          maximum bucket is 255.  It then takes 256 bytes to store.
+ *      (6) With %debugindex > 0, this makes a pdf that shows, for each tile,
+ *          the images and histograms.
+ * 
+ */ +l_ok +pixGenPhotoHistos(PIX *pixs, + BOX *box, + l_int32 factor, + l_float32 thresh, + l_int32 n, + NUMAA **pnaa, + l_int32 *pw, + l_int32 *ph, + l_int32 debugindex) +{ +char buf[64]; +NUMAA *naa; +PIX *pix1, *pix2, *pix3, *pixm; +PIXA *pixa; + + PROCNAME("pixGenPhotoHistos"); + + if (pnaa) *pnaa = NULL; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!pnaa) + return ERROR_INT("&naa not defined", procName, 1); + if (!pw || !ph) + return ERROR_INT("&w and &h not both defined", procName, 1); + if (!pixs || pixGetDepth(pixs) == 1) + return ERROR_INT("pixs not defined or 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (thresh <= 0.0) thresh = 1.3; /* default */ + if (n < 1 || n > 7) { + L_WARNING("n = %d is invalid; setting to 4\n", procName, n); + n = 4; + } + + pixa = NULL; + if (debugindex > 0) { + pixa = pixaCreate(0); + lept_mkdir("lept/comp"); + } + + /* Initial crop, if necessary */ + if (box) + pix1 = pixClipRectangle(pixs, box, NULL); + else + pix1 = pixClone(pixs); + + /* Convert to 8 bpp and pad to center the centroid */ + pix2 = pixConvertTo8(pix1, FALSE); + pix3 = pixPadToCenterCentroid(pix2, factor); + + /* Set to 255 all pixels above 230. Do this so that light gray + * pixels do not enter into the comparison. */ + pixm = pixThresholdToBinary(pix3, 230); + pixInvert(pixm, pixm); + pixSetMaskedGeneral(pix3, pixm, 255, 0, 0); + pixDestroy(&pixm); + + if (debugindex > 0) { + PIX *pix4, *pix5, *pix6, *pix7, *pix8; + PIXA *pixa2; + pix4 = pixConvertTo32(pix2); + pix5 = pixConvertTo32(pix3); + pix6 = pixScaleToSize(pix4, 400, 0); + pix7 = pixScaleToSize(pix5, 400, 0); + pixa2 = pixaCreate(2); + pixaAddPix(pixa2, pix6, L_INSERT); + pixaAddPix(pixa2, pix7, L_INSERT); + pix8 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 3); + pixaAddPix(pixa, pix8, L_INSERT); + pixDestroy(&pix4); + pixDestroy(&pix5); + pixaDestroy(&pixa2); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* Test if this is a photoimage */ + pixDecideIfPhotoImage(pix3, factor, thresh, n, &naa, pixa); + if (naa) { + *pnaa = naa; + *pw = pixGetWidth(pix3); + *ph = pixGetHeight(pix3); + } + + if (pixa) { + snprintf(buf, sizeof(buf), "/tmp/lept/comp/tiledhistos.%d.pdf", + debugindex); + fprintf(stderr, "Writing to %s\n", buf); + pixaConvertToPdf(pixa, 300, 1.0, L_FLATE_ENCODE, 0, NULL, buf); + pixaDestroy(&pixa); + } + + pixDestroy(&pix3); + return 0; +} + + +/*! + * \brief pixPadToCenterCentroid() + * + * \param[in] pixs any depth, colormap OK + * \param[in] factor subsampling for centroid; >= 1 + * \return pixd padded with white pixels, or NULL on error. + * + *
+ * Notes:
+ *      (1) This add minimum white padding to an 8 bpp pix, such that
+ *          the centroid of the photometric inverse is in the center of
+ *          the resulting image.  Thus in computing the centroid,
+ *          black pixels have weight 255, and white pixels have weight 0.
+ * 
+ */ +PIX * +pixPadToCenterCentroid(PIX *pixs, + l_int32 factor) + +{ +l_float32 cx, cy; +l_int32 xs, ys, delx, dely, icx, icy, ws, hs, wd, hd; +PIX *pix1, *pixd; + + PROCNAME("pixPadToCenterCentroid"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("invalid sampling factor", procName, NULL); + + pix1 = pixConvertTo8(pixs, FALSE); + pixCentroid8(pix1, factor, &cx, &cy); + icx = (l_int32)(cx + 0.5); + icy = (l_int32)(cy + 0.5); + pixGetDimensions(pix1, &ws, &hs, NULL); + delx = ws - 2 * icx; + dely = hs - 2 * icy; + xs = L_MAX(0, delx); + ys = L_MAX(0, dely); + wd = 2 * L_MAX(icx, ws - icx); + hd = 2 * L_MAX(icy, hs - icy); + pixd = pixCreate(wd, hd, 8); + pixSetAll(pixd); /* to white */ + pixCopyResolution(pixd, pixs); + pixRasterop(pixd, xs, ys, ws, hs, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + return pixd; +} + + +/*! + * \brief pixCentroid8() + * + * \param[in] pixs 8 bpp + * \param[in] factor subsampling factor; >= 1 + * \param[out] pcx x value of centroid + * \param[out] pcy y value of centroid + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This first does a photometric inversion (black = 255, white = 0).
+ *          It then finds the centroid of the result.  The inversion is
+ *          done because white is usually background, so the centroid
+ *          is computed based on the "foreground" gray pixels, and the
+ *          darker the pixel, the more weight it is given.
+ * 
+ */ +l_ok +pixCentroid8(PIX *pixs, + l_int32 factor, + l_float32 *pcx, + l_float32 *pcy) +{ +l_int32 i, j, w, h, wpl, val; +l_float32 sumx, sumy, sumv; +l_uint32 *data, *line; +PIX *pix1; + + PROCNAME("pixCentroid8"); + + if (pcx) *pcx = 0.0; + if (pcy) *pcy = 0.0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (!pcx || !pcy) + return ERROR_INT("&cx and &cy not both defined", procName, 1); + + pix1 = pixInvert(NULL, pixs); + pixGetDimensions(pix1, &w, &h, NULL); + data = pixGetData(pix1); + wpl = pixGetWpl(pix1); + sumx = sumy = sumv = 0.0; + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(line, j); + sumx += val * j; + sumy += val * i; + sumv += val; + } + } + pixDestroy(&pix1); + + if (sumv == 0) { + L_INFO("input image is white\n", procName); + *pcx = (l_float32)(w) / 2; + *pcy = (l_float32)(h) / 2; + } else { + *pcx = sumx / sumv; + *pcy = sumy / sumv; + } + + return 0; +} + + +/*! + * \brief pixDecideIfPhotoImage() + * + * \param[in] pix 8 bpp, centroid in center + * \param[in] factor subsampling for histograms; >= 1 + * \param[in] thresh threshold for photo/text; use 0 for default + * \param[in] n in range {1, ... 7}. n^2 is the maximum number + * of subregions for histograms; typ. n = 3. + * \param[out] pnaa array of normalized histograms + * \param[in] pixadebug [optional] use only for debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input image must be 8 bpp (no colormap), and padded with
+ *          white pixels so the centroid of photo-inverted pixels is at
+ *          the center of the image.
+ *      (2) The parameter %n specifies the "side" of the n x n grid
+ *          of subimages.  If the subimages have an aspect ratio larger
+ *          than 2, the grid will change, using n^2 as a maximum for
+ *          the number of subimages.  For example, if n == 3, but the
+ *          image is 600 x 200 pixels, a 3x3 grid would have subimages
+ *          of 200 x 67 pixels, which is more than 2:1, so we change
+ *          to a 4x2 grid where each subimage has 150 x 100 pixels.
+ *      (3) If the pix is not almost certainly a photoimage, the returned
+ *          histograms (%naa) are null.
+ *      (4) If histograms are generated, the white (255) count is set
+ *          to 0.  This removes all pixels values above 230, including
+ *          white padding from the centroid matching operation, from
+ *          consideration.  The resulting histograms are then normalized
+ *          so the maximum count is 255.
+ *      (5) Default for %thresh is 1.3; this seems sufficiently conservative.
+ *      (6) Use %pixadebug == NULL unless debug output is requested.
+ * 
+ */ +l_ok +pixDecideIfPhotoImage(PIX *pix, + l_int32 factor, + l_float32 thresh, + l_int32 n, + NUMAA **pnaa, + PIXA *pixadebug) +{ +char buf[64]; +l_int32 i, w, h, nx, ny, ngrids, istext, isphoto; +l_float32 maxval, sum1, sum2, ratio; +L_BMF *bmf; +NUMA *na1, *na2, *na3, *narv; +NUMAA *naa; +PIX *pix1; +PIXA *pixa1, *pixa2, *pixa3; + + PROCNAME("pixDecideIfPhotoImage"); + + if (!pnaa) + return ERROR_INT("&naa not defined", procName, 1); + *pnaa = NULL; + if (!pix || pixGetDepth(pix) != 8 || pixGetColormap(pix)) + return ERROR_INT("pix undefined or invalid", procName, 1); + if (n < 1 || n > 7) { + L_WARNING("n = %d is invalid; setting to 4\n", procName, n); + n = 4; + } + if (thresh <= 0.0) thresh = 1.3; /* default */ + + /* Look for text lines */ + pixDecideIfText(pix, NULL, &istext, pixadebug); + if (istext) { + L_INFO("Image is text\n", procName); + return 0; + } + + /* Determine grid from n */ + pixGetDimensions(pix, &w, &h, NULL); + if (w == 0 || h == 0) + return ERROR_INT("invalid pix dimension", procName, 1); + findHistoGridDimensions(n, w, h, &nx, &ny, 1); + + /* Evaluate histograms in each tile */ + pixa1 = pixaSplitPix(pix, nx, ny, 0, 0); + ngrids = nx * ny; + bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; + naa = numaaCreate(ngrids); + if (pixadebug) { + lept_rmdir("lept/compplot"); + lept_mkdir("lept/compplot"); + } + for (i = 0; i < ngrids; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + + /* Get histograms, set white count to 0, normalize max to 255 */ + na1 = pixGetGrayHistogram(pix1, factor); + numaSetValue(na1, 255, 0); + na2 = numaWindowedMean(na1, 5); /* do some smoothing */ + numaGetMax(na2, &maxval, NULL); + na3 = numaTransform(na2, 0, 255.0 / maxval); + if (pixadebug) { + snprintf(buf, sizeof(buf), "/tmp/lept/compplot/plot.%d", i); + gplotSimple1(na3, GPLOT_PNG, buf, "Histos"); + } + + numaaAddNuma(naa, na3, L_INSERT); + numaDestroy(&na1); + numaDestroy(&na2); + pixDestroy(&pix1); + } + if (pixadebug) { + pix1 = pixaDisplayTiledInColumns(pixa1, nx, 1.0, 30, 2); + pixaAddPix(pixadebug, pix1, L_INSERT); + pixa2 = pixaReadFiles("/tmp/lept/compplot", ".png"); + pixa3 = pixaScale(pixa2, 0.4, 0.4); + pix1 = pixaDisplayTiledInColumns(pixa3, nx, 1.0, 30, 2); + pixaAddPix(pixadebug, pix1, L_INSERT); + pixaDestroy(&pixa2); + pixaDestroy(&pixa3); + } + + /* Compute the standard deviation between these histos to decide + * if the image is photo or something more like line art, + * which does not support good comparison by tiled histograms. */ + grayInterHistogramStats(naa, 5, NULL, NULL, NULL, &narv); + + /* For photos, the root variance has a larger weight of + * values in the range [50 ... 150] compared to [200 ... 230], + * than text or line art. For the latter, most of the variance + * between tiles is in the lightest parts of the image, well + * above 150. */ + numaGetSumOnInterval(narv, 50, 150, &sum1); + numaGetSumOnInterval(narv, 200, 230, &sum2); + if (sum2 == 0.0) { /* shouldn't happen */ + ratio = 0.001; /* anything very small for debug output */ + isphoto = 0; /* be conservative */ + } else { + ratio = sum1 / sum2; + isphoto = (ratio > thresh) ? 1 : 0; + } + if (pixadebug) { + if (isphoto) + L_INFO("ratio %f > %f; isphoto is true\n", + procName, ratio, thresh); + else + L_INFO("ratio %f < %f; isphoto is false\n", + procName, ratio, thresh); + } + if (isphoto) + *pnaa = naa; + else + numaaDestroy(&naa); + bmfDestroy(&bmf); + numaDestroy(&narv); + pixaDestroy(&pixa1); + return 0; +} + + +/*! + * \brief findHistoGridDimensions() + * + * \param[in] n max number of grid elements is n^2; typ. n = 3 + * \param[in] w width of image to be subdivided + * \param[in] h height of image to be subdivided + * \param[out] pnx number of grid elements in x direction + * \param[out] pny number of grid elements in y direction + * \param[in] debug 1 for debug output to stderr + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This determines the number of subdivisions to be used on
+ *          the image in each direction.  A histogram will be built
+ *          for each subimage.
+ *      (2) The parameter %n specifies the "side" of the n x n grid
+ *          of subimages.  If the subimages have an aspect ratio larger
+ *          than 2, the grid will change, using n^2 as a maximum for
+ *          the number of subimages.  For example, if n == 3, but the
+ *          image is 600 x 200 pixels, a 3x3 grid would have subimages
+ *          of 200 x 67 pixels, which is more than 2:1, so we change
+ *          to a 4x2 grid where each subimage has 150 x 100 pixels.
+ * 
+ */ +static l_ok +findHistoGridDimensions(l_int32 n, + l_int32 w, + l_int32 h, + l_int32 *pnx, + l_int32 *pny, + l_int32 debug) +{ +l_int32 nx, ny, max; +l_float32 ratio; + + ratio = (l_float32)w / (l_float32)h; + max = n * n; + nx = ny = n; + while (nx > 1 && ny > 1) { + if (ratio > 2.0) { /* reduce ny */ + ny--; + nx = max / ny; + if (debug) + fprintf(stderr, "nx = %d, ny = %d, ratio w/h = %4.2f\n", + nx, ny, ratio); + } else if (ratio < 0.5) { /* reduce nx */ + nx--; + ny = max / nx; + if (debug) + fprintf(stderr, "nx = %d, ny = %d, ratio w/h = %4.2f\n", + nx, ny, ratio); + } else { /* we're ok */ + if (debug) + fprintf(stderr, "nx = %d, ny = %d, ratio w/h = %4.2f\n", + nx, ny, ratio); + break; + } + ratio = (l_float32)(ny * w) / (l_float32)(nx * h); + } + *pnx = nx; + *pny = ny; + return 0; +} + + +/*! + * \brief compareTilesByHisto() + * + * \param[in] naa1, naa2 each is a set of 256 entry histograms + * \param[in] minratio requiring image sizes be compatible; < 1.0 + * \param[in] w1, h1, w2, h2 image sizes from which histograms were made + * \param[out] pscore similarity score of histograms + * \param[in] pixadebug [optional] use only for debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) naa1 and naa2 must be generated using pixGenPhotoHistos(),
+ *          using the same tile sizes.
+ *      (2) The image dimensions must be similar.  The score is 0.0
+ *          if the ratio of widths and heights (smallest / largest)
+ *          exceeds a threshold %minratio, which must be between
+ *          0.5 and 1.0.  If set at 1.0, both images must be exactly
+ *          the same size.  A typical value for %minratio is 0.9.
+ *      (3) The input pixadebug is null unless debug output is requested.
+ * 
+ */ +l_ok +compareTilesByHisto(NUMAA *naa1, + NUMAA *naa2, + l_float32 minratio, + l_int32 w1, + l_int32 h1, + l_int32 w2, + l_int32 h2, + l_float32 *pscore, + PIXA *pixadebug) +{ +char buf1[128], buf2[128]; +l_int32 i, n; +l_float32 wratio, hratio, score, minscore, dist; +L_BMF *bmf; +NUMA *na1, *na2, *nadist, *nascore; + + PROCNAME("compareTilesByHisto"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!naa1 || !naa2) + return ERROR_INT("naa1 and naa2 not both defined", procName, 1); + + /* Filter for different sizes */ + wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : + (l_float32)w2 / (l_float32)w1; + hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : + (l_float32)h2 / (l_float32)h1; + if (wratio < minratio || hratio < minratio) { + if (pixadebug) + L_INFO("Sizes differ: wratio = %f, hratio = %f\n", + procName, wratio, hratio); + return 0; + } + n = numaaGetCount(naa1); + if (n != numaaGetCount(naa2)) { /* due to differing w/h ratio */ + L_INFO("naa1 and naa2 sizes are different\n", procName); + return 0; + } + + if (pixadebug) { + lept_rmdir("lept/comptile"); + lept_mkdir("lept/comptile"); + } + + + /* Evaluate histograms in each tile. Remove white before + * computing EMD, because there are may be a lot of white + * pixels due to padding, and we don't want to include them. + * This also makes the debug histo plots more informative. */ + minscore = 1.0; + nadist = numaCreate(n); + nascore = numaCreate(n); + bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; + for (i = 0; i < n; i++) { + na1 = numaaGetNuma(naa1, i, L_CLONE); + na2 = numaaGetNuma(naa2, i, L_CLONE); + numaSetValue(na1, 255, 0.0); + numaSetValue(na2, 255, 0.0); + + /* To compare histograms, use the normalized earthmover distance. + * Further normalize to get the EM distance as a fraction of the + * maximum distance in the histogram (255). Finally, scale this + * up by 10.0, and subtract from 1.0 to get a similarity score. */ + numaEarthMoverDistance(na1, na2, &dist); + score = L_MAX(0.0, 1.0 - 10.0 * (dist / 255.)); + numaAddNumber(nadist, dist); + numaAddNumber(nascore, score); + minscore = L_MIN(minscore, score); + if (pixadebug) { + snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d", i); + gplotSimple2(na1, na2, GPLOT_PNG, buf1, "Histos"); + } + numaDestroy(&na1); + numaDestroy(&na2); + } + *pscore = minscore; + + if (pixadebug) { + for (i = 0; i < n; i++) { + PIX *pix1, *pix2; + snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d.png", i); + pix1 = pixRead(buf1); + numaGetFValue(nadist, i, &dist); + numaGetFValue(nascore, i, &score); + snprintf(buf2, sizeof(buf2), + "Image %d\ndist = %5.3f, score = %5.3f", i, dist, score); + pix2 = pixAddTextlines(pix1, bmf, buf2, 0x0000ff00, L_ADD_BELOW); + pixaAddPix(pixadebug, pix2, L_INSERT); + pixDestroy(&pix1); + } + fprintf(stderr, "Writing to /tmp/lept/comptile/comparegray.pdf\n"); + pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL, + "/tmp/lept/comptile/comparegray.pdf"); + numaWriteDebug("/tmp/lept/comptile/scores.na", nascore); + numaWriteDebug("/tmp/lept/comptile/dists.na", nadist); + } + + bmfDestroy(&bmf); + numaDestroy(&nadist); + numaDestroy(&nascore); + return 0; +} + + +/*! + * \brief pixCompareGrayByHisto() + * + * \param[in] pix1, pix2 any depth; colormap OK + * \param[in] box1, box2 [optional] region selected from each; can be null + * \param[in] minratio requiring sizes be compatible; < 1.0 + * \param[in] maxgray max value to keep in histo; >= 200, 255 to keep all + * \param[in] factor subsampling factor; >= 1 + * \param[in] n in range {1, ... 7}. n^2 is the maximum number + * of subregions for histograms; typ. n = 3. + * \param[out] pscore similarity score of histograms + * \param[in] debugflag 1 for debug output; 0 for no debugging + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function compares two grayscale photo regions.  It can
+ *          do it with a single histogram from each region, or with a
+ *          set of spatially aligned histograms.  For both cases,
+ *          align the regions using the centroid of the inverse image,
+ *          and crop to the smallest of the two.
+ *      (2) The parameter %n specifies the "side" of an n x n grid
+ *          of subimages.  If the subimages have an aspect ratio larger
+ *          than 2, the grid will change, using n^2 as a maximum for
+ *          the number of subimages.  For example, if n == 3, but the
+ *          image is 600 x 200 pixels, a 3x3 grid would have subimages
+ *          of 200 x 67 pixels, which is more than 2:1, so we change
+ *          to a 4x2 grid where each subimage has 150 x 100 pixels.
+ *      (3) An initial filter gives %score = 0 if the ratio of widths
+ *          and heights (smallest / largest) does not exceed a
+ *          threshold %minratio.  This must be between 0.5 and 1.0.
+ *          If set at 1.0, both images must be exactly the same size.
+ *          A typical value for %minratio is 0.9.
+ *      (4) The lightest values in the histogram can be disregarded.
+ *          Set %maxgray to the lightest value to be kept.  For example,
+ *          to eliminate white (255), set %maxgray = 254.  %maxgray must
+ *          be >= 200.
+ *      (5) For an efficient representation of the histogram, normalize
+ *          using a multiplicative factor so that the number in the
+ *          maximum bucket is 255.  It then takes 256 bytes to store.
+ *      (6) When comparing the histograms of two regions:
+ *          ~ Use %maxgray = 254 to ignore the white pixels, the number
+ *            of which may be sensitive to the crop region if the pixels
+ *            outside that region are white.
+ *          ~ Use the Earth Mover distance (EMD), with the histograms
+ *            normalized so that the sum over bins is the same.
+ *            Further normalize by dividing by 255, so that the result
+ *            is in [0.0 ... 1.0].
+ *      (7) Get a similarity score S = 1.0 - k * D, where
+ *            k is a constant, say in the range 5-10
+ *            D = normalized EMD
+ *          and for multiple tiles, take the Min(S) to be the final score.
+ *          Using aligned tiles gives protection against accidental
+ *          similarity of the overall grayscale histograms.
+ *          A small number of aligned tiles works well.
+ *      (8) With debug on, you get a pdf that shows, for each tile,
+ *          the images, histograms and score.
+ *      (9) When to use:
+ *          (a) Because this function should not be used on text or
+ *              line graphics, which can give false positive results
+ *              (i.e., high scores for different images), the input
+ *              images should be filtered.
+ *          (b) To filter, first use pixDecideIfText().  If that function
+ *              says the image is text, do not use it.  If the function
+ *              says it is not text, it still may be line graphics, and
+ *              in that case, use:
+ *                 pixGetGrayHistogramTiled()
+ *                 grayInterHistogramStats()
+ *              to determine whether it is photo or line graphics.
+ * 
+ */ +l_ok +pixCompareGrayByHisto(PIX *pix1, + PIX *pix2, + BOX *box1, + BOX *box2, + l_float32 minratio, + l_int32 maxgray, + l_int32 factor, + l_int32 n, + l_float32 *pscore, + l_int32 debugflag) +{ +l_int32 w1, h1, w2, h2; +l_float32 wratio, hratio; +BOX *box3, *box4; +PIX *pix3, *pix4, *pix5, *pix6, *pix7, *pix8; +PIXA *pixa; + + PROCNAME("pixCompareGrayByHisto"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + if (minratio < 0.5 || minratio > 1.0) + return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1); + if (maxgray < 200) + return ERROR_INT("invalid maxgray; should be >= 200", procName, 1); + maxgray = L_MIN(255, maxgray); + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (n < 1 || n > 7) { + L_WARNING("n = %d is invalid; setting to 4\n", procName, n); + n = 4; + } + + if (debugflag) + lept_mkdir("lept/comp"); + + /* Initial filter by size */ + if (box1) + boxGetGeometry(box1, NULL, NULL, &w1, &h1); + else + pixGetDimensions(pix1, &w1, &h1, NULL); + if (box2) + boxGetGeometry(box2, NULL, NULL, &w2, &h2); + else + pixGetDimensions(pix1, &w2, &h2, NULL); + wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : + (l_float32)w2 / (l_float32)w1; + hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : + (l_float32)h2 / (l_float32)h1; + if (wratio < minratio || hratio < minratio) + return 0; + + /* Initial crop, if necessary */ + if (box1) + pix3 = pixClipRectangle(pix1, box1, NULL); + else + pix3 = pixClone(pix1); + if (box2) + pix4 = pixClipRectangle(pix2, box2, NULL); + else + pix4 = pixClone(pix2); + + /* Convert to 8 bpp, align centroids and do maximal crop */ + pix5 = pixConvertTo8(pix3, FALSE); + pix6 = pixConvertTo8(pix4, FALSE); + pixCropAlignedToCentroid(pix5, pix6, factor, &box3, &box4); + pix7 = pixClipRectangle(pix5, box3, NULL); + pix8 = pixClipRectangle(pix6, box4, NULL); + pixa = (debugflag) ? pixaCreate(0) : NULL; + if (debugflag) { + PIX *pix9, *pix10, *pix11, *pix12, *pix13; + PIXA *pixa2; + pix9 = pixConvertTo32(pix5); + pix10 = pixConvertTo32(pix6); + pixRenderBoxArb(pix9, box3, 2, 255, 0, 0); + pixRenderBoxArb(pix10, box4, 2, 255, 0, 0); + pix11 = pixScaleToSize(pix9, 400, 0); + pix12 = pixScaleToSize(pix10, 400, 0); + pixa2 = pixaCreate(2); + pixaAddPix(pixa2, pix11, L_INSERT); + pixaAddPix(pixa2, pix12, L_INSERT); + pix13 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 0); + pixaAddPix(pixa, pix13, L_INSERT); + pixDestroy(&pix9); + pixDestroy(&pix10); + pixaDestroy(&pixa2); + } + pixDestroy(&pix3); + pixDestroy(&pix4); + pixDestroy(&pix5); + pixDestroy(&pix6); + boxDestroy(&box3); + boxDestroy(&box4); + + /* Tile and compare histograms */ + pixCompareTilesByHisto(pix7, pix8, maxgray, factor, n, pscore, pixa); + pixaDestroy(&pixa); + pixDestroy(&pix7); + pixDestroy(&pix8); + return 0; +} + + +/*! + * \brief pixCompareTilesByHisto() + * + * \param[in] pix1, pix2 8 bpp + * \param[in] maxgray max value to keep in histo; 255 to keep all + * \param[in] factor subsampling factor; >= 1 + * \param[in] n see pixCompareGrayByHisto() + * \param[out] pscore similarity score of histograms + * \param[in] pixadebug [optional] use only for debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This static function is only called from pixCompareGrayByHisto().
+ *          The input images have been converted to 8 bpp if necessary,
+ *          aligned and cropped.
+ *      (2) The input pixadebug is null unless debug output is requested.
+ *      (3) See pixCompareGrayByHisto() for details.
+ * 
+ */ +static l_ok +pixCompareTilesByHisto(PIX *pix1, + PIX *pix2, + l_int32 maxgray, + l_int32 factor, + l_int32 n, + l_float32 *pscore, + PIXA *pixadebug) +{ +char buf[64]; +l_int32 w, h, i, j, nx, ny, ngr; +l_float32 score, minscore, maxval1, maxval2, dist; +L_BMF *bmf; +NUMA *na1, *na2, *na3, *na4, *na5, *na6, *na7; +PIX *pix3, *pix4; +PIXA *pixa1, *pixa2; + + PROCNAME("pixCompareTilesByHisto"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + + /* Determine grid from n */ + pixGetDimensions(pix1, &w, &h, NULL); + findHistoGridDimensions(n, w, h, &nx, &ny, 1); + ngr = nx * ny; + + /* Evaluate histograms in each tile */ + pixa1 = pixaSplitPix(pix1, nx, ny, 0, 0); + pixa2 = pixaSplitPix(pix2, nx, ny, 0, 0); + na7 = (pixadebug) ? numaCreate(ngr) : NULL; + bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; + minscore = 1.0; + for (i = 0; i < ngr; i++) { + pix3 = pixaGetPix(pixa1, i, L_CLONE); + pix4 = pixaGetPix(pixa2, i, L_CLONE); + + /* Get histograms, set white count to 0, normalize max to 255 */ + na1 = pixGetGrayHistogram(pix3, factor); + na2 = pixGetGrayHistogram(pix4, factor); + if (maxgray < 255) { + for (j = maxgray + 1; j <= 255; j++) { + numaSetValue(na1, j, 0); + numaSetValue(na2, j, 0); + } + } + na3 = numaWindowedMean(na1, 5); + na4 = numaWindowedMean(na2, 5); + numaGetMax(na3, &maxval1, NULL); + numaGetMax(na4, &maxval2, NULL); + na5 = numaTransform(na3, 0, 255.0 / maxval1); + na6 = numaTransform(na4, 0, 255.0 / maxval2); + if (pixadebug) { + gplotSimple2(na5, na6, GPLOT_PNG, "/tmp/lept/comp/plot1", "Histos"); + } + + /* To compare histograms, use the normalized earthmover distance. + * Further normalize to get the EM distance as a fraction of the + * maximum distance in the histogram (255). Finally, scale this + * up by 10.0, and subtract from 1.0 to get a similarity score. */ + numaEarthMoverDistance(na5, na6, &dist); + score = L_MAX(0.0, 1.0 - 8.0 * (dist / 255.)); + if (pixadebug) numaAddNumber(na7, score); + minscore = L_MIN(minscore, score); + if (pixadebug) { + PIX *pix5, *pix6, *pix7, *pix8, *pix9, *pix10; + PIXA *pixa3; + l_int32 w, h, wscale; + pixa3 = pixaCreate(3); + pixGetDimensions(pix3, &w, &h, NULL); + wscale = (w > h) ? 700 : 400; + pix5 = pixScaleToSize(pix3, wscale, 0); + pix6 = pixScaleToSize(pix4, wscale, 0); + pixaAddPix(pixa3, pix5, L_INSERT); + pixaAddPix(pixa3, pix6, L_INSERT); + pix7 = pixRead("/tmp/lept/comp/plot1.png"); + pix8 = pixScaleToSize(pix7, 700, 0); + snprintf(buf, sizeof(buf), "%5.3f", score); + pix9 = pixAddTextlines(pix8, bmf, buf, 0x0000ff00, L_ADD_RIGHT); + pixaAddPix(pixa3, pix9, L_INSERT); + pix10 = pixaDisplayTiledInRows(pixa3, 32, 1000, 1.0, 0, 50, 0); + pixaAddPix(pixadebug, pix10, L_INSERT); + pixDestroy(&pix7); + pixDestroy(&pix8); + pixaDestroy(&pixa3); + } + numaDestroy(&na1); + numaDestroy(&na2); + numaDestroy(&na3); + numaDestroy(&na4); + numaDestroy(&na5); + numaDestroy(&na6); + pixDestroy(&pix3); + pixDestroy(&pix4); + } + *pscore = minscore; + + if (pixadebug) { + pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL, + "/tmp/lept/comp/comparegray.pdf"); + numaWriteDebug("/tmp/lept/comp/tilescores.na", na7); + } + + bmfDestroy(&bmf); + numaDestroy(&na7); + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + return 0; +} + + +/*! + * \brief pixCropAlignedToCentroid() + * + * \param[in] pix1, pix2 any depth; colormap OK + * \param[in] factor subsampling; >= 1 + * \param[out] pbox1 crop box for pix1 + * \param[out] pbox2 crop box for pix2 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This finds the maximum crop boxes for two 8 bpp images when
+ *          their centroids of their photometric inverses are aligned.
+ *          Black pixels have weight 255; white pixels have weight 0.
+ * 
+ */ +l_ok +pixCropAlignedToCentroid(PIX *pix1, + PIX *pix2, + l_int32 factor, + BOX **pbox1, + BOX **pbox2) +{ +l_float32 cx1, cy1, cx2, cy2; +l_int32 w1, h1, w2, h2, icx1, icy1, icx2, icy2; +l_int32 xm, xm1, xm2, xp, xp1, xp2, ym, ym1, ym2, yp, yp1, yp2; +PIX *pix3, *pix4; + + PROCNAME("pixCropAlignedToCentroid"); + + if (pbox1) *pbox1 = NULL; + if (pbox2) *pbox2 = NULL; + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + if (factor < 1) + return ERROR_INT("subsampling factor must be >= 1", procName, 1); + if (!pbox1 || !pbox2) + return ERROR_INT("&box1 and &box2 not both defined", procName, 1); + + pix3 = pixConvertTo8(pix1, FALSE); + pix4 = pixConvertTo8(pix2, FALSE); + pixCentroid8(pix3, factor, &cx1, &cy1); + pixCentroid8(pix4, factor, &cx2, &cy2); + pixGetDimensions(pix3, &w1, &h1, NULL); + pixGetDimensions(pix4, &w2, &h2, NULL); + pixDestroy(&pix3); + pixDestroy(&pix4); + + icx1 = (l_int32)(cx1 + 0.5); + icy1 = (l_int32)(cy1 + 0.5); + icx2 = (l_int32)(cx2 + 0.5); + icy2 = (l_int32)(cy2 + 0.5); + xm = L_MIN(icx1, icx2); + xm1 = icx1 - xm; + xm2 = icx2 - xm; + xp = L_MIN(w1 - icx1, w2 - icx2); /* one pixel beyond to the right */ + xp1 = icx1 + xp; + xp2 = icx2 + xp; + ym = L_MIN(icy1, icy2); + ym1 = icy1 - ym; + ym2 = icy2 - ym; + yp = L_MIN(h1 - icy1, h2 - icy2); /* one pixel below the bottom */ + yp1 = icy1 + yp; + yp2 = icy2 + yp; + *pbox1 = boxCreate(xm1, ym1, xp1 - xm1, yp1 - ym1); + *pbox2 = boxCreate(xm2, ym2, xp2 - xm2, yp2 - ym2); + return 0; +} + + +/*! + * \brief l_compressGrayHistograms() + * + * \param[in] naa set of 256-entry histograms + * \param[in] w, h size of image + * \param[out] psize size of byte array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This first writes w and h to the byte array as 4 byte ints.
+ *      (2) Then it normalizes each histogram to a max value of 255,
+ *          and saves each value as a byte.  If there are
+ *          N histograms, the output bytearray has 8 + 256 * N bytes.
+ *      (3) Further compression of the array with zlib yields only about
+ *          a 25% decrease in size, so we don't bother.  If size reduction
+ *          were important, a lossy transform using a 1-dimensional DCT
+ *          would be effective, because we don't care about the fine
+ *          details of these histograms.
+ * 
+ */ +l_uint8 * +l_compressGrayHistograms(NUMAA *naa, + l_int32 w, + l_int32 h, + size_t *psize) +{ +l_uint8 *bytea; +l_int32 i, j, n, nn, ival; +l_float32 maxval; +NUMA *na1, *na2; + + PROCNAME("l_compressGrayHistograms"); + + if (!psize) + return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); + *psize = 0; + if (!naa) + return (l_uint8 *)ERROR_PTR("naa not defined", procName, NULL); + n = numaaGetCount(naa); + for (i = 0; i < n; i++) { + nn = numaaGetNumaCount(naa, i); + if (nn != 256) { + L_ERROR("%d numbers in numa[%d]\n", procName, nn, i); + return NULL; + } + } + + if ((bytea = (l_uint8 *)LEPT_CALLOC(8 + 256 * n, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL); + *psize = 8 + 256 * n; + l_setDataFourBytes(bytea, 0, w); + l_setDataFourBytes(bytea, 1, h); + for (i = 0; i < n; i++) { + na1 = numaaGetNuma(naa, i, L_COPY); + numaGetMax(na1, &maxval, NULL); + na2 = numaTransform(na1, 0, 255.0 / maxval); + for (j = 0; j < 256; j++) { + numaGetIValue(na2, j, &ival); + bytea[8 + 256 * i + j] = ival; + } + numaDestroy(&na1); + numaDestroy(&na2); + } + + return bytea; +} + + +/*! + * \brief l_uncompressGrayHistograms() + * + * \param[in] bytea byte array of size 8 + 256 * N, N an integer + * \param[in] size size of byte array + * \param[out] pw width of the image that generated the histograms + * \param[out] ph height of the image + * \return numaa representing N histograms, each with 256 bins, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) The first 8 bytes are read as two 32-bit ints.
+ *      (2) Then this constructs a numaa representing some number of
+ *          gray histograms that are normalized such that the max value
+ *          in each histogram is 255.  The data is stored as a byte
+ *          array, with 256 bytes holding the data for each histogram.
+ *          Each gray histogram was computed from a tile of a grayscale image.
+ * 
+ */ +NUMAA * +l_uncompressGrayHistograms(l_uint8 *bytea, + size_t size, + l_int32 *pw, + l_int32 *ph) +{ +l_int32 i, j, n; +NUMA *na; +NUMAA *naa; + + PROCNAME("l_uncompressGrayHistograms"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!pw || !ph) + return (NUMAA *)ERROR_PTR("&w and &h not both defined", procName, NULL); + if (!bytea) + return (NUMAA *)ERROR_PTR("bytea not defined", procName, NULL); + n = (size - 8) / 256; + if ((size - 8) % 256 != 0) + return (NUMAA *)ERROR_PTR("bytea size is invalid", procName, NULL); + + *pw = l_getDataFourBytes(bytea, 0); + *ph = l_getDataFourBytes(bytea, 1); + naa = numaaCreate(n); + for (i = 0; i < n; i++) { + na = numaCreate(256); + for (j = 0; j < 256; j++) + numaAddNumber(na, bytea[8 + 256 * i + j]); + numaaAddNuma(naa, na, L_INSERT); + } + + return naa; +} + + +/*------------------------------------------------------------------* + * Translated images at the same resolution * + *------------------------------------------------------------------*/ +/*! + * \brief pixCompareWithTranslation() + * + * \param[in] pix1, pix2 any depth; colormap OK + * \param[in] thresh threshold for converting to 1 bpp + * \param[out] pdelx x translation on pix2 to align with pix1 + * \param[out] pdely y translation on pix2 to align with pix1 + * \param[out] pscore correlation score at best alignment + * \param[in] debugflag 1 for debug output; 0 for no debugging + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a coarse-to-fine search for best translational
+ *          alignment of two images, measured by a scoring function
+ *          that is the correlation between the fg pixels.
+ *      (2) The threshold is used if the images aren't 1 bpp.
+ *      (3) With debug on, you get a pdf that shows, as a grayscale
+ *          image, the score as a function of shift from the initial
+ *          estimate, for each of the four levels.  The shift is 0 at
+ *          the center of the image.
+ *      (4) With debug on, you also get a pdf that shows the
+ *          difference at the best alignment between the two images,
+ *          at each of the four levels.  The red and green pixels
+ *          show locations where one image has a fg pixel and the
+ *          other doesn't.  The black pixels are where both images
+ *          have fg pixels, and white pixels are where neither image
+ *          has fg pixels.
+ * 
+ */ +l_ok +pixCompareWithTranslation(PIX *pix1, + PIX *pix2, + l_int32 thresh, + l_int32 *pdelx, + l_int32 *pdely, + l_float32 *pscore, + l_int32 debugflag) +{ +l_uint8 *subtab; +l_int32 i, level, area1, area2, delx, dely; +l_int32 etransx, etransy, maxshift, dbint; +l_int32 *stab, *ctab; +l_float32 cx1, cx2, cy1, cy2, score; +PIX *pixb1, *pixb2, *pixt1, *pixt2, *pixt3, *pixt4; +PIXA *pixa1, *pixa2, *pixadb; + + PROCNAME("pixCompareWithTranslation"); + + if (pdelx) *pdelx = 0; + if (pdely) *pdely = 0; + if (pscore) *pscore = 0.0; + if (!pdelx || !pdely) + return ERROR_INT("&delx and &dely not defined", procName, 1); + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + if (!pix1) + return ERROR_INT("pix1 not defined", procName, 1); + if (!pix2) + return ERROR_INT("pix2 not defined", procName, 1); + + /* Make tables */ + subtab = makeSubsampleTab2x(); + stab = makePixelSumTab8(); + ctab = makePixelCentroidTab8(); + + /* Binarize each image */ + pixb1 = pixConvertTo1(pix1, thresh); + pixb2 = pixConvertTo1(pix2, thresh); + + /* Make a cascade of 2x reduced images for each, thresholding + * with level 2 (neutral), down to 8x reduction */ + pixa1 = pixaCreate(4); + pixa2 = pixaCreate(4); + if (debugflag) + pixadb = pixaCreate(4); + pixaAddPix(pixa1, pixb1, L_INSERT); + pixaAddPix(pixa2, pixb2, L_INSERT); + for (i = 0; i < 3; i++) { + pixt1 = pixReduceRankBinary2(pixb1, 2, subtab); + pixt2 = pixReduceRankBinary2(pixb2, 2, subtab); + pixaAddPix(pixa1, pixt1, L_INSERT); + pixaAddPix(pixa2, pixt2, L_INSERT); + pixb1 = pixt1; + pixb2 = pixt2; + } + + /* At the lowest level, use the centroids with a maxshift of 6 + * to search for the best alignment. Then at higher levels, + * use the result from the level below as the initial approximation + * for the alignment, and search with a maxshift of 2. */ + for (level = 3; level >= 0; level--) { + pixt1 = pixaGetPix(pixa1, level, L_CLONE); + pixt2 = pixaGetPix(pixa2, level, L_CLONE); + pixCountPixels(pixt1, &area1, stab); + pixCountPixels(pixt2, &area2, stab); + if (level == 3) { + pixCentroid(pixt1, ctab, stab, &cx1, &cy1); + pixCentroid(pixt2, ctab, stab, &cx2, &cy2); + etransx = lept_roundftoi(cx1 - cx2); + etransy = lept_roundftoi(cy1 - cy2); + maxshift = 6; + } else { + etransx = 2 * delx; + etransy = 2 * dely; + maxshift = 2; + } + dbint = (debugflag) ? level + 1 : 0; + pixBestCorrelation(pixt1, pixt2, area1, area2, etransx, etransy, + maxshift, stab, &delx, &dely, &score, dbint); + if (debugflag) { + fprintf(stderr, "Level %d: delx = %d, dely = %d, score = %7.4f\n", + level, delx, dely, score); + pixRasteropIP(pixt2, delx, dely, L_BRING_IN_WHITE); + pixt3 = pixDisplayDiffBinary(pixt1, pixt2); + pixt4 = pixExpandReplicate(pixt3, 8 / (1 << (3 - level))); + pixaAddPix(pixadb, pixt4, L_INSERT); + pixDestroy(&pixt3); + } + pixDestroy(&pixt1); + pixDestroy(&pixt2); + } + + if (debugflag) { + pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, NULL, + "/tmp/lept/comp/compare.pdf"); + convertFilesToPdf("/tmp/lept/comp", "correl_", 30, 1.0, L_FLATE_ENCODE, + 0, "Correlation scores at levels 1 through 5", + "/tmp/lept/comp/correl.pdf"); + pixaDestroy(&pixadb); + } + + *pdelx = delx; + *pdely = dely; + *pscore = score; + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + LEPT_FREE(subtab); + LEPT_FREE(stab); + LEPT_FREE(ctab); + return 0; +} + + +/*! + * \brief pixBestCorrelation() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \param[in] area1 number of on pixels in pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] etransx estimated x translation of pix2 to align with pix1 + * \param[in] etransy estimated y translation of pix2 to align with pix1 + * \param[in] maxshift max x and y shift of pix2, around the estimated + * alignment location, relative to pix1 + * \param[in] tab8 [optional] sum tab for ON pixels in byte; can be NULL + * \param[out] pdelx [optional] best x shift of pix2 relative to pix1 + * \param[out] pdely [optional] best y shift of pix2 relative to pix1 + * \param[out] pscore [optional] maximum score found; can be NULL + * \param[in] debugflag <= 0 to skip; positive to generate output. + * The integer is used to label the debug image. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This maximizes the correlation score between two 1 bpp images,
+ *          by starting with an estimate of the alignment
+ *          (%etransx, %etransy) and computing the correlation around this.
+ *          It optionally returns the shift (%delx, %dely) that maximizes
+ *          the correlation score when pix2 is shifted by this amount
+ *          relative to pix1.
+ *      (2) Get the centroids of pix1 and pix2, using pixCentroid(),
+ *          to compute (%etransx, %etransy).  Get the areas using
+ *          pixCountPixels().
+ *      (3) The centroid of pix2 is shifted with respect to the centroid
+ *          of pix1 by all values between -maxshiftx and maxshiftx,
+ *          and likewise for the y shifts.  Therefore, the number of
+ *          correlations computed is:
+ *               (2 * maxshiftx + 1) * (2 * maxshifty + 1)
+ *          Consequently, if pix1 and pix2 are large, you should do this
+ *          in a coarse-to-fine sequence.  See the use of this function
+ *          in pixCompareWithTranslation().
+ * 
+ */ +l_ok +pixBestCorrelation(PIX *pix1, + PIX *pix2, + l_int32 area1, + l_int32 area2, + l_int32 etransx, + l_int32 etransy, + l_int32 maxshift, + l_int32 *tab8, + l_int32 *pdelx, + l_int32 *pdely, + l_float32 *pscore, + l_int32 debugflag) +{ +l_int32 shiftx, shifty, delx, dely; +l_int32 *tab; +l_float32 maxscore, score; +FPIX *fpix; +PIX *pix3, *pix4; + + PROCNAME("pixBestCorrelation"); + + if (pdelx) *pdelx = 0; + if (pdely) *pdely = 0; + if (pscore) *pscore = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); + if (!area1 || !area2) + return ERROR_INT("areas must be > 0", procName, 1); + + if (debugflag > 0) + fpix = fpixCreate(2 * maxshift + 1, 2 * maxshift + 1); + + if (!tab8) + tab = makePixelSumTab8(); + else + tab = tab8; + + /* Search over a set of {shiftx, shifty} for the max */ + maxscore = 0; + delx = etransx; + dely = etransy; + for (shifty = -maxshift; shifty <= maxshift; shifty++) { + for (shiftx = -maxshift; shiftx <= maxshift; shiftx++) { + pixCorrelationScoreShifted(pix1, pix2, area1, area2, + etransx + shiftx, + etransy + shifty, tab, &score); + if (debugflag > 0) { + fpixSetPixel(fpix, maxshift + shiftx, maxshift + shifty, + 1000.0 * score); +/* fprintf(stderr, "(sx, sy) = (%d, %d): score = %6.4f\n", + shiftx, shifty, score); */ + } + if (score > maxscore) { + maxscore = score; + delx = etransx + shiftx; + dely = etransy + shifty; + } + } + } + + if (debugflag > 0) { + lept_mkdir("lept/comp"); + char buf[128]; + pix3 = fpixDisplayMaxDynamicRange(fpix); + pix4 = pixExpandReplicate(pix3, 20); + snprintf(buf, sizeof(buf), "/tmp/lept/comp/correl_%d.png", + debugflag); + pixWrite(buf, pix4, IFF_PNG); + pixDestroy(&pix3); + pixDestroy(&pix4); + fpixDestroy(&fpix); + } + + if (pdelx) *pdelx = delx; + if (pdely) *pdely = dely; + if (pscore) *pscore = maxscore; + if (!tab8) LEPT_FREE(tab); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/config_auto.h b/hgdriver/3rdparty/hgOCR/leptonica/config_auto.h new file mode 100644 index 0000000..63f9492 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/config_auto.h @@ -0,0 +1,9 @@ +#define HAVE_LIBJPEG 0 +#define HAVE_LIBTIFF 0 +#define HAVE_LIBPNG 0 +#define HAVE_LIBZ 1 +#define HAVE_LIBGIF 0 +#define HAVE_LIBUNGIF 0 +#define HAVE_LIBWEBP 0 +#define HAVE_LIBJP2K 0 +#define LIBJP2K_HEADER \ No newline at end of file diff --git a/hgdriver/3rdparty/hgOCR/leptonica/conncomp.c b/hgdriver/3rdparty/hgOCR/leptonica/conncomp.c new file mode 100644 index 0000000..bd7aecb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/conncomp.c @@ -0,0 +1,1242 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file conncomp.c + *
+ *
+ *    Connected component counting and extraction, using Heckbert's
+ *    stack-based filling algorithm.
+ *
+ *      4- and 8-connected components: counts, bounding boxes and images
+ *
+ *      Top-level calls:
+ *            BOXA     *pixConnComp()
+ *            BOXA     *pixConnCompPixa()
+ *            BOXA     *pixConnCompBB()
+ *            l_int32   pixCountConnComp()
+ *
+ *      Identify the next c.c. to be erased:
+ *            l_int32   nextOnPixelInRaster()
+ *    static  l_int32   nextOnPixelInRasterLow()
+ *
+ *      Erase the c.c., saving the b.b.:
+ *            BOX      *pixSeedfillBB()
+ *            BOX      *pixSeedfill4BB()
+ *            BOX      *pixSeedfill8BB()
+ *
+ *      Just erase the c.c.:
+ *            l_int32   pixSeedfill()
+ *            l_int32   pixSeedfill4()
+ *            l_int32   pixSeedfill8()
+ *
+ *      Static stack helper functions for single raster line seedfill:
+ *            static void    pushFillsegBB()
+ *            static void    pushFillseg()
+ *            static void    popFillseg()
+ *
+ *  The basic method in pixConnCompBB() is very simple.  We scan the
+ *  image in raster order, looking for the next ON pixel.  When it
+ *  is found, we erase it and every pixel of the 4- or 8-connected
+ *  component to which it belongs, using Heckbert's seedfill
+ *  algorithm.  As pixels are erased, we keep track of the
+ *  minimum rectangle that encloses all erased pixels; after
+ *  the connected component has been erased, we save its
+ *  bounding box in an array of boxes.  When all pixels in the
+ *  image have been erased, we have an array that describes every
+ *  4- or 8-connected component in terms of its bounding box.
+ *
+ *  pixConnCompPixa() is a slight variation on pixConnCompBB(),
+ *  where we additionally save an array of images (in a Pixa)
+ *  of each of the 4- or 8-connected components.  This is done trivially
+ *  by maintaining two temporary images.  We erase a component from one,
+ *  and use the bounding box to extract the pixels within the b.b.
+ *  from each of the two images.  An XOR between these subimages
+ *  gives the erased component.  Then we erase the component from the
+ *  second image using the XOR again, with the extracted component
+ *  placed on the second image at the location of the bounding box.
+ *  Rasterop does all the work.  At the end, we have an array
+ *  of the 4- or 8-connected components, as well as an array of the
+ *  bounding boxes that describe where they came from in the original image.
+ *
+ *  If you just want the number of connected components, pixCountConnComp()
+ *  is a bit faster than pixConnCompBB(), because it doesn't have to
+ *  keep track of the bounding rectangles for each c.c.
+ * 
+ */ + +#include "allheaders.h" + +/*! + * \brief The struct FillSeg is used by the Heckbert seedfill algorithm to + * hold information about image segments that are waiting to be + * investigated. We use two Stacks, one to hold the FillSegs in use, + * and an auxiliary Stack as a reservoir to hold FillSegs for re-use. + */ +struct FillSeg +{ + l_int32 xleft; /*!< left edge of run */ + l_int32 xright; /*!< right edge of run */ + l_int32 y; /*!< run y */ + l_int32 dy; /*!< parent segment direction: 1 above, -1 below) */ +}; +typedef struct FillSeg FILLSEG; + +static l_int32 nextOnPixelInRasterLow(l_uint32 *data, l_int32 w, l_int32 h, + l_int32 wpl, l_int32 xstart, + l_int32 ystart, l_int32 *px, l_int32 *py); + + /* Static accessors for FillSegs on a stack */ +static void pushFillsegBB(L_STACK *stack, l_int32 xleft, l_int32 xright, + l_int32 y, l_int32 dy, l_int32 ymax, + l_int32 *pminx, l_int32 *pmaxx, + l_int32 *pminy, l_int32 *pmaxy); +static void pushFillseg(L_STACK *stack, l_int32 xleft, l_int32 xright, + l_int32 y, l_int32 dy, l_int32 ymax); +static void popFillseg(L_STACK *stack, l_int32 *pxleft, l_int32 *pxright, + l_int32 *py, l_int32 *pdy); + + +#ifndef NO_CONSOLE_IO +#define DEBUG 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-----------------------------------------------------------------------* + * Bounding boxes of 4 Connected Components * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixConnComp() + * + * \param[in] pixs 1 bpp + * \param[out] ppixa [optional] pixa of each c.c. + * \param[in] connectivity 4 or 8 + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This is the top-level call for getting bounding boxes or
+ *          a pixa of the components, and it can be used instead
+ *          of either pixConnCompBB() or pixConnCompPixa(), rsp.
+ * 
+ */ +BOXA * +pixConnComp(PIX *pixs, + PIXA **ppixa, + l_int32 connectivity) +{ + + PROCNAME("pixConnComp"); + + if (ppixa) *ppixa = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + if (!ppixa) + return pixConnCompBB(pixs, connectivity); + else + return pixConnCompPixa(pixs, ppixa, connectivity); +} + + +/*! + * \brief pixConnCompPixa() + * + * \param[in] pixs 1 bpp + * \param[out] ppixa pixa of each c.c. + * \param[in] connectivity 4 or 8 + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds bounding boxes of 4- or 8-connected components
+ *          in a binary image, and saves images of each c.c
+ *          in a pixa array.
+ *      (2) It sets up 2 temporary pix, and for each c.c. that is
+ *          located in raster order, it erases the c.c. from one pix,
+ *          then uses the b.b. to extract the c.c. from the two pix using
+ *          an XOR, and finally erases the c.c. from the second pix.
+ *      (3) A clone of the returned boxa (where all boxes in the array
+ *          are clones) is inserted into the pixa.
+ *      (4) If the input is valid, this always returns a boxa and a pixa.
+ *          If pixs is empty, the boxa and pixa will be empty.
+ * 
+ */ +BOXA * +pixConnCompPixa(PIX *pixs, + PIXA **ppixa, + l_int32 connectivity) +{ +l_int32 h, iszero; +l_int32 x, y, xstart, ystart; +PIX *pix1, *pix2, *pix3, *pix4; +PIXA *pixa; +BOX *box; +BOXA *boxa; +L_STACK *stack, *auxstack; + + PROCNAME("pixConnCompPixa"); + + if (!ppixa) + return (BOXA *)ERROR_PTR("&pixa not defined", procName, NULL); + *ppixa = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + pix1 = pix2 = pix3 = pix4 = NULL; + stack = NULL; + pixa = pixaCreate(0); + boxa = NULL; + *ppixa = pixa; + pixZero(pixs, &iszero); + if (iszero) + return boxaCreate(1); /* return empty boxa and empty pixa */ + + pixSetPadBits(pixs, 0); + pix1 = pixCopy(NULL, pixs); + pix2 = pixCopy(NULL, pixs); + if (!pix1 || !pix2) { + L_ERROR("pix1 or pix2 not made\n", procName); + pixaDestroy(ppixa); + goto cleanup; + } + + h = pixGetHeight(pixs); + if ((stack = lstackCreate(h)) == NULL) { + L_ERROR("stack not made\n", procName); + pixaDestroy(ppixa); + goto cleanup; + } + auxstack = lstackCreate(0); + stack->auxstack = auxstack; + boxa = boxaCreate(0); + + xstart = 0; + ystart = 0; + while (1) { + if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) + break; + + if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) { + boxaDestroy(&boxa); + pixaDestroy(ppixa); + L_ERROR("box not made\n", procName); + goto cleanup; + } + boxaAddBox(boxa, box, L_INSERT); + + /* Save the c.c. and remove from pix2 as well */ + pix3 = pixClipRectangle(pix1, box, NULL); + pix4 = pixClipRectangle(pix2, box, NULL); + pixXor(pix3, pix3, pix4); + pixRasterop(pix2, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, + pix3, 0, 0); + pixaAddPix(pixa, pix3, L_INSERT); + pixDestroy(&pix4); + + xstart = x; + ystart = y; + } + +#if DEBUG + pixCountPixels(pix1, &iszero, NULL); + fprintf(stderr, "Number of remaining pixels = %d\n", iszero); + lept_mkdir("lept/cc"); + pixWriteDebug("/tmp/lept/cc/remain.png", pix1, IFF_PNG); +#endif /* DEBUG */ + + /* Remove old boxa of pixa and replace with a copy */ + boxaDestroy(&pixa->boxa); + pixa->boxa = boxaCopy(boxa, L_COPY); + *ppixa = pixa; + + /* Cleanup, freeing the fillsegs on each stack */ +cleanup: + lstackDestroy(&stack, TRUE); + pixDestroy(&pix1); + pixDestroy(&pix2); + return boxa; +} + + +/*! + * \brief pixConnCompBB() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *     (1) Finds bounding boxes of 4- or 8-connected components
+ *         in a binary image.
+ *     (2) This works on a copy of the input pix.  The c.c. are located
+ *         in raster order and erased one at a time.  In the process,
+ *         the b.b. is computed and saved.
+ * 
+ */ +BOXA * +pixConnCompBB(PIX *pixs, + l_int32 connectivity) +{ +l_int32 h, iszero; +l_int32 x, y, xstart, ystart; +PIX *pix1; +BOX *box; +BOXA *boxa; +L_STACK *stack, *auxstack; + + PROCNAME("pixConnCompBB"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + boxa = NULL; + pix1 = NULL; + stack = NULL; + pixZero(pixs, &iszero); + if (iszero) + return boxaCreate(1); /* return empty boxa */ + + pixSetPadBits(pixs, 0); + if ((pix1 = pixCopy(NULL, pixs)) == NULL) + return (BOXA *)ERROR_PTR("pix1 not made", procName, NULL); + + h = pixGetHeight(pixs); + if ((stack = lstackCreate(h)) == NULL) { + L_ERROR("stack not made\n", procName); + goto cleanup; + } + auxstack = lstackCreate(0); + stack->auxstack = auxstack; + boxa = boxaCreate(0); + + xstart = 0; + ystart = 0; + while (1) { + if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) + break; + + if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) { + L_ERROR("box not made\n", procName); + boxaDestroy(&boxa); + goto cleanup; + } + boxaAddBox(boxa, box, L_INSERT); + + xstart = x; + ystart = y; + } + +#if DEBUG + pixCountPixels(pix1, &iszero, NULL); + fprintf(stderr, "Number of remaining pixels = %d\n", iszero); + lept_mkdir("lept/cc"); + pixWriteDebug("/tmp/lept/cc/remain.png", pix1, IFF_PNG); +#endif /* DEBUG */ + + /* Cleanup, freeing the fillsegs on each stack */ +cleanup: + lstackDestroy(&stack, TRUE); + pixDestroy(&pix1); + return boxa; +} + + +/*! + * \brief pixCountConnComp() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \param[out] pcount + * \return 0 if OK, 1 on error + * + * Notes: + * (1 This is the top-level call for getting the number of + * 4- or 8-connected components in a 1 bpp image. + * 2 It works on a copy of the input pix. The c.c. are located + * in raster order and erased one at a time. + */ +l_ok +pixCountConnComp(PIX *pixs, + l_int32 connectivity, + l_int32 *pcount) +{ +l_int32 h, iszero; +l_int32 x, y, xstart, ystart; +PIX *pix1; +L_STACK *stack, *auxstack; + + PROCNAME("pixCountConnComp"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; /* initialize the count to 0 */ + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not 4 or 8", procName, 1); + + stack = NULL; + pixZero(pixs, &iszero); + if (iszero) + return 0; + + pixSetPadBits(pixs, 0); + if ((pix1 = pixCopy(NULL, pixs)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + h = pixGetHeight(pixs); + if ((stack = lstackCreate(h)) == NULL) { + pixDestroy(&pix1); + return ERROR_INT("stack not made\n", procName, 1); + } + auxstack = lstackCreate(0); + stack->auxstack = auxstack; + + xstart = 0; + ystart = 0; + while (1) { + if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) + break; + + pixSeedfill(pix1, stack, x, y, connectivity); + (*pcount)++; + xstart = x; + ystart = y; + } + + /* Cleanup, freeing the fillsegs on each stack */ + lstackDestroy(&stack, TRUE); + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief nextOnPixelInRaster() + * + * \param[in] pixs 1 bpp + * \param[in] xstart, ystart starting point for search + * \param[out] px, py coord value of next ON pixel + * \return 1 if a pixel is found; 0 otherwise or on error + */ +l_int32 +nextOnPixelInRaster(PIX *pixs, + l_int32 xstart, + l_int32 ystart, + l_int32 *px, + l_int32 *py) +{ +l_int32 w, h, d, wpl; +l_uint32 *data; + + PROCNAME("nextOnPixelInRaster"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 0); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return ERROR_INT("pixs not 1 bpp", procName, 0); + + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + return nextOnPixelInRasterLow(data, w, h, wpl, xstart, ystart, px, py); +} + + +/*! + * \brief nextOnPixelInRasterLow() + * + * \param[in] data pix data + * \param[in] w, h width and height + * \param[in] wpl words per line + * \param[in] xstart, ystart starting point for search + * \param[out] px, py coord value of next ON pixel + * \return 1 if a pixel is found; 0 otherwise or on error + */ +static l_int32 +nextOnPixelInRasterLow(l_uint32 *data, + l_int32 w, + l_int32 h, + l_int32 wpl, + l_int32 xstart, + l_int32 ystart, + l_int32 *px, + l_int32 *py) +{ +l_int32 i, x, y, xend, startword; +l_uint32 *line, *pword; + + /* Look at the first word */ + line = data + ystart * wpl; + pword = line + (xstart / 32); + if (*pword) { + xend = xstart - (xstart % 32) + 31; + for (x = xstart; x <= xend && x < w; x++) { + if (GET_DATA_BIT(line, x)) { + *px = x; + *py = ystart; + return 1; + } + } + } + + /* Continue with the rest of the line */ + startword = (xstart / 32) + 1; + x = 32 * startword; + for (pword = line + startword; x < w; pword++, x += 32) { + if (*pword) { + for (i = 0; i < 32 && x < w; i++, x++) { + if (GET_DATA_BIT(line, x)) { + *px = x; + *py = ystart; + return 1; + } + } + } + } + + /* Continue with following lines */ + for (y = ystart + 1; y < h; y++) { + line = data + y * wpl; + for (pword = line, x = 0; x < w; pword++, x += 32) { + if (*pword) { + for (i = 0; i < 32 && x < w; i++, x++) { + if (GET_DATA_BIT(line, x)) { + *px = x; + *py = y; + return 1; + } + } + } + } + } + + return 0; +} + + +/*! + * \brief pixSeedfillBB() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \param[in] connectivity 4 or 8 + * \return box or NULL on error + * + *
+ * Notes:
+ *      (1) This is the high-level interface to Paul Heckbert's
+ *          stack-based seedfill algorithm.
+ * 
+ */ +BOX * +pixSeedfillBB(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y, + l_int32 connectivity) +{ +BOX *box; + + PROCNAME("pixSeedfillBB"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!stack) + return (BOX *)ERROR_PTR("stack not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + if (connectivity == 4) { + if ((box = pixSeedfill4BB(pixs, stack, x, y)) == NULL) + return (BOX *)ERROR_PTR("box not made", procName, NULL); + } else if (connectivity == 8) { + if ((box = pixSeedfill8BB(pixs, stack, x, y)) == NULL) + return (BOX *)ERROR_PTR("box not made", procName, NULL); + } else { + return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + } + + return box; +} + + +/*! + * \brief pixSeedfill4BB() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \return box or NULL on error. + * + *
+ * Notes:
+ *      (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm.
+ *      (2) This operates on the input 1 bpp pix to remove the fg seed
+ *          pixel, at (x,y), and all pixels that are 4-connected to it.
+ *          The seed pixel at (x,y) must initially be ON.
+ *      (3) Returns the bounding box of the erased 4-cc component.
+ *      (4) Reference: see Paul Heckbert's stack-based seed fill algorithm
+ *          in "Graphic Gems", ed. Andrew Glassner, Academic
+ *          Press, 1990.  The algorithm description is given
+ *          on pp. 275-277; working C code is on pp. 721-722.)
+ *          The code here follows Heckbert's exactly, except
+ *          we use function calls instead of macros for
+ *          pushing data on and popping data off the stack.
+ *          This makes sense to do because Heckbert's fixed-size
+ *          stack with macros is dangerous: images exist that
+ *          will overrun the stack and crash.   The stack utility
+ *          here grows dynamically as needed, and the fillseg
+ *          structures that are not in use are stored in another
+ *          stack for reuse.  It should be noted that the
+ *          overhead in the function calls (vs. macros) is negligible.
+ * 
+ */ +BOX * +pixSeedfill4BB(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, xstart, wpl, x1, x2, dy; +l_int32 xmax, ymax; +l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ +l_uint32 *data, *line; +BOX *box; + + PROCNAME("pixSeedfill4BB"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!stack) + return (BOX *)ERROR_PTR("stack not defined", procName, NULL); + if (!stack->auxstack) + stack->auxstack = lstackCreate(0); + + pixGetDimensions(pixs, &w, &h, NULL); + xmax = w - 1; + ymax = h - 1; + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + line = data + y * wpl; + + /* Check pix value of seed; must be within the image and ON */ + if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) + return NULL; + + /* Init stack to seed: + * Must first init b.b. values to prevent valgrind from complaining; + * then init b.b. boundaries correctly to seed. */ + minx = miny = 100000; + maxx = maxy = 0; + pushFillsegBB(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); + pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); + minx = maxx = x; + miny = maxy = y; + + while (lstackGetCount(stack) > 0) { + /* Pop segment off stack and fill a neighboring scan line */ + popFillseg(stack, &x1, &x2, &y, &dy); + line = data + y * wpl; + + /* A segment of scanline y - dy for x1 <= x <= x2 was + * previously filled. We now explore adjacent pixels + * in scan line y. There are three regions: to the + * left of x1 - 1, between x1 and x2, and to the right of x2. + * These regions are handled differently. Leaks are + * possible expansions beyond the previous segment and + * going back in the -dy direction. These can happen + * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments + * are plugged with a push in the -dy (opposite) direction. + * And any segments found anywhere are always extended + * in the +dy direction. */ + for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) + CLEAR_DATA_BIT(line,x); + if (x >= x1) /* pix at x1 was off and was not cleared */ + goto skip; + xstart = x + 1; + if (xstart < x1 - 1) /* leak on left? */ + pushFillsegBB(stack, xstart, x1 - 1, y, -dy, + ymax, &minx, &maxx, &miny, &maxy); + + x = x1 + 1; + do { + for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) + CLEAR_DATA_BIT(line, x); + pushFillsegBB(stack, xstart, x - 1, y, dy, + ymax, &minx, &maxx, &miny, &maxy); + if (x > x2 + 1) /* leak on right? */ + pushFillsegBB(stack, x2 + 1, x - 1, y, -dy, + ymax, &minx, &maxx, &miny, &maxy); + skip: for (x++; x <= x2 && + x <= xmax && + (GET_DATA_BIT(line, x) == 0); x++) + ; + xstart = x; + } while (x <= x2 && x <= xmax); + } + + if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) + == NULL) + return (BOX *)ERROR_PTR("box not made", procName, NULL); + return box; +} + + +/*! + * \brief pixSeedfill8BB() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \return box or NULL on error. + * + *
+ * Notes:
+ *      (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm.
+ *      (2) This operates on the input 1 bpp pix to remove the fg seed
+ *          pixel, at (x,y), and all pixels that are 8-connected to it.
+ *          The seed pixel at (x,y) must initially be ON.
+ *      (3) Returns the bounding box of the erased 8-cc component.
+ *      (4) Reference: see Paul Heckbert's stack-based seed fill algorithm
+ *          in "Graphic Gems", ed. Andrew Glassner, Academic
+ *          Press, 1990.  The algorithm description is given
+ *          on pp. 275-277; working C code is on pp. 721-722.)
+ *          The code here follows Heckbert's closely, except
+ *          the leak checks are changed for 8 connectivity.
+ *          See comments on pixSeedfill4BB() for more details.
+ * 
+ */ +BOX * +pixSeedfill8BB(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, xstart, wpl, x1, x2, dy; +l_int32 xmax, ymax; +l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ +l_uint32 *data, *line; +BOX *box; + + PROCNAME("pixSeedfill8BB"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!stack) + return (BOX *)ERROR_PTR("stack not defined", procName, NULL); + if (!stack->auxstack) + stack->auxstack = lstackCreate(0); + + pixGetDimensions(pixs, &w, &h, NULL); + xmax = w - 1; + ymax = h - 1; + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + line = data + y * wpl; + + /* Check pix value of seed; must be ON */ + if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) + return NULL; + + /* Init stack to seed: + * Must first init b.b. values to prevent valgrind from complaining; + * then init b.b. boundaries correctly to seed. */ + minx = miny = 100000; + maxx = maxy = 0; + pushFillsegBB(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); + pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); + minx = maxx = x; + miny = maxy = y; + + while (lstackGetCount(stack) > 0) { + /* Pop segment off stack and fill a neighboring scan line */ + popFillseg(stack, &x1, &x2, &y, &dy); + line = data + y * wpl; + + /* A segment of scanline y - dy for x1 <= x <= x2 was + * previously filled. We now explore adjacent pixels + * in scan line y. There are three regions: to the + * left of x1, between x1 and x2, and to the right of x2. + * These regions are handled differently. Leaks are + * possible expansions beyond the previous segment and + * going back in the -dy direction. These can happen + * for x < x1 and for x > x2. Any "leak" segments + * are plugged with a push in the -dy (opposite) direction. + * And any segments found anywhere are always extended + * in the +dy direction. */ + for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) + CLEAR_DATA_BIT(line,x); + if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ + goto skip; + xstart = x + 1; + if (xstart < x1) /* leak on left? */ + pushFillsegBB(stack, xstart, x1 - 1, y, -dy, + ymax, &minx, &maxx, &miny, &maxy); + + x = x1; + do { + for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) + CLEAR_DATA_BIT(line, x); + pushFillsegBB(stack, xstart, x - 1, y, dy, + ymax, &minx, &maxx, &miny, &maxy); + if (x > x2) /* leak on right? */ + pushFillsegBB(stack, x2 + 1, x - 1, y, -dy, + ymax, &minx, &maxx, &miny, &maxy); + skip: for (x++; x <= x2 + 1 && + x <= xmax && + (GET_DATA_BIT(line, x) == 0); x++) + ; + xstart = x; + } while (x <= x2 + 1 && x <= xmax); + } + + if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) + == NULL) + return (BOX *)ERROR_PTR("box not made", procName, NULL); + return box; +} + + +/*! + * \brief pixSeedfill() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \param[in] connectivity 4 or 8 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes the component from pixs with a fg pixel at (x,y).
+ *      (2) See pixSeedfill4() and pixSeedfill8() for details.
+ * 
+ */ +l_ok +pixSeedfill(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y, + l_int32 connectivity) +{ +l_int32 retval; + + PROCNAME("pixSeedfill"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!stack) + return ERROR_INT("stack not defined", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not 4 or 8", procName, 1); + + if (connectivity == 4) + retval = pixSeedfill4(pixs, stack, x, y); + else /* connectivity == 8 */ + retval = pixSeedfill8(pixs, stack, x, y); + + return retval; +} + + +/*! + * \brief pixSeedfill4() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm.
+ *      (2) This operates on the input 1 bpp pix to remove the fg seed
+ *          pixel, at (x,y), and all pixels that are 4-connected to it.
+ *          The seed pixel at (x,y) must initially be ON.
+ *      (3) Reference: see pixSeedFill4BB()
+ * 
+ */ +l_ok +pixSeedfill4(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, xstart, wpl, x1, x2, dy; +l_int32 xmax, ymax; +l_uint32 *data, *line; + + PROCNAME("pixSeedfill4"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!stack) + return ERROR_INT("stack not defined", procName, 1); + if (!stack->auxstack) + stack->auxstack = lstackCreate(0); + + pixGetDimensions(pixs, &w, &h, NULL); + xmax = w - 1; + ymax = h - 1; + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + line = data + y * wpl; + + /* Check pix value of seed; must be within the image and ON */ + if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) + return 0; + + /* Init stack to seed */ + pushFillseg(stack, x, x, y, 1, ymax); + pushFillseg(stack, x, x, y + 1, -1, ymax); + + while (lstackGetCount(stack) > 0) { + /* Pop segment off stack and fill a neighboring scan line */ + popFillseg(stack, &x1, &x2, &y, &dy); + line = data + y * wpl; + + /* A segment of scanline y - dy for x1 <= x <= x2 was + * previously filled. We now explore adjacent pixels + * in scan line y. There are three regions: to the + * left of x1 - 1, between x1 and x2, and to the right of x2. + * These regions are handled differently. Leaks are + * possible expansions beyond the previous segment and + * going back in the -dy direction. These can happen + * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments + * are plugged with a push in the -dy (opposite) direction. + * And any segments found anywhere are always extended + * in the +dy direction. */ + for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) + CLEAR_DATA_BIT(line,x); + if (x >= x1) /* pix at x1 was off and was not cleared */ + goto skip; + xstart = x + 1; + if (xstart < x1 - 1) /* leak on left? */ + pushFillseg(stack, xstart, x1 - 1, y, -dy, ymax); + + x = x1 + 1; + do { + for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) + CLEAR_DATA_BIT(line, x); + pushFillseg(stack, xstart, x - 1, y, dy, ymax); + if (x > x2 + 1) /* leak on right? */ + pushFillseg(stack, x2 + 1, x - 1, y, -dy, ymax); + skip: for (x++; x <= x2 && + x <= xmax && + (GET_DATA_BIT(line, x) == 0); x++) + ; + xstart = x; + } while (x <= x2 && x <= xmax); + } + + return 0; +} + + +/*! + * \brief pixSeedfill8() + * + * \param[in] pixs 1 bpp + * \param[in] stack for holding fillsegs + * \param[in] x,y location of seed pixel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm.
+ *      (2) This operates on the input 1 bpp pix to remove the fg seed
+ *          pixel, at (x,y), and all pixels that are 8-connected to it.
+ *          The seed pixel at (x,y) must initially be ON.
+ *      (3) Reference: see pixSeedFill8BB()
+ * 
+ */ +l_ok +pixSeedfill8(PIX *pixs, + L_STACK *stack, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, xstart, wpl, x1, x2, dy; +l_int32 xmax, ymax; +l_uint32 *data, *line; + + PROCNAME("pixSeedfill8"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!stack) + return ERROR_INT("stack not defined", procName, 1); + if (!stack->auxstack) + stack->auxstack = lstackCreate(0); + + pixGetDimensions(pixs, &w, &h, NULL); + xmax = w - 1; + ymax = h - 1; + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + line = data + y * wpl; + + /* Check pix value of seed; must be ON */ + if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) + return 0; + + /* Init stack to seed */ + pushFillseg(stack, x, x, y, 1, ymax); + pushFillseg(stack, x, x, y + 1, -1, ymax); + + while (lstackGetCount(stack) > 0) { + /* Pop segment off stack and fill a neighboring scan line */ + popFillseg(stack, &x1, &x2, &y, &dy); + line = data + y * wpl; + + /* A segment of scanline y - dy for x1 <= x <= x2 was + * previously filled. We now explore adjacent pixels + * in scan line y. There are three regions: to the + * left of x1, between x1 and x2, and to the right of x2. + * These regions are handled differently. Leaks are + * possible expansions beyond the previous segment and + * going back in the -dy direction. These can happen + * for x < x1 and for x > x2. Any "leak" segments + * are plugged with a push in the -dy (opposite) direction. + * And any segments found anywhere are always extended + * in the +dy direction. */ + for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) + CLEAR_DATA_BIT(line,x); + if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ + goto skip; + xstart = x + 1; + if (xstart < x1) /* leak on left? */ + pushFillseg(stack, xstart, x1 - 1, y, -dy, ymax); + + x = x1; + do { + for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) + CLEAR_DATA_BIT(line, x); + pushFillseg(stack, xstart, x - 1, y, dy, ymax); + if (x > x2) /* leak on right? */ + pushFillseg(stack, x2 + 1, x - 1, y, -dy, ymax); + skip: for (x++; x <= x2 + 1 && + x <= xmax && + (GET_DATA_BIT(line, x) == 0); x++) + ; + xstart = x; + } while (x <= x2 + 1 && x <= xmax); + } + + return 0; +} + + + +/*-----------------------------------------------------------------------* + * Static stack helper functions: push and pop fillsegs * + *-----------------------------------------------------------------------*/ +/*! + * \brief pushFillsegBB() + * + * \param[in] stack + * \param[in] xleft, xright + * \param[in] y + * \param[in] dy + * \param[in] ymax + * \param[out] pminx minimum x + * \param[out] pmaxx maximum x + * \param[out] pminy minimum y + * \param[out] pmaxy maximum y + * \return void + * + *
+ * Notes:
+ *      (1) This adds a line segment to the stack, and returns its size.
+ *      (2) The auxiliary stack is used as a storage area to recycle
+ *          fillsegs that are no longer in use.  We only calloc new
+ *          fillsegs if the auxiliary stack is empty.
+ * 
+ */ +static void +pushFillsegBB(L_STACK *stack, + l_int32 xleft, + l_int32 xright, + l_int32 y, + l_int32 dy, + l_int32 ymax, + l_int32 *pminx, + l_int32 *pmaxx, + l_int32 *pminy, + l_int32 *pmaxy) +{ +FILLSEG *fseg; +L_STACK *auxstack; + + PROCNAME("pushFillsegBB"); + + if (!stack) { + L_ERROR("stack not defined\n", procName); + return; + } + + *pminx = L_MIN(*pminx, xleft); + *pmaxx = L_MAX(*pmaxx, xright); + *pminy = L_MIN(*pminy, y); + *pmaxy = L_MAX(*pmaxy, y); + + if (y + dy >= 0 && y + dy <= ymax) { + if ((auxstack = stack->auxstack) == NULL) { + L_ERROR("auxstack not defined\n", procName); + return; + } + + /* Get a fillseg to use */ + if (lstackGetCount(auxstack) > 0) + fseg = (FILLSEG *)lstackRemove(auxstack); + else + fseg = (FILLSEG *)LEPT_CALLOC(1, sizeof(FILLSEG)); + fseg->xleft = xleft; + fseg->xright = xright; + fseg->y = y; + fseg->dy = dy; + lstackAdd(stack, fseg); + } + return; +} + + +/*! + * \brief pushFillseg() + * + * \param[in] stack + * \param[in] xleft, xright + * \param[in] y + * \param[in] dy + * \param[in] ymax + * \return void + * + *
+ * Notes:
+ *      (1) This adds a line segment to the stack.
+ *      (2) The auxiliary stack is used as a storage area to recycle
+ *          fillsegs that are no longer in use.  We only calloc new
+ *          fillsegs if the auxiliary stack is empty.
+ * 
+ */ +static void +pushFillseg(L_STACK *stack, + l_int32 xleft, + l_int32 xright, + l_int32 y, + l_int32 dy, + l_int32 ymax) +{ +FILLSEG *fseg; +L_STACK *auxstack; + + PROCNAME("pushFillseg"); + + if (!stack) { + L_ERROR("stack not defined\n", procName); + return; + } + + if (y + dy >= 0 && y + dy <= ymax) { + if ((auxstack = stack->auxstack) == NULL) { + L_ERROR("auxstack not defined\n", procName); + return; + } + + /* Get a fillseg to use */ + if (lstackGetCount(auxstack) > 0) + fseg = (FILLSEG *)lstackRemove(auxstack); + else + fseg = (FILLSEG *)LEPT_CALLOC(1, sizeof(FILLSEG)); + fseg->xleft = xleft; + fseg->xright = xright; + fseg->y = y; + fseg->dy = dy; + lstackAdd(stack, fseg); + } + return; +} + + +/*! + * \brief popFillseg() + * + * \param[in] stack + * \param[out] pxleft left x + * \param[out] pxright right x + * \param[out] py y coordinate + * \param[out] pdy delta y + * \return void + * + *
+ * Notes:
+ *      (1) This removes a line segment from the stack, and returns its size.
+ *      (2) The surplussed fillseg is placed on the auxiliary stack
+ *          for future use.
+ * 
+ */ +static void +popFillseg(L_STACK *stack, + l_int32 *pxleft, + l_int32 *pxright, + l_int32 *py, + l_int32 *pdy) +{ +FILLSEG *fseg; +L_STACK *auxstack; + + PROCNAME("popFillseg"); + + if (!stack) { + L_ERROR("stack not defined\n", procName); + return; + } + if ((auxstack = stack->auxstack) == NULL) { + L_ERROR("auxstack not defined\n", procName); + return; + } + + if ((fseg = (FILLSEG *)lstackRemove(stack)) == NULL) + return; + + *pxleft = fseg->xleft; + *pxright = fseg->xright; + *py = fseg->y + fseg->dy; /* this now points to the new line */ + *pdy = fseg->dy; + + /* Save it for re-use */ + lstackAdd(auxstack, fseg); + return; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/convertfiles.c b/hgdriver/3rdparty/hgOCR/leptonica/convertfiles.c new file mode 100644 index 0000000..186ce87 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/convertfiles.c @@ -0,0 +1,146 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file convertfiles.c + *
+ *
+ *      Conversion to 1 bpp
+ *          l_int32    convertFilesTo1bpp()
+ *
+ *  These are utility functions that will perform depth conversion
+ *  on selected files, writing the results to a specified directory.
+ *  We start with conversion to 1 bpp.
+ * 
+ */ + +#include +#include "allheaders.h" + + +/*------------------------------------------------------------------* + * Conversion to 1 bpp * + *------------------------------------------------------------------*/ +/*! + * \brief convertFilesTo1bpp() + * + * \param[in] dirin + * \param[in] substr [optional] substring filter on filenames; + 8 can be NULL + * \param[in] upscaling 1, 2 or 4; only for input color or grayscale + * \param[in] thresh global threshold for binarization; 0 for default + * \param[in] firstpage + * \param[in] npages use 0 to do all from %firstpage to the end + * \param[in] dirout + * \param[in] outformat IFF_PNG, IFF_TIFF_G4 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Images are sorted lexicographically, and the names in the
+ *          output directory are retained except for the extension.
+ * 
+ */ +l_ok +convertFilesTo1bpp(const char *dirin, + const char *substr, + l_int32 upscaling, + l_int32 thresh, + l_int32 firstpage, + l_int32 npages, + const char *dirout, + l_int32 outformat) +{ +l_int32 i, nfiles; +char buf[512]; +char *fname, *tail, *basename; +PIX *pixs, *pixg1, *pixg2, *pixb; +SARRAY *safiles; + + PROCNAME("convertFilesTo1bpp"); + + if (!dirin) + return ERROR_INT("dirin", procName, 1); + if (!dirout) + return ERROR_INT("dirout", procName, 1); + if (upscaling != 1 && upscaling != 2 && upscaling != 4) + return ERROR_INT("invalid upscaling factor", procName, 1); + if (thresh <= 0) thresh = 180; + if (firstpage < 0) firstpage = 0; + if (npages < 0) npages = 0; + if (outformat != IFF_TIFF_G4) + outformat = IFF_PNG; + + safiles = getSortedPathnamesInDirectory(dirin, substr, firstpage, npages); + if (!safiles) + return ERROR_INT("safiles not made", procName, 1); + if ((nfiles = sarrayGetCount(safiles)) == 0) { + sarrayDestroy(&safiles); + return ERROR_INT("no matching files in the directory", procName, 1); + } + + for (i = 0; i < nfiles; i++) { + fname = sarrayGetString(safiles, i, L_NOCOPY); + if ((pixs = pixRead(fname)) == NULL) { + L_WARNING("Couldn't read file %s\n", procName, fname); + continue; + } + if (pixGetDepth(pixs) == 32) + pixg1 = pixConvertRGBToLuminance(pixs); + else + pixg1 = pixClone(pixs); + pixg2 = pixRemoveColormap(pixg1, REMOVE_CMAP_TO_GRAYSCALE); + if (pixGetDepth(pixg2) == 1) { + pixb = pixClone(pixg2); + } else { + if (upscaling == 1) + pixb = pixThresholdToBinary(pixg2, thresh); + else if (upscaling == 2) + pixb = pixScaleGray2xLIThresh(pixg2, thresh); + else /* upscaling == 4 */ + pixb = pixScaleGray4xLIThresh(pixg2, thresh); + } + pixDestroy(&pixs); + pixDestroy(&pixg1); + pixDestroy(&pixg2); + + splitPathAtDirectory(fname, NULL, &tail); + splitPathAtExtension(tail, &basename, NULL); + if (outformat == IFF_TIFF_G4) { + snprintf(buf, sizeof(buf), "%s/%s.tif", dirout, basename); + pixWrite(buf, pixb, IFF_TIFF_G4); + } else { + snprintf(buf, sizeof(buf), "%s/%s.png", dirout, basename); + pixWrite(buf, pixb, IFF_PNG); + } + pixDestroy(&pixb); + LEPT_FREE(tail); + LEPT_FREE(basename); + } + + sarrayDestroy(&safiles); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/convolve.c b/hgdriver/3rdparty/hgOCR/leptonica/convolve.c new file mode 100644 index 0000000..0abae1e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/convolve.c @@ -0,0 +1,2576 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file convolve.c + *
+ *
+ *      Top level grayscale or color block convolution
+ *          PIX          *pixBlockconv()
+ *
+ *      Grayscale block convolution
+ *          PIX          *pixBlockconvGray()
+ *          static void   blockconvLow()
+ *
+ *      Accumulator for 1, 8 and 32 bpp convolution
+ *          PIX          *pixBlockconvAccum()
+ *          static void   blockconvAccumLow()
+ *
+ *      Un-normalized grayscale block convolution
+ *          PIX          *pixBlockconvGrayUnnormalized()
+ *
+ *      Tiled grayscale or color block convolution
+ *          PIX          *pixBlockconvTiled()
+ *          PIX          *pixBlockconvGrayTile()
+ *
+ *      Convolution for mean, mean square, variance and rms deviation
+ *      in specified window
+ *          l_int32       pixWindowedStats()
+ *          PIX          *pixWindowedMean()
+ *          PIX          *pixWindowedMeanSquare()
+ *          l_int32       pixWindowedVariance()
+ *          DPIX         *pixMeanSquareAccum()
+ *
+ *      Binary block sum and rank filter
+ *          PIX          *pixBlockrank()
+ *          PIX          *pixBlocksum()
+ *          static void   blocksumLow()
+ *
+ *      Census transform
+ *          PIX          *pixCensusTransform()
+ *
+ *      Generic convolution (with Pix)
+ *          PIX          *pixConvolve()
+ *          PIX          *pixConvolveSep()
+ *          PIX          *pixConvolveRGB()
+ *          PIX          *pixConvolveRGBSep()
+ *
+ *      Generic convolution (with float arrays)
+ *          FPIX         *fpixConvolve()
+ *          FPIX         *fpixConvolveSep()
+ *
+ *      Convolution with bias (for non-negative output)
+ *          PIX          *pixConvolveWithBias()
+ *
+ *      Set parameter for convolution subsampling
+ *          void          l_setConvolveSampling()
+ *
+ *      Additive gaussian noise
+ *          PIX          *pixAddGaussNoise()
+ *          l_float32     gaussDistribSampling()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* These globals determine the subsampling factors for + * generic convolution of pix and fpix. Declare extern to use. + * To change the values, use l_setConvolveSampling(). */ +LEPT_DLL l_int32 ConvolveSamplingFactX = 1; +LEPT_DLL l_int32 ConvolveSamplingFactY = 1; + + /* Low-level static functions */ +static void blockconvLow(l_uint32 *data, l_int32 w, l_int32 h, l_int32 wpl, + l_uint32 *dataa, l_int32 wpla, l_int32 wc, + l_int32 hc); +static void blockconvAccumLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 d, + l_int32 wpls); +static void blocksumLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpl, + l_uint32 *dataa, l_int32 wpla, l_int32 wc, l_int32 hc); + + +/*----------------------------------------------------------------------* + * Top-level grayscale or color block convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockconv() + * + * \param[in] pix 8 or 32 bpp; or 2, 4 or 8 bpp with colormap + * \param[in] wc, hc half width/height of convolution kernel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1)
+ *      (2) Returns a copy if both wc and hc are 0
+ *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ * 
+ */ +PIX * +pixBlockconv(PIX *pix, + l_int32 wc, + l_int32 hc) +{ +l_int32 w, h, d; +PIX *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; + + PROCNAME("pixBlockconv"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + pixGetDimensions(pix, &w, &h, &d); + if (w < 2 * wc + 1 || h < 2 * hc + 1) { + wc = L_MIN(wc, (w - 1) / 2); + hc = L_MIN(hc, (h - 1) / 2); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) /* no-op */ + return pixCopy(NULL, pix); + + /* Remove colormap if necessary */ + if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { + L_WARNING("pix has colormap; removing\n", procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixs); + } else { + pixs = pixClone(pix); + } + + if (d != 8 && d != 32) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); + } + + if (d == 8) { + pixd = pixBlockconvGray(pixs, NULL, wc, hc); + } else { /* d == 32 */ + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixrc = pixBlockconvGray(pixr, NULL, wc, hc); + pixDestroy(&pixr); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixgc = pixBlockconvGray(pixg, NULL, wc, hc); + pixDestroy(&pixg); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + pixbc = pixBlockconvGray(pixb, NULL, wc, hc); + pixDestroy(&pixb); + pixd = pixCreateRGBImage(pixrc, pixgc, pixbc); + pixDestroy(&pixrc); + pixDestroy(&pixgc); + pixDestroy(&pixbc); + } + + pixDestroy(&pixs); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Grayscale block convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockconvGray() + * + * \param[in] pixs 8 bpp + * \param[in] pixacc pix 32 bpp; can be null + * \param[in] wc, hc half width/height of convolution kernel + * \return pix 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If accum pix is null, make one and destroy it before
+ *          returning; otherwise, just use the input accum pix.
+ *      (2) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1).
+ *      (3) Returns a copy if both wc and hc are 0.
+ *      (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ * 
+ */ +PIX * +pixBlockconvGray(PIX *pixs, + PIX *pixacc, + l_int32 wc, + l_int32 hc) +{ +l_int32 w, h, d, wpl, wpla; +l_uint32 *datad, *dataa; +PIX *pixd, *pixt; + + PROCNAME("pixBlockconvGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + if (w < 2 * wc + 1 || h < 2 * hc + 1) { + wc = L_MIN(wc, (w - 1) / 2); + hc = L_MIN(hc, (h - 1) / 2); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) /* no-op */ + return pixCopy(NULL, pixs); + + if (pixacc) { + if (pixGetDepth(pixacc) == 32) { + pixt = pixClone(pixacc); + } else { + L_WARNING("pixacc not 32 bpp; making new one\n", procName); + if ((pixt = pixBlockconvAccum(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + } else { + if ((pixt = pixBlockconvAccum(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + + if ((pixd = pixCreateTemplate(pixs)) == NULL) { + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + wpl = pixGetWpl(pixs); + wpla = pixGetWpl(pixt); + datad = pixGetData(pixd); + dataa = pixGetData(pixt); + blockconvLow(datad, w, h, wpl, dataa, wpla, wc, hc); + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief blockconvLow() + * + * \param[in] data data of input image, to be convolved + * \param[in] w, h, wpl + * \param[in] dataa data of 32 bpp accumulator + * \param[in] wpla accumulator + * \param[in] wc convolution "half-width" + * \param[in] hc convolution "half-height" + * \return void + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1).
+ *      (2) The lack of symmetry between the handling of the
+ *          first (hc + 1) lines and the last (hc) lines,
+ *          and similarly with the columns, is due to fact that
+ *          for the pixel at (x,y), the accumulator values are
+ *          taken at (x + wc, y + hc), (x - wc - 1, y + hc),
+ *          (x + wc, y - hc - 1) and (x - wc - 1, y - hc - 1).
+ *      (3) We compute sums, normalized as if there were no reduced
+ *          area at the boundary.  This under-estimates the value
+ *          of the boundary pixels, so we multiply them by another
+ *          normalization factor that is greater than 1.
+ *      (4) This second normalization is done first for the first
+ *          hc + 1 lines; then for the last hc lines; and finally
+ *          for the first wc + 1 and last wc columns in the intermediate
+ *          lines.
+ *      (5) The caller should verify that wc < w and hc < h.
+ *          Under those conditions, illegal reads and writes can occur.
+ *      (6) Implementation note: to get the same results in the interior
+ *          between this function and pixConvolve(), it is necessary to
+ *          add 0.5 for roundoff in the main loop that runs over all pixels.
+ *          However, if we do that and have white (255) pixels near the
+ *          image boundary, some overflow occurs for pixels very close
+ *          to the boundary.  We can't fix this by subtracting from the
+ *          normalized values for the boundary pixels, because this results
+ *          in underflow if the boundary pixels are black (0).  Empirically,
+ *          adding 0.25 (instead of 0.5) before truncating in the main
+ *          loop will not cause overflow, but this gives some
+ *          off-by-1-level errors in interior pixel values.  So we add
+ *          0.5 for roundoff in the main loop, and for pixels within a
+ *          half filter width of the boundary, use a L_MIN of the
+ *          computed value and 255 to avoid overflow during normalization.
+ * 
+ */ +static void +blockconvLow(l_uint32 *data, + l_int32 w, + l_int32 h, + l_int32 wpl, + l_uint32 *dataa, + l_int32 wpla, + l_int32 wc, + l_int32 hc) +{ +l_int32 i, j, imax, imin, jmax, jmin; +l_int32 wn, hn, fwc, fhc, wmwc, hmhc; +l_float32 norm, normh, normw; +l_uint32 val; +l_uint32 *linemina, *linemaxa, *line; + + PROCNAME("blockconvLow"); + + wmwc = w - wc; + hmhc = h - hc; + if (wmwc <= 0 || hmhc <= 0) { + L_ERROR("wc >= w || hc >=h\n", procName); + return; + } + fwc = 2 * wc + 1; + fhc = 2 * hc + 1; + norm = 1.0 / ((l_float32)(fwc) * fhc); + + /*------------------------------------------------------------* + * Compute, using b.c. only to set limits on the accum image * + *------------------------------------------------------------*/ + for (i = 0; i < h; i++) { + imin = L_MAX(i - 1 - hc, 0); + imax = L_MIN(i + hc, h - 1); + line = data + wpl * i; + linemina = dataa + wpla * imin; + linemaxa = dataa + wpla * imax; + for (j = 0; j < w; j++) { + jmin = L_MAX(j - 1 - wc, 0); + jmax = L_MIN(j + wc, w - 1); + val = linemaxa[jmax] - linemaxa[jmin] + + linemina[jmin] - linemina[jmax]; + val = (l_uint8)(norm * val + 0.5); /* see comment above */ + SET_DATA_BYTE(line, j, val); + } + } + + /*------------------------------------------------------------* + * Fix normalization for boundary pixels * + *------------------------------------------------------------*/ + for (i = 0; i <= hc; i++) { /* first hc + 1 lines */ + hn = hc + i; + normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ + line = data + wpl * i; + for (j = 0; j <= wc; j++) { + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh * normw, 255); + SET_DATA_BYTE(line, j, val); + } + for (j = wc + 1; j < wmwc; j++) { + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh, 255); + SET_DATA_BYTE(line, j, val); + } + for (j = wmwc; j < w; j++) { + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh * normw, 255); + SET_DATA_BYTE(line, j, val); + } + } + + for (i = hmhc; i < h; i++) { /* last hc lines */ + hn = hc + h - i; + normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ + line = data + wpl * i; + for (j = 0; j <= wc; j++) { + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh * normw, 255); + SET_DATA_BYTE(line, j, val); + } + for (j = wc + 1; j < wmwc; j++) { + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh, 255); + SET_DATA_BYTE(line, j, val); + } + for (j = wmwc; j < w; j++) { + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normh * normw, 255); + SET_DATA_BYTE(line, j, val); + } + } + + for (i = hc + 1; i < hmhc; i++) { /* intermediate lines */ + line = data + wpl * i; + for (j = 0; j <= wc; j++) { /* first wc + 1 columns */ + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normw, 255); + SET_DATA_BYTE(line, j, val); + } + for (j = wmwc; j < w; j++) { /* last wc columns */ + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(line, j); + val = (l_uint8)L_MIN(val * normw, 255); + SET_DATA_BYTE(line, j, val); + } + } + + return; +} + + +/*----------------------------------------------------------------------* + * Accumulator for 1, 8 and 32 bpp convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockconvAccum() + * + * \param[in] pixs 1, 8 or 32 bpp + * \return accum pix 32 bpp, or NULL on error. + * + *
+ * Notes:
+ *      (1) The general recursion relation is
+ *            a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1)
+ *          For the first line, this reduces to the special case
+ *            a(i,j) = v(i,j) + a(i, j-1)
+ *          For the first column, the special case is
+ *            a(i,j) = v(i,j) + a(i-1, j)
+ * 
+ */ +PIX * +pixBlockconvAccum(PIX *pixs) +{ +l_int32 w, h, d, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixBlockconvAccum"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); + if ((pixd = pixCreate(w, h, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + blockconvAccumLow(datad, w, h, wpld, datas, d, wpls); + + return pixd; +} + + +/* + * \brief blockconvAccumLow() + * + * \param[in] datad 32 bpp dest + * \param[in] w, h, wpld of 32 bpp dest + * \param[in] datas 1, 8 or 32 bpp src + * \param[in] d bpp of src + * \param[in] wpls of src + * \return void + * + *
+ * Notes:
+ *      (1) The general recursion relation is
+ *             a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1)
+ *          For the first line, this reduces to the special case
+ *             a(0,j) = v(0,j) + a(0, j-1), j > 0
+ *          For the first column, the special case is
+ *             a(i,0) = v(i,0) + a(i-1, 0), i > 0
+ * 
+ */ +static void +blockconvAccumLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 d, + l_int32 wpls) +{ +l_uint8 val; +l_int32 i, j; +l_uint32 val32; +l_uint32 *lines, *lined, *linedp; + + PROCNAME("blockconvAccumLow"); + + lines = datas; + lined = datad; + + if (d == 1) { + /* Do the first line */ + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(lines, j); + if (j == 0) + lined[0] = val; + else + lined[j] = lined[j - 1] + val; + } + + /* Do the other lines */ + for (i = 1; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; /* curr dest line */ + linedp = lined - wpld; /* prev dest line */ + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(lines, j); + if (j == 0) + lined[0] = val + linedp[0]; + else + lined[j] = val + lined[j - 1] + linedp[j] - linedp[j - 1]; + } + } + } else if (d == 8) { + /* Do the first line */ + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + if (j == 0) + lined[0] = val; + else + lined[j] = lined[j - 1] + val; + } + + /* Do the other lines */ + for (i = 1; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; /* curr dest line */ + linedp = lined - wpld; /* prev dest line */ + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + if (j == 0) + lined[0] = val + linedp[0]; + else + lined[j] = val + lined[j - 1] + linedp[j] - linedp[j - 1]; + } + } + } else if (d == 32) { + /* Do the first line */ + for (j = 0; j < w; j++) { + val32 = lines[j]; + if (j == 0) + lined[0] = val32; + else + lined[j] = lined[j - 1] + val32; + } + + /* Do the other lines */ + for (i = 1; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; /* curr dest line */ + linedp = lined - wpld; /* prev dest line */ + for (j = 0; j < w; j++) { + val32 = lines[j]; + if (j == 0) + lined[0] = val32 + linedp[0]; + else + lined[j] = val32 + lined[j - 1] + linedp[j] - linedp[j - 1]; + } + } + } else { + L_ERROR("depth not 1, 8 or 32 bpp\n", procName); + } + + return; +} + + +/*----------------------------------------------------------------------* + * Un-normalized grayscale block convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockconvGrayUnnormalized() + * + * \param[in] pixs 8 bpp + * \param[in] wc, hc half width/height of convolution kernel + * \return pix 32 bpp; containing the convolution without normalizing + * for the window size, or NULL on error + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1).
+ *      (2) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ *      (3) Returns a copy if both wc and hc are 0.
+ *      (3) Adds mirrored border to avoid treating the boundary pixels
+ *          specially.  Note that we add wc + 1 pixels to the left
+ *          and wc to the right.  The added width is 2 * wc + 1 pixels,
+ *          and the particular choice simplifies the indexing in the loop.
+ *          Likewise, add hc + 1 pixels to the top and hc to the bottom.
+ *      (4) To get the normalized result, divide by the area of the
+ *          convolution kernel: (2 * wc + 1) * (2 * hc + 1)
+ *          Specifically, do this:
+ *               pixc = pixBlockconvGrayUnnormalized(pixs, wc, hc);
+ *               fract = 1. / ((2 * wc + 1) * (2 * hc + 1));
+ *               pixMultConstantGray(pixc, fract);
+ *               pixd = pixGetRGBComponent(pixc, L_ALPHA_CHANNEL);
+ *      (5) Unlike pixBlockconvGray(), this always computes the accumulation
+ *          pix because its size is tied to wc and hc.
+ *      (6) Compare this implementation with pixBlockconvGray(), where
+ *          most of the code in blockconvLow() is special casing for
+ *          efficiently handling the boundary.  Here, the use of
+ *          mirrored borders and destination indexing makes the
+ *          implementation very simple.
+ * 
+ */ +PIX * +pixBlockconvGrayUnnormalized(PIX *pixs, + l_int32 wc, + l_int32 hc) +{ +l_int32 i, j, w, h, d, wpla, wpld, jmax; +l_uint32 *linemina, *linemaxa, *lined, *dataa, *datad; +PIX *pixsb, *pixacc, *pixd; + + PROCNAME("pixBlockconvGrayUnnormalized"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + if (w < 2 * wc + 1 || h < 2 * hc + 1) { + wc = L_MIN(wc, (w - 1) / 2); + hc = L_MIN(hc, (h - 1) / 2); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) /* no-op */ + return pixCopy(NULL, pixs); + + if ((pixsb = pixAddMirroredBorder(pixs, wc + 1, wc, hc + 1, hc)) == NULL) + return (PIX *)ERROR_PTR("pixsb not made", procName, NULL); + pixacc = pixBlockconvAccum(pixsb); + pixDestroy(&pixsb); + if (!pixacc) + return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); + if ((pixd = pixCreate(w, h, 32)) == NULL) { + pixDestroy(&pixacc); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + wpla = pixGetWpl(pixacc); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + dataa = pixGetData(pixacc); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linemina = dataa + i * wpla; + linemaxa = dataa + (i + 2 * hc + 1) * wpla; + for (j = 0; j < w; j++) { + jmax = j + 2 * wc + 1; + lined[j] = linemaxa[jmax] - linemaxa[j] - + linemina[jmax] + linemina[j]; + } + } + + pixDestroy(&pixacc); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Tiled grayscale or color block convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockconvTiled() + * + * \param[in] pix 8 or 32 bpp; or 2, 4 or 8 bpp with colormap + * \param[in] wc, hc half width/height of convolution kernel + * \param[in] nx, ny subdivision into tiles + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1)
+ *      (2) Returns a copy if both wc and hc are 0
+ *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ *      (4) For nx == ny == 1, this defaults to pixBlockconv(), which
+ *          is typically about twice as fast, and gives nearly
+ *          identical results as pixBlockconvGrayTile().
+ *      (5) If the tiles are too small, nx and/or ny are reduced
+ *          a minimum amount so that the tiles are expanded to the
+ *          smallest workable size in the problematic direction(s).
+ *      (6) Why a tiled version?  Three reasons:
+ *          (a) Because the accumulator is a uint32, overflow can occur
+ *              for an image with more than 16M pixels.
+ *          (b) The accumulator array for 16M pixels is 64 MB; using
+ *              tiles reduces the size of this array.
+ *          (c) Each tile can be processed independently, in parallel,
+ *              on a multicore processor.
+ * 
+ */ +PIX * +pixBlockconvTiled(PIX *pix, + l_int32 wc, + l_int32 hc, + l_int32 nx, + l_int32 ny) +{ +l_int32 i, j, w, h, d, xrat, yrat; +PIX *pixs, *pixd, *pixc, *pixt; +PIX *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; +PIXTILING *pt; + + PROCNAME("pixBlockconvTiled"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + pixGetDimensions(pix, &w, &h, &d); + if (w < 2 * wc + 3 || h < 2 * hc + 3) { + wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); + hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) /* no-op */ + return pixCopy(NULL, pix); + if (nx <= 1 && ny <= 1) + return pixBlockconv(pix, wc, hc); + + /* Test to see if the tiles are too small. The required + * condition is that the tile dimensions must be at least + * (wc + 2) x (hc + 2). */ + xrat = w / nx; + yrat = h / ny; + if (xrat < wc + 2) { + nx = w / (wc + 2); + L_WARNING("tile width too small; nx reduced to %d\n", procName, nx); + } + if (yrat < hc + 2) { + ny = h / (hc + 2); + L_WARNING("tile height too small; ny reduced to %d\n", procName, ny); + } + + /* Remove colormap if necessary */ + if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { + L_WARNING("pix has colormap; removing\n", procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixs); + } else { + pixs = pixClone(pix); + } + + if (d != 8 && d != 32) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); + } + + /* Note that the overlaps in the width and height that + * are added to the tile are (wc + 2) and (hc + 2). + * These overlaps are removed by pixTilingPaintTile(). + * They are larger than the extent of the filter because + * although the filter is symmetric with respect to its origin, + * the implementation is asymmetric -- see the implementation in + * pixBlockconvGrayTile(). */ + if ((pixd = pixCreateTemplate(pixs)) == NULL) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pt = pixTilingCreate(pixs, nx, ny, 0, 0, wc + 2, hc + 2); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + pixt = pixTilingGetTile(pt, i, j); + + /* Convolve over the tile */ + if (d == 8) { + pixc = pixBlockconvGrayTile(pixt, NULL, wc, hc); + } else { /* d == 32 */ + pixr = pixGetRGBComponent(pixt, COLOR_RED); + pixrc = pixBlockconvGrayTile(pixr, NULL, wc, hc); + pixDestroy(&pixr); + pixg = pixGetRGBComponent(pixt, COLOR_GREEN); + pixgc = pixBlockconvGrayTile(pixg, NULL, wc, hc); + pixDestroy(&pixg); + pixb = pixGetRGBComponent(pixt, COLOR_BLUE); + pixbc = pixBlockconvGrayTile(pixb, NULL, wc, hc); + pixDestroy(&pixb); + pixc = pixCreateRGBImage(pixrc, pixgc, pixbc); + pixDestroy(&pixrc); + pixDestroy(&pixgc); + pixDestroy(&pixbc); + } + + pixTilingPaintTile(pixd, i, j, pixc, pt); + pixDestroy(&pixt); + pixDestroy(&pixc); + } + } + + pixDestroy(&pixs); + pixTilingDestroy(&pt); + return pixd; +} + + +/*! + * \brief pixBlockconvGrayTile() + * + * \param[in] pixs 8 bpp gray + * \param[in] pixacc 32 bpp accum pix + * \param[in] wc, hc half width/height of convolution kernel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1)
+ *      (2) Assumes that the input pixs is padded with (wc + 1) pixels on
+ *          left and right, and with (hc + 1) pixels on top and bottom.
+ *          The returned pix has these stripped off; they are only used
+ *          for computation.
+ *      (3) Returns a copy if both wc and hc are 0
+ *      (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ * 
+ */ +PIX * +pixBlockconvGrayTile(PIX *pixs, + PIX *pixacc, + l_int32 wc, + l_int32 hc) +{ +l_int32 w, h, d, wd, hd, i, j, imin, imax, jmin, jmax, wplt, wpld; +l_float32 norm; +l_uint32 val; +l_uint32 *datat, *datad, *lined, *linemint, *linemaxt; +PIX *pixt, *pixd; + + PROCNAME("pixBlockconvGrayTile"); + + if (!pixs) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + if (w < 2 * wc + 3 || h < 2 * hc + 3) { + wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); + hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) + return pixCopy(NULL, pixs); + wd = w - 2 * wc; + hd = h - 2 * hc; + + if (pixacc) { + if (pixGetDepth(pixacc) == 32) { + pixt = pixClone(pixacc); + } else { + L_WARNING("pixacc not 32 bpp; making new one\n", procName); + if ((pixt = pixBlockconvAccum(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + } else { + if ((pixt = pixBlockconvAccum(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + + if ((pixd = pixCreateTemplate(pixs)) == NULL) { + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + norm = 1. / (l_float32)((2 * wc + 1) * (2 * hc + 1)); + + /* Do the convolution over the subregion of size (wd - 2, hd - 2), + * which exactly corresponds to the size of the subregion that + * will be extracted by pixTilingPaintTile(). Note that the + * region in which points are computed is not symmetric about + * the center of the images; instead the computation in + * the accumulator image is shifted up and to the left by 1, + * relative to the center, because the 4 accumulator sampling + * points are taken at the LL corner of the filter and at 3 other + * points that are shifted -wc and -hc to the left and above. */ + for (i = hc; i < hc + hd - 2; i++) { + imin = L_MAX(i - hc - 1, 0); + imax = L_MIN(i + hc, h - 1); + lined = datad + i * wpld; + linemint = datat + imin * wplt; + linemaxt = datat + imax * wplt; + for (j = wc; j < wc + wd - 2; j++) { + jmin = L_MAX(j - wc - 1, 0); + jmax = L_MIN(j + wc, w - 1); + val = linemaxt[jmax] - linemaxt[jmin] + + linemint[jmin] - linemint[jmax]; + val = (l_uint8)(norm * val + 0.5); + SET_DATA_BYTE(lined, j, val); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Convolution for mean, mean square, variance and rms deviation * + *----------------------------------------------------------------------*/ +/*! + * \brief pixWindowedStats() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] wc, hc half width/height of convolution kernel + * \param[in] hasborder use 1 if it already has (wc + 1 border pixels + * on left and right, and hc + 1 on top and bottom; + * use 0 to add kernel-dependent border) + * \param[out] ppixm [optional] 8 bpp mean value in window + * \param[out] ppixms [optional] 32 bpp mean square value in window + * \param[out] pfpixv [optional] float variance in window + * \param[out] pfpixrv [optional] float rms deviation from the mean + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a high-level convenience function for calculating
+ *          any or all of these derived images.
+ *      (2) If %hasborder = 0, a border is added and the result is
+ *          computed over all pixels in pixs.  Otherwise, no border is
+ *          added and the border pixels are removed from the output images.
+ *      (3) These statistical measures over the pixels in the
+ *          rectangular window are:
+ *            ~ average value: 

(pixm) + * ~ average squared value: (pixms) + * ~ variance: <(p -

)*(p -

)> = -

*

(pixv) + * ~ square-root of variance: (pixrv) + * where the brackets < .. > indicate that the average value is + * to be taken over the window. + * (4) Note that the variance is just the mean square difference from + * the mean value; and the square root of the variance is the + * root mean square difference from the mean, sometimes also + * called the 'standard deviation'. + * (5) The added border, along with the use of an accumulator array, + * allows computation without special treatment of pixels near + * the image boundary, and runs in a time that is independent + * of the size of the convolution kernel. + *

+ */ +l_ok +pixWindowedStats(PIX *pixs, + l_int32 wc, + l_int32 hc, + l_int32 hasborder, + PIX **ppixm, + PIX **ppixms, + FPIX **pfpixv, + FPIX **pfpixrv) +{ +PIX *pixb, *pixm, *pixms; + + PROCNAME("pixWindowedStats"); + + if (!ppixm && !ppixms && !pfpixv && !pfpixrv) + return ERROR_INT("no output requested", procName, 1); + if (ppixm) *ppixm = NULL; + if (ppixms) *ppixms = NULL; + if (pfpixv) *pfpixv = NULL; + if (pfpixrv) *pfpixrv = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (wc < 2 || hc < 2) + return ERROR_INT("wc and hc not >= 2", procName, 1); + + /* Add border if requested */ + if (!hasborder) + pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); + else + pixb = pixClone(pixs); + + if (!pfpixv && !pfpixrv) { + if (ppixm) *ppixm = pixWindowedMean(pixb, wc, hc, 1, 1); + if (ppixms) *ppixms = pixWindowedMeanSquare(pixb, wc, hc, 1); + pixDestroy(&pixb); + return 0; + } + + pixm = pixWindowedMean(pixb, wc, hc, 1, 1); + pixms = pixWindowedMeanSquare(pixb, wc, hc, 1); + pixWindowedVariance(pixm, pixms, pfpixv, pfpixrv); + if (ppixm) + *ppixm = pixm; + else + pixDestroy(&pixm); + if (ppixms) + *ppixms = pixms; + else + pixDestroy(&pixms); + pixDestroy(&pixb); + return 0; +} + + +/*! + * \brief pixWindowedMean() + * + * \param[in] pixs 8 or 32 bpp grayscale + * \param[in] wc, hc half width/height of convolution kernel + * \param[in] hasborder use 1 if it already has (wc + 1 border pixels + * on left and right, and hc + 1 on top and bottom; + * use 0 to add kernel-dependent border) + * \param[in] normflag 1 for normalization to get average in window; + * 0 for the sum in the window (un-normalized) + * \return pixd 8 or 32 bpp, average over kernel window + * + *
+ * Notes:
+ *      (1) The input and output depths are the same.
+ *      (2) A set of border pixels of width (wc + 1) on left and right,
+ *          and of height (hc + 1) on top and bottom, must be on the
+ *          pix before the accumulator is found.  The output pixd
+ *          (after convolution) has this border removed.
+ *          If %hasborder = 0, the required border is added.
+ *      (3) Typically, %normflag == 1.  However, if you want the sum
+ *          within the window, rather than a normalized convolution,
+ *          use %normflag == 0.
+ *      (4) This builds a block accumulator pix, uses it here, and
+ *          destroys it.
+ *      (5) The added border, along with the use of an accumulator array,
+ *          allows computation without special treatment of pixels near
+ *          the image boundary, and runs in a time that is independent
+ *          of the size of the convolution kernel.
+ * 
+ */ +PIX * +pixWindowedMean(PIX *pixs, + l_int32 wc, + l_int32 hc, + l_int32 hasborder, + l_int32 normflag) +{ +l_int32 i, j, w, h, d, wd, hd, wplc, wpld, wincr, hincr; +l_uint32 val; +l_uint32 *datac, *datad, *linec1, *linec2, *lined; +l_float32 norm; +PIX *pixb, *pixc, *pixd; + + PROCNAME("pixWindowedMean"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (wc < 2 || hc < 2) + return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); + + pixb = pixc = pixd = NULL; + + /* Add border if requested */ + if (!hasborder) + pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); + else + pixb = pixClone(pixs); + + /* Make the accumulator pix from pixb */ + if ((pixc = pixBlockconvAccum(pixb)) == NULL) { + L_ERROR("pixc not made\n", procName); + goto cleanup; + } + wplc = pixGetWpl(pixc); + datac = pixGetData(pixc); + + /* The output has wc + 1 border pixels stripped from each side + * of pixb, and hc + 1 border pixels stripped from top and bottom. */ + pixGetDimensions(pixb, &w, &h, NULL); + wd = w - 2 * (wc + 1); + hd = h - 2 * (hc + 1); + if (wd < 2 || hd < 2) { + L_ERROR("w or h is too small for the kernel\n", procName); + goto cleanup; + } + if ((pixd = pixCreate(wd, hd, d)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup; + } + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + wincr = 2 * wc + 1; + hincr = 2 * hc + 1; + norm = 1.0; /* use this for sum-in-window */ + if (normflag) + norm = 1.0 / ((l_float32)(wincr) * hincr); + for (i = 0; i < hd; i++) { + linec1 = datac + i * wplc; + linec2 = datac + (i + hincr) * wplc; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + val = linec2[j + wincr] - linec2[j] - linec1[j + wincr] + linec1[j]; + if (d == 8) { + val = (l_uint8)(norm * val); + SET_DATA_BYTE(lined, j, val); + } else { /* d == 32 */ + val = (l_uint32)(norm * val); + lined[j] = val; + } + } + } + +cleanup: + pixDestroy(&pixb); + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixWindowedMeanSquare() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] wc, hc half width/height of convolution kernel + * \param[in] hasborder use 1 if it already has (wc + 1 border pixels + * on left and right, and hc + 1 on top and bottom; + * use 0 to add kernel-dependent border) + * \return pixd 32 bpp, average over rectangular window of + * width = 2 * wc + 1 and height = 2 * hc + 1 + * + *
+ * Notes:
+ *      (1) A set of border pixels of width (wc + 1) on left and right,
+ *          and of height (hc + 1) on top and bottom, must be on the
+ *          pix before the accumulator is found.  The output pixd
+ *          (after convolution) has this border removed.
+ *          If %hasborder = 0, the required border is added.
+ *      (2) The advantage is that we are unaffected by the boundary, and
+ *          it is not necessary to treat pixels within %wc and %hc of the
+ *          border differently.  This is because processing for pixd
+ *          only takes place for pixels in pixs for which the
+ *          kernel is entirely contained in pixs.
+ *      (3) Why do we have an added border of width (%wc + 1) and
+ *          height (%hc + 1), when we only need %wc and %hc pixels
+ *          to satisfy this condition?  Answer: the accumulators
+ *          are asymmetric, requiring an extra row and column of
+ *          pixels at top and left to work accurately.
+ *      (4) The added border, along with the use of an accumulator array,
+ *          allows computation without special treatment of pixels near
+ *          the image boundary, and runs in a time that is independent
+ *          of the size of the convolution kernel.
+ * 
+ */ +PIX * +pixWindowedMeanSquare(PIX *pixs, + l_int32 wc, + l_int32 hc, + l_int32 hasborder) +{ +l_int32 i, j, w, h, wd, hd, wpl, wpld, wincr, hincr; +l_uint32 ival; +l_uint32 *datad, *lined; +l_float64 norm; +l_float64 val; +l_float64 *data, *line1, *line2; +DPIX *dpix; +PIX *pixb, *pixd; + + PROCNAME("pixWindowedMeanSquare"); + + if (!pixs || (pixGetDepth(pixs) != 8)) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (wc < 2 || hc < 2) + return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); + + pixd = NULL; + + /* Add border if requested */ + if (!hasborder) + pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); + else + pixb = pixClone(pixs); + + if ((dpix = pixMeanSquareAccum(pixb)) == NULL) { + L_ERROR("dpix not made\n", procName); + goto cleanup; + } + wpl = dpixGetWpl(dpix); + data = dpixGetData(dpix); + + /* The output has wc + 1 border pixels stripped from each side + * of pixb, and hc + 1 border pixels stripped from top and bottom. */ + pixGetDimensions(pixb, &w, &h, NULL); + wd = w - 2 * (wc + 1); + hd = h - 2 * (hc + 1); + if (wd < 2 || hd < 2) { + L_ERROR("w or h too small for kernel\n", procName); + goto cleanup; + } + if ((pixd = pixCreate(wd, hd, 32)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup; + } + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + wincr = 2 * wc + 1; + hincr = 2 * hc + 1; + norm = 1.0 / ((l_float32)(wincr) * hincr); + for (i = 0; i < hd; i++) { + line1 = data + i * wpl; + line2 = data + (i + hincr) * wpl; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + val = line2[j + wincr] - line2[j] - line1[j + wincr] + line1[j]; + ival = (l_uint32)(norm * val + 0.5); /* to round up */ + lined[j] = ival; + } + } + +cleanup: + dpixDestroy(&dpix); + pixDestroy(&pixb); + return pixd; +} + + +/*! + * \brief pixWindowedVariance() + * + * \param[in] pixm mean over window; 8 or 32 bpp grayscale + * \param[in] pixms mean square over window; 32 bpp + * \param[out] pfpixv [optional] float variance -- the ms deviation + * from the mean + * \param[out] pfpixrv [optional] float rms deviation from the mean + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The mean and mean square values are precomputed, using
+ *          pixWindowedMean() and pixWindowedMeanSquare().
+ *      (2) Either or both of the variance and square-root of variance
+ *          are returned as an fpix, where the variance is the
+ *          average over the window of the mean square difference of
+ *          the pixel value from the mean:
+ *                <(p - 

)*(p -

)> = -

*

+ * (3) To visualize the results: + * ~ for both, use fpixDisplayMaxDynamicRange(). + * ~ for rms deviation, simply convert the output fpix to pix, + *

+ */ +l_ok +pixWindowedVariance(PIX *pixm, + PIX *pixms, + FPIX **pfpixv, + FPIX **pfpixrv) +{ +l_int32 i, j, w, h, ws, hs, ds, wplm, wplms, wplv, wplrv, valm, valms; +l_float32 var; +l_uint32 *linem, *linems, *datam, *datams; +l_float32 *linev, *linerv, *datav, *datarv; +FPIX *fpixv, *fpixrv; /* variance and square root of variance */ + + PROCNAME("pixWindowedVariance"); + + if (!pfpixv && !pfpixrv) + return ERROR_INT("no output requested", procName, 1); + if (pfpixv) *pfpixv = NULL; + if (pfpixrv) *pfpixrv = NULL; + if (!pixm || pixGetDepth(pixm) != 8) + return ERROR_INT("pixm undefined or not 8 bpp", procName, 1); + if (!pixms || pixGetDepth(pixms) != 32) + return ERROR_INT("pixms undefined or not 32 bpp", procName, 1); + pixGetDimensions(pixm, &w, &h, NULL); + pixGetDimensions(pixms, &ws, &hs, &ds); + if (w != ws || h != hs) + return ERROR_INT("pixm and pixms sizes differ", procName, 1); + + if (pfpixv) { + fpixv = fpixCreate(w, h); + *pfpixv = fpixv; + wplv = fpixGetWpl(fpixv); + datav = fpixGetData(fpixv); + } + if (pfpixrv) { + fpixrv = fpixCreate(w, h); + *pfpixrv = fpixrv; + wplrv = fpixGetWpl(fpixrv); + datarv = fpixGetData(fpixrv); + } + + wplm = pixGetWpl(pixm); + wplms = pixGetWpl(pixms); + datam = pixGetData(pixm); + datams = pixGetData(pixms); + for (i = 0; i < h; i++) { + linem = datam + i * wplm; + linems = datams + i * wplms; + if (pfpixv) + linev = datav + i * wplv; + if (pfpixrv) + linerv = datarv + i * wplrv; + for (j = 0; j < w; j++) { + valm = GET_DATA_BYTE(linem, j); + if (ds == 8) + valms = GET_DATA_BYTE(linems, j); + else /* ds == 32 */ + valms = (l_int32)linems[j]; + var = (l_float32)valms - (l_float32)valm * valm; + if (pfpixv) + linev[j] = var; + if (pfpixrv) + linerv[j] = (l_float32)sqrt(var); + } + } + + return 0; +} + + +/*! + * \brief pixMeanSquareAccum() + * + * \param[in] pixs 8 bpp grayscale + * \return dpix 64 bit array, or NULL on error + * + *
+ * Notes:
+ *      (1) Similar to pixBlockconvAccum(), this computes the
+ *          sum of the squares of the pixel values in such a way
+ *          that the value at (i,j) is the sum of all squares in
+ *          the rectangle from the origin to (i,j).
+ *      (2) The general recursion relation (v are squared pixel values) is
+ *            a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1)
+ *          For the first line, this reduces to the special case
+ *            a(i,j) = v(i,j) + a(i, j-1)
+ *          For the first column, the special case is
+ *            a(i,j) = v(i,j) + a(i-1, j)
+ * 
+ */ +DPIX * +pixMeanSquareAccum(PIX *pixs) +{ +l_int32 i, j, w, h, wpl, wpls, val; +l_uint32 *datas, *lines; +l_float64 *data, *line, *linep; +DPIX *dpix; + + PROCNAME("pixMeanSquareAccum"); + + + if (!pixs || (pixGetDepth(pixs) != 8)) + return (DPIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if ((dpix = dpixCreate(w, h)) == NULL) + return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + + lines = datas; + line = data; + for (j = 0; j < w; j++) { /* first line */ + val = GET_DATA_BYTE(lines, j); + if (j == 0) + line[0] = (l_float64)(val) * val; + else + line[j] = line[j - 1] + (l_float64)(val) * val; + } + + /* Do the other lines */ + for (i = 1; i < h; i++) { + lines = datas + i * wpls; + line = data + i * wpl; /* current dest line */ + linep = line - wpl;; /* prev dest line */ + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + if (j == 0) + line[0] = linep[0] + (l_float64)(val) * val; + else + line[j] = line[j - 1] + linep[j] - linep[j - 1] + + (l_float64)(val) * val; + } + } + + return dpix; +} + + +/*----------------------------------------------------------------------* + * Binary block sum/rank * + *----------------------------------------------------------------------*/ +/*! + * \brief pixBlockrank() + * + * \param[in] pixs 1 bpp + * \param[in] pixacc pix [optional] 32 bpp + * \param[in] wc, hc half width/height of block sum/rank kernel + * \param[in] rank between 0.0 and 1.0; 0.5 is median filter + * \return pixd 1 bpp + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1)
+ *      (2) This returns a pixd where each pixel is a 1 if the
+ *          neighborhood (2 * wc + 1) x (2 * hc + 1)) pixels
+ *          contains the rank fraction of 1 pixels.  Otherwise,
+ *          the returned pixel is 0.  Note that the special case
+ *          of rank = 0.0 is always satisfied, so the returned
+ *          pixd has all pixels with value 1.
+ *      (3) If accum pix is null, make one, use it, and destroy it
+ *          before returning; otherwise, just use the input accum pix
+ *      (4) If both wc and hc are 0, returns a copy unless rank == 0.0,
+ *          in which case this returns an all-ones image.
+ *      (5) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ * 
+ */ +PIX * +pixBlockrank(PIX *pixs, + PIX *pixacc, + l_int32 wc, + l_int32 hc, + l_float32 rank) +{ +l_int32 w, h, d, thresh; +PIX *pixt, *pixd; + + PROCNAME("pixBlockrank"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); + + if (rank == 0.0) { + pixd = pixCreateTemplate(pixs); + pixSetAll(pixd); + return pixd; + } + + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + if (w < 2 * wc + 1 || h < 2 * hc + 1) { + wc = L_MIN(wc, (w - 1) / 2); + hc = L_MIN(hc, (h - 1) / 2); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) + return pixCopy(NULL, pixs); + + if ((pixt = pixBlocksum(pixs, pixacc, wc, hc)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* 1 bpp block rank filter output. + * Must invert because threshold gives 1 for values < thresh, + * but we need a 1 if the value is >= thresh. */ + thresh = (l_int32)(255. * rank); + pixd = pixThresholdToBinary(pixt, thresh); + pixInvert(pixd, pixd); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixBlocksum() + * + * \param[in] pixs 1 bpp + * \param[in] pixacc pix [optional] 32 bpp + * \param[in] wc, hc half width/height of block sum/rank kernel + * \return pixd 8 bpp + * + *
+ * Notes:
+ *      (1) If accum pix is null, make one and destroy it before
+ *          returning; otherwise, just use the input accum pix
+ *      (2) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1)
+ *      (3) Use of wc = hc = 1, followed by pixInvert() on the
+ *          8 bpp result, gives a nice anti-aliased, and somewhat
+ *          darkened, result on text.
+ *      (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
+ *          where (w,h) are the dimensions of pixs.
+ *      (5) Returns in each dest pixel the sum of all src pixels
+ *          that are within a block of size of the kernel, centered
+ *          on the dest pixel.  This sum is the number of src ON
+ *          pixels in the block at each location, normalized to 255
+ *          for a block containing all ON pixels.  For pixels near
+ *          the boundary, where the block is not entirely contained
+ *          within the image, we then multiply by a second normalization
+ *          factor that is greater than one, so that all results
+ *          are normalized by the number of participating pixels
+ *          within the block.
+ * 
+ */ +PIX * +pixBlocksum(PIX *pixs, + PIX *pixacc, + l_int32 wc, + l_int32 hc) +{ +l_int32 w, h, d, wplt, wpld; +l_uint32 *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixBlocksum"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (wc < 0) wc = 0; + if (hc < 0) hc = 0; + if (w < 2 * wc + 1 || h < 2 * hc + 1) { + wc = L_MIN(wc, (w - 1) / 2); + hc = L_MIN(hc, (h - 1) / 2); + L_WARNING("kernel too large; reducing!\n", procName); + L_INFO("wc = %d, hc = %d\n", procName, wc, hc); + } + if (wc == 0 && hc == 0) + return pixCopy(NULL, pixs); + + if (pixacc) { + if (pixGetDepth(pixacc) != 32) + return (PIX *)ERROR_PTR("pixacc not 32 bpp", procName, NULL); + pixt = pixClone(pixacc); + } else { + if ((pixt = pixBlockconvAccum(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + + /* 8 bpp block sum output */ + if ((pixd = pixCreate(w, h, 8)) == NULL) { + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + + wpld = pixGetWpl(pixd); + wplt = pixGetWpl(pixt); + datad = pixGetData(pixd); + datat = pixGetData(pixt); + blocksumLow(datad, w, h, wpld, datat, wplt, wc, hc); + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief blocksumLow() + * + * \param[in] datad of 8 bpp dest + * \param[in] w, h, wpl of 8 bpp dest + * \param[in] dataa of 32 bpp accum + * \param[in] wpla of 32 bpp accum + * \param[in] wc, hc convolution "half-width" and "half-height" + * \return void + * + *
+ * Notes:
+ *      (1) The full width and height of the convolution kernel
+ *          are (2 * wc + 1) and (2 * hc + 1).
+ *      (2) The lack of symmetry between the handling of the
+ *          first (hc + 1) lines and the last (hc) lines,
+ *          and similarly with the columns, is due to fact that
+ *          for the pixel at (x,y), the accumulator values are
+ *          taken at (x + wc, y + hc), (x - wc - 1, y + hc),
+ *          (x + wc, y - hc - 1) and (x - wc - 1, y - hc - 1).
+ *      (3) Compute sums of ON pixels within the block filter size,
+ *          normalized between 0 and 255, as if there were no reduced
+ *          area at the boundary.  This under-estimates the value
+ *          of the boundary pixels, so we multiply them by another
+ *          normalization factor that is greater than 1.
+ *      (4) This second normalization is done first for the first
+ *          hc + 1 lines; then for the last hc lines; and finally
+ *          for the first wc + 1 and last wc columns in the intermediate
+ *          lines.
+ *      (5) Required constraints are: wc < w and hc < h.
+ * 
+ */ +static void +blocksumLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpl, + l_uint32 *dataa, + l_int32 wpla, + l_int32 wc, + l_int32 hc) +{ +l_int32 i, j, imax, imin, jmax, jmin; +l_int32 wn, hn, fwc, fhc, wmwc, hmhc; +l_float32 norm, normh, normw; +l_uint32 val; +l_uint32 *linemina, *linemaxa, *lined; + + PROCNAME("blocksumLow"); + + wmwc = w - wc; + hmhc = h - hc; + if (wmwc <= 0 || hmhc <= 0) { + L_ERROR("wc >= w || hc >=h\n", procName); + return; + } + fwc = 2 * wc + 1; + fhc = 2 * hc + 1; + norm = 255. / ((l_float32)(fwc) * fhc); + + /*------------------------------------------------------------* + * Compute, using b.c. only to set limits on the accum image * + *------------------------------------------------------------*/ + for (i = 0; i < h; i++) { + imin = L_MAX(i - 1 - hc, 0); + imax = L_MIN(i + hc, h - 1); + lined = datad + wpl * i; + linemina = dataa + wpla * imin; + linemaxa = dataa + wpla * imax; + for (j = 0; j < w; j++) { + jmin = L_MAX(j - 1 - wc, 0); + jmax = L_MIN(j + wc, w - 1); + val = linemaxa[jmax] - linemaxa[jmin] + - linemina[jmax] + linemina[jmin]; + val = (l_uint8)(norm * val); + SET_DATA_BYTE(lined, j, val); + } + } + + /*------------------------------------------------------------* + * Fix normalization for boundary pixels * + *------------------------------------------------------------*/ + for (i = 0; i <= hc; i++) { /* first hc + 1 lines */ + hn = hc + i; + normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ + lined = datad + wpl * i; + for (j = 0; j <= wc; j++) { + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh * normw); + SET_DATA_BYTE(lined, j, val); + } + for (j = wc + 1; j < wmwc; j++) { + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh); + SET_DATA_BYTE(lined, j, val); + } + for (j = wmwc; j < w; j++) { + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh * normw); + SET_DATA_BYTE(lined, j, val); + } + } + + for (i = hmhc; i < h; i++) { /* last hc lines */ + hn = hc + h - i; + normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ + lined = datad + wpl * i; + for (j = 0; j <= wc; j++) { + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh * normw); + SET_DATA_BYTE(lined, j, val); + } + for (j = wc + 1; j < wmwc; j++) { + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh); + SET_DATA_BYTE(lined, j, val); + } + for (j = wmwc; j < w; j++) { + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normh * normw); + SET_DATA_BYTE(lined, j, val); + } + } + + for (i = hc + 1; i < hmhc; i++) { /* intermediate lines */ + lined = datad + wpl * i; + for (j = 0; j <= wc; j++) { /* first wc + 1 columns */ + wn = wc + j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normw); + SET_DATA_BYTE(lined, j, val); + } + for (j = wmwc; j < w; j++) { /* last wc columns */ + wn = wc + w - j; + normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ + val = GET_DATA_BYTE(lined, j); + val = (l_uint8)(val * normw); + SET_DATA_BYTE(lined, j, val); + } + } + + return; +} + + +/*----------------------------------------------------------------------* + * Census transform * + *----------------------------------------------------------------------*/ +/*! + * \brief pixCensusTransform() + * + * \param[in] pixs 8 bpp + * \param[in] halfsize of square over which neighbors are averaged + * \param[in] pixacc [optional] 32 bpp pix + * \return pixd 1 bpp + * + *
+ * Notes:
+ *      (1) The Census transform was invented by Ramin Zabih and John Woodfill
+ *          ("Non-parametric local transforms for computing visual
+ *          correspondence", Third European Conference on Computer Vision,
+ *          Stockholm, Sweden, May 1994); see publications at
+ *             http://www.cs.cornell.edu/~rdz/index.htm
+ *          This compares each pixel against the average of its neighbors,
+ *          in a square of odd dimension centered on the pixel.
+ *          If the pixel is greater than the average of its neighbors,
+ *          the output pixel value is 1; otherwise it is 0.
+ *      (2) This can be used as an encoding for an image that is
+ *          fairly robust against slow illumination changes, with
+ *          applications in image comparison and mosaicing.
+ *      (3) The size of the convolution kernel is (2 * halfsize + 1)
+ *          on a side.  The halfsize parameter must be >= 1.
+ *      (4) If accum pix is null, make one, use it, and destroy it
+ *          before returning; otherwise, just use the input accum pix
+ * 
+ */ +PIX * +pixCensusTransform(PIX *pixs, + l_int32 halfsize, + PIX *pixacc) +{ +l_int32 i, j, w, h, wpls, wplv, wpld; +l_int32 vals, valv; +l_uint32 *datas, *datav, *datad, *lines, *linev, *lined; +PIX *pixav, *pixd; + + PROCNAME("pixCensusTransform"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (halfsize < 1) + return (PIX *)ERROR_PTR("halfsize must be >= 1", procName, NULL); + + /* Get the average of each pixel with its neighbors */ + if ((pixav = pixBlockconvGray(pixs, pixacc, halfsize, halfsize)) + == NULL) + return (PIX *)ERROR_PTR("pixav not made", procName, NULL); + + /* Subtract the pixel from the average, and then compare + * the pixel value with the remaining average */ + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 1)) == NULL) { + pixDestroy(&pixav); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + datas = pixGetData(pixs); + datav = pixGetData(pixav); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wplv = pixGetWpl(pixav); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linev = datav + i * wplv; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valv = GET_DATA_BYTE(linev, j); + if (vals > valv) + SET_DATA_BIT(lined, j); + } + } + + pixDestroy(&pixav); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Generic convolution * + *----------------------------------------------------------------------*/ +/*! + * \brief pixConvolve() + * + * \param[in] pixs 8, 16, 32 bpp; no colormap + * \param[in] kel kernel + * \param[in] outdepth of pixd: 8, 16 or 32 + * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise + * \return pixd 8, 16 or 32 bpp + * + *
+ * Notes:
+ *      (1) This gives a convolution with an arbitrary kernel.
+ *      (2) The input pixs must have only one sample/pixel.
+ *          To do a convolution on an RGB image, use pixConvolveRGB().
+ *      (3) The parameter %outdepth determines the depth of the result.
+ *          If the kernel is normalized to unit sum, the output values
+ *          can never exceed 255, so an output depth of 8 bpp is sufficient.
+ *          If the kernel is not normalized, it may be necessary to use
+ *          16 or 32 bpp output to avoid overflow.
+ *      (4) If normflag == 1, the result is normalized by scaling all
+ *          kernel values for a unit sum.  If the sum of kernel values
+ *          is very close to zero, the kernel can not be normalized and
+ *          the convolution will not be performed.  A warning is issued.
+ *      (5) The kernel values can be positive or negative, but the
+ *          result for the convolution can only be stored as a positive
+ *          number.  Consequently, if it goes negative, the choices are
+ *          to clip to 0 or take the absolute value.  We're choosing
+ *          to take the absolute value.  (Another possibility would be
+ *          to output a second unsigned image for the negative values.)
+ *          If you want to get a clipped result, or to keep the negative
+ *          values in the result, use fpixConvolve(), with the
+ *          converters in fpix2.c between pix and fpix.
+ *      (6) This uses a mirrored border to avoid special casing on
+ *          the boundaries.
+ *      (7) To get a subsampled output, call l_setConvolveSampling().
+ *          The time to make a subsampled output is reduced by the
+ *          product of the sampling factors.
+ *      (8) The function is slow, running at about 12 machine cycles for
+ *          each pixel-op in the convolution.  For example, with a 3 GHz
+ *          cpu, a 1 Mpixel grayscale image, and a kernel with
+ *          (sx * sy) = 25 elements, the convolution takes about 100 msec.
+ * 
+ */ +PIX * +pixConvolve(PIX *pixs, + L_KERNEL *kel, + l_int32 outdepth, + l_int32 normflag) +{ +l_int32 i, j, id, jd, k, m, w, h, d, wd, hd, sx, sy, cx, cy, wplt, wpld; +l_int32 val; +l_uint32 *datat, *datad, *linet, *lined; +l_float32 sum; +L_KERNEL *keli, *keln; +PIX *pixt, *pixd; + + PROCNAME("pixConvolve"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); + if (!kel) + return (PIX *)ERROR_PTR("kel not defined", procName, NULL); + + pixd = NULL; + + keli = kernelInvert(kel); + kernelGetParameters(keli, &sy, &sx, &cy, &cx); + if (normflag) + keln = kernelNormalize(keli, 1.0); + else + keln = kernelCopy(keli); + + if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) { + L_ERROR("pixt not made\n", procName); + goto cleanup; + } + + wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; + hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; + pixd = pixCreate(wd, hd, outdepth); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { + lined = datad + id * wpld; + for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { + sum = 0.0; + for (k = 0; k < sy; k++) { + linet = datat + (i + k) * wplt; + if (d == 8) { + for (m = 0; m < sx; m++) { + val = GET_DATA_BYTE(linet, j + m); + sum += val * keln->data[k][m]; + } + } else if (d == 16) { + for (m = 0; m < sx; m++) { + val = GET_DATA_TWO_BYTES(linet, j + m); + sum += val * keln->data[k][m]; + } + } else { /* d == 32 */ + for (m = 0; m < sx; m++) { + val = *(linet + j + m); + sum += val * keln->data[k][m]; + } + } + } + if (sum < 0.0) sum = -sum; /* make it non-negative */ + if (outdepth == 8) + SET_DATA_BYTE(lined, jd, (l_int32)(sum + 0.5)); + else if (outdepth == 16) + SET_DATA_TWO_BYTES(lined, jd, (l_int32)(sum + 0.5)); + else /* outdepth == 32 */ + *(lined + jd) = (l_uint32)(sum + 0.5); + } + } + +cleanup: + kernelDestroy(&keli); + kernelDestroy(&keln); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixConvolveSep() + * + * \param[in] pixs 8, 16, 32 bpp; no colormap + * \param[in] kelx x-dependent kernel + * \param[in] kely y-dependent kernel + * \param[in] outdepth of pixd: 8, 16 or 32 + * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise + * \return pixd 8, 16 or 32 bpp + * + *
+ * Notes:
+ *      (1) This does a convolution with a separable kernel that is
+ *          is a sequence of convolutions in x and y.  The two
+ *          one-dimensional kernel components must be input separately;
+ *          the full kernel is the product of these components.
+ *          The support for the full kernel is thus a rectangular region.
+ *      (2) The input pixs must have only one sample/pixel.
+ *          To do a convolution on an RGB image, use pixConvolveSepRGB().
+ *      (3) The parameter %outdepth determines the depth of the result.
+ *          If the kernel is normalized to unit sum, the output values
+ *          can never exceed 255, so an output depth of 8 bpp is sufficient.
+ *          If the kernel is not normalized, it may be necessary to use
+ *          16 or 32 bpp output to avoid overflow.
+ *      (2) The %normflag parameter is used as in pixConvolve().
+ *      (4) The kernel values can be positive or negative, but the
+ *          result for the convolution can only be stored as a positive
+ *          number.  Consequently, if it goes negative, the choices are
+ *          to clip to 0 or take the absolute value.  We're choosing
+ *          the former for now.  Another possibility would be to output
+ *          a second unsigned image for the negative values.
+ *      (5) Warning: if you use l_setConvolveSampling() to get a
+ *          subsampled output, and the sampling factor is larger than
+ *          the kernel half-width, it is faster to use the non-separable
+ *          version pixConvolve().  This is because the first convolution
+ *          here must be done on every raster line, regardless of the
+ *          vertical sampling factor.  If the sampling factor is smaller
+ *          than kernel half-width, it's faster to use the separable
+ *          convolution.
+ *      (6) This uses mirrored borders to avoid special casing on
+ *          the boundaries.
+ * 
+ */ +PIX * +pixConvolveSep(PIX *pixs, + L_KERNEL *kelx, + L_KERNEL *kely, + l_int32 outdepth, + l_int32 normflag) +{ +l_int32 d, xfact, yfact; +L_KERNEL *kelxn, *kelyn; +PIX *pixt, *pixd; + + PROCNAME("pixConvolveSep"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); + if (!kelx) + return (PIX *)ERROR_PTR("kelx not defined", procName, NULL); + if (!kely) + return (PIX *)ERROR_PTR("kely not defined", procName, NULL); + + xfact = ConvolveSamplingFactX; + yfact = ConvolveSamplingFactY; + if (normflag) { + kelxn = kernelNormalize(kelx, 1000.0); + kelyn = kernelNormalize(kely, 0.001); + l_setConvolveSampling(xfact, 1); + pixt = pixConvolve(pixs, kelxn, 32, 0); + l_setConvolveSampling(1, yfact); + pixd = pixConvolve(pixt, kelyn, outdepth, 0); + l_setConvolveSampling(xfact, yfact); /* restore */ + kernelDestroy(&kelxn); + kernelDestroy(&kelyn); + } else { /* don't normalize */ + l_setConvolveSampling(xfact, 1); + pixt = pixConvolve(pixs, kelx, 32, 0); + l_setConvolveSampling(1, yfact); + pixd = pixConvolve(pixt, kely, outdepth, 0); + l_setConvolveSampling(xfact, yfact); + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixConvolveRGB() + * + * \param[in] pixs 32 bpp rgb + * \param[in] kel kernel + * \return pixd 32 bpp rgb + * + *
+ * Notes:
+ *      (1) This gives a convolution on an RGB image using an
+ *          arbitrary kernel (which we normalize to keep each
+ *          component within the range [0 ... 255].
+ *      (2) The input pixs must be RGB.
+ *      (3) The kernel values can be positive or negative, but the
+ *          result for the convolution can only be stored as a positive
+ *          number.  Consequently, if it goes negative, we clip the
+ *          result to 0.
+ *      (4) To get a subsampled output, call l_setConvolveSampling().
+ *          The time to make a subsampled output is reduced by the
+ *          product of the sampling factors.
+ *      (5) This uses a mirrored border to avoid special casing on
+ *          the boundaries.
+ * 
+ */ +PIX * +pixConvolveRGB(PIX *pixs, + L_KERNEL *kel) +{ +PIX *pixt, *pixr, *pixg, *pixb, *pixd; + + PROCNAME("pixConvolveRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); + if (!kel) + return (PIX *)ERROR_PTR("kel not defined", procName, NULL); + + pixt = pixGetRGBComponent(pixs, COLOR_RED); + pixr = pixConvolve(pixt, kel, 8, 1); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + pixg = pixConvolve(pixt, kel, 8, 1); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + pixb = pixConvolve(pixt, kel, 8, 1); + pixDestroy(&pixt); + pixd = pixCreateRGBImage(pixr, pixg, pixb); + + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; +} + + +/*! + * \brief pixConvolveRGBSep() + * + * \param[in] pixs 32 bpp rgb + * \param[in] kelx x-dependent kernel + * \param[in] kely y-dependent kernel + * \return pixd 32 bpp rgb + * + *
+ * Notes:
+ *      (1) This does a convolution on an RGB image using a separable
+ *          kernel that is a sequence of convolutions in x and y.  The two
+ *          one-dimensional kernel components must be input separately;
+ *          the full kernel is the product of these components.
+ *          The support for the full kernel is thus a rectangular region.
+ *      (2) The kernel values can be positive or negative, but the
+ *          result for the convolution can only be stored as a positive
+ *          number.  Consequently, if it goes negative, we clip the
+ *          result to 0.
+ *      (3) To get a subsampled output, call l_setConvolveSampling().
+ *          The time to make a subsampled output is reduced by the
+ *          product of the sampling factors.
+ *      (4) This uses a mirrored border to avoid special casing on
+ *          the boundaries.
+ * 
+ */ +PIX * +pixConvolveRGBSep(PIX *pixs, + L_KERNEL *kelx, + L_KERNEL *kely) +{ +PIX *pixt, *pixr, *pixg, *pixb, *pixd; + + PROCNAME("pixConvolveRGBSep"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); + if (!kelx || !kely) + return (PIX *)ERROR_PTR("kelx, kely not both defined", procName, NULL); + + pixt = pixGetRGBComponent(pixs, COLOR_RED); + pixr = pixConvolveSep(pixt, kelx, kely, 8, 1); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + pixg = pixConvolveSep(pixt, kelx, kely, 8, 1); + pixDestroy(&pixt); + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + pixb = pixConvolveSep(pixt, kelx, kely, 8, 1); + pixDestroy(&pixt); + pixd = pixCreateRGBImage(pixr, pixg, pixb); + + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Generic convolution with float array * + *----------------------------------------------------------------------*/ +/*! + * \brief fpixConvolve() + * + * \param[in] fpixs 32 bit float array + * \param[in] kel kernel + * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise + * \return fpixd 32 bit float array + * + *
+ * Notes:
+ *      (1) This gives a float convolution with an arbitrary kernel.
+ *      (2) If normflag == 1, the result is normalized by scaling all
+ *          kernel values for a unit sum.  If the sum of kernel values
+ *          is very close to zero, the kernel can not be normalized and
+ *          the convolution will not be performed.  A warning is issued.
+ *      (3) With the FPix, there are no issues about negative
+ *          array or kernel values.  The convolution is performed
+ *          with single precision arithmetic.
+ *      (4) To get a subsampled output, call l_setConvolveSampling().
+ *          The time to make a subsampled output is reduced by the
+ *          product of the sampling factors.
+ *      (5) This uses a mirrored border to avoid special casing on
+ *          the boundaries.
+ * 
+ */ +FPIX * +fpixConvolve(FPIX *fpixs, + L_KERNEL *kel, + l_int32 normflag) +{ +l_int32 i, j, id, jd, k, m, w, h, wd, hd, sx, sy, cx, cy, wplt, wpld; +l_float32 val; +l_float32 *datat, *datad, *linet, *lined; +l_float32 sum; +L_KERNEL *keli, *keln; +FPIX *fpixt, *fpixd; + + PROCNAME("fpixConvolve"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (!kel) + return (FPIX *)ERROR_PTR("kel not defined", procName, NULL); + + fpixd = NULL; + + keli = kernelInvert(kel); + kernelGetParameters(keli, &sy, &sx, &cy, &cx); + if (normflag) + keln = kernelNormalize(keli, 1.0); + else + keln = kernelCopy(keli); + + fpixGetDimensions(fpixs, &w, &h); + fpixt = fpixAddMirroredBorder(fpixs, cx, sx - cx, cy, sy - cy); + if (!fpixt) { + L_ERROR("fpixt not made\n", procName); + goto cleanup; + } + + wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; + hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; + fpixd = fpixCreate(wd, hd); + datat = fpixGetData(fpixt); + datad = fpixGetData(fpixd); + wplt = fpixGetWpl(fpixt); + wpld = fpixGetWpl(fpixd); + for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { + lined = datad + id * wpld; + for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { + sum = 0.0; + for (k = 0; k < sy; k++) { + linet = datat + (i + k) * wplt; + for (m = 0; m < sx; m++) { + val = *(linet + j + m); + sum += val * keln->data[k][m]; + } + } + *(lined + jd) = sum; + } + } + +cleanup: + kernelDestroy(&keli); + kernelDestroy(&keln); + fpixDestroy(&fpixt); + return fpixd; +} + + +/*! + * \brief fpixConvolveSep() + * + * \param[in] fpixs 32 bit float array + * \param[in] kelx x-dependent kernel + * \param[in] kely y-dependent kernel + * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise + * \return fpixd 32 bit float array + * + *
+ * Notes:
+ *      (1) This does a convolution with a separable kernel that is
+ *          is a sequence of convolutions in x and y.  The two
+ *          one-dimensional kernel components must be input separately;
+ *          the full kernel is the product of these components.
+ *          The support for the full kernel is thus a rectangular region.
+ *      (2) The normflag parameter is used as in fpixConvolve().
+ *      (3) Warning: if you use l_setConvolveSampling() to get a
+ *          subsampled output, and the sampling factor is larger than
+ *          the kernel half-width, it is faster to use the non-separable
+ *          version pixConvolve().  This is because the first convolution
+ *          here must be done on every raster line, regardless of the
+ *          vertical sampling factor.  If the sampling factor is smaller
+ *          than kernel half-width, it's faster to use the separable
+ *          convolution.
+ *      (4) This uses mirrored borders to avoid special casing on
+ *          the boundaries.
+ * 
+ */ +FPIX * +fpixConvolveSep(FPIX *fpixs, + L_KERNEL *kelx, + L_KERNEL *kely, + l_int32 normflag) +{ +l_int32 xfact, yfact; +L_KERNEL *kelxn, *kelyn; +FPIX *fpixt, *fpixd; + + PROCNAME("fpixConvolveSep"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!kelx) + return (FPIX *)ERROR_PTR("kelx not defined", procName, NULL); + if (!kely) + return (FPIX *)ERROR_PTR("kely not defined", procName, NULL); + + xfact = ConvolveSamplingFactX; + yfact = ConvolveSamplingFactY; + if (normflag) { + kelxn = kernelNormalize(kelx, 1.0); + kelyn = kernelNormalize(kely, 1.0); + l_setConvolveSampling(xfact, 1); + fpixt = fpixConvolve(fpixs, kelxn, 0); + l_setConvolveSampling(1, yfact); + fpixd = fpixConvolve(fpixt, kelyn, 0); + l_setConvolveSampling(xfact, yfact); /* restore */ + kernelDestroy(&kelxn); + kernelDestroy(&kelyn); + } else { /* don't normalize */ + l_setConvolveSampling(xfact, 1); + fpixt = fpixConvolve(fpixs, kelx, 0); + l_setConvolveSampling(1, yfact); + fpixd = fpixConvolve(fpixt, kely, 0); + l_setConvolveSampling(xfact, yfact); + } + + fpixDestroy(&fpixt); + return fpixd; +} + + +/*------------------------------------------------------------------------* + * Convolution with bias (for non-negative output) * + *------------------------------------------------------------------------*/ +/*! + * \brief pixConvolveWithBias() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] kel1 + * \param[in] kel2 can be null; use if separable + * \param[in] force8 if 1, force output to 8 bpp; otherwise, determine + * output depth by the dynamic range of pixel values + * \param[out] pbias applied bias + * \return pixd 8 or 16 bpp + * + *
+ * Notes:
+ *      (1) This does a convolution with either a single kernel or
+ *          a pair of separable kernels, and automatically applies whatever
+ *          bias (shift) is required so that the resulting pixel values
+ *          are non-negative.
+ *      (2) The kernel is always normalized.  If there are no negative
+ *          values in the kernel, a standard normalized convolution is
+ *          performed, with 8 bpp output.  If the sum of kernel values is
+ *          very close to zero, the kernel can not be normalized and
+ *          the convolution will not be performed.  An error message results.
+ *      (3) If there are negative values in the kernel, the pix is
+ *          converted to an fpix, the convolution is done on the fpix, and
+ *          a bias (shift) may need to be applied.
+ *      (4) If force8 == TRUE and the range of values after the convolution
+ *          is > 255, the output values will be scaled to fit in [0 ... 255].
+ *          If force8 == FALSE, the output will be either 8 or 16 bpp,
+ *          to accommodate the dynamic range of output values without scaling.
+ * 
+ */ +PIX * +pixConvolveWithBias(PIX *pixs, + L_KERNEL *kel1, + L_KERNEL *kel2, + l_int32 force8, + l_int32 *pbias) +{ +l_int32 outdepth; +l_float32 min1, min2, min, minval, maxval, range; +FPIX *fpix1, *fpix2; +PIX *pixd; + + PROCNAME("pixConvolveWithBias"); + + if (!pbias) + return (PIX *)ERROR_PTR("&bias not defined", procName, NULL); + *pbias = 0; + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + if (!kel1) + return (PIX *)ERROR_PTR("kel1 not defined", procName, NULL); + + /* Determine if negative values can be produced in the convolution */ + kernelGetMinMax(kel1, &min1, NULL); + min2 = 0.0; + if (kel2) + kernelGetMinMax(kel2, &min2, NULL); + min = L_MIN(min1, min2); + + if (min >= 0.0) { + if (!kel2) + return pixConvolve(pixs, kel1, 8, 1); + else + return pixConvolveSep(pixs, kel1, kel2, 8, 1); + } + + /* Bias may need to be applied; convert to fpix and convolve */ + fpix1 = pixConvertToFPix(pixs, 1); + if (!kel2) + fpix2 = fpixConvolve(fpix1, kel1, 1); + else + fpix2 = fpixConvolveSep(fpix1, kel1, kel2, 1); + fpixDestroy(&fpix1); + + /* Determine the bias and the dynamic range. + * If the dynamic range is <= 255, just shift the values by the + * bias, if any. + * If the dynamic range is > 255, there are two cases: + * (1) the output depth is not forced to 8 bpp + * ==> apply the bias without scaling; outdepth = 16 + * (2) the output depth is forced to 8 + * ==> linearly map the pixel values to [0 ... 255]. */ + fpixGetMin(fpix2, &minval, NULL, NULL); + fpixGetMax(fpix2, &maxval, NULL, NULL); + range = maxval - minval; + *pbias = (minval < 0.0) ? -minval : 0.0; + fpixAddMultConstant(fpix2, *pbias, 1.0); /* shift: min val ==> 0 */ + if (range <= 255 || !force8) { /* no scaling of output values */ + outdepth = (range > 255) ? 16 : 8; + } else { /* scale output values to fit in 8 bpp */ + fpixAddMultConstant(fpix2, 0.0, (255.0 / range)); + outdepth = 8; + } + + /* Convert back to pix; it won't do any clipping */ + pixd = fpixConvertToPix(fpix2, outdepth, L_CLIP_TO_ZERO, 0); + fpixDestroy(&fpix2); + + return pixd; +} + + +/*------------------------------------------------------------------------* + * Set parameter for convolution subsampling * + *------------------------------------------------------------------------*/ +/*! + * \brief l_setConvolveSampling() + + * + * \param[in] xfact, yfact integer >= 1 + * \return void + * + *
+ * Notes:
+ *      (1) This sets the x and y output subsampling factors for generic pix
+ *          and fpix convolution.  The default values are 1 (no subsampling).
+ * 
+ */ +void +l_setConvolveSampling(l_int32 xfact, + l_int32 yfact) +{ + if (xfact < 1) xfact = 1; + if (yfact < 1) yfact = 1; + ConvolveSamplingFactX = xfact; + ConvolveSamplingFactY = yfact; +} + + +/*------------------------------------------------------------------------* + * Additive gaussian noise * + *------------------------------------------------------------------------*/ +/*! + * \brief pixAddGaussianNoise() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb; no colormap + * \param[in] stdev of noise + * \return pixd 8 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This adds noise to each pixel, taken from a normal
+ *          distribution with zero mean and specified standard deviation.
+ * 
+ */ +PIX * +pixAddGaussianNoise(PIX *pixs, + l_float32 stdev) +{ +l_int32 i, j, w, h, d, wpls, wpld, val, rval, gval, bval; +l_uint32 pixel; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixAddGaussianNoise"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + + pixd = pixCreateTemplateNoInit(pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 8) { + val = GET_DATA_BYTE(lines, j); + val += (l_int32)(stdev * gaussDistribSampling() + 0.5); + val = L_MIN(255, L_MAX(0, val)); + SET_DATA_BYTE(lined, j, val); + } else { /* d = 32 */ + pixel = *(lines + j); + extractRGBValues(pixel, &rval, &gval, &bval); + rval += (l_int32)(stdev * gaussDistribSampling() + 0.5); + rval = L_MIN(255, L_MAX(0, rval)); + gval += (l_int32)(stdev * gaussDistribSampling() + 0.5); + gval = L_MIN(255, L_MAX(0, gval)); + bval += (l_int32)(stdev * gaussDistribSampling() + 0.5); + bval = L_MIN(255, L_MAX(0, bval)); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + } + return pixd; +} + + +/*! + * \brief gaussDistribSampling() + * + * \return gaussian distributed variable with zero mean and unit stdev + * + *
+ * Notes:
+ *      (1) For an explanation of the Box-Muller method for generating
+ *          a normally distributed random variable with zero mean and
+ *          unit standard deviation, see Numerical Recipes in C,
+ *          2nd edition, p. 288ff.
+ *      (2) This can be called sequentially to get samples that can be
+ *          used for adding noise to each pixel of an image, for example.
+ * 
+ */ +l_float32 +gaussDistribSampling() +{ +static l_int32 select = 0; /* flips between 0 and 1 on successive calls */ +static l_float32 saveval; +l_float32 frand, xval, yval, rsq, factor; + + if (select == 0) { + while (1) { /* choose a point in a 2x2 square, centered at origin */ + frand = (l_float32)rand() / (l_float32)RAND_MAX; + xval = 2.0 * frand - 1.0; + frand = (l_float32)rand() / (l_float32)RAND_MAX; + yval = 2.0 * frand - 1.0; + rsq = xval * xval + yval * yval; + if (rsq > 0.0 && rsq < 1.0) /* point is inside the unit circle */ + break; + } + factor = sqrt(-2.0 * log(rsq) / rsq); + saveval = xval * factor; + select = 1; + return yval * factor; + } + else { + select = 0; + return saveval; + } +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/correlscore.c b/hgdriver/3rdparty/hgOCR/leptonica/correlscore.c new file mode 100644 index 0000000..3684f2c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/correlscore.c @@ -0,0 +1,879 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * correlscore.c + * + * These are functions for computing correlation between + * pairs of 1 bpp images. + * + * Optimized 2 pix correlators (for jbig2 clustering) + * l_int32 pixCorrelationScore() + * l_int32 pixCorrelationScoreThresholded() + * + * Simple 2 pix correlators + * l_int32 pixCorrelationScoreSimple() + * l_int32 pixCorrelationScoreShifted() + * + * There are other, more application-oriented functions, that + * compute the correlation between two binary images, taking into + * account small translational shifts, between two binary images. + * These are: + * compare.c: pixBestCorrelation() + * Uses coarse-to-fine translations of full image + * recogident.c: pixCorrelationBestShift() + * Uses small shifts between c.c. centroids. + */ + +#include +#include "allheaders.h" + + +/* -------------------------------------------------------------------- * + * Optimized 2 pix correlators (for jbig2 clustering) * + * -------------------------------------------------------------------- */ +/*! + * \brief pixCorrelationScore() + * + * \param[in] pix1 test pix, 1 bpp + * \param[in] pix2 exemplar pix, 1 bpp + * \param[in] area1 number of on pixels in pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] delx x comp of centroid difference + * \param[in] dely y comp of centroid difference + * \param[in] maxdiffw max width difference of pix1 and pix2 + * \param[in] maxdiffh max height difference of pix1 and pix2 + * \param[in] tab sum tab for byte + * \param[out] pscore correlation score + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *  We check first that the two pix are roughly the same size.
+ *  For jbclass (jbig2) applications at roughly 300 ppi, maxdiffw and
+ *  maxdiffh should be at least 2.
+ *
+ *  Only if they meet that criterion do we compare the bitmaps.
+ *  The centroid difference is used to align the two images to the
+ *  nearest integer for the correlation.
+ *
+ *  The correlation score is the ratio of the square of the number of
+ *  pixels in the AND of the two bitmaps to the product of the number
+ *  of ON pixels in each.  Denote the number of ON pixels in pix1
+ *  by |1|, the number in pix2 by |2|, and the number in the AND
+ *  of pix1 and pix2 by |1 & 2|.  The correlation score is then
+ *  (|1 & 2|)**2 / (|1|*|2|).
+ *
+ *  This score is compared with an input threshold, which can
+ *  be modified depending on the weight of the template.
+ *  The modified threshold is
+ *     thresh + (1.0 - thresh) * weight * R
+ *  where
+ *     weight is a fixed input factor between 0.0 and 1.0
+ *     R = |2| / area(2)
+ *  and area(2) is the total number of pixels in 2 (i.e., width x height).
+ *
+ *  To understand why a weight factor is useful, consider what happens
+ *  with thick, sans-serif characters that look similar and have a value
+ *  of R near 1.  Different characters can have a high correlation value,
+ *  and the classifier will make incorrect substitutions.  The weight
+ *  factor raises the threshold for these characters.
+ *
+ *  Yet another approach to reduce such substitutions is to run the classifier
+ *  in a non-greedy way, matching to the template with the highest
+ *  score, not the first template with a score satisfying the matching
+ *  constraint.  However, this is not particularly effective.
+ *
+ *  The implementation here gives the same result as in
+ *  pixCorrelationScoreSimple(), where a temporary Pix is made to hold
+ *  the AND and implementation uses rasterop:
+ *      pixt = pixCreateTemplate(pix1);
+ *      pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix2, 0, 0);
+ *      pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC & PIX_DST, pix1, 0, 0);
+ *      pixCountPixels(pixt, &count, tab);
+ *      pixDestroy(&pixt);
+ *  However, here it is done in a streaming fashion, counting as it goes,
+ *  and touching memory exactly once, giving a 3-4x speedup over the
+ *  simple implementation.  This very fast correlation matcher was
+ *  contributed by William Rucklidge.
+ * 
+ */ +l_ok +pixCorrelationScore(PIX *pix1, + PIX *pix2, + l_int32 area1, + l_int32 area2, + l_float32 delx, /* x(1) - x(3) */ + l_float32 dely, /* y(1) - y(3) */ + l_int32 maxdiffw, + l_int32 maxdiffh, + l_int32 *tab, + l_float32 *pscore) +{ +l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; +l_int32 wpl1, wpl2, lorow, hirow, locol, hicol; +l_int32 x, y, pix1lskip, pix2lskip, rowwords1, rowwords2; +l_uint32 word1, word2, andw; +l_uint32 *row1, *row2; + + PROCNAME("pixCorrelationScore"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); + if (!tab) + return ERROR_INT("tab not defined", procName, 1); + if (area1 <= 0 || area2 <= 0) + return ERROR_INT("areas must be > 0", procName, 1); + + /* Eliminate based on size difference */ + pixGetDimensions(pix1, &wi, &hi, NULL); + pixGetDimensions(pix2, &wt, &ht, NULL); + delw = L_ABS(wi - wt); + if (delw > maxdiffw) + return 0; + delh = L_ABS(hi - ht); + if (delh > maxdiffh) + return 0; + + /* Round difference to nearest integer */ + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + + count = 0; + wpl1 = pixGetWpl(pix1); + wpl2 = pixGetWpl(pix2); + rowwords2 = wpl2; + + /* What rows of pix1 need to be considered? Only those underlying the + * shifted pix2. */ + lorow = L_MAX(idely, 0); + hirow = L_MIN(ht + idely, hi); + + /* Get the pointer to the first row of each image that will be + * considered. */ + row1 = pixGetData(pix1) + wpl1 * lorow; + row2 = pixGetData(pix2) + wpl2 * (lorow - idely); + + /* Similarly, figure out which columns of pix1 will be considered. */ + locol = L_MAX(idelx, 0); + hicol = L_MIN(wt + idelx, wi); + + if (idelx >= 32) { + /* pix2 is shifted far enough to the right that pix1's first + * word(s) won't contribute to the count. Increment its + * pointer to point to the first word that will contribute, + * and adjust other values accordingly. */ + pix1lskip = idelx >> 5; /* # of words to skip on left */ + row1 += pix1lskip; + locol -= pix1lskip << 5; + hicol -= pix1lskip << 5; + idelx &= 31; + } else if (idelx <= -32) { + /* pix2 is shifted far enough to the left that its first word(s) + * won't contribute to the count. Increment its pointer + * to point to the first word that will contribute, + * and adjust other values accordingly. */ + pix2lskip = -((idelx + 31) >> 5); /* # of words to skip on left */ + row2 += pix2lskip; + rowwords2 -= pix2lskip; + idelx += pix2lskip << 5; + } + + if ((locol >= hicol) || (lorow >= hirow)) { /* there is no overlap */ + count = 0; + } else { + /* How many words of each row of pix1 need to be considered? */ + rowwords1 = (hicol + 31) >> 5; + + if (idelx == 0) { + /* There's no lateral offset; simple case. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1; x++) { + andw = row1[x] & row2[x]; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + } + } else if (idelx > 0) { + /* pix2 is shifted to the right. word 0 of pix1 is touched by + * word 0 of pix2; word 1 of pix1 is touched by word 0 and word + * 1 of pix2, and so on up to the last word of pix1 (word N), + * which is touched by words N-1 and N of pix1... if there is a + * word N. Handle the two cases (pix2 has N-1 words and pix2 + * has at least N words) separately. + * + * Note: we know that pix2 has at least N-1 words (i.e., + * rowwords2 >= rowwords1 - 1) by the following logic. + * We can pretend that idelx <= 31 because the >= 32 logic + * above adjusted everything appropriately. Then + * hicol <= wt + idelx <= wt + 31, so + * hicol + 31 <= wt + 62 + * rowwords1 = (hicol + 31) >> 5 <= (wt + 62) >> 5 + * rowwords2 == (wt + 31) >> 5, so + * rowwords1 <= rowwords2 + 1 */ + if (rowwords2 < rowwords1) { + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + /* Do the first iteration so the loop can be + * branch-free. */ + word1 = row1[0]; + word2 = row2[0] >> idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + for (x = 1; x < rowwords2; x++) { + word1 = row1[x]; + word2 = (row2[x] >> idelx) | + (row2[x - 1] << (32 - idelx)); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + /* Now the last iteration - we know that this is safe + * (i.e. rowwords1 >= 2) because rowwords1 > rowwords2 + * > 0 (if it was 0, we'd have taken the "count = 0" + * fast-path out of here). */ + word1 = row1[x]; + word2 = row2[x - 1] << (32 - idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + } else { + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + /* Do the first iteration so the loop can be + * branch-free. This section is the same as above + * except for the different limit on the loop, since + * the last iteration is the same as all the other + * iterations (beyond the first). */ + word1 = row1[0]; + word2 = row2[0] >> idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + for (x = 1; x < rowwords1; x++) { + word1 = row1[x]; + word2 = (row2[x] >> idelx) | + (row2[x - 1] << (32 - idelx)); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + } + } + } else { + /* pix2 is shifted to the left. word 0 of pix1 is touched by + * word 0 and word 1 of pix2, and so on up to the last word of + * pix1 (word N), which is touched by words N and N+1 of + * pix2... if there is a word N+1. Handle the two cases (pix2 + * has N words and pix2 has at least N+1 words) separately. */ + if (rowwords1 < rowwords2) { + /* pix2 has at least N+1 words, so every iteration through + * the loop can be the same. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1; x++) { + word1 = row1[x]; + word2 = row2[x] << -idelx; + word2 |= row2[x + 1] >> (32 + idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + } + } else { + /* pix2 has only N words, so the last iteration is broken + * out. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1 - 1; x++) { + word1 = row1[x]; + word2 = row2[x] << -idelx; + word2 |= row2[x + 1] >> (32 + idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + word1 = row1[x]; + word2 = row2[x] << -idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + } + } + } + + *pscore = (l_float32)count * (l_float32)count / + ((l_float32)area1 * (l_float32)area2); +/* fprintf(stderr, "score = %5.3f, count = %d, area1 = %d, area2 = %d\n", + *pscore, count, area1, area2); */ + return 0; +} + + +/*! + * \brief pixCorrelationScoreThresholded() + * + * \param[in] pix1 test pix, 1 bpp + * \param[in] pix2 exemplar pix, 1 bpp + * \param[in] area1 number of on pixels in pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] delx x comp of centroid difference + * \param[in] dely y comp of centroid difference + * \param[in] maxdiffw max width difference of pix1 and pix2 + * \param[in] maxdiffh max height difference of pix1 and pix2 + * \param[in] tab sum tab for byte + * \param[in] downcount count of 1 pixels below each row of pix1 + * \param[in] score_threshold + * \return whether the correlation score is >= score_threshold + * + * + *
+ * Notes:
+ *  We check first that the two pix are roughly the same size.
+ *  Only if they meet that criterion do we compare the bitmaps.
+ *  The centroid difference is used to align the two images to the
+ *  nearest integer for the correlation.
+ *
+ *  The correlation score is the ratio of the square of the number of
+ *  pixels in the AND of the two bitmaps to the product of the number
+ *  of ON pixels in each.  Denote the number of ON pixels in pix1
+ *  by |1|, the number in pix2 by |2|, and the number in the AND
+ *  of pix1 and pix2 by |1 & 2|.  The correlation score is then
+ *  (|1 & 2|)**2 / (|1|*|2|).
+ *
+ *  This score is compared with an input threshold, which can
+ *  be modified depending on the weight of the template.
+ *  The modified threshold is
+ *     thresh + (1.0 - thresh) * weight * R
+ *  where
+ *     weight is a fixed input factor between 0.0 and 1.0
+ *     R = |2| / area(2)
+ *  and area(2) is the total number of pixels in 2 (i.e., width x height).
+ *
+ *  To understand why a weight factor is useful, consider what happens
+ *  with thick, sans-serif characters that look similar and have a value
+ *  of R near 1.  Different characters can have a high correlation value,
+ *  and the classifier will make incorrect substitutions.  The weight
+ *  factor raises the threshold for these characters.
+ *
+ *  Yet another approach to reduce such substitutions is to run the classifier
+ *  in a non-greedy way, matching to the template with the highest
+ *  score, not the first template with a score satisfying the matching
+ *  constraint.  However, this is not particularly effective.
+ *
+ *  This very fast correlation matcher was contributed by William Rucklidge.
+ * 
+ */ +l_int32 +pixCorrelationScoreThresholded(PIX *pix1, + PIX *pix2, + l_int32 area1, + l_int32 area2, + l_float32 delx, /* x(1) - x(3) */ + l_float32 dely, /* y(1) - y(3) */ + l_int32 maxdiffw, + l_int32 maxdiffh, + l_int32 *tab, + l_int32 *downcount, + l_float32 score_threshold) +{ +l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; +l_int32 wpl1, wpl2, lorow, hirow, locol, hicol, untouchable; +l_int32 x, y, pix1lskip, pix2lskip, rowwords1, rowwords2; +l_uint32 word1, word2, andw; +l_uint32 *row1, *row2; +l_float32 score; +l_int32 threshold; + + PROCNAME("pixCorrelationScoreThresholded"); + + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 0); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 undefined or not 1 bpp", procName, 0); + if (!tab) + return ERROR_INT("tab not defined", procName, 0); + if (area1 <= 0 || area2 <= 0) + return ERROR_INT("areas must be > 0", procName, 0); + + /* Eliminate based on size difference */ + pixGetDimensions(pix1, &wi, &hi, NULL); + pixGetDimensions(pix2, &wt, &ht, NULL); + delw = L_ABS(wi - wt); + if (delw > maxdiffw) + return FALSE; + delh = L_ABS(hi - ht); + if (delh > maxdiffh) + return FALSE; + + /* Round difference to nearest integer */ + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + + /* Compute the correlation count that is needed so that + * count * count / (area1 * area2) >= score_threshold */ + threshold = (l_int32)ceil(sqrt((l_float64)score_threshold * area1 * area2)); + + count = 0; + wpl1 = pixGetWpl(pix1); + wpl2 = pixGetWpl(pix2); + rowwords2 = wpl2; + + /* What rows of pix1 need to be considered? Only those underlying the + * shifted pix2. */ + lorow = L_MAX(idely, 0); + hirow = L_MIN(ht + idely, hi); + + /* Get the pointer to the first row of each image that will be + * considered. */ + row1 = pixGetData(pix1) + wpl1 * lorow; + row2 = pixGetData(pix2) + wpl2 * (lorow - idely); + if (hirow <= hi) { + /* Some rows of pix1 will never contribute to count */ + untouchable = downcount[hirow - 1]; + } + + /* Similarly, figure out which columns of pix1 will be considered. */ + locol = L_MAX(idelx, 0); + hicol = L_MIN(wt + idelx, wi); + + if (idelx >= 32) { + /* pix2 is shifted far enough to the right that pix1's first + * word(s) won't contribute to the count. Increment its + * pointer to point to the first word that will contribute, + * and adjust other values accordingly. */ + pix1lskip = idelx >> 5; /* # of words to skip on left */ + row1 += pix1lskip; + locol -= pix1lskip << 5; + hicol -= pix1lskip << 5; + idelx &= 31; + } else if (idelx <= -32) { + /* pix2 is shifted far enough to the left that its first word(s) + * won't contribute to the count. Increment its pointer + * to point to the first word that will contribute, + * and adjust other values accordingly. */ + pix2lskip = -((idelx + 31) >> 5); /* # of words to skip on left */ + row2 += pix2lskip; + rowwords2 -= pix2lskip; + idelx += pix2lskip << 5; + } + + if ((locol >= hicol) || (lorow >= hirow)) { /* there is no overlap */ + count = 0; + } else { + /* How many words of each row of pix1 need to be considered? */ + rowwords1 = (hicol + 31) >> 5; + + if (idelx == 0) { + /* There's no lateral offset; simple case. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1; x++) { + andw = row1[x] & row2[x]; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + /* If the count is over the threshold, no need to + * calculate any further. Likewise, return early if the + * count plus the maximum count attainable from further + * rows is below the threshold. */ + if (count >= threshold) return TRUE; + if (count + downcount[y] - untouchable < threshold) { + return FALSE; + } + } + } else if (idelx > 0) { + /* pix2 is shifted to the right. word 0 of pix1 is touched by + * word 0 of pix2; word 1 of pix1 is touched by word 0 and word + * 1 of pix2, and so on up to the last word of pix1 (word N), + * which is touched by words N-1 and N of pix1... if there is a + * word N. Handle the two cases (pix2 has N-1 words and pix2 + * has at least N words) separately. + * + * Note: we know that pix2 has at least N-1 words (i.e., + * rowwords2 >= rowwords1 - 1) by the following logic. + * We can pretend that idelx <= 31 because the >= 32 logic + * above adjusted everything appropriately. Then + * hicol <= wt + idelx <= wt + 31, so + * hicol + 31 <= wt + 62 + * rowwords1 = (hicol + 31) >> 5 <= (wt + 62) >> 5 + * rowwords2 == (wt + 31) >> 5, so + * rowwords1 <= rowwords2 + 1 */ + if (rowwords2 < rowwords1) { + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + /* Do the first iteration so the loop can be + * branch-free. */ + word1 = row1[0]; + word2 = row2[0] >> idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + for (x = 1; x < rowwords2; x++) { + word1 = row1[x]; + word2 = (row2[x] >> idelx) | + (row2[x - 1] << (32 - idelx)); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + /* Now the last iteration - we know that this is safe + * (i.e. rowwords1 >= 2) because rowwords1 > rowwords2 + * > 0 (if it was 0, we'd have taken the "count = 0" + * fast-path out of here). */ + word1 = row1[x]; + word2 = row2[x - 1] << (32 - idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + if (count >= threshold) return TRUE; + if (count + downcount[y] - untouchable < threshold) { + return FALSE; + } + } + } else { + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + /* Do the first iteration so the loop can be + * branch-free. This section is the same as above + * except for the different limit on the loop, since + * the last iteration is the same as all the other + * iterations (beyond the first). */ + word1 = row1[0]; + word2 = row2[0] >> idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + for (x = 1; x < rowwords1; x++) { + word1 = row1[x]; + word2 = (row2[x] >> idelx) | + (row2[x - 1] << (32 - idelx)); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + if (count >= threshold) return TRUE; + if (count + downcount[y] - untouchable < threshold) { + return FALSE; + } + } + } + } else { + /* pix2 is shifted to the left. word 0 of pix1 is touched by + * word 0 and word 1 of pix2, and so on up to the last word of + * pix1 (word N), which is touched by words N and N+1 of + * pix2... if there is a word N+1. Handle the two cases (pix2 + * has N words and pix2 has at least N+1 words) separately. */ + if (rowwords1 < rowwords2) { + /* pix2 has at least N+1 words, so every iteration through + * the loop can be the same. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1; x++) { + word1 = row1[x]; + word2 = row2[x] << -idelx; + word2 |= row2[x + 1] >> (32 + idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + if (count >= threshold) return TRUE; + if (count + downcount[y] - untouchable < threshold) { + return FALSE; + } + } + } else { + /* pix2 has only N words, so the last iteration is broken + * out. */ + for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { + for (x = 0; x < rowwords1 - 1; x++) { + word1 = row1[x]; + word2 = row2[x] << -idelx; + word2 |= row2[x + 1] >> (32 + idelx); + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + } + + word1 = row1[x]; + word2 = row2[x] << -idelx; + andw = word1 & word2; + count += tab[andw & 0xff] + + tab[(andw >> 8) & 0xff] + + tab[(andw >> 16) & 0xff] + + tab[andw >> 24]; + + if (count >= threshold) return TRUE; + if (count + downcount[y] - untouchable < threshold) { + return FALSE; + } + } + } + } + } + + score = (l_float32)count * (l_float32)count / + ((l_float32)area1 * (l_float32)area2); + if (score >= score_threshold) { + fprintf(stderr, "count %d < threshold %d but score %g >= score_threshold %g\n", + count, threshold, score, score_threshold); + } + return FALSE; +} + + +/* -------------------------------------------------------------------- * + * Simple 2 pix correlators (for jbig2 clustering) * + * -------------------------------------------------------------------- */ +/*! + * \brief pixCorrelationScoreSimple() + * + * \param[in] pix1 test pix, 1 bpp + * \param[in] pix2 exemplar pix, 1 bpp + * \param[in] area1 number of on pixels in pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] delx x comp of centroid difference + * \param[in] dely y comp of centroid difference + * \param[in] maxdiffw max width difference of pix1 and pix2 + * \param[in] maxdiffh max height difference of pix1 and pix2 + * \param[in] tab sum tab for byte + * \param[out] pscore correlation score, in range [0.0 ... 1.0] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This calculates exactly the same value as pixCorrelationScore().
+ *          It is 2-3x slower, but much simpler to understand.
+ *      (2) The returned correlation score is 0.0 if the width or height
+ *          exceed %maxdiffw or %maxdiffh.
+ * 
+ */ +l_ok +pixCorrelationScoreSimple(PIX *pix1, + PIX *pix2, + l_int32 area1, + l_int32 area2, + l_float32 delx, /* x(1) - x(3) */ + l_float32 dely, /* y(1) - y(3) */ + l_int32 maxdiffw, + l_int32 maxdiffh, + l_int32 *tab, + l_float32 *pscore) +{ +l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; +PIX *pixt; + + PROCNAME("pixCorrelationScoreSimple"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); + if (!tab) + return ERROR_INT("tab not defined", procName, 1); + if (!area1 || !area2) + return ERROR_INT("areas must be > 0", procName, 1); + + /* Eliminate based on size difference */ + pixGetDimensions(pix1, &wi, &hi, NULL); + pixGetDimensions(pix2, &wt, &ht, NULL); + delw = L_ABS(wi - wt); + if (delw > maxdiffw) + return 0; + delh = L_ABS(hi - ht); + if (delh > maxdiffh) + return 0; + + /* Round difference to nearest integer */ + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + + /* pixt = pixAnd(NULL, pix1, pix2), including shift. + * To insure that pixels are ON only within the + * intersection of pix1 and the shifted pix2: + * (1) Start with pixt cleared and equal in size to pix1. + * (2) Blit the shifted pix2 onto pixt. Then all ON pixels + * are within the intersection of pix1 and the shifted pix2. + * (3) AND pix1 with pixt. */ + pixt = pixCreateTemplate(pix1); + pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix2, 0, 0); + pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC & PIX_DST, pix1, 0, 0); + pixCountPixels(pixt, &count, tab); + pixDestroy(&pixt); + + *pscore = (l_float32)count * (l_float32)count / + ((l_float32)area1 * (l_float32)area2); +/* fprintf(stderr, "score = %5.3f, count = %d, area1 = %d, area2 = %d\n", + *pscore, count, area1, area2); */ + return 0; +} + + +/*! + * \brief pixCorrelationScoreShifted() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \param[in] area1 number of on pixels in pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] delx x translation of pix2 relative to pix1 + * \param[in] dely y translation of pix2 relative to pix1 + * \param[in] tab sum tab for byte + * \param[out] pscore correlation score + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This finds the correlation between two 1 bpp images,
+ *          when pix2 is shifted by (delx, dely) with respect
+ *          to each other.
+ *      (2) This is implemented by starting with a copy of pix1 and
+ *          ANDing its pixels with those of a shifted pix2.
+ *      (3) Get the pixel counts for area1 and area2 using piCountPixels().
+ *      (4) A good estimate for a shift that would maximize the correlation
+ *          is to align the centroids (cx1, cy1; cx2, cy2), giving the
+ *          relative translations etransx and etransy:
+ *             etransx = cx1 - cx2
+ *             etransy = cy1 - cy2
+ *          Typically delx is chosen to be near etransx; ditto for dely.
+ *          This function is used in pixBestCorrelation(), where the
+ *          translations delx and dely are varied to find the best alignment.
+ *      (5) We do not check the sizes of pix1 and pix2, because they should
+ *          be comparable.
+ * 
+ */ +l_ok +pixCorrelationScoreShifted(PIX *pix1, + PIX *pix2, + l_int32 area1, + l_int32 area2, + l_int32 delx, + l_int32 dely, + l_int32 *tab, + l_float32 *pscore) +{ +l_int32 w1, h1, w2, h2, count; +PIX *pixt; + + PROCNAME("pixCorrelationScoreShifted"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); + if (!tab) + return ERROR_INT("tab not defined", procName, 1); + if (!area1 || !area2) + return ERROR_INT("areas must be > 0", procName, 1); + + pixGetDimensions(pix1, &w1, &h1, NULL); + pixGetDimensions(pix2, &w2, &h2, NULL); + + /* To insure that pixels are ON only within the + * intersection of pix1 and the shifted pix2: + * (1) Start with pixt cleared and equal in size to pix1. + * (2) Blit the shifted pix2 onto pixt. Then all ON pixels + * are within the intersection of pix1 and the shifted pix2. + * (3) AND pix1 with pixt. */ + pixt = pixCreateTemplate(pix1); + pixRasterop(pixt, delx, dely, w2, h2, PIX_SRC, pix2, 0, 0); + pixRasterop(pixt, 0, 0, w1, h1, PIX_SRC & PIX_DST, pix1, 0, 0); + pixCountPixels(pixt, &count, tab); + pixDestroy(&pixt); + + *pscore = (l_float32)count * (l_float32)count / + ((l_float32)area1 * (l_float32)area2); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dewarp.h b/hgdriver/3rdparty/hgOCR/leptonica/dewarp.h new file mode 100644 index 0000000..37bfb63 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dewarp.h @@ -0,0 +1,191 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_DEWARP_H +#define LEPTONICA_DEWARP_H + +/*! + * \file dewarp.h + * + *
+ *     Data structure to hold arrays and results for generating
+ *     horizontal and vertical disparity arrays based on textlines.
+ *     Each disparity array is two-dimensional.  The vertical disparity
+ *     array gives a vertical displacement, relative to the lowest point
+ *     in the textlines.  The horizontal disparty array gives a horizontal
+ *     displacement, relative to the minimum values (for even pages)
+ *     or maximum values (for odd pages) of the left and right ends of
+ *     full textlines.  Horizontal alignment always involves translations
+ *     away from the book gutter.
+ *
+ *     We have intentionally separated the process of building models
+ *     from the rendering process that uses the models.  For any page,
+ *     the building operation either creates an actual model (that is,
+ *     a model with at least the vertical disparity being computed, and
+ *     for which the 'success' flag is set) or fails to create a model.
+ *     However, at rendering time, a page can have one of two different
+ *     types of models.
+ *     (1) A valid model is an actual model that meets the rendering
+ *         constraints, which are limits on model curvature parameters.
+ *         See dewarpaTestForValidModel() for details.
+ *         Valid models are identified by dewarpaInsertRefModels(),
+ *         which sets the 'vvalid' and 'hvalid' fields.  Only valid
+ *         models are used for rendering.
+ *     (2) A reference model is used by a page that doesn't have
+ *         a valid model, but has a nearby valid model of the same
+ *         parity (even/odd page) that it can use.  The range in pages
+ *         to search for a valid model is given by the 'maxdist' field.
+ *
+ *     At the rendering stage, vertical and horizontal disparities are
+ *     treated differently.  It is somewhat more robust to generate
+ *     vertical disparity models (VDM) than horizontal disparity
+ *     models (HDM). A valid VDM is required for any correction to
+ *     be made; if a valid VDM is not available, just use the input
+ *     image.  Otherwise, assuming it is available, the use of the
+ *     HDM is controlled by two fields: 'useboth' and 'check_columns'.
+ *       (a) With useboth == 0, we use only the VDM.
+ *       (b) With useboth == 1, we require using the VDM and, if a valid
+ *           horizontal disparity model (HDM) is available, we also use it.
+ *       (c) With check_columns == 1, check for multiple columns and if
+ *           true, only use the VDM, even if a valid HDM is available.
+ *           Note that 'check_columns' takes precedence over 'useboth'
+ *           when there is more than 1 column of text.  By default,
+ *           check_columns == 0.
+ *
+ *     The 'maxdist' parameter is input when the dewarpa is created.
+ *     The other rendering parameters have default values given in dewarp1.c.
+ *     All parameters used by rendering can be set (or reset) using accessors.
+ *
+ *     After dewarping, use of the VDM will cause all points on each
+ *     altered curve to have a y-value equal to the minimum.  Use of
+ *     the HDA will cause the left and right edges of the textlines
+ *     to be vertically aligned if they had been typeset flush-left
+ *     and flush-right, respectively.
+ *
+ *     The sampled disparity arrays are expanded to full resolution,
+ *     using linear interpolation, and this is further expanded
+ *     by slope continuation to the right and below if the image
+ *     is larger than the full resolution disparity arrays.  Then
+ *     the disparity correction can be applied to the input image.
+ *     If the input pix are 2x reduced, the expansion from sampled
+ *     to full res uses the product of (sampling) * (redfactor).
+ *
+ *     The most accurate results are produced at full resolution, and
+ *     this is generally recommended.
+ * 
+ */ + + /*! Dewarp version for serialization + *
+     * Note on versioning of the serialization of this data structure:
+     * The dewarping utility and the stored data can be expected to change.
+     * In most situations, the serialized version is ephemeral -- it is
+     * not needed after being used.  No functions will be provided to
+     * convert between different versions.
+     * 
+ */ +#define DEWARP_VERSION_NUMBER 4 + +/*! Data structure to hold a number of Dewarp */ +struct L_Dewarpa +{ + l_int32 nalloc; /*!< size of dewarp ptr array */ + l_int32 maxpage; /*!< maximum page number in array */ + struct L_Dewarp **dewarp; /*!< array of ptrs to page dewarp */ + struct L_Dewarp **dewarpcache; /*!< array of ptrs to cached dewarps */ + struct Numa *namodels; /*!< list of page numbers for pages */ + /*!< with page models */ + struct Numa *napages; /*!< list of page numbers with either */ + /*!< page models or ref page models */ + l_int32 redfactor; /*!< reduction factor of input: 1 or 2 */ + l_int32 sampling; /*!< disparity arrays sampling factor */ + l_int32 minlines; /*!< min number of long lines required */ + l_int32 maxdist; /*!< max distance for getting ref page */ + l_int32 max_linecurv; /*!< maximum abs line curvature, */ + /*!< in micro-units */ + l_int32 min_diff_linecurv; /*!< minimum abs diff line */ + /*!< curvature in micro-units */ + l_int32 max_diff_linecurv; /*!< maximum abs diff line */ + /*!< curvature in micro-units */ + l_int32 max_edgeslope; /*!< maximum abs left or right edge */ + /*!< slope, in milli-units */ + l_int32 max_edgecurv; /*!< maximum abs left or right edge */ + /*!< curvature, in micro-units */ + l_int32 max_diff_edgecurv; /*!< maximum abs diff left-right */ + /*!< edge curvature, in micro-units */ + l_int32 useboth; /*!< use both disparity arrays if */ + /*!< available; only vertical otherwise */ + l_int32 check_columns; /*!< if there are multiple columns, */ + /*!< only use the vertical disparity */ + /*!< array */ + l_int32 modelsready; /*!< invalid models have been removed */ + /*!< and refs built against valid set */ +}; +typedef struct L_Dewarpa L_DEWARPA; + + +/*! Data structure for a single dewarp */ +struct L_Dewarp +{ + struct L_Dewarpa *dewa; /*!< ptr to parent (not owned) */ + struct Pix *pixs; /*!< source pix, 1 bpp */ + struct FPix *sampvdispar; /*!< sampled vert disparity array */ + struct FPix *samphdispar; /*!< sampled horiz disparity array */ + struct FPix *sampydispar; /*!< sampled slope h-disparity array */ + struct FPix *fullvdispar; /*!< full vert disparity array */ + struct FPix *fullhdispar; /*!< full horiz disparity array */ + struct FPix *fullydispar; /*!< full slope h-disparity array */ + struct Numa *namidys; /*!< sorted y val of midpoint each line */ + struct Numa *nacurves; /*!< sorted curvature of each line */ + l_int32 w; /*!< width of source image */ + l_int32 h; /*!< height of source image */ + l_int32 pageno; /*!< page number; important for reuse */ + l_int32 sampling; /*!< sampling factor of disparity arrays */ + l_int32 redfactor; /*!< reduction factor of pixs: 1 or 2 */ + l_int32 minlines; /*!< min number of long lines required */ + l_int32 nlines; /*!< number of long lines found */ + l_int32 mincurv; /*!< min line curvature in micro-units */ + l_int32 maxcurv; /*!< max line curvature in micro-units */ + l_int32 leftslope; /*!< left edge slope in milli-units */ + l_int32 rightslope; /*!< right edge slope in milli-units */ + l_int32 leftcurv; /*!< left edge curvature in micro-units */ + l_int32 rightcurv; /*!< right edge curvature in micro-units*/ + l_int32 nx; /*!< number of sampling pts in x-dir */ + l_int32 ny; /*!< number of sampling pts in y-dir */ + l_int32 hasref; /*!< 0 if normal; 1 if has a refpage */ + l_int32 refpage; /*!< page with disparity model to use */ + l_int32 vsuccess; /*!< sets to 1 if vert disparity builds */ + l_int32 hsuccess; /*!< sets to 1 if horiz disparity builds */ + l_int32 ysuccess; /*!< sets to 1 if slope disparity builds */ + l_int32 vvalid; /*!< sets to 1 if valid vert disparity */ + l_int32 hvalid; /*!< sets to 1 if valid horiz disparity */ + l_int32 skip_horiz; /*!< if 1, skip horiz disparity */ + /*!< correction */ + l_int32 debug; /*!< set to 1 if debug output requested */ +}; +typedef struct L_Dewarp L_DEWARP; + +#endif /* LEPTONICA_DEWARP_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dewarp1.c b/hgdriver/3rdparty/hgOCR/leptonica/dewarp1.c new file mode 100644 index 0000000..528033a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dewarp1.c @@ -0,0 +1,1697 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dewarp1.c + *
+ *
+ *    Basic operations and serialization
+ *
+ *      Create/destroy dewarp
+ *          L_DEWARP          *dewarpCreate()
+ *          L_DEWARP          *dewarpCreateRef()
+ *          void               dewarpDestroy()
+ *
+ *      Create/destroy dewarpa
+ *          L_DEWARPA         *dewarpaCreate()
+ *          L_DEWARPA         *dewarpaCreateFromPixacomp()
+ *          void               dewarpaDestroy()
+ *          l_int32            dewarpaDestroyDewarp()
+ *
+ *      Dewarpa insertion/extraction
+ *          l_int32            dewarpaInsertDewarp()
+ *          static l_int32     dewarpaExtendArraysToSize()
+ *          L_DEWARP          *dewarpaGetDewarp()
+ *
+ *      Setting parameters to control rendering from the model
+ *          l_int32            dewarpaSetCurvatures()
+ *          l_int32            dewarpaUseBothArrays()
+ *          l_int32            dewarpaSetCheckColumns()
+ *          l_int32            dewarpaSetMaxDistance()
+ *
+ *      Dewarp serialized I/O
+ *          L_DEWARP          *dewarpRead()
+ *          L_DEWARP          *dewarpReadStream()
+ *          L_DEWARP          *dewarpReadMem()
+ *          l_int32            dewarpWrite()
+ *          l_int32            dewarpWriteStream()
+ *          l_int32            dewarpWriteMem()
+ *
+ *      Dewarpa serialized I/O
+ *          L_DEWARPA         *dewarpaRead()
+ *          L_DEWARPA         *dewarpaReadStream()
+ *          L_DEWARPA         *dewarpaReadMem()
+ *          l_int32            dewarpaWrite()
+ *          l_int32            dewarpaWriteStream()
+ *          l_int32            dewarpaWriteMem()
+ *
+ *
+ *  Examples of usage
+ *  =================
+ *
+ *  See dewarpaCreateFromPixacomp() for an example of the basic
+ *  operations, starting from a set of 1 bpp images.
+ *
+ *  Basic functioning to dewarp a specific single page:
+ * \code
+ *     // Make the Dewarpa for the pages
+ *     L_Dewarpa *dewa = dewarpaCreate(1, 30, 1, 15, 50);
+ *     dewarpaSetCurvatures(dewa, -1, 5, -1, -1, -1, -1);
+ *     dewarpaUseBothArrays(dewa, 1);  // try to use both disparity
+ *                                     // arrays for this example
+ *
+ *     // Do the page: start with a binarized image
+ *     Pix *pixb = "binarize"(pixs);
+ *     // Initialize a Dewarp for this page (say, page 214)
+ *     L_Dewarp *dew = dewarpCreate(pixb, 214);
+ *     // Insert in Dewarpa and obtain parameters for building the model
+ *     dewarpaInsertDewarp(dewa, dew);
+ *     // Do the work
+ *     dewarpBuildPageModel(dew, NULL);  // no debugging
+ *     // Optionally set rendering parameters
+ *     // Apply model to the input pixs
+ *     Pix *pixd;
+ *     dewarpaApplyDisparity(dewa, 214, pixs, 255, 0, 0, &pixd, NULL);
+ *     pixDestroy(&pixb);
+ * \endcode
+ *
+ *  Basic functioning to dewarp many pages:
+ * \code
+ *     // Make the Dewarpa for the set of pages; use fullres 1 bpp
+ *     L_Dewarpa *dewa = dewarpaCreate(10, 30, 1, 15, 50);
+ *     // Optionally set rendering parameters
+ *     dewarpaSetCurvatures(dewa, -1, 10, -1, -1, -1, -1);
+ *     dewarpaUseBothArrays(dewa, 0);  // just use the vertical disparity
+ *                                     // array for this example
+ *
+ *     // Do first page: start with a binarized image
+ *     Pix *pixb = "binarize"(pixs);
+ *     // Initialize a Dewarp for this page (say, page 1)
+ *     L_Dewarp *dew = dewarpCreate(pixb, 1);
+ *     // Insert in Dewarpa and obtain parameters for building the model
+ *     dewarpaInsertDewarp(dewa, dew);
+ *     // Do the work
+ *     dewarpBuildPageModel(dew, NULL);  // no debugging
+ *     dewarpMinimze(dew);  // remove most heap storage
+ *     pixDestroy(&pixb);
+ *
+ *     // Do the other pages the same way
+ *     ...
+ *
+ *     // Apply models to each page; if the page model is invalid,
+ *     // try to use a valid neighboring model.  Note that the call
+ *     // to dewarpaInsertRefModels() is optional, because it is called
+ *     // by dewarpaApplyDisparity() on the first page it acts on.
+ *     dewarpaInsertRefModels(dewa, 0, 1); // use debug flag to get more
+ *                         // detailed information about the page models
+ *     [For each page, where pixs is the fullres image to be dewarped] {
+ *         L_Dewarp *dew = dewarpaGetDewarp(dewa, pageno);
+ *         if (dew) {  // disparity model exists
+ *             Pix *pixd;
+ *             dewarpaApplyDisparity(dewa, pageno, pixs, 255,
+ *                                   0, 0, &pixd, NULL);
+ *             dewarpMinimize(dew);  // clean out the pix and fpix arrays
+ *             // Squirrel pixd away somewhere ...)
+ *         }
+ *     }
+ * \endcode
+ *
+ *  Basic functioning to dewarp a small set of pages, potentially
+ *  using models from nearby pages:
+ * \code
+ *     // (1) Generate a set of binarized images in the vicinity of the
+ *     // pages to be dewarped.  We will attempt to compute models
+ *     // for pages from 'firstpage' to 'lastpage'.
+ *     // Store the binarized images in a compressed array of
+ *     // size 'n', where 'n' is the number of images to be stored,
+ *     // and where the offset is the first page.
+ *     PixaComp *pixac = pixacompCreateInitialized(n, firstpage, NULL,
+ *                                                 IFF_TIFF_G4);
+ *     for (i = firstpage; i <= lastpage; i++) {
+ *         Pix *pixb = "binarize"(pixs);
+ *         pixacompReplacePix(pixac, i, pixb, IFF_TIFF_G4);
+ *         pixDestroy(&pixb);
+ *     }
+ *
+ *     // (2) Make the Dewarpa for the pages.
+ *     L_Dewarpa *dewa =
+ *           dewarpaCreateFromPixacomp(pixac, 30, 15, 20);
+ *     dewarpaUseBothArrays(dewa, 1);  // try to use both disparity arrays
+ *                                     // in this example
+ *
+ *     // (3) Finally, apply the models.  For page 'firstpage' with image pixs:
+ *     L_Dewarp *dew = dewarpaGetDewarp(dewa, firstpage);
+ *     if (dew) {  // disparity model exists
+ *         Pix *pixd;
+ *         dewarpaApplyDisparity(dewa, firstpage, pixs, 255, 0, 0, &pixd, NULL);
+ *         dewarpMinimize(dew);
+ *     }
+ * \endcode
+ *
+ *  Because in general some pages will not have enough text to build a
+ *  model, we fill in for those pages with a reference to the page
+ *  model to use.  Both the target page and the reference page must
+ *  have the same parity.  We can also choose to use either a partial model
+ *  (with only vertical disparity) or the full model of a nearby page.
+ *
+ *  Minimizing the data in a model by stripping out images,
+ *  numas, and full resolution disparity arrays:
+ *     dewarpMinimize(dew);
+ *  This can be done at any time to save memory.  Serialization does
+ *  not use the data that is stripped.
+ *
+ *  You can apply any model (in a dew), stripped or not, to another image:
+ * \code
+ *     // For all pages with invalid models, assign the nearest valid
+ *     // page model with same parity.
+ *     dewarpaInsertRefModels(dewa, 0, 0);
+ *     // You can then apply to 'newpix' the page model that was assigned
+ *     // to 'pageno', giving the result in pixd:
+ *     Pix *pixd;
+ *     dewarpaApplyDisparity(dewa, pageno, newpix, 255, 0, 0, &pixd, NULL);
+ * \endcode
+ *
+ *  You can apply the disparity arrays to a deliberately undercropped
+ *  image.  Suppose that you undercrop by (left, right, top, bot), so
+ *  that the disparity arrays are aligned with their origin at (left, top).
+ *  Dewarp the undercropped image with:
+ * \code
+ *     Pix *pixd;
+ *     dewarpaApplyDisparity(dewa, pageno, undercropped_pix, 255,
+ *                           left, top, &pixd, NULL);
+ * \endcode
+ *
+ *  Description of the approach to analyzing page image distortion
+ *  ==============================================================
+ *
+ *  When a book page is scanned, there are several possible causes
+ *  for the text lines to appear to be curved:
+ *   (1) A barrel (fish-eye) effect because the camera is at
+ *       a finite distance from the page.  Take the normal from
+ *       the camera to the page (the 'optic axis').  Lines on
+ *       the page "below" this point will appear to curve upward
+ *       (negative curvature); lines "above" this will curve downward.
+ *   (2) Radial distortion from the camera lens.  Probably not
+ *       a big factor.
+ *   (3) Local curvature of the page in to (or out of) the image
+ *       plane (which is perpendicular to the optic axis).
+ *       This has no effect if the page is flat.
+ *
+ *  In the following, the optic axis is in the z direction and is
+ *  perpendicular to the xy plane;, the book is assumed to be aligned
+ *  so that y is approximately along the binding.
+ *  The goal is to compute the "disparity" field, D(x,y), which
+ *  is actually a vector composed of the horizontal and vertical
+ *  disparity fields H(x,y) and V(x,y).  Each of these is a local
+ *  function that gives the amount each point in the image is
+ *  required to move in order to rectify the horizontal and vertical
+ *  lines.  It would also be nice to "flatten" the page to compensate
+ *  for effect (3), foreshortening due to bending of the page into
+ *  the z direction, but that is more difficult.
+ *
+ *  Effects (1) and (2) can be directly compensated by calibrating
+ *  the scene, using a flat page with horizontal and vertical lines.
+ *  Then H(x,y) and V(x,y) can be found as two (non-parametric) arrays
+ *  of values.  Suppose this has been done.  Then the remaining
+ *  distortion is due to (3).
+ *
+ *  We consider the simple situation where the page bending is independent
+ *  of y, and is described by alpha(x), where alpha is the angle between
+ *  the normal to the page and the optic axis.  cos(alpha(x)) is the local
+ *  compression factor of the page image in the horizontal direction, at x.
+ *  Thus, if we know alpha(x), we can compute the disparity H(x) required
+ *  to flatten the image by simply integrating 1/cos(alpha), and we could
+ *  compute the remaining disparities, H(x,y) and V(x,y), from the
+ *  page content, as described below.  Unfortunately, we don't know
+ *  alpha.  What do we know?  If there are horizontal text lines
+ *  on the page, we can compute the vertical disparity, V(x,y), which
+ *  is the local translation required to make the text lines parallel
+ *  to the rasters.  If the margins are left and right aligned, we can
+ *  also estimate the horizontal disparity, H(x,y), required to have
+ *  uniform margins.  All that can be done from the image alone,
+ *  assuming we have text lines covering a sufficient part of the page.
+ *
+ *  What about alpha(x)?  The basic question relating to (3) is this:
+ *
+ *     Is it possible, using the shape of the text lines alone,
+ *     to compute both the vertical and horizontal disparity fields?
+ *
+ *  The underlying problem is to separate the line curvature effects due
+ *  to the camera view from those due to actual bending of the page.
+ *  I believe the proper way to do this is to make some measurements
+ *  based on the camera setup, which will depend mostly on the distance
+ *  of the camera from the page, and to a smaller extent on the location
+ *  of the optic axis with respect to the page.
+ *
+ *  Here is the procedure.  Photograph a page with a fine 2D line grid
+ *  several times, each with a different slope near the binding.
+ *  This can be done by placing the grid page on books that have
+ *  different shapes z(x) near the binding.  For each one you can
+ *  measure, near the binding:
+ *    (1) ds/dy, the vertical rate of change of slope of the horizontal lines
+ *    (2) the local horizontal compression of the vertical lines due
+ *        to the page angle dz/dx.
+ *  As mentioned above, the local horizontal compression is simply
+ *  cos(dz/dx).  But the measurement you can make on an actual book
+ *  page is (1).  The difficulty is to generate (2) from (1).
+ *
+ *  Back to the procedure.  The function in (1), ds/dy, likely needs
+ *  to be measured at a few y locations, because the relation
+ *  between (1) and (2) may weakly depend on the y-location with
+ *  respect to the y-coordinate of the optic axis of the camera.
+ *  From these measurements you can determine, for the camera setup
+ *  that you have, the local horizontal compression, cos(dz/dx), as a
+ *  function of the both vertical location (y) and your measured vertical
+ *  derivative of the text line slope there, ds/dy.  Then with
+ *  appropriate smoothing of your measured values, you can set up a
+ *  horizontal disparity array to correct for the compression due
+ *  to dz/dx.
+ *
+ *  Now consider V(x,0) and V(x,h), the vertical disparity along
+ *  the top and bottom of the image.  With a little thought you
+ *  can convince yourself that the local foreshortening,
+ *  as a function of x, is proportional to the difference
+ *  between the slope of V(x,0) and V(x,h).  The horizontal
+ *  disparity can then be computed by integrating the local foreshortening
+ *  over x.  Integration of the slope of V(x,0) and V(x,h) gives
+ *  the vertical disparity itself.  We have to normalize to h, the
+ *  height of the page.  So the very simple result is that
+ *
+ *      H(x) ~ (V(x,0) - V(x,h)) / h         [1]
+ *
+ *  which is easily computed.  There is a proportionality constant
+ *  that depends on the ratio of h to the distance to the camera.
+ *  Can we actually believe this for the case where the bending
+ *  is independent of y?  I believe the answer is yes,
+ *  as long as you first remove the apparent distortion due
+ *  to the camera being at a finite distance.
+ *
+ *  If you know the intersection of the optical axis with the page
+ *  and the distance to the camera, and if the page is perpendicular
+ *  to the optic axis, you can compute the horizontal and vertical
+ *  disparities due to (1) and (2) and remove them.  The resulting
+ *  distortion should be entirely due to bending (3), for which
+ *  the relation
+ *
+ *      Hx(x) dx = C * ((Vx(x,0) - Vx(x, h))/h) dx         [2]
+ *
+ *  holds for each point in x (Hx and Vx are partial derivatives w/rt x).
+ *  Integrating over x, and using H(0) = 0, we get the result [1].
+ *
+ *  I believe this result holds differentially for each value of y, so
+ *  that in the case where the bending is not independent of y,
+ *  the expression (V(x,0) - V(x,h)) / h goes over to Vy(x,y).  Then
+ *
+ *     H(x,y) = Integral(0,x) (Vyx(x,y) dx)         [3]
+ *
+ *  where Vyx() is the partial derivative of V w/rt both x and y.
+ *
+ *  It would be nice if there were a simple mathematical relation between
+ *  the horizontal and vertical disparities for the situation
+ *  where the paper bends without stretching or kinking.
+ *  I had hoped to get a relation between H and V, such as
+ *  Hx(x,y) ~ Vy(x,y), which would imply that H and V are real
+ *  and imaginary parts of a complex potential, each of which
+ *  satisfy the laplace equation.  But then the gradients of the
+ *  two potentials would be normal, and that does not appear to be the case.
+ *  Thus, the questions of proving the relations above (for small bending),
+ *  or finding a simpler relation between H and V than those equations,
+ *  remain open.  So far, we have only used [1] for the horizontal
+ *  disparity H(x).
+ *
+ *  In the version of the code that follows, we first use text lines
+ *  to find V(x,y).  Then, we try to compute H(x,y) that will align
+ *  the text vertically on the left and right margins.  This is not
+ *  always possible -- sometimes the right margin is not right justified.
+ *  By default, we don't require the horizontal disparity to have a
+ *  valid page model for dewarping a page, but this requirement can
+ *  be forced using dewarpaUseFullModel().
+ *
+ *  As described above, one can add a y-independent component of
+ *  the horizontal disparity H(x) to counter the foreshortening
+ *  effect due to the bending of the page near the binding.
+ *  This requires widening the image on the side near the binding,
+ *  and we do not provide this option here.  However, we do provide
+ *  a function that will generate this disparity field:
+ *       fpixExtraHorizDisparity()
+ *
+ *  Here is the basic outline for building the disparity arrays.
+ *
+ *  (1) Find lines going approximately through the center of the
+ *      text in each text line.  Accept only lines that are
+ *      close in length to the longest line.
+ *  (2) Use these lines to generate a regular and highly subsampled
+ *      vertical disparity field V(x,y).
+ *  (3) Interpolate this to generate a full resolution vertical
+ *      disparity field.
+ *  (4) For lines that are sufficiently long, assume they are approximately
+ *      left and right-justified, and construct a highly subsampled
+ *      horizontal disparity field H(x,y) that will bring them into alignment.
+ *  (5) Interpolate this to generate a full resolution horizontal
+ *      disparity field.
+ *  (6) Apply the vertical dewarping, followed by the horizontal dewarping.
+ *
+ *  Step (1) is clearly described by the code in pixGetTextlineCenters().
+ *
+ *  Steps (2) and (3) follow directly from the data in step (1),
+ *  and constitute the bulk of the work done in dewarpBuildPageModel().
+ *  Virtually all the noise in the data is smoothed out by doing
+ *  least-square quadratic fits, first horizontally to the data
+ *  points representing the text line centers, and then vertically.
+ *  The trick is to sample these lines on a regular grid.
+ *  First each horizontal line is sampled at equally spaced
+ *  intervals horizontally.  We thus get a set of points,
+ *  one in each line, that are vertically aligned, and
+ *  the data we represent is the vertical distance of each point
+ *  from the min or max value on the curve, depending on the
+ *  sign of the curvature component.  Each of these vertically
+ *  aligned sets of points constitutes a sampled vertical disparity,
+ *  and we do a LS quartic fit to each of them, followed by
+ *  vertical sampling at regular intervals.  We now have a subsampled
+ *  grid of points, all equally spaced, giving at each point the local
+ *  vertical disparity.  Finally, the full resolution vertical disparity
+ *  is formed by interpolation.  All the least square fits do a
+ *  great job of smoothing everything out, as can be observed by
+ *  the contour maps that are generated for the vertical disparity field.
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_int32 dewarpaExtendArraysToSize(L_DEWARPA *dewa, l_int32 size); + + /* Parameter values used in dewarpaCreate() */ +static const l_int32 InitialPtrArraySize = 20; /* n'import quoi */ +static const l_int32 MaxPtrArraySize = 10000; +static const l_int32 DefaultArraySampling = 30; +static const l_int32 MinArraySampling = 8; +static const l_int32 DefaultMinLines = 15; +static const l_int32 MinMinLines = 4; +static const l_int32 DefaultMaxRefDist = 16; +static const l_int32 DefaultUseBoth = TRUE; +static const l_int32 DefaultCheckColumns = TRUE; + + /* Parameter values used in dewarpaSetCurvatures() */ +static const l_int32 DefaultMaxLineCurv = 150; +static const l_int32 DefaultMinDiffLineCurv = 0; +static const l_int32 DefaultMaxDiffLineCurv = 170; +static const l_int32 DefaultMaxEdgeCurv = 50; +static const l_int32 DefaultMaxDiffEdgeCurv = 40; +static const l_int32 DefaultMaxEdgeSlope = 80; + +/*----------------------------------------------------------------------* + * Create/destroy Dewarp * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpCreate() + * + * \param[in] pixs 1 bpp + * \param[in] pageno page number + * \return dew or NULL on error + * + *
+ * Notes:
+ *      (1) The input pixs is either full resolution or 2x reduced.
+ *      (2) The page number is typically 0-based.  If scanned from a book,
+ *          the even pages are usually on the left.  Disparity arrays
+ *          built for even pages should only be applied to even pages.
+ * 
+ */ +L_DEWARP * +dewarpCreate(PIX *pixs, + l_int32 pageno) +{ +L_DEWARP *dew; + + PROCNAME("dewarpCreate"); + + if (!pixs) + return (L_DEWARP *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (L_DEWARP *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); + dew->pixs = pixClone(pixs); + dew->pageno = pageno; + dew->w = pixGetWidth(pixs); + dew->h = pixGetHeight(pixs); + return dew; +} + + +/*! + * \brief dewarpCreateRef() + * + * \param[in] pageno this page number + * \param[in] refpage page number of dewarp disparity arrays to be used + * \return dew or NULL on error + * + *
+ * Notes:
+ *      (1) This specifies which dewarp struct should be used for
+ *          the given page.  It is placed in dewarpa for pages
+ *          for which no model can be built.
+ *      (2) This page and the reference page have the same parity and
+ *          the reference page is the closest page with a disparity model
+ *          to this page.
+ * 
+ */ +L_DEWARP * +dewarpCreateRef(l_int32 pageno, + l_int32 refpage) +{ +L_DEWARP *dew; + + PROCNAME("dewarpCreateRef"); + + dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); + dew->pageno = pageno; + dew->hasref = 1; + dew->refpage = refpage; + return dew; +} + + +/*! + * \brief dewarpDestroy() + * + * \param[in,out] pdew will be set to null before returning + * \return void + */ +void +dewarpDestroy(L_DEWARP **pdew) +{ +L_DEWARP *dew; + + PROCNAME("dewarpDestroy"); + + if (pdew == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + if ((dew = *pdew) == NULL) + return; + + pixDestroy(&dew->pixs); + fpixDestroy(&dew->sampvdispar); + fpixDestroy(&dew->samphdispar); + fpixDestroy(&dew->sampydispar); + fpixDestroy(&dew->fullvdispar); + fpixDestroy(&dew->fullhdispar); + fpixDestroy(&dew->fullydispar); + numaDestroy(&dew->namidys); + numaDestroy(&dew->nacurves); + LEPT_FREE(dew); + *pdew = NULL; + return; +} + + +/*----------------------------------------------------------------------* + * Create/destroy Dewarpa * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaCreate() + * + * \param[in] nptrs number of dewarp page ptrs; typ. the number of pages + * \param[in] sampling use 0 for default value; the minimum allowed is 8 + * \param[in] redfactor of input images: 1 is full res; 2 is 2x reduced + * \param[in] minlines minimum number of lines to accept; use 0 for default + * \param[in] maxdist for locating reference disparity; use -1 for default + * \return dewa or NULL on error + * + *
+ * Notes:
+ *      (1) The sampling, minlines and maxdist parameters will be
+ *          applied to all images.
+ *      (2) The sampling factor is used for generating the disparity arrays
+ *          from the input image.  For 2x reduced input, use a sampling
+ *          factor that is half the sampling you want on the full resolution
+ *          images.
+ *      (3) Use %redfactor = 1 for full resolution; 2 for 2x reduction.
+ *          All input images must be at one of these two resolutions.
+ *      (4) %minlines is the minimum number of nearly full-length lines
+ *          required to generate a vertical disparity array.  The default
+ *          number is 15.  Use a smaller number to accept a questionable
+ *          array, but not smaller than 4.
+ *      (5) When a model can't be built for a page, it looks up to %maxdist
+ *          in either direction for a valid model with the same page parity.
+ *          Use -1 for the default value of %maxdist; use 0 to avoid using
+ *          a ref model.
+ *      (6) The ptr array is expanded as necessary to accommodate page images.
+ * 
+ */ +L_DEWARPA * +dewarpaCreate(l_int32 nptrs, + l_int32 sampling, + l_int32 redfactor, + l_int32 minlines, + l_int32 maxdist) +{ +L_DEWARPA *dewa; + + PROCNAME("dewarpaCreate"); + + if (nptrs <= 0) + nptrs = InitialPtrArraySize; + if (nptrs > MaxPtrArraySize) + return (L_DEWARPA *)ERROR_PTR("too many pages", procName, NULL); + if (redfactor != 1 && redfactor != 2) + return (L_DEWARPA *)ERROR_PTR("redfactor not in {1,2}", + procName, NULL); + if (sampling == 0) { + sampling = DefaultArraySampling; + } else if (sampling < MinArraySampling) { + L_WARNING("sampling too small; setting to %d\n", procName, + MinArraySampling); + sampling = MinArraySampling; + } + if (minlines == 0) { + minlines = DefaultMinLines; + } else if (minlines < MinMinLines) { + L_WARNING("minlines too small; setting to %d\n", procName, + MinMinLines); + minlines = DefaultMinLines; + } + if (maxdist < 0) + maxdist = DefaultMaxRefDist; + + dewa = (L_DEWARPA *)LEPT_CALLOC(1, sizeof(L_DEWARPA)); + dewa->dewarp = (L_DEWARP **)LEPT_CALLOC(nptrs, sizeof(L_DEWARPA *)); + dewa->dewarpcache = (L_DEWARP **)LEPT_CALLOC(nptrs, sizeof(L_DEWARPA *)); + if (!dewa->dewarp || !dewa->dewarpcache) { + dewarpaDestroy(&dewa); + return (L_DEWARPA *)ERROR_PTR("dewarp ptrs not made", procName, NULL); + } + dewa->nalloc = nptrs; + dewa->sampling = sampling; + dewa->redfactor = redfactor; + dewa->minlines = minlines; + dewa->maxdist = maxdist; + dewa->max_linecurv = DefaultMaxLineCurv; + dewa->min_diff_linecurv = DefaultMinDiffLineCurv; + dewa->max_diff_linecurv = DefaultMaxDiffLineCurv; + dewa->max_edgeslope = DefaultMaxEdgeSlope; + dewa->max_edgecurv = DefaultMaxEdgeCurv; + dewa->max_diff_edgecurv = DefaultMaxDiffEdgeCurv; + dewa->check_columns = DefaultCheckColumns; + dewa->useboth = DefaultUseBoth; + return dewa; +} + + +/*! + * \brief dewarpaCreateFromPixacomp() + * + * \param[in] pixac pixacomp of G4, 1 bpp images; with 1x1x1 placeholders + * \param[in] useboth 0 for only vert disparity; 1 for both vert and horiz + * \param[in] sampling use -1 or 0 for default value; otherwise minimum of 5 + * \param[in] minlines minimum number of lines to accept; e.g., 10 + * \param[in] maxdist for locating reference disparity; use -1 for default + * \return dewa or NULL on error + * + *
+ * Notes:
+ *      (1) The returned dewa has disparity arrays calculated and
+ *          is ready for serialization or for use in dewarping.
+ *      (2) The sampling, minlines and maxdist parameters are
+ *          applied to all images.  See notes in dewarpaCreate() for details.
+ *      (3) The pixac is full.  Placeholders, if any, are w=h=d=1 images,
+ *          and the real input images are 1 bpp at full resolution.
+ *          They are assumed to be cropped to the actual page regions,
+ *          and may be arbitrarily sparse in the array.
+ *      (4) The output dewarpa is indexed by the page number.
+ *          The offset in the pixac gives the mapping between the
+ *          array index in the pixac and the page number.
+ *      (5) This adds the ref page models.
+ *      (6) This can be used to make models for any desired set of pages.
+ *          The direct models are only made for pages with images in
+ *          the pixacomp; the ref models are made for pages of the
+ *          same parity within %maxdist of the nearest direct model.
+ * 
+ */ +L_DEWARPA * +dewarpaCreateFromPixacomp(PIXAC *pixac, + l_int32 useboth, + l_int32 sampling, + l_int32 minlines, + l_int32 maxdist) +{ +l_int32 i, nptrs, pageno; +L_DEWARP *dew; +L_DEWARPA *dewa; +PIX *pixt; + + PROCNAME("dewarpaCreateFromPixacomp"); + + if (!pixac) + return (L_DEWARPA *)ERROR_PTR("pixac not defined", procName, NULL); + + nptrs = pixacompGetCount(pixac); + if ((dewa = dewarpaCreate(pixacompGetOffset(pixac) + nptrs, + sampling, 1, minlines, maxdist)) == NULL) + return (L_DEWARPA *)ERROR_PTR("dewa not made", procName, NULL); + dewarpaUseBothArrays(dewa, useboth); + + for (i = 0; i < nptrs; i++) { + pageno = pixacompGetOffset(pixac) + i; /* index into pixacomp */ + pixt = pixacompGetPix(pixac, pageno); + if (pixt && (pixGetWidth(pixt) > 1)) { + dew = dewarpCreate(pixt, pageno); + pixDestroy(&pixt); + if (!dew) { + ERROR_INT("unable to make dew!", procName, 1); + continue; + } + + /* Insert into dewa for this page */ + dewarpaInsertDewarp(dewa, dew); + + /* Build disparity arrays for this page */ + dewarpBuildPageModel(dew, NULL); + if (!dew->vsuccess) { /* will need to use model from nearby page */ + dewarpaDestroyDewarp(dewa, pageno); + L_ERROR("unable to build model for page %d\n", procName, i); + continue; + } + /* Remove all extraneous data */ + dewarpMinimize(dew); + } + pixDestroy(&pixt); + } + dewarpaInsertRefModels(dewa, 0, 0); + + return dewa; +} + + +/*! + * \brief dewarpaDestroy() + * + * \param[in,out] pdewa will be set to null before returning + * \return void + */ +void +dewarpaDestroy(L_DEWARPA **pdewa) +{ +l_int32 i; +L_DEWARP *dew; +L_DEWARPA *dewa; + + PROCNAME("dewarpaDestroy"); + + if (pdewa == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + if ((dewa = *pdewa) == NULL) + return; + + for (i = 0; i < dewa->nalloc; i++) { + if ((dew = dewa->dewarp[i]) != NULL) + dewarpDestroy(&dew); + if ((dew = dewa->dewarpcache[i]) != NULL) + dewarpDestroy(&dew); + } + numaDestroy(&dewa->namodels); + numaDestroy(&dewa->napages); + + LEPT_FREE(dewa->dewarp); + LEPT_FREE(dewa->dewarpcache); + LEPT_FREE(dewa); + *pdewa = NULL; + return; +} + + +/*! + * \brief dewarpaDestroyDewarp() + * + * \param[in] dewa + * \param[in] pageno of dew to be destroyed + * \return 0 if OK, 1 on error + */ +l_ok +dewarpaDestroyDewarp(L_DEWARPA *dewa, + l_int32 pageno) +{ +L_DEWARP *dew; + + PROCNAME("dewarpaDestroyDewarp"); + + if (!dewa) + return ERROR_INT("dewa or dew not defined", procName, 1); + if (pageno < 0 || pageno > dewa->maxpage) + return ERROR_INT("page out of bounds", procName, 1); + if ((dew = dewa->dewarp[pageno]) == NULL) + return ERROR_INT("dew not defined", procName, 1); + + dewarpDestroy(&dew); + dewa->dewarp[pageno] = NULL; + return 0; +} + + +/*----------------------------------------------------------------------* + * Dewarpa insertion/extraction * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaInsertDewarp() + * + * \param[in] dewa + * \param[in] dew to be added + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This inserts the dewarp into the array, which now owns it.
+ *          It also keeps track of the largest page number stored.
+ *          It must be done before the disparity model is built.
+ *      (2) Note that this differs from the usual method of filling out
+ *          arrays in leptonica, where the arrays are compact and
+ *          new elements are typically added to the end.  Here,
+ *          the dewarp can be added anywhere, even beyond the initial
+ *          allocation.
+ * 
+ */ +l_ok +dewarpaInsertDewarp(L_DEWARPA *dewa, + L_DEWARP *dew) +{ +l_int32 pageno, n, newsize; +L_DEWARP *prevdew; + + PROCNAME("dewarpaInsertDewarp"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + + dew->dewa = dewa; + pageno = dew->pageno; + if (pageno > MaxPtrArraySize) + return ERROR_INT("too many pages", procName, 1); + if (pageno > dewa->maxpage) + dewa->maxpage = pageno; + dewa->modelsready = 0; /* force re-evaluation at application time */ + + /* Extend ptr array if necessary */ + n = dewa->nalloc; + newsize = n; + if (pageno >= 2 * n) + newsize = 2 * pageno; + else if (pageno >= n) + newsize = 2 * n; + if (newsize > n) + dewarpaExtendArraysToSize(dewa, newsize); + + if ((prevdew = dewarpaGetDewarp(dewa, pageno)) != NULL) + dewarpDestroy(&prevdew); + dewa->dewarp[pageno] = dew; + + dew->sampling = dewa->sampling; + dew->redfactor = dewa->redfactor; + dew->minlines = dewa->minlines; + + /* Get the dimensions of the sampled array. This will be + * stored in an fpix, and the input resolution version is + * guaranteed to be larger than pixs. However, if you + * want to apply the disparity to an image with a width + * w > nx * s - 2 * s + 2 + * you will need to extend the input res fpix. + * And similarly for h. */ + dew->nx = (dew->w + 2 * dew->sampling - 2) / dew->sampling; + dew->ny = (dew->h + 2 * dew->sampling - 2) / dew->sampling; + return 0; +} + + +/*! + * \brief dewarpaExtendArraysToSize() + * + * \param[in] dewa + * \param[in] size new size of dewarpa array + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If necessary, reallocs main and cache dewarpa ptr arrays to %size.
+ * 
+ */ +static l_int32 +dewarpaExtendArraysToSize(L_DEWARPA *dewa, + l_int32 size) +{ + PROCNAME("dewarpaExtendArraysToSize"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + if (size > dewa->nalloc) { + if ((dewa->dewarp = (L_DEWARP **)reallocNew((void **)&dewa->dewarp, + sizeof(L_DEWARP *) * dewa->nalloc, + size * sizeof(L_DEWARP *))) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + if ((dewa->dewarpcache = + (L_DEWARP **)reallocNew((void **)&dewa->dewarpcache, + sizeof(L_DEWARP *) * dewa->nalloc, + size * sizeof(L_DEWARP *))) == NULL) + return ERROR_INT("new ptr cache array not returned", procName, 1); + dewa->nalloc = size; + } + return 0; +} + + +/*! + * \brief dewarpaGetDewarp() + * + * \param[in] dewa populated with dewarp structs for pages + * \param[in] index into dewa: this is the pageno + * \return dew handle; still owned by dewa, or NULL on error + */ +L_DEWARP * +dewarpaGetDewarp(L_DEWARPA *dewa, + l_int32 index) +{ + PROCNAME("dewarpaGetDewarp"); + + if (!dewa) + return (L_DEWARP *)ERROR_PTR("dewa not defined", procName, NULL); + if (index < 0 || index > dewa->maxpage) { + L_ERROR("index = %d is invalid; max index = %d\n", + procName, index, dewa->maxpage); + return NULL; + } + + return dewa->dewarp[index]; +} + + +/*----------------------------------------------------------------------* + * Setting parameters to control rendering from the model * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaSetCurvatures() + * + * \param[in] dewa + * \param[in] max_linecurv -1 for default + * \param[in] min_diff_linecurv -1 for default; 0 to accept all models + * \param[in] max_diff_linecurv -1 for default + * \param[in] max_edgecurv -1 for default + * \param[in] max_diff_edgecurv -1 for default + * \param[in] max_edgeslope -1 for default + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Approximating the line by a quadratic, the coefficient
+ *          of the quadratic term is the curvature, and distance
+ *          units are in pixels (of course).  The curvature is very
+ *          small, so we multiply by 10^6 and express the constraints
+ *          on the model curvatures in micro-units.
+ *      (2) This sets five curvature thresholds and a slope threshold:
+ *          * the maximum absolute value of the vertical disparity
+ *            line curvatures
+ *          * the minimum absolute value of the largest difference in
+ *            vertical disparity line curvatures (Use a value of 0
+ *            to accept all models.)
+ *          * the maximum absolute value of the largest difference in
+ *            vertical disparity line curvatures
+ *          * the maximum absolute value of the left and right edge
+ *            curvature for the horizontal disparity
+ *          * the maximum absolute value of the difference between
+ *            left and right edge curvature for the horizontal disparity
+ *          all in micro-units, for dewarping to take place.
+ *          Use -1 for default values.
+ *      (3) An image with a line curvature less than about 0.00001
+ *          has fairly straight textlines.  This is 10 micro-units.
+ *      (4) For example, if %max_linecurv == 100, this would prevent dewarping
+ *          if any of the lines has a curvature exceeding 100 micro-units.
+ *          A model having maximum line curvature larger than about 150
+ *          micro-units should probably not be used.
+ *      (5) A model having a left or right edge curvature larger than
+ *          about 50 micro-units should probably not be used.
+ * 
+ */ +l_ok +dewarpaSetCurvatures(L_DEWARPA *dewa, + l_int32 max_linecurv, + l_int32 min_diff_linecurv, + l_int32 max_diff_linecurv, + l_int32 max_edgecurv, + l_int32 max_diff_edgecurv, + l_int32 max_edgeslope) +{ + PROCNAME("dewarpaSetCurvatures"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + if (max_linecurv == -1) + dewa->max_linecurv = DefaultMaxLineCurv; + else + dewa->max_linecurv = L_ABS(max_linecurv); + + if (min_diff_linecurv == -1) + dewa->min_diff_linecurv = DefaultMinDiffLineCurv; + else + dewa->min_diff_linecurv = L_ABS(min_diff_linecurv); + + if (max_diff_linecurv == -1) + dewa->max_diff_linecurv = DefaultMaxDiffLineCurv; + else + dewa->max_diff_linecurv = L_ABS(max_diff_linecurv); + + if (max_edgecurv == -1) + dewa->max_edgecurv = DefaultMaxEdgeCurv; + else + dewa->max_edgecurv = L_ABS(max_edgecurv); + + if (max_diff_edgecurv == -1) + dewa->max_diff_edgecurv = DefaultMaxDiffEdgeCurv; + else + dewa->max_diff_edgecurv = L_ABS(max_diff_edgecurv); + + if (max_edgeslope == -1) + dewa->max_edgeslope = DefaultMaxEdgeSlope; + else + dewa->max_edgeslope = L_ABS(max_edgeslope); + + dewa->modelsready = 0; /* force validation */ + return 0; +} + + +/*! + * \brief dewarpaUseBothArrays() + * + * \param[in] dewa + * \param[in] useboth 0 for false, 1 for true + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This sets the useboth field.  If set, this will attempt
+ *          to apply both vertical and horizontal disparity arrays.
+ *          Note that a model with only a vertical disparity array will
+ *          always be valid.
+ * 
+ */ +l_ok +dewarpaUseBothArrays(L_DEWARPA *dewa, + l_int32 useboth) +{ + PROCNAME("dewarpaUseBothArrays"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + dewa->useboth = useboth; + dewa->modelsready = 0; /* force validation */ + return 0; +} + + +/*! + * \brief dewarpaSetCheckColumns() + * + * \param[in] dewa + * \param[in] check_columns 0 for false, 1 for true + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This sets the 'check_columns" field.  If set, and if
+ *          'useboth' is set, this will count the number of text
+ *          columns.  If the number is larger than 1, this will
+ *          prevent the application of horizontal disparity arrays
+ *          if they exist.  Note that the default value of check_columns
+ *          if 0 (FALSE).
+ *      (2) This field is set to 0 by default.  For horizontal disparity
+ *          correction to take place on a single column of text, you must have:
+ *           - a valid horizontal disparity array
+ *           - useboth = 1 (TRUE)
+ *          If there are multiple columns, additionally
+ *           - check_columns = 0 (FALSE)
+ *
+ * 
+ */ +l_ok +dewarpaSetCheckColumns(L_DEWARPA *dewa, + l_int32 check_columns) +{ + PROCNAME("dewarpaSetCheckColumns"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + dewa->check_columns = check_columns; + return 0; +} + + +/*! + * \brief dewarpaSetMaxDistance() + * + * \param[in] dewa + * \param[in] maxdist for using ref models + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This sets the maxdist field.
+ * 
+ */ +l_ok +dewarpaSetMaxDistance(L_DEWARPA *dewa, + l_int32 maxdist) +{ + PROCNAME("dewarpaSetMaxDistance"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + dewa->maxdist = maxdist; + dewa->modelsready = 0; /* force validation */ + return 0; +} + + +/*----------------------------------------------------------------------* + * Dewarp serialized I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpRead() + * + * \param[in] filename + * \return dew, or NULL on error + */ +L_DEWARP * +dewarpRead(const char *filename) +{ +FILE *fp; +L_DEWARP *dew; + + PROCNAME("dewarpRead"); + + if (!filename) + return (L_DEWARP *)ERROR_PTR("filename not defined", procName, NULL); + if ((fp = fopenReadStream(filename)) == NULL) + return (L_DEWARP *)ERROR_PTR("stream not opened", procName, NULL); + + if ((dew = dewarpReadStream(fp)) == NULL) { + fclose(fp); + return (L_DEWARP *)ERROR_PTR("dew not read", procName, NULL); + } + + fclose(fp); + return dew; +} + + +/*! + * \brief dewarpReadStream() + * + * \param[in] fp file stream + * \return dew dewarp, or NULL on error + * + *
+ * Notes:
+ *      (1) The dewarp struct is stored in minimized format, with only
+ *          subsampled disparity arrays.
+ *      (2) The sampling and extra horizontal disparity parameters are
+ *          stored here.  During generation of the dewarp struct, they
+ *          are passed in from the dewarpa.  In readback, it is assumed
+ *          that they are (a) the same for each page and (b) the same
+ *          as the values used to create the dewarpa.
+ * 
+ */ +L_DEWARP * +dewarpReadStream(FILE *fp) +{ +l_int32 version, sampling, redfactor, minlines, pageno, hasref, refpage; +l_int32 w, h, nx, ny, vdispar, hdispar, nlines; +l_int32 mincurv, maxcurv, leftslope, rightslope, leftcurv, rightcurv; +L_DEWARP *dew; +FPIX *fpixv, *fpixh; + + PROCNAME("dewarpReadStream"); + + if (!fp) + return (L_DEWARP *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nDewarp Version %d\n", &version) != 1) + return (L_DEWARP *)ERROR_PTR("not a dewarp file", procName, NULL); + if (version != DEWARP_VERSION_NUMBER) + return (L_DEWARP *)ERROR_PTR("invalid dewarp version", procName, NULL); + if (fscanf(fp, "pageno = %d\n", &pageno) != 1) + return (L_DEWARP *)ERROR_PTR("read fail for pageno", procName, NULL); + if (fscanf(fp, "hasref = %d, refpage = %d\n", &hasref, &refpage) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for hasref, refpage", + procName, NULL); + if (fscanf(fp, "sampling = %d, redfactor = %d\n", &sampling, &redfactor) + != 2) + return (L_DEWARP *)ERROR_PTR("read fail for sampling/redfactor", + procName, NULL); + if (fscanf(fp, "nlines = %d, minlines = %d\n", &nlines, &minlines) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for nlines/minlines", + procName, NULL); + if (fscanf(fp, "w = %d, h = %d\n", &w, &h) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for w, h", procName, NULL); + if (fscanf(fp, "nx = %d, ny = %d\n", &nx, &ny) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for nx, ny", procName, NULL); + if (fscanf(fp, "vert_dispar = %d, horiz_dispar = %d\n", &vdispar, &hdispar) + != 2) + return (L_DEWARP *)ERROR_PTR("read fail for flags", procName, NULL); + if (vdispar) { + if (fscanf(fp, "min line curvature = %d, max line curvature = %d\n", + &mincurv, &maxcurv) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for mincurv & maxcurv", + procName, NULL); + } + if (hdispar) { + if (fscanf(fp, "left edge slope = %d, right edge slope = %d\n", + &leftslope, &rightslope) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for leftslope & rightslope", + procName, NULL); + if (fscanf(fp, "left edge curvature = %d, right edge curvature = %d\n", + &leftcurv, &rightcurv) != 2) + return (L_DEWARP *)ERROR_PTR("read fail for leftcurv & rightcurv", + procName, NULL); + } + if (vdispar) { + if ((fpixv = fpixReadStream(fp)) == NULL) + return (L_DEWARP *)ERROR_PTR("read fail for vdispar", + procName, NULL); + } + if (hdispar) { + if ((fpixh = fpixReadStream(fp)) == NULL) + return (L_DEWARP *)ERROR_PTR("read fail for hdispar", + procName, NULL); + } + getc(fp); + + dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); + dew->w = w; + dew->h = h; + dew->pageno = pageno; + dew->sampling = sampling; + dew->redfactor = redfactor; + dew->minlines = minlines; + dew->nlines = nlines; + dew->hasref = hasref; + dew->refpage = refpage; + if (hasref == 0) /* any dew without a ref has an actual model */ + dew->vsuccess = 1; + dew->nx = nx; + dew->ny = ny; + if (vdispar) { + dew->mincurv = mincurv; + dew->maxcurv = maxcurv; + dew->vsuccess = 1; + dew->sampvdispar = fpixv; + } + if (hdispar) { + dew->leftslope = leftslope; + dew->rightslope = rightslope; + dew->leftcurv = leftcurv; + dew->rightcurv = rightcurv; + dew->hsuccess = 1; + dew->samphdispar = fpixh; + } + + return dew; +} + + +/*! + * \brief dewarpReadMem() + * + * \param[in] data serialization of dewarp + * \param[in] size of data in bytes + * \return dew dewarp, or NULL on error + */ +L_DEWARP * +dewarpReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +L_DEWARP *dew; + + PROCNAME("dewarpReadMem"); + + if (!data) + return (L_DEWARP *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (L_DEWARP *)ERROR_PTR("stream not opened", procName, NULL); + + dew = dewarpReadStream(fp); + fclose(fp); + if (!dew) L_ERROR("dew not read\n", procName); + return dew; +} + + +/*! + * \brief dewarpWrite() + * + * \param[in] filename + * \param[in] dew + * \return 0 if OK, 1 on error + */ +l_ok +dewarpWrite(const char *filename, + L_DEWARP *dew) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dewarpWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dewarpWriteStream(fp, dew); + fclose(fp); + if (ret) + return ERROR_INT("dew not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief dewarpWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] dew + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This should not be written if there is no sampled
+ *          vertical disparity array, which means that no model has
+ *          been built for this page.
+ * 
+ */ +l_ok +dewarpWriteStream(FILE *fp, + L_DEWARP *dew) +{ +l_int32 vdispar, hdispar; + + PROCNAME("dewarpWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + + fprintf(fp, "\nDewarp Version %d\n", DEWARP_VERSION_NUMBER); + fprintf(fp, "pageno = %d\n", dew->pageno); + fprintf(fp, "hasref = %d, refpage = %d\n", dew->hasref, dew->refpage); + fprintf(fp, "sampling = %d, redfactor = %d\n", + dew->sampling, dew->redfactor); + fprintf(fp, "nlines = %d, minlines = %d\n", dew->nlines, dew->minlines); + fprintf(fp, "w = %d, h = %d\n", dew->w, dew->h); + fprintf(fp, "nx = %d, ny = %d\n", dew->nx, dew->ny); + vdispar = (dew->sampvdispar) ? 1 : 0; + hdispar = (dew->samphdispar) ? 1 : 0; + fprintf(fp, "vert_dispar = %d, horiz_dispar = %d\n", vdispar, hdispar); + if (vdispar) + fprintf(fp, "min line curvature = %d, max line curvature = %d\n", + dew->mincurv, dew->maxcurv); + if (hdispar) { + fprintf(fp, "left edge slope = %d, right edge slope = %d\n", + dew->leftslope, dew->rightslope); + fprintf(fp, "left edge curvature = %d, right edge curvature = %d\n", + dew->leftcurv, dew->rightcurv); + } + if (vdispar) fpixWriteStream(fp, dew->sampvdispar); + if (hdispar) fpixWriteStream(fp, dew->samphdispar); + fprintf(fp, "\n"); + + if (!vdispar) + L_WARNING("no disparity arrays!\n", procName); + return 0; +} + + +/*! + * \brief dewarpWriteMem() + * + * \param[out] pdata data of serialized dewarp (not ascii) + * \param[out] psize size of returned data + * \param[in] dew + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a dewarp in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +dewarpWriteMem(l_uint8 **pdata, + size_t *psize, + L_DEWARP *dew) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dewarpWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dewarpWriteStream(fp, dew); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = dewarpWriteStream(fp, dew); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*----------------------------------------------------------------------* + * Dewarpa serialized I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaRead() + * + * \param[in] filename + * \return dewa, or NULL on error + */ +L_DEWARPA * +dewarpaRead(const char *filename) +{ +FILE *fp; +L_DEWARPA *dewa; + + PROCNAME("dewarpaRead"); + + if (!filename) + return (L_DEWARPA *)ERROR_PTR("filename not defined", procName, NULL); + if ((fp = fopenReadStream(filename)) == NULL) + return (L_DEWARPA *)ERROR_PTR("stream not opened", procName, NULL); + + if ((dewa = dewarpaReadStream(fp)) == NULL) { + fclose(fp); + return (L_DEWARPA *)ERROR_PTR("dewa not read", procName, NULL); + } + + fclose(fp); + return dewa; +} + + +/*! + * \brief dewarpaReadStream() + * + * \param[in] fp file stream + * \return dewa, or NULL on error + * + *
+ * Notes:
+ *      (1) The serialized dewarp contains a Numa that gives the
+ *          (increasing) page number of the dewarp structs that are
+ *          contained.
+ *      (2) Reference pages are added in after readback.
+ * 
+ */ +L_DEWARPA * +dewarpaReadStream(FILE *fp) +{ +l_int32 i, version, ndewarp, maxpage; +l_int32 sampling, redfactor, minlines, maxdist, useboth; +l_int32 max_linecurv, min_diff_linecurv, max_diff_linecurv; +l_int32 max_edgeslope, max_edgecurv, max_diff_edgecurv; +L_DEWARP *dew; +L_DEWARPA *dewa; +NUMA *namodels; + + PROCNAME("dewarpaReadStream"); + + if (!fp) + return (L_DEWARPA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nDewarpa Version %d\n", &version) != 1) + return (L_DEWARPA *)ERROR_PTR("not a dewarpa file", procName, NULL); + if (version != DEWARP_VERSION_NUMBER) + return (L_DEWARPA *)ERROR_PTR("invalid dewarp version", procName, NULL); + + if (fscanf(fp, "ndewarp = %d, maxpage = %d\n", &ndewarp, &maxpage) != 2) + return (L_DEWARPA *)ERROR_PTR("read fail for maxpage+", procName, NULL); + if (fscanf(fp, + "sampling = %d, redfactor = %d, minlines = %d, maxdist = %d\n", + &sampling, &redfactor, &minlines, &maxdist) != 4) + return (L_DEWARPA *)ERROR_PTR("read fail for 4 params", procName, NULL); + if (fscanf(fp, + "max_linecurv = %d, min_diff_linecurv = %d, max_diff_linecurv = %d\n", + &max_linecurv, &min_diff_linecurv, &max_diff_linecurv) != 3) + return (L_DEWARPA *)ERROR_PTR("read fail for linecurv", procName, NULL); + if (fscanf(fp, + "max_edgeslope = %d, max_edgecurv = %d, max_diff_edgecurv = %d\n", + &max_edgeslope, &max_edgecurv, &max_diff_edgecurv) != 3) + return (L_DEWARPA *)ERROR_PTR("read fail for edgecurv", procName, NULL); + if (fscanf(fp, "fullmodel = %d\n", &useboth) != 1) + return (L_DEWARPA *)ERROR_PTR("read fail for useboth", procName, NULL); + + if (ndewarp > MaxPtrArraySize) + return (L_DEWARPA *)ERROR_PTR("too many pages", procName, NULL); + + dewa = dewarpaCreate(maxpage + 1, sampling, redfactor, minlines, maxdist); + dewa->maxpage = maxpage; + dewa->max_linecurv = max_linecurv; + dewa->min_diff_linecurv = min_diff_linecurv; + dewa->max_diff_linecurv = max_diff_linecurv; + dewa->max_edgeslope = max_edgeslope; + dewa->max_edgecurv = max_edgecurv; + dewa->max_diff_edgecurv = max_diff_edgecurv; + dewa->useboth = useboth; + namodels = numaCreate(ndewarp); + dewa->namodels = namodels; + for (i = 0; i < ndewarp; i++) { + if ((dew = dewarpReadStream(fp)) == NULL) { + L_ERROR("read fail for dew[%d]\n", procName, i); + dewarpaDestroy(&dewa); + return NULL; + } + dewarpaInsertDewarp(dewa, dew); + numaAddNumber(namodels, dew->pageno); + } + + /* Validate the models and insert reference models */ + dewarpaInsertRefModels(dewa, 0, 0); + return dewa; +} + + +/*! + * \brief dewarpaReadMem() + * + * \param[in] data serialization of dewarpa + * \param[in] size of data in bytes + * \return dewa dewarpa, or NULL on error + */ +L_DEWARPA * +dewarpaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +L_DEWARPA *dewa; + + PROCNAME("dewarpaReadMem"); + + if (!data) + return (L_DEWARPA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (L_DEWARPA *)ERROR_PTR("stream not opened", procName, NULL); + + dewa = dewarpaReadStream(fp); + fclose(fp); + if (!dewa) L_ERROR("dewa not read\n", procName); + return dewa; +} + + +/*! + * \brief dewarpaWrite() + * + * \param[in] filename + * \param[in] dewa + * \return 0 if OK, 1 on error + */ +l_ok +dewarpaWrite(const char *filename, + L_DEWARPA *dewa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dewarpaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dewarpaWriteStream(fp, dewa); + fclose(fp); + if (ret) + return ERROR_INT("dewa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief dewarpaWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] dewa + * \return 0 if OK, 1 on error + */ +l_ok +dewarpaWriteStream(FILE *fp, + L_DEWARPA *dewa) +{ +l_int32 ndewarp, i, pageno; + + PROCNAME("dewarpaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + /* Generate the list of page numbers for which a model exists. + * Note that no attempt is made to determine if the model is + * valid, because that determination is associated with + * using the model to remove the warping, which typically + * can happen later, after all the models have been built. */ + dewarpaListPages(dewa); + if (!dewa->namodels) + return ERROR_INT("dewa->namodels not made", procName, 1); + ndewarp = numaGetCount(dewa->namodels); /* with actual page models */ + + fprintf(fp, "\nDewarpa Version %d\n", DEWARP_VERSION_NUMBER); + fprintf(fp, "ndewarp = %d, maxpage = %d\n", ndewarp, dewa->maxpage); + fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d, maxdist = %d\n", + dewa->sampling, dewa->redfactor, dewa->minlines, dewa->maxdist); + fprintf(fp, + "max_linecurv = %d, min_diff_linecurv = %d, max_diff_linecurv = %d\n", + dewa->max_linecurv, dewa->min_diff_linecurv, dewa->max_diff_linecurv); + fprintf(fp, + "max_edgeslope = %d, max_edgecurv = %d, max_diff_edgecurv = %d\n", + dewa->max_edgeslope, dewa->max_edgecurv, dewa->max_diff_edgecurv); + fprintf(fp, "fullmodel = %d\n", dewa->useboth); + for (i = 0; i < ndewarp; i++) { + numaGetIValue(dewa->namodels, i, &pageno); + dewarpWriteStream(fp, dewarpaGetDewarp(dewa, pageno)); + } + + return 0; +} + + +/*! + * \brief dewarpaWriteMem() + * + * \param[out] pdata data of serialized dewarpa (not ascii) + * \param[out] psize size of returned data + * \param[in] dewa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a dewarpa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +dewarpaWriteMem(l_uint8 **pdata, + size_t *psize, + L_DEWARPA *dewa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dewarpaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dewarpaWriteStream(fp, dewa); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = dewarpaWriteStream(fp, dewa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dewarp2.c b/hgdriver/3rdparty/hgOCR/leptonica/dewarp2.c new file mode 100644 index 0000000..a638eda --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dewarp2.c @@ -0,0 +1,1912 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dewarp2.c + *
+ *
+ *    Build the page disparity model
+ *
+ *      Build basic page disparity model
+ *          l_int32            dewarpBuildPageModel()
+ *          l_int32            dewarpFindVertDisparity()
+ *          l_int32            dewarpFindHorizDisparity()
+ *          PTAA              *dewarpGetTextlineCenters()
+ *          static PTA        *dewarpGetMeanVerticals()
+ *          PTAA              *dewarpRemoveShortLines()
+ *          static l_int32     dewarpGetLineEndPoints()
+ *          static l_int32     dewarpFilterLineEndPoints()
+ *          static PTA        *dewarpRemoveBadEndPoints()
+ *          static l_int32     dewarpIsLineCoverageValid()
+ *          static l_int32     dewarpQuadraticLSF()
+ *
+ *      Build disparity model for slope near binding
+ *          l_int32            dewarpFindHorizSlopeDisparity()
+ *
+ *      Build the line disparity model
+ *          l_int32            dewarpBuildLineModel()
+ *
+ *      Query model status
+ *          l_int32            dewarpaModelStatus()
+ *
+ *      Rendering helpers
+ *          static l_int32     pixRenderMidYs()
+ *          static l_int32     pixRenderHorizEndPoints
+ * 
+ */ + +#include +#include "allheaders.h" + +static PTA *dewarpGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y); +static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal, + PTA **pptar); +static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1, + PTA **pptal2, PTA **pptar2); +static PTA *dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas); +static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h, + l_int32 *pntop, l_int32 *pnbot, + l_int32 *pytop, l_int32 *pybot); +static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, + l_float32 *pc, l_float32 *pmederr); +static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew); +static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar, + l_uint32 color); + + +#ifndef NO_CONSOLE_IO +#define DEBUG_TEXTLINE_CENTERS 0 /* set this to 1 for debugging */ +#define DEBUG_SHORT_LINES 0 /* ditto */ +#else +#define DEBUG_TEXTLINE_CENTERS 0 /* always must be 0 */ +#define DEBUG_SHORT_LINES 0 /* ditto */ +#endif /* !NO_CONSOLE_IO */ + + /* Special parameter values for reducing horizontal disparity */ +static const l_float32 MinRatioLinesToHeight = 0.45; +static const l_int32 MinLinesForHoriz1 = 10; /* initially */ +static const l_int32 MinLinesForHoriz2 = 3; /* after, in each half */ +static const l_float32 AllowedWidthFract = 0.05; /* no bigger */ + + +/*----------------------------------------------------------------------* + * Build basic page disparity model * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpBuildPageModel() + * + * \param[in] dew + * \param[in] debugfile use NULL to skip writing this + * \return 0 if OK, 1 if unable to build the model or on error + * + *
+ * Notes:
+ *      (1) This is the basic function that builds the horizontal and
+ *          vertical disparity arrays, which allow determination of the
+ *          src pixel in the input image corresponding to each
+ *          dest pixel in the dewarped image.
+ *      (2) Sets vsuccess = 1 if the vertical disparity array builds.
+ *          Always attempts to build the horizontal disparity array,
+ *          even if it will not be requested (useboth == 0).
+ *          Sets hsuccess = 1 if horizontal disparity builds.
+ *      (3) The method is as follows:
+ *          (a) Estimate the points along the centers of all the
+ *              long textlines.  If there are too few lines, no
+ *              disparity models are built.
+ *          (b) From the vertical deviation of the lines, estimate
+ *              the vertical disparity.
+ *          (c) From the ends of the lines, estimate the horizontal
+ *              disparity, assuming that the text is made of lines
+ *              that are close to left and right justified.
+ *          (d) One can also compute an additional contribution to the
+ *              horizontal disparity, inferred from slopes of the top
+ *              and bottom lines.  We do not do this.
+ *      (4) In more detail for the vertical disparity:
+ *          (a) Fit a LS quadratic to center locations along each line.
+ *              This smooths the curves.
+ *          (b) Sample each curve at a regular interval, find the y-value
+ *              of the mid-point on each curve, and subtract the sampled
+ *              curve value from this value.  This is the vertical
+ *              disparity at sampled points along each curve.
+ *          (c) Fit a LS quadratic to each set of vertically aligned
+ *              disparity samples.  This smooths the disparity values
+ *              in the vertical direction.  Then resample at the same
+ *              regular interval.  We now have a regular grid of smoothed
+ *              vertical disparity valuels.
+ *      (5) Once the sampled vertical disparity array is found, it can be
+ *          interpolated to get a full resolution vertical disparity map.
+ *          This can be applied directly to the src image pixels
+ *          to dewarp the image in the vertical direction, making
+ *          all textlines horizontal.  Likewise, the horizontal
+ *          disparity array is used to left- and right-align the
+ *          longest textlines.
+ * 
+ */ +l_ok +dewarpBuildPageModel(L_DEWARP *dew, + const char *debugfile) +{ +l_int32 linecount, ntop, nbot, ytop, ybot, ret; +PIX *pixs, *pix1, *pix2, *pix3; +PTA *pta; +PTAA *ptaa1, *ptaa2; + + PROCNAME("dewarpBuildPageModel"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + + dew->debug = (debugfile) ? 1 : 0; + dew->vsuccess = dew->hsuccess = 0; + pixs = dew->pixs; + if (debugfile) { + lept_rmdir("lept/dewmod"); /* erase previous images */ + lept_mkdir("lept/dewmod"); + pixDisplayWithTitle(pixs, 0, 0, "pixs", 1); + pixWriteDebug("/tmp/lept/dewmod/0010.png", pixs, IFF_PNG); + } + + /* Make initial estimate of centers of textlines */ + ptaa1 = dewarpGetTextlineCenters(pixs, debugfile || DEBUG_TEXTLINE_CENTERS); + if (!ptaa1) { + L_WARNING("textline centers not found; model not built\n", procName); + return 1; + } + if (debugfile) { + pix1 = pixConvertTo32(pixs); + pta = generatePtaFilledCircle(1); + pix2 = pixGenerateFromPta(pta, 5, 5); + pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa1, pix2, 2, 2); + pixWriteDebug("/tmp/lept/dewmod/0020.png", pix3, IFF_PNG); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + ptaDestroy(&pta); + } + + /* Remove all lines that are not at least 0.8 times the length + * of the longest line. */ + ptaa2 = dewarpRemoveShortLines(pixs, ptaa1, 0.8, + debugfile || DEBUG_SHORT_LINES); + if (debugfile) { + pix1 = pixConvertTo32(pixs); + pta = generatePtaFilledCircle(1); + pix2 = pixGenerateFromPta(pta, 5, 5); + pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa2, pix2, 2, 2); + pixWriteDebug("/tmp/lept/dewmod/0030.png", pix3, IFF_PNG); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + ptaDestroy(&pta); + } + ptaaDestroy(&ptaa1); + + /* Verify that there are sufficient "long" lines */ + linecount = ptaaGetCount(ptaa2); + if (linecount < dew->minlines) { + ptaaDestroy(&ptaa2); + L_WARNING("linecount %d < min req'd number of lines (%d) for model\n", + procName, linecount, dew->minlines); + return 1; + } + + /* Verify that the lines have a reasonable coverage of the + * vertical extent of the page. */ + if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs), + &ntop, &nbot, &ytop, &ybot) == FALSE) { + ptaaDestroy(&ptaa2); + L_WARNING("invalid line coverage: ntop = %d, nbot = %d;" + " spanning [%d ... %d] in height %d\n", procName, + ntop, nbot, ytop, ybot, pixGetHeight(pixs)); + return 1; + } + + /* Get the sampled vertical disparity from the textline centers. + * The disparity array will push pixels vertically so that each + * textline is flat and centered at the y-position of the mid-point. */ + if (dewarpFindVertDisparity(dew, ptaa2, 0) != 0) { + L_WARNING("vertical disparity not built\n", procName); + ptaaDestroy(&ptaa2); + return 1; + } + + /* Get the sampled horizontal disparity from the left and right + * edges of the text. The disparity array will expand the image + * linearly outward to align the text edges vertically. + * Do this even if useboth == 0; we still calculate it even + * if we don't plan to use it. */ + if ((ret = dewarpFindHorizDisparity(dew, ptaa2)) == 0) + L_INFO("hsuccess = 1\n", procName); + + /* Debug output */ + if (debugfile) { + dewarpPopulateFullRes(dew, NULL, 0, 0); + pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); + pixWriteDebug("/tmp/lept/dewmod/0060.png", pix1, IFF_PNG); + pixDisplay(pix1, 1000, 0); + pixDestroy(&pix1); + if (ret == 0) { + pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); + pixWriteDebug("/tmp/lept/dewmod/0070.png", pix1, IFF_PNG); + pixDisplay(pix1, 1000, 0); + pixDestroy(&pix1); + } + convertFilesToPdf("/tmp/lept/dewmod", NULL, 135, 1.0, 0, 0, + "Dewarp Build Model", debugfile); + fprintf(stderr, "pdf file: %s\n", debugfile); + } + + ptaaDestroy(&ptaa2); + return 0; +} + + +/*! + * \brief dewarpFindVertDisparity() + * + * \param[in] dew + * \param[in] ptaa unsmoothed lines, not vertically ordered + * \param[in] rotflag 0 if using dew->pixs; 1 if rotated by 90 degrees cw + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This starts with points along the centers of textlines.
+ *          It does quadratic fitting (and smoothing), first along the
+ *          lines and then in the vertical direction, to generate
+ *          the sampled vertical disparity map.  This can then be
+ *          interpolated to full resolution and used to remove
+ *          the vertical line warping.
+ *      (2) Use %rotflag == 1 if you are dewarping vertical lines, as
+ *          is done in dewarpBuildLineModel().  The usual case is for
+ *          %rotflag == 0.
+ *      (3) Note that this builds a vertical disparity model (VDM), but
+ *          does not check it against constraints for validity.
+ *          Constraint checking is done after building the models,
+ *          and before inserting reference models.
+ *      (4) This sets the vsuccess flag to 1 on success.
+ *      (5) Pix debug output goes to /tmp/dewvert/ for collection into
+ *          a pdf.  Non-pix debug output goes to /tmp.
+ * 
+ */ +l_ok +dewarpFindVertDisparity(L_DEWARP *dew, + PTAA *ptaa, + l_int32 rotflag) +{ +l_int32 i, j, nlines, npts, nx, ny, sampling; +l_float32 c0, c1, c2, x, y, midy, val, medval, meddev, minval, maxval; +l_float32 *famidys; +NUMA *nax, *nafit, *nacurve0, *nacurve1, *nacurves; +NUMA *namidy, *namidys, *namidysi; +PIX *pix1, *pix2, *pixcirc, *pixdb; +PTA *pta, *ptad, *ptacirc; +PTAA *ptaa0, *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaat; +FPIX *fpix; + + PROCNAME("dewarpFindVertDisparity"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + dew->vsuccess = 0; + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + if (dew->debug) L_INFO("finding vertical disparity\n", procName); + + /* Do quadratic fit to smooth each line. A single quadratic + * over the entire width of the line appears to be sufficient. + * Quartics tend to overfit to noise. Each line is thus + * represented by three coefficients: y(x) = c2 * x^2 + c1 * x + c0. + * Using the coefficients, sample each fitted curve uniformly + * across the full width of the image. The result is in ptaa0. */ + sampling = dew->sampling; + nx = (rotflag) ? dew->ny : dew->nx; + ny = (rotflag) ? dew->nx : dew->ny; + nlines = ptaaGetCount(ptaa); + dew->nlines = nlines; + ptaa0 = ptaaCreate(nlines); + nacurve0 = numaCreate(nlines); /* stores curvature coeff c2 */ + pixdb = (rotflag) ? pixRotateOrth(dew->pixs, 1) : pixClone(dew->pixs); + for (i = 0; i < nlines; i++) { /* for each line */ + pta = ptaaGetPta(ptaa, i, L_CLONE); + ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); + numaAddNumber(nacurve0, c2); + ptad = ptaCreate(nx); + for (j = 0; j < nx; j++) { /* uniformly sampled in x */ + x = j * sampling; + applyQuadraticFit(c2, c1, c0, x, &y); + ptaAddPt(ptad, x, y); + } + ptaaAddPta(ptaa0, ptad, L_INSERT); + ptaDestroy(&pta); + } + if (dew->debug) { + lept_mkdir("lept/dewarp"); + lept_mkdir("lept/dewdebug"); + lept_mkdir("lept/dewmod"); + ptaat = ptaaCreate(nlines); + for (i = 0; i < nlines; i++) { + pta = ptaaGetPta(ptaa, i, L_CLONE); + ptaGetArrays(pta, &nax, NULL); + ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit); + ptad = ptaCreateFromNuma(nax, nafit); + ptaaAddPta(ptaat, ptad, L_INSERT); + ptaDestroy(&pta); + numaDestroy(&nax); + numaDestroy(&nafit); + } + pix1 = pixConvertTo32(pixdb); + pta = generatePtaFilledCircle(1); + pixcirc = pixGenerateFromPta(pta, 5, 5); + pix2 = pixDisplayPtaaPattern(NULL, pix1, ptaat, pixcirc, 2, 2); + pixWriteDebug("/tmp/lept/dewmod/0041.png", pix2, IFF_PNG); + pixDestroy(&pix1); + pixDestroy(&pix2); + ptaDestroy(&pta); + pixDestroy(&pixcirc); + ptaaDestroy(&ptaat); + } + + /* Remove lines with outlier curvatures. + * Note that this is just looking for internal consistency in + * the line curvatures. It is not rejecting lines based on + * the magnitude of the curvature. That is done when constraints + * are applied for valid models. */ + numaGetMedianDevFromMedian(nacurve0, &medval, &meddev); + L_INFO("\nPage %d\n", procName, dew->pageno); + L_INFO("Pass 1: Curvature: medval = %f, meddev = %f\n", + procName, medval, meddev); + ptaa1 = ptaaCreate(nlines); + nacurve1 = numaCreate(nlines); + for (i = 0; i < nlines; i++) { /* for each line */ + numaGetFValue(nacurve0, i, &val); + if (L_ABS(val - medval) > 7.0 * meddev) /* TODO: reduce to ~ 3.0 */ + continue; + pta = ptaaGetPta(ptaa0, i, L_CLONE); + ptaaAddPta(ptaa1, pta, L_INSERT); + numaAddNumber(nacurve1, val); + } + nlines = ptaaGetCount(ptaa1); + numaDestroy(&nacurve0); + + /* Save the min and max curvature (in micro-units) */ + numaGetMin(nacurve1, &minval, NULL); + numaGetMax(nacurve1, &maxval, NULL); + dew->mincurv = lept_roundftoi(1000000. * minval); + dew->maxcurv = lept_roundftoi(1000000. * maxval); + L_INFO("Pass 2: Min/max curvature = (%d, %d)\n", procName, + dew->mincurv, dew->maxcurv); + + /* Find and save the y values at the mid-points in each curve. + * If the slope is zero anywhere, it will typically be here. */ + namidy = numaCreate(nlines); + for (i = 0; i < nlines; i++) { + pta = ptaaGetPta(ptaa1, i, L_CLONE); + npts = ptaGetCount(pta); + ptaGetPt(pta, npts / 2, NULL, &midy); + numaAddNumber(namidy, midy); + ptaDestroy(&pta); + } + + /* Sort the lines in ptaa1c by their vertical position, going down */ + namidysi = numaGetSortIndex(namidy, L_SORT_INCREASING); + namidys = numaSortByIndex(namidy, namidysi); + nacurves = numaSortByIndex(nacurve1, namidysi); + numaDestroy(&dew->namidys); /* in case previously made */ + numaDestroy(&dew->nacurves); + dew->namidys = namidys; + dew->nacurves = nacurves; + ptaa2 = ptaaSortByIndex(ptaa1, namidysi); + numaDestroy(&namidy); + numaDestroy(&nacurve1); + numaDestroy(&namidysi); + if (dew->debug) { + numaWriteDebug("/tmp/lept/dewdebug/midys.na", namidys); + numaWriteDebug("/tmp/lept/dewdebug/curves.na", nacurves); + pix1 = pixConvertTo32(pixdb); + ptacirc = generatePtaFilledCircle(5); + pixcirc = pixGenerateFromPta(ptacirc, 11, 11); + srand(3); + pixDisplayPtaaPattern(pix1, pix1, ptaa2, pixcirc, 5, 5); + srand(3); /* use the same colors for text and reference lines */ + pixRenderMidYs(pix1, namidys, 2); + pix2 = (rotflag) ? pixRotateOrth(pix1, 3) : pixClone(pix1); + pixWriteDebug("/tmp/lept/dewmod/0042.png", pix2, IFF_PNG); + pixDisplay(pix2, 0, 0); + ptaDestroy(&ptacirc); + pixDestroy(&pixcirc); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + pixDestroy(&pixdb); + + /* Convert the sampled points in ptaa2 to a sampled disparity with + * with respect to the y value at the mid point in the curve. + * The disparity is the distance the point needs to move; + * plus is downward. */ + ptaa3 = ptaaCreate(nlines); + for (i = 0; i < nlines; i++) { + pta = ptaaGetPta(ptaa2, i, L_CLONE); + numaGetFValue(namidys, i, &midy); + ptad = ptaCreate(nx); + for (j = 0; j < nx; j++) { + ptaGetPt(pta, j, &x, &y); + ptaAddPt(ptad, x, midy - y); + } + ptaaAddPta(ptaa3, ptad, L_INSERT); + ptaDestroy(&pta); + } + if (dew->debug) { + ptaaWriteDebug("/tmp/lept/dewdebug/ptaa3.ptaa", ptaa3, 0); + } + + /* Generate ptaa4 by taking vertical 'columns' from ptaa3. + * We want to fit the vertical disparity on the column to the + * vertical position of the line, which we call 'y' here and + * obtain from namidys. So each pta in ptaa4 is the set of + * vertical disparities down a column of points. The columns + * in ptaa4 are equally spaced in x. */ + ptaa4 = ptaaCreate(nx); + famidys = numaGetFArray(namidys, L_NOCOPY); + for (j = 0; j < nx; j++) { + pta = ptaCreate(nlines); + for (i = 0; i < nlines; i++) { + y = famidys[i]; + ptaaGetPt(ptaa3, i, j, NULL, &val); /* disparity value */ + ptaAddPt(pta, y, val); + } + ptaaAddPta(ptaa4, pta, L_INSERT); + } + if (dew->debug) { + ptaaWriteDebug("/tmp/lept/dewdebug/ptaa4.ptaa", ptaa4, 0); + } + + /* Do quadratic fit vertically on each of the pixel columns + * in ptaa4, for the vertical displacement (which identifies the + * src pixel(s) for each dest pixel) as a function of y (the + * y value of the mid-points for each line). Then generate + * ptaa5 by sampling the fitted vertical displacement on a + * regular grid in the vertical direction. Each pta in ptaa5 + * gives the vertical displacement for regularly sampled y values + * at a fixed x. */ + ptaa5 = ptaaCreate(nx); /* uniformly sampled across full height of image */ + for (j = 0; j < nx; j++) { /* for each column */ + pta = ptaaGetPta(ptaa4, j, L_CLONE); + ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); + ptad = ptaCreate(ny); + for (i = 0; i < ny; i++) { /* uniformly sampled in y */ + y = i * sampling; + applyQuadraticFit(c2, c1, c0, y, &val); + ptaAddPt(ptad, y, val); + } + ptaaAddPta(ptaa5, ptad, L_INSERT); + ptaDestroy(&pta); + } + if (dew->debug) { + ptaaWriteDebug("/tmp/lept/dewdebug/ptaa5.ptaa", ptaa5, 0); + convertFilesToPdf("/tmp/lept/dewmod", "004", 135, 1.0, 0, 0, + "Dewarp Vert Disparity", + "/tmp/lept/dewarp/vert_disparity.pdf"); + fprintf(stderr, "pdf file: /tmp/lept/dewarp/vert_disparity.pdf\n"); + } + + /* Save the result in a fpix at the specified subsampling */ + fpix = fpixCreate(nx, ny); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + ptaaGetPt(ptaa5, j, i, NULL, &val); + fpixSetPixel(fpix, j, i, val); + } + } + dew->sampvdispar = fpix; + dew->vsuccess = 1; + + ptaaDestroy(&ptaa0); + ptaaDestroy(&ptaa1); + ptaaDestroy(&ptaa2); + ptaaDestroy(&ptaa3); + ptaaDestroy(&ptaa4); + ptaaDestroy(&ptaa5); + return 0; +} + + +/*! + * \brief dewarpFindHorizDisparity() + * + * \param[in] dew + * \param[in] ptaa unsmoothed lines, not vertically ordered + * \return 0 if OK, 1 if horizontal disparity array is not built, or on error + * + *
+ * Notes:
+ *      (1) This builds a horizontal disparity model (HDM), but
+ *          does not check it against constraints for validity.
+ *          Constraint checking is done at rendering time.
+ *      (2) Horizontal disparity is not required for a successful model;
+ *          only the vertical disparity is required.  This will not be
+ *          called if the function to build the vertical disparity fails.
+ *      (3) This sets the hsuccess flag to 1 on success.
+ *      (4) Internally in ptal1, ptar1, ptal2, ptar2: x and y are reversed,
+ *          so the 'y' value is horizontal distance across the image width.
+ *      (5) Debug output goes to /tmp/lept/dewmod/ for collection into a pdf.
+ * 
+ */ +l_ok +dewarpFindHorizDisparity(L_DEWARP *dew, + PTAA *ptaa) +{ +l_int32 i, j, h, nx, ny, sampling, ret; +l_float32 c0, c1, cl0, cl1, cl2, cr0, cr1, cr2; +l_float32 x, y, refl, refr; +l_float32 val, mederr; +NUMA *nald, *nard; +PIX *pix1; +PTA *ptal1, *ptar1; /* left/right end points of lines; initial */ +PTA *ptal2, *ptar2; /* left/right end points; after filtering */ +PTA *ptal3, *ptar3; /* left and right block, fitted, uniform spacing */ +PTA *pta, *ptat, *pta1, *pta2; +PTAA *ptaah; +FPIX *fpix; + + PROCNAME("dewarpFindHorizDisparity"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + dew->hsuccess = 0; + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + if (dew->debug) L_INFO("finding horizontal disparity\n", procName); + + /* Get the endpoints of the lines, and sort from top to bottom */ + h = pixGetHeight(dew->pixs); + ret = dewarpGetLineEndPoints(h, ptaa, &ptal1, &ptar1); + if (ret) { + L_INFO("Horiz disparity not built\n", procName); + return 1; + } + if (dew->debug) { + lept_mkdir("lept/dewdebug"); + lept_mkdir("lept/dewarp"); + ptaWriteDebug("/tmp/lept/dewdebug/endpts_left1.pta", ptal1, 1); + ptaWriteDebug("/tmp/lept/dewdebug/endpts_right1.pta", ptar1, 1); + } + + /* Filter the points by x-location to prevent 2-column images + * from getting confused about left and right endpoints. We + * require valid left points to not be farther than + * 0.20 * (remaining distance to the right edge of the image) + * to the right of the leftmost endpoint, and similarly for + * the right endpoints. (Note: x and y are reversed in the pta.) + * Also require end points to be near the medians in the + * upper and lower halves. */ + ret = dewarpFilterLineEndPoints(dew, ptal1, ptar1, &ptal2, &ptar2); + ptaDestroy(&ptal1); + ptaDestroy(&ptar1); + if (ret) { + L_INFO("Not enough filtered end points\n", procName); + return 1; + } + + /* Do a quadratic fit to the left and right endpoints of the + * longest lines. Each line is represented by 3 coefficients: + * x(y) = c2 * y^2 + c1 * y + c0. + * Using the coefficients, sample each fitted curve uniformly + * along the full height of the image. */ + sampling = dew->sampling; + nx = dew->nx; + ny = dew->ny; + + /* Fit the left side, using quadratic LSF on the set of long + * lines. It is not necessary to use the noisy LSF fit + * function, because we've removed outlier end points by + * selecting the long lines. Then uniformly sample along + * this fitted curve. */ + dewarpQuadraticLSF(ptal2, &cl2, &cl1, &cl0, &mederr); + dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */ + dew->leftcurv = lept_roundftoi(1000000. * cl2); /* micro-units */ + L_INFO("Left quad LSF median error = %5.2f\n", procName, mederr); + L_INFO("Left edge slope = %d\n", procName, dew->leftslope); + L_INFO("Left edge curvature = %d\n", procName, dew->leftcurv); + ptal3 = ptaCreate(ny); + for (i = 0; i < ny; i++) { /* uniformly sampled in y */ + y = i * sampling; + applyQuadraticFit(cl2, cl1, cl0, y, &x); + ptaAddPt(ptal3, x, y); + } + + /* Fit the right side in the same way. */ + dewarpQuadraticLSF(ptar2, &cr2, &cr1, &cr0, &mederr); + dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */ + dew->rightcurv = lept_roundftoi(1000000. * cr2); /* micro-units */ + L_INFO("Right quad LSF median error = %5.2f\n", procName, mederr); + L_INFO("Right edge slope = %d\n", procName, dew->rightslope); + L_INFO("Right edge curvature = %d\n", procName, dew->rightcurv); + ptar3 = ptaCreate(ny); + for (i = 0; i < ny; i++) { /* uniformly sampled in y */ + y = i * sampling; + applyQuadraticFit(cr2, cr1, cr0, y, &x); + ptaAddPt(ptar3, x, y); + } + + if (dew->debug) { + PTA *ptalft, *ptarft; + h = pixGetHeight(dew->pixs); + pta1 = ptaCreate(h); + pta2 = ptaCreate(h); + for (i = 0; i < h; i++) { + applyQuadraticFit(cl2, cl1, cl0, i, &x); + ptaAddPt(pta1, x, i); + applyQuadraticFit(cr2, cr1, cr0, i, &x); + ptaAddPt(pta2, x, i); + } + pix1 = pixDisplayPta(NULL, dew->pixs, pta1); + pixDisplayPta(pix1, pix1, pta2); + pixRenderHorizEndPoints(pix1, ptal2, ptar2, 0xff000000); + pixDisplay(pix1, 600, 800); + pixWriteDebug("/tmp/lept/dewmod/0051.png", pix1, IFF_PNG); + pixDestroy(&pix1); + + pix1 = pixDisplayPta(NULL, dew->pixs, pta1); + pixDisplayPta(pix1, pix1, pta2); + ptalft = ptaTranspose(ptal3); + ptarft = ptaTranspose(ptar3); + pixRenderHorizEndPoints(pix1, ptalft, ptarft, 0x0000ff00); + pixDisplay(pix1, 800, 800); + pixWriteDebug("/tmp/lept/dewmod/0052.png", pix1, IFF_PNG); + convertFilesToPdf("/tmp/lept/dewmod", "005", 135, 1.0, 0, 0, + "Dewarp Horiz Disparity", + "/tmp/lept/dewarp/horiz_disparity.pdf"); + fprintf(stderr, "pdf file: /tmp/lept/dewarp/horiz_disparity.pdf\n"); + pixDestroy(&pix1); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + ptaDestroy(&ptalft); + ptaDestroy(&ptarft); + } + + /* Find the x value at the midpoints (in y) of the two vertical lines, + * ptal3 and ptar3. These are the reference values for each of the + * lines. Then use the difference between the these midpoint + * values and the actual x coordinates of the lines to represent + * the horizontal disparity (nald, nard) on the vertical lines + * for the sampled y values. */ + ptaGetPt(ptal3, ny / 2, &refl, NULL); + ptaGetPt(ptar3, ny / 2, &refr, NULL); + nald = numaCreate(ny); + nard = numaCreate(ny); + for (i = 0; i < ny; i++) { + ptaGetPt(ptal3, i, &x, NULL); + numaAddNumber(nald, refl - x); + ptaGetPt(ptar3, i, &x, NULL); + numaAddNumber(nard, refr - x); + } + + /* Now for each pair of sampled values of the two lines (at the + * same value of y), do a linear interpolation to generate + * the horizontal disparity on all sampled points between them. */ + ptaah = ptaaCreate(ny); + for (i = 0; i < ny; i++) { + pta = ptaCreate(2); + numaGetFValue(nald, i, &val); + ptaAddPt(pta, refl, val); + numaGetFValue(nard, i, &val); + ptaAddPt(pta, refr, val); + ptaGetLinearLSF(pta, &c1, &c0, NULL); /* horiz disparity along line */ + ptat = ptaCreate(nx); + for (j = 0; j < nx; j++) { + x = j * sampling; + applyLinearFit(c1, c0, x, &val); + ptaAddPt(ptat, x, val); + } + ptaaAddPta(ptaah, ptat, L_INSERT); + ptaDestroy(&pta); + } + numaDestroy(&nald); + numaDestroy(&nard); + + /* Save the result in a fpix at the specified subsampling */ + fpix = fpixCreate(nx, ny); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + ptaaGetPt(ptaah, i, j, NULL, &val); + fpixSetPixel(fpix, j, i, val); + } + } + dew->samphdispar = fpix; + dew->hsuccess = 1; + ptaDestroy(&ptal2); + ptaDestroy(&ptar2); + ptaDestroy(&ptal3); + ptaDestroy(&ptar3); + ptaaDestroy(&ptaah); + return 0; +} + + +/*! + * \brief dewarpGetTextlineCenters() + * + * \param[in] pixs 1 bpp + * \param[in] debugflag 1 for debug output + * \return ptaa of center values of textlines + * + *
+ * Notes:
+ *      (1) This in general does not have a point for each value
+ *          of x, because there will be gaps between words.
+ *          It doesn't matter because we will fit a quadratic to the
+ *          points that we do have.
+ * 
+ */ +PTAA * +dewarpGetTextlineCenters(PIX *pixs, + l_int32 debugflag) +{ +char buf[64]; +l_int32 i, w, h, bx, by, nsegs, csize1, csize2; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixa1, *pixa2; +PTA *pta; +PTAA *ptaa; + + PROCNAME("dewarpGetTextlineCenters"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + + if (debugflag) L_INFO("finding text line centers\n", procName); + + /* Filter to solidify the text lines within the x-height region, + * and to remove most of the ascenders and descenders. + * We start with a small vertical opening to remove noise beyond + * the line that can cause an error in the line end points. + * The small closing (csize1) is used to bridge the gaps between + * letters. The large closing (csize2) bridges the gaps between + * words; using 1/30 of the page width usually suffices. */ + csize1 = L_MAX(15, w / 80); + csize2 = L_MAX(40, w / 30); + snprintf(buf, sizeof(buf), "o1.3 + c%d.1 + o%d.1 + c%d.1", + csize1, csize1, csize2); + pix1 = pixMorphSequence(pixs, buf, 0); + + /* Remove the components (e.g., embedded images) that have + * long vertical runs (>= 50 pixels). You can't use bounding + * boxes because connected component b.b. of lines can be quite + * tall due to slope and curvature. */ + pix2 = pixMorphSequence(pix1, "e1.50", 0); /* seed */ + pixSeedfillBinary(pix2, pix2, pix1, 8); /* tall components */ + pixXor(pix2, pix2, pix1); /* remove tall */ + + if (debugflag) { + lept_mkdir("lept/dewmod"); + pixWriteDebug("/tmp/lept/dewmod/0011.tif", pix1, IFF_TIFF_G4); + pixDisplayWithTitle(pix1, 0, 600, "pix1", 1); + pixWriteDebug("/tmp/lept/dewmod/0012.tif", pix2, IFF_TIFF_G4); + pixDisplayWithTitle(pix2, 0, 800, "pix2", 1); + } + pixDestroy(&pix1); + + /* Get the 8-connected components ... */ + boxa = pixConnComp(pix2, &pixa1, 8); + pixDestroy(&pix2); + boxaDestroy(&boxa); + if (pixaGetCount(pixa1) == 0) { + pixaDestroy(&pixa1); + return NULL; + } + + /* ... and remove the short width and very short height c.c */ + pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH, + L_SELECT_IF_GT, NULL); + if ((nsegs = pixaGetCount(pixa2)) == 0) { + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + return NULL; + } + if (debugflag) { + pix2 = pixaDisplay(pixa2, w, h); + pixWriteDebug("/tmp/lept/dewmod/0013.tif", pix2, IFF_TIFF_G4); + pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1); + pixDestroy(&pix2); + } + + /* For each c.c., get the weighted center of each vertical column. + * The result is a set of points going approximately through + * the center of the x-height part of the text line. */ + ptaa = ptaaCreate(nsegs); + for (i = 0; i < nsegs; i++) { + pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL); + pix2 = pixaGetPix(pixa2, i, L_CLONE); + pta = dewarpGetMeanVerticals(pix2, bx, by); + ptaaAddPta(ptaa, pta, L_INSERT); + pixDestroy(&pix2); + } + if (debugflag) { + pix1 = pixCreateTemplate(pixs); + pix2 = pixDisplayPtaa(pix1, ptaa); + pixWriteDebug("/tmp/lept/dewmod/0014.tif", pix2, IFF_PNG); + pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + return ptaa; +} + + +/*! + * \brief dewarpGetMeanVerticals() + * + * \param[in] pixs 1 bpp, single c.c. + * \param[in] x,y location of UL corner of pixs, relative to page image + * \return pta (mean y-values in component for each x-value, + * both translated by (x,y + */ +static PTA * +dewarpGetMeanVerticals(PIX *pixs, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, i, j, wpl, sum, count; +l_uint32 *line, *data; +PTA *pta; + + PROCNAME("pixGetMeanVerticals"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + pta = ptaCreate(w); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (j = 0; j < w; j++) { + line = data; + sum = count = 0; + for (i = 0; i < h; i++) { + if (GET_DATA_BIT(line, j) == 1) { + sum += i; + count += 1; + } + line += wpl; + } + if (count == 0) continue; + ptaAddPt(pta, x + j, y + (sum / count)); + } + + return pta; +} + + +/*! + * \brief dewarpRemoveShortLines() + * + * \param[in] pixs 1 bpp + * \param[in] ptaas input lines + * \param[in] fract minimum fraction of longest line to keep + * \param[in] debugflag + * \return ptaad containing only lines of sufficient length, + * or NULL on error + */ +PTAA * +dewarpRemoveShortLines(PIX *pixs, + PTAA *ptaas, + l_float32 fract, + l_int32 debugflag) +{ +l_int32 w, n, i, index, maxlen, len; +l_float32 minx, maxx; +NUMA *na, *naindex; +PIX *pix1, *pix2; +PTA *pta; +PTAA *ptaad; + + PROCNAME("dewarpRemoveShortLines"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!ptaas) + return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL); + + pixGetDimensions(pixs, &w, NULL, NULL); + n = ptaaGetCount(ptaas); + ptaad = ptaaCreate(n); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pta = ptaaGetPta(ptaas, i, L_CLONE); + ptaGetRange(pta, &minx, &maxx, NULL, NULL); + numaAddNumber(na, maxx - minx + 1); + ptaDestroy(&pta); + } + + /* Sort by length and find all that are long enough */ + naindex = numaGetSortIndex(na, L_SORT_DECREASING); + numaGetIValue(naindex, 0, &index); + numaGetIValue(na, index, &maxlen); + if (maxlen < 0.5 * w) + L_WARNING("lines are relatively short\n", procName); + pta = ptaaGetPta(ptaas, index, L_CLONE); + ptaaAddPta(ptaad, pta, L_INSERT); + for (i = 1; i < n; i++) { + numaGetIValue(naindex, i, &index); + numaGetIValue(na, index, &len); + if (len < fract * maxlen) break; + pta = ptaaGetPta(ptaas, index, L_CLONE); + ptaaAddPta(ptaad, pta, L_INSERT); + } + + if (debugflag) { + pix1 = pixCopy(NULL, pixs); + pix2 = pixDisplayPtaa(pix1, ptaad); + pixDisplayWithTitle(pix2, 0, 200, "pix4", 1); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + numaDestroy(&na); + numaDestroy(&naindex); + return ptaad; +} + + +/*! + * \brief dewarpGetLineEndPoints() + * + * \param[in] h height of pixs + * \param[in] ptaa lines + * \param[out] pptal left end points of each line + * \param[out] pptar right end points of each line + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) We require that the set of end points extends over 45% of the
+ *          height of the input image, to insure good coverage and
+ *          avoid extrapolating the curvature too far beyond the
+ *          actual textlines.  Large extrapolations are particularly
+ *          dangerous if used as a reference model.  We also require
+ *          at least 10 lines of text.
+ *      (2) We sort the lines from top to bottom (sort by x in the ptas).
+ *      (3) For fitting the endpoints, x = f(y), we transpose x and y.
+ *          Thus all these ptas have x and y swapped!
+ * 
+ */ +static l_int32 +dewarpGetLineEndPoints(l_int32 h, + PTAA *ptaa, + PTA **pptal, + PTA **pptar) +{ +l_int32 i, n, npt, x, y; +l_float32 miny, maxy, ratio; +PTA *pta, *ptal1, *ptar1; + + PROCNAME("dewarpGetLineEndPoints"); + + if (!pptal || !pptar) + return ERROR_INT("&ptal and &ptar not both defined", procName, 1); + *pptal = *pptar = NULL; + if (!ptaa) + return ERROR_INT("ptaa undefined", procName, 1); + + /* Are there at least 10 lines? */ + n = ptaaGetCount(ptaa); + if (n < MinLinesForHoriz1) { + L_INFO("only %d lines; too few\n", procName, n); + return 1; + } + + /* Extract the line end points, and transpose x and y values */ + ptal1 = ptaCreate(n); + ptar1 = ptaCreate(n); + for (i = 0; i < n; i++) { + pta = ptaaGetPta(ptaa, i, L_CLONE); + ptaGetIPt(pta, 0, &x, &y); + ptaAddPt(ptal1, y, x); /* transpose */ + npt = ptaGetCount(pta); + ptaGetIPt(pta, npt - 1, &x, &y); + ptaAddPt(ptar1, y, x); /* transpose */ + ptaDestroy(&pta); + } + + /* Use the min and max of the y value on the left side. */ + ptaGetRange(ptal1, &miny, &maxy, NULL, NULL); + ratio = (maxy - miny) / (l_float32)h; + if (ratio < MinRatioLinesToHeight) { + L_INFO("ratio lines to height, %f, too small\n", procName, ratio); + ptaDestroy(&ptal1); + ptaDestroy(&ptar1); + return 1; + } + + /* Sort from top to bottom */ + *pptal = ptaSort(ptal1, L_SORT_BY_X, L_SORT_INCREASING, NULL); + *pptar = ptaSort(ptar1, L_SORT_BY_X, L_SORT_INCREASING, NULL); + ptaDestroy(&ptal1); + ptaDestroy(&ptar1); + return 0; +} + + +/*! + * \brief dewarpFilterLineEndPoints() + * + * \param[in] dew + * \param[in] ptal input left end points of each line + * \param[in] ptar input right end points of each line + * \param[out] pptalf filtered left end points + * \param[out] pptarf filtered right end points + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) Avoid confusion with multiple columns by requiring that line
+ *          end points be close enough to leftmost and rightmost end points.
+ *          Must have at least 8 points on left and right after this step.
+ *      (2) Apply second filtering step, find the median positions in
+ *          top and bottom halves, and removing end points that are
+ *          displaced too much from these in the x direction.
+ *          Must have at least 6 points on left and right after this step.
+ *      (3) Reminder: x and y in the pta are transposed; think x = f(y).
+ * 
+ */ +static l_int32 +dewarpFilterLineEndPoints(L_DEWARP *dew, + PTA *ptal, + PTA *ptar, + PTA **pptalf, + PTA **pptarf) +{ +l_int32 w, i, n; +l_float32 ymin, ymax, xvall, xvalr, yvall, yvalr; +PTA *ptal1, *ptar1, *ptal2, *ptar2; + + PROCNAME("dewarpFilterLineEndPoints"); + if (!ptal || !ptar) + return ERROR_INT("ptal or ptar not defined", procName, 1); + *pptalf = *pptarf = NULL; + + /* First filter for lines near left and right margins */ + w = pixGetWidth(dew->pixs); + ptaGetMinMax(ptal, NULL, &ymin, NULL, NULL); + ptaGetMinMax(ptar, NULL, NULL, NULL, &ymax); + n = ptaGetCount(ptal); /* ptar is the same size; at least 10 */ + ptal1 = ptaCreate(n); + ptar1 = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(ptal, i, &xvall, &yvall); + ptaGetPt(ptar, i, &xvalr, &yvalr); + if (yvall < ymin + 0.20 * (w - ymin) && + yvalr > 0.80 * ymax) { + ptaAddPt(ptal1, xvall, yvall); + ptaAddPt(ptar1, xvalr, yvalr); + } + } + if (dew->debug) { + ptaWriteDebug("/tmp/lept/dewdebug/endpts_left2.pta", ptal1, 1); + ptaWriteDebug("/tmp/lept/dewdebug/endpts_right2.pta", ptar1, 1); + } + + n = L_MIN(ptaGetCount(ptal1), ptaGetCount(ptar1)); + if (n < MinLinesForHoriz1 - 2) { + ptaDestroy(&ptal1); + ptaDestroy(&ptar1); + L_INFO("First filter: only %d endpoints; needed 8\n", procName, n); + return 1; + } + + /* Remove outlier points */ + ptal2 = dewarpRemoveBadEndPoints(w, ptal1); + ptar2 = dewarpRemoveBadEndPoints(w, ptar1); + ptaDestroy(&ptal1); + ptaDestroy(&ptar1); + if (!ptal2 || !ptar2) { + ptaDestroy(&ptal2); + ptaDestroy(&ptar2); + L_INFO("Second filter: too few endpoints left after outliers removed\n", + procName); + return 1; + } + if (dew->debug) { + ptaWriteDebug("/tmp/lept/dewdebug/endpts_left3.pta", ptal2, 1); + ptaWriteDebug("/tmp/lept/dewdebug/endpts_right3.pta", ptar2, 1); + } + + *pptalf = ptal2; + *pptarf = ptar2; + return 0; +} + + +/*! + * \brief dewarpRemoveBadEndPoints() + * + * \param[in] w width of input image + * \param[in] ptas left or right line end points + * \return ptad filtered left or right end points, or NULL on error. + * + *
+ * Notes:
+ *      (1) The input set is sorted by line position (x value).
+ *          Break into two (upper and lower); for each find the median
+ *          horizontal (y value), and remove all points farther than
+ *          a fraction of the image width from this.  Make sure each
+ *          part still has at least 3 points, and join the two sections
+ *          before returning.
+ *      (2) Reminder: x and y in the pta are transposed; think x = f(y).
+ * 
+ */ +static PTA * +dewarpRemoveBadEndPoints(l_int32 w, + PTA *ptas) +{ +l_int32 i, n, nu, nd; +l_float32 rval, xval, yval, delta; +PTA *ptau1, *ptau2, *ptad1, *ptad2; + + PROCNAME("dewarpRemoveBadEndPoints"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + delta = AllowedWidthFract * w; + n = ptaGetCount(ptas); /* will be at least 8 */ + + /* Check the upper half */ + ptau1 = ptaSelectRange(ptas, 0, n / 2); + ptaGetRankValue(ptau1, 0.5, NULL, L_SORT_BY_Y, &rval); + nu = ptaGetCount(ptau1); + ptau2 = ptaCreate(nu); + for (i = 0; i < nu; i++) { + ptaGetPt(ptau1, i, &xval, &yval); /* transposed */ + if (L_ABS(rval - yval) <= delta) + ptaAddPt(ptau2, xval, yval); + } + ptaDestroy(&ptau1); + if (ptaGetCount(ptau2) < MinLinesForHoriz2) { + ptaDestroy(&ptau2); + L_INFO("Second filter: upper set is too small after outliers removed\n", + procName); + return NULL; + } + + /* Check the lower half */ + ptad1 = ptaSelectRange(ptas, n / 2 + 1, -1); + ptaGetRankValue(ptad1, 0.5, NULL, L_SORT_BY_Y, &rval); + nd = ptaGetCount(ptad1); + ptad2 = ptaCreate(nd); + for (i = 0; i < nd; i++) { + ptaGetPt(ptad1, i, &xval, &yval); /* transposed */ + if (L_ABS(rval - yval) <= delta) + ptaAddPt(ptad2, xval, yval); + } + ptaDestroy(&ptad1); + if (ptaGetCount(ptad2) < MinLinesForHoriz2) { + ptaDestroy(&ptau2); + ptaDestroy(&ptad2); + L_INFO("Second filter: lower set is too small after outliers removed\n", + procName); + return NULL; + } + + ptaJoin(ptau2, ptad2, 0, -1); + ptaDestroy(&ptad2); + return ptau2; +} + + +/*! + * \brief dewarpIsLineCoverageValid() + * + * \param[in] ptaa of validated lines + * \param[in] h height of pix + * \param[out] pntop number of lines in top half + * \param[out] pnbot number of lines in bottom half + * \param[out] pytop location of top line + * \param[out] pybot location of bottom line + * \return 1 if coverage is valid, 0 if not or on error. + * + *
+ * Notes:
+ *      (1) The criterion for valid coverage is:
+ *          (a) there must be at least 4 lines in each half (top and bottom)
+ *              of the image.
+ *          (b) the coverage must be at least 50% of the image height
+ * 
+ */ +static l_int32 +dewarpIsLineCoverageValid(PTAA *ptaa, + l_int32 h, + l_int32 *pntop, + l_int32 *pnbot, + l_int32 *pytop, + l_int32 *pybot) +{ +l_int32 i, n, iy, both_halves, ntop, nbot, ytop, ybot, nmin; +l_float32 y, fraction; +NUMA *na; + + PROCNAME("dewarpIsLineCoverageValid"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 0); + if ((n = ptaaGetCount(ptaa)) == 0) + return ERROR_INT("ptaa empty", procName, 0); + if (h <= 0) + return ERROR_INT("invalid h", procName, 0); + if (!pntop || !pnbot) + return ERROR_INT("&ntop and &nbot not defined", procName, 0); + if (!pytop || !pybot) + return ERROR_INT("&ytop and &ybot not defined", procName, 0); + + na = numaCreate(n); + for (i = 0; i < n; i++) { + ptaaGetPt(ptaa, i, 0, NULL, &y); + numaAddNumber(na, y); + } + numaSort(na, na, L_SORT_INCREASING); + for (i = 0, ntop = 0; i < n; i++) { + numaGetIValue(na, i, &iy); + if (i == 0) ytop = iy; + if (i == n - 1) ybot = iy; + if (iy < 0.5 * h) + ntop++; + } + numaDestroy(&na); + nbot = n - ntop; + *pntop = ntop; + *pnbot = nbot; + *pytop = ytop; + *pybot = ybot; + nmin = 4; /* minimum number of lines required in each half */ + both_halves = (ntop >= nmin) && (nbot >= nmin); + fraction = (l_float32)(ybot - ytop) / (l_float32)h; + if (both_halves && fraction > 0.50) + return 1; + return 0; +} + + +/*! + * \brief dewarpQuadraticLSF() + * + * \param[in] ptad left or right end points of longest lines + * \param[out] pa coeff a of LSF: y = ax^2 + bx + c + * \param[out] pb coeff b of LSF: y = ax^2 + bx + c + * \param[out] pc coeff c of LSF: y = ax^2 + bx + c + * \param[out] pmederr [optional] median error + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This is used for finding the left or right sides of
+ *          the text block, computed as a quadratic curve.
+ *          Only the longest lines are input, so there are
+ *          no outliers.
+ *      (2) The ptas for the end points all have x and y swapped.
+ * 
+ */ +static l_int32 +dewarpQuadraticLSF(PTA *ptad, + l_float32 *pa, + l_float32 *pb, + l_float32 *pc, + l_float32 *pmederr) +{ +l_int32 i, n; +l_float32 x, y, xp, c0, c1, c2; +NUMA *naerr; + + PROCNAME("dewarpQuadraticLSF"); + + if (pmederr) *pmederr = 0.0; + if (!pa || !pb || !pc) + return ERROR_INT("not all ptrs are defined", procName, 1); + *pa = *pb = *pc = 0.0; + if (!ptad) + return ERROR_INT("ptad not defined", procName, 1); + + /* Fit to the longest lines */ + ptaGetQuadraticLSF(ptad, &c2, &c1, &c0, NULL); + *pa = c2; + *pb = c1; + *pc = c0; + + /* Optionally, find the median error */ + if (pmederr) { + n = ptaGetCount(ptad); + naerr = numaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(ptad, i, &y, &xp); + applyQuadraticFit(c2, c1, c0, y, &x); + numaAddNumber(naerr, L_ABS(x - xp)); + } + numaGetMedian(naerr, pmederr); + numaDestroy(&naerr); + } + return 0; +} + +/*----------------------------------------------------------------------* + * Build disparity model for slope near binding * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpFindHorizSlopeDisparity() + * + * \param[in] dew + * \param[in] pixb 1 bpp, with vert and horiz disparity removed + * \param[in] fractthresh threshold fractional difference in density + * \param[in] parity 0 if even page, 1 if odd page + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) %fractthresh is a threshold on the fractional difference in stroke
+ *          density between between left and right sides.  Process this
+ *          disparity only if the absolute value of the fractional
+ *          difference equals or exceeds this threshold.
+ *      (2) %parity indicates where the binding is: on the left for
+ *          %parity == 0 and on the right for %parity == 1.
+ *      (3) This takes a 1 bpp %pixb where both vertical and horizontal
+ *          disparity have been applied, so the text lines are straight and,
+ *          more importantly, the line end points are vertically aligned.
+ *          It estimates the foreshortening of the characters on the
+ *          binding side, and if significant, computes a one-dimensional
+ *          horizontal disparity function to compensate.
+ *      (4) The first attempt was to use the average width of the
+ *          connected components (c.c.) in vertical slices.  This does not work
+ *          reliably, because the horizontal compression of the text is
+ *          often accompanied by horizontal joining of c.c.
+ *      (5) We use the density of vertical strokes, measured by first using
+ *          a vertical opening, which improves the signal.  The result
+ *          is relatively insensitive to the size of the opening; we use
+ *          a 10-pixel opening.  The relative density is measured by
+ *          finding the number of c.c. in a full height sliding window
+ *          of width 50 pixels, and compute every 25 pixels.  Similar results
+ *          are obtained counting c.c. that either intersect the window
+ *          or are fully contained within it.
+ *      (6) Debug output goes to /tmp/lept/dewmod/ for collection into a pdf.
+ * 
+ */ +l_ok +dewarpFindHorizSlopeDisparity(L_DEWARP *dew, + PIX *pixb, + l_float32 fractthresh, + l_int32 parity) +{ +l_int32 i, j, x, n1, n2, nb, ne, count, w, h, ival, prev; +l_int32 istart, iend, first, last, x0, x1, nx, ny; +l_float32 fract, delta, sum, aveval, fval, del, denom; +l_float32 ca, cb, cc, cd, ce, y; +BOX *box; +BOXA *boxa1, *boxa2; +NUMA *na1, *na2, *na3, *na4, *nasum; +PIX *pix1; +PTA *pta1; +FPIX *fpix; + + PROCNAME("dewarpFindHorizSlopeDisparity"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + if (!dew->vvalid || !dew->hvalid) + return ERROR_INT("invalid vert or horiz disparity model", procName, 1); + if (!pixb || pixGetDepth(pixb) != 1) + return ERROR_INT("pixb not defined or not 1 bpp", procName, 1); + + if (dew->debug) L_INFO("finding slope horizontal disparity\n", procName); + + /* Find the bounding boxes of the vertical strokes; remove noise */ + pix1 = pixMorphSequence(pixb, "o1.10", 0); + pixDisplay(pix1, 100, 100); + boxa1 = pixConnCompBB(pix1, 4); + boxa2 = boxaSelectBySize(boxa1, 0, 5, L_SELECT_HEIGHT, L_SELECT_IF_GT, + NULL); + nb = boxaGetCount(boxa2); + fprintf(stderr, "number of components: %d\n", nb); + boxaDestroy(&boxa1); + + /* Estimate the horizontal density of vertical strokes */ + na1 = numaCreate(0); + numaSetParameters(na1, 0, 25); + pixGetDimensions(pixb, &w, &h, NULL); + for (x = 0; x + 50 < w; x += 25) { + box = boxCreate(x, 0, 50, h); + boxaContainedInBoxCount(boxa2, box, &count); + numaAddNumber(na1, count); + boxDestroy(&box); + } + if (dew->debug) { + lept_mkdir("lept/dew"); + gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/dew/0091", NULL); + lept_mv("/tmp/lept/dew/0091.png", "lept/dewmod", NULL, NULL); + pixWriteDebug("/tmp/lept/dewmod/0090.png", pix1, IFF_PNG); + } + pixDestroy(&pix1); + boxaDestroy(&boxa2); + + /* Find the left and right end local maxima; if the difference + * is small, quit. */ + n1 = numaGetCount(na1); + prev = 0; + istart = 0; + first = 0; + for (i = 0; i < n1; i++) { + numaGetIValue(na1, i, &ival); + if (ival >= prev) { + prev = ival; + continue; + } else { + first = prev; + istart = i - 1; + break; + } + } + prev = 0; + last = 0; + iend = n1 - 1; + for (i = n1 - 1; i >= 0; i--) { + numaGetIValue(na1, i, &ival); + if (ival >= prev) { + prev = ival; + continue; + } else { + last = prev; + iend = i + 1; + break; + } + } + na2 = numaClipToInterval(na1, istart, iend); + numaDestroy(&na1); + n2 = numaGetCount(na2); + delta = (parity == 0) ? last - first : first - last; + denom = L_MAX(1.0, (l_float32)(L_MIN(first, last))); + fract = (l_float32)delta / denom; + if (dew->debug) { + L_INFO("Slope-disparity: first = %d, last = %d, fract = %7.3f\n", + procName, first, last, fract); + gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0092", NULL); + lept_mv("/tmp/lept/dew/0092.png", "lept/dewmod", NULL, NULL); + } + if (fract < fractthresh) { + L_INFO("Small slope-disparity: first = %d, last = %d, fract = %7.3f\n", + procName, first, last, fract); + numaDestroy(&na2); + return 0; + } + + /* Find the density far from the binding, and normalize to 1. */ + ne = n2 - n2 % 2; + if (parity == 0) + numaGetSumOnInterval(na2, 0, ne / 2 - 1, &sum); + else /* parity == 1 */ + numaGetSumOnInterval(na2, ne / 2, ne - 1, &sum); + denom = L_MAX(1.0, (l_float32)(ne / 2)); + aveval = sum / denom; + na3 = numaMakeConstant(aveval, n2); + numaArithOp(na2, na2, na3, L_ARITH_DIVIDE); + numaDestroy(&na3); + if (dew->debug) { + L_INFO("Average background density: %5.1f\n", procName, aveval); + gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0093", NULL); + lept_mv("/tmp/lept/dew/0093.png", "lept/dewmod", NULL, NULL); + } + + /* Fit the normalized density curve to a quartic */ + pta1 = numaConvertToPta1(na2); + ptaWriteStream(stderr, pta1, 0); +/* ptaGetQuadraticLSF(pta1, NULL, NULL, NULL, &na3); */ + ptaGetQuarticLSF(pta1, &ca, &cb, &cc, &cd, &ce, &na3); + ptaGetArrays(pta1, &na4, NULL); + if (dew->debug) { + gplotSimpleXY1(na4, na3, GPLOT_LINES, GPLOT_PNG, + "/tmp/lept/dew/0094", NULL); + lept_mv("/tmp/lept/dew/0094.png", "lept/dewmod", NULL, NULL); + } + ptaDestroy(&pta1); + + /* Integrate from the high point down to 1 (or v.v) to get the + * disparity needed to make the density constant. */ + nasum = numaMakeConstant(0, w); /* area under the curve above 1.0 */ + if (parity == 0) { + for (i = n2 - 1; i >= 0; i--) { + numaGetFValue(na3, i, &fval); + if (fval < 1.0) break; + } + numaGetIValue(na4, i + 1, &x0); + numaGetIValue(na4, n2 - 1, &x1); + numaSetParameters(nasum, x0, 1); + sum = 0.0; + for (x = x0; x < x1; x++) { + applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y); + sum += (y - 1.0); + numaReplaceNumber(nasum, x, sum); + } + for (x = x1; x < w; x++) + numaReplaceNumber(nasum, x, sum); + } else { /* parity == 1 */ + for (i = 0; i < n2; i++) { + numaGetFValue(na3, i, &fval); + if (fval < 1.0) break; + } + numaGetIValue(na4, 0, &x0); + numaGetIValue(na4, i - 1, &x1); + numaSetParameters(nasum, x0, 1); + sum = 0.0; + for (x = x1; x >= x0; x--) { + applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y); + sum += (y - 1.0); + numaReplaceNumber(nasum, x, sum); + } + for (x = x0; x >= 0; x--) + numaReplaceNumber(nasum, x, sum); + } + + /* Save the result in a fpix at the specified subsampling */ + nx = dew->nx; + ny = dew->ny; + fpix = fpixCreate(nx, ny); + del = (l_float32)w / (l_float32)nx; + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + x = del * j; + numaGetFValue(nasum, x, &fval); + fpixSetPixel(fpix, j, i, fval); + } + } + dew->sampydispar = fpix; + dew->ysuccess = 1; + + numaDestroy(&na2); + numaDestroy(&na3); + numaDestroy(&na4); + numaDestroy(&nasum); + return 0; +} + + +/*----------------------------------------------------------------------* + * Build line disparity model * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpBuildLineModel() + * + * \param[in] dew + * \param[in] opensize size of opening to remove perpendicular lines + * \param[in] debugfile use NULL to skip writing this + * \return 0 if OK, 1 if unable to build the model or on error + * + *
+ * Notes:
+ *      (1) This builds the horizontal and vertical disparity arrays
+ *          for an input of ruled lines, typically for calibration.
+ *          In book scanning, you could lay the ruled paper over a page.
+ *          Then for that page and several below it, you can use the
+ *          disparity correction of the line model to dewarp the pages.
+ *      (2) The dew has been initialized with the image of ruled lines.
+ *          These lines must be continuous, but we do a small amount
+ *          of pre-processing here to insure that.
+ *      (3) %opensize is typically about 8.  It must be larger than
+ *          the thickness of the lines to be extracted.  This is the
+ *          default value, which is applied if %opensize < 3.
+ *      (4) Sets vsuccess = 1 and hsuccess = 1 if the vertical and/or
+ *          horizontal disparity arrays build.
+ *      (5) Similar to dewarpBuildPageModel(), except here the vertical
+ *          and horizontal disparity arrays are both built from ruled lines.
+ *          See notes there.
+ * 
+ */ +l_ok +dewarpBuildLineModel(L_DEWARP *dew, + l_int32 opensize, + const char *debugfile) +{ +char buf[64]; +l_int32 i, j, bx, by, ret, nlines; +BOXA *boxa; +PIX *pixs, *pixh, *pixv, *pix, *pix1, *pix2; +PIXA *pixa1, *pixa2; +PTA *pta; +PTAA *ptaa1, *ptaa2; + + PROCNAME("dewarpBuildLineModel"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + if (opensize < 3) { + L_WARNING("opensize should be >= 3; setting to 8\n", procName); + opensize = 8; /* default */ + } + + dew->debug = (debugfile) ? 1 : 0; + dew->vsuccess = dew->hsuccess = 0; + pixs = dew->pixs; + if (debugfile) { + lept_rmdir("lept/dewline"); /* erase previous images */ + lept_mkdir("lept/dewline"); + lept_rmdir("lept/dewmod"); /* erase previous images */ + lept_mkdir("lept/dewmod"); + lept_mkdir("lept/dewarp"); + pixDisplayWithTitle(pixs, 0, 0, "pixs", 1); + pixWriteDebug("/tmp/lept/dewline/001.png", pixs, IFF_PNG); + } + + /* Extract and solidify the horizontal and vertical lines. We use + * the horizontal lines to derive the vertical disparity, and v.v. + * Both disparities are computed using the vertical disparity + * algorithm; the horizontal disparity is found from the + * vertical lines by rotating them clockwise by 90 degrees. + * On the first pass, we compute the horizontal disparity, from + * the vertical lines, by rotating them by 90 degrees (so they + * are horizontal) and computing the vertical disparity on them; + * we rotate the resulting fpix array for the horizontal disparity + * back by -90 degrees. On the second pass, we compute the vertical + * disparity from the horizontal lines in the usual fashion. */ + snprintf(buf, sizeof(buf), "d1.3 + c%d.1 + o%d.1", opensize - 2, opensize); + pixh = pixMorphSequence(pixs, buf, 0); /* horiz */ + snprintf(buf, sizeof(buf), "d3.1 + c1.%d + o1.%d", opensize - 2, opensize); + pix1 = pixMorphSequence(pixs, buf, 0); /* vert */ + pixv = pixRotateOrth(pix1, 1); /* vert rotated to horizontal */ + pixa1 = pixaCreate(2); + pixaAddPix(pixa1, pixv, L_INSERT); /* get horizontal disparity first */ + pixaAddPix(pixa1, pixh, L_INSERT); + pixDestroy(&pix1); + + /*--------------------------------------------------------------*/ + /* Process twice: first for horiz disparity, then for vert */ + /*--------------------------------------------------------------*/ + for (i = 0; i < 2; i++) { + pix = pixaGetPix(pixa1, i, L_CLONE); + pixDisplay(pix, 0, 900); + boxa = pixConnComp(pix, &pixa2, 8); + nlines = boxaGetCount(boxa); + boxaDestroy(&boxa); + if (nlines < dew->minlines) { + L_WARNING("only found %d lines\n", procName, nlines); + pixDestroy(&pix); + pixaDestroy(&pixa1); + continue; + } + + /* Identify the pixels along the skeleton of each line */ + ptaa1 = ptaaCreate(nlines); + for (j = 0; j < nlines; j++) { + pixaGetBoxGeometry(pixa2, j, &bx, &by, NULL, NULL); + pix1 = pixaGetPix(pixa2, j, L_CLONE); + pta = dewarpGetMeanVerticals(pix1, bx, by); + ptaaAddPta(ptaa1, pta, L_INSERT); + pixDestroy(&pix1); + } + pixaDestroy(&pixa2); + if (debugfile) { + pix1 = pixConvertTo32(pix); + pix2 = pixDisplayPtaa(pix1, ptaa1); + snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 2 + 2 * i); + pixWriteDebug(buf, pix2, IFF_PNG); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + /* Remove all lines that are not at least 0.75 times the length + * of the longest line. */ + ptaa2 = dewarpRemoveShortLines(pix, ptaa1, 0.75, DEBUG_SHORT_LINES); + if (debugfile) { + pix1 = pixConvertTo32(pix); + pix2 = pixDisplayPtaa(pix1, ptaa2); + snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 3 + 2 * i); + pixWriteDebug(buf, pix2, IFF_PNG); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + ptaaDestroy(&ptaa1); + nlines = ptaaGetCount(ptaa2); + if (nlines < dew->minlines) { + pixDestroy(&pix); + ptaaDestroy(&ptaa2); + L_WARNING("%d lines: too few to build model\n", procName, nlines); + continue; + } + + /* Get the sampled 'vertical' disparity from the textline + * centers. The disparity array will push pixels vertically + * so that each line is flat and centered at the y-position + * of the mid-point. */ + ret = dewarpFindVertDisparity(dew, ptaa2, 1 - i); + + /* If i == 0, move the result to the horizontal disparity, + * rotating it back by -90 degrees. */ + if (i == 0) { /* horizontal disparity, really */ + if (ret) { + L_WARNING("horizontal disparity not built\n", procName); + } else { + L_INFO("hsuccess = 1\n", procName); + dew->samphdispar = fpixRotateOrth(dew->sampvdispar, 3); + fpixDestroy(&dew->sampvdispar); + if (debugfile) + lept_mv("/tmp/lept/dewarp/vert_disparity.pdf", + "lept/dewarp", "horiz_disparity.pdf", NULL); + } + dew->hsuccess = dew->vsuccess; + dew->vsuccess = 0; + } else { /* i == 1 */ + if (ret) + L_WARNING("vertical disparity not built\n", procName); + else + L_INFO("vsuccess = 1\n", procName); + } + ptaaDestroy(&ptaa2); + pixDestroy(&pix); + } + pixaDestroy(&pixa1); + + /* Debug output */ + if (debugfile) { + if (dew->vsuccess == 1) { + dewarpPopulateFullRes(dew, NULL, 0, 0); + pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); + pixWriteDebug("/tmp/lept/dewline/006.png", pix1, IFF_PNG); + pixDisplay(pix1, 1000, 0); + pixDestroy(&pix1); + } + if (dew->hsuccess == 1) { + pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); + pixWriteDebug("/tmp/lept/dewline/007.png", pix1, IFF_PNG); + pixDisplay(pix1, 1000, 0); + pixDestroy(&pix1); + } + convertFilesToPdf("/tmp/lept/dewline", NULL, 135, 1.0, 0, 0, + "Dewarp Build Line Model", debugfile); + fprintf(stderr, "pdf file: %s\n", debugfile); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Query model status * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaModelStatus() + * + * \param[in] dewa + * \param[in] pageno + * \param[out] pvsuccess [optional] 1 on success + * \param[out] phsuccess [optional] 1 on success + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This tests if a model has been built, not if it is valid.
+ * 
+ */ +l_ok +dewarpaModelStatus(L_DEWARPA *dewa, + l_int32 pageno, + l_int32 *pvsuccess, + l_int32 *phsuccess) +{ +L_DEWARP *dew; + + PROCNAME("dewarpaModelStatus"); + + if (pvsuccess) *pvsuccess = 0; + if (phsuccess) *phsuccess = 0; + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL) + return ERROR_INT("dew not retrieved", procName, 1); + if (pvsuccess) *pvsuccess = dew->vsuccess; + if (phsuccess) *phsuccess = dew->hsuccess; + return 0; +} + + +/*----------------------------------------------------------------------* + * Rendering helpers * + *----------------------------------------------------------------------*/ +/*! + * \brief pixRenderMidYs() + * + * \param[in] pixs 32 bpp + * \param[in] namidys y location of reference lines for vertical disparity + * \param[in] linew width of rendered line; typ 2 + * \return 0 if OK, 1 on error + */ +static l_int32 +pixRenderMidYs(PIX *pixs, + NUMA *namidys, + l_int32 linew) +{ +l_int32 i, n, w, yval, rval, gval, bval; +PIXCMAP *cmap; + + PROCNAME("pixRenderMidYs"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!namidys) + return ERROR_INT("namidys not defined", procName, 1); + + w = pixGetWidth(pixs); + n = numaGetCount(namidys); + cmap = pixcmapCreateRandom(8, 0, 0); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i % 256, &rval, &gval, &bval); + numaGetIValue(namidys, i, &yval); + pixRenderLineArb(pixs, 0, yval, w, yval, linew, rval, gval, bval); + } + pixcmapDestroy(&cmap); + return 0; +} + + +/*! + * \brief pixRenderHorizEndPoints() + * + * \param[in] pixs 32 bpp + * \param[in] ptal left side line end points + * \param[in] ptar right side line end points + * \param[in] color 0xrrggbb00 + * \return 0 if OK, 1 on error + */ +static l_int32 +pixRenderHorizEndPoints(PIX *pixs, + PTA *ptal, + PTA *ptar, + l_uint32 color) +{ +PIX *pixcirc; +PTA *ptalt, *ptart, *ptacirc; + + PROCNAME("pixRenderHorizEndPoints"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!ptal || !ptar) + return ERROR_INT("ptal and ptar not both defined", procName, 1); + + ptacirc = generatePtaFilledCircle(5); + pixcirc = pixGenerateFromPta(ptacirc, 11, 11); + ptalt = ptaTranspose(ptal); + ptart = ptaTranspose(ptar); + + pixDisplayPtaPattern(pixs, pixs, ptalt, pixcirc, 5, 5, color); + pixDisplayPtaPattern(pixs, pixs, ptart, pixcirc, 5, 5, color); + ptaDestroy(&ptacirc); + ptaDestroy(&ptalt); + ptaDestroy(&ptart); + pixDestroy(&pixcirc); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dewarp3.c b/hgdriver/3rdparty/hgOCR/leptonica/dewarp3.c new file mode 100644 index 0000000..0159a56 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dewarp3.c @@ -0,0 +1,1012 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dewarp3.c + *
+ *
+ *    Applying and stripping the page disparity model
+ *
+ *      Apply disparity array to pix
+ *          l_int32            dewarpaApplyDisparity()
+ *          static l_int32     dewarpaApplyInit()
+ *          static PIX        *pixApplyVertDisparity()
+ *          static PIX        *pixApplyHorizDisparity()
+ *
+ *      Apply disparity array to boxa
+ *          l_int32            dewarpaApplyDisparityBoxa()
+ *          static BOXA       *boxaApplyDisparity()
+ *
+ *      Stripping out data and populating full res disparity
+ *          l_int32            dewarpMinimize()
+ *          l_int32            dewarpPopulateFullRes()
+ *
+ *      Static functions not presently in use
+ *          static FPIX       *fpixSampledDisparity()
+ *          static FPIX       *fpixExtraHorizDisparity()
+ *
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_int32 dewarpaApplyInit(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, + l_int32 x, l_int32 y, L_DEWARP **pdew, + const char *debugfile); +static PIX *pixApplyVertDisparity(L_DEWARP *dew, PIX *pixs, l_int32 grayin); +static PIX * pixApplyHorizDisparity(L_DEWARP *dew, PIX *pixs, l_int32 grayin); +static BOXA *boxaApplyDisparity(L_DEWARP *dew, BOXA *boxa, l_int32 direction, + l_int32 mapdir); + +/*----------------------------------------------------------------------* + * Apply warping disparity array to pixa * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaApplyDisparity() + * + * \param[in] dewa + * \param[in] pageno of page model to be used; may be a ref model + * \param[in] pixs image to be modified; can be 1, 8 or 32 bpp + * \param[in] grayin gray value, from 0 to 255, for pixels brought in; + * use -1 to use pixels on the boundary of pixs + * \param[in] x, y origin for generation of disparity arrays + * \param[out] ppixd disparity corrected image + * \param[in] debugfile use NULL to skip writing this + * \return 0 if OK, 1 on error no models or ref models available + * + *
+ * Notes:
+ *      (1) This applies the disparity arrays to the specified image.
+ *      (2) Specify gray color for pixels brought in from the outside:
+ *          0 is black, 255 is white.  Use -1 to select pixels from the
+ *          boundary of the source image.
+ *      (3) If the models and ref models have not been validated, this
+ *          will do so by calling dewarpaInsertRefModels().
+ *      (4) This works with both stripped and full resolution page models.
+ *          If the full res disparity array(s) are missing, they are remade.
+ *      (5) The caller must handle errors that are returned because there
+ *          are no valid models or ref models for the page -- typically
+ *          by using the input pixs.
+ *      (6) If there is no model for %pageno, this will use the model for
+ *          'refpage' and put the result in the dew for %pageno.
+ *      (7) This populates the full resolution disparity arrays if
+ *          necessary.  If x and/or y are positive, they are used,
+ *          in conjunction with pixs, to determine the required
+ *          slope-based extension of the full resolution disparity
+ *          arrays in each direction.  When (x,y) == (0,0), all
+ *          extension is to the right and down.  Nonzero values of (x,y)
+ *          are useful for dewarping when pixs is deliberately undercropped.
+ *      (8) Important: when applying disparity to a number of images,
+ *          after calling this function and saving the resulting pixd,
+ *          you should call dewarpMinimize(dew) on the dew for %pageno.
+ *          This will remove pixs and pixd (or their clones) stored in dew,
+ *          as well as the full resolution disparity arrays.  Together,
+ *          these hold approximately 16 bytes for each pixel in pixs.
+ * 
+ */ +l_ok +dewarpaApplyDisparity(L_DEWARPA *dewa, + l_int32 pageno, + PIX *pixs, + l_int32 grayin, + l_int32 x, + l_int32 y, + PIX **ppixd, + const char *debugfile) +{ +L_DEWARP *dew1, *dew; +PIX *pixv, *pixh; + + PROCNAME("dewarpaApplyDisparity"); + + /* Initialize the output with the input, so we'll have that + * in case we can't apply the page model. */ + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = pixClone(pixs); + if (grayin > 255) { + L_WARNING("invalid grayin = %d; clipping at 255\n", procName, grayin); + grayin = 255; + } + + /* Find the appropriate dew to use and fully populate its array(s) */ + if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) + return ERROR_INT("no model available", procName, 1); + + /* Correct for vertical disparity and save the result */ + if ((pixv = pixApplyVertDisparity(dew, pixs, grayin)) == NULL) { + dewarpMinimize(dew); + return ERROR_INT("pixv not made", procName, 1); + } + pixDestroy(ppixd); + *ppixd = pixv; + if (debugfile) { + pixDisplayWithTitle(pixv, 300, 0, "pixv", 1); + lept_rmdir("lept/dewapply"); /* remove previous images */ + lept_mkdir("lept/dewapply"); + pixWriteDebug("/tmp/lept/dewapply/001.png", pixs, IFF_PNG); + pixWriteDebug("/tmp/lept/dewapply/002.png", pixv, IFF_PNG); + } + + /* Optionally, correct for horizontal disparity */ + if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) { + if (dew->hvalid == FALSE) { + L_INFO("invalid horiz model for page %d\n", procName, pageno); + } else { + if ((pixh = pixApplyHorizDisparity(dew, pixv, grayin)) != NULL) { + pixDestroy(ppixd); + *ppixd = pixh; + if (debugfile) { + pixDisplayWithTitle(pixh, 600, 0, "pixh", 1); + pixWriteDebug("/tmp/lept/dewapply/003.png", pixh, IFF_PNG); + } + } else { + L_ERROR("horiz disparity failed on page %d\n", + procName, pageno); + } + } + } + + if (debugfile) { + dew1 = dewarpaGetDewarp(dewa, pageno); + dewarpDebug(dew1, "lept/dewapply", 0); + convertFilesToPdf("/tmp/lept/dewapply", NULL, 250, 1.0, 0, 0, + "Dewarp Apply Disparity", debugfile); + fprintf(stderr, "pdf file: %s\n", debugfile); + } + + /* Get rid of the large full res disparity arrays */ + dewarpMinimize(dew); + + return 0; +} + + +/*! + * \brief dewarpaApplyInit() + * + * \param[in] dewa + * \param[in] pageno of page model to be used; may be a ref model + * \param[in] pixs image to be modified; can be 1, 8 or 32 bpp + * \param[in] x, y origin for generation of disparity arrays + * \param[out] pdew dewarp to be used for this page + * \param[in] debugfile use NULL to skip writing this + * \return 0 if OK, 1 on error no models or ref models available + * + *
+ * Notes:
+ *      (1) This prepares pixs for being dewarped.  It returns 1 if
+ *          no dewarping model exists.
+ *      (2) The returned %dew contains the model to be used for this page
+ *          image.  The %dew is owned by dewa; do not destroy.
+ *      (3) If both the 'useboth' and 'check_columns' fields are true,
+ *          this checks for multiple text columns and if found, sets
+ *          the 'skip_horiz' field in the %dew for this page.
+ * 
+ */ +static l_int32 +dewarpaApplyInit(L_DEWARPA *dewa, + l_int32 pageno, + PIX *pixs, + l_int32 x, + l_int32 y, + L_DEWARP **pdew, + const char *debugfile) +{ +l_int32 ncols, debug; +L_DEWARP *dew1, *dew2; +PIX *pix1; + + PROCNAME("dewarpaApplyInit"); + + if (!pdew) + return ERROR_INT("&dew not defined", procName, 1); + *pdew = NULL; + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + if (pageno < 0 || pageno > dewa->maxpage) + return ERROR_INT("invalid pageno", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (x < 0) x = 0; + if (y < 0) y = 0; + debug = (debugfile) ? 1 : 0; + + /* Make sure all models are valid and all refmodels have + * been added to dewa */ + if (dewa->modelsready == FALSE) + dewarpaInsertRefModels(dewa, 0, debug); + + /* Check for the existence of a valid model; we don't expect + * all pages to have them. */ + if ((dew1 = dewarpaGetDewarp(dewa, pageno)) == NULL) { + L_INFO("no valid dew model for page %d\n", procName, pageno); + return 1; + } + + /* Get the page model that we will use and sanity-check that + * it is valid. The ultimate result will be put in dew1->pixd. */ + if (dew1->hasref) /* point to another page with a model */ + dew2 = dewarpaGetDewarp(dewa, dew1->refpage); + else + dew2 = dew1; + if (dew2->vvalid == FALSE) + return ERROR_INT("no model; shouldn't happen", procName, 1); + *pdew = dew2; + + /* If check_columns is TRUE and useboth is TRUE, check for + * multiple columns. If there is more than one column, we + * only apply vertical disparity. */ + if (dewa->useboth && dewa->check_columns) { + pix1 = pixConvertTo1(pixs, 140); + pixCountTextColumns(pix1, 0.3, 0.5, 0.1, &ncols, NULL); + pixDestroy(&pix1); + if (ncols > 1) { + L_INFO("found %d columns; not correcting horiz disparity\n", + procName, ncols); + dew2->skip_horiz = TRUE; + } else { + dew2->skip_horiz = FALSE; + } + } + + /* Generate the full res disparity arrays if they don't exist + * (e.g., if they've been minimized or read from file), or if + * they are too small for the current image. */ + dewarpPopulateFullRes(dew2, pixs, x, y); + return 0; +} + + +/*! + * \brief pixApplyVertDisparity() + * + * \param[in] dew + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] grayin gray value, from 0 to 255, for pixels brought in; + * use -1 to use pixels on the boundary of pixs + * \return pixd modified to remove vertical disparity, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies the vertical disparity array to the specified
+ *          image.  For src pixels above the image, we use the pixels
+ *          in the first raster line.
+ *      (2) Specify gray color for pixels brought in from the outside:
+ *          0 is black, 255 is white.  Use -1 to select pixels from the
+ *          boundary of the source image.
+ * 
+ */ +static PIX * +pixApplyVertDisparity(L_DEWARP *dew, + PIX *pixs, + l_int32 grayin) +{ +l_int32 i, j, w, h, d, fw, fh, wpld, wplf, isrc, val8; +l_uint32 *datad, *lined; +l_float32 *dataf, *linef; +void **lineptrs; +FPIX *fpix; +PIX *pixd; + + PROCNAME("pixApplyVertDisparity"); + + if (!dew) + return (PIX *)ERROR_PTR("dew not defined", procName, NULL); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL); + if ((fpix = dew->fullvdispar) == NULL) + return (PIX *)ERROR_PTR("fullvdispar not defined", procName, NULL); + fpixGetDimensions(fpix, &fw, &fh); + if (fw < w || fh < h) { + fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h); + return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL); + } + + /* Two choices for requested pixels outside pixs: (1) use pixels' + * from the boundary of pixs; use white or light gray pixels. */ + pixd = pixCreateTemplate(pixs); + if (grayin >= 0) + pixSetAllGray(pixd, grayin); + datad = pixGetData(pixd); + dataf = fpixGetData(fpix); + wpld = pixGetWpl(pixd); + wplf = fpixGetWpl(fpix); + if (d == 1) { + lineptrs = pixGetLinePtrs(pixs, NULL); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + isrc = (l_int32)(i - linef[j] + 0.5); + if (grayin < 0) /* use value at boundary if outside */ + isrc = L_MIN(L_MAX(isrc, 0), h - 1); + if (isrc >= 0 && isrc < h) { /* remains gray if outside */ + if (GET_DATA_BIT(lineptrs[isrc], j)) + SET_DATA_BIT(lined, j); + } + } + } + } else if (d == 8) { + lineptrs = pixGetLinePtrs(pixs, NULL); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + isrc = (l_int32)(i - linef[j] + 0.5); + if (grayin < 0) + isrc = L_MIN(L_MAX(isrc, 0), h - 1); + if (isrc >= 0 && isrc < h) { + val8 = GET_DATA_BYTE(lineptrs[isrc], j); + SET_DATA_BYTE(lined, j, val8); + } + } + } + } else { /* d == 32 */ + lineptrs = pixGetLinePtrs(pixs, NULL); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + isrc = (l_int32)(i - linef[j] + 0.5); + if (grayin < 0) + isrc = L_MIN(L_MAX(isrc, 0), h - 1); + if (isrc >= 0 && isrc < h) + lined[j] = GET_DATA_FOUR_BYTES(lineptrs[isrc], j); + } + } + } + + LEPT_FREE(lineptrs); + return pixd; +} + + +/*! + * \brief pixApplyHorizDisparity() + * + * \param[in] dew + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] grayin gray value, from 0 to 255, for pixels brought in; + * use -1 to use pixels on the boundary of pixs + * \return pixd modified to remove horizontal disparity if possible, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) This applies the horizontal disparity array to the specified
+ *          image.
+ *      (2) Specify gray color for pixels brought in from the outside:
+ *          0 is black, 255 is white.  Use -1 to select pixels from the
+ *          boundary of the source image.
+ *      (3) The input pixs has already been corrected for vertical disparity.
+ *          If the horizontal disparity array doesn't exist, this returns
+ *          a clone of %pixs.
+ * 
+ */ +static PIX * +pixApplyHorizDisparity(L_DEWARP *dew, + PIX *pixs, + l_int32 grayin) +{ +l_int32 i, j, w, h, d, fw, fh, wpls, wpld, wplf, jsrc, val8; +l_uint32 *datas, *lines, *datad, *lined; +l_float32 *dataf, *linef; +FPIX *fpix; +PIX *pixd; + + PROCNAME("pixApplyHorizDisparity"); + + if (!dew) + return (PIX *)ERROR_PTR("dew not defined", procName, pixs); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL); + if ((fpix = dew->fullhdispar) == NULL) + return (PIX *)ERROR_PTR("fullhdispar not defined", procName, NULL); + fpixGetDimensions(fpix, &fw, &fh); + if (fw < w || fh < h) { + fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h); + return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL); + } + + /* Two choices for requested pixels outside pixs: (1) use pixels' + * from the boundary of pixs; use white or light gray pixels. */ + pixd = pixCreateTemplate(pixs); + if (grayin >= 0) + pixSetAllGray(pixd, grayin); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + dataf = fpixGetData(fpix); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + wplf = fpixGetWpl(fpix); + if (d == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + jsrc = (l_int32)(j - linef[j] + 0.5); + if (grayin < 0) /* use value at boundary if outside */ + jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); + if (jsrc >= 0 && jsrc < w) { /* remains gray if outside */ + if (GET_DATA_BIT(lines, jsrc)) + SET_DATA_BIT(lined, j); + } + } + } + } else if (d == 8) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + jsrc = (l_int32)(j - linef[j] + 0.5); + if (grayin < 0) + jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); + if (jsrc >= 0 && jsrc < w) { + val8 = GET_DATA_BYTE(lines, jsrc); + SET_DATA_BYTE(lined, j, val8); + } + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + linef = dataf + i * wplf; + for (j = 0; j < w; j++) { + jsrc = (l_int32)(j - linef[j] + 0.5); + if (grayin < 0) + jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); + if (jsrc >= 0 && jsrc < w) + lined[j] = lines[jsrc]; + } + } + } + + return pixd; +} + + +/*----------------------------------------------------------------------* + * Apply warping disparity array to boxa * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaApplyDisparityBoxa() + * + * \param[in] dewa + * \param[in] pageno of page model to be used; may be a ref model + * \param[in] pixs initial pix reference; for alignment and debugging + * \param[in] boxas boxa to be mapped + * \param[in] mapdir 1 if mapping forward from original to dewarped; + * 0 if backward + * \param[in] x, y origin for generation of disparity arrays with + * respect to the source region + * \param[out] pboxad disparity corrected boxa + * \param[in] debugfile use NULL to skip writing this + * \return 0 if OK, 1 on error no models or ref models available + * + *
+ * Notes:
+ *      (1) This applies the disparity arrays in one of two mapping directions
+ *          to the specified boxa.  It can be used in the backward direction
+ *          to locate a box in the original coordinates that would have
+ *          been dewarped to to the specified image.
+ *      (2) If there is no model for %pageno, this will use the model for
+ *          'refpage' and put the result in the dew for %pageno.
+ *      (3) This works with both stripped and full resolution page models.
+ *          If the full res disparity array(s) are missing, they are remade.
+ *      (4) If an error occurs, a copy of the input boxa is returned.
+ * 
+ */ +l_ok +dewarpaApplyDisparityBoxa(L_DEWARPA *dewa, + l_int32 pageno, + PIX *pixs, + BOXA *boxas, + l_int32 mapdir, + l_int32 x, + l_int32 y, + BOXA **pboxad, + const char *debugfile) +{ +l_int32 debug_out; +L_DEWARP *dew1, *dew; +BOXA *boxav, *boxah; +PIX *pixv, *pixh; + + PROCNAME("dewarpaApplyDisparityBoxa"); + + /* Initialize the output with the input, so we'll have that + * in case we can't apply the page model. */ + if (!pboxad) + return ERROR_INT("&boxad not defined", procName, 1); + *pboxad = boxaCopy(boxas, L_CLONE); + + /* Find the appropriate dew to use and fully populate its array(s) */ + if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) + return ERROR_INT("no model available", procName, 1); + + /* Correct for vertical disparity and save the result */ + if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) { + dewarpMinimize(dew); + return ERROR_INT("boxa1 not made", procName, 1); + } + boxaDestroy(pboxad); + *pboxad = boxav; + pixv = NULL; + pixh = NULL; + if (debugfile && mapdir != 1) + L_INFO("Reverse map direction; no debug output\n", procName); + debug_out = debugfile && (mapdir == 1); + if (debug_out) { + PIX *pix1; + lept_rmdir("lept/dewboxa"); /* remove previous images */ + lept_mkdir("lept/dewboxa"); + pix1 = pixConvertTo32(pixs); + pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0); + pixWriteDebug("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG); + pixDestroy(&pix1); + pixv = pixApplyVertDisparity(dew, pixs, 255); + pix1 = pixConvertTo32(pixv); + pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0); + pixWriteDebug("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG); + pixDestroy(&pix1); + } + + /* Optionally, correct for horizontal disparity */ + if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) { + if (dew->hvalid == FALSE) { + L_INFO("invalid horiz model for page %d\n", procName, pageno); + } else { + boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir); + if (!boxah) { + L_ERROR("horiz disparity fails on page %d\n", procName, pageno); + } else { + boxaDestroy(pboxad); + *pboxad = boxah; + if (debug_out) { + PIX *pix1; + pixh = pixApplyHorizDisparity(dew, pixv, 255); + pix1 = pixConvertTo32(pixh); + pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255); + pixWriteDebug("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG); + pixDestroy(&pixh); + pixDestroy(&pix1); + } + } + } + } + + if (debug_out) { + pixDestroy(&pixv); + dew1 = dewarpaGetDewarp(dewa, pageno); + dewarpDebug(dew1, "lept/dewapply", 0); + convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0, + "Dewarp Apply Disparity Boxa", debugfile); + fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file: %s\n", + debugfile); + } + + /* Get rid of the large full res disparity arrays */ + dewarpMinimize(dew); + + return 0; +} + + +/*! + * \brief boxaApplyDisparity() + * + * \param[in] dew + * \param[in] boxa + * \param[in] direction L_HORIZ or L_VERT + * \param[in] mapdir 1 if mapping forward from original to dewarped; + * 0 if backward + * \return boxad modified by the disparity, or NULL on error + */ +static BOXA * +boxaApplyDisparity(L_DEWARP *dew, + BOXA *boxa, + l_int32 direction, + l_int32 mapdir) +{ +l_int32 x, y, w, h, ib, ip, nbox, wpl; +l_float32 xn, yn; +l_float32 *data, *line; +BOX *boxs, *boxd; +BOXA *boxad; +FPIX *fpix; +PTA *ptas, *ptad; + + PROCNAME("boxaApplyDisparity"); + + if (!dew) + return (BOXA *)ERROR_PTR("dew not defined", procName, NULL); + if (!boxa) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + if (direction == L_VERT) + fpix = dew->fullvdispar; + else if (direction == L_HORIZ) + fpix = dew->fullhdispar; + else + return (BOXA *)ERROR_PTR("invalid direction", procName, NULL); + if (!fpix) + return (BOXA *)ERROR_PTR("full disparity not defined", procName, NULL); + fpixGetDimensions(fpix, &w, &h); + + /* Clip the output to the positive quadrant because all box + * coordinates must be non-negative. */ + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + nbox = boxaGetCount(boxa); + boxad = boxaCreate(nbox); + for (ib = 0; ib < nbox; ib++) { + boxs = boxaGetBox(boxa, ib, L_COPY); + ptas = boxConvertToPta(boxs, 4); + ptad = ptaCreate(4); + for (ip = 0; ip < 4; ip++) { + ptaGetIPt(ptas, ip, &x, &y); + line = data + y * wpl; + if (direction == L_VERT) { + if (mapdir == 0) + yn = y - line[x]; + else + yn = y + line[x]; + yn = L_MAX(0, yn); + ptaAddPt(ptad, x, yn); + } else { /* direction == L_HORIZ */ + if (mapdir == 0) + xn = x - line[x]; + else + xn = x + line[x]; + xn = L_MAX(0, xn); + ptaAddPt(ptad, xn, y); + } + } + boxd = ptaConvertToBox(ptad); + boxaAddBox(boxad, boxd, L_INSERT); + boxDestroy(&boxs); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + } + + return boxad; +} + + +/*----------------------------------------------------------------------* + * Stripping out data and populating full res disparity * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpMinimize() + * + * \param[in] dew + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes all data that is not needed for serialization.
+ *          It keeps the subsampled disparity array(s), so the full
+ *          resolution arrays can be reconstructed.
+ * 
+ */ +l_ok +dewarpMinimize(L_DEWARP *dew) +{ +L_DEWARP *dewt; + + PROCNAME("dewarpMinimize"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + + /* If dew is a ref, minimize the actual dewarp */ + if (dew->hasref) + dewt = dewarpaGetDewarp(dew->dewa, dew->refpage); + else + dewt = dew; + if (!dewt) + return ERROR_INT("dewt not found", procName, 1); + + pixDestroy(&dewt->pixs); + fpixDestroy(&dewt->fullvdispar); + fpixDestroy(&dewt->fullhdispar); + numaDestroy(&dewt->namidys); + numaDestroy(&dewt->nacurves); + return 0; +} + + +/*! + * \brief dewarpPopulateFullRes() + * + * \param[in] dew + * \param[in] pix [optional], to give size of actual image + * \param[in] x, y origin for generation of disparity arrays + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the full resolution vertical and horizontal disparity
+ *          arrays do not exist, they are built from the subsampled ones.
+ *      (2) If pixs is not given, the size of the arrays is determined
+ *          by the original image from which the sampled version was
+ *          generated.  Any values of (x,y) are ignored.
+ *      (3) If pixs is given, the full resolution disparity arrays must
+ *          be large enough to accommodate it.
+ *          (a) If the arrays do not exist, the value of (x,y) determines
+ *              the origin of the full resolution arrays without extension,
+ *              relative to pixs.  Thus, (x,y) gives the amount of
+ *              slope extension in (left, top).  The (right, bottom)
+ *              extension is then determined by the size of pixs and
+ *              (x,y); the values should never be < 0.
+ *          (b) If the arrays exist and pixs is too large, the existing
+ *              full res arrays are destroyed and new ones are made,
+ *              again using (x,y) to determine the extension in the
+ *              four directions.
+ * 
+ */ +l_ok +dewarpPopulateFullRes(L_DEWARP *dew, + PIX *pix, + l_int32 x, + l_int32 y) +{ +l_int32 width, height, fw, fh, deltaw, deltah, redfactor; +FPIX *fpixt1, *fpixt2; + + PROCNAME("dewarpPopulateFullRes"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + if (!dew->sampvdispar) + return ERROR_INT("no sampled vert disparity", procName, 1); + if (x < 0) x = 0; + if (y < 0) y = 0; + + /* Establish the target size for the full res arrays */ + if (pix) + pixGetDimensions(pix, &width, &height, NULL); + else { + width = dew->w; + height = dew->h; + } + + /* Destroy the existing arrays if they are too small */ + if (dew->fullvdispar) { + fpixGetDimensions(dew->fullvdispar, &fw, &fh); + if (width > fw || height > fw) + fpixDestroy(&dew->fullvdispar); + } + if (dew->fullhdispar) { + fpixGetDimensions(dew->fullhdispar, &fw, &fh); + if (width > fw || height > fw) + fpixDestroy(&dew->fullhdispar); + } + + /* Find the required width and height expansion deltas */ + deltaw = width - dew->sampling * (dew->nx - 1) + 2; + deltah = height - dew->sampling * (dew->ny - 1) + 2; + redfactor = dew->redfactor; + deltaw = redfactor * L_MAX(0, deltaw); + deltah = redfactor * L_MAX(0, deltah); + + /* Generate the full res vertical array if it doesn't exist, + * extending it as required to make it big enough. Use x,y + * to determine the amounts on each side. */ + if (!dew->fullvdispar) { + fpixt1 = fpixCopy(NULL, dew->sampvdispar); + if (redfactor == 2) + fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor); + fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor); + fpixDestroy(&fpixt1); + if (deltah == 0 && deltaw == 0) { + dew->fullvdispar = fpixt2; + } + else { + dew->fullvdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x, + y, deltah - y); + fpixDestroy(&fpixt2); + } + } + + /* Similarly, generate the full res horizontal array if it + * doesn't exist. Do this even if useboth == 1, but + * not if required to skip running horizontal disparity. */ + if (!dew->fullhdispar && dew->samphdispar && !dew->skip_horiz) { + fpixt1 = fpixCopy(NULL, dew->samphdispar); + if (redfactor == 2) + fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor); + fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor); + fpixDestroy(&fpixt1); + if (deltah == 0 && deltaw == 0) { + dew->fullhdispar = fpixt2; + } + else { + dew->fullhdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x, + y, deltah - y); + fpixDestroy(&fpixt2); + } + } + + return 0; +} + + +#if 0 +/*----------------------------------------------------------------------* + * Static functions not presently in use * + *----------------------------------------------------------------------*/ +/*! + * \brief fpixSampledDisparity() + * + * \param[in] fpixs full resolution disparity model + * \param[in] sampling sampling factor + * \return fpixd sampled disparity model, or NULL on error + * + *
+ * Notes:
+ *      (1) This converts full to sampled disparity.
+ *      (2) The input array is sampled at the right and top edges, and
+ *          at every %sampling pixels horizontally and vertically.
+ *      (3) The sampled array may not extend to the right and bottom
+ *          pixels in fpixs.  This will occur if fpixs was generated
+ *          with slope extension because the image on that page was
+ *          larger than normal.  This is fine, because in use the
+ *          sampled array will be interpolated back to full resolution
+ *          and then extended as required.  So the operations of
+ *          sampling and interpolation will be idempotent.
+ *      (4) There must be at least 3 sampled points horizontally and
+ *          vertically.
+ * 
+ */ +static FPIX * +fpixSampledDisparity(FPIX *fpixs, + l_int32 sampling) +{ +l_int32 w, h, wd, hd, i, j, is, js; +l_float32 val; +FPIX *fpixd; + + PROCNAME("fpixSampledDisparity"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (sampling < 1) + return (FPIX *)ERROR_PTR("sampling < 1", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + wd = 1 + (w + sampling - 2) / sampling; + hd = 1 + (h + sampling - 2) / sampling; + if (wd < 3 || hd < 3) + return (FPIX *)ERROR_PTR("wd < 3 or hd < 3", procName, NULL); + fpixd = fpixCreate(wd, hd); + for (i = 0; i < hd; i++) { + is = sampling * i; + if (is >= h) continue; + for (j = 0; j < wd; j++) { + js = sampling * j; + if (js >= w) continue; + fpixGetPixel(fpixs, js, is, &val); + fpixSetPixel(fpixd, j, i, val); + } + } + + return fpixd; +} + +static const l_float32 DefaultSlopeFactor = 0.1; /* just a guess; fix it */ + +/*! + * \brief fpixExtraHorizDisparity() + * + * \param[in] fpixv vertical disparity model + * \param[in] factor conversion factor for vertical disparity slope; + * use 0 for default + * \param[out] pxwid extra width to be added to dewarped pix + * \return fpixh, or NULL on error + * + *
+ * Notes:
+ *      (1) This takes the difference in vertical disparity at top
+ *          and bottom of the image, and converts it to an assumed
+ *          horizontal disparity.  In use, we add this to the
+ *          horizontal disparity determined by the left and right
+ *          ends of textlines.
+ *      (2) Usage:
+ *            l_int32 xwid = [extra width to be added to fpix and image]
+ *            FPix *fpix = fpixExtraHorizDisparity(dew->fullvdispar, 0, &xwid);
+ *            fpixLinearCombination(dew->fullhdispar, dew->fullhdispar,
+ *                                  fpix, 1.0, 1.0);
+ * 
+ */ +static FPIX * +fpixExtraHorizDisparity(FPIX *fpixv, + l_float32 factor, + l_int32 *pxwid) +{ +l_int32 w, h, i, j, fw, wpl, maxloc; +l_float32 val1, val2, vdisp, vdisp0, maxval; +l_float32 *data, *line, *fadiff; +NUMA *nadiff; +FPIX *fpixh; + + PROCNAME("fpixExtraHorizDisparity"); + + if (!fpixv) + return (FPIX *)ERROR_PTR("fpixv not defined", procName, NULL); + if (!pxwid) + return (FPIX *)ERROR_PTR("&xwid not defined", procName, NULL); + if (factor == 0.0) + factor = DefaultSlopeFactor; + + /* Estimate horizontal disparity from the vertical disparity + * difference between the top and bottom, normalized to the + * image height. Add the maximum value to the width of the + * output image, so that all src pixels can be mapped + * into the dest. */ + fpixGetDimensions(fpixv, &w, &h); + nadiff = numaCreate(w); + for (j = 0; j < w; j++) { + fpixGetPixel(fpixv, j, 0, &val1); + fpixGetPixel(fpixv, j, h - 1, &val2); + vdisp = factor * (val2 - val1) / (l_float32)h; + if (j == 0) vdisp0 = vdisp; + vdisp = vdisp0 - vdisp; + numaAddNumber(nadiff, vdisp); + } + numaGetMax(nadiff, &maxval, &maxloc); + *pxwid = (l_int32)(maxval + 0.5); + + fw = w + *pxwid; + fpixh = fpixCreate(fw, h); + data = fpixGetData(fpixh); + wpl = fpixGetWpl(fpixh); + fadiff = numaGetFArray(nadiff, L_NOCOPY); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < fw; j++) { + if (j < maxloc) /* this may not work for even pages */ + line[j] = fadiff[j]; + else /* keep it at the max value the rest of the way across */ + line[j] = maxval; + } + } + + numaDestroy(&nadiff); + return fpixh; +} +#endif diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dewarp4.c b/hgdriver/3rdparty/hgOCR/leptonica/dewarp4.c new file mode 100644 index 0000000..1bdc859 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dewarp4.c @@ -0,0 +1,1172 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dewarp4.c + *
+ *
+ *    Single page dewarper
+ *
+ *    Reference model (book-level, dewarpa) operations and debugging output
+ *
+ *      Top-level single page dewarper
+ *          l_int32            dewarpSinglePage()
+ *          l_int32            dewarpSinglePageInit()
+ *          l_int32            dewarpSinglePageRun()
+ *
+ *      Operations on dewarpa
+ *          l_int32            dewarpaListPages()
+ *          l_int32            dewarpaSetValidModels()
+ *          l_int32            dewarpaInsertRefModels()
+ *          l_int32            dewarpaStripRefModels()
+ *          l_int32            dewarpaRestoreModels()
+ *
+ *      Dewarp debugging output
+ *          l_int32            dewarpaInfo()
+ *          l_int32            dewarpaModelStats()
+ *          static l_int32     dewarpaTestForValidModel()
+ *          l_int32            dewarpaShowArrays()
+ *          l_int32            dewarpDebug()
+ *          l_int32            dewarpShowResults()
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_int32 dewarpaTestForValidModel(L_DEWARPA *dewa, L_DEWARP *dew, + l_int32 notests); + +#ifndef NO_CONSOLE_IO +#define DEBUG_INVALID_MODELS 0 /* set this to 1 for debugging */ +#endif /* !NO_CONSOLE_IO */ + + /* Special parameter value */ +static const l_int32 GrayInValue = 200; + + +/*----------------------------------------------------------------------* + * Top-level single page dewarper * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpSinglePage() + * + * \param[in] pixs with text, any depth + * \param[in] thresh for global thresh to 1 bpp; ignore otherwise + * \param[in] adaptive 1 for adaptive thresh; 0 for global threshold + * \param[in] useboth 1 for both horiz and vert; 0 for vertical only + * \param[in] check_columns 1 to skip horizontal if multiple columns; + * 0 otherwise; default is to skip + * \param[out] ppixd dewarped result + * \param[out] pdewa [optional] dewa with single page; NULL to skip + * \param[in] debug 1 for debugging output, 0 otherwise + * \return 0 if OK, 1 on error list of page numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) Dewarps pixs and returns the result in &pixd.
+ *      (2) This uses default values for all model parameters.
+ *      (3) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
+ *      (4) If it can't build a model, returns a copy of pixs in &pixd.
+ * 
+ */ +l_ok +dewarpSinglePage(PIX *pixs, + l_int32 thresh, + l_int32 adaptive, + l_int32 useboth, + l_int32 check_columns, + PIX **ppixd, + L_DEWARPA **pdewa, + l_int32 debug) +{ +L_DEWARPA *dewa; +PIX *pixb; + + PROCNAME("dewarpSinglePage"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (pdewa) *pdewa = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + dewarpSinglePageInit(pixs, thresh, adaptive, useboth, + check_columns, &pixb, &dewa); + if (!pixb) { + dewarpaDestroy(&dewa); + return ERROR_INT("pixb not made", procName, 1); + } + + dewarpSinglePageRun(pixs, pixb, dewa, ppixd, debug); + + if (pdewa) + *pdewa = dewa; + else + dewarpaDestroy(&dewa); + pixDestroy(&pixb); + return 0; +} + + +/*! + * \brief dewarpSinglePageInit() + * + * \param[in] pixs with text, any depth + * \param[in] thresh for global thresh to 1 bpp; ignore otherwise + * \param[in] adaptive 1 for adaptive thresh; 0 for global threshold + * \param[in] useboth 1 for both horiz and vert; 0 for vertical only + * \param[in] check_columns 1 to skip horizontal if multiple columns; + * 0 otherwise; default is to skip + * \param[out] ppixb 1 bpp debug image + * \param[out] pdewa initialized dewa + * \return 0 if OK, 1 on error list of page numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) This binarizes the input pixs if necessary, returning the
+ *          binarized image.  It also initializes the dewa to default values
+ *          for the model parameters.
+ *      (2) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
+ *      (3) To change the model parameters, call dewarpaSetCurvatures()
+ *          before running dewarpSinglePageRun().  For example:
+ *             dewarpSinglePageInit(pixs, 0, 1, 1, 1, &pixb, &dewa);
+ *             dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150);
+ *             dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0);
+ *             dewarpaDestroy(&dewa);
+ *             pixDestroy(&pixb);
+ * 
+ */ +l_ok +dewarpSinglePageInit(PIX *pixs, + l_int32 thresh, + l_int32 adaptive, + l_int32 useboth, + l_int32 check_columns, + PIX **ppixb, + L_DEWARPA **pdewa) +{ +PIX *pix1; + + PROCNAME("dewarpSinglePageInit"); + + if (ppixb) *ppixb = NULL; + if (pdewa) *pdewa = NULL; + if (!ppixb || !pdewa) + return ERROR_INT("&pixb and &dewa not both defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + *pdewa = dewarpaCreate(1, 0, 1, 0, -1); + dewarpaUseBothArrays(*pdewa, useboth); + dewarpaSetCheckColumns(*pdewa, check_columns); + + /* Generate a binary image, if necessary */ + if (pixGetDepth(pixs) > 1) { + pix1 = pixConvertTo8(pixs, 0); + if (adaptive) + *ppixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0); + else + *ppixb = pixThresholdToBinary(pix1, thresh); + pixDestroy(&pix1); + } else { + *ppixb = pixClone(pixs); + } + return 0; +} + + +/*! + * \brief dewarpSinglePageRun() + * + * \param[in] pixs any depth + * \param[in] pixb 1 bpp + * \param[in] dewa initialized + * \param[out] ppixd dewarped result + * \param[in] debug 1 for debugging output, 0 otherwise + * \return 0 if OK, 1 on error list of page numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) Dewarps pixs and returns the result in &pixd.
+ *      (2) The 1 bpp version %pixb and %dewa are conveniently generated by
+ *          dewarpSinglePageInit().
+ *      (3) Non-default model parameters must be set before calling this.
+ *      (4) If a model cannot be built, this returns a copy of pixs in &pixd.
+ * 
+ */ +l_ok +dewarpSinglePageRun(PIX *pixs, + PIX *pixb, + L_DEWARPA *dewa, + PIX **ppixd, + l_int32 debug) +{ +const char *debugfile; +l_int32 vsuccess, ret; +L_DEWARP *dew; + + PROCNAME("dewarpSinglePageRun"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixb) + return ERROR_INT("pixb not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + if (debug) + lept_mkdir("lept/dewarp"); + + /* Generate the page model */ + dew = dewarpCreate(pixb, 0); + dewarpaInsertDewarp(dewa, dew); + debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_model.pdf" : NULL; + dewarpBuildPageModel(dew, debugfile); + dewarpaModelStatus(dewa, 0, &vsuccess, NULL); + if (vsuccess == 0) { + L_ERROR("failure to build model for vertical disparity\n", procName); + *ppixd = pixCopy(NULL, pixs); + return 0; + } + + /* Apply the page model */ + debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_apply.pdf" : NULL; + ret = dewarpaApplyDisparity(dewa, 0, pixs, 255, 0, 0, ppixd, debugfile); + if (ret) + L_ERROR("invalid model; failure to apply disparity\n", procName); + return 0; +} + + +/*----------------------------------------------------------------------* + * Operations on dewarpa * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaListPages() + * + * \param[in] dewa populated with dewarp structs for pages + * \return 0 if OK, 1 on error list of page numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates two numas, stored in the dewarpa, that give:
+ *          (a) the page number for each dew that has a page model.
+ *          (b) the page number for each dew that has either a page
+ *              model or a reference model.
+ *          It can be called at any time.
+ *      (2) It is called by the dewarpa serializer before writing.
+ * 
+ */ +l_ok +dewarpaListPages(L_DEWARPA *dewa) +{ +l_int32 i; +L_DEWARP *dew; +NUMA *namodels, *napages; + + PROCNAME("dewarpaListPages"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + numaDestroy(&dewa->namodels); + numaDestroy(&dewa->napages); + namodels = numaCreate(dewa->maxpage + 1); + napages = numaCreate(dewa->maxpage + 1); + dewa->namodels = namodels; + dewa->napages = napages; + for (i = 0; i <= dewa->maxpage; i++) { + if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) { + if (dew->hasref == 0) + numaAddNumber(namodels, dew->pageno); + numaAddNumber(napages, dew->pageno); + } + } + return 0; +} + + +/*! + * \brief dewarpaSetValidModels() + * + * \param[in] dewa + * \param[in] notests + * \param[in] debug 1 to output information on invalid page models + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) A valid model must meet the rendering requirements, which
+ *          include whether or not a vertical disparity model exists
+ *          and conditions on curvatures for vertical and horizontal
+ *          disparity models.
+ *      (2) If %notests == 1, this ignores the curvature constraints
+ *          and assumes that all successfully built models are valid.
+ *      (3) This function does not need to be called by the application.
+ *          It is called by dewarpaInsertRefModels(), which
+ *          will destroy all invalid dewarps.  Consequently, to inspect
+ *          an invalid dewarp model, it must be done before calling
+ *          dewarpaInsertRefModels().
+ * 
+ */ +l_ok +dewarpaSetValidModels(L_DEWARPA *dewa, + l_int32 notests, + l_int32 debug) +{ +l_int32 i, n, maxcurv, diffcurv, diffedge; +L_DEWARP *dew; + + PROCNAME("dewarpaSetValidModels"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + n = dewa->maxpage + 1; + for (i = 0; i < n; i++) { + if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) + continue; + + if (debug) { + if (dew->hasref == 1) { + L_INFO("page %d: has only a ref model\n", procName, i); + } else if (dew->vsuccess == 0) { + L_INFO("page %d: no model successfully built\n", + procName, i); + } else if (!notests) { + maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv)); + diffcurv = dew->maxcurv - dew->mincurv; + if (dewa->useboth && !dew->hsuccess) + L_INFO("page %d: useboth, but no horiz disparity\n", + procName, i); + if (maxcurv > dewa->max_linecurv) + L_INFO("page %d: max curvature %d > max_linecurv\n", + procName, i, diffcurv); + if (diffcurv < dewa->min_diff_linecurv) + L_INFO("page %d: diff curv %d < min_diff_linecurv\n", + procName, i, diffcurv); + if (diffcurv > dewa->max_diff_linecurv) + L_INFO("page %d: abs diff curv %d > max_diff_linecurv\n", + procName, i, diffcurv); + if (dew->hsuccess) { + if (L_ABS(dew->leftslope) > dewa->max_edgeslope) + L_INFO("page %d: abs left slope %d > max_edgeslope\n", + procName, i, dew->leftslope); + if (L_ABS(dew->rightslope) > dewa->max_edgeslope) + L_INFO("page %d: abs right slope %d > max_edgeslope\n", + procName, i, dew->rightslope); + diffedge = L_ABS(dew->leftcurv - dew->rightcurv); + if (L_ABS(dew->leftcurv) > dewa->max_edgecurv) + L_INFO("page %d: left curvature %d > max_edgecurv\n", + procName, i, dew->leftcurv); + if (L_ABS(dew->rightcurv) > dewa->max_edgecurv) + L_INFO("page %d: right curvature %d > max_edgecurv\n", + procName, i, dew->rightcurv); + if (diffedge > dewa->max_diff_edgecurv) + L_INFO("page %d: abs diff left-right curv %d > " + "max_diff_edgecurv\n", procName, i, diffedge); + } + } + } + + dewarpaTestForValidModel(dewa, dew, notests); + } + + return 0; +} + + +/*! + * \brief dewarpaInsertRefModels() + * + * \param[in] dewa + * \param[in] notests if 1, ignore curvature constraints on model + * \param[in] debug 1 to output information on invalid page models + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys all dewarp models that are invalid, and then
+ *          inserts reference models where possible.
+ *      (2) If %notests == 1, this ignores the curvature constraints
+ *          and assumes that all successfully built models are valid.
+ *      (3) If useboth == 0, it uses the closest valid model within the
+ *          distance and parity constraints.  If useboth == 1, it tries
+ *          to use the closest allowed hvalid model; if it doesn't find
+ *          an hvalid model, it uses the closest valid model.
+ *      (4) For all pages without a model, this clears out any existing
+ *          invalid and reference dewarps, finds the nearest valid model
+ *          with the same parity, and inserts an empty dewarp with the
+ *          reference page.
+ *      (5) Then if it is requested to use both vertical and horizontal
+ *          disparity arrays (useboth == 1), it tries to replace any
+ *          hvalid == 0 model or reference with an hvalid == 1 reference.
+ *      (6) The distance constraint is that any reference model must
+ *          be within maxdist.  Note that with the parity constraint,
+ *          no reference models will be used if maxdist < 2.
+ *      (7) This function must be called, even if reference models will
+ *          not be used.  It should be called after building models on all
+ *          available pages, and after setting the rendering parameters.
+ *      (8) If the dewa has been serialized, this function is called by
+ *          dewarpaRead() when it is read back.  It is also called
+ *          any time the rendering parameters are changed.
+ *      (9) Note: if this has been called with useboth == 1, and useboth
+ *          is reset to 0, you should first call dewarpaRestoreModels()
+ *          to bring real models from the cache back to the primary array.
+ * 
+ */ +l_ok +dewarpaInsertRefModels(L_DEWARPA *dewa, + l_int32 notests, + l_int32 debug) +{ +l_int32 i, j, n, val, min, distdown, distup; +L_DEWARP *dew; +NUMA *na, *nah; + + PROCNAME("dewarpaInsertRefModels"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + if (dewa->maxdist < 2) + L_INFO("maxdist < 2; no ref models can be used\n", procName); + + /* Make an indicator numa for pages with valid models. */ + dewarpaSetValidModels(dewa, notests, debug); + n = dewa->maxpage + 1; + na = numaMakeConstant(0, n); + for (i = 0; i < n; i++) { + dew = dewarpaGetDewarp(dewa, i); + if (dew && dew->vvalid) + numaReplaceNumber(na, i, 1); + } + + /* Remove all existing ref models and restore models from cache */ + dewarpaRestoreModels(dewa); + + /* Move invalid models to the cache, and insert reference dewarps + * for pages that need to borrow a model. + * First, try to find a valid model for each page. */ + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &val); + if (val == 1) continue; /* already has a valid model */ + if ((dew = dewa->dewarp[i]) != NULL) { /* exists but is not valid; */ + dewa->dewarpcache[i] = dew; /* move it to the cache */ + dewa->dewarp[i] = NULL; + } + if (dewa->maxdist < 2) continue; /* can't use a ref model */ + /* Look back for nearest model */ + distdown = distup = dewa->maxdist + 1; + for (j = i - 2; j >= 0 && distdown > dewa->maxdist; j -= 2) { + numaGetIValue(na, j, &val); + if (val == 1) distdown = i - j; + } + /* Look ahead for nearest model */ + for (j = i + 2; j < n && distup > dewa->maxdist; j += 2) { + numaGetIValue(na, j, &val); + if (val == 1) distup = j - i; + } + min = L_MIN(distdown, distup); + if (min > dewa->maxdist) continue; /* no valid model in range */ + if (distdown <= distup) + dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown)); + else + dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup)); + } + numaDestroy(&na); + + /* If a valid model will do, we're finished. */ + if (dewa->useboth == 0) { + dewa->modelsready = 1; /* validated */ + return 0; + } + + /* The request is useboth == 1. Now try to find an hvalid model */ + nah = numaMakeConstant(0, n); + for (i = 0; i < n; i++) { + dew = dewarpaGetDewarp(dewa, i); + if (dew && dew->hvalid) + numaReplaceNumber(nah, i, 1); + } + for (i = 0; i < n; i++) { + numaGetIValue(nah, i, &val); + if (val == 1) continue; /* already has a hvalid model */ + if (dewa->maxdist < 2) continue; /* can't use a ref model */ + distdown = distup = 100000; + for (j = i - 2; j >= 0; j -= 2) { /* look back for nearest model */ + numaGetIValue(nah, j, &val); + if (val == 1) { + distdown = i - j; + break; + } + } + for (j = i + 2; j < n; j += 2) { /* look ahead for nearest model */ + numaGetIValue(nah, j, &val); + if (val == 1) { + distup = j - i; + break; + } + } + min = L_MIN(distdown, distup); + if (min > dewa->maxdist) continue; /* no hvalid model within range */ + + /* We can replace the existing valid model with an hvalid model. + * If it's not a reference, save it in the cache. */ + if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) { + L_ERROR("dew is null for page %d!\n", procName, i); + } else { + if (dew->hasref == 0) { /* not a ref model */ + dewa->dewarpcache[i] = dew; /* move it to the cache */ + dewa->dewarp[i] = NULL; /* must null the ptr */ + } + } + if (distdown <= distup) /* insert the hvalid ref model */ + dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown)); + else + dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup)); + } + numaDestroy(&nah); + + dewa->modelsready = 1; /* validated */ + return 0; +} + + +/*! + * \brief dewarpaStripRefModels() + * + * \param[in] dewa populated with dewarp structs for pages + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This examines each dew in a dewarpa, and removes
+ *          all that don't have their own page model (i.e., all
+ *          that have "references" to nearby pages with valid models).
+ *          These references were generated by dewarpaInsertRefModels(dewa).
+ * 
+ */ +l_ok +dewarpaStripRefModels(L_DEWARPA *dewa) +{ +l_int32 i; +L_DEWARP *dew; + + PROCNAME("dewarpaStripRefModels"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + for (i = 0; i <= dewa->maxpage; i++) { + if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) { + if (dew->hasref) + dewarpDestroy(&dewa->dewarp[i]); + } + } + dewa->modelsready = 0; + + /* Regenerate the page lists */ + dewarpaListPages(dewa); + return 0; +} + + +/*! + * \brief dewarpaRestoreModels() + * + * \param[in] dewa populated with dewarp structs for pages + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This puts all real models (and only real models) in the
+ *          primary dewarpa array.  First remove all dewarps that are
+ *          only references to other page models.  Then move all models
+ *          that had been cached back into the primary dewarp array.
+ *      (2) After this is done, we still need to recompute and insert
+ *          the reference models before dewa->modelsready is true.
+ * 
+ */ +l_ok +dewarpaRestoreModels(L_DEWARPA *dewa) +{ +l_int32 i; +L_DEWARP *dew; + + PROCNAME("dewarpaRestoreModels"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + /* Strip out ref models. Then only real models will be in the + * primary dewarp array. */ + dewarpaStripRefModels(dewa); + + /* The cache holds only real models, which are not necessarily valid. */ + for (i = 0; i <= dewa->maxpage; i++) { + if ((dew = dewa->dewarpcache[i]) != NULL) { + if (dewa->dewarp[i]) { + L_ERROR("dew in both cache and main array!: page %d\n", + procName, i); + } else { + dewa->dewarp[i] = dew; + dewa->dewarpcache[i] = NULL; + } + } + } + dewa->modelsready = 0; /* new ref models not yet inserted */ + + /* Regenerate the page lists */ + dewarpaListPages(dewa); + return 0; +} + + +/*----------------------------------------------------------------------* + * Dewarp debugging output * + *----------------------------------------------------------------------*/ +/*! + * \brief dewarpaInfo() + * + * \param[in] fp + * \param[in] dewa + * \return 0 if OK, 1 on error + */ +l_ok +dewarpaInfo(FILE *fp, + L_DEWARPA *dewa) +{ +l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref; +L_DEWARP *dew; + + PROCNAME("dewarpaInfo"); + + if (!fp) + return ERROR_INT("dewa not defined", procName, 1); + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + fprintf(fp, "\nDewarpaInfo: %p\n", dewa); + fprintf(fp, "nalloc = %d, maxpage = %d\n", dewa->nalloc, dewa->maxpage); + fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d\n", + dewa->sampling, dewa->redfactor, dewa->minlines); + fprintf(fp, "maxdist = %d, useboth = %d\n", + dewa->maxdist, dewa->useboth); + + dewarpaModelStats(dewa, &nnone, &nvsuccess, &nvvalid, + &nhsuccess, &nhvalid, &nref); + n = numaGetCount(dewa->napages); + fprintf(stderr, "Total number of pages with a dew = %d\n", n); + fprintf(stderr, "Number of pages without any models = %d\n", nnone); + fprintf(stderr, "Number of pages with a vert model = %d\n", nvsuccess); + fprintf(stderr, "Number of pages with a valid vert model = %d\n", nvvalid); + fprintf(stderr, "Number of pages with both models = %d\n", nhsuccess); + fprintf(stderr, "Number of pages with both models valid = %d\n", nhvalid); + fprintf(stderr, "Number of pages with a ref model = %d\n", nref); + + for (i = 0; i < n; i++) { + numaGetIValue(dewa->napages, i, &pageno); + if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL) + continue; + fprintf(stderr, "Page: %d\n", dew->pageno); + fprintf(stderr, " hasref = %d, refpage = %d\n", + dew->hasref, dew->refpage); + fprintf(stderr, " nlines = %d\n", dew->nlines); + fprintf(stderr, " w = %d, h = %d, nx = %d, ny = %d\n", + dew->w, dew->h, dew->nx, dew->ny); + if (dew->sampvdispar) + fprintf(stderr, " Vertical disparity builds:\n" + " (min,max,abs-diff) line curvature = (%d,%d,%d)\n", + dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv); + if (dew->samphdispar) + fprintf(stderr, " Horizontal disparity builds:\n" + " left edge slope = %d, right edge slope = %d\n" + " (left,right,abs-diff) edge curvature = (%d,%d,%d)\n", + dew->leftslope, dew->rightslope, dew->leftcurv, + dew->rightcurv, L_ABS(dew->leftcurv - dew->rightcurv)); + } + return 0; +} + + +/*! + * \brief dewarpaModelStats() + * + * \param[in] dewa + * \param[out] pnnone [optional] number without any model + * \param[out] pnvsuccess [optional] number with a vert model + * \param[out] pnvvalid [optional] number with a valid vert model + * \param[out] pnhsuccess [optional] number with both models + * \param[out] pnhvalid [optional] number with both models valid + * \param[out] pnref [optional] number with a reference model + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) A page without a model has no dew.  It most likely failed to
+ *          generate a vertical model, and has not been assigned a ref
+ *          model from a neighboring page with a valid vertical model.
+ *      (2) A page has vsuccess == 1 if there is at least a model of the
+ *          vertical disparity.  The model may be invalid, in which case
+ *          dewarpaInsertRefModels() will stash it in the cache and
+ *          attempt to replace it by a valid ref model.
+ *      (3) A vvvalid model is a vertical disparity model whose parameters
+ *          satisfy the constraints given in dewarpaSetValidModels().
+ *      (4) A page has hsuccess == 1 if both the vertical and horizontal
+ *          disparity arrays have been constructed.
+ *      (5) An  hvalid model has vertical and horizontal disparity
+ *          models whose parameters satisfy the constraints given
+ *          in dewarpaSetValidModels().
+ *      (6) A page has a ref model if it failed to generate a valid
+ *          model but was assigned a vvalid or hvalid model on another
+ *          page (within maxdist) by dewarpaInsertRefModel().
+ *      (7) This calls dewarpaTestForValidModel(); it ignores the vvalid
+ *          and hvalid fields.
+ * 
+ */ +l_ok +dewarpaModelStats(L_DEWARPA *dewa, + l_int32 *pnnone, + l_int32 *pnvsuccess, + l_int32 *pnvvalid, + l_int32 *pnhsuccess, + l_int32 *pnhvalid, + l_int32 *pnref) +{ +l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref; +L_DEWARP *dew; + + PROCNAME("dewarpaModelStats"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + + dewarpaListPages(dewa); + n = numaGetCount(dewa->napages); + nnone = nref = nvsuccess = nvvalid = nhsuccess = nhvalid = 0; + for (i = 0; i < n; i++) { + numaGetIValue(dewa->napages, i, &pageno); + dew = dewarpaGetDewarp(dewa, pageno); + if (!dew) { + nnone++; + continue; + } + if (dew->hasref == 1) + nref++; + if (dew->vsuccess == 1) + nvsuccess++; + if (dew->hsuccess == 1) + nhsuccess++; + dewarpaTestForValidModel(dewa, dew, 0); + if (dew->vvalid == 1) + nvvalid++; + if (dew->hvalid == 1) + nhvalid++; + } + + if (pnnone) *pnnone = nnone; + if (pnref) *pnref = nref; + if (pnvsuccess) *pnvsuccess = nvsuccess; + if (pnvvalid) *pnvvalid = nvvalid; + if (pnhsuccess) *pnhsuccess = nhsuccess; + if (pnhvalid) *pnhvalid = nhvalid; + return 0; +} + + +/*! + * \brief dewarpaTestForValidModel() + * + * \param[in] dewa + * \param[in] dew + * \param[in] notests + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Computes validity of vertical (vvalid) model and both
+ *          vertical and horizontal (hvalid) models.
+ *      (2) If %notests == 1, this ignores the curvature constraints
+ *          and assumes that all successfully built models are valid.
+ *      (3) This is just about the models, not the rendering process,
+ *          so the value of useboth is not considered here.
+ * 
+ */ +static l_int32 +dewarpaTestForValidModel(L_DEWARPA *dewa, + L_DEWARP *dew, + l_int32 notests) +{ +l_int32 maxcurv, diffcurv, diffedge; + + PROCNAME("dewarpaTestForValidModel"); + + if (!dewa || !dew) + return ERROR_INT("dewa and dew not both defined", procName, 1); + + if (notests) { + dew->vvalid = dew->vsuccess; + dew->hvalid = dew->hsuccess; + return 0; + } + + /* No actual model was built */ + if (dew->vsuccess == 0) return 0; + + /* Was previously found not to have a valid model */ + if (dew->hasref == 1) return 0; + + /* vsuccess == 1; a vertical (line) model exists. + * First test that the vertical curvatures are within allowed + * bounds. Note that all curvatures are signed.*/ + maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv)); + diffcurv = dew->maxcurv - dew->mincurv; + if (maxcurv <= dewa->max_linecurv && + diffcurv >= dewa->min_diff_linecurv && + diffcurv <= dewa->max_diff_linecurv) { + dew->vvalid = 1; + } else { + L_INFO("invalid vert model for page %d:\n", procName, dew->pageno); +#if DEBUG_INVALID_MODELS + fprintf(stderr, " max line curv = %d, max allowed = %d\n", + maxcurv, dewa->max_linecurv); + fprintf(stderr, " diff line curv = %d, max allowed = %d\n", + diffcurv, dewa->max_diff_linecurv); +#endif /* DEBUG_INVALID_MODELS */ + } + + /* If a horizontal (edge) model exists, test for validity. */ + if (dew->hsuccess) { + diffedge = L_ABS(dew->leftcurv - dew->rightcurv); + if (L_ABS(dew->leftslope) <= dewa->max_edgeslope && + L_ABS(dew->rightslope) <= dewa->max_edgeslope && + L_ABS(dew->leftcurv) <= dewa->max_edgecurv && + L_ABS(dew->rightcurv) <= dewa->max_edgecurv && + diffedge <= dewa->max_diff_edgecurv) { + dew->hvalid = 1; + } else { + L_INFO("invalid horiz model for page %d:\n", procName, dew->pageno); +#if DEBUG_INVALID_MODELS + fprintf(stderr, " left edge slope = %d, max allowed = %d\n", + dew->leftslope, dewa->max_edgeslope); + fprintf(stderr, " right edge slope = %d, max allowed = %d\n", + dew->rightslope, dewa->max_edgeslope); + fprintf(stderr, " left edge curv = %d, max allowed = %d\n", + dew->leftcurv, dewa->max_edgecurv); + fprintf(stderr, " right edge curv = %d, max allowed = %d\n", + dew->rightcurv, dewa->max_edgecurv); + fprintf(stderr, " diff edge curv = %d, max allowed = %d\n", + diffedge, dewa->max_diff_edgecurv); +#endif /* DEBUG_INVALID_MODELS */ + } + } + + return 0; +} + + +/*! + * \brief dewarpaShowArrays() + * + * \param[in] dewa + * \param[in] scalefact on contour images; typ. 0.5 + * \param[in] first first page model to render + * \param[in] last last page model to render; use 0 to go to end + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates a pdf of contour plots of the disparity arrays.
+ *      (2) This only shows actual models; not ref models
+ * 
+ */ +l_ok +dewarpaShowArrays(L_DEWARPA *dewa, + l_float32 scalefact, + l_int32 first, + l_int32 last) +{ +char buf[256]; +l_int32 i, svd, shd; +L_BMF *bmf; +L_DEWARP *dew; +PIX *pixv, *pixvs, *pixh, *pixhs, *pixt, *pixd; +PIXA *pixa; + + PROCNAME("dewarpaShowArrays"); + + if (!dewa) + return ERROR_INT("dew not defined", procName, 1); + if (first < 0 || first > dewa->maxpage) + return ERROR_INT("first out of bounds", procName, 1); + if (last <= 0 || last > dewa->maxpage) last = dewa->maxpage; + if (last < first) + return ERROR_INT("last < first", procName, 1); + + lept_rmdir("lept/dewarp1"); /* temp directory for contour plots */ + lept_mkdir("lept/dewarp1"); + if ((bmf = bmfCreate(NULL, 8)) == NULL) + L_ERROR("bmf not made; page info not displayed", procName); + + fprintf(stderr, "Generating contour plots\n"); + for (i = first; i <= last; i++) { + if (i && ((i % 10) == 0)) + fprintf(stderr, " .. %d", i); + dew = dewarpaGetDewarp(dewa, i); + if (!dew) continue; + if (dew->hasref == 1) continue; + svd = shd = 0; + if (dew->sampvdispar) svd = 1; + if (dew->samphdispar) shd = 1; + if (!svd) { + L_ERROR("sampvdispar not made for page %d!\n", procName, i); + continue; + } + + /* Generate contour plots at reduced resolution */ + dewarpPopulateFullRes(dew, NULL, 0, 0); + pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); + pixvs = pixScaleBySampling(pixv, scalefact, scalefact); + pixDestroy(&pixv); + if (shd) { + pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); + pixhs = pixScaleBySampling(pixh, scalefact, scalefact); + pixDestroy(&pixh); + } + dewarpMinimize(dew); + + /* Save side-by-side */ + pixa = pixaCreate(2); + pixaAddPix(pixa, pixvs, L_INSERT); + if (shd) + pixaAddPix(pixa, pixhs, L_INSERT); + pixt = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2); + snprintf(buf, sizeof(buf), "Page %d", i); + pixd = pixAddSingleTextblock(pixt, bmf, buf, 0x0000ff00, + L_ADD_BELOW, NULL); + snprintf(buf, sizeof(buf), "/tmp/lept/dewarp1/arrays_%04d.png", i); + pixWriteDebug(buf, pixd, IFF_PNG); + pixaDestroy(&pixa); + pixDestroy(&pixt); + pixDestroy(&pixd); + } + bmfDestroy(&bmf); + fprintf(stderr, "\n"); + + fprintf(stderr, "Generating pdf of contour plots\n"); + convertFilesToPdf("/tmp/lept/dewarp1", "arrays_", 90, 1.0, L_FLATE_ENCODE, + 0, "Disparity arrays", "/tmp/lept/disparity_arrays.pdf"); + fprintf(stderr, "Output written to: /tmp/lept/disparity_arrays.pdf\n"); + return 0; +} + + +/*! + * \brief dewarpDebug() + * + * \param[in] dew + * \param[in] subdirs one or more subdirectories of /tmp; e.g., "dew1" + * \param[in] index to help label output images; e.g., the page number + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Prints dewarp fields and generates disparity array contour images.
+ *          The contour images are written to file:
+ *                /tmp/[subdirs]/pixv_[index].png
+ * 
+ */ +l_ok +dewarpDebug(L_DEWARP *dew, + const char *subdirs, + l_int32 index) +{ +char fname[256]; +char *outdir; +l_int32 svd, shd; +PIX *pixv, *pixh; + + PROCNAME("dewarpDebug"); + + if (!dew) + return ERROR_INT("dew not defined", procName, 1); + if (!subdirs) + return ERROR_INT("subdirs not defined", procName, 1); + + fprintf(stderr, "pageno = %d, hasref = %d, refpage = %d\n", + dew->pageno, dew->hasref, dew->refpage); + fprintf(stderr, "sampling = %d, redfactor = %d, minlines = %d\n", + dew->sampling, dew->redfactor, dew->minlines); + svd = shd = 0; + if (!dew->hasref) { + if (dew->sampvdispar) svd = 1; + if (dew->samphdispar) shd = 1; + fprintf(stderr, "sampv = %d, samph = %d\n", svd, shd); + fprintf(stderr, "w = %d, h = %d\n", dew->w, dew->h); + fprintf(stderr, "nx = %d, ny = %d\n", dew->nx, dew->ny); + fprintf(stderr, "nlines = %d\n", dew->nlines); + if (svd) { + fprintf(stderr, "(min,max,abs-diff) line curvature = (%d,%d,%d)\n", + dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv); + } + if (shd) { + fprintf(stderr, "(left edge slope = %d, right edge slope = %d\n", + dew->leftslope, dew->rightslope); + fprintf(stderr, "(left,right,abs-diff) edge curvature = " + "(%d,%d,%d)\n", dew->leftcurv, dew->rightcurv, + L_ABS(dew->leftcurv - dew->rightcurv)); + } + } + if (!svd && !shd) { + fprintf(stderr, "No disparity arrays\n"); + return 0; + } + + dewarpPopulateFullRes(dew, NULL, 0, 0); + lept_mkdir(subdirs); + outdir = pathJoin("/tmp", subdirs); + if (svd) { + pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); + snprintf(fname, sizeof(fname), "%s/pixv_%d.png", outdir, index); + pixWriteDebug(fname, pixv, IFF_PNG); + pixDestroy(&pixv); + } + if (shd) { + pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); + snprintf(fname, sizeof(fname), "%s/pixh_%d.png", outdir, index); + pixWriteDebug(fname, pixh, IFF_PNG); + pixDestroy(&pixh); + } + LEPT_FREE(outdir); + return 0; +} + + +/*! + * \brief dewarpShowResults() + * + * \param[in] dewa + * \param[in] sa of indexed input images + * \param[in] boxa crop boxes for input images; can be null + * \param[in] firstpage + * \param[in] lastpage + * \param[in] pdfout filename + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a pdf of image pairs (before, after) for
+ *          the designated set of input pages.
+ *      (2) If the boxa exists, its elements are aligned with numbers
+ *          in the filenames in %sa.  It is used to crop the input images.
+ *          It is assumed that the dewa was generated from the cropped
+ *          images.  No undercropping is applied before rendering.
+ * 
+ */ +l_ok +dewarpShowResults(L_DEWARPA *dewa, + SARRAY *sa, + BOXA *boxa, + l_int32 firstpage, + l_int32 lastpage, + const char *pdfout) +{ +char bufstr[256]; +l_int32 i, modelpage; +L_BMF *bmf; +BOX *box; +L_DEWARP *dew; +PIX *pixs, *pixc, *pixd, *pixt1, *pixt2; +PIXA *pixa; + + PROCNAME("dewarpShowResults"); + + if (!dewa) + return ERROR_INT("dewa not defined", procName, 1); + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pdfout) + return ERROR_INT("pdfout not defined", procName, 1); + if (firstpage > lastpage) + return ERROR_INT("invalid first/last page numbers", procName, 1); + + lept_rmdir("lept/dewarp_pdfout"); + lept_mkdir("lept/dewarp_pdfout"); + bmf = bmfCreate(NULL, 6); + + fprintf(stderr, "Dewarping and generating s/by/s view\n"); + for (i = firstpage; i <= lastpage; i++) { + if (i && (i % 10 == 0)) fprintf(stderr, ".. %d ", i); + pixs = pixReadIndexed(sa, i); + if (boxa) { + box = boxaGetBox(boxa, i, L_CLONE); + pixc = pixClipRectangle(pixs, box, NULL); + boxDestroy(&box); + } + else + pixc = pixClone(pixs); + dew = dewarpaGetDewarp(dewa, i); + pixd = NULL; + if (dew) { + dewarpaApplyDisparity(dewa, dew->pageno, pixc, + GrayInValue, 0, 0, &pixd, NULL); + dewarpMinimize(dew); + } + pixa = pixaCreate(2); + pixaAddPix(pixa, pixc, L_INSERT); + if (pixd) + pixaAddPix(pixa, pixd, L_INSERT); + pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 500, 2, 0, 35, 2); + if (dew) { + modelpage = (dew->hasref) ? dew->refpage : dew->pageno; + snprintf(bufstr, sizeof(bufstr), "Page %d; using %d\n", + i, modelpage); + } + else + snprintf(bufstr, sizeof(bufstr), "Page %d; no dewarp\n", i); + pixt2 = pixAddSingleTextblock(pixt1, bmf, bufstr, 0x0000ff00, + L_ADD_BELOW, 0); + snprintf(bufstr, sizeof(bufstr), "/tmp/lept/dewarp_pdfout/%05d", i); + pixWriteDebug(bufstr, pixt2, IFF_JFIF_JPEG); + pixaDestroy(&pixa); + pixDestroy(&pixs); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Generating pdf of result\n"); + convertFilesToPdf("/tmp/lept/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE, + 0, "Dewarp sequence", pdfout); + fprintf(stderr, "Output written to: %s\n", pdfout); + bmfDestroy(&bmf); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dnabasic.c b/hgdriver/3rdparty/hgOCR/leptonica/dnabasic.c new file mode 100644 index 0000000..35fa550 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dnabasic.c @@ -0,0 +1,1682 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dnabasic.c + *
+ *
+ *      Dna creation, destruction, copy, clone, etc.
+ *          L_DNA       *l_dnaCreate()
+ *          L_DNA       *l_dnaCreateFromIArray()
+ *          L_DNA       *l_dnaCreateFromDArray()
+ *          L_DNA       *l_dnaMakeSequence()
+ *          void        *l_dnaDestroy()
+ *          L_DNA       *l_dnaCopy()
+ *          L_DNA       *l_dnaClone()
+ *          l_int32      l_dnaEmpty()
+ *
+ *      Dna: add/remove number and extend array
+ *          l_int32      l_dnaAddNumber()
+ *          static l_int32  l_dnaExtendArray()
+ *          l_int32      l_dnaInsertNumber()
+ *          l_int32      l_dnaRemoveNumber()
+ *          l_int32      l_dnaReplaceNumber()
+ *
+ *      Dna accessors
+ *          l_int32      l_dnaGetCount()
+ *          l_int32      l_dnaSetCount()
+ *          l_int32      l_dnaGetIValue()
+ *          l_int32      l_dnaGetDValue()
+ *          l_int32      l_dnaSetValue()
+ *          l_int32      l_dnaShiftValue()
+ *          l_int32     *l_dnaGetIArray()
+ *          l_float64   *l_dnaGetDArray()
+ *          l_int32      l_dnaGetRefcount()
+ *          l_int32      l_dnaChangeRefcount()
+ *          l_int32      l_dnaGetParameters()
+ *          l_int32      l_dnaSetParameters()
+ *          l_int32      l_dnaCopyParameters()
+ *
+ *      Serialize Dna for I/O
+ *          L_DNA       *l_dnaRead()
+ *          L_DNA       *l_dnaReadStream()
+ *          l_int32      l_dnaWrite()
+ *          l_int32      l_dnaWriteStream()
+ *
+ *      Dnaa creation, destruction
+ *          L_DNAA      *l_dnaaCreate()
+ *          L_DNAA      *l_dnaaCreateFull()
+ *          l_int32      l_dnaaTruncate()
+ *          void        *l_dnaaDestroy()
+ *
+ *      Add Dna to Dnaa
+ *          l_int32      l_dnaaAddDna()
+ *          static l_int32  l_dnaaExtendArray()
+ *
+ *      Dnaa accessors
+ *          l_int32      l_dnaaGetCount()
+ *          l_int32      l_dnaaGetDnaCount()
+ *          l_int32      l_dnaaGetNumberCount()
+ *          L_DNA       *l_dnaaGetDna()
+ *          L_DNA       *l_dnaaReplaceDna()
+ *          l_int32      l_dnaaGetValue()
+ *          l_int32      l_dnaaAddNumber()
+ *
+ *      Serialize Dnaa for I/O
+ *          L_DNAA      *l_dnaaRead()
+ *          L_DNAA      *l_dnaaReadStream()
+ *          l_int32      l_dnaaWrite()
+ *          l_int32      l_dnaaWriteStream()
+ *
+ *    (1) The Dna is a struct holding an array of doubles.  It can also
+ *        be used to store l_int32 values, up to the full precision
+ *        of int32.  Always use it whenever integers larger than a
+ *        few million need to be stored.
+ *
+ *    (2) Always use the accessors in this file, never the fields directly.
+ *
+ *    (3) Storing and retrieving numbers:
+ *
+ *       * to append a new number to the array, use l_dnaAddNumber().  If
+ *         the number is an int, it will will automatically be converted
+ *         to l_float64 and stored.
+ *
+ *       * to reset a value stored in the array, use l_dnaSetValue().
+ *
+ *       * to increment or decrement a value stored in the array,
+ *         use l_dnaShiftValue().
+ *
+ *       * to obtain a value from the array, use either l_dnaGetIValue()
+ *         or l_dnaGetDValue(), depending on whether you are retrieving
+ *         an integer or a float64.  This avoids doing an explicit cast,
+ *         such as
+ *           (a) return a l_float64 and cast it to an l_int32
+ *           (b) cast the return directly to (l_float64 *) to
+ *               satisfy the function prototype, as in
+ *                 l_dnaGetDValue(da, index, (l_float64 *)&ival);   [ugly!]
+ *
+ *    (4) int <--> double conversions:
+ *
+ *        Conversions go automatically from l_int32 --> l_float64,
+ *        without loss of precision.  You must cast (l_int32)
+ *        to go from l_float64 --> l_int32 because you're truncating
+ *        to the integer value.
+ *
+ *    (5) As with other arrays in leptonica, the l_dna has both an allocated
+ *        size and a count of the stored numbers.  When you add a number, it
+ *        goes on the end of the array, and causes a realloc if the array
+ *        is already filled.  However, in situations where you want to
+ *        add numbers randomly into an array, such as when you build a
+ *        histogram, you must set the count of stored numbers in advance.
+ *        This is done with l_dnaSetCount().  If you set a count larger
+ *        than the allocated array, it does a realloc to the size requested.
+ *
+ *    (6) In situations where the data in a l_dna correspond to a function
+ *        y(x), the values can be either at equal spacings in x or at
+ *        arbitrary spacings.  For the former, we can represent all x values
+ *        by two parameters: startx (corresponding to y[0]) and delx
+ *        for the change in x for adjacent values y[i] and y[i+1].
+ *        startx and delx are initialized to 0.0 and 1.0, rsp.
+ *        For arbitrary spacings, we use a second l_dna, and the two
+ *        l_dnas are typically denoted dnay and dnax.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxArraySize = 100000000; /* dna */ +static const l_uint32 MaxPtrArraySize = 10000; /* dnaa */ +static const l_int32 InitialArraySize = 50; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 l_dnaExtendArray(L_DNA *da); +static l_int32 l_dnaaExtendArray(L_DNAA *daa); + + +/*--------------------------------------------------------------------------* + * Dna creation, destruction, copy, clone, etc. * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaCreate() + * + * \param[in] n size of number array to be alloc'd; 0 for default + * \return da, or NULL on error + */ +L_DNA * +l_dnaCreate(l_int32 n) +{ +L_DNA *da; + + PROCNAME("l_dnaCreate"); + + if (n <= 0 || n > MaxArraySize) + n = InitialArraySize; + + da = (L_DNA *)LEPT_CALLOC(1, sizeof(L_DNA)); + if ((da->array = (l_float64 *)LEPT_CALLOC(n, sizeof(l_float64))) == NULL) { + l_dnaDestroy(&da); + return (L_DNA *)ERROR_PTR("double array not made", procName, NULL); + } + + da->nalloc = n; + da->n = 0; + da->refcount = 1; + da->startx = 0.0; + da->delx = 1.0; + + return da; +} + + +/*! + * \brief l_dnaCreateFromIArray() + * + * \param[in] iarray integer array + * \param[in] size of the array + * \return da, or NULL on error + * + *
+ * Notes:
+ *      (1) We can't insert this int array into the l_dna, because a l_dna
+ *          takes a double array.  So this just copies the data from the
+ *          input array into the l_dna.  The input array continues to be
+ *          owned by the caller.
+ * 
+ */ +L_DNA * +l_dnaCreateFromIArray(l_int32 *iarray, + l_int32 size) +{ +l_int32 i; +L_DNA *da; + + PROCNAME("l_dnaCreateFromIArray"); + + if (!iarray) + return (L_DNA *)ERROR_PTR("iarray not defined", procName, NULL); + if (size <= 0) + return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); + + da = l_dnaCreate(size); + for (i = 0; i < size; i++) + l_dnaAddNumber(da, iarray[i]); + + return da; +} + + +/*! + * \brief l_dnaCreateFromDArray() + * + * \param[in] darray float + * \param[in] size of the array + * \param[in] copyflag L_INSERT or L_COPY + * \return da, or NULL on error + * + *
+ * Notes:
+ *      (1) With L_INSERT, ownership of the input array is transferred
+ *          to the returned l_dna, and all %size elements are considered
+ *          to be valid.
+ * 
+ */ +L_DNA * +l_dnaCreateFromDArray(l_float64 *darray, + l_int32 size, + l_int32 copyflag) +{ +l_int32 i; +L_DNA *da; + + PROCNAME("l_dnaCreateFromDArray"); + + if (!darray) + return (L_DNA *)ERROR_PTR("darray not defined", procName, NULL); + if (size <= 0) + return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); + if (copyflag != L_INSERT && copyflag != L_COPY) + return (L_DNA *)ERROR_PTR("invalid copyflag", procName, NULL); + + da = l_dnaCreate(size); + if (copyflag == L_INSERT) { + if (da->array) LEPT_FREE(da->array); + da->array = darray; + da->n = size; + } else { /* just copy the contents */ + for (i = 0; i < size; i++) + l_dnaAddNumber(da, darray[i]); + } + + return da; +} + + +/*! + * \brief l_dnaMakeSequence() + * + * \param[in] startval + * \param[in] increment + * \param[in] size of sequence + * \return l_dna of sequence of evenly spaced values, or NULL on error + */ +L_DNA * +l_dnaMakeSequence(l_float64 startval, + l_float64 increment, + l_int32 size) +{ +l_int32 i; +l_float64 val; +L_DNA *da; + + PROCNAME("l_dnaMakeSequence"); + + if ((da = l_dnaCreate(size)) == NULL) + return (L_DNA *)ERROR_PTR("da not made", procName, NULL); + + for (i = 0; i < size; i++) { + val = startval + i * increment; + l_dnaAddNumber(da, val); + } + + return da; +} + + +/*! + * \brief l_dnaDestroy() + * + * \param[in,out] pda will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the l_dna.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +l_dnaDestroy(L_DNA **pda) +{ +L_DNA *da; + + PROCNAME("l_dnaDestroy"); + + if (pda == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + + if ((da = *pda) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the l_dna. */ + l_dnaChangeRefcount(da, -1); + if (l_dnaGetRefcount(da) <= 0) { + if (da->array) + LEPT_FREE(da->array); + LEPT_FREE(da); + } + + *pda = NULL; + return; +} + + +/*! + * \brief l_dnaCopy() + * + * \param[in] da + * \return copy of da, or NULL on error + * + *
+ * Notes:
+ *      (1) This removes unused ptrs above da->n.
+ * 
+ */ +L_DNA * +l_dnaCopy(L_DNA *da) +{ +l_int32 i; +L_DNA *dac; + + PROCNAME("l_dnaCopy"); + + if (!da) + return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); + + if ((dac = l_dnaCreate(da->n)) == NULL) + return (L_DNA *)ERROR_PTR("dac not made", procName, NULL); + dac->startx = da->startx; + dac->delx = da->delx; + + for (i = 0; i < da->n; i++) + l_dnaAddNumber(dac, da->array[i]); + + return dac; +} + + +/*! + * \brief l_dnaClone() + * + * \param[in] da + * \return ptr to same da, or NULL on error + */ +L_DNA * +l_dnaClone(L_DNA *da) +{ + PROCNAME("l_dnaClone"); + + if (!da) + return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); + + l_dnaChangeRefcount(da, 1); + return da; +} + + +/*! + * \brief l_dnaEmpty() + * + * \param[in] da + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This does not change the allocation of the array.
+ *          It just clears the number of stored numbers, so that
+ *          the array appears to be empty.
+ * 
+ */ +l_ok +l_dnaEmpty(L_DNA *da) +{ + PROCNAME("l_dnaEmpty"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + + da->n = 0; + return 0; +} + + + +/*--------------------------------------------------------------------------* + * Dna: add/remove number and extend array * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaAddNumber() + * + * \param[in] da + * \param[in] val float or int to be added; stored as a float + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaAddNumber(L_DNA *da, + l_float64 val) +{ +l_int32 n; + + PROCNAME("l_dnaAddNumber"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + + n = l_dnaGetCount(da); + if (n >= da->nalloc) + l_dnaExtendArray(da); + da->array[n] = val; + da->n++; + return 0; +} + + +/*! + * \brief l_dnaExtendArray() + * + * \param[in] da + * \return 0 if OK, 1 on error + */ +static l_int32 +l_dnaExtendArray(L_DNA *da) +{ + PROCNAME("l_dnaExtendArray"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if ((da->array = (l_float64 *)reallocNew((void **)&da->array, + sizeof(l_float64) * da->nalloc, + 2 * sizeof(l_float64) * da->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + da->nalloc *= 2; + return 0; +} + + +/*! + * \brief l_dnaInsertNumber() + * + * \param[in] da + * \param[in] index location in da to insert new value + * \param[in] val float64 or integer to be added + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts da[i] --> da[i + 1] for all i >= %index,
+ *          and then inserts %val as da[%index].
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ *
+ * 
+ */ +l_ok +l_dnaInsertNumber(L_DNA *da, + l_int32 index, + l_float64 val) +{ +l_int32 i, n; + + PROCNAME("l_dnaInsertNumber"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + n = l_dnaGetCount(da); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + + if (n >= da->nalloc) + l_dnaExtendArray(da); + for (i = n; i > index; i--) + da->array[i] = da->array[i - 1]; + da->array[index] = val; + da->n++; + return 0; +} + + +/*! + * \brief l_dnaRemoveNumber() + * + * \param[in] da + * \param[in] index element to be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts da[i] --> da[i - 1] for all i > %index.
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +l_dnaRemoveNumber(L_DNA *da, + l_int32 index) +{ +l_int32 i, n; + + PROCNAME("l_dnaRemoveNumber"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + n = l_dnaGetCount(da); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + for (i = index + 1; i < n; i++) + da->array[i - 1] = da->array[i]; + da->n--; + return 0; +} + + +/*! + * \brief l_dnaReplaceNumber() + * + * \param[in] da + * \param[in] index element to be replaced + * \param[in] val new value to replace old one + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaReplaceNumber(L_DNA *da, + l_int32 index, + l_float64 val) +{ +l_int32 n; + + PROCNAME("l_dnaReplaceNumber"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + n = l_dnaGetCount(da); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + da->array[index] = val; + return 0; +} + + +/*----------------------------------------------------------------------* + * Dna accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaGetCount() + * + * \param[in] da + * \return count, or 0 if no numbers or on error + */ +l_int32 +l_dnaGetCount(L_DNA *da) +{ + PROCNAME("l_dnaGetCount"); + + if (!da) + return ERROR_INT("da not defined", procName, 0); + return da->n; +} + + +/*! + * \brief l_dnaSetCount() + * + * \param[in] da + * \param[in] newcount + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %newcount <= da->nalloc, this resets da->n.
+ *          Using %newcount = 0 is equivalent to l_dnaEmpty().
+ *      (2) If %newcount > da->nalloc, this causes a realloc
+ *          to a size da->nalloc = %newcount.
+ *      (3) All the previously unused values in da are set to 0.0.
+ * 
+ */ +l_ok +l_dnaSetCount(L_DNA *da, + l_int32 newcount) +{ + PROCNAME("l_dnaSetCount"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + if (newcount > da->nalloc) { + if ((da->array = (l_float64 *)reallocNew((void **)&da->array, + sizeof(l_float64) * da->nalloc, + sizeof(l_float64) * newcount)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + da->nalloc = newcount; + } + da->n = newcount; + return 0; +} + + +/*! + * \brief l_dnaGetDValue() + * + * \param[in] da + * \param[in] index into l_dna + * \param[out] pval double value; 0.0 on error + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caller may need to check the function return value to
+ *          decide if a 0.0 in the returned ival is valid.
+ * 
+ */ +l_ok +l_dnaGetDValue(L_DNA *da, + l_int32 index, + l_float64 *pval) +{ + PROCNAME("l_dnaGetDValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if (index < 0 || index >= da->n) + return ERROR_INT("index not valid", procName, 1); + + *pval = da->array[index]; + return 0; +} + + +/*! + * \brief l_dnaGetIValue() + * + * \param[in] da + * \param[in] index into l_dna + * \param[out] pival integer value; 0 on error + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caller may need to check the function return value to
+ *          decide if a 0 in the returned ival is valid.
+ * 
+ */ +l_ok +l_dnaGetIValue(L_DNA *da, + l_int32 index, + l_int32 *pival) +{ +l_float64 val; + + PROCNAME("l_dnaGetIValue"); + + if (!pival) + return ERROR_INT("&ival not defined", procName, 1); + *pival = 0; + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if (index < 0 || index >= da->n) + return ERROR_INT("index not valid", procName, 1); + + val = da->array[index]; + *pival = (l_int32)(val + L_SIGN(val) * 0.5); + return 0; +} + + +/*! + * \brief l_dnaSetValue() + * + * \param[in] da + * \param[in] index to element to be set + * \param[in] val to set element + * \return 0 if OK; 1 on error + */ +l_ok +l_dnaSetValue(L_DNA *da, + l_int32 index, + l_float64 val) +{ + PROCNAME("l_dnaSetValue"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + if (index < 0 || index >= da->n) + return ERROR_INT("index not valid", procName, 1); + + da->array[index] = val; + return 0; +} + + +/*! + * \brief l_dnaShiftValue() + * + * \param[in] da + * \param[in] index to element to change relative to the current value + * \param[in] diff increment if diff > 0 or decrement if diff < 0 + * \return 0 if OK; 1 on error + */ +l_ok +l_dnaShiftValue(L_DNA *da, + l_int32 index, + l_float64 diff) +{ + PROCNAME("l_dnaShiftValue"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + if (index < 0 || index >= da->n) + return ERROR_INT("index not valid", procName, 1); + + da->array[index] += diff; + return 0; +} + + +/*! + * \brief l_dnaGetIArray() + * + * \param[in] da + * \return a copy of the bare internal array, integerized + * by rounding, or NULL on error + *
+ * Notes:
+ *      (1) A copy of the array is made, because we need to
+ *          generate an integer array from the bare double array.
+ *          The caller is responsible for freeing the array.
+ *      (2) The array size is determined by the number of stored numbers,
+ *          not by the size of the allocated array in the l_dna.
+ *      (3) This function is provided to simplify calculations
+ *          using the bare internal array, rather than continually
+ *          calling accessors on the l_dna.  It is typically used
+ *          on an array of size 256.
+ * 
+ */ +l_int32 * +l_dnaGetIArray(L_DNA *da) +{ +l_int32 i, n, ival; +l_int32 *array; + + PROCNAME("l_dnaGetIArray"); + + if (!da) + return (l_int32 *)ERROR_PTR("da not defined", procName, NULL); + + n = l_dnaGetCount(da); + if ((array = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) + return (l_int32 *)ERROR_PTR("array not made", procName, NULL); + for (i = 0; i < n; i++) { + l_dnaGetIValue(da, i, &ival); + array[i] = ival; + } + + return array; +} + + +/*! + * \brief l_dnaGetDArray() + * + * \param[in] da + * \param[in] copyflag L_NOCOPY or L_COPY + * \return either the bare internal array or a copy of it, or NULL on error + * + *
+ * Notes:
+ *      (1) If %copyflag == L_COPY, it makes a copy which the caller
+ *          is responsible for freeing.  Otherwise, it operates
+ *          directly on the bare array of the l_dna.
+ *      (2) Very important: for L_NOCOPY, any writes to the array
+ *          will be in the l_dna.  Do not write beyond the size of
+ *          the count field, because it will not be accessible
+ *          from the l_dna!  If necessary, be sure to set the count
+ *          field to a larger number (such as the alloc size)
+ *          BEFORE calling this function.  Creating with l_dnaMakeConstant()
+ *          is another way to insure full initialization.
+ * 
+ */ +l_float64 * +l_dnaGetDArray(L_DNA *da, + l_int32 copyflag) +{ +l_int32 i, n; +l_float64 *array; + + PROCNAME("l_dnaGetDArray"); + + if (!da) + return (l_float64 *)ERROR_PTR("da not defined", procName, NULL); + + if (copyflag == L_NOCOPY) { + array = da->array; + } else { /* copyflag == L_COPY */ + n = l_dnaGetCount(da); + if ((array = (l_float64 *)LEPT_CALLOC(n, sizeof(l_float64))) == NULL) + return (l_float64 *)ERROR_PTR("array not made", procName, NULL); + for (i = 0; i < n; i++) + array[i] = da->array[i]; + } + + return array; +} + + +/*! + * \brief l_dnaGetRefCount() + * + * \param[in] da + * \return refcount, or UNDEF on error + */ +l_int32 +l_dnaGetRefcount(L_DNA *da) +{ + PROCNAME("l_dnaGetRefcount"); + + if (!da) + return ERROR_INT("da not defined", procName, UNDEF); + return da->refcount; +} + + +/*! + * \brief l_dnaChangeRefCount() + * + * \param[in] da + * \param[in] delta change to be applied + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaChangeRefcount(L_DNA *da, + l_int32 delta) +{ + PROCNAME("l_dnaChangeRefcount"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + da->refcount += delta; + return 0; +} + + +/*! + * \brief l_dnaGetParameters() + * + * \param[in] da + * \param[out] pstartx [optional] startx + * \param[out] pdelx [optional] delx + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaGetParameters(L_DNA *da, + l_float64 *pstartx, + l_float64 *pdelx) +{ + PROCNAME("l_dnaGetParameters"); + + if (pstartx) *pstartx = 0.0; + if (pdelx) *pdelx = 1.0; + if (!pstartx && !pdelx) + return ERROR_INT("neither &startx nor &delx are defined", procName, 1); + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if (pstartx) *pstartx = da->startx; + if (pdelx) *pdelx = da->delx; + return 0; +} + + +/*! + * \brief l_dnaSetParameters() + * + * \param[in] da + * \param[in] startx x value corresponding to da[0] + * \param[in] delx difference in x values for the situation where the + * elements of da correspond to the evaulation of a + * function at equal intervals of size %delx + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaSetParameters(L_DNA *da, + l_float64 startx, + l_float64 delx) +{ + PROCNAME("l_dnaSetParameters"); + + if (!da) + return ERROR_INT("da not defined", procName, 1); + + da->startx = startx; + da->delx = delx; + return 0; +} + + +/*! + * \brief l_dnaCopyParameters() + * + * \param[in] dad destination DNuma + * \param[in] das source DNuma + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaCopyParameters(L_DNA *dad, + L_DNA *das) +{ +l_float64 start, binsize; + + PROCNAME("l_dnaCopyParameters"); + + if (!das || !dad) + return ERROR_INT("das and dad not both defined", procName, 1); + + l_dnaGetParameters(das, &start, &binsize); + l_dnaSetParameters(dad, start, binsize); + return 0; +} + + +/*----------------------------------------------------------------------* + * Serialize Dna for I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaRead() + * + * \param[in] filename + * \return da, or NULL on error + */ +L_DNA * +l_dnaRead(const char *filename) +{ +FILE *fp; +L_DNA *da; + + PROCNAME("l_dnaRead"); + + if (!filename) + return (L_DNA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (L_DNA *)ERROR_PTR("stream not opened", procName, NULL); + da = l_dnaReadStream(fp); + fclose(fp); + if (!da) + return (L_DNA *)ERROR_PTR("da not read", procName, NULL); + return da; +} + + +/*! + * \brief l_dnaReadStream() + * + * \param[in] fp file stream + * \return da, or NULL on error + * + *
+ * Notes:
+ *      (1) fscanf takes %lf to read a double; fprintf takes %f to write it.
+ * 
+ */ +L_DNA * +l_dnaReadStream(FILE *fp) +{ +l_int32 i, n, index, ret, version; +l_float64 val, startx, delx; +L_DNA *da; + + PROCNAME("l_dnaReadStream"); + + if (!fp) + return (L_DNA *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, "\nL_Dna Version %d\n", &version); + if (ret != 1) + return (L_DNA *)ERROR_PTR("not a l_dna file", procName, NULL); + if (version != DNA_VERSION_NUMBER) + return (L_DNA *)ERROR_PTR("invalid l_dna version", procName, NULL); + if (fscanf(fp, "Number of numbers = %d\n", &n) != 1) + return (L_DNA *)ERROR_PTR("invalid number of numbers", procName, NULL); + + if (n > MaxArraySize) { + L_ERROR("n = %d > %d\n", procName, n, MaxArraySize); + return NULL; + } + if ((da = l_dnaCreate(n)) == NULL) + return (L_DNA *)ERROR_PTR("da not made", procName, NULL); + for (i = 0; i < n; i++) { + if (fscanf(fp, " [%d] = %lf\n", &index, &val) != 2) { + l_dnaDestroy(&da); + return (L_DNA *)ERROR_PTR("bad input data", procName, NULL); + } + l_dnaAddNumber(da, val); + } + + /* Optional data */ + if (fscanf(fp, "startx = %lf, delx = %lf\n", &startx, &delx) == 2) + l_dnaSetParameters(da, startx, delx); + return da; +} + + +/*! + * \brief l_dnaWrite() + * + * \param[in] filename + * \param[in] da + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaWrite(const char *filename, + L_DNA *da) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("l_dnaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = l_dnaWriteStream(fp, da); + fclose(fp); + if (ret) + return ERROR_INT("da not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief l_dnaWriteStream() + * + * \param[in] fp file stream + * \param[in] da + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaWriteStream(FILE *fp, + L_DNA *da) +{ +l_int32 i, n; +l_float64 startx, delx; + + PROCNAME("l_dnaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!da) + return ERROR_INT("da not defined", procName, 1); + + n = l_dnaGetCount(da); + fprintf(fp, "\nL_Dna Version %d\n", DNA_VERSION_NUMBER); + fprintf(fp, "Number of numbers = %d\n", n); + for (i = 0; i < n; i++) + fprintf(fp, " [%d] = %f\n", i, da->array[i]); + fprintf(fp, "\n"); + + /* Optional data */ + l_dnaGetParameters(da, &startx, &delx); + if (startx != 0.0 || delx != 1.0) + fprintf(fp, "startx = %f, delx = %f\n", startx, delx); + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Dnaa creation, destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaaCreate() + * + * \param[in] n size of l_dna ptr array to be alloc'd 0 for default + * \return daa, or NULL on error + * + */ +L_DNAA * +l_dnaaCreate(l_int32 n) +{ +L_DNAA *daa; + + PROCNAME("l_dnaaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialArraySize; + + daa = (L_DNAA *)LEPT_CALLOC(1, sizeof(L_DNAA)); + if ((daa->dna = (L_DNA **)LEPT_CALLOC(n, sizeof(L_DNA *))) == NULL) { + l_dnaaDestroy(&daa); + return (L_DNAA *)ERROR_PTR("l_dna ptr array not made", procName, NULL); + } + daa->nalloc = n; + daa->n = 0; + return daa; +} + + +/*! + * \brief l_dnaaCreateFull() + * + * \param[in] nptr size of dna ptr array to be alloc'd + * \param[in] n size of individual dna arrays to be alloc'd 0 for default + * \return daa, or NULL on error + * + *
+ * Notes:
+ *      (1) This allocates a dnaa and fills the array with allocated dnas.
+ *          In use, after calling this function, use
+ *              l_dnaaAddNumber(dnaa, index, val);
+ *          to add val to the index-th dna in dnaa.
+ * 
+ */ +L_DNAA * +l_dnaaCreateFull(l_int32 nptr, + l_int32 n) +{ +l_int32 i; +L_DNAA *daa; +L_DNA *da; + + daa = l_dnaaCreate(nptr); + for (i = 0; i < nptr; i++) { + da = l_dnaCreate(n); + l_dnaaAddDna(daa, da, L_INSERT); + } + + return daa; +} + + +/*! + * \brief l_dnaaTruncate() + * + * \param[in] daa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This identifies the largest index containing a dna that
+ *          has any numbers within it, destroys all dna beyond that
+ *          index, and resets the count.
+ * 
+ */ +l_ok +l_dnaaTruncate(L_DNAA *daa) +{ +l_int32 i, n, nn; +L_DNA *da; + + PROCNAME("l_dnaaTruncate"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + + n = l_dnaaGetCount(daa); + for (i = n - 1; i >= 0; i--) { + da = l_dnaaGetDna(daa, i, L_CLONE); + if (!da) + continue; + nn = l_dnaGetCount(da); + l_dnaDestroy(&da); /* the clone */ + if (nn == 0) + l_dnaDestroy(&daa->dna[i]); + else + break; + } + daa->n = i + 1; + return 0; +} + + +/*! + * \brief l_dnaaDestroy() + * + * \param[in,out] pdaa will be set to null before returning + * \return void + */ +void +l_dnaaDestroy(L_DNAA **pdaa) +{ +l_int32 i; +L_DNAA *daa; + + PROCNAME("l_dnaaDestroy"); + + if (pdaa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((daa = *pdaa) == NULL) + return; + + for (i = 0; i < daa->n; i++) + l_dnaDestroy(&daa->dna[i]); + LEPT_FREE(daa->dna); + LEPT_FREE(daa); + *pdaa = NULL; + + return; +} + + +/*--------------------------------------------------------------------------* + * Add Dna to Dnaa * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaaAddDna() + * + * \param[in] daa + * \param[in] da to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaaAddDna(L_DNAA *daa, + L_DNA *da, + l_int32 copyflag) +{ +l_int32 n; +L_DNA *dac; + + PROCNAME("l_dnaaAddDna"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + if (!da) + return ERROR_INT("da not defined", procName, 1); + + if (copyflag == L_INSERT) { + dac = da; + } else if (copyflag == L_COPY) { + if ((dac = l_dnaCopy(da)) == NULL) + return ERROR_INT("dac not made", procName, 1); + } else if (copyflag == L_CLONE) { + dac = l_dnaClone(da); + } else { + return ERROR_INT("invalid copyflag", procName, 1); + } + + n = l_dnaaGetCount(daa); + if (n >= daa->nalloc) + l_dnaaExtendArray(daa); + daa->dna[n] = dac; + daa->n++; + return 0; +} + + +/*! + * \brief l_dnaaExtendArray() + * + * \param[in] daa + * \return 0 if OK, 1 on error + */ +static l_int32 +l_dnaaExtendArray(L_DNAA *daa) +{ + PROCNAME("l_dnaaExtendArray"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + + if ((daa->dna = (L_DNA **)reallocNew((void **)&daa->dna, + sizeof(L_DNA *) * daa->nalloc, + 2 * sizeof(L_DNA *) * daa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + daa->nalloc *= 2; + return 0; +} + + +/*----------------------------------------------------------------------* + * DNumaa accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaaGetCount() + * + * \param[in] daa + * \return count number of l_dna, or 0 if no l_dna or on error + */ +l_int32 +l_dnaaGetCount(L_DNAA *daa) +{ + PROCNAME("l_dnaaGetCount"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 0); + return daa->n; +} + + +/*! + * \brief l_dnaaGetDnaCount() + * + * \param[in] daa + * \param[in] index of l_dna in daa + * \return count of numbers in the referenced l_dna, or 0 on error. + */ +l_int32 +l_dnaaGetDnaCount(L_DNAA *daa, + l_int32 index) +{ + PROCNAME("l_dnaaGetDnaCount"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 0); + if (index < 0 || index >= daa->n) + return ERROR_INT("invalid index into daa", procName, 0); + return l_dnaGetCount(daa->dna[index]); +} + + +/*! + * \brief l_dnaaGetNumberCount() + * + * \param[in] daa + * \return count total number of numbers in the l_dnaa, + * or 0 if no numbers or on error + */ +l_int32 +l_dnaaGetNumberCount(L_DNAA *daa) +{ +L_DNA *da; +l_int32 n, sum, i; + + PROCNAME("l_dnaaGetNumberCount"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 0); + + n = l_dnaaGetCount(daa); + for (sum = 0, i = 0; i < n; i++) { + da = l_dnaaGetDna(daa, i, L_CLONE); + sum += l_dnaGetCount(da); + l_dnaDestroy(&da); + } + + return sum; +} + + +/*! + * \brief l_dnaaGetDna() + * + * \param[in] daa + * \param[in] index to the index-th l_dna + * \param[in] accessflag L_COPY or L_CLONE + * \return l_dna, or NULL on error + */ +L_DNA * +l_dnaaGetDna(L_DNAA *daa, + l_int32 index, + l_int32 accessflag) +{ + PROCNAME("l_dnaaGetDna"); + + if (!daa) + return (L_DNA *)ERROR_PTR("daa not defined", procName, NULL); + if (index < 0 || index >= daa->n) + return (L_DNA *)ERROR_PTR("index not valid", procName, NULL); + + if (accessflag == L_COPY) + return l_dnaCopy(daa->dna[index]); + else if (accessflag == L_CLONE) + return l_dnaClone(daa->dna[index]); + else + return (L_DNA *)ERROR_PTR("invalid accessflag", procName, NULL); +} + + +/*! + * \brief l_dnaaReplaceDna() + * + * \param[in] daa + * \param[in] index to the index-th l_dna + * \param[in] da insert and replace any existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Any existing l_dna is destroyed, and the input one
+ *          is inserted in its place.
+ *      (2) If %index is invalid, return 1 (error)
+ * 
+ */ +l_ok +l_dnaaReplaceDna(L_DNAA *daa, + l_int32 index, + L_DNA *da) +{ +l_int32 n; + + PROCNAME("l_dnaaReplaceDna"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + if (!da) + return ERROR_INT("da not defined", procName, 1); + n = l_dnaaGetCount(daa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + + l_dnaDestroy(&daa->dna[index]); + daa->dna[index] = da; + return 0; +} + + +/*! + * \brief l_dnaaGetValue() + * + * \param[in] daa + * \param[in] i index of l_dna within l_dnaa + * \param[in] j index into l_dna + * \param[out] pval double value + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaaGetValue(L_DNAA *daa, + l_int32 i, + l_int32 j, + l_float64 *pval) +{ +l_int32 n; +L_DNA *da; + + PROCNAME("l_dnaaGetValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + n = l_dnaaGetCount(daa); + if (i < 0 || i >= n) + return ERROR_INT("invalid index into daa", procName, 1); + da = daa->dna[i]; + if (j < 0 || j >= da->n) + return ERROR_INT("invalid index into da", procName, 1); + *pval = da->array[j]; + return 0; +} + + +/*! + * \brief l_dnaaAddNumber() + * + * \param[in] daa + * \param[in] index of l_dna within l_dnaa + * \param[in] val number to be added; stored as a double + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Adds to an existing l_dna only.
+ * 
+ */ +l_ok +l_dnaaAddNumber(L_DNAA *daa, + l_int32 index, + l_float64 val) +{ +l_int32 n; +L_DNA *da; + + PROCNAME("l_dnaaAddNumber"); + + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + n = l_dnaaGetCount(daa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index in daa", procName, 1); + + da = l_dnaaGetDna(daa, index, L_CLONE); + l_dnaAddNumber(da, val); + l_dnaDestroy(&da); + return 0; +} + + +/*----------------------------------------------------------------------* + * Serialize Dna for I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaaRead() + * + * \param[in] filename + * \return daa, or NULL on error + */ +L_DNAA * +l_dnaaRead(const char *filename) +{ +FILE *fp; +L_DNAA *daa; + + PROCNAME("l_dnaaRead"); + + if (!filename) + return (L_DNAA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (L_DNAA *)ERROR_PTR("stream not opened", procName, NULL); + daa = l_dnaaReadStream(fp); + fclose(fp); + if (!daa) + return (L_DNAA *)ERROR_PTR("daa not read", procName, NULL); + return daa; +} + + +/*! + * \brief l_dnaaReadStream() + * + * \param[in] fp file stream + * \return daa, or NULL on error + */ +L_DNAA * +l_dnaaReadStream(FILE *fp) +{ +l_int32 i, n, index, ret, version; +L_DNA *da; +L_DNAA *daa; + + PROCNAME("l_dnaaReadStream"); + + if (!fp) + return (L_DNAA *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, "\nL_Dnaa Version %d\n", &version); + if (ret != 1) + return (L_DNAA *)ERROR_PTR("not a l_dna file", procName, NULL); + if (version != DNA_VERSION_NUMBER) + return (L_DNAA *)ERROR_PTR("invalid l_dnaa version", procName, NULL); + if (fscanf(fp, "Number of L_Dna = %d\n\n", &n) != 1) + return (L_DNAA *)ERROR_PTR("invalid number of l_dna", procName, NULL); + + if (n > MaxPtrArraySize) { + L_ERROR("n = %d > %d\n", procName, n, MaxPtrArraySize); + return NULL; + } + if ((daa = l_dnaaCreate(n)) == NULL) + return (L_DNAA *)ERROR_PTR("daa not made", procName, NULL); + + for (i = 0; i < n; i++) { + if (fscanf(fp, "L_Dna[%d]:", &index) != 1) { + l_dnaaDestroy(&daa); + return (L_DNAA *)ERROR_PTR("invalid l_dna header", procName, NULL); + } + if ((da = l_dnaReadStream(fp)) == NULL) { + l_dnaaDestroy(&daa); + return (L_DNAA *)ERROR_PTR("da not made", procName, NULL); + } + l_dnaaAddDna(daa, da, L_INSERT); + } + + return daa; +} + + +/*! + * \brief l_dnaaWrite() + * + * \param[in] filename + * \param[in] daa + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaaWrite(const char *filename, + L_DNAA *daa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("l_dnaaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = l_dnaaWriteStream(fp, daa); + fclose(fp); + if (ret) + return ERROR_INT("daa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief l_dnaaWriteStream() + * + * \param[in] fp file stream + * \param[in] daa + * \return 0 if OK, 1 on error + */ +l_ok +l_dnaaWriteStream(FILE *fp, + L_DNAA *daa) +{ +l_int32 i, n; +L_DNA *da; + + PROCNAME("l_dnaaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!daa) + return ERROR_INT("daa not defined", procName, 1); + + n = l_dnaaGetCount(daa); + fprintf(fp, "\nL_Dnaa Version %d\n", DNA_VERSION_NUMBER); + fprintf(fp, "Number of L_Dna = %d\n\n", n); + for (i = 0; i < n; i++) { + if ((da = l_dnaaGetDna(daa, i, L_CLONE)) == NULL) + return ERROR_INT("da not found", procName, 1); + fprintf(fp, "L_Dna[%d]:", i); + l_dnaWriteStream(fp, da); + l_dnaDestroy(&da); + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dnafunc1.c b/hgdriver/3rdparty/hgOCR/leptonica/dnafunc1.c new file mode 100644 index 0000000..0a05ebc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dnafunc1.c @@ -0,0 +1,404 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dnafunc1.c + *
+ *
+ *      Rearrangements
+ *          l_int32     *l_dnaJoin()
+ *          l_int32     *l_dnaaFlattenToDna()
+ *
+ *      Conversion between numa and dna
+ *          NUMA        *l_dnaConvertToNuma()
+ *          L_DNA       *numaConvertToDna()
+ *
+ *      Set operations using aset (rbtree)
+ *          L_DNA       *l_dnaUnionByAset()
+ *          L_DNA       *l_dnaRemoveDupsByAset()
+ *          L_DNA       *l_dnaIntersectionByAset()
+ *          L_ASET      *l_asetCreateFromDna()
+ *
+ *      Miscellaneous operations
+ *          L_DNA       *l_dnaDiffAdjValues()
+ *
+ *
+ * This file contains an implementation on sets of doubles (or integers)
+ * that uses an underlying tree (rbtree).  The keys stored in the tree
+ * are simply the double array values in the dna.  Use of a DnaHash
+ * is typically more efficient, with O(1) in lookup and insertion.
+ *
+ * 
+ */ + +#include "allheaders.h" + +/*----------------------------------------------------------------------* + * Rearrangements * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaJoin() + * + * \param[in] dad dest dna; add to this one + * \param[in] das [optional] source dna; add from this one + * \param[in] istart starting index in das + * \param[in] iend ending index in das; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (2) iend < 0 means 'read to the end'
+ *      (3) if das == NULL, this is a no-op
+ * 
+ */ +l_ok +l_dnaJoin(L_DNA *dad, + L_DNA *das, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +l_float64 val; + + PROCNAME("l_dnaJoin"); + + if (!dad) + return ERROR_INT("dad not defined", procName, 1); + if (!das) + return 0; + + if (istart < 0) + istart = 0; + n = l_dnaGetCount(das); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + l_dnaGetDValue(das, i, &val); + l_dnaAddNumber(dad, val); + } + + return 0; +} + + +/*! + * \brief l_dnaaFlattenToDna() + * + * \param[in] daa + * \return dad, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the dnaa to a dna, by joining successively
+ *          each dna in the dnaa.
+ *      (2) It leaves the input dnaa unchanged.
+ * 
+ */ +L_DNA * +l_dnaaFlattenToDna(L_DNAA *daa) +{ +l_int32 i, nalloc; +L_DNA *da, *dad; +L_DNA **array; + + PROCNAME("l_dnaaFlattenToDna"); + + if (!daa) + return (L_DNA *)ERROR_PTR("daa not defined", procName, NULL); + + nalloc = daa->nalloc; + array = daa->dna; + dad = l_dnaCreate(0); + for (i = 0; i < nalloc; i++) { + da = array[i]; + if (!da) continue; + l_dnaJoin(dad, da, 0, -1); + } + + return dad; +} + + +/*----------------------------------------------------------------------* + * Conversion between numa and dna * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaConvertToNuma() + * + * \param[in] da + * \return na, or NULL on error + */ +NUMA * +l_dnaConvertToNuma(L_DNA *da) +{ +l_int32 i, n; +l_float64 val; +NUMA *na; + + PROCNAME("l_dnaConvertToNuma"); + + if (!da) + return (NUMA *)ERROR_PTR("da not defined", procName, NULL); + + n = l_dnaGetCount(da); + na = numaCreate(n); + for (i = 0; i < n; i++) { + l_dnaGetDValue(da, i, &val); + numaAddNumber(na, val); + } + return na; +} + + +/*! + * \brief numaConvertToDna + * + * \param[in] na + * \return da, or NULL on error + */ +L_DNA * +numaConvertToDna(NUMA *na) +{ +l_int32 i, n; +l_float32 val; +L_DNA *da; + + PROCNAME("numaConvertToDna"); + + if (!na) + return (L_DNA *)ERROR_PTR("na not defined", procName, NULL); + + n = numaGetCount(na); + da = l_dnaCreate(n); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + l_dnaAddNumber(da, val); + } + return da; +} + + +/*----------------------------------------------------------------------* + * Set operations using aset (rbtree) * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaUnionByAset() + * + * \param[in] da1, da2 + * \return dad with the union of the set of numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) See sarrayUnionByAset() for the approach.
+ *      (2) Here, the key in building the sorted tree is the number itself.
+ *      (3) Operations using an underlying tree are O(nlogn), which is
+ *          typically less efficient than hashing, which is O(n).
+ * 
+ */ +L_DNA * +l_dnaUnionByAset(L_DNA *da1, + L_DNA *da2) +{ +L_DNA *da3, *dad; + + PROCNAME("l_dnaUnionByAset"); + + if (!da1) + return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); + if (!da2) + return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); + + /* Join */ + da3 = l_dnaCopy(da1); + l_dnaJoin(da3, da2, 0, -1); + + /* Eliminate duplicates */ + dad = l_dnaRemoveDupsByAset(da3); + l_dnaDestroy(&da3); + return dad; +} + + +/*! + * \brief l_dnaRemoveDupsByAset() + * + * \param[in] das + * \return dad with duplicates removed, or NULL on error + */ +L_DNA * +l_dnaRemoveDupsByAset(L_DNA *das) +{ +l_int32 i, n; +l_float64 val; +L_DNA *dad; +L_ASET *set; +RB_TYPE key; + + PROCNAME("l_dnaRemoveDupsByAset"); + + if (!das) + return (L_DNA *)ERROR_PTR("das not defined", procName, NULL); + + set = l_asetCreate(L_FLOAT_TYPE); + dad = l_dnaCreate(0); + n = l_dnaGetCount(das); + for (i = 0; i < n; i++) { + l_dnaGetDValue(das, i, &val); + key.ftype = val; + if (!l_asetFind(set, key)) { + l_dnaAddNumber(dad, val); + l_asetInsert(set, key); + } + } + + l_asetDestroy(&set); + return dad; +} + + +/*! + * \brief l_dnaIntersectionByAset() + * + * \param[in] da1, da2 + * \return dad with the intersection of the two arrays, or NULL on error + * + *
+ * Notes:
+ *      (1) See sarrayIntersection() for the approach.
+ *      (2) Here, the key in building the sorted tree is the number itself.
+ *      (3) Operations using an underlying tree are O(nlogn), which is
+ *          typically less efficient than hashing, which is O(n).
+ * 
+ */ +L_DNA * +l_dnaIntersectionByAset(L_DNA *da1, + L_DNA *da2) +{ +l_int32 n1, n2, i, n; +l_float64 val; +L_ASET *set1, *set2; +RB_TYPE key; +L_DNA *da_small, *da_big, *dad; + + PROCNAME("l_dnaIntersectionByAset"); + + if (!da1) + return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); + if (!da2) + return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); + + /* Put the elements of the largest array into a set */ + n1 = l_dnaGetCount(da1); + n2 = l_dnaGetCount(da2); + da_small = (n1 < n2) ? da1 : da2; /* do not destroy da_small */ + da_big = (n1 < n2) ? da2 : da1; /* do not destroy da_big */ + set1 = l_asetCreateFromDna(da_big); + + /* Build up the intersection of floats */ + dad = l_dnaCreate(0); + n = l_dnaGetCount(da_small); + set2 = l_asetCreate(L_FLOAT_TYPE); + for (i = 0; i < n; i++) { + l_dnaGetDValue(da_small, i, &val); + key.ftype = val; + if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { + l_dnaAddNumber(dad, val); + l_asetInsert(set2, key); + } + } + + l_asetDestroy(&set1); + l_asetDestroy(&set2); + return dad; +} + + +/*! + * \brief l_asetCreateFromDna() + * + * \param[in] da source dna + * \return set using the doubles in %da as keys + */ +L_ASET * +l_asetCreateFromDna(L_DNA *da) +{ +l_int32 i, n; +l_float64 val; +L_ASET *set; +RB_TYPE key; + + PROCNAME("l_asetCreateFromDna"); + + if (!da) + return (L_ASET *)ERROR_PTR("da not defined", procName, NULL); + + set = l_asetCreate(L_FLOAT_TYPE); + n = l_dnaGetCount(da); + for (i = 0; i < n; i++) { + l_dnaGetDValue(da, i, &val); + key.ftype = val; + l_asetInsert(set, key); + } + + return set; +} + + +/*----------------------------------------------------------------------* + * Miscellaneous operations * + *----------------------------------------------------------------------*/ +/*! + * \brief l_dnaDiffAdjValues() + * + * \param[in] das input l_dna + * \return dad of difference values val[i+1] - val[i], + * or NULL on error + */ +L_DNA * +l_dnaDiffAdjValues(L_DNA *das) +{ +l_int32 i, n, prev, cur; +L_DNA *dad; + + PROCNAME("l_dnaDiffAdjValues"); + + if (!das) + return (L_DNA *)ERROR_PTR("das not defined", procName, NULL); + n = l_dnaGetCount(das); + dad = l_dnaCreate(n - 1); + prev = 0; + for (i = 1; i < n; i++) { + l_dnaGetIValue(das, i, &cur); + l_dnaAddNumber(dad, cur - prev); + prev = cur; + } + return dad; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dnahash.c b/hgdriver/3rdparty/hgOCR/leptonica/dnahash.c new file mode 100644 index 0000000..ecac067 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dnahash.c @@ -0,0 +1,589 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file dnahash.c + *
+ *
+ *      DnaHash creation, destruction
+ *          L_DNAHASH   *l_dnaHashCreate()
+ *          void         l_dnaHashDestroy()
+ *
+ *      DnaHash: Accessors and modifiers                      *
+ *          l_int32      l_dnaHashGetCount()
+ *          l_int32      l_dnaHashGetTotalCount()
+ *          L_DNA       *l_dnaHashGetDna()
+ *          void         l_dnaHashAdd()
+ *
+ *      DnaHash: Operations on Dna
+ *          L_DNAHASH   *l_dnaHashCreateFromDna()
+ *          l_int32      l_dnaRemoveDupsByHash()
+ *          l_int32      l_dnaMakeHistoByHash()
+ *          L_DNA       *l_dnaIntersectionByHash()
+ *          l_int32      l_dnaFindValByHash()
+ *
+ *    (1) The DnaHash is an array of Dna.  It is useful for fast
+ *        storage and lookup for sets and maps.  If the set or map
+ *        is on a Dna itself, the hash is a simple function that
+ *        maps a double to a l_uint64; otherwise the function will
+ *        map a string or a (x,y) point to a l_uint64.  The result of
+ *        the map is the "key", which is then used with the mod
+ *        function to select which Dna array is to be used.  The
+ *        number of arrays in a DnaHash should be a prime number.
+ *        If there are N items, we set up the DnaHash array to have
+ *        approximately N/20 Dna, so the average size of these arrays
+ *        will be about 20 when fully populated.  The number 20 was
+ *        found empirically to be in a broad maximum of efficiency.
+ *    (2) Note that the word "hash" is overloaded.  There are actually
+ *        two hashing steps: the first hashes the object to a l_uint64,
+ *        called the "key", and the second uses the mod function to
+ *        "hash" the "key" to the index of a particular Dna in the
+ *        DnaHash array.
+ *    (3) Insertion and lookup time for DnaHash is O(1).  Hash collisions
+ *        are easily handled (we expect an average of 20 for each key),
+ *        so we can use simple (fast) hash functions: we deal with
+ *        collisions by storing an array for each hash key.
+ *        This can be contrasted with using rbtree for sets and
+ *        maps, where insertion and lookup are O(logN) and hash functions
+ *        are slower because they must be good enough (i.e, random
+ *        enough with arbitrary input) to avoid collisions.
+ *    (4) Hash functions that map points, strings and floats to l_uint64
+ *        are given in utils.c.
+ *    (5) The use of the DnaHash (and RBTree) with strings and
+ *        (x,y) points can be found in string2.c and ptafunc2.c, rsp.
+ *        This file has similar hash set functions, using DnaHash on
+ *        two input Dna, for removing duplicates and finding the
+ *        intersection.  It also uses DnaHash as a hash map to find
+ *        a histogram of counts from an input Dna.
+ *    (6) Comparisons in running time, between DnaHash and RBTree, for
+ *        large sets of strings and points, are given in prog/hashtest.c.
+ *    (7) This is a very simple implementation, that expects that you
+ *        know approximately (i.e., within a factor of 2 or 3) how many
+ *        items are to be stored when you initialize the DnaHash.
+ *        (It would be nice to modify the l_dnaHashAdd() function
+ *        to increase the number of bins when the average occupation
+ *        exceeds 40 or so.)
+ *    (8) Useful rule of thumb for hashing collisions:
+ *        For a random hashing function (say, from strings to l_uint64),
+ *        the probability of a collision increases as N^2 for N much
+ *        less than 2^32.  The quadratic behavior switches over to
+ *        approaching 1.0 around 2^32, which is the square root of 2^64.
+ *        So, for example, if you have 10^7 strings, the probability
+ *        of a single collision using an l_uint64 key is on the order of
+ *            (10^7/10^9)^2 ~ 10^-4.
+ *        For a million strings you don't need to worry about collisons
+ *        (~10-6 probability), and for most applications can use the
+ *        RBTree (sorting) implementation with confidence.
+ * 
+ */ + +#include "allheaders.h" + +/*--------------------------------------------------------------------------* + * Dna hash: Creation and destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaHashCreate() + * + * \param[in] nbuckets the number of buckets in the hash table, + * which should be prime. + * \param[in] initsize initial size of each allocated dna; 0 for default + * \return ptr to new dnahash, or NULL on error + * + *
+ * Notes:
+ *      (1) Actual dna are created only as required by l_dnaHashAdd()
+ * 
+ */ +L_DNAHASH * +l_dnaHashCreate(l_int32 nbuckets, + l_int32 initsize) +{ +L_DNAHASH *dahash; + + PROCNAME("l_dnaHashCreate"); + + if (nbuckets <= 0) + return (L_DNAHASH *)ERROR_PTR("negative hash size", procName, NULL); + dahash = (L_DNAHASH *)LEPT_CALLOC(1, sizeof(L_DNAHASH)); + if ((dahash->dna = (L_DNA **)LEPT_CALLOC(nbuckets, sizeof(L_DNA *))) + == NULL) { + LEPT_FREE(dahash); + return (L_DNAHASH *)ERROR_PTR("dna ptr array not made", procName, NULL); + } + + dahash->nbuckets = nbuckets; + dahash->initsize = initsize; + return dahash; +} + + +/*! + * \brief l_dnaHashDestroy() + * + * \param[in,out] pdahash will be set to null before returning + * \return void + */ +void +l_dnaHashDestroy(L_DNAHASH **pdahash) +{ +L_DNAHASH *dahash; +l_int32 i; + + PROCNAME("l_dnaHashDestroy"); + + if (pdahash == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((dahash = *pdahash) == NULL) + return; + + for (i = 0; i < dahash->nbuckets; i++) + l_dnaDestroy(&dahash->dna[i]); + LEPT_FREE(dahash->dna); + LEPT_FREE(dahash); + *pdahash = NULL; +} + + +/*--------------------------------------------------------------------------* + * Dna hash: Accessors and modifiers * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaHashGetCount() + * + * \param[in] dahash + * \return nbuckets allocated, or 0 on error + */ +l_int32 +l_dnaHashGetCount(L_DNAHASH *dahash) +{ + + PROCNAME("l_dnaHashGetCount"); + + if (!dahash) + return ERROR_INT("dahash not defined", procName, 0); + return dahash->nbuckets; +} + + +/*! + * \brief l_dnaHashGetTotalCount() + * + * \param[in] dahash + * \return n number of numbers in all dna, or 0 on error + */ +l_int32 +l_dnaHashGetTotalCount(L_DNAHASH *dahash) +{ +l_int32 i, n; +L_DNA *da; + + PROCNAME("l_dnaHashGetTotalCount"); + + if (!dahash) + return ERROR_INT("dahash not defined", procName, 0); + + for (i = 0, n = 0; i < dahash->nbuckets; i++) { + da = l_dnaHashGetDna(dahash, i, L_NOCOPY); + if (da) + n += l_dnaGetCount(da); + } + + return n; +} + + +/*! + * \brief l_dnaHashGetDna() + * + * \param[in] dahash + * \param[in] key key to be hashed into a bucket number + * \param[in] copyflag L_NOCOPY, L_COPY, L_CLONE + * \return ptr to dna + */ +L_DNA * +l_dnaHashGetDna(L_DNAHASH *dahash, + l_uint64 key, + l_int32 copyflag) +{ +l_int32 bucket; +L_DNA *da; + + PROCNAME("l_dnaHashGetDna"); + + if (!dahash) + return (L_DNA *)ERROR_PTR("dahash not defined", procName, NULL); + bucket = key % dahash->nbuckets; + da = dahash->dna[bucket]; + if (da) { + if (copyflag == L_NOCOPY) + return da; + else if (copyflag == L_COPY) + return l_dnaCopy(da); + else + return l_dnaClone(da); + } + else + return NULL; +} + + +/*! + * \brief l_dnaHashAdd() + * + * \param[in] dahash + * \param[in] key key to be hashed into a bucket number + * \param[in] value float value to be appended to the specific dna + * \return 0 if OK; 1 on error + */ +l_ok +l_dnaHashAdd(L_DNAHASH *dahash, + l_uint64 key, + l_float64 value) +{ +l_int32 bucket; +L_DNA *da; + + PROCNAME("l_dnaHashAdd"); + + if (!dahash) + return ERROR_INT("dahash not defined", procName, 1); + bucket = key % dahash->nbuckets; + da = dahash->dna[bucket]; + if (!da) { + if ((da = l_dnaCreate(dahash->initsize)) == NULL) + return ERROR_INT("da not made", procName, 1); + dahash->dna[bucket] = da; + } + l_dnaAddNumber(da, value); + return 0; +} + + +/*--------------------------------------------------------------------------* + * DnaHash: Operations on Dna * + *--------------------------------------------------------------------------*/ +/*! + * \brief l_dnaHashCreateFromDna() + * + * \param[in] da + * \return dahash if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The values stored in the %dahash are indices into %da;
+ *          %dahash has no use without %da.
+ * 
+ */ +L_DNAHASH * +l_dnaHashCreateFromDna(L_DNA *da) +{ +l_int32 i, n; +l_uint32 nsize; +l_uint64 key; +l_float64 val; +L_DNAHASH *dahash; + + PROCNAME("l_dnaHashCreateFromDna"); + + if (!da) + return (L_DNAHASH *)ERROR_PTR("da not defined", procName, NULL); + + n = l_dnaGetCount(da); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ + + dahash = l_dnaHashCreate(nsize, 8); + for (i = 0; i < n; i++) { + l_dnaGetDValue(da, i, &val); + l_hashFloat64ToUint64(nsize, val, &key); + l_dnaHashAdd(dahash, key, (l_float64)i); + } + + return dahash; +} + + +/*! + * \brief l_dnaRemoveDupsByHash() + * + * \param[in] das + * \param[out] pdad hash set + * \param[out] pdahash [optional] dnahash used for lookup + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Generates a dna with unique values.
+ *      (2) The dnahash is built up with dad to assure uniqueness.
+ *          It can be used to find if an element is in the set:
+ *              l_dnaFindValByHash(dad, dahash, val, &index)
+ * 
+ */ +l_ok +l_dnaRemoveDupsByHash(L_DNA *das, + L_DNA **pdad, + L_DNAHASH **pdahash) +{ +l_int32 i, n, index, items; +l_uint32 nsize; +l_uint64 key; +l_float64 val; +L_DNA *dad; +L_DNAHASH *dahash; + + PROCNAME("l_dnaRemoveDupsByHash"); + + if (pdahash) *pdahash = NULL; + if (!pdad) + return ERROR_INT("&dad not defined", procName, 1); + *pdad = NULL; + if (!das) + return ERROR_INT("das not defined", procName, 1); + + n = l_dnaGetCount(das); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ + dahash = l_dnaHashCreate(nsize, 8); + dad = l_dnaCreate(n); + *pdad = dad; + for (i = 0, items = 0; i < n; i++) { + l_dnaGetDValue(das, i, &val); + l_dnaFindValByHash(dad, dahash, val, &index); + if (index < 0) { /* not found */ + l_hashFloat64ToUint64(nsize, val, &key); + l_dnaHashAdd(dahash, key, (l_float64)items); + l_dnaAddNumber(dad, val); + items++; + } + } + + if (pdahash) + *pdahash = dahash; + else + l_dnaHashDestroy(&dahash); + return 0; +} + + +/*! + * \brief l_dnaMakeHistoByHash() + * + * \param[in] das + * \param[out] pdahash hash map: val --> index + * \param[out] pdav array of values: index --> val + * \param[out] pdac histo array of counts: index --> count + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Generates and returns a dna of occurrences (histogram),
+ *          an aligned dna of values, and an associated hashmap.
+ *          The hashmap takes %dav and a value, and points into the
+ *          histogram in %dac.
+ *      (2) The dna of values, %dav, is aligned with the histogram %dac,
+ *          and is needed for fast lookup.  It is a hash set, because
+ *          the values are unique.
+ *      (3) Lookup is simple:
+ *              l_dnaFindValByHash(dav, dahash, val, &index);
+ *              if (index >= 0)
+ *                  l_dnaGetIValue(dac, index, &icount);
+ *              else
+ *                  icount = 0;
+ * 
+ */ +l_ok +l_dnaMakeHistoByHash(L_DNA *das, + L_DNAHASH **pdahash, + L_DNA **pdav, + L_DNA **pdac) +{ +l_int32 i, n, nitems, index, count; +l_uint32 nsize; +l_uint64 key; +l_float64 val; +L_DNA *dac, *dav; +L_DNAHASH *dahash; + + PROCNAME("l_dnaMakeHistoByHash"); + + if (pdahash) *pdahash = NULL; + if (pdac) *pdac = NULL; + if (pdav) *pdav = NULL; + if (!pdahash || !pdac || !pdav) + return ERROR_INT("&dahash, &dac, &dav not all defined", procName, 1); + if (!das) + return ERROR_INT("das not defined", procName, 1); + if ((n = l_dnaGetCount(das)) == 0) + return ERROR_INT("no data in das", procName, 1); + + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ + dahash = l_dnaHashCreate(nsize, 8); + dac = l_dnaCreate(n); /* histogram */ + dav = l_dnaCreate(n); /* the values */ + for (i = 0, nitems = 0; i < n; i++) { + l_dnaGetDValue(das, i, &val); + /* Is this value already stored in dav? */ + l_dnaFindValByHash(dav, dahash, val, &index); + if (index >= 0) { /* found */ + l_dnaGetIValue(dac, (l_float64)index, &count); + l_dnaSetValue(dac, (l_float64)index, count + 1); + } else { /* not found */ + l_hashFloat64ToUint64(nsize, val, &key); + l_dnaHashAdd(dahash, key, (l_float64)nitems); + l_dnaAddNumber(dav, val); + l_dnaAddNumber(dac, 1); + nitems++; + } + } + + *pdahash = dahash; + *pdac = dac; + *pdav = dav; + return 0; +} + + +/*! + * \brief l_dnaIntersectionByHash() + * + * \param[in] da1, da2 + * \return dad intersection of the number arrays, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses the same method for building the intersection set
+ *          as ptaIntersectionByHash() and sarrayIntersectionByHash().
+ * 
+ */ +L_DNA * +l_dnaIntersectionByHash(L_DNA *da1, + L_DNA *da2) +{ +l_int32 n1, n2, nsmall, nbuckets, i, index1, index2; +l_uint32 nsize2; +l_uint64 key; +l_float64 val; +L_DNAHASH *dahash1, *dahash2; +L_DNA *da_small, *da_big, *dad; + + PROCNAME("l_dnaIntersectionByHash"); + + if (!da1) + return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); + if (!da2) + return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); + + /* Put the elements of the biggest array into a dnahash */ + n1 = l_dnaGetCount(da1); + n2 = l_dnaGetCount(da2); + da_small = (n1 < n2) ? da1 : da2; /* do not destroy da_small */ + da_big = (n1 < n2) ? da2 : da1; /* do not destroy da_big */ + dahash1 = l_dnaHashCreateFromDna(da_big); + + /* Build up the intersection of numbers. Add to %dad + * if the number is in da_big (using dahash1) but hasn't + * yet been seen in the traversal of da_small (using dahash2). */ + dad = l_dnaCreate(0); + nsmall = l_dnaGetCount(da_small); + findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ + dahash2 = l_dnaHashCreate(nsize2, 0); + nbuckets = l_dnaHashGetCount(dahash2); + for (i = 0; i < nsmall; i++) { + l_dnaGetDValue(da_small, i, &val); + l_dnaFindValByHash(da_big, dahash1, val, &index1); + if (index1 >= 0) { /* found */ + l_dnaFindValByHash(da_small, dahash2, val, &index2); + if (index2 == -1) { /* not found */ + l_dnaAddNumber(dad, val); + l_hashFloat64ToUint64(nbuckets, val, &key); + l_dnaHashAdd(dahash2, key, (l_float64)i); + } + } + } + + l_dnaHashDestroy(&dahash1); + l_dnaHashDestroy(&dahash2); + return dad; +} + + +/*! + * \brief l_dnaFindValByHash() + * + * \param[in] da + * \param[in] dahash containing indices into %da + * \param[in] val searching for this number in %da + * \param[out] pindex index into da if found; -1 otherwise + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Algo: hash %val into a key; hash the key to get the dna
+ *                in %dahash (that holds indices into %da); traverse
+ *                the dna of indices looking for %val in %da.
+ * 
+ */ +l_ok +l_dnaFindValByHash(L_DNA *da, + L_DNAHASH *dahash, + l_float64 val, + l_int32 *pindex) +{ +l_int32 i, nbuckets, nvals, indexval; +l_float64 vali; +l_uint64 key; +L_DNA *da1; + + PROCNAME("l_dnaFindValByHash"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = -1; + if (!da) + return ERROR_INT("da not defined", procName, 1); + if (!dahash) + return ERROR_INT("dahash not defined", procName, 1); + + nbuckets = l_dnaHashGetCount(dahash); + l_hashFloat64ToUint64(nbuckets, val, &key); + da1 = l_dnaHashGetDna(dahash, key, L_NOCOPY); + if (!da1) return 0; + + /* Run through da1, looking for this %val */ + nvals = l_dnaGetCount(da1); + for (i = 0; i < nvals; i++) { + l_dnaGetIValue(da1, i, &indexval); + l_dnaGetDValue(da, indexval, &vali); + if (val == vali) { + *pindex = indexval; + return 0; + } + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dwacomb.2.c b/hgdriver/3rdparty/hgOCR/leptonica/dwacomb.2.c new file mode 100644 index 0000000..438e3f0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dwacomb.2.c @@ -0,0 +1,295 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Top-level fast binary morphology with auto-generated sels + * + * PIX *pixMorphDwa_2() + * PIX *pixFMorphopGen_2() + */ + +#include +#include "allheaders.h" + +PIX *pixMorphDwa_2(PIX *pixd, PIX *pixs, l_int32 operation, char *selname); +PIX *pixFMorphopGen_2(PIX *pixd, PIX *pixs, l_int32 operation, char *selname); +l_int32 fmorphopgen_low_2(l_uint32 *datad, l_int32 w, + l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, + l_int32 index); + +static l_int32 NUM_SELS_GENERATED = 76; +static char SEL_NAMES[][80] = { + "sel_comb_4h", + "sel_comb_4v", + "sel_comb_5h", + "sel_comb_5v", + "sel_comb_6h", + "sel_comb_6v", + "sel_comb_7h", + "sel_comb_7v", + "sel_comb_8h", + "sel_comb_8v", + "sel_comb_9h", + "sel_comb_9v", + "sel_comb_10h", + "sel_comb_10v", + "sel_comb_12h", + "sel_comb_12v", + "sel_comb_14h", + "sel_comb_14v", + "sel_comb_15h", + "sel_comb_15v", + "sel_comb_16h", + "sel_comb_16v", + "sel_comb_18h", + "sel_comb_18v", + "sel_comb_20h", + "sel_comb_20v", + "sel_comb_21h", + "sel_comb_21v", + "sel_comb_22h", + "sel_comb_22v", + "sel_comb_24h", + "sel_comb_24v", + "sel_comb_25h", + "sel_comb_25v", + "sel_comb_27h", + "sel_comb_27v", + "sel_comb_28h", + "sel_comb_28v", + "sel_comb_30h", + "sel_comb_30v", + "sel_comb_32h", + "sel_comb_32v", + "sel_comb_33h", + "sel_comb_33v", + "sel_comb_35h", + "sel_comb_35v", + "sel_comb_36h", + "sel_comb_36v", + "sel_comb_39h", + "sel_comb_39v", + "sel_comb_40h", + "sel_comb_40v", + "sel_comb_42h", + "sel_comb_42v", + "sel_comb_44h", + "sel_comb_44v", + "sel_comb_45h", + "sel_comb_45v", + "sel_comb_48h", + "sel_comb_48v", + "sel_comb_49h", + "sel_comb_49v", + "sel_comb_50h", + "sel_comb_50v", + "sel_comb_52h", + "sel_comb_52v", + "sel_comb_54h", + "sel_comb_54v", + "sel_comb_55h", + "sel_comb_55v", + "sel_comb_56h", + "sel_comb_56v", + "sel_comb_60h", + "sel_comb_60v", + "sel_comb_63h", + "sel_comb_63v"}; + +/*! + * \brief pixMorphDwa_2() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, + * L_MORPH_OPEN, L_MORPH_CLOSE + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This simply adds a border, calls the appropriate
+ *          pixFMorphopGen_*(), and removes the border.
+ *          See the notes for that function.
+ *      (2) The size of the border depends on the operation
+ *          and the boundary conditions.
+ * 
+ */ +PIX * +pixMorphDwa_2(PIX *pixd, + PIX *pixs, + l_int32 operation, + char *selname) +{ +l_int32 bordercolor, bordersize; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixMorphDwa_2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + /* Set the border size */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + bordersize = 32; + if (bordercolor == 0 && operation == L_MORPH_CLOSE) + bordersize += 32; + + pixt1 = pixAddBorder(pixs, bordersize, 0); + pixt2 = pixFMorphopGen_2(NULL, pixt1, operation, selname); + pixt3 = pixRemoveBorder(pixt2, bordersize); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixCopy(pixd, pixt3); + pixDestroy(&pixt3); + return pixd; +} + + +/*! + * \brief pixFMorphopGen_2() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, + * L_MORPH_OPEN, L_MORPH_CLOSE + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This is a dwa operation, and the Sels must be limited in
+ *          size to not more than 31 pixels about the origin.
+ *      (2) A border of appropriate size (32 pixels, or 64 pixels
+ *          for safe closing with asymmetric b.c.) must be added before
+ *          this function is called.
+ *      (3) This handles all required setting of the border pixels
+ *          before erosion and dilation.
+ *      (4) The closing operation is safe; no pixels can be removed
+ *          near the boundary.
+ * 
+ */ +PIX * +pixFMorphopGen_2(PIX *pixd, + PIX *pixs, + l_int32 operation, + char *selname) +{ +l_int32 i, index, found, w, h, wpls, wpld, bordercolor, erodeop, borderop; +l_uint32 *datad, *datas, *datat; +PIX *pixt; + + PROCNAME("pixFMorphopGen_2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + /* Get boundary colors to use */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + if (bordercolor == 1) + erodeop = PIX_SET; + else + erodeop = PIX_CLR; + + found = FALSE; + for (i = 0; i < NUM_SELS_GENERATED; i++) { + if (strcmp(selname, SEL_NAMES[i]) == 0) { + found = TRUE; + index = 2 * i; + break; + } + } + if (found == FALSE) + return (PIX *)ERROR_PTR("sel index not found", procName, pixd); + + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + else /* for in-place or pre-allocated */ + pixResizeImageData(pixd, pixs); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* The images must be surrounded, in advance, with a border of + * size 32 pixels (or 64, for closing), that we'll read from. + * Fabricate a "proper" image as the subimage within the 32 + * pixel border, having the following parameters: */ + w = pixGetWidth(pixs) - 64; + h = pixGetHeight(pixs) - 64; + datas = pixGetData(pixs) + 32 * wpls + 1; + datad = pixGetData(pixd) + 32 * wpld + 1; + + if (operation == L_MORPH_DILATE || operation == L_MORPH_ERODE) { + borderop = PIX_CLR; + if (operation == L_MORPH_ERODE) { + borderop = erodeop; + index++; + } + if (pixd == pixs) { /* in-place; generate a temp image */ + if ((pixt = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + 32 * wpls + 1; + pixSetOrClearBorder(pixt, 32, 32, 32, 32, borderop); + fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index); + pixDestroy(&pixt); + } + else { /* not in-place */ + pixSetOrClearBorder(pixs, 32, 32, 32, 32, borderop); + fmorphopgen_low_2(datad, w, h, wpld, datas, wpls, index); + } + } + else { /* opening or closing; generate a temp image */ + if ((pixt = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + 32 * wpls + 1; + if (operation == L_MORPH_OPEN) { + pixSetOrClearBorder(pixs, 32, 32, 32, 32, erodeop); + fmorphopgen_low_2(datat, w, h, wpls, datas, wpls, index+1); + pixSetOrClearBorder(pixt, 32, 32, 32, 32, PIX_CLR); + fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index); + } + else { /* closing */ + pixSetOrClearBorder(pixs, 32, 32, 32, 32, PIX_CLR); + fmorphopgen_low_2(datat, w, h, wpls, datas, wpls, index); + pixSetOrClearBorder(pixt, 32, 32, 32, 32, erodeop); + fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index+1); + } + pixDestroy(&pixt); + } + + return pixd; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/dwacomblow.2.c b/hgdriver/3rdparty/hgOCR/leptonica/dwacomblow.2.c new file mode 100644 index 0000000..2831b9b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/dwacomblow.2.c @@ -0,0 +1,4966 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Low-level fast binary morphology with auto-generated sels + * + * Dispatcher: + * l_int32 fmorphopgen_low_2() + * + * Static Low-level: + * void fdilate_2_*() + * void ferode_2_*() + */ + +#include "allheaders.h" + +static void fdilate_2_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_58(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_58(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_59(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_59(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_60(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_60(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_61(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_61(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_62(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_62(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_63(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_63(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_64(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_64(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_65(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_65(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_66(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_66(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_67(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_67(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_68(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_68(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_69(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_69(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_70(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_70(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_71(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_71(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_72(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_72(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_73(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_73(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_74(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_74(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_2_75(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_2_75(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); + + +/*---------------------------------------------------------------------* + * Fast morph dispatcher * + *---------------------------------------------------------------------*/ +/*! + * fmorphopgen_low_2() + * + * a dispatcher to appropriate low-level code + */ +l_int32 +fmorphopgen_low_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 index) +{ + + switch (index) + { + case 0: + fdilate_2_0(datad, w, h, wpld, datas, wpls); + break; + case 1: + ferode_2_0(datad, w, h, wpld, datas, wpls); + break; + case 2: + fdilate_2_1(datad, w, h, wpld, datas, wpls); + break; + case 3: + ferode_2_1(datad, w, h, wpld, datas, wpls); + break; + case 4: + fdilate_2_2(datad, w, h, wpld, datas, wpls); + break; + case 5: + ferode_2_2(datad, w, h, wpld, datas, wpls); + break; + case 6: + fdilate_2_3(datad, w, h, wpld, datas, wpls); + break; + case 7: + ferode_2_3(datad, w, h, wpld, datas, wpls); + break; + case 8: + fdilate_2_4(datad, w, h, wpld, datas, wpls); + break; + case 9: + ferode_2_4(datad, w, h, wpld, datas, wpls); + break; + case 10: + fdilate_2_5(datad, w, h, wpld, datas, wpls); + break; + case 11: + ferode_2_5(datad, w, h, wpld, datas, wpls); + break; + case 12: + fdilate_2_6(datad, w, h, wpld, datas, wpls); + break; + case 13: + ferode_2_6(datad, w, h, wpld, datas, wpls); + break; + case 14: + fdilate_2_7(datad, w, h, wpld, datas, wpls); + break; + case 15: + ferode_2_7(datad, w, h, wpld, datas, wpls); + break; + case 16: + fdilate_2_8(datad, w, h, wpld, datas, wpls); + break; + case 17: + ferode_2_8(datad, w, h, wpld, datas, wpls); + break; + case 18: + fdilate_2_9(datad, w, h, wpld, datas, wpls); + break; + case 19: + ferode_2_9(datad, w, h, wpld, datas, wpls); + break; + case 20: + fdilate_2_10(datad, w, h, wpld, datas, wpls); + break; + case 21: + ferode_2_10(datad, w, h, wpld, datas, wpls); + break; + case 22: + fdilate_2_11(datad, w, h, wpld, datas, wpls); + break; + case 23: + ferode_2_11(datad, w, h, wpld, datas, wpls); + break; + case 24: + fdilate_2_12(datad, w, h, wpld, datas, wpls); + break; + case 25: + ferode_2_12(datad, w, h, wpld, datas, wpls); + break; + case 26: + fdilate_2_13(datad, w, h, wpld, datas, wpls); + break; + case 27: + ferode_2_13(datad, w, h, wpld, datas, wpls); + break; + case 28: + fdilate_2_14(datad, w, h, wpld, datas, wpls); + break; + case 29: + ferode_2_14(datad, w, h, wpld, datas, wpls); + break; + case 30: + fdilate_2_15(datad, w, h, wpld, datas, wpls); + break; + case 31: + ferode_2_15(datad, w, h, wpld, datas, wpls); + break; + case 32: + fdilate_2_16(datad, w, h, wpld, datas, wpls); + break; + case 33: + ferode_2_16(datad, w, h, wpld, datas, wpls); + break; + case 34: + fdilate_2_17(datad, w, h, wpld, datas, wpls); + break; + case 35: + ferode_2_17(datad, w, h, wpld, datas, wpls); + break; + case 36: + fdilate_2_18(datad, w, h, wpld, datas, wpls); + break; + case 37: + ferode_2_18(datad, w, h, wpld, datas, wpls); + break; + case 38: + fdilate_2_19(datad, w, h, wpld, datas, wpls); + break; + case 39: + ferode_2_19(datad, w, h, wpld, datas, wpls); + break; + case 40: + fdilate_2_20(datad, w, h, wpld, datas, wpls); + break; + case 41: + ferode_2_20(datad, w, h, wpld, datas, wpls); + break; + case 42: + fdilate_2_21(datad, w, h, wpld, datas, wpls); + break; + case 43: + ferode_2_21(datad, w, h, wpld, datas, wpls); + break; + case 44: + fdilate_2_22(datad, w, h, wpld, datas, wpls); + break; + case 45: + ferode_2_22(datad, w, h, wpld, datas, wpls); + break; + case 46: + fdilate_2_23(datad, w, h, wpld, datas, wpls); + break; + case 47: + ferode_2_23(datad, w, h, wpld, datas, wpls); + break; + case 48: + fdilate_2_24(datad, w, h, wpld, datas, wpls); + break; + case 49: + ferode_2_24(datad, w, h, wpld, datas, wpls); + break; + case 50: + fdilate_2_25(datad, w, h, wpld, datas, wpls); + break; + case 51: + ferode_2_25(datad, w, h, wpld, datas, wpls); + break; + case 52: + fdilate_2_26(datad, w, h, wpld, datas, wpls); + break; + case 53: + ferode_2_26(datad, w, h, wpld, datas, wpls); + break; + case 54: + fdilate_2_27(datad, w, h, wpld, datas, wpls); + break; + case 55: + ferode_2_27(datad, w, h, wpld, datas, wpls); + break; + case 56: + fdilate_2_28(datad, w, h, wpld, datas, wpls); + break; + case 57: + ferode_2_28(datad, w, h, wpld, datas, wpls); + break; + case 58: + fdilate_2_29(datad, w, h, wpld, datas, wpls); + break; + case 59: + ferode_2_29(datad, w, h, wpld, datas, wpls); + break; + case 60: + fdilate_2_30(datad, w, h, wpld, datas, wpls); + break; + case 61: + ferode_2_30(datad, w, h, wpld, datas, wpls); + break; + case 62: + fdilate_2_31(datad, w, h, wpld, datas, wpls); + break; + case 63: + ferode_2_31(datad, w, h, wpld, datas, wpls); + break; + case 64: + fdilate_2_32(datad, w, h, wpld, datas, wpls); + break; + case 65: + ferode_2_32(datad, w, h, wpld, datas, wpls); + break; + case 66: + fdilate_2_33(datad, w, h, wpld, datas, wpls); + break; + case 67: + ferode_2_33(datad, w, h, wpld, datas, wpls); + break; + case 68: + fdilate_2_34(datad, w, h, wpld, datas, wpls); + break; + case 69: + ferode_2_34(datad, w, h, wpld, datas, wpls); + break; + case 70: + fdilate_2_35(datad, w, h, wpld, datas, wpls); + break; + case 71: + ferode_2_35(datad, w, h, wpld, datas, wpls); + break; + case 72: + fdilate_2_36(datad, w, h, wpld, datas, wpls); + break; + case 73: + ferode_2_36(datad, w, h, wpld, datas, wpls); + break; + case 74: + fdilate_2_37(datad, w, h, wpld, datas, wpls); + break; + case 75: + ferode_2_37(datad, w, h, wpld, datas, wpls); + break; + case 76: + fdilate_2_38(datad, w, h, wpld, datas, wpls); + break; + case 77: + ferode_2_38(datad, w, h, wpld, datas, wpls); + break; + case 78: + fdilate_2_39(datad, w, h, wpld, datas, wpls); + break; + case 79: + ferode_2_39(datad, w, h, wpld, datas, wpls); + break; + case 80: + fdilate_2_40(datad, w, h, wpld, datas, wpls); + break; + case 81: + ferode_2_40(datad, w, h, wpld, datas, wpls); + break; + case 82: + fdilate_2_41(datad, w, h, wpld, datas, wpls); + break; + case 83: + ferode_2_41(datad, w, h, wpld, datas, wpls); + break; + case 84: + fdilate_2_42(datad, w, h, wpld, datas, wpls); + break; + case 85: + ferode_2_42(datad, w, h, wpld, datas, wpls); + break; + case 86: + fdilate_2_43(datad, w, h, wpld, datas, wpls); + break; + case 87: + ferode_2_43(datad, w, h, wpld, datas, wpls); + break; + case 88: + fdilate_2_44(datad, w, h, wpld, datas, wpls); + break; + case 89: + ferode_2_44(datad, w, h, wpld, datas, wpls); + break; + case 90: + fdilate_2_45(datad, w, h, wpld, datas, wpls); + break; + case 91: + ferode_2_45(datad, w, h, wpld, datas, wpls); + break; + case 92: + fdilate_2_46(datad, w, h, wpld, datas, wpls); + break; + case 93: + ferode_2_46(datad, w, h, wpld, datas, wpls); + break; + case 94: + fdilate_2_47(datad, w, h, wpld, datas, wpls); + break; + case 95: + ferode_2_47(datad, w, h, wpld, datas, wpls); + break; + case 96: + fdilate_2_48(datad, w, h, wpld, datas, wpls); + break; + case 97: + ferode_2_48(datad, w, h, wpld, datas, wpls); + break; + case 98: + fdilate_2_49(datad, w, h, wpld, datas, wpls); + break; + case 99: + ferode_2_49(datad, w, h, wpld, datas, wpls); + break; + case 100: + fdilate_2_50(datad, w, h, wpld, datas, wpls); + break; + case 101: + ferode_2_50(datad, w, h, wpld, datas, wpls); + break; + case 102: + fdilate_2_51(datad, w, h, wpld, datas, wpls); + break; + case 103: + ferode_2_51(datad, w, h, wpld, datas, wpls); + break; + case 104: + fdilate_2_52(datad, w, h, wpld, datas, wpls); + break; + case 105: + ferode_2_52(datad, w, h, wpld, datas, wpls); + break; + case 106: + fdilate_2_53(datad, w, h, wpld, datas, wpls); + break; + case 107: + ferode_2_53(datad, w, h, wpld, datas, wpls); + break; + case 108: + fdilate_2_54(datad, w, h, wpld, datas, wpls); + break; + case 109: + ferode_2_54(datad, w, h, wpld, datas, wpls); + break; + case 110: + fdilate_2_55(datad, w, h, wpld, datas, wpls); + break; + case 111: + ferode_2_55(datad, w, h, wpld, datas, wpls); + break; + case 112: + fdilate_2_56(datad, w, h, wpld, datas, wpls); + break; + case 113: + ferode_2_56(datad, w, h, wpld, datas, wpls); + break; + case 114: + fdilate_2_57(datad, w, h, wpld, datas, wpls); + break; + case 115: + ferode_2_57(datad, w, h, wpld, datas, wpls); + break; + case 116: + fdilate_2_58(datad, w, h, wpld, datas, wpls); + break; + case 117: + ferode_2_58(datad, w, h, wpld, datas, wpls); + break; + case 118: + fdilate_2_59(datad, w, h, wpld, datas, wpls); + break; + case 119: + ferode_2_59(datad, w, h, wpld, datas, wpls); + break; + case 120: + fdilate_2_60(datad, w, h, wpld, datas, wpls); + break; + case 121: + ferode_2_60(datad, w, h, wpld, datas, wpls); + break; + case 122: + fdilate_2_61(datad, w, h, wpld, datas, wpls); + break; + case 123: + ferode_2_61(datad, w, h, wpld, datas, wpls); + break; + case 124: + fdilate_2_62(datad, w, h, wpld, datas, wpls); + break; + case 125: + ferode_2_62(datad, w, h, wpld, datas, wpls); + break; + case 126: + fdilate_2_63(datad, w, h, wpld, datas, wpls); + break; + case 127: + ferode_2_63(datad, w, h, wpld, datas, wpls); + break; + case 128: + fdilate_2_64(datad, w, h, wpld, datas, wpls); + break; + case 129: + ferode_2_64(datad, w, h, wpld, datas, wpls); + break; + case 130: + fdilate_2_65(datad, w, h, wpld, datas, wpls); + break; + case 131: + ferode_2_65(datad, w, h, wpld, datas, wpls); + break; + case 132: + fdilate_2_66(datad, w, h, wpld, datas, wpls); + break; + case 133: + ferode_2_66(datad, w, h, wpld, datas, wpls); + break; + case 134: + fdilate_2_67(datad, w, h, wpld, datas, wpls); + break; + case 135: + ferode_2_67(datad, w, h, wpld, datas, wpls); + break; + case 136: + fdilate_2_68(datad, w, h, wpld, datas, wpls); + break; + case 137: + ferode_2_68(datad, w, h, wpld, datas, wpls); + break; + case 138: + fdilate_2_69(datad, w, h, wpld, datas, wpls); + break; + case 139: + ferode_2_69(datad, w, h, wpld, datas, wpls); + break; + case 140: + fdilate_2_70(datad, w, h, wpld, datas, wpls); + break; + case 141: + ferode_2_70(datad, w, h, wpld, datas, wpls); + break; + case 142: + fdilate_2_71(datad, w, h, wpld, datas, wpls); + break; + case 143: + ferode_2_71(datad, w, h, wpld, datas, wpls); + break; + case 144: + fdilate_2_72(datad, w, h, wpld, datas, wpls); + break; + case 145: + ferode_2_72(datad, w, h, wpld, datas, wpls); + break; + case 146: + fdilate_2_73(datad, w, h, wpld, datas, wpls); + break; + case 147: + ferode_2_73(datad, w, h, wpld, datas, wpls); + break; + case 148: + fdilate_2_74(datad, w, h, wpld, datas, wpls); + break; + case 149: + ferode_2_74(datad, w, h, wpld, datas, wpls); + break; + case 150: + fdilate_2_75(datad, w, h, wpld, datas, wpls); + break; + case 151: + ferode_2_75(datad, w, h, wpld, datas, wpls); + break; + } + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Low-level auto-generated static routines * + *--------------------------------------------------------------------------*/ +/* + * N.B. In all the low-level routines, the part of the image + * that is accessed has been clipped by 32 pixels on + * all four sides. This is done in the higher level + * code by redefining w and h smaller and by moving the + * start-of-image pointers up to the beginning of this + * interior rectangle. + */ +static void +fdilate_2_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)); + } + } +} + +static void +ferode_2_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)); + } + } +} + +static void +fdilate_2_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls)) | + (*(sptr - wpls)); + } + } +} + +static void +ferode_2_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls)) & + (*(sptr + wpls)); + } + } +} + +static void +fdilate_2_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +ferode_2_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +fdilate_2_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +ferode_2_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +fdilate_2_4(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)); + } + } +} + +static void +ferode_2_4(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)); + } + } +} + +static void +fdilate_2_5(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls2)) | + (*(sptr - wpls)); + } + } +} + +static void +ferode_2_5(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls2)) & + (*(sptr + wpls)); + } + } +} + +static void +fdilate_2_6(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +ferode_2_6(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +fdilate_2_7(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +ferode_2_7(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr); + } + } +} + +static void +fdilate_2_8(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)); + } + } +} + +static void +ferode_2_8(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)); + } + } +} + +static void +fdilate_2_9(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls2)) | + (*(sptr - wpls2)); + } + } +} + +static void +ferode_2_9(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls2)) & + (*(sptr + wpls2)); + } + } +} + +static void +fdilate_2_10(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + (*sptr) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)); + } + } +} + +static void +ferode_2_10(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + (*sptr) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)); + } + } +} + +static void +fdilate_2_11(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; + + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls3)) | + (*sptr) | + (*(sptr - wpls3)); + } + } +} + +static void +ferode_2_11(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; + + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls3)) & + (*sptr) & + (*(sptr + wpls3)); + } + } +} + +static void +fdilate_2_12(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)); + } + } +} + +static void +ferode_2_12(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)); + } + } +} + +static void +fdilate_2_13(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls3)) | + (*(sptr - wpls2)); + } + } +} + +static void +ferode_2_13(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls3)) & + (*(sptr + wpls2)); + } + } +} + +static void +fdilate_2_14(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + (*sptr) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)); + } + } +} + +static void +ferode_2_14(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + (*sptr) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)); + } + } +} + +static void +fdilate_2_15(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; + + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls4)) | + (*sptr) | + (*(sptr - wpls4)); + } + } +} + +static void +ferode_2_15(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; + + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls4)) & + (*sptr) & + (*(sptr + wpls4)); + } + } +} + +static void +fdilate_2_16(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)); + } + } +} + +static void +ferode_2_16(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)); + } + } +} + +static void +fdilate_2_17(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls4)) | + (*(sptr - wpls3)); + } + } +} + +static void +ferode_2_17(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls4)) & + (*(sptr + wpls3)); + } + } +} + +static void +fdilate_2_18(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + (*sptr) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)); + } + } +} + +static void +ferode_2_18(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + (*sptr) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)); + } + } +} + +static void +fdilate_2_19(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; + + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls5)) | + (*sptr) | + (*(sptr - wpls5)); + } + } +} + +static void +ferode_2_19(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; + + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls5)) & + (*sptr) & + (*(sptr + wpls5)); + } + } +} + +static void +fdilate_2_20(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)); + } + } +} + +static void +ferode_2_20(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)); + } + } +} + +static void +fdilate_2_21(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls6; + + wpls2 = 2 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls6)) | + (*(sptr + wpls2)) | + (*(sptr - wpls2)) | + (*(sptr - wpls6)); + } + } +} + +static void +ferode_2_21(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls6; + + wpls2 = 2 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls6)) & + (*(sptr - wpls2)) & + (*(sptr + wpls2)) & + (*(sptr + wpls6)); + } + } +} + +static void +fdilate_2_22(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + (*sptr) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)); + } + } +} + +static void +ferode_2_22(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + (*sptr) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)); + } + } +} + +static void +fdilate_2_23(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; + + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls6)) | + (*sptr) | + (*(sptr - wpls6)); + } + } +} + +static void +ferode_2_23(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; + + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls6)) & + (*sptr) & + (*(sptr + wpls6)); + } + } +} + +static void +fdilate_2_24(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)); + } + } +} + +static void +ferode_2_24(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)); + } + } +} + +static void +fdilate_2_25(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls3; +l_int32 wpls7; +l_int32 wpls8; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls8)) | + (*(sptr + wpls3)) | + (*(sptr - wpls2)) | + (*(sptr - wpls7)); + } + } +} + +static void +ferode_2_25(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; +l_int32 wpls3; +l_int32 wpls7; +l_int32 wpls8; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls8)) & + (*(sptr - wpls3)) & + (*(sptr + wpls2)) & + (*(sptr + wpls7)); + } + } +} + +static void +fdilate_2_26(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + (*sptr) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)); + } + } +} + +static void +ferode_2_26(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + (*sptr) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)); + } + } +} + +static void +fdilate_2_27(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; + + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls7)) | + (*sptr) | + (*(sptr - wpls7)); + } + } +} + +static void +ferode_2_27(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; + + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls7)) & + (*sptr) & + (*(sptr + wpls7)); + } + } +} + +static void +fdilate_2_28(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)); + } + } +} + +static void +ferode_2_28(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)); + } + } +} + +static void +fdilate_2_29(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls6; + + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls6)) | + (*(sptr - wpls5)); + } + } +} + +static void +ferode_2_29(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls6; + + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls6)) & + (*(sptr + wpls5)); + } + } +} + +static void +fdilate_2_30(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)); + } + } +} + +static void +ferode_2_30(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)); + } + } +} + +static void +fdilate_2_31(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls9; + + wpls3 = 3 * wpls; + wpls9 = 9 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls9)) | + (*(sptr + wpls3)) | + (*(sptr - wpls3)) | + (*(sptr - wpls9)); + } + } +} + +static void +ferode_2_31(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls9; + + wpls3 = 3 * wpls; + wpls9 = 9 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls9)) & + (*(sptr - wpls3)) & + (*(sptr + wpls3)) & + (*(sptr + wpls9)); + } + } +} + +static void +fdilate_2_32(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + (*sptr) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)); + } + } +} + +static void +ferode_2_32(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + (*sptr) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)); + } + } +} + +static void +fdilate_2_33(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls10; + + wpls5 = 5 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls10)) | + (*(sptr + wpls5)) | + (*sptr) | + (*(sptr - wpls5)) | + (*(sptr - wpls10)); + } + } +} + +static void +ferode_2_33(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls10; + + wpls5 = 5 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls10)) & + (*(sptr - wpls5)) & + (*sptr) & + (*(sptr + wpls5)) & + (*(sptr + wpls10)); + } + } +} + +static void +fdilate_2_34(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + (*sptr) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)); + } + } +} + +static void +ferode_2_34(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + (*sptr) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)); + } + } +} + +static void +fdilate_2_35(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; + + wpls9 = 9 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls9)) | + (*sptr) | + (*(sptr - wpls9)); + } + } +} + +static void +ferode_2_35(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; + + wpls9 = 9 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls9)) & + (*sptr) & + (*(sptr + wpls9)); + } + } +} + +static void +fdilate_2_36(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)); + } + } +} + +static void +ferode_2_36(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)); + } + } +} + +static void +fdilate_2_37(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; +l_int32 wpls10; +l_int32 wpls11; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls11)) | + (*(sptr + wpls4)) | + (*(sptr - wpls3)) | + (*(sptr - wpls10)); + } + } +} + +static void +ferode_2_37(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; +l_int32 wpls10; +l_int32 wpls11; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls11)) & + (*(sptr - wpls4)) & + (*(sptr + wpls3)) & + (*(sptr + wpls10)); + } + } +} + +static void +fdilate_2_38(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + (*sptr) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)); + } + } +} + +static void +ferode_2_38(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + (*sptr) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)); + } + } +} + +static void +fdilate_2_39(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; +l_int32 wpls12; + + wpls6 = 6 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls12)) | + (*(sptr + wpls6)) | + (*sptr) | + (*(sptr - wpls6)) | + (*(sptr - wpls12)); + } + } +} + +static void +ferode_2_39(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; +l_int32 wpls12; + + wpls6 = 6 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls12)) & + (*(sptr - wpls6)) & + (*sptr) & + (*(sptr + wpls6)) & + (*(sptr + wpls12)); + } + } +} + +static void +fdilate_2_40(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)); + } + } +} + +static void +ferode_2_40(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)); + } + } +} + +static void +fdilate_2_41(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls12; + + wpls4 = 4 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls12)) | + (*(sptr + wpls4)) | + (*(sptr - wpls4)) | + (*(sptr - wpls12)); + } + } +} + +static void +ferode_2_41(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls12; + + wpls4 = 4 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls12)) & + (*(sptr - wpls4)) & + (*(sptr + wpls4)) & + (*(sptr + wpls12)); + } + } +} + +static void +fdilate_2_42(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + (*sptr) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)); + } + } +} + +static void +ferode_2_42(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + (*sptr) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)); + } + } +} + +static void +fdilate_2_43(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls11; + + wpls11 = 11 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls11)) | + (*sptr) | + (*(sptr - wpls11)); + } + } +} + +static void +ferode_2_43(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls11; + + wpls11 = 11 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls11)) & + (*sptr) & + (*(sptr + wpls11)); + } + } +} + +static void +fdilate_2_44(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + (*sptr) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)); + } + } +} + +static void +ferode_2_44(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + (*sptr) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)); + } + } +} + +static void +fdilate_2_45(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; +l_int32 wpls14; + + wpls7 = 7 * wpls; + wpls14 = 14 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls14)) | + (*(sptr + wpls7)) | + (*sptr) | + (*(sptr - wpls7)) | + (*(sptr - wpls14)); + } + } +} + +static void +ferode_2_45(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; +l_int32 wpls14; + + wpls7 = 7 * wpls; + wpls14 = 14 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls14)) & + (*(sptr - wpls7)) & + (*sptr) & + (*(sptr + wpls7)) & + (*(sptr + wpls14)); + } + } +} + +static void +fdilate_2_46(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)); + } + } +} + +static void +ferode_2_46(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)); + } + } +} + +static void +fdilate_2_47(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls9; +l_int32 wpls15; + + wpls3 = 3 * wpls; + wpls9 = 9 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls15)) | + (*(sptr + wpls9)) | + (*(sptr + wpls3)) | + (*(sptr - wpls3)) | + (*(sptr - wpls9)) | + (*(sptr - wpls15)); + } + } +} + +static void +ferode_2_47(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls9; +l_int32 wpls15; + + wpls3 = 3 * wpls; + wpls9 = 9 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls15)) & + (*(sptr - wpls9)) & + (*(sptr - wpls3)) & + (*(sptr + wpls3)) & + (*(sptr + wpls9)) & + (*(sptr + wpls15)); + } + } +} + +static void +fdilate_2_48(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + (*sptr) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)); + } + } +} + +static void +ferode_2_48(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + (*sptr) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)); + } + } +} + +static void +fdilate_2_49(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls13; + + wpls13 = 13 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls13)) | + (*sptr) | + (*(sptr - wpls13)); + } + } +} + +static void +ferode_2_49(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls13; + + wpls13 = 13 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls13)) & + (*sptr) & + (*(sptr + wpls13)); + } + } +} + +static void +fdilate_2_50(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + (*sptr) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)); + } + } +} + +static void +ferode_2_50(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + (*sptr) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)); + } + } +} + +static void +fdilate_2_51(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls8; +l_int32 wpls16; + + wpls8 = 8 * wpls; + wpls16 = 16 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls16)) | + (*(sptr + wpls8)) | + (*sptr) | + (*(sptr - wpls8)) | + (*(sptr - wpls16)); + } + } +} + +static void +ferode_2_51(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls8; +l_int32 wpls16; + + wpls8 = 8 * wpls; + wpls16 = 16 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls16)) & + (*(sptr - wpls8)) & + (*sptr) & + (*(sptr + wpls8)) & + (*(sptr + wpls16)); + } + } +} + +static void +fdilate_2_52(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)); + } + } +} + +static void +ferode_2_52(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)); + } + } +} + +static void +fdilate_2_53(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; +l_int32 wpls10; +l_int32 wpls11; +l_int32 wpls17; +l_int32 wpls18; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls18)) | + (*(sptr + wpls11)) | + (*(sptr + wpls4)) | + (*(sptr - wpls3)) | + (*(sptr - wpls10)) | + (*(sptr - wpls17)); + } + } +} + +static void +ferode_2_53(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls3; +l_int32 wpls4; +l_int32 wpls10; +l_int32 wpls11; +l_int32 wpls17; +l_int32 wpls18; + + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls18)) & + (*(sptr - wpls11)) & + (*(sptr - wpls4)) & + (*(sptr + wpls3)) & + (*(sptr + wpls10)) & + (*(sptr + wpls17)); + } + } +} + +static void +fdilate_2_54(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)); + } + } +} + +static void +ferode_2_54(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)); + } + } +} + +static void +fdilate_2_55(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls6; +l_int32 wpls16; +l_int32 wpls17; + + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls17)) | + (*(sptr + wpls6)) | + (*(sptr - wpls5)) | + (*(sptr - wpls16)); + } + } +} + +static void +ferode_2_55(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls6; +l_int32 wpls16; +l_int32 wpls17; + + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls17)) & + (*(sptr - wpls6)) & + (*(sptr + wpls5)) & + (*(sptr + wpls16)); + } + } +} + +static void +fdilate_2_56(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + (*sptr) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)); + } + } +} + +static void +ferode_2_56(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + (*sptr) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)); + } + } +} + +static void +fdilate_2_57(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; +l_int32 wpls18; + + wpls9 = 9 * wpls; + wpls18 = 18 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls18)) | + (*(sptr + wpls9)) | + (*sptr) | + (*(sptr - wpls9)) | + (*(sptr - wpls18)); + } + } +} + +static void +ferode_2_57(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; +l_int32 wpls18; + + wpls9 = 9 * wpls; + wpls18 = 18 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls18)) & + (*(sptr - wpls9)) & + (*sptr) & + (*(sptr + wpls9)) & + (*(sptr + wpls18)); + } + } +} + +static void +fdilate_2_58(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)); + } + } +} + +static void +ferode_2_58(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)); + } + } +} + +static void +fdilate_2_59(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls12; +l_int32 wpls20; + + wpls4 = 4 * wpls; + wpls12 = 12 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls20)) | + (*(sptr + wpls12)) | + (*(sptr + wpls4)) | + (*(sptr - wpls4)) | + (*(sptr - wpls12)) | + (*(sptr - wpls20)); + } + } +} + +static void +ferode_2_59(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls12; +l_int32 wpls20; + + wpls4 = 4 * wpls; + wpls12 = 12 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls20)) & + (*(sptr - wpls12)) & + (*(sptr - wpls4)) & + (*(sptr + wpls4)) & + (*(sptr + wpls12)) & + (*(sptr + wpls20)); + } + } +} + +static void +fdilate_2_60(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + (*sptr) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 21) | (*(sptr - 1) << 11)); + } + } +} + +static void +ferode_2_60(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + (*sptr) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 21) | (*(sptr + 1) >> 11)); + } + } +} + +static void +fdilate_2_61(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; +l_int32 wpls14; +l_int32 wpls21; + + wpls7 = 7 * wpls; + wpls14 = 14 * wpls; + wpls21 = 21 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls21)) | + (*(sptr + wpls14)) | + (*(sptr + wpls7)) | + (*sptr) | + (*(sptr - wpls7)) | + (*(sptr - wpls14)) | + (*(sptr - wpls21)); + } + } +} + +static void +ferode_2_61(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls7; +l_int32 wpls14; +l_int32 wpls21; + + wpls7 = 7 * wpls; + wpls14 = 14 * wpls; + wpls21 = 21 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls21)) & + (*(sptr - wpls14)) & + (*(sptr - wpls7)) & + (*sptr) & + (*(sptr + wpls7)) & + (*(sptr + wpls14)) & + (*(sptr + wpls21)); + } + } +} + +static void +fdilate_2_62(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + (*sptr) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)); + } + } +} + +static void +ferode_2_62(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + (*sptr) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)); + } + } +} + +static void +fdilate_2_63(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls10; +l_int32 wpls20; + + wpls10 = 10 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls20)) | + (*(sptr + wpls10)) | + (*sptr) | + (*(sptr - wpls10)) | + (*(sptr - wpls20)); + } + } +} + +static void +ferode_2_63(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls10; +l_int32 wpls20; + + wpls10 = 10 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls20)) & + (*(sptr - wpls10)) & + (*sptr) & + (*(sptr + wpls10)) & + (*(sptr + wpls20)); + } + } +} + +static void +fdilate_2_64(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)); + } + } +} + +static void +ferode_2_64(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)); + } + } +} + +static void +fdilate_2_65(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; +l_int32 wpls7; +l_int32 wpls19; +l_int32 wpls20; + + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls20)) | + (*(sptr + wpls7)) | + (*(sptr - wpls6)) | + (*(sptr - wpls19)); + } + } +} + +static void +ferode_2_65(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls6; +l_int32 wpls7; +l_int32 wpls19; +l_int32 wpls20; + + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls20)) & + (*(sptr - wpls7)) & + (*(sptr + wpls6)) & + (*(sptr + wpls19)); + } + } +} + +static void +fdilate_2_66(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 23) | (*(sptr + 1) >> 9)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 22) | (*(sptr - 1) << 10)); + } + } +} + +static void +ferode_2_66(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 23) | (*(sptr - 1) << 9)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 22) | (*(sptr + 1) >> 10)); + } + } +} + +static void +fdilate_2_67(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls5; +l_int32 wpls13; +l_int32 wpls14; +l_int32 wpls22; +l_int32 wpls23; + + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls23)) | + (*(sptr + wpls14)) | + (*(sptr + wpls5)) | + (*(sptr - wpls4)) | + (*(sptr - wpls13)) | + (*(sptr - wpls22)); + } + } +} + +static void +ferode_2_67(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls4; +l_int32 wpls5; +l_int32 wpls13; +l_int32 wpls14; +l_int32 wpls22; +l_int32 wpls23; + + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls23)) & + (*(sptr - wpls14)) & + (*(sptr - wpls5)) & + (*(sptr + wpls4)) & + (*(sptr + wpls13)) & + (*(sptr + wpls22)); + } + } +} + +static void +fdilate_2_68(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + (*sptr) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 22) | (*(sptr - 1) << 10)); + } + } +} + +static void +ferode_2_68(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + (*sptr) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 22) | (*(sptr + 1) >> 10)); + } + } +} + +static void +fdilate_2_69(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls11; +l_int32 wpls22; + + wpls11 = 11 * wpls; + wpls22 = 22 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls22)) | + (*(sptr + wpls11)) | + (*sptr) | + (*(sptr - wpls11)) | + (*(sptr - wpls22)); + } + } +} + +static void +ferode_2_69(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls11; +l_int32 wpls22; + + wpls11 = 11 * wpls; + wpls22 = 22 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls22)) & + (*(sptr - wpls11)) & + (*sptr) & + (*(sptr + wpls11)) & + (*(sptr + wpls22)); + } + } +} + +static void +fdilate_2_70(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 24) | (*(sptr + 1) >> 8)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + (*sptr) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 24) | (*(sptr - 1) << 8)); + } + } +} + +static void +ferode_2_70(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 24) | (*(sptr - 1) << 8)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + (*sptr) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 24) | (*(sptr + 1) >> 8)); + } + } +} + +static void +fdilate_2_71(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls8; +l_int32 wpls16; +l_int32 wpls24; + + wpls8 = 8 * wpls; + wpls16 = 16 * wpls; + wpls24 = 24 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls24)) | + (*(sptr + wpls16)) | + (*(sptr + wpls8)) | + (*sptr) | + (*(sptr - wpls8)) | + (*(sptr - wpls16)) | + (*(sptr - wpls24)); + } + } +} + +static void +ferode_2_71(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls8; +l_int32 wpls16; +l_int32 wpls24; + + wpls8 = 8 * wpls; + wpls16 = 16 * wpls; + wpls24 = 24 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls24)) & + (*(sptr - wpls16)) & + (*(sptr - wpls8)) & + (*sptr) & + (*(sptr + wpls8)) & + (*(sptr + wpls16)) & + (*(sptr + wpls24)); + } + } +} + +static void +fdilate_2_72(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 25) | (*(sptr + 1) >> 7)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 25) | (*(sptr - 1) << 7)); + } + } +} + +static void +ferode_2_72(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 25) | (*(sptr - 1) << 7)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 25) | (*(sptr + 1) >> 7)); + } + } +} + +static void +fdilate_2_73(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls15; +l_int32 wpls25; + + wpls5 = 5 * wpls; + wpls15 = 15 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls25)) | + (*(sptr + wpls15)) | + (*(sptr + wpls5)) | + (*(sptr - wpls5)) | + (*(sptr - wpls15)) | + (*(sptr - wpls25)); + } + } +} + +static void +ferode_2_73(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls5; +l_int32 wpls15; +l_int32 wpls25; + + wpls5 = 5 * wpls; + wpls15 = 15 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls25)) & + (*(sptr - wpls15)) & + (*(sptr - wpls5)) & + (*(sptr + wpls5)) & + (*(sptr + wpls15)) & + (*(sptr + wpls25)); + } + } +} + +static void +fdilate_2_74(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 27) | (*(sptr + 1) >> 5)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + (*sptr) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 27) | (*(sptr - 1) << 5)); + } + } +} + +static void +ferode_2_74(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 27) | (*(sptr - 1) << 5)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + (*sptr) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 27) | (*(sptr + 1) >> 5)); + } + } +} + +static void +fdilate_2_75(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; +l_int32 wpls18; +l_int32 wpls27; + + wpls9 = 9 * wpls; + wpls18 = 18 * wpls; + wpls27 = 27 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls27)) | + (*(sptr + wpls18)) | + (*(sptr + wpls9)) | + (*sptr) | + (*(sptr - wpls9)) | + (*(sptr - wpls18)) | + (*(sptr - wpls27)); + } + } +} + +static void +ferode_2_75(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls9; +l_int32 wpls18; +l_int32 wpls27; + + wpls9 = 9 * wpls; + wpls18 = 18 * wpls; + wpls27 = 27 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls27)) & + (*(sptr - wpls18)) & + (*(sptr - wpls9)) & + (*sptr) & + (*(sptr + wpls9)) & + (*(sptr + wpls18)) & + (*(sptr + wpls27)); + } + } +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/edge.c b/hgdriver/3rdparty/hgOCR/leptonica/edge.c new file mode 100644 index 0000000..d620cb8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/edge.c @@ -0,0 +1,644 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file edge.c + *
+ *
+ *      Sobel edge detecting filter
+ *          PIX      *pixSobelEdgeFilter()
+ *
+ *      Two-sided edge gradient filter
+ *          PIX      *pixTwoSidedEdgeFilter()
+ *
+ *      Measurement of edge smoothness
+ *          l_int32   pixMeasureEdgeSmoothness()
+ *          NUMA     *pixGetEdgeProfile()
+ *          l_int32   pixGetLastOffPixelInRun()
+ *          l_int32   pixGetLastOnPixelInRun()
+ *
+ *
+ *  The Sobel edge detector uses these two simple gradient filters.
+ *
+ *       1    2    1             1    0   -1
+ *       0    0    0             2    0   -2
+ *      -1   -2   -1             1    0   -1
+ *
+ *      (horizontal)             (vertical)
+ *
+ *  To use both the vertical and horizontal filters, set the orientation
+ *  flag to L_ALL_EDGES; this sums the abs. value of their outputs,
+ *  clipped to 255.
+ *
+ *  See comments below for displaying the resulting image with
+ *  the edges dark, both for 8 bpp and 1 bpp.
+ * 
+ */ + +#include "allheaders.h" + + +/*----------------------------------------------------------------------* + * Sobel edge detecting filter * + *----------------------------------------------------------------------*/ +/*! + * \brief pixSobelEdgeFilter() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] orientflag L_HORIZONTAL_EDGES, L_VERTICAL_EDGES, L_ALL_EDGES + * \return pixd 8 bpp, edges are brighter, or NULL on error + * + *
+ * Notes:
+ *      (1) Invert pixd to see larger gradients as darker (grayscale).
+ *      (2) To generate a binary image of the edges, threshold
+ *          the result using pixThresholdToBinary().  If the high
+ *          edge values are to be fg (1), invert after running
+ *          pixThresholdToBinary().
+ *      (3) Label the pixels as follows:
+ *              1    4    7
+ *              2    5    8
+ *              3    6    9
+ *          Read the data incrementally across the image and unroll
+ *          the loop.
+ *      (4) This runs at about 45 Mpix/sec on a 3 GHz processor.
+ * 
+ */ +PIX * +pixSobelEdgeFilter(PIX *pixs, + l_int32 orientflag) +{ +l_int32 w, h, d, i, j, wplt, wpld, gx, gy, vald; +l_int32 val1, val2, val3, val4, val5, val6, val7, val8, val9; +l_uint32 *datat, *linet, *datad, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixSobelEdgeFilter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES && + orientflag != L_ALL_EDGES) + return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL); + + /* Add 1 pixel (mirrored) to each side of the image. */ + if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* Compute filter output at each location. */ + pixd = pixCreateTemplate(pixs); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (j == 0) { /* start a new row */ + val1 = GET_DATA_BYTE(linet, j); + val2 = GET_DATA_BYTE(linet + wplt, j); + val3 = GET_DATA_BYTE(linet + 2 * wplt, j); + val4 = GET_DATA_BYTE(linet, j + 1); + val5 = GET_DATA_BYTE(linet + wplt, j + 1); + val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1); + val7 = GET_DATA_BYTE(linet, j + 2); + val8 = GET_DATA_BYTE(linet + wplt, j + 2); + val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2); + } else { /* shift right by 1 pixel; update incrementally */ + val1 = val4; + val2 = val5; + val3 = val6; + val4 = val7; + val5 = val8; + val6 = val9; + val7 = GET_DATA_BYTE(linet, j + 2); + val8 = GET_DATA_BYTE(linet + wplt, j + 2); + val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2); + } + if (orientflag == L_HORIZONTAL_EDGES) + vald = L_ABS(val1 + 2 * val4 + val7 + - val3 - 2 * val6 - val9) >> 3; + else if (orientflag == L_VERTICAL_EDGES) + vald = L_ABS(val1 + 2 * val2 + val3 - val7 + - 2 * val8 - val9) >> 3; + else { /* L_ALL_EDGES */ + gx = L_ABS(val1 + 2 * val2 + val3 - val7 + - 2 * val8 - val9) >> 3; + gy = L_ABS(val1 + 2 * val4 + val7 + - val3 - 2 * val6 - val9) >> 3; + vald = L_MIN(255, gx + gy); + } + SET_DATA_BYTE(lined, j, vald); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Two-sided edge gradient filter * + *----------------------------------------------------------------------*/ +/*! + * \brief pixTwoSidedEdgeFilter() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] orientflag L_HORIZONTAL_EDGES, L_VERTICAL_EDGES + * \return pixd 8 bpp, edges are brighter, or NULL on error + * + *
+ * Notes:
+ *      (1) For detecting vertical edges, this considers the
+ *          difference of the central pixel from those on the left
+ *          and right.  For situations where the gradient is the same
+ *          sign on both sides, this computes and stores the minimum
+ *          (absolute value of the) difference.  The reason for
+ *          checking the sign is that we are looking for pixels within
+ *          a transition.  By contrast, for single pixel noise, the pixel
+ *          value is either larger than or smaller than its neighbors,
+ *          so the gradient would change direction on each side.  Horizontal
+ *          edges are handled similarly, looking for vertical gradients.
+ *      (2) To generate a binary image of the edges, threshold
+ *          the result using pixThresholdToBinary().  If the high
+ *          edge values are to be fg (1), invert after running
+ *          pixThresholdToBinary().
+ *      (3) This runs at about 60 Mpix/sec on a 3 GHz processor.
+ *          It is about 30% faster than Sobel, and the results are
+ *          similar.
+ * 
+ */ +PIX * +pixTwoSidedEdgeFilter(PIX *pixs, + l_int32 orientflag) +{ +l_int32 w, h, d, i, j, wpls, wpld; +l_int32 cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixd; + + PROCNAME("pixTwoSidedEdgeFilter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES) + return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL); + + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + if (orientflag == L_VERTICAL_EDGES) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + cval = GET_DATA_BYTE(lines, 1); + lgrad = cval - GET_DATA_BYTE(lines, 0); + for (j = 1; j < w - 1; j++) { + rval = GET_DATA_BYTE(lines, j + 1); + rgrad = rval - cval; + if (lgrad * rgrad > 0) { + if (lgrad < 0) + val = -L_MAX(lgrad, rgrad); + else + val = L_MIN(lgrad, rgrad); + SET_DATA_BYTE(lined, j, val); + } + lgrad = rgrad; + cval = rval; + } + } + } + else { /* L_HORIZONTAL_EDGES) */ + for (j = 0; j < w; j++) { + lines = datas + wpls; + cval = GET_DATA_BYTE(lines, j); /* for line 1 */ + tgrad = cval - GET_DATA_BYTE(datas, j); + for (i = 1; i < h - 1; i++) { + lines += wpls; /* for line i + 1 */ + lined = datad + i * wpld; + bval = GET_DATA_BYTE(lines, j); + bgrad = bval - cval; + if (tgrad * bgrad > 0) { + if (tgrad < 0) + val = -L_MAX(tgrad, bgrad); + else + val = L_MIN(tgrad, bgrad); + SET_DATA_BYTE(lined, j, val); + } + tgrad = bgrad; + cval = bval; + } + } + } + + return pixd; +} + + +/*----------------------------------------------------------------------* + * Measurement of edge smoothness * + *----------------------------------------------------------------------*/ +/*! + * \brief pixMeasureEdgeSmoothness() + * + * \param[in] pixs 1 bpp + * \param[in] side L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT + * \param[in] minjump minimum jump to be counted; >= 1 + * \param[in] minreversal minimum reversal size for new peak or valley + * \param[out] pjpl [optional] jumps/length: number of jumps, + * normalized to length of component side + * \param[out] pjspl [optional] jumpsum/length: sum of all + * sufficiently large jumps, normalized to length + * of component side + * \param[out] prpl [optional] reversals/length: number of + * peak-to-valley or valley-to-peak reversals, + * normalized to length of component side + * \param[in] debugfile [optional] displays constructed edge; use NULL + * for no output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes three measures of smoothness of the edge of a
+ *          connected component:
+ *            * jumps/length: (jpl) the number of jumps of size >= %minjump,
+ *              normalized to the length of the side
+ *            * jump sum/length: (jspl) the sum of all jump lengths of
+ *              size >= %minjump, normalized to the length of the side
+ *            * reversals/length: (rpl) the number of peak <--> valley
+ *              reversals, using %minreverse as a minimum deviation of
+ *              the peak or valley from its preceding extremum,
+ *              normalized to the length of the side
+ *      (2) The input pix should be a single connected component, but
+ *          this is not required.
+ * 
+ */ +l_ok +pixMeasureEdgeSmoothness(PIX *pixs, + l_int32 side, + l_int32 minjump, + l_int32 minreversal, + l_float32 *pjpl, + l_float32 *pjspl, + l_float32 *prpl, + const char *debugfile) +{ +l_int32 i, n, val, nval, diff, njumps, jumpsum, nreversal; +NUMA *na, *nae; + + PROCNAME("pixMeasureEdgeSmoothness"); + + if (pjpl) *pjpl = 0.0; + if (pjspl) *pjspl = 0.0; + if (prpl) *prpl = 0.0; + if (!pjpl && !pjspl && !prpl && !debugfile) + return ERROR_INT("no output requested", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (side != L_FROM_LEFT && side != L_FROM_RIGHT && + side != L_FROM_TOP && side != L_FROM_BOT) + return ERROR_INT("invalid side", procName, 1); + if (minjump < 1) + return ERROR_INT("invalid minjump; must be >= 1", procName, 1); + if (minreversal < 1) + return ERROR_INT("invalid minreversal; must be >= 1", procName, 1); + + if ((na = pixGetEdgeProfile(pixs, side, debugfile)) == NULL) + return ERROR_INT("edge profile not made", procName, 1); + if ((n = numaGetCount(na)) < 2) { + numaDestroy(&na); + return 0; + } + + if (pjpl || pjspl) { + jumpsum = 0; + njumps = 0; + numaGetIValue(na, 0, &val); + for (i = 1; i < n; i++) { + numaGetIValue(na, i, &nval); + diff = L_ABS(nval - val); + if (diff >= minjump) { + njumps++; + jumpsum += diff; + } + val = nval; + } + if (pjpl) + *pjpl = (l_float32)njumps / (l_float32)(n - 1); + if (pjspl) + *pjspl = (l_float32)jumpsum / (l_float32)(n - 1); + } + + if (prpl) { + nae = numaFindExtrema(na, minreversal, NULL); + nreversal = numaGetCount(nae) - 1; + *prpl = (l_float32)nreversal / (l_float32)(n - 1); + numaDestroy(&nae); + } + + numaDestroy(&na); + return 0; +} + + +/*! + * \brief pixGetEdgeProfile() + * + * \param[in] pixs 1 bpp + * \param[in] side L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT + * \param[in] debugfile [optional] displays constructed edge; use NULL + * for no output + * \return na of fg edge pixel locations, or NULL on error + */ +NUMA * +pixGetEdgeProfile(PIX *pixs, + l_int32 side, + const char *debugfile) +{ +l_int32 x, y, w, h, loc, index, ival; +l_uint32 val; +NUMA *na; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGetEdgeProfile"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (side != L_FROM_LEFT && side != L_FROM_RIGHT && + side != L_FROM_TOP && side != L_FROM_BOT) + return (NUMA *)ERROR_PTR("invalid side", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (side == L_FROM_LEFT || side == L_FROM_RIGHT) + na = numaCreate(h); + else + na = numaCreate(w); + if (side == L_FROM_LEFT) { + pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_LEFT, &loc); + loc = (loc == w - 1) ? 0 : loc + 1; /* back to the left edge */ + numaAddNumber(na, loc); + for (y = 1; y < h; y++) { + pixGetPixel(pixs, loc, y, &val); + if (val == 1) { + pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc); + } else { + pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc); + loc = (loc == w - 1) ? 0 : loc + 1; + } + numaAddNumber(na, loc); + } + } + else if (side == L_FROM_RIGHT) { + pixGetLastOffPixelInRun(pixs, w - 1, 0, L_FROM_RIGHT, &loc); + loc = (loc == 0) ? w - 1 : loc - 1; /* back to the right edge */ + numaAddNumber(na, loc); + for (y = 1; y < h; y++) { + pixGetPixel(pixs, loc, y, &val); + if (val == 1) { + pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc); + } else { + pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc); + loc = (loc == 0) ? w - 1 : loc - 1; + } + numaAddNumber(na, loc); + } + } + else if (side == L_FROM_TOP) { + pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_TOP, &loc); + loc = (loc == h - 1) ? 0 : loc + 1; /* back to the top edge */ + numaAddNumber(na, loc); + for (x = 1; x < w; x++) { + pixGetPixel(pixs, x, loc, &val); + if (val == 1) { + pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_BOT, &loc); + } else { + pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_TOP, &loc); + loc = (loc == h - 1) ? 0 : loc + 1; + } + numaAddNumber(na, loc); + } + } + else { /* side == L_FROM_BOT */ + pixGetLastOffPixelInRun(pixs, 0, h - 1, L_FROM_BOT, &loc); + loc = (loc == 0) ? h - 1 : loc - 1; /* back to the bottom edge */ + numaAddNumber(na, loc); + for (x = 1; x < w; x++) { + pixGetPixel(pixs, x, loc, &val); + if (val == 1) { + pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_TOP, &loc); + } else { + pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_BOT, &loc); + loc = (loc == 0) ? h - 1 : loc - 1; + } + numaAddNumber(na, loc); + } + } + + if (debugfile) { + pixt = pixConvertTo8(pixs, TRUE); + cmap = pixGetColormap(pixt); + pixcmapAddColor(cmap, 255, 0, 0); + index = pixcmapGetCount(cmap) - 1; + if (side == L_FROM_LEFT || side == L_FROM_RIGHT) { + for (y = 0; y < h; y++) { + numaGetIValue(na, y, &ival); + pixSetPixel(pixt, ival, y, index); + } + } else { /* L_FROM_TOP or L_FROM_BOT */ + for (x = 0; x < w; x++) { + numaGetIValue(na, x, &ival); + pixSetPixel(pixt, x, ival, index); + } + } + pixWrite(debugfile, pixt, IFF_PNG); + pixDestroy(&pixt); + } + + return na; +} + + +/* + * \brief pixGetLastOffPixelInRun() + * + * \param[in] pixs 1 bpp + * \param[in] x, y starting location + * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT + * \param[out] ploc location in scan direction coordinate + * of last OFF pixel found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Search starts from the pixel at (x, y), which is OFF.
+ *      (2) It returns the location in the scan direction of the last
+ *          pixel in the current run that is OFF.
+ *      (3) The interface for these pixel run functions is cleaner when
+ *          you ask for the last pixel in the current run, rather than the
+ *          first pixel of opposite polarity that is found, because the
+ *          current run may go to the edge of the image, in which case
+ *          no pixel of opposite polarity is found.
+ * 
+ */ +l_ok +pixGetLastOffPixelInRun(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 direction, + l_int32 *ploc) +{ +l_int32 loc, w, h; +l_uint32 val; + + PROCNAME("pixGetLastOffPixelInRun"); + + if (!ploc) + return ERROR_INT("&loc not defined", procName, 1); + *ploc = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT && + direction != L_FROM_TOP && direction != L_FROM_BOT) + return ERROR_INT("invalid side", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + if (direction == L_FROM_LEFT) { + for (loc = x; loc < w; loc++) { + pixGetPixel(pixs, loc, y, &val); + if (val == 1) + break; + } + *ploc = loc - 1; + } else if (direction == L_FROM_RIGHT) { + for (loc = x; loc >= 0; loc--) { + pixGetPixel(pixs, loc, y, &val); + if (val == 1) + break; + } + *ploc = loc + 1; + } + else if (direction == L_FROM_TOP) { + for (loc = y; loc < h; loc++) { + pixGetPixel(pixs, x, loc, &val); + if (val == 1) + break; + } + *ploc = loc - 1; + } + else if (direction == L_FROM_BOT) { + for (loc = y; loc >= 0; loc--) { + pixGetPixel(pixs, x, loc, &val); + if (val == 1) + break; + } + *ploc = loc + 1; + } + return 0; +} + + +/* + * \brief pixGetLastOnPixelInRun() + * + * \param[in] pixs 1 bpp + * \param[in] x, y starting location + * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT + * \param[out] ploc location in scan direction coordinate + * of first ON pixel found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Search starts from the pixel at (x, y), which is ON.
+ *      (2) It returns the location in the scan direction of the last
+ *          pixel in the current run that is ON.
+ * 
+ */ +l_int32 +pixGetLastOnPixelInRun(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 direction, + l_int32 *ploc) +{ +l_int32 loc, w, h; +l_uint32 val; + + PROCNAME("pixLastOnPixelInRun"); + + if (!ploc) + return ERROR_INT("&loc not defined", procName, 1); + *ploc = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT && + direction != L_FROM_TOP && direction != L_FROM_BOT) + return ERROR_INT("invalid side", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + if (direction == L_FROM_LEFT) { + for (loc = x; loc < w; loc++) { + pixGetPixel(pixs, loc, y, &val); + if (val == 0) + break; + } + *ploc = loc - 1; + } else if (direction == L_FROM_RIGHT) { + for (loc = x; loc >= 0; loc--) { + pixGetPixel(pixs, loc, y, &val); + if (val == 0) + break; + } + *ploc = loc + 1; + } + else if (direction == L_FROM_TOP) { + for (loc = y; loc < h; loc++) { + pixGetPixel(pixs, x, loc, &val); + if (val == 0) + break; + } + *ploc = loc - 1; + } + else if (direction == L_FROM_BOT) { + for (loc = y; loc >= 0; loc--) { + pixGetPixel(pixs, x, loc, &val); + if (val == 0) + break; + } + *ploc = loc + 1; + } + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/encoding.c b/hgdriver/3rdparty/hgOCR/leptonica/encoding.c new file mode 100644 index 0000000..d940413 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/encoding.c @@ -0,0 +1,649 @@ +/*====================================================================* + - 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. + *====================================================================*/ + +/* + * encodings.c + * + * Base64 + * char *encodeBase64() + * l_uint8 *decodeBase64() + * static l_int32 isBase64() + * static l_int32 *genReverseTab64() + * static void byteConvert3to4() + * static void byteConvert4to3() + * + * Ascii85 + * char *encodeAscii85() + * l_uint8 *decodeAscii85() + * static l_int32 convertChunkToAscii85() + * + * String reformatting for base 64 encoded data + * char *reformatPacked64() + * + * Base64 encoding is useful for encding binary data in a restricted set of + * 64 printable ascii symbols, that includes the 62 alphanumerics and '+' + * and '/'. Notably it does not include quotes, so that base64 encoded + * strings can be used in situations where quotes are used for formatting. + * 64 symbols was chosen because it is the smallest number that can be used + * in 4-for-3 byte encoding of binary data: + * log2(64) / log2(256) = 0.75 = 3/4 + * + * Ascii85 encoding is used in PostScript and some pdf files for + * representing binary data (for example, a compressed image) in printable + * ascii symbols. It has a dictionary of 85 symbols; 85 was chosen because + * it is the smallest number that can be used in 5-for-4 byte encoding + * of binary data (256 possible input values). This can be seen from + * the max information content in such a sequence: + * log2(84) / log2(256) = 0.799 < 4/5 + * log2(85) / log2(256) = 0.801 > 4/5 + */ + +#include +#include "allheaders.h" + + /* Base64 encoding table in string representation */ +static const l_int32 MAX_BASE64_LINE = 72; /* max line length base64 */ +static const char *tablechar64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static l_int32 isBase64(char); +static l_int32 *genReverseTab64(void); +static void byteConvert3to4(l_uint8 *in3, l_uint8 *out4); +static void byteConvert4to3(l_uint8 *in4, l_uint8 *out3); + + /* Ascii85 encoding */ +static const l_int32 MAX_ASCII85_LINE = 64; /* max line length ascii85 */ +static const l_uint32 power85[5] = {1, + 85, + 85 * 85, + 85 * 85 * 85, + 85 * 85 * 85 * 85}; + +static l_int32 convertChunkToAscii85(const l_uint8 *inarray, l_int32 insize, + l_int32 *pindex, char *outbuf, + l_int32 *pnbout); + + +/*-------------------------------------------------------------* + * Utility for encoding and decoding data with base64 * + *-------------------------------------------------------------*/ +/*! + * \brief encodeBase64() + * + * \param[in] inarray input binary data + * \param[in] insize number of bytes in input array + * \param[out] poutsize number of bytes in output char array + * \return chara with MAX_BASE64_LINE characters + \n in each line + * + *
+ * Notes:
+ *      (1) The input character data is unrestricted binary.
+ *          The output encoded data consists of the 64 characters
+ *          in the base64 set, plus newlines and the pad character '='.
+ * 
+ */ +char * +encodeBase64(const l_uint8 *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char *chara; +const l_uint8 *bytea; +l_uint8 array3[3], array4[4]; +l_int32 outsize, i, j, index, linecount; + + PROCNAME("encodeBase64"); + + if (!poutsize) + return (char *)ERROR_PTR("&outsize not defined", procName, NULL); + *poutsize = 0; + if (!inarray) + return (char *)ERROR_PTR("inarray not defined", procName, NULL); + if (insize <= 0) + return (char *)ERROR_PTR("insize not > 0", procName, NULL); + + /* The output array is padded to a multiple of 4 bytes, not + * counting the newlines. We just need to allocate a large + * enough array, and add 4 bytes to make sure it is big enough. */ + outsize = 4 * ((insize + 2) / 3); /* without newlines */ + outsize += outsize / MAX_BASE64_LINE + 4; /* with the newlines */ + if ((chara = (char *)LEPT_CALLOC(outsize, sizeof(char))) == NULL) + return (char *)ERROR_PTR("chara not made", procName, NULL); + + /* Read all the input data, and convert in sets of 3 input + * bytes --> 4 output bytes. */ + i = index = linecount = 0; + bytea = inarray; + while (insize--) { + if (linecount == MAX_BASE64_LINE) { + chara[index++] = '\n'; + linecount = 0; + } + array3[i++] = *bytea++; + if (i == 3) { /* convert 3 to 4 and save */ + byteConvert3to4(array3, array4); + for (j = 0; j < 4; j++) + chara[index++] = tablechar64[array4[j]]; + i = 0; + linecount += 4; + } + } + + /* Suppose 1 or 2 bytes has been read but not yet processed. + * If 1 byte has been read, this will generate 2 bytes of + * output, with 6 bits to the first byte and 2 bits to the second. + * We will add two bytes of '=' for padding. + * If 2 bytes has been read, this will generate 3 bytes of output, + * with 6 bits to the first 2 bytes and 4 bits to the third, and + * we add a fourth padding byte ('='). */ + if (i > 0) { /* left-over 1 or 2 input bytes */ + for (j = i; j < 3; j++) + array3[j] = '\0'; /* zero the remaining input bytes */ + byteConvert3to4(array3, array4); + for (j = 0; j <= i; j++) + chara[index++] = tablechar64[array4[j]]; + for (j = i + 1; j < 4; j++) + chara[index++] = '='; + } + *poutsize = index; + + return chara; +} + + +/*! + * \brief decodeBase64() + * + * \param[in] inarray input encoded char data, with 72 chars/line) + * \param[in] insize number of bytes in input array + * \param[out] poutsize number of bytes in output byte array + * \return bytea decoded byte data, or NULL on error + * + *
+ * Notes:
+ *      (1) The input character data should have only 66 different characters:
+ *          The 64 character set for base64 encoding, plus the pad
+ *          character '=' and newlines for formatting with fixed line
+ *          lengths.  If there are any other characters, the decoder
+ *          will declare the input data to be invalid and return NULL.
+ *      (2) The decoder ignores newlines and, for a valid input string,
+ *          stops reading input when a pad byte is found.
+ * 
+ */ +l_uint8 * +decodeBase64(const char *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char inchar; +l_uint8 *bytea; +l_uint8 array3[3], array4[4]; +l_int32 *rtable64; +l_int32 i, j, outsize, in_index, out_index; + + PROCNAME("decodeBase64"); + + if (!poutsize) + return (l_uint8 *)ERROR_PTR("&outsize not defined", procName, NULL); + *poutsize = 0; + if (!inarray) + return (l_uint8 *)ERROR_PTR("inarray not defined", procName, NULL); + if (insize <= 0) + return (l_uint8 *)ERROR_PTR("insize not > 0", procName, NULL); + + /* Validate the input data */ + for (i = 0; i < insize; i++) { + inchar = inarray[i]; + if (inchar == '\n') continue; + if (isBase64(inchar) == 0 && inchar != '=') + return (l_uint8 *)ERROR_PTR("invalid char in inarray", + procName, NULL); + } + + /* The input array typically is made with a newline every + * MAX_BASE64_LINE input bytes. However, as a printed string, the + * newlines would be stripped. So when we allocate the output + * array, assume the input array is all data, but strip + * out the newlines during decoding. This guarantees that + * the allocated array is large enough. */ + outsize = 3 * ((insize + 3) / 4) + 4; + if ((bytea = (l_uint8 *)LEPT_CALLOC(outsize, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL); + + /* The number of encoded input data bytes is always a multiple of 4. + * Read all the data, until you reach either the end or + * the first pad character '='. The data is processed in + * units of 4 input bytes, generating 3 output decoded bytes + * of binary data. Newlines are ignored. If there are no + * pad bytes, i == 0 at the end of this section. */ + rtable64 = genReverseTab64(); + i = in_index = out_index = 0; + for (in_index = 0; in_index < insize; in_index++) { + inchar = inarray[in_index]; + if (inchar == '\n') continue; + if (inchar == '=') break; + array4[i++] = rtable64[(unsigned char)inchar]; + if (i < 4) { + continue; + } else { /* i == 4; convert 4 to 3 and save */ + byteConvert4to3(array4, array3); + for (j = 0; j < 3; j++) + bytea[out_index++] = array3[j]; + i = 0; + } + } + + /* If i > 0, we ran into pad bytes ('='). If i == 2, there are + * two input pad bytes and one output data byte. If i == 3, + * there is one input pad byte and two output data bytes. */ + if (i > 0) { + for (j = i; j < 4; j++) + array4[j] = '\0'; /* zero the remaining input bytes */ + byteConvert4to3(array4, array3); + for (j = 0; j < i - 1; j++) + bytea[out_index++] = array3[j]; + } + *poutsize = out_index; + + LEPT_FREE(rtable64); + return bytea; +} + + +/*! + * \brief isBase64() + */ +static l_int32 +isBase64(char c) +{ + return (isalnum(((int)c)) || ((c) == '+') || ((c) == '/')) ? 1 : 0; +} + +/*! + * \brief genReverseTab64() + */ +static l_int32 * +genReverseTab64() +{ +l_int32 i; +l_int32 *rtable64; + + rtable64 = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); + for (i = 0; i < 64; i++) { + rtable64[(unsigned char)tablechar64[i]] = i; + } + return rtable64; +} + +/*! + * \brief byteConvert3to4() + */ +static void +byteConvert3to4(l_uint8 *in3, + l_uint8 *out4) +{ + out4[0] = in3[0] >> 2; + out4[1] = ((in3[0] & 0x03) << 4) | (in3[1] >> 4); + out4[2] = ((in3[1] & 0x0f) << 2) | (in3[2] >> 6); + out4[3] = in3[2] & 0x3f; + return; +} + +/*! + * \brief byteConvert4to3() + */ +static void +byteConvert4to3(l_uint8 *in4, + l_uint8 *out3) +{ + out3[0] = (in4[0] << 2) | (in4[1] >> 4); + out3[1] = ((in4[1] & 0x0f) << 4) | (in4[2] >> 2); + out3[2] = ((in4[2] & 0x03) << 6) | in4[3]; + return; +} + + +/*-------------------------------------------------------------* + * Utility for encoding and decoding data with ascii85 * + *-------------------------------------------------------------*/ +/*! + * \brief encodeAscii85() + * + * \param[in] inarray input data + * \param[in] insize number of bytes in input array + * \param[out] poutsize number of bytes in output char array + * \return chara with 64 characters + \n in each line + * + *
+ * Notes:
+ *      (1) Ghostscript has a stack break if the last line of
+ *          data only has a '>', so we avoid the problem by
+ *          always putting '~>' on the last line.
+ * 
+ */ +char * +encodeAscii85(const l_uint8 *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char *chara; +char outbuf[8]; +l_int32 maxsize, i, index, outindex, linecount, nbout, eof; + + PROCNAME("encodeAscii85"); + + if (!poutsize) + return (char *)ERROR_PTR("&outsize not defined", procName, NULL); + *poutsize = 0; + if (!inarray) + return (char *)ERROR_PTR("inarray not defined", procName, NULL); + if (insize <= 0) + return (char *)ERROR_PTR("insize not > 0", procName, NULL); + + /* Accumulate results in char array */ + maxsize = (l_int32)(80. + (insize * 5. / 4.) * + (1. + 2. / MAX_ASCII85_LINE)); + if ((chara = (char *)LEPT_CALLOC(maxsize, sizeof(char))) == NULL) + return (char *)ERROR_PTR("chara not made", procName, NULL); + + linecount = 0; + index = 0; + outindex = 0; + while (1) { + eof = convertChunkToAscii85(inarray, insize, &index, outbuf, &nbout); + for (i = 0; i < nbout; i++) { + chara[outindex++] = outbuf[i]; + linecount++; + if (linecount >= MAX_ASCII85_LINE) { + chara[outindex++] = '\n'; + linecount = 0; + } + } + if (eof == TRUE) { + if (linecount != 0) + chara[outindex++] = '\n'; + chara[outindex++] = '~'; + chara[outindex++] = '>'; + chara[outindex++] = '\n'; + break; + } + } + + *poutsize = outindex; + return chara; +} + + +/*! + * \brief convertChunkToAscii85() + * + * \param[in] inarray input data + * \param[in] insize number of bytes in input array + * \param[out] pindex use and -- ptr + * \param[in] outbuf holds 8 ascii chars; we use no more than 7 + * \param[out] pnbsout number of bytes written to outbuf + * \return boolean for eof 0 if more data, 1 if end of file + * + *
+ * Notes:
+ *      (1) Attempts to read 4 bytes and write 5.
+ *      (2) Writes 1 byte if the value is 0.
+ * 
+ */ +static l_int32 +convertChunkToAscii85(const l_uint8 *inarray, + l_int32 insize, + l_int32 *pindex, + char *outbuf, + l_int32 *pnbout) +{ +l_uint8 inbyte; +l_uint32 inword, val; +l_int32 eof, index, nread, nbout, i; + + eof = FALSE; + index = *pindex; + nread = L_MIN(4, (insize - index)); + if (insize == index + nread) + eof = TRUE; + *pindex += nread; /* save new index */ + + /* Read input data and save in l_uint32 */ + inword = 0; + for (i = 0; i < nread; i++) { + inbyte = inarray[index + i]; + inword += inbyte << (8 * (3 - i)); + } + +#if 0 + fprintf(stderr, "index = %d, nread = %d\n", index, nread); + fprintf(stderr, "inword = %x\n", inword); + fprintf(stderr, "eof = %d\n", eof); +#endif + + /* Special case: output 1 byte only */ + if (inword == 0) { + outbuf[0] = 'z'; + nbout = 1; + } else { /* output nread + 1 bytes */ + for (i = 4; i >= 4 - nread; i--) { + val = inword / power85[i]; + outbuf[4 - i] = (l_uint8)(val + '!'); + inword -= val * power85[i]; + } + nbout = nread + 1; + } + *pnbout = nbout; + + return eof; +} + + +/*! + * \brief decodeAscii85() + * + * \param[in] inarray ascii85 input data + * \param[in] insize number of bytes in input array + * \param[out] poutsize number of bytes in output l_uint8 array + * \return outarray binary + * + *
+ * Notes:
+ *      (1) We assume the data is properly encoded, so we do not check
+ *          for invalid characters or the final '>' character.
+ *      (2) We permit whitespace to be added to the encoding in an
+ *          arbitrary way.
+ * 
+ */ +l_uint8 * +decodeAscii85(const char *inarray, + l_int32 insize, + l_int32 *poutsize) +{ +char inc; +const char *pin; +l_uint8 val; +l_uint8 *outa; +l_int32 maxsize, ocount, bytecount, index; +l_uint32 oword; + + PROCNAME("decodeAscii85"); + + if (!poutsize) + return (l_uint8 *)ERROR_PTR("&outsize not defined", procName, NULL); + *poutsize = 0; + if (!inarray) + return (l_uint8 *)ERROR_PTR("inarray not defined", procName, NULL); + if (insize <= 0) + return (l_uint8 *)ERROR_PTR("insize not > 0", procName, NULL); + + /* Accumulate results in outa */ + maxsize = (l_int32)(80. + (insize * 4. / 5.)); /* plenty big */ + if ((outa = (l_uint8 *)LEPT_CALLOC(maxsize, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("outa not made", procName, NULL); + + pin = inarray; + ocount = 0; /* byte index into outa */ + oword = 0; + for (index = 0, bytecount = 0; index < insize; index++, pin++) { + inc = *pin; + + if (inc == ' ' || inc == '\t' || inc == '\n' || + inc == '\f' || inc == '\r' || inc == '\v') /* ignore white space */ + continue; + + val = inc - '!'; + if (val < 85) { + oword = oword * 85 + val; + if (bytecount < 4) { + bytecount++; + } else { /* we have all 5 input chars for the oword */ + outa[ocount] = (oword >> 24) & 0xff; + outa[ocount + 1] = (oword >> 16) & 0xff; + outa[ocount + 2] = (oword >> 8) & 0xff; + outa[ocount + 3] = oword & 0xff; + ocount += 4; + bytecount = 0; + oword = 0; + } + } else if (inc == 'z' && bytecount == 0) { + outa[ocount] = 0; + outa[ocount + 1] = 0; + outa[ocount + 2] = 0; + outa[ocount + 3] = 0; + ocount += 4; + } else if (inc == '~') { /* end of data */ + L_INFO(" %d extra bytes output\n", procName, bytecount - 1); + switch (bytecount) { + case 0: /* normal eof */ + case 1: /* error */ + break; + case 2: /* 1 extra byte */ + oword = oword * power85[3] + 0xffffff; + outa[ocount] = (oword >> 24) & 0xff; + break; + case 3: /* 2 extra bytes */ + oword = oword * power85[2] + 0xffff; + outa[ocount] = (oword >> 24) & 0xff; + outa[ocount + 1] = (oword >> 16) & 0xff; + break; + case 4: /* 3 extra bytes */ + oword = oword * 85 + 0xff; + outa[ocount] = (oword >> 24) & 0xff; + outa[ocount + 1] = (oword >> 16) & 0xff; + outa[ocount + 2] = (oword >> 8) & 0xff; + break; + } + if (bytecount > 1) + ocount += (bytecount - 1); + break; + } + } + *poutsize = ocount; + + return outa; +} + + +/*-------------------------------------------------------------* + * String reformatting for base 64 encoded data * + *-------------------------------------------------------------*/ +/*! + * \brief reformatPacked64() + * + * \param[in] inarray base64 encoded string with newlines + * \param[in] insize number of bytes in input array + * \param[in] leadspace number of spaces in each line before the data + * \param[in] linechars number of bytes of data in each line; multiple of 4 + * \param[in] addquotes 1 to add quotes to each line of data; 0 to skip + * \param[out] poutsize number of bytes in output char array + * \return outarray ascii + * + *
+ * Notes:
+ *      (1) Each line in the output array has %leadspace space characters,
+ *          followed optionally by a double-quote, followed by %linechars
+ *          bytes of base64 data, followed optionally by a double-quote,
+ *          followed by a newline.
+ *      (2) This can be used to convert a base64 encoded string to a
+ *          string formatted for inclusion in a C source file.
+ * 
+ */ +char * +reformatPacked64(const char *inarray, + l_int32 insize, + l_int32 leadspace, + l_int32 linechars, + l_int32 addquotes, + l_int32 *poutsize) +{ +char *flata, *outa; +l_int32 i, j, flatindex, flatsize, outindex, nlines, linewithpad, linecount; + + PROCNAME("reformatPacked64"); + + if (!poutsize) + return (char *)ERROR_PTR("&outsize not defined", procName, NULL); + *poutsize = 0; + if (!inarray) + return (char *)ERROR_PTR("inarray not defined", procName, NULL); + if (insize <= 0) + return (char *)ERROR_PTR("insize not > 0", procName, NULL); + if (leadspace < 0) + return (char *)ERROR_PTR("leadspace must be >= 0", procName, NULL); + if (linechars % 4) + return (char *)ERROR_PTR("linechars % 4 must be 0", procName, NULL); + + /* Remove all white space */ + if ((flata = (char *)LEPT_CALLOC(insize, sizeof(char))) == NULL) + return (char *)ERROR_PTR("flata not made", procName, NULL); + for (i = 0, flatindex = 0; i < insize; i++) { + if (isBase64(inarray[i]) || inarray[i] == '=') + flata[flatindex++] = inarray[i]; + } + + /* Generate output string */ + flatsize = flatindex; + nlines = (flatsize + linechars - 1) / linechars; + linewithpad = leadspace + linechars + 1; /* including newline */ + if (addquotes) linewithpad += 2; + if ((outa = (char *)LEPT_CALLOC((size_t)nlines * linewithpad, + sizeof(char))) == NULL) { + LEPT_FREE(flata); + return (char *)ERROR_PTR("outa not made", procName, NULL); + } + for (j = 0, outindex = 0; j < leadspace; j++) + outa[outindex++] = ' '; + if (addquotes) outa[outindex++] = '"'; + for (i = 0, linecount = 0; i < flatsize; i++) { + if (linecount == linechars) { + if (addquotes) outa[outindex++] = '"'; + outa[outindex++] = '\n'; + for (j = 0; j < leadspace; j++) + outa[outindex++] = ' '; + if (addquotes) outa[outindex++] = '"'; + linecount = 0; + } + outa[outindex++] = flata[i]; + linecount++; + } + if (addquotes) outa[outindex++] = '"'; + *poutsize = outindex; + + LEPT_FREE(flata); + return outa; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/endianness.h b/hgdriver/3rdparty/hgOCR/leptonica/endianness.h new file mode 100644 index 0000000..e9eaba9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/endianness.h @@ -0,0 +1,11 @@ +#if !defined (L_BIG_ENDIAN) && !defined (L_LITTLE_ENDIAN) +# if 0 +# ifdef __BIG_ENDIAN__ +# define L_BIG_ENDIAN +# else +# define L_LITTLE_ENDIAN +# endif +# else +# define L_LITTLE_ENDIAN +# endif +#endif diff --git a/hgdriver/3rdparty/hgOCR/leptonica/enhance.c b/hgdriver/3rdparty/hgOCR/leptonica/enhance.c new file mode 100644 index 0000000..a34cd55 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/enhance.c @@ -0,0 +1,2353 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file enhance.c + *
+ *
+ *      Gamma TRC (tone reproduction curve) mapping
+ *           PIX     *pixGammaTRC()
+ *           PIX     *pixGammaTRCMasked()
+ *           PIX     *pixGammaTRCWithAlpha()
+ *           NUMA    *numaGammaTRC()
+ *
+ *      Contrast enhancement
+ *           PIX     *pixContrastTRC()
+ *           PIX     *pixContrastTRCMasked()
+ *           NUMA    *numaContrastTRC()
+ *
+ *      Histogram equalization
+ *           PIX     *pixEqualizeTRC()
+ *           NUMA    *numaEqualizeTRC()
+ *
+ *      Generic TRC mapper
+ *           PIX     *pixTRCMap()
+ *           PIX     *pixTRCMapGeneral()
+ *
+ *      Unsharp-masking
+ *           PIX     *pixUnsharpMasking()
+ *           PIX     *pixUnsharpMaskingGray()
+ *           PIX     *pixUnsharpMaskingFast()
+ *           PIX     *pixUnsharpMaskingGrayFast()
+ *           PIX     *pixUnsharpMaskingGray1D()
+ *           PIX     *pixUnsharpMaskingGray2D()
+ *
+ *      Hue and saturation modification
+ *           PIX     *pixModifyHue()
+ *           PIX     *pixModifySaturation()
+ *           l_int32  pixMeasureSaturation()
+ *           PIX     *pixModifyBrightness()
+ *
+ *      Color shifting
+ *           PIX     *pixMosaicColorShiftRGB()
+ *           PIX     *pixColorShiftRGB()
+ *
+ *      Darken gray (unsaturated) pixels
+ *           PIX     *pixDarkenGray()
+ *
+ *      General multiplicative constant color transform
+ *           PIX     *pixMultConstantColor()
+ *           PIX     *pixMultMatrixColor()
+ *
+ *      Edge by bandpass
+ *           PIX     *pixHalfEdgeByBandpass()
+ *
+ *      Gamma correction, contrast enhancement and histogram equalization
+ *      apply a simple mapping function to each pixel (or, for color
+ *      images, to each sample (i.e., r,g,b) of the pixel).
+ *
+ *       ~ Gamma correction either lightens the image or darkens
+ *         it, depending on whether the gamma factor is greater
+ *         or less than 1.0, respectively.
+ *
+ *       ~ Contrast enhancement darkens the pixels that are already
+ *         darker than the middle of the dynamic range (128)
+ *         and lightens pixels that are lighter than 128.
+ *
+ *       ~ Histogram equalization remaps to have the same number
+ *         of image pixels at each of 256 intensity values.  This is
+ *         a quick and dirty method of adjusting contrast and brightness
+ *         to bring out details in both light and dark regions.
+ *
+ *      Unsharp masking is a more complicated enhancement.
+ *      A "high frequency" image, generated by subtracting
+ *      the smoothed ("low frequency") part of the image from
+ *      itself, has all the energy at the edges.  This "edge image"
+ *      has 0 average value.  A fraction of the edge image is
+ *      then added to the original, enhancing the differences
+ *      between pixel values at edges.  Because we represent
+ *      images as l_uint8 arrays, we preserve dynamic range and
+ *      handle negative values by doing all the arithmetic on
+ *      shifted l_uint16 arrays; the l_uint8 values are recovered
+ *      at the end.
+ *
+ *      Hue and saturation modification work in HSV space.  Because
+ *      this is too large for efficient table lookup, each pixel value
+ *      is transformed to HSV, modified, and transformed back.
+ *      It's not the fastest way to do this, but the method is
+ *      easily understood.
+ *
+ *      Unsharp masking is never in-place, and returns a clone if no
+ *      operation is to be performed.
+ * 
+ */ + + +#include +#include "allheaders.h" + + /* Scales contrast enhancement factor to have a useful range + * between 0.0 and 1.0 */ +static const l_float32 EnhanceScaleFactor = 5.0; + +/*-------------------------------------------------------------* + * Gamma TRC (tone reproduction curve) mapping * + *-------------------------------------------------------------*/ +/*! + * \brief pixGammaTRC() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 8 or 32 bpp; or 2, 4 or 8 bpp with colormap + * \param[in] gamma gamma correction; must be > 0.0 + * \param[in] minval input value that gives 0 for output; can be < 0 + * \param[in] maxval input value that gives 255 for output; can be > 255 + * \return pixd always + * + *
+ * Notes:
+ *      (1) pixd must either be null or equal to pixs.
+ *          For in-place operation, set pixd == pixs:
+ *             pixGammaTRC(pixs, pixs, ...);
+ *          To get a new image, set pixd == null:
+ *             pixd = pixGammaTRC(NULL, pixs, ...);
+ *      (2) If pixs is colormapped, the colormap is transformed,
+ *          either in-place or in a copy of pixs.
+ *      (3) We use a gamma mapping between minval and maxval.
+ *      (4) If gamma < 1.0, the image will appear darker;
+ *          if gamma > 1.0, the image will appear lighter;
+ *      (5) If gamma = 1.0 and minval = 0 and maxval = 255, no
+ *          enhancement is performed; return a copy unless in-place,
+ *          in which case this is a no-op.
+ *      (6) For color images that are not colormapped, the mapping
+ *          is applied to each component.
+ *      (7) minval and maxval are not restricted to the interval [0, 255].
+ *          If minval < 0, an input value of 0 is mapped to a
+ *          nonzero output.  This will turn black to gray.
+ *          If maxval > 255, an input value of 255 is mapped to
+ *          an output value less than 255.  This will turn
+ *          white (e.g., in the background) to gray.
+ *      (8) Increasing minval darkens the image.
+ *      (9) Decreasing maxval bleaches the image.
+ *      (10) Simultaneously increasing minval and decreasing maxval
+ *           will darken the image and make the colors more intense;
+ *           e.g., minval = 50, maxval = 200.
+ *      (11) See numaGammaTRC() for further examples of use.
+ *      (12) Use pixTRCMapGeneral() if applying different mappings
+ *           to each channel in an RGB image.
+ * 
+ */ +PIX * +pixGammaTRC(PIX *pixd, + PIX *pixs, + l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +l_int32 d; +NUMA *nag; +PIXCMAP *cmap; + + PROCNAME("pixGammaTRC"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + if (gamma <= 0.0) { + L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); + gamma = 1.0; + } + if (minval >= maxval) + return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); + + if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */ + return pixCopy(pixd, pixs); + + if (!pixd) /* start with a copy if not in-place */ + pixd = pixCopy(NULL, pixs); + + if (cmap) { + pixcmapGammaTRC(pixGetColormap(pixd), gamma, minval, maxval); + return pixd; + } + + /* pixd is 8 or 32 bpp */ + if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) + return (PIX *)ERROR_PTR("nag not made", procName, pixd); + pixTRCMap(pixd, NULL, nag); + numaDestroy(&nag); + + return pixd; +} + + +/*! + * \brief pixGammaTRCMasked() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 8 or 32 bpp; not colormapped + * \param[in] pixm [optional] null or 1 bpp + * \param[in] gamma gamma correction; must be > 0.0 + * \param[in] minval input value that gives 0 for output; can be < 0 + * \param[in] maxval input value that gives 255 for output; can be > 255 + * \return pixd always + * + *
+ * Notes:
+ *      (1) Same as pixGammaTRC() except mapping is optionally over
+ *          a subset of pixels described by pixm.
+ *      (2) Masking does not work for colormapped images.
+ *      (3) See pixGammaTRC() for details on how to use the parameters.
+ * 
+ */ +PIX * +pixGammaTRCMasked(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +l_int32 d; +NUMA *nag; + + PROCNAME("pixGammaTRCMasked"); + + if (!pixm) + return pixGammaTRC(pixd, pixs, gamma, minval, maxval); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); + if (minval >= maxval) + return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); + if (gamma <= 0.0) { + L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); + gamma = 1.0; + } + + if (gamma == 1.0 && minval == 0 && maxval == 255) + return pixCopy(pixd, pixs); + + if (!pixd) /* start with a copy if not in-place */ + pixd = pixCopy(NULL, pixs); + + if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) + return (PIX *)ERROR_PTR("nag not made", procName, pixd); + pixTRCMap(pixd, pixm, nag); + numaDestroy(&nag); + + return pixd; +} + + +/*! + * \brief pixGammaTRCWithAlpha() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 32 bpp + * \param[in] gamma gamma correction; must be > 0.0 + * \param[in] minval input value that gives 0 for output; can be < 0 + * \param[in] maxval input value that gives 255 for output; can be > 255 + * \return pixd always + * + *
+ * Notes:
+ *      (1) See usage notes in pixGammaTRC().
+ *      (2) This version saves the alpha channel.  It is only valid
+ *          for 32 bpp (no colormap), and is a bit slower.
+ * 
+ */ +PIX * +pixGammaTRCWithAlpha(PIX *pixd, + PIX *pixs, + l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +NUMA *nag; +PIX *pixalpha; + + PROCNAME("pixGammaTRCWithAlpha"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + if (gamma <= 0.0) { + L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); + gamma = 1.0; + } + if (minval >= maxval) + return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); + + if (gamma == 1.0 && minval == 0 && maxval == 255) + return pixCopy(pixd, pixs); + if (!pixd) /* start with a copy if not in-place */ + pixd = pixCopy(NULL, pixs); + + pixalpha = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); /* save */ + if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) + return (PIX *)ERROR_PTR("nag not made", procName, pixd); + pixTRCMap(pixd, NULL, nag); + pixSetRGBComponent(pixd, pixalpha, L_ALPHA_CHANNEL); /* restore */ + pixSetSpp(pixd, 4); + + numaDestroy(&nag); + pixDestroy(&pixalpha); + return pixd; +} + + +/*! + * \brief numaGammaTRC() + * + * \param[in] gamma gamma factor; must be > 0.0 + * \param[in] minval input value that gives 0 for output + * \param[in] maxval input value that gives 255 for output + * \return na, or NULL on error + * + *
+ * Notes:
+ *      (1) The map is returned as a numa; values are clipped to [0, 255].
+ *      (2) To force all intensities into a range within fraction delta
+ *          of white, use: minval = -256 * (1 - delta) / delta
+ *                         maxval = 255
+ *      (3) To force all intensities into a range within fraction delta
+ *          of black, use: minval = 0
+ *                         maxval = 256 * (1 - delta) / delta
+ * 
+ */ +NUMA * +numaGammaTRC(l_float32 gamma, + l_int32 minval, + l_int32 maxval) +{ +l_int32 i, val; +l_float32 x, invgamma; +NUMA *na; + + PROCNAME("numaGammaTRC"); + + if (minval >= maxval) + return (NUMA *)ERROR_PTR("minval not < maxval", procName, NULL); + if (gamma <= 0.0) { + L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); + gamma = 1.0; + } + + invgamma = 1. / gamma; + na = numaCreate(256); + for (i = 0; i < minval; i++) + numaAddNumber(na, 0); + for (i = minval; i <= maxval; i++) { + if (i < 0) continue; + if (i > 255) continue; + x = (l_float32)(i - minval) / (l_float32)(maxval - minval); + val = (l_int32)(255. * powf(x, invgamma) + 0.5); + val = L_MAX(val, 0); + val = L_MIN(val, 255); + numaAddNumber(na, val); + } + for (i = maxval + 1; i < 256; i++) + numaAddNumber(na, 255); + + return na; +} + + +/*-------------------------------------------------------------* + * Contrast enhancement * + *-------------------------------------------------------------*/ +/*! + * \brief pixContrastTRC() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 8 or 32 bpp; or 2, 4 or 8 bpp with colormap + * \param[in] factor 0.0 is no enhancement + * \return pixd always + * + *
+ * Notes:
+ *      (1) pixd must either be null or equal to pixs.
+ *          For in-place operation, set pixd == pixs:
+ *             pixContrastTRC(pixs, pixs, ...);
+ *          To get a new image, set pixd == null:
+ *             pixd = pixContrastTRC(NULL, pixs, ...);
+ *      (2) If pixs is colormapped, the colormap is transformed,
+ *          either in-place or in a copy of pixs.
+ *      (3) Contrast is enhanced by mapping each color component
+ *          using an atan function with maximum slope at 127.
+ *          Pixels below 127 are lowered in intensity and pixels
+ *          above 127 are increased.
+ *      (4) The useful range for the contrast factor is scaled to
+ *          be in (0.0 to 1.0), but larger values can also be used.
+ *      (5) If factor == 0.0, no enhancement is performed; return a copy
+ *          unless in-place, in which case this is a no-op.
+ *      (6) For color images that are not colormapped, the mapping
+ *          is applied to each component.
+ * 
+ */ +PIX * +pixContrastTRC(PIX *pixd, + PIX *pixs, + l_float32 factor) +{ +l_int32 d; +NUMA *nac; +PIXCMAP *cmap; + + PROCNAME("pixContrastTRC"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + if (factor < 0.0) { + L_WARNING("factor must be >= 0.0; using 0.0\n", procName); + factor = 0.0; + } + if (factor == 0.0) + return pixCopy(pixd, pixs); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); + + if (!pixd) /* start with a copy if not in-place */ + pixd = pixCopy(NULL, pixs); + + if (cmap) { + pixcmapContrastTRC(pixGetColormap(pixd), factor); + return pixd; + } + + /* pixd is 8 or 32 bpp */ + if ((nac = numaContrastTRC(factor)) == NULL) + return (PIX *)ERROR_PTR("nac not made", procName, pixd); + pixTRCMap(pixd, NULL, nac); + numaDestroy(&nac); + + return pixd; +} + + +/*! + * \brief pixContrastTRCMasked() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 8 or 32 bpp; or 2, 4 or 8 bpp with colormap + * \param[in] pixm [optional] null or 1 bpp + * \param[in] factor 0.0 is no enhancement + * \return pixd always + * + *
+ * Notes:
+ *      (1) Same as pixContrastTRC() except mapping is optionally over
+ *          a subset of pixels described by pixm.
+ *      (2) Masking does not work for colormapped images.
+ *      (3) See pixContrastTRC() for details on how to use the parameters.
+ * 
+ */ +PIX * +pixContrastTRCMasked(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_float32 factor) +{ +l_int32 d; +NUMA *nac; + + PROCNAME("pixContrastTRCMasked"); + + if (!pixm) + return pixContrastTRC(pixd, pixs, factor); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); + + if (factor < 0.0) { + L_WARNING("factor must be >= 0.0; using 0.0\n", procName); + factor = 0.0; + } + if (factor == 0.0) + return pixCopy(pixd, pixs); + + if (!pixd) /* start with a copy if not in-place */ + pixd = pixCopy(NULL, pixs); + + if ((nac = numaContrastTRC(factor)) == NULL) + return (PIX *)ERROR_PTR("nac not made", procName, pixd); + pixTRCMap(pixd, pixm, nac); + numaDestroy(&nac); + + return pixd; +} + + +/*! + * \brief numaContrastTRC() + * + * \param[in] factor generally between 0.0 [no enhancement] + * and 1.0, but can be larger than 1.0 + * \return na, or NULL on error + * + *
+ * Notes:
+ *      (1) The mapping is monotonic increasing, where 0 is mapped
+ *          to 0 and 255 is mapped to 255.
+ *      (2) As 'factor' is increased from 0.0 (where the mapping is linear),
+ *          the map gets closer to its limit as a step function that
+ *          jumps from 0 to 255 at the center (input value = 127).
+ * 
+ */ +NUMA * +numaContrastTRC(l_float32 factor) +{ +l_int32 i, val; +l_float64 x, ymax, ymin, dely, scale; +NUMA *na; + + PROCNAME("numaContrastTRC"); + + if (factor < 0.0) { + L_WARNING("factor must be >= 0.0; using 0.0; no enhancement\n", + procName); + factor = 0.0; + } + if (factor == 0.0) + return numaMakeSequence(0, 1, 256); /* linear map */ + + scale = EnhanceScaleFactor; + ymax = atan((l_float64)(1.0 * factor * scale)); + ymin = atan((l_float64)(-127. * factor * scale / 128.)); + dely = ymax - ymin; + na = numaCreate(256); + for (i = 0; i < 256; i++) { + x = (l_float64)i; + val = (l_int32)((255. / dely) * + (-ymin + atan((l_float64)(factor * scale * (x - 127.) / 128.))) + + 0.5); + numaAddNumber(na, val); + } + + return na; +} + + +/*-------------------------------------------------------------* + * Histogram equalization * + *-------------------------------------------------------------*/ +/*! + * \brief pixEqualizeTRC() + * + * \param[in] pixd [optional] null or equal to pixs + * \param[in] pixs 8 bpp gray, 32 bpp rgb, or colormapped + * \param[in] fract fraction of equalization movement of pixel values + * \param[in] factor subsampling factor; integer >= 1 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) pixd must either be null or equal to pixs.
+ *          For in-place operation, set pixd == pixs:
+ *             pixEqualizeTRC(pixs, pixs, ...);
+ *          To get a new image, set pixd == null:
+ *             pixd = pixEqualizeTRC(NULL, pixs, ...);
+ *      (2) In histogram equalization, a tone reproduction curve
+ *          mapping is used to make the number of pixels at each
+ *          intensity equal.
+ *      (3) If fract == 0.0, no equalization is performed; return a copy
+ *          unless in-place, in which case this is a no-op.
+ *          If fract == 1.0, equalization is complete.
+ *      (4) Set the subsampling factor > 1 to reduce the amount of computation.
+ *      (5) If pixs is colormapped, the colormap is removed and
+ *          converted to rgb or grayscale.
+ *      (6) If pixs has color, equalization is done in each channel
+ *          separately.
+ *      (7) Note that even if there is a colormap, we can get an
+ *          in-place operation because the intermediate image pixt
+ *          is copied back to pixs (which for in-place is the same
+ *          as pixd).
+ * 
+ */ +PIX * +pixEqualizeTRC(PIX *pixd, + PIX *pixs, + l_float32 fract, + l_int32 factor) +{ +l_int32 d; +NUMA *na; +PIX *pixt, *pix8; +PIXCMAP *cmap; + + PROCNAME("pixEqualizeTRC"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (d != 8 && d != 32 && !cmap) + return (PIX *)ERROR_PTR("pixs not 8/32 bpp or cmapped", procName, NULL); + if (fract < 0.0 || fract > 1.0) + return (PIX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); + + if (fract == 0.0) + return pixCopy(pixd, pixs); + + /* If there is a colormap, remove it. */ + if (cmap) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pixt = pixClone(pixs); + + /* Make a copy if necessary */ + pixd = pixCopy(pixd, pixt); + pixDestroy(&pixt); + + d = pixGetDepth(pixd); + if (d == 8) { + na = numaEqualizeTRC(pixd, fract, factor); + pixTRCMap(pixd, NULL, na); + numaDestroy(&na); + } else { /* 32 bpp */ + pix8 = pixGetRGBComponent(pixd, COLOR_RED); + na = numaEqualizeTRC(pix8, fract, factor); + pixTRCMap(pix8, NULL, na); + pixSetRGBComponent(pixd, pix8, COLOR_RED); + numaDestroy(&na); + pixDestroy(&pix8); + pix8 = pixGetRGBComponent(pixd, COLOR_GREEN); + na = numaEqualizeTRC(pix8, fract, factor); + pixTRCMap(pix8, NULL, na); + pixSetRGBComponent(pixd, pix8, COLOR_GREEN); + numaDestroy(&na); + pixDestroy(&pix8); + pix8 = pixGetRGBComponent(pixd, COLOR_BLUE); + na = numaEqualizeTRC(pix8, fract, factor); + pixTRCMap(pix8, NULL, na); + pixSetRGBComponent(pixd, pix8, COLOR_BLUE); + numaDestroy(&na); + pixDestroy(&pix8); + } + + return pixd; +} + + +/*! + * \brief numaEqualizeTRC() + * + * \param[in] pix 8 bpp, no colormap + * \param[in] fract fraction of equalization movement of pixel values + * \param[in] factor subsampling factor; integer >= 1 + * \return nad, or NULL on error + * + *
+ * Notes:
+ *      (1) If fract == 0.0, no equalization will be performed.
+ *          If fract == 1.0, equalization is complete.
+ *      (2) Set the subsampling factor > 1 to reduce the amount of computation.
+ *      (3) The map is returned as a numa with 256 values, specifying
+ *          the equalized value (array value) for every input value
+ *          (the array index).
+ * 
+ */ +NUMA * +numaEqualizeTRC(PIX *pix, + l_float32 fract, + l_int32 factor) +{ +l_int32 iin, iout, itarg; +l_float32 val, sum; +NUMA *nah, *nasum, *nad; + + PROCNAME("numaEqualizeTRC"); + + if (!pix) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + if (pixGetDepth(pix) != 8) + return (NUMA *)ERROR_PTR("pix not 8 bpp", procName, NULL); + if (fract < 0.0 || fract > 1.0) + return (NUMA *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling factor < 1", procName, NULL); + + if (fract == 0.0) + L_WARNING("fract = 0.0; no equalization requested\n", procName); + + if ((nah = pixGetGrayHistogram(pix, factor)) == NULL) + return (NUMA *)ERROR_PTR("histogram not made", procName, NULL); + numaGetSum(nah, &sum); + nasum = numaGetPartialSums(nah); + + nad = numaCreate(256); + for (iin = 0; iin < 256; iin++) { + numaGetFValue(nasum, iin, &val); + itarg = (l_int32)(255. * val / sum + 0.5); + iout = iin + (l_int32)(fract * (itarg - iin)); + iout = L_MIN(iout, 255); /* to be safe */ + numaAddNumber(nad, iout); + } + + numaDestroy(&nah); + numaDestroy(&nasum); + return nad; +} + + +/*-------------------------------------------------------------* + * Generic TRC mapping * + *-------------------------------------------------------------*/ +/*! + * \brief pixTRCMap() + * + * \param[in] pixs 8 grayscale or 32 bpp rgb; not colormapped + * \param[in] pixm [optional] 1 bpp mask + * \param[in] na mapping array + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This operation is in-place on pixs.
+ *      (2) For 32 bpp, this applies the same map to each of the r,g,b
+ *          components.
+ *      (3) The mapping array is of size 256, and it maps the input
+ *          index into values in the range [0, 255].
+ *      (4) If defined, the optional 1 bpp mask pixm has its origin
+ *          aligned with pixs, and the map function is applied only
+ *          to pixels in pixs under the fg of pixm.
+ *      (5) For 32 bpp, this does not save the alpha channel.
+ * 
+ */ +l_int32 +pixTRCMap(PIX *pixs, + PIX *pixm, + NUMA *na) +{ +l_int32 w, h, d, wm, hm, wpl, wplm, i, j, sval8, dval8; +l_uint32 sval32, dval32; +l_uint32 *data, *datam, *line, *linem, *tab; + + PROCNAME("pixTRCMap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (numaGetCount(na) != 256) + return ERROR_INT("na not of size 256", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32) + return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); + if (pixm) { + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + } + + tab = (l_uint32 *)numaGetIArray(na); /* get the array for efficiency */ + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + if (!pixm) { + if (d == 8) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + sval8 = GET_DATA_BYTE(line, j); + dval8 = tab[sval8]; + SET_DATA_BYTE(line, j, dval8); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + sval32 = *(line + j); + dval32 = + tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | + tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | + tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; + *(line + j) = dval32; + } + } + } + } else { + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixm, &wm, &hm, NULL); + if (d == 8) { + for (i = 0; i < h; i++) { + if (i >= hm) + break; + line = data + i * wpl; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if (j >= wm) + break; + if (GET_DATA_BIT(linem, j) == 0) + continue; + sval8 = GET_DATA_BYTE(line, j); + dval8 = tab[sval8]; + SET_DATA_BYTE(line, j, dval8); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + if (i >= hm) + break; + line = data + i * wpl; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if (j >= wm) + break; + if (GET_DATA_BIT(linem, j) == 0) + continue; + sval32 = *(line + j); + dval32 = + tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | + tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | + tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; + *(line + j) = dval32; + } + } + } + } + + LEPT_FREE(tab); + return 0; +} + + +/*! + * \brief pixTRCMapGeneral() + * + * \param[in] pixs 32 bpp rgb; not colormapped + * \param[in] pixm [optional] 1 bpp mask + * \param[in] nar, nag, nab mapping arrays + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This operation is in-place on %pixs.
+ *      (2) Each of the r,g,b mapping arrays is of size 256. They map the
+ *          input value for that color component into values in the
+ *          range [0, 255].
+ *      (3) In the special case where the r, g and b mapping arrays are
+ *          all the same, call pixTRCMap() instead.
+ *      (4) If defined, the optional 1 bpp mask %pixm has its origin
+ *          aligned with %pixs, and the map function is applied only
+ *          to pixels in %pixs under the fg of pixm.
+ *      (5) The alpha channel is not saved.
+ * 
+ */ +l_int32 +pixTRCMapGeneral(PIX *pixs, + PIX *pixm, + NUMA *nar, + NUMA *nag, + NUMA *nab) +{ +l_int32 w, h, wm, hm, wpl, wplm, i, j; +l_uint32 sval32, dval32; +l_uint32 *data, *datam, *line, *linem, *tabr, *tabg, *tabb; + + PROCNAME("pixTRCMapGeneral"); + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm defined and not 1 bpp", procName, 1); + if (!nar || !nag || !nab) + return ERROR_INT("na{r,g,b} not all defined", procName, 1); + if (numaGetCount(nar) != 256 || numaGetCount(nag) != 256 || + numaGetCount(nab) != 256) + return ERROR_INT("na{r,g,b} not all of size 256", procName, 1); + + /* Get the arrays for efficiency */ + tabr = (l_uint32 *)numaGetIArray(nar); + tabg = (l_uint32 *)numaGetIArray(nag); + tabb = (l_uint32 *)numaGetIArray(nab); + pixGetDimensions(pixs, &w, &h, NULL); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + if (!pixm) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + sval32 = *(line + j); + dval32 = + tabr[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | + tabg[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | + tabb[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; + *(line + j) = dval32; + } + } + } else { + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixm, &wm, &hm, NULL); + for (i = 0; i < h; i++) { + if (i >= hm) + break; + line = data + i * wpl; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if (j >= wm) + break; + if (GET_DATA_BIT(linem, j) == 0) + continue; + sval32 = *(line + j); + dval32 = + tabr[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | + tabg[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | + tabb[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; + *(line + j) = dval32; + } + } + } + + LEPT_FREE(tabr); + LEPT_FREE(tabg); + LEPT_FREE(tabb); + return 0; +} + + + +/*-----------------------------------------------------------------------* + * Unsharp masking * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixUnsharpMasking() + * + * \param[in] pixs all depths except 1 bpp; with or without colormaps + * \param[in] halfwidth "half-width" of smoothing filter + * \param[in] fract fraction of edge added back into image + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) We use symmetric smoothing filters of odd dimension,
+ *          typically use sizes of 3, 5, 7, etc.  The %halfwidth parameter
+ *          for these is (size - 1)/2; i.e., 1, 2, 3, etc.
+ *      (2) The fract parameter is typically taken in the
+ *          range:  0.2 < fract < 0.7
+ *      (3) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMasking(PIX *pixs, + l_int32 halfwidth, + l_float32 fract) +{ +l_int32 d; +PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; + + PROCNAME("pixUnsharpMasking"); + + if (!pixs || (pixGetDepth(pixs) == 1)) + return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + + if (halfwidth == 1 || halfwidth == 2) + return pixUnsharpMaskingFast(pixs, halfwidth, fract, L_BOTH_DIRECTIONS); + + /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ + if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* Sharpen */ + d = pixGetDepth(pixt); + if (d == 8) { + pixd = pixUnsharpMaskingGray(pixt, halfwidth, fract); + } else { /* d == 32 */ + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixrs = pixUnsharpMaskingGray(pixr, halfwidth, fract); + pixDestroy(&pixr); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixgs = pixUnsharpMaskingGray(pixg, halfwidth, fract); + pixDestroy(&pixg); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + pixbs = pixUnsharpMaskingGray(pixb, halfwidth, fract); + pixDestroy(&pixb); + pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); + pixDestroy(&pixrs); + pixDestroy(&pixgs); + pixDestroy(&pixbs); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixUnsharpMaskingGray() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] halfwidth "half-width" of smoothing filter + * \param[in] fract fraction of edge added back into image + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) We use symmetric smoothing filters of odd dimension,
+ *          typically use sizes of 3, 5, 7, etc.  The %halfwidth parameter
+ *          for these is (size - 1)/2; i.e., 1, 2, 3, etc.
+ *      (2) The fract parameter is typically taken in the range:
+ *          0.2 < fract < 0.7
+ *      (3) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMaskingGray(PIX *pixs, + l_int32 halfwidth, + l_float32 fract) +{ +l_int32 w, h, d; +PIX *pixc, *pixd; +PIXACC *pixacc; + + PROCNAME("pixUnsharpMaskingGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 || pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + if (halfwidth == 1 || halfwidth == 2) + return pixUnsharpMaskingGrayFast(pixs, halfwidth, fract, + L_BOTH_DIRECTIONS); + + if ((pixc = pixBlockconvGray(pixs, NULL, halfwidth, halfwidth)) == NULL) + return (PIX *)ERROR_PTR("pixc not made", procName, NULL); + + /* Steps: + * (1) edge image is pixs - pixc (this is highpass part) + * (2) multiply edge image by fract + * (3) add fraction of edge to pixs + * + * To show how this is done with both interfaces to arithmetic + * on integer Pix, here is the implementation in the lower-level + * function calls: + * pixt = pixInitAccumulate(w, h, 0x10000000)) == NULL) + * pixAccumulate(pixt, pixs, L_ARITH_ADD); + * pixAccumulate(pixt, pixc, L_ARITH_SUBTRACT); + * pixMultConstAccumulate(pixt, fract, 0x10000000); + * pixAccumulate(pixt, pixs, L_ARITH_ADD); + * pixd = pixFinalAccumulate(pixt, 0x10000000, 8)) == NULL) + * pixDestroy(&pixt); + * + * The code below does the same thing using the Pixacc accumulator, + * hiding the details of the offset that is needed for subtraction. + */ + pixacc = pixaccCreate(w, h, 1); + pixaccAdd(pixacc, pixs); + pixaccSubtract(pixacc, pixc); + pixaccMultConst(pixacc, fract); + pixaccAdd(pixacc, pixs); + pixd = pixaccFinal(pixacc, 8); + pixaccDestroy(&pixacc); + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixUnsharpMaskingFast() + * + * \param[in] pixs all depths except 1 bpp; with or without colormaps + * \param[in] halfwidth "half-width" of smoothing filter; 1 and 2 only + * \param[in] fract fraction of high frequency added to image + * \param[in] direction L_HORIZ, L_VERT, L_BOTH_DIRECTIONS + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The fast version uses separable 1-D filters directly on
+ *          the input image.  The halfwidth is either 1 (full width = 3)
+ *          or 2 (full width = 5).
+ *      (2) The fract parameter is typically taken in the
+ *            range:  0.2 < fract < 0.7
+ *      (3) To skip horizontal sharpening, use %fracth = 0.0; ditto for %fractv
+ *      (4) For one dimensional filtering (as an example):
+ *          For %halfwidth = 1, the low-pass filter is
+ *              L:    1/3    1/3   1/3
+ *          and the high-pass filter is
+ *              H = I - L:   -1/3   2/3   -1/3
+ *          For %halfwidth = 2, the low-pass filter is
+ *              L:    1/5    1/5   1/5    1/5    1/5
+ *          and the high-pass filter is
+ *              H = I - L:   -1/5  -1/5   4/5  -1/5   -1/5
+ *          The new sharpened pixel value is found by adding some fraction
+ *          of the high-pass filter value (which sums to 0) to the
+ *          initial pixel value:
+ *              N = I + fract * H
+ *      (5) For 2D, the sharpening filter is not separable, because the
+ *          vertical filter depends on the horizontal location relative
+ *          to the filter origin, and v.v.   So we either do the full
+ *          2D filter (for %halfwidth == 1) or do the low-pass
+ *          convolution separably and then compose with the original pix.
+ *      (6) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMaskingFast(PIX *pixs, + l_int32 halfwidth, + l_float32 fract, + l_int32 direction) +{ +l_int32 d; +PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; + + PROCNAME("pixUnsharpMaskingFast"); + + if (!pixs || (pixGetDepth(pixs) == 1)) + return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + if (halfwidth != 1 && halfwidth != 2) + return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT && + direction != L_BOTH_DIRECTIONS) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + + /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ + if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* Sharpen */ + d = pixGetDepth(pixt); + if (d == 8) { + pixd = pixUnsharpMaskingGrayFast(pixt, halfwidth, fract, direction); + } else { /* d == 32 */ + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixrs = pixUnsharpMaskingGrayFast(pixr, halfwidth, fract, direction); + pixDestroy(&pixr); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixgs = pixUnsharpMaskingGrayFast(pixg, halfwidth, fract, direction); + pixDestroy(&pixg); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + pixbs = pixUnsharpMaskingGrayFast(pixb, halfwidth, fract, direction); + pixDestroy(&pixb); + pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); + pixDestroy(&pixrs); + pixDestroy(&pixgs); + pixDestroy(&pixbs); + } + + pixDestroy(&pixt); + return pixd; +} + + + +/*! + * \brief pixUnsharpMaskingGrayFast() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 + * \param[in] fract fraction of high frequency added to image + * \param[in] direction L_HORIZ, L_VERT, L_BOTH_DIRECTIONS + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) For usage and explanation of the algorithm, see notes
+ *          in pixUnsharpMaskingFast().
+ *      (2) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMaskingGrayFast(PIX *pixs, + l_int32 halfwidth, + l_float32 fract, + l_int32 direction) +{ +PIX *pixd; + + PROCNAME("pixUnsharpMaskingGrayFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8 || pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + if (halfwidth != 1 && halfwidth != 2) + return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT && + direction != L_BOTH_DIRECTIONS) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + + if (direction != L_BOTH_DIRECTIONS) + pixd = pixUnsharpMaskingGray1D(pixs, halfwidth, fract, direction); + else /* 2D sharpening */ + pixd = pixUnsharpMaskingGray2D(pixs, halfwidth, fract); + + return pixd; +} + + +/*! + * \brief pixUnsharpMaskingGray1D() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 + * \param[in] fract fraction of high frequency added to image + * \param[in] direction filtering direction; use L_HORIZ or L_VERT + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) For usage and explanation of the algorithm, see notes
+ *          in pixUnsharpMaskingFast().
+ *      (2) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMaskingGray1D(PIX *pixs, + l_int32 halfwidth, + l_float32 fract, + l_int32 direction) +{ +l_int32 w, h, d, wpls, wpld, i, j, ival; +l_uint32 *datas, *datad; +l_uint32 *lines, *lines0, *lines1, *lines2, *lines3, *lines4, *lined; +l_float32 val, a[5]; +PIX *pixd; + + PROCNAME("pixUnsharpMaskingGray1D"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 || pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + if (halfwidth != 1 && halfwidth != 2) + return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); + + /* Initialize pixd with pixels from pixs that will not be + * set when computing the sharpened values. */ + pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, + halfwidth, halfwidth); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + if (halfwidth == 1) { + a[0] = -fract / 3.0; + a[1] = 1.0 + fract * 2.0 / 3.0; + a[2] = a[0]; + } else { /* halfwidth == 2 */ + a[0] = -fract / 5.0; + a[1] = a[0]; + a[2] = 1.0 + fract * 4.0 / 5.0; + a[3] = a[0]; + a[4] = a[0]; + } + + if (direction == L_HORIZ) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (halfwidth == 1) { + for (j = 1; j < w - 1; j++) { + val = a[0] * GET_DATA_BYTE(lines, j - 1) + + a[1] * GET_DATA_BYTE(lines, j) + + a[2] * GET_DATA_BYTE(lines, j + 1); + ival = (l_int32)val; + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } else { /* halfwidth == 2 */ + for (j = 2; j < w - 2; j++) { + val = a[0] * GET_DATA_BYTE(lines, j - 2) + + a[1] * GET_DATA_BYTE(lines, j - 1) + + a[2] * GET_DATA_BYTE(lines, j) + + a[3] * GET_DATA_BYTE(lines, j + 1) + + a[4] * GET_DATA_BYTE(lines, j + 2); + ival = (l_int32)val; + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } + } + } else { /* direction == L_VERT */ + if (halfwidth == 1) { + for (i = 1; i < h - 1; i++) { + lines0 = datas + (i - 1) * wpls; + lines1 = datas + i * wpls; + lines2 = datas + (i + 1) * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = a[0] * GET_DATA_BYTE(lines0, j) + + a[1] * GET_DATA_BYTE(lines1, j) + + a[2] * GET_DATA_BYTE(lines2, j); + ival = (l_int32)val; + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } + } else { /* halfwidth == 2 */ + for (i = 2; i < h - 2; i++) { + lines0 = datas + (i - 2) * wpls; + lines1 = datas + (i - 1) * wpls; + lines2 = datas + i * wpls; + lines3 = datas + (i + 1) * wpls; + lines4 = datas + (i + 2) * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = a[0] * GET_DATA_BYTE(lines0, j) + + a[1] * GET_DATA_BYTE(lines1, j) + + a[2] * GET_DATA_BYTE(lines2, j) + + a[3] * GET_DATA_BYTE(lines3, j) + + a[4] * GET_DATA_BYTE(lines4, j); + ival = (l_int32)val; + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } + } + } + + return pixd; +} + + +/*! + * \brief pixUnsharpMaskingGray2D() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 + * \param[in] fract fraction of high frequency added to image + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is for %halfwidth == 1, 2.
+ *      (2) The lowpass filter is implemented separably.
+ *      (3) Returns a clone if no sharpening is requested.
+ * 
+ */ +PIX * +pixUnsharpMaskingGray2D(PIX *pixs, + l_int32 halfwidth, + l_float32 fract) +{ +l_int32 w, h, d, wpls, wpld, wplf, i, j, ival, sval; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 val, norm; +l_float32 *dataf, *linef, *linef0, *linef1, *linef2, *linef3, *linef4; +PIX *pixd; +FPIX *fpix; + + PROCNAME("pixUnsharpMaskingGray2D"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 || pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); + if (fract <= 0.0 || halfwidth <= 0) { + L_WARNING("no sharpening requested; clone returned\n", procName); + return pixClone(pixs); + } + if (halfwidth != 1 && halfwidth != 2) + return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); + + if ((pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, + halfwidth, halfwidth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Do the low pass separably. Store the result of horizontal + * smoothing in an intermediate fpix. */ + if ((fpix = fpixCreate(w, h)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("fpix not made", procName, NULL); + } + dataf = fpixGetData(fpix); + wplf = fpixGetWpl(fpix); + if (halfwidth == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linef = dataf + i * wplf; + for (j = 1; j < w - 1; j++) { + val = GET_DATA_BYTE(lines, j - 1) + + GET_DATA_BYTE(lines, j) + + GET_DATA_BYTE(lines, j + 1); + linef[j] = val; + } + } + } else { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linef = dataf + i * wplf; + for (j = 2; j < w - 2; j++) { + val = GET_DATA_BYTE(lines, j - 2) + + GET_DATA_BYTE(lines, j - 1) + + GET_DATA_BYTE(lines, j) + + GET_DATA_BYTE(lines, j + 1) + + GET_DATA_BYTE(lines, j + 2); + linef[j] = val; + } + } + } + + /* Do vertical smoothing to finish the low-pass filter. + * At each pixel, if L is the lowpass value, I is the + * src pixel value and f is the fraction of highpass to + * be added to I, then the highpass filter value is + * H = I - L + * and the new sharpened value is + * N = I + f * H. */ + if (halfwidth == 1) { + for (i = 1; i < h - 1; i++) { + linef0 = dataf + (i - 1) * wplf; + linef1 = dataf + i * wplf; + linef2 = dataf + (i + 1) * wplf; + lined = datad + i * wpld; + lines = datas + i * wpls; + norm = 1.0 / 9.0; + for (j = 1; j < w - 1; j++) { + val = norm * (linef0[j] + linef1[j] + + linef2[j]); /* L: lowpass filter value */ + sval = GET_DATA_BYTE(lines, j); /* I: source pixel */ + ival = (l_int32)(sval + fract * (sval - val) + 0.5); + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } + } else { + for (i = 2; i < h - 2; i++) { + linef0 = dataf + (i - 2) * wplf; + linef1 = dataf + (i - 1) * wplf; + linef2 = dataf + i * wplf; + linef3 = dataf + (i + 1) * wplf; + linef4 = dataf + (i + 2) * wplf; + lined = datad + i * wpld; + lines = datas + i * wpls; + norm = 1.0 / 25.0; + for (j = 2; j < w - 2; j++) { + val = norm * (linef0[j] + linef1[j] + linef2[j] + linef3[j] + + linef4[j]); /* L: lowpass filter value */ + sval = GET_DATA_BYTE(lines, j); /* I: source pixel */ + ival = (l_int32)(sval + fract * (sval - val) + 0.5); + ival = L_MAX(0, ival); + ival = L_MIN(255, ival); + SET_DATA_BYTE(lined, j, ival); + } + } + } + + fpixDestroy(&fpix); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Hue and saturation modification * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixModifyHue() + * + * \param[in] pixd [optional] can be null or equal to pixs + * \param[in] pixs 32 bpp rgb + * \param[in] fract between -1.0 and 1.0 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) pixd must either be null or equal to pixs.
+ *          For in-place operation, set pixd == pixs:
+ *             pixEqualizeTRC(pixs, pixs, ...);
+ *          To get a new image, set pixd == null:
+ *             pixd = pixEqualizeTRC(NULL, pixs, ...);
+ *      (1) Use fract > 0.0 to increase hue value; < 0.0 to decrease it.
+ *          1.0 (or -1.0) represents a 360 degree rotation; i.e., no change.
+ *      (2) If no modification is requested (fract = -1.0 or 0 or 1.0),
+ *          return a copy unless in-place, in which case this is a no-op.
+ *      (3) See discussion of color-modification methods, in coloring.c.
+ * 
+ */ +PIX * +pixModifyHue(PIX *pixd, + PIX *pixs, + l_float32 fract) +{ +l_int32 w, h, d, i, j, wpl, delhue; +l_int32 rval, gval, bval, hval, sval, vval; +l_uint32 *data, *line; + + PROCNAME("pixModifyHue"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs colormapped", procName, NULL); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (L_ABS(fract) > 1.0) + return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); + + pixd = pixCopy(pixd, pixs); + + delhue = (l_int32)(240 * fract); + if (delhue == 0 || delhue == 240 || delhue == -240) { + L_WARNING("no change requested in hue\n", procName); + return pixd; + } + if (delhue < 0) + delhue += 240; + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + hval = (hval + delhue) % 240; + convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, line + j); + } + } + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); + + return pixd; +} + + +/*! + * \brief pixModifySaturation() + * + * \param[in] pixd [optional] can be null, existing or equal to pixs + * \param[in] pixs 32 bpp rgb + * \param[in] fract between -1.0 and 1.0 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If fract > 0.0, it gives the fraction that the pixel
+ *          saturation is moved from its initial value toward 255.
+ *          If fract < 0.0, it gives the fraction that the pixel
+ *          saturation is moved from its initial value toward 0.
+ *          The limiting values for fract = -1.0 (1.0) thus set the
+ *          saturation to 0 (255).
+ *      (2) If fract = 0, no modification is requested; return a copy
+ *          unless in-place, in which case this is a no-op.
+ *      (3) See discussion of color-modification methods, in coloring.c.
+ * 
+ */ +PIX * +pixModifySaturation(PIX *pixd, + PIX *pixs, + l_float32 fract) +{ +l_int32 w, h, d, i, j, wpl; +l_int32 rval, gval, bval, hval, sval, vval; +l_uint32 *data, *line; + + PROCNAME("pixModifySaturation"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (L_ABS(fract) > 1.0) + return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); + + pixd = pixCopy(pixd, pixs); + if (fract == 0.0) { + L_WARNING("no change requested in saturation\n", procName); + return pixd; + } + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + if (fract < 0.0) + sval = (l_int32)(sval * (1.0 + fract)); + else + sval = (l_int32)(sval + fract * (255 - sval)); + convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, line + j); + } + } + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); + + return pixd; +} + + +/*! + * \brief pixMeasureSaturation() + * + * \param[in] pixs 32 bpp rgb + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] psat average saturation + * \return 0 if OK, 1 on error + */ +l_int32 +pixMeasureSaturation(PIX *pixs, + l_int32 factor, + l_float32 *psat) +{ +l_int32 w, h, d, i, j, wpl, sum, count; +l_int32 rval, gval, bval, hval, sval, vval; +l_uint32 *data, *line; + + PROCNAME("pixMeasureSaturation"); + + if (!psat) + return ERROR_INT("pixs not defined", procName, 1); + *psat = 0.0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("subsampling factor < 1", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0, sum = 0, count = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + sum += sval; + count++; + } + } + + if (count > 0) + *psat = (l_float32)sum / (l_float32)count; + return 0; +} + + +/*! + * \brief pixModifyBrightness() + * + * \param[in] pixd [optional] can be null, existing or equal to pixs + * \param[in] pixs 32 bpp rgb + * \param[in] fract between -1.0 and 1.0 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If fract > 0.0, it gives the fraction that the v-parameter,
+ *          which is max(r,g,b), is moved from its initial value toward 255.
+ *          If fract < 0.0, it gives the fraction that the v-parameter
+ *          is moved from its initial value toward 0.
+ *          The limiting values for fract = -1.0 (1.0) thus set the
+ *          v-parameter to 0 (255).
+ *      (2) If fract = 0, no modification is requested; return a copy
+ *          unless in-place, in which case this is a no-op.
+ *      (3) See discussion of color-modification methods, in coloring.c.
+ * 
+ */ +PIX * +pixModifyBrightness(PIX *pixd, + PIX *pixs, + l_float32 fract) +{ +l_int32 w, h, d, i, j, wpl; +l_int32 rval, gval, bval, hval, sval, vval; +l_uint32 *data, *line; + + PROCNAME("pixModifyBrightness"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (L_ABS(fract) > 1.0) + return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); + + pixd = pixCopy(pixd, pixs); + if (fract == 0.0) { + L_WARNING("no change requested in brightness\n", procName); + return pixd; + } + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + extractRGBValues(line[j], &rval, &gval, &bval); + convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); + if (fract > 0.0) + vval = (l_int32)(vval + fract * (255.0 - vval)); + else + vval = (l_int32)(vval * (1.0 + fract)); + convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, line + j); + } + } + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Color shifting * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMosaicColorShiftRGB() + * + * \param[in] pixs 32 bpp rgb + * \param[in] roff center offset of red component + * \param[in] goff center offset of green component + * \param[in] boff center offset of blue component + * \param[in] delta increments from center offsets [0.0 - 0.1]; + * use 0.0 to get the default (0.04) + * \param[in] nincr number of increments in each (positive and negative) + * direction; use 0 to get the default (2). + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a mosaic view of the effect of shifting the RGB
+ *          components.  See pixColorShiftRGB() for details on the shifting.
+ *      (2) The offsets (%roff, %goff, %boff) set the color center point,
+ *          and the deviations from this are shown separately for deltas
+ *          in r, g and b.  For each component, we show 2 * %nincr + 1
+ *          images.
+ *      (3) Usage: color prints differ from the original due to three factors:
+ *          illumination, calibration of the camera in acquisition,
+ *          and calibration of the printer.  This function can be used
+ *          to iteratively match a color print to the original.  On each
+ *          iteration, the center offsets are set to the best match so
+ *          far, and the %delta increments are typically reduced.
+ * 
+ */ +PIX * +pixMosaicColorShiftRGB(PIX *pixs, + l_float32 roff, + l_float32 goff, + l_float32 boff, + l_float32 delta, + l_int32 nincr) +{ +char buf[64]; +l_int32 i; +l_float32 del; +L_BMF *bmf; +PIX *pix1, *pix2, *pix3; +PIXA *pixa; + + PROCNAME("pixMosaicColorShiftRGB"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); + if (roff < -1.0 || roff > 1.0) + return (PIX *)ERROR_PTR("roff not in [-1.0, 1.0]", procName, NULL); + if (goff < -1.0 || goff > 1.0) + return (PIX *)ERROR_PTR("goff not in [-1.0, 1.0]", procName, NULL); + if (boff < -1.0 || boff > 1.0) + return (PIX *)ERROR_PTR("boff not in [-1.0, 1.0]", procName, NULL); + if (delta < 0.0 || delta > 0.1) + return (PIX *)ERROR_PTR("delta not in [0.0, 0.1]", procName, NULL); + if (delta == 0.0) delta = 0.04; + if (nincr < 0 || nincr > 6) + return (PIX *)ERROR_PTR("nincr not in [0, 6]", procName, NULL); + if (nincr == 0) nincr = 2; + + pixa = pixaCreate(3 * (2 * nincr + 1)); + bmf = bmfCreate(NULL, 8); + pix1 = pixScaleToSize(pixs, 400, 0); + for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { + pix2 = pixColorShiftRGB(pix1, roff + del, goff, boff); + snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", + roff + del, goff, boff); + pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, + L_ADD_BELOW, 0); + pixaAddPix(pixa, pix3, L_INSERT); + pixDestroy(&pix2); + } + for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { + pix2 = pixColorShiftRGB(pix1, roff, goff + del, boff); + snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", + roff, goff + del, boff); + pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, + L_ADD_BELOW, 0); + pixaAddPix(pixa, pix3, L_INSERT); + pixDestroy(&pix2); + } + for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { + pix2 = pixColorShiftRGB(pix1, roff, goff, boff + del); + snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", + roff, goff, boff + del); + pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, + L_ADD_BELOW, 0); + pixaAddPix(pixa, pix3, L_INSERT); + pixDestroy(&pix2); + } + pixDestroy(&pix1); + + pix1 = pixaDisplayTiledAndScaled(pixa, 32, 300, 2 * nincr + 1, 0, 30, 2); + pixaDestroy(&pixa); + bmfDestroy(&bmf); + return pix1; +} + + +/*! + * \brief pixColorShiftRGB() + * + * \param[in] pixs 32 bpp rgb + * \param[in] rfract fractional shift in red component + * \param[in] gfract fractional shift in green component + * \param[in] bfract fractional shift in blue component + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This allows independent fractional shifts of the r,g and b
+ *          components.  A positive shift pushes to saturation (255);
+ *          a negative shift pushes toward 0 (black).
+ *      (2) The effect can be imagined using a color wheel that consists
+ *          (for our purposes) of these 6 colors, separated by 60 degrees:
+ *             red, magenta, blue, cyan, green, yellow
+ *      (3) So, for example, a negative shift of the blue component
+ *          (bfract < 0) could be accompanied by positive shifts
+ *          of red and green to make an image more yellow.
+ *      (4) Examples of limiting cases:
+ *            rfract = 1 ==> r = 255
+ *            rfract = -1 ==> r = 0
+ * 
+ */ +PIX * +pixColorShiftRGB(PIX *pixs, + l_float32 rfract, + l_float32 gfract, + l_float32 bfract) +{ +l_int32 w, h, i, j, wpls, wpld, rval, gval, bval; +l_int32 *rlut, *glut, *blut; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 fi; +PIX *pixd; + + PROCNAME("pixColorShiftRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (rfract < -1.0 || rfract > 1.0) + return (PIX *)ERROR_PTR("rfract not in [-1.0, 1.0]", procName, NULL); + if (gfract < -1.0 || gfract > 1.0) + return (PIX *)ERROR_PTR("gfract not in [-1.0, 1.0]", procName, NULL); + if (bfract < -1.0 || bfract > 1.0) + return (PIX *)ERROR_PTR("bfract not in [-1.0, 1.0]", procName, NULL); + if (rfract == 0.0 && gfract == 0.0 && bfract == 0.0) + return pixCopy(NULL, pixs); + + rlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + glut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + blut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) { + fi = i; + if (rfract >= 0) { + rlut[i] = (l_int32)(fi + (255.0 - fi) * rfract); + } else { + rlut[i] = (l_int32)(fi * (1.0 + rfract)); + } + if (gfract >= 0) { + glut[i] = (l_int32)(fi + (255.0 - fi) * gfract); + } else { + glut[i] = (l_int32)(fi * (1.0 + gfract)); + } + if (bfract >= 0) { + blut[i] = (l_int32)(fi + (255.0 - fi) * bfract); + } else { + blut[i] = (l_int32)(fi * (1.0 + bfract)); + } + } + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreate(w, h, 32); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + composeRGBPixel(rlut[rval], glut[gval], blut[bval], lined + j); + } + } + + LEPT_FREE(rlut); + LEPT_FREE(glut); + LEPT_FREE(blut); + return pixd; +} + +/*-----------------------------------------------------------------------* + * Darken gray (unsaturated) pixels + *-----------------------------------------------------------------------*/ +/*! + * \brief pixDarkenGray() + * + * \param[in] pixd [optional] can be null or equal to pixs + * \param[in] pixs 32 bpp rgb + * \param[in] thresh pixels with max component >= %thresh are unchanged + * \param[in] satlimit pixels with saturation >= %satlimit are unchanged + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This darkens gray pixels, by a fraction (sat/%satlimit), where
+ *          the saturation, sat, is the component difference (max - min).
+ *          The pixel value is unchanged if sat >= %satlimit.  A typical
+ *          value of %satlimit might be 40; the larger the value, the
+ *          more that pixels with a smaller saturation will be darkened.
+ *      (2) Pixels with max component >= %thresh are unchanged. This can be
+ *          used to prevent bright pixels with low saturation from being
+ *          darkened.  Setting thresh == 0 is a no-op; setting %thresh == 255
+ *          causes the darkening to be applied to all pixels.
+ *      (3) This function is useful to enhance pixels relative to a
+ *          gray background.
+ *      (4) A related function that builds a 1 bpp mask over the gray
+ *          pixels is pixMaskOverGrayPixels().
+ * 
+ */ +PIX * +pixDarkenGray(PIX *pixd, + PIX *pixs, + l_int32 thresh, + l_int32 satlimit) +{ +l_int32 w, h, i, j, wpls, wpld; +l_int32 rval, gval, bval, minrg, min, maxrg, max, sat; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 ratio; + + PROCNAME("pixDarkenGray"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (thresh < 0 || thresh > 255) + return (PIX *)ERROR_PTR("invalid thresh", procName, NULL); + if (satlimit < 1) + return (PIX *)ERROR_PTR("invalid satlimit", procName, NULL); + if (pixd && (pixs != pixd)) + return (PIX *)ERROR_PTR("not new or in-place", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + sat = max - min; + if (max >= thresh || sat >= satlimit) + continue; + ratio = (l_float32)sat / (l_float32)satlimit; + composeRGBPixel((l_int32)(ratio * rval), (l_int32)(ratio * gval), + (l_int32)(ratio * bval), &lined[j]); + } + } + return pixd; +} + + +/*-----------------------------------------------------------------------* + * General multiplicative constant color transform * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMultConstantColor() + * + * \param[in] pixs colormapped or rgb + * \param[in] rfact red multiplicative factor + * \param[in] gfact green multiplicative factor + * \param[in] bfact blue multiplicative factor + * \return pixd colormapped or rgb, with colors scaled, or NULL on error + * + *
+ * Notes:
+ *      (1) rfact, gfact and bfact can only have non-negative values.
+ *          They can be greater than 1.0.  All transformed component
+ *          values are clipped to the interval [0, 255].
+ *      (2) For multiplication with a general 3x3 matrix of constants,
+ *          use pixMultMatrixColor().
+ * 
+ */ +PIX * +pixMultConstantColor(PIX *pixs, + l_float32 rfact, + l_float32 gfact, + l_float32 bfact) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; +l_uint32 nval; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMultConstantColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + rfact = L_MAX(0.0, rfact); + gfact = L_MAX(0.0, gfact); + bfact = L_MAX(0.0, bfact); + + if (cmap) { + if ((pixd = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixGetColormap(pixd); + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + nrval = (l_int32)(rfact * rval); + ngval = (l_int32)(gfact * gval); + nbval = (l_int32)(bfact * bval); + nrval = L_MIN(255, nrval); + ngval = L_MIN(255, ngval); + nbval = L_MIN(255, nbval); + pixcmapResetColor(cmap, i, nrval, ngval, nbval); + } + return pixd; + } + + if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + nrval = (l_int32)(rfact * rval); + ngval = (l_int32)(gfact * gval); + nbval = (l_int32)(bfact * bval); + nrval = L_MIN(255, nrval); + ngval = L_MIN(255, ngval); + nbval = L_MIN(255, nbval); + composeRGBPixel(nrval, ngval, nbval, &nval); + *(lined + j) = nval; + } + } + + return pixd; +} + + +/*! + * \brief pixMultMatrixColor() + * + * \param[in] pixs colormapped or rgb + * \param[in] kel kernel 3x3 matrix of floats + * \return pixd colormapped or rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) The kernel is a data structure used mostly for floating point
+ *          convolution.  Here it is a 3x3 matrix of floats that are used
+ *          to transform the pixel values by matrix multiplication:
+ *            nrval = a[0,0] * rval + a[0,1] * gval + a[0,2] * bval
+ *            ngval = a[1,0] * rval + a[1,1] * gval + a[1,2] * bval
+ *            nbval = a[2,0] * rval + a[2,1] * gval + a[2,2] * bval
+ *      (2) The matrix can be generated in several ways.
+ *          See kernel.c for details.  Here are two of them:
+ *            (a) kel = kernelCreate(3, 3);
+ *                kernelSetElement(kel, 0, 0, val00);
+ *                kernelSetElement(kel, 0, 1, val01);
+ *                ...
+ *            (b) from a static string; e.g.,:
+ *                const char *kdata = " 0.6  0.3 -0.2 "
+ *                                    " 0.1  1.2  0.4 "
+ *                                    " -0.4 0.2  0.9 ";
+ *                kel = kernelCreateFromString(3, 3, 0, 0, kdata);
+ *      (3) For the special case where the matrix is diagonal, it is easier
+ *          to use pixMultConstantColor().
+ *      (4) Matrix entries can have positive and negative values, and can
+ *          be larger than 1.0.  All transformed component values
+ *          are clipped to [0, 255].
+ * 
+ */ +PIX * +pixMultMatrixColor(PIX *pixs, + L_KERNEL *kel) +{ +l_int32 i, j, index, kw, kh, w, h, d, wpls, wpld; +l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; +l_uint32 nval; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 v[9]; /* use linear array for convenience */ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixMultMatrixColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!kel) + return (PIX *)ERROR_PTR("kel not defined", procName, NULL); + kernelGetParameters(kel, &kw, &kh, NULL, NULL); + if (kw != 3 || kh != 3) + return (PIX *)ERROR_PTR("matrix not 3x3", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (!cmap && d != 32) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + + for (i = 0, index = 0; i < 3; i++) + for (j = 0; j < 3; j++, index++) + kernelGetElement(kel, i, j, v + index); + + if (cmap) { + if ((pixd = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixGetColormap(pixd); + ncolors = pixcmapGetCount(cmap); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); + ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); + nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); + nrval = L_MAX(0, L_MIN(255, nrval)); + ngval = L_MAX(0, L_MIN(255, ngval)); + nbval = L_MAX(0, L_MIN(255, nbval)); + pixcmapResetColor(cmap, i, nrval, ngval, nbval); + } + return pixd; + } + + if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); + ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); + nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); + nrval = L_MAX(0, L_MIN(255, nrval)); + ngval = L_MAX(0, L_MIN(255, ngval)); + nbval = L_MAX(0, L_MIN(255, nbval)); + composeRGBPixel(nrval, ngval, nbval, &nval); + *(lined + j) = nval; + } + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Half-edge by bandpass * + *-------------------------------------------------------------*/ +/*! + * \brief pixHalfEdgeByBandpass() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb + * \param[in] sm1h, sm1v "half-widths" of smoothing filter sm1 + * \param[in] sm2h, sm2v "half-widths" of smoothing filter sm2; + * require sm2 != sm1 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) We use symmetric smoothing filters of odd dimension,
+ *          typically use 3, 5, 7, etc.  The smoothing parameters
+ *          for these are 1, 2, 3, etc.  The filter size is related
+ *          to the smoothing parameter by
+ *               size = 2 * smoothing + 1
+ *      (2) Because we take the difference of two lowpass filters,
+ *          this is actually a bandpass filter.
+ *      (3) We allow both filters to be anisotropic.
+ *      (4) Consider either the h or v component of the 2 filters.
+ *          Depending on whether sm1 > sm2 or sm2 > sm1, we get
+ *          different halves of the smoothed gradients (or "edges").
+ *          This difference of smoothed signals looks more like
+ *          a second derivative of a transition, which we rectify
+ *          by not allowing the signal to go below zero.  If sm1 < sm2,
+ *          the sm2 transition is broader, so the difference between
+ *          sm1 and sm2 signals is positive on the upper half of
+ *          the transition.  Likewise, if sm1 > sm2, the sm1 - sm2
+ *          signal difference is positive on the lower half of
+ *          the transition.
+ * 
+ */ +PIX * +pixHalfEdgeByBandpass(PIX *pixs, + l_int32 sm1h, + l_int32 sm1v, + l_int32 sm2h, + l_int32 sm2v) +{ +l_int32 d; +PIX *pixg, *pixacc, *pixc1, *pixc2; + + PROCNAME("pixHalfEdgeByBandpass"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (sm1h == sm2h && sm1v == sm2v) + return (PIX *)ERROR_PTR("sm2 = sm1", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (d == 32) + pixg = pixConvertRGBToLuminance(pixs); + else /* d == 8 */ + pixg = pixClone(pixs); + + /* Make a convolution accumulator and use it twice */ + if ((pixacc = pixBlockconvAccum(pixg)) == NULL) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); + } + if ((pixc1 = pixBlockconvGray(pixg, pixacc, sm1h, sm1v)) == NULL) { + pixDestroy(&pixg); + pixDestroy(&pixacc); + return (PIX *)ERROR_PTR("pixc1 not made", procName, NULL); + } + pixc2 = pixBlockconvGray(pixg, pixacc, sm2h, sm2v); + pixDestroy(&pixg); + pixDestroy(&pixacc); + if (!pixc2) { + pixDestroy(&pixc1); + return (PIX *)ERROR_PTR("pixc2 not made", procName, NULL); + } + + /* Compute the half-edge using pixc1 - pixc2. */ + pixSubtractGray(pixc1, pixc1, pixc2); + pixDestroy(&pixc2); + return pixc1; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/environ.h b/hgdriver/3rdparty/hgOCR/leptonica/environ.h new file mode 100644 index 0000000..20e61fa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/environ.h @@ -0,0 +1,554 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_ENVIRON_H +#define LEPTONICA_ENVIRON_H + +/*------------------------------------------------------------------------* + * Defines and includes differ for Unix and Windows. Also for Windows, * + * differentiate between conditionals based on platform and compiler. * + * For platforms: * + * _WIN32 => Windows, 32- or 64-bit * + * _WIN64 => Windows, 64-bit only * + * __CYGWIN__ => Cygwin * + * For compilers: * + * __GNUC__ => gcc * + * _MSC_VER => msvc * + *------------------------------------------------------------------------*/ + +/* MS VC++ does not provide stdint.h, so define the missing types here */ + + +#ifndef _MSC_VER +#include + +#else +/* Note that _WIN32 is defined for both 32 and 64 bit applications, + whereas _WIN64 is defined only for the latter */ + +#ifdef _WIN64 +typedef __int64 intptr_t; +typedef unsigned __int64 uintptr_t; +#else +typedef int intptr_t; +typedef unsigned int uintptr_t; +#endif + +/* VC++6 doesn't seem to have powf, expf. */ +#if (_MSC_VER < 1400) +#define powf(x, y) (float)pow((double)(x), (double)(y)) +#define expf(x) (float)exp((double)(x)) +#endif + +#endif /* _MSC_VER */ + +/* Windows specifics */ +#ifdef _WIN32 + /* DLL EXPORTS and IMPORTS */ + #if defined(LIBLEPT_EXPORTS) + #define LEPT_DLL __declspec(dllexport) + #elif defined(LIBLEPT_IMPORTS) + #define LEPT_DLL __declspec(dllimport) + #else + #define LEPT_DLL + #endif +#else /* non-Windows specifics */ + #include + #define LEPT_DLL +#endif /* _WIN32 */ + +typedef intptr_t l_intptr_t; +typedef uintptr_t l_uintptr_t; + + +/*--------------------------------------------------------------------* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * USER CONFIGURABLE * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * Environment variables with I/O libraries * + * Manual Configuration Only: NOT AUTO_CONF * + *--------------------------------------------------------------------*/ +/* + * Leptonica provides interfaces to link to several external image + * I/O libraries, plus zlib. Setting any of these to 0 here causes + * non-functioning stubs to be linked. + */ +#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) && !defined(OS_IOS) + + #if !defined(HAVE_LIBJPEG) + #define HAVE_LIBJPEG 0 + #endif + #if !defined(HAVE_LIBTIFF) + #define HAVE_LIBTIFF 0 + #endif + #if !defined(HAVE_LIBPNG) + #define HAVE_LIBPNG 0 + #endif + #if !defined(HAVE_LIBZ) + #define HAVE_LIBZ 1 + #endif + #if !defined(HAVE_LIBGIF) + #define HAVE_LIBGIF 0 + #endif + #if !defined(HAVE_LIBUNGIF) + #define HAVE_LIBUNGIF 0 + #endif + #if !defined(HAVE_LIBWEBP) + #define HAVE_LIBWEBP 0 + #endif + #if !defined(HAVE_LIBWEBP_ANIM) + #define HAVE_LIBWEBP_ANIM 0 + #endif + #if !defined(HAVE_LIBJP2K) + #define HAVE_LIBJP2K 0 + #endif + + /*-----------------------------------------------------------------------* + * Leptonica supports OpenJPEG 2.0+. If you have a version of openjpeg * + * (HAVE_LIBJP2K == 1) that is >= 2.0, set the path to the openjpeg.h * + * header in angle brackets here. * + *-----------------------------------------------------------------------*/ + #define LIBJP2K_HEADER + +#endif /* ! HAVE_CONFIG_H etc. */ + +/*--------------------------------------------------------------------* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * USER CONFIGURABLE * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* + * Environ variables for image I/O without external libraries * + *--------------------------------------------------------------------*/ +/* + * Leptonica supplies I/O support without using external libraries for: + * * image read/write for bmp, pnm + * * header read for jp2k + * * image wrapping write for pdf and ps. + * Setting any of these to 0 causes non-functioning stubs to be linked. + */ +#define USE_BMPIO 1 +#define USE_PNMIO 1 +#define USE_JP2KHEADER 1 +#define USE_PDFIO 1 +#define USE_PSIO 1 + + +/*-------------------------------------------------------------------------* + * On linux systems, you can do I/O between Pix and memory. Specifically, + * you can compress (write compressed data to memory from a Pix) and + * uncompress (read from compressed data in memory to a Pix). + * For jpeg, png, jp2k, gif, pnm and bmp, these use the non-posix GNU + * functions fmemopen() and open_memstream(). These functions are not + * available on other systems. + * To use these functions in linux, you must define HAVE_FMEMOPEN to 1. + * To use them on MacOS, which does not support these functions, set it to 0. + *-------------------------------------------------------------------------*/ +#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) && !defined(OS_IOS) && \ + !defined(_WIN32) +#define HAVE_FMEMOPEN 1 +#endif /* ! HAVE_CONFIG_H etc. */ + +/*-------------------------------------------------------------------------* + * fstatat() is defined by POSIX, but some systems do not support it. * + * One example is older macOS systems (pre-10.10). * + * Play it safe and set the default value to 0. * + *-------------------------------------------------------------------------*/ +#if !defined(HAVE_CONFIG_H) +#define HAVE_FSTATAT 0 +#endif /* ! HAVE_CONFIG_H */ + +/*--------------------------------------------------------------------* + * It is desirable on Windows to have all temp files written to the same + * subdirectory of the Windows directory, because files under + * persist after reboot, and the regression tests write a lot of files. + * We write all test files to /tmp/lept or subdirectories of /tmp/lept. + * Windows temp files are specified as in unix, but have the translation + * /tmp/lept/xxx --> /lept/xxx + *--------------------------------------------------------------------*/ + + +/*--------------------------------------------------------------------* + * Built-in types * + *--------------------------------------------------------------------*/ +typedef int l_ok; /*!< return type 0 if OK, 1 on error */ +typedef signed char l_int8; /*!< signed 8-bit value */ +typedef unsigned char l_uint8; /*!< unsigned 8-bit value */ +typedef short l_int16; /*!< signed 16-bit value */ +typedef unsigned short l_uint16; /*!< unsigned 16-bit value */ +typedef int l_int32; /*!< signed 32-bit value */ +typedef unsigned int l_uint32; /*!< unsigned 32-bit value */ +typedef float l_float32; /*!< 32-bit floating point value */ +typedef double l_float64; /*!< 64-bit floating point value */ +#ifdef COMPILER_MSVC +typedef __int64 l_int64; /*!< signed 64-bit value */ +typedef unsigned __int64 l_uint64; /*!< unsigned 64-bit value */ +#else +typedef long long l_int64; /*!< signed 64-bit value */ +typedef unsigned long long l_uint64; /*!< unsigned 64-bit value */ +#endif /* COMPILER_MSVC */ + + +/*-------------------------------------------------------------------------* + * For security, the library is distributed in a configuration that does * + * not permit (1) forking with 'system', which is used for displaying * + * images and generating gnuplots, and (2) writing files with specified * + * compiled-in file names. All such writes are with functions such as * + * pixWriteDebug() where the "Debug" is appended to the usual name. * + * Whether the "Debug" version defaults to the standard version or is a * + * no-op depends on the value of this global variable. The default value * + * of LeptDebugOK is 0, and it is set in writefile.c. This value can be * + * over-ridden, for development and debugging, by setLeptDebugOK(). * + *-------------------------------------------------------------------------*/ +LEPT_DLL extern l_int32 LeptDebugOK; /* default is 0 */ + + +/*------------------------------------------------------------------------* + * Standard macros * + *------------------------------------------------------------------------*/ +#ifndef L_MIN +/*! Minimum of %x and %y */ +#define L_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#ifndef L_MAX +/*! Maximum of %x and %y */ +#define L_MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef L_ABS +/*! Absolute value of %x */ +#define L_ABS(x) (((x) < 0) ? (-1 * (x)) : (x)) +#endif + +#ifndef L_SIGN +/*! Sign of %x */ +#define L_SIGN(x) (((x) < 0) ? -1 : 1) +#endif + +#ifndef UNDEF +/*! Undefined value */ +#define UNDEF -1 +#endif + +#ifndef NULL +/*! NULL value */ +#define NULL 0 +#endif + +#ifndef TRUE +/*! True value */ +#define TRUE 1 +#endif + +#ifndef FALSE +/*! False value */ +#define FALSE 0 +#endif + + +/*--------------------------------------------------------------------* + * Environment variables for endian dependence * + *--------------------------------------------------------------------*/ +/* + * To control conditional compilation, one of two variables + * + * L_LITTLE_ENDIAN (e.g., for Intel X86) + * L_BIG_ENDIAN (e.g., for Sun SPARC, Mac Power PC) + * + * is defined when the GCC compiler is invoked. + * All code should compile properly for both hardware architectures. + */ + + +/*------------------------------------------------------------------------* + * Simple search state variables * + *------------------------------------------------------------------------*/ +/*! Search State */ +enum { + L_NOT_FOUND = 0, + L_FOUND = 1 +}; + + +/*------------------------------------------------------------------------* + * Path separator conversion * + *------------------------------------------------------------------------*/ +/*! Path Separators */ +enum { + UNIX_PATH_SEPCHAR = 0, + WIN_PATH_SEPCHAR = 1 +}; + + +/*------------------------------------------------------------------------* + * Timing structs * + *------------------------------------------------------------------------*/ +typedef void *L_TIMER; + +/*! Timing struct */ +struct L_WallTimer { + l_int32 start_sec; + l_int32 start_usec; + l_int32 stop_sec; + l_int32 stop_usec; +}; +typedef struct L_WallTimer L_WALLTIMER; + + +/*------------------------------------------------------------------------* + * Standard memory allocation * + * * + * These specify the memory management functions that are used * + * on all heap data except for Pix. Memory management for Pix * + * also defaults to malloc and free. See pix1.c for details. * + *------------------------------------------------------------------------*/ +#define LEPT_MALLOC(blocksize) malloc(blocksize) +#define LEPT_CALLOC(numelem, elemsize) calloc(numelem, elemsize) +#define LEPT_REALLOC(ptr, blocksize) realloc(ptr, blocksize) +#define LEPT_FREE(ptr) free(ptr) + + +/*------------------------------------------------------------------------* + * Control printing of error, warning, and info messages * + * * + * To omit all messages to stderr, simply define NO_CONSOLE_IO on the * + * command line. For finer grained control, we have a mechanism * + * based on the message severity level. The following assumes that * + * NO_CONSOLE_IO is not defined. * + * * + * Messages are printed if the message severity is greater than or equal * + * to the current severity threshold. The current severity threshold * + * is the greater of the compile-time severity, which is the minimum * + * severity that can be reported, and the run-time severity, which is * + * the severity threshold at the moment. * + * * + * The compile-time threshold determines which messages are compiled * + * into the library for potential printing. Messages below the * + * compile-time threshold are omitted and can never be printed. The * + * default compile-time threshold is L_SEVERITY_INFO, but this may be * + * overridden by defining MINIMUM_SEVERITY to the desired enumeration * + * identifier on the compiler command line. Defining NO_CONSOLE_IO on * + * the command line is the same as setting MINIMUM_SEVERITY to * + * L_SEVERITY_NONE. * + * * + * The run-time threshold determines which messages are printed during * + * library execution. It defaults to the compile-time threshold but * + * may be changed either statically by defining DEFAULT_SEVERITY to * + * the desired enumeration identifier on the compiler command line, or * + * dynamically by calling setMsgSeverity() to specify a new threshold. * + * The run-time threshold may also be set from the value of the * + * environment variable LEPT_MSG_SEVERITY by calling setMsgSeverity() * + * and specifying L_SEVERITY_EXTERNAL. * + * * + * In effect, the compile-time threshold setting says, "Generate code * + * to permit messages of equal or greater severity than this to be * + * printed, if desired," whereas the run-time threshold setting says, * + * "Print messages that have an equal or greater severity than this." * + *------------------------------------------------------------------------*/ + + /*! Control printing of error, warning and info messages */ +/*! Message Control */ +enum { + L_SEVERITY_EXTERNAL = 0, /* Get the severity from the environment */ + L_SEVERITY_ALL = 1, /* Lowest severity: print all messages */ + L_SEVERITY_DEBUG = 2, /* Print debugging and higher messages */ + L_SEVERITY_INFO = 3, /* Print informational and higher messages */ + L_SEVERITY_WARNING = 4, /* Print warning and higher messages */ + L_SEVERITY_ERROR = 5, /* Print error and higher messages */ + L_SEVERITY_NONE = 6 /* Highest severity: print no messages */ +}; + +/* No message less than the compile-time threshold will ever be + * reported, regardless of the current run-time threshold. This allows + * selection of the set of messages to include in the library. For + * example, setting the threshold to L_SEVERITY_WARNING eliminates all + * informational messages from the library. With that setting, both + * warning and error messages would be printed unless setMsgSeverity() + * was called, or DEFAULT_SEVERITY was redefined, to set the run-time + * severity to L_SEVERITY_ERROR. In that case, only error messages + * would be printed. + * + * This mechanism makes the library smaller and faster, by eliminating + * undesired message reporting and the associated run-time overhead for + * message threshold checking, because code for messages whose severity + * is lower than MINIMUM_SEVERITY won't be generated. + * + * A production library might typically permit ERROR messages to be + * generated, and a development library might permit DEBUG and higher. + * The actual messages printed (as opposed to generated) would depend + * on the current run-time severity threshold. + * + * This is a complex mechanism and a few examples may help. + * (1) No output permitted under any circumstances. + * Use: -DNO_CONSOLE_IO or -DMINIMUM_SEVERITY=6 + * (2) Suppose you want to only allow error messages, and you don't + * want to permit info or warning messages at runtime. + * Use: -DMINIMUM_SEVERITY=5 + * (3) Suppose you want to only allow error messages by default, + * but you will permit this to be over-ridden at runtime. + * Use: -DDEFAULT_SEVERITY=5 + * and to allow info and warning override: + * setMsgSeverity(L_SEVERITY_INFO); + */ + +#ifdef NO_CONSOLE_IO + #undef MINIMUM_SEVERITY + #undef DEFAULT_SEVERITY + + #define MINIMUM_SEVERITY L_SEVERITY_NONE /*!< Compile-time default */ + #define DEFAULT_SEVERITY L_SEVERITY_NONE /*!< Run-time default */ + +#else + #ifndef MINIMUM_SEVERITY + #define MINIMUM_SEVERITY L_SEVERITY_INFO /*!< Compile-time default */ + #endif + + #ifndef DEFAULT_SEVERITY + #define DEFAULT_SEVERITY MINIMUM_SEVERITY /*!< Run-time default */ + #endif +#endif + + +/*! The run-time message severity threshold is defined in utils.c. */ +LEPT_DLL extern l_int32 LeptMsgSeverity; + +/* + *
+ *  Usage
+ *  =====
+ *  Messages are of two types.
+ *
+ *  (1) The messages
+ *      ERROR_INT(a,b,c)       : returns l_int32
+ *      ERROR_FLOAT(a,b,c)     : returns l_float32
+ *      ERROR_PTR(a,b,c)       : returns void*
+ *  are used to return from functions and take a fixed set of parameters:
+ *      a : 
+ *      b : procName
+ *      c : 
+ *  where procName is the name of the local variable naming the function.
+ *
+ *  (2) The purely informational L_* messages
+ *      L_ERROR(a,...)
+ *      L_WARNING(a,...)
+ *      L_INFO(a,...)
+ *  do not take a return value, but they take at least two parameters:
+ *      a  :   with optional format conversions
+ *      v1 : procName    (this must be included as the first vararg)
+ *      v2, ... :  optional varargs to match format converters in the message
+ *
+ *  To return an error from a function that returns void, use:
+ *      L_ERROR(, procName, [...])
+ *      return;
+ *
+ *  Implementation details
+ *  ======================
+ *  Messages are defined with the IF_SEV macro.  The first parameter is
+ *  the message severity, the second is the function to call if the
+ *  message is to be printed, and the third is the return value if the
+ *  message is to be suppressed.  For example, we might have an
+ *  informational message defined as:
+ *
+ *    IF_SEV(L_SEVERITY_INFO, fprintf(.......), 0)
+ *
+ *  The macro expands into a conditional.  Because the first comparison
+ *  is between two constants, an optimizing compiler will remove either
+ *  the comparison (if it's true) or the entire macro expansion (if it
+ *  is false).  This means that there is no run-time overhead for
+ *  messages whose severity falls below the minimum specified at compile
+ *  time, and for others the overhead is one (not two) comparisons.
+ *
+ *  The L_nnn() macros below do not return a value, but because the
+ *  conditional operator requires one for the false condition, we
+ *  specify a void expression.
+ * 
+ */ + +#ifdef NO_CONSOLE_IO + + #define PROCNAME(name) + #define ERROR_INT(a, b, c) ((l_int32)(c)) + #define ERROR_FLOAT(a, b, c) ((l_float32)(c)) + #define ERROR_PTR(a, b, c) ((void *)(c)) + #define L_ERROR(a, ...) + #define L_WARNING(a, ...) + #define L_INFO(a, ...) + +#else + + #define PROCNAME(name) static const char procName[] = name + #define IF_SEV(l, t, f) \ + ((l) >= MINIMUM_SEVERITY && (l) >= LeptMsgSeverity ? (t) : (f)) + + #define ERROR_INT(a, b, c) \ + IF_SEV(L_SEVERITY_ERROR, returnErrorInt((a), (b), (c)), (l_int32)(c)) + #define ERROR_FLOAT(a, b, c) \ + IF_SEV(L_SEVERITY_ERROR, returnErrorFloat((a), (b), (c)), (l_float32)(c)) + #define ERROR_PTR(a, b, c) \ + IF_SEV(L_SEVERITY_ERROR, returnErrorPtr((a), (b), (c)), (void *)(c)) + + #define L_ERROR(a, ...) \ + IF_SEV(L_SEVERITY_ERROR, \ + (void)fprintf(stderr, "Error in %s: " a, __VA_ARGS__), \ + (void)0) + #define L_WARNING(a, ...) \ + IF_SEV(L_SEVERITY_WARNING, \ + (void)fprintf(stderr, "Warning in %s: " a, __VA_ARGS__), \ + (void)0) + #define L_INFO(a, ...) \ + IF_SEV(L_SEVERITY_INFO, \ + (void)fprintf(stderr, "Info in %s: " a, __VA_ARGS__), \ + (void)0) + +#if 0 /* Alternative method for controlling L_* message output */ + #define L_ERROR(a, ...) \ + { if (L_SEVERITY_ERROR >= MINIMUM_SEVERITY && \ + L_SEVERITY_ERROR >= LeptMsgSeverity) \ + fprintf(stderr, "Error in %s: " a, __VA_ARGS__) \ + } + #define L_WARNING(a, ...) \ + { if (L_SEVERITY_WARNING >= MINIMUM_SEVERITY && \ + L_SEVERITY_WARNING >= LeptMsgSeverity) \ + fprintf(stderr, "Warning in %s: " a, __VA_ARGS__) \ + } + #define L_INFO(a, ...) \ + { if (L_SEVERITY_INFO >= MINIMUM_SEVERITY && \ + L_SEVERITY_INFO >= LeptMsgSeverity) \ + fprintf(stderr, "Info in %s: " a, __VA_ARGS__) \ + } +#endif + +#endif /* NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------------* + * snprintf() renamed in MSVC (pre-VS2015) * + *------------------------------------------------------------------------*/ +#if defined _MSC_VER && _MSC_VER < 1900 +#define snprintf(buf, size, ...) _snprintf_s(buf, size, _TRUNCATE, __VA_ARGS__) +#endif + + +#endif /* LEPTONICA_ENVIRON_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fhmtauto.c b/hgdriver/3rdparty/hgOCR/leptonica/fhmtauto.c new file mode 100644 index 0000000..f8b0d43 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fhmtauto.c @@ -0,0 +1,817 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file fhmtauto.c + *
+ *
+ *    Main function calls:
+ *       l_int32             fhmtautogen()
+ *       l_int32             fhmtautogen1()
+ *       l_int32             fhmtautogen2()
+ *
+ *    Static helpers:
+ *       static SARRAY      *sarrayMakeWplsCode()
+ *       static SARRAY      *sarrayMakeInnerLoopDWACode()
+ *       static char        *makeBarrelshiftString()
+ *
+ *    This automatically generates dwa code for the hit-miss transform.
+ *    Here's a road map for how it all works.
+ *
+ *    (1) You generate an array (a SELA) of hit-miss transform SELs.
+ *        This can be done in several ways, including
+ *           (a) calling the function selaAddHitMiss() for
+ *               pre-compiled SELs
+ *           (b) generating the SELA in code in line
+ *           (c) reading in a SELA from file, using selaRead()
+ *               or various other formats.
+ *
+ *    (2) You call fhmtautogen1() and fhmtautogen2() on this SELA.
+ *        This uses the text files hmttemplate1.txt and
+ *        hmttemplate2.txt for building up the source code.  See the file
+ *        prog/fhmtautogen.c for an example of how this is done.
+ *        The output is written to files named fhmtgen.*.c
+ *        and fhmtgenlow.*.c, where "*" is an integer that you
+ *        input to this function.  That integer labels both
+ *        the output files, as well as all the functions that
+ *        are generated.  That way, using different integers,
+ *        you can invoke fhmtautogen() any number of times
+ *        to get functions that all have different names so that
+ *        they can be linked into one program.
+ *
+ *    (3) You copy the generated source code back to your src
+ *        directory for compilation.  Put their names in the
+ *        Makefile, regnerate the prototypes, and recompile
+ *        the libraries.  Look at the Makefile to see how I've
+ *        included fhmtgen.1.c and fhmtgenlow.1.c.  These files
+ *        provide the high-level interfaces for the hmt, and
+ *        the low-level interfaces to do the actual work.
+ *
+ *    (4) In an application, you now use this interface.  Again
+ *        for the example files generated, using integer "1":
+ *
+ *           PIX   *pixHMTDwa_1(PIX *pixd, PIX *pixs, const char *selname);
+ *
+ *              or
+ *
+ *           PIX   *pixFHMTGen_1(PIX *pixd, PIX *pixs, const char *selname);
+ *
+ *        where the selname is one of the set that were defined
+ *        as the name field of sels.  This set is listed at the
+ *        beginning of the file fhmtgen.1.c.
+ *        As an example, see the file prog/fmtauto_reg.c, which
+ *        verifies the correctness of the implementation by
+ *        comparing the dwa result with that of full-image
+ *        rasterops.
+ * 
+ */ + +#include +#include "allheaders.h" + +#define OUTROOT "fhmtgen" +#define TEMPLATE1 "hmttemplate1.txt" +#define TEMPLATE2 "hmttemplate2.txt" + +#define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);" + +//static const l_int32 L_BUF_SIZE = 512; +#define L_BUF_SIZE 512 + +static char * makeBarrelshiftString(l_int32 delx, l_int32 dely, l_int32 type); +static SARRAY * sarrayMakeInnerLoopDWACode(SEL *sel, l_int32 nhits, l_int32 nmisses); +static SARRAY * sarrayMakeWplsCode(SEL *sel); + +static char wpldecls[][60] = { + "l_int32 wpls2;", + "l_int32 wpls2, wpls3;", + "l_int32 wpls2, wpls3, wpls4;", + "l_int32 wpls5;", + "l_int32 wpls5, wpls6;", + "l_int32 wpls5, wpls6, wpls7;", + "l_int32 wpls5, wpls6, wpls7, wpls8;", + "l_int32 wpls9;", + "l_int32 wpls9, wpls10;", + "l_int32 wpls9, wpls10, wpls11;", + "l_int32 wpls9, wpls10, wpls11, wpls12;", + "l_int32 wpls13;", + "l_int32 wpls13, wpls14;", + "l_int32 wpls13, wpls14, wpls15;", + "l_int32 wpls13, wpls14, wpls15, wpls16;", + "l_int32 wpls17;", + "l_int32 wpls17, wpls18;", + "l_int32 wpls17, wpls18, wpls19;", + "l_int32 wpls17, wpls18, wpls19, wpls20;", + "l_int32 wpls21;", + "l_int32 wpls21, wpls22;", + "l_int32 wpls21, wpls22, wpls23;", + "l_int32 wpls21, wpls22, wpls23, wpls24;", + "l_int32 wpls25;", + "l_int32 wpls25, wpls26;", + "l_int32 wpls25, wpls26, wpls27;", + "l_int32 wpls25, wpls26, wpls27, wpls28;", + "l_int32 wpls29;", + "l_int32 wpls29, wpls30;", + "l_int32 wpls29, wpls30, wpls31;"}; + +static char wpldefs[][24] = { + " wpls2 = 2 * wpls;", + " wpls3 = 3 * wpls;", + " wpls4 = 4 * wpls;", + " wpls5 = 5 * wpls;", + " wpls6 = 6 * wpls;", + " wpls7 = 7 * wpls;", + " wpls8 = 8 * wpls;", + " wpls9 = 9 * wpls;", + " wpls10 = 10 * wpls;", + " wpls11 = 11 * wpls;", + " wpls12 = 12 * wpls;", + " wpls13 = 13 * wpls;", + " wpls14 = 14 * wpls;", + " wpls15 = 15 * wpls;", + " wpls16 = 16 * wpls;", + " wpls17 = 17 * wpls;", + " wpls18 = 18 * wpls;", + " wpls19 = 19 * wpls;", + " wpls20 = 20 * wpls;", + " wpls21 = 21 * wpls;", + " wpls22 = 22 * wpls;", + " wpls23 = 23 * wpls;", + " wpls24 = 24 * wpls;", + " wpls25 = 25 * wpls;", + " wpls26 = 26 * wpls;", + " wpls27 = 27 * wpls;", + " wpls28 = 28 * wpls;", + " wpls29 = 29 * wpls;", + " wpls30 = 30 * wpls;", + " wpls31 = 31 * wpls;"}; + +static char wplstrp[][10] = {"+ wpls", "+ wpls2", "+ wpls3", "+ wpls4", + "+ wpls5", "+ wpls6", "+ wpls7", "+ wpls8", + "+ wpls9", "+ wpls10", "+ wpls11", "+ wpls12", + "+ wpls13", "+ wpls14", "+ wpls15", "+ wpls16", + "+ wpls17", "+ wpls18", "+ wpls19", "+ wpls20", + "+ wpls21", "+ wpls22", "+ wpls23", "+ wpls24", + "+ wpls25", "+ wpls26", "+ wpls27", "+ wpls28", + "+ wpls29", "+ wpls30", "+ wpls31"}; + +static char wplstrm[][10] = {"- wpls", "- wpls2", "- wpls3", "- wpls4", + "- wpls5", "- wpls6", "- wpls7", "- wpls8", + "- wpls9", "- wpls10", "- wpls11", "- wpls12", + "- wpls13", "- wpls14", "- wpls15", "- wpls16", + "- wpls17", "- wpls18", "- wpls19", "- wpls20", + "- wpls21", "- wpls22", "- wpls23", "- wpls24", + "- wpls25", "- wpls26", "- wpls27", "- wpls28", + "- wpls29", "- wpls30", "- wpls31"}; + + +/*! + * \brief fhmtautogen() + * + * \param[in] sela + * \param[in] fileindex + * \param[in] filename [optional]; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This function generates all the code for implementing
+ *          dwa morphological operations using all the sels in the sela.
+ *      (2) See fhmtautogen1() and fhmtautogen2() for details.
+ * 
+ */ +l_ok +fhmtautogen(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +l_int32 ret1, ret2; + + PROCNAME("fhmtautogen"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + ret1 = fhmtautogen1(sela, fileindex, filename); + ret2 = fhmtautogen2(sela, fileindex, filename); + if (ret1 || ret2) + return ERROR_INT("code generation problem", procName, 1); + return 0; +} + + +/*! + * \brief fhmtautogen1() + * + * \param[in] sela array + * \param[in] fileindex + * \param[in] filename [optional]; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This function uses hmttemplate1.txt to create a
+ *          top-level file that contains two functions that carry
+ *          out the hit-miss transform for any of the sels in
+ *          the input sela.
+ *      (2) The fileindex parameter is inserted into the output
+ *          filename, as described below.
+ *      (3) If filename == NULL, the output file is fhmtgen.[n].c,
+ *          where [n] is equal to the 'fileindex' parameter.
+ *      (4) If filename != NULL, the output file is [filename].[n].c.
+ *      (5) Each sel must have at least one hit.  A sel with only misses
+ *          generates code that will abort the operation if it is called.
+ * 
+ */ +l_ok +fhmtautogen1(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +char *filestr; +char *str_proto1, *str_proto2, *str_proto3; +char *str_doc1, *str_doc2, *str_doc3, *str_doc4; +char *str_def1, *str_def2, *str_proc1, *str_proc2; +char *str_dwa1, *str_low_dt, *str_low_ds; +char bigbuf[L_BUF_SIZE]; +l_int32 i, nsels, nbytes, actstart, end, newstart; +size_t size; +SARRAY *sa1, *sa2, *sa3; + + PROCNAME("fhmtautogen1"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + if (fileindex < 0) + fileindex = 0; + if ((nsels = selaGetCount(sela)) == 0) + return ERROR_INT("no sels in sela", procName, 1); + + /* Make array of textlines from from hmttemplate1.txt */ + if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + sa2 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + if (!sa2) + return ERROR_INT("sa2 not made", procName, 1); + + /* Make array of sel names */ + sa1 = selaGetSelnames(sela); + + /* Make strings containing function call names */ + sprintf(bigbuf, "PIX *pixHMTDwa_%d(PIX *pixd, PIX *pixs, " + "const char *selname);", fileindex); + str_proto1 = stringNew(bigbuf); + sprintf(bigbuf, "PIX *pixFHMTGen_%d(PIX *pixd, PIX *pixs, " + "const char *selname);", fileindex); + str_proto2 = stringNew(bigbuf); + sprintf(bigbuf, "l_int32 fhmtgen_low_%d(l_uint32 *datad, l_int32 w,\n" + " l_int32 h, l_int32 wpld,\n" + " l_uint32 *datas, l_int32 wpls,\n" + " l_int32 index);", fileindex); + str_proto3 = stringNew(bigbuf); + sprintf(bigbuf, " * PIX *pixHMTDwa_%d()", fileindex); + str_doc1 = stringNew(bigbuf); + sprintf(bigbuf, " * PIX *pixFHMTGen_%d()", fileindex); + str_doc2 = stringNew(bigbuf); + sprintf(bigbuf, " * \\brief pixHMTDwa_%d()", fileindex); + str_doc3 = stringNew(bigbuf); + sprintf(bigbuf, " * \\brief pixFHMTGen_%d()", fileindex); + str_doc4 = stringNew(bigbuf); + sprintf(bigbuf, "pixHMTDwa_%d(PIX *pixd,", fileindex); + str_def1 = stringNew(bigbuf); + sprintf(bigbuf, "pixFHMTGen_%d(PIX *pixd,", fileindex); + str_def2 = stringNew(bigbuf); + sprintf(bigbuf, " PROCNAME(\"pixHMTDwa_%d\");", fileindex); + str_proc1 = stringNew(bigbuf); + sprintf(bigbuf, " PROCNAME(\"pixFHMTGen_%d\");", fileindex); + str_proc2 = stringNew(bigbuf); + sprintf(bigbuf, " pixt2 = pixFHMTGen_%d(NULL, pixt1, selname);", + fileindex); + str_dwa1 = stringNew(bigbuf); + sprintf(bigbuf, + " fhmtgen_low_%d(datad, w, h, wpld, datat, wpls, index);", + fileindex); + str_low_dt = stringNew(bigbuf); + sprintf(bigbuf, + " fhmtgen_low_%d(datad, w, h, wpld, datas, wpls, index);", + fileindex); + str_low_ds = stringNew(bigbuf); + + /* Make the output sa */ + sa3 = sarrayCreate(0); + + /* Copyright notice and info header */ + sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Insert function names as documentation */ + sarrayAddString(sa3, str_doc1, L_INSERT); + sarrayAddString(sa3, str_doc2, L_INSERT); + + /* Add '#include's */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Insert function prototypes */ + sarrayAddString(sa3, str_proto1, L_INSERT); + sarrayAddString(sa3, str_proto2, L_INSERT); + sarrayAddString(sa3, str_proto3, L_INSERT); + + /* Add static globals */ + sprintf(bigbuf, "\nstatic l_int32 NUM_SELS_GENERATED = %d;", nsels); + sarrayAddString(sa3, bigbuf, L_COPY); + sprintf(bigbuf, "static char SEL_NAMES[][80] = {"); + sarrayAddString(sa3, bigbuf, L_COPY); + for (i = 0; i < nsels - 1; i++) { + sprintf(bigbuf, " \"%s\",", + sarrayGetString(sa1, i, L_NOCOPY)); + sarrayAddString(sa3, bigbuf, L_COPY); + } + sprintf(bigbuf, " \"%s\"};", + sarrayGetString(sa1, i, L_NOCOPY)); + sarrayAddString(sa3, bigbuf, L_COPY); + + /* Start pixHMTDwa_*() function description */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_doc3, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Finish pixHMTDwa_*() function definition */ + sarrayAddString(sa3, str_def1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_proc1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_dwa1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Start pixFHMTGen_*() function description */ + sarrayAddString(sa3, str_doc4, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Finish pixFHMTGen_*() function description */ + sarrayAddString(sa3, str_def2, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_proc2, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_dt, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_ds, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + filestr = sarrayToString(sa3, 1); + nbytes = strlen(filestr); + if (filename) + snprintf(bigbuf, L_BUF_SIZE, "%s.%d.c", filename, fileindex); + else + sprintf(bigbuf, "%s.%d.c", OUTROOT, fileindex); + l_binaryWrite(bigbuf, "w", filestr, nbytes); + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + LEPT_FREE(filestr); + return 0; +} + + +/*! + * \brief fhmtautogen2() + * + * \param[in] sela array + * \param[in] fileindex + * \param[in] filename [optional]; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This function uses hmttemplate2.txt to create a
+ *          low-level file that contains the low-level functions for
+ *          implementing the hit-miss transform for every sel
+ *          in the input sela.
+ *      (2) The fileindex parameter is inserted into the output
+ *          filename, as described below.
+ *      (3) If filename == NULL, the output file is fhmtgenlow.[n].c,
+ *          where [n] is equal to the %fileindex parameter.
+ *      (4) If filename != NULL, the output file is [filename]low.[n].c.
+ * 
+ */ +l_ok +fhmtautogen2(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +char *filestr, *fname, *linestr; +char *str_doc1, *str_doc2, *str_doc3, *str_def1; +char bigbuf[L_BUF_SIZE]; +char breakstring[] = " break;"; +char staticstring[] = "static void"; +l_int32 i, k, l, nsels, nbytes, nhits, nmisses; +l_int32 actstart, end, newstart; +l_int32 argstart, argend, loopstart, loopend, finalstart, finalend; +size_t size; +SARRAY *sa1, *sa2, *sa3, *sa4, *sa5, *sa6; +SEL *sel; + + PROCNAME("fhmtautogen2"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + if (fileindex < 0) + fileindex = 0; + if ((nsels = selaGetCount(sela)) == 0) + return ERROR_INT("no sels in sela", procName, 1); + + /* Make the array of textlines from hmttemplate2.txt */ + if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + sa1 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + if (!sa1) + return ERROR_INT("sa1 not made", procName, 1); + + /* Make the array of static function names */ + if ((sa2 = sarrayCreate(nsels)) == NULL) { + sarrayDestroy(&sa1); + return ERROR_INT("sa2 not made", procName, 1); + } + for (i = 0; i < nsels; i++) { + sprintf(bigbuf, "fhmt_%d_%d", fileindex, i); + sarrayAddString(sa2, bigbuf, L_COPY); + } + + /* Make the static prototype strings */ + sa3 = sarrayCreate(2 * nsels); /* should be ok */ + for (i = 0; i < nsels; i++) { + fname = sarrayGetString(sa2, i, L_NOCOPY); + sprintf(bigbuf, "static void %s%s", fname, PROTOARGS); + sarrayAddString(sa3, bigbuf, L_COPY); + } + + /* Make strings containing function names */ + sprintf(bigbuf, " * l_int32 fhmtgen_low_%d()", + fileindex); + str_doc1 = stringNew(bigbuf); + sprintf(bigbuf, " * void fhmt_%d_*()", fileindex); + str_doc2 = stringNew(bigbuf); + + /* Output to this sa */ + sa4 = sarrayCreate(0); + + /* Copyright notice and info header */ + sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Insert function names as documentation */ + sarrayAddString(sa4, str_doc1, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_doc2, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Insert static protos */ + for (i = 0; i < nsels; i++) { + if ((linestr = sarrayGetString(sa3, i, L_COPY)) == NULL) { + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + return ERROR_INT("linestr not retrieved", procName, 1); + } + sarrayAddString(sa4, linestr, L_INSERT); + } + + /* Make more strings containing function names */ + sprintf(bigbuf, " * fhmtgen_low_%d()", fileindex); + str_doc3 = stringNew(bigbuf); + sprintf(bigbuf, "fhmtgen_low_%d(l_uint32 *datad,", fileindex); + str_def1 = stringNew(bigbuf); + + /* Insert function header */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_doc3, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_def1, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Generate and insert the dispatcher code */ + for (i = 0; i < nsels; i++) { + sprintf(bigbuf, " case %d:", i); + sarrayAddString(sa4, bigbuf, L_COPY); + sprintf(bigbuf, " %s(datad, w, h, wpld, datas, wpls);", + sarrayGetString(sa2, i, L_NOCOPY)); + sarrayAddString(sa4, bigbuf, L_COPY); + sarrayAddString(sa4, breakstring, L_COPY); + } + + /* Finish the dispatcher and introduce the low-level code */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Get the range for the args common to all functions */ + sarrayParseRange(sa1, newstart, &argstart, &argend, &newstart, "--", 0); + + /* Get the range for the loop code common to all functions */ + sarrayParseRange(sa1, newstart, &loopstart, &loopend, &newstart, "--", 0); + + /* Get the range for the ending code common to all functions */ + sarrayParseRange(sa1, newstart, &finalstart, &finalend, &newstart, "--", 0); + + /* Do all the static functions */ + for (i = 0; i < nsels; i++) { + /* Generate the function header and add the common args */ + sarrayAddString(sa4, staticstring, L_COPY); + fname = sarrayGetString(sa2, i, L_NOCOPY); + sprintf(bigbuf, "%s(l_uint32 *datad,", fname); + sarrayAddString(sa4, bigbuf, L_COPY); + sarrayAppendRange(sa4, sa1, argstart, argend); + + /* Declare and define wplsN args, as necessary */ + if ((sel = selaGetSel(sela, i)) == NULL) { + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + return ERROR_INT("sel not returned", procName, 1); + } + sa5 = sarrayMakeWplsCode(sel); + sarrayJoin(sa4, sa5); + sarrayDestroy(&sa5); + + /* Make sure sel has at least one hit */ + nhits = 0; + nmisses = 0; + for (k = 0; k < sel->sy; k++) { + for (l = 0; l < sel->sx; l++) { + if (sel->data[k][l] == 1) + nhits++; + else if (sel->data[k][l] == 2) + nmisses++; + } + } + if (nhits == 0) { + linestr = stringNew(" fprintf(stderr, \"Error in HMT: no hits in sel!\\n\");\n}\n\n"); + sarrayAddString(sa4, linestr, L_INSERT); + continue; + } + + /* Add the function loop code */ + sarrayAppendRange(sa4, sa1, loopstart, loopend); + + /* Insert barrel-op code for *dptr */ + if ((sa6 = sarrayMakeInnerLoopDWACode(sel, nhits, nmisses)) == NULL) { + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + return ERROR_INT("sa6 not made", procName, 1); + } + sarrayJoin(sa4, sa6); + sarrayDestroy(&sa6); + + /* Finish the function code */ + sarrayAppendRange(sa4, sa1, finalstart, finalend); + } + + /* Output to file */ + filestr = sarrayToString(sa4, 1); + nbytes = strlen(filestr); + if (filename) + snprintf(bigbuf, L_BUF_SIZE, "%slow.%d.c", filename, fileindex); + else + sprintf(bigbuf, "%slow.%d.c", OUTROOT, fileindex); + l_binaryWrite(bigbuf, "w", filestr, nbytes); + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + LEPT_FREE(filestr); + return 0; +} + + + +/*--------------------------------------------------------------------------* + * Helper code for sel * + *--------------------------------------------------------------------------*/ +/*! + * \brief sarrayMakeWplsCode() + */ +static SARRAY * +sarrayMakeWplsCode(SEL *sel) +{ +char emptystring[] = ""; +l_int32 i, j, ymax, dely; +SARRAY *sa; + + PROCNAME("sarrayMakeWplsCode"); + + if (!sel) + return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); + + ymax = 0; + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + if (sel->data[i][j] == 1 || sel->data[i][j] == 2) { + dely = L_ABS(i - sel->cy); + ymax = L_MAX(ymax, dely); + } + } + } + if (ymax > 31) { + L_WARNING("ymax > 31; truncating to 31\n", procName); + ymax = 31; + } + + sa = sarrayCreate(0); + + /* Declarations */ + if (ymax > 4) + sarrayAddString(sa, wpldecls[2], L_COPY); + if (ymax > 8) + sarrayAddString(sa, wpldecls[6], L_COPY); + if (ymax > 12) + sarrayAddString(sa, wpldecls[10], L_COPY); + if (ymax > 16) + sarrayAddString(sa, wpldecls[14], L_COPY); + if (ymax > 20) + sarrayAddString(sa, wpldecls[18], L_COPY); + if (ymax > 24) + sarrayAddString(sa, wpldecls[22], L_COPY); + if (ymax > 28) + sarrayAddString(sa, wpldecls[26], L_COPY); + if (ymax > 1) + sarrayAddString(sa, wpldecls[ymax - 2], L_COPY); + + sarrayAddString(sa, emptystring, L_COPY); + + /* Definitions */ + for (i = 2; i <= ymax; i++) + sarrayAddString(sa, wpldefs[i - 2], L_COPY); + + return sa; +} + + +/*! + * \brief sarrayMakeInnerLoopDWACode() + */ +static SARRAY * +sarrayMakeInnerLoopDWACode(SEL *sel, + l_int32 nhits, + l_int32 nmisses) +{ +char *string; +char land[] = "&"; +char bigbuf[L_BUF_SIZE]; +l_int32 i, j, ntot, nfound, type, delx, dely; +SARRAY *sa; + + PROCNAME("sarrayMakeInnerLoopDWACode"); + + if (!sel) + return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); + + sa = sarrayCreate(0); + ntot = nhits + nmisses; + nfound = 0; + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + type = sel->data[i][j]; + if (type == SEL_HIT || type == SEL_MISS) { + nfound++; + dely = i - sel->cy; + delx = j - sel->cx; + if ((string = makeBarrelshiftString(delx, dely, type)) + == NULL) { + L_WARNING("barrel shift string not made\n", procName); + continue; + } + if (ntot == 1) /* just one item */ + sprintf(bigbuf, " *dptr = %s;", string); + else if (nfound == 1) + sprintf(bigbuf, " *dptr = %s %s", string, land); + else if (nfound < ntot) + sprintf(bigbuf, " %s %s", string, land); + else /* nfound == ntot */ + sprintf(bigbuf, " %s;", string); + sarrayAddString(sa, bigbuf, L_COPY); + LEPT_FREE(string); + } + } + } + + return sa; +} + + +/*! + * \brief makeBarrelshiftString() + */ +static char * +makeBarrelshiftString(l_int32 delx, /* j - cx */ + l_int32 dely, /* i - cy */ + l_int32 type) /* SEL_HIT or SEL_MISS */ +{ +l_int32 absx, absy; +char bigbuf[L_BUF_SIZE]; + + PROCNAME("makeBarrelshiftString"); + + if (delx < -31 || delx > 31) + return (char *)ERROR_PTR("delx out of bounds", procName, NULL); + if (dely < -31 || dely > 31) + return (char *)ERROR_PTR("dely out of bounds", procName, NULL); + absx = L_ABS(delx); + absy = L_ABS(dely); + + if (type == SEL_HIT) { + if ((delx == 0) && (dely == 0)) + sprintf(bigbuf, "(*sptr)"); + else if ((delx == 0) && (dely < 0)) + sprintf(bigbuf, "(*(sptr %s))", wplstrm[absy - 1]); + else if ((delx == 0) && (dely > 0)) + sprintf(bigbuf, "(*(sptr %s))", wplstrp[absy - 1]); + else if ((delx < 0) && (dely == 0)) + sprintf(bigbuf, "((*(sptr) >> %d) | (*(sptr - 1) << %d))", + absx, 32 - absx); + else if ((delx > 0) && (dely == 0)) + sprintf(bigbuf, "((*(sptr) << %d) | (*(sptr + 1) >> %d))", + absx, 32 - absx); + else if ((delx < 0) && (dely < 0)) + sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx > 0) && (dely < 0)) + sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx < 0) && (dely > 0)) + sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + else /* ((delx > 0) && (dely > 0)) */ + sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + } else { /* type == SEL_MISS */ + if ((delx == 0) && (dely == 0)) + sprintf(bigbuf, "(~*sptr)"); + else if ((delx == 0) && (dely < 0)) + sprintf(bigbuf, "(~*(sptr %s))", wplstrm[absy - 1]); + else if ((delx == 0) && (dely > 0)) + sprintf(bigbuf, "(~*(sptr %s))", wplstrp[absy - 1]); + else if ((delx < 0) && (dely == 0)) + sprintf(bigbuf, "((~*(sptr) >> %d) | (~*(sptr - 1) << %d))", + absx, 32 - absx); + else if ((delx > 0) && (dely == 0)) + sprintf(bigbuf, "((~*(sptr) << %d) | (~*(sptr + 1) >> %d))", + absx, 32 - absx); + else if ((delx < 0) && (dely < 0)) + sprintf(bigbuf, "((~*(sptr %s) >> %d) | (~*(sptr %s - 1) << %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx > 0) && (dely < 0)) + sprintf(bigbuf, "((~*(sptr %s) << %d) | (~*(sptr %s + 1) >> %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx < 0) && (dely > 0)) + sprintf(bigbuf, "((~*(sptr %s) >> %d) | (~*(sptr %s - 1) << %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + else /* ((delx > 0) && (dely > 0)) */ + sprintf(bigbuf, "((~*(sptr %s) << %d) | (~*(sptr %s + 1) >> %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + } + + return stringNew(bigbuf); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fhmtgen.1.c b/hgdriver/3rdparty/hgOCR/leptonica/fhmtgen.1.c new file mode 100644 index 0000000..8a1fcab --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fhmtgen.1.c @@ -0,0 +1,177 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Top-level fast hit-miss transform with auto-generated sels + * + * PIX *pixHMTDwa_1() + * PIX *pixFHMTGen_1() + */ + +#include +#include "allheaders.h" + +PIX *pixHMTDwa_1(PIX *pixd, PIX *pixs, const char *selname); +PIX *pixFHMTGen_1(PIX *pixd, PIX *pixs, const char *selname); +l_int32 fhmtgen_low_1(l_uint32 *datad, l_int32 w, + l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, + l_int32 index); + +static l_int32 NUM_SELS_GENERATED = 10; +static char SEL_NAMES[][80] = { + "sel_3hm", + "sel_3de", + "sel_3ue", + "sel_3re", + "sel_3le", + "sel_sl1", + "sel_ulc", + "sel_urc", + "sel_llc", + "sel_lrc"}; + +/*! + * \brief pixHMTDwa_1() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This simply adds a 32 pixel border, calls the appropriate
+ *          pixFHMTGen_*(), and removes the border.
+ *          See notes below for that function.
+ * 
+ */ +PIX * +pixHMTDwa_1(PIX *pixd, + PIX *pixs, + const char *selname) +{ +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixHMTDwa_1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + pixt1 = pixAddBorder(pixs, 32, 0); + pixt2 = pixFHMTGen_1(NULL, pixt1, selname); + pixt3 = pixRemoveBorder(pixt2, 32); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixCopy(pixd, pixt3); + pixDestroy(&pixt3); + return pixd; +} + + +/*! + * \brief pixFHMTGen_1() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This is a dwa implementation of the hit-miss transform
+ *          on pixs by the sel.
+ *      (2) The sel must be limited in size to not more than 31 pixels
+ *          about the origin.  It must have at least one hit, and it
+ *          can have any number of misses.
+ *      (3) This handles all required setting of the border pixels
+ *          before erosion and dilation.
+ * 
+ */ +PIX * +pixFHMTGen_1(PIX *pixd, + PIX *pixs, + const char *selname) +{ +l_int32 i, index, found, w, h, wpls, wpld; +l_uint32 *datad, *datas, *datat; +PIX *pixt; + + PROCNAME("pixFHMTGen_1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + found = FALSE; + for (i = 0; i < NUM_SELS_GENERATED; i++) { + if (strcmp(selname, SEL_NAMES[i]) == 0) { + found = TRUE; + index = i; + break; + } + } + if (found == FALSE) + return (PIX *)ERROR_PTR("sel index not found", procName, pixd); + + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + else /* for in-place or pre-allocated */ + pixResizeImageData(pixd, pixs); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* The images must be surrounded with 32 additional border + * pixels, that we'll read from. We fabricate a "proper" + * image as the subimage within the border, having the + * following parameters: */ + w = pixGetWidth(pixs) - 64; + h = pixGetHeight(pixs) - 64; + datas = pixGetData(pixs) + 32 * wpls + 1; + datad = pixGetData(pixd) + 32 * wpld + 1; + + if (pixd == pixs) { /* need temp image if in-place */ + if ((pixt = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + 32 * wpls + 1; + fhmtgen_low_1(datad, w, h, wpld, datat, wpls, index); + pixDestroy(&pixt); + } + else { /* not in-place */ + fhmtgen_low_1(datad, w, h, wpld, datas, wpls, index); + } + + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fhmtgenlow.1.c b/hgdriver/3rdparty/hgOCR/leptonica/fhmtgenlow.1.c new file mode 100644 index 0000000..b1c863c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fhmtgenlow.1.c @@ -0,0 +1,445 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Low-level fast hit-miss transform with auto-generated sels + * + * Dispatcher: + * l_int32 fhmtgen_low_1() + * + * Static Low-level: + * void fhmt_1_*() + */ + +#include "allheaders.h" + +static void fhmt_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); + + +/*---------------------------------------------------------------------* + * Fast hmt dispatcher * + *---------------------------------------------------------------------*/ +/*! + * fhmtgen_low_1() + * + * a dispatcher to appropriate low-level code + */ +l_int32 +fhmtgen_low_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 index) +{ + + switch (index) + { + case 0: + fhmt_1_0(datad, w, h, wpld, datas, wpls); + break; + case 1: + fhmt_1_1(datad, w, h, wpld, datas, wpls); + break; + case 2: + fhmt_1_2(datad, w, h, wpld, datas, wpls); + break; + case 3: + fhmt_1_3(datad, w, h, wpld, datas, wpls); + break; + case 4: + fhmt_1_4(datad, w, h, wpld, datas, wpls); + break; + case 5: + fhmt_1_5(datad, w, h, wpld, datas, wpls); + break; + case 6: + fhmt_1_6(datad, w, h, wpld, datas, wpls); + break; + case 7: + fhmt_1_7(datad, w, h, wpld, datas, wpls); + break; + case 8: + fhmt_1_8(datad, w, h, wpld, datas, wpls); + break; + case 9: + fhmt_1_9(datad, w, h, wpld, datas, wpls); + break; + } + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Low-level auto-generated static routines * + *--------------------------------------------------------------------------*/ +/* + * N.B. In all the low-level routines, the part of the image + * that is accessed has been clipped by 32 pixels on + * all four sides. This is done in the higher level + * code by redefining w and h smaller and by moving the + * start-of-image pointers up to the beginning of this + * interior rectangle. + */ +static void +fhmt_1_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (~*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (~*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fhmt_1_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (~*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fhmt_1_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (~*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)); + } + } +} + +static void +fhmt_1_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + (*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + (*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fhmt_1_4(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (*(sptr - wpls)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (*sptr) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (*(sptr + wpls)); + } + } +} + +static void +fhmt_1_5(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls6) << 1) | (~*(sptr - wpls6 + 1) >> 31)) & + ((*(sptr - wpls6) << 3) | (*(sptr - wpls6 + 1) >> 29)) & + (~*(sptr - wpls2)) & + ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & + ((~*(sptr + wpls2) >> 1) | (~*(sptr + wpls2 - 1) << 31)) & + ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & + ((~*(sptr + wpls6) >> 2) | (~*(sptr + wpls6 - 1) << 30)) & + (*(sptr + wpls6)); + } + } +} + +static void +fhmt_1_6(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (~*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((~*(sptr - wpls) << 2) | (~*(sptr - wpls + 1) >> 30)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & + ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) & + ((~*(sptr + wpls2) >> 1) | (~*(sptr + wpls2 - 1) << 31)) & + (*(sptr + wpls2)) & + ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & + ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); + } + } +} + +static void +fhmt_1_7(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 2) | (~*(sptr - wpls - 1) << 30)) & + ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (~*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)) & + ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) & + ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) & + (*(sptr + wpls2)) & + ((~*(sptr + wpls2) << 1) | (~*(sptr + wpls2 + 1) >> 31)); + } + } +} + +static void +fhmt_1_8(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls2) >> 1) | (~*(sptr - wpls2 - 1) << 31)) & + (*(sptr - wpls2)) & + ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & + ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & + ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & + ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (~*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)) & + ((~*(sptr + wpls) << 2) | (~*(sptr + wpls + 1) >> 30)); + } + } +} + +static void +fhmt_1_9(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & + ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & + (*(sptr - wpls2)) & + ((~*(sptr - wpls2) << 1) | (~*(sptr - wpls2 + 1) >> 31)) & + ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((~*(sptr + wpls) >> 2) | (~*(sptr + wpls - 1) << 30)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (~*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); + } + } +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/finditalic.c b/hgdriver/3rdparty/hgOCR/leptonica/finditalic.c new file mode 100644 index 0000000..1e0d48d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/finditalic.c @@ -0,0 +1,239 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * \file finditalic.c + *
+ *
+ *      l_int32   pixItalicWords()
+ *
+ *    Locate italic words.  This is an example of the use of
+ *    hit-miss binary morphology with binary reconstruction
+ *    (filling from a seed into a mask).
+ *
+ *    To see how this works, run with prog/italic.png.
+ * 
+ */ + +#include "allheaders.h" + + /* --------------------------------------------------------------- * + * These hit-miss sels match the slanted edge of italic characters * + * --------------------------------------------------------------- */ +static const char *str_ital1 = " o x" + " " + " " + " " + " o x " + " " + " C " + " " + " o x " + " " + " " + " " + "o x "; + +static const char *str_ital2 = " o x" + " " + " " + " o x " + " C " + " " + " o x " + " " + " " + "o x "; + + /* ------------------------------------------------------------- * + * This sel removes noise that is not oriented as a slanted edge * + * ------------------------------------------------------------- */ +static const char *str_ital3 = " x" + "Cx" + "x " + "x "; + +/*! + * \brief pixItalicWords() + * + * \param[in] pixs 1 bpp + * \param[in] boxaw [optional] word bounding boxes; can be NULL + * \param[in] pixw [optional] word box mask; can be NULL + * \param[out] pboxa boxa of italic words + * \param[in] debugflag 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) You can input the bounding boxes for the words in one of
+ *          two forms: as bounding boxes (%boxaw) or as a word mask with
+ *          the word bounding boxes filled (%pixw).  For example,
+ *          to compute %pixw, you can use pixWordMaskByDilation().
+ *      (2) Alternatively, you can set both of these inputs to NULL,
+ *          in which case the word mask is generated here.  This is
+ *          done by dilating and closing the input image to connect
+ *          letters within a word, while leaving the words separated.
+ *          The parameters are chosen under the assumption that the
+ *          input is 10 to 12 pt text, scanned at about 300 ppi.
+ *      (3) sel_ital1 and sel_ital2 detect the right edges that are
+ *          nearly vertical, at approximately the angle of italic
+ *          strokes.  We use the right edge to avoid getting seeds
+ *          from lower-case 'y'.  The typical italic slant has a smaller
+ *          angle with the vertical than the 'W', so in most cases we
+ *          will not trigger on the slanted lines in the 'W'.
+ *      (4) Note that sel_ital2 is shorter than sel_ital1.  It is
+ *          more appropriate for a typical font scanned at 200 ppi.
+ * 
+ */ +l_ok +pixItalicWords(PIX *pixs, + BOXA *boxaw, + PIX *pixw, + BOXA **pboxa, + l_int32 debugflag) +{ +char opstring[32]; +l_int32 size; +BOXA *boxa; +PIX *pixsd, *pixm, *pixd; +SEL *sel_ital1, *sel_ital2, *sel_ital3; + + PROCNAME("pixItalicWords"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pboxa) + return ERROR_INT("&boxa not defined", procName, 1); + if (boxaw && pixw) + return ERROR_INT("both boxaw and pixw are defined", procName, 1); + + sel_ital1 = selCreateFromString(str_ital1, 13, 6, NULL); + sel_ital2 = selCreateFromString(str_ital2, 10, 6, NULL); + sel_ital3 = selCreateFromString(str_ital3, 4, 2, NULL); + + /* Make the italic seed: extract with HMT; remove noise. + * The noise removal close/open is important to exclude + * situations where a small slanted line accidentally + * matches sel_ital1. */ + pixsd = pixHMT(NULL, pixs, sel_ital1); + pixClose(pixsd, pixsd, sel_ital3); + pixOpen(pixsd, pixsd, sel_ital3); + + /* Make the word mask. Use input boxes or mask if given. */ + size = 0; /* init */ + if (boxaw) { + pixm = pixCreateTemplate(pixs); + pixMaskBoxa(pixm, pixm, boxaw, L_SET_PIXELS); + } else if (pixw) { + pixm = pixClone(pixw); + } else { + pixWordMaskByDilation(pixs, NULL, &size, NULL); + L_INFO("dilation size = %d\n", procName, size); + snprintf(opstring, sizeof(opstring), "d1.5 + c%d.1", size); + pixm = pixMorphSequence(pixs, opstring, 0); + } + + /* Binary reconstruction to fill in those word mask + * components for which there is at least one seed pixel. */ + pixd = pixSeedfillBinary(NULL, pixsd, pixm, 8); + boxa = pixConnComp(pixd, NULL, 8); + *pboxa = boxa; + + if (debugflag) { + /* Save results at at 2x reduction */ + lept_mkdir("lept/ital"); + l_int32 res, upper; + BOXA *boxat; + GPLOT *gplot; + NUMA *na; + PIXA *pad; + PIX *pix1, *pix2, *pix3; + pad = pixaCreate(0); + boxat = pixConnComp(pixm, NULL, 8); + boxaWriteDebug("/tmp/lept/ital/ital.ba", boxat); + pixSaveTiledOutline(pixs, pad, 0.5, 1, 20, 2, 32); /* orig */ + pixSaveTiledOutline(pixsd, pad, 0.5, 1, 20, 2, 0); /* seed */ + pix1 = pixConvertTo32(pixm); + pixRenderBoxaArb(pix1, boxat, 3, 255, 0, 0); + pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* mask + outline */ + pixDestroy(&pix1); + pixSaveTiledOutline(pixd, pad, 0.5, 1, 20, 2, 0); /* ital mask */ + pix1 = pixConvertTo32(pixs); + pixRenderBoxaArb(pix1, boxa, 3, 255, 0, 0); + pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* orig + outline */ + pixDestroy(&pix1); + pix1 = pixCreateTemplate(pixs); + pix2 = pixSetBlackOrWhiteBoxa(pix1, boxa, L_SET_BLACK); + pixCopy(pix1, pixs); + pix3 = pixDilateBrick(NULL, pixs, 3, 3); + pixCombineMasked(pix1, pix3, pix2); + pixSaveTiledOutline(pix1, pad, 0.5, 1, 20, 2, 0); /* ital bolded */ + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pix2 = pixaDisplay(pad, 0, 0); + pixWriteDebug("/tmp/lept/ital/ital.png", pix2, IFF_PNG); + pixDestroy(&pix2); + + /* Assuming the image represents 6 inches of actual page width, + * the pixs resolution is approximately + * (width of pixs in pixels) / 6 + * and the images have been saved at half this resolution. */ + res = pixGetWidth(pixs) / 12; + L_INFO("resolution = %d\n", procName, res); + l_pdfSetDateAndVersion(0); + pixaConvertToPdf(pad, res, 1.0, L_FLATE_ENCODE, 75, "Italic Finder", + "/tmp/lept/ital/ital.pdf"); + l_pdfSetDateAndVersion(1); + pixaDestroy(&pad); + boxaDestroy(&boxat); + + /* Plot histogram of horizontal white run sizes. A small + * initial vertical dilation removes most runs that are neither + * inter-character nor inter-word. The larger first peak is + * from inter-character runs, and the smaller second peak is + * from inter-word runs. */ + pix1 = pixDilateBrick(NULL, pixs, 1, 15); + upper = L_MAX(30, 3 * size); + na = pixRunHistogramMorph(pix1, L_RUN_OFF, L_HORIZ, upper); + pixDestroy(&pix1); + gplot = gplotCreate("/tmp/lept/ital/runhisto", GPLOT_PNG, + "Histogram of horizontal runs of white pixels, vs length", + "run length", "number of runs"); + gplotAddPlot(gplot, NULL, na, GPLOT_LINES, "plot1"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + numaDestroy(&na); + } + + selDestroy(&sel_ital1); + selDestroy(&sel_ital2); + selDestroy(&sel_ital3); + pixDestroy(&pixsd); + pixDestroy(&pixm); + pixDestroy(&pixd); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/flipdetect.c b/hgdriver/3rdparty/hgOCR/leptonica/flipdetect.c new file mode 100644 index 0000000..34786c6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/flipdetect.c @@ -0,0 +1,1130 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file flipdetect.c + *
+ *
+ *      High-level interface for detection and correction
+ *          l_int32      pixOrientCorrect()
+ *
+ *      Page orientation detection (pure rotation by 90 degree increments):
+ *          l_int32      pixOrientDetect()
+ *          l_int32      makeOrientDecision()
+ *          l_int32      pixUpDownDetect()
+ *          l_int32      pixUpDownDetectGeneral()
+ *          l_int32      pixOrientDetectDwa()
+ *          l_int32      pixUpDownDetectDwa()
+ *          l_int32      pixUpDownDetectGeneralDwa()
+ *
+ *      Page mirror detection (flip 180 degrees about line in plane of image):
+ *          l_int32      pixMirrorDetect()
+ *          l_int32      pixMirrorDetectDwa()
+ *
+ *      Static debug helper
+ *          void         pixDebugFlipDetect()
+ *
+ *  ===================================================================
+ *
+ *  Page transformation detection:
+ *
+ *  Once a page is deskewed, there are 8 possible states that it
+ *  can be in, shown symbolically below.  Suppose state 0 is correct.
+ *
+ *      0: correct     1          2          3
+ *      +------+   +------+   +------+   +------+
+ *      | **** |   | *    |   | **** |   |    * |
+ *      | *    |   | *    |   |    * |   |    * |
+ *      | *    |   | **** |   |    * |   | **** |
+ *      +------+   +------+   +------+   +------+
+ *
+ *         4          5          6          7
+ *      +-----+    +-----+    +-----+    +-----+
+ *      | *** |    |   * |    | *** |    | *   |
+ *      |   * |    |   * |    | *   |    | *   |
+ *      |   * |    |   * |    | *   |    | *   |
+ *      |   * |    | *** |    | *   |    | *** |
+ *      +-----+    +-----+    +-----+    +-----+
+ *
+ *  Each of the other seven can be derived from state 0 by applying some
+ *  combination of a 90 degree clockwise rotation, a flip about
+ *  a horizontal line, and a flip about a vertical line,
+ *  all abbreviated as:
+ *      R = Rotation (about a line perpendicular to the image)
+ *      H = Horizontal flip (about a vertical line in the plane of the image)
+ *      V = Vertical flip (about a horizontal line in the plane of the image)
+ *
+ *  We get these transformations:
+ *      RHV
+ *      000  -> 0
+ *      001  -> 1
+ *      010  -> 2
+ *      011  -> 3
+ *      100  -> 4
+ *      101  -> 5
+ *      110  -> 6
+ *      111  -> 7
+ *
+ *  Note that in four of these, the sum of H and V is 1 (odd).
+ *  For these four, we have a change in parity (handedness) of
+ *  the image, and the transformation cannot be performed by
+ *  rotation about a vertical line out of the page.   Under
+ *  rotation R, the set of 8 transformations decomposes into
+ *  two subgroups linking {0, 3, 4, 7} and {1, 2, 5, 6} independently.
+ *
+ *  pixOrientDetect*() tests for a pure rotation (0, 90, 180, 270 degrees).
+ *  It doesn't change parity.
+ *
+ *  pixMirrorDetect*() tests for a horizontal flip about the vertical axis.
+ *  It changes parity.
+ *
+ *  The landscape/portrait rotation can be detected in two ways:
+ *
+ *    (1) Compute the deskew confidence for an image segment,
+ *        both as is and rotated 90 degrees  (see skew.c).
+ *
+ *    (2) Compute the ascender/descender signal for the image,
+ *        both as is and rotated 90 degrees  (implemented here).
+ *
+ *  The ascender/descender signal is useful for determining text
+ *  orientation in Roman alphabets because the incidence of letters
+ *  with straight-line ascenders (b, d, h, k, l, 't') outnumber
+ *  those with descenders ('g', p, q).  The letters 't' and 'g'
+ *  will respond variably to the filter, depending on the type face.
+ *
+ *  What about the mirror image situations?  These aren't common
+ *  unless you're dealing with film, for example.
+ *  But you can reliably test if the image has undergone a
+ *  parity-changing flip once about some axis in the plane
+ *  of the image, using pixMirrorDetect*().  This works ostensibly by
+ *  counting the number of characters with ascenders that
+ *  stick out to the left and right of the ascender.  Characters
+ *  that are not mirror flipped are more likely to extend to the
+ *  right (b, h, k) than to the left (d).  Of course, that is for
+ *  text that is rightside-up.  So before you apply the mirror
+ *  test, it is necessary to insure that the text has the ascenders
+ *  going up, and not down or to the left or right.  But here's
+ *  what *really* happens.  It turns out that the pre-filtering before
+ *  the hit-miss transform (HMT) is crucial, and surprisingly, when
+ *  the pre-filtering is chosen to generate a large signal, the majority
+ *  of the signal comes from open regions of common lower-case
+ *  letters such as 'e', 'c' and 'f'.
+ *
+ *  All operations are given in two implementations whose results are
+ *  identical: rasterop morphology and dwa morphology.  The dwa
+ *  implementations are between 2x and 3x faster.
+ *
+ *  The set of operations you actually use depends on your prior knowledge:
+ *
+ *  (1) If the page is known to be either rightside-up or upside-down, use
+ *      either pixOrientDetect*() with pleftconf = NULL, or
+ *      pixUpDownDetect*().   [The '*' refers to either the rasterop
+ *      or dwa versions.]
+ *
+ *  (2) If any of the four orientations are possible, use pixOrientDetect*().
+ *
+ *  (3) If the text is horizontal and rightside-up, the only remaining
+ *      degree of freedom is a left-right mirror flip: use
+ *      pixMirrorDetect*().
+ *
+ *  (4) If you have a relatively large amount of numbers on the page,
+ *      us the slower pixUpDownDetectGeneral().
+ *
+ *  We summarize the full orientation and mirror flip detection process:
+ *
+ *  (1) First determine which of the four 90 degree rotations
+ *      causes the text to be rightside-up.  This can be done
+ *      with either skew confidence or the pixOrientDetect*()
+ *      signals.  For the latter, see the table for pixOrientDetect().
+ *
+ *  (2) Then, with ascenders pointing up, apply pixMirrorDetect*().
+ *      In the normal situation the confidence confidence will be
+ *      large and positive.  However, if mirror flipped, the
+ *      confidence will be large and negative.
+ *
+ *  A high-level interface, pixOrientCorrect() combines the detection
+ *  of the orientation with the rotation decision and the rotation itself.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Sels for pixOrientDetect() and pixMirrorDetect() */ +static const char *textsel1 = "x oo " + "x oOo " + "x o " + "x " + "xxxxxx"; + +static const char *textsel2 = " oo x" + " oOo x" + " o x" + " x" + "xxxxxx"; + +static const char *textsel3 = "xxxxxx" + "x " + "x o " + "x oOo " + "x oo "; + +static const char *textsel4 = "xxxxxx" + " x" + " o x" + " oOo x" + " oo x"; + + /* Parameters for determining orientation */ +static const l_int32 DefaultMinUpDownCount = 70; +static const l_float32 DefaultMinUpDownConf = 8.0; +static const l_float32 DefaultMinUpDownRatio = 2.5; + + /* Parameters for determining mirror flip */ +static const l_int32 DefaultMinMirrorFlipCount = 100; +static const l_float32 DefaultMinMirrorFlipConf = 5.0; + + /* Static debug function */ +static void pixDebugFlipDetect(const char *filename, PIX *pixs, + PIX *pixhm, l_int32 enable); + + +/*----------------------------------------------------------------* + * High-level interface for detection and correction * + *----------------------------------------------------------------*/ +/*! + * \brief pixOrientCorrect() + * + * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi + * \param[in] minupconf minimum value for which a decision can be made + * \param[in] minratio minimum conf ratio required for a decision + * \param[out] pupconf [optional] ; use NULL to skip + * \param[out] pleftconf [optional] ; use NULL to skip + * \param[out] protation [optional] ; use NULL to skip + * \param[in] debug 1 for debug output; 0 otherwise + * \return pixd may be rotated by 90, 180 or 270; null on error + * + *
+ * Notes:
+ *      (1) Simple top-level function to detect if Roman text is in
+ *          reading orientation, and to rotate the image accordingly if not.
+ *      (2) Returns a copy if no rotation is needed.
+ *      (3) See notes for pixOrientDetect() and pixOrientDecision().
+ *          Use 0.0 for default values for %minupconf and %minratio
+ *      (4) Optional output of intermediate confidence results and
+ *          the rotation performed on pixs.
+ * 
+ */ +PIX * +pixOrientCorrect(PIX *pixs, + l_float32 minupconf, + l_float32 minratio, + l_float32 *pupconf, + l_float32 *pleftconf, + l_int32 *protation, + l_int32 debug) +{ +l_int32 orient; +l_float32 upconf, leftconf; +PIX *pix1; + + PROCNAME("pixOrientCorrect"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + /* Get confidences for orientation */ + pixUpDownDetectDwa(pixs, &upconf, 0, debug); + pix1 = pixRotate90(pixs, 1); + pixUpDownDetectDwa(pix1, &leftconf, 0, debug); + pixDestroy(&pix1); + if (pupconf) *pupconf = upconf; + if (pleftconf) *pleftconf = leftconf; + + /* Decide what to do */ + makeOrientDecision(upconf,leftconf, minupconf, minratio, &orient, debug); + + /* Do it */ + switch (orient) + { + case L_TEXT_ORIENT_UNKNOWN: + L_INFO("text orientation not determined; no rotation\n", procName); + if (protation) *protation = 0; + return pixCopy(NULL, pixs); + break; + case L_TEXT_ORIENT_UP: + L_INFO("text is oriented up; no rotation\n", procName); + if (protation) *protation = 0; + return pixCopy(NULL, pixs); + break; + case L_TEXT_ORIENT_LEFT: + L_INFO("landscape; text oriented left; 90 cw rotation\n", procName); + if (protation) *protation = 90; + return pixRotateOrth(pixs, 1); + break; + case L_TEXT_ORIENT_DOWN: + L_INFO("text oriented down; 180 cw rotation\n", procName); + if (protation) *protation = 180; + return pixRotateOrth(pixs, 2); + break; + case L_TEXT_ORIENT_RIGHT: + L_INFO("landscape; text oriented right; 270 cw rotation\n", procName); + if (protation) *protation = 270; + return pixRotateOrth(pixs, 3); + break; + default: + L_ERROR("invalid orient flag!\n", procName); + return pixCopy(NULL, pixs); + } +} + + +/*----------------------------------------------------------------* + * Orientation detection (four 90 degree angles) * + * Rasterop implementation * + *----------------------------------------------------------------*/ +/*! + * \brief pixOrientDetect() + * + * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi + * \param[out] pupconf [optional] ; may be NULL + * \param[out] pleftconf [optional] ; may be NULL + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See "Measuring document image skew and orientation"
+ *          Dan S. Bloomberg, Gary E. Kopec and Lakshmi Dasari
+ *          IS&T/SPIE EI'95, Conference 2422: Document Recognition II
+ *          pp 302-316, Feb 6-7, 1995, San Jose, CA
+ *      (2) upconf is the normalized difference between up ascenders
+ *          and down ascenders.  The image is analyzed without rotation
+ *          for being rightside-up or upside-down.  Set &upconf to null
+ *          to skip this operation.
+ *      (3) leftconf is the normalized difference between up ascenders
+ *          and down ascenders in the image after it has been
+ *          rotated 90 degrees clockwise.  With that rotation, ascenders
+ *          projecting to the left in the source image will project up
+ *          in the rotated image.  We compute this by rotating 90 degrees
+ *          clockwise and testing for up and down ascenders.  Set
+ *          &leftconf to null to skip this operation.
+ *      (4) Note that upconf and leftconf are not linear measures of
+ *          confidence, e.g., in a range between 0 and 100.  They
+ *          measure how far you are out on the tail of a (presumably)
+ *          normal distribution.  For example, a confidence of 10 means
+ *          that it is nearly certain that the difference did not
+ *          happen at random.  However, these values must be interpreted
+ *          cautiously, taking into consideration the estimated prior
+ *          for a particular orientation or mirror flip.   The up-down
+ *          signal is very strong if applied to text with ascenders
+ *          up and down, and relatively weak for text at 90 degrees,
+ *          but even at 90 degrees, the difference can look significant.
+ *          For example, suppose the ascenders are oriented horizontally,
+ *          but the test is done vertically.  Then upconf can
+ *          be < -MIN_CONF_FOR_UP_DOWN, suggesting the text may be
+ *          upside-down.  However, if instead the test were done
+ *          horizontally, leftconf will be very much larger
+ *          (in absolute value), giving the correct orientation.
+ *      (5) If you compute both upconf and leftconf, and there is
+ *          sufficient signal, the following table determines the
+ *          cw angle necessary to rotate pixs so that the text is
+ *          rightside-up:
+ *             0 deg :           upconf >> 1,    abs(upconf) >> abs(leftconf)
+ *             90 deg :          leftconf >> 1,  abs(leftconf) >> abs(upconf)
+ *             180 deg :         upconf << -1,   abs(upconf) >> abs(leftconf)
+ *             270 deg :         leftconf << -1, abs(leftconf) >> abs(upconf)
+ *      (6) One should probably not interpret the direction unless
+ *          there are a sufficient number of counts for both orientations,
+ *          in which case neither upconf nor leftconf will be 0.0.
+ *      (7) This algorithm will fail on some images, such as tables,
+ *          where most of the characters are numbers and appear as
+ *          uppercase, but there are some repeated words that give a
+ *          biased signal.  It may be advisable to run a table detector
+ *          first (e.g., pixDecideIfTable()), and not run the orientation
+ *          detector if it is a table.
+ *      (8) Uses rasterop implementation of HMT.
+ * 
+ */ +l_ok +pixOrientDetect(PIX *pixs, + l_float32 *pupconf, + l_float32 *pleftconf, + l_int32 mincount, + l_int32 debug) +{ +PIX *pix1; + + PROCNAME("pixOrientDetect"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!pupconf && !pleftconf) + return ERROR_INT("nothing to do", procName, 1); + if (mincount == 0) + mincount = DefaultMinUpDownCount; + + if (pupconf) + pixUpDownDetect(pixs, pupconf, mincount, debug); + if (pleftconf) { + pix1 = pixRotate90(pixs, 1); + pixUpDownDetect(pix1, pleftconf, mincount, debug); + pixDestroy(&pix1); + } + + return 0; +} + + +/*! + * \brief makeOrientDecision() + * + * \param[in] upconf nonzero + * \param[in] leftconf nonzero + * \param[in] minupconf minimum value for which a decision can be made + * \param[in] minratio minimum conf ratio required for a decision + * \param[out] porient text orientation enum {0,1,2,3,4} + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This can be run after pixOrientDetect()
+ *      (2) Both upconf and leftconf must be nonzero; otherwise the
+ *          orientation cannot be determined.
+ *      (3) The abs values of the input confidences are compared to
+ *          minupconf.
+ *      (4) The abs value of the largest of (upconf/leftconf) and
+ *          (leftconf/upconf) is compared with minratio.
+ *      (5) Input 0.0 for the default values for minupconf and minratio.
+ *      (6) The return value of orient is interpreted thus:
+ *            L_TEXT_ORIENT_UNKNOWN:  not enough evidence to determine
+ *            L_TEXT_ORIENT_UP:       text rightside-up
+ *            L_TEXT_ORIENT_LEFT:     landscape, text up facing left
+ *            L_TEXT_ORIENT_DOWN:     text upside-down
+ *            L_TEXT_ORIENT_RIGHT:    landscape, text up facing right
+ * 
+ */ +l_ok +makeOrientDecision(l_float32 upconf, + l_float32 leftconf, + l_float32 minupconf, + l_float32 minratio, + l_int32 *porient, + l_int32 debug) +{ +l_float32 absupconf, absleftconf; + + PROCNAME("makeOrientDecision"); + + if (!porient) + return ERROR_INT("&orient not defined", procName, 1); + *porient = L_TEXT_ORIENT_UNKNOWN; /* default: no decision */ + if (upconf == 0.0 || leftconf == 0.0) { + L_INFO("not enough confidence to get orientation\n", procName); + return 0; + } + + if (minupconf == 0.0) + minupconf = DefaultMinUpDownConf; + if (minratio == 0.0) + minratio = DefaultMinUpDownRatio; + absupconf = L_ABS(upconf); + absleftconf = L_ABS(leftconf); + + /* Here are the four possible orientation decisions, based + * on satisfaction of two threshold constraints. */ + if (upconf > minupconf && absupconf > minratio * absleftconf) + *porient = L_TEXT_ORIENT_UP; + else if (leftconf > minupconf && absleftconf > minratio * absupconf) + *porient = L_TEXT_ORIENT_LEFT; + else if (upconf < -minupconf && absupconf > minratio * absleftconf) + *porient = L_TEXT_ORIENT_DOWN; + else if (leftconf < -minupconf && absleftconf > minratio * absupconf) + *porient = L_TEXT_ORIENT_RIGHT; + + if (debug) { + fprintf(stderr, "upconf = %7.3f, leftconf = %7.3f\n", upconf, leftconf); + if (*porient == L_TEXT_ORIENT_UNKNOWN) + fprintf(stderr, "Confidence is low; no determination is made\n"); + else if (*porient == L_TEXT_ORIENT_UP) + fprintf(stderr, "Text is rightside-up\n"); + else if (*porient == L_TEXT_ORIENT_LEFT) + fprintf(stderr, "Text is rotated 90 deg ccw\n"); + else if (*porient == L_TEXT_ORIENT_DOWN) + fprintf(stderr, "Text is upside-down\n"); + else /* *porient == L_TEXT_ORIENT_RIGHT */ + fprintf(stderr, "Text is rotated 90 deg cw\n"); + } + + return 0; +} + + +/*! + * \brief pixUpDownDetect() + * + * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi + * \param[out] pconf confidence that text is rightside-up + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Special (typical, slightly faster) case, where the pixels
+ *          identified through the HMT (hit-miss transform) are not
+ *          clipped by a truncated word mask pixm.  See pixOrientDetect()
+ *          and pixUpDownDetectGeneral() for details.
+ *      (2) The returned confidence is the normalized difference
+ *          between the number of detected up and down ascenders,
+ *          assuming that the text is either rightside-up or upside-down
+ *          and not rotated at a 90 degree angle.
+ * 
+ */ +l_ok +pixUpDownDetect(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 debug) +{ + return pixUpDownDetectGeneral(pixs, pconf, mincount, 0, debug); +} + + +/*! + * \brief pixUpDownDetectGeneral() + * + * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi + * \param[out] pconf confidence that text is rightside-up + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] npixels number of pixels removed from each side of word box + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixOrientDetect() for other details.
+ *      (2) %conf is the normalized difference between the number of
+ *          detected up and down ascenders, assuming that the text
+ *          is either rightside-up or upside-down and not rotated
+ *          at a 90 degree angle.
+ *      (3) The typical mode of operation is %npixels == 0.
+ *          If %npixels > 0, this removes HMT matches at the
+ *          beginning and ending of "words."  This is useful for
+ *          pages that may have mostly digits, because if npixels == 0,
+ *          leading "1" and "3" digits can register as having
+ *          ascenders or descenders, and "7" digits can match descenders.
+ *          Consequently, a page image of only digits may register
+ *          as being upside-down.
+ *      (4) We want to count the number of instances found using the HMT.
+ *          An expensive way to do this would be to count the
+ *          number of connected components.  A cheap way is to do a rank
+ *          reduction cascade that reduces each component to a single
+ *          pixel, and results (after two or three 2x reductions)
+ *          in one pixel for each of the original components.
+ *          After the reduction, you have a much smaller pix over
+ *          which to count pixels.  We do only 2 reductions, because
+ *          this function is designed to work for input pix between
+ *          150 and 300 ppi, and an 8x reduction on a 150 ppi image
+ *          is going too far -- components will get merged.
+ * 
+ */ +l_ok +pixUpDownDetectGeneral(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 npixels, + l_int32 debug) +{ +l_int32 countup, countdown, nmax; +l_float32 nup, ndown; +PIX *pix0, *pix1, *pix2, *pix3, *pixm; +SEL *sel1, *sel2, *sel3, *sel4; + + PROCNAME("pixUpDownDetectGeneral"); + + if (!pconf) + return ERROR_INT("&conf not defined", procName, 1); + *pconf = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (mincount == 0) + mincount = DefaultMinUpDownCount; + if (npixels < 0) + npixels = 0; + + if (debug) { + lept_mkdir("lept/orient"); + } + + sel1 = selCreateFromString(textsel1, 5, 6, NULL); + sel2 = selCreateFromString(textsel2, 5, 6, NULL); + sel3 = selCreateFromString(textsel3, 5, 6, NULL); + sel4 = selCreateFromString(textsel4, 5, 6, NULL); + + /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1). + * This closes holes in x-height characters and joins them at + * the x-height. There is more noise in the descender detection + * from this, but it works fairly well. */ + pix0 = pixMorphCompSequence(pixs, "c1.8 + c30.1", 0); + + /* Optionally, make a mask of the word bounding boxes, shortening + * each of them by a fixed amount at each end. */ + pixm = NULL; + if (npixels > 0) { + l_int32 i, nbox, x, y, w, h; + BOX *box; + BOXA *boxa; + pix1 = pixMorphSequence(pix0, "o10.1", 0); + boxa = pixConnComp(pix1, NULL, 8); + pixm = pixCreateTemplate(pix1); + pixDestroy(&pix1); + nbox = boxaGetCount(boxa); + for (i = 0; i < nbox; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + if (w > 2 * npixels) + pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13, + PIX_SET, NULL, 0, 0); + boxDestroy(&box); + } + boxaDestroy(&boxa); + } + + /* Find the ascenders and optionally filter with pixm. + * For an explanation of the procedure used for counting the result + * of the HMT, see comments at the beginning of this function. */ + pix1 = pixHMT(NULL, pix0, sel1); + pix2 = pixHMT(NULL, pix0, sel2); + pixOr(pix1, pix1, pix2); + if (pixm) + pixAnd(pix1, pix1, pixm); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &countup, NULL); + pixDebugFlipDetect("/tmp/lept/orient/up.png", pixs, pix1, debug); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + /* Find the ascenders and optionally filter with pixm. */ + pix1 = pixHMT(NULL, pix0, sel3); + pix2 = pixHMT(NULL, pix0, sel4); + pixOr(pix1, pix1, pix2); + if (pixm) + pixAnd(pix1, pix1, pixm); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &countdown, NULL); + pixDebugFlipDetect("/tmp/lept/orient/down.png", pixs, pix1, debug); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + /* Evaluate statistically, generating a confidence that is + * related to the probability with a gaussian distribution. */ + nup = (l_float32)(countup); + ndown = (l_float32)(countdown); + nmax = L_MAX(countup, countdown); + if (nmax > mincount) + *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown)); + + if (debug) { + if (pixm) pixWriteDebug("/tmp/lept/orient/pixm1.png", pixm, IFF_PNG); + fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n", + nup, ndown, *pconf); + if (*pconf > DefaultMinUpDownConf) + fprintf(stderr, "Text is rightside-up\n"); + if (*pconf < -DefaultMinUpDownConf) + fprintf(stderr, "Text is upside-down\n"); + } + + pixDestroy(&pix0); + pixDestroy(&pixm); + selDestroy(&sel1); + selDestroy(&sel2); + selDestroy(&sel3); + selDestroy(&sel4); + return 0; +} + + +/*----------------------------------------------------------------* + * Orientation detection (four 90 degree angles) * + * DWA implementation * + *----------------------------------------------------------------*/ +/*! + * \brief pixOrientDetectDwa() + * + * \param[in] pixs 1 bpp, deskewed, English text + * \param[out] pupconf [optional] ; may be NULL + * \param[out] pleftconf [optional] ; may be NULL + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Same interface as for pixOrientDetect().  See notes
+ *          there for usage.
+ *      (2) Uses auto-gen'd code for the Sels defined at the
+ *          top of this file, with some renaming of functions.
+ *          The auto-gen'd code is in fliphmtgen.c, and can
+ *          be generated by a simple executable; see prog/flipselgen.c.
+ *      (3) This runs about 2.5 times faster than the pixOrientDetect().
+ * 
+ */ +l_ok +pixOrientDetectDwa(PIX *pixs, + l_float32 *pupconf, + l_float32 *pleftconf, + l_int32 mincount, + l_int32 debug) +{ +PIX *pix1; + + PROCNAME("pixOrientDetectDwa"); + + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!pupconf && !pleftconf) + return ERROR_INT("nothing to do", procName, 1); + if (mincount == 0) + mincount = DefaultMinUpDownCount; + + if (pupconf) + pixUpDownDetectDwa(pixs, pupconf, mincount, debug); + if (pleftconf) { + pix1 = pixRotate90(pixs, 1); + pixUpDownDetectDwa(pix1, pleftconf, mincount, debug); + pixDestroy(&pix1); + } + + return 0; +} + + +/*! + * \brief pixUpDownDetectDwa() + * + * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi + * \param[out] pconf confidence that text is rightside-up + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Faster (DWA) version of pixUpDownDetect().
+ *      (2) This is a special case (but typical and slightly faster) of
+ *          pixUpDownDetectGeneralDwa(), where the pixels identified
+ *          through the HMT (hit-miss transform) are not clipped by
+ *          a truncated word mask pixm.  See pixUpDownDetectGeneral()
+ *          for usage and other details.
+ *      (3) The returned confidence is the normalized difference
+ *          between the number of detected up and down ascenders,
+ *          assuming that the text is either rightside-up or upside-down
+ *          and not rotated at a 90 degree angle.
+ * 
+ */ +l_ok +pixUpDownDetectDwa(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 debug) +{ + return pixUpDownDetectGeneralDwa(pixs, pconf, mincount, 0, debug); +} + + +/*! + * \brief pixUpDownDetectGeneralDwa() + * + * \param[in] pixs 1 bpp, deskewed, English text + * \param[out] pconf confidence that text is rightside-up + * \param[in] mincount min number of up + down; use 0 for default + * \param[in] npixels number of pixels removed from each side of word box + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See the notes in pixUpDownDetectGeneral() for usage.
+ * 
+ */ +l_ok +pixUpDownDetectGeneralDwa(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 npixels, + l_int32 debug) +{ +char flipsel1[] = "flipsel1"; +char flipsel2[] = "flipsel2"; +char flipsel3[] = "flipsel3"; +char flipsel4[] = "flipsel4"; +l_int32 countup, countdown, nmax; +l_float32 nup, ndown; +PIX *pixt, *pix0, *pix1, *pix2, *pix3, *pixm; + + PROCNAME("pixUpDownDetectGeneralDwa"); + + if (!pconf) + return ERROR_INT("&conf not defined", procName, 1); + *pconf = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (mincount == 0) + mincount = DefaultMinUpDownCount; + if (npixels < 0) + npixels = 0; + + /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1). + * This closes holes in x-height characters and joins them at + * the x-height. There is more noise in the descender detection + * from this, but it works fairly well. */ + pixt = pixMorphSequenceDwa(pixs, "c1.8 + c30.1", 0); + + /* Be sure to add the border before the flip DWA operations! */ + pix0 = pixAddBorderGeneral(pixt, ADDED_BORDER, ADDED_BORDER, + ADDED_BORDER, ADDED_BORDER, 0); + pixDestroy(&pixt); + + /* Optionally, make a mask of the word bounding boxes, shortening + * each of them by a fixed amount at each end. */ + pixm = NULL; + if (npixels > 0) { + l_int32 i, nbox, x, y, w, h; + BOX *box; + BOXA *boxa; + pix1 = pixMorphSequenceDwa(pix0, "o10.1", 0); + boxa = pixConnComp(pix1, NULL, 8); + pixm = pixCreateTemplate(pix1); + pixDestroy(&pix1); + nbox = boxaGetCount(boxa); + for (i = 0; i < nbox; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + if (w > 2 * npixels) + pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13, + PIX_SET, NULL, 0, 0); + boxDestroy(&box); + } + boxaDestroy(&boxa); + } + + /* Find the ascenders and optionally filter with pixm. + * For an explanation of the procedure used for counting the result + * of the HMT, see comments in pixUpDownDetectGeneral(). */ + pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1); + pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2); + pixOr(pix1, pix1, pix2); + if (pixm) + pixAnd(pix1, pix1, pixm); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &countup, NULL); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + /* Find the ascenders and optionally filter with pixm. */ + pix1 = pixFlipFHMTGen(NULL, pix0, flipsel3); + pix2 = pixFlipFHMTGen(NULL, pix0, flipsel4); + pixOr(pix1, pix1, pix2); + if (pixm) + pixAnd(pix1, pix1, pixm); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &countdown, NULL); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + + /* Evaluate statistically, generating a confidence that is + * related to the probability with a gaussian distribution. */ + nup = (l_float32)(countup); + ndown = (l_float32)(countdown); + nmax = L_MAX(countup, countdown); + if (nmax > mincount) + *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown)); + + if (debug) { + if (pixm) { + lept_mkdir("lept/orient"); + pixWriteDebug("/tmp/lept/orient/pixm2.png", pixm, IFF_PNG); + } + fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n", + nup, ndown, *pconf); + if (*pconf > DefaultMinUpDownConf) + fprintf(stderr, "Text is rightside-up\n"); + if (*pconf < -DefaultMinUpDownConf) + fprintf(stderr, "Text is upside-down\n"); + } + + pixDestroy(&pix0); + pixDestroy(&pixm); + return 0; +} + + + +/*----------------------------------------------------------------* + * Left-right mirror detection * + * Rasterop implementation * + *----------------------------------------------------------------*/ +/*! + * \brief pixMirrorDetect() + * + * \param[in] pixs 1 bpp, deskewed, English text + * \param[out] pconf confidence that text is not LR mirror reversed + * \param[in] mincount min number of left + right; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For this test, it is necessary that the text is horizontally
+ *          oriented, with ascenders going up.
+ *      (2) conf is the normalized difference between the number of
+ *          right and left facing characters with ascenders.
+ *          Left-facing are {d}; right-facing are {b, h, k}.
+ *          At least that was the expectation.  In practice, we can
+ *          really just say that it is the normalized difference in
+ *          hits using two specific hit-miss filters, textsel1 and textsel2,
+ *          after the image has been suitably pre-filtered so that
+ *          these filters are effective.  See (4) for what's really happening.
+ *      (3) A large positive conf value indicates normal text, whereas
+ *          a large negative conf value means the page is mirror reversed.
+ *      (4) The implementation is a bit tricky.  The general idea is
+ *          to fill the x-height part of characters, but not the space
+ *          between them, before doing the HMT.  This is done by
+ *          finding pixels added using two different operations -- a
+ *          horizontal close and a vertical dilation -- and adding
+ *          the intersection of these sets to the original.  It turns
+ *          out that the original intuition about the signal was largely
+ *          in error: much of the signal for right-facing characters
+ *          comes from the lower part of common x-height characters, like
+ *          the e and c, that remain open after these operations.
+ *          So it's important that the operations to close the x-height
+ *          parts of the characters are purposely weakened sufficiently
+ *          to allow these characters to remain open.  The wonders
+ *          of morphology!
+ * 
+ */ +l_ok +pixMirrorDetect(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 debug) +{ +l_int32 count1, count2, nmax; +l_float32 nleft, nright; +PIX *pix0, *pix1, *pix2, *pix3; +SEL *sel1, *sel2; + + PROCNAME("pixMirrorDetect"); + + if (!pconf) + return ERROR_INT("&conf not defined", procName, 1); + *pconf = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (mincount == 0) + mincount = DefaultMinMirrorFlipCount; + + if (debug) { + lept_mkdir("lept/orient"); + } + + sel1 = selCreateFromString(textsel1, 5, 6, NULL); + sel2 = selCreateFromString(textsel2, 5, 6, NULL); + + /* Fill x-height characters but not space between them, sort of. */ + pix3 = pixMorphCompSequence(pixs, "d1.30", 0); + pixXor(pix3, pix3, pixs); + pix0 = pixMorphCompSequence(pixs, "c15.1", 0); + pixXor(pix0, pix0, pixs); + pixAnd(pix0, pix0, pix3); + pixOr(pix0, pix0, pixs); + pixDestroy(&pix3); + + /* Filter the right-facing characters. */ + pix1 = pixHMT(NULL, pix0, sel1); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &count1, NULL); + pixDebugFlipDetect("/tmp/lept/orient/right.png", pixs, pix1, debug); + pixDestroy(&pix1); + pixDestroy(&pix3); + + /* Filter the left-facing characters. */ + pix2 = pixHMT(NULL, pix0, sel2); + pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0); + pixCountPixels(pix3, &count2, NULL); + pixDebugFlipDetect("/tmp/lept/orient/left.png", pixs, pix2, debug); + pixDestroy(&pix2); + pixDestroy(&pix3); + + nright = (l_float32)count1; + nleft = (l_float32)count2; + nmax = L_MAX(count1, count2); + pixDestroy(&pix0); + selDestroy(&sel1); + selDestroy(&sel2); + + if (nmax > mincount) + *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft)); + + if (debug) { + fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft); + if (*pconf > DefaultMinMirrorFlipConf) + fprintf(stderr, "Text is not mirror reversed\n"); + if (*pconf < -DefaultMinMirrorFlipConf) + fprintf(stderr, "Text is mirror reversed\n"); + } + + return 0; +} + + +/*----------------------------------------------------------------* + * Left-right mirror detection * + * DWA implementation * + *----------------------------------------------------------------*/ +/*! + * \brief pixMirrorDetectDwa() + * + * \param[in] pixs 1 bpp, deskewed, English text + * \param[out] pconf confidence that text is not LR mirror reversed + * \param[in] mincount min number of left + right; use 0 for default + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We assume the text is horizontally oriented, with
+ *          ascenders going up.
+ *      (2) See notes in pixMirrorDetect().
+ * 
+ */ +l_ok +pixMirrorDetectDwa(PIX *pixs, + l_float32 *pconf, + l_int32 mincount, + l_int32 debug) +{ +char flipsel1[] = "flipsel1"; +char flipsel2[] = "flipsel2"; +l_int32 count1, count2, nmax; +l_float32 nleft, nright; +PIX *pix0, *pix1, *pix2, *pix3; + + PROCNAME("pixMirrorDetectDwa"); + + if (!pconf) + return ERROR_INT("&conf not defined", procName, 1); + *pconf = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (mincount == 0) + mincount = DefaultMinMirrorFlipCount; + + /* Fill x-height characters but not space between them, sort of. */ + pix3 = pixMorphSequenceDwa(pixs, "d1.30", 0); + pixXor(pix3, pix3, pixs); + pix0 = pixMorphSequenceDwa(pixs, "c15.1", 0); + pixXor(pix0, pix0, pixs); + pixAnd(pix0, pix0, pix3); + pixOr(pix3, pix0, pixs); + pixDestroy(&pix0); + pix0 = pixAddBorderGeneral(pix3, ADDED_BORDER, ADDED_BORDER, + ADDED_BORDER, ADDED_BORDER, 0); + pixDestroy(&pix3); + + /* Filter the right-facing characters. */ + pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1); + pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + pixCountPixels(pix3, &count1, NULL); + pixDestroy(&pix1); + pixDestroy(&pix3); + + /* Filter the left-facing characters. */ + pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2); + pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0); + pixCountPixels(pix3, &count2, NULL); + pixDestroy(&pix2); + pixDestroy(&pix3); + + pixDestroy(&pix0); + nright = (l_float32)count1; + nleft = (l_float32)count2; + nmax = L_MAX(count1, count2); + + if (nmax > mincount) + *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft)); + + if (debug) { + fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft); + if (*pconf > DefaultMinMirrorFlipConf) + fprintf(stderr, "Text is not mirror reversed\n"); + if (*pconf < -DefaultMinMirrorFlipConf) + fprintf(stderr, "Text is mirror reversed\n"); + } + + return 0; +} + + +/*----------------------------------------------------------------* + * Static debug helper * + *----------------------------------------------------------------*/ +/* + * \brief pixDebugFlipDetect() + * + * \param[in] filename for output debug file + * \param[in] pixs input to pix*Detect + * \param[in] pixhm hit-miss result from ascenders or descenders + * \param[in] enable 1 to enable this function; 0 to disable + * \return void + */ +static void +pixDebugFlipDetect(const char *filename, + PIX *pixs, + PIX *pixhm, + l_int32 enable) +{ +PIX *pixt, *pixthm; + + if (!enable) return; + + /* Display with red dot at counted locations */ + pixt = pixConvert1To4Cmap(pixs); + pixthm = pixMorphSequence(pixhm, "d5.5", 0); + pixSetMaskedCmap(pixt, pixthm, 0, 0, 255, 0, 0); + + pixWriteDebug(filename, pixt, IFF_PNG); + pixDestroy(&pixthm); + pixDestroy(&pixt); + return; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fliphmtgen.c b/hgdriver/3rdparty/hgOCR/leptonica/fliphmtgen.c new file mode 100644 index 0000000..25e1219 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fliphmtgen.c @@ -0,0 +1,352 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * fliphmtgen.c + * + * DWA implementation of hit-miss transforms with auto-generated sels + * for pixOrientDetectDwa() and pixUpDownDetectDwa() in flipdetect.c + * + * PIX *pixFlipFHMTGen() + * static l_int32 flipfhmtgen_low() -- dispatcher + * static void fhmt_1_0() + * static void fhmt_1_1() + * static void fhmt_1_2() + * static void fhmt_1_3() + * + * The code (rearranged) was generated by prog/flipselgen.c + */ + +#include +#include "allheaders.h" + +static l_int32 NUM_SELS_GENERATED = 4; +static char SEL_NAMES[][10] = {"flipsel1", + "flipsel2", + "flipsel3", + "flipsel4"}; + +static l_int32 flipfhmtgen_low(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32, l_int32); + +static void fhmt_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fhmt_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); + + +/*---------------------------------------------------------------------* + * Top-level hmt functions * + *---------------------------------------------------------------------*/ +/* + * pixFlipFHMTGen() + * + * Input: pixd (usual 3 choices: null, == pixs, != pixs) + * pixs + * sel name (one of four defined in SEL_NAMES[]) + * Return: pixd + * + * Notes: + * Action: hit-miss transform on pixs by the sel + * N.B.: the sel must have at least one hit, and it + * can have any number of misses. + */ +PIX * +pixFlipFHMTGen(PIX *pixd, + PIX *pixs, + const char *selname) +{ +l_int32 i, index, found, w, h, wpls, wpld; +l_uint32 *datad, *datas, *datat; +PIX *pixt; + + PROCNAME("pixFlipFHMTGen"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + found = FALSE; + for (i = 0; i < NUM_SELS_GENERATED; i++) { + if (strcmp(selname, SEL_NAMES[i]) == 0) { + found = TRUE; + index = i; + break; + } + } + if (found == FALSE) + return (PIX *)ERROR_PTR("sel index not found", procName, pixd); + + if (pixd) { + if (!pixSizesEqual(pixs, pixd)) + return (PIX *)ERROR_PTR("sizes not equal", procName, pixd); + } else { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* The images must be surrounded with ADDED_BORDER white pixels, + * that we'll read from. We fabricate a "proper" + * image as the subimage within the border, having the + * following parameters: */ + w = pixGetWidth(pixs) - 2 * ADDED_BORDER; + h = pixGetHeight(pixs) - 2 * ADDED_BORDER; + datas = pixGetData(pixs) + ADDED_BORDER * wpls + ADDED_BORDER / 32; + datad = pixGetData(pixd) + ADDED_BORDER * wpld + ADDED_BORDER / 32; + + if (pixd == pixs) { /* need temp image if in-place */ + if ((pixt = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + ADDED_BORDER * wpls + ADDED_BORDER / 32; + flipfhmtgen_low(datad, w, h, wpld, datat, wpls, index); + pixDestroy(&pixt); + } else { /* simple and not in-place */ + flipfhmtgen_low(datad, w, h, wpld, datas, wpls, index); + } + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Fast hmt dispatcher * + *---------------------------------------------------------------------*/ +/* + * flipfhmtgen_low() + * + * A dispatcher to appropriate low-level code for flip hmt ops + */ +static l_int32 +flipfhmtgen_low(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 index) +{ + + switch (index) + { + case 0: + fhmt_1_0(datad, w, h, wpld, datas, wpls); + break; + case 1: + fhmt_1_1(datad, w, h, wpld, datas, wpls); + break; + case 2: + fhmt_1_2(datad, w, h, wpld, datas, wpls); + break; + case 3: + fhmt_1_3(datad, w, h, wpld, datas, wpls); + break; + } + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Low-level auto-generated hmt routines * + *--------------------------------------------------------------------------*/ +/* + * N.B. in all the low-level routines, the part of the image + * that is accessed has been clipped by ADDED_BORDER pixels + * on all four sides. This is done in the higher level + * code by redefining w and h smaller and by moving the + * start-of-image pointers up to the beginning of this + * interior rectangle. + */ + +static void +fhmt_1_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) & + (~*(sptr - wpls)) & + ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (~*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) & + (~*(sptr + wpls)) & + ((*(sptr + wpls2) >> 3) | (*(sptr + wpls2 - 1) << 29)) & + ((*(sptr + wpls3) >> 3) | (*(sptr + wpls3 - 1) << 29)) & + ((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) & + ((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) & + (*(sptr + wpls3)) & + ((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) & + ((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30)); + } + } +} + + +static void +fhmt_1_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & + (~*(sptr - wpls)) & + ((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (~*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + (~*(sptr + wpls)) & + ((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29)) & + ((*(sptr + wpls2) << 3) | (*(sptr + wpls2 + 1) >> 29)) & + ((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) & + ((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) & + (*(sptr + wpls3)) & + ((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) & + ((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30)) & + ((*(sptr + wpls3) << 3) | (*(sptr + wpls3 + 1) >> 29)); + } + } +} + + +static void +fhmt_1_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls3) >> 3) | (*(sptr - wpls3 - 1) << 29)) & + ((*(sptr - wpls3) >> 2) | (*(sptr - wpls3 - 1) << 30)) & + ((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) & + (*(sptr - wpls3)) & + ((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) & + ((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) & + ((*(sptr - wpls2) >> 3) | (*(sptr - wpls2 - 1) << 29)) & + ((*(sptr - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) & + (~*(sptr - wpls)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (~*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) & + (~*(sptr + wpls)) & + ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); + } + } +} + + +static void +fhmt_1_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls3) >> 2) | (*(sptr - wpls3 - 1) << 30)) & + ((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) & + (*(sptr - wpls3)) & + ((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) & + ((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) & + ((*(sptr - wpls3) << 3) | (*(sptr - wpls3 + 1) >> 29)) & + ((*(sptr - wpls2) << 3) | (*(sptr - wpls2 + 1) >> 29)) & + (~*(sptr - wpls)) & + ((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) & + ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & + (~*sptr) & + ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & + (~*(sptr + wpls)) & + ((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29)); + } + } +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fmorphauto.c b/hgdriver/3rdparty/hgOCR/leptonica/fmorphauto.c new file mode 100644 index 0000000..2059279 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fmorphauto.c @@ -0,0 +1,874 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file fmorphauto.c + *
+ *
+ *    Main function calls:
+ *       l_int32             fmorphautogen()
+ *       l_int32             fmorphautogen1()
+ *       l_int32             fmorphautogen2()
+ *
+ *    Static helpers:
+ *       static SARRAY      *sarrayMakeWplsCode()
+ *       static SARRAY      *sarrayMakeInnerLoopDWACode()
+ *       static char        *makeBarrelshiftString()
+ *
+ *
+ *    This automatically generates dwa code for erosion and dilation.
+ *    Here's a road map for how it all works.
+ *
+ *    (1) You generate an array (a SELA) of structuring elements (SELs).
+ *        This can be done in several ways, including
+ *           (a) calling the function selaAddBasic() for
+ *               pre-compiled SELs
+ *           (b) generating the SELA in code in line
+ *           (c) reading in a SELA from file, using selaRead() or
+ *               various other formats.
+ *
+ *    (2) You call fmorphautogen1() and fmorphautogen2() on this SELA.
+ *        These use the text files morphtemplate1.txt and
+ *        morphtemplate2.txt for building up the source code.  See the file
+ *        prog/fmorphautogen.c for an example of how this is done.
+ *        The output is written to files named fmorphgen.*.c
+ *        and fmorphgenlow.*.c, where "*" is an integer that you
+ *        input to this function.  That integer labels both
+ *        the output files, as well as all the functions that
+ *        are generated.  That way, using different integers,
+ *        you can invoke fmorphautogen() any number of times
+ *        to get functions that all have different names so that
+ *        they can be linked into one program.
+ *
+ *    (3) You copy the generated source files back to your src
+ *        directory for compilation.  Put their names in the
+ *        Makefile, regenerate the prototypes, and recompile
+ *        the library.  Look at the Makefile to see how I've
+ *        included morphgen.1.c and fmorphgenlow.1.c.  These files
+ *        provide the high-level interfaces for erosion, dilation,
+ *        opening and closing, and the low-level interfaces to
+ *        do the actual work, for all 58 SELs in the SEL array.
+ *
+ *    (4) In an application, you now use this interface.  Again
+ *        for the example files in the library, using integer "1":
+ *
+ *            PIX   *pixMorphDwa_1(PIX *pixd, PIX, *pixs,
+ *                                 l_int32 operation, char *selname);
+ *
+ *                 or
+ *
+ *            PIX   *pixFMorphopGen_1(PIX *pixd, PIX *pixs,
+ *                                    l_int32 operation, char *selname);
+ *
+ *        where the operation is one of {L_MORPH_DILATE, L_MORPH_ERODE.
+ *        L_MORPH_OPEN, L_MORPH_CLOSE}, and the selname is one
+ *        of the set that were defined as the name field of sels.
+ *        This set is listed at the beginning of the file fmorphgen.1.c.
+ *        For examples of use, see the file prog/binmorph_reg1.c, which
+ *        verifies the consistency of the various implementations by
+ *        comparing the dwa result with that of full-image rasterops.
+ * 
+ */ + +#include +#include "allheaders.h" + +#define OUTROOT "fmorphgen" +#define TEMPLATE1 "morphtemplate1.txt" +#define TEMPLATE2 "morphtemplate2.txt" + +#define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);" + +//static const l_int32 L_BUF_SIZE = 512; +#define L_BUF_SIZE 512 + +static char * makeBarrelshiftString(l_int32 delx, l_int32 dely); +static SARRAY * sarrayMakeInnerLoopDWACode(SEL *sel, l_int32 index); +static SARRAY * sarrayMakeWplsCode(SEL *sel); + +static char wpldecls[][53] = { + "l_int32 wpls2;", + "l_int32 wpls2, wpls3;", + "l_int32 wpls2, wpls3, wpls4;", + "l_int32 wpls5;", + "l_int32 wpls5, wpls6;", + "l_int32 wpls5, wpls6, wpls7;", + "l_int32 wpls5, wpls6, wpls7, wpls8;", + "l_int32 wpls9;", + "l_int32 wpls9, wpls10;", + "l_int32 wpls9, wpls10, wpls11;", + "l_int32 wpls9, wpls10, wpls11, wpls12;", + "l_int32 wpls13;", + "l_int32 wpls13, wpls14;", + "l_int32 wpls13, wpls14, wpls15;", + "l_int32 wpls13, wpls14, wpls15, wpls16;", + "l_int32 wpls17;", + "l_int32 wpls17, wpls18;", + "l_int32 wpls17, wpls18, wpls19;", + "l_int32 wpls17, wpls18, wpls19, wpls20;", + "l_int32 wpls21;", + "l_int32 wpls21, wpls22;", + "l_int32 wpls21, wpls22, wpls23;", + "l_int32 wpls21, wpls22, wpls23, wpls24;", + "l_int32 wpls25;", + "l_int32 wpls25, wpls26;", + "l_int32 wpls25, wpls26, wpls27;", + "l_int32 wpls25, wpls26, wpls27, wpls28;", + "l_int32 wpls29;", + "l_int32 wpls29, wpls30;", + "l_int32 wpls29, wpls30, wpls31;"}; + +static char wplgendecls[][30] = { + "l_int32 wpls2;", + "l_int32 wpls3;", + "l_int32 wpls4;", + "l_int32 wpls5;", + "l_int32 wpls6;", + "l_int32 wpls7;", + "l_int32 wpls8;", + "l_int32 wpls9;", + "l_int32 wpls10;", + "l_int32 wpls11;", + "l_int32 wpls12;", + "l_int32 wpls13;", + "l_int32 wpls14;", + "l_int32 wpls15;", + "l_int32 wpls16;", + "l_int32 wpls17;", + "l_int32 wpls18;", + "l_int32 wpls19;", + "l_int32 wpls20;", + "l_int32 wpls21;", + "l_int32 wpls22;", + "l_int32 wpls23;", + "l_int32 wpls24;", + "l_int32 wpls25;", + "l_int32 wpls26;", + "l_int32 wpls27;", + "l_int32 wpls28;", + "l_int32 wpls29;", + "l_int32 wpls30;", + "l_int32 wpls31;"}; + +static char wpldefs[][25] = { + " wpls2 = 2 * wpls;", + " wpls3 = 3 * wpls;", + " wpls4 = 4 * wpls;", + " wpls5 = 5 * wpls;", + " wpls6 = 6 * wpls;", + " wpls7 = 7 * wpls;", + " wpls8 = 8 * wpls;", + " wpls9 = 9 * wpls;", + " wpls10 = 10 * wpls;", + " wpls11 = 11 * wpls;", + " wpls12 = 12 * wpls;", + " wpls13 = 13 * wpls;", + " wpls14 = 14 * wpls;", + " wpls15 = 15 * wpls;", + " wpls16 = 16 * wpls;", + " wpls17 = 17 * wpls;", + " wpls18 = 18 * wpls;", + " wpls19 = 19 * wpls;", + " wpls20 = 20 * wpls;", + " wpls21 = 21 * wpls;", + " wpls22 = 22 * wpls;", + " wpls23 = 23 * wpls;", + " wpls24 = 24 * wpls;", + " wpls25 = 25 * wpls;", + " wpls26 = 26 * wpls;", + " wpls27 = 27 * wpls;", + " wpls28 = 28 * wpls;", + " wpls29 = 29 * wpls;", + " wpls30 = 30 * wpls;", + " wpls31 = 31 * wpls;"}; + +static char wplstrp[][10] = {"+ wpls", "+ wpls2", "+ wpls3", "+ wpls4", + "+ wpls5", "+ wpls6", "+ wpls7", "+ wpls8", + "+ wpls9", "+ wpls10", "+ wpls11", "+ wpls12", + "+ wpls13", "+ wpls14", "+ wpls15", "+ wpls16", + "+ wpls17", "+ wpls18", "+ wpls19", "+ wpls20", + "+ wpls21", "+ wpls22", "+ wpls23", "+ wpls24", + "+ wpls25", "+ wpls26", "+ wpls27", "+ wpls28", + "+ wpls29", "+ wpls30", "+ wpls31"}; + +static char wplstrm[][10] = {"- wpls", "- wpls2", "- wpls3", "- wpls4", + "- wpls5", "- wpls6", "- wpls7", "- wpls8", + "- wpls9", "- wpls10", "- wpls11", "- wpls12", + "- wpls13", "- wpls14", "- wpls15", "- wpls16", + "- wpls17", "- wpls18", "- wpls19", "- wpls20", + "- wpls21", "- wpls22", "- wpls23", "- wpls24", + "- wpls25", "- wpls26", "- wpls27", "- wpls28", + "- wpls29", "- wpls30", "- wpls31"}; + + +/*! + * \brief fmorphautogen() + * + * \param[in] sela + * \param[in] fileindex + * \param[in] filename [optional]; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This function generates all the code for implementing
+ *          dwa morphological operations using all the sels in the sela.
+ *      (2) See fmorphautogen1() and fmorphautogen2() for details.
+ * 
+ */ +l_ok +fmorphautogen(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +l_int32 ret1, ret2; + + PROCNAME("fmorphautogen"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + ret1 = fmorphautogen1(sela, fileindex, filename); + ret2 = fmorphautogen2(sela, fileindex, filename); + if (ret1 || ret2) + return ERROR_INT("code generation problem", procName, 1); + return 0; +} + + +/*! + * \brief fmorphautogen1() + * + * \param[in] sela + * \param[in] fileindex + * \param[in] filename [optional]; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This function uses morphtemplate1.txt to create a
+ *          top-level file that contains two functions.  These
+ *          functions will carry out dilation, erosion,
+ *          opening or closing for any of the sels in the input sela.
+ *      (2) The fileindex parameter is inserted into the output
+ *          filename, as described below.
+ *      (3) If filename == NULL, the output file is fmorphgen.[n].c,
+ *          where [n] is equal to the %fileindex parameter.
+ *      (4) If filename != NULL, the output file is [%filename].[n].c.
+ * 
+ */ +l_ok +fmorphautogen1(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +char *filestr; +char *str_proto1, *str_proto2, *str_proto3; +char *str_doc1, *str_doc2, *str_doc3, *str_doc4; +char *str_def1, *str_def2, *str_proc1, *str_proc2; +char *str_dwa1, *str_low_dt, *str_low_ds, *str_low_ts; +char *str_low_tsp1, *str_low_dtp1; +char bigbuf[L_BUF_SIZE]; +l_int32 i, nsels, nbytes, actstart, end, newstart; +size_t size; +SARRAY *sa1, *sa2, *sa3; + + PROCNAME("fmorphautogen1"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + if (fileindex < 0) + fileindex = 0; + if ((nsels = selaGetCount(sela)) == 0) + return ERROR_INT("no sels in sela", procName, 1); + + /* Make array of textlines from morphtemplate1.txt */ + if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + sa2 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + if (!sa2) + return ERROR_INT("sa2 not made", procName, 1); + + /* Make array of sel names */ + sa1 = selaGetSelnames(sela); + + /* Make strings containing function call names */ + sprintf(bigbuf, "PIX *pixMorphDwa_%d(PIX *pixd, PIX *pixs, " + "l_int32 operation, char *selname);", fileindex); + str_proto1 = stringNew(bigbuf); + sprintf(bigbuf, "PIX *pixFMorphopGen_%d(PIX *pixd, PIX *pixs, " + "l_int32 operation, char *selname);", fileindex); + str_proto2 = stringNew(bigbuf); + sprintf(bigbuf, "l_int32 fmorphopgen_low_%d(l_uint32 *datad, l_int32 w,\n" + " l_int32 h, l_int32 wpld,\n" + " l_uint32 *datas, l_int32 wpls,\n" + " l_int32 index);", fileindex); + str_proto3 = stringNew(bigbuf); + sprintf(bigbuf, " * PIX *pixMorphDwa_%d()", fileindex); + str_doc1 = stringNew(bigbuf); + sprintf(bigbuf, " * PIX *pixFMorphopGen_%d()", fileindex); + str_doc2 = stringNew(bigbuf); + sprintf(bigbuf, " * \\brief pixMorphDwa_%d()", fileindex); + str_doc3 = stringNew(bigbuf); + sprintf(bigbuf, " * \\brief pixFMorphopGen_%d()", fileindex); + str_doc4 = stringNew(bigbuf); + sprintf(bigbuf, "pixMorphDwa_%d(PIX *pixd,", fileindex); + str_def1 = stringNew(bigbuf); + sprintf(bigbuf, "pixFMorphopGen_%d(PIX *pixd,", fileindex); + str_def2 = stringNew(bigbuf); + sprintf(bigbuf, " PROCNAME(\"pixMorphDwa_%d\");", fileindex); + str_proc1 = stringNew(bigbuf); + sprintf(bigbuf, " PROCNAME(\"pixFMorphopGen_%d\");", fileindex); + str_proc2 = stringNew(bigbuf); + sprintf(bigbuf, + " pixt2 = pixFMorphopGen_%d(NULL, pixt1, operation, selname);", + fileindex); + str_dwa1 = stringNew(bigbuf); + sprintf(bigbuf, + " fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index);", + fileindex); + str_low_dt = stringNew(bigbuf); + sprintf(bigbuf, + " fmorphopgen_low_%d(datad, w, h, wpld, datas, wpls, index);", + fileindex); + str_low_ds = stringNew(bigbuf); + sprintf(bigbuf, + " fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index+1);", + fileindex); + str_low_tsp1 = stringNew(bigbuf); + sprintf(bigbuf, + " fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index);", + fileindex); + str_low_ts = stringNew(bigbuf); + sprintf(bigbuf, + " fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index+1);", + fileindex); + str_low_dtp1 = stringNew(bigbuf); + + /* Make the output sa */ + sa3 = sarrayCreate(0); + + /* Copyright notice and info header */ + sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Insert function names as documentation */ + sarrayAddString(sa3, str_doc1, L_INSERT); + sarrayAddString(sa3, str_doc2, L_INSERT); + + /* Add '#include's */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Insert function prototypes */ + sarrayAddString(sa3, str_proto1, L_INSERT); + sarrayAddString(sa3, str_proto2, L_INSERT); + sarrayAddString(sa3, str_proto3, L_INSERT); + + /* Add static globals */ + sprintf(bigbuf, "\nstatic l_int32 NUM_SELS_GENERATED = %d;", nsels); + sarrayAddString(sa3, bigbuf, L_COPY); + sprintf(bigbuf, "static char SEL_NAMES[][80] = {"); + sarrayAddString(sa3, bigbuf, L_COPY); + for (i = 0; i < nsels - 1; i++) { + sprintf(bigbuf, " \"%s\",", + sarrayGetString(sa1, i, L_NOCOPY)); + sarrayAddString(sa3, bigbuf, L_COPY); + } + sprintf(bigbuf, " \"%s\"};", + sarrayGetString(sa1, i, L_NOCOPY)); + sarrayAddString(sa3, bigbuf, L_COPY); + + /* Start pixMorphDwa_*() function description */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_doc3, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Finish pixMorphDwa_*() function definition */ + sarrayAddString(sa3, str_def1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_proc1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_dwa1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Start pixFMorphopGen_*() function description */ + sarrayAddString(sa3, str_doc4, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Finish pixFMorphopGen_*() function definition */ + sarrayAddString(sa3, str_def2, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_proc2, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_dt, L_COPY); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_ds, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_tsp1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_dt, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_ts, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + sarrayAddString(sa3, str_low_dtp1, L_INSERT); + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Output to file */ + filestr = sarrayToString(sa3, 1); + nbytes = strlen(filestr); + if (filename) + snprintf(bigbuf, L_BUF_SIZE, "%s.%d.c", filename, fileindex); + else + sprintf(bigbuf, "%s.%d.c", OUTROOT, fileindex); + l_binaryWrite(bigbuf, "w", filestr, nbytes); + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + LEPT_FREE(filestr); + return 0; +} + + +/* + * fmorphautogen2() + * + * Input: sela + * fileindex + * filename (; can be null) + * Return: 0 if OK; 1 on error + * + * Notes: + * (1) This function uses morphtemplate2.txt to create a + * low-level file that contains the low-level functions for + * implementing dilation and erosion for every sel + * in the input sela. + * (2) The fileindex parameter is inserted into the output + * filename, as described below. + * (3) If filename == NULL, the output file is fmorphgenlow.[n].c, + * where [n] is equal to the 'fileindex' parameter. + * (4) If filename != NULL, the output file is [filename]low.[n].c. + */ +l_int32 +fmorphautogen2(SELA *sela, + l_int32 fileindex, + const char *filename) +{ +char *filestr, *linestr, *fname; +char *str_doc1, *str_doc2, *str_doc3, *str_doc4, *str_def1; +char bigbuf[L_BUF_SIZE]; +char breakstring[] = " break;"; +char staticstring[] = "static void"; +l_int32 i, nsels, nbytes, actstart, end, newstart; +l_int32 argstart, argend, loopstart, loopend, finalstart, finalend; +size_t size; +SARRAY *sa1, *sa2, *sa3, *sa4, *sa5, *sa6; +SEL *sel; + + PROCNAME("fmorphautogen2"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + if (fileindex < 0) + fileindex = 0; + if ((nsels = selaGetCount(sela)) == 0) + return ERROR_INT("no sels in sela", procName, 1); + + /* Make the array of textlines from morphtemplate2.txt */ + if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL) + return ERROR_INT("filestr not made", procName, 1); + sa1 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + if (!sa1) + return ERROR_INT("sa1 not made", procName, 1); + + /* Make the array of static function names */ + if ((sa2 = sarrayCreate(2 * nsels)) == NULL) { + sarrayDestroy(&sa1); + return ERROR_INT("sa2 not made", procName, 1); + } + for (i = 0; i < nsels; i++) { + sprintf(bigbuf, "fdilate_%d_%d", fileindex, i); + sarrayAddString(sa2, bigbuf, L_COPY); + sprintf(bigbuf, "ferode_%d_%d", fileindex, i); + sarrayAddString(sa2, bigbuf, L_COPY); + } + + /* Make the static prototype strings */ + sa3 = sarrayCreate(2 * nsels); /* should be ok */ + for (i = 0; i < 2 * nsels; i++) { + fname = sarrayGetString(sa2, i, L_NOCOPY); + sprintf(bigbuf, "static void %s%s", fname, PROTOARGS); + sarrayAddString(sa3, bigbuf, L_COPY); + } + + /* Make strings containing function names */ + sprintf(bigbuf, " * l_int32 fmorphopgen_low_%d()", + fileindex); + str_doc1 = stringNew(bigbuf); + sprintf(bigbuf, " * void fdilate_%d_*()", fileindex); + str_doc2 = stringNew(bigbuf); + sprintf(bigbuf, " * void ferode_%d_*()", fileindex); + str_doc3 = stringNew(bigbuf); + + /* Output to this sa */ + sa4 = sarrayCreate(0); + + /* Copyright notice and info header */ + sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Insert function names as documentation */ + sarrayAddString(sa4, str_doc1, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_doc2, L_INSERT); + sarrayAddString(sa4, str_doc3, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Insert static protos */ + for (i = 0; i < 2 * nsels; i++) { + if ((linestr = sarrayGetString(sa3, i, L_COPY)) == NULL) { + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + return ERROR_INT("linestr not retrieved", procName, 1); + } + sarrayAddString(sa4, linestr, L_INSERT); + } + + /* More strings with function names */ + sprintf(bigbuf, " * fmorphopgen_low_%d()", fileindex); + str_doc4 = stringNew(bigbuf); + sprintf(bigbuf, "fmorphopgen_low_%d(l_uint32 *datad,", fileindex); + str_def1 = stringNew(bigbuf); + + /* Insert function header */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_doc4, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + sarrayAddString(sa4, str_def1, L_INSERT); + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Generate and insert the dispatcher code */ + for (i = 0; i < 2 * nsels; i++) { + sprintf(bigbuf, " case %d:", i); + sarrayAddString(sa4, bigbuf, L_COPY); + sprintf(bigbuf, " %s(datad, w, h, wpld, datas, wpls);", + sarrayGetString(sa2, i, L_NOCOPY)); + sarrayAddString(sa4, bigbuf, L_COPY); + sarrayAddString(sa4, breakstring, L_COPY); + } + + /* Finish the dispatcher and introduce the low-level code */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa4, sa1, actstart, end); + + /* Get the range for the args common to all functions */ + sarrayParseRange(sa1, newstart, &argstart, &argend, &newstart, "--", 0); + + /* Get the range for the loop code common to all functions */ + sarrayParseRange(sa1, newstart, &loopstart, &loopend, &newstart, "--", 0); + + /* Get the range for the ending code common to all functions */ + sarrayParseRange(sa1, newstart, &finalstart, &finalend, &newstart, "--", 0); + + /* Do all the static functions */ + for (i = 0; i < 2 * nsels; i++) { + /* Generate the function header and add the common args */ + sarrayAddString(sa4, staticstring, L_COPY); + fname = sarrayGetString(sa2, i, L_NOCOPY); + sprintf(bigbuf, "%s(l_uint32 *datad,", fname); + sarrayAddString(sa4, bigbuf, L_COPY); + sarrayAppendRange(sa4, sa1, argstart, argend); + + /* Declare and define wplsN args, as necessary */ + if ((sel = selaGetSel(sela, i/2)) == NULL) { + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + return ERROR_INT("sel not returned", procName, 1); + } + sa5 = sarrayMakeWplsCode(sel); + sarrayJoin(sa4, sa5); + sarrayDestroy(&sa5); + + /* Add the function loop code */ + sarrayAppendRange(sa4, sa1, loopstart, loopend); + + /* Insert barrel-op code for *dptr */ + sa6 = sarrayMakeInnerLoopDWACode(sel, i); + sarrayJoin(sa4, sa6); + sarrayDestroy(&sa6); + + /* Finish the function code */ + sarrayAppendRange(sa4, sa1, finalstart, finalend); + } + + /* Output to file */ + filestr = sarrayToString(sa4, 1); + nbytes = strlen(filestr); + if (filename) + snprintf(bigbuf, L_BUF_SIZE, "%slow.%d.c", filename, fileindex); + else + sprintf(bigbuf, "%slow.%d.c", OUTROOT, fileindex); + l_binaryWrite(bigbuf, "w", filestr, nbytes); + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + sarrayDestroy(&sa4); + LEPT_FREE(filestr); + return 0; +} + + +/*--------------------------------------------------------------------------* + * Helper code for sel * + *--------------------------------------------------------------------------*/ +/*! + * \brief sarrayMakeWplsCode() + */ +static SARRAY * +sarrayMakeWplsCode(SEL *sel) +{ +char emptystring[] = ""; +l_int32 i, j, ymax, dely, allvshifts; +l_int32 vshift[32]; +SARRAY *sa; + + PROCNAME("sarrayMakeWplsCode"); + + if (!sel) + return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); + + for (i = 0; i < 32; i++) + vshift[i] = 0; + ymax = 0; + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + if (sel->data[i][j] == 1) { + dely = L_ABS(i - sel->cy); + if (dely < 32) + vshift[dely] = 1; + ymax = L_MAX(ymax, dely); + } + } + } + if (ymax > 31) { + L_WARNING("ymax > 31; truncating to 31\n", procName); + ymax = 31; + } + + /* Test if this is a vertical brick */ + allvshifts = TRUE; + for (i = 0; i < ymax; i++) { + if (vshift[i] == 0) { + allvshifts = FALSE; + break; + } + } + + sa = sarrayCreate(0); + + /* Add declarations */ + if (allvshifts == TRUE) { /* packs them as well as possible */ + if (ymax > 4) + sarrayAddString(sa, wpldecls[2], L_COPY); + if (ymax > 8) + sarrayAddString(sa, wpldecls[6], L_COPY); + if (ymax > 12) + sarrayAddString(sa, wpldecls[10], L_COPY); + if (ymax > 16) + sarrayAddString(sa, wpldecls[14], L_COPY); + if (ymax > 20) + sarrayAddString(sa, wpldecls[18], L_COPY); + if (ymax > 24) + sarrayAddString(sa, wpldecls[22], L_COPY); + if (ymax > 28) + sarrayAddString(sa, wpldecls[26], L_COPY); + if (ymax > 1) + sarrayAddString(sa, wpldecls[ymax - 2], L_COPY); + } else { /* puts them one/line */ + for (i = 2; i <= ymax; i++) { + if (vshift[i]) + sarrayAddString(sa, wplgendecls[i - 2], L_COPY); + } + } + + sarrayAddString(sa, emptystring, L_COPY); + + /* Add definitions */ + for (i = 2; i <= ymax; i++) { + if (vshift[i]) + sarrayAddString(sa, wpldefs[i - 2], L_COPY); + } + + return sa; +} + + +/*! + * \brief sarrayMakeInnerLoopDWACode() + */ +static SARRAY * +sarrayMakeInnerLoopDWACode(SEL *sel, + l_int32 index) +{ +char *tstr, *string; +char logicalor[] = "|"; +char logicaland[] = "&"; +char bigbuf[L_BUF_SIZE]; +l_int32 i, j, optype, count, nfound, delx, dely; +SARRAY *sa; + + PROCNAME("sarrayMakeInnerLoopDWACode"); + + if (!sel) + return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); + + if (index % 2 == 0) { + optype = L_MORPH_DILATE; + tstr = logicalor; + } else { + optype = L_MORPH_ERODE; + tstr = logicaland; + } + + count = 0; + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + if (sel->data[i][j] == 1) + count++; + } + } + + sa = sarrayCreate(0); + if (count == 0) { + L_WARNING("no hits in Sel %d\n", procName, index); + return sa; /* no code inside! */ + } + + nfound = 0; + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + if (sel->data[i][j] == 1) { + nfound++; + if (optype == L_MORPH_DILATE) { + dely = sel->cy - i; + delx = sel->cx - j; + } else { /* optype == L_MORPH_ERODE */ + dely = i - sel->cy; + delx = j - sel->cx; + } + if ((string = makeBarrelshiftString(delx, dely)) == NULL) { + L_WARNING("barrel shift string not made\n", procName); + continue; + } + if (count == 1) /* just one item */ + sprintf(bigbuf, " *dptr = %s;", string); + else if (nfound == 1) + sprintf(bigbuf, " *dptr = %s %s", string, tstr); + else if (nfound < count) + sprintf(bigbuf, " %s %s", string, tstr); + else /* nfound == count */ + sprintf(bigbuf, " %s;", string); + sarrayAddString(sa, bigbuf, L_COPY); + LEPT_FREE(string); + } + } + } + + return sa; +} + + +/*! + * \brief makeBarrelshiftString() + */ +static char * +makeBarrelshiftString(l_int32 delx, /* j - cx */ + l_int32 dely) /* i - cy */ +{ +l_int32 absx, absy; +char bigbuf[L_BUF_SIZE]; + + PROCNAME("makeBarrelshiftString"); + + if (delx < -31 || delx > 31) + return (char *)ERROR_PTR("delx out of bounds", procName, NULL); + if (dely < -31 || dely > 31) + return (char *)ERROR_PTR("dely out of bounds", procName, NULL); + absx = L_ABS(delx); + absy = L_ABS(dely); + + if ((delx == 0) && (dely == 0)) + sprintf(bigbuf, "(*sptr)"); + else if ((delx == 0) && (dely < 0)) + sprintf(bigbuf, "(*(sptr %s))", wplstrm[absy - 1]); + else if ((delx == 0) && (dely > 0)) + sprintf(bigbuf, "(*(sptr %s))", wplstrp[absy - 1]); + else if ((delx < 0) && (dely == 0)) + sprintf(bigbuf, "((*(sptr) >> %d) | (*(sptr - 1) << %d))", + absx, 32 - absx); + else if ((delx > 0) && (dely == 0)) + sprintf(bigbuf, "((*(sptr) << %d) | (*(sptr + 1) >> %d))", + absx, 32 - absx); + else if ((delx < 0) && (dely < 0)) + sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx > 0) && (dely < 0)) + sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", + wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); + else if ((delx < 0) && (dely > 0)) + sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + else /* ((delx > 0) && (dely > 0)) */ + sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", + wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); + + return stringNew(bigbuf); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fmorphgen.1.c b/hgdriver/3rdparty/hgOCR/leptonica/fmorphgen.1.c new file mode 100644 index 0000000..f0bf9ae --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fmorphgen.1.c @@ -0,0 +1,277 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Top-level fast binary morphology with auto-generated sels + * + * PIX *pixMorphDwa_1() + * PIX *pixFMorphopGen_1() + */ + +#include +#include "allheaders.h" + +PIX *pixMorphDwa_1(PIX *pixd, PIX *pixs, l_int32 operation, char *selname); +PIX *pixFMorphopGen_1(PIX *pixd, PIX *pixs, l_int32 operation, char *selname); +l_int32 fmorphopgen_low_1(l_uint32 *datad, l_int32 w, + l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, + l_int32 index); + +static l_int32 NUM_SELS_GENERATED = 58; +static char SEL_NAMES[][80] = { + "sel_2h", + "sel_3h", + "sel_4h", + "sel_5h", + "sel_6h", + "sel_7h", + "sel_8h", + "sel_9h", + "sel_10h", + "sel_11h", + "sel_12h", + "sel_13h", + "sel_14h", + "sel_15h", + "sel_20h", + "sel_21h", + "sel_25h", + "sel_30h", + "sel_31h", + "sel_35h", + "sel_40h", + "sel_41h", + "sel_45h", + "sel_50h", + "sel_51h", + "sel_2v", + "sel_3v", + "sel_4v", + "sel_5v", + "sel_6v", + "sel_7v", + "sel_8v", + "sel_9v", + "sel_10v", + "sel_11v", + "sel_12v", + "sel_13v", + "sel_14v", + "sel_15v", + "sel_20v", + "sel_21v", + "sel_25v", + "sel_30v", + "sel_31v", + "sel_35v", + "sel_40v", + "sel_41v", + "sel_45v", + "sel_50v", + "sel_51v", + "sel_2", + "sel_3", + "sel_4", + "sel_5", + "sel_2dp", + "sel_2dm", + "sel_5dp", + "sel_5dm"}; + +/*! + * \brief pixMorphDwa_1() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, + * L_MORPH_OPEN, L_MORPH_CLOSE + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This simply adds a border, calls the appropriate
+ *          pixFMorphopGen_*(), and removes the border.
+ *          See the notes for that function.
+ *      (2) The size of the border depends on the operation
+ *          and the boundary conditions.
+ * 
+ */ +PIX * +pixMorphDwa_1(PIX *pixd, + PIX *pixs, + l_int32 operation, + char *selname) +{ +l_int32 bordercolor, bordersize; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixMorphDwa_1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + /* Set the border size */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + bordersize = 32; + if (bordercolor == 0 && operation == L_MORPH_CLOSE) + bordersize += 32; + + pixt1 = pixAddBorder(pixs, bordersize, 0); + pixt2 = pixFMorphopGen_1(NULL, pixt1, operation, selname); + pixt3 = pixRemoveBorder(pixt2, bordersize); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixCopy(pixd, pixt3); + pixDestroy(&pixt3); + return pixd; +} + + +/*! + * \brief pixFMorphopGen_1() + * + * \param[in] pixd usual 3 choices: null, == pixs, != pixs + * \param[in] pixs 1 bpp + * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, + * L_MORPH_OPEN, L_MORPH_CLOSE + * \param[in] sel name + * \return pixd + * + *
+ * Notes:
+ *      (1) This is a dwa operation, and the Sels must be limited in
+ *          size to not more than 31 pixels about the origin.
+ *      (2) A border of appropriate size (32 pixels, or 64 pixels
+ *          for safe closing with asymmetric b.c.) must be added before
+ *          this function is called.
+ *      (3) This handles all required setting of the border pixels
+ *          before erosion and dilation.
+ *      (4) The closing operation is safe; no pixels can be removed
+ *          near the boundary.
+ * 
+ */ +PIX * +pixFMorphopGen_1(PIX *pixd, + PIX *pixs, + l_int32 operation, + char *selname) +{ +l_int32 i, index, found, w, h, wpls, wpld, bordercolor, erodeop, borderop; +l_uint32 *datad, *datas, *datat; +PIX *pixt; + + PROCNAME("pixFMorphopGen_1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); + + /* Get boundary colors to use */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + if (bordercolor == 1) + erodeop = PIX_SET; + else + erodeop = PIX_CLR; + + found = FALSE; + for (i = 0; i < NUM_SELS_GENERATED; i++) { + if (strcmp(selname, SEL_NAMES[i]) == 0) { + found = TRUE; + index = 2 * i; + break; + } + } + if (found == FALSE) + return (PIX *)ERROR_PTR("sel index not found", procName, pixd); + + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + else /* for in-place or pre-allocated */ + pixResizeImageData(pixd, pixs); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* The images must be surrounded, in advance, with a border of + * size 32 pixels (or 64, for closing), that we'll read from. + * Fabricate a "proper" image as the subimage within the 32 + * pixel border, having the following parameters: */ + w = pixGetWidth(pixs) - 64; + h = pixGetHeight(pixs) - 64; + datas = pixGetData(pixs) + 32 * wpls + 1; + datad = pixGetData(pixd) + 32 * wpld + 1; + + if (operation == L_MORPH_DILATE || operation == L_MORPH_ERODE) { + borderop = PIX_CLR; + if (operation == L_MORPH_ERODE) { + borderop = erodeop; + index++; + } + if (pixd == pixs) { /* in-place; generate a temp image */ + if ((pixt = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + 32 * wpls + 1; + pixSetOrClearBorder(pixt, 32, 32, 32, 32, borderop); + fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index); + pixDestroy(&pixt); + } + else { /* not in-place */ + pixSetOrClearBorder(pixs, 32, 32, 32, 32, borderop); + fmorphopgen_low_1(datad, w, h, wpld, datas, wpls, index); + } + } + else { /* opening or closing; generate a temp image */ + if ((pixt = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + datat = pixGetData(pixt) + 32 * wpls + 1; + if (operation == L_MORPH_OPEN) { + pixSetOrClearBorder(pixs, 32, 32, 32, 32, erodeop); + fmorphopgen_low_1(datat, w, h, wpls, datas, wpls, index+1); + pixSetOrClearBorder(pixt, 32, 32, 32, 32, PIX_CLR); + fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index); + } + else { /* closing */ + pixSetOrClearBorder(pixs, 32, 32, 32, 32, PIX_CLR); + fmorphopgen_low_1(datat, w, h, wpls, datas, wpls, index); + pixSetOrClearBorder(pixt, 32, 32, 32, 32, erodeop); + fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index+1); + } + pixDestroy(&pixt); + } + + return pixd; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fmorphgenlow.1.c b/hgdriver/3rdparty/hgOCR/leptonica/fmorphgenlow.1.c new file mode 100644 index 0000000..dd43da2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fmorphgenlow.1.c @@ -0,0 +1,5862 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * Low-level fast binary morphology with auto-generated sels + * + * Dispatcher: + * l_int32 fmorphopgen_low_1() + * + * Static Low-level: + * void fdilate_1_*() + * void ferode_1_*() + */ + +#include "allheaders.h" + +static void fdilate_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void fdilate_1_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); +static void ferode_1_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); + + +/*---------------------------------------------------------------------* + * Fast morph dispatcher * + *---------------------------------------------------------------------*/ +/*! + * fmorphopgen_low_1() + * + * a dispatcher to appropriate low-level code + */ +l_int32 +fmorphopgen_low_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 index) +{ + + switch (index) + { + case 0: + fdilate_1_0(datad, w, h, wpld, datas, wpls); + break; + case 1: + ferode_1_0(datad, w, h, wpld, datas, wpls); + break; + case 2: + fdilate_1_1(datad, w, h, wpld, datas, wpls); + break; + case 3: + ferode_1_1(datad, w, h, wpld, datas, wpls); + break; + case 4: + fdilate_1_2(datad, w, h, wpld, datas, wpls); + break; + case 5: + ferode_1_2(datad, w, h, wpld, datas, wpls); + break; + case 6: + fdilate_1_3(datad, w, h, wpld, datas, wpls); + break; + case 7: + ferode_1_3(datad, w, h, wpld, datas, wpls); + break; + case 8: + fdilate_1_4(datad, w, h, wpld, datas, wpls); + break; + case 9: + ferode_1_4(datad, w, h, wpld, datas, wpls); + break; + case 10: + fdilate_1_5(datad, w, h, wpld, datas, wpls); + break; + case 11: + ferode_1_5(datad, w, h, wpld, datas, wpls); + break; + case 12: + fdilate_1_6(datad, w, h, wpld, datas, wpls); + break; + case 13: + ferode_1_6(datad, w, h, wpld, datas, wpls); + break; + case 14: + fdilate_1_7(datad, w, h, wpld, datas, wpls); + break; + case 15: + ferode_1_7(datad, w, h, wpld, datas, wpls); + break; + case 16: + fdilate_1_8(datad, w, h, wpld, datas, wpls); + break; + case 17: + ferode_1_8(datad, w, h, wpld, datas, wpls); + break; + case 18: + fdilate_1_9(datad, w, h, wpld, datas, wpls); + break; + case 19: + ferode_1_9(datad, w, h, wpld, datas, wpls); + break; + case 20: + fdilate_1_10(datad, w, h, wpld, datas, wpls); + break; + case 21: + ferode_1_10(datad, w, h, wpld, datas, wpls); + break; + case 22: + fdilate_1_11(datad, w, h, wpld, datas, wpls); + break; + case 23: + ferode_1_11(datad, w, h, wpld, datas, wpls); + break; + case 24: + fdilate_1_12(datad, w, h, wpld, datas, wpls); + break; + case 25: + ferode_1_12(datad, w, h, wpld, datas, wpls); + break; + case 26: + fdilate_1_13(datad, w, h, wpld, datas, wpls); + break; + case 27: + ferode_1_13(datad, w, h, wpld, datas, wpls); + break; + case 28: + fdilate_1_14(datad, w, h, wpld, datas, wpls); + break; + case 29: + ferode_1_14(datad, w, h, wpld, datas, wpls); + break; + case 30: + fdilate_1_15(datad, w, h, wpld, datas, wpls); + break; + case 31: + ferode_1_15(datad, w, h, wpld, datas, wpls); + break; + case 32: + fdilate_1_16(datad, w, h, wpld, datas, wpls); + break; + case 33: + ferode_1_16(datad, w, h, wpld, datas, wpls); + break; + case 34: + fdilate_1_17(datad, w, h, wpld, datas, wpls); + break; + case 35: + ferode_1_17(datad, w, h, wpld, datas, wpls); + break; + case 36: + fdilate_1_18(datad, w, h, wpld, datas, wpls); + break; + case 37: + ferode_1_18(datad, w, h, wpld, datas, wpls); + break; + case 38: + fdilate_1_19(datad, w, h, wpld, datas, wpls); + break; + case 39: + ferode_1_19(datad, w, h, wpld, datas, wpls); + break; + case 40: + fdilate_1_20(datad, w, h, wpld, datas, wpls); + break; + case 41: + ferode_1_20(datad, w, h, wpld, datas, wpls); + break; + case 42: + fdilate_1_21(datad, w, h, wpld, datas, wpls); + break; + case 43: + ferode_1_21(datad, w, h, wpld, datas, wpls); + break; + case 44: + fdilate_1_22(datad, w, h, wpld, datas, wpls); + break; + case 45: + ferode_1_22(datad, w, h, wpld, datas, wpls); + break; + case 46: + fdilate_1_23(datad, w, h, wpld, datas, wpls); + break; + case 47: + ferode_1_23(datad, w, h, wpld, datas, wpls); + break; + case 48: + fdilate_1_24(datad, w, h, wpld, datas, wpls); + break; + case 49: + ferode_1_24(datad, w, h, wpld, datas, wpls); + break; + case 50: + fdilate_1_25(datad, w, h, wpld, datas, wpls); + break; + case 51: + ferode_1_25(datad, w, h, wpld, datas, wpls); + break; + case 52: + fdilate_1_26(datad, w, h, wpld, datas, wpls); + break; + case 53: + ferode_1_26(datad, w, h, wpld, datas, wpls); + break; + case 54: + fdilate_1_27(datad, w, h, wpld, datas, wpls); + break; + case 55: + ferode_1_27(datad, w, h, wpld, datas, wpls); + break; + case 56: + fdilate_1_28(datad, w, h, wpld, datas, wpls); + break; + case 57: + ferode_1_28(datad, w, h, wpld, datas, wpls); + break; + case 58: + fdilate_1_29(datad, w, h, wpld, datas, wpls); + break; + case 59: + ferode_1_29(datad, w, h, wpld, datas, wpls); + break; + case 60: + fdilate_1_30(datad, w, h, wpld, datas, wpls); + break; + case 61: + ferode_1_30(datad, w, h, wpld, datas, wpls); + break; + case 62: + fdilate_1_31(datad, w, h, wpld, datas, wpls); + break; + case 63: + ferode_1_31(datad, w, h, wpld, datas, wpls); + break; + case 64: + fdilate_1_32(datad, w, h, wpld, datas, wpls); + break; + case 65: + ferode_1_32(datad, w, h, wpld, datas, wpls); + break; + case 66: + fdilate_1_33(datad, w, h, wpld, datas, wpls); + break; + case 67: + ferode_1_33(datad, w, h, wpld, datas, wpls); + break; + case 68: + fdilate_1_34(datad, w, h, wpld, datas, wpls); + break; + case 69: + ferode_1_34(datad, w, h, wpld, datas, wpls); + break; + case 70: + fdilate_1_35(datad, w, h, wpld, datas, wpls); + break; + case 71: + ferode_1_35(datad, w, h, wpld, datas, wpls); + break; + case 72: + fdilate_1_36(datad, w, h, wpld, datas, wpls); + break; + case 73: + ferode_1_36(datad, w, h, wpld, datas, wpls); + break; + case 74: + fdilate_1_37(datad, w, h, wpld, datas, wpls); + break; + case 75: + ferode_1_37(datad, w, h, wpld, datas, wpls); + break; + case 76: + fdilate_1_38(datad, w, h, wpld, datas, wpls); + break; + case 77: + ferode_1_38(datad, w, h, wpld, datas, wpls); + break; + case 78: + fdilate_1_39(datad, w, h, wpld, datas, wpls); + break; + case 79: + ferode_1_39(datad, w, h, wpld, datas, wpls); + break; + case 80: + fdilate_1_40(datad, w, h, wpld, datas, wpls); + break; + case 81: + ferode_1_40(datad, w, h, wpld, datas, wpls); + break; + case 82: + fdilate_1_41(datad, w, h, wpld, datas, wpls); + break; + case 83: + ferode_1_41(datad, w, h, wpld, datas, wpls); + break; + case 84: + fdilate_1_42(datad, w, h, wpld, datas, wpls); + break; + case 85: + ferode_1_42(datad, w, h, wpld, datas, wpls); + break; + case 86: + fdilate_1_43(datad, w, h, wpld, datas, wpls); + break; + case 87: + ferode_1_43(datad, w, h, wpld, datas, wpls); + break; + case 88: + fdilate_1_44(datad, w, h, wpld, datas, wpls); + break; + case 89: + ferode_1_44(datad, w, h, wpld, datas, wpls); + break; + case 90: + fdilate_1_45(datad, w, h, wpld, datas, wpls); + break; + case 91: + ferode_1_45(datad, w, h, wpld, datas, wpls); + break; + case 92: + fdilate_1_46(datad, w, h, wpld, datas, wpls); + break; + case 93: + ferode_1_46(datad, w, h, wpld, datas, wpls); + break; + case 94: + fdilate_1_47(datad, w, h, wpld, datas, wpls); + break; + case 95: + ferode_1_47(datad, w, h, wpld, datas, wpls); + break; + case 96: + fdilate_1_48(datad, w, h, wpld, datas, wpls); + break; + case 97: + ferode_1_48(datad, w, h, wpld, datas, wpls); + break; + case 98: + fdilate_1_49(datad, w, h, wpld, datas, wpls); + break; + case 99: + ferode_1_49(datad, w, h, wpld, datas, wpls); + break; + case 100: + fdilate_1_50(datad, w, h, wpld, datas, wpls); + break; + case 101: + ferode_1_50(datad, w, h, wpld, datas, wpls); + break; + case 102: + fdilate_1_51(datad, w, h, wpld, datas, wpls); + break; + case 103: + ferode_1_51(datad, w, h, wpld, datas, wpls); + break; + case 104: + fdilate_1_52(datad, w, h, wpld, datas, wpls); + break; + case 105: + ferode_1_52(datad, w, h, wpld, datas, wpls); + break; + case 106: + fdilate_1_53(datad, w, h, wpld, datas, wpls); + break; + case 107: + ferode_1_53(datad, w, h, wpld, datas, wpls); + break; + case 108: + fdilate_1_54(datad, w, h, wpld, datas, wpls); + break; + case 109: + ferode_1_54(datad, w, h, wpld, datas, wpls); + break; + case 110: + fdilate_1_55(datad, w, h, wpld, datas, wpls); + break; + case 111: + ferode_1_55(datad, w, h, wpld, datas, wpls); + break; + case 112: + fdilate_1_56(datad, w, h, wpld, datas, wpls); + break; + case 113: + ferode_1_56(datad, w, h, wpld, datas, wpls); + break; + case 114: + fdilate_1_57(datad, w, h, wpld, datas, wpls); + break; + case 115: + ferode_1_57(datad, w, h, wpld, datas, wpls); + break; + } + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Low-level auto-generated static routines * + *--------------------------------------------------------------------------*/ +/* + * N.B. In all the low-level routines, the part of the image + * that is accessed has been clipped by 32 pixels on + * all four sides. This is done in the higher level + * code by redefining w and h smaller and by moving the + * start-of-image pointers up to the beginning of this + * interior rectangle. + */ +static void +fdilate_1_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr); + } + } +} + +static void +ferode_1_0(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr); + } + } +} + +static void +fdilate_1_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)); + } + } +} + +static void +ferode_1_1(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)); + } + } +} + +static void +fdilate_1_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)); + } + } +} + +static void +ferode_1_2(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)); + } + } +} + +static void +fdilate_1_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)); + } + } +} + +static void +ferode_1_3(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)); + } + } +} + +static void +fdilate_1_4(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)); + } + } +} + +static void +ferode_1_4(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)); + } + } +} + +static void +fdilate_1_5(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)); + } + } +} + +static void +ferode_1_5(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)); + } + } +} + +static void +fdilate_1_6(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)); + } + } +} + +static void +ferode_1_6(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)); + } + } +} + +static void +fdilate_1_7(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)); + } + } +} + +static void +ferode_1_7(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)); + } + } +} + +static void +fdilate_1_8(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)); + } + } +} + +static void +ferode_1_8(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)); + } + } +} + +static void +fdilate_1_9(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)); + } + } +} + +static void +ferode_1_9(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)); + } + } +} + +static void +fdilate_1_10(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)); + } + } +} + +static void +ferode_1_10(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)); + } + } +} + +static void +fdilate_1_11(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)); + } + } +} + +static void +ferode_1_11(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)); + } + } +} + +static void +fdilate_1_12(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)); + } + } +} + +static void +ferode_1_12(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)); + } + } +} + +static void +fdilate_1_13(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)); + } + } +} + +static void +ferode_1_13(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)); + } + } +} + +static void +fdilate_1_14(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)); + } + } +} + +static void +ferode_1_14(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)); + } + } +} + +static void +fdilate_1_15(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)); + } + } +} + +static void +ferode_1_15(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)); + } + } +} + +static void +fdilate_1_16(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)); + } + } +} + +static void +ferode_1_16(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)); + } + } +} + +static void +fdilate_1_17(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)); + } + } +} + +static void +ferode_1_17(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)); + } + } +} + +static void +fdilate_1_18(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)); + } + } +} + +static void +ferode_1_18(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)); + } + } +} + +static void +fdilate_1_19(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)); + } + } +} + +static void +ferode_1_19(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)); + } + } +} + +static void +fdilate_1_20(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)); + } + } +} + +static void +ferode_1_20(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)); + } + } +} + +static void +fdilate_1_21(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)); + } + } +} + +static void +ferode_1_21(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)); + } + } +} + +static void +fdilate_1_22(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | + ((*(sptr) >> 22) | (*(sptr - 1) << 10)); + } + } +} + +static void +ferode_1_22(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & + ((*(sptr) << 22) | (*(sptr + 1) >> 10)); + } + } +} + +static void +fdilate_1_23(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 25) | (*(sptr + 1) >> 7)) | + ((*(sptr) << 24) | (*(sptr + 1) >> 8)) | + ((*(sptr) << 23) | (*(sptr + 1) >> 9)) | + ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | + ((*(sptr) >> 22) | (*(sptr - 1) << 10)) | + ((*(sptr) >> 23) | (*(sptr - 1) << 9)) | + ((*(sptr) >> 24) | (*(sptr - 1) << 8)); + } + } +} + +static void +ferode_1_23(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 25) | (*(sptr - 1) << 7)) & + ((*(sptr) >> 24) | (*(sptr - 1) << 8)) & + ((*(sptr) >> 23) | (*(sptr - 1) << 9)) & + ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & + ((*(sptr) << 22) | (*(sptr + 1) >> 10)) & + ((*(sptr) << 23) | (*(sptr + 1) >> 9)) & + ((*(sptr) << 24) | (*(sptr + 1) >> 8)); + } + } +} + +static void +fdilate_1_24(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 25) | (*(sptr + 1) >> 7)) | + ((*(sptr) << 24) | (*(sptr + 1) >> 8)) | + ((*(sptr) << 23) | (*(sptr + 1) >> 9)) | + ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | + ((*(sptr) >> 22) | (*(sptr - 1) << 10)) | + ((*(sptr) >> 23) | (*(sptr - 1) << 9)) | + ((*(sptr) >> 24) | (*(sptr - 1) << 8)) | + ((*(sptr) >> 25) | (*(sptr - 1) << 7)); + } + } +} + +static void +ferode_1_24(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 25) | (*(sptr - 1) << 7)) & + ((*(sptr) >> 24) | (*(sptr - 1) << 8)) & + ((*(sptr) >> 23) | (*(sptr - 1) << 9)) & + ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & + ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & + ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & + ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & + ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & + ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & + ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & + ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & + ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & + ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & + ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & + ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & + ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & + ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & + ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & + ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & + ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & + ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & + ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & + ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & + ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & + ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & + ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & + ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & + ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & + ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & + ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & + ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & + ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & + ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & + ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & + ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & + ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & + ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & + ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & + ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & + ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & + ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & + ((*(sptr) << 22) | (*(sptr + 1) >> 10)) & + ((*(sptr) << 23) | (*(sptr + 1) >> 9)) & + ((*(sptr) << 24) | (*(sptr + 1) >> 8)) & + ((*(sptr) << 25) | (*(sptr + 1) >> 7)); + } + } +} + +static void +fdilate_1_25(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls)) | + (*sptr); + } + } +} + +static void +ferode_1_25(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls)) & + (*sptr); + } + } +} + +static void +fdilate_1_26(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)); + } + } +} + +static void +ferode_1_26(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)); + } + } +} + +static void +fdilate_1_27(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)); + } + } +} + +static void +ferode_1_27(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)); + } + } +} + +static void +fdilate_1_28(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)); + } + } +} + +static void +ferode_1_28(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)); + } + } +} + +static void +fdilate_1_29(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)); + } + } +} + +static void +ferode_1_29(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)); + } + } +} + +static void +fdilate_1_30(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)); + } + } +} + +static void +ferode_1_30(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)); + } + } +} + +static void +fdilate_1_31(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)); + } + } +} + +static void +ferode_1_31(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)); + } + } +} + +static void +fdilate_1_32(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)); + } + } +} + +static void +ferode_1_32(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)); + } + } +} + +static void +fdilate_1_33(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)); + } + } +} + +static void +ferode_1_33(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)); + } + } +} + +static void +fdilate_1_34(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)); + } + } +} + +static void +ferode_1_34(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)); + } + } +} + +static void +fdilate_1_35(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)); + } + } +} + +static void +ferode_1_35(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)); + } + } +} + +static void +fdilate_1_36(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)); + } + } +} + +static void +ferode_1_36(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)); + } + } +} + +static void +fdilate_1_37(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)); + } + } +} + +static void +ferode_1_37(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)); + } + } +} + +static void +fdilate_1_38(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)); + } + } +} + +static void +ferode_1_38(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)); + } + } +} + +static void +fdilate_1_39(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)); + } + } +} + +static void +ferode_1_39(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)); + } + } +} + +static void +fdilate_1_40(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)); + } + } +} + +static void +ferode_1_40(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)); + } + } +} + +static void +fdilate_1_41(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)); + } + } +} + +static void +ferode_1_41(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)); + } + } +} + +static void +fdilate_1_42(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)); + } + } +} + +static void +ferode_1_42(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)); + } + } +} + +static void +fdilate_1_43(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)); + } + } +} + +static void +ferode_1_43(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)); + } + } +} + +static void +fdilate_1_44(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)); + } + } +} + +static void +ferode_1_44(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)); + } + } +} + +static void +fdilate_1_45(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls20)) | + (*(sptr + wpls19)) | + (*(sptr + wpls18)) | + (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)) | + (*(sptr - wpls18)) | + (*(sptr - wpls19)); + } + } +} + +static void +ferode_1_45(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls20)) & + (*(sptr - wpls19)) & + (*(sptr - wpls18)) & + (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)) & + (*(sptr + wpls18)) & + (*(sptr + wpls19)); + } + } +} + +static void +fdilate_1_46(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls20)) | + (*(sptr + wpls19)) | + (*(sptr + wpls18)) | + (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)) | + (*(sptr - wpls18)) | + (*(sptr - wpls19)) | + (*(sptr - wpls20)); + } + } +} + +static void +ferode_1_46(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls20)) & + (*(sptr - wpls19)) & + (*(sptr - wpls18)) & + (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)) & + (*(sptr + wpls18)) & + (*(sptr + wpls19)) & + (*(sptr + wpls20)); + } + } +} + +static void +fdilate_1_47(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls22)) | + (*(sptr + wpls21)) | + (*(sptr + wpls20)) | + (*(sptr + wpls19)) | + (*(sptr + wpls18)) | + (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)) | + (*(sptr - wpls18)) | + (*(sptr - wpls19)) | + (*(sptr - wpls20)) | + (*(sptr - wpls21)) | + (*(sptr - wpls22)); + } + } +} + +static void +ferode_1_47(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls22)) & + (*(sptr - wpls21)) & + (*(sptr - wpls20)) & + (*(sptr - wpls19)) & + (*(sptr - wpls18)) & + (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)) & + (*(sptr + wpls18)) & + (*(sptr + wpls19)) & + (*(sptr + wpls20)) & + (*(sptr + wpls21)) & + (*(sptr + wpls22)); + } + } +} + +static void +fdilate_1_48(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22, wpls23, wpls24; +l_int32 wpls25; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + wpls24 = 24 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls25)) | + (*(sptr + wpls24)) | + (*(sptr + wpls23)) | + (*(sptr + wpls22)) | + (*(sptr + wpls21)) | + (*(sptr + wpls20)) | + (*(sptr + wpls19)) | + (*(sptr + wpls18)) | + (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)) | + (*(sptr - wpls18)) | + (*(sptr - wpls19)) | + (*(sptr - wpls20)) | + (*(sptr - wpls21)) | + (*(sptr - wpls22)) | + (*(sptr - wpls23)) | + (*(sptr - wpls24)); + } + } +} + +static void +ferode_1_48(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22, wpls23, wpls24; +l_int32 wpls25; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + wpls24 = 24 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls25)) & + (*(sptr - wpls24)) & + (*(sptr - wpls23)) & + (*(sptr - wpls22)) & + (*(sptr - wpls21)) & + (*(sptr - wpls20)) & + (*(sptr - wpls19)) & + (*(sptr - wpls18)) & + (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)) & + (*(sptr + wpls18)) & + (*(sptr + wpls19)) & + (*(sptr + wpls20)) & + (*(sptr + wpls21)) & + (*(sptr + wpls22)) & + (*(sptr + wpls23)) & + (*(sptr + wpls24)); + } + } +} + +static void +fdilate_1_49(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22, wpls23, wpls24; +l_int32 wpls25; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + wpls24 = 24 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr + wpls25)) | + (*(sptr + wpls24)) | + (*(sptr + wpls23)) | + (*(sptr + wpls22)) | + (*(sptr + wpls21)) | + (*(sptr + wpls20)) | + (*(sptr + wpls19)) | + (*(sptr + wpls18)) | + (*(sptr + wpls17)) | + (*(sptr + wpls16)) | + (*(sptr + wpls15)) | + (*(sptr + wpls14)) | + (*(sptr + wpls13)) | + (*(sptr + wpls12)) | + (*(sptr + wpls11)) | + (*(sptr + wpls10)) | + (*(sptr + wpls9)) | + (*(sptr + wpls8)) | + (*(sptr + wpls7)) | + (*(sptr + wpls6)) | + (*(sptr + wpls5)) | + (*(sptr + wpls4)) | + (*(sptr + wpls3)) | + (*(sptr + wpls2)) | + (*(sptr + wpls)) | + (*sptr) | + (*(sptr - wpls)) | + (*(sptr - wpls2)) | + (*(sptr - wpls3)) | + (*(sptr - wpls4)) | + (*(sptr - wpls5)) | + (*(sptr - wpls6)) | + (*(sptr - wpls7)) | + (*(sptr - wpls8)) | + (*(sptr - wpls9)) | + (*(sptr - wpls10)) | + (*(sptr - wpls11)) | + (*(sptr - wpls12)) | + (*(sptr - wpls13)) | + (*(sptr - wpls14)) | + (*(sptr - wpls15)) | + (*(sptr - wpls16)) | + (*(sptr - wpls17)) | + (*(sptr - wpls18)) | + (*(sptr - wpls19)) | + (*(sptr - wpls20)) | + (*(sptr - wpls21)) | + (*(sptr - wpls22)) | + (*(sptr - wpls23)) | + (*(sptr - wpls24)) | + (*(sptr - wpls25)); + } + } +} + +static void +ferode_1_49(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2, wpls3, wpls4; +l_int32 wpls5, wpls6, wpls7, wpls8; +l_int32 wpls9, wpls10, wpls11, wpls12; +l_int32 wpls13, wpls14, wpls15, wpls16; +l_int32 wpls17, wpls18, wpls19, wpls20; +l_int32 wpls21, wpls22, wpls23, wpls24; +l_int32 wpls25; + + wpls2 = 2 * wpls; + wpls3 = 3 * wpls; + wpls4 = 4 * wpls; + wpls5 = 5 * wpls; + wpls6 = 6 * wpls; + wpls7 = 7 * wpls; + wpls8 = 8 * wpls; + wpls9 = 9 * wpls; + wpls10 = 10 * wpls; + wpls11 = 11 * wpls; + wpls12 = 12 * wpls; + wpls13 = 13 * wpls; + wpls14 = 14 * wpls; + wpls15 = 15 * wpls; + wpls16 = 16 * wpls; + wpls17 = 17 * wpls; + wpls18 = 18 * wpls; + wpls19 = 19 * wpls; + wpls20 = 20 * wpls; + wpls21 = 21 * wpls; + wpls22 = 22 * wpls; + wpls23 = 23 * wpls; + wpls24 = 24 * wpls; + wpls25 = 25 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*(sptr - wpls25)) & + (*(sptr - wpls24)) & + (*(sptr - wpls23)) & + (*(sptr - wpls22)) & + (*(sptr - wpls21)) & + (*(sptr - wpls20)) & + (*(sptr - wpls19)) & + (*(sptr - wpls18)) & + (*(sptr - wpls17)) & + (*(sptr - wpls16)) & + (*(sptr - wpls15)) & + (*(sptr - wpls14)) & + (*(sptr - wpls13)) & + (*(sptr - wpls12)) & + (*(sptr - wpls11)) & + (*(sptr - wpls10)) & + (*(sptr - wpls9)) & + (*(sptr - wpls8)) & + (*(sptr - wpls7)) & + (*(sptr - wpls6)) & + (*(sptr - wpls5)) & + (*(sptr - wpls4)) & + (*(sptr - wpls3)) & + (*(sptr - wpls2)) & + (*(sptr - wpls)) & + (*sptr) & + (*(sptr + wpls)) & + (*(sptr + wpls2)) & + (*(sptr + wpls3)) & + (*(sptr + wpls4)) & + (*(sptr + wpls5)) & + (*(sptr + wpls6)) & + (*(sptr + wpls7)) & + (*(sptr + wpls8)) & + (*(sptr + wpls9)) & + (*(sptr + wpls10)) & + (*(sptr + wpls11)) & + (*(sptr + wpls12)) & + (*(sptr + wpls13)) & + (*(sptr + wpls14)) & + (*(sptr + wpls15)) & + (*(sptr + wpls16)) & + (*(sptr + wpls17)) & + (*(sptr + wpls18)) & + (*(sptr + wpls19)) & + (*(sptr + wpls20)) & + (*(sptr + wpls21)) & + (*(sptr + wpls22)) & + (*(sptr + wpls23)) & + (*(sptr + wpls24)) & + (*(sptr + wpls25)); + } + } +} + +static void +fdilate_1_50(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | + (*(sptr + wpls)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr); + } + } +} + +static void +ferode_1_50(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + (*(sptr - wpls)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr); + } + } +} + +static void +fdilate_1_51(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | + (*(sptr + wpls)) | + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | + (*(sptr - wpls)) | + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); + } + } +} + +static void +ferode_1_51(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + (*(sptr - wpls)) & + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & + (*(sptr + wpls)) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fdilate_1_52(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | + ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) | + (*(sptr + wpls2)) | + ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) | + ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) | + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | + (*(sptr + wpls)) | + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) | + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | + (*(sptr - wpls)) | + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); + } + } +} + +static void +ferode_1_52(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & + ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & + (*(sptr - wpls2)) & + ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & + ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + (*(sptr - wpls)) & + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & + (*(sptr + wpls)) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fdilate_1_53(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | + ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) | + (*(sptr + wpls2)) | + ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) | + ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) | + ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) | + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | + (*(sptr + wpls)) | + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | + ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) | + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | + (*sptr) | + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | + ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) | + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | + (*(sptr - wpls)) | + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) | + ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) | + ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) | + ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) | + (*(sptr - wpls2)) | + ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) | + ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)); + } + } +} + +static void +ferode_1_53(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & + ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & + (*(sptr - wpls2)) & + ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & + ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & + ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + (*(sptr - wpls)) & + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & + ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) & + ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & + ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & + (*sptr) & + ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & + ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & + (*(sptr + wpls)) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & + ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) & + ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) & + ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) & + (*(sptr + wpls2)) & + ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & + ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); + } + } +} + +static void +fdilate_1_54(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | + (*(sptr - wpls)); + } + } +} + +static void +ferode_1_54(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & + (*(sptr + wpls)); + } + } +} + +static void +fdilate_1_55(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr) | + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); + } + } +} + +static void +ferode_1_55(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; + + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = (*sptr) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); + } + } +} + +static void +fdilate_1_56(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) | + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | + (*sptr) | + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | + ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)); + } + } +} + +static void +ferode_1_56(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & + ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & + (*sptr) & + ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & + ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)); + } + } +} + +static void +fdilate_1_57(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | + (*sptr) | + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) | + ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)); + } + } +} + +static void +ferode_1_57(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls) +{ +l_int32 i; +l_int32 j, pwpls; +l_uint32 *sptr, *dptr; +l_int32 wpls2; + + wpls2 = 2 * wpls; + pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ + + for (i = 0; i < h; i++) { + sptr = datas + i * wpls; + dptr = datad + i * wpld; + for (j = 0; j < pwpls; j++, sptr++, dptr++) { + *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & + ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & + (*sptr) & + ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & + ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); + } + } +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fpix1.c b/hgdriver/3rdparty/hgOCR/leptonica/fpix1.c new file mode 100644 index 0000000..5c24f5e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fpix1.c @@ -0,0 +1,2339 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file fpix1.c + *
+ *
+ *    ---------------------------------------------------
+ *    This file has these FPix, FPixa and DPix utilities:
+ *         - creation and destruction
+ *         - accessors
+ *         - serialization and deserialization
+ *    ---------------------------------------------------
+ *
+ *    FPix Create/copy/destroy
+ *          FPIX          *fpixCreate()
+ *          FPIX          *fpixCreateTemplate()
+ *          FPIX          *fpixClone()
+ *          FPIX          *fpixCopy()
+ *          l_int32        fpixResizeImageData()
+ *          void           fpixDestroy()
+ *
+ *    FPix accessors
+ *          l_int32        fpixGetDimensions()
+ *          l_int32        fpixSetDimensions()
+ *          l_int32        fpixGetWpl()
+ *          l_int32        fpixSetWpl()
+ *          l_int32        fpixGetRefcount()
+ *          l_int32        fpixChangeRefcount()
+ *          l_int32        fpixGetResolution()
+ *          l_int32        fpixSetResolution()
+ *          l_int32        fpixCopyResolution()
+ *          l_float32     *fpixGetData()
+ *          l_int32        fpixSetData()
+ *          l_int32        fpixGetPixel()
+ *          l_int32        fpixSetPixel()
+ *
+ *    FPixa Create/copy/destroy
+ *          FPIXA         *fpixaCreate()
+ *          FPIXA         *fpixaCopy()
+ *          void           fpixaDestroy()
+ *
+ *    FPixa addition
+ *          l_int32        fpixaAddFPix()
+ *          static l_int32 fpixaExtendArray()
+ *          static l_int32 fpixaExtendArrayToSize()
+ *
+ *    FPixa accessors
+ *          l_int32        fpixaGetCount()
+ *          l_int32        fpixaChangeRefcount()
+ *          FPIX          *fpixaGetFPix()
+ *          l_int32        fpixaGetFPixDimensions()
+ *          l_float32     *fpixaGetData()
+ *          l_int32        fpixaGetPixel()
+ *          l_int32        fpixaSetPixel()
+ *
+ *    DPix Create/copy/destroy
+ *          DPIX          *dpixCreate()
+ *          DPIX          *dpixCreateTemplate()
+ *          DPIX          *dpixClone()
+ *          DPIX          *dpixCopy()
+ *          l_int32        dpixResizeImageData()
+ *          void           dpixDestroy()
+ *
+ *    DPix accessors
+ *          l_int32        dpixGetDimensions()
+ *          l_int32        dpixSetDimensions()
+ *          l_int32        dpixGetWpl()
+ *          l_int32        dpixSetWpl()
+ *          l_int32        dpixGetRefcount()
+ *          l_int32        dpixChangeRefcount()
+ *          l_int32        dpixGetResolution()
+ *          l_int32        dpixSetResolution()
+ *          l_int32        dpixCopyResolution()
+ *          l_float64     *dpixGetData()
+ *          l_int32        dpixSetData()
+ *          l_int32        dpixGetPixel()
+ *          l_int32        dpixSetPixel()
+ *
+ *    FPix serialized I/O
+ *          FPIX          *fpixRead()
+ *          FPIX          *fpixReadStream()
+ *          FPIX          *fpixReadMem()
+ *          l_int32        fpixWrite()
+ *          l_int32        fpixWriteStream()
+ *          l_int32        fpixWriteMem()
+ *          FPIX          *fpixEndianByteSwap()
+ *
+ *    DPix serialized I/O
+ *          DPIX          *dpixRead()
+ *          DPIX          *dpixReadStream()
+ *          DPIX          *dpixReadMem()
+ *          l_int32        dpixWrite()
+ *          l_int32        dpixWriteStream()
+ *          l_int32        dpixWriteMem()
+ *          DPIX          *dpixEndianByteSwap()
+ *
+ *    Print FPix (subsampled, for debugging)
+ *          l_int32        fpixPrintStream()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 fpixaExtendArray(FPIXA *fpixa); +static l_int32 fpixaExtendArrayToSize(FPIXA *fpixa, l_int32 size); + + +/*--------------------------------------------------------------------* + * FPix Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixCreate() + * + * \param[in] width, height + * \return fpixd with data allocated and initialized to 0, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a FPix of specified size, with the data array
+ *          allocated and initialized to 0.
+ *      (2) The number of pixels must be less than 2^29.
+ * 
+ */ +FPIX * +fpixCreate(l_int32 width, + l_int32 height) +{ +l_float32 *data; +l_uint64 npix64; +FPIX *fpixd; + + PROCNAME("fpixCreate"); + + if (width <= 0) + return (FPIX *)ERROR_PTR("width must be > 0", procName, NULL); + if (height <= 0) + return (FPIX *)ERROR_PTR("height must be > 0", procName, NULL); + + /* Avoid overflow in malloc arg, malicious or otherwise */ + npix64 = (l_uint64)width * (l_uint64)height; /* # of 4-byte pixels */ + if (npix64 >= (1LL << 29)) { + L_ERROR("requested w = %d, h = %d\n", procName, width, height); + return (FPIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); + } + + fpixd = (FPIX *)LEPT_CALLOC(1, sizeof(FPIX)); + fpixSetDimensions(fpixd, width, height); + fpixSetWpl(fpixd, width); /* 4-byte words */ + fpixd->refcount = 1; + + data = (l_float32 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float32)); + if (!data) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("calloc fail for data", procName, NULL); + } + fpixSetData(fpixd, data); + return fpixd; +} + + +/*! + * \brief fpixCreateTemplate() + * + * \param[in] fpixs + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a FPix of the same size as the input FPix, with the
+ *          data array allocated and initialized to 0.
+ *      (2) Copies the resolution.
+ * 
+ */ +FPIX * +fpixCreateTemplate(FPIX *fpixs) +{ +l_int32 w, h; +FPIX *fpixd; + + PROCNAME("fpixCreateTemplate"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + if ((fpixd = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + fpixCopyResolution(fpixd, fpixs); + return fpixd; +} + + +/*! + * \brief fpixClone() + * + * \param[in] fpix + * \return same fpix ptr, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixClone() for definition and usage.
+ * 
+ */ +FPIX * +fpixClone(FPIX *fpix) +{ + PROCNAME("fpixClone"); + + if (!fpix) + return (FPIX *)ERROR_PTR("fpix not defined", procName, NULL); + fpixChangeRefcount(fpix, 1); + + return fpix; +} + + +/*! + * \brief fpixCopy() + * + * \param[in] fpixd [optional] can be null, or equal to fpixs, + * or different from fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) There are three cases:
+ *            (a) fpixd == null  (makes a new fpix; refcount = 1)
+ *            (b) fpixd == fpixs  (no-op)
+ *            (c) fpixd != fpixs  (data copy; no change in refcount)
+ *          If the refcount of fpixd > 1, case (c) will side-effect
+ *          these handles.
+ *      (2) The general pattern of use is:
+ *             fpixd = fpixCopy(fpixd, fpixs);
+ *          This will work for all three cases.
+ *          For clarity when the case is known, you can use:
+ *            (a) fpixd = fpixCopy(NULL, fpixs);
+ *            (c) fpixCopy(fpixd, fpixs);
+ *      (3) For case (c), we check if fpixs and fpixd are the same size.
+ *          If so, the data is copied directly.
+ *          Otherwise, the data is reallocated to the correct size
+ *          and the copy proceeds.  The refcount of fpixd is unchanged.
+ *      (4) This operation, like all others that may involve a pre-existing
+ *          fpixd, will side-effect any existing clones of fpixd.
+ * 
+ */ +FPIX * +fpixCopy(FPIX *fpixd, /* can be null */ + FPIX *fpixs) +{ +l_int32 w, h, bytes; +l_float32 *datas, *datad; + + PROCNAME("fpixCopy"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (fpixs == fpixd) + return fpixd; + + /* Total bytes in image data */ + fpixGetDimensions(fpixs, &w, &h); + bytes = 4 * w * h; + + /* If we're making a new fpix ... */ + if (!fpixd) { + if ((fpixd = fpixCreateTemplate(fpixs)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + memcpy(datad, datas, bytes); + return fpixd; + } + + /* Reallocate image data if sizes are different */ + fpixResizeImageData(fpixd, fpixs); + + /* Copy data */ + fpixCopyResolution(fpixd, fpixs); + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + memcpy(datad, datas, bytes); + return fpixd; +} + + +/*! + * \brief fpixResizeImageData() + * + * \param[in] fpixd, fpixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the data sizes differ, this destroys the existing
+ *          data in fpixd and allocates a new, uninitialized, data array
+ *          of the same size as the data in fpixs.  Otherwise, this
+ *          doesn't do anything.
+ * 
+ */ +l_ok +fpixResizeImageData(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 ws, hs, wd, hd, bytes; +l_float32 *data; + + PROCNAME("fpixResizeImageData"); + + if (!fpixs) + return ERROR_INT("fpixs not defined", procName, 1); + if (!fpixd) + return ERROR_INT("fpixd not defined", procName, 1); + + fpixGetDimensions(fpixs, &ws, &hs); + fpixGetDimensions(fpixd, &wd, &hd); + if (ws == wd && hs == hd) /* nothing to do */ + return 0; + + fpixSetDimensions(fpixd, ws, hs); + fpixSetWpl(fpixd, ws); + bytes = 4 * ws * hs; + data = fpixGetData(fpixd); + if (data) LEPT_FREE(data); + if ((data = (l_float32 *)LEPT_MALLOC(bytes)) == NULL) + return ERROR_INT("LEPT_MALLOC fail for data", procName, 1); + fpixSetData(fpixd, data); + return 0; +} + + +/*! + * \brief fpixDestroy() + * + * \param[in,out] pfpix will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the fpix.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +fpixDestroy(FPIX **pfpix) +{ +l_float32 *data; +FPIX *fpix; + + PROCNAME("fpixDestroy"); + + if (!pfpix) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((fpix = *pfpix) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the fpix. */ + fpixChangeRefcount(fpix, -1); + if (fpixGetRefcount(fpix) <= 0) { + if ((data = fpixGetData(fpix)) != NULL) + LEPT_FREE(data); + LEPT_FREE(fpix); + } + + *pfpix = NULL; + return; +} + + +/*--------------------------------------------------------------------* + * FPix Accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixGetDimensions() + * + * \param[in] fpix + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +fpixGetDimensions(FPIX *fpix, + l_int32 *pw, + l_int32 *ph) +{ + PROCNAME("fpixGetDimensions"); + + if (!pw && !ph) + return ERROR_INT("no return val requested", procName, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + if (pw) *pw = fpix->w; + if (ph) *ph = fpix->h; + return 0; +} + + +/*! + * \brief fpixSetDimensions() + * + * \param[in] fpix + * \param[in] w, h + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetDimensions(FPIX *fpix, + l_int32 w, + l_int32 h) +{ + PROCNAME("fpixSetDimensions"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + fpix->w = w; + fpix->h = h; + return 0; +} + + +/*! + * \brief fpixGetWpl() + * + * \param[in] fpix + * \return wpl, or UNDEF on error + */ +l_int32 +fpixGetWpl(FPIX *fpix) +{ + PROCNAME("fpixGetWpl"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, UNDEF); + return fpix->wpl; +} + + +/*! + * \brief fpixSetWpl() + * + * \param[in] fpix + * \param[in] wpl + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetWpl(FPIX *fpix, + l_int32 wpl) +{ + PROCNAME("fpixSetWpl"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpix->wpl = wpl; + return 0; +} + + +/*! + * \brief fpixGetRefcount() + * + * \param[in] fpix + * \return refcount, or UNDEF on error + */ +l_int32 +fpixGetRefcount(FPIX *fpix) +{ + PROCNAME("fpixGetRefcount"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, UNDEF); + return fpix->refcount; +} + + +/*! + * \brief fpixChangeRefcount() + * + * \param[in] fpix + * \param[in] delta + * \return 0 if OK, 1 on error + */ +l_ok +fpixChangeRefcount(FPIX *fpix, + l_int32 delta) +{ + PROCNAME("fpixChangeRefcount"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpix->refcount += delta; + return 0; +} + + +/*! + * \brief fpixGetResolution() + * + * \param[in] fpix + * \param[out] pxres, pyres [optional] x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +fpixGetResolution(FPIX *fpix, + l_int32 *pxres, + l_int32 *pyres) +{ + PROCNAME("fpixGetResolution"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + if (pxres) *pxres = fpix->xres; + if (pyres) *pyres = fpix->yres; + return 0; +} + + +/*! + * \brief fpixSetResolution() + * + * \param[in] fpix + * \param[in] xres, yres x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetResolution(FPIX *fpix, + l_int32 xres, + l_int32 yres) +{ + PROCNAME("fpixSetResolution"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpix->xres = xres; + fpix->yres = yres; + return 0; +} + + +/*! + * \brief fpixCopyResolution() + * + * \param[in] fpixd, fpixs + * \return 0 if OK, 1 on error + */ +l_ok +fpixCopyResolution(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 xres, yres; + PROCNAME("fpixCopyResolution"); + + if (!fpixs || !fpixd) + return ERROR_INT("fpixs and fpixd not both defined", procName, 1); + + fpixGetResolution(fpixs, &xres, &yres); + fpixSetResolution(fpixd, xres, yres); + return 0; +} + + +/*! + * \brief fpixGetData() + * + * \param[in] fpix + * \return ptr to fpix data, or NULL on error + */ +l_float32 * +fpixGetData(FPIX *fpix) +{ + PROCNAME("fpixGetData"); + + if (!fpix) + return (l_float32 *)ERROR_PTR("fpix not defined", procName, NULL); + return fpix->data; +} + + +/*! + * \brief fpixSetData() + * + * \param[in] fpix + * \param[in] data + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetData(FPIX *fpix, + l_float32 *data) +{ + PROCNAME("fpixSetData"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpix->data = data; + return 0; +} + + +/*! + * \brief fpixGetPixel() + * + * \param[in] fpix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +fpixGetPixel(FPIX *fpix, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 w, h; + + PROCNAME("fpixGetPixel"); + + if (!pval) + return ERROR_INT("pval not defined", procName, 1); + *pval = 0.0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpixGetDimensions(fpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *pval = *(fpix->data + y * w + x); + return 0; +} + + +/*! + * \brief fpixSetPixel() + * + * \param[in] fpix + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +fpixSetPixel(FPIX *fpix, + l_int32 x, + l_int32 y, + l_float32 val) +{ +l_int32 w, h; + + PROCNAME("fpixSetPixel"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpixGetDimensions(fpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *(fpix->data + y * w + x) = val; + return 0; +} + + +/*--------------------------------------------------------------------* + * FPixa Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaCreate() + * + * \param[in] n initial number of ptrs + * \return fpixa, or NULL on error + */ +FPIXA * +fpixaCreate(l_int32 n) +{ +FPIXA *fpixa; + + PROCNAME("fpixaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + fpixa = (FPIXA *)LEPT_CALLOC(1, sizeof(FPIXA)); + fpixa->n = 0; + fpixa->nalloc = n; + fpixa->refcount = 1; + if ((fpixa->fpix = (FPIX **)LEPT_CALLOC(n, sizeof(FPIX *))) == NULL) { + fpixaDestroy(&fpixa); + return (FPIXA *)ERROR_PTR("fpixa ptrs not made", procName, NULL); + } + + return fpixa; +} + + +/*! + * \brief fpixaCopy() + * + * \param[in] fpixa + * \param[in] copyflag L_COPY, L_CLODE or L_COPY_CLONE + * \return new fpixa, or NULL on error + * + *
+ * Notes:
+ *      copyflag may be one of
+ *        ~ L_COPY makes a new fpixa and copies each fpix
+ *        ~ L_CLONE gives a new ref-counted handle to the input fpixa
+ *        ~ L_COPY_CLONE makes a new fpixa with clones of all fpix
+ * 
+ */ +FPIXA * +fpixaCopy(FPIXA *fpixa, + l_int32 copyflag) +{ +l_int32 i; +FPIX *fpixc; +FPIXA *fpixac; + + PROCNAME("fpixaCopy"); + + if (!fpixa) + return (FPIXA *)ERROR_PTR("fpixa not defined", procName, NULL); + + if (copyflag == L_CLONE) { + fpixaChangeRefcount(fpixa, 1); + return fpixa; + } + + if (copyflag != L_COPY && copyflag != L_COPY_CLONE) + return (FPIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + if ((fpixac = fpixaCreate(fpixa->n)) == NULL) + return (FPIXA *)ERROR_PTR("fpixac not made", procName, NULL); + for (i = 0; i < fpixa->n; i++) { + if (copyflag == L_COPY) + fpixc = fpixaGetFPix(fpixa, i, L_COPY); + else /* copy-clone */ + fpixc = fpixaGetFPix(fpixa, i, L_CLONE); + fpixaAddFPix(fpixac, fpixc, L_INSERT); + } + + return fpixac; +} + + +/*! + * \brief fpixaDestroy() + * + * \param[in,out] pfpixa will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the fpixa.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +fpixaDestroy(FPIXA **pfpixa) +{ +l_int32 i; +FPIXA *fpixa; + + PROCNAME("fpixaDestroy"); + + if (pfpixa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((fpixa = *pfpixa) == NULL) + return; + + /* Decrement the refcount. If it is 0, destroy the pixa. */ + fpixaChangeRefcount(fpixa, -1); + if (fpixa->refcount <= 0) { + for (i = 0; i < fpixa->n; i++) + fpixDestroy(&fpixa->fpix[i]); + LEPT_FREE(fpixa->fpix); + LEPT_FREE(fpixa); + } + + *pfpixa = NULL; + return; +} + + +/*--------------------------------------------------------------------* + * FPixa addition * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaAddFPix() + * + * \param[in] fpixa + * \param[in] fpix to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK; 1 on error + */ +l_ok +fpixaAddFPix(FPIXA *fpixa, + FPIX *fpix, + l_int32 copyflag) +{ +l_int32 n; +FPIX *fpixc; + + PROCNAME("fpixaAddFPix"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + if (copyflag == L_INSERT) + fpixc = fpix; + else if (copyflag == L_COPY) + fpixc = fpixCopy(NULL, fpix); + else if (copyflag == L_CLONE) + fpixc = fpixClone(fpix); + else + return ERROR_INT("invalid copyflag", procName, 1); + if (!fpixc) + return ERROR_INT("fpixc not made", procName, 1); + + n = fpixaGetCount(fpixa); + if (n >= fpixa->nalloc) + fpixaExtendArray(fpixa); + fpixa->fpix[n] = fpixc; + fpixa->n++; + + return 0; +} + + +/*! + * \brief fpixaExtendArray() + * + * \param[in] fpixa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Doubles the size of the fpixa ptr array.
+ * 
+ */ +static l_int32 +fpixaExtendArray(FPIXA *fpixa) +{ + PROCNAME("fpixaExtendArray"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + + return fpixaExtendArrayToSize(fpixa, 2 * fpixa->nalloc); +} + + +/*! + * \brief fpixaExtendArrayToSize() + * + * \param[in] fpixa + * \param[in] size new ptr array size + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If necessary, reallocs new fpixa ptrs array to %size.
+ * 
+ */ +static l_int32 +fpixaExtendArrayToSize(FPIXA *fpixa, + l_int32 size) +{ + PROCNAME("fpixaExtendArrayToSize"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + + if (size > fpixa->nalloc) { + if ((fpixa->fpix = (FPIX **)reallocNew((void **)&fpixa->fpix, + sizeof(FPIX *) * fpixa->nalloc, + size * sizeof(FPIX *))) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + fpixa->nalloc = size; + } + return 0; +} + + +/*--------------------------------------------------------------------* + * FPixa accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixaGetCount() + * + * \param[in] fpixa + * \return count, or 0 if no pixa + */ +l_int32 +fpixaGetCount(FPIXA *fpixa) +{ + PROCNAME("fpixaGetCount"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 0); + + return fpixa->n; +} + + +/*! + * \brief fpixaChangeRefcount() + * + * \param[in] fpixa + * \param[in] delta + * \return 0 if OK, 1 on error + */ +l_ok +fpixaChangeRefcount(FPIXA *fpixa, + l_int32 delta) +{ + PROCNAME("fpixaChangeRefcount"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + + fpixa->refcount += delta; + return 0; +} + + +/*! + * \brief fpixaGetFPix() + * + * \param[in] fpixa + * \param[in] index to the index-th fpix + * \param[in] accesstype L_COPY or L_CLONE + * \return fpix, or NULL on error + */ +FPIX * +fpixaGetFPix(FPIXA *fpixa, + l_int32 index, + l_int32 accesstype) +{ + PROCNAME("fpixaGetFPix"); + + if (!fpixa) + return (FPIX *)ERROR_PTR("fpixa not defined", procName, NULL); + if (index < 0 || index >= fpixa->n) + return (FPIX *)ERROR_PTR("index not valid", procName, NULL); + + if (accesstype == L_COPY) + return fpixCopy(NULL, fpixa->fpix[index]); + else if (accesstype == L_CLONE) + return fpixClone(fpixa->fpix[index]); + else + return (FPIX *)ERROR_PTR("invalid accesstype", procName, NULL); +} + + +/*! + * \brief fpixaGetFPixDimensions() + * + * \param[in] fpixa + * \param[in] index to the index-th box + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +fpixaGetFPixDimensions(FPIXA *fpixa, + l_int32 index, + l_int32 *pw, + l_int32 *ph) +{ +FPIX *fpix; + + PROCNAME("fpixaGetFPixDimensions"); + + if (!pw && !ph) + return ERROR_INT("no return val requested", procName, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + if (index < 0 || index >= fpixa->n) + return ERROR_INT("index not valid", procName, 1); + + if ((fpix = fpixaGetFPix(fpixa, index, L_CLONE)) == NULL) + return ERROR_INT("fpix not found!", procName, 1); + fpixGetDimensions(fpix, pw, ph); + fpixDestroy(&fpix); + return 0; +} + + +/*! + * \brief fpixaGetData() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \return data not a copy, or NULL on error + */ +l_float32 * +fpixaGetData(FPIXA *fpixa, + l_int32 index) +{ +l_int32 n; +l_float32 *data; +FPIX *fpix; + + PROCNAME("fpixaGetData"); + + if (!fpixa) + return (l_float32 *)ERROR_PTR("fpixa not defined", procName, NULL); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return (l_float32 *)ERROR_PTR("invalid index", procName, NULL); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + data = fpixGetData(fpix); + fpixDestroy(&fpix); + return data; +} + + +/*! + * \brief fpixaGetPixel() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 on error + */ +l_ok +fpixaGetPixel(FPIXA *fpixa, + l_int32 index, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 n, ret; +FPIX *fpix; + + PROCNAME("fpixaGetPixel"); + + if (!pval) + return ERROR_INT("pval not defined", procName, 1); + *pval = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index into fpixa", procName, 1); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + ret = fpixGetPixel(fpix, x, y, pval); + fpixDestroy(&fpix); + return ret; +} + + +/*! + * \brief fpixaSetPixel() + * + * \param[in] fpixa + * \param[in] index into fpixa array + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 on error + */ +l_ok +fpixaSetPixel(FPIXA *fpixa, + l_int32 index, + l_int32 x, + l_int32 y, + l_float32 val) +{ +l_int32 n, ret; +FPIX *fpix; + + PROCNAME("fpixaSetPixel"); + + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + n = fpixaGetCount(fpixa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index into fpixa", procName, 1); + + fpix = fpixaGetFPix(fpixa, index, L_CLONE); + ret = fpixSetPixel(fpix, x, y, val); + fpixDestroy(&fpix); + return ret; +} + + +/*--------------------------------------------------------------------* + * DPix Create/copy/destroy * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixCreate() + * + * \param[in] width, height + * \return dpix with data allocated and initialized to 0, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a DPix of specified size, with the data array
+ *          allocated and initialized to 0.
+ *      (2) The number of pixels must be less than 2^28.
+ * 
+ */ +DPIX * +dpixCreate(l_int32 width, + l_int32 height) +{ +l_float64 *data; +l_uint64 npix64; +DPIX *dpix; + + PROCNAME("dpixCreate"); + + if (width <= 0) + return (DPIX *)ERROR_PTR("width must be > 0", procName, NULL); + if (height <= 0) + return (DPIX *)ERROR_PTR("height must be > 0", procName, NULL); + + /* Avoid overflow in malloc arg, malicious or otherwise */ + npix64 = (l_uint64)width * (l_uint64)height; /* # of 8 byte pixels */ + if (npix64 >= (1LL << 28)) { + L_ERROR("requested w = %d, h = %d\n", procName, width, height); + return (DPIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); + } + + dpix = (DPIX *)LEPT_CALLOC(1, sizeof(DPIX)); + dpixSetDimensions(dpix, width, height); + dpixSetWpl(dpix, width); /* 8 byte words */ + dpix->refcount = 1; + + data = (l_float64 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float64)); + if (!data) { + dpixDestroy(&dpix); + return (DPIX *)ERROR_PTR("calloc fail for data", procName, NULL); + } + dpixSetData(dpix, data); + return dpix; +} + + +/*! + * \brief dpixCreateTemplate() + * + * \param[in] dpixs + * \return dpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a DPix of the same size as the input DPix, with the
+ *          data array allocated and initialized to 0.
+ *      (2) Copies the resolution.
+ * 
+ */ +DPIX * +dpixCreateTemplate(DPIX *dpixs) +{ +l_int32 w, h; +DPIX *dpixd; + + PROCNAME("dpixCreateTemplate"); + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); + + dpixGetDimensions(dpixs, &w, &h); + dpixd = dpixCreate(w, h); + dpixCopyResolution(dpixd, dpixs); + return dpixd; +} + + +/*! + * \brief dpixClone() + * + * \param[in] dpix + * \return same dpix ptr, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixClone() for definition and usage.
+ * 
+ */ +DPIX * +dpixClone(DPIX *dpix) +{ + PROCNAME("dpixClone"); + + if (!dpix) + return (DPIX *)ERROR_PTR("dpix not defined", procName, NULL); + dpixChangeRefcount(dpix, 1); + + return dpix; +} + + +/*! + * \brief dpixCopy() + * + * \param[in] dpixd [optional] can be null, or equal to dpixs, + * or different from dpixs + * \param[in] dpixs + * \return dpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) There are three cases:
+ *            (a) dpixd == null  (makes a new dpix; refcount = 1)
+ *            (b) dpixd == dpixs  (no-op)
+ *            (c) dpixd != dpixs  (data copy; no change in refcount)
+ *          If the refcount of dpixd > 1, case (c) will side-effect
+ *          these handles.
+ *      (2) The general pattern of use is:
+ *             dpixd = dpixCopy(dpixd, dpixs);
+ *          This will work for all three cases.
+ *          For clarity when the case is known, you can use:
+ *            (a) dpixd = dpixCopy(NULL, dpixs);
+ *            (c) dpixCopy(dpixd, dpixs);
+ *      (3) For case (c), we check if dpixs and dpixd are the same size.
+ *          If so, the data is copied directly.
+ *          Otherwise, the data is reallocated to the correct size
+ *          and the copy proceeds.  The refcount of dpixd is unchanged.
+ *      (4) This operation, like all others that may involve a pre-existing
+ *          dpixd, will side-effect any existing clones of dpixd.
+ * 
+ */ +DPIX * +dpixCopy(DPIX *dpixd, /* can be null */ + DPIX *dpixs) +{ +l_int32 w, h, bytes; +l_float64 *datas, *datad; + + PROCNAME("dpixCopy"); + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); + if (dpixs == dpixd) + return dpixd; + + /* Total bytes in image data */ + dpixGetDimensions(dpixs, &w, &h); + bytes = 8 * w * h; + + /* If we're making a new dpix ... */ + if (!dpixd) { + if ((dpixd = dpixCreateTemplate(dpixs)) == NULL) + return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL); + datas = dpixGetData(dpixs); + datad = dpixGetData(dpixd); + memcpy(datad, datas, bytes); + return dpixd; + } + + /* Reallocate image data if sizes are different */ + dpixResizeImageData(dpixd, dpixs); + + /* Copy data */ + dpixCopyResolution(dpixd, dpixs); + datas = dpixGetData(dpixs); + datad = dpixGetData(dpixd); + memcpy(datad, datas, bytes); + return dpixd; +} + + +/*! + * \brief dpixResizeImageData() + * + * \param[in] dpixd, dpixs + * \return 0 if OK, 1 on error + */ +l_ok +dpixResizeImageData(DPIX *dpixd, + DPIX *dpixs) +{ +l_int32 ws, hs, wd, hd, bytes; +l_float64 *data; + + PROCNAME("dpixResizeImageData"); + + if (!dpixs) + return ERROR_INT("dpixs not defined", procName, 1); + if (!dpixd) + return ERROR_INT("dpixd not defined", procName, 1); + + dpixGetDimensions(dpixs, &ws, &hs); + dpixGetDimensions(dpixd, &wd, &hd); + if (ws == wd && hs == hd) /* nothing to do */ + return 0; + + dpixSetDimensions(dpixd, ws, hs); + dpixSetWpl(dpixd, ws); /* 8 byte words */ + bytes = 8 * ws * hs; + data = dpixGetData(dpixd); + if (data) LEPT_FREE(data); + if ((data = (l_float64 *)LEPT_MALLOC(bytes)) == NULL) + return ERROR_INT("LEPT_MALLOC fail for data", procName, 1); + dpixSetData(dpixd, data); + return 0; +} + + +/*! + * \brief dpixDestroy() + * + * \param[in,out] pdpix will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the dpix.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +dpixDestroy(DPIX **pdpix) +{ +l_float64 *data; +DPIX *dpix; + + PROCNAME("dpixDestroy"); + + if (!pdpix) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((dpix = *pdpix) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the dpix. */ + dpixChangeRefcount(dpix, -1); + if (dpixGetRefcount(dpix) <= 0) { + if ((data = dpixGetData(dpix)) != NULL) + LEPT_FREE(data); + LEPT_FREE(dpix); + } + + *pdpix = NULL; + return; +} + + +/*--------------------------------------------------------------------* + * DPix Accessors * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixGetDimensions() + * + * \param[in] dpix + * \param[out] pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +dpixGetDimensions(DPIX *dpix, + l_int32 *pw, + l_int32 *ph) +{ + PROCNAME("dpixGetDimensions"); + + if (!pw && !ph) + return ERROR_INT("no return val requested", procName, 1); + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + if (pw) *pw = dpix->w; + if (ph) *ph = dpix->h; + return 0; +} + + +/*! + * \brief dpixSetDimensions() + * + * \param[in] dpix + * \param[in] w, h + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetDimensions(DPIX *dpix, + l_int32 w, + l_int32 h) +{ + PROCNAME("dpixSetDimensions"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + dpix->w = w; + dpix->h = h; + return 0; +} + + +/*! + * \brief dpixGetWpl() + * + * \param[in] dpix + * \return wpl, or UNDEF on error + */ +l_int32 +dpixGetWpl(DPIX *dpix) +{ + PROCNAME("dpixGetWpl"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + return dpix->wpl; +} + + +/*! + * \brief dpixSetWpl() + * + * \param[in] dpix + * \param[in] wpl + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetWpl(DPIX *dpix, + l_int32 wpl) +{ + PROCNAME("dpixSetWpl"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpix->wpl = wpl; + return 0; +} + + +/*! + * \brief dpixGetRefcount() + * + * \param[in] dpix + * \return refcount, or UNDEF on error + */ +l_int32 +dpixGetRefcount(DPIX *dpix) +{ + PROCNAME("dpixGetRefcount"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, UNDEF); + return dpix->refcount; +} + + +/*! + * \brief dpixChangeRefcount() + * + * \param[in] dpix + * \param[in] delta + * \return 0 if OK, 1 on error + */ +l_ok +dpixChangeRefcount(DPIX *dpix, + l_int32 delta) +{ + PROCNAME("dpixChangeRefcount"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpix->refcount += delta; + return 0; +} + + +/*! + * \brief dpixGetResolution() + * + * \param[in] dpix + * \param[out] pxres, pyres [optional] x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +dpixGetResolution(DPIX *dpix, + l_int32 *pxres, + l_int32 *pyres) +{ + PROCNAME("dpixGetResolution"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + if (pxres) *pxres = dpix->xres; + if (pyres) *pyres = dpix->yres; + return 0; +} + + +/*! + * \brief dpixSetResolution() + * + * \param[in] dpix + * \param[in] xres, yres x and y resolution + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetResolution(DPIX *dpix, + l_int32 xres, + l_int32 yres) +{ + PROCNAME("dpixSetResolution"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpix->xres = xres; + dpix->yres = yres; + return 0; +} + + +/*! + * \brief dpixCopyResolution() + * + * \param[in] dpixd, dpixs + * \return 0 if OK, 1 on error + */ +l_ok +dpixCopyResolution(DPIX *dpixd, + DPIX *dpixs) +{ +l_int32 xres, yres; + PROCNAME("dpixCopyResolution"); + + if (!dpixs || !dpixd) + return ERROR_INT("dpixs and dpixd not both defined", procName, 1); + + dpixGetResolution(dpixs, &xres, &yres); + dpixSetResolution(dpixd, xres, yres); + return 0; +} + + +/*! + * \brief dpixGetData() + * + * \param[in] dpix + * \return ptr to dpix data, or NULL on error + */ +l_float64 * +dpixGetData(DPIX *dpix) +{ + PROCNAME("dpixGetData"); + + if (!dpix) + return (l_float64 *)ERROR_PTR("dpix not defined", procName, NULL); + return dpix->data; +} + + +/*! + * \brief dpixSetData() + * + * \param[in] dpix + * \param[in] data + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetData(DPIX *dpix, + l_float64 *data) +{ + PROCNAME("dpixSetData"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpix->data = data; + return 0; +} + + +/*! + * \brief dpixGetPixel() + * + * \param[in] dpix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +dpixGetPixel(DPIX *dpix, + l_int32 x, + l_int32 y, + l_float64 *pval) +{ +l_int32 w, h; + + PROCNAME("dpixGetPixel"); + + if (!pval) + return ERROR_INT("pval not defined", procName, 1); + *pval = 0.0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpixGetDimensions(dpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *pval = *(dpix->data + y * w + x); + return 0; +} + + +/*! + * \brief dpixSetPixel() + * + * \param[in] dpix + * \param[in] x,y pixel coords + * \param[in] val pixel value + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0.0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +dpixSetPixel(DPIX *dpix, + l_int32 x, + l_int32 y, + l_float64 val) +{ +l_int32 w, h; + + PROCNAME("dpixSetPixel"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpixGetDimensions(dpix, &w, &h); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + *(dpix->data + y * w + x) = val; + return 0; +} + + +/*--------------------------------------------------------------------* + * FPix serialized I/O * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRead() + * + * \param[in] filename + * \return fpix, or NULL on error + */ +FPIX * +fpixRead(const char *filename) +{ +FILE *fp; +FPIX *fpix; + + PROCNAME("fpixRead"); + + if (!filename) + return (FPIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (FPIX *)ERROR_PTR("stream not opened", procName, NULL); + fpix = fpixReadStream(fp); + fclose(fp); + if (!fpix) + return (FPIX *)ERROR_PTR("fpix not read", procName, NULL); + return fpix; +} + + +/*! + * \brief fpixReadStream() + * + * \param[in] fp file stream + * \return fpix, or NULL on error + */ +FPIX * +fpixReadStream(FILE *fp) +{ +char buf[256]; +l_int32 w, h, nbytes, xres, yres, version; +l_float32 *data; +FPIX *fpix; + + PROCNAME("fpixReadStream"); + + if (!fp) + return (FPIX *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nFPix Version %d\n", &version) != 1) + return (FPIX *)ERROR_PTR("not a fpix file", procName, NULL); + if (version != FPIX_VERSION_NUMBER) + return (FPIX *)ERROR_PTR("invalid fpix version", procName, NULL); + if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) + return (FPIX *)ERROR_PTR("read fail for data size", procName, NULL); + + /* Use fgets() and sscanf(); not fscanf(), for the last + * bit of header data before the float data. The reason is + * that fscanf throws away white space, and if the float data + * happens to begin with ascii character(s) that are white + * space, it will swallow them and all will be lost! */ + if (fgets(buf, sizeof(buf), fp) == NULL) + return (FPIX *)ERROR_PTR("fgets read fail", procName, NULL); + if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) + return (FPIX *)ERROR_PTR("read fail for xres, yres", procName, NULL); + + if ((fpix = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpix not made", procName, NULL); + fpixSetResolution(fpix, xres, yres); + data = fpixGetData(fpix); + if (fread(data, 1, nbytes, fp) != nbytes) { + fpixDestroy(&fpix); + return (FPIX *)ERROR_PTR("read error for nbytes", procName, NULL); + } + fgetc(fp); /* ending nl */ + + /* Convert to little-endian if necessary */ + fpixEndianByteSwap(fpix, fpix); + return fpix; +} + + +/*! + * \brief fpixReadMem() + * + * \param[in] data of serialized fpix + * \param[in] size of data in bytes + * \return fpix, or NULL on error + */ +FPIX * +fpixReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +FPIX *fpix; + + PROCNAME("fpixReadMem"); + + if (!data) + return (FPIX *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (FPIX *)ERROR_PTR("stream not opened", procName, NULL); + + fpix = fpixReadStream(fp); + fclose(fp); + if (!fpix) L_ERROR("fpix not read\n", procName); + return fpix; +} + + +/*! + * \brief fpixWrite() + * + * \param[in] filename + * \param[in] fpix + * \return 0 if OK, 1 on error + */ +l_ok +fpixWrite(const char *filename, + FPIX *fpix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("fpixWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = fpixWriteStream(fp, fpix); + fclose(fp); + if (ret) + return ERROR_INT("fpix not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief fpixWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] fpix + * \return 0 if OK, 1 on error + */ +l_ok +fpixWriteStream(FILE *fp, + FPIX *fpix) +{ +l_int32 w, h, xres, yres; +l_uint32 nbytes; +l_float32 *data; +FPIX *fpixt; + + PROCNAME("fpixWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + /* Convert to little-endian if necessary */ + fpixt = fpixEndianByteSwap(NULL, fpix); + + fpixGetDimensions(fpixt, &w, &h); + data = fpixGetData(fpixt); + nbytes = sizeof(l_float32) * w * h; + fpixGetResolution(fpixt, &xres, &yres); + fprintf(fp, "\nFPix Version %d\n", FPIX_VERSION_NUMBER); + fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); + fprintf(fp, "xres = %d, yres = %d\n", xres, yres); + fwrite(data, 1, nbytes, fp); + fprintf(fp, "\n"); + + fpixDestroy(&fpixt); + return 0; +} + + +/*! + * \brief fpixWriteMem() + * + * \param[out] pdata data of serialized fpix + * \param[out] psize size of returned data + * \param[in] fpix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a fpix in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +fpixWriteMem(l_uint8 **pdata, + size_t *psize, + FPIX *fpix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("fpixWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = fpixWriteStream(fp, fpix); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = fpixWriteStream(fp, fpix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief fpixEndianByteSwap() + * + * \param[in] fpixd can be equal to fpixs or NULL + * \param[in] fpixs + * \return fpixd always + * + *
+ * Notes:
+ *      (1) On big-endian hardware, this does byte-swapping on each of
+ *          the 4-byte floats in the fpix data.  On little-endians,
+ *          the data is unchanged.  This is used for serialization
+ *          of fpix; the data is serialized in little-endian byte
+ *          order because most hardware is little-endian.
+ *      (2) The operation can be either in-place or, if fpixd == NULL,
+ *          a new fpix is made.  If not in-place, caller must catch
+ *          the returned pointer.
+ * 
+ */ +FPIX * +fpixEndianByteSwap(FPIX *fpixd, + FPIX *fpixs) +{ + PROCNAME("fpixEndianByteSwap"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, fpixd); + if (fpixd && (fpixs != fpixd)) + return (FPIX *)ERROR_PTR("fpixd != fpixs", procName, fpixd); + +#ifdef L_BIG_ENDIAN + { + l_uint32 *data; + l_int32 i, j, w, h; + l_uint32 word; + + fpixGetDimensions(fpixs, &w, &h); + fpixd = fpixCopy(fpixd, fpixs); /* no copy if fpixd == fpixs */ + + data = (l_uint32 *)fpixGetData(fpixd); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + return fpixd; + } +#else /* L_LITTLE_ENDIAN */ + + if (fpixd) + return fpixd; /* no-op */ + else + return fpixClone(fpixs); + +#endif /* L_BIG_ENDIAN */ +} + + +/*--------------------------------------------------------------------* + * DPix serialized I/O * + *--------------------------------------------------------------------*/ +/*! + * \brief dpixRead() + * + * \param[in] filename + * \return dpix, or NULL on error + */ +DPIX * +dpixRead(const char *filename) +{ +FILE *fp; +DPIX *dpix; + + PROCNAME("dpixRead"); + + if (!filename) + return (DPIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (DPIX *)ERROR_PTR("stream not opened", procName, NULL); + dpix = dpixReadStream(fp); + fclose(fp); + if (!dpix) + return (DPIX *)ERROR_PTR("dpix not read", procName, NULL); + return dpix; +} + + +/*! + * \brief dpixReadStream() + * + * \param[in] fp file stream + * \return dpix, or NULL on error + */ +DPIX * +dpixReadStream(FILE *fp) +{ +char buf[256]; +l_int32 w, h, nbytes, version, xres, yres; +l_float64 *data; +DPIX *dpix; + + PROCNAME("dpixReadStream"); + + if (!fp) + return (DPIX *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nDPix Version %d\n", &version) != 1) + return (DPIX *)ERROR_PTR("not a dpix file", procName, NULL); + if (version != DPIX_VERSION_NUMBER) + return (DPIX *)ERROR_PTR("invalid dpix version", procName, NULL); + if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) + return (DPIX *)ERROR_PTR("read fail for data size", procName, NULL); + + /* Use fgets() and sscanf(); not fscanf(), for the last + * bit of header data before the float data. The reason is + * that fscanf throws away white space, and if the float data + * happens to begin with ascii character(s) that are white + * space, it will swallow them and all will be lost! */ + if (fgets(buf, sizeof(buf), fp) == NULL) + return (DPIX *)ERROR_PTR("fgets read fail", procName, NULL); + if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) + return (DPIX *)ERROR_PTR("read fail for xres, yres", procName, NULL); + + if ((dpix = dpixCreate(w, h)) == NULL) + return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); + dpixSetResolution(dpix, xres, yres); + data = dpixGetData(dpix); + if (fread(data, 1, nbytes, fp) != nbytes) { + dpixDestroy(&dpix); + return (DPIX *)ERROR_PTR("read error for nbytes", procName, NULL); + } + fgetc(fp); /* ending nl */ + + /* Convert to little-endian if necessary */ + dpixEndianByteSwap(dpix, dpix); + return dpix; +} + + +/*! + * \brief dpixReadMem() + * + * \param[in] data of serialized dpix + * \param[in] size of data in bytes + * \return dpix, or NULL on error + */ +DPIX * +dpixReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +DPIX *dpix; + + PROCNAME("dpixReadMem"); + + if (!data) + return (DPIX *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (DPIX *)ERROR_PTR("stream not opened", procName, NULL); + + dpix = dpixReadStream(fp); + fclose(fp); + if (!dpix) L_ERROR("dpix not read\n", procName); + return dpix; +} + + +/*! + * \brief dpixWrite() + * + * \param[in] filename + * \param[in] dpix + * \return 0 if OK, 1 on error + */ +l_ok +dpixWrite(const char *filename, + DPIX *dpix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dpixWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dpixWriteStream(fp, dpix); + fclose(fp); + if (ret) + return ERROR_INT("dpix not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief dpixWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] dpix + * \return 0 if OK, 1 on error + */ +l_ok +dpixWriteStream(FILE *fp, + DPIX *dpix) +{ +l_int32 w, h, xres, yres; +l_uint32 nbytes; +l_float64 *data; +DPIX *dpixt; + + PROCNAME("dpixWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + /* Convert to little-endian if necessary */ + dpixt = dpixEndianByteSwap(NULL, dpix); + + dpixGetDimensions(dpixt, &w, &h); + dpixGetResolution(dpixt, &xres, &yres); + data = dpixGetData(dpixt); + nbytes = sizeof(l_float64) * w * h; + fprintf(fp, "\nDPix Version %d\n", DPIX_VERSION_NUMBER); + fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); + fprintf(fp, "xres = %d, yres = %d\n", xres, yres); + fwrite(data, 1, nbytes, fp); + fprintf(fp, "\n"); + + dpixDestroy(&dpixt); + return 0; +} + + +/*! + * \brief dpixWriteMem() + * + * \param[out] pdata data of serialized dpix + * \param[out] psize size of returned data + * \param[in] dpix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a dpix in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +dpixWriteMem(l_uint8 **pdata, + size_t *psize, + DPIX *dpix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("dpixWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = dpixWriteStream(fp, dpix); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = dpixWriteStream(fp, dpix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief dpixEndianByteSwap() + * + * \param[in] dpixd can be equal to dpixs or NULL + * \param[in] dpixs + * \return dpixd always + * + *
+ * Notes:
+ *      (1) On big-endian hardware, this does byte-swapping on each of
+ *          the 4-byte words in the dpix data.  On little-endians,
+ *          the data is unchanged.  This is used for serialization
+ *          of dpix; the data is serialized in little-endian byte
+ *          order because most hardware is little-endian.
+ *      (2) The operation can be either in-place or, if dpixd == NULL,
+ *          a new dpix is made.  If not in-place, caller must catch
+ *          the returned pointer.
+ * 
+ */ +DPIX * +dpixEndianByteSwap(DPIX *dpixd, + DPIX *dpixs) +{ + PROCNAME("dpixEndianByteSwap"); + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", procName, dpixd); + if (dpixd && (dpixs != dpixd)) + return (DPIX *)ERROR_PTR("dpixd != dpixs", procName, dpixd); + +#ifdef L_BIG_ENDIAN + { + l_uint32 *data; + l_int32 i, j, w, h; + l_uint32 word; + + dpixGetDimensions(dpixs, &w, &h); + dpixd = dpixCopy(dpixd, dpixs); /* no copy if dpixd == dpixs */ + + data = (l_uint32 *)dpixGetData(dpixd); + for (i = 0; i < h; i++) { + for (j = 0; j < 2 * w; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + return dpixd; + } +#else /* L_LITTLE_ENDIAN */ + + if (dpixd) + return dpixd; /* no-op */ + else + return dpixClone(dpixs); + +#endif /* L_BIG_ENDIAN */ +} + + +/*--------------------------------------------------------------------* + * Print FPix (subsampled, for debugging) * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixPrintStream() + * + * \param[in] fp file stream + * \param[in] fpix + * \param[in] factor for subsampling + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Subsampled printout of fpix for debugging.
+ * 
+ */ +l_ok +fpixPrintStream(FILE *fp, + FPIX *fpix, + l_int32 factor) +{ +l_int32 i, j, w, h, count; +l_float32 val; + + PROCNAME("fpixPrintStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor < 1f", procName, 1); + + fpixGetDimensions(fpix, &w, &h); + fprintf(fp, "\nFPix: w = %d, h = %d\n", w, h); + for (i = 0; i < h; i += factor) { + for (count = 0, j = 0; j < w; j += factor, count++) { + fpixGetPixel(fpix, j, i, &val); + fprintf(fp, "val[%d, %d] = %f ", i, j, val); + if ((count + 1) % 3 == 0) fprintf(fp, "\n"); + } + if (count % 3) fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/fpix2.c b/hgdriver/3rdparty/hgOCR/leptonica/fpix2.c new file mode 100644 index 0000000..47f9e6c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/fpix2.c @@ -0,0 +1,2467 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file fpix2.c + *
+ *
+ *    ------------------------------------------
+ *    This file has these FPix utilities:
+ *       ~ interconversions with pix, fpix, dpix
+ *       ~ min and max values
+ *       ~ integer scaling
+ *       ~ arithmetic operations
+ *       ~ set all
+ *       ~ border functions
+ *       ~ simple rasterop (source --> dest)
+ *       ~ geometric transforms
+ *    ------------------------------------------
+ *
+ *    Interconversions between Pix, FPix and DPix
+ *          FPIX          *pixConvertToFPix()
+ *          DPIX          *pixConvertToDPix()
+ *          PIX           *fpixConvertToPix()
+ *          PIX           *fpixDisplayMaxDynamicRange()  [useful for debugging]
+ *          DPIX          *fpixConvertToDPix()
+ *          PIX           *dpixConvertToPix()
+ *          FPIX          *dpixConvertToFPix()
+ *
+ *    Min/max value
+ *          l_int32        fpixGetMin()
+ *          l_int32        fpixGetMax()
+ *          l_int32        dpixGetMin()
+ *          l_int32        dpixGetMax()
+ *
+ *    Integer scaling
+ *          FPIX          *fpixScaleByInteger()
+ *          DPIX          *dpixScaleByInteger()
+ *
+ *    Arithmetic operations
+ *          FPIX          *fpixLinearCombination()
+ *          l_int32        fpixAddMultConstant()
+ *          DPIX          *dpixLinearCombination()
+ *          l_int32        dpixAddMultConstant()
+ *
+ *    Set all
+ *          l_int32        fpixSetAllArbitrary()
+ *          l_int32        dpixSetAllArbitrary()
+ *
+ *    FPix border functions
+ *          FPIX          *fpixAddBorder()
+ *          FPIX          *fpixRemoveBorder()
+ *          FPIX          *fpixAddMirroredBorder()
+ *          FPIX          *fpixAddContinuedBorder()
+ *          FPIX          *fpixAddSlopeBorder()
+ *
+ *    FPix simple rasterop
+ *          l_int32        fpixRasterop()
+ *
+ *    FPix rotation by multiples of 90 degrees
+ *          FPIX          *fpixRotateOrth()
+ *          FPIX          *fpixRotate180()
+ *          FPIX          *fpixRotate90()
+ *          FPIX          *fpixFlipLR()
+ *          FPIX          *fpixFlipTB()
+ *
+ *    FPix affine and projective interpolated transforms
+ *          FPIX          *fpixAffinePta()
+ *          FPIX          *fpixAffine()
+ *          FPIX          *fpixProjectivePta()
+ *          FPIX          *fpixProjective()
+ *          l_int32        linearInterpolatePixelFloat()
+ *
+ *    Thresholding to 1 bpp Pix
+ *          PIX           *fpixThresholdToPix()
+ *
+ *    Generate function from components
+ *          FPIX          *pixComponentFunction()
+ * 
+ */ + +#include +#include "allheaders.h" + +/*--------------------------------------------------------------------* + * FPix <--> Pix conversions * + *--------------------------------------------------------------------*/ +/*! + * \brief pixConvertToFPix() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] ncomps number of components: 3 for RGB, 1 otherwise + * \return fpix, or NULL on error + * + *
+ * Notes:
+ *      (1) If colormapped, remove to grayscale.
+ *      (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
+ *          In all other cases the src image is treated as having a single
+ *          component of pixel values.
+ * 
+ */ +FPIX * +pixConvertToFPix(PIX *pixs, + l_int32 ncomps) +{ +l_int32 w, h, d, i, j, val, wplt, wpld; +l_uint32 uval; +l_uint32 *datat, *linet; +l_float32 *datad, *lined; +PIX *pixt; +FPIX *fpixd; + + PROCNAME("pixConvertToFPix"); + + if (!pixs) + return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert to a single component */ + if (pixGetColormap(pixs)) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else if (pixGetDepth(pixs) == 32 && ncomps == 3) + pixt = pixConvertRGBToLuminance(pixs); + else + pixt = pixClone(pixs); + pixGetDimensions(pixt, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { + pixDestroy(&pixt); + return (FPIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + if ((fpixd = fpixCreate(w, h)) == NULL) { + pixDestroy(&pixt); + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + if (d == 1) { + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 2) { + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 4) { + for (j = 0; j < w; j++) { + val = GET_DATA_QBIT(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 8) { + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + lined[j] = (l_float32)val; + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(linet, j); + lined[j] = (l_float32)val; + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + uval = GET_DATA_FOUR_BYTES(linet, j); + lined[j] = (l_float32)uval; + } + } + } + + pixDestroy(&pixt); + return fpixd; +} + + +/*! + * \brief pixConvertToDPix() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] ncomps number of components: 3 for RGB, 1 otherwise + * \return dpix, or NULL on error + * + *
+ * Notes:
+ *      (1) If colormapped, remove to grayscale.
+ *      (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
+ *          In all other cases the src image is treated as having a single
+ *          component of pixel values.
+ * 
+ */ +DPIX * +pixConvertToDPix(PIX *pixs, + l_int32 ncomps) +{ +l_int32 w, h, d, i, j, val, wplt, wpld; +l_uint32 uval; +l_uint32 *datat, *linet; +l_float64 *datad, *lined; +PIX *pixt; +DPIX *dpixd; + + PROCNAME("pixConvertToDPix"); + + if (!pixs) + return (DPIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert to a single component */ + if (pixGetColormap(pixs)) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else if (pixGetDepth(pixs) == 32 && ncomps == 3) + pixt = pixConvertRGBToLuminance(pixs); + else + pixt = pixClone(pixs); + pixGetDimensions(pixt, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { + pixDestroy(&pixt); + return (DPIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + if ((dpixd = dpixCreate(w, h)) == NULL) { + pixDestroy(&pixt); + return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = dpixGetData(dpixd); + wpld = dpixGetWpl(dpixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + if (d == 1) { + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 2) { + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 4) { + for (j = 0; j < w; j++) { + val = GET_DATA_QBIT(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 8) { + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + lined[j] = (l_float64)val; + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(linet, j); + lined[j] = (l_float64)val; + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + uval = GET_DATA_FOUR_BYTES(linet, j); + lined[j] = (l_float64)uval; + } + } + } + + pixDestroy(&pixt); + return dpixd; +} + + +/*! + * \brief fpixConvertToPix() + * + * \param[in] fpixs + * \param[in] outdepth 0, 8, 16 or 32 bpp + * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL + * \param[in] errorflag 1 to output error stats; 0 otherwise + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %outdepth = 0 to programmatically determine the
+ *          output depth.  If no values are greater than 255,
+ *          it will set outdepth = 8; otherwise to 16 or 32.
+ *      (2) Because we are converting a float to an unsigned int
+ *          with a specified dynamic range (8, 16 or 32 bits), errors
+ *          can occur.  If errorflag == TRUE, output the number
+ *          of values out of range, both negative and positive.
+ *      (3) If a pixel value is positive and out of range, clip to
+ *          the maximum value represented at the outdepth of 8, 16
+ *          or 32 bits.
+ * 
+ */ +PIX * +fpixConvertToPix(FPIX *fpixs, + l_int32 outdepth, + l_int32 negvals, + l_int32 errorflag) +{ +l_int32 w, h, i, j, wpls, wpld; +l_uint32 vald, maxval; +l_float32 val; +l_float32 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("fpixConvertToPix"); + + if (!fpixs) + return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) + return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); + if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + + /* Adaptive determination of output depth */ + if (outdepth == 0) { + outdepth = 8; + for (i = 0; i < h && outdepth < 32; i++) { + lines = datas + i * wpls; + for (j = 0; j < w && outdepth < 32; j++) { + if (lines[j] > 65535.5) + outdepth = 32; + else if (lines[j] > 255.5) + outdepth = 16; + } + } + } + if (outdepth == 8) + maxval = 0xff; + else if (outdepth == 16) + maxval = 0xffff; + else /* outdepth == 32 */ + maxval = 0xffffffff; + + /* Gather statistics if %errorflag = TRUE */ + if (errorflag) { + l_int32 negs = 0; + l_int32 overvals = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val < 0.0) + negs++; + else if (val > maxval) + overvals++; + } + } + if (negs > 0) + L_ERROR("Number of negative values: %d\n", procName, negs); + if (overvals > 0) + L_ERROR("Number of too-large values: %d\n", procName, overvals); + } + + /* Make the pix and convert the data */ + if ((pixd = pixCreate(w, h, outdepth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val >= 0.0) + vald = (l_uint32)(val + 0.5); + else if (negvals == L_CLIP_TO_ZERO) /* and val < 0.0 */ + vald = 0; + else + vald = (l_uint32)(-val + 0.5); + if (vald > maxval) + vald = maxval; + + if (outdepth == 8) + SET_DATA_BYTE(lined, j, vald); + else if (outdepth == 16) + SET_DATA_TWO_BYTES(lined, j, vald); + else /* outdepth == 32 */ + SET_DATA_FOUR_BYTES(lined, j, vald); + } + } + + return pixd; +} + + +/*! + * \brief fpixDisplayMaxDynamicRange() + * + * \param[in] fpixs + * \return pixd 8 bpp, or NULL on error + */ +PIX * +fpixDisplayMaxDynamicRange(FPIX *fpixs) +{ +l_uint8 dval; +l_int32 i, j, w, h, wpls, wpld; +l_float32 factor, sval, maxval; +l_float32 *lines, *datas; +l_uint32 *lined, *datad; +PIX *pixd; + + PROCNAME("fpixDisplayMaxDynamicRange"); + + if (!fpixs) + return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + + maxval = 0.0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + sval = *(lines + j); + if (sval > maxval) + maxval = sval; + } + } + + pixd = pixCreate(w, h, 8); + if (maxval == 0.0) + return pixd; /* all pixels are 0 */ + + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + factor = 255. / maxval; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = *(lines + j); + if (sval < 0.0) sval = 0.0; + dval = (l_uint8)(factor * sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + + return pixd; +} + + +/*! + * \brief fpixConvertToDPix() + * + * \param[in] fpix + * \return dpix, or NULL on error + */ +DPIX * +fpixConvertToDPix(FPIX *fpix) +{ +l_int32 w, h, i, j, wpls, wpld; +l_float32 val; +l_float32 *datas, *lines; +l_float64 *datad, *lined; +DPIX *dpix; + + PROCNAME("fpixConvertToDPix"); + + if (!fpix) + return (DPIX *)ERROR_PTR("fpix not defined", procName, NULL); + + fpixGetDimensions(fpix, &w, &h); + if ((dpix = dpixCreate(w, h)) == NULL) + return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); + + datas = fpixGetData(fpix); + datad = dpixGetData(dpix); + wpls = fpixGetWpl(fpix); + wpld = dpixGetWpl(dpix); /* 8 byte words */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + lined[j] = val; + } + } + + return dpix; +} + + +/*! + * \brief dpixConvertToPix() + * + * \param[in] dpixs + * \param[in] outdepth 0, 8, 16 or 32 bpp + * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL + * \param[in] errorflag 1 to output error stats; 0 otherwise + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %outdepth = 0 to programmatically determine the
+ *          output depth.  If no values are greater than 255,
+ *          it will set outdepth = 8; otherwise to 16 or 32.
+ *      (2) Because we are converting a float to an unsigned int
+ *          with a specified dynamic range (8, 16 or 32 bits), errors
+ *          can occur.  If errorflag == TRUE, output the number
+ *          of values out of range, both negative and positive.
+ *      (3) If a pixel value is positive and out of range, clip to
+ *          the maximum value represented at the outdepth of 8, 16
+ *          or 32 bits.
+ * 
+ */ +PIX * +dpixConvertToPix(DPIX *dpixs, + l_int32 outdepth, + l_int32 negvals, + l_int32 errorflag) +{ +l_int32 w, h, i, j, wpls, wpld, maxval; +l_uint32 vald; +l_float64 val; +l_float64 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("dpixConvertToPix"); + + if (!dpixs) + return (PIX *)ERROR_PTR("dpixs not defined", procName, NULL); + if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) + return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); + if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); + + dpixGetDimensions(dpixs, &w, &h); + datas = dpixGetData(dpixs); + wpls = dpixGetWpl(dpixs); + + /* Adaptive determination of output depth */ + if (outdepth == 0) { + outdepth = 8; + for (i = 0; i < h && outdepth < 32; i++) { + lines = datas + i * wpls; + for (j = 0; j < w && outdepth < 32; j++) { + if (lines[j] > 65535.5) + outdepth = 32; + else if (lines[j] > 255.5) + outdepth = 16; + } + } + } + maxval = 0xff; + if (outdepth == 16) + maxval = 0xffff; + else /* outdepth == 32 */ + maxval = 0xffffffff; + + /* Gather statistics if %errorflag = TRUE */ + if (errorflag) { + l_int32 negs = 0; + l_int32 overvals = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val < 0.0) + negs++; + else if (val > maxval) + overvals++; + } + } + if (negs > 0) + L_ERROR("Number of negative values: %d\n", procName, negs); + if (overvals > 0) + L_ERROR("Number of too-large values: %d\n", procName, overvals); + } + + /* Make the pix and convert the data */ + if ((pixd = pixCreate(w, h, outdepth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + if (val >= 0.0) { + vald = (l_uint32)(val + 0.5); + } else { /* val < 0.0 */ + if (negvals == L_CLIP_TO_ZERO) + vald = 0; + else + vald = (l_uint32)(-val + 0.5); + } + if (vald > maxval) + vald = maxval; + if (outdepth == 8) + SET_DATA_BYTE(lined, j, vald); + else if (outdepth == 16) + SET_DATA_TWO_BYTES(lined, j, vald); + else /* outdepth == 32 */ + SET_DATA_FOUR_BYTES(lined, j, vald); + } + } + + return pixd; +} + + +/*! + * \brief dpixConvertToFPix() + * + * \param[in] dpix + * \return fpix, or NULL on error + */ +FPIX * +dpixConvertToFPix(DPIX *dpix) +{ +l_int32 w, h, i, j, wpls, wpld; +l_float64 val; +l_float32 *datad, *lined; +l_float64 *datas, *lines; +FPIX *fpix; + + PROCNAME("dpixConvertToFPix"); + + if (!dpix) + return (FPIX *)ERROR_PTR("dpix not defined", procName, NULL); + + dpixGetDimensions(dpix, &w, &h); + if ((fpix = fpixCreate(w, h)) == NULL) + return (FPIX *)ERROR_PTR("fpix not made", procName, NULL); + + datas = dpixGetData(dpix); + datad = fpixGetData(fpix); + wpls = dpixGetWpl(dpix); /* 8 byte words */ + wpld = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + lined[j] = (l_float32)val; + } + } + + return fpix; +} + + + +/*--------------------------------------------------------------------* + * Min/max value * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixGetMin() + * + * \param[in] fpix + * \param[out] pminval [optional] min value + * \param[out] pxminloc [optional] x location of min + * \param[out] pyminloc [optional] y location of min + * \return 0 if OK; 1 on error + */ +l_ok +fpixGetMin(FPIX *fpix, + l_float32 *pminval, + l_int32 *pxminloc, + l_int32 *pyminloc) +{ +l_int32 i, j, w, h, wpl, xminloc, yminloc; +l_float32 *data, *line; +l_float32 minval; + + PROCNAME("fpixGetMin"); + + if (!pminval && !pxminloc && !pyminloc) + return ERROR_INT("no return val requested", procName, 1); + if (pminval) *pminval = 0.0; + if (pxminloc) *pxminloc = 0; + if (pyminloc) *pyminloc = 0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + minval = +1.0e20; + xminloc = 0; + yminloc = 0; + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] < minval) { + minval = line[j]; + xminloc = j; + yminloc = i; + } + } + } + + if (pminval) *pminval = minval; + if (pxminloc) *pxminloc = xminloc; + if (pyminloc) *pyminloc = yminloc; + return 0; +} + + +/*! + * \brief fpixGetMax() + * + * \param[in] fpix + * \param[out] pmaxval [optional] max value + * \param[out] pxmaxloc [optional] x location of max + * \param[out] pymaxloc [optional] y location of max + * \return 0 if OK; 1 on error + */ +l_ok +fpixGetMax(FPIX *fpix, + l_float32 *pmaxval, + l_int32 *pxmaxloc, + l_int32 *pymaxloc) +{ +l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; +l_float32 *data, *line; +l_float32 maxval; + + PROCNAME("fpixGetMax"); + + if (!pmaxval && !pxmaxloc && !pymaxloc) + return ERROR_INT("no return val requested", procName, 1); + if (pmaxval) *pmaxval = 0.0; + if (pxmaxloc) *pxmaxloc = 0; + if (pymaxloc) *pymaxloc = 0; + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + maxval = -1.0e20; + xmaxloc = 0; + ymaxloc = 0; + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] > maxval) { + maxval = line[j]; + xmaxloc = j; + ymaxloc = i; + } + } + } + + if (pmaxval) *pmaxval = maxval; + if (pxmaxloc) *pxmaxloc = xmaxloc; + if (pymaxloc) *pymaxloc = ymaxloc; + return 0; +} + + +/*! + * \brief dpixGetMin() + * + * \param[in] dpix + * \param[out] pminval [optional] min value + * \param[out] pxminloc [optional] x location of min + * \param[out] pyminloc [optional] y location of min + * \return 0 if OK; 1 on error + */ +l_ok +dpixGetMin(DPIX *dpix, + l_float64 *pminval, + l_int32 *pxminloc, + l_int32 *pyminloc) +{ +l_int32 i, j, w, h, wpl, xminloc, yminloc; +l_float64 *data, *line; +l_float64 minval; + + PROCNAME("dpixGetMin"); + + if (!pminval && !pxminloc && !pyminloc) + return ERROR_INT("no return val requested", procName, 1); + if (pminval) *pminval = 0.0; + if (pxminloc) *pxminloc = 0; + if (pyminloc) *pyminloc = 0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + minval = +1.0e300; + xminloc = 0; + yminloc = 0; + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] < minval) { + minval = line[j]; + xminloc = j; + yminloc = i; + } + } + } + + if (pminval) *pminval = minval; + if (pxminloc) *pxminloc = xminloc; + if (pyminloc) *pyminloc = yminloc; + return 0; +} + + +/*! + * \brief dpixGetMax() + * + * \param[in] dpix + * \param[out] pmaxval [optional] max value + * \param[out] pxmaxloc [optional] x location of max + * \param[out] pymaxloc [optional] y location of max + * \return 0 if OK; 1 on error + */ +l_ok +dpixGetMax(DPIX *dpix, + l_float64 *pmaxval, + l_int32 *pxmaxloc, + l_int32 *pymaxloc) +{ +l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; +l_float64 *data, *line; +l_float64 maxval; + + PROCNAME("dpixGetMax"); + + if (!pmaxval && !pxmaxloc && !pymaxloc) + return ERROR_INT("no return val requested", procName, 1); + if (pmaxval) *pmaxval = 0.0; + if (pxmaxloc) *pxmaxloc = 0; + if (pymaxloc) *pymaxloc = 0; + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + maxval = -1.0e20; + xmaxloc = 0; + ymaxloc = 0; + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + if (line[j] > maxval) { + maxval = line[j]; + xmaxloc = j; + ymaxloc = i; + } + } + } + + if (pmaxval) *pmaxval = maxval; + if (pxmaxloc) *pxmaxloc = xmaxloc; + if (pymaxloc) *pymaxloc = ymaxloc; + return 0; +} + + +/*--------------------------------------------------------------------* + * Special integer scaling * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixScaleByInteger() + * + * \param[in] fpixs typically low resolution + * \param[in] factor integer scaling factor + * \return fpixd interpolated result, or NULL on error + * + *
+ * Notes:
+ *      (1) The width wd of fpixd is related to ws of fpixs by:
+ *              wd = factor * (ws - 1) + 1   (and ditto for the height)
+ *          We avoid special-casing boundary pixels in the interpolation
+ *          by constructing fpixd by inserting (factor - 1) interpolated
+ *          pixels between each pixel in fpixs.  Then
+ *               wd = ws + (ws - 1) * (factor - 1)    (same as above)
+ *          This also has the advantage that if we subsample by %factor,
+ *          throwing out all the interpolated pixels, we regain the
+ *          original low resolution fpix.
+ * 
+ */ +FPIX * +fpixScaleByInteger(FPIX *fpixs, + l_int32 factor) +{ +l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; +l_float32 val0, val1, val2, val3; +l_float32 *datas, *datad, *lines, *lined, *fract; +FPIX *fpixd; + + PROCNAME("fpixScaleByInteger"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &ws, &hs); + wd = factor * (ws - 1) + 1; + hd = factor * (hs - 1) + 1; + fpixd = fpixCreate(wd, hd); + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs); + wpld = fpixGetWpl(fpixd); + fract = (l_float32 *)LEPT_CALLOC(factor, sizeof(l_float32)); + for (i = 0; i < factor; i++) + fract[i] = i / (l_float32)factor; + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + val2 = lines[wpls + j]; + val3 = lines[wpls + j + 1]; + for (k = 0; k < factor; k++) { /* rows of sub-block */ + lined = datad + (i * factor + k) * wpld; + for (m = 0; m < factor; m++) { /* cols of sub-block */ + lined[j * factor + m] = + val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + + val1 * fract[m] * (1.0 - fract[k]) + + val2 * (1.0 - fract[m]) * fract[k] + + val3 * fract[m] * fract[k]; + } + } + } + } + + /* Do the right-most column of fpixd, skipping LR corner */ + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + val0 = lines[ws - 1]; + val1 = lines[wpls + ws - 1]; + for (k = 0; k < factor; k++) { + lined = datad + (i * factor + k) * wpld; + lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; + } + } + + /* Do the bottom-most row of fpixd */ + lines = datas + (hs - 1) * wpls; + lined = datad + (hd - 1) * wpld; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + for (m = 0; m < factor; m++) + lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; + lined[wd - 1] = lines[ws - 1]; /* LR corner */ + } + + LEPT_FREE(fract); + return fpixd; +} + + +/*! + * \brief dpixScaleByInteger() + * + * \param[in] dpixs typically low resolution + * \param[in] factor integer scaling factor + * \return dpixd interpolated result, or NULL on error + * + *
+ * Notes:
+ *      (1) The width wd of dpixd is related to ws of dpixs by:
+ *              wd = factor * (ws - 1) + 1   (and ditto for the height)
+ *          We avoid special-casing boundary pixels in the interpolation
+ *          by constructing fpixd by inserting (factor - 1) interpolated
+ *          pixels between each pixel in fpixs.  Then
+ *               wd = ws + (ws - 1) * (factor - 1)    (same as above)
+ *          This also has the advantage that if we subsample by %factor,
+ *          throwing out all the interpolated pixels, we regain the
+ *          original low resolution dpix.
+ * 
+ */ +DPIX * +dpixScaleByInteger(DPIX *dpixs, + l_int32 factor) +{ +l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; +l_float64 val0, val1, val2, val3; +l_float64 *datas, *datad, *lines, *lined, *fract; +DPIX *dpixd; + + PROCNAME("dpixScaleByInteger"); + + if (!dpixs) + return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); + + dpixGetDimensions(dpixs, &ws, &hs); + wd = factor * (ws - 1) + 1; + hd = factor * (hs - 1) + 1; + dpixd = dpixCreate(wd, hd); + datas = dpixGetData(dpixs); + datad = dpixGetData(dpixd); + wpls = dpixGetWpl(dpixs); + wpld = dpixGetWpl(dpixd); + fract = (l_float64 *)LEPT_CALLOC(factor, sizeof(l_float64)); + for (i = 0; i < factor; i++) + fract[i] = i / (l_float64)factor; + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + val2 = lines[wpls + j]; + val3 = lines[wpls + j + 1]; + for (k = 0; k < factor; k++) { /* rows of sub-block */ + lined = datad + (i * factor + k) * wpld; + for (m = 0; m < factor; m++) { /* cols of sub-block */ + lined[j * factor + m] = + val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + + val1 * fract[m] * (1.0 - fract[k]) + + val2 * (1.0 - fract[m]) * fract[k] + + val3 * fract[m] * fract[k]; + } + } + } + } + + /* Do the right-most column of dpixd, skipping LR corner */ + for (i = 0; i < hs - 1; i++) { + lines = datas + i * wpls; + val0 = lines[ws - 1]; + val1 = lines[wpls + ws - 1]; + for (k = 0; k < factor; k++) { + lined = datad + (i * factor + k) * wpld; + lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; + } + } + + /* Do the bottom-most row of dpixd */ + lines = datas + (hs - 1) * wpls; + lined = datad + (hd - 1) * wpld; + for (j = 0; j < ws - 1; j++) { + val0 = lines[j]; + val1 = lines[j + 1]; + for (m = 0; m < factor; m++) + lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; + lined[wd - 1] = lines[ws - 1]; /* LR corner */ + } + + LEPT_FREE(fract); + return dpixd; +} + + +/*--------------------------------------------------------------------* + * Arithmetic operations * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixLinearCombination() + * + * \param[in] fpixd [optional] this can be null, equal to fpixs1, or + * different from fpixs1 + * \param[in] fpixs1 can be equal to fpixd + * \param[in] fpixs2 + * \param[in] a, b multiplication factors on fpixs1 and fpixs2, rsp. + * \return fpixd always + * + *
+ * Notes:
+ *      (1) Computes pixelwise linear combination: a * src1 + b * src2
+ *      (2) Alignment is to UL corner.
+ *      (3) There are 3 cases.  The result can go to a new dest,
+ *          in-place to fpixs1, or to an existing input dest:
+ *          * fpixd == null:   (src1 + src2) --> new fpixd
+ *          * fpixd == fpixs1:  (src1 + src2) --> src1  (in-place)
+ *          * fpixd != fpixs1: (src1 + src2) --> input fpixd
+ *      (4) fpixs2 must be different from both fpixd and fpixs1.
+ * 
+ */ +FPIX * +fpixLinearCombination(FPIX *fpixd, + FPIX *fpixs1, + FPIX *fpixs2, + l_float32 a, + l_float32 b) +{ +l_int32 i, j, ws, hs, w, h, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; + + PROCNAME("fpixLinearCombination"); + + if (!fpixs1) + return (FPIX *)ERROR_PTR("fpixs1 not defined", procName, fpixd); + if (!fpixs2) + return (FPIX *)ERROR_PTR("fpixs2 not defined", procName, fpixd); + if (fpixs1 == fpixs2) + return (FPIX *)ERROR_PTR("fpixs1 == fpixs2", procName, fpixd); + if (fpixs2 == fpixd) + return (FPIX *)ERROR_PTR("fpixs2 == fpixd", procName, fpixd); + + if (fpixs1 != fpixd) + fpixd = fpixCopy(fpixd, fpixs1); + + datas = fpixGetData(fpixs2); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs2); + wpld = fpixGetWpl(fpixd); + fpixGetDimensions(fpixs2, &ws, &hs); + fpixGetDimensions(fpixd, &w, &h); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = a * lined[j] + b * lines[j]; + } + + return fpixd; +} + + +/*! + * \brief fpixAddMultConstant() + * + * \param[in] fpix + * \param[in] addc use 0.0 to skip the operation + * \param[in] multc use 1.0 to skip the operation + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) It can be used to multiply each pixel by a constant,
+ *          and also to add a constant to each pixel.  Multiplication
+ *          is done first.
+ * 
+ */ +l_ok +fpixAddMultConstant(FPIX *fpix, + l_float32 addc, + l_float32 multc) +{ +l_int32 i, j, w, h, wpl; +l_float32 *line, *data; + + PROCNAME("fpixAddMultConstant"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + if (addc == 0.0 && multc == 1.0) + return 0; + + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + wpl = fpixGetWpl(fpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (addc == 0.0) { + for (j = 0; j < w; j++) + line[j] *= multc; + } else if (multc == 1.0) { + for (j = 0; j < w; j++) + line[j] += addc; + } else { + for (j = 0; j < w; j++) { + line[j] = multc * line[j] + addc; + } + } + } + + return 0; +} + + +/*! + * \brief dpixLinearCombination() + * + * \param[in] dpixd [optional] this can be null, equal to dpixs1, or + * different from dpixs1 + * \param[in] dpixs1 can be equal to dpixd + * \param[in] dpixs2 + * \param[in] a, b multiplication factors on dpixs1 and dpixs2, rsp. + * \return dpixd always + * + *
+ * Notes:
+ *      (1) Computes pixelwise linear combination: a * src1 + b * src2
+ *      (2) Alignment is to UL corner.
+ *      (3) There are 3 cases.  The result can go to a new dest,
+ *          in-place to dpixs1, or to an existing input dest:
+ *          * dpixd == null:   (src1 + src2) --> new dpixd
+ *          * dpixd == dpixs1:  (src1 + src2) --> src1  (in-place)
+ *          * dpixd != dpixs1: (src1 + src2) --> input dpixd
+ *      (4) dpixs2 must be different from both dpixd and dpixs1.
+ * 
+ */ +DPIX * +dpixLinearCombination(DPIX *dpixd, + DPIX *dpixs1, + DPIX *dpixs2, + l_float32 a, + l_float32 b) +{ +l_int32 i, j, ws, hs, w, h, wpls, wpld; +l_float64 *datas, *datad, *lines, *lined; + + PROCNAME("dpixLinearCombination"); + + if (!dpixs1) + return (DPIX *)ERROR_PTR("dpixs1 not defined", procName, dpixd); + if (!dpixs2) + return (DPIX *)ERROR_PTR("dpixs2 not defined", procName, dpixd); + if (dpixs1 == dpixs2) + return (DPIX *)ERROR_PTR("dpixs1 == dpixs2", procName, dpixd); + if (dpixs2 == dpixd) + return (DPIX *)ERROR_PTR("dpixs2 == dpixd", procName, dpixd); + + if (dpixs1 != dpixd) + dpixd = dpixCopy(dpixd, dpixs1); + + datas = dpixGetData(dpixs2); + datad = dpixGetData(dpixd); + wpls = dpixGetWpl(dpixs2); + wpld = dpixGetWpl(dpixd); + dpixGetDimensions(dpixs2, &ws, &hs); + dpixGetDimensions(dpixd, &w, &h); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = a * lined[j] + b * lines[j]; + } + + return dpixd; +} + + +/*! + * \brief dpixAddMultConstant() + * + * \param[in] dpix + * \param[in] addc use 0.0 to skip the operation + * \param[in] multc use 1.0 to skip the operation + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) It can be used to multiply each pixel by a constant,
+ *          and also to add a constant to each pixel.  Multiplication
+ *          is done first.
+ * 
+ */ +l_ok +dpixAddMultConstant(DPIX *dpix, + l_float64 addc, + l_float64 multc) +{ +l_int32 i, j, w, h, wpl; +l_float64 *line, *data; + + PROCNAME("dpixAddMultConstant"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + if (addc == 0.0 && multc == 1.0) + return 0; + + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + wpl = dpixGetWpl(dpix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (addc == 0.0) { + for (j = 0; j < w; j++) + line[j] *= multc; + } else if (multc == 1.0) { + for (j = 0; j < w; j++) + line[j] += addc; + } else { + for (j = 0; j < w; j++) + line[j] = multc * line[j] + addc; + } + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Set all * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixSetAllArbitrary() + * + * \param[in] fpix + * \param[in] inval to set at each pixel + * \return 0 if OK, 1 on error + */ +l_ok +fpixSetAllArbitrary(FPIX *fpix, + l_float32 inval) +{ +l_int32 i, j, w, h; +l_float32 *data, *line; + + PROCNAME("fpixSetAllArbitrary"); + + if (!fpix) + return ERROR_INT("fpix not defined", procName, 1); + + fpixGetDimensions(fpix, &w, &h); + data = fpixGetData(fpix); + for (i = 0; i < h; i++) { + line = data + i * w; + for (j = 0; j < w; j++) + *(line + j) = inval; + } + + return 0; +} + + +/*! + * \brief dpixSetAllArbitrary() + * + * \param[in] dpix + * \param[in] inval to set at each pixel + * \return 0 if OK, 1 on error + */ +l_ok +dpixSetAllArbitrary(DPIX *dpix, + l_float64 inval) +{ +l_int32 i, j, w, h; +l_float64 *data, *line; + + PROCNAME("dpixSetAllArbitrary"); + + if (!dpix) + return ERROR_INT("dpix not defined", procName, 1); + + dpixGetDimensions(dpix, &w, &h); + data = dpixGetData(dpix); + for (i = 0; i < h; i++) { + line = data + i * w; + for (j = 0; j < w; j++) + *(line + j) = inval; + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Border functions * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixAddBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds border of '0' 32-bit pixels
+ * 
+ */ +FPIX * +fpixAddBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd; +FPIX *fpixd; + + PROCNAME("fpixAddBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) + return fpixCopy(NULL, fpixs); + fpixGetDimensions(fpixs, &ws, &hs); + wd = ws + left + right; + hd = hs + top + bot; + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + fpixCopyResolution(fpixd, fpixs); + fpixRasterop(fpixd, left, top, ws, hs, fpixs, 0, 0); + return fpixd; +} + + +/*! + * \brief fpixRemoveBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be removed + * \return fpixd, or NULL on error + */ +FPIX * +fpixRemoveBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd; +FPIX *fpixd; + + PROCNAME("fpixRemoveBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) + return fpixCopy(NULL, fpixs); + fpixGetDimensions(fpixs, &ws, &hs); + wd = ws - left - right; + hd = hs - top - bot; + if (wd <= 0 || hd <= 0) + return (FPIX *)ERROR_PTR("width & height not both > 0", procName, NULL); + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + fpixCopyResolution(fpixd, fpixs); + fpixRasterop(fpixd, 0, 0, wd, hd, fpixs, left, top); + return fpixd; +} + + + +/*! + * \brief fpixAddMirroredBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixAddMirroredBorder() for situations of usage.
+ * 
+ */ +FPIX * +fpixAddMirroredBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +FPIX *fpixd; + + PROCNAME("fpixAddMirroredBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + for (j = 0; j < left; j++) + fpixRasterop(fpixd, left - 1 - j, top, 1, h, + fpixd, left + j, top); + for (j = 0; j < right; j++) + fpixRasterop(fpixd, left + w + j, top, 1, h, + fpixd, left + w - 1 - j, top); + for (i = 0; i < top; i++) + fpixRasterop(fpixd, 0, top - 1 - i, left + w + right, 1, + fpixd, 0, top + i); + for (i = 0; i < bot; i++) + fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, + fpixd, 0, top + h - 1 - i); + + return fpixd; +} + + +/*! + * \brief fpixAddContinuedBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This adds pixels on each side whose values are equal to
+ *          the value on the closest boundary pixel.
+ * 
+ */ +FPIX * +fpixAddContinuedBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +FPIX *fpixd; + + PROCNAME("fpixAddContinuedBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + for (j = 0; j < left; j++) + fpixRasterop(fpixd, j, top, 1, h, fpixd, left, top); + for (j = 0; j < right; j++) + fpixRasterop(fpixd, left + w + j, top, 1, h, fpixd, left + w - 1, top); + for (i = 0; i < top; i++) + fpixRasterop(fpixd, 0, i, left + w + right, 1, fpixd, 0, top); + for (i = 0; i < bot; i++) + fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, + fpixd, 0, top + h - 1); + + return fpixd; +} + + +/*! + * \brief fpixAddSlopeBorder() + * + * \param[in] fpixs + * \param[in] left, right, top, bot pixels on each side to be added + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This adds pixels on each side whose values have a normal
+ *          derivative equal to the normal derivative at the boundary
+ *          of fpixs.
+ * 
+ */ +FPIX * +fpixAddSlopeBorder(FPIX *fpixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h, fullw, fullh; +l_float32 val1, val2, del; +FPIX *fpixd; + + PROCNAME("fpixAddSlopeBorder"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixd = fpixAddBorder(fpixs, left, right, top, bot); + fpixGetDimensions(fpixs, &w, &h); + + /* Left */ + for (i = top; i < top + h; i++) { + fpixGetPixel(fpixd, left, i, &val1); + fpixGetPixel(fpixd, left + 1, i, &val2); + del = val1 - val2; + for (j = 0; j < left; j++) + fpixSetPixel(fpixd, j, i, val1 + del * (left - j)); + } + + /* Right */ + fullw = left + w + right; + for (i = top; i < top + h; i++) { + fpixGetPixel(fpixd, left + w - 1, i, &val1); + fpixGetPixel(fpixd, left + w - 2, i, &val2); + del = val1 - val2; + for (j = left + w; j < fullw; j++) + fpixSetPixel(fpixd, j, i, val1 + del * (j - left - w + 1)); + } + + /* Top */ + for (j = 0; j < fullw; j++) { + fpixGetPixel(fpixd, j, top, &val1); + fpixGetPixel(fpixd, j, top + 1, &val2); + del = val1 - val2; + for (i = 0; i < top; i++) + fpixSetPixel(fpixd, j, i, val1 + del * (top - i)); + } + + /* Bottom */ + fullh = top + h + bot; + for (j = 0; j < fullw; j++) { + fpixGetPixel(fpixd, j, top + h - 1, &val1); + fpixGetPixel(fpixd, j, top + h - 2, &val2); + del = val1 - val2; + for (i = top + h; i < fullh; i++) + fpixSetPixel(fpixd, j, i, val1 + del * (i - top - h + 1)); + } + + return fpixd; +} + + +/*--------------------------------------------------------------------* + * Simple rasterop * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRasterop() + * + * \param[in] fpixd dest fpix + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] fpixs src fpix + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return 0 if OK; 1 on error. + * + *
+ * Notes:
+ *      (1) This is similar in structure to pixRasterop(), except
+ *          it only allows copying from the source into the destination.
+ *          For that reason, no op code is necessary.  Additionally,
+ *          all pixels are 32 bit words (float values), which makes
+ *          the copy very simple.
+ *      (2) Clipping of both src and dest fpix are done automatically.
+ *      (3) This allows in-place copying, without checking to see if
+ *          the result is valid:  use for in-place with caution!
+ * 
+ */ +l_ok +fpixRasterop(FPIX *fpixd, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + FPIX *fpixs, + l_int32 sx, + l_int32 sy) +{ +l_int32 fsw, fsh, fdw, fdh, dhangw, shangw, dhangh, shangh; +l_int32 i, j, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; + + PROCNAME("fpixRasterop"); + + if (!fpixs) + return ERROR_INT("fpixs not defined", procName, 1); + if (!fpixd) + return ERROR_INT("fpixd not defined", procName, 1); + + /* -------------------------------------------------------- * + * Clip to maximum rectangle with both src and dest * + * -------------------------------------------------------- */ + fpixGetDimensions(fpixs, &fsw, &fsh); + fpixGetDimensions(fpixd, &fdw, &fdh); + + /* First clip horizontally (sx, dx, dw) */ + if (dx < 0) { + sx -= dx; /* increase sx */ + dw += dx; /* reduce dw */ + dx = 0; + } + if (sx < 0) { + dx -= sx; /* increase dx */ + dw += sx; /* reduce dw */ + sx = 0; + } + dhangw = dx + dw - fdw; /* rect overhang of dest to right */ + if (dhangw > 0) + dw -= dhangw; /* reduce dw */ + shangw = sx + dw - fsw; /* rect overhang of src to right */ + if (shangw > 0) + dw -= shangw; /* reduce dw */ + + /* Then clip vertically (sy, dy, dh) */ + if (dy < 0) { + sy -= dy; /* increase sy */ + dh += dy; /* reduce dh */ + dy = 0; + } + if (sy < 0) { + dy -= sy; /* increase dy */ + dh += sy; /* reduce dh */ + sy = 0; + } + dhangh = dy + dh - fdh; /* rect overhang of dest below */ + if (dhangh > 0) + dh -= dhangh; /* reduce dh */ + shangh = sy + dh - fsh; /* rect overhang of src below */ + if (shangh > 0) + dh -= shangh; /* reduce dh */ + + /* if clipped entirely, quit */ + if ((dw <= 0) || (dh <= 0)) + return 0; + + /* -------------------------------------------------------- * + * Copy block of data * + * -------------------------------------------------------- */ + datas = fpixGetData(fpixs); + datad = fpixGetData(fpixd); + wpls = fpixGetWpl(fpixs); + wpld = fpixGetWpl(fpixd); + datas += sy * wpls + sx; /* at UL corner of block */ + datad += dy * wpld + dx; /* at UL corner of block */ + for (i = 0; i < dh; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < dw; j++) { + *lined = *lines; + lines++; + lined++; + } + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Rotation by multiples of 90 degrees * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixRotateOrth() + * + * \param[in] fpixs + * \param[in] quads 0-3; number of 90 degree cw rotations + * \return fpixd, or NULL on error + */ +FPIX * +fpixRotateOrth(FPIX *fpixs, + l_int32 quads) +{ + PROCNAME("fpixRotateOrth"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (quads < 0 || quads > 3) + return (FPIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL); + + if (quads == 0) + return fpixCopy(NULL, fpixs); + else if (quads == 1) + return fpixRotate90(fpixs, 1); + else if (quads == 2) + return fpixRotate180(NULL, fpixs); + else /* quads == 3 */ + return fpixRotate90(fpixs, -1); +} + + +/*! + * \brief fpixRotate180() + * + * \param[in] fpixd [optional] can be null, equal to fpixs, + * or different from fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a 180 rotation of the image about the center,
+ *          which is equivalent to a left-right flip about a vertical
+ *          line through the image center, followed by a top-bottom
+ *          flip about a horizontal line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) fpixd == null (creates a new fpixd)
+ *          (b) fpixd == fpixs (in-place operation)
+ *          (c) fpixd != fpixs (existing fpixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) fpixd = fpixRotate180(NULL, fpixs);
+ *          (b) fpixRotate180(fpixs, fpixs);
+ *          (c) fpixRotate180(fpixd, fpixs);
+ * 
+ */ +FPIX * +fpixRotate180(FPIX *fpixd, + FPIX *fpixs) +{ + PROCNAME("fpixRotate180"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + fpixFlipLR(fpixd, fpixd); + fpixFlipTB(fpixd, fpixd); + return fpixd; +} + + +/*! + * \brief fpixRotate90() + * + * \param[in] fpixs + * \param[in] direction 1 = clockwise; -1 = counter-clockwise + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a 90 degree rotation of the image about the center,
+ *          either cw or ccw, returning a new pix.
+ *      (2) The direction must be either 1 (cw) or -1 (ccw).
+ * 
+ */ +FPIX * +fpixRotate90(FPIX *fpixs, + l_int32 direction) +{ +l_int32 i, j, wd, hd, wpls, wpld; +l_float32 *datas, *datad, *lines, *lined; +FPIX *fpixd; + + PROCNAME("fpixRotate90"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (direction != 1 && direction != -1) + return (FPIX *)ERROR_PTR("invalid direction", procName, NULL); + + fpixGetDimensions(fpixs, &hd, &wd); + if ((fpixd = fpixCreate(wd, hd)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + fpixCopyResolution(fpixd, fpixs); + + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + if (direction == 1) { /* clockwise */ + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + lined[j] = lines[i]; + lines -= wpls; + } + } + } else { /* ccw */ + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + lined[j] = lines[hd - 1 - i]; + lines += wpls; + } + } + } + + return fpixd; +} + + +/*! + * \brief pixFlipLR() + * + * \param[in] fpixd [optional] can be null, equal to fpixs, + * or different from fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a left-right flip of the image, which is
+ *          equivalent to a rotation out of the plane about a
+ *          vertical line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) fpixd == null (creates a new fpixd)
+ *          (b) fpixd == fpixs (in-place operation)
+ *          (c) fpixd != fpixs (existing fpixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) fpixd = fpixFlipLR(NULL, fpixs);
+ *          (b) fpixFlipLR(fpixs, fpixs);
+ *          (c) fpixFlipLR(fpixd, fpixs);
+ *      (4) If an existing fpixd is not the same size as fpixs, the
+ *          image data will be reallocated.
+ * 
+ */ +FPIX * +fpixFlipLR(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 i, j, w, h, wpl, bpl; +l_float32 *line, *data, *buffer; + + PROCNAME("fpixFlipLR"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + fpixGetDimensions(fpixs, &w, &h); + + /* Prepare fpixd for in-place operation */ + if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + data = fpixGetData(fpixd); + wpl = fpixGetWpl(fpixd); /* 4-byte words */ + bpl = 4 * wpl; + if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); + } + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < w; j++) + line[j] = buffer[w - 1 - j]; + } + LEPT_FREE(buffer); + return fpixd; +} + + +/*! + * \brief fpixFlipTB() + * + * \param[in] fpixd [optional] can be null, equal to fpixs, + * or different from fpixs + * \param[in] fpixs + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a top-bottom flip of the image, which is
+ *          equivalent to a rotation out of the plane about a
+ *          horizontal line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) fpixd == null (creates a new fpixd)
+ *          (b) fpixd == fpixs (in-place operation)
+ *          (c) fpixd != fpixs (existing fpixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) fpixd = fpixFlipTB(NULL, fpixs);
+ *          (b) fpixFlipTB(fpixs, fpixs);
+ *          (c) fpixFlipTB(fpixd, fpixs);
+ *      (4) If an existing fpixd is not the same size as fpixs, the
+ *          image data will be reallocated.
+ * 
+ */ +FPIX * +fpixFlipTB(FPIX *fpixd, + FPIX *fpixs) +{ +l_int32 i, k, h, h2, wpl, bpl; +l_float32 *linet, *lineb, *data, *buffer; + + PROCNAME("fpixFlipTB"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + + /* Prepare fpixd for in-place operation */ + if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) + return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); + + data = fpixGetData(fpixd); + wpl = fpixGetWpl(fpixd); + fpixGetDimensions(fpixd, NULL, &h); + if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { + fpixDestroy(&fpixd); + return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); + } + h2 = h / 2; + bpl = 4 * wpl; + for (i = 0, k = h - 1; i < h2; i++, k--) { + linet = data + i * wpl; + lineb = data + k * wpl; + memcpy(buffer, linet, bpl); + memcpy(linet, lineb, bpl); + memcpy(lineb, buffer, bpl); + } + LEPT_FREE(buffer); + return fpixd; +} + + +/*--------------------------------------------------------------------* + * Affine and projective interpolated transforms * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixAffinePta() + * + * \param[in] fpixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] border size of extension with constant normal derivative + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If %border > 0, all four sides are extended by that distance,
+ *          and removed after the transformation is finished.  Pixels
+ *          that would be brought in to the trimmed result from outside
+ *          the extended region are assigned %inval.  The purpose of
+ *          extending the image is to avoid such assignments.
+ *      (2) On the other hand, you may want to give all pixels that
+ *          are brought in from outside fpixs a specific value.  In that
+ *          case, set %border == 0.
+ * 
+ */ +FPIX * +fpixAffinePta(FPIX *fpixs, + PTA *ptad, + PTA *ptas, + l_int32 border, + l_float32 inval) +{ +l_float32 *vc; +PTA *ptas2, *ptad2; +FPIX *fpixs2, *fpixd, *fpixd2; + + PROCNAME("fpixAffinePta"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (!ptas) + return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); + + /* If a border is to be added, also translate the ptas */ + if (border > 0) { + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); + } else { + ptas2 = ptaClone(ptas); + ptad2 = ptaClone(ptad); + fpixs2 = fpixClone(fpixs); + } + + /* Get backwards transform from dest to src, and apply it */ + getAffineXformCoeffs(ptad2, ptas2, &vc); + fpixd2 = fpixAffine(fpixs2, vc, inval); + fpixDestroy(&fpixs2); + ptaDestroy(&ptas2); + ptaDestroy(&ptad2); + LEPT_FREE(vc); + + if (border == 0) + return fpixd2; + + /* Remove the added border */ + fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); + fpixDestroy(&fpixd2); + return fpixd; +} + + +/*! + * \brief fpixAffine() + * + * \param[in] fpixs 8 bpp + * \param[in] vc vector of 8 coefficients for projective transformation + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + */ +FPIX * +fpixAffine(FPIX *fpixs, + l_float32 *vc, + l_float32 inval) +{ +l_int32 i, j, w, h, wpld; +l_float32 val; +l_float32 *datas, *datad, *lined; +l_float32 x, y; +FPIX *fpixd; + + PROCNAME("fpixAffine"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + fpixGetDimensions(fpixs, &w, &h); + if (!vc) + return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = fpixGetData(fpixs); + fpixd = fpixCreateTemplate(fpixs); + fpixSetAllArbitrary(fpixd, inval); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + affineXformPt(vc, j, i, &x, &y); + linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); + *(lined + j) = val; + } + } + + return fpixd; +} + + +/*! + * \brief fpixProjectivePta() + * + * \param[in] fpixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] border size of extension with constant normal derivative + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If %border > 0, all four sides are extended by that distance,
+ *          and removed after the transformation is finished.  Pixels
+ *          that would be brought in to the trimmed result from outside
+ *          the extended region are assigned %inval.  The purpose of
+ *          extending the image is to avoid such assignments.
+ *      (2) On the other hand, you may want to give all pixels that
+ *          are brought in from outside fpixs a specific value.  In that
+ *          case, set %border == 0.
+ * 
+ */ +FPIX * +fpixProjectivePta(FPIX *fpixs, + PTA *ptad, + PTA *ptas, + l_int32 border, + l_float32 inval) +{ +l_float32 *vc; +PTA *ptas2, *ptad2; +FPIX *fpixs2, *fpixd, *fpixd2; + + PROCNAME("fpixProjectivePta"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (!ptas) + return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); + + /* If a border is to be added, also translate the ptas */ + if (border > 0) { + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); + } else { + ptas2 = ptaClone(ptas); + ptad2 = ptaClone(ptad); + fpixs2 = fpixClone(fpixs); + } + + /* Get backwards transform from dest to src, and apply it */ + getProjectiveXformCoeffs(ptad2, ptas2, &vc); + fpixd2 = fpixProjective(fpixs2, vc, inval); + fpixDestroy(&fpixs2); + ptaDestroy(&ptas2); + ptaDestroy(&ptad2); + LEPT_FREE(vc); + + if (border == 0) + return fpixd2; + + /* Remove the added border */ + fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); + fpixDestroy(&fpixd2); + return fpixd; +} + + +/*! + * \brief fpixProjective() + * + * \param[in] fpixs 8 bpp + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] inval value brought in; typ. 0 + * \return fpixd, or NULL on error + */ +FPIX * +fpixProjective(FPIX *fpixs, + l_float32 *vc, + l_float32 inval) +{ +l_int32 i, j, w, h, wpld; +l_float32 val; +l_float32 *datas, *datad, *lined; +l_float32 x, y; +FPIX *fpixd; + + PROCNAME("fpixProjective"); + + if (!fpixs) + return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); + fpixGetDimensions(fpixs, &w, &h); + if (!vc) + return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = fpixGetData(fpixs); + fpixd = fpixCreateTemplate(fpixs); + fpixSetAllArbitrary(fpixd, inval); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + projectiveXformPt(vc, j, i, &x, &y); + linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); + *(lined + j) = val; + } + } + + return fpixd; +} + + +/*! + * \brief linearInterpolatePixelFloat() + * + * \param[in] datas ptr to beginning of float image data + * \param[in] w, h dimensions of image + * \param[in] x, y floating pt location for evaluation + * \param[in] inval float value brought in from the outside when the + * input x,y location is outside the image + * \param[out] pval interpolated float value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a standard linear interpolation function.  It is
+ *          equivalent to area weighting on each component, and
+ *          avoids "jaggies" when rendering sharp edges.
+ * 
+ */ +l_ok +linearInterpolatePixelFloat(l_float32 *datas, + l_int32 w, + l_int32 h, + l_float32 x, + l_float32 y, + l_float32 inval, + l_float32 *pval) +{ +l_int32 xpm, ypm, xp, yp, xf, yf; +l_float32 v00, v01, v10, v11; +l_float32 *lines; + + PROCNAME("linearInterpolatePixelFloat"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = inval; + if (!datas) + return ERROR_INT("datas not defined", procName, 1); + + /* Skip if off the edge */ + if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0) + return 0; + + xpm = (l_int32)(16.0 * x + 0.5); + ypm = (l_int32)(16.0 * y + 0.5); + xp = xpm >> 4; + yp = ypm >> 4; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + +#if DEBUG + if (xf < 0 || yf < 0) + fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); +#endif /* DEBUG */ + + /* Interpolate by area weighting. */ + lines = datas + yp * w; + v00 = (16.0 - xf) * (16.0 - yf) * (*(lines + xp)); + v10 = xf * (16.0 - yf) * (*(lines + xp + 1)); + v01 = (16.0 - xf) * yf * (*(lines + w + xp)); + v11 = (l_float32)(xf) * yf * (*(lines + w + xp + 1)); + *pval = (v00 + v01 + v10 + v11) / 256.0; + return 0; +} + + +/*--------------------------------------------------------------------* + * Thresholding to 1 bpp Pix * + *--------------------------------------------------------------------*/ +/*! + * \brief fpixThresholdToPix() + * + * \param[in] fpix + * \param[in] thresh + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) For all values of fpix that are <= thresh, sets the pixel
+ *          in pixd to 1.
+ * 
+ */ +PIX * +fpixThresholdToPix(FPIX *fpix, + l_float32 thresh) +{ +l_int32 i, j, w, h, wpls, wpld; +l_float32 *datas, *lines; +l_uint32 *datad, *lined; +PIX *pixd; + + PROCNAME("fpixThresholdToPix"); + + if (!fpix) + return (PIX *)ERROR_PTR("fpix not defined", procName, NULL); + + fpixGetDimensions(fpix, &w, &h); + datas = fpixGetData(fpix); + wpls = fpixGetWpl(fpix); + pixd = pixCreate(w, h, 1); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (lines[j] <= thresh) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*--------------------------------------------------------------------* + * Generate function from components * + *--------------------------------------------------------------------*/ +/*! + * \brief pixComponentFunction() + * + * \param[in] pix 32 bpp rgb + * \param[in] rnum, gnum, bnum coefficients for numerator + * \param[in] rdenom, gdenom, bdenom coefficients for denominator + * \return fpixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This stores a function of the component values of each
+ *          input pixel in %fpixd.
+ *      (2) The function is a ratio of linear combinations of component values.
+ *          There are two special cases for denominator coefficients:
+ *          (a) The denominator is 1.0: input 0 for all denominator coefficients
+ *          (b) Only one component is used in the denominator: input 1.0
+ *              for that denominator component and 0.0 for the other two.
+ *      (3) If the denominator is 0, multiply by an arbitrary number that
+ *          is much larger than 1.  Choose 256 "arbitrarily".
+ *
+ * 
+ */ +FPIX * +pixComponentFunction(PIX *pix, + l_float32 rnum, + l_float32 gnum, + l_float32 bnum, + l_float32 rdenom, + l_float32 gdenom, + l_float32 bdenom) +{ +l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, zerodenom, onedenom; +l_float32 fnum, fdenom; +l_uint32 *datas, *lines; +l_float32 *datad, *lined, *recip; +FPIX *fpixd; + + PROCNAME("pixComponentFunction"); + + if (!pix || pixGetDepth(pix) != 32) + return (FPIX *)ERROR_PTR("pix undefined or not 32 bpp", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + datas = pixGetData(pix); + wpls = pixGetWpl(pix); + fpixd = fpixCreate(w, h); + datad = fpixGetData(fpixd); + wpld = fpixGetWpl(fpixd); + zerodenom = (rdenom == 0.0 && gdenom == 0.0 && bdenom == 0.0) ? 1: 0; + onedenom = ((rdenom == 1.0 && gdenom == 0.0 && bdenom == 0.0) || + (rdenom == 0.0 && gdenom == 1.0 && bdenom == 0.0) || + (rdenom == 0.0 && gdenom == 0.0 && bdenom == 1.0)) ? 1 : 0; + recip = NULL; + if (onedenom) { + recip = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); + recip[0] = 256; /* arbitrary large number */ + for (i = 1; i < 256; i++) + recip[i] = 1.0 / (l_float32)i; + } + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (zerodenom) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] = rnum * rval + gnum * gval + bnum * bval; + } + } else if (onedenom && rdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[rval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else if (onedenom && gdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[gval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else if (onedenom && bdenom == 1.0) { + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + lined[j] + = recip[bval] * (rnum * rval + gnum * gval + bnum * bval); + } + } else { /* general case */ + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + fnum = rnum * rval + gnum * gval + bnum * bval; + fdenom = rdenom * rval + gdenom * gval + bdenom * bval; + lined[j] = (fdenom == 0) ? 256.0 * fnum : fnum / fdenom; + } + } + } + + LEPT_FREE(recip); + return fpixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/gifio.c b/hgdriver/3rdparty/hgOCR/leptonica/gifio.c new file mode 100644 index 0000000..b3d15d4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/gifio.c @@ -0,0 +1,675 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file gifio.c + *
+ *
+ *    Reading gif
+ *          PIX            *pixReadStreamGif()
+ *          PIX            *pixReadMemGif()
+ *          static l_int32  gifReadFunc()
+ *          static PIX     *gifToPix()
+ *
+ *    Writing gif
+ *          l_int32         pixWriteStreamGif()
+ *          l_int32         pixWriteMemGif()
+ *          static l_int32  gifWriteFunc()
+ *          static l_int32  pixToGif()
+ *
+ *    The initial version of this module was generously contribued by
+ *    Antony Dovgal.
+ *
+ *    The functions that read and write from pix to gif-compressed memory,
+ *    using gif internal functions DGifOpen() and EGifOpen() that are
+ *    available in 5.1 and later, were contributed by Tobias Peirick.
+ *
+ *    Version information:
+ *
+ *    (1) This supports the gif library, version 5.1 or later, for which
+ *        gif read-from-mem and write-to-mem allow these operations
+ *        without writing temporary files.
+ *    (2) There has never been a gif stream interface.  For versions
+ *        before 5.1, it was necessary to use a file descriptor, and to
+ *        generate a file stream from the low-level descriptor.  With the
+ *        memory interface in 5.1 that can be used on all platforms, it
+ *        is no longer necessary to use any API code with file descriptors.
+ *    (3) The public interface changed with 5.0 and with 5.1, and we
+ *        no longer support 4.6.1 and 5.0.
+ *    (4) Version 5.1.2 came out on Jan 7, 2016.  Leptonica cannot
+ *        successfully read gif files that it writes with this version;
+ *        DGifSlurp() gets an internal error from an uninitialized array
+ *        and returns failure.  The problem was fixed in 5.1.3.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + +/* --------------------------------------------------------------------*/ +#if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */ +/* --------------------------------------------------------------------*/ + +#include "gif_lib.h" + + /* Interface that enables low-level GIF support for reading from memory */ +static PIX * gifToPix(GifFileType *gif); + /* Interface that enables low-level GIF support for writing to memory */ +static l_int32 pixToGif(PIX *pix, GifFileType *gif); + + /*! For in-memory decoding of GIF; 5.1+ */ +typedef struct GifReadBuffer +{ + size_t size; /*!< size of buffer */ + size_t pos; /*!< position relative to beginning of buffer */ + const l_uint8 *cdata; /*!< data in the buffer */ +} GifReadBuffer; + + /*! Low-level callback for in-memory decoding */ +static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest, + l_int32 bytesToRead); + /*! Low-level callback for in-memory encoding */ +static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src, + l_int32 bytesToWrite); + + +/*---------------------------------------------------------------------* + * Reading gif * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadStreamGif() + * + * \param[in] fp file stream opened for reading + * \return pix, or NULL on error + */ +PIX * +pixReadStreamGif(FILE *fp) +{ +l_uint8 *filedata; +size_t filesize; +PIX *pix; + + PROCNAME("pixReadStreamGif"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + + /* Read data into memory from file */ + rewind(fp); + if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL) + return (PIX *)ERROR_PTR("filedata not read", procName, NULL); + + /* Uncompress from memory */ + pix = pixReadMemGif(filedata, filesize); + LEPT_FREE(filedata); + if (!pix) + L_ERROR("failed to read gif from file data\n", procName); + return pix; +} + + +/*! + * \brief pixReadMemGif() + * + * \param[in] cdata const; gif-encoded + * \param[in] size bytes data + * \return pix, or NULL on error + * + *
+ * Notes:
+ *     (1) For libgif version >= 5.1, this uses the DGifOpen() buffer
+ *         interface.  No temp files are required.
+ *     (2) For libgif version < 5.1, it was necessary to write the compressed
+ *         data to file and read it back, and we couldn't use the GNU
+ *         runtime extension fmemopen() because libgif doesn't have a file
+ *         stream interface.
+ * 
+ */ +PIX * +pixReadMemGif(const l_uint8 *cdata, + size_t size) +{ +GifFileType *gif; +GifReadBuffer buffer; + + PROCNAME("pixReadMemGif"); + + /* 5.1+ and not 5.1.2 */ +#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) + L_ERROR("Require giflib-5.1 or later\n", procName); + return NULL; +#endif /* < 5.1 */ +#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ + L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName); + return NULL; +#endif /* 5.1.2 */ + + if (!cdata) + return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); + + buffer.cdata = cdata; + buffer.size = size; + buffer.pos = 0; + if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL) + return (PIX *)ERROR_PTR("could not open gif stream from memory", + procName, NULL); + + return gifToPix(gif); +} + + +static l_int32 +gifReadFunc(GifFileType *gif, + GifByteType *dest, + l_int32 bytesToRead) +{ +GifReadBuffer *buffer; +l_int32 bytesRead; + + PROCNAME("gifReadFunc"); + + if ((buffer = (GifReadBuffer*)gif->UserData) == NULL) + return ERROR_INT("UserData not set", procName, -1); + + if(buffer->pos >= buffer->size || bytesToRead > buffer->size) + return -1; + + bytesRead = (buffer->pos < buffer->size - bytesToRead) + ? bytesToRead : buffer->size - buffer->pos; + memcpy(dest, buffer->cdata + buffer->pos, bytesRead); + buffer->pos += bytesRead; + return bytesRead; +} + + +/*! + * \brief gifToPix() + * + * \param[in] gif opened gif stream + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This decodes the pix from the compressed gif stream and
+ *          closes the stream.
+ *      (2) It is static so that the stream is not exposed to clients.
+ * 
+ */ +static PIX * +gifToPix(GifFileType *gif) +{ +l_int32 wpl, i, j, w, h, d, cindex, ncolors; +l_int32 rval, gval, bval; +l_uint32 *data, *line; +PIX *pixd; +PIXCMAP *cmap; +ColorMapObject *gif_cmap; +SavedImage si; +int giferr; + + PROCNAME("gifToPix"); + + /* Read all the data, but use only the first image found */ + if (DGifSlurp(gif) != GIF_OK) { + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL); + } + + if (gif->SavedImages == NULL) { + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL); + } + + si = gif->SavedImages[0]; + w = si.ImageDesc.Width; + h = si.ImageDesc.Height; + if (w <= 0 || h <= 0) { + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL); + } + + if (si.RasterBits == NULL) { + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL); + } + + if (si.ImageDesc.ColorMap) { + /* private cmap for this image */ + gif_cmap = si.ImageDesc.ColorMap; + } else if (gif->SColorMap) { + /* global cmap for whole picture */ + gif_cmap = gif->SColorMap; + } else { + /* don't know where to take cmap from */ + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("color map is missing", procName, NULL); + } + + ncolors = gif_cmap->ColorCount; + if (ncolors <= 2) + d = 1; + else if (ncolors <= 4) + d = 2; + else if (ncolors <= 16) + d = 4; + else + d = 8; + if ((cmap = pixcmapCreate(d)) == NULL) { + DGifCloseFile(gif, &giferr); + return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL); + } + + for (cindex = 0; cindex < ncolors; cindex++) { + rval = gif_cmap->Colors[cindex].Red; + gval = gif_cmap->Colors[cindex].Green; + bval = gif_cmap->Colors[cindex].Blue; + pixcmapAddColor(cmap, rval, gval, bval); + } + + if ((pixd = pixCreate(w, h, d)) == NULL) { + DGifCloseFile(gif, &giferr); + pixcmapDestroy(&cmap); + return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL); + } + pixSetInputFormat(pixd, IFF_GIF); + pixSetColormap(pixd, cmap); + + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 1) { + for (j = 0; j < w; j++) { + if (si.RasterBits[i * w + j]) + SET_DATA_BIT(line, j); + } + } else if (d == 2) { + for (j = 0; j < w; j++) + SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]); + } else if (d == 4) { + for (j = 0; j < w; j++) + SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]); + } else { /* d == 8 */ + for (j = 0; j < w; j++) + SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]); + } + } + + /* Versions before 5.0 required un-interlacing to restore + * the raster lines to normal order if the image + * had been interlaced (for viewing in a browser): + if (gif->Image.Interlace) { + PIX *pixdi = pixUninterlaceGIF(pixd); + pixTransferAllData(pixd, &pixdi, 0, 0); + } + * This is no longer required. */ + + DGifCloseFile(gif, &giferr); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Writing gif * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteStreamGif() + * + * \param[in] fp file stream opened for writing + * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) All output gif have colormaps.  If the pix is 32 bpp rgb,
+ *          this quantizes the colors and writes out 8 bpp.
+ *          If the pix is 16 bpp grayscale, it converts to 8 bpp first.
+ * 
+ */ +l_ok +pixWriteStreamGif(FILE *fp, + PIX *pix) +{ +l_uint8 *filedata; +size_t filebytes, nbytes; + + PROCNAME("pixWriteStreamGif"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixSetPadBits(pix, 0); + if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) { + LEPT_FREE(filedata); + return ERROR_INT("failure to gif encode pix", procName, 1); + } + + rewind(fp); + nbytes = fwrite(filedata, 1, filebytes, fp); + LEPT_FREE(filedata); + if (nbytes != filebytes) + return ERROR_INT("write error", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteMemGif() + * + * \param[out] pdata data of gif compressed image + * \param[out] psize size of returned data + * \param[in] pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See comments in pixReadMemGif()
+ * 
+ */ +l_ok +pixWriteMemGif(l_uint8 **pdata, + size_t *psize, + PIX *pix) +{ +int giferr; +l_int32 result; +GifFileType *gif; +L_BBUFFER *buffer; + + PROCNAME("pixWriteMemGif"); + + /* 5.1+ and not 5.1.2 */ +#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) + L_ERROR("Require giflib-5.1 or later\n", procName); + return 1; +#endif /* < 5.1 */ +#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ + L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName); + return 1; +#endif /* 5.1.2 */ + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + *pdata = NULL; + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + *psize = 0; + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + + if ((buffer = bbufferCreate(NULL, 0)) == NULL) + return ERROR_INT("failed to create buffer", procName, 1); + + if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) { + bbufferDestroy(&buffer); + return ERROR_INT("failed to create GIF image handle", procName, 1); + } + + result = pixToGif(pix, gif); + EGifCloseFile(gif, &giferr); + + if (result == 0) { + *pdata = bbufferDestroyAndSaveData(&buffer, psize); + } else { + bbufferDestroy(&buffer); + } + return result; +} + + +static l_int32 +gifWriteFunc(GifFileType *gif, + const GifByteType *src, + l_int32 bytesToWrite) +{ +L_BBUFFER *buffer; + + PROCNAME("gifWriteFunc"); + + if ((buffer = (L_BBUFFER*)gif->UserData) == NULL) + return ERROR_INT("UserData not set", procName, -1); + + if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0) + return bytesToWrite; + return 0; +} + + +/*! + * \brief pixToGif() + * + * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp + * \param[in] gif opened gif stream + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This encodes the pix to the gif stream. The stream is not
+ *          closed by this function.
+ *      (2) It is static to make this function private.
+ * 
+ */ +static l_int32 +pixToGif(PIX *pix, + GifFileType *gif) +{ +char *text; +l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval; +l_int32 gif_ncolor = 0; +l_uint32 *data, *line; +PIX *pixd; +PIXCMAP *cmap; +ColorMapObject *gif_cmap; +GifByteType *gif_line; + + PROCNAME("pixToGif"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!gif) + return ERROR_INT("gif not defined", procName, 1); + + d = pixGetDepth(pix); + if (d == 32) { + pixd = pixConvertRGBToColormap(pix, 1); + } else if (d > 1) { + pixd = pixConvertTo8(pix, TRUE); + } else { /* d == 1; make sure there's a colormap */ + pixd = pixClone(pix); + if (!pixGetColormap(pixd)) { + cmap = pixcmapCreate(1); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixSetColormap(pixd, cmap); + } + } + + if (!pixd) + return ERROR_INT("failed to convert image to indexed", procName, 1); + d = pixGetDepth(pixd); + + if ((cmap = pixGetColormap(pixd)) == NULL) { + pixDestroy(&pixd); + return ERROR_INT("cmap is missing", procName, 1); + } + + /* 'Round' the number of gif colors up to a power of 2 */ + ncolor = pixcmapGetCount(cmap); + for (i = 0; i <= 8; i++) { + if ((1 << i) >= ncolor) { + gif_ncolor = (1 << i); + break; + } + } + if (gif_ncolor < 1) { + pixDestroy(&pixd); + return ERROR_INT("number of colors is invalid", procName, 1); + } + + /* Save the cmap colors in a gif_cmap */ + if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) { + pixDestroy(&pixd); + return ERROR_INT("failed to create GIF color map", procName, 1); + } + for (i = 0; i < gif_ncolor; i++) { + rval = gval = bval = 0; + if (ncolor > 0) { + if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) { + pixDestroy(&pixd); + GifFreeMapObject(gif_cmap); + return ERROR_INT("failed to get color from color map", + procName, 1); + } + ncolor--; + } + gif_cmap->Colors[i].Red = rval; + gif_cmap->Colors[i].Green = gval; + gif_cmap->Colors[i].Blue = bval; + } + + pixGetDimensions(pixd, &w, &h, NULL); + if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap) + != GIF_OK) { + pixDestroy(&pixd); + GifFreeMapObject(gif_cmap); + return ERROR_INT("failed to write screen description", procName, 1); + } + GifFreeMapObject(gif_cmap); /* not needed after this point */ + + if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) { + pixDestroy(&pixd); + return ERROR_INT("failed to image screen description", procName, 1); + } + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + if (d != 1 && d != 2 && d != 4 && d != 8) { + pixDestroy(&pixd); + return ERROR_INT("image depth is not in {1, 2, 4, 8}", procName, 1); + } + + if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w)) + == NULL) { + pixDestroy(&pixd); + return ERROR_INT("mem alloc fail for data line", procName, 1); + } + + for (i = 0; i < h; i++) { + line = data + i * wpl; + /* Gif's way of setting the raster line up for compression */ + for (j = 0; j < w; j++) { + switch(d) + { + case 8: + gif_line[j] = GET_DATA_BYTE(line, j); + break; + case 4: + gif_line[j] = GET_DATA_QBIT(line, j); + break; + case 2: + gif_line[j] = GET_DATA_DIBIT(line, j); + break; + case 1: + gif_line[j] = GET_DATA_BIT(line, j); + break; + } + } + + /* Compress and save the line */ + if (EGifPutLine(gif, gif_line, w) != GIF_OK) { + LEPT_FREE(gif_line); + pixDestroy(&pixd); + return ERROR_INT("failed to write data line into GIF", procName, 1); + } + } + + /* Write a text comment. This must be placed after writing the + * data (!!) Note that because libgif does not provide a function + * for reading comments from file, you will need another way + * to read comments. */ + if ((text = pixGetText(pix)) != NULL) { + if (EGifPutComment(gif, text) != GIF_OK) + L_WARNING("gif comment not written\n", procName); + } + + LEPT_FREE(gif_line); + pixDestroy(&pixd); + return 0; +} + + +#if 0 +/*---------------------------------------------------------------------* + * Removing interlacing (reference only; not used) * + *---------------------------------------------------------------------*/ + /* GIF supports 4-way interlacing by raster lines. + * Before 5.0, it was necessary for leptonica to restore interlaced + * data to normal raster order when reading to a pix. With 5.0, + * the de-interlacing is done by the library read function. + * It is here only as a reference. */ +static const l_int32 InterlacedOffset[] = {0, 4, 2, 1}; +static const l_int32 InterlacedJumps[] = {8, 8, 4, 2}; + +static PIX * +pixUninterlaceGIF(PIX *pixs) +{ +l_int32 w, h, d, wpl, j, k, srow, drow; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixUninterlaceGIF"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + wpl = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + for (k = 0, srow = 0; k < 4; k++) { + for (drow = InterlacedOffset[k]; drow < h; + drow += InterlacedJumps[k], srow++) { + lines = datas + srow * wpl; + lined = datad + drow * wpl; + for (j = 0; j < w; j++) + memcpy(lined, lines, 4 * wpl); + } + } + + return pixd; +} +#endif + + +/* -----------------------------------------------------------------*/ +#endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/gifiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/gifiostub.c new file mode 100644 index 0000000..6f69525 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/gifiostub.c @@ -0,0 +1,72 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file gifiostub.c + *
+ *
+ *     Stubs for gifio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* -----------------------------------------------------------------*/ +#if (!HAVE_LIBGIF) && (!HAVE_LIBUNGIF) /* defined in environ.h */ +/* -----------------------------------------------------------------*/ + +PIX * pixReadStreamGif(FILE *fp) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamGif", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemGif(const l_uint8 *cdata, size_t size) +{ + return (PIX *)ERROR_PTR("function not present", "pixReadMemGif", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamGif(FILE *fp, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteStreamGif", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemGif(l_uint8 **pdata, size_t *psize, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteMemGif", 1); +} + +/* -----------------------------------------------------------------*/ +#endif /* !HAVE_LIBGIF && !HAVE_LIBUNGIF */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/gplot.c b/hgdriver/3rdparty/hgOCR/leptonica/gplot.c new file mode 100644 index 0000000..108300a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/gplot.c @@ -0,0 +1,957 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + /*! + * \file gplot.c + *
+  *
+  *     Basic plotting functions
+  *          GPLOT      *gplotCreate()
+  *          void        gplotDestroy()
+  *          l_int32     gplotAddPlot()
+  *          l_int32     gplotSetScaling()
+  *          l_int32     gplotMakeOutput()
+  *          l_int32     gplotGenCommandFile()
+  *          l_int32     gplotGenDataFiles()
+  *
+  *     Quick and dirty plots
+  *          l_int32     gplotSimple1()
+  *          l_int32     gplotSimple2()
+  *          l_int32     gplotSimpleN()
+  *          l_int32     gplotSimpleXY1()
+  *          l_int32     gplotSimpleXY2()
+  *          l_int32     gplotSimpleXYN()
+  *
+  *     Serialize for I/O
+  *          GPLOT      *gplotRead()
+  *          l_int32     gplotWrite()
+  *
+  *
+  *     Utility for programmatic plotting using gnuplot 4.6 or later
+  *     Enabled:
+  *         ~ output to png (color), ps and eps (mono), latex (mono)
+  *         ~ optional title for graph
+  *         ~ optional x and y axis labels
+  *         ~ multiple plots on one frame
+  *         ~ optional title for each plot on the frame
+  *         ~ optional log scaling on either or both axes
+  *         ~ choice of 5 plot styles for each plot
+  *         ~ choice of 2 plot modes, either using one input array
+  *           (Y vs index) or two input arrays (Y vs X).  This
+  *           choice is made implicitly depending on the number of
+  *           input arrays.
+  *
+  *     Usage:
+  *         gplotCreate() initializes for plotting
+  *         gplotAddPlot() for each plot on the frame
+  *         gplotMakeOutput() to generate all output files and run gnuplot
+  *         gplotDestroy() to clean up
+  *
+  *     Example of use:
+  *         gplot = gplotCreate("tempskew", GPLOT_PNG, "Skew score vs angle",
+  *                    "angle (deg)", "score");
+  *         gplotAddPlot(gplot, natheta, nascore1, GPLOT_LINES, "plot 1");
+  *         gplotAddPlot(gplot, natheta, nascore2, GPLOT_POINTS, "plot 2");
+  *         gplotSetScaling(gplot, GPLOT_LOG_SCALE_Y);
+  *         gplotMakeOutput(gplot);
+  *         gplotDestroy(&gplot);
+  *
+  *     Note for output to GPLOT_LATEX:
+  *         This creates latex output of the plot, named .tex.
+  *         It needs to be placed in a latex file .tex
+  *         that precedes the plot output with, at a minimum:
+  *           \documentclass{article}
+  *           \begin{document}
+  *         and ends with
+  *           \end{document}
+  *         You can then generate a dvi file .dvi using
+  *           latex .tex
+  *         and a PostScript file .ps from that using
+  *           dvips -o .ps .dvi
+  *
+  *     N.B. To generate plots, it is necessary to have gnuplot installed on
+  *          your Unix system, or wgnuplot on Windows.
+  * 
+ */ + +#include +#include "allheaders.h" + +//static const l_int32 Bufsize = 512; /* hardcoded below in fscanf */ +#define Bufsize 512 + +const char *gplotstylenames[] = { "with lines", + "with points", + "with impulses", + "with linespoints", + "with dots" }; +const char *gplotfileoutputs[] = { "", + "PNG", + "PS", + "EPS", + "LATEX" }; + + +/*-----------------------------------------------------------------* + * Basic Plotting Functions * + *-----------------------------------------------------------------*/ + /*! + * \brief gplotCreate() + * + * \param[in] rootname root for all output files + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] title [optional] overall title + * \param[in] xlabel [optional] x axis label + * \param[in] ylabel [optional] y axis label + * \return gplot, or NULL on error + * + *
+  * Notes:
+  *      (1) This initializes the plot.
+  *      (2) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
+  *          double quotes and backquotes, but not single quotes.
+  * 
+ */ +GPLOT * +gplotCreate(const char *rootname, + l_int32 outformat, + const char *title, + const char *xlabel, + const char *ylabel) +{ + char *newroot; + char buf[Bufsize]; + l_int32 badchar; + GPLOT *gplot; + + PROCNAME("gplotCreate"); + + if (!rootname) + return (GPLOT *)ERROR_PTR("rootname not defined", procName, NULL); + if (outformat != GPLOT_PNG && outformat != GPLOT_PS && + outformat != GPLOT_EPS && outformat != GPLOT_LATEX) + return (GPLOT *)ERROR_PTR("outformat invalid", procName, NULL); + stringCheckForChars(rootname, "`;&|><\"?*$()", &badchar); + if (badchar) /* danger of command injection */ + return (GPLOT *)ERROR_PTR("invalid rootname", procName, NULL); + + gplot = (GPLOT *)LEPT_CALLOC(1, sizeof(GPLOT)); + gplot->cmddata = sarrayCreate(0); + gplot->datanames = sarrayCreate(0); + gplot->plotdata = sarrayCreate(0); + gplot->plottitles = sarrayCreate(0); + gplot->plotstyles = numaCreate(0); + + /* Save title, labels, rootname, outformat, cmdname, outname */ + newroot = genPathname(rootname, NULL); + gplot->rootname = newroot; + gplot->outformat = outformat; + snprintf(buf, Bufsize, "%s.cmd", rootname); + gplot->cmdname = stringNew(buf); + if (outformat == GPLOT_PNG) + snprintf(buf, Bufsize, "%s.png", newroot); + else if (outformat == GPLOT_PS) + snprintf(buf, Bufsize, "%s.ps", newroot); + else if (outformat == GPLOT_EPS) + snprintf(buf, Bufsize, "%s.eps", newroot); + else if (outformat == GPLOT_LATEX) + snprintf(buf, Bufsize, "%s.tex", newroot); + gplot->outname = stringNew(buf); + if (title) gplot->title = stringNew(title); + if (xlabel) gplot->xlabel = stringNew(xlabel); + if (ylabel) gplot->ylabel = stringNew(ylabel); + + return gplot; +} + + +/*! + * \brief gplotDestroy() + * + * \param[in,out] pgplot will be set to null before returning + */ +void +gplotDestroy(GPLOT **pgplot) +{ + GPLOT *gplot; + + PROCNAME("gplotDestroy"); + + if (pgplot == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((gplot = *pgplot) == NULL) + return; + + LEPT_FREE(gplot->rootname); + LEPT_FREE(gplot->cmdname); + sarrayDestroy(&gplot->cmddata); + sarrayDestroy(&gplot->datanames); + sarrayDestroy(&gplot->plotdata); + sarrayDestroy(&gplot->plottitles); + numaDestroy(&gplot->plotstyles); + LEPT_FREE(gplot->outname); + if (gplot->title) + LEPT_FREE(gplot->title); + if (gplot->xlabel) + LEPT_FREE(gplot->xlabel); + if (gplot->ylabel) + LEPT_FREE(gplot->ylabel); + + LEPT_FREE(gplot); + *pgplot = NULL; + return; +} + + +/*! + * \brief gplotAddPlot() + * + * \param[in] gplot + * \param[in] nax [optional] numa: set to null for Y_VS_I; + * required for Y_VS_X + * \param[in] nay numa; required for both Y_VS_I and Y_VS_X + * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, + * GPLOT_LINESPOINTS, GPLOT_DOTS + * \param[in] plottitle [optional] title for individual plot + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) There are 2 options for (x,y) values:
+ *            o  To plot an array vs a linear function of the
+ *               index, set %nax = NULL.
+ *            o  To plot one array vs another, use both %nax and %nay.
+ *      (2) If %nax is NULL, the x value corresponding to the i-th
+ *          value of %nay is found from the startx and delx fields
+ *          in %nay:
+ *               x = startx + i * delx
+ *          These are set with numaSetParameters().  Their default
+ *          values are startx = 0.0, delx = 1.0.
+ *      (3) If %nax is defined, it must be the same size as %nay, and
+ *          must have at least one number.
+ *      (4) The 'plottitle' string can have spaces, double
+ *          quotes and backquotes, but not single quotes.
+ * 
+ */ +l_ok +gplotAddPlot(GPLOT *gplot, + NUMA *nax, + NUMA *nay, + l_int32 plotstyle, + const char *plottitle) +{ + char buf[Bufsize]; + char emptystring[] = ""; + char *datastr, *title; + l_int32 n, i; + l_float32 valx, valy, startx, delx; + SARRAY *sa; + + PROCNAME("gplotAddPlot"); + + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) + return ERROR_INT("invalid plotstyle", procName, 1); + + if ((n = numaGetCount(nay)) == 0) + return ERROR_INT("no points to plot", procName, 1); + if (nax && (n != numaGetCount(nax))) + return ERROR_INT("nax and nay sizes differ", procName, 1); + if (n == 1 && plotstyle == GPLOT_LINES) { + L_INFO("only 1 pt; changing style to points\n", procName); + plotstyle = GPLOT_POINTS; + } + + /* Save plotstyle and plottitle */ + numaGetParameters(nay, &startx, &delx); + numaAddNumber(gplot->plotstyles, plotstyle); + if (plottitle) { + title = stringNew(plottitle); + sarrayAddString(gplot->plottitles, title, L_INSERT); + } + else { + sarrayAddString(gplot->plottitles, emptystring, L_COPY); + } + + /* Generate and save data filename */ + gplot->nplots++; + snprintf(buf, Bufsize, "%s.data.%d", gplot->rootname, gplot->nplots); + sarrayAddString(gplot->datanames, buf, L_COPY); + + /* Generate data and save as a string */ + sa = sarrayCreate(n); + for (i = 0; i < n; i++) { + if (nax) + numaGetFValue(nax, i, &valx); + else + valx = startx + i * delx; + numaGetFValue(nay, i, &valy); + snprintf(buf, Bufsize, "%f %f\n", valx, valy); + sarrayAddString(sa, buf, L_COPY); + } + datastr = sarrayToString(sa, 0); + sarrayAddString(gplot->plotdata, datastr, L_INSERT); + sarrayDestroy(&sa); + + return 0; +} + + +/*! + * \brief gplotSetScaling() + * + * \param[in] gplot + * \param[in] scaling GPLOT_LINEAR_SCALE, GPLOT_LOG_SCALE_X, + * GPLOT_LOG_SCALE_Y, GPLOT_LOG_SCALE_X_Y + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) By default, the x and y axis scaling is linear.
+ *      (2) Call this function to set semi-log or log-log scaling.
+ * 
+ */ +l_ok +gplotSetScaling(GPLOT *gplot, + l_int32 scaling) +{ + PROCNAME("gplotSetScaling"); + + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + if (scaling != GPLOT_LINEAR_SCALE && + scaling != GPLOT_LOG_SCALE_X && + scaling != GPLOT_LOG_SCALE_Y && + scaling != GPLOT_LOG_SCALE_X_Y) + return ERROR_INT("invalid gplot scaling", procName, 1); + gplot->scaling = scaling; + return 0; +} + + +/*! + * \brief gplotMakeOutput() + * + * \param[in] gplot + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This uses gplot and the new arrays to add a plot
+ *          to the output, by writing a new data file and appending
+ *          the appropriate plot commands to the command file.
+ *      (2) This is the only function in this file that requires the
+ *          gnuplot executable, to actually generate the plot.
+ *      (3) The command file name for unix is canonical (i.e., directory /tmp)
+ *          but the temp filename paths in the command file must be correct.
+ *      (4) The gnuplot program for windows is wgnuplot.exe.
+ * 
+ */ +l_ok +gplotMakeOutput(GPLOT *gplot) +{ + char buf[Bufsize]; + char *cmdname; + + PROCNAME("gplotMakeOutput"); + + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + + if (!LeptDebugOK) { + L_INFO("running gnuplot is disabled; " + "use setLeptDebugOK(1) to enable\n", procName); + return 0; + } + +#ifdef OS_IOS /* iOS 11 does not support system() */ + return ERROR_INT("iOS 11 does not support system()", procName, 0); +#endif /* OS_IOS */ + + gplotGenCommandFile(gplot); + gplotGenDataFiles(gplot); + cmdname = genPathname(gplot->cmdname, NULL); + +#ifndef _WIN32 + snprintf(buf, Bufsize, "gnuplot %s", cmdname); +#else + snprintf(buf, Bufsize, "wgnuplot %s", cmdname); +#endif /* _WIN32 */ + + callSystemDebug(buf); /* gnuplot || wgnuplot */ + LEPT_FREE(cmdname); + return 0; +} + + +/*! + * \brief gplotGenCommandFile() + * + * \param[in] gplot + * \return 0 if OK, 1 on error + */ +l_ok +gplotGenCommandFile(GPLOT *gplot) +{ + char buf[Bufsize]; + char *cmdstr, *plottitle, *dataname; + l_int32 i, plotstyle, nplots; + FILE *fp; + + PROCNAME("gplotGenCommandFile"); + + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + + /* Remove any previous command data */ + sarrayClear(gplot->cmddata); + + /* Generate command data instructions */ + if (gplot->title) { /* set title */ + snprintf(buf, Bufsize, "set title '%s'", gplot->title); + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + if (gplot->xlabel) { /* set xlabel */ + snprintf(buf, Bufsize, "set xlabel '%s'", gplot->xlabel); + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + if (gplot->ylabel) { /* set ylabel */ + snprintf(buf, Bufsize, "set ylabel '%s'", gplot->ylabel); + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + + /* Set terminal type and output */ + if (gplot->outformat == GPLOT_PNG) { + snprintf(buf, Bufsize, "set terminal png; set output '%s'", + gplot->outname); + } + else if (gplot->outformat == GPLOT_PS) { + snprintf(buf, Bufsize, "set terminal postscript; set output '%s'", + gplot->outname); + } + else if (gplot->outformat == GPLOT_EPS) { + snprintf(buf, Bufsize, + "set terminal postscript eps; set output '%s'", + gplot->outname); + } + else if (gplot->outformat == GPLOT_LATEX) { + snprintf(buf, Bufsize, "set terminal latex; set output '%s'", + gplot->outname); + } + sarrayAddString(gplot->cmddata, buf, L_COPY); + + if (gplot->scaling == GPLOT_LOG_SCALE_X || + gplot->scaling == GPLOT_LOG_SCALE_X_Y) { + snprintf(buf, Bufsize, "set logscale x"); + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + if (gplot->scaling == GPLOT_LOG_SCALE_Y || + gplot->scaling == GPLOT_LOG_SCALE_X_Y) { + snprintf(buf, Bufsize, "set logscale y"); + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + + nplots = sarrayGetCount(gplot->datanames); + for (i = 0; i < nplots; i++) { + plottitle = sarrayGetString(gplot->plottitles, i, L_NOCOPY); + dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY); + numaGetIValue(gplot->plotstyles, i, &plotstyle); + if (nplots == 1) { + snprintf(buf, Bufsize, "plot '%s' title '%s' %s", + dataname, plottitle, gplotstylenames[plotstyle]); + } + else { + if (i == 0) + snprintf(buf, Bufsize, "plot '%s' title '%s' %s, \\", + dataname, plottitle, gplotstylenames[plotstyle]); + else if (i < nplots - 1) + snprintf(buf, Bufsize, " '%s' title '%s' %s, \\", + dataname, plottitle, gplotstylenames[plotstyle]); + else + snprintf(buf, Bufsize, " '%s' title '%s' %s", + dataname, plottitle, gplotstylenames[plotstyle]); + } + sarrayAddString(gplot->cmddata, buf, L_COPY); + } + + /* Write command data to file */ + cmdstr = sarrayToString(gplot->cmddata, 1); + if ((fp = fopenWriteStream(gplot->cmdname, "w")) == NULL) { + LEPT_FREE(cmdstr); + return ERROR_INT("cmd stream not opened", procName, 1); + } + fwrite(cmdstr, 1, strlen(cmdstr), fp); + fclose(fp); + LEPT_FREE(cmdstr); + return 0; +} + + +/*! + * \brief gplotGenDataFiles() + * + * \param[in] gplot + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pathnames in the gplot command file are actual pathnames,
+ *          which can be in temp directories.  Consequently, they must not be
+ *          rewritten by calling fopenWriteStream(), and we use fopen().
+ * 
+ */ +l_ok +gplotGenDataFiles(GPLOT *gplot) +{ + char *plotdata, *dataname; + l_int32 i, nplots; + FILE *fp; + + PROCNAME("gplotGenDataFiles"); + + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + + nplots = sarrayGetCount(gplot->datanames); + for (i = 0; i < nplots; i++) { + plotdata = sarrayGetString(gplot->plotdata, i, L_NOCOPY); + dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY); + if ((fp = fopen(dataname, "w")) == NULL) + return ERROR_INT("datafile stream not opened", procName, 1); + fwrite(plotdata, 1, strlen(plotdata), fp); + fclose(fp); + } + + return 0; +} + + +/*-----------------------------------------------------------------* + * Quick and Dirty Plots * + *-----------------------------------------------------------------*/ + /*! + * \brief gplotSimple1() + * + * \param[in] na numa; plot Y_VS_I + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional], can be NULL + * \return 0 if OK, 1 on error + * + *
+  * Notes:
+  *      (1) This gives a line plot of a numa, where the array value
+  *          is plotted vs the array index.  The plot is generated
+  *          in the specified output format; the title  is optional.
+  *      (2) When calling these simple plot functions more than once, use
+  *          different %outroot to avoid overwriting the output files.
+  * 
+ */ +l_ok +gplotSimple1(NUMA *na, + l_int32 outformat, + const char *outroot, + const char *title) +{ + return gplotSimpleXY1(NULL, na, GPLOT_LINES, outformat, outroot, title); +} + + +/*! + * \brief gplotSimple2() + * + * \param[in] na1 numa; plot with Y_VS_I + * \param[in] na2 ditto + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives a line plot of two numa, where the array values
+ *          are each plotted vs the array index.  The plot is generated
+ *          in the specified output format; the title  is optional.
+ *      (2) When calling these simple plot functions more than once, use
+ *          different %outroot to avoid overwriting the output files.
+ * 
+ */ +l_ok +gplotSimple2(NUMA *na1, + NUMA *na2, + l_int32 outformat, + const char *outroot, + const char *title) +{ + return gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES, + outformat, outroot, title); +} + + +/*! + * \brief gplotSimpleN() + * + * \param[in] naa numaa; we plotted with Y_VS_I for each numa + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives a line plot of all numas in a numaa (array of numa),
+ *          where the array values are each plotted vs the array index.
+ *          The plot is generated in the specified output format;
+ *          the title  is optional.
+ *      (2) When calling these simple plot functions more than once, use
+ *          different %outroot to avoid overwriting the output files.
+ * 
+ */ +l_ok +gplotSimpleN(NUMAA *naa, + l_int32 outformat, + const char *outroot, + const char *title) +{ + return gplotSimpleXYN(NULL, naa, GPLOT_LINES, outformat, outroot, title); +} + + +/*! + * \brief gplotSimpleXY1() + * + * \param[in] nax [optional] + * \param[in] nay [required] + * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, + * GPLOT_LINESPOINTS, GPLOT_DOTS + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional], can be NULL + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives a plot of a %nay vs %nax, generated in
+ *          the specified output format.  The title is optional.
+ *      (2) Use 0 for default plotstyle (lines).
+ *      (3) %nax is optional.  If NULL, %nay is plotted against
+ *          the array index.
+ *      (4) When calling these simple plot functions more than once, use
+ *          different %outroot to avoid overwriting the output files.
+ * 
+ */ +l_ok +gplotSimpleXY1(NUMA *nax, + NUMA *nay, + l_int32 plotstyle, + l_int32 outformat, + const char *outroot, + const char *title) +{ + GPLOT *gplot; + + PROCNAME("gplotSimpleXY1"); + + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) + return ERROR_INT("invalid plotstyle", procName, 1); + if (outformat != GPLOT_PNG && outformat != GPLOT_PS && + outformat != GPLOT_EPS && outformat != GPLOT_LATEX) + return ERROR_INT("invalid outformat", procName, 1); + if (!outroot) + return ERROR_INT("outroot not specified", procName, 1); + + if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) + return ERROR_INT("gplot not made", procName, 1); + gplotAddPlot(gplot, nax, nay, plotstyle, NULL); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + return 0; +} + + +/*! + * \brief gplotSimpleXY2() + * + * \param[in] nax [optional], can be NULL + * \param[in] nay1 + * \param[in] nay2 + * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, + * GPLOT_LINESPOINTS, GPLOT_DOTS + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives plots of %nay1 and %nay2 against %nax, generated
+ *          in the specified output format.  The title is optional.
+ *      (2) Use 0 for default plotstyle (lines).
+ *      (3) %nax is optional.  If NULL, %nay1 and %nay2 are plotted
+ *          against the array index.
+ *      (4) When calling these simple plot functions more than once, use
+ *          different %outroot to avoid overwriting the output files.
+ * 
+ */ +l_ok +gplotSimpleXY2(NUMA *nax, + NUMA *nay1, + NUMA *nay2, + l_int32 plotstyle, + l_int32 outformat, + const char *outroot, + const char *title) +{ + GPLOT *gplot; + + PROCNAME("gplotSimpleXY2"); + + if (!nay1 || !nay2) + return ERROR_INT("nay1 and nay2 not both defined", procName, 1); + if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) + return ERROR_INT("invalid plotstyle", procName, 1); + if (outformat != GPLOT_PNG && outformat != GPLOT_PS && + outformat != GPLOT_EPS && outformat != GPLOT_LATEX) + return ERROR_INT("invalid outformat", procName, 1); + if (!outroot) + return ERROR_INT("outroot not specified", procName, 1); + + if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) + return ERROR_INT("gplot not made", procName, 1); + gplotAddPlot(gplot, nax, nay1, plotstyle, NULL); + gplotAddPlot(gplot, nax, nay2, plotstyle, NULL); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + return 0; +} + + +/*! + * \brief gplotSimpleXYN() + * + * \param[in] nax [optional]; can be NULL + * \param[in] naay numaa of arrays to plot against %nax + * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, + * GPLOT_LINESPOINTS, GPLOT_DOTS + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] outroot root of output files + * \param[in] title [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives plots of each Numa in %naa against %nax,
+ *          generated in the specified output format.  The title is optional.
+ *      (2) Use 0 for default plotstyle (lines).
+ *      (3) %nax is optional.  If NULL, each Numa array is plotted against
+ *          the array index.
+ *      (4) When calling these simple plot functions more than once, use
+ *          different %outroot to avoid overwriting the output files.
+ * 
+ */ +l_ok +gplotSimpleXYN(NUMA *nax, + NUMAA *naay, + l_int32 plotstyle, + l_int32 outformat, + const char *outroot, + const char *title) +{ + l_int32 i, n; + GPLOT *gplot; + NUMA *nay; + + PROCNAME("gplotSimpleXYN"); + + if (!naay) + return ERROR_INT("naay not defined", procName, 1); + if ((n = numaaGetCount(naay)) == 0) + return ERROR_INT("no numa in array", procName, 1); + if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) + return ERROR_INT("invalid plotstyle", procName, 1); + if (outformat != GPLOT_PNG && outformat != GPLOT_PS && + outformat != GPLOT_EPS && outformat != GPLOT_LATEX) + return ERROR_INT("invalid outformat", procName, 1); + if (!outroot) + return ERROR_INT("outroot not specified", procName, 1); + + if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) + return ERROR_INT("gplot not made", procName, 1); + for (i = 0; i < n; i++) { + nay = numaaGetNuma(naay, i, L_CLONE); + gplotAddPlot(gplot, nax, nay, plotstyle, NULL); + numaDestroy(&nay); + } + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + return 0; +} + + +/*-----------------------------------------------------------------* + * Serialize for I/O * + *-----------------------------------------------------------------*/ + /*! + * \brief gplotRead() + * + * \param[in] filename + * \return gplot, or NULL on error + */ +GPLOT * +gplotRead(const char *filename) +{ + char buf[Bufsize]; + char *rootname, *title, *xlabel, *ylabel, *ignores; + l_int32 outformat, ret, version, ignore; + FILE *fp; + GPLOT *gplot; + + PROCNAME("gplotRead"); + + if (!filename) + return (GPLOT *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (GPLOT *)ERROR_PTR("stream not opened", procName, NULL); + + ret = fscanf(fp, "Gplot Version %d\n", &version); + if (ret != 1) { + fclose(fp); + return (GPLOT *)ERROR_PTR("not a gplot file", procName, NULL); + } + if (version != GPLOT_VERSION_NUMBER) { + fclose(fp); + return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL); + } + + ignore = fscanf(fp, "Rootname: %511s\n", buf); /* Bufsize - 1 */ + rootname = stringNew(buf); + ignore = fscanf(fp, "Output format: %d\n", &outformat); + ignores = fgets(buf, Bufsize, fp); /* Title: ... */ + title = stringNew(buf + 7); + title[strlen(title) - 1] = '\0'; + ignores = fgets(buf, Bufsize, fp); /* X axis label: ... */ + xlabel = stringNew(buf + 14); + xlabel[strlen(xlabel) - 1] = '\0'; + ignores = fgets(buf, Bufsize, fp); /* Y axis label: ... */ + ylabel = stringNew(buf + 14); + ylabel[strlen(ylabel) - 1] = '\0'; + + gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel); + LEPT_FREE(rootname); + LEPT_FREE(title); + LEPT_FREE(xlabel); + LEPT_FREE(ylabel); + if (!gplot) { + fclose(fp); + return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); + } + sarrayDestroy(&gplot->cmddata); + sarrayDestroy(&gplot->datanames); + sarrayDestroy(&gplot->plotdata); + sarrayDestroy(&gplot->plottitles); + numaDestroy(&gplot->plotstyles); + + ignore = fscanf(fp, "Commandfile name: %511s\n", buf); /* Bufsize - 1 */ + stringReplace(&gplot->cmdname, buf); + ignore = fscanf(fp, "\nCommandfile data:"); + gplot->cmddata = sarrayReadStream(fp); + ignore = fscanf(fp, "\nDatafile names:"); + gplot->datanames = sarrayReadStream(fp); + ignore = fscanf(fp, "\nPlot data:"); + gplot->plotdata = sarrayReadStream(fp); + ignore = fscanf(fp, "\nPlot titles:"); + gplot->plottitles = sarrayReadStream(fp); + ignore = fscanf(fp, "\nPlot styles:"); + gplot->plotstyles = numaReadStream(fp); + + ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots); + ignore = fscanf(fp, "Output file name: %511s\n", buf); + stringReplace(&gplot->outname, buf); + ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling); + + fclose(fp); + return gplot; +} + + +/*! + * \brief gplotWrite() + * + * \param[in] filename + * \param[in] gplot + * \return 0 if OK; 1 on error + */ +l_ok +gplotWrite(const char *filename, + GPLOT *gplot) +{ + FILE *fp; + + PROCNAME("gplotWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!gplot) + return ERROR_INT("gplot not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + + fprintf(fp, "Gplot Version %d\n", GPLOT_VERSION_NUMBER); + fprintf(fp, "Rootname: %s\n", gplot->rootname); + fprintf(fp, "Output format: %d\n", gplot->outformat); + fprintf(fp, "Title: %s\n", gplot->title); + fprintf(fp, "X axis label: %s\n", gplot->xlabel); + fprintf(fp, "Y axis label: %s\n", gplot->ylabel); + + fprintf(fp, "Commandfile name: %s\n", gplot->cmdname); + fprintf(fp, "\nCommandfile data:"); + sarrayWriteStream(fp, gplot->cmddata); + fprintf(fp, "\nDatafile names:"); + sarrayWriteStream(fp, gplot->datanames); + fprintf(fp, "\nPlot data:"); + sarrayWriteStream(fp, gplot->plotdata); + fprintf(fp, "\nPlot titles:"); + sarrayWriteStream(fp, gplot->plottitles); + fprintf(fp, "\nPlot styles:"); + numaWriteStream(fp, gplot->plotstyles); + + fprintf(fp, "Number of plots: %d\n", gplot->nplots); + fprintf(fp, "Output file name: %s\n", gplot->outname); + fprintf(fp, "Axis scaling: %d\n", gplot->scaling); + + fclose(fp); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/gplot.h b/hgdriver/3rdparty/hgOCR/leptonica/gplot.h new file mode 100644 index 0000000..8cf6b86 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/gplot.h @@ -0,0 +1,95 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_GPLOT_H +#define LEPTONICA_GPLOT_H + +/*! + * \file gplot.h + * + *
+ *   Data structures and parameters for generating gnuplot files
+ *
+ *   We used to support X11 output, but recent versions of gnuplot do not
+ *   support the X11 terminal.  To get display to your screen, use
+ *   GPLOT_PNG output; e.g.,
+ *       gplotSimple1(na, GPLOT_PNG, "/tmp/someroot", ...);
+ *       l_fileDisplay("/tmp/someroot.png", ...);
+ * 
+ */ + +#define GPLOT_VERSION_NUMBER 1 + +#define NUM_GPLOT_STYLES 5 +enum GPLOT_STYLE { + GPLOT_LINES = 0, + GPLOT_POINTS = 1, + GPLOT_IMPULSES = 2, + GPLOT_LINESPOINTS = 3, + GPLOT_DOTS = 4 +}; + +#define NUM_GPLOT_OUTPUTS 5 +enum GPLOT_OUTPUT { + GPLOT_NONE = 0, + GPLOT_PNG = 1, + GPLOT_PS = 2, + GPLOT_EPS = 3, + GPLOT_LATEX = 4 +}; + +enum GPLOT_SCALING { + GPLOT_LINEAR_SCALE = 0, /*!< default */ + GPLOT_LOG_SCALE_X = 1, + GPLOT_LOG_SCALE_Y = 2, + GPLOT_LOG_SCALE_X_Y = 3 +}; + +extern const char *gplotstylenames[]; /*!< used in gnuplot cmd file */ +extern const char *gplotfileoutputs[]; /*!< used in simple file input */ + +/*! Data structure for generating gnuplot files */ +struct GPlot +{ + char *rootname; /*!< for cmd, data, output */ + char *cmdname; /*!< command file name */ + struct Sarray *cmddata; /*!< command file contents */ + struct Sarray *datanames; /*!< data file names */ + struct Sarray *plotdata; /*!< plot data (1 string/file) */ + struct Sarray *plottitles; /*!< title for each individual plot */ + struct Numa *plotstyles; /*!< plot style for individual plots */ + l_int32 nplots; /*!< current number of plots */ + char *outname; /*!< output file name */ + l_int32 outformat; /*!< GPLOT_OUTPUT values */ + l_int32 scaling; /*!< GPLOT_SCALING values */ + char *title; /*!< optional */ + char *xlabel; /*!< optional x axis label */ + char *ylabel; /*!< optional y axis label */ +}; +typedef struct GPlot GPLOT; + + +#endif /* LEPTONICA_GPLOT_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/graphics.c b/hgdriver/3rdparty/hgOCR/leptonica/graphics.c new file mode 100644 index 0000000..c746cfb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/graphics.c @@ -0,0 +1,2900 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file graphics.c + *
+ *
+ *      Pta generation for arbitrary shapes built with lines
+ *          PTA        *generatePtaLine()
+ *          PTA        *generatePtaWideLine()
+ *          PTA        *generatePtaBox()
+ *          PTA        *generatePtaBoxa()
+ *          PTA        *generatePtaHashBox()
+ *          PTA        *generatePtaHashBoxa()
+ *          PTAA       *generatePtaaBoxa()
+ *          PTAA       *generatePtaaHashBoxa()
+ *          PTA        *generatePtaPolyline()
+ *          PTA        *generatePtaGrid()
+ *          PTA        *convertPtaLineTo4cc()
+ *          PTA        *generatePtaFilledCircle()
+ *          PTA        *generatePtaFilledSquare()
+ *          PTA        *generatePtaLineFromPt()
+ *          l_int32     locatePtRadially()
+ *
+ *      Rendering function plots directly on images
+ *          l_int32     pixRenderPlotFromNuma()
+ *          l_int32     pixRenderPlotFromNumaGen()
+ *          PTA        *makePlotPtaFromNuma()
+ *          PTA        *makePlotPtaFromNumaGen()
+ *
+ *      Pta rendering
+ *          l_int32     pixRenderPta()
+ *          l_int32     pixRenderPtaArb()
+ *          l_int32     pixRenderPtaBlend()
+ *
+ *      Rendering of arbitrary shapes built with lines
+ *          l_int32     pixRenderLine()
+ *          l_int32     pixRenderLineArb()
+ *          l_int32     pixRenderLineBlend()
+ *
+ *          l_int32     pixRenderBox()
+ *          l_int32     pixRenderBoxArb()
+ *          l_int32     pixRenderBoxBlend()
+ *
+ *          l_int32     pixRenderBoxa()
+ *          l_int32     pixRenderBoxaArb()
+ *          l_int32     pixRenderBoxaBlend()
+ *
+ *          l_int32     pixRenderHashBox()
+ *          l_int32     pixRenderHashBoxArb()
+ *          l_int32     pixRenderHashBoxBlend()
+ *          l_int32     pixRenderHashMaskArb()
+ *
+ *          l_int32     pixRenderHashBoxa()
+ *          l_int32     pixRenderHashBoxaArb()
+ *          l_int32     pixRenderHashBoxaBlend()
+ *
+ *          l_int32     pixRenderPolyline()
+ *          l_int32     pixRenderPolylineArb()
+ *          l_int32     pixRenderPolylineBlend()
+ *
+ *          l_int32     pixRenderGrid()
+ *
+ *          l_int32     pixRenderRandomCmapPtaa()
+ *
+ *      Rendering and filling of polygons
+ *          PIX        *pixRenderPolygon()
+ *          PIX        *pixFillPolygon()
+ *
+ *      Contour rendering on grayscale images
+ *          PIX        *pixRenderContours()
+ *          PIX        *fpixAutoRenderContours()
+ *          PIX        *fpixRenderContours()
+ *
+ *      Boundary pt generation on 1 bpp images
+ *          PTA        *pixGeneratePtaBoundary()
+ *
+ *  The line rendering functions are relatively crude, but they
+ *  get the job done for most simple situations.  We use the pta
+ *  (array of points) as an intermediate data structure.  For example,
+ *  to render a line we first generate a pta.
+ *
+ *  Some rendering functions come in sets of three.  For example
+ *       pixRenderLine() -- render on 1 bpp pix
+ *       pixRenderLineArb() -- render on 32 bpp pix with arbitrary (r,g,b)
+ *       pixRenderLineBlend() -- render on 32 bpp pix, blending the
+ *               (r,g,b) graphic object with the underlying rgb pixels.
+ *
+ *  There are also procedures for plotting a function, computed
+ *  from the row or column pixels, directly on the image.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + +/*------------------------------------------------------------------* + * Pta generation for arbitrary shapes built with lines * + *------------------------------------------------------------------*/ +/*! + * \brief generatePtaLine() + * + * \param[in] x1, y1 end point 1 + * \param[in] x2, y2 end point 2 + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Uses Bresenham line drawing, which results in an 8-connected line.
+ * 
+ */ +PTA * +generatePtaLine(l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2) +{ +l_int32 npts, diff, getyofx, sign, i, x, y; +l_float32 slope; +PTA *pta; + + PROCNAME("generatePtaLine"); + + /* Generate line parameters */ + if (x1 == x2 && y1 == y2) { /* same point */ + getyofx = TRUE; + npts = 1; + } else if (L_ABS(x2 - x1) >= L_ABS(y2 - y1)) { + getyofx = TRUE; + npts = L_ABS(x2 - x1) + 1; + diff = x2 - x1; + sign = L_SIGN(x2 - x1); + slope = (l_float32)(sign * (y2 - y1)) / (l_float32)diff; + } else { + getyofx = FALSE; + npts = L_ABS(y2 - y1) + 1; + diff = y2 - y1; + sign = L_SIGN(y2 - y1); + slope = (l_float32)(sign * (x2 - x1)) / (l_float32)diff; + } + + if ((pta = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + + if (npts == 1) { /* degenerate case */ + ptaAddPt(pta, x1, y1); + return pta; + } + + /* Generate the set of points */ + if (getyofx) { /* y = y(x) */ + for (i = 0; i < npts; i++) { + x = x1 + sign * i; + y = (l_int32)(y1 + (l_float32)i * slope + 0.5); + ptaAddPt(pta, x, y); + } + } else { /* x = x(y) */ + for (i = 0; i < npts; i++) { + x = (l_int32)(x1 + (l_float32)i * slope + 0.5); + y = y1 + sign * i; + ptaAddPt(pta, x, y); + } + } + + return pta; +} + + +/*! + * \brief generatePtaWideLine() + * + * \param[in] x1, y1 end point 1 + * \param[in] x2, y2 end point 2 + * \param[in] width + * \return ptaj, or NULL on error + */ +PTA * +generatePtaWideLine(l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 width) +{ +l_int32 i, x1a, x2a, y1a, y2a; +PTA *pta, *ptaj; + + PROCNAME("generatePtaWideLine"); + + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((ptaj = generatePtaLine(x1, y1, x2, y2)) == NULL) + return (PTA *)ERROR_PTR("ptaj not made", procName, NULL); + if (width == 1) + return ptaj; + + /* width > 1; estimate line direction & join */ + if (L_ABS(x1 - x2) > L_ABS(y1 - y2)) { /* "horizontal" line */ + for (i = 1; i < width; i++) { + if ((i & 1) == 1) { /* place above */ + y1a = y1 - (i + 1) / 2; + y2a = y2 - (i + 1) / 2; + } else { /* place below */ + y1a = y1 + (i + 1) / 2; + y2a = y2 + (i + 1) / 2; + } + if ((pta = generatePtaLine(x1, y1a, x2, y2a)) != NULL) { + ptaJoin(ptaj, pta, 0, -1); + ptaDestroy(&pta); + } + } + } else { /* "vertical" line */ + for (i = 1; i < width; i++) { + if ((i & 1) == 1) { /* place to left */ + x1a = x1 - (i + 1) / 2; + x2a = x2 - (i + 1) / 2; + } else { /* place to right */ + x1a = x1 + (i + 1) / 2; + x2a = x2 + (i + 1) / 2; + } + if ((pta = generatePtaLine(x1a, y1, x2a, y2)) != NULL) { + ptaJoin(ptaj, pta, 0, -1); + ptaDestroy(&pta); + } + } + } + + return ptaj; +} + + +/*! + * \brief generatePtaBox() + * + * \param[in] box + * \param[in] width of line + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) Because the box is constructed so that we don't have any
+ *          overlapping lines, there is no need to remove duplicates.
+ * 
+ */ +PTA * +generatePtaBox(BOX *box, + l_int32 width) +{ +l_int32 x, y, w, h; +PTA *ptad, *pta; + + PROCNAME("generatePtaBox"); + + if (!box) + return (PTA *)ERROR_PTR("box not defined", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + /* Generate line points and add them to the pta. */ + boxGetGeometry(box, &x, &y, &w, &h); + if (w == 0 || h == 0) + return (PTA *)ERROR_PTR("box has w = 0 or h = 0", procName, NULL); + ptad = ptaCreate(0); + if ((width & 1) == 1) { /* odd width */ + pta = generatePtaWideLine(x - width / 2, y, + x + w - 1 + width / 2, y, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x + w - 1, y + 1 + width / 2, + x + w - 1, y + h - 2 - width / 2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x + w - 1 + width / 2, y + h - 1, + x - width / 2, y + h - 1, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x, y + h - 2 - width / 2, + x, y + 1 + width / 2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } else { /* even width */ + pta = generatePtaWideLine(x - width / 2, y, + x + w - 2 + width / 2, y, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x + w - 1, y + 0 + width / 2, + x + w - 1, y + h - 2 - width / 2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x + w - 2 + width / 2, y + h - 1, + x - width / 2, y + h - 1, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + pta = generatePtaWideLine(x, y + h - 2 - width / 2, + x, y + 0 + width / 2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + + return ptad; +} + + +/*! + * \brief generatePtaBoxa() + * + * \param[in] boxa + * \param[in] width + * \param[in] removedups 1 to remove, 0 to leave + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) If %boxa has overlapping boxes, and if blending will
+ *          be used to give a transparent effect, transparency
+ *          artifacts at line intersections can be removed using
+ *          %removedups = 1.
+ * 
+ */ +PTA * +generatePtaBoxa(BOXA *boxa, + l_int32 width, + l_int32 removedups) +{ +l_int32 i, n; +BOX *box; +PTA *ptad, *ptat, *pta; + + PROCNAME("generatePtaBoxa"); + + if (!boxa) + return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + n = boxaGetCount(boxa); + ptat = ptaCreate(0); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pta = generatePtaBox(box, width); + ptaJoin(ptat, pta, 0, -1); + ptaDestroy(&pta); + boxDestroy(&box); + } + + if (removedups) + ptad = ptaRemoveDupsByAset(ptat); + else + ptad = ptaClone(ptat); + + ptaDestroy(&ptat); + return ptad; +} + + +/*! + * \brief generatePtaHashBox() + * + * \param[in] box + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width of line + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
+ *          slope +1, slope -1).
+ *      (2) The full outline is also drawn if %outline = 1.
+ * 
+ */ +PTA * +generatePtaHashBox(BOX *box, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline) +{ +l_int32 bx, by, bh, bw, x, y, x1, y1, x2, y2, i, n, npts; +PTA *ptad, *pta; + + PROCNAME("generatePtaHashBox"); + + if (!box) + return (PTA *)ERROR_PTR("box not defined", procName, NULL); + if (spacing <= 1) + return (PTA *)ERROR_PTR("spacing not > 1", procName, NULL); + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return (PTA *)ERROR_PTR("invalid line orientation", procName, NULL); + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (bw == 0 || bh == 0) + return (PTA *)ERROR_PTR("box has bw = 0 or bh = 0", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + /* Generate line points and add them to the pta. */ + ptad = ptaCreate(0); + if (outline) { + pta = generatePtaBox(box, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + if (orient == L_HORIZONTAL_LINE) { + n = 1 + bh / spacing; + for (i = 0; i < n; i++) { + y = by + (i * (bh - 1)) / (n - 1); + pta = generatePtaWideLine(bx, y, bx + bw - 1, y, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + } else if (orient == L_VERTICAL_LINE) { + n = 1 + bw / spacing; + for (i = 0; i < n; i++) { + x = bx + (i * (bw - 1)) / (n - 1); + pta = generatePtaWideLine(x, by, x, by + bh - 1, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + } else if (orient == L_POS_SLOPE_LINE) { + n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); + for (i = 0; i < n; i++) { + x = (l_int32)(bx + (i + 0.5) * 1.4 * spacing); + boxIntersectByLine(box, x, by - 1, 1.0, &x1, &y1, &x2, &y2, &npts); + if (npts == 2) { + pta = generatePtaWideLine(x1, y1, x2, y2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + } + } else { /* orient == L_NEG_SLOPE_LINE */ + n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); + for (i = 0; i < n; i++) { + x = (l_int32)(bx - bh + (i + 0.5) * 1.4 * spacing); + boxIntersectByLine(box, x, by - 1, -1.0, &x1, &y1, &x2, &y2, &npts); + if (npts == 2) { + pta = generatePtaWideLine(x1, y1, x2, y2, width); + ptaJoin(ptad, pta, 0, -1); + ptaDestroy(&pta); + } + } + } + + return ptad; +} + + +/*! + * \brief generatePtaHashBoxa() + * + * \param[in] boxa + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width of line + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \param[in] removedups 1 to remove, 0 to leave + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
+ *          slope +1, slope -1).
+ *      (2) The full outline is also drawn if %outline = 1.
+ *      (3) If the boxa has overlapping boxes, and if blending will
+ *          be used to give a transparent effect, transparency
+ *          artifacts at line intersections can be removed using
+ *          %removedups = 1.
+ * 
+ */ +PTA * +generatePtaHashBoxa(BOXA *boxa, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 removedups) +{ +l_int32 i, n; +BOX *box; +PTA *ptad, *ptat, *pta; + + PROCNAME("generatePtaHashBoxa"); + + if (!boxa) + return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); + if (spacing <= 1) + return (PTA *)ERROR_PTR("spacing not > 1", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return (PTA *)ERROR_PTR("invalid line orientation", procName, NULL); + + n = boxaGetCount(boxa); + ptat = ptaCreate(0); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pta = generatePtaHashBox(box, spacing, width, orient, outline); + ptaJoin(ptat, pta, 0, -1); + ptaDestroy(&pta); + boxDestroy(&box); + } + + if (removedups) + ptad = ptaRemoveDupsByAset(ptat); + else + ptad = ptaClone(ptat); + + ptaDestroy(&ptat); + return ptad; +} + + +/*! + * \brief generatePtaaBoxa() + * + * \param[in] boxa + * \return ptaa, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a pta of the four corners for each box in
+ *          the boxa.
+ *      (2) Each of these pta can be rendered onto a pix with random colors,
+ *          by using pixRenderRandomCmapPtaa() with closeflag = 1.
+ * 
+ */ +PTAA * +generatePtaaBoxa(BOXA *boxa) +{ +l_int32 i, n, x, y, w, h; +BOX *box; +PTA *pta; +PTAA *ptaa; + + PROCNAME("generatePtaaBoxa"); + + if (!boxa) + return (PTAA *)ERROR_PTR("boxa not defined", procName, NULL); + + n = boxaGetCount(boxa); + ptaa = ptaaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + pta = ptaCreate(4); + ptaAddPt(pta, x, y); + ptaAddPt(pta, x + w - 1, y); + ptaAddPt(pta, x + w - 1, y + h - 1); + ptaAddPt(pta, x, y + h - 1); + ptaaAddPta(ptaa, pta, L_INSERT); + boxDestroy(&box); + } + + return ptaa; +} + + +/*! + * \brief generatePtaaHashBoxa() + * + * \param[in] boxa + * \param[in] spacing spacing between hash lines; must be > 1 + * \param[in] width hash line width + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \return ptaa, or NULL on error + * + *
+ * Notes:
+ *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
+ *          slope +1, slope -1).
+ *      (2) The full outline is also drawn if %outline = 1.
+ *      (3) Each of these pta can be rendered onto a pix with random colors,
+ *          by using pixRenderRandomCmapPtaa() with closeflag = 1.
+ *
+ * 
+ */ +PTAA * +generatePtaaHashBoxa(BOXA *boxa, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline) +{ +l_int32 i, n; +BOX *box; +PTA *pta; +PTAA *ptaa; + + PROCNAME("generatePtaaHashBoxa"); + + if (!boxa) + return (PTAA *)ERROR_PTR("boxa not defined", procName, NULL); + if (spacing <= 1) + return (PTAA *)ERROR_PTR("spacing not > 1", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return (PTAA *)ERROR_PTR("invalid line orientation", procName, NULL); + + n = boxaGetCount(boxa); + ptaa = ptaaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pta = generatePtaHashBox(box, spacing, width, orient, outline); + ptaaAddPta(ptaa, pta, L_INSERT); + boxDestroy(&box); + } + + return ptaa; +} + + +/*! + * \brief generatePtaPolyline() + * + * \param[in] ptas vertices of polyline + * \param[in] width + * \param[in] closeflag 1 to close the contour; 0 otherwise + * \param[in] removedups 1 to remove, 0 to leave + * \return ptad, or NULL on error + */ +PTA * +generatePtaPolyline(PTA *ptas, + l_int32 width, + l_int32 closeflag, + l_int32 removedups) +{ +l_int32 i, n, x1, y1, x2, y2; +PTA *ptad, *ptat, *pta; + + PROCNAME("generatePtaPolyline"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + n = ptaGetCount(ptas); + ptat = ptaCreate(0); + if (n < 2) /* nothing to do */ + return ptat; + + ptaGetIPt(ptas, 0, &x1, &y1); + for (i = 1; i < n; i++) { + ptaGetIPt(ptas, i, &x2, &y2); + pta = generatePtaWideLine(x1, y1, x2, y2, width); + ptaJoin(ptat, pta, 0, -1); + ptaDestroy(&pta); + x1 = x2; + y1 = y2; + } + + if (closeflag) { + ptaGetIPt(ptas, 0, &x2, &y2); + pta = generatePtaWideLine(x1, y1, x2, y2, width); + ptaJoin(ptat, pta, 0, -1); + ptaDestroy(&pta); + } + + if (removedups) + ptad = ptaRemoveDupsByAset(ptat); + else + ptad = ptaClone(ptat); + + ptaDestroy(&ptat); + return ptad; +} + + +/*! + * \brief generatePtaGrid() + * + * \param[in] w, h of region where grid will be displayed + * \param[in] nx, ny number of rectangles in each direction in grid + * \param[in] width of rendered lines + * \return ptad, or NULL on error + */ +PTA * +generatePtaGrid(l_int32 w, + l_int32 h, + l_int32 nx, + l_int32 ny, + l_int32 width) +{ +l_int32 i, j, bx, by, x1, x2, y1, y2; +BOX *box; +BOXA *boxa; +PTA *pta; + + PROCNAME("generatePtaGrid"); + + if (nx < 1 || ny < 1) + return (PTA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); + if (w < 2 * nx || h < 2 * ny) + return (PTA *)ERROR_PTR("w and/or h too small", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + boxa = boxaCreate(nx * ny); + bx = (w + nx - 1) / nx; + by = (h + ny - 1) / ny; + for (i = 0; i < ny; i++) { + y1 = by * i; + y2 = L_MIN(y1 + by, h - 1); + for (j = 0; j < nx; j++) { + x1 = bx * j; + x2 = L_MIN(x1 + bx, w - 1); + box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + boxaAddBox(boxa, box, L_INSERT); + } + } + + pta = generatePtaBoxa(boxa, width, 1); + boxaDestroy(&boxa); + return pta; +} + + +/*! + * \brief convertPtaLineTo4cc() + * + * \param[in] ptas 8-connected line of points + * \return ptad 4-connected line, or NULL on error + * + *
+ * Notes:
+ *      (1) When a polyline is generated with width = 1, the resulting
+ *          line is not 4-connected in general.  This function adds
+ *          points as necessary to convert the line to 4-cconnected.
+ *          It is useful when rendering 1 bpp on a pix.
+ *      (2) Do not use this for lines generated with width > 1.
+ * 
+ */ +PTA * +convertPtaLineTo4cc(PTA *ptas) +{ +l_int32 i, n, x, y, xp, yp; +PTA *ptad; + + PROCNAME("convertPtaLineTo4cc"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + ptaGetIPt(ptas, 0, &xp, &yp); + ptaAddPt(ptad, xp, yp); + for (i = 1; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + if (x != xp && y != yp) /* diagonal */ + ptaAddPt(ptad, x, yp); + ptaAddPt(ptad, x, y); + xp = x; + yp = y; + } + + return ptad; +} + + +/*! + * \brief generatePtaFilledCircle() + * + * \param[in] radius + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) The circle is has diameter = 2 * radius + 1.
+ *      (2) It is located with the center of the circle at the
+ *          point (%radius, %radius).
+ *      (3) Consequently, it typically must be translated if
+ *          it is to represent a set of pixels in an image.
+ * 
+ */ +PTA * +generatePtaFilledCircle(l_int32 radius) +{ +l_int32 x, y; +l_float32 radthresh, sqdist; +PTA *pta; + + PROCNAME("generatePtaFilledCircle"); + + if (radius < 1) + return (PTA *)ERROR_PTR("radius must be >= 1", procName, NULL); + + pta = ptaCreate(0); + radthresh = (radius + 0.5) * (radius + 0.5); + for (y = 0; y <= 2 * radius; y++) { + for (x = 0; x <= 2 * radius; x++) { + sqdist = (l_float32)((y - radius) * (y - radius) + + (x - radius) * (x - radius)); + if (sqdist <= radthresh) + ptaAddPt(pta, x, y); + } + } + + return pta; +} + + +/*! + * \brief generatePtaFilledSquare() + * + * \param[in] side + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) The center of the square can be chosen to be at
+ *          (side / 2, side / 2).  It must be translated by this amount
+ *          when used for replication.
+ * 
+ */ +PTA * +generatePtaFilledSquare(l_int32 side) +{ +l_int32 x, y; +PTA *pta; + + PROCNAME("generatePtaFilledSquare"); + if (side < 1) + return (PTA *)ERROR_PTR("side must be > 0", procName, NULL); + + pta = ptaCreate(0); + for (y = 0; y < side; y++) + for (x = 0; x < side; x++) + ptaAddPt(pta, x, y); + + return pta; +} + + +/*! + * \brief generatePtaLineFromPt() + * + * \param[in] x, y point of origination + * \param[in] length of line, including starting point + * \param[in] radang angle in radians, CW from horizontal + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) %length of the line is 1 greater than the distance
+ *          used in locatePtRadially().  Example: a distance of 1
+ *          gives rise to a length of 2.
+ * 
+ */ +PTA * +generatePtaLineFromPt(l_int32 x, + l_int32 y, + l_float64 length, + l_float64 radang) +{ +l_int32 x2, y2; /* the point at the other end of the line */ + + x2 = x + (l_int32)((length - 1.0) * cos(radang)); + y2 = y + (l_int32)((length - 1.0) * sin(radang)); + return generatePtaLine(x, y, x2, y2); +} + + +/*! + * \brief locatePtRadially() + * + * \param[in] xr, yr reference point + * \param[in] radang angle in radians, CW from horizontal + * \param[in] dist distance of point from reference point along + * line given by the specified angle + * \param[out] px, py location of point + * \return 0 if OK, 1 on error + */ +l_ok +locatePtRadially(l_int32 xr, + l_int32 yr, + l_float64 dist, + l_float64 radang, + l_float64 *px, + l_float64 *py) +{ + PROCNAME("locatePtRadially"); + + if (!px || !py) + return ERROR_INT("&x and &y not both defined", procName, 1); + + *px = xr + dist * cos(radang); + *py = yr + dist * sin(radang); + return 0; +} + + +/*------------------------------------------------------------------* + * Rendering function plots directly on images * + *------------------------------------------------------------------*/ +/*! + * \brief pixRenderPlotFromNuma() + * + * \param[in,out] ppix any type; replaced if not 32 bpp rgb + * \param[in] na to be plotted + * \param[in] plotloc location of plot: L_PLOT_AT_TOP, etc + * \param[in] linewidth width of "line" that is drawn; between 1 and 7 + * \param[in] max maximum excursion in pixels from baseline + * \param[in] color plot color: 0xrrggbb00 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simplified interface for plotting row or column aligned data
+ *          on a pix.
+ *      (2) This replaces %pix with a 32 bpp rgb version if it is not
+ *          already 32 bpp.  It then draws the plot on the pix.
+ *      (3) See makePlotPtaFromNumaGen() for more details.
+ * 
+ */ +l_ok +pixRenderPlotFromNuma(PIX **ppix, + NUMA *na, + l_int32 plotloc, + l_int32 linewidth, + l_int32 max, + l_uint32 color) +{ +l_int32 w, h, size, rval, gval, bval; +PIX *pix1; +PTA *pta; + + PROCNAME("pixRenderPlotFromNuma"); + + if (!ppix) + return ERROR_INT("&pix not defined", procName, 1); + if (*ppix == NULL) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(*ppix, &w, &h, NULL); + size = (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || + plotloc == L_PLOT_AT_BOT) ? h : w; + pta = makePlotPtaFromNuma(na, size, plotloc, linewidth, max); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + + if (pixGetDepth(*ppix) != 32) { + pix1 = pixConvertTo32(*ppix); + pixDestroy(ppix); + *ppix = pix1; + } + extractRGBValues(color, &rval, &gval, &bval); + pixRenderPtaArb(*ppix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief makePlotPtaFromNuma() + * + * \param[in] na + * \param[in] size pix height for horizontal plot; pix width + * for vertical plot + * \param[in] plotloc location of plot: L_PLOT_AT_TOP, etc + * \param[in] linewidth width of "line" that is drawn; between 1 and 7 + * \param[in] max maximum excursion in pixels from baseline + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates points from %numa representing y(x) or x(y)
+ *          with respect to a pix.  A horizontal plot y(x) is drawn for
+ *          a function of column position, and a vertical plot is drawn
+ *          for a function x(y) of row position.  The baseline is located
+ *          so that all plot points will fit in the pix.
+ *      (2) See makePlotPtaFromNumaGen() for more details.
+ * 
+ */ +PTA * +makePlotPtaFromNuma(NUMA *na, + l_int32 size, + l_int32 plotloc, + l_int32 linewidth, + l_int32 max) +{ +l_int32 orient, refpos; + + PROCNAME("makePlotPtaFromNuma"); + + if (!na) + return (PTA *)ERROR_PTR("na not defined", procName, NULL); + if (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || + plotloc == L_PLOT_AT_BOT) { + orient = L_HORIZONTAL_LINE; + } else if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_MID_VERT || + plotloc == L_PLOT_AT_RIGHT) { + orient = L_VERTICAL_LINE; + } else { + return (PTA *)ERROR_PTR("invalid plotloc", procName, NULL); + } + + if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_TOP) + refpos = max; + else if (plotloc == L_PLOT_AT_MID_VERT || plotloc == L_PLOT_AT_MID_HORIZ) + refpos = size / 2; + else /* L_PLOT_AT_RIGHT || L_PLOT_AT_BOT */ + refpos = size - max - 1; + + return makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, 1); +} + + +/*! + * \brief pixRenderPlotFromNumaGen() + * + * \param[in,out] ppix any type; replaced if not 32 bpp rgb + * \param[in] na to be plotted + * \param[in] orient L_HORIZONTAL_LINE, L_VERTICAL_LINE + * \param[in] linewidth width of "line" that is drawn; between 1 and 7 + * \param[in] refpos reference position: y for horizontal; + * x for vertical + * \param[in] max maximum excursion in pixels from baseline + * \param[in] drawref 1 to draw the reference line and its normal + * \param[in] color plot color: 0xrrggbb00 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) General interface for plotting row or column aligned data
+ *          on a pix.
+ *      (2) This replaces %pix with a 32 bpp rgb version if it is not
+ *          already 32 bpp.  It then draws the plot on the pix.
+ *      (3) See makePlotPtaFromNumaGen() for other input parameters.
+ * 
+ */ +l_ok +pixRenderPlotFromNumaGen(PIX **ppix, + NUMA *na, + l_int32 orient, + l_int32 linewidth, + l_int32 refpos, + l_int32 max, + l_int32 drawref, + l_uint32 color) +{ +l_int32 rval, gval, bval; +PIX *pix1; +PTA *pta; + + PROCNAME("pixRenderPlotFromNumaGen"); + + if (!ppix) + return ERROR_INT("&pix not defined", procName, 1); + if (*ppix == NULL) + return ERROR_INT("pix not defined", procName, 1); + + pta = makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, drawref); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + + if (pixGetDepth(*ppix) != 32) { + pix1 = pixConvertTo32(*ppix); + pixDestroy(ppix); + *ppix = pix1; + } + extractRGBValues(color, &rval, &gval, &bval); + pixRenderPtaArb(*ppix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief makePlotPtaFromNumaGen() + * + * \param[in] na + * \param[in] orient L_HORIZONTAL_LINE, L_VERTICAL_LINE + * \param[in] linewidth width of "line" that is drawn; between 1 and 7 + * \param[in] refpos reference position: y for horizontal; + * x for vertical + * \param[in] max maximum excursion in pixels from baseline + * \param[in] drawref 1 to draw the reference line and its normal + * \return ptad, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates points from %numa representing y(x) or x(y)
+ *          with respect to a pix.  For y(x), we draw a horizontal line
+ *          at the reference position and a vertical line at the edge; then
+ *          we draw the values of %numa, scaled so that the maximum
+ *          excursion from the reference position is %max pixels.
+ *      (2) The start and delx parameters of %numa are used to refer
+ *          its values to the raster lines (L_VERTICAL_LINE) or columns
+ *          (L_HORIZONTAL_LINE).
+ *      (3) The linewidth is chosen in the interval [1 ... 7].
+ *      (4) %refpos should be chosen so the plot is entirely within the pix
+ *          that it will be painted onto.
+ *      (5) This would typically be used to plot, in place, a function
+ *          computed along pixel rows or columns.
+ * 
+ */ +PTA * +makePlotPtaFromNumaGen(NUMA *na, + l_int32 orient, + l_int32 linewidth, + l_int32 refpos, + l_int32 max, + l_int32 drawref) +{ +l_int32 i, n, maxw, maxh; +l_float32 minval, maxval, absval, val, scale, start, del; +PTA *pta1, *pta2, *ptad; + + PROCNAME("makePlotPtaFromNumaGen"); + + if (!na) + return (PTA *)ERROR_PTR("na not defined", procName, NULL); + if (orient != L_HORIZONTAL_LINE && orient != L_VERTICAL_LINE) + return (PTA *)ERROR_PTR("invalid orient", procName, NULL); + if (linewidth < 1) { + L_WARNING("linewidth < 1; setting to 1\n", procName); + linewidth = 1; + } + if (linewidth > 7) { + L_WARNING("linewidth > 7; setting to 7\n", procName); + linewidth = 7; + } + + numaGetMin(na, &minval, NULL); + numaGetMax(na, &maxval, NULL); + absval = L_MAX(L_ABS(minval), L_ABS(maxval)); + scale = (l_float32)max / (l_float32)absval; + n = numaGetCount(na); + numaGetParameters(na, &start, &del); + + /* Generate the plot points */ + pta1 = ptaCreate(n); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (orient == L_HORIZONTAL_LINE) { + ptaAddPt(pta1, start + i * del, refpos + scale * val); + maxw = (del >= 0) ? start + n * del + linewidth + : start + linewidth; + maxh = refpos + max + linewidth; + } else { /* vertical line */ + ptaAddPt(pta1, refpos + scale * val, start + i * del); + maxw = refpos + max + linewidth; + maxh = (del >= 0) ? start + n * del + linewidth + : start + linewidth; + } + } + + /* Optionally, widen the plot */ + if (linewidth > 1) { + if (linewidth % 2 == 0) /* even linewidth; use side of a square */ + pta2 = generatePtaFilledSquare(linewidth); + else /* odd linewidth; use radius of a circle */ + pta2 = generatePtaFilledCircle(linewidth / 2); + ptad = ptaReplicatePattern(pta1, NULL, pta2, linewidth / 2, + linewidth / 2, maxw, maxh); + ptaDestroy(&pta2); + } else { + ptad = ptaClone(pta1); + } + ptaDestroy(&pta1); + + /* Optionally, add the reference lines */ + if (drawref) { + if (orient == L_HORIZONTAL_LINE) { + pta1 = generatePtaLine(start, refpos, start + n * del, refpos); + ptaJoin(ptad, pta1, 0, -1); + ptaDestroy(&pta1); + pta1 = generatePtaLine(start, refpos - max, + start, refpos + max); + ptaJoin(ptad, pta1, 0, -1); + } else { /* vertical line */ + pta1 = generatePtaLine(refpos, start, refpos, start + n * del); + ptaJoin(ptad, pta1, 0, -1); + ptaDestroy(&pta1); + pta1 = generatePtaLine(refpos - max, start, + refpos + max, start); + ptaJoin(ptad, pta1, 0, -1); + } + ptaDestroy(&pta1); + } + + return ptad; +} + + +/*------------------------------------------------------------------* + * Pta generation for arbitrary shapes built with lines * + *------------------------------------------------------------------*/ +/*! + * \brief pixRenderPta() + * + * \param[in] pix any depth, not cmapped + * \param[in] pta arbitrary set of points + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) L_SET_PIXELS puts all image bits in each pixel to 1
+ *          (black for 1 bpp; white for depth > 1)
+ *      (2) L_CLEAR_PIXELS puts all image bits in each pixel to 0
+ *          (white for 1 bpp; black for depth > 1)
+ *      (3) L_FLIP_PIXELS reverses all image bits in each pixel
+ *      (4) This function clips the rendering to the pix.  It performs
+ *          clipping for functions such as pixRenderLine(),
+ *          pixRenderBox() and pixRenderBoxa(), that call pixRenderPta().
+ * 
+ */ +l_ok +pixRenderPta(PIX *pix, + PTA *pta, + l_int32 op) +{ +l_int32 i, n, x, y, w, h, d, maxval; + + PROCNAME("pixRenderPta"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (pixGetColormap(pix)) + return ERROR_INT("pix is colormapped", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + maxval = 1; + if (op == L_SET_PIXELS) { + switch (d) + { + case 2: + maxval = 0x3; + break; + case 4: + maxval = 0xf; + break; + case 8: + maxval = 0xff; + break; + case 16: + maxval = 0xffff; + break; + case 32: + maxval = 0xffffffff; + break; + } + } + + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w) + continue; + if (y < 0 || y >= h) + continue; + switch (op) + { + case L_SET_PIXELS: + pixSetPixel(pix, x, y, maxval); + break; + case L_CLEAR_PIXELS: + pixClearPixel(pix, x, y); + break; + case L_FLIP_PIXELS: + pixFlipPixel(pix, x, y); + break; + default: + break; + } + } + + return 0; +} + + +/*! + * \brief pixRenderPtaArb() + * + * \param[in] pix any depth, cmapped ok + * \param[in] pta arbitrary set of points + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %pix is colormapped, render this color (or the nearest
+ *          color if the cmap is full) on each pixel.
+ *      (2) The rgb components have the standard dynamic range [0 ... 255]
+ *      (3) If pix is not colormapped, do the best job you can using
+ *          the input colors:
+ *          ~ d = 1: set the pixels
+ *          ~ d = 2, 4, 8: average the input rgb value
+ *          ~ d = 32: use the input rgb value
+ *      (4) This function clips the rendering to %pix.
+ * 
+ */ +l_ok +pixRenderPtaArb(PIX *pix, + PTA *pta, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval) +{ +l_int32 i, n, x, y, w, h, d, index; +l_uint8 val; +l_uint32 val32; +PIXCMAP *cmap; + + PROCNAME("pixRenderPtaArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + d = pixGetDepth(pix); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return ERROR_INT("depth not in {1,2,4,8,32}", procName, 1); + + if (d == 1) { + pixRenderPta(pix, pta, L_SET_PIXELS); + return 0; + } + + cmap = pixGetColormap(pix); + pixGetDimensions(pix, &w, &h, &d); + if (cmap) { + pixcmapAddNearestColor(cmap, rval, gval, bval, &index); + } else { + if (d == 2) + val = (rval + gval + bval) / (3 * 64); + else if (d == 4) + val = (rval + gval + bval) / (3 * 16); + else if (d == 8) + val = (rval + gval + bval) / 3; + else /* d == 32 */ + composeRGBPixel(rval, gval, bval, &val32); + } + + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w) + continue; + if (y < 0 || y >= h) + continue; + if (cmap) + pixSetPixel(pix, x, y, index); + else if (d == 32) + pixSetPixel(pix, x, y, val32); + else + pixSetPixel(pix, x, y, val); + } + + return 0; +} + + +/*! + * \brief pixRenderPtaBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] pta arbitrary set of points + * \param[in] rval, gval, bval + * \param[in] fract + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function clips the rendering to %pix.
+ * 
+ */ +l_ok +pixRenderPtaBlend(PIX *pix, + PTA *pta, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_float32 fract) +{ +l_int32 i, n, x, y, w, h; +l_uint8 nrval, ngval, nbval; +l_uint32 val32; +l_float32 frval, fgval, fbval; + + PROCNAME("pixRenderPtaBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (pixGetDepth(pix) != 32) + return ERROR_INT("depth not 32 bpp", procName, 1); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); + fract = 0.5; + } + + pixGetDimensions(pix, &w, &h, NULL); + n = ptaGetCount(pta); + frval = fract * rval; + fgval = fract * gval; + fbval = fract * bval; + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w) + continue; + if (y < 0 || y >= h) + continue; + pixGetPixel(pix, x, y, &val32); + nrval = GET_DATA_BYTE(&val32, COLOR_RED); + nrval = (l_uint8)((1. - fract) * nrval + frval); + ngval = GET_DATA_BYTE(&val32, COLOR_GREEN); + ngval = (l_uint8)((1. - fract) * ngval + fgval); + nbval = GET_DATA_BYTE(&val32, COLOR_BLUE); + nbval = (l_uint8)((1. - fract) * nbval + fbval); + composeRGBPixel(nrval, ngval, nbval, &val32); + pixSetPixel(pix, x, y, val32); + } + + return 0; +} + + +/*------------------------------------------------------------------* + * Rendering of arbitrary shapes built with lines * + *------------------------------------------------------------------*/ +/*! + * \brief pixRenderLine() + * + * \param[in] pix any depth, not cmapped + * \param[in] x1, y1 + * \param[in] x2, y2 + * \param[in] width thickness of line + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderLine(PIX *pix, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 width, + l_int32 op) +{ +PTA *pta; + + PROCNAME("pixRenderLine"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (width < 1) { + L_WARNING("width must be > 0; setting to 1\n", procName); + width = 1; + } + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderLineArb() + * + * \param[in] pix any depth, cmapped ok + * \param[in] x1, y1 + * \param[in] x2, y2 + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderLineArb(PIX *pix, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval) +{ +PTA *pta; + + PROCNAME("pixRenderLineArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (width < 1) { + L_WARNING("width must be > 0; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderLineBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] x1, y1 + * \param[in] x2, y2 + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \param[in] fract + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderLineBlend(PIX *pix, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_float32 fract) +{ +PTA *pta; + + PROCNAME("pixRenderLineBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (width < 1) { + L_WARNING("width must be > 0; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBox() + * + * \param[in] pix any depth, not cmapped + * \param[in] box + * \param[in] width thickness of box lines + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBox(PIX *pix, + BOX *box, + l_int32 width, + l_int32 op) +{ +PTA *pta; + + PROCNAME("pixRenderBox"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + if ((pta = generatePtaBox(box, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBoxArb() + * + * \param[in] pix any depth, cmapped ok + * \param[in] box + * \param[in] width thickness of box lines + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBoxArb(PIX *pix, + BOX *box, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval) +{ +PTA *pta; + + PROCNAME("pixRenderBoxArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaBox(box, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBoxBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] box + * \param[in] width thickness of box lines + * \param[in] rval, gval, bval + * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; + * 0.0 is complete transparency (no effect) + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBoxBlend(PIX *pix, + BOX *box, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_float32 fract) +{ +PTA *pta; + + PROCNAME("pixRenderBoxBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaBox(box, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBoxa() + * + * \param[in] pix any depth, not cmapped + * \param[in] boxa + * \param[in] width thickness of line + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBoxa(PIX *pix, + BOXA *boxa, + l_int32 width, + l_int32 op) +{ +PTA *pta; + + PROCNAME("pixRenderBoxa"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBoxaArb() + * + * \param[in] pix any depth; colormapped is ok + * \param[in] boxa + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBoxaArb(PIX *pix, + BOXA *boxa, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval) +{ +PTA *pta; + + PROCNAME("pixRenderBoxaArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderBoxaBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] boxa + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; + * 0.0 is complete transparency (no effect) + * \param[in] removedups 1 to remove; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderBoxaBlend(PIX *pix, + BOXA *boxa, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_float32 fract, + l_int32 removedups) +{ +PTA *pta; + + PROCNAME("pixRenderBoxaBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaBoxa(boxa, width, removedups)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashBox() + * + * \param[in] pix any depth, not cmapped + * \param[in] box + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... + * \param[in] outline 0 to skip drawing box outline + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBox(PIX *pix, + BOX *box, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 op) +{ +PTA *pta; + + PROCNAME("pixRenderHashBox"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + pta = generatePtaHashBox(box, spacing, width, orient, outline); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashBoxArb() + * + * \param[in] pix any depth; cmapped ok + * \param[in] box + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... + * \param[in] outline 0 to skip drawing box outline + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBoxArb(PIX *pix, + BOX *box, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +PTA *pta; + + PROCNAME("pixRenderHashBoxArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + + pta = generatePtaHashBox(box, spacing, width, orient, outline); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashBoxBlend() + * + * \param[in] pix 32 bpp + * \param[in] box + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... + * \param[in] outline 0 to skip drawing box outline + * \param[in] rval, gval, bval + * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; + * 0.0 is complete transparency (no effect) + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBoxBlend(PIX *pix, + BOX *box, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_float32 fract) +{ +PTA *pta; + + PROCNAME("pixRenderHashBoxBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + + pta = generatePtaHashBox(box, spacing, width, orient, outline); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashMaskArb() + * + * \param[in] pix any depth; cmapped ok + * \param[in] pixm 1 bpp clipping mask for hash marks + * \param[in] x,y UL corner of %pixm with respect to %pix + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + *
+ * Notes:
+ *      (1) This is an in-place operation that renders hash lines
+ *          through a mask %pixm onto %pix.  The mask origin is
+ *          translated by (%x,%y) relative to the origin of %pix.
+ * 
+ */ +l_ok +pixRenderHashMaskArb(PIX *pix, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h; +BOX *box1, *box2; +PIX *pix1; +PTA *pta1, *pta2; + + PROCNAME("pixRenderHashMaskArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not defined or not 1 bpp", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + + /* Get the points for masked hash lines */ + pixGetDimensions(pixm, &w, &h, NULL); + box1 = boxCreate(0, 0, w, h); + pta1 = generatePtaHashBox(box1, spacing, width, orient, outline); + pta2 = ptaCropToMask(pta1, pixm); + boxDestroy(&box1); + ptaDestroy(&pta1); + + /* Clip out the region and apply the hash lines */ + box2 = boxCreate(x, y, w, h); + pix1 = pixClipRectangle(pix, box2, NULL); + pixRenderPtaArb(pix1, pta2, rval, gval, bval); + ptaDestroy(&pta2); + boxDestroy(&box2); + + /* Rasterop the altered rectangle back in place */ + pixRasterop(pix, x, y, w, h, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief pixRenderHashBoxa() + * + * \param[in] pix any depth, not cmapped + * \param[in] boxa + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBoxa(PIX *pix, + BOXA *boxa, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 op) + { +PTA *pta; + + PROCNAME("pixRenderHashBoxa"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashBoxaArb() + * + * \param[in] pix any depth; cmapped ok + * \param[in] box + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBoxaArb(PIX *pix, + BOXA *boxa, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +PTA *pta; + + PROCNAME("pixRenderHashBoxArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + + pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderHashBoxaBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] boxa + * \param[in] spacing spacing between lines; must be > 1 + * \param[in] width thickness of box and hash lines + * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, + * L_POS_SLOPE_LINE, L_VERTICAL_LINE, + * L_NEG_SLOPE_LINE + * \param[in] outline 0 to skip drawing box outline + * \param[in] rval, gval, bval + * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; + * 0.0 is complete transparency (no effect) + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderHashBoxaBlend(PIX *pix, + BOXA *boxa, + l_int32 spacing, + l_int32 width, + l_int32 orient, + l_int32 outline, + l_int32 rval, + l_int32 gval, + l_int32 bval, + l_float32 fract) +{ +PTA *pta; + + PROCNAME("pixRenderHashBoxaBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (spacing <= 1) + return ERROR_INT("spacing not > 1", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && + orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) + return ERROR_INT("invalid line orientation", procName, 1); + + pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); + if (!pta) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderPolyline() + * + * \param[in] pix any depth, not cmapped + * \param[in] ptas + * \param[in] width thickness of line + * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS + * \param[in] closeflag 1 to close the contour; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      This renders a closed contour.
+ * 
+ */ +l_ok +pixRenderPolyline(PIX *pix, + PTA *ptas, + l_int32 width, + l_int32 op, + l_int32 closeflag) +{ +PTA *pta; + + PROCNAME("pixRenderPolyline"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) + return ERROR_INT("invalid op", procName, 1); + + if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPta(pix, pta, op); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderPolylineArb() + * + * \param[in] pix any depth; cmapped ok + * \param[in] ptas + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \param[in] closeflag 1 to close the contour; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      This renders a closed contour.
+ * 
+ */ +l_ok +pixRenderPolylineArb(PIX *pix, + PTA *ptas, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_int32 closeflag) +{ +PTA *pta; + + PROCNAME("pixRenderPolylineArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderPolylineBlend() + * + * \param[in] pix 32 bpp rgb + * \param[in] ptas + * \param[in] width thickness of line + * \param[in] rval, gval, bval + * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; + * 0.0 is complete transparency (no effect) + * \param[in] closeflag 1 to close the contour; 0 otherwise + * \param[in] removedups 1 to remove; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderPolylineBlend(PIX *pix, + PTA *ptas, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval, + l_float32 fract, + l_int32 closeflag, + l_int32 removedups) +{ +PTA *pta; + + PROCNAME("pixRenderPolylineBlend"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + if ((pta = generatePtaPolyline(ptas, width, closeflag, removedups)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderGridArb() + * + * \param[in] pix any depth, cmapped ok + * \param[in] nx, ny number of rectangles in each direction + * \param[in] width thickness of grid lines + * \param[in] rval, gval, bval + * \return 0 if OK, 1 on error + */ +l_ok +pixRenderGridArb(PIX *pix, + l_int32 nx, + l_int32 ny, + l_int32 width, + l_uint8 rval, + l_uint8 gval, + l_uint8 bval) +{ +l_int32 w, h; +PTA *pta; + + PROCNAME("pixRenderGridArb"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (nx < 1 || ny < 1) + return ERROR_INT("nx, ny must be > 0", procName, 1); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + pixGetDimensions(pix, &w, &h, NULL); + if ((pta = generatePtaGrid(w, h, nx, ny, width)) == NULL) + return ERROR_INT("pta not made", procName, 1); + pixRenderPtaArb(pix, pta, rval, gval, bval); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief pixRenderRandomCmapPtaa() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] ptaa + * \param[in] polyflag 1 to interpret each Pta as a polyline; + * 0 to simply render the Pta as a set of pixels + * \param[in] width thickness of line; use only for polyline + * \param[in] closeflag 1 to close the contour; 0 otherwise; + * use only for polyline mode + * \return pixd cmapped, 8 bpp or NULL on error + * + *
+ * Notes:
+ *      (1) This is a debugging routine, that displays a set of
+ *          pixels, selected by the set of Ptas in a Ptaa,
+ *          in a random color in a pix.
+ *      (2) If %polyflag == 1, each Pta is considered to be a polyline,
+ *          and is rendered using %width and %closeflag.  Each polyline
+ *          is rendered in a random color.
+ *      (3) If %polyflag == 0, all points in each Pta are rendered in a
+ *          random color.  The %width and %closeflag parameters are ignored.
+ *      (4) The output pix is 8 bpp and colormapped.  Up to 254
+ *          different, randomly selected colors, can be used.
+ *      (5) The rendered pixels replace the input pixels.  They will
+ *          be clipped silently to the input pix.
+ * 
+ */ +PIX * +pixRenderRandomCmapPtaa(PIX *pix, + PTAA *ptaa, + l_int32 polyflag, + l_int32 width, + l_int32 closeflag) +{ +l_int32 i, n, index, rval, gval, bval; +PIXCMAP *cmap; +PTA *pta, *ptat; +PIX *pixd; + + PROCNAME("pixRenderRandomCmapPtaa"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + if (!ptaa) + return (PIX *)ERROR_PTR("ptaa not defined", procName, NULL); + if (polyflag != 0 && width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + pixd = pixConvertTo8(pix, FALSE); + cmap = pixcmapCreateRandom(8, 1, 1); + pixSetColormap(pixd, cmap); + + if ((n = ptaaGetCount(ptaa)) == 0) + return pixd; + + for (i = 0; i < n; i++) { + index = 1 + (i % 254); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + pta = ptaaGetPta(ptaa, i, L_CLONE); + if (polyflag) + ptat = generatePtaPolyline(pta, width, closeflag, 0); + else + ptat = ptaClone(pta); + pixRenderPtaArb(pixd, ptat, rval, gval, bval); + ptaDestroy(&pta); + ptaDestroy(&ptat); + } + + return pixd; +} + + + +/*------------------------------------------------------------------* + * Rendering and filling of polygons * + *------------------------------------------------------------------*/ +/*! + * \brief pixRenderPolygon() + * + * \param[in] ptas of vertices, none repeated + * \param[in] width of polygon outline + * \param[out] pxmin [optional] min x value of input pts + * \param[out] pymin [optional] min y value of input pts + * \return pix 1 bpp, with outline generated, or NULL on error + * + *
+ * Notes:
+ *      (1) The pix is the minimum size required to contain the origin
+ *          and the polygon.  For example, the max x value of the input
+ *          points is w - 1, where w is the pix width.
+ *      (2) The rendered line is 4-connected, so that an interior or
+ *          exterior 8-c.c. flood fill operation works properly.
+ * 
+ */ +PIX * +pixRenderPolygon(PTA *ptas, + l_int32 width, + l_int32 *pxmin, + l_int32 *pymin) +{ +l_float32 fxmin, fxmax, fymin, fymax; +PIX *pixd; +PTA *pta1, *pta2; + + PROCNAME("pixRenderPolygon"); + + if (pxmin) *pxmin = 0; + if (pymin) *pymin = 0; + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + + /* Generate a 4-connected polygon line */ + if ((pta1 = generatePtaPolyline(ptas, width, 1, 0)) == NULL) + return (PIX *)ERROR_PTR("pta1 not made", procName, NULL); + if (width < 2) + pta2 = convertPtaLineTo4cc(pta1); + else + pta2 = ptaClone(pta1); + + /* Render onto a minimum-sized pix */ + ptaGetRange(pta2, &fxmin, &fxmax, &fymin, &fymax); + if (pxmin) *pxmin = (l_int32)(fxmin + 0.5); + if (pymin) *pymin = (l_int32)(fymin + 0.5); + pixd = pixCreate((l_int32)(fxmax + 0.5) + 1, (l_int32)(fymax + 0.5) + 1, 1); + pixRenderPolyline(pixd, pta2, width, L_SET_PIXELS, 1); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + return pixd; +} + + +/*! + * \brief pixFillPolygon() + * + * \param[in] pixs 1 bpp, with 4-connected polygon outline + * \param[in] pta vertices of the polygon + * \param[in] xmin, ymin min values of vertices of polygon + * \return pixd with outline filled, or NULL on error + * + *
+ * Notes:
+ *      (1) This fills the interior of the polygon, returning a
+ *          new pix.  It works for both convex and non-convex polygons.
+ *      (2) To generate a filled polygon from %pta:
+ *            PIX *pixt = pixRenderPolygon(pta, 1, &xmin, &ymin);
+ *            PIX *pixd = pixFillPolygon(pixt, pta, xmin, ymin);
+ *            pixDestroy(&pixt);
+ * 
+ */ +PIX * +pixFillPolygon(PIX *pixs, + PTA *pta, + l_int32 xmin, + l_int32 ymin) +{ +l_int32 w, h, i, n, inside, found; +l_int32 *xstart, *xend; +PIX *pixi, *pixd; + + PROCNAME("pixFillPolygon"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!pta) + return (PIX *)ERROR_PTR("pta not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + xstart = (l_int32 *)LEPT_CALLOC(w / 2, sizeof(l_int32)); + xend = (l_int32 *)LEPT_CALLOC(w / 2, sizeof(l_int32)); + + /* Find a raster with 2 or more black runs. The first background + * pixel after the end of the first run is likely to be inside + * the polygon, and can be used as a seed pixel. */ + found = FALSE; + for (i = ymin + 1; i < h; i++) { + pixFindHorizontalRuns(pixs, i, xstart, xend, &n); + if (n > 1) { + ptaPtInsidePolygon(pta, xend[0] + 1, i, &inside); + if (inside) { + found = TRUE; + break; + } + } + } + if (!found) { + L_WARNING("nothing found to fill\n", procName); + LEPT_FREE(xstart); + LEPT_FREE(xend); + return 0; + } + + /* Place the seed pixel in the output image */ + pixd = pixCreateTemplate(pixs); + pixSetPixel(pixd, xend[0] + 1, i, 1); + + /* Invert pixs to make a filling mask, and fill from the seed */ + pixi = pixInvert(NULL, pixs); + pixSeedfillBinary(pixd, pixd, pixi, 4); + + /* Add the pixels of the original polygon outline */ + pixOr(pixd, pixd, pixs); + + pixDestroy(&pixi); + LEPT_FREE(xstart); + LEPT_FREE(xend); + return pixd; +} + + +/*------------------------------------------------------------------* + * Contour rendering on grayscale images * + *------------------------------------------------------------------*/ +/*! + * \brief pixRenderContours() + * + * \param[in] pixs 8 or 16 bpp; no colormap + * \param[in] startval value of lowest contour; must be in [0 ... maxval] + * \param[in] incr increment to next contour; must be > 0 + * \param[in] outdepth either 1 or depth of pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The output can be either 1 bpp, showing just the contour
+ *          lines, or a copy of the input pixs with the contour lines
+ *          superposed.
+ * 
+ */ +PIX * +pixRenderContours(PIX *pixs, + l_int32 startval, + l_int32 incr, + l_int32 outdepth) +{ +l_int32 w, h, d, maxval, wpls, wpld, i, j, val, test; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixRenderContours"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16) + return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); + if (outdepth != 1 && outdepth != d) { + L_WARNING("invalid outdepth; setting to 1\n", procName); + outdepth = 1; + } + maxval = (1 << d) - 1; + if (startval < 0 || startval > maxval) + return (PIX *)ERROR_PTR("startval not in [0 ... maxval]", + procName, NULL); + if (incr < 1) + return (PIX *)ERROR_PTR("incr < 1", procName, NULL); + + if (outdepth == d) + pixd = pixCopy(NULL, pixs); + else + pixd = pixCreate(w, h, 1); + + pixCopyResolution(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + switch (d) + { + case 8: + if (outdepth == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + if (val < startval) + continue; + test = (val - startval) % incr; + if (!test) + SET_DATA_BIT(lined, j); + } + } + } else { /* outdepth == d */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + if (val < startval) + continue; + test = (val - startval) % incr; + if (!test) + SET_DATA_BYTE(lined, j, 0); + } + } + } + break; + + case 16: + if (outdepth == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(lines, j); + if (val < startval) + continue; + test = (val - startval) % incr; + if (!test) + SET_DATA_BIT(lined, j); + } + } + } else { /* outdepth == d */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(lines, j); + if (val < startval) + continue; + test = (val - startval) % incr; + if (!test) + SET_DATA_TWO_BYTES(lined, j, 0); + } + } + } + break; + + default: + return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); + } + + return pixd; +} + + +/*! + * \brief fpixAutoRenderContours() + * + * \param[in] fpix + * \param[in] ncontours in [2 ... 500]; typically about 50 + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) The increment is set to get approximately %ncontours.
+ *      (2) The proximity to the target value for contour display
+ *          is set to 0.15.
+ *      (3) Negative values are rendered in red; positive values as black.
+ * 
+ */ +PIX * +fpixAutoRenderContours(FPIX *fpix, + l_int32 ncontours) +{ +l_float32 minval, maxval, incr; + + PROCNAME("fpixAutoRenderContours"); + + if (!fpix) + return (PIX *)ERROR_PTR("fpix not defined", procName, NULL); + if (ncontours < 2 || ncontours > 500) + return (PIX *)ERROR_PTR("ncontours < 2 or > 500", procName, NULL); + + fpixGetMin(fpix, &minval, NULL, NULL); + fpixGetMax(fpix, &maxval, NULL, NULL); + if (minval == maxval) + return (PIX *)ERROR_PTR("all values in fpix are equal", procName, NULL); + incr = (maxval - minval) / ((l_float32)ncontours - 1); + return fpixRenderContours(fpix, incr, 0.15); +} + + +/*! + * \brief fpixRenderContours() + * + * \param[in] fpixs + * \param[in] incr increment between contours; must be > 0.0 + * \param[in] proxim required proximity to target value; default 0.15 + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Values are displayed when val/incr is within +-proxim
+ *          to an integer.  The default value is 0.15; smaller values
+ *          result in thinner contour lines.
+ *      (2) Negative values are rendered in red; positive values as black.
+ * 
+ */ +PIX * +fpixRenderContours(FPIX *fpixs, + l_float32 incr, + l_float32 proxim) +{ +l_int32 i, j, w, h, wpls, wpld; +l_float32 val, invincr, finter, above, below, diff; +l_uint32 *datad, *lined; +l_float32 *datas, *lines; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("fpixRenderContours"); + + if (!fpixs) + return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); + if (incr <= 0.0) + return (PIX *)ERROR_PTR("incr <= 0.0", procName, NULL); + if (proxim <= 0.0) + proxim = 0.15; /* default */ + + fpixGetDimensions(fpixs, &w, &h); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(8); + pixSetColormap(pixd, cmap); + pixcmapAddColor(cmap, 255, 255, 255); /* white */ + pixcmapAddColor(cmap, 0, 0, 0); /* black */ + pixcmapAddColor(cmap, 255, 0, 0); /* red */ + + datas = fpixGetData(fpixs); + wpls = fpixGetWpl(fpixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + invincr = 1.0 / incr; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j]; + finter = invincr * val; + above = finter - floorf(finter); + below = ceilf(finter) - finter; + diff = L_MIN(above, below); + if (diff <= proxim) { + if (val < 0.0) + SET_DATA_BYTE(lined, j, 2); + else + SET_DATA_BYTE(lined, j, 1); + } + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Boundary pt generation on 1 bpp images * + *------------------------------------------------------------------*/ +/*! + * \brief pixGeneratePtaBoundary() + * + * \param[in] pixs 1 bpp + * \param[in] width of boundary line + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Similar to ptaGetBoundaryPixels(), except here:
+ *          * we only get pixels in the foreground
+ *          * we can have a "line" width greater than 1 pixel.
+ *      (2) Once generated, this can be applied to a random 1 bpp image
+ *          to add a color boundary as follows:
+ *             Pta *pta = pixGeneratePtaBoundary(pixs, width);
+ *             Pix *pix1 = pixConvert1To8Cmap(pixs);
+ *             pixRenderPtaArb(pix1, pta, rval, gval, bval);
+ * 
+ */ +PTA * +pixGeneratePtaBoundary(PIX *pixs, + l_int32 width) +{ +PIX *pix1; +PTA *pta; + + PROCNAME("pixGeneratePtaBoundary"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (width < 1) { + L_WARNING("width < 1; setting to 1\n", procName); + width = 1; + } + + pix1 = pixErodeBrick(NULL, pixs, 2 * width + 1, 2 * width + 1); + pixXor(pix1, pix1, pixs); + pta = ptaGetPixelsFromPix(pix1, NULL); + pixDestroy(&pix1); + return pta; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/graymorph.c b/hgdriver/3rdparty/hgOCR/leptonica/graymorph.c new file mode 100644 index 0000000..c2e3303 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/graymorph.c @@ -0,0 +1,1372 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file graymorph.c + *
+ *
+ *      Top-level grayscale morphological operations (van Herk / Gil-Werman)
+ *            PIX           *pixErodeGray()
+ *            PIX           *pixDilateGray()
+ *            PIX           *pixOpenGray()
+ *            PIX           *pixCloseGray()
+ *
+ *      Special operations for 1x3, 3x1 and 3x3 Sels  (direct)
+ *            PIX           *pixErodeGray3()
+ *            static PIX    *pixErodeGray3h()
+ *            static PIX    *pixErodeGray3v()
+ *            PIX           *pixDilateGray3()
+ *            static PIX    *pixDilateGray3h()
+ *            static PIX    *pixDilateGray3v()
+ *            PIX           *pixOpenGray3()
+ *            PIX           *pixCloseGray3()
+ *
+ *      Low-level grayscale morphological operations
+ *            static void    dilateGrayLow()
+ *            static void    erodeGrayLow()
+ *
+ *
+ *      Method: Algorithm by van Herk and Gil and Werman, 1992
+ *
+ *      Measured speed of the vH/G-W implementation is about 1 output
+ *      pixel per 120 PIII clock cycles, for a horizontal or vertical
+ *      erosion or dilation.  The computation time doubles for opening
+ *      or closing, or for a square SE, as expected, and is independent
+ *      of the size of the SE.
+ *
+ *      A faster implementation can be made directly for brick Sels
+ *      of maximum size 3.  We unroll the computation for sets of 8 bytes.
+ *      It needs to be called explicitly; the general functions do not
+ *      default for the size 3 brick Sels.
+ *
+ *      We use the van Herk/Gil-Werman (vHGW) algorithm, [van Herk,
+ *      Patt. Recog. Let. 13, pp. 517-521, 1992; Gil and Werman,
+ *      IEEE Trans PAMI 15(5), pp. 504-507, 1993.]
+ *      This was the first grayscale morphology
+ *      algorithm to compute dilation and erosion with
+ *      complexity independent of the size of the structuring
+ *      element.  It is simple and elegant, and surprising that
+ *      it was discovered as recently as 1992.  It works for
+ *      SEs composed of horizontal and/or vertical lines.  The
+ *      general case requires finding the Min or Max over an
+ *      arbitrary set of pixels, and this requires a number of
+ *      pixel comparisons equal to the SE "size" at each pixel
+ *      in the image.  The vHGW algorithm requires not
+ *      more than 3 comparisons at each point.  The algorithm has been
+ *      recently refined by Gil and Kimmel ("Efficient Dilation
+ *      Erosion, Opening and Closing Algorithms", in "Mathematical
+ *      Morphology and its Applications to Image and Signal Processing",
+ *      the proceedings of the International Symposium on Mathematical
+ *      Morphology, Palo Alto, CA, June 2000, Kluwer Academic
+ *      Publishers, pp. 301-310).  They bring this number down below
+ *      1.5 comparisons per output pixel but at a cost of significantly
+ *      increased complexity, so I don't bother with that here.
+ *
+ *      In brief, the method is as follows.  We evaluate the dilation
+ *      in groups of "size" pixels, equal to the size of the SE.
+ *      For horizontal, we start at x = "size"/2 and go
+ *      (w - 2 * ("size"/2))/"size" steps.  This means that
+ *      we don't evaluate the first 0.5 * "size" pixels and, worst
+ *      case, the last 1.5 * "size" pixels.  Thus we embed the
+ *      image in a larger image with these augmented dimensions, where
+ *      the new border pixels are appropriately initialized (0 for
+ *      dilation; 255 for erosion), and remove the boundary at the end.
+ *      (For vertical, use h instead of w.)   Then for each group
+ *      of "size" pixels, we form an array of length 2 * "size" + 1,
+ *      consisting of backward and forward partial maxima (for
+ *      dilation) or minima (for erosion).  This represents a
+ *      jumping window computed from the source image, over which
+ *      the SE will slide.  The center of the array gets the source
+ *      pixel at the center of the SE.  Call this the center pixel
+ *      of the window.  Array values to left of center get
+ *      the maxima(minima) of the pixels from the center
+ *      one and going to the left an equal distance.  Array
+ *      values to the right of center get the maxima(minima) to
+ *      the pixels from the center one and going to the right
+ *      an equal distance.  These are computed sequentially starting
+ *      from the center one.  The SE (of length "size") can slide over this
+ *      window (of length 2 * "size + 1) at "size" different places.
+ *      At each place, the maxima(minima) of the values in the window
+ *      that correspond to the end points of the SE give the extremal
+ *      values over that interval, and these are stored at the dest
+ *      pixel corresponding to the SE center.  A picture is worth
+ *      at least this many words, so if this isn't clear, see the
+ *      leptonica documentation on grayscale morphology.
+ * 
+ */ + +#include "allheaders.h" + + /* Special static operations for 3x1, 1x3 and 3x3 structuring elements */ +static PIX *pixErodeGray3h(PIX *pixs); +static PIX *pixErodeGray3v(PIX *pixs); +static PIX *pixDilateGray3h(PIX *pixs); +static PIX *pixDilateGray3v(PIX *pixs); + + /* Low-level gray morphological operations */ +static void dilateGrayLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 size, l_int32 direction, l_uint8 *buffer, + l_uint8 *maxarray); +static void erodeGrayLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 size, l_int32 direction, l_uint8 *buffer, + l_uint8 *minarray); + +/*-----------------------------------------------------------------* + * Top-level grayscale morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixErodeGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixErodeGray(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_uint8 *buffer, *minarray; +l_int32 w, h, wplb, wplt; +l_int32 leftpix, rightpix, toppix, bottompix, maxsize; +l_uint32 *datab, *datat; +PIX *pixb, *pixt, *pixd; + + PROCNAME("pixErodeGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = minarray = NULL; + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + if (vsize == 1) { /* horizontal sel */ + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = 0; + bottompix = 0; + } else if (hsize == 1) { /* vertical sel */ + leftpix = 0; + rightpix = 0; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } else { + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", procName); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + minarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !minarray) { + L_ERROR("buffer and minarray not made\n", procName); + goto cleanup; + } + + if (vsize == 1) { + erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, minarray); + } else if (hsize == 1) { + erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, + buffer, minarray); + } else { + erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, minarray); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, minarray); + pixDestroy(&pixt); + pixt = pixClone(pixb); + } + + pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", procName); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(minarray); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixDilateGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixDilateGray(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_uint8 *buffer, *maxarray; +l_int32 w, h, wplb, wplt; +l_int32 leftpix, rightpix, toppix, bottompix, maxsize; +l_uint32 *datab, *datat; +PIX *pixb, *pixt, *pixd; + + PROCNAME("pixDilateGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = maxarray = NULL; + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + if (vsize == 1) { /* horizontal sel */ + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = 0; + bottompix = 0; + } else if (hsize == 1) { /* vertical sel */ + leftpix = 0; + rightpix = 0; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } else { + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", procName); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + maxarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !maxarray) { + L_ERROR("buffer and maxarray not made\n", procName); + goto cleanup; + } + + if (vsize == 1) { + dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, maxarray); + } else if (hsize == 1) { + dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, + buffer, maxarray); + } else { + dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, maxarray); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, maxarray); + pixDestroy(&pixt); + pixt = pixClone(pixb); + } + + pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", procName); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(maxarray); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixOpenGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixOpenGray(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_uint8 *buffer; +l_uint8 *array; /* used to find either min or max in interval */ +l_int32 w, h, wplb, wplt; +l_int32 leftpix, rightpix, toppix, bottompix, maxsize; +l_uint32 *datab, *datat; +PIX *pixb, *pixt, *pixd; + + PROCNAME("pixOpenGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = array = NULL; + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + if (vsize == 1) { /* horizontal sel */ + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = 0; + bottompix = 0; + } else if (hsize == 1) { /* vertical sel */ + leftpix = 0; + rightpix = 0; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } else { + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", procName); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !array) { + L_ERROR("buffer and array not made\n", procName); + goto cleanup; + } + + if (vsize == 1) { + erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, + buffer, array); + } + else if (hsize == 1) { + erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + } else { + erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + } + + pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", procName); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(array); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixCloseGray() + * + * \param[in] pixs + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixCloseGray(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_uint8 *buffer; +l_uint8 *array; /* used to find either min or max in interval */ +l_int32 w, h, wplb, wplt; +l_int32 leftpix, rightpix, toppix, bottompix, maxsize; +l_uint32 *datab, *datat; +PIX *pixb, *pixt, *pixd; + + PROCNAME("pixCloseGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + pixb = pixt = pixd = NULL; + buffer = array = NULL; + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + if (vsize == 1) { /* horizontal sel */ + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = 0; + bottompix = 0; + } else if (hsize == 1) { /* vertical sel */ + leftpix = 0; + rightpix = 0; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } else { + leftpix = (hsize + 1) / 2; + rightpix = (3 * hsize + 1) / 2; + toppix = (vsize + 1) / 2; + bottompix = (3 * vsize + 1) / 2; + } + + pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); + pixt = pixCreateTemplate(pixb); + if (!pixb || !pixt) { + L_ERROR("pixb and pixt not made\n", procName); + goto cleanup; + } + + pixGetDimensions(pixt, &w, &h, NULL); + datab = pixGetData(pixb); + datat = pixGetData(pixt); + wplb = pixGetWpl(pixb); + wplt = pixGetWpl(pixt); + + buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); + maxsize = L_MAX(hsize, vsize); + array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); + if (!buffer || !array) { + L_ERROR("buffer and array not made\n", procName); + goto cleanup; + } + + if (vsize == 1) { + dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, + buffer, array); + } else if (hsize == 1) { + dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + } else { + dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_CLR); + dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, + buffer, array); + pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, + PIX_SET); + erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, + buffer, array); + } + + pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); + if (!pixd) + L_ERROR("pixd not made\n", procName); + +cleanup: + LEPT_FREE(buffer); + LEPT_FREE(array); + pixDestroy(&pixb); + pixDestroy(&pixt); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Special operations for 1x3, 3x1 and 3x3 Sels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixErodeGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ *      (3) It would be nice not to add a border, but it is required
+ *          if we want the same results as from the general case.
+ *          We add 4 bytes on the left to speed up the copying, and
+ *          8 bytes at the right and bottom to allow unrolling of
+ *          the computation of 8 pixels.
+ * 
+ */ +PIX * +pixErodeGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + PROCNAME("pixErodeGray3"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); + + if (vsize == 1) + pixbd = pixErodeGray3h(pixb); + else if (hsize == 1) + pixbd = pixErodeGray3v(pixb); + else { /* vize == hsize == 3 */ + pixt = pixErodeGray3h(pixb); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixErodeGray3h() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for horizontal 3x1 brick Sel;
+ *          also used as the first step for the 3x3 brick Sel.
+ * 
+ */ +static PIX * +pixErodeGray3h(PIX *pixs) +{ +l_uint32 *datas, *datad, *lines, *lined; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; +PIX *pixd; + + PROCNAME("pixErodeGray3h"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpl; + lined = datad + i * wpl; + for (j = 1; j < w - 8; j += 8) { + val0 = GET_DATA_BYTE(lines, j - 1); + val1 = GET_DATA_BYTE(lines, j); + val2 = GET_DATA_BYTE(lines, j + 1); + val3 = GET_DATA_BYTE(lines, j + 2); + val4 = GET_DATA_BYTE(lines, j + 3); + val5 = GET_DATA_BYTE(lines, j + 4); + val6 = GET_DATA_BYTE(lines, j + 5); + val7 = GET_DATA_BYTE(lines, j + 6); + val8 = GET_DATA_BYTE(lines, j + 7); + val9 = GET_DATA_BYTE(lines, j + 8); + minval = L_MIN(val1, val2); + SET_DATA_BYTE(lined, j, L_MIN(val0, minval)); + SET_DATA_BYTE(lined, j + 1, L_MIN(minval, val3)); + minval = L_MIN(val3, val4); + SET_DATA_BYTE(lined, j + 2, L_MIN(val2, minval)); + SET_DATA_BYTE(lined, j + 3, L_MIN(minval, val5)); + minval = L_MIN(val5, val6); + SET_DATA_BYTE(lined, j + 4, L_MIN(val4, minval)); + SET_DATA_BYTE(lined, j + 5, L_MIN(minval, val7)); + minval = L_MIN(val7, val8); + SET_DATA_BYTE(lined, j + 6, L_MIN(val6, minval)); + SET_DATA_BYTE(lined, j + 7, L_MIN(minval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixErodeGray3v() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for vertical 1x3 brick Sel;
+ *          also used as the second step for the 3x3 brick Sel.
+ *      (2) Surprisingly, this is faster than setting up the
+ *          lineptrs array and accessing into it; e.g.,
+ *              val4 = GET_DATA_BYTE(lines8[i + 3], j);
+ * 
+ */ +static PIX * +pixErodeGray3v(PIX *pixs) +{ +l_uint32 *datas, *datad, *linesi, *linedi; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; +PIX *pixd; + + PROCNAME("pixErodeGray3v"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (j = 0; j < w; j++) { + for (i = 1; i < h - 8; i += 8) { + linesi = datas + i * wpl; + linedi = datad + i * wpl; + val0 = GET_DATA_BYTE(linesi - wpl, j); + val1 = GET_DATA_BYTE(linesi, j); + val2 = GET_DATA_BYTE(linesi + wpl, j); + val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); + val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); + val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); + val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); + val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); + val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); + val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); + minval = L_MIN(val1, val2); + SET_DATA_BYTE(linedi, j, L_MIN(val0, minval)); + SET_DATA_BYTE(linedi + wpl, j, L_MIN(minval, val3)); + minval = L_MIN(val3, val4); + SET_DATA_BYTE(linedi + 2 * wpl, j, L_MIN(val2, minval)); + SET_DATA_BYTE(linedi + 3 * wpl, j, L_MIN(minval, val5)); + minval = L_MIN(val5, val6); + SET_DATA_BYTE(linedi + 4 * wpl, j, L_MIN(val4, minval)); + SET_DATA_BYTE(linedi + 5 * wpl, j, L_MIN(minval, val7)); + minval = L_MIN(val7, val8); + SET_DATA_BYTE(linedi + 6 * wpl, j, L_MIN(val6, minval)); + SET_DATA_BYTE(linedi + 7 * wpl, j, L_MIN(minval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixDilateGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixDilateGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + PROCNAME("pixDilateGray3"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); + + if (vsize == 1) + pixbd = pixDilateGray3h(pixb); + else if (hsize == 1) + pixbd = pixDilateGray3v(pixb); + else { /* vize == hsize == 3 */ + pixt = pixDilateGray3h(pixb); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixDilateGray3h() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for horizontal 3x1 brick Sel;
+ *          also used as the first step for the 3x3 brick Sel.
+ * 
+ */ +static PIX * +pixDilateGray3h(PIX *pixs) +{ +l_uint32 *datas, *datad, *lines, *lined; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; +PIX *pixd; + + PROCNAME("pixDilateGray3h"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + lines = datas + i * wpl; + lined = datad + i * wpl; + for (j = 1; j < w - 8; j += 8) { + val0 = GET_DATA_BYTE(lines, j - 1); + val1 = GET_DATA_BYTE(lines, j); + val2 = GET_DATA_BYTE(lines, j + 1); + val3 = GET_DATA_BYTE(lines, j + 2); + val4 = GET_DATA_BYTE(lines, j + 3); + val5 = GET_DATA_BYTE(lines, j + 4); + val6 = GET_DATA_BYTE(lines, j + 5); + val7 = GET_DATA_BYTE(lines, j + 6); + val8 = GET_DATA_BYTE(lines, j + 7); + val9 = GET_DATA_BYTE(lines, j + 8); + maxval = L_MAX(val1, val2); + SET_DATA_BYTE(lined, j, L_MAX(val0, maxval)); + SET_DATA_BYTE(lined, j + 1, L_MAX(maxval, val3)); + maxval = L_MAX(val3, val4); + SET_DATA_BYTE(lined, j + 2, L_MAX(val2, maxval)); + SET_DATA_BYTE(lined, j + 3, L_MAX(maxval, val5)); + maxval = L_MAX(val5, val6); + SET_DATA_BYTE(lined, j + 4, L_MAX(val4, maxval)); + SET_DATA_BYTE(lined, j + 5, L_MAX(maxval, val7)); + maxval = L_MAX(val7, val8); + SET_DATA_BYTE(lined, j + 6, L_MAX(val6, maxval)); + SET_DATA_BYTE(lined, j + 7, L_MAX(maxval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixDilateGray3v() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for vertical 1x3 brick Sel;
+ *          also used as the second step for the 3x3 brick Sel.
+ * 
+ */ +static PIX * +pixDilateGray3v(PIX *pixs) +{ +l_uint32 *datas, *datad, *linesi, *linedi; +l_int32 w, h, wpl, i, j; +l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; +PIX *pixd; + + PROCNAME("pixDilateGray3v"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (j = 0; j < w; j++) { + for (i = 1; i < h - 8; i += 8) { + linesi = datas + i * wpl; + linedi = datad + i * wpl; + val0 = GET_DATA_BYTE(linesi - wpl, j); + val1 = GET_DATA_BYTE(linesi, j); + val2 = GET_DATA_BYTE(linesi + wpl, j); + val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); + val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); + val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); + val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); + val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); + val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); + val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); + maxval = L_MAX(val1, val2); + SET_DATA_BYTE(linedi, j, L_MAX(val0, maxval)); + SET_DATA_BYTE(linedi + wpl, j, L_MAX(maxval, val3)); + maxval = L_MAX(val3, val4); + SET_DATA_BYTE(linedi + 2 * wpl, j, L_MAX(val2, maxval)); + SET_DATA_BYTE(linedi + 3 * wpl, j, L_MAX(maxval, val5)); + maxval = L_MAX(val5, val6); + SET_DATA_BYTE(linedi + 4 * wpl, j, L_MAX(val4, maxval)); + SET_DATA_BYTE(linedi + 5 * wpl, j, L_MAX(maxval, val7)); + maxval = L_MAX(val7, val8); + SET_DATA_BYTE(linedi + 6 * wpl, j, L_MAX(val6, maxval)); + SET_DATA_BYTE(linedi + 7 * wpl, j, L_MAX(maxval, val9)); + } + } + return pixd; +} + + +/*! + * \brief pixOpenGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ *      (3) It would be nice not to add a border, but it is required
+ *          to get the same results as for the general case.
+ * 
+ */ +PIX * +pixOpenGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + PROCNAME("pixOpenGray3"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); /* set to max */ + + if (vsize == 1) { + pixt = pixErodeGray3h(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 0); /* set to min */ + pixbd = pixDilateGray3h(pixt); + pixDestroy(&pixt); + } else if (hsize == 1) { + pixt = pixErodeGray3v(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 0); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } else { /* vize == hsize == 3 */ + pixt = pixErodeGray3h(pixb); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + pixSetBorderVal(pixbd, 4, 8, 2, 8, 0); + pixt = pixDilateGray3h(pixbd); + pixDestroy(&pixbd); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*! + * \brief pixCloseGray3() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] hsize 1 or 3 + * \param[in] vsize 1 or 3 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits)
+ *      (2) If hsize = vsize = 1, just returns a copy.
+ * 
+ */ +PIX * +pixCloseGray3(PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt, *pixb, *pixbd, *pixd; + + PROCNAME("pixCloseGray3"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); + if ((hsize != 1 && hsize != 3) || + (vsize != 1 && vsize != 3)) + return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); + + if (hsize == 1 && vsize == 1) + return pixCopy(NULL, pixs); + + pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); /* set to min */ + + if (vsize == 1) { + pixt = pixDilateGray3h(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 255); /* set to max */ + pixbd = pixErodeGray3h(pixt); + pixDestroy(&pixt); + } else if (hsize == 1) { + pixt = pixDilateGray3v(pixb); + pixSetBorderVal(pixt, 4, 8, 2, 8, 255); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } else { /* vize == hsize == 3 */ + pixt = pixDilateGray3h(pixb); + pixbd = pixDilateGray3v(pixt); + pixDestroy(&pixt); + pixSetBorderVal(pixbd, 4, 8, 2, 8, 255); + pixt = pixErodeGray3h(pixbd); + pixDestroy(&pixbd); + pixbd = pixErodeGray3v(pixt); + pixDestroy(&pixt); + } + + pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); + pixDestroy(&pixb); + pixDestroy(&pixbd); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Low-level gray morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief dilateGrayLow() + * + * \param[in] datad 8 bpp dsst image + * \param[in] w, h dimensions of src and dest + * \param[in] wpld words/line of dest + * \param[in] datas 8 bpp src image + * \param[in] wpls words/line of src + * \param[in] size full length of SEL; restricted to odd numbers + * \param[in] direction L_HORIZ or L_VERT + * \param[in] buffer holds full line or column of src image pixels + * \param[in] maxarray array of dimension 2*size+1 + * \return void + * + *
+ * Notes:
+ *        (1) To eliminate border effects on the actual image, these images
+ *            are prepared with an additional border of dimensions:
+ *               leftpix = 0.5 * size
+ *               rightpix = 1.5 * size
+ *               toppix = 0.5 * size
+ *               bottompix = 1.5 * size
+ *            and we initialize the src border pixels to 0.
+ *            This allows full processing over the actual image; at
+ *            the end the border is removed.
+ *        (2) Uses algorithm of van Herk, Gil and Werman
+ * 
+ */ +static void +dilateGrayLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 size, + l_int32 direction, + l_uint8 *buffer, + l_uint8 *maxarray) +{ +l_int32 i, j, k; +l_int32 hsize, nsteps, startmax, startx, starty; +l_uint8 maxval; +l_uint32 *lines, *lined; + + if (direction == L_HORIZ) { + hsize = size / 2; + nsteps = (w - 2 * hsize) / size; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + + /* fill buffer with pixels in byte order */ + for (j = 0; j < w; j++) + buffer[j] = GET_DATA_BYTE(lines, j); + + for (j = 0; j < nsteps; j++) { + /* refill the minarray */ + startmax = (j + 1) * size - 1; + maxarray[size - 1] = buffer[startmax]; + for (k = 1; k < size; k++) { + maxarray[size - 1 - k] = + L_MAX(maxarray[size - k], buffer[startmax - k]); + maxarray[size - 1 + k] = + L_MAX(maxarray[size + k - 2], buffer[startmax + k]); + } + + /* compute dilation values */ + startx = hsize + j * size; + SET_DATA_BYTE(lined, startx, maxarray[0]); + SET_DATA_BYTE(lined, startx + size - 1, maxarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); + SET_DATA_BYTE(lined, startx + k, maxval); + } + } + } + } else { /* direction == L_VERT */ + hsize = size / 2; + nsteps = (h - 2 * hsize) / size; + for (j = 0; j < w; j++) { + /* fill buffer with pixels in byte order */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + buffer[i] = GET_DATA_BYTE(lines, j); + } + + for (i = 0; i < nsteps; i++) { + /* refill the minarray */ + startmax = (i + 1) * size - 1; + maxarray[size - 1] = buffer[startmax]; + for (k = 1; k < size; k++) { + maxarray[size - 1 - k] = + L_MAX(maxarray[size - k], buffer[startmax - k]); + maxarray[size - 1 + k] = + L_MAX(maxarray[size + k - 2], buffer[startmax + k]); + } + + /* compute dilation values */ + starty = hsize + i * size; + lined = datad + starty * wpld; + SET_DATA_BYTE(lined, j, maxarray[0]); + SET_DATA_BYTE(lined + (size - 1) * wpld, j, + maxarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); + SET_DATA_BYTE(lined + wpld * k, j, maxval); + } + } + } + } + + return; +} + + +/*! + * \brief erodeGrayLow() + * + * \param[in] datad 8 bpp dsst image + * \param[in] w, h dimensions of src and dest + * \param[in] wpld words/line of dest + * \param[in] datas 8 bpp src image + * \param[in] wpls words/line of src + * \param[in] size full length of SEL; restricted to odd numbers + * \param[in] direction L_HORIZ or L_VERT + * \param[in] buffer holds full line or column of src image pixels + * \param[in] minarray array of dimension 2*size+1 + * \return void + * + *
+ * Notes:
+ *        (1) See notes in dilateGrayLow()
+ * 
+ */ +static void +erodeGrayLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 size, + l_int32 direction, + l_uint8 *buffer, + l_uint8 *minarray) +{ +l_int32 i, j, k; +l_int32 hsize, nsteps, startmin, startx, starty; +l_uint8 minval; +l_uint32 *lines, *lined; + + if (direction == L_HORIZ) { + hsize = size / 2; + nsteps = (w - 2 * hsize) / size; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + + /* fill buffer with pixels in byte order */ + for (j = 0; j < w; j++) + buffer[j] = GET_DATA_BYTE(lines, j); + + for (j = 0; j < nsteps; j++) { + /* refill the minarray */ + startmin = (j + 1) * size - 1; + minarray[size - 1] = buffer[startmin]; + for (k = 1; k < size; k++) { + minarray[size - 1 - k] = + L_MIN(minarray[size - k], buffer[startmin - k]); + minarray[size - 1 + k] = + L_MIN(minarray[size + k - 2], buffer[startmin + k]); + } + + /* compute erosion values */ + startx = hsize + j * size; + SET_DATA_BYTE(lined, startx, minarray[0]); + SET_DATA_BYTE(lined, startx + size - 1, minarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + minval = L_MIN(minarray[k], minarray[k + size - 1]); + SET_DATA_BYTE(lined, startx + k, minval); + } + } + } + } else { /* direction == L_VERT */ + hsize = size / 2; + nsteps = (h - 2 * hsize) / size; + for (j = 0; j < w; j++) { + /* fill buffer with pixels in byte order */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + buffer[i] = GET_DATA_BYTE(lines, j); + } + + for (i = 0; i < nsteps; i++) { + /* refill the minarray */ + startmin = (i + 1) * size - 1; + minarray[size - 1] = buffer[startmin]; + for (k = 1; k < size; k++) { + minarray[size - 1 - k] = + L_MIN(minarray[size - k], buffer[startmin - k]); + minarray[size - 1 + k] = + L_MIN(minarray[size + k - 2], buffer[startmin + k]); + } + + /* compute erosion values */ + starty = hsize + i * size; + lined = datad + starty * wpld; + SET_DATA_BYTE(lined, j, minarray[0]); + SET_DATA_BYTE(lined + (size - 1) * wpld, j, + minarray[2 * size - 2]); + for (k = 1; k < size - 1; k++) { + minval = L_MIN(minarray[k], minarray[k + size - 1]); + SET_DATA_BYTE(lined + wpld * k, j, minval); + } + } + } + } + + return; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/grayquant.c b/hgdriver/3rdparty/hgOCR/leptonica/grayquant.c new file mode 100644 index 0000000..d8b02d3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/grayquant.c @@ -0,0 +1,2908 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file grayquant.c + *
+ *
+ *      Thresholding from 8 bpp to 1 bpp
+ *
+ *          Floyd-Steinberg dithering to binary
+ *              PIX         *pixDitherToBinary()
+ *              PIX         *pixDitherToBinarySpec()
+ *              static void  ditherToBinaryLow()
+ *              void         ditherToBinaryLineLow()
+ *
+ *          Simple (pixelwise) binarization with fixed threshold
+ *              PIX         *pixThresholdToBinary()
+ *              static void  thresholdToBinaryLow()
+ *              void         thresholdToBinaryLineLow()
+ *
+ *          Binarization with variable threshold
+ *              PIX         *pixVarThresholdToBinary()
+ *
+ *          Binarization by adaptive mapping
+ *              PIX         *pixAdaptThresholdToBinary()
+ *              PIX         *pixAdaptThresholdToBinaryGen()
+ *
+ *          Generate a binary mask from pixels of particular values
+ *              PIX         *pixGenerateMaskByValue()
+ *              PIX         *pixGenerateMaskByBand()
+ *
+ *      Thresholding from 8 bpp to 2 bpp
+ *
+ *          Floyd-Steinberg-like dithering to 2 bpp
+ *              PIX         *pixDitherTo2bpp()
+ *              PIX         *pixDitherTo2bppSpec()
+ *              static void  ditherTo2bppLow()
+ *              static void  ditherTo2bppLineLow()
+ *              static l_int32  make8To2DitherTables()
+ *
+ *          Simple (pixelwise) thresholding to 2 bpp with optional cmap
+ *              PIX         *pixThresholdTo2bpp()
+ *              static void  thresholdTo2bppLow()
+ *
+ *      Simple (pixelwise) thresholding from 8 bpp to 4 bpp
+ *              PIX         *pixThresholdTo4bpp()
+ *              static void  thresholdTo4bppLow()
+ *
+ *      Simple (pixelwise) quantization on 8 bpp grayscale
+ *              PIX         *pixThresholdOn8bpp()
+ *
+ *      Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp
+ *              PIX         *pixThresholdGrayArb()
+ *
+ *      Quantization tables for linear thresholds of grayscale images
+ *              l_int32     *makeGrayQuantIndexTable()
+ *              static l_int32  *makeGrayQuantTargetTable()
+ *
+ *      Quantization table for arbitrary thresholding of grayscale images
+ *              l_int32      makeGrayQuantTableArb()
+ *              static l_int32   makeGrayQuantColormapArb()
+ *
+ *      Thresholding from 32 bpp rgb to 1 bpp
+ *      (really color quantization, but it's better placed in this file)
+ *              PIX         *pixGenerateMaskByBand32()
+ *              PIX         *pixGenerateMaskByDiscr32()
+ *
+ *      Histogram-based grayscale quantization
+ *              PIX         *pixGrayQuantFromHisto()
+ *              static l_int32  numaFillCmapFromHisto()
+ *
+ *      Color quantize grayscale image using existing colormap
+ *              PIX         *pixGrayQuantFromCmap()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +static void ditherToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_uint32 *bufs1, l_uint32 *bufs2, + l_int32 lowerclip, l_int32 upperclip); +static void thresholdToBinaryLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 d, + l_int32 wpls, l_int32 thresh); +static void ditherTo2bppLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, l_uint32 *bufs1, + l_uint32 *bufs2, l_int32 *tabval, l_int32 *tab38, + l_int32 *tab14); +static void ditherTo2bppLineLow(l_uint32 *lined, l_int32 w, l_uint32 *bufs1, + l_uint32 *bufs2, l_int32 *tabval, + l_int32 *tab38, l_int32 *tab14, + l_int32 lastlineflag); +static l_int32 make8To2DitherTables(l_int32 **ptabval, l_int32 **ptab38, + l_int32 **ptab14, l_int32 cliptoblack, + l_int32 cliptowhite); +static void thresholdTo2bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, l_int32 *tab); +static void thresholdTo4bppLow(l_uint32 *datad, l_int32 h, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, l_int32 *tab); +static l_int32 *makeGrayQuantTargetTable(l_int32 nlevels, l_int32 depth); +static l_int32 makeGrayQuantColormapArb(PIX *pixs, l_int32 *tab, + l_int32 outdepth, PIXCMAP **pcmap); +static l_int32 numaFillCmapFromHisto(NUMA *na, PIXCMAP *cmap, + l_float32 minfract, l_int32 maxsize, + l_int32 **plut); + +#ifndef NO_CONSOLE_IO +#define DEBUG_UNROLLING 0 +#endif /* ~NO_CONSOLE_IO */ + +/*------------------------------------------------------------------* + * Binarization by Floyd-Steinberg dithering * + *------------------------------------------------------------------*/ +/*! + * \brief pixDitherToBinary() + * + * \param[in] pixs + * \return pixd dithered binary, or NULL on error + * + * The Floyd-Steinberg error diffusion dithering algorithm + * binarizes an 8 bpp grayscale image to a threshold of 128. + * If a pixel has a value above 127, it is binarized to white + * and the excess below 255 is subtracted from three + * neighboring pixels in the fractions 3/8 to i, j+1, + * 3/8 to i+1, j) and 1/4 to (i+1,j+1, truncating to 0 + * if necessary. Likewise, if it the pixel has a value + * below 128, it is binarized to black and the excess above 0 + * is added to the neighboring pixels, truncating to 255 if necessary. + * + * This function differs from straight dithering in that it allows + * clipping of grayscale to 0 or 255 if the values are + * sufficiently close, without distribution of the excess. + * This uses default values to specify the range of lower + * and upper values near 0 and 255, rsp that are clipped + * to black and white without propagating the excess. + * Not propagating the excess has the effect of reducing the + * snake patterns in parts of the image that are nearly black or white; + * however, it also prevents the attempt to reproduce gray for those values. + * + * The implementation is straightforward. It uses a pair of + * line buffers to avoid changing pixs. It is about the same speed + * as pixDitherToBinaryLUT(), which uses three LUTs. + */ +PIX * +pixDitherToBinary(PIX *pixs) +{ + PROCNAME("pixDitherToBinary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); + + return pixDitherToBinarySpec(pixs, DEFAULT_CLIP_LOWER_1, + DEFAULT_CLIP_UPPER_1); +} + + +/*! + * \brief pixDitherToBinarySpec() + * + * \param[in] pixs + * \param[in] lowerclip lower clip distance to black; use 0 for default + * \param[in] upperclip upper clip distance to white; use 0 for default + * \return pixd dithered binary, or NULL on error + * + *
+ * Notes:
+ *      (1) See comments above in pixDitherToBinary() for details.
+ *      (2) The input parameters lowerclip and upperclip specify the range
+ *          of lower and upper values (near 0 and 255, rsp) that are
+ *          clipped to black and white without propagating the excess.
+ *          For that reason, lowerclip and upperclip should be small numbers.
+ * 
+ */ +PIX * +pixDitherToBinarySpec(PIX *pixs, + l_int32 lowerclip, + l_int32 upperclip) +{ +l_int32 w, h, d, wplt, wpld; +l_uint32 *datat, *datad; +l_uint32 *bufs1, *bufs2; +PIX *pixt, *pixd; + + PROCNAME("pixDitherToBinarySpec"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); + if (lowerclip < 0 || lowerclip > 255) + return (PIX *)ERROR_PTR("invalid value for lowerclip", procName, NULL); + if (upperclip < 0 || upperclip > 255) + return (PIX *)ERROR_PTR("invalid value for upperclip", procName, NULL); + + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Remove colormap if it exists */ + if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Two line buffers, 1 for current line and 2 for next line */ + bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + if (!bufs1 || !bufs2) { + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + pixDestroy(&pixd); + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); + } + + ditherToBinaryLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, + lowerclip, upperclip); + + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief ditherToBinaryLow() + * + * See comments in pixDitherToBinary() in binarize.c + */ +static void +ditherToBinaryLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 lowerclip, + l_int32 upperclip) +{ +l_int32 i; +l_uint32 *lined; + + /* do all lines except last line */ + memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ + for (i = 0; i < h - 1; i++) { + memcpy(bufs1, bufs2, 4 * wpls); + memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); + lined = datad + i * wpld; + ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0); + } + + /* do last line */ + memcpy(bufs1, bufs2, 4 * wpls); + lined = datad + (h - 1) * wpld; + ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1); +} + + +/*! + * \brief ditherToBinaryLineLow() + * + * \param[in] lined ptr to beginning of dest line + * \param[in] w width of image in pixels + * \param[in] bufs1 buffer of current source line + * \param[in] bufs2 buffer of next source line + * \param[in] lowerclip lower clip distance to black + * \param[in] upperclip upper clip distance to white + * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line + * \return void + * + * Dispatches FS error diffusion dithering for + * a single line of the image. If lastlineflag == 0, + * both source buffers are used; otherwise, only bufs1 + * is used. We use source buffers because the error + * is propagated into them, and we don't want to change + * the input src image. + * + * We break dithering out line by line to make it + * easier to combine functions like interpolative + * scaling and error diffusion dithering, as such a + * combination of operations obviates the need to + * generate a 2x grayscale image as an intermediary. + */ +void +ditherToBinaryLineLow(l_uint32 *lined, + l_int32 w, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 lowerclip, + l_int32 upperclip, + l_int32 lastlineflag) +{ +l_int32 j; +l_int32 oval, eval; +l_uint8 fval1, fval2, rval, bval, dval; + + if (lastlineflag == 0) { + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + if (oval > 127) { /* binarize to OFF */ + if ((eval = 255 - oval) > upperclip) { + /* subtract from neighbors */ + fval1 = (3 * eval) / 8; + fval2 = eval / 4; + rval = GET_DATA_BYTE(bufs1, j + 1); + rval = L_MAX(0, rval - fval1); + SET_DATA_BYTE(bufs1, j + 1, rval); + bval = GET_DATA_BYTE(bufs2, j); + bval = L_MAX(0, bval - fval1); + SET_DATA_BYTE(bufs2, j, bval); + dval = GET_DATA_BYTE(bufs2, j + 1); + dval = L_MAX(0, dval - fval2); + SET_DATA_BYTE(bufs2, j + 1, dval); + } + } else { /* oval <= 127; binarize to ON */ + SET_DATA_BIT(lined, j); /* ON pixel */ + if (oval > lowerclip) { + /* add to neighbors */ + fval1 = (3 * oval) / 8; + fval2 = oval / 4; + rval = GET_DATA_BYTE(bufs1, j + 1); + rval = L_MIN(255, rval + fval1); + SET_DATA_BYTE(bufs1, j + 1, rval); + bval = GET_DATA_BYTE(bufs2, j); + bval = L_MIN(255, bval + fval1); + SET_DATA_BYTE(bufs2, j, bval); + dval = GET_DATA_BYTE(bufs2, j + 1); + dval = L_MIN(255, dval + fval2); + SET_DATA_BYTE(bufs2, j + 1, dval); + } + } + } + + /* do last column: j = w - 1 */ + oval = GET_DATA_BYTE(bufs1, j); + if (oval > 127) { /* binarize to OFF */ + if ((eval = 255 - oval) > upperclip) { + /* subtract from neighbors */ + fval1 = (3 * eval) / 8; + bval = GET_DATA_BYTE(bufs2, j); + bval = L_MAX(0, bval - fval1); + SET_DATA_BYTE(bufs2, j, bval); + } + } else { /*oval <= 127; binarize to ON */ + SET_DATA_BIT(lined, j); /* ON pixel */ + if (oval > lowerclip) { + /* add to neighbors */ + fval1 = (3 * oval) / 8; + bval = GET_DATA_BYTE(bufs2, j); + bval = L_MIN(255, bval + fval1); + SET_DATA_BYTE(bufs2, j, bval); + } + } + } else { /* lastlineflag == 1 */ + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + if (oval > 127) { /* binarize to OFF */ + if ((eval = 255 - oval) > upperclip) { + /* subtract from neighbors */ + fval1 = (3 * eval) / 8; + rval = GET_DATA_BYTE(bufs1, j + 1); + rval = L_MAX(0, rval - fval1); + SET_DATA_BYTE(bufs1, j + 1, rval); + } + } else { /* oval <= 127; binarize to ON */ + SET_DATA_BIT(lined, j); /* ON pixel */ + if (oval > lowerclip) { + /* add to neighbors */ + fval1 = (3 * oval) / 8; + rval = GET_DATA_BYTE(bufs1, j + 1); + rval = L_MIN(255, rval + fval1); + SET_DATA_BYTE(bufs1, j + 1, rval); + } + } + } + + /* do last pixel: (i, j) = (h - 1, w - 1) */ + oval = GET_DATA_BYTE(bufs1, j); + if (oval < 128) + SET_DATA_BIT(lined, j); /* ON pixel */ + } +} + + +/*------------------------------------------------------------------* + * Simple (pixelwise) binarization with fixed threshold * + *------------------------------------------------------------------*/ +/*! + * \brief pixThresholdToBinary() + * + * \param[in] pixs 4 or 8 bpp + * \param[in] thresh threshold value + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If the source pixel is less than the threshold value,
+ *          the dest will be 1; otherwise, it will be 0.
+ *      (2) For example, for 8 bpp src pix, if %thresh == 256, the dest
+ *          1 bpp pix is all ones (fg), and if %thresh == 0, the dest
+ *          pix is all zeros (bg).
+ *
+ * 
+ */ +PIX * +pixThresholdToBinary(PIX *pixs, + l_int32 thresh) +{ +l_int32 d, w, h, wplt, wpld; +l_uint32 *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixThresholdToBinary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 4 && d != 8) + return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", procName, NULL); + if (thresh < 0) + return (PIX *)ERROR_PTR("thresh must be non-negative", procName, NULL); + if (d == 4 && thresh > 16) + return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", procName, NULL); + if (d == 8 && thresh > 256) + return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", procName, NULL); + + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Remove colormap if it exists. If there is a colormap, + * pixt will be 8 bpp regardless of the depth of pixs. */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + if (pixGetColormap(pixs) && d == 4) { /* promoted to 8 bpp */ + d = 8; + thresh *= 16; + } + + thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief thresholdToBinaryLow() + * + * If the source pixel is less than thresh, + * the dest will be 1; otherwise, it will be 0 + */ +static void +thresholdToBinaryLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 d, + l_int32 wpls, + l_int32 thresh) +{ +l_int32 i; +l_uint32 *lines, *lined; + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + thresholdToBinaryLineLow(lined, w, lines, d, thresh); + } +} + + +/* + * thresholdToBinaryLineLow() + * + */ +void +thresholdToBinaryLineLow(l_uint32 *lined, + l_int32 w, + l_uint32 *lines, + l_int32 d, + l_int32 thresh) +{ +l_int32 j, k, gval, scount, dcount; +l_uint32 sword, dword; + + PROCNAME("thresholdToBinaryLineLow"); + + switch (d) + { + case 4: + /* Unrolled as 4 source words, 1 dest word */ + for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { + dword = 0; + for (k = 0; k < 4; k++) { + sword = lines[scount++]; + dword <<= 8; + gval = (sword >> 28) & 0xf; + /* Trick used here and below: if gval < thresh then + * gval - thresh < 0, so its high-order bit is 1, and + * ((gval - thresh) >> 31) & 1 == 1; likewise, if + * gval >= thresh, then ((gval - thresh) >> 31) & 1 == 0 + * Doing it this way avoids a random (and thus easily + * mispredicted) branch on each pixel. */ + dword |= ((gval - thresh) >> 24) & 128; + gval = (sword >> 24) & 0xf; + dword |= ((gval - thresh) >> 25) & 64; + gval = (sword >> 20) & 0xf; + dword |= ((gval - thresh) >> 26) & 32; + gval = (sword >> 16) & 0xf; + dword |= ((gval - thresh) >> 27) & 16; + gval = (sword >> 12) & 0xf; + dword |= ((gval - thresh) >> 28) & 8; + gval = (sword >> 8) & 0xf; + dword |= ((gval - thresh) >> 29) & 4; + gval = (sword >> 4) & 0xf; + dword |= ((gval - thresh) >> 30) & 2; + gval = sword & 0xf; + dword |= ((gval - thresh) >> 31) & 1; + } + lined[dcount++] = dword; + } + + if (j < w) { + dword = 0; + for (; j < w; j++) { + if ((j & 7) == 0) { + sword = lines[scount++]; + } + gval = (sword >> 28) & 0xf; + sword <<= 4; + dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31)); + } + lined[dcount] = dword; + } +#if DEBUG_UNROLLING +#define CHECK_BIT(a, b, c) if (GET_DATA_BIT(a, b) != c) { \ + fprintf(stderr, "Error: mismatch at %d/%d(%d), %d vs %d\n", \ + j, w, d, GET_DATA_BIT(a, b), c); } + for (j = 0; j < w; j++) { + gval = GET_DATA_QBIT(lines, j); + CHECK_BIT(lined, j, gval < thresh ? 1 : 0); + } +#endif + break; + case 8: + /* Unrolled as 8 source words, 1 dest word */ + for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { + dword = 0; + for (k = 0; k < 8; k++) { + sword = lines[scount++]; + dword <<= 4; + gval = (sword >> 24) & 0xff; + dword |= ((gval - thresh) >> 28) & 8; + gval = (sword >> 16) & 0xff; + dword |= ((gval - thresh) >> 29) & 4; + gval = (sword >> 8) & 0xff; + dword |= ((gval - thresh) >> 30) & 2; + gval = sword & 0xff; + dword |= ((gval - thresh) >> 31) & 1; + } + lined[dcount++] = dword; + } + + if (j < w) { + dword = 0; + for (; j < w; j++) { + if ((j & 3) == 0) { + sword = lines[scount++]; + } + gval = (sword >> 24) & 0xff; + sword <<= 8; + dword |= (l_uint64)(((gval - thresh) >> 31) & 1) + << (31 - (j & 31)); + } + lined[dcount] = dword; + } +#if DEBUG_UNROLLING + for (j = 0; j < w; j++) { + gval = GET_DATA_BYTE(lines, j); + CHECK_BIT(lined, j, gval < thresh ? 1 : 0); + } +#undef CHECK_BIT +#endif + break; + default: + L_ERROR("src depth not 4 or 8 bpp\n", procName); + break; + } +} + + +/*------------------------------------------------------------------* + * Binarization with variable threshold * + *------------------------------------------------------------------*/ +/*! + * \brief pixVarThresholdToBinary() + * + * \param[in] pixs 8 bpp + * \param[in] pixg 8 bpp; contains threshold values for each pixel + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If the pixel in pixs is less than the corresponding pixel
+ *          in pixg, the dest will be 1; otherwise it will be 0.
+ * 
+ */ +PIX * +pixVarThresholdToBinary(PIX *pixs, + PIX *pixg) +{ +l_int32 i, j, vals, valg, w, h, d, wpls, wplg, wpld; +l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; +PIX *pixd; + + PROCNAME("pixVarThresholdToBinary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixg) + return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); + if (!pixSizesEqual(pixs, pixg)) + return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lineg = datag + i * wplg; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + valg = GET_DATA_BYTE(lineg, j); + if (vals < valg) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Binarization by adaptive mapping * + *------------------------------------------------------------------*/ +/*! + * \brief pixAdaptThresholdToBinary() + * + * \param[in] pixs 8 bpp + * \param[in] pixm [optional] 1 bpp image mask; can be null + * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a simple convenience function for doing adaptive
+ *          thresholding on a grayscale image with variable background.
+ *          It uses default parameters appropriate for typical text images.
+ *      (2) %pixm is a 1 bpp mask over "image" regions, which are not
+ *          expected to have a white background.  The mask inhibits
+ *          background finding under the fg pixels of the mask.  For
+ *          images with both text and image, the image regions would
+ *          be binarized (or quantized) by a different set of operations.
+ *      (3) As %gamma is increased, the foreground pixels are reduced.
+ *      (4) Under the covers:  The default background value for normalization
+ *          is 200, so we choose 170 for 'maxval' in pixGammaTRC.  Likewise,
+ *          the default foreground threshold for normalization is 60,
+ *          so we choose 50 for 'minval' in pixGammaTRC.  Because
+ *          170 was mapped to 255, choosing 200 for the threshold is
+ *          quite safe for avoiding speckle noise from the background.
+ * 
+ */ +PIX * +pixAdaptThresholdToBinary(PIX *pixs, + PIX *pixm, + l_float32 gamma) +{ + PROCNAME("pixAdaptThresholdToBinary"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + return pixAdaptThresholdToBinaryGen(pixs, pixm, gamma, 50, 170, 200); +} + + +/*! + * \brief pixAdaptThresholdToBinaryGen() + * + * \param[in] pixs 8 bpp + * \param[in] pixm [optional] 1 bpp image mask; can be null + * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 + * \param[in] blackval dark value to set to black (0) + * \param[in] whiteval light value to set to white (255) + * \param[in] thresh final threshold for binarization + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a convenience function for doing adaptive thresholding
+ *          on a grayscale image with variable background.  Also see notes
+ *          in pixAdaptThresholdToBinary().
+ *      (2) Reducing %gamma increases the foreground (text) pixels.
+ *          Use a low value (e.g., 0.5) for images with light text.
+ *      (3) For normal images, see default args in pixAdaptThresholdToBinary().
+ *          For images with very light text, these values are appropriate:
+ *             gamma     ~0.5
+ *             blackval  ~70
+ *             whiteval  ~190
+ *             thresh    ~200
+ * 
+ */ +PIX * +pixAdaptThresholdToBinaryGen(PIX *pixs, + PIX *pixm, + l_float32 gamma, + l_int32 blackval, + l_int32 whiteval, + l_int32 thresh) +{ +PIX *pix1, *pixd; + + PROCNAME("pixAdaptThresholdToBinaryGen"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + pix1 = pixBackgroundNormSimple(pixs, pixm, NULL); + pixGammaTRC(pix1, pix1, gamma, blackval, whiteval); + pixd = pixThresholdToBinary(pix1, thresh); + pixDestroy(&pix1); + return pixd; +} + + +/*--------------------------------------------------------------------* + * Generate a binary mask from pixels of particular value(s) * + *--------------------------------------------------------------------*/ +/*! + * \brief pixGenerateMaskByValue() + * + * \param[in] pixs 2, 4 or 8 bpp, or colormapped + * \param[in] val of pixels for which we set 1 in dest + * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) %val is the pixel value that we are selecting.  It can be
+ *          either a gray value or a colormap index.
+ *      (2) If pixs is colormapped, %usecmap determines if the colormap
+ *          index values are used, or if the colormap is removed to gray and
+ *          the gray values are used.  For the latter, it generates
+ *          an approximate grayscale value for each pixel, and then looks
+ *          for gray pixels with the value %val.
+ * 
+ */ +PIX * +pixGenerateMaskByValue(PIX *pixs, + l_int32 val, + l_int32 usecmap) +{ +l_int32 i, j, w, h, d, wplg, wpld; +l_uint32 *datag, *datad, *lineg, *lined; +PIX *pixg, *pixd; + + PROCNAME("pixGenerateMaskByValue"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", procName, NULL); + + if (!usecmap && pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + pixGetDimensions(pixg, &w, &h, &d); + if (d == 8 && (val < 0 || val > 255)) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("val out of 8 bpp range", procName, NULL); + } + if (d == 4 && (val < 0 || val > 15)) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("val out of 4 bpp range", procName, NULL); + } + if (d == 2 && (val < 0 || val > 3)) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("val out of 2 bpp range", procName, NULL); + } + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixg); + pixCopyInputFormat(pixd, pixs); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lineg = datag + i * wplg; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 8) { + if (GET_DATA_BYTE(lineg, j) == val) + SET_DATA_BIT(lined, j); + } else if (d == 4) { + if (GET_DATA_QBIT(lineg, j) == val) + SET_DATA_BIT(lined, j); + } else { /* d == 2 */ + if (GET_DATA_DIBIT(lineg, j) == val) + SET_DATA_BIT(lined, j); + } + } + } + + pixDestroy(&pixg); + return pixd; +} + + +/*! + * \brief pixGenerateMaskByBand() + * + * \param[in] pixs 2, 4 or 8 bpp, or colormapped + * \param[in] lower, upper two pixel values from which a range, either + * between (inband) or outside of (!inband), + * determines which pixels in pixs cause us to + * set a 1 in the dest mask + * \param[in] inband 1 for finding pixels in [lower, upper]; + * 0 for finding pixels in + * [0, lower) union (upper, 255] + * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a 1 bpp mask pixd, the same size as pixs, where
+ *          the fg pixels in the mask are those either within the specified
+ *          band (for inband == 1) or outside the specified band
+ *          (for inband == 0).
+ *      (2) If pixs is colormapped, %usecmap determines if the colormap
+ *          values are used, or if the colormap is removed to gray and
+ *          the gray values are used.  For the latter, it generates
+ *          an approximate grayscale value for each pixel, and then looks
+ *          for gray pixels with the value %val.
+ * 
+ */ +PIX * +pixGenerateMaskByBand(PIX *pixs, + l_int32 lower, + l_int32 upper, + l_int32 inband, + l_int32 usecmap) +{ +l_int32 i, j, w, h, d, wplg, wpld, val; +l_uint32 *datag, *datad, *lineg, *lined; +PIX *pixg, *pixd; + + PROCNAME("pixGenerateMaskByBand"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", procName, NULL); + if (lower < 0 || lower > upper) + return (PIX *)ERROR_PTR("lower < 0 or lower > upper!", procName, NULL); + + if (!usecmap && pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + pixGetDimensions(pixg, &w, &h, &d); + if (d == 8 && upper > 255) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("d == 8 and upper > 255", procName, NULL); + } + if (d == 4 && upper > 15) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("d == 4 and upper > 15", procName, NULL); + } + if (d == 2 && upper > 3) { + pixDestroy(&pixg); + return (PIX *)ERROR_PTR("d == 2 and upper > 3", procName, NULL); + } + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixg); + pixCopyInputFormat(pixd, pixs); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lineg = datag + i * wplg; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 8) + val = GET_DATA_BYTE(lineg, j); + else if (d == 4) + val = GET_DATA_QBIT(lineg, j); + else /* d == 2 */ + val = GET_DATA_DIBIT(lineg, j); + if (inband) { + if (val >= lower && val <= upper) + SET_DATA_BIT(lined, j); + } else { /* out of band */ + if (val < lower || val > upper) + SET_DATA_BIT(lined, j); + } + } + } + + pixDestroy(&pixg); + return pixd; +} + + +/*------------------------------------------------------------------* + * Thresholding to 2 bpp by dithering * + *------------------------------------------------------------------*/ +/*! + * \brief pixDitherTo2bpp() + * + * \param[in] pixs 8 bpp + * \param[in] cmapflag 1 to generate a colormap + * \return pixd dithered 2 bpp, or NULL on error + * + * An analog of the Floyd-Steinberg error diffusion dithering + * algorithm is used to "dibitize" an 8 bpp grayscale image + * to 2 bpp, using equally spaced gray values of 0, 85, 170, and 255, + * which are served by thresholds of 43, 128 and 213. + * If cmapflag == 1, the colormap values are set to 0, 85, 170 and 255. + * If a pixel has a value between 0 and 42, it is dibitized + * to 0, and the excess above 0 is added to the + * three neighboring pixels, in the fractions 3/8 to i, j+1, + * 3/8 to i+1, j) and 1/4 to (i+1, j+1, truncating to 255 if + * necessary. If a pixel has a value between 43 and 127, it is + * dibitized to 1, and the excess above 85 is added to the three + * neighboring pixels as before. If the value is below 85, the + * excess is subtracted. With a value between 128 + * and 212, it is dibitized to 2, with the excess on either side + * of 170 distributed as before. Finally, with a value between + * 213 and 255, it is dibitized to 3, with the excess below 255 + * subtracted from the neighbors. We always truncate to 0 or 255. + * The details can be seen in the lookup table generation. + * + * This function differs from straight dithering in that it allows + * clipping of grayscale to 0 or 255 if the values are + * sufficiently close, without distribution of the excess. + * This uses default values from pix.h to specify the range of lower + * and upper values near 0 and 255, rsp that are clipped to black + * and white without propagating the excess. + * Not propagating the excess has the effect of reducing the snake + * patterns in parts of the image that are nearly black or white; + * however, it also prevents any attempt to reproduce gray for those values. + * + * The implementation uses 3 lookup tables for simplicity, and + * a pair of line buffers to avoid modifying pixs. + */ +PIX * +pixDitherTo2bpp(PIX *pixs, + l_int32 cmapflag) +{ + PROCNAME("pixDitherTo2bpp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); + + return pixDitherTo2bppSpec(pixs, DEFAULT_CLIP_LOWER_2, + DEFAULT_CLIP_UPPER_2, cmapflag); +} + + +/*! + * \brief pixDitherTo2bppSpec() + * + * \param[in] pixs 8 bpp + * \param[in] lowerclip lower clip distance to black; use 0 for default + * \param[in] upperclip upper clip distance to white; use 0 for default + * \param[in] cmapflag 1 to generate a colormap + * \return pixd dithered 2 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) See comments above in pixDitherTo2bpp() for details.
+ *      (2) The input parameters lowerclip and upperclip specify the range
+ *          of lower and upper values (near 0 and 255, rsp) that are
+ *          clipped to black and white without propagating the excess.
+ *          For that reason, lowerclip and upperclip should be small numbers.
+ * 
+ */ +PIX * +pixDitherTo2bppSpec(PIX *pixs, + l_int32 lowerclip, + l_int32 upperclip, + l_int32 cmapflag) +{ +l_int32 w, h, d, wplt, wpld; +l_int32 *tabval, *tab38, *tab14; +l_uint32 *datat, *datad; +l_uint32 *bufs1, *bufs2; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixDitherTo2bppSpec"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); + if (lowerclip < 0 || lowerclip > 255) + return (PIX *)ERROR_PTR("invalid value for lowerclip", procName, NULL); + if (upperclip < 0 || upperclip > 255) + return (PIX *)ERROR_PTR("invalid value for upperclip", procName, NULL); + + if ((pixd = pixCreate(w, h, 2)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* If there is a colormap, remove it */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Two line buffers, 1 for current line and 2 for next line */ + bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + if (!bufs1 || !bufs2) { + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + pixDestroy(&pixd); + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); + } + + /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ + make8To2DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); + + ditherTo2bppLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, + tabval, tab38, tab14); + + if (cmapflag) { + cmap = pixcmapCreateLinear(2, 4); + pixSetColormap(pixd, cmap); + } + + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + LEPT_FREE(tabval); + LEPT_FREE(tab38); + LEPT_FREE(tab14); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief ditherTo2bppLow() + * + * Low-level function for doing Floyd-Steinberg error diffusion + * dithering from 8 bpp (datas) to 2 bpp (datad). Two source + * line buffers, bufs1 and bufs2, are provided, along with three + * 256-entry lookup tables: tabval gives the output pixel value, + * tab38 gives the extra (plus or minus) transferred to the pixels + * directly to the left and below, and tab14 gives the extra + * transferred to the diagonal below. The choice of 3/8 and 1/4 + * is traditional but arbitrary when you use a lookup table; the + * only constraint is that the sum is 1. See other comments + * below and in grayquant.c. + */ +static void +ditherTo2bppLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 *tabval, + l_int32 *tab38, + l_int32 *tab14) +{ +l_int32 i; +l_uint32 *lined; + + /* do all lines except last line */ + memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ + for (i = 0; i < h - 1; i++) { + memcpy(bufs1, bufs2, 4 * wpls); + memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); + lined = datad + i * wpld; + ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0); + } + + /* do last line */ + memcpy(bufs1, bufs2, 4 * wpls); + lined = datad + (h - 1) * wpld; + ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); +} + + +/*! + * \brief ditherTo2bppLineLow() + * + * \param[in] lined ptr to beginning of dest line + * \param[in] w width of image in pixels + * \param[in] bufs1 buffer of current source line + * \param[in] bufs2 buffer of next source line + * \param[in] tabval value to assign for current pixel + * \param[in] tab38 excess value to give to neighboring 3/8 pixels + * \param[in] tab14 excess value to give to neighboring 1/4 pixel + * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line + * \return void + * + * Dispatches error diffusion dithering for + * a single line of the image. If lastlineflag == 0, + * both source buffers are used; otherwise, only bufs1 + * is used. We use source buffers because the error + * is propagated into them, and we don't want to change + * the input src image. + * + * We break dithering out line by line to make it + * easier to combine functions like interpolative + * scaling and error diffusion dithering, as such a + * combination of operations obviates the need to + * generate a 2x grayscale image as an intermediary. + */ +static void +ditherTo2bppLineLow(l_uint32 *lined, + l_int32 w, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 *tabval, + l_int32 *tab38, + l_int32 *tab14, + l_int32 lastlineflag) +{ +l_int32 j; +l_int32 oval, tab38val, tab14val; +l_uint8 rval, bval, dval; + + if (lastlineflag == 0) { + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + SET_DATA_DIBIT(lined, j, tabval[oval]); + rval = GET_DATA_BYTE(bufs1, j + 1); + bval = GET_DATA_BYTE(bufs2, j); + dval = GET_DATA_BYTE(bufs2, j + 1); + tab38val = tab38[oval]; + tab14val = tab14[oval]; + if (tab38val < 0) { + rval = L_MAX(0, rval + tab38val); + bval = L_MAX(0, bval + tab38val); + dval = L_MAX(0, dval + tab14val); + } else { + rval = L_MIN(255, rval + tab38val); + bval = L_MIN(255, bval + tab38val); + dval = L_MIN(255, dval + tab14val); + } + SET_DATA_BYTE(bufs1, j + 1, rval); + SET_DATA_BYTE(bufs2, j, bval); + SET_DATA_BYTE(bufs2, j + 1, dval); + } + + /* do last column: j = w - 1 */ + oval = GET_DATA_BYTE(bufs1, j); + SET_DATA_DIBIT(lined, j, tabval[oval]); + bval = GET_DATA_BYTE(bufs2, j); + tab38val = tab38[oval]; + if (tab38val < 0) + bval = L_MAX(0, bval + tab38val); + else + bval = L_MIN(255, bval + tab38val); + SET_DATA_BYTE(bufs2, j, bval); + } else { /* lastlineflag == 1 */ + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + SET_DATA_DIBIT(lined, j, tabval[oval]); + rval = GET_DATA_BYTE(bufs1, j + 1); + tab38val = tab38[oval]; + if (tab38val < 0) + rval = L_MAX(0, rval + tab38val); + else + rval = L_MIN(255, rval + tab38val); + SET_DATA_BYTE(bufs1, j + 1, rval); + } + + /* do last pixel: (i, j) = (h - 1, w - 1) */ + oval = GET_DATA_BYTE(bufs1, j); + SET_DATA_DIBIT(lined, j, tabval[oval]); + } +} + + +/*! + * \brief make8To2DitherTables() + * + * \param[out] ptabval value assigned to output pixel; 0, 1, 2 or 3 + * \param[out] ptab38 amount propagated to pixels left and below + * \param[out] ptab14 amount propagated to pixel to left and down + * \param[in] cliptoblack values near 0 where the excess is not propagated + * \param[in] cliptowhite values near 255 where the deficit is not propagated + * + * \return 0 if OK, 1 on error + */ +static l_int32 +make8To2DitherTables(l_int32 **ptabval, + l_int32 **ptab38, + l_int32 **ptab14, + l_int32 cliptoblack, + l_int32 cliptowhite) +{ +l_int32 i; +l_int32 *tabval, *tab38, *tab14; + + /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ + tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + *ptabval = tabval; + *ptab38 = tab38; + *ptab14 = tab14; + + for (i = 0; i < 256; i++) { + if (i <= cliptoblack) { + tabval[i] = 0; + tab38[i] = 0; + tab14[i] = 0; + } else if (i < 43) { + tabval[i] = 0; + tab38[i] = (3 * i + 4) / 8; + tab14[i] = (i + 2) / 4; + } else if (i < 85) { + tabval[i] = 1; + tab38[i] = (3 * (i - 85) - 4) / 8; + tab14[i] = ((i - 85) - 2) / 4; + } else if (i < 128) { + tabval[i] = 1; + tab38[i] = (3 * (i - 85) + 4) / 8; + tab14[i] = ((i - 85) + 2) / 4; + } else if (i < 170) { + tabval[i] = 2; + tab38[i] = (3 * (i - 170) - 4) / 8; + tab14[i] = ((i - 170) - 2) / 4; + } else if (i < 213) { + tabval[i] = 2; + tab38[i] = (3 * (i - 170) + 4) / 8; + tab14[i] = ((i - 170) + 2) / 4; + } else if (i < 255 - cliptowhite) { + tabval[i] = 3; + tab38[i] = (3 * (i - 255) - 4) / 8; + tab14[i] = ((i - 255) - 2) / 4; + } else { /* i >= 255 - cliptowhite */ + tabval[i] = 3; + tab38[i] = 0; + tab14[i] = 0; + } + } + + return 0; +} + + +/*--------------------------------------------------------------------* + * Simple (pixelwise) thresholding to 2 bpp with optional colormap * + *--------------------------------------------------------------------*/ +/*! + * \brief pixThresholdTo2bpp() + * + * \param[in] pixs 8 bpp + * \param[in] nlevels equally spaced; must be between 2 and 4 + * \param[in] cmapflag 1 to build colormap; 0 otherwise + * \return pixd 2 bpp, optionally with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Valid values for nlevels is the set {2, 3, 4}.
+ *      (2) Any colormap on the input pixs is removed to 8 bpp grayscale.
+ *      (3) This function is typically invoked with cmapflag == 1.
+ *          In the situation where no colormap is desired, nlevels is
+ *          ignored and pixs is thresholded to 4 levels.
+ *      (4) The target output colors are equally spaced, with the
+ *          darkest at 0 and the lightest at 255.  The thresholds are
+ *          chosen halfway between adjacent output values.  A table
+ *          is built that specifies the mapping from src to dest.
+ *      (5) If cmapflag == 1, a colormap of size 'nlevels' is made,
+ *          and the pixel values in pixs are replaced by their
+ *          appropriate color indices.  The number of holdouts,
+ *          4 - nlevels, will be between 0 and 2.
+ *      (6) If you don't want the thresholding to be equally spaced,
+ *          either first transform the 8 bpp src using pixGammaTRC().
+ *          or, if cmapflag == 1, after calling this function you can use
+ *          pixcmapResetColor() to change any individual colors.
+ *      (7) If a colormap is generated, it will specify (to display
+ *          programs) exactly how each level is to be represented in RGB
+ *          space.  When representing text, 3 levels is far better than
+ *          2 because of the antialiasing of the single gray level,
+ *          and 4 levels (black, white and 2 gray levels) is getting
+ *          close to the perceptual quality of a (nearly continuous)
+ *          grayscale image.  With 2 bpp, you can set up a colormap
+ *          and allocate from 2 to 4 levels to represent antialiased text.
+ *          Any left over colormap entries can be used for coloring regions.
+ *          For the same number of levels, the file size of a 2 bpp image
+ *          is about 10% smaller than that of a 4 bpp result for the same
+ *          number of levels.  For both 2 bpp and 4 bpp, using 4 levels you
+ *          get compression far better than that of jpeg, because the
+ *          quantization to 4 levels will remove the jpeg ringing in the
+ *          background near character edges.
+ * 
+ */ +PIX * +pixThresholdTo2bpp(PIX *pixs, + l_int32 nlevels, + l_int32 cmapflag) +{ +l_int32 *qtab; +l_int32 w, h, d, wplt, wpld; +l_uint32 *datat, *datad; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixThresholdTo2bpp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (nlevels < 2 || nlevels > 4) + return (PIX *)ERROR_PTR("nlevels not in {2, 3, 4}", procName, NULL); + + if ((pixd = pixCreate(w, h, 2)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + if (cmapflag) { /* hold out (4 - nlevels) cmap entries */ + cmap = pixcmapCreateLinear(2, nlevels); + pixSetColormap(pixd, cmap); + } + + /* If there is a colormap in the src, remove it */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Make the appropriate table */ + if (cmapflag) + qtab = makeGrayQuantIndexTable(nlevels); + else + qtab = makeGrayQuantTargetTable(4, 2); + + thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); + + LEPT_FREE(qtab); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief thresholdTo2bppLow() + * + * Low-level function for thresholding from 8 bpp (datas) to + * 2 bpp (datad), using thresholds implicitly defined through %tab, + * a 256-entry lookup table that gives a 2-bit output value + * for each possible input. + * + * For each line, unroll the loop so that for each 32 bit src word, + * representing four consecutive 8-bit pixels, we compose one byte + * of output consisiting of four 2-bit pixels. + */ +static void +thresholdTo2bppLow(l_uint32 *datad, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 *tab) +{ +l_uint8 sval1, sval2, sval3, sval4, dval; +l_int32 i, j, k; +l_uint32 *lines, *lined; + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wpls; j++) { + k = 4 * j; + sval1 = GET_DATA_BYTE(lines, k); + sval2 = GET_DATA_BYTE(lines, k + 1); + sval3 = GET_DATA_BYTE(lines, k + 2); + sval4 = GET_DATA_BYTE(lines, k + 3); + dval = (tab[sval1] << 6) | (tab[sval2] << 4) | + (tab[sval3] << 2) | tab[sval4]; + SET_DATA_BYTE(lined, j, dval); + } + } +} + + +/*----------------------------------------------------------------------* + * Simple (pixelwise) thresholding to 4 bpp * + *----------------------------------------------------------------------*/ +/*! + * \brief pixThresholdTo4bpp() + * + * \param[in] pixs 8 bpp, can have colormap + * \param[in] nlevels equally spaced; must be between 2 and 16 + * \param[in] cmapflag 1 to build colormap; 0 otherwise + * \return pixd 4 bpp, optionally with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Valid values for nlevels is the set {2, ... 16}.
+ *      (2) Any colormap on the input pixs is removed to 8 bpp grayscale.
+ *      (3) This function is typically invoked with cmapflag == 1.
+ *          In the situation where no colormap is desired, nlevels is
+ *          ignored and pixs is thresholded to 16 levels.
+ *      (4) The target output colors are equally spaced, with the
+ *          darkest at 0 and the lightest at 255.  The thresholds are
+ *          chosen halfway between adjacent output values.  A table
+ *          is built that specifies the mapping from src to dest.
+ *      (5) If cmapflag == 1, a colormap of size 'nlevels' is made,
+ *          and the pixel values in pixs are replaced by their
+ *          appropriate color indices.  The number of holdouts,
+ *          16 - nlevels, will be between 0 and 14.
+ *      (6) If you don't want the thresholding to be equally spaced,
+ *          either first transform the 8 bpp src using pixGammaTRC().
+ *          or, if cmapflag == 1, after calling this function you can use
+ *          pixcmapResetColor() to change any individual colors.
+ *      (7) If a colormap is generated, it will specify, to display
+ *          programs, exactly how each level is to be represented in RGB
+ *          space.  When representing text, 3 levels is far better than
+ *          2 because of the antialiasing of the single gray level,
+ *          and 4 levels (black, white and 2 gray levels) is getting
+ *          close to the perceptual quality of a (nearly continuous)
+ *          grayscale image.  Therefore, with 4 bpp, you can set up a
+ *          colormap, allocate a relatively small fraction of the 16
+ *          possible values to represent antialiased text, and use the
+ *          other colormap entries for other things, such as coloring
+ *          text or background.  Two other reasons for using a small number
+ *          of gray values for antialiased text are (1) PNG compression
+ *          gets worse as the number of levels that are used is increased,
+ *          and (2) using a small number of levels will filter out most of
+ *          the jpeg ringing that is typically introduced near sharp edges
+ *          of text.  This filtering is partly responsible for the improved
+ *          compression.
+ * 
+ */ +PIX * +pixThresholdTo4bpp(PIX *pixs, + l_int32 nlevels, + l_int32 cmapflag) +{ +l_int32 *qtab; +l_int32 w, h, d, wplt, wpld; +l_uint32 *datat, *datad; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixThresholdTo4bpp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (nlevels < 2 || nlevels > 16) + return (PIX *)ERROR_PTR("nlevels not in [2,...,16]", procName, NULL); + + if ((pixd = pixCreate(w, h, 4)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + if (cmapflag) { /* hold out (16 - nlevels) cmap entries */ + cmap = pixcmapCreateLinear(4, nlevels); + pixSetColormap(pixd, cmap); + } + + /* If there is a colormap in the src, remove it */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Make the appropriate table */ + if (cmapflag) + qtab = makeGrayQuantIndexTable(nlevels); + else + qtab = makeGrayQuantTargetTable(16, 4); + + thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); + + LEPT_FREE(qtab); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief thresholdTo4bppLow() + * + * Low-level function for thresholding from 8 bpp (datas) to + * 4 bpp (datad), using thresholds implicitly defined through %tab, + * a 256-entry lookup table that gives a 4-bit output value + * for each possible input. + * + * For each line, unroll the loop so that for each 32 bit src word, + * representing four consecutive 8-bit pixels, we compose two bytes + * of output consisiting of four 4-bit pixels. + */ +static void +thresholdTo4bppLow(l_uint32 *datad, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 *tab) +{ +l_uint8 sval1, sval2, sval3, sval4; +l_uint16 dval; +l_int32 i, j, k; +l_uint32 *lines, *lined; + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wpls; j++) { + k = 4 * j; + sval1 = GET_DATA_BYTE(lines, k); + sval2 = GET_DATA_BYTE(lines, k + 1); + sval3 = GET_DATA_BYTE(lines, k + 2); + sval4 = GET_DATA_BYTE(lines, k + 3); + dval = (tab[sval1] << 12) | (tab[sval2] << 8) | + (tab[sval3] << 4) | tab[sval4]; + SET_DATA_TWO_BYTES(lined, j, dval); + } + } +} + + +/*----------------------------------------------------------------------* + * Simple (pixelwise) thresholding on 8 bpp with optional colormap * + *----------------------------------------------------------------------*/ +/*! + * \brief pixThresholdOn8bpp() + * + * \param[in] pixs 8 bpp, can have colormap + * \param[in] nlevels equally spaced; must be between 2 and 256 + * \param[in] cmapflag 1 to build colormap; 0 otherwise + * \return pixd 8 bpp, optionally with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Valid values for nlevels is the set {2,...,256}.
+ *      (2) Any colormap on the input pixs is removed to 8 bpp grayscale.
+ *      (3) If cmapflag == 1, a colormap of size 'nlevels' is made,
+ *          and the pixel values in pixs are replaced by their
+ *          appropriate color indices.  Otherwise, the pixel values
+ *          are the actual thresholded (i.e., quantized) grayscale values.
+ *      (4) If you don't want the thresholding to be equally spaced,
+ *          first transform the input 8 bpp src using pixGammaTRC().
+ * 
+ */ +PIX * +pixThresholdOn8bpp(PIX *pixs, + l_int32 nlevels, + l_int32 cmapflag) +{ +l_int32 *qtab; /* quantization table */ +l_int32 i, j, w, h, wpld, val, newval; +l_uint32 *datad, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixThresholdOn8bpp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (nlevels < 2 || nlevels > 256) + return (PIX *)ERROR_PTR("nlevels not in [2,...,256]", procName, NULL); + + /* Get a new pixd; if there is a colormap in the src, remove it */ + if (pixGetColormap(pixs)) + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixd = pixCopy(NULL, pixs); + + if (cmapflag) { /* hold out (256 - nlevels) cmap entries */ + cmap = pixcmapCreateLinear(8, nlevels); + pixSetColormap(pixd, cmap); + } + + if (cmapflag) + qtab = makeGrayQuantIndexTable(nlevels); + else + qtab = makeGrayQuantTargetTable(nlevels, 8); + + pixGetDimensions(pixd, &w, &h, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lined, j); + newval = qtab[val]; + SET_DATA_BYTE(lined, j, newval); + } + } + + LEPT_FREE(qtab); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp * + *----------------------------------------------------------------------*/ +/*! + * \brief pixThresholdGrayArb() + * + * \param[in] pixs 8 bpp grayscale; can have colormap + * \param[in] edgevals string giving edge value of each bin + * \param[in] outdepth 0, 2, 4 or 8 bpp; 0 is default for min depth + * \param[in] use_average 1 if use the average pixel value in colormap + * \param[in] setblack 1 if darkest color is set to black + * \param[in] setwhite 1 if lightest color is set to white + * \return pixd 2, 4 or 8 bpp quantized image with colormap, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This function allows exact specification of the quantization bins.
+ *          The string %edgevals is a space-separated set of values
+ *          specifying the dividing points between output quantization bins.
+ *          These threshold values are assigned to the bin with higher
+ *          values, so that each of them is the smallest value in their bin.
+ *      (2) The output image (pixd) depth is specified by %outdepth.  The
+ *          number of bins is the number of edgevals + 1.  The
+ *          relation between outdepth and the number of bins is:
+ *               outdepth = 2       nbins <= 4
+ *               outdepth = 4       nbins <= 16
+ *               outdepth = 8       nbins <= 256
+ *          With %outdepth == 0, the minimum required depth for the
+ *          given number of bins is used.
+ *          The output pixd has a colormap.
+ *      (3) The last 3 args determine the specific values that go into
+ *          the colormap.
+ *      (4) For %use_average:
+ *            ~ if TRUE, the average value of pixels falling in the bin is
+ *              chosen as the representative gray value.  Otherwise,
+ *            ~ if FALSE, the central value of each bin is chosen as
+ *              the representative value.
+ *          The colormap holds the representative value.
+ *      (5) For %setblack, if TRUE the darkest color is set to (0,0,0).
+ *      (6) For %setwhite, if TRUE the lightest color is set to (255,255,255).
+ *      (7) An alternative to using this function to quantize to
+ *          unequally-spaced bins is to first transform the 8 bpp pixs
+ *          using pixGammaTRC(), and follow this with pixThresholdTo4bpp().
+ * 
+ */ +PIX * +pixThresholdGrayArb(PIX *pixs, + const char *edgevals, + l_int32 outdepth, + l_int32 use_average, + l_int32 setblack, + l_int32 setwhite) +{ +l_int32 *qtab; +l_int32 w, h, d, i, j, n, wplt, wpld, val, newval; +l_uint32 *datat, *datad, *linet, *lined; +NUMA *na; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixThresholdGrayArb"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (!edgevals) + return (PIX *)ERROR_PTR("edgevals not defined", procName, NULL); + if (outdepth != 0 && outdepth != 2 && outdepth != 4 && outdepth != 8) + return (PIX *)ERROR_PTR("invalid outdepth", procName, NULL); + + /* Parse and sort (if required) the bin edge values */ + na = parseStringForNumbers(edgevals, " \t\n,"); + n = numaGetCount(na); + if (n > 255) { + numaDestroy(&na); + return (PIX *)ERROR_PTR("more than 256 levels", procName, NULL); + } + if (outdepth == 0) { + if (n <= 3) + outdepth = 2; + else if (n <= 15) + outdepth = 4; + else + outdepth = 8; + } else if (n + 1 > (1 << outdepth)) { + L_WARNING("outdepth too small; setting to 8 bpp\n", procName); + outdepth = 8; + } + numaSort(na, na, L_SORT_INCREASING); + + /* Make the quantization LUT and the colormap */ + makeGrayQuantTableArb(na, outdepth, &qtab, &cmap); + if (use_average) { /* use the average value in each bin */ + pixcmapDestroy(&cmap); + makeGrayQuantColormapArb(pixs, qtab, outdepth, &cmap); + } + pixcmapSetBlackAndWhite(cmap, setblack, setwhite); + numaDestroy(&na); + + if ((pixd = pixCreate(w, h, outdepth)) == NULL) { + LEPT_FREE(qtab); + pixcmapDestroy(&cmap); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixSetColormap(pixd, cmap); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* If there is a colormap in the src, remove it */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + if (outdepth == 2) { + thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); + } else if (outdepth == 4) { + thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); + } else { + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linet = datat + i * wplt; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + newval = qtab[val]; + SET_DATA_BYTE(lined, j, newval); + } + } + } + + LEPT_FREE(qtab); + pixDestroy(&pixt); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Quantization tables for linear thresholds of grayscale images * + *----------------------------------------------------------------------*/ +/*! + * \brief makeGrayQuantIndexTable() + * + * \param[in] nlevels number of output levels + * \return table maps input gray level to colormap index, + * or NULL on error + *
+ * Notes:
+ *      (1) 'nlevels' is some number between 2 and 256 (typically 8 or less).
+ *      (2) The table is typically used for quantizing 2, 4 and 8 bpp
+ *          grayscale src pix, and generating a colormapped dest pix.
+ * 
+ */ +l_int32 * +makeGrayQuantIndexTable(l_int32 nlevels) +{ +l_int32 *tab; +l_int32 i, j, thresh; + + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) { + for (j = 0; j < nlevels; j++) { + thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); + if (i <= thresh) { + tab[i] = j; +/* fprintf(stderr, "tab[%d] = %d\n", i, j); */ + break; + } + } + } + return tab; +} + + +/*! + * \brief makeGrayQuantTargetTable() + * + * \param[in] nlevels number of output levels + * \param[in] depth of dest pix, in bpp; 2, 4 or 8 bpp + * \return table maps input gray level to thresholded gray level, + * or NULL on error + * + *
+ * Notes:
+ *      (1) nlevels is some number between 2 and 2^(depth)
+ *      (2) The table is used in two similar ways:
+ *           ~ for 8 bpp, it quantizes to a given number of target levels
+ *           ~ for 2 and 4 bpp, it thresholds to appropriate target values
+ *             that will use the full dynamic range of the dest pix.
+ *      (3) For depth = 8, the number of thresholds chosen is
+ *          ('nlevels' - 1), and the 'nlevels' values stored in the
+ *          table are at the two at the extreme ends, (0, 255), plus
+ *          plus ('nlevels' - 2) values chosen at equal intervals between.
+ *          For example, for depth = 8 and 'nlevels' = 3, the two
+ *          threshold values are 3f and bf, and the three target pixel
+ *          values are 0, 7f and ff.
+ *      (4) For depth < 8, we ignore nlevels, and always use the maximum
+ *          number of levels, which is 2^(depth).
+ *          If you want nlevels < the maximum number, you should always
+ *          use a colormap.
+ * 
+ */ +static l_int32 * +makeGrayQuantTargetTable(l_int32 nlevels, + l_int32 depth) +{ +l_int32 *tab; +l_int32 i, j, thresh, maxval, quantval; + + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + maxval = (1 << depth) - 1; + if (depth < 8) + nlevels = 1 << depth; + for (i = 0; i < 256; i++) { + for (j = 0; j < nlevels; j++) { + thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); + if (i <= thresh) { + quantval = maxval * j / (nlevels - 1); + tab[i] = quantval; +/* fprintf(stderr, "tab[%d] = %d\n", i, tab[i]); */ + break; + } + } + } + return tab; +} + + +/*----------------------------------------------------------------------* + * Quantization table for arbitrary thresholding of grayscale images * + *----------------------------------------------------------------------*/ +/*! + * \brief makeGrayQuantTableArb() + * + * \param[in] na numa of bin boundaries + * \param[in] outdepth of colormap: 1, 2, 4 or 8 + * \param[out] ptab table mapping input gray level to cmap index + * \param[out] pcmap colormap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The number of bins is the count of %na + 1.
+ *      (2) The bin boundaries in na must be sorted in increasing order.
+ *      (3) The table is an inverse colormap: it maps input gray level
+ *          to colormap index (the bin number).
+ *      (4) The colormap generated here has quantized values at the
+ *          center of each bin.  If you want to use the average gray
+ *          value of pixels within the bin, discard the colormap and
+ *          compute it using makeGrayQuantColormapArb().
+ *      (5) Returns an error if there are not enough levels in the
+ *          output colormap for the number of bins.  The number
+ *          of bins must not exceed 2^outdepth.
+ * 
+ */ +l_ok +makeGrayQuantTableArb(NUMA *na, + l_int32 outdepth, + l_int32 **ptab, + PIXCMAP **pcmap) +{ +l_int32 i, j, n, jstart, ave, val; +l_int32 *tab; +PIXCMAP *cmap; + + PROCNAME("makeGrayQuantTableArb"); + + if (!ptab) + return ERROR_INT("&tab not defined", procName, 1); + *ptab = NULL; + if (!pcmap) + return ERROR_INT("&cmap not defined", procName, 1); + *pcmap = NULL; + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (n + 1 > (1 << outdepth)) + return ERROR_INT("more bins than cmap levels", procName, 1); + + if ((cmap = pixcmapCreate(outdepth)) == NULL) + return ERROR_INT("cmap not made", procName, 1); + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + *ptab = tab; + *pcmap = cmap; + + /* First n bins */ + jstart = 0; + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &val); + ave = (jstart + val) / 2; + pixcmapAddColor(cmap, ave, ave, ave); + for (j = jstart; j < val; j++) + tab[j] = i; + jstart = val; + } + + /* Last bin */ + ave = (jstart + 255) / 2; + pixcmapAddColor(cmap, ave, ave, ave); + for (j = jstart; j < 256; j++) + tab[j] = n; + + return 0; +} + + +/*! + * \brief makeGrayQuantColormapArb() + * + * \param[in] pixs 8 bpp + * \param[in] tab table mapping input gray level to cmap index + * \param[in] outdepth of colormap: 1, 2, 4 or 8 + * \param[out] pcmap colormap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The table is a 256-entry inverse colormap: it maps input gray
+ *          level to colormap index (the bin number).  It is computed
+ *          using makeGrayQuantTableArb().
+ *      (2) The colormap generated here has quantized values at the
+ *          average gray value of the pixels that are in each bin.
+ *      (3) Returns an error if there are not enough levels in the
+ *          output colormap for the number of bins.  The number
+ *          of bins must not exceed 2^outdepth.
+ * 
+ */ +static l_int32 +makeGrayQuantColormapArb(PIX *pixs, + l_int32 *tab, + l_int32 outdepth, + PIXCMAP **pcmap) +{ +l_int32 i, j, index, w, h, d, nbins, wpl, factor, val; +l_int32 *bincount, *binave, *binstart; +l_uint32 *line, *data; + + PROCNAME("makeGrayQuantColormapArb"); + + if (!pcmap) + return ERROR_INT("&cmap not defined", procName, 1); + *pcmap = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return ERROR_INT("pixs not 8 bpp", procName, 1); + if (!tab) + return ERROR_INT("tab not defined", procName, 1); + nbins = tab[255] + 1; + if (nbins > (1 << outdepth)) + return ERROR_INT("more bins than cmap levels", procName, 1); + + /* Find the count and weighted count for each bin */ + if ((bincount = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) + return ERROR_INT("calloc fail for bincount", procName, 1); + if ((binave = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) { + LEPT_FREE(bincount); + return ERROR_INT("calloc fail for binave", procName, 1); + } + factor = (l_int32)(sqrt((l_float64)(w * h) / 30000.) + 0.5); + factor = L_MAX(1, factor); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + val = GET_DATA_BYTE(line, j); + bincount[tab[val]]++; + binave[tab[val]] += val; + } + } + + /* Find the smallest gray values in each bin */ + binstart = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); + for (i = 1, index = 1; i < 256; i++) { + if (tab[i] < index) continue; + if (tab[i] == index) + binstart[index++] = i; + } + + /* Get the averages. If there are no samples in a bin, use + * the center value of the bin. */ + *pcmap = pixcmapCreate(outdepth); + for (i = 0; i < nbins; i++) { + if (bincount[i]) { + val = binave[i] / bincount[i]; + } else { /* no samples in the bin */ + if (i < nbins - 1) + val = (binstart[i] + binstart[i + 1]) / 2; + else /* last bin */ + val = (binstart[i] + 255) / 2; + } + pixcmapAddColor(*pcmap, val, val, val); + } + + LEPT_FREE(bincount); + LEPT_FREE(binave); + LEPT_FREE(binstart); + return 0; +} + + +/*--------------------------------------------------------------------* + * Thresholding from 32 bpp rgb to 1 bpp * + *--------------------------------------------------------------------*/ +/*! + * \brief pixGenerateMaskByBand32() + * + * \param[in] pixs 32 bpp + * \param[in] refval reference rgb value + * \param[in] delm max amount below the ref value for any component + * \param[in] delp max amount above the ref value for any component + * \param[in] fractm fractional amount below ref value for all components + * \param[in] fractp fractional amount above ref value for all components + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a 1 bpp mask pixd, the same size as pixs, where
+ *          the fg pixels in the mask within a band of rgb values
+ *          surrounding %refval.  The band can be chosen in two ways
+ *          for each component:
+ *          (a) Use (%delm, %delp) to specify how many levels down and up
+ *          (b) Use (%fractm, %fractp) to specify the fractional
+ *              distance toward 0 and 255, respectively.
+ *          Note that %delm and %delp must be in [0 ... 255], whereas
+ *          %fractm and %fractp must be in [0.0 - 1.0].
+ *      (2) Either (%delm, %delp) or (%fractm, %fractp) can be used.
+ *          Set each value in the other pair to 0.
+ * 
+ */ +PIX * +pixGenerateMaskByBand32(PIX *pixs, + l_uint32 refval, + l_int32 delm, + l_int32 delp, + l_float32 fractm, + l_float32 fractp) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_int32 rref, gref, bref, rval, gval, bval; +l_int32 rmin, gmin, bmin, rmax, gmax, bmax; +l_uint32 pixel; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixGenerateMaskByBand32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("not 32 bpp", procName, NULL); + if (delm < 0 || delp < 0) + return (PIX *)ERROR_PTR("delm and delp must be >= 0", procName, NULL); + if (fractm < 0.0 || fractm > 1.0 || fractp < 0.0 || fractp > 1.0) + return (PIX *)ERROR_PTR("fractm and/or fractp invalid", procName, NULL); + + extractRGBValues(refval, &rref, &gref, &bref); + if (fractm == 0.0 && fractp == 0.0) { + rmin = rref - delm; + gmin = gref - delm; + bmin = bref - delm; + rmax = rref + delm; + gmax = gref + delm; + bmax = bref + delm; + } else if (delm == 0 && delp == 0) { + rmin = (l_int32)((1.0 - fractm) * rref); + gmin = (l_int32)((1.0 - fractm) * gref); + bmin = (l_int32)((1.0 - fractm) * bref); + rmax = rref + (l_int32)(fractp * (255 - rref)); + gmax = gref + (l_int32)(fractp * (255 - gref)); + bmax = bref + (l_int32)(fractp * (255 - bref)); + } else { + L_ERROR("bad input: either (delm, delp) or (fractm, fractp) " + "must be 0\n", procName); + return NULL; + } + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = lines[j]; + rval = (pixel >> L_RED_SHIFT) & 0xff; + if (rval < rmin || rval > rmax) + continue; + gval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (gval < gmin || gval > gmax) + continue; + bval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (bval < bmin || bval > bmax) + continue; + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*! + * \brief pixGenerateMaskByDiscr32() + * + * \param[in] pixs 32 bpp + * \param[in] refval1 reference rgb value + * \param[in] refval2 reference rgb value + * \param[in] distflag L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a 1 bpp mask pixd, the same size as pixs, where
+ *          the fg pixels in the mask are those where the pixel in pixs
+ *          is "closer" to refval1 than to refval2.
+ *      (2) "Closer" can be defined in several ways, such as:
+ *            ~ manhattan distance (L1)
+ *            ~ euclidean distance (L2)
+ *            ~ majority vote of the individual components
+ *          Here, we have a choice of L1 or L2.
+ * 
+ */ +PIX * +pixGenerateMaskByDiscr32(PIX *pixs, + l_uint32 refval1, + l_uint32 refval2, + l_int32 distflag) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_int32 rref1, gref1, bref1, rref2, gref2, bref2, rval, gval, bval; +l_uint32 pixel, dist1, dist2; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixGenerateMaskByDiscr32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("not 32 bpp", procName, NULL); + if (distflag != L_MANHATTAN_DISTANCE && distflag != L_EUCLIDEAN_DISTANCE) + return (PIX *)ERROR_PTR("invalid distflag", procName, NULL); + + extractRGBValues(refval1, &rref1, &gref1, &bref1); + extractRGBValues(refval2, &rref2, &gref2, &bref2); + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + pixel = lines[j]; + extractRGBValues(pixel, &rval, &gval, &bval); + if (distflag == L_MANHATTAN_DISTANCE) { + dist1 = L_ABS(rref1 - rval); + dist2 = L_ABS(rref2 - rval); + dist1 += L_ABS(gref1 - gval); + dist2 += L_ABS(gref2 - gval); + dist1 += L_ABS(bref1 - bval); + dist2 += L_ABS(bref2 - bval); + } else { + dist1 = (rref1 - rval) * (rref1 - rval); + dist2 = (rref2 - rval) * (rref2 - rval); + dist1 += (gref1 - gval) * (gref1 - gval); + dist2 += (gref2 - gval) * (gref2 - gval); + dist1 += (bref1 - bval) * (bref1 - bval); + dist2 += (bref2 - bval) * (bref2 - bval); + } + if (dist1 < dist2) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*----------------------------------------------------------------------* + * Histogram-based grayscale quantization * + *----------------------------------------------------------------------*/ +/*! + * \brief pixGrayQuantFromHisto() + * + * \param[in] pixd [optional] quantized pix with cmap; can be null + * \param[in] pixs 8 bpp gray input pix; not cmapped + * \param[in] pixm [optional] mask over pixels in pixs to quantize + * \param[in] minfract minimum fraction of pixels in a set of adjacent + * histo bins that causes the set to be automatically + * set aside as a color in the colormap; must be + * at least 0.01 + * \param[in] maxsize maximum number of adjacent bins allowed to represent + * a color, regardless of the population of pixels + * in the bins; must be at least 2 + * \return pixd 8 bpp, cmapped, or NULL on error + * + *
+ * Notes:
+ *      (1) This is useful for quantizing images with relatively few
+ *          colors, but which may have both color and gray pixels.
+ *          If there are color pixels, it is assumed that an input
+ *          rgb image has been color quantized first so that:
+ *            ~ pixd has a colormap describing the color pixels
+ *            ~ pixm is a mask over the non-color pixels in pixd
+ *            ~ the colormap in pixd, and the color pixels in pixd,
+ *              have been repacked to go from 0 to n-1 (n colors)
+ *          If there are no color pixels, pixd and pixm are both null,
+ *          and all pixels in pixs are quantized to gray.
+ *      (2) A 256-entry histogram is built of the gray values in pixs.
+ *          If pixm exists, the pixels contributing to the histogram are
+ *          restricted to the fg of pixm.  A colormap and LUT are generated
+ *          from this histogram.  We break up the array into a set
+ *          of intervals, each one constituting a color in the colormap:
+ *          An interval is identified by summing histogram bins until
+ *          either the sum equals or exceeds the %minfract of the total
+ *          number of pixels, or the span itself equals or exceeds %maxsize.
+ *          The color of each bin is always an average of the pixels
+ *          that constitute it.
+ *      (3) Note that we do not specify the number of gray colors in
+ *          the colormap.  Instead, we specify two parameters that
+ *          describe the accuracy of the color assignments; this and
+ *          the actual image determine the number of resulting colors.
+ *      (4) If a mask exists and it is not the same size as pixs, make
+ *          a new mask the same size as pixs, with the original mask
+ *          aligned at the UL corners.  Set all additional pixels
+ *          in the (larger) new mask set to 1, causing those pixels
+ *          in pixd to be set as gray.
+ *      (5) We estimate the total number of colors (color plus gray);
+ *          if it exceeds 255, return null.
+ * 
+ */ +PIX * +pixGrayQuantFromHisto(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_float32 minfract, + l_int32 maxsize) +{ +l_int32 w, h, wd, hd, wm, hm, wpls, wplm, wpld; +l_int32 nc, nestim, i, j, vals, vald; +l_int32 *lut; +l_uint32 *datas, *datam, *datad, *lines, *linem, *lined; +NUMA *na; +PIX *pixmr; /* resized mask */ +PIXCMAP *cmap; + + PROCNAME("pixGrayQuantFromHisto"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (minfract < 0.01) { + L_WARNING("minfract < 0.01; setting to 0.05\n", procName); + minfract = 0.05; + } + if (maxsize < 2) { + L_WARNING("maxsize < 2; setting to 10\n", procName); + maxsize = 10; + } + if ((pixd && !pixm) || (!pixd && pixm)) + return (PIX *)ERROR_PTR("(pixd,pixm) not defined together", + procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm not 1 bpp", procName, NULL); + if ((cmap = pixGetColormap(pixd)) == NULL) + return (PIX *)ERROR_PTR("pixd not cmapped", procName, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + if (w != wd || h != hd) + return (PIX *)ERROR_PTR("pixs, pixd sizes differ", procName, NULL); + nc = pixcmapGetCount(cmap); + nestim = nc + (l_int32)(1.5 * 255 / maxsize); + fprintf(stderr, "nestim = %d\n", nestim); + if (nestim > 255) { + L_ERROR("Estimate %d colors!\n", procName, nestim); + return (PIX *)ERROR_PTR("probably too many colors", procName, NULL); + } + pixGetDimensions(pixm, &wm, &hm, NULL); + if (w != wm || h != hm) { /* resize the mask */ + L_WARNING("mask and dest sizes not equal\n", procName); + pixmr = pixCreateNoInit(w, h, 1); + pixRasterop(pixmr, 0, 0, wm, hm, PIX_SRC, pixm, 0, 0); + pixRasterop(pixmr, wm, 0, w - wm, h, PIX_SET, NULL, 0, 0); + pixRasterop(pixmr, 0, hm, wm, h - hm, PIX_SET, NULL, 0, 0); + } else { + pixmr = pixClone(pixm); + } + } else { + pixd = pixCreateTemplate(pixs); + cmap = pixcmapCreate(8); + pixSetColormap(pixd, cmap); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Use original mask, if it exists, to select gray pixels */ + na = pixGetGrayHistogramMasked(pixs, pixm, 0, 0, 1); + + /* Fill out the cmap with gray colors, and generate the lut + * for pixel assignment. Issue a warning on failure. */ + if (numaFillCmapFromHisto(na, cmap, minfract, maxsize, &lut)) + L_ERROR("ran out of colors in cmap!\n", procName); + numaDestroy(&na); + + /* Assign the gray pixels to their cmap indices */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + if (!pixm) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + vald = lut[vals]; + SET_DATA_BYTE(lined, j, vald); + } + } + LEPT_FREE(lut); + return pixd; + } + + datam = pixGetData(pixmr); + wplm = pixGetWpl(pixmr); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (!GET_DATA_BIT(linem, j)) + continue; + vals = GET_DATA_BYTE(lines, j); + vald = lut[vals]; + SET_DATA_BYTE(lined, j, vald); + } + } + pixDestroy(&pixmr); + LEPT_FREE(lut); + return pixd; +} + + +/*! + * \brief numaFillCmapFromHisto() + * + * \param[in] na histogram of gray values + * \param[in] cmap 8 bpp cmap, possibly initialized with color value + * \param[in] minfract minimum fraction of pixels in a set of adjacent + * histo bins that causes the set to be automatically + * set aside as a color in the colormap; must be + * at least 0.01 + * \param[in] maxsize maximum number of adjacent bins allowed to represent + * a color, regardless of the population of pixels + * in the bins; must be at least 2 + * \param[out] plut lookup table from gray value to colormap index + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This static function must be called from pixGrayQuantFromHisto()
+ * 
+ */ +static l_int32 +numaFillCmapFromHisto(NUMA *na, + PIXCMAP *cmap, + l_float32 minfract, + l_int32 maxsize, + l_int32 **plut) +{ +l_int32 mincount, index, sum, wtsum, span, istart, i, val, ret; +l_int32 *iahisto, *lut; +l_float32 total; + + PROCNAME("numaFillCmapFromHisto"); + + if (!plut) + return ERROR_INT("&lut not defined", procName, 1); + *plut = NULL; + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + + numaGetSum(na, &total); + mincount = (l_int32)(minfract * total); + iahisto = numaGetIArray(na); + lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + *plut = lut; + index = pixcmapGetCount(cmap); /* start with number of colors + * already reserved */ + + /* March through, associating colors with sets of adjacent + * gray levels. During the process, the LUT that gives + * the colormap index for each gray level is computed. + * To complete a color, either the total count must equal + * or exceed %mincount, or the current span of colors must + * equal or exceed %maxsize. An empty span is not converted + * into a color; it is simply ignored. When a span is completed for a + * color, the weighted color in the span is added to the colormap. */ + sum = 0; + wtsum = 0; + istart = 0; + ret = 0; + for (i = 0; i < 256; i++) { + lut[i] = index; + sum += iahisto[i]; + wtsum += i * iahisto[i]; + span = i - istart + 1; + if (sum < mincount && span < maxsize) + continue; + + if (sum == 0) { /* empty span; don't save */ + istart = i + 1; + continue; + } + + /* Found new color; sum > 0 */ + val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); + ret = pixcmapAddColor(cmap, val, val, val); + istart = i + 1; + sum = 0; + wtsum = 0; + index++; + } + if (istart < 256 && sum > 0) { /* last one */ + span = 256 - istart; + val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); + ret = pixcmapAddColor(cmap, val, val, val); + } + + LEPT_FREE(iahisto); + return ret; +} + + +/*----------------------------------------------------------------------* + * Color quantize grayscale image using existing colormap * + *----------------------------------------------------------------------*/ +/*! + * \brief pixGrayQuantFromCmap() + * + * \param[in] pixs 8 bpp grayscale without cmap + * \param[in] cmap to quantize to; of dest pix + * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp + * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error + * + *
+ * Notes:
+ *      (1) In use, pixs is an 8 bpp grayscale image without a colormap.
+ *          If there is an existing colormap, a warning is issued and
+ *          a copy of the input pixs is returned.
+ * 
+ */ +PIX * +pixGrayQuantFromCmap(PIX *pixs, + PIXCMAP *cmap, + l_int32 mindepth) +{ +l_int32 i, j, index, w, h, d, depth, wpls, wpld; +l_int32 hascolor, vals, vald; +l_int32 *tab; +l_uint32 *datas, *datad, *lines, *lined; +PIXCMAP *cmapd; +PIX *pixd; + + PROCNAME("pixGrayQuantFromCmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) { + L_WARNING("pixs already has a colormap; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (!cmap) + return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); + if (mindepth != 2 && mindepth != 4 && mindepth != 8) + return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); + + /* Make sure the colormap is gray */ + pixcmapHasColor(cmap, &hascolor); + if (hascolor) { + L_WARNING("Converting colormap colors to gray\n", procName); + cmapd = pixcmapColorToGray(cmap, 0.3, 0.5, 0.2); + } else { + cmapd = pixcmapCopy(cmap); + } + + /* Make LUT into colormap */ + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) { + pixcmapGetNearestGrayIndex(cmapd, i, &index); + tab[i] = index; + } + + pixcmapGetMinDepth(cmap, &depth); + depth = L_MAX(depth, mindepth); + pixd = pixCreate(w, h, depth); + pixSetColormap(pixd, cmapd); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + vald = tab[vals]; + if (depth == 2) + SET_DATA_DIBIT(lined, j, vald); + else if (depth == 4) + SET_DATA_QBIT(lined, j, vald); + else /* depth == 8 */ + SET_DATA_BYTE(lined, j, vald); + } + } + + LEPT_FREE(tab); + return pixd; +} + + +#if 0 /* Documentation */ +/*--------------------------------------------------------------------* + * Implementation of binarization by dithering using LUTs * + * It is archived here. * + *--------------------------------------------------------------------*/ +/*! + * \brief pixDitherToBinaryLUT() + * + * \param[in] pixs + * \param[in] lowerclip lower clip distance to black; use -1 for default + * \param[in] upperclip upper clip distance to white; use -1 for default + * \return pixd dithered binary, or NULL on error + * + * We don't need two implementations of Floyd-Steinberg dithering, + * and this one with LUTs is a little more complicated than + * pixDitherToBinary(). It uses three lookup tables to generate the + * output pixel value and the excess or deficit carried over to the + * neighboring pixels. It's here for pedagogical reasons only. + */ +PIX * +pixDitherToBinaryLUT(PIX *pixs, + l_int32 lowerclip, + l_int32 upperclip) +{ +l_int32 w, h, d, wplt, wpld; +l_int32 *tabval, *tab38, *tab14; +l_uint32 *datat, *datad; +l_uint32 *bufs1, *bufs2; +PIX *pixt, *pixd; + + PROCNAME("pixDitherToBinaryLUT"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); + if (lowerclip < 0) + lowerclip = DEFAULT_CLIP_LOWER_1; + if (upperclip < 0) + upperclip = DEFAULT_CLIP_UPPER_1; + + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Remove colormap if it exists */ + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Two line buffers, 1 for current line and 2 for next line */ + bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); + if (!bufs1 || !bufs2) { + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + pixDestroy(&pixd); + pixDestroy(&pixt); + return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); + } + + /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ + make8To1DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); + + ditherToBinaryLUTLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, + tabval, tab38, tab14); + + LEPT_FREE(bufs1); + LEPT_FREE(bufs2); + LEPT_FREE(tabval); + LEPT_FREE(tab38); + LEPT_FREE(tab14); + pixDestroy(&pixt); + return pixd; +} + +/*! + * \brief ditherToBinaryLUTLow() + * + * Low-level function for doing Floyd-Steinberg error diffusion + * dithering from 8 bpp (datas) to 1 bpp (datad). Two source + * line buffers, bufs1 and bufs2, are provided, along with three + * 256-entry lookup tables: tabval gives the output pixel value, + * tab38 gives the extra (plus or minus) transferred to the pixels + * directly to the left and below, and tab14 gives the extra + * transferred to the diagonal below. The choice of 3/8 and 1/4 + * is traditional but arbitrary when you use a lookup table; the + * only constraint is that the sum is 1. See other comments below. + */ +void +ditherToBinaryLUTLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 *tabval, + l_int32 *tab38, + l_int32 *tab14) +{ +l_int32 i; +l_uint32 *lined; + + /* do all lines except last line */ + memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ + for (i = 0; i < h - 1; i++) { + memcpy(bufs1, bufs2, 4 * wpls); + memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); + lined = datad + i * wpld; + ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, + tabval, tab38, tab14, 0); + } + + /* do last line */ + memcpy(bufs1, bufs2, 4 * wpls); + lined = datad + (h - 1) * wpld; + ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); + return; +} + +/*! + * \brief ditherToBinaryLineLUTLow() + * + * \param[in] lined ptr to beginning of dest line + * \param[in] w width of image in pixels + * \param[in] bufs1 buffer of current source line + * \param[in] bufs2 buffer of next source line + * \param[in] tabval value to assign for current pixel + * \param[in] tab38 excess value to give to neighboring 3/8 pixels + * \param[in] tab14 excess value to give to neighboring 1/4 pixel + * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line + * \return void + */ +void +ditherToBinaryLineLUTLow(l_uint32 *lined, + l_int32 w, + l_uint32 *bufs1, + l_uint32 *bufs2, + l_int32 *tabval, + l_int32 *tab38, + l_int32 *tab14, + l_int32 lastlineflag) +{ +l_int32 j; +l_int32 oval, tab38val, tab14val; +l_uint8 rval, bval, dval; + + if (lastlineflag == 0) { + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + if (tabval[oval]) + SET_DATA_BIT(lined, j); + rval = GET_DATA_BYTE(bufs1, j + 1); + bval = GET_DATA_BYTE(bufs2, j); + dval = GET_DATA_BYTE(bufs2, j + 1); + tab38val = tab38[oval]; + if (tab38val == 0) + continue; + tab14val = tab14[oval]; + if (tab38val < 0) { + rval = L_MAX(0, rval + tab38val); + bval = L_MAX(0, bval + tab38val); + dval = L_MAX(0, dval + tab14val); + } else { + rval = L_MIN(255, rval + tab38val); + bval = L_MIN(255, bval + tab38val); + dval = L_MIN(255, dval + tab14val); + } + SET_DATA_BYTE(bufs1, j + 1, rval); + SET_DATA_BYTE(bufs2, j, bval); + SET_DATA_BYTE(bufs2, j + 1, dval); + } + + /* do last column: j = w - 1 */ + oval = GET_DATA_BYTE(bufs1, j); + if (tabval[oval]) + SET_DATA_BIT(lined, j); + bval = GET_DATA_BYTE(bufs2, j); + tab38val = tab38[oval]; + if (tab38val < 0) { + bval = L_MAX(0, bval + tab38val); + SET_DATA_BYTE(bufs2, j, bval); + } else if (tab38val > 0 ) { + bval = L_MIN(255, bval + tab38val); + SET_DATA_BYTE(bufs2, j, bval); + } + } else { /* lastlineflag == 1 */ + for (j = 0; j < w - 1; j++) { + oval = GET_DATA_BYTE(bufs1, j); + if (tabval[oval]) + SET_DATA_BIT(lined, j); + rval = GET_DATA_BYTE(bufs1, j + 1); + tab38val = tab38[oval]; + if (tab38val == 0) + continue; + if (tab38val < 0) + rval = L_MAX(0, rval + tab38val); + else + rval = L_MIN(255, rval + tab38val); + SET_DATA_BYTE(bufs1, j + 1, rval); + } + + /* do last pixel: (i, j) = (h - 1, w - 1) */ + oval = GET_DATA_BYTE(bufs1, j); + if (tabval[oval]) + SET_DATA_BIT(lined, j); + } + + return; +} + +/*! + * \brief make8To1DitherTables() + * + * \param[out] ptabval value assigned to output pixel; 0 or 1 + * \param[out] ptab38 amount propagated to pixels left and below + * \param[out] ptab14 amount propagated to pixel to left and down + * \param[in] lowerclip values near 0 where the excess is not propagated + * \param[in] upperclip values near 255 where the deficit is not propagated + * + * \return 0 if OK, 1 on error + */ +l_ok +make8To1DitherTables(l_int32 **ptabval, + l_int32 **ptab38, + l_int32 **ptab14, + l_int32 lowerclip, + l_int32 upperclip) +{ +l_int32 i; +l_int32 *tabval, *tab38, *tab14; + + PROCNAME("make8To1DitherTables"); + + if (ptabval) *ptabval = NULL; + if (ptab38) *ptab38 = NULL; + if (ptab14) *ptab14 = NULL; + if (!ptabval || !ptab38 || !ptab14) + return ERROR_INT("table ptrs not all defined", procName, 1); + + /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ + tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (!tabval || !tab38 || !tab14) + return ERROR_INT("calloc failure to make small table", procName, 1); + *ptabval = tabval; + *ptab38 = tab38; + *ptab14 = tab14; + + for (i = 0; i < 256; i++) { + if (i <= lowerclip) { + tabval[i] = 1; + tab38[i] = 0; + tab14[i] = 0; + } else if (i < 128) { + tabval[i] = 1; + tab38[i] = (3 * i + 4) / 8; + tab14[i] = (i + 2) / 4; + } else if (i < 255 - upperclip) { + tabval[i] = 0; + tab38[i] = (3 * (i - 255) + 4) / 8; + tab14[i] = ((i - 255) + 2) / 4; + } else { /* i >= 255 - upperclip */ + tabval[i] = 0; + tab38[i] = 0; + tab14[i] = 0; + } + } + + return 0; +} +#endif /* Documentation */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/heap.c b/hgdriver/3rdparty/hgOCR/leptonica/heap.c new file mode 100644 index 0000000..e089a96 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/heap.c @@ -0,0 +1,545 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file heap.c + *
+ *
+ *      Create/Destroy L_Heap
+ *          L_HEAP         *lheapCreate()
+ *          void           *lheapDestroy()
+ *
+ *      Operations to add/remove to/from the heap
+ *          l_int32         lheapAdd()
+ *          static l_int32  lheapExtendArray()
+ *          void           *lheapRemove()
+ *
+ *      Heap operations
+ *          l_int32         lheapSwapUp()
+ *          l_int32         lheapSwapDown()
+ *          l_int32         lheapSort()
+ *          l_int32         lheapSortStrictOrder()
+ *
+ *      Accessors
+ *          l_int32         lheapGetCount()
+ *
+ *      Debug output
+ *          l_int32         lheapPrint()
+ *
+ *    The L_Heap is useful to implement a priority queue, that is sorted
+ *    on a key in each element of the heap.  The heap is an array
+ *    of nearly arbitrary structs, with a l_float32 the first field.
+ *    This field is the key on which the heap is sorted.
+ *
+ *    Internally, we keep track of the heap size, n.  The item at the
+ *    root of the heap is at the head of the array.  Items are removed
+ *    from the head of the array and added to the end of the array.
+ *    When an item is removed from the head, the item at the end
+ *    of the array is moved to the head.  When items are either
+ *    added or removed, it is usually necessary to swap array items
+ *    to restore the heap order.  It is guaranteed that the number
+ *    of swaps does not exceed log(n).
+ *
+ *    --------------------------  N.B.  ------------------------------
+ *    The items on the heap (or, equivalently, in the array) are cast
+ *    to void*.  Their key is a l_float32, and it is REQUIRED that the
+ *    key be the first field in the struct.  That allows us to get the
+ *    key by simply dereferencing the struct.  Alternatively, we could
+ *    choose (but don't) to pass an application-specific comparison
+ *    function into the heap operation functions.
+ *    --------------------------  N.B.  ------------------------------
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + +#define SWAP_ITEMS(i, j) { void *tempitem = lh->array[(i)]; \ + lh->array[(i)] = lh->array[(j)]; \ + lh->array[(j)] = tempitem; } + + /* Static function */ +static l_int32 lheapExtendArray(L_HEAP *lh); + + +/*--------------------------------------------------------------------------* + * L_Heap create/destroy * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapCreate() + * + * \param[in] n size of ptr array to be alloc'd; use 0 for default + * \param[in] direction L_SORT_INCREASING, L_SORT_DECREASING + * \return lheap, or NULL on error + */ +L_HEAP * +lheapCreate(l_int32 n, + l_int32 direction) +{ +L_HEAP *lh; + + PROCNAME("lheapCreate"); + + if (n < InitialPtrArraySize || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + /* Allocate ptr array and initialize counters. */ + lh = (L_HEAP *)LEPT_CALLOC(1, sizeof(L_HEAP)); + if ((lh->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { + lheapDestroy(&lh, FALSE); + return (L_HEAP *)ERROR_PTR("ptr array not made", procName, NULL); + } + lh->nalloc = n; + lh->n = 0; + lh->direction = direction; + return lh; +} + + +/*! + * \brief lheapDestroy() + * + * \param[in,out] plh will be set to null before returning + * \param[in] freeflag TRUE to free each remaining struct in the array + * \return void + * + *
+ * Notes:
+ *      (1) Use %freeflag == TRUE when the items in the array can be
+ *          simply destroyed using free.  If those items require their
+ *          own destroy function, they must be destroyed before
+ *          calling this function, and then this function is called
+ *          with %freeflag == FALSE.
+ *      (2) To destroy the lheap, we destroy the ptr array, then
+ *          the lheap, and then null the contents of the input ptr.
+ * 
+ */ +void +lheapDestroy(L_HEAP **plh, + l_int32 freeflag) +{ +l_int32 i; +L_HEAP *lh; + + PROCNAME("lheapDestroy"); + + if (plh == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((lh = *plh) == NULL) + return; + + if (freeflag) { /* free each struct in the array */ + for (i = 0; i < lh->n; i++) + LEPT_FREE(lh->array[i]); + } else if (lh->n > 0) { /* freeflag == FALSE but elements exist on array */ + L_WARNING("memory leak of %d items in lheap!\n", procName, lh->n); + } + + if (lh->array) + LEPT_FREE(lh->array); + LEPT_FREE(lh); + *plh = NULL; + + return; +} + +/*--------------------------------------------------------------------------* + * Accessors * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapAdd() + * + * \param[in] lh heap + * \param[in] item to be added to the tail of the heap + * \return 0 if OK, 1 on error + */ +l_ok +lheapAdd(L_HEAP *lh, + void *item) +{ + PROCNAME("lheapAdd"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (!item) + return ERROR_INT("item not defined", procName, 1); + + /* If necessary, expand the allocated array by a factor of 2 */ + if (lh->n >= lh->nalloc) + lheapExtendArray(lh); + + /* Add the item */ + lh->array[lh->n] = item; + lh->n++; + + /* Restore the heap */ + lheapSwapUp(lh, lh->n - 1); + return 0; +} + + +/*! + * \brief lheapExtendArray() + * + * \param[in] lh heap + * \return 0 if OK, 1 on error + */ +static l_int32 +lheapExtendArray(L_HEAP *lh) +{ + PROCNAME("lheapExtendArray"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + if ((lh->array = (void **)reallocNew((void **)&lh->array, + sizeof(void *) * lh->nalloc, + 2 * sizeof(void *) * lh->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + lh->nalloc = 2 * lh->nalloc; + return 0; +} + + +/*! + * \brief lheapRemove() + * + * \param[in] lh heap + * \return ptr to item popped from the root of the heap, + * or NULL if the heap is empty or on error + */ +void * +lheapRemove(L_HEAP *lh) +{ +void *item; + + PROCNAME("lheapRemove"); + + if (!lh) + return (void *)ERROR_PTR("lh not defined", procName, NULL); + + if (lh->n == 0) + return NULL; + + item = lh->array[0]; + lh->array[0] = lh->array[lh->n - 1]; /* move last to the head */ + lh->array[lh->n - 1] = NULL; /* set ptr to null */ + lh->n--; + + lheapSwapDown(lh); /* restore the heap */ + return item; +} + + +/*! + * \brief lheapGetCount() + * + * \param[in] lh heap + * \return count, or 0 on error + */ +l_int32 +lheapGetCount(L_HEAP *lh) +{ + PROCNAME("lheapGetCount"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 0); + + return lh->n; +} + + + +/*--------------------------------------------------------------------------* + * Heap operations * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapSwapUp() + * + * \param[in] lh heap + * \param[in] index of array corresponding to node to be swapped up + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is called after a new item is put on the heap, at the
+ *          bottom of a complete tree.
+ *      (2) To regain the heap order, we let it bubble up,
+ *          iteratively swapping with its parent, until it either
+ *          reaches the root of the heap or it finds a parent that
+ *          is in the correct position already vis-a-vis the child.
+ * 
+ */ +l_ok +lheapSwapUp(L_HEAP *lh, + l_int32 index) +{ +l_int32 ip; /* index to heap for parent; 1 larger than array index */ +l_int32 ic; /* index into heap for child */ +l_float32 valp, valc; + + PROCNAME("lheapSwapUp"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (index < 0 || index >= lh->n) + return ERROR_INT("invalid index", procName, 1); + + ic = index + 1; /* index into heap: add 1 to array index */ + if (lh->direction == L_SORT_INCREASING) { + while (1) { + if (ic == 1) /* root of heap */ + break; + ip = ic / 2; + valc = *(l_float32 *)(lh->array[ic - 1]); + valp = *(l_float32 *)(lh->array[ip - 1]); + if (valp <= valc) + break; + SWAP_ITEMS(ip - 1, ic - 1); + ic = ip; + } + } else { /* lh->direction == L_SORT_DECREASING */ + while (1) { + if (ic == 1) /* root of heap */ + break; + ip = ic / 2; + valc = *(l_float32 *)(lh->array[ic - 1]); + valp = *(l_float32 *)(lh->array[ip - 1]); + if (valp >= valc) + break; + SWAP_ITEMS(ip - 1, ic - 1); + ic = ip; + } + } + return 0; +} + + +/*! + * \brief lheapSwapDown() + * + * \param[in] lh heap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is called after an item has been popped off the
+ *          root of the heap, and the last item in the heap has
+ *          been placed at the root.
+ *      (2) To regain the heap order, we let it bubble down,
+ *          iteratively swapping with one of its children.  For a
+ *          decreasing sort, it swaps with the largest child; for
+ *          an increasing sort, the smallest.  This continues until
+ *          it either reaches the lowest level in the heap, or the
+ *          parent finds that neither child should swap with it
+ *          (e.g., for a decreasing heap, the parent is larger
+ *          than or equal to both children).
+ * 
+ */ +l_ok +lheapSwapDown(L_HEAP *lh) +{ +l_int32 ip; /* index to heap for parent; 1 larger than array index */ +l_int32 icr, icl; /* index into heap for left/right children */ +l_float32 valp, valcl, valcr; + + PROCNAME("lheapSwapDown"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (lheapGetCount(lh) < 1) + return 0; + + ip = 1; /* index into top of heap: corresponds to array[0] */ + if (lh->direction == L_SORT_INCREASING) { + while (1) { + icl = 2 * ip; + if (icl > lh->n) + break; + valp = *(l_float32 *)(lh->array[ip - 1]); + valcl = *(l_float32 *)(lh->array[icl - 1]); + icr = icl + 1; + if (icr > lh->n) { /* only a left child; no iters below */ + if (valp > valcl) + SWAP_ITEMS(ip - 1, icl - 1); + break; + } else { /* both children exist; swap with the smallest if bigger */ + valcr = *(l_float32 *)(lh->array[icr - 1]); + if (valp <= valcl && valp <= valcr) /* smaller than both */ + break; + if (valcl <= valcr) { /* left smaller; swap */ + SWAP_ITEMS(ip - 1, icl - 1); + ip = icl; + } else { /* right smaller; swap */ + SWAP_ITEMS(ip - 1, icr - 1); + ip = icr; + } + } + } + } else { /* lh->direction == L_SORT_DECREASING */ + while (1) { + icl = 2 * ip; + if (icl > lh->n) + break; + valp = *(l_float32 *)(lh->array[ip - 1]); + valcl = *(l_float32 *)(lh->array[icl - 1]); + icr = icl + 1; + if (icr > lh->n) { /* only a left child; no iters below */ + if (valp < valcl) + SWAP_ITEMS(ip - 1, icl - 1); + break; + } else { /* both children exist; swap with the biggest if smaller */ + valcr = *(l_float32 *)(lh->array[icr - 1]); + if (valp >= valcl && valp >= valcr) /* bigger than both */ + break; + if (valcl >= valcr) { /* left bigger; swap */ + SWAP_ITEMS(ip - 1, icl - 1); + ip = icl; + } else { /* right bigger; swap */ + SWAP_ITEMS(ip - 1, icr - 1); + ip = icr; + } + } + } + } + + return 0; +} + + +/*! + * \brief lheapSort() + * + * \param[in] lh heap, with internal array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This sorts an array into heap order.  If the heap is already
+ *          in heap order for the direction given, this has no effect.
+ * 
+ */ +l_ok +lheapSort(L_HEAP *lh) +{ +l_int32 i; + + PROCNAME("lheapSort"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + for (i = 0; i < lh->n; i++) + lheapSwapUp(lh, i); + + return 0; +} + + +/*! + * \brief lheapSortStrictOrder() + * + * \param[in] lh heap, with internal array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This sorts a heap into strict order.
+ *      (2) For each element, starting at the end of the array and
+ *          working forward, the element is swapped with the head
+ *          element and then allowed to swap down onto a heap of
+ *          size reduced by one.  The result is that the heap is
+ *          reversed but in strict order.  The array elements are
+ *          then reversed to put it in the original order.
+ * 
+ */ +l_ok +lheapSortStrictOrder(L_HEAP *lh) +{ +l_int32 i, index, size; + + PROCNAME("lheapSortStrictOrder"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + /* Start from a sorted heap */ + lheapSort(lh); + + size = lh->n; /* save the actual size */ + for (i = 0; i < size; i++) { + index = size - i; + SWAP_ITEMS(0, index - 1); + lh->n--; /* reduce the apparent heap size by 1 */ + lheapSwapDown(lh); + } + lh->n = size; /* restore the size */ + + for (i = 0; i < size / 2; i++) /* reverse */ + SWAP_ITEMS(i, size - i - 1); + + return 0; +} + + + +/*---------------------------------------------------------------------* + * Debug output * + *---------------------------------------------------------------------*/ +/*! + * \brief lheapPrint() + * + * \param[in] fp file stream + * \param[in] lh heap + * \return 0 if OK; 1 on error + */ +l_ok +lheapPrint(FILE *fp, + L_HEAP *lh) +{ +l_int32 i; + + PROCNAME("lheapPrint"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + fprintf(fp, "\n L_Heap: nalloc = %d, n = %d, array = %p\n", + lh->nalloc, lh->n, lh->array); + for (i = 0; i < lh->n; i++) + fprintf(fp, "keyval[%d] = %f\n", i, *(l_float32 *)lh->array[i]); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/heap.h b/hgdriver/3rdparty/hgOCR/leptonica/heap.h new file mode 100644 index 0000000..d39b06b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/heap.h @@ -0,0 +1,87 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_HEAP_H +#define LEPTONICA_HEAP_H + +/*! + * \file heap.h + * + *
+ *      Expandable priority queue configured as a heap for arbitrary void* data
+ *
+ *      The L_Heap is used to implement a priority queue.  The elements
+ *      in the heap are ordered in either increasing or decreasing key value.
+ *      The key is a float field 'keyval' that is required to be
+ *      contained in the elements of the queue.
+ *
+ *      The heap is a simple binary tree with the following constraints:
+ *         - the key of each node is >= the keys of the two children
+ *         - the tree is complete, meaning that each level (1, 2, 4, ...)
+ *           is filled and the last level is filled from left to right
+ *
+ *      The tree structure is implicit in the queue array, with the
+ *      array elements numbered as a breadth-first search of the tree
+ *      from left to right.  It is thus guaranteed that the largest
+ *      (or smallest) key belongs to the first element in the array.
+ *
+ *      Heap sort is used to sort the array.  Once an array has been
+ *      sorted as a heap, it is convenient to use it as a priority queue,
+ *      because the min (or max) elements are always at the root of
+ *      the tree (element 0), and once removed, the heap can be
+ *      resorted in not more than log[n] steps, where n is the number
+ *      of elements on the heap.  Likewise, if an arbitrary element is
+ *      added to the end of the array A, the sorted heap can be restored
+ *      in not more than log[n] steps.
+ *
+ *      A L_Heap differs from a L_Queue in that the elements in the former
+ *      are sorted by a key.  Internally, the array is maintained
+ *      as a queue, with a pointer to the end of the array.  The
+ *      head of the array always remains at array[0].  The array is
+ *      maintained (sorted) as a heap.  When an item is removed from
+ *      the head, the last item takes its place (thus reducing the
+ *      array length by 1), and this is followed by array element
+ *      swaps to restore the heap property.   When an item is added,
+ *      it goes at the end of the array, and is swapped up to restore
+ *      the heap.  If the ptr array is full, adding another item causes
+ *      the ptr array size to double.
+ *
+ *      For further implementation details, see heap.c.
+ * 
+ */ + +/*! Heap of arbitrary void* data */ +struct L_Heap +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 n; /*!< number of elements stored in the heap */ + void **array; /*!< ptr array */ + l_int32 direction; /*!< L_SORT_INCREASING or L_SORT_DECREASING */ +}; +typedef struct L_Heap L_HEAP; + + +#endif /* LEPTONICA_HEAP_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/imageio.h b/hgdriver/3rdparty/hgOCR/leptonica/imageio.h new file mode 100644 index 0000000..b79d453 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/imageio.h @@ -0,0 +1,238 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file imageio.h + * + *
+ *  General features of image I/O in leptonica
+ *
+ *  At present, there are 9 file formats for images that can be read
+ *  and written:
+ *      png (requires libpng, libz)
+ *      jpeg (requires libjpeg)
+ *      tiff (requires libtiff, libz)
+ *      gif (requires libgif)
+ *      webp (requires libwebp)
+ *      jp2 (requires libopenjp2)
+ *      bmp (no library required)
+ *      pnm (no library required)
+ *      spix (no library required)
+ *  Additionally, there are two file formats for writing (only) images:
+ *      PostScript (requires libpng, libz, libjpeg, libtiff)
+ *      pdf (requires libpng, libz, libjpeg, libtiff)
+ *
+ *  For all 9 read/write formats, leptonica provides interconversion
+ *  between pix (with raster data) and formatted image data:
+ *      Conversion from pix (typically compression):
+ *          pixWrite():        pix --> file
+ *          pixWriteStream():  pix --> filestream (aka FILE*)
+ *          pixWriteMem():     pix --> memory buffer
+ *      Conversion to pix (typically decompression):
+ *          pixRead():         file --> pix
+ *          pixReadStream():   filestream --> pix
+ *          pixReadMem():      memory buffer --> pix
+ *
+ *  Conversions for which the image data is not compressed are:
+ *     * uncompressed tiff   (IFF_TIFF)
+ *     * bmp
+ *     * pnm
+ *     * spix (fast serialization that copies the pix raster data)
+ *
+ *  The image header (metadata) information can be read from either
+ *  the compressed file or a memory buffer, for all 9 formats.
+ * 
+ */ + +#ifndef LEPTONICA_IMAGEIO_H +#define LEPTONICA_IMAGEIO_H + +/* --------------------------------------------------------------- * + * Image file format types * + * --------------------------------------------------------------- */ +/* + * The IFF_DEFAULT flag is used to write the file out in the + * same (input) file format that the pix was read from. If the pix + * was not read from file, the input format field will be + * IFF_UNKNOWN and the output file format will be chosen to + * be compressed and lossless; namely, IFF_TIFF_G4 for d = 1 + * and IFF_PNG for everything else. + * + * In the future, new format types that have defined extensions + * will be added before IFF_DEFAULT, and will be kept in sync with + * the file format extensions in writefile.c. The positions of + * file formats before IFF_DEFAULT will remain invariant. + */ + +/*! Image Formats */ +enum { + IFF_UNKNOWN = 0, + IFF_BMP = 1, + IFF_JFIF_JPEG = 2, + IFF_PNG = 3, + IFF_TIFF = 4, + IFF_TIFF_PACKBITS = 5, + IFF_TIFF_RLE = 6, + IFF_TIFF_G3 = 7, + IFF_TIFF_G4 = 8, + IFF_TIFF_LZW = 9, + IFF_TIFF_ZIP = 10, + IFF_PNM = 11, + IFF_PS = 12, + IFF_GIF = 13, + IFF_JP2 = 14, + IFF_WEBP = 15, + IFF_LPDF = 16, + IFF_TIFF_JPEG = 17, + IFF_DEFAULT = 18, + IFF_SPIX = 19 +}; + + +/* --------------------------------------------------------------- * + * Format header ids * + * --------------------------------------------------------------- */ + +/*! Header Ids */ +enum { + BMP_ID = 0x4d42, /*!< BM - for bitmaps */ + TIFF_BIGEND_ID = 0x4d4d, /*!< MM - for 'motorola' */ + TIFF_LITTLEEND_ID = 0x4949 /*!< II - for 'intel' */ +}; + + +/* --------------------------------------------------------------- * + * Hinting bit flags in jpeg reader * + * --------------------------------------------------------------- */ + +/*! Jpeg Hints */ +enum { + L_JPEG_READ_LUMINANCE = 1, /*!< only want luminance data; no chroma */ + L_JPEG_FAIL_ON_BAD_DATA = 2 /*!< don't return possibly damaged pix */ +}; + + +/* --------------------------------------------------------------- * + * Pdf formatted encoding types * + * --------------------------------------------------------------- */ + +/*! Pdf Encoding */ +enum { + L_DEFAULT_ENCODE = 0, /*!< use default encoding based on image */ + L_JPEG_ENCODE = 1, /*!< use dct encoding: 8 and 32 bpp, no cmap */ + L_G4_ENCODE = 2, /*!< use ccitt g4 fax encoding: 1 bpp */ + L_FLATE_ENCODE = 3, /*!< use flate encoding: any depth, cmap ok */ + L_JP2K_ENCODE = 4 /*!< use jp2k encoding: 8 and 32 bpp, no cmap */ +}; + + +/* --------------------------------------------------------------- * + * Compressed image data * + * --------------------------------------------------------------- */ +/* + * In use, either datacomp or data85 will be produced, depending + * on whether the data needs to be ascii85 encoded. PostScript + * requires ascii85 encoding; pdf does not. + * + * For the colormap (flate compression only), PostScript uses ascii85 + * encoding and pdf uses a bracketed array of space-separated + * hex-encoded rgb triples. Only tiff g4 (type == L_G4_ENCODE) uses + * the minisblack field. + */ + +/*! Compressed image data */ +struct L_Compressed_Data +{ + l_int32 type; /*!< encoding type: L_JPEG_ENCODE, etc */ + l_uint8 *datacomp; /*!< gzipped raster data */ + size_t nbytescomp; /*!< number of compressed bytes */ + char *data85; /*!< ascii85-encoded gzipped raster data */ + size_t nbytes85; /*!< number of ascii85 encoded bytes */ + char *cmapdata85; /*!< ascii85-encoded uncompressed cmap */ + char *cmapdatahex; /*!< hex pdf array for the cmap */ + l_int32 ncolors; /*!< number of colors in cmap */ + l_int32 w; /*!< image width */ + l_int32 h; /*!< image height */ + l_int32 bps; /*!< bits/sample; typ. 1, 2, 4 or 8 */ + l_int32 spp; /*!< samples/pixel; typ. 1 or 3 */ + l_int32 minisblack; /*!< tiff g4 photometry */ + l_int32 predictor; /*!< flate data has PNG predictors */ + size_t nbytes; /*!< number of uncompressed raster bytes */ + l_int32 res; /*!< resolution (ppi) */ +}; +typedef struct L_Compressed_Data L_COMP_DATA; + + +/* ------------------------------------------------------------------------- * + * Pdf multi image flags * + * ------------------------------------------------------------------------- */ + +/*! Pdf MultiImage */ +enum { + L_FIRST_IMAGE = 1, /*!< first image to be used */ + L_NEXT_IMAGE = 2, /*!< intermediate image; not first or last */ + L_LAST_IMAGE = 3 /*!< last image to be used */ +}; + + +/* ------------------------------------------------------------------------- * + * Intermediate pdf generation data * + * ------------------------------------------------------------------------- */ +/* + * This accumulates data for generating a pdf of a single page consisting + * of an arbitrary number of images. + * + * None of the strings have a trailing newline. + */ + +/*! Intermediate pdf generation data */ +struct L_Pdf_Data +{ + char *title; /*!< optional title for pdf */ + l_int32 n; /*!< number of images */ + l_int32 ncmap; /*!< number of colormaps */ + struct L_Ptra *cida; /*!< array of compressed image data */ + char *id; /*!< %PDF-1.2 id string */ + char *obj1; /*!< catalog string */ + char *obj2; /*!< metadata string */ + char *obj3; /*!< pages string */ + char *obj4; /*!< page string (variable data) */ + char *obj5; /*!< content string (variable data) */ + char *poststream; /*!< post-binary-stream string */ + char *trailer; /*!< trailer string (variable data) */ + struct Pta *xy; /*!< store (xpt, ypt) array */ + struct Pta *wh; /*!< store (wpt, hpt) array */ + struct Box *mediabox; /*!< bounding region for all images */ + struct Sarray *saprex; /*!< pre-binary-stream xobject strings */ + struct Sarray *sacmap; /*!< colormap pdf object strings */ + struct L_Dna *objsize; /*!< sizes of each pdf string object */ + struct L_Dna *objloc; /*!< location of each pdf string object */ + l_int32 xrefloc; /*!< location of xref */ +}; +typedef struct L_Pdf_Data L_PDF_DATA; + + +#endif /* LEPTONICA_IMAGEIO_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/crc32.h b/hgdriver/3rdparty/hgOCR/leptonica/include/crc32.h new file mode 100644 index 0000000..9e0c778 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/deflate.h b/hgdriver/3rdparty/hgOCR/leptonica/include/deflate.h new file mode 100644 index 0000000..23ecdd3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/deflate.h @@ -0,0 +1,349 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2016 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#ifdef GZIP +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +#endif +#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + const static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + ulg pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + ulg gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef ZLIB_DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef ZLIB_DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/gzguts.h b/hgdriver/3rdparty/hgOCR/leptonica/include/gzguts.h new file mode 100644 index 0000000..990a4d2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/gzguts.h @@ -0,0 +1,218 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer (double-sized when writing) */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/inffast.h b/hgdriver/3rdparty/hgOCR/leptonica/include/inffast.h new file mode 100644 index 0000000..e5c1aa4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/inffixed.h b/hgdriver/3rdparty/hgOCR/leptonica/include/inffixed.h new file mode 100644 index 0000000..d628327 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/inflate.h b/hgdriver/3rdparty/hgOCR/leptonica/include/inflate.h new file mode 100644 index 0000000..a46cce6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/inflate.h @@ -0,0 +1,125 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD = 16180, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ +struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/inftrees.h b/hgdriver/3rdparty/hgOCR/leptonica/include/inftrees.h new file mode 100644 index 0000000..baa53a0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/trees.h b/hgdriver/3rdparty/hgOCR/leptonica/include/trees.h new file mode 100644 index 0000000..d35639d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/zconf.h b/hgdriver/3rdparty/hgOCR/leptonica/include/zconf.h new file mode 100644 index 0000000..5e1d68a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/zlib.h b/hgdriver/3rdparty/hgOCR/leptonica/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/include/zutil.h b/hgdriver/3rdparty/hgOCR/leptonica/include/zutil.h new file mode 100644 index 0000000..b079ea6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/include/zutil.h @@ -0,0 +1,271 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 1 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 2 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef __370__ +# if __TARGET_LIB__ < 0x20000000 +# define OS_CODE 4 +# elif __TARGET_LIB__ < 0x40000000 +# define OS_CODE 11 +# else +# define OS_CODE 8 +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 5 +#endif + +#ifdef OS2 +# define OS_CODE 6 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 7 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef __acorn +# define OS_CODE 13 +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +# define OS_CODE 10 +#endif + +#ifdef _BEOS_ +# define OS_CODE 16 +#endif + +#ifdef __TOS_OS400__ +# define OS_CODE 18 +#endif + +#ifdef __APPLE__ +# define OS_CODE 19 +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 3 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef ZLIB_DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jbclass.c b/hgdriver/3rdparty/hgOCR/leptonica/jbclass.c new file mode 100644 index 0000000..e673550 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jbclass.c @@ -0,0 +1,2576 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * jbclass.c + * + * These are functions for unsupervised classification of + * collections of connected components -- either characters or + * words -- in binary images. They can be used as image + * processing steps in jbig2 compression. + * + * Initialization + * + * JBCLASSER *jbRankHausInit() [rank hausdorff encoder] + * JBCLASSER *jbCorrelationInit() [correlation encoder] + * JBCLASSER *jbCorrelationInitWithoutComponents() [ditto] + * static JBCLASSER *jbCorrelationInitInternal() + * + * Classify the pages + * + * l_int32 jbAddPages() + * l_int32 jbAddPage() + * l_int32 jbAddPageComponents() + * + * Rank hausdorff classifier + * + * l_int32 jbClassifyRankHaus() + * l_int32 pixHaustest() + * l_int32 pixRankHaustest() + * + * Binary correlation classifier + * + * l_int32 jbClassifyCorrelation() + * + * Determine the image components we start with + * + * l_int32 jbGetComponents() + * l_int32 pixWordMaskByDilation() + * l_int32 pixWordBoxesByDilation() + * + * Build grayscale composites (templates) + * + * PIXA *jbAccumulateComposites + * PIXA *jbTemplatesFromComposites + * + * Utility functions for Classer + * + * JBCLASSER *jbClasserCreate() + * void jbClasserDestroy() + * + * Utility functions for Data + * + * JBDATA *jbDataSave() + * void jbDataDestroy() + * l_int32 jbDataWrite() + * JBDATA *jbDataRead() + * PIXA *jbDataRender() + * l_int32 jbGetULCorners() + * l_int32 jbGetLLCorners() + * + * Static helpers + * + * static JBFINDCTX *findSimilarSizedTemplatesInit() + * static l_int32 findSimilarSizedTemplatesNext() + * static void findSimilarSizedTemplatesDestroy() + * static l_int32 finalPositioningForAlignment() + * + * Note: this is NOT an implementation of the JPEG jbig2 + * proposed standard encoder, the specifications for which + * can be found at http://www.jpeg.org/jbigpt2.html. + * (See below for a full implementation.) + * It is an implementation of the lower-level part of an encoder that: + * + * (1) identifies connected components that are going to be used + * (2) puts them in similarity classes (this is an unsupervised + * classifier), and + * (3) stores the result in a simple file format (2 files, + * one for templates and one for page/coordinate/template-index + * quartets). + * + * An actual implementation of the official jbig2 encoder could + * start with parts (1) and (2), and would then compress the quartets + * according to the standards requirements (e.g., Huffman or + * arithmetic coding of coordinate differences and image templates). + * + * The low-level part of the encoder provided here has the + * following useful features: + * + * ~ It is accurate in the identification of templates + * and classes because it uses a windowed hausdorff + * distance metric. + * ~ It is accurate in the placement of the connected + * components, doing a two step process of first aligning + * the the centroids of the template with those of each instance, + * and then making a further correction of up to +- 1 pixel + * in each direction to best align the templates. + * ~ It is fast because it uses a morphologically based + * matching algorithm to implement the hausdorff criterion, + * and it selects the patterns that are possible matches + * based on their size. + * + * We provide two different matching functions, one using Hausdorff + * distance and one using a simple image correlation. + * The Hausdorff method sometimes produces better results for the + * same number of classes, because it gives a relatively small + * effective weight to foreground pixels near the boundary, + * and a relatively large weight to foreground pixels that are + * not near the boundary. By effectively ignoring these boundary + * pixels, Hausdorff weighting corresponds better to the expected + * probabilities of the pixel values in a scanned image, where the + * variations in instances of the same printed character are much + * more likely to be in pixels near the boundary. By contrast, + * the correlation method gives equal weight to all foreground pixels. + * + * For best results, use the correlation method. Correlation takes + * the number of fg pixels in the AND of instance and template, + * divided by the product of the number of fg pixels in instance + * and template. It compares this with a threshold that, in + * general, depends on the fractional coverage of the template. + * For heavy text, the threshold is raised above that for light + * text, By using both these parameters (basic threshold and + * adjustment factor for text weight), one has more flexibility + * and can arrive at the fewest substitution errors, although + * this comes at the price of more templates. + * + * The strict Hausdorff scoring is not a rank weighting, because a + * single pixel beyond the given distance will cause a match + * failure. A rank Hausdorff is more robust to non-boundary noise, + * but it is also more susceptible to confusing components that + * should be in different classes. For implementing a jbig2 + * application for visually lossless binary image compression, + * you have two choices: + * + * (1) use a 3x3 structuring element (size = 3) and a strict + * Hausdorff comparison (rank = 1.0 in the rank Hausdorff + * function). This will result in a minimal number of classes, + * but confusion of small characters, such as italic and + * non-italic lower-case 'o', can still occur. + * (2) use the correlation method with a threshold of 0.85 + * and a weighting factor of about 0.7. This will result in + * a larger number of classes, but should not be confused + * either by similar small characters or by extremely + * thick sans serif characters, such as in prog/cootoots.png. + * + * As mentioned above, if visual substitution errors must be + * avoided, you should use the correlation method. + * + * We provide executables that show how to do the encoding: + * prog/jbrankhaus.c + * prog/jbcorrelation.c + * + * The basic flow for correlation classification goes as follows, + * where specific choices have been made for parameters (Hausdorff + * is the same except for initialization): + * + * // Initialize and save data in the classer + * JBCLASSER *classer = + * jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7); + * SARRAY *safiles = getSortedPathnamesInDirectory(directory, + * NULL, 0, 0); + * jbAddPages(classer, safiles); + * + * // Save the data in a data structure for serialization, + * // and write it into two files. + * JBDATA *data = jbDataSave(classer); + * jbDataWrite(rootname, data); + * + * // Reconstruct (render) the pages from the encoded data. + * PIXA *pixa = jbDataRender(data, FALSE); + * + * Adam Langley has built a jbig2 standards-compliant encoder, the + * first one to appear in open source. You can get this encoder at: + * http://www.imperialviolet.org/jbig2.html + * + * It uses arithmetic encoding throughout. It encodes binary images + * losslessly with a single arithmetic coding over the full image. + * It also does both lossy and lossless encoding from connected + * components, using leptonica to generate the templates representing + * each cluster. + */ + +#include +#include +#include "allheaders.h" + +//static const l_int32 L_BUF_SIZE = 512; +#define L_BUF_SIZE 512 + + /* For jbClassifyRankHaus(): size of border added around + * pix of each c.c., to allow further processing. This + * should be at least the sum of the MAX_DIFF_HEIGHT + * (or MAX_DIFF_WIDTH) and one-half the size of the Sel */ +static const l_int32 JB_ADDED_PIXELS = 6; + + /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore(): + * choose these to be 2 or greater */ +static const l_int32 MAX_DIFF_WIDTH = 2; /* use at least 2 */ +static const l_int32 MAX_DIFF_HEIGHT = 2; /* use at least 2 */ + + /* In initialization, you have the option to discard components + * (cc, characters or words) that have either width or height larger + * than a given size. This is convenient for jbDataSave(), because + * the components are placed onto a regular lattice with cell + * dimension equal to the maximum component size. The default + * values are given here. If you want to save all components, + * use a sufficiently large set of dimensions. */ +static const l_int32 MAX_CONN_COMP_WIDTH = 350; /* default max cc width */ +static const l_int32 MAX_CHAR_COMP_WIDTH = 350; /* default max char width */ +static const l_int32 MAX_WORD_COMP_WIDTH = 1000; /* default max word width */ +static const l_int32 MAX_COMP_HEIGHT = 120; /* default max component height */ + + /* This stores the state of a state machine which fetches + * similar sized templates */ +struct JbFindTemplatesState +{ + JBCLASSER *classer; /* classer */ + l_int32 w; /* desired width */ + l_int32 h; /* desired height */ + l_int32 i; /* index into two_by_two step array */ + L_DNA *dna; /* current number array */ + l_int32 n; /* current element of dna */ +}; +typedef struct JbFindTemplatesState JBFINDCTX; + + /* Static initialization function */ +static JBCLASSER * jbCorrelationInitInternal(l_int32 components, + l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, + l_float32 weightfactor, l_int32 keep_components); + + /* Static helper functions */ +static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs); +static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context); +static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext); +static l_int32 finalPositioningForAlignment(PIX *pixs, l_int32 x, l_int32 y, + l_int32 idelx, l_int32 idely, PIX *pixt, + l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy); + +#ifndef NO_CONSOLE_IO +#define DEBUG_CORRELATION_SCORE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*----------------------------------------------------------------------* + * Initialization * + *----------------------------------------------------------------------*/ +/*! + * \brief jbRankHausInit() + * + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] maxwidth of component; use 0 for default + * \param[in] maxheight of component; use 0 for default + * \param[in] size of square structuring element; 2, representing + * 2x2 sel, is necessary for reasonable accuracy of + * small components; combine this with rank ~ 0.97 + * to avoid undue class expansion + * \param[in] rank rank val of match, each way; in [0.5 - 1.0]; + * when using size = 2, 0.97 is a reasonable value + * \return jbclasser if OK; NULL on error + */ +JBCLASSER * +jbRankHausInit(l_int32 components, + l_int32 maxwidth, + l_int32 maxheight, + l_int32 size, + l_float32 rank) +{ +JBCLASSER *classer; + + PROCNAME("jbRankHausInit"); + + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL); + if (size < 1 || size > 10) + return (JBCLASSER *)ERROR_PTR("size not reasonable", procName, NULL); + if (rank < 0.5 || rank > 1.0) + return (JBCLASSER *)ERROR_PTR("rank not in [0.5-1.0]", procName, NULL); + if (maxwidth == 0) { + if (components == JB_CONN_COMPS) + maxwidth = MAX_CONN_COMP_WIDTH; + else if (components == JB_CHARACTERS) + maxwidth = MAX_CHAR_COMP_WIDTH; + else /* JB_WORDS */ + maxwidth = MAX_WORD_COMP_WIDTH; + } + if (maxheight == 0) + maxheight = MAX_COMP_HEIGHT; + + if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL) + return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); + classer->maxwidth = maxwidth; + classer->maxheight = maxheight; + classer->sizehaus = size; + classer->rankhaus = rank; + classer->dahash = l_dnaHashCreate(5507, 4); /* 5507 is prime */ + classer->keep_pixaa = 1; /* keep all components in pixaa */ + return classer; +} + + +/*! + * \brief jbCorrelationInit() + * + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] maxwidth of component; use 0 for default + * \param[in] maxheight of component; use 0 for default + * \param[in] thresh value for correlation score: in [0.4 - 0.98] + * \param[in] weightfactor corrects thresh for thick characters [0.0 - 1.0] + * \return jbclasser if OK; NULL on error + * + *
+ * Notes:
+ *      (1) For scanned text, suggested input values are:
+ *            thresh ~ [0.8 - 0.85]
+ *            weightfactor ~ [0.5 - 0.6]
+ *      (2) For electronically generated fonts (e.g., rasterized pdf),
+ *          a very high thresh (e.g., 0.95) will not cause a significant
+ *          increase in the number of classes.
+ * 
+ */ +JBCLASSER * +jbCorrelationInit(l_int32 components, + l_int32 maxwidth, + l_int32 maxheight, + l_float32 thresh, + l_float32 weightfactor) +{ + return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, + weightfactor, 1); +} + +/*! + * \brief jbCorrelationInitWithoutComponents() + * + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] maxwidth of component; use 0 for default + * \param[in] maxheight of component; use 0 for default + * \param[in] thresh value for correlation score: in [0.4 - 0.98] + * \param[in] weightfactor corrects thresh for thick characters [0.0 - 1.0] + * \return jbclasser if OK; NULL on error + * + *
+ * Notes:
+ *      Acts the same as jbCorrelationInit(), but the resulting
+ *      object doesn't keep a list of all the components.
+ * 
+ */ +JBCLASSER * +jbCorrelationInitWithoutComponents(l_int32 components, + l_int32 maxwidth, + l_int32 maxheight, + l_float32 thresh, + l_float32 weightfactor) +{ + return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, + weightfactor, 0); +} + + +static JBCLASSER * +jbCorrelationInitInternal(l_int32 components, + l_int32 maxwidth, + l_int32 maxheight, + l_float32 thresh, + l_float32 weightfactor, + l_int32 keep_components) +{ +JBCLASSER *classer; + + PROCNAME("jbCorrelationInitInternal"); + + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL); + if (thresh < 0.4 || thresh > 0.98) + return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.98]", + procName, NULL); + if (weightfactor < 0.0 || weightfactor > 1.0) + return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]", + procName, NULL); + if (maxwidth == 0) { + if (components == JB_CONN_COMPS) + maxwidth = MAX_CONN_COMP_WIDTH; + else if (components == JB_CHARACTERS) + maxwidth = MAX_CHAR_COMP_WIDTH; + else /* JB_WORDS */ + maxwidth = MAX_WORD_COMP_WIDTH; + } + if (maxheight == 0) + maxheight = MAX_COMP_HEIGHT; + + + if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL) + return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); + classer->maxwidth = maxwidth; + classer->maxheight = maxheight; + classer->thresh = thresh; + classer->weightfactor = weightfactor; + classer->dahash = l_dnaHashCreate(5507, 4); /* 5507 is prime */ + classer->keep_pixaa = keep_components; + return classer; +} + + +/*----------------------------------------------------------------------* + * Classify the pages * + *----------------------------------------------------------------------*/ +/*! + * \brief jbAddPages() + * + * \param[in] jbclasser + * \param[in] safiles of page image file names + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) jbclasser makes a copy of the array of file names.
+ *      (2) The caller is still responsible for destroying the input array.
+ * 
+ */ +l_ok +jbAddPages(JBCLASSER *classer, + SARRAY *safiles) +{ +l_int32 i, nfiles; +char *fname; +PIX *pix; + + PROCNAME("jbAddPages"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + if (!safiles) + return ERROR_INT("safiles not defined", procName, 1); + + classer->safiles = sarrayCopy(safiles); + nfiles = sarrayGetCount(safiles); + for (i = 0; i < nfiles; i++) { + fname = sarrayGetString(safiles, i, L_NOCOPY); + if ((pix = pixRead(fname)) == NULL) { + L_WARNING("image file %d not read\n", procName, i); + continue; + } + if (pixGetDepth(pix) != 1) { + L_WARNING("image file %d not 1 bpp\n", procName, i); + continue; + } + jbAddPage(classer, pix); + pixDestroy(&pix); + } + + return 0; +} + + +/*! + * \brief jbAddPage() + * + * \param[in] jbclasser + * \param[in] pixs input page + * \return 0 if OK; 1 on error + */ +l_ok +jbAddPage(JBCLASSER *classer, + PIX *pixs) +{ +BOXA *boxas; +PIXA *pixas; + + PROCNAME("jbAddPage"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + classer->w = pixGetWidth(pixs); + classer->h = pixGetHeight(pixs); + + /* Get the appropriate components and their bounding boxes */ + if (jbGetComponents(pixs, classer->components, classer->maxwidth, + classer->maxheight, &boxas, &pixas)) { + return ERROR_INT("components not made", procName, 1); + } + + jbAddPageComponents(classer, pixs, boxas, pixas); + boxaDestroy(&boxas); + pixaDestroy(&pixas); + return 0; +} + + +/*! + * \brief jbAddPageComponents() + * + * \param[in] jbclasser + * \param[in] pixs input page + * \param[in] boxas b.b. of components for this page + * \param[in] pixas components for this page + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If there are no components on the page, we don't require input
+ *          of empty boxas or pixas, although that's the typical situation.
+ * 
+ */ +l_ok +jbAddPageComponents(JBCLASSER *classer, + PIX *pixs, + BOXA *boxas, + PIXA *pixas) +{ +l_int32 n; + + PROCNAME("jbAddPageComponents"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + if (!pixs) + return ERROR_INT("pix not defined", procName, 1); + + /* Test for no components on the current page. Always update the + * number of pages processed, even if nothing is on it. */ + if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) { + classer->npages++; + return 0; + } + + /* Get classes. For hausdorff, it uses a specified size of + * structuring element and specified rank. For correlation, + * it uses a specified threshold. */ + if (classer->method == JB_RANKHAUS) { + if (jbClassifyRankHaus(classer, boxas, pixas)) + return ERROR_INT("rankhaus classification failed", procName, 1); + } else { /* classer->method == JB_CORRELATION */ + if (jbClassifyCorrelation(classer, boxas, pixas)) + return ERROR_INT("correlation classification failed", procName, 1); + } + + /* Find the global UL corners, adjusted for each instance so + * that the class template and instance will have their + * centroids in the same place. Then the template can be + * used to replace the instance. */ + if (jbGetULCorners(classer, pixs, boxas)) + return ERROR_INT("UL corners not found", procName, 1); + + /* Update total component counts and number of pages processed. */ + n = boxaGetCount(boxas); + classer->baseindex += n; + numaAddNumber(classer->nacomps, n); + classer->npages++; + return 0; +} + + +/*----------------------------------------------------------------------* + * Classification using windowed rank hausdorff metric * + *----------------------------------------------------------------------*/ +/*! + * \brief jbClassifyRankHaus() + * + * \param[in] jbclasser + * \param[in] boxa new components for classification + * \param[in] pixas new components for classification + * \return 0 if OK; 1 on error + */ +l_ok +jbClassifyRankHaus(JBCLASSER *classer, + BOXA *boxa, + PIXA *pixas) +{ +l_int32 n, nt, i, wt, ht, iclass, size, found, testval; +l_int32 npages, area1, area3; +l_int32 *tab8; +l_float32 rank, x1, y1, x2, y2; +BOX *box; +NUMA *naclass, *napage; +NUMA *nafg; /* fg area of all instances */ +NUMA *nafgt; /* fg area of all templates */ +JBFINDCTX *findcontext; +L_DNAHASH *dahash; +PIX *pix, *pix1, *pix2, *pix3, *pix4; +PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd; +PIXAA *pixaa; +PTA *pta, *ptac, *ptact; +SEL *sel; + + PROCNAME("jbClassifyRankHaus"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (!pixas) + return ERROR_INT("pixas not defined", procName, 1); + if ((n = pixaGetCount(pixas)) == 0) + return ERROR_INT("pixas is empty", procName, 1); + if ((nafg = pixaCountPixels(pixas)) == NULL) /* areas for this page */ + return ERROR_INT("fg counting failed", procName, 1); + + npages = classer->npages; + size = classer->sizehaus; + sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT); + + /* Generate the bordered pixa, with and without dilation. + * pixa1 and pixa2 contain all the input components. */ + pixa1 = pixaCreate(n); + pixa2 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, + JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); + pix2 = pixDilate(NULL, pix1, sel); + pixaAddPix(pixa1, pix1, L_INSERT); /* un-dilated */ + pixaAddPix(pixa2, pix2, L_INSERT); /* dilated */ + pixDestroy(&pix); + } + + /* Get the centroids of all the bordered images. + * These are relative to the UL corner of each (bordered) pix. */ + pta = pixaCentroids(pixa1); /* centroids for this page; use here */ + ptac = classer->ptac; /* holds centroids of components up to this page */ + ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */ + ptact = classer->ptact; /* holds centroids of templates */ + + /* Use these to save the class and page of each component. */ + naclass = classer->naclass; + napage = classer->napage; + + /* Store the unbordered pix in a pixaa, in a hierarchical + * set of arrays. There is one pixa for each class, + * and the pix in each pixa are all the instances found + * of that class. This is actually more than one would need + * for a jbig2 encoder, but there are two reasons to keep + * them around: (1) the set of instances for each class + * can be used to make an improved binary (or, better, + * a grayscale) template, rather than simply using the first + * one in the set; (2) we can investigate the failures + * of the classifier. This pixaa grows as we process + * successive pages. */ + pixaa = classer->pixaa; + + /* arrays to store class exemplars (templates) */ + pixat = classer->pixat; /* un-dilated */ + pixatd = classer->pixatd; /* dilated */ + + /* Fill up the pixaa tree with the template exemplars as + * the first pix in each pixa. As we add each pix, + * we also add the associated box to the pixa. + * We also keep track of the centroid of each pix, + * and use the difference between centroids (of the + * pix with the exemplar we are checking it with) + * to align the two when checking that the Hausdorff + * distance does not exceed a threshold. + * The threshold is set by the Sel used for dilating. + * For example, a 3x3 brick, sel_3, corresponds to a + * Hausdorff distance of 1. In general, for an NxN brick, + * with N odd, corresponds to a Hausdorff distance of (N - 1)/2. + * It turns out that we actually need to use a sel of size 2x2 + * to avoid small bad components when there is a halftone image + * from which components can be chosen. + * The larger the Sel you use, the fewer the number of classes, + * and the greater the likelihood of putting semantically + * different objects in the same class. For simplicity, + * we do this separately for the case of rank == 1.0 (exact + * match within the Hausdorff distance) and rank < 1.0. */ + rank = classer->rankhaus; + dahash = classer->dahash; + if (rank == 1.0) { + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + pix2 = pixaGetPix(pixa2, i, L_CLONE); + ptaGetPt(pta, i, &x1, &y1); + nt = pixaGetCount(pixat); /* number of templates */ + found = FALSE; + findcontext = findSimilarSizedTemplatesInit(classer, pix1); + while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { + /* Find score for this template */ + pix3 = pixaGetPix(pixat, iclass, L_CLONE); + pix4 = pixaGetPix(pixatd, iclass, L_CLONE); + ptaGetPt(ptact, iclass, &x2, &y2); + testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2, + MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT); + pixDestroy(&pix3); + pixDestroy(&pix4); + if (testval == 1) { + found = TRUE; + numaAddNumber(naclass, iclass); + numaAddNumber(napage, npages); + if (classer->keep_pixaa) { + pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); + pix = pixaGetPix(pixas, i, L_CLONE); + pixaAddPix(pixa, pix, L_INSERT); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaDestroy(&pixa); + } + break; + } + } + findSimilarSizedTemplatesDestroy(&findcontext); + if (found == FALSE) { /* new class */ + numaAddNumber(naclass, nt); + numaAddNumber(napage, npages); + pixa = pixaCreate(0); + pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ + pixaAddPix(pixa, pix, L_INSERT); + wt = pixGetWidth(pix); + ht = pixGetHeight(pix); + l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ + ptaAddPt(ptact, x1, y1); + pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ + pixaAddPix(pixatd, pix2, L_INSERT); /* bordered dil template */ + } else { /* don't save them */ + pixDestroy(&pix1); + pixDestroy(&pix2); + } + } + } else { /* rank < 1.0 */ + nafgt = classer->nafgt; + tab8 = makePixelSumTab8(); + for (i = 0; i < n; i++) { /* all instances on this page */ + pix1 = pixaGetPix(pixa1, i, L_CLONE); + numaGetIValue(nafg, i, &area1); + pix2 = pixaGetPix(pixa2, i, L_CLONE); + ptaGetPt(pta, i, &x1, &y1); /* use pta for this page */ + nt = pixaGetCount(pixat); /* number of templates */ + found = FALSE; + findcontext = findSimilarSizedTemplatesInit(classer, pix1); + while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { + /* Find score for this template */ + pix3 = pixaGetPix(pixat, iclass, L_CLONE); + numaGetIValue(nafgt, iclass, &area3); + pix4 = pixaGetPix(pixatd, iclass, L_CLONE); + ptaGetPt(ptact, iclass, &x2, &y2); + testval = pixRankHaustest(pix1, pix2, pix3, pix4, + x1 - x2, y1 - y2, + MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, + area1, area3, rank, tab8); + pixDestroy(&pix3); + pixDestroy(&pix4); + if (testval == 1) { /* greedy match; take the first */ + found = TRUE; + numaAddNumber(naclass, iclass); + numaAddNumber(napage, npages); + if (classer->keep_pixaa) { + pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); + pix = pixaGetPix(pixas, i, L_CLONE); + pixaAddPix(pixa, pix, L_INSERT); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaDestroy(&pixa); + } + break; + } + } + findSimilarSizedTemplatesDestroy(&findcontext); + if (found == FALSE) { /* new class */ + numaAddNumber(naclass, nt); + numaAddNumber(napage, npages); + pixa = pixaCreate(0); + pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ + pixaAddPix(pixa, pix, L_INSERT); + wt = pixGetWidth(pix); + ht = pixGetHeight(pix); + l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ + ptaAddPt(ptact, x1, y1); + pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ + pixaAddPix(pixatd, pix2, L_INSERT); /* ditto */ + numaAddNumber(nafgt, area1); + } else { /* don't save them */ + pixDestroy(&pix1); + pixDestroy(&pix2); + } + } + LEPT_FREE(tab8); + } + classer->nclass = pixaGetCount(pixat); + + numaDestroy(&nafg); + ptaDestroy(&pta); + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + selDestroy(&sel); + return 0; +} + + +/*! + * \brief pixHaustest() + * + * \param[in] pix1 new pix, not dilated + * \param[in] pix2 new pix, dilated + * \param[in] pix3 exemplar pix, not dilated + * \param[in] pix4 exemplar pix, dilated + * \param[in] delx x comp of centroid difference + * \param[in] dely y comp of centroid difference + * \param[in] maxdiffw max width difference of pix1 and pix2 + * \param[in] maxdiffh max height difference of pix1 and pix2 + * \return 0 FALSE) if no match, 1 (TRUE if the new + * pix is in the same class as the exemplar. + * + *
+ * Notes:
+ *  We check first that the two pix are roughly
+ *  the same size.  Only if they meet that criterion do
+ *  we compare the bitmaps.  The Hausdorff is a 2-way
+ *  check.  The centroid difference is used to align the two
+ *  images to the nearest integer for each of the checks.
+ *  These check that the dilated image of one contains
+ *  ALL the pixels of the undilated image of the other.
+ *  Checks are done in both direction.  A single pixel not
+ *  contained in either direction results in failure of the test.
+ * 
+ */ +l_int32 +pixHaustest(PIX *pix1, + PIX *pix2, + PIX *pix3, + PIX *pix4, + l_float32 delx, /* x(1) - x(3) */ + l_float32 dely, /* y(1) - y(3) */ + l_int32 maxdiffw, + l_int32 maxdiffh) +{ +l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; +PIX *pixt; + + /* Eliminate possible matches based on size difference */ + wi = pixGetWidth(pix1); + hi = pixGetHeight(pix1); + wt = pixGetWidth(pix3); + ht = pixGetHeight(pix3); + delw = L_ABS(wi - wt); + if (delw > maxdiffw) + return FALSE; + delh = L_ABS(hi - ht); + if (delh > maxdiffh) + return FALSE; + + /* Round difference in centroid location to nearest integer; + * use this as a shift when doing the matching. */ + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + + /* Do 1-direction hausdorff, checking that every pixel in pix1 + * is within a dilation distance of some pixel in pix3. Namely, + * that pix4 entirely covers pix1: + * pixt = pixSubtract(NULL, pix1, pix4), including shift + * where pixt has no ON pixels. */ + pixt = pixCreateTemplate(pix1); + pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); + pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), + pix4, 0, 0); + pixZero(pixt, &boolmatch); + if (boolmatch == 0) { + pixDestroy(&pixt); + return FALSE; + } + + /* Do 1-direction hausdorff, checking that every pixel in pix3 + * is within a dilation distance of some pixel in pix1. Namely, + * that pix2 entirely covers pix3: + * pixSubtract(pixt, pix3, pix2), including shift + * where pixt has no ON pixels. */ + pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); + pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); + pixZero(pixt, &boolmatch); + pixDestroy(&pixt); + return boolmatch; +} + + +/*! + * \brief pixRankHaustest() + * + * \param[in] pix1 new pix, not dilated + * \param[in] pix2 new pix, dilated + * \param[in] pix3 exemplar pix, not dilated + * \param[in] pix4 exemplar pix, dilated + * \param[in] delx x comp of centroid difference + * \param[in] dely y comp of centroid difference + * \param[in] maxdiffw max width difference of pix1 and pix2 + * \param[in] maxdiffh max height difference of pix1 and pix2 + * \param[in] area1 fg pixels in pix1 + * \param[in] area3 fg pixels in pix3 + * \param[in] rank rank value of test, each way + * \param[in] tab8 table of pixel sums for byte + * \return 0 FALSE) if no match, 1 (TRUE if the new + * pix is in the same class as the exemplar. + * + *
+ * Notes:
+ *  We check first that the two pix are roughly
+ *  the same size.  Only if they meet that criterion do
+ *  we compare the bitmaps.  We convert the rank value to
+ *  a number of pixels by multiplying the rank fraction by the number
+ *  of pixels in the undilated image.  The Hausdorff is a 2-way
+ *  check.  The centroid difference is used to align the two
+ *  images to the nearest integer for each of the checks.
+ *  The rank hausdorff checks that the dilated image of one
+ *  contains the rank fraction of the pixels of the undilated
+ *  image of the other.   Checks are done in both direction.
+ *  Failure of the test in either direction results in failure
+ *  of the test.
+ * 
+ */ +l_int32 +pixRankHaustest(PIX *pix1, + PIX *pix2, + PIX *pix3, + PIX *pix4, + l_float32 delx, /* x(1) - x(3) */ + l_float32 dely, /* y(1) - y(3) */ + l_int32 maxdiffw, + l_int32 maxdiffh, + l_int32 area1, + l_int32 area3, + l_float32 rank, + l_int32 *tab8) +{ +l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; +l_int32 thresh1, thresh3; +PIX *pixt; + + /* Eliminate possible matches based on size difference */ + wi = pixGetWidth(pix1); + hi = pixGetHeight(pix1); + wt = pixGetWidth(pix3); + ht = pixGetHeight(pix3); + delw = L_ABS(wi - wt); + if (delw > maxdiffw) + return FALSE; + delh = L_ABS(hi - ht); + if (delh > maxdiffh) + return FALSE; + + /* Upper bounds in remaining pixels for allowable match */ + thresh1 = (l_int32)(area1 * (1. - rank) + 0.5); + thresh3 = (l_int32)(area3 * (1. - rank) + 0.5); + + /* Round difference in centroid location to nearest integer; + * use this as a shift when doing the matching. */ + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + + /* Do 1-direction hausdorff, checking that every pixel in pix1 + * is within a dilation distance of some pixel in pix3. Namely, + * that pix4 entirely covers pix1: + * pixt = pixSubtract(NULL, pix1, pix4), including shift + * where pixt has no ON pixels. */ + pixt = pixCreateTemplate(pix1); + pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); + pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), + pix4, 0, 0); + pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8); + if (boolmatch == 1) { /* above thresh1 */ + pixDestroy(&pixt); + return FALSE; + } + + /* Do 1-direction hausdorff, checking that every pixel in pix3 + * is within a dilation distance of some pixel in pix1. Namely, + * that pix2 entirely covers pix3: + * pixSubtract(pixt, pix3, pix2), including shift + * where pixt has no ON pixels. */ + pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); + pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); + pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8); + pixDestroy(&pixt); + if (boolmatch == 1) /* above thresh3 */ + return FALSE; + else + return TRUE; +} + + +/*----------------------------------------------------------------------* + * Classification using windowed correlation score * + *----------------------------------------------------------------------*/ +/*! + * \brief jbClassifyCorrelation() + * + * \param[in] jbclasser + * \param[in] boxa new components for classification + * \param[in] pixas new components for classification + * \return 0 if OK; 1 on error + */ +l_ok +jbClassifyCorrelation(JBCLASSER *classer, + BOXA *boxa, + PIXA *pixas) +{ +l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages, + overthreshold; +l_int32 *sumtab, *centtab; +l_uint32 *row, word; +l_float32 x1, y1, x2, y2, xsum, ysum; +l_float32 thresh, weight, threshold; +BOX *box; +NUMA *naclass, *napage; +NUMA *nafgt; /* fg area of all templates */ +NUMA *naarea; /* w * h area of all templates */ +JBFINDCTX *findcontext; +L_DNAHASH *dahash; +PIX *pix, *pix1, *pix2; +PIXA *pixa, *pixa1, *pixat; +PIXAA *pixaa; +PTA *pta, *ptac, *ptact; +l_int32 *pixcts; /* pixel counts of each pixa */ +l_int32 **pixrowcts; /* row-by-row pixel counts of each pixa */ +l_int32 x, y, rowcount, downcount, wpl; +l_uint8 byte; + + PROCNAME("jbClassifyCorrelation"); + + if (!classer) + return ERROR_INT("classer not found", procName, 1); + if (!boxa) + return ERROR_INT("boxa not found", procName, 1); + if (!pixas) + return ERROR_INT("pixas not found", procName, 1); + + npages = classer->npages; + + /* Generate the bordered pixa, which contains all the the + * input components. This will not be saved. */ + if ((n = pixaGetCount(pixas)) == 0) { + L_WARNING("pixas is empty\n", procName); + return 0; + } + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, + JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); + pixaAddPix(pixa1, pix1, L_INSERT); + pixDestroy(&pix); + } + + /* Use these to save the class and page of each component. */ + naclass = classer->naclass; + napage = classer->napage; + + /* Get the number of fg pixels in each component. */ + nafgt = classer->nafgt; /* holds fg areas of the templates */ + sumtab = makePixelSumTab8(); + + pixcts = (l_int32 *)LEPT_CALLOC(n, sizeof(*pixcts)); + pixrowcts = (l_int32 **)LEPT_CALLOC(n, sizeof(*pixrowcts)); + centtab = makePixelCentroidTab8(); + + /* Count the "1" pixels in each row of the pix in pixa1; this + * allows pixCorrelationScoreThresholded to abort early if a match + * is impossible. This loop merges three calculations: the total + * number of "1" pixels, the number of "1" pixels in each row, and + * the centroid. The centroids are relative to the UL corner of + * each (bordered) pix. The pixrowcts[i][y] are the total number + * of fg pixels in pixa[i] below row y. */ + pta = ptaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa1, i, L_CLONE); + pixrowcts[i] = (l_int32 *)LEPT_CALLOC(pixGetHeight(pix), + sizeof(**pixrowcts)); + xsum = 0; + ysum = 0; + wpl = pixGetWpl(pix); + row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl; + downcount = 0; + for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) { + pixrowcts[i][y] = downcount; + rowcount = 0; + for (x = 0; x < wpl; x++) { + word = row[x]; + byte = word & 0xff; + rowcount += sumtab[byte]; + xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte]; + byte = (word >> 8) & 0xff; + rowcount += sumtab[byte]; + xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte]; + byte = (word >> 16) & 0xff; + rowcount += sumtab[byte]; + xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte]; + byte = (word >> 24) & 0xff; + rowcount += sumtab[byte]; + xsum += centtab[byte] + x * 32 * sumtab[byte]; + } + downcount += rowcount; + ysum += rowcount * y; + } + pixcts[i] = downcount; + if (downcount > 0) { + ptaAddPt(pta, + xsum / (l_float32)downcount, ysum / (l_float32)downcount); + } else { /* no pixels; shouldn't happen */ + L_ERROR("downcount == 0 !\n", procName); + ptaAddPt(pta, pixGetWidth(pix) / 2, pixGetHeight(pix) / 2); + } + pixDestroy(&pix); + } + + ptac = classer->ptac; /* holds centroids of components up to this page */ + ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */ + ptact = classer->ptact; /* holds centroids of templates */ + + /* Store the unbordered pix in a pixaa, in a hierarchical + * set of arrays. There is one pixa for each class, + * and the pix in each pixa are all the instances found + * of that class. This is actually more than one would need + * for a jbig2 encoder, but there are two reasons to keep + * them around: (1) the set of instances for each class + * can be used to make an improved binary (or, better, + * a grayscale) template, rather than simply using the first + * one in the set; (2) we can investigate the failures + * of the classifier. This pixaa grows as we process + * successive pages. */ + pixaa = classer->pixaa; + + /* Array to store class exemplars */ + pixat = classer->pixat; + + /* Fill up the pixaa tree with the template exemplars as + * the first pix in each pixa. As we add each pix, + * we also add the associated box to the pixa. + * We also keep track of the centroid of each pix, + * and use the difference between centroids (of the + * pix with the exemplar we are checking it with) + * to align the two when checking that the correlation + * score exceeds a threshold. The correlation score + * is given by the square of the area of the AND + * between aligned instance and template, divided by + * the product of areas of each image. For identical + * template and instance, the score is 1.0. + * If the threshold is too small, non-equivalent instances + * will be placed in the same class; if too large, there will + * be an unnecessary division of classes representing the + * same character. The weightfactor adds in some of the + * difference (1.0 - thresh), depending on the heaviness + * of the template (measured as the fraction of fg pixels). */ + thresh = classer->thresh; + weight = classer->weightfactor; + naarea = classer->naarea; + dahash = classer->dahash; + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + area1 = pixcts[i]; + ptaGetPt(pta, i, &x1, &y1); /* centroid for this instance */ + nt = pixaGetCount(pixat); + found = FALSE; + findcontext = findSimilarSizedTemplatesInit(classer, pix1); + while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { + /* Get the template */ + pix2 = pixaGetPix(pixat, iclass, L_CLONE); + numaGetIValue(nafgt, iclass, &area2); + ptaGetPt(ptact, iclass, &x2, &y2); /* template centroid */ + + /* Find threshold for this template */ + if (weight > 0.0) { + numaGetIValue(naarea, iclass, &area); + threshold = thresh + (1. - thresh) * weight * area2 / area; + } else { + threshold = thresh; + } + + /* Find score for this template */ + overthreshold = pixCorrelationScoreThresholded(pix1, pix2, + area1, area2, x1 - x2, y1 - y2, + MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, + sumtab, pixrowcts[i], threshold); +#if DEBUG_CORRELATION_SCORE + { + l_float32 score, testscore; + l_int32 count, testcount; + pixCorrelationScore(pix1, pix2, area1, area2, x1 - x2, y1 - y2, + MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, + sumtab, &score); + + pixCorrelationScoreSimple(pix1, pix2, area1, area2, + x1 - x2, y1 - y2, MAX_DIFF_WIDTH, + MAX_DIFF_HEIGHT, sumtab, &testscore); + count = (l_int32)rint(sqrt(score * area1 * area2)); + testcount = (l_int32)rint(sqrt(testscore * area1 * area2)); + if ((score >= threshold) != (testscore >= threshold)) { + fprintf(stderr, "Correlation score mismatch: " + "%d(%g,%d) vs %d(%g,%d) (%g)\n", + count, score, score >= threshold, + testcount, testscore, testscore >= threshold, + score - testscore); + } + + if ((score >= threshold) != overthreshold) { + fprintf(stderr, "Mismatch between correlation/threshold " + "comparison: %g(%g,%d) >= %g(%g) vs %s\n", + score, score*area1*area2, count, threshold, + threshold*area1*area2, + (overthreshold ? "true" : "false")); + } + } +#endif /* DEBUG_CORRELATION_SCORE */ + pixDestroy(&pix2); + + if (overthreshold) { /* greedy match */ + found = TRUE; + numaAddNumber(naclass, iclass); + numaAddNumber(napage, npages); + if (classer->keep_pixaa) { + /* We are keeping a record of all components */ + pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); + pix = pixaGetPix(pixas, i, L_CLONE); + pixaAddPix(pixa, pix, L_INSERT); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaDestroy(&pixa); + } + break; + } + } + findSimilarSizedTemplatesDestroy(&findcontext); + if (found == FALSE) { /* new class */ + numaAddNumber(naclass, nt); + numaAddNumber(napage, npages); + pixa = pixaCreate(0); + pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ + pixaAddPix(pixa, pix, L_INSERT); + wt = pixGetWidth(pix); + ht = pixGetHeight(pix); + l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); + box = boxaGetBox(boxa, i, L_CLONE); + pixaAddBox(pixa, box, L_INSERT); + pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ + ptaAddPt(ptact, x1, y1); + numaAddNumber(nafgt, area1); + pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ + area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) * + (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS); + numaAddNumber(naarea, area); + } else { /* don't save it */ + pixDestroy(&pix1); + } + } + classer->nclass = pixaGetCount(pixat); + + LEPT_FREE(pixcts); + LEPT_FREE(centtab); + for (i = 0; i < n; i++) { + LEPT_FREE(pixrowcts[i]); + } + LEPT_FREE(pixrowcts); + + LEPT_FREE(sumtab); + ptaDestroy(&pta); + pixaDestroy(&pixa1); + return 0; +} + + +/*----------------------------------------------------------------------* + * Determine the image components we start with * + *----------------------------------------------------------------------*/ +/*! + * \brief jbGetComponents() + * + * \param[in] pixs 1 bpp + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \param[in] maxwidth of saved components; larger are discarded + * \param[in] maxheight of saved components; larger are discarded + * \param[out] ppboxa b.b. of component items + * \param[out] pppixa component items + * \return 0 if OK, 1 on error + */ +l_ok +jbGetComponents(PIX *pixs, + l_int32 components, + l_int32 maxwidth, + l_int32 maxheight, + BOXA **pboxad, + PIXA **ppixad) +{ +l_int32 empty, res, redfactor; +BOXA *boxa; +PIX *pix1, *pix2, *pix3; +PIXA *pixa, *pixat; + + PROCNAME("jbGetComponents"); + + if (!pboxad) + return ERROR_INT("&boxad not defined", procName, 1); + *pboxad = NULL; + if (!ppixad) + return ERROR_INT("&pixad not defined", procName, 1); + *ppixad = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return ERROR_INT("invalid components", procName, 1); + + pixZero(pixs, &empty); + if (empty) { + *pboxad = boxaCreate(0); + *ppixad = pixaCreate(0); + return 0; + } + + /* If required, preprocess input pixs. The method for both + * characters and words is to generate a connected component + * mask over the units that we want to aggregrate, which are, + * in general, sets of related connected components in pixs. + * For characters, we want to include the dots with + * 'i', 'j' and '!', so we do a small vertical closing to + * generate the mask. For words, we make a mask over all + * characters in each word. This is a bit more tricky, because + * the spacing between words is difficult to predict a priori, + * and words can be typeset with variable spacing that can + * in some cases be barely larger than the space between + * characters. The first step is to generate the mask and + * identify each of its connected components. */ + if (components == JB_CONN_COMPS) { /* no preprocessing */ + boxa = pixConnComp(pixs, &pixa, 8); + } else if (components == JB_CHARACTERS) { + pix1 = pixMorphSequence(pixs, "c1.6", 0); + boxa = pixConnComp(pix1, &pixat, 8); + pixa = pixaClipToPix(pixat, pixs); + pixDestroy(&pix1); + pixaDestroy(&pixat); + } else { /* components == JB_WORDS */ + + /* Do the operations at about 150 ppi resolution. + * It is much faster at 75 ppi, but the results are + * more accurate at 150 ppi. This will segment the + * words in body text. It can be expected that relatively + * infrequent words in a larger font will be split. */ + res = pixGetXRes(pixs); + if (res <= 200) { + redfactor = 1; + pix1 = pixClone(pixs); + } else if (res <= 400) { + redfactor = 2; + pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); + } else { + redfactor = 4; + pix1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); + } + + /* Estimate the word mask, at approximately 150 ppi. + * This has both very large and very small components left in. */ + pixWordMaskByDilation(pix1, &pix2, NULL, NULL); + + /* Expand the optimally dilated word mask to full res. */ + pix3 = pixExpandReplicate(pix2, redfactor); + + /* Pull out the pixels in pixs corresponding to the mask + * components in pix3. Note that above we used threshold + * levels in the reduction of 1 to insure that the resulting + * mask fully covers the input pixs. The downside of using + * a threshold of 1 is that very close characters from adjacent + * lines can be joined. But with a level of 2 or greater, + * it is necessary to use a seedfill, followed by a pixOr(): + * pixt4 = pixSeedfillBinary(NULL, pix3, pixs, 8); + * pixOr(pix3, pix3, pixt4); + * to insure that the mask coverage is complete over pixs. */ + boxa = pixConnComp(pix3, &pixat, 4); + pixa = pixaClipToPix(pixat, pixs); + pixaDestroy(&pixat); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + } + + /* Remove large components, and save the results. */ + *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH, + L_SELECT_IF_LTE, NULL); + *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH, + L_SELECT_IF_LTE, NULL); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + + return 0; +} + + +/*! + * \brief pixWordMaskByDilation() + * + * \param[in] pixs 1 bpp; typ. at 75 to 150 ppi + * \param[out] pmask [optional] dilated word mask + * \param[out] psize [optional] size of good horizontal dilation + * \param[out] pixadb [optional] debug: pixa of intermediate steps + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives an estimate of the word masks.  See
+ *          pixWordBoxesByDilation() for further filtering of the word boxes.
+ *      (2) The resolution should be between 75 and 150 ppi, and the optimal
+ *          dilation will be between 3 and 10.
+ *      (3) A good size for dilating to get word masks is optionally returned.
+ *      (4) Typically, the number of c.c. reduced with each successive
+ *          dilation (stored in nadiff) decreases quickly to a minimum
+ *          (where the characters in a word are joined), and then
+ *          increases again as the smaller number of words are joined.
+ *          For the typical case, you can then look for this minimum
+ *          and dilate to get the word mask.  However, there are many
+ *          cases where the function is not so simple. For example, if the
+ *          pix has been upscaled 2x, the nadiff function oscillates, with
+ *          every other value being zero!  And for some images it tails
+ *          off without a clear minimum to indicate where to break.
+ *          So a more simple and robust method is to find the dilation
+ *          where the initial number of c.c. has been reduced by some
+ *          fraction (we use a 70% reduction).
+ * 
+ */ +l_ok +pixWordMaskByDilation(PIX *pixs, + PIX **ppixm, + l_int32 *psize, + PIXA *pixadb) +{ +l_int32 i, n, ndil, maxdiff, diff, ibest; +l_int32 check, count, total, xres; +l_int32 ncc[13]; /* max dilation + 1 */ +l_int32 *diffa; +BOXA *boxa; +NUMA *nacc, *nadiff; +PIX *pix1, *pix2; + + PROCNAME("pixWordMaskByDilation"); + + if (ppixm) *ppixm = NULL; + if (psize) *psize = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (!ppixm && !psize) + return ERROR_INT("no output requested", procName, 1); + + /* Find a good dilation to create the word mask, by successively + * increasing dilation size and counting the connected components. */ + pix1 = pixCopy(NULL, pixs); + ndil = 12; /* appropriate for 75 to 150 ppi */ + nacc = numaCreate(ndil + 1); + nadiff = numaCreate(ndil + 1); + for (i = 0; i <= ndil; i++) { + if (i == 0) /* first one not dilated */ + pix2 = pixCopy(NULL, pix1); + else /* successive dilation by sel_2h */ + pix2 = pixMorphSequence(pix1, "d2.1", 0); + boxa = pixConnCompBB(pix2, 4); + ncc[i] = boxaGetCount(boxa); + numaAddNumber(nacc, ncc[i]); + if (i == 0) total = ncc[0]; + if (i > 0) { + diff = ncc[i - 1] - ncc[i]; + numaAddNumber(nadiff, diff); + } + pixDestroy(&pix1); + pix1 = pix2; + boxaDestroy(&boxa); + } + pixDestroy(&pix1); + + /* Find the dilation at which the c.c. count has reduced + * to 30% of the initial value. Although 30% seems high, + * it seems better to use this but add one to ibest. */ + diffa = numaGetIArray(nadiff); + n = numaGetCount(nadiff); + maxdiff = 0; + check = TRUE; + ibest = 2; + for (i = 1; i < n; i++) { + numaGetIValue(nacc, i, &count); + if (check && count < 0.3 * total) { + ibest = i + 1; + check = FALSE; + } + diff = diffa[i]; + if (diff > maxdiff) + maxdiff = diff; + } + LEPT_FREE(diffa); + + /* Add small compensation for higher resolution */ + xres = pixGetXRes(pixs); + if (xres == 0) xres = 150; + if (xres > 110) ibest++; + if (ibest < 2) { + L_INFO("setting ibest to minimum allowed value of 2\n", procName); + ibest = 2; + } + + if (pixadb) { + lept_mkdir("lept/jb"); + {GPLOT *gplot; + NUMA *naseq; + PIX *pix3, *pix4; + L_INFO("Best dilation: %d\n", procName, L_MAX(3, ibest + 1)); + naseq = numaMakeSequence(1, 1, numaGetCount(nacc)); + gplot = gplotCreate("/tmp/lept/jb/numcc", GPLOT_PNG, + "Number of cc vs. horizontal dilation", + "Sel horiz", "Number of cc"); + gplotAddPlot(gplot, naseq, nacc, GPLOT_LINES, ""); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + pix3 = pixRead("/tmp/lept/jb/numcc.png"); + pixaAddPix(pixadb, pix3, L_INSERT); + numaDestroy(&naseq); + naseq = numaMakeSequence(1, 1, numaGetCount(nadiff)); + gplot = gplotCreate("/tmp/lept/jb/diffcc", GPLOT_PNG, + "Diff count of cc vs. horizontal dilation", + "Sel horiz", "Diff in cc"); + gplotAddPlot(gplot, naseq, nadiff, GPLOT_LINES, ""); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + pix3 = pixRead("/tmp/lept/jb/diffcc.png"); + pixaAddPix(pixadb, pix3, L_INSERT); + numaDestroy(&naseq); + pix3 = pixCloseBrick(NULL, pixs, ibest + 1, 1); + pix4 = pixScaleToSize(pix3, 600, 0); + pixaAddPix(pixadb, pix4, L_INSERT); + pixDestroy(&pix3); + } + } + + if (psize) *psize = ibest + 1; + if (ppixm) + *ppixm = pixCloseBrick(NULL, pixs, ibest + 1, 1); + + numaDestroy(&nacc); + numaDestroy(&nadiff); + return 0; +} + + +/*! + * \brief pixWordBoxesByDilation() + * + * \param[in] pixs 1 bpp; typ. 75 - 200 ppi + * \param[in] minwidth saved components; smaller are discarded + * \param[in] minheight saved components; smaller are discarded + * \param[in] maxwidth saved components; larger are discarded + * \param[in] maxheight saved components; larger are discarded + * \param[out] pboxa of dilated word mask + * \param[out] psize [optional] size of good horizontal dilation + * \param[out] pixadb [optional] debug: pixa of intermediate steps + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Returns a pruned set of word boxes.
+ *      (2) See pixWordMaskByDilation().
+ * 
+ */ +l_ok +pixWordBoxesByDilation(PIX *pixs, + l_int32 minwidth, + l_int32 minheight, + l_int32 maxwidth, + l_int32 maxheight, + BOXA **pboxa, + l_int32 *psize, + PIXA *pixadb) +{ +BOXA *boxa1, *boxa2; +PIX *pix1, *pix2; + + PROCNAME("pixWordBoxesByDilation"); + + if (psize) *psize = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (!pboxa) + return ERROR_INT("&boxa not defined", procName, 1); + *pboxa = NULL; + + /* Make a first estimate of the word mask */ + if (pixWordMaskByDilation(pixs, &pix1, psize, pixadb)) + return ERROR_INT("pixWordMaskByDilation() failed", procName, 1); + + /* Prune the word mask. Get the bounding boxes of the words. + * Remove the small ones, which can be due to punctuation + * that was not joined to a word. Also remove the large ones, + * which are not likely to be words. */ + boxa1 = pixConnComp(pix1, NULL, 8); + boxa2 = boxaSelectBySize(boxa1, minwidth, minheight, L_SELECT_IF_BOTH, + L_SELECT_IF_GTE, NULL); + *pboxa = boxaSelectBySize(boxa2, maxwidth, maxheight, L_SELECT_IF_BOTH, + L_SELECT_IF_LTE, NULL); + if (pixadb) { + pix2 = pixUnpackBinary(pixs, 32, 1); + pixRenderBoxaArb(pix2, boxa1, 2, 255, 0, 0); + pixaAddPix(pixadb, pix2, L_INSERT); + pix2 = pixUnpackBinary(pixs, 32, 1); + pixRenderBoxaArb(pix2, boxa2, 2, 0, 255, 0); + pixaAddPix(pixadb, pix2, L_INSERT); + } + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + pixDestroy(&pix1); + return 0; +} + + +/*----------------------------------------------------------------------* + * Build grayscale composites (templates) * + *----------------------------------------------------------------------*/ +/*! + * \brief jbAccumulateComposites() + * + * \param[in] pixaa one pixa for each class + * \param[out] ppna number of samples used to build each composite + * \param[out] pptat centroids of bordered composites + * \return pixad accumulated sum of samples in each class, or NULL on error + * + */ +PIXA * +jbAccumulateComposites(PIXAA *pixaa, + NUMA **pna, + PTA **pptat) +{ +l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff; +l_float32 x, y, xave, yave; +NUMA *na; +PIX *pix, *pixt1, *pixt2, *pixsum; +PIXA *pixa, *pixad; +PTA *ptat, *pta; + + PROCNAME("jbAccumulateComposites"); + + if (!pptat) + return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL); + *pptat = NULL; + if (!pna) + return (PIXA *)ERROR_PTR("&na not defined", procName, NULL); + *pna = NULL; + if (!pixaa) + return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); + + n = pixaaGetCount(pixaa, NULL); + if ((ptat = ptaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("ptat not made", procName, NULL); + *pptat = ptat; + pixad = pixaCreate(n); + na = numaCreate(n); + *pna = na; + + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(pixaa, i, L_CLONE); + nt = pixaGetCount(pixa); + numaAddNumber(na, nt); + if (nt == 0) { + L_WARNING("empty pixa found!\n", procName); + pixaDestroy(&pixa); + continue; + } + pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh); + pix = pixaGetPix(pixa, 0, L_CLONE); + d = pixGetDepth(pix); + pixDestroy(&pix); + pixt1 = pixCreate(maxw, maxh, d); + pixsum = pixInitAccumulate(maxw, maxh, 0); + pta = pixaCentroids(pixa); + + /* Find the average value of the centroids ... */ + xave = yave = 0; + for (j = 0; j < nt; j++) { + ptaGetPt(pta, j, &x, &y); + xave += x; + yave += y; + } + xave = xave / (l_float32)nt; + yave = yave / (l_float32)nt; + + /* and place all centroids at their average value */ + for (j = 0; j < nt; j++) { + pixt2 = pixaGetPix(pixa, j, L_CLONE); + ptaGetPt(pta, j, &x, &y); + xdiff = (l_int32)(x - xave); + ydiff = (l_int32)(y - yave); + pixClearAll(pixt1); + pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC, + pixt2, 0, 0); + pixAccumulate(pixsum, pixt1, L_ARITH_ADD); + pixDestroy(&pixt2); + } + pixaAddPix(pixad, pixsum, L_INSERT); + ptaAddPt(ptat, xave, yave); + + pixaDestroy(&pixa); + pixDestroy(&pixt1); + ptaDestroy(&pta); + } + + return pixad; +} + + +/*! + * \brief jbTemplatesFromComposites() + * + * \param[in] pixac one pix of composites for each class + * \param[in] na number of samples used for each class composite + * \return pixad 8 bpp templates for each class, or NULL on error + * + */ +PIXA * +jbTemplatesFromComposites(PIXA *pixac, + NUMA *na) +{ +l_int32 n, i; +l_float32 nt; /* number of samples in the composite; always an integer */ +l_float32 factor; +PIX *pixsum; /* accumulated composite */ +PIX *pixd; +PIXA *pixad; + + PROCNAME("jbTemplatesFromComposites"); + + if (!pixac) + return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL); + if (!na) + return (PIXA *)ERROR_PTR("na not defined", procName, NULL); + + n = pixaGetCount(pixac); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pixsum = pixaGetPix(pixac, i, L_COPY); /* changed internally */ + numaGetFValue(na, i, &nt); + factor = 255. / nt; + pixMultConstAccumulate(pixsum, factor, 0); /* changes pixsum */ + pixd = pixFinalAccumulate(pixsum, 0, 8); + pixaAddPix(pixad, pixd, L_INSERT); + pixDestroy(&pixsum); + } + + return pixad; +} + + + +/*----------------------------------------------------------------------* + * jbig2 utility routines * + *----------------------------------------------------------------------*/ +/*! + * \brief jbClasserCreate() + * + * \param[in] method JB_RANKHAUS, JB_CORRELATION + * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS + * \return jbclasser, or NULL on error + */ +JBCLASSER * +jbClasserCreate(l_int32 method, + l_int32 components) +{ +JBCLASSER *classer; + + PROCNAME("jbClasserCreate"); + + if (method != JB_RANKHAUS && method != JB_CORRELATION) + return (JBCLASSER *)ERROR_PTR("invalid method", procName, NULL); + if (components != JB_CONN_COMPS && components != JB_CHARACTERS && + components != JB_WORDS) + return (JBCLASSER *)ERROR_PTR("invalid component", procName, NULL); + + classer = (JBCLASSER *)LEPT_CALLOC(1, sizeof(JBCLASSER)); + classer->method = method; + classer->components = components; + classer->nacomps = numaCreate(0); + classer->pixaa = pixaaCreate(0); + classer->pixat = pixaCreate(0); + classer->pixatd = pixaCreate(0); + classer->nafgt = numaCreate(0); + classer->naarea = numaCreate(0); + classer->ptac = ptaCreate(0); + classer->ptact = ptaCreate(0); + classer->naclass = numaCreate(0); + classer->napage = numaCreate(0); + classer->ptaul = ptaCreate(0); + return classer; +} + + +/* + * \brief jbClasserDestroy() + * + * \param[in,out] pclasser will be set to null before returning + * \return void + */ +void +jbClasserDestroy(JBCLASSER **pclasser) +{ +JBCLASSER *classer; + + if (!pclasser) + return; + if ((classer = *pclasser) == NULL) + return; + + sarrayDestroy(&classer->safiles); + numaDestroy(&classer->nacomps); + pixaaDestroy(&classer->pixaa); + pixaDestroy(&classer->pixat); + pixaDestroy(&classer->pixatd); + l_dnaHashDestroy(&classer->dahash); + numaDestroy(&classer->nafgt); + numaDestroy(&classer->naarea); + ptaDestroy(&classer->ptac); + ptaDestroy(&classer->ptact); + numaDestroy(&classer->naclass); + numaDestroy(&classer->napage); + ptaDestroy(&classer->ptaul); + ptaDestroy(&classer->ptall); + LEPT_FREE(classer); + *pclasser = NULL; + return; +} + + +/*! + * \brief jbDataSave() + * + * \param[in] jbclasser + * \param[in] latticew cell width used to store each connected + * component in the composite + * \param[in] latticeh ditto for cell height + * \return jbdata, or NULL on error + * + *
+ * Notes:
+ *      (1) This routine stores the jbig2-type data required for
+ *          generating a lossy jbig2 version of the image.
+ *          It can be losslessly written to (and read from) two files.
+ *      (2) It generates and stores the mosaic of templates.
+ *      (3) It clones the Numa and Pta arrays, so these must all
+ *          be destroyed by the caller.
+ *      (4) Input 0 to use the default values for latticew and/or latticeh,
+ * 
+ */ +JBDATA * +jbDataSave(JBCLASSER *classer) +{ +l_int32 maxw, maxh; +JBDATA *data; +PIX *pix; + + PROCNAME("jbDataSave"); + + if (!classer) + return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL); + + /* Write the templates into an array. */ + pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh); + pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1, + NULL, NULL); + if (!pix) + return (JBDATA *)ERROR_PTR("data not made", procName, NULL); + + data = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA)); + data->pix = pix; + data->npages = classer->npages; + data->w = classer->w; + data->h = classer->h; + data->nclass = classer->nclass; + data->latticew = maxw + 1; + data->latticeh = maxh + 1; + data->naclass = numaClone(classer->naclass); + data->napage = numaClone(classer->napage); + data->ptaul = ptaClone(classer->ptaul); + return data; +} + + +/* + * \brief jbDataDestroy() + * + * \param[in,out] pdata will be set to null before returning + * \return void + */ +void +jbDataDestroy(JBDATA **pdata) +{ +JBDATA *data; + + if (!pdata) + return; + if ((data = *pdata) == NULL) + return; + + pixDestroy(&data->pix); + numaDestroy(&data->naclass); + numaDestroy(&data->napage); + ptaDestroy(&data->ptaul); + LEPT_FREE(data); + *pdata = NULL; + return; +} + + +/*! + * \brief jbDataWrite() + * + * \param[in] rootname for output files; everything but the extension + * \param[in] jbdata + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serialization function that writes data in jbdata to file.
+ * 
+ */ +l_ok +jbDataWrite(const char *rootout, + JBDATA *jbdata) +{ +char buf[L_BUF_SIZE]; +l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage; +NUMA *naclass, *napage; +PTA *ptaul; +PIX *pixt; +FILE *fp; + + PROCNAME("jbDataWrite"); + + if (!rootout) + return ERROR_INT("no rootout", procName, 1); + if (!jbdata) + return ERROR_INT("no jbdata", procName, 1); + + npages = jbdata->npages; + w = jbdata->w; + h = jbdata->h; + pixt = jbdata->pix; + nclass = jbdata->nclass; + cellw = jbdata->latticew; + cellh = jbdata->latticeh; + naclass = jbdata->naclass; + napage = jbdata->napage; + ptaul = jbdata->ptaul; + + snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT); + pixWrite(buf, pixt, IFF_PNG); + + snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT); + if ((fp = fopenWriteStream(buf, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ncomp = ptaGetCount(ptaul); + fprintf(fp, "jb data file\n"); + fprintf(fp, "num pages = %d\n", npages); + fprintf(fp, "page size: w = %d, h = %d\n", w, h); + fprintf(fp, "num components = %d\n", ncomp); + fprintf(fp, "num classes = %d\n", nclass); + fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh); + for (i = 0; i < ncomp; i++) { + numaGetIValue(napage, i, &ipage); + numaGetIValue(naclass, i, &iclass); + ptaGetIPt(ptaul, i, &x, &y); + fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y); + } + fclose(fp); + + return 0; +} + + +/*! + * \brief jbDataRead() + * + * \param[in] rootname for template and data files + * \return jbdata, or NULL on error + */ +JBDATA * +jbDataRead(const char *rootname) +{ +char fname[L_BUF_SIZE]; +char *linestr; +l_uint8 *data; +l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage; +l_int32 npages, nclass, ncomp, ninit; +size_t size; +JBDATA *jbdata; +NUMA *naclass, *napage; +PIX *pixs; +PTA *ptaul; +SARRAY *sa; + + PROCNAME("jbDataRead"); + + if (!rootname) + return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL); + + snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT); + if ((pixs = pixRead(fname)) == NULL) + return (JBDATA *)ERROR_PTR("pix not read", procName, NULL); + + snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT); + if ((data = l_binaryRead(fname, &size)) == NULL) { + pixDestroy(&pixs); + return (JBDATA *)ERROR_PTR("data not read", procName, NULL); + } + + if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL) { + pixDestroy(&pixs); + LEPT_FREE(data); + return (JBDATA *)ERROR_PTR("sa not made", procName, NULL); + } + nsa = sarrayGetCount(sa); /* number of cc + 6 */ + linestr = sarrayGetString(sa, 0, L_NOCOPY); + if (strcmp(linestr, "jb data file") != 0) { + pixDestroy(&pixs); + LEPT_FREE(data); + sarrayDestroy(&sa); + return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL); + } + linestr = sarrayGetString(sa, 1, L_NOCOPY); + sscanf(linestr, "num pages = %d", &npages); + linestr = sarrayGetString(sa, 2, L_NOCOPY); + sscanf(linestr, "page size: w = %d, h = %d", &w, &h); + linestr = sarrayGetString(sa, 3, L_NOCOPY); + sscanf(linestr, "num components = %d", &ncomp); + linestr = sarrayGetString(sa, 4, L_NOCOPY); + sscanf(linestr, "num classes = %d\n", &nclass); + linestr = sarrayGetString(sa, 5, L_NOCOPY); + sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh); + +#if 1 + fprintf(stderr, "num pages = %d\n", npages); + fprintf(stderr, "page size: w = %d, h = %d\n", w, h); + fprintf(stderr, "num components = %d\n", ncomp); + fprintf(stderr, "num classes = %d\n", nclass); + fprintf(stderr, "template lattice size: w = %d, h = %d\n", cellw, cellh); +#endif + + ninit = ncomp; + if (ncomp > 1000000) { /* fuzz protection */ + L_WARNING("ncomp > 1M\n", procName); + ninit = 1000000; + } + naclass = numaCreate(ninit); + napage = numaCreate(ninit); + ptaul = ptaCreate(ninit); + for (i = 6; i < nsa; i++) { + linestr = sarrayGetString(sa, i, L_NOCOPY); + sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y); + numaAddNumber(napage, ipage); + numaAddNumber(naclass, iclass); + ptaAddPt(ptaul, x, y); + } + + jbdata = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA)); + jbdata->pix = pixs; + jbdata->npages = npages; + jbdata->w = w; + jbdata->h = h; + jbdata->nclass = nclass; + jbdata->latticew = cellw; + jbdata->latticeh = cellh; + jbdata->naclass = naclass; + jbdata->napage = napage; + jbdata->ptaul = ptaul; + + LEPT_FREE(data); + sarrayDestroy(&sa); + return jbdata; +} + + +/*! + * \brief jbDataRender() + * + * \param[in] jbdata + * \param[in] debugflag if TRUE, writes into 2 bpp pix and adds + * component outlines in color + * \return pixa reconstruction of original images, using templates or + * NULL on error + */ +PIXA * +jbDataRender(JBDATA *data, + l_int32 debugflag) +{ +l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage; +l_int32 npages, nclass, ncomp, wp, hp; +BOX *box; +NUMA *naclass, *napage; +PIX *pixt, *pixt2, *pix, *pixd; +PIXA *pixat; /* pixa of templates */ +PIXA *pixad; /* pixa of output images */ +PIXCMAP *cmap; +PTA *ptaul; + + PROCNAME("jbDataRender"); + + if (!data) + return (PIXA *)ERROR_PTR("data not defined", procName, NULL); + + npages = data->npages; + w = data->w; + h = data->h; + pixt = data->pix; + nclass = data->nclass; + cellw = data->latticew; + cellh = data->latticeh; + naclass = data->naclass; + napage = data->napage; + ptaul = data->ptaul; + ncomp = numaGetCount(naclass); + + /* Reconstruct the original set of images from the templates + * and the data associated with each component. First, + * generate the output pixa as a set of empty pix. */ + if ((pixad = pixaCreate(npages)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + for (i = 0; i < npages; i++) { + if (debugflag == FALSE) { + pix = pixCreate(w, h, 1); + } else { + pix = pixCreate(w, h, 2); + cmap = pixcmapCreate(2); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixcmapAddColor(cmap, 255, 0, 0); /* for box outlines */ + pixSetColormap(pix, cmap); + } + pixaAddPix(pixad, pix, L_INSERT); + } + + /* Put the class templates into a pixa. */ + if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pixat not made", procName, NULL); + } + + /* Place each component in the right location on its page. */ + for (i = 0; i < ncomp; i++) { + numaGetIValue(napage, i, &ipage); + numaGetIValue(naclass, i, &iclass); + pix = pixaGetPix(pixat, iclass, L_CLONE); /* the template */ + wp = pixGetWidth(pix); + hp = pixGetHeight(pix); + ptaGetIPt(ptaul, i, &x, &y); + pixd = pixaGetPix(pixad, ipage, L_CLONE); /* the output page */ + if (debugflag == FALSE) { + pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0); + } else { + pixt2 = pixConvert1To2Cmap(pix); + pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0); + box = boxCreate(x, y, wp, hp); + pixRenderBoxArb(pixd, box, 1, 255, 0, 0); + pixDestroy(&pixt2); + boxDestroy(&box); + } + pixDestroy(&pix); /* the clone only */ + pixDestroy(&pixd); /* the clone only */ + } + + pixaDestroy(&pixat); + return pixad; +} + + +/*! + * \brief jbGetULCorners() + * + * \param[in] jbclasser + * \param[in] pixs full res image + * \param[in] boxa of c.c. bounding rectangles for this page + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes the ptaul field, which has the global UL corners,
+ *          adjusted for each specific component, so that each component
+ *          can be replaced by the template for its class and have the
+ *          centroid in the template in the same position as the
+ *          centroid of the original connected component.  It is important
+ *          that this be done properly to avoid a wavy baseline in the
+ *          result.
+ *      (2) The array fields ptac and ptact give the centroids of
+ *          those components relative to the UL corner of each component.
+ *          Here, we compute the difference in each component, round to
+ *          nearest integer, and correct the box->x and box->y by
+ *          the appropriate integral difference.
+ *      (3) The templates and stored instances are all bordered.
+ * 
+ */ +l_ok +jbGetULCorners(JBCLASSER *classer, + PIX *pixs, + BOXA *boxa) +{ +l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy; +l_int32 *sumtab; +l_float32 x1, x2, y1, y2, delx, dely; +BOX *box; +NUMA *naclass; +PIX *pixt; +PTA *ptac, *ptact, *ptaul; + + PROCNAME("jbGetULCorners"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + + n = boxaGetCount(boxa); + ptaul = classer->ptaul; + naclass = classer->naclass; + ptac = classer->ptac; + ptact = classer->ptact; + baseindex = classer->baseindex; /* num components before this page */ + sumtab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + index = baseindex + i; + ptaGetPt(ptac, index, &x1, &y1); + numaGetIValue(naclass, index, &iclass); + ptaGetPt(ptact, iclass, &x2, &y2); + delx = x2 - x1; + dely = y2 - y1; + if (delx >= 0) + idelx = (l_int32)(delx + 0.5); + else + idelx = (l_int32)(delx - 0.5); + if (dely >= 0) + idely = (l_int32)(dely + 0.5); + else + idely = (l_int32)(dely - 0.5); + if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) { + LEPT_FREE(sumtab); + return ERROR_INT("box not found", procName, 1); + } + boxGetGeometry(box, &x, &y, NULL, NULL); + + /* Get final increments dx and dy for best alignment */ + pixt = pixaGetPix(classer->pixat, iclass, L_CLONE); + finalPositioningForAlignment(pixs, x, y, idelx, idely, + pixt, sumtab, &dx, &dy); +/* if (i % 20 == 0) + fprintf(stderr, "dx = %d, dy = %d\n", dx, dy); */ + ptaAddPt(ptaul, x - idelx + dx, y - idely + dy); + boxDestroy(&box); + pixDestroy(&pixt); + } + + LEPT_FREE(sumtab); + return 0; +} + + +/*! + * \brief jbGetLLCorners() + * + * \param[in] jbclasser + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes the ptall field, which has the global LL corners,
+ *          adjusted for each specific component, so that each component
+ *          can be replaced by the template for its class and have the
+ *          centroid in the template in the same position as the
+ *          centroid of the original connected component. It is important
+ *          that this be done properly to avoid a wavy baseline in the result.
+ *      (2) It is computed here from the corresponding UL corners, where
+ *          the input templates and stored instances are all bordered.
+ *          This should be done after all pages have been processed.
+ *      (3) For proper substitution, the templates whose LL corners are
+ *          placed in these locations must be UN-bordered.
+ *          This is available for a realistic jbig2 encoder, which would
+ *          (1) encode each template without a border, and (2) encode
+ *          the position using the LL corner (rather than the UL
+ *          corner) because the difference between y-values
+ *          of successive instances is typically close to zero.
+ * 
+ */ +l_ok +jbGetLLCorners(JBCLASSER *classer) +{ +l_int32 i, iclass, n, x1, y1, h; +NUMA *naclass; +PIX *pix; +PIXA *pixat; +PTA *ptaul, *ptall; + + PROCNAME("jbGetLLCorners"); + + if (!classer) + return ERROR_INT("classer not defined", procName, 1); + + ptaul = classer->ptaul; + naclass = classer->naclass; + pixat = classer->pixat; + + ptaDestroy(&classer->ptall); + n = ptaGetCount(ptaul); + ptall = ptaCreate(n); + classer->ptall = ptall; + + /* If the templates were bordered, we would add h - 1 to the UL + * corner y-value. However, because the templates to be used + * here have their borders removed, and the borders are + * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS + * to the UL corner y-value. */ + for (i = 0; i < n; i++) { + ptaGetIPt(ptaul, i, &x1, &y1); + numaGetIValue(naclass, i, &iclass); + pix = pixaGetPix(pixat, iclass, L_CLONE); + h = pixGetHeight(pix); + ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS); + pixDestroy(&pix); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Static helpers * + *----------------------------------------------------------------------*/ +/* When looking for similar matches we check templates whose size is +/- 2 in + * each direction. This involves 25 possible sizes. This array contains the + * offsets for each of those positions in a spiral pattern. There are 25 pairs + * of numbers in this array: even positions are x values. */ +static int two_by_two_walk[50] = { + 0, 0, + 0, 1, + -1, 0, + 0, -1, + 1, 0, + -1, 1, + 1, 1, + -1, -1, + 1, -1, + 0, -2, + 2, 0, + 0, 2, + -2, 0, + -1, -2, + 1, -2, + 2, -1, + 2, 1, + 1, 2, + -1, 2, + -2, 1, + -2, -1, + -2, -2, + 2, -2, + 2, 2, + -2, 2}; + + +/*! + * \brief findSimilarSizedTemplatesInit() + * + * \param[in] classer + * \param[in] pixs instance to be matched + * \return Allocated context to be used with findSimilar* + */ +static JBFINDCTX * +findSimilarSizedTemplatesInit(JBCLASSER *classer, + PIX *pixs) +{ +JBFINDCTX *state; + + state = (JBFINDCTX *)LEPT_CALLOC(1, sizeof(JBFINDCTX)); + state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS; + state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS; + state->classer = classer; + return state; +} + + +static void +findSimilarSizedTemplatesDestroy(JBFINDCTX **pstate) +{ +JBFINDCTX *state; + + PROCNAME("findSimilarSizedTemplatesDestroy"); + + if (pstate == NULL) { + L_WARNING("ptr address is null\n", procName); + return; + } + if ((state = *pstate) == NULL) + return; + + l_dnaDestroy(&state->dna); + LEPT_FREE(state); + *pstate = NULL; + return; +} + + +/*! + * \brief findSimilarSizedTemplatesNext() + * + * \param[in] state from findSimilarSizedTemplatesInit + * \return next template number, or -1 when finished + * + * We have a dna hash table that maps template area to a list of template + * numbers with that area. We wish to find similar sized templates, + * so we first look for templates with the same width and height, and + * then with width + 1, etc. This walk is guided by the + * two_by_two_walk array, above. + * + * We don't want to have to collect the whole list of templates first, + * because we hope to find a well-matching template quickly. So we + * keep the context for this walk in an explictit state structure, + * and this function acts like a generator. + */ +static l_int32 +findSimilarSizedTemplatesNext(JBFINDCTX *state) +{ +l_int32 desiredh, desiredw, size, templ; +PIX *pixt; + + while(1) { /* Continue the walk over step 'i' */ + if (state->i >= 25) { /* all done; didn't find a good match */ + return -1; + } + + desiredw = state->w + two_by_two_walk[2 * state->i]; + desiredh = state->h + two_by_two_walk[2 * state->i + 1]; + if (desiredh < 1 || desiredw < 1) { /* invalid size */ + state->i++; + continue; + } + + if (!state->dna) { + /* We have yet to start walking the array for the step 'i' */ + state->dna = l_dnaHashGetDna(state->classer->dahash, + (l_uint64)desiredh * desiredw, L_CLONE); + if (!state->dna) { /* nothing there */ + state->i++; + continue; + } + + state->n = 0; /* OK, we got a dna. */ + } + + /* Continue working on this dna */ + size = l_dnaGetCount(state->dna); + for ( ; state->n < size; ) { + templ = (l_int32)(state->dna->array[state->n++] + 0.5); + pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE); + if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw && + pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) { + pixDestroy(&pixt); + return templ; + } + pixDestroy(&pixt); + } + + /* Exhausted the dna (no match found); take another step and + * try again. */ + state->i++; + l_dnaDestroy(&state->dna); + continue; + } +} + + +/*! + * \brief finalPositioningForAlignment() + * + * \param[in] pixs input page image + * \param[in] x, y location of UL corner of bb of component in pixs + * \param[in] idelx, idely compensation to match centroids of component + * and template + * \param[in] pixt template, with JB_ADDED_PIXELS of padding + * on all sides + * \param[in] sumtab for summing fg pixels in an image + * \param[in] pdx, pdy return delta on position for best match; each + * one is in the set {-1, 0, 1} + * \return 0 if OK, 1 on error + * + */ +static l_int32 +finalPositioningForAlignment(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 idelx, + l_int32 idely, + PIX *pixt, + l_int32 *sumtab, + l_int32 *pdx, + l_int32 *pdy) +{ +l_int32 w, h, i, j, minx, miny, count, mincount; +PIX *pixi; /* clipped from source pixs */ +PIX *pixr; /* temporary storage */ +BOX *box; + + PROCNAME("finalPositioningForAlignment"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixt) + return ERROR_INT("pixt not defined", procName, 1); + if (!pdx || !pdy) + return ERROR_INT("&dx and &dy not both defined", procName, 1); + if (!sumtab) + return ERROR_INT("sumtab not defined", procName, 1); + *pdx = *pdy = 0; + + /* Use JB_ADDED_PIXELS pixels padding on each side */ + pixGetDimensions(pixt, &w, &h, NULL); + box = boxCreate(x - idelx - JB_ADDED_PIXELS, + y - idely - JB_ADDED_PIXELS, w, h); + pixi = pixClipRectangle(pixs, box, NULL); + boxDestroy(&box); + if (!pixi) + return ERROR_INT("pixi not made", procName, 1); + + pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1); + mincount = 0x7fffffff; + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + pixCopy(pixr, pixi); + pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0); + pixCountPixels(pixr, &count, sumtab); + if (count < mincount) { + minx = j; + miny = i; + mincount = count; + } + } + } + pixDestroy(&pixi); + pixDestroy(&pixr); + + *pdx = minx; + *pdy = miny; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jbclass.h b/hgdriver/3rdparty/hgOCR/leptonica/jbclass.h new file mode 100644 index 0000000..62aad60 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jbclass.h @@ -0,0 +1,142 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_JBCLASS_H +#define LEPTONICA_JBCLASS_H + +/*! + * \file jbclass.h + * + * JbClasser + * JbData + */ + + + /*! + *
+     * The JbClasser struct holds all the data accumulated during the
+     * classification process that can be used for a compressed
+     * jbig2-type representation of a set of images.  This is created
+     * in an initialization process and added to as the selected components
+     * on each successive page are analyzed.
+     * 
+ */ +struct JbClasser +{ + struct Sarray *safiles; /*!< input page image file names */ + l_int32 method; /*!< JB_RANKHAUS, JB_CORRELATION */ + l_int32 components; /*!< JB_CONN_COMPS, JB_CHARACTERS or */ + /*!< JB_WORDS */ + l_int32 maxwidth; /*!< max component width allowed */ + l_int32 maxheight; /*!< max component height allowed */ + l_int32 npages; /*!< number of pages already processed */ + l_int32 baseindex; /*!< number components already processed */ + /*!< on fully processed pages */ + struct Numa *nacomps; /*!< number of components on each page */ + l_int32 sizehaus; /*!< size of square struct elem for haus */ + l_float32 rankhaus; /*!< rank val of haus match, each way */ + l_float32 thresh; /*!< thresh value for correlation score */ + l_float32 weightfactor; /*!< corrects thresh value for heaver */ + /*!< components; use 0 for no correction */ + struct Numa *naarea; /*!< w * h of each template, without */ + /*!< extra border pixels */ + l_int32 w; /*!< max width of original src images */ + l_int32 h; /*!< max height of original src images */ + l_int32 nclass; /*!< current number of classes */ + l_int32 keep_pixaa; /*!< If zero, pixaa isn't filled */ + struct Pixaa *pixaa; /*!< instances for each class; unbordered */ + struct Pixa *pixat; /*!< templates for each class; bordered */ + /*!< and not dilated */ + struct Pixa *pixatd; /*!< templates for each class; bordered */ + /*!< and dilated */ + struct L_DnaHash *dahash; /*!< Hash table to find templates by size */ + struct Numa *nafgt; /*!< fg areas of undilated templates; */ + /*!< only used for rank < 1.0 */ + struct Pta *ptac; /*!< centroids of all bordered cc */ + struct Pta *ptact; /*!< centroids of all bordered template cc */ + struct Numa *naclass; /*!< array of class ids for each component */ + struct Numa *napage; /*!< array of page nums for each component */ + struct Pta *ptaul; /*!< array of UL corners at which the */ + /*!< template is to be placed for each */ + /*!< component */ + struct Pta *ptall; /*!< similar to ptaul, but for LL corners */ +}; +typedef struct JbClasser JBCLASSER; + + + /*! + *
+     * The JbData struct holds all the data required for
+     * the compressed jbig-type representation of a set of images.
+     * The data can be written to file, read back, and used
+     * to regenerate an approximate version of the original,
+     * which differs in two ways from the original:
+     *   (1) It uses a template image for each c.c. instead of the
+     *       original instance, for each occurrence on each page.
+     *   (2) It discards components with either a height or width larger
+     *       than the maximuma, given here by the lattice dimensions
+     *       used for storing the templates.
+     * 
+ */ +struct JbData +{ + struct Pix *pix; /*!< template composite for all classes */ + l_int32 npages; /*!< number of pages */ + l_int32 w; /*!< max width of original page images */ + l_int32 h; /*!< max height of original page images */ + l_int32 nclass; /*!< number of classes */ + l_int32 latticew; /*!< lattice width for template composite */ + l_int32 latticeh; /*!< lattice height for template composite */ + struct Numa *naclass; /*!< array of class ids for each component */ + struct Numa *napage; /*!< array of page nums for each component */ + struct Pta *ptaul; /*!< array of UL corners at which the */ + /*!< template is to be placed for each */ + /*!< component */ +}; +typedef struct JbData JBDATA; + + +/*! JB Classifier */ +enum { + JB_RANKHAUS = 0, + JB_CORRELATION = 1 +}; + + /*! For jbGetComponents(): type of component to extract from images */ +/*! JB Component */ +enum { + JB_CONN_COMPS = 0, + JB_CHARACTERS = 1, + JB_WORDS = 2 +}; + + /*! These parameters are used for naming the two files + * in which the jbig2-like compressed data is stored. */ +#define JB_TEMPLATE_EXT ".templates.png" +#define JB_DATA_EXT ".data" + + +#endif /* LEPTONICA_JBCLASS_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jp2kheader.c b/hgdriver/3rdparty/hgOCR/leptonica/jp2kheader.c new file mode 100644 index 0000000..2570481 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jp2kheader.c @@ -0,0 +1,312 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file jp2kheader.c + *
+ *
+ *      Read header
+ *          l_int32          readHeaderJp2k()
+ *          l_int32          freadHeaderJp2k()
+ *          l_int32          readHeaderMemJp2k()
+ *          l_int32          fgetJp2kResolution()
+ *
+ *  Note: these function read image metadata from a jp2k file, without
+ *  using any jp2k libraries.
+ *
+ *  To read and write jp2k data, using the OpenJPEG library
+ *  (http://www.openjpeg.org), see jpegio.c.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_IHDR 0 +#endif /* ~NO_CONSOLE_IO */ + +/* --------------------------------------------*/ +#if USE_JP2KHEADER /* defined in environ.h */ +/* --------------------------------------------*/ + + /* a sanity check on the size read from file */ +static const l_int32 MAX_JP2K_WIDTH = 100000; +static const l_int32 MAX_JP2K_HEIGHT = 100000; + +/*--------------------------------------------------------------------* + * Stream interface * + *--------------------------------------------------------------------*/ +/*! + * \brief readHeaderJp2k() + * + * \param[in] filename + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderJp2k(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderJp2k"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderJp2k(fp, pw, ph, pbps, pspp); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderJp2k() + * + * \param[in] fp file stream opened for read + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + */ +l_ok +freadHeaderJp2k(FILE *fp, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp) +{ +l_uint8 buf[80]; /* just need the first 80 bytes */ +l_int32 nread, ret; + + PROCNAME("freadHeaderJp2k"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + + rewind(fp); + nread = fread(buf, 1, sizeof(buf), fp); + if (nread != sizeof(buf)) + return ERROR_INT("read failure", procName, 1); + + ret = readHeaderMemJp2k(buf, sizeof(buf), pw, ph, pbps, pspp); + rewind(fp); + return ret; +} + + +/*! + * \brief readHeaderMemJp2k() + * + * \param[in] data + * \param[in] size at least 80 + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The ISO/IEC reference for jpeg2000 is
+ *               http://www.jpeg.org/public/15444-1annexi.pdf
+ *          and the file format syntax begins at page 127.
+ *      (2) The Image Header Box begins with 'ihdr' = 0x69686472 in
+ *          big-endian order.  This typically, but not always, starts
+ *          byte 44, with the big-endian data fields beginning at byte 48:
+ *               h:    4 bytes
+ *               w:    4 bytes
+ *               spp:  2 bytes
+ *               bps:  1 byte   (contains bps - 1)
+ * 
+ */ +l_ok +readHeaderMemJp2k(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp) +{ +l_int32 format, val, w, h, bps, spp, loc, found, windex; +l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* 'ihdr' */ + + PROCNAME("readHeaderMemJp2k"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (size < 80) + return ERROR_INT("size < 80", procName, 1); + findFileFormatBuffer(data, &format); + if (format != IFF_JP2) + return ERROR_INT("not jp2 file", procName, 1); + + /* Search for beginning of the Image Header Box: 'ihdr' */ + arrayFindSequence(data, size, ihdr, 4, &loc, &found); + if (!found) + return ERROR_INT("image parameters not found", procName, 1); +#if DEBUG_IHDR + if (loc != 44) + L_INFO("Beginning of ihdr is at byte %d\n", procName, loc); +#endif /* DEBUG_IHDR */ + + windex = loc / 4 + 1; + if (4 * (windex + 2) + 2 >= size) + return ERROR_INT("image parameters end are outside of header", + procName, 1); + val = *((l_uint32 *)data + windex); + h = convertOnLittleEnd32(val); + val = *((l_uint32 *)data + windex + 1); + w = convertOnLittleEnd32(val); + val = *((l_uint16 *)data + 2 * (windex + 2)); + spp = convertOnLittleEnd16(val); + bps = *(data + 4 * (windex + 2) + 2) + 1; + if (w < 1 || h < 1) + return ERROR_INT("w and h must both be > 0", procName, 1); + if (w > MAX_JP2K_WIDTH || h > MAX_JP2K_HEIGHT) + return ERROR_INT("unrealistically large sizes", procName, 1); + if (spp != 1 && spp != 3 && spp != 4) + return ERROR_INT("spp must be in 1, 3 or 4", procName, 1); + if (bps != 8 && bps != 16) + return ERROR_INT("bps must be 8 or 16", procName, 1); + if (pw) *pw = w; + if (ph) *ph = h; + if (pspp) *pspp = spp; + if (pbps) *pbps = bps; + return 0; +} + + +/* + * fgetJp2kResolution() + * + * Input: fp (file stream opened for read) + * &xres, &yres ( resolution in ppi) + * Return: 0 if found; 1 if not found or on error + * + * Notes: + * (1) If the capture resolution field is not set, this is not an error; + * the returned resolution values are 0 (designating 'unknown'). + * (2) Side-effect: this rewinds the stream. + * (3) The capture resolution box is optional in the jp2 spec, and + * it is usually not written. + * (4) The big-endian data fields that follow the 4 bytes of 'resc' are: + * ynum: 2 bytes + * ydenom: 2 bytes + * xnum: 2 bytes + * xdenom: 2 bytes + * yexp: 1 byte + * xexp: 1 byte + */ +l_int32 +fgetJp2kResolution(FILE *fp, + l_int32 *pxres, + l_int32 *pyres) +{ +l_uint8 xexp, yexp; +l_uint8 *data; +l_uint16 xnum, ynum, xdenom, ydenom; /* these jp2k fields are 2-byte */ +l_int32 loc, found; +l_uint8 resc[4] = {0x72, 0x65, 0x73, 0x63}; /* 'resc' */ +size_t nbytes; +l_float64 xres, yres, maxres; + + PROCNAME("fgetJp2kResolution"); + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", procName, 1); + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + + rewind(fp); + data = l_binaryReadStream(fp, &nbytes); + rewind(fp); + + /* Search for the start of the first capture resolution box: 'resc' */ + arrayFindSequence(data, nbytes, resc, 4, &loc, &found); + if (!found) { + L_WARNING("image resolution not found\n", procName); + LEPT_FREE(data); + return 1; + } + if (nbytes < 80 || loc >= nbytes - 13) { + L_WARNING("image resolution found without enough space\n", procName); + LEPT_FREE(data); + return 1; + } + + /* Extract the fields and calculate the resolution in pixels/meter. + * See section 1.5.3.7.1 of JPEG 2000 ISO/IEC 15444-1 spec. */ + ynum = data[loc + 5] << 8 | data[loc + 4]; + ynum = convertOnLittleEnd16(ynum); + ydenom = data[loc + 7] << 8 | data[loc + 6]; + ydenom = convertOnLittleEnd16(ydenom); + xnum = data[loc + 9] << 8 | data[loc + 8]; + xnum = convertOnLittleEnd16(xnum); + xdenom = data[loc + 11] << 8 | data[loc + 10]; + xdenom = convertOnLittleEnd16(xdenom); + yexp = data[loc + 12]; + xexp = data[loc + 13]; + yres = ((l_float64)ynum / (l_float64)ydenom) * pow(10.0, (l_float64)yexp); + xres = ((l_float64)xnum / (l_float64)xdenom) * pow(10.0, (l_float64)xexp); + + /* Convert from pixels/meter to ppi */ + yres *= (300.0 / 11811.0); + xres *= (300.0 / 11811.0); + + /* Sanity check for bad data */ + maxres = 100000.0; /* ppi */ + if (xres > maxres || yres > maxres) { + L_WARNING("ridiculously large resolution\n", procName); + } else { + *pyres = (l_int32)(yres + 0.5); + *pxres = (l_int32)(xres + 0.5); + } + + LEPT_FREE(data); + return 0; +} + +/* --------------------------------------------*/ +#endif /* USE_JP2KHEADER */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jp2kheaderstub.c b/hgdriver/3rdparty/hgOCR/leptonica/jp2kheaderstub.c new file mode 100644 index 0000000..1342150 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jp2kheaderstub.c @@ -0,0 +1,71 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file jp2kheaderstub.c + *
+ *
+ *     Stubs for jp2kheader.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_JP2KHEADER /* defined in environ.h */ +/* --------------------------------------------*/ + +l_ok readHeaderJp2k(const char *filename, l_int32 *pw, l_int32 *ph, + l_int32 *pbps, l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok freadHeaderJp2k(FILE *fp, l_int32 *pw, l_int32 *ph, + l_int32 *pbps, l_int32 *pspp) +{ + return ERROR_INT("function not present", "freadHeaderJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemJp2k(const l_uint8 *cdata, size_t size, l_int32 *pw, + l_int32 *ph, l_int32 *pbps, l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderMemJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_int32 fgetJp2kResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres) +{ + return ERROR_INT("function not present", "fgetJp2kResolution", 1); +} + +/* --------------------------------------------*/ +#endif /* !USE_JP2KHEADER */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jp2kio.c b/hgdriver/3rdparty/hgOCR/leptonica/jp2kio.c new file mode 100644 index 0000000..7360329 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jp2kio.c @@ -0,0 +1,949 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file jp2kio.c + *
+ *
+ *    Read jp2k from file
+ *          PIX                *pixReadJp2k()  [special top level]
+ *          PIX                *pixReadStreamJp2k()
+ *
+ *    Write jp2k to file
+ *          l_int32             pixWriteJp2k()  [special top level]
+ *          l_int32             pixWriteStreamJp2k()
+ *          static opj_image_t *pixConvertToOpjImage()
+ *
+ *    Read/write to memory
+ *          PIX                *pixReadMemJp2k()
+ *          l_int32             pixWriteMemJp2k()
+ *
+ *    Static functions from opj 2.0 to retain file stream interface
+ *          static opj_stream_t  *opjCreateStream()
+ *          [other static helpers]
+ *
+ *    Based on the OpenJPEG distribution:
+ *        http://www.openjpeg.org/
+ *    The ISO/IEC reference for jpeg2000 is:
+ *        http://www.jpeg.org/public/15444-1annexi.pdf
+ *
+ *    Compressing to memory and decompressing from memory
+ *    ---------------------------------------------------
+ *    On systems like windows without fmemopen() and open_memstream(),
+ *    we write data to a temp file and read it back for operations
+ *    between pix and compressed-data, such as pixReadMemJp2k() and
+ *    pixWriteMemJp2k().
+ *
+ *    Pdf can accept jp2k compressed strings directly
+ *    -----------------------------------------------
+ *    Transcoding (with the uncompress/compress cycle) is not required
+ *    to wrap images that have already been compressed with jp2k in pdf,
+ *    because the pdf format for jp2k includes the full string of the
+ *    jp2k compressed images.  This is also true for jpeg compressed
+ *    strings.
+ *
+ *    N.B.
+ *    * This is based on the most recent openjpeg release: 2.1.
+ *    * The openjpeg interface was massively changed from 1.X.  The debian
+ *      distribution is way back at 1.3.  We have inquired but are unable
+ *      to determine if or when a debian distribution will be built for 2.1.
+ *    * For version 2.1, the openjpeg.h file is installed in an
+ *      openjpeg-2.1 subdirectory, which is hard to support.
+ *    * In openjpeg-2.1, reading is slow compared to jpeg or webp,
+ *      and writing is very slow compared to jpeg or webp.  This is expected
+ *      to improve significantly in future versions.
+ *    * Reading and writing jp2k are supported here for 2.1.
+ *      The high-level interface to openjpeg continues to change.
+ *      From 2.0 to 2.1, the ability to interface to a C file stream
+ *      was removed permanently.  Leptonica supports both file stream
+ *      and memory buffer interfaces for every image I/O library, and
+ *      it requires the libraries to support at least one of these.
+ *      However, openjpeg-2.1 provides neither, so we have brought
+ *      several static functions over from openjpeg-2.0 in order to
+ *      retain the file stream interface.  See our static function
+ *      opjCreateStream().
+ *    * Specifying a quality factor for jpeg2000 requires caution.  Unlike
+ *      jpeg and webp, which have a sensible scale that goes from 0 (very poor)
+ *      to 100 (nearly lossless), kakadu and openjpeg use idiosyncratic and
+ *      non-intuitive numbers.  kakadu uses "rate/distortion" numbers in
+ *      a narrow range around 50,000; openjpeg (and our write interface)
+ *      use SNR.  The visually apparent artifacts introduced by compression
+ *      are strongly content-dependent and vary in a highly non-linear
+ *      way with SNR.  We take SNR = 34 as default, roughly similar in
+ *      quality to jpeg's default standard of 75.  For document images,
+ *      SNR = 25 is very poor, whereas SNR = 45 is nearly lossless.  If you
+ *      use the latter, you will pay dearly in the size of the compressed file.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if HAVE_LIBJP2K /* defined in environ.h */ +/* --------------------------------------------*/ + + /* Leptonica supports versions 2.0 and newer */ +#ifdef LIBJP2K_HEADER +#include LIBJP2K_HEADER +#else +#include +#endif + + /* 2.0 didn't define OPJ_VERSION_MINOR. */ +#ifndef OPJ_VERSION_MINOR +#define OPJ_VERSION_MINOR 0 +#endif + + /* Static generator of opj_stream from file stream. + * In 2.0.1, this functionality is provided by + * opj_stream_create_default_file_stream(), + * but it was removed in 2.1.0. Because we must have either + * a file stream or a memory interface to the compressed data, + * it is necessary to recreate the stream interface here. */ +static opj_stream_t *opjCreateStream(FILE *fp, l_int32 is_read); + + /* Static converter pix --> opj_image. Used for compressing pix, + * because the codec works on data stored in their raster format. */ +static opj_image_t *pixConvertToOpjImage(PIX *pix); + +/*---------------------------------------------------------------------* + * Callback event handlers * + *---------------------------------------------------------------------*/ +static void error_callback(const char *msg, void *client_data) { + (void)client_data; + fprintf(stdout, "[ERROR] %s", msg); +} + +static void warning_callback(const char *msg, void *client_data) { + (void)client_data; + fprintf(stdout, "[WARNING] %s", msg); +} + +static void info_callback(const char *msg, void *client_data) { + (void)client_data; + fprintf(stdout, "[INFO] %s", msg); +} + + +/*---------------------------------------------------------------------* + * Read jp2k from file (special function) * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadJp2k() + * + * \param[in] filename + * \param[in] reduction scaling factor: 1, 2, 4, 8, 16 + * \param[in] box [optional] for extracting a subregion, can be null + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return pix 8 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a special function for reading jp2k files.
+ *          The high-level pixReadStream() uses default values:
+ *             %reduction = 1
+ *             %box = NULL
+ *      (2) This decodes at either full resolution or at a reduction by
+ *          a power of 2.  The default value %reduction == 1 gives a full
+ *          resolution image.  Use %reduction > 1 to get a reduced image.
+ *          The actual values of %reduction that can be used on an image
+ *          depend on the number of resolution levels chosen when the
+ *          image was compressed.  Typical values might be 1, 2, 4, 8 and 16.
+ *          Using a value representing a reduction level that was not
+ *          stored when the file was written will fail with the message:
+ *          "failed to read the header".
+ *      (3) Use %box to decode only a part of the image.  The box is defined
+ *          at full resolution.  It is reduced internally by %reduction,
+ *          and clipping to the right and bottom of the image is automatic.
+ *      (4) We presently only handle images with 8 bits/sample (bps).
+ *          If the image has 16 bps, the read will fail.
+ *      (5) There are 4 possible values of samples/pixel (spp).
+ *          The values in brackets give the pixel values in the Pix:
+ *           spp = 1  ==>  grayscale           [8 bpp grayscale]
+ *           spp = 2  ==>  grayscale + alpha   [32 bpp rgba]
+ *           spp = 3  ==>  rgb                 [32 bpp rgb]
+ *           spp = 4  ==>  rgba                [32 bpp rgba]
+ *      (6) The %hint parameter is reserved for future use.
+ * 
+ */ +PIX * +pixReadJp2k(const char *filename, + l_uint32 reduction, + BOX *box, + l_int32 hint, + l_int32 debug) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadJp2k"); + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX *)ERROR_PTR("image file not found", procName, NULL); + pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); + fclose(fp); + + if (!pix) + return (PIX *)ERROR_PTR("image not returned", procName, NULL); + return pix; +} + + +/*! + * \brief pixReadStreamJp2k() + * + * \param[in] fp file stream + * \param[in] reduction scaling factor: 1, 2, 4, 8 + * \param[in] box [optional] for extracting a subregion, can be null + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return pix 8 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixReadJp2k() for usage.
+ * 
+ */ +PIX * +pixReadStreamJp2k(FILE *fp, + l_uint32 reduction, + BOX *box, + l_int32 hint, + l_int32 debug) +{ +const char *opjVersion; +l_int32 i, j, index, bx, by, bw, bh, val, rval, gval, bval, aval; +l_int32 w, h, wpl, bps, spp, xres, yres, reduce, prec, colorspace; +l_uint32 pixel; +l_uint32 *data, *line; +opj_dparameters_t parameters; /* decompression parameters */ +opj_image_t *image = NULL; +opj_codec_t *l_codec = NULL; /* handle to decompressor */ +opj_stream_t *l_stream = NULL; /* opj stream */ +PIX *pix = NULL; + + PROCNAME("pixReadStreamJp2k"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + + opjVersion = opj_version(); + if (opjVersion[0] != '2') { + L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); + return NULL; + } + if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { + L_ERROR("version %s: differs from minor = %d\n", + procName, opjVersion, OPJ_VERSION_MINOR); + return NULL; + } + + /* Get the resolution and the bits/sample */ + rewind(fp); + fgetJp2kResolution(fp, &xres, &yres); + freadHeaderJp2k(fp, NULL, NULL, &bps, NULL); + rewind(fp); + + if (bps > 8) { + L_ERROR("found %d bps; can only handle 8 bps\n", procName, bps); + return NULL; + } + + /* Set decoding parameters to default values */ + opj_set_default_decoder_parameters(¶meters); + + /* Find and set the reduce parameter, which is log2(reduction). + * Valid reductions are powers of 2, and are determined when the + * compressed string is made. A request for an invalid reduction + * will cause an error in opj_read_header(), and no image will + * be returned. */ + for (reduce = 0; (1L << reduce) < reduction; reduce++) { } + if ((1L << reduce) != reduction) { + L_ERROR("invalid reduction %d; not power of 2\n", procName, reduction); + return NULL; + } + parameters.cp_reduce = reduce; + + /* Get a decoder handle */ + if ((l_codec = opj_create_decompress(OPJ_CODEC_JP2)) == NULL) { + L_ERROR("failed to make the codec\n", procName); + return NULL; + } + + /* Catch and report events using callbacks */ + if (debug) { + opj_set_info_handler(l_codec, info_callback, NULL); + opj_set_warning_handler(l_codec, warning_callback, NULL); + opj_set_error_handler(l_codec, error_callback, NULL); + } + + /* Setup the decoding parameters using user parameters */ + if (!opj_setup_decoder(l_codec, ¶meters)){ + L_ERROR("failed to set up decoder\n", procName); + opj_destroy_codec(l_codec); + return NULL; + } + + /* Open decompression 'stream'. In 2.0, we could call this: + * opj_stream_create_default_file_stream(fp, 1) + * but the file stream interface was removed in 2.1. */ + if ((l_stream = opjCreateStream(fp, 1)) == NULL) { + L_ERROR("failed to open the stream\n", procName); + opj_destroy_codec(l_codec); + return NULL; + } + + /* Read the main header of the codestream and, if necessary, + * the JP2 boxes */ + if(!opj_read_header(l_stream, l_codec, &image)){ + L_ERROR("failed to read the header\n", procName); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + return NULL; + } + + /* Set up to decode a rectangular region */ + if (box) { + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (!opj_set_decode_area(l_codec, image, bx, by, + bx + bw, by + bh)) { + L_ERROR("failed to set the region for decoding\n", procName); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + return NULL; + } + } + + /* Get the decoded image */ + if (!(opj_decode(l_codec, l_stream, image) && + opj_end_decompress(l_codec, l_stream))) { + L_ERROR("failed to decode the image\n", procName); + opj_destroy_codec(l_codec); + opj_stream_destroy(l_stream); + opj_image_destroy(image); + return NULL; + } + + /* Finished with the byte stream and the codec */ + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + + /* Get the image parameters */ + spp = image->numcomps; + w = image->comps[0].w; + h = image->comps[0].h; + prec = image->comps[0].prec; + if (prec != bps) + L_WARNING("precision %d != bps %d!\n", procName, prec, bps); + if (debug) { + L_INFO("w = %d, h = %d, bps = %d, spp = %d\n", + procName, w, h, bps, spp); + colorspace = image->color_space; + if (colorspace == OPJ_CLRSPC_SRGB) + L_INFO("colorspace is sRGB\n", procName); + else if (colorspace == OPJ_CLRSPC_GRAY) + L_INFO("colorspace is grayscale\n", procName); + else if (colorspace == OPJ_CLRSPC_SYCC) + L_INFO("colorspace is YUV\n", procName); + } + + /* Convert the image to a pix */ + if (spp == 1) + pix = pixCreate(w, h, 8); + else + pix = pixCreate(w, h, 32); + pixSetInputFormat(pix, IFF_JP2); + pixSetResolution(pix, xres, yres); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + index = 0; + if (spp == 1) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = image->comps[0].data[index]; + SET_DATA_BYTE(line, j, val); + index++; + } + } + } else if (spp == 2) { /* convert to RGBA */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = image->comps[0].data[index]; + aval = image->comps[1].data[index]; + composeRGBAPixel(val, val, val, aval, &pixel); + line[j] = pixel; + index++; + } + } + } else if (spp >= 3) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + rval = image->comps[0].data[index]; + gval = image->comps[1].data[index]; + bval = image->comps[2].data[index]; + if (spp == 3) { + composeRGBPixel(rval, gval, bval, &pixel); + } else { /* spp == 4 */ + aval = image->comps[3].data[index]; + composeRGBAPixel(rval, gval, bval, aval, &pixel); + } + line[j] = pixel; + index++; + } + } + } + + /* Free the opj image data structure */ + opj_image_destroy(image); + + return pix; +} + + +/*---------------------------------------------------------------------* + * Write jp2k to file * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteJp2k() + * + * \param[in] filename + * \param[in] pix any depth, cmap is OK + * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless + * \param[in] nlevels resolution levels; <= 10; default = 5 + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The %quality parameter is the SNR.  The useful range is narrow:
+ *             SNR < 27  (terrible quality)
+ *             SNR = 34  (default; approximately equivalent to jpeg quality 75)
+ *             SNR = 40  (very high quality)
+ *             SNR = 45  (nearly lossless)
+ *          Use 0 for default; 100 for lossless.
+ *      (2) The %nlevels parameter is the number of resolution levels
+ *          to be written.  For example, with nlevels == 5, images with
+ *          reduction factors of 1, 2, 4, 8 and 16 are encoded, and retrieval
+ *          is done at the level requested when reading.  For default,
+ *          use either 5 or 0.
+ *      (3) The %hint parameter is not yet in use.
+ *      (4) For now, we only support 1 "layer" for quality.
+ * 
+ */ +l_ok +pixWriteJp2k(const char *filename, + PIX *pix, + l_int32 quality, + l_int32 nlevels, + l_int32 hint, + l_int32 debug) +{ +FILE *fp; + + PROCNAME("pixWriteJp2k"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + + if (pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug)) { + fclose(fp); + return ERROR_INT("pix not written to stream", procName, 1); + } + + fclose(fp); + return 0; +} + + +/*! + * \brief pixWriteStreamJp2k() + * + * \param[in] fp file stream + * \param[in] pix any depth, cmap is OK + * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless + * \param[in] nlevels <= 10 + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return 0 if OK, 1 on error + *
+ * Notes:
+ *      (1) See pixWriteJp2k() for usage.
+ *      (2) For an encoder with more encoding options, see, e.g.,
+ *    https://github.com/OpenJPEG/openjpeg/blob/master/tests/test_tile_encoder.c
+ * 
+ */ +l_ok +pixWriteStreamJp2k(FILE *fp, + PIX *pix, + l_int32 quality, + l_int32 nlevels, + l_int32 hint, + l_int32 debug) +{ +l_int32 w, h, d, success; +l_float32 snr; +const char *opjVersion; +PIX *pixs; +opj_cparameters_t parameters; /* compression parameters */ +opj_stream_t *l_stream = NULL; +opj_codec_t* l_codec = NULL;; +opj_image_t *image = NULL; + + PROCNAME("pixWriteStreamJp2k"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + snr = (l_float32)quality; + if (snr <= 0) snr = 34.0; /* default */ + if (snr < 27) + L_WARNING("SNR = %d < 27; very low\n", procName, (l_int32)snr); + if (snr == 100) snr = 0; /* for lossless */ + if (snr > 45) { + L_WARNING("SNR > 45; using lossless encoding\n", procName); + snr = 0; + } + + if (nlevels <= 0) nlevels = 5; /* default */ + if (nlevels > 10) { + L_WARNING("nlevels = %d > 10; setting to 10\n", procName, nlevels); + nlevels = 10; + } + + opjVersion = opj_version(); + if (opjVersion[0] != '2') { + L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); + return 1; + } + if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { + L_ERROR("version %s: differs from minor = %d\n", + procName, opjVersion, OPJ_VERSION_MINOR); + return 1; + } + + /* Remove colormap if it exists; result is 8 or 32 bpp */ + pixGetDimensions(pix, &w, &h, &d); + if (d == 24) { + pixs = pixConvert24To32(pix); + } else if (d == 32) { + pixs = pixClone(pix); + } else if (pixGetColormap(pix) == NULL) { + pixs = pixConvertTo8(pix, 0); + } else { /* colormap */ + L_INFO("removing colormap; may be better to compress losslessly\n", + procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + } + + /* Convert to opj image format. */ + pixSetPadBits(pixs, 0); + image = pixConvertToOpjImage(pixs); + pixDestroy(&pixs); + + /* Set encoding parameters to default values. + * We use one layer with the input SNR. */ + opj_set_default_encoder_parameters(¶meters); + parameters.cp_fixed_quality = 1; + parameters.cp_disto_alloc = 0; + parameters.cp_fixed_alloc = 0; + parameters.tcp_distoratio[0] = snr; + parameters.tcp_numlayers = 1; + parameters.numresolution = nlevels + 1; + + /* Create comment for codestream */ + if (parameters.cp_comment == NULL) { + const char comment1[] = "Created by Leptonica, version "; + const char comment2[] = "; using OpenJPEG, version "; + size_t len1 = strlen(comment1); + size_t len2 = strlen(comment2); + char *version1 = getLeptonicaVersion(); + const char *version2 = opj_version(); + len1 += len2 + strlen(version1) + strlen(version2) + 1; + parameters.cp_comment = (char *)LEPT_MALLOC(len1); + snprintf(parameters.cp_comment, len1, "%s%s%s%s", comment1, version1, + comment2, version2); + LEPT_FREE(version1); + } + + /* Get the encoder handle */ + if ((l_codec = opj_create_compress(OPJ_CODEC_JP2)) == NULL) { + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + return ERROR_INT("failed to get the encoder handle\n", procName, 1); + } + + /* Catch and report events using callbacks */ + if (debug) { + opj_set_info_handler(l_codec, info_callback, NULL); + opj_set_warning_handler(l_codec, warning_callback, NULL); + opj_set_error_handler(l_codec, error_callback, NULL); + } + + /* Set up the encoder */ + if (!opj_setup_encoder(l_codec, ¶meters, image)) { + opj_destroy_codec(l_codec); + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + return ERROR_INT("failed to set up the encoder\n", procName, 1); + } + + /* Open a compression stream for writing. In 2.0 we could use this: + * opj_stream_create_default_file_stream(fp, 0) + * but the file stream interface was removed in 2.1. */ + rewind(fp); + if ((l_stream = opjCreateStream(fp, 0)) == NULL) { + opj_destroy_codec(l_codec); + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + return ERROR_INT("failed to open l_stream\n", procName, 1); + } + + /* Encode the image */ + if (!opj_start_compress(l_codec, image, l_stream)) { + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + return ERROR_INT("opj_start_compress failed\n", procName, 1); + } + if (!opj_encode(l_codec, l_stream)) { + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + return ERROR_INT("opj_encode failed\n", procName, 1); + } + success = opj_end_compress(l_codec, l_stream); + + /* Clean up */ + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + LEPT_FREE(parameters.cp_comment); + if (success) + return 0; + else + return ERROR_INT("opj_end_compress failed\n", procName, 1); +} + + +/*! + * \brief pixConvertToOpjImage() + * + * \param[in] pix 8 or 32 bpp + * \return opj_image, or NULL on error + * + *
+ * Notes:
+ *      (1) Input pix is 8 bpp grayscale, 32 bpp rgb, or 32 bpp rgba.
+ *      (2) Gray + alpha pix are all represented as rgba.
+ * 
+ */ +static opj_image_t * +pixConvertToOpjImage(PIX *pix) +{ +l_int32 i, j, k, w, h, d, spp, wpl; +OPJ_COLOR_SPACE colorspace; +l_int32 *ir = NULL; +l_int32 *ig = NULL; +l_int32 *ib = NULL; +l_int32 *ia = NULL; +l_uint32 *line, *data; +opj_image_t *image; +opj_image_cmptparm_t cmptparm[4]; + + PROCNAME("pixConvertToOpjImage"); + + if (!pix) + return (opj_image_t *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 8 && d != 32) { + L_ERROR("invalid depth: %d\n", procName, d); + return NULL; + } + + /* Allocate the opj_image. */ + spp = pixGetSpp(pix); + memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); + for (i = 0; i < spp; i++) { + cmptparm[i].prec = 8; + cmptparm[i].bpp = 8; + cmptparm[i].sgnd = 0; + cmptparm[i].dx = 1; + cmptparm[i].dy = 1; + cmptparm[i].w = w; + cmptparm[i].h = h; + } + colorspace = (spp == 1) ? OPJ_CLRSPC_GRAY : OPJ_CLRSPC_SRGB; + if ((image = opj_image_create(spp, &cmptparm[0], colorspace)) == NULL) + return (opj_image_t *)ERROR_PTR("image not made", procName, NULL); + image->x0 = 0; + image->y0 = 0; + image->x1 = w; + image->y1 = h; + + /* Set the component pointers */ + ir = image->comps[0].data; + if (spp > 1) { + ig = image->comps[1].data; + ib = image->comps[2].data; + } + if(spp == 4) + ia = image->comps[3].data; + + /* Transfer the data from the pix */ + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0, k = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++, k++) { + if (spp == 1) { + ir[k] = GET_DATA_BYTE(line, j); + } else if (spp > 1) { + ir[k] = GET_DATA_BYTE(line + j, COLOR_RED); + ig[k] = GET_DATA_BYTE(line + j, COLOR_GREEN); + ib[k] = GET_DATA_BYTE(line + j, COLOR_BLUE); + } + if (spp == 4) + ia[k] = GET_DATA_BYTE(line + j, L_ALPHA_CHANNEL); + } + } + + return image; +} + + +/*---------------------------------------------------------------------* + * Read/write to memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadMemJp2k() + * + * \param[in] data const; jpeg-encoded + * \param[in] size of data + * \param[in] reduction scaling factor: 1, 2, 4, 8 + * \param[in] box [optional] for extracting a subregion, can be null + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This crashes when reading through the fmemopen cookie.
+ *          Until we can fix this, we use the file-based work-around.
+ *          And fixing this may take some time, because the basic
+ *          stream interface is no longer supported in openjpeg.
+ *      (2) See pixReadJp2k() for usage.
+ * 
+ */ +PIX * +pixReadMemJp2k(const l_uint8 *data, + size_t size, + l_uint32 reduction, + BOX *box, + l_int32 hint, + l_int32 debug) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadMemJp2k"); + + if (!data) + return (PIX *)ERROR_PTR("data not defined", procName, NULL); + + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIX *)ERROR_PTR("stream not opened", procName, NULL); + pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); + fclose(fp); + if (!pix) L_ERROR("pix not read\n", procName); + return pix; +} + + +/*! + * \brief pixWriteMemJp2k() + * + * \param[out] pdata data of jpeg compressed image + * \param[out] psize size of returned data + * \param[in] pix 8 or 32 bpp + * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless + * \param[in] nlevels 0 for default + * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default + * \param[in] debug output callback messages, etc + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteJp2k() for usage.  This version writes to
+ *          memory instead of to a file stream.
+ * 
+ */ +l_ok +pixWriteMemJp2k(l_uint8 **pdata, + size_t *psize, + PIX *pix, + l_int32 quality, + l_int32 nlevels, + l_int32 hint, + l_int32 debug) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixWriteMemJp2k"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*---------------------------------------------------------------------* + * Static functions from opj 2.0 to retain file stream interface * + *---------------------------------------------------------------------*/ +static l_uint64 +opj_get_user_data_length(FILE *fp) { + OPJ_OFF_T length = 0; + fseek(fp, 0, SEEK_END); + length = (OPJ_OFF_T)ftell(fp); + fseek(fp, 0, SEEK_SET); + return (l_uint64)length; +} + +static OPJ_SIZE_T +opj_read_from_file(void *p_buffer, OPJ_SIZE_T p_nb_bytes, FILE *fp) { + OPJ_SIZE_T l_nb_read = fread(p_buffer, 1, p_nb_bytes, fp); + return l_nb_read ? l_nb_read : (OPJ_SIZE_T) - 1; +} + +static OPJ_SIZE_T +opj_write_from_file(void *p_buffer, OPJ_SIZE_T p_nb_bytes, FILE *fp) +{ + return fwrite(p_buffer, 1, p_nb_bytes, fp); +} + +static OPJ_OFF_T +opj_skip_from_file(OPJ_OFF_T offset, FILE *fp) { + if (fseek(fp, offset, SEEK_CUR)) { + return -1; + } + return offset; +} + +static l_int32 +opj_seek_from_file(OPJ_OFF_T offset, FILE *fp) { + if (fseek(fp, offset, SEEK_SET)) { + return 0; + } + return 1; +} + + /* Static generator of opj_stream from file stream */ +static opj_stream_t * +opjCreateStream(FILE *fp, + l_int32 is_read_stream) +{ +opj_stream_t *l_stream; + + PROCNAME("opjCreateStream"); + + if (!fp) + return (opj_stream_t *)ERROR_PTR("fp not defined", procName, NULL); + + l_stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, is_read_stream); + if (!l_stream) + return (opj_stream_t *)ERROR_PTR("stream not made", procName, NULL); + +#if OPJ_VERSION_MINOR == 0 + opj_stream_set_user_data(l_stream, fp); +#else + opj_stream_set_user_data(l_stream, fp, + (opj_stream_free_user_data_fn)NULL); +#endif + opj_stream_set_user_data_length(l_stream, opj_get_user_data_length(fp)); + opj_stream_set_read_function(l_stream, + (opj_stream_read_fn)opj_read_from_file); + opj_stream_set_write_function(l_stream, + (opj_stream_write_fn)opj_write_from_file); + opj_stream_set_skip_function(l_stream, + (opj_stream_skip_fn)opj_skip_from_file); + opj_stream_set_seek_function(l_stream, + (opj_stream_seek_fn)opj_seek_from_file); + + return l_stream; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBJP2K */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jp2kiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/jp2kiostub.c new file mode 100644 index 0000000..ef61128 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jp2kiostub.c @@ -0,0 +1,98 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file jp2kiostub.c + *
+ *
+ *     Stubs for jp2kio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBJP2K /* defined in environ.h */ +/* --------------------------------------------*/ + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadJp2k(const char *filename, l_uint32 reduction, BOX *box, + l_int32 hint, l_int32 debug) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadJp2k", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box, + l_int32 hint, l_int32 debug) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamJp2k", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteJp2k(const char *filename, PIX *pix, l_int32 quality, + l_int32 nlevels, l_int32 hint, l_int32 debug) +{ + return ERROR_INT("function not present", "pixWriteJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamJp2k(FILE *fp, PIX *pix, l_int32 quality, + l_int32 nlevels, l_int32 hint, l_int32 debug) +{ + return ERROR_INT("function not present", "pixWriteStreamJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemJp2k(const l_uint8 *data, size_t size, l_uint32 reduction, + BOX *box, l_int32 hint, l_int32 debug) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadMemJp2k", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemJp2k(l_uint8 **pdata, size_t *psize, PIX *pix, + l_int32 quality, l_int32 nlevels, l_int32 hint, + l_int32 debug) +{ + return ERROR_INT("function not present", "pixWriteMemJp2k", 1); +} + +/* ----------------------------------------------------------------------*/ + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBJP2K */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jpegio.c b/hgdriver/3rdparty/hgOCR/leptonica/jpegio.c new file mode 100644 index 0000000..03ba284 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jpegio.c @@ -0,0 +1,1321 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + /*! + * \file jpegio.c + *
+  *
+  *    Read jpeg from file
+  *          PIX             *pixReadJpeg()  [special top level]
+  *          PIX             *pixReadStreamJpeg()
+  *
+  *    Read jpeg metadata from file
+  *          l_int32          readHeaderJpeg()
+  *          l_int32          freadHeaderJpeg()
+  *          l_int32          fgetJpegResolution()
+  *          l_int32          fgetJpegComment()
+  *
+  *    Write jpeg to file
+  *          l_int32          pixWriteJpeg()  [special top level]
+  *          l_int32          pixWriteStreamJpeg()
+  *
+  *    Read/write to memory
+  *          PIX             *pixReadMemJpeg()
+  *          l_int32          readHeaderMemJpeg()
+  *          l_int32          readResolutionMemJpeg()
+  *          l_int32          pixWriteMemJpeg()
+  *
+  *    Setting special flag for chroma sampling on write
+  *          l_int32          pixSetChromaSampling()
+  *
+  *    Static system helpers
+  *          static void      jpeg_error_catch_all_1()
+  *          static void      jpeg_error_catch_all_2()
+  *          static l_uint8   jpeg_getc()
+  *          static l_int32   jpeg_comment_callback()
+  *
+  *    Documentation: libjpeg.doc can be found, along with all
+  *    source code, at ftp://ftp.uu.net/graphics/jpeg
+  *    Download and untar the file:  jpegsrc.v6b.tar.gz
+  *    A good paper on jpeg can also be found there: wallace.ps.gz
+  *
+  *    The functions in libjpeg make it very simple to compress
+  *    and decompress images.  On input (decompression from file),
+  *    3 component color images can be read into either an 8 bpp Pix
+  *    with a colormap or a 32 bpp Pix with RGB components.  For output
+  *    (compression to file), all color Pix, whether 8 bpp with a
+  *    colormap or 32 bpp, are written compressed as a set of three
+  *    8 bpp (rgb) images.
+  *
+  *    Low-level error handling
+  *    ------------------------
+  *    The default behavior of the jpeg library is to call exit.
+  *    This is often undesirable, and the caller should make the
+  *    decision when to abort a process.  To prevent the jpeg library
+  *    from calling exit(), setjmp() has been inserted into all
+  *    readers and writers, and the cinfo struct has been set up so that
+  *    the low-level jpeg library will call a special error handler
+  *    that doesn't exit, instead of the default function error_exit().
+  *
+  *    To avoid race conditions and make these functions thread-safe in
+  *    the rare situation where calls to two threads are simultaneously
+  *    failing on bad jpegs, we insert a local copy of the jmp_buf struct
+  *    into the cinfo.client_data field, and use this on longjmp.
+  *    For extracting the jpeg comment, we have the added complication
+  *    that the client_data field must also return the jpeg comment,
+  *    and we use a different error handler.
+  *
+  *    How to avoid subsampling the chroma channels
+  *    --------------------------------------------
+  *    When writing, you can avoid subsampling the U,V (chroma)
+  *    channels.  This gives higher quality for the color, which is
+  *    important for some situations.  The default subsampling is 2x2 on
+  *    both channels.  Before writing, call pixSetChromaSampling(pix, 0)
+  *    to prevent chroma subsampling.
+  *
+  *    How to extract just the luminance channel in reading RGB
+  *    --------------------------------------------------------
+  *    For higher resolution and faster decoding of an RGB image, you
+  *    can extract just the 8 bpp luminance channel, using pixReadJpeg(),
+  *    where you use L_JPEG_READ_LUMINANCE for the %hint arg.
+  *
+  *    How to fail to read if the data is corrupted
+  *    ---------------------------------------------
+  *    By default, if the low-level jpeg library functions do not abort,
+  *    a pix will be returned, even if the data is corrupted and warnings
+  *    are issued.  In order to be most likely to fail to read when there
+  *    is data corruption, use L_JPEG_FAIL_ON_BAD_DATA in the %hint arg.
+  *
+  *    Compressing to memory and decompressing from memory
+  *    ---------------------------------------------------
+  *    On systems like windows without fmemopen() and open_memstream(),
+  *    we write data to a temp file and read it back for operations
+  *    between pix and compressed-data, such as pixReadMemJpeg() and
+  *    pixWriteMemJpeg().
+  *
+  *    Vestigial code: parsing the jpeg file for header metadata
+  *    ---------------------------------------------------------
+  *    For extracting header metadata, we previously parsed the file, looking
+  *    for specific markers.  This is error-prone because of non-standard
+  *    jpeg files, and we now use readHeaderJpeg() and readHeaderMemJpeg().
+  *    The vestigial code is retained in jpegio_notused.c to help you
+  *    understand a bit about how to parse jpeg markers.  It is not compiled
+  *    into the library.
+  * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + + /* --------------------------------------------*/ +#if HAVE_LIBJPEG /* defined in environ.h */ +/* --------------------------------------------*/ + +#include + + /* jconfig.h makes the error of setting + * #define HAVE_STDLIB_H + * which conflicts with config_auto.h (where it is set to 1) and results + * for some gcc compiler versions in a warning. The conflict is harmless + * but we suppress it by undefining the variable. */ +#undef HAVE_STDLIB_H +#include "jpeglib.h" + +static void jpeg_error_catch_all_1(j_common_ptr cinfo); +static void jpeg_error_catch_all_2(j_common_ptr cinfo); +static l_uint8 jpeg_getc(j_decompress_ptr cinfo); + +/* Note: 'boolean' is defined in jmorecfg.h. We use it explicitly + * here because for windows where __MINGW32__ is defined, + * the prototype for jpeg_comment_callback() is given as + * returning a boolean. */ +static boolean jpeg_comment_callback(j_decompress_ptr cinfo); + +/* This is saved in the client_data field of cinfo, and used both + * to retrieve the comment from its callback and to handle + * exceptions with a longjmp. */ +struct callback_data { + jmp_buf jmpbuf; + l_uint8 *comment; +}; + +#ifndef NO_CONSOLE_IO +#define DEBUG_INFO 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * Read jpeg from file (special function) * + *---------------------------------------------------------------------*/ + /*! + * \brief pixReadJpeg() + * + * \param[in] filename + * \param[in] cmapflag 0 for no colormap in returned pix; + * 1 to return an 8 bpp cmapped pix if spp = 3 or 4 + * \param[in] reduction scaling factor: 1, 2, 4 or 8 + * \param[out] pnwarn [optional] number of warnings about + * corrupted data + * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default + * \return pix, or NULL on error + * + *
+  * Notes:
+  *      (1) This is a special function for reading jpeg files.
+  *      (2) Use this if you want the jpeg library to create
+  *          an 8 bpp colormapped image.
+  *      (3) Images reduced by factors of 2, 4 or 8 can be returned
+  *          significantly faster than full resolution images.
+  *      (4) If the jpeg data is bad, the jpeg library will continue
+  *          silently, or return warnings, or attempt to exit.  Depending
+  *          on the severity of the data corruption, there are two possible
+  *          outcomes:
+  *          (a) a possibly damaged pix can be generated, along with zero
+  *              or more warnings, or
+  *          (b) the library will attempt to exit (caught by our error
+  *              handler) and no pix will be returned.
+  *          If a pix is generated with at least one warning of data
+  *          corruption, and if L_JPEG_FAIL_ON_BAD_DATA is included in %hint,
+  *          no pix will be returned.
+  *      (5) The possible hint values are given in the enum in imageio.h:
+  *            * L_JPEG_READ_LUMINANCE
+  *            * L_JPEG_FAIL_ON_BAD_DATA
+  *          Default (0) is to do neither.
+  * 
+ */ +PIX * +pixReadJpeg(const char *filename, + l_int32 cmapflag, + l_int32 reduction, + l_int32 *pnwarn, + l_int32 hint) +{ + l_int32 ret; + l_uint8 *comment; + FILE *fp; + PIX *pix; + + PROCNAME("pixReadJpeg"); + + if (pnwarn) *pnwarn = 0; + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", procName, NULL); + if (cmapflag != 0 && cmapflag != 1) + cmapflag = 0; /* default */ + if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) + return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX *)ERROR_PTR("image file not found", procName, NULL); + pix = pixReadStreamJpeg(fp, cmapflag, reduction, pnwarn, hint); + if (pix) { + ret = fgetJpegComment(fp, &comment); + if (!ret && comment) + pixSetText(pix, (char *)comment); + LEPT_FREE(comment); + } + fclose(fp); + + if (!pix) + return (PIX *)ERROR_PTR("image not returned", procName, NULL); + return pix; +} + + +/*! + * \brief pixReadStreamJpeg() + * + * \param[in] fp file stream + * \param[in] cmapflag 0 for no colormap in returned pix; + * 1 to return an 8 bpp cmapped pix if spp = 3 or 4 + * \param[in] reduction scaling factor: 1, 2, 4 or 8 + * \param[out] pnwarn [optional] number of warnings + * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default + * \return pix, or NULL on error + * + * Usage: see pixReadJpeg + *
+ * Notes:
+ *      (1) The jpeg comment, if it exists, is not stored in the pix.
+ * 
+ */ +PIX * +pixReadStreamJpeg(FILE *fp, + l_int32 cmapflag, + l_int32 reduction, + l_int32 *pnwarn, + l_int32 hint) +{ + l_int32 cyan, yellow, magenta, black, nwarn; + l_int32 i, j, k, rval, gval, bval; + l_int32 w, h, wpl, spp, ncolors, cindex, ycck, cmyk; + l_uint32 *data; + l_uint32 *line, *ppixel; + JSAMPROW rowbuffer; + PIX *pix; + PIXCMAP *cmap; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jmp_buf jmpbuf; /* must be local to the function */ + + PROCNAME("pixReadStreamJpeg"); + + if (pnwarn) *pnwarn = 0; + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + if (cmapflag != 0 && cmapflag != 1) + cmapflag = 0; /* default */ + if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) + return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL); + + if (BITS_IN_JSAMPLE != 8) /* set in jmorecfg.h */ + return (PIX *)ERROR_PTR("BITS_IN_JSAMPLE != 8", procName, NULL); + + rewind(fp); + pix = NULL; + rowbuffer = NULL; + + /* Modify the jpeg error handling to catch fatal errors */ + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_catch_all_1; + cinfo.client_data = (void *)&jmpbuf; + if (setjmp(jmpbuf)) { + pixDestroy(&pix); + LEPT_FREE(rowbuffer); + return (PIX *)ERROR_PTR("internal jpeg error", procName, NULL); + } + + /* Initialize jpeg structs for decompression */ + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + jpeg_read_header(&cinfo, TRUE); + cinfo.scale_denom = reduction; + cinfo.scale_num = 1; + jpeg_calc_output_dimensions(&cinfo); + if (hint & L_JPEG_READ_LUMINANCE) { + cinfo.out_color_space = JCS_GRAYSCALE; + spp = 1; + L_INFO("reading luminance channel only\n", procName); + } + else { + spp = cinfo.out_color_components; + } + + /* Allocate the image and a row buffer */ + w = cinfo.output_width; + h = cinfo.output_height; + ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmapflag == 0); + cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmapflag == 0); + if (spp != 1 && spp != 3 && !ycck && !cmyk) { + return (PIX *)ERROR_PTR("spp must be 1 or 3, or YCCK or CMYK", + procName, NULL); + } + if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ + rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), (size_t)spp * w); + pix = pixCreate(w, h, 32); + } + else { /* 8 bpp gray or colormapped */ + rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), w); + pix = pixCreate(w, h, 8); + } + pixSetInputFormat(pix, IFF_JFIF_JPEG); + if (!rowbuffer || !pix) { + LEPT_FREE(rowbuffer); + pixDestroy(&pix); + return (PIX *)ERROR_PTR("rowbuffer or pix not made", procName, NULL); + } + + /* Initialize decompression. Set up a colormap for color + * quantization if requested. */ + if (spp == 1) { /* Grayscale or colormapped */ + jpeg_start_decompress(&cinfo); + } + else { /* Color; spp == 3 or YCCK or CMYK */ + if (cmapflag == 0) { /* 24 bit color in 32 bit pix or YCCK/CMYK */ + cinfo.quantize_colors = FALSE; + jpeg_start_decompress(&cinfo); + } + else { /* Color quantize to 8 bits */ + cinfo.quantize_colors = TRUE; + cinfo.desired_number_of_colors = 256; + jpeg_start_decompress(&cinfo); + + /* Construct a pix cmap */ + cmap = pixcmapCreate(8); + ncolors = cinfo.actual_number_of_colors; + for (cindex = 0; cindex < ncolors; cindex++) { + rval = cinfo.colormap[0][cindex]; + gval = cinfo.colormap[1][cindex]; + bval = cinfo.colormap[2][cindex]; + pixcmapAddColor(cmap, rval, gval, bval); + } + pixSetColormap(pix, cmap); + } + } + wpl = pixGetWpl(pix); + data = pixGetData(pix); + + /* Decompress. Unfortunately, we cannot use the return value + * from jpeg_read_scanlines() to determine if there was a problem + * with the data; it always appears to return 1. We can only + * tell from the warnings during decoding, such as "premature + * end of data segment". The default behavior is to return an + * image even if there are warnings. However, by setting the + * hint to have the same bit flag as L_JPEG_FAIL_ON_BAD_DATA, + * no image will be returned if there are any warnings. */ + for (i = 0; i < h; i++) { + if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) == 0) { + L_ERROR("read error at scanline %d\n", procName, i); + pixDestroy(&pix); + jpeg_destroy_decompress(&cinfo); + LEPT_FREE(rowbuffer); + return (PIX *)ERROR_PTR("bad data", procName, NULL); + } + + /* -- 24 bit color -- */ + if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { + ppixel = data + i * wpl; + if (spp == 3) { + for (j = k = 0; j < w; j++) { + SET_DATA_BYTE(ppixel, COLOR_RED, rowbuffer[k++]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowbuffer[k++]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowbuffer[k++]); + /* should not use alpha byte, but for buggy readers, + * set it to opaque */ + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255); + ppixel++; + } + } + else { + /* This is a conversion from CMYK -> RGB that ignores + color profiles, and is invoked when the image header + claims to be in CMYK or YCCK colorspace. If in YCCK, + libjpeg may be doing YCCK -> CMYK under the hood. + To understand why the colors need to be inverted on + read-in for the Adobe marker, see the "Special + color spaces" section of "Using the IJG JPEG + Library" by Thomas G. Lane: + http://www.jpegcameras.com/libjpeg/libjpeg-3.html#ss3.1 + The non-Adobe conversion is equivalent to: + rval = black - black * cyan / 255 + ... + The Adobe conversion is equivalent to: + rval = black - black * (255 - cyan) / 255 + ... + Note that cyan is the complement to red, and we + are subtracting the complement color (weighted + by black) from black. For Adobe conversions, + where they've already inverted the CMY but not + the K, we have to invert again. The results + must be clipped to [0 ... 255]. */ + for (j = k = 0; j < w; j++) { + cyan = rowbuffer[k++]; + magenta = rowbuffer[k++]; + yellow = rowbuffer[k++]; + black = rowbuffer[k++]; + if (cinfo.saw_Adobe_marker) { + rval = (black * cyan) / 255; + gval = (black * magenta) / 255; + bval = (black * yellow) / 255; + } + else { + rval = black * (255 - cyan) / 255; + gval = black * (255 - magenta) / 255; + bval = black * (255 - yellow) / 255; + } + rval = L_MIN(L_MAX(rval, 0), 255); + gval = L_MIN(L_MAX(gval, 0), 255); + bval = L_MIN(L_MAX(bval, 0), 255); + composeRGBPixel(rval, gval, bval, ppixel); + ppixel++; + } + } + } + else { /* 8 bpp grayscale or colormapped pix */ + line = data + i * wpl; + for (j = 0; j < w; j++) + SET_DATA_BYTE(line, j, rowbuffer[j]); + } + } + + nwarn = cinfo.err->num_warnings; + if (pnwarn) *pnwarn = nwarn; + + /* If the pixel density is neither 1 nor 2, it may not be defined. + * In that case, don't set the resolution. */ + if (cinfo.density_unit == 1) { /* pixels per inch */ + pixSetXRes(pix, cinfo.X_density); + pixSetYRes(pix, cinfo.Y_density); + } + else if (cinfo.density_unit == 2) { /* pixels per centimeter */ + pixSetXRes(pix, (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5)); + pixSetYRes(pix, (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5)); + } + + if (cinfo.output_components != spp) + fprintf(stderr, "output spp = %d, spp = %d\n", + cinfo.output_components, spp); + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + LEPT_FREE(rowbuffer); + + if (nwarn > 0) { + if (hint & L_JPEG_FAIL_ON_BAD_DATA) { + L_ERROR("fail with %d warning(s) of bad data\n", procName, nwarn); + pixDestroy(&pix); + } + else { + L_WARNING("%d warning(s) of bad data\n", procName, nwarn); + } + } + + return pix; +} + + +/*---------------------------------------------------------------------* + * Read jpeg metadata from file * + *---------------------------------------------------------------------*/ + /*! + * \brief readHeaderJpeg() + * + * \param[in] filename + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pspp [optional] samples/pixel + * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise + * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderJpeg(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pspp, + l_int32 *pycck, + l_int32 *pcmyk) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("readHeaderJpeg"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pspp) *pspp = 0; + if (pycck) *pycck = 0; + if (pcmyk) *pcmyk = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pw && !ph && !pspp && !pycck && !pcmyk) + return ERROR_INT("no results requested", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderJpeg(fp, pw, ph, pspp, pycck, pcmyk); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderJpeg() + * + * \param[in] fp file stream + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pspp [optional] samples/pixel + * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise + * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +freadHeaderJpeg(FILE *fp, + l_int32 *pw, + l_int32 *ph, + l_int32 *pspp, + l_int32 *pycck, + l_int32 *pcmyk) +{ + l_int32 spp, w, h; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jmp_buf jmpbuf; /* must be local to the function */ + + PROCNAME("freadHeaderJpeg"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pspp) *pspp = 0; + if (pycck) *pycck = 0; + if (pcmyk) *pcmyk = 0; + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pw && !ph && !pspp && !pycck && !pcmyk) + return ERROR_INT("no results requested", procName, 1); + + rewind(fp); + + /* Modify the jpeg error handling to catch fatal errors */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = (void *)&jmpbuf; + jerr.error_exit = jpeg_error_catch_all_1; + if (setjmp(jmpbuf)) + return ERROR_INT("internal jpeg error", procName, 1); + + /* Initialize the jpeg structs for reading the header */ + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + jpeg_read_header(&cinfo, TRUE); + jpeg_calc_output_dimensions(&cinfo); + spp = cinfo.out_color_components; + w = cinfo.output_width; + h = cinfo.output_height; + if (w < 1 || h < 1 || spp < 1 || spp > 4) { + jpeg_destroy_decompress(&cinfo); + rewind(fp); + return ERROR_INT("bad jpeg image parameters", procName, 1); + } + + if (pspp) *pspp = spp; + if (pw) *pw = cinfo.output_width; + if (ph) *ph = cinfo.output_height; + if (pycck) *pycck = + (cinfo.jpeg_color_space == JCS_YCCK && spp == 4); + if (pcmyk) *pcmyk = + (cinfo.jpeg_color_space == JCS_CMYK && spp == 4); + + jpeg_destroy_decompress(&cinfo); + rewind(fp); + return 0; +} + + +/* + * \brief fgetJpegResolution() + * + * \param[in] fp file stream + * \param[out] pxres, pyres resolutions + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If neither resolution field is set, this is not an error;
+ *          the returned resolution values are 0 (designating 'unknown').
+ *      (2) Side-effect: this rewinds the stream.
+ * 
+ */ +l_int32 +fgetJpegResolution(FILE *fp, + l_int32 *pxres, + l_int32 *pyres) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jmp_buf jmpbuf; /* must be local to the function */ + + PROCNAME("fgetJpegResolution"); + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", procName, 1); + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + + rewind(fp); + + /* Modify the jpeg error handling to catch fatal errors */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = (void *)&jmpbuf; + jerr.error_exit = jpeg_error_catch_all_1; + if (setjmp(jmpbuf)) + return ERROR_INT("internal jpeg error", procName, 1); + + /* Initialize the jpeg structs for reading the header */ + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + jpeg_read_header(&cinfo, TRUE); + + /* It is common for the input resolution to be omitted from the + * jpeg file. If density_unit is not 1 or 2, simply return 0. */ + if (cinfo.density_unit == 1) { /* pixels/inch */ + *pxres = cinfo.X_density; + *pyres = cinfo.Y_density; + } + else if (cinfo.density_unit == 2) { /* pixels/cm */ + *pxres = (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5); + *pyres = (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5); + } + + jpeg_destroy_decompress(&cinfo); + rewind(fp); + return 0; +} + + +/* + * \brief fgetJpegComment() + * + * \param[in] fp file stream opened for read + * \param[out] pcomment comment + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Side-effect: this rewinds the stream.
+ * 
+ */ +l_int32 +fgetJpegComment(FILE *fp, + l_uint8 **pcomment) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + struct callback_data cb_data; /* contains local jmp_buf */ + + PROCNAME("fgetJpegComment"); + + if (!pcomment) + return ERROR_INT("&comment not defined", procName, 1); + *pcomment = NULL; + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + + rewind(fp); + + /* Modify the jpeg error handling to catch fatal errors */ + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = jpeg_error_catch_all_2; + cb_data.comment = NULL; + cinfo.client_data = (void *)&cb_data; + if (setjmp(cb_data.jmpbuf)) { + LEPT_FREE(cb_data.comment); + return ERROR_INT("internal jpeg error", procName, 1); + } + + /* Initialize the jpeg structs for reading the header */ + jpeg_create_decompress(&cinfo); + jpeg_set_marker_processor(&cinfo, JPEG_COM, jpeg_comment_callback); + jpeg_stdio_src(&cinfo, fp); + jpeg_read_header(&cinfo, TRUE); + + /* Save the result */ + *pcomment = cb_data.comment; + jpeg_destroy_decompress(&cinfo); + rewind(fp); + return 0; +} + + +/*---------------------------------------------------------------------* + * Writing Jpeg * + *---------------------------------------------------------------------*/ + /*! + * \brief pixWriteJpeg() + * + * \param[in] filename + * \param[in] pix any depth; cmap is OK + * \param[in] quality 1 - 100; 75 is default + * \param[in] progressive 0 for baseline sequential; 1 for progressive + * \return 0 if OK; 1 on error + */ +l_ok +pixWriteJpeg(const char *filename, + PIX *pix, + l_int32 quality, + l_int32 progressive) +{ + FILE *fp; + + PROCNAME("pixWriteJpeg"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + + if (pixWriteStreamJpeg(fp, pix, quality, progressive)) { + fclose(fp); + return ERROR_INT("pix not written to stream", procName, 1); + } + + fclose(fp); + return 0; +} + + +/*! + * \brief pixWriteStreamJpeg() + * + * \param[in] fp file stream + * \param[in] pixs any depth; cmap is OK + * \param[in] quality 1 - 100; 75 is default value; 0 is also default + * \param[in] progressive 0 for baseline sequential; 1 for progressive + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Progressive encoding gives better compression, at the
+ *          expense of slower encoding and decoding.
+ *      (2) Standard chroma subsampling is 2x2 on both the U and V
+ *          channels.  For highest quality, use no subsampling; this
+ *          option is set by pixSetChromaSampling(pix, 0).
+ *      (3) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
+ *          and 32 bpp.  However, it is possible, and in some cases desirable,
+ *          to write out a jpeg file using an rgb pix that has 24 bpp.
+ *          This can be created by appending the raster data for a 24 bpp
+ *          image (with proper scanline padding) directly to a 24 bpp
+ *          pix that was created without a data array.
+ *      (4) There are two compression paths in this function:
+ *          * Grayscale image, no colormap: compress as 8 bpp image.
+ *          * rgb full color image: copy each line into the color
+ *            line buffer, and compress as three 8 bpp images.
+ *      (5) Under the covers, the jpeg library transforms rgb to a
+ *          luminance-chromaticity triple, each component of which is
+ *          also 8 bits, and compresses that.  It uses 2 Huffman tables,
+ *          a higher resolution one (with more quantization levels)
+ *          for luminosity and a lower resolution one for the chromas.
+ * 
+ */ +l_ok +pixWriteStreamJpeg(FILE *fp, + PIX *pixs, + l_int32 quality, + l_int32 progressive) +{ + l_int32 xres, yres; + l_int32 i, j, k; + l_int32 w, h, d, wpl, spp, colorflag, rowsamples; + l_uint32 *ppixel, *line, *data; + JSAMPROW rowbuffer; + PIX *pix; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + char *text; + jmp_buf jmpbuf; /* must be local to the function */ + + PROCNAME("pixWriteStreamJpeg"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (quality <= 0) quality = 75; /* default */ + if (quality > 100) { + L_ERROR("invalid jpeg quality; setting to 75\n", procName); + quality = 75; + } + + /* If necessary, convert the pix so that it can be jpeg compressed. + * The colormap is removed based on the source, so if the colormap + * has only gray colors, the image will be compressed with spp = 1. */ + pixGetDimensions(pixs, &w, &h, &d); + pix = NULL; + if (pixGetColormap(pixs) != NULL) { + L_INFO("removing colormap; may be better to compress losslessly\n", + procName); + pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + } + else if (d >= 8 && d != 16) { /* normal case; no rewrite */ + pix = pixClone(pixs); + } + else if (d < 8 || d == 16) { + L_INFO("converting from %d to 8 bpp\n", procName, d); + pix = pixConvertTo8(pixs, 0); /* 8 bpp, no cmap */ + } + else { + L_ERROR("unknown pix type with d = %d and no cmap\n", procName, d); + return 1; + } + if (!pix) + return ERROR_INT("pix not made", procName, 1); + pixSetPadBits(pix, 0); + + rewind(fp); + rowbuffer = NULL; + + /* Modify the jpeg error handling to catch fatal errors */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = (void *)&jmpbuf; + jerr.error_exit = jpeg_error_catch_all_1; + if (setjmp(jmpbuf)) { + LEPT_FREE(rowbuffer); + pixDestroy(&pix); + return ERROR_INT("internal jpeg error", procName, 1); + } + + /* Initialize the jpeg structs for compression */ + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, fp); + cinfo.image_width = w; + cinfo.image_height = h; + + /* Set the color space and number of components */ + d = pixGetDepth(pix); + if (d == 8) { + colorflag = 0; /* 8 bpp grayscale; no cmap */ + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + } + else { /* d == 32 || d == 24 */ + colorflag = 1; /* rgb */ + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + } + + jpeg_set_defaults(&cinfo); + + /* Setting optimize_coding to TRUE seems to improve compression + * by approx 2-4 percent, and increases comp time by approx 20%. */ + cinfo.optimize_coding = FALSE; + + /* Set resolution in pixels/in (density_unit: 1 = in, 2 = cm) */ + xres = pixGetXRes(pix); + yres = pixGetYRes(pix); + if ((xres != 0) && (yres != 0)) { + cinfo.density_unit = 1; /* designates pixels per inch */ + cinfo.X_density = xres; + cinfo.Y_density = yres; + } + + /* Set the quality and progressive parameters */ + jpeg_set_quality(&cinfo, quality, TRUE); + if (progressive) + jpeg_simple_progression(&cinfo); + + /* Set the chroma subsampling parameters. This is done in + * YUV color space. The Y (intensity) channel is never subsampled. + * The standard subsampling is 2x2 on both the U and V channels. + * Notation on this is confusing. For a nice illustrations, see + * http://en.wikipedia.org/wiki/Chroma_subsampling + * The standard subsampling is written as 4:2:0. + * We allow high quality where there is no subsampling on the + * chroma channels: denoted as 4:4:4. */ + if (pixs->special == L_NO_CHROMA_SAMPLING_JPEG) { + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + } + + jpeg_start_compress(&cinfo, TRUE); + + /* Cap the text the length limit, 65533, for JPEG_COM payload. + * Just to be safe, subtract 100 to cover the Adobe name space. */ + if ((text = pixGetText(pix)) != NULL) { + if (strlen(text) > 65433) { + L_WARNING("text is %zu bytes; clipping to 65433\n", + procName, strlen(text)); + text[65433] = '\0'; + } + jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text)); + } + + /* Allocate row buffer */ + spp = cinfo.input_components; + rowsamples = spp * w; + if ((rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), rowsamples)) + == NULL) { + pixDestroy(&pix); + return ERROR_INT("calloc fail for rowbuffer", procName, 1); + } + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (colorflag == 0) { /* 8 bpp gray */ + for (j = 0; j < w; j++) + rowbuffer[j] = GET_DATA_BYTE(line, j); + } + else { /* colorflag == 1 */ + if (d == 24) { /* See note 3 above; special case of 24 bpp rgb */ + jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1); + } + else { /* standard 32 bpp rgb */ + ppixel = line; + for (j = k = 0; j < w; j++) { + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); + ppixel++; + } + } + } + if (d != 24) + jpeg_write_scanlines(&cinfo, &rowbuffer, 1); + } + jpeg_finish_compress(&cinfo); + + pixDestroy(&pix); + LEPT_FREE(rowbuffer); + jpeg_destroy_compress(&cinfo); + return 0; +} + + +/*---------------------------------------------------------------------* + * Read/write to memory * + *---------------------------------------------------------------------*/ + + /*! + * \brief pixReadMemJpeg() + * + * \param[in] data const; jpeg-encoded + * \param[in] size of data + * \param[in] cmflag colormap flag 0 means return RGB image if color; + * 1 means create a colormap and return + * an 8 bpp colormapped image if color + * \param[in] reduction scaling factor: 1, 2, 4 or 8 + * \param[out] pnwarn [optional] number of warnings + * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default + * \return pix, or NULL on error + * + *
+  * Notes:
+  *      (1) The %size byte of %data must be a null character.
+  *      (2) The only hint flag so far is L_JPEG_READ_LUMINANCE,
+  *          given in the enum in imageio.h.
+  *      (3) See pixReadJpeg() for usage.
+  * 
+ */ +PIX * +pixReadMemJpeg(const l_uint8 *data, + size_t size, + l_int32 cmflag, + l_int32 reduction, + l_int32 *pnwarn, + l_int32 hint) +{ + l_int32 ret; + l_uint8 *comment; + FILE *fp; + PIX *pix; + + PROCNAME("pixReadMemJpeg"); + + if (pnwarn) *pnwarn = 0; + if (!data) + return (PIX *)ERROR_PTR("data not defined", procName, NULL); + + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIX *)ERROR_PTR("stream not opened", procName, NULL); + pix = pixReadStreamJpeg(fp, cmflag, reduction, pnwarn, hint); + if (pix) { + ret = fgetJpegComment(fp, &comment); + if (!ret && comment) { + pixSetText(pix, (char *)comment); + LEPT_FREE(comment); + } + } + fclose(fp); + if (!pix) L_ERROR("pix not read\n", procName); + return pix; +} + + +/*! + * \brief readHeaderMemJpeg() + * + * \param[in] data const; jpeg-encoded + * \param[in] size of data + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pspp [optional] samples/pixel + * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise + * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderMemJpeg(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pspp, + l_int32 *pycck, + l_int32 *pcmyk) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("readHeaderMemJpeg"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pspp) *pspp = 0; + if (pycck) *pycck = 0; + if (pcmyk) *pcmyk = 0; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (!pw && !ph && !pspp && !pycck && !pcmyk) + return ERROR_INT("no results requested", procName, 1); + + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = freadHeaderJpeg(fp, pw, ph, pspp, pycck, pcmyk); + fclose(fp); + return ret; +} + + +/*! + * \brief readResolutionMemJpeg() + * + * \param[in] data const; jpeg-encoded + * \param[in] size of data + * \param[out] pxres [optional] + * \param[out] pyres [optional] + * \return 0 if OK, 1 on error + */ +l_ok +readResolutionMemJpeg(const l_uint8 *data, + size_t size, + l_int32 *pxres, + l_int32 *pyres) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("readResolutionMemJpeg"); + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (!pxres && !pyres) + return ERROR_INT("no results requested", procName, 1); + + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = fgetJpegResolution(fp, pxres, pyres); + fclose(fp); + return ret; +} + + +/*! + * \brief pixWriteMemJpeg() + * + * \param[out] pdata data of jpeg compressed image + * \param[out] psize size of returned data + * \param[in] pix any depth; cmap is OK + * \param[in] quality 1 - 100; 75 is default value; 0 is also default + * \param[in] progressive 0 for baseline sequential; 1 for progressive + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStreamJpeg() for usage.  This version writes to
+ *          memory instead of to a file stream.
+ * 
+ */ +l_ok +pixWriteMemJpeg(l_uint8 **pdata, + size_t *psize, + PIX *pix, + l_int32 quality, + l_int32 progressive) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("pixWriteMemJpeg"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixWriteStreamJpeg(fp, pix, quality, progressive); +#else + L_INFO("work-around: writing to a temp file\n", procName); +#ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#endif /* _WIN32 */ + ret = pixWriteStreamJpeg(fp, pix, quality, progressive); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*---------------------------------------------------------------------* + * Setting special flag for chroma sampling on write * + *---------------------------------------------------------------------*/ + /*! + * \brief pixSetChromaSampling() + * + * \param[in] pix + * \param[in] sampling 1 for subsampling; 0 for no subsampling + * \return 0 if OK, 1 on error + * + *
+  * Notes:
+  *      (1) The default is for 2x2 chroma subsampling because the files are
+  *          considerably smaller and the appearance is typically satisfactory.
+  *          To get full resolution output in the chroma channels for
+  *          jpeg writing, call this with %sampling == 0.
+  * 
+ */ +l_ok +pixSetChromaSampling(PIX *pix, + l_int32 sampling) +{ + PROCNAME("pixSetChromaSampling"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (sampling) + pixSetSpecial(pix, 0); /* default */ + else + pixSetSpecial(pix, L_NO_CHROMA_SAMPLING_JPEG); + return 0; +} + + +/*---------------------------------------------------------------------* + * Static system helpers * + *---------------------------------------------------------------------*/ + /*! + * \brief jpeg_error_catch_all_1() + * + * Notes: + * (1) The default jpeg error_exit() kills the process, but we + * never want a call to leptonica to kill a process. If you + * do want this behavior, remove the calls to these error handlers. + * (2) This is used where cinfo->client_data holds only jmpbuf. + */ +static void +jpeg_error_catch_all_1(j_common_ptr cinfo) +{ + jmp_buf *pjmpbuf = (jmp_buf *)cinfo->client_data; + (*cinfo->err->output_message) (cinfo); + jpeg_destroy(cinfo); + longjmp(*pjmpbuf, 1); + return; +} + +/*! + * \brief jpeg_error_catch_all_2() + * + * Notes: + * (1) This is used where cinfo->client_data needs to hold both + * the jmpbuf and the jpeg comment data. + * (2) On error, the comment data will be freed by the caller. + */ +static void +jpeg_error_catch_all_2(j_common_ptr cinfo) +{ + struct callback_data *pcb_data; + + pcb_data = (struct callback_data *)cinfo->client_data; + (*cinfo->err->output_message) (cinfo); + jpeg_destroy(cinfo); + longjmp(pcb_data->jmpbuf, 1); + return; +} + +/* This function was borrowed from libjpeg */ +static l_uint8 +jpeg_getc(j_decompress_ptr cinfo) +{ + struct jpeg_source_mgr *datasrc; + + datasrc = cinfo->src; + if (datasrc->bytes_in_buffer == 0) { + if (!(*datasrc->fill_input_buffer) (cinfo)) { + return 0; + } + } + datasrc->bytes_in_buffer--; + return GETJOCTET(*datasrc->next_input_byte++); +} + +/*! + * \brief jpeg_comment_callback() + * + * Notes: + * (1) This is used to read the jpeg comment (JPEG_COM). + * See the note above the declaration for why it returns + * a "boolean". + */ +static boolean +jpeg_comment_callback(j_decompress_ptr cinfo) +{ + l_int32 length, i; + l_uint8 *comment; + struct callback_data *pcb_data; + + /* Get the size of the comment */ + length = jpeg_getc(cinfo) << 8; + length += jpeg_getc(cinfo); + length -= 2; + if (length <= 0) + return 1; + + /* Extract the comment from the file */ + if ((comment = (l_uint8 *)LEPT_CALLOC(length + 1, sizeof(l_uint8))) == NULL) + return 0; + for (i = 0; i < length; i++) + comment[i] = jpeg_getc(cinfo); + + /* Save the comment and return */ + pcb_data = (struct callback_data *)cinfo->client_data; + if (pcb_data->comment) { /* clear before overwriting previous comment */ + LEPT_FREE(pcb_data->comment); + pcb_data->comment = NULL; + } + pcb_data->comment = comment; + return 1; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBJPEG */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/jpegiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/jpegiostub.c new file mode 100644 index 0000000..dc31c16 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/jpegiostub.c @@ -0,0 +1,151 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file jpegiostub.c + *
+ *
+ *     Stubs for jpegio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBJPEG /* defined in environ.h */ +/* --------------------------------------------*/ + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadJpeg(const char *filename, l_int32 cmflag, l_int32 reduction, + l_int32 *pnwarn, l_int32 hint) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadJpeg", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadStreamJpeg(FILE *fp, l_int32 cmflag, l_int32 reduction, + l_int32 *pnwarn, l_int32 hint) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamJpeg", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderJpeg(const char *filename, l_int32 *pw, l_int32 *ph, + l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk) +{ + return ERROR_INT("function not present", "readHeaderJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok freadHeaderJpeg(FILE *fp, l_int32 *pw, l_int32 *ph, + l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk) +{ + return ERROR_INT("function not present", "freadHeaderJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_int32 fgetJpegResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres) +{ + return ERROR_INT("function not present", "fgetJpegResolution", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_int32 fgetJpegComment(FILE *fp, l_uint8 **pcomment) +{ + return ERROR_INT("function not present", "fgetJpegComment", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteJpeg(const char *filename, PIX *pix, l_int32 quality, + l_int32 progressive) +{ + return ERROR_INT("function not present", "pixWriteJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamJpeg(FILE *fp, PIX *pix, l_int32 quality, + l_int32 progressive) +{ + return ERROR_INT("function not present", "pixWriteStreamJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemJpeg(const l_uint8 *cdata, size_t size, l_int32 cmflag, + l_int32 reduction, l_int32 *pnwarn, l_int32 hint) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadMemJpeg", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemJpeg(const l_uint8 *cdata, size_t size, + l_int32 *pw, l_int32 *ph, l_int32 *pspp, + l_int32 *pycck, l_int32 *pcmyk) +{ + return ERROR_INT("function not present", "readHeaderMemJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readResolutionMemJpeg(const l_uint8 *data, size_t size, + l_int32 *pxres, l_int32 *pyres) +{ + return ERROR_INT("function not present", "readResolutionMemJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemJpeg(l_uint8 **pdata, size_t *psize, PIX *pix, + l_int32 quality, l_int32 progressive) +{ + return ERROR_INT("function not present", "pixWriteMemJpeg", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixSetChromaSampling(PIX *pix, l_int32 sampling) +{ + return ERROR_INT("function not present", "pixSetChromaSampling", 1); +} + +/* ----------------------------------------------------------------------*/ + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBJPEG */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/kernel.c b/hgdriver/3rdparty/hgOCR/leptonica/kernel.c new file mode 100644 index 0000000..9e34e27 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/kernel.c @@ -0,0 +1,1284 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file kernel.c + *
+ *
+ *      Basic operations on kernels for image convolution
+ *
+ *         Create/destroy/copy
+ *            L_KERNEL   *kernelCreate()
+ *            void        kernelDestroy()
+ *            L_KERNEL   *kernelCopy()
+ *
+ *         Accessors:
+ *            l_int32     kernelGetElement()
+ *            l_int32     kernelSetElement()
+ *            l_int32     kernelGetParameters()
+ *            l_int32     kernelSetOrigin()
+ *            l_int32     kernelGetSum()
+ *            l_int32     kernelGetMinMax()
+ *
+ *         Normalize/invert
+ *            L_KERNEL   *kernelNormalize()
+ *            L_KERNEL   *kernelInvert()
+ *
+ *         Helper function
+ *            l_float32 **create2dFloatArray()
+ *
+ *         Serialized I/O
+ *            L_KERNEL   *kernelRead()
+ *            L_KERNEL   *kernelReadStream()
+ *            l_int32     kernelWrite()
+ *            l_int32     kernelWriteStream()
+ *
+ *         Making a kernel from a compiled string
+ *            L_KERNEL   *kernelCreateFromString()
+ *
+ *         Making a kernel from a simple file format
+ *            L_KERNEL   *kernelCreateFromFile()
+ *
+ *         Making a kernel from a Pix
+ *            L_KERNEL   *kernelCreateFromPix()
+ *
+ *         Display a kernel in a pix
+ *            PIX        *kernelDisplayInPix()
+ *
+ *         Parse string to extract numbers
+ *            NUMA       *parseStringForNumbers()
+ *
+ *      Simple parametric kernels
+ *            L_KERNEL   *makeFlatKernel()
+ *            L_KERNEL   *makeGaussianKernel()
+ *            L_KERNEL   *makeGaussianKernelSep()
+ *            L_KERNEL   *makeDoGKernel()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Array size must be > 0 and not larger than this */ +static const l_uint32 MaxArraySize = 100000; + + +/*------------------------------------------------------------------------* + * Create / Destroy * + *------------------------------------------------------------------------*/ +/*! + * \brief kernelCreate() + * + * \param[in] height, width + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) kernelCreate() initializes all values to 0.
+ *      (2) After this call, (cy,cx) and nonzero data values must be
+ *          assigned.
+ *      (2) The number of kernel elements must be less than 2^29.
+ * 
+ */ +L_KERNEL * +kernelCreate(l_int32 height, + l_int32 width) +{ +l_uint64 size64; +L_KERNEL *kel; + + PROCNAME("kernelCreate"); + + if (width <= 0) + return (L_KERNEL *)ERROR_PTR("width must be > 0", procName, NULL); + if (height <= 0) + return (L_KERNEL *)ERROR_PTR("height must be > 0", procName, NULL); + + /* Avoid overflow in malloc arg */ + size64 = (l_uint64)width * (l_uint64)height; + if (size64 >= (1LL << 29)) { + L_ERROR("requested width = %d, height = %d\n", procName, width, height); + return (L_KERNEL *)ERROR_PTR("size >= 2^29", procName, NULL); + } + + kel = (L_KERNEL *)LEPT_CALLOC(1, sizeof(L_KERNEL)); + kel->sy = height; + kel->sx = width; + if ((kel->data = create2dFloatArray(height, width)) == NULL) { + LEPT_FREE(kel); + return (L_KERNEL *)ERROR_PTR("data not allocated", procName, NULL); + } + return kel; +} + + +/*! + * \brief kernelDestroy() + * + * \param[in,out] pkel will be set to null before returning + * \return void + */ +void +kernelDestroy(L_KERNEL **pkel) +{ +l_int32 i; +L_KERNEL *kel; + + PROCNAME("kernelDestroy"); + + if (pkel == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + if ((kel = *pkel) == NULL) + return; + + for (i = 0; i < kel->sy; i++) + LEPT_FREE(kel->data[i]); + LEPT_FREE(kel->data); + LEPT_FREE(kel); + + *pkel = NULL; + return; +} + + +/*! + * \brief kernelCopy() + * + * \param[in] kels source kernel + * \return keld copy of kels, or NULL on error + */ +L_KERNEL * +kernelCopy(L_KERNEL *kels) +{ +l_int32 i, j, sx, sy, cx, cy; +L_KERNEL *keld; + + PROCNAME("kernelCopy"); + + if (!kels) + return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); + + kernelGetParameters(kels, &sy, &sx, &cy, &cx); + if ((keld = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); + keld->cy = cy; + keld->cx = cx; + for (i = 0; i < sy; i++) + for (j = 0; j < sx; j++) + keld->data[i][j] = kels->data[i][j]; + + return keld; +} + + +/*----------------------------------------------------------------------* + * Accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelGetElement() + * + * \param[in] kel + * \param[in] row + * \param[in] col + * \param[out] pval + * \return 0 if OK; 1 on error + */ +l_ok +kernelGetElement(L_KERNEL *kel, + l_int32 row, + l_int32 col, + l_float32 *pval) +{ + PROCNAME("kernelGetElement"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!kel) + return ERROR_INT("kernel not defined", procName, 1); + if (row < 0 || row >= kel->sy) + return ERROR_INT("kernel row out of bounds", procName, 1); + if (col < 0 || col >= kel->sx) + return ERROR_INT("kernel col out of bounds", procName, 1); + + *pval = kel->data[row][col]; + return 0; +} + + +/*! + * \brief kernelSetElement() + * + * \param[in] kel kernel + * \param[in] row + * \param[in] col + * \param[in] val + * \return 0 if OK; 1 on error + */ +l_ok +kernelSetElement(L_KERNEL *kel, + l_int32 row, + l_int32 col, + l_float32 val) +{ + PROCNAME("kernelSetElement"); + + if (!kel) + return ERROR_INT("kel not defined", procName, 1); + if (row < 0 || row >= kel->sy) + return ERROR_INT("kernel row out of bounds", procName, 1); + if (col < 0 || col >= kel->sx) + return ERROR_INT("kernel col out of bounds", procName, 1); + + kel->data[row][col] = val; + return 0; +} + + +/*! + * \brief kernelGetParameters() + * + * \param[in] kel kernel + * \param[out] psy, psx, pcy, pcx [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +kernelGetParameters(L_KERNEL *kel, + l_int32 *psy, + l_int32 *psx, + l_int32 *pcy, + l_int32 *pcx) +{ + PROCNAME("kernelGetParameters"); + + if (psy) *psy = 0; + if (psx) *psx = 0; + if (pcy) *pcy = 0; + if (pcx) *pcx = 0; + if (!kel) + return ERROR_INT("kernel not defined", procName, 1); + if (psy) *psy = kel->sy; + if (psx) *psx = kel->sx; + if (pcy) *pcy = kel->cy; + if (pcx) *pcx = kel->cx; + return 0; +} + + +/*! + * \brief kernelSetOrigin() + * + * \param[in] kel kernel + * \param[in] cy, cx + * \return 0 if OK; 1 on error + */ +l_ok +kernelSetOrigin(L_KERNEL *kel, + l_int32 cy, + l_int32 cx) +{ + PROCNAME("kernelSetOrigin"); + + if (!kel) + return ERROR_INT("kel not defined", procName, 1); + kel->cy = cy; + kel->cx = cx; + return 0; +} + + +/*! + * \brief kernelGetSum() + * + * \param[in] kel kernel + * \param[out] psum sum of all kernel values + * \return 0 if OK, 1 on error + */ +l_ok +kernelGetSum(L_KERNEL *kel, + l_float32 *psum) +{ +l_int32 sx, sy, i, j; + + PROCNAME("kernelGetSum"); + + if (!psum) + return ERROR_INT("&sum not defined", procName, 1); + *psum = 0.0; + if (!kel) + return ERROR_INT("kernel not defined", procName, 1); + + kernelGetParameters(kel, &sy, &sx, NULL, NULL); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + *psum += kel->data[i][j]; + } + } + return 0; +} + + +/*! + * \brief kernelGetMinMax() + * + * \param[in] kel kernel + * \param[out] pmin [optional] minimum value + * \param[out] pmax [optional] maximum value + * \return 0 if OK, 1 on error + */ +l_ok +kernelGetMinMax(L_KERNEL *kel, + l_float32 *pmin, + l_float32 *pmax) +{ +l_int32 sx, sy, i, j; +l_float32 val, minval, maxval; + + PROCNAME("kernelGetMinmax"); + + if (!pmin && !pmax) + return ERROR_INT("neither &min nor &max defined", procName, 1); + if (pmin) *pmin = 0.0; + if (pmax) *pmax = 0.0; + if (!kel) + return ERROR_INT("kernel not defined", procName, 1); + + kernelGetParameters(kel, &sy, &sx, NULL, NULL); + minval = 10000000.0; + maxval = -10000000.0; + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + val = kel->data[i][j]; + if (val < minval) + minval = val; + if (val > maxval) + maxval = val; + } + } + if (pmin) + *pmin = minval; + if (pmax) + *pmax = maxval; + + return 0; +} + + +/*----------------------------------------------------------------------* + * Normalize/Invert * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelNormalize() + * + * \param[in] kels source kel, to be normalized + * \param[in] normsum desired sum of elements in keld + * \return keld normalized version of kels, or NULL on error + * or if sum of elements is very close to 0) + * + *
+ * Notes:
+ *      (1) If the sum of kernel elements is close to 0, do not
+ *          try to calculate the normalized kernel.  Instead,
+ *          return a copy of the input kernel, with a warning.
+ * 
+ */ +L_KERNEL * +kernelNormalize(L_KERNEL *kels, + l_float32 normsum) +{ +l_int32 i, j, sx, sy, cx, cy; +l_float32 sum, factor; +L_KERNEL *keld; + + PROCNAME("kernelNormalize"); + + if (!kels) + return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); + + kernelGetSum(kels, &sum); + if (L_ABS(sum) < 0.00001) { + L_WARNING("null sum; not normalizing; returning a copy\n", procName); + return kernelCopy(kels); + } + + kernelGetParameters(kels, &sy, &sx, &cy, &cx); + if ((keld = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); + keld->cy = cy; + keld->cx = cx; + + factor = normsum / sum; + for (i = 0; i < sy; i++) + for (j = 0; j < sx; j++) + keld->data[i][j] = factor * kels->data[i][j]; + + return keld; +} + + +/*! + * \brief kernelInvert() + * + * \param[in] kels source kel, to be inverted + * \return keld spatially inverted, about the origin, or NULL on error + * + *
+ * Notes:
+ *      (1) For convolution, the kernel is spatially inverted before
+ *          a "correlation" operation is done between the kernel and the image.
+ * 
+ */ +L_KERNEL * +kernelInvert(L_KERNEL *kels) +{ +l_int32 i, j, sx, sy, cx, cy; +L_KERNEL *keld; + + PROCNAME("kernelInvert"); + + if (!kels) + return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); + + kernelGetParameters(kels, &sy, &sx, &cy, &cx); + if ((keld = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); + keld->cy = sy - 1 - cy; + keld->cx = sx - 1 - cx; + + for (i = 0; i < sy; i++) + for (j = 0; j < sx; j++) + keld->data[i][j] = kels->data[sy - 1 - i][sx - 1 - j]; + + return keld; +} + + +/*----------------------------------------------------------------------* + * Helper function * + *----------------------------------------------------------------------*/ +/*! + * \brief create2dFloatArray() + * + * \param[in] sy rows == height + * \param[in] sx columns == width + * \return doubly indexed array i.e., an array of sy row pointers, + * each of which points to an array of sx floats + * + *
+ * Notes:
+ *      (1) The array[%sy][%sx] is indexed in standard "matrix notation",
+ *          with the row index first.
+ *      (2) The caller kernelCreate() limits the size to < 2^29 pixels.
+ * 
+ */ +l_float32 ** +create2dFloatArray(l_int32 sy, + l_int32 sx) +{ +l_int32 i; +l_float32 **array; + + PROCNAME("create2dFloatArray"); + + if (sx <= 0 || sx > MaxArraySize) + return (l_float32 **)ERROR_PTR("sx out of bounds", procName, NULL); + if (sy <= 0 || sy > MaxArraySize) + return (l_float32 **)ERROR_PTR("sy out of bounds", procName, NULL); + + if ((array = (l_float32 **)LEPT_CALLOC(sy, sizeof(l_float32 *))) == NULL) + return (l_float32 **)ERROR_PTR("ptr array not made", procName, NULL); + for (i = 0; i < sy; i++) + array[i] = (l_float32 *)LEPT_CALLOC(sx, sizeof(l_float32)); + return array; +} + + +/*----------------------------------------------------------------------* + * Kernel serialized I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelRead() + * + * \param[in] fname filename + * \return kernel, or NULL on error + */ +L_KERNEL * +kernelRead(const char *fname) +{ +FILE *fp; +L_KERNEL *kel; + + PROCNAME("kernelRead"); + + if (!fname) + return (L_KERNEL *)ERROR_PTR("fname not defined", procName, NULL); + + if ((fp = fopenReadStream(fname)) == NULL) + return (L_KERNEL *)ERROR_PTR("stream not opened", procName, NULL); + if ((kel = kernelReadStream(fp)) == NULL) { + fclose(fp); + return (L_KERNEL *)ERROR_PTR("kel not returned", procName, NULL); + } + fclose(fp); + + return kel; +} + + +/*! + * \brief kernelReadStream() + * + * \param[in] fp file stream + * \return kernel, or NULL on error + */ +L_KERNEL * +kernelReadStream(FILE *fp) +{ +l_int32 sy, sx, cy, cx, i, j, ret, version, ignore; +L_KERNEL *kel; + + PROCNAME("kernelReadStream"); + + if (!fp) + return (L_KERNEL *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, " Kernel Version %d\n", &version); + if (ret != 1) + return (L_KERNEL *)ERROR_PTR("not a kernel file", procName, NULL); + if (version != KERNEL_VERSION_NUMBER) + return (L_KERNEL *)ERROR_PTR("invalid kernel version", procName, NULL); + + if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", + &sy, &sx, &cy, &cx) != 4) + return (L_KERNEL *)ERROR_PTR("dimensions not read", procName, NULL); + if (sx > MaxArraySize || sy > MaxArraySize) { + L_ERROR("sx = %d or sy = %d > %d\n", procName, sx, sy, MaxArraySize); + return NULL; + } + if ((kel = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); + kernelSetOrigin(kel, cy, cx); + + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) + ignore = fscanf(fp, "%15f", &kel->data[i][j]); + ignore = fscanf(fp, "\n"); + } + ignore = fscanf(fp, "\n"); + + return kel; +} + + +/*! + * \brief kernelWrite() + * + * \param[in] fname output file + * \param[in] kel kernel + * \return 0 if OK, 1 on error + */ +l_ok +kernelWrite(const char *fname, + L_KERNEL *kel) +{ +FILE *fp; + + PROCNAME("kernelWrite"); + + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (!kel) + return ERROR_INT("kel not defined", procName, 1); + + if ((fp = fopenWriteStream(fname, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + kernelWriteStream(fp, kel); + fclose(fp); + + return 0; +} + + +/*! + * \brief kernelWriteStream() + * + * \param[in] fp file stream + * \param[in] kel + * \return 0 if OK, 1 on error + */ +l_ok +kernelWriteStream(FILE *fp, + L_KERNEL *kel) +{ +l_int32 sx, sy, cx, cy, i, j; + + PROCNAME("kernelWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!kel) + return ERROR_INT("kel not defined", procName, 1); + kernelGetParameters(kel, &sy, &sx, &cy, &cx); + + fprintf(fp, " Kernel Version %d\n", KERNEL_VERSION_NUMBER); + fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) + fprintf(fp, "%15.4f", kel->data[i][j]); + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + + return 0; +} + + +/*----------------------------------------------------------------------* + * Making a kernel from a compiled string * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelCreateFromString() + * + * \param[in] h, w height, width + * \param[in] cy, cx origin + * \param[in] kdata + * \return kernel of the given size, or NULL on error + * + *
+ * Notes:
+ *      (1) The data is an array of chars, in row-major order, giving
+ *          space separated integers in the range [-255 ... 255].
+ *      (2) The only other formatting limitation is that you must
+ *          leave space between the last number in each row and
+ *          the double-quote.  If possible, it's also nice to have each
+ *          line in the string represent a line in the kernel; e.g.,
+ *              static const char *kdata =
+ *                  " 20   50   20 "
+ *                  " 70  140   70 "
+ *                  " 20   50   20 ";
+ * 
+ */ +L_KERNEL * +kernelCreateFromString(l_int32 h, + l_int32 w, + l_int32 cy, + l_int32 cx, + const char *kdata) +{ +l_int32 n, i, j, index; +l_float32 val; +L_KERNEL *kel; +NUMA *na; + + PROCNAME("kernelCreateFromString"); + + if (h < 1) + return (L_KERNEL *)ERROR_PTR("height must be > 0", procName, NULL); + if (w < 1) + return (L_KERNEL *)ERROR_PTR("width must be > 0", procName, NULL); + if (cy < 0 || cy >= h) + return (L_KERNEL *)ERROR_PTR("cy invalid", procName, NULL); + if (cx < 0 || cx >= w) + return (L_KERNEL *)ERROR_PTR("cx invalid", procName, NULL); + + kel = kernelCreate(h, w); + kernelSetOrigin(kel, cy, cx); + na = parseStringForNumbers(kdata, " \t\n"); + n = numaGetCount(na); + if (n != w * h) { + kernelDestroy(&kel); + numaDestroy(&na); + fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n); + return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); + } + + index = 0; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + numaGetFValue(na, index, &val); + kernelSetElement(kel, i, j, val); + index++; + } + } + + numaDestroy(&na); + return kel; +} + + +/*----------------------------------------------------------------------* + * Making a kernel from a simple file format * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelCreateFromFile() + * + * \param[in] filename + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) The file contains, in the following order:
+ *           ~ Any number of comment lines starting with '#' are ignored
+ *           ~ The height and width of the kernel
+ *           ~ The y and x values of the kernel origin
+ *           ~ The kernel data, formatted as lines of numbers (integers
+ *             or floats) for the kernel values in row-major order,
+ *             and with no other punctuation.
+ *             (Note: this differs from kernelCreateFromString(),
+ *             where each line must begin and end with a double-quote
+ *             to tell the compiler it's part of a string.)
+ *           ~ The kernel specification ends when a blank line,
+ *             a comment line, or the end of file is reached.
+ *      (2) All lines must be left-justified.
+ *      (3) See kernelCreateFromString() for a description of the string
+ *          format for the kernel data.  As an example, here are the lines
+ *          of a valid kernel description file  In the file, all lines
+ *          are left-justified:
+ * \code
+ *                    # small 3x3 kernel
+ *                    3 3
+ *                    1 1
+ *                    25.5   51    24.3
+ *                    70.2  146.3  73.4
+ *                    20     50.9  18.4
+ * \endcode
+ * 
+ */ +L_KERNEL * +kernelCreateFromFile(const char *filename) +{ +char *filestr, *line; +l_int32 nlines, i, j, first, index, w, h, cx, cy, n; +l_float32 val; +size_t size; +NUMA *na, *nat; +SARRAY *sa; +L_KERNEL *kel; + + PROCNAME("kernelCreateFromFile"); + + if (!filename) + return (L_KERNEL *)ERROR_PTR("filename not defined", procName, NULL); + + if ((filestr = (char *)l_binaryRead(filename, &size)) == NULL) + return (L_KERNEL *)ERROR_PTR("file not found", procName, NULL); + if (size == 0) { + LEPT_FREE(filestr); + return (L_KERNEL *)ERROR_PTR("file is empty", procName, NULL); + } + + sa = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + nlines = sarrayGetCount(sa); + + /* Find the first data line. */ + for (i = 0, first = 0; i < nlines; i++) { + line = sarrayGetString(sa, i, L_NOCOPY); + if (line[0] != '#') { + first = i; + break; + } + } + + /* Find the kernel dimensions and origin location. */ + line = sarrayGetString(sa, first, L_NOCOPY); + if (sscanf(line, "%d %d", &h, &w) != 2) { + sarrayDestroy(&sa); + return (L_KERNEL *)ERROR_PTR("error reading h,w", procName, NULL); + } + if (h > MaxArraySize || w > MaxArraySize) { + L_ERROR("h = %d or w = %d > %d\n", procName, h, w, MaxArraySize); + return NULL; + } + line = sarrayGetString(sa, first + 1, L_NOCOPY); + if (sscanf(line, "%d %d", &cy, &cx) != 2) { + sarrayDestroy(&sa); + return (L_KERNEL *)ERROR_PTR("error reading cy,cx", procName, NULL); + } + + /* Extract the data. This ends when we reach eof, or when we + * encounter a line of data that is either a null string or + * contains just a newline. */ + na = numaCreate(0); + for (i = first + 2; i < nlines; i++) { + line = sarrayGetString(sa, i, L_NOCOPY); + if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') + break; + nat = parseStringForNumbers(line, " \t\n"); + numaJoin(na, nat, 0, -1); + numaDestroy(&nat); + } + sarrayDestroy(&sa); + + n = numaGetCount(na); + if (n != w * h) { + numaDestroy(&na); + fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n); + return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); + } + + kel = kernelCreate(h, w); + kernelSetOrigin(kel, cy, cx); + index = 0; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + numaGetFValue(na, index, &val); + kernelSetElement(kel, i, j, val); + index++; + } + } + + numaDestroy(&na); + return kel; +} + + +/*----------------------------------------------------------------------* + * Making a kernel from a Pix * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelCreateFromPix() + * + * \param[in] pix + * \param[in] cy, cx origin of kernel + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) The origin must be positive and within the dimensions of the pix.
+ * 
+ */ +L_KERNEL * +kernelCreateFromPix(PIX *pix, + l_int32 cy, + l_int32 cx) +{ +l_int32 i, j, w, h, d; +l_uint32 val; +L_KERNEL *kel; + + PROCNAME("kernelCreateFromPix"); + + if (!pix) + return (L_KERNEL *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 8) + return (L_KERNEL *)ERROR_PTR("pix not 8 bpp", procName, NULL); + if (cy < 0 || cx < 0 || cy >= h || cx >= w) + return (L_KERNEL *)ERROR_PTR("(cy, cx) invalid", procName, NULL); + + kel = kernelCreate(h, w); + kernelSetOrigin(kel, cy, cx); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pix, j, i, &val); + kernelSetElement(kel, i, j, (l_float32)val); + } + } + + return kel; +} + + +/*----------------------------------------------------------------------* + * Display a kernel in a pix * + *----------------------------------------------------------------------*/ +/*! + * \brief kernelDisplayInPix() + * + * \param[in] kel kernel + * \param[in] size of grid interiors; odd; either 1 or a minimum size + * of 17 is enforced + * \param[in] gthick grid thickness; either 0 or a minimum size of 2 + * is enforced + * \return pix display of kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) This gives a visual representation of a kernel.
+ *      (2) There are two modes of display:
+ *          (a) Grid lines of minimum width 2, surrounding regions
+ *              representing kernel elements of minimum size 17,
+ *              with a "plus" mark at the kernel origin, or
+ *          (b) A pix without grid lines and using 1 pixel per kernel element.
+ *      (3) For both cases, the kernel absolute value is displayed,
+ *          normalized such that the maximum absolute value is 255.
+ *      (4) Large 2D separable kernels should be used for convolution
+ *          with two 1D kernels.  However, for the bilateral filter,
+ *          the computation time is independent of the size of the
+ *          2D content kernel.
+ * 
+ */ +PIX * +kernelDisplayInPix(L_KERNEL *kel, + l_int32 size, + l_int32 gthick) +{ +l_int32 i, j, w, h, sx, sy, cx, cy, width, x0, y0; +l_int32 normval; +l_float32 minval, maxval, max, val, norm; +PIX *pixd, *pixt0, *pixt1; + + PROCNAME("kernelDisplayInPix"); + + if (!kel) + return (PIX *)ERROR_PTR("kernel not defined", procName, NULL); + + /* Normalize the max value to be 255 for display */ + kernelGetParameters(kel, &sy, &sx, &cy, &cx); + kernelGetMinMax(kel, &minval, &maxval); + max = L_MAX(maxval, -minval); + if (max == 0.0) + return (PIX *)ERROR_PTR("kernel elements all 0.0", procName, NULL); + norm = 255. / (l_float32)max; + + /* Handle the 1 element/pixel case; typically with large kernels */ + if (size == 1 && gthick == 0) { + pixd = pixCreate(sx, sy, 8); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + kernelGetElement(kel, i, j, &val); + normval = (l_int32)(norm * L_ABS(val)); + pixSetPixel(pixd, j, i, normval); + } + } + return pixd; + } + + /* Enforce the constraints for the grid line version */ + if (size < 17) { + L_WARNING("size < 17; setting to 17\n", procName); + size = 17; + } + if (size % 2 == 0) + size++; + if (gthick < 2) { + L_WARNING("grid thickness < 2; setting to 2\n", procName); + gthick = 2; + } + + w = size * sx + gthick * (sx + 1); + h = size * sy + gthick * (sy + 1); + pixd = pixCreate(w, h, 8); + + /* Generate grid lines */ + for (i = 0; i <= sy; i++) + pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), + w - 1, gthick / 2 + i * (size + gthick), + gthick, L_SET_PIXELS); + for (j = 0; j <= sx; j++) + pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, + gthick / 2 + j * (size + gthick), h - 1, + gthick, L_SET_PIXELS); + + /* Generate mask for each element */ + pixt0 = pixCreate(size, size, 1); + pixSetAll(pixt0); + + /* Generate crossed lines for origin pattern */ + pixt1 = pixCreate(size, size, 1); + width = size / 8; + pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size), + size / 2, (l_int32)(0.88 * size), + width, L_SET_PIXELS); + pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2, + (l_int32)(0.85 * size), size / 2, + width, L_FLIP_PIXELS); + pixRasterop(pixt1, size / 2 - width, size / 2 - width, + 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); + + /* Paste the patterns in */ + y0 = gthick; + for (i = 0; i < sy; i++) { + x0 = gthick; + for (j = 0; j < sx; j++) { + kernelGetElement(kel, i, j, &val); + normval = (l_int32)(norm * L_ABS(val)); + pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0); + if (i == cy && j == cx) + pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval); + x0 += size + gthick; + } + y0 += size + gthick; + } + + pixDestroy(&pixt0); + pixDestroy(&pixt1); + return pixd; +} + + +/*------------------------------------------------------------------------* + * Parse string to extract numbers * + *------------------------------------------------------------------------*/ +/*! + * \brief parseStringForNumbers() + * + * \param[in] str string containing numbers; not changed + * \param[in] seps string of characters that can be used between ints + * \return numa of numbers found, or NULL on error + * + *
+ * Notes:
+ *     (1) The numbers can be ints or floats.
+ * 
+ */ +NUMA * +parseStringForNumbers(const char *str, + const char *seps) +{ +char *newstr, *head; +char *tail = NULL; +l_float32 val; +NUMA *na; + + PROCNAME("parseStringForNumbers"); + + if (!str) + return (NUMA *)ERROR_PTR("str not defined", procName, NULL); + + newstr = stringNew(str); /* to enforce const-ness of str */ + na = numaCreate(0); + head = strtokSafe(newstr, seps, &tail); + val = atof(head); + numaAddNumber(na, val); + LEPT_FREE(head); + while ((head = strtokSafe(NULL, seps, &tail)) != NULL) { + val = atof(head); + numaAddNumber(na, val); + LEPT_FREE(head); + } + + LEPT_FREE(newstr); + return na; +} + + +/*------------------------------------------------------------------------* + * Simple parametric kernels * + *------------------------------------------------------------------------*/ +/*! + * \brief makeFlatKernel() + * + * \param[in] height, width + * \param[in] cy, cx origin of kernel + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) This is the same low-pass filtering kernel that is used
+ *          in the block convolution functions.
+ *      (2) The kernel origin (%cy, %cx) is typically placed as near
+ *          the center of the kernel as possible.  If height and
+ *          width are odd, then using %cy = height / 2 and
+ *          %cx = width / 2 places the origin at the exact center.
+ *      (3) This returns a normalized kernel.
+ * 
+ */ +L_KERNEL * +makeFlatKernel(l_int32 height, + l_int32 width, + l_int32 cy, + l_int32 cx) +{ +l_int32 i, j; +l_float32 normval; +L_KERNEL *kel; + + PROCNAME("makeFlatKernel"); + + if ((kel = kernelCreate(height, width)) == NULL) + return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); + kernelSetOrigin(kel, cy, cx); + normval = 1.0 / (l_float32)(height * width); + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + kernelSetElement(kel, i, j, normval); + } + } + + return kel; +} + + +/*! + * \brief makeGaussianKernel() + * + * \param[in] halfh sy = 2 * halfh + 1 + * \param[in] halfw sx = 2 * halfw + 1 + * \param[in] stdev standard deviation + * \param[in] max value at (cx,cy) + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) The kernel size (sx, sy) = (2 * %halfw + 1, 2 * %halfh + 1)
+ *      (2) The kernel center (cx, cy) = (%halfw, %halfh).
+ *      (3) %halfw and %halfh are typically equal, and
+ *          are typically several times larger than the standard deviation.
+ *      (4) If pixConvolve() is invoked with normalization (the sum of
+ *          kernel elements = 1.0), use 1.0 for max (or any number that's
+ *          not too small or too large).
+ * 
+ */ +L_KERNEL * +makeGaussianKernel(l_int32 halfh, + l_int32 halfw, + l_float32 stdev, + l_float32 max) +{ +l_int32 sx, sy, i, j; +l_float32 val; +L_KERNEL *kel; + + PROCNAME("makeGaussianKernel"); + + sx = 2 * halfw + 1; + sy = 2 * halfh + 1; + if ((kel = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); + kernelSetOrigin(kel, halfh, halfw); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + val = expf(-(l_float32)((i - halfh) * (i - halfh) + + (j - halfw) * (j - halfw)) / + (2. * stdev * stdev)); + kernelSetElement(kel, i, j, max * val); + } + } + + return kel; +} + + +/*! + * \brief makeGaussianKernelSep() + * + * \param[in] halfh sy = 2 * halfh + 1 + * \param[in] halfw sx = 2 * halfw + 1 + * \param[in] stdev standard deviation + * \param[in] max value at (cx,cy) + * \param[out] pkelx x part of kernel + * \param[out] pkely y part of kernel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See makeGaussianKernel() for description of input parameters.
+ *      (2) These kernels are constructed so that the result of both
+ *          normalized and un-normalized convolution will be the same
+ *          as when convolving with pixConvolve() using the full kernel.
+ *      (3) The trick for the un-normalized convolution is to have the
+ *          product of the two kernel elemets at (cx,cy) be equal to %max,
+ *          not max**2.  That's why %max for kely is 1.0.  If instead
+ *          we use sqrt(%max) for both, the results are slightly less
+ *          accurate, when compared to using the full kernel in
+ *          makeGaussianKernel().
+ * 
+ */ +l_ok +makeGaussianKernelSep(l_int32 halfh, + l_int32 halfw, + l_float32 stdev, + l_float32 max, + L_KERNEL **pkelx, + L_KERNEL **pkely) +{ + PROCNAME("makeGaussianKernelSep"); + + if (!pkelx || !pkely) + return ERROR_INT("&kelx and &kely not defined", procName, 1); + + *pkelx = makeGaussianKernel(0, halfw, stdev, max); + *pkely = makeGaussianKernel(halfh, 0, stdev, 1.0); + return 0; +} + + +/*! + * \brief makeDoGKernel() + * + * \param[in] halfh sy = 2 * halfh + 1 + * \param[in] halfw sx = 2 * halfw + 1 + * \param[in] stdev standard deviation of narrower gaussian + * \param[in] ratio of stdev for wide filter to stdev for narrow one + * \return kernel, or NULL on error + * + *
+ * Notes:
+ *      (1) The DoG (difference of gaussians) is a wavelet mother
+ *          function with null total sum.  By subtracting two blurred
+ *          versions of the image, it acts as a bandpass filter for
+ *          frequencies passed by the narrow gaussian but stopped
+ *          by the wide one.See:
+ *               http://en.wikipedia.org/wiki/Difference_of_Gaussians
+ *      (2) The kernel size (sx, sy) = (2 * halfw + 1, 2 * halfh + 1).
+ *      (3) The kernel center (cx, cy) = (halfw, halfh).
+ *      (4) %halfw and %halfh are typically equal, and are typically
+ *          several times larger than the standard deviation.
+ *      (5) %ratio is the ratio of standard deviations of the wide
+ *          to narrow gaussian.  It must be >= 1.0; 1.0 is a no-op.
+ *      (6) Because the kernel is a null sum, it must be invoked without
+ *          normalization in pixConvolve().
+ * 
+ */ +L_KERNEL * +makeDoGKernel(l_int32 halfh, + l_int32 halfw, + l_float32 stdev, + l_float32 ratio) +{ +l_int32 sx, sy, i, j; +l_float32 pi, squaredist, highnorm, lownorm, val; +L_KERNEL *kel; + + PROCNAME("makeDoGKernel"); + + sx = 2 * halfw + 1; + sy = 2 * halfh + 1; + if ((kel = kernelCreate(sy, sx)) == NULL) + return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); + kernelSetOrigin(kel, halfh, halfw); + + pi = 3.1415926535; + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + squaredist = (l_float32)((i - halfh) * (i - halfh) + + (j - halfw) * (j - halfw)); + highnorm = 1. / (2 * stdev * stdev); + lownorm = highnorm / (ratio * ratio); + val = (highnorm / pi) * expf(-(highnorm * squaredist)) + - (lownorm / pi) * expf(-(lownorm * squaredist)); + kernelSetElement(kel, i, j, val); + } + } + + return kel; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/leptwin.c b/hgdriver/3rdparty/hgOCR/leptonica/leptwin.c new file mode 100644 index 0000000..72643a0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/leptwin.c @@ -0,0 +1,368 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file leptwin.c + *
+ *
+ *    This file contains Leptonica routines needed only on Microsoft Windows
+ *
+ *    Currently it only contains one public function
+ *    (based on dibsectn.c by jmh, 03-30-98):
+ *
+ *      HBITMAP    pixGetWindowsHBITMAP(PIX *pix)
+ * 
+ */ + +#ifdef _WIN32 +#include +#include +#include "allheaders.h" +#include "leptwin.h" + +/* Macro to determine the number of bytes per line in the DIB bits. + * This accounts for DWORD alignment by adding 31 bits, + * then dividing by 32, then rounding up to the next highest + * count of 4-bytes. Then, we multiply by 4 to get the total byte count. */ +#define BYTESPERLINE(Width, BPP) ((l_int32)((((DWORD)(Width) * (DWORD)(BPP) + 31) >> 5)) << 2) + + +/* ********************************************************************** + DWORD DSImageBitsSize(LPBITMAPINFO pbmi) + + PARAMETERS: + LPBITMAPINFO - pointer to a BITMAPINFO describing a DIB + + RETURNS: + DWORD - the size, in bytes, of the DIB's image bits + + REMARKS: + Calculates and returns the size, in bytes, of the image bits for + the DIB described by the BITMAPINFO. +********************************************************************** */ +static DWORD +DSImageBitsSize(LPBITMAPINFO pbmi) +{ + switch(pbmi->bmiHeader.biCompression) + { + case BI_RLE8: /* wrong if haven't called DSCreateDIBSection or + * CreateDIBSection with this pbmi */ + case BI_RLE4: + return pbmi->bmiHeader.biSizeImage; + break; + default: /* should not have to use "default" */ + case BI_RGB: + case BI_BITFIELDS: + return BYTESPERLINE(pbmi->bmiHeader.biWidth, \ + pbmi->bmiHeader.biBitCount * pbmi->bmiHeader.biPlanes) * + pbmi->bmiHeader.biHeight; + break; + } + return 0; +} + +/* ********************************************************************** + DWORD ImageBitsSize(HBITMAP hbitmap) + + PARAMETERS: + HBITMAP - hbitmap + + RETURNS: + DWORD - the size, in bytes, of the HBITMAP's image bits + + REMARKS: + Calculates and returns the size, in bytes, of the image bits for + the DIB described by the HBITMAP. +********************************************************************** */ +static DWORD +ImageBitsSize(HBITMAP hBitmap) +{ + DIBSECTION ds; + + GetObject(hBitmap, sizeof(DIBSECTION), &ds); + switch( ds.dsBmih.biCompression ) + { + case BI_RLE8: /* wrong if haven't called DSCreateDIBSection or + * CreateDIBSection with this pbmi */ + case BI_RLE4: + return ds.dsBmih.biSizeImage; + break; + default: /* should not have to use "default" */ + case BI_RGB: + case BI_BITFIELDS: + return BYTESPERLINE(ds.dsBmih.biWidth, \ + ds.dsBmih.biBitCount * ds.dsBmih.biPlanes) * + ds.dsBmih.biHeight; + break; + } + return 0; +} + +/*! + * \brief setColormap(LPBITMAPINFO pbmi, PIXCMAP *cmap) + * + * \param[in] pbmi pointer to a BITMAPINFO describing a DIB + * \param[in] cmap leptonica colormap + * \return number of colors in cmap + */ +static int +setColormap(LPBITMAPINFO pbmi, + PIXCMAP *cmap) +{ +l_int32 i, nColors, rval, gval, bval; + + nColors = pixcmapGetCount(cmap); + for (i = 0; i < nColors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + pbmi->bmiColors[i].rgbRed = rval; + pbmi->bmiColors[i].rgbGreen = gval; + pbmi->bmiColors[i].rgbBlue = bval; + pbmi->bmiColors[i].rgbReserved = 0; + } + pbmi->bmiHeader.biClrUsed = nColors; + return nColors; +} + +/* ********************************************************************** + HBITMAP DSCreateBitmapInfo(l_int32 width, l_int32 height, l_int32 depth, + PIXCMAP *cmap) + + PARAMETERS: + l_int32 width - Desired width of the DIBSection + l_int32 height - Desired height of the DIBSection + l_int32 depth - Desired bit-depth of the DIBSection + PIXCMAP cmap - leptonica colormap for depths < 16 + + RETURNS: + LPBITMAPINFO - a ptr to BITMAPINFO of the desired size and bit-depth + NULL on failure + + REMARKS: + Creates a BITMAPINFO based on the criteria passed in as parameters. + +********************************************************************** */ +static LPBITMAPINFO +DSCreateBitmapInfo(l_int32 width, + l_int32 height, + l_int32 depth, + PIXCMAP *cmap) +{ +l_int32 nInfoSize; +LPBITMAPINFO pbmi; +LPDWORD pMasks; + + nInfoSize = sizeof(BITMAPINFOHEADER); + if( depth <= 8 ) + nInfoSize += sizeof(RGBQUAD) * (1 << depth); + if((depth == 16) || (depth == 32)) + nInfoSize += (3 * sizeof(DWORD)); + + /* Create the header big enough to contain color table and + * bitmasks if needed. */ + pbmi = (LPBITMAPINFO)malloc(nInfoSize); + if (!pbmi) + return NULL; + + ZeroMemory(pbmi, nInfoSize); + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = width; + pbmi->bmiHeader.biHeight = height; + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biBitCount = depth; + + /* override below for 16 and 32 bpp */ + pbmi->bmiHeader.biCompression = BI_RGB; + + /* ?? not sure if this is right? */ + pbmi->bmiHeader.biSizeImage = DSImageBitsSize(pbmi); + + pbmi->bmiHeader.biXPelsPerMeter = 0; + pbmi->bmiHeader.biYPelsPerMeter = 0; + pbmi->bmiHeader.biClrUsed = 0; /* override below */ + pbmi->bmiHeader.biClrImportant = 0; + + switch(depth) + { + case 24: + /* 24bpp requires no special handling */ + break; + case 16: + /* if it's 16bpp, fill in the masks and override the + * compression. These are the default masks -- you + * could change them if needed. */ + pMasks = (LPDWORD)(pbmi->bmiColors); + pMasks[0] = 0x00007c00; + pMasks[1] = 0x000003e0; + pMasks[2] = 0x0000001f; + pbmi->bmiHeader.biCompression = BI_BITFIELDS; + break; + case 32: + /* if it's 32 bpp, fill in the masks and override + * the compression */ + pMasks = (LPDWORD)(pbmi->bmiColors); + /*pMasks[0] = 0x00ff0000; */ + /*pMasks[1] = 0x0000ff00; */ + /*pMasks[2] = 0x000000ff; */ + pMasks[0] = 0xff000000; + pMasks[1] = 0x00ff0000; + pMasks[2] = 0x0000ff00; + + pbmi->bmiHeader.biCompression = BI_BITFIELDS; + break; + case 8: + case 4: + case 1: + setColormap(pbmi, cmap); + break; + } + return pbmi; +} + +/* ********************************************************************** + HBITMAP DSCreateDIBSection(l_int32 width, l_int32 height, l_int32 depth, + PIXCMAP *cmap) + + PARAMETERS: + l_int32 width - Desired width of the DIBSection + l_int32 height - Desired height of the DIBSection + l_int32 depth - Desired bit-depth of the DIBSection + PIXCMAP cmap - leptonica colormap for depths < 16 + + RETURNS: + HBITMAP - a DIBSection HBITMAP of the desired size and bit-depth + NULL on failure + + REMARKS: + Creates a DIBSection based on the criteria passed in as parameters. + +********************************************************************** */ +static HBITMAP +DSCreateDIBSection(l_int32 width, + l_int32 height, + l_int32 depth, + PIXCMAP *cmap) +{ +HBITMAP hBitmap; +l_int32 nInfoSize; +LPBITMAPINFO pbmi; +HDC hRefDC; +LPBYTE pBits; + + pbmi = DSCreateBitmapInfo (width, height, depth, cmap); + if (!pbmi) + return NULL; + + hRefDC = GetDC(NULL); + hBitmap = CreateDIBSection(hRefDC, pbmi, DIB_RGB_COLORS, + (void **) &pBits, NULL, 0); + nInfoSize = GetLastError(); + ReleaseDC(NULL, hRefDC); + free(pbmi); + + return hBitmap; +} + + +/*! + * \brief pixGetWindowsHBITMAP() + * + * \param[in] pix + * \return Windows hBitmap, or NULL on error + * + *
+ * Notes:
+ *      (1) It's the responsibility of the caller to destroy the
+ *          returned hBitmap with a call to DeleteObject (or with
+ *          something that eventually calls DeleteObject).
+ * 
+ */ +HBITMAP +pixGetWindowsHBITMAP(PIX *pix) +{ +l_int32 width, height, depth; +l_uint32 *data; +HBITMAP hBitmap = NULL; +BITMAP bm; +DWORD imageBitsSize; +PIX *pixt = NULL; +PIXCMAP *cmap; + + PROCNAME("pixGetWindowsHBITMAP"); + if (!pix) + return (HBITMAP)ERROR_PTR("pix not defined", procName, NULL); + + pixGetDimensions(pix, &width, &height, &depth); + cmap = pixGetColormap(pix); + + if (depth == 24) depth = 32; + if (depth == 2) { + pixt = pixConvert2To8(pix, 0, 85, 170, 255, TRUE); + if (!pixt) + return (HBITMAP)ERROR_PTR("unable to convert pix from 2bpp to 8bpp", + procName, NULL); + depth = pixGetDepth(pixt); + cmap = pixGetColormap(pixt); + } + + if (depth < 16) { + if (!cmap) + cmap = pixcmapCreateLinear(depth, 1< + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +LEPT_DLL extern HBITMAP pixGetWindowsHBITMAP( PIX *pixs ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LEPTONICA_LEPTWIN_H */ +#endif /* _WIN32 */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/libversions.c b/hgdriver/3rdparty/hgOCR/leptonica/libversions.c new file mode 100644 index 0000000..cbad4fa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/libversions.c @@ -0,0 +1,204 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file libversions.c + *
+ *
+ *       Image library version number
+ *           char      *getImagelibVersions()
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +#if HAVE_LIBGIF +#include "gif_lib.h" +#endif + +#if HAVE_LIBJPEG +/* jpeglib.h includes jconfig.h, which makes the error of setting + * #define HAVE_STDLIB_H + * which conflicts with config_auto.h (where it is set to 1) and results + * for some gcc compiler versions in a warning. The conflict is harmless + * but we suppress it by undefining the variable. */ +#undef HAVE_STDLIB_H +#include "jpeglib.h" +#include "jerror.h" +#endif + +#if HAVE_LIBPNG +#include "png.h" +#endif + +#if HAVE_LIBTIFF +#include "tiffio.h" +#endif + +#if HAVE_LIBZ +#include "zlib.h" +#endif + +#if HAVE_LIBWEBP +#include "webp/encode.h" +#endif + +#if HAVE_LIBJP2K +#ifdef LIBJP2K_HEADER +#include LIBJP2K_HEADER +#else +#include +#endif +#endif + + +/*---------------------------------------------------------------------* + * Image Library Version number * + *---------------------------------------------------------------------*/ +/*! + * \brief getImagelibVersions() + * + *
+ * Notes:
+ *      (1) This returns a string of version numbers; e.g.,
+ *            libgif 5.0.3
+ *            libjpeg 8b (libjpeg-turbo 1.3.0)
+ *            libpng 1.4.3
+ *            libtiff 3.9.5
+ *            zlib 1.2.5
+ *            libwebp 0.3.0
+ *            libopenjp2 2.1.0
+ *      (2) The caller must free the memory.
+ * 
+ */ +char * +getImagelibVersions() +{ +char buf[128]; +l_int32 first = TRUE; +char *versionNumP; +char *nextTokenP; +char *versionStrP = NULL; + +#if HAVE_LIBGIF + first = FALSE; + stringJoinIP(&versionStrP, "libgif "); + #ifdef GIFLIB_MAJOR + snprintf(buf, sizeof(buf), "%d.%d.%d", GIFLIB_MAJOR, GIFLIB_MINOR, + GIFLIB_RELEASE); + #else + stringCopy(buf, "4.1.6(?)", sizeof(buf)); + #endif + stringJoinIP(&versionStrP, buf); +#endif /* HAVE_LIBGIF */ + +#if HAVE_LIBJPEG + { + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr err; + char buffer[JMSG_LENGTH_MAX]; + cinfo.err = jpeg_std_error(&err); + err.msg_code = JMSG_VERSION; + (*err.format_message) ((j_common_ptr ) &cinfo, buffer); + + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "libjpeg "); + versionNumP = strtokSafe(buffer, " ", &nextTokenP); + stringJoinIP(&versionStrP, versionNumP); + LEPT_FREE(versionNumP); + + #if defined(LIBJPEG_TURBO_VERSION) + /* To stringify the result of expansion of a macro argument, + * you must use two levels of macros. See: + * https://gcc.gnu.org/onlinedocs/cpp/Stringification.html */ + #define l_xstr(s) l_str(s) + #define l_str(s) #s + snprintf(buf, sizeof(buf), " (libjpeg-turbo %s)", + l_xstr(LIBJPEG_TURBO_VERSION)); + stringJoinIP(&versionStrP, buf); + #endif /* LIBJPEG_TURBO_VERSION */ + } +#endif /* HAVE_LIBJPEG */ + +#if HAVE_LIBPNG + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "libpng "); + stringJoinIP(&versionStrP, png_get_libpng_ver(NULL)); +#endif /* HAVE_LIBPNG */ + +#if HAVE_LIBTIFF + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "libtiff "); + versionNumP = strtokSafe((char *)TIFFGetVersion(), " \n", &nextTokenP); + LEPT_FREE(versionNumP); + versionNumP = strtokSafe(NULL, " \n", &nextTokenP); + LEPT_FREE(versionNumP); + versionNumP = strtokSafe(NULL, " \n", &nextTokenP); + stringJoinIP(&versionStrP, versionNumP); + LEPT_FREE(versionNumP); +#endif /* HAVE_LIBTIFF */ + +#if HAVE_LIBZ + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "zlib "); + stringJoinIP(&versionStrP, zlibVersion()); +#endif /* HAVE_LIBZ */ + +#if HAVE_LIBWEBP + { + l_int32 val; + char buf[32]; + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "libwebp "); + val = WebPGetEncoderVersion(); + snprintf(buf, sizeof(buf), "%d.%d.%d", val >> 16, (val >> 8) & 0xff, + val & 0xff); + stringJoinIP(&versionStrP, buf); + } +#endif /* HAVE_LIBWEBP */ + +#if HAVE_LIBJP2K + { + const char *version; + if (!first) stringJoinIP(&versionStrP, " : "); + first = FALSE; + stringJoinIP(&versionStrP, "libopenjp2 "); + version = opj_version(); + stringJoinIP(&versionStrP, version); + } +#endif /* HAVE_LIBJP2K */ + + return versionStrP; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/list.c b/hgdriver/3rdparty/hgOCR/leptonica/list.c new file mode 100644 index 0000000..87eb24e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/list.c @@ -0,0 +1,812 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file list.c + *
+ *
+ *      Inserting and removing elements
+ *
+ *           void      listDestroy()
+ *           DLLIST   *listAddToHead()
+ *           l_int32   listAddToTail()
+ *           l_int32   listInsertBefore()
+ *           l_int32   listInsertAfter()
+ *           void     *listRemoveElement()
+ *           void     *listRemoveFromHead()
+ *           void     *listRemoveFromTail()
+ *
+ *      Other list operations
+ *
+ *           DLLIST   *listFindElement()
+ *           DLLIST   *listFindTail()
+ *           l_int32   listGetCount()
+ *           l_int32   listReverse()
+ *           DLLIST   *listJoin()
+ *
+ *      Lists are much harder to handle than arrays.  There is
+ *      more overhead for the programmer, both cognitive and
+ *      codewise, and more likelihood that an error can be made.
+ *      For that reason, lists should only be used when it is
+ *      inefficient to use arrays, such as when elements are
+ *      routinely inserted or deleted from inside arrays whose
+ *      average size is greater than about 10.
+ *
+ *      A list of data structures can be implemented in a number
+ *      of ways.  The two most popular are:
+ *
+ *         (1) The list can be composed of a linked list of
+ *             pointer cells ("cons cells"), where the data structures
+ *             are hung off the cells.  This is more difficult
+ *             to use because you have to keep track of both
+ *             your hanging data and the cell structures.
+ *             It requires 3 pointers for every data structure
+ *             that is put in a list.  There is no problem
+ *             cloning (using reference counts) for structures that
+ *             are put in such a list.  We implement lists by this
+ *             method here.
+ *
+ *         (2) The list pointers can be inserted directly into
+ *             the data structures.  This is easy to implement
+ *             and easier to use, but it adds 2 ptrs of overhead
+ *             to every data structure in which the ptrs are embedded.
+ *             It also requires special care not to put the ptrs
+ *             in any data that is cloned with a reference count;
+ *             else your lists will break.
+ *
+ *      Writing C code that uses list pointers explicitly to make
+ *      and alter lists is difficult and prone to error.
+ *      Consequently, a generic list utility that handles lists
+ *      of arbitrary objects and doesn't force the programmer to
+ *      touch the "next" and "prev" pointers, is quite useful.
+ *      Such functions are provided here.   However, the usual
+ *      situation requires traversing a list and applying some
+ *      function to one or more of the list elements.  Macros
+ *      for traversing the list are, in general, necessary, to
+ *      achieve the goal of invisibly handling all "next" and "prev"
+ *      pointers in generic lists.  We provide macros for
+ *      traversing a list in both forward and reverse directions.
+ *
+ *      Because of the typing in C, implementation of a general
+ *      list utility requires casting.  If macros are used, the
+ *      casting can be done implicitly; otherwise, using functions,
+ *      some of the casts must be explicit.  Fortunately, this
+ *      can be implemented with void* so the programmer using
+ *      the library will not have to make any casts!  (Unless you
+ *      compile with g++, in which case the rules on implicit
+ *      conversion are more strict.)
+ *
+ *      For example, to add an arbitrary data structure foo to the
+ *      tail of a list, use
+ *             listAddToTail(&head, &tail, pfoo);
+ *      where head and tail are list cell ptrs and pfoo is
+ *      a pointer to the foo object.
+ *      And to remove an arbitrary data structure foo from a
+ *      list, when you know the list cell element it is hanging from,
+ *      use
+ *             pfoo = listRemoveElement(&head, elem)
+ *      where head and elem are list cell ptrs and pfoo is a pointer
+ *      to the foo object.  No casts are required for foo in
+ *      either direction in ANSI C.  (However, casts are
+ *      required for ANSI C++).
+ *
+ *      We use lists that are composed of doubly-linked
+ *      cells with data structures hanging off the cells.
+ *      We use doubly-linked cells to simplify insertion
+ *      and deletion, and to allow operations to proceed in either
+ *      direction along the list.  With doubly-linked lists,
+ *      it is tempting to make them circular, by setting head->prev
+ *      to the tail of the list and tail->next to the head.
+ *      The circular list costs nothing extra in storage, and
+ *      allows operations to proceed from either end of the list
+ *      with equal speed.  However, the circular link adds
+ *      cognitive overhead for the application programmer in
+ *      general, and it greatly complicates list traversal when
+ *      arbitrary list elements can be added or removed as you
+ *      move through.  It can be done, but in the spirit of
+ *      simplicity, we avoid the temptation.  The price to be paid
+ *      is the extra cost to find the tail of a list -- a full
+ *      traversal -- before the tail can be used.  This is a
+ *      cheap price to pay to avoid major headaches and buggy code.
+ *
+ *      When you are only applying some function to each element
+ *      in a list, you can go either forwards or backwards.
+ *      To run through a list forwards, use:
+ * \code
+ *          for (elem = head; elem; elem = nextelem) {
+ *              nextelem = elem->next;   (in case we destroy elem)
+ *              data>
+ *          }
+ * \endcode
+ *      To run through a list backwards, find the tail and use:
+ *
+ *          for (elem = tail; elem; elem = prevelem) {
+ #              prevelem = elem->prev;  (in case we destroy elem)
+ *              data>
+ *          }
+ *
+ *      Even though these patterns are very simple, they are so common
+ *      that we've provided macros for them in list.h.  Using the
+ *      macros, this becomes:
+ * \code
+ *          L_BEGIN_LIST_FORWARD(head, elem)
+ *              data>
+ *          L_END_LIST
+ *
+ *          L_BEGIN_LIST_REVERSE(tail, elem)
+ *              data>
+ *          L_END_LIST
+ * \endcode
+ *      Note again that with macros, the application programmer does
+ *      not need to refer explicitly to next and prev fields.  Also,
+ *      in the reverse case, note that we do not explicitly
+ *      show the head of the list.  However, the head of the list
+ *      is always in scope, and functions can be called within the
+ *      iterator that change the head.
+ *
+ *      Some special cases are simpler.  For example, when
+ *      removing all items from the head of the list, you can use
+ * \code
+ *          while (head) {
+ *              obj = listRemoveFromHead(&head);
+ *              
+ *          }
+ * \endcode
+ *      Removing successive elements from the tail is equally simple:
+ * \code
+ *          while (tail) {
+ *              obj = listRemoveFromTail(&head, &tail);
+ *              
+ *          }
+ * \endcode
+ *      When removing an arbitrary element from a list, use
+ * \code
+ *              obj = listRemoveElement(&head, elem);
+ * \endcode
+ *      All the listRemove*() functions hand you the object,
+ *      destroy the list cell to which it was attached, and
+ *      reset the list pointers if necessary.
+ *
+ *      Several other list operations, that do not involve
+ *      inserting or removing objects, are also provided.
+ *      The function listFindElement() locates a list pointer
+ *      by matching the object hanging on it to a given
+ *      object.  The function listFindTail() gets a handle
+ *      to the tail list ptr, allowing backwards traversals of
+ *      the list.  listGetCount() gives the number of elements
+ *      in a list.  Functions that reverse a list and concatenate
+ *      two lists are also provided.
+ *
+ *      These functions can be modified for efficiency in the
+ *      situation where there is a large amount of creation and
+ *      destruction of list cells.  If millions of cells are
+ *      made and destroyed, but a relatively small number are
+ *      around at any time, the list cells can be stored for
+ *      later re-use in a stack (see the generic stack functions
+ *      in stack.c).
+ * 
+ */ + +#include +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Inserting and removing elements * + *---------------------------------------------------------------------*/ +/*! + * \brief listDestroy() + * + * \param[in,out] phead head of list; will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) This only destroys the cons cells.  Before destroying
+ *          the list, it is necessary to remove all data and set the
+ *          data pointers in each cons cell to NULL.
+ *      (2) listDestroy() will give a warning message for each data
+ *          ptr that is not NULL.
+ * 
+ */ +void +listDestroy(DLLIST **phead) +{ +DLLIST *elem, *next, *head; + + PROCNAME("listDestroy"); + + if (phead == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((head = *phead) == NULL) + return; + + for (elem = head; elem; elem = next) { + if (elem->data) + L_WARNING("list data ptr is not null\n", procName); + next = elem->next; + LEPT_FREE(elem); + } + *phead = NULL; + return; +} + + +/*! + * \brief listAddToHead() + * + * \param[in,out] phead [optional] input head + * \param[in] data void* ptr, to be added + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This makes a new cell, attaches %data, and adds the
+ *          cell to the head of the list.
+ *      (2) When consing from NULL, be sure to initialize head to NULL
+ *          before calling this function.
+ * 
+ */ +l_ok +listAddToHead(DLLIST **phead, + void *data) +{ +DLLIST *cell, *head; + + PROCNAME("listAddToHead"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + head = *phead; + if (!data) + return ERROR_INT("data not defined", procName, 1); + + cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); + cell->data = data; + if (!head) { /* start the list; initialize the ptrs */ + cell->prev = NULL; + cell->next = NULL; + } else { + cell->prev = NULL; + cell->next = head; + head->prev = cell; + } + *phead = cell; + return 0; +} + + +/*! + * \brief listAddToTail() + * + * \param[in,out] phead [may be updated], can be NULL + * \param[in,out] ptail [updated], can be NULL + * \param[in] data void* ptr, to be hung on tail cons cell + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This makes a new cell, attaches %data, and adds the
+ *          cell to the tail of the list.
+ *      (2) &head is input to allow the list to be "cons'd" up from NULL.
+ *      (3) &tail is input to allow the tail to be updated
+ *          for efficient sequential operation with this function.
+ *      (4) We assume that if *phead and/or *ptail are not NULL,
+ *          then they are valid addresses.  Therefore:
+ *           (a) when consing from NULL, be sure to initialize both
+ *               head and tail to NULL.
+ *           (b) when tail == NULL for an existing list, the tail
+ *               will be found and updated.
+ * 
+ */ +l_ok +listAddToTail(DLLIST **phead, + DLLIST **ptail, + void *data) +{ +DLLIST *cell, *head, *tail; + + PROCNAME("listAddToTail"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + head = *phead; + if (!ptail) + return ERROR_INT("&tail not defined", procName, 1); + if (!data) + return ERROR_INT("data not defined", procName, 1); + + cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); + cell->data = data; + if (!head) { /* Start the list and initialize the ptrs. *ptail + * should also have been initialized to NULL */ + cell->prev = NULL; + cell->next = NULL; + *phead = cell; + *ptail = cell; + } else { + if ((tail = *ptail) == NULL) + tail = listFindTail(head); + cell->prev = tail; + cell->next = NULL; + tail->next = cell; + *ptail = cell; + } + + return 0; +} + + +/*! + * \brief listInsertBefore() + * + * \param[in,out] phead [optional] input head + * \param[in] elem list element to be inserted in front of; + * must be NULL if head is NULL + * \param[in] data void* address, to be added + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This can be called on a null list, in which case both
+ *          head and elem must be null.
+ *      (2) If you are searching through a list, looking for a condition
+ *          to add an element, you can do something like this:
+ * \code
+ *            L_BEGIN_LIST_FORWARD(head, elem)
+ *                
+ *                listInsertBefore(&head, elem, data);
+ *            L_END_LIST
+ * \endcode
+ * 
+ */ +l_ok +listInsertBefore(DLLIST **phead, + DLLIST *elem, + void *data) +{ +DLLIST *cell, *head; + + PROCNAME("listInsertBefore"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + head = *phead; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if ((!head && elem) || (head && !elem)) + return ERROR_INT("head and elem not consistent", procName, 1); + + /* New cell to insert */ + cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); + cell->data = data; + if (!head) { /* start the list; initialize the ptrs */ + cell->prev = NULL; + cell->next = NULL; + *phead = cell; + } else if (head == elem) { /* insert before head of list */ + cell->prev = NULL; + cell->next = head; + head->prev = cell; + *phead = cell; + } else { /* insert before elem and after head of list */ + cell->prev = elem->prev; + cell->next = elem; + elem->prev->next = cell; + elem->prev = cell; + } + return 0; +} + + +/*! + * \brief listInsertAfter() + * + * \param[in,out] phead [optional] input head + * \param[in] elem list element to be inserted after; + * must be NULL if head is NULL + * \param[in] data void* ptr, to be added + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This can be called on a null list, in which case both
+ *          head and elem must be null.  The head is included
+ *          in the call to allow "consing" up from NULL.
+ *      (2) If you are searching through a list, looking for a condition
+ *          to add an element, you can do something like this:
+ * \code
+ *            L_BEGIN_LIST_FORWARD(head, elem)
+ *                
+ *                listInsertAfter(&head, elem, data);
+ *            L_END_LIST
+ * \endcode
+ * 
+ */ +l_ok +listInsertAfter(DLLIST **phead, + DLLIST *elem, + void *data) +{ +DLLIST *cell, *head; + + PROCNAME("listInsertAfter"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + head = *phead; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if ((!head && elem) || (head && !elem)) + return ERROR_INT("head and elem not consistent", procName, 1); + + /* New cell to insert */ + cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); + cell->data = data; + if (!head) { /* start the list; initialize the ptrs */ + cell->prev = NULL; + cell->next = NULL; + *phead = cell; + } else if (elem->next == NULL) { /* insert after last */ + cell->prev = elem; + cell->next = NULL; + elem->next = cell; + } else { /* insert after elem and before the end */ + cell->prev = elem; + cell->next = elem->next; + elem->next->prev = cell; + elem->next = cell; + } + return 0; +} + + +/*! + * \brief listRemoveElement() + * + * \param[in,out] phead input head; can be changed + * \param[in] elem list element to be removed + * \return data void* struct on cell + * + *
+ * Notes:
+ *      (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
+ *             pix = listRemoveElement(&head, elem);
+ *          but in ANSI C++, it is necessary to do the cast:
+ *             pix = (Pix *)listRemoveElement(&head, elem);
+ * 
+ */ +void * +listRemoveElement(DLLIST **phead, + DLLIST *elem) +{ +void *data; +DLLIST *head; + + PROCNAME("listRemoveElement"); + + if (!phead) + return (void *)ERROR_PTR("&head not defined", procName, NULL); + head = *phead; + if (!head) + return (void *)ERROR_PTR("head not defined", procName, NULL); + if (!elem) + return (void *)ERROR_PTR("elem not defined", procName, NULL); + + data = elem->data; + + if (head->next == NULL) { /* only one */ + if (elem != head) + return (void *)ERROR_PTR("elem must be head", procName, NULL); + *phead = NULL; + } else if (head == elem) { /* first one */ + elem->next->prev = NULL; + *phead = elem->next; + } else if (elem->next == NULL) { /* last one */ + elem->prev->next = NULL; + } else { /* neither the first nor the last one */ + elem->next->prev = elem->prev; + elem->prev->next = elem->next; + } + + LEPT_FREE(elem); + return data; +} + + +/*! + * \brief listRemoveFromHead() + * + * \param[in,out] phead head of list; updated + * \return data void* struct on cell, or NULL on error + * + *
+ * Notes:
+ *      (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
+ *            pix = listRemoveFromHead(&head);
+ *          but in ANSI C++, it is necessary to do the cast; e.g.,
+ *            pix = (Pix *)listRemoveFromHead(&head);
+ * 
+ */ +void * +listRemoveFromHead(DLLIST **phead) +{ +DLLIST *head; +void *data; + + PROCNAME("listRemoveFromHead"); + + if (!phead) + return (void *)ERROR_PTR("&head not defined", procName, NULL); + if ((head = *phead) == NULL) + return (void *)ERROR_PTR("head not defined", procName, NULL); + + if (head->next == NULL) { /* only one */ + *phead = NULL; + } else { + head->next->prev = NULL; + *phead = head->next; + } + + data = head->data; + LEPT_FREE(head); + return data; +} + + +/*! + * \brief listRemoveFromTail() + * + * \param[in,out] phead list head must NOT be NULL; may be changed + * \param[in,out] ptail list tail may be NULL; always updated + * \return data void* struct on cell or NULL on error + * + *
+ * Notes:
+ *      (1) We include &head so that it can be set to NULL if
+ *          if the only element in the list is removed.
+ *      (2) The function is relying on the fact that if tail is
+ *          not NULL, then is is a valid address.  You can use
+ *          this function with tail == NULL for an existing list, in
+ *          which case  the tail is found and updated, and the
+ *          removed element is returned.
+ *      (3) In ANSI C, it is not necessary to cast return to actual type; e.g.,
+ *            pix = listRemoveFromTail(&head, &tail);
+ *          but in ANSI C++, it is necessary to do the cast; e.g.,
+ *            pix = (Pix *)listRemoveFromTail(&head, &tail);
+ * 
+ */ +void * +listRemoveFromTail(DLLIST **phead, + DLLIST **ptail) +{ +DLLIST *head, *tail; +void *data; + + PROCNAME("listRemoveFromTail"); + + if (!phead) + return (void *)ERROR_PTR("&head not defined", procName, NULL); + if ((head = *phead) == NULL) + return (void *)ERROR_PTR("head not defined", procName, NULL); + if (!ptail) + return (void *)ERROR_PTR("&tail not defined", procName, NULL); + if ((tail = *ptail) == NULL) + tail = listFindTail(head); + + if (head->next == NULL) { /* only one */ + *phead = NULL; + *ptail = NULL; + } else { + tail->prev->next = NULL; + *ptail = tail->prev; + } + + data = tail->data; + LEPT_FREE(tail); + return data; +} + + + +/*---------------------------------------------------------------------* + * Other list operations * + *---------------------------------------------------------------------*/ +/*! + * \brief listFindElement() + * + * \param[in] head list head + * \param[in] data void* address, to be searched for + * \return cell the containing cell, or NULL if not found or on error + * + *
+ * Notes:
+ *      (1) This returns a ptr to the cell, which is still embedded in
+ *          the list.
+ *      (2) This handle and the attached data have not been copied or
+ *          reference counted, so they must not be destroyed.  This
+ *          violates our basic rule that every handle returned from a
+ *          function is owned by that function and must be destroyed,
+ *          but if rules aren't there to be broken, why have them?
+ * 
+ */ +DLLIST * +listFindElement(DLLIST *head, + void *data) +{ +DLLIST *cell; + + PROCNAME("listFindElement"); + + if (!head) + return (DLLIST *)ERROR_PTR("head not defined", procName, NULL); + if (!data) + return (DLLIST *)ERROR_PTR("data not defined", procName, NULL); + + for (cell = head; cell; cell = cell->next) { + if (cell->data == data) + return cell; + } + + return NULL; +} + + +/*! + * \brief listFindTail() + * + * \param[in] head + * \return tail, or NULL on error + */ +DLLIST * +listFindTail(DLLIST *head) +{ +DLLIST *cell; + + PROCNAME("listFindTail"); + + if (!head) + return (DLLIST *)ERROR_PTR("head not defined", procName, NULL); + + for (cell = head; cell; cell = cell->next) { + if (cell->next == NULL) + return cell; + } + + return (DLLIST *)ERROR_PTR("tail not found !!", procName, NULL); +} + + +/*! + * \brief listGetCount() + * + * \param[in] head of list + * \return number of elements; 0 if no list or on error + */ +l_int32 +listGetCount(DLLIST *head) +{ +l_int32 count; +DLLIST *elem; + + PROCNAME("listGetCount"); + + if (!head) + return ERROR_INT("head not defined", procName, 0); + + count = 0; + for (elem = head; elem; elem = elem->next) + count++; + + return count; +} + + +/*! + * \brief listReverse() + * + * \param[in,out] phead list head; may be changed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This reverses the list in-place.
+ * 
+ */ +l_ok +listReverse(DLLIST **phead) +{ +void *obj; /* whatever */ +DLLIST *head, *rhead; + + PROCNAME("listReverse"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + if ((head = *phead) == NULL) + return ERROR_INT("head not defined", procName, 1); + + rhead = NULL; + while (head) { + obj = listRemoveFromHead(&head); + listAddToHead(&rhead, obj); + } + + *phead = rhead; + return 0; +} + + +/*! + * \brief listJoin() + * + * \param[in,out] phead1 head of first list; may be changed + * \param[in,out] phead2 head of second list; to be nulled + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The concatenated list is returned with head1 as the new head.
+ *      (2) Both input ptrs must exist, though either can have the value NULL.
+ * 
+ */ +l_ok +listJoin(DLLIST **phead1, + DLLIST **phead2) +{ +void *obj; +DLLIST *head1, *head2, *tail1; + + PROCNAME("listJoin"); + + if (!phead1) + return ERROR_INT("&head1 not defined", procName, 1); + if (!phead2) + return ERROR_INT("&head2 not defined", procName, 1); + + /* If no list2, just return list1 unchanged */ + if ((head2 = *phead2) == NULL) + return 0; + + /* If no list1, just return list2 */ + if ((head1 = *phead1) == NULL) { + *phead1 = head2; + *phead2 = NULL; + return 0; + } + + /* General case for concatenation into list 1 */ + tail1 = listFindTail(head1); + while (head2) { + obj = listRemoveFromHead(&head2); + listAddToTail(&head1, &tail1, obj); + } + *phead2 = NULL; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/list.h b/hgdriver/3rdparty/hgOCR/leptonica/list.h new file mode 100644 index 0000000..d207e79 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/list.h @@ -0,0 +1,90 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_LIST_H +#define LEPTONICA_LIST_H + +/*! + * \file list.h + * + *
+ *       Cell for double-linked lists
+ *
+ *       This allows composition of a list of cells with
+ *           prev, next and data pointers.  Generic data
+ *           structures hang on the list cell data pointers.
+ *
+ *       The list is not circular because that would add much
+ *           complexity in traversing the list under general
+ *           conditions where list cells can be added and removed.
+ *           The only disadvantage of not having the head point to
+ *           the last cell is that the list must be traversed to
+ *           find its tail.  However, this traversal is fast, and
+ *           the listRemoveFromTail() function updates the tail
+ *           so there is no searching overhead with repeated use.
+ *
+ *       The list macros are used to run through a list, and their
+ *       use is encouraged.  They are invoked, e.g., as
+ *
+ *             DLLIST  *head, *elem;
+ *             ...
+ *             L_BEGIN_LIST_FORWARD(head, elem)
+ *                 data >
+ *             L_END_LIST
+ * 
+ */ + +struct DoubleLinkedList +{ + struct DoubleLinkedList *prev; + struct DoubleLinkedList *next; + void *data; +}; +typedef struct DoubleLinkedList DLLIST; + + + /*! Simple list traverse macro - forward */ +#define L_BEGIN_LIST_FORWARD(head, element) \ + { \ + DLLIST *_leptvar_nextelem_; \ + for ((element) = (head); (element); (element) = _leptvar_nextelem_) { \ + _leptvar_nextelem_ = (element)->next; + + + /*! Simple list traverse macro - reverse */ +#define L_BEGIN_LIST_REVERSE(tail, element) \ + { \ + DLLIST *_leptvar_prevelem_; \ + for ((element) = (tail); (element); (element) = _leptvar_prevelem_) { \ + _leptvar_prevelem_ = (element)->prev; + + + /*! Simple list traverse macro - end of a list traverse */ +#define L_END_LIST }} + + +#endif /* LEPTONICA_LIST_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/map.c b/hgdriver/3rdparty/hgOCR/leptonica/map.c new file mode 100644 index 0000000..71b3923 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/map.c @@ -0,0 +1,260 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file map.c + *
+ *
+ *  This is an interface for map and set functions, based on using
+ *  red-black binary search trees.  Because these trees are sorted,
+ *  they are O(nlogn) to build.  They allow logn insertion, find
+ *  and deletion of elements.
+ *
+ *  Both the map and set are ordered by key value, with unique keys.
+ *  For the map, the elements are key/value pairs.
+ *  For the set we only store unique, ordered keys, and the value
+ *  (set to 0 in the implementation) is ignored.
+ *
+ *  The keys for the map and set can be any of the three types in the
+ *  l_rbtree_keytype enum.  The values stored can be any of the four
+ *  types in the rb_type union.
+ *
+ *  In-order forward and reverse iterators are provided for maps and sets.
+ *  To forward iterate over the map for any type of key (in this example,
+ *  uint32), extracting integer values:
+ *
+ *      L_AMAP  *m = l_amapCreate(L_UINT_TYPE);
+ *      [add elements to the map ...]
+ *      L_AMAP_NODE  *n = l_amapGetFirst(m);
+ *      while (n) {
+ *          l_int32 val = n->value.itype;
+ *          // do something ...
+ *          n = l_amapGetNext(n);
+ *      }
+ *
+ *  If the nodes are deleted during the iteration:
+ *
+ *      L_AMAP  *m = l_amapCreate(L_UINT_TYPE);
+ *      [add elements to the map ...]
+ *      L_AMAP_NODE  *n = l_amapGetFirst(m);
+ *      L_AMAP_NODE  *nn;
+ *      while (n) {
+ *          nn = l_amapGetNext(n);
+ *          l_int32 val = n->value.itype;
+ *          l_uint32 key = n->key.utype;
+ *          // do something ...
+ *          l_amapDelete(m, n->key);
+ *          n = nn;
+ *      }
+ *
+ *  See prog/maptest.c and prog/settest.c for more examples of usage.
+ *
+ *  Interface to (a) map using a general key and storing general values
+ *           L_AMAP        *l_amapCreate()
+ *           RB_TYPE       *l_amapFind()
+ *           void           l_amapInsert()
+ *           void           l_amapDelete()
+ *           void           l_amapDestroy()
+ *           L_AMAP_NODE   *l_amapGetFirst()
+ *           L_AMAP_NODE   *l_amapGetNext()
+ *           L_AMAP_NODE   *l_amapGetLast()
+ *           L_AMAP_NODE   *l_amapGetPrev()
+ *           l_int32        l_amapSize()
+ *
+ *  Interface to (a) set using a general key
+ *           L_ASET        *l_asetCreate()
+ *           RB_TYPE       *l_asetFind()
+ *           void           l_asetInsert()
+ *           void           l_asetDelete()
+ *           void           l_asetDestroy()
+ *           L_ASET_NODE   *l_asetGetFirst()
+ *           L_ASET_NODE   *l_asetGetNext()
+ *           L_ASET_NODE   *l_asetGetLast()
+ *           L_ASET_NODE   *l_asetGetPrev()
+ *           l_int32        l_asetSize()
+ * 
+ */ + +#include "allheaders.h" + +/* ------------------------------------------------------------- * + * Interface to Map * + * ------------------------------------------------------------- */ +L_AMAP * +l_amapCreate(l_int32 keytype) +{ + PROCNAME("l_amapCreate"); + + if (keytype != L_INT_TYPE && keytype != L_UINT_TYPE && + keytype != L_FLOAT_TYPE) + return (L_AMAP *)ERROR_PTR("invalid keytype", procName, NULL); + + L_AMAP *m = (L_AMAP *)LEPT_CALLOC(1, sizeof(L_AMAP)); + m->keytype = keytype; + return m; +} + +RB_TYPE * +l_amapFind(L_AMAP *m, + RB_TYPE key) +{ + return l_rbtreeLookup(m, key); +} + +void +l_amapInsert(L_AMAP *m, + RB_TYPE key, + RB_TYPE value) +{ + return l_rbtreeInsert(m, key, value); +} + +void +l_amapDelete(L_AMAP *m, + RB_TYPE key) +{ + l_rbtreeDelete(m, key); +} + +void +l_amapDestroy(L_AMAP **pm) +{ + l_rbtreeDestroy(pm); +} + +L_AMAP_NODE * +l_amapGetFirst(L_AMAP *m) +{ + return l_rbtreeGetFirst(m); +} + +L_AMAP_NODE * +l_amapGetNext(L_AMAP_NODE *n) +{ + return l_rbtreeGetNext(n); +} + +L_AMAP_NODE * +l_amapGetLast(L_AMAP *m) +{ + return l_rbtreeGetLast(m); +} + +L_AMAP_NODE * +l_amapGetPrev(L_AMAP_NODE *n) +{ + return l_rbtreeGetPrev(n); +} + +l_int32 +l_amapSize(L_AMAP *m) +{ + return l_rbtreeGetCount(m); +} + + +/* ------------------------------------------------------------- * + * Interface to Set * + * ------------------------------------------------------------- */ +L_ASET * +l_asetCreate(l_int32 keytype) +{ + PROCNAME("l_asetCreate"); + + if (keytype != L_INT_TYPE && keytype != L_UINT_TYPE && + keytype != L_FLOAT_TYPE) + return (L_ASET *)ERROR_PTR("invalid keytype", procName, NULL); + + L_ASET *s = (L_ASET *)LEPT_CALLOC(1, sizeof(L_ASET)); + s->keytype = keytype; + return s; +} + +/* + * l_asetFind() + * + * This returns NULL if not found, non-null if it is. In the latter + * case, the value stored in the returned pointer has no significance. + */ +RB_TYPE * +l_asetFind(L_ASET *s, + RB_TYPE key) +{ + return l_rbtreeLookup(s, key); +} + +void +l_asetInsert(L_ASET *s, + RB_TYPE key) +{ +RB_TYPE value; + + value.itype = 0; /* meaningless */ + return l_rbtreeInsert(s, key, value); +} + +void +l_asetDelete(L_ASET *s, + RB_TYPE key) +{ + l_rbtreeDelete(s, key); +} + +void +l_asetDestroy(L_ASET **ps) +{ + l_rbtreeDestroy(ps); +} + +L_ASET_NODE * +l_asetGetFirst(L_ASET *s) +{ + return l_rbtreeGetFirst(s); +} + +L_ASET_NODE * +l_asetGetNext(L_ASET_NODE *n) +{ + return l_rbtreeGetNext(n); +} + +L_ASET_NODE * +l_asetGetLast(L_ASET *s) +{ + return l_rbtreeGetLast(s); +} + +L_ASET_NODE * +l_asetGetPrev(L_ASET_NODE *n) +{ + return l_rbtreeGetPrev(n); +} + +l_int32 +l_asetSize(L_ASET *s) +{ + return l_rbtreeGetCount(s); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/maze.c b/hgdriver/3rdparty/hgOCR/leptonica/maze.c new file mode 100644 index 0000000..20bd60b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/maze.c @@ -0,0 +1,906 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file maze.c + *
+ *
+ *      This is a game with a pedagogical slant.  A maze is represented
+ *      by a binary image.  The ON pixels (fg) are walls.  The goal is
+ *      to navigate on OFF pixels (bg), using Manhattan steps
+ *      (N, S, E, W), between arbitrary start and end positions.
+ *      The problem is thus to find the shortest route between two points
+ *      in a binary image that are 4-connected in the bg.  This is done
+ *      with a breadth-first search, implemented with a queue.
+ *      We also use a queue of pointers to generate the maze (image).
+ *
+ *          PIX             *generateBinaryMaze()
+ *          static MAZEEL   *mazeelCreate()
+ *
+ *          PIX             *pixSearchBinaryMaze()
+ *          static l_int32   localSearchForBackground()
+ *
+ *      Generalizing a maze to a grayscale image, the search is
+ *      now for the "shortest" or least cost path, for some given
+ *      cost function.
+ *
+ *          PIX             *pixSearchGrayMaze()
+ * 
+ */ + +#include +#ifdef _WIN32 +#include +#include +#endif /* _WIN32 */ +#include "allheaders.h" + +static const l_int32 MinMazeWidth = 50; +static const l_int32 MinMazeHeight = 50; + +static const l_float32 DefaultWallProbability = 0.65; +static const l_float32 DefaultAnisotropyRatio = 0.25; + +enum { /* direction from parent to newly created element */ + START_LOC = 0, + DIR_NORTH = 1, + DIR_SOUTH = 2, + DIR_WEST = 3, + DIR_EAST = 4 +}; + +struct MazeElement { + l_float32 distance; + l_int32 x; + l_int32 y; + l_uint32 val; /* value of maze pixel at this location */ + l_int32 dir; /* direction from parent to child */ +}; +typedef struct MazeElement MAZEEL; + + +static MAZEEL *mazeelCreate(l_int32 x, l_int32 y, l_int32 dir); +static l_int32 localSearchForBackground(PIX *pix, l_int32 *px, + l_int32 *py, l_int32 maxrad); + +#ifndef NO_CONSOLE_IO +#define DEBUG_PATH 0 +#define DEBUG_MAZE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * Binary maze generation as cellular automaton * + *---------------------------------------------------------------------*/ +/*! + * \brief generateBinaryMaze() + * + * \param[in] w, h size of maze + * \param[in] xi, yi initial location + * \param[in] wallps probability that a pixel to the side is ON + * \param[in] ranis ratio of prob that pixel in forward direction + * is a wall to the probability that pixel in + * side directions is a wall + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) We have two input probability factors that determine the
+ *          density of walls and average length of straight passages.
+ *          When ranis < 1.0, you are more likely to generate a wall
+ *          to the side than going forward.  Enter 0.0 for either if
+ *          you want to use the default values.
+ *      (2) This is a type of percolation problem, and exhibits
+ *          different phases for different parameters wallps and ranis.
+ *          For larger values of these parameters, regions in the maze
+ *          are not explored because the maze generator walls them
+ *          off and cannot get through.  The boundary between the
+ *          two phases in this two-dimensional parameter space goes
+ *          near these values:
+ *                wallps       ranis
+ *                0.35         1.00
+ *                0.40         0.85
+ *                0.45         0.70
+ *                0.50         0.50
+ *                0.55         0.40
+ *                0.60         0.30
+ *                0.65         0.25
+ *                0.70         0.19
+ *                0.75         0.15
+ *                0.80         0.11
+ *      (3) Because there is a considerable amount of overhead in calling
+ *          pixGetPixel() and pixSetPixel(), this function can be sped
+ *          up with little effort using raster line pointers and the
+ *          GET_DATA* and SET_DATA* macros.
+ * 
+ */ +PIX * +generateBinaryMaze(l_int32 w, + l_int32 h, + l_int32 xi, + l_int32 yi, + l_float32 wallps, + l_float32 ranis) +{ +l_int32 x, y, dir; +l_uint32 val; +l_float32 frand, wallpf, testp; +MAZEEL *el, *elp; +PIX *pixd; /* the destination maze */ +PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ +L_QUEUE *lq; + + /* On Windows, seeding is apparently necessary to get decent mazes. + * Windows rand() returns a value up to 2^15 - 1, whereas unix + * rand() returns a value up to 2^31 - 1. Therefore the generated + * mazes will differ on the two platforms. */ +#ifdef _WIN32 + srand(28*333); +#endif /* _WIN32 */ + + if (w < MinMazeWidth) + w = MinMazeWidth; + if (h < MinMazeHeight) + h = MinMazeHeight; + if (xi <= 0 || xi >= w) + xi = w / 6; + if (yi <= 0 || yi >= h) + yi = h / 5; + if (wallps < 0.05 || wallps > 0.95) + wallps = DefaultWallProbability; + if (ranis < 0.05 || ranis > 1.0) + ranis = DefaultAnisotropyRatio; + wallpf = wallps * ranis; + +#if DEBUG_MAZE + fprintf(stderr, "(w, h) = (%d, %d), (xi, yi) = (%d, %d)\n", w, h, xi, yi); + fprintf(stderr, "Using: prob(wall) = %7.4f, anisotropy factor = %7.4f\n", + wallps, ranis); +#endif /* DEBUG_MAZE */ + + /* These are initialized to OFF */ + pixd = pixCreate(w, h, 1); + pixm = pixCreate(w, h, 1); + + lq = lqueueCreate(0); + + /* Prime the queue with the first pixel; it is OFF */ + el = mazeelCreate(xi, yi, START_LOC); + pixSetPixel(pixm, xi, yi, 1); /* mark visited */ + lqueueAdd(lq, el); + + /* While we're at it ... */ + while (lqueueGetCount(lq) > 0) { + elp = (MAZEEL *)lqueueRemove(lq); + x = elp->x; + y = elp->y; + dir = elp->dir; + if (x > 0) { /* check west */ + pixGetPixel(pixm, x - 1, y, &val); + if (val == 0) { /* not yet visited */ + pixSetPixel(pixm, x - 1, y, 1); /* mark visited */ + frand = (l_float32)rand() / (l_float32)RAND_MAX; + testp = wallps; + if (dir == DIR_WEST) + testp = wallpf; + if (frand <= testp) { /* make it a wall */ + pixSetPixel(pixd, x - 1, y, 1); + } else { /* not a wall */ + el = mazeelCreate(x - 1, y, DIR_WEST); + lqueueAdd(lq, el); + } + } + } + if (y > 0) { /* check north */ + pixGetPixel(pixm, x, y - 1, &val); + if (val == 0) { /* not yet visited */ + pixSetPixel(pixm, x, y - 1, 1); /* mark visited */ + frand = (l_float32)rand() / (l_float32)RAND_MAX; + testp = wallps; + if (dir == DIR_NORTH) + testp = wallpf; + if (frand <= testp) { /* make it a wall */ + pixSetPixel(pixd, x, y - 1, 1); + } else { /* not a wall */ + el = mazeelCreate(x, y - 1, DIR_NORTH); + lqueueAdd(lq, el); + } + } + } + if (x < w - 1) { /* check east */ + pixGetPixel(pixm, x + 1, y, &val); + if (val == 0) { /* not yet visited */ + pixSetPixel(pixm, x + 1, y, 1); /* mark visited */ + frand = (l_float32)rand() / (l_float32)RAND_MAX; + testp = wallps; + if (dir == DIR_EAST) + testp = wallpf; + if (frand <= testp) { /* make it a wall */ + pixSetPixel(pixd, x + 1, y, 1); + } else { /* not a wall */ + el = mazeelCreate(x + 1, y, DIR_EAST); + lqueueAdd(lq, el); + } + } + } + if (y < h - 1) { /* check south */ + pixGetPixel(pixm, x, y + 1, &val); + if (val == 0) { /* not yet visited */ + pixSetPixel(pixm, x, y + 1, 1); /* mark visited */ + frand = (l_float32)rand() / (l_float32)RAND_MAX; + testp = wallps; + if (dir == DIR_SOUTH) + testp = wallpf; + if (frand <= testp) { /* make it a wall */ + pixSetPixel(pixd, x, y + 1, 1); + } else { /* not a wall */ + el = mazeelCreate(x, y + 1, DIR_SOUTH); + lqueueAdd(lq, el); + } + } + } + LEPT_FREE(elp); + } + + lqueueDestroy(&lq, TRUE); + pixDestroy(&pixm); + return pixd; +} + + +static MAZEEL * +mazeelCreate(l_int32 x, + l_int32 y, + l_int32 dir) +{ +MAZEEL *el; + + el = (MAZEEL *)LEPT_CALLOC(1, sizeof(MAZEEL)); + el->x = x; + el->y = y; + el->dir = dir; + return el; +} + + +/*---------------------------------------------------------------------* + * Binary maze search * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSearchBinaryMaze() + * + * \param[in] pixs 1 bpp, maze + * \param[in] xi, yi beginning point; use same initial point + * that was used to generate the maze + * \param[in] xf, yf end point, or close to it + * \param[out] ppixd [optional] maze with path illustrated, or + * if no path possible, the part of the maze + * that was searched + * \return pta shortest path, or NULL if either no path + * exists or on error + * + *
+ * Notes:
+ *      (1) Because of the overhead in calling pixGetPixel() and
+ *          pixSetPixel(), we have used raster line pointers and the
+ *          GET_DATA* and SET_DATA* macros for many of the pix accesses.
+ *      (2) Commentary:
+ *            The goal is to find the shortest path between beginning and
+ *          end points, without going through walls, and there are many
+ *          ways to solve this problem.
+ *            We use a queue to implement a breadth-first search.  Two auxiliary
+ *          "image" data structures can be used: one to mark the visited
+ *          pixels and one to give the direction to the parent for each
+ *          visited pixel.  The first structure is used to avoid putting
+ *          pixels on the queue more than once, and the second is used
+ *          for retracing back to the origin, like the breadcrumbs in
+ *          Hansel and Gretel.  Each pixel taken off the queue is destroyed
+ *          after it is used to locate the allowed neighbors.  In fact,
+ *          only one distance image is required, if you initialize it
+ *          to some value that signifies "not yet visited."  (We use
+ *          a binary image for marking visited pixels because it is clearer.)
+ *          This method for a simple search of a binary maze is implemented in
+ *          pixSearchBinaryMaze().
+ *            An alternative method would store the (manhattan) distance
+ *          from the start point with each pixel on the queue.  The children
+ *          of each pixel get a distance one larger than the parent.  These
+ *          values can be stored in an auxiliary distance map image
+ *          that is constructed simultaneously with the search.  Once the
+ *          end point is reached, the distance map is used to backtrack
+ *          along a minimum path.  There may be several equal length
+ *          minimum paths, any one of which can be chosen this way.
+ * 
+ */ +PTA * +pixSearchBinaryMaze(PIX *pixs, + l_int32 xi, + l_int32 yi, + l_int32 xf, + l_int32 yf, + PIX **ppixd) +{ +l_int32 i, j, x, y, w, h, d, found; +l_uint32 val, rpixel, gpixel, bpixel; +void **lines1, **linem1, **linep8, **lined32; +MAZEEL *el, *elp; +PIX *pixd; /* the shortest path written on the maze image */ +PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ +PIX *pixp; /* for bookkeeping, to indicate direction to parent */ +L_QUEUE *lq; +PTA *pta; + + PROCNAME("pixSearchBinaryMaze"); + + if (ppixd) *ppixd = NULL; + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (xi <= 0 || xi >= w) + return (PTA *)ERROR_PTR("xi not valid", procName, NULL); + if (yi <= 0 || yi >= h) + return (PTA *)ERROR_PTR("yi not valid", procName, NULL); + pixGetPixel(pixs, xi, yi, &val); + if (val != 0) + return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL); + pixd = NULL; + pta = NULL; + + /* Find a bg pixel near input point (xf, yf) */ + localSearchForBackground(pixs, &xf, &yf, 5); + +#if DEBUG_MAZE + fprintf(stderr, "(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n", + xi, yi, xf, yf); +#endif /* DEBUG_MAZE */ + + pixm = pixCreate(w, h, 1); /* initialized to OFF */ + pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ + lines1 = pixGetLinePtrs(pixs, NULL); + linem1 = pixGetLinePtrs(pixm, NULL); + linep8 = pixGetLinePtrs(pixp, NULL); + + lq = lqueueCreate(0); + + /* Prime the queue with the first pixel; it is OFF */ + el = mazeelCreate(xi, yi, 0); /* don't need direction here */ + pixSetPixel(pixm, xi, yi, 1); /* mark visited */ + lqueueAdd(lq, el); + + /* Fill up the pix storing directions to parents, + * stopping when we hit the point (xf, yf) */ + found = FALSE; + while (lqueueGetCount(lq) > 0) { + elp = (MAZEEL *)lqueueRemove(lq); + x = elp->x; + y = elp->y; + if (x == xf && y == yf) { + found = TRUE; + LEPT_FREE(elp); + break; + } + + if (x > 0) { /* check to west */ + val = GET_DATA_BIT(linem1[y], x - 1); + if (val == 0) { /* not yet visited */ + SET_DATA_BIT(linem1[y], x - 1); /* mark visited */ + val = GET_DATA_BIT(lines1[y], x - 1); + if (val == 0) { /* bg, not a wall */ + SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent E */ + el = mazeelCreate(x - 1, y, 0); + lqueueAdd(lq, el); + } + } + } + if (y > 0) { /* check north */ + val = GET_DATA_BIT(linem1[y - 1], x); + if (val == 0) { /* not yet visited */ + SET_DATA_BIT(linem1[y - 1], x); /* mark visited */ + val = GET_DATA_BIT(lines1[y - 1], x); + if (val == 0) { /* bg, not a wall */ + SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent S */ + el = mazeelCreate(x, y - 1, 0); + lqueueAdd(lq, el); + } + } + } + if (x < w - 1) { /* check east */ + val = GET_DATA_BIT(linem1[y], x + 1); + if (val == 0) { /* not yet visited */ + SET_DATA_BIT(linem1[y], x + 1); /* mark visited */ + val = GET_DATA_BIT(lines1[y], x + 1); + if (val == 0) { /* bg, not a wall */ + SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent W */ + el = mazeelCreate(x + 1, y, 0); + lqueueAdd(lq, el); + } + } + } + if (y < h - 1) { /* check south */ + val = GET_DATA_BIT(linem1[y + 1], x); + if (val == 0) { /* not yet visited */ + SET_DATA_BIT(linem1[y + 1], x); /* mark visited */ + val = GET_DATA_BIT(lines1[y + 1], x); + if (val == 0) { /* bg, not a wall */ + SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent N */ + el = mazeelCreate(x, y + 1, 0); + lqueueAdd(lq, el); + } + } + } + LEPT_FREE(elp); + } + + lqueueDestroy(&lq, TRUE); + pixDestroy(&pixm); + LEPT_FREE(linem1); + + if (ppixd) { + pixd = pixUnpackBinary(pixs, 32, 1); + *ppixd = pixd; + } + composeRGBPixel(255, 0, 0, &rpixel); /* start point */ + composeRGBPixel(0, 255, 0, &gpixel); + composeRGBPixel(0, 0, 255, &bpixel); /* end point */ + + if (found) { + L_INFO(" Path found\n", procName); + pta = ptaCreate(0); + x = xf; + y = yf; + while (1) { + ptaAddPt(pta, x, y); + if (x == xi && y == yi) + break; + if (pixd) /* write 'gpixel' onto the path */ + pixSetPixel(pixd, x, y, gpixel); + pixGetPixel(pixp, x, y, &val); + if (val == DIR_NORTH) + y--; + else if (val == DIR_SOUTH) + y++; + else if (val == DIR_EAST) + x++; + else if (val == DIR_WEST) + x--; + } + } else { + L_INFO(" No path found\n", procName); + if (pixd) { /* paint all visited locations */ + lined32 = pixGetLinePtrs(pixd, NULL); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (GET_DATA_BYTE(linep8[i], j) != 0) + SET_DATA_FOUR_BYTES(lined32[i], j, gpixel); + } + } + LEPT_FREE(lined32); + } + } + if (pixd) { + pixSetPixel(pixd, xi, yi, rpixel); + pixSetPixel(pixd, xf, yf, bpixel); + } + + pixDestroy(&pixp); + LEPT_FREE(lines1); + LEPT_FREE(linep8); + return pta; +} + + +/*! + * \brief localSearchForBackground() + * + * \param[in] pix + * \param[out] px, py starting position for search; return found position + * \param[in] maxrad max distance to search from starting location + * \return 0 if bg pixel found; 1 if not found + */ +static l_int32 +localSearchForBackground(PIX *pix, + l_int32 *px, + l_int32 *py, + l_int32 maxrad) +{ +l_int32 x, y, w, h, r, i, j; +l_uint32 val; + + x = *px; + y = *py; + pixGetPixel(pix, x, y, &val); + if (val == 0) return 0; + + /* For each value of r, restrict the search to the boundary + * pixels in a square centered on (x,y), clipping to the + * image boundaries if necessary. */ + pixGetDimensions(pix, &w, &h, NULL); + for (r = 1; r < maxrad; r++) { + for (i = -r; i <= r; i++) { + if (y + i < 0 || y + i >= h) + continue; + for (j = -r; j <= r; j++) { + if (x + j < 0 || x + j >= w) + continue; + if (L_ABS(i) != r && L_ABS(j) != r) /* not on "r ring" */ + continue; + pixGetPixel(pix, x + j, y + i, &val); + if (val == 0) { + *px = x + j; + *py = y + i; + return 0; + } + } + } + } + return 1; +} + + + +/*---------------------------------------------------------------------* + * Gray maze search * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSearchGrayMaze() + * + * \param[in] pixs 1 bpp, maze + * \param[in] xi, yi beginning point; use same initial point + * that was used to generate the maze + * \param[in] xf, yf end point, or close to it + * \param[out] ppixd [optional] maze with path illustrated, or + * if no path possible, the part of the maze + * that was searched + * \return pta shortest path, or NULL if either no path + * exists or on error + * + * Commentary: + * Consider first a slight generalization of the binary maze + * search problem. Suppose that you can go through walls, + * but the cost is higher say, an increment of 3 to go into + * a wall pixel rather than 1? You're still trying to find + * the shortest path. One way to do this is with an ordered + * queue, and a simple way to visualize an ordered queue is as + * a set of stacks, each stack being marked with the distance + * of each pixel in the stack from the start. We place the + * start pixel in stack 0, pop it, and process its 4 children. + * Each pixel is given a distance that is incremented from that + * of its parent 0 in this case, depending on if it is a wall + * pixel or not. That value may be recorded on a distance map, + * according to the algorithm below. For children of the first + * pixel, those not on a wall go in stack 1, and wall + * children go in stack 3. Stack 0 being emptied, the process + * then continues with pixels being popped from stack 1. + * Here is the algorithm for each child pixel. The pixel's + * distance value, were it to be placed on a stack, is compared + * with the value for it that is on the distance map. There + * are three possible cases: + * 1 If the pixel has not yet been registered, it is pushed + * on its stack and the distance is written to the map. + * 2 If it has previously been registered with a higher distance, + * the distance on the map is relaxed to that of the + * current pixel, which is then placed on its stack. + * 3 If it has previously been registered with an equal + * or lower value, the pixel is discarded. + * The pixels are popped and processed successively from + * stack 1, and when stack 1 is empty, popping starts on stack 2. + * This continues until the destination pixel is popped off + * a stack. The minimum path is then derived from the distance map, + * going back from the end point as before. This is just Dijkstra's + * algorithm for a directed graph; here, the underlying graph + * consisting of the pixels and four edges connecting each pixel + * to its 4-neighbor is a special case of a directed graph, where + * each edge is bi-directional. The implementation of this generalized + * maze search is left as an exercise to the reader. + * + * Let's generalize a bit further. Suppose the "maze" is just + * a grayscale image -- think of it as an elevation map. The cost + * of moving on this surface depends on the height, or the gradient, + * or whatever you want. All that is required is that the cost + * is specified and non-negative on each link between adjacent + * pixels. Now the problem becomes: find the least cost path + * moving on this surface between two specified end points. + * For example, if the cost across an edge between two pixels + * depends on the "gradient", you can use: + * cost = 1 + L_ABSdeltaV + * where deltaV is the difference in value between two adjacent + * pixels. If the costs are all integers, we can still use an array + * of stacks to avoid ordering the queue e.g., by using a heap sort. + * This is a neat problem, because you don't even have to build a + * maze -- you can can use it on any grayscale image! + * + * Rather than using an array of stacks, a more practical + * approach is to implement with a priority queue, which is + * a queue that is sorted so that the elements with the largest + * or smallest key values always come off first. The + * priority queue is efficiently implemented as a heap, and + * this is how we do it. Suppose you run the algorithm + * using a priority queue, doing the bookkeeping with an + * auxiliary image data structure that saves the distance of + * each pixel put on the queue as before, according to the method + * described above. We implement it as a 2-way choice by + * initializing the distance array to a large value and putting + * a pixel on the queue if its distance is less than the value + * found on the array. When you finally pop the end pixel from + * the queue, you're done, and you can trace the path backward, + * either always going downhill or using an auxiliary image to + * give you the direction to go at each step. This is implemented + * here in searchGrayMaze. + * + * Do we really have to use a sorted queue? Can we solve this + * generalized maze with an unsorted queue of pixels? Or even + * an unsorted stack, doing a depth-first search (DFS)? + * Consider a different algorithm for this generalized maze, where + * we travel again breadth first, but this time use a single, + * unsorted queue. An auxiliary image is used as before to + * store the distances and to determine if pixels get pushed + * on the stack or dropped. As before, we must allow pixels + * to be revisited, with relaxation of the distance if a shorter + * path arrives later. As a result, we will in general have + * multiple instances of the same pixel on the stack with different + * distances. However, because the queue is not ordered, some of + * these pixels will be popped when another instance with a lower + * distance is still on the stack. Here, we're just popping them + * in the order they go on, rather than setting up a priority + * based on minimum distance. Thus, unlike the priority queue, + * when a pixel is popped we have to check the distance map to + * see if a pixel with a lower distance has been put on the queue, + * and, if so, we discard the pixel we just popped. So the + * "while" loop looks like this: + * ~ pop a pixel from the queue + * ~ check its distance against the distance stored in the + * distance map; if larger, discard + * ~ otherwise, for each of its neighbors: + * ~ compute its distance from the start pixel + * ~ compare this distance with that on the distance map: + * ~ if the distance map value higher, relax the distance + * and push the pixel on the queue + * ~ if the distance map value is lower, discard the pixel + * + * How does this loop terminate? Before, with an ordered queue, + * it terminates when you pop the end pixel. But with an unordered + * queue or stack, the first time you hit the end pixel, the + * distance is not guaranteed to be correct, because the pixels + * along the shortest path may not have yet been visited and relaxed. + * Because the shortest path can theoretically go anywhere, + * we must keep going. How do we know when to stop? Dijkstra + * uses an ordered queue to systematically remove nodes from + * further consideration. Each time a pixel is popped, we're + * done with it; it's "finalized" in the Dijkstra sense because + * we know the shortest path to it. However, with an unordered + * queue, the brute force answer is: stop when the queue + * or stack is empty, because then every pixel in the image + * has been assigned its minimum "distance" from the start pixel. + * + * This is similar to the situation when you use a stack for the + * simpler uniform-step problem: with breadth-first search BFS + * the pixels on the queue are automatically ordered, so you are + * done when you locate the end pixel as a neighbor of a popped pixel; + * whereas depth-first search DFS, using a stack, requires, + * in general, a search of every accessible pixel. Further, if + * a pixel is revisited with a smaller distance, that distance is + * recorded and the pixel is put on the stack again. + * + * But surely, you ask, can't we stop sooner? What if the + * start and end pixels are very close to each other? + * OK, suppose they are, and you have very high walls and a + * long snaking level path that is actually the minimum cost. + * That long path can wind back and forth across the entire + * maze many times before ending up at the end point, which + * could be just over a wall from the start. With the unordered + * queue, you very quickly get a high distance for the end + * pixel, which will be relaxed to the minimum distance only + * after all the pixels of the path have been visited and placed + * on the queue, multiple times for many of them. So that's the + * price for not ordering the queue! + */ +PTA * +pixSearchGrayMaze(PIX *pixs, + l_int32 xi, + l_int32 yi, + l_int32 xf, + l_int32 yf, + PIX **ppixd) +{ +l_int32 x, y, w, h, d; +l_uint32 val, valr, vals, rpixel, gpixel, bpixel; +void **lines8, **liner32, **linep8; +l_int32 cost, dist, distparent, sival, sivals; +MAZEEL *el, *elp; +PIX *pixd; /* optionally plot the path on this RGB version of pixs */ +PIX *pixr; /* for bookkeeping, to indicate the minimum distance */ + /* to pixels already visited */ +PIX *pixp; /* for bookkeeping, to indicate direction to parent */ +L_HEAP *lh; +PTA *pta; + + PROCNAME("pixSearchGrayMaze"); + + if (ppixd) *ppixd = NULL; + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PTA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (xi <= 0 || xi >= w) + return (PTA *)ERROR_PTR("xi not valid", procName, NULL); + if (yi <= 0 || yi >= h) + return (PTA *)ERROR_PTR("yi not valid", procName, NULL); + pixd = NULL; + pta = NULL; + + /* Allocate stuff */ + pixr = pixCreate(w, h, 32); + pixSetAll(pixr); /* initialize to max value */ + pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ + lines8 = pixGetLinePtrs(pixs, NULL); + linep8 = pixGetLinePtrs(pixp, NULL); + liner32 = pixGetLinePtrs(pixr, NULL); + lh = lheapCreate(0, L_SORT_INCREASING); /* always remove closest pixels */ + + /* Prime the heap with the first pixel */ + pixGetPixel(pixs, xi, yi, &val); + el = mazeelCreate(xi, yi, 0); /* don't need direction here */ + el->distance = 0; + pixGetPixel(pixs, xi, yi, &val); + el->val = val; + pixSetPixel(pixr, xi, yi, 0); /* distance is 0 */ + lheapAdd(lh, el); + + /* Breadth-first search with priority queue (implemented by + a heap), labeling direction to parents in pixp and minimum + distance to visited pixels in pixr. Stop when we pull + the destination point (xf, yf) off the queue. */ + while (lheapGetCount(lh) > 0) { + elp = (MAZEEL *)lheapRemove(lh); + if (!elp) { + L_ERROR("heap broken!!\n", procName); + goto cleanup_stuff; + } + x = elp->x; + y = elp->y; + if (x == xf && y == yf) { /* exit condition */ + LEPT_FREE(elp); + break; + } + distparent = (l_int32)elp->distance; + val = elp->val; + sival = val; + + if (x > 0) { /* check to west */ + vals = GET_DATA_BYTE(lines8[y], x - 1); + valr = GET_DATA_FOUR_BYTES(liner32[y], x - 1); + sivals = (l_int32)vals; + cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ + dist = distparent + cost; + if (dist < valr) { /* shortest path so far to this pixel */ + SET_DATA_FOUR_BYTES(liner32[y], x - 1, dist); /* new dist */ + SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent to E */ + el = mazeelCreate(x - 1, y, 0); + el->val = vals; + el->distance = dist; + lheapAdd(lh, el); + } + } + if (y > 0) { /* check north */ + vals = GET_DATA_BYTE(lines8[y - 1], x); + valr = GET_DATA_FOUR_BYTES(liner32[y - 1], x); + sivals = (l_int32)vals; + cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ + dist = distparent + cost; + if (dist < valr) { /* shortest path so far to this pixel */ + SET_DATA_FOUR_BYTES(liner32[y - 1], x, dist); /* new dist */ + SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent to S */ + el = mazeelCreate(x, y - 1, 0); + el->val = vals; + el->distance = dist; + lheapAdd(lh, el); + } + } + if (x < w - 1) { /* check east */ + vals = GET_DATA_BYTE(lines8[y], x + 1); + valr = GET_DATA_FOUR_BYTES(liner32[y], x + 1); + sivals = (l_int32)vals; + cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ + dist = distparent + cost; + if (dist < valr) { /* shortest path so far to this pixel */ + SET_DATA_FOUR_BYTES(liner32[y], x + 1, dist); /* new dist */ + SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent to W */ + el = mazeelCreate(x + 1, y, 0); + el->val = vals; + el->distance = dist; + lheapAdd(lh, el); + } + } + if (y < h - 1) { /* check south */ + vals = GET_DATA_BYTE(lines8[y + 1], x); + valr = GET_DATA_FOUR_BYTES(liner32[y + 1], x); + sivals = (l_int32)vals; + cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ + dist = distparent + cost; + if (dist < valr) { /* shortest path so far to this pixel */ + SET_DATA_FOUR_BYTES(liner32[y + 1], x, dist); /* new dist */ + SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent to N */ + el = mazeelCreate(x, y + 1, 0); + el->val = vals; + el->distance = dist; + lheapAdd(lh, el); + } + } + LEPT_FREE(elp); + } + + lheapDestroy(&lh, TRUE); + + if (ppixd) { + pixd = pixConvert8To32(pixs); + *ppixd = pixd; + } + composeRGBPixel(255, 0, 0, &rpixel); /* start point */ + composeRGBPixel(0, 255, 0, &gpixel); + composeRGBPixel(0, 0, 255, &bpixel); /* end point */ + + x = xf; + y = yf; + pta = ptaCreate(0); + while (1) { /* write path onto pixd */ + ptaAddPt(pta, x, y); + if (x == xi && y == yi) + break; + if (pixd) + pixSetPixel(pixd, x, y, gpixel); + pixGetPixel(pixp, x, y, &val); + if (val == DIR_NORTH) + y--; + else if (val == DIR_SOUTH) + y++; + else if (val == DIR_EAST) + x++; + else if (val == DIR_WEST) + x--; + pixGetPixel(pixr, x, y, &val); + +#if DEBUG_PATH + fprintf(stderr, "(x,y) = (%d, %d); dist = %d\n", x, y, val); +#endif /* DEBUG_PATH */ + + } + if (pixd) { + pixSetPixel(pixd, xi, yi, rpixel); + pixSetPixel(pixd, xf, yf, bpixel); + } + +cleanup_stuff: + lheapDestroy(&lh, TRUE); + pixDestroy(&pixp); + pixDestroy(&pixr); + LEPT_FREE(lines8); + LEPT_FREE(linep8); + LEPT_FREE(liner32); + return pta; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/morph.c b/hgdriver/3rdparty/hgOCR/leptonica/morph.c new file mode 100644 index 0000000..8a0d674 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/morph.c @@ -0,0 +1,1822 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file morph.c + *
+ *
+ *     Generic binary morphological ops implemented with rasterop
+ *         PIX     *pixDilate()
+ *         PIX     *pixErode()
+ *         PIX     *pixHMT()
+ *         PIX     *pixOpen()
+ *         PIX     *pixClose()
+ *         PIX     *pixCloseSafe()
+ *         PIX     *pixOpenGeneralized()
+ *         PIX     *pixCloseGeneralized()
+ *
+ *     Binary morphological (raster) ops with brick Sels
+ *         PIX     *pixDilateBrick()
+ *         PIX     *pixErodeBrick()
+ *         PIX     *pixOpenBrick()
+ *         PIX     *pixCloseBrick()
+ *         PIX     *pixCloseSafeBrick()
+ *
+ *     Binary composed morphological (raster) ops with brick Sels
+ *         l_int32  selectComposableSels()
+ *         l_int32  selectComposableSizes()
+ *         PIX     *pixDilateCompBrick()
+ *         PIX     *pixErodeCompBrick()
+ *         PIX     *pixOpenCompBrick()
+ *         PIX     *pixCloseCompBrick()
+ *         PIX     *pixCloseSafeCompBrick()
+ *
+ *     Functions associated with boundary conditions
+ *         void     resetMorphBoundaryCondition()
+ *         l_int32  getMorphBorderPixelColor()
+ *
+ *     Static helpers for arg processing
+ *         static PIX     *processMorphArgs1()
+ *         static PIX     *processMorphArgs2()
+ *
+ *  You are provided with many simple ways to do binary morphology.
+ *  In particular, if you are using brick Sels, there are six
+ *  convenient methods, all specially tailored for separable operations
+ *  on brick Sels.  A "brick" Sel is a Sel that is a rectangle
+ *  of solid SEL_HITs with the origin at or near the center.
+ *  Note that a brick Sel can have one dimension of size 1.
+ *  This is very common.  All the brick Sel operations are
+ *  separable, meaning the operation is done first in the horizontal
+ *  direction and then in the vertical direction.  If one of the
+ *  dimensions is 1, this is a special case where the operation is
+ *  only performed in the other direction.
+ *
+ *  These six brick Sel methods are enumerated as follows:
+ *
+ *  (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}.
+ *      These are separable rasterop implementations.  The Sels are
+ *      automatically generated, used, and destroyed at the end.
+ *      You can get the result as a new Pix, in-place back into the src Pix,
+ *      or written to another existing Pix.
+ *
+ *  (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}.
+ *      These are separable, 2-way composite, rasterop implementations.
+ *      The Sels are automatically generated, used, and destroyed at the end.
+ *      You can get the result as a new Pix, in-place back into the src Pix,
+ *      or written to another existing Pix.  For large Sels, these are
+ *      considerably faster than the corresponding pix*Brick() functions.
+ *      N.B.:  The size of the Sels that are actually used are typically
+ *      close to, but not exactly equal to, the size input to the function.
+ *
+ *  (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}.
+ *      These are separable dwa (destination word accumulation)
+ *      implementations.  They use auto-gen'd dwa code.  You can get
+ *      the result as a new Pix, in-place back into the src Pix,
+ *      or written to another existing Pix.  This is typically
+ *      about 3x faster than the analogous rasterop pix*Brick()
+ *      function, but it has the limitation that the Sel size must
+ *      be less than 63.  This is pre-set to work on a number
+ *      of pre-generated Sels.  If you want to use other Sels, the
+ *      code can be auto-gen'd for them; see the instructions in morphdwa.c.
+ *
+ *  (4) Same as (1), but you run it through pixMorphSequence(), with
+ *      the sequence string either compiled in or generated using snprintf.
+ *      All intermediate images and Sels are created, used and destroyed.
+ *      You always get the result as a new Pix.  For example, you can
+ *      specify a separable 11 x 17 brick opening as "o11.17",
+ *      or you can specify the horizontal and vertical operations
+ *      explicitly as "o11.1 + o1.11".  See morphseq.c for details.
+ *
+ *  (5) Same as (2), but you run it through pixMorphCompSequence(), with
+ *      the sequence string either compiled in or generated using snprintf.
+ *      All intermediate images and Sels are created, used and destroyed.
+ *      You always get the result as a new Pix.  See morphseq.c for details.
+ *
+ *  (6) Same as (3), but you run it through pixMorphSequenceDwa(), with
+ *      the sequence string either compiled in or generated using snprintf.
+ *      All intermediate images and Sels are created, used and destroyed.
+ *      You always get the result as a new Pix.  See morphseq.c for details.
+ *
+ *  If you are using Sels that are not bricks, you have two choices:
+ *      (a) simplest: use the basic rasterop implementations (pixDilate(), ...)
+ *      (b) fastest: generate the destination word accumumlation (dwa)
+ *          code for your Sels and compile it with the library.
+ *
+ *      For an example, see flipdetect.c, which gives implementations
+ *      using hit-miss Sels with both the rasterop and dwa versions.
+ *      For the latter, the dwa code resides in fliphmtgen.c, and it
+ *      was generated by prog/flipselgen.c.  Both the rasterop and dwa
+ *      implementations are tested by prog/fliptest.c.
+ *
+ *  A global constant MORPH_BC is used to set the boundary conditions
+ *  for rasterop-based binary morphology.  MORPH_BC, in morph.c,
+ *  is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric
+ *  convention for boundary pixels in dilation and erosion:
+ *      All pixels outside the image are assumed to be OFF
+ *      for both dilation and erosion.
+ *  To use a symmetric definition, see comments in pixErode()
+ *  and reset MORPH_BC to SYMMETRIC_MORPH_BC, using
+ *  resetMorphBoundaryCondition().
+ *
+ *  Boundary artifacts are possible in closing when the non-symmetric
+ *  boundary conditions are used, because foreground pixels very close
+ *  to the edge can be removed.  This can be avoided by using either
+ *  the symmetric boundary conditions or the function pixCloseSafe(),
+ *  which adds a border before the operation and removes it afterwards.
+ *
+ *  The hit-miss transform (HMT) is the bit-and of 2 erosions:
+ *     (erosion of the src by the hits)  &  (erosion of the bit-inverted
+ *                                           src by the misses)
+ *
+ *  The 'generalized opening' is an HMT followed by a dilation that uses
+ *  only the hits of the hit-miss Sel.
+ *  The 'generalized closing' is a dilation (again, with the hits
+ *  of a hit-miss Sel), followed by the HMT.
+ *  Both of these 'generalized' functions are idempotent.
+ *
+ *  These functions are extensively tested in prog/binmorph1_reg.c,
+ *  prog/binmorph2_reg.c, and prog/binmorph3_reg.c.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Global constant; initialized here; must be declared extern + * in other files to access it directly. However, in most + * cases that is not necessary, because it can be reset + * using resetMorphBoundaryCondition(). */ +LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC; + + /* We accept this cost in extra rasterops for decomposing exactly. */ +static const l_int32 ACCEPTABLE_COST = 5; + + /* Static helpers for arg processing */ +static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt); +static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel); + + +/*-----------------------------------------------------------------* + * Generic binary morphological ops implemented with rasterop * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDilate() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) This dilates src using hits in Sel.
+ *      (2) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (3) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixDilate(NULL, pixs, ...);
+ *          (b) pixDilate(pixs, pixs, ...);
+ *          (c) pixDilate(pixd, pixs, ...);
+ *      (4) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixDilate(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +l_int32 i, j, w, h, sx, sy, cx, cy, seldata; +PIX *pixt; + + PROCNAME("pixDilate"); + + if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) + return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + selGetParameters(sel, &sy, &sx, &cy, &cx); + pixClearAll(pixd); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + seldata = sel->data[i][j]; + if (seldata == 1) { /* src | dst */ + pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST, + pixt, 0, 0); + } + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixErode() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) This erodes src using hits in Sel.
+ *      (2) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (3) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixErode(NULL, pixs, ...);
+ *          (b) pixErode(pixs, pixs, ...);
+ *          (c) pixErode(pixd, pixs, ...);
+ *      (4) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixErode(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +l_int32 i, j, w, h, sx, sy, cx, cy, seldata; +l_int32 xp, yp, xn, yn; +PIX *pixt; + + PROCNAME("pixErode"); + + if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) + return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + selGetParameters(sel, &sy, &sx, &cy, &cx); + pixSetAll(pixd); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + seldata = sel->data[i][j]; + if (seldata == 1) { /* src & dst */ + pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, + pixt, 0, 0); + } + } + } + + /* Clear near edges. We do this for the asymmetric boundary + * condition convention that implements erosion assuming all + * pixels surrounding the image are OFF. If you use a + * use a symmetric b.c. convention, where the erosion is + * implemented assuming pixels surrounding the image + * are ON, these operations are omitted. */ + if (MORPH_BC == ASYMMETRIC_MORPH_BC) { + selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); + if (xp > 0) + pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); + if (xn > 0) + pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); + if (yp > 0) + pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); + if (yn > 0) + pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixHMT() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) The hit-miss transform erodes the src, using both hits
+ *          and misses in the Sel.  It ANDs the shifted src for hits
+ *          and ANDs the inverted shifted src for misses.
+ *      (2) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (3) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixHMT(NULL, pixs, ...);
+ *          (b) pixHMT(pixs, pixs, ...);
+ *          (c) pixHMT(pixd, pixs, ...);
+ *      (4) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixHMT(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata; +l_int32 xp, yp, xn, yn; +PIX *pixt; + + PROCNAME("pixHMT"); + + if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) + return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + selGetParameters(sel, &sy, &sx, &cy, &cx); + firstrasterop = TRUE; + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + seldata = sel->data[i][j]; + if (seldata == 1) { /* hit */ + if (firstrasterop == TRUE) { /* src only */ + pixClearAll(pixd); + pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC, + pixt, 0, 0); + firstrasterop = FALSE; + } else { /* src & dst */ + pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, + pixt, 0, 0); + } + } else if (seldata == 2) { /* miss */ + if (firstrasterop == TRUE) { /* ~src only */ + pixSetAll(pixd); + pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC), + pixt, 0, 0); + firstrasterop = FALSE; + } else { /* ~src & dst */ + pixRasterop(pixd, cx - j, cy - i, w, h, + PIX_NOT(PIX_SRC) & PIX_DST, + pixt, 0, 0); + } + } + } + } + + /* Clear near edges */ + selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); + if (xp > 0) + pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); + if (xn > 0) + pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); + if (yp > 0) + pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); + if (yn > 0) + pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixOpen() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Generic morphological opening, using hits in the Sel.
+ *      (2) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (3) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpen(NULL, pixs, ...);
+ *          (b) pixOpen(pixs, pixs, ...);
+ *          (c) pixOpen(pixd, pixs, ...);
+ *      (4) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixOpen(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +PIX *pixt; + + PROCNAME("pixOpen"); + + if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); + + if ((pixt = pixErode(NULL, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + pixDilate(pixd, pixt, sel); + pixDestroy(&pixt); + + return pixd; +} + + +/*! + * \brief pixClose() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Generic morphological closing, using hits in the Sel.
+ *      (2) This implementation is a strict dual of the opening if
+ *          symmetric boundary conditions are used (see notes at top
+ *          of this file).
+ *      (3) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (4) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixClose(NULL, pixs, ...);
+ *          (b) pixClose(pixs, pixs, ...);
+ *          (c) pixClose(pixd, pixs, ...);
+ *      (5) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixClose(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +PIX *pixt; + + PROCNAME("pixClose"); + + if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); + + if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + pixErode(pixd, pixt, sel); + pixDestroy(&pixt); + + return pixd; +} + + +/*! + * \brief pixCloseSafe() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Generic morphological closing, using hits in the Sel.
+ *      (2) If non-symmetric boundary conditions are used, this
+ *          function adds a border of OFF pixels that is of
+ *          sufficient size to avoid losing pixels from the dilation,
+ *          and it removes the border after the operation is finished.
+ *          It thus enforces a correct extensive result for closing.
+ *      (3) If symmetric b.c. are used, it is not necessary to add
+ *          and remove this border.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseSafe(NULL, pixs, ...);
+ *          (b) pixCloseSafe(pixs, pixs, ...);
+ *          (c) pixCloseSafe(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixCloseSafe(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +l_int32 xp, yp, xn, yn, xmax, xbord; +PIX *pixt1, *pixt2; + + PROCNAME("pixCloseSafe"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!sel) + return (PIX *)ERROR_PTR("sel not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + /* Symmetric b.c. handles correctly without added pixels */ + if (MORPH_BC == SYMMETRIC_MORPH_BC) + return pixClose(pixd, pixs, sel); + + selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); + xmax = L_MAX(xp, xn); + xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */ + + if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL) + return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd); + pixClose(pixt1, pixt1, sel); + if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL) + return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd); + pixDestroy(&pixt1); + + if (!pixd) + return pixt2; + + pixCopy(pixd, pixt2); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixOpenGeneralized() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Generalized morphological opening, using both hits and
+ *          misses in the Sel.
+ *      (2) This does a hit-miss transform, followed by a dilation
+ *          using the hits.
+ *      (3) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (4) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpenGeneralized(NULL, pixs, ...);
+ *          (b) pixOpenGeneralized(pixs, pixs, ...);
+ *          (c) pixOpenGeneralized(pixd, pixs, ...);
+ *      (5) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixOpenGeneralized(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +PIX *pixt; + + PROCNAME("pixOpenGeneralized"); + + if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); + + if ((pixt = pixHMT(NULL, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + pixDilate(pixd, pixt, sel); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixCloseGeneralized() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Generalized morphological closing, using both hits and
+ *          misses in the Sel.
+ *      (2) This does a dilation using the hits, followed by a
+ *          hit-miss transform.
+ *      (3) This operation is a dual of the generalized opening.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseGeneralized(NULL, pixs, ...);
+ *          (b) pixCloseGeneralized(pixs, pixs, ...);
+ *          (c) pixCloseGeneralized(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixCloseGeneralized(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +PIX *pixt; + + PROCNAME("pixCloseGeneralized"); + + if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); + + if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + pixHMT(pixd, pixt, sel); + pixDestroy(&pixt); + + return pixd; +} + + +/*-----------------------------------------------------------------* + * Binary morphological (raster) ops with brick Sels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDilateBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do separably if both hsize and vsize are > 1.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixDilateBrick(NULL, pixs, ...);
+ *          (b) pixDilateBrick(pixs, pixs, ...);
+ *          (c) pixDilateBrick(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixDilateBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *sel, *selh, *selv; + + PROCNAME("pixDilateBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize == 1 || vsize == 1) { /* no intermediate result */ + sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); + pixd = pixDilate(pixd, pixs, sel); + selDestroy(&sel); + } else { + selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); + selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); + pixt = pixDilate(NULL, pixs, selh); + pixd = pixDilate(pixd, pixt, selv); + pixDestroy(&pixt); + selDestroy(&selh); + selDestroy(&selv); + } + + return pixd; +} + + +/*! + * \brief pixErodeBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do separably if both hsize and vsize are > 1.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixErodeBrick(NULL, pixs, ...);
+ *          (b) pixErodeBrick(pixs, pixs, ...);
+ *          (c) pixErodeBrick(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixErodeBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *sel, *selh, *selv; + + PROCNAME("pixErodeBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize == 1 || vsize == 1) { /* no intermediate result */ + sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); + pixd = pixErode(pixd, pixs, sel); + selDestroy(&sel); + } else { + selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); + selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); + pixt = pixErode(NULL, pixs, selh); + pixd = pixErode(pixd, pixt, selv); + pixDestroy(&pixt); + selDestroy(&selh); + selDestroy(&selv); + } + + return pixd; +} + + +/*! + * \brief pixOpenBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do separably if both hsize and vsize are > 1.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpenBrick(NULL, pixs, ...);
+ *          (b) pixOpenBrick(pixs, pixs, ...);
+ *          (c) pixOpenBrick(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixOpenBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *sel, *selh, *selv; + + PROCNAME("pixOpenBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize == 1 || vsize == 1) { /* no intermediate result */ + sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); + pixd = pixOpen(pixd, pixs, sel); + selDestroy(&sel); + } else { /* do separably */ + selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); + selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); + pixt = pixErode(NULL, pixs, selh); + pixd = pixErode(pixd, pixt, selv); + pixDilate(pixt, pixd, selh); + pixDilate(pixd, pixt, selv); + pixDestroy(&pixt); + selDestroy(&selh); + selDestroy(&selv); + } + + return pixd; +} + + +/*! + * \brief pixCloseBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do separably if both hsize and vsize are > 1.
+ *      (4) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (5) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseBrick(NULL, pixs, ...);
+ *          (b) pixCloseBrick(pixs, pixs, ...);
+ *          (c) pixCloseBrick(pixd, pixs, ...);
+ *      (6) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixCloseBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *sel, *selh, *selv; + + PROCNAME("pixCloseBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize == 1 || vsize == 1) { /* no intermediate result */ + sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); + pixd = pixClose(pixd, pixs, sel); + selDestroy(&sel); + } else { /* do separably */ + selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); + selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); + pixt = pixDilate(NULL, pixs, selh); + pixd = pixDilate(pixd, pixt, selv); + pixErode(pixt, pixd, selh); + pixErode(pixd, pixt, selv); + pixDestroy(&pixt); + selDestroy(&selh); + selDestroy(&selv); + } + + return pixd; +} + + +/*! + * \brief pixCloseSafeBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do separably if both hsize and vsize are > 1.
+ *      (4) Safe closing adds a border of 0 pixels, of sufficient size so
+ *          that all pixels in input image are processed within
+ *          32-bit words in the expanded image.  As a result, there is
+ *          no special processing for pixels near the boundary, and there
+ *          are no boundary effects.  The border is removed at the end.
+ *      (5) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (6) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseBrick(NULL, pixs, ...);
+ *          (b) pixCloseBrick(pixs, pixs, ...);
+ *          (c) pixCloseBrick(pixd, pixs, ...);
+ *      (7) The size of the result is determined by pixs.
+ * 
+ */ +PIX * +pixCloseSafeBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 maxtrans, bordsize; +PIX *pixsb, *pixt, *pixdb; +SEL *sel, *selh, *selv; + + PROCNAME("pixCloseSafeBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + /* Symmetric b.c. handles correctly without added pixels */ + if (MORPH_BC == SYMMETRIC_MORPH_BC) + return pixCloseBrick(pixd, pixs, hsize, vsize); + + maxtrans = L_MAX(hsize / 2, vsize / 2); + bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ + pixsb = pixAddBorder(pixs, bordsize, 0); + + if (hsize == 1 || vsize == 1) { /* no intermediate result */ + sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); + pixdb = pixClose(NULL, pixsb, sel); + selDestroy(&sel); + } else { /* do separably */ + selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); + selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); + pixt = pixDilate(NULL, pixsb, selh); + pixdb = pixDilate(NULL, pixt, selv); + pixErode(pixt, pixdb, selh); + pixErode(pixdb, pixt, selv); + pixDestroy(&pixt); + selDestroy(&selh); + selDestroy(&selv); + } + + pixt = pixRemoveBorder(pixdb, bordsize); + pixDestroy(&pixsb); + pixDestroy(&pixdb); + + if (!pixd) { + pixd = pixt; + } else { + pixCopy(pixd, pixt); + pixDestroy(&pixt); + } + + return pixd; +} + + +/*-----------------------------------------------------------------* + * Binary composed morphological (raster) ops with brick Sels * + *-----------------------------------------------------------------*/ +/* \brief selectComposableSels() + * + * \param[in] size of composed sel + * \param[in] direction L_HORIZ, L_VERT + * \param[out] psel1 [optional] contiguous sel; can be null + * \param[out] psel2 [optional] comb sel; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) When using composable Sels, where the original Sel is
+ *          decomposed into two, the best you can do in terms
+ *          of reducing the computation is by a factor:
+ *
+ *               2 * sqrt(size) / size
+ *
+ *          In practice, you get quite close to this.  E.g.,
+ *
+ *             Sel size     |   Optimum reduction factor
+ *             --------         ------------------------
+ *                36        |          1/3
+ *                64        |          1/4
+ *               144        |          1/6
+ *               256        |          1/8
+ * 
+ */ +l_int32 +selectComposableSels(l_int32 size, + l_int32 direction, + SEL **psel1, + SEL **psel2) +{ +l_int32 factor1, factor2; + + PROCNAME("selectComposableSels"); + + if (!psel1 && !psel2) + return ERROR_INT("neither &sel1 nor &sel2 are defined", procName, 1); + if (psel1) *psel1 = NULL; + if (psel2) *psel2 = NULL; + if (size < 1 || size > 250 * 250) + return ERROR_INT("size < 1", procName, 1); + if (direction != L_HORIZ && direction != L_VERT) + return ERROR_INT("invalid direction", procName, 1); + + if (selectComposableSizes(size, &factor1, &factor2)) + return ERROR_INT("factors not found", procName, 1); + + if (psel1) { + if (direction == L_HORIZ) + *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT); + else + *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT); + } + if (psel2) + *psel2 = selCreateComb(factor1, factor2, direction); + return 0; +} + + +/*! + * \brief selectComposableSizes() + * + * \param[in] size of sel to be decomposed + * \param[out] pfactor1 larger factor + * \param[out] pfactor2 smaller factor + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This works for Sel sizes up to 62500, which seems sufficient.
+ *      (2) The composable sel size is typically within +- 1 of
+ *          the requested size.  Up to size = 300, the maximum difference
+ *          is +- 2.
+ *      (3) We choose an overall cost function where the penalty for
+ *          the size difference between input and actual is 4 times
+ *          the penalty for additional rasterops.
+ *      (4) Returned values: factor1 >= factor2
+ *          If size > 1, then factor1 > 1.
+ * 
+ */ +l_ok +selectComposableSizes(l_int32 size, + l_int32 *pfactor1, + l_int32 *pfactor2) +{ +l_int32 i, midval, val1, val2m, val2p; +l_int32 index, prodm, prodp; +l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp; +l_int32 lowval[256]; +l_int32 hival[256]; +l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */ +l_int32 diff[256]; /* diff between product (sel size) and input size */ + + PROCNAME("selectComposableSizes"); + + if (size < 1 || size > 250 * 250) + return ERROR_INT("size < 1", procName, 1); + if (!pfactor1 || !pfactor2) + return ERROR_INT("&factor1 or &factor2 not defined", procName, 1); + + midval = (l_int32)(sqrt((l_float64)size) + 0.001); + if (midval * midval == size) { + *pfactor1 = *pfactor2 = midval; + return 0; + } + + /* Set up arrays. For each val1, optimize for lowest diff, + * and save the rastcost, the diff, and the two factors. */ + for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) { + val2m = size / val1; + val2p = val2m + 1; + prodm = val1 * val2m; + prodp = val1 * val2p; + rastcostm = val1 + val2m - 2 * midval; + rastcostp = val1 + val2p - 2 * midval; + diffm = L_ABS(size - prodm); + diffp = L_ABS(size - prodp); + if (diffm <= diffp) { + lowval[i] = L_MIN(val1, val2m); + hival[i] = L_MAX(val1, val2m); + rastcost[i] = rastcostm; + diff[i] = diffm; + } else { + lowval[i] = L_MIN(val1, val2p); + hival[i] = L_MAX(val1, val2p); + rastcost[i] = rastcostp; + diff[i] = diffp; + } + } + + /* Choose the optimum factors; use cost ratio 4 on diff */ + mincost = 10000; + index = 1; /* unimportant initial value */ + for (i = 0; i < midval + 1; i++) { + if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) { + *pfactor1 = hival[i]; + *pfactor2 = lowval[i]; + return 0; + } + totcost = 4 * diff[i] + rastcost[i]; + if (totcost < mincost) { + mincost = totcost; + index = i; + } + } + *pfactor1 = hival[index]; + *pfactor2 = lowval[index]; + + return 0; +} + + +/*! + * \brief pixDilateCompBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do compositely for each dimension > 1.
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (6) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixDilateCompBrick(NULL, pixs, ...);
+ *          (b) pixDilateCompBrick(pixs, pixs, ...);
+ *          (c) pixDilateCompBrick(pixd, pixs, ...);
+ *      (7) The dimensions of the resulting image are determined by pixs.
+ *      (8) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *          but not necessarily equal to it.  It attempts to optimize:
+ *             (a) for consistency with the input values: the product
+ *                 of terms is close to the input size
+ *             (b) for efficiency of the operation: the sum of the
+ *                 terms is small; ideally about twice the square
+ *                 root of the input size.
+ *          So, for example, if the input hsize = 37, which is
+ *          a prime number, the decomposer will break this into two
+ *          terms, 6 and 6, so that the net result is a dilation
+ *          with hsize = 36.
+ * 
+ */ +PIX * +pixDilateCompBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pix1, *pix2, *pix3; +SEL *selh1, *selh2, *selv1, *selv2; + + PROCNAME("pixDilateCompBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize > 1) + selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); + if (vsize > 1) + selectComposableSels(vsize, L_VERT, &selv1, &selv2); + + pix1 = pixAddBorder(pixs, 32, 0); + if (vsize == 1) { + pix2 = pixDilate(NULL, pix1, selh1); + pix3 = pixDilate(NULL, pix2, selh2); + } else if (hsize == 1) { + pix2 = pixDilate(NULL, pix1, selv1); + pix3 = pixDilate(NULL, pix2, selv2); + } else { + pix2 = pixDilate(NULL, pix1, selh1); + pix3 = pixDilate(NULL, pix2, selh2); + pixDilate(pix2, pix3, selv1); + pixDilate(pix3, pix2, selv2); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + + if (hsize > 1) { + selDestroy(&selh1); + selDestroy(&selh2); + } + if (vsize > 1) { + selDestroy(&selv1); + selDestroy(&selv2); + } + + pix1 = pixRemoveBorder(pix3, 32); + pixDestroy(&pix3); + if (!pixd) + return pix1; + pixCopy(pixd, pix1); + pixDestroy(&pix1); + return pixd; +} + + +/*! + * \brief pixErodeCompBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do compositely for each dimension > 1.
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (6) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixErodeCompBrick(NULL, pixs, ...);
+ *          (b) pixErodeCompBrick(pixs, pixs, ...);
+ *          (c) pixErodeCompBrick(pixd, pixs, ...);
+ *      (7) The dimensions of the resulting image are determined by pixs.
+ *      (8) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *          but not necessarily equal to it.  It attempts to optimize:
+ *             (a) for consistency with the input values: the product
+ *                 of terms is close to the input size
+ *             (b) for efficiency of the operation: the sum of the
+ *                 terms is small; ideally about twice the square
+ *                 root of the input size.
+ *          So, for example, if the input hsize = 37, which is
+ *          a prime number, the decomposer will break this into two
+ *          terms, 6 and 6, so that the net result is a dilation
+ *          with hsize = 36.
+ * 
+ */ +PIX * +pixErodeCompBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *selh1, *selh2, *selv1, *selv2; + + PROCNAME("pixErodeCompBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize > 1) + selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); + if (vsize > 1) + selectComposableSels(vsize, L_VERT, &selv1, &selv2); + if (vsize == 1) { + pixt = pixErode(NULL, pixs, selh1); + pixd = pixErode(pixd, pixt, selh2); + } else if (hsize == 1) { + pixt = pixErode(NULL, pixs, selv1); + pixd = pixErode(pixd, pixt, selv2); + } else { + pixt = pixErode(NULL, pixs, selh1); + pixd = pixErode(pixd, pixt, selh2); + pixErode(pixt, pixd, selv1); + pixErode(pixd, pixt, selv2); + } + pixDestroy(&pixt); + + if (hsize > 1) { + selDestroy(&selh1); + selDestroy(&selh2); + } + if (vsize > 1) { + selDestroy(&selv1); + selDestroy(&selv2); + } + + return pixd; +} + + +/*! + * \brief pixOpenCompBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do compositely for each dimension > 1.
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (6) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpenCompBrick(NULL, pixs, ...);
+ *          (b) pixOpenCompBrick(pixs, pixs, ...);
+ *          (c) pixOpenCompBrick(pixd, pixs, ...);
+ *      (7) The dimensions of the resulting image are determined by pixs.
+ *      (8) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *          but not necessarily equal to it.  It attempts to optimize:
+ *             (a) for consistency with the input values: the product
+ *                 of terms is close to the input size
+ *             (b) for efficiency of the operation: the sum of the
+ *                 terms is small; ideally about twice the square
+ *                 root of the input size.
+ *          So, for example, if the input hsize = 37, which is
+ *          a prime number, the decomposer will break this into two
+ *          terms, 6 and 6, so that the net result is a dilation
+ *          with hsize = 36.
+ * 
+ */ +PIX * +pixOpenCompBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *selh1, *selh2, *selv1, *selv2; + + PROCNAME("pixOpenCompBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize > 1) + selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); + if (vsize > 1) + selectComposableSels(vsize, L_VERT, &selv1, &selv2); + if (vsize == 1) { + pixt = pixErode(NULL, pixs, selh1); + pixd = pixErode(pixd, pixt, selh2); + pixDilate(pixt, pixd, selh1); + pixDilate(pixd, pixt, selh2); + } else if (hsize == 1) { + pixt = pixErode(NULL, pixs, selv1); + pixd = pixErode(pixd, pixt, selv2); + pixDilate(pixt, pixd, selv1); + pixDilate(pixd, pixt, selv2); + } else { /* do separably */ + pixt = pixErode(NULL, pixs, selh1); + pixd = pixErode(pixd, pixt, selh2); + pixErode(pixt, pixd, selv1); + pixErode(pixd, pixt, selv2); + pixDilate(pixt, pixd, selh1); + pixDilate(pixd, pixt, selh2); + pixDilate(pixt, pixd, selv1); + pixDilate(pixd, pixt, selv2); + } + pixDestroy(&pixt); + + if (hsize > 1) { + selDestroy(&selh1); + selDestroy(&selh2); + } + if (vsize > 1) { + selDestroy(&selv1); + selDestroy(&selv2); + } + + return pixd; +} + + +/*! + * \brief pixCloseCompBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do compositely for each dimension > 1.
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (6) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseCompBrick(NULL, pixs, ...);
+ *          (b) pixCloseCompBrick(pixs, pixs, ...);
+ *          (c) pixCloseCompBrick(pixd, pixs, ...);
+ *      (7) The dimensions of the resulting image are determined by pixs.
+ *      (8) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *          but not necessarily equal to it.  It attempts to optimize:
+ *             (a) for consistency with the input values: the product
+ *                 of terms is close to the input size
+ *             (b) for efficiency of the operation: the sum of the
+ *                 terms is small; ideally about twice the square
+ *                 root of the input size.
+ *          So, for example, if the input hsize = 37, which is
+ *          a prime number, the decomposer will break this into two
+ *          terms, 6 and 6, so that the net result is a dilation
+ *          with hsize = 36.
+ * 
+ */ +PIX * +pixCloseCompBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; +SEL *selh1, *selh2, *selv1, *selv2; + + PROCNAME("pixCloseCompBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + if (hsize > 1) + selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); + if (vsize > 1) + selectComposableSels(vsize, L_VERT, &selv1, &selv2); + if (vsize == 1) { + pixt = pixDilate(NULL, pixs, selh1); + pixd = pixDilate(pixd, pixt, selh2); + pixErode(pixt, pixd, selh1); + pixErode(pixd, pixt, selh2); + } else if (hsize == 1) { + pixt = pixDilate(NULL, pixs, selv1); + pixd = pixDilate(pixd, pixt, selv2); + pixErode(pixt, pixd, selv1); + pixErode(pixd, pixt, selv2); + } else { /* do separably */ + pixt = pixDilate(NULL, pixs, selh1); + pixd = pixDilate(pixd, pixt, selh2); + pixDilate(pixt, pixd, selv1); + pixDilate(pixd, pixt, selv2); + pixErode(pixt, pixd, selh1); + pixErode(pixd, pixt, selh2); + pixErode(pixt, pixd, selv1); + pixErode(pixd, pixt, selv2); + } + pixDestroy(&pixt); + + if (hsize > 1) { + selDestroy(&selh1); + selDestroy(&selh2); + } + if (vsize > 1) { + selDestroy(&selv1); + selDestroy(&selv2); + } + + return pixd; +} + + +/*! + * \brief pixCloseSafeCompBrick() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
+ *      (3) Do compositely for each dimension > 1.
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) Safe closing adds a border of 0 pixels, of sufficient size so
+ *          that all pixels in input image are processed within
+ *          32-bit words in the expanded image.  As a result, there is
+ *          no special processing for pixels near the boundary, and there
+ *          are no boundary effects.  The border is removed at the end.
+ *      (6) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (7) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...);
+ *          (b) pixCloseSafeCompBrick(pixs, pixs, ...);
+ *          (c) pixCloseSafeCompBrick(pixd, pixs, ...);
+ *      (8) The dimensions of the resulting image are determined by pixs.
+ *      (9) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *          but not necessarily equal to it.  It attempts to optimize:
+ *             (a) for consistency with the input values: the product
+ *                 of terms is close to the input size
+ *             (b) for efficiency of the operation: the sum of the
+ *                 terms is small; ideally about twice the square
+ *                 root of the input size.
+ *          So, for example, if the input hsize = 37, which is
+ *          a prime number, the decomposer will break this into two
+ *          terms, 6 and 6, so that the net result is a dilation
+ *          with hsize = 36.
+ * 
+ */ +PIX * +pixCloseSafeCompBrick(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 maxtrans, bordsize; +PIX *pixsb, *pixt, *pixdb; +SEL *selh1, *selh2, *selv1, *selv2; + + PROCNAME("pixCloseSafeCompBrick"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + /* Symmetric b.c. handles correctly without added pixels */ + if (MORPH_BC == SYMMETRIC_MORPH_BC) + return pixCloseCompBrick(pixd, pixs, hsize, vsize); + + maxtrans = L_MAX(hsize / 2, vsize / 2); + bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ + pixsb = pixAddBorder(pixs, bordsize, 0); + + if (hsize > 1) + selectComposableSels(hsize, L_HORIZ, &selh1, &selh2); + if (vsize > 1) + selectComposableSels(vsize, L_VERT, &selv1, &selv2); + if (vsize == 1) { + pixt = pixDilate(NULL, pixsb, selh1); + pixdb = pixDilate(NULL, pixt, selh2); + pixErode(pixt, pixdb, selh1); + pixErode(pixdb, pixt, selh2); + } else if (hsize == 1) { + pixt = pixDilate(NULL, pixsb, selv1); + pixdb = pixDilate(NULL, pixt, selv2); + pixErode(pixt, pixdb, selv1); + pixErode(pixdb, pixt, selv2); + } else { /* do separably */ + pixt = pixDilate(NULL, pixsb, selh1); + pixdb = pixDilate(NULL, pixt, selh2); + pixDilate(pixt, pixdb, selv1); + pixDilate(pixdb, pixt, selv2); + pixErode(pixt, pixdb, selh1); + pixErode(pixdb, pixt, selh2); + pixErode(pixt, pixdb, selv1); + pixErode(pixdb, pixt, selv2); + } + pixDestroy(&pixt); + + pixt = pixRemoveBorder(pixdb, bordsize); + pixDestroy(&pixsb); + pixDestroy(&pixdb); + + if (!pixd) { + pixd = pixt; + } else { + pixCopy(pixd, pixt); + pixDestroy(&pixt); + } + + if (hsize > 1) { + selDestroy(&selh1); + selDestroy(&selh2); + } + if (vsize > 1) { + selDestroy(&selv1); + selDestroy(&selv2); + } + + return pixd; +} + + +/*-----------------------------------------------------------------* + * Functions associated with boundary conditions * + *-----------------------------------------------------------------*/ +/*! + * \brief resetMorphBoundaryCondition() + * + * \param[in] bc SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC + * \return void + */ +void +resetMorphBoundaryCondition(l_int32 bc) +{ + PROCNAME("resetMorphBoundaryCondition"); + + if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) { + L_WARNING("invalid bc; using asymmetric\n", procName); + bc = ASYMMETRIC_MORPH_BC; + } + MORPH_BC = bc; + return; +} + + +/*! + * \brief getMorphBorderPixelColor() + * + * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE + * \param[in] depth of pix + * \return color of border pixels for this operation + */ +l_uint32 +getMorphBorderPixelColor(l_int32 type, + l_int32 depth) +{ + PROCNAME("getMorphBorderPixelColor"); + + if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) + return ERROR_INT("invalid type", procName, 0); + if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && + depth != 16 && depth != 32) + return ERROR_INT("invalid depth", procName, 0); + + if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE) + return 0; + + /* Symmetric & erosion */ + if (depth < 32) + return ((1 << depth) - 1); + else /* depth == 32 */ + return 0xffffff00; +} + + +/*-----------------------------------------------------------------* + * Static helpers for arg processing * + *-----------------------------------------------------------------*/ +/*! + * \brief processMorphArgs1() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] sel + * \param[out] ppixt copy or clone of %pixs + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) This is used for generic erosion, dilation and HMT.
+ * 
+ */ +static PIX * +processMorphArgs1(PIX *pixd, + PIX *pixs, + SEL *sel, + PIX **ppixt) +{ +l_int32 sx, sy; + + PROCNAME("processMorphArgs1"); + + if (!ppixt) + return (PIX *)ERROR_PTR("&pixt not defined", procName, pixd); + *ppixt = NULL; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!sel) + return (PIX *)ERROR_PTR("sel not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + selGetParameters(sel, &sx, &sy, NULL, NULL); + if (sx == 0 || sy == 0) + return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); + + /* We require pixd to exist and to be the same size as pixs. + * Further, pixt must be a copy (or clone) of pixs. */ + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + *ppixt = pixClone(pixs); + } else { + pixResizeImageData(pixd, pixs); + if (pixd == pixs) { /* in-place; must make a copy of pixs */ + if ((*ppixt = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + } else { + *ppixt = pixClone(pixs); + } + } + return pixd; +} + + +/*! + * \brief processMorphArgs2() + * + * This is used for generic openings and closings. + */ +static PIX * +processMorphArgs2(PIX *pixd, + PIX *pixs, + SEL *sel) +{ +l_int32 sx, sy; + + PROCNAME("processMorphArgs2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!sel) + return (PIX *)ERROR_PTR("sel not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + selGetParameters(sel, &sx, &sy, NULL, NULL); + if (sx == 0 || sy == 0) + return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); + + if (!pixd) + return pixCreateTemplate(pixs); + pixResizeImageData(pixd, pixs); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/morph.h b/hgdriver/3rdparty/hgOCR/leptonica/morph.h new file mode 100644 index 0000000..d17723f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/morph.h @@ -0,0 +1,248 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_MORPH_H +#define LEPTONICA_MORPH_H + +/*! + * \file morph.h + * + *
+ *  Contains the following structs:
+ *      struct Sel
+ *      struct Sela
+ *      struct Kernel
+ *
+ *  Contains definitions for:
+ *      morphological b.c. flags
+ *      structuring element types
+ *      runlength flags for granulometry
+ *      direction flags for grayscale morphology
+ *      morphological operation flags
+ *      standard border size
+ *      grayscale intensity scaling flags
+ *      morphological tophat flags
+ *      arithmetic and logical operator flags
+ *      grayscale morphology selection flags
+ *      distance function b.c. flags
+ *      image comparison flags
+ *      color content flags
+ * 
+ */ + +/*-------------------------------------------------------------------------* + * Sel and Sel array * + *-------------------------------------------------------------------------*/ +#define SEL_VERSION_NUMBER 1 + +/*! Selection */ +struct Sel +{ + l_int32 sy; /*!< sel height */ + l_int32 sx; /*!< sel width */ + l_int32 cy; /*!< y location of sel origin */ + l_int32 cx; /*!< x location of sel origin */ + l_int32 **data; /*!< {0,1,2}; data[i][j] in [row][col] order */ + char *name; /*!< used to find sel by name */ +}; +typedef struct Sel SEL; + +/*! Array of Sel */ +struct Sela +{ + l_int32 n; /*!< number of sel actually stored */ + l_int32 nalloc; /*!< size of allocated ptr array */ + struct Sel **sel; /*!< sel ptr array */ +}; +typedef struct Sela SELA; + + +/*-------------------------------------------------------------------------* + * Kernel * + *-------------------------------------------------------------------------*/ +#define KERNEL_VERSION_NUMBER 2 + +/*! Kernel */ +struct L_Kernel +{ + l_int32 sy; /*!< kernel height */ + l_int32 sx; /*!< kernel width */ + l_int32 cy; /*!< y location of kernel origin */ + l_int32 cx; /*!< x location of kernel origin */ + l_float32 **data; /*!< data[i][j] in [row][col] order */ +}; +typedef struct L_Kernel L_KERNEL; + + +/*-------------------------------------------------------------------------* + * Morphological boundary condition flags * + * * + * Two types of boundary condition for erosion. * + * The global variable MORPH_BC takes on one of these two values. * + * See notes in morph.c for usage. * + *-------------------------------------------------------------------------*/ + +/*! Morph Boundary */ +enum { + SYMMETRIC_MORPH_BC = 0, + ASYMMETRIC_MORPH_BC = 1 +}; + +/*-------------------------------------------------------------------------* + * Structuring element vals * + *-------------------------------------------------------------------------*/ + +/*! SEL Vals */ +enum { + SEL_DONT_CARE = 0, + SEL_HIT = 1, + SEL_MISS = 2 +}; + +/*-------------------------------------------------------------------------* + * Runlength flags for granulometry * + *-------------------------------------------------------------------------*/ + +/*! Runlength Polarity */ +enum { + L_RUN_OFF = 0, + L_RUN_ON = 1 +}; + +/*-------------------------------------------------------------------------* + * Direction flags for grayscale morphology, granulometry, * + * composable Sels, convolution, etc. * + *-------------------------------------------------------------------------*/ + +/*! Direction Flags */ +enum { + L_HORIZ = 1, + L_VERT = 2, + L_BOTH_DIRECTIONS = 3 +}; + +/*-------------------------------------------------------------------------* + * Morphological operation flags * + *-------------------------------------------------------------------------*/ + +/*! Morph Operator */ +enum { + L_MORPH_DILATE = 1, + L_MORPH_ERODE = 2, + L_MORPH_OPEN = 3, + L_MORPH_CLOSE = 4, + L_MORPH_HMT = 5 +}; + +/*-------------------------------------------------------------------------* + * Grayscale intensity scaling flags * + *-------------------------------------------------------------------------*/ + +/*! Pixel Value Scaling */ +enum { + L_LINEAR_SCALE = 1, + L_LOG_SCALE = 2 +}; + +/*-------------------------------------------------------------------------* + * Morphological tophat flags * + *-------------------------------------------------------------------------*/ + +/*! Morph Tophat */ +enum { + L_TOPHAT_WHITE = 0, + L_TOPHAT_BLACK = 1 +}; + +/*-------------------------------------------------------------------------* + * Arithmetic and logical operator flags * + * (use on grayscale images and Numas) * + *-------------------------------------------------------------------------*/ + +/*! ArithLogical Ops */ +enum { + L_ARITH_ADD = 1, + L_ARITH_SUBTRACT = 2, + L_ARITH_MULTIPLY = 3, /* on numas only */ + L_ARITH_DIVIDE = 4, /* on numas only */ + L_UNION = 5, /* on numas only */ + L_INTERSECTION = 6, /* on numas only */ + L_SUBTRACTION = 7, /* on numas only */ + L_EXCLUSIVE_OR = 8 /* on numas only */ +}; + +/*-------------------------------------------------------------------------* + * Min/max selection flags * + *-------------------------------------------------------------------------*/ + +/*! MinMax Selection */ +enum { + L_CHOOSE_MIN = 1, /* useful in a downscaling "erosion" */ + L_CHOOSE_MAX = 2, /* useful in a downscaling "dilation" */ + L_CHOOSE_MAXDIFF = 3, /* useful in a downscaling contrast */ + L_CHOOSE_MIN_BOOST = 4, /* use a modification of the min value */ + L_CHOOSE_MAX_BOOST = 5 /* use a modification of the max value */ +}; + +/*-------------------------------------------------------------------------* + * Exterior value b.c. for distance function flags * + *-------------------------------------------------------------------------*/ + +/*! Exterior Value */ +enum { + L_BOUNDARY_BG = 1, /* assume bg outside image */ + L_BOUNDARY_FG = 2 /* assume fg outside image */ +}; + +/*-------------------------------------------------------------------------* + * Image comparison flags * + *-------------------------------------------------------------------------*/ + +/*! Image Comparison */ +enum { + L_COMPARE_XOR = 1, + L_COMPARE_SUBTRACT = 2, + L_COMPARE_ABS_DIFF = 3 +}; + +/*-------------------------------------------------------------------------* + * Color content flags * + *-------------------------------------------------------------------------*/ + +/*! Color Content */ +enum { + L_MAX_DIFF_FROM_AVERAGE_2 = 1, + L_MAX_MIN_DIFF_FROM_2 = 2, + L_MAX_DIFF = 3 +}; + +/*-------------------------------------------------------------------------* + * Standard size of border added around images for special processing * + *-------------------------------------------------------------------------*/ +static const l_int32 ADDED_BORDER = 32; /*!< pixels, not bits */ + + +#endif /* LEPTONICA_MORPH_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/morphapp.c b/hgdriver/3rdparty/hgOCR/leptonica/morphapp.c new file mode 100644 index 0000000..9730584 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/morphapp.c @@ -0,0 +1,1633 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file morphapp.c + *
+ *
+ *      These are some useful and/or interesting composite
+ *      image processing operations, of the type that are often
+ *      useful in applications.  Most are morphological in
+ *      nature.
+ *
+ *      Extraction of boundary pixels
+ *            PIX       *pixExtractBoundary()
+ *
+ *      Selective morph sequence operation under mask
+ *            PIX       *pixMorphSequenceMasked()
+ *
+ *      Selective morph sequence operation on each component
+ *            PIX       *pixMorphSequenceByComponent()
+ *            PIXA      *pixaMorphSequenceByComponent()
+ *
+ *      Selective morph sequence operation on each region
+ *            PIX       *pixMorphSequenceByRegion()
+ *            PIXA      *pixaMorphSequenceByRegion()
+ *
+ *      Union and intersection of parallel composite operations
+ *            PIX       *pixUnionOfMorphOps()
+ *            PIX       *pixIntersectionOfMorphOps()
+ *
+ *      Selective connected component filling
+ *            PIX       *pixSelectiveConnCompFill()
+ *
+ *      Removal of matched patterns
+ *            PIX       *pixRemoveMatchedPattern()
+ *
+ *      Display of matched patterns
+ *            PIX       *pixDisplayMatchedPattern()
+ *
+ *      Extension of pixa by iterative erosion or dilation (and by scaling)
+ *            PIXA      *pixaExtendByMorph()
+ *            PIXA      *pixaExtendByScaling()
+ *
+ *      Iterative morphological seed filling (don't use for real work)
+ *            PIX       *pixSeedfillMorph()
+ *
+ *      Granulometry on binary images
+ *            NUMA      *pixRunHistogramMorph()
+ *
+ *      Composite operations on grayscale images
+ *            PIX       *pixTophat()
+ *            PIX       *pixHDome()
+ *            PIX       *pixFastTophat()
+ *            PIX       *pixMorphGradient()
+ *
+ *      Centroid of component
+ *            PTA       *pixaCentroids()
+ *            l_int32    pixCentroid()
+ * 
+ */ + +#include "allheaders.h" + +#define SWAP(x, y) {temp = (x); (x) = (y); (y) = temp;} + + +/*-----------------------------------------------------------------* + * Extraction of boundary pixels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixExtractBoundary() + * + * \param[in] pixs 1 bpp + * \param[in] type 0 for background pixels; 1 for foreground pixels + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Extracts the fg or bg boundary pixels for each component.
+ *          Components are assumed to end at the boundary of pixs.
+ * 
+ */ +PIX * +pixExtractBoundary(PIX *pixs, + l_int32 type) +{ +PIX *pixd; + + PROCNAME("pixExtractBoundary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (type == 0) + pixd = pixDilateBrick(NULL, pixs, 3, 3); + else + pixd = pixErodeBrick(NULL, pixs, 3, 3); + pixXor(pixd, pixd, pixs); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Selective morph sequence operation under mask * + *-----------------------------------------------------------------*/ +/*! + * \brief pixMorphSequenceMasked() + * + * \param[in] pixs 1 bpp + * \param[in] pixm [optional] 1 bpp mask + * \param[in] sequence string specifying sequence of operations + * \param[in] dispsep horizontal separation in pixels between + * successive displays; use zero to suppress display + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies the morph sequence to the image, but only allows
+ *          changes in pixs for pixels under the background of pixm.
+ *      (5) If pixm is NULL, this is just pixMorphSequence().
+ * 
+ */ +PIX * +pixMorphSequenceMasked(PIX *pixs, + PIX *pixm, + const char *sequence, + l_int32 dispsep) +{ +PIX *pixd; + + PROCNAME("pixMorphSequenceMasked"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + pixd = pixMorphSequence(pixs, sequence, dispsep); + pixCombineMasked(pixd, pixs, pixm); /* restore src pixels under mask fg */ + return pixd; +} + + +/*-----------------------------------------------------------------* + * Morph sequence operation on each component * + *-----------------------------------------------------------------*/ +/*! + * \brief pixMorphSequenceByComponent() + * + * \param[in] pixs 1 bpp + * \param[in] sequence string specifying sequence + * \param[in] connectivity 4 or 8 + * \param[in] minw min width to consider; use 0 or 1 for any width + * \param[in] minh min height to consider; use 0 or 1 for any height + * \param[out] pboxa [optional] return boxa of c.c. in pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixMorphSequence() for composing operation sequences.
+ *      (2) This operates separately on each c.c. in the input pix.
+ *      (3) The dilation does NOT increase the c.c. size; it is clipped
+ *          to the size of the original c.c.   This is necessary to
+ *          keep the c.c. independent after the operation.
+ *      (4) You can specify that the width and/or height must equal
+ *          or exceed a minimum size for the operation to take place.
+ *      (5) Use NULL for boxa to avoid returning the boxa.
+ * 
+ */ +PIX * +pixMorphSequenceByComponent(PIX *pixs, + const char *sequence, + l_int32 connectivity, + l_int32 minw, + l_int32 minh, + BOXA **pboxa) +{ +l_int32 n, i, x, y, w, h; +BOXA *boxa; +PIX *pix, *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixMorphSequenceByComponent"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + if (minw <= 0) minw = 1; + if (minh <= 0) minh = 1; + + /* Get the c.c. */ + if ((boxa = pixConnComp(pixs, &pixas, connectivity)) == NULL) + return (PIX *)ERROR_PTR("boxa not made", procName, NULL); + + /* Operate on each c.c. independently */ + pixad = pixaMorphSequenceByComponent(pixas, sequence, minw, minh); + pixaDestroy(&pixas); + boxaDestroy(&boxa); + if (!pixad) + return (PIX *)ERROR_PTR("pixad not made", procName, NULL); + + /* Display the result out into pixd */ + pixd = pixCreateTemplate(pixs); + n = pixaGetCount(pixad); + for (i = 0; i < n; i++) { + pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); + pix = pixaGetPix(pixad, i, L_CLONE); + pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); + pixDestroy(&pix); + } + + if (pboxa) + *pboxa = pixaGetBoxa(pixad, L_CLONE); + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaMorphSequenceByComponent() + * + * \param[in] pixas of 1 bpp pix + * \param[in] sequence string specifying sequence + * \param[in] minw min width to consider; use 0 or 1 for any width + * \param[in] minh min height to consider; use 0 or 1 for any height + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixMorphSequence() for composing operation sequences.
+ *      (2) This operates separately on each c.c. in the input pixa.
+ *      (3) You can specify that the width and/or height must equal
+ *          or exceed a minimum size for the operation to take place.
+ *      (4) The input pixa should have a boxa giving the locations
+ *          of the pix components.
+ * 
+ */ +PIXA * +pixaMorphSequenceByComponent(PIXA *pixas, + const char *sequence, + l_int32 minw, + l_int32 minh) +{ +l_int32 n, i, w, h, d; +BOX *box; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaMorphSequenceByComponent"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if ((n = pixaGetCount(pixas)) == 0) + return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL); + if (n != pixaGetBoxaCount(pixas)) + L_WARNING("boxa size != n\n", procName); + pixaGetPixDimensions(pixas, 0, NULL, NULL, &d); + if (d != 1) + return (PIXA *)ERROR_PTR("depth not 1 bpp", procName, NULL); + + if (!sequence) + return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); + if (minw <= 0) minw = 1; + if (minh <= 0) minh = 1; + + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + for (i = 0; i < n; i++) { + pixaGetPixDimensions(pixas, i, &w, &h, NULL); + if (w >= minw && h >= minh) { + if ((pix1 = pixaGetPix(pixas, i, L_CLONE)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pix1 not found", procName, NULL); + } + if ((pix2 = pixMorphCompSequence(pix1, sequence, 0)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pix2 not made", procName, NULL); + } + pixaAddPix(pixad, pix2, L_INSERT); + box = pixaGetBox(pixas, i, L_COPY); + pixaAddBox(pixad, box, L_INSERT); + pixDestroy(&pix1); + } + } + + return pixad; +} + + +/*-----------------------------------------------------------------* + * Morph sequence operation on each region * + *-----------------------------------------------------------------*/ +/*! + * \brief pixMorphSequenceByRegion() + * + * \param[in] pixs 1 bpp + * \param[in] pixm mask specifying regions + * \param[in] sequence string specifying sequence + * \param[in] connectivity 4 or 8, used on mask + * \param[in] minw min width to consider; use 0 or 1 for any width + * \param[in] minh min height to consider; use 0 or 1 for any height + * \param[out] pboxa [optional] return boxa of c.c. in pixm + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixMorphCompSequence() for composing operation sequences.
+ *      (2) This operates separately on the region in pixs corresponding
+ *          to each c.c. in the mask pixm.  It differs from
+ *          pixMorphSequenceByComponent() in that the latter does not have
+ *          a pixm (mask), but instead operates independently on each
+ *          component in pixs.
+ *      (3) Dilation will NOT increase the region size; the result
+ *          is clipped to the size of the mask region.  This is necessary
+ *          to make regions independent after the operation.
+ *      (4) You can specify that the width and/or height of a region must
+ *          equal or exceed a minimum size for the operation to take place.
+ *      (5) Use NULL for %pboxa to avoid returning the boxa.
+ * 
+ */ +PIX * +pixMorphSequenceByRegion(PIX *pixs, + PIX *pixm, + const char *sequence, + l_int32 connectivity, + l_int32 minw, + l_int32 minh, + BOXA **pboxa) +{ +l_int32 n, i, x, y, w, h; +BOXA *boxa; +PIX *pix, *pixd; +PIXA *pixam, *pixad; + + PROCNAME("pixMorphSequenceByRegion"); + + if (pboxa) *pboxa = NULL; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixm) + return (PIX *)ERROR_PTR("pixm not defined", procName, NULL); + if (pixGetDepth(pixs) != 1 || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixs and pixm not both 1 bpp", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + if (minw <= 0) minw = 1; + if (minh <= 0) minh = 1; + + /* Get the c.c. of the mask */ + if ((boxa = pixConnComp(pixm, &pixam, connectivity)) == NULL) + return (PIX *)ERROR_PTR("boxa not made", procName, NULL); + + /* Operate on each region in pixs independently */ + pixad = pixaMorphSequenceByRegion(pixs, pixam, sequence, minw, minh); + pixaDestroy(&pixam); + boxaDestroy(&boxa); + if (!pixad) + return (PIX *)ERROR_PTR("pixad not made", procName, NULL); + + /* Display the result out into pixd */ + pixd = pixCreateTemplate(pixs); + n = pixaGetCount(pixad); + for (i = 0; i < n; i++) { + pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); + pix = pixaGetPix(pixad, i, L_CLONE); + pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); + pixDestroy(&pix); + } + + if (pboxa) + *pboxa = pixaGetBoxa(pixad, L_CLONE); + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaMorphSequenceByRegion() + * + * \param[in] pixs 1 bpp + * \param[in] pixam of 1 bpp mask elements + * \param[in] sequence string specifying sequence + * \param[in] minw min width to consider; use 0 or 1 for any width + * \param[in] minh min height to consider; use 0 or 1 for any height + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixMorphSequence() for composing operation sequences.
+ *      (2) This operates separately on each region in the input pixs
+ *          defined by the components in pixam.
+ *      (3) You can specify that the width and/or height of a mask
+ *          component must equal or exceed a minimum size for the
+ *          operation to take place.
+ *      (4) The input pixam should have a boxa giving the locations
+ *          of the regions in pixs.
+ * 
+ */ +PIXA * +pixaMorphSequenceByRegion(PIX *pixs, + PIXA *pixam, + const char *sequence, + l_int32 minw, + l_int32 minh) +{ +l_int32 n, i, w, h, same, maxd, fullpa, fullba; +BOX *box; +PIX *pix1, *pix2, *pix3; +PIXA *pixad; + + PROCNAME("pixaMorphSequenceByRegion"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (!sequence) + return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); + if (!pixam) + return (PIXA *)ERROR_PTR("pixam not defined", procName, NULL); + pixaVerifyDepth(pixam, &same, &maxd); + if (maxd != 1) + return (PIXA *)ERROR_PTR("mask depth not 1 bpp", procName, NULL); + pixaIsFull(pixam, &fullpa, &fullba); + if (!fullpa || !fullba) + return (PIXA *)ERROR_PTR("missing comps in pixam", procName, NULL); + n = pixaGetCount(pixam); + if (minw <= 0) minw = 1; + if (minh <= 0) minh = 1; + + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + + /* Use the rectangle to remove the appropriate part of pixs; + * then AND with the mask component to get the actual fg + * of pixs that is under the mask component. */ + for (i = 0; i < n; i++) { + pixaGetPixDimensions(pixam, i, &w, &h, NULL); + if (w >= minw && h >= minh) { + pix1 = pixaGetPix(pixam, i, L_CLONE); + box = pixaGetBox(pixam, i, L_COPY); + pix2 = pixClipRectangle(pixs, box, NULL); + pixAnd(pix2, pix2, pix1); + pix3 = pixMorphCompSequence(pix2, sequence, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (!pix3) { + boxDestroy(&box); + pixaDestroy(&pixad); + L_ERROR("pix3 not made in iter %d; aborting\n", procName, i); + break; + } + pixaAddPix(pixad, pix3, L_INSERT); + pixaAddBox(pixad, box, L_INSERT); + } + } + + return pixad; +} + + +/*-----------------------------------------------------------------* + * Union and intersection of parallel composite operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixUnionOfMorphOps() + * + * \param[in] pixs 1 bpp + * \param[in] sela + * \param[in] type L_MORPH_DILATE, etc. + * \return pixd union of the specified morphological operation + * on pixs for each Sel in the Sela, or NULL on error + */ +PIX * +pixUnionOfMorphOps(PIX *pixs, + SELA *sela, + l_int32 type) +{ +l_int32 n, i; +PIX *pixt, *pixd; +SEL *sel; + + PROCNAME("pixUnionOfMorphOps"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!sela) + return (PIX *)ERROR_PTR("sela not defined", procName, NULL); + n = selaGetCount(sela); + if (n == 0) + return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); + if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && + type != L_MORPH_OPEN && type != L_MORPH_CLOSE && + type != L_MORPH_HMT) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + pixd = pixCreateTemplate(pixs); + for (i = 0; i < n; i++) { + sel = selaGetSel(sela, i); + if (type == L_MORPH_DILATE) + pixt = pixDilate(NULL, pixs, sel); + else if (type == L_MORPH_ERODE) + pixt = pixErode(NULL, pixs, sel); + else if (type == L_MORPH_OPEN) + pixt = pixOpen(NULL, pixs, sel); + else if (type == L_MORPH_CLOSE) + pixt = pixClose(NULL, pixs, sel); + else /* type == L_MORPH_HMT */ + pixt = pixHMT(NULL, pixs, sel); + pixOr(pixd, pixd, pixt); + pixDestroy(&pixt); + } + + return pixd; +} + + +/*! + * \brief pixIntersectionOfMorphOps() + * + * \param[in] pixs 1 bpp + * \param[in] sela + * \param[in] type L_MORPH_DILATE, etc. + * \return pixd intersection of the specified morphological operation + * on pixs for each Sel in the Sela, or NULL on error + */ +PIX * +pixIntersectionOfMorphOps(PIX *pixs, + SELA *sela, + l_int32 type) +{ +l_int32 n, i; +PIX *pixt, *pixd; +SEL *sel; + + PROCNAME("pixIntersectionOfMorphOps"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!sela) + return (PIX *)ERROR_PTR("sela not defined", procName, NULL); + n = selaGetCount(sela); + if (n == 0) + return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); + if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && + type != L_MORPH_OPEN && type != L_MORPH_CLOSE && + type != L_MORPH_HMT) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixSetAll(pixd); + for (i = 0; i < n; i++) { + sel = selaGetSel(sela, i); + if (type == L_MORPH_DILATE) + pixt = pixDilate(NULL, pixs, sel); + else if (type == L_MORPH_ERODE) + pixt = pixErode(NULL, pixs, sel); + else if (type == L_MORPH_OPEN) + pixt = pixOpen(NULL, pixs, sel); + else if (type == L_MORPH_CLOSE) + pixt = pixClose(NULL, pixs, sel); + else /* type == L_MORPH_HMT */ + pixt = pixHMT(NULL, pixs, sel); + pixAnd(pixd, pixd, pixt); + pixDestroy(&pixt); + } + + return pixd; +} + + + +/*-----------------------------------------------------------------* + * Selective connected component filling * + *-----------------------------------------------------------------*/ +/*! + * \brief pixSelectiveConnCompFill() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \param[in] minw min width to consider; use 0 or 1 for any width + * \param[in] minh min height to consider; use 0 or 1 for any height + * \return pix with holes filled in selected c.c., or NULL on error + */ +PIX * +pixSelectiveConnCompFill(PIX *pixs, + l_int32 connectivity, + l_int32 minw, + l_int32 minh) +{ +l_int32 n, i, x, y, w, h; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; + + PROCNAME("pixSelectiveConnCompFill"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (minw <= 0) minw = 1; + if (minh <= 0) minh = 1; + + if ((boxa = pixConnComp(pixs, &pixa, connectivity)) == NULL) + return (PIX *)ERROR_PTR("boxa not made", procName, NULL); + n = boxaGetCount(boxa); + pixd = pixCopy(NULL, pixs); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + if (w >= minw && h >= minh) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + if ((pix2 = pixHolesByFilling(pix1, 12 - connectivity)) == NULL) { + L_ERROR("pix2 not made in iter %d\n", procName, i); + pixDestroy(&pix1); + continue; + } + pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix2, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + } + pixaDestroy(&pixa); + boxaDestroy(&boxa); + + return pixd; +} + + +/*-----------------------------------------------------------------* + * Removal of matched patterns * + *-----------------------------------------------------------------*/ +/*! + * \brief pixRemoveMatchedPattern() + * + * \param[in] pixs input image, 1 bpp + * \param[in] pixp pattern to be removed from image, 1 bpp + * \param[in] pixe image after erosion by Sel that approximates pixp + * \param[in] x0, y0 center of Sel + * \param[in] dsize number of pixels on each side by which pixp is + * dilated before being subtracted from pixs; + * valid values are {0, 1, 2, 3, 4} + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *    (1) This is in-place.
+ *    (2) You can use various functions in selgen to create a Sel
+ *        that is used to generate pixe from pixs.
+ *    (3) This function is applied after pixe has been computed.
+ *        It finds the centroid of each c.c., and subtracts
+ *        (the appropriately dilated version of) pixp, with the center
+ *        of the Sel used to align pixp with pixs.
+ * 
+ */ +l_ok +pixRemoveMatchedPattern(PIX *pixs, + PIX *pixp, + PIX *pixe, + l_int32 x0, + l_int32 y0, + l_int32 dsize) +{ +l_int32 i, nc, x, y, w, h, xb, yb; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixa; +PTA *pta; +SEL *sel; + + PROCNAME("pixRemoveMatchedPattern"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixp) + return ERROR_INT("pixp not defined", procName, 1); + if (!pixe) + return ERROR_INT("pixe not defined", procName, 1); + if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || + pixGetDepth(pixe) != 1) + return ERROR_INT("all input pix not 1 bpp", procName, 1); + if (dsize < 0 || dsize > 4) + return ERROR_INT("dsize not in {0,1,2,3,4}", procName, 1); + + /* Find the connected components and their centroids */ + boxa = pixConnComp(pixe, &pixa, 8); + if ((nc = boxaGetCount(boxa)) == 0) { + L_WARNING("no matched patterns\n", procName); + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return 0; + } + pta = pixaCentroids(pixa); + pixaDestroy(&pixa); + + /* Optionally dilate the pattern, first adding a border that + * is large enough to accommodate the dilated pixels */ + sel = NULL; + if (dsize > 0) { + sel = selCreateBrick(2 * dsize + 1, 2 * dsize + 1, dsize, dsize, + SEL_HIT); + pix1 = pixAddBorder(pixp, dsize, 0); + pix2 = pixDilate(NULL, pix1, sel); + selDestroy(&sel); + pixDestroy(&pix1); + } else { + pix2 = pixClone(pixp); + } + + /* Subtract out each dilated pattern. The centroid of each + * component is located at: + * (box->x + x, box->y + y) + * and the 'center' of the pattern used in making pixe is located at + * (x0 + dsize, (y0 + dsize) + * relative to the UL corner of the pattern. The center of the + * pattern is placed at the center of the component. */ + pixGetDimensions(pix2, &w, &h, NULL); + for (i = 0; i < nc; i++) { + ptaGetIPt(pta, i, &x, &y); + boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); + pixRasterop(pixs, xb + x - x0 - dsize, yb + y - y0 - dsize, + w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); + } + + boxaDestroy(&boxa); + ptaDestroy(&pta); + pixDestroy(&pix2); + return 0; +} + + +/*-----------------------------------------------------------------* + * Display of matched patterns * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDisplayMatchedPattern() + * + * \param[in] pixs input image, 1 bpp + * \param[in] pixp pattern to be removed from image, 1 bpp + * \param[in] pixe image after erosion by Sel that approximates pixp + * \param[in] x0, y0 center of Sel + * \param[in] color to paint the matched patterns; 0xrrggbb00 + * \param[in] scale reduction factor for output pixd + * \param[in] nlevels if scale < 1.0, threshold to this number of levels + * \return pixd 8 bpp, colormapped, or NULL on error + * + *
+ * Notes:
+ *    (1) A 4 bpp colormapped image is generated.
+ *    (2) If scale <= 1.0, do scale to gray for the output, and threshold
+ *        to nlevels of gray.
+ *    (3) You can use various functions in selgen to create a Sel
+ *        that will generate pixe from pixs.
+ *    (4) This function is applied after pixe has been computed.
+ *        It finds the centroid of each c.c., and colors the output
+ *        pixels using pixp (appropriately aligned) as a stencil.
+ *        Alignment is done using the origin of the Sel and the
+ *        centroid of the eroded image to place the stencil pixp.
+ * 
+ */ +PIX * +pixDisplayMatchedPattern(PIX *pixs, + PIX *pixp, + PIX *pixe, + l_int32 x0, + l_int32 y0, + l_uint32 color, + l_float32 scale, + l_int32 nlevels) +{ +l_int32 i, nc, xb, yb, x, y, xi, yi, rval, gval, bval; +BOXA *boxa; +PIX *pixd, *pixt, *pixps; +PIXA *pixa; +PTA *pta; +PIXCMAP *cmap; + + PROCNAME("pixDisplayMatchedPattern"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixp) + return (PIX *)ERROR_PTR("pixp not defined", procName, NULL); + if (!pixe) + return (PIX *)ERROR_PTR("pixe not defined", procName, NULL); + if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || + pixGetDepth(pixe) != 1) + return (PIX *)ERROR_PTR("all input pix not 1 bpp", procName, NULL); + if (scale > 1.0 || scale <= 0.0) { + L_WARNING("scale > 1.0 or < 0.0; setting to 1.0\n", procName); + scale = 1.0; + } + + /* Find the connected components and their centroids */ + boxa = pixConnComp(pixe, &pixa, 8); + if ((nc = boxaGetCount(boxa)) == 0) { + L_WARNING("no matched patterns\n", procName); + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return 0; + } + pta = pixaCentroids(pixa); + + extractRGBValues(color, &rval, &gval, &bval); + if (scale == 1.0) { /* output 4 bpp at full resolution */ + pixd = pixConvert1To4(NULL, pixs, 0, 1); + cmap = pixcmapCreate(4); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixSetColormap(pixd, cmap); + + /* Paint through pixp for each match location. The centroid of each + * component in pixe is located at: + * (box->x + x, box->y + y) + * and the 'center' of the pattern used in making pixe is located at + * (x0, y0) + * relative to the UL corner of the pattern. The center of the + * pattern is placed at the center of the component. */ + for (i = 0; i < nc; i++) { + ptaGetIPt(pta, i, &x, &y); + boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); + pixSetMaskedCmap(pixd, pixp, xb + x - x0, yb + y - y0, + rval, gval, bval); + } + } else { /* output 4 bpp downscaled */ + pixt = pixScaleToGray(pixs, scale); + pixd = pixThresholdTo4bpp(pixt, nlevels, 1); + pixps = pixScaleBySampling(pixp, scale, scale); + + for (i = 0; i < nc; i++) { + ptaGetIPt(pta, i, &x, &y); + boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); + xi = (l_int32)(scale * (xb + x - x0)); + yi = (l_int32)(scale * (yb + y - y0)); + pixSetMaskedCmap(pixd, pixps, xi, yi, rval, gval, bval); + } + pixDestroy(&pixt); + pixDestroy(&pixps); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + ptaDestroy(&pta); + return pixd; +} + + +/*------------------------------------------------------------------------* + * Extension of pixa by iterative erosion or dilation (and by scaling) * + *------------------------------------------------------------------------*/ +/*! + * \brief pixaExtendByMorph() + * + * \param[in] pixas + * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE + * \param[in] niters + * \param[in] sel used for dilation, erosion; uses 2x2 if null + * \param[in] include 1 to include a copy of the input pixas in pixad; + * 0 to omit + * \return pixad with derived pix, using all iterations, or NULL on error + * + *
+ * Notes:
+ *    (1) This dilates or erodes every pix in %pixas, iteratively,
+ *        using the input Sel (or, if null, a 2x2 Sel by default),
+ *        and puts the results in %pixad.
+ *    (2) If %niters <= 0, this is a no-op; it returns a clone of pixas.
+ *    (3) If %include == 1, the output %pixad contains all the pix
+ *        in %pixas.  Otherwise, it doesn't, but pixaJoin() can be
+ *        used later to join pixas with pixad.
+ * 
+ */ +PIXA * +pixaExtendByMorph(PIXA *pixas, + l_int32 type, + l_int32 niters, + SEL *sel, + l_int32 include) +{ +l_int32 maxdepth, i, j, n; +PIX *pix0, *pix1, *pix2; +SEL *selt; +PIXA *pixad; + + PROCNAME("pixaExtendByMorph"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas undefined", procName, NULL); + if (niters <= 0) { + L_INFO("niters = %d; nothing to do\n", procName, niters); + return pixaCopy(pixas, L_CLONE); + } + if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + pixaGetDepthInfo(pixas, &maxdepth, NULL); + if (maxdepth > 1) + return (PIXA *)ERROR_PTR("some pix have bpp > 1", procName, NULL); + + if (!sel) + selt = selCreateBrick(2, 2, 0, 0, SEL_HIT); /* default */ + else + selt = selCopy(sel); + n = pixaGetCount(pixas); + pixad = pixaCreate(n * niters); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + if (include) pixaAddPix(pixad, pix1, L_COPY); + pix0 = pix1; /* need to keep the handle to destroy the clone */ + for (j = 0; j < niters; j++) { + if (type == L_MORPH_DILATE) { + pix2 = pixDilate(NULL, pix1, selt); + } else { /* L_MORPH_ERODE */ + pix2 = pixErode(NULL, pix1, selt); + } + pixaAddPix(pixad, pix2, L_INSERT); + pix1 = pix2; /* owned by pixad; do not destroy */ + } + pixDestroy(&pix0); + } + + selDestroy(&selt); + return pixad; +} + + +/*! + * \brief pixaExtendByScaling() + * + * \param[in] pixas + * \param[in] nasc numa of scaling factors + * \param[in] type L_HORIZ, L_VERT, L_BOTH_DIRECTIONS + * \param[in] include 1 to include a copy of the input pixas in pixad; + * 0 to omit + * \return pixad with derived pix, using all scalings, or NULL on error + * + *
+ * Notes:
+ *    (1) This scales every pix in %pixas by each factor in %nasc.
+ *        and puts the results in %pixad.
+ *    (2) If %include == 1, the output %pixad contains all the pix
+ *        in %pixas.  Otherwise, it doesn't, but pixaJoin() can be
+ *        used later to join pixas with pixad.
+ * 
+ */ +PIXA * +pixaExtendByScaling(PIXA *pixas, + NUMA *nasc, + l_int32 type, + l_int32 include) +{ +l_int32 i, j, n, nsc, w, h, scalew, scaleh; +l_float32 scalefact; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaExtendByScaling"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas undefined", procName, NULL); + if (!nasc || numaGetCount(nasc) == 0) + return (PIXA *)ERROR_PTR("nasc undefined or empty", procName, NULL); + if (type != L_HORIZ && type != L_VERT && type != L_BOTH_DIRECTIONS) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + + n = pixaGetCount(pixas); + nsc = numaGetCount(nasc); + if ((pixad = pixaCreate(n * (nsc + 1))) == NULL) { + L_ERROR("pixad not made: n = %d, nsc = %d\n", procName, n, nsc); + return NULL; + } + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + if (include) pixaAddPix(pixad, pix1, L_COPY); + pixGetDimensions(pix1, &w, &h, NULL); + for (j = 0; j < nsc; j++) { + numaGetFValue(nasc, j, &scalefact); + scalew = w; + scaleh = h; + if (type == L_HORIZ || type == L_BOTH_DIRECTIONS) + scalew = w * scalefact; + if (type == L_VERT || type == L_BOTH_DIRECTIONS) + scaleh = h * scalefact; + pix2 = pixScaleToSize(pix1, scalew, scaleh); + pixaAddPix(pixad, pix2, L_INSERT); + } + pixDestroy(&pix1); + } + return pixad; +} + + +/*-----------------------------------------------------------------* + * Iterative morphological seed filling * + *-----------------------------------------------------------------*/ +/*! + * \brief pixSeedfillMorph() + * + * \param[in] pixs seed + * \param[in] pixm mask + * \param[in] maxiters use 0 to go to completion + * \param[in] connectivity 4 or 8 + * \return pixd after filling into the mask or NULL on error + * + *
+ * Notes:
+ *    (1) This is in general a very inefficient method for filling
+ *        from a seed into a mask.  Use it for a small number of iterations,
+ *        but if you expect more than a few iterations, use
+ *        pixSeedfillBinary().
+ *    (2) We use a 3x3 brick SEL for 8-cc filling and a 3x3 plus SEL for 4-cc.
+ * 
+ */ +PIX * +pixSeedfillMorph(PIX *pixs, + PIX *pixm, + l_int32 maxiters, + l_int32 connectivity) +{ +l_int32 same, i; +PIX *pixt, *pixd, *temp; +SEL *sel_3; + + PROCNAME("pixSeedfillMorph"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!pixm) + return (PIX *)ERROR_PTR("mask pix not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL); + if (maxiters <= 0) maxiters = 1000; + if (pixSizesEqual(pixs, pixm) == 0) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, NULL); + + if ((sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT)) == NULL) + return (PIX *)ERROR_PTR("sel_3 not made", procName, NULL); + if (connectivity == 4) { /* remove corner hits to make a '+' */ + selSetElement(sel_3, 0, 0, SEL_DONT_CARE); + selSetElement(sel_3, 2, 2, SEL_DONT_CARE); + selSetElement(sel_3, 2, 0, SEL_DONT_CARE); + selSetElement(sel_3, 0, 2, SEL_DONT_CARE); + } + + pixt = pixCopy(NULL, pixs); + pixd = pixCreateTemplate(pixs); + for (i = 1; i <= maxiters; i++) { + pixDilate(pixd, pixt, sel_3); + pixAnd(pixd, pixd, pixm); + pixEqual(pixd, pixt, &same); + if (same || i == maxiters) + break; + else + SWAP(pixt, pixd); + } + fprintf(stderr, " Num iters in binary reconstruction = %d\n", i); + + pixDestroy(&pixt); + selDestroy(&sel_3); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Granulometry on binary images * + *-----------------------------------------------------------------*/ +/*! + * \brief pixRunHistogramMorph() + * + * \param[in] pixs 1 bpp + * \param[in] runtype L_RUN_OFF, L_RUN_ON + * \param[in] direction L_HORIZ, L_VERT + * \param[in] maxsize size of largest runlength counted + * \return numa of run-lengths + */ +NUMA * +pixRunHistogramMorph(PIX *pixs, + l_int32 runtype, + l_int32 direction, + l_int32 maxsize) +{ +l_int32 count, i, size; +l_float32 val; +NUMA *na, *nah; +PIX *pix1, *pix2, *pix3; +SEL *sel_2a; + + PROCNAME("pixRunHistogramMorph"); + + if (!pixs) + return (NUMA *)ERROR_PTR("seed pix not defined", procName, NULL); + if (runtype != L_RUN_OFF && runtype != L_RUN_ON) + return (NUMA *)ERROR_PTR("invalid run type", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT) + return (NUMA *)ERROR_PTR("direction not in {L_HORIZ, L_VERT}", + procName, NULL); + if (pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs must be binary", procName, NULL); + + if (direction == L_HORIZ) + sel_2a = selCreateBrick(1, 2, 0, 0, SEL_HIT); + else /* direction == L_VERT */ + sel_2a = selCreateBrick(2, 1, 0, 0, SEL_HIT); + if (!sel_2a) + return (NUMA *)ERROR_PTR("sel_2a not made", procName, NULL); + + if (runtype == L_RUN_OFF) { + if ((pix1 = pixCopy(NULL, pixs)) == NULL) { + selDestroy(&sel_2a); + return (NUMA *)ERROR_PTR("pix1 not made", procName, NULL); + } + pixInvert(pix1, pix1); + } else { /* runtype == L_RUN_ON */ + pix1 = pixClone(pixs); + } + + /* Get pixel counts at different stages of erosion */ + na = numaCreate(0); + pix2 = pixCreateTemplate(pixs); + pix3 = pixCreateTemplate(pixs); + pixCountPixels(pix1, &count, NULL); + numaAddNumber(na, count); + pixErode(pix2, pix1, sel_2a); + pixCountPixels(pix2, &count, NULL); + numaAddNumber(na, count); + for (i = 0; i < maxsize / 2; i++) { + pixErode(pix3, pix2, sel_2a); + pixCountPixels(pix3, &count, NULL); + numaAddNumber(na, count); + pixErode(pix2, pix3, sel_2a); + pixCountPixels(pix2, &count, NULL); + numaAddNumber(na, count); + } + + /* Compute length histogram */ + size = numaGetCount(na); + nah = numaCreate(size); + numaAddNumber(nah, 0); /* number at length 0 */ + for (i = 1; i < size - 1; i++) { + val = na->array[i+1] - 2 * na->array[i] + na->array[i-1]; + numaAddNumber(nah, val); + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + selDestroy(&sel_2a); + numaDestroy(&na); + return nah; +} + + +/*-----------------------------------------------------------------* + * Composite operations on grayscale images * + *-----------------------------------------------------------------*/ +/*! + * \brief pixTophat() + * + * \param[in] pixs 1 bpp + * \param[in] hsize of Sel; must be odd; origin implicitly in center + * \param[in] vsize ditto + * \param[in] type L_TOPHAT_WHITE: image - opening + * L_TOPHAT_BLACK: closing - image + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Sel is a brick with all elements being hits
+ *      (2) If hsize = vsize = 1, returns an image with all 0 data.
+ *      (3) The L_TOPHAT_WHITE flag emphasizes small bright regions,
+ *          whereas the L_TOPHAT_BLACK flag emphasizes small dark regions.
+ *          The L_TOPHAT_WHITE tophat can be accomplished by doing a
+ *          L_TOPHAT_BLACK tophat on the inverse, or v.v.
+ * 
+ */ +PIX * +pixTophat(PIX *pixs, + l_int32 hsize, + l_int32 vsize, + l_int32 type) +{ +PIX *pixt, *pixd; + + PROCNAME("pixTophat"); + + if (!pixs) + return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) + return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", + procName, NULL); + + if (hsize == 1 && vsize == 1) + return pixCreateTemplate(pixs); + + switch (type) + { + case L_TOPHAT_WHITE: + if ((pixt = pixOpenGray(pixs, hsize, vsize)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixSubtractGray(NULL, pixs, pixt); + pixDestroy(&pixt); + break; + case L_TOPHAT_BLACK: + if ((pixd = pixCloseGray(pixs, hsize, vsize)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSubtractGray(pixd, pixd, pixs); + break; + default: + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + } + + return pixd; +} + + +/*! + * \brief pixHDome() + * + * \param[in] pixs 8 bpp, filling mask + * \param[in] height of seed below the filling maskhdome; must be >= 0 + * \param[in] connectivity 4 or 8 + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) It is more efficient to use a connectivity of 4 for the fill.
+ *      (2) This fills bumps to some level, and extracts the unfilled
+ *          part of the bump.  To extract the troughs of basins, first
+ *          invert pixs and then apply pixHDome().
+ *      (3) It is useful to compare the HDome operation with the TopHat.
+ *          The latter extracts peaks or valleys that have a width
+ *          not exceeding the size of the structuring element used
+ *          in the opening or closing, rsp.  The height of the peak is
+ *          irrelevant.  By contrast, for the HDome, the gray seedfill
+ *          is used to extract all peaks that have a height not exceeding
+ *          a given value, regardless of their width!
+ *      (4) Slightly more precisely, suppose you set 'height' = 40.
+ *          Then all bumps in pixs with a height greater than or equal
+ *          to 40 become, in pixd, bumps with a max value of exactly 40.
+ *          All shorter bumps have a max value in pixd equal to the height
+ *          of the bump.
+ *      (5) The method: the filling mask, pixs, is the image whose peaks
+ *          are to be extracted.  The height of a peak is the distance
+ *          between the top of the peak and the highest "leak" to the
+ *          outside -- think of a sombrero, where the leak occurs
+ *          at the highest point on the rim.
+ *            (a) Generate a seed, pixd, by subtracting some value, p, from
+ *                each pixel in the filling mask, pixs.  The value p is
+ *                the 'height' input to this function.
+ *            (b) Fill in pixd starting with this seed, clipping by pixs,
+ *                in the way described in seedfillGrayLow().  The filling
+ *                stops before the peaks in pixs are filled.
+ *                For peaks that have a height > p, pixd is filled to
+ *                the level equal to the (top-of-the-peak - p).
+ *                For peaks of height < p, the peak is left unfilled
+ *                from its highest saddle point (the leak to the outside).
+ *            (c) Subtract the filled seed (pixd) from the filling mask (pixs).
+ *          Note that in this procedure, everything is done starting
+ *          with the filling mask, pixs.
+ *      (6) For segmentation, the resulting image, pixd, can be thresholded
+ *          and used as a seed for another filling operation.
+ * 
+ */ +PIX * +pixHDome(PIX *pixs, + l_int32 height, + l_int32 connectivity) +{ +PIX *pixsd, *pixd; + + PROCNAME("pixHDome"); + + if (!pixs) + return (PIX *)ERROR_PTR("src pix not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (height < 0) + return (PIX *)ERROR_PTR("height not >= 0", procName, NULL); + if (height == 0) + return pixCreateTemplate(pixs); + + if ((pixsd = pixCopy(NULL, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixsd not made", procName, NULL); + pixAddConstantGray(pixsd, -height); + pixSeedfillGray(pixsd, pixs, connectivity); + pixd = pixSubtractGray(NULL, pixs, pixsd); + pixDestroy(&pixsd); + return pixd; +} + + +/*! + * \brief pixFastTophat() + * + * \param[in] pixs 8 bpp + * \param[in] xsize width of max/min op, smoothing; any integer >= 1 + * \param[in] ysize height of max/min op, smoothing; any integer >= 1 + * \param[in] type L_TOPHAT_WHITE: image - min + * L_TOPHAT_BLACK: max - image + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Don't be fooled. This is NOT a tophat.  It is a tophat-like
+ *          operation, where the result is similar to what you'd get
+ *          if you used an erosion instead of an opening, or a dilation
+ *          instead of a closing.
+ *      (2) Instead of opening or closing at full resolution, it does
+ *          a fast downscale/minmax operation, then a quick small smoothing
+ *          at low res, a replicative expansion of the "background"
+ *          to full res, and finally a removal of the background level
+ *          from the input image.  The smoothing step may not be important.
+ *      (3) It does not remove noise as well as a tophat, but it is
+ *          5 to 10 times faster.
+ *          If you need the preciseness of the tophat, don't use this.
+ *      (4) The L_TOPHAT_WHITE flag emphasizes small bright regions,
+ *          whereas the L_TOPHAT_BLACK flag emphasizes small dark regions.
+ * 
+ */ +PIX * +pixFastTophat(PIX *pixs, + l_int32 xsize, + l_int32 ysize, + l_int32 type) +{ +PIX *pix1, *pix2, *pix3, *pixd; + + PROCNAME("pixFastTophat"); + + if (!pixs) + return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (xsize < 1 || ysize < 1) + return (PIX *)ERROR_PTR("size < 1", procName, NULL); + if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) + return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", + procName, NULL); + + if (xsize == 1 && ysize == 1) + return pixCreateTemplate(pixs); + + switch (type) + { + case L_TOPHAT_WHITE: + if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MIN)) + == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ + pix3 = pixScaleBySampling(pix2, xsize, ysize); + pixd = pixSubtractGray(NULL, pixs, pix3); + pixDestroy(&pix3); + break; + case L_TOPHAT_BLACK: + if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MAX)) + == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ + pixd = pixScaleBySampling(pix2, xsize, ysize); + pixSubtractGray(pixd, pixd, pixs); + break; + default: + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief pixMorphGradient() + * + * \param[in] pixs 8 bpp + * \param[in] hsize sel width; must be odd; origin implicitly in center + * \param[in] vsize sel height + * \param[in] smoothing half-width of convolution smoothing filter. + * The width is (2 * smoothing + 1, so 0 is no-op. + * \return pixd, or NULL on error + */ +PIX * +pixMorphGradient(PIX *pixs, + l_int32 hsize, + l_int32 vsize, + l_int32 smoothing) +{ +PIX *pixg, *pixd; + + PROCNAME("pixMorphGradient"); + + if (!pixs) + return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); + if ((hsize & 1) == 0 ) { + L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); + hsize++; + } + if ((vsize & 1) == 0 ) { + L_WARNING("vert sel size must be odd; increasing by 1\n", procName); + vsize++; + } + + /* Optionally smooth first to remove noise. + * If smoothing is 0, just get a copy */ + pixg = pixBlockconvGray(pixs, NULL, smoothing, smoothing); + + /* This gives approximately the gradient of a transition */ + pixd = pixDilateGray(pixg, hsize, vsize); + pixSubtractGray(pixd, pixd, pixg); + pixDestroy(&pixg); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Centroid of component * + *-----------------------------------------------------------------*/ +/*! + * \brief pixaCentroids() + * + * \param[in] pixa of components; 1 or 8 bpp + * \return pta of centroids relative to the UL corner of + * each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) An error message is returned if any pix has something other
+ *          than 1 bpp or 8 bpp depth, and the centroid from that pix
+ *          is saved as (0, 0).
+ * 
+ */ +PTA * +pixaCentroids(PIXA *pixa) +{ +l_int32 i, n; +l_int32 *centtab = NULL; +l_int32 *sumtab = NULL; +l_float32 x, y; +PIX *pix; +PTA *pta; + + PROCNAME("pixaCentroids"); + + if (!pixa) + return (PTA *)ERROR_PTR("pixa not defined", procName, NULL); + if ((n = pixaGetCount(pixa)) == 0) + return (PTA *)ERROR_PTR("no pix in pixa", procName, NULL); + + if ((pta = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("pta not defined", procName, NULL); + centtab = makePixelCentroidTab8(); + sumtab = makePixelSumTab8(); + + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + if (pixCentroid(pix, centtab, sumtab, &x, &y) == 1) + L_ERROR("centroid failure for pix %d\n", procName, i); + pixDestroy(&pix); + ptaAddPt(pta, x, y); + } + + LEPT_FREE(centtab); + LEPT_FREE(sumtab); + return pta; +} + + +/*! + * \brief pixCentroid() + * + * \param[in] pix 1 or 8 bpp + * \param[in] centtab [optional] table for finding centroids; can be null + * \param[in] sumtab [optional] table for finding pixel sums; can be null + * \param[out] pxave x coordinate of centroid, relative to the UL corner + * of the pix + * \param[out] pyave y coordinate of centroid, relative to the UL corner + * of the pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The sum and centroid tables are only used for 1 bpp.
+ *      (2) Any table not passed in will be made internally and destroyed
+ *          after use.
+ * 
+ */ +l_ok +pixCentroid(PIX *pix, + l_int32 *centtab, + l_int32 *sumtab, + l_float32 *pxave, + l_float32 *pyave) +{ +l_int32 w, h, d, i, j, wpl, pixsum, rowsum, val; +l_float32 xsum, ysum; +l_uint32 *data, *line; +l_uint32 word; +l_uint8 byte; +l_int32 *ctab, *stab; + + PROCNAME("pixCentroid"); + + if (!pxave || !pyave) + return ERROR_INT("&pxave and &pyave not defined", procName, 1); + *pxave = *pyave = 0.0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 8) + return ERROR_INT("pix not 1 or 8 bpp", procName, 1); + + ctab = centtab; + stab = sumtab; + if (d == 1) { + pixSetPadBits(pix, 0); + if (!centtab) + ctab = makePixelCentroidTab8(); + if (!sumtab) + stab = makePixelSumTab8(); + } + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + xsum = ysum = 0.0; + pixsum = 0; + if (d == 1) { + for (i = 0; i < h; i++) { + /* The body of this loop computes the sum of the set + * (1) bits on this row, weighted by their distance + * from the left edge of pix, and accumulates that into + * xsum; it accumulates their distance from the top + * edge of pix into ysum, and their total count into + * pixsum. It's equivalent to + * for (j = 0; j < w; j++) { + * if (GET_DATA_BIT(line, j)) { + * xsum += j; + * ysum += i; + * pixsum++; + * } + * } + */ + line = data + wpl * i; + rowsum = 0; + for (j = 0; j < wpl; j++) { + word = line[j]; + if (word) { + byte = word & 0xff; + rowsum += stab[byte]; + xsum += ctab[byte] + (j * 32 + 24) * stab[byte]; + byte = (word >> 8) & 0xff; + rowsum += stab[byte]; + xsum += ctab[byte] + (j * 32 + 16) * stab[byte]; + byte = (word >> 16) & 0xff; + rowsum += stab[byte]; + xsum += ctab[byte] + (j * 32 + 8) * stab[byte]; + byte = (word >> 24) & 0xff; + rowsum += stab[byte]; + xsum += ctab[byte] + j * 32 * stab[byte]; + } + } + pixsum += rowsum; + ysum += rowsum * i; + } + if (pixsum == 0) { + L_WARNING("no ON pixels in pix\n", procName); + } else { + *pxave = xsum / (l_float32)pixsum; + *pyave = ysum / (l_float32)pixsum; + } + } else { /* d == 8 */ + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(line, j); + xsum += val * j; + ysum += val * i; + pixsum += val; + } + } + if (pixsum == 0) { + L_WARNING("all pixels are 0\n", procName); + } else { + *pxave = xsum / (l_float32)pixsum; + *pyave = ysum / (l_float32)pixsum; + } + } + + if (!centtab) LEPT_FREE(ctab); + if (!sumtab) LEPT_FREE(stab); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/morphdwa.c b/hgdriver/3rdparty/hgOCR/leptonica/morphdwa.c new file mode 100644 index 0000000..b5da0b3 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/morphdwa.c @@ -0,0 +1,1596 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file morphdwa.c + *
+ *
+ *    Binary morphological (dwa) ops with brick Sels
+ *         PIX     *pixDilateBrickDwa()
+ *         PIX     *pixErodeBrickDwa()
+ *         PIX     *pixOpenBrickDwa()
+ *         PIX     *pixCloseBrickDwa()
+ *
+ *    Binary composite morphological (dwa) ops with brick Sels
+ *         PIX     *pixDilateCompBrickDwa()
+ *         PIX     *pixErodeCompBrickDwa()
+ *         PIX     *pixOpenCompBrickDwa()
+ *         PIX     *pixCloseCompBrickDwa()
+ *
+ *    Binary extended composite morphological (dwa) ops with brick Sels
+ *         PIX     *pixDilateCompBrickExtendDwa()
+ *         PIX     *pixErodeCompBrickExtendDwa()
+ *         PIX     *pixOpenCompBrickExtendDwa()
+ *         PIX     *pixCloseCompBrickExtendDwa()
+ *         l_int32  getExtendedCompositeParameters()
+ *
+ *    These are higher-level interfaces for dwa morphology with brick Sels.
+ *    Because many morphological operations are performed using
+ *    separable brick Sels, it is useful to have a simple interface
+ *    for this.
+ *
+ *    We have included all 58 of the brick Sels that are generated
+ *    by selaAddBasic().  These are sufficient for all the decomposable
+ *    bricks up to size 63, which is the limit for dwa Sels with
+ *    origins at the center of the Sel.
+ *
+ *    All three sets can be used as the basic interface for general
+ *    brick operations.  Here are the internal calling sequences:
+ *
+ *      (1) If you try to apply a non-decomposable operation, such as
+ *          pixErodeBrickDwa(), with a Sel size that doesn't exist,
+ *          this calls a decomposable operation, pixErodeCompBrickDwa(),
+ *          instead.  This can differ in linear Sel size by up to
+ *          2 pixels from the request.
+ *
+ *      (2) If either Sel brick dimension is greater than 63, the extended
+ *          composite function is called.
+ *
+ *      (3) The extended composite function calls the composite function
+ *          a number of times with size 63, and once with size < 63.
+ *          Because each operation with a size of 63 is done compositely
+ *          with 7 x 9 (exactly 63), the net result is correct in
+ *          length to within 2 pixels.
+ *
+ *    For composite operations, both using a comb and extended (beyond 63),
+ *    horizontal and vertical operations are composed separately
+ *    and sequentially.
+ *
+ *    We have also included use of all the 76 comb Sels that are generated
+ *    by selaAddDwaCombs().  The generated code is in dwacomb.2.c
+ *    and dwacomblow.2.c.  These are used for the composite dwa
+ *    brick operations.
+ *
+ *    The non-composite brick operations, such as pixDilateBrickDwa(),
+ *    will call the associated composite operation in situations where
+ *    the requisite brick Sel has not been compiled into fmorphgen*.1.c.
+ *
+ *    If you want to use brick Sels that are not represented in the
+ *    basic set of 58, you must generate the dwa code to implement them.
+ *    You have three choices for how to use these:
+ *
+ *    (1) Add both the new Sels and the dwa code to the library:
+ *        ~ For simplicity, add your new brick Sels to those defined
+ *          in selaAddBasic().
+ *        ~ Recompile the library.
+ *        ~ Make prog/fmorphautogen.
+ *        ~ Run prog/fmorphautogen, to generate new versions of the
+ *          dwa code in fmorphgen.1.c and fmorphgenlow.1.c.
+ *        ~ Copy these two files to src.
+ *        ~ Recompile the library again.
+ *        ~ Use the new brick Sels in your program and compile it.
+ *
+ *    (2) Make both the new Sels and dwa code outside the library,
+ *        and link it directly to an executable:
+ *        ~ Write a function to generate the new Sels in a Sela, and call
+ *          fmorphautogen(sela, , filename) to generate the code.
+ *        ~ Compile your program that uses the newly generated function
+ *          pixMorphDwa_(), and link to the two new C files.
+ *
+ *    (3) Make the new Sels in the library and use the dwa code outside it:
+ *        ~ Add code in the library to generate your new brick Sels.
+ *          (It is suggested that you NOT add these Sels to the
+ *          selaAddBasic() function; write a new function that generates
+ *          a new Sela.)
+ *        ~ Recompile the library.
+ *        ~ Write a small program that generates the Sela and calls
+ *          fmorphautogen(sela, , filename) to generate the code.
+ *        ~ Compile your program that uses the newly generated function
+ *          pixMorphDwa_(), and link to the two new C files.
+ *       As an example of this approach, see prog/dwamorph*_reg.c:
+ *        ~ added selaAddDwaLinear() to sel2.c
+ *        ~ wrote dwamorph1_reg.c, to generate the dwa code.
+ *        ~ compiled and linked the generated code with the application,
+ *          dwamorph2_reg.c.  (Note: because this was a regression test,
+ *          dwamorph1_reg also builds and runs the application program.)
+ * 
+ */ + +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_SEL_LOOKUP 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-----------------------------------------------------------------* + * Binary morphological (dwa) ops with brick Sels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDilateBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement 2D brick Sels, using linear Sels generated
+ *          with selaAddBasic().
+ *      (2) A brick Sel has hits for all elements.
+ *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (6) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (7) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixDilateBrickDwa(NULL, pixs, ...);
+ *          (b) pixDilateBrickDwa(pixs, pixs, ...);
+ *          (c) pixDilateBrickDwa(pixd, pixs, ...);
+ *      (8) The size of pixd is determined by pixs.
+ *      (9) If either linear Sel is not found, this calls
+ *          the appropriate decomposible function.
+ * 
+ */ +PIX * +pixDilateBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 found; +char *selnameh, *selnamev; +SELA *sela; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixDilateBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + sela = selaAddBasic(NULL); + found = TRUE; + selnameh = selnamev = NULL; + if (hsize > 1) { + selnameh = selaGetBrickName(sela, hsize, 1); + if (!selnameh) found = FALSE; + } + if (vsize > 1) { + selnamev = selaGetBrickName(sela, 1, vsize); + if (!selnamev) found = FALSE; + } + selaDestroy(&sela); + if (!found) { + L_INFO("Calling the decomposable dwa function\n", procName); + if (selnameh) LEPT_FREE(selnameh); + if (selnamev) LEPT_FREE(selnamev); + return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); + } + + if (vsize == 1) { + pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); + LEPT_FREE(selnameh); + } else if (hsize == 1) { + pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); + LEPT_FREE(selnamev); + } else { + pixt1 = pixAddBorder(pixs, 32, 0); + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); + pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev); + pixt2 = pixRemoveBorder(pixt1, 32); + pixDestroy(&pixt1); + pixDestroy(&pixt3); + LEPT_FREE(selnameh); + LEPT_FREE(selnamev); + } + + if (!pixd) + return pixt2; + + pixTransferAllData(pixd, &pixt2, 0, 0); + return pixd; +} + + +/*! + * \brief pixErodeBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement 2D brick Sels, using linear Sels generated
+ *          with selaAddBasic().
+ *      (2) A brick Sel has hits for all elements.
+ *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (6) Note that we must always set or clear the border pixels
+ *          before each operation, depending on the the b.c.
+ *          (symmetric or asymmetric).
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixErodeBrickDwa(NULL, pixs, ...);
+ *          (b) pixErodeBrickDwa(pixs, pixs, ...);
+ *          (c) pixErodeBrickDwa(pixd, pixs, ...);
+ *      (9) The size of the result is determined by pixs.
+ *      (10) If either linear Sel is not found, this calls
+ *           the appropriate decomposible function.
+ * 
+ */ +PIX * +pixErodeBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 found; +char *selnameh, *selnamev; +SELA *sela; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixErodeBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + sela = selaAddBasic(NULL); + found = TRUE; + selnameh = selnamev = NULL; + if (hsize > 1) { + selnameh = selaGetBrickName(sela, hsize, 1); + if (!selnameh) found = FALSE; + } + if (vsize > 1) { + selnamev = selaGetBrickName(sela, 1, vsize); + if (!selnamev) found = FALSE; + } + selaDestroy(&sela); + if (!found) { + L_INFO("Calling the decomposable dwa function\n", procName); + if (selnameh) LEPT_FREE(selnameh); + if (selnamev) LEPT_FREE(selnamev); + return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); + } + + if (vsize == 1) { + pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); + LEPT_FREE(selnameh); + } else if (hsize == 1) { + pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); + LEPT_FREE(selnamev); + } else { + pixt1 = pixAddBorder(pixs, 32, 0); + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); + pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev); + pixt2 = pixRemoveBorder(pixt1, 32); + pixDestroy(&pixt1); + pixDestroy(&pixt3); + LEPT_FREE(selnameh); + LEPT_FREE(selnamev); + } + + if (!pixd) + return pixt2; + + pixTransferAllData(pixd, &pixt2, 0, 0); + return pixd; +} + + +/*! + * \brief pixOpenBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement 2D brick Sels, using linear Sels generated
+ *          with selaAddBasic().
+ *      (2) A brick Sel has hits for all elements.
+ *      (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (4) Do separably if both hsize and vsize are > 1.
+ *      (5) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (6) Note that we must always set or clear the border pixels
+ *          before each operation, depending on the the b.c.
+ *          (symmetric or asymmetric).
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpenBrickDwa(NULL, pixs, ...);
+ *          (b) pixOpenBrickDwa(pixs, pixs, ...);
+ *          (c) pixOpenBrickDwa(pixd, pixs, ...);
+ *      (9) The size of the result is determined by pixs.
+ *      (10) If either linear Sel is not found, this calls
+ *           the appropriate decomposible function.
+ * 
+ */ +PIX * +pixOpenBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 found; +char *selnameh, *selnamev; +SELA *sela; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixOpenBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + sela = selaAddBasic(NULL); + found = TRUE; + selnameh = selnamev = NULL; + if (hsize > 1) { + selnameh = selaGetBrickName(sela, hsize, 1); + if (!selnameh) found = FALSE; + } + if (vsize > 1) { + selnamev = selaGetBrickName(sela, 1, vsize); + if (!selnamev) found = FALSE; + } + selaDestroy(&sela); + if (!found) { + L_INFO("Calling the decomposable dwa function\n", procName); + if (selnameh) LEPT_FREE(selnameh); + if (selnamev) LEPT_FREE(selnamev); + return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize); + } + + pixt1 = pixAddBorder(pixs, 32, 0); + if (vsize == 1) { /* horizontal only */ + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh); + LEPT_FREE(selnameh); + } else if (hsize == 1) { /* vertical only */ + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); + LEPT_FREE(selnamev); + } else { /* do separable */ + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev); + LEPT_FREE(selnameh); + LEPT_FREE(selnamev); + pixDestroy(&pixt3); + } + pixt3 = pixRemoveBorder(pixt2, 32); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixTransferAllData(pixd, &pixt3, 0, 0); + return pixd; +} + + +/*! + * \brief pixCloseBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) This is a 'safe' closing; we add an extra border of 32 OFF
+ *          pixels for the standard asymmetric b.c.
+ *      (2) These implement 2D brick Sels, using linear Sels generated
+ *          with selaAddBasic().
+ *      (3) A brick Sel has hits for all elements.
+ *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (5) Do separably if both hsize and vsize are > 1.
+ *      (6) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (7) Note that we must always set or clear the border pixels
+ *          before each operation, depending on the the b.c.
+ *          (symmetric or asymmetric).
+ *      (8) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (9) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseBrickDwa(NULL, pixs, ...);
+ *          (b) pixCloseBrickDwa(pixs, pixs, ...);
+ *          (c) pixCloseBrickDwa(pixd, pixs, ...);
+ *      (10) The size of the result is determined by pixs.
+ *      (11) If either linear Sel is not found, this calls
+ *           the appropriate decomposible function.
+ * 
+ */ +PIX * +pixCloseBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 bordercolor, bordersize, found; +char *selnameh, *selnamev; +SELA *sela; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixCloseBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + sela = selaAddBasic(NULL); + found = TRUE; + selnameh = selnamev = NULL; + if (hsize > 1) { + selnameh = selaGetBrickName(sela, hsize, 1); + if (!selnameh) found = FALSE; + } + if (vsize > 1) { + selnamev = selaGetBrickName(sela, 1, vsize); + if (!selnamev) found = FALSE; + } + selaDestroy(&sela); + if (!found) { + L_INFO("Calling the decomposable dwa function\n", procName); + if (selnameh) LEPT_FREE(selnameh); + if (selnamev) LEPT_FREE(selnamev); + return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize); + } + + /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need + * an extra 32 OFF pixels around the image (in addition to + * the 32 added pixels for all dwa operations), whereas with + * SYMMETRIC_MORPH_BC this is not necessary. */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + if (bordercolor == 0) /* asymmetric b.c. */ + bordersize = 64; + else /* symmetric b.c. */ + bordersize = 32; + pixt1 = pixAddBorder(pixs, bordersize, 0); + + if (vsize == 1) { /* horizontal only */ + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh); + LEPT_FREE(selnameh); + } else if (hsize == 1) { /* vertical only */ + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); + LEPT_FREE(selnamev); + } else { /* do separable */ + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev); + LEPT_FREE(selnameh); + LEPT_FREE(selnamev); + pixDestroy(&pixt3); + } + pixt3 = pixRemoveBorder(pixt2, bordersize); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixTransferAllData(pixd, &pixt3, 0, 0); + return pixd; +} + + +/*-----------------------------------------------------------------* + * Binary composite morphological (dwa) ops with brick Sels * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDilateCompBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement a separable composite dilation with 2D brick Sels.
+ *      (2) For efficiency, it may decompose each linear morphological
+ *          operation into two (brick + comb).
+ *      (3) A brick Sel has hits for all elements.
+ *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (5) Do separably if both hsize and vsize are > 1.
+ *      (6) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...);
+ *          (b) pixDilateCompBrickDwa(pixs, pixs, ...);
+ *          (c) pixDilateCompBrickDwa(pixd, pixs, ...);
+ *      (9) The size of pixd is determined by pixs.
+ *      (10) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *           but not necessarily equal to it.  It attempts to optimize:
+ *              (a) for consistency with the input values: the product
+ *                  of terms is close to the input size
+ *              (b) for efficiency of the operation: the sum of the
+ *                  terms is small; ideally about twice the square
+ *                   root of the input size.
+ *           So, for example, if the input hsize = 37, which is
+ *           a prime number, the decomposer will break this into two
+ *           terms, 6 and 6, so that the net result is a dilation
+ *           with hsize = 36.
+ * 
+ */ +PIX * +pixDilateCompBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +char *selnameh1, *selnameh2, *selnamev1, *selnamev2; +l_int32 hsize1, hsize2, vsize1, vsize2; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixDilateCompBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + if (hsize > 63 || vsize > 63) + return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + hsize1 = hsize2 = vsize1 = vsize2 = 1; + selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; + if (hsize > 1) + getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, + &selnameh2, NULL, NULL); + if (vsize > 1) + getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, + &selnamev1, &selnamev2); + +#if DEBUG_SEL_LOOKUP + fprintf(stderr, "nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n", + selnameh1, selnameh2, selnamev1, selnamev2); + fprintf(stderr, "hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n", + hsize1, hsize2, vsize1, vsize2); +#endif /* DEBUG_SEL_LOOKUP */ + + pixt1 = pixAddBorder(pixs, 64, 0); + if (vsize == 1) { + if (hsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); + pixDestroy(&pixt3); + } + } else if (hsize == 1) { + if (vsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); + pixDestroy(&pixt3); + } + } else { /* vsize and hsize both > 1 */ + if (hsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + } else { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2); + pixDestroy(&pixt2); + } + if (vsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); + } else { + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2); + } + pixDestroy(&pixt3); + } + pixDestroy(&pixt1); + pixt1 = pixRemoveBorder(pixt2, 64); + pixDestroy(&pixt2); + if (selnameh1) LEPT_FREE(selnameh1); + if (selnameh2) LEPT_FREE(selnameh2); + if (selnamev1) LEPT_FREE(selnamev1); + if (selnamev2) LEPT_FREE(selnamev2); + + if (!pixd) + return pixt1; + + pixTransferAllData(pixd, &pixt1, 0, 0); + return pixd; +} + + +/*! + * \brief pixErodeCompBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement a separable composite erosion with 2D brick Sels.
+ *      (2) For efficiency, it may decompose each linear morphological
+ *          operation into two (brick + comb).
+ *      (3) A brick Sel has hits for all elements.
+ *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (5) Do separably if both hsize and vsize are > 1.
+ *      (6) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...);
+ *          (b) pixErodeCompBrickDwa(pixs, pixs, ...);
+ *          (c) pixErodeCompBrickDwa(pixd, pixs, ...);
+ *      (9) The size of pixd is determined by pixs.
+ *      (10) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *           but not necessarily equal to it.  It attempts to optimize:
+ *              (a) for consistency with the input values: the product
+ *                  of terms is close to the input size
+ *              (b) for efficiency of the operation: the sum of the
+ *                  terms is small; ideally about twice the square
+ *                   root of the input size.
+ *           So, for example, if the input hsize = 37, which is
+ *           a prime number, the decomposer will break this into two
+ *           terms, 6 and 6, so that the net result is a dilation
+ *           with hsize = 36.
+ * 
+ */ +PIX * +pixErodeCompBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +char *selnameh1, *selnameh2, *selnamev1, *selnamev2; +l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixErodeCompBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + if (hsize > 63 || vsize > 63) + return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + hsize1 = hsize2 = vsize1 = vsize2 = 1; + selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; + if (hsize > 1) + getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, + &selnameh2, NULL, NULL); + if (vsize > 1) + getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, + &selnamev1, &selnamev2); + + /* For symmetric b.c., bordercolor == 1 for erosion */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + pixt1 = pixAddBorder(pixs, 64, bordercolor); + + if (vsize == 1) { + if (hsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); + pixDestroy(&pixt3); + } + } else if (hsize == 1) { + if (vsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); + pixDestroy(&pixt3); + } + } else { /* vsize and hsize both > 1 */ + if (hsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + } else { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2); + pixDestroy(&pixt2); + } + if (vsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); + } else { + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2); + } + pixDestroy(&pixt3); + } + pixDestroy(&pixt1); + pixt1 = pixRemoveBorder(pixt2, 64); + pixDestroy(&pixt2); + if (selnameh1) LEPT_FREE(selnameh1); + if (selnameh2) LEPT_FREE(selnameh2); + if (selnamev1) LEPT_FREE(selnamev1); + if (selnamev2) LEPT_FREE(selnamev2); + + if (!pixd) + return pixt1; + + pixTransferAllData(pixd, &pixt1, 0, 0); + return pixd; +} + + +/*! + * \brief pixOpenCompBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) These implement a separable composite opening with 2D brick Sels.
+ *      (2) For efficiency, it may decompose each linear morphological
+ *          operation into two (brick + comb).
+ *      (3) A brick Sel has hits for all elements.
+ *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (5) Do separably if both hsize and vsize are > 1.
+ *      (6) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...);
+ *          (b) pixOpenCompBrickDwa(pixs, pixs, ...);
+ *          (c) pixOpenCompBrickDwa(pixd, pixs, ...);
+ *      (9) The size of pixd is determined by pixs.
+ *      (10) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *           but not necessarily equal to it.  It attempts to optimize:
+ *              (a) for consistency with the input values: the product
+ *                  of terms is close to the input size
+ *              (b) for efficiency of the operation: the sum of the
+ *                  terms is small; ideally about twice the square
+ *                   root of the input size.
+ *           So, for example, if the input hsize = 37, which is
+ *           a prime number, the decomposer will break this into two
+ *           terms, 6 and 6, so that the net result is a dilation
+ *           with hsize = 36.
+ * 
+ */ +PIX * +pixOpenCompBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +char *selnameh1, *selnameh2, *selnamev1, *selnamev2; +l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixOpenCompBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + if (hsize > 63 || vsize > 63) + return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + hsize1 = hsize2 = vsize1 = vsize2 = 1; + selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; + if (hsize > 1) + getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, + &selnameh2, NULL, NULL); + if (vsize > 1) + getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, + &selnamev1, &selnamev2); + + /* For symmetric b.c., initialize erosion with bordercolor == 1 */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + pixt1 = pixAddBorder(pixs, 64, bordercolor); + + if (vsize == 1) { + if (hsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + if (bordercolor == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); + if (bordercolor == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); + } + } else if (hsize == 1) { + if (vsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); + if (bordercolor == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); + if (bordercolor == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); + } + } else { /* vsize and hsize both > 1 */ + if (hsize2 == 1 && vsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); + if (bordercolor == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); + } else if (vsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); + if (bordercolor == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); + pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); + } else if (hsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2); + if (bordercolor == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); + } else { /* both directions are combed */ + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); + if (bordercolor == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); + } + } + pixDestroy(&pixt3); + + pixDestroy(&pixt1); + pixt1 = pixRemoveBorder(pixt2, 64); + pixDestroy(&pixt2); + if (selnameh1) LEPT_FREE(selnameh1); + if (selnameh2) LEPT_FREE(selnameh2); + if (selnamev1) LEPT_FREE(selnamev1); + if (selnamev2) LEPT_FREE(selnamev2); + + if (!pixd) + return pixt1; + + pixTransferAllData(pixd, &pixt1, 0, 0); + return pixd; +} + + +/*! + * \brief pixCloseCompBrickDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) This implements a separable composite safe closing with 2D
+ *          brick Sels.
+ *      (2) For efficiency, it may decompose each linear morphological
+ *          operation into two (brick + comb).
+ *      (3) A brick Sel has hits for all elements.
+ *      (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
+ *      (5) Do separably if both hsize and vsize are > 1.
+ *      (6) It is necessary that both horizontal and vertical Sels
+ *          of the input size are defined in the basic sela.
+ *      (7) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (8) For clarity, if the case is known, use these patterns:
+ *          (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...);
+ *          (b) pixCloseCompBrickDwa(pixs, pixs, ...);
+ *          (c) pixCloseCompBrickDwa(pixd, pixs, ...);
+ *      (9) The size of pixd is determined by pixs.
+ *      (10) CAUTION: both hsize and vsize are being decomposed.
+ *          The decomposer chooses a product of sizes (call them
+ *          'terms') for each that is close to the input size,
+ *           but not necessarily equal to it.  It attempts to optimize:
+ *              (a) for consistency with the input values: the product
+ *                  of terms is close to the input size
+ *              (b) for efficiency of the operation: the sum of the
+ *                  terms is small; ideally about twice the square
+ *                   root of the input size.
+ *           So, for example, if the input hsize = 37, which is
+ *           a prime number, the decomposer will break this into two
+ *           terms, 6 and 6, so that the net result is a dilation
+ *           with hsize = 36.
+ * 
+ */ +PIX * +pixCloseCompBrickDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +char *selnameh1, *selnameh2, *selnamev1, *selnamev2; +l_int32 hsize1, hsize2, vsize1, vsize2, setborder; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixCloseCompBrickDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + if (hsize > 63 || vsize > 63) + return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize); + + if (hsize == 1 && vsize == 1) + return pixCopy(pixd, pixs); + + hsize1 = hsize2 = vsize1 = vsize2 = 1; + selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; + if (hsize > 1) + getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, + &selnameh2, NULL, NULL); + if (vsize > 1) + getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, + &selnamev1, &selnamev2); + + pixt3 = NULL; + /* For symmetric b.c., PIX_SET border for erosions */ + setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + pixt1 = pixAddBorder(pixs, 64, 0); + + if (vsize == 1) { + if (hsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); + if (setborder == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); + } + } else if (hsize == 1) { + if (vsize2 == 1) { + pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1); + } else { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); + if (setborder == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); + } + } else { /* vsize and hsize both > 1 */ + if (hsize2 == 1 && vsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); + if (setborder == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); + } else if (vsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); + if (setborder == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); + pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); + } else if (hsize2 == 1) { + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2); + if (setborder == 1) + pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); + } else { /* both directions are combed */ + pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); + pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); + if (setborder == 1) + pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); + pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); + pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); + } + } + pixDestroy(&pixt3); + + pixDestroy(&pixt1); + pixt1 = pixRemoveBorder(pixt2, 64); + pixDestroy(&pixt2); + if (selnameh1) LEPT_FREE(selnameh1); + if (selnameh2) LEPT_FREE(selnameh2); + if (selnamev1) LEPT_FREE(selnamev1); + if (selnamev2) LEPT_FREE(selnamev2); + + if (!pixd) + return pixt1; + + pixTransferAllData(pixd, &pixt1, 0, 0); + return pixd; +} + + +/*--------------------------------------------------------------------------* + * Binary expanded composite morphological (dwa) ops with brick Sels * + *--------------------------------------------------------------------------*/ +/*! + * \brief pixDilateCompBrickExtendDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) Ankur Jain suggested and implemented extending the composite
+ *          DWA operations beyond the 63 pixel limit.  This is a
+ *          simplified and approximate implementation of the extension.
+ *          This allows arbitrary Dwa morph operations using brick Sels,
+ *          by decomposing the horizontal and vertical dilations into
+ *          a sequence of 63-element dilations plus a dilation of size
+ *          between 3 and 62.
+ *      (2) The 63-element dilations are exact, whereas the extra dilation
+ *          is approximate, because the underlying decomposition is
+ *          in pixDilateCompBrickDwa().  See there for further details.
+ *      (3) There are three cases:
+ *          (a) pixd == null   (result into new pixd)
+ *          (b) pixd == pixs   (in-place; writes result back to pixs)
+ *          (c) pixd != pixs   (puts result into existing pixd)
+ *      (4) There is no need to call this directly:  pixDilateCompBrickDwa()
+ *          calls this function if either brick dimension exceeds 63.
+ * 
+ */ +PIX * +pixDilateCompBrickExtendDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 i, nops, nh, extrah, nv, extrav; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixDilateCompBrickExtendDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize < 64 && vsize < 64) + return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); + + if (hsize > 63) + getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); + if (vsize > 63) + getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); + + /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */ + pixt1 = pixCreateTemplate(pixs); /* temp image */ + if (hsize == 1) { + pixt2 = pixClone(pixs); + } else if (hsize < 64) { + pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1); + } else if (hsize == 64) { /* approximate */ + pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); + } else { + nops = (extrah < 3) ? nh : nh + 1; + if (nops & 1) { /* odd */ + if (extrah > 2) + pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1); + else + pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); + for (i = 0; i < nops / 2; i++) { + pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); + pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); + } + } else { /* nops even */ + if (extrah > 2) { + pixDilateCompBrickDwa(pixt1, pixs, extrah, 1); + pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); + } else { /* they're all 63s */ + pixDilateCompBrickDwa(pixt1, pixs, 63, 1); + pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); + } + for (i = 0; i < nops / 2 - 1; i++) { + pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); + pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); + } + } + } + + /* Vertical dilation: pixt2 --> pixt3. */ + if (vsize == 1) { + pixt3 = pixClone(pixt2); + } else if (vsize < 64) { + pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize); + } else if (vsize == 64) { /* approximate */ + pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); + } else { + nops = (extrav < 3) ? nv : nv + 1; + if (nops & 1) { /* odd */ + if (extrav > 2) + pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav); + else + pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); + for (i = 0; i < nops / 2; i++) { + pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); + pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); + } + } else { /* nops even */ + if (extrav > 2) { + pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav); + pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); + } else { /* they're all 63s */ + pixDilateCompBrickDwa(pixt1, pixt2, 1, 63); + pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); + } + for (i = 0; i < nops / 2 - 1; i++) { + pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); + pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); + } + } + } + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixTransferAllData(pixd, &pixt3, 0, 0); + return pixd; +} + + +/*! + * \brief pixErodeCompBrickExtendDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      (1) See pixDilateCompBrickExtendDwa() for usage.
+ *      (2) There is no need to call this directly:  pixErodeCompBrickDwa()
+ *          calls this function if either brick dimension exceeds 63.
+ * 
+ */ +PIX * +pixErodeCompBrickExtendDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 i, nops, nh, extrah, nv, extrav; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixErodeCompBrickExtendDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + if (hsize < 64 && vsize < 64) + return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); + + if (hsize > 63) + getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); + if (vsize > 63) + getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); + + /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */ + pixt1 = pixCreateTemplate(pixs); /* temp image */ + if (hsize == 1) { + pixt2 = pixClone(pixs); + } else if (hsize < 64) { + pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1); + } else if (hsize == 64) { /* approximate */ + pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); + } else { + nops = (extrah < 3) ? nh : nh + 1; + if (nops & 1) { /* odd */ + if (extrah > 2) + pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1); + else + pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); + for (i = 0; i < nops / 2; i++) { + pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); + pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); + } + } else { /* nops even */ + if (extrah > 2) { + pixErodeCompBrickDwa(pixt1, pixs, extrah, 1); + pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); + } else { /* they're all 63s */ + pixErodeCompBrickDwa(pixt1, pixs, 63, 1); + pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); + } + for (i = 0; i < nops / 2 - 1; i++) { + pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); + pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); + } + } + } + + /* Vertical erosion: pixt2 --> pixt3. */ + if (vsize == 1) { + pixt3 = pixClone(pixt2); + } else if (vsize < 64) { + pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize); + } else if (vsize == 64) { /* approximate */ + pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); + } else { + nops = (extrav < 3) ? nv : nv + 1; + if (nops & 1) { /* odd */ + if (extrav > 2) + pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav); + else + pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); + for (i = 0; i < nops / 2; i++) { + pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); + pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); + } + } else { /* nops even */ + if (extrav > 2) { + pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav); + pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); + } else { /* they're all 63s */ + pixErodeCompBrickDwa(pixt1, pixt2, 1, 63); + pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); + } + for (i = 0; i < nops / 2 - 1; i++) { + pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); + pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); + } + } + } + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixTransferAllData(pixd, &pixt3, 0, 0); + return pixd; +} + + +/*! + * \brief pixOpenCompBrickExtendDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      1) There are three cases:
+ *          a) pixd == null   (result into new pixd
+ *          b) pixd == pixs   (in-place; writes result back to pixs
+ *          c) pixd != pixs   (puts result into existing pixd
+ *      2) There is no need to call this directly:  pixOpenCompBrickDwa(
+ *          calls this function if either brick dimension exceeds 63.
+ * 
+ */ +PIX * +pixOpenCompBrickExtendDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +PIX *pixt; + + PROCNAME("pixOpenCompBrickExtendDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize); + pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixCloseCompBrickExtendDwa() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs 1 bpp + * \param[in] hsize width of brick Sel + * \param[in] vsize height of brick Sel + * \return pixd + * + *
+ * Notes:
+ *      1) There are three cases:
+ *          a) pixd == null   (result into new pixd
+ *          b) pixd == pixs   (in-place; writes result back to pixs
+ *          c) pixd != pixs   (puts result into existing pixd
+ *      2) There is no need to call this directly:  pixCloseCompBrickDwa(
+ *          calls this function if either brick dimension exceeds 63.
+ * 
+ */ +PIX * +pixCloseCompBrickExtendDwa(PIX *pixd, + PIX *pixs, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 bordercolor, borderx, bordery; +PIX *pixt1, *pixt2, *pixt3; + + PROCNAME("pixCloseCompBrickExtendDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + if (hsize < 1 || vsize < 1) + return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); + + /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need + * an extra 32 OFF pixels around the image (in addition to + * the 32 added pixels for all dwa operations), whereas with + * SYMMETRIC_MORPH_BC this is not necessary. */ + bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); + if (bordercolor == 0) { /* asymmetric b.c. */ + borderx = 32 + (hsize / 64) * 32; + bordery = 32 + (vsize / 64) * 32; + } else { /* symmetric b.c. */ + borderx = bordery = 32; + } + pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0); + + pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize); + pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize); + + pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + + if (!pixd) + return pixt3; + + pixTransferAllData(pixd, &pixt3, 0, 0); + return pixd; +} + + +/*! + * \brief getExtendedCompositeParameters() + * + * \param[in] size of linear Sel + * \param[out] pn number of 63 wide convolutions + * \param[out] pextra size of extra Sel + * \param[out] pactualsize [optional] actual size used in operation + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The DWA implementation allows Sels to be used with hits
+ *          up to 31 pixels from the origin, either horizontally or
+ *          vertically.  Larger Sels can be used if decomposed into
+ *          a set of operations with Sels not exceeding 63 pixels
+ *          in either width or height (and with the origin as close
+ *          to the center of the Sel as possible).
+ *      (2) This returns the decomposition of a linear Sel of length
+ *          %size into a set of %n Sels of length 63 plus an extra
+ *          Sel of length %extra.
+ *      (3) For notation, let w == %size, n == %n, and e == %extra.
+ *          We have 1 < e < 63.
+ *
+ *          Then if w < 64, we have n = 0 and e = w.
+ *          The general formula for w > 63 is:
+ *             w = 63 + (n - 1) * 62 + (e - 1)
+ *
+ *          Where did this come from?  Each successive convolution with
+ *          a Sel of length L adds a total length (L - 1) to w.
+ *          This accounts for using 62 for each additional Sel of size 63,
+ *          and using (e - 1) for the additional Sel of size e.
+ *
+ *          Solving for n and e for w > 63:
+ *             n = 1 + Int((w - 63) / 62)
+ *             e = w - 63 - (n - 1) * 62 + 1
+ *
+ *          The extra part is decomposed into two factors f1 and f2,
+ *          and the actual size of the extra part is
+ *             e' = f1 * f2
+ *          Then the actual width is:
+ *             w' = 63 + (n - 1) * 62 + f1 * f2 - 1
+ * 
+ */ +l_ok +getExtendedCompositeParameters(l_int32 size, + l_int32 *pn, + l_int32 *pextra, + l_int32 *pactualsize) +{ +l_int32 n, extra, fact1, fact2; + + PROCNAME("getExtendedCompositeParameters"); + + if (!pn || !pextra) + return ERROR_INT("&n and &extra not both defined", procName, 1); + + if (size <= 63) { + n = 0; + extra = L_MIN(1, size); + } else { /* size > 63 */ + n = 1 + (l_int32)((size - 63) / 62); + extra = size - 63 - (n - 1) * 62 + 1; + } + + if (pactualsize) { + selectComposableSizes(extra, &fact1, &fact2); + *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1; + } + + *pn = n; + *pextra = extra; + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/morphseq.c b/hgdriver/3rdparty/hgOCR/leptonica/morphseq.c new file mode 100644 index 0000000..cb401a6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/morphseq.c @@ -0,0 +1,1244 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file morphseq.c + *
+ *
+ *      Run a sequence of binary rasterop morphological operations
+ *            PIX     *pixMorphSequence()
+ *
+ *      Run a sequence of binary composite rasterop morphological operations
+ *            PIX     *pixMorphCompSequence()
+ *
+ *      Run a sequence of binary dwa morphological operations
+ *            PIX     *pixMorphSequenceDwa()
+ *
+ *      Run a sequence of binary composite dwa morphological operations
+ *            PIX     *pixMorphCompSequenceDwa()
+ *
+ *      Parser verifier for binary morphological operations
+ *            l_int32  morphSequenceVerify()
+ *
+ *      Run a sequence of grayscale morphological operations
+ *            PIX     *pixGrayMorphSequence()
+ *
+ *      Run a sequence of color morphological operations
+ *            PIX     *pixColorMorphSequence()
+ * 
+ */ + +#include +#include "allheaders.h" + +/*-------------------------------------------------------------------------* + * Run a sequence of binary rasterop morphological operations * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixMorphSequence() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does rasterop morphology on binary images.
+ *      (2) This runs a pipeline of operations; no branching is allowed.
+ *      (3) This only uses brick Sels, which are created on the fly.
+ *          In the future this will be generalized to extract Sels from
+ *          a Sela by name.
+ *      (4) A new image is always produced; the input image is not changed.
+ *      (5) This contains an interpreter, allowing sequences to be
+ *          generated and run.
+ *      (6) The format of the sequence string is defined below.
+ *      (7) In addition to morphological operations, rank order reduction
+ *          and replicated expansion allow operations to take place
+ *          downscaled by a power of 2.
+ *      (8) Intermediate results can optionally be displayed.
+ *      (9) Thanks to Dar-Shyang Lee, who had the idea for this and
+ *          built the first implementation.
+ *      (10) The sequence string is formatted as follows:
+ *            ~ An arbitrary number of operations,  each separated
+ *              by a '+' character.  White space is ignored.
+ *            ~ Each operation begins with a case-independent character
+ *              specifying the operation:
+ *                 d or D  (dilation)
+ *                 e or E  (erosion)
+ *                 o or O  (opening)
+ *                 c or C  (closing)
+ *                 r or R  (rank binary reduction)
+ *                 x or X  (replicative binary expansion)
+ *                 b or B  (add a border of 0 pixels of this size)
+ *            ~ The args to the morphological operations are bricks of hits,
+ *              and are formatted as a.b, where a and b are horizontal and
+ *              vertical dimensions, rsp.
+ *            ~ The args to the reduction are a sequence of up to 4 integers,
+ *              each from 1 to 4.
+ *            ~ The arg to the expansion is a power of two, in the set
+ *              {2, 4, 8, 16}.
+ *      (11) An example valid sequence is:
+ *               "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4"
+ *           In this example, the following operation sequence is carried out:
+ *             * b32: Add a 32 pixel border around the input image
+ *             * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3)
+ *             * C3.1: Closing with horiz sel of length 3  (e.g., 3 x 1)
+ *             * r23: Two successive 2x2 reductions with rank 2 in the first
+ *                    and rank 3 in the second.  The result is a 4x reduced pix.
+ *             * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0)
+ *             * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0)
+ *             * X4: 4x replicative expansion, back to original resolution
+ *      (12) The safe closing is used.  However, if you implement a
+ *           closing as separable dilations followed by separable erosions,
+ *           it will not be safe.  For that situation, you need to add
+ *           a sufficiently large border as the first operation in
+ *           the sequence.  This will be removed automatically at the
+ *           end.  There are two cautions:
+ *              ~ When computing what is sufficient, remember that if
+ *                reductions are carried out, the border is also reduced.
+ *              ~ The border is removed at the end, so if a border is
+ *                added at the beginning, the result must be at the
+ *                same resolution as the input!
+ * 
+ */ +PIX * +pixMorphSequence(PIX *pixs, + const char *sequence, + l_int32 dispsep) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; +l_int32 level[4]; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixMorphSequence"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + if (!morphSequenceVerify(sa)) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + border = 0; + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixDilateBrick(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixErodeBrick(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pixOpenBrick(pix1, pix1, w, h); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pixCloseSafeBrick(pix1, pix1, w, h); + break; + case 'r': + case 'R': + nred = strlen(op) - 1; + for (j = 0; j < nred; j++) + level[j] = op[j + 1] - '0'; + for (j = nred; j < 4; j++) + level[j] = 0; + pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], + level[2], level[3]); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'x': + case 'X': + sscanf(&op[1], "%d", &fact); + pix2 = pixExpandReplicate(pix1, fact); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'b': + case 'B': + sscanf(&op[1], "%d", &border); + pix2 = pixAddBorder(pix1, border, 0); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, 0); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + if (border > 0) { + pix2 = pixRemoveBorder(pix1, border); + pixSwapAndDestroy(&pix1, &pix2); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} + + +/*-------------------------------------------------------------------------* + * Run a sequence of binary composite rasterop morphological operations * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixMorphCompSequence() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display of results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does rasterop morphology on binary images, using composite
+ *          operations for extra speed on large Sels.
+ *      (2) Safe closing is used atomically.  However, if you implement a
+ *          closing as a sequence with a dilation followed by an
+ *          erosion, it will not be safe, and to ensure that you have
+ *          no boundary effects you must add a border in advance and
+ *          remove it at the end.
+ *      (3) For other usage details, see the notes for pixMorphSequence().
+ *      (4) The sequence string is formatted as follows:
+ *            ~ An arbitrary number of operations,  each separated
+ *              by a '+' character.  White space is ignored.
+ *            ~ Each operation begins with a case-independent character
+ *              specifying the operation:
+ *                 d or D  (dilation)
+ *                 e or E  (erosion)
+ *                 o or O  (opening)
+ *                 c or C  (closing)
+ *                 r or R  (rank binary reduction)
+ *                 x or X  (replicative binary expansion)
+ *                 b or B  (add a border of 0 pixels of this size)
+ *            ~ The args to the morphological operations are bricks of hits,
+ *              and are formatted as a.b, where a and b are horizontal and
+ *              vertical dimensions, rsp.
+ *            ~ The args to the reduction are a sequence of up to 4 integers,
+ *              each from 1 to 4.
+ *            ~ The arg to the expansion is a power of two, in the set
+ *              {2, 4, 8, 16}.
+ * 
+ */ +PIX * +pixMorphCompSequence(PIX *pixs, + const char *sequence, + l_int32 dispsep) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; +l_int32 level[4]; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixMorphCompSequence"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + + if (!morphSequenceVerify(sa)) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + border = 0; + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixDilateCompBrick(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixErodeCompBrick(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pixOpenCompBrick(pix1, pix1, w, h); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pixCloseSafeCompBrick(pix1, pix1, w, h); + break; + case 'r': + case 'R': + nred = strlen(op) - 1; + for (j = 0; j < nred; j++) + level[j] = op[j + 1] - '0'; + for (j = nred; j < 4; j++) + level[j] = 0; + pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], + level[2], level[3]); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'x': + case 'X': + sscanf(&op[1], "%d", &fact); + pix2 = pixExpandReplicate(pix1, fact); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'b': + case 'B': + sscanf(&op[1], "%d", &border); + pix2 = pixAddBorder(pix1, border, 0); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, 0); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + if (border > 0) { + pix2 = pixRemoveBorder(pix1, border); + pixSwapAndDestroy(&pix1, &pix2); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} + + +/*-------------------------------------------------------------------------* + * Run a sequence of binary dwa morphological operations * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixMorphSequenceDwa() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display of results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does dwa morphology on binary images.
+ *      (2) This runs a pipeline of operations; no branching is allowed.
+ *      (3) This only uses brick Sels that have been pre-compiled with
+ *          dwa code.
+ *      (4) A new image is always produced; the input image is not changed.
+ *      (5) This contains an interpreter, allowing sequences to be
+ *          generated and run.
+ *      (6) See pixMorphSequence() for further information about usage.
+ * 
+ */ +PIX * +pixMorphSequenceDwa(PIX *pixs, + const char *sequence, + l_int32 dispsep) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; +l_int32 level[4]; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixMorphSequenceDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + + if (!morphSequenceVerify(sa)) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + border = 0; + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixDilateBrickDwa(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixErodeBrickDwa(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pixOpenBrickDwa(pix1, pix1, w, h); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pixCloseBrickDwa(pix1, pix1, w, h); + break; + case 'r': + case 'R': + nred = strlen(op) - 1; + for (j = 0; j < nred; j++) + level[j] = op[j + 1] - '0'; + for (j = nred; j < 4; j++) + level[j] = 0; + pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], + level[2], level[3]); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'x': + case 'X': + sscanf(&op[1], "%d", &fact); + pix2 = pixExpandReplicate(pix1, fact); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'b': + case 'B': + sscanf(&op[1], "%d", &border); + pix2 = pixAddBorder(pix1, border, 0); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, 0); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + if (border > 0) { + pix2 = pixRemoveBorder(pix1, border); + pixSwapAndDestroy(&pix1, &pix2); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} + + +/*-------------------------------------------------------------------------* + * Run a sequence of binary composite dwa morphological operations * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixMorphCompSequenceDwa() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display of results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does dwa morphology on binary images, using brick Sels.
+ *      (2) This runs a pipeline of operations; no branching is allowed.
+ *      (3) It implements all brick Sels that have dimensions up to 63
+ *          on each side, using a composite (linear + comb) when useful.
+ *      (4) A new image is always produced; the input image is not changed.
+ *      (5) This contains an interpreter, allowing sequences to be
+ *          generated and run.
+ *      (6) See pixMorphSequence() for further information about usage.
+ * 
+ */ +PIX * +pixMorphCompSequenceDwa(PIX *pixs, + const char *sequence, + l_int32 dispsep) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; +l_int32 level[4]; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixMorphCompSequenceDwa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + + if (!morphSequenceVerify(sa)) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + border = 0; + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixDilateCompBrickDwa(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixErodeCompBrickDwa(NULL, pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pixOpenCompBrickDwa(pix1, pix1, w, h); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pixCloseCompBrickDwa(pix1, pix1, w, h); + break; + case 'r': + case 'R': + nred = strlen(op) - 1; + for (j = 0; j < nred; j++) + level[j] = op[j + 1] - '0'; + for (j = nred; j < 4; j++) + level[j] = 0; + pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], + level[2], level[3]); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'x': + case 'X': + sscanf(&op[1], "%d", &fact); + pix2 = pixExpandReplicate(pix1, fact); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'b': + case 'B': + sscanf(&op[1], "%d", &border); + pix2 = pixAddBorder(pix1, border, 0); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, 0); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + if (border > 0) { + pix2 = pixRemoveBorder(pix1, border); + pixSwapAndDestroy(&pix1, &pix2); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} + + +/*-------------------------------------------------------------------------* + * Parser verifier for binary morphological operations * + *-------------------------------------------------------------------------*/ +/*! + * \brief morphSequenceVerify() + * + * \param[in] sa string array of operation sequence + * \return TRUE if valid; FALSE otherwise or on error + * + *
+ * Notes:
+ *      (1) This does verification of valid binary morphological
+ *          operation sequences.
+ *      (2) See pixMorphSequence() for notes on valid operations
+ *          in the sequence.
+ * 
+ */ +l_int32 +morphSequenceVerify(SARRAY *sa) +{ +char *rawop, *op; +l_int32 nops, i, j, nred, fact, valid, w, h, netred, border; +l_int32 level[4]; +l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */ + + PROCNAME("morphSequenceVerify"); + + if (!sa) + return ERROR_INT("sa not defined", procName, FALSE); + + nops = sarrayGetCount(sa); + valid = TRUE; + netred = 0; + border = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + case 'e': + case 'E': + case 'o': + case 'O': + case 'c': + case 'C': + if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { + fprintf(stderr, "*** op: %s invalid\n", op); + valid = FALSE; + break; + } + if (w <= 0 || h <= 0) { + fprintf(stderr, + "*** op: %s; w = %d, h = %d; must both be > 0\n", + op, w, h); + valid = FALSE; + break; + } +/* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ + break; + case 'r': + case 'R': + nred = strlen(op) - 1; + netred += nred; + if (nred < 1 || nred > 4) { + fprintf(stderr, + "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n", + op, nred); + valid = FALSE; + break; + } + for (j = 0; j < nred; j++) { + level[j] = op[j + 1] - '0'; + if (level[j] < 1 || level[j] > 4) { + fprintf(stderr, "*** op = %s; level[%d] = %d is invalid\n", + op, j, level[j]); + valid = FALSE; + break; + } + } + if (!valid) + break; +/* fprintf(stderr, "op = %s", op); */ + for (j = 0; j < nred; j++) { + level[j] = op[j + 1] - '0'; +/* fprintf(stderr, ", level[%d] = %d", j, level[j]); */ + } +/* fprintf(stderr, "\n"); */ + break; + case 'x': + case 'X': + if (sscanf(&op[1], "%d", &fact) != 1) { + fprintf(stderr, "*** op: %s; fact invalid\n", op); + valid = FALSE; + break; + } + if (fact != 2 && fact != 4 && fact != 8 && fact != 16) { + fprintf(stderr, "*** op = %s; invalid fact = %d\n", op, fact); + valid = FALSE; + break; + } + netred -= intlogbase2[fact / 4]; +/* fprintf(stderr, "op = %s; fact = %d\n", op, fact); */ + break; + case 'b': + case 'B': + if (sscanf(&op[1], "%d", &fact) != 1) { + fprintf(stderr, "*** op: %s; fact invalid\n", op); + valid = FALSE; + break; + } + if (i > 0) { + fprintf(stderr, "*** op = %s; must be first op\n", op); + valid = FALSE; + break; + } + if (fact < 1) { + fprintf(stderr, "*** op = %s; invalid fact = %d\n", op, fact); + valid = FALSE; + break; + } + border = fact; +/* fprintf(stderr, "op = %s; fact = %d\n", op, fact); */ + break; + default: + fprintf(stderr, "*** nonexistent op = %s\n", op); + valid = FALSE; + } + LEPT_FREE(op); + } + + if (border != 0 && netred != 0) { + fprintf(stderr, + "*** op = %s; border added but net reduction not 0\n", op); + valid = FALSE; + } + return valid; +} + + +/*-----------------------------------------------------------------* + * Run a sequence of grayscale morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixGrayMorphSequence() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display of results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \param[in] dispy if dispsep > 0, this gives the y-value of the + * UL corner for display; otherwise it is ignored + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This works on 8 bpp grayscale images.
+ *      (2) This runs a pipeline of operations; no branching is allowed.
+ *      (3) This only uses brick SELs.
+ *      (4) A new image is always produced; the input image is not changed.
+ *      (5) This contains an interpreter, allowing sequences to be
+ *          generated and run.
+ *      (6) The format of the sequence string is defined below.
+ *      (7) In addition to morphological operations, the composite
+ *          morph/subtract tophat can be performed.
+ *      (8) Sel sizes (width, height) must each be odd numbers.
+ *      (9) Intermediate results can optionally be displayed
+ *      (10) The sequence string is formatted as follows:
+ *            ~ An arbitrary number of operations,  each separated
+ *              by a '+' character.  White space is ignored.
+ *            ~ Each operation begins with a case-independent character
+ *              specifying the operation:
+ *                 d or D  (dilation)
+ *                 e or E  (erosion)
+ *                 o or O  (opening)
+ *                 c or C  (closing)
+ *                 t or T  (tophat)
+ *            ~ The args to the morphological operations are bricks of hits,
+ *              and are formatted as a.b, where a and b are horizontal and
+ *              vertical dimensions, rsp. (each must be an odd number)
+ *            ~ The args to the tophat are w or W (for white tophat)
+ *              or b or B (for black tophat), followed by a.b as for
+ *              the dilation, erosion, opening and closing.
+ *           Example valid sequences are:
+ *             "c5.3 + o7.5"
+ *             "c9.9 + tw9.9"
+ * 
+ */ +PIX * +pixGrayMorphSequence(PIX *pixs, + const char *sequence, + l_int32 dispsep, + l_int32 dispy) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, valid, w, h, x, pdfout; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixGrayMorphSequence"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + + /* Verify that the operation sequence is valid */ + valid = TRUE; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + case 'e': + case 'E': + case 'o': + case 'O': + case 'c': + case 'C': + if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { + fprintf(stderr, "*** op: %s invalid\n", op); + valid = FALSE; + break; + } + if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { + fprintf(stderr, + "*** op: %s; w = %d, h = %d; must both be odd\n", + op, w, h); + valid = FALSE; + break; + } +/* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ + break; + case 't': + case 'T': + if (op[1] != 'w' && op[1] != 'W' && + op[1] != 'b' && op[1] != 'B') { + fprintf(stderr, + "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]); + valid = FALSE; + break; + } + sscanf(&op[2], "%d.%d", &w, &h); + if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { + fprintf(stderr, + "*** op: %s; w = %d, h = %d; must both be odd\n", + op, w, h); + valid = FALSE; + break; + } +/* fprintf(stderr, "op = %s", op); */ + break; + default: + fprintf(stderr, "*** nonexistent op = %s\n", op); + valid = FALSE; + } + LEPT_FREE(op); + } + if (!valid) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixDilateGray(pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixErodeGray(pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixOpenGray(pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixCloseGray(pix1, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 't': + case 'T': + sscanf(&op[2], "%d.%d", &w, &h); + if (op[1] == 'w' || op[1] == 'W') + pix2 = pixTophat(pix1, w, h, L_TOPHAT_WHITE); + else /* 'b' or 'B' */ + pix2 = pixTophat(pix1, w, h, L_TOPHAT_BLACK); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, dispy); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} + + +/*-----------------------------------------------------------------* + * Run a sequence of color morphological operations * + *-----------------------------------------------------------------*/ +/*! + * \brief pixColorMorphSequence() + * + * \param[in] pixs + * \param[in] sequence string specifying sequence + * \param[in] dispsep controls debug display of results in the sequence: + * 0: no output + * > 0: gives horizontal separation in pixels between + * successive displays + * < 0: pdf output; abs(dispsep) is used for naming + * \param[in] dispy if dispsep > 0, this gives the y-value of the + * UL corner for display; otherwise it is ignored + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This works on 32 bpp rgb images.
+ *      (2) Each component is processed separately.
+ *      (3) This runs a pipeline of operations; no branching is allowed.
+ *      (4) This only uses brick SELs.
+ *      (5) A new image is always produced; the input image is not changed.
+ *      (6) This contains an interpreter, allowing sequences to be
+ *          generated and run.
+ *      (7) Sel sizes (width, height) must each be odd numbers.
+ *      (8) The format of the sequence string is defined below.
+ *      (9) Intermediate results can optionally be displayed.
+ *      (10) The sequence string is formatted as follows:
+ *            ~ An arbitrary number of operations,  each separated
+ *              by a '+' character.  White space is ignored.
+ *            ~ Each operation begins with a case-independent character
+ *              specifying the operation:
+ *                 d or D  (dilation)
+ *                 e or E  (erosion)
+ *                 o or O  (opening)
+ *                 c or C  (closing)
+ *            ~ The args to the morphological operations are bricks of hits,
+ *              and are formatted as a.b, where a and b are horizontal and
+ *              vertical dimensions, rsp. (each must be an odd number)
+ *           Example valid sequences are:
+ *             "c5.3 + o7.5"
+ *             "D9.1"
+ * 
+ */ +PIX * +pixColorMorphSequence(PIX *pixs, + const char *sequence, + l_int32 dispsep, + l_int32 dispy) +{ +char *rawop, *op; +char fname[256]; +l_int32 nops, i, valid, w, h, x, pdfout; +PIX *pix1, *pix2; +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixColorMorphSequence"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sequence) + return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); + + /* Split sequence into individual operations */ + sa = sarrayCreate(0); + sarraySplitString(sa, sequence, "+"); + nops = sarrayGetCount(sa); + pdfout = (dispsep < 0) ? 1 : 0; + + /* Verify that the operation sequence is valid */ + valid = TRUE; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + case 'e': + case 'E': + case 'o': + case 'O': + case 'c': + case 'C': + if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { + fprintf(stderr, "*** op: %s invalid\n", op); + valid = FALSE; + break; + } + if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { + fprintf(stderr, + "*** op: %s; w = %d, h = %d; must both be odd\n", + op, w, h); + valid = FALSE; + break; + } +/* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ + break; + default: + fprintf(stderr, "*** nonexistent op = %s\n", op); + valid = FALSE; + } + LEPT_FREE(op); + } + if (!valid) { + sarrayDestroy(&sa); + return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); + } + + /* Parse and operate */ + pixa = NULL; + if (pdfout) { + pixa = pixaCreate(0); + pixaAddPix(pixa, pixs, L_CLONE); + } + pix1 = pixCopy(NULL, pixs); + pix2 = NULL; + x = 0; + for (i = 0; i < nops; i++) { + rawop = sarrayGetString(sa, i, L_NOCOPY); + op = stringRemoveChars(rawop, " \n\t"); + switch (op[0]) + { + case 'd': + case 'D': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixColorMorph(pix1, L_MORPH_DILATE, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'e': + case 'E': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixColorMorph(pix1, L_MORPH_ERODE, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'o': + case 'O': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixColorMorph(pix1, L_MORPH_OPEN, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + case 'c': + case 'C': + sscanf(&op[1], "%d.%d", &w, &h); + pix2 = pixColorMorph(pix1, L_MORPH_CLOSE, w, h); + pixSwapAndDestroy(&pix1, &pix2); + break; + default: + /* All invalid ops are caught in the first pass */ + break; + } + LEPT_FREE(op); + + /* Debug output */ + if (dispsep > 0) { + pixDisplay(pix1, x, dispy); + x += dispsep; + } + if (pdfout) + pixaAddPix(pixa, pix1, L_COPY); + } + + if (pdfout) { + snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", + L_ABS(dispsep)); + pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); + pixaDestroy(&pixa); + } + + sarrayDestroy(&sa); + return pix1; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/numabasic.c b/hgdriver/3rdparty/hgOCR/leptonica/numabasic.c new file mode 100644 index 0000000..84339c0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/numabasic.c @@ -0,0 +1,2023 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file numabasic.c + *
+ *
+ *      Numa creation, destruction, copy, clone, etc.
+ *          NUMA        *numaCreate()
+ *          NUMA        *numaCreateFromIArray()
+ *          NUMA        *numaCreateFromFArray()
+ *          NUMA        *numaCreateFromString()
+ *          void        *numaDestroy()
+ *          NUMA        *numaCopy()
+ *          NUMA        *numaClone()
+ *          l_int32      numaEmpty()
+ *
+ *      Add/remove number (float or integer)
+ *          l_int32      numaAddNumber()
+ *          static l_int32  numaExtendArray()
+ *          l_int32      numaInsertNumber()
+ *          l_int32      numaRemoveNumber()
+ *          l_int32      numaReplaceNumber()
+ *
+ *      Numa accessors
+ *          l_int32      numaGetCount()
+ *          l_int32      numaSetCount()
+ *          l_int32      numaGetIValue()
+ *          l_int32      numaGetFValue()
+ *          l_int32      numaSetValue()
+ *          l_int32      numaShiftValue()
+ *          l_int32     *numaGetIArray()
+ *          l_float32   *numaGetFArray()
+ *          l_int32      numaGetRefcount()
+ *          l_int32      numaChangeRefcount()
+ *          l_int32      numaGetParameters()
+ *          l_int32      numaSetParameters()
+ *          l_int32      numaCopyParameters()
+ *
+ *      Convert to string array
+ *          SARRAY      *numaConvertToSarray()
+ *
+ *      Serialize numa for I/O
+ *          NUMA        *numaRead()
+ *          NUMA        *numaReadStream()
+ *          NUMA        *numaReadMem()
+ *          l_int32      numaWriteDebug()
+ *          l_int32      numaWrite()
+ *          l_int32      numaWriteStream()
+ *          l_int32      numaWriteMem()
+ *
+ *      Numaa creation, destruction, truncation
+ *          NUMAA       *numaaCreate()
+ *          NUMAA       *numaaCreateFull()
+ *          NUMAA       *numaaTruncate()
+ *          void        *numaaDestroy()
+ *
+ *      Add Numa to Numaa
+ *          l_int32      numaaAddNuma()
+ *          static l_int32   numaaExtendArray()
+ *
+ *      Numaa accessors
+ *          l_int32      numaaGetCount()
+ *          l_int32      numaaGetNumaCount()
+ *          l_int32      numaaGetNumberCount()
+ *          NUMA       **numaaGetPtrArray()
+ *          NUMA        *numaaGetNuma()
+ *          NUMA        *numaaReplaceNuma()
+ *          l_int32      numaaGetValue()
+ *          l_int32      numaaAddNumber()
+ *
+ *      Serialize numaa for I/O
+ *          NUMAA       *numaaRead()
+ *          NUMAA       *numaaReadStream()
+ *          NUMAA       *numaaReadMem()
+ *          l_int32      numaaWrite()
+ *          l_int32      numaaWriteStream()
+ *          l_int32      numaaWriteMem()
+ *
+ *    (1) The Numa is a struct holding an array of floats.  It can also
+ *        be used to store l_int32 values, with some loss of precision
+ *        for floats larger than about 10 million.  Use the L_Dna instead
+ *        if integers larger than a few million need to be stored.
+ *
+ *    (2) Always use the accessors in this file, never the fields directly.
+ *
+ *    (3) Storing and retrieving numbers:
+ *
+ *       * to append a new number to the array, use numaAddNumber().  If
+ *         the number is an int, it will will automatically be converted
+ *         to l_float32 and stored.
+ *
+ *       * to reset a value stored in the array, use numaSetValue().
+ *
+ *       * to increment or decrement a value stored in the array,
+ *         use numaShiftValue().
+ *
+ *       * to obtain a value from the array, use either numaGetIValue()
+ *         or numaGetFValue(), depending on whether you are retrieving
+ *         an integer or a float.  This avoids doing an explicit cast,
+ *         such as
+ *           (a) return a l_float32 and cast it to an l_int32
+ *           (b) cast the return directly to (l_float32 *) to
+ *               satisfy the function prototype, as in
+ *                 numaGetFValue(na, index, (l_float32 *)&ival);   [ugly!]
+ *
+ *    (4) int <--> float conversions:
+ *
+ *        Tradition dictates that type conversions go automatically from
+ *        l_int32 --> l_float32, even though it is possible to lose
+ *        precision for large integers, whereas you must cast (l_int32)
+ *        to go from l_float32 --> l_int32 because you're truncating
+ *        to the integer value.
+ *
+ *    (5) As with other arrays in leptonica, the numa has both an allocated
+ *        size and a count of the stored numbers.  When you add a number, it
+ *        goes on the end of the array, and causes a realloc if the array
+ *        is already filled.  However, in situations where you want to
+ *        add numbers randomly into an array, such as when you build a
+ *        histogram, you must set the count of stored numbers in advance.
+ *        This is done with numaSetCount().  If you set a count larger
+ *        than the allocated array, it does a realloc to the size requested.
+ *
+ *    (6) In situations where the data in a numa correspond to a function
+ *        y(x), the values can be either at equal spacings in x or at
+ *        arbitrary spacings.  For the former, we can represent all x values
+ *        by two parameters: startx (corresponding to y[0]) and delx
+ *        for the change in x for adjacent values y[i] and y[i+1].
+ *        startx and delx are initialized to 0.0 and 1.0, rsp.
+ *        For arbitrary spacings, we use a second numa, and the two
+ *        numas are typically denoted nay and nax.
+ *
+ *    (7) The numa is also the basic struct used for histograms.  Every numa
+ *        has startx and delx fields, initialized to 0.0 and 1.0, that can
+ *        be used to represent the "x" value for the location of the
+ *        first bin and the bin width, respectively.  Accessors are the
+ *        numa*Parameters() functions.  All functions that make numa
+ *        histograms must set these fields properly, and many functions
+ *        that use numa histograms rely on the correctness of these values.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxArraySize = 100000000; /* for numa */ +static const l_uint32 MaxPtrArraySize = 10000; /* for numaa */ +static const l_int32 InitialArraySize = 50; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 numaExtendArray(NUMA *na); +static l_int32 numaaExtendArray(NUMAA *naa); + +/*--------------------------------------------------------------------------* + * Numa creation, destruction, copy, clone, etc. * + *--------------------------------------------------------------------------*/ +/*! + * \brief numaCreate() + * + * \param[in] n size of number array to be alloc'd 0 for default + * \return na, or NULL on error + */ +NUMA * +numaCreate(l_int32 n) +{ +NUMA *na; + + PROCNAME("numaCreate"); + + if (n <= 0 || n > MaxArraySize) + n = InitialArraySize; + + na = (NUMA *)LEPT_CALLOC(1, sizeof(NUMA)); + if ((na->array = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32))) == NULL) { + numaDestroy(&na); + return (NUMA *)ERROR_PTR("number array not made", procName, NULL); + } + + na->nalloc = n; + na->n = 0; + na->refcount = 1; + na->startx = 0.0; + na->delx = 1.0; + return na; +} + + +/*! + * \brief numaCreateFromIArray() + * + * \param[in] iarray integer array + * \param[in] size of the array + * \return na, or NULL on error + * + *
+ * Notes:
+ *      (1) We can't insert this int array into the numa, because a numa
+ *          takes a float array.  So this just copies the data from the
+ *          input array into the numa.  The input array continues to be
+ *          owned by the caller.
+ * 
+ */ +NUMA * +numaCreateFromIArray(l_int32 *iarray, + l_int32 size) +{ +l_int32 i; +NUMA *na; + + PROCNAME("numaCreateFromIArray"); + + if (!iarray) + return (NUMA *)ERROR_PTR("iarray not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + + na = numaCreate(size); + for (i = 0; i < size; i++) + numaAddNumber(na, iarray[i]); + + return na; +} + + +/*! + * \brief numaCreateFromFArray() + * + * \param[in] farray float array + * \param[in] size of the array + * \param[in] copyflag L_INSERT or L_COPY + * \return na, or NULL on error + * + *
+ * Notes:
+ *      (1) With L_INSERT, ownership of the input array is transferred
+ *          to the returned numa, and all %size elements are considered
+ *          to be valid.
+ * 
+ */ +NUMA * +numaCreateFromFArray(l_float32 *farray, + l_int32 size, + l_int32 copyflag) +{ +l_int32 i; +NUMA *na; + + PROCNAME("numaCreateFromFArray"); + + if (!farray) + return (NUMA *)ERROR_PTR("farray not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + if (copyflag != L_INSERT && copyflag != L_COPY) + return (NUMA *)ERROR_PTR("invalid copyflag", procName, NULL); + + na = numaCreate(size); + if (copyflag == L_INSERT) { + if (na->array) LEPT_FREE(na->array); + na->array = farray; + na->n = size; + } else { /* just copy the contents */ + for (i = 0; i < size; i++) + numaAddNumber(na, farray[i]); + } + + return na; +} + + +/*! + * \brief numaCreateFromString() + * + * \param[in] str string of comma-separated numbers + * \return na, or NULL on error + * + *
+ * Notes:
+ *      (1) The numbers can be ints or floats; they will be interpreted
+ *          and stored as floats.  To use them as integers (e.g., for
+ *          indexing into arrays), use numaGetIValue(...).
+ * 
+ */ +NUMA * +numaCreateFromString(const char *str) +{ +char *substr; +l_int32 i, n, nerrors; +l_float32 val; +NUMA *na; +SARRAY *sa; + + PROCNAME("numaCreateFromString"); + + if (!str || (strlen(str) == 0)) + return (NUMA *)ERROR_PTR("str not defined or empty", procName, NULL); + + sa = sarrayCreate(0); + sarraySplitString(sa, str, ","); + n = sarrayGetCount(sa); + na = numaCreate(n); + nerrors = 0; + for (i = 0; i < n; i++) { + substr = sarrayGetString(sa, i, L_NOCOPY); + if (sscanf(substr, "%f", &val) != 1) { + L_ERROR("substr %d not float\n", procName, i); + nerrors++; + } else { + numaAddNumber(na, val); + } + } + + sarrayDestroy(&sa); + if (nerrors > 0) { + numaDestroy(&na); + return (NUMA *)ERROR_PTR("non-floats in string", procName, NULL); + } + + return na; +} + + +/*! + * \brief numaDestroy() + * + * \param[in,out] pna numa to be destroyed and nulled if it exists + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the numa.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +numaDestroy(NUMA **pna) +{ +NUMA *na; + + PROCNAME("numaDestroy"); + + if (pna == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + + if ((na = *pna) == NULL) + return; + + /* Decrement the ref count. If it is 0, destroy the numa. */ + numaChangeRefcount(na, -1); + if (numaGetRefcount(na) <= 0) { + if (na->array) + LEPT_FREE(na->array); + LEPT_FREE(na); + } + + *pna = NULL; + return; +} + + +/*! + * \brief numaCopy() + * + * \param[in] na + * \return copy of numa, or NULL on error + */ +NUMA * +numaCopy(NUMA *na) +{ +l_int32 i; +NUMA *cna; + + PROCNAME("numaCopy"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + + if ((cna = numaCreate(na->nalloc)) == NULL) + return (NUMA *)ERROR_PTR("cna not made", procName, NULL); + cna->startx = na->startx; + cna->delx = na->delx; + + for (i = 0; i < na->n; i++) + numaAddNumber(cna, na->array[i]); + + return cna; +} + + +/*! + * \brief numaClone() + * + * \param[in] na + * \return ptr to same numa, or NULL on error + */ +NUMA * +numaClone(NUMA *na) +{ + PROCNAME("numaClone"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + + numaChangeRefcount(na, 1); + return na; +} + + +/*! + * \brief numaEmpty() + * + * \param[in] na + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This does not change the allocation of the array.
+ *          It just clears the number of stored numbers, so that
+ *          the array appears to be empty.
+ * 
+ */ +l_ok +numaEmpty(NUMA *na) +{ + PROCNAME("numaEmpty"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + + na->n = 0; + return 0; +} + + + +/*--------------------------------------------------------------------------* + * Number array: add number and extend array * + *--------------------------------------------------------------------------*/ +/*! + * \brief numaAddNumber() + * + * \param[in] na + * \param[in] val float or int to be added; stored as a float + * \return 0 if OK, 1 on error + */ +l_ok +numaAddNumber(NUMA *na, + l_float32 val) +{ +l_int32 n; + + PROCNAME("numaAddNumber"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + + n = numaGetCount(na); + if (n >= na->nalloc) + numaExtendArray(na); + na->array[n] = val; + na->n++; + return 0; +} + + +/*! + * \brief numaExtendArray() + * + * \param[in] na + * \return 0 if OK, 1 on error + */ +static l_int32 +numaExtendArray(NUMA *na) +{ + PROCNAME("numaExtendArray"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if ((na->array = (l_float32 *)reallocNew((void **)&na->array, + sizeof(l_float32) * na->nalloc, + 2 * sizeof(l_float32) * na->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + na->nalloc *= 2; + return 0; +} + + +/*! + * \brief numaInsertNumber() + * + * \param[in] na + * \param[in] index location in na to insert new value + * \param[in] val float32 or integer to be added + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts na[i] --> na[i + 1] for all i >= index,
+ *          and then inserts val as na[index].
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ *
+ * 
+ */ +l_ok +numaInsertNumber(NUMA *na, + l_int32 index, + l_float32 val) +{ +l_int32 i, n; + + PROCNAME("numaInsertNumber"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + + if (n >= na->nalloc) + numaExtendArray(na); + for (i = n; i > index; i--) + na->array[i] = na->array[i - 1]; + na->array[index] = val; + na->n++; + return 0; +} + + +/*! + * \brief numaRemoveNumber() + * + * \param[in] na + * \param[in] index element to be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts na[i] --> na[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +numaRemoveNumber(NUMA *na, + l_int32 index) +{ +l_int32 i, n; + + PROCNAME("numaRemoveNumber"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + for (i = index + 1; i < n; i++) + na->array[i - 1] = na->array[i]; + na->n--; + return 0; +} + + +/*! + * \brief numaReplaceNumber() + * + * \param[in] na + * \param[in] index element to be replaced + * \param[in] val new value to replace old one + * \return 0 if OK, 1 on error + */ +l_ok +numaReplaceNumber(NUMA *na, + l_int32 index, + l_float32 val) +{ +l_int32 n; + + PROCNAME("numaReplaceNumber"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + na->array[index] = val; + return 0; +} + + +/*----------------------------------------------------------------------* + * Numa accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief numaGetCount() + * + * \param[in] na + * \return count, or 0 if no numbers or on error + */ +l_int32 +numaGetCount(NUMA *na) +{ + PROCNAME("numaGetCount"); + + if (!na) + return ERROR_INT("na not defined", procName, 0); + return na->n; +} + + +/*! + * \brief numaSetCount() + * + * \param[in] na + * \param[in] newcount + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If newcount <= na->nalloc, this resets na->n.
+ *          Using newcount = 0 is equivalent to numaEmpty().
+ *      (2) If newcount > na->nalloc, this causes a realloc
+ *          to a size na->nalloc = newcount.
+ *      (3) All the previously unused values in na are set to 0.0.
+ * 
+ */ +l_ok +numaSetCount(NUMA *na, + l_int32 newcount) +{ + PROCNAME("numaSetCount"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (newcount > na->nalloc) { + if ((na->array = (l_float32 *)reallocNew((void **)&na->array, + sizeof(l_float32) * na->nalloc, + sizeof(l_float32) * newcount)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + na->nalloc = newcount; + } + na->n = newcount; + return 0; +} + + +/*! + * \brief numaGetFValue() + * + * \param[in] na + * \param[in] index into numa + * \param[out] pval float value; set to 0.0 on error + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caller may need to check the function return value to
+ *          decide if a 0.0 in the returned ival is valid.
+ * 
+ */ +l_ok +numaGetFValue(NUMA *na, + l_int32 index, + l_float32 *pval) +{ + PROCNAME("numaGetFValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if (index < 0 || index >= na->n) + return ERROR_INT("index not valid", procName, 1); + + *pval = na->array[index]; + return 0; +} + + +/*! + * \brief numaGetIValue() + * + * \param[in] na + * \param[in] index into numa + * \param[out] pival integer value; set to 0 on error + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caller may need to check the function return value to
+ *          decide if a 0 in the returned ival is valid.
+ * 
+ */ +l_ok +numaGetIValue(NUMA *na, + l_int32 index, + l_int32 *pival) +{ +l_float32 val; + + PROCNAME("numaGetIValue"); + + if (!pival) + return ERROR_INT("&ival not defined", procName, 1); + *pival = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if (index < 0 || index >= na->n) + return ERROR_INT("index not valid", procName, 1); + + val = na->array[index]; + *pival = (l_int32)(val + L_SIGN(val) * 0.5); + return 0; +} + + +/*! + * \brief numaSetValue() + * + * \param[in] na + * \param[in] index to element to be set + * \param[in] val to set + * \return 0 if OK; 1 on error + */ +l_ok +numaSetValue(NUMA *na, + l_int32 index, + l_float32 val) +{ + PROCNAME("numaSetValue"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (index < 0 || index >= na->n) + return ERROR_INT("index not valid", procName, 1); + + na->array[index] = val; + return 0; +} + + +/*! + * \brief numaShiftValue() + * + * \param[in] na + * \param[in] index to element to change relative to the current value + * \param[in] diff increment if diff > 0 or decrement if diff < 0 + * \return 0 if OK; 1 on error + */ +l_ok +numaShiftValue(NUMA *na, + l_int32 index, + l_float32 diff) +{ + PROCNAME("numaShiftValue"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (index < 0 || index >= na->n) + return ERROR_INT("index not valid", procName, 1); + + na->array[index] += diff; + return 0; +} + + +/*! + * \brief numaGetIArray() + * + * \param[in] na + * \return a copy of the bare internal array, integerized + * by rounding, or NULL on error + *
+ * Notes:
+ *      (1) A copy of the array is always made, because we need to
+ *          generate an integer array from the bare float array.
+ *          The caller is responsible for freeing the array.
+ *      (2) The array size is determined by the number of stored numbers,
+ *          not by the size of the allocated array in the Numa.
+ *      (3) This function is provided to simplify calculations
+ *          using the bare internal array, rather than continually
+ *          calling accessors on the numa.  It is typically used
+ *          on an array of size 256.
+ * 
+ */ +l_int32 * +numaGetIArray(NUMA *na) +{ +l_int32 i, n, ival; +l_int32 *array; + + PROCNAME("numaGetIArray"); + + if (!na) + return (l_int32 *)ERROR_PTR("na not defined", procName, NULL); + + n = numaGetCount(na); + if ((array = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) + return (l_int32 *)ERROR_PTR("array not made", procName, NULL); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + array[i] = ival; + } + + return array; +} + + +/*! + * \brief numaGetFArray() + * + * \param[in] na + * \param[in] copyflag L_NOCOPY or L_COPY + * \return either the bare internal array or a copy of it, + * or NULL on error + * + *
+ * Notes:
+ *      (1) If copyflag == L_COPY, it makes a copy which the caller
+ *          is responsible for freeing.  Otherwise, it operates
+ *          directly on the bare array of the numa.
+ *      (2) Very important: for L_NOCOPY, any writes to the array
+ *          will be in the numa.  Do not write beyond the size of
+ *          the count field, because it will not be accessible
+ *          from the numa!  If necessary, be sure to set the count
+ *          field to a larger number (such as the alloc size)
+ *          BEFORE calling this function.  Creating with numaMakeConstant()
+ *          is another way to insure full initialization.
+ * 
+ */ +l_float32 * +numaGetFArray(NUMA *na, + l_int32 copyflag) +{ +l_int32 i, n; +l_float32 *array; + + PROCNAME("numaGetFArray"); + + if (!na) + return (l_float32 *)ERROR_PTR("na not defined", procName, NULL); + + if (copyflag == L_NOCOPY) { + array = na->array; + } else { /* copyflag == L_COPY */ + n = numaGetCount(na); + if ((array = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32))) == NULL) + return (l_float32 *)ERROR_PTR("array not made", procName, NULL); + for (i = 0; i < n; i++) + array[i] = na->array[i]; + } + + return array; +} + + +/*! + * \brief numaGetRefCount() + * + * \param[in] na + * \return refcount, or UNDEF on error + */ +l_int32 +numaGetRefcount(NUMA *na) +{ + PROCNAME("numaGetRefcount"); + + if (!na) + return ERROR_INT("na not defined", procName, UNDEF); + return na->refcount; +} + + +/*! + * \brief numaChangeRefCount() + * + * \param[in] na + * \param[in] delta change to be applied + * \return 0 if OK, 1 on error + */ +l_ok +numaChangeRefcount(NUMA *na, + l_int32 delta) +{ + PROCNAME("numaChangeRefcount"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + na->refcount += delta; + return 0; +} + + +/*! + * \brief numaGetParameters() + * + * \param[in] na + * \param[out] pstartx [optional] startx + * \param[out] pdelx [optional] delx + * \return 0 if OK, 1 on error + */ +l_ok +numaGetParameters(NUMA *na, + l_float32 *pstartx, + l_float32 *pdelx) +{ + PROCNAME("numaGetParameters"); + + if (!pdelx && !pstartx) + return ERROR_INT("no return val requested", procName, 1); + if (pstartx) *pstartx = 0.0; + if (pdelx) *pdelx = 1.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if (pstartx) *pstartx = na->startx; + if (pdelx) *pdelx = na->delx; + return 0; +} + + +/*! + * \brief numaSetParameters() + * + * \param[in] na + * \param[in] startx x value corresponding to na[0] + * \param[in] delx difference in x values for the situation where the + * elements of na correspond to the evaulation of a + * function at equal intervals of size %delx + * \return 0 if OK, 1 on error + */ +l_ok +numaSetParameters(NUMA *na, + l_float32 startx, + l_float32 delx) +{ + PROCNAME("numaSetParameters"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + + na->startx = startx; + na->delx = delx; + return 0; +} + + +/*! + * \brief numaCopyParameters() + * + * \param[in] nad destination Numa + * \param[in] nas source Numa + * \return 0 if OK, 1 on error + */ +l_ok +numaCopyParameters(NUMA *nad, + NUMA *nas) +{ +l_float32 start, binsize; + + PROCNAME("numaCopyParameters"); + + if (!nas || !nad) + return ERROR_INT("nas and nad not both defined", procName, 1); + + numaGetParameters(nas, &start, &binsize); + numaSetParameters(nad, start, binsize); + return 0; +} + + +/*----------------------------------------------------------------------* + * Convert to string array * + *----------------------------------------------------------------------*/ +/*! + * \brief numaConvertToSarray() + * + * \param[in] na + * \param[in] size1 size of conversion field + * \param[in] size2 for float conversion: size of field to the right + * of the decimal point + * \param[in] addzeros for integer conversion: to add lead zeros + * \param[in] type L_INTEGER_VALUE, L_FLOAT_VALUE + * \return a sarray of the float values converted to strings + * representing either integer or float values; or NULL on error. + * + *
+ * Notes:
+ *      (1) For integer conversion, size2 is ignored.
+ *          For float conversion, addzeroes is ignored.
+ * 
+ */ +SARRAY * +numaConvertToSarray(NUMA *na, + l_int32 size1, + l_int32 size2, + l_int32 addzeros, + l_int32 type) +{ +char fmt[32], strbuf[64]; +l_int32 i, n, ival; +l_float32 fval; +SARRAY *sa; + + PROCNAME("numaConvertToSarray"); + + if (!na) + return (SARRAY *)ERROR_PTR("na not defined", procName, NULL); + if (type != L_INTEGER_VALUE && type != L_FLOAT_VALUE) + return (SARRAY *)ERROR_PTR("invalid type", procName, NULL); + + if (type == L_INTEGER_VALUE) { + if (addzeros) + snprintf(fmt, sizeof(fmt), "%%0%dd", size1); + else + snprintf(fmt, sizeof(fmt), "%%%dd", size1); + } else { /* L_FLOAT_VALUE */ + snprintf(fmt, sizeof(fmt), "%%%d.%df", size1, size2); + } + + n = numaGetCount(na); + if ((sa = sarrayCreate(n)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + + for (i = 0; i < n; i++) { + if (type == L_INTEGER_VALUE) { + numaGetIValue(na, i, &ival); + snprintf(strbuf, sizeof(strbuf), fmt, ival); + } else { /* L_FLOAT_VALUE */ + numaGetFValue(na, i, &fval); + snprintf(strbuf, sizeof(strbuf), fmt, fval); + } + sarrayAddString(sa, strbuf, L_COPY); + } + + return sa; +} + + +/*----------------------------------------------------------------------* + * Serialize numa for I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief numaRead() + * + * \param[in] filename + * \return na, or NULL on error + */ +NUMA * +numaRead(const char *filename) +{ +FILE *fp; +NUMA *na; + + PROCNAME("numaRead"); + + if (!filename) + return (NUMA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (NUMA *)ERROR_PTR("stream not opened", procName, NULL); + na = numaReadStream(fp); + fclose(fp); + if (!na) + return (NUMA *)ERROR_PTR("na not read", procName, NULL); + return na; +} + + +/*! + * \brief numaReadStream() + * + * \param[in] fp file stream + * \return numa, or NULL on error + */ +NUMA * +numaReadStream(FILE *fp) +{ +l_int32 i, n, index, ret, version; +l_float32 val, startx, delx; +NUMA *na; + + PROCNAME("numaReadStream"); + + if (!fp) + return (NUMA *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, "\nNuma Version %d\n", &version); + if (ret != 1) + return (NUMA *)ERROR_PTR("not a numa file", procName, NULL); + if (version != NUMA_VERSION_NUMBER) + return (NUMA *)ERROR_PTR("invalid numa version", procName, NULL); + if (fscanf(fp, "Number of numbers = %d\n", &n) != 1) + return (NUMA *)ERROR_PTR("invalid number of numbers", procName, NULL); + + if (n > MaxArraySize) { + L_ERROR("n = %d > %d\n", procName, n, MaxArraySize); + return NULL; + } + if ((na = numaCreate(n)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + + for (i = 0; i < n; i++) { + if (fscanf(fp, " [%d] = %f\n", &index, &val) != 2) { + numaDestroy(&na); + return (NUMA *)ERROR_PTR("bad input data", procName, NULL); + } + numaAddNumber(na, val); + } + + /* Optional data */ + if (fscanf(fp, "startx = %f, delx = %f\n", &startx, &delx) == 2) + numaSetParameters(na, startx, delx); + + return na; +} + + +/*! + * \brief numaReadMem() + * + * \param[in] data numa serialization; in ascii + * \param[in] size of data; can use strlen to get it + * \return na, or NULL on error + */ +NUMA * +numaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +NUMA *na; + + PROCNAME("numaReadMem"); + + if (!data) + return (NUMA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (NUMA *)ERROR_PTR("stream not opened", procName, NULL); + + na = numaReadStream(fp); + fclose(fp); + if (!na) L_ERROR("numa not read\n", procName); + return na; +} + + +/*! + * \brief numaWriteDebug() + * + * \param[in] filename + * \param[in] na + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of numaWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +numaWriteDebug(const char *filename, + NUMA *na) +{ + PROCNAME("numaWriteDebug"); + + if (LeptDebugOK) { + return numaWrite(filename, na); + } else { + L_INFO("write to named temp file %s is disabled\n", procName, filename); + return 0; + } +} + + +/*! + * \brief numaWrite() + * + * \param[in] filename + * \param[in] na + * \return 0 if OK, 1 on error + */ +l_ok +numaWrite(const char *filename, + NUMA *na) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("numaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = numaWriteStream(fp, na); + fclose(fp); + if (ret) + return ERROR_INT("na not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief numaWriteStream() + * + * \param[in] fp file stream + * \param[in] na + * \return 0 if OK, 1 on error + */ +l_ok +numaWriteStream(FILE *fp, + NUMA *na) +{ +l_int32 i, n; +l_float32 startx, delx; + + PROCNAME("numaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + + n = numaGetCount(na); + fprintf(fp, "\nNuma Version %d\n", NUMA_VERSION_NUMBER); + fprintf(fp, "Number of numbers = %d\n", n); + for (i = 0; i < n; i++) + fprintf(fp, " [%d] = %f\n", i, na->array[i]); + fprintf(fp, "\n"); + + /* Optional data */ + numaGetParameters(na, &startx, &delx); + if (startx != 0.0 || delx != 1.0) + fprintf(fp, "startx = %f, delx = %f\n", startx, delx); + + return 0; +} + + +/*! + * \brief numaWriteMem() + * + * \param[out] pdata data of serialized numa; ascii + * \param[out] psize size of returned data + * \param[in] na + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a numa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +numaWriteMem(l_uint8 **pdata, + size_t *psize, + NUMA *na) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("numaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = numaWriteStream(fp, na); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = numaWriteStream(fp, na); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*--------------------------------------------------------------------------* + * Numaa creation, destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief numaaCreate() + * + * \param[in] n size of numa ptr array to be alloc'd 0 for default + * \return naa, or NULL on error + * + */ +NUMAA * +numaaCreate(l_int32 n) +{ +NUMAA *naa; + + PROCNAME("numaaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialArraySize; + + naa = (NUMAA *)LEPT_CALLOC(1, sizeof(NUMAA)); + if ((naa->numa = (NUMA **)LEPT_CALLOC(n, sizeof(NUMA *))) == NULL) { + numaaDestroy(&naa); + return (NUMAA *)ERROR_PTR("numa ptr array not made", procName, NULL); + } + + naa->nalloc = n; + naa->n = 0; + return naa; +} + + +/*! + * \brief numaaCreateFull() + * + * \param[in] nptr size of numa ptr array to be alloc'd + * \param[in] n size of individual numa arrays to be allocated + * to 0 for default + * \return naa, or NULL on error + * + *
+ * Notes:
+ *      (1) This allocates numaa and fills the array with allocated numas.
+ *          In use, after calling this function, use
+ *              numaaAddNumber(naa, index, val);
+ *          to add val to the index-th numa in naa.
+ * 
+ */ +NUMAA * +numaaCreateFull(l_int32 nptr, + l_int32 n) +{ +l_int32 i; +NUMAA *naa; +NUMA *na; + + naa = numaaCreate(nptr); + for (i = 0; i < nptr; i++) { + na = numaCreate(n); + numaaAddNuma(naa, na, L_INSERT); + } + + return naa; +} + + +/*! + * \brief numaaTruncate() + * + * \param[in] naa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This identifies the largest index containing a numa that
+ *          has any numbers within it, destroys all numa beyond that
+ *          index, and resets the count.
+ * 
+ */ +l_ok +numaaTruncate(NUMAA *naa) +{ +l_int32 i, n, nn; +NUMA *na; + + PROCNAME("numaaTruncate"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + + n = numaaGetCount(naa); + for (i = n - 1; i >= 0; i--) { + na = numaaGetNuma(naa, i, L_CLONE); + if (!na) + continue; + nn = numaGetCount(na); + numaDestroy(&na); + if (nn == 0) + numaDestroy(&naa->numa[i]); + else + break; + } + naa->n = i + 1; + return 0; +} + + +/*! + * \brief numaaDestroy() + * + * \param[in,out] pnaa to be destroyed and nulled, if it exists + * \return void + */ +void +numaaDestroy(NUMAA **pnaa) +{ +l_int32 i; +NUMAA *naa; + + PROCNAME("numaaDestroy"); + + if (pnaa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((naa = *pnaa) == NULL) + return; + + for (i = 0; i < naa->n; i++) + numaDestroy(&naa->numa[i]); + LEPT_FREE(naa->numa); + LEPT_FREE(naa); + *pnaa = NULL; + + return; +} + + + +/*--------------------------------------------------------------------------* + * Add Numa to Numaa * + *--------------------------------------------------------------------------*/ +/*! + * \brief numaaAddNuma() + * + * \param[in] naa + * \param[in] na to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +numaaAddNuma(NUMAA *naa, + NUMA *na, + l_int32 copyflag) +{ +l_int32 n; +NUMA *nac; + + PROCNAME("numaaAddNuma"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if (copyflag == L_INSERT) { + nac = na; + } else if (copyflag == L_COPY) { + if ((nac = numaCopy(na)) == NULL) + return ERROR_INT("nac not made", procName, 1); + } else if (copyflag == L_CLONE) { + nac = numaClone(na); + } else { + return ERROR_INT("invalid copyflag", procName, 1); + } + + n = numaaGetCount(naa); + if (n >= naa->nalloc) + numaaExtendArray(naa); + naa->numa[n] = nac; + naa->n++; + return 0; +} + + +/*! + * \brief numaaExtendArray() + * + * \param[in] naa + * \return 0 if OK, 1 on error + */ +static l_int32 +numaaExtendArray(NUMAA *naa) +{ + PROCNAME("numaaExtendArray"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + + if ((naa->numa = (NUMA **)reallocNew((void **)&naa->numa, + sizeof(NUMA *) * naa->nalloc, + 2 * sizeof(NUMA *) * naa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + naa->nalloc *= 2; + return 0; +} + + +/*----------------------------------------------------------------------* + * Numaa accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief numaaGetCount() + * + * \param[in] naa + * \return count number of numa, or 0 if no numa or on error + */ +l_int32 +numaaGetCount(NUMAA *naa) +{ + PROCNAME("numaaGetCount"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 0); + return naa->n; +} + + +/*! + * \brief numaaGetNumaCount() + * + * \param[in] naa + * \param[in] index of numa in naa + * \return count of numbers in the referenced numa, or 0 on error. + */ +l_int32 +numaaGetNumaCount(NUMAA *naa, + l_int32 index) +{ + PROCNAME("numaaGetNumaCount"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 0); + if (index < 0 || index >= naa->n) + return ERROR_INT("invalid index into naa", procName, 0); + return numaGetCount(naa->numa[index]); +} + + +/*! + * \brief numaaGetNumberCount() + * + * \param[in] naa + * \return count total number of numbers in the numaa, + * or 0 if no numbers or on error + */ +l_int32 +numaaGetNumberCount(NUMAA *naa) +{ +NUMA *na; +l_int32 n, sum, i; + + PROCNAME("numaaGetNumberCount"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 0); + + n = numaaGetCount(naa); + for (sum = 0, i = 0; i < n; i++) { + na = numaaGetNuma(naa, i, L_CLONE); + sum += numaGetCount(na); + numaDestroy(&na); + } + + return sum; +} + + +/*! + * \brief numaaGetPtrArray() + * + * \param[in] naa + * \return the internal array of ptrs to Numa, or NULL on error + * + *
+ * Notes:
+ *      (1) This function is convenient for doing direct manipulation on
+ *          a fixed size array of Numas.  To do this, it sets the count
+ *          to the full size of the allocated array of Numa ptrs.
+ *          The originating Numaa owns this array: DO NOT free it!
+ *      (2) Intended usage:
+ *            Numaa *naa = numaaCreate(n);
+ *            Numa **array = numaaGetPtrArray(naa);
+ *             ...  [manipulate Numas directly on the array]
+ *            numaaDestroy(&naa);
+ *      (3) Cautions:
+ *           ~ Do not free this array; it is owned by tne Numaa.
+ *           ~ Do not call any functions on the Numaa, other than
+ *             numaaDestroy() when you're finished with the array.
+ *             Adding a Numa will force a resize, destroying the ptr array.
+ *           ~ Do not address the array outside its allocated size.
+ *             With the bare array, there are no protections.  If the
+ *             allocated size is n, array[n] is an error.
+ * 
+ */ +NUMA ** +numaaGetPtrArray(NUMAA *naa) +{ + PROCNAME("numaaGetPtrArray"); + + if (!naa) + return (NUMA **)ERROR_PTR("naa not defined", procName, NULL); + + naa->n = naa->nalloc; + return naa->numa; +} + + +/*! + * \brief numaaGetNuma() + * + * \param[in] naa + * \param[in] index to the index-th numa + * \param[in] accessflag L_COPY or L_CLONE + * \return numa, or NULL on error + */ +NUMA * +numaaGetNuma(NUMAA *naa, + l_int32 index, + l_int32 accessflag) +{ + PROCNAME("numaaGetNuma"); + + if (!naa) + return (NUMA *)ERROR_PTR("naa not defined", procName, NULL); + if (index < 0 || index >= naa->n) + return (NUMA *)ERROR_PTR("index not valid", procName, NULL); + + if (accessflag == L_COPY) + return numaCopy(naa->numa[index]); + else if (accessflag == L_CLONE) + return numaClone(naa->numa[index]); + else + return (NUMA *)ERROR_PTR("invalid accessflag", procName, NULL); +} + + +/*! + * \brief numaaReplaceNuma() + * + * \param[in] naa + * \param[in] index to the index-th numa + * \param[in] na insert and replace any existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Any existing numa is destroyed, and the input one
+ *          is inserted in its place.
+ *      (2) If the index is invalid, return 1 (error)
+ * 
+ */ +l_ok +numaaReplaceNuma(NUMAA *naa, + l_int32 index, + NUMA *na) +{ +l_int32 n; + + PROCNAME("numaaReplaceNuma"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaaGetCount(naa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + + numaDestroy(&naa->numa[index]); + naa->numa[index] = na; + return 0; +} + + +/*! + * \brief numaaGetValue() + * + * \param[in] naa + * \param[in] i index of numa within numaa + * \param[in] j index into numa + * \param[out] pfval [optional] float value + * \param[out] pival [optional] int value + * \return 0 if OK, 1 on error + */ +l_ok +numaaGetValue(NUMAA *naa, + l_int32 i, + l_int32 j, + l_float32 *pfval, + l_int32 *pival) +{ +l_int32 n; +NUMA *na; + + PROCNAME("numaaGetValue"); + + if (!pfval && !pival) + return ERROR_INT("no return val requested", procName, 1); + if (pfval) *pfval = 0.0; + if (pival) *pival = 0; + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + n = numaaGetCount(naa); + if (i < 0 || i >= n) + return ERROR_INT("invalid index into naa", procName, 1); + na = naa->numa[i]; + if (j < 0 || j >= na->n) + return ERROR_INT("invalid index into na", procName, 1); + if (pfval) *pfval = na->array[j]; + if (pival) *pival = (l_int32)(na->array[j]); + return 0; +} + + +/*! + * \brief numaaAddNumber() + * + * \param[in] naa + * \param[in] index of numa within numaa + * \param[in] val float or int to be added; stored as a float + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Adds to an existing numa only.
+ * 
+ */ +l_ok +numaaAddNumber(NUMAA *naa, + l_int32 index, + l_float32 val) +{ +l_int32 n; +NUMA *na; + + PROCNAME("numaaAddNumber"); + + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + n = numaaGetCount(naa); + if (index < 0 || index >= n) + return ERROR_INT("invalid index in naa", procName, 1); + + na = numaaGetNuma(naa, index, L_CLONE); + numaAddNumber(na, val); + numaDestroy(&na); + return 0; +} + + +/*----------------------------------------------------------------------* + * Serialize numaa for I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief numaaRead() + * + * \param[in] filename + * \return naa, or NULL on error + */ +NUMAA * +numaaRead(const char *filename) +{ +FILE *fp; +NUMAA *naa; + + PROCNAME("numaaRead"); + + if (!filename) + return (NUMAA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (NUMAA *)ERROR_PTR("stream not opened", procName, NULL); + naa = numaaReadStream(fp); + fclose(fp); + if (!naa) + return (NUMAA *)ERROR_PTR("naa not read", procName, NULL); + return naa; +} + + +/*! + * \brief numaaReadStream() + * + * \param[in] fp file stream + * \return naa, or NULL on error + */ +NUMAA * +numaaReadStream(FILE *fp) +{ +l_int32 i, n, index, ret, version; +NUMA *na; +NUMAA *naa; + + PROCNAME("numaaReadStream"); + + if (!fp) + return (NUMAA *)ERROR_PTR("stream not defined", procName, NULL); + + ret = fscanf(fp, "\nNumaa Version %d\n", &version); + if (ret != 1) + return (NUMAA *)ERROR_PTR("not a numa file", procName, NULL); + if (version != NUMA_VERSION_NUMBER) + return (NUMAA *)ERROR_PTR("invalid numaa version", procName, NULL); + if (fscanf(fp, "Number of numa = %d\n\n", &n) != 1) + return (NUMAA *)ERROR_PTR("invalid number of numa", procName, NULL); + + if (n > MaxPtrArraySize) { + L_ERROR("n = %d > %d\n", procName, n, MaxPtrArraySize); + return NULL; + } + if ((naa = numaaCreate(n)) == NULL) + return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); + + for (i = 0; i < n; i++) { + if (fscanf(fp, "Numa[%d]:", &index) != 1) { + numaaDestroy(&naa); + return (NUMAA *)ERROR_PTR("invalid numa header", procName, NULL); + } + if ((na = numaReadStream(fp)) == NULL) { + numaaDestroy(&naa); + return (NUMAA *)ERROR_PTR("na not made", procName, NULL); + } + numaaAddNuma(naa, na, L_INSERT); + } + + return naa; +} + + +/*! + * \brief numaaReadMem() + * + * \param[in] data numaa serialization; in ascii + * \param[in] size of data; can use strlen to get it + * \return naa, or NULL on error + */ +NUMAA * +numaaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +NUMAA *naa; + + PROCNAME("numaaReadMem"); + + if (!data) + return (NUMAA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (NUMAA *)ERROR_PTR("stream not opened", procName, NULL); + + naa = numaaReadStream(fp); + fclose(fp); + if (!naa) L_ERROR("naa not read\n", procName); + return naa; +} + + +/*! + * \brief numaaWrite() + * + * \param[in] filename + * \param[in] naa + * \return 0 if OK, 1 on error + */ +l_ok +numaaWrite(const char *filename, + NUMAA *naa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("numaaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = numaaWriteStream(fp, naa); + fclose(fp); + if (ret) + return ERROR_INT("naa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief numaaWriteStream() + * + * \param[in] fp file stream + * \param[in] naa + * \return 0 if OK, 1 on error + */ +l_ok +numaaWriteStream(FILE *fp, + NUMAA *naa) +{ +l_int32 i, n; +NUMA *na; + + PROCNAME("numaaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + + n = numaaGetCount(naa); + fprintf(fp, "\nNumaa Version %d\n", NUMA_VERSION_NUMBER); + fprintf(fp, "Number of numa = %d\n\n", n); + for (i = 0; i < n; i++) { + if ((na = numaaGetNuma(naa, i, L_CLONE)) == NULL) + return ERROR_INT("na not found", procName, 1); + fprintf(fp, "Numa[%d]:", i); + numaWriteStream(fp, na); + numaDestroy(&na); + } + + return 0; +} + + +/*! + * \brief numaaWriteMem() + * + * \param[out] pdata data of serialized numaa; ascii + * \param[out] psize size of returned data + * \param[in] naa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a numaa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +numaaWriteMem(l_uint8 **pdata, + size_t *psize, + NUMAA *naa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("numaaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = numaaWriteStream(fp, naa); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = numaaWriteStream(fp, naa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/numafunc1.c b/hgdriver/3rdparty/hgOCR/leptonica/numafunc1.c new file mode 100644 index 0000000..e46092b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/numafunc1.c @@ -0,0 +1,3488 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file numafunc1.c + *
+ *
+ *      --------------------------------------
+ *      This file has these Numa utilities:
+ *         - arithmetic operations
+ *         - simple data analysis
+ *         - generation of special sequences
+ *         - permutations
+ *         - interpolation
+ *         - sorting
+ *         - data analysis requiring sorting
+ *         - joins and rearrangements
+ *      --------------------------------------
+ *
+ *      Arithmetic and logic
+ *          NUMA        *numaArithOp()
+ *          NUMA        *numaLogicalOp()
+ *          NUMA        *numaInvert()
+ *          l_int32      numaSimilar()
+ *          l_int32      numaAddToNumber()
+ *
+ *      Simple extractions
+ *          l_int32      numaGetMin()
+ *          l_int32      numaGetMax()
+ *          l_int32      numaGetSum()
+ *          NUMA        *numaGetPartialSums()
+ *          l_int32      numaGetSumOnInterval()
+ *          l_int32      numaHasOnlyIntegers()
+ *          NUMA        *numaSubsample()
+ *          NUMA        *numaMakeDelta()
+ *          NUMA        *numaMakeSequence()
+ *          NUMA        *numaMakeConstant()
+ *          NUMA        *numaMakeAbsValue()
+ *          NUMA        *numaAddBorder()
+ *          NUMA        *numaAddSpecifiedBorder()
+ *          NUMA        *numaRemoveBorder()
+ *          l_int32      numaCountNonzeroRuns()
+ *          l_int32      numaGetNonzeroRange()
+ *          l_int32      numaGetCountRelativeToZero()
+ *          NUMA        *numaClipToInterval()
+ *          NUMA        *numaMakeThresholdIndicator()
+ *          NUMA        *numaUniformSampling()
+ *          NUMA        *numaReverse()
+ *
+ *      Signal feature extraction
+ *          NUMA        *numaLowPassIntervals()
+ *          NUMA        *numaThresholdEdges()
+ *          NUMA        *numaGetSpanValues()
+ *          NUMA        *numaGetEdgeValues()
+ *
+ *      Interpolation
+ *          l_int32      numaInterpolateEqxVal()
+ *          l_int32      numaInterpolateEqxInterval()
+ *          l_int32      numaInterpolateArbxVal()
+ *          l_int32      numaInterpolateArbxInterval()
+ *
+ *      Functions requiring interpolation
+ *          l_int32      numaFitMax()
+ *          l_int32      numaDifferentiateInterval()
+ *          l_int32      numaIntegrateInterval()
+ *
+ *      Sorting
+ *          NUMA        *numaSortGeneral()
+ *          NUMA        *numaSortAutoSelect()
+ *          NUMA        *numaSortIndexAutoSelect()
+ *          l_int32      numaChooseSortType()
+ *          NUMA        *numaSort()
+ *          NUMA        *numaBinSort()
+ *          NUMA        *numaGetSortIndex()
+ *          NUMA        *numaGetBinSortIndex()
+ *          NUMA        *numaSortByIndex()
+ *          l_int32      numaIsSorted()
+ *          l_int32      numaSortPair()
+ *          NUMA        *numaInvertMap()
+ *
+ *      Random permutation
+ *          NUMA        *numaPseudorandomSequence()
+ *          NUMA        *numaRandomPermutation()
+ *
+ *      Functions requiring sorting
+ *          l_int32      numaGetRankValue()
+ *          l_int32      numaGetMedian()
+ *          l_int32      numaGetBinnedMedian()
+ *          l_int32      numaGetMeanDevFromMedian()
+ *          l_int32      numaGetMedianDevFromMedian()
+ *          l_int32      numaGetMode()
+ *
+ *      Rearrangements
+ *          l_int32      numaJoin()
+ *          l_int32      numaaJoin()
+ *          NUMA        *numaaFlattenToNuma()
+ *
+ *    Things to remember when using the Numa:
+ *
+ *    (1) The numa is a struct, not an array.  Always use accessors
+ *        (see numabasic.c), never the fields directly.
+ *
+ *    (2) The number array holds l_float32 values.  It can also
+ *        be used to store l_int32 values.  See numabasic.c for
+ *        details on using the accessors.
+ *
+ *    (3) If you use numaCreate(), no numbers are stored and the size is 0.
+ *        You have to add numbers to increase the size.
+ *        If you want to start with a numa of a fixed size, with each
+ *        entry initialized to the same value, use numaMakeConstant().
+ *
+ *    (4) Occasionally, in the comments we denote the i-th element of a
+ *        numa by na[i].  This is conceptual only -- the numa is not an array!
+ * 
+ */ + +#include +#include "allheaders.h" + + +/*----------------------------------------------------------------------* + * Arithmetic and logical ops on Numas * + *----------------------------------------------------------------------*/ +/*! + * \brief numaArithOp() + * + * \param[in] nad [optional] can be null or equal to na1 (in-place + * \param[in] na1 + * \param[in] na2 + * \param[in] op L_ARITH_ADD, L_ARITH_SUBTRACT, + * L_ARITH_MULTIPLY, L_ARITH_DIVIDE + * \return nad always: operation applied to na1 and na2 + * + *
+ * Notes:
+ *      (1) The sizes of na1 and na2 must be equal.
+ *      (2) nad can only null or equal to na1.
+ *      (3) To add a constant to a numa, or to multipy a numa by
+ *          a constant, use numaTransform().
+ * 
+ */ +NUMA * +numaArithOp(NUMA *nad, + NUMA *na1, + NUMA *na2, + l_int32 op) +{ +l_int32 i, n; +l_float32 val1, val2; + + PROCNAME("numaArithOp"); + + if (!na1 || !na2) + return (NUMA *)ERROR_PTR("na1, na2 not both defined", procName, nad); + n = numaGetCount(na1); + if (n != numaGetCount(na2)) + return (NUMA *)ERROR_PTR("na1, na2 sizes differ", procName, nad); + if (nad && nad != na1) + return (NUMA *)ERROR_PTR("nad defined but not in-place", procName, nad); + if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT && + op != L_ARITH_MULTIPLY && op != L_ARITH_DIVIDE) + return (NUMA *)ERROR_PTR("invalid op", procName, nad); + if (op == L_ARITH_DIVIDE) { + for (i = 0; i < n; i++) { + numaGetFValue(na2, i, &val2); + if (val2 == 0.0) + return (NUMA *)ERROR_PTR("na2 has 0 element", procName, nad); + } + } + + /* If nad is not identical to na1, make it an identical copy */ + if (!nad) + nad = numaCopy(na1); + + for (i = 0; i < n; i++) { + numaGetFValue(nad, i, &val1); + numaGetFValue(na2, i, &val2); + switch (op) { + case L_ARITH_ADD: + numaSetValue(nad, i, val1 + val2); + break; + case L_ARITH_SUBTRACT: + numaSetValue(nad, i, val1 - val2); + break; + case L_ARITH_MULTIPLY: + numaSetValue(nad, i, val1 * val2); + break; + case L_ARITH_DIVIDE: + numaSetValue(nad, i, val1 / val2); + break; + default: + fprintf(stderr, " Unknown arith op: %d\n", op); + return nad; + } + } + + return nad; +} + + +/*! + * \brief numaLogicalOp() + * + * \param[in] nad [optional] can be null or equal to na1 (in-place + * \param[in] na1 + * \param[in] na2 + * \param[in] op L_UNION, L_INTERSECTION, L_SUBTRACTION, L_EXCLUSIVE_OR + * \return nad always: operation applied to na1 and na2 + * + *
+ * Notes:
+ *      (1) The sizes of na1 and na2 must be equal.
+ *      (2) nad can only be null or equal to na1.
+ *      (3) This is intended for use with indicator arrays (0s and 1s).
+ *          Input data is extracted as integers (0 == false, anything
+ *          else == true); output results are 0 and 1.
+ *      (4) L_SUBTRACTION is subtraction of val2 from val1.  For bit logical
+ *          arithmetic this is (val1 & ~val2), but because these values
+ *          are integers, we use (val1 && !val2).
+ * 
+ */ +NUMA * +numaLogicalOp(NUMA *nad, + NUMA *na1, + NUMA *na2, + l_int32 op) +{ +l_int32 i, n, val1, val2, val; + + PROCNAME("numaLogicalOp"); + + if (!na1 || !na2) + return (NUMA *)ERROR_PTR("na1, na2 not both defined", procName, nad); + n = numaGetCount(na1); + if (n != numaGetCount(na2)) + return (NUMA *)ERROR_PTR("na1, na2 sizes differ", procName, nad); + if (nad && nad != na1) + return (NUMA *)ERROR_PTR("nad defined; not in-place", procName, nad); + if (op != L_UNION && op != L_INTERSECTION && + op != L_SUBTRACTION && op != L_EXCLUSIVE_OR) + return (NUMA *)ERROR_PTR("invalid op", procName, nad); + + /* If nad is not identical to na1, make it an identical copy */ + if (!nad) + nad = numaCopy(na1); + + for (i = 0; i < n; i++) { + numaGetIValue(nad, i, &val1); + numaGetIValue(na2, i, &val2); + val1 = (val1 == 0) ? 0 : 1; + val2 = (val2 == 0) ? 0 : 1; + switch (op) { + case L_UNION: + val = (val1 || val2) ? 1 : 0; + numaSetValue(nad, i, val); + break; + case L_INTERSECTION: + val = (val1 && val2) ? 1 : 0; + numaSetValue(nad, i, val); + break; + case L_SUBTRACTION: + val = (val1 && !val2) ? 1 : 0; + numaSetValue(nad, i, val); + break; + case L_EXCLUSIVE_OR: + val = (val1 != val2) ? 1 : 0; + numaSetValue(nad, i, val); + break; + default: + fprintf(stderr, " Unknown logical op: %d\n", op); + return nad; + } + } + + return nad; +} + + +/*! + * \brief numaInvert() + * + * \param[in] nad [optional] can be null or equal to nas (in-place + * \param[in] nas + * \return nad always: 'inverts' nas + * + *
+ * Notes:
+ *      (1) This is intended for use with indicator arrays (0s and 1s).
+ *          It gives a boolean-type output, taking the input as
+ *          an integer and inverting it:
+ *              0              -->  1
+ *              anything else  -->   0
+ * 
+ */ +NUMA * +numaInvert(NUMA *nad, + NUMA *nas) +{ +l_int32 i, n, val; + + PROCNAME("numaInvert"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, nad); + if (nad && nad != nas) + return (NUMA *)ERROR_PTR("nad defined; not in-place", procName, nad); + + if (!nad) + nad = numaCopy(nas); + n = numaGetCount(nad); + for (i = 0; i < n; i++) { + numaGetIValue(nad, i, &val); + if (!val) + val = 1; + else + val = 0; + numaSetValue(nad, i, val); + } + + return nad; +} + + +/*! + * \brief numaSimilar() + * + * \param[in] na1 + * \param[in] na2 + * \param[in] maxdiff use 0.0 for exact equality + * \param[out] psimilar 1 if similar; 0 if different + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Float values can differ slightly due to roundoff and
+ *          accumulated errors.  Using %maxdiff > 0.0 allows similar
+ *          arrays to be identified.
+ * 
+*/ +l_int32 +numaSimilar(NUMA *na1, + NUMA *na2, + l_float32 maxdiff, + l_int32 *psimilar) +{ +l_int32 i, n; +l_float32 val1, val2; + + PROCNAME("numaSimilar"); + + if (!psimilar) + return ERROR_INT("&similar not defined", procName, 1); + *psimilar = 0; + if (!na1 || !na2) + return ERROR_INT("na1 and na2 not both defined", procName, 1); + maxdiff = L_ABS(maxdiff); + + n = numaGetCount(na1); + if (n != numaGetCount(na2)) return 0; + + for (i = 0; i < n; i++) { + numaGetFValue(na1, i, &val1); + numaGetFValue(na2, i, &val2); + if (L_ABS(val1 - val2) > maxdiff) return 0; + } + + *psimilar = 1; + return 0; +} + + +/*! + * \brief numaAddToNumber() + * + * \param[in] na source numa + * \param[in] index element to be changed + * \param[in] val new value to be added + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is useful for accumulating sums, regardless of the index
+ *          order in which the values are made available.
+ *      (2) Before use, the numa has to be filled up to %index.  This would
+ *          typically be used by creating the numa with the full sized
+ *          array, initialized to 0.0, using numaMakeConstant().
+ * 
+ */ +l_ok +numaAddToNumber(NUMA *na, + l_int32 index, + l_float32 val) +{ +l_int32 n; + + PROCNAME("numaAddToNumber"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + na->array[index] += val; + return 0; +} + + +/*----------------------------------------------------------------------* + * Simple extractions * + *----------------------------------------------------------------------*/ +/*! + * \brief numaGetMin() + * + * \param[in] na source numa + * \param[out] pminval [optional] min value + * \param[out] piminloc [optional] index of min location + * \return 0 if OK; 1 on error + */ +l_ok +numaGetMin(NUMA *na, + l_float32 *pminval, + l_int32 *piminloc) +{ +l_int32 i, n, iminloc; +l_float32 val, minval; + + PROCNAME("numaGetMin"); + + if (!pminval && !piminloc) + return ERROR_INT("nothing to do", procName, 1); + if (pminval) *pminval = 0.0; + if (piminloc) *piminloc = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + minval = +1000000000.; + iminloc = 0; + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (val < minval) { + minval = val; + iminloc = i; + } + } + + if (pminval) *pminval = minval; + if (piminloc) *piminloc = iminloc; + return 0; +} + + +/*! + * \brief numaGetMax() + * + * \param[in] na source numa + * \param[out] pmaxval [optional] max value + * \param[out] pimaxloc [optional] index of max location + * \return 0 if OK; 1 on error + */ +l_ok +numaGetMax(NUMA *na, + l_float32 *pmaxval, + l_int32 *pimaxloc) +{ +l_int32 i, n, imaxloc; +l_float32 val, maxval; + + PROCNAME("numaGetMax"); + + if (!pmaxval && !pimaxloc) + return ERROR_INT("nothing to do", procName, 1); + if (pmaxval) *pmaxval = 0.0; + if (pimaxloc) *pimaxloc = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + maxval = -1000000000.; + imaxloc = 0; + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (val > maxval) { + maxval = val; + imaxloc = i; + } + } + + if (pmaxval) *pmaxval = maxval; + if (pimaxloc) *pimaxloc = imaxloc; + return 0; +} + + +/*! + * \brief numaGetSum() + * + * \param[in] na source numa + * \param[out] psum sum of values + * \return 0 if OK, 1 on error + */ +l_ok +numaGetSum(NUMA *na, + l_float32 *psum) +{ +l_int32 i, n; +l_float32 val, sum; + + PROCNAME("numaGetSum"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (!psum) + return ERROR_INT("&sum not defined", procName, 1); + + sum = 0.0; + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + sum += val; + } + *psum = sum; + return 0; +} + + +/*! + * \brief numaGetPartialSums() + * + * \param[in] na source numa + * \return nasum, or NULL on error + * + *
+ * Notes:
+ *      (1) nasum[i] is the sum for all j <= i of na[j].
+ *          So nasum[0] = na[0].
+ *      (2) If you want to generate a rank function, where rank[0] - 0.0,
+ *          insert a 0.0 at the beginning of the nasum array.
+ * 
+ */ +NUMA * +numaGetPartialSums(NUMA *na) +{ +l_int32 i, n; +l_float32 val, sum; +NUMA *nasum; + + PROCNAME("numaGetPartialSums"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + + n = numaGetCount(na); + nasum = numaCreate(n); + sum = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + sum += val; + numaAddNumber(nasum, sum); + } + return nasum; +} + + +/*! + * \brief numaGetSumOnInterval() + * + * \param[in] na source numa + * \param[in] first beginning index + * \param[in] last final index + * \param[out] psum sum of values in the index interval range + * \return 0 if OK, 1 on error + */ +l_ok +numaGetSumOnInterval(NUMA *na, + l_int32 first, + l_int32 last, + l_float32 *psum) +{ +l_int32 i, n, truelast; +l_float32 val, sum; + + PROCNAME("numaGetSumOnInterval"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (!psum) + return ERROR_INT("&sum not defined", procName, 1); + *psum = 0.0; + + sum = 0.0; + n = numaGetCount(na); + if (first >= n) /* not an error */ + return 0; + truelast = L_MIN(last, n - 1); + + for (i = first; i <= truelast; i++) { + numaGetFValue(na, i, &val); + sum += val; + } + *psum = sum; + return 0; +} + + +/*! + * \brief numaHasOnlyIntegers() + * + * \param[in] na source numa + * \param[in] maxsamples maximum number of samples to check + * \param[out] pallints 1 if all sampled values are ints; else 0 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Set %maxsamples == 0 to check every integer in na.  Otherwise,
+ *          this samples no more than %maxsamples.
+ * 
+ */ +l_ok +numaHasOnlyIntegers(NUMA *na, + l_int32 maxsamples, + l_int32 *pallints) +{ +l_int32 i, n, incr; +l_float32 val; + + PROCNAME("numaHasOnlyIntegers"); + + if (!pallints) + return ERROR_INT("&allints not defined", procName, 1); + *pallints = TRUE; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + if ((n = numaGetCount(na)) == 0) + return ERROR_INT("na empty", procName, 1); + if (maxsamples <= 0) + incr = 1; + else + incr = (l_int32)((n + maxsamples - 1) / maxsamples); + for (i = 0; i < n; i += incr) { + numaGetFValue(na, i, &val); + if (val != (l_int32)val) { + *pallints = FALSE; + return 0; + } + } + + return 0; +} + + +/*! + * \brief numaSubsample() + * + * \param[in] nas + * \param[in] subfactor subsample factor, >= 1 + * \return nad evenly sampled values from nas, or NULL on error + */ +NUMA * +numaSubsample(NUMA *nas, + l_int32 subfactor) +{ +l_int32 i, n; +l_float32 val; +NUMA *nad; + + PROCNAME("numaSubsample"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (subfactor < 1) + return (NUMA *)ERROR_PTR("subfactor < 1", procName, NULL); + + nad = numaCreate(0); + n = numaGetCount(nas); + for (i = 0; i < n; i++) { + if (i % subfactor != 0) continue; + numaGetFValue(nas, i, &val); + numaAddNumber(nad, val); + } + + return nad; +} + + +/*! + * \brief numaMakeDelta() + * + * \param[in] nas input numa + * \return numa of difference values val[i+1] - val[i], + * or NULL on error + */ +NUMA * +numaMakeDelta(NUMA *nas) +{ +l_int32 i, n, prev, cur; +NUMA *nad; + + PROCNAME("numaMakeDelta"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + nad = numaCreate(n - 1); + prev = 0; + for (i = 1; i < n; i++) { + numaGetIValue(nas, i, &cur); + numaAddNumber(nad, cur - prev); + prev = cur; + } + return nad; +} + + +/*! + * \brief numaMakeSequence() + * + * \param[in] startval + * \param[in] increment + * \param[in] size of sequence + * \return numa of sequence of evenly spaced values, or NULL on error + */ +NUMA * +numaMakeSequence(l_float32 startval, + l_float32 increment, + l_int32 size) +{ +l_int32 i; +l_float32 val; +NUMA *na; + + PROCNAME("numaMakeSequence"); + + if ((na = numaCreate(size)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + + for (i = 0; i < size; i++) { + val = startval + i * increment; + numaAddNumber(na, val); + } + + return na; +} + + +/*! + * \brief numaMakeConstant() + * + * \param[in] val + * \param[in] size of numa + * \return numa of given size with all entries equal to 'val', + * or NULL on error + */ +NUMA * +numaMakeConstant(l_float32 val, + l_int32 size) +{ + return numaMakeSequence(val, 0.0, size); +} + + +/*! + * \brief numaMakeAbsValue() + * + * \param[in] nad can be null for new array, or the same as nas for inplace + * \param[in] nas input numa + * \return nad with all numbers being the absval of the input, + * or NULL on error + */ +NUMA * +numaMakeAbsValue(NUMA *nad, + NUMA *nas) +{ +l_int32 i, n; +l_float32 val; + + PROCNAME("numaMakeAbsValue"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (nad && nad != nas) + return (NUMA *)ERROR_PTR("nad and not in-place", procName, NULL); + + if (!nad) + nad = numaCopy(nas); + n = numaGetCount(nad); + for (i = 0; i < n; i++) { + val = nad->array[i]; + nad->array[i] = L_ABS(val); + } + + return nad; +} + + +/*! + * \brief numaAddBorder() + * + * \param[in] nas + * \param[in] left number of elements to add before the start + * \param[in] right number of elements to add after the end + * \param[in] val initialize border elements + * \return nad with added elements at left and right, or NULL on error + */ +NUMA * +numaAddBorder(NUMA *nas, + l_int32 left, + l_int32 right, + l_float32 val) +{ +l_int32 i, n, len; +l_float32 startx, delx; +l_float32 *fas, *fad; +NUMA *nad; + + PROCNAME("numaAddBorder"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (left < 0) left = 0; + if (right < 0) right = 0; + if (left == 0 && right == 0) + return numaCopy(nas); + + n = numaGetCount(nas); + len = n + left + right; + nad = numaMakeConstant(val, len); + numaGetParameters(nas, &startx, &delx); + numaSetParameters(nad, startx - delx * left, delx); + fas = numaGetFArray(nas, L_NOCOPY); + fad = numaGetFArray(nad, L_NOCOPY); + for (i = 0; i < n; i++) + fad[left + i] = fas[i]; + + return nad; +} + + +/*! + * \brief numaAddSpecifiedBorder() + * + * \param[in] nas + * \param[in] left number of elements to add before the start + * \param[in] right number of elements to add after the end + * \param[in] type L_CONTINUED_BORDER, L_MIRRORED_BORDER + * \return nad with added elements at left and right, or NULL on error + */ +NUMA * +numaAddSpecifiedBorder(NUMA *nas, + l_int32 left, + l_int32 right, + l_int32 type) +{ +l_int32 i, n; +l_float32 *fa; +NUMA *nad; + + PROCNAME("numaAddSpecifiedBorder"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (left < 0) left = 0; + if (right < 0) right = 0; + if (left == 0 && right == 0) + return numaCopy(nas); + if (type != L_CONTINUED_BORDER && type != L_MIRRORED_BORDER) + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + n = numaGetCount(nas); + if (type == L_MIRRORED_BORDER && (left > n || right > n)) + return (NUMA *)ERROR_PTR("border too large", procName, NULL); + + nad = numaAddBorder(nas, left, right, 0); + n = numaGetCount(nad); + fa = numaGetFArray(nad, L_NOCOPY); + if (type == L_CONTINUED_BORDER) { + for (i = 0; i < left; i++) + fa[i] = fa[left]; + for (i = n - right; i < n; i++) + fa[i] = fa[n - right - 1]; + } else { /* type == L_MIRRORED_BORDER */ + for (i = 0; i < left; i++) + fa[i] = fa[2 * left - 1 - i]; + for (i = 0; i < right; i++) + fa[n - right + i] = fa[n - right - i - 1]; + } + + return nad; +} + + +/*! + * \brief numaRemoveBorder() + * + * \param[in] nas + * \param[in] left number of elements to remove from the start + * \param[in] right number of elements to remove up to the end + * \return nad with removed elements at left and right, or NULL on error + */ +NUMA * +numaRemoveBorder(NUMA *nas, + l_int32 left, + l_int32 right) +{ +l_int32 i, n, len; +l_float32 startx, delx; +l_float32 *fas, *fad; +NUMA *nad; + + PROCNAME("numaRemoveBorder"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (left < 0) left = 0; + if (right < 0) right = 0; + if (left == 0 && right == 0) + return numaCopy(nas); + + n = numaGetCount(nas); + if ((len = n - left - right) < 0) + return (NUMA *)ERROR_PTR("len < 0 after removal", procName, NULL); + nad = numaMakeConstant(0, len); + numaGetParameters(nas, &startx, &delx); + numaSetParameters(nad, startx + delx * left, delx); + fas = numaGetFArray(nas, L_NOCOPY); + fad = numaGetFArray(nad, L_NOCOPY); + for (i = 0; i < len; i++) + fad[i] = fas[left + i]; + + return nad; +} + + +/*! + * \brief numaCountNonzeroRuns() + * + * \param[in] na e.g., of pixel counts in rows or columns + * \param[out] pcount number of nonzero runs + * \return 0 if OK, 1 on error + */ +l_ok +numaCountNonzeroRuns(NUMA *na, + l_int32 *pcount) +{ +l_int32 n, i, val, count, inrun; + + PROCNAME("numaCountNonzeroRuns"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + count = 0; + inrun = FALSE; + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &val); + if (!inrun && val > 0) { + count++; + inrun = TRUE; + } else if (inrun && val == 0) { + inrun = FALSE; + } + } + *pcount = count; + return 0; +} + + +/*! + * \brief numaGetNonzeroRange() + * + * \param[in] na source numa + * \param[in] eps largest value considered to be zero + * \param[out] pfirst, plast interval of array indices + * where values are nonzero + * \return 0 if OK, 1 on error or if no nonzero range is found. + */ +l_ok +numaGetNonzeroRange(NUMA *na, + l_float32 eps, + l_int32 *pfirst, + l_int32 *plast) +{ +l_int32 n, i, found; +l_float32 val; + + PROCNAME("numaGetNonzeroRange"); + + if (pfirst) *pfirst = 0; + if (plast) *plast = 0; + if (!pfirst || !plast) + return ERROR_INT("pfirst and plast not both defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + found = FALSE; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (val > eps) { + found = TRUE; + break; + } + } + if (!found) { + *pfirst = n - 1; + *plast = 0; + return 1; + } + + *pfirst = i; + for (i = n - 1; i >= 0; i--) { + numaGetFValue(na, i, &val); + if (val > eps) + break; + } + *plast = i; + return 0; +} + + +/*! + * \brief numaGetCountRelativeToZero() + * + * \param[in] na source numa + * \param[in] type L_LESS_THAN_ZERO, L_EQUAL_TO_ZERO, L_GREATER_THAN_ZERO + * \param[out] pcount count of values of given type + * \return 0 if OK, 1 on error + */ +l_ok +numaGetCountRelativeToZero(NUMA *na, + l_int32 type, + l_int32 *pcount) +{ +l_int32 n, i, count; +l_float32 val; + + PROCNAME("numaGetCountRelativeToZero"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + for (i = 0, count = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (type == L_LESS_THAN_ZERO && val < 0.0) + count++; + else if (type == L_EQUAL_TO_ZERO && val == 0.0) + count++; + else if (type == L_GREATER_THAN_ZERO && val > 0.0) + count++; + } + + *pcount = count; + return 0; +} + + +/*! + * \brief numaClipToInterval() + * + * \param[in] nas + * \param[in] first, last clipping interval + * \return numa with the same values as the input, but clipped + * to the specified interval + * + *
+ * Notes:
+ *        If you want the indices of the array values to be unchanged,
+ *        use first = 0.
+ *  Usage:
+ *        This is useful to clip a histogram that has a few nonzero
+ *        values to its nonzero range.
+ * 
+ */ +NUMA * +numaClipToInterval(NUMA *nas, + l_int32 first, + l_int32 last) +{ +l_int32 n, i, truelast; +l_float32 val, startx, delx; +NUMA *nad; + + PROCNAME("numaClipToInterval"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (first > last) + return (NUMA *)ERROR_PTR("range not valid", procName, NULL); + + n = numaGetCount(nas); + if (first >= n) + return (NUMA *)ERROR_PTR("no elements in range", procName, NULL); + truelast = L_MIN(last, n - 1); + if ((nad = numaCreate(truelast - first + 1)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + for (i = first; i <= truelast; i++) { + numaGetFValue(nas, i, &val); + numaAddNumber(nad, val); + } + numaGetParameters(nas, &startx, &delx); + numaSetParameters(nad, startx + first * delx, delx); + return nad; +} + + +/*! + * \brief numaMakeThresholdIndicator() + * + * \param[in] nas input numa + * \param[in] thresh threshold value + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return nad : indicator array: values are 0 and 1 + * + *
+ * Notes:
+ *      (1) For each element in nas, if the constraint given by 'type'
+ *          correctly specifies its relation to thresh, a value of 1
+ *          is recorded in nad.
+ * 
+ */ +NUMA * +numaMakeThresholdIndicator(NUMA *nas, + l_float32 thresh, + l_int32 type) +{ +l_int32 n, i, ival; +l_float32 fval; +NUMA *nai; + + PROCNAME("numaMakeThresholdIndicator"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + nai = numaCreate(n); + for (i = 0; i < n; i++) { + numaGetFValue(nas, i, &fval); + ival = 0; + switch (type) + { + case L_SELECT_IF_LT: + if (fval < thresh) ival = 1; + break; + case L_SELECT_IF_GT: + if (fval > thresh) ival = 1; + break; + case L_SELECT_IF_LTE: + if (fval <= thresh) ival = 1; + break; + case L_SELECT_IF_GTE: + if (fval >= thresh) ival = 1; + break; + default: + numaDestroy(&nai); + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + } + numaAddNumber(nai, ival); + } + + return nai; +} + + +/*! + * \brief numaUniformSampling() + * + * \param[in] nas input numa + * \param[in] nsamp number of samples + * \return nad : resampled array, or NULL on error + * + *
+ * Notes:
+ *      (1) This resamples the values in the array, using %nsamp
+ *          equal divisions.
+ * 
+ */ +NUMA * +numaUniformSampling(NUMA *nas, + l_int32 nsamp) +{ +l_int32 n, i, j, ileft, iright; +l_float32 left, right, binsize, lfract, rfract, sum, startx, delx; +l_float32 *array; +NUMA *nad; + + PROCNAME("numaUniformSampling"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (nsamp <= 0) + return (NUMA *)ERROR_PTR("nsamp must be > 0", procName, NULL); + + n = numaGetCount(nas); + nad = numaCreate(nsamp); + array = numaGetFArray(nas, L_NOCOPY); + binsize = (l_float32)n / (l_float32)nsamp; + numaGetParameters(nas, &startx, &delx); + numaSetParameters(nad, startx, binsize * delx); + left = 0.0; + for (i = 0; i < nsamp; i++) { + sum = 0.0; + right = left + binsize; + ileft = (l_int32)left; + lfract = 1.0 - left + ileft; + if (lfract >= 1.0) /* on left bin boundary */ + lfract = 0.0; + iright = (l_int32)right; + rfract = right - iright; + iright = L_MIN(iright, n - 1); + if (ileft == iright) { /* both are within the same original sample */ + sum += (lfract + rfract - 1.0) * array[ileft]; + } else { + if (lfract > 0.0001) /* left fraction */ + sum += lfract * array[ileft]; + if (rfract > 0.0001) /* right fraction */ + sum += rfract * array[iright]; + for (j = ileft + 1; j < iright; j++) /* entire pixels */ + sum += array[j]; + } + + numaAddNumber(nad, sum); + left = right; + } + return nad; +} + + +/*! + * \brief numaReverse() + * + * \param[in] nad [optional] can be null or equal to nas + * \param[in] nas input numa + * \return nad : reversed, or NULL on error + * + *
+ * Notes:
+ *      (1) Usage:
+ *            numaReverse(nas, nas);   // in-place
+ *            nad = numaReverse(NULL, nas);  // makes a new one
+ * 
+ */ +NUMA * +numaReverse(NUMA *nad, + NUMA *nas) +{ +l_int32 n, i; +l_float32 val1, val2; + + PROCNAME("numaReverse"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (nad && nas != nad) + return (NUMA *)ERROR_PTR("nad defined but != nas", procName, NULL); + + n = numaGetCount(nas); + if (nad) { /* in-place */ + for (i = 0; i < n / 2; i++) { + numaGetFValue(nad, i, &val1); + numaGetFValue(nad, n - i - 1, &val2); + numaSetValue(nad, i, val2); + numaSetValue(nad, n - i - 1, val1); + } + } else { + nad = numaCreate(n); + for (i = n - 1; i >= 0; i--) { + numaGetFValue(nas, i, &val1); + numaAddNumber(nad, val1); + } + } + + /* Reverse the startx and delx fields */ + nad->startx = nas->startx + (n - 1) * nas->delx; + nad->delx = -nas->delx; + return nad; +} + + +/*----------------------------------------------------------------------* + * Signal feature extraction * + *----------------------------------------------------------------------*/ +/*! + * \brief numaLowPassIntervals() + * + * \param[in] nas input numa + * \param[in] thresh threshold fraction of max; in [0.0 ... 1.0] + * \param[in] maxn for normalizing; set maxn = 0.0 to use the max in nas + * \return nad : interval abscissa pairs, or NULL on error + * + *
+ * Notes:
+ *      (1) For each interval where the value is less than a specified
+ *          fraction of the maximum, this records the left and right "x"
+ *          value.
+ * 
+ */ +NUMA * +numaLowPassIntervals(NUMA *nas, + l_float32 thresh, + l_float32 maxn) +{ +l_int32 n, i, inrun; +l_float32 maxval, threshval, fval, startx, delx, x0, x1; +NUMA *nad; + + PROCNAME("numaLowPassIntervals"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (thresh < 0.0 || thresh > 1.0) + return (NUMA *)ERROR_PTR("invalid thresh", procName, NULL); + + /* The input threshold is a fraction of the max. + * The first entry in nad is the value of the max. */ + n = numaGetCount(nas); + if (maxn == 0.0) + numaGetMax(nas, &maxval, NULL); + else + maxval = maxn; + numaGetParameters(nas, &startx, &delx); + threshval = thresh * maxval; + nad = numaCreate(0); + numaAddNumber(nad, maxval); + + /* Write pairs of pts (x0, x1) for the intervals */ + inrun = FALSE; + for (i = 0; i < n; i++) { + numaGetFValue(nas, i, &fval); + if (fval < threshval && inrun == FALSE) { /* start a new run */ + inrun = TRUE; + x0 = startx + i * delx; + } else if (fval > threshval && inrun == TRUE) { /* end the run */ + inrun = FALSE; + x1 = startx + i * delx; + numaAddNumber(nad, x0); + numaAddNumber(nad, x1); + } + } + if (inrun == TRUE) { /* must end the last run */ + x1 = startx + (n - 1) * delx; + numaAddNumber(nad, x0); + numaAddNumber(nad, x1); + } + + return nad; +} + + +/*! + * \brief numaThresholdEdges() + * + * \param[in] nas input numa + * \param[in] thresh1 low threshold as fraction of max; in [0.0 ... 1.0] + * \param[in] thresh2 high threshold as fraction of max; in [0.0 ... 1.0] + * \param[in] maxn for normalizing; set maxn = 0.0 to use the max in nas + * \return nad edge interval triplets, or NULL on error + * + *
+ * Notes:
+ *      (1) For each edge interval, where where the value is less
+ *          than %thresh1 on one side, greater than %thresh2 on
+ *          the other, and between these thresholds throughout the
+ *          interval, this records a triplet of values: the
+ *          'left' and 'right' edges, and either +1 or -1, depending
+ *          on whether the edge is rising or falling.
+ *      (2) No assumption is made about the value outside the array,
+ *          so if the value at the array edge is between the threshold
+ *          values, it is not considered part of an edge.  We start
+ *          looking for edge intervals only after leaving the thresholded
+ *          band.
+ * 
+ */ +NUMA * +numaThresholdEdges(NUMA *nas, + l_float32 thresh1, + l_float32 thresh2, + l_float32 maxn) +{ +l_int32 n, i, istart, inband, output, sign; +l_int32 startbelow, below, above, belowlast, abovelast; +l_float32 maxval, threshval1, threshval2, fval, startx, delx, x0, x1; +NUMA *nad; + + PROCNAME("numaThresholdEdges"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (thresh1 < 0.0 || thresh1 > 1.0 || thresh2 < 0.0 || thresh2 > 1.0) + return (NUMA *)ERROR_PTR("invalid thresholds", procName, NULL); + if (thresh2 < thresh1) + return (NUMA *)ERROR_PTR("thresh2 < thresh1", procName, NULL); + + /* The input thresholds are fractions of the max. + * The first entry in nad is the value of the max used + * here for normalization. */ + n = numaGetCount(nas); + if (maxn == 0.0) + numaGetMax(nas, &maxval, NULL); + else + maxval = maxn; + numaGetMax(nas, &maxval, NULL); + numaGetParameters(nas, &startx, &delx); + threshval1 = thresh1 * maxval; + threshval2 = thresh2 * maxval; + nad = numaCreate(0); + numaAddNumber(nad, maxval); + + /* Write triplets of pts (x0, x1, sign) for the edges. + * First make sure we start search from outside the band. + * Only one of {belowlast, abovelast} is true. */ + for (i = 0; i < n; i++) { + istart = i; + numaGetFValue(nas, i, &fval); + belowlast = (fval < threshval1) ? TRUE : FALSE; + abovelast = (fval > threshval2) ? TRUE : FALSE; + if (belowlast == TRUE || abovelast == TRUE) + break; + } + if (istart == n) /* no intervals found */ + return nad; + + /* x0 and x1 can only be set from outside the edge. + * They are the values just before entering the band, + * and just after entering the band. We can jump through + * the band, in which case they differ by one index in nas. */ + inband = FALSE; + startbelow = belowlast; /* one of these is true */ + output = FALSE; + x0 = startx + istart * delx; + for (i = istart + 1; i < n; i++) { + numaGetFValue(nas, i, &fval); + below = (fval < threshval1) ? TRUE : FALSE; + above = (fval > threshval2) ? TRUE : FALSE; + if (!inband && belowlast && above) { /* full jump up */ + x1 = startx + i * delx; + sign = 1; + startbelow = FALSE; /* for the next transition */ + output = TRUE; + } else if (!inband && abovelast && below) { /* full jump down */ + x1 = startx + i * delx; + sign = -1; + startbelow = TRUE; /* for the next transition */ + output = TRUE; + } else if (inband && startbelow && above) { /* exit rising; success */ + x1 = startx + i * delx; + sign = 1; + inband = FALSE; + startbelow = FALSE; /* for the next transition */ + output = TRUE; + } else if (inband && !startbelow && below) { + /* exit falling; success */ + x1 = startx + i * delx; + sign = -1; + inband = FALSE; + startbelow = TRUE; /* for the next transition */ + output = TRUE; + } else if (inband && !startbelow && above) { /* exit rising; failure */ + x0 = startx + i * delx; + inband = FALSE; + } else if (inband && startbelow && below) { /* exit falling; failure */ + x0 = startx + i * delx; + inband = FALSE; + } else if (!inband && !above && !below) { /* enter */ + inband = TRUE; + startbelow = belowlast; + } else if (!inband && (above || below)) { /* outside and remaining */ + x0 = startx + i * delx; /* update position */ + } + belowlast = below; + abovelast = above; + if (output) { /* we have exited; save new x0 */ + numaAddNumber(nad, x0); + numaAddNumber(nad, x1); + numaAddNumber(nad, sign); + output = FALSE; + x0 = startx + i * delx; + } + } + + return nad; +} + + +/*! + * \brief numaGetSpanValues() + * + * \param[in] na numa that is output of numaLowPassIntervals() + * \param[in] span span number, zero-based + * \param[out] pstart [optional] location of start of transition + * \param[out] pend [optional] location of end of transition + * \return 0 if OK, 1 on error + */ +l_int32 +numaGetSpanValues(NUMA *na, + l_int32 span, + l_int32 *pstart, + l_int32 *pend) +{ +l_int32 n, nspans; + + PROCNAME("numaGetSpanValues"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (n % 2 != 1) + return ERROR_INT("n is not odd", procName, 1); + nspans = n / 2; + if (nspans < 0 || span >= nspans) + return ERROR_INT("invalid span", procName, 1); + + if (pstart) numaGetIValue(na, 2 * span + 1, pstart); + if (pend) numaGetIValue(na, 2 * span + 2, pend); + return 0; +} + + +/*! + * \brief numaGetEdgeValues() + * + * \param[in] na numa that is output of numaThresholdEdges() + * \param[in] edge edge number, zero-based + * \param[out] pstart [optional] location of start of transition + * \param[out] pend [optional] location of end of transition + * \param[out] psign [optional] transition sign: +1 is rising, + * -1 is falling + * \return 0 if OK, 1 on error + */ +l_int32 +numaGetEdgeValues(NUMA *na, + l_int32 edge, + l_int32 *pstart, + l_int32 *pend, + l_int32 *psign) +{ +l_int32 n, nedges; + + PROCNAME("numaGetEdgeValues"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = numaGetCount(na); + if (n % 3 != 1) + return ERROR_INT("n % 3 is not 1", procName, 1); + nedges = (n - 1) / 3; + if (edge < 0 || edge >= nedges) + return ERROR_INT("invalid edge", procName, 1); + + if (pstart) numaGetIValue(na, 3 * edge + 1, pstart); + if (pend) numaGetIValue(na, 3 * edge + 2, pend); + if (psign) numaGetIValue(na, 3 * edge + 3, psign); + return 0; +} + + +/*----------------------------------------------------------------------* + * Interpolation * + *----------------------------------------------------------------------*/ +/*! + * \brief numaInterpolateEqxVal() + * + * \param[in] startx xval corresponding to first element in array + * \param[in] deltax x increment between array elements + * \param[in] nay numa of ordinate values, assumed equally spaced + * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP + * \param[in] xval + * \param[out] pyval interpolated value + * \return 0 if OK, 1 on error e.g., if xval is outside range + * + *
+ * Notes:
+ *      (1) Considering nay as a function of x, the x values
+ *          are equally spaced
+ *      (2) Caller should check for valid return.
+ *
+ *  For linear Lagrangian interpolation (through 2 data pts):
+ *         y(x) = y1(x-x2)/(x1-x2) + y2(x-x1)/(x2-x1)
+ *
+ *  For quadratic Lagrangian interpolation (through 3 data pts):
+ *         y(x) = y1(x-x2)(x-x3)/((x1-x2)(x1-x3)) +
+ *                y2(x-x1)(x-x3)/((x2-x1)(x2-x3)) +
+ *                y3(x-x1)(x-x2)/((x3-x1)(x3-x2))
+ *
+ * 
+ */ +l_ok +numaInterpolateEqxVal(l_float32 startx, + l_float32 deltax, + NUMA *nay, + l_int32 type, + l_float32 xval, + l_float32 *pyval) +{ +l_int32 i, n, i1, i2, i3; +l_float32 x1, x2, x3, fy1, fy2, fy3, d1, d2, d3, del, fi, maxx; +l_float32 *fa; + + PROCNAME("numaInterpolateEqxVal"); + + if (!pyval) + return ERROR_INT("&yval not defined", procName, 1); + *pyval = 0.0; + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (deltax <= 0.0) + return ERROR_INT("deltax not > 0", procName, 1); + if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) + return ERROR_INT("invalid interp type", procName, 1); + n = numaGetCount(nay); + if (n < 2) + return ERROR_INT("not enough points", procName, 1); + if (type == L_QUADRATIC_INTERP && n == 2) { + type = L_LINEAR_INTERP; + L_WARNING("only 2 points; using linear interp\n", procName); + } + maxx = startx + deltax * (n - 1); + if (xval < startx || xval > maxx) + return ERROR_INT("xval is out of bounds", procName, 1); + + fa = numaGetFArray(nay, L_NOCOPY); + fi = (xval - startx) / deltax; + i = (l_int32)fi; + del = fi - i; + if (del == 0.0) { /* no interpolation required */ + *pyval = fa[i]; + return 0; + } + + if (type == L_LINEAR_INTERP) { + *pyval = fa[i] + del * (fa[i + 1] - fa[i]); + return 0; + } + + /* Quadratic interpolation */ + d1 = d3 = 0.5 / (deltax * deltax); + d2 = -2. * d1; + if (i == 0) { + i1 = i; + i2 = i + 1; + i3 = i + 2; + } else { + i1 = i - 1; + i2 = i; + i3 = i + 1; + } + x1 = startx + i1 * deltax; + x2 = startx + i2 * deltax; + x3 = startx + i3 * deltax; + fy1 = d1 * fa[i1]; + fy2 = d2 * fa[i2]; + fy3 = d3 * fa[i3]; + *pyval = fy1 * (xval - x2) * (xval - x3) + + fy2 * (xval - x1) * (xval - x3) + + fy3 * (xval - x1) * (xval - x2); + return 0; +} + + +/*! + * \brief numaInterpolateArbxVal() + * + * \param[in] nax numa of abscissa values + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP + * \param[in] xval + * \param[out] pyval interpolated value + * \return 0 if OK, 1 on error e.g., if xval is outside range + * + *
+ * Notes:
+ *      (1) The values in nax must be sorted in increasing order.
+ *          If, additionally, they are equally spaced, you can use
+ *          numaInterpolateEqxVal().
+ *      (2) Caller should check for valid return.
+ *      (3) Uses lagrangian interpolation.  See numaInterpolateEqxVal()
+ *          for formulas.
+ * 
+ */ +l_ok +numaInterpolateArbxVal(NUMA *nax, + NUMA *nay, + l_int32 type, + l_float32 xval, + l_float32 *pyval) +{ +l_int32 i, im, nx, ny, i1, i2, i3; +l_float32 delu, dell, fract, d1, d2, d3; +l_float32 minx, maxx; +l_float32 *fax, *fay; + + PROCNAME("numaInterpolateArbxVal"); + + if (!pyval) + return ERROR_INT("&yval not defined", procName, 1); + *pyval = 0.0; + if (!nax) + return ERROR_INT("nax not defined", procName, 1); + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) + return ERROR_INT("invalid interp type", procName, 1); + ny = numaGetCount(nay); + nx = numaGetCount(nax); + if (nx != ny) + return ERROR_INT("nax and nay not same size arrays", procName, 1); + if (ny < 2) + return ERROR_INT("not enough points", procName, 1); + if (type == L_QUADRATIC_INTERP && ny == 2) { + type = L_LINEAR_INTERP; + L_WARNING("only 2 points; using linear interp\n", procName); + } + numaGetFValue(nax, 0, &minx); + numaGetFValue(nax, nx - 1, &maxx); + if (xval < minx || xval > maxx) + return ERROR_INT("xval is out of bounds", procName, 1); + + fax = numaGetFArray(nax, L_NOCOPY); + fay = numaGetFArray(nay, L_NOCOPY); + + /* Linear search for interval. We are guaranteed + * to either return or break out of the loop. + * In addition, we are assured that fax[i] - fax[im] > 0.0 */ + if (xval == fax[0]) { + *pyval = fay[0]; + return 0; + } + im = 0; + dell = 0.0; + for (i = 1; i < nx; i++) { + delu = fax[i] - xval; + if (delu >= 0.0) { /* we've passed it */ + if (delu == 0.0) { + *pyval = fay[i]; + return 0; + } + im = i - 1; + dell = xval - fax[im]; /* >= 0 */ + break; + } + } + fract = dell / (fax[i] - fax[im]); + + if (type == L_LINEAR_INTERP) { + *pyval = fay[i] + fract * (fay[i + 1] - fay[i]); + return 0; + } + + /* Quadratic interpolation */ + if (im == 0) { + i1 = im; + i2 = im + 1; + i3 = im + 2; + } else { + i1 = im - 1; + i2 = im; + i3 = im + 1; + } + d1 = (fax[i1] - fax[i2]) * (fax[i1] - fax[i3]); + d2 = (fax[i2] - fax[i1]) * (fax[i2] - fax[i3]); + d3 = (fax[i3] - fax[i1]) * (fax[i3] - fax[i2]); + *pyval = fay[i1] * (xval - fax[i2]) * (xval - fax[i3]) / d1 + + fay[i2] * (xval - fax[i1]) * (xval - fax[i3]) / d2 + + fay[i3] * (xval - fax[i1]) * (xval - fax[i2]) / d3; + return 0; +} + + +/*! + * \brief numaInterpolateEqxInterval() + * + * \param[in] startx xval corresponding to first element in nas + * \param[in] deltax x increment between array elements in nas + * \param[in] nasy numa of ordinate values, assumed equally spaced + * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP + * \param[in] x0 start value of interval + * \param[in] x1 end value of interval + * \param[in] npts number of points to evaluate function in interval + * \param[out] pnax [optional] array of x values in interval + * \param[out] pnay array of y values in interval + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Considering nasy as a function of x, the x values
+ *          are equally spaced.
+ *      (2) This creates nay (and optionally nax) of interpolated
+ *          values over the specified interval (x0, x1).
+ *      (3) If the interval (x0, x1) lies partially outside the array
+ *          nasy (as interpreted by startx and deltax), it is an
+ *          error and returns 1.
+ *      (4) Note that deltax is the intrinsic x-increment for the input
+ *          array nasy, whereas delx is the intrinsic x-increment for the
+ *          output interpolated array nay.
+ * 
+ */ +l_ok +numaInterpolateEqxInterval(l_float32 startx, + l_float32 deltax, + NUMA *nasy, + l_int32 type, + l_float32 x0, + l_float32 x1, + l_int32 npts, + NUMA **pnax, + NUMA **pnay) +{ +l_int32 i, n; +l_float32 x, yval, maxx, delx; +NUMA *nax, *nay; + + PROCNAME("numaInterpolateEqxInterval"); + + if (pnax) *pnax = NULL; + if (!pnay) + return ERROR_INT("&nay not defined", procName, 1); + *pnay = NULL; + if (!nasy) + return ERROR_INT("nasy not defined", procName, 1); + if (deltax <= 0.0) + return ERROR_INT("deltax not > 0", procName, 1); + if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) + return ERROR_INT("invalid interp type", procName, 1); + n = numaGetCount(nasy); + if (type == L_QUADRATIC_INTERP && n == 2) { + type = L_LINEAR_INTERP; + L_WARNING("only 2 points; using linear interp\n", procName); + } + maxx = startx + deltax * (n - 1); + if (x0 < startx || x1 > maxx || x1 <= x0) + return ERROR_INT("[x0 ... x1] is not valid", procName, 1); + if (npts < 3) + return ERROR_INT("npts < 3", procName, 1); + delx = (x1 - x0) / (l_float32)(npts - 1); /* delx is for output nay */ + + if ((nay = numaCreate(npts)) == NULL) + return ERROR_INT("nay not made", procName, 1); + numaSetParameters(nay, x0, delx); + *pnay = nay; + if (pnax) { + nax = numaCreate(npts); + *pnax = nax; + } + + for (i = 0; i < npts; i++) { + x = x0 + i * delx; + if (pnax) + numaAddNumber(nax, x); + numaInterpolateEqxVal(startx, deltax, nasy, type, x, &yval); + numaAddNumber(nay, yval); + } + + return 0; +} + + +/*! + * \brief numaInterpolateArbxInterval() + * + * \param[in] nax numa of abscissa values + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP + * \param[in] x0 start value of interval + * \param[in] x1 end value of interval + * \param[in] npts number of points to evaluate function in interval + * \param[out] pnadx [optional] array of x values in interval + * \param[out] pnady array of y values in interval + * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range + * + *
+ * Notes:
+ *      (1) The values in nax must be sorted in increasing order.
+ *          If they are not sorted, we do it here, and complain.
+ *      (2) If the values in nax are equally spaced, you can use
+ *          numaInterpolateEqxInterval().
+ *      (3) Caller should check for valid return.
+ *      (4) We don't call numaInterpolateArbxVal() for each output
+ *          point, because that requires an O(n) search for
+ *          each point.  Instead, we do a single O(n) pass through
+ *          nax, saving the indices to be used for each output yval.
+ *      (5) Uses lagrangian interpolation.  See numaInterpolateEqxVal()
+ *          for formulas.
+ * 
+ */ +l_ok +numaInterpolateArbxInterval(NUMA *nax, + NUMA *nay, + l_int32 type, + l_float32 x0, + l_float32 x1, + l_int32 npts, + NUMA **pnadx, + NUMA **pnady) +{ +l_int32 i, im, j, nx, ny, i1, i2, i3, sorted; +l_int32 *index; +l_float32 del, xval, yval, excess, fract, minx, maxx, d1, d2, d3; +l_float32 *fax, *fay; +NUMA *nasx, *nasy, *nadx, *nady; + + PROCNAME("numaInterpolateArbxInterval"); + + if (pnadx) *pnadx = NULL; + if (!pnady) + return ERROR_INT("&nady not defined", procName, 1); + *pnady = NULL; + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (!nax) + return ERROR_INT("nax not defined", procName, 1); + if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) + return ERROR_INT("invalid interp type", procName, 1); + if (x0 > x1) + return ERROR_INT("x0 > x1", procName, 1); + ny = numaGetCount(nay); + nx = numaGetCount(nax); + if (nx != ny) + return ERROR_INT("nax and nay not same size arrays", procName, 1); + if (ny < 2) + return ERROR_INT("not enough points", procName, 1); + if (type == L_QUADRATIC_INTERP && ny == 2) { + type = L_LINEAR_INTERP; + L_WARNING("only 2 points; using linear interp\n", procName); + } + numaGetMin(nax, &minx, NULL); + numaGetMax(nax, &maxx, NULL); + if (x0 < minx || x1 > maxx) + return ERROR_INT("xval is out of bounds", procName, 1); + + /* Make sure that nax is sorted in increasing order */ + numaIsSorted(nax, L_SORT_INCREASING, &sorted); + if (!sorted) { + L_WARNING("we are sorting nax in increasing order\n", procName); + numaSortPair(nax, nay, L_SORT_INCREASING, &nasx, &nasy); + } else { + nasx = numaClone(nax); + nasy = numaClone(nay); + } + + fax = numaGetFArray(nasx, L_NOCOPY); + fay = numaGetFArray(nasy, L_NOCOPY); + + /* Get array of indices into fax for interpolated locations */ + if ((index = (l_int32 *)LEPT_CALLOC(npts, sizeof(l_int32))) == NULL) { + numaDestroy(&nasx); + numaDestroy(&nasy); + return ERROR_INT("ind not made", procName, 1); + } + del = (x1 - x0) / (npts - 1.0); + for (i = 0, j = 0; j < nx && i < npts; i++) { + xval = x0 + i * del; + while (j < nx - 1 && xval > fax[j]) + j++; + if (xval == fax[j]) + index[i] = L_MIN(j, nx - 1); + else /* the index of fax[] is just below xval */ + index[i] = L_MAX(j - 1, 0); + } + + /* For each point to be interpolated, get the y value */ + nady = numaCreate(npts); + *pnady = nady; + if (pnadx) { + nadx = numaCreate(npts); + *pnadx = nadx; + } + for (i = 0; i < npts; i++) { + xval = x0 + i * del; + if (pnadx) + numaAddNumber(nadx, xval); + im = index[i]; + excess = xval - fax[im]; + if (excess == 0.0) { + numaAddNumber(nady, fay[im]); + continue; + } + fract = excess / (fax[im + 1] - fax[im]); + + if (type == L_LINEAR_INTERP) { + yval = fay[im] + fract * (fay[im + 1] - fay[im]); + numaAddNumber(nady, yval); + continue; + } + + /* Quadratic interpolation */ + if (im == 0) { + i1 = im; + i2 = im + 1; + i3 = im + 2; + } else { + i1 = im - 1; + i2 = im; + i3 = im + 1; + } + d1 = (fax[i1] - fax[i2]) * (fax[i1] - fax[i3]); + d2 = (fax[i2] - fax[i1]) * (fax[i2] - fax[i3]); + d3 = (fax[i3] - fax[i1]) * (fax[i3] - fax[i2]); + yval = fay[i1] * (xval - fax[i2]) * (xval - fax[i3]) / d1 + + fay[i2] * (xval - fax[i1]) * (xval - fax[i3]) / d2 + + fay[i3] * (xval - fax[i1]) * (xval - fax[i2]) / d3; + numaAddNumber(nady, yval); + } + + LEPT_FREE(index); + numaDestroy(&nasx); + numaDestroy(&nasy); + return 0; +} + + +/*----------------------------------------------------------------------* + * Functions requiring interpolation * + *----------------------------------------------------------------------*/ +/*! + * \brief numaFitMax() + * + * \param[in] na numa of ordinate values, to fit a max to + * \param[out] pmaxval max value + * \param[in] naloc [optional] associated numa of abscissa values + * \param[out] pmaxloc abscissa value that gives max value in na; + * if naloc == null, this is given as an interpolated + * index value + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *        If %naloc is given, there is no requirement that the
+ *        data points are evenly spaced.  Lagrangian interpolation
+ *        handles that.  The only requirement is that the
+ *        data points are ordered so that the values in naloc
+ *        are either increasing or decreasing.  We test to make
+ *        sure that the sizes of na and naloc are equal, and it
+ *        is assumed that the correspondences %na[i] as a function
+ *        of %naloc[i] are properly arranged for all i.
+ *
+ *  The formula for Lagrangian interpolation through 3 data pts is:
+ *       y(x) = y1(x-x2)(x-x3)/((x1-x2)(x1-x3)) +
+ *              y2(x-x1)(x-x3)/((x2-x1)(x2-x3)) +
+ *              y3(x-x1)(x-x2)/((x3-x1)(x3-x2))
+ *
+ *  Then the derivative, using the constants (c1,c2,c3) defined below,
+ *  is set to 0:
+ *       y'(x) = 2x(c1+c2+c3) - c1(x2+x3) - c2(x1+x3) - c3(x1+x2) = 0
+ * 
+ */ +l_ok +numaFitMax(NUMA *na, + l_float32 *pmaxval, + NUMA *naloc, + l_float32 *pmaxloc) +{ +l_float32 val; +l_float32 smaxval; /* start value of maximum sample, before interpolating */ +l_int32 n, imaxloc; +l_float32 x1, x2, x3, y1, y2, y3, c1, c2, c3, a, b, xmax, ymax; + + PROCNAME("numaFitMax"); + + if (pmaxval) *pmaxval = 0.0; + if (pmaxloc) *pmaxloc = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (!pmaxval) + return ERROR_INT("&maxval not defined", procName, 1); + if (!pmaxloc) + return ERROR_INT("&maxloc not defined", procName, 1); + + n = numaGetCount(na); + if (naloc) { + if (n != numaGetCount(naloc)) + return ERROR_INT("na and naloc of unequal size", procName, 1); + } + numaGetMax(na, &smaxval, &imaxloc); + + /* Simple case: max is at end point */ + if (imaxloc == 0 || imaxloc == n - 1) { + *pmaxval = smaxval; + if (naloc) { + numaGetFValue(naloc, imaxloc, &val); + *pmaxloc = val; + } else { + *pmaxloc = imaxloc; + } + return 0; + } + + /* Interior point; use quadratic interpolation */ + y2 = smaxval; + numaGetFValue(na, imaxloc - 1, &val); + y1 = val; + numaGetFValue(na, imaxloc + 1, &val); + y3 = val; + if (naloc) { + numaGetFValue(naloc, imaxloc - 1, &val); + x1 = val; + numaGetFValue(naloc, imaxloc, &val); + x2 = val; + numaGetFValue(naloc, imaxloc + 1, &val); + x3 = val; + } else { + x1 = imaxloc - 1; + x2 = imaxloc; + x3 = imaxloc + 1; + } + + /* Can't interpolate; just use the max val in na + * and the corresponding one in naloc */ + if (x1 == x2 || x1 == x3 || x2 == x3) { + *pmaxval = y2; + *pmaxloc = x2; + return 0; + } + + /* Use lagrangian interpolation; set dy/dx = 0 */ + c1 = y1 / ((x1 - x2) * (x1 - x3)); + c2 = y2 / ((x2 - x1) * (x2 - x3)); + c3 = y3 / ((x3 - x1) * (x3 - x2)); + a = c1 + c2 + c3; + b = c1 * (x2 + x3) + c2 * (x1 + x3) + c3 * (x1 + x2); + xmax = b / (2 * a); + ymax = c1 * (xmax - x2) * (xmax - x3) + + c2 * (xmax - x1) * (xmax - x3) + + c3 * (xmax - x1) * (xmax - x2); + *pmaxval = ymax; + *pmaxloc = xmax; + + return 0; +} + + +/*! + * \brief numaDifferentiateInterval() + * + * \param[in] nax numa of abscissa values + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] x0 start value of interval + * \param[in] x1 end value of interval + * \param[in] npts number of points to evaluate function in interval + * \param[out] pnadx [optional] array of x values in interval + * \param[out] pnady array of derivatives in interval + * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range + * + *
+ * Notes:
+ *      (1) The values in nax must be sorted in increasing order.
+ *          If they are not sorted, it is done in the interpolation
+ *          step, and a warning is issued.
+ *      (2) Caller should check for valid return.
+ * 
+ */ +l_ok +numaDifferentiateInterval(NUMA *nax, + NUMA *nay, + l_float32 x0, + l_float32 x1, + l_int32 npts, + NUMA **pnadx, + NUMA **pnady) +{ +l_int32 i, nx, ny; +l_float32 minx, maxx, der, invdel; +l_float32 *fay; +NUMA *nady, *naiy; + + PROCNAME("numaDifferentiateInterval"); + + if (pnadx) *pnadx = NULL; + if (!pnady) + return ERROR_INT("&nady not defined", procName, 1); + *pnady = NULL; + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (!nax) + return ERROR_INT("nax not defined", procName, 1); + if (x0 > x1) + return ERROR_INT("x0 > x1", procName, 1); + ny = numaGetCount(nay); + nx = numaGetCount(nax); + if (nx != ny) + return ERROR_INT("nax and nay not same size arrays", procName, 1); + if (ny < 2) + return ERROR_INT("not enough points", procName, 1); + numaGetMin(nax, &minx, NULL); + numaGetMax(nax, &maxx, NULL); + if (x0 < minx || x1 > maxx) + return ERROR_INT("xval is out of bounds", procName, 1); + if (npts < 2) + return ERROR_INT("npts < 2", procName, 1); + + /* Generate interpolated array over specified interval */ + if (numaInterpolateArbxInterval(nax, nay, L_LINEAR_INTERP, x0, x1, + npts, pnadx, &naiy)) + return ERROR_INT("interpolation failed", procName, 1); + + nady = numaCreate(npts); + *pnady = nady; + invdel = 0.5 * ((l_float32)npts - 1.0) / (x1 - x0); + fay = numaGetFArray(naiy, L_NOCOPY); + + /* Compute and save derivatives */ + der = 0.5 * invdel * (fay[1] - fay[0]); + numaAddNumber(nady, der); + for (i = 1; i < npts - 1; i++) { + der = invdel * (fay[i + 1] - fay[i - 1]); + numaAddNumber(nady, der); + } + der = 0.5 * invdel * (fay[npts - 1] - fay[npts - 2]); + numaAddNumber(nady, der); + + numaDestroy(&naiy); + return 0; +} + + +/*! + * \brief numaIntegrateInterval() + * + * \param[in] nax numa of abscissa values + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] x0 start value of interval + * \param[in] x1 end value of interval + * \param[in] npts number of points to evaluate function in interval + * \param[out] psum integral of function over interval + * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range + * + *
+ * Notes:
+ *      (1) The values in nax must be sorted in increasing order.
+ *          If they are not sorted, it is done in the interpolation
+ *          step, and a warning is issued.
+ *      (2) Caller should check for valid return.
+ * 
+ */ +l_ok +numaIntegrateInterval(NUMA *nax, + NUMA *nay, + l_float32 x0, + l_float32 x1, + l_int32 npts, + l_float32 *psum) +{ +l_int32 i, nx, ny; +l_float32 minx, maxx, sum, del; +l_float32 *fay; +NUMA *naiy; + + PROCNAME("numaIntegrateInterval"); + + if (!psum) + return ERROR_INT("&sum not defined", procName, 1); + *psum = 0.0; + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (!nax) + return ERROR_INT("nax not defined", procName, 1); + if (x0 > x1) + return ERROR_INT("x0 > x1", procName, 1); + if (npts < 2) + return ERROR_INT("npts < 2", procName, 1); + ny = numaGetCount(nay); + nx = numaGetCount(nax); + if (nx != ny) + return ERROR_INT("nax and nay not same size arrays", procName, 1); + if (ny < 2) + return ERROR_INT("not enough points", procName, 1); + numaGetMin(nax, &minx, NULL); + numaGetMax(nax, &maxx, NULL); + if (x0 < minx || x1 > maxx) + return ERROR_INT("xval is out of bounds", procName, 1); + + /* Generate interpolated array over specified interval */ + if (numaInterpolateArbxInterval(nax, nay, L_LINEAR_INTERP, x0, x1, + npts, NULL, &naiy)) + return ERROR_INT("interpolation failed", procName, 1); + + del = (x1 - x0) / ((l_float32)npts - 1.0); + fay = numaGetFArray(naiy, L_NOCOPY); + + /* Compute integral (simple trapezoid) */ + sum = 0.5 * (fay[0] + fay[npts - 1]); + for (i = 1; i < npts - 1; i++) + sum += fay[i]; + *psum = del * sum; + + numaDestroy(&naiy); + return 0; +} + + +/*----------------------------------------------------------------------* + * Sorting * + *----------------------------------------------------------------------*/ +/*! + * \brief numaSortGeneral() + * + * \param[in] na source numa + * \param[out] pnasort [optional] sorted numa + * \param[out] pnaindex [optional] index of elements in na associated + * with each element of nasort + * \param[out] pnainvert [optional] index of elements in nasort associated + * with each element of na + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \param[in] sorttype L_SHELL_SORT or L_BIN_SORT + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Sorting can be confusing.  Here's an array of five values with
+ *          the results shown for the 3 output arrays.
+ *
+ *          na      nasort   naindex   nainvert
+ *          -----------------------------------
+ *          3         9         2         3
+ *          4         6         3         2
+ *          9         4         1         0
+ *          6         3         0         1
+ *          1         1         4         4
+ *
+ *          Note that naindex is a LUT into na for the sorted array values,
+ *          and nainvert directly gives the sorted index values for the
+ *          input array.  It is useful to view naindex is as a map:
+ *                 0  -->  2
+ *                 1  -->  3
+ *                 2  -->  1
+ *                 3  -->  0
+ *                 4  -->  4
+ *          and nainvert, the inverse of this map:
+ *                 0  -->  3
+ *                 1  -->  2
+ *                 2  -->  0
+ *                 3  -->  1
+ *                 4  -->  4
+ *
+ *          We can write these relations symbolically as:
+ *              nasort[i] = na[naindex[i]]
+ *              na[i] = nasort[nainvert[i]]
+ * 
+ */ +l_ok +numaSortGeneral(NUMA *na, + NUMA **pnasort, + NUMA **pnaindex, + NUMA **pnainvert, + l_int32 sortorder, + l_int32 sorttype) +{ +NUMA *naindex; + + PROCNAME("numaSortGeneral"); + + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return ERROR_INT("invalid sort order", procName, 1); + if (sorttype != L_SHELL_SORT && sorttype != L_BIN_SORT) + return ERROR_INT("invalid sort type", procName, 1); + if (!pnasort && !pnaindex && !pnainvert) + return ERROR_INT("nothing to do", procName, 1); + if (pnasort) *pnasort = NULL; + if (pnaindex) *pnaindex = NULL; + if (pnainvert) *pnainvert = NULL; + + if (sorttype == L_SHELL_SORT) + naindex = numaGetSortIndex(na, sortorder); + else /* sorttype == L_BIN_SORT */ + naindex = numaGetBinSortIndex(na, sortorder); + + if (pnasort) + *pnasort = numaSortByIndex(na, naindex); + if (pnainvert) + *pnainvert = numaInvertMap(naindex); + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + return 0; +} + + +/*! + * \brief numaSortAutoSelect() + * + * \param[in] nas input numa + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return naout output sorted numa, or NULL on error + * + *
+ * Notes:
+ *      (1) This does either a shell sort or a bin sort, depending on
+ *          the number of elements in nas and the dynamic range.
+ * 
+ */ +NUMA * +numaSortAutoSelect(NUMA *nas, + l_int32 sortorder) +{ +l_int32 type; + + PROCNAME("numaSortAutoSelect"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); + + type = numaChooseSortType(nas); + if (type == L_SHELL_SORT) + return numaSort(NULL, nas, sortorder); + else if (type == L_BIN_SORT) + return numaBinSort(nas, sortorder); + else + return (NUMA *)ERROR_PTR("invalid sort type", procName, NULL); +} + + +/*! + * \brief numaSortIndexAutoSelect() + * + * \param[in] nas + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return nad indices of nas, sorted by value in nas, or NULL on error + * + *
+ * Notes:
+ *      (1) This does either a shell sort or a bin sort, depending on
+ *          the number of elements in nas and the dynamic range.
+ * 
+ */ +NUMA * +numaSortIndexAutoSelect(NUMA *nas, + l_int32 sortorder) +{ +l_int32 type; + + PROCNAME("numaSortIndexAutoSelect"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); + + type = numaChooseSortType(nas); + if (type == L_SHELL_SORT) + return numaGetSortIndex(nas, sortorder); + else if (type == L_BIN_SORT) + return numaGetBinSortIndex(nas, sortorder); + else + return (NUMA *)ERROR_PTR("invalid sort type", procName, NULL); +} + + +/*! + * \brief numaChooseSortType() + * + * \param[in] nas to be sorted + * \return sorttype L_SHELL_SORT or L_BIN_SORT, or UNDEF on error. + * + *
+ * Notes:
+ *      (1) This selects either a shell sort or a bin sort, depending on
+ *          the number of elements in nas and the dynamic range.
+ *      (2) If there are negative values in nas, it selects shell sort.
+ * 
+ */ +l_int32 +numaChooseSortType(NUMA *nas) +{ +l_int32 n, type; +l_float32 minval, maxval; + + PROCNAME("numaChooseSortType"); + + if (!nas) + return ERROR_INT("nas not defined", procName, UNDEF); + + numaGetMin(nas, &minval, NULL); + n = numaGetCount(nas); + + /* Very small histogram; use shell sort */ + if (minval < 0.0 || n < 200) { + L_INFO("Shell sort chosen\n", procName); + return L_SHELL_SORT; + } + + /* Need to compare nlog(n) with maxval. The factor of 0.003 + * was determined by comparing times for different histogram + * sizes and maxval. It is very small because binsort is fast + * and shell sort gets slow for large n. */ + numaGetMax(nas, &maxval, NULL); + if (n * log((l_float32)n) < 0.003 * maxval) { + type = L_SHELL_SORT; + L_INFO("Shell sort chosen\n", procName); + } else { + type = L_BIN_SORT; + L_INFO("Bin sort chosen\n", procName); + } + return type; +} + + +/*! + * \brief numaSort() + * + * \param[in] naout output numa; can be NULL or equal to nain + * \param[in] nain input numa + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return naout output sorted numa, or NULL on error + * + *
+ * Notes:
+ *      (1) Set naout = nain for in-place; otherwise, set naout = NULL.
+ *      (2) Source: Shell sort, modified from K&R, 2nd edition, p.62.
+ *          Slow but simple O(n logn) sort.
+ * 
+ */ +NUMA * +numaSort(NUMA *naout, + NUMA *nain, + l_int32 sortorder) +{ +l_int32 i, n, gap, j; +l_float32 tmp; +l_float32 *array; + + PROCNAME("numaSort"); + + if (!nain) + return (NUMA *)ERROR_PTR("nain not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); + + /* Make naout if necessary; otherwise do in-place */ + if (!naout) + naout = numaCopy(nain); + else if (nain != naout) + return (NUMA *)ERROR_PTR("invalid: not in-place", procName, NULL); + array = naout->array; /* operate directly on the array */ + n = numaGetCount(naout); + + /* Shell sort */ + for (gap = n/2; gap > 0; gap = gap / 2) { + for (i = gap; i < n; i++) { + for (j = i - gap; j >= 0; j -= gap) { + if ((sortorder == L_SORT_INCREASING && + array[j] > array[j + gap]) || + (sortorder == L_SORT_DECREASING && + array[j] < array[j + gap])) + { + tmp = array[j]; + array[j] = array[j + gap]; + array[j + gap] = tmp; + } + } + } + } + + return naout; +} + + +/*! + * \brief numaBinSort() + * + * \param[in] nas of non-negative integers with a max that is + * typically less than 50,000 + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return na sorted, or NULL on error + * + *
+ * Notes:
+ *      (1) Because this uses a bin sort with buckets of size 1, it
+ *          is not appropriate for sorting either small arrays or
+ *          arrays containing very large integer values.  For such
+ *          arrays, use a standard general sort function like
+ *          numaSort().
+ * 
+ */ +NUMA * +numaBinSort(NUMA *nas, + l_int32 sortorder) +{ +NUMA *nat, *nad; + + PROCNAME("numaBinSort"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); + + nat = numaGetBinSortIndex(nas, sortorder); + nad = numaSortByIndex(nas, nat); + numaDestroy(&nat); + return nad; +} + + +/*! + * \brief numaGetSortIndex() + * + * \param[in] na source numa + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return na giving an array of indices that would sort + * the input array, or NULL on error + */ +NUMA * +numaGetSortIndex(NUMA *na, + l_int32 sortorder) +{ +l_int32 i, n, gap, j; +l_float32 tmp; +l_float32 *array; /* copy of input array */ +l_float32 *iarray; /* array of indices */ +NUMA *naisort; + + PROCNAME("numaGetSortIndex"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sortorder", procName, NULL); + + n = numaGetCount(na); + if ((array = numaGetFArray(na, L_COPY)) == NULL) + return (NUMA *)ERROR_PTR("array not made", procName, NULL); + if ((iarray = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32))) == NULL) { + LEPT_FREE(array); + return (NUMA *)ERROR_PTR("iarray not made", procName, NULL); + } + for (i = 0; i < n; i++) + iarray[i] = i; + + /* Shell sort */ + for (gap = n/2; gap > 0; gap = gap / 2) { + for (i = gap; i < n; i++) { + for (j = i - gap; j >= 0; j -= gap) { + if ((sortorder == L_SORT_INCREASING && + array[j] > array[j + gap]) || + (sortorder == L_SORT_DECREASING && + array[j] < array[j + gap])) + { + tmp = array[j]; + array[j] = array[j + gap]; + array[j + gap] = tmp; + tmp = iarray[j]; + iarray[j] = iarray[j + gap]; + iarray[j + gap] = tmp; + } + } + } + } + + naisort = numaCreate(n); + for (i = 0; i < n; i++) + numaAddNumber(naisort, iarray[i]); + + LEPT_FREE(array); + LEPT_FREE(iarray); + return naisort; +} + + +/*! + * \brief numaGetBinSortIndex() + * + * \param[in] nas of non-negative integers with a max that is + * typically less than 1,000,000 + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return na sorted, or NULL on error + * + *
+ * Notes:
+ *      (1) This creates an array (or lookup table) that contains
+ *          the sorted position of the elements in the input Numa.
+ *      (2) Because it uses a bin sort with buckets of size 1, it
+ *          is not appropriate for sorting either small arrays or
+ *          arrays containing very large integer values.  For such
+ *          arrays, use a standard general sort function like
+ *          numaGetSortIndex().
+ * 
+ */ +NUMA * +numaGetBinSortIndex(NUMA *nas, + l_int32 sortorder) +{ +l_int32 i, n, isize, ival, imax; +l_float32 size; +NUMA *na, *nai, *nad; +L_PTRA *paindex; + + PROCNAME("numaGetBinSortIndex"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); + + /* Set up a ptra holding numa at indices for which there + * are values in nas. Suppose nas has the value 230 at index + * 7355. A numa holding the index 7355 is created and stored + * at the ptra index 230. If there is another value of 230 + * in nas, its index is added to the same numa (at index 230 + * in the ptra). When finished, the ptra can be scanned for numa, + * and the original indices in the nas can be read out. In this + * way, the ptra effectively sorts the input numbers in the nas. */ + numaGetMax(nas, &size, NULL); + isize = (l_int32)size; + if (isize > 1000000) + L_WARNING("large array: %d elements\n", procName, isize); + paindex = ptraCreate(isize + 1); + n = numaGetCount(nas); + for (i = 0; i < n; i++) { + numaGetIValue(nas, i, &ival); + nai = (NUMA *)ptraGetPtrToItem(paindex, ival); + if (!nai) { /* make it; no shifting will occur */ + nai = numaCreate(1); + ptraInsert(paindex, ival, nai, L_MIN_DOWNSHIFT); + } + numaAddNumber(nai, i); + } + + /* Sort by scanning the ptra, extracting numas and pulling + * the (index into nas) numbers out of each numa, taken + * successively in requested order. */ + ptraGetMaxIndex(paindex, &imax); + nad = numaCreate(0); + if (sortorder == L_SORT_INCREASING) { + for (i = 0; i <= imax; i++) { + na = (NUMA *)ptraRemove(paindex, i, L_NO_COMPACTION); + if (!na) continue; + numaJoin(nad, na, 0, -1); + numaDestroy(&na); + } + } else { /* L_SORT_DECREASING */ + for (i = imax; i >= 0; i--) { + na = (NUMA *)ptraRemoveLast(paindex); + if (!na) break; /* they've all been removed */ + numaJoin(nad, na, 0, -1); + numaDestroy(&na); + } + } + + ptraDestroy(&paindex, FALSE, FALSE); + return nad; +} + + +/*! + * \brief numaSortByIndex() + * + * \param[in] nas + * \param[in] naindex na that maps from the new numa to the input numa + * \return nad sorted, or NULL on error + */ +NUMA * +numaSortByIndex(NUMA *nas, + NUMA *naindex) +{ +l_int32 i, n, index; +l_float32 val; +NUMA *nad; + + PROCNAME("numaSortByIndex"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (!naindex) + return (NUMA *)ERROR_PTR("naindex not defined", procName, NULL); + + n = numaGetCount(nas); + nad = numaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + numaGetFValue(nas, index, &val); + numaAddNumber(nad, val); + } + + return nad; +} + + +/*! + * \brief numaIsSorted() + * + * \param[in] nas + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \param[out] psorted 1 if sorted; 0 if not + * \return 1 if OK; 0 on error + * + *
+ * Notes:
+ *      (1) This is a quick O(n) test if nas is sorted.  It is useful
+ *          in situations where the array is likely to be already
+ *          sorted, and a sort operation can be avoided.
+ * 
+ */ +l_int32 +numaIsSorted(NUMA *nas, + l_int32 sortorder, + l_int32 *psorted) +{ +l_int32 i, n; +l_float32 prevval, val; + + PROCNAME("numaIsSorted"); + + if (!psorted) + return ERROR_INT("&sorted not defined", procName, 1); + *psorted = FALSE; + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return ERROR_INT("invalid sortorder", procName, 1); + + n = numaGetCount(nas); + numaGetFValue(nas, 0, &prevval); + for (i = 1; i < n; i++) { + numaGetFValue(nas, i, &val); + if ((sortorder == L_SORT_INCREASING && val < prevval) || + (sortorder == L_SORT_DECREASING && val > prevval)) + return 0; + } + + *psorted = TRUE; + return 0; +} + + +/*! + * \brief numaSortPair() + * + * \param[in] nax, nay input arrays + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \param[out] pnasx sorted + * \param[out] pnasy sorted exactly in order of nasx + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function sorts the two input arrays, nax and nay,
+ *          together, using nax as the key for sorting.
+ * 
+ */ +l_ok +numaSortPair(NUMA *nax, + NUMA *nay, + l_int32 sortorder, + NUMA **pnasx, + NUMA **pnasy) +{ +l_int32 sorted; +NUMA *naindex; + + PROCNAME("numaSortPair"); + + if (pnasx) *pnasx = NULL; + if (pnasy) *pnasy = NULL; + if (!pnasx || !pnasy) + return ERROR_INT("&nasx and/or &nasy not defined", procName, 1); + if (!nax) + return ERROR_INT("nax not defined", procName, 1); + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return ERROR_INT("invalid sortorder", procName, 1); + + numaIsSorted(nax, sortorder, &sorted); + if (sorted == TRUE) { + *pnasx = numaCopy(nax); + *pnasy = numaCopy(nay); + } else { + naindex = numaGetSortIndex(nax, sortorder); + *pnasx = numaSortByIndex(nax, naindex); + *pnasy = numaSortByIndex(nay, naindex); + numaDestroy(&naindex); + } + + return 0; +} + + +/*! + * \brief numaInvertMap() + * + * \param[in] nas + * \return nad the inverted map, or NULL on error or if not invertible + * + *
+ * Notes:
+ *      (1) This requires that nas contain each integer from 0 to n-1.
+ *          The array is typically an index array into a sort or permutation
+ *          of another array.
+ * 
+ */ +NUMA * +numaInvertMap(NUMA *nas) +{ +l_int32 i, n, val, error; +l_int32 *test; +NUMA *nad; + + PROCNAME("numaInvertMap"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + + n = numaGetCount(nas); + nad = numaMakeConstant(0.0, n); + test = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); + error = 0; + for (i = 0; i < n; i++) { + numaGetIValue(nas, i, &val); + if (val >= n) { + error = 1; + break; + } + numaReplaceNumber(nad, val, i); + if (test[val] == 0) { + test[val] = 1; + } else { + error = 1; + break; + } + } + + LEPT_FREE(test); + if (error) { + numaDestroy(&nad); + return (NUMA *)ERROR_PTR("nas not invertible", procName, NULL); + } + + return nad; +} + + +/*----------------------------------------------------------------------* + * Random permutation * + *----------------------------------------------------------------------*/ +/*! + * \brief numaPseudorandomSequence() + * + * \param[in] size of sequence + * \param[in] seed for random number generation + * \return na pseudorandom on {0,...,size - 1}, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses the Durstenfeld shuffle.
+ *          See: http://en.wikipedia.org/wiki/Fisher鈥揧ates_shuffle.
+ *          Result is a pseudorandom permutation of the sequence of integers
+ *          from 0 to size - 1.
+ * 
+ */ +NUMA * +numaPseudorandomSequence(l_int32 size, + l_int32 seed) +{ +l_int32 i, index, temp; +l_int32 *array; +NUMA *na; + + PROCNAME("numaPseudorandomSequence"); + + if (size <= 0) + return (NUMA *)ERROR_PTR("size <= 0", procName, NULL); + + if ((array = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) + return (NUMA *)ERROR_PTR("array not made", procName, NULL); + for (i = 0; i < size; i++) + array[i] = i; + srand(seed); + for (i = size - 1; i > 0; i--) { + index = (l_int32)((i + 1) * ((l_float64)rand() / (l_float64)RAND_MAX)); + index = L_MIN(index, i); + temp = array[i]; + array[i] = array[index]; + array[index] = temp; + } + + na = numaCreateFromIArray(array, size); + LEPT_FREE(array); + return na; +} + + +/*! + * \brief numaRandomPermutation() + * + * \param[in] nas input array + * \param[in] seed for random number generation + * \return nas randomly shuffled array, or NULL on error + */ +NUMA * +numaRandomPermutation(NUMA *nas, + l_int32 seed) +{ +l_int32 i, index, size; +l_float32 val; +NUMA *naindex, *nad; + + PROCNAME("numaRandomPermutation"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + + size = numaGetCount(nas); + naindex = numaPseudorandomSequence(size, seed); + nad = numaCreate(size); + for (i = 0; i < size; i++) { + numaGetIValue(naindex, i, &index); + numaGetFValue(nas, index, &val); + numaAddNumber(nad, val); + } + + numaDestroy(&naindex); + return nad; +} + + +/*----------------------------------------------------------------------* + * Functions requiring sorting * + *----------------------------------------------------------------------*/ +/*! + * \brief numaGetRankValue() + * + * \param[in] na source numa + * \param[in] fract use 0.0 for smallest, 1.0 for largest + * \param[in] nasort [optional] increasing sorted version of na + * \param[in] usebins 0 for general sort; 1 for bin sort + * \param[out] pval rank val + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Computes the rank value of a number in the %na, which is
+ *          the number that is a fraction %fract from the small
+ *          end of the sorted version of %na.
+ *      (2) If you do this multiple times for different rank values,
+ *          sort the array in advance and use that for %nasort;
+ *          if you're only calling this once, input %nasort == NULL.
+ *      (3) If %usebins == 1, this uses a bin sorting method.
+ *          Use this only where:
+ *           * the numbers are non-negative integers
+ *           * there are over 100 numbers
+ *           * the maximum value is less than about 50,000
+ *      (4) The advantage of using a bin sort is that it is O(n),
+ *          instead of O(nlogn) for general sort routines.
+ * 
+ */ +l_ok +numaGetRankValue(NUMA *na, + l_float32 fract, + NUMA *nasort, + l_int32 usebins, + l_float32 *pval) +{ +l_int32 n, index; +NUMA *nas; + + PROCNAME("numaGetRankValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; /* init */ + if (!na) + return ERROR_INT("na not defined", procName, 1); + if ((n = numaGetCount(na)) == 0) + return ERROR_INT("na empty", procName, 1); + if (fract < 0.0 || fract > 1.0) + return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); + + if (nasort) { + nas = nasort; + } else { + if (usebins == 0) + nas = numaSort(NULL, na, L_SORT_INCREASING); + else + nas = numaBinSort(na, L_SORT_INCREASING); + if (!nas) + return ERROR_INT("nas not made", procName, 1); + } + index = (l_int32)(fract * (l_float32)(n - 1) + 0.5); + numaGetFValue(nas, index, pval); + + if (!nasort) numaDestroy(&nas); + return 0; +} + + +/*! + * \brief numaGetMedian() + * + * \param[in] na source numa + * \param[out] pval median value + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Computes the median value of the numbers in the numa, by
+ *          sorting and finding the middle value in the sorted array.
+ * 
+ */ +l_ok +numaGetMedian(NUMA *na, + l_float32 *pval) +{ + PROCNAME("numaGetMedian"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; /* init */ + if (!na) + return ERROR_INT("na not defined", procName, 1); + + return numaGetRankValue(na, 0.5, NULL, 0, pval); +} + + +/*! + * \brief numaGetBinnedMedian() + * + * \param[in] na source numa + * \param[out] pval integer median value + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Computes the median value of the numbers in the numa,
+ *          using bin sort and finding the middle value in the sorted array.
+ *      (2) See numaGetRankValue() for conditions on na for which
+ *          this should be used.  Otherwise, use numaGetMedian().
+ * 
+ */ +l_ok +numaGetBinnedMedian(NUMA *na, + l_int32 *pval) +{ +l_int32 ret; +l_float32 fval; + + PROCNAME("numaGetBinnedMedian"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; /* init */ + if (!na) + return ERROR_INT("na not defined", procName, 1); + + ret = numaGetRankValue(na, 0.5, NULL, 1, &fval); + *pval = lept_roundftoi(fval); + return ret; +} + + +/*! + * \brief numaGetMeanDevFromMedian() + * + * \param[in] na source numa + * \param[in] med median value + * \param[out] pdev average absolute value deviation from median value + * \return 0 if OK; 1 on error + */ +l_ok +numaGetMeanDevFromMedian(NUMA *na, + l_float32 med, + l_float32 *pdev) +{ +l_int32 i, n; +l_float32 val, dev; + + PROCNAME("numaGetMeanDevFromMedian"); + + if (!pdev) + return ERROR_INT("&dev not defined", procName, 1); + *pdev = 0.0; /* init */ + if (!na) + return ERROR_INT("na not defined", procName, 1); + if ((n = numaGetCount(na)) == 0) + return ERROR_INT("na is empty", procName, 1); + + dev = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + dev += L_ABS(val - med); + } + *pdev = dev / (l_float32)n; + return 0; +} + + +/*! + * \brief numaGetMedianDevFromMedian() + * + * \param[in] na source numa + * \param[out] pmed [optional] median value + * \param[out] pdev median deviation from median val + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Finds the median of the absolute value of the deviation from
+ *          the median value in the array.  Why take the absolute value?
+ *          Consider the case where you have values equally distributed
+ *          about both sides of a median value.  Without taking the absolute
+ *          value of the differences, you will get 0 for the deviation,
+ *          and this is not useful.
+ * 
+ */ +l_ok +numaGetMedianDevFromMedian(NUMA *na, + l_float32 *pmed, + l_float32 *pdev) +{ +l_int32 n, i; +l_float32 val, med; +NUMA *nadev; + + PROCNAME("numaGetMedianDevFromMedian"); + + if (pmed) *pmed = 0.0; + if (!pdev) + return ERROR_INT("&dev not defined", procName, 1); + *pdev = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + numaGetMedian(na, &med); + if (pmed) *pmed = med; + n = numaGetCount(na); + nadev = numaCreate(n); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + numaAddNumber(nadev, L_ABS(val - med)); + } + numaGetMedian(nadev, pdev); + + numaDestroy(&nadev); + return 0; +} + + +/*! + * \brief numaGetMode() + * + * \param[in] na source numa + * \param[out] pval mode val + * \param[out] pcount [optional] mode count + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Computes the mode value of the numbers in the numa, by
+ *          sorting and finding the value of the number with the
+ *          largest count.
+ *      (2) Optionally, also returns that count.
+ * 
+ */ +l_ok +numaGetMode(NUMA *na, + l_float32 *pval, + l_int32 *pcount) +{ +l_int32 i, n, maxcount, prevcount; +l_float32 val, maxval, prevval; +l_float32 *array; +NUMA *nasort; + + PROCNAME("numaGetMode"); + + if (pcount) *pcount = 0; + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + if ((n = numaGetCount(na)) == 0) + return 1; + + if ((nasort = numaSort(NULL, na, L_SORT_DECREASING)) == NULL) + return ERROR_INT("nas not made", procName, 1); + array = numaGetFArray(nasort, L_NOCOPY); + + /* Initialize with array[0] */ + prevval = array[0]; + prevcount = 1; + maxval = prevval; + maxcount = prevcount; + + /* Scan the sorted array, aggregating duplicates */ + for (i = 1; i < n; i++) { + val = array[i]; + if (val == prevval) { + prevcount++; + } else { /* new value */ + if (prevcount > maxcount) { /* new max */ + maxcount = prevcount; + maxval = prevval; + } + prevval = val; + prevcount = 1; + } + } + + /* Was the mode the last run of elements? */ + if (prevcount > maxcount) { + maxcount = prevcount; + maxval = prevval; + } + + *pval = maxval; + if (pcount) + *pcount = maxcount; + + numaDestroy(&nasort); + return 0; +} + + +/*----------------------------------------------------------------------* + * Rearrangements * + *----------------------------------------------------------------------*/ +/*! + * \brief numaJoin() + * + * \param[in] nad dest numa; add to this one + * \param[in] nas [optional] source numa; add from this one + * \param[in] istart starting index in nas + * \param[in] iend ending index in nas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (2) iend < 0 means 'read to the end'
+ *      (3) if nas == NULL, this is a no-op
+ * 
+ */ +l_ok +numaJoin(NUMA *nad, + NUMA *nas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +l_float32 val; + + PROCNAME("numaJoin"); + + if (!nad) + return ERROR_INT("nad not defined", procName, 1); + if (!nas) + return 0; + + if (istart < 0) + istart = 0; + n = numaGetCount(nas); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + numaGetFValue(nas, i, &val); + numaAddNumber(nad, val); + } + + return 0; +} + + +/*! + * \brief numaaJoin() + * + * \param[in] naad dest naa; add to this one + * \param[in] naas [optional] source naa; add from this one + * \param[in] istart starting index in nas + * \param[in] iend ending index in naas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (2) iend < 0 means 'read to the end'
+ *      (3) if naas == NULL, this is a no-op
+ * 
+ */ +l_ok +numaaJoin(NUMAA *naad, + NUMAA *naas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +NUMA *na; + + PROCNAME("numaaJoin"); + + if (!naad) + return ERROR_INT("naad not defined", procName, 1); + if (!naas) + return 0; + + if (istart < 0) + istart = 0; + n = numaaGetCount(naas); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + na = numaaGetNuma(naas, i, L_CLONE); + numaaAddNuma(naad, na, L_INSERT); + } + + return 0; +} + + +/*! + * \brief numaaFlattenToNuma() + * + * \param[in] naa + * \return numa, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the Numaa to a Numa, by joining successively
+ *          each Numa in the Numaa.
+ *      (2) It doesn't make any assumptions about the location of the
+ *          Numas in the Numaa array, unlike most Numaa functions.
+ *      (3) It leaves the input Numaa unchanged.
+ * 
+ */ +NUMA * +numaaFlattenToNuma(NUMAA *naa) +{ +l_int32 i, nalloc; +NUMA *na, *nad; +NUMA **array; + + PROCNAME("numaaFlattenToNuma"); + + if (!naa) + return (NUMA *)ERROR_PTR("naa not defined", procName, NULL); + + nalloc = naa->nalloc; + array = numaaGetPtrArray(naa); + nad = numaCreate(0); + for (i = 0; i < nalloc; i++) { + na = array[i]; + if (!na) continue; + numaJoin(nad, na, 0, -1); + } + + return nad; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/numafunc2.c b/hgdriver/3rdparty/hgOCR/leptonica/numafunc2.c new file mode 100644 index 0000000..5f37df7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/numafunc2.c @@ -0,0 +1,3139 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file numafunc2.c + *
+ *
+ *      --------------------------------------
+ *      This file has these Numa utilities:
+ *         - morphological operations
+ *         - arithmetic transforms
+ *         - windowed statistical operations
+ *         - histogram extraction
+ *         - histogram comparison
+ *         - extrema finding
+ *         - frequency and crossing analysis
+ *      --------------------------------------
+
+ *      Morphological (min/max) operations
+ *          NUMA        *numaErode()
+ *          NUMA        *numaDilate()
+ *          NUMA        *numaOpen()
+ *          NUMA        *numaClose()
+ *
+ *      Other transforms
+ *          NUMA        *numaTransform()
+ *          l_int32      numaSimpleStats()
+ *          l_int32      numaWindowedStats()
+ *          NUMA        *numaWindowedMean()
+ *          NUMA        *numaWindowedMeanSquare()
+ *          l_int32      numaWindowedVariance()
+ *          NUMA        *numaWindowedMedian()
+ *          NUMA        *numaConvertToInt()
+ *
+ *      Histogram generation and statistics
+ *          NUMA        *numaMakeHistogram()
+ *          NUMA        *numaMakeHistogramAuto()
+ *          NUMA        *numaMakeHistogramClipped()
+ *          NUMA        *numaRebinHistogram()
+ *          NUMA        *numaNormalizeHistogram()
+ *          l_int32      numaGetStatsUsingHistogram()
+ *          l_int32      numaGetHistogramStats()
+ *          l_int32      numaGetHistogramStatsOnInterval()
+ *          l_int32      numaMakeRankFromHistogram()
+ *          l_int32      numaHistogramGetRankFromVal()
+ *          l_int32      numaHistogramGetValFromRank()
+ *          l_int32      numaDiscretizeRankAndIntensity()
+ *          l_int32      numaGetRankBinValues()
+ *
+ *      Splitting a distribution
+ *          l_int32      numaSplitDistribution()
+ *
+ *      Comparing histograms
+ *          l_int32      grayHistogramsToEMD()
+ *          l_int32      numaEarthMoverDistance()
+ *          l_int32      grayInterHistogramStats()
+ *
+ *      Extrema finding
+ *          NUMA        *numaFindPeaks()
+ *          NUMA        *numaFindExtrema()
+ *          l_int32     *numaCountReversals()
+ *
+ *      Threshold crossings and frequency analysis
+ *          l_int32      numaSelectCrossingThreshold()
+ *          NUMA        *numaCrossingsByThreshold()
+ *          NUMA        *numaCrossingsByPeaks()
+ *          NUMA        *numaEvalBestHaarParameters()
+ *          l_int32      numaEvalHaarSum()
+ *
+ *      Generating numbers in a range under constraints
+ *          NUMA        *genConstrainedNumaInRange()
+ *
+ *    Things to remember when using the Numa:
+ *
+ *    (1) The numa is a struct, not an array.  Always use accessors
+ *        (see numabasic.c), never the fields directly.
+ *
+ *    (2) The number array holds l_float32 values.  It can also
+ *        be used to store l_int32 values.  See numabasic.c for
+ *        details on using the accessors.  Integers larger than
+ *        about 10M will lose accuracy due on retrieval due to round-off.
+ *        For large integers, use the dna (array of l_float64) instead.
+ *
+ *    (3) Occasionally, in the comments we denote the i-th element of a
+ *        numa by na[i].  This is conceptual only -- the numa is not an array!
+ *
+ *    Some general comments on histograms:
+ *
+ *    (1) Histograms are the generic statistical representation of
+ *        the data about some attribute.  Typically they're not
+ *        normalized -- they simply give the number of occurrences
+ *        within each range of values of the attribute.  This range
+ *        of values is referred to as a 'bucket'.  For example,
+ *        the histogram could specify how many connected components
+ *        are found for each value of their width; in that case,
+ *        the bucket size is 1.
+ *
+ *    (2) In leptonica, all buckets have the same size.  Histograms
+ *        are therefore specified by a numa of occurrences, along
+ *        with two other numbers: the 'value' associated with the
+ *        occupants of the first bucket and the size (i.e., 'width')
+ *        of each bucket.  These two numbers then allow us to calculate
+ *        the value associated with the occupants of each bucket.
+ *        These numbers are fields in the numa, initialized to
+ *        a startx value of 0.0 and a binsize of 1.0.  Accessors for
+ *        these fields are functions numa*Parameters().  All histograms
+ *        must have these two numbers properly set.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* bin sizes in numaMakeHistogram() */ +static const l_int32 BinSizeArray[] = {2, 5, 10, 20, 50, 100, 200, 500, 1000,\ + 2000, 5000, 10000, 20000, 50000, 100000, 200000,\ + 500000, 1000000, 2000000, 5000000, 10000000,\ + 200000000, 50000000, 100000000}; +static const l_int32 NBinSizes = 24; + + +#ifndef NO_CONSOLE_IO +#define DEBUG_HISTO 0 +#define DEBUG_CROSSINGS 0 +#define DEBUG_FREQUENCY 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*----------------------------------------------------------------------* + * Morphological operations * + *----------------------------------------------------------------------*/ +/*! + * \brief numaErode() + * + * \param[in] nas + * \param[in] size of sel; greater than 0, odd. The origin + * is implicitly in the center. + * \return nad eroded, or NULL on error + * + *
+ * Notes:
+ *      (1) The structuring element (sel) is linear, all "hits"
+ *      (2) If size == 1, this returns a copy
+ *      (3) General comment.  The morphological operations are equivalent
+ *          to those that would be performed on a 1-dimensional fpix.
+ *          However, because we have not implemented morphological
+ *          operations on fpix, we do this here.  Because it is only
+ *          1 dimensional, there is no reason to use the more
+ *          complicated van Herk/Gil-Werman algorithm, and we do it
+ *          by brute force.
+ * 
+ */ +NUMA * +numaErode(NUMA *nas, + l_int32 size) +{ +l_int32 i, j, n, hsize, len; +l_float32 minval; +l_float32 *fa, *fas, *fad; +NUMA *nad; + + PROCNAME("numaErode"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + if ((size & 1) == 0 ) { + L_WARNING("sel size must be odd; increasing by 1\n", procName); + size++; + } + + if (size == 1) + return numaCopy(nas); + + /* Make a source fa (fas) that has an added (size / 2) boundary + * on left and right, contains a copy of nas in the interior region + * (between 'size' and 'size + n', and has large values + * inserted in the boundary (because it is an erosion). */ + n = numaGetCount(nas); + hsize = size / 2; + len = n + 2 * hsize; + if ((fas = (l_float32 *)LEPT_CALLOC(len, sizeof(l_float32))) == NULL) + return (NUMA *)ERROR_PTR("fas not made", procName, NULL); + for (i = 0; i < hsize; i++) + fas[i] = 1.0e37; + for (i = hsize + n; i < len; i++) + fas[i] = 1.0e37; + fa = numaGetFArray(nas, L_NOCOPY); + for (i = 0; i < n; i++) + fas[hsize + i] = fa[i]; + + nad = numaMakeConstant(0, n); + numaCopyParameters(nad, nas); + fad = numaGetFArray(nad, L_NOCOPY); + for (i = 0; i < n; i++) { + minval = 1.0e37; /* start big */ + for (j = 0; j < size; j++) + minval = L_MIN(minval, fas[i + j]); + fad[i] = minval; + } + + LEPT_FREE(fas); + return nad; +} + + +/*! + * \brief numaDilate() + * + * \param[in] nas + * \param[in] size of sel; greater than 0, odd. The origin + * is implicitly in the center. + * \return nad dilated, or NULL on error + * + *
+ * Notes:
+ *      (1) The structuring element (sel) is linear, all "hits"
+ *      (2) If size == 1, this returns a copy
+ * 
+ */ +NUMA * +numaDilate(NUMA *nas, + l_int32 size) +{ +l_int32 i, j, n, hsize, len; +l_float32 maxval; +l_float32 *fa, *fas, *fad; +NUMA *nad; + + PROCNAME("numaDilate"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + if ((size & 1) == 0 ) { + L_WARNING("sel size must be odd; increasing by 1\n", procName); + size++; + } + + if (size == 1) + return numaCopy(nas); + + /* Make a source fa (fas) that has an added (size / 2) boundary + * on left and right, contains a copy of nas in the interior region + * (between 'size' and 'size + n', and has small values + * inserted in the boundary (because it is a dilation). */ + n = numaGetCount(nas); + hsize = size / 2; + len = n + 2 * hsize; + if ((fas = (l_float32 *)LEPT_CALLOC(len, sizeof(l_float32))) == NULL) + return (NUMA *)ERROR_PTR("fas not made", procName, NULL); + for (i = 0; i < hsize; i++) + fas[i] = -1.0e37; + for (i = hsize + n; i < len; i++) + fas[i] = -1.0e37; + fa = numaGetFArray(nas, L_NOCOPY); + for (i = 0; i < n; i++) + fas[hsize + i] = fa[i]; + + nad = numaMakeConstant(0, n); + numaCopyParameters(nad, nas); + fad = numaGetFArray(nad, L_NOCOPY); + for (i = 0; i < n; i++) { + maxval = -1.0e37; /* start small */ + for (j = 0; j < size; j++) + maxval = L_MAX(maxval, fas[i + j]); + fad[i] = maxval; + } + + LEPT_FREE(fas); + return nad; +} + + +/*! + * \brief numaOpen() + * + * \param[in] nas + * \param[in] size of sel; greater than 0, odd. The origin + * is implicitly in the center. + * \return nad opened, or NULL on error + * + *
+ * Notes:
+ *      (1) The structuring element (sel) is linear, all "hits"
+ *      (2) If size == 1, this returns a copy
+ * 
+ */ +NUMA * +numaOpen(NUMA *nas, + l_int32 size) +{ +NUMA *nat, *nad; + + PROCNAME("numaOpen"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + if ((size & 1) == 0 ) { + L_WARNING("sel size must be odd; increasing by 1\n", procName); + size++; + } + + if (size == 1) + return numaCopy(nas); + + nat = numaErode(nas, size); + nad = numaDilate(nat, size); + numaDestroy(&nat); + return nad; +} + + +/*! + * \brief numaClose() + * + * \param[in] nas + * \param[in] size of sel; greater than 0, odd. The origin + * is implicitly in the center. + * \return nad closed, or NULL on error + * + *
+ * Notes:
+ *      (1) The structuring element (sel) is linear, all "hits"
+ *      (2) If size == 1, this returns a copy
+ *      (3) We add a border before doing this operation, for the same
+ *          reason that we add a border to a pix before doing a safe closing.
+ *          Without the border, a small component near the border gets
+ *          clipped at the border on dilation, and can be entirely removed
+ *          by the following erosion, violating the basic extensivity
+ *          property of closing.
+ * 
+ */ +NUMA * +numaClose(NUMA *nas, + l_int32 size) +{ +NUMA *nab, *nat1, *nat2, *nad; + + PROCNAME("numaClose"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (size <= 0) + return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); + if ((size & 1) == 0 ) { + L_WARNING("sel size must be odd; increasing by 1\n", procName); + size++; + } + + if (size == 1) + return numaCopy(nas); + + nab = numaAddBorder(nas, size, size, 0); /* to preserve extensivity */ + nat1 = numaDilate(nab, size); + nat2 = numaErode(nat1, size); + nad = numaRemoveBorder(nat2, size, size); + numaDestroy(&nab); + numaDestroy(&nat1); + numaDestroy(&nat2); + return nad; +} + + +/*----------------------------------------------------------------------* + * Other transforms * + *----------------------------------------------------------------------*/ +/*! + * \brief numaTransform() + * + * \param[in] nas + * \param[in] shift add this to each number + * \param[in] scale multiply each number by this + * \return nad with all values shifted and scaled, or NULL on error + * + *
+ * Notes:
+ *      (1) Each number is shifted before scaling.
+ * 
+ */ +NUMA * +numaTransform(NUMA *nas, + l_float32 shift, + l_float32 scale) +{ +l_int32 i, n; +l_float32 val; +NUMA *nad; + + PROCNAME("numaTransform"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + if ((nad = numaCreate(n)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + numaCopyParameters(nad, nas); + for (i = 0; i < n; i++) { + numaGetFValue(nas, i, &val); + val = scale * (val + shift); + numaAddNumber(nad, val); + } + return nad; +} + + +/*! + * \brief numaSimpleStats() + * + * \param[in] na input numa + * \param[in] first first element to use + * \param[in] last last element to use; -1 to go to the end + * \param[out] pmean [optional] mean value + * \param[out] pvar [optional] variance + * \param[out] prvar [optional] rms deviation from the mean + * \return 0 if OK, 1 on error + */ +l_ok +numaSimpleStats(NUMA *na, + l_int32 first, + l_int32 last, + l_float32 *pmean, + l_float32 *pvar, + l_float32 *prvar) +{ +l_int32 i, n, ni; +l_float32 sum, sumsq, val, mean, var; + + PROCNAME("numaSimpleStats"); + + if (pmean) *pmean = 0.0; + if (pvar) *pvar = 0.0; + if (prvar) *prvar = 0.0; + if (!pmean && !pvar && !prvar) + return ERROR_INT("nothing requested", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + if ((n = numaGetCount(na)) == 0) + return ERROR_INT("na is empty", procName, 1); + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return ERROR_INT("invalid first", procName, 1); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return ERROR_INT("first > last\n", procName, 1); + ni = last - first + 1; + sum = sumsq = 0.0; + for (i = first; i <= last; i++) { + numaGetFValue(na, i, &val); + sum += val; + sumsq += val * val; + } + + mean = sum / ni; + if (pmean) + *pmean = mean; + if (pvar || prvar) { + var = sumsq / ni - mean * mean; + if (pvar) *pvar = var; + if (prvar) *prvar = sqrtf(var); + } + + return 0; +} + + +/*! + * \brief numaWindowedStats() + * + * \param[in] nas input numa + * \param[in] wc half width of the window + * \param[out] pnam [optional] mean value in window + * \param[out] pnams [optional] mean square value in window + * \param[out] pnav [optional] variance in window + * \param[out] pnarv [optional] rms deviation from the mean + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a high-level convenience function for calculating
+ *          any or all of these derived arrays.
+ *      (2) These statistical measures over the values in the
+ *          rectangular window are:
+ *            ~ average value: [x]  (nam)
+ *            ~ average squared value: [x*x] (nams)
+ *            ~ variance: [(x - [x])*(x - [x])] = [x*x] - [x]*[x]  (nav)
+ *            ~ square-root of variance: (narv)
+ *          where the brackets [ .. ] indicate that the average value is
+ *          to be taken over the window.
+ *      (3) Note that the variance is just the mean square difference from
+ *          the mean value; and the square root of the variance is the
+ *          root mean square difference from the mean, sometimes also
+ *          called the 'standard deviation'.
+ *      (4) Internally, use mirrored borders to handle values near the
+ *          end of each array.
+ * 
+ */ +l_ok +numaWindowedStats(NUMA *nas, + l_int32 wc, + NUMA **pnam, + NUMA **pnams, + NUMA **pnav, + NUMA **pnarv) +{ +NUMA *nam, *nams; + + PROCNAME("numaWindowedStats"); + + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if (2 * wc + 1 > numaGetCount(nas)) + L_WARNING("filter wider than input array!\n", procName); + + if (!pnav && !pnarv) { + if (pnam) *pnam = numaWindowedMean(nas, wc); + if (pnams) *pnams = numaWindowedMeanSquare(nas, wc); + return 0; + } + + nam = numaWindowedMean(nas, wc); + nams = numaWindowedMeanSquare(nas, wc); + numaWindowedVariance(nam, nams, pnav, pnarv); + if (pnam) + *pnam = nam; + else + numaDestroy(&nam); + if (pnams) + *pnams = nams; + else + numaDestroy(&nams); + return 0; +} + + +/*! + * \brief numaWindowedMean() + * + * \param[in] nas + * \param[in] wc half width of the convolution window + * \return nad after low-pass filtering, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a convolution.  The window has width = 2 * %wc + 1.
+ *      (2) We add a mirrored border of size %wc to each end of the array.
+ * 
+ */ +NUMA * +numaWindowedMean(NUMA *nas, + l_int32 wc) +{ +l_int32 i, n, n1, width; +l_float32 sum, norm; +l_float32 *fa1, *fad, *suma; +NUMA *na1, *nad; + + PROCNAME("numaWindowedMean"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + width = 2 * wc + 1; /* filter width */ + if (width > n) + L_WARNING("filter wider than input array!\n", procName); + + na1 = numaAddSpecifiedBorder(nas, wc, wc, L_MIRRORED_BORDER); + n1 = n + 2 * wc; + fa1 = numaGetFArray(na1, L_NOCOPY); + nad = numaMakeConstant(0, n); + fad = numaGetFArray(nad, L_NOCOPY); + + /* Make sum array; note the indexing */ + if ((suma = (l_float32 *)LEPT_CALLOC(n1 + 1, sizeof(l_float32))) == NULL) { + numaDestroy(&na1); + numaDestroy(&nad); + return (NUMA *)ERROR_PTR("suma not made", procName, NULL); + } + sum = 0.0; + suma[0] = 0.0; + for (i = 0; i < n1; i++) { + sum += fa1[i]; + suma[i + 1] = sum; + } + + norm = 1. / (2 * wc + 1); + for (i = 0; i < n; i++) + fad[i] = norm * (suma[width + i] - suma[i]); + + LEPT_FREE(suma); + numaDestroy(&na1); + return nad; +} + + +/*! + * \brief numaWindowedMeanSquare() + * + * \param[in] nas + * \param[in] wc half width of the window + * \return nad containing windowed mean square values, or NULL on error + * + *
+ * Notes:
+ *      (1) The window has width = 2 * %wc + 1.
+ *      (2) We add a mirrored border of size %wc to each end of the array.
+ * 
+ */ +NUMA * +numaWindowedMeanSquare(NUMA *nas, + l_int32 wc) +{ +l_int32 i, n, n1, width; +l_float32 sum, norm; +l_float32 *fa1, *fad, *suma; +NUMA *na1, *nad; + + PROCNAME("numaWindowedMeanSquare"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + width = 2 * wc + 1; /* filter width */ + if (width > n) + L_WARNING("filter wider than input array!\n", procName); + + na1 = numaAddSpecifiedBorder(nas, wc, wc, L_MIRRORED_BORDER); + n1 = n + 2 * wc; + fa1 = numaGetFArray(na1, L_NOCOPY); + nad = numaMakeConstant(0, n); + fad = numaGetFArray(nad, L_NOCOPY); + + /* Make sum array; note the indexing */ + if ((suma = (l_float32 *)LEPT_CALLOC(n1 + 1, sizeof(l_float32))) == NULL) { + numaDestroy(&na1); + numaDestroy(&nad); + return (NUMA *)ERROR_PTR("suma not made", procName, NULL); + } + sum = 0.0; + suma[0] = 0.0; + for (i = 0; i < n1; i++) { + sum += fa1[i] * fa1[i]; + suma[i + 1] = sum; + } + + norm = 1. / (2 * wc + 1); + for (i = 0; i < n; i++) + fad[i] = norm * (suma[width + i] - suma[i]); + + LEPT_FREE(suma); + numaDestroy(&na1); + return nad; +} + + +/*! + * \brief numaWindowedVariance() + * + * \param[in] nam windowed mean values + * \param[in] nams windowed mean square values + * \param[out] pnav [optional] numa of variance -- the ms deviation + * from the mean + * \param[out] pnarv [optional] numa of rms deviation from the mean + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The numas of windowed mean and mean square are precomputed,
+ *          using numaWindowedMean() and numaWindowedMeanSquare().
+ *      (2) Either or both of the variance and square-root of variance
+ *          are returned, where the variance is the average over the
+ *          window of the mean square difference of the pixel value
+ *          from the mean:
+ *                [(x - [x])*(x - [x])] = [x*x] - [x]*[x]
+ * 
+ */ +l_ok +numaWindowedVariance(NUMA *nam, + NUMA *nams, + NUMA **pnav, + NUMA **pnarv) +{ +l_int32 i, nm, nms; +l_float32 var; +l_float32 *fam, *fams, *fav, *farv; +NUMA *nav, *narv; /* variance and square root of variance */ + + PROCNAME("numaWindowedVariance"); + + if (pnav) *pnav = NULL; + if (pnarv) *pnarv = NULL; + if (!pnav && !pnarv) + return ERROR_INT("neither &nav nor &narv are defined", procName, 1); + if (!nam) + return ERROR_INT("nam not defined", procName, 1); + if (!nams) + return ERROR_INT("nams not defined", procName, 1); + nm = numaGetCount(nam); + nms = numaGetCount(nams); + if (nm != nms) + return ERROR_INT("sizes of nam and nams differ", procName, 1); + + if (pnav) { + nav = numaMakeConstant(0, nm); + *pnav = nav; + fav = numaGetFArray(nav, L_NOCOPY); + } + if (pnarv) { + narv = numaMakeConstant(0, nm); + *pnarv = narv; + farv = numaGetFArray(narv, L_NOCOPY); + } + fam = numaGetFArray(nam, L_NOCOPY); + fams = numaGetFArray(nams, L_NOCOPY); + + for (i = 0; i < nm; i++) { + var = fams[i] - fam[i] * fam[i]; + if (pnav) + fav[i] = var; + if (pnarv) + farv[i] = sqrtf(var); + } + + return 0; +} + + +/*! + * \brief numaWindowedMedian() + * + * \param[in] nas + * \param[in] halfwin half width of window over which the median is found + * \return nad after windowed median filtering, or NULL on error + * + *
+ * Notes:
+ *      (1) The requested window has width = 2 * %halfwin + 1.
+ *      (2) If the input nas has less then 3 elements, return a copy.
+ *      (3) If the filter is too small (%halfwin <= 0), return a copy.
+ *      (4) If the filter is too large, it is reduced in size.
+ *      (5) We add a mirrored border of size %halfwin to each end of
+ *          the array to simplify the calculation by avoiding end-effects.
+ * 
+ */ +NUMA * +numaWindowedMedian(NUMA *nas, + l_int32 halfwin) +{ +l_int32 i, n; +l_float32 medval; +NUMA *na1, *na2, *nad; + + PROCNAME("numaWindowedMedian"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if ((n = numaGetCount(nas)) < 3) + return numaCopy(nas); + if (halfwin <= 0) { + L_ERROR("filter too small; returning a copy\n", procName); + return numaCopy(nas); + } + + if (halfwin > (n - 1) / 2) { + halfwin = (n - 1) / 2; + L_INFO("reducing filter to halfwin = %d\n", procName, halfwin); + } + + /* Add a border to both ends */ + na1 = numaAddSpecifiedBorder(nas, halfwin, halfwin, L_MIRRORED_BORDER); + + /* Get the median value at the center of each window, corresponding + * to locations in the input nas. */ + nad = numaCreate(n); + for (i = 0; i < n; i++) { + na2 = numaClipToInterval(na1, i, i + 2 * halfwin); + numaGetMedian(na2, &medval); + numaAddNumber(nad, medval); + numaDestroy(&na2); + } + + numaDestroy(&na1); + return nad; +} + + +/*! + * \brief numaConvertToInt() + * + * \param[in] nas source numa + * \return na with all values rounded to nearest integer, or + * NULL on error + */ +NUMA * +numaConvertToInt(NUMA *nas) +{ +l_int32 i, n, ival; +NUMA *nad; + + PROCNAME("numaConvertToInt"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + + n = numaGetCount(nas); + if ((nad = numaCreate(n)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + numaCopyParameters(nad, nas); + for (i = 0; i < n; i++) { + numaGetIValue(nas, i, &ival); + numaAddNumber(nad, ival); + } + return nad; +} + + +/*----------------------------------------------------------------------* + * Histogram generation and statistics * + *----------------------------------------------------------------------*/ +/*! + * \brief numaMakeHistogram() + * + * \param[in] na + * \param[in] maxbins max number of histogram bins + * \param[out] pbinsize size of histogram bins + * \param[out] pbinstart [optional] start val of minimum bin; + * input NULL to force start at 0 + * \return na consisiting of histogram of integerized values, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) This simple interface is designed for integer data.
+ *          The bins are of integer width and start on integer boundaries,
+ *          so the results on float data will not have high precision.
+ *      (2) Specify the max number of input bins.   Then %binsize,
+ *          the size of bins necessary to accommodate the input data,
+ *          is returned.  It is one of the sequence:
+ *                {1, 2, 5, 10, 20, 50, ...}.
+ *      (3) If &binstart is given, all values are accommodated,
+ *          and the min value of the starting bin is returned.
+ *          Otherwise, all negative values are discarded and
+ *          the histogram bins start at 0.
+ * 
+ */ +NUMA * +numaMakeHistogram(NUMA *na, + l_int32 maxbins, + l_int32 *pbinsize, + l_int32 *pbinstart) +{ +l_int32 i, n, ival, hval; +l_int32 iminval, imaxval, range, binsize, nbins, ibin; +l_float32 val, ratio; +NUMA *nai, *nahist; + + PROCNAME("numaMakeHistogram"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + if (!pbinsize) + return (NUMA *)ERROR_PTR("&binsize not defined", procName, NULL); + + /* Determine input range */ + numaGetMin(na, &val, NULL); + iminval = (l_int32)(val + 0.5); + numaGetMax(na, &val, NULL); + imaxval = (l_int32)(val + 0.5); + if (pbinstart == NULL) { /* clip negative vals; start from 0 */ + iminval = 0; + if (imaxval < 0) + return (NUMA *)ERROR_PTR("all values < 0", procName, NULL); + } + + /* Determine binsize */ + range = imaxval - iminval + 1; + if (range > maxbins - 1) { + ratio = (l_float64)range / (l_float64)maxbins; + binsize = 0; + for (i = 0; i < NBinSizes; i++) { + if (ratio < BinSizeArray[i]) { + binsize = BinSizeArray[i]; + break; + } + } + if (binsize == 0) + return (NUMA *)ERROR_PTR("numbers too large", procName, NULL); + } else { + binsize = 1; + } + *pbinsize = binsize; + nbins = 1 + range / binsize; /* +1 seems to be sufficient */ + + /* Redetermine iminval */ + if (pbinstart && binsize > 1) { + if (iminval >= 0) + iminval = binsize * (iminval / binsize); + else + iminval = binsize * ((iminval - binsize + 1) / binsize); + } + if (pbinstart) + *pbinstart = iminval; + +#if DEBUG_HISTO + fprintf(stderr, " imaxval = %d, range = %d, nbins = %d\n", + imaxval, range, nbins); +#endif /* DEBUG_HISTO */ + + /* Use integerized data for input */ + if ((nai = numaConvertToInt(na)) == NULL) + return (NUMA *)ERROR_PTR("nai not made", procName, NULL); + n = numaGetCount(nai); + + /* Make histogram, converting value in input array + * into a bin number for this histogram array. */ + if ((nahist = numaCreate(nbins)) == NULL) { + numaDestroy(&nai); + return (NUMA *)ERROR_PTR("nahist not made", procName, NULL); + } + numaSetCount(nahist, nbins); + numaSetParameters(nahist, iminval, binsize); + for (i = 0; i < n; i++) { + numaGetIValue(nai, i, &ival); + ibin = (ival - iminval) / binsize; + if (ibin >= 0 && ibin < nbins) { + numaGetIValue(nahist, ibin, &hval); + numaSetValue(nahist, ibin, hval + 1.0); + } + } + + numaDestroy(&nai); + return nahist; +} + + +/*! + * \brief numaMakeHistogramAuto() + * + * \param[in] na numa of floats; these may be integers + * \param[in] maxbins max number of histogram bins; >= 1 + * \return na consisiting of histogram of quantized float values, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) This simple interface is designed for accurate binning
+ *          of both integer and float data.
+ *      (2) If the array data is integers, and the range of integers
+ *          is smaller than %maxbins, they are binned as they fall,
+ *          with binsize = 1.
+ *      (3) If the range of data, (maxval - minval), is larger than
+ *          %maxbins, or if the data is floats, they are binned into
+ *          exactly %maxbins bins.
+ *      (4) Unlike numaMakeHistogram(), these bins in general have
+ *          non-integer location and width, even for integer data.
+ * 
+ */ +NUMA * +numaMakeHistogramAuto(NUMA *na, + l_int32 maxbins) +{ +l_int32 i, n, imin, imax, irange, ibin, ival, allints; +l_float32 minval, maxval, range, binsize, fval; +NUMA *nah; + + PROCNAME("numaMakeHistogramAuto"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + maxbins = L_MAX(1, maxbins); + + /* Determine input range */ + numaGetMin(na, &minval, NULL); + numaGetMax(na, &maxval, NULL); + + /* Determine if values are all integers */ + n = numaGetCount(na); + numaHasOnlyIntegers(na, maxbins, &allints); + + /* Do simple integer binning if possible */ + if (allints && (maxval - minval < maxbins)) { + imin = (l_int32)minval; + imax = (l_int32)maxval; + irange = imax - imin + 1; + nah = numaCreate(irange); + numaSetCount(nah, irange); /* init */ + numaSetParameters(nah, minval, 1.0); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + ibin = ival - imin; + numaGetIValue(nah, ibin, &ival); + numaSetValue(nah, ibin, ival + 1.0); + } + + return nah; + } + + /* Do float binning, even if the data is integers. */ + range = maxval - minval; + binsize = range / (l_float32)maxbins; + if (range == 0.0) { + nah = numaCreate(1); + numaSetParameters(nah, minval, binsize); + numaAddNumber(nah, n); + return nah; + } + nah = numaCreate(maxbins); + numaSetCount(nah, maxbins); + numaSetParameters(nah, minval, binsize); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &fval); + ibin = (l_int32)((fval - minval) / binsize); + ibin = L_MIN(ibin, maxbins - 1); /* "edge" case; stay in bounds */ + numaGetIValue(nah, ibin, &ival); + numaSetValue(nah, ibin, ival + 1.0); + } + + return nah; +} + + +/*! + * \brief numaMakeHistogramClipped() + * + * \param[in] na + * \param[in] binsize typically 1.0 + * \param[in] maxsize of histogram ordinate + * \return na histogram of bins of size %binsize, starting with + * the na[0] (x = 0.0 and going up to a maximum of + * x = %maxsize, by increments of %binsize), or NULL on error + * + *
+ * Notes:
+ *      (1) This simple function generates a histogram of values
+ *          from na, discarding all values < 0.0 or greater than
+ *          min(%maxsize, maxval), where maxval is the maximum value in na.
+ *          The histogram data is put in bins of size delx = %binsize,
+ *          starting at x = 0.0.  We use as many bins as are
+ *          needed to hold the data.
+ * 
+ */ +NUMA * +numaMakeHistogramClipped(NUMA *na, + l_float32 binsize, + l_float32 maxsize) +{ +l_int32 i, n, nbins, ival, ibin; +l_float32 val, maxval; +NUMA *nad; + + PROCNAME("numaMakeHistogramClipped"); + + if (!na) + return (NUMA *)ERROR_PTR("na not defined", procName, NULL); + if (binsize <= 0.0) + return (NUMA *)ERROR_PTR("binsize must be > 0.0", procName, NULL); + if (binsize > maxsize) + binsize = maxsize; /* just one bin */ + + numaGetMax(na, &maxval, NULL); + n = numaGetCount(na); + maxsize = L_MIN(maxsize, maxval); + nbins = (l_int32)(maxsize / binsize) + 1; + +/* fprintf(stderr, "maxsize = %7.3f, nbins = %d\n", maxsize, nbins); */ + + if ((nad = numaCreate(nbins)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + numaSetParameters(nad, 0.0, binsize); + numaSetCount(nad, nbins); /* interpret zeroes in bins as data */ + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + ibin = (l_int32)(val / binsize); + if (ibin >= 0 && ibin < nbins) { + numaGetIValue(nad, ibin, &ival); + numaSetValue(nad, ibin, ival + 1.0); + } + } + + return nad; +} + + +/*! + * \brief numaRebinHistogram() + * + * \param[in] nas input histogram + * \param[in] newsize number of old bins contained in each new bin + * \return nad more coarsely re-binned histogram, or NULL on error + */ +NUMA * +numaRebinHistogram(NUMA *nas, + l_int32 newsize) +{ +l_int32 i, j, ns, nd, index, count, val; +l_float32 start, oldsize; +NUMA *nad; + + PROCNAME("numaRebinHistogram"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (newsize <= 1) + return (NUMA *)ERROR_PTR("newsize must be > 1", procName, NULL); + if ((ns = numaGetCount(nas)) == 0) + return (NUMA *)ERROR_PTR("no bins in nas", procName, NULL); + + nd = (ns + newsize - 1) / newsize; + if ((nad = numaCreate(nd)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + numaGetParameters(nad, &start, &oldsize); + numaSetParameters(nad, start, oldsize * newsize); + + for (i = 0; i < nd; i++) { /* new bins */ + count = 0; + index = i * newsize; + for (j = 0; j < newsize; j++) { + if (index < ns) { + numaGetIValue(nas, index, &val); + count += val; + index++; + } + } + numaAddNumber(nad, count); + } + + return nad; +} + + +/*! + * \brief numaNormalizeHistogram() + * + * \param[in] nas input histogram + * \param[in] tsum target sum of all numbers in dest histogram; e.g., use + * %tsum= 1.0 if this represents a probability distribution + * \return nad normalized histogram, or NULL on error + */ +NUMA * +numaNormalizeHistogram(NUMA *nas, + l_float32 tsum) +{ +l_int32 i, ns; +l_float32 sum, factor, fval; +NUMA *nad; + + PROCNAME("numaNormalizeHistogram"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (tsum <= 0.0) + return (NUMA *)ERROR_PTR("tsum must be > 0.0", procName, NULL); + if ((ns = numaGetCount(nas)) == 0) + return (NUMA *)ERROR_PTR("no bins in nas", procName, NULL); + + numaGetSum(nas, &sum); + factor = tsum / sum; + + if ((nad = numaCreate(ns)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + numaCopyParameters(nad, nas); + + for (i = 0; i < ns; i++) { + numaGetFValue(nas, i, &fval); + fval *= factor; + numaAddNumber(nad, fval); + } + + return nad; +} + + +/*! + * \brief numaGetStatsUsingHistogram() + * + * \param[in] na an arbitrary set of numbers; not ordered and not + * a histogram + * \param[in] maxbins the maximum number of bins to be allowed in + * the histogram; use an integer larger than the + * largest number in %na for consecutive integer bins + * \param[out] pmin [optional] min value of set + * \param[out] pmax [optional] max value of set + * \param[out] pmean [optional] mean value of set + * \param[out] pvariance [optional] variance + * \param[out] pmedian [optional] median value of set + * \param[in] rank in [0.0 ... 1.0]; median has a rank 0.5; + * ignored if &rval == NULL + * \param[out] prval [optional] value in na corresponding to %rank + * \param[out] phisto [optional] Numa histogram; use NULL to prevent + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a simple interface for gathering statistics
+ *          from a numa, where a histogram is used 'under the covers'
+ *          to avoid sorting if a rank value is requested.  In that case,
+ *          by using a histogram we are trading speed for accuracy, because
+ *          the values in %na are quantized to the center of a set of bins.
+ *      (2) If the median, other rank value, or histogram are not requested,
+ *          the calculation is all performed on the input Numa.
+ *      (3) The variance is the average of the square of the
+ *          difference from the mean.  The median is the value in na
+ *          with rank 0.5.
+ *      (4) There are two situations where this gives rank results with
+ *          accuracy comparable to computing stastics directly on the input
+ *          data, without binning into a histogram:
+ *           (a) the data is integers and the range of data is less than
+ *               %maxbins, and
+ *           (b) the data is floats and the range is small compared to
+ *               %maxbins, so that the binsize is much less than 1.
+ *      (5) If a histogram is used and the numbers in the Numa extend
+ *          over a large range, you can limit the required storage by
+ *          specifying the maximum number of bins in the histogram.
+ *          Use %maxbins == 0 to force the bin size to be 1.
+ *      (6) This optionally returns the median and one arbitrary rank value.
+ *          If you need several rank values, return the histogram and use
+ *               numaHistogramGetValFromRank(nah, rank, &rval)
+ *          multiple times.
+ * 
+ */ +l_ok +numaGetStatsUsingHistogram(NUMA *na, + l_int32 maxbins, + l_float32 *pmin, + l_float32 *pmax, + l_float32 *pmean, + l_float32 *pvariance, + l_float32 *pmedian, + l_float32 rank, + l_float32 *prval, + NUMA **phisto) +{ +l_int32 i, n; +l_float32 minval, maxval, fval, mean, sum; +NUMA *nah; + + PROCNAME("numaGetStatsUsingHistogram"); + + if (pmin) *pmin = 0.0; + if (pmax) *pmax = 0.0; + if (pmean) *pmean = 0.0; + if (pvariance) *pvariance = 0.0; + if (pmedian) *pmedian = 0.0; + if (prval) *prval = 0.0; + if (phisto) *phisto = NULL; + if (!na) + return ERROR_INT("na not defined", procName, 1); + if ((n = numaGetCount(na)) == 0) + return ERROR_INT("numa is empty", procName, 1); + + numaGetMin(na, &minval, NULL); + numaGetMax(na, &maxval, NULL); + if (pmin) *pmin = minval; + if (pmax) *pmax = maxval; + if (pmean || pvariance) { + sum = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &fval); + sum += fval; + } + mean = sum / (l_float32)n; + if (pmean) *pmean = mean; + } + if (pvariance) { + sum = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &fval); + sum += fval * fval; + } + *pvariance = sum / (l_float32)n - mean * mean; + } + + if (!pmedian && !prval && !phisto) + return 0; + + nah = numaMakeHistogramAuto(na, maxbins); + if (pmedian) + numaHistogramGetValFromRank(nah, 0.5, pmedian); + if (prval) + numaHistogramGetValFromRank(nah, rank, prval); + if (phisto) + *phisto = nah; + else + numaDestroy(&nah); + return 0; +} + + +/*! + * \brief numaGetHistogramStats() + * + * \param[in] nahisto histogram: y(x(i)), i = 0 ... nbins - 1 + * \param[in] startx x value of first bin: x(0) + * \param[in] deltax x increment between bins; the bin size; x(1) - x(0) + * \param[out] pxmean [optional] mean value of histogram + * \param[out] pxmedian [optional] median value of histogram + * \param[out] pxmode [optional] mode value of histogram: + * xmode = x(imode), where y(xmode) >= y(x(i)) for + * all i != imode + * \param[out] pxvariance [optional] variance of x + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the histogram represents the relation y(x), the
+ *          computed values that are returned are the x values.
+ *          These are NOT the bucket indices i; they are related to the
+ *          bucket indices by
+ *                x(i) = startx + i * deltax
+ * 
+ */ +l_ok +numaGetHistogramStats(NUMA *nahisto, + l_float32 startx, + l_float32 deltax, + l_float32 *pxmean, + l_float32 *pxmedian, + l_float32 *pxmode, + l_float32 *pxvariance) +{ + PROCNAME("numaGetHistogramStats"); + + if (pxmean) *pxmean = 0.0; + if (pxmedian) *pxmedian = 0.0; + if (pxmode) *pxmode = 0.0; + if (pxvariance) *pxvariance = 0.0; + if (!nahisto) + return ERROR_INT("nahisto not defined", procName, 1); + + return numaGetHistogramStatsOnInterval(nahisto, startx, deltax, 0, -1, + pxmean, pxmedian, pxmode, + pxvariance); +} + + +/*! + * \brief numaGetHistogramStatsOnInterval() + * + * \param[in] nahisto histogram: y(x(i)), i = 0 ... nbins - 1 + * \param[in] startx x value of first bin: x(0) + * \param[in] deltax x increment between bins; the bin size; x(1) - x(0) + * \param[in] ifirst first bin to use for collecting stats + * \param[in] ilast last bin for collecting stats; -1 to go to the end + * \param[out] pxmean [optional] mean value of histogram + * \param[out] pxmedian [optional] median value of histogram + * \param[out] pxmode [optional] mode value of histogram: + * xmode = x(imode), where y(xmode) >= y(x(i)) for + * all i != imode + * \param[out] pxvariance [optional] variance of x + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the histogram represents the relation y(x), the
+ *          computed values that are returned are the x values.
+ *          These are NOT the bucket indices i; they are related to the
+ *          bucket indices by
+ *                x(i) = startx + i * deltax
+ * 
+ */ +l_ok +numaGetHistogramStatsOnInterval(NUMA *nahisto, + l_float32 startx, + l_float32 deltax, + l_int32 ifirst, + l_int32 ilast, + l_float32 *pxmean, + l_float32 *pxmedian, + l_float32 *pxmode, + l_float32 *pxvariance) +{ +l_int32 i, n, imax; +l_float32 sum, sumval, halfsum, moment, var, x, y, ymax; + + PROCNAME("numaGetHistogramStatsOnInterval"); + + if (pxmean) *pxmean = 0.0; + if (pxmedian) *pxmedian = 0.0; + if (pxmode) *pxmode = 0.0; + if (pxvariance) *pxvariance = 0.0; + if (!nahisto) + return ERROR_INT("nahisto not defined", procName, 1); + if (!pxmean && !pxmedian && !pxmode && !pxvariance) + return ERROR_INT("nothing to compute", procName, 1); + + n = numaGetCount(nahisto); + ifirst = L_MAX(0, ifirst); + if (ilast < 0) ilast = n - 1; + if (ifirst >= n) + return ERROR_INT("invalid ifirst", procName, 1); + if (ilast >= n) { + L_WARNING("ilast = %d is beyond max index = %d; adjusting\n", + procName, ilast, n - 1); + ilast = n - 1; + } + if (ifirst > ilast) + return ERROR_INT("ifirst > ilast", procName, 1); + for (sum = 0.0, moment = 0.0, var = 0.0, i = ifirst; i <= ilast ; i++) { + x = startx + i * deltax; + numaGetFValue(nahisto, i, &y); + sum += y; + moment += x * y; + var += x * x * y; + } + if (sum == 0.0) { + L_INFO("sum is 0\n", procName); + return 0; + } + + if (pxmean) + *pxmean = moment / sum; + if (pxvariance) + *pxvariance = var / sum - moment * moment / (sum * sum); + + if (pxmedian) { + halfsum = sum / 2.0; + for (sumval = 0.0, i = ifirst; i <= ilast; i++) { + numaGetFValue(nahisto, i, &y); + sumval += y; + if (sumval >= halfsum) { + *pxmedian = startx + i * deltax; + break; + } + } + } + + if (pxmode) { + imax = -1; + ymax = -1.0e10; + for (i = ifirst; i <= ilast; i++) { + numaGetFValue(nahisto, i, &y); + if (y > ymax) { + ymax = y; + imax = i; + } + } + *pxmode = startx + imax * deltax; + } + + return 0; +} + + +/*! + * \brief numaMakeRankFromHistogram() + * + * \param[in] startx xval corresponding to first element in nay + * \param[in] deltax x increment between array elements in nay + * \param[in] nasy input histogram, assumed equally spaced + * \param[in] npts number of points to evaluate rank function + * \param[out] pnax [optional] array of x values in range + * \param[out] pnay rank array of specified npts + * \return 0 if OK, 1 on error + */ +l_ok +numaMakeRankFromHistogram(l_float32 startx, + l_float32 deltax, + NUMA *nasy, + l_int32 npts, + NUMA **pnax, + NUMA **pnay) +{ +l_int32 i, n; +l_float32 sum, fval; +NUMA *nan, *nar; + + PROCNAME("numaMakeRankFromHistogram"); + + if (pnax) *pnax = NULL; + if (!pnay) + return ERROR_INT("&nay not defined", procName, 1); + *pnay = NULL; + if (!nasy) + return ERROR_INT("nasy not defined", procName, 1); + if ((n = numaGetCount(nasy)) == 0) + return ERROR_INT("no bins in nas", procName, 1); + + /* Normalize and generate the rank array corresponding to + * the binned histogram. */ + nan = numaNormalizeHistogram(nasy, 1.0); + nar = numaCreate(n + 1); /* rank numa corresponding to nan */ + sum = 0.0; + numaAddNumber(nar, sum); /* first element is 0.0 */ + for (i = 0; i < n; i++) { + numaGetFValue(nan, i, &fval); + sum += fval; + numaAddNumber(nar, sum); + } + + /* Compute rank array on full range with specified + * number of points and correspondence to x-values. */ + numaInterpolateEqxInterval(startx, deltax, nar, L_LINEAR_INTERP, + startx, startx + n * deltax, npts, + pnax, pnay); + numaDestroy(&nan); + numaDestroy(&nar); + return 0; +} + + +/*! + * \brief numaHistogramGetRankFromVal() + * + * \param[in] na histogram + * \param[in] rval value of input sample for which we want the rank + * \param[out] prank fraction of total samples below rval + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If we think of the histogram as a function y(x), normalized
+ *          to 1, for a given input value of x, this computes the
+ *          rank of x, which is the integral of y(x) from the start
+ *          value of x to the input value.
+ *      (2) This function only makes sense when applied to a Numa that
+ *          is a histogram.  The values in the histogram can be ints and
+ *          floats, and are computed as floats.  The rank is returned
+ *          as a float between 0.0 and 1.0.
+ *      (3) The numa parameters startx and binsize are used to
+ *          compute x from the Numa index i.
+ * 
+ */ +l_ok +numaHistogramGetRankFromVal(NUMA *na, + l_float32 rval, + l_float32 *prank) +{ +l_int32 i, ibinval, n; +l_float32 startval, binsize, binval, maxval, fractval, total, sum, val; + + PROCNAME("numaHistogramGetRankFromVal"); + + if (!prank) + return ERROR_INT("prank not defined", procName, 1); + *prank = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + numaGetParameters(na, &startval, &binsize); + n = numaGetCount(na); + if (rval < startval) + return 0; + maxval = startval + n * binsize; + if (rval > maxval) { + *prank = 1.0; + return 0; + } + + binval = (rval - startval) / binsize; + ibinval = (l_int32)binval; + if (ibinval >= n) { + *prank = 1.0; + return 0; + } + fractval = binval - (l_float32)ibinval; + + sum = 0.0; + for (i = 0; i < ibinval; i++) { + numaGetFValue(na, i, &val); + sum += val; + } + numaGetFValue(na, ibinval, &val); + sum += fractval * val; + numaGetSum(na, &total); + *prank = sum / total; + +/* fprintf(stderr, "binval = %7.3f, rank = %7.3f\n", binval, *prank); */ + + return 0; +} + + +/*! + * \brief numaHistogramGetValFromRank() + * + * \param[in] na histogram + * \param[in] rank fraction of total samples + * \param[out] prval approx. to the bin value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If we think of the histogram as a function y(x), this returns
+ *          the value x such that the integral of y(x) from the start
+ *          value to x gives the fraction 'rank' of the integral
+ *          of y(x) over all bins.
+ *      (2) This function only makes sense when applied to a Numa that
+ *          is a histogram.  The values in the histogram can be ints and
+ *          floats, and are computed as floats.  The val is returned
+ *          as a float, even though the buckets are of integer width.
+ *      (3) The numa parameters startx and binsize are used to
+ *          compute x from the Numa index i.
+ * 
+ */ +l_ok +numaHistogramGetValFromRank(NUMA *na, + l_float32 rank, + l_float32 *prval) +{ +l_int32 i, n; +l_float32 startval, binsize, rankcount, total, sum, fract, val; + + PROCNAME("numaHistogramGetValFromRank"); + + if (!prval) + return ERROR_INT("prval not defined", procName, 1); + *prval = 0.0; + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (rank < 0.0) { + L_WARNING("rank < 0; setting to 0.0\n", procName); + rank = 0.0; + } + if (rank > 1.0) { + L_WARNING("rank > 1.0; setting to 1.0\n", procName); + rank = 1.0; + } + + n = numaGetCount(na); + numaGetParameters(na, &startval, &binsize); + numaGetSum(na, &total); + rankcount = rank * total; /* count that corresponds to rank */ + sum = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + if (sum + val >= rankcount) + break; + sum += val; + } + if (val <= 0.0) /* can == 0 if rank == 0.0 */ + fract = 0.0; + else /* sum + fract * val = rankcount */ + fract = (rankcount - sum) / val; + + /* The use of the fraction of a bin allows a simple calculation + * for the histogram value at the given rank. */ + *prval = startval + binsize * ((l_float32)i + fract); + +/* fprintf(stderr, "rank = %7.3f, val = %7.3f\n", rank, *prval); */ + + return 0; +} + + +/*! + * \brief numaDiscretizeRankAndIntensity() + * + * \param[in] na normalized histo of probability density vs intensity + * \param[in] nbins number of bins at which the rank is divided + * \param[out] pnarbin [optional] rank bin value vs intensity + * \param[out] pnam [optional] median intensity in a bin vs rank bin + * value, with %nbins of discretized rank values + * \param[out] pnar [optional] rank vs intensity; this is + * a cumulative norm histogram + * \param[out] pnabb [optional] intensity at the right bin boundary + * vs rank bin + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We are inverting the rank(intensity) function to get
+ *          the intensity(rank) function at %nbins equally spaced
+ *          values of rank between 0.0 and 1.0.  We save integer values
+ *          for the intensity.
+ *      (2) We are using the word "intensity" to describe the type of
+ *          array values, but any array of non-negative numbers will work.
+ *      (3) The output arrays give the following mappings, where the
+ *          input is a normalized histogram of array values:
+ *             array values     -->  rank bin number  (narbin)
+ *             rank bin number  -->  median array value in bin (nam)
+ *             array values     -->  cumulative norm = rank  (nar)
+ *             rank bin number  -->  array value at right bin edge (nabb)
+ * 
+ */ +l_ok +numaDiscretizeRankAndIntensity(NUMA *na, + l_int32 nbins, + NUMA **pnarbin, + NUMA **pnam, + NUMA **pnar, + NUMA **pnabb) +{ +NUMA *nar; /* rank value as function of intensity */ +NUMA *nam; /* median intensity in the rank bins */ +NUMA *nabb; /* rank bin right boundaries (in intensity) */ +NUMA *narbin; /* binned rank value as a function of intensity */ +l_int32 i, j, npts, start, midfound, mcount, rightedge; +l_float32 sum, midrank, endrank, val; + + PROCNAME("numaDiscretizeRankAndIntensity"); + + if (pnarbin) *pnarbin = NULL; + if (pnam) *pnam = NULL; + if (pnar) *pnar = NULL; + if (pnabb) *pnabb = NULL; + if (!pnarbin && !pnam && !pnar && !pnabb) + return ERROR_INT("no output requested", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (nbins < 2) + return ERROR_INT("nbins must be > 1", procName, 1); + + /* Get cumulative normalized histogram (rank vs intensity value). + * For a normalized histogram from an 8 bpp grayscale image + * as input, we have 256 bins and 257 points in the + * cumulative (rank) histogram. */ + npts = numaGetCount(na); + if ((nar = numaCreate(npts + 1)) == NULL) + return ERROR_INT("nar not made", procName, 1); + sum = 0.0; + numaAddNumber(nar, sum); /* left side of first bin */ + for (i = 0; i < npts; i++) { + numaGetFValue(na, i, &val); + sum += val; + numaAddNumber(nar, sum); + } + + nam = numaCreate(nbins); + narbin = numaCreate(npts); + nabb = numaCreate(nbins); + if (!nam || !narbin || !nabb) { + numaDestroy(&nar); + numaDestroy(&nam); + numaDestroy(&narbin); + numaDestroy(&nabb); + return ERROR_INT("numa not made", procName, 1); + } + + /* We find the intensity value at the right edge of each of + * the rank bins. We also find the median intensity in the bin, + * where approximately half the samples are lower and half are + * higher. This can be considered as a simple approximation + * for the average intensity in the bin. */ + start = 0; /* index in nar */ + mcount = 0; /* count of median values in rank bins; not to exceed nbins */ + for (i = 0; i < nbins; i++) { + midrank = (l_float32)(i + 0.5) / (l_float32)(nbins); + endrank = (l_float32)(i + 1.0) / (l_float32)(nbins); + endrank = L_MAX(0.0, L_MIN(endrank - 0.001, 1.0)); + midfound = FALSE; + for (j = start; j < npts; j++) { /* scan up for each bin value */ + numaGetFValue(nar, j, &val); + /* Use (j == npts - 1) tests in case all weight is at top end */ + if ((!midfound && val >= midrank) || + (mcount < nbins && j == npts - 1)) { + midfound = TRUE; + numaAddNumber(nam, j); + mcount++; + } + if ((val >= endrank) || (j == npts - 1)) { + numaAddNumber(nabb, j); + if (val == endrank) + start = j; + else + start = j - 1; + break; + } + } + } + numaSetValue(nabb, nbins - 1, npts - 1); /* extend to max */ + + /* Error checking: did we get data in all bins? */ + if (mcount != nbins) + L_WARNING("found data for %d bins; should be %d\n", + procName, mcount, nbins); + + /* Generate LUT that maps from intensity to bin number */ + start = 0; + for (i = 0; i < nbins; i++) { + numaGetIValue(nabb, i, &rightedge); + for (j = start; j < npts; j++) { + if (j <= rightedge) + numaAddNumber(narbin, i); + if (j > rightedge) { + start = j; + break; + } + if (j == npts - 1) { /* we're done */ + start = j + 1; + break; + } + } + } + + if (pnarbin) + *pnarbin = narbin; + else + numaDestroy(&narbin); + if (pnam) + *pnam = nam; + else + numaDestroy(&nam); + if (pnar) + *pnar = nar; + else + numaDestroy(&nar); + if (pnabb) + *pnabb = nabb; + else + numaDestroy(&nabb); + return 0; +} + + +/*! + * \brief numaGetRankBinValues() + * + * \param[in] na an array of values + * \param[in] nbins number of bins at which the rank is divided + * \param[out] pnarbin [optional] rank bin value vs array value + * \param[out] pnam [optional] median intensity in a bin vs rank bin + * value, with %nbins of discretized rank values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simple interface for getting a binned rank representation
+ *          of an input array of values.  This returns two mappings:
+ *             array value     -->  rank bin number  (narbin)
+ *             rank bin number -->  median array value in each rank bin (nam)
+ * 
+ */ +l_ok +numaGetRankBinValues(NUMA *na, + l_int32 nbins, + NUMA **pnarbin, + NUMA **pnam) +{ +NUMA *nah, *nan; /* histo and normalized histo */ +l_int32 maxbins, discardval; +l_float32 maxval, delx; + + PROCNAME("numaGetRankBinValues"); + + if (pnarbin) *pnarbin = NULL; + if (pnam) *pnam = NULL; + if (!pnarbin && !pnam) + return ERROR_INT("no output requested", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + if (numaGetCount(na) == 0) + return ERROR_INT("na is empty", procName, 1); + if (nbins < 2) + return ERROR_INT("nbins must be > 1", procName, 1); + + /* Get normalized histogram */ + numaGetMax(na, &maxval, NULL); + maxbins = L_MIN(100002, (l_int32)maxval + 2); + nah = numaMakeHistogram(na, maxbins, &discardval, NULL); + nan = numaNormalizeHistogram(nah, 1.0); + + /* Warn if there is a scale change. This shouldn't happen + * unless the max value is above 100000. */ + numaGetParameters(nan, NULL, &delx); + if (delx > 1.0) + L_WARNING("scale change: delx = %6.2f\n", procName, delx); + + /* Rank bin the results */ + numaDiscretizeRankAndIntensity(nan, nbins, pnarbin, pnam, NULL, NULL); + numaDestroy(&nah); + numaDestroy(&nan); + return 0; +} + + +/*----------------------------------------------------------------------* + * Splitting a distribution * + *----------------------------------------------------------------------*/ +/*! + * \brief numaSplitDistribution() + * + * \param[in] na histogram + * \param[in] scorefract fraction of the max score, used to determine + * range over which the histogram min is searched + * \param[out] psplitindex [optional] index for splitting + * \param[out] pave1 [optional] average of lower distribution + * \param[out] pave2 [optional] average of upper distribution + * \param[out] pnum1 [optional] population of lower distribution + * \param[out] pnum2 [optional] population of upper distribution + * \param[out] pnascore [optional] for debugging; otherwise use NULL + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function is intended to be used on a distribution of
+ *          values that represent two sets, such as a histogram of
+ *          pixel values for an image with a fg and bg, and the goal
+ *          is to determine the averages of the two sets and the
+ *          best splitting point.
+ *      (2) The Otsu method finds a split point that divides the distribution
+ *          into two parts by maximizing a score function that is the
+ *          product of two terms:
+ *            (a) the square of the difference of centroids, (ave1 - ave2)^2
+ *            (b) fract1 * (1 - fract1)
+ *          where fract1 is the fraction in the lower distribution.
+ *      (3) This works well for images where the fg and bg are
+ *          each relatively homogeneous and well-separated in color.
+ *          However, if the actual fg and bg sets are very different
+ *          in size, and the bg is highly varied, as can occur in some
+ *          scanned document images, this will bias the split point
+ *          into the larger "bump" (i.e., toward the point where the
+ *          (b) term reaches its maximum of 0.25 at fract1 = 0.5.
+ *          To avoid this, we define a range of values near the
+ *          maximum of the score function, and choose the value within
+ *          this range such that the histogram itself has a minimum value.
+ *          The range is determined by scorefract: we include all abscissa
+ *          values to the left and right of the value that maximizes the
+ *          score, such that the score stays above (1 - scorefract) * maxscore.
+ *          The intuition behind this modification is to try to find
+ *          a split point that both has a high variance score and is
+ *          at or near a minimum in the histogram, so that the histogram
+ *          slope is small at the split point.
+ *      (4) We normalize the score so that if the two distributions
+ *          were of equal size and at opposite ends of the numa, the
+ *          score would be 1.0.
+ * 
+ */ +l_ok +numaSplitDistribution(NUMA *na, + l_float32 scorefract, + l_int32 *psplitindex, + l_float32 *pave1, + l_float32 *pave2, + l_float32 *pnum1, + l_float32 *pnum2, + NUMA **pnascore) +{ +l_int32 i, n, bestsplit, minrange, maxrange, maxindex; +l_float32 ave1, ave2, ave1prev, ave2prev; +l_float32 num1, num2, num1prev, num2prev; +l_float32 val, minval, sum, fract1; +l_float32 norm, score, minscore, maxscore; +NUMA *nascore, *naave1, *naave2, *nanum1, *nanum2; + + PROCNAME("numaSplitDistribution"); + + if (psplitindex) *psplitindex = 0; + if (pave1) *pave1 = 0.0; + if (pave2) *pave2 = 0.0; + if (pnum1) *pnum1 = 0.0; + if (pnum2) *pnum2 = 0.0; + if (pnascore) *pnascore = NULL; + if (!na) + return ERROR_INT("na not defined", procName, 1); + + n = numaGetCount(na); + if (n <= 1) + return ERROR_INT("n = 1 in histogram", procName, 1); + numaGetSum(na, &sum); + if (sum <= 0.0) + return ERROR_INT("sum <= 0.0", procName, 1); + norm = 4.0 / ((l_float32)(n - 1) * (n - 1)); + ave1prev = 0.0; + numaGetHistogramStats(na, 0.0, 1.0, &ave2prev, NULL, NULL, NULL); + num1prev = 0.0; + num2prev = sum; + maxindex = n / 2; /* initialize with something */ + + /* Split the histogram with [0 ... i] in the lower part + * and [i+1 ... n-1] in upper part. First, compute an otsu + * score for each possible splitting. */ + if ((nascore = numaCreate(n)) == NULL) + return ERROR_INT("nascore not made", procName, 1); + naave1 = (pave1) ? numaCreate(n) : NULL; + naave2 = (pave2) ? numaCreate(n) : NULL; + nanum1 = (pnum1) ? numaCreate(n) : NULL; + nanum2 = (pnum2) ? numaCreate(n) : NULL; + maxscore = 0.0; + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + num1 = num1prev + val; + if (num1 == 0) + ave1 = ave1prev; + else + ave1 = (num1prev * ave1prev + i * val) / num1; + num2 = num2prev - val; + if (num2 == 0) + ave2 = ave2prev; + else + ave2 = (num2prev * ave2prev - i * val) / num2; + fract1 = num1 / sum; + score = norm * (fract1 * (1 - fract1)) * (ave2 - ave1) * (ave2 - ave1); + numaAddNumber(nascore, score); + if (pave1) numaAddNumber(naave1, ave1); + if (pave2) numaAddNumber(naave2, ave2); + if (pnum1) numaAddNumber(nanum1, num1); + if (pnum2) numaAddNumber(nanum2, num2); + if (score > maxscore) { + maxscore = score; + maxindex = i; + } + num1prev = num1; + num2prev = num2; + ave1prev = ave1; + ave2prev = ave2; + } + + /* Next, for all contiguous scores within a specified fraction + * of the max, choose the split point as the value with the + * minimum in the histogram. */ + minscore = (1. - scorefract) * maxscore; + for (i = maxindex - 1; i >= 0; i--) { + numaGetFValue(nascore, i, &val); + if (val < minscore) + break; + } + minrange = i + 1; + for (i = maxindex + 1; i < n; i++) { + numaGetFValue(nascore, i, &val); + if (val < minscore) + break; + } + maxrange = i - 1; + numaGetFValue(na, minrange, &minval); + bestsplit = minrange; + for (i = minrange + 1; i <= maxrange; i++) { + numaGetFValue(na, i, &val); + if (val < minval) { + minval = val; + bestsplit = i; + } + } + + /* Add one to the bestsplit value to get the threshold value, + * because when we take a threshold, as in pixThresholdToBinary(), + * we always choose the set with values below the threshold. */ + bestsplit = L_MIN(255, bestsplit + 1); + + if (psplitindex) *psplitindex = bestsplit; + if (pave1) numaGetFValue(naave1, bestsplit, pave1); + if (pave2) numaGetFValue(naave2, bestsplit, pave2); + if (pnum1) numaGetFValue(nanum1, bestsplit, pnum1); + if (pnum2) numaGetFValue(nanum2, bestsplit, pnum2); + + if (pnascore) { /* debug mode */ + fprintf(stderr, "minrange = %d, maxrange = %d\n", minrange, maxrange); + fprintf(stderr, "minval = %10.0f\n", minval); + gplotSimple1(nascore, GPLOT_PNG, "/tmp/lept/nascore", + "Score for split distribution"); + *pnascore = nascore; + } else { + numaDestroy(&nascore); + } + + if (pave1) numaDestroy(&naave1); + if (pave2) numaDestroy(&naave2); + if (pnum1) numaDestroy(&nanum1); + if (pnum2) numaDestroy(&nanum2); + return 0; +} + + +/*----------------------------------------------------------------------* + * Comparing histograms * + *----------------------------------------------------------------------*/ +/*! + * \brief grayHistogramsToEMD() + * + * \param[in] naa1, naa2 two numaa, each with one or more 256-element + * histograms + * \param[out] pnad nad of EM distances for each histogram + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) The two numaas must be the same size and have corresponding
+ *         256-element histograms.  Pairs do not need to be normalized
+ *         to the same sum.
+ *     (2) This is typically used on two sets of histograms from
+ *         corresponding tiles of two images.  The similarity of two
+ *         images can be found with the scoring function used in
+ *         pixCompareGrayByHisto():
+ *             score S = 1.0 - k * D, where
+ *                 k is a constant, say in the range 5-10
+ *                 D = EMD
+ *             for each tile; for multiple tiles, take the Min(S) over
+ *             the set of tiles to be the final score.
+ * 
+ */ +l_ok +grayHistogramsToEMD(NUMAA *naa1, + NUMAA *naa2, + NUMA **pnad) +{ +l_int32 i, n, nt; +l_float32 dist; +NUMA *na1, *na2, *nad; + + PROCNAME("grayHistogramsToEMD"); + + if (!pnad) + return ERROR_INT("&nad not defined", procName, 1); + *pnad = NULL; + if (!naa1 || !naa2) + return ERROR_INT("na1 and na2 not both defined", procName, 1); + n = numaaGetCount(naa1); + if (n != numaaGetCount(naa2)) + return ERROR_INT("naa1 and naa2 numa counts differ", procName, 1); + nt = numaaGetNumberCount(naa1); + if (nt != numaaGetNumberCount(naa2)) + return ERROR_INT("naa1 and naa2 number counts differ", procName, 1); + if (256 * n != nt) /* good enough check */ + return ERROR_INT("na sizes must be 256", procName, 1); + + nad = numaCreate(n); + *pnad = nad; + for (i = 0; i < n; i++) { + na1 = numaaGetNuma(naa1, i, L_CLONE); + na2 = numaaGetNuma(naa2, i, L_CLONE); + numaEarthMoverDistance(na1, na2, &dist); + numaAddNumber(nad, dist / 255.); /* normalize to [0.0 - 1.0] */ + numaDestroy(&na1); + numaDestroy(&na2); + } + return 0; +} + + +/*! + * \brief numaEarthMoverDistance() + * + * \param[in] na1, na2 two numas of the same size, typically histograms + * \param[out] pdist earthmover distance + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) The two numas must have the same size.  They do not need to be
+ *         normalized to the same sum before applying the function.
+ *     (2) For a 1D discrete function, the implementation of the EMD
+ *         is trivial.  Just keep filling or emptying buckets in one numa
+ *         to match the amount in the other, moving sequentially along
+ *         both arrays.
+ *     (3) We divide the sum of the absolute value of everything moved
+ *         (by 1 unit at a time) by the sum of the numa (amount of "earth")
+ *         to get the average distance that the "earth" was moved.
+ *         This is the value returned here.
+ *     (4) The caller can do a further normalization, by the number of
+ *         buckets (minus 1), to get the EM distance as a fraction of
+ *         the maximum possible distance, which is n-1.  This fraction
+ *         is 1.0 for the situation where all the 'earth' in the first
+ *         array is at one end, and all in the second array is at the
+ *         other end.
+ * 
+ */ +l_ok +numaEarthMoverDistance(NUMA *na1, + NUMA *na2, + l_float32 *pdist) +{ +l_int32 n, norm, i; +l_float32 sum1, sum2, diff, total; +l_float32 *array1, *array3; +NUMA *na3; + + PROCNAME("numaEarthMoverDistance"); + + if (!pdist) + return ERROR_INT("&dist not defined", procName, 1); + *pdist = 0.0; + if (!na1 || !na2) + return ERROR_INT("na1 and na2 not both defined", procName, 1); + n = numaGetCount(na1); + if (n != numaGetCount(na2)) + return ERROR_INT("na1 and na2 have different size", procName, 1); + + /* Generate na3; normalize to na1 if necessary */ + numaGetSum(na1, &sum1); + numaGetSum(na2, &sum2); + norm = (L_ABS(sum1 - sum2) < 0.00001 * L_ABS(sum1)) ? 1 : 0; + if (!norm) + na3 = numaTransform(na2, 0, sum1 / sum2); + else + na3 = numaCopy(na2); + array1 = numaGetFArray(na1, L_NOCOPY); + array3 = numaGetFArray(na3, L_NOCOPY); + + /* Move earth in n3 from array elements, to match n1 */ + total = 0; + for (i = 1; i < n; i++) { + diff = array1[i - 1] - array3[i - 1]; + array3[i] -= diff; + total += L_ABS(diff); + } + *pdist = total / sum1; + + numaDestroy(&na3); + return 0; +} + + +/*! + * \brief grayInterHistogramStats() + * + * \param[in] naa numaa with two or more 256-element histograms + * \param[in] wc half-width of the smoothing window + * \param[out] pnam [optional] mean values + * \param[out] pnams [optional] mean square values + * \param[out] pnav [optional] variances + * \param[out] pnarv [optional] rms deviations from the mean + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) The %naa has two or more 256-element numa histograms, which
+ *         are to be compared value-wise at each of the 256 gray levels.
+ *         The result are stats (mean, mean square, variance, root variance)
+ *         aggregated across the set of histograms, and each is output
+ *         as a 256 entry numa.  Think of these histograms as a matrix,
+ *         where each histogram is one row of the array.  The stats are
+ *         then aggregated column-wise, between the histograms.
+ *     (2) These stats are:
+ *            ~ average value:   (nam)
+ *            ~ average squared value:  (nams)
+ *            ~ variance: <(v - )*(v - )> =  - *  (nav)
+ *            ~ square-root of variance: (narv)
+ *         where the brackets < .. > indicate that the average value is
+ *         to be taken over each column of the array.
+ *     (3) The input histograms are optionally smoothed before these
+ *         statistical operations.
+ *     (4) The input histograms are normalized to a sum of 10000.  By
+ *         doing this, the resulting numbers are independent of the
+ *         number of samples used in building the individual histograms.
+ *     (5) A typical application is on a set of histograms from tiles
+ *         of an image, to distinguish between text/tables and photo
+ *         regions.  If the tiles are much larger than the text line
+ *         spacing, text/table regions typically have smaller variance
+ *         across tiles than photo regions.  For this application, it
+ *         may be useful to ignore values near white, which are large for
+ *         text and would magnify the variance due to variations in
+ *         illumination.  However, because the variance of a drawing or
+ *         a light photo can be similar to that of grayscale text, this
+ *         function is only a discriminator between darker photos/drawings
+ *         and light photos/text/line-graphics.
+ * 
+ */ +l_ok +grayInterHistogramStats(NUMAA *naa, + l_int32 wc, + NUMA **pnam, + NUMA **pnams, + NUMA **pnav, + NUMA **pnarv) +{ +l_int32 i, j, n, nn; +l_float32 **arrays; +l_float32 mean, var, rvar; +NUMA *na1, *na2, *na3, *na4; + + PROCNAME("grayInterHistogramStats"); + + if (pnam) *pnam = NULL; + if (pnams) *pnams = NULL; + if (pnav) *pnav = NULL; + if (pnarv) *pnarv = NULL; + if (!pnam && !pnams && !pnav && !pnarv) + return ERROR_INT("nothing requested", procName, 1); + if (!naa) + return ERROR_INT("naa not defined", procName, 1); + n = numaaGetCount(naa); + for (i = 0; i < n; i++) { + nn = numaaGetNumaCount(naa, i); + if (nn != 256) { + L_ERROR("%d numbers in numa[%d]\n", procName, nn, i); + return 1; + } + } + + if (pnam) *pnam = numaCreate(256); + if (pnams) *pnams = numaCreate(256); + if (pnav) *pnav = numaCreate(256); + if (pnarv) *pnarv = numaCreate(256); + + /* First, use mean smoothing, normalize each histogram, + * and save all results in a 2D matrix. */ + arrays = (l_float32 **)LEPT_CALLOC(n, sizeof(l_float32 *)); + for (i = 0; i < n; i++) { + na1 = numaaGetNuma(naa, i, L_CLONE); + na2 = numaWindowedMean(na1, wc); + na3 = numaNormalizeHistogram(na2, 10000.); + arrays[i] = numaGetFArray(na3, L_COPY); + numaDestroy(&na1); + numaDestroy(&na2); + numaDestroy(&na3); + } + + /* Get stats between histograms */ + for (j = 0; j < 256; j++) { + na4 = numaCreate(n); + for (i = 0; i < n; i++) { + numaAddNumber(na4, arrays[i][j]); + } + numaSimpleStats(na4, 0, -1, &mean, &var, &rvar); + if (pnam) numaAddNumber(*pnam, mean); + if (pnams) numaAddNumber(*pnams, mean * mean); + if (pnav) numaAddNumber(*pnav, var); + if (pnarv) numaAddNumber(*pnarv, rvar); + numaDestroy(&na4); + } + + for (i = 0; i < n; i++) + LEPT_FREE(arrays[i]); + LEPT_FREE(arrays); + return 0; +} + + +/*----------------------------------------------------------------------* + * Extrema finding * + *----------------------------------------------------------------------*/ +/*! + * \brief numaFindPeaks() + * + * \param[in] nas source numa + * \param[in] nmax max number of peaks to be found + * \param[in] fract1 min fraction of peak value + * \param[in] fract2 min slope + * \return peak na, or NULL on error. + * + *
+ * Notes:
+ *     (1) The returned na consists of sets of four numbers representing
+ *         the peak, in the following order:
+ *            left edge; peak center; right edge; normalized peak area
+ * 
+ */ +NUMA * +numaFindPeaks(NUMA *nas, + l_int32 nmax, + l_float32 fract1, + l_float32 fract2) +{ +l_int32 i, k, n, maxloc, lloc, rloc; +l_float32 fmaxval, sum, total, newtotal, val, lastval; +l_float32 peakfract; +NUMA *na, *napeak; + + PROCNAME("numaFindPeaks"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + numaGetSum(nas, &total); + + /* We munge this copy */ + if ((na = numaCopy(nas)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + if ((napeak = numaCreate(4 * nmax)) == NULL) { + numaDestroy(&na); + return (NUMA *)ERROR_PTR("napeak not made", procName, NULL); + } + + for (k = 0; k < nmax; k++) { + numaGetSum(na, &newtotal); + if (newtotal == 0.0) /* sanity check */ + break; + numaGetMax(na, &fmaxval, &maxloc); + sum = fmaxval; + lastval = fmaxval; + lloc = 0; + for (i = maxloc - 1; i >= 0; --i) { + numaGetFValue(na, i, &val); + if (val == 0.0) { + lloc = i + 1; + break; + } + if (val > fract1 * fmaxval) { + sum += val; + lastval = val; + continue; + } + if (lastval - val > fract2 * lastval) { + sum += val; + lastval = val; + continue; + } + lloc = i; + break; + } + lastval = fmaxval; + rloc = n - 1; + for (i = maxloc + 1; i < n; ++i) { + numaGetFValue(na, i, &val); + if (val == 0.0) { + rloc = i - 1; + break; + } + if (val > fract1 * fmaxval) { + sum += val; + lastval = val; + continue; + } + if (lastval - val > fract2 * lastval) { + sum += val; + lastval = val; + continue; + } + rloc = i; + break; + } + peakfract = sum / total; + numaAddNumber(napeak, lloc); + numaAddNumber(napeak, maxloc); + numaAddNumber(napeak, rloc); + numaAddNumber(napeak, peakfract); + + for (i = lloc; i <= rloc; i++) + numaSetValue(na, i, 0.0); + } + + numaDestroy(&na); + return napeak; +} + + +/*! + * \brief numaFindExtrema() + * + * \param[in] nas input values + * \param[in] delta relative amount to resolve peaks and valleys + * \param[out] pnav [optional] values of extrema + * \return nad (locations of extrema, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a sequence of extrema (peaks and valleys).
+ *      (2) The algorithm is analogous to that for determining
+ *          mountain peaks.  Suppose we have a local peak, with
+ *          bumps on the side.  Under what conditions can we consider
+ *          those 'bumps' to be actual peaks?  The answer: if the
+ *          bump is separated from the peak by a saddle that is at
+ *          least 500 feet below the bump.
+ *      (3) Operationally, suppose we are looking for a peak.
+ *          We are keeping the largest value we've seen since the
+ *          last valley, and are looking for a value that is delta
+ *          BELOW our current peak.  When we find such a value,
+ *          we label the peak, use the current value to label the
+ *          valley, and then do the same operation in reverse (looking
+ *          for a valley).
+ * 
+ */ +NUMA * +numaFindExtrema(NUMA *nas, + l_float32 delta, + NUMA **pnav) +{ +l_int32 i, n, found, loc, direction; +l_float32 startval, val, maxval, minval; +NUMA *nav, *nad; + + PROCNAME("numaFindExtrema"); + + if (pnav) *pnav = NULL; + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (delta < 0.0) + return (NUMA *)ERROR_PTR("delta < 0", procName, NULL); + + n = numaGetCount(nas); + nad = numaCreate(0); + nav = NULL; + if (pnav) { + nav = numaCreate(0); + *pnav = nav; + } + + /* We don't know if we'll find a peak or valley first, + * but use the first element of nas as the reference point. + * Break when we deviate by 'delta' from the first point. */ + numaGetFValue(nas, 0, &startval); + found = FALSE; + for (i = 1; i < n; i++) { + numaGetFValue(nas, i, &val); + if (L_ABS(val - startval) >= delta) { + found = TRUE; + break; + } + } + + if (!found) + return nad; /* it's empty */ + + /* Are we looking for a peak or a valley? */ + if (val > startval) { /* peak */ + direction = 1; + maxval = val; + } else { + direction = -1; + minval = val; + } + loc = i; + + /* Sweep through the rest of the array, recording alternating + * peak/valley extrema. */ + for (i = i + 1; i < n; i++) { + numaGetFValue(nas, i, &val); + if (direction == 1 && val > maxval ) { /* new local max */ + maxval = val; + loc = i; + } else if (direction == -1 && val < minval ) { /* new local min */ + minval = val; + loc = i; + } else if (direction == 1 && (maxval - val >= delta)) { + numaAddNumber(nad, loc); /* save the current max location */ + if (nav) numaAddNumber(nav, maxval); + direction = -1; /* reverse: start looking for a min */ + minval = val; + loc = i; /* current min location */ + } else if (direction == -1 && (val - minval >= delta)) { + numaAddNumber(nad, loc); /* save the current min location */ + if (nav) numaAddNumber(nav, minval); + direction = 1; /* reverse: start looking for a max */ + maxval = val; + loc = i; /* current max location */ + } + } + + /* Save the final extremum */ +/* numaAddNumber(nad, loc); */ + return nad; +} + + +/*! + * \brief numaCountReversals() + * + * \param[in] nas input values + * \param[in] minreversal relative amount to resolve peaks and valleys + * \param[out] pnr [optional] number of reversals + * \param[out] prd [optional] reversal density: reversals/length + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input numa is can be generated from pixExtractAlongLine().
+ *          If so, the x parameters can be used to find the reversal
+ *          frequency along a line.
+ *      (2) If the input numa was generated from a 1 bpp pix, the
+ *          values will be 0 and 1.  Use %minreversal == 1 to get
+ *          the number of pixel flips.  If the only values are 0 and 1,
+ *          but %minreversal > 1, set the reversal count to 0 and
+ *          issue a warning.
+ * 
+ */ +l_ok +numaCountReversals(NUMA *nas, + l_float32 minreversal, + l_int32 *pnr, + l_float32 *prd) +{ +l_int32 i, n, nr, ival, binvals; +l_int32 *ia; +l_float32 fval, delx, len; +NUMA *nat; + + PROCNAME("numaCountReversals"); + + if (pnr) *pnr = 0; + if (prd) *prd = 0.0; + if (!pnr && !prd) + return ERROR_INT("neither &nr nor &rd are defined", procName, 1); + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if ((n = numaGetCount(nas)) == 0) { + L_INFO("nas is empty\n", procName); + return 0; + } + if (minreversal < 0.0) + return ERROR_INT("minreversal < 0", procName, 1); + + /* Decide if the only values are 0 and 1 */ + binvals = TRUE; + for (i = 0; i < n; i++) { + numaGetFValue(nas, i, &fval); + if (fval != 0.0 && fval != 1.0) { + binvals = FALSE; + break; + } + } + + nr = 0; + if (binvals) { + if (minreversal > 1.0) { + L_WARNING("binary values but minreversal > 1\n", procName); + } else { + ia = numaGetIArray(nas); + ival = ia[0]; + for (i = 1; i < n; i++) { + if (ia[i] != ival) { + nr++; + ival = ia[i]; + } + } + LEPT_FREE(ia); + } + } else { + nat = numaFindExtrema(nas, minreversal, NULL); + nr = numaGetCount(nat); + numaDestroy(&nat); + } + if (pnr) *pnr = nr; + if (prd) { + numaGetParameters(nas, NULL, &delx); + len = delx * n; + *prd = (l_float32)nr / len; + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Threshold crossings and frequency analysis * + *----------------------------------------------------------------------*/ +/*! + * \brief numaSelectCrossingThreshold() + * + * \param[in] nax [optional] numa of abscissa values; can be NULL + * \param[in] nay signal + * \param[in] estthresh estimated pixel threshold for crossing: + * e.g., for images, white <--> black; typ. ~120 + * \param[out] pbestthresh robust estimate of threshold to use + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) When a valid threshold is used, the number of crossings is
+ *         a maximum, because none are missed.  If no threshold intersects
+ *         all the crossings, the crossings must be determined with
+ *         numaCrossingsByPeaks().
+ *     (2) %estthresh is an input estimate of the threshold that should
+ *         be used.  We compute the crossings with 41 thresholds
+ *         (20 below and 20 above).  There is a range in which the
+ *         number of crossings is a maximum.  Return a threshold
+ *         in the center of this stable plateau of crossings.
+ *         This can then be used with numaCrossingsByThreshold()
+ *         to get a good estimate of crossing locations.
+ * 
+ */ +l_ok +numaSelectCrossingThreshold(NUMA *nax, + NUMA *nay, + l_float32 estthresh, + l_float32 *pbestthresh) +{ +l_int32 i, inrun, istart, iend, maxstart, maxend, runlen, maxrunlen; +l_int32 val, maxval, nmax, count; +l_float32 thresh, fmaxval, fmodeval; +NUMA *nat, *nac; + + PROCNAME("numaSelectCrossingThreshold"); + + if (!pbestthresh) + return ERROR_INT("&bestthresh not defined", procName, 1); + *pbestthresh = 0.0; + if (!nay) + return ERROR_INT("nay not defined", procName, 1); + + /* Compute the number of crossings for different thresholds */ + nat = numaCreate(41); + for (i = 0; i < 41; i++) { + thresh = estthresh - 80.0 + 4.0 * i; + nac = numaCrossingsByThreshold(nax, nay, thresh); + numaAddNumber(nat, numaGetCount(nac)); + numaDestroy(&nac); + } + + /* Find the center of the plateau of max crossings, which + * extends from thresh[istart] to thresh[iend]. */ + numaGetMax(nat, &fmaxval, NULL); + maxval = (l_int32)fmaxval; + nmax = 0; + for (i = 0; i < 41; i++) { + numaGetIValue(nat, i, &val); + if (val == maxval) + nmax++; + } + if (nmax < 3) { /* likely accidental max; try the mode */ + numaGetMode(nat, &fmodeval, &count); + if (count > nmax && fmodeval > 0.5 * fmaxval) + maxval = (l_int32)fmodeval; /* use the mode */ + } + + inrun = FALSE; + iend = 40; + maxrunlen = 0, maxstart = 0, maxend = 0; + for (i = 0; i < 41; i++) { + numaGetIValue(nat, i, &val); + if (val == maxval) { + if (!inrun) { + istart = i; + inrun = TRUE; + } + continue; + } + if (inrun && (val != maxval)) { + iend = i - 1; + runlen = iend - istart + 1; + inrun = FALSE; + if (runlen > maxrunlen) { + maxstart = istart; + maxend = iend; + maxrunlen = runlen; + } + } + } + if (inrun) { + runlen = i - istart; + if (runlen > maxrunlen) { + maxstart = istart; + maxend = i - 1; + maxrunlen = runlen; + } + } + + *pbestthresh = estthresh - 80.0 + 2.0 * (l_float32)(maxstart + maxend); + +#if DEBUG_CROSSINGS + fprintf(stderr, "\nCrossings attain a maximum at %d thresholds, between:\n" + " thresh[%d] = %5.1f and thresh[%d] = %5.1f\n", + nmax, maxstart, estthresh - 80.0 + 4.0 * maxstart, + maxend, estthresh - 80.0 + 4.0 * maxend); + fprintf(stderr, "The best choice: %5.1f\n", *pbestthresh); + fprintf(stderr, "Number of crossings at the 41 thresholds:"); + numaWriteStream(stderr, nat); +#endif /* DEBUG_CROSSINGS */ + + numaDestroy(&nat); + return 0; +} + + +/*! + * \brief numaCrossingsByThreshold() + * + * \param[in] nax [optional] numa of abscissa values; can be NULL + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] thresh threshold value for nay + * \return nad abscissa pts at threshold, or NULL on error + * + *
+ * Notes:
+ *      (1) If nax == NULL, we use startx and delx from nay to compute
+ *          the crossing values in nad.
+ * 
+ */ +NUMA * +numaCrossingsByThreshold(NUMA *nax, + NUMA *nay, + l_float32 thresh) +{ +l_int32 i, n; +l_float32 startx, delx; +l_float32 xval1, xval2, yval1, yval2, delta1, delta2, crossval, fract; +NUMA *nad; + + PROCNAME("numaCrossingsByThreshold"); + + if (!nay) + return (NUMA *)ERROR_PTR("nay not defined", procName, NULL); + n = numaGetCount(nay); + + if (nax && (numaGetCount(nax) != n)) + return (NUMA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); + + nad = numaCreate(0); + numaGetFValue(nay, 0, &yval1); + numaGetParameters(nay, &startx, &delx); + if (nax) + numaGetFValue(nax, 0, &xval1); + else + xval1 = startx; + for (i = 1; i < n; i++) { + numaGetFValue(nay, i, &yval2); + if (nax) + numaGetFValue(nax, i, &xval2); + else + xval2 = startx + i * delx; + delta1 = yval1 - thresh; + delta2 = yval2 - thresh; + if (delta1 == 0.0) { + numaAddNumber(nad, xval1); + } else if (delta2 == 0.0) { + numaAddNumber(nad, xval2); + } else if (delta1 * delta2 < 0.0) { /* crossing */ + fract = L_ABS(delta1) / L_ABS(yval1 - yval2); + crossval = xval1 + fract * (xval2 - xval1); + numaAddNumber(nad, crossval); + } + xval1 = xval2; + yval1 = yval2; + } + + return nad; +} + + +/*! + * \brief numaCrossingsByPeaks() + * + * \param[in] nax [optional] numa of abscissa values + * \param[in] nay numa of ordinate values, corresponding to nax + * \param[in] delta parameter used to identify when a new peak can be found + * \return nad abscissa pts at threshold, or NULL on error + * + *
+ * Notes:
+ *      (1) If nax == NULL, we use startx and delx from nay to compute
+ *          the crossing values in nad.
+ * 
+ */ +NUMA * +numaCrossingsByPeaks(NUMA *nax, + NUMA *nay, + l_float32 delta) +{ +l_int32 i, j, n, np, previndex, curindex; +l_float32 startx, delx; +l_float32 xval1, xval2, yval1, yval2, delta1, delta2; +l_float32 prevval, curval, thresh, crossval, fract; +NUMA *nap, *nad; + + PROCNAME("numaCrossingsByPeaks"); + + if (!nay) + return (NUMA *)ERROR_PTR("nay not defined", procName, NULL); + + n = numaGetCount(nay); + if (nax && (numaGetCount(nax) != n)) + return (NUMA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); + + /* Find the extrema. Also add last point in nay to get + * the last transition (from the last peak to the end). + * The number of crossings is 1 more than the number of extrema. */ + nap = numaFindExtrema(nay, delta, NULL); + numaAddNumber(nap, n - 1); + np = numaGetCount(nap); + L_INFO("Number of crossings: %d\n", procName, np); + + /* Do all computation in index units of nax or the delx of nay */ + nad = numaCreate(np); /* output crossing locations, in nax units */ + previndex = 0; /* prime the search with 1st point */ + numaGetFValue(nay, 0, &prevval); /* prime the search with 1st point */ + numaGetParameters(nay, &startx, &delx); + for (i = 0; i < np; i++) { + numaGetIValue(nap, i, &curindex); + numaGetFValue(nay, curindex, &curval); + thresh = (prevval + curval) / 2.0; + if (nax) + numaGetFValue(nax, previndex, &xval1); + else + xval1 = startx + previndex * delx; + numaGetFValue(nay, previndex, &yval1); + for (j = previndex + 1; j <= curindex; j++) { + if (nax) + numaGetFValue(nax, j, &xval2); + else + xval2 = startx + j * delx; + numaGetFValue(nay, j, &yval2); + delta1 = yval1 - thresh; + delta2 = yval2 - thresh; + if (delta1 == 0.0) { + numaAddNumber(nad, xval1); + break; + } else if (delta2 == 0.0) { + numaAddNumber(nad, xval2); + break; + } else if (delta1 * delta2 < 0.0) { /* crossing */ + fract = L_ABS(delta1) / L_ABS(yval1 - yval2); + crossval = xval1 + fract * (xval2 - xval1); + numaAddNumber(nad, crossval); + break; + } + xval1 = xval2; + yval1 = yval2; + } + previndex = curindex; + prevval = curval; + } + + numaDestroy(&nap); + return nad; +} + + +/*! + * \brief numaEvalBestHaarParameters() + * + * \param[in] nas numa of non-negative signal values + * \param[in] relweight relative weight of (-1 comb) / (+1 comb) + * contributions to the 'convolution'. In effect, + * the convolution kernel is a comb consisting of + * alternating +1 and -weight. + * \param[in] nwidth number of widths to consider + * \param[in] nshift number of shifts to consider for each width + * \param[in] minwidth smallest width to consider + * \param[in] maxwidth largest width to consider + * \param[out] pbestwidth width giving largest score + * \param[out] pbestshift shift giving largest score + * \param[out] pbestscore [optional] convolution with "Haar"-like comb + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a linear sweep of widths, evaluating at %nshift
+ *          shifts for each width, computing the score from a convolution
+ *          with a long comb, and finding the (width, shift) pair that
+ *          gives the maximum score.  The best width is the "half-wavelength"
+ *          of the signal.
+ *      (2) The convolving function is a comb of alternating values
+ *          +1 and -1 * relweight, separated by the width and phased by
+ *          the shift.  This is similar to a Haar transform, except
+ *          there the convolution is performed with a square wave.
+ *      (3) The function is useful for finding the line spacing
+ *          and strength of line signal from pixel sum projections.
+ *      (4) The score is normalized to the size of nas divided by
+ *          the number of half-widths.  For image applications, the input is
+ *          typically an array of pixel projections, so one should
+ *          normalize by dividing the score by the image width in the
+ *          pixel projection direction.
+ * 
+ */ +l_ok +numaEvalBestHaarParameters(NUMA *nas, + l_float32 relweight, + l_int32 nwidth, + l_int32 nshift, + l_float32 minwidth, + l_float32 maxwidth, + l_float32 *pbestwidth, + l_float32 *pbestshift, + l_float32 *pbestscore) +{ +l_int32 i, j; +l_float32 delwidth, delshift, width, shift, score; +l_float32 bestwidth, bestshift, bestscore; + + PROCNAME("numaEvalBestHaarParameters"); + + if (pbestscore) *pbestscore = 0.0; + if (pbestwidth) *pbestwidth = 0.0; + if (pbestshift) *pbestshift = 0.0; + if (!pbestwidth || !pbestshift) + return ERROR_INT("&bestwidth and &bestshift not defined", procName, 1); + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + + bestscore = bestwidth = bestshift = 0.0; + delwidth = (maxwidth - minwidth) / (nwidth - 1.0); + for (i = 0; i < nwidth; i++) { + width = minwidth + delwidth * i; + delshift = width / (l_float32)(nshift); + for (j = 0; j < nshift; j++) { + shift = j * delshift; + numaEvalHaarSum(nas, width, shift, relweight, &score); + if (score > bestscore) { + bestscore = score; + bestwidth = width; + bestshift = shift; +#if DEBUG_FREQUENCY + fprintf(stderr, "width = %7.3f, shift = %7.3f, score = %7.3f\n", + width, shift, score); +#endif /* DEBUG_FREQUENCY */ + } + } + } + + *pbestwidth = bestwidth; + *pbestshift = bestshift; + if (pbestscore) + *pbestscore = bestscore; + return 0; +} + + +/*! + * \brief numaEvalHaarSum() + * + * \param[in] nas numa of non-negative signal values + * \param[in] width distance between +1 and -1 in convolution comb + * \param[in] shift phase of the comb: location of first +1 + * \param[in] relweight relative weight of (-1 comb) / (+1 comb) + * contributions to the 'convolution'. In effect, + * the convolution kernel is a comb consisting of + * alternating +1 and -weight. + * \param[out] pscore convolution with "Haar"-like comb + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a convolution with a comb of alternating values
+ *          +1 and -relweight, separated by the width and phased by the shift.
+ *          This is similar to a Haar transform, except that for Haar,
+ *            (1) the convolution kernel is symmetric about 0, so the
+ *                relweight is 1.0, and
+ *            (2) the convolution is performed with a square wave.
+ *      (2) The score is normalized to the size of nas divided by
+ *          twice the "width".  For image applications, the input is
+ *          typically an array of pixel projections, so one should
+ *          normalize by dividing the score by the image width in the
+ *          pixel projection direction.
+ *      (3) To get a Haar-like result, use relweight = 1.0.  For detecting
+ *          signals where you expect every other sample to be close to
+ *          zero, as with barcodes or filtered text lines, you can
+ *          use relweight > 1.0.
+ * 
+ */ +l_ok +numaEvalHaarSum(NUMA *nas, + l_float32 width, + l_float32 shift, + l_float32 relweight, + l_float32 *pscore) +{ +l_int32 i, n, nsamp, index; +l_float32 score, weight, val; + + PROCNAME("numaEvalHaarSum"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = 0.0; + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if ((n = numaGetCount(nas)) < 2 * width) + return ERROR_INT("nas size too small", procName, 1); + + score = 0.0; + nsamp = (l_int32)((n - shift) / width); + for (i = 0; i < nsamp; i++) { + index = (l_int32)(shift + i * width); + weight = (i % 2) ? 1.0 : -1.0 * relweight; + numaGetFValue(nas, index, &val); + score += weight * val; + } + + *pscore = 2.0 * width * score / (l_float32)n; + return 0; +} + + +/*----------------------------------------------------------------------* + * Generating numbers in a range under constraints * + *----------------------------------------------------------------------*/ +/*! + * \brief genConstrainedNumaInRange() + * + * \param[in] first first number to choose; >= 0 + * \param[in] last biggest possible number to reach; >= first + * \param[in] nmax maximum number of numbers to select; > 0 + * \param[in] use_pairs 1 = select pairs of adjacent numbers; + * 0 = select individual numbers + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) Selection is made uniformly in the range.  This can be used
+ *         to select pages distributed as uniformly as possible
+ *         through a book, where you are constrained to:
+ *          ~ choose between [first, ... biggest],
+ *          ~ choose no more than nmax numbers, and
+ *         and you have the option of requiring pairs of adjacent numbers.
+ * 
+ */ +NUMA * +genConstrainedNumaInRange(l_int32 first, + l_int32 last, + l_int32 nmax, + l_int32 use_pairs) +{ +l_int32 i, nsets, val; +l_float32 delta; +NUMA *na; + + PROCNAME("genConstrainedNumaInRange"); + + first = L_MAX(0, first); + if (last < first) + return (NUMA *)ERROR_PTR("last < first!", procName, NULL); + if (nmax < 1) + return (NUMA *)ERROR_PTR("nmax < 1!", procName, NULL); + + nsets = L_MIN(nmax, last - first + 1); + if (use_pairs == 1) + nsets = nsets / 2; + if (nsets == 0) + return (NUMA *)ERROR_PTR("nsets == 0", procName, NULL); + + /* Select delta so that selection covers the full range if possible */ + if (nsets == 1) { + delta = 0.0; + } else { + if (use_pairs == 0) + delta = (l_float32)(last - first) / (nsets - 1); + else + delta = (l_float32)(last - first - 1) / (nsets - 1); + } + + na = numaCreate(nsets); + for (i = 0; i < nsets; i++) { + val = (l_int32)(first + i * delta + 0.5); + numaAddNumber(na, val); + if (use_pairs == 1) + numaAddNumber(na, val + 1); + } + + return na; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pageseg.c b/hgdriver/3rdparty/hgOCR/leptonica/pageseg.c new file mode 100644 index 0000000..4316450 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pageseg.c @@ -0,0 +1,2360 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pageseg.c + *
+ *
+ *      Top level page segmentation
+ *          l_int32   pixGetRegionsBinary()
+ *
+ *      Halftone region extraction
+ *          PIX      *pixGenHalftoneMask()    **Deprecated wrapper**
+ *          PIX      *pixGenerateHalftoneMask()
+ *
+ *      Textline extraction
+ *          PIX      *pixGenTextlineMask()
+ *
+ *      Textblock extraction
+ *          PIX      *pixGenTextblockMask()
+ *
+ *      Location of page foreground
+ *          PIX      *pixFindPageForeground()
+ *
+ *      Extraction of characters from image with only text
+ *          l_int32   pixSplitIntoCharacters()
+ *          BOXA     *pixSplitComponentWithProfile()
+ *
+ *      Extraction of lines of text
+ *          PIXA     *pixExtractTextlines()
+ *          PIXA     *pixExtractRawTextlines()
+ *
+ *      How many text columns
+ *          l_int32   pixCountTextColumns()
+ *
+ *      Decision: text vs photo
+ *          l_int32   pixDecideIfText()
+ *          l_int32   pixFindThreshFgExtent()
+ *
+ *      Decision: table vs text
+ *          l_int32   pixDecideIfTable()
+ *          Pix      *pixPrepare1bpp()
+ *
+ *      Estimate the grayscale background value
+ *          l_int32   pixEstimateBackground()
+ *
+ *      Largest white or black rectangles in an image
+ *          l_int32   pixFindLargeRectangles()
+ *          l_int32   pixFindLargestRectangle()
+ *
+ *      Generate rectangle inside connected component
+ *          BOX      *pixFindRectangleInCC()
+ * 
+ */ + +#include "allheaders.h" +#include "math.h" + + /* These functions are not intended to work on very low-res images */ +static const l_int32 MinWidth = 100; +static const l_int32 MinHeight = 100; + +/*------------------------------------------------------------------* + * Top level page segmentation * + *------------------------------------------------------------------*/ +/*! + * \brief pixGetRegionsBinary() + * + * \param[in] pixs 1 bpp, assumed to be 300 to 400 ppi + * \param[out] ppixhm [optional] halftone mask + * \param[out] ppixtm [optional] textline mask + * \param[out] ppixtb [optional] textblock mask + * \param[in] pixadb input for collecting debug pix; use NULL to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is best to deskew the image before segmenting.
+ *      (2) Passing in %pixadb enables debug output.
+ * 
+ */ +l_ok +pixGetRegionsBinary(PIX *pixs, + PIX **ppixhm, + PIX **ppixtm, + PIX **ppixtb, + PIXA *pixadb) +{ +l_int32 w, h, htfound, tlfound; +PIX *pixr, *pix1, *pix2; +PIX *pixtext; /* text pixels only */ +PIX *pixhm2; /* halftone mask; 2x reduction */ +PIX *pixhm; /* halftone mask; */ +PIX *pixtm2; /* textline mask; 2x reduction */ +PIX *pixtm; /* textline mask */ +PIX *pixvws; /* vertical white space mask */ +PIX *pixtb2; /* textblock mask; 2x reduction */ +PIX *pixtbf2; /* textblock mask; 2x reduction; small comps filtered */ +PIX *pixtb; /* textblock mask */ + + PROCNAME("pixGetRegionsBinary"); + + if (ppixhm) *ppixhm = NULL; + if (ppixtm) *ppixtm = NULL; + if (ppixtb) *ppixtb = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinWidth || h < MinHeight) { + L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); + return 1; + } + + /* 2x reduce, to 150 -200 ppi */ + pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); + if (pixadb) pixaAddPix(pixadb, pixr, L_COPY); + + /* Get the halftone mask */ + pixhm2 = pixGenerateHalftoneMask(pixr, &pixtext, &htfound, pixadb); + + /* Get the textline mask from the text pixels */ + pixtm2 = pixGenTextlineMask(pixtext, &pixvws, &tlfound, pixadb); + + /* Get the textblock mask from the textline mask */ + pixtb2 = pixGenTextblockMask(pixtm2, pixvws, pixadb); + pixDestroy(&pixr); + pixDestroy(&pixtext); + pixDestroy(&pixvws); + + /* Remove small components from the mask, where a small + * component is defined as one with both width and height < 60 */ + pixtbf2 = NULL; + if (pixtb2) { + pixtbf2 = pixSelectBySize(pixtb2, 60, 60, 4, L_SELECT_IF_EITHER, + L_SELECT_IF_GTE, NULL); + pixDestroy(&pixtb2); + if (pixadb) pixaAddPix(pixadb, pixtbf2, L_COPY); + } + + /* Expand all masks to full resolution, and do filling or + * small dilations for better coverage. */ + pixhm = pixExpandReplicate(pixhm2, 2); + pix1 = pixSeedfillBinary(NULL, pixhm, pixs, 8); + pixOr(pixhm, pixhm, pix1); + pixDestroy(&pixhm2); + pixDestroy(&pix1); + if (pixadb) pixaAddPix(pixadb, pixhm, L_COPY); + + pix1 = pixExpandReplicate(pixtm2, 2); + pixtm = pixDilateBrick(NULL, pix1, 3, 3); + pixDestroy(&pixtm2); + pixDestroy(&pix1); + if (pixadb) pixaAddPix(pixadb, pixtm, L_COPY); + + if (pixtbf2) { + pix1 = pixExpandReplicate(pixtbf2, 2); + pixtb = pixDilateBrick(NULL, pix1, 3, 3); + pixDestroy(&pixtbf2); + pixDestroy(&pix1); + if (pixadb) pixaAddPix(pixadb, pixtb, L_COPY); + } else { + pixtb = pixCreateTemplate(pixs); /* empty mask */ + } + + /* Debug: identify objects that are neither text nor halftone image */ + if (pixadb) { + pix1 = pixSubtract(NULL, pixs, pixtm); /* remove text pixels */ + pix2 = pixSubtract(NULL, pix1, pixhm); /* remove halftone pixels */ + pixaAddPix(pixadb, pix2, L_INSERT); + pixDestroy(&pix1); + } + + /* Debug: display textline components with random colors */ + if (pixadb) { + l_int32 w, h; + BOXA *boxa; + PIXA *pixa; + boxa = pixConnComp(pixtm, &pixa, 8); + pixGetDimensions(pixtm, &w, &h, NULL); + pix1 = pixaDisplayRandomCmap(pixa, w, h); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + } + + /* Debug: identify the outlines of each textblock */ + if (pixadb) { + PIXCMAP *cmap; + PTAA *ptaa; + ptaa = pixGetOuterBordersPtaa(pixtb); + lept_mkdir("lept/pageseg"); + ptaaWriteDebug("/tmp/lept/pageseg/tb_outlines.ptaa", ptaa, 1); + pix1 = pixRenderRandomCmapPtaa(pixtb, ptaa, 1, 16, 1); + cmap = pixGetColormap(pix1); + pixcmapResetColor(cmap, 0, 130, 130, 130); + pixaAddPix(pixadb, pix1, L_INSERT); + ptaaDestroy(&ptaa); + } + + /* Debug: get b.b. for all mask components */ + if (pixadb) { + BOXA *bahm, *batm, *batb; + bahm = pixConnComp(pixhm, NULL, 4); + batm = pixConnComp(pixtm, NULL, 4); + batb = pixConnComp(pixtb, NULL, 4); + boxaWriteDebug("/tmp/lept/pageseg/htmask.boxa", bahm); + boxaWriteDebug("/tmp/lept/pageseg/textmask.boxa", batm); + boxaWriteDebug("/tmp/lept/pageseg/textblock.boxa", batb); + boxaDestroy(&bahm); + boxaDestroy(&batm); + boxaDestroy(&batb); + } + if (pixadb) { + pixaConvertToPdf(pixadb, 0, 1.0, 0, 0, "Debug page segmentation", + "/tmp/lept/pageseg/debug.pdf"); + L_INFO("Writing debug pdf to /tmp/lept/pageseg/debug.pdf\n", procName); + } + + if (ppixhm) + *ppixhm = pixhm; + else + pixDestroy(&pixhm); + if (ppixtm) + *ppixtm = pixtm; + else + pixDestroy(&pixtm); + if (ppixtb) + *ppixtb = pixtb; + else + pixDestroy(&pixtb); + + return 0; +} + + +/*------------------------------------------------------------------* + * Halftone region extraction * + *------------------------------------------------------------------*/ +/*! + * \brief pixGenHalftoneMask() + * + *
+ * Deprecated:
+ *   This wrapper avoids an ABI change with tesseract 3.0.4.
+ *   It should be removed when we no longer need to support 3.0.4.
+ *   The debug parameter is ignored (assumed 0).
+ * 
+ */ +PIX * +pixGenHalftoneMask(PIX *pixs, + PIX **ppixtext, + l_int32 *phtfound, + l_int32 debug) +{ + return pixGenerateHalftoneMask(pixs, ppixtext, phtfound, NULL); +} + + +/*! + * \brief pixGenerateHalftoneMask() + * + * \param[in] pixs 1 bpp, assumed to be 150 to 200 ppi + * \param[out] ppixtext [optional] text part of pixs + * \param[out] phtfound [optional] 1 if the mask is not empty + * \param[in] pixadb input for collecting debug pix; use NULL to skip + * \return pixd halftone mask, or NULL on error + * + *
+ * Notes:
+ *      (1) This is not intended to work on small thumbnails.  The
+ *          dimensions of pixs must be at least MinWidth x MinHeight.
+ * 
+ */ +PIX * +pixGenerateHalftoneMask(PIX *pixs, + PIX **ppixtext, + l_int32 *phtfound, + PIXA *pixadb) +{ +l_int32 w, h, empty; +PIX *pix1, *pix2, *pixhs, *pixhm, *pixd; + + PROCNAME("pixGenerateHalftoneMask"); + + if (ppixtext) *ppixtext = NULL; + if (phtfound) *phtfound = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinWidth || h < MinHeight) { + L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); + return NULL; + } + + /* Compute seed for halftone parts at 8x reduction */ + pix1 = pixReduceRankBinaryCascade(pixs, 4, 4, 3, 0); + pix2 = pixOpenBrick(NULL, pix1, 5, 5); + pixhs = pixExpandReplicate(pix2, 8); /* back to 2x reduction */ + pixDestroy(&pix1); + pixDestroy(&pix2); + if (pixadb) pixaAddPix(pixadb, pixhs, L_COPY); + + /* Compute mask for connected regions */ + pixhm = pixCloseSafeBrick(NULL, pixs, 4, 4); + if (pixadb) pixaAddPix(pixadb, pixhm, L_COPY); + + /* Fill seed into mask to get halftone mask */ + pixd = pixSeedfillBinary(NULL, pixhs, pixhm, 4); + +#if 0 + /* Moderate opening to remove thin lines, etc. */ + pixOpenBrick(pixd, pixd, 10, 10); +#endif + + /* Check if mask is empty */ + pixZero(pixd, &empty); + if (phtfound && !empty) + *phtfound = 1; + + /* Optionally, get all pixels that are not under the halftone mask */ + if (ppixtext) { + if (empty) + *ppixtext = pixCopy(NULL, pixs); + else + *ppixtext = pixSubtract(NULL, pixs, pixd); + if (pixadb) pixaAddPix(pixadb, *ppixtext, L_COPY); + } + + pixDestroy(&pixhs); + pixDestroy(&pixhm); + return pixd; +} + + +/*------------------------------------------------------------------* + * Textline extraction * + *------------------------------------------------------------------*/ +/*! + * \brief pixGenTextlineMask() + * + * \param[in] pixs 1 bpp, assumed to be 150 to 200 ppi + * \param[out] ppixvws vertical whitespace mask + * \param[out] ptlfound [optional] 1 if the mask is not empty + * \param[in] pixadb input for collecting debug pix; use NULL to skip + * \return pixd textline mask, or NULL on error + * + *
+ * Notes:
+ *      (1) The input pixs should be deskewed.
+ *      (2) pixs should have no halftone pixels.
+ *      (3) This is not intended to work on small thumbnails.  The
+ *          dimensions of pixs must be at least MinWidth x MinHeight.
+ *      (4) Both the input image and the returned textline mask
+ *          are at the same resolution.
+ * 
+ */ +PIX * +pixGenTextlineMask(PIX *pixs, + PIX **ppixvws, + l_int32 *ptlfound, + PIXA *pixadb) +{ +l_int32 w, h, empty; +PIX *pix1, *pix2, *pixvws, *pixd; + + PROCNAME("pixGenTextlineMask"); + + if (ptlfound) *ptlfound = 0; + if (!ppixvws) + return (PIX *)ERROR_PTR("&pixvws not defined", procName, NULL); + *ppixvws = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinWidth || h < MinHeight) { + L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); + return NULL; + } + + /* First we need a vertical whitespace mask. Invert the image. */ + pix1 = pixInvert(NULL, pixs); + + /* The whitespace mask will break textlines where there + * is a large amount of white space below or above. + * This can be prevented by identifying regions of the + * inverted image that have large horizontal extent (bigger than + * the separation between columns) and significant + * vertical extent (bigger than the separation between + * textlines), and subtracting this from the bg. */ + pix2 = pixMorphCompSequence(pix1, "o80.60", 0); + pixSubtract(pix1, pix1, pix2); + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + pixDestroy(&pix2); + + /* Identify vertical whitespace by opening the remaining bg. + * o5.1 removes thin vertical bg lines and o1.200 extracts + * long vertical bg lines. */ + pixvws = pixMorphCompSequence(pix1, "o5.1 + o1.200", 0); + *ppixvws = pixvws; + if (pixadb) pixaAddPix(pixadb, pixvws, L_COPY); + pixDestroy(&pix1); + + /* Three steps to getting text line mask: + * (1) close the characters and words in the textlines + * (2) open the vertical whitespace corridors back up + * (3) small opening to remove noise */ + pix1 = pixMorphSequence(pixs, "c30.1", 0); + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + pixd = pixSubtract(NULL, pix1, pixvws); + pixOpenBrick(pixd, pixd, 3, 3); + if (pixadb) pixaAddPix(pixadb, pixd, L_COPY); + pixDestroy(&pix1); + + /* Check if text line mask is empty */ + if (ptlfound) { + pixZero(pixd, &empty); + if (!empty) + *ptlfound = 1; + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Textblock extraction * + *------------------------------------------------------------------*/ +/*! + * \brief pixGenTextblockMask() + * + * \param[in] pixs 1 bpp, textline mask, assumed to be 150 to 200 ppi + * \param[in] pixvws vertical white space mask + * \param[in] pixadb input for collecting debug pix; use NULL to skip + * \return pixd textblock mask, or NULL if empty or on error + * + *
+ * Notes:
+ *      (1) Both the input masks (textline and vertical white space) and
+ *          the returned textblock mask are at the same resolution.
+ *      (2) This is not intended to work on small thumbnails.  The
+ *          dimensions of pixs must be at least MinWidth x MinHeight.
+ *      (3) The result is somewhat noisy, in that small "blocks" of
+ *          text may be included.  These can be removed by post-processing,
+ *          using, e.g.,
+ *             pixSelectBySize(pix, 60, 60, 4, L_SELECT_IF_EITHER,
+ *                             L_SELECT_IF_GTE, NULL);
+ * 
+ */ +PIX * +pixGenTextblockMask(PIX *pixs, + PIX *pixvws, + PIXA *pixadb) +{ +l_int32 w, h, empty; +PIX *pix1, *pix2, *pix3, *pixd; + + PROCNAME("pixGenTextblockMask"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinWidth || h < MinHeight) { + L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); + return NULL; + } + if (!pixvws) + return (PIX *)ERROR_PTR("pixvws not defined", procName, NULL); + + /* Join pixels vertically to make a textblock mask */ + pix1 = pixMorphSequence(pixs, "c1.10 + o4.1", 0); + pixZero(pix1, &empty); + if (empty) { + pixDestroy(&pix1); + L_INFO("no fg pixels in textblock mask\n", procName); + return NULL; + } + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + + /* Solidify the textblock mask and remove noise: + * (1) For each cc, close the blocks and dilate slightly + * to form a solid mask. + * (2) Small horizontal closing between components. + * (3) Open the white space between columns, again. + * (4) Remove small components. */ + pix2 = pixMorphSequenceByComponent(pix1, "c30.30 + d3.3", 8, 0, 0, NULL); + pixCloseSafeBrick(pix2, pix2, 10, 1); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + pix3 = pixSubtract(NULL, pix2, pixvws); + if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); + pixd = pixSelectBySize(pix3, 25, 5, 8, L_SELECT_IF_BOTH, + L_SELECT_IF_GTE, NULL); + if (pixadb) pixaAddPix(pixadb, pixd, L_COPY); + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + return pixd; +} + + +/*------------------------------------------------------------------* + * Location of page foreground * + *------------------------------------------------------------------*/ +/*! + * \brief pixFindPageForeground() + * + * \param[in] pixs full resolution (any type or depth + * \param[in] threshold for binarization; typically about 128 + * \param[in] mindist min distance of text from border to allow + * cleaning near border; at 2x reduction, this + * should be larger than 50; typically about 70 + * \param[in] erasedist when conditions are satisfied, erase anything + * within this distance of the edge; + * typically 20-30 at 2x reduction + * \param[in] showmorph debug: set to a negative integer to show steps + * in generating masks; this is typically used + * for debugging region extraction + * \param[in] pixac debug: allocate outside and pass this in to + * accumulate results of each call to this function, + * which can be displayed in a mosaic or a pdf. + * \return box region including foreground, with some pixel noise + * removed, or NULL if not found + * + *
+ * Notes:
+ *      (1) This doesn't simply crop to the fg.  It attempts to remove
+ *          pixel noise and junk at the edge of the image before cropping.
+ *          The input %threshold is used if pixs is not 1 bpp.
+ *      (2) This is not intended to work on small thumbnails.  The
+ *          dimensions of pixs must be at least MinWidth x MinHeight.
+ *      (3) Debug: set showmorph to display the intermediate image in
+ *          the morphological operations on this page.
+ *      (4) Debug: to get pdf output of results when called repeatedly,
+ *          call with an existing pixac, which will add an image of this page,
+ *          with the fg outlined.  If no foreground is found, there is
+ *          no output for this page image.
+ * 
+ */ +BOX * +pixFindPageForeground(PIX *pixs, + l_int32 threshold, + l_int32 mindist, + l_int32 erasedist, + l_int32 showmorph, + PIXAC *pixac) +{ +l_int32 flag, nbox, intersects; +l_int32 w, h, bx, by, bw, bh, left, right, top, bottom; +PIX *pixb, *pixb2, *pixseed, *pixsf, *pixm, *pix1, *pixg2; +BOX *box, *boxfg, *boxin, *boxd; +BOXA *ba1, *ba2; + + PROCNAME("pixFindPageForeground"); + + if (!pixs) + return (BOX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (w < MinWidth || h < MinHeight) { + L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); + return NULL; + } + + /* Binarize, downscale by 0.5, remove the noise to generate a seed, + * and do a seedfill back from the seed into those 8-connected + * components of the binarized image for which there was at least + * one seed pixel. Also clear out any components that are within + * 10 pixels of the edge at 2x reduction. */ + flag = (showmorph) ? 100 : 0; + pixb = pixConvertTo1(pixs, threshold); + pixb2 = pixScale(pixb, 0.5, 0.5); + pixseed = pixMorphSequence(pixb2, "o1.2 + c9.9 + o3.3", flag); + pix1 = pixMorphSequence(pixb2, "o50.1", 0); + pixOr(pixseed, pixseed, pix1); + pixDestroy(&pix1); + pix1 = pixMorphSequence(pixb2, "o1.50", 0); + pixOr(pixseed, pixseed, pix1); + pixDestroy(&pix1); + pixsf = pixSeedfillBinary(NULL, pixseed, pixb2, 8); + pixSetOrClearBorder(pixsf, 10, 10, 10, 10, PIX_SET); + pixm = pixRemoveBorderConnComps(pixsf, 8); + + /* Now, where is the main block of text? We want to remove noise near + * the edge of the image, but to do that, we have to be convinced that + * (1) there is noise and (2) it is far enough from the text block + * and close enough to the edge. For each edge, if the block + * is more than mindist from that edge, then clean 'erasedist' + * pixels from the edge. */ + pix1 = pixMorphSequence(pixm, "c50.50", flag); + ba1 = pixConnComp(pix1, NULL, 8); + ba2 = boxaSort(ba1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); + pixGetDimensions(pix1, &w, &h, NULL); + nbox = boxaGetCount(ba2); + if (nbox > 1) { + box = boxaGetBox(ba2, 0, L_CLONE); + boxGetGeometry(box, &bx, &by, &bw, &bh); + left = (bx > mindist) ? erasedist : 0; + right = (w - bx - bw > mindist) ? erasedist : 0; + top = (by > mindist) ? erasedist : 0; + bottom = (h - by - bh > mindist) ? erasedist : 0; + pixSetOrClearBorder(pixm, left, right, top, bottom, PIX_CLR); + boxDestroy(&box); + } + pixDestroy(&pix1); + boxaDestroy(&ba1); + boxaDestroy(&ba2); + + /* Locate the foreground region; don't bother cropping */ + pixClipToForeground(pixm, NULL, &boxfg); + + /* Sanity check the fg region. Make sure it's not confined + * to a thin boundary on the left and right sides of the image, + * in which case it is likely to be noise. */ + if (boxfg) { + boxin = boxCreate(0.1 * w, 0, 0.8 * w, h); + boxIntersects(boxfg, boxin, &intersects); + boxDestroy(&boxin); + if (!intersects) boxDestroy(&boxfg); + } + + boxd = NULL; + if (boxfg) { + boxAdjustSides(boxfg, boxfg, -2, 2, -2, 2); /* tiny expansion */ + boxd = boxTransform(boxfg, 0, 0, 2.0, 2.0); + + /* Save the debug image showing the box for this page */ + if (pixac) { + pixg2 = pixConvert1To4Cmap(pixb); + pixRenderBoxArb(pixg2, boxd, 3, 255, 0, 0); + pixacompAddPix(pixac, pixg2, IFF_DEFAULT); + pixDestroy(&pixg2); + } + } + + pixDestroy(&pixb); + pixDestroy(&pixb2); + pixDestroy(&pixseed); + pixDestroy(&pixsf); + pixDestroy(&pixm); + boxDestroy(&boxfg); + return boxd; +} + + +/*------------------------------------------------------------------* + * Extraction of characters from image with only text * + *------------------------------------------------------------------*/ +/*! + * \brief pixSplitIntoCharacters() + * + * \param[in] pixs 1 bpp, contains only deskewed text + * \param[in] minw min component width for initial filtering; typ. 4 + * \param[in] minh min component height for initial filtering; typ. 4 + * \param[out] pboxa [optional] character bounding boxes + * \param[out] ppixa [optional] character images + * \param[out] ppixdebug [optional] showing splittings + * + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a simple function that attempts to find split points
+ *          based on vertical pixel profiles.
+ *      (2) It should be given an image that has an arbitrary number
+ *          of text characters.
+ *      (3) The returned pixa includes the boxes from which the
+ *          (possibly split) components are extracted.
+ * 
+ */ +l_ok +pixSplitIntoCharacters(PIX *pixs, + l_int32 minw, + l_int32 minh, + BOXA **pboxa, + PIXA **ppixa, + PIX **ppixdebug) +{ +l_int32 ncomp, i, xoff, yoff; +BOXA *boxa1, *boxa2, *boxat1, *boxat2, *boxad; +BOXAA *baa; +PIX *pix, *pix1, *pix2, *pixdb; +PIXA *pixa1, *pixadb; + + PROCNAME("pixSplitIntoCharacters"); + + if (pboxa) *pboxa = NULL; + if (ppixa) *ppixa = NULL; + if (ppixdebug) *ppixdebug = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + /* Remove the small stuff */ + pix1 = pixSelectBySize(pixs, minw, minh, 8, L_SELECT_IF_BOTH, + L_SELECT_IF_GT, NULL); + + /* Small vertical close for consolidation */ + pix2 = pixMorphSequence(pix1, "c1.10", 0); + pixDestroy(&pix1); + + /* Get the 8-connected components */ + boxa1 = pixConnComp(pix2, &pixa1, 8); + pixDestroy(&pix2); + boxaDestroy(&boxa1); + + /* Split the components if obvious */ + ncomp = pixaGetCount(pixa1); + boxa2 = boxaCreate(ncomp); + pixadb = (ppixdebug) ? pixaCreate(ncomp) : NULL; + for (i = 0; i < ncomp; i++) { + pix = pixaGetPix(pixa1, i, L_CLONE); + if (ppixdebug) { + boxat1 = pixSplitComponentWithProfile(pix, 10, 7, &pixdb); + if (pixdb) + pixaAddPix(pixadb, pixdb, L_INSERT); + } else { + boxat1 = pixSplitComponentWithProfile(pix, 10, 7, NULL); + } + pixaGetBoxGeometry(pixa1, i, &xoff, &yoff, NULL, NULL); + boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0); + boxaJoin(boxa2, boxat2, 0, -1); + pixDestroy(&pix); + boxaDestroy(&boxat1); + boxaDestroy(&boxat2); + } + pixaDestroy(&pixa1); + + /* Generate the debug image */ + if (ppixdebug) { + if (pixaGetCount(pixadb) > 0) { + *ppixdebug = pixaDisplayTiledInRows(pixadb, 32, 1500, + 1.0, 0, 20, 1); + } + pixaDestroy(&pixadb); + } + + /* Do a 2D sort on the bounding boxes, and flatten the result to 1D */ + baa = boxaSort2d(boxa2, NULL, 0, 0, 5); + boxad = boxaaFlattenToBoxa(baa, NULL, L_CLONE); + boxaaDestroy(&baa); + boxaDestroy(&boxa2); + + /* Optionally extract the pieces from the input image */ + if (ppixa) + *ppixa = pixClipRectangles(pixs, boxad); + if (pboxa) + *pboxa = boxad; + else + boxaDestroy(&boxad); + return 0; +} + + +/*! + * \brief pixSplitComponentWithProfile() + * + * \param[in] pixs 1 bpp, exactly one connected component + * \param[in] delta distance used in extrema finding in a numa; typ. 10 + * \param[in] mindel minimum required difference between profile + * minimum and profile values +2 and -2 away; typ. 7 + * \param[out] ppixdebug [optional] debug image of splitting + * \return boxa of c.c. after splitting, or NULL on error + * + *
+ * Notes:
+ *      (1) This will split the most obvious cases of touching characters.
+ *          The split points it is searching for are narrow and deep
+ *          minimima in the vertical pixel projection profile, after a
+ *          large vertical closing has been applied to the component.
+ * 
+ */ +BOXA * +pixSplitComponentWithProfile(PIX *pixs, + l_int32 delta, + l_int32 mindel, + PIX **ppixdebug) +{ +l_int32 w, h, n2, i, firstmin, xmin, xshift; +l_int32 nmin, nleft, nright, nsplit, isplit, ncomp; +l_int32 *array1, *array2; +BOX *box; +BOXA *boxad; +NUMA *na1, *na2, *nasplit; +PIX *pix1, *pixdb; + + PROCNAME("pixSplitComponentsWithProfile"); + + if (ppixdebug) *ppixdebug = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixa undefined or not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + + /* Closing to consolidate characters vertically */ + pix1 = pixCloseSafeBrick(NULL, pixs, 1, 100); + + /* Get extrema of column projections */ + boxad = boxaCreate(2); + na1 = pixCountPixelsByColumn(pix1); /* w elements */ + pixDestroy(&pix1); + na2 = numaFindExtrema(na1, delta, NULL); + n2 = numaGetCount(na2); + if (n2 < 3) { /* no split possible */ + box = boxCreate(0, 0, w, h); + boxaAddBox(boxad, box, L_INSERT); + numaDestroy(&na1); + numaDestroy(&na2); + return boxad; + } + + /* Look for sufficiently deep and narrow minima. + * All minima of of interest must be surrounded by max on each + * side. firstmin is the index of first possible minimum. */ + array1 = numaGetIArray(na1); + array2 = numaGetIArray(na2); + if (ppixdebug) numaWriteStream(stderr, na2); + firstmin = (array1[array2[0]] > array1[array2[1]]) ? 1 : 2; + nasplit = numaCreate(n2); /* will hold split locations */ + for (i = firstmin; i < n2 - 1; i+= 2) { + xmin = array2[i]; + nmin = array1[xmin]; + if (xmin + 2 >= w) break; /* no more splits possible */ + nleft = array1[xmin - 2]; + nright = array1[xmin + 2]; + if (ppixdebug) { + fprintf(stderr, + "Splitting: xmin = %d, w = %d; nl = %d, nmin = %d, nr = %d\n", + xmin, w, nleft, nmin, nright); + } + if (nleft - nmin >= mindel && nright - nmin >= mindel) /* split */ + numaAddNumber(nasplit, xmin); + } + nsplit = numaGetCount(nasplit); + +#if 0 + if (ppixdebug && nsplit > 0) { + lept_mkdir("lept/split"); + gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/split/split", NULL); + } +#endif + + numaDestroy(&na1); + numaDestroy(&na2); + LEPT_FREE(array1); + LEPT_FREE(array2); + + if (nsplit == 0) { /* no splitting */ + numaDestroy(&nasplit); + box = boxCreate(0, 0, w, h); + boxaAddBox(boxad, box, L_INSERT); + return boxad; + } + + /* Use split points to generate b.b. after splitting */ + for (i = 0, xshift = 0; i < nsplit; i++) { + numaGetIValue(nasplit, i, &isplit); + box = boxCreate(xshift, 0, isplit - xshift, h); + boxaAddBox(boxad, box, L_INSERT); + xshift = isplit + 1; + } + box = boxCreate(xshift, 0, w - xshift, h); + boxaAddBox(boxad, box, L_INSERT); + numaDestroy(&nasplit); + + if (ppixdebug) { + pixdb = pixConvertTo32(pixs); + ncomp = boxaGetCount(boxad); + for (i = 0; i < ncomp; i++) { + box = boxaGetBox(boxad, i, L_CLONE); + pixRenderBoxBlend(pixdb, box, 1, 255, 0, 0, 0.5); + boxDestroy(&box); + } + *ppixdebug = pixdb; + } + + return boxad; +} + + +/*------------------------------------------------------------------* + * Extraction of lines of text * + *------------------------------------------------------------------*/ +/*! + * \brief pixExtractTextlines() + * + * \param[in] pixs any depth, assumed to have nearly horizontal text + * \param[in] maxw, maxh initial filtering: remove any components in pixs + * with components larger than maxw or maxh + * \param[in] minw, minh final filtering: remove extracted 'lines' + * with sizes smaller than minw or minh; use + * 0 for default. + * \param[in] adjw, adjh final adjustment of boxes representing each + * text line. If > 0, these increase the box + * size at each edge by this amount. + * \param[in] pixadb pixa for saving intermediate steps; NULL to omit + * \return pixa of textline images, including bounding boxes, or + * NULL on error + * + *
+ * Notes:
+ *      (1) This function assumes that textline fragments have sufficient
+ *          vertical separation and small enough skew so that a
+ *          horizontal dilation sufficient to join words will not join
+ *          textlines.  It does not guarantee that horizontally adjacent
+ *          textline fragments on the same line will be joined.
+ *      (2) For images with multiple columns, it attempts to avoid joining
+ *          textlines across the space between columns.  If that is not
+ *          a concern, you can also use pixExtractRawTextlines(),
+ *          which will join them with alacrity.
+ *      (3) This first removes components from pixs that are either
+ *          wide (> %maxw) or tall (> %maxh).
+ *      (4) A final filtering operation removes small components, such
+ *          that width < %minw or height < %minh.
+ *      (5) For reasonable accuracy, the resolution of pixs should be
+ *          at least 100 ppi.  For reasonable efficiency, the resolution
+ *          should not exceed 600 ppi.
+ *      (6) This can be used to determine if some region of a scanned
+ *          image is horizontal text.
+ *      (7) As an example, for a pix with resolution 300 ppi, a reasonable
+ *          set of parameters is:
+ *             pixExtractTextlines(pix, 150, 150, 36, 20, 5, 5, NULL);
+ *          The defaults minw and minh for 300 ppi are about 36 and 20,
+ *          so the same result is obtained with:
+ *             pixExtractTextlines(pix, 150, 150, 0, 0, 5, 5, NULL);
+ *      (8) The output pixa is composed of subimages, one for each textline,
+ *          and the boxa in the pixa tells where in %pixs each textline goes.
+ * 
+ */ +PIXA * +pixExtractTextlines(PIX *pixs, + l_int32 maxw, + l_int32 maxh, + l_int32 minw, + l_int32 minh, + l_int32 adjw, + l_int32 adjh, + PIXA *pixadb) +{ +char buf[64]; +l_int32 res, csize, empty; +BOXA *boxa1, *boxa2, *boxa3; +PIX *pix1, *pix2, *pix3; +PIXA *pixa1, *pixa2, *pixa3; + + PROCNAME("pixExtractTextlines"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Binarize carefully, if necessary */ + if (pixGetDepth(pixs) > 1) { + pix2 = pixConvertTo8(pixs, FALSE); + pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 190); + pix1 = pixThresholdToBinary(pix3, 150); + pixDestroy(&pix2); + pixDestroy(&pix3); + } else { + pix1 = pixClone(pixs); + } + pixZero(pix1, &empty); + if (empty) { + pixDestroy(&pix1); + L_INFO("no fg pixels in input image\n", procName); + return NULL; + } + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + + /* Remove any very tall or very wide connected components */ + pix2 = pixSelectBySize(pix1, maxw, maxh, 8, L_SELECT_IF_BOTH, + L_SELECT_IF_LT, NULL); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + pixDestroy(&pix1); + + /* Filter to solidify the text lines within the x-height region. + * The closing (csize) bridges gaps between words. The opening + * removes isolated bridges between textlines. */ + if ((res = pixGetXRes(pixs)) == 0) { + L_INFO("Resolution is not set: setting to 300 ppi\n", procName); + res = 300; + } + csize = L_MIN(120., 60.0 * res / 300.0); + snprintf(buf, sizeof(buf), "c%d.1 + o%d.1", csize, csize / 3); + pix3 = pixMorphCompSequence(pix2, buf, 0); + if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); + + /* Extract the connected components. These should be dilated lines */ + boxa1 = pixConnComp(pix3, &pixa1, 4); + if (pixadb) { + pix1 = pixaDisplayRandomCmap(pixa1, 0, 0); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + /* Set minw, minh if default is requested */ + minw = (minw != 0) ? minw : (l_int32)(0.12 * res); + minh = (minh != 0) ? minh : (l_int32)(0.07 * res); + + /* Remove line components that are too small */ + pixa2 = pixaSelectBySize(pixa1, minw, minh, L_SELECT_IF_BOTH, + L_SELECT_IF_GTE, NULL); + if (pixadb) { + pix1 = pixaDisplayRandomCmap(pixa2, 0, 0); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + pix1 = pixConvertTo32(pix2); + pixRenderBoxaArb(pix1, pixa2->boxa, 2, 255, 0, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + /* Selectively AND with the version before dilation, and save */ + boxa2 = pixaGetBoxa(pixa2, L_CLONE); + boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh); + pixa3 = pixClipRectangles(pix2, boxa3); + if (pixadb) { + pix1 = pixaDisplayRandomCmap(pixa3, 0, 0); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + pixDestroy(&pix2); + pixDestroy(&pix3); + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + return pixa3; +} + + +/*! + * \brief pixExtractRawTextlines() + * + * \param[in] pixs any depth, assumed to have nearly horizontal text + * \param[in] maxw, maxh initial filtering: remove any components in pixs + * with components larger than maxw or maxh; + * use 0 for default values. + * \param[in] adjw, adjh final adjustment of boxes representing each + * text line. If > 0, these increase the box + * size at each edge by this amount. + * \param[in] pixadb pixa for saving intermediate steps; NULL to omit + * \return pixa of textline images, including bounding boxes, or + * NULL on error + * + *
+ * Notes:
+ *      (1) This function assumes that textlines have sufficient
+ *          vertical separation and small enough skew so that a
+ *          horizontal dilation sufficient to join words will not join
+ *          textlines.  It aggressively joins textlines across multiple
+ *          columns, so if that is not desired, you must either (a) make
+ *          sure that %pixs is a single column of text or (b) use instead
+ *          pixExtractTextlines(), which is more conservative
+ *          about joining text fragments that have vertical overlap.
+ *      (2) This first removes components from pixs that are either
+ *          very wide (> %maxw) or very tall (> %maxh).
+ *      (3) For reasonable accuracy, the resolution of pixs should be
+ *          at least 100 ppi.  For reasonable efficiency, the resolution
+ *          should not exceed 600 ppi.
+ *      (4) This can be used to determine if some region of a scanned
+ *          image is horizontal text.
+ *      (5) As an example, for a pix with resolution 300 ppi, a reasonable
+ *          set of parameters is:
+ *             pixExtractRawTextlines(pix, 150, 150, 0, 0, NULL);
+ *      (6) The output pixa is composed of subimages, one for each textline,
+ *          and the boxa in the pixa tells where in %pixs each textline goes.
+ * 
+ */ +PIXA * +pixExtractRawTextlines(PIX *pixs, + l_int32 maxw, + l_int32 maxh, + l_int32 adjw, + l_int32 adjh, + PIXA *pixadb) +{ +char buf[64]; +l_int32 res, csize, empty; +BOXA *boxa1, *boxa2, *boxa3; +BOXAA *baa1; +PIX *pix1, *pix2, *pix3; +PIXA *pixa1, *pixa2; + + PROCNAME("pixExtractRawTextlines"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Set maxw, maxh if default is requested */ + if ((res = pixGetXRes(pixs)) == 0) { + L_INFO("Resolution is not set: setting to 300 ppi\n", procName); + res = 300; + } + maxw = (maxw != 0) ? maxw : (l_int32)(0.5 * res); + maxh = (maxh != 0) ? maxh : (l_int32)(0.5 * res); + + /* Binarize carefully, if necessary */ + if (pixGetDepth(pixs) > 1) { + pix2 = pixConvertTo8(pixs, FALSE); + pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 190); + pix1 = pixThresholdToBinary(pix3, 150); + pixDestroy(&pix2); + pixDestroy(&pix3); + } else { + pix1 = pixClone(pixs); + } + pixZero(pix1, &empty); + if (empty) { + pixDestroy(&pix1); + L_INFO("no fg pixels in input image\n", procName); + return NULL; + } + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + + /* Remove any very tall or very wide connected components */ + pix2 = pixSelectBySize(pix1, maxw, maxh, 8, L_SELECT_IF_BOTH, + L_SELECT_IF_LT, NULL); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + pixDestroy(&pix1); + + /* Filter to solidify the text lines within the x-height region. + * The closing (csize) bridges gaps between words. */ + csize = L_MIN(120., 60.0 * res / 300.0); + snprintf(buf, sizeof(buf), "c%d.1", csize); + pix3 = pixMorphCompSequence(pix2, buf, 0); + if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); + + /* Extract the connected components. These should be dilated lines */ + boxa1 = pixConnComp(pix3, &pixa1, 4); + if (pixadb) { + pix1 = pixaDisplayRandomCmap(pixa1, 0, 0); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + /* Do a 2-d sort, and generate a bounding box for each set of text + * line segments that is aligned horizontally (i.e., has vertical + * overlap) into a box representing a single text line. */ + baa1 = boxaSort2d(boxa1, NULL, -1, -1, 5); + boxaaGetExtent(baa1, NULL, NULL, NULL, &boxa2); + if (pixadb) { + pix1 = pixConvertTo32(pix2); + pixRenderBoxaArb(pix1, boxa2, 2, 255, 0, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + /* Optionally adjust the sides of each text line box, and then + * use the boxes to generate a pixa of the text lines. */ + boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh); + pixa2 = pixClipRectangles(pix2, boxa3); + if (pixadb) { + pix1 = pixaDisplayRandomCmap(pixa2, 0, 0); + pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); + pixaAddPix(pixadb, pix1, L_INSERT); + } + + pixDestroy(&pix2); + pixDestroy(&pix3); + pixaDestroy(&pixa1); + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + boxaaDestroy(&baa1); + return pixa2; +} + + +/*------------------------------------------------------------------* + * How many text columns * + *------------------------------------------------------------------*/ +/*! + * \brief pixCountTextColumns() + * + * \param[in] pixs 1 bpp + * \param[in] deltafract fraction of (max - min) to be used in the delta + * for extrema finding; typ 0.3 + * \param[in] peakfract fraction of (max - min) to be used to threshold + * the peak value; typ. 0.5 + * \param[in] clipfract fraction of image dimension removed on each side; + * typ. 0.1, which leaves w and h reduced by 0.8 + * \param[out] pncols number of columns; -1 if not determined + * \param[in] pixadb [optional] pre-allocated, for showing + * intermediate computation; use null to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is assumed that pixs has the correct resolution set.
+ *          If the resolution is 0, we set to 300 and issue a warning.
+ *      (2) If necessary, the image is scaled to between 37 and 75 ppi;
+ *          most of the processing is done at this resolution.
+ *      (3) If no text is found (essentially a blank page),
+ *          this returns ncols = 0.
+ *      (4) For debug output, input a pre-allocated pixa.
+ * 
+ */ +l_ok +pixCountTextColumns(PIX *pixs, + l_float32 deltafract, + l_float32 peakfract, + l_float32 clipfract, + l_int32 *pncols, + PIXA *pixadb) +{ +l_int32 w, h, res, i, n, npeak; +l_float32 scalefact, redfact, minval, maxval, val4, val5, fract; +BOX *box; +NUMA *na1, *na2, *na3, *na4, *na5; +PIX *pix1, *pix2, *pix3, *pix4, *pix5; + + PROCNAME("pixCountTextColumns"); + + if (!pncols) + return ERROR_INT("&ncols not defined", procName, 1); + *pncols = -1; /* init */ + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (deltafract < 0.15 || deltafract > 0.75) + L_WARNING("deltafract not in [0.15 ... 0.75]\n", procName); + if (peakfract < 0.25 || peakfract > 0.9) + L_WARNING("peakfract not in [0.25 ... 0.9]\n", procName); + if (clipfract < 0.0 || clipfract >= 0.5) + return ERROR_INT("clipfract not in [0.0 ... 0.5)\n", procName, 1); + if (pixadb) pixaAddPix(pixadb, pixs, L_COPY); + + /* Scale to between 37.5 and 75 ppi */ + if ((res = pixGetXRes(pixs)) == 0) { + L_WARNING("resolution undefined; set to 300\n", procName); + pixSetResolution(pixs, 300, 300); + res = 300; + } + if (res < 37) { + L_WARNING("resolution %d very low\n", procName, res); + scalefact = 37.5 / res; + pix1 = pixScale(pixs, scalefact, scalefact); + } else { + redfact = (l_float32)res / 37.5; + if (redfact < 2.0) + pix1 = pixClone(pixs); + else if (redfact < 4.0) + pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); + else if (redfact < 8.0) + pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 0, 0); + else if (redfact < 16.0) + pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 0); + else + pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 2); + } + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + + /* Crop inner 80% of image */ + pixGetDimensions(pix1, &w, &h, NULL); + box = boxCreate(clipfract * w, clipfract * h, + (1.0 - 2 * clipfract) * w, (1.0 - 2 * clipfract) * h); + pix2 = pixClipRectangle(pix1, box, NULL); + pixGetDimensions(pix2, &w, &h, NULL); + boxDestroy(&box); + if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); + + /* Deskew */ + pix3 = pixDeskew(pix2, 0); + if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); + + /* Close to increase column counts for text */ + pix4 = pixCloseSafeBrick(NULL, pix3, 5, 21); + if (pixadb) pixaAddPix(pixadb, pix4, L_COPY); + pixInvert(pix4, pix4); + na1 = pixCountByColumn(pix4, NULL); + + if (pixadb) { + gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/plot", NULL); + pix5 = pixRead("/tmp/lept/plot.png"); + pixaAddPix(pixadb, pix5, L_INSERT); + } + + /* Analyze the column counts. na4 gives the locations of + * the extrema in normalized units (0.0 to 1.0) across the + * cropped image. na5 gives the magnitude of the + * extrema, normalized to the dynamic range. The peaks + * are values that are at least peakfract of (max - min). */ + numaGetMax(na1, &maxval, NULL); + numaGetMin(na1, &minval, NULL); + fract = (l_float32)(maxval - minval) / h; /* is there much at all? */ + if (fract < 0.05) { + L_INFO("very little content on page; 0 text columns\n", procName); + *pncols = 0; + } else { + na2 = numaFindExtrema(na1, deltafract * (maxval - minval), &na3); + na4 = numaTransform(na2, 0, 1.0 / w); + na5 = numaTransform(na3, -minval, 1.0 / (maxval - minval)); + n = numaGetCount(na4); + for (i = 0, npeak = 0; i < n; i++) { + numaGetFValue(na4, i, &val4); + numaGetFValue(na5, i, &val5); + if (val4 > 0.3 && val4 < 0.7 && val5 >= peakfract) { + npeak++; + L_INFO("Peak(loc,val) = (%5.3f,%5.3f)\n", procName, val4, val5); + } + } + *pncols = npeak + 1; + numaDestroy(&na2); + numaDestroy(&na3); + numaDestroy(&na4); + numaDestroy(&na5); + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + numaDestroy(&na1); + return 0; +} + + +/*------------------------------------------------------------------* + * Decision text vs photo * + *------------------------------------------------------------------*/ +/*! + * \brief pixDecideIfText() + * + * \param[in] pixs any depth + * \param[in] box [optional] if null, use entire pixs + * \param[out] pistext 1 if text; 0 if photo; -1 if not determined or empty + * \param[in] pixadb [optional] pre-allocated, for showing intermediate + * computation; use NULL to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is assumed that pixs has the correct resolution set.
+ *          If the resolution is 0, we set to 300 and issue a warning.
+ *      (2) If necessary, the image is scaled to 300 ppi; most of the
+ *          processing is done at this resolution.
+ *      (3) Text is assumed to be in horizontal lines.
+ *      (4) Because thin vertical lines are removed before filtering for
+ *          text lines, this should identify tables as text.
+ *      (5) If %box is null and pixs contains both text lines and line art,
+ *          this function might return %istext == true.
+ *      (6) If the input pixs is empty, or for some other reason the
+ *          result can not be determined, return -1.
+ *      (7) For debug output, input a pre-allocated pixa.
+ * 
+ */ +l_ok +pixDecideIfText(PIX *pixs, + BOX *box, + l_int32 *pistext, + PIXA *pixadb) +{ +l_int32 i, empty, maxw, w, h, n1, n2, n3, minlines, big_comp; +l_float32 ratio1, ratio2; +L_BMF *bmf; +BOXA *boxa1, *boxa2, *boxa3, *boxa4, *boxa5; +PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7; +PIXA *pixa1; +SEL *sel1; + + PROCNAME("pixDecideIfText"); + + if (!pistext) + return ERROR_INT("&istext not defined", procName, 1); + *pistext = -1; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Crop, convert to 1 bpp, 300 ppi */ + if ((pix1 = pixPrepare1bpp(pixs, box, 0.1, 300)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + + pixZero(pix1, &empty); + if (empty) { + pixDestroy(&pix1); + L_INFO("pix is empty\n", procName); + return 0; + } + w = pixGetWidth(pix1); + + /* Identify and remove tall, thin vertical lines (as found in tables) + * that are up to 9 pixels wide. Make a hit-miss sel with an + * 81 pixel vertical set of hits and with 3 pairs of misses that + * are 10 pixels apart horizontally. It is necessary to use a + * hit-miss transform; if we only opened with a vertical line of + * hits, we would remove solid regions of pixels that are not + * text or vertical lines. */ + pix2 = pixCreate(11, 81, 1); + for (i = 0; i < 81; i++) + pixSetPixel(pix2, 5, i, 1); + sel1 = selCreateFromPix(pix2, 40, 5, NULL); + selSetElement(sel1, 20, 0, SEL_MISS); + selSetElement(sel1, 20, 10, SEL_MISS); + selSetElement(sel1, 40, 0, SEL_MISS); + selSetElement(sel1, 40, 10, SEL_MISS); + selSetElement(sel1, 60, 0, SEL_MISS); + selSetElement(sel1, 60, 10, SEL_MISS); + pix3 = pixHMT(NULL, pix1, sel1); + pix4 = pixSeedfillBinaryRestricted(NULL, pix3, pix1, 8, 5, 1000); + pix5 = pixXor(NULL, pix1, pix4); + pixDestroy(&pix2); + selDestroy(&sel1); + + /* Convert the text lines to separate long horizontal components */ + pix6 = pixMorphCompSequence(pix5, "c30.1 + o15.1 + c60.1 + o2.2", 0); + + /* Estimate the distance to the bottom of the significant region */ + if (box) { /* use full height */ + pixGetDimensions(pix6, NULL, &h, NULL); + } else { /* use height of region that has text lines */ + pixFindThreshFgExtent(pix6, 400, NULL, &h); + } + + if (pixadb) { + bmf = bmfCreate(NULL, 6); + pixaAddPixWithText(pixadb, pix1, 1, bmf, "threshold/crop to binary", + 0x0000ff00, L_ADD_BELOW); + pixaAddPixWithText(pixadb, pix3, 2, bmf, "hit-miss for vertical line", + 0x0000ff00, L_ADD_BELOW); + pixaAddPixWithText(pixadb, pix4, 2, bmf, "restricted seed-fill", + 0x0000ff00, L_ADD_BELOW); + pixaAddPixWithText(pixadb, pix5, 2, bmf, "remove using xor", + 0x0000ff00, L_ADD_BELOW); + pixaAddPixWithText(pixadb, pix6, 2, bmf, "make long horiz components", + 0x0000ff00, L_ADD_BELOW); + } + + /* Extract the connected components */ + if (pixadb) { + boxa1 = pixConnComp(pix6, &pixa1, 8); + pix7 = pixaDisplayRandomCmap(pixa1, 0, 0); + pixcmapResetColor(pixGetColormap(pix7), 0, 255, 255, 255); + pixaAddPixWithText(pixadb, pix7, 2, bmf, "show connected components", + 0x0000ff00, L_ADD_BELOW); + pixDestroy(&pix7); + pixaDestroy(&pixa1); + bmfDestroy(&bmf); + } else { + boxa1 = pixConnComp(pix6, NULL, 8); + } + + /* Analyze the connected components. The following conditions + * at 300 ppi must be satisfied if the image is text: + * (1) There are no components that are wider than 400 pixels and + * taller than 175 pixels. + * (2) The second longest component is at least 60% of the + * (possibly cropped) image width. This catches images + * that don't have any significant content. + * (3) Of the components that are at least 40% of the length + * of the longest (n2), at least 80% of them must not exceed + * 60 pixels in height. + * (4) The number of those long, thin components (n3) must + * equal or exceed a minimum that scales linearly with the + * image height. + * Most images that are not text fail more than one of these + * conditions. */ + boxa2 = boxaSort(boxa1, L_SORT_BY_WIDTH, L_SORT_DECREASING, NULL); + boxaGetBoxGeometry(boxa2, 1, NULL, NULL, &maxw, NULL); /* 2nd longest */ + boxa3 = boxaSelectBySize(boxa1, 0.4 * maxw, 0, L_SELECT_WIDTH, + L_SELECT_IF_GTE, NULL); + boxa4 = boxaSelectBySize(boxa3, 0, 60, L_SELECT_HEIGHT, + L_SELECT_IF_LTE, NULL); + boxa5 = boxaSelectBySize(boxa1, 400, 175, L_SELECT_IF_BOTH, + L_SELECT_IF_GT, NULL); + big_comp = (boxaGetCount(boxa5) == 0) ? 0 : 1; + n1 = boxaGetCount(boxa1); + n2 = boxaGetCount(boxa3); + n3 = boxaGetCount(boxa4); + ratio1 = (l_float32)maxw / (l_float32)w; + ratio2 = (l_float32)n3 / (l_float32)n2; + minlines = L_MAX(2, h / 125); + if (big_comp || ratio1 < 0.6 || ratio2 < 0.8 || n3 < minlines) + *pistext = 0; + else + *pistext = 1; + if (pixadb) { + if (*pistext == 1) { + L_INFO("This is text: \n n1 = %d, n2 = %d, n3 = %d, " + "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, " + "big_comp = %d\n", procName, n1, n2, n3, minlines, + maxw, ratio1, h, big_comp); + } else { + L_INFO("This is not text: \n n1 = %d, n2 = %d, n3 = %d, " + "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, " + "big_comp = %d\n", procName, n1, n2, n3, minlines, + maxw, ratio1, h, big_comp); + } + } + + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + boxaDestroy(&boxa4); + boxaDestroy(&boxa5); + pixDestroy(&pix1); + pixDestroy(&pix3); + pixDestroy(&pix4); + pixDestroy(&pix5); + pixDestroy(&pix6); + return 0; +} + + +/*! + * \brief pixFindThreshFgExtent() + * + * \param[in] pixs 1 bpp + * \param[in] thresh threshold number of pixels in row + * \param[out] ptop [optional] location of top of region + * \param[out] pbot [optional] location of bottom of region + * \return 0 if OK, 1 on error + */ +l_ok +pixFindThreshFgExtent(PIX *pixs, + l_int32 thresh, + l_int32 *ptop, + l_int32 *pbot) +{ +l_int32 i, n; +l_int32 *array; +NUMA *na; + + PROCNAME("pixFindThreshFgExtent"); + + if (ptop) *ptop = 0; + if (pbot) *pbot = 0; + if (!ptop && !pbot) + return ERROR_INT("nothing to determine", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + na = pixCountPixelsByRow(pixs, NULL); + n = numaGetCount(na); + array = numaGetIArray(na); + if (ptop) { + for (i = 0; i < n; i++) { + if (array[i] >= thresh) { + *ptop = i; + break; + } + } + } + if (pbot) { + for (i = n - 1; i >= 0; i--) { + if (array[i] >= thresh) { + *pbot = i; + break; + } + } + } + LEPT_FREE(array); + numaDestroy(&na); + return 0; +} + + +/*------------------------------------------------------------------* + * Decision: table vs text * + *------------------------------------------------------------------*/ +/*! + * \brief pixDecideIfTable() + * + * \param[in] pixs any depth, any resolution >= 75 ppi + * \param[in] box [optional] if null, use entire pixs + * \param[in] orient L_PORTRAIT_MODE, L_LANDSCAPE_MODE + * \param[out] pscore 0 - 4; -1 if not determined + * \param[in] pixadb [optional] pre-allocated, for showing intermediate + * computation; use NULL to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is assumed that pixs has the correct resolution set.
+ *          If the resolution is 0, we assume it is 300 ppi and issue a warning.
+ *      (2) If %orient == L_LANDSCAPE_MODE, the image is rotated 90 degrees
+ *          clockwise before being analyzed.
+ *      (3) The interpretation of the returned score:
+ *            -1     undetermined
+ *             0     no table
+ *             1     unlikely to have a table
+ *             2     likely to have a table
+ *             3     even more likely to have a table
+ *             4     extremely likely to have a table
+ *          * Setting the condition for finding a table at score >= 2 works
+ *            well, except for false positives on kanji and landscape text.
+ *          * These false positives can be removed by setting the condition
+ *            at score >= 3, but recall is lowered because it will not find
+ *            tables without either horizontal or vertical lines.
+ *      (4) Most of the processing takes place at 75 ppi.
+ *      (5) Internally, three numbers are determined, for horizontal and
+ *          vertical fg lines, and for vertical bg lines.  From these,
+ *          four tests are made to decide if there is a table occupying
+ *          a significant part of the image.
+ *      (6) Images have arbitrary content and would be likely to trigger
+ *          this detector, so they are checked for first, and if found,
+ *          return with a 0 (no table) score.
+ *      (7) Musical scores (tablature) are likely to trigger the detector.
+ *      (8) Tables of content with more than 2 columns are likely to
+ *          trigger the detector.
+ *      (9) For debug output, input a pre-allocated pixa.
+ * 
+ */ +l_ok +pixDecideIfTable(PIX *pixs, + BOX *box, + l_int32 orient, + l_int32 *pscore, + PIXA *pixadb) +{ +l_int32 empty, nhb, nvb, nvw, score, htfound; +PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; + + PROCNAME("pixDecideIfTable"); + + if (!pscore) + return ERROR_INT("&score not defined", procName, 1); + *pscore = -1; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Check if there is an image region. First convert to 1 bpp + * at 175 ppi. If an image is found, assume there is no table. */ + pix1 = pixPrepare1bpp(pixs, box, 0.1, 175); + pix2 = pixGenerateHalftoneMask(pix1, NULL, &htfound, NULL); + if (htfound && pixadb) pixaAddPix(pixadb, pix2, L_COPY); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (htfound) { + *pscore = 0; + L_INFO("pix has an image region\n", procName); + return 0; + } + + /* Crop, convert to 1 bpp, 75 ppi */ + if ((pix1 = pixPrepare1bpp(pixs, box, 0.05, 75)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + + pixZero(pix1, &empty); + if (empty) { + *pscore = 0; + pixDestroy(&pix1); + L_INFO("pix is empty\n", procName); + return 0; + } + + /* The 2x2 dilation on 75 ppi makes these two approaches very similar: + * (1) pix1 = pixPrepare1bpp(..., 300); // 300 ppi resolution + * pix2 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); + * (2) pix1 = pixPrepare1bpp(..., 75); // 75 ppi resolution + * pix2 = pixDilateBrick(NULL, pix1, 2, 2); + * But (2) is more efficient if the input image to pixPrepare1bpp() + * is not at 300 ppi. */ + pix2 = pixDilateBrick(NULL, pix1, 2, 2); + + /* Deskew both horizontally and vertically; rotate by 90 + * degrees if in landscape mode. */ + pix3 = pixDeskewBoth(pix2, 1); + if (pixadb) { + pixaAddPix(pixadb, pix2, L_COPY); + pixaAddPix(pixadb, pix3, L_COPY); + } + if (orient == L_LANDSCAPE_MODE) + pix4 = pixRotate90(pix3, 1); + else + pix4 = pixClone(pix3); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pix1 = pixClone(pix4); + pixDestroy(&pix4); + + /* Look for horizontal and vertical lines */ + pix2 = pixMorphSequence(pix1, "o100.1 + c1.4", 0); + pix3 = pixSeedfillBinary(NULL, pix2, pix1, 8); + pix4 = pixMorphSequence(pix1, "o1.100 + c4.1", 0); + pix5 = pixSeedfillBinary(NULL, pix4, pix1, 8); + pix6 = pixOr(NULL, pix3, pix5); + if (pixadb) { + pixaAddPix(pixadb, pix2, L_COPY); + pixaAddPix(pixadb, pix4, L_COPY); + pixaAddPix(pixadb, pix3, L_COPY); + pixaAddPix(pixadb, pix5, L_COPY); + pixaAddPix(pixadb, pix6, L_COPY); + } + pixCountConnComp(pix2, 8, &nhb); /* number of horizontal black lines */ + pixCountConnComp(pix4, 8, &nvb); /* number of vertical black lines */ + + /* Remove the lines */ + pixSubtract(pix1, pix1, pix6); + if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); + + /* Remove noise pixels */ + pix7 = pixMorphSequence(pix1, "c4.1 + o8.1", 0); + if (pixadb) pixaAddPix(pixadb, pix7, L_COPY); + + /* Look for vertical white space. Invert to convert white bg + * to fg. Use a single rank-1 2x reduction, which closes small + * fg holes, for the final processing at 37.5 ppi. + * The vertical opening is then about 3 inches on a 300 ppi image. + * We also remove vertical whitespace that is less than 5 pixels + * wide at this resolution (about 0.1 inches) */ + pixInvert(pix7, pix7); + pix8 = pixMorphSequence(pix7, "r1 + o1.100", 0); + pix9 = pixSelectBySize(pix8, 5, 0, 8, L_SELECT_WIDTH, + L_SELECT_IF_GTE, NULL); + pixCountConnComp(pix9, 8, &nvw); /* number of vertical white lines */ + if (pixadb) { + pixaAddPix(pixadb, pixScale(pix8, 2.0, 2.0), L_INSERT); + pixaAddPix(pixadb, pixScale(pix9, 2.0, 2.0), L_INSERT); + } + + /* Require at least 2 of the following 4 conditions for a table. + * Some tables do not have black (fg) lines, and for those we + * require more than 6 long vertical whitespace (bg) lines. */ + score = 0; + if (nhb > 1) score++; + if (nvb > 2) score++; + if (nvw > 3) score++; + if (nvw > 6) score++; + *pscore = score; + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + pixDestroy(&pix5); + pixDestroy(&pix6); + pixDestroy(&pix7); + pixDestroy(&pix8); + pixDestroy(&pix9); + return 0; +} + + +/*! + * \brief pixPrepare1bpp() + * + * \param[in] pixs any depth + * \param[in] box [optional] if null, use entire pixs + * \param[in] cropfract fraction to be removed from the boundary; + * use 0.0 to retain the entire image + * \param[in] outres desired resolution of output image; if the + * input image resolution is not set, assume + * 300 ppi; use 0 to skip scaling. + * \return pixd if OK, NULL on error + * + *
+ * Notes:
+ *      (1) This handles some common pre-processing operations,
+ *          where the page segmentation algorithm takes a 1 bpp image.
+ * 
+ */ +PIX * +pixPrepare1bpp(PIX *pixs, + BOX *box, + l_float32 cropfract, + l_int32 outres) +{ +l_int32 w, h, res; +l_float32 factor; +BOX *box1; +PIX *pix1, *pix2, *pix3, *pix4, *pix5; + + PROCNAME("pixPrepare1bpp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Crop the image. If no box is given, use %cropfract to remove + * pixels near the image boundary; this helps avoid false + * negatives from noise that is often found there. */ + if (box) { + pix1 = pixClipRectangle(pixs, box, NULL); + } else { + pixGetDimensions(pixs, &w, &h, NULL); + box1 = boxCreate((l_int32)(cropfract * w), (l_int32)(cropfract * h), + (l_int32)((1.0 - 2 * cropfract) * w), + (l_int32)((1.0 - 2 * cropfract) * h)); + pix1 = pixClipRectangle(pixs, box1, NULL); + boxDestroy(&box1); + } + + /* Convert to 1 bpp with adaptive background cleaning */ + if (pixGetDepth(pixs) > 1) { + pix2 = pixConvertTo8(pix1, 0); + pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 160); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (!pix3) { + L_INFO("pix cleaning failed\n", procName); + return NULL; + } + pix4 = pixThresholdToBinary(pix3, 200); + pixDestroy(&pix3); + } else { + pix4 = pixClone(pix1); + pixDestroy(&pix1); + } + + /* Scale the image to the requested output resolution; + do not scale if %outres <= 0 */ + if (outres <= 0) + return pix4; + if ((res = pixGetXRes(pixs)) == 0) { + L_WARNING("Resolution is not set: using 300 ppi\n", procName); + res = 300; + } + if (res != outres) { + factor = (l_float32)outres / (l_float32)res; + pix5 = pixScale(pix4, factor, factor); + } else { + pix5 = pixClone(pix4); + } + pixDestroy(&pix4); + return pix5; +} + + +/*------------------------------------------------------------------* + * Estimate the grayscale background value * + *------------------------------------------------------------------*/ +/*! + * \brief pixEstimateBackground() + * + * \param[in] pixs 8 bpp, with or without colormap + * \param[in] darkthresh pixels below this value are never considered + * part of the background; typ. 70; use 0 to skip + * \param[in] edgecrop fraction of half-width on each side, and of + * half-height at top and bottom, that are cropped + * \param[out] pbg estimated background, or 0 on error + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Caller should check that return bg value is > 0.
+ * 
+ */ +l_ok +pixEstimateBackground(PIX *pixs, + l_int32 darkthresh, + l_float32 edgecrop, + l_int32 *pbg) +{ +l_int32 w, h, sampling; +l_float32 fbg; +BOX *box; +PIX *pix1, *pix2, *pixm; + + PROCNAME("pixEstimateBackground"); + + if (!pbg) + return ERROR_INT("&bg not defined", procName, 1); + *pbg = 0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (darkthresh > 128) + L_WARNING("darkthresh unusually large\n", procName); + if (edgecrop < 0.0 || edgecrop >= 1.0) + return ERROR_INT("edgecrop not in [0.0 ... 1.0)", procName, 1); + + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + pixGetDimensions(pix1, &w, &h, NULL); + + /* Optionally crop inner part of image */ + if (edgecrop > 0.0) { + box = boxCreate(0.5 * edgecrop * w, 0.5 * edgecrop * h, + (1.0 - edgecrop) * w, (1.0 - edgecrop) * h); + pix2 = pixClipRectangle(pix1, box, NULL); + boxDestroy(&box); + } else { + pix2 = pixClone(pix1); + } + + /* We will use no more than 50K samples */ + sampling = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5)); + + /* Optionally make a mask over all pixels lighter than %darkthresh */ + pixm = NULL; + if (darkthresh > 0) { + pixm = pixThresholdToBinary(pix2, darkthresh); + pixInvert(pixm, pixm); + } + + pixGetRankValueMasked(pix2, pixm, 0, 0, sampling, 0.5, &fbg, NULL); + *pbg = (l_int32)(fbg + 0.5); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pixm); + return 0; +} + + +/*---------------------------------------------------------------------* + * Largest white or black rectangles in an image * + *---------------------------------------------------------------------*/ +/*! + * \brief pixFindLargeRectangles() + * + * \param[in] pixs 1 bpp + * \param[in] polarity 0 within background, 1 within foreground + * \param[in] nrect number of rectangles to be found + * \param[out] pboxa largest rectangles, sorted by decreasing area + * \param[in,out] ppixdb optional return output with rectangles drawn on it + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a greedy search to find the largest rectangles,
+ *          either black or white and without overlaps, in %pix.
+ *      (2) See pixFindLargestRectangle(), which is called multiple
+ *          times, for details.  On each call, the largest rectangle
+ *          found is painted, so that none of its pixels can be
+ *          used later, before calling it again.
+ *      (3) This function is surprisingly fast.  Although
+ *          pixFindLargestRectangle() runs at about 50 MPix/sec, when it
+ *          is run multiple times by pixFindLargeRectangles(), it processes
+ *          at 150 - 250 MPix/sec, and the time is approximately linear
+ *          in %nrect.  For example, for a 1 MPix image, searching for
+ *          the largest 50 boxes takes about 0.2 seconds.
+ * 
+ */ +l_ok +pixFindLargeRectangles(PIX *pixs, + l_int32 polarity, + l_int32 nrect, + BOXA **pboxa, + PIX **ppixdb) +{ +l_int32 i, op, bx, by, bw, bh; +BOX *box; +BOXA *boxa; +PIX *pix; + + PROCNAME("pixFindLargeRectangles"); + + if (ppixdb) *ppixdb = NULL; + if (!pboxa) + return ERROR_INT("&boxa not defined", procName, 1); + *pboxa = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (polarity != 0 && polarity != 1) + return ERROR_INT("invalid polarity", procName, 1); + if (nrect > 1000) { + L_WARNING("large num rectangles = %d requested; using 1000\n", + procName, nrect); + nrect = 1000; + } + + pix = pixCopy(NULL, pixs); + boxa = boxaCreate(nrect); + *pboxa = boxa; + + /* Sequentially find largest rectangle and fill with opposite color */ + for (i = 0; i < nrect; i++) { + if (pixFindLargestRectangle(pix, polarity, &box, NULL) == 1) { + boxDestroy(&box); + L_ERROR("failure in pixFindLargestRectangle\n", procName); + break; + } + boxaAddBox(boxa, box, L_INSERT); + op = (polarity == 0) ? PIX_SET : PIX_CLR; + boxGetGeometry(box, &bx, &by, &bw, &bh); + pixRasterop(pix, bx, by, bw, bh, op, NULL, 0, 0); + } + + if (ppixdb) + *ppixdb = pixDrawBoxaRandom(pixs, boxa, 3); + + pixDestroy(&pix); + return 0; +} + + +/*! + * \brief pixFindLargestRectangle() + * + * \param[in] pixs 1 bpp + * \param[in] polarity 0 within background, 1 within foreground + * \param[out] pbox largest area rectangle + * \param[in,out] ppixdb optional return output with rectangle drawn on it + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a simple and elegant solution to a problem in
+ *          computational geometry that at first appears to be quite
+ *          difficult: what is the largest rectangle that can be
+ *          placed in the image, covering only pixels of one polarity
+ *          (bg or fg)?  The solution is O(n), where n is the number
+ *          of pixels in the image, and it requires nothing more than
+ *          using a simple recursion relation in a single sweep of the image.
+ *      (2) In a sweep from UL to LR with left-to-right being the fast
+ *          direction, calculate the largest white rectangle at (x, y),
+ *          using previously calculated values at pixels #1 and #2:
+ *             #1:    (x, y - 1)
+ *             #2:    (x - 1, y)
+ *          We also need the most recent "black" pixels that were seen
+ *          in the current row and column.
+ *          Consider the largest area.  There are only two possibilities:
+ *             (a)  Min(w(1), horizdist) * (h(1) + 1)
+ *             (b)  Min(h(2), vertdist) * (w(2) + 1)
+ *          where
+ *             horizdist: the distance from the rightmost "black" pixel seen
+ *                        in the current row across to the current pixel
+ *             vertdist: the distance from the lowest "black" pixel seen
+ *                       in the current column down to the current pixel
+ *          and we choose the Max of (a) and (b).
+ *      (3) To convince yourself that these recursion relations are correct,
+ *          it helps to draw the maximum rectangles at #1 and #2.
+ *          Then for #1, you try to extend the rectangle down one line,
+ *          so that the height is h(1) + 1.  Do you get the full
+ *          width of #1, w(1)?  It depends on where the black pixels are
+ *          in the current row.  You know the final width is bounded by w(1)
+ *          and w(2) + 1, but the actual value depends on the distribution
+ *          of black pixels in the current row that are at a distance
+ *          from the current pixel that is between these limits.
+ *          We call that value "horizdist", and the area is then given
+ *          by the expression (a) above.  Using similar reasoning for #2,
+ *          where you attempt to extend the rectangle to the right
+ *          by 1 pixel, you arrive at (b).  The largest rectangle is
+ *          then found by taking the Max.
+ * 
+ */ +l_ok +pixFindLargestRectangle(PIX *pixs, + l_int32 polarity, + BOX **pbox, + PIX **ppixdb) +{ +l_int32 i, j, w, h, d, wpls, val; +l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2; +l_int32 xmax, ymax; /* LR corner of the largest rectangle */ +l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg; +l_int32 *lowestfg; +l_uint32 *datas, *lines; +l_uint32 **linew, **lineh; +BOX *box; +PIX *pixw, *pixh; /* keeps the width and height for the largest */ + /* rectangles whose LR corner is located there. */ + + PROCNAME("pixFindLargestRectangle"); + + if (ppixdb) *ppixdb = NULL; + if (!pbox) + return ERROR_INT("&box not defined", procName, 1); + *pbox = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1) + return ERROR_INT("pixs not 1 bpp", procName, 1); + if (polarity != 0 && polarity != 1) + return ERROR_INT("invalid polarity", procName, 1); + + /* Initialize lowest "fg" seen so far for each column */ + lowestfg = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); + for (i = 0; i < w; i++) + lowestfg[i] = -1; + + /* The combination (val ^ polarity) is the color for which we + * are searching for the maximum rectangle. For polarity == 0, + * we search in the bg (white). */ + pixw = pixCreate(w, h, 32); /* stores width */ + pixh = pixCreate(w, h, 32); /* stores height */ + linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL); + lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + maxarea = xmax = ymax = wmax = hmax = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + prevfg = -1; + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(lines, j); + if ((val ^ polarity) == 0) { /* bg (0) if polarity == 0, etc. */ + if (i == 0 && j == 0) { + wp = hp = 1; + } else if (i == 0) { + wp = linew[i][j - 1] + 1; + hp = 1; + } else if (j == 0) { + wp = 1; + hp = lineh[i - 1][j] + 1; + } else { + /* Expand #1 prev rectangle down */ + w1 = linew[i - 1][j]; + h1 = lineh[i - 1][j]; + horizdist = j - prevfg; + wmin = L_MIN(w1, horizdist); /* width of new rectangle */ + area1 = wmin * (h1 + 1); + + /* Expand #2 prev rectangle to right */ + w2 = linew[i][j - 1]; + h2 = lineh[i][j - 1]; + vertdist = i - lowestfg[j]; + hmin = L_MIN(h2, vertdist); /* height of new rectangle */ + area2 = hmin * (w2 + 1); + + if (area1 > area2) { + wp = wmin; + hp = h1 + 1; + } else { + wp = w2 + 1; + hp = hmin; + } + } + } else { /* fg (1) if polarity == 0; bg (0) if polarity == 1 */ + prevfg = j; + lowestfg[j] = i; + wp = hp = 0; + } + linew[i][j] = wp; + lineh[i][j] = hp; + if (wp * hp > maxarea) { + maxarea = wp * hp; + xmax = j; + ymax = i; + wmax = wp; + hmax = hp; + } + } + } + + /* Translate from LR corner to Box coords (UL corner, w, h) */ + box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax); + *pbox = box; + + if (ppixdb) { + *ppixdb = pixConvertTo8(pixs, TRUE); + pixRenderHashBoxArb(*ppixdb, box, 6, 2, L_NEG_SLOPE_LINE, 1, 255, 0, 0); + } + + LEPT_FREE(linew); + LEPT_FREE(lineh); + LEPT_FREE(lowestfg); + pixDestroy(&pixw); + pixDestroy(&pixh); + return 0; +} + + +/*---------------------------------------------------------------------* + * Generate rectangle inside connected component * + *---------------------------------------------------------------------*/ +/*! + * \brief pixFindRectangleInCC() + * + * \param[in] pixs 1 bpp, with sufficient closings to make the fg be + * a single c.c. that is a convex hull + * \param[in] boxs [optional] if NULL, %pixs should be a minimum + * container of a single c.c. + * \param[in] fract first and all consecutive lines found must be at + * least this fraction of the fast scan dimension + * \param[in] dir L_SCAN_HORIZONTAL, L_SCAN_VERTICAL; direction of + * fast scan + * \param[in] select L_GEOMETRIC_UNION, L_GEOMETRIC_INTERSECTION, + * L_LARGEST_AREA, L_SMALEST_AREA + * \param[in] debug if 1, generates output pdf showing intermediate + * computation and final result + * \return box of included rectangle, or NULL on error + * + *
+ * Notes:
+ *      (1) Computation is similar to pixFindLargestRectangle(), but allows
+ *          a different set of results to choose from.
+ *      (2) Select the fast scan direction.  Then, scanning in the slow
+ *          direction, finds the longest run of ON pixels in the fast
+ *          scan direction and look for the first first run that is longer
+ *          than %fract of the dimension.  Continues until a shorter run
+ *          is found.  This generates a box of ON pixels fitting into the c.c.
+ *      (3) Do this from both slow scan directions and use %select to get
+ *          a resulting box from these two.
+ *      (4) The extracted rectangle is not necessarily the largest that
+ *          can fit in the c.c.  To get that, use pixFindLargestRectangle().
+ */
+BOX *
+pixFindRectangleInCC(PIX       *pixs,
+                     BOX       *boxs,
+                     l_float32  fract,
+                     l_int32    dir,
+                     l_int32    select,
+                     l_int32    debug)
+{
+l_int32  x, y, i, j, w, h, w1, h1, w2, h2, found, res;
+l_int32  xfirst, xlast, xstart, yfirst, ylast, length;
+BOX     *box1, *box2, *box3, *box4, *box5;
+PIX     *pix1, *pix2, *pixdb1, *pixdb2;
+PIXA    *pixadb;
+
+    PROCNAME("pixFindRectangleInCC");
+
+    if (!pixs || pixGetDepth(pixs) != 1)
+        return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
+    if (fract <= 0.0 || fract > 1.0)
+        return (BOX *)ERROR_PTR("invalid fraction", procName, NULL);
+    if (dir != L_SCAN_VERTICAL && dir != L_SCAN_HORIZONTAL)
+        return (BOX *)ERROR_PTR("invalid scan direction", procName, NULL);
+    if (select != L_GEOMETRIC_UNION && select != L_GEOMETRIC_INTERSECTION &&
+        select != L_LARGEST_AREA && select != L_SMALLEST_AREA)
+        return (BOX *)ERROR_PTR("invalid select", procName, NULL);
+
+        /* Extract the c.c. if necessary */
+    x = y = 0;
+    if (boxs) {
+        pix1 = pixClipRectangle(pixs, boxs, NULL);
+        boxGetGeometry(boxs, &x, &y, NULL, NULL);
+    } else {
+        pix1 = pixClone(pixs);
+    }
+
+        /* All fast scans are horizontal; rotate 90 deg cw if necessary */
+    if (dir == L_SCAN_VERTICAL)
+        pix2 = pixRotate90(pix1, 1);
+    else  /* L_SCAN_HORIZONTAL */
+        pix2 = pixClone(pix1);
+    pixGetDimensions(pix2, &w, &h, NULL);
+
+    pixadb = (debug) ? pixaCreate(0) : NULL;
+    pixdb1 = NULL;
+    if (pixadb) {
+        lept_mkdir("lept/rect");
+        pixaAddPix(pixadb, pix1, L_CLONE);
+        pixdb1 = pixConvertTo32(pix2);
+    }
+    pixDestroy(&pix1);
+
+        /* Scanning down, find the first scanline with a long enough run.
+         * That run goes from (xfirst, yfirst) to (xlast, yfirst).  */
+    found = FALSE;
+    for (i = 0; i < h; i++) {
+        pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
+        if (length >= (l_int32)(fract * w + 0.5)) {
+            yfirst = i;
+            xfirst = xstart;
+            xlast = xfirst + length - 1;
+            found = TRUE;
+            break;
+        }
+    }
+    if (!found) {
+        L_WARNING("no run of sufficient size was found\n", procName);
+        pixDestroy(&pix2);
+        pixDestroy(&pixdb1);
+        pixaDestroy(&pixadb);
+        return NULL;
+    }
+
+         /* Continue down until the condition fails */
+    w1 = xlast - xfirst + 1;
+    h1 = h - yfirst;  /* initialize */
+    for (i = yfirst + 1; i < h; i++) {
+        pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
+        if (xstart > xfirst || (xstart + length - 1 < xlast) ||
+            i == h - 1) {
+            ylast = i - 1;
+            h1 = ylast - yfirst + 1;
+            break;
+        }
+    }
+    box1 = boxCreate(xfirst, yfirst, w1, h1);
+
+        /* Scanning up, find the first scanline with a long enough run.
+         * That run goes from (xfirst, ylast) to (xlast, ylast).  */
+    for (i = h - 1; i >= 0; i--) {
+        pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
+        if (length >= (l_int32)(fract * w + 0.5)) {
+            ylast = i;
+            xfirst = xstart;
+            xlast = xfirst + length - 1;
+            break;
+        }
+    }
+
+         /* Continue up until the condition fails */
+    w2 = xlast - xfirst + 1;
+    h2 = ylast + 1;  /* initialize */
+    for (i = ylast - 1; i >= 0; i--) {
+        pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length);
+        if (xstart > xfirst || (xstart + length - 1 < xlast) ||
+            i == 0) {
+            yfirst = i + 1;
+            h2 = ylast - yfirst + 1;
+            break;
+        }
+    }
+    box2 = boxCreate(xfirst, yfirst, w2, h2);
+    pixDestroy(&pix2);
+
+    if (pixadb) {
+        pixRenderBoxArb(pixdb1, box1, 2, 255, 0, 0);
+        pixRenderBoxArb(pixdb1, box2, 2, 0, 255, 0);
+        pixaAddPix(pixadb, pixdb1, L_INSERT);
+    }
+
+        /* Select the final result from the two boxes */
+    if (select == L_GEOMETRIC_UNION)
+        box3 = boxBoundingRegion(box1, box2);
+    else if (select == L_GEOMETRIC_INTERSECTION)
+        box3 = boxOverlapRegion(box1, box2);
+    else if (select == L_LARGEST_AREA)
+        box3 = (w1 * h1 >= w2 * h2) ? boxCopy(box1) : boxCopy(box2);
+    else  /* select == L_SMALLEST_AREA) */
+        box3 = (w1 * h1 <= w2 * h2) ? boxCopy(box1) : boxCopy(box2);
+    boxDestroy(&box1);
+    boxDestroy(&box2);
+
+        /* Rotate the box 90 degrees ccw if necessary */
+    box4 = NULL;
+    if (box3) {
+        if (dir == L_SCAN_VERTICAL)
+            box4 = boxRotateOrth(box3, w, h, 3);
+        else
+            box4 = boxCopy(box3);
+    }
+
+        /* Transform back to global coordinates if %boxs exists */
+    box5 = (box4) ? boxTransform(box4, x, y, 1.0, 1.0) : NULL;
+    boxDestroy(&box3);
+    boxDestroy(&box4);
+
+        /* Debug output */
+    if (pixadb) {
+        pixdb1 = pixConvertTo8(pixs, 0);
+        pixAddConstantGray(pixdb1, 190);
+        pixdb2 = pixConvertTo32(pixdb1);
+        if (box5) pixRenderBoxArb(pixdb2, box5, 4, 0, 0, 255);
+        pixaAddPix(pixadb, pixdb2, L_INSERT);
+        res = pixGetXRes(pixs);
+        L_INFO("Writing debug files to /tmp/lept/rect/\n", procName);
+        pixaConvertToPdf(pixadb, res, 1.0, L_DEFAULT_ENCODE, 75, NULL,
+                        "/tmp/lept/rect/fitrect.pdf");
+        pix1 = pixaDisplayTiledAndScaled(pixadb, 32, 800, 1, 0, 40, 2);
+        pixWrite("/tmp/lept/rect/fitrect.png", pix1, IFF_PNG);
+        pixDestroy(&pix1);
+        pixDestroy(&pixdb1);
+        pixaDestroy(&pixadb);
+    }
+
+    return box5;
+}
+
diff --git a/hgdriver/3rdparty/hgOCR/leptonica/paintcmap.c b/hgdriver/3rdparty/hgOCR/leptonica/paintcmap.c
new file mode 100644
index 0000000..1e919e0
--- /dev/null
+++ b/hgdriver/3rdparty/hgOCR/leptonica/paintcmap.c
@@ -0,0 +1,761 @@
+/*====================================================================*
+ -  Copyright (C) 2001 Leptonica.  All rights reserved.
+ -
+ -  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.
+ -
+ -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ -  ``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 ANY
+ -  CONTRIBUTORS 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.
+ *====================================================================*/
+
+/*!
+ * \file paintcmap.c
+ * 
+ *
+ *      These in-place functions paint onto colormap images.
+ *
+ *      Repaint selected pixels in region
+ *           l_int32     pixSetSelectCmap()
+ *
+ *      Repaint non-white pixels in region
+ *           l_int32     pixColorGrayRegionsCmap()
+ *           l_int32     pixColorGrayCmap()
+ *           l_int32     pixColorGrayMaskedCmap()
+ *           l_int32     addColorizedGrayToCmap()
+ *
+ *      Repaint selected pixels through mask
+ *           l_int32     pixSetSelectMaskedCmap()
+ *
+ *      Repaint all pixels through mask
+ *           l_int32     pixSetMaskedCmap()
+ *
+ *
+ *  The 'set select' functions condition the setting on a specific
+ *  pixel value (i.e., index into the colormap) of the underyling
+ *  Pix that is being modified.  The same conditioning is used in
+ *  pixBlendCmap().
+ *
+ *  The pixColorGrayCmap() function sets all truly gray (r = g = b) pixels,
+ *  with the exception of either black or white pixels, to a new color.
+ *
+ *  The pixSetSelectMaskedCmap() function conditions pixel painting
+ *  on both a specific pixel value and location within the fg mask.
+ *  By contrast, pixSetMaskedCmap() sets all pixels under the
+ *  mask foreground, without considering the initial pixel values.
+ * 
+ */ + +#include +#include "allheaders.h" + +/*-------------------------------------------------------------* + * Repaint selected pixels in region * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetSelectCmap() + * + * \param[in] pixs 1, 2, 4 or 8 bpp, with colormap + * \param[in] box [optional] region to set color; can be NULL + * \param[in] sindex colormap index of pixels to be changed + * \param[in] rval, gval, bval new color to paint + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) It sets all pixels in region that have the color specified
+ *          by the colormap index %sindex to the new color.
+ *      (3) %sindex must be in the existing colormap; otherwise an
+ *          error is returned.
+ *      (4) If the new color exists in the colormap, it is used;
+ *          otherwise, it is added to the colormap.  If it cannot be
+ *          added because the colormap is full, an error is returned.
+ *      (5) If %box is NULL, applies function to the entire image; otherwise,
+ *          clips the operation to the intersection of the box and pix.
+ *      (6) An example of use would be to set to a specific color all
+ *          the light (background) pixels within a certain region of
+ *          a 3-level 2 bpp image, while leaving light pixels outside
+ *          this region unchanged.
+ * 
+ */ +l_ok +pixSetSelectCmap(PIX *pixs, + BOX *box, + l_int32 sindex, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls; +l_int32 index; /* of new color to be set */ +l_uint32 *lines, *datas; +PIXCMAP *cmap; + + PROCNAME("pixSetSelectCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap", procName, 1); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8) + return ERROR_INT("depth not in {1,2,4,8}", procName, 1); + + /* Add new color if necessary; get index of this color in cmap */ + n = pixcmapGetCount(cmap); + if (sindex >= n) + return ERROR_INT("sindex too large; no cmap entry", procName, 1); + if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ + if (pixcmapAddColor(cmap, rval, gval, bval)) + return ERROR_INT("error adding cmap entry", procName, 1); + else + index = n; /* we've added one color */ + } + + /* Determine the region of substitution */ + pixGetDimensions(pixs, &w, &h, NULL); + if (!box) { + x1 = y1 = 0; + x2 = w; + y2 = h; + } else { + boxGetGeometry(box, &x1, &y1, &bw, &bh); + x2 = x1 + bw - 1; + y2 = y1 + bh - 1; + } + + /* Replace pixel value sindex by index in the region */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + for (i = y1; i <= y2; i++) { + if (i < 0 || i >= h) /* clip */ + continue; + lines = datas + i * wpls; + for (j = x1; j <= x2; j++) { + if (j < 0 || j >= w) /* clip */ + continue; + switch (d) { + case 1: + val = GET_DATA_BIT(lines, j); + if (val == sindex) { + if (index == 0) + CLEAR_DATA_BIT(lines, j); + else + SET_DATA_BIT(lines, j); + } + break; + case 2: + val = GET_DATA_DIBIT(lines, j); + if (val == sindex) + SET_DATA_DIBIT(lines, j, index); + break; + case 4: + val = GET_DATA_QBIT(lines, j); + if (val == sindex) + SET_DATA_QBIT(lines, j, index); + break; + case 8: + val = GET_DATA_BYTE(lines, j); + if (val == sindex) + SET_DATA_BYTE(lines, j, index); + break; + default: + return ERROR_INT("depth not in {1,2,4,8}", procName, 1); + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Repaint gray pixels in region * + *-------------------------------------------------------------*/ +/*! + * \brief pixColorGrayRegionsCmap() + * + * \param[in] pixs 8 bpp, with colormap + * \param[in] boxa of regions in which to apply color + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] rval, gval, bval target color + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
+ *          preserving antialiasing.
+ *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
+ *          preserving antialiasing.  See pixColorGrayCmap() for details.
+ *      (3) This can also be called through pixColorGrayRegions().
+ *      (4) This increases the colormap size by the number of
+ *          different gray (non-black or non-white) colors in the
+ *          selected regions of pixs.  If there is not enough room in
+ *          the colormap for this expansion, it returns 1 (error),
+ *          and the caller should check the return value.
+ *      (5) Because two boxes in %boxa can overlap, pixels that
+ *          are colorized in the first box must be excluded in the
+ *          second because their value exceeds the size of the map.
+ * 
+ */ +l_ok +pixColorGrayRegionsCmap(PIX *pixs, + BOXA *boxa, + l_int32 type, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, k, w, h, n, nc, x1, y1, x2, y2, bw, bh, wpl; +l_int32 val, nval; +l_int32 *map; +l_uint32 *line, *data; +BOX *box; +NUMA *na; +PIXCMAP *cmap; + + PROCNAME("pixColorGrayRegionsCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap", procName, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("depth not 8 bpp", procName, 1); + if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) + return ERROR_INT("invalid type", procName, 1); + + nc = pixcmapGetCount(cmap); + if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) + return ERROR_INT("no room; cmap full", procName, 1); + map = numaGetIArray(na); + numaDestroy(&na); + if (!map) + return ERROR_INT("map not made", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + n = boxaGetCount(boxa); + for (k = 0; k < n; k++) { + box = boxaGetBox(boxa, k, L_CLONE); + boxGetGeometry(box, &x1, &y1, &bw, &bh); + x2 = x1 + bw - 1; + y2 = y1 + bh - 1; + + /* Remap gray pixels in the region */ + for (i = y1; i <= y2; i++) { + if (i < 0 || i >= h) /* clip */ + continue; + line = data + i * wpl; + for (j = x1; j <= x2; j++) { + if (j < 0 || j >= w) /* clip */ + continue; + val = GET_DATA_BYTE(line, j); + if (val >= nc) continue; /* from overlapping b.b. */ + nval = map[val]; + if (nval != 256) + SET_DATA_BYTE(line, j, nval); + } + } + boxDestroy(&box); + } + + LEPT_FREE(map); + return 0; +} + + +/*! + * \brief pixColorGrayCmap() + * + * \param[in] pixs 2, 4 or 8 bpp, with colormap + * \param[in] box [optional] region to set color; can be NULL + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] rval, gval, bval target color + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
+ *          preserving antialiasing.
+ *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
+ *          preserving antialiasing.
+ *      (3) %box gives the region to apply color; if NULL, this
+ *          colorizes the entire image.
+ *      (4) If the cmap is only 2 or 4 bpp, pixs is converted in-place
+ *          to an 8 bpp cmap.  A 1 bpp cmap is not a valid input pix.
+ *      (5) This can also be called through pixColorGray().
+ *      (6) This operation increases the colormap size by the number of
+ *          different gray (non-black or non-white) colors in the
+ *          input colormap.  If there is not enough room in the colormap
+ *          for this expansion, it returns 1 (error), and the caller
+ *          should check the return value.
+ *      (7) Using the darkness of each original pixel in the rect,
+ *          it generates a new color (based on the input rgb values).
+ *          If %type == L_PAINT_LIGHT, the new color is a (generally)
+ *          darken-to-black version of the input rgb color, where the
+ *          amount of darkening increases with the darkness of the
+ *          original pixel color.
+ *          If %type == L_PAINT_DARK, the new color is a (generally)
+ *          faded-to-white version of the input rgb color, where the
+ *          amount of fading increases with the brightness of the
+ *          original pixel color.
+ * 
+ */ +l_ok +pixColorGrayCmap(PIX *pixs, + BOX *box, + l_int32 type, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h, d, ret; +PIX *pixt; +BOXA *boxa; +PIXCMAP *cmap; + + PROCNAME("pixColorGrayCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("depth not in {2, 4, 8}", procName, 1); + if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) + return ERROR_INT("invalid type", procName, 1); + + /* If 2 bpp or 4 bpp, convert in-place to 8 bpp. */ + if (d == 2 || d == 4) { + pixt = pixConvertTo8(pixs, 1); + pixTransferAllData(pixs, &pixt, 0, 0); + } + + /* If box == NULL, color the entire image */ + boxa = boxaCreate(1); + if (box) { + boxaAddBox(boxa, box, L_COPY); + } else { + box = boxCreate(0, 0, w, h); + boxaAddBox(boxa, box, L_INSERT); + } + ret = pixColorGrayRegionsCmap(pixs, boxa, type, rval, gval, bval); + + boxaDestroy(&boxa); + return ret; +} + + +/*! + * \brief pixColorGrayMaskedCmap() + * + * \param[in] pixs 8 bpp, with colormap + * \param[in] pixm 1 bpp mask, through which to apply color + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] rval, gval, bval target color + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
+ *          preserving antialiasing.
+ *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
+ *          preserving antialiasing.  See pixColorGrayCmap() for details.
+ *      (3) This increases the colormap size by the number of
+ *          different gray (non-black or non-white) colors in the
+ *          input colormap.  If there is not enough room in the colormap
+ *          for this expansion, it returns 1 (error).
+ * 
+ */ +l_ok +pixColorGrayMaskedCmap(PIX *pixs, + PIX *pixm, + l_int32 type, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, w, h, wm, hm, wmin, hmin, wpl, wplm; +l_int32 val, nval; +l_int32 *map; +l_uint32 *line, *data, *linem, *datam; +NUMA *na; +PIXCMAP *cmap; + + PROCNAME("pixColorGrayMaskedCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap", procName, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("depth not 8 bpp", procName, 1); + if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) + return ERROR_INT("invalid type", procName, 1); + + if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) + return ERROR_INT("no room; cmap full", procName, 1); + map = numaGetIArray(na); + numaDestroy(&na); + if (!map) + return ERROR_INT("map not made", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + pixGetDimensions(pixm, &wm, &hm, NULL); + if (wm != w) + L_WARNING("wm = %d differs from w = %d\n", procName, wm, w); + if (hm != h) + L_WARNING("hm = %d differs from h = %d\n", procName, hm, h); + wmin = L_MIN(w, wm); + hmin = L_MIN(h, hm); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + + /* Remap gray pixels in the region */ + for (i = 0; i < hmin; i++) { + line = data + i * wpl; + linem = datam + i * wplm; + for (j = 0; j < wmin; j++) { + if (GET_DATA_BIT(linem, j) == 0) + continue; + val = GET_DATA_BYTE(line, j); + nval = map[val]; + if (nval != 256) + SET_DATA_BYTE(line, j, nval); + } + } + + LEPT_FREE(map); + return 0; +} + + +/*! + * \brief addColorizedGrayToCmap() + * + * \param[in] cmap from 2 or 4 bpp pix + * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK + * \param[in] rval, gval, bval target color + * \param[out] pna [optional] table for mapping new cmap entries + * \return 0 if OK; 1 on error; 2 if new colors will not fit in cmap. + * + *
+ * Notes:
+ *      (1) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
+ *          preserving antialiasing.
+ *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
+ *          preserving antialiasing.
+ *      (2) This increases the colormap size by the number of
+ *          different gray (non-black or non-white) colors in the
+ *          input colormap.  If there is not enough room in the colormap
+ *          for this expansion, it returns 1 (treated as a warning);
+ *          the caller should check the return value.
+ *      (3) This can be used to determine if the new colors will fit in
+ *          the cmap, using null for &na.  Returns 0 if they fit; 2 if
+ *          they don't fit.
+ *      (4) The mapping table contains, for each gray color found, the
+ *          index of the corresponding colorized pixel.  Non-gray
+ *          pixels are assigned the invalid index 256.
+ *      (5) See pixColorGrayCmap() for usage.
+ * 
+ */ +l_ok +addColorizedGrayToCmap(PIXCMAP *cmap, + l_int32 type, + l_int32 rval, + l_int32 gval, + l_int32 bval, + NUMA **pna) +{ +l_int32 i, n, erval, egval, ebval, nrval, ngval, nbval, newindex; +NUMA *na; + + PROCNAME("addColorizedGrayToCmap"); + + if (pna) *pna = NULL; + if (!cmap) + return ERROR_INT("cmap not defined", procName, 1); + if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) + return ERROR_INT("invalid type", procName, 1); + + n = pixcmapGetCount(cmap); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pixcmapGetColor(cmap, i, &erval, &egval, &ebval); + if (type == L_PAINT_LIGHT) { + if (erval == egval && erval == ebval && erval != 0) { + nrval = (l_int32)(rval * (l_float32)erval / 255.); + ngval = (l_int32)(gval * (l_float32)egval / 255.); + nbval = (l_int32)(bval * (l_float32)ebval / 255.); + if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) { + numaDestroy(&na); + L_WARNING("no room; colormap full\n", procName); + return 2; + } + numaAddNumber(na, newindex); + } else { + numaAddNumber(na, 256); /* invalid number; not gray */ + } + } else { /* L_PAINT_DARK */ + if (erval == egval && erval == ebval && erval != 255) { + nrval = rval + + (l_int32)((255. - rval) * (l_float32)erval / 255.); + ngval = gval + + (l_int32)((255. - gval) * (l_float32)egval / 255.); + nbval = bval + + (l_int32)((255. - bval) * (l_float32)ebval / 255.); + if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) { + numaDestroy(&na); + L_WARNING("no room; colormap full\n", procName); + return 2; + } + numaAddNumber(na, newindex); + } else { + numaAddNumber(na, 256); /* invalid number; not gray */ + } + } + } + + if (pna) + *pna = na; + else + numaDestroy(&na); + return 0; +} + + +/*-------------------------------------------------------------* + * Repaint selected pixels through mask * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetSelectMaskedCmap() + * + * \param[in] pixs 2, 4 or 8 bpp, with colormap + * \param[in] pixm [optional] 1 bpp mask; no-op if NULL + * \param[in] x, y UL corner of mask relative to pixs + * \param[in] sindex cmap index of pixels in pixs to be changed + * \param[in] rval, gval, bval new color to substitute + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) This paints through the fg of pixm and replaces all pixels
+ *          in pixs that have the value %sindex with the new color.
+ *      (3) If pixm == NULL, a warning is given.
+ *      (4) %sindex must be in the existing colormap; otherwise an
+ *          error is returned.
+ *      (5) If the new color exists in the colormap, it is used;
+ *          otherwise, it is added to the colormap.  If the colormap
+ *          is full, an error is returned.
+ * 
+ */ +l_ok +pixSetSelectMaskedCmap(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 sindex, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 i, j, w, h, d, n, wm, hm, wpls, wplm, val; +l_int32 index; /* of new color to be set */ +l_uint32 *lines, *linem, *datas, *datam; +PIXCMAP *cmap; + + PROCNAME("pixSetSelectMaskedCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap", procName, 1); + if (!pixm) { + L_WARNING("no mask; nothing to do\n", procName); + return 0; + } + + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("depth not in {2, 4, 8}", procName, 1); + + /* add new color if necessary; get index of this color in cmap */ + n = pixcmapGetCount(cmap); + if (sindex >= n) + return ERROR_INT("sindex too large; no cmap entry", procName, 1); + if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ + if (pixcmapAddColor(cmap, rval, gval, bval)) + return ERROR_INT("error adding cmap entry", procName, 1); + else + index = n; /* we've added one color */ + } + + /* replace pixel value sindex by index when fg pixel in pixmc + * overlays it */ + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wm = pixGetWidth(pixm); + hm = pixGetHeight(pixm); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + for (i = 0; i < hm; i++) { + if (i + y < 0 || i + y >= h) continue; + lines = datas + (y + i) * wpls; + linem = datam + i * wplm; + for (j = 0; j < wm; j++) { + if (j + x < 0 || j + x >= w) continue; + if (GET_DATA_BIT(linem, j)) { + switch (d) { + case 2: + val = GET_DATA_DIBIT(lines, x + j); + if (val == sindex) + SET_DATA_DIBIT(lines, x + j, index); + break; + case 4: + val = GET_DATA_QBIT(lines, x + j); + if (val == sindex) + SET_DATA_QBIT(lines, x + j, index); + break; + case 8: + val = GET_DATA_BYTE(lines, x + j); + if (val == sindex) + SET_DATA_BYTE(lines, x + j, index); + break; + default: + return ERROR_INT("depth not in {1,2,4,8}", procName, 1); + } + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Repaint all pixels through mask * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetMaskedCmap() + * + * \param[in] pixs 2, 4 or 8 bpp, colormapped + * \param[in] pixm [optional] 1 bpp mask; no-op if NULL + * \param[in] x, y origin of pixm relative to pixs; + * can be negative + * \param[in] rval, gval, bval new color to set at each masked pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) It paints a single color through the mask (as a stencil).
+ *      (3) The mask origin is placed at (%x,%y) on %pixs, and the
+ *          operation is clipped to the intersection of the mask and pixs.
+ *      (4) If %pixm == NULL, a warning is given.
+ *      (5) Typically, %pixm is a small binary mask located somewhere
+ *          on the larger %pixs.
+ *      (6) If the color is in the colormap, it is used.  Otherwise,
+ *          it is added if possible; an error is returned if the
+ *          colormap is already full.
+ * 
+ */ +l_ok +pixSetMaskedCmap(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h, d, wpl, wm, hm, wplm; +l_int32 i, j, index; +l_uint32 *data, *datam, *line, *linem; +PIXCMAP *cmap; + + PROCNAME("pixSetMaskedCmap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if ((cmap = pixGetColormap(pixs)) == NULL) + return ERROR_INT("no colormap in pixs", procName, 1); + if (!pixm) { + L_WARNING("no mask; nothing to do\n", procName); + return 0; + } + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("depth not in {2,4,8}", procName, 1); + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + + /* Add new color if necessary; store in 'index' */ + if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ + if (pixcmapAddColor(cmap, rval, gval, bval)) + return ERROR_INT("no room in cmap", procName, 1); + index = pixcmapGetCount(cmap) - 1; + } + + pixGetDimensions(pixs, &w, &h, NULL); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + pixGetDimensions(pixm, &wm, &hm, NULL); + wplm = pixGetWpl(pixm); + datam = pixGetData(pixm); + for (i = 0; i < hm; i++) { + if (i + y < 0 || i + y >= h) continue; + line = data + (i + y) * wpl; + linem = datam + i * wplm; + for (j = 0; j < wm; j++) { + if (j + x < 0 || j + x >= w) continue; + if (GET_DATA_BIT(linem, j)) { /* paint color */ + switch (d) { + case 2: + SET_DATA_DIBIT(line, j + x, index); + break; + case 4: + SET_DATA_QBIT(line, j + x, index); + break; + case 8: + SET_DATA_BYTE(line, j + x, index); + break; + default: + return ERROR_INT("depth not in {2,4,8}", procName, 1); + } + } + } + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/parseprotos.c b/hgdriver/3rdparty/hgOCR/leptonica/parseprotos.c new file mode 100644 index 0000000..17db265 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/parseprotos.c @@ -0,0 +1,975 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * \file parseprotos.c + *
+ *
+ *       char             *parseForProtos()
+ *
+ *    Static helpers
+ *       static l_int32    getNextNonCommentLine()
+ *       static l_int32    getNextNonBlankLine()
+ *       static l_int32    getNextNonDoubleSlashLine()
+ *       static l_int32    searchForProtoSignature()
+ *       static char      *captureProtoSignature()
+ *       static char      *cleanProtoSignature()
+ *       static l_int32    skipToEndOfFunction()
+ *       static l_int32    skipToMatchingBrace()
+ *       static l_int32    skipToSemicolon()
+ *       static l_int32    getOffsetForCharacter()
+ *       static l_int32    getOffsetForMatchingRP()
+ * 
+ */ + +#include +#include "allheaders.h" + +//static const l_int32 Bufsize = 2048; /* max token size */ +#define Bufsize 2048 + +static l_int32 getNextNonCommentLine(SARRAY *sa, l_int32 start, l_int32 *pnext); +static l_int32 getNextNonBlankLine(SARRAY *sa, l_int32 start, l_int32 *pnext); +static l_int32 getNextNonDoubleSlashLine(SARRAY *sa, l_int32 start, + l_int32 *pnext); +static l_int32 searchForProtoSignature(SARRAY *sa, l_int32 begin, + l_int32 *pstart, l_int32 *pstop, l_int32 *pcharindex, + l_int32 *pfound); +static char * captureProtoSignature(SARRAY *sa, l_int32 start, l_int32 stop, + l_int32 charindex); +static char * cleanProtoSignature(char *str); +static l_int32 skipToEndOfFunction(SARRAY *sa, l_int32 start, + l_int32 charindex, l_int32 *pnext); +static l_int32 skipToMatchingBrace(SARRAY *sa, l_int32 start, + l_int32 lbindex, l_int32 *prbline, l_int32 *prbindex); +static l_int32 skipToSemicolon(SARRAY *sa, l_int32 start, + l_int32 charindex, l_int32 *pnext); +static l_int32 getOffsetForCharacter(SARRAY *sa, l_int32 start, char tchar, + l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset); +static l_int32 getOffsetForMatchingRP(SARRAY *sa, l_int32 start, + l_int32 soffsetlp, l_int32 boffsetlp, l_int32 toffsetlp, + l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset); + + +/* + * \brief parseForProtos() + * + * \param[in] filein output of cpp + * \param[in] prestring [optional] string that prefaces each decl; + * use NULL to omit + * \return parsestr string of function prototypes, or NULL on error + * + *
+ * Notes:
+ *      (1) We parse the output of cpp:
+ *              cpp -ansi 
+ *          Three plans were attempted, with success on the third.
+ *      (2) Plan 1.  A cursory examination of the cpp output indicated that
+ *          every function was preceded by a cpp comment statement.
+ *          So we just need to look at statements beginning after comments.
+ *          Unfortunately, this is NOT the case.  Some functions start
+ *          without cpp comment lines, typically when there are no
+ *          comments in the source that immediately precede the function.
+ *      (3) Plan 2.  Consider the keywords in the language that start
+ *          parts of the cpp file.  Some, like 'enum', 'union' and
+ *          'struct', are followed after a while by '{', and eventually
+ *          end with '}, plus an optional token and a final ';'.
+ *          Others, like 'extern', 'static' and 'typedef', are never
+ *          the beginnings of global function definitions.   Function
+ *          prototypes have one or more sets of '(' followed eventually
+ *          by a ')', and end with ';'.  But function definitions have
+ *          tokens, followed by '(', more tokens, ')' and then
+ *          immediately a '{'.  We would generate a prototype from this
+ *          by adding a ';' to all tokens up to the ')'.  So we use
+ *          these special tokens to decide what we are parsing.  And
+ *          whenever a function definition is found and the prototype
+ *          extracted, we skip through the rest of the function
+ *          past the corresponding '}'.  This token ends a line, and
+ *          is often on a line of its own.  But as it turns out,
+ *          the only keyword we need to consider is 'static'.
+ *      (4) Plan 3.  Consider the parentheses and braces for various
+ *          declarations.  A struct, enum, or union has a pair of
+ *          braces followed by a semicolon.  With the exception of an
+ *          __attribute__ declaration for a struct, they cannot have parentheses
+ *          before the left brace, but a struct can have lots of parentheses
+ *          within the brace set.  A function prototype has no braces.
+ *          A function declaration can have sets of left and right
+ *          parentheses, but these are followed by a left brace.
+ *          So plan 3 looks at the way parentheses and braces are
+ *          organized.  Once the beginning of a function definition
+ *          is found, the prototype is extracted and we search for
+ *          the ending right brace.
+ *      (5) To find the ending right brace, it is necessary to do some
+ *          careful parsing.  For example, in this file, we have
+ *          left and right braces as characters, and these must not
+ *          be counted.  Somewhat more tricky, the file fhmtauto.c
+ *          generates code, and includes a right brace in a string.
+ *          So we must not include braces that are in strings.  But how
+ *          do we know if something is inside a string?  Keep state,
+ *          starting with not-inside, and every time you hit a double quote
+ *          that is not escaped, toggle the condition.  Any brace
+ *          found in the state of being within a string is ignored.
+ *      (6) When a prototype is extracted, it is put in a canonical
+ *          form (i.e., cleaned up).  Finally, we check that it is
+ *          not static and save it.  (If static, it is ignored).
+ *      (7) The %prestring for unix is NULL; it is included here so that
+ *          you can use Microsoft's declaration for importing or
+ *          exporting to a dll.  See environ.h for examples of use.
+ *          Here, we set: %prestring = "LEPT_DLL ".  Note in particular
+ *          the space character that will separate 'LEPT_DLL' from
+ *          the standard unix prototype that follows.
+ * 
+ */ +char * +parseForProtos(const char *filein, + const char *prestring) +{ +char *strdata, *str, *newstr, *parsestr, *secondword; +l_int32 start, next, stop, charindex, found; +size_t nbytes; +SARRAY *sa, *saout, *satest; + + PROCNAME("parseForProtos"); + + if (!filein) + return (char *)ERROR_PTR("filein not defined", procName, NULL); + + /* Read in the cpp output into memory, one string for each + * line in the file, omitting blank lines. */ + strdata = (char *)l_binaryRead(filein, &nbytes); + sa = sarrayCreateLinesFromString(strdata, 0); + + saout = sarrayCreate(0); + next = 0; + while (1) { /* repeat after each non-static prototype is extracted */ + searchForProtoSignature(sa, next, &start, &stop, &charindex, &found); + if (!found) + break; +/* fprintf(stderr, " start = %d, stop = %d, charindex = %d\n", + start, stop, charindex); */ + str = captureProtoSignature(sa, start, stop, charindex); + + /* Make sure that the signature found by cpp does not begin with + * static, extern or typedef. We get 'extern' declarations + * from header files, and with some versions of cpp running on + * #include we get something of the form: + * extern ... (( ... )) ... ( ... ) { ... + * For this, the 1st '(' is the lp, the 2nd ')' is the rp, + * and there is a lot of garbage between the rp and the lp. + * It is easiest to simply reject any signature that starts + * with 'extern'. Note also that an 'extern' token has been + * prepended to each prototype, so the 'static' or + * 'extern' keywords we are looking for, if they exist, + * would be the second word. We also have a typedef in + * bmpio.c that has the form: + * typedef struct __attribute__((....)) { ...} ... ; + * This is avoided by blacklisting 'typedef' along with 'extern' + * and 'static'. */ + satest = sarrayCreateWordsFromString(str); + secondword = sarrayGetString(satest, 1, L_NOCOPY); + if (strcmp(secondword, "static") && /* not static */ + strcmp(secondword, "extern") && /* not extern */ + strcmp(secondword, "typedef")) { /* not typedef */ + if (prestring) { /* prepend it to the prototype */ + newstr = stringJoin(prestring, str); + sarrayAddString(saout, newstr, L_INSERT); + LEPT_FREE(str); + } else { + sarrayAddString(saout, str, L_INSERT); + } + } else { + LEPT_FREE(str); + } + sarrayDestroy(&satest); + + skipToEndOfFunction(sa, stop, charindex, &next); + if (next == -1) break; + } + + /* Flatten into a string with newlines between prototypes */ + parsestr = sarrayToString(saout, 1); + LEPT_FREE(strdata); + sarrayDestroy(&sa); + sarrayDestroy(&saout); + + return parsestr; +} + + +/* + * \brief getNextNonCommentLine() + * + * \param[in] sa output from cpp, by line) + * \param[in] start starting index to search) + * \param[out] pnext index of first uncommented line after the start line + * \return 0 if OK, o on error + * + *
+ * Notes:
+ *      (1) Skips over all consecutive comment lines, beginning at 'start'
+ *      (2) If all lines to the end are '#' comments, return next = -1
+ * 
+ */ +static l_int32 +getNextNonCommentLine(SARRAY *sa, + l_int32 start, + l_int32 *pnext) +{ +char *str; +l_int32 i, n; + + PROCNAME("getNextNonCommentLine"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pnext) + return ERROR_INT("&pnext not defined", procName, 1); + + /* Init for situation where this line and all following are comments */ + *pnext = -1; + + n = sarrayGetCount(sa); + for (i = start; i < n; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return ERROR_INT("str not returned; shouldn't happen", procName, 1); + if (str[0] != '#') { + *pnext = i; + return 0; + } + } + + return 0; +} + + +/* + * \brief getNextNonBlankLine() + * + * \param[in] sa output from cpp, by line + * \param[in] start starting index to search + * \param[out] pnext index of first nonblank line after the start line + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Skips over all consecutive blank lines, beginning at 'start'
+ *      (2) A blank line has only whitespace characters (' ', '\t', '\n', '\r')
+ *      (3) If all lines to the end are blank, return next = -1
+ * 
+ */ +static l_int32 +getNextNonBlankLine(SARRAY *sa, + l_int32 start, + l_int32 *pnext) +{ +char *str; +l_int32 i, j, n, len; + + PROCNAME("getNextNonBlankLine"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pnext) + return ERROR_INT("&pnext not defined", procName, 1); + + /* Init for situation where this line and all following are blank */ + *pnext = -1; + + n = sarrayGetCount(sa); + for (i = start; i < n; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return ERROR_INT("str not returned; shouldn't happen", procName, 1); + len = strlen(str); + for (j = 0; j < len; j++) { + if (str[j] != ' ' && str[j] != '\t' + && str[j] != '\n' && str[j] != '\r') { /* non-blank */ + *pnext = i; + return 0; + } + } + } + + return 0; +} + + +/* + * \brief getNextNonDoubleSlashLine() + * + * \param[in] sa output from cpp, by line + * \param[in] start starting index to search + * \param[out] pnext index of first uncommented line after the start line + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Skips over all consecutive '//' lines, beginning at 'start'
+ *      (2) If all lines to the end start with '//', return next = -1
+ * 
+ */ +static l_int32 +getNextNonDoubleSlashLine(SARRAY *sa, + l_int32 start, + l_int32 *pnext) +{ +char *str; +l_int32 i, n, len; + + PROCNAME("getNextNonDoubleSlashLine"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pnext) + return ERROR_INT("&pnext not defined", procName, 1); + + /* Init for situation where this line and all following + * start with '//' */ + *pnext = -1; + + n = sarrayGetCount(sa); + for (i = start; i < n; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return ERROR_INT("str not returned; shouldn't happen", procName, 1); + len = strlen(str); + if (len < 2 || str[0] != '/' || str[1] != '/') { + *pnext = i; + return 0; + } + } + + return 0; +} + + +/* + * \brief searchForProtoSignature() + * + * \param[in] sa output from cpp, by line + * \param[in] begin beginning index to search + * \param[out] pstart starting index for function definition + * \param[out] pstop index of line on which proto is completed + * \param[out] pcharindex char index of completing ')' character + * \param[out] pfound 1 if valid signature is found; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If this returns found == 0, it means that there are no
+ *          more function definitions in the file.  Caller must check
+ *          this value and exit the loop over the entire cpp file.
+ *      (2) This follows plan 3 (see above).  We skip comment and blank
+ *          lines at the beginning.  Then we don't check for keywords.
+ *          Instead, find the relative locations of the first occurrences
+ *          of these four tokens: left parenthesis (lp), right
+ *          parenthesis (rp), left brace (lb) and semicolon (sc).
+ *      (3) The signature of a function definition looks like this:
+ *               .... '(' .... ')' '{'
+ *          where the lp and rp must both precede the lb, with only
+ *          whitespace between the rp and the lb.  The '....'
+ *          are sets of tokens that have no braces.
+ *      (4) If a function definition is found, this returns found = 1,
+ *          with 'start' being the first line of the definition and
+ *          'charindex' being the position of the ')' in line 'stop'
+ *          at the end of the arg list.
+ * 
+ */ +static l_int32 +searchForProtoSignature(SARRAY *sa, + l_int32 begin, + l_int32 *pstart, + l_int32 *pstop, + l_int32 *pcharindex, + l_int32 *pfound) +{ +l_int32 next, rbline, rbindex, scline; +l_int32 soffsetlp, soffsetrp, soffsetlb, soffsetsc; +l_int32 boffsetlp, boffsetrp, boffsetlb, boffsetsc; +l_int32 toffsetlp, toffsetrp, toffsetlb, toffsetsc; + + PROCNAME("searchForProtoSignature"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pstart) + return ERROR_INT("&start not defined", procName, 1); + if (!pstop) + return ERROR_INT("&stop not defined", procName, 1); + if (!pcharindex) + return ERROR_INT("&charindex not defined", procName, 1); + if (!pfound) + return ERROR_INT("&found not defined", procName, 1); + + *pfound = FALSE; + + while (1) { + + /* Skip over sequential '#' comment lines */ + getNextNonCommentLine(sa, begin, &next); + if (next == -1) return 0; + if (next != begin) { + begin = next; + continue; + } + + /* Skip over sequential blank lines */ + getNextNonBlankLine(sa, begin, &next); + if (next == -1) return 0; + if (next != begin) { + begin = next; + continue; + } + + /* Skip over sequential lines starting with '//' */ + getNextNonDoubleSlashLine(sa, begin, &next); + if (next == -1) return 0; + if (next != begin) { + begin = next; + continue; + } + + /* Search for specific character sequence patterns; namely + * a lp, a matching rp, a lb and a semicolon. + * Abort the search if no lp is found. */ + getOffsetForCharacter(sa, next, '(', &soffsetlp, &boffsetlp, + &toffsetlp); + if (soffsetlp == -1) + break; + getOffsetForMatchingRP(sa, next, soffsetlp, boffsetlp, toffsetlp, + &soffsetrp, &boffsetrp, &toffsetrp); + getOffsetForCharacter(sa, next, '{', &soffsetlb, &boffsetlb, + &toffsetlb); + getOffsetForCharacter(sa, next, ';', &soffsetsc, &boffsetsc, + &toffsetsc); + + /* We've found a lp. Now weed out the case where a matching + * rp and a lb are not both found. */ + if (soffsetrp == -1 || soffsetlb == -1) + break; + + /* Check if a left brace occurs before a left parenthesis; + * if so, skip it */ + if (toffsetlb < toffsetlp) { + skipToMatchingBrace(sa, next + soffsetlb, boffsetlb, + &rbline, &rbindex); + skipToSemicolon(sa, rbline, rbindex, &scline); + begin = scline + 1; + continue; + } + + /* Check if a semicolon occurs before a left brace or + * a left parenthesis; if so, skip it */ + if ((soffsetsc != -1) && + (toffsetsc < toffsetlb || toffsetsc < toffsetlp)) { + skipToSemicolon(sa, next, 0, &scline); + begin = scline + 1; + continue; + } + + /* OK, it should be a function definition. We haven't + * checked that there is only white space between the + * rp and lb, but we've only seen problems with two + * extern inlines in sys/stat.h, and this is handled + * later by eliminating any prototype beginning with 'extern'. */ + *pstart = next; + *pstop = next + soffsetrp; + *pcharindex = boffsetrp; + *pfound = TRUE; + break; + } + + return 0; +} + + +/* + * \brief captureProtoSignature() + * + * \param[in] sa output from cpp, by line + * \param[in] start starting index to search; never a comment line + * \param[in] stop index of line on which pattern is completed + * \param[in] charindex char index of completing ')' character + * \return cleanstr prototype string, or NULL on error + * + *
+ * Notes:
+ *      (1) Return all characters, ending with a ';' after the ')'
+ * 
+ */ +static char * +captureProtoSignature(SARRAY *sa, + l_int32 start, + l_int32 stop, + l_int32 charindex) +{ +char *str, *newstr, *protostr, *cleanstr; +SARRAY *sap; +l_int32 i; + + PROCNAME("captureProtoSignature"); + + if (!sa) + return (char *)ERROR_PTR("sa not defined", procName, NULL); + + sap = sarrayCreate(0); + for (i = start; i < stop; i++) { + str = sarrayGetString(sa, i, L_COPY); + sarrayAddString(sap, str, L_INSERT); + } + str = sarrayGetString(sa, stop, L_COPY); + str[charindex + 1] = '\0'; + newstr = stringJoin(str, ";"); + sarrayAddString(sap, newstr, L_INSERT); + LEPT_FREE(str); + protostr = sarrayToString(sap, 2); + sarrayDestroy(&sap); + cleanstr = cleanProtoSignature(protostr); + LEPT_FREE(protostr); + + return cleanstr; +} + + +/* + * \brief cleanProtoSignature() + * + * \param[in] instr input prototype string + * \return cleanstr clean prototype string, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds 'extern' at beginning and regularizes spaces
+ *          between tokens.
+ * 
+ */ +static char * +cleanProtoSignature(char *instr) +{ +char *str, *cleanstr; +char buf[Bufsize]; +char externstring[] = "extern"; +l_int32 i, j, nwords, nchars, index, len; +SARRAY *sa, *saout; + + PROCNAME("cleanProtoSignature"); + + if (!instr) + return (char *)ERROR_PTR("instr not defined", procName, NULL); + + sa = sarrayCreateWordsFromString(instr); + nwords = sarrayGetCount(sa); + saout = sarrayCreate(0); + sarrayAddString(saout, externstring, L_COPY); + for (i = 0; i < nwords; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + nchars = strlen(str); + index = 0; + for (j = 0; j < nchars; j++) { + if (index > Bufsize - 6) { + sarrayDestroy(&sa); + sarrayDestroy(&saout); + return (char *)ERROR_PTR("token too large", procName, NULL); + } + if (str[j] == '(') { + buf[index++] = ' '; + buf[index++] = '('; + buf[index++] = ' '; + } else if (str[j] == ')') { + buf[index++] = ' '; + buf[index++] = ')'; + } else { + buf[index++] = str[j]; + } + } + buf[index] = '\0'; + sarrayAddString(saout, buf, L_COPY); + } + + /* Flatten to a prototype string with spaces added after + * each word, and remove the last space */ + cleanstr = sarrayToString(saout, 2); + len = strlen(cleanstr); + cleanstr[len - 1] = '\0'; + + sarrayDestroy(&sa); + sarrayDestroy(&saout); + return cleanstr; +} + + +/* + * \brief skipToEndOfFunction() + * + * \param[in] sa output from cpp, by line + * \param[in] start index of starting line with left bracket to search + * \param[in] lbindex starting char index for left bracket + * \param[out] pnext index of line following the ending '}' for function + * \return 0 if OK, 1 on error + */ +static l_int32 +skipToEndOfFunction(SARRAY *sa, + l_int32 start, + l_int32 lbindex, + l_int32 *pnext) +{ +l_int32 end, rbindex; +l_int32 soffsetlb, boffsetlb, toffsetlb; + + PROCNAME("skipToEndOfFunction"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pnext) + return ERROR_INT("&next not defined", procName, 1); + + getOffsetForCharacter(sa, start, '{', &soffsetlb, &boffsetlb, + &toffsetlb); + skipToMatchingBrace(sa, start + soffsetlb, boffsetlb, &end, &rbindex); + if (end == -1) { /* shouldn't happen! */ + *pnext = -1; + return 1; + } + + *pnext = end + 1; + return 0; +} + + +/* + * \brief skipToMatchingBrace() + * + * \param[in] sa output from cpp, by line + * \param[in] start index of starting line with left bracket to search + * \param[in] lbindex starting char index for left bracket + * \param[out] pstop index of line with the matching right bracket + * \param[out] prbindex char index of matching right bracket + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the matching right brace is not found, returns
+ *          stop = -1.  This shouldn't happen.
+ * 
+ */ +static l_int32 +skipToMatchingBrace(SARRAY *sa, + l_int32 start, + l_int32 lbindex, + l_int32 *pstop, + l_int32 *prbindex) +{ +char *str; +l_int32 i, j, jstart, n, sumbrace, found, instring, nchars; + + PROCNAME("skipToMatchingBrace"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pstop) + return ERROR_INT("&stop not defined", procName, 1); + if (!prbindex) + return ERROR_INT("&rbindex not defined", procName, 1); + + instring = 0; /* init to FALSE; toggle on double quotes */ + *pstop = -1; + n = sarrayGetCount(sa); + sumbrace = 1; + found = FALSE; + for (i = start; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + jstart = 0; + if (i == start) + jstart = lbindex + 1; + nchars = strlen(str); + for (j = jstart; j < nchars; j++) { + /* Toggle the instring state every time you encounter + * a double quote that is NOT escaped. */ + if (j == jstart && str[j] == '\"') + instring = 1 - instring; + if (j > jstart && str[j] == '\"' && str[j-1] != '\\') + instring = 1 - instring; + /* Record the braces if they are neither a literal character + * nor within a string. */ + if (str[j] == '{' && str[j+1] != '\'' && !instring) { + sumbrace++; + } else if (str[j] == '}' && str[j+1] != '\'' && !instring) { + sumbrace--; + if (sumbrace == 0) { + found = TRUE; + *prbindex = j; + break; + } + } + } + if (found) { + *pstop = i; + return 0; + } + } + + return ERROR_INT("matching right brace not found", procName, 1); +} + + +/* + * \brief skipToSemicolon() + * + * \param[in] sa output from cpp, by line + * \param[in] start index of starting line to search + * \param[in] charindex starting char index for search + * \param[out] pnext index of line containing the next ';' + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the semicolon isn't found, returns next = -1.
+ *          This shouldn't happen.
+ *      (2) This is only used in contexts where the semicolon is
+ *          not within a string.
+ * 
+ */ +static l_int32 +skipToSemicolon(SARRAY *sa, + l_int32 start, + l_int32 charindex, + l_int32 *pnext) +{ +char *str; +l_int32 i, j, n, jstart, nchars, found; + + PROCNAME("skipToSemicolon"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pnext) + return ERROR_INT("&next not defined", procName, 1); + + *pnext = -1; + n = sarrayGetCount(sa); + found = FALSE; + for (i = start; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + jstart = 0; + if (i == start) + jstart = charindex + 1; + nchars = strlen(str); + for (j = jstart; j < nchars; j++) { + if (str[j] == ';') { + found = TRUE;; + break; + } + } + if (found) { + *pnext = i; + return 0; + } + } + + return ERROR_INT("semicolon not found", procName, 1); +} + + +/* + * \brief getOffsetForCharacter() + * + * \param[in] sa output from cpp, by line + * \param[in] start starting index in sa to search; + * never a comment line + * \param[in] tchar we are searching for the first instance of this + * \param[out] psoffset offset in strings from start index + * \param[out] pboffset offset in bytes within string in which + * the character is first found + * \param[out] ptoffset offset in total bytes from beginning of string + * indexed by 'start' to the location where + * the character is first found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We are searching for the first instance of 'tchar', starting
+ *          at the beginning of the string indexed by start.
+ *      (2) If the character is not found, soffset is returned as -1,
+ *          and the other offsets are set to very large numbers.  The
+ *          caller must check the value of soffset.
+ *      (3) This is only used in contexts where it is not necessary to
+ *          consider if the character is inside a string.
+ * 
+ */ +static l_int32 +getOffsetForCharacter(SARRAY *sa, + l_int32 start, + char tchar, + l_int32 *psoffset, + l_int32 *pboffset, + l_int32 *ptoffset) +{ +char *str; +l_int32 i, j, n, nchars, totchars, found; + + PROCNAME("getOffsetForCharacter"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!psoffset) + return ERROR_INT("&soffset not defined", procName, 1); + if (!pboffset) + return ERROR_INT("&boffset not defined", procName, 1); + if (!ptoffset) + return ERROR_INT("&toffset not defined", procName, 1); + + *psoffset = -1; /* init to not found */ + *pboffset = 100000000; + *ptoffset = 100000000; + + n = sarrayGetCount(sa); + found = FALSE; + totchars = 0; + for (i = start; i < n; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return ERROR_INT("str not returned; shouldn't happen", procName, 1); + nchars = strlen(str); + for (j = 0; j < nchars; j++) { + if (str[j] == tchar) { + found = TRUE; + break; + } + } + if (found) + break; + totchars += nchars; + } + + if (found) { + *psoffset = i - start; + *pboffset = j; + *ptoffset = totchars + j; + } + + return 0; +} + + +/* + * \brief getOffsetForMatchingRP() + * + * \param[in] sa output from cpp, by line + * \param[in] start starting index in sa to search; + * never a comment line + * \param[in] soffsetlp string offset to first LP + * \param[in] boffsetlp byte offset within string to first LP + * \param[in] toffsetlp total byte offset to first LP + * \param[out] psoffset offset in strings from start index + * \param[out] pboffset offset in bytes within string in which + * the matching RP is found + * \param[out] ptoffset offset in total bytes from beginning of string + * indexed by 'start' to the location where + * the matching RP is found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We are searching for the matching right parenthesis (RP) that
+ *          corresponds to the first LP found beginning at the string
+ *          indexed by start.
+ *      (2) If the matching RP is not found, soffset is returned as -1,
+ *          and the other offsets are set to very large numbers.  The
+ *          caller must check the value of soffset.
+ *      (3) This is only used in contexts where it is not necessary to
+ *          consider if the character is inside a string.
+ *      (4) We must do this because although most arg lists have a single
+ *          left and right parenthesis, it is possible to construct
+ *          more complicated prototype declarations, such as those
+ *          where functions are passed in.  The C++ rules for prototypes
+ *          are strict, and require that for functions passed in as args,
+ *          the function name arg be placed in parenthesis, as well
+ *          as its arg list, thus incurring two extra levels of parentheses.
+ * 
+ */ +static l_int32 +getOffsetForMatchingRP(SARRAY *sa, + l_int32 start, + l_int32 soffsetlp, + l_int32 boffsetlp, + l_int32 toffsetlp, + l_int32 *psoffset, + l_int32 *pboffset, + l_int32 *ptoffset) +{ +char *str; +l_int32 i, j, n, nchars, totchars, leftmatch, firstline, jstart, found; + + PROCNAME("getOffsetForMatchingRP"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!psoffset) + return ERROR_INT("&soffset not defined", procName, 1); + if (!pboffset) + return ERROR_INT("&boffset not defined", procName, 1); + if (!ptoffset) + return ERROR_INT("&toffset not defined", procName, 1); + + *psoffset = -1; /* init to not found */ + *pboffset = 100000000; + *ptoffset = 100000000; + + n = sarrayGetCount(sa); + found = FALSE; + totchars = toffsetlp; + leftmatch = 1; /* count of (LP - RP); we're finished when it goes to 0. */ + firstline = start + soffsetlp; + for (i = firstline; i < n; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return ERROR_INT("str not returned; shouldn't happen", procName, 1); + nchars = strlen(str); + jstart = 0; + if (i == firstline) + jstart = boffsetlp + 1; + for (j = jstart; j < nchars; j++) { + if (str[j] == '(') + leftmatch++; + else if (str[j] == ')') + leftmatch--; + if (leftmatch == 0) { + found = TRUE; + break; + } + } + if (found) + break; + if (i == firstline) + totchars += nchars - boffsetlp; + else + totchars += nchars; + } + + if (found) { + *psoffset = i - start; + *pboffset = j; + *ptoffset = totchars + j; + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/partify.c b/hgdriver/3rdparty/hgOCR/leptonica/partify.c new file mode 100644 index 0000000..6c074c0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/partify.c @@ -0,0 +1,312 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file partify.c + *
+ *
+ *     Top level
+ *         l_int32          partifyFiles()
+ *         l_int32          partifyPixac()
+ *
+ *     Helpers
+ *         static BOXA     *pixLocateStaveSets()
+ *         static l_int32   boxaRemoveVGaps()
+ * 
+ */ + +#include "allheaders.h" + + /* Static helplers */ +static BOXA *pixLocateStaveSets(PIX *pixs, l_int32 pageno, PIXA *pixadb); +static l_ok boxaRemoveVGaps(BOXA *boxa); + + +/*---------------------------------------------------------------------* + * Top level * + *---------------------------------------------------------------------*/ +/*! + * \brief partifyFiles() + * + * \param[in] dirname directory of files + * \param[in] substr required filename substring; use NULL for all files + * \param[in] nparts number of parts to generate (counting from top) + * \param[in] outroot root name of output pdf files + * \param[in] debugfile [optional] set to NULL for no debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) All page images are compressed in png format into a pixacomp.
+ *      (2) Each page image is deskewed, binarized at 300 ppi,
+ *          partified into %nparts, and saved in a set of pixacomps
+ *          in tiff-g4 format.
+ *      (3) Each partified pixacomp is rendered into a set of page images,
+ *          and output as a pdf.
+ * 
+ */ +l_ok +partifyFiles(const char *dirname, + const char *substr, + l_int32 nparts, + const char *outroot, + const char *debugfile) +{ +PIXA *pixadb; +PIXAC *pixac; + + PROCNAME("partifyFiles"); + + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + if (nparts < 0 || nparts > 10) + return ERROR_INT("nparts not in [1 ... 10]", procName, 1); + if (!outroot || outroot[0] == '\n') + return ERROR_INT("outroot undefined or empty", procName, 1); + + pixadb = (debugfile) ? pixaCreate(0) : NULL; + pixac = pixacompCreateFromFiles(dirname, substr, IFF_PNG); + partifyPixac(pixac, nparts, outroot, pixadb); + if (pixadb) { + L_INFO("writing debug output to %s\n", procName, debugfile); + pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, + "Partify Debug", debugfile); + } + pixacompDestroy(&pixac); + pixaDestroy(&pixadb); + return 0; +} + + +/*! + * \brief partifyPixac() + * + * \param[in] pixac with at least one image + * \param[in] nparts number of parts to generate (counting from top) + * \param[in] outroot root name of output pdf files + * \param[in] pixadb [optional] debug pixa; can be NULL + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See partifyPixac().
+ *      (2) If the image files do not have a resolution, 300 ppi is assumed.
+ * 
+ */ +l_ok +partifyPixac(PIXAC *pixac, + l_int32 nparts, + const char *outroot, + PIXA *pixadb) +{ +char buf[512]; +l_int32 i, j, pageno, res, npage, nbox, icount, line; +l_float32 factor; +L_BMF *bmf; +BOX *box1, *box2; +BOXA *boxa1, *boxa2, *boxa3; +PIX *pix1, *pix2, *pix3, *pix4, *pix5; +PIXAC **pixaca; + + PROCNAME("partifyPixac"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + if ((npage = pixacompGetCount(pixac)) == 0) + return ERROR_INT("pixac is empty", procName, 1); + if (nparts < 1 || nparts > 10) + return ERROR_INT("nparts not in [1 ... 10]", procName, 1); + if (!outroot || outroot[0] == '\n') + return ERROR_INT("outroot undefined or empty", procName, 1); + + /* Initialize the output array for each of the nparts */ + pixaca = (PIXAC **)LEPT_CALLOC(nparts, sizeof(PIXAC *)); + for (i = 0; i < nparts; i++) + pixaca[i] = pixacompCreate(0); + + /* Process each page */ + line = 1; + bmf = bmfCreate(NULL, 10); + for (pageno = 0; pageno < npage; pageno++) { + if ((pix1 = pixacompGetPix(pixac, pageno)) == NULL) { + L_ERROR("pix for page %d not found\n", procName, pageno); + continue; + } + + /* Scale, binarize and deskew */ + res = pixGetXRes(pix1); + if (res == 0 || res == 300 || res > 600) { + pix2 = pixClone(pix1); + } else { + factor = 300.0 / (l_float32)res; + if (factor > 3) + L_WARNING("resolution is very low\n", procName); + pix2 = pixScale(pix1, factor, factor); + } + pix3 = pixConvertTo1Adaptive(pix2); + pix4 = pixDeskew(pix3, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + if (!pix4) { + L_ERROR("pix for page %d not deskewed\n", procName, pageno); + continue; + } + pix1 = pixClone(pix4); /* rename */ + pixDestroy(&pix4); + + /* Find the stave sets at 4x reduction */ + boxa1 = pixLocateStaveSets(pix1, pageno, pixadb); + + /* Break each stave set into the separate staves (parts). + * A typical set will have more than one part, but if one of + * the parts is a keyboard, it will usually have two staves + * (also called a Grand Staff), composed of treble and + * bass staves. For example, a classical violin sonata + * could have a staff for the violin and two staves for + * the piano. We would set nparts == 2, and extract both + * of the piano staves as the piano part. */ + nbox = boxaGetCount(boxa1); + fprintf(stderr, "number of boxes in page %d: %d\n", pageno, nbox); + for (i = 0; i < nbox; i++, line++) { + snprintf(buf, sizeof(buf), "%d", line); + box1 = boxaGetBox(boxa1, i, L_COPY); + pix2 = pixClipRectangle(pix1, box1, NULL); + pix3 = pixMorphSequence(pix2, "d1.20 + o50.1 + o1.30", 0); + boxa2 = pixConnCompBB(pix3, 8); + boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); + boxaRemoveVGaps(boxa3); + icount = boxaGetCount(boxa3); + if (icount < nparts) + L_WARNING("nparts requested = %d, but only found %d\n", + procName, nparts, icount); + for (j = 0; j < icount && j < nparts; j++) { + box2 = boxaGetBox(boxa3, j, L_COPY); + if (j == nparts - 1) /* extend the box to the bottom */ + boxSetSideLocations(box2, -1, -1, -1, + pixGetHeight(pix1) - 1); + pix4 = pixClipRectangle(pix2, box2, NULL); + pix5 = pixAddTextlines(pix4, bmf, buf, 1, L_ADD_LEFT); + pixacompAddPix(pixaca[j], pix5, IFF_TIFF_G4); + boxDestroy(&box2); + pixDestroy(&pix4); + pixDestroy(&pix5); + } + boxaDestroy(&boxa2); + boxaDestroy(&boxa3); + boxDestroy(&box1); + pixDestroy(&pix2); + pixDestroy(&pix3); + } + boxaDestroy(&boxa1); + pixDestroy(&pix1); + } + + /* Output separate pdfs for each part */ + for (i = 0; i < nparts; i++) { + snprintf(buf, sizeof(buf), "%s-%d.pdf", outroot, i); + L_INFO("writing part %d: %s\n", procName, i, buf); + pixacompConvertToPdf(pixaca[i], 300, 1.0, L_G4_ENCODE, 0, NULL, buf); + pixacompDestroy(&pixaca[i]); + } + LEPT_FREE(pixaca); + bmfDestroy(&bmf); + return 0; +} + + +/* + * \brief pixLocateStaveSets() + * + * \param[in] pixs 1 bpp, 300 ppi, deskewed + * \param[in] pageno page number; used for debug output + * \param[in] pixadb [optional] debug pixa; can be NULL + * \return boxa containing the stave sets at full resolution + */ +static BOXA * +pixLocateStaveSets(PIX *pixs, + l_int32 pageno, + PIXA *pixadb) +{ +BOXA *boxa1, *boxa2, *boxa3, *boxa4; +PIX *pix1, *pix2; + + /* Find the stave sets at 4x reduction */ + pix1 = pixMorphSequence(pixs, "r11", 0); + boxa1 = pixConnCompBB(pix1, 8); + boxa2 = boxaSelectByArea(boxa1, 15000, L_SELECT_IF_GT, NULL); + boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); + if (pixadb) { + pix2 = pixConvertTo32(pix1); + pixRenderBoxaArb(pix2, boxa3, 2, 255, 0, 0); + pixaAddPix(pixadb, pix2, L_INSERT); + pixDisplay(pix2, 100 * pageno, 100); + } + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + + boxaRemoveVGaps(boxa3); + if (pixadb) { + pix2 = pixConvertTo32(pix1); + pixRenderBoxaArb(pix2, boxa3, 2, 0, 255, 0); + pixaAddPix(pixadb, pix2, L_INSERT); + pixDisplay(pix2, 100 * pageno, 600); + } + boxa4 = boxaTransform(boxa3, 0, 0, 4.0, 4.0); /* back to full res */ + boxaDestroy(&boxa3); + pixDestroy(&pix1); + return boxa4; +} + + +/* + * \brief boxaRemoveVGaps() + * + * \param[in] boxa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The boxes in %boxa are aligned vertically.  Move the horizontal
+ *          edges vertically to remove the gaps between boxes.
+ * 
+ */ +static l_ok +boxaRemoveVGaps(BOXA *boxa) +{ +l_int32 nbox, i, y1, h1, y2, h2, delta; + + nbox = boxaGetCount(boxa); + for (i = 0; i < nbox - 1; i++) { + boxaGetBoxGeometry(boxa, i, NULL, &y1, NULL, &h1); + boxaGetBoxGeometry(boxa, i + 1, NULL, &y2, NULL, &h2); + delta = (y2 - y1 - h1) / 2; + boxaAdjustBoxSides(boxa, i, 0, 0, 0, delta); + boxaAdjustBoxSides(boxa, i + 1, 0, 0, -delta, 0); + } + boxaAdjustBoxSides(boxa, nbox - 1, 0, 0, 0, delta); /* bot of last */ + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/partition.c b/hgdriver/3rdparty/hgOCR/leptonica/partition.c new file mode 100644 index 0000000..040eaeb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/partition.c @@ -0,0 +1,659 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file partition.c + *
+ *
+ *      Whitespace block extraction
+ *          BOXA            *boxaGetWhiteblocks()
+ *
+ *      Helpers
+ *          static PARTEL   *partelCreate()
+ *          static void      partelDestroy()
+ *          static l_int32   partelSetSize()
+ *          static BOXA     *boxaGenerateSubboxes()
+ *          static BOX      *boxaSelectPivotBox()
+ *          static l_int32   boxaCheckIfOverlapIsSmall()
+ *          BOXA            *boxaPruneSortedOnOverlap()
+ * 
+ */ + +#include "allheaders.h" + +/*! Partition element */ +struct PartitionElement { + l_float32 size; /* sorting key */ + BOX *box; /* region of the element */ + BOXA *boxa; /* set of intersecting boxes */ +}; +typedef struct PartitionElement PARTEL; + +static PARTEL * partelCreate(BOX *box); +static void partelDestroy(PARTEL **ppartel); +static l_int32 partelSetSize(PARTEL *partel, l_int32 sortflag); +static BOXA * boxaGenerateSubboxes(BOX *box, BOXA *boxa, l_int32 maxperim, + l_float32 fract); +static BOX * boxaSelectPivotBox(BOX *box, BOXA *boxa, l_int32 maxperim, + l_float32 fract); +static l_int32 boxCheckIfOverlapIsBig(BOX *box, BOXA *boxa, + l_float32 maxoverlap); + +static const l_int32 DefaultMaxPops = 20000; + + +#ifndef NO_CONSOLE_IO +#define OUTPUT_HEAP_STATS 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------* + * Whitespace block extraction * + *------------------------------------------------------------------*/ +/*! + * \brief boxaGetWhiteblocks() + * + * \param[in] boxas typ. a set of bounding boxes of fg components + * \param[in] box initial region; typically including all boxes + * in boxas; if null, it computes the region to + * include all boxes in boxas + * \param[in] sortflag L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, + * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, + * L_SORT_BY_PERIMETER, L_SORT_BY_AREA + * \param[in] maxboxes max number of output whitespace boxes; e.g., 100 + * \param[in] maxoverlap maximum fractional overlap of a box by any + * of the larger boxes; e.g., 0.2 + * \param[in] maxperim maximum half-perimeter, in pixels, for which + * pivot is selected by proximity to box centroid; + * e.g., 200 + * \param[in] fract fraction of box diagonal that is an acceptable + * distance from the box centroid to select + * the pivot; e.g., 0.2 + * \param[in] maxpops max number of pops from the heap; use 0 as default + * \return boxa of sorted whitespace boxes, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses the elegant Breuel algorithm, found in "Two
+ *          Geometric Algorithms for Layout Analysis", 2002,
+ *          url: "citeseer.ist.psu.edu/breuel02two.html".
+ *          It starts with the bounding boxes (b.b.) of the connected
+ *          components (c.c.) in a region, along with the rectangle
+ *          representing that region.  It repeatedly divides the
+ *          rectangle into four maximal rectangles that exclude a
+ *          pivot rectangle, sorting them in a priority queue
+ *          according to one of the six sort flags.  It returns a boxa
+ *          of the "largest" set that have no intersection with boxes
+ *          from the input boxas.
+ *      (2) If box == NULL, the initial region is the minimal region
+ *          that includes the origin and every box in boxas.
+ *      (3) maxboxes is the maximum number of whitespace boxes that will
+ *          be returned.  The actual number will depend on the image
+ *          and the values chosen for maxoverlap and maxpops.  In many
+ *          cases, the actual number will be 'maxboxes'.
+ *      (4) maxoverlap allows pruning of whitespace boxes depending on
+ *          the overlap.  To avoid all pruning, use maxoverlap = 1.0.
+ *          To select only boxes that have no overlap with each other
+ *          (maximal pruning), choose maxoverlap = 0.0.
+ *          Otherwise, no box can have more than the 'maxoverlap' fraction
+ *          of its area overlapped by any larger (in the sense of the
+ *          sortflag) box.
+ *      (5) Choose maxperim (actually, maximum half-perimeter) to
+ *          represent a c.c. that is small enough so that you don't care
+ *          about the white space that could be inside of it.  For all such
+ *          c.c., the pivot for 'quadfurcation' of a rectangle is selected
+ *          as having a reasonable proximity to the rectangle centroid.
+ *      (6) Use fract in the range [0.0 ... 1.0].  Set fract = 0.0
+ *          to choose the small box nearest the centroid as the pivot.
+ *          If you choose fract > 0.0, it is suggested that you call
+ *          boxaPermuteRandom() first, to permute the boxes (see usage below).
+ *          This should reduce the search time for each of the pivot boxes.
+ *      (7) Choose maxpops to be the maximum number of rectangles that
+ *          are popped from the heap.  This is an indirect way to limit the
+ *          execution time.  Use 0 for default (a fairly large number).
+ *          At any time, you can expect the heap to contain about
+ *          2.5 times as many boxes as have been popped off.
+ *      (8) The output result is a sorted set of overlapping
+ *          boxes, constrained by 'maxboxes', 'maxoverlap' and 'maxpops'.
+ *      (9) The main defect of the method is that it abstracts out the
+ *          actual components, retaining only the b.b. for analysis.
+ *          Consider a component with a large b.b.  If this is chosen
+ *          as a pivot, all white space inside is immediately taken
+ *          out of consideration.  Furthermore, even if it is never chosen
+ *          as a pivot, as the partitioning continues, at no time will
+ *          any of the whitespace inside this component be part of a
+ *          rectangle with zero overlapping boxes.  Thus, the interiors
+ *          of all boxes are necessarily excluded from the union of
+ *          the returned whitespace boxes.
+ *     (10) It should be noted that the algorithm puts a large number
+ *          of partels on the queue.  Setting a limit of X partels to
+ *          remove from the queue, one typically finds that there will be
+ *          several times that number (say, 2X - 3X) left on the queue.
+ *          For an efficient algorithm to find the largest white or
+ *          or black rectangles, without permitting them to overlap,
+ *          see pixFindLargeRectangles().
+ *     (11) USAGE: One way to accommodate to this weakness is to remove such
+ *          large b.b. before starting the computation.  For example,
+ *          if 'box' is an input image region containing 'boxa' b.b. of c.c.:
+ *
+ *                   // Faster pivot choosing
+ *               boxaPermuteRandom(boxa, boxa);
+ *
+ *                   // Remove anything either large width or height
+ *               boxat = boxaSelectBySize(boxa, maxwidth, maxheight,
+ *                                        L_SELECT_IF_BOTH, L_SELECT_IF_LT,
+ *                                        NULL);
+ *
+ *               boxad = boxaGetWhiteblocks(boxat, box, type, maxboxes,
+ *                                          maxoverlap, maxperim, fract,
+ *                                          maxpops);
+ *
+ *          The result will be rectangular regions of "white space" that
+ *          extend into (and often through) the excluded components.
+ *     (11) As a simple example, suppose you wish to find the columns on a page.
+ *          First exclude large c.c. that may block the columns, and then call:
+ *
+ *               boxad = boxaGetWhiteblocks(boxa, box, L_SORT_BY_HEIGHT,
+ *                                          20, 0.15, 200, 0.2, 2000);
+ *
+ *          to get the 20 tallest boxes with no more than 0.15 overlap
+ *          between a box and any of the taller ones, and avoiding the
+ *          use of any c.c. with a b.b. half perimeter greater than 200
+ *          as a pivot.
+ * 
+ */ +BOXA * +boxaGetWhiteblocks(BOXA *boxas, + BOX *box, + l_int32 sortflag, + l_int32 maxboxes, + l_float32 maxoverlap, + l_int32 maxperim, + l_float32 fract, + l_int32 maxpops) +{ +l_int32 i, w, h, n, nsub, npush, npop; +BOX *boxsub; +BOXA *boxa, *boxa4, *boxasub, *boxad; +PARTEL *partel; +L_HEAP *lh; + + PROCNAME("boxaGetWhiteblocks"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (sortflag != L_SORT_BY_WIDTH && sortflag != L_SORT_BY_HEIGHT && + sortflag != L_SORT_BY_MIN_DIMENSION && + sortflag != L_SORT_BY_MAX_DIMENSION && + sortflag != L_SORT_BY_PERIMETER && sortflag != L_SORT_BY_AREA) + return (BOXA *)ERROR_PTR("invalid sort flag", procName, NULL); + if (maxboxes < 1) { + maxboxes = 1; + L_WARNING("setting maxboxes = 1\n", procName); + } + if (maxoverlap < 0.0 || maxoverlap > 1.0) + return (BOXA *)ERROR_PTR("invalid maxoverlap", procName, NULL); + if (maxpops == 0) + maxpops = DefaultMaxPops; + + if (!box) { + boxaGetExtent(boxas, &w, &h, NULL); + box = boxCreate(0, 0, w, h); + } + + /* Prime the heap */ + lh = lheapCreate(20, L_SORT_DECREASING); + partel = partelCreate(box); + partel->boxa = boxaCopy(boxas, L_CLONE); + partelSetSize(partel, sortflag); + lheapAdd(lh, partel); + + npush = 1; + npop = 0; + boxad = boxaCreate(0); + while (1) { + if ((partel = (PARTEL *)lheapRemove(lh)) == NULL) /* we're done */ + break; + + npop++; /* How many boxes have we retrieved from the queue? */ + if (npop > maxpops) { + partelDestroy(&partel); + break; + } + + /* Extract the contents */ + boxa = boxaCopy(partel->boxa, L_CLONE); + box = boxClone(partel->box); + partelDestroy(&partel); + + /* Can we output this one? */ + n = boxaGetCount(boxa); + if (n == 0) { + if (boxCheckIfOverlapIsBig(box, boxad, maxoverlap) == 0) + boxaAddBox(boxad, box, L_INSERT); + else + boxDestroy(&box); + boxaDestroy(&boxa); + if (boxaGetCount(boxad) >= maxboxes) /* we're done */ + break; + continue; + } + + + /* Generate up to 4 subboxes and put them on the heap */ + boxa4 = boxaGenerateSubboxes(box, boxa, maxperim, fract); + boxDestroy(&box); + nsub = boxaGetCount(boxa4); + for (i = 0; i < nsub; i++) { + boxsub = boxaGetBox(boxa4, i, L_CLONE); + boxasub = boxaIntersectsBox(boxa, boxsub); + partel = partelCreate(boxsub); + partel->boxa = boxasub; + partelSetSize(partel, sortflag); + lheapAdd(lh, partel); + boxDestroy(&boxsub); + } + npush += nsub; /* How many boxes have we put on the queue? */ + +/* boxaWriteStream(stderr, boxa4); */ + + boxaDestroy(&boxa4); + boxaDestroy(&boxa); + } + +#if OUTPUT_HEAP_STATS + fprintf(stderr, "Heap statistics:\n"); + fprintf(stderr, " Number of boxes pushed: %d\n", npush); + fprintf(stderr, " Number of boxes popped: %d\n", npop); + fprintf(stderr, " Number of boxes on heap: %d\n", lheapGetCount(lh)); +#endif /* OUTPUT_HEAP_STATS */ + + /* Clean up the heap */ + while ((partel = (PARTEL *)lheapRemove(lh)) != NULL) + partelDestroy(&partel); + lheapDestroy(&lh, FALSE); + + return boxad; +} + + +/*------------------------------------------------------------------* + * Helpers * + *------------------------------------------------------------------*/ +/*! + * \brief partelCreate() + * + * \param[in] box region; inserts a copy + * \return partel, or NULL on error + */ +static PARTEL * +partelCreate(BOX *box) +{ +PARTEL *partel; + + partel = (PARTEL *)LEPT_CALLOC(1, sizeof(PARTEL)); + partel->box = boxCopy(box); + return partel; +} + + +/*! + * \brief partelDestroy() + * + * \param[in,out] ppartel contents will be set to null before returning + * \return void + */ +static void +partelDestroy(PARTEL **ppartel) +{ +PARTEL *partel; + + PROCNAME("partelDestroy"); + + if (ppartel == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((partel = *ppartel) == NULL) + return; + + boxDestroy(&partel->box); + boxaDestroy(&partel->boxa); + LEPT_FREE(partel); + *ppartel = NULL; + return; +} + + +/*! + * \brief partelSetSize() + * + * \param[in] partel + * \param[in] sortflag L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, + * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, + * L_SORT_BY_PERIMETER, L_SORT_BY_AREA + * \return 0 if OK, 1 on error + */ +static l_int32 +partelSetSize(PARTEL *partel, + l_int32 sortflag) +{ +l_int32 w, h; + + PROCNAME("partelSetSize"); + + if (!partel) + return ERROR_INT("partel not defined", procName, 1); + + boxGetGeometry(partel->box, NULL, NULL, &w, &h); + if (sortflag == L_SORT_BY_WIDTH) + partel->size = (l_float32)w; + else if (sortflag == L_SORT_BY_HEIGHT) + partel->size = (l_float32)h; + else if (sortflag == L_SORT_BY_MIN_DIMENSION) + partel->size = (l_float32)L_MIN(w, h); + else if (sortflag == L_SORT_BY_MAX_DIMENSION) + partel->size = (l_float32)L_MAX(w, h); + else if (sortflag == L_SORT_BY_PERIMETER) + partel->size = (l_float32)(w + h); + else if (sortflag == L_SORT_BY_AREA) + partel->size = (l_float32)(w * h); + else + return ERROR_INT("invalid sortflag", procName, 1); + return 0; +} + + +/*! + * \brief boxaGenerateSubboxes() + * + * \param[in] box region to be split into up to four overlapping + * subregions + * \param[in] boxa boxes of rectangles intersecting the box + * \param[in] maxperim maximum half-perimeter for which pivot + * is selected by proximity to box centroid + * \param[in] fract fraction of box diagonal that is an acceptable + * distance from the box centroid to select the pivot + * \return boxa of four or less overlapping subrectangles of + * the box, or NULL on error + */ +static BOXA * +boxaGenerateSubboxes(BOX *box, + BOXA *boxa, + l_int32 maxperim, + l_float32 fract) +{ +l_int32 x, y, w, h, xp, yp, wp, hp; +BOX *boxp; /* pivot box */ +BOX *boxsub; +BOXA *boxa4; + + PROCNAME("boxaGenerateSubboxes"); + + if (!box) + return (BOXA *)ERROR_PTR("box not defined", procName, NULL); + if (!boxa) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + + boxa4 = boxaCreate(4); + boxp = boxaSelectPivotBox(box, boxa, maxperim, fract); + boxGetGeometry(box, &x, &y, &w, &h); + boxGetGeometry(boxp, &xp, &yp, &wp, &hp); + boxDestroy(&boxp); + if (xp > x) { /* left sub-box */ + boxsub = boxCreate(x, y, xp - x, h); + boxaAddBox(boxa4, boxsub, L_INSERT); + } + if (yp > y) { /* top sub-box */ + boxsub = boxCreate(x, y, w, yp - y); + boxaAddBox(boxa4, boxsub, L_INSERT); + } + if (xp + wp < x + w) { /* right sub-box */ + boxsub = boxCreate(xp + wp, y, x + w - xp - wp, h); + boxaAddBox(boxa4, boxsub, L_INSERT); + } + if (yp + hp < y + h) { /* bottom sub-box */ + boxsub = boxCreate(x, yp + hp, w, y + h - yp - hp); + boxaAddBox(boxa4, boxsub, L_INSERT); + } + + return boxa4; +} + + +/*! + * \brief boxaSelectPivotBox() + * + * \param[in] box containing box; to be split by the pivot box + * \param[in] boxa boxes of rectangles, from which 1 is to be chosen + * \param[in] maxperim maximum half-perimeter for which pivot + * is selected by proximity to box centroid + * \param[in] fract fraction of box diagonal that is an acceptable + * distance from the box centroid to select the pivot + * \return box pivot box for subdivision into 4 rectangles, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This is a tricky piece that wasn't discussed in the
+ *          Breuel's 2002 paper.
+ *      (2) Selects a box from boxa whose centroid is reasonably close to
+ *          the centroid of the containing box (xc, yc) and whose
+ *          half-perimeter does not exceed the maxperim value.
+ *      (3) If there are no boxes in the boxa that are small enough,
+ *          then it selects the smallest of the larger boxes,
+ *          without reference to its location in the containing box.
+ *      (4) If a small box has a centroid at a distance from the
+ *          centroid of the containing box that is not more than
+ *          the fraction 'fract' of the diagonal of the containing
+ *          box, that box is chosen as the pivot, terminating the
+ *          search for the nearest small box.
+ *      (5) Use fract in the range [0.0 ... 1.0].  Set fract = 0.0
+ *          to choose the small box nearest the centroid.
+ *      (6) Choose maxperim to represent a connected component that is
+ *          small enough so that you don't care about the white space
+ *          that could be inside of it.
+ * 
+ */ +static BOX * +boxaSelectPivotBox(BOX *box, + BOXA *boxa, + l_int32 maxperim, + l_float32 fract) +{ +l_int32 i, n, bw, bh, w, h; +l_int32 smallfound, minindex, perim, minsize; +l_float32 delx, dely, mindist, threshdist, dist, x, y, cx, cy; +BOX *boxt; + + PROCNAME("boxaSelectPivotBox"); + + if (!box) + return (BOX *)ERROR_PTR("box not defined", procName, NULL); + if (!boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + n = boxaGetCount(boxa); + if (n == 0) + return (BOX *)ERROR_PTR("no boxes in boxa", procName, NULL); + if (fract < 0.0 || fract > 1.0) { + L_WARNING("fract out of bounds; using 0.0\n", procName); + fract = 0.0; + } + + boxGetGeometry(box, NULL, NULL, &w, &h); + boxGetCenter(box, &x, &y); + threshdist = fract * (w * w + h * h); + mindist = 1000000000.; + minindex = 0; + smallfound = FALSE; + for (i = 0; i < n; i++) { + boxt = boxaGetBox(boxa, i, L_CLONE); + boxGetGeometry(boxt, NULL, NULL, &bw, &bh); + boxGetCenter(boxt, &cx, &cy); + boxDestroy(&boxt); + if (bw + bh > maxperim) + continue; + smallfound = TRUE; + delx = cx - x; + dely = cy - y; + dist = delx * delx + dely * dely; + if (dist <= threshdist) + return boxaGetBox(boxa, i, L_COPY); + if (dist < mindist) { + minindex = i; + mindist = dist; + } + } + + /* If there are small boxes but none are within 'fract' of the + * centroid, return the nearest one. */ + if (smallfound == TRUE) + return boxaGetBox(boxa, minindex, L_COPY); + + /* No small boxes; return the smallest of the large boxes */ + minsize = 1000000000; + minindex = 0; + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, NULL, NULL, &bw, &bh); + perim = bw + bh; + if (perim < minsize) { + minsize = perim; + minindex = i; + } + } + return boxaGetBox(boxa, minindex, L_COPY); +} + + +/*! + * \brief boxCheckIfOverlapIsBig() + * + * \param[in] box to be tested + * \param[in] boxa of boxes already stored + * \param[in] maxoverlap maximum fractional overlap of the input box + * by any of the boxes in boxa + * \return 0 if box has small overlap with every box in boxa; + * 1 otherwise or on error + */ +static l_int32 +boxCheckIfOverlapIsBig(BOX *box, + BOXA *boxa, + l_float32 maxoverlap) +{ +l_int32 i, n, bigoverlap; +l_float32 fract; +BOX *boxt; + + PROCNAME("boxCheckIfOverlapIsBig"); + + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (maxoverlap < 0.0 || maxoverlap > 1.0) + return ERROR_INT("invalid maxoverlap", procName, 1); + + n = boxaGetCount(boxa); + if (n == 0 || maxoverlap == 1.0) + return 0; + + bigoverlap = 0; + for (i = 0; i < n; i++) { + boxt = boxaGetBox(boxa, i, L_CLONE); + boxOverlapFraction(boxt, box, &fract); + boxDestroy(&boxt); + if (fract > maxoverlap) { + bigoverlap = 1; + break; + } + } + + return bigoverlap; +} + + +/*! + * \brief boxaPruneSortedOnOverlap() + * + * \param[in] boxas sorted by size in decreasing order + * \param[in] maxoverlap maximum fractional overlap of a box by any + * of the larger boxes + * \return boxad pruned, or NULL on error + * + *
+ * Notes:
+ *      (1) This selectively removes smaller boxes when they are overlapped
+ *          by any larger box by more than the input 'maxoverlap' fraction.
+ *      (2) To avoid all pruning, use maxoverlap = 1.0.  To select only
+ *          boxes that have no overlap with each other (maximal pruning),
+ *          set maxoverlap = 0.0.
+ *      (3) If there are no boxes in boxas, returns an empty boxa.
+ * 
+ */ +BOXA * +boxaPruneSortedOnOverlap(BOXA *boxas, + l_float32 maxoverlap) +{ +l_int32 i, j, n, remove; +l_float32 fract; +BOX *box1, *box2; +BOXA *boxad; + + PROCNAME("boxaPruneSortedOnOverlap"); + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); + if (maxoverlap < 0.0 || maxoverlap > 1.0) + return (BOXA *)ERROR_PTR("invalid maxoverlap", procName, NULL); + + n = boxaGetCount(boxas); + if (n == 0 || maxoverlap == 1.0) + return boxaCopy(boxas, L_COPY); + + boxad = boxaCreate(0); + box2 = boxaGetBox(boxas, 0, L_COPY); + boxaAddBox(boxad, box2, L_INSERT); + for (j = 1; j < n; j++) { /* prune on j */ + box2 = boxaGetBox(boxas, j, L_COPY); + remove = FALSE; + for (i = 0; i < j; i++) { /* test on i */ + box1 = boxaGetBox(boxas, i, L_CLONE); + boxOverlapFraction(box1, box2, &fract); + boxDestroy(&box1); + if (fract > maxoverlap) { + remove = TRUE; + break; + } + } + if (remove == TRUE) + boxDestroy(&box2); + else + boxaAddBox(boxad, box2, L_INSERT); + } + + return boxad; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pdfio1.c b/hgdriver/3rdparty/hgOCR/leptonica/pdfio1.c new file mode 100644 index 0000000..2dd95c4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pdfio1.c @@ -0,0 +1,2252 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pdfio1.c + *
+ *
+ *    Higher-level operations for generating pdf from images.
+ *    Use poppler's pdfimages to invert the process, extracting
+ *    raster images from pdf.
+ *
+ *    |=============================================================|
+ *    |                        Important notes                      |
+ *    |=============================================================|
+ *    | Some of these functions require I/O libraries such as       |
+ *    | libtiff, libjpeg, libpng, libz and libopenjp2.  If you do   |
+ *    | not have these libraries, some calls will fail.  For        |
+ *    | example, if you do not have libopenjp2, you cannot write a  |
+ *    | pdf where transcoding is required to incorporate a          |
+ *    | jp2k image.                                                 |
+ *    |                                                             |
+ *    | You can manually deactivate all pdf writing by setting      |
+ *    | this in environ.h:                                          |
+ *    | \code                                                       |
+ *    |      #define  USE_PDFIO     0                               |
+ *    | \endcode                                                    |
+ *    | This will link the stub file pdfiostub.c.                   |
+ *    |=============================================================|
+ *
+ *     Set 1. These functions convert a set of image files
+ *     to a multi-page pdf file, with one image on each page.
+ *     All images are rendered at the same (input) resolution.
+ *     The images can be specified as being in a directory, or they
+ *     can be in an sarray.  The output pdf can be either a file
+ *     or an array of bytes in memory.
+ *
+ *     Set 2. These functions are a special case of set 1, where
+ *     no scaling or change in quality is required.  For jpeg and jp2k
+ *     images, the bytes in each file can be directly incorporated
+ *     into the output pdf, and the wrapping up of multiple image
+ *     files is very fast.  For non-interlaced png, the data bytes
+ *     including the predictors can also be written directly into the
+ *     flate pdf data.  For other image formats (e.g., tiff-g4),
+ *     transcoding is required, where the image data is first decompressed
+ *     and then the G4 or Flate (gzip) encodings are generated.
+ *
+ *     Set 3. These functions convert a set of images in memory
+ *     to a multi-page pdf, with one image on each page.  The pdf
+ *     output can be either a file or an array of bytes in memory.
+ *
+ *     Set 4. These functions implement a pdf output "device driver"
+ *     for wrapping (encoding) any number of images on a single page
+ *     in pdf.  The input can be either an image file or a Pix;
+ *     the pdf output can be either a file or an array of bytes in memory.
+ *
+ *     Set 5. These "segmented" functions take a set of image
+ *     files, along with optional segmentation information, and
+ *     generate a multi-page pdf file, where each page consists
+ *     in general of a mixed raster pdf of image and non-image regions.
+ *     The segmentation information for each page can be input as
+ *     either a mask over the image parts, or as a Boxa of those
+ *     regions.
+ *
+ *     Set 6. These "segmented" functions convert an image and
+ *     an optional Boxa of image regions into a mixed raster pdf file
+ *     for the page.  The input image can be either a file or a Pix.
+ *
+ *     Set 7. These functions take a set of single-page pdf files
+ *     and concatenates it into a multi-page pdf.  The input can be
+ *     a set of either single page pdf files or pdf 'strings' in memory.
+ *     The output can be either a file or an array of bytes in memory.
+ *
+ *     The images in the pdf file can be rendered using a pdf viewer,
+ *     such as evince, gv, xpdf or acroread.
+ *
+ *     Reference on the pdf file format:
+ *         http://www.adobe.com/devnet/pdf/pdf_reference_archive.html
+ *
+ *     1. Convert specified image files to pdf (one image file per page)
+ *          l_int32             convertFilesToPdf()
+ *          l_int32             saConvertFilesToPdf()
+ *          l_int32             saConvertFilesToPdfData()
+ *          l_int32             selectDefaultPdfEncoding()
+ *
+ *     2. Convert specified image files to pdf without scaling
+ *          l_int32             convertUnscaledFilesToPdf()
+ *          l_int32             saConvertUnscaledFilesToPdf()
+ *          l_int32             saConvertUnscaledFilesToPdfData()
+ *          l_int32             convertUnscaledToPdfData()
+ *
+ *     3. Convert multiple images to pdf (one image per page)
+ *          l_int32             pixaConvertToPdf()
+ *          l_int32             pixaConvertToPdfData()
+ *
+ *     4. Single page, multi-image converters
+ *          l_int32             convertToPdf()
+ *          l_int32             convertImageDataToPdf()
+ *          l_int32             convertToPdfData()
+ *          l_int32             convertImageDataToPdfData()
+ *          l_int32             pixConvertToPdf()
+ *          l_int32             pixWriteStreamPdf()
+ *          l_int32             pixWriteMemPdf()
+ *
+ *     5. Segmented multi-page, multi-image converter
+ *          l_int32             convertSegmentedFilesToPdf()
+ *          BOXAA              *convertNumberedMasksToBoxaa()
+ *
+ *     6. Segmented single page, multi-image converters
+ *          l_int32             convertToPdfSegmented()
+ *          l_int32             pixConvertToPdfSegmented()
+ *          l_int32             convertToPdfDataSegmented()
+ *          l_int32             pixConvertToPdfDataSegmented()
+ *
+ *     7. Multipage concatenation
+ *          l_int32             concatenatePdf()
+ *          l_int32             saConcatenatePdf()
+ *          l_int32             ptraConcatenatePdf()
+ *          l_int32             concatenatePdfToData()
+ *          l_int32             saConcatenatePdfToData()
+ *
+ *     The top-level multi-image functions can be visualized as follows:
+ *          Output pdf data to file:
+ *             convertToPdf()  and  convertImageDataToPdf()
+ *                     --> pixConvertToPdf()
+ *                           --> pixConvertToPdfData()
+ *
+ *          Output pdf data to array in memory:
+ *             convertToPdfData()  and  convertImageDataToPdfData()
+ *                     --> pixConvertToPdfData()
+ *
+ *     The top-level segmented image functions can be visualized as follows:
+ *          Output pdf data to file:
+ *             convertToPdfSegmented()
+ *                     --> pixConvertToPdfSegmented()
+ *                           --> pixConvertToPdfDataSegmented()
+ *
+ *          Output pdf data to array in memory:
+ *             convertToPdfDataSegmented()
+ *                     --> pixConvertToPdfDataSegmented()
+ *
+ *     For multi-page concatenation, there are three different types of input
+ *        (1) directory and optional filename filter
+ *        (2) sarray of filenames
+ *        (3) ptra of byte arrays of pdf data
+ *     and two types of output for the concatenated pdf data
+ *        (1) filename
+ *        (2) data array and size
+ *     High-level interfaces are given for each of the six combinations.
+ *
+ *     Note: When wrapping small images into pdf, it is useful to give
+ *     them a relatively low resolution value, to avoid rounding errors
+ *     when rendering the images.  For example, if you want an image
+ *     of width w pixels to be 5 inches wide on a screen, choose a
+ *     resolution w/5.
+ *
+ *     The very fast functions in section (2) require neither transcoding
+ *     nor parsing of the compressed jpeg file.  With three types of image
+ *     compression, the compressed strings can be incorporated into
+ *     the pdf data without decompression and re-encoding: jpeg, jp2k
+ *     and png.  The DCTDecode and JPXDecode filters can handle the
+ *     entire jpeg and jp2k encoded string as a byte array in the pdf file.
+ *     The FlateDecode filter can handle the png compressed image data,
+ *     including predictors that occur as the first byte in each
+ *     raster line, but it is necessary to store only the png IDAT chunk
+ *     data in the pdf array.  The alternative for wrapping png images
+ *     is to transcode them: uncompress into a raster (a pix) and then
+ *     gzip the raster data.  This typically results in a larger pdf file
+ *     because it doesn't use the two-dimensional png predictor.
+ *     Colormaps, which are found in png PLTE chunks, must always be
+ *     pulled out and included separately in the pdf.  For CCITT-G4
+ *     compression, you can not simply include a tiff G4 file -- you must
+ *     either parse it and extract the G4 compressed data within it,
+ *     or uncompress to a raster and G4 compress again.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if USE_PDFIO /* defined in environ.h */ + /* --------------------------------------------*/ + + /* Typical scan resolution in ppi (pixels/inch) */ +static const l_int32 DefaultInputRes = 300; + + +/*---------------------------------------------------------------------* + * Convert specified image files to pdf (one image file per page) * + *---------------------------------------------------------------------*/ +/*! + * \brief convertFilesToPdf() + * + * \param[in] dirname directory name containing images + * \param[in] substr [optional] substring filter on filenames; + * can be NULL + * \param[in] res input resolution of all images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE or + * L_DEFAULT_ENCODE for default) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] title [optional] pdf title; if null, taken from + * the first image filename + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %substr is not NULL, only image filenames that contain
+ *          the substring can be used.  If %substr == NULL, all files
+ *          in the directory are used.
+ *      (2) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order
+ *          before concatenation.
+ *      (3) The scalefactor is applied to each image before encoding.
+ *          If you enter a value <= 0.0, it will be set to 1.0.
+ *      (4) Specifying one of the four encoding types for %type forces
+ *          all images to be compressed with that type.  Use 0 to have
+ *          the type determined for each image based on depth and whether
+ *          or not it has a colormap.
+ * 
+ */ +l_ok +convertFilesToPdf(const char *dirname, + const char *substr, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + const char *fileout) +{ +l_int32 ret; +SARRAY *sa; + + PROCNAME("convertFilesToPdf"); + + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return ERROR_INT("sa not made", procName, 1); + ret = saConvertFilesToPdf(sa, res, scalefactor, type, quality, + title, fileout); + sarrayDestroy(&sa); + return ret; +} + + +/*! + * \brief saConvertFilesToPdf() + * + * \param[in] sa string array of pathnames for images + * \param[in] res input resolution of all images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE or + * L_DEFAULT_ENCODE for default) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] title [optional] pdf title; if null, taken from + * the first image filename + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertFilesToPdf().
+ * 
+ */ +l_ok +saConvertFilesToPdf(SARRAY *sa, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("saConvertFilesToPdf"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + ret = saConvertFilesToPdfData(sa, res, scalefactor, type, quality, + title, &data, &nbytes); + if (ret) { + if (data) LEPT_FREE(data); + return ERROR_INT("pdf data not made", procName, 1); + } + + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + L_ERROR("pdf data not written to file\n", procName); + return ret; +} + + +/*! + * \brief saConvertFilesToPdfData() + * + * \param[in] sa string array of pathnames for images + * \param[in] res input resolution of all images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE or + * L_DEFAULT_ENCODE for default) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] title [optional] pdf title; if null, taken from + * the first image filename + * \param[out] pdata output pdf data (of all images + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertFilesToPdf().
+ * 
+ */ +l_ok +saConvertFilesToPdfData(SARRAY *sa, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +char *fname; +const char *pdftitle; +l_uint8 *imdata; +l_int32 i, n, ret, pagetype, npages, scaledres; +size_t imbytes; +L_BYTEA *ba; +PIX *pixs, *pix; +L_PTRA *pa_data; + + PROCNAME("saConvertFilesToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (scalefactor <= 0.0) scalefactor = 1.0; + if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { + type = L_DEFAULT_ENCODE; + } + + /* Generate all the encoded pdf strings */ + n = sarrayGetCount(sa); + pa_data = ptraCreate(n); + pdftitle = NULL; + for (i = 0; i < n; i++) { + if (i && (i % 10 == 0)) fprintf(stderr, ".. %d ", i); + fname = sarrayGetString(sa, i, L_NOCOPY); + if ((pixs = pixRead(fname)) == NULL) { + L_ERROR("image not readable from file %s\n", procName, fname); + continue; + } + if (!pdftitle) + pdftitle = (title) ? title : fname; + if (scalefactor != 1.0) + pix = pixScale(pixs, scalefactor, scalefactor); + else + pix = pixClone(pixs); + pixDestroy(&pixs); + scaledres = (l_int32)(res * scalefactor); + + /* Select the encoding type */ + if (type != L_DEFAULT_ENCODE) { + pagetype = type; + } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { + pixDestroy(&pix); + L_ERROR("encoding type selection failed for file %s\n", + procName, fname); + continue; + } + + ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, + 0, 0, scaledres, pdftitle, NULL, 0); + pixDestroy(&pix); + if (ret) { + LEPT_FREE(imdata); + L_ERROR("pdf encoding failed for %s\n", procName, fname); + continue; + } + ba = l_byteaInitFromMem(imdata, imbytes); + LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + ptraGetActualCount(pa_data, &npages); + if (npages == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate them */ + fprintf(stderr, "\nconcatenating ... "); + ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); + fprintf(stderr, "done\n"); + + ptraGetActualCount(pa_data, &npages); /* recalculate in case it changes */ + for (i = 0; i < npages; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + + +/*! + * \brief selectDefaultPdfEncoding() + * + * \param[in] pix + * \param[out] ptype L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This attempts to choose an encoding for the pix that results
+ *          in the smallest file, assuming that if jpeg encoded, it will
+ *          use quality = 75.  The decision is approximate, in that
+ *          (a) all colormapped images will be losslessly encoded with
+ *          gzip (flate), and (b) an image with less than about 20 colors
+ *          is likely to be smaller if flate encoded than if encoded
+ *          as a jpeg (dct).  For example, an image made by pixScaleToGray3()
+ *          will have 10 colors, and flate encoding will give about
+ *          twice the compression as jpeg with quality = 75.
+ * 
+ */ +l_ok +selectDefaultPdfEncoding(PIX *pix, + l_int32 *ptype) +{ +l_int32 w, h, d, factor, ncolors; +PIXCMAP *cmap; + + PROCNAME("selectDefaultPdfEncoding"); + + if (!ptype) + return ERROR_INT("&type not defined", procName, 1); + *ptype = L_FLATE_ENCODE; /* default universal encoding */ + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + cmap = pixGetColormap(pix); + if (d == 8 && !cmap) { + factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 20000.)); + pixNumColors(pix, factor, &ncolors); + if (ncolors < 20) + *ptype = L_FLATE_ENCODE; + else + *ptype = L_JPEG_ENCODE; + } else if (d == 1) { + *ptype = L_G4_ENCODE; + } else if (cmap || d == 2 || d == 4) { + *ptype = L_FLATE_ENCODE; + } else if (d == 8 || d == 32) { + *ptype = L_JPEG_ENCODE; + } else { + return ERROR_INT("type selection failure", procName, 1); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Convert specified image files to pdf without scaling * + *---------------------------------------------------------------------*/ +/*! + * \brief convertUnscaledFilesToPdf() + * + * \param[in] dirname directory name containing images + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] title [optional] pdf title; if null, taken from the first + * image filename + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %substr is not NULL, only image filenames that contain
+ *          the substring can be used.  If %substr == NULL, all files
+ *          in the directory are used.
+ *      (2) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order
+ *          before concatenation.
+ *      (3) This is very fast for jpeg, jp2k and some png files, because
+ *          the compressed data is wrapped up and concatenated.  For tiffg4
+ *          and other types of png, the images must be read and recompressed.
+ * 
+ */ +l_ok +convertUnscaledFilesToPdf(const char *dirname, + const char *substr, + const char *title, + const char *fileout) +{ +l_int32 ret; +SARRAY *sa; + + PROCNAME("convertUnscaledFilesToPdf"); + + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return ERROR_INT("sa not made", procName, 1); + ret = saConvertUnscaledFilesToPdf(sa, title, fileout); + sarrayDestroy(&sa); + return ret; +} + + +/*! + * \brief saConvertUnscaledFilesToPdf() + * + * \param[in] sa string array of pathnames for images + * \param[in] title [optional] pdf title; if null, taken from the first + * image filename + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertUnscaledFilesToPdf().
+ * 
+ */ +l_ok +saConvertUnscaledFilesToPdf(SARRAY *sa, + const char *title, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("saConvertUnscaledFilesToPdf"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + ret = saConvertUnscaledFilesToPdfData(sa, title, &data, &nbytes); + if (ret) { + if (data) LEPT_FREE(data); + return ERROR_INT("pdf data not made", procName, 1); + } + + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + L_ERROR("pdf data not written to file\n", procName); + return ret; +} + + +/*! + * \brief saConvertUnscaledFilesToPdfData() + * + * \param[in] sa string array of pathnames for image files + * \param[in] title [optional] pdf title; if null, taken from the first + * image filename + * \param[out] pdata output pdf data (of all images) + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is very fast for jpeg, jp2k and some png files, because
+ *          the compressed data is wrapped up and concatenated.  For tiffg4
+ *          and other types of png, the images must be read and recompressed.
+ * 
+ */ +l_ok +saConvertUnscaledFilesToPdfData(SARRAY *sa, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +char *fname; +l_uint8 *imdata; +l_int32 i, n, ret, npages; +size_t imbytes; +L_BYTEA *ba; +L_PTRA *pa_data; + + PROCNAME("saConvertUnscaledFilesToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + /* Generate all the encoded pdf strings */ + n = sarrayGetCount(sa); + pa_data = ptraCreate(n); + for (i = 0; i < n; i++) { + if (i && (i % 10 == 0)) fprintf(stderr, ".. %d ", i); + fname = sarrayGetString(sa, i, L_NOCOPY); + + /* Generate the pdf data */ + if (convertUnscaledToPdfData(fname, title, &imdata, &imbytes)) + continue; + + /* ... and add it to the array of single page data */ + ba = l_byteaInitFromMem(imdata, imbytes); + if (imdata) LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + ptraGetActualCount(pa_data, &npages); + if (npages == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate to generate a multipage pdf */ + fprintf(stderr, "\nconcatenating ... "); + ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); + fprintf(stderr, "done\n"); + + /* Clean up */ + ptraGetActualCount(pa_data, &npages); /* maybe failed to read some files */ + for (i = 0; i < npages; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + + +/*! + * \brief convertUnscaledToPdfData() + * + * \param[in] fname of image file in all formats + * \param[in] title [optional] pdf title; can be NULL + * \param[out] pdata output pdf data for image + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is very fast for jpeg, jp2k and some png files, because
+ *          the compressed data is wrapped up and concatenated.  For tiffg4
+ *          and other types of png, the images must be read and recompressed.
+ * 
+ */ +l_ok +convertUnscaledToPdfData(const char *fname, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +const char *pdftitle = NULL; +char *tail = NULL; +l_int32 format; +L_COMP_DATA *cid; + + PROCNAME("convertUnscaledToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + + findFileFormat(fname, &format); + if (format == IFF_UNKNOWN) { + L_WARNING("file %s format is unknown; skip\n", procName, fname); + return 1; + } + if (format == IFF_PS || format == IFF_LPDF) { + L_WARNING("file %s format is %d; skip\n", procName, fname, format); + return 1; + } + + /* Generate the image data required for pdf generation, always + * in binary (not ascii85) coding. Note that jpeg, jp2k and + * some png files are not transcoded. */ + l_generateCIDataForPdf(fname, NULL, 0, &cid); + if (!cid) { + L_ERROR("file %s format is %d; unreadable\n", procName, fname, format); + return 1; + } + + /* If %title == NULL, use the tail of %fname. */ + if (title) { + pdftitle = title; + } else { + splitPathAtDirectory(fname, NULL, &tail); + pdftitle = tail; + } + + /* Generate the pdf string for this page (image). This destroys + * the cid by attaching it to an lpd and destroying the lpd. */ + cidConvertToPdfData(cid, pdftitle, pdata, pnbytes); + LEPT_FREE(tail); + return 0; +} + + +/*---------------------------------------------------------------------* + * Convert multiple images to pdf (one image per page) * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaConvertToPdf() + * + * \param[in] pixa containing images all at the same resolution + * \param[in] res override the resolution of each input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE, or + * L_DEFAULT_ENCODE for default) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] title [optional] pdf title + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
+ *          colormap and many colors, or 32 bpp; FLATE for anything else.
+ *      (2) The scalefactor must be > 0.0; otherwise it is set to 1.0.
+ *      (3) Specifying one of the three encoding types for %type forces
+ *          all images to be compressed with that type.  Use 0 to have
+ *          the type determined for each image based on depth and whether
+ *          or not it has a colormap.
+ * 
+ */ +l_ok +pixaConvertToPdf(PIXA *pixa, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("pixaConvertToPdf"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + ret = pixaConvertToPdfData(pixa, res, scalefactor, type, quality, + title, &data, &nbytes); + if (ret) { + LEPT_FREE(data); + return ERROR_INT("conversion to pdf failed", procName, 1); + } + + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + L_ERROR("pdf data not written to file\n", procName); + return ret; +} + + +/*! + * \brief pixaConvertToPdfData() + * + * \param[in] pixa containing images all at the same resolution + * \param[in] res input resolution of all images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE, or + * L_DEFAULT_ENCODE for default) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] title [optional] pdf title + * \param[out] pdata output pdf data of all images + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixaConvertToPdf().
+ * 
+ */ +l_ok +pixaConvertToPdfData(PIXA *pixa, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_uint8 *imdata; +l_int32 i, n, ret, scaledres, pagetype; +size_t imbytes; +L_BYTEA *ba; +PIX *pixs, *pix; +L_PTRA *pa_data; + + PROCNAME("pixaConvertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (scalefactor <= 0.0) scalefactor = 1.0; + if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE && + type != L_G4_ENCODE && type != L_FLATE_ENCODE && + type != L_JP2K_ENCODE) { + L_WARNING("invalid compression type; using per-page default\n", + procName); + type = L_DEFAULT_ENCODE; + } + + /* Generate all the encoded pdf strings */ + n = pixaGetCount(pixa); + pa_data = ptraCreate(n); + for (i = 0; i < n; i++) { + if ((pixs = pixaGetPix(pixa, i, L_CLONE)) == NULL) { + L_ERROR("pix[%d] not retrieved\n", procName, i); + continue; + } + if (scalefactor != 1.0) + pix = pixScale(pixs, scalefactor, scalefactor); + else + pix = pixClone(pixs); + pixDestroy(&pixs); + scaledres = (l_int32)(res * scalefactor); + + /* Select the encoding type */ + if (type != L_DEFAULT_ENCODE) { + pagetype = type; + } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { + L_ERROR("encoding type selection failed for pix[%d]\n", + procName, i); + pixDestroy(&pix); + continue; + } + + ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, + 0, 0, scaledres, title, NULL, 0); + pixDestroy(&pix); + if (ret) { + LEPT_FREE(imdata); + L_ERROR("pdf encoding failed for pix[%d]\n", procName, i); + continue; + } + ba = l_byteaInitFromMem(imdata, imbytes); + LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + ptraGetActualCount(pa_data, &n); + if (n == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate them */ + ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); + + ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ + for (i = 0; i < n; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + + +/*---------------------------------------------------------------------* + * Single page, multi-image converters * + *---------------------------------------------------------------------*/ +/*! + * \brief convertToPdf() + * + * \param[in] filein input image file -- any format + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, or L_JP2K_ENCODE) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] fileout output pdf file; only required on last + * image on page + * \param[in] x, y location of lower-left corner of image, + * in pixels, relative to the PostScript origin + * (0,0) at the lower-left corner of the page + * \param[in] res override the resolution of the input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] title [optional] pdf title; if null, taken from filein + * \param[in,out] plpd ptr to lpd, which is created on the first + * invocation and returned until last image is + * processed, at which time it is destroyed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) To wrap only one image in pdf, input %plpd = NULL, and
+ *          the value of %position will be ignored:
+ *            convertToPdf(...  type, quality, x, y, res, NULL, 0);
+ *      (2) To wrap multiple images on a single pdf page, this is called
+ *          once for each successive image.  Do it this way:
+ *            L_PDF_DATA   *lpd;
+ *            convertToPdf(...  type, quality, x, y, res, &lpd, L_FIRST_IMAGE);
+ *            convertToPdf(...  type, quality, x, y, res, &lpd, L_NEXT_IMAGE);
+ *            ...
+ *            convertToPdf(...  type, quality, x, y, res, &lpd, L_LAST_IMAGE);
+ *          This will write the result to the value of %fileout specified
+ *          in the first call; succeeding values of %fileout are ignored.
+ *          On the last call: the pdf data bytes are computed and written
+ *          to %fileout, lpd is destroyed internally, and the returned
+ *          value of lpd is null.  So the client has nothing to clean up.
+ *      (3) (a) Set %res == 0 to respect the resolution embedded in the
+ *              image file.  If no resolution is embedded, it will be set
+ *              to the default value.
+ *          (b) Set %res to some other value to override the file resolution.
+ *      (4) (a) If the input %res and the resolution of the output device
+ *              are equal, the image will be "displayed" at the same size
+ *              as the original.
+ *          (b) If the input %res is 72, the output device will render
+ *              the image at 1 pt/pixel.
+ *          (c) Some possible choices for the default input pix resolution are:
+ *                 72 ppi     Render pix on any output device at one pt/pixel
+ *                 96 ppi     Windows default for generated display images
+ *                300 ppi     Typical default for scanned images.
+ *              We choose 300, which is sensible for rendering page images.
+ *              However,  images come from a variety of sources, and
+ *              some are explicitly created for viewing on a display.
+ * 
+ */ +l_ok +convertToPdf(const char *filein, + l_int32 type, + l_int32 quality, + const char *fileout, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("convertToPdf"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!plpd || (position == L_LAST_IMAGE)) { + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + } + + if (convertToPdfData(filein, type, quality, &data, &nbytes, x, y, + res, title, plpd, position)) + return ERROR_INT("pdf data not made", procName, 1); + + if (!plpd || (position == L_LAST_IMAGE)) { + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + return ERROR_INT("pdf data not written to file", procName, 1); + } + + return 0; +} + + +/*! + * \brief convertImageDataToPdf() + * + * \param[in] imdata array of formatted image data; e.g., png, jpeg + * \param[in] size size of image data + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, or L_JP2K_ENCODE) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] fileout output pdf file; only required on last + * image on page + * \param[in] x, y location of lower-left corner of image, + * in pixels, relative to the PostScript origin + * (0,0) at the lower-left corner of the page + * \param[in] res override the resolution of the input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] title [optional] pdf title + * \param[in,out] plpd ptr to lpd, which is created on the first + * invocation and returned until last image is + * processed, at which time it is destroyed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %res == 0 and the input resolution field is 0,
+ *          this will use DefaultInputRes.
+ *      (2) See comments in convertToPdf().
+ * 
+ */ +l_ok +convertImageDataToPdf(l_uint8 *imdata, + size_t size, + l_int32 type, + l_int32 quality, + const char *fileout, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +l_int32 ret; +PIX *pix; + + PROCNAME("convertImageDataToPdf"); + + if (!imdata) + return ERROR_INT("image data not defined", procName, 1); + if (!plpd || (position == L_LAST_IMAGE)) { + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + } + + if ((pix = pixReadMem(imdata, size)) == NULL) + return ERROR_INT("pix not read", procName, 1); + if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { + selectDefaultPdfEncoding(pix, &type); + } + ret = pixConvertToPdf(pix, type, quality, fileout, x, y, res, + title, plpd, position); + pixDestroy(&pix); + return ret; +} + + +/*! + * \brief convertToPdfData() + * + * \param[in] filein input image file -- any format + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, or L_JP2K_ENCODE) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[out] pdata pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \param[in] x, y location of lower-left corner of image, + * in pixels, relative to the PostScript origin + * (0,0) at the lower-left corner of the page + * \param[in] res override the resolution of the input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] title [optional] pdf title; if null, use filein + * \param[in,out] plpd ptr to lpd, which is created on the first + * invocation and returned until last image is + * processed, at which time it is destroyed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %res == 0 and the input resolution field is 0,
+ *          this will use DefaultInputRes.
+ *      (2) See comments in convertToPdf().
+ * 
+ */ +l_ok +convertToPdfData(const char *filein, + l_int32 type, + l_int32 quality, + l_uint8 **pdata, + size_t *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +PIX *pix; + + PROCNAME("convertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + + if ((pix = pixRead(filein)) == NULL) + return ERROR_INT("pix not made", procName, 1); + + pixConvertToPdfData(pix, type, quality, pdata, pnbytes, + x, y, res, (title) ? title : filein, plpd, position); + pixDestroy(&pix); + return 0; +} + + +/*! + * \brief convertImageDataToPdfData() + * + * \param[in] imdata array of formatted image data; e.g., png, jpeg + * \param[in] size size of image data + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, or L_JP2K_ENCODE) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[out] pdata pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \param[in] x, y location of lower-left corner of image, + * in pixels, relative to the PostScript origin + * (0,0) at the lower-left corner of the page + * \param[in] res override the resolution of the input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] title [optional] pdf title + * \param[out] plpd ptr to lpd, which is created on the first + * invocation and returned until last image is + * processed, at which time it is destroyed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %res == 0 and the input resolution field is 0,
+ *          this will use DefaultInputRes.
+ *      (2) See comments in convertToPdf().
+ * 
+ */ +l_ok +convertImageDataToPdfData(l_uint8 *imdata, + size_t size, + l_int32 type, + l_int32 quality, + l_uint8 **pdata, + size_t *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +l_int32 ret; +PIX *pix; + + PROCNAME("convertImageDataToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!imdata) + return ERROR_INT("image data not defined", procName, 1); + if (plpd) { /* part of multi-page invocation */ + if (position == L_FIRST_IMAGE) + *plpd = NULL; + } + + if ((pix = pixReadMem(imdata, size)) == NULL) + return ERROR_INT("pix not read", procName, 1); + if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { + selectDefaultPdfEncoding(pix, &type); + } + ret = pixConvertToPdfData(pix, type, quality, pdata, pnbytes, + x, y, res, title, plpd, position); + pixDestroy(&pix); + return ret; +} + + +/*! + * \brief pixConvertToPdf() + * + * \param[in] pix + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE) + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[in] fileout output pdf file; only required on last + * image on page + * \param[in] x, y location of lower-left corner of image, + * in pixels, relative to the PostScript origin + * (0,0) at the lower-left corner of the page + * \param[in] res override the resolution of the input image, + * in ppi; use 0 to respect the resolution + * embedded in the input images + * \param[in] title [optional] pdf title + * \param[in,out] plpd ptr to lpd, which is created on the first + * invocation and returned until last image is + * processed, at which time it is destroyed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %res == 0 and the input resolution field is 0,
+ *          this will use DefaultInputRes.
+ *      (2) This only writes data to fileout if it is the last
+ *          image to be written on the page.
+ *      (3) See comments in convertToPdf().
+ * 
+ */ +l_ok +pixConvertToPdf(PIX *pix, + l_int32 type, + l_int32 quality, + const char *fileout, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("pixConvertToPdf"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!plpd || (position == L_LAST_IMAGE)) { + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + } + + if (pixConvertToPdfData(pix, type, quality, &data, &nbytes, + x, y, res, title, plpd, position)) { + LEPT_FREE(data); + return ERROR_INT("pdf data not made", procName, 1); + } + + if (!plpd || (position == L_LAST_IMAGE)) { + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + return ERROR_INT("pdf data not written to file", procName, 1); + } + return 0; +} + + +/*! + * \brief pixWriteStreamPdf() + * + * \param[in] fp file stream opened for writing + * \param[in] pix all depths, cmap OK + * \param[in] res override the resolution of the input image, in ppi; + * use 0 to respect the resolution embedded in the input + * \param[in] title [optional] pdf title; taken from the first image + * placed on a page; e.g., an input image filename + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is the simplest interface for writing a single image
+ *          with pdf encoding to a stream.  It uses G4 encoding for 1 bpp,
+ *          JPEG encoding for 8 bpp (no cmap) and 32 bpp, and FLATE
+ *          encoding for everything else.
+ * 
+ */ +l_ok +pixWriteStreamPdf(FILE *fp, + PIX *pix, + l_int32 res, + const char *title) +{ +l_uint8 *data; +size_t nbytes, nbytes_written; + + PROCNAME("pixWriteStreamPdf"); + + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (pixWriteMemPdf(&data, &nbytes, pix, res, title) != 0) { + LEPT_FREE(data); + return ERROR_INT("pdf data not made", procName, 1); + } + + nbytes_written = fwrite(data, 1, nbytes, fp); + LEPT_FREE(data); + if (nbytes != nbytes_written) + return ERROR_INT("failure writing pdf data to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteMemPdf() + * + * \param[out] pdata pdf as byte array + * \param[out] pnbytes number of bytes in pdf array + * \param[in] pix all depths, cmap OK + * \param[in] res override the resolution of the input image, in ppi; + * use 0 to respect the res embedded in the input + * \param[in] title [optional] pdf title; taken from the first image + * placed on a page; e.g., an input image filename + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is the simplest interface for writing a single image
+ *          with pdf encoding to memory.  It uses G4 encoding for 1 bpp,
+ *          and makes a guess whether to use JPEG or FLATE encoding for
+ *          everything else.
+ * 
+ */ +l_ok +pixWriteMemPdf(l_uint8 **pdata, + size_t *pnbytes, + PIX *pix, + l_int32 res, + const char *title) +{ +l_int32 ret, type; + + PROCNAME("pixWriteMemPdf"); + + if (pdata) *pdata = NULL; + if (pnbytes) *pnbytes = 0; + if (!pdata || !pnbytes) + return ERROR_INT("&data or &nbytes not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + selectDefaultPdfEncoding(pix, &type); + ret = pixConvertToPdfData(pix, type, 75, pdata, pnbytes, + 0, 0, res, title, NULL, 0); + if (ret) + return ERROR_INT("pdf data not made", procName, 1); + return 0; +} + + +/*---------------------------------------------------------------------* + * Segmented multi-page, multi-image converter * + *---------------------------------------------------------------------*/ +/*! + * \brief convertSegmentedFilesToPdf() + * + * \param[in] dirname directory name containing images + * \param[in] substr [optional] substring filter on filenames; + * can be NULL + * \param[in] res input resolution of all images + * \param[in] type compression type for non-image regions; the + * image regions are always compressed with + * L_JPEG_ENCODE + * \param[in] thresh used for converting gray --> 1 bpp with + * L_G4_ENCODE + * \param[in] baa [optional] boxaa of image regions + * \param[in] quality used for JPEG only; 0 for default (75) + * \param[in] scalefactor scaling factor applied to each image region + * \param[in] title [optional] pdf title; if null, taken from + * the first image filename + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %substr is not NULL, only image filenames that contain
+ *          the substring can be used.  If %substr == NULL, all files
+ *          in the directory are used.
+ *      (2) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order
+ *          before concatenation.
+ *      (3) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
+ *          colormap and many colors, or 32 bpp; FLATE for anything else.
+ *      (4) The boxaa, if it exists, contains one boxa of "image regions"
+ *          for each image file.  The boxa must be aligned with the
+ *          sorted set of images.
+ *      (5) The scalefactor is applied to each image region.  It is
+ *          typically < 1.0, to save bytes in the final pdf, because
+ *          the resolution is often not critical in non-text regions.
+ *      (6) If the non-image regions have pixel depth > 1 and the encoding
+ *          type is G4, they are automatically scaled up by 2x and
+ *          thresholded.  Otherwise, no scaling is performed on them.
+ *      (7) Note that this function can be used to generate multipage
+ *          G4 compressed pdf from any input, by using %boxaa == NULL
+ *          and %type == L_G4_ENCODE.
+ * 
+ */ +l_ok +convertSegmentedFilesToPdf(const char *dirname, + const char *substr, + l_int32 res, + l_int32 type, + l_int32 thresh, + BOXAA *baa, + l_int32 quality, + l_float32 scalefactor, + const char *title, + const char *fileout) +{ +char *fname; +l_uint8 *imdata, *data; +l_int32 i, npages, nboxa, nboxes, ret; +size_t imbytes, databytes; +BOXA *boxa; +L_BYTEA *ba; +L_PTRA *pa_data; +SARRAY *sa; + + PROCNAME("convertSegmentedFilesToPdf"); + + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((sa = getNumberedPathnamesInDirectory(dirname, substr, 0, 0, 10000)) + == NULL) + return ERROR_INT("sa not made", procName, 1); + + npages = sarrayGetCount(sa); + /* If necessary, extend the boxaa, which is page-aligned with + * the image files, to be as large as the set of images. */ + if (baa) { + nboxa = boxaaGetCount(baa); + if (nboxa < npages) { + boxa = boxaCreate(1); + boxaaExtendWithInit(baa, npages, boxa); + boxaDestroy(&boxa); + } + } + + /* Generate and save all the encoded pdf strings */ + pa_data = ptraCreate(npages); + for (i = 0; i < npages; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if (!strcmp(fname, "")) continue; + boxa = NULL; + if (baa) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + nboxes = boxaGetCount(boxa); + if (nboxes == 0) + boxaDestroy(&boxa); + } + ret = convertToPdfDataSegmented(fname, res, type, thresh, boxa, + quality, scalefactor, title, + &imdata, &imbytes); + boxaDestroy(&boxa); /* safe; in case nboxes > 0 */ + if (ret) { + L_ERROR("pdf encoding failed for %s\n", procName, fname); + continue; + } + ba = l_byteaInitFromMem(imdata, imbytes); + if (imdata) LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + sarrayDestroy(&sa); + + ptraGetActualCount(pa_data, &npages); + if (npages == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate */ + ret = ptraConcatenatePdfToData(pa_data, NULL, &data, &databytes); + + /* Clean up */ + ptraGetActualCount(pa_data, &npages); /* recalculate in case it changes */ + for (i = 0; i < npages; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + + if (ret) { + if (data) LEPT_FREE(data); + return ERROR_INT("pdf data not made", procName, 1); + } + + ret = l_binaryWrite(fileout, "w", data, databytes); + LEPT_FREE(data); + if (ret) + L_ERROR("pdf data not written to file\n", procName); + return ret; +} + + +/*! + * \brief convertNumberedMasksToBoxaa() + * + * \param[in] dirname directory name containing mask images + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] numpre number of characters in name before number + * \param[in] numpost number of characters in name after number, up + * to a dot before an extension + * \return boxaa of mask regions, or NULL on error + * + *
+ * Notes:
+ *      (1) This is conveniently used to generate the input boxaa
+ *          for convertSegmentedFilesToPdf().  It guarantees that the
+ *          boxa will be aligned with the page images, even if some
+ *          of the boxa are empty.
+ * 
+ */ +BOXAA * +convertNumberedMasksToBoxaa(const char *dirname, + const char *substr, + l_int32 numpre, + l_int32 numpost) +{ +char *fname; +l_int32 i, n; +BOXA *boxa; +BOXAA *baa; +PIX *pix; +SARRAY *sa; + + PROCNAME("convertNumberedMasksToBoxaa"); + + if (!dirname) + return (BOXAA *)ERROR_PTR("dirname not defined", procName, NULL); + + if ((sa = getNumberedPathnamesInDirectory(dirname, substr, numpre, + numpost, 10000)) == NULL) + return (BOXAA *)ERROR_PTR("sa not made", procName, NULL); + + /* Generate and save all the encoded pdf strings */ + n = sarrayGetCount(sa); + baa = boxaaCreate(n); + boxa = boxaCreate(1); + boxaaInitFull(baa, boxa); + boxaDestroy(&boxa); + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if (!strcmp(fname, "")) continue; + if ((pix = pixRead(fname)) == NULL) { + L_WARNING("invalid image on page %d\n", procName, i); + continue; + } + boxa = pixConnComp(pix, NULL, 8); + boxaaReplaceBoxa(baa, i, boxa); + pixDestroy(&pix); + } + + sarrayDestroy(&sa); + return baa; +} + + +/*---------------------------------------------------------------------* + * Segmented single page, multi-image converters * + *---------------------------------------------------------------------*/ +/*! + * \brief convertToPdfSegmented() + * + * \param[in] filein input image file -- any format + * \param[in] res input image resolution; typ. 300 ppi; + * use 0 for default + * \param[in] type compression type for non-image regions; image + * regions are always compressed with L_JPEG_ENCODE + * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE + * \param[in] boxa [optional] of image regions; can be null + * \param[in] quality used for jpeg image regions; 0 for default + * \param[in] scalefactor used for jpeg regions; must be <= 1.0 + * \param[in] title [optional] pdf title; typically taken from the + * input file for the pix + * \param[in] fileout output pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there are no image regions, set %boxa == NULL;
+ *          %quality and %scalefactor are ignored.
+ *      (2) Typically, %scalefactor is < 1.0, because the image regions
+ *          can be rendered at a lower resolution (for better compression)
+ *          than the text regions.  If %scalefactor == 0, we use 1.0.
+ *          If the input image is 1 bpp and scalefactor < 1.0, we
+ *          use scaleToGray() to downsample the image regions to gray
+ *          before compressing them.
+ *      (3) If the compression type for non-image regions is L_G4_ENCODE
+ *          and bpp > 1, the image is upscaled 2x and thresholded
+ *          to 1 bpp.  That is the only situation where %thresh is used.
+ *      (4) The parameter %quality is only used for image regions.
+ *          If %type == L_JPEG_ENCODE, default jpeg quality (75) is
+ *          used for the non-image regions.
+ *      (5) Processing matrix for non-image regions.
+ *
+ *          Input           G4              JPEG                FLATE
+ *          ----------|---------------------------------------------------
+ *          1 bpp     |  1x, 1 bpp       1x flate, 1 bpp     1x, 1 bpp
+ *                    |
+ *          cmap      |  2x, 1 bpp       1x flate, cmap      1x, cmap
+ *                    |
+ *          2,4 bpp   |  2x, 1 bpp       1x flate            1x, 2,4 bpp
+ *          no cmap   |                  2,4 bpp
+ *                    |
+ *          8,32 bpp  |  2x, 1 bpp       1x (jpeg)           1x, 8,32 bpp
+ *          no cmap   |                  8,32 bpp
+ *
+ *          Summary:
+ *          (a) if G4 is requested, G4 is used, with 2x upscaling
+ *              for all cases except 1 bpp.
+ *          (b) if JPEG is requested, use flate encoding for all cases
+ *              except 8 bpp without cmap and 32 bpp (rgb).
+ *          (c) if FLATE is requested, use flate with no transformation
+ *              of the raster data.
+ *      (6) Calling options/sequence for these functions:
+ *              file  -->  file      (convertToPdfSegmented)
+ *                  pix  -->  file      (pixConvertToPdfSegmented)
+ *                      pix  -->  data      (pixConvertToPdfDataSegmented)
+ *              file  -->  data      (convertToPdfDataSegmented)
+ *                      pix  -->  data      (pixConvertToPdfDataSegmented)
+ * 
+ */ +l_ok +convertToPdfSegmented(const char *filein, + l_int32 res, + l_int32 type, + l_int32 thresh, + BOXA *boxa, + l_int32 quality, + l_float32 scalefactor, + const char *title, + const char *fileout) +{ +l_int32 ret; +PIX *pixs; + + PROCNAME("convertToPdfSegmented"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE) + return ERROR_INT("invalid conversion type", procName, 1); + if (boxa && scalefactor > 1.0) { + L_WARNING("setting scalefactor to 1.0\n", procName); + scalefactor = 1.0; + } + + if ((pixs = pixRead(filein)) == NULL) + return ERROR_INT("pixs not made", procName, 1); + + ret = pixConvertToPdfSegmented(pixs, res, type, thresh, boxa, quality, + scalefactor, (title) ? title : filein, + fileout); + pixDestroy(&pixs); + return ret; +} + + +/*! + * \brief pixConvertToPdfSegmented() + * + * \param[in] pixs any depth, cmap OK + * \param[in] res input image resolution; typ. 300 ppi; + * use 0 for default + * \param[in] type compression type for non-image regions; image + * regions are always compressed with L_JPEG_ENCODE + * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE + * \param[in] boxa [optional] of image regions; can be null + * \param[in] quality used for jpeg image regions; 0 for default + * \param[in] scalefactor used for jpeg regions; must be <= 1.0 + * \param[in] title [optional] pdf title; typically taken from the + * input file for the pix + * \param[in] fileout output pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertToPdfSegmented() for details.
+ * 
+ */ +l_ok +pixConvertToPdfSegmented(PIX *pixs, + l_int32 res, + l_int32 type, + l_int32 thresh, + BOXA *boxa, + l_int32 quality, + l_float32 scalefactor, + const char *title, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("pixConvertToPdfSegmented"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE) + return ERROR_INT("invalid conversion type", procName, 1); + if (boxa && scalefactor > 1.0) { + L_WARNING("setting scalefactor to 1.0\n", procName); + scalefactor = 1.0; + } + + ret = pixConvertToPdfDataSegmented(pixs, res, type, thresh, boxa, quality, + scalefactor, title, &data, &nbytes); + if (ret) + return ERROR_INT("pdf generation failure", procName, 1); + + ret = l_binaryWrite(fileout, "w", data, nbytes); + if (data) LEPT_FREE(data); + return ret; +} + + +/*! + * \brief convertToPdfDataSegmented() + * + * \param[in] filein input image file -- any format + * \param[in] res input image resolution; typ. 300 ppi; + * use 0 for default + * \param[in] type compression type for non-image regions; image + * regions are always compressed with L_JPEG_ENCODE + * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE + * \param[in] boxa [optional] image regions; can be null + * \param[in] quality used for jpeg image regions; 0 for default + * \param[in] scalefactor used for jpeg regions; must be <= 1.0 + * \param[in] title [optional] pdf title; if null, uses filein + * \param[out] pdata pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there are no image regions, set %boxa == NULL;
+ *          %quality and %scalefactor are ignored.
+ *      (2) Typically, %scalefactor is < 1.0.  The image regions are
+ * 
+ */ +l_ok +convertToPdfDataSegmented(const char *filein, + l_int32 res, + l_int32 type, + l_int32 thresh, + BOXA *boxa, + l_int32 quality, + l_float32 scalefactor, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 ret; +PIX *pixs; + + PROCNAME("convertToPdfDataSegmented"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE) + return ERROR_INT("invalid conversion type", procName, 1); + if (boxa && scalefactor > 1.0) { + L_WARNING("setting scalefactor to 1.0\n", procName); + scalefactor = 1.0; + } + + if ((pixs = pixRead(filein)) == NULL) + return ERROR_INT("pixs not made", procName, 1); + + ret = pixConvertToPdfDataSegmented(pixs, res, type, thresh, boxa, + quality, scalefactor, + (title) ? title : filein, + pdata, pnbytes); + pixDestroy(&pixs); + return ret; +} + + +/*! + * \brief pixConvertToPdfDataSegmented() + * + * \param[in] pixs any depth, cmap OK + * \param[in] res input image resolution; typ. 300 ppi; + * use 0 for default + * \param[in] type compression type for non-image regions; image + * regions are always compressed with L_JPEG_ENCODE + * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE + * \param[in] boxa [optional] of image regions; can be null + * \param[in] quality used for jpeg image regions; 0 for default + * \param[in] scalefactor used for jpeg regions; must be <= 1.0 + * \param[in] title [optional] pdf title; typically taken from the + * input file for the pix + * \param[out] pdata pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See convertToPdfSegmented() for details.
+ * 
+ */ +l_ok +pixConvertToPdfDataSegmented(PIX *pixs, + l_int32 res, + l_int32 type, + l_int32 thresh, + BOXA *boxa, + l_int32 quality, + l_float32 scalefactor, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 i, nbox, seq, bx, by, bw, bh, upscale; +l_float32 scale; +BOX *box, *boxc, *box2; +PIX *pix, *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; +PIXCMAP *cmap; +L_PDF_DATA *lpd; + + PROCNAME("pixConvertToPdfDataSegmented"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE) + return ERROR_INT("invalid conversion type", procName, 1); + if (boxa && (scalefactor <= 0.0 || scalefactor > 1.0)) { + L_WARNING("setting scalefactor to 1.0\n", procName); + scalefactor = 1.0; + } + + /* Adjust scalefactor so that the product with res gives an integer */ + if (res <= 0) + res = DefaultInputRes; + scale = (l_float32)((l_int32)(scalefactor * res + 0.5)) / (l_float32)res; + cmap = pixGetColormap(pixs); + + /* Simple case: single image to be encoded */ + if (!boxa || boxaGetCount(boxa) == 0) { + if (pixGetDepth(pixs) > 1 && type == L_G4_ENCODE) { + if (cmap) + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixt1 = pixConvertTo8(pixs, FALSE); + pixt2 = pixScaleGray2xLIThresh(pixt1, thresh); + pixConvertToPdfData(pixt2, type, quality, pdata, pnbytes, + 0, 0, 2 * res, title, NULL, 0); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + } else { + pixConvertToPdfData(pixs, type, quality, pdata, pnbytes, + 0, 0, res, title, NULL, 0); + } + return 0; + } + + /* Multiple images to be encoded. If %type == L_G4_ENCODE, + * jpeg encode a version of pixs that is blanked in the non-image + * regions, and paint the scaled non-image part onto it through a mask. + * Otherwise, we must put the non-image part down first and + * then render all the image regions separately on top of it, + * at their own resolution. */ + pixt1 = pixSetBlackOrWhiteBoxa(pixs, boxa, L_SET_WHITE); /* non-image */ + nbox = boxaGetCount(boxa); + if (type == L_G4_ENCODE) { + pixt2 = pixCreateTemplate(pixs); /* only image regions */ + pixSetBlackOrWhite(pixt2, L_SET_WHITE); + for (i = 0; i < nbox; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pix = pixClipRectangle(pixs, box, &boxc); + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + pixRasterop(pixt2, bx, by, bw, bh, PIX_SRC, pix, 0, 0); + pixDestroy(&pix); + boxDestroy(&box); + boxDestroy(&boxc); + } + pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pixt3) == 1) + pixt4 = pixScaleToGray(pixt3, scale); + else + pixt4 = pixScale(pixt3, scale, scale); + pixConvertToPdfData(pixt4, L_JPEG_ENCODE, quality, pdata, pnbytes, + 0, 0, (l_int32)(scale * res), title, + &lpd, L_FIRST_IMAGE); + + if (pixGetDepth(pixt1) == 1) { + pixt5 = pixClone(pixt1); + upscale = 1; + } else { + pixt6 = pixConvertTo8(pixt1, 0); + pixt5 = pixScaleGray2xLIThresh(pixt6, thresh); + pixDestroy(&pixt6); + upscale = 2; + } + pixConvertToPdfData(pixt5, L_G4_ENCODE, quality, pdata, pnbytes, + 0, 0, upscale * res, title, &lpd, L_LAST_IMAGE); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + pixDestroy(&pixt4); + pixDestroy(&pixt5); + } else { + /* Put the non-image part down first. This is the full + size of the page, so we can use it to find the page + height in pixels, which is required for determining + the LL corner of the image relative to the LL corner + of the page. */ + pixConvertToPdfData(pixt1, type, quality, pdata, pnbytes, 0, 0, + res, title, &lpd, L_FIRST_IMAGE); + for (i = 0; i < nbox; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pixt2 = pixClipRectangle(pixs, box, &boxc); + pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_BASED_ON_SRC); + if (pixGetDepth(pixt3) == 1) + pixt4 = pixScaleToGray(pixt3, scale); + else + pixt4 = pixScale(pixt3, scale, scale); + box2 = boxTransform(boxc, 0, 0, scale, scale); + boxGetGeometry(box2, &bx, &by, NULL, &bh); + seq = (i == nbox - 1) ? L_LAST_IMAGE : L_NEXT_IMAGE; + pixConvertToPdfData(pixt4, L_JPEG_ENCODE, quality, pdata, pnbytes, + bx, by, (l_int32)(scale * res), title, + &lpd, seq); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + pixDestroy(&pixt4); + boxDestroy(&box); + boxDestroy(&boxc); + boxDestroy(&box2); + } + } + + pixDestroy(&pixt1); + return 0; +} + + +/*---------------------------------------------------------------------* + * Multi-page concatenation * + *---------------------------------------------------------------------*/ +/*! + * \brief concatenatePdf() + * + * \param[in] dirname directory name containing single-page pdf files + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] fileout concatenated pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ *      (2) If %substr is not NULL, only filenames that contain
+ *          the substring can be returned.  If %substr == NULL,
+ *          none of the filenames are filtered out.
+ *      (3) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order
+ *          before concatenation.
+ * 
+ */ +l_ok +concatenatePdf(const char *dirname, + const char *substr, + const char *fileout) +{ +l_int32 ret; +SARRAY *sa; + + PROCNAME("concatenatePdf"); + + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return ERROR_INT("sa not made", procName, 1); + ret = saConcatenatePdf(sa, fileout); + sarrayDestroy(&sa); + return ret; +} + + +/*! + * \brief saConcatenatePdf() + * + * \param[in] sa string array of pathnames for single-page pdf files + * \param[in] fileout concatenated pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ * 
+ */ +l_ok +saConcatenatePdf(SARRAY *sa, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("saConcatenatePdf"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + ret = saConcatenatePdfToData(sa, &data, &nbytes); + if (ret) + return ERROR_INT("pdf data not made", procName, 1); + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + return ret; +} + + +/*! + * \brief ptraConcatenatePdf() + * + * \param[in] pa array of pdf strings, each for a single-page pdf file + * \param[in] fileout concatenated pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ * 
+ */ +l_ok +ptraConcatenatePdf(L_PTRA *pa, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("ptraConcatenatePdf"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + ret = ptraConcatenatePdfToData(pa, NULL, &data, &nbytes); + if (ret) + return ERROR_INT("pdf data not made", procName, 1); + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + return ret; +} + + +/*! + * \brief concatenatePdfToData() + * + * \param[in] dirname directory name containing single-page pdf files + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[out] pdata concatenated pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ *      (2) If %substr is not NULL, only filenames that contain
+ *          the substring can be returned.  If %substr == NULL,
+ *          none of the filenames are filtered out.
+ *      (3) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order
+ *          before concatenation.
+ * 
+ */ +l_ok +concatenatePdfToData(const char *dirname, + const char *substr, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 ret; +SARRAY *sa; + + PROCNAME("concatenatePdfToData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!dirname) + return ERROR_INT("dirname not defined", procName, 1); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return ERROR_INT("sa not made", procName, 1); + ret = saConcatenatePdfToData(sa, pdata, pnbytes); + sarrayDestroy(&sa); + return ret; +} + + +/*! + * \brief saConcatenatePdfToData() + * + * \param[in] sa string array of pathnames for single-page pdf files + * \param[out] pdata concatenated pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ * 
+ */ +l_ok +saConcatenatePdfToData(SARRAY *sa, + l_uint8 **pdata, + size_t *pnbytes) +{ +char *fname; +l_int32 i, npages, ret; +L_BYTEA *bas; +L_PTRA *pa_data; /* input pdf data for each page */ + + PROCNAME("saConcatenatePdfToData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + /* Read the pdf files into memory */ + if ((npages = sarrayGetCount(sa)) == 0) + return ERROR_INT("no filenames found", procName, 1); + pa_data = ptraCreate(npages); + for (i = 0; i < npages; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + bas = l_byteaInitFromFile(fname); + ptraAdd(pa_data, bas); + } + + ret = ptraConcatenatePdfToData(pa_data, sa, pdata, pnbytes); + + /* Cleanup: some pages could have been removed */ + ptraGetActualCount(pa_data, &npages); + for (i = 0; i < npages; i++) { + bas = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&bas); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + +/* --------------------------------------------*/ +#endif /* USE_PDFIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pdfio1stub.c b/hgdriver/3rdparty/hgOCR/leptonica/pdfio1stub.c new file mode 100644 index 0000000..a56def8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pdfio1stub.c @@ -0,0 +1,305 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pdfio1stub.c + *
+ *
+ *     Stubs for pdfio1.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_PDFIO /* defined in environ.h */ +/* --------------------------------------------*/ + +/* ----------------------------------------------------------------------*/ + +l_ok convertFilesToPdf(const char *dirname, const char *substr, + l_int32 res, l_float32 scalefactor, + l_int32 type, l_int32 quality, + const char *title, const char *fileout) +{ + return ERROR_INT("function not present", "convertFilesToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConvertFilesToPdf(SARRAY *sa, l_int32 res, l_float32 scalefactor, + l_int32 type, l_int32 quality, + const char *title, const char *fileout) +{ + return ERROR_INT("function not present", "saConvertFilesToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConvertFilesToPdfData(SARRAY *sa, l_int32 res, + l_float32 scalefactor, l_int32 type, + l_int32 quality, const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "saConvertFilesToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok selectDefaultPdfEncoding(PIX *pix, l_int32 *ptype) +{ + return ERROR_INT("function not present", "selectDefaultPdfEncoding", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertUnscaledFilesToPdf(const char *dirname, const char *substr, + const char *title, const char *fileout) +{ + return ERROR_INT("function not present", "convertUnscaledFilesToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConvertUnscaledFilesToPdf(SARRAY *sa, const char *title, + const char *fileout) +{ + return ERROR_INT("function not present", "saConvertUnscaledFilesToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConvertUnscaledFilesToPdfData(SARRAY *sa, const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", + "saConvertUnscaledFilesToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertUnscaledToPdfData(const char *fname, const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "convertUnscaledToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaConvertToPdf(PIXA *pixa, l_int32 res, l_float32 scalefactor, + l_int32 type, l_int32 quality, + const char *title, const char *fileout) +{ + return ERROR_INT("function not present", "pixaConvertToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaConvertToPdfData(PIXA *pixa, l_int32 res, l_float32 scalefactor, + l_int32 type, l_int32 quality, const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "pixaConvertToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertToPdf(const char *filein, + l_int32 type, l_int32 quality, + const char *fileout, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "convertToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertImageDataToPdf(l_uint8 *imdata, size_t size, + l_int32 type, l_int32 quality, + const char *fileout, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "convertImageDataToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertToPdfData(const char *filein, + l_int32 type, l_int32 quality, + l_uint8 **pdata, size_t *pnbytes, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "convertToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertImageDataToPdfData(l_uint8 *imdata, size_t size, + l_int32 type, l_int32 quality, + l_uint8 **pdata, size_t *pnbytes, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "convertImageDataToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixConvertToPdf(PIX *pix, l_int32 type, l_int32 quality, + const char *fileout, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "pixConvertToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamPdf(FILE *fp, PIX *pix, l_int32 res, const char *title) +{ + return ERROR_INT("function not present", "pixWriteStreamPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemPdf(l_uint8 **pdata, size_t *pnbytes, PIX *pix, + l_int32 res, const char *title) +{ + return ERROR_INT("function not present", "pixWriteMemPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertSegmentedFilesToPdf(const char *dirname, const char *substr, + l_int32 res, l_int32 type, l_int32 thresh, + BOXAA *baa, l_int32 quality, + l_float32 scalefactor, const char *title, + const char *fileout) +{ + return ERROR_INT("function not present", "convertSegmentedFilesToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +BOXAA * convertNumberedMasksToBoxaa(const char *dirname, const char *substr, + l_int32 numpre, l_int32 numpost) +{ + return (BOXAA *)ERROR_PTR("function not present", + "convertNumberedMasksToBoxaa", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertToPdfSegmented(const char *filein, l_int32 res, l_int32 type, + l_int32 thresh, BOXA *boxa, l_int32 quality, + l_float32 scalefactor, const char *title, + const char *fileout) +{ + return ERROR_INT("function not present", "convertToPdfSegmented", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixConvertToPdfSegmented(PIX *pixs, l_int32 res, l_int32 type, + l_int32 thresh, BOXA *boxa, l_int32 quality, + l_float32 scalefactor, const char *title, + const char *fileout) +{ + return ERROR_INT("function not present", "pixConvertToPdfSegmented", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertToPdfDataSegmented(const char *filein, l_int32 res, + l_int32 type, l_int32 thresh, BOXA *boxa, + l_int32 quality, l_float32 scalefactor, + const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "convertToPdfDataSegmented", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixConvertToPdfDataSegmented(PIX *pixs, l_int32 res, l_int32 type, + l_int32 thresh, BOXA *boxa, + l_int32 quality, l_float32 scalefactor, + const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "pixConvertToPdfDataSegmented", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok concatenatePdf(const char *dirname, const char *substr, + const char *fileout) +{ + return ERROR_INT("function not present", "concatenatePdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConcatenatePdf(SARRAY *sa, const char *fileout) +{ + return ERROR_INT("function not present", "saConcatenatePdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok ptraConcatenatePdf(L_PTRA *pa, const char *fileout) +{ + return ERROR_INT("function not present", "ptraConcatenatePdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok concatenatePdfToData(const char *dirname, const char *substr, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "concatenatePdfToData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok saConcatenatePdfToData(SARRAY *sa, l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "saConcatenatePdfToData", 1); +} + +/* ----------------------------------------------------------------------*/ + +/* --------------------------------------------*/ +#endif /* !USE_PDFIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pdfio2.c b/hgdriver/3rdparty/hgOCR/leptonica/pdfio2.c new file mode 100644 index 0000000..9fbc540 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pdfio2.c @@ -0,0 +1,2560 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pdfio2.c + *
+ *
+ *    Lower-level operations for generating pdf.
+ *
+ *     Intermediate function for single page, multi-image conversion
+ *          l_int32              pixConvertToPdfData()
+ *
+ *     Intermediate function for generating multipage pdf output
+ *          l_int32              ptraConcatenatePdfToData()
+ *
+ *     Convert tiff multipage to pdf file
+ *          l_int32              convertTiffMultipageToPdf()
+ *
+ *     Low-level CID-based operations
+ *
+ *       Without transcoding
+ *          l_int32              l_generateCIDataForPdf()
+ *          L_COMP_DATA         *l_generateFlateDataPdf()
+ *          L_COMP_DATA         *l_generateJpegData()
+ *          L_COMP_DATA         *l_generateJpegDataMem()
+ *          static L_COMP_DATA  *l_generateJp2kData()
+ *
+ *       With transcoding
+ *          l_int32              l_generateCIData()
+ *          l_int32              pixGenerateCIData()
+ *          L_COMP_DATA         *l_generateFlateData()
+ *          static L_COMP_DATA  *pixGenerateFlateData()
+ *          static L_COMP_DATA  *pixGenerateJpegData()
+ *          static L_COMP_DATA  *pixGenerateJp2kData()
+ *          static L_COMP_DATA  *pixGenerateG4Data()
+ *          L_COMP_DATA         *l_generateG4Data()
+ *
+ *       Other
+ *          l_int32              cidConvertToPdfData()
+ *          void                 l_CIDataDestroy()
+ *
+ *     Helper functions for generating the output pdf string
+ *          static l_int32       l_generatePdf()
+ *          static void          generateFixedStringsPdf()
+ *          static char         *generateEscapeString()
+ *          static void          generateMediaboxPdf()
+ *          static l_int32       generatePageStringPdf()
+ *          static l_int32       generateContentStringPdf()
+ *          static l_int32       generatePreXStringsPdf()
+ *          static l_int32       generateColormapStringsPdf()
+ *          static void          generateTrailerPdf()
+ *          static l_int32       makeTrailerStringPdf()
+ *          static l_int32       generateOutputDataPdf()
+ *
+ *     Helper functions for generating multipage pdf output
+ *          static l_int32       parseTrailerPdf()
+ *          static char         *generatePagesObjStringPdf()
+ *          static L_BYTEA      *substituteObjectNumbers()
+ *
+ *     Create/destroy/access pdf data
+ *          static L_PDF_DATA   *pdfdataCreate()
+ *          static void          pdfdataDestroy()
+ *          static L_COMP_DATA  *pdfdataGetCid()
+ *
+ *     Set flags for special modes
+ *          void                 l_pdfSetG4ImageMask()
+ *          void                 l_pdfSetDateAndVersion()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if USE_PDFIO /* defined in environ.h */ + /* --------------------------------------------*/ + + /* Typical scan resolution in ppi (pixels/inch) */ +static const l_int32 DefaultInputRes = 300; + + /* Static helpers */ +static L_COMP_DATA *l_generateJp2kData(const char *fname); +static L_COMP_DATA *pixGenerateFlateData(PIX *pixs, l_int32 ascii85flag); +static L_COMP_DATA *pixGenerateJpegData(PIX *pixs, l_int32 ascii85flag, + l_int32 quality); +static L_COMP_DATA *pixGenerateJp2kData(PIX *pixs, l_int32 quality); +static L_COMP_DATA *pixGenerateG4Data(PIX *pixs, l_int32 ascii85flag); + +static l_int32 l_generatePdf(l_uint8 **pdata, size_t *pnbytes, + L_PDF_DATA *lpd); +static void generateFixedStringsPdf(L_PDF_DATA *lpd); +static char *generateEscapeString(const char *str); +static void generateMediaboxPdf(L_PDF_DATA *lpd); +static l_int32 generatePageStringPdf(L_PDF_DATA *lpd); +static l_int32 generateContentStringPdf(L_PDF_DATA *lpd); +static l_int32 generatePreXStringsPdf(L_PDF_DATA *lpd); +static l_int32 generateColormapStringsPdf(L_PDF_DATA *lpd); +static void generateTrailerPdf(L_PDF_DATA *lpd); +static char *makeTrailerStringPdf(L_DNA *daloc); +static l_int32 generateOutputDataPdf(l_uint8 **pdata, size_t *pnbytes, + L_PDF_DATA *lpd); + +static l_int32 parseTrailerPdf(L_BYTEA *bas, L_DNA **pda); +static char *generatePagesObjStringPdf(NUMA *napage); +static L_BYTEA *substituteObjectNumbers(L_BYTEA *bas, NUMA *na_objs); + +static L_PDF_DATA *pdfdataCreate(const char *title); +static void pdfdataDestroy(L_PDF_DATA **plpd); +static L_COMP_DATA *pdfdataGetCid(L_PDF_DATA *lpd, l_int32 index); + + +/* ---------------- Defaults for rendering options ----------------- */ + /* Output G4 as writing through image mask; this is the default */ +static l_int32 var_WRITE_G4_IMAGE_MASK = 1; + /* Write date/time and lib version into pdf; this is the default */ +static l_int32 var_WRITE_DATE_AND_VERSION = 1; + +#define L_SMALLBUF 256 +#define L_BIGBUF 2048 /* must be able to hold hex colormap */ + + +#ifndef NO_CONSOLE_IO +#define DEBUG_MULTIPAGE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * Intermediate function for generating multipage pdf output * + *---------------------------------------------------------------------*/ +/*! + * \brief pixConvertToPdfData() + * + * \param[in] pix all depths; cmap OK + * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE, + * L_JP2K_ENCODE + * \param[in] quality for jpeg: 1-100; 0 for default (75) + * for jp2k: 27-45; 0 for default (34) + * \param[out] pdata pdf array + * \param[out] pnbytes number of bytes in pdf array + * \param[in] x, y location of lower-left corner of image, in pixels, + * relative to the PostScript origin (0,0) at + * the lower-left corner of the page) + * \param[in] res override the resolution of the input image, in ppi; + * use 0 to respect resolution embedded in the input + * \param[in] title [optional] pdf title; can be null + * \param[in,out] plpd ptr to lpd; created on the first invocation and + * returned until last image is processed + * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, + * L_LAST_IMAGE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %res == 0 and the input resolution field is 0,
+ *          this will use DefaultInputRes.
+ *      (2) This only writes %data if it is the last image to be
+ *          written on the page.
+ *      (3) See comments in convertToPdf().
+ * 
+ */ +l_ok +pixConvertToPdfData(PIX *pix, + l_int32 type, + l_int32 quality, + l_uint8 **pdata, + size_t *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + const char *title, + L_PDF_DATA **plpd, + l_int32 position) +{ +l_int32 pixres, w, h, ret; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid = NULL; +L_PDF_DATA *lpd = NULL; + + PROCNAME("pixConvertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { + selectDefaultPdfEncoding(pix, &type); + } + if (plpd) { /* part of multi-page invocation */ + if (position == L_FIRST_IMAGE) + *plpd = NULL; + } + + /* Generate the compressed image data. It must NOT + * be ascii85 encoded. */ + pixGenerateCIData(pix, type, quality, 0, &cid); + if (!cid) + return ERROR_INT("cid not made", procName, 1); + + /* Get media box in pts. Guess the input image resolution + * based on the input parameter %res, the resolution data in + * the pix, and the size of the image. */ + pixres = cid->res; + w = cid->w; + h = cid->h; + if (res <= 0.0) { + if (pixres > 0) + res = pixres; + else + res = DefaultInputRes; + } + xpt = x * 72. / res; + ypt = y * 72. / res; + wpt = w * 72. / res; + hpt = h * 72. / res; + + /* Set up lpd */ + if (!plpd) { /* single image */ + if ((lpd = pdfdataCreate(title)) == NULL) + return ERROR_INT("lpd not made", procName, 1); + } else if (position == L_FIRST_IMAGE) { /* first of multiple images */ + if ((lpd = pdfdataCreate(title)) == NULL) + return ERROR_INT("lpd not made", procName, 1); + *plpd = lpd; + } else { /* not the first of multiple images */ + lpd = *plpd; + } + + /* Add the data to the lpd */ + ptraAdd(lpd->cida, cid); + lpd->n++; + ptaAddPt(lpd->xy, xpt, ypt); + ptaAddPt(lpd->wh, wpt, hpt); + + /* If a single image or the last of multiple images, + * generate the pdf and destroy the lpd */ + if (!plpd || (position == L_LAST_IMAGE)) { + ret = l_generatePdf(pdata, pnbytes, lpd); + pdfdataDestroy(&lpd); + if (plpd) *plpd = NULL; + if (ret) + return ERROR_INT("pdf output not made", procName, 1); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Intermediate function for generating multipage pdf output * + *---------------------------------------------------------------------*/ +/*! + * \brief ptraConcatenatePdfToData() + * + * \param[in] pa_data ptra array of pdf strings, each for a + * single-page pdf file + * \param[in] sa [optional] string array of pathnames for + * input pdf files; can be null + * \param[out] pdata concatenated pdf data in memory + * \param[out] pnbytes number of bytes in pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This only works with leptonica-formatted single-page pdf files.
+ *          pdf files generated by other programs will have unpredictable
+ *          (and usually bad) results.  The requirements for each pdf file:
+ *            (a) The Catalog and Info objects are the first two.
+ *            (b) Object 3 is Pages
+ *            (c) Object 4 is Page
+ *            (d) The remaining objects are Contents, XObjects, and ColorSpace
+ *      (2) We remove trailers from each page, and append the full trailer
+ *          for all pages at the end.
+ *      (3) For all but the first file, remove the ID and the first 3
+ *          objects (catalog, info, pages), so that each subsequent
+ *          file has only objects of these classes:
+ *              Page, Contents, XObject, ColorSpace (Indexed RGB).
+ *          For those objects, we substitute these refs to objects
+ *          in the local file:
+ *              Page:  Parent(object 3), Contents, XObject(typically multiple)
+ *              XObject:  [ColorSpace if indexed]
+ *          The Pages object on the first page (object 3) has a Kids array
+ *          of references to all the Page objects, with a Count equal
+ *          to the number of pages.  Each Page object refers back to
+ *          this parent.
+ * 
+ */ +l_ok +ptraConcatenatePdfToData(L_PTRA *pa_data, + SARRAY *sa, + l_uint8 **pdata, + size_t *pnbytes) +{ +char *fname, *str_pages, *str_trailer; +l_uint8 *pdfdata, *data; +l_int32 i, j, index, nobj, npages; +l_int32 *sizes, *locs; +size_t size; +L_BYTEA *bas, *bad, *bat1, *bat2; +L_DNA *da_locs, *da_sizes, *da_outlocs, *da; +L_DNAA *daa_locs; /* object locations on each page */ +NUMA *na_objs, *napage; +NUMAA *naa_objs; /* object mapping numbers to new values */ + + PROCNAME("ptraConcatenatePdfToData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pa_data) + return ERROR_INT("pa_data not defined", procName, 1); + + /* Parse the files and find the object locations. + * Remove file data that cannot be parsed. */ + ptraGetActualCount(pa_data, &npages); + daa_locs = l_dnaaCreate(npages); + for (i = 0; i < npages; i++) { + bas = (L_BYTEA *)ptraGetPtrToItem(pa_data, i); + if (parseTrailerPdf(bas, &da_locs) != 0) { + bas = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&bas); + if (sa) { + fname = sarrayGetString(sa, i, L_NOCOPY); + L_ERROR("can't parse file %s; skipping\n", procName, fname); + } else { + L_ERROR("can't parse file %d; skipping\n", procName, i); + } + } else { + l_dnaaAddDna(daa_locs, da_locs, L_INSERT); + } + } + + /* Recompute npages in case some of the files were not pdf */ + ptraCompactArray(pa_data); + ptraGetActualCount(pa_data, &npages); + if (npages == 0) { + l_dnaaDestroy(&daa_locs); + return ERROR_INT("no parsable pdf files found", procName, 1); + } + + /* Find the mapping from initial to final object numbers */ + naa_objs = numaaCreate(npages); /* stores final object numbers */ + napage = numaCreate(npages); /* stores "Page" object numbers */ + index = 0; + for (i = 0; i < npages; i++) { + da = l_dnaaGetDna(daa_locs, i, L_CLONE); + nobj = l_dnaGetCount(da); + if (i == 0) { + numaAddNumber(napage, 4); /* object 4 on first page */ + na_objs = numaMakeSequence(0.0, 1.0, nobj - 1); + index = nobj - 1; + } else { /* skip the first 3 objects in each file */ + numaAddNumber(napage, index); /* Page object is first we add */ + na_objs = numaMakeConstant(0.0, nobj - 1); + numaReplaceNumber(na_objs, 3, 3); /* refers to parent of all */ + for (j = 4; j < nobj - 1; j++) + numaSetValue(na_objs, j, index++); + } + numaaAddNuma(naa_objs, na_objs, L_INSERT); + l_dnaDestroy(&da); + } + + /* Make the Pages object (#3) */ + str_pages = generatePagesObjStringPdf(napage); + + /* Build the output */ + bad = l_byteaCreate(5000); + da_outlocs = l_dnaCreate(0); /* locations of all output objects */ + for (i = 0; i < npages; i++) { + bas = (L_BYTEA *)ptraGetPtrToItem(pa_data, i); + pdfdata = l_byteaGetData(bas, &size); + da_locs = l_dnaaGetDna(daa_locs, i, L_CLONE); /* locs on this page */ + na_objs = numaaGetNuma(naa_objs, i, L_CLONE); /* obj # on this page */ + nobj = l_dnaGetCount(da_locs) - 1; + da_sizes = l_dnaDiffAdjValues(da_locs); /* object sizes on this page */ + sizes = l_dnaGetIArray(da_sizes); + locs = l_dnaGetIArray(da_locs); + if (i == 0) { + l_byteaAppendData(bad, pdfdata, sizes[0]); + l_byteaAppendData(bad, pdfdata + locs[1], sizes[1]); + l_byteaAppendData(bad, pdfdata + locs[2], sizes[2]); + l_byteaAppendString(bad, str_pages); + for (j = 0; j < 4; j++) + l_dnaAddNumber(da_outlocs, locs[j]); + } + for (j = 4; j < nobj; j++) { + l_dnaAddNumber(da_outlocs, l_byteaGetSize(bad)); + bat1 = l_byteaInitFromMem(pdfdata + locs[j], sizes[j]); + bat2 = substituteObjectNumbers(bat1, na_objs); + data = l_byteaGetData(bat2, &size); + l_byteaAppendData(bad, data, size); + l_byteaDestroy(&bat1); + l_byteaDestroy(&bat2); + } + if (i == npages - 1) /* last one */ + l_dnaAddNumber(da_outlocs, l_byteaGetSize(bad)); + LEPT_FREE(sizes); + LEPT_FREE(locs); + l_dnaDestroy(&da_locs); + numaDestroy(&na_objs); + l_dnaDestroy(&da_sizes); + } + + /* Add the trailer */ + str_trailer = makeTrailerStringPdf(da_outlocs); + l_byteaAppendString(bad, str_trailer); + + /* Transfer the output data */ + *pdata = l_byteaCopyData(bad, pnbytes); + l_byteaDestroy(&bad); + +#if DEBUG_MULTIPAGE + fprintf(stderr, "******** object mapper **********"); + numaaWriteStream(stderr, naa_objs); + + fprintf(stderr, "******** Page object numbers ***********"); + numaWriteStream(stderr, napage); + + fprintf(stderr, "******** Pages object ***********\n"); + fprintf(stderr, "%s\n", str_pages); +#endif /* DEBUG_MULTIPAGE */ + + numaDestroy(&napage); + numaaDestroy(&naa_objs); + l_dnaDestroy(&da_outlocs); + l_dnaaDestroy(&daa_locs); + LEPT_FREE(str_pages); + LEPT_FREE(str_trailer); + return 0; +} + + +/*---------------------------------------------------------------------* + * Convert tiff multipage to pdf file * + *---------------------------------------------------------------------*/ +/*! + * \brief convertTiffMultipageToPdf() + * + * \param[in] filein (tiff) + * \param[in] fileout (pdf) + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) A multipage tiff file can also be converted to PS, using
+ *          convertTiffMultipageToPS()
+ * 
+ */ +l_ok +convertTiffMultipageToPdf(const char *filein, + const char *fileout) +{ +l_int32 istiff; +PIXA *pixa; +FILE *fp; + + PROCNAME("convertTiffMultipageToPdf"); + + if ((fp = fopenReadStream(filein)) == NULL) + return ERROR_INT("file not found", procName, 1); + istiff = fileFormatIsTiff(fp); + fclose(fp); + if (!istiff) + return ERROR_INT("file not tiff format", procName, 1); + + pixa = pixaReadMultipageTiff(filein); + pixaConvertToPdf(pixa, 0, 1.0, 0, 0, "weasel2", fileout); + pixaDestroy(&pixa); + return 0; +} + + +/*---------------------------------------------------------------------* + * Low-level CID-based operations * + *---------------------------------------------------------------------*/ +/*! + * \brief l_generateCIDataForPdf() + * + * \param[in] fname [optional] can be null + * \param[in] pix [optional] can be null + * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) + * for jp2k if transcoded: 27-45; 0 for default (34) + * \param[out] pcid compressed data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) You must set either filename or pix.
+ *      (2) Given an image file and optionally a pix raster of that data,
+ *          this provides a CID that is compatible with PDF, preferably
+ *          without transcoding.
+ *      (3) The pix is included for efficiency, in case transcoding
+ *          is required and the pix is available to the caller.
+ *      (4) We don't try to open files named "stdin" or "-" for Tesseract
+ *          compatibility reasons. We may remove this restriction
+ *          in the future.
+ * 
+ */ +l_ok +l_generateCIDataForPdf(const char *fname, + PIX *pix, + l_int32 quality, + L_COMP_DATA **pcid) +{ +l_int32 format, type; +L_COMP_DATA *cid; +PIX *pixt; + + PROCNAME("l_generateCIDataForPdf"); + + //FILE* file = fopenWriteStream("aaa.bmp", "w"); + //pixWriteStreamBmp(file, pix); + //fclose(file); + if (!pcid) + return ERROR_INT("&cid not defined", procName, 1); + *pcid = cid = NULL; + if (!fname && !pix) + return ERROR_INT("neither fname nor pix are defined", procName, 1); + + /* If a compressed file is given that is not 'stdin', see if we + * can generate the pdf output without transcoding. */ + if (fname && strcmp(fname, "-") != 0 && strcmp(fname, "stdin") != 0) { + findFileFormat(fname, &format); + if (format == IFF_UNKNOWN) + L_WARNING("file %s format is unknown\n", procName, fname); + if (format == IFF_PS || format == IFF_LPDF) { + L_ERROR("file %s is unsupported format %d\n", + procName, fname, format); + return 1; + } + if (format == IFF_JFIF_JPEG) { + cid = l_generateJpegData(fname, 0); + } else if (format == IFF_JP2) { + cid = l_generateJp2kData(fname); + } else if (format == IFF_PNG) { + cid = l_generateFlateDataPdf(fname, pix); + } + + } + + /* Otherwise, use the pix to generate the pdf output */ + if (!cid) { + if (!pix) + pixt = pixRead(fname); + else + pixt = pixClone(pix); + if (!pixt) + return ERROR_INT("pixt not made", procName, 1); + if (selectDefaultPdfEncoding(pixt, &type)) { + pixDestroy(&pixt); + return 1; + } + pixGenerateCIData(pixt, type, quality, 0, &cid); + pixDestroy(&pixt); + } + if (!cid) { + L_ERROR("totally kerflummoxed\n", procName); + return 1; + } + *pcid = cid; + return 0; +} + + +/*! + * \brief l_generateFlateDataPdf() + * + * \param[in] fname preferably png + * \param[in] pixs [optional] can be null + * \return cid containing png data, or NULL on error + * + *
+ * Notes:
+ *      (1) If you hand this a png file, you are going to get
+ *          png predictors embedded in the flate data. So it has
+ *          come to this. http://xkcd.com/1022/
+ *      (2) Exception: if the png is interlaced or if it is RGBA,
+ *          it will be transcoded.
+ *      (3) If transcoding is required, this will not have to read from
+ *          file if you also input a pix.
+ * 
+ */ +L_COMP_DATA * +l_generateFlateDataPdf(const char *fname, + PIX *pixs) +{ +l_uint8 *pngcomp = NULL; /* entire PNG compressed file */ +l_uint8 *datacomp = NULL; /* gzipped raster data */ +l_uint8 *cmapdata = NULL; /* uncompressed colormap */ +char *cmapdatahex = NULL; /* hex ascii uncompressed colormap */ +l_uint32 i, j, n; +l_int32 format, interlaced; +l_int32 ncolors; /* in colormap */ +l_int32 bps; /* bits/sample: usually 8 */ +l_int32 spp; /* samples/pixel: 1-grayscale/cmap); 3-rgb; 4-rgba */ +l_int32 w, h, cmapflag; +l_int32 xres, yres; +size_t nbytescomp = 0, nbytespng = 0; +FILE *fp; +L_COMP_DATA *cid; +PIX *pix; +PIXCMAP *cmap = NULL; + + PROCNAME("l_generateFlateDataPdf"); + + if (!fname) + return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); + + findFileFormat(fname, &format); + spp = 0; /* init to spp != 4 if not png */ + interlaced = 0; /* initialize to no interlacing */ + bps = 0; /* initialize to a nonsense value */ + if (format == IFF_PNG) { + isPngInterlaced(fname, &interlaced); + if (readHeaderPng(fname, NULL, NULL, &bps, &spp, NULL)) + return (L_COMP_DATA *)ERROR_PTR("bad png input", procName, NULL); + } + + /* PDF is capable of inlining some types of PNG files, but not all + of them. We need to transcode anything with interlacing, an + alpha channel, or 1 bpp (which would otherwise be photo-inverted). + + Be careful with spp. Any PNG image file with an alpha + channel is converted on reading to RGBA (spp == 4). This + includes the (gray + alpha) format with spp == 2. You + will get different results if you look at spp via + readHeaderPng() versus pixGetSpp() */ + if (format != IFF_PNG || interlaced || bps == 1 || spp == 4 || spp == 2) { + if (!pixs) + pix = pixRead(fname); + else + pix = pixClone(pixs); + if (!pix) + return (L_COMP_DATA *)ERROR_PTR("pix not made", procName, NULL); + cid = pixGenerateFlateData(pix, 0); + pixDestroy(&pix); + return cid; + } + + /* It's png. Generate the pdf data without transcoding. + * Implementation by Jeff Breidenbach. + * First, read the metadata */ + if ((fp = fopenReadStream(fname)) == NULL) + return (L_COMP_DATA *)ERROR_PTR("stream not opened", procName, NULL); + freadHeaderPng(fp, &w, &h, &bps, &spp, &cmapflag); + fgetPngResolution(fp, &xres, &yres); + fclose(fp); + + /* We get pdf corruption when inlining the data from 16 bpp png. */ + if (bps == 16) + return l_generateFlateData(fname, 0); + + /* Read the entire png file */ + if ((pngcomp = l_binaryRead(fname, &nbytespng)) == NULL) + return (L_COMP_DATA *)ERROR_PTR("unable to read file", + procName, NULL); + + /* Extract flate data, copying portions of it to memory, including + * the predictor information in a byte at the beginning of each + * raster line. The flate data makes up the vast majority of + * the png file, so after extraction we expect datacomp to + * be nearly full (i.e., nbytescomp will be only slightly less + * than nbytespng). Also extract the colormap if present. */ + if ((datacomp = (l_uint8 *)LEPT_CALLOC(1, nbytespng)) == NULL) { + LEPT_FREE(pngcomp); + return (L_COMP_DATA *)ERROR_PTR("unable to allocate memory", + procName, NULL); + } + + /* Parse the png file. Each chunk consists of: + * length: 4 bytes + * name: 4 bytes (e.g., "IDAT") + * data: n bytes + * CRC: 4 bytes + * Start at the beginning of the data section of the first chunk, + * byte 16, because the png file begins with 8 bytes of header, + * followed by the first 8 bytes of the first chunk + * (length and name). On each loop, increment by 12 bytes to + * skip over the CRC, length and name of the next chunk. */ + for (i = 16; i < nbytespng; i += 12) { /* do each successive chunk */ + /* Get the chunk length */ + n = pngcomp[i - 8] << 24; + n += pngcomp[i - 7] << 16; + n += pngcomp[i - 6] << 8; + n += pngcomp[i - 5] << 0; + if (n >= nbytespng - i) { /* "n + i" can overflow */ + LEPT_FREE(pngcomp); + LEPT_FREE(datacomp); + pixcmapDestroy(&cmap); + L_ERROR("invalid png: i = %d, n = %d, nbytes = %zu\n", procName, + i, n, nbytespng); + return NULL; + } + + /* Is it a data chunk? */ + if (memcmp(pngcomp + i - 4, "IDAT", 4) == 0) { + memcpy(datacomp + nbytescomp, pngcomp + i, n); + nbytescomp += n; + } + + /* Is it a palette chunk? */ + if (cmapflag && !cmap && + memcmp(pngcomp + i - 4, "PLTE", 4) == 0) { + if ((n / 3) > (1 << bps)) { + LEPT_FREE(pngcomp); + LEPT_FREE(datacomp); + pixcmapDestroy(&cmap); + L_ERROR("invalid png: i = %d, n = %d, cmapsize = %d\n", + procName, i, n, (1 << bps)); + return NULL; + } + cmap = pixcmapCreate(bps); + for (j = i; j < i + n; j += 3) { + pixcmapAddColor(cmap, pngcomp[j], pngcomp[j + 1], + pngcomp[j + 2]); + } + } + i += n; /* move to the end of the data chunk */ + } + LEPT_FREE(pngcomp); + + if (nbytescomp == 0) { + LEPT_FREE(datacomp); + pixcmapDestroy(&cmap); + return (L_COMP_DATA *)ERROR_PTR("invalid PNG file", procName, NULL); + } + + /* Extract and encode the colormap data as hexascii */ + ncolors = 0; + if (cmap) { + pixcmapSerializeToMemory(cmap, 3, &ncolors, &cmapdata); + pixcmapDestroy(&cmap); + if (!cmapdata) { + LEPT_FREE(datacomp); + return (L_COMP_DATA *)ERROR_PTR("cmapdata not made", + procName, NULL); + } + cmapdatahex = pixcmapConvertToHex(cmapdata, ncolors); + LEPT_FREE(cmapdata); + } + + /* Note that this is the only situation where the predictor + * field of the CID is set to 1. Adobe's predictor values on + * p. 76 of pdf_reference_1-7.pdf give 1 for no predictor and + * 10-14 for inline predictors, the specifics of which are + * ignored by the pdf interpreter, which just needs to know that + * the first byte on each compressed scanline is some predictor + * whose type can be inferred from the byte itself. */ + cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); + cid->datacomp = datacomp; + cid->type = L_FLATE_ENCODE; + cid->cmapdatahex = cmapdatahex; + cid->nbytescomp = nbytescomp; + cid->ncolors = ncolors; + cid->predictor = TRUE; + cid->w = w; + cid->h = h; + cid->bps = bps; + cid->spp = spp; + cid->res = xres; + return cid; +} + + +/*! + * \brief l_generateJpegData() + * + * \param[in] fname of jpeg file + * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg + * \return cid containing jpeg data, or NULL on error + * + *
+ * Notes:
+ *      (1) Set ascii85flag:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ *               (not permitted in pdf)
+ *      (2) Do not free the data.  l_generateJpegDataMem() will free
+ *          the data if the data is invalid, or if it does not use
+ *          ascii encoding.
+ * 
+ */ +L_COMP_DATA * +l_generateJpegData(const char *fname, + l_int32 ascii85flag) +{ +l_uint8 *data = NULL; +size_t nbytes; + + PROCNAME("l_generateJpegData"); + + if (!fname) + return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); + + /* The returned jpeg data in memory is the entire jpeg file, + * which starts with ffd8 and ends with ffd9 */ + if ((data = l_binaryRead(fname, &nbytes)) == NULL) + return (L_COMP_DATA *)ERROR_PTR("data not extracted", procName, NULL); + + return l_generateJpegDataMem(data, nbytes, ascii85flag); +} + + +/*! + * \brief l_generateJpegDataMem() + * + * \param[in] data of jpeg file + * \param[in] nbytes of jpeg file + * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg + * \return cid containing jpeg data, or NULL on error + * + *
+ * Notes:
+ *      (1) See l_generateJpegData().
+ * 
+ */ +L_COMP_DATA * +l_generateJpegDataMem(l_uint8 *data, + size_t nbytes, + l_int32 ascii85flag) +{ +char *data85 = NULL; /* ascii85 encoded jpeg compressed file */ +l_int32 w, h, xres, yres, bps, spp; +l_int32 nbytes85; +L_COMP_DATA *cid; + + PROCNAME("l_generateJpegDataMem"); + + if (!data) + return (L_COMP_DATA *)ERROR_PTR("data not defined", procName, NULL); + + /* Read the metadata */ + if (readHeaderMemJpeg(data, nbytes, &w, &h, &spp, NULL, NULL)) { + LEPT_FREE(data); + return (L_COMP_DATA *)ERROR_PTR("bad jpeg metadata", procName, NULL); + } + bps = 8; + readResolutionMemJpeg(data, nbytes, &xres, &yres); + + /* Optionally, encode the compressed data */ + if (ascii85flag == 1) { + data85 = encodeAscii85(data, nbytes, &nbytes85); + LEPT_FREE(data); + if (!data85) + return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); + else + data85[nbytes85 - 1] = '\0'; /* remove the newline */ + } + + cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); + if (ascii85flag == 0) { + cid->datacomp = data; + } else { /* ascii85 */ + cid->data85 = data85; + cid->nbytes85 = nbytes85; + } + cid->type = L_JPEG_ENCODE; + cid->nbytescomp = nbytes; + cid->w = w; + cid->h = h; + cid->bps = bps; + cid->spp = spp; + cid->res = xres; + return cid; +} + + +/*! + * \brief l_generateJp2kData() + * + * \param[in] fname of jp2k file + * \return cid containing jp2k data, or NULL on error + * + *
+ * Notes:
+ *      (1) This is only called after the file is verified to be jp2k.
+ * 
+ */ +static L_COMP_DATA * +l_generateJp2kData(const char *fname) +{ +l_int32 w, h, bps, spp, xres, yres; +size_t nbytes; +L_COMP_DATA *cid; +FILE *fp; + + PROCNAME("l_generateJp2kData"); + + if (!fname) + return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); + + if (readHeaderJp2k(fname, &w, &h, &bps, &spp)) + return (L_COMP_DATA *)ERROR_PTR("bad jp2k metadata", procName, NULL); + + if ((cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA))) == NULL) + return (L_COMP_DATA *)ERROR_PTR("cid not made", procName, NULL); + + /* The returned jp2k data in memory is the entire jp2k file */ + if ((cid->datacomp = l_binaryRead(fname, &nbytes)) == NULL) { + l_CIDataDestroy(&cid); + return (L_COMP_DATA *)ERROR_PTR("data not extracted", procName, NULL); + } + + xres = yres = 0; + if ((fp = fopenReadStream(fname)) != NULL) { + fgetJp2kResolution(fp, &xres, &yres); + fclose(fp); + } + cid->type = L_JP2K_ENCODE; + cid->nbytescomp = nbytes; + cid->w = w; + cid->h = h; + cid->bps = bps; + cid->spp = spp; + cid->res = xres; + return cid; +} + + +/*! + * \brief l_generateCIData() + * + * \param[in] fname + * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE, + * L_JP2K_ENCODE + * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) + * for jp2k if transcoded: 27-45; 0 for default (34) + * \param[in] ascii85 0 for binary; 1 for ascii85-encoded + * \param[out] pcid compressed data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This can be used for both PostScript and pdf.
+ *      (1) Set ascii85:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ *      (2) This attempts to compress according to the requested type.
+ *          If this can't be done, it falls back to ordinary flate encoding.
+ *      (3) This differs from l_generateCIDataPdf(), which determines
+ *          the format and attempts to generate the CID without transcoding.
+ * 
+ */ +l_ok +l_generateCIData(const char *fname, + l_int32 type, + l_int32 quality, + l_int32 ascii85, + L_COMP_DATA **pcid) +{ +l_int32 format, d, bps, spp, iscmap; +L_COMP_DATA *cid; +PIX *pix; + + PROCNAME("l_generateCIData"); + + if (!pcid) + return ERROR_INT("&cid not defined", procName, 1); + *pcid = NULL; + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) + return ERROR_INT("invalid conversion type", procName, 1); + if (ascii85 != 0 && ascii85 != 1) + return ERROR_INT("invalid ascii85", procName, 1); + + /* Sanity check on requested encoding */ + pixReadHeader(fname, &format, NULL, NULL, &bps, &spp, &iscmap); + d = bps * spp; + if (d == 24) d = 32; + if (iscmap && type != L_FLATE_ENCODE) { + L_WARNING("pixs has cmap; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } else if (d < 8 && type == L_JPEG_ENCODE) { + L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } else if (d < 8 && type == L_JP2K_ENCODE) { + L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } else if (d > 1 && type == L_G4_ENCODE) { + L_WARNING("pixs has > 1 bpp; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } + + if (type == L_JPEG_ENCODE) { + if (format == IFF_JFIF_JPEG) { /* do not transcode */ + cid = l_generateJpegData(fname, ascii85); + } else { + if ((pix = pixRead(fname)) == NULL) + return ERROR_INT("pix not returned", procName, 1); + cid = pixGenerateJpegData(pix, ascii85, quality); + pixDestroy(&pix); + } + if (!cid) + return ERROR_INT("jpeg data not made", procName, 1); + } else if (type == L_JP2K_ENCODE) { + if (format == IFF_JP2) { /* do not transcode */ + cid = l_generateJp2kData(fname); + } else { + if ((pix = pixRead(fname)) == NULL) + return ERROR_INT("pix not returned", procName, 1); + cid = pixGenerateJp2kData(pix, quality); + pixDestroy(&pix); + } + if (!cid) + return ERROR_INT("jp2k data not made", procName, 1); + } else if (type == L_G4_ENCODE) { + if ((cid = l_generateG4Data(fname, ascii85)) == NULL) + return ERROR_INT("g4 data not made", procName, 1); + } else if (type == L_FLATE_ENCODE) { + if ((cid = l_generateFlateData(fname, ascii85)) == NULL) + return ERROR_INT("flate data not made", procName, 1); + } else { + return ERROR_INT("invalid conversion type", procName, 1); + } + *pcid = cid; + + return 0; +} + + +/*! + * \brief pixGenerateCIData() + * + * \param[in] pixs 8 or 32 bpp, no colormap + * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE or + * L_JP2K_ENCODE + * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) + * for jp2k if transcoded: 27-45; 0 for default (34) + * \param[in] ascii85 0 for binary; 1 for ascii85-encoded + * \param[out] pcid compressed data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Set ascii85:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ * 
+ */ +l_ok +pixGenerateCIData(PIX *pixs, + l_int32 type, + l_int32 quality, + l_int32 ascii85, + L_COMP_DATA **pcid) +{ +l_int32 d; +PIXCMAP *cmap; + + PROCNAME("pixGenerateCIData"); + + if (!pcid) + return ERROR_INT("&cid not defined", procName, 1); + *pcid = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && + type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { + selectDefaultPdfEncoding(pixs, &type); + } + if (ascii85 != 0 && ascii85 != 1) + return ERROR_INT("invalid ascii85", procName, 1); + + /* Sanity check on requested encoding */ + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (cmap && type != L_FLATE_ENCODE) { + L_WARNING("pixs has cmap; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } else if (d < 8 && (type == L_JPEG_ENCODE || type == L_JP2K_ENCODE)) { + L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } else if (d > 1 && type == L_G4_ENCODE) { + L_WARNING("pixs has > 1 bpp; using flate encoding\n", procName); + type = L_FLATE_ENCODE; + } + + if (type == L_JPEG_ENCODE) { + if ((*pcid = pixGenerateJpegData(pixs, ascii85, quality)) == NULL) + return ERROR_INT("jpeg data not made", procName, 1); + } else if (type == L_JP2K_ENCODE) { + if ((*pcid = pixGenerateJp2kData(pixs, quality)) == NULL) + return ERROR_INT("jp2k data not made", procName, 1); + } else if (type == L_G4_ENCODE) { + if ((*pcid = pixGenerateG4Data(pixs, ascii85)) == NULL) + return ERROR_INT("g4 data not made", procName, 1); + } else { /* type == L_FLATE_ENCODE */ + if ((*pcid = pixGenerateFlateData(pixs, ascii85)) == NULL) + return ERROR_INT("flate data not made", procName, 1); + } + return 0; +} + + +/*! + * \brief l_generateFlateData() + * + * \param[in] fname + * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped + * \return cid flate compressed image data, or NULL on error + * + *
+ * Notes:
+ *      (1) The input image is converted to one of these 4 types:
+ *           ~ 1 bpp
+ *           ~ 8 bpp, no colormap
+ *           ~ 8 bpp, colormap
+ *           ~ 32 bpp rgb
+ *      (2) Set ascii85flag:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ * 
+ */ +L_COMP_DATA * +l_generateFlateData(const char *fname, + l_int32 ascii85flag) +{ +L_COMP_DATA *cid; +PIX *pixs; + + PROCNAME("l_generateFlateData"); + + if (!fname) + return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); + + if ((pixs = pixRead(fname)) == NULL) + return (L_COMP_DATA *)ERROR_PTR("pixs not made", procName, NULL); + cid = pixGenerateFlateData(pixs, ascii85flag); + pixDestroy(&pixs); + return cid; +} + + +/*! + * \brief pixGenerateFlateData() + * + * \param[in] pixs + * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped + * \return cid flate compressed image data, or NULL on error + * + * Notes: + * 1) This should not be called with an RGBA pix (spp == 4; it + * will ignore the alpha channel. Likewise, if called with a + * colormapped pix, the alpha component in the colormap will + * be ignored as it is for all leptonica operations + * on colormapped pix. + */ +static L_COMP_DATA * +pixGenerateFlateData(PIX *pixs, + l_int32 ascii85flag) +{ +l_uint8 *data = NULL; /* uncompressed raster data in required format */ +l_uint8 *datacomp = NULL; /* gzipped raster data */ +char *data85 = NULL; /* ascii85 encoded gzipped raster data */ +l_uint8 *cmapdata = NULL; /* uncompressed colormap */ +char *cmapdata85 = NULL; /* ascii85 encoded uncompressed colormap */ +char *cmapdatahex = NULL; /* hex ascii uncompressed colormap */ +l_int32 ncolors; /* in colormap; not used if cmapdata85 is null */ +l_int32 bps; /* bits/sample: usually 8 */ +l_int32 spp; /* samples/pixel: 1-grayscale/cmap); 3-rgb */ +l_int32 w, h, d, cmapflag; +l_int32 ncmapbytes85 = 0; +l_int32 nbytes85 = 0; +size_t nbytes, nbytescomp; +L_COMP_DATA *cid; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGenerateFlateData"); + + if (!pixs) + return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert the image to one of these 4 types: + * 1 bpp + * 8 bpp, no colormap + * 8 bpp, colormap + * 32 bpp rgb */ + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + cmapflag = (cmap) ? 1 : 0; + if (d == 2 || d == 4 || d == 16) { + pixt = pixConvertTo8(pixs, cmapflag); + cmap = pixGetColormap(pixt); + d = pixGetDepth(pixt); + } else { + pixt = pixClone(pixs); + } + spp = (d == 32) ? 3 : 1; /* ignores alpha */ + bps = (d == 32) ? 8 : d; + + /* Extract and encode the colormap data as both ascii85 and hexascii */ + ncolors = 0; + if (cmap) { + pixcmapSerializeToMemory(cmap, 3, &ncolors, &cmapdata); + if (!cmapdata) { + pixDestroy(&pixt); + return (L_COMP_DATA *)ERROR_PTR("cmapdata not made", + procName, NULL); + } + + cmapdata85 = encodeAscii85(cmapdata, 3 * ncolors, &ncmapbytes85); + cmapdatahex = pixcmapConvertToHex(cmapdata, ncolors); + LEPT_FREE(cmapdata); + } + + /* Extract and compress the raster data */ + pixGetRasterData(pixt, &data, &nbytes); + pixDestroy(&pixt); + datacomp = zlibCompress(data, nbytes, &nbytescomp); + LEPT_FREE(data); + if (!datacomp) { + LEPT_FREE(cmapdata85); + LEPT_FREE(cmapdatahex); + return (L_COMP_DATA *)ERROR_PTR("datacomp not made", procName, NULL); + } + + /* Optionally, encode the compressed data */ + if (ascii85flag == 1) { + data85 = encodeAscii85(datacomp, nbytescomp, &nbytes85); + LEPT_FREE(datacomp); + if (!data85) { + LEPT_FREE(cmapdata85); + LEPT_FREE(cmapdatahex); + return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); + } else { + data85[nbytes85 - 1] = '\0'; /* remove the newline */ + } + } + + cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); + if (ascii85flag == 0) { + cid->datacomp = datacomp; + } else { /* ascii85 */ + cid->data85 = data85; + cid->nbytes85 = nbytes85; + } + cid->type = L_FLATE_ENCODE; + cid->cmapdatahex = cmapdatahex; + cid->cmapdata85 = cmapdata85; + cid->nbytescomp = nbytescomp; + cid->ncolors = ncolors; + cid->w = w; + cid->h = h; + cid->bps = bps; + cid->spp = spp; + cid->res = pixGetXRes(pixs); + cid->nbytes = nbytes; /* only for debugging */ + return cid; +} + + +/*! + * \brief pixGenerateJpegData() + * + * \param[in] pixs 8 or 32 bpp, no colormap + * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg + * \param[in] quality 0 for default, which is 75 + * \return cid jpeg compressed data, or NULL on error + * + *
+ * Notes:
+ *      (1) Set ascii85flag:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ * 
+ */ +static L_COMP_DATA * +pixGenerateJpegData(PIX *pixs, + l_int32 ascii85flag, + l_int32 quality) +{ +l_int32 d; +char *fname; +L_COMP_DATA *cid; + + PROCNAME("pixGenerateJpegData"); + + if (!pixs) + return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (L_COMP_DATA *)ERROR_PTR("pixs has colormap", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (L_COMP_DATA *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + + /* Compress to a temp jpeg file */ + fname = l_makeTempFilename(); + if (pixWriteJpeg(fname, pixs, quality, 0)) { + LEPT_FREE(fname); + return NULL; + } + + /* Generate the data */ + cid = l_generateJpegData(fname, ascii85flag); + if (lept_rmfile(fname) != 0) + L_ERROR("temp file %s was not deleted\n", procName, fname); + LEPT_FREE(fname); + return cid; +} + + +/*! + * \brief pixGenerateJp2kData() + * + * \param[in] pixs 8 or 32 bpp, no colormap + * \param[in] quality 0 for default, which is 34 + * \return cid jp2k compressed data, or NULL on error + * + *
+ * Notes:
+ *      (1) The quality can be set between 27 (very poor) and 45
+ *          (nearly perfect).  Use 0 for default (34). Use 100 for lossless,
+ *          but this is very expensive and not recommended.
+ * 
+ */ +static L_COMP_DATA * +pixGenerateJp2kData(PIX *pixs, + l_int32 quality) +{ +l_int32 d; +char *fname; +L_COMP_DATA *cid; + + PROCNAME("pixGenerateJp2kData"); + + if (!pixs) + return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (L_COMP_DATA *)ERROR_PTR("pixs has colormap", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (L_COMP_DATA *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + + /* Compress to a temp jp2k file */ + fname = l_makeTempFilename(); + if (pixWriteJp2k(fname, pixs, quality, 5, 0, 0)) { + LEPT_FREE(fname); + return NULL; + } + + /* Generate the data */ + cid = l_generateJp2kData(fname); + if (lept_rmfile(fname) != 0) + L_ERROR("temp file %s was not deleted\n", procName, fname); + LEPT_FREE(fname); + return cid; +} + + +/*! + * \brief pixGenerateG4Data() + * + * \param[in] pixs 1 bpp + * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped + * \return cid g4 compressed image data, or NULL on error + * + *
+ * Notes:
+ *      (1) Set ascii85flag:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ * 
+ */ +static L_COMP_DATA * +pixGenerateG4Data(PIX *pixs, + l_int32 ascii85flag) +{ +char *fname; +L_COMP_DATA *cid; + + PROCNAME("pixGenerateG4Data"); + + if (!pixs) + return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (L_COMP_DATA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + /* Compress to a temp tiff g4 file */ + fname = l_makeTempFilename(); + if (pixWrite(fname, pixs, IFF_TIFF_G4)) { + LEPT_FREE(fname); + return NULL; + } + + cid = l_generateG4Data(fname, ascii85flag); + if (lept_rmfile(fname) != 0) + L_ERROR("temp file %s was not deleted\n", procName, fname); + LEPT_FREE(fname); + return cid; +} + + +/*! + * \brief l_generateG4Data() + * + * \param[in] fname of g4 compressed file + * \param[in] ascii85flag 0 for g4 compressed; 1 for ascii85-encoded g4 + * \return cid g4 compressed image data, or NULL on error + * + *
+ * Notes:
+ *      (1) Set ascii85flag:
+ *           ~ 0 for binary data (not permitted in PostScript)
+ *           ~ 1 for ascii85 (5 for 4) encoded binary data
+ *             (not permitted in pdf)
+ * 
+ */ +L_COMP_DATA * +l_generateG4Data(const char *fname, + l_int32 ascii85flag) +{ +l_uint8 *datacomp = NULL; /* g4 compressed raster data */ +char *data85 = NULL; /* ascii85 encoded g4 compressed data */ +l_int32 w, h, xres, yres; +l_int32 minisblack; /* TRUE or FALSE */ +l_int32 nbytes85; +size_t nbytescomp; +L_COMP_DATA *cid; +FILE *fp; + + PROCNAME("l_generateG4Data"); + + if (!fname) + return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); + + /* Read the resolution */ + if ((fp = fopenReadStream(fname)) == NULL) + return (L_COMP_DATA *)ERROR_PTR("stream not opened", procName, NULL); + getTiffResolution(fp, &xres, &yres); + fclose(fp); + + /* The returned ccitt g4 data in memory is the block of + * bytes in the tiff file, starting after 8 bytes and + * ending before the directory. */ + if (extractG4DataFromFile(fname, &datacomp, &nbytescomp, + &w, &h, &minisblack)) { + return (L_COMP_DATA *)ERROR_PTR("datacomp not extracted", + procName, NULL); + } + + /* Optionally, encode the compressed data */ + if (ascii85flag == 1) { + data85 = encodeAscii85(datacomp, nbytescomp, &nbytes85); + LEPT_FREE(datacomp); + if (!data85) + return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); + else + data85[nbytes85 - 1] = '\0'; /* remove the newline */ + } + + cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); + if (ascii85flag == 0) { + cid->datacomp = datacomp; + } else { /* ascii85 */ + cid->data85 = data85; + cid->nbytes85 = nbytes85; + } + cid->type = L_G4_ENCODE; + cid->nbytescomp = nbytescomp; + cid->w = w; + cid->h = h; + cid->bps = 1; + cid->spp = 1; + cid->minisblack = minisblack; + cid->res = xres; + return cid; +} + + +/*! + * \brief cidConvertToPdfData() + * + * \param[in] cid compressed image data + * \param[in] title [optional] pdf title; can be NULL + * \param[out] pdata output pdf data for image + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Caller must not destroy the cid.  It is absorbed in the
+ *          lpd and destroyed by this function.
+ * 
+ */ +l_ok +cidConvertToPdfData(L_COMP_DATA *cid, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 res, ret; +l_float32 wpt, hpt; +L_PDF_DATA *lpd = NULL; + + PROCNAME("cidConvertToPdfData"); + + if (!pdata || !pnbytes) + return ERROR_INT("&data and &nbytes not both defined", procName, 1); + *pdata = NULL; + *pnbytes = 0; + if (!cid) + return ERROR_INT("cid not defined", procName, 1); + + /* Get media box parameters, in pts */ + res = cid->res; + if (res <= 0) + res = DefaultInputRes; + wpt = cid->w * 72. / res; + hpt = cid->h * 72. / res; + + /* Set up the pdf data struct (lpd) */ + if ((lpd = pdfdataCreate(title)) == NULL) + return ERROR_INT("lpd not made", procName, 1); + ptraAdd(lpd->cida, cid); + lpd->n++; + ptaAddPt(lpd->xy, 0, 0); /* xpt = ypt = 0 */ + ptaAddPt(lpd->wh, wpt, hpt); + + /* Generate the pdf string and destroy the lpd */ + ret = l_generatePdf(pdata, pnbytes, lpd); + pdfdataDestroy(&lpd); + if (ret) + return ERROR_INT("pdf output not made", procName, 1); + return 0; +} + + +/*! + * \brief l_CIDataDestroy() + * + * \param[in,out] pcid will be set to null before returning + * \return void + */ +void +l_CIDataDestroy(L_COMP_DATA **pcid) +{ +L_COMP_DATA *cid; + + PROCNAME("l_CIDataDestroy"); + + if (pcid == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + if ((cid = *pcid) == NULL) + return; + + if (cid->datacomp) LEPT_FREE(cid->datacomp); + if (cid->data85) LEPT_FREE(cid->data85); + if (cid->cmapdata85) LEPT_FREE(cid->cmapdata85); + if (cid->cmapdatahex) LEPT_FREE(cid->cmapdatahex); + LEPT_FREE(cid); + *pcid = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Helper functions for generating the output pdf string * + *---------------------------------------------------------------------*/ +/*! + * \brief l_generatePdf() + * + * \param[out] pdata pdf array + * \param[out] pnbytes number of bytes in pdf array + * \param[in] lpd all the required input image data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) On error, no data is returned.
+ *      (2) The objects are:
+ *            1: Catalog
+ *            2: Info
+ *            3: Pages
+ *            4: Page
+ *            5: Contents  (rendering command)
+ *            6 to 6+n-1: n XObjects
+ *            6+n to 6+n+m-1: m colormaps
+ * 
+ */ +static l_int32 +l_generatePdf(l_uint8 **pdata, + size_t *pnbytes, + L_PDF_DATA *lpd) +{ + PROCNAME("l_generatePdf"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!lpd) + return ERROR_INT("lpd not defined", procName, 1); + + generateFixedStringsPdf(lpd); + generateMediaboxPdf(lpd); + generatePageStringPdf(lpd); + generateContentStringPdf(lpd); + generatePreXStringsPdf(lpd); + generateColormapStringsPdf(lpd); + generateTrailerPdf(lpd); + return generateOutputDataPdf(pdata, pnbytes, lpd); +} + + +static void +generateFixedStringsPdf(L_PDF_DATA *lpd) +{ +char buf[L_SMALLBUF]; +char *version, *datestr; +SARRAY *sa; + + PROCNAME("generateFixedStringsPdf"); + + /* Accumulate data for the header and objects 1-3 */ + lpd->id = stringNew("%PDF-1.5\n"); + l_dnaAddNumber(lpd->objsize, strlen(lpd->id)); + + lpd->obj1 = stringNew("1 0 obj\n" + "<<\n" + "/Type /Catalog\n" + "/Pages 3 0 R\n" + ">>\n" + "endobj\n"); + l_dnaAddNumber(lpd->objsize, strlen(lpd->obj1)); + + sa = sarrayCreate(0); + sarrayAddString(sa, "2 0 obj\n" + "<<\n", L_COPY); + if (var_WRITE_DATE_AND_VERSION) { + datestr = l_getFormattedDate(); + snprintf(buf, sizeof(buf), "/CreationDate (D:%s)\n", datestr); + sarrayAddString(sa, buf, L_COPY); + LEPT_FREE(datestr); + version = getLeptonicaVersion(); + snprintf(buf, sizeof(buf), + "/Producer (leptonica: %s)\n", version); + LEPT_FREE(version); + } else { + snprintf(buf, sizeof(buf), "/Producer (leptonica)\n"); + } + sarrayAddString(sa, buf, L_COPY); + if (lpd->title) { + char *hexstr; + if ((hexstr = generateEscapeString(lpd->title)) != NULL) { + snprintf(buf, sizeof(buf), "/Title %s\n", hexstr); + sarrayAddString(sa, buf, L_COPY); + } else { + L_ERROR("title string is not ascii\n", procName); + } + LEPT_FREE(hexstr); + } + sarrayAddString(sa, ">>\n" + "endobj\n", L_COPY); + lpd->obj2 = sarrayToString(sa, 0); + l_dnaAddNumber(lpd->objsize, strlen(lpd->obj2)); + sarrayDestroy(&sa); + + lpd->obj3 = stringNew("3 0 obj\n" + "<<\n" + "/Type /Pages\n" + "/Kids [ 4 0 R ]\n" + "/Count 1\n" + ">>\n"); + l_dnaAddNumber(lpd->objsize, strlen(lpd->obj3)); + + /* Do the post-datastream string */ + lpd->poststream = stringNew("\n" + "endstream\n" + "endobj\n"); + return; +} + + +/*! + * \brief generateEscapeString() + * + * \param[in] str input string + * \return hex escape string, or null on error + * + *
+ * Notes:
+ *      (1) If the input string is not ascii, returns null.
+ *      (2) This takes an input ascii string and generates a hex
+ *          ascii output string with 4 bytes out for each byte in.
+ *          The feff code at the beginning tells the pdf interpreter
+ *          that the data is to be interpreted as big-endian, 4 bytes
+ *          at a time.  For ascii, the first two bytes are 0 and the
+ *          last two bytes are less than 0x80.
+ * 
+ */ +static char * +generateEscapeString(const char *str) +{ +char smallbuf[8]; +char *buffer; +l_int32 i, nchar, buflen; + + PROCNAME("generateEscapeString"); + + if (!str) + return (char *)ERROR_PTR("str not defined", procName, NULL); + nchar = strlen(str); + for (i = 0; i < nchar; i++) { + if (str[i] < 0) + return (char *)ERROR_PTR("str not all ascii", procName, NULL); + } + + buflen = 4 * nchar + 10; + buffer = (char *)LEPT_CALLOC(buflen, sizeof(char)); + stringCat(buffer, buflen, ""); + return buffer; +} + + +static void +generateMediaboxPdf(L_PDF_DATA *lpd) +{ +l_int32 i; +l_float32 xpt, ypt, wpt, hpt, maxx, maxy; + + /* First get the full extent of all the images. + * This is the mediabox, in pts. */ + maxx = maxy = 0; + for (i = 0; i < lpd->n; i++) { + ptaGetPt(lpd->xy, i, &xpt, &ypt); + ptaGetPt(lpd->wh, i, &wpt, &hpt); + maxx = L_MAX(maxx, xpt + wpt); + maxy = L_MAX(maxy, ypt + hpt); + } + + lpd->mediabox = boxCreate(0, 0, (l_int32)(maxx + 0.5), + (l_int32)(maxy + 0.5)); + + /* ypt is in standard image coordinates: the location of + * the UL image corner with respect to the UL media box corner. + * Rewrite each ypt for PostScript coordinates: the location of + * the LL image corner with respect to the LL media box corner. */ + for (i = 0; i < lpd->n; i++) { + ptaGetPt(lpd->xy, i, &xpt, &ypt); + ptaGetPt(lpd->wh, i, &wpt, &hpt); + ptaSetPt(lpd->xy, i, xpt, maxy - ypt - hpt); + } + + return; +} + + +static l_int32 +generatePageStringPdf(L_PDF_DATA *lpd) +{ +char *buf; +char *xstr; +l_int32 bufsize, i, wpt, hpt; +SARRAY *sa; + + PROCNAME("generatePageStringPdf"); + + /* Allocate 1000 bytes for the boilerplate text, and + * 50 bytes for each reference to an image in the + * ProcSet array. */ + bufsize = 1000 + 50 * lpd->n; + if ((buf = (char *)LEPT_CALLOC(bufsize, sizeof(char))) == NULL) + return ERROR_INT("calloc fail for buf", procName, 1); + + boxGetGeometry(lpd->mediabox, NULL, NULL, &wpt, &hpt); + sa = sarrayCreate(lpd->n); + for (i = 0; i < lpd->n; i++) { + snprintf(buf, bufsize, "/Im%d %d 0 R ", i + 1, 6 + i); + sarrayAddString(sa, buf, L_COPY); + } + xstr = sarrayToString(sa, 0); + sarrayDestroy(&sa); + if (!xstr) { + LEPT_FREE(buf); + return ERROR_INT("xstr not made", procName, 1); + } + + snprintf(buf, bufsize, "4 0 obj\n" + "<<\n" + "/Type /Page\n" + "/Parent 3 0 R\n" + "/MediaBox [%d %d %d %d]\n" + "/Contents 5 0 R\n" + "/Resources\n" + "<<\n" + "/XObject << %s >>\n" + "/ProcSet [ /ImageB /ImageI /ImageC ]\n" + ">>\n" + ">>\n" + "endobj\n", + 0, 0, wpt, hpt, xstr); + + lpd->obj4 = stringNew(buf); + l_dnaAddNumber(lpd->objsize, strlen(lpd->obj4)); + sarrayDestroy(&sa); + LEPT_FREE(buf); + LEPT_FREE(xstr); + return 0; +} + + +static l_int32 +generateContentStringPdf(L_PDF_DATA *lpd) +{ +char *buf; +char *cstr; +l_int32 i, bufsize; +l_float32 xpt, ypt, wpt, hpt; +SARRAY *sa; + + PROCNAME("generateContentStringPdf"); + + bufsize = 1000 + 200 * lpd->n; + if ((buf = (char *)LEPT_CALLOC(bufsize, sizeof(char))) == NULL) + return ERROR_INT("calloc fail for buf", procName, 1); + + sa = sarrayCreate(lpd->n); + for (i = 0; i < lpd->n; i++) { + ptaGetPt(lpd->xy, i, &xpt, &ypt); + ptaGetPt(lpd->wh, i, &wpt, &hpt); + snprintf(buf, bufsize, + "q %.4f %.4f %.4f %.4f %.4f %.4f cm /Im%d Do Q\n", + wpt, 0.0, 0.0, hpt, xpt, ypt, i + 1); + sarrayAddString(sa, buf, L_COPY); + } + cstr = sarrayToString(sa, 0); + sarrayDestroy(&sa); + if (!cstr) { + LEPT_FREE(buf); + return ERROR_INT("cstr not made", procName, 1); + } + + snprintf(buf, bufsize, "5 0 obj\n" + "<< /Length %d >>\n" + "stream\n" + "%s" + "endstream\n" + "endobj\n", + (l_int32)strlen(cstr), cstr); + + lpd->obj5 = stringNew(buf); + l_dnaAddNumber(lpd->objsize, strlen(lpd->obj5)); + sarrayDestroy(&sa); + LEPT_FREE(buf); + LEPT_FREE(cstr); + return 0; +} + + +static l_int32 +generatePreXStringsPdf(L_PDF_DATA *lpd) +{ +char buff[256]; +char buf[L_BIGBUF]; +char *cstr, *bstr, *fstr, *pstr, *xstr; +l_int32 i, cmindex; +L_COMP_DATA *cid; +SARRAY *sa; + + PROCNAME("generatePreXStringsPdf"); + + sa = lpd->saprex; + cmindex = 6 + lpd->n; /* starting value */ + for (i = 0; i < lpd->n; i++) { + pstr = cstr = NULL; + if ((cid = pdfdataGetCid(lpd, i)) == NULL) + return ERROR_INT("cid not found", procName, 1); + + if (cid->type == L_G4_ENCODE) { + if (var_WRITE_G4_IMAGE_MASK) { + cstr = stringNew("/ImageMask true\n" + "/ColorSpace /DeviceGray"); + } else { + cstr = stringNew("/ColorSpace /DeviceGray"); + } + bstr = stringNew("/BitsPerComponent 1\n" + "/Interpolate true"); + snprintf(buff, sizeof(buff), + "/Filter /CCITTFaxDecode\n" + "/DecodeParms\n" + "<<\n" + "/K -1\n" + "/Columns %d\n" + ">>", cid->w); + fstr = stringNew(buff); + } else if (cid->type == L_JPEG_ENCODE) { + if (cid->spp == 1) + cstr = stringNew("/ColorSpace /DeviceGray"); + else if (cid->spp == 3) + cstr = stringNew("/ColorSpace /DeviceRGB"); + else if (cid->spp == 4) /* pdf supports cmyk */ + cstr = stringNew("/ColorSpace /DeviceCMYK"); + else + L_ERROR("in jpeg: spp != 1, 3 or 4\n", procName); + bstr = stringNew("/BitsPerComponent 8"); + fstr = stringNew("/Filter /DCTDecode"); + } else if (cid->type == L_JP2K_ENCODE) { + if (cid->spp == 1) + cstr = stringNew("/ColorSpace /DeviceGray"); + else if (cid->spp == 3) + cstr = stringNew("/ColorSpace /DeviceRGB"); + else + L_ERROR("in jp2k: spp != 1 && spp != 3\n", procName); + bstr = stringNew("/BitsPerComponent 8"); + fstr = stringNew("/Filter /JPXDecode"); + } else { /* type == L_FLATE_ENCODE */ + if (cid->ncolors > 0) { /* cmapped */ + snprintf(buff, sizeof(buff), "/ColorSpace %d 0 R", cmindex++); + cstr = stringNew(buff); + } else { + if (cid->spp == 1 && cid->bps == 1) + cstr = stringNew("/ColorSpace /DeviceGray\n" + "/Decode [1 0]"); + else if (cid->spp == 1) /* 8 bpp */ + cstr = stringNew("/ColorSpace /DeviceGray"); + else if (cid->spp == 3) + cstr = stringNew("/ColorSpace /DeviceRGB"); + else + L_ERROR("unknown colorspace: spp = %d\n", + procName, cid->spp); + } + snprintf(buff, sizeof(buff), "/BitsPerComponent %d", cid->bps); + bstr = stringNew(buff); + fstr = stringNew("/Filter /FlateDecode"); + if (cid->predictor == TRUE) { + snprintf(buff, sizeof(buff), + "/DecodeParms\n" + "<<\n" + " /Columns %d\n" + " /Predictor 14\n" + " /Colors %d\n" + " /BitsPerComponent %d\n" + ">>\n", cid->w, cid->spp, cid->bps); + pstr = stringNew(buff); + } + } + if (!pstr) /* no decode parameters */ + pstr = stringNew(""); + + snprintf(buf, sizeof(buf), + "%d 0 obj\n" + "<<\n" + "/Length %zu\n" + "/Subtype /Image\n" + "%s\n" /* colorspace */ + "/Width %d\n" + "/Height %d\n" + "%s\n" /* bits/component */ + "%s\n" /* filter */ + "%s" /* decode parms; can be empty */ + ">>\n" + "stream\n", + 6 + i, cid->nbytescomp, cstr, + cid->w, cid->h, bstr, fstr, pstr); + xstr = stringNew(buf); + sarrayAddString(sa, xstr, L_INSERT); + l_dnaAddNumber(lpd->objsize, + strlen(xstr) + cid->nbytescomp + strlen(lpd->poststream)); + LEPT_FREE(cstr); + LEPT_FREE(bstr); + LEPT_FREE(fstr); + LEPT_FREE(pstr); + } + + return 0; +} + + +static l_int32 +generateColormapStringsPdf(L_PDF_DATA *lpd) +{ +char buf[L_BIGBUF]; +char *cmstr; +l_int32 i, cmindex, ncmap; +L_COMP_DATA *cid; +SARRAY *sa; + + PROCNAME("generateColormapStringsPdf"); + + /* In our canonical format, we have 5 objects, followed + * by n XObjects, followed by m colormaps, so the index of + * the first colormap object is 6 + n. */ + sa = lpd->sacmap; + cmindex = 6 + lpd->n; /* starting value */ + ncmap = 0; + for (i = 0; i < lpd->n; i++) { + if ((cid = pdfdataGetCid(lpd, i)) == NULL) + return ERROR_INT("cid not found", procName, 1); + if (cid->ncolors == 0) continue; + + ncmap++; + snprintf(buf, sizeof(buf), "%d 0 obj\n" + "[ /Indexed /DeviceRGB\n" + "%d\n" + "%s\n" + "]\n" + "endobj\n", + cmindex, cid->ncolors - 1, cid->cmapdatahex); + cmindex++; + cmstr = stringNew(buf); + l_dnaAddNumber(lpd->objsize, strlen(cmstr)); + sarrayAddString(sa, cmstr, L_INSERT); + } + + lpd->ncmap = ncmap; + return 0; +} + + +static void +generateTrailerPdf(L_PDF_DATA *lpd) +{ +l_int32 i, n, size, linestart; +L_DNA *daloc, *dasize; + + /* Let nobj be the number of numbered objects. These numbered + * objects are indexed by their pdf number in arrays naloc[] + * and nasize[]. The 0th object is the 9 byte header. Then + * the number of objects in nasize, which includes the header, + * is n = nobj + 1. The array naloc[] has n + 1 elements, + * because it includes as the last element the starting + * location of xref. The indexing of these objects, their + * starting locations and sizes are: + * + * Object number Starting location Size + * ------------- ----------------- -------------- + * 0 daloc[0] = 0 dasize[0] = 9 + * 1 daloc[1] = 9 dasize[1] = 49 + * n daloc[n] dasize[n] + * xref daloc[n+1] + * + * We first generate daloc. + */ + dasize = lpd->objsize; + daloc = lpd->objloc; + linestart = 0; + l_dnaAddNumber(daloc, linestart); /* header */ + n = l_dnaGetCount(dasize); + for (i = 0; i < n; i++) { + l_dnaGetIValue(dasize, i, &size); + linestart += size; + l_dnaAddNumber(daloc, linestart); + } + l_dnaGetIValue(daloc, n, &lpd->xrefloc); /* save it */ + + /* Now make the actual trailer string */ + lpd->trailer = makeTrailerStringPdf(daloc); +} + + +static char * +makeTrailerStringPdf(L_DNA *daloc) +{ +char *outstr; +char buf[L_BIGBUF]; +l_int32 i, n, linestart, xrefloc; +SARRAY *sa; + + PROCNAME("makeTrailerStringPdf"); + + if (!daloc) + return (char *)ERROR_PTR("daloc not defined", procName, NULL); + n = l_dnaGetCount(daloc) - 1; /* numbered objects + 1 (yes, +1) */ + + sa = sarrayCreate(0); + snprintf(buf, sizeof(buf), "xref\n" + "0 %d\n" + "0000000000 65535 f \n", n); + sarrayAddString(sa, buf, L_COPY); + for (i = 1; i < n; i++) { + l_dnaGetIValue(daloc, i, &linestart); + snprintf(buf, sizeof(buf), "%010d 00000 n \n", linestart); + sarrayAddString(sa, buf, L_COPY); + } + + l_dnaGetIValue(daloc, n, &xrefloc); + snprintf(buf, sizeof(buf), "trailer\n" + "<<\n" + "/Size %d\n" + "/Root 1 0 R\n" + "/Info 2 0 R\n" + ">>\n" + "startxref\n" + "%d\n" + "%%%%EOF\n", n, xrefloc); + sarrayAddString(sa, buf, L_COPY); + outstr = sarrayToString(sa, 0); + sarrayDestroy(&sa); + return outstr; +} + + +/*! + * \brief generateOutputDataPdf() + * + * \param[out] pdata pdf data array + * \param[out] pnbytes size of pdf data array + * \param[in] lpd input data used to make pdf + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Only called from l_generatePdf().  On error, no data is returned.
+ * 
+ */ +static l_int32 +generateOutputDataPdf(l_uint8 **pdata, + size_t *pnbytes, + L_PDF_DATA *lpd) +{ +char *str; +l_uint8 *data; +l_int32 nimages, i, len; +l_int32 *sizes, *locs; +size_t nbytes; +L_COMP_DATA *cid; + + PROCNAME("generateOutputDataPdf"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + nbytes = lpd->xrefloc + strlen(lpd->trailer); + *pnbytes = nbytes; + if ((data = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) + return ERROR_INT("calloc fail for data", procName, 1); + *pdata = data; + + sizes = l_dnaGetIArray(lpd->objsize); + locs = l_dnaGetIArray(lpd->objloc); + memcpy(data, lpd->id, sizes[0]); + memcpy(data + locs[1], lpd->obj1, sizes[1]); + memcpy(data + locs[2], lpd->obj2, sizes[2]); + memcpy(data + locs[3], lpd->obj3, sizes[3]); + memcpy(data + locs[4], lpd->obj4, sizes[4]); + memcpy(data + locs[5], lpd->obj5, sizes[5]); + + /* Each image has 3 parts: variable preamble, the compressed + * data stream, and the fixed poststream. */ + nimages = lpd->n; + for (i = 0; i < nimages; i++) { + if ((cid = pdfdataGetCid(lpd, i)) == NULL) { /* should not happen */ + LEPT_FREE(sizes); + LEPT_FREE(locs); + return ERROR_INT("cid not found", procName, 1); + } + str = sarrayGetString(lpd->saprex, i, L_NOCOPY); + len = strlen(str); + memcpy(data + locs[6 + i], str, len); + memcpy(data + locs[6 + i] + len, + cid->datacomp, cid->nbytescomp); + memcpy(data + locs[6 + i] + len + cid->nbytescomp, + lpd->poststream, strlen(lpd->poststream)); + } + + /* Each colormap is simply a stored string */ + for (i = 0; i < lpd->ncmap; i++) { + str = sarrayGetString(lpd->sacmap, i, L_NOCOPY); + memcpy(data + locs[6 + nimages + i], str, strlen(str)); + } + + /* And finally the trailer */ + memcpy(data + lpd->xrefloc, lpd->trailer, strlen(lpd->trailer)); + LEPT_FREE(sizes); + LEPT_FREE(locs); + return 0; +} + + +/*---------------------------------------------------------------------* + * Helper functions for generating multipage pdf output * + *---------------------------------------------------------------------*/ +/*! + * \brief parseTrailerPdf() + * + * \param[in] bas lba of a pdf file + * \param[out] pda byte locations of the beginning of each object + * \return 0 if OK, 1 on error + */ +static l_int32 +parseTrailerPdf(L_BYTEA *bas, + L_DNA **pda) +{ +char *str; +l_uint8 nl = '\n'; +l_uint8 *data; +l_int32 i, j, start, startloc, xrefloc, found, loc, nobj, objno, trailer_ok; +size_t size; +L_DNA *da, *daobj, *daxref; +SARRAY *sa; + + PROCNAME("parseTrailerPdf"); + + if (!pda) + return ERROR_INT("&da not defined", procName, 1); + *pda = NULL; + if (!bas) + return ERROR_INT("bas not defined", procName, 1); + data = l_byteaGetData(bas, &size); + if (memcmp(data, "%PDF-1.", 7) != 0) + return ERROR_INT("PDF header signature not found", procName, 1); + + /* Search for "startxref" starting 50 bytes from the EOF */ + start = 0; + if (size > 50) + start = size - 50; + arrayFindSequence(data + start, size - start, + (l_uint8 *)"startxref\n", 10, &loc, &found); + if (!found) + return ERROR_INT("startxref not found!", procName, 1); + if (sscanf((char *)(data + start + loc + 10), "%d\n", &xrefloc) != 1) + return ERROR_INT("xrefloc not found!", procName, 1); + if (xrefloc < 0 || xrefloc >= size) + return ERROR_INT("invalid xrefloc!", procName, 1); + sa = sarrayCreateLinesFromString((char *)(data + xrefloc), 0); + str = sarrayGetString(sa, 1, L_NOCOPY); + if ((sscanf(str, "0 %d", &nobj)) != 1) { + sarrayDestroy(&sa); + return ERROR_INT("nobj not found", procName, 1); + } + + /* Get starting locations. The numa index is the + * object number. loc[0] is the ID; loc[nobj + 1] is xrefloc. */ + da = l_dnaCreate(nobj + 1); + *pda = da; + for (i = 0; i < nobj; i++) { + str = sarrayGetString(sa, i + 2, L_NOCOPY); + sscanf(str, "%d", &startloc); + l_dnaAddNumber(da, startloc); + } + l_dnaAddNumber(da, xrefloc); + +#if DEBUG_MULTIPAGE + fprintf(stderr, "************** Trailer string ************\n"); + fprintf(stderr, "xrefloc = %d", xrefloc); + sarrayWriteStream(stderr, sa); + + fprintf(stderr, "************** Object locations ************"); + l_dnaWriteStream(stderr, da); +#endif /* DEBUG_MULTIPAGE */ + sarrayDestroy(&sa); + + /* Verify correct parsing */ + trailer_ok = TRUE; + for (i = 1; i < nobj; i++) { + l_dnaGetIValue(da, i, &startloc); + if ((sscanf((char *)(data + startloc), "%d 0 obj", &objno)) != 1) { + L_ERROR("bad trailer for object %d\n", procName, i); + trailer_ok = FALSE; + break; + } + } + + /* If the trailer is broken, reconstruct the correct obj locations */ + if (!trailer_ok) { + L_INFO("rebuilding pdf trailer\n", procName); + l_dnaEmpty(da); + l_dnaAddNumber(da, 0); + l_byteaFindEachSequence(bas, (l_uint8 *)" 0 obj\n", 7, &daobj); + nobj = l_dnaGetCount(daobj); + for (i = 0; i < nobj; i++) { + l_dnaGetIValue(daobj, i, &loc); + for (j = loc - 1; j > 0; j--) { + if (data[j] == nl) + break; + } + l_dnaAddNumber(da, j + 1); + } + l_byteaFindEachSequence(bas, (l_uint8 *)"xref", 4, &daxref); + l_dnaGetIValue(daxref, 0, &loc); + l_dnaAddNumber(da, loc); + l_dnaDestroy(&daobj); + l_dnaDestroy(&daxref); + } + + return 0; +} + + +static char * +generatePagesObjStringPdf(NUMA *napage) +{ +char *str; +char *buf; +l_int32 i, n, index, bufsize; +SARRAY *sa; + + PROCNAME("generatePagesObjStringPdf"); + + if (!napage) + return (char *)ERROR_PTR("napage not defined", procName, NULL); + + n = numaGetCount(napage); + bufsize = 100 + 16 * n; /* large enough to hold the output string */ + buf = (char *)LEPT_CALLOC(bufsize, sizeof(char)); + sa = sarrayCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(napage, i, &index); + snprintf(buf, bufsize, " %d 0 R ", index); + sarrayAddString(sa, buf, L_COPY); + } + + str = sarrayToString(sa, 0); + snprintf(buf, bufsize - 1, "3 0 obj\n" + "<<\n" + "/Type /Pages\n" + "/Kids [%s]\n" + "/Count %d\n" + ">>\n", str, n); + sarrayDestroy(&sa); + LEPT_FREE(str); + return buf; +} + + +/*! + * \brief substituteObjectNumbers() + * + * \param[in] bas lba of a pdf object + * \param[in] na_objs object number mapping array + * \return bad lba of rewritten pdf for the object + * + *
+ * Notes:
+ *      (1) Interpret the first set of bytes as the object number,
+ *          map to the new number, and write it out.
+ *      (2) Find all occurrences of this 4-byte sequence: " 0 R"
+ *      (3) Find the location and value of the integer preceding this,
+ *          and map it to the new value.
+ *      (4) Rewrite the object with new object numbers.
+ * 
+ */ +static L_BYTEA * +substituteObjectNumbers(L_BYTEA *bas, + NUMA *na_objs) +{ +l_uint8 space = ' '; +l_uint8 *datas; +l_uint8 buf[32]; /* only needs to hold one integer in ascii format */ +l_int32 start, nrepl, i, j, objin, objout, found; +l_int32 *objs, *matches; +size_t size; +L_BYTEA *bad; +L_DNA *da_match; + + datas = l_byteaGetData(bas, &size); + bad = l_byteaCreate(100); + objs = numaGetIArray(na_objs); /* object number mapper */ + + /* Substitute the object number on the first line */ + sscanf((char *)datas, "%d", &objin); + objout = objs[objin]; + snprintf((char *)buf, 32, "%d", objout); + l_byteaAppendString(bad, (char *)buf); + + /* Find the set of matching locations for object references */ + arrayFindSequence(datas, size, &space, 1, &start, &found); + da_match = arrayFindEachSequence(datas, size, (l_uint8 *)" 0 R", 4); + if (!da_match) { + l_byteaAppendData(bad, datas + start, size - start); + LEPT_FREE(objs); + return bad; + } + + /* Substitute all the object reference numbers */ + nrepl = l_dnaGetCount(da_match); + matches = l_dnaGetIArray(da_match); + for (i = 0; i < nrepl; i++) { + /* Find the first space before the object number */ + for (j = matches[i] - 1; j > 0; j--) { + if (datas[j] == space) + break; + } + /* Copy bytes from 'start' up to the object number */ + l_byteaAppendData(bad, datas + start, j - start + 1); + sscanf((char *)(datas + j + 1), "%d", &objin); + objout = objs[objin]; + snprintf((char *)buf, 32, "%d", objout); + l_byteaAppendString(bad, (char *)buf); + start = matches[i]; + } + l_byteaAppendData(bad, datas + start, size - start); + + LEPT_FREE(objs); + LEPT_FREE(matches); + l_dnaDestroy(&da_match); + return bad; +} + + +/*---------------------------------------------------------------------* + * Create/destroy/access pdf data * + *---------------------------------------------------------------------*/ +static L_PDF_DATA * +pdfdataCreate(const char *title) +{ +L_PDF_DATA *lpd; + + lpd = (L_PDF_DATA *)LEPT_CALLOC(1, sizeof(L_PDF_DATA)); + if (title) lpd->title = stringNew(title); + lpd->cida = ptraCreate(10); + lpd->xy = ptaCreate(10); + lpd->wh = ptaCreate(10); + lpd->saprex = sarrayCreate(10); + lpd->sacmap = sarrayCreate(10); + lpd->objsize = l_dnaCreate(20); + lpd->objloc = l_dnaCreate(20); + return lpd; +} + +static void +pdfdataDestroy(L_PDF_DATA **plpd) +{ +l_int32 i; +L_COMP_DATA *cid; +L_PDF_DATA *lpd; + + PROCNAME("pdfdataDestroy"); + + if (plpd== NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + if ((lpd = *plpd) == NULL) + return; + + if (lpd->title) LEPT_FREE(lpd->title); + for (i = 0; i < lpd->n; i++) { + cid = (L_COMP_DATA *)ptraRemove(lpd->cida, i, L_NO_COMPACTION); + l_CIDataDestroy(&cid); + } + + ptraDestroy(&lpd->cida, 0, 0); + if (lpd->id) LEPT_FREE(lpd->id); + if (lpd->obj1) LEPT_FREE(lpd->obj1); + if (lpd->obj2) LEPT_FREE(lpd->obj2); + if (lpd->obj3) LEPT_FREE(lpd->obj3); + if (lpd->obj4) LEPT_FREE(lpd->obj4); + if (lpd->obj5) LEPT_FREE(lpd->obj5); + if (lpd->poststream) LEPT_FREE(lpd->poststream); + if (lpd->trailer) LEPT_FREE(lpd->trailer); + if (lpd->xy) ptaDestroy(&lpd->xy); + if (lpd->wh) ptaDestroy(&lpd->wh); + if (lpd->mediabox) boxDestroy(&lpd->mediabox); + if (lpd->saprex) sarrayDestroy(&lpd->saprex); + if (lpd->sacmap) sarrayDestroy(&lpd->sacmap); + if (lpd->objsize) l_dnaDestroy(&lpd->objsize); + if (lpd->objloc) l_dnaDestroy(&lpd->objloc); + LEPT_FREE(lpd); + *plpd = NULL; + return; +} + + +static L_COMP_DATA * +pdfdataGetCid(L_PDF_DATA *lpd, + l_int32 index) +{ + PROCNAME("pdfdataGetCid"); + + if (!lpd) + return (L_COMP_DATA *)ERROR_PTR("lpd not defined", procName, NULL); + if (index < 0 || index >= lpd->n) + return (L_COMP_DATA *)ERROR_PTR("invalid image index", procName, NULL); + + return (L_COMP_DATA *)ptraGetPtrToItem(lpd->cida, index); +} + + +/*---------------------------------------------------------------------* + * Set flags for special modes * + *---------------------------------------------------------------------*/ +/*! + * \brief l_pdfSetG4ImageMask() + * + * \param[in] flag 1 for writing g4 data as fg only through a mask; + * 0 for writing fg and bg + * \return void + * + *
+ * Notes:
+ *      (1) The default is for writing only the fg (through the mask).
+ *          That way when you write a 1 bpp image, the bg is transparent,
+ *          so any previously written image remains visible behind it.
+ * 
+ */ +void +l_pdfSetG4ImageMask(l_int32 flag) +{ + var_WRITE_G4_IMAGE_MASK = flag; +} + + +/*! + * \brief l_pdfSetDateAndVersion() + * + * \param[in] flag 1 for writing date/time and leptonica version; + * 0 for omitting this from the metadata + * \return void + * + *
+ * Notes:
+ *      (1) The default is for writing this data.  For regression tests
+ *          that compare output against golden files, it is useful to omit.
+ * 
+ */ +void +l_pdfSetDateAndVersion(l_int32 flag) +{ + var_WRITE_DATE_AND_VERSION = flag; +} + +/* --------------------------------------------*/ +#endif /* USE_PDFIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pdfio2stub.c b/hgdriver/3rdparty/hgOCR/leptonica/pdfio2stub.c new file mode 100644 index 0000000..d9a53dc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pdfio2stub.c @@ -0,0 +1,168 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pdfio2stub.c + *
+ *
+ *     Stubs for pdfio2.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_PDFIO /* defined in environ.h */ +/* --------------------------------------------*/ + +/* ----------------------------------------------------------------------*/ + +l_ok pixConvertToPdfData(PIX *pix, l_int32 type, l_int32 quality, + l_uint8 **pdata, size_t *pnbytes, + l_int32 x, l_int32 y, l_int32 res, + const char *title, + L_PDF_DATA **plpd, l_int32 position) +{ + return ERROR_INT("function not present", "pixConvertToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok ptraConcatenatePdfToData(L_PTRA *pa_data, SARRAY *sa, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "ptraConcatenatePdfToData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertTiffMultipageToPdf(const char *filein, const char *fileout) +{ + return ERROR_INT("function not present", "convertTiffMultipageToPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok l_generateCIDataForPdf(const char *fname, PIX *pix, l_int32 quality, + L_COMP_DATA **pcid) +{ + return ERROR_INT("function not present", "l_generateCIDataForPdf", 1); +} + +/* ----------------------------------------------------------------------*/ + +L_COMP_DATA * l_generateFlateDataPdf(const char *fname, PIX *pix) +{ + return (L_COMP_DATA *)ERROR_PTR("function not present", + "l_generateFlateDataPdf", NULL); +} + +/* ----------------------------------------------------------------------*/ + +L_COMP_DATA * l_generateJpegData(const char *fname, l_int32 ascii85flag) +{ + return (L_COMP_DATA *)ERROR_PTR("function not present", + "l_generateJpegData", NULL); +} + +/* ----------------------------------------------------------------------*/ + +L_COMP_DATA * l_generateJpegDataMem(l_uint8 *data, size_t nbytes, + l_int32 ascii85flag) +{ + return (L_COMP_DATA *)ERROR_PTR("function not present", + "l_generateJpegDataMem", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok l_generateCIData(const char *fname, l_int32 type, l_int32 quality, + l_int32 ascii85, L_COMP_DATA **pcid) +{ + return ERROR_INT("function not present", "l_generateCIData", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixGenerateCIData(PIX *pixs, l_int32 type, l_int32 quality, + l_int32 ascii85, L_COMP_DATA **pcid) +{ + return ERROR_INT("function not present", "pixGenerateCIData", 1); +} + +/* ----------------------------------------------------------------------*/ + +L_COMP_DATA * l_generateFlateData(const char *fname, l_int32 ascii85flag) +{ + return (L_COMP_DATA *)ERROR_PTR("function not present", + "l_generateFlateData", NULL); +} + +/* ----------------------------------------------------------------------*/ + +L_COMP_DATA * l_generateG4Data(const char *fname, l_int32 ascii85flag) +{ + return (L_COMP_DATA *)ERROR_PTR("function not present", + "l_generateG4Data", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok cidConvertToPdfData(L_COMP_DATA *cid, const char *title, + l_uint8 **pdata, size_t *pnbytes) +{ + return ERROR_INT("function not present", "cidConvertToPdfData", 1); +} + +/* ----------------------------------------------------------------------*/ + +void l_CIDataDestroy(L_COMP_DATA **pcid) +{ + L_ERROR("function not present\n", "l_CIDataDestroy"); + return; +} + +/* ----------------------------------------------------------------------*/ + +void l_pdfSetG4ImageMask(l_int32 flag) +{ + L_ERROR("function not present\n", "l_pdfSetG4ImageMask"); + return; +} + +/* ----------------------------------------------------------------------*/ + +void l_pdfSetDateAndVersion(l_int32 flag) +{ + L_ERROR("function not present\n", "l_pdfSetDateAndVersion"); + return; +} + +/* ----------------------------------------------------------------------*/ + +/* --------------------------------------------*/ +#endif /* !USE_PDFIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix.h b/hgdriver/3rdparty/hgOCR/leptonica/pix.h new file mode 100644 index 0000000..d1d75c9 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix.h @@ -0,0 +1,1341 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_PIX_H +#define LEPTONICA_PIX_H + +/*! + * \file pix.h + * + *
+ *   Valid image types in leptonica:
+ *       Pix: 1 bpp, with and without colormap
+ *       Pix: 2 bpp, with and without colormap
+ *       Pix: 4 bpp, with and without colormap
+ *       Pix: 8 bpp, with and without colormap
+ *       Pix: 16 bpp (1 spp)
+ *       Pix: 32 bpp (rgb, 3 spp)
+ *       Pix: 32 bpp (rgba, 4 spp)
+ *       FPix: 32 bpp float
+ *       DPix: 64 bpp double
+ *       Notes:
+ *          (1) The only valid Pix image type with alpha is rgba.
+ *              In particular, the alpha component is not used in
+ *              cmapped images.
+ *          (2) PixComp can hold any Pix with IFF_PNG encoding.
+ *
+ *   Contents:
+ *
+ *   (1) This file defines most of the image-related structs used in leptonica:
+ *         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
+ *
+ *   (2) This file has definitions for:
+ *         Colors for RGBA
+ *         Colors for drawing boxes
+ *         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 and location filter flags
+ *         Color component selection flags
+ *         16-bit conversion flags
+ *         Rotation and shear flags
+ *         Affine transform order flags
+ *         Grayscale filling flags
+ *         Flags for setting to white or black
+ *         Flags for getting white or black pixel value
+ *         Flags for 8 and 16 bit pixel sums
+ *         Dithering flags
+ *         Distance flags
+ *         Value flags
+ *         Statistical measures
+ *         Set selection flags
+ *         Text orientation flags
+ *         Edge orientation flags
+ *         Line orientation flags
+ *         Image orientation flags
+ *         Scan direction flags
+ *         Box size adjustment flags
+ *         Flags for modifying box boundaries using a second box
+ *         Handling overlapping bounding boxes in boxa
+ *         Selecting or making a box from two (intersecting) boxes
+ *         Flags for replacing invalid boxes
+ *         Flags for box corners
+ *         Horizontal warp
+ *         Pixel selection for resampling
+ *         Thinning flags
+ *         Runlength flags
+ *         Edge filter flags
+ *         Subpixel color component ordering in LCD display
+ *         HSV histogram flags
+ *         Region flags (inclusion, exclusion)
+ *         Flags for adding text to a pix
+ *         Flags for plotting on a pix
+ *         Flags for making simple masks
+ *         Flags for selecting display program
+ *         Flags in the 'special' pix field for non-default operations
+ *         Handling negative values in conversion to unsigned int
+ *         Relative to zero flags
+ *         Flags for adding or removing trailing slash from string
+ *
+ *   (3) This file has typedefs for the pix allocator and deallocator functions
+ *         alloc_fn()
+ *         dealloc_fn().
+ * 
+ */ + + +/*-------------------------------------------------------------------------* + * Basic Pix * + *-------------------------------------------------------------------------*/ + /* The 'special' field is by default 0, but it can hold integers + * that direct non-default actions, e.g., in png and jpeg I/O. */ + +/*! Basic Pix */ +struct Pix +{ + l_uint32 w; /*!< width in pixels */ + l_uint32 h; /*!< height in pixels */ + l_uint32 d; /*!< depth in bits (bpp) */ + l_uint32 spp; /*!< number of samples per pixel */ + 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_* */ + l_int32 special; /*!< special instructions for I/O, etc */ + char *text; /*!< text string associated with pix */ + struct PixColormap *colormap; /*!< colormap (may be null) */ + l_uint32 *data; /*!< the image data */ +}; +typedef struct Pix PIX; + +/*! Colormap of a 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; /*!< blue value */ + l_uint8 green; /*!< green value */ + l_uint8 red; /*!< red value */ + l_uint8 alpha; /*!< alpha value */ +}; +typedef struct RGBA_Quad RGBA_QUAD; + + +/*-------------------------------------------------------------------------* + * Colors for 32 RGBA * + *-------------------------------------------------------------------------*/ +/*
+ *  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 used to a small extent in leptonica.
+ *      (2) Do not change these values!  If you redefine them, functions
+ *          that have the shifts hardcoded for efficiency and conciseness
+ *          (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.
+ *      (3) The shifts to extract the red, green, blue and alpha components
+ *          from a 32 bit pixel are defined here.
+ * 
+ */ + +/*! RGBA Color */ +enum { + COLOR_RED = 0, /*!< red color index in RGBA_QUAD */ + COLOR_GREEN = 1, /*!< green color index in RGBA_QUAD */ + COLOR_BLUE = 2, /*!< blue color index in RGBA_QUAD */ + L_ALPHA_CHANNEL = 3 /*!< alpha value index in RGBA_QUAD */ +}; + +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 */ + + +/*-------------------------------------------------------------------------* + * Colors for drawing boxes * + *-------------------------------------------------------------------------*/ +/*! Box Color */ +enum { + L_DRAW_RED = 0, /*!< draw in red */ + L_DRAW_GREEN = 1, /*!< draw in green */ + L_DRAW_BLUE = 2, /*!< draw in blue */ + L_DRAW_SPECIFIED = 3, /*!< draw specified color */ + L_DRAW_RGB = 4, /*!< draw as sequence of r,g,b */ + L_DRAW_RANDOM = 5 /*!< draw randomly chosen colors */ +}; + + +/*-------------------------------------------------------------------------* + * Perceptual color weights * + *-------------------------------------------------------------------------*/ +/*
+ *  Notes:
+ *      (1) These perceptual weighting factors are ad-hoc, but they do
+ *          add up to 1.  Unlike, for example, the weighting factors for
+ *          converting 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.3f; /*!< Percept. weight for red */ +static const l_float32 L_GREEN_WEIGHT = 0.5f; /*!< Percept. weight for green */ +static const l_float32 L_BLUE_WEIGHT = 0.2f; /*!< Percept. weight for blue */ + + +/*-------------------------------------------------------------------------* + * Flags for colormap conversion * + *-------------------------------------------------------------------------*/ +/*! Cmap Conversion */ +enum { + REMOVE_CMAP_TO_BINARY = 0, /*!< remove colormap for conv to 1 bpp */ + REMOVE_CMAP_TO_GRAYSCALE = 1, /*!< remove colormap for conv to 8 bpp */ + REMOVE_CMAP_TO_FULL_COLOR = 2, /*!< remove colormap for conv to 32 bpp */ + REMOVE_CMAP_WITH_ALPHA = 3, /*!< remove colormap and alpha */ + REMOVE_CMAP_BASED_ON_SRC = 4 /*!< remove depending on src format */ +}; + + +/*------------------------------------------------------------------------* + *! + *
+ * 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 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
+ *
+ * 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.
+ *
+ * [If for some reason you need compatibility with Sun's xview package,
+ * you can adopt the original Sun definitions to avoid redefinition conflicts:
+ *
+ *      #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)
+ * ]
+ *
+ * We have, for reference, the following 16 unique op flags:
+ *
+ *      PIX_CLR                           0000             0x0
+ *      PIX_SET                           1111             0xf
+ *      PIX_SRC                           1100             0xc
+ *      PIX_DST                           1010             0xa
+ *      PIX_NOT(PIX_SRC)                  0011             0x3
+ *      PIX_NOT(PIX_DST)                  0101             0x5
+ *      PIX_SRC | PIX_DST                 1110             0xe
+ *      PIX_SRC & PIX_DST                 1000             0x8
+ *      PIX_SRC ^ PIX_DST                 0110             0x6
+ *      PIX_NOT(PIX_SRC) | PIX_DST        1011             0xb
+ *      PIX_NOT(PIX_SRC) & PIX_DST        0010             0x2
+ *      PIX_SRC | PIX_NOT(PIX_DST)        1101             0xd
+ *      PIX_SRC & PIX_NOT(PIX_DST)        0100             0x4
+ *      PIX_NOT(PIX_SRC | PIX_DST)        0001             0x1
+ *      PIX_NOT(PIX_SRC & PIX_DST)        0111             0x7
+ *      PIX_NOT(PIX_SRC ^ PIX_DST)        1001             0x9
+ *
+ * 
+ *-------------------------------------------------------------------------*/ + +#define PIX_SRC (0xc) /*!< use source pixels */ +#define PIX_DST (0xa) /*!< use destination pixels */ +#define PIX_NOT(op) ((op) ^ 0x0f) /*!< invert operation %op */ +#define PIX_CLR (0x0) /*!< clear pixels */ +#define PIX_SET (0xf) /*!< set pixels */ + +#define PIX_PAINT (PIX_SRC | PIX_DST) /*!< paint = src | dst */ +#define PIX_MASK (PIX_SRC & PIX_DST) /*!< mask = src & dst */ +#define PIX_SUBTRACT (PIX_DST & PIX_NOT(PIX_SRC)) /*!< subtract = */ + /*!< src & !dst */ +#define PIX_XOR (PIX_SRC ^ PIX_DST) /*!< xor = src ^ 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) We use 32 bit pixels for both RGB and RGBA color images.
+ *           The A (alpha) byte is ignored in most leptonica functions
+ *           operating on color images.  Within each 4 byte pixel, the
+ *           color samples are ordered from MSB to LSB, as follows:
+ *
+ *                |  MSB  |  2nd MSB  |  3rd MSB  |  LSB  |
+ *                   red      green       blue      alpha
+ *                    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)
+ *                 alpha:  byte 3  (LSB)
+ *
+ *           The specific color assignment is made in this file,
+ *           through the definitions of COLOR_RED, etc.  Then the R, G
+ *           B and A 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);
+ *                 alphaval = GET_DATA_BYTE(&pixel, L_ALPHA_CHANNEL);
+ *           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);
+ *                 SET_DATA_BYTE(&pixel, L_ALPHA_CHANNEL, alphaval);
+ *
+ *           More efficiently, these components can be extracted directly
+ *           by shifting and masking, explicitly using the values in
+ *           L_RED_SHIFT, etc.:
+ *                 (pixel32 >> L_RED_SHIFT) & 0xff;         (red)
+ *                 (pixel32 >> L_GREEN_SHIFT) & 0xff;       (green)
+ *                 (pixel32 >> L_BLUE_SHIFT) & 0xff;        (blue)
+ *                 (pixel32 >> L_ALPHA_SHIFT) & 0xff;       (alpha)
+ *           The functions extractRGBValues() and extractRGBAValues() are
+ *           provided to do this.  Likewise, the pixels can be set
+ *           directly by shifting, using composeRGBPixel() and
+ *           composeRGBAPixel().
+ *
+ *           All these operations work properly on both big- and little-endians.
+ *
+ *       (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 /*!< Version for Pixaa serialization */ +#define PIXA_VERSION_NUMBER 2 /*!< Version for Pixa serialization */ +#define BOXA_VERSION_NUMBER 2 /*!< Version for Boxa serialization */ +#define BOXAA_VERSION_NUMBER 3 /*!< Version for Boxaa serialization */ + +/*! Array of pix */ +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; + +/*! Array of arrays of pix */ +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 * + *-------------------------------------------------------------------------*/ + +/*! Basic rectangle */ +struct Box +{ + l_int32 x; /*!< left coordinate */ + l_int32 y; /*!< top coordinate */ + l_int32 w; /*!< box width */ + l_int32 h; /*!< box height */ + l_uint32 refcount; /*!< reference count (1 if no clones) */ +}; +typedef struct Box BOX; + +/*! Array of 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; + +/*! Array of 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 /*!< Version for Pta serialization */ + +/*! Array of points */ +struct Pta +{ + l_int32 n; /*!< actual number of pts */ + l_int32 nalloc; /*!< size of allocated arrays */ + l_uint32 refcount; /*!< reference count (1 if no clones) */ + l_float32 *x, *y; /*!< arrays of floats */ +}; +typedef struct Pta PTA; + + +/*-------------------------------------------------------------------------* + * Array of 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 * + *-------------------------------------------------------------------------*/ + +/*! 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 * + *-------------------------------------------------------------------------*/ + +/*! 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 2 /*!< Version for FPix serialization */ + +/*! Pix with float array */ +struct FPix +{ + l_int32 w; /*!< width in pixels */ + l_int32 h; /*!< height in pixels */ + l_int32 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_float32 *data; /*!< the float image data */ +}; +typedef struct FPix FPIX; + +/*! Array of FPix */ +struct FPixa +{ + l_int32 n; /*!< number of fpix in ptr array */ + l_int32 nalloc; /*!< number of fpix 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 2 /*!< Version for DPix serialization */ + +/*! Pix with double array */ +struct DPix +{ + l_int32 w; /*!< width in pixels */ + l_int32 h; /*!< height in pixels */ + l_int32 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_float64 *data; /*!< the double image data */ +}; +typedef struct DPix DPIX; + + +/*-------------------------------------------------------------------------* + * PixComp: compressed pix * + *-------------------------------------------------------------------------*/ + +/*! 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 2 /*!< Version for PixaComp serialization */ + +/*! Array of compressed pix */ +struct PixaComp +{ + l_int32 n; /*!< number of PixComp in ptr array */ + l_int32 nalloc; /*!< number of PixComp ptrs allocated */ + l_int32 offset; /*!< indexing offset into ptr array */ + 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.  OK to use except in two situations:
+ *                (a) You change data through one of the handles but don't
+ *                    want those changes to be seen by the other handle.
+ *                (b) The application is multi-threaded.  Because the clone
+ *                    operation is not atomic (e.g., locked with a mutex),
+ *                    it is possible to end up with an incorrect ref count,
+ *                    causing either a memory leak or a crash.
+ *
+ *  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., the
+ *  string is not owned by the handle).  You must not either free the string
+ *  or insert it in some other struct that would own it.  Specifically,
+ *  for an Sarray, the copyflag for retrieval is either:
+ *         L_COPY or L_NOCOPY
+ *  and for insertion, the copyflag is either:
+ *         L_COPY or one of {L_INSERT , L_NOCOPY} (the latter are equivalent
+ *                                                 for insertion))
+ *  Typical patterns are:
+ *  (1) Reference a string in an Sarray with L_NOCOPY and insert a copy
+ *      of it in another Sarray with L_COPY.
+ *  (2) Copy a string from an Sarray with L_COPY and insert it in
+ *      another Sarray with L_INSERT (or L_NOCOPY).
+ *  In both cases, a copy is made and both Sarrays own their instance
+ *  of that string.
+ * 
+ */ + +/*! Object Access */ +enum { + L_NOCOPY = 0, /*!< do not copy the object; do not delete the ptr */ + L_INSERT = L_NOCOPY, /*!< stuff it in; do not copy or 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 array object (e.g., pixa) and fill */ + /*!< the array with clones (e.g., pix) */ +}; + + +/*----------------------------------------------------------------------------* + * Sort flags * + *----------------------------------------------------------------------------*/ +/*! Sort Mode */ +enum { + L_SHELL_SORT = 1, /*!< use shell sort */ + L_BIN_SORT = 2 /*!< use bin sort */ +}; + +/*! Sort Order */ +enum { + L_SORT_INCREASING = 1, /*!< sort in increasing order */ + L_SORT_DECREASING = 2 /*!< sort in decreasing order */ +}; + +/*! Sort Type */ +enum { + L_SORT_BY_X = 1, /*!< sort box or c.c. by left edge location */ + L_SORT_BY_Y = 2, /*!< sort box or c.c. by top edge location */ + L_SORT_BY_RIGHT = 3, /*!< sort box or c.c. by right edge location */ + L_SORT_BY_BOT = 4, /*!< sort box or c.c. by bot edge 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 * + *---------------------------------------------------------------------------*/ +/*! Blend Types */ +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 */ +}; + +/*! Paint Selection */ +enum { + L_PAINT_LIGHT = 1, /*!< colorize non-black pixels */ + L_PAINT_DARK = 2 /*!< colorize non-white pixels */ +}; + + +/*-------------------------------------------------------------------------* + * Graphics pixel setting * + *-------------------------------------------------------------------------*/ +/*! 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 and location filter flags * + *-------------------------------------------------------------------------*/ +/*! Size Comparison */ +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 */ +}; + +/*! Size Selection */ +enum { + L_SELECT_BY_WIDTH = 1, /*!< select by width; 1 bpp */ + L_SELECT_BY_HEIGHT = 2, /*!< select by height; 1 bpp */ + L_SELECT_BY_MAX_DIMENSION = 3, /*!< select by max of width and */ + /*!< height; 1 bpp */ + L_SELECT_BY_AREA = 4, /*!< select by foreground area; 1 bpp */ + L_SELECT_BY_PERIMETER = 5 /*!< select by perimeter; 1 bpp */ +}; + +/*! Location Filter */ +enum { + L_SELECT_WIDTH = 1, /*!< width must satisfy constraint */ + L_SELECT_HEIGHT = 2, /*!< height must satisfy constraint */ + L_SELECT_XVAL = 3, /*!< x value must satisfy constraint */ + L_SELECT_YVAL = 4, /*!< y value must satisfy constraint */ + L_SELECT_IF_EITHER = 5, /*!< either width or height (or xval */ + /*!< or yval) can satisfy constraint */ + L_SELECT_IF_BOTH = 6 /*!< both width and height (or xval */ + /*!< and yval must satisfy constraint */ +}; + +/*! Boxa Check */ +enum { + L_CHECK_WIDTH = 1, /*!< check and possibly modify width */ + L_CHECK_HEIGHT = 2, /*!< check and possibly modify height */ + L_CHECK_BOTH = 3 /*!< check and possibly modify both */ +}; + + +/*-------------------------------------------------------------------------* + * Color component selection flags * + *-------------------------------------------------------------------------*/ +/*! Color Selection */ +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 */ + L_SELECT_AVERAGE = 6, /*!< use average of color components */ + L_SELECT_HUE = 7, /*!< use hue value (in HSV color space) */ + L_SELECT_SATURATION = 8 /*!< use saturation value (in HSV space) */ +}; + + +/*-------------------------------------------------------------------------* + * 16-bit conversion flags * + *-------------------------------------------------------------------------*/ +/*! 16-bit Conversion */ +enum { + L_LS_BYTE = 1, /*!< use LSB */ + L_MS_BYTE = 2, /*!< use MSB */ + L_AUTO_BYTE = 3, /*!< use LSB if max(val) < 256; else MSB */ + L_CLIP_TO_FF = 4, /*!< use max(val, 255) */ + L_LS_TWO_BYTES = 5, /*!< use two LSB */ + L_MS_TWO_BYTES = 6, /*!< use two MSB */ + L_CLIP_TO_FFFF = 7 /*!< use max(val, 65535) */ +}; + + +/*-------------------------------------------------------------------------* + * Rotate and shear flags * + *-------------------------------------------------------------------------*/ +/*! Rotation Type */ +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 */ +}; + +/*! Background Color */ +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 */ +}; + +/*! Shear Point */ +enum { + L_SHEAR_ABOUT_CORNER = 1, /*!< shear image about UL corner */ + L_SHEAR_ABOUT_CENTER = 2 /*!< shear image about center */ +}; + + +/*-------------------------------------------------------------------------* + * Affine transform order flags * + *-------------------------------------------------------------------------*/ +/*! Affine Transform Order */ +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 * + *-------------------------------------------------------------------------*/ +/*! Grayscale Fill */ +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 * + *-------------------------------------------------------------------------*/ +/*! BlackWhite Set */ +enum { + L_SET_WHITE = 1, /*!< set pixels to white */ + L_SET_BLACK = 2 /*!< set pixels to black */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for getting white or black value * + *-------------------------------------------------------------------------*/ +/*! BlackWhite Get */ +enum { + L_GET_WHITE_VAL = 1, /*!< get white pixel value */ + L_GET_BLACK_VAL = 2 /*!< get black pixel value */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for 8 bit and 16 bit pixel sums * + *-------------------------------------------------------------------------*/ +/*! BlackWhite Sum */ +enum { + L_WHITE_IS_MAX = 1, /*!< white pixels are 0xff or 0xffff; black are 0 */ + L_BLACK_IS_MAX = 2 /*!< black pixels are 0xff or 0xffff; white are 0 */ +}; + + +/*-------------------------------------------------------------------------* + * Dither parameters * + * If within this grayscale distance from black or white, * + * do not propagate excess or deficit to neighboring pixels. * + *-------------------------------------------------------------------------*/ +/*! Dither Distance */ +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 type flags * + *-------------------------------------------------------------------------*/ +/*! Distance Type */ +enum { + L_MANHATTAN_DISTANCE = 1, /*!< L1 distance (e.g., in color space) */ + L_EUCLIDEAN_DISTANCE = 2 /*!< L2 distance */ +}; + + +/*-------------------------------------------------------------------------* + * Distance Value flags * + *-------------------------------------------------------------------------*/ +/*! Distance Value */ +enum { + L_NEGATIVE = 1, /*!< values < 0 */ + L_NON_NEGATIVE = 2, /*!< values >= 0 */ + L_POSITIVE = 3, /*!< values > 0 */ + L_NON_POSITIVE = 4, /*!< values <= 0 */ + L_ZERO = 5, /*!< values = 0 */ + L_ALL = 6 /*!< all values */ +}; + + +/*-------------------------------------------------------------------------* + * Statistical measures * + *-------------------------------------------------------------------------*/ +/*! Stats Type */ +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 index selection flags * + *-------------------------------------------------------------------------*/ +/*! Index Selection */ +enum { + L_CHOOSE_CONSECUTIVE = 1, /*!< select 'n' consecutive */ + L_CHOOSE_SKIP_BY = 2 /*!< select at intervals of 'n' */ +}; + + +/*-------------------------------------------------------------------------* + * Text orientation flags * + *-------------------------------------------------------------------------*/ +/*! Text Orientation */ +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 * + *-------------------------------------------------------------------------*/ +/*! Edge Orientation */ +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 * + *-------------------------------------------------------------------------*/ +/*! Line Orientation */ +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 */ +}; + + +/*-------------------------------------------------------------------------* + * Image orientation flags * + *-------------------------------------------------------------------------*/ +/*! Image Orientation */ +enum { + L_PORTRAIT_MODE = 0, /*!< typical: page is viewed with height > width */ + L_LANDSCAPE_MODE = 1 /*!< page is viewed at 90 deg to portrait mode */ +}; + + +/*-------------------------------------------------------------------------* + * Scan direction flags * + *-------------------------------------------------------------------------*/ +/*! Scan Direction */ +enum { + L_FROM_LEFT = 0, /*!< scan from left */ + L_FROM_RIGHT = 1, /*!< scan from right */ + L_FROM_TOP = 2, /*!< scan from top */ + L_FROM_BOT = 3, /*!< scan from bottom */ + L_SCAN_NEGATIVE = 4, /*!< scan in negative direction */ + L_SCAN_POSITIVE = 5, /*!< scan in positive direction */ + L_SCAN_BOTH = 6, /*!< scan in both directions */ + L_SCAN_HORIZONTAL = 7, /*!< horizontal scan (direction unimportant) */ + L_SCAN_VERTICAL = 8 /*!< vertical scan (direction unimportant) */ +}; + + +/*-------------------------------------------------------------------------* + * Box size adjustment and location flags * + *-------------------------------------------------------------------------*/ +/*! Box Adjustment */ +enum { + L_ADJUST_SKIP = 0, /*!< do not adjust */ + L_ADJUST_LEFT = 1, /*!< adjust left edge */ + L_ADJUST_RIGHT = 2, /*!< adjust right edge */ + L_ADJUST_LEFT_AND_RIGHT = 3, /*!< adjust both left and right edges */ + L_ADJUST_TOP = 4, /*!< adjust top edge */ + L_ADJUST_BOT = 5, /*!< adjust bottom edge */ + L_ADJUST_TOP_AND_BOT = 6, /*!< adjust both top and bottom edges */ + L_ADJUST_CHOOSE_MIN = 7, /*!< choose the min median value */ + L_ADJUST_CHOOSE_MAX = 8, /*!< choose the max median value */ + L_SET_LEFT = 9, /*!< set left side to a given value */ + L_SET_RIGHT = 10, /*!< set right side to a given value */ + L_SET_TOP = 11, /*!< set top side to a given value */ + L_SET_BOT = 12, /*!< set bottom side to a given value */ + L_GET_LEFT = 13, /*!< get left side location */ + L_GET_RIGHT = 14, /*!< get right side location */ + L_GET_TOP = 15, /*!< get top side location */ + L_GET_BOT = 16 /*!< get bottom side location */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for modifying box boundaries using a second box * + *-------------------------------------------------------------------------*/ +/*! Box Boundary Mod */ +enum { + L_USE_MINSIZE = 1, /*!< use boundaries giving min size */ + L_USE_MAXSIZE = 2, /*!< use boundaries giving max size */ + L_SUB_ON_LOC_DIFF = 3, /*!< modify boundary if big location diff */ + L_SUB_ON_SIZE_DIFF = 4, /*!< modify boundary if big size diff */ + L_USE_CAPPED_MIN = 5, /*!< modify boundary with capped min */ + L_USE_CAPPED_MAX = 6 /*!< modify boundary with capped max */ +}; + + +/*-------------------------------------------------------------------------* + * Handling overlapping bounding boxes in boxa * + *-------------------------------------------------------------------------*/ +/*! Box Overlap Mod */ +enum { + L_COMBINE = 1, /*!< resize to bounding region; remove smaller */ + L_REMOVE_SMALL = 2 /*!< only remove smaller */ +}; + + +/*-------------------------------------------------------------------------* + * Selecting or making a box from two (intersecting) boxes * + *-------------------------------------------------------------------------*/ +/*! Box Combine or Select */ +enum { + L_GEOMETRIC_UNION = 1, /*!< use union of two boxes */ + L_GEOMETRIC_INTERSECTION = 2, /*!< use intersection of two boxes */ + L_LARGEST_AREA = 3, /*!< use box with largest area */ + L_SMALLEST_AREA = 4 /*!< use box with smallest area */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for replacing invalid boxes * + *-------------------------------------------------------------------------*/ +/*! Box Replacement */ +enum { + L_USE_ALL_BOXES = 1, /*!< consider all boxes in the sequence */ + L_USE_SAME_PARITY_BOXES = 2 /*!< consider boxes with the same parity */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for box corners * + *-------------------------------------------------------------------------*/ +/*! Box Corners */ +enum { + L_UPPER_LEFT = 1, /*!< UL corner */ + L_UPPER_RIGHT = 2, /*!< UR corner */ + L_LOWER_LEFT = 3, /*!< LL corner */ + L_LOWER_RIGHT = 4 /*!< LR corner */ +}; + + +/*-------------------------------------------------------------------------* + * Horizontal warp * + *-------------------------------------------------------------------------*/ +/*! Horiz Warp Stretch */ +enum { + L_WARP_TO_LEFT = 1, /*!< increasing stretch or contraction to left */ + L_WARP_TO_RIGHT = 2 /*!< increasing stretch or contraction to right */ +}; + +/*! Horiz Warp Mode */ +enum { + L_LINEAR_WARP = 1, /*!< stretch or contraction grows linearly */ + L_QUADRATIC_WARP = 2 /*!< stretch or contraction grows quadratically */ +}; + + +/*-------------------------------------------------------------------------* + * Pixel selection for resampling * + *-------------------------------------------------------------------------*/ +/*! Pixel Selection */ +enum { + L_INTERPOLATED = 1, /*!< linear interpolation from src pixels */ + L_SAMPLED = 2 /*!< nearest src pixel sampling only */ +}; + + +/*-------------------------------------------------------------------------* + * Thinning flags * + *-------------------------------------------------------------------------*/ +/*! Thinning Polarity */ +enum { + L_THIN_FG = 1, /*!< thin foreground of 1 bpp image */ + L_THIN_BG = 2 /*!< thin background of 1 bpp image */ +}; + + +/*-------------------------------------------------------------------------* + * Runlength flags * + *-------------------------------------------------------------------------*/ +/*! Runlength Direction */ +enum { + L_HORIZONTAL_RUNS = 0, /*!< determine runlengths of horizontal runs */ + L_VERTICAL_RUNS = 1 /*!< determine runlengths of vertical runs */ +}; + + +/*-------------------------------------------------------------------------* + * Edge filter flags * + *-------------------------------------------------------------------------*/ +/*! Edge Filter */ +enum { + L_SOBEL_EDGE = 1, /*!< Sobel edge filter */ + L_TWO_SIDED_EDGE = 2 /*!< Two-sided edge filter */ +}; + + +/*-------------------------------------------------------------------------* + * Subpixel color component ordering in LCD display * + *-------------------------------------------------------------------------*/ +/*! Subpixel Color Order */ +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 */ +}; + + +/*-------------------------------------------------------------------------* + * HSV histogram flags * + *-------------------------------------------------------------------------*/ +/*! HSV Histogram */ +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 */ +}; + + +/*-------------------------------------------------------------------------* + * HSV Region flags (inclusion, exclusion) * + *-------------------------------------------------------------------------*/ +/*! HSV Region */ +enum { + L_INCLUDE_REGION = 1, /*!< Use pixels with specified HSV region */ + L_EXCLUDE_REGION = 2 /*!< Use pixels outside HSV region */ +}; + + +/*-------------------------------------------------------------------------* + * Location flags for adding text to a pix * + *-------------------------------------------------------------------------*/ +/*! Add Text Location */ +enum { + L_ADD_ABOVE = 1, /*!< Add text above the image */ + L_ADD_BELOW = 2, /*!< Add text below the image */ + L_ADD_LEFT = 3, /*!< Add text to the left of the image */ + L_ADD_RIGHT = 4, /*!< Add text to the right of the image */ + L_ADD_AT_TOP = 5, /*!< Add text over the top of the image */ + L_ADD_AT_BOT = 6, /*!< Add text over the bottom of the image */ + L_ADD_AT_LEFT = 7, /*!< Add text over left side of the image */ + L_ADD_AT_RIGHT = 8 /*!< Add text over right side of the image */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for plotting on a pix * + *-------------------------------------------------------------------------*/ +/*! Pix Plot */ +enum { + L_PLOT_AT_TOP = 1, /*!< Plot horizontally at top */ + L_PLOT_AT_MID_HORIZ = 2, /*!< Plot horizontally at middle */ + L_PLOT_AT_BOT = 3, /*!< Plot horizontally at bottom */ + L_PLOT_AT_LEFT = 4, /*!< Plot vertically at left */ + L_PLOT_AT_MID_VERT = 5, /*!< Plot vertically at middle */ + L_PLOT_AT_RIGHT = 6 /*!< Plot vertically at right */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for making simple masks * + *-------------------------------------------------------------------------*/ +/*! Mask Generation */ +enum { + L_USE_INNER = 1, /*!< Select the interior part */ + L_USE_OUTER = 2 /*!< Select the outer part (e.g., a frame) */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for selecting display program * + *-------------------------------------------------------------------------*/ +/*! Display Program */ +enum { + L_DISPLAY_WITH_XZGV = 1, /*!< Use xzgv with pixDisplay() */ + L_DISPLAY_WITH_XLI = 2, /*!< Use xli with pixDisplay() */ + L_DISPLAY_WITH_XV = 3, /*!< Use xv with pixDisplay() */ + L_DISPLAY_WITH_IV = 4, /*!< Use irfvanview (win) with pixDisplay() */ + L_DISPLAY_WITH_OPEN = 5 /*!< Use open (apple) with pixDisplay() */ +}; + +/*-------------------------------------------------------------------------* + * Flag(s) used in the 'special' pix field for non-default operations * + * - 0 is default for chroma sampling in jpeg * + * - 10-19 are used for zlib compression in png write * + * - 4 and 8 are used for specifying connectivity in labelling * + *-------------------------------------------------------------------------*/ +/*! Flags used in Pix::special */ +enum { + L_NO_CHROMA_SAMPLING_JPEG = 1 /*!< Write full resolution chroma */ +}; + + +/*-------------------------------------------------------------------------* + * Handling negative values in conversion to unsigned int * + *-------------------------------------------------------------------------*/ +/*! Negative Value */ +enum { + L_CLIP_TO_ZERO = 1, /*!< Clip negative values to 0 */ + L_TAKE_ABSVAL = 2 /*!< Convert to positive using L_ABS() */ +}; + + +/*-------------------------------------------------------------------------* + * Relative to zero flags * + *-------------------------------------------------------------------------*/ +/*! Relative To Zero */ +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 */ +}; + + +/*-------------------------------------------------------------------------* + * Flags for adding or removing trailing slash from string * + *-------------------------------------------------------------------------*/ +/*! Trailing Slash */ +enum { + L_ADD_TRAIL_SLASH = 1, /*!< Add trailing slash to string */ + L_REMOVE_TRAIL_SLASH = 2 /*!< Remove trailing slash from string */ +}; + + +/*-------------------------------------------------------------------------* + * Pix allocator and deallocator function types * + *-------------------------------------------------------------------------*/ +/*! Allocator function type */ +typedef void *(*alloc_fn)(size_t); + +/*! Deallocator function type */ +typedef void (*dealloc_fn)(void *); + + +#endif /* LEPTONICA_PIX_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix1.c b/hgdriver/3rdparty/hgOCR/leptonica/pix1.c new file mode 100644 index 0000000..ca4fe9b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix1.c @@ -0,0 +1,1877 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pix1.c + *
+ *
+ *    The pixN.c {N = 1,2,3,4,5} files are sorted by the type of operation.
+ *    The primary functions in these files are:
+ *
+ *        pix1.c: constructors, destructors and field accessors
+ *        pix2.c: pixel poking of image, pad and border pixels
+ *        pix3.c: masking and logical ops, counting, mirrored tiling
+ *        pix4.c: histograms, statistics, fg/bg estimation
+ *        pix5.c: property measurements, rectangle extraction
+ *
+ *
+ *    This file has the basic constructors, destructors and field accessors
+ *
+ *    Pix memory management (allows custom allocator and deallocator)
+ *          static void  *pix_malloc()
+ *          static void   pix_free()
+ *          void          setPixMemoryManager()
+ *
+ *    Pix creation
+ *          PIX          *pixCreate()
+ *          PIX          *pixCreateNoInit()
+ *          PIX          *pixCreateTemplate()
+ *          PIX          *pixCreateTemplateNoInit()
+ *          PIX          *pixCreateHeader()
+ *          PIX          *pixClone()
+ *
+ *    Pix destruction
+ *          void          pixDestroy()
+ *          static void   pixFree()
+ *
+ *    Pix copy
+ *          PIX          *pixCopy()
+ *          l_int32       pixResizeImageData()
+ *          l_int32       pixCopyColormap()
+ *          l_int32       pixSizesEqual()
+ *          l_int32       pixTransferAllData()
+ *          l_int32       pixSwapAndDestroy()
+ *
+ *    Pix accessors
+ *          l_int32       pixGetWidth()
+ *          l_int32       pixSetWidth()
+ *          l_int32       pixGetHeight()
+ *          l_int32       pixSetHeight()
+ *          l_int32       pixGetDepth()
+ *          l_int32       pixSetDepth()
+ *          l_int32       pixGetDimensions()
+ *          l_int32       pixSetDimensions()
+ *          l_int32       pixCopyDimensions()
+ *          l_int32       pixGetSpp()
+ *          l_int32       pixSetSpp()
+ *          l_int32       pixCopySpp()
+ *          l_int32       pixGetWpl()
+ *          l_int32       pixSetWpl()
+ *          l_int32       pixGetRefcount()
+ *          l_int32       pixChangeRefcount()
+ *          l_uint32      pixGetXRes()
+ *          l_int32       pixSetXRes()
+ *          l_uint32      pixGetYRes()
+ *          l_int32       pixSetYRes()
+ *          l_int32       pixGetResolution()
+ *          l_int32       pixSetResolution()
+ *          l_int32       pixCopyResolution()
+ *          l_int32       pixScaleResolution()
+ *          l_int32       pixGetInputFormat()
+ *          l_int32       pixSetInputFormat()
+ *          l_int32       pixCopyInputFormat()
+ *          l_int32       pixSetSpecial()
+ *          char         *pixGetText()
+ *          l_int32       pixSetText()
+ *          l_int32       pixAddText()
+ *          l_int32       pixCopyText()
+ *          PIXCMAP      *pixGetColormap()
+ *          l_int32       pixSetColormap()
+ *          l_int32       pixDestroyColormap()
+ *          l_uint32     *pixGetData()
+ *          l_int32       pixSetData()
+ *          l_uint32     *pixExtractData()
+ *          l_int32       pixFreeData()
+ *
+ *    Pix line ptrs
+ *          void        **pixGetLinePtrs()
+ *
+ *    Pix debug
+ *          l_int32       pixPrintStreamInfo()
+ *
+ *
+ *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *      Important notes on direct management of pix image data
+ *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ *  Custom allocator and deallocator
+ *  --------------------------------
+ *
+ *  At the lowest level, you can specify the function that does the
+ *  allocation and deallocation of the data field in the pix.
+ *  By default, this is malloc and free.  However, by calling
+ *  setPixMemoryManager(), custom functions can be substituted.
+ *  When using this, keep two things in mind:
+ *
+ *   (1) Call setPixMemoryManager() before any pix have been allocated
+ *   (2) Destroy all pix as usual, in order to prevent leaks.
+ *
+ *  In pixalloc.c, we provide an example custom allocator and deallocator.
+ *  To use it, you must call pmsCreate() before any pix have been allocated
+ *  and pmsDestroy() at the end after all pix have been destroyed.
+ *
+ *
+ *  Direct manipulation of the pix data field
+ *  -----------------------------------------
+ *
+ *  Memory management of the (image) data field in the pix is
+ *  handled differently from that in the colormap or text fields.
+ *  For colormap and text, the functions pixSetColormap() and
+ *  pixSetText() remove the existing heap data and insert the
+ *  new data.  For the image data, pixSetData() just reassigns the
+ *  data field; any existing data will be lost if there isn't
+ *  another handle for it.
+ *
+ *  Why is pixSetData() limited in this way?  Because the image
+ *  data can be very large, we need flexible ways to handle it,
+ *  particularly when you want to re-use the data in a different
+ *  context without making a copy.  Here are some different
+ *  things you might want to do:
+ *
+ *  (1) Use pixCopy(pixd, pixs) where pixd is not the same size
+ *      as pixs.  This will remove the data in pixd, allocate a
+ *      new data field in pixd, and copy the data from pixs, leaving
+ *      pixs unchanged.
+ *
+ *  (2) Use pixTransferAllData(pixd, &pixs, ...) to transfer the
+ *      data from pixs to pixd without making a copy of it.  If
+ *      pixs is not cloned, this will do the transfer and destroy pixs.
+ *      But if the refcount of pixs is greater than 1, it just copies
+ *      the data and decrements the ref count.
+ *
+ *  (3) Use pixSwapAndDestroy(pixd, &pixs) to replace pixs by an
+ *      existing pixd.  This is similar to pixTransferAllData(), but
+ *      simpler, in that it never makes any copies and if pixs is
+ *      cloned, the other references are not changed by this operation.
+ *
+ *  (4) Use pixExtractData() to extract the image data from the pix
+ *      without copying if possible.  This could be used, for example,
+ *      to convert from a pix to some other data structure with minimal
+ *      heap allocation.  After the data is extracated, the pixels can
+ *      be munged and used in another context.  However, the danger
+ *      here is that the pix might have a refcount > 1, in which case
+ *      a copy of the data must be made and the input pix left unchanged.
+ *      If there are no clones, the image data can be extracted without
+ *      a copy, and the data ptr in the pix must be nulled before
+ *      destroying it because the pix will no longer 'own' the data.
+ *
+ *  We have provided accessors and functions here that should be
+ *  sufficient so that you can do anything you want without
+ *  explicitly referencing any of the pix member fields.
+ *
+ *  However, to avoid memory smashes and leaks when doing special operations
+ *  on the pix data field, look carefully at the behavior of the image
+ *  data accessors and keep in mind that when you invoke pixDestroy(),
+ *  the pix considers itself the owner of all its heap data.
+ * 
+ */ + +#include +#include "allheaders.h" + +static void pixFree(PIX *pix); + + +/*-------------------------------------------------------------------------* + * Pix Memory Management * + * * + * These functions give you the freedom to specify at compile or run * + * time the allocator and deallocator to be used for pix. It has no * + * effect on memory management for other data structs, which are * + * controlled by the #defines in environ.h. Likewise, the #defines * + * in environ.h have no effect on the pix memory management. * + * The default functions are malloc and free. Use setPixMemoryManager() * + * to specify other functions to use. * + *-------------------------------------------------------------------------*/ + +/*! Pix memory manager */ + /* + *
+     * Notes:
+     *      (1) The allocator and deallocator function types,
+     *          alloc_fn and dealloc_fn, are defined in pix.h.
+     * 
+ */ +struct PixMemoryManager +{ + alloc_fn allocator; + dealloc_fn deallocator; +}; + +/*! Default Pix memory manager */ +static struct PixMemoryManager pix_mem_manager = { + &malloc, + &free +}; + +static void * +pix_malloc(size_t size) +{ +#ifndef _MSC_VER + return (*pix_mem_manager.allocator)(size); +#else /* _MSC_VER */ + /* Under MSVC++, pix_mem_manager is initialized after a call + * to pix_malloc. Just ignore the custom allocator feature. */ + return malloc(size); +#endif /* _MSC_VER */ +} + +static void +pix_free(void *ptr) +{ +#ifndef _MSC_VER + (*pix_mem_manager.deallocator)(ptr); + return; +#else /* _MSC_VER */ + /* Under MSVC++, pix_mem_manager is initialized after a call + * to pix_malloc. Just ignore the custom allocator feature. */ + free(ptr); + return; +#endif /* _MSC_VER */ +} + +/*! + * \brief setPixMemoryManager() + * + * \param[in] allocator [optional] use NULL to skip + * \param[in] deallocator [optional] use NULL to skip + * \return void + * + *
+ * Notes:
+ *      (1) Use this to change the alloc and/or dealloc functions;
+ *          e.g., setPixMemoryManager(my_malloc, my_free).
+ *      (2) The C99 standard (section 6.7.5.3, par. 8) says:
+ *            A declaration of a parameter as "function returning type"
+ *            shall be adjusted to "pointer to function returning type"
+ *          so that it can be in either of these two forms:
+ *            (a) type (function-ptr(type, ...))
+ *            (b) type ((*function-ptr)(type, ...))
+ *          because form (a) is implictly converted to form (b), as in the
+ *          definition of struct PixMemoryManager above.  So, for example,
+ *          we should be able to declare either of these:
+ *            (a) void *(allocator(size_t))
+ *            (b) void *((*allocator)(size_t))
+ *          However, MSVC++ only accepts the second version.
+ * 
+ */ +void +setPixMemoryManager(alloc_fn allocator, + dealloc_fn deallocator) +{ + if (allocator) pix_mem_manager.allocator = allocator; + if (deallocator) pix_mem_manager.deallocator = deallocator; + return; +} + + +/*--------------------------------------------------------------------* + * Pix Creation * + *--------------------------------------------------------------------*/ +/*! + * \brief pixCreate() + * + * \param[in] width, height, depth + * \return pixd with data allocated and initialized to 0, + * or NULL on error + */ +PIX * +pixCreate(l_int32 width, + l_int32 height, + l_int32 depth) +{ +PIX *pixd; + + PROCNAME("pixCreate"); + + if ((pixd = pixCreateNoInit(width, height, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); + return pixd; +} + + +/*! + * \brief pixCreateNoInit() + * + * \param[in] width, height, depth + * \return pixd with data allocated but not initialized, + * or NULL on error + * + *
+ * Notes:
+ *      (1) Must set pad bits to avoid reading uninitialized data, because
+ *          some optimized routines (e.g., pixConnComp()) read from pad bits.
+ * 
+ */ +PIX * +pixCreateNoInit(l_int32 width, + l_int32 height, + l_int32 depth) +{ +l_int32 wpl; +PIX *pixd; +l_uint32 *data; + + PROCNAME("pixCreateNoInit"); + if ((pixd = pixCreateHeader(width, height, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + wpl = pixGetWpl(pixd); + if ((data = (l_uint32 *)pix_malloc(4LL * wpl * height)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pix_malloc fail for data", procName, NULL); + } + pixSetData(pixd, data); + pixSetPadBits(pixd, 0); + return pixd; +} + + +/*! + * \brief pixCreateTemplate() + * + * \param[in] pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a Pix of the same size as the input Pix, with the
+ *          data array allocated and initialized to 0.
+ *      (2) Copies the other fields, including colormap if it exists.
+ * 
+ */ +PIX * +pixCreateTemplate(const PIX *pixs) +{ +PIX *pixd; + + PROCNAME("pixCreateTemplate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); + return pixd; +} + + +/*! + * \brief pixCreateTemplateNoInit() + * + * \param[in] pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Makes a Pix of the same size as the input Pix, with
+ *          the data array allocated but not initialized to 0.
+ *      (2) Copies the other fields, including colormap if it exists.
+ * 
+ */ +PIX * +pixCreateTemplateNoInit(const PIX *pixs) +{ +l_int32 w, h, d; +PIX *pixd; + + PROCNAME("pixCreateTemplateNoInit"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &d); + if ((pixd = pixCreateNoInit(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopySpp(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixCreateHeader() + * + * \param[in] width, height, depth + * \return pixd with no data allocated, or NULL on error + * + *
+ * Notes:
+ *      (1) It is assumed that all 32 bit pix have 3 spp.  If there is
+ *          a valid alpha channel, this will be set to 4 spp later.
+ *      (2) All pixCreate*() functions call pixCreateHeader().
+            If the number of bytes to be allocated is larger than the
+ *          maximum value in an int32, we can get overflow, resulting
+ *          in a smaller amount of memory actually being allocated.
+ *          Later, an attempt to access memory that wasn't allocated will
+ *          cause a crash.  So to avoid crashing a program (or worse)
+ *          with bad (or malicious) input, we limit the requested
+ *          allocation of image data in a typesafe way.
+ * 
+ */ +PIX * +pixCreateHeader(l_int32 width, + l_int32 height, + l_int32 depth) +{ +l_int32 wpl; +l_uint64 wpl64, bignum; +PIX *pixd; + + PROCNAME("pixCreateHeader"); + + if ((depth != 1) && (depth != 2) && (depth != 4) && (depth != 8) + && (depth != 16) && (depth != 24) && (depth != 32)) + return (PIX *)ERROR_PTR("depth must be {1, 2, 4, 8, 16, 24, 32}", + procName, NULL); + if (width <= 0) + return (PIX *)ERROR_PTR("width must be > 0", procName, NULL); + if (height <= 0) + return (PIX *)ERROR_PTR("height must be > 0", procName, NULL); + + /* Avoid overflow in malloc, malicious or otherwise */ + wpl64 = ((l_uint64)width * (l_uint64)depth + 31) / 32; + if (wpl64 > ((1LL << 29) - 1)) { + L_ERROR("requested w = %d, h = %d, d = %d\n", + procName, width, height, depth); + return (PIX *)ERROR_PTR("wpl >= 2^29", procName, NULL); + } + wpl = (l_int32)wpl64; + bignum = 4LL * wpl * height; /* number of bytes to be requested */ + if (bignum > ((1LL << 31) - 1)) { + L_ERROR("requested w = %d, h = %d, d = %d\n", + procName, width, height, depth); + return (PIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); + } + + pixd = (PIX *)LEPT_CALLOC(1, sizeof(PIX)); + pixSetWidth(pixd, width); + pixSetHeight(pixd, height); + pixSetDepth(pixd, depth); + pixSetWpl(pixd, wpl); + if (depth == 24 || depth == 32) + pixSetSpp(pixd, 3); + else + pixSetSpp(pixd, 1); + pixd->refcount = 1; + pixd->informat = IFF_UNKNOWN; + return pixd; +} + + +/*! + * \brief pixClone() + * + * \param[in] pixs + * \return same pix ptr, or NULL on error + * + *
+ * Notes:
+ *      (1) A "clone" is simply a handle (ptr) to an existing pix.
+ *          It is implemented because (a) images can be large and
+ *          hence expensive to copy, and (b) extra handles to a data
+ *          structure need to be made with a simple policy to avoid
+ *          both double frees and memory leaks.  Pix are reference
+ *          counted.  The side effect of pixClone() is an increase
+ *          by 1 in the ref count.
+ *      (2) The protocol to be used is:
+ *          (a) Whenever you want a new handle to an existing image,
+ *              call pixClone(), which just bumps a ref count.
+ *          (b) Always call pixDestroy() on all handles.  This
+ *              decrements the ref count, nulls the handle, and
+ *              only destroys the pix when pixDestroy() has been
+ *              called on all handles.
+ * 
+ */ +PIX * +pixClone(PIX *pixs) +{ + PROCNAME("pixClone"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixChangeRefcount(pixs, 1); + + return pixs; +} + + +/*--------------------------------------------------------------------* + * Pix Destruction * + *--------------------------------------------------------------------*/ +/*! + * \brief pixDestroy() + * + * \param[in,out] ppix will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pix.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +pixDestroy(PIX **ppix) +{ +PIX *pix; + + PROCNAME("pixDestroy"); + + if (!ppix) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((pix = *ppix) == NULL) + return; + pixFree(pix); + *ppix = NULL; + return; +} + + +/*! + * \brief pixFree() + * + * \param[in] pix + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pix.
+ * 
+ */ +static void +pixFree(PIX *pix) +{ +l_uint32 *data; +char *text; + + if (!pix) return; + + pixChangeRefcount(pix, -1); + if (pixGetRefcount(pix) <= 0) { + if ((data = pixGetData(pix)) != NULL) + pix_free(data); + if ((text = pixGetText(pix)) != NULL) + LEPT_FREE(text); + pixDestroyColormap(pix); + LEPT_FREE(pix); + } + return; +} + + +/*-------------------------------------------------------------------------* + * Pix Copy * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixCopy() + * + * \param[in] pixd [optional] can be null, equal to pixs, + * different from pixs + * \param[in] pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) There are three cases:
+ *            (a) pixd == null  (makes a new pix; refcount = 1)
+ *            (b) pixd == pixs  (no-op)
+ *            (c) pixd != pixs  (data copy; no change in refcount)
+ *          If the refcount of pixd > 1, case (c) will side-effect
+ *          these handles.
+ *      (2) The general pattern of use is:
+ *             pixd = pixCopy(pixd, pixs);
+ *          This will work for all three cases.
+ *          For clarity when the case is known, you can use:
+ *            (a) pixd = pixCopy(NULL, pixs);
+ *            (c) pixCopy(pixd, pixs);
+ *      (3) For case (c), we check if pixs and pixd are the same
+ *          size (w,h,d).  If so, the data is copied directly.
+ *          Otherwise, the data is reallocated to the correct size
+ *          and the copy proceeds.  The refcount of pixd is unchanged.
+ *      (4) This operation, like all others that may involve a pre-existing
+ *          pixd, will side-effect any existing clones of pixd.
+ * 
+ */ +PIX * +pixCopy(PIX *pixd, /* can be null */ + const PIX *pixs) +{ +l_int32 bytes; + + PROCNAME("pixCopy"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixs == pixd) + return pixd; + + /* Total bytes in image data */ + bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); + + /* If we're making a new pix ... */ + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + memcpy(pixd->data, pixs->data, bytes); + return pixd; + } + + /* Reallocate image data if sizes are different. If this fails, + * pixd hasn't been changed. But we want to signal that the copy + * failed, so return NULL. This will cause a memory leak if the + * return ptr is assigned to pixd, but that is preferred to proceeding + * with an incorrect pixd, and in any event this use case of + * pixCopy() -- reallocating into an existing pix -- is infrequent. */ + if (pixResizeImageData(pixd, pixs) == 1) + return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL); + + /* Copy non-image data fields */ + pixCopyColormap(pixd, pixs); + pixCopySpp(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixCopyText(pixd, pixs); + + /* Copy image data */ + memcpy(pixd->data, pixs->data, bytes); + return pixd; +} + + +/*! + * \brief pixResizeImageData() + * + * \param[in] pixd gets new uninitialized buffer for image data + * \param[in] pixs determines the size of the buffer; not changed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the sizes of data in pixs and pixd are unequal, this
+ *          frees the existing image data in pixd and allocates
+ *          an uninitialized buffer that will hold the required amount
+ *          of image data in pixs.  The image data from pixs is not
+ *          copied into the new buffer.
+ *      (2) On failure to allocate, pixd is unchanged.
+ * 
+ */ +l_ok +pixResizeImageData(PIX *pixd, + const PIX *pixs) +{ +l_int32 w, h, d, wpl, bytes; +l_uint32 *data; + + PROCNAME("pixResizeImageData"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + + if (pixSizesEqual(pixs, pixd)) /* nothing to do */ + return 0; + + /* Make sure we can copy the data */ + pixGetDimensions(pixs, &w, &h, &d); + wpl = pixGetWpl(pixs); + bytes = 4 * wpl * h; + if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) + return ERROR_INT("pix_malloc fail for data", procName, 1); + + /* OK, do it */ + pixSetWidth(pixd, w); + pixSetHeight(pixd, h); + pixSetDepth(pixd, d); + pixSetWpl(pixd, wpl); + pixFreeData(pixd); /* free any existing image data */ + pixSetData(pixd, data); /* set the uninitialized memory buffer */ + pixCopyResolution(pixd, pixs); + return 0; +} + + +/*! + * \brief pixCopyColormap() + * + * \param[in] pixd + * \param[in] pixs copies the colormap to %pixd + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys the colormap in pixd, unless the operation is a no-op
+ * 
+ */ +l_ok +pixCopyColormap(PIX *pixd, + const PIX *pixs) +{ +l_int32 valid; +const PIXCMAP *cmaps; +PIXCMAP *cmapd; + + PROCNAME("pixCopyColormap"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixDestroyColormap(pixd); + if ((cmaps = pixs->colormap) == NULL) /* not an error */ + return 0; + pixcmapIsValid(cmaps, &valid); + if (!valid) + return ERROR_INT("cmap not valid", procName, 1); + + if ((cmapd = pixcmapCopy(cmaps)) == NULL) + return ERROR_INT("cmapd not made", procName, 1); + pixSetColormap(pixd, cmapd); + return 0; +} + + +/*! + * \brief pixSizesEqual() + * + * \param[in] pix1, pix2 + * \return 1 if the two pix have same {h, w, d}; 0 otherwise. + */ +l_int32 +pixSizesEqual(const PIX *pix1, + const PIX *pix2) +{ + PROCNAME("pixSizesEqual"); + + if (!pix1 || !pix2) + return ERROR_INT("pix1 and pix2 not both defined", procName, 0); + + if (pix1 == pix2) + return 1; + + if ((pixGetWidth(pix1) != pixGetWidth(pix2)) || + (pixGetHeight(pix1) != pixGetHeight(pix2)) || + (pixGetDepth(pix1) != pixGetDepth(pix2))) + return 0; + else + return 1; +} + + +/*! + * \brief pixTransferAllData() + * + * \param[in] pixd must be different from pixs + * \param[in,out] ppixs will be nulled if refcount goes to 0 + * \param[in] copytext 1 to copy the text field; 0 to skip + * \param[in] copyformat 1 to copy the informat field; 0 to skip + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a complete data transfer from pixs to pixd,
+ *          followed by the destruction of pixs (refcount permitting).
+ *      (2) If the refcount of pixs is 1, pixs is destroyed.  Otherwise,
+ *          the data in pixs is copied (rather than transferred) to pixd.
+ *      (3) This operation, like all others with a pre-existing pixd,
+ *          will side-effect any existing clones of pixd.  The pixd
+ *          refcount does not change.
+ *      (4) When might you use this?  Suppose you have an in-place Pix
+ *          function (returning void) with the typical signature:
+ *              void function-inplace(PIX *pix, ...)
+ *          where "..." are non-pointer input parameters, and suppose
+ *          further that you sometimes want to return an arbitrary Pix
+ *          in place of the input Pix.  There are two ways you can do this:
+ *          (a) The straightforward way is to change the function
+ *              signature to take the address of the Pix ptr:
+ * \code
+ *                  void function-inplace(PIX **ppix, ...) {
+ *                      PIX *pixt = function-makenew(*ppix);
+ *                      pixDestroy(ppix);
+ *                      *ppix = pixt;
+ *                      return;
+ *                  }
+ * \endcode
+ *              Here, the input and returned pix are different, as viewed
+ *              by the calling function, and the inplace function is
+ *              expected to destroy the input pix to avoid a memory leak.
+ *          (b) Keep the signature the same and use pixTransferAllData()
+ *              to return the new Pix in the input Pix struct:
+ * \code
+ *                  void function-inplace(PIX *pix, ...) {
+ *                      PIX *pixt = function-makenew(pix);
+ *                      pixTransferAllData(pix, &pixt, 0, 0);
+ *                               // pixDestroy() is called on pixt
+ *                      return;
+ *                  }
+ * \endcode
+ *              Here, the input and returned pix are the same, as viewed
+ *              by the calling function, and the inplace function must
+ *              never destroy the input pix, because the calling function
+ *              maintains an unchanged handle to it.
+ * 
+ */ +l_ok +pixTransferAllData(PIX *pixd, + PIX **ppixs, + l_int32 copytext, + l_int32 copyformat) +{ +l_int32 nbytes; +PIX *pixs; + + PROCNAME("pixTransferAllData"); + + if (!ppixs) + return ERROR_INT("&pixs not defined", procName, 1); + if ((pixs = *ppixs) == NULL) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixs == pixd) /* no-op */ + return ERROR_INT("pixd == pixs", procName, 1); + + if (pixGetRefcount(pixs) == 1) { /* transfer the data, cmap, text */ + pixFreeData(pixd); /* dealloc any existing data */ + pixSetData(pixd, pixGetData(pixs)); /* transfer new data from pixs */ + pixs->data = NULL; /* pixs no longer owns data */ + pixSetColormap(pixd, pixGetColormap(pixs)); /* frees old; sets new */ + pixs->colormap = NULL; /* pixs no longer owns colormap */ + if (copytext) { + pixSetText(pixd, pixGetText(pixs)); + pixSetText(pixs, NULL); + } + } else { /* preserve pixs by making a copy of the data, cmap, text */ + pixResizeImageData(pixd, pixs); + nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); + memcpy(pixGetData(pixd), pixGetData(pixs), nbytes); + pixCopyColormap(pixd, pixs); + if (copytext) + pixCopyText(pixd, pixs); + } + + pixCopySpp(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyDimensions(pixd, pixs); + if (copyformat) + pixCopyInputFormat(pixd, pixs); + + /* This will destroy pixs if data was transferred; + * otherwise, it just decrements its refcount. */ + pixDestroy(ppixs); + return 0; +} + + +/*! + * \brief pixSwapAndDestroy() + * + * \param[out] ppixd [optional] input pixd can be null, + * and it must be different from pixs + * \param[in,out] ppixs will be nulled after the swap + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simple operation to change the handle name safely.
+ *          After this operation, the original image in pixd has
+ *          been destroyed, pixd points to what was pixs, and
+ *          the input pixs ptr has been nulled.
+ *      (2) This works safely whether or not pixs and pixd are cloned.
+ *          If pixs is cloned, the other handles still point to
+ *          the original image, with the ref count reduced by 1.
+ *      (3) Usage example:
+ * \code
+ *            Pix *pix1 = pixRead("...");
+ *            Pix *pix2 = function(pix1, ...);
+ *            pixSwapAndDestroy(&pix1, &pix2);
+ *            pixDestroy(&pix1);  // holds what was in pix2
+ * \endcode
+ *          Example with clones ([] shows ref count of image generated
+ *                               by the function):
+ * \code
+ *            Pix *pixs = pixRead("...");
+ *            Pix *pix1 = pixClone(pixs);
+ *            Pix *pix2 = function(pix1, ...);   [1]
+ *            Pix *pix3 = pixClone(pix2);   [1] --> [2]
+ *            pixSwapAndDestroy(&pix1, &pix2);
+ *            pixDestroy(&pixs);  // still holds read image
+ *            pixDestroy(&pix1);  // holds what was in pix2  [2] --> [1]
+ *            pixDestroy(&pix3);  // holds what was in pix2  [1] --> [0]
+ * \endcode
+ * 
+ */ +l_ok +pixSwapAndDestroy(PIX **ppixd, + PIX **ppixs) +{ + PROCNAME("pixSwapAndDestroy"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + if (!ppixs) + return ERROR_INT("&pixs not defined", procName, 1); + if (*ppixs == NULL) + return ERROR_INT("pixs not defined", procName, 1); + if (ppixs == ppixd) /* no-op */ + return ERROR_INT("&pixd == &pixs", procName, 1); + + pixDestroy(ppixd); + *ppixd = pixClone(*ppixs); + pixDestroy(ppixs); + return 0; +} + + +/*--------------------------------------------------------------------* + * Accessors * + *--------------------------------------------------------------------*/ +l_int32 +pixGetWidth(const PIX *pix) +{ + PROCNAME("pixGetWidth"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + return pix->w; +} + + +l_int32 +pixSetWidth(PIX *pix, + l_int32 width) +{ + PROCNAME("pixSetWidth"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (width < 0) { + pix->w = 0; + return ERROR_INT("width must be >= 0", procName, 1); + } + + pix->w = width; + return 0; +} + + +l_int32 +pixGetHeight(const PIX *pix) +{ + PROCNAME("pixGetHeight"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + return pix->h; +} + + +l_int32 +pixSetHeight(PIX *pix, + l_int32 height) +{ + PROCNAME("pixSetHeight"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (height < 0) { + pix->h = 0; + return ERROR_INT("h must be >= 0", procName, 1); + } + + pix->h = height; + return 0; +} + + +l_int32 +pixGetDepth(const PIX *pix) +{ + PROCNAME("pixGetDepth"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + return pix->d; +} + + +l_int32 +pixSetDepth(PIX *pix, + l_int32 depth) +{ + PROCNAME("pixSetDepth"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (depth < 1) + return ERROR_INT("d must be >= 1", procName, 1); + + pix->d = depth; + return 0; +} + + +/*! + * \brief pixGetDimensions() + * + * \param[in] pix + * \param[out] pw, ph, pd [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixGetDimensions(const PIX *pix, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd) +{ + PROCNAME("pixGetDimensions"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pd) *pd = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (pw) *pw = pix->w; + if (ph) *ph = pix->h; + if (pd) *pd = pix->d; + return 0; +} + + +/*! + * \brief pixSetDimensions() + * + * \param[in] pix + * \param[in] w, h, d use 0 to skip the setting for any of these + * \return 0 if OK, 1 on error + */ +l_ok +pixSetDimensions(PIX *pix, + l_int32 w, + l_int32 h, + l_int32 d) +{ + PROCNAME("pixSetDimensions"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (w > 0) pixSetWidth(pix, w); + if (h > 0) pixSetHeight(pix, h); + if (d > 0) pixSetDepth(pix, d); + return 0; +} + + +/*! + * \brief pixCopyDimensions() + * + * \param[in] pixd + * \param[in] pixs + * \return 0 if OK, 1 on error + */ +l_ok +pixCopyDimensions(PIX *pixd, + const PIX *pixs) +{ + PROCNAME("pixCopyDimensions"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixSetWidth(pixd, pixGetWidth(pixs)); + pixSetHeight(pixd, pixGetHeight(pixs)); + pixSetDepth(pixd, pixGetDepth(pixs)); + pixSetWpl(pixd, pixGetWpl(pixs)); + return 0; +} + + +l_int32 +pixGetSpp(const PIX *pix) +{ + PROCNAME("pixGetSpp"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + return pix->spp; +} + + +/* + * \brief pixSetSpp() + * + * \param[in] pix + * \param[in] spp 1, 3 or 4 samples + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For a 32 bpp pix, this can be used to ignore the
+ *          alpha sample (spp == 3) or to use it (spp == 4).
+ *          For example, to write a spp == 4 image without the alpha
+ *          sample (as an rgb pix), call pixSetSpp(pix, 3) and
+ *          then write it out as a png.
+ * 
+ */ +l_int32 +pixSetSpp(PIX *pix, + l_int32 spp) +{ + PROCNAME("pixSetSpp"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (spp < 1) + return ERROR_INT("spp must be >= 1", procName, 1); + + pix->spp = spp; + return 0; +} + + +/*! + * \brief pixCopySpp() + * + * \param[in] pixd + * \param[in] pixs + * \return 0 if OK, 1 on error + */ +l_ok +pixCopySpp(PIX *pixd, + const PIX *pixs) +{ + PROCNAME("pixCopySpp"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixSetSpp(pixd, pixGetSpp(pixs)); + return 0; +} + + +l_int32 +pixGetWpl(const PIX *pix) +{ + PROCNAME("pixGetWpl"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + return pix->wpl; +} + + +l_int32 +pixSetWpl(PIX *pix, + l_int32 wpl) +{ + PROCNAME("pixSetWpl"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pix->wpl = wpl; + return 0; +} + + +l_int32 +pixGetRefcount(const PIX *pix) +{ + PROCNAME("pixGetRefcount"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + return pix->refcount; +} + + +l_int32 +pixChangeRefcount(PIX *pix, + l_int32 delta) +{ + PROCNAME("pixChangeRefcount"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pix->refcount += delta; + return 0; +} + + +l_int32 +pixGetXRes(const PIX *pix) +{ + PROCNAME("pixGetXRes"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + return pix->xres; +} + + +l_int32 +pixSetXRes(PIX *pix, + l_int32 res) +{ + PROCNAME("pixSetXRes"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pix->xres = res; + return 0; +} + + +l_int32 +pixGetYRes(const PIX *pix) +{ + PROCNAME("pixGetYRes"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + return pix->yres; +} + + +l_int32 +pixSetYRes(PIX *pix, + l_int32 res) +{ + PROCNAME("pixSetYRes"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pix->yres = res; + return 0; +} + + +/*! + * \brief pixGetResolution() + * + * \param[in] pix + * \param[out] pxres, pyres [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixGetResolution(const PIX *pix, + l_int32 *pxres, + l_int32 *pyres) +{ + PROCNAME("pixGetResolution"); + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!pxres && !pyres) + return ERROR_INT("no output requested", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (pxres) *pxres = pix->xres; + if (pyres) *pyres = pix->yres; + return 0; +} + + +/*! + * \brief pixSetResolution() + * + * \param[in] pix + * \param[in] xres, yres use 0 to skip setting a value for either of these + * \return 0 if OK, 1 on error + */ +l_ok +pixSetResolution(PIX *pix, + l_int32 xres, + l_int32 yres) +{ + PROCNAME("pixSetResolution"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (xres > 0) pix->xres = xres; + if (yres > 0) pix->yres = yres; + return 0; +} + + +l_int32 +pixCopyResolution(PIX *pixd, + const PIX *pixs) +{ + PROCNAME("pixCopyResolution"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixSetXRes(pixd, pixGetXRes(pixs)); + pixSetYRes(pixd, pixGetYRes(pixs)); + return 0; +} + + +l_int32 +pixScaleResolution(PIX *pix, + l_float32 xscale, + l_float32 yscale) +{ + PROCNAME("pixScaleResolution"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (pix->xres != 0 && pix->yres != 0) { + pix->xres = (l_uint32)(xscale * (l_float32)(pix->xres) + 0.5); + pix->yres = (l_uint32)(yscale * (l_float32)(pix->yres) + 0.5); + } + return 0; +} + + +l_int32 +pixGetInputFormat(const PIX *pix) +{ + PROCNAME("pixGetInputFormat"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + return pix->informat; +} + + +l_int32 +pixSetInputFormat(PIX *pix, + l_int32 informat) +{ + PROCNAME("pixSetInputFormat"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pix->informat = informat; + return 0; +} + + +l_int32 +pixCopyInputFormat(PIX *pixd, + const PIX *pixs) +{ + PROCNAME("pixCopyInputFormat"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixSetInputFormat(pixd, pixGetInputFormat(pixs)); + return 0; +} + + +l_int32 +pixSetSpecial(PIX *pix, + l_int32 special) +{ + PROCNAME("pixSetSpecial"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pix->special = special; + return 0; +} + + +/*! + * \brief pixGetText() + * + * \param[in] pix + * \return ptr to existing text string + * + *
+ * Notes:
+ *      (1) The text string belongs to the pix:
+ *          * the caller must NOT free it
+ *          * it must not be used after the pix is destroyed
+ * 
+ */ +char * +pixGetText(PIX *pix) +{ + PROCNAME("pixGetText"); + + if (!pix) + return (char *)ERROR_PTR("pix not defined", procName, NULL); + return pix->text; +} + + +/*! + * \brief pixSetText() + * + * \param[in] pix + * \param[in] textstring can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This removes any existing textstring and puts a copy of
+ *          the input textstring there.
+ * 
+ */ +l_ok +pixSetText(PIX *pix, + const char *textstring) +{ + PROCNAME("pixSetText"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + stringReplace(&pix->text, textstring); + return 0; +} + + +/*! + * \brief pixAddText() + * + * \param[in] pix + * \param[in] textstring can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This adds the new textstring to any existing text.
+ *      (2) Either or both the existing text and the new text
+ *          string can be null.
+ * 
+ */ +l_ok +pixAddText(PIX *pix, + const char *textstring) +{ +char *newstring; + + PROCNAME("pixAddText"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + newstring = stringJoin(pixGetText(pix), textstring); + stringReplace(&pix->text, newstring); + LEPT_FREE(newstring); + return 0; +} + + +l_int32 +pixCopyText(PIX *pixd, + const PIX *pixs) +{ + PROCNAME("pixCopyText"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixs == pixd) + return 0; /* no-op */ + + pixSetText(pixd, pixs->text); + return 0; +} + + +PIXCMAP * +pixGetColormap(PIX *pix) +{ + PROCNAME("pixGetColormap"); + + if (!pix) + return (PIXCMAP *)ERROR_PTR("pix not defined", procName, NULL); + return pix->colormap; +} + + +/*! + * \brief pixSetColormap() + * + * \param[in] pix + * \param[in] colormap to be assigned + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) Unlike with the pix data field, pixSetColormap() destroys
+ *          any existing colormap before assigning the new one.
+ *          Because colormaps are not ref counted, it is important that
+ *          the new colormap does not belong to any other pix.
+ * 
+ */ +l_ok +pixSetColormap(PIX *pix, + PIXCMAP *colormap) +{ + PROCNAME("pixSetColormap"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixDestroyColormap(pix); + pix->colormap = colormap; + return 0; +} + + +/*! + * \brief pixDestroyColormap() + * + * \param[in] pix + * \return 0 if OK, 1 on error + */ +l_ok +pixDestroyColormap(PIX *pix) +{ +PIXCMAP *cmap; + + PROCNAME("pixDestroyColormap"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if ((cmap = pix->colormap) != NULL) { + pixcmapDestroy(&cmap); + pix->colormap = NULL; + } + return 0; +} + + +/*! + * \brief pixGetData() + * + * \param[in] pix + * \return ptr to image data + * + *
+ * Notes:
+ *      (1) This gives a new handle for the data.  The data is still
+ *          owned by the pix, so do not call LEPT_FREE() on it.
+ * 
+ */ +l_uint32 * +pixGetData(PIX *pix) +{ + PROCNAME("pixGetData"); + + if (!pix) + return (l_uint32 *)ERROR_PTR("pix not defined", procName, NULL); + return pix->data; +} + + +/*! + * \brief pixSetData() + * + * \param[in] pix + * \param[in] data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does not free any existing data.  To free existing
+ *          data, use pixFreeData() before pixSetData().
+ * 
+ */ +l_int32 +pixSetData(PIX *pix, + l_uint32 *data) +{ + PROCNAME("pixSetData"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pix->data = data; + return 0; +} + + +/*! + * \brief pixExtractData() + * + * \param[in] pix + * \return ptr to data, or null on error + * + *
+ * Notes:
+ *      (1) This extracts the pix image data for use in another context.
+ *          The caller still needs to use pixDestroy() on the input pix.
+ *      (2) If refcount == 1, the data is extracted and the
+ *          pix->data ptr is set to NULL.
+ *      (3) If refcount > 1, this simply returns a copy of the data,
+ *          using the pix allocator, and leaving the input pix unchanged.
+ * 
+ */ +l_uint32 * +pixExtractData(PIX *pixs) +{ +l_int32 count, bytes; +l_uint32 *data, *datas; + + PROCNAME("pixExtractData"); + + if (!pixs) + return (l_uint32 *)ERROR_PTR("pixs not defined", procName, NULL); + + count = pixGetRefcount(pixs); + if (count == 1) { /* extract */ + data = pixGetData(pixs); + pixSetData(pixs, NULL); + } else { /* refcount > 1; copy */ + bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); + datas = pixGetData(pixs); + if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) + return (l_uint32 *)ERROR_PTR("data not made", procName, NULL); + memcpy(data, datas, bytes); + } + + return data; +} + + +/*! + * \brief pixFreeData() + * + * \param[in] pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This frees the data and sets the pix data ptr to null.
+ *          It should be used before pixSetData() in the situation where
+ *          you want to free any existing data before doing
+ *          a subsequent assignment with pixSetData().
+ * 
+ */ +l_int32 +pixFreeData(PIX *pix) +{ +l_uint32 *data; + + PROCNAME("pixFreeData"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if ((data = pixGetData(pix)) != NULL) { + pix_free(data); + pix->data = NULL; + } + return 0; +} + + +/*--------------------------------------------------------------------* + * Pix line ptrs * + *--------------------------------------------------------------------*/ +/*! + * \brief pixGetLinePtrs() + * + * \param[in] pix + * \param[out] psize [optional] array size, which is the pix height + * \return array of line ptrs, or NULL on error + * + *
+ * Notes:
+ *      (1) This is intended to be used for fast random pixel access.
+ *          For example, for an 8 bpp image,
+ *              val = GET_DATA_BYTE(lines8[i], j);
+ *          is equivalent to, but much faster than,
+ *              pixGetPixel(pix, j, i, &val);
+ *      (2) How much faster?  For 1 bpp, it's from 6 to 10x faster.
+ *          For 8 bpp, it's an amazing 30x faster.  So if you are
+ *          doing random access over a substantial part of the image,
+ *          use this line ptr array.
+ *      (3) When random access is used in conjunction with a stack,
+ *          queue or heap, the overall computation time depends on
+ *          the operations performed on each struct that is popped
+ *          or pushed, and whether we are using a priority queue (O(logn))
+ *          or a queue or stack (O(1)).  For example, for maze search,
+ *          the overall ratio of time for line ptrs vs. pixGet/Set* is
+ *             Maze type     Type                   Time ratio
+ *               binary      queue                     0.4
+ *               gray        heap (priority queue)     0.6
+ *      (4) Because this returns a void** and the accessors take void*,
+ *          the compiler cannot check the pointer types.  It is
+ *          strongly recommended that you adopt a naming scheme for
+ *          the returned ptr arrays that indicates the pixel depth.
+ *          (This follows the original intent of Simonyi's "Hungarian"
+ *          application notation, where naming is used proactively
+ *          to make errors visibly obvious.)  By doing this, you can
+ *          tell by inspection if the correct accessor is used.
+ *          For example, for an 8 bpp pixg:
+ *              void **lineg8 = pixGetLinePtrs(pixg, NULL);
+ *              val = GET_DATA_BYTE(lineg8[i], j);  // fast access; BYTE, 8
+ *              ...
+ *              LEPT_FREE(lineg8);  // don't forget this
+ *      (5) These are convenient for accessing bytes sequentially in an
+ *          8 bpp grayscale image.  People who write image processing code
+ *          on 8 bpp images are accustomed to grabbing pixels directly out
+ *          of the raster array.  Note that for little endians, you first
+ *          need to reverse the byte order in each 32-bit word.
+ *          Here's a typical usage pattern:
+ *              pixEndianByteSwap(pix);   // always safe; no-op on big-endians
+ *              l_uint8 **lineptrs = (l_uint8 **)pixGetLinePtrs(pix, NULL);
+ *              pixGetDimensions(pix, &w, &h, NULL);
+ *              for (i = 0; i < h; i++) {
+ *                  l_uint8 *line = lineptrs[i];
+ *                  for (j = 0; j < w; j++) {
+ *                      val = line[j];
+ *                      ...
+ *                  }
+ *              }
+ *              pixEndianByteSwap(pix);  // restore big-endian order
+ *              LEPT_FREE(lineptrs);
+ *          This can be done even more simply as follows:
+ *              l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h);
+ *              for (i = 0; i < h; i++) {
+ *                  l_uint8 *line = lineptrs[i];
+ *                  for (j = 0; j < w; j++) {
+ *                      val = line[j];
+ *                      ...
+ *                  }
+ *              }
+ *              pixCleanupByteProcessing(pix, lineptrs);
+ * 
+ */ +void ** +pixGetLinePtrs(PIX *pix, + l_int32 *psize) +{ +l_int32 i, h, wpl; +l_uint32 *data; +void **lines; + + PROCNAME("pixGetLinePtrs"); + + if (psize) *psize = 0; + if (!pix) + return (void **)ERROR_PTR("pix not defined", procName, NULL); + + h = pixGetHeight(pix); + if (psize) *psize = h; + if ((lines = (void **)LEPT_CALLOC(h, sizeof(void *))) == NULL) + return (void **)ERROR_PTR("lines not made", procName, NULL); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + for (i = 0; i < h; i++) + lines[i] = (void *)(data + i * wpl); + + return lines; +} + + +/*--------------------------------------------------------------------* + * Print output for debugging * + *--------------------------------------------------------------------*/ +extern const char *ImageFileFormatExtensions[]; + +/*! + * \brief pixPrintStreamInfo() + * + * \param[in] fp file stream + * \param[in] pix + * \param[in] text [optional] identifying string; can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixPrintStreamInfo(FILE *fp, + const PIX *pix, + const char *text) +{ +l_int32 informat; +const PIXCMAP *cmap; + + PROCNAME("pixPrintStreamInfo"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (text) + fprintf(fp, " Pix Info for %s:\n", text); + fprintf(fp, " width = %d, height = %d, depth = %d, spp = %d\n", + pixGetWidth(pix), pixGetHeight(pix), pixGetDepth(pix), + pixGetSpp(pix)); + fprintf(fp, " wpl = %d, data = %p, refcount = %d\n", + pixGetWpl(pix), pix->data, pixGetRefcount(pix)); + fprintf(fp, " xres = %d, yres = %d\n", pixGetXRes(pix), pixGetYRes(pix)); + if ((cmap = pix->colormap) != NULL) + pixcmapWriteStream(fp, cmap); + else + fprintf(fp, " no colormap\n"); + informat = pixGetInputFormat(pix); + fprintf(fp, " input format: %d (%s)\n", informat, + ImageFileFormatExtensions[informat]); + if (pix->text != NULL) + fprintf(fp, " text: %s\n", pix->text); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix2.c b/hgdriver/3rdparty/hgOCR/leptonica/pix2.c new file mode 100644 index 0000000..1f03736 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix2.c @@ -0,0 +1,3390 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pix2.c + *
+ *
+ *    This file has these basic operations:
+ *
+ *      (1) Get and set: individual pixels, full image, rectangular region,
+ *          pad pixels, border pixels, and color components for RGB
+ *      (2) Add and remove border pixels
+ *      (3) Endian byte swaps
+ *      (4) Simple method for byte-processing images (instead of words)
+ *
+ *      Pixel poking
+ *           l_int32     pixGetPixel()
+ *           l_int32     pixSetPixel()
+ *           l_int32     pixGetRGBPixel()
+ *           l_int32     pixSetRGBPixel()
+ *           l_int32     pixGetRandomPixel()
+ *           l_int32     pixClearPixel()
+ *           l_int32     pixFlipPixel()
+ *           void        setPixelLow()
+ *
+ *      Find black or white value
+ *           l_int32     pixGetBlackOrWhiteVal()
+ *
+ *      Full image clear/set/set-to-arbitrary-value
+ *           l_int32     pixClearAll()
+ *           l_int32     pixSetAll()
+ *           l_int32     pixSetAllGray()
+ *           l_int32     pixSetAllArbitrary()
+ *           l_int32     pixSetBlackOrWhite()
+ *           l_int32     pixSetComponentArbitrary()
+ *
+ *      Rectangular region clear/set/set-to-arbitrary-value/blend
+ *           l_int32     pixClearInRect()
+ *           l_int32     pixSetInRect()
+ *           l_int32     pixSetInRectArbitrary()
+ *           l_int32     pixBlendInRect()
+ *
+ *      Set pad bits
+ *           l_int32     pixSetPadBits()
+ *           l_int32     pixSetPadBitsBand()
+ *
+ *      Assign border pixels
+ *           l_int32     pixSetOrClearBorder()
+ *           l_int32     pixSetBorderVal()
+ *           l_int32     pixSetBorderRingVal()
+ *           l_int32     pixSetMirroredBorder()
+ *           PIX        *pixCopyBorder()
+ *
+ *      Add and remove border
+ *           PIX        *pixAddBorder()
+ *           PIX        *pixAddBlackOrWhiteBorder()
+ *           PIX        *pixAddBorderGeneral()
+ *           PIX        *pixRemoveBorder()
+ *           PIX        *pixRemoveBorderGeneral()
+ *           PIX        *pixRemoveBorderToSize()
+ *           PIX        *pixAddMirroredBorder()
+ *           PIX        *pixAddRepeatedBorder()
+ *           PIX        *pixAddMixedBorder()
+ *           PIX        *pixAddContinuedBorder()
+ *
+ *      Helper functions using alpha
+ *           l_int32     pixShiftAndTransferAlpha()
+ *           PIX        *pixDisplayLayersRGBA()
+ *
+ *      Color sample setting and extraction
+ *           PIX        *pixCreateRGBImage()
+ *           PIX        *pixGetRGBComponent()
+ *           l_int32     pixSetRGBComponent()
+ *           PIX        *pixGetRGBComponentCmap()
+ *           l_int32     pixCopyRGBComponent()
+ *           l_int32     composeRGBPixel()
+ *           l_int32     composeRGBAPixel()
+ *           void        extractRGBValues()
+ *           void        extractRGBAValues()
+ *           l_int32     extractMinMaxComponent()
+ *           l_int32     pixGetRGBLine()
+ *
+ *      Conversion between big and little endians
+ *           PIX        *pixEndianByteSwapNew()
+ *           l_int32     pixEndianByteSwap()
+ *           l_int32     lineEndianByteSwap()
+ *           PIX        *pixEndianTwoByteSwapNew()
+ *           l_int32     pixEndianTwoByteSwap()
+ *
+ *      Extract raster data as binary string
+ *           l_int32     pixGetRasterData()
+ *
+ *      Test alpha component opaqueness
+ *           l_int32     pixAlphaIsOpaque
+ *
+ *      Setup helpers for 8 bpp byte processing
+ *           l_uint8   **pixSetupByteProcessing()
+ *           l_int32     pixCleanupByteProcessing()
+ *
+ *      Setting parameters for antialias masking with alpha transforms
+ *           void        l_setAlphaMaskBorder()
+ * 
+ */ + + +#include +#include "allheaders.h" + +static const l_uint32 rmask32[] = {0x0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, + 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, + 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, + 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, + 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; + + /* This is a global that determines the default 8 bpp alpha mask values + * for rings at distance 1 and 2 from the border. Declare extern + * to use. To change the values, use l_setAlphaMaskBorder(). */ +LEPT_DLL l_float32 AlphaMaskBorderVals[2] = {0.0, 0.5}; + + +#ifndef NO_CONSOLE_IO +#define DEBUG_SERIALIZE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Pixel poking * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetPixel() + * + * \param[in] pix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + *
+ * Notes:
+ *      (1) This returns the value in the data array.  If the pix is
+ *          colormapped, it returns the colormap index, not the rgb value.
+ *      (2) Because of the function overhead and the parameter checking,
+ *          this is much slower than using the GET_DATA_*() macros directly.
+ *          Speed on a 1 Mpixel RGB image, using a 3 GHz machine:
+ *            * pixGet/pixSet: ~25 Mpix/sec
+ *            * GET_DATA/SET_DATA: ~350 MPix/sec
+ *          If speed is important and you're doing random access into
+ *          the pix, use pixGetLinePtrs() and the array access macros.
+ *      (3) If the point is outside the image, this returns an error (2),
+ *          with 0 in %pval.  To avoid spamming output, it fails silently.
+ * 
+ */ +l_ok +pixGetPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_uint32 *pval) +{ +l_int32 w, h, d, wpl, val; +l_uint32 *line, *data; + + PROCNAME("pixGetPixel"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + switch (d) + { + case 1: + val = GET_DATA_BIT(line, x); + break; + case 2: + val = GET_DATA_DIBIT(line, x); + break; + case 4: + val = GET_DATA_QBIT(line, x); + break; + case 8: + val = GET_DATA_BYTE(line, x); + break; + case 16: + val = GET_DATA_TWO_BYTES(line, x); + break; + case 32: + val = line[x]; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); + } + + *pval = val; + return 0; +} + + +/*! + * \brief pixSetPixel() + * + * \param[in] pix + * \param[in] x,y pixel coords + * \param[in] val value to be inserted + * \return 0 if OK; 1 or 2 on error + * + *
+ * Notes:
+ *      (1) Warning: the input value is not checked for overflow with respect
+ *          the the depth of %pix, and the sign bit (if any) is ignored.
+ *          * For d == 1, %val > 0 sets the bit on.
+ *          * For d == 2, 4, 8 and 16, %val is masked to the maximum allowable
+ *            pixel value, and any (invalid) higher order bits are discarded.
+ *      (2) See pixGetPixel() for information on performance.
+ *      (3) If the point is outside the image, this returns an error (2),
+ *          with 0 in %pval.  To avoid spamming output, it fails silently.
+ * 
+ */ +l_ok +pixSetPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_uint32 val) +{ +l_int32 w, h, d, wpl; +l_uint32 *line, *data; + + PROCNAME("pixSetPixel"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + line = data + y * wpl; + switch (d) + { + case 1: + if (val) + SET_DATA_BIT(line, x); + else + CLEAR_DATA_BIT(line, x); + break; + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); + } + + return 0; +} + + +/*! + * \brief pixGetRGBPixel() + * + * \param[in] pix 32 bpp rgb, not colormapped + * \param[in] x,y pixel coords + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +pixGetRGBPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 w, h, d, wpl; +l_uint32 *data, *ppixel; + + PROCNAME("pixGetRGBPixel"); + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval && !pgval && !pbval) + return ERROR_INT("no output requested", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 32) + return ERROR_INT("pix not 32 bpp", procName, 1); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + ppixel = data + y * wpl + x; + if (prval) *prval = GET_DATA_BYTE(ppixel, COLOR_RED); + if (pgval) *pgval = GET_DATA_BYTE(ppixel, COLOR_GREEN); + if (pbval) *pbval = GET_DATA_BYTE(ppixel, COLOR_BLUE); + return 0; +} + + +/*! + * \brief pixSetRGBPixel() + * + * \param[in] pix 32 bpp rgb + * \param[in] x,y pixel coords + * \param[in] rval red component + * \param[in] gval green component + * \param[in] bval blue component + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +pixSetRGBPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h, d, wpl; +l_uint32 pixel; +l_uint32 *data, *line; + + PROCNAME("pixSetRGBPixel"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 32) + return ERROR_INT("pix not 32 bpp", procName, 1); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + composeRGBPixel(rval, gval, bval, &pixel); + *(line + x) = pixel; + return 0; +} + + +/*! + * \brief pixGetRandomPixel() + * + * \param[in] pix any depth; can be colormapped + * \param[out] pval [optional] pixel value + * \param[out] px [optional] x coordinate chosen; can be null + * \param[out] py [optional] y coordinate chosen; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If the pix is colormapped, it returns the rgb value.
+ * 
+ */ +l_ok +pixGetRandomPixel(PIX *pix, + l_uint32 *pval, + l_int32 *px, + l_int32 *py) +{ +l_int32 w, h, x, y, rval, gval, bval; +l_uint32 val; +PIXCMAP *cmap; + + PROCNAME("pixGetRandomPixel"); + + if (pval) *pval = 0; + if (px) *px = 0; + if (py) *py = 0; + if (!pval && !px && !py) + return ERROR_INT("no output requested", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, NULL); + x = rand() % w; + y = rand() % h; + if (px) *px = x; + if (py) *py = y; + if (pval) { + pixGetPixel(pix, x, y, &val); + if ((cmap = pixGetColormap(pix)) != NULL) { + pixcmapGetColor(cmap, val, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, pval); + } else { + *pval = val; + } + } + + return 0; +} + + +/*! + * \brief pixClearPixel() + * + * \param[in] pix any depth; warning if colormapped + * \param[in] x,y pixel coords + * \return 0 if OK; 1 or 2 on error. + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +pixClearPixel(PIX *pix, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, d, wpl; +l_uint32 *line, *data; + + PROCNAME("pixClearPixel"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (pixGetColormap(pix)) + L_WARNING("cmapped: setting to 0 may not be intended\n", procName); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + switch (d) + { + case 1: + CLEAR_DATA_BIT(line, x); + break; + case 2: + CLEAR_DATA_DIBIT(line, x); + break; + case 4: + CLEAR_DATA_QBIT(line, x); + break; + case 8: + SET_DATA_BYTE(line, x, 0); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, 0); + break; + case 32: + line[x] = 0; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); + } + + return 0; +} + + +/*! + * \brief pixFlipPixel() + * + * \param[in] pix any depth, warning if colormapped + * \param[in] x,y pixel coords + * \return 0 if OK; 1 or 2 on error + * + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + */ +l_ok +pixFlipPixel(PIX *pix, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, d, wpl; +l_uint32 val; +l_uint32 *line, *data; + + PROCNAME("pixFlipPixel"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (pixGetColormap(pix)) + L_WARNING("cmapped: setting to 0 may not be intended\n", procName); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + line = data + y * wpl; + switch (d) + { + case 1: + val = GET_DATA_BIT(line, x); + if (val) + CLEAR_DATA_BIT(line, x); + else + SET_DATA_BIT(line, x); + break; + case 2: + val = GET_DATA_DIBIT(line, x); + val ^= 0x3; + SET_DATA_DIBIT(line, x, val); + break; + case 4: + val = GET_DATA_QBIT(line, x); + val ^= 0xf; + SET_DATA_QBIT(line, x, val); + break; + case 8: + val = GET_DATA_BYTE(line, x); + val ^= 0xff; + SET_DATA_BYTE(line, x, val); + break; + case 16: + val = GET_DATA_TWO_BYTES(line, x); + val ^= 0xffff; + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + val = line[x] ^ 0xffffffff; + line[x] = val; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); + } + + return 0; +} + + +/*! + * \brief setPixelLow() + * + * \param[in] line ptr to beginning of line, + * \param[in] x pixel location in line + * \param[in] depth bpp + * \param[in] val to be inserted + * \return void + * + *
+ * Notes:
+ *      (1) Caution: input variables are not checked!
+ * 
+ */ +void +setPixelLow(l_uint32 *line, + l_int32 x, + l_int32 depth, + l_uint32 val) +{ + switch (depth) + { + case 1: + if (val) + SET_DATA_BIT(line, x); + else + CLEAR_DATA_BIT(line, x); + break; + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + fprintf(stderr, "illegal depth in setPixelLow()\n"); + } + + return; +} + + +/*-------------------------------------------------------------* + * Find black or white value * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetBlackOrWhiteVal() + * + * \param[in] pixs all depths; cmap ok + * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL + * \param[out] pval pixel value + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Side effect.  For a colormapped image, if the requested
+ *          color is not present and there is room to add it in the cmap,
+ *          it is added and the new index is returned.  If there is no room,
+ *          the index of the closest color in intensity is returned.
+ * 
+ */ +l_ok +pixGetBlackOrWhiteVal(PIX *pixs, + l_int32 op, + l_uint32 *pval) +{ +l_int32 d, val; +PIXCMAP *cmap; + + PROCNAME("pixGetBlackOrWhiteVal"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) + return ERROR_INT("invalid op", procName, 1); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap) { + if ((d == 1 && op == L_GET_WHITE_VAL) || + (d > 1 && op == L_GET_BLACK_VAL)) { /* min val */ + val = 0; + } else { /* max val */ + val = (d == 32) ? 0xffffff00 : (1 << d) - 1; + } + } else { /* handle colormap */ + if (op == L_GET_BLACK_VAL) + pixcmapAddBlackOrWhite(cmap, 0, &val); + else /* L_GET_WHITE_VAL */ + pixcmapAddBlackOrWhite(cmap, 1, &val); + } + *pval = val; + + return 0; +} + + +/*-------------------------------------------------------------* + * Full image clear/set/set-to-arbitrary-value/invert * + *-------------------------------------------------------------*/ +/*! + * \brief pixClearAll() + * + * \param[in] pix all depths; use cmapped with caution + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Clears all data to 0.  For 1 bpp, this is white; for grayscale
+ *          or color, this is black.
+ *      (2) Caution: for colormapped pix, this sets the color to the first
+ *          one in the colormap.  Be sure that this is the intended color!
+ * 
+ */ +l_ok +pixClearAll(PIX *pix) +{ + PROCNAME("pixClearAll"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix), + PIX_CLR, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetAll() + * + * \param[in] pix all depths; use cmapped with caution + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Sets all data to 1.  For 1 bpp, this is black; for grayscale
+ *          or color, this is white.
+ *      (2) Caution: for colormapped pix, this sets the pixel value to the
+ *          maximum value supported by the colormap: 2^d - 1.  However, this
+ *          color may not be defined, because the colormap may not be full.
+ * 
+ */ +l_ok +pixSetAll(PIX *pix) +{ +l_int32 n; +PIXCMAP *cmap; + + PROCNAME("pixSetAll"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (n < cmap->nalloc) /* cmap is not full */ + return ERROR_INT("cmap entry does not exist", procName, 1); + } + + pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix), + PIX_SET, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetAllGray() + * + * \param[in] pix all depths, cmap ok + * \param[in] grayval in range 0 ... 255 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) N.B.  For all images, %grayval == 0 represents black and
+ *          %grayval == 255 represents white.
+ *      (2) For depth < 8, we do our best to approximate the gray level.
+ *          For 1 bpp images, any %grayval < 128 is black; >= 128 is white.
+ *          For 32 bpp images, each r,g,b component is set to %grayval,
+ *          and the alpha component is preserved.
+ *      (3) If pix is colormapped, it adds the gray value, replicated in
+ *          all components, to the colormap if it's not there and there
+ *          is room.  If the colormap is full, it finds the closest color in
+ *          L2 distance of components.  This index is written to all pixels.
+ * 
+ */ +l_ok +pixSetAllGray(PIX *pix, + l_int32 grayval) +{ +l_int32 d, spp, index; +l_uint32 val32; +PIX *alpha; +PIXCMAP *cmap; + + PROCNAME("pixSetAllGray"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (grayval < 0) { + L_WARNING("grayval < 0; setting to 0\n", procName); + grayval = 0; + } else if (grayval > 255) { + L_WARNING("grayval > 255; setting to 255\n", procName); + grayval = 255; + } + + /* Handle the colormap case */ + cmap = pixGetColormap(pix); + if (cmap) { + pixcmapAddNearestColor(cmap, grayval, grayval, grayval, &index); + pixSetAllArbitrary(pix, index); + return 0; + } + + /* Non-cmapped */ + d = pixGetDepth(pix); + spp = pixGetSpp(pix); + if (d == 1) { + if (grayval < 128) /* black */ + pixSetAll(pix); + else + pixClearAll(pix); /* white */ + } else if (d < 8) { + grayval >>= 8 - d; + pixSetAllArbitrary(pix, grayval); + } else if (d == 8) { + pixSetAllArbitrary(pix, grayval); + } else if (d == 16) { + grayval |= (grayval << 8); + pixSetAllArbitrary(pix, grayval); + } else if (d == 32 && spp == 3) { + composeRGBPixel(grayval, grayval, grayval, &val32); + pixSetAllArbitrary(pix, val32); + } else if (d == 32 && spp == 4) { + alpha = pixGetRGBComponent(pix, L_ALPHA_CHANNEL); + composeRGBPixel(grayval, grayval, grayval, &val32); + pixSetAllArbitrary(pix, val32); + pixSetRGBComponent(pix, alpha, L_ALPHA_CHANNEL); + pixDestroy(&alpha); + } else { + L_ERROR("invalid depth: %d\n", procName, d); + return 1; + } + + return 0; +} + + +/*! + * \brief pixSetAllArbitrary() + * + * \param[in] pix all depths; use cmapped with caution + * \param[in] val value to set all pixels + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caution 1!  For colormapped pix, %val is used as an index
+ *          into a colormap.  Be sure that index refers to the intended color.
+ *          If the color is not in the colormap, you should first add it
+ *          and then call this function.
+ *      (2) Caution 2!  For 32 bpp pix, the interpretation of the LSB
+ *          of %val depends on whether spp == 3 (RGB) or spp == 4 (RGBA).
+ *          For RGB, the LSB is ignored in image transformations.
+ *          For RGBA, the LSB is interpreted as the alpha (transparency)
+ *          component; full transparency has alpha == 0x0, whereas
+ *          full opacity has alpha = 0xff.  An RGBA image with full
+ *          opacity behaves like an RGB image.
+ *      (3) As an example of (2), suppose you want to initialize a 32 bpp
+ *          pix with partial opacity, say 0xee337788.  If the pix is 3 spp,
+ *          the 0x88 alpha component will be ignored and may be changed
+ *          in subsequent processing.  However, if the pix is 4 spp, the
+ *          alpha component will be retained and used. The function
+ *          pixCreate(w, h, 32) makes an RGB image by default, and
+ *          pixSetSpp(pix, 4) can be used to promote an RGB image to RGBA.
+ * 
+ */ +l_ok +pixSetAllArbitrary(PIX *pix, + l_uint32 val) +{ +l_int32 n, i, j, w, h, d, wpl, npix; +l_uint32 maxval, wordval; +l_uint32 *data, *line; +PIXCMAP *cmap; + + PROCNAME("pixSetAllArbitrary"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + /* If colormapped, make sure that val is less than the size + * of the cmap array. */ + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (val >= n) { + L_WARNING("index not in colormap; using last color\n", procName); + val = n - 1; + } + } + + /* Make sure val isn't too large for the pixel depth. + * If it is too large, set the pixel color to white. */ + pixGetDimensions(pix, &w, &h, &d); + if (d < 32) { + maxval = (1 << d) - 1; + if (val > maxval) { + L_WARNING("val = %d too large for depth; using maxval = %d\n", + procName, val, maxval); + val = maxval; + } + } + + /* Set up word to tile with */ + wordval = 0; + npix = 32 / d; /* number of pixels per 32 bit word */ + for (j = 0; j < npix; j++) + wordval |= (val << (j * d)); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + *(line + j) = wordval; + } + } + return 0; +} + + +/*! + * \brief pixSetBlackOrWhite() + * + * \param[in] pixs all depths; cmap ok + * \param[in] op L_SET_BLACK, L_SET_WHITE + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Function for setting all pixels in an image to either black
+ *          or white.
+ *      (2) If pixs is colormapped, it adds black or white to the
+ *          colormap if it's not there and there is room.  If the colormap
+ *          is full, it finds the closest color in intensity.
+ *          This index is written to all pixels.
+ * 
+ */ +l_ok +pixSetBlackOrWhite(PIX *pixs, + l_int32 op) +{ +l_int32 d, index; +PIXCMAP *cmap; + + PROCNAME("pixSetBlackOrWhite"); + + if (!pixs) + return ERROR_INT("pix not defined", procName, 1); + if (op != L_SET_BLACK && op != L_SET_WHITE) + return ERROR_INT("invalid op", procName, 1); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap) { + if ((d == 1 && op == L_SET_BLACK) || (d > 1 && op == L_SET_WHITE)) + pixSetAll(pixs); + else + pixClearAll(pixs); + } else { /* handle colormap */ + if (op == L_SET_BLACK) + pixcmapAddBlackOrWhite(cmap, 0, &index); + else /* L_SET_WHITE */ + pixcmapAddBlackOrWhite(cmap, 1, &index); + pixSetAllArbitrary(pixs, index); + } + + return 0; +} + + +/*! + * \brief pixSetComponentArbitrary() + * + * \param[in] pix 32 bpp + * \param[in] comp COLOR_RED, COLOR_GREEN, COLOR_BLUE, L_ALPHA_CHANNEL + * \param[in] val value to set this component + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) For example, this can be used to set the alpha component to opaque:
+ *              pixSetComponentArbitrary(pix, L_ALPHA_CHANNEL, 255)
+ * 
+ */ +l_ok +pixSetComponentArbitrary(PIX *pix, + l_int32 comp, + l_int32 val) +{ +l_int32 i, nwords; +l_uint32 mask1, mask2; +l_uint32 *data; + + PROCNAME("pixSetComponentArbitrary"); + + if (!pix || pixGetDepth(pix) != 32) + return ERROR_INT("pix not defined or not 32 bpp", procName, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE && + comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid component", procName, 1); + if (val < 0 || val > 255) + return ERROR_INT("val not in [0 ... 255]", procName, 1); + + mask1 = ~(255 << (8 * (3 - comp))); + mask2 = val << (8 * (3 - comp)); + nwords = pixGetHeight(pix) * pixGetWpl(pix); + data = pixGetData(pix); + for (i = 0; i < nwords; i++) { + data[i] &= mask1; /* clear out the component */ + data[i] |= mask2; /* insert the new component value */ + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Rectangular region clear/set/set-to-arbitrary-value * + *-------------------------------------------------------------*/ +/*! + * \brief pixClearInRect() + * + * \param[in] pix all depths; can be cmapped + * \param[in] box in which all pixels will be cleared + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Clears all data in rect to 0.  For 1 bpp, this is white;
+ *          for grayscale or color, this is black.
+ *      (2) Caution: for colormapped pix, this sets the color to the first
+ *          one in the colormap.  Be sure that this is the intended color!
+ * 
+ */ +l_ok +pixClearInRect(PIX *pix, + BOX *box) +{ +l_int32 x, y, w, h; + + PROCNAME("pixClearInRect"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pix, x, y, w, h, PIX_CLR, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetInRect() + * + * \param[in] pix all depths, can be cmapped + * \param[in] box in which all pixels will be set + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Sets all data in rect to 1.  For 1 bpp, this is black;
+ *          for grayscale or color, this is white.
+ *      (2) Caution: for colormapped pix, this sets the pixel value to the
+ *          maximum value supported by the colormap: 2^d - 1.  However, this
+ *          color may not be defined, because the colormap may not be full.
+ * 
+ */ +l_ok +pixSetInRect(PIX *pix, + BOX *box) +{ +l_int32 n, x, y, w, h; +PIXCMAP *cmap; + + PROCNAME("pixSetInRect"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (n < cmap->nalloc) /* cmap is not full */ + return ERROR_INT("cmap entry does not exist", procName, 1); + } + + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pix, x, y, w, h, PIX_SET, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetInRectArbitrary() + * + * \param[in] pix all depths; can be cmapped + * \param[in] box in which all pixels will be set to val + * \param[in] val value to set all pixels + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) For colormapped pix, be sure the value is the intended
+ *          one in the colormap.
+ *      (2) Caution: for colormapped pix, this sets each pixel in the
+ *          rect to the color at the index equal to val.  Be sure that
+ *          this index exists in the colormap and that it is the intended one!
+ * 
+ */ +l_ok +pixSetInRectArbitrary(PIX *pix, + BOX *box, + l_uint32 val) +{ +l_int32 n, x, y, xstart, xend, ystart, yend, bw, bh, w, h, d, wpl, maxval; +l_uint32 *data, *line; +BOX *boxc; +PIXCMAP *cmap; + + PROCNAME("pixSetInRectArbitrary"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d !=8 && d != 16 && d != 32) + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (val >= n) { + L_WARNING("index not in colormap; using last color\n", procName); + val = n - 1; + } + } + + maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; + if (val > maxval) val = maxval; + + /* Handle the simple cases: the min and max values */ + if (val == 0) { + pixClearInRect(pix, box); + return 0; + } + if (d == 1 || + (d == 2 && val == 3) || + (d == 4 && val == 0xf) || + (d == 8 && val == 0xff) || + (d == 16 && val == 0xffff) || + (d == 32 && ((val ^ 0xffffff00) >> 8 == 0))) { + pixSetInRect(pix, box); + return 0; + } + + /* Find the overlap of box with the input pix */ + if ((boxc = boxClipToRectangle(box, w, h)) == NULL) + return ERROR_INT("no overlap of box with image", procName, 1); + boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh); + xend = xstart + bw - 1; + yend = ystart + bh - 1; + boxDestroy(&boxc); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + for (y = ystart; y <= yend; y++) { + line = data + y * wpl; + for (x = xstart; x <= xend; x++) { + switch(d) + { + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + return ERROR_INT("depth not 2|4|8|16|32 bpp", procName, 1); + } + } + } + + return 0; +} + + +/*! + * \brief pixBlendInRect() + * + * \param[in] pixs 32 bpp rgb + * \param[in] box [optional] in which all pixels will be blended + * \param[in] val blend value; 0xrrggbb00 + * \param[in] fract fraction of color to be blended with each pixel in pixs + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place function.  It blends the input color %val
+ *          with the pixels in pixs in the specified rectangle.
+ *          If no rectangle is specified, it blends over the entire image.
+ * 
+ */ +l_ok +pixBlendInRect(PIX *pixs, + BOX *box, + l_uint32 val, + l_float32 fract) +{ +l_int32 i, j, bx, by, bw, bh, w, h, wpls; +l_int32 prval, pgval, pbval, rval, gval, bval; +l_uint32 val32; +l_uint32 *datas, *lines; + + PROCNAME("pixBlendInRect"); + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + + extractRGBValues(val, &rval, &gval, &bval); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (!box) { + for (i = 0; i < h; i++) { /* scan over box */ + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val32 = *(lines + j); + extractRGBValues(val32, &prval, &pgval, &pbval); + prval = (l_int32)((1. - fract) * prval + fract * rval); + pgval = (l_int32)((1. - fract) * pgval + fract * gval); + pbval = (l_int32)((1. - fract) * pbval + fract * bval); + composeRGBPixel(prval, pgval, pbval, &val32); + *(lines + j) = val32; + } + } + return 0; + } + + boxGetGeometry(box, &bx, &by, &bw, &bh); + for (i = 0; i < bh; i++) { /* scan over box */ + if (by + i < 0 || by + i >= h) continue; + lines = datas + (by + i) * wpls; + for (j = 0; j < bw; j++) { + if (bx + j < 0 || bx + j >= w) continue; + val32 = *(lines + bx + j); + extractRGBValues(val32, &prval, &pgval, &pbval); + prval = (l_int32)((1. - fract) * prval + fract * rval); + pgval = (l_int32)((1. - fract) * pgval + fract * gval); + pbval = (l_int32)((1. - fract) * pbval + fract * bval); + composeRGBPixel(prval, pgval, pbval, &val32); + *(lines + bx + j) = val32; + } + } + return 0; +} + + +/*-------------------------------------------------------------* + * Set pad bits * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetPadBits() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] val 0 or 1 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The pad bits are the bits that expand each scanline to a
+ *          multiple of 32 bits.  They are usually not used in
+ *          image processing operations.  When boundary conditions
+ *          are important, as in seedfill, they must be set properly.
+ *      (2) This sets the value of the pad bits (if any) in the last
+ *          32-bit word in each scanline.
+ *      (3) For 32 bpp pix, there are no pad bits, so this is a no-op.
+ *      (4) When writing formatted output, such as tiff, png or jpeg,
+ *          the pad bits have no effect on the raster image that is
+ *          generated by reading back from the file.  However, in some
+ *          cases, the compressed file itself will depend on the pad
+ *          bits.  This is seen, for example, in Windows with 2 and 4 bpp
+ *          tiff-compressed images that have pad bits on each scanline.
+ *          It is sometimes convenient to use a golden file with a
+ *          byte-by-byte check to verify invariance.  Consequently,
+ *          and because setting the pad bits is cheap, the pad bits are
+ *          set to 0 before writing these compressed files.
+ * 
+ */ +l_ok +pixSetPadBits(PIX *pix, + l_int32 val) +{ +l_int32 i, w, h, d, wpl, endbits, fullwords; +l_uint32 mask; +l_uint32 *data, *pword; + + PROCNAME("pixSetPadBits"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d == 32) /* no padding exists for 32 bpp */ + return 0; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + endbits = 32 - (((l_int64)w * d) % 32); + if (endbits == 32) /* no partial word */ + return 0; + fullwords = (1LL * w * d) / 32; + mask = rmask32[endbits]; + if (val == 0) + mask = ~mask; + + for (i = 0; i < h; i++) { + pword = data + i * wpl + fullwords; + if (val == 0) /* clear */ + *pword = *pword & mask; + else /* set */ + *pword = *pword | mask; + } + + return 0; +} + + +/*! + * \brief pixSetPadBitsBand() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] by starting y value of band + * \param[in] bh height of band + * \param[in] val 0 or 1 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The pad bits are the bits that expand each scanline to a
+ *          multiple of 32 bits.  They are usually not used in
+ *          image processing operations.  When boundary conditions
+ *          are important, as in seedfill, they must be set properly.
+ *      (2) This sets the value of the pad bits (if any) in the last
+ *          32-bit word in each scanline, within the specified
+ *          band of raster lines.
+ *      (3) For 32 bpp pix, there are no pad bits, so this is a no-op.
+ * 
+ */ +l_ok +pixSetPadBitsBand(PIX *pix, + l_int32 by, + l_int32 bh, + l_int32 val) +{ +l_int32 i, w, h, d, wpl, endbits, fullwords; +l_uint32 mask; +l_uint32 *data, *pword; + + PROCNAME("pixSetPadBitsBand"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d == 32) /* no padding exists for 32 bpp */ + return 0; + + if (by < 0) + by = 0; + if (by >= h) + return ERROR_INT("start y not in image", procName, 1); + if (by + bh > h) + bh = h - by; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + endbits = 32 - (((l_int64)w * d) % 32); + if (endbits == 32) /* no partial word */ + return 0; + fullwords = (l_int64)w * d / 32; + + mask = rmask32[endbits]; + if (val == 0) + mask = ~mask; + + for (i = by; i < by + bh; i++) { + pword = data + i * wpl + fullwords; + if (val == 0) /* clear */ + *pword = *pword & mask; + else /* set */ + *pword = *pword | mask; + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Set border pixels * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetOrClearBorder() + * + * \param[in] pixs all depths + * \param[in] left, right, top, bot amount to set or clear + * \param[in] op operation PIX_SET or PIX_CLR + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The border region is defined to be the region in the
+ *          image within a specific distance of each edge.  Here, we
+ *          allow the pixels within a specified distance of each
+ *          edge to be set independently.  This either sets or
+ *          clears all pixels in the border region.
+ *      (2) For binary images, use PIX_SET for black and PIX_CLR for white.
+ *      (3) For grayscale or color images, use PIX_SET for white
+ *          and PIX_CLR for black.
+ * 
+ */ +l_ok +pixSetOrClearBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_int32 op) +{ +l_int32 w, h; + + PROCNAME("pixSetOrClearBorder"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (op != PIX_SET && op != PIX_CLR) + return ERROR_INT("op must be PIX_SET or PIX_CLR", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + pixRasterop(pixs, 0, 0, left, h, op, NULL, 0, 0); + pixRasterop(pixs, w - right, 0, right, h, op, NULL, 0, 0); + pixRasterop(pixs, 0, 0, w, top, op, NULL, 0, 0); + pixRasterop(pixs, 0, h - bot, w, bot, op, NULL, 0, 0); + + return 0; +} + + +/*! + * \brief pixSetBorderVal() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] left, right, top, bot amount to set + * \param[in] val value to set at each border pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The border region is defined to be the region in the
+ *          image within a specific distance of each edge.  Here, we
+ *          allow the pixels within a specified distance of each
+ *          edge to be set independently.  This sets the pixels
+ *          in the border region to the given input value.
+ *      (2) For efficiency, use pixSetOrClearBorder() if
+ *          you're setting the border to either black or white.
+ *      (3) If d != 32, the input value should be masked off
+ *          to the appropriate number of least significant bits.
+ *      (4) The code is easily generalized for 2 or 4 bpp.
+ * 
+ */ +l_ok +pixSetBorderVal(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_uint32 val) +{ +l_int32 w, h, d, wpls, i, j, bstart, rstart; +l_uint32 *datas, *lines; + + PROCNAME("pixSetBorderVal"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("depth must be 8, 16 or 32 bpp", procName, 1); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (d == 8) { + val &= 0xff; + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + SET_DATA_BYTE(lines, j, val); + for (j = rstart; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + } else if (d == 16) { + val &= 0xffff; + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + SET_DATA_TWO_BYTES(lines, j, val); + for (j = rstart; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + } else { /* d == 32 */ + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + *(lines + j) = val; + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + *(lines + j) = val; + for (j = rstart; j < w; j++) + *(lines + j) = val; + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + *(lines + j) = val; + } + } + + return 0; +} + + +/*! + * \brief pixSetBorderRingVal() + * + * \param[in] pixs any depth; cmap OK + * \param[in] dist distance from outside; must be > 0; first ring is 1 + * \param[in] val value to set at each border pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The rings are single-pixel-wide rectangular sets of
+ *          pixels at a given distance from the edge of the pix.
+ *          This sets all pixels in a given ring to a value.
+ * 
+ */ +l_ok +pixSetBorderRingVal(PIX *pixs, + l_int32 dist, + l_uint32 val) +{ +l_int32 w, h, d, i, j, xend, yend; + + PROCNAME("pixSetBorderRingVal"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (dist < 1) + return ERROR_INT("dist must be > 0", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (w < 2 * dist + 1 || h < 2 * dist + 1) + return ERROR_INT("ring doesn't exist", procName, 1); + if (d < 32 && (val >= (1 << d))) + return ERROR_INT("invalid pixel value", procName, 1); + + xend = w - dist; + yend = h - dist; + for (j = dist - 1; j <= xend; j++) + pixSetPixel(pixs, j, dist - 1, val); + for (j = dist - 1; j <= xend; j++) + pixSetPixel(pixs, j, yend, val); + for (i = dist - 1; i <= yend; i++) + pixSetPixel(pixs, dist - 1, i, val); + for (i = dist - 1; i <= yend; i++) + pixSetPixel(pixs, xend, i, val); + + return 0; +} + + +/*! + * \brief pixSetMirroredBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels to set + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This applies what is effectively mirror boundary conditions
+ *          to a border region in the image.  It is in-place.
+ *      (2) This is useful for setting pixels near the border to a
+ *          value representative of the near pixels to the interior.
+ *      (3) The general pixRasterop() is used for an in-place operation here
+ *          because there is no overlap between the src and dest rectangles.
+ * 
+ */ +l_ok +pixSetMirroredBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; + + PROCNAME("pixSetMirroredBorder"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + for (j = 0; j < left; j++) + pixRasterop(pixs, left - 1 - j, top, 1, h - top - bot, PIX_SRC, + pixs, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixs, w - right + j, top, 1, h - top - bot, PIX_SRC, + pixs, w - right - 1 - j, top); + for (i = 0; i < top; i++) + pixRasterop(pixs, 0, top - 1 - i, w, 1, PIX_SRC, + pixs, 0, top + i); + for (i = 0; i < bot; i++) + pixRasterop(pixs, 0, h - bot + i, w, 1, PIX_SRC, + pixs, 0, h - bot - 1 - i); + + return 0; +} + + +/*! + * \brief pixCopyBorder() + * + * \param[in] pixd all depths; colormap ok; can be NULL + * \param[in] pixs same depth and size as pixd + * \param[in] left, right, top, bot number of pixels to copy + * \return pixd, or NULL on error if pixd is not defined + * + *
+ * Notes:
+ *      (1) pixd can be null, but otherwise it must be the same size
+ *          and depth as pixs.  Always returns pixd.
+ *      (2) This is useful in situations where by setting a few border
+ *          pixels we can avoid having to copy all pixels in pixs into
+ *          pixd as an initialization step for some operation.
+ *          Nevertheless, for safety, if making a new pixd, all the
+ *          non-border pixels are initialized to 0.
+ * 
+ */ +PIX * +pixCopyBorder(PIX *pixd, + PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 w, h; + + PROCNAME("pixCopyBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + + if (pixd) { + if (pixd == pixs) { + L_WARNING("same: nothing to do\n", procName); + return pixd; + } else if (!pixSizesEqual(pixs, pixd)) { + return (PIX *)ERROR_PTR("pixs and pixd sizes differ", + procName, pixd); + } + } else { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, pixd); + } + + pixGetDimensions(pixs, &w, &h, NULL); + pixRasterop(pixd, 0, 0, left, h, PIX_SRC, pixs, 0, 0); + pixRasterop(pixd, w - right, 0, right, h, PIX_SRC, pixs, w - right, 0); + pixRasterop(pixd, 0, 0, w, top, PIX_SRC, pixs, 0, 0); + pixRasterop(pixd, 0, h - bot, w, bot, PIX_SRC, pixs, 0, h - bot); + return pixd; +} + + + +/*-------------------------------------------------------------* + * Add and remove border * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] npix number of pixels to be added to each side + * \param[in] val value of added border pixels + * \return pixd with the added exterior pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixGetBlackOrWhiteVal() for values of black and white pixels.
+ * 
+ */ +PIX * +pixAddBorder(PIX *pixs, + l_int32 npix, + l_uint32 val) +{ + PROCNAME("pixAddBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (npix == 0) + return pixClone(pixs); + return pixAddBorderGeneral(pixs, npix, npix, npix, npix, val); +} + + +/*! + * \brief pixAddBlackOrWhiteBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL + * \return pixd with the added exterior pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixGetBlackOrWhiteVal() for possible side effect (adding
+ *          a color to a colormap).
+ *      (2) The only complication is that pixs may have a colormap.
+ *          There are two ways to add the black or white border:
+ *          (a) As done here (simplest, most efficient)
+ *          (b) l_int32 ws, hs, d;
+ *              pixGetDimensions(pixs, &ws, &hs, &d);
+ *              Pix *pixd = pixCreate(ws + left + right, hs + top + bot, d);
+ *              PixColormap *cmap = pixGetColormap(pixs);
+ *              if (cmap != NULL)
+ *                  pixSetColormap(pixd, pixcmapCopy(cmap));
+ *              pixSetBlackOrWhite(pixd, L_SET_WHITE);  // uses cmap
+ *              pixRasterop(pixd, left, top, ws, hs, PIX_SET, pixs, 0, 0);
+ * 
+ */ +PIX * +pixAddBlackOrWhiteBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_int32 op) +{ +l_uint32 val; + + PROCNAME("pixAddBlackOrWhiteBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) + return (PIX *)ERROR_PTR("invalid op", procName, NULL); + + pixGetBlackOrWhiteVal(pixs, op, &val); + return pixAddBorderGeneral(pixs, left, right, top, bot, val); +} + + +/*! + * \brief pixAddBorderGeneral() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \param[in] val value of added border pixels + * \return pixd with the added exterior pixels, or NULL on error + * + *
+ * Notes:
+ *      (1) For binary images:
+ *             white:  val = 0
+ *             black:  val = 1
+ *          For grayscale images:
+ *             white:  val = 2 ** d - 1
+ *             black:  val = 0
+ *          For rgb color images:
+ *             white:  val = 0xffffff00
+ *             black:  val = 0
+ *          For colormapped images, set val to the appropriate colormap index.
+ *      (2) If the added border is either black or white, you can use
+ *             pixAddBlackOrWhiteBorder()
+ *          The black and white values for all images can be found with
+ *             pixGetBlackOrWhiteVal()
+ *          which, if pixs is cmapped, may add an entry to the colormap.
+ *          Alternatively, if pixs has a colormap, you can find the index
+ *          of the pixel whose intensity is closest to white or black:
+ *             white: pixcmapGetRankIntensity(cmap, 1.0, &index);
+ *             black: pixcmapGetRankIntensity(cmap, 0.0, &index);
+ *          and use that for val.
+ * 
+ */ +PIX * +pixAddBorderGeneral(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_uint32 val) +{ +l_int32 ws, hs, wd, hd, d, maxval, op; +PIX *pixd; + + PROCNAME("pixAddBorderGeneral"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (left < 0 || right < 0 || top < 0 || bot < 0) + return (PIX *)ERROR_PTR("negative border added!", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, &d); + wd = ws + left + right; + hd = hs + top + bot; + if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + + /* Set the new border pixels */ + maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; + op = UNDEF; + if (val == 0) + op = PIX_CLR; + else if (val >= maxval) + op = PIX_SET; + if (op == UNDEF) { + pixSetAllArbitrary(pixd, val); + } else { /* just set or clear the border pixels */ + pixRasterop(pixd, 0, 0, left, hd, op, NULL, 0, 0); + pixRasterop(pixd, wd - right, 0, right, hd, op, NULL, 0, 0); + pixRasterop(pixd, 0, 0, wd, top, op, NULL, 0, 0); + pixRasterop(pixd, 0, hd - bot, wd, bot, op, NULL, 0, 0); + } + + /* Copy pixs into the interior */ + pixRasterop(pixd, left, top, ws, hs, PIX_SRC, pixs, 0, 0); + return pixd; +} + + +/*! + * \brief pixRemoveBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] npix number to be removed from each of the 4 sides + * \return pixd with pixels removed around border, or NULL on error + */ +PIX * +pixRemoveBorder(PIX *pixs, + l_int32 npix) +{ + PROCNAME("pixRemoveBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (npix == 0) + return pixClone(pixs); + return pixRemoveBorderGeneral(pixs, npix, npix, npix, npix); +} + + +/*! + * \brief pixRemoveBorderGeneral() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels removed + * \return pixd with pixels removed around border, or NULL on error + */ +PIX * +pixRemoveBorderGeneral(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd, d; +PIX *pixd; + + PROCNAME("pixRemoveBorderGeneral"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (left < 0 || right < 0 || top < 0 || bot < 0) + return (PIX *)ERROR_PTR("negative border removed!", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, &d); + wd = ws - left - right; + hd = hs - top - bot; + if (wd <= 0) + return (PIX *)ERROR_PTR("width must be > 0", procName, NULL); + if (hd <= 0) + return (PIX *)ERROR_PTR("height must be > 0", procName, NULL); + if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopySpp(pixd, pixs); + pixCopyColormap(pixd, pixs); + + pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, left, top); + if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) + pixShiftAndTransferAlpha(pixd, pixs, -left, -top); + return pixd; +} + + +/*! + * \brief pixRemoveBorderToSize() + * + * \param[in] pixs all depths; colormap ok + * \param[in] wd target width; use 0 if only removing from height + * \param[in] hd target height; use 0 if only removing from width + * \return pixd with pixels removed around border, or NULL on error + * + *
+ * Notes:
+ *      (1) Removes pixels as evenly as possible from the sides of the
+ *          image, leaving the central part.
+ *      (2) Returns clone if no pixels requested removed, or the target
+ *          sizes are larger than the image.
+ * 
+ */ +PIX * +pixRemoveBorderToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h, top, bot, left, right, delta; + + PROCNAME("pixRemoveBorderToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((wd <= 0 || wd >= w) && (hd <= 0 || hd >= h)) + return pixClone(pixs); + + left = right = (w - wd) / 2; + delta = w - 2 * left - wd; + right += delta; + top = bot = (h - hd) / 2; + delta = h - hd - 2 * top; + bot += delta; + if (wd <= 0 || wd > w) + left = right = 0; + else if (hd <= 0 || hd > h) + top = bot = 0; + + return pixRemoveBorderGeneral(pixs, left, right, top, bot); +} + + +/*! + * \brief pixAddMirroredBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies what is effectively mirror boundary conditions.
+ *          For the added border pixels in pixd, the pixels in pixs
+ *          near the border are mirror-copied into the border region.
+ *      (2) This is useful for avoiding special operations near
+ *          boundaries when doing image processing operations
+ *          such as rank filters and convolution.  In use, one first
+ *          adds mirrored pixels to each side of the image.  The number
+ *          of pixels added on each side is half the filter dimension.
+ *          Then the image processing operations proceed over a
+ *          region equal to the size of the original image, and
+ *          write directly into a dest pix of the same size as pixs.
+ *      (3) The general pixRasterop() is used for an in-place operation here
+ *          because there is no overlap between the src and dest rectangles.
+ * 
+ */ +PIX * +pixAddMirroredBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +PIX *pixd; + + PROCNAME("pixAddMirroredBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", procName, NULL); + + /* Set pixels on left, right, top and bottom, in that order */ + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + for (j = 0; j < left; j++) + pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, + pixd, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, + pixd, left + w - 1 - j, top); + for (i = 0; i < top; i++) + pixRasterop(pixd, 0, top - 1 - i, left + w + right, 1, PIX_SRC, + pixd, 0, top + i); + for (i = 0; i < bot; i++) + pixRasterop(pixd, 0, top + h + i, left + w + right, 1, PIX_SRC, + pixd, 0, top + h - 1 - i); + + return pixd; +} + + +/*! + * \brief pixAddRepeatedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies a repeated border, as if the central part of
+ *          the image is tiled over the plane.  So, for example, the
+ *          pixels in the left border come from the right side of the image.
+ *      (2) The general pixRasterop() is used for an in-place operation here
+ *          because there is no overlap between the src and dest rectangles.
+ * 
+ */ +PIX * +pixAddRepeatedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 w, h; +PIX *pixd; + + PROCNAME("pixAddRepeatedBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", procName, NULL); + + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + + /* Set pixels on left, right, top and bottom, in that order */ + pixRasterop(pixd, 0, top, left, h, PIX_SRC, pixd, w, top); + pixRasterop(pixd, left + w, top, right, h, PIX_SRC, pixd, left, top); + pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); + pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); + + return pixd; +} + + +/*! + * \brief pixAddMixedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies mirrored boundary conditions horizontally
+ *          and repeated b.c. vertically.
+ *      (2) It is specifically used for avoiding special operations
+ *          near boundaries when convolving a hue-saturation histogram
+ *          with a given window size.  The repeated b.c. are used
+ *          vertically for hue, and the mirrored b.c. are used
+ *          horizontally for saturation.  The number of pixels added
+ *          on each side is approximately (but not quite) half the
+ *          filter dimension.  The image processing operations can
+ *          then proceed over a region equal to the size of the original
+ *          image, and write directly into a dest pix of the same
+ *          size as pixs.
+ *      (3) The general pixRasterop() can be used for an in-place
+ *          operation here because there is no overlap between the
+ *          src and dest rectangles.
+ * 
+ */ +PIX * +pixAddMixedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 j, w, h; +PIX *pixd; + + PROCNAME("pixAddMixedBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", procName, NULL); + + /* Set mirrored pixels on left and right; + * then set repeated pixels on top and bottom. */ + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + for (j = 0; j < left; j++) + pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, + pixd, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, + pixd, left + w - 1 - j, top); + pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); + pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); + + return pixd; +} + + +/*! + * \brief pixAddContinuedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot pixels on each side to be added + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This adds pixels on each side whose values are equal to
+ *          the value on the closest boundary pixel.
+ * 
+ */ +PIX * +pixAddContinuedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +PIX *pixd; + + PROCNAME("pixAddContinuedBorder"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + pixGetDimensions(pixs, &w, &h, NULL); + for (j = 0; j < left; j++) + pixRasterop(pixd, j, top, 1, h, PIX_SRC, pixd, left, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, + PIX_SRC, pixd, left + w - 1, top); + for (i = 0; i < top; i++) + pixRasterop(pixd, 0, i, left + w + right, 1, PIX_SRC, pixd, 0, top); + for (i = 0; i < bot; i++) + pixRasterop(pixd, 0, top + h + i, left + w + right, 1, + PIX_SRC, pixd, 0, top + h - 1); + + return pixd; +} + + +/*-------------------------------------------------------------------* + * Helper functions using alpha * + *-------------------------------------------------------------------*/ +/*! + * \brief pixShiftAndTransferAlpha() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 32 bpp + * \param[in] shiftx, shifty + * \return 0 if OK; 1 on error + */ +l_ok +pixShiftAndTransferAlpha(PIX *pixd, + PIX *pixs, + l_float32 shiftx, + l_float32 shifty) +{ +l_int32 w, h; +PIX *pix1, *pix2; + + PROCNAME("pixShiftAndTransferAlpha"); + + if (!pixs || !pixd) + return ERROR_INT("pixs and pixd not both defined", procName, 1); + if (pixGetDepth(pixs) != 32 || pixGetSpp(pixs) != 4) + return ERROR_INT("pixs not 32 bpp and 4 spp", procName, 1); + if (pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not 32 bpp", procName, 1); + + if (shiftx == 0 && shifty == 0) { + pixCopyRGBComponent(pixd, pixs, L_ALPHA_CHANNEL); + return 0; + } + + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pixGetDimensions(pixd, &w, &h, NULL); + pix2 = pixCreate(w, h, 8); + pixRasterop(pix2, 0, 0, w, h, PIX_SRC, pix1, -shiftx, -shifty); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + return 0; +} + + +/*! + * \brief pixDisplayLayersRGBA() + * + * \param[in] pixs cmap or 32 bpp rgba + * \param[in] val 32 bit unsigned color to use as background + * \param[in] maxw max output image width; 0 for no scaling + * \return pixd showing various image views, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %val == 0xffffff00 for white background.
+ *      (2) Three views are given:
+ *           ~ the image with a fully opaque alpha
+ *           ~ the alpha layer
+ *           ~ the image as it would appear with a white background.
+ * 
+ */ +PIX * +pixDisplayLayersRGBA(PIX *pixs, + l_uint32 val, + l_int32 maxw) +{ +l_int32 w, width; +l_float32 scalefact; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; +PIXCMAP *cmap; + + PROCNAME("pixDisplayLayersRGBA"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + cmap = pixGetColormap(pixs); + if (!cmap && !(pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4)) + return (PIX *)ERROR_PTR("pixs not cmap and not 32 bpp rgba", + procName, NULL); + if ((w = pixGetWidth(pixs)) == 0) + return (PIX *)ERROR_PTR("pixs width 0 !!", procName, NULL); + + if (cmap) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA); + else + pix1 = pixCopy(NULL, pixs); + + /* Scale if necessary so the output width is not larger than maxw */ + scalefact = (maxw == 0) ? 1.0 : L_MIN(1.0, (l_float32)(maxw) / w); + width = (l_int32)(scalefact * w); + + pixa = pixaCreate(3); + pixSetSpp(pix1, 3); + pixaAddPix(pixa, pix1, L_INSERT); /* show the rgb values */ + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixa, pix2, L_INSERT); /* show the alpha channel */ + pixDestroy(&pix1); + pix1 = pixAlphaBlendUniform(pixs, (val & 0xffffff00)); + pixaAddPix(pixa, pix1, L_INSERT); /* with %val color bg showing */ + pixd = pixaDisplayTiledInRows(pixa, 32, width, scalefact, 0, 25, 2); + pixaDestroy(&pixa); + return pixd; +} + + +/*-------------------------------------------------------------* + * Color sample setting and extraction * + *-------------------------------------------------------------*/ +/*! + * \brief pixCreateRGBImage() + * + * \param[in] pixr 8 bpp red pix + * \param[in] pixg 8 bpp green pix + * \param[in] pixb 8 bpp blue pix + * \return 32 bpp pix, interleaved with 4 samples/pixel, + * or NULL on error + * + *
+ * Notes:
+ *      (1) the 4th byte, sometimes called the "alpha channel",
+ *          and which is often used for blending between different
+ *          images, is left with 0 value.
+ *      (2) see Note (4) in pix.h for details on storage of
+ *          8-bit samples within each 32-bit word.
+ *      (3) This implementation, setting the r, g and b components
+ *          sequentially, is much faster than setting them in parallel
+ *          by constructing an RGB dest pixel and writing it to dest.
+ *          The reason is there are many more cache misses when reading
+ *          from 3 input images simultaneously.
+ * 
+ */ +PIX * +pixCreateRGBImage(PIX *pixr, + PIX *pixg, + PIX *pixb) +{ +l_int32 wr, wg, wb, hr, hg, hb, dr, dg, db; +PIX *pixd; + + PROCNAME("pixCreateRGBImage"); + + if (!pixr) + return (PIX *)ERROR_PTR("pixr not defined", procName, NULL); + if (!pixg) + return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); + if (!pixb) + return (PIX *)ERROR_PTR("pixb not defined", procName, NULL); + pixGetDimensions(pixr, &wr, &hr, &dr); + pixGetDimensions(pixg, &wg, &hg, &dg); + pixGetDimensions(pixb, &wb, &hb, &db); + if (dr != 8 || dg != 8 || db != 8) + return (PIX *)ERROR_PTR("input pix not all 8 bpp", procName, NULL); + if (wr != wg || wr != wb) + return (PIX *)ERROR_PTR("widths not the same", procName, NULL); + if (hr != hg || hr != hb) + return (PIX *)ERROR_PTR("heights not the same", procName, NULL); + + if ((pixd = pixCreate(wr, hr, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixr); + pixSetRGBComponent(pixd, pixr, COLOR_RED); + pixSetRGBComponent(pixd, pixg, COLOR_GREEN); + pixSetRGBComponent(pixd, pixb, COLOR_BLUE); + + return pixd; +} + + +/*! + * \brief pixGetRGBComponent() + * + * \param[in] pixs 32 bpp, or colormapped + * \param[in] comp one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE, + * L_ALPHA_CHANNEL} + * \return pixd the selected 8 bpp component image of the + * input 32 bpp image or NULL on error + * + *
+ * Notes:
+ *      (1) Three calls to this function generate the r, g and b 8 bpp
+ *          component images.  This is much faster than generating the
+ *          three images in parallel, by extracting a src pixel and setting
+ *          the pixels of each component image from it.  The reason is
+ *          there are many more cache misses when writing to three
+ *          output images simultaneously.
+ * 
+ */ +PIX * +pixGetRGBComponent(PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixGetRGBComponent"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return pixGetRGBComponentCmap(pixs, comp); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return (PIX *)ERROR_PTR("invalid comp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines + j, comp); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixSetRGBComponent() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 8 bpp + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, + * COLOR_BLUE, L_ALPHA_CHANNEL} + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This places the 8 bpp pixel in pixs into the
+ *          specified component (properly interleaved) in pixd,
+ *      (2) The two images are registered to the UL corner; the sizes
+ *          need not be the same, but a warning is issued if they differ.
+ * 
+ */ +l_ok +pixSetRGBComponent(PIX *pixd, + PIX *pixs, + l_int32 comp) +{ +l_uint8 srcbyte; +l_int32 i, j, w, h, ws, hs, wd, hd; +l_int32 wpls, wpld; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; + + PROCNAME("pixSetRGBComponent"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not 32 bpp", procName, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not 8 bpp", procName, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid comp", procName, 1); + pixGetDimensions(pixs, &ws, &hs, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + if (ws != wd || hs != hd) + L_WARNING("images sizes not equal\n", procName); + w = L_MIN(ws, wd); + h = L_MIN(hs, hd); + if (comp == L_ALPHA_CHANNEL) + pixSetSpp(pixd, 4); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + srcbyte = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(lined + j, comp, srcbyte); + } + } + + return 0; +} + + +/*! + * \brief pixGetRGBComponentCmap() + * + * \param[in] pixs colormapped + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, COLOR_BLUE} + * \return pixd the selected 8 bpp component image of the + * input cmapped image, or NULL on error + * + *
+ * Notes:
+ *      (1) In leptonica, we do not support alpha in colormaps.
+ * 
+ */ +PIX * +pixGetRGBComponentCmap(PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, val, index; +l_int32 wplc, wpld; +l_uint32 *linec, *lined; +l_uint32 *datac, *datad; +PIX *pixc, *pixd; +PIXCMAP *cmap; +RGBA_QUAD *cta; + + PROCNAME("pixGetRGBComponentCmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if ((cmap = pixGetColormap(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped", procName, NULL); + if (comp == L_ALPHA_CHANNEL) + return (PIX *)ERROR_PTR("alpha in cmaps not supported", procName, NULL); + if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE) + return (PIX *)ERROR_PTR("invalid comp", procName, NULL); + + /* If not 8 bpp, make a cmapped 8 bpp pix */ + if (pixGetDepth(pixs) == 8) + pixc = pixClone(pixs); + else + pixc = pixConvertTo8(pixs, TRUE); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreateNoInit(w, h, 8)) == NULL) { + pixDestroy(&pixc); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + wplc = pixGetWpl(pixc); + wpld = pixGetWpl(pixd); + datac = pixGetData(pixc); + datad = pixGetData(pixd); + cta = (RGBA_QUAD *)cmap->array; + + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + lined = datad + i * wpld; + if (comp == COLOR_RED) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].red; + SET_DATA_BYTE(lined, j, val); + } + } else if (comp == COLOR_GREEN) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].green; + SET_DATA_BYTE(lined, j, val); + } + } else if (comp == COLOR_BLUE) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].blue; + SET_DATA_BYTE(lined, j, val); + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixCopyRGBComponent() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 32 bpp + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, + * COLOR_BLUE, L_ALPHA_CHANNEL} + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The two images are registered to the UL corner.  The sizes
+ *          are usually the same, and a warning is issued if they differ.
+ * 
+ */ +l_ok +pixCopyRGBComponent(PIX *pixd, + PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, ws, hs, wd, hd, val; +l_int32 wpls, wpld; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; + + PROCNAME("pixCopyRGBComponent"); + + if (!pixd && pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not defined or not 32 bpp", procName, 1); + if (!pixs && pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid component", procName, 1); + pixGetDimensions(pixs, &ws, &hs, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + if (ws != wd || hs != hd) + L_WARNING("images sizes not equal\n", procName); + w = L_MIN(ws, wd); + h = L_MIN(hs, hd); + if (comp == L_ALPHA_CHANNEL) + pixSetSpp(pixd, 4); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines + j, comp); + SET_DATA_BYTE(lined + j, comp, val); + } + } + return 0; +} + + +/*! + * \brief composeRGBPixel() + * + * \param[in] rval, gval, bval + * \param[out] ppixel 32-bit pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) All channels are 8 bits: the input values must be between
+ *          0 and 255.  For speed, this is not enforced by masking
+ *          with 0xff before shifting.
+ *      (2) A slower implementation uses macros:
+ *            SET_DATA_BYTE(ppixel, COLOR_RED, rval);
+ *            SET_DATA_BYTE(ppixel, COLOR_GREEN, gval);
+ *            SET_DATA_BYTE(ppixel, COLOR_BLUE, bval);
+ * 
+ */ +l_ok +composeRGBPixel(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_uint32 *ppixel) +{ + PROCNAME("composeRGBPixel"); + + if (!ppixel) + return ERROR_INT("&pixel not defined", procName, 1); + + *ppixel = ((l_uint32)rval << L_RED_SHIFT) | + ((l_uint32)gval << L_GREEN_SHIFT) | + ((l_uint32)bval << L_BLUE_SHIFT); + return 0; +} + + +/*! + * \brief composeRGBAPixel() + * + * \param[in] rval, gval, bval, aval + * \param[out] ppixel 32-bit pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) All channels are 8 bits: the input values must be between
+ *          0 and 255.  For speed, this is not enforced by masking
+ *          with 0xff before shifting.
+ * 
+ */ +l_ok +composeRGBAPixel(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 aval, + l_uint32 *ppixel) +{ + PROCNAME("composeRGBAPixel"); + + if (!ppixel) + return ERROR_INT("&pixel not defined", procName, 1); + + *ppixel = ((l_uint32)rval << L_RED_SHIFT) | + ((l_uint32)gval << L_GREEN_SHIFT) | + ((l_uint32)bval << L_BLUE_SHIFT) | + aval; + return 0; +} + + +/*! + * \brief extractRGBValues() + * + * \param[in] pixel 32 bit + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \return void + * + *
+ * Notes:
+ *      (1) A slower implementation uses macros:
+ *             *prval = GET_DATA_BYTE(&pixel, COLOR_RED);
+ *             *pgval = GET_DATA_BYTE(&pixel, COLOR_GREEN);
+ *             *pbval = GET_DATA_BYTE(&pixel, COLOR_BLUE);
+ * 
+ */ +void +extractRGBValues(l_uint32 pixel, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ + if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; + if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; + return; +} + + +/*! + * \brief extractRGBAValues() + * + * \param[in] pixel 32 bit + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \param[out] paval [optional] alpha component + * \return void + */ +void +extractRGBAValues(l_uint32 pixel, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval, + l_int32 *paval) +{ + if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; + if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (paval) *paval = (pixel >> L_ALPHA_SHIFT) & 0xff; + return; +} + + +/*! + * \brief extractMinMaxComponent() + * + * \param[in] pixel 32 bpp RGB + * \param[in] type L_CHOOSE_MIN or L_CHOOSE_MAX + * \return component in range [0 ... 255], or NULL on error + */ +l_int32 +extractMinMaxComponent(l_uint32 pixel, + l_int32 type) +{ +l_int32 rval, gval, bval, val; + + extractRGBValues(pixel, &rval, &gval, &bval); + if (type == L_CHOOSE_MIN) { + val = L_MIN(rval, gval); + val = L_MIN(val, bval); + } else { /* type == L_CHOOSE_MAX */ + val = L_MAX(rval, gval); + val = L_MAX(val, bval); + } + return val; +} + + +/*! + * \brief pixGetRGBLine() + * + * \param[in] pixs 32 bpp + * \param[in] row + * \param[in] bufr array of red samples; size w bytes + * \param[in] bufg array of green samples; size w bytes + * \param[in] bufb array of blue samples; size w bytes + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This puts rgb components from the input line in pixs
+ *          into the given buffers.
+ * 
+ */ +l_ok +pixGetRGBLine(PIX *pixs, + l_int32 row, + l_uint8 *bufr, + l_uint8 *bufg, + l_uint8 *bufb) +{ +l_uint32 *lines; +l_int32 j, w, h; +l_int32 wpls; + + PROCNAME("pixGetRGBLine"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (!bufr || !bufg || !bufb) + return ERROR_INT("buffer not defined", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + if (row < 0 || row >= h) + return ERROR_INT("row out of bounds", procName, 1); + wpls = pixGetWpl(pixs); + lines = pixGetData(pixs) + row * wpls; + + for (j = 0; j < w; j++) { + bufr[j] = GET_DATA_BYTE(lines + j, COLOR_RED); + bufg[j] = GET_DATA_BYTE(lines + j, COLOR_GREEN); + bufb[j] = GET_DATA_BYTE(lines + j, COLOR_BLUE); + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Pixel endian conversion * + *-------------------------------------------------------------*/ +/*! + * \brief pixEndianByteSwapNew() + * + * \param[in] pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is used to convert the data in a pix to a
+ *          serialized byte buffer in raster order, and, for RGB,
+ *          in order RGBA.  This requires flipping bytes within
+ *          each 32-bit word for little-endian platforms, because the
+ *          words have a MSB-to-the-left rule, whereas byte raster-order
+ *          requires the left-most byte in each word to be byte 0.
+ *          For big-endians, no swap is necessary, so this returns a clone.
+ *      (2) Unlike pixEndianByteSwap(), which swaps the bytes in-place,
+ *          this returns a new pix (or a clone).  We provide this
+ *          because often when serialization is done, the source
+ *          pix needs to be restored to canonical little-endian order,
+ *          and this requires a second byte swap.  In such a situation,
+ *          it is twice as fast to make a new pix in big-endian order,
+ *          use it, and destroy it.
+ * 
+ */ +PIX * +pixEndianByteSwapNew(PIX *pixs) +{ +l_uint32 *datas, *datad; +l_int32 i, j, h, wpl; +l_uint32 word; +PIX *pixd; + + PROCNAME("pixEndianByteSwapNew"); + +#ifdef L_BIG_ENDIAN + + return pixClone(pixs); + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + datas = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + + return pixd; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianByteSwap() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used on little-endian platforms to swap
+ *          the bytes within a word; bytes 0 and 3 are swapped,
+ *          and bytes 1 and 2 are swapped.
+ *      (2) This is required for little-endians in situations
+ *          where we convert from a serialized byte order that is
+ *          in raster order, as one typically has in file formats,
+ *          to one with MSB-to-the-left in each 32-bit word, or v.v.
+ *          See pix.h for a description of the canonical format
+ *          (MSB-to-the left) that is used for both little-endian
+ *          and big-endian platforms.   For big-endians, the
+ *          MSB-to-the-left word order has the bytes in raster
+ *          order when serialized, so no byte flipping is required.
+ * 
+ */ +l_ok +pixEndianByteSwap(PIX *pixs) +{ +l_uint32 *data; +l_int32 i, j, h, wpl; +l_uint32 word; + + PROCNAME("pixEndianByteSwap"); + +#ifdef L_BIG_ENDIAN + + return 0; + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief lineEndianByteSwap() + * + * \param[in] datad dest byte array data, reordered on little-endians + * \param[in] datas a src line of pix data) + * \param[in] wpl number of 32 bit words in the line + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used on little-endian platforms to swap
+ *          the bytes within each word in the line of image data.
+ *          Bytes 0 <==> 3 and 1 <==> 2 are swapped in the dest
+ *          byte array data8d, relative to the pix data in datas.
+ *      (2) The bytes represent 8 bit pixel values.  They are swapped
+ *          for little endians so that when the dest array datad
+ *          is addressed by bytes, the pixels are chosen sequentially
+ *          from left to right in the image.
+ * 
+ */ +l_int32 +lineEndianByteSwap(l_uint32 *datad, + l_uint32 *datas, + l_int32 wpl) +{ +l_int32 j; +l_uint32 word; + + PROCNAME("lineEndianByteSwap"); + + if (!datad || !datas) + return ERROR_INT("datad and datas not both defined", procName, 1); + +#ifdef L_BIG_ENDIAN + + memcpy(datad, datas, 4 * wpl); + return 0; + +#else /* L_LITTLE_ENDIAN */ + + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianTwoByteSwapNew() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used on little-endian platforms to swap the
+ *          2-byte entities within a 32-bit word.
+ *      (2) This is equivalent to a full byte swap, as performed
+ *          by pixEndianByteSwap(), followed by byte swaps in
+ *          each of the 16-bit entities separately.
+ *      (3) Unlike pixEndianTwoByteSwap(), which swaps the shorts in-place,
+ *          this returns a new pix (or a clone).  We provide this
+ *          to avoid having to swap twice in situations where the input
+ *          pix must be restored to canonical little-endian order.
+ * 
+ */ +PIX * +pixEndianTwoByteSwapNew(PIX *pixs) +{ +l_uint32 *datas, *datad; +l_int32 i, j, h, wpl; +l_uint32 word; +PIX *pixd; + + PROCNAME("pixEndianTwoByteSwapNew"); + +#ifdef L_BIG_ENDIAN + + return pixClone(pixs); + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + datas = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word << 16) | (word >> 16); + } + } + + return pixd; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianTwoByteSwap() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used on little-endian platforms to swap the
+ *          2-byte entities within a 32-bit word.
+ *      (2) This is equivalent to a full byte swap, as performed
+ *          by pixEndianByteSwap(), followed by byte swaps in
+ *          each of the 16-bit entities separately.
+ * 
+ */ +l_ok +pixEndianTwoByteSwap(PIX *pixs) +{ +l_uint32 *data; +l_int32 i, j, h, wpl; +l_uint32 word; + + PROCNAME("pixEndianTwoByteSwap"); + +#ifdef L_BIG_ENDIAN + + return 0; + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, data++) { + word = *data; + *data = (word << 16) | (word >> 16); + } + } + + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*-------------------------------------------------------------* + * Extract raster data as binary string * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetRasterData() + * + * \param[in] pixs 1, 8, 32 bpp + * \param[out] pdata raster data in memory + * \param[out] pnbytes number of bytes in data string + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns the raster data as a byte string, padded to the
+ *          byte.  For 1 bpp, the first pixel is the MSbit in the first byte.
+ *          For rgb, the bytes are in (rgb) order.  This is the format
+ *          required for flate encoding of pixels in a PostScript file.
+ * 
+ */ +l_ok +pixGetRasterData(PIX *pixs, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval; +l_int32 databpl; /* bytes for each raster line in returned data */ +l_uint8 *line, *data; /* packed data in returned array */ +l_uint32 *rline, *rdata; /* data in pix raster */ + + PROCNAME("pixGetRasterData"); + + if (pdata) *pdata = NULL; + if (pnbytes) *pnbytes = 0; + if (!pdata || !pnbytes) + return ERROR_INT("&data and &nbytes not both defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return ERROR_INT("depth not in {1,2,4,8,16,32}", procName, 1); + rdata = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (d == 1) + databpl = (w + 7) / 8; + else if (d == 2) + databpl = (w + 3) / 4; + else if (d == 4) + databpl = (w + 1) / 2; + else if (d == 8 || d == 16) + databpl = w * (d / 8); + else /* d == 32 bpp rgb */ + databpl = 3 * w; + if ((data = (l_uint8 *)LEPT_CALLOC((size_t)databpl * h, sizeof(l_uint8))) + == NULL) + return ERROR_INT("data not allocated", procName, 1); + *pdata = data; + *pnbytes = (size_t)databpl * h; + + for (i = 0; i < h; i++) { + rline = rdata + i * wpl; + line = data + i * databpl; + if (d <= 8) { + for (j = 0; j < databpl; j++) + line[j] = GET_DATA_BYTE(rline, j); + } else if (d == 16) { + for (j = 0; j < w; j++) + line[2 * j] = GET_DATA_TWO_BYTES(rline, j); + } else { /* d == 32 bpp rgb */ + for (j = 0; j < w; j++) { + extractRGBValues(rline[j], &rval, &gval, &bval); + *(line + 3 * j) = rval; + *(line + 3 * j + 1) = gval; + *(line + 3 * j + 2) = bval; + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Test alpha component opaqueness * + *-------------------------------------------------------------*/ +/*! + * \brief pixAlphaIsOpaque() + * + * \param[in] pix 32 bpp, spp == 4 + * \param[out] popaque 1 if spp == 4 and all alpha component + * values are 255 (opaque); 0 otherwise + * \return 0 if OK, 1 on error + * Notes: + * 1) On error, opaque is returned as 0 (FALSE). + */ +l_ok +pixAlphaIsOpaque(PIX *pix, + l_int32 *popaque) +{ +l_int32 w, h, wpl, i, j, alpha; +l_uint32 *data, *line; + + PROCNAME("pixAlphaIsOpaque"); + + if (!popaque) + return ERROR_INT("&opaque not defined", procName, 1); + *popaque = FALSE; + if (!pix) + return ERROR_INT("&pix not defined", procName, 1); + if (pixGetDepth(pix) != 32) + return ERROR_INT("&pix not 32 bpp", procName, 1); + if (pixGetSpp(pix) != 4) + return ERROR_INT("&pix not 4 spp", procName, 1); + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + pixGetDimensions(pix, &w, &h, NULL); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + alpha = GET_DATA_BYTE(line + j, L_ALPHA_CHANNEL); + if (alpha ^ 0xff) /* not opaque */ + return 0; + } + } + + *popaque = TRUE; + return 0; +} + + +/*-------------------------------------------------------------* + * Setup helpers for 8 bpp byte processing * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetupByteProcessing() + * + * \param[in] pix 8 bpp, no colormap + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \return line ptr array, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a simple helper for processing 8 bpp images with
+ *          direct byte access.  It can swap byte order within each word.
+ *      (2) After processing, you must call pixCleanupByteProcessing(),
+ *          which frees the lineptr array and restores byte order.
+ *      (3) Usage:
+ *              l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h);
+ *              for (i = 0; i < h; i++) {
+ *                  l_uint8 *line = lineptrs[i];
+ *                  for (j = 0; j < w; j++) {
+ *                      val = line[j];
+ *                      ...
+ *                  }
+ *              }
+ *              pixCleanupByteProcessing(pix, lineptrs);
+ * 
+ */ +l_uint8 ** +pixSetupByteProcessing(PIX *pix, + l_int32 *pw, + l_int32 *ph) +{ +l_int32 w, h; + + PROCNAME("pixSetupByteProcessing"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!pix || pixGetDepth(pix) != 8) + return (l_uint8 **)ERROR_PTR("pix not defined or not 8 bpp", + procName, NULL); + pixGetDimensions(pix, &w, &h, NULL); + if (pw) *pw = w; + if (ph) *ph = h; + if (pixGetColormap(pix)) + return (l_uint8 **)ERROR_PTR("pix has colormap", procName, NULL); + + pixEndianByteSwap(pix); + return (l_uint8 **)pixGetLinePtrs(pix, NULL); +} + + +/*! + * \brief pixCleanupByteProcessing() + * + * \param[in] pix 8 bpp, no colormap + * \param[in] lineptrs ptrs to the beginning of each raster line of data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This must be called after processing that was initiated
+ *          by pixSetupByteProcessing() has finished.
+ * 
+ */ +l_ok +pixCleanupByteProcessing(PIX *pix, + l_uint8 **lineptrs) +{ + PROCNAME("pixCleanupByteProcessing"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!lineptrs) + return ERROR_INT("lineptrs not defined", procName, 1); + + pixEndianByteSwap(pix); + LEPT_FREE(lineptrs); + return 0; +} + + +/*------------------------------------------------------------------------* + * Setting parameters for antialias masking with alpha transforms * + *------------------------------------------------------------------------*/ +/*! + * \brief l_setAlphaMaskBorder() + * + * \param[in] val1, val2 in [0.0 ... 1.0] + * \return void + * + *
+ * Notes:
+ *      (1) This sets the opacity values used to generate the two outer
+ *          boundary rings in the alpha mask associated with geometric
+ *          transforms such as pixRotateWithAlpha().
+ *      (2) The default values are val1 = 0.0 (completely transparent
+ *          in the outermost ring) and val2 = 0.5 (half transparent
+ *          in the second ring).  When the image is blended, this
+ *          completely removes the outer ring (shrinking the image by
+ *          2 in each direction), and alpha-blends with 0.5 the second ring.
+ *          Using val1 = 0.25 and val2 = 0.75 gives a slightly more
+ *          blurred border, with no perceptual difference at screen resolution.
+ *      (3) The actual mask values are found by multiplying these
+ *          normalized opacity values by 255.
+ * 
+ */ +void +l_setAlphaMaskBorder(l_float32 val1, + l_float32 val2) +{ + val1 = L_MAX(0.0, L_MIN(1.0, val1)); + val2 = L_MAX(0.0, L_MIN(1.0, val2)); + AlphaMaskBorderVals[0] = val1; + AlphaMaskBorderVals[1] = val2; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix3.c b/hgdriver/3rdparty/hgOCR/leptonica/pix3.c new file mode 100644 index 0000000..39d171f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix3.c @@ -0,0 +1,3507 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pix3.c + *
+ *
+ *    This file has these operations:
+ *
+ *      (1) Mask-directed operations
+ *      (2) Full-image bit-logical operations
+ *      (3) Foreground pixel counting operations on 1 bpp images
+ *      (4) Average and variance of pixel values
+ *      (5) Mirrored tiling of a smaller image
+ *
+ *
+ *    Masked operations
+ *           l_int32     pixSetMasked()
+ *           l_int32     pixSetMaskedGeneral()
+ *           l_int32     pixCombineMasked()
+ *           l_int32     pixCombineMaskedGeneral()
+ *           l_int32     pixPaintThroughMask()
+ *           l_int32     pixCopyWithBoxa()  -- this is boxa-directed
+ *           PIX        *pixPaintSelfThroughMask()
+ *           PIX        *pixMakeMaskFromVal()
+ *           PIX        *pixMakeMaskFromLUT()
+ *           PIX        *pixMakeArbMaskFromRGB()
+ *           PIX        *pixSetUnderTransparency()
+ *           PIX        *pixMakeAlphaFromMask()
+ *           l_int32     pixGetColorNearMaskBoundary()
+ *
+ *    One and two-image boolean operations on arbitrary depth images
+ *           PIX        *pixInvert()
+ *           PIX        *pixOr()
+ *           PIX        *pixAnd()
+ *           PIX        *pixXor()
+ *           PIX        *pixSubtract()
+ *
+ *    Foreground pixel counting in 1 bpp images
+ *           l_int32     pixZero()
+ *           l_int32     pixForegroundFraction()
+ *           NUMA       *pixaCountPixels()
+ *           l_int32     pixCountPixels()
+ *           l_int32     pixCountPixelsInRect()
+ *           NUMA       *pixCountByRow()
+ *           NUMA       *pixCountByColumn()
+ *           NUMA       *pixCountPixelsByRow()
+ *           NUMA       *pixCountPixelsByColumn()
+ *           l_int32     pixCountPixelsInRow()
+ *           NUMA       *pixGetMomentByColumn()
+ *           l_int32     pixThresholdPixelSum()
+ *           l_int32    *makePixelSumTab8()
+ *           l_int32    *makePixelCentroidTab8()
+ *
+ *    Average of pixel values in gray images
+ *           NUMA       *pixAverageByRow()
+ *           NUMA       *pixAverageByColumn()
+ *           l_int32     pixAverageInRect()
+ *
+ *    Variance of pixel values in gray images
+ *           NUMA       *pixVarianceByRow()
+ *           NUMA       *pixVarianceByColumn()
+ *           l_int32     pixVarianceInRect()
+ *
+ *    Average of absolute value of pixel differences in gray images
+ *           NUMA       *pixAbsDiffByRow()
+ *           NUMA       *pixAbsDiffByColumn()
+ *           l_int32     pixAbsDiffInRect()
+ *           l_int32     pixAbsDiffOnLine()
+ *
+ *    Count of pixels with specific value
+ *           l_int32     pixCountArbInRect()
+ *
+ *    Mirrored tiling
+ *           PIX        *pixMirroredTiling()
+ *
+ *    Representative tile near but outside region
+ *           l_int32     pixFindRepCloseTile()
+ *
+ *    Static helper function
+ *           static BOXA    *findTileRegionsForSearch()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +static BOXA *findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h, + l_int32 searchdir, l_int32 mindist, + l_int32 tsize, l_int32 ntiles); + +#ifndef NO_CONSOLE_IO +#define EQUAL_SIZE_WARNING 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Masked operations * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetMasked() + * + * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped + * \param[in] pixm [optional] 1 bpp mask; no operation if NULL + * \param[in] val value to set at each masked pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation.
+ *      (2) NOTE: For cmapped images, this calls pixSetMaskedCmap().
+ *          %val must be the 32-bit color representation of the RGB pixel.
+ *          It is not the index into the colormap!
+ *      (2) If pixm == NULL, a warning is given.
+ *      (3) This is an implicitly aligned operation, where the UL
+ *          corners of pixd and pixm coincide.  A warning is
+ *          issued if the two image sizes differ significantly,
+ *          but the operation proceeds.
+ *      (4) Each pixel in pixd that co-locates with an ON pixel
+ *          in pixm is set to the specified input value.
+ *          Other pixels in pixd are not changed.
+ *      (5) You can visualize this as painting the color through
+ *          the mask, as a stencil.
+ *      (6) If you do not want to have the UL corners aligned,
+ *          use the function pixSetMaskedGeneral(), which requires
+ *          you to input the UL corner of pixm relative to pixd.
+ *      (7) Implementation details: see comments in pixPaintThroughMask()
+ *          for when we use rasterop to do the painting.
+ * 
+ */ +l_ok +pixSetMasked(PIX *pixd, + PIX *pixm, + l_uint32 val) +{ +l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm; +l_int32 i, j, rval, gval, bval; +l_uint32 *datad, *datam, *lined, *linem; + + PROCNAME("pixSetMasked"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixm) { + L_WARNING("no mask; nothing to do\n", procName); + return 0; + } + if (pixGetColormap(pixd)) { + extractRGBValues(val, &rval, &gval, &bval); + return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval); + } + + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + d = pixGetDepth(pixd); + if (d == 1) + val &= 1; + else if (d == 2) + val &= 3; + else if (d == 4) + val &= 0x0f; + else if (d == 8) + val &= 0xff; + else if (d == 16) + val &= 0xffff; + else if (d != 32) + return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1); + pixGetDimensions(pixm, &wm, &hm, NULL); + + /* If d == 1, use rasterop; it's about 25x faster */ + if (d == 1) { + if (val == 0) { + PIX *pixmi = pixInvert(NULL, pixm); + pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0); + pixDestroy(&pixmi); + } else { /* val == 1 */ + pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0); + } + return 0; + } + + /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */ + if (d < 32 && val == 0) { + PIX *pixmd = pixUnpackBinary(pixm, d, 1); + pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0); + pixDestroy(&pixmd); + return 0; + } + + /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */ + if (d < 32 && val == ((1 << d) - 1)) { + PIX *pixmd = pixUnpackBinary(pixm, d, 0); + pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0); + pixDestroy(&pixmd); + return 0; + } + + pixGetDimensions(pixd, &wd, &hd, &d); + w = L_MIN(wd, wm); + h = L_MIN(hd, hm); + if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */ + L_WARNING("pixd and pixm sizes differ\n", procName); + + datad = pixGetData(pixd); + datam = pixGetData(pixm); + wpld = pixGetWpl(pixd); + wplm = pixGetWpl(pixm); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if (GET_DATA_BIT(linem, j)) { + switch(d) + { + case 2: + SET_DATA_DIBIT(lined, j, val); + break; + case 4: + SET_DATA_QBIT(lined, j, val); + break; + case 8: + SET_DATA_BYTE(lined, j, val); + break; + case 16: + SET_DATA_TWO_BYTES(lined, j, val); + break; + case 32: + *(lined + j) = val; + break; + default: + return ERROR_INT("shouldn't get here", procName, 1); + } + } + } + } + + return 0; +} + + +/*! + * \brief pixSetMaskedGeneral() + * + * \param[in] pixd 8, 16 or 32 bpp + * \param[in] pixm [optional] 1 bpp mask; no operation if null + * \param[in] val value to set at each masked pixel + * \param[in] x, y location of UL corner of pixm relative to pixd; + * can be negative + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place operation.
+ *      (2) Alignment is explicit.  If you want the UL corners of
+ *          the two images to be aligned, use pixSetMasked().
+ *      (3) A typical use would be painting through the foreground
+ *          of a small binary mask pixm, located somewhere on a
+ *          larger pixd.  Other pixels in pixd are not changed.
+ *      (4) You can visualize this as painting the color through
+ *          the mask, as a stencil.
+ *      (5) This uses rasterop to handle clipping and different depths of pixd.
+ *      (6) If pixd has a colormap, you should call pixPaintThroughMask().
+ *      (7) Why is this function here, if pixPaintThroughMask() does the
+ *          same thing, and does it more generally?  I've retained it here
+ *          to show how one can paint through a mask using only full
+ *          image rasterops, rather than pixel peeking in pixm and poking
+ *          in pixd.  It's somewhat baroque, but I found it amusing.
+ * 
+ */ +l_ok +pixSetMaskedGeneral(PIX *pixd, + PIX *pixm, + l_uint32 val, + l_int32 x, + l_int32 y) +{ +l_int32 wm, hm, d; +PIX *pixmu, *pixc; + + PROCNAME("pixSetMaskedGeneral"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixm) /* nothing to do */ + return 0; + + d = pixGetDepth(pixd); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1); + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + + /* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */ + if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL) + return ERROR_INT("pixmu not made", procName, 1); + + /* Clear stenciled pixels in pixd */ + pixGetDimensions(pixm, &wm, &hm, NULL); + pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0); + + /* Generate image with requisite color */ + if ((pixc = pixCreateTemplate(pixmu)) == NULL) { + pixDestroy(&pixmu); + return ERROR_INT("pixc not made", procName, 1); + } + pixSetAllArbitrary(pixc, val); + + /* Invert stencil mask, and paint color color into stencil */ + pixInvert(pixmu, pixmu); + pixAnd(pixmu, pixmu, pixc); + + /* Finally, repaint stenciled pixels, with val, in pixd */ + pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0); + + pixDestroy(&pixmu); + pixDestroy(&pixc); + return 0; +} + + +/*! + * \brief pixCombineMasked() + * + * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap + * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap + * \param[in] pixm [optional] 1 bpp mask; no operation if NULL + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation; pixd is changed.
+ *      (2) This sets each pixel in pixd that co-locates with an ON
+ *          pixel in pixm to the corresponding value of pixs.
+ *      (3) pixs and pixd must be the same depth and not colormapped.
+ *      (4) All three input pix are aligned at the UL corner, and the
+ *          operation is clipped to the intersection of all three images.
+ *      (5) If pixm == NULL, it's a no-op.
+ *      (6) Implementation: see notes in pixCombineMaskedGeneral().
+ *          For 8 bpp selective masking, you might guess that it
+ *          would be faster to generate an 8 bpp version of pixm,
+ *          using pixConvert1To8(pixm, 0, 255), and then use a
+ *          general combine operation
+ *               d = (d & ~m) | (s & m)
+ *          on a word-by-word basis.  Not always.  The word-by-word
+ *          combine takes a time that is independent of the mask data.
+ *          If the mask is relatively sparse, the byte-check method
+ *          is actually faster!
+ * 
+ */ +l_ok +pixCombineMasked(PIX *pixd, + PIX *pixs, + PIX *pixm) +{ +l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin; +l_int32 wpl, wpls, wplm, i, j, val; +l_uint32 *data, *datas, *datam, *line, *lines, *linem; +PIX *pixt; + + PROCNAME("pixCombineMasked"); + + if (!pixm) /* nothing to do */ + return 0; + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixd, &w, &h, &d); + pixGetDimensions(pixs, &ws, &hs, &ds); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (d != ds) + return ERROR_INT("pixs and pixd depths differ", procName, 1); + if (dm != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (d != 1 && d != 8 && d != 32) + return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1); + if (pixGetColormap(pixd) || pixGetColormap(pixs)) + return ERROR_INT("pixs and/or pixd is cmapped", procName, 1); + + /* For d = 1, use rasterop. pixt is the part from pixs, under + * the fg of pixm, that is to be combined with pixd. We also + * use pixt to remove all fg of pixd that is under the fg of pixm. + * Then pixt and pixd are combined by ORing. */ + wmin = L_MIN(w, L_MIN(ws, wm)); + hmin = L_MIN(h, L_MIN(hs, hm)); + if (d == 1) { + pixt = pixAnd(NULL, pixs, pixm); + pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), + pixm, 0, 0); + pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); + pixDestroy(&pixt); + return 0; + } + + data = pixGetData(pixd); + datas = pixGetData(pixs); + datam = pixGetData(pixm); + wpl = pixGetWpl(pixd); + wpls = pixGetWpl(pixs); + wplm = pixGetWpl(pixm); + if (d == 8) { + for (i = 0; i < hmin; i++) { + line = data + i * wpl; + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < wmin; j++) { + if (GET_DATA_BIT(linem, j)) { + val = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(line, j, val); + } + } + } + } else { /* d == 32 */ + for (i = 0; i < hmin; i++) { + line = data + i * wpl; + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < wmin; j++) { + if (GET_DATA_BIT(linem, j)) + line[j] = lines[j]; + } + } + } + + return 0; +} + + +/*! + * \brief pixCombineMaskedGeneral() + * + * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb + * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb + * \param[in] pixm [optional] 1 bpp mask + * \param[in] x, y origin of pixs and pixm relative to pixd; can be negative + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation; pixd is changed.
+ *      (2) This is a generalized version of pixCombinedMasked(), where
+ *          the source and mask can be placed at the same (arbitrary)
+ *          location relative to pixd.
+ *      (3) pixs and pixd must be the same depth and not colormapped.
+ *      (4) The UL corners of both pixs and pixm are aligned with
+ *          the point (x, y) of pixd, and the operation is clipped to
+ *          the intersection of all three images.
+ *      (5) If pixm == NULL, it's a no-op.
+ *      (6) Implementation.  There are two ways to do these.  In the first,
+ *          we use rasterop, ORing the part of pixs under the mask
+ *          with pixd (which has been appropriately cleared there first).
+ *          In the second, the mask is used one pixel at a time to
+ *          selectively replace pixels of pixd with those of pixs.
+ *          Here, we use rasterop for 1 bpp and pixel-wise replacement
+ *          for 8 and 32 bpp.  To use rasterop for 8 bpp, for example,
+ *          we must first generate an 8 bpp version of the mask.
+ *          The code is simple:
+ *
+ *             Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255);
+ *             Pix *pixt = pixAnd(NULL, pixs, pixm8);
+ *             pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
+ *                         pixm8, 0, 0);
+ *             pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST,
+ *                         pixt, 0, 0);
+ *             pixDestroy(&pixt);
+ *             pixDestroy(&pixm8);
+ * 
+ */ +l_ok +pixCombineMaskedGeneral(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y) +{ +l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin; +l_int32 wpl, wpls, wplm, i, j, val; +l_uint32 *data, *datas, *datam, *line, *lines, *linem; +PIX *pixt; + + PROCNAME("pixCombineMaskedGeneral"); + + if (!pixm) /* nothing to do */ + return 0; + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixd, &w, &h, &d); + pixGetDimensions(pixs, &ws, &hs, &ds); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (d != ds) + return ERROR_INT("pixs and pixd depths differ", procName, 1); + if (dm != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (d != 1 && d != 8 && d != 32) + return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1); + if (pixGetColormap(pixd) || pixGetColormap(pixs)) + return ERROR_INT("pixs and/or pixd is cmapped", procName, 1); + + /* For d = 1, use rasterop. pixt is the part from pixs, under + * the fg of pixm, that is to be combined with pixd. We also + * use pixt to remove all fg of pixd that is under the fg of pixm. + * Then pixt and pixd are combined by ORing. */ + wmin = L_MIN(ws, wm); + hmin = L_MIN(hs, hm); + if (d == 1) { + pixt = pixAnd(NULL, pixs, pixm); + pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), + pixm, 0, 0); + pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); + pixDestroy(&pixt); + return 0; + } + + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wplm = pixGetWpl(pixm); + datam = pixGetData(pixm); + + for (i = 0; i < hmin; i++) { + if (y + i < 0 || y + i >= h) continue; + line = data + (y + i) * wpl; + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < wmin; j++) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + switch (d) + { + case 8: + val = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(line, x + j, val); + break; + case 32: + *(line + x + j) = *(lines + j); + break; + default: + return ERROR_INT("shouldn't get here", procName, 1); + } + } + } + } + + return 0; +} + + +/*! + * \brief pixPaintThroughMask() + * + * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped + * \param[in] pixm [optional] 1 bpp mask + * \param[in] x, y origin of pixm relative to pixd; can be negative + * \param[in] val pixel value to set at each masked pixel + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation.  Calls pixSetMaskedCmap() for colormapped
+ *          images.
+ *      (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate
+ *          number of least significant bits of val.
+ *      (3) If pixm == NULL, it's a no-op.
+ *      (4) The mask origin is placed at (x,y) on pixd, and the
+ *          operation is clipped to the intersection of rectangles.
+ *      (5) For rgb, the components in val are in the canonical locations,
+ *          with red in location COLOR_RED, etc.
+ *      (6) Implementation detail 1:
+ *          For painting with val == 0 or val == maxval, you can use rasterop.
+ *          If val == 0, invert the mask so that it's 0 over the region
+ *          into which you want to write, and use PIX_SRC & PIX_DST to
+ *          clear those pixels.  To write with val = maxval (all 1's),
+ *          use PIX_SRC | PIX_DST to set all bits under the mask.
+ *      (7) Implementation detail 2:
+ *          The rasterop trick can be used for depth > 1 as well.
+ *          For val == 0, generate the mask for depth d from the binary
+ *          mask using
+ *              pixmd = pixUnpackBinary(pixm, d, 1);
+ *          and use pixRasterop() with PIX_MASK.  For val == maxval,
+ *              pixmd = pixUnpackBinary(pixm, d, 0);
+ *          and use pixRasterop() with PIX_PAINT.
+ *          But note that if d == 32 bpp, it is about 3x faster to use
+ *          the general implementation (not pixRasterop()).
+ *      (8) Implementation detail 3:
+ *          It might be expected that the switch in the inner loop will
+ *          cause large branching delays and should be avoided.
+ *          This is not the case, because the entrance is always the
+ *          same and the compiler can correctly predict the jump.
+ * 
+ */ +l_ok +pixPaintThroughMask(PIX *pixd, + PIX *pixm, + l_int32 x, + l_int32 y, + l_uint32 val) +{ +l_int32 d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval; +l_uint32 *data, *datam, *line, *linem; + + PROCNAME("pixPaintThroughMask"); + + if (!pixm) /* nothing to do */ + return 0; + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixGetColormap(pixd)) { + extractRGBValues(val, &rval, &gval, &bval); + return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval); + } + + if (pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + d = pixGetDepth(pixd); + if (d == 1) + val &= 1; + else if (d == 2) + val &= 3; + else if (d == 4) + val &= 0x0f; + else if (d == 8) + val &= 0xff; + else if (d == 16) + val &= 0xffff; + else if (d != 32) + return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1); + pixGetDimensions(pixm, &wm, &hm, NULL); + + /* If d == 1, use rasterop; it's about 25x faster. */ + if (d == 1) { + if (val == 0) { + PIX *pixmi = pixInvert(NULL, pixm); + pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0); + pixDestroy(&pixmi); + } else { /* val == 1 */ + pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0); + } + return 0; + } + + /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */ + if (d < 32 && val == 0) { + PIX *pixmd = pixUnpackBinary(pixm, d, 1); + pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0); + pixDestroy(&pixmd); + return 0; + } + + /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */ + if (d < 32 && val == ((1 << d) - 1)) { + PIX *pixmd = pixUnpackBinary(pixm, d, 0); + pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0); + pixDestroy(&pixmd); + return 0; + } + + /* All other cases */ + pixGetDimensions(pixd, &w, &h, NULL); + wpl = pixGetWpl(pixd); + data = pixGetData(pixd); + wplm = pixGetWpl(pixm); + datam = pixGetData(pixm); + for (i = 0; i < hm; i++) { + if (y + i < 0 || y + i >= h) continue; + line = data + (y + i) * wpl; + linem = datam + i * wplm; + for (j = 0; j < wm; j++) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + switch (d) + { + case 2: + SET_DATA_DIBIT(line, x + j, val); + break; + case 4: + SET_DATA_QBIT(line, x + j, val); + break; + case 8: + SET_DATA_BYTE(line, x + j, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x + j, val); + break; + case 32: + *(line + x + j) = val; + break; + default: + return ERROR_INT("shouldn't get here", procName, 1); + } + } + } + } + + return 0; +} + + +/*! + * \brief pixCopyWithBoxa() + * + * \param[in] pixs all depths; cmap ok + * \param[in] boxa e.g., from components of a photomask + * \param[in] background L_SET_WHITE or L_SET_BLACK + * \return pixd or NULL on error + * + *
+ * Notes:
+ *      (1) Pixels from pixs are copied ("blitted") through each box into pixd.
+ *      (2) Pixels not copied are preset to either white or black.
+ *      (3) This fast and simple implementation can use rasterop because
+ *          each region to be copied is rectangular.
+ *      (4) A much slower implemention that doesn't use rasterop would make
+ *          a 1 bpp mask from the boxa and then copy, pixel by pixel,
+ *          through the mask:
+ *             pixGetDimensions(pixs, &w, &h, NULL);
+ *             pixm = pixCreate(w, h, 1);
+ *             pixm = pixMaskBoxa(pixm, pixm, boxa);
+ *             pixd = pixCreateTemplate(pixs);
+ *             pixSetBlackOrWhite(pixd, background);
+ *             pixCombineMasked(pixd, pixs, pixm);
+ *             pixDestroy(&pixm);
+ * 
+ */ +PIX * +pixCopyWithBoxa(PIX *pixs, + BOXA *boxa, + l_int32 background) +{ +l_int32 i, n, x, y, w, h; +PIX *pixd; + + PROCNAME("pixCopyWithBoxa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); + if (background != L_SET_WHITE && background != L_SET_BLACK) + return (PIX *)ERROR_PTR("invalid background", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixSetBlackOrWhite(pixd, background); + n = boxaGetCount(boxa); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + pixRasterop(pixd, x, y, w, h, PIX_SRC, pixs, x, y); + } + return pixd; +} + + +/*! + * \brief pixPaintSelfThroughMask() + * + * \param[in] pixd 8 bpp gray or 32 bpp rgb; not colormapped + * \param[in] pixm 1 bpp mask + * \param[in] x, y origin of pixm relative to pixd; must not be negative + * \param[in] searchdir L_HORIZ, L_VERT or L_BOTH_DIRECTIONS + * \param[in] mindist min distance of nearest tile edge to box; >= 0 + * \param[in] tilesize requested size for tiling; may be reduced + * \param[in] ntiles number of tiles tested in each row/column + * \param[in] distblend distance outside the fg used for blending with pixs + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation; pixd is changed.
+ *      (2) If pixm == NULL, it's a no-op.
+ *      (3) The mask origin is placed at (x,y) on pixd, and the
+ *          operation is clipped to the intersection of pixd and the
+ *          fg of the mask.
+ *      (4) %tsize is the the requested size for tiling.  The actual
+ *          actual size for each c.c. will be bounded by the minimum
+ *          dimension of the c.c.
+ *      (5) For %mindist, %searchdir and %ntiles, see pixFindRepCloseTile().
+ *          They determine the set of possible tiles that can be used
+ *          to build a larger mirrored tile to paint onto pixd through
+ *          the c.c. of pixm.
+ *      (6) %distblend is used for alpha blending.  It is only applied
+ *          if there is exactly one c.c. in the mask.  Use distblend == 0
+ *          to skip blending and just paint through the 1 bpp mask.
+ *      (7) To apply blending to more than 1 component, call this function
+ *          repeatedly with %pixm, %x and %y representing one component of
+ *          the mask each time.  This would be done as follows, for an
+ *          underlying image pixs and mask pixm of components to fill:
+ *              Boxa *boxa = pixConnComp(pixm, &pixa, 8);
+ *              n = boxaGetCount(boxa);
+ *              for (i = 0; i < n; i++) {
+ *                  Pix *pix = pixaGetPix(pixa, i, L_CLONE);
+ *                  Box *box = pixaGetBox(pixa, i, L_CLONE);
+ *                  boxGetGeometry(box, &bx, &by, &bw, &bh);
+ *                  pixPaintSelfThroughMask(pixs, pix, bx, by, searchdir,
+ *                                     mindist, tilesize, ntiles, distblend);
+ *                  pixDestroy(&pix);
+ *                  boxDestroy(&box);
+ *              }
+ *              pixaDestroy(&pixa);
+ *              boxaDestroy(&boxa);
+ *      (8) If no tiles can be found, this falls back to estimating the
+ *          color near the boundary of the region to be textured.
+ *      (9) This can be used to replace the pixels in some regions of
+ *          an image by selected neighboring pixels.  The mask represents
+ *          the pixels to be replaced.  For each connected component in
+ *          the mask, this function selects up to two tiles of neighboring
+ *          pixels to be used for replacement of pixels represented by
+ *          the component (i.e., under the FG of that component in the mask).
+ *          After selection, mirror replication is used to generate an
+ *          image that is large enough to cover the component.  Alpha
+ *          blending can also be used outside of the component, but near the
+ *          edge, to blur the transition between painted and original pixels.
+ * 
+ */ +l_ok +pixPaintSelfThroughMask(PIX *pixd, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 searchdir, + l_int32 mindist, + l_int32 tilesize, + l_int32 ntiles, + l_int32 distblend) +{ +l_int32 w, h, d, wm, hm, dm, i, n, bx, by, bw, bh, edgeblend, retval, minside; +l_uint32 pixval; +BOX *box, *boxv, *boxh; +BOXA *boxa; +PIX *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5; +PIXA *pixa; + + PROCNAME("pixPaintSelfThroughMask"); + + if (!pixm) /* nothing to do */ + return 0; + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (pixGetColormap(pixd) != NULL) + return ERROR_INT("pixd has colormap", procName, 1); + pixGetDimensions(pixd, &w, &h, &d); + if (d != 8 && d != 32) + return ERROR_INT("pixd not 8 or 32 bpp", procName, 1); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (dm != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (x < 0 || y < 0) + return ERROR_INT("x and y must be non-negative", procName, 1); + if (searchdir != L_HORIZ && searchdir != L_VERT && + searchdir != L_BOTH_DIRECTIONS) + return ERROR_INT("invalid searchdir", procName, 1); + if (tilesize < 2) + return ERROR_INT("tilesize must be >= 2", procName, 1); + if (distblend < 0) + return ERROR_INT("distblend must be >= 0", procName, 1); + + /* Embed mask in full sized mask */ + if (wm < w || hm < h) { + pixf = pixCreate(w, h, 1); + pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0); + } else { + pixf = pixCopy(NULL, pixm); + } + + /* Get connected components of mask */ + boxa = pixConnComp(pixf, &pixa, 8); + if ((n = pixaGetCount(pixa)) == 0) { + L_WARNING("no fg in mask\n", procName); + pixDestroy(&pixf); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + return 1; + } + boxaDestroy(&boxa); + + /* For each c.c., generate one or two representative tiles for + * texturizing and apply through the mask. The input 'tilesize' + * is the requested value. Note that if there is exactly one + * component, and blending at the edge is requested, an alpha mask + * is generated, which is larger than the bounding box of the c.c. */ + edgeblend = (n == 1 && distblend > 0) ? 1 : 0; + if (distblend > 0 && n > 1) + L_WARNING("%d components; can not blend at edges\n", procName, n); + retval = 0; + for (i = 0; i < n; i++) { + if (edgeblend) { + pix1 = pixMakeAlphaFromMask(pixf, distblend, &box); + } else { + pix1 = pixaGetPix(pixa, i, L_CLONE); + box = pixaGetBox(pixa, i, L_CLONE); + } + boxGetGeometry(box, &bx, &by, &bw, &bh); + minside = L_MIN(bw, bh); + + boxh = boxv = NULL; + if (searchdir == L_HORIZ || searchdir == L_BOTH_DIRECTIONS) { + pixFindRepCloseTile(pixd, box, L_HORIZ, mindist, + L_MIN(minside, tilesize), ntiles, &boxh, 0); + } + if (searchdir == L_VERT || searchdir == L_BOTH_DIRECTIONS) { + pixFindRepCloseTile(pixd, box, L_VERT, mindist, + L_MIN(minside, tilesize), ntiles, &boxv, 0); + } + if (!boxh && !boxv) { + L_WARNING("tile region not selected; paint color near boundary\n", + procName); + pixDestroy(&pix1); + pix1 = pixaGetPix(pixa, i, L_CLONE); + pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); + retval = pixGetColorNearMaskBoundary(pixd, pixm, box, distblend, + &pixval, 0); + pixSetMaskedGeneral(pixd, pix1, pixval, bx, by); + pixDestroy(&pix1); + boxDestroy(&box); + continue; + } + + /* Extract the selected squares from pixd */ + pixh = (boxh) ? pixClipRectangle(pixd, boxh, NULL) : NULL; + pixv = (boxv) ? pixClipRectangle(pixd, boxv, NULL) : NULL; + if (pixh && pixv) + pix2 = pixBlend(pixh, pixv, 0, 0, 0.5); + else if (pixh) + pix2 = pixClone(pixh); + else /* pixv */ + pix2 = pixClone(pixv); + pixDestroy(&pixh); + pixDestroy(&pixv); + boxDestroy(&boxh); + boxDestroy(&boxv); + + /* Generate an image the size of the b.b. of the c.c., + * possibly extended by the blending distance, which + * is then either painted through the c.c. mask or + * blended using the alpha mask for that c.c. */ + pix3 = pixMirroredTiling(pix2, bw, bh); + if (edgeblend) { + pix4 = pixClipRectangle(pixd, box, NULL); + pix5 = pixBlendWithGrayMask(pix4, pix3, pix1, 0, 0); + pixRasterop(pixd, bx, by, bw, bh, PIX_SRC, pix5, 0, 0); + pixDestroy(&pix4); + pixDestroy(&pix5); + } else { + pixCombineMaskedGeneral(pixd, pix3, pix1, bx, by); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + boxDestroy(&box); + } + + pixaDestroy(&pixa); + pixDestroy(&pixf); + return retval; +} + + +/*! + * \brief pixMakeMaskFromVal() + * + * \param[in] pixs 2, 4 or 8 bpp; can be colormapped + * \param[in] val pixel value + * \return pixd 1 bpp mask, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a 1 bpp mask image, where a 1 is written in
+ *          the mask for each pixel in pixs that has a value %val.
+ *      (2) If no pixels have the value, an empty mask is generated.
+ * 
+ */ +PIX * +pixMakeMaskFromVal(PIX *pixs, + l_int32 val) +{ +l_int32 w, h, d, i, j, sval, wpls, wpld; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixMakeMaskFromVal"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL); + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 2) + sval = GET_DATA_DIBIT(lines, j); + else if (d == 4) + sval = GET_DATA_QBIT(lines, j); + else /* d == 8 */ + sval = GET_DATA_BYTE(lines, j); + if (sval == val) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*! + * \brief pixMakeMaskFromLUT() + * + * \param[in] pixs 2, 4 or 8 bpp; can be colormapped + * \param[in] tab 256-entry LUT; 1 means to write to mask + * \return pixd 1 bpp mask, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a 1 bpp mask image, where a 1 is written in
+ *          the mask for each pixel in pixs that has a value corresponding
+ *          to a 1 in the LUT.
+ *      (2) The LUT should be of size 256.
+ * 
+ */ +PIX * +pixMakeMaskFromLUT(PIX *pixs, + l_int32 *tab) +{ +l_int32 w, h, d, i, j, val, wpls, wpld; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixMakeMaskFromLUT"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!tab) + return (PIX *)ERROR_PTR("tab not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL); + + pixd = pixCreate(w, h, 1); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 2) + val = GET_DATA_DIBIT(lines, j); + else if (d == 4) + val = GET_DATA_QBIT(lines, j); + else /* d == 8 */ + val = GET_DATA_BYTE(lines, j); + if (tab[val] == 1) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*! + * \brief pixMakeArbMaskFromRGB() + * + * \param[in] pixs 32 bpp RGB + * \param[in] rc, gc, bc arithmetic factors; can be negative + * \param[in] thresh lower threshold on weighted sum of components + * \return pixd 1 bpp mask, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a 1 bpp mask image, where a 1 is written in
+ *          the mask for each pixel in pixs that satisfies
+ *               rc * rval + gc * gval + bc * bval > thresh
+ *          where rval is the red component, etc.
+ *      (2) Unlike with pixConvertToGray(), there are no constraints
+ *          on the color coefficients, which can be negative.  For
+ *          example, a mask that discriminates against red and in favor
+ *          of blue will have rc < 0.0 and bc > 0.0.
+ *      (3) To make the result independent of intensity (the 'V' in HSV),
+ *          select coefficients so that %thresh = 0.  Then the result
+ *          is not changed when all components are multiplied by the
+ *          same constant (as long as nothing saturates).  This can be
+ *          useful if, for example, the illumination is not uniform.
+ * 
+ */ +PIX * +pixMakeArbMaskFromRGB(PIX *pixs, + l_float32 rc, + l_float32 gc, + l_float32 bc, + l_float32 thresh) +{ +PIX *pix1, *pix2; + + PROCNAME("pixMakeArbMaskFromRGB"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (thresh >= 255.0) thresh = 254.0; /* avoid 8 bit overflow */ + + if ((pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc)) == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + pix2 = pixThresholdToBinary(pix1, thresh + 1); + pixInvert(pix2, pix2); + pixDestroy(&pix1); + return pix2; +} + + +/*! + * \brief pixSetUnderTransparency() + * + * \param[in] pixs 32 bpp rgba + * \param[in] val 32 bit unsigned color to use where alpha == 0 + * \param[in] debug displays layers of pixs + * \return pixd 32 bpp rgba, or NULL on error + * + *
+ * Notes:
+ *      (1) This sets the r, g and b components under every fully
+ *          transparent alpha component to %val.  The alpha components
+ *          are unchanged.
+ *      (2) Full transparency is denoted by alpha == 0.  Setting
+ *          all pixels to a constant %val where alpha is transparent
+ *          can improve compressibility by reducing the entropy.
+ *      (3) The visual result depends on how the image is displayed.
+ *          (a) For display devices that respect the use of the alpha
+ *              layer, this will not affect the appearance.
+ *          (b) For typical leptonica operations, alpha is ignored,
+ *              so there will be a change in appearance because this
+ *              resets the rgb values in the fully transparent region.
+ *      (4) pixRead() and pixWrite() will, by default, read and write
+ *          4-component (rgba) pix in png format.  To ignore the alpha
+ *          component after reading, or omit it on writing, pixSetSpp(..., 3).
+ *      (5) Here are some examples:
+ *          * To convert all fully transparent pixels in a 4 component
+ *            (rgba) png file to white:
+ *              pixs = pixRead();
+ *              pixd = pixSetUnderTransparency(pixs, 0xffffff00, 0);
+ *          * To write pixd with the alpha component:
+ *              pixWrite(, pixd, IFF_PNG);
+ *          * To write and rgba image without the alpha component, first do:
+ *              pixSetSpp(pixd, 3);
+ *            If you later want to use the alpha, spp must be reset to 4.
+ *          * (fancier) To remove the alpha by blending the image over
+ *            a white background:
+ *              pixRemoveAlpha()
+ *            This changes all pixel values where the alpha component is
+ *            not opaque (255).
+ *      (6) Caution.  rgb images in leptonica typically have value 0 in
+ *          the alpha channel, which is fully transparent.  If spp for
+ *          such an image were changed from 3 to 4, the image becomes
+ *          fully transparent, and this function will set each pixel to %val.
+ *          If you really want to set every pixel to the same value,
+ *          use pixSetAllArbitrary().
+ *      (7) This is useful for compressing an RGBA image where the part
+ *          of the image that is fully transparent is random junk; compression
+ *          is typically improved by setting that region to a constant.
+ *          For rendering as a 3 component RGB image over a uniform
+ *          background of arbitrary color, use pixAlphaBlendUniform().
+ * 
+ */ +PIX * +pixSetUnderTransparency(PIX *pixs, + l_uint32 val, + l_int32 debug) +{ +PIX *pixg, *pixm, *pixt, *pixd; + + PROCNAME("pixSetUnderTransparency"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not defined or not 32 bpp", + procName, NULL); + + if (pixGetSpp(pixs) != 4) { + L_WARNING("no alpha channel; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + /* Make a mask from the alpha component with ON pixels + * wherever the alpha component is fully transparent (0). + * The hard way: + * l_int32 *lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + * lut[0] = 1; + * pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + * pixm = pixMakeMaskFromLUT(pixg, lut); + * LEPT_FREE(lut); + * But there's an easier way to set pixels in a mask where + * the alpha component is 0 ... */ + pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pixm = pixThresholdToBinary(pixg, 1); + + if (debug) { + pixt = pixDisplayLayersRGBA(pixs, 0xffffff00, 600); + pixDisplay(pixt, 0, 0); + pixDestroy(&pixt); + } + + pixd = pixCopy(NULL, pixs); + pixSetMasked(pixd, pixm, (val & 0xffffff00)); + pixDestroy(&pixg); + pixDestroy(&pixm); + return pixd; +} + + +/*! + * \brief pixMakeAlphaFromMask() + * + * \param[in] pixs 1 bpp + * \param[in] dist blending distance; typically 10 - 30 + * \param[out] pbox [optional] use NULL to get the full size + * \return pixd (8 bpp gray, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a 8 bpp alpha layer that is opaque (256)
+ *          over the FG of pixs, and goes transparent linearly away
+ *          from the FG pixels, decaying to 0 (transparent) is an
+ *          8-connected distance given by %dist.  If %dist == 0,
+ *          this does a simple conversion from 1 to 8 bpp.
+ *      (2) If &box == NULL, this returns an alpha mask that is the
+ *          full size of pixs.  Otherwise, the returned mask pixd covers
+ *          just the FG pixels of pixs, expanded by %dist in each
+ *          direction (if possible), and the returned box gives the
+ *          location of the returned mask relative to pixs.
+ *      (3) This is useful for painting through a mask and allowing
+ *          blending of the painted image with an underlying image
+ *          in the mask background for pixels near foreground mask pixels.
+ *          For example, with an underlying rgb image pix1, an overlaying
+ *          image rgb pix2, binary mask pixm, and dist > 0, this
+ *          blending is achieved with:
+ *              pix3 = pixMakeAlphaFromMask(pixm, dist, &box);
+ *              boxGetGeometry(box, &x, &y, NULL, NULL);
+ *              pix4 = pixBlendWithGrayMask(pix1, pix2, pix3, x, y);
+ * 
+ */ +PIX * +pixMakeAlphaFromMask(PIX *pixs, + l_int32 dist, + BOX **pbox) +{ +l_int32 w, h; +BOX *box1, *box2; +PIX *pix1, *pixd; + + PROCNAME("pixMakeAlphaFromMask"); + + if (pbox) *pbox = NULL; + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (dist < 0) + return (PIX *)ERROR_PTR("dist must be >= 0", procName, NULL); + + /* If requested, extract just the region to be affected by the mask */ + if (pbox) { + pixClipToForeground(pixs, NULL, &box1); + if (!box1) { + L_WARNING("no ON pixels in mask\n", procName); + return pixCreateTemplate(pixs); /* all background (0) */ + } + + boxAdjustSides(box1, box1, -dist, dist, -dist, dist); + pixGetDimensions(pixs, &w, &h, NULL); + box2 = boxClipToRectangle(box1, w, h); + *pbox = box2; + pix1 = pixClipRectangle(pixs, box2, NULL); + boxDestroy(&box1); + } else { + pix1 = pixCopy(NULL, pixs); + } + + if (dist == 0) { + pixd = pixConvert1To8(NULL, pix1, 0, 255); + pixDestroy(&pix1); + return pixd; + } + + /* Blur the boundary of the input mask */ + pixInvert(pix1, pix1); + pixd = pixDistanceFunction(pix1, 8, 8, L_BOUNDARY_FG); + pixMultConstantGray(pixd, 256.0 / dist); + pixInvert(pixd, pixd); + pixDestroy(&pix1); + return pixd; +} + + +/*! + * \brief pixGetColorNearMaskBoundary() + * + * \param[in] pixs 32 bpp rgb + * \param[in] pixm 1 bpp mask, full image + * \param[in] box region of mask; typically b.b. of a component + * \param[in] dist distance into BG from mask boundary to use + * \param[out] pval average pixel value + * \param[in] debug 1 to output mask images + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This finds the average color in a set of pixels that are
+ *          roughly a distance %dist from the c.c. boundary and in the
+ *          background of the mask image.
+ * 
+ */ +l_ok +pixGetColorNearMaskBoundary(PIX *pixs, + PIX *pixm, + BOX *box, + l_int32 dist, + l_uint32 *pval, + l_int32 debug) +{ +char op[64]; +l_int32 empty, bx, by; +l_float32 rval, gval, bval; +BOX *box1, *box2; +PIX *pix1, *pix2, *pix3; + + PROCNAME("pixGetColorNearMaskBoundary"); + + if (!pval) + return ERROR_INT("&pval not defined", procName, 1); + *pval = 0xffffff00; /* white */ + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (dist < 0) + return ERROR_INT("dist must be >= 0", procName, 1); + + /* Clip mask piece, expanded beyond %box by (%dist + 5) on each side. + * box1 is the region requested; box2 is the actual region retrieved, + * which is clipped to %pixm */ + box1 = boxAdjustSides(NULL, box, -dist - 5, dist + 5, -dist - 5, dist + 5); + pix1 = pixClipRectangle(pixm, box1, &box2); + + /* Expand FG by %dist into the BG */ + if (dist == 0) { + pix2 = pixCopy(NULL, pix1); + } else { + snprintf(op, sizeof(op), "d%d.%d", 2 * dist, 2 * dist); + pix2 = pixMorphSequence(pix1, op, 0); + } + + /* Expand again by 5 pixels on all sides (dilate 11x11) and XOR, + * getting the annulus of FG pixels between %dist and %dist + 5 */ + pix3 = pixCopy(NULL, pix2); + pixDilateBrick(pix3, pix3, 11, 11); + pixXor(pix3, pix3, pix2); + pixZero(pix3, &empty); + if (!empty) { + /* Scan the same region in %pixs, to get average under FG in pix3 */ + boxGetGeometry(box2, &bx, &by, NULL, NULL); + pixGetAverageMaskedRGB(pixs, pix3, bx, by, 1, L_MEAN_ABSVAL, + &rval, &gval, &bval); + composeRGBPixel((l_int32)(rval + 0.5), (l_int32)(gval + 0.5), + (l_int32)(bval + 0.5), pval); + } else { + L_WARNING("no pixels found\n", procName); + } + + if (debug) { + lept_rmdir("masknear"); /* erase previous images */ + lept_mkdir("masknear"); + pixWriteDebug("/tmp/masknear/input.png", pix1, IFF_PNG); + pixWriteDebug("/tmp/masknear/adjusted.png", pix2, IFF_PNG); + pixWriteDebug("/tmp/masknear/outerfive.png", pix3, IFF_PNG); + fprintf(stderr, "Input box; with adjusted sides; clipped\n"); + boxPrintStreamInfo(stderr, box); + boxPrintStreamInfo(stderr, box1); + boxPrintStreamInfo(stderr, box2); + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + boxDestroy(&box1); + boxDestroy(&box2); + return 0; +} + + +/*-------------------------------------------------------------* + * One and two-image boolean ops on arbitrary depth images * + *-------------------------------------------------------------*/ +/*! + * \brief pixInvert() + * + * \param[in] pixd [optional]; this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This inverts pixs, for all pixel depths.
+ *      (2) There are 3 cases:
+ *           (a) pixd == null,   ~src --> new pixd
+ *           (b) pixd == pixs,   ~src --> src  (in-place)
+ *           (c) pixd != pixs,   ~src --> input pixd
+ *      (3) For clarity, if the case is known, use these patterns:
+ *           (a) pixd = pixInvert(NULL, pixs);
+ *           (b) pixInvert(pixs, pixs);
+ *           (c) pixInvert(pixd, pixs);
+ * 
+ */ +PIX * +pixInvert(PIX *pixd, + PIX *pixs) +{ + PROCNAME("pixInvert"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), + PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */ + + return pixd; +} + + +/*! + * \brief pixOr() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, + * different from pixs1 + * \param[in] pixs1 can be == pixd + * \param[in] pixs2 must be != pixd + * \return pixd always + * + *
+ * Notes:
+ *      (1) This gives the union of two images with equal depth,
+ *          aligning them to the the UL corner.  pixs1 and pixs2
+ *          need not have the same width and height.
+ *      (2) There are 3 cases:
+ *            (a) pixd == null,   (src1 | src2) --> new pixd
+ *            (b) pixd == pixs1,  (src1 | src2) --> src1  (in-place)
+ *            (c) pixd != pixs1,  (src1 | src2) --> input pixd
+ *      (3) For clarity, if the case is known, use these patterns:
+ *            (a) pixd = pixOr(NULL, pixs1, pixs2);
+ *            (b) pixOr(pixs1, pixs1, pixs2);
+ *            (c) pixOr(pixd, pixs1, pixs2);
+ *      (4) The size of the result is determined by pixs1.
+ *      (5) The depths of pixs1 and pixs2 must be equal.
+ *      (6) Note carefully that the order of pixs1 and pixs2 only matters
+ *          for the in-place case.  For in-place, you must have
+ *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
+ *          result: the copy puts pixs1 image data in pixs2, and
+ *          the rasterop is then between pixs2 and pixs2 (a no-op).
+ * 
+ */ +PIX * +pixOr(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ + PROCNAME("pixOr"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixd == pixs2) + return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); + if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); + +#if EQUAL_SIZE_WARNING + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); +#endif /* EQUAL_SIZE_WARNING */ + + /* Prepare pixd to be a copy of pixs1 */ + if ((pixd = pixCopy(pixd, pixs1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, pixd); + + /* src1 | src2 --> dest */ + pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), + PIX_SRC | PIX_DST, pixs2, 0, 0); + + return pixd; +} + + +/*! + * \brief pixAnd() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, + * different from pixs1 + * \param[in] pixs1 can be == pixd + * \param[in] pixs2 must be != pixd + * \return pixd always + * + *
+ * Notes:
+ *      (1) This gives the intersection of two images with equal depth,
+ *          aligning them to the the UL corner.  pixs1 and pixs2
+ *          need not have the same width and height.
+ *      (2) There are 3 cases:
+ *            (a) pixd == null,   (src1 & src2) --> new pixd
+ *            (b) pixd == pixs1,  (src1 & src2) --> src1  (in-place)
+ *            (c) pixd != pixs1,  (src1 & src2) --> input pixd
+ *      (3) For clarity, if the case is known, use these patterns:
+ *            (a) pixd = pixAnd(NULL, pixs1, pixs2);
+ *            (b) pixAnd(pixs1, pixs1, pixs2);
+ *            (c) pixAnd(pixd, pixs1, pixs2);
+ *      (4) The size of the result is determined by pixs1.
+ *      (5) The depths of pixs1 and pixs2 must be equal.
+ *      (6) Note carefully that the order of pixs1 and pixs2 only matters
+ *          for the in-place case.  For in-place, you must have
+ *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
+ *          result: the copy puts pixs1 image data in pixs2, and
+ *          the rasterop is then between pixs2 and pixs2 (a no-op).
+ * 
+ */ +PIX * +pixAnd(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ + PROCNAME("pixAnd"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixd == pixs2) + return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); + if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); + +#if EQUAL_SIZE_WARNING + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); +#endif /* EQUAL_SIZE_WARNING */ + + /* Prepare pixd to be a copy of pixs1 */ + if ((pixd = pixCopy(pixd, pixs1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, pixd); + + /* src1 & src2 --> dest */ + pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), + PIX_SRC & PIX_DST, pixs2, 0, 0); + + return pixd; +} + + +/*! + * \brief pixXor() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, + * different from pixs1 + * \param[in] pixs1 can be == pixd + * \param[in] pixs2 must be != pixd + * \return pixd always + * + *
+ * Notes:
+ *      (1) This gives the XOR of two images with equal depth,
+ *          aligning them to the the UL corner.  pixs1 and pixs2
+ *          need not have the same width and height.
+ *      (2) There are 3 cases:
+ *            (a) pixd == null,   (src1 ^ src2) --> new pixd
+ *            (b) pixd == pixs1,  (src1 ^ src2) --> src1  (in-place)
+ *            (c) pixd != pixs1,  (src1 ^ src2) --> input pixd
+ *      (3) For clarity, if the case is known, use these patterns:
+ *            (a) pixd = pixXor(NULL, pixs1, pixs2);
+ *            (b) pixXor(pixs1, pixs1, pixs2);
+ *            (c) pixXor(pixd, pixs1, pixs2);
+ *      (4) The size of the result is determined by pixs1.
+ *      (5) The depths of pixs1 and pixs2 must be equal.
+ *      (6) Note carefully that the order of pixs1 and pixs2 only matters
+ *          for the in-place case.  For in-place, you must have
+ *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
+ *          result: the copy puts pixs1 image data in pixs2, and
+ *          the rasterop is then between pixs2 and pixs2 (a no-op).
+ * 
+ */ +PIX * +pixXor(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ + PROCNAME("pixXor"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixd == pixs2) + return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); + if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); + +#if EQUAL_SIZE_WARNING + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); +#endif /* EQUAL_SIZE_WARNING */ + + /* Prepare pixd to be a copy of pixs1 */ + if ((pixd = pixCopy(pixd, pixs1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, pixd); + + /* src1 ^ src2 --> dest */ + pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), + PIX_SRC ^ PIX_DST, pixs2, 0, 0); + + return pixd; +} + + +/*! + * \brief pixSubtract() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, + * equal to pixs2, or different from both pixs1 and pixs2 + * \param[in] pixs1 can be == pixd + * \param[in] pixs2 can be == pixd + * \return pixd always + * + *
+ * Notes:
+ *      (1) This gives the set subtraction of two images with equal depth,
+ *          aligning them to the the UL corner.  pixs1 and pixs2
+ *          need not have the same width and height.
+ *      (2) Source pixs2 is always subtracted from source pixs1.
+ *          The result is
+ *                  pixs1 \ pixs2 = pixs1 & (~pixs2)
+ *      (3) There are 4 cases:
+ *            (a) pixd == null,   (src1 - src2) --> new pixd
+ *            (b) pixd == pixs1,  (src1 - src2) --> src1  (in-place)
+ *            (c) pixd == pixs2,  (src1 - src2) --> src2  (in-place)
+ *            (d) pixd != pixs1 && pixd != pixs2),
+ *                                 (src1 - src2) --> input pixd
+ *      (4) For clarity, if the case is known, use these patterns:
+ *            (a) pixd = pixSubtract(NULL, pixs1, pixs2);
+ *            (b) pixSubtract(pixs1, pixs1, pixs2);
+ *            (c) pixSubtract(pixs2, pixs1, pixs2);
+ *            (d) pixSubtract(pixd, pixs1, pixs2);
+ *      (5) The size of the result is determined by pixs1.
+ *      (6) The depths of pixs1 and pixs2 must be equal.
+ * 
+ */ +PIX * +pixSubtract(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ +l_int32 w, h; + + PROCNAME("pixSubtract"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); + +#if EQUAL_SIZE_WARNING + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); +#endif /* EQUAL_SIZE_WARNING */ + + pixGetDimensions(pixs1, &w, &h, NULL); + if (!pixd) { + pixd = pixCopy(NULL, pixs1); + pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), + pixs2, 0, 0); /* src1 & (~src2) */ + } else if (pixd == pixs1) { + pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), + pixs2, 0, 0); /* src1 & (~src2) */ + } else if (pixd == pixs2) { + pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC, + pixs1, 0, 0); /* src1 & (~src2) */ + } else { /* pixd != pixs1 && pixd != pixs2 */ + pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */ + pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), + pixs2, 0, 0); /* src1 & (~src2) */ + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Pixel counting * + *-------------------------------------------------------------*/ +/*! + * \brief pixZero() + * + * \param[in] pix all depths; colormap OK + * \param[out] pempty 1 if all bits in image data field are 0; 0 otherwise + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) For a binary image, if there are no fg (black) pixels, empty = 1.
+ *      (2) For a grayscale image, if all pixels are black (0), empty = 1.
+ *      (3) For an RGB image, if all 4 components in every pixel is 0,
+ *          empty = 1.
+ *      (4) For a colormapped image, pixel values are 0.  The colormap
+ *          is ignored.
+ * 
+ */ +l_ok +pixZero(PIX *pix, + l_int32 *pempty) +{ +l_int32 w, h, wpl, i, j, fullwords, endbits; +l_uint32 endmask; +l_uint32 *data, *line; + + PROCNAME("pixZero"); + + if (!pempty) + return ERROR_INT("&empty not defined", procName, 1); + *pempty = 1; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + w = pixGetWidth(pix) * pixGetDepth(pix); /* in bits */ + h = pixGetHeight(pix); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + fullwords = w / 32; + endbits = w & 31; + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); + + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < fullwords; j++) + if (*line++) { + *pempty = 0; + return 0; + } + if (endbits) { + if (*line & endmask) { + *pempty = 0; + return 0; + } + } + } + + return 0; +} + + +/*! + * \brief pixForegroundFraction() + * + * \param[in] pix 1 bpp + * \param[out] pfract fraction of ON pixels + * \return 0 if OK; 1 on error + */ +l_ok +pixForegroundFraction(PIX *pix, + l_float32 *pfract) +{ +l_int32 w, h, count; + + PROCNAME("pixForegroundFraction"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp", procName, 1); + + pixCountPixels(pix, &count, NULL); + pixGetDimensions(pix, &w, &h, NULL); + *pfract = (l_float32)count / (l_float32)(w * h); + return 0; +} + + +/*! + * \brief pixaCountPixels() + * + * \param[in] pixa array of 1 bpp pix + * \return na of ON pixels in each pix, or NULL on error + */ +NUMA * +pixaCountPixels(PIXA *pixa) +{ +l_int32 d, i, n, count; +l_int32 *tab; +NUMA *na; +PIX *pix; + + PROCNAME("pixaCountPixels"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + + if ((n = pixaGetCount(pixa)) == 0) + return numaCreate(1); + + pix = pixaGetPix(pixa, 0, L_CLONE); + d = pixGetDepth(pix); + pixDestroy(&pix); + if (d != 1) + return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL); + + if ((na = numaCreate(n)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + tab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixCountPixels(pix, &count, tab); + numaAddNumber(na, count); + pixDestroy(&pix); + } + + LEPT_FREE(tab); + return na; +} + + +/*! + * \brief pixCountPixels() + * + * \param[in] pixs 1 bpp + * \param[out] pcount count of ON pixels + * \param[in] tab8 [optional] 8-bit pixel lookup table + * \return 0 if OK; 1 on error + */ +l_ok +pixCountPixels(PIX *pixs, + l_int32 *pcount, + l_int32 *tab8) +{ +l_uint32 endmask; +l_int32 w, h, wpl, i, j; +l_int32 fullwords, endbits, sum; +l_int32 *tab; +l_uint32 *data; + + PROCNAME("pixCountPixels"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + pixGetDimensions(pixs, &w, &h, NULL); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + fullwords = w >> 5; + endbits = w & 31; + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); + + sum = 0; + for (i = 0; i < h; i++, data += wpl) { + for (j = 0; j < fullwords; j++) { + l_uint32 word = data[j]; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + if (endbits) { + l_uint32 word = data[j] & endmask; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + } + *pcount = sum; + + if (!tab8) LEPT_FREE(tab); + return 0; +} + + +/*! + * \brief pixCountPixelsInRect() + * + * \param[in] pixs 1 bpp + * \param[in] box (can be null) + * \param[out] pcount count of ON pixels + * \param[in] tab8 [optional] 8-bit pixel lookup table + * \return 0 if OK; 1 on error + */ +l_ok +pixCountPixelsInRect(PIX *pixs, + BOX *box, + l_int32 *pcount, + l_int32 *tab8) +{ +l_int32 bx, by, bw, bh; +PIX *pix1; + + PROCNAME("pixCountPixelsInRect"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (box) { + boxGetGeometry(box, &bx, &by, &bw, &bh); + pix1 = pixCreate(bw, bh, 1); + pixRasterop(pix1, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); + pixCountPixels(pix1, pcount, tab8); + pixDestroy(&pix1); + } else { + pixCountPixels(pixs, pcount, tab8); + } + + return 0; +} + + +/*! + * \brief pixCountByRow() + * + * \param[in] pix 1 bpp + * \param[in] box [optional] clipping box for count; can be null + * \return na of number of ON pixels by row, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ * 
+ */ +NUMA * +pixCountByRow(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; +l_uint32 *line, *data; +NUMA *na; + + PROCNAME("pixCountByRow"); + + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + if (!box) + return pixCountPixelsByRow(pix, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + if ((na = numaCreate(bh)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, ystart, 1); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = ystart; i < yend; i++) { + count = 0; + line = data + i * wpl; + for (j = xstart; j < xend; j++) { + if (GET_DATA_BIT(line, j)) + count++; + } + numaAddNumber(na, count); + } + + return na; +} + + +/*! + * \brief pixCountByColumn() + * + * \param[in] pix 1 bpp + * \param[in] box [optional] clipping box for count; can be null + * \return na of number of ON pixels by column, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ * 
+ */ +NUMA * +pixCountByColumn(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; +l_uint32 *line, *data; +NUMA *na; + + PROCNAME("pixCountByColumn"); + + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + if (!box) + return pixCountPixelsByColumn(pix); + + pixGetDimensions(pix, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + if ((na = numaCreate(bw)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, xstart, 1); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (j = xstart; j < xend; j++) { + count = 0; + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + if (GET_DATA_BIT(line, j)) + count++; + } + numaAddNumber(na, count); + } + + return na; +} + + +/*! + * \brief pixCountPixelsByRow() + * + * \param[in] pix 1 bpp + * \param[in] tab8 [optional] 8-bit pixel lookup table + * \return na of counts, or NULL on error + */ +NUMA * +pixCountPixelsByRow(PIX *pix, + l_int32 *tab8) +{ +l_int32 h, i, count; +l_int32 *tab; +NUMA *na; + + PROCNAME("pixCountPixelsByRow"); + + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + + h = pixGetHeight(pix); + if ((na = numaCreate(h)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + for (i = 0; i < h; i++) { + pixCountPixelsInRow(pix, i, &count, tab); + numaAddNumber(na, count); + } + + if (!tab8) LEPT_FREE(tab); + return na; +} + + +/*! + * \brief pixCountPixelsByColumn() + * + * \param[in] pix 1 bpp + * \return na of counts in each column, or NULL on error + */ +NUMA * +pixCountPixelsByColumn(PIX *pix) +{ +l_int32 i, j, w, h, wpl; +l_uint32 *line, *data; +l_float32 *array; +NUMA *na; + + PROCNAME("pixCountPixelsByColumn"); + + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + if ((na = numaCreate(w)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, w); + array = numaGetFArray(na, L_NOCOPY); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < w; j++) { + if (GET_DATA_BIT(line, j)) + array[j] += 1.0; + } + } + + return na; +} + + +/*! + * \brief pixCountPixelsInRow() + * + * \param[in] pix 1 bpp + * \param[in] row number + * \param[out] pcount sum of ON pixels in raster line + * \param[in] tab8 [optional] 8-bit pixel lookup table + * \return 0 if OK; 1 on error + */ +l_ok +pixCountPixelsInRow(PIX *pix, + l_int32 row, + l_int32 *pcount, + l_int32 *tab8) +{ +l_uint32 word, endmask; +l_int32 j, w, h, wpl; +l_int32 fullwords, endbits, sum; +l_int32 *tab; +l_uint32 *line; + + PROCNAME("pixCountPixelsInRow"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp", procName, 1); + + pixGetDimensions(pix, &w, &h, NULL); + if (row < 0 || row >= h) + return ERROR_INT("row out of bounds", procName, 1); + wpl = pixGetWpl(pix); + line = pixGetData(pix) + row * wpl; + fullwords = w >> 5; + endbits = w & 31; + endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + sum = 0; + for (j = 0; j < fullwords; j++) { + word = line[j]; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + if (endbits) { + word = line[j] & endmask; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + *pcount = sum; + + if (!tab8) LEPT_FREE(tab); + return 0; +} + + +/*! + * \brief pixGetMomentByColumn() + * + * \param[in] pix 1 bpp + * \param[in] order of moment, either 1 or 2 + * \return na of first moment of fg pixels, by column, or NULL on error + */ +NUMA * +pixGetMomentByColumn(PIX *pix, + l_int32 order) +{ +l_int32 i, j, w, h, wpl; +l_uint32 *line, *data; +l_float32 *array; +NUMA *na; + + PROCNAME("pixGetMomentByColumn"); + + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + if (order != 1 && order != 2) + return (NUMA *)ERROR_PTR("order of moment not 1 or 2", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + if ((na = numaCreate(w)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, w); + array = numaGetFArray(na, L_NOCOPY); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < w; j++) { + if (GET_DATA_BIT(line, j)) { + if (order == 1) + array[j] += i; + else /* order == 2 */ + array[j] += i * i; + } + } + } + + return na; +} + + +/*! + * \brief pixThresholdPixelSum() + * + * \param[in] pix 1 bpp + * \param[in] thresh threshold + * \param[out] pabove 1 if above threshold; + * 0 if equal to or less than threshold + * \param[in] tab8 [optional] 8-bit pixel lookup table + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This sums the ON pixels and returns immediately if the count
+ *          goes above threshold.  It is therefore more efficient
+ *          for matching images (by running this function on the xor of
+ *          the 2 images) than using pixCountPixels(), which counts all
+ *          pixels before returning.
+ * 
+ */ +l_ok +pixThresholdPixelSum(PIX *pix, + l_int32 thresh, + l_int32 *pabove, + l_int32 *tab8) +{ +l_uint32 word, endmask; +l_int32 *tab; +l_int32 w, h, wpl, i, j; +l_int32 fullwords, endbits, sum; +l_uint32 *line, *data; + + PROCNAME("pixThresholdPixelSum"); + + if (!pabove) + return ERROR_INT("&above not defined", procName, 1); + *pabove = 0; + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp", procName, 1); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + pixGetDimensions(pix, &w, &h, NULL); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + fullwords = w >> 5; + endbits = w & 31; + endmask = 0xffffffff << (32 - endbits); + + sum = 0; + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < fullwords; j++) { + word = line[j]; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + if (endbits) { + word = line[j] & endmask; + if (word) { + sum += tab[word & 0xff] + + tab[(word >> 8) & 0xff] + + tab[(word >> 16) & 0xff] + + tab[(word >> 24) & 0xff]; + } + } + if (sum > thresh) { + *pabove = 1; + if (!tab8) LEPT_FREE(tab); + return 0; + } + } + + if (!tab8) LEPT_FREE(tab); + return 0; +} + + +/*! + * \brief makePixelSumTab8() + * + * \return table of 256 l_int32. + * + *
+ * Notes:
+ *      (1) This table of integers gives the number of 1 bits
+ *          in the 8 bit index.
+ * 
+ */ +l_int32 * +makePixelSumTab8(void) +{ +l_uint8 byte; +l_int32 i; +l_int32 *tab; + + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) { + byte = (l_uint8)i; + tab[i] = (byte & 0x1) + + ((byte >> 1) & 0x1) + + ((byte >> 2) & 0x1) + + ((byte >> 3) & 0x1) + + ((byte >> 4) & 0x1) + + ((byte >> 5) & 0x1) + + ((byte >> 6) & 0x1) + + ((byte >> 7) & 0x1); + } + return tab; +} + + +/*! + * \brief makePixelCentroidTab8() + * + * \return table of 256 l_int32. + * + *
+ * Notes:
+ *      (1) This table of integers gives the centroid weight of the 1 bits
+ *          in the 8 bit index.  In other words, if sumtab is obtained by
+ *          makePixelSumTab8, and centroidtab is obtained by
+ *          makePixelCentroidTab8, then, for 1 <= i <= 255,
+ *          centroidtab[i] / (float)sumtab[i]
+ *          is the centroid of the 1 bits in the 8-bit index i, where the
+ *          MSB is considered to have position 0 and the LSB is considered
+ *          to have position 7.
+ * 
+ */ +l_int32 * +makePixelCentroidTab8(void) +{ +l_int32 i; +l_int32 *tab; + + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + tab[0] = 0; + tab[1] = 7; + for (i = 2; i < 4; i++) { + tab[i] = tab[i - 2] + 6; + } + for (i = 4; i < 8; i++) { + tab[i] = tab[i - 4] + 5; + } + for (i = 8; i < 16; i++) { + tab[i] = tab[i - 8] + 4; + } + for (i = 16; i < 32; i++) { + tab[i] = tab[i - 16] + 3; + } + for (i = 32; i < 64; i++) { + tab[i] = tab[i - 32] + 2; + } + for (i = 64; i < 128; i++) { + tab[i] = tab[i - 64] + 1; + } + for (i = 128; i < 256; i++) { + tab[i] = tab[i - 128]; + } + return tab; +} + + +/*-------------------------------------------------------------* + * Average of pixel values in gray images * + *-------------------------------------------------------------*/ +/*! + * \brief pixAverageByRow() + * + * \param[in] pix 8 or 16 bpp; no colormap + * \param[in] box [optional] clipping box for sum; can be null + * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX + * \return na of pixel averages by row, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ *      (2) If type == L_BLACK_IS_MAX, black pixels get the maximum
+ *          value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0.
+ * 
+ */ +NUMA * +pixAverageByRow(PIX *pix, + BOX *box, + l_int32 type) +{ +l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; +l_uint32 *line, *data; +l_float64 norm, sum; +NUMA *na; + + PROCNAME("pixAverageByRow"); + + if (!pix) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 8 && d != 16) + return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); + if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + norm = 1. / (l_float32)bw; + if ((na = numaCreate(bh)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, ystart, 1); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = ystart; i < yend; i++) { + sum = 0.0; + line = data + i * wpl; + if (d == 8) { + for (j = xstart; j < xend; j++) + sum += GET_DATA_BYTE(line, j); + if (type == L_BLACK_IS_MAX) + sum = bw * 255 - sum; + } else { /* d == 16 */ + for (j = xstart; j < xend; j++) + sum += GET_DATA_TWO_BYTES(line, j); + if (type == L_BLACK_IS_MAX) + sum = bw * 0xffff - sum; + } + numaAddNumber(na, (l_float32)(norm * sum)); + } + + return na; +} + + +/*! + * \brief pixAverageByColumn() + * + * \param[in] pix 8 or 16 bpp; no colormap + * \param[in] box [optional] clipping box for sum; can be null + * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX + * \return na of pixel averages by column, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ *      (2) If type == L_BLACK_IS_MAX, black pixels get the maximum
+ *          value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0.
+ * 
+ */ +NUMA * +pixAverageByColumn(PIX *pix, + BOX *box, + l_int32 type) +{ +l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; +l_uint32 *line, *data; +l_float32 norm, sum; +NUMA *na; + + PROCNAME("pixAverageByColumn"); + + if (!pix) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + + if (d != 8 && d != 16) + return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); + if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + if ((na = numaCreate(bw)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, xstart, 1); + norm = 1. / (l_float32)bh; + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (j = xstart; j < xend; j++) { + sum = 0.0; + if (d == 8) { + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + sum += GET_DATA_BYTE(line, j); + } + if (type == L_BLACK_IS_MAX) + sum = bh * 255 - sum; + } else { /* d == 16 */ + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + sum += GET_DATA_TWO_BYTES(line, j); + } + if (type == L_BLACK_IS_MAX) + sum = bh * 0xffff - sum; + } + numaAddNumber(na, (l_float32)(norm * sum)); + } + + return na; +} + + +/*! + * \brief pixAverageInRect() + * + * \param[in] pix 1, 2, 4, 8 bpp; not cmapped + * \param[in] box [optional] if null, use entire image + * \param[out] pave average of pixel values in region + * \return 0 if OK; 1 on error + */ +l_ok +pixAverageInRect(PIX *pix, + BOX *box, + l_float32 *pave) +{ +l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh; +l_uint32 *data, *line; +l_float64 ave; + + PROCNAME("pixAverageInRect"); + + if (!pave) + return ERROR_INT("&ave not defined", procName, 1); + *pave = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8) + return ERROR_INT("pix not 1, 2, 4 or 8 bpp", procName, 1); + if (pixGetColormap(pix) != NULL) + return ERROR_INT("pix is colormapped", procName, 1); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return ERROR_INT("invalid clipping box", procName, 1); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + ave = 0; + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + for (j = xstart; j < xend; j++) { + if (d == 1) + ave += GET_DATA_BIT(line, j); + else if (d == 2) + ave += GET_DATA_DIBIT(line, j); + else if (d == 4) + ave += GET_DATA_QBIT(line, j); + else /* d == 8 */ + ave += GET_DATA_BYTE(line, j); + } + } + + *pave = ave / ((l_float32)(bw) * bh); + return 0; +} + + +/*------------------------------------------------------------------* + * Variance of pixel values in gray images * + *------------------------------------------------------------------*/ +/*! + * \brief pixVarianceByRow() + * + * \param[in] pix 8 or 16 bpp; no colormap + * \param[in] box [optional] clipping box for variance; can be null + * \return na of rmsdev by row, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ *      (2) We are actually computing the RMS deviation in each row.
+ *          This is the square root of the variance.
+ * 
+ */ +NUMA * +pixVarianceByRow(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; +l_uint32 *line, *data; +l_float64 sum1, sum2, norm, ave, var, rootvar; +NUMA *na; + + PROCNAME("pixVarianceByRow"); + + if (!pix) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 8 && d != 16) + return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + if ((na = numaCreate(bh)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, ystart, 1); + norm = 1. / (l_float32)bw; + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = ystart; i < yend; i++) { + sum1 = sum2 = 0.0; + line = data + i * wpl; + for (j = xstart; j < xend; j++) { + if (d == 8) + val = GET_DATA_BYTE(line, j); + else /* d == 16 */ + val = GET_DATA_TWO_BYTES(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } + ave = norm * sum1; + var = norm * sum2 - ave * ave; + rootvar = sqrt(var); + numaAddNumber(na, (l_float32)rootvar); + } + + return na; +} + + +/*! + * \brief pixVarianceByColumn() + * + * \param[in] pix 8 or 16 bpp; no colormap + * \param[in] box [optional] clipping box for variance; can be null + * \return na of rmsdev by column, or NULL on error + * + *
+ * Notes:
+ *      (1) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ *      (2) We are actually computing the RMS deviation in each row.
+ *          This is the square root of the variance.
+ * 
+ */ +NUMA * +pixVarianceByColumn(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; +l_uint32 *line, *data; +l_float64 sum1, sum2, norm, ave, var, rootvar; +NUMA *na; + + PROCNAME("pixVarianceByColumn"); + + if (!pix) + return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 8 && d != 16) + return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + + if ((na = numaCreate(bw)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, xstart, 1); + norm = 1. / (l_float32)bh; + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (j = xstart; j < xend; j++) { + sum1 = sum2 = 0.0; + for (i = ystart; i < yend; i++) { + line = data + wpl * i; + if (d == 8) + val = GET_DATA_BYTE(line, j); + else /* d == 16 */ + val = GET_DATA_TWO_BYTES(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } + ave = norm * sum1; + var = norm * sum2 - ave * ave; + rootvar = sqrt(var); + numaAddNumber(na, (l_float32)rootvar); + } + + return na; +} + + +/*! + * \brief pixVarianceInRect() + * + * \param[in] pix 1, 2, 4, 8 bpp; not cmapped + * \param[in] box [optional] if null, use entire image + * \param[out] prootvar sqrt variance of pixel values in region + * \return 0 if OK; 1 on error + */ +l_ok +pixVarianceInRect(PIX *pix, + BOX *box, + l_float32 *prootvar) +{ +l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val; +l_uint32 *data, *line; +l_float64 sum1, sum2, norm, ave, var; + + PROCNAME("pixVarianceInRect"); + + if (!prootvar) + return ERROR_INT("&rootvar not defined", procName, 1); + *prootvar = 0.0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8) + return ERROR_INT("pix not 1, 2, 4 or 8 bpp", procName, 1); + if (pixGetColormap(pix) != NULL) + return ERROR_INT("pix is colormapped", procName, 1); + + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return ERROR_INT("invalid clipping box", procName, 1); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + sum1 = sum2 = 0.0; + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + for (j = xstart; j < xend; j++) { + if (d == 1) { + val = GET_DATA_BIT(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } else if (d == 2) { + val = GET_DATA_DIBIT(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } else if (d == 4) { + val = GET_DATA_QBIT(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } else { /* d == 8 */ + val = GET_DATA_BYTE(line, j); + sum1 += val; + sum2 += (l_float64)(val) * val; + } + } + } + norm = 1.0 / ((l_float64)(bw) * bh); + ave = norm * sum1; + var = norm * sum2 - ave * ave; + *prootvar = (l_float32)sqrt(var); + return 0; +} + + +/*---------------------------------------------------------------------* + * Average of absolute value of pixel differences in gray images * + *---------------------------------------------------------------------*/ +/*! + * \brief pixAbsDiffByRow() + * + * \param[in] pix 8 bpp; no colormap + * \param[in] box [optional] clipping box for region; can be null + * \return na of abs val pixel difference averages by row, or NULL on error + * + *
+ * Notes:
+ *      (1) This is an average over differences of adjacent pixels along
+ *          each row.
+ *      (2) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ * 
+ */ +NUMA * +pixAbsDiffByRow(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; +l_uint32 *line, *data; +l_float64 norm, sum; +NUMA *na; + + PROCNAME("pixAbsDiffByRow"); + + if (!pix || pixGetDepth(pix) != 8) + return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + if (bw < 2) + return (NUMA *)ERROR_PTR("row width must be >= 2", procName, NULL); + + norm = 1. / (l_float32)(bw - 1); + if ((na = numaCreate(bh)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, ystart, 1); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = ystart; i < yend; i++) { + sum = 0.0; + line = data + i * wpl; + val0 = GET_DATA_BYTE(line, xstart); + for (j = xstart + 1; j < xend; j++) { + val1 = GET_DATA_BYTE(line, j); + sum += L_ABS(val1 - val0); + val0 = val1; + } + numaAddNumber(na, (l_float32)(norm * sum)); + } + + return na; +} + + +/*! + * \brief pixAbsDiffByColumn() + * + * \param[in] pix 8 bpp; no colormap + * \param[in] box [optional] clipping box for region; can be null + * \return na of abs val pixel difference averages by column, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This is an average over differences of adjacent pixels along
+ *          each column.
+ *      (2) To resample for a bin size different from 1, use
+ *          numaUniformSampling() on the result of this function.
+ * 
+ */ +NUMA * +pixAbsDiffByColumn(PIX *pix, + BOX *box) +{ +l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; +l_uint32 *line, *data; +l_float64 norm, sum; +NUMA *na; + + PROCNAME("pixAbsDiffByColumn"); + + if (!pix || pixGetDepth(pix) != 8) + return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); + if (pixGetColormap(pix) != NULL) + return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); + if (bh < 2) + return (NUMA *)ERROR_PTR("column height must be >= 2", procName, NULL); + + norm = 1. / (l_float32)(bh - 1); + if ((na = numaCreate(bw)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetParameters(na, xstart, 1); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (j = xstart; j < xend; j++) { + sum = 0.0; + line = data + ystart * wpl; + val0 = GET_DATA_BYTE(line, j); + for (i = ystart + 1; i < yend; i++) { + line = data + i * wpl; + val1 = GET_DATA_BYTE(line, j); + sum += L_ABS(val1 - val0); + val0 = val1; + } + numaAddNumber(na, (l_float32)(norm * sum)); + } + + return na; +} + + +/*! + * \brief pixAbsDiffInRect() + * + * \param[in] pix 8 bpp; not cmapped + * \param[in] box [optional] if null, use entire image + * \param[in] dir differences along L_HORIZONTAL_LINE or L_VERTICAL_LINE + * \param[out] pabsdiff average of abs diff pixel values in region + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This gives the average over the abs val of differences of
+ *          adjacent pixels values, along either each
+ *             row:     dir == L_HORIZONTAL_LINE
+ *             column:  dir == L_VERTICAL_LINE
+ * 
+ */ +l_ok +pixAbsDiffInRect(PIX *pix, + BOX *box, + l_int32 dir, + l_float32 *pabsdiff) +{ +l_int32 w, h, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val0, val1; +l_uint32 *data, *line; +l_float64 norm, sum; + + PROCNAME("pixAbsDiffInRect"); + + if (!pabsdiff) + return ERROR_INT("&absdiff not defined", procName, 1); + *pabsdiff = 0.0; + if (!pix || pixGetDepth(pix) != 8) + return ERROR_INT("pix undefined or not 8 bpp", procName, 1); + if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) + return ERROR_INT("invalid direction", procName, 1); + if (pixGetColormap(pix) != NULL) + return ERROR_INT("pix is colormapped", procName, 1); + + pixGetDimensions(pix, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return ERROR_INT("invalid clipping box", procName, 1); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + if (dir == L_HORIZONTAL_LINE) { + norm = 1. / (l_float32)(bh * (bw - 1)); + sum = 0.0; + for (i = ystart; i < yend; i++) { + line = data + i * wpl; + val0 = GET_DATA_BYTE(line, xstart); + for (j = xstart + 1; j < xend; j++) { + val1 = GET_DATA_BYTE(line, j); + sum += L_ABS(val1 - val0); + val0 = val1; + } + } + } else { /* vertical line */ + norm = 1. / (l_float32)(bw * (bh - 1)); + sum = 0.0; + for (j = xstart; j < xend; j++) { + line = data + ystart * wpl; + val0 = GET_DATA_BYTE(line, j); + for (i = ystart + 1; i < yend; i++) { + line = data + i * wpl; + val1 = GET_DATA_BYTE(line, j); + sum += L_ABS(val1 - val0); + val0 = val1; + } + } + } + *pabsdiff = (l_float32)(norm * sum); + return 0; +} + + +/*! + * \brief pixAbsDiffOnLine() + * + * \param[in] pix 8 bpp; not cmapped + * \param[in] x1, y1 first point; x1 <= x2, y1 <= y2 + * \param[in] x2, y2 first point + * \param[out] pabsdiff average of abs diff pixel values on line + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This gives the average over the abs val of differences of
+ *          adjacent pixels values, along a line that is either horizontal
+ *          or vertical.
+ *      (2) If horizontal, require x1 < x2; if vertical, require y1 < y2.
+ * 
+ */ +l_ok +pixAbsDiffOnLine(PIX *pix, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_float32 *pabsdiff) +{ +l_int32 w, h, i, j, dir, size, sum; +l_uint32 val0, val1; + + PROCNAME("pixAbsDiffOnLine"); + + if (!pabsdiff) + return ERROR_INT("&absdiff not defined", procName, 1); + *pabsdiff = 0.0; + if (!pix || pixGetDepth(pix) != 8) + return ERROR_INT("pix undefined or not 8 bpp", procName, 1); + if (y1 == y2) { + dir = L_HORIZONTAL_LINE; + } else if (x1 == x2) { + dir = L_VERTICAL_LINE; + } else { + return ERROR_INT("line is neither horiz nor vert", procName, 1); + } + if (pixGetColormap(pix) != NULL) + return ERROR_INT("pix is colormapped", procName, 1); + + pixGetDimensions(pix, &w, &h, NULL); + sum = 0; + if (dir == L_HORIZONTAL_LINE) { + x1 = L_MAX(x1, 0); + x2 = L_MIN(x2, w - 1); + if (x1 >= x2) + return ERROR_INT("x1 >= x2", procName, 1); + size = x2 - x1; + pixGetPixel(pix, x1, y1, &val0); + for (j = x1 + 1; j <= x2; j++) { + pixGetPixel(pix, j, y1, &val1); + sum += L_ABS((l_int32)val1 - (l_int32)val0); + val0 = val1; + } + } else { /* vertical */ + y1 = L_MAX(y1, 0); + y2 = L_MIN(y2, h - 1); + if (y1 >= y2) + return ERROR_INT("y1 >= y2", procName, 1); + size = y2 - y1; + pixGetPixel(pix, x1, y1, &val0); + for (i = y1 + 1; i <= y2; i++) { + pixGetPixel(pix, x1, i, &val1); + sum += L_ABS((l_int32)val1 - (l_int32)val0); + val0 = val1; + } + } + *pabsdiff = (l_float32)sum / (l_float32)size; + return 0; +} + + +/*-------------------------------------------------------------* + * Count of pixels with specific value * + *-------------------------------------------------------------*/ +/*! + * \brief pixCountArbInRect() + * + * \param[in] pixs 8 bpp, or colormapped + * \param[in] box [optional] over which count is made; + * use entire image if NULL + * \param[in] val pixel value to count + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] pcount count; estimate it if factor > 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is cmapped, %val is compared to the colormap index;
+ *          otherwise, %val is compared to the grayscale value.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ *          If %factor > 1, multiply the count by %factor * %factor.
+ * 
+ */ +l_int32 +pixCountArbInRect(PIX *pixs, + BOX *box, + l_int32 val, + l_int32 factor, + l_int32 *pcount) +{ +l_int32 i, j, bx, by, bw, bh, w, h, wpl, pixval; +l_uint32 *data, *line; + + PROCNAME("pixCountArbInRect"); + + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) + return ERROR_INT("pixs neither 8 bpp nor colormapped", + procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor < 1", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + + if (!box) { + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + pixval = GET_DATA_BYTE(line, j); + if (pixval == val) (*pcount)++; + } + } + } else { + boxGetGeometry(box, &bx, &by, &bw, &bh); + for (i = 0; i < bh; i += factor) { + if (by + i < 0 || by + i >= h) continue; + line = data + (by + i) * wpl; + for (j = 0; j < bw; j += factor) { + if (bx + j < 0 || bx + j >= w) continue; + pixval = GET_DATA_BYTE(line, bx + j); + if (pixval == val) (*pcount)++; + } + } + } + + if (factor > 1) /* assume pixel color is randomly distributed */ + *pcount = *pcount * factor * factor; + return 0; +} + + +/*-------------------------------------------------------------* + * Mirrored tiling of a smaller image * + *-------------------------------------------------------------*/ +/*! + * \brief pixMirroredTiling() + * + * \param[in] pixs 8 or 32 bpp, small tile; to be replicated + * \param[in] w, h dimensions of output pix + * \return pixd usually larger pix, mirror-tiled with pixs, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This uses mirrored tiling, where each row alternates
+ *          with LR flips and every column alternates with TB
+ *          flips, such that the result is a tiling with identical
+ *          2 x 2 tiles, each of which is composed of these transforms:
+ *                  -----------------
+ *                  | 1    |  LR    |
+ *                  -----------------
+ *                  | TB   |  LR/TB |
+ *                  -----------------
+ * 
+ */ +PIX * +pixMirroredTiling(PIX *pixs, + l_int32 w, + l_int32 h) +{ +l_int32 wt, ht, d, i, j, nx, ny; +PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix; + + PROCNAME("pixMirroredTiling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &wt, &ht, &d); + if (wt <= 0 || ht <= 0) + return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopySpp(pixd, pixs); + + nx = (w + wt - 1) / wt; + ny = (h + ht - 1) / ht; + pixsfx = pixFlipLR(NULL, pixs); + pixsfy = pixFlipTB(NULL, pixs); + pixsfxy = pixFlipTB(NULL, pixsfx); + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + pix = pixs; + if ((i & 1) && !(j & 1)) + pix = pixsfy; + else if (!(i & 1) && (j & 1)) + pix = pixsfx; + else if ((i & 1) && (j & 1)) + pix = pixsfxy; + pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0); + } + } + + pixDestroy(&pixsfx); + pixDestroy(&pixsfy); + pixDestroy(&pixsfxy); + return pixd; +} + + +/*! + * \brief pixFindRepCloseTile() + * + * \param[in] pixs 32 bpp rgb + * \param[in] box region of pixs to search around + * \param[in] searchdir L_HORIZ or L_VERT; direction to search + * \param[in] mindist min distance of selected tile edge from box; >= 0 + * \param[in] tsize tile size; > 1; even; typically ~50 + * \param[in] ntiles number of tiles tested in each row/column + * \param[out] pboxtile region of best tile + * \param[in] debug 1 for debug output + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This looks for one or two square tiles with conforming median
+ *          intensity and low variance, that is outside but near the input box.
+ *      (2) %mindist specifies the gap between the box and the
+ *          potential tiles.  The tiles are given an overlap of 50%.
+ *          %ntiles specifies the number of tiles that are tested
+ *          beyond %mindist for each row or column.
+ *      (3) For example, if %mindist = 20, %tilesize = 50 and %ntiles = 3,
+ *          a horizontal search to the right will have 3 tiles in each row,
+ *          with left edges at 20, 45 and 70 from the right edge of the
+ *          input %box.  The number of rows of tiles is determined by
+ *          the height of %box and %tsize, with the 50% overlap..
+ * 
+ */ +l_ok +pixFindRepCloseTile(PIX *pixs, + BOX *box, + l_int32 searchdir, + l_int32 mindist, + l_int32 tsize, + l_int32 ntiles, + BOX **pboxtile, + l_int32 debug) +{ +l_int32 w, h, i, n, bestindex; +l_float32 var_of_mean, median_of_mean, median_of_stdev, mean_val, stdev_val; +l_float32 mindels, bestdelm, delm, dels, mean, stdev; +BOXA *boxa; +NUMA *namean, *nastdev; +PIX *pix, *pixg; +PIXA *pixa; + + PROCNAME("pixFindRepCloseTile"); + + if (!pboxtile) + return ERROR_INT("&boxtile not defined", procName, 1); + *pboxtile = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (searchdir != L_HORIZ && searchdir != L_VERT) + return ERROR_INT("invalid searchdir", procName, 1); + if (mindist < 0) + return ERROR_INT("mindist must be >= 0", procName, 1); + if (tsize < 2) + return ERROR_INT("tsize must be > 1", procName, 1); + if (ntiles > 7) { + L_WARNING("ntiles = %d; larger than suggested max of 7\n", + procName, ntiles); + } + + /* Locate tile regions */ + pixGetDimensions(pixs, &w, &h, NULL); + boxa = findTileRegionsForSearch(box, w, h, searchdir, mindist, + tsize, ntiles); + if (!boxa) + return ERROR_INT("no tiles found", procName, 1); + + /* Generate the tiles and the mean and stdev of intensity */ + pixa = pixClipRectangles(pixs, boxa); + n = pixaGetCount(pixa); + namean = numaCreate(n); + nastdev = numaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixg = pixConvertRGBToGray(pix, 0.33, 0.34, 0.33); + pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &mean); + pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_STANDARD_DEVIATION, &stdev); + numaAddNumber(namean, mean); + numaAddNumber(nastdev, stdev); + pixDestroy(&pix); + pixDestroy(&pixg); + } + + /* Find the median and variance of the averages. We require + * the best tile to have a mean pixel intensity within a standard + * deviation of the median of mean intensities, and choose the + * tile in that set with the smallest stdev of pixel intensities + * (as a proxy for the tile with least visible structure). + * The median of the stdev is used, for debugging, as a normalizing + * factor for the stdev of intensities within a tile. */ + numaGetStatsUsingHistogram(namean, 256, NULL, NULL, NULL, &var_of_mean, + &median_of_mean, 0.0, NULL, NULL); + numaGetStatsUsingHistogram(nastdev, 256, NULL, NULL, NULL, NULL, + &median_of_stdev, 0.0, NULL, NULL); + mindels = 1000.0; + bestdelm = 1000.0; + bestindex = 0; + for (i = 0; i < n; i++) { + numaGetFValue(namean, i, &mean_val); + numaGetFValue(nastdev, i, &stdev_val); + if (var_of_mean == 0.0) { /* uniform color; any box will do */ + delm = 0.0; /* any value < 1.01 */ + dels = 1.0; /* n'importe quoi */ + } else { + delm = L_ABS(mean_val - median_of_mean) / sqrt(var_of_mean); + dels = stdev_val / median_of_stdev; + } + if (delm < 1.01) { + if (dels < mindels) { + if (debug) { + fprintf(stderr, "i = %d, mean = %7.3f, delm = %7.3f," + " stdev = %7.3f, dels = %7.3f\n", + i, mean_val, delm, stdev_val, dels); + } + mindels = dels; + bestdelm = delm; + bestindex = i; + } + } + } + *pboxtile = boxaGetBox(boxa, bestindex, L_COPY); + + if (debug) { + L_INFO("median of mean = %7.3f\n", procName, median_of_mean); + L_INFO("standard dev of mean = %7.3f\n", procName, sqrt(var_of_mean)); + L_INFO("median of stdev = %7.3f\n", procName, median_of_stdev); + L_INFO("best tile: index = %d\n", procName, bestindex); + L_INFO("delta from median in units of stdev = %5.3f\n", + procName, bestdelm); + L_INFO("stdev as fraction of median stdev = %5.3f\n", + procName, mindels); + } + + numaDestroy(&namean); + numaDestroy(&nastdev); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + return 0; +} + + +/*! + * \brief findTileRegionsForSearch() + * + * \param[in] box region of Pix to search around + * \param[in] w, h dimensions of Pix + * \param[in] searchdir L_HORIZ or L_VERT; direction to search + * \param[in] mindist min distance of selected tile edge from box; >= 0 + * \param[in] tsize tile size; > 1; even; typically ~50 + * \param[in] ntiles number of tiles tested in each row/column + * \return boxa if OK, or NULL on error + * + *
+ * Notes:
+ *      (1) See calling function pixfindRepCloseTile().
+ * 
+ */ +static BOXA * +findTileRegionsForSearch(BOX *box, + l_int32 w, + l_int32 h, + l_int32 searchdir, + l_int32 mindist, + l_int32 tsize, + l_int32 ntiles) +{ +l_int32 bx, by, bw, bh, left, right, top, bot, i, j, nrows, ncols; +l_int32 x0, y0, x, y, w_avail, w_needed, h_avail, h_needed, t_avail; +BOX *box1; +BOXA *boxa; + + PROCNAME("findTileRegionsForSearch"); + + if (!box) + return (BOXA *)ERROR_PTR("box not defined", procName, NULL); + if (ntiles == 0) + return (BOXA *)ERROR_PTR("no tiles requested", procName, NULL); + + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (searchdir == L_HORIZ) { + /* Find the tile parameters for the search. Note that the + * tiles are overlapping by 50% in each direction. */ + left = bx; /* distance to left of box */ + right = w - bx - bw + 1; /* distance to right of box */ + w_avail = L_MAX(left, right) - mindist; + if (tsize & 1) tsize++; /* be sure it's even */ + if (w_avail < tsize) { + L_ERROR("tsize = %d, w_avail = %d\n", procName, tsize, w_avail); + return NULL; + } + w_needed = tsize + (ntiles - 1) * (tsize / 2); + if (w_needed > w_avail) { + t_avail = 1 + 2 * (w_avail - tsize) / tsize; + L_WARNING("ntiles = %d; room for only %d\n", procName, + ntiles, t_avail); + ntiles = t_avail; + w_needed = tsize + (ntiles - 1) * (tsize / 2); + } + nrows = L_MAX(1, 1 + 2 * (bh - tsize) / tsize); + + /* Generate the tile regions to search */ + boxa = boxaCreate(0); + if (left > right) /* search to left */ + x0 = bx - w_needed; + else /* search to right */ + x0 = bx + bw + mindist; + for (i = 0; i < nrows; i++) { + y = by + i * tsize / 2; + for (j = 0; j < ntiles; j++) { + x = x0 + j * tsize / 2; + box1 = boxCreate(x, y, tsize, tsize); + boxaAddBox(boxa, box1, L_INSERT); + } + } + } else { /* L_VERT */ + /* Find the tile parameters for the search */ + top = by; /* distance above box */ + bot = h - by - bh + 1; /* distance below box */ + h_avail = L_MAX(top, bot) - mindist; + if (h_avail < tsize) { + L_ERROR("tsize = %d, h_avail = %d\n", procName, tsize, h_avail); + return NULL; + } + h_needed = tsize + (ntiles - 1) * (tsize / 2); + if (h_needed > h_avail) { + t_avail = 1 + 2 * (h_avail - tsize) / tsize; + L_WARNING("ntiles = %d; room for only %d\n", procName, + ntiles, t_avail); + ntiles = t_avail; + h_needed = tsize + (ntiles - 1) * (tsize / 2); + } + ncols = L_MAX(1, 1 + 2 * (bw - tsize) / tsize); + + /* Generate the tile regions to search */ + boxa = boxaCreate(0); + if (top > bot) /* search above */ + y0 = by - h_needed; + else /* search below */ + y0 = by + bh + mindist; + for (j = 0; j < ncols; j++) { + x = bx + j * tsize / 2; + for (i = 0; i < ntiles; i++) { + y = y0 + i * tsize / 2; + box1 = boxCreate(x, y, tsize, tsize); + boxaAddBox(boxa, box1, L_INSERT); + } + } + } + return boxa; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix4.c b/hgdriver/3rdparty/hgOCR/leptonica/pix4.c new file mode 100644 index 0000000..526232a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix4.c @@ -0,0 +1,3437 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pix4.c + *
+ *
+ *    This file has these operations:
+ *
+ *      (1) Pixel histograms
+ *      (2) Pixel row/column statistics
+ *      (3) Foreground/background estimation
+ *
+ *    Pixel histogram, rank val, averaging and min/max
+ *           NUMA       *pixGetGrayHistogram()
+ *           NUMA       *pixGetGrayHistogramMasked()
+ *           NUMA       *pixGetGrayHistogramInRect()
+ *           NUMAA      *pixGetGrayHistogramTiled()
+ *           l_int32     pixGetColorHistogram()
+ *           l_int32     pixGetColorHistogramMasked()
+ *           NUMA       *pixGetCmapHistogram()
+ *           NUMA       *pixGetCmapHistogramMasked()
+ *           NUMA       *pixGetCmapHistogramInRect()
+ *           l_int32     pixCountRGBColors()
+ *           L_AMAP     *pixGetColorAmapHistogram()
+ *           l_int32     amapGetCountForColor()
+ *           l_int32     pixGetRankValue()
+ *           l_int32     pixGetRankValueMaskedRGB()
+ *           l_int32     pixGetRankValueMasked()
+ *           l_int32     pixGetPixelAverage()
+ *           l_int32     pixGetPixelStats()
+ *           l_int32     pixGetAverageMaskedRGB()
+ *           l_int32     pixGetAverageMasked()
+ *           l_int32     pixGetAverageTiledRGB()
+ *           PIX        *pixGetAverageTiled()
+ *           NUMA       *pixRowStats()
+ *           NUMA       *pixColumnStats()
+ *           l_int32     pixGetRangeValues()
+ *           l_int32     pixGetExtremeValue()
+ *           l_int32     pixGetMaxValueInRect()
+ *           l_int32     pixGetBinnedComponentRange()
+ *           l_int32     pixGetRankColorArray()
+ *           l_int32     pixGetBinnedColor()
+ *           PIX        *pixDisplayColorArray()
+ *           PIX        *pixRankBinByStrip()
+ *
+ *    Pixelwise aligned statistics
+ *           PIX        *pixaGetAlignedStats()
+ *           l_int32     pixaExtractColumnFromEachPix()
+ *           l_int32     pixGetRowStats()
+ *           l_int32     pixGetColumnStats()
+ *           l_int32     pixSetPixelColumn()
+ *
+ *    Foreground/background estimation
+ *           l_int32     pixThresholdForFgBg()
+ *           l_int32     pixSplitDistributionFgBg()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + +/*------------------------------------------------------------------* + * Pixel histogram and averaging * + *------------------------------------------------------------------*/ +/*! + * \brief pixGetGrayHistogram() + * + * \param[in] pixs 1, 2, 4, 8, 16 bpp; can be colormapped + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, it is converted to 8 bpp gray.
+ *          If you want a histogram of the colormap indices, use
+ *          pixGetCmapHistogram().
+ *      (2) If pixs does not have a colormap, the output histogram is
+ *          of size 2^d, where d is the depth of pixs.
+ *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
+ * 
+ */ +NUMA * +pixGetGrayHistogram(PIX *pixs, + l_int32 factor) +{ +l_int32 i, j, w, h, d, wpl, val, size, count; +l_uint32 *data, *line; +l_float32 *array; +NUMA *na; +PIX *pixg; + + PROCNAME("pixGetGrayHistogram"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d > 16) + return (NUMA *)ERROR_PTR("depth not in {1,2,4,8,16}", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + + if (pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + + pixGetDimensions(pixg, &w, &h, &d); + size = 1 << d; + if ((na = numaCreate(size)) == NULL) { + pixDestroy(&pixg); + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + } + numaSetCount(na, size); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + if (d == 1) { /* special case */ + pixCountPixels(pixg, &count, NULL); + array[0] = w * h - count; + array[1] = count; + pixDestroy(&pixg); + return na; + } + + wpl = pixGetWpl(pixg); + data = pixGetData(pixg); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + if (d == 2) { + for (j = 0; j < w; j += factor) { + val = GET_DATA_DIBIT(line, j); + array[val] += 1.0; + } + } else if (d == 4) { + for (j = 0; j < w; j += factor) { + val = GET_DATA_QBIT(line, j); + array[val] += 1.0; + } + } else if (d == 8) { + for (j = 0; j < w; j += factor) { + val = GET_DATA_BYTE(line, j); + array[val] += 1.0; + } + } else { /* d == 16 */ + for (j = 0; j < w; j += factor) { + val = GET_DATA_TWO_BYTES(line, j); + array[val] += 1.0; + } + } + } + + pixDestroy(&pixg); + return na; +} + + +/*! + * \brief pixGetGrayHistogramMasked() + * + * \param[in] pixs 8 bpp, or colormapped + * \param[in] pixm [optional] 1 bpp mask over which histogram is + * to be computed; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0; these values are ignored if pixm is null + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
+ *          If you want a histogram of the colormap indices, use
+ *          pixGetCmapHistogramMasked().
+ *      (2) This always returns a 256-value histogram of pixel values.
+ *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
+ *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
+ *      (5) Input x,y are ignored unless pixm exists.
+ * 
+ */ +NUMA * +pixGetGrayHistogramMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor) +{ +l_int32 i, j, w, h, wm, hm, dm, wplg, wplm, val; +l_uint32 *datag, *datam, *lineg, *linem; +l_float32 *array; +NUMA *na; +PIX *pixg; + + PROCNAME("pixGetGrayHistogramMasked"); + + if (!pixm) + return pixGetGrayHistogram(pixs, factor); + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) + return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped", + procName, NULL); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (dm != 1) + return (NUMA *)ERROR_PTR("pixm not 1 bpp", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + + if ((na = numaCreate(256)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, 256); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + if (pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + pixGetDimensions(pixg, &w, &h, NULL); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + + /* Generate the histogram */ + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + lineg = datag + (y + i) * wplg; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + val = GET_DATA_BYTE(lineg, x + j); + array[val] += 1.0; + } + } + } + + pixDestroy(&pixg); + return na; +} + + +/*! + * \brief pixGetGrayHistogramInRect() + * + * \param[in] pixs 8 bpp, or colormapped + * \param[in] box [optional] over which histogram is to be computed; + * use full image if NULL + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
+ *          If you want a histogram of the colormap indices, use
+ *          pixGetCmapHistogramInRect().
+ *      (2) This always returns a 256-value histogram of pixel values.
+ *      (3) Set the subsampling %factor > 1 to reduce the amount of computation.
+ * 
+ */ +NUMA * +pixGetGrayHistogramInRect(PIX *pixs, + BOX *box, + l_int32 factor) +{ +l_int32 i, j, bx, by, bw, bh, w, h, wplg, val; +l_uint32 *datag, *lineg; +l_float32 *array; +NUMA *na; +PIX *pixg; + + PROCNAME("pixGetGrayHistogramInRect"); + + if (!box) + return pixGetGrayHistogram(pixs, factor); + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) + return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped", + procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + + if ((na = numaCreate(256)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, 256); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + if (pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + pixGetDimensions(pixg, &w, &h, NULL); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + boxGetGeometry(box, &bx, &by, &bw, &bh); + + /* Generate the histogram */ + for (i = 0; i < bh; i += factor) { + if (by + i < 0 || by + i >= h) continue; + lineg = datag + (by + i) * wplg; + for (j = 0; j < bw; j += factor) { + if (bx + j < 0 || bx + j >= w) continue; + val = GET_DATA_BYTE(lineg, bx + j); + array[val] += 1.0; + } + } + + pixDestroy(&pixg); + return na; +} + + +/*! + * \brief pixGetGrayHistogramTiled() + * + * \param[in] pixs any depth, colormap OK + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] nx, ny tiling; >= 1; typically small + * \return naa set of histograms, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is cmapped, it is converted to 8 bpp gray.
+ *      (2) This returns a set of 256-value histograms of pixel values.
+ *      (3) Set the subsampling factor > 1 to reduce the amount of computation.
+ * 
+ */ +NUMAA * +pixGetGrayHistogramTiled(PIX *pixs, + l_int32 factor, + l_int32 nx, + l_int32 ny) +{ +l_int32 i, n; +NUMA *na; +NUMAA *naa; +PIX *pix1, *pix2; +PIXA *pixa; + + PROCNAME("pixGetGrayHistogramTiled"); + + if (!pixs) + return (NUMAA *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (NUMAA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + if (nx < 1 || ny < 1) + return (NUMAA *)ERROR_PTR("nx and ny must both be > 0", procName, NULL); + + n = nx * ny; + if ((naa = numaaCreate(n)) == NULL) + return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); + + pix1 = pixConvertTo8(pixs, FALSE); + pixa = pixaSplitPix(pix1, nx, ny, 0, 0); + for (i = 0; i < n; i++) { + pix2 = pixaGetPix(pixa, i, L_CLONE); + na = pixGetGrayHistogram(pix2, factor); + numaaAddNuma(naa, na, L_INSERT); + pixDestroy(&pix2); + } + + pixDestroy(&pix1); + pixaDestroy(&pixa); + return naa; +} + + +/*! + * \brief pixGetColorHistogram() + * + * \param[in] pixs rgb or colormapped + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] pnar red histogram + * \param[out] pnag green histogram + * \param[out] pnab blue histogram + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a set of three 256 entry histograms,
+ *          one for each color component (r,g,b).
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ * 
+ */ +l_ok +pixGetColorHistogram(PIX *pixs, + l_int32 factor, + NUMA **pnar, + NUMA **pnag, + NUMA **pnab) +{ +l_int32 i, j, w, h, d, wpl, index, rval, gval, bval; +l_uint32 *data, *line; +l_float32 *rarray, *garray, *barray; +NUMA *nar, *nag, *nab; +PIXCMAP *cmap; + + PROCNAME("pixGetColorHistogram"); + + if (pnar) *pnar = NULL; + if (pnag) *pnag = NULL; + if (pnab) *pnab = NULL; + if (!pnar || !pnag || !pnab) + return ERROR_INT("&nar, &nag, &nab not all defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (cmap && (d != 2 && d != 4 && d != 8)) + return ERROR_INT("colormap and not 2, 4, or 8 bpp", procName, 1); + if (!cmap && d != 32) + return ERROR_INT("no colormap and not rgb", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + + /* Set up the histogram arrays */ + nar = numaCreate(256); + nag = numaCreate(256); + nab = numaCreate(256); + numaSetCount(nar, 256); + numaSetCount(nag, 256); + numaSetCount(nab, 256); + rarray = numaGetFArray(nar, L_NOCOPY); + garray = numaGetFArray(nag, L_NOCOPY); + barray = numaGetFArray(nab, L_NOCOPY); + *pnar = nar; + *pnag = nag; + *pnab = nab; + + /* Generate the color histograms */ + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (cmap) { + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + if (d == 8) + index = GET_DATA_BYTE(line, j); + else if (d == 4) + index = GET_DATA_QBIT(line, j); + else /* 2 bpp */ + index = GET_DATA_DIBIT(line, j); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + rarray[rval] += 1.0; + garray[gval] += 1.0; + barray[bval] += 1.0; + } + } + } else { /* 32 bpp rgb */ + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + extractRGBValues(line[j], &rval, &gval, &bval); + rarray[rval] += 1.0; + garray[gval] += 1.0; + barray[bval] += 1.0; + } + } + } + + return 0; +} + + +/*! + * \brief pixGetColorHistogramMasked() + * + * \param[in] pixs 32 bpp rgb, or colormapped + * \param[in] pixm [optional] 1 bpp mask over which histogram is + * to be computed; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0; these values are ignored if pixm is null + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] pnar red histogram + * \param[out] pnag green histogram + * \param[out] pnab blue histogram + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a set of three 256 entry histograms,
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ *      (3) Clipping of pixm (if it exists) to pixs is done in the inner loop.
+ *      (4) Input x,y are ignored unless pixm exists.
+ * 
+ */ +l_ok +pixGetColorHistogramMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + NUMA **pnar, + NUMA **pnag, + NUMA **pnab) +{ +l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, index, rval, gval, bval; +l_uint32 *datas, *datam, *lines, *linem; +l_float32 *rarray, *garray, *barray; +NUMA *nar, *nag, *nab; +PIXCMAP *cmap; + + PROCNAME("pixGetColorHistogramMasked"); + + if (!pixm) + return pixGetColorHistogram(pixs, factor, pnar, pnag, pnab); + + if (pnar) *pnar = NULL; + if (pnag) *pnag = NULL; + if (pnab) *pnab = NULL; + if (!pnar || !pnag || !pnab) + return ERROR_INT("&nar, &nag, &nab not all defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (cmap && (d != 2 && d != 4 && d != 8)) + return ERROR_INT("colormap and not 2, 4, or 8 bpp", procName, 1); + if (!cmap && d != 32) + return ERROR_INT("no colormap and not rgb", procName, 1); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (dm != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + + /* Set up the histogram arrays */ + nar = numaCreate(256); + nag = numaCreate(256); + nab = numaCreate(256); + numaSetCount(nar, 256); + numaSetCount(nag, 256); + numaSetCount(nab, 256); + rarray = numaGetFArray(nar, L_NOCOPY); + garray = numaGetFArray(nag, L_NOCOPY); + barray = numaGetFArray(nab, L_NOCOPY); + *pnar = nar; + *pnag = nag; + *pnab = nab; + + /* Generate the color histograms */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + if (cmap) { + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + lines = datas + (y + i) * wpls; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + if (d == 8) + index = GET_DATA_BYTE(lines, x + j); + else if (d == 4) + index = GET_DATA_QBIT(lines, x + j); + else /* 2 bpp */ + index = GET_DATA_DIBIT(lines, x + j); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + rarray[rval] += 1.0; + garray[gval] += 1.0; + barray[bval] += 1.0; + } + } + } + } else { /* 32 bpp rgb */ + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + lines = datas + (y + i) * wpls; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + extractRGBValues(lines[x + j], &rval, &gval, &bval); + rarray[rval] += 1.0; + garray[gval] += 1.0; + barray[bval] += 1.0; + } + } + } + } + + return 0; +} + + +/*! + * \brief pixGetCmapHistogram() + * + * \param[in] pixs colormapped: d = 2, 4 or 8 + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram of cmap indices, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a histogram of colormap pixel indices,
+ *          and is of size 2^d.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ * 
+ */ +NUMA * +pixGetCmapHistogram(PIX *pixs, + l_int32 factor) +{ +l_int32 i, j, w, h, d, wpl, val, size; +l_uint32 *data, *line; +l_float32 *array; +NUMA *na; + + PROCNAME("pixGetCmapHistogram"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) == NULL) + return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); + + size = 1 << d; + if ((na = numaCreate(size)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, size); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + if (d == 8) + val = GET_DATA_BYTE(line, j); + else if (d == 4) + val = GET_DATA_QBIT(line, j); + else /* d == 2 */ + val = GET_DATA_DIBIT(line, j); + array[val] += 1.0; + } + } + + return na; +} + + +/*! + * \brief pixGetCmapHistogramMasked() + * + * \param[in] pixs colormapped: d = 2, 4 or 8 + * \param[in] pixm [optional] 1 bpp mask over which histogram is + * to be computed; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0; these values are ignored if pixm is null + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a histogram of colormap pixel indices,
+ *          and is of size 2^d.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ *      (3) Clipping of pixm to pixs is done in the inner loop.
+ * 
+ */ +NUMA * +pixGetCmapHistogramMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor) +{ +l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, val, size; +l_uint32 *datas, *datam, *lines, *linem; +l_float32 *array; +NUMA *na; + + PROCNAME("pixGetCmapHistogramMasked"); + + if (!pixm) + return pixGetCmapHistogram(pixs, factor); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) == NULL) + return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); + pixGetDimensions(pixm, &wm, &hm, &dm); + if (dm != 1) + return (NUMA *)ERROR_PTR("pixm not 1 bpp", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); + + size = 1 << d; + if ((na = numaCreate(size)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, size); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + lines = datas + (y + i) * wpls; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + if (d == 8) + val = GET_DATA_BYTE(lines, x + j); + else if (d == 4) + val = GET_DATA_QBIT(lines, x + j); + else /* d == 2 */ + val = GET_DATA_DIBIT(lines, x + j); + array[val] += 1.0; + } + } + } + + return na; +} + + +/*! + * \brief pixGetCmapHistogramInRect() + * + * \param[in] pixs colormapped: d = 2, 4 or 8 + * \param[in] box [optional] over which histogram is to be computed; + * use full image if NULL + * \param[in] factor subsampling factor; integer >= 1 + * \return na histogram, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a histogram of colormap pixel indices,
+ *          and is of size 2^d.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
+ *      (3) Clipping to the box is done in the inner loop.
+ * 
+ */ +NUMA * +pixGetCmapHistogramInRect(PIX *pixs, + BOX *box, + l_int32 factor) +{ +l_int32 i, j, bx, by, bw, bh, w, h, d, wpls, val, size; +l_uint32 *datas, *lines; +l_float32 *array; +NUMA *na; + + PROCNAME("pixGetCmapHistogramInRect"); + + if (!box) + return pixGetCmapHistogram(pixs, factor); + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) == NULL) + return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); + if (factor < 1) + return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); + + size = 1 << d; + if ((na = numaCreate(size)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + numaSetCount(na, size); /* all initialized to 0.0 */ + array = numaGetFArray(na, L_NOCOPY); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + boxGetGeometry(box, &bx, &by, &bw, &bh); + + for (i = 0; i < bh; i += factor) { + if (by + i < 0 || by + i >= h) continue; + lines = datas + (by + i) * wpls; + for (j = 0; j < bw; j += factor) { + if (bx + j < 0 || bx + j >= w) continue; + if (d == 8) + val = GET_DATA_BYTE(lines, bx + j); + else if (d == 4) + val = GET_DATA_QBIT(lines, bx + j); + else /* d == 2 */ + val = GET_DATA_DIBIT(lines, bx + j); + array[val] += 1.0; + } + } + + return na; +} + + +/*! + * \brief pixCountRGBColors() + * + * \param[in] pixs rgb or rgba + * \return ncolors, or -1 on error + */ +l_int32 +pixCountRGBColors(PIX *pixs) +{ +l_int32 ncolors; +L_AMAP *amap; + + PROCNAME("pixCountRGBColors"); + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, -1); + amap = pixGetColorAmapHistogram(pixs, 1); + ncolors = l_amapSize(amap); + l_amapDestroy(&amap); + return ncolors; +} + + +/*! + * \brief pixGetColorAmapHistogram() + * + * \param[in] pixs rgb or rgba + * \param[in] factor subsampling factor; integer >= 1 + * \return amap, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates an ordered map from pixel value to histogram count.
+ *      (2) Use amapGetCountForColor() to use the map to look up a count.
+ * 
+ */ +L_AMAP * +pixGetColorAmapHistogram(PIX *pixs, + l_int32 factor) +{ +l_int32 i, j, w, h, wpl; +l_uint32 *data, *line; +L_AMAP *amap; +RB_TYPE key, value; +RB_TYPE *pval; + + PROCNAME("pixGetColorAmapHistogram"); + + if (!pixs) + return (L_AMAP *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (L_AMAP *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + amap = l_amapCreate(L_UINT_TYPE); + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + key.utype = line[j]; + pval = l_amapFind(amap, key); + if (!pval) + value.itype = 1; + else + value.itype = 1 + pval->itype; + l_amapInsert(amap, key, value); + } + } + + return amap; +} + + +/*! + * \brief amapGetCountForColor() + * + * \param[in] amap map from pixel value to count + * \param[in] val rgb or rgba pixel value + * \return count, or -1 on error + * + *
+ * Notes:
+ *      (1) The ordered map is made by pixGetColorAmapHistogram().
+ * 
+ */ +l_int32 +amapGetCountForColor(L_AMAP *amap, + l_uint32 val) +{ +RB_TYPE key; +RB_TYPE *pval; + + PROCNAME("amapGetCountForColor"); + + if (!amap) + return ERROR_INT("amap not defined", procName, -1); + + key.utype = val; + pval = l_amapFind(amap, key); + return (pval) ? pval->itype : 0; +} + + +/*! + * \brief pixGetRankValue() + * + * \param[in] pixs 8 bpp, 32 bpp or colormapped + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest + * \param[out] pvalue pixel value corresponding to input rank + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simple function to get rank values of an image.
+ *          For a color image, the median value (rank = 0.5) can be
+ *          used to linearly remap the colors based on the median
+ *          of a target image, using pixLinearMapToTargetColor().
+ * 
+ */ +l_ok +pixGetRankValue(PIX *pixs, + l_int32 factor, + l_float32 rank, + l_uint32 *pvalue) +{ +l_int32 d; +l_float32 val, rval, gval, bval; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGetRankValue"); + + if (!pvalue) + return ERROR_INT("&value not defined", procName, 1); + *pvalue = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d != 8 && d != 32 && !cmap) + return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", procName, 1); + if (cmap) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pixt = pixClone(pixs); + d = pixGetDepth(pixt); + + if (d == 8) { + pixGetRankValueMasked(pixt, NULL, 0, 0, factor, rank, &val, NULL); + *pvalue = lept_roundftoi(val); + } else { + pixGetRankValueMaskedRGB(pixt, NULL, 0, 0, factor, rank, + &rval, &gval, &bval); + composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval), + lept_roundftoi(bval), pvalue); + } + + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixGetRankValueMaskedRGB() + * + * \param[in] pixs 32 bpp + * \param[in] pixm [optional] 1 bpp mask over which rank val is to be taken; + * use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0; these values are ignored if pixm is null + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest + * \param[out] prval [optional] red component val for input rank + * \param[out] pgval [optional] green component val for input rank + * \param[out] pbval [optional] blue component val for input rank + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Computes the rank component values of pixels in pixs that
+ *          are under the fg of the optional mask.  If the mask is null, it
+ *          computes the average of the pixels in pixs.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of
+ *          computation.
+ *      (4) Input x,y are ignored unless pixm exists.
+ *      (5) The rank must be in [0.0 ... 1.0], where the brightest pixel
+ *          has rank 1.0.  For the median pixel value, use 0.5.
+ * 
+ */ +l_ok +pixGetRankValueMaskedRGB(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + l_float32 rank, + l_float32 *prval, + l_float32 *pgval, + l_float32 *pbval) +{ +l_float32 scale; +PIX *pixmt, *pixt; + + PROCNAME("pixGetRankValueMaskedRGB"); + + if (prval) *prval = 0.0; + if (pgval) *pgval = 0.0; + if (pbval) *pbval = 0.0; + if (!prval && !pgval && !pbval) + return ERROR_INT("no results requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (rank < 0.0 || rank > 1.0) + return ERROR_INT("rank not in [0.0 ... 1.0]", procName, 1); + + pixmt = NULL; + if (pixm) { + scale = 1.0 / (l_float32)factor; + pixmt = pixScale(pixm, scale, scale); + } + if (prval) { + pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_RED); + pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, + factor, rank, prval, NULL); + pixDestroy(&pixt); + } + if (pgval) { + pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_GREEN); + pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, + factor, rank, pgval, NULL); + pixDestroy(&pixt); + } + if (pbval) { + pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_BLUE); + pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, + factor, rank, pbval, NULL); + pixDestroy(&pixt); + } + pixDestroy(&pixmt); + return 0; +} + + +/*! + * \brief pixGetRankValueMasked() + * + * \param[in] pixs 8 bpp, or colormapped + * \param[in] pixm [optional] 1 bpp mask, over which the rank val + * is to be taken; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0; these values are ignored if pixm is null + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest + * \param[out] pval pixel value corresponding to input rank + * \param[out] pna [optional] of histogram + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Computes the rank value of pixels in pixs that are under
+ *          the fg of the optional mask.  If the mask is null, it
+ *          computes the average of the pixels in pixs.
+ *      (2) Set the subsampling %factor > 1 to reduce the amount of
+ *          computation.
+ *      (3) Clipping of pixm (if it exists) to pixs is done in the inner loop.
+ *      (4) Input x,y are ignored unless pixm exists.
+ *      (5) The rank must be in [0.0 ... 1.0], where the brightest pixel
+ *          has rank 1.0.  For the median pixel value, use 0.5.
+ *      (6) The histogram can optionally be returned, so that other rank
+ *          values can be extracted without recomputing the histogram.
+ *          In that case, just use
+ *              numaHistogramGetValFromRank(na, rank, &val);
+ *          on the returned Numa for additional rank values.
+ * 
+ */ +l_ok +pixGetRankValueMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + l_float32 rank, + l_float32 *pval, + NUMA **pna) +{ +NUMA *na; + + PROCNAME("pixGetRankValueMasked"); + + if (pna) *pna = NULL; + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) + return ERROR_INT("pixs neither 8 bpp nor colormapped", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (rank < 0.0 || rank > 1.0) + return ERROR_INT("rank not in [0.0 ... 1.0]", procName, 1); + + if ((na = pixGetGrayHistogramMasked(pixs, pixm, x, y, factor)) == NULL) + return ERROR_INT("na not made", procName, 1); + numaHistogramGetValFromRank(na, rank, pval); + if (pna) + *pna = na; + else + numaDestroy(&na); + + return 0; +} + + +/*! + * \brief pixGetPixelAverage() + * + * \param[in] pixs 8 or 32 bpp, or colormapped + * \param[in] pixm [optional] 1 bpp mask over which average is + * to be taken; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0 + * \param[in] factor subsampling factor; >= 1 + * \param[out] pval average pixel value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For rgb pix, this is a more direct computation of the
+ *          average value of the pixels in %pixs that are under the
+ *          mask %pixm. It is faster than pixGetPixelStats(), which
+ *          calls pixGetAverageMaskedRGB() and has the overhead of
+ *          generating a temporary pix of each of the three components;
+ *          this can take most of the time if %factor > 1.
+ *      (2) If %pixm is null, this gives the average value of all
+ *          pixels in %pixs.  The returned value is an integer.
+ *      (3) For color %pixs, the returned pixel value is in the standard
+ *          uint32 RGBA packing.
+ *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
+ *      (5) Input x,y are ignored if %pixm does not exist.
+ * 
+ */ +l_ok +pixGetPixelAverage(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + l_uint32 *pval) +{ +l_int32 i, j, w, h, d, wm, hm, wpl1, wplm, val, rval, gval, bval, count; +l_uint32 *data1, *datam, *line1, *linem; +l_float64 sum, rsum, gsum, bsum; +PIX *pix1; + + PROCNAME("pixGetPixelAverage"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + if (d != 32 && !pixGetColormap(pixs)) + return ERROR_INT("pixs not rgb or colormapped", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + + if (pixGetColormap(pixs)) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix1 = pixClone(pixs); + pixGetDimensions(pix1, &w, &h, &d); + if (d == 1) { + pixDestroy(&pix1); + return ERROR_INT("pix1 is just 1 bpp", procName, 1); + } + data1 = pixGetData(pix1); + wpl1 = pixGetWpl(pix1); + + sum = rsum = gsum = bsum = 0.0; + count = 0; + if (!pixm) { + for (i = 0; i < h; i += factor) { + line1 = data1 + i * wpl1; + for (j = 0; j < w; j += factor) { + if (d == 8) { + val = GET_DATA_BYTE(line1, j); + sum += val; + } else { /* rgb */ + extractRGBValues(*(line1 + j), &rval, &gval, &bval); + rsum += rval; + gsum += gval; + bsum += bval; + } + count++; + } + } + } else { /* masked */ + pixGetDimensions(pixm, &wm, &hm, NULL); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + line1 = data1 + (y + i) * wpl1; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + if (d == 8) { + val = GET_DATA_BYTE(line1, x + j); + sum += val; + } else { /* rgb */ + extractRGBValues(*(line1 + x + j), &rval, &gval, &bval); + rsum += rval; + gsum += gval; + bsum += bval; + } + count++; + } + } + } + } + + pixDestroy(&pix1); + if (count == 0) + return ERROR_INT("no pixels sampled", procName, 1); + if (d == 8) { + *pval = (l_uint32)((l_float64)sum / (l_float64)count); + } else { /* d == 32 */ + rval = (l_uint32)((l_float64)rsum / (l_float64)count); + gval = (l_uint32)((l_float64)gsum / (l_float64)count); + bval = (l_uint32)((l_float64)bsum / (l_float64)count); + composeRGBPixel(rval, gval, bval, pval); + } + + return 0; +} + + +/*! + * \brief pixGetPixelStats() + * + * \param[in] pixs 8 bpp, 32 bpp or colormapped + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, + * L_STANDARD_DEVIATION, L_VARIANCE + * \param[out] pvalue pixel value corresponding to input type + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simple function to get one of four statistical values of an image.
+ *      (2) It does not take a mask: it uses the entire image.
+ *      (3) To get the average pixel value of an RGB image, suggest using
+ *          pixGetPixelAverage(), which is considerably faster.
+ * 
+ */ +l_ok +pixGetPixelStats(PIX *pixs, + l_int32 factor, + l_int32 type, + l_uint32 *pvalue) +{ +l_int32 d; +l_float32 val, rval, gval, bval; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGetPixelStats"); + + if (!pvalue) + return ERROR_INT("&value not defined", procName, 1); + *pvalue = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d != 8 && d != 32 && !cmap) + return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", procName, 1); + if (cmap) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pixt = pixClone(pixs); + d = pixGetDepth(pixt); + + if (d == 8) { + pixGetAverageMasked(pixt, NULL, 0, 0, factor, type, &val); + *pvalue = lept_roundftoi(val); + } else { + pixGetAverageMaskedRGB(pixt, NULL, 0, 0, factor, type, + &rval, &gval, &bval); + composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval), + lept_roundftoi(bval), pvalue); + } + + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixGetAverageMaskedRGB() + * + * \param[in] pixs 32 bpp, or colormapped + * \param[in] pixm [optional] 1 bpp mask over which average is + * to be taken; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0 + * \param[in] factor subsampling factor; >= 1 + * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, + * L_STANDARD_DEVIATION, L_VARIANCE + * \param[out] prval [optional] measured red value of given 'type' + * \param[out] pgval [optional] measured green value of given 'type' + * \param[out] pbval [optional] measured blue value of given 'type' + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For usage, see pixGetAverageMasked().
+ *      (2) If there is a colormap, it is removed before the 8 bpp
+ *          component images are extracted.
+ *      (3) A better name for this would be: pixGetPixelStatsRGB()
+ * 
+ */ +l_ok +pixGetAverageMaskedRGB(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + l_int32 type, + l_float32 *prval, + l_float32 *pgval, + l_float32 *pbval) +{ +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGetAverageMaskedRGB"); + + if (prval) *prval = 0.0; + if (pgval) *pgval = 0.0; + if (pbval) *pbval = 0.0; + if (!prval && !pgval && !pbval) + return ERROR_INT("no values requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + cmap = pixGetColormap(pixs); + if (pixGetDepth(pixs) != 32 && !cmap) + return ERROR_INT("pixs neither 32 bpp nor colormapped", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && + type != L_STANDARD_DEVIATION && type != L_VARIANCE) + return ERROR_INT("invalid measure type", procName, 1); + + if (prval) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_RED); + else + pixt = pixGetRGBComponent(pixs, COLOR_RED); + pixGetAverageMasked(pixt, pixm, x, y, factor, type, prval); + pixDestroy(&pixt); + } + if (pgval) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN); + else + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + pixGetAverageMasked(pixt, pixm, x, y, factor, type, pgval); + pixDestroy(&pixt); + } + if (pbval) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE); + else + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + pixGetAverageMasked(pixt, pixm, x, y, factor, type, pbval); + pixDestroy(&pixt); + } + + return 0; +} + + +/*! + * \brief pixGetAverageMasked() + * + * \param[in] pixs 8 or 16 bpp, or colormapped + * \param[in] pixm [optional] 1 bpp mask over which average is + * to be taken; use all pixels if null + * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; + * can be < 0 + * \param[in] factor subsampling factor; >= 1 + * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, + * L_STANDARD_DEVIATION, L_VARIANCE + * \param[out] pval measured value of given 'type' + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Use L_MEAN_ABSVAL to get the average value of pixels in pixs
+ *          that are under the fg of the optional mask.  If the mask
+ *          is null, it finds the average of the pixels in pixs.
+ *      (2) Likewise, use L_ROOT_MEAN_SQUARE to get the rms value of
+ *          pixels in pixs, either masked or not; L_STANDARD_DEVIATION
+ *          to get the standard deviation from the mean of the pixels;
+ *          L_VARIANCE to get the average squared difference from the
+ *          expected value.  The variance is the square of the stdev.
+ *          For the standard deviation, we use
+ *              sqrt([([x] - x)]^2) = sqrt([x^2] - [x]^2)
+ *      (3) Set the subsampling %factor > 1 to reduce the amount of
+ *          computation.
+ *      (4) Clipping of pixm (if it exists) to pixs is done in the inner loop.
+ *      (5) Input x,y are ignored unless pixm exists.
+ *      (6) A better name for this would be: pixGetPixelStatsGray()
+ * 
+ */ +l_ok +pixGetAverageMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_int32 factor, + l_int32 type, + l_float32 *pval) +{ +l_int32 i, j, w, h, d, wm, hm, wplg, wplm, val, count; +l_uint32 *datag, *datam, *lineg, *linem; +l_float64 sumave, summs, ave, meansq, var; +PIX *pixg; + + PROCNAME("pixGetAverageMasked"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + if (d != 8 && d != 16 && !pixGetColormap(pixs)) + return ERROR_INT("pixs not 8 or 16 bpp or colormapped", procName, 1); + if (pixm && pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not 1 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && + type != L_STANDARD_DEVIATION && type != L_VARIANCE) + return ERROR_INT("invalid measure type", procName, 1); + + if (pixGetColormap(pixs)) + pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixg = pixClone(pixs); + pixGetDimensions(pixg, &w, &h, &d); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + + sumave = summs = 0.0; + count = 0; + if (!pixm) { + for (i = 0; i < h; i += factor) { + lineg = datag + i * wplg; + for (j = 0; j < w; j += factor) { + if (d == 8) + val = GET_DATA_BYTE(lineg, j); + else /* d == 16 */ + val = GET_DATA_TWO_BYTES(lineg, j); + if (type != L_ROOT_MEAN_SQUARE) + sumave += val; + if (type != L_MEAN_ABSVAL) + summs += (l_float64)(val) * val; + count++; + } + } + } else { + pixGetDimensions(pixm, &wm, &hm, NULL); + datam = pixGetData(pixm); + wplm = pixGetWpl(pixm); + for (i = 0; i < hm; i += factor) { + if (y + i < 0 || y + i >= h) continue; + lineg = datag + (y + i) * wplg; + linem = datam + i * wplm; + for (j = 0; j < wm; j += factor) { + if (x + j < 0 || x + j >= w) continue; + if (GET_DATA_BIT(linem, j)) { + if (d == 8) + val = GET_DATA_BYTE(lineg, x + j); + else /* d == 16 */ + val = GET_DATA_TWO_BYTES(lineg, x + j); + if (type != L_ROOT_MEAN_SQUARE) + sumave += val; + if (type != L_MEAN_ABSVAL) + summs += (l_float64)(val) * val; + count++; + } + } + } + } + + pixDestroy(&pixg); + if (count == 0) + return ERROR_INT("no pixels sampled", procName, 1); + ave = sumave / (l_float64)count; + meansq = summs / (l_float64)count; + var = meansq - ave * ave; + if (type == L_MEAN_ABSVAL) + *pval = (l_float32)ave; + else if (type == L_ROOT_MEAN_SQUARE) + *pval = (l_float32)sqrt(meansq); + else if (type == L_STANDARD_DEVIATION) + *pval = (l_float32)sqrt(var); + else /* type == L_VARIANCE */ + *pval = (l_float32)var; + + return 0; +} + + +/*! + * \brief pixGetAverageTiledRGB() + * + * \param[in] pixs 32 bpp, or colormapped + * \param[in] sx, sy tile size; must be at least 2 x 2 + * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION + * \param[out] ppixr [optional] tiled 'average' of red component + * \param[out] ppixg [optional] tiled 'average' of green component + * \param[out] ppixb [optional] tiled 'average' of blue component + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For usage, see pixGetAverageTiled().
+ *      (2) If there is a colormap, it is removed before the 8 bpp
+ *          component images are extracted.
+ * 
+ */ +l_ok +pixGetAverageTiledRGB(PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 type, + PIX **ppixr, + PIX **ppixg, + PIX **ppixb) +{ +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixGetAverageTiledRGB"); + + if (ppixr) *ppixr = NULL; + if (ppixg) *ppixg = NULL; + if (ppixb) *ppixb = NULL; + if (!ppixr && !ppixg && !ppixb) + return ERROR_INT("no data requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + cmap = pixGetColormap(pixs); + if (pixGetDepth(pixs) != 32 && !cmap) + return ERROR_INT("pixs neither 32 bpp nor colormapped", procName, 1); + if (sx < 2 || sy < 2) + return ERROR_INT("sx and sy not both > 1", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && + type != L_STANDARD_DEVIATION) + return ERROR_INT("invalid measure type", procName, 1); + + if (ppixr) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_RED); + else + pixt = pixGetRGBComponent(pixs, COLOR_RED); + *ppixr = pixGetAverageTiled(pixt, sx, sy, type); + pixDestroy(&pixt); + } + if (ppixg) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN); + else + pixt = pixGetRGBComponent(pixs, COLOR_GREEN); + *ppixg = pixGetAverageTiled(pixt, sx, sy, type); + pixDestroy(&pixt); + } + if (ppixb) { + if (cmap) + pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE); + else + pixt = pixGetRGBComponent(pixs, COLOR_BLUE); + *ppixb = pixGetAverageTiled(pixt, sx, sy, type); + pixDestroy(&pixt); + } + + return 0; +} + + +/*! + * \brief pixGetAverageTiled() + * + * \param[in] pixs 8 bpp, or colormapped + * \param[in] sx, sy tile size; must be at least 2 x 2 + * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION + * \return pixd average values in each tile, or NULL on error + * + *
+ * Notes:
+ *      (1) Only computes for tiles that are entirely contained in pixs.
+ *      (2) Use L_MEAN_ABSVAL to get the average abs value within the tile;
+ *          L_ROOT_MEAN_SQUARE to get the rms value within each tile;
+ *          L_STANDARD_DEVIATION to get the standard dev. from the average
+ *          within each tile.
+ *      (3) If colormapped, converts to 8 bpp gray.
+ * 
+ */ +PIX * +pixGetAverageTiled(PIX *pixs, + l_int32 sx, + l_int32 sy, + l_int32 type) +{ +l_int32 i, j, k, m, w, h, wd, hd, d, pos, wplt, wpld, valt; +l_uint32 *datat, *datad, *linet, *lined, *startt; +l_float64 sumave, summs, ave, meansq, normfact; +PIX *pixt, *pixd; + + PROCNAME("pixGetAverageTiled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL); + if (sx < 2 || sy < 2) + return (PIX *)ERROR_PTR("sx and sy not both > 1", procName, NULL); + wd = w / sx; + hd = h / sy; + if (wd < 1 || hd < 1) + return (PIX *)ERROR_PTR("wd or hd == 0", procName, NULL); + if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && + type != L_STANDARD_DEVIATION) + return (PIX *)ERROR_PTR("invalid measure type", procName, NULL); + + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + pixd = pixCreate(wd, hd, 8); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + normfact = 1. / (l_float64)(sx * sy); + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + linet = datat + i * sy * wplt; + for (j = 0; j < wd; j++) { + if (type == L_MEAN_ABSVAL || type == L_STANDARD_DEVIATION) { + sumave = 0.0; + for (k = 0; k < sy; k++) { + startt = linet + k * wplt; + for (m = 0; m < sx; m++) { + pos = j * sx + m; + valt = GET_DATA_BYTE(startt, pos); + sumave += valt; + } + } + ave = normfact * sumave; + } + if (type == L_ROOT_MEAN_SQUARE || type == L_STANDARD_DEVIATION) { + summs = 0.0; + for (k = 0; k < sy; k++) { + startt = linet + k * wplt; + for (m = 0; m < sx; m++) { + pos = j * sx + m; + valt = GET_DATA_BYTE(startt, pos); + summs += (l_float64)(valt) * valt; + } + } + meansq = normfact * summs; + } + if (type == L_MEAN_ABSVAL) + valt = (l_int32)(ave + 0.5); + else if (type == L_ROOT_MEAN_SQUARE) + valt = (l_int32)(sqrt(meansq) + 0.5); + else /* type == L_STANDARD_DEVIATION */ + valt = (l_int32)(sqrt(meansq - ave * ave) + 0.5); + SET_DATA_BYTE(lined, j, valt); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixRowStats() + * + * \param[in] pixs 8 bpp; not cmapped + * \param[in] box [optional] clipping box; can be null + * \param[out] pnamean [optional] numa of mean values + * \param[out] pnamedian [optional] numa of median values + * \param[out] pnamode [optional] numa of mode intensity values + * \param[out] pnamodecount [optional] numa of mode counts + * \param[out] pnavar [optional] numa of variance + * \param[out] pnarootvar [optional] numa of square root of variance + * \return na numa of requested statistic for each row, or NULL on error + * + *
+ * Notes:
+ *      (1) This computes numas that represent column vectors of statistics,
+ *          with each of its values derived from the corresponding row of a Pix.
+ *      (2) Use NULL on input to prevent computation of any of the 5 numas.
+ *      (3) Other functions that compute pixel row statistics are:
+ *             pixCountPixelsByRow()
+ *             pixAverageByRow()
+ *             pixVarianceByRow()
+ *             pixGetRowStats()
+ * 
+ */ +l_int32 +pixRowStats(PIX *pixs, + BOX *box, + NUMA **pnamean, + NUMA **pnamedian, + NUMA **pnamode, + NUMA **pnamodecount, + NUMA **pnavar, + NUMA **pnarootvar) +{ +l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval; +l_int32 xstart, xend, ystart, yend, bw, bh; +l_int32 *histo; +l_uint32 *lines, *datas; +l_float32 norm; +l_float32 *famean, *fameansq, *favar, *farootvar; +l_float32 *famedian, *famode, *famodecount; + + PROCNAME("pixRowStats"); + + if (pnamean) *pnamean = NULL; + if (pnamedian) *pnamedian = NULL; + if (pnamode) *pnamode = NULL; + if (pnamodecount) *pnamodecount = NULL; + if (pnavar) *pnavar = NULL; + if (pnarootvar) *pnarootvar = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + famean = fameansq = favar = farootvar = NULL; + famedian = famode = famodecount = NULL; + + pixGetDimensions(pixs, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return ERROR_INT("invalid clipping box", procName, 1); + + /* We need the mean for variance and root variance */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (pnamean || pnavar || pnarootvar) { + norm = 1. / (l_float32)bw; + famean = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); + fameansq = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); + if (pnavar || pnarootvar) { + favar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); + if (pnarootvar) + farootvar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); + } + for (i = ystart; i < yend; i++) { + sum = sumsq = 0; + lines = datas + i * wpls; + for (j = xstart; j < xend; j++) { + val = GET_DATA_BYTE(lines, j); + sum += val; + sumsq += val * val; + } + famean[i] = norm * sum; + fameansq[i] = norm * sumsq; + if (pnavar || pnarootvar) { + favar[i] = fameansq[i] - famean[i] * famean[i]; + if (pnarootvar) + farootvar[i] = sqrtf(favar[i]); + } + } + LEPT_FREE(fameansq); + if (pnamean) + *pnamean = numaCreateFromFArray(famean, bh, L_INSERT); + else + LEPT_FREE(famean); + if (pnavar) + *pnavar = numaCreateFromFArray(favar, bh, L_INSERT); + else + LEPT_FREE(favar); + if (pnarootvar) + *pnarootvar = numaCreateFromFArray(farootvar, bh, L_INSERT); + } + + /* We need a histogram to find the median and/or mode values */ + if (pnamedian || pnamode || pnamodecount) { + histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (pnamedian) { + *pnamedian = numaMakeConstant(0, bh); + famedian = numaGetFArray(*pnamedian, L_NOCOPY); + } + if (pnamode) { + *pnamode = numaMakeConstant(0, bh); + famode = numaGetFArray(*pnamode, L_NOCOPY); + } + if (pnamodecount) { + *pnamodecount = numaMakeConstant(0, bh); + famodecount = numaGetFArray(*pnamodecount, L_NOCOPY); + } + for (i = ystart; i < yend; i++) { + lines = datas + i * wpls; + memset(histo, 0, 1024); + for (j = xstart; j < xend; j++) { + val = GET_DATA_BYTE(lines, j); + histo[val]++; + } + + if (pnamedian) { + sum = 0; + target = (bw + 1) / 2; + for (k = 0; k < 256; k++) { + sum += histo[k]; + if (sum >= target) { + famedian[i] = k; + break; + } + } + } + + if (pnamode || pnamodecount) { + max = 0; + modeval = 0; + for (k = 0; k < 256; k++) { + if (histo[k] > max) { + max = histo[k]; + modeval = k; + } + } + if (pnamode) + famode[i] = modeval; + if (pnamodecount) + famodecount[i] = max; + } + } + LEPT_FREE(histo); + } + + return 0; +} + + +/*! + * \brief pixColumnStats() + * + * \param[in] pixs 8 bpp; not cmapped + * \param[in] box [optional] clipping box; can be null + * \param[out] pnamean [optional] numa of mean values + * \param[out] pnamedian [optional] numa of median values + * \param[out] pnamode [optional] numa of mode intensity values + * \param[out] pnamodecount [optional] numa of mode counts + * \param[out] pnavar [optional] numa of variance + * \param[out] pnarootvar [optional] numa of square root of variance + * \return na numa of requested statistic for each column, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This computes numas that represent row vectors of statistics,
+ *          with each of its values derived from the corresponding col of a Pix.
+ *      (2) Use NULL on input to prevent computation of any of the 5 numas.
+ *      (3) Other functions that compute pixel column statistics are:
+ *             pixCountPixelsByColumn()
+ *             pixAverageByColumn()
+ *             pixVarianceByColumn()
+ *             pixGetColumnStats()
+ * 
+ */ +l_int32 +pixColumnStats(PIX *pixs, + BOX *box, + NUMA **pnamean, + NUMA **pnamedian, + NUMA **pnamode, + NUMA **pnamodecount, + NUMA **pnavar, + NUMA **pnarootvar) +{ +l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval; +l_int32 xstart, xend, ystart, yend, bw, bh; +l_int32 *histo; +l_uint32 *lines, *datas; +l_float32 norm; +l_float32 *famean, *fameansq, *favar, *farootvar; +l_float32 *famedian, *famode, *famodecount; + + PROCNAME("pixColumnStats"); + + if (pnamean) *pnamean = NULL; + if (pnamedian) *pnamedian = NULL; + if (pnamode) *pnamode = NULL; + if (pnamodecount) *pnamodecount = NULL; + if (pnavar) *pnavar = NULL; + if (pnarootvar) *pnarootvar = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + famean = fameansq = favar = farootvar = NULL; + famedian = famode = famodecount = NULL; + + pixGetDimensions(pixs, &w, &h, NULL); + if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, + &bw, &bh) == 1) + return ERROR_INT("invalid clipping box", procName, 1); + + /* We need the mean for variance and root variance */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (pnamean || pnavar || pnarootvar) { + norm = 1. / (l_float32)bh; + famean = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); + fameansq = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); + if (pnavar || pnarootvar) { + favar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); + if (pnarootvar) + farootvar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); + } + for (j = xstart; j < xend; j++) { + sum = sumsq = 0; + for (i = ystart, lines = datas; i < yend; lines += wpls, i++) { + val = GET_DATA_BYTE(lines, j); + sum += val; + sumsq += val * val; + } + famean[j] = norm * sum; + fameansq[j] = norm * sumsq; + if (pnavar || pnarootvar) { + favar[j] = fameansq[j] - famean[j] * famean[j]; + if (pnarootvar) + farootvar[j] = sqrtf(favar[j]); + } + } + LEPT_FREE(fameansq); + if (pnamean) + *pnamean = numaCreateFromFArray(famean, bw, L_INSERT); + else + LEPT_FREE(famean); + if (pnavar) + *pnavar = numaCreateFromFArray(favar, bw, L_INSERT); + else + LEPT_FREE(favar); + if (pnarootvar) + *pnarootvar = numaCreateFromFArray(farootvar, bw, L_INSERT); + } + + /* We need a histogram to find the median and/or mode values */ + if (pnamedian || pnamode || pnamodecount) { + histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + if (pnamedian) { + *pnamedian = numaMakeConstant(0, bw); + famedian = numaGetFArray(*pnamedian, L_NOCOPY); + } + if (pnamode) { + *pnamode = numaMakeConstant(0, bw); + famode = numaGetFArray(*pnamode, L_NOCOPY); + } + if (pnamodecount) { + *pnamodecount = numaMakeConstant(0, bw); + famodecount = numaGetFArray(*pnamodecount, L_NOCOPY); + } + for (j = xstart; j < xend; j++) { + memset(histo, 0, 1024); + for (i = ystart, lines = datas; i < yend; lines += wpls, i++) { + val = GET_DATA_BYTE(lines, j); + histo[val]++; + } + + if (pnamedian) { + sum = 0; + target = (bh + 1) / 2; + for (k = 0; k < 256; k++) { + sum += histo[k]; + if (sum >= target) { + famedian[j] = k; + break; + } + } + } + + if (pnamode || pnamodecount) { + max = 0; + modeval = 0; + for (k = 0; k < 256; k++) { + if (histo[k] > max) { + max = histo[k]; + modeval = k; + } + } + if (pnamode) + famode[j] = modeval; + if (pnamodecount) + famodecount[j] = max; + } + } + LEPT_FREE(histo); + } + + return 0; +} + + +/*! + * \brief pixGetRangeValues() + * + * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; >= 1; ignored if colormapped + * \param[in] color L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE + * \param[out] pminval [optional] minimum value of component + * \param[out] pmaxval [optional] maximum value of component + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If pixs is 8 bpp grayscale, the color selection type is ignored.
+ * 
+ */ +l_ok +pixGetRangeValues(PIX *pixs, + l_int32 factor, + l_int32 color, + l_int32 *pminval, + l_int32 *pmaxval) +{ +l_int32 d; +PIXCMAP *cmap; + + PROCNAME("pixGetRangeValues"); + + if (pminval) *pminval = 0; + if (pmaxval) *pmaxval = 0; + if (!pminval && !pmaxval) + return ERROR_INT("no result requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + cmap = pixGetColormap(pixs); + if (cmap) + return pixcmapGetRangeValues(cmap, color, pminval, pmaxval, + NULL, NULL); + + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); + + if (d == 8) { + pixGetExtremeValue(pixs, factor, L_SELECT_MIN, + NULL, NULL, NULL, pminval); + pixGetExtremeValue(pixs, factor, L_SELECT_MAX, + NULL, NULL, NULL, pmaxval); + } else if (color == L_SELECT_RED) { + pixGetExtremeValue(pixs, factor, L_SELECT_MIN, + pminval, NULL, NULL, NULL); + pixGetExtremeValue(pixs, factor, L_SELECT_MAX, + pmaxval, NULL, NULL, NULL); + } else if (color == L_SELECT_GREEN) { + pixGetExtremeValue(pixs, factor, L_SELECT_MIN, + NULL, pminval, NULL, NULL); + pixGetExtremeValue(pixs, factor, L_SELECT_MAX, + NULL, pmaxval, NULL, NULL); + } else if (color == L_SELECT_BLUE) { + pixGetExtremeValue(pixs, factor, L_SELECT_MIN, + NULL, NULL, pminval, NULL); + pixGetExtremeValue(pixs, factor, L_SELECT_MAX, + NULL, NULL, pmaxval, NULL); + } else { + return ERROR_INT("invalid color", procName, 1); + } + + return 0; +} + + +/*! + * \brief pixGetExtremeValue() + * + * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped + * \param[in] factor subsampling factor; >= 1; ignored if colormapped + * \param[in] type L_SELECT_MIN or L_SELECT_MAX + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \param[out] pgrayval [optional] min or max gray value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If pixs is grayscale, the result is returned in &grayval.
+ *          Otherwise, if there is a colormap or d == 32,
+ *          each requested color component is returned.  At least
+ *          one color component (address) must be input.
+ * 
+ */ +l_ok +pixGetExtremeValue(PIX *pixs, + l_int32 factor, + l_int32 type, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval, + l_int32 *pgrayval) +{ +l_int32 i, j, w, h, d, wpl; +l_int32 val, extval, rval, gval, bval, extrval, extgval, extbval; +l_uint32 pixel; +l_uint32 *data, *line; +PIXCMAP *cmap; + + PROCNAME("pixGetExtremeValue"); + + if (prval) *prval = -1; + if (pgval) *pgval = -1; + if (pbval) *pbval = -1; + if (pgrayval) *pgrayval = -1; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (type != L_SELECT_MIN && type != L_SELECT_MAX) + return ERROR_INT("invalid type", procName, 1); + + cmap = pixGetColormap(pixs); + if (cmap) { + if (type == L_SELECT_MIN) { + if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, prval, NULL, + NULL, NULL); + if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, pgval, NULL, + NULL, NULL); + if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, pbval, NULL, + NULL, NULL); + } else { /* type == L_SELECT_MAX */ + if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, NULL, prval, + NULL, NULL); + if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, NULL, pgval, + NULL, NULL); + if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, NULL, pbval, + NULL, NULL); + } + return 0; + } + + pixGetDimensions(pixs, &w, &h, &d); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (d != 8 && d != 32) + return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); + if (d == 8 && !pgrayval) + return ERROR_INT("can't return result in grayval", procName, 1); + if (d == 32 && !prval && !pgval && !pbval) + return ERROR_INT("can't return result in r/g/b-val", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (d == 8) { + if (type == L_SELECT_MIN) + extval = 100000; + else /* get max */ + extval = -1; + + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + val = GET_DATA_BYTE(line, j); + if ((type == L_SELECT_MIN && val < extval) || + (type == L_SELECT_MAX && val > extval)) + extval = val; + } + } + *pgrayval = extval; + return 0; + } + + /* 32 bpp rgb */ + if (type == L_SELECT_MIN) { + extrval = 100000; + extgval = 100000; + extbval = 100000; + } else { + extrval = -1; + extgval = -1; + extbval = -1; + } + for (i = 0; i < h; i += factor) { + line = data + i * wpl; + for (j = 0; j < w; j += factor) { + pixel = line[j]; + if (prval) { + rval = (pixel >> L_RED_SHIFT) & 0xff; + if ((type == L_SELECT_MIN && rval < extrval) || + (type == L_SELECT_MAX && rval > extrval)) + extrval = rval; + } + if (pgval) { + gval = (pixel >> L_GREEN_SHIFT) & 0xff; + if ((type == L_SELECT_MIN && gval < extgval) || + (type == L_SELECT_MAX && gval > extgval)) + extgval = gval; + } + if (pbval) { + bval = (pixel >> L_BLUE_SHIFT) & 0xff; + if ((type == L_SELECT_MIN && bval < extbval) || + (type == L_SELECT_MAX && bval > extbval)) + extbval = bval; + } + } + } + if (prval) *prval = extrval; + if (pgval) *pgval = extgval; + if (pbval) *pbval = extbval; + return 0; +} + + +/*! + * \brief pixGetMaxValueInRect() + * + * \param[in] pixs 8, 16 or 32 bpp grayscale; no color space components + * \param[in] box [optional] region; set box = NULL to use entire pixs + * \param[out] pmaxval [optional] max value in region + * \param[out] pxmax [optional] x location of max value + * \param[out] pymax [optional] y location of max value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This can be used to find the maximum and its location
+ *          in a 2-dimensional histogram, where the x and y directions
+ *          represent two color components (e.g., saturation and hue).
+ *      (2) Note that here a 32 bpp pixs has pixel values that are simply
+ *          numbers.  They are not 8 bpp components in a colorspace.
+ * 
+ */ +l_ok +pixGetMaxValueInRect(PIX *pixs, + BOX *box, + l_uint32 *pmaxval, + l_int32 *pxmax, + l_int32 *pymax) +{ +l_int32 i, j, w, h, d, wpl, bw, bh; +l_int32 xstart, ystart, xend, yend, xmax, ymax; +l_uint32 val, maxval; +l_uint32 *data, *line; + + PROCNAME("pixGetMaxValueInRect"); + + if (pmaxval) *pmaxval = 0; + if (pxmax) *pxmax = 0; + if (pymax) *pymax = 0; + if (!pmaxval && !pxmax && !pymax) + return ERROR_INT("no data requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetColormap(pixs) != NULL) + return ERROR_INT("pixs has colormap", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); + + xstart = ystart = 0; + xend = w - 1; + yend = h - 1; + if (box) { + boxGetGeometry(box, &xstart, &ystart, &bw, &bh); + xend = xstart + bw - 1; + yend = ystart + bh - 1; + } + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + maxval = 0; + xmax = ymax = 0; + for (i = ystart; i <= yend; i++) { + line = data + i * wpl; + for (j = xstart; j <= xend; j++) { + if (d == 8) + val = GET_DATA_BYTE(line, j); + else if (d == 16) + val = GET_DATA_TWO_BYTES(line, j); + else /* d == 32 */ + val = line[j]; + if (val > maxval) { + maxval = val; + xmax = j; + ymax = i; + } + } + } + if (maxval == 0) { /* no counts; pick the center of the rectangle */ + xmax = (xstart + xend) / 2; + ymax = (ystart + yend) / 2; + } + + if (pmaxval) *pmaxval = maxval; + if (pxmax) *pxmax = xmax; + if (pymax) *pymax = ymax; + return 0; +} + + +/*! + * \brief pixGetBinnedComponentRange() + * + * \param[in] pixs 32 bpp rgb + * \param[in] nbins number of equal population bins; must be > 1 + * \param[in] factor subsampling factor; >= 1 + * \param[in] color L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE + * \param[out] pminval [optional] minimum value of component + * \param[out] pmaxval [optional] maximum value of component + * \param[out] pcarray [optional] color array of bins + * \param[in] fontsize [optional] 0 for no debug; for debug, valid set + * is {4,6,8,10,12,14,16,18,20}. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns the min and max average values of the
+ *          selected color component in the set of rank bins,
+ *          where the ranking is done using the specified component.
+ * 
+ */ +l_ok +pixGetBinnedComponentRange(PIX *pixs, + l_int32 nbins, + l_int32 factor, + l_int32 color, + l_int32 *pminval, + l_int32 *pmaxval, + l_uint32 **pcarray, + l_int32 fontsize) +{ +l_int32 i, minval, maxval, rval, gval, bval; +l_uint32 *carray; +PIX *pixt; + + PROCNAME("pixGetBinnedComponentRange"); + + if (pminval) *pminval = 0; + if (pmaxval) *pmaxval = 0; + if (pcarray) *pcarray = NULL; + if (!pminval && !pmaxval) + return ERROR_INT("no result requested", procName, 1); + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (color != L_SELECT_RED && color != L_SELECT_GREEN && + color != L_SELECT_BLUE) + return ERROR_INT("invalid color", procName, 1); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return ERROR_INT("invalid fontsize", procName, 1); + + pixGetRankColorArray(pixs, nbins, color, factor, &carray, 0, 0); + if (fontsize > 0) { + for (i = 0; i < nbins; i++) + L_INFO("c[%d] = %x\n", procName, i, carray[i]); + pixt = pixDisplayColorArray(carray, nbins, 200, 5, fontsize); + pixDisplay(pixt, 100, 100); + pixDestroy(&pixt); + } + + extractRGBValues(carray[0], &rval, &gval, &bval); + minval = rval; + if (color == L_SELECT_GREEN) + minval = gval; + else if (color == L_SELECT_BLUE) + minval = bval; + extractRGBValues(carray[nbins - 1], &rval, &gval, &bval); + maxval = rval; + if (color == L_SELECT_GREEN) + maxval = gval; + else if (color == L_SELECT_BLUE) + maxval = bval; + + if (pminval) *pminval = minval; + if (pmaxval) *pmaxval = maxval; + if (pcarray) + *pcarray = carray; + else + LEPT_FREE(carray); + return 0; +} + + +/*! + * \brief pixGetRankColorArray() + * + * \param[in] pixs 32 bpp or cmapped + * \param[in] nbins number of equal population bins; must be > 1 + * \param[in] type color selection flag + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] pcarray array of colors, ranked by intensity + * \param[in] debugflag 1 to display color squares and plots of color + * components; 2 to write them as png to file + * \param[in] fontsize [optional] 0 for no debug; for debug, valid set + * is {4,6,8,10,12,14,16,18,20}. Ignored if + * debugflag == 0. fontsize == 6 is typical. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN,
+ *          L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE,
+ *          L_SELECT_HUE, L_SELECT_SATURATION.
+ *      (2) Then it finds the histogram of the selected color type in each
+ *          RGB pixel.  For each of the %nbins sets of pixels,
+ *          ordered by this color type value, find the average RGB color,
+ *          and return this as a "rank color" array.  The output array
+ *          has %nbins colors.
+ *      (3) Set the subsampling factor > 1 to reduce the amount of
+ *          computation.  Typically you want at least 10,000 pixels
+ *          for reasonable statistics.
+ *      (4) The rank color as a function of rank can then be found from
+ *             rankint = (l_int32)(rank * (nbins - 1) + 0.5);
+ *             extractRGBValues(array[rankint], &rval, &gval, &bval);
+ *          where the rank is in [0.0 ... 1.0].
+ *          This function is meant to be simple and approximate.
+ *      (5) Compare this with pixGetBinnedColor(), which generates equal
+ *          width intensity bins and finds the average color in each bin.
+ * 
+ */ +l_ok +pixGetRankColorArray(PIX *pixs, + l_int32 nbins, + l_int32 type, + l_int32 factor, + l_uint32 **pcarray, + l_int32 debugflag, + l_int32 fontsize) +{ +l_int32 ret; +l_uint32 *array; +NUMA *na, *nan, *narbin; +PIX *pixt, *pixc, *pixg, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixGetRankColorArray"); + + if (!pcarray) + return ERROR_INT("&carray not defined", procName, 1); + *pcarray = NULL; + if (factor < 1) + return ERROR_INT("sampling factor must be >= 1", procName, 1); + if (nbins < 2) + return ERROR_INT("nbins must be at least 2", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + cmap = pixGetColormap(pixs); + if (pixGetDepth(pixs) != 32 && !cmap) + return ERROR_INT("pixs neither 32 bpp nor cmapped", procName, 1); + if (type != L_SELECT_RED && type != L_SELECT_GREEN && + type != L_SELECT_BLUE && type != L_SELECT_MIN && + type != L_SELECT_MAX && type != L_SELECT_AVERAGE && + type != L_SELECT_HUE && type != L_SELECT_SATURATION) + return ERROR_INT("invalid type", procName, 1); + if (debugflag > 0) { + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return ERROR_INT("invalid fontsize", procName, 1); + } + + /* Downscale by factor and remove colormap if it exists */ + pixt = pixScaleByIntSampling(pixs, factor); + if (cmap) + pixc = pixRemoveColormap(pixt, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc = pixClone(pixt); + pixDestroy(&pixt); + + /* Get normalized histogram of the selected component */ + if (type == L_SELECT_RED) + pixg = pixGetRGBComponent(pixc, COLOR_RED); + else if (type == L_SELECT_GREEN) + pixg = pixGetRGBComponent(pixc, COLOR_GREEN); + else if (type == L_SELECT_BLUE) + pixg = pixGetRGBComponent(pixc, COLOR_BLUE); + else if (type == L_SELECT_MIN) + pixg = pixConvertRGBToGrayMinMax(pixc, L_CHOOSE_MIN); + else if (type == L_SELECT_MAX) + pixg = pixConvertRGBToGrayMinMax(pixc, L_CHOOSE_MAX); + else if (type == L_SELECT_AVERAGE) + pixg = pixConvertRGBToGray(pixc, 0.34, 0.33, 0.33); + else if (type == L_SELECT_HUE) + pixg = pixConvertRGBToHue(pixc); + else /* L_SELECT_SATURATION */ + pixg = pixConvertRGBToSaturation(pixc); + if ((na = pixGetGrayHistogram(pixg, 1)) == NULL) { + pixDestroy(&pixc); + pixDestroy(&pixg); + return ERROR_INT("na not made", procName, 1); + } + nan = numaNormalizeHistogram(na, 1.0); + + /* Get the following arrays: + * (1) nar: cumulative normalized histogram (rank vs intensity value). + * With 256 intensity values, we have 257 rank values. + * (2) nai: "average" intensity as function of rank bin, for + * %nbins equally spaced in rank between 0.0 and 1.0. + * (3) narbin: bin number of discretized rank as a function of + * intensity. This is the 'inverse' of nai. + * (4) nabb: intensity value of the right bin boundary, for each + * of the %nbins discretized rank bins. */ + if (!debugflag) { + numaDiscretizeRankAndIntensity(nan, nbins, &narbin, NULL, NULL, NULL); + } else { + NUMA *nai, *nar, *nabb; + numaDiscretizeRankAndIntensity(nan, nbins, &narbin, &nai, &nar, &nabb); + lept_mkdir("lept/regout"); + gplotSimple1(nan, GPLOT_PNG, "/tmp/lept/regout/rtnan", + "Normalized Histogram"); + gplotSimple1(nar, GPLOT_PNG, "/tmp/lept/regout/rtnar", + "Cumulative Histogram"); + gplotSimple1(nai, GPLOT_PNG, "/tmp/lept/regout/rtnai", + "Intensity vs. rank bin"); + gplotSimple1(narbin, GPLOT_PNG, "/tmp/lept/regout/rtnarbin", + "LUT: rank bin vs. Intensity"); + gplotSimple1(nabb, GPLOT_PNG, "/tmp/lept/regout/rtnabb", + "Intensity of right edge vs. rank bin"); + numaDestroy(&nai); + numaDestroy(&nar); + numaDestroy(&nabb); + } + + /* Get the average color in each bin for pixels whose grayscale + * values fall in the bin range. %narbin is the LUT that + * determines the bin number from the grayscale version of + * the image. Because this mapping may not be unique, + * some bins may not be represented in the LUT. In use, to get fair + * allocation into all the bins, bin population is monitored + * as pixels are accumulated, and when bins fill up, + * pixels are required to overflow into succeeding bins. */ + pixGetBinnedColor(pixc, pixg, 1, nbins, narbin, pcarray, debugflag); + ret = 0; + if ((array = *pcarray) == NULL) { + L_ERROR("color array not returned\n", procName); + ret = 1; + debugflag = 0; /* make sure to skip the following */ + } + if (debugflag) { + pixd = pixDisplayColorArray(array, nbins, 200, 5, fontsize); + if (debugflag == 1) + pixDisplayWithTitle(pixd, 0, 500, "binned colors", 1); + else /* debugflag == 2 */ + pixWriteDebug("/tmp/lept/regout/rankhisto.png", pixd, IFF_PNG); + pixDestroy(&pixd); + } + + pixDestroy(&pixc); + pixDestroy(&pixg); + numaDestroy(&na); + numaDestroy(&nan); + numaDestroy(&narbin); + return ret; +} + + +/*! + * \brief pixGetBinnedColor() + * + * \param[in] pixs 32 bpp + * \param[in] pixg 8 bpp grayscale version of pixs + * \param[in] factor sampling factor along pixel counting direction + * \param[in] nbins number of intensity bins + * \param[in] nalut LUT for mapping from intensity to bin number + * \param[out] pcarray array of average color values in each bin + * \param[in] debugflag 1 to display output debug plots of color + * components; 2 to write them as png to file + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This takes a color image, a grayscale (intensity) version,
+ *          a LUT from intensity to bin number, and the number of bins.
+ *          It computes the average color for pixels whose intensity
+ *          is in each bin.  This is returned as an array of l_uint32
+ *          colors in our standard RGBA ordering.
+ *      (2) This function generates equal width intensity bins and
+ *          finds the average color in each bin.  Compare this with
+ *          pixGetRankColorArray(), which rank orders the pixels
+ *          by the value of the selected component in each pixel,
+ *          sets up bins with equal population (not intensity width!),
+ *          and gets the average color in each bin.
+ * 
+ */ +l_ok +pixGetBinnedColor(PIX *pixs, + PIX *pixg, + l_int32 factor, + l_int32 nbins, + NUMA *nalut, + l_uint32 **pcarray, + l_int32 debugflag) +{ +l_int32 i, j, w, h, wpls, wplg, grayval, bin, rval, gval, bval, success; +l_int32 npts, avepts, maxpts; +l_uint32 *datas, *datag, *lines, *lineg, *carray; +l_float64 norm; +l_float64 *rarray, *garray, *barray, *narray; + + PROCNAME("pixGetBinnedColor"); + + if (!pcarray) + return ERROR_INT("&carray not defined", procName, 1); + *pcarray = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixg) + return ERROR_INT("pixg not defined", procName, 1); + if (!nalut) + return ERROR_INT("nalut not defined", procName, 1); + if (factor < 1) { + L_WARNING("sampling factor less than 1; setting to 1\n", procName); + factor = 1; + } + + /* Find the color for each rank bin. Note that we can have + * multiple bins filled with pixels having the same gray value. + * Therefore, because in general the mapping from gray value + * to bin number is not unique, if a bin fills up (actually, + * we allow it to slightly overfill), we roll the excess + * over to the next bin, etc. */ + pixGetDimensions(pixs, &w, &h, NULL); + npts = (w + factor - 1) * (h + factor - 1) / (factor * factor); + avepts = (npts + nbins - 1) / nbins; /* average number of pts in a bin */ + maxpts = (l_int32)((1.0 + 0.5 / (l_float32)nbins) * avepts); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + rarray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); + garray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); + barray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); + narray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); + for (i = 0; i < h; i += factor) { + lines = datas + i * wpls; + lineg = datag + i * wplg; + for (j = 0; j < w; j += factor) { + grayval = GET_DATA_BYTE(lineg, j); + numaGetIValue(nalut, grayval, &bin); + extractRGBValues(lines[j], &rval, &gval, &bval); + while (narray[bin] >= maxpts && bin < nbins - 1) + bin++; + rarray[bin] += rval; + garray[bin] += gval; + barray[bin] += bval; + narray[bin] += 1.0; /* count samples in each bin */ + } + } + + for (i = 0; i < nbins; i++) { + norm = 1. / narray[i]; + rarray[i] *= norm; + garray[i] *= norm; + barray[i] *= norm; +/* fprintf(stderr, "narray[%d] = %f\n", i, narray[i]); */ + } + + if (debugflag) { + NUMA *nared, *nagreen, *nablue; + nared = numaCreate(nbins); + nagreen = numaCreate(nbins); + nablue = numaCreate(nbins); + for (i = 0; i < nbins; i++) { + numaAddNumber(nared, rarray[i]); + numaAddNumber(nagreen, garray[i]); + numaAddNumber(nablue, barray[i]); + } + lept_mkdir("lept/regout"); + gplotSimple1(nared, GPLOT_PNG, "/tmp/lept/regout/rtnared", + "Average red val vs. rank bin"); + gplotSimple1(nagreen, GPLOT_PNG, "/tmp/lept/regout/rtnagreen", + "Average green val vs. rank bin"); + gplotSimple1(nablue, GPLOT_PNG, "/tmp/lept/regout/rtnablue", + "Average blue val vs. rank bin"); + numaDestroy(&nared); + numaDestroy(&nagreen); + numaDestroy(&nablue); + } + + /* Save colors for all bins in a single array */ + success = TRUE; + if ((carray = (l_uint32 *)LEPT_CALLOC(nbins, sizeof(l_uint32))) == NULL) { + success = FALSE; + L_ERROR("carray not made\n", procName); + goto cleanup_arrays; + } + *pcarray = carray; + for (i = 0; i < nbins; i++) { + rval = (l_int32)(rarray[i] + 0.5); + gval = (l_int32)(garray[i] + 0.5); + bval = (l_int32)(barray[i] + 0.5); + composeRGBPixel(rval, gval, bval, carray + i); + } + +cleanup_arrays: + LEPT_FREE(rarray); + LEPT_FREE(garray); + LEPT_FREE(barray); + LEPT_FREE(narray); + return (success) ? 0 : 1; +} + + +/*! + * \brief pixDisplayColorArray() + * + * \param[in] carray array of colors: 0xrrggbb00 + * \param[in] ncolors size of array + * \param[in] side size of each color square; suggest 200 + * \param[in] ncols number of columns in output color matrix + * \param[in] fontsize to label each square with text. Valid set is + * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \return pixd color array, or NULL on error + */ +PIX * +pixDisplayColorArray(l_uint32 *carray, + l_int32 ncolors, + l_int32 side, + l_int32 ncols, + l_int32 fontsize) +{ +char textstr[256]; +l_int32 i, rval, gval, bval; +L_BMF *bmf; +PIX *pixt, *pixd; +PIXA *pixa; + + PROCNAME("pixDisplayColorArray"); + + if (!carray) + return (PIX *)ERROR_PTR("carray not defined", procName, NULL); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return (PIX *)ERROR_PTR("invalid fontsize", procName, NULL); + + bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize); + pixa = pixaCreate(ncolors); + for (i = 0; i < ncolors; i++) { + pixt = pixCreate(side, side, 32); + pixSetAllArbitrary(pixt, carray[i]); + if (bmf) { + extractRGBValues(carray[i], &rval, &gval, &bval); + snprintf(textstr, sizeof(textstr), + "%d: (%d %d %d)", i, rval, gval, bval); + pixSaveTiledWithText(pixt, pixa, side, (i % ncols == 0) ? 1 : 0, + 20, 2, bmf, textstr, 0xff000000, L_ADD_BELOW); + } else { + pixSaveTiled(pixt, pixa, 1.0, (i % ncols == 0) ? 1 : 0, 20, 32); + } + pixDestroy(&pixt); + } + pixd = pixaDisplay(pixa, 0, 0); + + pixaDestroy(&pixa); + bmfDestroy(&bmf); + return pixd; +} + + +/*! + * \brief pixRankBinByStrip() + * + * \param[in] pixs 32 bpp or cmapped + * \param[in] direction L_SCAN_HORIZONTAL or L_SCAN_VERTICAL + * \param[in] size of strips in scan direction + * \param[in] nbins number of equal population bins; must be > 1 + * \param[in] type color selection flag + * \return pixd result, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a pix where each column represents a strip of
+ *          the input image.  If %direction == L_SCAN_HORIZONTAL, the
+ *          input impage is tiled into vertical strips of width %size,
+ *          where %size is a compromise between getting better spatial
+ *          columnwise resolution (small %size) and getting better
+ *          columnwise statistical information (larger %size).  Likewise
+ *          with rows of the image if %direction == L_SCAN_VERTICAL.
+ *      (2) For L_HORIZONTAL_SCAN, the output pix contains rank binned
+ *          median colors in each column that correspond to a vertical
+ *          strip of width %size in the input image.
+ *      (3) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN,
+ *          L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE.
+ *          It determines how the rank ordering is done.
+ *      (4) Typical input values might be %size = 5, %nbins = 10.
+ * 
+ */ +PIX * +pixRankBinByStrip(PIX *pixs, + l_int32 direction, + l_int32 size, + l_int32 nbins, + l_int32 type) +{ +l_int32 i, j, w, h, nstrips; +l_uint32 *array; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; +PIXCMAP *cmap; + + PROCNAME("pixRankBinByStrip"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + cmap = pixGetColormap(pixs); + if (pixGetDepth(pixs) != 32 && !cmap) + return (PIX *)ERROR_PTR("pixs neither 32 bpp nor cmapped", + procName, NULL); + if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (size < 1) + return (PIX *)ERROR_PTR("size < 1", procName, NULL); + if (nbins < 2) + return (PIX *)ERROR_PTR("nbins must be at least 2", procName, NULL); + if (type != L_SELECT_RED && type != L_SELECT_GREEN && + type != L_SELECT_BLUE && type != L_SELECT_MIN && + type != L_SELECT_MAX && type != L_SELECT_AVERAGE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + /* Downscale by factor and remove colormap if it exists */ + if (cmap) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + else + pix1 = pixClone(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + + pixd = NULL; + boxa = makeMosaicStrips(w, h, direction, size); + pixa = pixClipRectangles(pix1, boxa); + nstrips = pixaGetCount(pixa); + if (direction == L_SCAN_HORIZONTAL) { + pixd = pixCreate(nstrips, nbins, 32); + for (i = 0; i < nstrips; i++) { + pix2 = pixaGetPix(pixa, i, L_CLONE); + pixGetRankColorArray(pix2, nbins, type, 1, &array, 0, 0); + for (j = 0; j < nbins; j++) + pixSetPixel(pixd, i, j, array[j]); + LEPT_FREE(array); + pixDestroy(&pix2); + } + } else { /* L_SCAN_VERTICAL */ + pixd = pixCreate(nbins, nstrips, 32); + for (i = 0; i < nstrips; i++) { + pix2 = pixaGetPix(pixa, i, L_CLONE); + pixGetRankColorArray(pix2, nbins, type, 1, &array, 0, 0); + for (j = 0; j < nbins; j++) + pixSetPixel(pixd, j, i, array[j]); + LEPT_FREE(array); + pixDestroy(&pix2); + } + } + pixDestroy(&pix1); + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return pixd; +} + + + +/*-------------------------------------------------------------* + * Pixelwise aligned statistics * + *-------------------------------------------------------------*/ +/*! + * \brief pixaGetAlignedStats() + * + * \param[in] pixa of identically sized, 8 bpp pix; not cmapped + * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT + * \param[in] nbins of histogram for median and mode; ignored for mean + * \param[in] thresh on histogram for mode val; ignored for all other types + * \return pix with pixelwise aligned stats, or NULL on error. + * + *
+ * Notes:
+ *      (1) Each pixel in the returned pix represents an average
+ *          (or median, or mode) over the corresponding pixels in each
+ *          pix in the pixa.
+ *      (2) The %thresh parameter works with L_MODE_VAL only, and
+ *          sets a minimum occupancy of the mode bin.
+ *          If the occupancy of the mode bin is less than %thresh, the
+ *          mode value is returned as 0.  To always return the actual
+ *          mode value, set %thresh = 0.  See pixGetRowStats().
+ * 
+ */ +PIX * +pixaGetAlignedStats(PIXA *pixa, + l_int32 type, + l_int32 nbins, + l_int32 thresh) +{ +l_int32 j, n, w, h, d; +l_float32 *colvect; +PIX *pixt, *pixd; + + PROCNAME("pixaGetAlignedStats"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && + type != L_MODE_VAL && type != L_MODE_COUNT) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + n = pixaGetCount(pixa); + if (n == 0) + return (PIX *)ERROR_PTR("no pix in pixa", procName, NULL); + pixaGetPixDimensions(pixa, 0, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pix not 8 bpp", procName, NULL); + + pixd = pixCreate(w, h, 8); + pixt = pixCreate(n, h, 8); + colvect = (l_float32 *)LEPT_CALLOC(h, sizeof(l_float32)); + for (j = 0; j < w; j++) { + pixaExtractColumnFromEachPix(pixa, j, pixt); + pixGetRowStats(pixt, type, nbins, thresh, colvect); + pixSetPixelColumn(pixd, j, colvect); + } + + LEPT_FREE(colvect); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixaExtractColumnFromEachPix() + * + * \param[in] pixa of identically sized, 8 bpp; not cmapped + * \param[in] col column index + * \param[in] pixd pix into which each column is inserted + * \return 0 if OK, 1 on error + */ +l_ok +pixaExtractColumnFromEachPix(PIXA *pixa, + l_int32 col, + PIX *pixd) +{ +l_int32 i, k, n, w, h, ht, val, wplt, wpld; +l_uint32 *datad, *datat; +PIX *pixt; + + PROCNAME("pixaExtractColumnFromEachPix"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!pixd || pixGetDepth(pixd) != 8) + return ERROR_INT("pixd not defined or not 8 bpp", procName, 1); + n = pixaGetCount(pixa); + pixGetDimensions(pixd, &w, &h, NULL); + if (n != w) + return ERROR_INT("pix width != n", procName, 1); + pixt = pixaGetPix(pixa, 0, L_CLONE); + wplt = pixGetWpl(pixt); + pixGetDimensions(pixt, NULL, &ht, NULL); + pixDestroy(&pixt); + if (h != ht) + return ERROR_INT("pixd height != column height", procName, 1); + + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (k = 0; k < n; k++) { + pixt = pixaGetPix(pixa, k, L_CLONE); + datat = pixGetData(pixt); + for (i = 0; i < h; i++) { + val = GET_DATA_BYTE(datat, col); + SET_DATA_BYTE(datad + i * wpld, k, val); + datat += wplt; + } + pixDestroy(&pixt); + } + + return 0; +} + + +/*! + * \brief pixGetRowStats() + * + * \param[in] pixs 8 bpp; not cmapped + * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT + * \param[in] nbins of histogram for median and mode; ignored for mean + * \param[in] thresh on histogram for mode; ignored for mean and median + * \param[in] colvect vector of results gathered across the rows of pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes a column vector of statistics using each
+ *          row of a Pix.  The result is put in %colvect.
+ *      (2) The %thresh parameter works with L_MODE_VAL only, and
+ *          sets a minimum occupancy of the mode bin.
+ *          If the occupancy of the mode bin is less than %thresh, the
+ *          mode value is returned as 0.  To always return the actual
+ *          mode value, set %thresh = 0.
+ *      (3) What is the meaning of this %thresh parameter?
+ *          For each row, the total count in the histogram is w, the
+ *          image width.  So %thresh, relative to w, gives a measure
+ *          of the ratio of the bin width to the width of the distribution.
+ *          The larger %thresh, the narrower the distribution must be
+ *          for the mode value to be returned (instead of returning 0).
+ *      (4) If the Pix consists of a set of corresponding columns,
+ *          one for each Pix in a Pixa, the width of the Pix is the
+ *          number of Pix in the Pixa and the column vector can
+ *          be stored as a column in a Pix of the same size as
+ *          each Pix in the Pixa.
+ * 
+ */ +l_ok +pixGetRowStats(PIX *pixs, + l_int32 type, + l_int32 nbins, + l_int32 thresh, + l_float32 *colvect) +{ +l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval; +l_int32 *histo, *gray2bin, *bin2gray; +l_uint32 *lines, *datas; + + PROCNAME("pixGetRowStats"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!colvect) + return ERROR_INT("colvect not defined", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && + type != L_MODE_VAL && type != L_MODE_COUNT) + return ERROR_INT("invalid type", procName, 1); + if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256)) + return ERROR_INT("invalid nbins", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (type == L_MEAN_ABSVAL) { + for (i = 0; i < h; i++) { + sum = 0; + lines = datas + i * wpls; + for (j = 0; j < w; j++) + sum += GET_DATA_BYTE(lines, j); + colvect[i] = (l_float32)sum / (l_float32)w; + } + return 0; + } + + /* We need a histogram; binwidth ~ 256 / nbins */ + histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); + gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); + for (i = 0; i < 256; i++) /* gray value --> histo bin */ + gray2bin[i] = (i * nbins) / 256; + for (i = 0; i < nbins; i++) /* histo bin --> gray value */ + bin2gray[i] = (i * 256 + 128) / nbins; + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (k = 0; k < nbins; k++) + histo[k] = 0; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + histo[gray2bin[val]]++; + } + + if (type == L_MEDIAN_VAL) { + sum = 0; + target = (w + 1) / 2; + for (k = 0; k < nbins; k++) { + sum += histo[k]; + if (sum >= target) { + colvect[i] = bin2gray[k]; + break; + } + } + } else if (type == L_MODE_VAL) { + max = 0; + modeval = 0; + for (k = 0; k < nbins; k++) { + if (histo[k] > max) { + max = histo[k]; + modeval = k; + } + } + if (max < thresh) + colvect[i] = 0; + else + colvect[i] = bin2gray[modeval]; + } else { /* type == L_MODE_COUNT */ + max = 0; + for (k = 0; k < nbins; k++) { + if (histo[k] > max) + max = histo[k]; + } + colvect[i] = max; + } + } + + LEPT_FREE(histo); + LEPT_FREE(gray2bin); + LEPT_FREE(bin2gray); + return 0; +} + + +/*! + * \brief pixGetColumnStats() + * + * \param[in] pixs 8 bpp; not cmapped + * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT + * \param[in] nbins of histogram for median and mode; ignored for mean + * \param[in] thresh on histogram for mode val; ignored for all other types + * \param[in] rowvect vector of results gathered down the columns of pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes a row vector of statistics using each
+ *          column of a Pix.  The result is put in %rowvect.
+ *      (2) The %thresh parameter works with L_MODE_VAL only, and
+ *          sets a minimum occupancy of the mode bin.
+ *          If the occupancy of the mode bin is less than %thresh, the
+ *          mode value is returned as 0.  To always return the actual
+ *          mode value, set %thresh = 0.
+ *      (3) What is the meaning of this %thresh parameter?
+ *          For each column, the total count in the histogram is h, the
+ *          image height.  So %thresh, relative to h, gives a measure
+ *          of the ratio of the bin width to the width of the distribution.
+ *          The larger %thresh, the narrower the distribution must be
+ *          for the mode value to be returned (instead of returning 0).
+ * 
+ */ +l_ok +pixGetColumnStats(PIX *pixs, + l_int32 type, + l_int32 nbins, + l_int32 thresh, + l_float32 *rowvect) +{ +l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval; +l_int32 *histo, *gray2bin, *bin2gray; +l_uint32 *datas; + + PROCNAME("pixGetColumnStats"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!rowvect) + return ERROR_INT("rowvect not defined", procName, 1); + if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && + type != L_MODE_VAL && type != L_MODE_COUNT) + return ERROR_INT("invalid type", procName, 1); + if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256)) + return ERROR_INT("invalid nbins", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (type == L_MEAN_ABSVAL) { + for (j = 0; j < w; j++) { + sum = 0; + for (i = 0; i < h; i++) + sum += GET_DATA_BYTE(datas + i * wpls, j); + rowvect[j] = (l_float32)sum / (l_float32)h; + } + return 0; + } + + /* We need a histogram; binwidth ~ 256 / nbins */ + histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); + gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); + for (i = 0; i < 256; i++) /* gray value --> histo bin */ + gray2bin[i] = (i * nbins) / 256; + for (i = 0; i < nbins; i++) /* histo bin --> gray value */ + bin2gray[i] = (i * 256 + 128) / nbins; + + for (j = 0; j < w; j++) { + for (i = 0; i < h; i++) { + val = GET_DATA_BYTE(datas + i * wpls, j); + histo[gray2bin[val]]++; + } + + if (type == L_MEDIAN_VAL) { + sum = 0; + target = (h + 1) / 2; + for (k = 0; k < nbins; k++) { + sum += histo[k]; + if (sum >= target) { + rowvect[j] = bin2gray[k]; + break; + } + } + } else if (type == L_MODE_VAL) { + max = 0; + modeval = 0; + for (k = 0; k < nbins; k++) { + if (histo[k] > max) { + max = histo[k]; + modeval = k; + } + } + if (max < thresh) + rowvect[j] = 0; + else + rowvect[j] = bin2gray[modeval]; + } else { /* type == L_MODE_COUNT */ + max = 0; + for (k = 0; k < nbins; k++) { + if (histo[k] > max) + max = histo[k]; + } + rowvect[j] = max; + } + for (k = 0; k < nbins; k++) + histo[k] = 0; + } + + LEPT_FREE(histo); + LEPT_FREE(gray2bin); + LEPT_FREE(bin2gray); + return 0; +} + + +/*! + * \brief pixSetPixelColumn() + * + * \param[in] pix 8 bpp; not cmapped + * \param[in] col column index + * \param[in] colvect vector of floats + * \return 0 if OK, 1 on error + */ +l_ok +pixSetPixelColumn(PIX *pix, + l_int32 col, + l_float32 *colvect) +{ +l_int32 i, w, h, wpl; +l_uint32 *data; + + PROCNAME("pixSetCPixelColumn"); + + if (!pix || pixGetDepth(pix) != 8) + return ERROR_INT("pix not defined or not 8 bpp", procName, 1); + if (!colvect) + return ERROR_INT("colvect not defined", procName, 1); + pixGetDimensions(pix, &w, &h, NULL); + if (col < 0 || col > w) + return ERROR_INT("invalid col", procName, 1); + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + for (i = 0; i < h; i++) + SET_DATA_BYTE(data + i * wpl, col, (l_int32)colvect[i]); + + return 0; +} + + +/*-------------------------------------------------------------* + * Foreground/background estimation * + *-------------------------------------------------------------*/ +/*! + * \brief pixThresholdForFgBg() + * + * \param[in] pixs any depth; cmapped ok + * \param[in] factor subsampling factor; integer >= 1 + * \param[in] thresh threshold for generating foreground mask + * \param[out] pfgval [optional] average foreground value + * \param[out] pbgval [optional] average background value + * \return 0 if OK, 1 on error + */ +l_ok +pixThresholdForFgBg(PIX *pixs, + l_int32 factor, + l_int32 thresh, + l_int32 *pfgval, + l_int32 *pbgval) +{ +l_float32 fval; +PIX *pixg, *pixm; + + PROCNAME("pixThresholdForFgBg"); + + if (pfgval) *pfgval = 0; + if (pbgval) *pbgval = 0; + if (!pfgval && !pbgval) + return ERROR_INT("no data requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Generate a subsampled 8 bpp version and a mask over the fg */ + pixg = pixConvertTo8BySampling(pixs, factor, 0); + pixm = pixThresholdToBinary(pixg, thresh); + + if (pfgval) { + pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval); + *pfgval = (l_int32)(fval + 0.5); + } + + if (pbgval) { + pixInvert(pixm, pixm); + pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval); + *pbgval = (l_int32)(fval + 0.5); + } + + pixDestroy(&pixg); + pixDestroy(&pixm); + return 0; +} + + +/*! + * \brief pixSplitDistributionFgBg() + * + * \param[in] pixs any depth; cmapped ok + * \param[in] scorefract fraction of the max score, used to determine + * the range over which the histogram min is searched + * \param[in] factor subsampling factor; integer >= 1 + * \param[out] pthresh [optional] best threshold for separating + * \param[out] pfgval [optional] average foreground value + * \param[out] pbgval [optional] average background value + * \param[out] ppixdb [optional] plot of distribution and split point + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See numaSplitDistribution() for details on the underlying
+ *          method of choosing a threshold.
+ * 
+ */ +l_ok +pixSplitDistributionFgBg(PIX *pixs, + l_float32 scorefract, + l_int32 factor, + l_int32 *pthresh, + l_int32 *pfgval, + l_int32 *pbgval, + PIX **ppixdb) +{ +char buf[256]; +l_int32 thresh; +l_float32 avefg, avebg, maxnum; +GPLOT *gplot; +NUMA *na, *nascore, *nax, *nay; +PIX *pixg; + + PROCNAME("pixSplitDistributionFgBg"); + + if (pthresh) *pthresh = 0; + if (pfgval) *pfgval = 0; + if (pbgval) *pbgval = 0; + if (ppixdb) *ppixdb = NULL; + if (!pthresh && !pfgval && !pbgval) + return ERROR_INT("no data requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Generate a subsampled 8 bpp version */ + pixg = pixConvertTo8BySampling(pixs, factor, 0); + + /* Make the fg/bg estimates */ + na = pixGetGrayHistogram(pixg, 1); + if (ppixdb) { + numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg, + NULL, NULL, &nascore); + numaDestroy(&nascore); + } else { + numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg, + NULL, NULL, NULL); + } + + if (pthresh) *pthresh = thresh; + if (pfgval) *pfgval = (l_int32)(avefg + 0.5); + if (pbgval) *pbgval = (l_int32)(avebg + 0.5); + + if (ppixdb) { + lept_mkdir("lept/redout"); + gplot = gplotCreate("/tmp/lept/redout/histplot", GPLOT_PNG, "Histogram", + "Grayscale value", "Number of pixels"); + gplotAddPlot(gplot, NULL, na, GPLOT_LINES, NULL); + nax = numaMakeConstant(thresh, 2); + numaGetMax(na, &maxnum, NULL); + nay = numaMakeConstant(0, 2); + numaReplaceNumber(nay, 1, (l_int32)(0.5 * maxnum)); + snprintf(buf, sizeof(buf), "score fract = %3.1f", scorefract); + gplotAddPlot(gplot, nax, nay, GPLOT_LINES, buf); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + numaDestroy(&nax); + numaDestroy(&nay); + *ppixdb = pixRead("/tmp/lept/redout/histplot.png"); + } + + pixDestroy(&pixg); + numaDestroy(&na); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pix5.c b/hgdriver/3rdparty/hgOCR/leptonica/pix5.c new file mode 100644 index 0000000..11aabb0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pix5.c @@ -0,0 +1,3153 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pix5.c + *
+ *
+ *    This file has these operations:
+ *
+ *      (1) Measurement of 1 bpp image properties
+ *      (2) Extract rectangular regions
+ *      (3) Clip to foreground
+ *      (4) Extract pixel averages, reversals and variance along lines
+ *      (5) Rank row and column transforms
+ *
+ *    Measurement of properties
+ *           l_int32     pixaFindDimensions()
+ *           l_int32     pixFindAreaPerimRatio()
+ *           NUMA       *pixaFindPerimToAreaRatio()
+ *           l_int32     pixFindPerimToAreaRatio()
+ *           NUMA       *pixaFindPerimSizeRatio()
+ *           l_int32     pixFindPerimSizeRatio()
+ *           NUMA       *pixaFindAreaFraction()
+ *           l_int32     pixFindAreaFraction()
+ *           NUMA       *pixaFindAreaFractionMasked()
+ *           l_int32     pixFindAreaFractionMasked()
+ *           NUMA       *pixaFindWidthHeightRatio()
+ *           NUMA       *pixaFindWidthHeightProduct()
+ *           l_int32     pixFindOverlapFraction()
+ *           BOXA       *pixFindRectangleComps()
+ *           l_int32     pixConformsToRectangle()
+ *
+ *    Extract rectangular region
+ *           PIXA       *pixClipRectangles()
+ *           PIX        *pixClipRectangle()
+ *           PIX        *pixClipMasked()
+ *           l_int32     pixCropToMatch()
+ *           PIX        *pixCropToSize()
+ *           PIX        *pixResizeToMatch()
+ *
+ *    Select a connected component by size
+ *           PIX        *pixSelectComponentBySize()
+ *           PIX        *pixFilterComponentBySize()
+ *
+ *    Make special masks
+ *           PIX        *pixMakeSymmetricMask()
+ *           PIX        *pixMakeFrameMask()
+ *
+ *    Generate a covering of rectangles over connected components
+ *           PIX        * pixMakeCoveringOfRectangles()
+ *
+ *    Fraction of Fg pixels under a mask
+ *           l_int32     pixFractionFgInMask()
+ *
+ *    Clip to foreground
+ *           PIX        *pixClipToForeground()
+ *           l_int32     pixTestClipToForeground()
+ *           l_int32     pixClipBoxToForeground()
+ *           l_int32     pixScanForForeground()
+ *           l_int32     pixClipBoxToEdges()
+ *           l_int32     pixScanForEdge()
+ *
+ *    Extract pixel averages and reversals along lines
+ *           NUMA       *pixExtractOnLine()
+ *           l_float32   pixAverageOnLine()
+ *           NUMA       *pixAverageIntensityProfile()
+ *           NUMA       *pixReversalProfile()
+ *
+ *    Extract windowed variance along a line
+ *           NUMA       *pixWindowedVarianceOnLine()
+ *
+ *    Extract min/max of pixel values near lines
+ *           l_int32     pixMinMaxNearLine()
+ *
+ *    Rank row and column transforms
+ *           PIX        *pixRankRowTransform()
+ *           PIX        *pixRankColumnTransform()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +static const l_uint32 rmask32[] = {0x0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, + 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, + 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, + 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, + 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; + +#ifndef NO_CONSOLE_IO +#define DEBUG_EDGES 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Measurement of properties * + *-------------------------------------------------------------*/ +/*! + * \brief pixaFindDimensions() + * + * \param[in] pixa + * \param[out] pnaw [optional] numa of pix widths + * \param[out] pnah [optional] numa of pix heights + * \return 0 if OK, 1 on error + */ +l_ok +pixaFindDimensions(PIXA *pixa, + NUMA **pnaw, + NUMA **pnah) +{ +l_int32 i, n, w, h; +PIX *pixt; + + PROCNAME("pixaFindDimensions"); + + if (pnaw) *pnaw = NULL; + if (pnah) *pnah = NULL; + if (!pnaw && !pnah) + return ERROR_INT("no output requested", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + if (pnaw) *pnaw = numaCreate(n); + if (pnah) *pnah = numaCreate(n); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pixt, &w, &h, NULL); + if (pnaw) + numaAddNumber(*pnaw, w); + if (pnah) + numaAddNumber(*pnah, h); + pixDestroy(&pixt); + } + return 0; +} + + +/*! + * \brief pixFindAreaPerimRatio() + * + * \param[in] pixs 1 bpp + * \param[in] tab [optional] pixel sum table, can be NULL + * \param[out] pfract area/perimeter ratio + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The area is the number of fg pixels that are not on the
+ *          boundary (i.e., are not 8-connected to a bg pixel), and the
+ *          perimeter is the number of fg boundary pixels.  Returns
+ *          0.0 if there are no fg pixels.
+ *      (2) This function is retained because clients are using it.
+ * 
+ */ +l_ok +pixFindAreaPerimRatio(PIX *pixs, + l_int32 *tab, + l_float32 *pfract) +{ +l_int32 *tab8; +l_int32 nfg, nbound; +PIX *pixt; + + PROCNAME("pixFindAreaPerimRatio"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + + pixt = pixErodeBrick(NULL, pixs, 3, 3); + pixCountPixels(pixt, &nfg, tab8); + if (nfg == 0) { + pixDestroy(&pixt); + if (!tab) LEPT_FREE(tab8); + return 0; + } + pixXor(pixt, pixt, pixs); + pixCountPixels(pixt, &nbound, tab8); + *pfract = (l_float32)nfg / (l_float32)nbound; + pixDestroy(&pixt); + + if (!tab) LEPT_FREE(tab8); + return 0; +} + + +/*! + * \brief pixaFindPerimToAreaRatio() + * + * \param[in] pixa of 1 bpp pix + * \return na of perimeter/arear ratio for each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components.
+ * 
+ */ +NUMA * +pixaFindPerimToAreaRatio(PIXA *pixa) +{ +l_int32 i, n; +l_int32 *tab; +l_float32 fract; +NUMA *na; +PIX *pixt; + + PROCNAME("pixaFindPerimToAreaRatio"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + tab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixFindPerimToAreaRatio(pixt, tab, &fract); + numaAddNumber(na, fract); + pixDestroy(&pixt); + } + LEPT_FREE(tab); + return na; +} + + +/*! + * \brief pixFindPerimToAreaRatio() + * + * \param[in] pixs 1 bpp + * \param[in] tab [optional] pixel sum table, can be NULL + * \param[out] pfract perimeter/area ratio + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The perimeter is the number of fg boundary pixels, and the
+ *          area is the number of fg pixels.  This returns 0.0 if
+ *          there are no fg pixels.
+ *      (2) Unlike pixFindAreaPerimRatio(), this uses the full set of
+ *          fg pixels for the area, and the ratio is taken in the opposite
+ *          order.
+ *      (3) This is typically used for a single connected component.
+ *          This always has a value <= 1.0, and if the average distance
+ *          of a fg pixel from the nearest bg pixel is d, this has
+ *          a value ~1/d.
+ * 
+ */ +l_ok +pixFindPerimToAreaRatio(PIX *pixs, + l_int32 *tab, + l_float32 *pfract) +{ +l_int32 *tab8; +l_int32 nfg, nbound; +PIX *pixt; + + PROCNAME("pixFindPerimToAreaRatio"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + + pixCountPixels(pixs, &nfg, tab8); + if (nfg == 0) { + if (!tab) LEPT_FREE(tab8); + return 0; + } + pixt = pixErodeBrick(NULL, pixs, 3, 3); + pixXor(pixt, pixt, pixs); + pixCountPixels(pixt, &nbound, tab8); + *pfract = (l_float32)nbound / (l_float32)nfg; + pixDestroy(&pixt); + + if (!tab) LEPT_FREE(tab8); + return 0; +} + + +/*! + * \brief pixaFindPerimSizeRatio() + * + * \param[in] pixa of 1 bpp pix + * \return na of fg perimeter/(2*(w+h)) ratio for each pix, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components.
+ *      (2) This has a minimum value for a circle of pi/4; a value for
+ *          a rectangle component of approx. 1.0; and a value much larger
+ *          than 1.0 for a component with a highly irregular boundary.
+ * 
+ */ +NUMA * +pixaFindPerimSizeRatio(PIXA *pixa) +{ +l_int32 i, n; +l_int32 *tab; +l_float32 ratio; +NUMA *na; +PIX *pixt; + + PROCNAME("pixaFindPerimSizeRatio"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + tab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixFindPerimSizeRatio(pixt, tab, &ratio); + numaAddNumber(na, ratio); + pixDestroy(&pixt); + } + LEPT_FREE(tab); + return na; +} + + +/*! + * \brief pixFindPerimSizeRatio() + * + * \param[in] pixs 1 bpp + * \param[in] tab [optional] pixel sum table, can be NULL + * \param[out] pratio perimeter/size ratio + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We take the 'size' as twice the sum of the width and
+ *          height of pixs, and the perimeter is the number of fg
+ *          boundary pixels.  We use the fg pixels of the boundary
+ *          because the pix may be clipped to the boundary, so an
+ *          erosion is required to count all boundary pixels.
+ *      (2) This has a large value for dendritic, fractal-like components
+ *          with highly irregular boundaries.
+ *      (3) This is typically used for a single connected component.
+ *          It has a value of about 1.0 for rectangular components with
+ *          relatively smooth boundaries.
+ * 
+ */ +l_ok +pixFindPerimSizeRatio(PIX *pixs, + l_int32 *tab, + l_float32 *pratio) +{ +l_int32 *tab8; +l_int32 w, h, nbound; +PIX *pixt; + + PROCNAME("pixFindPerimSizeRatio"); + + if (!pratio) + return ERROR_INT("&ratio not defined", procName, 1); + *pratio = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + + pixt = pixErodeBrick(NULL, pixs, 3, 3); + pixXor(pixt, pixt, pixs); + pixCountPixels(pixt, &nbound, tab8); + pixGetDimensions(pixs, &w, &h, NULL); + *pratio = (0.5 * nbound) / (l_float32)(w + h); + pixDestroy(&pixt); + + if (!tab) LEPT_FREE(tab8); + return 0; +} + + +/*! + * \brief pixaFindAreaFraction() + * + * \param[in] pixa of 1 bpp pix + * \return na of area fractions for each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components.
+ * 
+ */ +NUMA * +pixaFindAreaFraction(PIXA *pixa) +{ +l_int32 i, n; +l_int32 *tab; +l_float32 fract; +NUMA *na; +PIX *pixt; + + PROCNAME("pixaFindAreaFraction"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + tab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixFindAreaFraction(pixt, tab, &fract); + numaAddNumber(na, fract); + pixDestroy(&pixt); + } + LEPT_FREE(tab); + return na; +} + + +/*! + * \brief pixFindAreaFraction() + * + * \param[in] pixs 1 bpp + * \param[in] tab [optional] pixel sum table, can be NULL + * \param[out] pfract fg area/size ratio + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This finds the ratio of the number of fg pixels to the
+ *          size of the pix (w * h).  It is typically used for a
+ *          single connected component.
+ * 
+ */ +l_ok +pixFindAreaFraction(PIX *pixs, + l_int32 *tab, + l_float32 *pfract) +{ +l_int32 w, h, sum; +l_int32 *tab8; + + PROCNAME("pixFindAreaFraction"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + pixGetDimensions(pixs, &w, &h, NULL); + pixCountPixels(pixs, &sum, tab8); + *pfract = (l_float32)sum / (l_float32)(w * h); + + if (!tab) LEPT_FREE(tab8); + return 0; +} + + +/*! + * \brief pixaFindAreaFractionMasked() + * + * \param[in] pixa of 1 bpp pix + * \param[in] pixm mask image + * \param[in] debug 1 for output, 0 to suppress + * \return na of ratio masked/total fractions for each pix, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components, which has an associated
+ *          boxa giving the location of the components relative
+ *          to the mask origin.
+ *      (2) The debug flag displays in green and red the masked and
+ *          unmasked parts of the image from which pixa was derived.
+ * 
+ */ +NUMA * +pixaFindAreaFractionMasked(PIXA *pixa, + PIX *pixm, + l_int32 debug) +{ +l_int32 i, n, full; +l_int32 *tab; +l_float32 fract; +BOX *box; +NUMA *na; +PIX *pix; + + PROCNAME("pixaFindAreaFractionMasked"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 1) + return (NUMA *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + tab = makePixelSumTab8(); + pixaIsFull(pixa, NULL, &full); /* check boxa */ + box = NULL; + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + if (full) + box = pixaGetBox(pixa, i, L_CLONE); + pixFindAreaFractionMasked(pix, box, pixm, tab, &fract); + numaAddNumber(na, fract); + boxDestroy(&box); + pixDestroy(&pix); + } + LEPT_FREE(tab); + + if (debug) { + l_int32 w, h; + PIX *pix1, *pix2; + pixGetDimensions(pixm, &w, &h, NULL); + pix1 = pixaDisplay(pixa, w, h); /* recover original image */ + pix2 = pixCreate(w, h, 8); /* make an 8 bpp white image ... */ + pixSetColormap(pix2, pixcmapCreate(8)); /* that's cmapped ... */ + pixSetBlackOrWhite(pix2, L_SET_WHITE); /* and init to white */ + pixSetMaskedCmap(pix2, pix1, 0, 0, 255, 0, 0); /* color all fg red */ + pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, 0, 0); + pixSetMaskedCmap(pix2, pix1, 0, 0, 0, 255, 0); /* turn masked green */ + pixDisplay(pix2, 100, 100); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return na; +} + + +/*! + * \brief pixFindAreaFractionMasked() + * + * \param[in] pixs 1 bpp, typically a single component + * \param[in] box [optional] for pixs relative to pixm + * \param[in] pixm 1 bpp mask, typically over the entire image from + * which the component pixs was extracted + * \param[in] tab [optional] pixel sum table, can be NULL + * \param[out] pfract fg area/size ratio + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This finds the ratio of the number of masked fg pixels
+ *          in pixs to the total number of fg pixels in pixs.
+ *          It is typically used for a single connected component.
+ *          If there are no fg pixels, this returns a ratio of 0.0.
+ *      (2) The box gives the location of the pix relative to that
+ *          of the UL corner of the mask.  Therefore, the rasterop
+ *          is performed with the pix translated to its location
+ *          (x, y) in the mask before ANDing.
+ *          If box == NULL, the UL corners of pixs and pixm are aligned.
+ * 
+ */ +l_ok +pixFindAreaFractionMasked(PIX *pixs, + BOX *box, + PIX *pixm, + l_int32 *tab, + l_float32 *pfract) +{ +l_int32 x, y, w, h, sum, masksum; +l_int32 *tab8; +PIX *pix1; + + PROCNAME("pixFindAreaFractionMasked"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + x = y = 0; + if (box) + boxGetGeometry(box, &x, &y, NULL, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + + pix1 = pixCopy(NULL, pixs); + pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, x, y); + pixCountPixels(pixs, &sum, tab8); + if (sum == 0) { + pixDestroy(&pix1); + if (!tab) LEPT_FREE(tab8); + return 0; + } + pixCountPixels(pix1, &masksum, tab8); + *pfract = (l_float32)masksum / (l_float32)sum; + + if (!tab) LEPT_FREE(tab8); + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief pixaFindWidthHeightRatio() + * + * \param[in] pixa of 1 bpp pix + * \return na of width/height ratios for each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components.
+ * 
+ */ +NUMA * +pixaFindWidthHeightRatio(PIXA *pixa) +{ +l_int32 i, n, w, h; +NUMA *na; +PIX *pixt; + + PROCNAME("pixaFindWidthHeightRatio"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pixt, &w, &h, NULL); + numaAddNumber(na, (l_float32)w / (l_float32)h); + pixDestroy(&pixt); + } + return na; +} + + +/*! + * \brief pixaFindWidthHeightProduct() + * + * \param[in] pixa of 1 bpp pix + * \return na of width*height products for each pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is typically used for a pixa consisting of
+ *          1 bpp connected components.
+ * 
+ */ +NUMA * +pixaFindWidthHeightProduct(PIXA *pixa) +{ +l_int32 i, n, w, h; +NUMA *na; +PIX *pixt; + + PROCNAME("pixaFindWidthHeightProduct"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pixt, &w, &h, NULL); + numaAddNumber(na, w * h); + pixDestroy(&pixt); + } + return na; +} + + +/*! + * \brief pixFindOverlapFraction() + * + * \param[in] pixs1, pixs2 1 bpp + * \param[in] x2, y2 location in pixs1 of UL corner of pixs2 + * \param[in] tab [optional] pixel sum table, can be null + * \param[out] pratio ratio fg intersection to fg union + * \param[out] pnoverlap [optional] number of overlapping pixels + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The UL corner of pixs2 is placed at (x2, y2) in pixs1.
+ *      (2) This measure is similar to the correlation.
+ * 
+ */ +l_ok +pixFindOverlapFraction(PIX *pixs1, + PIX *pixs2, + l_int32 x2, + l_int32 y2, + l_int32 *tab, + l_float32 *pratio, + l_int32 *pnoverlap) +{ +l_int32 *tab8; +l_int32 w, h, nintersect, nunion; +PIX *pixt; + + PROCNAME("pixFindOverlapFraction"); + + if (pnoverlap) *pnoverlap = 0; + if (!pratio) + return ERROR_INT("&ratio not defined", procName, 1); + *pratio = 0.0; + if (!pixs1 || pixGetDepth(pixs1) != 1) + return ERROR_INT("pixs1 not defined or not 1 bpp", procName, 1); + if (!pixs2 || pixGetDepth(pixs2) != 1) + return ERROR_INT("pixs2 not defined or not 1 bpp", procName, 1); + + if (!tab) + tab8 = makePixelSumTab8(); + else + tab8 = tab; + + pixGetDimensions(pixs2, &w, &h, NULL); + pixt = pixCopy(NULL, pixs1); + pixRasterop(pixt, x2, y2, w, h, PIX_MASK, pixs2, 0, 0); /* AND */ + pixCountPixels(pixt, &nintersect, tab8); + if (pnoverlap) + *pnoverlap = nintersect; + pixCopy(pixt, pixs1); + pixRasterop(pixt, x2, y2, w, h, PIX_PAINT, pixs2, 0, 0); /* OR */ + pixCountPixels(pixt, &nunion, tab8); + if (!tab) LEPT_FREE(tab8); + pixDestroy(&pixt); + + if (nunion > 0) + *pratio = (l_float32)nintersect / (l_float32)nunion; + return 0; +} + + +/*! + * \brief pixFindRectangleComps() + * + * \param[in] pixs 1 bpp + * \param[in] dist max distance allowed between bounding box + * and nearest foreground pixel within it + * \param[in] minw, minh minimum size in each direction as a requirement + * for a conforming rectangle + * \return boxa of components that conform, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies the function pixConformsToRectangle() to
+ *          each 8-c.c. in pixs, and returns a boxa containing the
+ *          regions of all components that are conforming.
+ *      (2) Conforming components must satisfy both the size constraint
+ *          given by %minsize and the slop in conforming to a rectangle
+ *          determined by %dist.
+ * 
+ */ +BOXA * +pixFindRectangleComps(PIX *pixs, + l_int32 dist, + l_int32 minw, + l_int32 minh) +{ +l_int32 w, h, i, n, conforms; +BOX *box; +BOXA *boxa, *boxad; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixFindRectangleComps"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (dist < 0) + return (BOXA *)ERROR_PTR("dist must be >= 0", procName, NULL); + if (minw <= 2 * dist && minh <= 2 * dist) + return (BOXA *)ERROR_PTR("invalid parameters", procName, NULL); + + boxa = pixConnComp(pixs, &pixa, 8); + boxad = boxaCreate(0); + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pix, &w, &h, NULL); + if (w < minw || h < minh) { + pixDestroy(&pix); + continue; + } + pixConformsToRectangle(pix, NULL, dist, &conforms); + if (conforms) { + box = boxaGetBox(boxa, i, L_COPY); + boxaAddBox(boxad, box, L_INSERT); + } + pixDestroy(&pix); + } + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return boxad; +} + + +/*! + * \brief pixConformsToRectangle() + * + * \param[in] pixs 1 bpp + * \param[in] box [optional] if null, use the entire pixs + * \param[in] dist max distance allowed between bounding box and + * nearest foreground pixel within it + * \param[out] pconforms 0 (false) if not conforming; + * 1 (true) if conforming + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) There are several ways to test if a connected component has
+ *          an essentially rectangular boundary, such as:
+ *           a. Fraction of fill into the bounding box
+ *           b. Max-min distance of fg pixel from periphery of bounding box
+ *           c. Max depth of bg intrusions into component within bounding box
+ *          The weakness of (a) is that it is highly sensitive to holes
+ *          within the c.c.  The weakness of (b) is that it can have
+ *          arbitrarily large intrusions into the c.c.  Method (c) tests
+ *          the integrity of the outer boundary of the c.c., with respect
+ *          to the enclosing bounding box, so we use it.
+ *      (2) This tests if the connected component within the box conforms
+ *          to the box at all points on the periphery within %dist.
+ *          Inside, at a distance from the box boundary that is greater
+ *          than %dist, we don't care about the pixels in the c.c.
+ *      (3) We can think of the conforming condition as follows:
+ *          No pixel inside a distance %dist from the boundary
+ *          can connect to the boundary through a path through the bg.
+ *          To implement this, we need to do a flood fill.  We can go
+ *          either from inside toward the boundary, or the other direction.
+ *          It's easiest to fill from the boundary, and then verify that
+ *          there are no filled pixels farther than %dist from the boundary.
+ * 
+ */ +l_ok +pixConformsToRectangle(PIX *pixs, + BOX *box, + l_int32 dist, + l_int32 *pconforms) +{ +l_int32 w, h, empty; +PIX *pix1, *pix2; + + PROCNAME("pixConformsToRectangle"); + + if (!pconforms) + return ERROR_INT("&conforms not defined", procName, 1); + *pconforms = 0; + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (dist < 0) + return ERROR_INT("dist must be >= 0", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (w <= 2 * dist || h <= 2 * dist) { + L_WARNING("automatic conformation: distance too large\n", procName); + *pconforms = 1; + return 0; + } + + /* Extract the region, if necessary */ + if (box) + pix1 = pixClipRectangle(pixs, box, NULL); + else + pix1 = pixCopy(NULL, pixs); + + /* Invert and fill from the boundary into the interior. + * Because we're considering the connected component in an + * 8-connected sense, we do the background filling as 4 c.c. */ + pixInvert(pix1, pix1); + pix2 = pixExtractBorderConnComps(pix1, 4); + + /* Mask out all pixels within a distance %dist from the box + * boundary. Any remaining pixels are from filling that goes + * more than %dist from the boundary. If no pixels remain, + * the component conforms to the bounding rectangle within + * a distance %dist. */ + pixSetOrClearBorder(pix2, dist, dist, dist, dist, PIX_CLR); + pixZero(pix2, &empty); + pixDestroy(&pix1); + pixDestroy(&pix2); + *pconforms = (empty) ? 1 : 0; + return 0; +} + + +/*-----------------------------------------------------------------------* + * Extract rectangular region * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixClipRectangles() + * + * \param[in] pixs + * \param[in] boxa requested clipping regions + * \return pixa consisting of requested regions, or NULL on error + * + *
+ * Notes:
+ *     (1) The returned pixa includes the actual regions clipped out from
+ *         the input pixs.
+ * 
+ */ +PIXA * +pixClipRectangles(PIX *pixs, + BOXA *boxa) +{ +l_int32 i, n; +BOX *box, *boxc; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixClipRectangles"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIXA *)ERROR_PTR("boxa not defined", procName, NULL); + + n = boxaGetCount(boxa); + pixa = pixaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pix = pixClipRectangle(pixs, box, &boxc); + pixaAddPix(pixa, pix, L_INSERT); + pixaAddBox(pixa, boxc, L_INSERT); + boxDestroy(&box); + } + + return pixa; +} + + +/*! + * \brief pixClipRectangle() + * + * \param[in] pixs + * \param[in] box requested clipping region; const + * \param[out] pboxc [optional] actual box of clipped region + * \return clipped pix, or NULL on error or if rectangle + * doesn't intersect pixs + * + *
+ * Notes:
+ *
+ *  This should be simple, but there are choices to be made.
+ *  The box is defined relative to the pix coordinates.  However,
+ *  if the box is not contained within the pix, we have two choices:
+ *
+ *      (1) clip the box to the pix
+ *      (2) make a new pix equal to the full box dimensions,
+ *          but let rasterop do the clipping and positioning
+ *          of the src with respect to the dest
+ *
+ *  Choice (2) immediately brings up the problem of what pixel values
+ *  to use that were not taken from the src.  For example, on a grayscale
+ *  image, do you want the pixels not taken from the src to be black
+ *  or white or something else?  To implement choice 2, one needs to
+ *  specify the color of these extra pixels.
+ *
+ *  So we adopt (1), and clip the box first, if necessary,
+ *  before making the dest pix and doing the rasterop.  But there
+ *  is another issue to consider.  If you want to paste the
+ *  clipped pix back into pixs, it must be properly aligned, and
+ *  it is necessary to use the clipped box for alignment.
+ *  Accordingly, this function has a third (optional) argument, which is
+ *  the input box clipped to the src pix.
+ * 
+ */ +PIX * +pixClipRectangle(PIX *pixs, + BOX *box, + BOX **pboxc) +{ +l_int32 w, h, d, bx, by, bw, bh; +BOX *boxc; +PIX *pixd; + + PROCNAME("pixClipRectangle"); + + if (pboxc) *pboxc = NULL; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!box) + return (PIX *)ERROR_PTR("box not defined", procName, NULL); + + /* Clip the input box to the pix */ + pixGetDimensions(pixs, &w, &h, &d); + if ((boxc = boxClipToRectangle(box, w, h)) == NULL) { + L_WARNING("box doesn't overlap pix\n", procName); + return NULL; + } + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + + /* Extract the block */ + if ((pixd = pixCreate(bw, bh, d)) == NULL) { + boxDestroy(&boxc); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixRasterop(pixd, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); + + if (pboxc) + *pboxc = boxc; + else + boxDestroy(&boxc); + + return pixd; +} + + +/*! + * \brief pixClipMasked() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp; colormap ok + * \param[in] pixm clipping mask, 1 bpp + * \param[in] x, y origin of clipping mask relative to pixs + * \param[in] outval val to use for pixels that are outside the mask + * \return pixd, clipped pix or NULL on error or if pixm doesn't + * intersect pixs + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, it is preserved in pixd.
+ *      (2) The depth of pixd is the same as that of pixs.
+ *      (3) If the depth of pixs is 1, use %outval = 0 for white background
+ *          and 1 for black; otherwise, use the max value for white
+ *          and 0 for black.  If pixs has a colormap, the max value for
+ *          %outval is 0xffffffff; otherwise, it is 2^d - 1.
+ *      (4) When using 1 bpp pixs, this is a simple clip and
+ *          blend operation.  For example, if both pix1 and pix2 are
+ *          black text on white background, and you want to OR the
+ *          fg on the two images, let pixm be the inverse of pix2.
+ *          Then the operation takes all of pix1 that's in the bg of
+ *          pix2, and for the remainder (which are the pixels
+ *          corresponding to the fg of the pix2), paint them black
+ *          (1) in pix1.  The function call looks like
+ *             pixClipMasked(pix2, pixInvert(pix1, pix1), x, y, 1);
+ * 
+ */ +PIX * +pixClipMasked(PIX *pixs, + PIX *pixm, + l_int32 x, + l_int32 y, + l_uint32 outval) +{ +l_int32 wm, hm, index, rval, gval, bval; +l_uint32 pixel; +BOX *box; +PIX *pixmi, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixClipMasked"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); + + /* Clip out the region specified by pixm and (x,y) */ + pixGetDimensions(pixm, &wm, &hm, NULL); + box = boxCreate(x, y, wm, hm); + pixd = pixClipRectangle(pixs, box, NULL); + + /* Paint 'outval' (or something close to it if cmapped) through + * the pixels not masked by pixm */ + cmap = pixGetColormap(pixd); + pixmi = pixInvert(NULL, pixm); + if (cmap) { + extractRGBValues(outval, &rval, &gval, &bval); + pixcmapGetNearestIndex(cmap, rval, gval, bval, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &pixel); + pixPaintThroughMask(pixd, pixmi, 0, 0, pixel); + } else { + pixPaintThroughMask(pixd, pixmi, 0, 0, outval); + } + + boxDestroy(&box); + pixDestroy(&pixmi); + return pixd; +} + + +/*! + * \brief pixCropToMatch() + * + * \param[in] pixs1 any depth, colormap OK + * \param[in] pixs2 any depth, colormap OK + * \param[out] ppixd1 may be a clone + * \param[out] ppixd2 may be a clone + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This resizes pixs1 and/or pixs2 by cropping at the right
+ *          and bottom, so that they're the same size.
+ *      (2) If a pix doesn't need to be cropped, a clone is returned.
+ *      (3) Note: the images are implicitly aligned to the UL corner.
+ * 
+ */ +l_ok +pixCropToMatch(PIX *pixs1, + PIX *pixs2, + PIX **ppixd1, + PIX **ppixd2) +{ +l_int32 w1, h1, w2, h2, w, h; + + PROCNAME("pixCropToMatch"); + + if (!ppixd1 || !ppixd2) + return ERROR_INT("&pixd1 and &pixd2 not both defined", procName, 1); + *ppixd1 = *ppixd2 = NULL; + if (!pixs1 || !pixs2) + return ERROR_INT("pixs1 and pixs2 not defined", procName, 1); + + pixGetDimensions(pixs1, &w1, &h1, NULL); + pixGetDimensions(pixs2, &w2, &h2, NULL); + w = L_MIN(w1, w2); + h = L_MIN(h1, h2); + + *ppixd1 = pixCropToSize(pixs1, w, h); + *ppixd2 = pixCropToSize(pixs2, w, h); + if (*ppixd1 == NULL || *ppixd2 == NULL) + return ERROR_INT("cropped image failure", procName, 1); + return 0; +} + + +/*! + * \brief pixCropToSize() + * + * \param[in] pixs any depth, colormap OK + * \param[in] w, h max dimensions of cropped image + * \return pixd cropped if necessary or NULL on error. + * + *
+ * Notes:
+ *      (1) If either w or h is smaller than the corresponding dimension
+ *          of pixs, this returns a cropped image; otherwise it returns
+ *          a clone of pixs.
+ * 
+ */ +PIX * +pixCropToSize(PIX *pixs, + l_int32 w, + l_int32 h) +{ +l_int32 ws, hs, wd, hd, d; +PIX *pixd; + + PROCNAME("pixCropToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, &d); + if (ws <= w && hs <= h) /* no cropping necessary */ + return pixClone(pixs); + + wd = L_MIN(ws, w); + hd = L_MIN(hs, h); + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, 0, 0); + return pixd; +} + + +/*! + * \brief pixResizeToMatch() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp; colormap ok + * \param[in] pixt can be null; we use only the size + * \param[in] w, h ignored if pixt is defined + * \return pixd resized to match or NULL on error + * + *
+ * Notes:
+ *      (1) This resizes pixs to make pixd, without scaling, by either
+ *          cropping or extending separately in both width and height.
+ *          Extension is done by replicating the last row or column.
+ *          This is useful in a situation where, due to scaling
+ *          operations, two images that are expected to be the
+ *          same size can differ slightly in each dimension.
+ *      (2) You can use either an existing pixt or specify
+ *          both %w and %h.  If pixt is defined, the values
+ *          in %w and %h are ignored.
+ *      (3) If pixt is larger than pixs (or if w and/or d is larger
+ *          than the dimension of pixs, replicate the outer row and
+ *          column of pixels in pixs into pixd.
+ * 
+ */ +PIX * +pixResizeToMatch(PIX *pixs, + PIX *pixt, + l_int32 w, + l_int32 h) +{ +l_int32 i, j, ws, hs, d; +PIX *pixd; + + PROCNAME("pixResizeToMatch"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!pixt && (w <= 0 || h <= 0)) + return (PIX *)ERROR_PTR("both w and h not > 0", procName, NULL); + + if (pixt) /* redefine w, h */ + pixGetDimensions(pixt, &w, &h, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (ws == w && hs == h) + return pixCopy(NULL, pixs); + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixRasterop(pixd, 0, 0, ws, hs, PIX_SRC, pixs, 0, 0); + if (ws >= w && hs >= h) + return pixd; + + /* Replicate the last column and then the last row */ + if (ws < w) { + for (j = ws; j < w; j++) + pixRasterop(pixd, j, 0, 1, h, PIX_SRC, pixd, ws - 1, 0); + } + if (hs < h) { + for (i = hs; i < h; i++) + pixRasterop(pixd, 0, i, w, 1, PIX_SRC, pixd, 0, hs - 1); + } + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Select a connected component by size * + *---------------------------------------------------------------------*/ +/*! + * \brief pixSelectComponentBySize() + * + * \param[in] pixs 1 bpp + * \param[in] rankorder in decreasing size: 0 for largest. + * \param[in] type L_SELECT_BY_WIDTH, L_SELECT_BY_HEIGHT, + * L_SELECT_BY_MAX_DIMENSION, + * L_SELECT_BY_AREA, L_SELECT_BY_PERIMETER + * \param[in] connectivity 4 or 8 + * \param[out] pbox [optional] location of returned component + * \return pix of rank order connected component, or NULL on error. + * + *
+ * Notes:
+ *      (1) This selects the Nth largest connected component, based on
+ *          the selection type and connectivity.
+ *      (2) Note that %rankorder is an integer.  Use %rankorder = 0 for
+ *          the largest component and %rankorder = -1 for the smallest.
+ *          If %rankorder >= number of components, select the smallest.
+ */
+PIX *
+pixSelectComponentBySize(PIX     *pixs,
+                         l_int32  rankorder,
+                         l_int32  type,
+                         l_int32  connectivity,
+                         BOX    **pbox)
+{
+l_int32  n, empty, sorttype, index;
+BOXA    *boxa1;
+NUMA    *naindex;
+PIX     *pixd;
+PIXA    *pixa1, *pixa2;
+
+    PROCNAME("pixSelectComponentBySize");
+
+    if (pbox) *pbox = NULL;
+    if (!pixs || pixGetDepth(pixs) != 1)
+        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
+    if (type == L_SELECT_BY_WIDTH)
+        sorttype = L_SORT_BY_WIDTH;
+    else if (type == L_SELECT_BY_HEIGHT)
+        sorttype = L_SORT_BY_HEIGHT;
+    else if (type == L_SELECT_BY_MAX_DIMENSION)
+        sorttype = L_SORT_BY_MAX_DIMENSION;
+    else if (type == L_SELECT_BY_AREA)
+        sorttype = L_SORT_BY_AREA;
+    else if (type == L_SELECT_BY_PERIMETER)
+        sorttype = L_SORT_BY_PERIMETER;
+    else
+        return (PIX *)ERROR_PTR("invalid selection type", procName, NULL);
+    if (connectivity != 4 && connectivity != 8)
+        return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
+    pixZero(pixs, &empty);
+    if (empty)
+        return (PIX *)ERROR_PTR("no foreground pixels", procName, NULL);
+
+    boxa1 = pixConnComp(pixs, &pixa1, connectivity);
+    n = boxaGetCount(boxa1);
+    if (rankorder < 0 || rankorder >= n)
+        rankorder = n - 1;  /* smallest */
+    pixa2 = pixaSort(pixa1, sorttype, L_SORT_DECREASING, &naindex, L_CLONE);
+    pixd = pixaGetPix(pixa2, rankorder, L_COPY);
+    if (pbox) {
+        numaGetIValue(naindex, rankorder, &index);
+        *pbox = boxaGetBox(boxa1, index, L_COPY);
+    }
+
+    numaDestroy(&naindex);
+    boxaDestroy(&boxa1);
+    pixaDestroy(&pixa1);
+    pixaDestroy(&pixa2);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixFilterComponentBySize()
+ *
+ * \param[in]    pixs          1 bpp
+ * \param[in]    rankorder     in decreasing size: 0 for largest.
+ * \param[in]    type          L_SELECT_BY_WIDTH, L_SELECT_BY_HEIGHT,
+ *                             L_SELECT_BY_MAX_DIMENSION,
+ *                             L_SELECT_BY_AREA, L_SELECT_BY_PERIMETER
+ * \param[in]    connectivity  4 or 8
+ * \param[out]   pbox          [optional] location of returned component
+ * \return  pix with all other components removed, or NULL on error.
+ *
+ * 
+ * Notes:
+ *      (1) See notes in pixSelectComponentBySize().
+ *      (2) This returns a copy of %pixs, with all components removed
+ *          except for the selected one.
+ */
+PIX *
+pixFilterComponentBySize(PIX     *pixs,
+                         l_int32  rankorder,
+                         l_int32  type,
+                         l_int32  connectivity,
+                         BOX    **pbox)
+{
+l_int32  x, y, w, h;
+BOX     *box;
+PIX     *pix1, *pix2;
+
+    PROCNAME("pixFilterComponentBySize");
+
+    if (!pixs || pixGetDepth(pixs) != 1)
+        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
+
+    pix1 = pixSelectComponentBySize(pixs, rankorder, type, connectivity, &box);
+    if (!pix1) {
+        boxDestroy(&box);
+        return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
+    }
+
+        /* Put the selected component in a new pix at the same
+         * location as it had in %pixs */
+    boxGetGeometry(box, &x, &y, &w, &h);
+    pix2 = pixCreateTemplate(pixs);
+    pixRasterop(pix2, x, y, w, h, PIX_SRC, pix1, 0, 0);
+    if (pbox)
+        *pbox = box;
+    else
+        boxDestroy(&box);
+    pixDestroy(&pix1);
+    return pix2;
+}
+
+
+/*---------------------------------------------------------------------*
+ *                         Make special masks                          *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixMakeSymmetricMask()
+ *
+ * \param[in]    w, h    dimensions of output 1 bpp pix
+ * \param[in]    hf      horizontal fraction of half-width
+ * \param[in]    vf      vertical fraction of half-height
+ * \param[in]    type    L_USE_INNER, L_USE_OUTER
+ * \return  pixd 1 bpp, or NULL on error.
+ *
+ * 
+ * Notes:
+ *      (1) This is a convenience function for generating masks with
+ *          horizontal and vertical reflection symmetry, over either
+ *          the inner or outer parts of an image.
+ *      (2) Using L_USE_INNER to generate a mask over the inner part
+ *          of the image, the mask is a solid rectangle, and the fractions
+ *          describe the distance between the boundary of the image and
+ *          the rectangle boundary.  For example, with hf == vf == 0.0,
+ *          the mask covers the full image.
+ *      (3) Using L_USE_OUTER to generate a mask over an outer frame
+ *          of the image, the mask touches the boundary of the image,
+ *          and the fractions describe the location of the inner
+ *          boundary of the frame.  For example, with hf == vf == 1.0,
+ *          the inner boundary is at the center of the image, so the
+ *          mask covers the full image.
+ *      (4) More examples:
+ *           * mask covering the inner 70%: hf = vf = 0.3, type = L_USE_INNER
+ *           * frame covering the outer 30%: hf = vf = 0.3, type = L_USE_OUTER
+ * 
+ */ +PIX * +pixMakeSymmetricMask(l_int32 w, + l_int32 h, + l_float32 hf, + l_float32 vf, + l_int32 type) +{ + PROCNAME("pixMakeSymmetricMask"); + + if (w <= 0 || h <= 0) + return (PIX *)ERROR_PTR("mask size 0", procName, NULL); + if (hf < 0.0 || hf > 1.0) + return (PIX *)ERROR_PTR("invalid horiz fractions", procName, NULL); + if (vf < 0.0 || vf > 1.0) + return (PIX *)ERROR_PTR("invalid vert fractions", procName, NULL); + + if (type == L_USE_INNER) + return pixMakeFrameMask(w, h, hf, 1.0, vf, 1.0); + else if (type == L_USE_OUTER) + return pixMakeFrameMask(w, h, 0.0, hf, 0.0, vf); + else + return (PIX *)ERROR_PTR("invalid type", procName, NULL); +} + + +/*! + * \brief pixMakeFrameMask() + * + * \param[in] w, h dimensions of output 1 bpp pix + * \param[in] hf1 horizontal fraction of half-width at outer frame bdry + * \param[in] hf2 horizontal fraction of half-width at inner frame bdry + * \param[in] vf1 vertical fraction of half-width at outer frame bdry + * \param[in] vf2 vertical fraction of half-width at inner frame bdry + * \return pixd 1 bpp, or NULL on error. + * + *
+ * Notes:
+ *      (1) This makes an arbitrary 1-component mask with a centered fg
+ *          frame, which can have both an inner and an outer boundary.
+ *          All input fractional distances are measured from the image
+ *          border to the frame boundary, in units of the image half-width
+ *          for hf1 and hf2 and the image half-height for vf1 and vf2.
+ *          The distances to the outer frame boundary are given by hf1
+ *          and vf1; to the inner frame boundary, by hf2 and vf2.
+ *          Input fractions are thus in [0.0 ... 1.0], with hf1 <= hf2
+ *          and vf1 <= vf2.  Horizontal and vertical frame widths are
+ *          thus independently specified.
+ *      (2) Special cases:
+ *           * full fg mask: hf1 = vf1 = 0.0, hf2 = vf2 = 1.0.
+ *           * empty fg (zero width) mask: set  hf1 = hf2  and vf1 = vf2.
+ *           * fg rectangle with no hole: set hf2 = vf2 = 1.0.
+ *           * frame touching outer boundary: set hf1 = vf1 = 0.0.
+ *      (3) The vertical thickness of the horizontal mask parts
+ *          is 0.5 * (vf2 - vf1) * h.  The horizontal thickness of the
+ *          vertical mask parts is 0.5 * (hf2 - hf1) * w.
+ * 
+ */ +PIX * +pixMakeFrameMask(l_int32 w, + l_int32 h, + l_float32 hf1, + l_float32 hf2, + l_float32 vf1, + l_float32 vf2) +{ +l_int32 h1, h2, v1, v2; +PIX *pixd; + + PROCNAME("pixMakeFrameMask"); + + if (w <= 0 || h <= 0) + return (PIX *)ERROR_PTR("mask size 0", procName, NULL); + if (hf1 < 0.0 || hf1 > 1.0 || hf2 < 0.0 || hf2 > 1.0) + return (PIX *)ERROR_PTR("invalid horiz fractions", procName, NULL); + if (vf1 < 0.0 || vf1 > 1.0 || vf2 < 0.0 || vf2 > 1.0) + return (PIX *)ERROR_PTR("invalid vert fractions", procName, NULL); + if (hf1 > hf2 || vf1 > vf2) + return (PIX *)ERROR_PTR("invalid relative sizes", procName, NULL); + + pixd = pixCreate(w, h, 1); + + /* Special cases */ + if (hf1 == 0.0 && vf1 == 0.0 && hf2 == 1.0 && vf2 == 1.0) { /* full */ + pixSetAll(pixd); + return pixd; + } + if (hf1 == hf2 && vf1 == vf2) { /* empty */ + return pixd; + } + + /* General case */ + h1 = 0.5 * hf1 * w; + h2 = 0.5 * hf2 * w; + v1 = 0.5 * vf1 * h; + v2 = 0.5 * vf2 * h; + pixRasterop(pixd, h1, v1, w - 2 * h1, h - 2 * v1, PIX_SET, NULL, 0, 0); + if (hf2 < 1.0 && vf2 < 1.0) + pixRasterop(pixd, h2, v2, w - 2 * h2, h - 2 * v2, PIX_CLR, NULL, 0, 0); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Generate a covering of rectangles over connected components * + *---------------------------------------------------------------------*/ +/*! + * \brief pixMakeCoveringOfRectangles() + * + * \param[in] pixs 1 bpp + * \param[in] maxiters max iterations: use 0 to iterate to completion + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This iteratively finds the bounding boxes of the connected
+ *          components and generates a mask from them.  Two iterations
+ *          should suffice for most situations.
+ *      (2) Returns an empty pix if %pixs is empty.
+ *      (3) If there are many small components in proximity, it may
+ *          be useful to merge them with a morphological closing before
+ *          calling this one.
+ * 
+ */ +PIX * +pixMakeCoveringOfRectangles(PIX *pixs, + l_int32 maxiters) +{ +l_int32 empty, same, niters; +BOXA *boxa; +PIX *pix1, *pix2; + + PROCNAME("pixMakeCoveringOfRectangles"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (maxiters < 0) + return (PIX *)ERROR_PTR("maxiters must be >= 0", procName, NULL); + if (maxiters == 0) maxiters = 50; /* ridiculously large number */ + + pixZero(pixs, &empty); + pix1 = pixCreateTemplate(pixs); + if (empty) return pix1; + + /* Do first iteration */ + boxa = pixConnCompBB(pixs, 8); + pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); + boxaDestroy(&boxa); + if (maxiters == 1) return pix1; + + niters = 1; + while (niters < maxiters) { /* continue to add pixels to pix1 */ + niters++; + boxa = pixConnCompBB(pix1, 8); + pix2 = pixCopy(NULL, pix1); + pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); + boxaDestroy(&boxa); + pixEqual(pix1, pix2, &same); + pixDestroy(&pix2); + if (same) { + L_INFO("%d iterations\n", procName, niters - 1); + return pix1; + } + } + L_INFO("maxiters = %d reached\n", procName, niters); + return pix1; +} + + +/*---------------------------------------------------------------------* + * Fraction of Fg pixels under a mask * + *---------------------------------------------------------------------*/ +/*! + * \brief pixFractionFgInMask() + * + * \param[in] pix1 1 bpp + * \param[in] pix2 1 bpp + * \param[out] pfract fraction of fg pixels in 1 that are + * aligned with the fg of 2 + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This gives the fraction of fg pixels in pix1 that are in
+ *          the intersection (i.e., under the fg) of pix2:
+ *          |1 & 2|/|1|, where |...| means the number of fg pixels.
+ *          Note that this is different from the situation where
+ *          pix1 and pix2 are reversed.
+ *      (2) Both pix1 and pix2 are registered to the UL corners.  A warning
+ *          is issued if pix1 and pix2 have different sizes.
+ *      (3) This can also be used to find the fraction of fg pixels in pix1
+ *          that are NOT under the fg of pix2: 1.0 - |1 & 2|/|1|
+ *      (4) If pix1 or pix2 are empty, this returns %fract = 0.0.
+ *      (5) For example, pix2 could be a frame around the outside of the
+ *          image, made from pixMakeFrameMask().
+ * 
+ */ +l_ok +pixFractionFgInMask(PIX *pix1, + PIX *pix2, + l_float32 *pfract) +{ +l_int32 w1, h1, w2, h2, empty, count1, count3; +PIX *pix3; + + PROCNAME("pixFractionFgInMask"); + + if (!pfract) + return ERROR_INT("&fract not defined", procName, 1); + *pfract = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); + + pixGetDimensions(pix1, &w1, &h1, NULL); + pixGetDimensions(pix2, &w2, &h2, NULL); + if (w1 != w2 || h1 != h2) { + L_INFO("sizes unequal: (w1,w2) = (%d,%d), (h1,h2) = (%d,%d)\n", + procName, w1, w2, h1, h2); + } + pixZero(pix1, &empty); + if (empty) return 0; + pixZero(pix2, &empty); + if (empty) return 0; + + pix3 = pixCopy(NULL, pix1); + pixAnd(pix3, pix3, pix2); + pixCountPixels(pix1, &count1, NULL); /* |1| */ + pixCountPixels(pix3, &count3, NULL); /* |1 & 2| */ + *pfract = (l_float32)count3 / (l_float32)count1; + pixDestroy(&pix3); + return 0; +} + + +/*---------------------------------------------------------------------* + * Clip to Foreground * + *---------------------------------------------------------------------*/ +/*! + * \brief pixClipToForeground() + * + * \param[in] pixs 1 bpp + * \param[out] ppixd [optional] clipped pix returned + * \param[out] pbox [optional] bounding box + * \return 0 if OK; 1 on error or if there are no fg pixels + * + *
+ * Notes:
+ *      (1) At least one of {&pixd, &box} must be specified.
+ *      (2) If there are no fg pixels, the returned ptrs are null.
+ * 
+ */ +l_ok +pixClipToForeground(PIX *pixs, + PIX **ppixd, + BOX **pbox) +{ +l_int32 w, h, wpl, nfullwords, extra, i, j; +l_int32 minx, miny, maxx, maxy; +l_uint32 result, mask; +l_uint32 *data, *line; +BOX *box; + + PROCNAME("pixClipToForeground"); + + if (ppixd) *ppixd = NULL; + if (pbox) *pbox = NULL; + if (!ppixd && !pbox) + return ERROR_INT("no output requested", procName, 1); + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + nfullwords = w / 32; + extra = w & 31; + mask = ~rmask32[32 - extra]; + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + + result = 0; + for (i = 0, miny = 0; i < h; i++, miny++) { + line = data + i * wpl; + for (j = 0; j < nfullwords; j++) + result |= line[j]; + if (extra) + result |= (line[j] & mask); + if (result) + break; + } + if (miny == h) /* no ON pixels */ + return 1; + + result = 0; + for (i = h - 1, maxy = h - 1; i >= 0; i--, maxy--) { + line = data + i * wpl; + for (j = 0; j < nfullwords; j++) + result |= line[j]; + if (extra) + result |= (line[j] & mask); + if (result) + break; + } + + minx = 0; + for (j = 0, minx = 0; j < w; j++, minx++) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (GET_DATA_BIT(line, j)) + goto minx_found; + } + } + +minx_found: + for (j = w - 1, maxx = w - 1; j >= 0; j--, maxx--) { + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (GET_DATA_BIT(line, j)) + goto maxx_found; + } + } + +maxx_found: + box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); + + if (ppixd) + *ppixd = pixClipRectangle(pixs, box, NULL); + if (pbox) + *pbox = box; + else + boxDestroy(&box); + + return 0; +} + + +/*! + * \brief pixTestClipToForeground() + * + * \param[in] pixs 1 bpp + * \param[out] pcanclip 1 if fg does not extend to all four edges + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is a lightweight test to determine if a 1 bpp image
+ *          can be further cropped without loss of fg pixels.
+ *          If it cannot, canclip is set to 0.
+ *      (2) It does not test for the existence of any fg pixels.
+ *          If there are no fg pixels, it will return %canclip = 1.
+ *          Check the output of the subsequent call to pixClipToForeground().
+ * 
+ */ +l_ok +pixTestClipToForeground(PIX *pixs, + l_int32 *pcanclip) +{ +l_int32 i, j, w, h, wpl, found; +l_uint32 *data, *line; + + PROCNAME("pixTestClipToForeground"); + + if (!pcanclip) + return ERROR_INT("&canclip not defined", procName, 1); + *pcanclip = 0; + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + /* Check top and bottom raster lines */ + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + found = FALSE; + for (j = 0; found == FALSE && j < w; j++) + found = GET_DATA_BIT(data, j); + if (!found) { + *pcanclip = 1; + return 0; + } + + line = data + (h - 1) * wpl; + found = FALSE; + for (j = 0; found == FALSE && j < w; j++) + found = GET_DATA_BIT(data, j); + if (!found) { + *pcanclip = 1; + return 0; + } + + /* Check left and right edges */ + found = FALSE; + for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++) + found = GET_DATA_BIT(line, 0); + if (!found) { + *pcanclip = 1; + return 0; + } + + found = FALSE; + for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++) + found = GET_DATA_BIT(line, w - 1); + if (!found) + *pcanclip = 1; + + return 0; /* fg pixels found on all edges */ +} + + +/*! + * \brief pixClipBoxToForeground() + * + * \param[in] pixs 1 bpp + * \param[in] boxs [optional] use full image if null + * \param[out] ppixd [optional] clipped pix returned + * \param[out] pboxd [optional] bounding box + * \return 0 if OK; 1 on error or if there are no fg pixels + * + *
+ * Notes:
+ *      (1) At least one of {&pixd, &boxd} must be specified.
+ *      (2) If there are no fg pixels, the returned ptrs are null.
+ *      (3) Do not use &pixs for the 3rd arg or &boxs for the 4th arg;
+ *          this will leak memory.
+ * 
+ */ +l_ok +pixClipBoxToForeground(PIX *pixs, + BOX *boxs, + PIX **ppixd, + BOX **pboxd) +{ +l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; +BOX *boxt, *boxd; + + PROCNAME("pixClipBoxToForeground"); + + if (ppixd) *ppixd = NULL; + if (pboxd) *pboxd = NULL; + if (!ppixd && !pboxd) + return ERROR_INT("no output requested", procName, 1); + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + if (!boxs) + return pixClipToForeground(pixs, ppixd, pboxd); + + pixGetDimensions(pixs, &w, &h, NULL); + boxGetGeometry(boxs, &bx, &by, &bw, &bh); + cbw = L_MIN(bw, w - bx); + cbh = L_MIN(bh, h - by); + if (cbw < 0 || cbh < 0) + return ERROR_INT("box not within image", procName, 1); + boxt = boxCreate(bx, by, cbw, cbh); + + if (pixScanForForeground(pixs, boxt, L_FROM_LEFT, &left)) { + boxDestroy(&boxt); + return 1; + } + pixScanForForeground(pixs, boxt, L_FROM_RIGHT, &right); + pixScanForForeground(pixs, boxt, L_FROM_TOP, &top); + pixScanForForeground(pixs, boxt, L_FROM_BOT, &bottom); + + boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); + if (ppixd) + *ppixd = pixClipRectangle(pixs, boxd, NULL); + if (pboxd) + *pboxd = boxd; + else + boxDestroy(&boxd); + + boxDestroy(&boxt); + return 0; +} + + +/*! + * \brief pixScanForForeground() + * + * \param[in] pixs 1 bpp + * \param[in] box [optional] within which the search is conducted + * \param[in] scanflag direction of scan; e.g., L_FROM_LEFT + * \param[out] ploc location in scan direction of first black pixel + * \return 0 if OK; 1 on error or if no fg pixels are found + * + *
+ * Notes:
+ *      (1) If there are no fg pixels, the position is set to 0.
+ *          Caller must check the return value!
+ *      (2) Use %box == NULL to scan from edge of pixs
+ * 
+ */ +l_ok +pixScanForForeground(PIX *pixs, + BOX *box, + l_int32 scanflag, + l_int32 *ploc) +{ +l_int32 bx, by, bw, bh, x, xstart, xend, y, ystart, yend, wpl; +l_uint32 *data, *line; +BOX *boxt; + + PROCNAME("pixScanForForeground"); + + if (!ploc) + return ERROR_INT("&loc not defined", procName, 1); + *ploc = 0; + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + /* Clip box to pixs if it exists */ + pixGetDimensions(pixs, &bw, &bh, NULL); + if (box) { + if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) + return ERROR_INT("invalid box", procName, 1); + boxGetGeometry(boxt, &bx, &by, &bw, &bh); + boxDestroy(&boxt); + } else { + bx = by = 0; + } + xstart = bx; + ystart = by; + xend = bx + bw - 1; + yend = by + bh - 1; + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (scanflag == L_FROM_LEFT) { + for (x = xstart; x <= xend; x++) { + for (y = ystart; y <= yend; y++) { + line = data + y * wpl; + if (GET_DATA_BIT(line, x)) { + *ploc = x; + return 0; + } + } + } + } else if (scanflag == L_FROM_RIGHT) { + for (x = xend; x >= xstart; x--) { + for (y = ystart; y <= yend; y++) { + line = data + y * wpl; + if (GET_DATA_BIT(line, x)) { + *ploc = x; + return 0; + } + } + } + } else if (scanflag == L_FROM_TOP) { + for (y = ystart; y <= yend; y++) { + line = data + y * wpl; + for (x = xstart; x <= xend; x++) { + if (GET_DATA_BIT(line, x)) { + *ploc = y; + return 0; + } + } + } + } else if (scanflag == L_FROM_BOT) { + for (y = yend; y >= ystart; y--) { + line = data + y * wpl; + for (x = xstart; x <= xend; x++) { + if (GET_DATA_BIT(line, x)) { + *ploc = y; + return 0; + } + } + } + } else { + return ERROR_INT("invalid scanflag", procName, 1); + } + + return 1; /* no fg found */ +} + + +/*! + * \brief pixClipBoxToEdges() + * + * \param[in] pixs 1 bpp + * \param[in] boxs [optional] ; use full image if null + * \param[in] lowthresh threshold to choose clipping location + * \param[in] highthresh threshold required to find an edge + * \param[in] maxwidth max allowed width between low and high thresh locs + * \param[in] factor sampling factor along pixel counting direction + * \param[out] ppixd [optional] clipped pix returned + * \param[out] pboxd [optional] bounding box + * \return 0 if OK; 1 on error or if a fg edge is not found from + * all four sides. + * + *
+ * Notes:
+ *      (1) At least one of {&pixd, &boxd} must be specified.
+ *      (2) If there are no fg pixels, the returned ptrs are null.
+ *      (3) This function attempts to locate rectangular "image" regions
+ *          of high-density fg pixels, that have well-defined edges
+ *          on the four sides.
+ *      (4) Edges are searched for on each side, iterating in order
+ *          from left, right, top and bottom.  As each new edge is
+ *          found, the search box is resized to use that location.
+ *          Once an edge is found, it is held.  If no more edges
+ *          are found in one iteration, the search fails.
+ *      (5) See pixScanForEdge() for usage of the thresholds and %maxwidth.
+ *      (6) The thresholds must be at least 1, and the low threshold
+ *          cannot be larger than the high threshold.
+ *      (7) If the low and high thresholds are both 1, this is equivalent
+ *          to pixClipBoxToForeground().
+ * 
+ */ +l_ok +pixClipBoxToEdges(PIX *pixs, + BOX *boxs, + l_int32 lowthresh, + l_int32 highthresh, + l_int32 maxwidth, + l_int32 factor, + PIX **ppixd, + BOX **pboxd) +{ +l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; +l_int32 lfound, rfound, tfound, bfound, change; +BOX *boxt, *boxd; + + PROCNAME("pixClipBoxToEdges"); + + if (ppixd) *ppixd = NULL; + if (pboxd) *pboxd = NULL; + if (!ppixd && !pboxd) + return ERROR_INT("no output requested", procName, 1); + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (lowthresh < 1 || highthresh < 1 || + lowthresh > highthresh || maxwidth < 1) + return ERROR_INT("invalid thresholds", procName, 1); + factor = L_MIN(1, factor); + + if (lowthresh == 1 && highthresh == 1) + return pixClipBoxToForeground(pixs, boxs, ppixd, pboxd); + + pixGetDimensions(pixs, &w, &h, NULL); + if (boxs) { + boxGetGeometry(boxs, &bx, &by, &bw, &bh); + cbw = L_MIN(bw, w - bx); + cbh = L_MIN(bh, h - by); + if (cbw < 0 || cbh < 0) + return ERROR_INT("box not within image", procName, 1); + boxt = boxCreate(bx, by, cbw, cbh); + } else { + boxt = boxCreate(0, 0, w, h); + } + + lfound = rfound = tfound = bfound = 0; + while (!lfound || !rfound || !tfound || !bfound) { + change = 0; + if (!lfound) { + if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, + factor, L_FROM_LEFT, &left)) { + lfound = 1; + change = 1; + boxRelocateOneSide(boxt, boxt, left, L_FROM_LEFT); + } + } + if (!rfound) { + if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, + factor, L_FROM_RIGHT, &right)) { + rfound = 1; + change = 1; + boxRelocateOneSide(boxt, boxt, right, L_FROM_RIGHT); + } + } + if (!tfound) { + if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, + factor, L_FROM_TOP, &top)) { + tfound = 1; + change = 1; + boxRelocateOneSide(boxt, boxt, top, L_FROM_TOP); + } + } + if (!bfound) { + if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, + factor, L_FROM_BOT, &bottom)) { + bfound = 1; + change = 1; + boxRelocateOneSide(boxt, boxt, bottom, L_FROM_BOT); + } + } + +#if DEBUG_EDGES + fprintf(stderr, "iter: %d %d %d %d\n", lfound, rfound, tfound, bfound); +#endif /* DEBUG_EDGES */ + + if (change == 0) break; + } + boxDestroy(&boxt); + + if (change == 0) + return ERROR_INT("not all edges found", procName, 1); + + boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); + if (ppixd) + *ppixd = pixClipRectangle(pixs, boxd, NULL); + if (pboxd) + *pboxd = boxd; + else + boxDestroy(&boxd); + + return 0; +} + + +/*! + * \brief pixScanForEdge() + * + * \param[in] pixs 1 bpp + * \param[in] box [optional] within which the search is conducted + * \param[in] lowthresh threshold to choose clipping location + * \param[in] highthresh threshold required to find an edge + * \param[in] maxwidth max allowed width between low and high thresh locs + * \param[in] factor sampling factor along pixel counting direction + * \param[in] scanflag direction of scan; e.g., L_FROM_LEFT + * \param[out] ploc location in scan direction of first black pixel + * \return 0 if OK; 1 on error or if the edge is not found + * + *
+ * Notes:
+ *      (1) If there are no fg pixels, the position is set to 0.
+ *          Caller must check the return value!
+ *      (2) Use %box == NULL to scan from edge of pixs
+ *      (3) As the scan progresses, the location where the sum of
+ *          pixels equals or excees %lowthresh is noted (loc).  The
+ *          scan is stopped when the sum of pixels equals or exceeds
+ *          %highthresh.  If the scan distance between loc and that
+ *          point does not exceed %maxwidth, an edge is found and
+ *          its position is taken to be loc.  %maxwidth implicitly
+ *          sets a minimum on the required gradient of the edge.
+ *      (4) The thresholds must be at least 1, and the low threshold
+ *          cannot be larger than the high threshold.
+ * 
+ */ +l_ok +pixScanForEdge(PIX *pixs, + BOX *box, + l_int32 lowthresh, + l_int32 highthresh, + l_int32 maxwidth, + l_int32 factor, + l_int32 scanflag, + l_int32 *ploc) +{ +l_int32 bx, by, bw, bh, foundmin, loc, sum, wpl; +l_int32 x, xstart, xend, y, ystart, yend; +l_uint32 *data, *line; +BOX *boxt; + + PROCNAME("pixScanForEdge"); + + if (!ploc) + return ERROR_INT("&ploc not defined", procName, 1); + *ploc = 0; + if (!pixs || (pixGetDepth(pixs) != 1)) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (lowthresh < 1 || highthresh < 1 || + lowthresh > highthresh || maxwidth < 1) + return ERROR_INT("invalid thresholds", procName, 1); + factor = L_MIN(1, factor); + + /* Clip box to pixs if it exists */ + pixGetDimensions(pixs, &bw, &bh, NULL); + if (box) { + if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) + return ERROR_INT("invalid box", procName, 1); + boxGetGeometry(boxt, &bx, &by, &bw, &bh); + boxDestroy(&boxt); + } else { + bx = by = 0; + } + xstart = bx; + ystart = by; + xend = bx + bw - 1; + yend = by + bh - 1; + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + foundmin = 0; + if (scanflag == L_FROM_LEFT) { + for (x = xstart; x <= xend; x++) { + sum = 0; + for (y = ystart; y <= yend; y += factor) { + line = data + y * wpl; + if (GET_DATA_BIT(line, x)) + sum++; + } + if (!foundmin && sum < lowthresh) + continue; + if (!foundmin) { /* save the loc of the beginning of the edge */ + foundmin = 1; + loc = x; + } + if (sum >= highthresh) { +#if DEBUG_EDGES + fprintf(stderr, "Left: x = %d, loc = %d\n", x, loc); +#endif /* DEBUG_EDGES */ + if (x - loc < maxwidth) { + *ploc = loc; + return 0; + } else { + return 1; + } + } + } + } else if (scanflag == L_FROM_RIGHT) { + for (x = xend; x >= xstart; x--) { + sum = 0; + for (y = ystart; y <= yend; y += factor) { + line = data + y * wpl; + if (GET_DATA_BIT(line, x)) + sum++; + } + if (!foundmin && sum < lowthresh) + continue; + if (!foundmin) { + foundmin = 1; + loc = x; + } + if (sum >= highthresh) { +#if DEBUG_EDGES + fprintf(stderr, "Right: x = %d, loc = %d\n", x, loc); +#endif /* DEBUG_EDGES */ + if (loc - x < maxwidth) { + *ploc = loc; + return 0; + } else { + return 1; + } + } + } + } else if (scanflag == L_FROM_TOP) { + for (y = ystart; y <= yend; y++) { + sum = 0; + line = data + y * wpl; + for (x = xstart; x <= xend; x += factor) { + if (GET_DATA_BIT(line, x)) + sum++; + } + if (!foundmin && sum < lowthresh) + continue; + if (!foundmin) { + foundmin = 1; + loc = y; + } + if (sum >= highthresh) { +#if DEBUG_EDGES + fprintf(stderr, "Top: y = %d, loc = %d\n", y, loc); +#endif /* DEBUG_EDGES */ + if (y - loc < maxwidth) { + *ploc = loc; + return 0; + } else { + return 1; + } + } + } + } else if (scanflag == L_FROM_BOT) { + for (y = yend; y >= ystart; y--) { + sum = 0; + line = data + y * wpl; + for (x = xstart; x <= xend; x += factor) { + if (GET_DATA_BIT(line, x)) + sum++; + } + if (!foundmin && sum < lowthresh) + continue; + if (!foundmin) { + foundmin = 1; + loc = y; + } + if (sum >= highthresh) { +#if DEBUG_EDGES + fprintf(stderr, "Bottom: y = %d, loc = %d\n", y, loc); +#endif /* DEBUG_EDGES */ + if (loc - y < maxwidth) { + *ploc = loc; + return 0; + } else { + return 1; + } + } + } + } else { + return ERROR_INT("invalid scanflag", procName, 1); + } + + return 1; /* edge not found */ +} + + +/*---------------------------------------------------------------------* + * Extract pixel averages and reversals along lines * + *---------------------------------------------------------------------*/ +/*! + * \brief pixExtractOnLine() + * + * \param[in] pixs 1 bpp or 8 bpp; no colormap + * \param[in] x1, y1 one end point for line + * \param[in] x2, y2 another end pt for line + * \param[in] factor sampling; >= 1 + * \return na of pixel values along line, or NULL on error. + * + *
+ * Notes:
+ *      (1) Input end points are clipped to the pix.
+ *      (2) If the line is either horizontal, or closer to horizontal
+ *          than to vertical, the points will be extracted from left
+ *          to right in the pix.  Likewise, if the line is vertical,
+ *          or closer to vertical than to horizontal, the points will
+ *          be extracted from top to bottom.
+ *      (3) Can be used with numaCountReverals(), for example, to
+ *          characterize the intensity smoothness along a line.
+ * 
+ */ +NUMA * +pixExtractOnLine(PIX *pixs, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 factor) +{ +l_int32 i, w, h, d, xmin, ymin, xmax, ymax, npts, direction; +l_uint32 val; +l_float32 x, y; +l_float64 slope; +NUMA *na; +PTA *pta; + + PROCNAME("pixExtractOnLine"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8) + return (NUMA *)ERROR_PTR("d not 1 or 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (NUMA *)ERROR_PTR("pixs has a colormap", procName, NULL); + if (factor < 1) { + L_WARNING("factor must be >= 1; setting to 1\n", procName); + factor = 1; + } + + /* Clip line to the image */ + x1 = L_MAX(0, L_MIN(x1, w - 1)); + x2 = L_MAX(0, L_MIN(x2, w - 1)); + y1 = L_MAX(0, L_MIN(y1, h - 1)); + y2 = L_MAX(0, L_MIN(y2, h - 1)); + + if (x1 == x2 && y1 == y2) { + pixGetPixel(pixs, x1, y1, &val); + na = numaCreate(1); + numaAddNumber(na, val); + return na; + } + + if (y1 == y2) + direction = L_HORIZONTAL_LINE; + else if (x1 == x2) + direction = L_VERTICAL_LINE; + else + direction = L_OBLIQUE_LINE; + + na = numaCreate(0); + if (direction == L_HORIZONTAL_LINE) { /* plot against x */ + xmin = L_MIN(x1, x2); + xmax = L_MAX(x1, x2); + numaSetParameters(na, xmin, factor); + for (i = xmin; i <= xmax; i += factor) { + pixGetPixel(pixs, i, y1, &val); + numaAddNumber(na, val); + } + } else if (direction == L_VERTICAL_LINE) { /* plot against y */ + ymin = L_MIN(y1, y2); + ymax = L_MAX(y1, y2); + numaSetParameters(na, ymin, factor); + for (i = ymin; i <= ymax; i += factor) { + pixGetPixel(pixs, x1, i, &val); + numaAddNumber(na, val); + } + } else { /* direction == L_OBLIQUE_LINE */ + slope = (l_float64)((y2 - y1) / (x2 - x1)); + if (L_ABS(slope) < 1.0) { /* quasi-horizontal */ + xmin = L_MIN(x1, x2); + xmax = L_MAX(x1, x2); + ymin = (xmin == x1) ? y1 : y2; /* pt that goes with xmin */ + ymax = (ymin == y1) ? y2 : y1; /* pt that goes with xmax */ + pta = generatePtaLine(xmin, ymin, xmax, ymax); + numaSetParameters(na, xmin, (l_float32)factor); + } else { /* quasi-vertical */ + ymin = L_MIN(y1, y2); + ymax = L_MAX(y1, y2); + xmin = (ymin == y1) ? x1 : x2; /* pt that goes with ymin */ + xmax = (xmin == x1) ? x2 : x1; /* pt that goes with ymax */ + pta = generatePtaLine(xmin, ymin, xmax, ymax); + numaSetParameters(na, ymin, (l_float32)factor); + } + npts = ptaGetCount(pta); + for (i = 0; i < npts; i += factor) { + ptaGetPt(pta, i, &x, &y); + pixGetPixel(pixs, (l_int32)x, (l_int32)y, &val); + numaAddNumber(na, val); + } + +#if 0 /* debugging */ + pixPlotAlongPta(pixs, pta, GPLOT_PNG, NULL); +#endif + + ptaDestroy(&pta); + } + + return na; +} + + +/*! + * \brief pixAverageOnLine() + * + * \param[in] pixs 1 bpp or 8 bpp; no colormap + * \param[in] x1, y1 starting pt for line + * \param[in] x2, y2 end pt for line + * \param[in] factor sampling; >= 1 + * \return average of pixel values along line, or NULL on error. + * + *
+ * Notes:
+ *      (1) The line must be either horizontal or vertical, so either
+ *          y1 == y2 (horizontal) or x1 == x2 (vertical).
+ *      (2) If horizontal, x1 must be <= x2.
+ *          If vertical, y1 must be <= y2.
+ *          characterize the intensity smoothness along a line.
+ *      (3) Input end points are clipped to the pix.
+ * 
+ */ +l_float32 +pixAverageOnLine(PIX *pixs, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 factor) +{ +l_int32 i, j, w, h, d, direction, count, wpl; +l_uint32 *data, *line; +l_float32 sum; + + PROCNAME("pixAverageOnLine"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8) + return ERROR_INT("d not 1 or 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs has a colormap", procName, 1); + if (x1 > x2 || y1 > y2) + return ERROR_INT("x1 > x2 or y1 > y2", procName, 1); + + if (y1 == y2) { + x1 = L_MAX(0, x1); + x2 = L_MIN(w - 1, x2); + y1 = L_MAX(0, L_MIN(y1, h - 1)); + direction = L_HORIZONTAL_LINE; + } else if (x1 == x2) { + y1 = L_MAX(0, y1); + y2 = L_MIN(h - 1, y2); + x1 = L_MAX(0, L_MIN(x1, w - 1)); + direction = L_VERTICAL_LINE; + } else { + return ERROR_INT("line neither horiz nor vert", procName, 1); + } + + if (factor < 1) { + L_WARNING("factor must be >= 1; setting to 1\n", procName); + factor = 1; + } + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + sum = 0; + count = 0; + if (direction == L_HORIZONTAL_LINE) { + line = data + y1 * wpl; + for (j = x1, count = 0; j <= x2; count++, j += factor) { + if (d == 1) + sum += GET_DATA_BIT(line, j); + else /* d == 8 */ + sum += GET_DATA_BYTE(line, j); + } + } else if (direction == L_VERTICAL_LINE) { + for (i = y1, count = 0; i <= y2; count++, i += factor) { + line = data + i * wpl; + if (d == 1) + sum += GET_DATA_BIT(line, x1); + else /* d == 8 */ + sum += GET_DATA_BYTE(line, x1); + } + } + + return sum / (l_float32)count; +} + + +/*! + * \brief pixAverageIntensityProfile() + * + * \param[in] pixs any depth; colormap OK + * \param[in] fract fraction of image width or height to be used + * \param[in] dir averaging direction: L_HORIZONTAL_LINE or + * L_VERTICAL_LINE + * \param[in] first, last span of rows or columns to measure + * \param[in] factor1 sampling along fast scan direction; >= 1 + * \param[in] factor2 sampling along slow scan direction; >= 1 + * \return na of reversal profile, or NULL on error. + * + *
+ * Notes:
+ *      (1) If d != 1 bpp, colormaps are removed and the result
+ *          is converted to 8 bpp.
+ *      (2) If %dir == L_HORIZONTAL_LINE, the intensity is averaged
+ *          along each horizontal raster line (sampled by %factor1),
+ *          and the profile is the array of these averages in the
+ *          vertical direction between %first and %last raster lines,
+ *          and sampled by %factor2.
+ *      (3) If %dir == L_VERTICAL_LINE, the intensity is averaged
+ *          along each vertical line (sampled by %factor1),
+ *          and the profile is the array of these averages in the
+ *          horizontal direction between %first and %last columns,
+ *          and sampled by %factor2.
+ *      (4) The averages are measured over the central %fract of the image.
+ *          Use %fract == 1.0 to average across the entire width or height.
+ * 
+ */ +NUMA * +pixAverageIntensityProfile(PIX *pixs, + l_float32 fract, + l_int32 dir, + l_int32 first, + l_int32 last, + l_int32 factor1, + l_int32 factor2) +{ +l_int32 i, j, w, h, d, start, end; +l_float32 ave; +NUMA *nad; +PIX *pixr, *pixg; + + PROCNAME("pixAverageIntensityProfile"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (fract < 0.0 || fract > 1.0) + return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); + if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) + return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); + if (first < 0) first = 0; + if (last < first) + return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); + if (factor1 < 1) { + L_WARNING("factor1 must be >= 1; setting to 1\n", procName); + factor1 = 1; + } + if (factor2 < 1) { + L_WARNING("factor2 must be >= 1; setting to 1\n", procName); + factor2 = 1; + } + + /* Use 1 or 8 bpp, without colormap */ + if (pixGetColormap(pixs)) + pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixr = pixClone(pixs); + pixGetDimensions(pixr, &w, &h, &d); + if (d == 1) + pixg = pixClone(pixr); + else + pixg = pixConvertTo8(pixr, 0); + + nad = numaCreate(0); /* output: samples in slow scan direction */ + numaSetParameters(nad, 0, factor2); + if (dir == L_HORIZONTAL_LINE) { + start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); + end = w - start; + if (last > h - 1) { + L_WARNING("last > h - 1; clipping\n", procName); + last = h - 1; + } + for (i = first; i <= last; i += factor2) { + ave = pixAverageOnLine(pixg, start, i, end, i, factor1); + numaAddNumber(nad, ave); + } + } else if (dir == L_VERTICAL_LINE) { + start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); + end = h - start; + if (last > w - 1) { + L_WARNING("last > w - 1; clipping\n", procName); + last = w - 1; + } + for (j = first; j <= last; j += factor2) { + ave = pixAverageOnLine(pixg, j, start, j, end, factor1); + numaAddNumber(nad, ave); + } + } + + pixDestroy(&pixr); + pixDestroy(&pixg); + return nad; +} + + +/*! + * \brief pixReversalProfile() + * + * \param[in] pixs any depth; colormap OK + * \param[in] fract fraction of image width or height to be used + * \param[in] dir profile direction: L_HORIZONTAL_LINE or + * L_VERTICAL_LINE + * \param[in] first, last span of rows or columns to measure + * \param[in] minreversal minimum change in intensity to trigger a reversal + * \param[in] factor1 sampling along raster line (fast scan); >= 1 + * \param[in] factor2 sampling of raster lines (slow scan); >= 1 + * \return na of reversal profile, or NULL on error. + * + *
+ * Notes:
+ *      (1) If d != 1 bpp, colormaps are removed and the result
+ *          is converted to 8 bpp.
+ *      (2) If %dir == L_HORIZONTAL_LINE, the the reversals are counted
+ *          along each horizontal raster line (sampled by %factor1),
+ *          and the profile is the array of these sums in the
+ *          vertical direction between %first and %last raster lines,
+ *          and sampled by %factor2.
+ *      (3) If %dir == L_VERTICAL_LINE, the the reversals are counted
+ *          along each vertical column (sampled by %factor1),
+ *          and the profile is the array of these sums in the
+ *          horizontal direction between %first and %last columns,
+ *          and sampled by %factor2.
+ *      (4) For each row or column, the reversals are summed over the
+ *          central %fract of the image.  Use %fract == 1.0 to sum
+ *          across the entire width (of row) or height (of column).
+ *      (5) %minreversal is the relative change in intensity that is
+ *          required to resolve peaks and valleys.  A typical number for
+ *          locating text in 8 bpp might be 50.  For 1 bpp, minreversal
+ *          must be 1.
+ *      (6) The reversal profile is simply the number of reversals
+ *          in a row or column, vs the row or column index.
+ * 
+ */ +NUMA * +pixReversalProfile(PIX *pixs, + l_float32 fract, + l_int32 dir, + l_int32 first, + l_int32 last, + l_int32 minreversal, + l_int32 factor1, + l_int32 factor2) +{ +l_int32 i, j, w, h, d, start, end, nr; +NUMA *naline, *nad; +PIX *pixr, *pixg; + + PROCNAME("pixReversalProfile"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (fract < 0.0 || fract > 1.0) + return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); + if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) + return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); + if (first < 0) first = 0; + if (last < first) + return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); + if (factor1 < 1) { + L_WARNING("factor1 must be >= 1; setting to 1\n", procName); + factor1 = 1; + } + if (factor2 < 1) { + L_WARNING("factor2 must be >= 1; setting to 1\n", procName); + factor2 = 1; + } + + /* Use 1 or 8 bpp, without colormap */ + if (pixGetColormap(pixs)) + pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixr = pixClone(pixs); + pixGetDimensions(pixr, &w, &h, &d); + if (d == 1) { + pixg = pixClone(pixr); + minreversal = 1; /* enforce this */ + } else { + pixg = pixConvertTo8(pixr, 0); + } + + nad = numaCreate(0); /* output: samples in slow scan direction */ + numaSetParameters(nad, 0, factor2); + if (dir == L_HORIZONTAL_LINE) { + start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); + end = w - start; + if (last > h - 1) { + L_WARNING("last > h - 1; clipping\n", procName); + last = h - 1; + } + for (i = first; i <= last; i += factor2) { + naline = pixExtractOnLine(pixg, start, i, end, i, factor1); + numaCountReversals(naline, minreversal, &nr, NULL); + numaAddNumber(nad, nr); + numaDestroy(&naline); + } + } else if (dir == L_VERTICAL_LINE) { + start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); + end = h - start; + if (last > w - 1) { + L_WARNING("last > w - 1; clipping\n", procName); + last = w - 1; + } + for (j = first; j <= last; j += factor2) { + naline = pixExtractOnLine(pixg, j, start, j, end, factor1); + numaCountReversals(naline, minreversal, &nr, NULL); + numaAddNumber(nad, nr); + numaDestroy(&naline); + } + } + + pixDestroy(&pixr); + pixDestroy(&pixg); + return nad; +} + + +/*---------------------------------------------------------------------* + * Extract windowed variance along a line * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWindowedVarianceOnLine() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] dir L_HORIZONTAL_LINE or L_VERTICAL_LINE + * \param[in] loc location of the constant coordinate for the line + * \param[in] c1, c2 end point coordinates for the line + * \param[in] size window size; must be > 1 + * \param[out] pnad windowed square root of variance + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The returned variance array traverses the line starting
+ *          from the smallest coordinate, min(c1,c2).
+ *      (2) Line end points are clipped to pixs.
+ *      (3) The reference point for the variance calculation is the center of
+ *          the window.  Therefore, the numa start parameter from
+ *          pixExtractOnLine() is incremented by %size/2,
+ *          to align the variance values with the pixel coordinate.
+ *      (4) The square root of the variance is the RMS deviation from the mean.
+ * 
+ */ +l_ok +pixWindowedVarianceOnLine(PIX *pixs, + l_int32 dir, + l_int32 loc, + l_int32 c1, + l_int32 c2, + l_int32 size, + NUMA **pnad) +{ +l_int32 i, j, w, h, cmin, cmax, maxloc, n, x, y; +l_uint32 val; +l_float32 norm, rootvar; +l_float32 *array; +l_float64 sum1, sum2, ave, var; +NUMA *na1, *nad; +PTA *pta; + + PROCNAME("pixWindowedVarianceOnLine"); + + if (!pnad) + return ERROR_INT("&nad not defined", procName, 1); + *pnad = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8bpp", procName, 1); + if (size < 2) + return ERROR_INT("window size must be > 1", procName, 1); + if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) + return ERROR_INT("invalid direction", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + maxloc = (dir == L_HORIZONTAL_LINE) ? h - 1 : w - 1; + if (loc < 0 || loc > maxloc) + return ERROR_INT("invalid line position", procName, 1); + + /* Clip line to the image */ + cmin = L_MIN(c1, c2); + cmax = L_MAX(c1, c2); + maxloc = (dir == L_HORIZONTAL_LINE) ? w - 1 : h - 1; + cmin = L_MAX(0, L_MIN(cmin, maxloc)); + cmax = L_MAX(0, L_MIN(cmax, maxloc)); + n = cmax - cmin + 1; + + /* Generate pta along the line */ + pta = ptaCreate(n); + if (dir == L_HORIZONTAL_LINE) { + for (i = cmin; i <= cmax; i++) + ptaAddPt(pta, i, loc); + } else { /* vertical line */ + for (i = cmin; i <= cmax; i++) + ptaAddPt(pta, loc, i); + } + + /* Get numa of pixel values on the line */ + na1 = numaCreate(n); + numaSetParameters(na1, cmin, 1); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + pixGetPixel(pixs, x, y, &val); + numaAddNumber(na1, val); + } + array = numaGetFArray(na1, L_NOCOPY); + ptaDestroy(&pta); + + /* Compute root variance on overlapping windows */ + nad = numaCreate(n); + *pnad = nad; + numaSetParameters(nad, cmin + size / 2, 1); + norm = 1.0 / (l_float32)size; + for (i = 0; i < n - size; i++) { /* along the line */ + sum1 = sum2 = 0; + for (j = 0; j < size; j++) { /* over the window */ + val = array[i + j]; + sum1 += val; + sum2 += (l_float64)(val) * val; + } + ave = norm * sum1; + var = norm * sum2 - ave * ave; + rootvar = (l_float32)sqrt(var); + numaAddNumber(nad, rootvar); + } + + numaDestroy(&na1); + return 0; +} + + +/*---------------------------------------------------------------------* + * Extract min/max of pixel values near lines * + *---------------------------------------------------------------------*/ +/*! + * \brief pixMinMaxNearLine() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] x1, y1 starting pt for line + * \param[in] x2, y2 end pt for line + * \param[in] dist distance to search from line in each direction + * \param[in] direction L_SCAN_NEGATIVE, L_SCAN_POSITIVE, L_SCAN_BOTH + * \param[out] pnamin [optional] minimum values + * \param[out] pnamax [optional] maximum values + * \param[out] pminave [optional] average of minimum values + * \param[out] pmaxave [optional] average of maximum values + * \return 0 if OK; 1 on error or if there are no sampled points + * within the image. + * + *
+ * Notes:
+ *      (1) If the line is more horizontal than vertical, the values
+ *          are computed for [x1, x2], and the pixels are taken
+ *          below and/or above the local y-value.  Otherwise, the
+ *          values are computed for [y1, y2] and the pixels are taken
+ *          to the left and/or right of the local x value.
+ *      (2) %direction specifies which side (or both sides) of the
+ *          line are scanned for min and max values.
+ *      (3) There are two ways to tell if the returned values of min
+ *          and max averages are valid: the returned values cannot be
+ *          negative and the function must return 0.
+ *      (4) All accessed pixels are clipped to the pix.
+ * 
+ */ +l_ok +pixMinMaxNearLine(PIX *pixs, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2, + l_int32 dist, + l_int32 direction, + NUMA **pnamin, + NUMA **pnamax, + l_float32 *pminave, + l_float32 *pmaxave) +{ +l_int32 i, j, w, h, d, x, y, n, dir, found, minval, maxval, negloc, posloc; +l_uint32 val; +l_float32 sum; +NUMA *namin, *namax; +PTA *pta; + + PROCNAME("pixMinMaxNearLine"); + + if (pnamin) *pnamin = NULL; + if (pnamax) *pnamax = NULL; + if (pminave) *pminave = UNDEF; + if (pmaxave) *pmaxave = UNDEF; + if (!pnamin && !pnamax && !pminave && !pmaxave) + return ERROR_INT("no output requested", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 || pixGetColormap(pixs)) + return ERROR_INT("pixs not 8 bpp or has colormap", procName, 1); + dist = L_ABS(dist); + if (direction != L_SCAN_NEGATIVE && direction != L_SCAN_POSITIVE && + direction != L_SCAN_BOTH) + return ERROR_INT("invalid direction", procName, 1); + + pta = generatePtaLine(x1, y1, x2, y2); + n = ptaGetCount(pta); + dir = (L_ABS(x1 - x2) == n - 1) ? L_HORIZ : L_VERT; + namin = numaCreate(n); + namax = numaCreate(n); + negloc = -dist; + posloc = dist; + if (direction == L_SCAN_NEGATIVE) + posloc = 0; + else if (direction == L_SCAN_POSITIVE) + negloc = 0; + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + minval = 255; + maxval = 0; + found = FALSE; + if (dir == L_HORIZ) { + if (x < 0 || x >= w) continue; + for (j = negloc; j <= posloc; j++) { + if (y + j < 0 || y + j >= h) continue; + pixGetPixel(pixs, x, y + j, &val); + found = TRUE; + if (val < minval) minval = val; + if (val > maxval) maxval = val; + } + } else { /* dir == L_VERT */ + if (y < 0 || y >= h) continue; + for (j = negloc; j <= posloc; j++) { + if (x + j < 0 || x + j >= w) continue; + pixGetPixel(pixs, x + j, y, &val); + found = TRUE; + if (val < minval) minval = val; + if (val > maxval) maxval = val; + } + } + if (found) { + numaAddNumber(namin, minval); + numaAddNumber(namax, maxval); + } + } + + n = numaGetCount(namin); + if (n == 0) { + numaDestroy(&namin); + numaDestroy(&namax); + ptaDestroy(&pta); + return ERROR_INT("no output from this line", procName, 1); + } + + if (pminave) { + numaGetSum(namin, &sum); + *pminave = sum / n; + } + if (pmaxave) { + numaGetSum(namax, &sum); + *pmaxave = sum / n; + } + if (pnamin) + *pnamin = namin; + else + numaDestroy(&namin); + if (pnamax) + *pnamax = namax; + else + numaDestroy(&namax); + ptaDestroy(&pta); + return 0; +} + + +/*---------------------------------------------------------------------* + * Rank row and column transforms * + *---------------------------------------------------------------------*/ +/*! + * \brief pixRankRowTransform() + * + * \param[in] pixs 8 bpp; no colormap + * \return pixd with pixels sorted in each row, from + * min to max value + * + *
+ * Notes:
+ *     (1) The time is O(n) in the number of pixels and runs about
+ *         100 Mpixels/sec on a 3 GHz machine.
+ * 
+ */ +PIX * +pixRankRowTransform(PIX *pixs) +{ +l_int32 i, j, k, m, w, h, wpl, val; +l_int32 histo[256]; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixRankRowTransform"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + memset(histo, 0, 1024); + lines = datas + i * wpl; + lined = datad + i * wpl; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + histo[val]++; + } + for (m = 0, j = 0; m < 256; m++) { + for (k = 0; k < histo[m]; k++, j++) + SET_DATA_BYTE(lined, j, m); + } + } + + return pixd; +} + + +/*! + * \brief pixRankColumnTransform() + * + * \param[in] pixs 8 bpp; no colormap + * \return pixd with pixels sorted in each column, from + * min to max value + * + *
+ * Notes:
+ *     (1) The time is O(n) in the number of pixels and runs about
+ *         50 Mpixels/sec on a 3 GHz machine.
+ * 
+ */ +PIX * +pixRankColumnTransform(PIX *pixs) +{ +l_int32 i, j, k, m, w, h, val; +l_int32 histo[256]; +void **lines8, **lined8; +PIX *pixd; + + PROCNAME("pixRankColumnTransform"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreateTemplate(pixs); + lines8 = pixGetLinePtrs(pixs, NULL); + lined8 = pixGetLinePtrs(pixd, NULL); + for (j = 0; j < w; j++) { + memset(histo, 0, 1024); + for (i = 0; i < h; i++) { + val = GET_DATA_BYTE(lines8[i], j); + histo[val]++; + } + for (m = 0, i = 0; m < 256; m++) { + for (k = 0; k < histo[m]; k++, i++) + SET_DATA_BYTE(lined8[i], j, m); + } + } + + LEPT_FREE(lines8); + LEPT_FREE(lined8); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixabasic.c b/hgdriver/3rdparty/hgOCR/leptonica/pixabasic.c new file mode 100644 index 0000000..007550d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixabasic.c @@ -0,0 +1,3243 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + /*! + * \file pixabasic.c + *
+  *
+  *      Pixa creation, destruction, copying
+  *           PIXA     *pixaCreate()
+  *           PIXA     *pixaCreateFromPix()
+  *           PIXA     *pixaCreateFromBoxa()
+  *           PIXA     *pixaSplitPix()
+  *           void      pixaDestroy()
+  *           PIXA     *pixaCopy()
+  *
+  *      Pixa addition
+  *           l_int32   pixaAddPix()
+  *           l_int32   pixaAddBox()
+  *           static l_int32   pixaExtendArray()
+  *           l_int32   pixaExtendArrayToSize()
+  *
+  *      Pixa accessors
+  *           l_int32   pixaGetCount()
+  *           l_int32   pixaChangeRefcount()
+  *           PIX      *pixaGetPix()
+  *           l_int32   pixaGetPixDimensions()
+  *           BOXA     *pixaGetBoxa()
+  *           l_int32   pixaGetBoxaCount()
+  *           BOX      *pixaGetBox()
+  *           l_int32   pixaGetBoxGeometry()
+  *           l_int32   pixaSetBoxa()
+  *           PIX     **pixaGetPixArray()
+  *           l_int32   pixaVerifyDepth()
+  *           l_int32   pixaVerifyDimensions()
+  *           l_int32   pixaIsFull()
+  *           l_int32   pixaCountText()
+  *           l_int32   pixaSetText()
+  *           void   ***pixaGetLinePtrs()
+  *
+  *      Pixa output info
+  *           l_int32   pixaWriteStreamInfo()
+  *
+  *      Pixa array modifiers
+  *           l_int32   pixaReplacePix()
+  *           l_int32   pixaInsertPix()
+  *           l_int32   pixaRemovePix()
+  *           l_int32   pixaRemovePixAndSave()
+  *           l_int32   pixaRemoveSelected()
+  *           l_int32   pixaInitFull()
+  *           l_int32   pixaClear()
+  *
+  *      Pixa and Pixaa combination
+  *           l_int32   pixaJoin()
+  *           PIXA     *pixaInterleave()
+  *           l_int32   pixaaJoin()
+  *
+  *      Pixaa creation, destruction
+  *           PIXAA    *pixaaCreate()
+  *           PIXAA    *pixaaCreateFromPixa()
+  *           void      pixaaDestroy()
+  *
+  *      Pixaa addition
+  *           l_int32   pixaaAddPixa()
+  *           l_int32   pixaaExtendArray()
+  *           l_int32   pixaaAddPix()
+  *           l_int32   pixaaAddBox()
+  *
+  *      Pixaa accessors
+  *           l_int32   pixaaGetCount()
+  *           PIXA     *pixaaGetPixa()
+  *           BOXA     *pixaaGetBoxa()
+  *           PIX      *pixaaGetPix()
+  *           l_int32   pixaaVerifyDepth()
+  *           l_int32   pixaaVerifyDimensions()
+  *           l_int32   pixaaIsFull()
+  *
+  *      Pixaa array modifiers
+  *           l_int32   pixaaInitFull()
+  *           l_int32   pixaaReplacePixa()
+  *           l_int32   pixaaClear()
+  *           l_int32   pixaaTruncate()
+  *
+  *      Pixa serialized I/O  (requires png support)
+  *           PIXA     *pixaRead()
+  *           PIXA     *pixaReadStream()
+  *           PIXA     *pixaReadMem()
+  *           l_int32   pixaWriteDebug()
+  *           l_int32   pixaWrite()
+  *           l_int32   pixaWriteStream()
+  *           l_int32   pixaWriteMem()
+  *           PIXA     *pixaReadBoth()
+  *
+  *      Pixaa serialized I/O  (requires png support)
+  *           PIXAA    *pixaaReadFromFiles()
+  *           PIXAA    *pixaaRead()
+  *           PIXAA    *pixaaReadStream()
+  *           PIXAA    *pixaaReadMem()
+  *           l_int32   pixaaWrite()
+  *           l_int32   pixaaWriteStream()
+  *           l_int32   pixaaWriteMem()
+  *
+  *
+  *   Important note on reference counting:
+  *     Reference counting for the Pixa is analogous to that for the Boxa.
+  *     See pix.h for details.   pixaCopy() provides three possible modes
+  *     of copy.  The basic rule is that however a Pixa is obtained
+  *     (e.g., from pixaCreate*(), pixaCopy(), or a Pixaa accessor),
+  *     it is necessary to call pixaDestroy() on it.
+  * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 pixaExtendArray(PIXA *pixa); + +/*---------------------------------------------------------------------* + * Pixa creation, destruction, copy * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaCreate() + * + * \param[in] n initial number of ptrs + * \return pixa, or NULL on error + * + *
+  * Notes:
+  *      (1) This creates an empty boxa.
+  * 
+ */ +PIXA * +pixaCreate(l_int32 n) +{ + PIXA *pixa; + + PROCNAME("pixaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + pixa = (PIXA *)LEPT_CALLOC(1, sizeof(PIXA)); + pixa->n = 0; + pixa->nalloc = n; + pixa->refcount = 1; + pixa->pix = (PIX **)LEPT_CALLOC(n, sizeof(PIX *)); + pixa->boxa = boxaCreate(n); + if (!pixa->pix || !pixa->boxa) { + pixaDestroy(&pixa); + return (PIXA *)ERROR_PTR("pix or boxa not made", procName, NULL); + } + return pixa; +} + + +/*! + * \brief pixaCreateFromPix() + * + * \param[in] pixs with individual components on a lattice + * \param[in] n number of components + * \param[in] cellw width of each cell + * \param[in] cellh height of each cell + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) For bpp = 1, we truncate each retrieved pix to the ON
+ *          pixels, which we assume for now start at (0,0)
+ * 
+ */ +PIXA * +pixaCreateFromPix(PIX *pixs, + l_int32 n, + l_int32 cellw, + l_int32 cellh) +{ + l_int32 w, h, d, nw, nh, i, j, index; + PIX *pix1, *pix2; + PIXA *pixa; + + PROCNAME("pixaCreateFromPix"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (n <= 0) + return (PIXA *)ERROR_PTR("n must be > 0", procName, NULL); + + if ((pixa = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if ((pix1 = pixCreate(cellw, cellh, d)) == NULL) { + pixaDestroy(&pixa); + return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); + } + + nw = (w + cellw - 1) / cellw; + nh = (h + cellh - 1) / cellh; + for (i = 0, index = 0; i < nh; i++) { + for (j = 0; j < nw && index < n; j++, index++) { + pixRasterop(pix1, 0, 0, cellw, cellh, PIX_SRC, pixs, + j * cellw, i * cellh); + if (d == 1 && !pixClipToForeground(pix1, &pix2, NULL)) + pixaAddPix(pixa, pix2, L_INSERT); + else + pixaAddPix(pixa, pix1, L_COPY); + } + } + + pixDestroy(&pix1); + return pixa; +} + + +/*! + * \brief pixaCreateFromBoxa() + * + * \param[in] pixs + * \param[in] boxa + * \param[in] start first box to use + * \param[in] num number of boxes; use 0 to go to the end + * \param[out] pcropwarn [optional] TRUE if the boxa extent + * is larger than pixs. + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) This simply extracts from pixs the region corresponding to each
+ *          box in the boxa.  To extract all the regions, set both %start
+ *          and %num to 0.
+ *      (2) The 5th arg is optional.  If the extent of the boxa exceeds the
+ *          size of the pixa, so that some boxes are either clipped
+ *          or entirely outside the pix, a warning is returned as TRUE.
+ *      (3) pixad will have only the properly clipped elements, and
+ *          the internal boxa will be correct.
+ * 
+ */ +PIXA * +pixaCreateFromBoxa(PIX *pixs, + BOXA *boxa, + l_int32 start, + l_int32 num, + l_int32 *pcropwarn) +{ + l_int32 i, n, end, w, h, wbox, hbox, cropwarn; + BOX *box, *boxc; + PIX *pixd; + PIXA *pixad; + + PROCNAME("pixaCreateFromBoxa"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa) + return (PIXA *)ERROR_PTR("boxa not defined", procName, NULL); + if (num < 0) + return (PIXA *)ERROR_PTR("num must be >= 0", procName, NULL); + + n = boxaGetCount(boxa); + end = (num == 0) ? n - 1 : L_MIN(start + num - 1, n - 1); + if ((pixad = pixaCreate(end - start + 1)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + + boxaGetExtent(boxa, &wbox, &hbox, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + cropwarn = FALSE; + if (wbox > w || hbox > h) + cropwarn = TRUE; + if (pcropwarn) + *pcropwarn = cropwarn; + + for (i = start; i <= end; i++) { + box = boxaGetBox(boxa, i, L_COPY); + if (cropwarn) { /* if box is outside pixs, pixd is NULL */ + pixd = pixClipRectangle(pixs, box, &boxc); /* may be NULL */ + if (pixd) { + pixaAddPix(pixad, pixd, L_INSERT); + pixaAddBox(pixad, boxc, L_INSERT); + } + boxDestroy(&box); + } + else { + pixd = pixClipRectangle(pixs, box, NULL); + pixaAddPix(pixad, pixd, L_INSERT); + pixaAddBox(pixad, box, L_INSERT); + } + } + + return pixad; +} + + +/*! + * \brief pixaSplitPix() + * + * \param[in] pixs with individual components on a lattice + * \param[in] nx number of mosaic cells horizontally + * \param[in] ny number of mosaic cells vertically + * \param[in] borderwidth of added border on all sides + * \param[in] bordercolor in our RGBA format: 0xrrggbbaa + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a variant on pixaCreateFromPix(), where we
+ *          simply divide the image up into (approximately) equal
+ *          subunits.  If you want the subimages to have essentially
+ *          the same aspect ratio as the input pix, use nx = ny.
+ *      (2) If borderwidth is 0, we ignore the input bordercolor and
+ *          redefine it to white.
+ *      (3) The bordercolor is always used to initialize each tiled pix,
+ *          so that if the src is clipped, the unblitted part will
+ *          be this color.  This avoids 1 pixel wide black stripes at the
+ *          left and lower edges.
+ * 
+ */ +PIXA * +pixaSplitPix(PIX *pixs, + l_int32 nx, + l_int32 ny, + l_int32 borderwidth, + l_uint32 bordercolor) +{ + l_int32 w, h, d, cellw, cellh, i, j; + PIX *pix1; + PIXA *pixa; + + PROCNAME("pixaSplitPix"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (nx <= 0 || ny <= 0) + return (PIXA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); + borderwidth = L_MAX(0, borderwidth); + + if ((pixa = pixaCreate(nx * ny)) == NULL) + return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + cellw = (w + nx - 1) / nx; /* round up */ + cellh = (h + ny - 1) / ny; + + for (i = 0; i < ny; i++) { + for (j = 0; j < nx; j++) { + if ((pix1 = pixCreate(cellw + 2 * borderwidth, + cellh + 2 * borderwidth, d)) == NULL) { + pixaDestroy(&pixa); + return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); + } + pixCopyColormap(pix1, pixs); + if (borderwidth == 0) { /* initialize full image to white */ + if (d == 1) + pixClearAll(pix1); + else + pixSetAll(pix1); + } + else { + pixSetAllArbitrary(pix1, bordercolor); + } + pixRasterop(pix1, borderwidth, borderwidth, cellw, cellh, + PIX_SRC, pixs, j * cellw, i * cellh); + pixaAddPix(pixa, pix1, L_INSERT); + } + } + + return pixa; +} + + +/*! + * \brief pixaDestroy() + * + * \param[in,out] ppixa use ptr address so it will be nulled + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pixa.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +pixaDestroy(PIXA **ppixa) +{ + l_int32 i; + PIXA *pixa; + + PROCNAME("pixaDestroy"); + + if (ppixa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((pixa = *ppixa) == NULL) + return; + + /* Decrement the refcount. If it is 0, destroy the pixa. */ + pixaChangeRefcount(pixa, -1); + if (pixa->refcount <= 0) { + for (i = 0; i < pixa->n; i++) + pixDestroy(&pixa->pix[i]); + LEPT_FREE(pixa->pix); + boxaDestroy(&pixa->boxa); + LEPT_FREE(pixa); + } + + *ppixa = NULL; + return; +} + + +/*! + * \brief pixaCopy() + * + * \param[in] pixa + * \param[in] copyflag see pix.h for details: + * L_COPY makes a new pixa and copies each pix and each box; + * L_CLONE gives a new ref-counted handle to the input pixa; + * L_COPY_CLONE makes a new pixa and inserts clones of + * all pix and boxes + * \return new pixa, or NULL on error + */ +PIXA * +pixaCopy(PIXA *pixa, + l_int32 copyflag) +{ + l_int32 i, nb; + BOX *boxc; + PIX *pixc; + PIXA *pixac; + + PROCNAME("pixaCopy"); + + if (!pixa) + return (PIXA *)ERROR_PTR("pixa not defined", procName, NULL); + + if (copyflag == L_CLONE) { + pixaChangeRefcount(pixa, 1); + return pixa; + } + + if (copyflag != L_COPY && copyflag != L_COPY_CLONE) + return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + if ((pixac = pixaCreate(pixa->n)) == NULL) + return (PIXA *)ERROR_PTR("pixac not made", procName, NULL); + nb = pixaGetBoxaCount(pixa); + for (i = 0; i < pixa->n; i++) { + if (copyflag == L_COPY) { + pixc = pixaGetPix(pixa, i, L_COPY); + if (i < nb) boxc = pixaGetBox(pixa, i, L_COPY); + } + else { /* copy-clone */ + pixc = pixaGetPix(pixa, i, L_CLONE); + if (i < nb) boxc = pixaGetBox(pixa, i, L_CLONE); + } + pixaAddPix(pixac, pixc, L_INSERT); + if (i < nb) pixaAddBox(pixac, boxc, L_INSERT); + } + + return pixac; +} + + + +/*---------------------------------------------------------------------* + * Pixa addition * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaAddPix() + * + * \param[in] pixa + * \param[in] pix to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK; 1 on error + */ +l_ok +pixaAddPix(PIXA *pixa, + PIX *pix, + l_int32 copyflag) +{ + l_int32 n; + PIX *pixc; + + PROCNAME("pixaAddPix"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (copyflag == L_INSERT) + pixc = pix; + else if (copyflag == L_COPY) + pixc = pixCopy(NULL, pix); + else if (copyflag == L_CLONE) + pixc = pixClone(pix); + else + return ERROR_INT("invalid copyflag", procName, 1); + if (!pixc) + return ERROR_INT("pixc not made", procName, 1); + + n = pixaGetCount(pixa); + if (n >= pixa->nalloc) + pixaExtendArray(pixa); + pixa->pix[n] = pixc; + pixa->n++; + + return 0; +} + + +/*! + * \brief pixaAddBox() + * + * \param[in] pixa + * \param[in] box + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +pixaAddBox(PIXA *pixa, + BOX *box, + l_int32 copyflag) +{ + PROCNAME("pixaAddBox"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) + return ERROR_INT("invalid copyflag", procName, 1); + + boxaAddBox(pixa->boxa, box, copyflag); + return 0; +} + + +/*! + * \brief pixaExtendArray() + * + * \param[in] pixa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Doubles the size of the pixa and boxa ptr arrays.
+ * 
+ */ +static l_int32 +pixaExtendArray(PIXA *pixa) +{ + PROCNAME("pixaExtendArray"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + return pixaExtendArrayToSize(pixa, 2 * pixa->nalloc); +} + + +/*! + * \brief pixaExtendArrayToSize() + * + * \param[in] pixa + * \param[in] size + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If necessary, reallocs new pixa and boxa ptrs arrays to %size.
+ *          The pixa and boxa ptr arrays must always be equal in size.
+ * 
+ */ +l_ok +pixaExtendArrayToSize(PIXA *pixa, + l_int32 size) +{ + PROCNAME("pixaExtendArrayToSize"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + if (size > pixa->nalloc) { + if ((pixa->pix = (PIX **)reallocNew((void **)&pixa->pix, + sizeof(PIX *) * pixa->nalloc, + size * sizeof(PIX *))) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + pixa->nalloc = size; + } + return boxaExtendArrayToSize(pixa->boxa, size); +} + + +/*---------------------------------------------------------------------* + * Pixa accessors * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaGetCount() + * + * \param[in] pixa + * \return count, or 0 if no pixa + */ +l_int32 +pixaGetCount(PIXA *pixa) +{ + PROCNAME("pixaGetCount"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 0); + + return pixa->n; +} + + +/*! + * \brief pixaChangeRefcount() + * + * \param[in] pixa + * \param[in] delta + * \return 0 if OK, 1 on error + */ +l_ok +pixaChangeRefcount(PIXA *pixa, + l_int32 delta) +{ + PROCNAME("pixaChangeRefcount"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + pixa->refcount += delta; + return 0; +} + + +/*! + * \brief pixaGetPix() + * + * \param[in] pixa + * \param[in] index to the index-th pix + * \param[in] accesstype L_COPY or L_CLONE + * \return pix, or NULL on error + */ +PIX * +pixaGetPix(PIXA *pixa, + l_int32 index, + l_int32 accesstype) +{ + PIX *pix; + + PROCNAME("pixaGetPix"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (index < 0 || index >= pixa->n) + return (PIX *)ERROR_PTR("index not valid", procName, NULL); + if ((pix = pixa->pix[index]) == NULL) { + L_ERROR("no pix at pixa[%d]\n", procName, index); + return (PIX *)ERROR_PTR("pix not found!", procName, NULL); + } + + if (accesstype == L_COPY) + return pixCopy(NULL, pix); + else if (accesstype == L_CLONE) + return pixClone(pix); + else + return (PIX *)ERROR_PTR("invalid accesstype", procName, NULL); +} + + +/*! + * \brief pixaGetPixDimensions() + * + * \param[in] pixa + * \param[in] index to the index-th box + * \param[out] pw, ph, pd [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixaGetPixDimensions(PIXA *pixa, + l_int32 index, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd) +{ + PIX *pix; + + PROCNAME("pixaGetPixDimensions"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pd) *pd = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (index < 0 || index >= pixa->n) + return ERROR_INT("index not valid", procName, 1); + + if ((pix = pixaGetPix(pixa, index, L_CLONE)) == NULL) + return ERROR_INT("pix not found!", procName, 1); + pixGetDimensions(pix, pw, ph, pd); + pixDestroy(&pix); + return 0; +} + + +/*! + * \brief pixaGetBoxa() + * + * \param[in] pixa + * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE + * \return boxa, or NULL on error + */ +BOXA * +pixaGetBoxa(PIXA *pixa, + l_int32 accesstype) +{ + PROCNAME("pixaGetBoxa"); + + if (!pixa) + return (BOXA *)ERROR_PTR("pixa not defined", procName, NULL); + if (!pixa->boxa) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE && + accesstype != L_COPY_CLONE) + return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL); + + return boxaCopy(pixa->boxa, accesstype); +} + + +/*! + * \brief pixaGetBoxaCount() + * + * \param[in] pixa + * \return count, or 0 on error + */ +l_int32 +pixaGetBoxaCount(PIXA *pixa) +{ + PROCNAME("pixaGetBoxaCount"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 0); + + return boxaGetCount(pixa->boxa); +} + + +/*! + * \brief pixaGetBox() + * + * \param[in] pixa + * \param[in] index to the index-th pix + * \param[in] accesstype L_COPY or L_CLONE + * \return box if null, not automatically an error, or NULL on error + * + *
+ * Notes:
+ *      (1) There is always a boxa with a pixa, and it is initialized so
+ *          that each box ptr is NULL.
+ *      (2) In general, we expect that there is either a box associated
+ *          with each pix, or no boxes at all in the boxa.
+ *      (3) Having no boxes is thus not an automatic error.  Whether it
+ *          is an actual error is determined by the calling program.
+ *          If the caller expects to get a box, it is an error; see, e.g.,
+ *          pixaGetBoxGeometry().
+ * 
+ */ +BOX * +pixaGetBox(PIXA *pixa, + l_int32 index, + l_int32 accesstype) +{ + BOX *box; + + PROCNAME("pixaGetBox"); + + if (!pixa) + return (BOX *)ERROR_PTR("pixa not defined", procName, NULL); + if (!pixa->boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + if (index < 0 || index >= pixa->boxa->n) + return (BOX *)ERROR_PTR("index not valid", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE) + return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL); + + box = pixa->boxa->box[index]; + if (box) { + if (accesstype == L_COPY) + return boxCopy(box); + else /* accesstype == L_CLONE */ + return boxClone(box); + } + else { + return NULL; + } +} + + +/*! + * \brief pixaGetBoxGeometry() + * + * \param[in] pixa + * \param[in] index to the index-th box + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixaGetBoxGeometry(PIXA *pixa, + l_int32 index, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ + BOX *box; + + PROCNAME("pixaGetBoxGeometry"); + + if (px) *px = 0; + if (py) *py = 0; + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (index < 0 || index >= pixa->n) + return ERROR_INT("index not valid", procName, 1); + + if ((box = pixaGetBox(pixa, index, L_CLONE)) == NULL) + return ERROR_INT("box not found!", procName, 1); + boxGetGeometry(box, px, py, pw, ph); + boxDestroy(&box); + return 0; +} + + +/*! + * \brief pixaSetBoxa() + * + * \param[in] pixa + * \param[in] boxa + * \param[in] accesstype L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys the existing boxa in the pixa.
+ * 
+ */ +l_ok +pixaSetBoxa(PIXA *pixa, + BOXA *boxa, + l_int32 accesstype) +{ + PROCNAME("pixaSetBoxa"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!boxa) + return ERROR_INT("boxa not defined", procName, 1); + if (accesstype != L_INSERT && accesstype != L_COPY && + accesstype != L_CLONE) + return ERROR_INT("invalid access type", procName, 1); + + boxaDestroy(&pixa->boxa); + if (accesstype == L_INSERT) + pixa->boxa = boxa; + else + pixa->boxa = boxaCopy(boxa, accesstype); + + return 0; +} + + +/*! + * \brief pixaGetPixArray() + * + * \param[in] pixa + * \return pix array, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a ptr to the actual array.  The array is
+ *          owned by the pixa, so it must not be destroyed.
+ *      (2) The caller should always check if the return value is NULL
+ *          before accessing any of the pix ptrs in this array!
+ * 
+ */ +PIX ** +pixaGetPixArray(PIXA *pixa) +{ + PROCNAME("pixaGetPixArray"); + + if (!pixa) + return (PIX **)ERROR_PTR("pixa not defined", procName, NULL); + + return pixa->pix; +} + + +/*! + * \brief pixaVerifyDepth() + * + * \param[in] pixa + * \param[out] psame 1 if depth is the same for all pix; 0 otherwise + * \param[out] pmaxd [optional] max depth of all pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is considered to be an error if there are no pix.
+ * 
+ */ +l_ok +pixaVerifyDepth(PIXA *pixa, + l_int32 *psame, + l_int32 *pmaxd) +{ + l_int32 i, n, d, maxd, same; + + PROCNAME("pixaVerifyDepth"); + + if (pmaxd) *pmaxd = 0; + if (!psame) + return ERROR_INT("psame not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if ((n = pixaGetCount(pixa)) == 0) + return ERROR_INT("no pix in pixa", procName, 1); + + same = 1; + pixaGetPixDimensions(pixa, 0, NULL, NULL, &maxd); + for (i = 1; i < n; i++) { + if (pixaGetPixDimensions(pixa, i, NULL, NULL, &d)) + return ERROR_INT("pix depth not found", procName, 1); + maxd = L_MAX(maxd, d); + if (d != maxd) + same = 0; + } + *psame = same; + if (pmaxd) *pmaxd = maxd; + return 0; +} + + +/*! + * \brief pixaVerifyDimensions() + * + * \param[in] pixa + * \param[out] psame 1 if dimensions are the same for all pix; 0 otherwise + * \param[out] pmaxw [optional] max width of all pix + * \param[out] pmaxh [optional] max height of all pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is considered to be an error if there are no pix.
+ * 
+ */ +l_ok +pixaVerifyDimensions(PIXA *pixa, + l_int32 *psame, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ + l_int32 i, n, w, h, maxw, maxh, same; + + PROCNAME("pixaVerifyDimensions"); + + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!psame) + return ERROR_INT("psame not defined", procName, 1); + *psame = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if ((n = pixaGetCount(pixa)) == 0) + return ERROR_INT("no pix in pixa", procName, 1); + + same = 1; + pixaGetPixDimensions(pixa, 0, &maxw, &maxh, NULL); + for (i = 1; i < n; i++) { + if (pixaGetPixDimensions(pixa, i, &w, &h, NULL)) + return ERROR_INT("pix dimensions not found", procName, 1); + maxw = L_MAX(maxw, w); + maxh = L_MAX(maxh, h); + if (w != maxw || h != maxh) + same = 0; + } + *psame = same; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + return 0; +} + + +/*! + * \brief pixaIsFull() + * + * \param[in] pixa + * \param[out] pfullpa [optional] 1 if pixa is full + * \param[out] pfullba [optional] 1 if boxa is full + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) A pixa is "full" if the array of pix is fully
+ *          occupied from index 0 to index (pixa->n - 1).
+ * 
+ */ +l_ok +pixaIsFull(PIXA *pixa, + l_int32 *pfullpa, + l_int32 *pfullba) +{ + l_int32 i, n, full; + BOXA *boxa; + PIX *pix; + + PROCNAME("pixaIsFull"); + + if (pfullpa) *pfullpa = 0; + if (pfullba) *pfullba = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + if (pfullpa) { + full = 1; + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { + full = 0; + break; + } + pixDestroy(&pix); + } + *pfullpa = full; + } + if (pfullba) { + boxa = pixaGetBoxa(pixa, L_CLONE); + boxaIsFull(boxa, pfullba); + boxaDestroy(&boxa); + } + return 0; +} + + +/*! + * \brief pixaCountText() + * + * \param[in] pixa + * \param[out] pntext number of pix with non-empty text strings + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) All pix have non-empty text strings if the returned value %ntext
+ *          equals the pixa count.
+ * 
+ */ +l_ok +pixaCountText(PIXA *pixa, + l_int32 *pntext) +{ + char *text; + l_int32 i, n; + PIX *pix; + + PROCNAME("pixaCountText"); + + if (!pntext) + return ERROR_INT("&ntext not defined", procName, 1); + *pntext = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + continue; + text = pixGetText(pix); + if (text && strlen(text) > 0) + (*pntext)++; + pixDestroy(&pix); + } + + return 0; +} + + +/*! + * \brief pixaSetText() + * + * \param[in] pixa + * \param[in] text [optional] single text string, to insert in each pix + * \param[in] sa [optional] array of text strings, to insert in each pix + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) To clear all the text fields, use %sa == NULL and %text == NULL.
+ *      (2) To set all the text fields to the same value %text, use %sa = NULL.
+ *      (3) If %sa is defined, we ignore %text and use it; %sa must have
+ *          the same count as %pixa.
+ * 
+ */ +l_ok +pixaSetText(PIXA *pixa, + const char *text, + SARRAY *sa) +{ + char *str; + l_int32 i, n; + PIX *pix; + + PROCNAME("pixaSetText"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + if (sa && (sarrayGetCount(sa) != n)) + return ERROR_INT("pixa and sa sizes differ", procName, 1); + + if (!sa) { + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + continue; + pixSetText(pix, text); + pixDestroy(&pix); + } + return 0; + } + + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + continue; + str = sarrayGetString(sa, i, L_NOCOPY); + pixSetText(pix, str); + pixDestroy(&pix); + } + + return 0; +} + + +/*! + * \brief pixaGetLinePtrs() + * + * \param[in] pixa of pix that all have the same depth + * \param[out] psize [optional] number of pix in the pixa + * \return array of array of line ptrs, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixGetLinePtrs() for details.
+ *      (2) It is best if all pix in the pixa are the same size.
+ *          The size of each line ptr array is equal to the height
+ *          of the pix that it refers to.
+ *      (3) This is an array of arrays.  To destroy it:
+ *            for (i = 0; i < size; i++)
+ *                LEPT_FREE(lineset[i]);
+ *            LEPT_FREE(lineset);
+ * 
+ */ +void *** +pixaGetLinePtrs(PIXA *pixa, + l_int32 *psize) +{ + l_int32 i, n, same; + void **lineptrs; + void ***lineset; + PIX *pix; + + PROCNAME("pixaGetLinePtrs"); + + if (psize) *psize = 0; + if (!pixa) + return (void ***)ERROR_PTR("pixa not defined", procName, NULL); + pixaVerifyDepth(pixa, &same, NULL); + if (!same) + return (void ***)ERROR_PTR("pixa not all same depth", procName, NULL); + n = pixaGetCount(pixa); + if (psize) *psize = n; + if ((lineset = (void ***)LEPT_CALLOC(n, sizeof(void **))) == NULL) + return (void ***)ERROR_PTR("lineset not made", procName, NULL); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + lineptrs = pixGetLinePtrs(pix, NULL); + lineset[i] = lineptrs; + pixDestroy(&pix); + } + + return lineset; +} + + +/*---------------------------------------------------------------------* + * Pixa output info * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaWriteStreamInfo() + * + * \param[in] fp file stream + * \param[in] pixa + * \return 0 if OK, 1 on error. + * + *
+  * Notes:
+  *      (1) For each pix in the pixa, write out the pix dimensions, spp,
+  *          text string (if it exists), and cmap info.
+  * 
+ */ +l_ok +pixaWriteStreamInfo(FILE *fp, + PIXA *pixa) +{ + char *text; + l_int32 i, n, w, h, d, spp, count, hastext; + PIX *pix; + PIXCMAP *cmap; + + PROCNAME("pixaWriteStreamInfo"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { + fprintf(fp, "%d: no pix at this index\n", i); + continue; + } + pixGetDimensions(pix, &w, &h, &d); + spp = pixGetSpp(pix); + text = pixGetText(pix); + hastext = (text && strlen(text) > 0); + if ((cmap = pixGetColormap(pix)) != NULL) + count = pixcmapGetCount(cmap); + fprintf(fp, "Pix %d: w = %d, h = %d, d = %d, spp = %d", + i, w, h, d, spp); + if (cmap) fprintf(fp, ", cmap(%d colors)", count); + if (hastext) fprintf(fp, ", text = %s", text); + fprintf(fp, "\n"); + pixDestroy(&pix); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixa array modifiers * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaReplacePix() + * + * \param[in] pixa + * \param[in] index to the index-th pix + * \param[in] pix insert to replace existing one + * \param[in] box [optional] insert to replace existing + * \return 0 if OK, 1 on error + * + *
+  * Notes:
+  *      (1) In-place replacement of one pix.
+  *      (2) The previous pix at that location is destroyed.
+  * 
+ */ +l_ok +pixaReplacePix(PIXA *pixa, + l_int32 index, + PIX *pix, + BOX *box) +{ + BOXA *boxa; + + PROCNAME("pixaReplacePix"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (index < 0 || index >= pixa->n) + return ERROR_INT("index not valid", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixDestroy(&(pixa->pix[index])); + pixa->pix[index] = pix; + + if (box) { + boxa = pixa->boxa; + if (index > boxa->n) + return ERROR_INT("boxa index not valid", procName, 1); + boxaReplaceBox(boxa, index, box); + } + + return 0; +} + + +/*! + * \brief pixaInsertPix() + * + * \param[in] pixa + * \param[in] index at which pix is to be inserted + * \param[in] pixs new pix to be inserted + * \param[in] box [optional] new box to be inserted + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts pixa[i] --> pixa[i + 1] for all i >= index,
+ *          and then inserts at pixa[index].
+ *      (2) To insert at the beginning of the array, set index = 0.
+ *      (3) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ *      (4) To append a pix to a pixa, it's easier to use pixaAddPix().
+ * 
+ */ +l_ok +pixaInsertPix(PIXA *pixa, + l_int32 index, + PIX *pixs, + BOX *box) +{ + l_int32 i, n; + + PROCNAME("pixaInsertPix"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + n = pixaGetCount(pixa); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + if (n >= pixa->nalloc) { /* extend both ptr arrays */ + pixaExtendArray(pixa); + boxaExtendArray(pixa->boxa); + } + pixa->n++; + for (i = n; i > index; i--) + pixa->pix[i] = pixa->pix[i - 1]; + pixa->pix[index] = pixs; + + /* Optionally, insert the box */ + if (box) + boxaInsertBox(pixa->boxa, index, box); + + return 0; +} + + +/*! + * \brief pixaRemovePix() + * + * \param[in] pixa + * \param[in] index of pix to be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts pixa[i] --> pixa[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ *      (3) The corresponding box is removed as well, if it exists.
+ * 
+ */ +l_ok +pixaRemovePix(PIXA *pixa, + l_int32 index) +{ + l_int32 i, n, nbox; + BOXA *boxa; + PIX **array; + + PROCNAME("pixaRemovePix"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + n = pixaGetCount(pixa); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + /* Remove the pix */ + array = pixa->pix; + pixDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + pixa->n--; + + /* Remove the box if it exists */ + boxa = pixa->boxa; + nbox = boxaGetCount(boxa); + if (index < nbox) + boxaRemoveBox(boxa, index); + + return 0; +} + + +/*! + * \brief pixaRemovePixAndSave() + * + * \param[in] pixa + * \param[in] index of pix to be removed + * \param[out] ppix [optional] removed pix + * \param[out] pbox [optional] removed box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts pixa[i] --> pixa[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ *      (3) The corresponding box is removed as well, if it exists.
+ *      (4) The removed pix and box can either be retained or destroyed.
+ * 
+ */ +l_ok +pixaRemovePixAndSave(PIXA *pixa, + l_int32 index, + PIX **ppix, + BOX **pbox) +{ + l_int32 i, n, nbox; + BOXA *boxa; + PIX **array; + + PROCNAME("pixaRemovePixAndSave"); + + if (ppix) *ppix = NULL; + if (pbox) *pbox = NULL; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + n = pixaGetCount(pixa); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + /* Remove the pix */ + array = pixa->pix; + if (ppix) + *ppix = pixaGetPix(pixa, index, L_CLONE); + pixDestroy(&array[index]); + for (i = index + 1; i < n; i++) + array[i - 1] = array[i]; + array[n - 1] = NULL; + pixa->n--; + + /* Remove the box if it exists */ + boxa = pixa->boxa; + nbox = boxaGetCount(boxa); + if (index < nbox) + boxaRemoveBoxAndSave(boxa, index, pbox); + + return 0; +} + + +/*! + * \brief pixaRemoveSelected() + * + * \param[in] pixa + * \param[in] naindex numa of indices of pix to be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives error messages for invalid indices
+ * 
+ */ +l_ok +pixaRemoveSelected(PIXA *pixa, + NUMA *naindex) +{ + l_int32 i, n, index; + NUMA *na1; + + PROCNAME("pixaRemoveSelected"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!naindex) + return ERROR_INT("naindex not defined", procName, 1); + if ((n = numaGetCount(naindex)) == 0) + return ERROR_INT("naindex is empty", procName, 1); + + /* Remove from highest indices first */ + na1 = numaSort(NULL, naindex, L_SORT_DECREASING); + for (i = 0; i < n; i++) { + numaGetIValue(na1, i, &index); + pixaRemovePix(pixa, index); + } + numaDestroy(&na1); + return 0; +} + + +/*! + * \brief pixaInitFull() + * + * \param[in] pixa typically empty + * \param[in] pix [optional] to be replicated to the entire pixa ptr array + * \param[in] box [optional] to be replicated to the entire boxa ptr array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This initializes a pixa by filling up the entire pix ptr array
+ *          with copies of %pix.  If %pix == NULL, we use a tiny placeholder
+ *          pix (w = h = d = 1).  Any existing pix are destroyed.
+ *          It also optionally fills the boxa with copies of %box.
+ *          After this operation, the numbers of pix and (optionally)
+ *          boxes are equal to the number of allocated ptrs.
+ *      (2) Note that we use pixaReplacePix() instead of pixaInsertPix().
+ *          They both have the same effect when inserting into a NULL ptr
+ *          in the pixa ptr array:
+ *      (3) If the boxa is not initialized (i.e., filled with boxes),
+ *          later insertion of boxes will cause an error, because the
+ *          'n' field is 0.
+ *      (4) Example usage.  This function is useful to prepare for a
+ *          random insertion (or replacement) of pix into a pixa.
+ *          To randomly insert pix into a pixa, without boxes, up to
+ *          some index "max":
+ *             Pixa *pixa = pixaCreate(max);
+ *             pixaInitFull(pixa, NULL, NULL);
+ *          An existing pixa with a smaller ptr array can also be reused:
+ *             pixaExtendArrayToSize(pixa, max);
+ *             pixaInitFull(pixa, NULL, NULL);
+ *          The initialization allows the pixa to always be properly
+ *          filled, even if all pix (and boxes) are not later replaced.
+ * 
+ */ +l_ok +pixaInitFull(PIXA *pixa, + PIX *pix, + BOX *box) +{ + l_int32 i, n; + PIX *pix1; + + PROCNAME("pixaInitFull"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixa->nalloc; + pixa->n = n; + for (i = 0; i < n; i++) { + if (pix) + pix1 = pixCopy(NULL, pix); + else + pix1 = pixCreate(1, 1, 1); + pixaReplacePix(pixa, i, pix1, NULL); + } + if (box) + boxaInitFull(pixa->boxa, box); + + return 0; +} + + +/*! + * \brief pixaClear() + * + * \param[in] pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys all pix in the pixa, as well as
+ *          all boxes in the boxa.  The ptrs in the pix ptr array
+ *          are all null'd.  The number of allocated pix, n, is set to 0.
+ * 
+ */ +l_ok +pixaClear(PIXA *pixa) +{ + l_int32 i, n; + + PROCNAME("pixaClear"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) + pixDestroy(&pixa->pix[i]); + pixa->n = 0; + return boxaClear(pixa->boxa); +} + + +/*---------------------------------------------------------------------* + * Pixa and Pixaa combination * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaJoin() + * + * \param[in] pixad dest pixa; add to this one + * \param[in] pixas [optional] source pixa; add from this one + * \param[in] istart starting index in pixas + * \param[in] iend ending index in pixas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+  * Notes:
+  *      (1) This appends a clone of each indicated pix in pixas to pixad
+  *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+  *      (3) iend < 0 means 'read to the end'
+  *      (4) If pixas is NULL or contains no pix, this is a no-op.
+  * 
+ */ +l_ok +pixaJoin(PIXA *pixad, + PIXA *pixas, + l_int32 istart, + l_int32 iend) +{ + l_int32 i, n, nb; + BOXA *boxas, *boxad; + PIX *pix; + + PROCNAME("pixaJoin"); + + if (!pixad) + return ERROR_INT("pixad not defined", procName, 1); + if (!pixas || ((n = pixaGetCount(pixas)) == 0)) + return 0; + + if (istart < 0) + istart = 0; + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + pixaAddPix(pixad, pix, L_INSERT); + } + + boxas = pixaGetBoxa(pixas, L_CLONE); + boxad = pixaGetBoxa(pixad, L_CLONE); + nb = pixaGetBoxaCount(pixas); + iend = L_MIN(iend, nb - 1); + boxaJoin(boxad, boxas, istart, iend); + boxaDestroy(&boxas); /* just the clones */ + boxaDestroy(&boxad); + return 0; +} + + +/*! + * \brief pixaInterleave() + * + * \param[in] pixa1 first src pixa + * \param[in] pixa2 second src pixa + * \param[in] copyflag L_CLONE, L_COPY + * \return pixa interleaved from sources, or NULL on error. + * + *
+ * Notes:
+ *      (1) %copyflag determines if the pix are copied or cloned.
+ *          The boxes, if they exist, are copied.
+ *      (2) If the two pixa have different sizes, a warning is issued,
+ *          and the number of pairs returned is the minimum size.
+ * 
+ */ +PIXA * +pixaInterleave(PIXA *pixa1, + PIXA *pixa2, + l_int32 copyflag) +{ + l_int32 i, n1, n2, n, nb1, nb2; + BOX *box; + PIX *pix; + PIXA *pixad; + + PROCNAME("pixaInterleave"); + + if (!pixa1) + return (PIXA *)ERROR_PTR("pixa1 not defined", procName, NULL); + if (!pixa2) + return (PIXA *)ERROR_PTR("pixa2 not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + n1 = pixaGetCount(pixa1); + n2 = pixaGetCount(pixa2); + n = L_MIN(n1, n2); + if (n == 0) + return (PIXA *)ERROR_PTR("at least one input pixa is empty", + procName, NULL); + if (n1 != n2) + L_WARNING("counts differ: %d != %d\n", procName, n1, n2); + + pixad = pixaCreate(2 * n); + nb1 = pixaGetBoxaCount(pixa1); + nb2 = pixaGetBoxaCount(pixa2); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa1, i, copyflag); + pixaAddPix(pixad, pix, L_INSERT); + if (i < nb1) { + box = pixaGetBox(pixa1, i, L_COPY); + pixaAddBox(pixad, box, L_INSERT); + } + pix = pixaGetPix(pixa2, i, copyflag); + pixaAddPix(pixad, pix, L_INSERT); + if (i < nb2) { + box = pixaGetBox(pixa2, i, L_COPY); + pixaAddBox(pixad, box, L_INSERT); + } + } + + return pixad; +} + + +/*! + * \brief pixaaJoin() + * + * \param[in] paad dest pixaa; add to this one + * \param[in] paas [optional] source pixaa; add from this one + * \param[in] istart starting index in pixaas + * \param[in] iend ending index in pixaas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This appends a clone of each indicated pixa in paas to pixaad
+ *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (3) iend < 0 means 'read to the end'
+ * 
+ */ +l_ok +pixaaJoin(PIXAA *paad, + PIXAA *paas, + l_int32 istart, + l_int32 iend) +{ + l_int32 i, n; + PIXA *pixa; + + PROCNAME("pixaaJoin"); + + if (!paad) + return ERROR_INT("pixaad not defined", procName, 1); + if (!paas) + return 0; + + if (istart < 0) + istart = 0; + n = pixaaGetCount(paas, NULL); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + pixa = pixaaGetPixa(paas, i, L_CLONE); + pixaaAddPixa(paad, pixa, L_INSERT); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixaa creation and destruction * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaaCreate() + * + * \param[in] n initial number of pixa ptrs + * \return paa, or NULL on error + * + *
+  * Notes:
+  *      (1) A pixaa provides a 2-level hierarchy of images.
+  *          A common use is for segmentation masks, which are
+  *          inexpensive to store in png format.
+  *      (2) For example, suppose you want a mask for each textline
+  *          in a two-column page.  The textline masks for each column
+  *          can be represented by a pixa, of which there are 2 in the pixaa.
+  *          The boxes for the textline mask components within a column
+  *          can have their origin referred to the column rather than the page.
+  *          Then the boxa field can be used to represent the two box (regions)
+  *          for the columns, and the (x,y) components of each box can
+  *          be used to get the absolute position of the textlines on
+  *          the page.
+  * 
+ */ +PIXAA * +pixaaCreate(l_int32 n) +{ + PIXAA *paa; + + PROCNAME("pixaaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + paa = (PIXAA *)LEPT_CALLOC(1, sizeof(PIXAA)); + paa->n = 0; + paa->nalloc = n; + if ((paa->pixa = (PIXA **)LEPT_CALLOC(n, sizeof(PIXA *))) == NULL) { + pixaaDestroy(&paa); + return (PIXAA *)ERROR_PTR("pixa ptrs not made", procName, NULL); + } + paa->boxa = boxaCreate(n); + + return paa; +} + + +/*! + * \brief pixaaCreateFromPixa() + * + * \param[in] pixa + * \param[in] n number specifying subdivision of pixa + * \param[in] type L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY + * \param[in] copyflag L_CLONE, L_COPY + * \return paa, or NULL on error + * + *
+ * Notes:
+ *      (1) This subdivides a pixa into a set of smaller pixa that
+ *          are accumulated into a pixaa.
+ *      (2) If type == L_CHOOSE_CONSECUTIVE, the first 'n' pix are
+ *          put in a pixa and added to pixaa, then the next 'n', etc.
+ *          If type == L_CHOOSE_SKIP_BY, the first pixa is made by
+ *          aggregating pix[0], pix[n], pix[2*n], etc.
+ *      (3) The copyflag specifies if each new pix is a copy or a clone.
+ * 
+ */ +PIXAA * +pixaaCreateFromPixa(PIXA *pixa, + l_int32 n, + l_int32 type, + l_int32 copyflag) +{ + l_int32 count, i, j, npixa; + PIX *pix; + PIXA *pixat; + PIXAA *paa; + + PROCNAME("pixaaCreateFromPixa"); + + if (!pixa) + return (PIXAA *)ERROR_PTR("pixa not defined", procName, NULL); + count = pixaGetCount(pixa); + if (count == 0) + return (PIXAA *)ERROR_PTR("no pix in pixa", procName, NULL); + if (n <= 0) + return (PIXAA *)ERROR_PTR("n must be > 0", procName, NULL); + if (type != L_CHOOSE_CONSECUTIVE && type != L_CHOOSE_SKIP_BY) + return (PIXAA *)ERROR_PTR("invalid type", procName, NULL); + if (copyflag != L_CLONE && copyflag != L_COPY) + return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); + + if (type == L_CHOOSE_CONSECUTIVE) + npixa = (count + n - 1) / n; + else /* L_CHOOSE_SKIP_BY */ + npixa = L_MIN(n, count); + paa = pixaaCreate(npixa); + if (type == L_CHOOSE_CONSECUTIVE) { + for (i = 0; i < count; i++) { + if (i % n == 0) + pixat = pixaCreate(n); + pix = pixaGetPix(pixa, i, copyflag); + pixaAddPix(pixat, pix, L_INSERT); + if (i % n == n - 1) + pixaaAddPixa(paa, pixat, L_INSERT); + } + if (i % n != 0) + pixaaAddPixa(paa, pixat, L_INSERT); + } + else { /* L_CHOOSE_SKIP_BY */ + for (i = 0; i < npixa; i++) { + pixat = pixaCreate(count / npixa + 1); + for (j = i; j < count; j += n) { + pix = pixaGetPix(pixa, j, copyflag); + pixaAddPix(pixat, pix, L_INSERT); + } + pixaaAddPixa(paa, pixat, L_INSERT); + } + } + + return paa; +} + + +/*! + * \brief pixaaDestroy() + * + * \param[in,out] ppaa use ptr address so it will be nulled + * \return void + */ +void +pixaaDestroy(PIXAA **ppaa) +{ + l_int32 i; + PIXAA *paa; + + PROCNAME("pixaaDestroy"); + + if (ppaa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((paa = *ppaa) == NULL) + return; + + for (i = 0; i < paa->n; i++) + pixaDestroy(&paa->pixa[i]); + LEPT_FREE(paa->pixa); + boxaDestroy(&paa->boxa); + + LEPT_FREE(paa); + *ppaa = NULL; + + return; +} + + +/*---------------------------------------------------------------------* + * Pixaa addition * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaaAddPixa() + * + * \param[in] paa + * \param[in] pixa to be added + * \param[in] copyflag: + * L_INSERT inserts the pixa directly; + * L_COPY makes a new pixa and copies each pix and each box; + * L_CLONE gives a new handle to the input pixa; + * L_COPY_CLONE makes a new pixa and inserts clones of + * all pix and boxes + * \return 0 if OK; 1 on error + */ +l_ok +pixaaAddPixa(PIXAA *paa, + PIXA *pixa, + l_int32 copyflag) +{ + l_int32 n; + PIXA *pixac; + + PROCNAME("pixaaAddPixa"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY && + copyflag != L_CLONE && copyflag != L_COPY_CLONE) + return ERROR_INT("invalid copyflag", procName, 1); + + if (copyflag == L_INSERT) { + pixac = pixa; + } + else { + if ((pixac = pixaCopy(pixa, copyflag)) == NULL) + return ERROR_INT("pixac not made", procName, 1); + } + + n = pixaaGetCount(paa, NULL); + if (n >= paa->nalloc) + pixaaExtendArray(paa); + paa->pixa[n] = pixac; + paa->n++; + + return 0; +} + + +/*! + * \brief pixaaExtendArray() + * + * \param[in] paa + * \return 0 if OK; 1 on error + */ +l_ok +pixaaExtendArray(PIXAA *paa) +{ + PROCNAME("pixaaExtendArray"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + + if ((paa->pixa = (PIXA **)reallocNew((void **)&paa->pixa, + sizeof(PIXA *) * paa->nalloc, + 2 * sizeof(PIXA *) * paa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + paa->nalloc = 2 * paa->nalloc; + return 0; +} + + +/*! + * \brief pixaaAddPix() + * + * \param[in] paa input paa + * \param[in] index index of pixa in paa + * \param[in] pix to be added + * \param[in] box [optional] to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK; 1 on error + */ +l_ok +pixaaAddPix(PIXAA *paa, + l_int32 index, + PIX *pix, + BOX *box, + l_int32 copyflag) +{ + PIXA *pixa; + + PROCNAME("pixaaAddPix"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) + return ERROR_INT("pixa not found", procName, 1); + pixaAddPix(pixa, pix, copyflag); + if (box) pixaAddBox(pixa, box, copyflag); + pixaDestroy(&pixa); + return 0; +} + + +/*! + * \brief pixaaAddBox() + * + * \param[in] paa + * \param[in] box + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The box can be used, for example, to hold the support region
+ *          of a pixa that is being added to the pixaa.
+ * 
+ */ +l_ok +pixaaAddBox(PIXAA *paa, + BOX *box, + l_int32 copyflag) +{ + PROCNAME("pixaaAddBox"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) + return ERROR_INT("invalid copyflag", procName, 1); + + boxaAddBox(paa->boxa, box, copyflag); + return 0; +} + + + +/*---------------------------------------------------------------------* + * Pixaa accessors * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaaGetCount() + * + * \param[in] paa + * \param[out] pna [optional] number of pix in each pixa + * \return count, or 0 if no pixaa + * + *
+  * Notes:
+  *      (1) If paa is empty, a returned na will also be empty.
+  * 
+ */ +l_int32 +pixaaGetCount(PIXAA *paa, + NUMA **pna) +{ + l_int32 i, n; + NUMA *na; + PIXA *pixa; + + PROCNAME("pixaaGetCount"); + + if (pna) *pna = NULL; + if (!paa) + return ERROR_INT("paa not defined", procName, 0); + + n = paa->n; + if (pna) { + if ((na = numaCreate(n)) == NULL) + return ERROR_INT("na not made", procName, 0); + *pna = na; + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + numaAddNumber(na, pixaGetCount(pixa)); + pixaDestroy(&pixa); + } + } + return n; +} + + +/*! + * \brief pixaaGetPixa() + * + * \param[in] paa + * \param[in] index to the index-th pixa + * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) L_COPY makes a new pixa with a copy of every pix
+ *      (2) L_CLONE just makes a new reference to the pixa,
+ *          and bumps the counter.  You would use this, for example,
+ *          when you need to extract some data from a pix within a
+ *          pixa within a pixaa.
+ *      (3) L_COPY_CLONE makes a new pixa with a clone of every pix
+ *          and box
+ *      (4) In all cases, you must invoke pixaDestroy() on the returned pixa
+ * 
+ */ +PIXA * +pixaaGetPixa(PIXAA *paa, + l_int32 index, + l_int32 accesstype) +{ + PIXA *pixa; + + PROCNAME("pixaaGetPixa"); + + if (!paa) + return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); + if (index < 0 || index >= paa->n) + return (PIXA *)ERROR_PTR("index not valid", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE && + accesstype != L_COPY_CLONE) + return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL); + + if ((pixa = paa->pixa[index]) == NULL) { /* shouldn't happen! */ + L_ERROR("missing pixa[%d]\n", procName, index); + return (PIXA *)ERROR_PTR("pixa not found at index", procName, NULL); + } + return pixaCopy(pixa, accesstype); +} + + +/*! + * \brief pixaaGetBoxa() + * + * \param[in] paa + * \param[in] accesstype L_COPY, L_CLONE + * \return boxa, or NULL on error + * + *
+ * Notes:
+ *      (1) L_COPY returns a copy; L_CLONE returns a new reference to the boxa.
+ *      (2) In both cases, invoke boxaDestroy() on the returned boxa.
+ * 
+ */ +BOXA * +pixaaGetBoxa(PIXAA *paa, + l_int32 accesstype) +{ + PROCNAME("pixaaGetBoxa"); + + if (!paa) + return (BOXA *)ERROR_PTR("paa not defined", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE) + return (BOXA *)ERROR_PTR("invalid access type", procName, NULL); + + return boxaCopy(paa->boxa, accesstype); +} + + +/*! + * \brief pixaaGetPix() + * + * \param[in] paa + * \param[in] index index into the pixa array in the pixaa + * \param[in] ipix index into the pix array in the pixa + * \param[in] accessflag L_COPY or L_CLONE + * \return pix, or NULL on error + */ +PIX * +pixaaGetPix(PIXAA *paa, + l_int32 index, + l_int32 ipix, + l_int32 accessflag) +{ + PIX *pix; + PIXA *pixa; + + PROCNAME("pixaaGetPix"); + + if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) + return (PIX *)ERROR_PTR("pixa not retrieved", procName, NULL); + if ((pix = pixaGetPix(pixa, ipix, accessflag)) == NULL) + L_ERROR("pix not retrieved\n", procName); + pixaDestroy(&pixa); + return pix; +} + + +/*! + * \brief pixaaVerifyDepth() + * + * \param[in] paa + * \param[out] psame 1 if all pix have the same depth; 0 otherwise + * \param[out] pmaxd [optional] max depth of all pix in pixaa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) It is considered to be an error if any pixa have no pix.
+ * 
+ */ +l_ok +pixaaVerifyDepth(PIXAA *paa, + l_int32 *psame, + l_int32 *pmaxd) +{ + l_int32 i, n, d, maxd, same, samed; + PIXA *pixa; + + PROCNAME("pixaaVerifyDepth"); + + if (pmaxd) *pmaxd = 0; + if (!psame) + return ERROR_INT("psame not defined", procName, 1); + *psame = 0; + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if ((n = pixaaGetCount(paa, NULL)) == 0) + return ERROR_INT("no pixa in paa", procName, 1); + + pixa = pixaaGetPixa(paa, 0, L_CLONE); + pixaVerifyDepth(pixa, &same, &maxd); /* init same, maxd with first pixa */ + pixaDestroy(&pixa); + for (i = 1; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + pixaVerifyDepth(pixa, &samed, &d); + pixaDestroy(&pixa); + maxd = L_MAX(maxd, d); + if (!samed || maxd != d) + same = 0; + } + *psame = same; + if (pmaxd) *pmaxd = maxd; + return 0; +} + + +/*! + * \brief pixaaVerifyDimensions() + * + * \param[in] paa + * \param[out] psame 1 if all pix have the same depth; 0 otherwise + * \param[out] pmaxw [optional] max width of all pix in pixaa + * \param[out] pmaxh [optional] max height of all pix in pixaa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) It is considered to be an error if any pixa have no pix.
+ * 
+ */ +l_ok +pixaaVerifyDimensions(PIXAA *paa, + l_int32 *psame, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ + l_int32 i, n, w, h, maxw, maxh, same, same2; + PIXA *pixa; + + PROCNAME("pixaaVerifyDimensions"); + + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!psame) + return ERROR_INT("psame not defined", procName, 1); + *psame = 0; + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if ((n = pixaaGetCount(paa, NULL)) == 0) + return ERROR_INT("no pixa in paa", procName, 1); + + /* Init same; init maxw and maxh from first pixa */ + pixa = pixaaGetPixa(paa, 0, L_CLONE); + pixaVerifyDimensions(pixa, &same, &maxw, &maxh); + pixaDestroy(&pixa); + + for (i = 1; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + pixaVerifyDimensions(pixa, &same2, &w, &h); + pixaDestroy(&pixa); + maxw = L_MAX(maxw, w); + maxh = L_MAX(maxh, h); + if (!same2 || maxw != w || maxh != h) + same = 0; + } + *psame = same; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + return 0; +} + + +/*! + * \brief pixaaIsFull() + * + * \param[in] paa + * \param[out] pfull 1 if all pixa in the paa have full pix arrays + * \return return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Does not require boxa associated with each pixa to be full.
+ * 
+ */ +l_int32 +pixaaIsFull(PIXAA *paa, + l_int32 *pfull) +{ + l_int32 i, n, full; + PIXA *pixa; + + PROCNAME("pixaaIsFull"); + + if (!pfull) + return ERROR_INT("&full not defined", procName, 0); + *pfull = 0; + if (!paa) + return ERROR_INT("paa not defined", procName, 0); + + n = pixaaGetCount(paa, NULL); + full = 1; + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + pixaIsFull(pixa, &full, NULL); + pixaDestroy(&pixa); + if (!full) break; + } + *pfull = full; + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixaa array modifiers * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaaInitFull() + * + * \param[in] paa typically empty + * \param[in] pixa to be replicated into the entire pixa ptr array + * \return 0 if OK, 1 on error + * + *
+  * Notes:
+  *      (1) This initializes a pixaa by filling up the entire pixa ptr array
+  *          with copies of %pixa.  Any existing pixa are destroyed.
+  *      (2) Example usage.  This function is useful to prepare for a
+  *          random insertion (or replacement) of pixa into a pixaa.
+  *          To randomly insert pixa into a pixaa, up to some index "max":
+  *             Pixaa *paa = pixaaCreate(max);
+  *             Pixa *pixa = pixaCreate(1);  // if you want little memory
+  *             pixaaInitFull(paa, pixa);  // copy it to entire array
+  *             pixaDestroy(&pixa);  // no longer needed
+  *          The initialization allows the pixaa to always be properly filled.
+  * 
+ */ +l_ok +pixaaInitFull(PIXAA *paa, + PIXA *pixa) +{ + l_int32 i, n; + PIXA *pixat; + + PROCNAME("pixaaInitFull"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = paa->nalloc; + paa->n = n; + for (i = 0; i < n; i++) { + pixat = pixaCopy(pixa, L_COPY); + pixaaReplacePixa(paa, i, pixat); + } + + return 0; +} + + +/*! + * \brief pixaaReplacePixa() + * + * \param[in] paa + * \param[in] index to the index-th pixa + * \param[in] pixa insert to replace existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This allows random insertion of a pixa into a pixaa, with
+ *          destruction of any existing pixa at that location.
+ *          The input pixa is now owned by the pixaa.
+ *      (2) No other pixa in the array are affected.
+ *      (3) The index must be within the allowed set.
+ * 
+ */ +l_ok +pixaaReplacePixa(PIXAA *paa, + l_int32 index, + PIXA *pixa) +{ + + PROCNAME("pixaaReplacePixa"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (index < 0 || index >= paa->n) + return ERROR_INT("index not valid", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + pixaDestroy(&(paa->pixa[index])); + paa->pixa[index] = pixa; + return 0; +} + + +/*! + * \brief pixaaClear() + * + * \param[in] paa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys all pixa in the pixaa, and nulls the ptrs
+ *          in the pixa ptr array.
+ * 
+ */ +l_ok +pixaaClear(PIXAA *paa) +{ + l_int32 i, n; + + PROCNAME("pixaClear"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + + n = pixaaGetCount(paa, NULL); + for (i = 0; i < n; i++) + pixaDestroy(&paa->pixa[i]); + paa->n = 0; + return 0; +} + + +/*! + * \brief pixaaTruncate() + * + * \param[in] paa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This identifies the largest index containing a pixa that
+ *          has any pix within it, destroys all pixa above that index,
+ *          and resets the count.
+ * 
+ */ +l_ok +pixaaTruncate(PIXAA *paa) +{ + l_int32 i, n, np; + PIXA *pixa; + + PROCNAME("pixaaTruncate"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + + n = pixaaGetCount(paa, NULL); + for (i = n - 1; i >= 0; i--) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + if (!pixa) { + paa->n--; + continue; + } + np = pixaGetCount(pixa); + pixaDestroy(&pixa); + if (np == 0) { + pixaDestroy(&paa->pixa[i]); + paa->n--; + } + else { + break; + } + } + return 0; +} + + + +/*---------------------------------------------------------------------* + * Pixa serialized I/O * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaRead() + * + * \param[in] filename + * \return pixa, or NULL on error + * + *
+  * Notes:
+  *      (1) The pix are stored in the file as png.
+  *          If the png library is not linked, this will fail.
+  * 
+ */ +PIXA * +pixaRead(const char *filename) +{ + FILE *fp; + PIXA *pixa; + + PROCNAME("pixaRead"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); +#endif /* !HAVE_LIBPNG */ + + if (!filename) + return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); + pixa = pixaReadStream(fp); + fclose(fp); + if (!pixa) + return (PIXA *)ERROR_PTR("pixa not read", procName, NULL); + return pixa; +} + + +/*! + * \brief pixaReadStream() + * + * \param[in] fp file stream + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +PIXA * +pixaReadStream(FILE *fp) +{ + l_int32 n, i, xres, yres, version; + l_int32 ignore; + BOXA *boxa; + PIX *pix; + PIXA *pixa; + + PROCNAME("pixaReadStream"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); +#endif /* !HAVE_LIBPNG */ + + if (!fp) + return (PIXA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nPixa Version %d\n", &version) != 1) + return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); + if (version != PIXA_VERSION_NUMBER) + return (PIXA *)ERROR_PTR("invalid pixa version", procName, NULL); + if (fscanf(fp, "Number of pix = %d\n", &n) != 1) + return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); + + if ((boxa = boxaReadStream(fp)) == NULL) + return (PIXA *)ERROR_PTR("boxa not made", procName, NULL); + if ((pixa = pixaCreate(n)) == NULL) { + boxaDestroy(&boxa); + return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); + } + boxaDestroy(&pixa->boxa); + pixa->boxa = boxa; + + for (i = 0; i < n; i++) { + if ((fscanf(fp, " pix[%d]: xres = %d, yres = %d\n", + &ignore, &xres, &yres)) != 3) { + pixaDestroy(&pixa); + return (PIXA *)ERROR_PTR("res reading error", procName, NULL); + } + if ((pix = pixReadStreamPng(fp)) == NULL) { + pixaDestroy(&pixa); + return (PIXA *)ERROR_PTR("pix not read", procName, NULL); + } + pixSetXRes(pix, xres); + pixSetYRes(pix, yres); + pixaAddPix(pixa, pix, L_INSERT); + } + return pixa; +} + + +/*! + * \brief pixaReadMem() + * + * \param[in] data of serialized pixa + * \param[in] size of data in bytes + * \return pixa, or NULL on error + */ +PIXA * +pixaReadMem(const l_uint8 *data, + size_t size) +{ + FILE *fp; + PIXA *pixa; + + PROCNAME("pixaReadMem"); + + if (!data) + return (PIXA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); + + pixa = pixaReadStream(fp); + fclose(fp); + if (!pixa) L_ERROR("pixa not read\n", procName); + return pixa; +} + + +/*! + * \brief pixaWriteDebug() + * + * \param[in] fname + * \param[in] pixa + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of pixaWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +pixaWriteDebug(const char *fname, + PIXA *pixa) +{ + PROCNAME("pixaWriteDebug"); + + if (LeptDebugOK) { + return pixaWrite(fname, pixa); + } + else { + L_INFO("write to named temp file %s is disabled\n", procName, fname); + return 0; + } +} + + +/*! + * \brief pixaWrite() + * + * \param[in] filename + * \param[in] pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +l_ok +pixaWrite(const char *filename, + PIXA *pixa) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("pixaWrite"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return ERROR_INT("no libpng: can't write data", procName, 1); +#endif /* !HAVE_LIBPNG */ + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixaWriteStream(fp, pixa); + fclose(fp); + if (ret) + return ERROR_INT("pixa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixaWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +l_ok +pixaWriteStream(FILE *fp, + PIXA *pixa) +{ + l_int32 n, i; + PIX *pix; + + PROCNAME("pixaWriteStream"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return ERROR_INT("no libpng: can't write data", procName, 1); +#endif /* !HAVE_LIBPNG */ + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + fprintf(fp, "\nPixa Version %d\n", PIXA_VERSION_NUMBER); + fprintf(fp, "Number of pix = %d\n", n); + boxaWriteStream(fp, pixa->boxa); + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + return ERROR_INT("pix not found", procName, 1); + fprintf(fp, " pix[%d]: xres = %d, yres = %d\n", + i, pix->xres, pix->yres); + pixWriteStreamPng(fp, pix, 0.0); + pixDestroy(&pix); + } + return 0; +} + + +/*! + * \brief pixaWriteMem() + * + * \param[out] pdata data of serialized pixa + * \param[out] psize size of returned data + * \param[in] pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a pixa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +pixaWriteMem(l_uint8 **pdata, + size_t *psize, + PIXA *pixa) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("pixaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixaWriteStream(fp, pixa); +#else + L_INFO("work-around: writing to a temp file\n", procName); +#ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#endif /* _WIN32 */ + ret = pixaWriteStream(fp, pixa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief pixaReadBoth() + * + * \param[in] filename + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) This reads serialized files of either a pixa or a pixacomp,
+ *          and returns a pixa in memory.  It requires png and jpeg libraries.
+ * 
+ */ +PIXA * +pixaReadBoth(const char *filename) +{ + char buf[32]; + char *sname; + PIXA *pixa; + PIXAC *pac; + + PROCNAME("pixaReadBoth"); + + if (!filename) + return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); + + l_getStructStrFromFile(filename, L_STR_NAME, &sname); + if (!sname) + return (PIXA *)ERROR_PTR("struct name not found", procName, NULL); + snprintf(buf, sizeof(buf), "%s", sname); + LEPT_FREE(sname); + + if (strcmp(buf, "Pixacomp") == 0) { + if ((pac = pixacompRead(filename)) == NULL) + return (PIXA *)ERROR_PTR("pac not made", procName, NULL); + pixa = pixaCreateFromPixacomp(pac, L_COPY); + pixacompDestroy(&pac); + } + else if (strcmp(buf, "Pixa") == 0) { + if ((pixa = pixaRead(filename)) == NULL) + return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); + } + else { + return (PIXA *)ERROR_PTR("invalid file type", procName, NULL); + } + return pixa; +} + + +/*---------------------------------------------------------------------* + * Pixaa serialized I/O * + *---------------------------------------------------------------------*/ + /*! + * \brief pixaaReadFromFiles() + * + * \param[in] dirname directory + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] first 0-based + * \param[in] nfiles use 0 for everything from %first to the end + * \return paa, or NULL on error or if no pixa files are found. + * + *
+  * Notes:
+  *      (1) The files must be serialized pixa files (e.g., *.pa)
+  *          If some files cannot be read, warnings are issued.
+  *      (2) Use %substr to filter filenames in the directory.  If
+  *          %substr == NULL, this takes all files.
+  *      (3) After filtering, use %first and %nfiles to select
+  *          a contiguous set of files, that have been lexically
+  *          sorted in increasing order.
+  * 
+ */ +PIXAA * +pixaaReadFromFiles(const char *dirname, + const char *substr, + l_int32 first, + l_int32 nfiles) +{ + char *fname; + l_int32 i, n; + PIXA *pixa; + PIXAA *paa; + SARRAY *sa; + + PROCNAME("pixaaReadFromFiles"); + + if (!dirname) + return (PIXAA *)ERROR_PTR("dirname not defined", procName, NULL); + + sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); + if (!sa || ((n = sarrayGetCount(sa)) == 0)) { + sarrayDestroy(&sa); + return (PIXAA *)ERROR_PTR("no pixa files found", procName, NULL); + } + + paa = pixaaCreate(n); + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if ((pixa = pixaRead(fname)) == NULL) { + L_ERROR("pixa not read for %d-th file", procName, i); + continue; + } + pixaaAddPixa(paa, pixa, L_INSERT); + } + + sarrayDestroy(&sa); + return paa; +} + + +/*! + * \brief pixaaRead() + * + * \param[in] filename + * \return paa, or NULL on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +PIXAA * +pixaaRead(const char *filename) +{ + FILE *fp; + PIXAA *paa; + + PROCNAME("pixaaRead"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); +#endif /* !HAVE_LIBPNG */ + + if (!filename) + return (PIXAA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); + paa = pixaaReadStream(fp); + fclose(fp); + if (!paa) + return (PIXAA *)ERROR_PTR("paa not read", procName, NULL); + return paa; +} + + +/*! + * \brief pixaaReadStream() + * + * \param[in] fp file stream + * \return paa, or NULL on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +PIXAA * +pixaaReadStream(FILE *fp) +{ + l_int32 n, i, version; + l_int32 ignore; + BOXA *boxa; + PIXA *pixa; + PIXAA *paa; + + PROCNAME("pixaaReadStream"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); +#endif /* !HAVE_LIBPNG */ + + if (!fp) + return (PIXAA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nPixaa Version %d\n", &version) != 1) + return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); + if (version != PIXAA_VERSION_NUMBER) + return (PIXAA *)ERROR_PTR("invalid pixaa version", procName, NULL); + if (fscanf(fp, "Number of pixa = %d\n", &n) != 1) + return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); + + if ((paa = pixaaCreate(n)) == NULL) + return (PIXAA *)ERROR_PTR("paa not made", procName, NULL); + if ((boxa = boxaReadStream(fp)) == NULL) { + pixaaDestroy(&paa); + return (PIXAA *)ERROR_PTR("boxa not made", procName, NULL); + } + boxaDestroy(&paa->boxa); + paa->boxa = boxa; + + for (i = 0; i < n; i++) { + if ((fscanf(fp, "\n\n --------------- pixa[%d] ---------------\n", + &ignore)) != 1) { + pixaaDestroy(&paa); + return (PIXAA *)ERROR_PTR("text reading", procName, NULL); + } + if ((pixa = pixaReadStream(fp)) == NULL) { + pixaaDestroy(&paa); + return (PIXAA *)ERROR_PTR("pixa not read", procName, NULL); + } + pixaaAddPixa(paa, pixa, L_INSERT); + } + + return paa; +} + + +/*! + * \brief pixaaReadMem() + * + * \param[in] data of serialized pixaa + * \param[in] size of data in bytes + * \return paa, or NULL on error + */ +PIXAA * +pixaaReadMem(const l_uint8 *data, + size_t size) +{ + FILE *fp; + PIXAA *paa; + + PROCNAME("paaReadMem"); + + if (!data) + return (PIXAA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); + + paa = pixaaReadStream(fp); + fclose(fp); + if (!paa) L_ERROR("paa not read\n", procName); + return paa; +} + + +/*! + * \brief pixaaWrite() + * + * \param[in] filename + * \param[in] paa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +l_ok +pixaaWrite(const char *filename, + PIXAA *paa) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("pixaaWrite"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return ERROR_INT("no libpng: can't read data", procName, 1); +#endif /* !HAVE_LIBPNG */ + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixaaWriteStream(fp, paa); + fclose(fp); + if (ret) + return ERROR_INT("paa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixaaWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] paa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pix are stored in the file as png.
+ *          If the png library is not linked, this will fail.
+ * 
+ */ +l_ok +pixaaWriteStream(FILE *fp, + PIXAA *paa) +{ + l_int32 n, i; + PIXA *pixa; + + PROCNAME("pixaaWriteStream"); + +#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ + return ERROR_INT("no libpng: can't read data", procName, 1); +#endif /* !HAVE_LIBPNG */ + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + + n = pixaaGetCount(paa, NULL); + fprintf(fp, "\nPixaa Version %d\n", PIXAA_VERSION_NUMBER); + fprintf(fp, "Number of pixa = %d\n", n); + boxaWriteStream(fp, paa->boxa); + for (i = 0; i < n; i++) { + if ((pixa = pixaaGetPixa(paa, i, L_CLONE)) == NULL) + return ERROR_INT("pixa not found", procName, 1); + fprintf(fp, "\n\n --------------- pixa[%d] ---------------\n", i); + pixaWriteStream(fp, pixa); + pixaDestroy(&pixa); + } + return 0; +} + + +/*! + * \brief pixaaWriteMem() + * + * \param[out] pdata data of serialized pixaa + * \param[out] psize size of returned data + * \param[in] paa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a pixaa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +pixaaWriteMem(l_uint8 **pdata, + size_t *psize, + PIXAA *paa) +{ + l_int32 ret; + FILE *fp; + + PROCNAME("pixaaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixaaWriteStream(fp, paa); +#else + L_INFO("work-around: writing to a temp file\n", procName); +#ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#endif /* _WIN32 */ + ret = pixaaWriteStream(fp, paa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixacc.c b/hgdriver/3rdparty/hgOCR/leptonica/pixacc.c new file mode 100644 index 0000000..f7aee0a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixacc.c @@ -0,0 +1,354 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file pixacc.c + *
+ *
+ *      Pixacc creation, destruction
+ *           PIXACC   *pixaccCreate()
+ *           PIXACC   *pixaccCreateFromPix()
+ *           void      pixaccDestroy()
+ *
+ *      Pixacc finalization
+ *           PIX      *pixaccFinal()
+ *
+ *      Pixacc accessors
+ *           PIX      *pixaccGetPix()
+ *           l_int32   pixaccGetOffset()
+ *
+ *      Pixacc accumulators
+ *           l_int32   pixaccAdd()
+ *           l_int32   pixaccSubtract()
+ *           l_int32   pixaccMultConst()
+ *           l_int32   pixaccMultConstAccumulate()
+ *
+ *  This is a simple interface for some of the pixel arithmetic operations
+ *  in pixarith.c.  These are easy to code up, but not as fast as
+ *  hand-coded functions that do arithmetic on corresponding pixels.
+ *
+ *  Suppose you want to make a linear combination of pix1 and pix2:
+ *     pixd = 0.4 * pix1 + 0.6 * pix2
+ *  where pix1 and pix2 are the same size and have depth 'd'.  Then:
+ *     Pixacc *pacc = pixaccCreateFromPix(pix1, 0);  // first; addition only
+ *     pixaccMultConst(pacc, 0.4);
+ *     pixaccMultConstAccumulate(pacc, pix2, 0.6);  // Add in 0.6 of the second
+ *     pixd = pixaccFinal(pacc, d);  // Get the result
+ *     pixaccDestroy(&pacc);
+ * 
+ */ + +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Pixacc creation, destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaccCreate() + * + * \param[in] w, h of 32 bpp internal Pix + * \param[in] negflag 0 if only positive numbers are involved; + * 1 if there will be negative numbers + * \return pixacc, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %negflag = 1 for safety if any negative numbers are going
+ *          to be used in the chain of operations.  Negative numbers
+ *          arise, e.g., by subtracting a pix, or by adding a pix
+ *          that has been pre-multiplied by a negative number.
+ *      (2) Initializes the internal 32 bpp pix, similarly to the
+ *          initialization in pixInitAccumulate().
+ * 
+ */ +PIXACC * +pixaccCreate(l_int32 w, + l_int32 h, + l_int32 negflag) +{ +PIXACC *pixacc; + + PROCNAME("pixaccCreate"); + + if ((pixacc = (PIXACC *)LEPT_CALLOC(1, sizeof(PIXACC))) == NULL) + return (PIXACC *)ERROR_PTR("pixacc not made", procName, NULL); + pixacc->w = w; + pixacc->h = h; + + if ((pixacc->pix = pixCreate(w, h, 32)) == NULL) { + pixaccDestroy(&pixacc); + return (PIXACC *)ERROR_PTR("pix not made", procName, NULL); + } + + if (negflag) { + pixacc->offset = 0x40000000; + pixSetAllArbitrary(pixacc->pix, pixacc->offset); + } + + return pixacc; +} + + +/*! + * \brief pixaccCreateFromPix() + * + * \param[in] pix + * \param[in] negflag 0 if only positive numbers are involved; + * 1 if there will be negative numbers + * \return pixacc, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixaccCreate()
+ * 
+ */ +PIXACC * +pixaccCreateFromPix(PIX *pix, + l_int32 negflag) +{ +l_int32 w, h; +PIXACC *pixacc; + + PROCNAME("pixaccCreateFromPix"); + + if (!pix) + return (PIXACC *)ERROR_PTR("pix not defined", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + pixacc = pixaccCreate(w, h, negflag); + pixaccAdd(pixacc, pix); + return pixacc; +} + + +/*! + * \brief pixaccDestroy() + * + * \param[in,out] ppixacc will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Always nulls the input ptr.
+ * 
+ */ +void +pixaccDestroy(PIXACC **ppixacc) +{ +PIXACC *pixacc; + + PROCNAME("pixaccDestroy"); + + if (ppixacc == NULL) { + L_WARNING("ptr address is NULL!", procName); + return; + } + + if ((pixacc = *ppixacc) == NULL) + return; + + pixDestroy(&pixacc->pix); + LEPT_FREE(pixacc); + *ppixacc = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Pixacc finalization * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaccFinal() + * + * \param[in] pixacc + * \param[in] outdepth 8, 16 or 32 bpp + * \return pixd 8, 16 or 32 bpp, or NULL on error + */ +PIX * +pixaccFinal(PIXACC *pixacc, + l_int32 outdepth) +{ + PROCNAME("pixaccFinal"); + + if (!pixacc) + return (PIX *)ERROR_PTR("pixacc not defined", procName, NULL); + + return pixFinalAccumulate(pixaccGetPix(pixacc), pixaccGetOffset(pixacc), + outdepth); +} + + +/*---------------------------------------------------------------------* + * Pixacc accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaccGetPix() + * + * \param[in] pixacc + * \return pix, or NULL on error + */ +PIX * +pixaccGetPix(PIXACC *pixacc) +{ + PROCNAME("pixaccGetPix"); + + if (!pixacc) + return (PIX *)ERROR_PTR("pixacc not defined", procName, NULL); + return pixacc->pix; +} + + +/*! + * \brief pixaccGetOffset() + * + * \param[in] pixacc + * \return offset, or -1 on error + */ +l_int32 +pixaccGetOffset(PIXACC *pixacc) +{ + PROCNAME("pixaccGetOffset"); + + if (!pixacc) + return ERROR_INT("pixacc not defined", procName, -1); + return pixacc->offset; +} + + +/*---------------------------------------------------------------------* + * Pixacc accumulators * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaccAdd() + * + * \param[in] pixacc + * \param[in] pix to be added + * \return 0 if OK, 1 on error + */ +l_ok +pixaccAdd(PIXACC *pixacc, + PIX *pix) +{ + PROCNAME("pixaccAdd"); + + if (!pixacc) + return ERROR_INT("pixacc not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixAccumulate(pixaccGetPix(pixacc), pix, L_ARITH_ADD); + return 0; +} + + +/*! + * \brief pixaccSubtract() + * + * \param[in] pixacc + * \param[in] pix to be subtracted + * \return 0 if OK, 1 on error + */ +l_ok +pixaccSubtract(PIXACC *pixacc, + PIX *pix) +{ + PROCNAME("pixaccSubtract"); + + if (!pixacc) + return ERROR_INT("pixacc not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixAccumulate(pixaccGetPix(pixacc), pix, L_ARITH_SUBTRACT); + return 0; +} + + +/*! + * \brief pixaccMultConst() + * + * \param[in] pixacc + * \param[in] factor + * \return 0 if OK, 1 on error + */ +l_ok +pixaccMultConst(PIXACC *pixacc, + l_float32 factor) +{ + PROCNAME("pixaccMultConst"); + + if (!pixacc) + return ERROR_INT("pixacc not defined", procName, 1); + pixMultConstAccumulate(pixaccGetPix(pixacc), factor, + pixaccGetOffset(pixacc)); + return 0; +} + + +/*! + * \brief pixaccMultConstAccumulate() + * + * \param[in] pixacc + * \param[in] pix + * \param[in] factor + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This creates a temp pix that is %pix multiplied by the
+ *          constant %factor.  It then adds that into %pixacc.
+ * 
+ */ +l_ok +pixaccMultConstAccumulate(PIXACC *pixacc, + PIX *pix, + l_float32 factor) +{ +l_int32 w, h, d, negflag; +PIX *pixt; +PIXACC *pacct; + + PROCNAME("pixaccMultConstAccumulate"); + + if (!pixacc) + return ERROR_INT("pixacc not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (factor == 0.0) return 0; + + pixGetDimensions(pix, &w, &h, &d); + negflag = (factor > 0.0) ? 0 : 1; + pacct = pixaccCreate(w, h, negflag); + pixaccAdd(pacct, pix); + pixaccMultConst(pacct, factor); + pixt = pixaccFinal(pacct, d); + pixaccAdd(pixacc, pixt); + + pixaccDestroy(&pacct); + pixDestroy(&pixt); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixafunc1.c b/hgdriver/3rdparty/hgOCR/leptonica/pixafunc1.c new file mode 100644 index 0000000..d8c1ed7 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixafunc1.c @@ -0,0 +1,2972 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixafunc1.c + *
+ *
+ *      Filters
+ *           PIX      *pixSelectBySize()
+ *           PIXA     *pixaSelectBySize()
+ *           NUMA     *pixaMakeSizeIndicator()
+ *
+ *           PIX      *pixSelectByPerimToAreaRatio()
+ *           PIXA     *pixaSelectByPerimToAreaRatio()
+ *           PIX      *pixSelectByPerimSizeRatio()
+ *           PIXA     *pixaSelectByPerimSizeRatio()
+ *           PIX      *pixSelectByAreaFraction()
+ *           PIXA     *pixaSelectByAreaFraction()
+ *           PIX      *pixSelectByWidthHeightRatio()
+ *           PIXA     *pixaSelectByWidthHeightRatio()
+ *           PIXA     *pixaSelectByNumConnComp()
+ *
+ *           PIXA     *pixaSelectWithIndicator()
+ *           l_int32   pixRemoveWithIndicator()
+ *           l_int32   pixAddWithIndicator()
+ *           PIXA     *pixaSelectWithString()
+ *           PIX      *pixaRenderComponent()
+ *
+ *      Sort functions
+ *           PIXA     *pixaSort()
+ *           PIXA     *pixaBinSort()
+ *           PIXA     *pixaSortByIndex()
+ *           PIXAA    *pixaSort2dByIndex()
+ *
+ *      Pixa and Pixaa range selection
+ *           PIXA     *pixaSelectRange()
+ *           PIXAA    *pixaaSelectRange()
+ *
+ *      Pixa and Pixaa scaling
+ *           PIXAA    *pixaaScaleToSize()
+ *           PIXAA    *pixaaScaleToSizeVar()
+ *           PIXA     *pixaScaleToSize()
+ *           PIXA     *pixaScaleToSizeRel()
+ *           PIXA     *pixaScale()
+ *           PIXA     *pixaScaleBySampling()
+ *
+ *      Pixa rotation and translation
+ *           PIXA     *pixaRotate()
+ *           PIXA     *pixaRotateOrth()
+ *           PIXA     *pixaTranslate()
+ *
+ *      Miscellaneous
+ *           PIXA     *pixaAddBorderGeneral()
+ *           PIXA     *pixaaFlattenToPixa()
+ *           l_int32   pixaaSizeRange()
+ *           l_int32   pixaSizeRange()
+ *           PIXA     *pixaClipToPix()
+ *           PIXA     *pixaClipToForeground()
+ *           l_int32   pixaGetRenderingDepth()
+ *           l_int32   pixaHasColor()
+ *           l_int32   pixaAnyColormaps()
+ *           l_int32   pixaGetDepthInfo()
+ *           PIXA     *pixaConvertToSameDepth()
+ *           l_int32   pixaEqual()
+ *           l_int32   pixaSetFullSizeBoxa()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* For more than this number of c.c. in a binarized image of + * semi-perimeter (w + h) about 5000 or less, the O(n) binsort + * is faster than the O(nlogn) shellsort. */ +static const l_int32 MinCompsForBinSort = 200; + + /* Don't rotate any angle smaller than this */ +static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */ + + +/*---------------------------------------------------------------------* + * Filters * + *---------------------------------------------------------------------*/ +/* + * These filters work on the connected components of 1 bpp images. + * They are typically used on pixa that have been generated from a Pix + * using pixConnComp(), so that the corresponding Boxa is available. + * + * The filters remove or retain c.c. based on these properties: + * (a) size [pixaFindDimensions()] + * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()] + * (c) foreground area as a fraction of bounding box area (w * h) + * [pixaFindForegroundArea()] + * (d) number of foreground pixels [pixaCountPixels()] + * (e) width/height aspect ratio [pixFindWidthHeightRatio()] + * + * We provide two different high-level interfaces: + * (1) Functions that use one of the filters on either + * a pix or the pixa of components. + * (2) A general method that generates numas of indicator functions, + * logically combines them, and efficiently removes or adds + * the selected components. + * + * For interface (1), the filtering is performed with a single function call. + * This is the easiest way to do simple filtering. These functions + * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of: + * Size + * PerimToAreaRatio + * PerimSizeRatio + * AreaFraction + * WidthHeightRatio + * + * For more complicated filtering, use the general method (2). + * The numa indicator functions for a pixa are generated by these functions: + * pixaFindDimensions() + * pixaFindPerimToAreaRatio() + * pixaFindPerimSizeRatio() + * pixaFindAreaFraction() + * pixaCountPixels() + * pixaFindWidthHeightRatio() + * pixaFindWidthHeightProduct() + * + * Here is an illustration using the general method. Suppose you want + * all 8-connected components that have a height greater than 40 pixels, + * a width not more than 30 pixels, between 150 and 300 fg pixels, + * and a perimeter-to-size ratio between 1.2 and 2.0. + * + * // Generate the pixa of 8 cc pieces. + * boxa = pixConnComp(pixs, &pixa, 8); + * + * // Extract the data we need about each component. + * pixaFindDimensions(pixa, &naw, &nah); + * nas = pixaCountPixels(pixa); + * nar = pixaFindPerimSizeRatio(pixa); + * + * // Build the indicator arrays for the set of components, + * // based on thresholds and selection criteria. + * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT); + * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE); + * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE); + * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE); + * na5 = numaMakeThresholdIndicator(nar, 1.2, L_SELECT_IF_GTE); + * na6 = numaMakeThresholdIndicator(nar, 2.0, L_SELECT_IF_LTE); + * + * // Combine the indicator arrays logically to find + * // the components that will be retained. + * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION); + * numaLogicalOp(nad, nad, na3, L_INTERSECTION); + * numaLogicalOp(nad, nad, na4, L_INTERSECTION); + * numaLogicalOp(nad, nad, na5, L_INTERSECTION); + * numaLogicalOp(nad, nad, na6, L_INTERSECTION); + * + * // Invert to get the components that will be removed. + * numaInvert(nad, nad); + * + * // Remove the components, in-place. + * pixRemoveWithIndicator(pixs, pixa, nad); + */ + + +/*! + * \brief pixSelectBySize() + * + * \param[in] pixs 1 bpp + * \param[in] width, height threshold dimensions + * \param[in] connectivity 4 or 8 + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 otherwise + * \return filtered pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) If unchanged, returns a copy of pixs.  Otherwise,
+ *          returns a new pix with the filtered components.
+ *      (3) If the selection type is L_SELECT_WIDTH, the input
+ *          height is ignored, and v.v.
+ *      (4) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +PIX * +pixSelectBySize(PIX *pixs, + l_int32 width, + l_int32 height, + l_int32 connectivity, + l_int32 type, + l_int32 relation, + l_int32 *pchanged) +{ +l_int32 w, h, empty, changed, count; +BOXA *boxa; +PIX *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixSelectBySize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid relation", procName, NULL); + if (pchanged) *pchanged = FALSE; + + /* Check if any components exist */ + pixZero(pixs, &empty); + if (empty) + return pixCopy(NULL, pixs); + + /* Identify and select the components */ + boxa = pixConnComp(pixs, &pixas, connectivity); + pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed); + boxaDestroy(&boxa); + pixaDestroy(&pixas); + + if (!changed) { + pixaDestroy(&pixad); + return pixCopy(NULL, pixs); + } + + /* Render the result */ + if (pchanged) *pchanged = TRUE; + pixGetDimensions(pixs, &w, &h, NULL); + count = pixaGetCount(pixad); + if (count == 0) { /* return empty pix */ + pixd = pixCreateTemplate(pixs); + } else { + pixd = pixaDisplay(pixad, w, h); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + } + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaSelectBySize() + * + * \param[in] pixas + * \param[in] width, height threshold dimensions + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 otherwise + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) If the selection type is L_SELECT_WIDTH, the input
+ *          height is ignored, and v.v.
+ *      (4) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +PIXA * +pixaSelectBySize(PIXA *pixas, + l_int32 width, + l_int32 height, + l_int32 type, + l_int32 relation, + l_int32 *pchanged) +{ +NUMA *na; +PIXA *pixad; + + PROCNAME("pixaSelectBySize"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (PIXA *)ERROR_PTR("invalid relation", procName, NULL); + + /* Compute the indicator array for saving components */ + na = pixaMakeSizeIndicator(pixas, width, height, type, relation); + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, na, pchanged); + + numaDestroy(&na); + return pixad; +} + + +/*! + * \brief pixaMakeSizeIndicator() + * + * \param[in] pixa + * \param[in] width, height threshold dimensions + * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return na indicator array, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) If the selection type is L_SELECT_WIDTH, the input
+ *          height is ignored, and v.v.
+ *      (3) To keep small components, use relation = L_SELECT_IF_LT or
+ *          L_SELECT_IF_LTE.
+ *          To keep large components, use relation = L_SELECT_IF_GT or
+ *          L_SELECT_IF_GTE.
+ * 
+ */ +NUMA * +pixaMakeSizeIndicator(PIXA *pixa, + l_int32 width, + l_int32 height, + l_int32 type, + l_int32 relation) +{ +l_int32 i, n, w, h, ival; +NUMA *na; + + PROCNAME("pixaMakeSizeIndicator"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (NUMA *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); + + n = pixaGetCount(pixa); + na = numaCreate(n); + for (i = 0; i < n; i++) { + ival = 0; + pixaGetPixDimensions(pixa, i, &w, &h, NULL); + switch (type) + { + case L_SELECT_WIDTH: + if ((relation == L_SELECT_IF_LT && w < width) || + (relation == L_SELECT_IF_GT && w > width) || + (relation == L_SELECT_IF_LTE && w <= width) || + (relation == L_SELECT_IF_GTE && w >= width)) + ival = 1; + break; + case L_SELECT_HEIGHT: + if ((relation == L_SELECT_IF_LT && h < height) || + (relation == L_SELECT_IF_GT && h > height) || + (relation == L_SELECT_IF_LTE && h <= height) || + (relation == L_SELECT_IF_GTE && h >= height)) + ival = 1; + break; + case L_SELECT_IF_EITHER: + if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || + ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || + ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || + ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) + ival = 1; + break; + case L_SELECT_IF_BOTH: + if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || + ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || + ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || + ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) + ival = 1; + break; + default: + L_WARNING("can't get here!\n", procName); + break; + } + numaAddNumber(na, ival); + } + + return na; +} + + +/*! + * \brief pixSelectByPerimToAreaRatio() + * + * \param[in] pixs 1 bpp + * \param[in] thresh threshold ratio of fg boundary to fg pixels + * \param[in] connectivity 4 or 8 + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) If unchanged, returns a copy of pixs.  Otherwise,
+ *          returns a new pix with the filtered components.
+ *      (3) This filters "thick" components, where a thick component
+ *          is defined to have a ratio of boundary to interior pixels
+ *          that is smaller than a given threshold value.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thicker
+ *          components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
+ * 
+ */ +PIX * +pixSelectByPerimToAreaRatio(PIX *pixs, + l_float32 thresh, + l_int32 connectivity, + l_int32 type, + l_int32 *pchanged) +{ +l_int32 w, h, empty, changed, count; +BOXA *boxa; +PIX *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixSelectByPerimToAreaRatio"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (pchanged) *pchanged = FALSE; + + /* Check if any components exist */ + pixZero(pixs, &empty); + if (empty) + return pixCopy(NULL, pixs); + + /* Filter thin components */ + boxa = pixConnComp(pixs, &pixas, connectivity); + pixad = pixaSelectByPerimToAreaRatio(pixas, thresh, type, &changed); + boxaDestroy(&boxa); + pixaDestroy(&pixas); + + if (!changed) { + pixaDestroy(&pixad); + return pixCopy(NULL, pixs); + } + + /* Render the result */ + if (pchanged) *pchanged = TRUE; + pixGetDimensions(pixs, &w, &h, NULL); + count = pixaGetCount(pixad); + if (count == 0) { /* return empty pix */ + pixd = pixCreateTemplate(pixs); + } else { + pixd = pixaDisplay(pixad, w, h); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + } + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaSelectByPerimToAreaRatio() + * + * \param[in] pixas + * \param[in] thresh threshold ratio of fg boundary to fg pixels + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) See pixSelectByPerimToAreaRatio().
+ * 
+ */ +PIXA * +pixaSelectByPerimToAreaRatio(PIXA *pixas, + l_float32 thresh, + l_int32 type, + l_int32 *pchanged) +{ +NUMA *na, *nai; +PIXA *pixad; + + PROCNAME("pixaSelectByPerimToAreaRatio"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + + /* Compute component ratios. */ + na = pixaFindPerimToAreaRatio(pixas); + + /* Generate indicator array for elements to be saved. */ + nai = numaMakeThresholdIndicator(na, thresh, type); + numaDestroy(&na); + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, nai, pchanged); + + numaDestroy(&nai); + return pixad; +} + + +/*! + * \brief pixSelectByPerimSizeRatio() + * + * \param[in] pixs 1 bpp + * \param[in] thresh threshold ratio of fg boundary to fg pixels + * \param[in] connectivity 4 or 8 + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the size of the
+ *          components that are kept.
+ *      (2) If unchanged, returns a copy of pixs.  Otherwise,
+ *          returns a new pix with the filtered components.
+ *      (3) This filters components with smooth vs. dendritic shape, using
+ *          the ratio of the fg boundary pixels to the circumference of
+ *          the bounding box, and comparing it to a threshold value.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the smooth
+ *          boundary components, and L_SELECT_IF_GT or L_SELECT_IF_GTE
+ *          to remove them.
+ * 
+ */ +PIX * +pixSelectByPerimSizeRatio(PIX *pixs, + l_float32 thresh, + l_int32 connectivity, + l_int32 type, + l_int32 *pchanged) +{ +l_int32 w, h, empty, changed, count; +BOXA *boxa; +PIX *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixSelectByPerimSizeRatio"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (pchanged) *pchanged = FALSE; + + /* Check if any components exist */ + pixZero(pixs, &empty); + if (empty) + return pixCopy(NULL, pixs); + + /* Filter thin components */ + boxa = pixConnComp(pixs, &pixas, connectivity); + pixad = pixaSelectByPerimSizeRatio(pixas, thresh, type, &changed); + boxaDestroy(&boxa); + pixaDestroy(&pixas); + + if (!changed) { + pixaDestroy(&pixad); + return pixCopy(NULL, pixs); + } + + /* Render the result */ + if (pchanged) *pchanged = TRUE; + pixGetDimensions(pixs, &w, &h, NULL); + count = pixaGetCount(pixad); + if (count == 0) { /* return empty pix */ + pixd = pixCreateTemplate(pixs); + } else { + pixd = pixaDisplay(pixad, w, h); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + } + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaSelectByPerimSizeRatio() + * + * \param[in] pixas + * \param[in] thresh threshold ratio of fg boundary to b.b. circumference + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) See pixSelectByPerimSizeRatio().
+ * 
+ */ +PIXA * +pixaSelectByPerimSizeRatio(PIXA *pixas, + l_float32 thresh, + l_int32 type, + l_int32 *pchanged) +{ +NUMA *na, *nai; +PIXA *pixad; + + PROCNAME("pixaSelectByPerimSizeRatio"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + + /* Compute component ratios. */ + na = pixaFindPerimSizeRatio(pixas); + + /* Generate indicator array for elements to be saved. */ + nai = numaMakeThresholdIndicator(na, thresh, type); + numaDestroy(&na); + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, nai, pchanged); + + numaDestroy(&nai); + return pixad; +} + + +/*! + * \brief pixSelectByAreaFraction() + * + * \param[in] pixs 1 bpp + * \param[in] thresh threshold ratio of fg pixels to (w * h) + * \param[in] connectivity 4 or 8 + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the amount of foreground
+ *          coverage of the components that are kept.
+ *      (2) If unchanged, returns a copy of pixs.  Otherwise,
+ *          returns a new pix with the filtered components.
+ *      (3) This filters components based on the fraction of fg pixels
+ *          of the component in its bounding box.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
+ *          with less than the threshold fraction of foreground, and
+ *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
+ * 
+ */ +PIX * +pixSelectByAreaFraction(PIX *pixs, + l_float32 thresh, + l_int32 connectivity, + l_int32 type, + l_int32 *pchanged) +{ +l_int32 w, h, empty, changed, count; +BOXA *boxa; +PIX *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixSelectByAreaFraction"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (pchanged) *pchanged = FALSE; + + /* Check if any components exist */ + pixZero(pixs, &empty); + if (empty) + return pixCopy(NULL, pixs); + + /* Filter components */ + boxa = pixConnComp(pixs, &pixas, connectivity); + pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed); + boxaDestroy(&boxa); + pixaDestroy(&pixas); + + if (!changed) { + pixaDestroy(&pixad); + return pixCopy(NULL, pixs); + } + + /* Render the result */ + if (pchanged) *pchanged = TRUE; + pixGetDimensions(pixs, &w, &h, NULL); + count = pixaGetCount(pixad); + if (count == 0) { /* return empty pix */ + pixd = pixCreateTemplate(pixs); + } else { + pixd = pixaDisplay(pixad, w, h); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + } + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaSelectByAreaFraction() + * + * \param[in] pixas + * \param[in] thresh threshold ratio of fg pixels to (w * h) + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) This filters components based on the fraction of fg pixels
+ *          of the component in its bounding box.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
+ *          with less than the threshold fraction of foreground, and
+ *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
+ * 
+ */ +PIXA * +pixaSelectByAreaFraction(PIXA *pixas, + l_float32 thresh, + l_int32 type, + l_int32 *pchanged) +{ +NUMA *na, *nai; +PIXA *pixad; + + PROCNAME("pixaSelectByAreaFraction"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + + /* Compute component ratios. */ + na = pixaFindAreaFraction(pixas); + + /* Generate indicator array for elements to be saved. */ + nai = numaMakeThresholdIndicator(na, thresh, type); + numaDestroy(&na); + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, nai, pchanged); + + numaDestroy(&nai); + return pixad; +} + + +/*! + * \brief pixSelectByWidthHeightRatio() + * + * \param[in] pixs 1 bpp + * \param[in] thresh threshold ratio of width/height + * \param[in] connectivity 4 or 8 + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The args specify constraints on the width-to-height ratio
+ *          for components that are kept.
+ *      (2) If unchanged, returns a copy of pixs.  Otherwise,
+ *          returns a new pix with the filtered components.
+ *      (3) This filters components based on the width-to-height ratios.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
+ *          with less than the threshold ratio, and
+ *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
+ * 
+ */ +PIX * +pixSelectByWidthHeightRatio(PIX *pixs, + l_float32 thresh, + l_int32 connectivity, + l_int32 type, + l_int32 *pchanged) +{ +l_int32 w, h, empty, changed, count; +BOXA *boxa; +PIX *pixd; +PIXA *pixas, *pixad; + + PROCNAME("pixSelectByWidthHeightRatio"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (pchanged) *pchanged = FALSE; + + /* Check if any components exist */ + pixZero(pixs, &empty); + if (empty) + return pixCopy(NULL, pixs); + + /* Filter components */ + boxa = pixConnComp(pixs, &pixas, connectivity); + pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed); + boxaDestroy(&boxa); + pixaDestroy(&pixas); + + if (!changed) { + pixaDestroy(&pixad); + return pixCopy(NULL, pixs); + } + + /* Render the result */ + if (pchanged) *pchanged = TRUE; + pixGetDimensions(pixs, &w, &h, NULL); + count = pixaGetCount(pixad); + if (count == 0) { /* return empty pix */ + pixd = pixCreateTemplate(pixs); + } else { + pixd = pixaDisplay(pixad, w, h); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + } + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaSelectByWidthHeightRatio() + * + * \param[in] pixas + * \param[in] thresh threshold ratio of width/height + * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) This filters components based on the width-to-height ratio
+ *          of each pix.
+ *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
+ *          with less than the threshold ratio, and
+ *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
+ * 
+ */ +PIXA * +pixaSelectByWidthHeightRatio(PIXA *pixas, + l_float32 thresh, + l_int32 type, + l_int32 *pchanged) +{ +NUMA *na, *nai; +PIXA *pixad; + + PROCNAME("pixaSelectByWidthHeightRatio"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && + type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + + /* Compute component ratios. */ + na = pixaFindWidthHeightRatio(pixas); + + /* Generate indicator array for elements to be saved. */ + nai = numaMakeThresholdIndicator(na, thresh, type); + numaDestroy(&na); + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, nai, pchanged); + + numaDestroy(&nai); + return pixad; +} + + +/*! + * \brief pixaSelectByNumConnComp() + * + * \param[in] pixas + * \param[in] nmin minimum number of components + * \param[in] nmax maximum number of components + * \param[in] connectivity 4 or 8 + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) This filters by the number of connected components in
+ *          a given range.
+ * 
+ */ +PIXA * +pixaSelectByNumConnComp(PIXA *pixas, + l_int32 nmin, + l_int32 nmax, + l_int32 connectivity, + l_int32 *pchanged) +{ +l_int32 n, i, count; +NUMA *na; +PIX *pix; +PIXA *pixad; + + PROCNAME("pixaSelectByNumConnComp"); + + if (pchanged) *pchanged = 0; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (nmin > nmax) + return (PIXA *)ERROR_PTR("nmin > nmax", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + /* Get indicator array based on number of c.c. */ + n = pixaGetCount(pixas); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + pixCountConnComp(pix, connectivity, &count); + if (count >= nmin && count <= nmax) + numaAddNumber(na, 1); + else + numaAddNumber(na, 0); + pixDestroy(&pix); + } + + /* Filter to get output */ + pixad = pixaSelectWithIndicator(pixas, na, pchanged); + numaDestroy(&na); + return pixad; +} + + +/*! + * \brief pixaSelectWithIndicator() + * + * \param[in] pixas + * \param[in] na indicator numa + * \param[out] pchanged [optional] 1 if changed; 0 if clone returned + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa clone if no components are removed.
+ *      (2) Uses pix and box clones in the new pixa.
+ *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
+ *      (4) If the source boxa is not fully populated, it is left
+ *          empty in the dest pixa.
+ * 
+ */ +PIXA * +pixaSelectWithIndicator(PIXA *pixas, + NUMA *na, + l_int32 *pchanged) +{ +l_int32 i, n, nbox, ival, nsave; +BOX *box; +PIX *pix1; +PIXA *pixad; + + PROCNAME("pixaSelectWithIndicator"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!na) + return (PIXA *)ERROR_PTR("na not defined", procName, NULL); + + nsave = 0; + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 1) nsave++; + } + + if (nsave == n) { + if (pchanged) *pchanged = FALSE; + return pixaCopy(pixas, L_CLONE); + } + if (pchanged) *pchanged = TRUE; + pixad = pixaCreate(nsave); + nbox = pixaGetBoxaCount(pixas); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 0) continue; + pix1 = pixaGetPix(pixas, i, L_CLONE); + pixaAddPix(pixad, pix1, L_INSERT); + if (nbox == n) { /* fully populated boxa */ + box = pixaGetBox(pixas, i, L_CLONE); + pixaAddBox(pixad, box, L_INSERT); + } + } + + return pixad; +} + + +/*! + * \brief pixRemoveWithIndicator() + * + * \param[in] pixs 1 bpp pix from which components are removed; in-place + * \param[in] pixa of connected components in pixs + * \param[in] na numa indicator: remove components corresponding to 1s + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This complements pixAddWithIndicator().   Here, the selected
+ *          components are set subtracted from pixs.
+ * 
+ */ +l_ok +pixRemoveWithIndicator(PIX *pixs, + PIXA *pixa, + NUMA *na) +{ +l_int32 i, n, ival, x, y, w, h; +BOX *box; +PIX *pix; + + PROCNAME("pixRemoveWithIndicator"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = pixaGetCount(pixa); + if (n != numaGetCount(na)) + return ERROR_INT("pixa and na sizes not equal", procName, 1); + + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 1) { + pix = pixaGetPix(pixa, i, L_CLONE); + box = pixaGetBox(pixa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC), + pix, 0, 0); + boxDestroy(&box); + pixDestroy(&pix); + } + } + + return 0; +} + + +/*! + * \brief pixAddWithIndicator() + * + * \param[in] pixs 1 bpp pix from which components are added; in-place + * \param[in] pixa of connected components, some of which will be put + * into pixs + * \param[in] na numa indicator: add components corresponding to 1s + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This complements pixRemoveWithIndicator().   Here, the selected
+ *          components are added to pixs.
+ * 
+ */ +l_ok +pixAddWithIndicator(PIX *pixs, + PIXA *pixa, + NUMA *na) +{ +l_int32 i, n, ival, x, y, w, h; +BOX *box; +PIX *pix; + + PROCNAME("pixAddWithIndicator"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!na) + return ERROR_INT("na not defined", procName, 1); + n = pixaGetCount(pixa); + if (n != numaGetCount(na)) + return ERROR_INT("pixa and na sizes not equal", procName, 1); + + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &ival); + if (ival == 1) { + pix = pixaGetPix(pixa, i, L_CLONE); + box = pixaGetBox(pixa, i, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); + boxDestroy(&box); + pixDestroy(&pix); + } + } + + return 0; +} + + +/*! + * \brief pixaSelectWithString() + * + * \param[in] pixas + * \param[in] str string of indices into pixa, giving the pix to + * be selected + * \param[out] perror [optional] 1 if any indices are invalid; + * 0 if all indices are valid + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a pixa with copies of selected pix.
+ *      (2) Associated boxes are also copied, if fully populated.
+ * 
+ */ +PIXA * +pixaSelectWithString(PIXA *pixas, + const char *str, + l_int32 *perror) +{ +l_int32 i, nval, npix, nbox, val, imaxval; +l_float32 maxval; +BOX *box; +NUMA *na; +PIX *pix1; +PIXA *pixad; + + PROCNAME("pixaSelectWithString"); + + if (perror) *perror = 0; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!str) + return (PIXA *)ERROR_PTR("str not defined", procName, NULL); + + if ((na = numaCreateFromString(str)) == NULL) + return (PIXA *)ERROR_PTR("na not made", procName, NULL); + if ((nval = numaGetCount(na)) == 0) { + numaDestroy(&na); + return (PIXA *)ERROR_PTR("no indices found", procName, NULL); + } + numaGetMax(na, &maxval, NULL); + imaxval = (l_int32)(maxval + 0.1); + nbox = pixaGetBoxaCount(pixas); + npix = pixaGetCount(pixas); + if (imaxval >= npix) { + if (perror) *perror = 1; + L_ERROR("max index = %d, size of pixa = %d\n", procName, imaxval, npix); + } + + pixad = pixaCreate(nval); + for (i = 0; i < nval; i++) { + numaGetIValue(na, i, &val); + if (val < 0 || val >= npix) { + L_ERROR("index %d out of range of pix\n", procName, val); + continue; + } + pix1 = pixaGetPix(pixas, val, L_COPY); + pixaAddPix(pixad, pix1, L_INSERT); + if (nbox == npix) { /* fully populated boxa */ + box = pixaGetBox(pixas, val, L_COPY); + pixaAddBox(pixad, box, L_INSERT); + } + } + numaDestroy(&na); + return pixad; +} + + +/*! + * \brief pixaRenderComponent() + * + * \param[in] pixs [optional] 1 bpp pix + * \param[in] pixa of 1 bpp connected components, one of which will + * be rendered in pixs, with its origin determined + * by the associated box. + * \param[in] index of component to be rendered + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs is null, this generates an empty pix of a size determined
+ *          by union of the component bounding boxes, and including the origin.
+ *      (2) The selected component is blitted into pixs.
+ * 
+ */ +PIX * +pixaRenderComponent(PIX *pixs, + PIXA *pixa, + l_int32 index) +{ +l_int32 n, x, y, w, h, same, maxd; +BOX *box; +BOXA *boxa; +PIX *pix; + + PROCNAME("pixaRenderComponent"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, pixs); + n = pixaGetCount(pixa); + if (index < 0 || index >= n) + return (PIX *)ERROR_PTR("invalid index", procName, pixs); + if (pixs && (pixGetDepth(pixs) != 1)) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixs); + pixaVerifyDepth(pixa, &same, &maxd); + if (maxd > 1) + return (PIX *)ERROR_PTR("not all pix with d == 1", procName, pixs); + + boxa = pixaGetBoxa(pixa, L_CLONE); + if (!pixs) { + boxaGetExtent(boxa, &w, &h, NULL); + pixs = pixCreate(w, h, 1); + } + + pix = pixaGetPix(pixa, index, L_CLONE); + box = boxaGetBox(boxa, index, L_CLONE); + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); + boxDestroy(&box); + pixDestroy(&pix); + boxaDestroy(&boxa); + + return pixs; +} + + +/*---------------------------------------------------------------------* + * Sort functions * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaSort() + * + * \param[in] pixas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, + * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, + * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, + * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex [optional] index of sorted order into + * original array + * \param[in] copyflag L_COPY, L_CLONE + * \return pixad sorted version of pixas, or NULL on error + * + *
+ * Notes:
+ *      (1) This sorts based on the data in the boxa.  If the boxa
+ *          count is not the same as the pixa count, this returns an error.
+ *      (2) If the boxa is empty, it makes one corresponding to the
+ *          dimensions of each pix, which allows meaningful sorting on
+ *          all types except x and y.
+ *      (3) The copyflag refers to the pix and box copies that are
+ *          inserted into the sorted pixa.  These are either L_COPY
+ *          or L_CLONE.
+ * 
+ */ +PIXA * +pixaSort(PIXA *pixas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex, + l_int32 copyflag) +{ +l_int32 i, n, nb, x, y, w, h; +BOXA *boxa; +NUMA *na, *naindex; +PIXA *pixad; + + PROCNAME("pixaSort"); + + if (pnaindex) *pnaindex = NULL; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && + sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && + sorttype != L_SORT_BY_MIN_DIMENSION && + sorttype != L_SORT_BY_MAX_DIMENSION && + sorttype != L_SORT_BY_PERIMETER && + sorttype != L_SORT_BY_AREA && + sorttype != L_SORT_BY_ASPECT_RATIO) + return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); + + /* Check the pixa and boxa counts. Make a boxa if required. */ + if ((n = pixaGetCount(pixas)) == 0) { + L_INFO("no pix in pixa\n", procName); + return pixaCopy(pixas, copyflag); + } + if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ + return (PIXA *)ERROR_PTR("boxa not found!", procName, NULL); + nb = boxaGetCount(boxa); + if (nb == 0) { + pixaSetFullSizeBoxa(pixas); + nb = n; + boxa = pixas->boxa; /* not owned */ + if (sorttype == L_SORT_BY_X || sorttype == L_SORT_BY_Y) + L_WARNING("sort by x or y where all values are 0\n", procName); + } + if (nb != n) + return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); + + /* Use O(n) binsort if possible */ + if (n > MinCompsForBinSort && + ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || + (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || + (sorttype == L_SORT_BY_PERIMETER))) + return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); + + /* Build up numa of specific data */ + if ((na = numaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("na not made", procName, NULL); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + switch (sorttype) + { + case L_SORT_BY_X: + numaAddNumber(na, x); + break; + case L_SORT_BY_Y: + numaAddNumber(na, y); + break; + case L_SORT_BY_WIDTH: + numaAddNumber(na, w); + break; + case L_SORT_BY_HEIGHT: + numaAddNumber(na, h); + break; + case L_SORT_BY_MIN_DIMENSION: + numaAddNumber(na, L_MIN(w, h)); + break; + case L_SORT_BY_MAX_DIMENSION: + numaAddNumber(na, L_MAX(w, h)); + break; + case L_SORT_BY_PERIMETER: + numaAddNumber(na, w + h); + break; + case L_SORT_BY_AREA: + numaAddNumber(na, w * h); + break; + case L_SORT_BY_ASPECT_RATIO: + numaAddNumber(na, (l_float32)w / (l_float32)h); + break; + default: + L_WARNING("invalid sort type\n", procName); + } + } + + /* Get the sort index for data array */ + naindex = numaGetSortIndex(na, sortorder); + numaDestroy(&na); + if (!naindex) + return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); + + /* Build up sorted pixa using sort index */ + if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { + numaDestroy(&naindex); + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + } + + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + return pixad; +} + + +/*! + * \brief pixaBinSort() + * + * \param[in] pixas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, + * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex [optional] index of sorted order into + * original array + * \param[in] copyflag L_COPY, L_CLONE + * \return pixad sorted version of pixas, or NULL on error + * + *
+ * Notes:
+ *      (1) This sorts based on the data in the boxa.  If the boxa
+ *          count is not the same as the pixa count, this returns an error.
+ *      (2) The copyflag refers to the pix and box copies that are
+ *          inserted into the sorted pixa.  These are either L_COPY
+ *          or L_CLONE.
+ *      (3) For a large number of boxes (say, greater than 1000), this
+ *          O(n) binsort is much faster than the O(nlogn) shellsort.
+ *          For 5000 components, this is over 20x faster than boxaSort().
+ *      (4) Consequently, pixaSort() calls this function if it will
+ *          likely go much faster.
+ * 
+ */ +PIXA * +pixaBinSort(PIXA *pixas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex, + l_int32 copyflag) +{ +l_int32 i, n, x, y, w, h; +BOXA *boxa; +NUMA *na, *naindex; +PIXA *pixad; + + PROCNAME("pixaBinSort"); + + if (pnaindex) *pnaindex = NULL; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && + sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && + sorttype != L_SORT_BY_PERIMETER) + return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); + + /* Verify that the pixa and its boxa have the same count */ + if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ + return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); + n = pixaGetCount(pixas); + if (boxaGetCount(boxa) != n) + return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); + + /* Generate Numa of appropriate box dimensions */ + if ((na = numaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("na not made", procName, NULL); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + switch (sorttype) + { + case L_SORT_BY_X: + numaAddNumber(na, x); + break; + case L_SORT_BY_Y: + numaAddNumber(na, y); + break; + case L_SORT_BY_WIDTH: + numaAddNumber(na, w); + break; + case L_SORT_BY_HEIGHT: + numaAddNumber(na, h); + break; + case L_SORT_BY_PERIMETER: + numaAddNumber(na, w + h); + break; + default: + L_WARNING("invalid sort type\n", procName); + } + } + + /* Get the sort index for data array */ + naindex = numaGetBinSortIndex(na, sortorder); + numaDestroy(&na); + if (!naindex) + return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); + + /* Build up sorted pixa using sort index */ + if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { + numaDestroy(&naindex); + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + } + + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + return pixad; +} + + +/*! + * \brief pixaSortByIndex() + * + * \param[in] pixas + * \param[in] naindex na that maps from the new pixa to the input pixa + * \param[in] copyflag L_COPY, L_CLONE + * \return pixad sorted, or NULL on error + */ +PIXA * +pixaSortByIndex(PIXA *pixas, + NUMA *naindex, + l_int32 copyflag) +{ +l_int32 i, n, index; +BOX *box; +PIX *pix; +PIXA *pixad; + + PROCNAME("pixaSortByIndex"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!naindex) + return (PIXA *)ERROR_PTR("naindex not defined", procName, NULL); + if (copyflag != L_CLONE && copyflag != L_COPY) + return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + pix = pixaGetPix(pixas, index, copyflag); + box = pixaGetBox(pixas, index, copyflag); + pixaAddPix(pixad, pix, L_INSERT); + pixaAddBox(pixad, box, L_INSERT); + } + + return pixad; +} + + +/*! + * \brief pixaSort2dByIndex() + * + * \param[in] pixas + * \param[in] naa numaa that maps from the new pixaa to the input pixas + * \param[in] copyflag L_CLONE or L_COPY + * \return paa sorted, or NULL on error + */ +PIXAA * +pixaSort2dByIndex(PIXA *pixas, + NUMAA *naa, + l_int32 copyflag) +{ +l_int32 pixtot, ntot, i, j, n, nn, index; +BOX *box; +NUMA *na; +PIX *pix; +PIXA *pixa; +PIXAA *paa; + + PROCNAME("pixaSort2dByIndex"); + + if (!pixas) + return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!naa) + return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL); + + /* Check counts */ + ntot = numaaGetNumberCount(naa); + pixtot = pixaGetCount(pixas); + if (ntot != pixtot) + return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL); + + n = numaaGetCount(naa); + paa = pixaaCreate(n); + for (i = 0; i < n; i++) { + na = numaaGetNuma(naa, i, L_CLONE); + nn = numaGetCount(na); + pixa = pixaCreate(nn); + for (j = 0; j < nn; j++) { + numaGetIValue(na, j, &index); + pix = pixaGetPix(pixas, index, copyflag); + box = pixaGetBox(pixas, index, copyflag); + pixaAddPix(pixa, pix, L_INSERT); + pixaAddBox(pixa, box, L_INSERT); + } + pixaaAddPixa(paa, pixa, L_INSERT); + numaDestroy(&na); + } + + return paa; +} + + +/*---------------------------------------------------------------------* + * Pixa and Pixaa range selection * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaSelectRange() + * + * \param[in] pixas + * \param[in] first use 0 to select from the beginning + * \param[in] last use -1 to select to the end + * \param[in] copyflag L_COPY, L_CLONE + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) The copyflag specifies what we do with each pix from pixas.
+ *          Specifically, L_CLONE inserts a clone into pixad of each
+ *          selected pix from pixas.
+ * 
+ */ +PIXA * +pixaSelectRange(PIXA *pixas, + l_int32 first, + l_int32 last, + l_int32 copyflag) +{ +l_int32 n, npix, i; +PIX *pix; +PIXA *pixad; + + PROCNAME("pixaSelectRange"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + n = pixaGetCount(pixas); + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return (PIXA *)ERROR_PTR("invalid first", procName, NULL); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return (PIXA *)ERROR_PTR("first > last", procName, NULL); + + npix = last - first + 1; + pixad = pixaCreate(npix); + for (i = first; i <= last; i++) { + pix = pixaGetPix(pixas, i, copyflag); + pixaAddPix(pixad, pix, L_INSERT); + } + return pixad; +} + + +/*! + * \brief pixaaSelectRange() + * + * \param[in] paas + * \param[in] first use 0 to select from the beginning + * \param[in] last use -1 to select to the end + * \param[in] copyflag L_COPY, L_CLONE + * \return paad, or NULL on error + * + *
+ * Notes:
+ *      (1) The copyflag specifies what we do with each pixa from paas.
+ *          Specifically, L_CLONE inserts a clone into paad of each
+ *          selected pixa from paas.
+ * 
+ */ +PIXAA * +pixaaSelectRange(PIXAA *paas, + l_int32 first, + l_int32 last, + l_int32 copyflag) +{ +l_int32 n, npixa, i; +PIXA *pixa; +PIXAA *paad; + + PROCNAME("pixaaSelectRange"); + + if (!paas) + return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); + n = pixaaGetCount(paas, NULL); + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return (PIXAA *)ERROR_PTR("invalid first", procName, NULL); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return (PIXAA *)ERROR_PTR("first > last", procName, NULL); + + npixa = last - first + 1; + paad = pixaaCreate(npixa); + for (i = first; i <= last; i++) { + pixa = pixaaGetPixa(paas, i, copyflag); + pixaaAddPixa(paad, pixa, L_INSERT); + } + return paad; +} + + +/*---------------------------------------------------------------------* + * Pixa and Pixaa scaling * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaaScaleToSize() + * + * \param[in] paas + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return paad, or NULL on error + * + *
+ * Notes:
+ *      (1) This guarantees that each output scaled image has the
+ *          dimension(s) you specify.
+ *           ~ To specify the width with isotropic scaling, set %hd = 0.
+ *           ~ To specify the height with isotropic scaling, set %wd = 0.
+ *           ~ If both %wd and %hd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *           ~ It is an error to set both %wd and %hd to 0.
+ * 
+ */ +PIXAA * +pixaaScaleToSize(PIXAA *paas, + l_int32 wd, + l_int32 hd) +{ +l_int32 n, i; +PIXA *pixa1, *pixa2; +PIXAA *paad; + + PROCNAME("pixaaScaleToSize"); + + if (!paas) + return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); + if (wd <= 0 && hd <= 0) + return (PIXAA *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); + + n = pixaaGetCount(paas, NULL); + paad = pixaaCreate(n); + for (i = 0; i < n; i++) { + pixa1 = pixaaGetPixa(paas, i, L_CLONE); + pixa2 = pixaScaleToSize(pixa1, wd, hd); + pixaaAddPixa(paad, pixa2, L_INSERT); + pixaDestroy(&pixa1); + } + return paad; +} + + +/*! + * \brief pixaaScaleToSizeVar() + * + * \param[in] paas + * \param[in] nawd [optional] target widths; use NULL if using height + * \param[in] nahd [optional] target height; use NULL if using width + * \return paad, or NULL on error + * + *
+ * Notes:
+ *      (1) This guarantees that the scaled images in each pixa have the
+ *          dimension(s) you specify in the numas.
+ *           ~ To specify the width with isotropic scaling, set %nahd = NULL.
+ *           ~ To specify the height with isotropic scaling, set %nawd = NULL.
+ *           ~ If both %nawd and %nahd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *           ~ It is an error to set both %nawd and %nahd to NULL.
+ *      (2) If either nawd and/or nahd is defined, it must have the same
+ *          count as the number of pixa in paas.
+ * 
+ */ +PIXAA * +pixaaScaleToSizeVar(PIXAA *paas, + NUMA *nawd, + NUMA *nahd) +{ +l_int32 n, i, wd, hd; +PIXA *pixa1, *pixa2; +PIXAA *paad; + + PROCNAME("pixaaScaleToSizeVar"); + + if (!paas) + return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); + if (!nawd && !nahd) + return (PIXAA *)ERROR_PTR("!nawd && !nahd", procName, NULL); + + n = pixaaGetCount(paas, NULL); + if (nawd && (n != numaGetCount(nawd))) + return (PIXAA *)ERROR_PTR("nawd wrong size", procName, NULL); + if (nahd && (n != numaGetCount(nahd))) + return (PIXAA *)ERROR_PTR("nahd wrong size", procName, NULL); + paad = pixaaCreate(n); + for (i = 0; i < n; i++) { + wd = hd = 0; + if (nawd) numaGetIValue(nawd, i, &wd); + if (nahd) numaGetIValue(nahd, i, &hd); + pixa1 = pixaaGetPixa(paas, i, L_CLONE); + pixa2 = pixaScaleToSize(pixa1, wd, hd); + pixaaAddPixa(paad, pixa2, L_INSERT); + pixaDestroy(&pixa1); + } + return paad; +} + + +/*! + * \brief pixaScaleToSize() + * + * \param[in] pixas + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixaaScaleToSize()
+ * 
+ */ +PIXA * +pixaScaleToSize(PIXA *pixas, + l_int32 wd, + l_int32 hd) +{ +l_int32 n, i; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaScaleToSize"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + if (wd <= 0 && hd <= 0) /* no scaling requested */ + return pixaCopy(pixas, L_CLONE); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixScaleToSize(pix1, wd, hd); + pixCopyText(pix2, pix1); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + return pixad; +} + + +/*! + * \brief pixaScaleToSizeRel() + * + * \param[in] pixas + * \param[in] delw change in width, in pixels; 0 means no change + * \param[in] delh change in height, in pixels; 0 means no change + * return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) If a requested change in a pix is not possible because
+ *          either the requested width or height is <= 0, issue a
+ *          warning and return a copy.
+ * 
+ */ +PIXA * +pixaScaleToSizeRel(PIXA *pixas, + l_int32 delw, + l_int32 delh) +{ +l_int32 n, i; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaScaleToSizeRel"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixScaleToSizeRel(pix1, delw, delh); + if (pix2) { + pixaAddPix(pixad, pix2, L_INSERT); + } else { + L_WARNING("relative scale to size failed; use a copy\n", procName); + pixaAddPix(pixad, pix1, L_COPY); + } + pixDestroy(&pix1); + } + return pixad; +} + + +/*! + * \brief pixaScale() + * + * \param[in] pixas + * \param[in] scalex + * \param[in] scaley + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixas has a full boxes, it is scaled as well.
+ * 
+ */ +PIXA * +pixaScale(PIXA *pixas, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 i, n, nb; +BOXA *boxa1, *boxa2; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaScale"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIXA *)ERROR_PTR("invalid scaling parameters", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixScale(pix1, scalex, scaley); + pixCopyText(pix2, pix1); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa1 = pixaGetBoxa(pixas, L_CLONE); + nb = boxaGetCount(boxa1); + if (nb == n) { + boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); + pixaSetBoxa(pixad, boxa2, L_INSERT); + } + boxaDestroy(&boxa1); + return pixad; +} + + +/*! + * \brief pixaScaleBySampling() + * + * \param[in] pixas + * \param[in] scalex + * \param[in] scaley + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixas has a full boxes, it is scaled as well.
+ * 
+ */ +PIXA * +pixaScaleBySampling(PIXA *pixas, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 i, n, nb; +BOXA *boxa1, *boxa2; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaScaleBySampling"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIXA *)ERROR_PTR("invalid scaling parameters", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixScaleBySampling(pix1, scalex, scaley); + pixCopyText(pix2, pix1); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa1 = pixaGetBoxa(pixas, L_CLONE); + nb = boxaGetCount(boxa1); + if (nb == n) { + boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); + pixaSetBoxa(pixad, boxa2, L_INSERT); + } + boxaDestroy(&boxa1); + return pixad; +} + + +/*---------------------------------------------------------------------* + * Pixa rotation and translation * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaRotate() + * + * \param[in] pixas 1, 2, 4, 8, 32 bpp rgb + * \param[in] angle rotation angle in radians; clockwise is positive + * \param[in] type L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \param[in] width original width; use 0 to avoid embedding + * \param[in] height original height; use 0 to avoid embedding + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Each pix is rotated about its center.  See pixRotate() for details.
+ *      (2) The boxa array is copied.  Why is it not rotated?
+ *          If a boxa exists, the array of boxes is in 1-to-1
+ *          correspondence with the array of pix, and each box typically
+ *          represents the location of the pix relative to an image from
+ *          which it has been extracted.  Like the pix, we could rotate
+ *          each box around its center, and then generate a box that
+ *          contains all four corners, as is done in boxaRotate(), but
+ *          this seems unnecessary.
+ * 
+ */ +PIXA * +pixaRotate(PIXA *pixas, + l_float32 angle, + l_int32 type, + l_int32 incolor, + l_int32 width, + l_int32 height) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pixs, *pixd; +PIXA *pixad; + + PROCNAME("pixaRotate"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && + type != L_ROTATE_SAMPLING) + return (PIXA *)ERROR_PTR("invalid type", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIXA *)ERROR_PTR("invalid incolor", procName, NULL); + if (L_ABS(angle) < MinAngleToRotate) + return pixaCopy(pixas, L_COPY); + + n = pixaGetCount(pixas); + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + boxa = pixaGetBoxa(pixad, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + for (i = 0; i < n; i++) { + if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); + } + pixd = pixRotate(pixs, angle, type, incolor, width, height); + pixaAddPix(pixad, pixd, L_INSERT); + pixDestroy(&pixs); + } + + return pixad; +} + + +/*! + * \brief pixaRotateOrth() + * + * \param[in] pixas + * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; + * all rotations are clockwise + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates each pix in the pixa.  Rotates and saves the boxes in
+ *          the boxa if the boxa is full.
+ * 
+ */ +PIXA * +pixaRotateOrth(PIXA *pixas, + l_int32 rotation) +{ +l_int32 i, n, nb, w, h; +BOX *boxs, *boxd; +PIX *pixs, *pixd; +PIXA *pixad; + + PROCNAME("pixaRotateOrth"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (rotation < 0 || rotation > 3) + return (PIXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); + if (rotation == 0) + return pixaCopy(pixas, L_COPY); + + n = pixaGetCount(pixas); + nb = pixaGetBoxaCount(pixas); + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); + } + pixd = pixRotateOrth(pixs, rotation); + pixaAddPix(pixad, pixd, L_INSERT); + if (n == nb) { + boxs = pixaGetBox(pixas, i, L_COPY); + pixGetDimensions(pixs, &w, &h, NULL); + boxd = boxRotateOrth(boxs, w, h, rotation); + pixaAddBox(pixad, boxd, L_INSERT); + boxDestroy(&boxs); + } + pixDestroy(&pixs); + } + + return pixad; +} + + +/*! + * \brief pixaTranslate() + * + * \param[in] pixas + * \param[in] hshift horizontal shift; hshift > 0 is to right + * \param[in] vshift vertical shift; vshift > 0 is down + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixad, or NULL on error. + */ +PIXA * +pixaTranslate(PIXA *pixas, + l_int32 hshift, + l_int32 vshift, + l_int32 incolor) +{ +l_int32 i, n, nb; +BOXA *boxas, *boxad; +PIX *pixs, *pixd; +PIXA *pixad; + + PROCNAME("pixaTranslate"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (hshift == 0 && vshift == 0) + return pixaCopy(pixas, L_COPY); + + n = pixaGetCount(pixas); + nb = pixaGetBoxaCount(pixas); + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { + pixaDestroy(&pixad); + return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); + } + pixd = pixTranslate(NULL, pixs, hshift, vshift, incolor); + pixaAddPix(pixad, pixd, L_INSERT); + pixDestroy(&pixs); + } + if (n == nb) { + boxas = pixaGetBoxa(pixas, L_CLONE); + boxad = boxaTransform(boxas, hshift, vshift, 1.0, 1.0); + pixaSetBoxa(pixad, boxad, L_INSERT); + boxaDestroy(&boxas); + } + + return pixad; +} + + +/*---------------------------------------------------------------------* + * Miscellaneous functions * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaAddBorderGeneral() + * + * \param[in] pixad can be null or equal to pixas + * \param[in] pixas containing pix of all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \param[in] val value of added border pixels + * \return pixad with border added to each pix, including on error + * + *
+ * Notes:
+ *      (1) For binary images:
+ *             white:  val = 0
+ *             black:  val = 1
+ *          For grayscale images:
+ *             white:  val = 2 ** d - 1
+ *             black:  val = 0
+ *          For rgb color images:
+ *             white:  val = 0xffffff00
+ *             black:  val = 0
+ *          For colormapped images, use 'index' found this way:
+ *             white: pixcmapGetRankIntensity(cmap, 1.0, &index);
+ *             black: pixcmapGetRankIntensity(cmap, 0.0, &index);
+ *      (2) For in-place replacement of each pix with a bordered version,
+ *          use %pixad = %pixas.  To make a new pixa, use %pixad = NULL.
+ *      (3) In both cases, the boxa has sides adjusted as if it were
+ *          expanded by the border.
+ * 
+ */ +PIXA * +pixaAddBorderGeneral(PIXA *pixad, + PIXA *pixas, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_uint32 val) +{ +l_int32 i, n, nbox; +BOX *box; +BOXA *boxad; +PIX *pixs, *pixd; + + PROCNAME("pixaAddBorderGeneral"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, pixad); + if (left < 0 || right < 0 || top < 0 || bot < 0) + return (PIXA *)ERROR_PTR("negative border added!", procName, pixad); + if (pixad && (pixad != pixas)) + return (PIXA *)ERROR_PTR("pixad defined but != pixas", procName, pixad); + + n = pixaGetCount(pixas); + if (!pixad) + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pixs = pixaGetPix(pixas, i, L_CLONE); + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val); + if (pixad == pixas) /* replace */ + pixaReplacePix(pixad, i, pixd, NULL); + else + pixaAddPix(pixad, pixd, L_INSERT); + pixDestroy(&pixs); + } + + nbox = pixaGetBoxaCount(pixas); + boxad = pixaGetBoxa(pixad, L_CLONE); + for (i = 0; i < nbox; i++) { + if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) { + L_WARNING("box %d not found\n", procName, i); + break; + } + boxAdjustSides(box, box, -left, right, -top, bot); + if (pixad == pixas) /* replace */ + boxaReplaceBox(boxad, i, box); + else + boxaAddBox(boxad, box, L_INSERT); + } + boxaDestroy(&boxad); + + return pixad; +} + + +/*! + * \brief pixaaFlattenToPixa() + * + * \param[in] paa + * \param[out] pnaindex [optional] the pixa index in the pixaa + * \param[in] copyflag L_COPY or L_CLONE + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the pixaa to a pixa, taking the pix in
+ *          order in the first pixa, then the second, etc.
+ *      (2) If &naindex is defined, we generate a Numa that gives, for
+ *          each pix in the pixaa, the index of the pixa to which it belongs.
+ * 
+ */ +PIXA * +pixaaFlattenToPixa(PIXAA *paa, + NUMA **pnaindex, + l_int32 copyflag) +{ +l_int32 i, j, m, mb, n; +BOX *box; +NUMA *naindex; +PIX *pix; +PIXA *pixa, *pixat; + + PROCNAME("pixaaFlattenToPixa"); + + if (pnaindex) *pnaindex = NULL; + if (!paa) + return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); + if (copyflag != L_COPY && copyflag != L_CLONE) + return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); + + if (pnaindex) { + naindex = numaCreate(0); + *pnaindex = naindex; + } + + n = pixaaGetCount(paa, NULL); + pixa = pixaCreate(n); + for (i = 0; i < n; i++) { + pixat = pixaaGetPixa(paa, i, L_CLONE); + m = pixaGetCount(pixat); + mb = pixaGetBoxaCount(pixat); + for (j = 0; j < m; j++) { + pix = pixaGetPix(pixat, j, copyflag); + pixaAddPix(pixa, pix, L_INSERT); + if (j < mb) { + box = pixaGetBox(pixat, j, copyflag); + pixaAddBox(pixa, box, L_INSERT); + } + if (pnaindex) + numaAddNumber(naindex, i); /* save 'row' number */ + } + pixaDestroy(&pixat); + } + + return pixa; +} + + +/*! + * \brief pixaaSizeRange() + * + * \param[in] paa + * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of + * dimensions of all boxes + * \return 0 if OK, 1 on error + */ +l_ok +pixaaSizeRange(PIXAA *paa, + l_int32 *pminw, + l_int32 *pminh, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ +l_int32 minw, minh, maxw, maxh, minpw, minph, maxpw, maxph, i, n; +PIXA *pixa; + + PROCNAME("pixaaSizeRange"); + + if (pminw) *pminw = 0; + if (pminh) *pminh = 0; + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!pminw && !pmaxw && !pminh && !pmaxh) + return ERROR_INT("no data can be returned", procName, 1); + + minw = minh = 100000000; + maxw = maxh = 0; + n = pixaaGetCount(paa, NULL); + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + pixaSizeRange(pixa, &minpw, &minph, &maxpw, &maxph); + if (minpw < minw) + minw = minpw; + if (minph < minh) + minh = minph; + if (maxpw > maxw) + maxw = maxpw; + if (maxph > maxh) + maxh = maxph; + pixaDestroy(&pixa); + } + + if (pminw) *pminw = minw; + if (pminh) *pminh = minh; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + return 0; +} + + +/*! + * \brief pixaSizeRange() + * + * \param[in] pixa + * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of + * dimensions of pix in the array + * \return 0 if OK, 1 on error + */ +l_ok +pixaSizeRange(PIXA *pixa, + l_int32 *pminw, + l_int32 *pminh, + l_int32 *pmaxw, + l_int32 *pmaxh) +{ +l_int32 minw, minh, maxw, maxh, i, n, w, h; +PIX *pix; + + PROCNAME("pixaSizeRange"); + + if (pminw) *pminw = 0; + if (pminh) *pminh = 0; + if (pmaxw) *pmaxw = 0; + if (pmaxh) *pmaxh = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!pminw && !pmaxw && !pminh && !pmaxh) + return ERROR_INT("no data can be returned", procName, 1); + + minw = minh = 1000000; + maxw = maxh = 0; + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + w = pixGetWidth(pix); + h = pixGetHeight(pix); + if (w < minw) + minw = w; + if (h < minh) + minh = h; + if (w > maxw) + maxw = w; + if (h > maxh) + maxh = h; + pixDestroy(&pix); + } + + if (pminw) *pminw = minw; + if (pminh) *pminh = minh; + if (pmaxw) *pmaxw = maxw; + if (pmaxh) *pmaxh = maxh; + + return 0; +} + + +/*! + * \brief pixaClipToPix() + * + * \param[in] pixas + * \param[in] pixs + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) This is intended for use in situations where pixas
+ *          was originally generated from the input pixs.
+ *      (2) Returns a pixad where each pix in pixas is ANDed
+ *          with its associated region of the input pixs.  This
+ *          region is specified by the the box that is associated
+ *          with the pix.
+ *      (3) In a typical application of this function, pixas has
+ *          a set of region masks, so this generates a pixa of
+ *          the parts of pixs that correspond to each region
+ *          mask component, along with the bounding box for
+ *          the region.
+ * 
+ */ +PIXA * +pixaClipToPix(PIXA *pixas, + PIX *pixs) +{ +l_int32 i, n; +BOX *box; +PIX *pix, *pixc; +PIXA *pixad; + + PROCNAME("pixaClipToPix"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + + n = pixaGetCount(pixas); + if ((pixad = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); + + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixas, i, L_CLONE); + box = pixaGetBox(pixas, i, L_COPY); + pixc = pixClipRectangle(pixs, box, NULL); + pixAnd(pixc, pixc, pix); + pixaAddPix(pixad, pixc, L_INSERT); + pixaAddBox(pixad, box, L_INSERT); + pixDestroy(&pix); + } + + return pixad; +} + + +/*! + * \brief pixaClipToForeground() + * + * \param[in] pixas + * \param[out] ppixad [optional] pixa of clipped pix returned + * \param[out] pboxa [optional] clipping boxes returned + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) At least one of [&pixd, &boxa] must be specified.
+ *      (2) Any pix with no fg pixels is skipped.
+ *      (3) See pixClipToForeground().
+ * 
+ */ +l_ok +pixaClipToForeground(PIXA *pixas, + PIXA **ppixad, + BOXA **pboxa) +{ +l_int32 i, n; +BOX *box1; +PIX *pix1, *pix2; + + PROCNAME("pixaClipToForeground"); + + if (ppixad) *ppixad = NULL; + if (pboxa) *pboxa = NULL; + if (!pixas) + return ERROR_INT("pixas not defined", procName, 1); + if (!ppixad && !pboxa) + return ERROR_INT("no output requested", procName, 1); + + n = pixaGetCount(pixas); + if (ppixad) *ppixad = pixaCreate(n); + if (pboxa) *pboxa = boxaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pixClipToForeground(pix1, &pix2, &box1); + pixDestroy(&pix1); + if (ppixad) + pixaAddPix(*ppixad, pix2, L_INSERT); + else + pixDestroy(&pix2); + if (pboxa) + boxaAddBox(*pboxa, box1, L_INSERT); + else + boxDestroy(&box1); + } + + return 0; +} + + +/*! + * \brief pixaGetRenderingDepth() + * + * \param[in] pixa + * \param[out] pdepth depth required to render if all colormaps are removed + * \return 0 if OK; 1 on error + */ +l_ok +pixaGetRenderingDepth(PIXA *pixa, + l_int32 *pdepth) +{ +l_int32 hascolor, maxdepth; + + PROCNAME("pixaGetRenderingDepth"); + + if (!pdepth) + return ERROR_INT("&depth not defined", procName, 1); + *pdepth = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + pixaHasColor(pixa, &hascolor); + if (hascolor) { + *pdepth = 32; + return 0; + } + + pixaGetDepthInfo(pixa, &maxdepth, NULL); + if (maxdepth == 1) + *pdepth = 1; + else /* 2, 4, 8 or 16 */ + *pdepth = 8; + return 0; +} + + +/*! + * \brief pixaHasColor() + * + * \param[in] pixa + * \param[out] phascolor 1 if any pix is rgb or has a colormap with color; + * 0 otherwise + * \return 0 if OK; 1 on error + */ +l_ok +pixaHasColor(PIXA *pixa, + l_int32 *phascolor) +{ +l_int32 i, n, hascolor, d; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixaHasColor"); + + if (!phascolor) + return ERROR_INT("&hascolor not defined", procName, 1); + *phascolor = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + hascolor = 0; + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + if ((cmap = pixGetColormap(pix)) != NULL) + pixcmapHasColor(cmap, &hascolor); + d = pixGetDepth(pix); + pixDestroy(&pix); + if (d == 32 || hascolor == 1) { + *phascolor = 1; + break; + } + } + + return 0; +} + + +/*! + * \brief pixaAnyColormaps() + * + * \param[in] pixa + * \param[out] phascmap 1 if any pix has a colormap; 0 otherwise + * \return 0 if OK; 1 on error + */ +l_ok +pixaAnyColormaps(PIXA *pixa, + l_int32 *phascmap) +{ +l_int32 i, n; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixaAnyColormaps"); + + if (!phascmap) + return ERROR_INT("&hascmap not defined", procName, 1); + *phascmap = 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + cmap = pixGetColormap(pix); + pixDestroy(&pix); + if (cmap) { + *phascmap = 1; + return 0; + } + } + + return 0; +} + + +/*! + * \brief pixaGetDepthInfo() + * + * \param[in] pixa + * \param[out] pmaxdepth [optional] max pixel depth of pix in pixa + * \param[out] psame [optional] true if all depths are equal + * \return 0 if OK; 1 on error + */ +l_ok +pixaGetDepthInfo(PIXA *pixa, + l_int32 *pmaxdepth, + l_int32 *psame) +{ +l_int32 i, n, d, d0; +l_int32 maxd, same; /* depth info */ + + PROCNAME("pixaGetDepthInfo"); + + if (pmaxdepth) *pmaxdepth = 0; + if (psame) *psame = TRUE; + if (!pmaxdepth && !psame) return 0; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if ((n = pixaGetCount(pixa)) == 0) + return ERROR_INT("pixa is empty", procName, 1); + + same = TRUE; + maxd = 0; + for (i = 0; i < n; i++) { + pixaGetPixDimensions(pixa, i, NULL, NULL, &d); + if (i == 0) + d0 = d; + else if (d != d0) + same = FALSE; + if (d > maxd) maxd = d; + } + + if (pmaxdepth) *pmaxdepth = maxd; + if (psame) *psame = same; + return 0; +} + + +/*! + * \brief pixaConvertToSameDepth() + * + * \param[in] pixas + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) If any pix has a colormap, they are all converted to rgb.
+ *          Otherwise, they are all converted to the maximum depth of
+ *          all the pix.
+ *      (2) This can be used to allow lossless rendering onto a single pix.
+ * 
+ */ +PIXA * +pixaConvertToSameDepth(PIXA *pixas) +{ +l_int32 i, n, same, hascmap, maxdepth; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixa1, *pixad; + + PROCNAME("pixaConvertToSameDepth"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + /* Remove colormaps to rgb */ + if ((n = pixaGetCount(pixas)) == 0) + return (PIXA *)ERROR_PTR("no components", procName, NULL); + pixaAnyColormaps(pixas, &hascmap); + if (hascmap) { + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + } else { + pixa1 = pixaCopy(pixas, L_CLONE); + } + + pixaGetDepthInfo(pixa1, &maxdepth, &same); + if (!same) { /* at least one pix has depth < maxdepth */ + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + if (maxdepth <= 8) + pix2 = pixConvertTo8(pix1, 0); + else + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + } else { + pixad = pixaCopy(pixa1, L_CLONE); + } + + boxa = pixaGetBoxa(pixas, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + pixaDestroy(&pixa1); + return pixad; +} + + +/*! + * \brief pixaEqual() + * + * \param[in] pixa1 + * \param[in] pixa2 + * \param[in] maxdist + * \param[out] pnaindex [optional] index array of correspondences + * \param[out] psame 1 if equal; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The two pixa are the "same" if they contain the same
+ *          boxa and the same ordered set of pix.  However, if they
+ *          have boxa, the pix in each pixa can differ in ordering
+ *          by an amount given by the parameter %maxdist.  If they
+ *          don't have a boxa, the %maxdist parameter is ignored,
+ *          and the ordering of the pix must be identical.
+ *      (2) This applies only to boxa geometry, pixels and ordering;
+ *          other fields in the pix are ignored.
+ *      (3) naindex[i] gives the position of the box in pixa2 that
+ *          corresponds to box i in pixa1.  It is only returned if the
+ *          pixa have boxa and the boxa are equal.
+ *      (4) In situations where the ordering is very different, so that
+ *          a large %maxdist is required for "equality", this should be
+ *          implemented with a hash function for efficiency.
+ * 
+ */ +l_ok +pixaEqual(PIXA *pixa1, + PIXA *pixa2, + l_int32 maxdist, + NUMA **pnaindex, + l_int32 *psame) +{ +l_int32 i, j, n, empty1, empty2, same, sameboxa; +BOXA *boxa1, *boxa2; +NUMA *na; +PIX *pix1, *pix2; + + PROCNAME("pixaEqual"); + + if (pnaindex) *pnaindex = NULL; + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + sameboxa = 0; + na = NULL; + if (!pixa1 || !pixa2) + return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); + n = pixaGetCount(pixa1); + if (n != pixaGetCount(pixa2)) + return 0; + + /* If there are no boxes, strict ordering of the pix in each + * pixa is required. */ + boxa1 = pixaGetBoxa(pixa1, L_CLONE); + boxa2 = pixaGetBoxa(pixa2, L_CLONE); + empty1 = (boxaGetCount(boxa1) == 0) ? 1 : 0; + empty2 = (boxaGetCount(boxa2) == 0) ? 1 : 0; + if (!empty1 && !empty2) { + boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa); + if (!sameboxa) { + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + numaDestroy(&na); + return 0; + } + } + boxaDestroy(&boxa1); + boxaDestroy(&boxa2); + if ((!empty1 && empty2) || (empty1 && !empty2)) + return 0; + + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + if (na) + numaGetIValue(na, i, &j); + else + j = i; + pix2 = pixaGetPix(pixa2, j, L_CLONE); + pixEqual(pix1, pix2, &same); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (!same) { + numaDestroy(&na); + return 0; + } + } + + *psame = 1; + if (pnaindex) + *pnaindex = na; + else + numaDestroy(&na); + return 0; +} + + +/*! + * \brief pixaSetFullSizeBoxa() + * + * \param[in] pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Replaces the existing boxa.  Each box gives the dimensions
+ *          of the corresponding pix.  This is needed for functions
+ *          like pixaSort() that sort based on the boxes.
+ * 
+ */ +l_ok +pixaSetFullSizeBoxa(PIXA *pixa) +{ +l_int32 i, n, w, h; +BOX *box; +BOXA *boxa; +PIX *pix; + + PROCNAME("pixaSetFullSizeBoxa"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if ((n = pixaGetCount(pixa)) == 0) { + L_INFO("pixa contains no pix\n", procName); + return 0; + } + + boxa = boxaCreate(n); + pixaSetBoxa(pixa, boxa, L_INSERT); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pix, &w, &h, NULL); + box = boxCreate(0, 0, w, h); + boxaAddBox(boxa, box, L_INSERT); + pixDestroy(&pix); + } + return 0; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixafunc2.c b/hgdriver/3rdparty/hgOCR/leptonica/pixafunc2.c new file mode 100644 index 0000000..9081013 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixafunc2.c @@ -0,0 +1,2768 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixafunc2.c + *
+ *
+ *      Pixa display (render into a pix)
+ *           PIX      *pixaDisplay()
+ *           PIX      *pixaDisplayOnColor()
+ *           PIX      *pixaDisplayRandomCmap()
+ *           PIX      *pixaDisplayLinearly()
+ *           PIX      *pixaDisplayOnLattice()
+ *           PIX      *pixaDisplayUnsplit()
+ *           PIX      *pixaDisplayTiled()
+ *           PIX      *pixaDisplayTiledInRows()
+ *           PIX      *pixaDisplayTiledInColumns()
+ *           PIX      *pixaDisplayTiledAndScaled()
+ *           PIX      *pixaDisplayTiledWithText()
+ *           PIX      *pixaDisplayTiledByIndex()
+ *
+ *      Pixaa display (render into a pix)
+ *           PIX      *pixaaDisplay()
+ *           PIX      *pixaaDisplayByPixa()
+ *           PIXA     *pixaaDisplayTiledAndScaled()
+ *
+ *      Conversion of all pix to specified type (e.g., depth)
+ *           PIXA     *pixaConvertTo1()
+ *           PIXA     *pixaConvertTo8()
+ *           PIXA     *pixaConvertTo8Colormap()
+ *           PIXA     *pixaConvertTo32()
+ *
+ *      Pixa constrained selection and pdf generation
+ *           PIXA     *pixaConstrainedSelect()
+ *           l_int32   pixaSelectToPdf()
+ *
+ *      Generate pixa from tiled images
+ *           PIXA     *pixaMakeFromTiledPixa()
+ *           PIXA     *pixaMakeFromTiledPix()
+ *           l_int32   pixGetTileCount()
+ *
+ *      Pixa display into multiple tiles
+ *           PIXA     *pixaDisplayMultiTiled()
+ *
+ *      Split pixa into files
+ *           l_int32   pixaSplitIntoFiles()
+ *
+ *      Tile N-Up
+ *           l_int32   convertToNUpFiles()
+ *           PIXA     *convertToNUpPixa()
+ *           PIXA     *pixaConvertToNUpPixa()
+ *
+ *      Render two pixa side-by-side for comparison                   *
+ *           l_int32   pixaCompareInPdf()
+ *
+ *  We give twelve pixaDisplay*() methods for tiling a pixa in a pix.
+ *  Some work for 1 bpp input; others for any input depth.
+ *  Some give an output depth that depends on the input depth;
+ *  others give a different output depth or allow you to choose it.
+ *  Some use a boxes to determine where each pix goes; others tile
+ *  onto a regular lattice; others tile onto an irregular lattice;
+ *  one uses an associated index array to determine which column
+ *  each pix goes into.
+ *
+ *  Here is a brief description of what the pixa display functions do.
+ *
+ *    pixaDisplay()
+ *        This uses the boxes in the pixa to lay out each pix.  This
+ *        can be used to reconstruct a pix that has been broken into
+ *        components, if the boxes represents the positions of the
+ *        components in the original image.
+ *    pixaDisplayOnColor()
+ *        pixaDisplay() with choice of background color.
+ *    pixaDisplayRandomCmap()
+ *        This also uses the boxes to lay out each pix.  However, it creates
+ *        a colormapped dest, where each 1 bpp pix is given a randomly
+ *        generated color (up to 256 are used).
+ *    pixaDisplayLinearly()
+ *        This puts each pix, sequentially, in a line, either horizontally
+ *        or vertically.
+ *    pixaDisplayOnLattice()
+ *        This puts each pix, sequentially, onto a regular lattice,
+ *        omitting any pix that are too big for the lattice size.
+ *        This is useful, for example, to store bitmapped fonts,
+ *        where all the characters are stored in a single image.
+ *    pixaDisplayUnsplit()
+ *        This lays out a mosaic of tiles (the pix in the pixa) that
+ *        are all of equal size.  (Don't use this for unequal sized pix!)
+ *        For example, it can be used to invert the action of
+ *        pixaSplitPix().
+ *    pixaDisplayTiled()
+ *        Like pixaDisplayOnLattice(), this places each pix on a regular
+ *        lattice, but here the lattice size is determined by the
+ *        largest component, and no components are omitted.  This is
+ *        dangerous if there are thousands of small components and
+ *        one or more very large one, because the size of the resulting
+ *        pix can be huge!
+ *    pixaDisplayTiledInRows()
+ *        This puts each pix down in a series of rows, where the upper
+ *        edges of each pix in a row are aligned and there is a uniform
+ *        spacing between the pix.  The height of each row is determined
+ *        by the tallest pix that was put in the row.  This function
+ *        is a reasonably efficient way to pack the subimages.
+ *        A boxa of the locations of each input pix is stored in the output.
+ *    pixaDisplayTiledInColumns()
+ *        This puts each pix down in a series of rows, each row having
+ *        a specified number of pix.  The upper edges of each pix in a
+ *        row are aligned and there is a uniform spacing between the pix.
+ *        The height of each row is determined by the tallest pix that
+ *        was put in the row.  A boxa of the locations of each input
+ *        pix is stored in the output.
+ *    pixaDisplayTiledAndScaled()
+ *        This scales each pix to a given width and output depth, and then
+ *        tiles them in rows with a given number placed in each row.
+ *        This is useful for presenting a sequence of images that can be
+ *        at different resolutions, but which are derived from the same
+ *        initial image.
+ *    pixaDisplayTiledWithText()
+ *        This is a version of pixaDisplayTiledInRows() that prints, below
+ *        each pix, the text in the pix text field.  It renders a pixa
+ *        to an image with white background that does not exceed a
+ *        given value in width.
+ *    pixaDisplayTiledByIndex()
+ *        This scales each pix to a given width and output depth,
+ *        and then tiles them in columns corresponding to the value
+ *        in an associated numa.  All pix with the same index value are
+ *        rendered in the same column.  Text in the pix text field are
+ *        rendered below the pix.
+ * 
+ */ + +#include +#include /* for sqrt() */ +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Pixa Display * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaDisplay() + * + * \param[in] pixa + * \param[in] w, h if set to 0, the size is determined from the + * bounding box of the components in pixa + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses the boxes to place each pix in the rendered composite.
+ *      (2) Set w = h = 0 to use the b.b. of the components to determine
+ *          the size of the returned pix.
+ *      (3) Uses the first pix in pixa to determine the depth.
+ *      (4) The background is written "white".  On 1 bpp, each successive
+ *          pix is "painted" (adding foreground), whereas for grayscale
+ *          or color each successive pix is blitted with just the src.
+ *      (5) If the pixa is empty, returns an empty 1 bpp pix.
+ * 
+ */ +PIX * +pixaDisplay(PIXA *pixa, + l_int32 w, + l_int32 h) +{ +l_int32 i, n, d, xb, yb, wb, hb, res; +BOXA *boxa; +PIX *pix1, *pixd; + + PROCNAME("pixaDisplay"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + + n = pixaGetCount(pixa); + if (n == 0 && w == 0 && h == 0) + return (PIX *)ERROR_PTR("no components; no size", procName, NULL); + if (n == 0) { + L_WARNING("no components; returning empty 1 bpp pix\n", procName); + return pixCreate(w, h, 1); + } + + /* If w and h not input, determine the minimum size required + * to contain the origin and all c.c. */ + if (w == 0 || h == 0) { + boxa = pixaGetBoxa(pixa, L_CLONE); + boxaGetExtent(boxa, &w, &h, NULL); + boxaDestroy(&boxa); + if (w == 0 || h == 0) + return (PIX *)ERROR_PTR("no associated boxa", procName, NULL); + } + + /* Use the first pix in pixa to determine depth and resolution */ + pix1 = pixaGetPix(pixa, 0, L_CLONE); + d = pixGetDepth(pix1); + res = pixGetXRes(pix1); + pixDestroy(&pix1); + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSetResolution(pixd, res, res); + if (d > 1) + pixSetAll(pixd); + for (i = 0; i < n; i++) { + if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) { + L_WARNING("no box found!\n", procName); + continue; + } + pix1 = pixaGetPix(pixa, i, L_CLONE); + if (d == 1) + pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix1, 0, 0); + else + pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + } + + return pixd; +} + + +/*! + * \brief pixaDisplayOnColor() + * + * \param[in] pixa + * \param[in] w, h if set to 0, the size is determined from the + * bounding box of the components in pixa + * \param[in] bgcolor background color to use + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses the boxes to place each pix in the rendered composite.
+ *      (2) Set w = h = 0 to use the b.b. of the components to determine
+ *          the size of the returned pix.
+ *      (3) If any pix in %pixa are colormapped, or if the pix have
+ *          different depths, it returns a 32 bpp pix.  Otherwise,
+ *          the depth of the returned pixa equals that of the pix in %pixa.
+ *      (4) If the pixa is empty, return null.
+ * 
+ */ +PIX * +pixaDisplayOnColor(PIXA *pixa, + l_int32 w, + l_int32 h, + l_uint32 bgcolor) +{ +l_int32 i, n, xb, yb, wb, hb, hascmap, maxdepth, same, res; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixat; + + PROCNAME("pixaDisplayOnColor"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + + /* If w and h are not input, determine the minimum size + * required to contain the origin and all c.c. */ + if (w == 0 || h == 0) { + boxa = pixaGetBoxa(pixa, L_CLONE); + boxaGetExtent(boxa, &w, &h, NULL); + boxaDestroy(&boxa); + } + + /* If any pix have colormaps, or if they have different depths, + * generate rgb */ + pixaAnyColormaps(pixa, &hascmap); + pixaGetDepthInfo(pixa, &maxdepth, &same); + if (hascmap || !same) { + maxdepth = 32; + pixat = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixat, pix2, L_INSERT); + pixDestroy(&pix1); + } + } else { + pixat = pixaCopy(pixa, L_CLONE); + } + + /* Make the output pix and set the background color */ + if ((pixd = pixCreate(w, h, maxdepth)) == NULL) { + pixaDestroy(&pixat); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + if ((maxdepth == 1 && bgcolor > 0) || + (maxdepth == 2 && bgcolor >= 0x3) || + (maxdepth == 4 && bgcolor >= 0xf) || + (maxdepth == 8 && bgcolor >= 0xff) || + (maxdepth == 16 && bgcolor >= 0xffff) || + (maxdepth == 32 && bgcolor >= 0xffffff00)) { + pixSetAll(pixd); + } else if (bgcolor > 0) { + pixSetAllArbitrary(pixd, bgcolor); + } + + /* Blit each pix into its place */ + for (i = 0; i < n; i++) { + if (pixaGetBoxGeometry(pixat, i, &xb, &yb, &wb, &hb)) { + L_WARNING("no box found!\n", procName); + continue; + } + pix1 = pixaGetPix(pixat, i, L_CLONE); + if (i == 0) res = pixGetXRes(pix1); + pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + } + pixSetResolution(pixd, res, res); + + pixaDestroy(&pixat); + return pixd; +} + + +/*! + * \brief pixaDisplayRandomCmap() + * + * \param[in] pixa 1 bpp regions, with boxa delineating those regions + * \param[in] w, h if set to 0, the size is determined from the + * bounding box of the components in pixa + * \return pix 8 bpp, cmapped, with random colors assigned to each region, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) This uses the boxes to place each pix in the rendered composite.
+ *          The fg of each pix in %pixa, such as a single connected
+ *          component or a line of text, is given a random color.
+ *      (2) By default, the background color is black (cmap index 0).
+ *          This can be changed by pixcmapResetColor()
+ * 
+ */ +PIX * +pixaDisplayRandomCmap(PIXA *pixa, + l_int32 w, + l_int32 h) +{ +l_int32 i, n, same, maxd, index, xb, yb, wb, hb, res; +BOXA *boxa; +PIX *pixs, *pix1, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixaDisplayRandomCmap"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + pixaVerifyDepth(pixa, &same, &maxd); + if (maxd > 1) + return (PIX *)ERROR_PTR("not all components are 1 bpp", procName, NULL); + + /* If w and h are not input, determine the minimum size required + * to contain the origin and all c.c. */ + if (w == 0 || h == 0) { + boxa = pixaGetBoxa(pixa, L_CLONE); + boxaGetExtent(boxa, &w, &h, NULL); + boxaDestroy(&boxa); + } + + /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */ + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreateRandom(8, 1, 1); + pixSetColormap(pixd, cmap); + + /* Color each component and blit it in */ + for (i = 0; i < n; i++) { + index = 1 + (i % 254); + pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); + pixs = pixaGetPix(pixa, i, L_CLONE); + if (i == 0) res = pixGetXRes(pixs); + pix1 = pixConvert1To8(NULL, pixs, 0, index); + pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix1, 0, 0); + pixDestroy(&pixs); + pixDestroy(&pix1); + } + + pixSetResolution(pixd, res, res); + return pixd; +} + + +/*! + * \brief pixaDisplayLinearly() + * + * \param[in] pixas + * \param[in] direction L_HORIZ or L_VERT + * \param[in] scalefactor applied to every pix; use 1.0 for no scaling + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \param[out] pboxa [optional] location of images in output pix + * \return pix of composite images, or NULL on error + * + *
+ * Notes:
+ *      (1) This puts each pix, sequentially, in a line, either horizontally
+ *          or vertically.
+ *      (2) If any pix has a colormap, all pix are rendered in rgb.
+ *      (3) The boxa gives the location of each image.
+ * 
+ */ +PIX * +pixaDisplayLinearly(PIXA *pixas, + l_int32 direction, + l_float32 scalefactor, + l_int32 background, /* not used */ + l_int32 spacing, + l_int32 border, + BOXA **pboxa) +{ +l_int32 i, n, x, y, w, h, size, depth, bordval; +BOX *box; +PIX *pix1, *pix2, *pix3, *pixd; +PIXA *pixa1, *pixa2; + + PROCNAME("pixaDisplayLinearly"); + + if (pboxa) *pboxa = NULL; + if (!pixas) + return (PIX *)ERROR_PTR("pixas not defined", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + + /* Make sure all pix are at the same depth */ + pixa1 = pixaConvertToSameDepth(pixas); + pixaGetDepthInfo(pixa1, &depth, NULL); + + /* Scale and add border if requested */ + n = pixaGetCount(pixa1); + pixa2 = pixaCreate(n); + bordval = (depth == 1) ? 1 : 0; + size = (n - 1) * spacing; + x = y = 0; + for (i = 0; i < n; i++) { + if ((pix1 = pixaGetPix(pixa1, i, L_CLONE)) == NULL) { + L_WARNING("missing pix at index %d\n", procName, i); + continue; + } + + if (scalefactor != 1.0) + pix2 = pixScale(pix1, scalefactor, scalefactor); + else + pix2 = pixClone(pix1); + if (border) + pix3 = pixAddBorder(pix2, border, bordval); + else + pix3 = pixClone(pix2); + + pixGetDimensions(pix3, &w, &h, NULL); + box = boxCreate(x, y, w, h); + if (direction == L_HORIZ) { + size += w; + x += w + spacing; + } else { /* vertical */ + size += h; + y += h + spacing; + } + pixaAddPix(pixa2, pix3, L_INSERT); + pixaAddBox(pixa2, box, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + pixd = pixaDisplay(pixa2, 0, 0); + + if (pboxa) + *pboxa = pixaGetBoxa(pixa2, L_COPY); + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + return pixd; +} + + +/*! + * \brief pixaDisplayOnLattice() + * + * \param[in] pixa + * \param[in] cellw lattice cell width + * \param[in] cellh lattice cell height + * \param[out] pncols [optional] number of columns in output lattice + * \param[out] pboxa [optional] location of images in lattice + * \return pix of composite images, or NULL on error + * + *
+ * Notes:
+ *      (1) This places each pix on sequentially on a regular lattice
+ *          in the rendered composite.  If a pix is too large to fit in the
+ *          allocated lattice space, it is not rendered.
+ *      (2) If any pix has a colormap, all pix are rendered in rgb.
+ *      (3) This is useful when putting bitmaps of components,
+ *          such as characters, into a single image.
+ *      (4) Save the number of tiled images in the text field of the pix,
+ *          in the format: n = %d.  This survives write/read into png files,
+ *          for example.
+ *      (5) The boxa gives the location of each image.  The UL corner
+ *          of each image is on a lattice cell corner.  Omitted images
+ *          (due to size) are assigned an invalid width and height of 0.
+ * 
+ */ +PIX * +pixaDisplayOnLattice(PIXA *pixa, + l_int32 cellw, + l_int32 cellh, + l_int32 *pncols, + BOXA **pboxa) +{ +char buf[16]; +l_int32 n, nw, nh, w, h, d, wt, ht, res, samedepth; +l_int32 index, i, j, hascmap; +BOX *box; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixa1; + + PROCNAME("pixaDisplayOnLattice"); + + if (pncols) *pncols = 0; + if (pboxa) *pboxa = NULL; + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + + /* If any pix have colormaps, or if the depths differ, generate rgb */ + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + pixaAnyColormaps(pixa, &hascmap); + pixaVerifyDepth(pixa, &samedepth, NULL); + if (hascmap || !samedepth) { + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + } else { + pixa1 = pixaCopy(pixa, L_CLONE); + } + + /* Have number of rows and columns approximately equal */ + nw = (l_int32)sqrt((l_float64)n); + nh = (n + nw - 1) / nw; + w = cellw * nw; + h = cellh * nh; + + /* Use the first pix to determine output depth and resolution */ + pix1 = pixaGetPix(pixa1, 0, L_CLONE); + d = pixGetDepth(pix1); + res = pixGetXRes(pix1); + pixDestroy(&pix1); + if ((pixd = pixCreate(w, h, d)) == NULL) { + pixaDestroy(&pixa1); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixSetBlackOrWhite(pixd, L_SET_WHITE); + pixSetResolution(pixd, res, res); + boxa = boxaCreate(n); + + /* Tile the output */ + index = 0; + for (i = 0; i < nh; i++) { + for (j = 0; j < nw && index < n; j++, index++) { + pix1 = pixaGetPix(pixa1, index, L_CLONE); + pixGetDimensions(pix1, &wt, &ht, NULL); + if (wt > cellw || ht > cellh) { + L_INFO("pix(%d) omitted; size %dx%x\n", procName, index, + wt, ht); + box = boxCreate(0, 0, 0, 0); + boxaAddBox(boxa, box, L_INSERT); + pixDestroy(&pix1); + continue; + } + pixRasterop(pixd, j * cellw, i * cellh, wt, ht, + PIX_SRC, pix1, 0, 0); + box = boxCreate(j * cellw, i * cellh, wt, ht); + boxaAddBox(boxa, box, L_INSERT); + pixDestroy(&pix1); + } + } + + /* Save the number of tiles in the text field */ + snprintf(buf, sizeof(buf), "n = %d", boxaGetCount(boxa)); + pixSetText(pixd, buf); + + if (pncols) *pncols = nw; + if (pboxa) + *pboxa = boxa; + else + boxaDestroy(&boxa); + pixaDestroy(&pixa1); + return pixd; +} + + +/*! + * \brief pixaDisplayUnsplit() + * + * \param[in] pixa + * \param[in] nx number of mosaic cells horizontally + * \param[in] ny number of mosaic cells vertically + * \param[in] borderwidth of added border on all sides + * \param[in] bordercolor in our RGBA format: 0xrrggbbaa + * \return pix of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a logical inverse of pixaSplitPix().  It
+ *          constructs a pix from a mosaic of tiles, all of equal size.
+ *      (2) For added generality, a border of arbitrary color can
+ *          be added to each of the tiles.
+ *      (3) In use, pixa will typically have either been generated
+ *          from pixaSplitPix() or will derived from a pixa that
+ *          was so generated.
+ *      (4) All pix in the pixa must be of equal depth, and, if
+ *          colormapped, have the same colormap.
+ * 
+ */ +PIX * +pixaDisplayUnsplit(PIXA *pixa, + l_int32 nx, + l_int32 ny, + l_int32 borderwidth, + l_uint32 bordercolor) +{ +l_int32 w, h, d, wt, ht; +l_int32 i, j, k, x, y, n; +PIX *pix1, *pixd; + + PROCNAME("pixaDisplayUnsplit"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (nx <= 0 || ny <= 0) + return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL); + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + if (n != nx * ny) + return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL); + borderwidth = L_MAX(0, borderwidth); + + pixaGetPixDimensions(pixa, 0, &wt, &ht, &d); + w = nx * (wt + 2 * borderwidth); + h = ny * (ht + 2 * borderwidth); + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pix1 = pixaGetPix(pixa, 0, L_CLONE); + pixCopyColormap(pixd, pix1); + pixDestroy(&pix1); + if (borderwidth > 0) + pixSetAllArbitrary(pixd, bordercolor); + + y = borderwidth; + for (i = 0, k = 0; i < ny; i++) { + x = borderwidth; + for (j = 0; j < nx; j++, k++) { + pix1 = pixaGetPix(pixa, k, L_CLONE); + pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + x += wt + 2 * borderwidth; + } + y += ht + 2 * borderwidth; + } + + return pixd; +} + + +/*! + * \brief pixaDisplayTiled() + * + * \param[in] pixa + * \param[in] maxwidth of output image + * \param[in] background 0 for white, 1 for black + * \param[in] spacing + * \return pix of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This renders a pixa to a single image of width not to
+ *          exceed maxwidth, with background color either white or black,
+ *          and with each subimage spaced on a regular lattice.
+ *      (2) The lattice size is determined from the largest width and height,
+ *          separately, of all pix in the pixa.
+ *      (3) All pix in the pixa must be of equal depth.
+ *      (4) If any pix has a colormap, all pix are rendered in rgb.
+ *      (5) Careful: because no components are omitted, this is
+ *          dangerous if there are thousands of small components and
+ *          one or more very large one, because the size of the
+ *          resulting pix can be huge!
+ * 
+ */ +PIX * +pixaDisplayTiled(PIXA *pixa, + l_int32 maxwidth, + l_int32 background, + l_int32 spacing) +{ +l_int32 wmax, hmax, wd, hd, d, hascmap, res, same; +l_int32 i, j, n, ni, ncols, nrows; +l_int32 ystart, xstart, wt, ht; +PIX *pix1, *pix2, *pixd; +PIXA *pixa1; + + PROCNAME("pixaDisplayTiled"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + + /* If any pix have colormaps, generate rgb */ + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + pixaAnyColormaps(pixa, &hascmap); + if (hascmap) { + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + } else { + pixa1 = pixaCopy(pixa, L_CLONE); + } + + /* Find the max dimensions and depth subimages */ + pixaGetDepthInfo(pixa1, &d, &same); + if (!same) { + pixaDestroy(&pixa1); + return (PIX *)ERROR_PTR("depths not equal", procName, NULL); + } + pixaSizeRange(pixa1, NULL, NULL, &wmax, &hmax); + + /* Get the number of rows and columns and the output image size */ + spacing = L_MAX(spacing, 0); + ncols = (l_int32)((l_float32)(maxwidth - spacing) / + (l_float32)(wmax + spacing)); + ncols = L_MAX(ncols, 1); + nrows = (n + ncols - 1) / ncols; + wd = wmax * ncols + spacing * (ncols + 1); + hd = hmax * nrows + spacing * (nrows + 1); + if ((pixd = pixCreate(wd, hd, d)) == NULL) { + pixaDestroy(&pixa1); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + /* Reset the background color if necessary */ + if ((background == 1 && d == 1) || (background == 0 && d != 1)) + pixSetAll(pixd); + + /* Blit the images to the dest */ + for (i = 0, ni = 0; i < nrows; i++) { + ystart = spacing + i * (hmax + spacing); + for (j = 0; j < ncols && ni < n; j++, ni++) { + xstart = spacing + j * (wmax + spacing); + pix1 = pixaGetPix(pixa1, ni, L_CLONE); + if (ni == 0) res = pixGetXRes(pix1); + pixGetDimensions(pix1, &wt, &ht, NULL); + pixRasterop(pixd, xstart, ystart, wt, ht, PIX_SRC, pix1, 0, 0); + pixDestroy(&pix1); + } + } + pixSetResolution(pixd, res, res); + + pixaDestroy(&pixa1); + return pixd; +} + + +/*! + * \brief pixaDisplayTiledInRows() + * + * \param[in] pixa + * \param[in] outdepth output depth: 1, 8 or 32 bpp + * \param[in] maxwidth of output image + * \param[in] scalefactor applied to every pix; use 1.0 for no scaling + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \return pixd of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This renders a pixa to a single image of width not to
+ *          exceed maxwidth, with background color either white or black,
+ *          and with each row tiled such that the top of each pix is
+ *          aligned and separated by 'spacing' from the next one.
+ *          A black border can be added to each pix.
+ *      (2) All pix are converted to outdepth; existing colormaps are removed.
+ *      (3) This does a reasonably spacewise-efficient job of laying
+ *          out the individual pix images into a tiled composite.
+ *      (4) A serialized boxa giving the location in pixd of each input
+ *          pix (without added border) is stored in the text string of pixd.
+ *          This allows, e.g., regeneration of a pixa from pixd, using
+ *          pixaCreateFromBoxa().  If there is no scaling and the depth of
+ *          each input pix in the pixa is the same, this tiling operation
+ *          can be inverted using the boxa (except for loss of text in
+ *          each of the input pix):
+ *            pix1 = pixaDisplayTiledInRows(pixa1, 1, 1500, 1.0, 0, 30, 0);
+ *            char *boxatxt = pixGetText(pix1);
+ *            boxa1 = boxaReadMem((l_uint8 *)boxatxt, strlen(boxatxt));
+ *            pixa2 = pixaCreateFromBoxa(pix1, boxa1, 0, 0, NULL);
+ * 
+ */ +PIX * +pixaDisplayTiledInRows(PIXA *pixa, + l_int32 outdepth, + l_int32 maxwidth, + l_float32 scalefactor, + l_int32 background, + l_int32 spacing, + l_int32 border) +{ +l_int32 h; /* cumulative height over all the rows */ +l_int32 w; /* cumulative height in the current row */ +l_int32 bordval, wtry, wt, ht; +l_int32 irow; /* index of current pix in current row */ +l_int32 wmaxrow; /* width of the largest row */ +l_int32 maxh; /* max height in row */ +l_int32 i, j, index, n, x, y, nrows, ninrow, res; +size_t size; +l_uint8 *data; +BOXA *boxa; +NUMA *nainrow; /* number of pix in the row */ +NUMA *namaxh; /* height of max pix in the row */ +PIX *pix, *pixn, *pix1, *pixd; +PIXA *pixan; + + PROCNAME("pixaDisplayTiledInRows"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (outdepth != 1 && outdepth != 8 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); + if (border < 0) + border = 0; + if (scalefactor <= 0.0) scalefactor = 1.0; + + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + + /* Normalize depths, scale, remove colormaps; optionally add border */ + pixan = pixaCreate(n); + bordval = (outdepth == 1) ? 1 : 0; + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + continue; + + if (outdepth == 1) + pixn = pixConvertTo1(pix, 128); + else if (outdepth == 8) + pixn = pixConvertTo8(pix, FALSE); + else /* outdepth == 32 */ + pixn = pixConvertTo32(pix); + pixDestroy(&pix); + + if (scalefactor != 1.0) + pix1 = pixScale(pixn, scalefactor, scalefactor); + else + pix1 = pixClone(pixn); + if (border) + pixd = pixAddBorder(pix1, border, bordval); + else + pixd = pixClone(pix1); + pixDestroy(&pixn); + pixDestroy(&pix1); + + pixaAddPix(pixan, pixd, L_INSERT); + } + if (pixaGetCount(pixan) != n) { + n = pixaGetCount(pixan); + L_WARNING("only got %d components\n", procName, n); + if (n == 0) { + pixaDestroy(&pixan); + return (PIX *)ERROR_PTR("no components", procName, NULL); + } + } + + /* Compute parameters for layout */ + nainrow = numaCreate(0); + namaxh = numaCreate(0); + wmaxrow = 0; + w = h = spacing; + maxh = 0; /* max height in row */ + for (i = 0, irow = 0; i < n; i++, irow++) { + pixaGetPixDimensions(pixan, i, &wt, &ht, NULL); + wtry = w + wt + spacing; + if (wtry > maxwidth) { /* end the current row and start next one */ + numaAddNumber(nainrow, irow); + numaAddNumber(namaxh, maxh); + wmaxrow = L_MAX(wmaxrow, w); + h += maxh + spacing; + irow = 0; + w = wt + 2 * spacing; + maxh = ht; + } else { + w = wtry; + maxh = L_MAX(maxh, ht); + } + } + + /* Enter the parameters for the last row */ + numaAddNumber(nainrow, irow); + numaAddNumber(namaxh, maxh); + wmaxrow = L_MAX(wmaxrow, w); + h += maxh + spacing; + + if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) { + numaDestroy(&nainrow); + numaDestroy(&namaxh); + pixaDestroy(&pixan); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + /* Reset the background color if necessary */ + if ((background == 1 && outdepth == 1) || + (background == 0 && outdepth != 1)) + pixSetAll(pixd); + + /* Blit the images to the dest, and save the boxa identifying + * the image regions that do not include the borders. */ + nrows = numaGetCount(nainrow); + y = spacing; + boxa = boxaCreate(n); + for (i = 0, index = 0; i < nrows; i++) { /* over rows */ + numaGetIValue(nainrow, i, &ninrow); + numaGetIValue(namaxh, i, &maxh); + x = spacing; + for (j = 0; j < ninrow; j++, index++) { /* over pix in row */ + pix = pixaGetPix(pixan, index, L_CLONE); + if (index == 0) { + res = pixGetXRes(pix); + pixSetResolution(pixd, res, res); + } + pixGetDimensions(pix, &wt, &ht, NULL); + boxaAddBox(boxa, boxCreate(x + border, y + border, + wt - 2 * border, ht - 2 *border), L_INSERT); + pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0); + pixDestroy(&pix); + x += wt + spacing; + } + y += maxh + spacing; + } + boxaWriteMem(&data, &size, boxa); + pixSetText(pixd, (char *)data); /* data is ascii */ + LEPT_FREE(data); + boxaDestroy(&boxa); + + numaDestroy(&nainrow); + numaDestroy(&namaxh); + pixaDestroy(&pixan); + return pixd; +} + + +/*! + * \brief pixaDisplayTiledInColumns() + * + * \param[in] pixas + * \param[in] nx number of columns in output image + * \param[in] scalefactor applied to every pix; use 1.0 for no scaling + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \return pixd of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This renders a pixa to a single image with &nx columns of
+ *          subimages.  The background color is white, and each row
+ *          is tiled such that the top of each pix is aligned and
+ *          each pix is separated by 'spacing' from the next one.
+ *          A black border can be added to each pix.
+ *      (2) The output depth is determined by the largest depth
+ *          required by the pix in the pixa.  Colormaps are removed.
+ *      (3) A serialized boxa giving the location in pixd of each input
+ *          pix (without added border) is stored in the text string of pixd.
+ *          This allows, e.g., regeneration of a pixa from pixd, using
+ *          pixaCreateFromBoxa().  If there is no scaling and the depth of
+ *          each input pix in the pixa is the same, this tiling operation
+ *          can be inverted using the boxa (except for loss of text in
+ *          each of the input pix):
+ *            pix1 = pixaDisplayTiledInColumns(pixa1, 3, 1.0, 0, 30, 2);
+ *            char *boxatxt = pixGetText(pix1);
+ *            boxa1 = boxaReadMem((l_uint8 *)boxatxt, strlen(boxatxt));
+ *            pixa2 = pixaCreateFromBoxa(pix1, boxa1, NULL);
+ * 
+ */ +PIX * +pixaDisplayTiledInColumns(PIXA *pixas, + l_int32 nx, + l_float32 scalefactor, + l_int32 spacing, + l_int32 border) +{ +l_int32 i, j, index, n, x, y, nrows, wb, hb, w, h, maxd, maxh, bordval, res; +size_t size; +l_uint8 *data; +BOX *box; +BOXA *boxa; +PIX *pix1, *pix2, *pix3, *pixd; +PIXA *pixa1, *pixa2; + + PROCNAME("pixaDisplayTiledInColumns"); + + if (!pixas) + return (PIX *)ERROR_PTR("pixas not defined", procName, NULL); + if (border < 0) + border = 0; + if (scalefactor <= 0.0) scalefactor = 1.0; + + if ((n = pixaGetCount(pixas)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + + /* Convert to same depth, if necessary */ + pixa1 = pixaConvertToSameDepth(pixas); + pixaGetDepthInfo(pixa1, &maxd, NULL); + + /* Scale and optionally add border */ + pixa2 = pixaCreate(n); + bordval = (maxd == 1) ? 1 : 0; + for (i = 0; i < n; i++) { + if ((pix1 = pixaGetPix(pixa1, i, L_CLONE)) == NULL) + continue; + if (scalefactor != 1.0) + pix2 = pixScale(pix1, scalefactor, scalefactor); + else + pix2 = pixClone(pix1); + if (border) + pix3 = pixAddBorder(pix2, border, bordval); + else + pix3 = pixClone(pix2); + if (i == 0) res = pixGetXRes(pix3); + pixaAddPix(pixa2, pix3, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + pixaDestroy(&pixa1); + if (pixaGetCount(pixa2) != n) { + n = pixaGetCount(pixa2); + L_WARNING("only got %d components\n", procName, n); + if (n == 0) { + pixaDestroy(&pixa2); + return (PIX *)ERROR_PTR("no components", procName, NULL); + } + } + + /* Compute layout parameters and save as a boxa */ + boxa = boxaCreate(n); + nrows = (n + nx - 1) / nx; + y = spacing; + for (i = 0, index = 0; i < nrows; i++) { + x = spacing; + maxh = 0; + for (j = 0; j < nx && index < n; j++) { + pixaGetPixDimensions(pixa2, index, &wb, &hb, NULL); + box = boxCreate(x, y, wb, hb); + boxaAddBox(boxa, box, L_INSERT); + maxh = L_MAX(maxh, hb + spacing); + x += wb + spacing; + index++; + } + y += maxh; + } + pixaSetBoxa(pixa2, boxa, L_INSERT); + + /* Render the output pix */ + boxaGetExtent(boxa, &w, &h, NULL); + pixd = pixaDisplay(pixa2, w + spacing, h + spacing); + pixSetResolution(pixd, res, res); + + /* Save the boxa in the text field of the output pix */ + boxaWriteMem(&data, &size, boxa); + pixSetText(pixd, (char *)data); /* data is ascii */ + LEPT_FREE(data); + + pixaDestroy(&pixa2); + return pixd; +} + + +/*! + * \brief pixaDisplayTiledAndScaled() + * + * \param[in] pixa + * \param[in] outdepth output depth: 1, 8 or 32 bpp + * \param[in] tilewidth each pix is scaled to this width + * \param[in] ncols number of tiles in each row + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \return pix of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This can be used to tile a number of renderings of
+ *          an image that are at different scales and depths.
+ *      (2) Each image, after scaling and optionally adding the
+ *          black border, has width 'tilewidth'.  Thus, the border does
+ *          not affect the spacing between the image tiles.  The
+ *          maximum allowed border width is tilewidth / 5.
+ * 
+ */ +PIX * +pixaDisplayTiledAndScaled(PIXA *pixa, + l_int32 outdepth, + l_int32 tilewidth, + l_int32 ncols, + l_int32 background, + l_int32 spacing, + l_int32 border) +{ +l_int32 x, y, w, h, wd, hd, d, res; +l_int32 i, n, nrows, maxht, ninrow, irow, bordval; +l_int32 *rowht; +l_float32 scalefact; +PIX *pix, *pixn, *pix1, *pixb, *pixd; +PIXA *pixan; + + PROCNAME("pixaDisplayTiledAndScaled"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (outdepth != 1 && outdepth != 8 && outdepth != 32) + return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); + if (ncols <= 0) + return (PIX *)ERROR_PTR("ncols must be > 0", procName, NULL); + if (border < 0 || border > tilewidth / 5) + border = 0; + + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + + /* Normalize scale and depth for each pix; optionally add border */ + pixan = pixaCreate(n); + bordval = (outdepth == 1) ? 1 : 0; + for (i = 0; i < n; i++) { + if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) + continue; + + pixGetDimensions(pix, &w, &h, &d); + scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w; + if (d == 1 && outdepth > 1 && scalefact < 1.0) + pix1 = pixScaleToGray(pix, scalefact); + else + pix1 = pixScale(pix, scalefact, scalefact); + + if (outdepth == 1) + pixn = pixConvertTo1(pix1, 128); + else if (outdepth == 8) + pixn = pixConvertTo8(pix1, FALSE); + else /* outdepth == 32 */ + pixn = pixConvertTo32(pix1); + pixDestroy(&pix1); + + if (border) + pixb = pixAddBorder(pixn, border, bordval); + else + pixb = pixClone(pixn); + + pixaAddPix(pixan, pixb, L_INSERT); + pixDestroy(&pix); + pixDestroy(&pixn); + } + if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */ + pixaDestroy(&pixan); + return (PIX *)ERROR_PTR("no components", procName, NULL); + } + + /* Determine the size of each row and of pixd */ + wd = tilewidth * ncols + spacing * (ncols + 1); + nrows = (n + ncols - 1) / ncols; + if ((rowht = (l_int32 *)LEPT_CALLOC(nrows, sizeof(l_int32))) == NULL) { + pixaDestroy(&pixan); + return (PIX *)ERROR_PTR("rowht array not made", procName, NULL); + } + maxht = 0; + ninrow = 0; + irow = 0; + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixan, i, L_CLONE); + ninrow++; + pixGetDimensions(pix, &w, &h, NULL); + maxht = L_MAX(h, maxht); + if (ninrow == ncols) { + rowht[irow] = maxht; + maxht = ninrow = 0; /* reset */ + irow++; + } + pixDestroy(&pix); + } + if (ninrow > 0) { /* last fencepost */ + rowht[irow] = maxht; + irow++; /* total number of rows */ + } + nrows = irow; + hd = spacing * (nrows + 1); + for (i = 0; i < nrows; i++) + hd += rowht[i]; + + pixd = pixCreate(wd, hd, outdepth); + if ((background == 1 && outdepth == 1) || + (background == 0 && outdepth != 1)) + pixSetAll(pixd); + + /* Now blit images to pixd */ + x = y = spacing; + irow = 0; + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixan, i, L_CLONE); + if (i == 0) { + res = pixGetXRes(pix); + pixSetResolution(pixd, res, res); + } + pixGetDimensions(pix, &w, &h, NULL); + if (i && ((i % ncols) == 0)) { /* start new row */ + x = spacing; + y += spacing + rowht[irow]; + irow++; + } + pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0); + x += tilewidth + spacing; + pixDestroy(&pix); + } + + pixaDestroy(&pixan); + LEPT_FREE(rowht); + return pixd; +} + + +/*! + * \brief pixaDisplayTiledWithText() + * + * \param[in] pixa + * \param[in] maxwidth of output image + * \param[in] scalefactor applied to every pix; use 1.0 for no scaling + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \param[in] fontsize 4, 6, ... 20 + * \param[in] textcolor 0xrrggbb00 + * \return pixd of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a version of pixaDisplayTiledInRows() that prints, below
+ *          each pix, the text in the pix text field.  Up to 127 chars
+ *          of text in the pix text field are rendered below each pix.
+ *      (2) It renders a pixa to a single image of width not to
+ *          exceed %maxwidth, with white background color, with each row
+ *          tiled such that the top of each pix is aligned and separated
+ *          by %spacing from the next one.
+ *      (3) All pix are converted to 32 bpp.
+ *      (4) This does a reasonably spacewise-efficient job of laying
+ *          out the individual pix images into a tiled composite.
+ * 
+ */ +PIX * +pixaDisplayTiledWithText(PIXA *pixa, + l_int32 maxwidth, + l_float32 scalefactor, + l_int32 spacing, + l_int32 border, + l_int32 fontsize, + l_uint32 textcolor) +{ +char buf[128]; +char *textstr; +l_int32 i, n, maxw; +L_BMF *bmf; +PIX *pix1, *pix2, *pix3, *pix4, *pixd; +PIXA *pixad; + + PROCNAME("pixaDisplayTiledWithText"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + if (maxwidth <= 0) + return (PIX *)ERROR_PTR("invalid maxwidth", procName, NULL); + if (border < 0) + border = 0; + if (scalefactor <= 0.0) { + L_WARNING("invalid scalefactor; setting to 1.0\n", procName); + scalefactor = 1.0; + } + if (fontsize < 4 || fontsize > 20 || (fontsize & 1)) { + l_int32 fsize = L_MAX(L_MIN(fontsize, 20), 4); + if (fsize & 1) fsize--; + L_WARNING("changed fontsize from %d to %d\n", procName, + fontsize, fsize); + fontsize = fsize; + } + + /* Be sure the width can accommodate a single column of images */ + pixaSizeRange(pixa, NULL, NULL, &maxw, NULL); + maxwidth = L_MAX(maxwidth, scalefactor * (maxw + 2 * spacing + 2 * border)); + + bmf = bmfCreate(NULL, fontsize); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pix3 = pixAddBorderGeneral(pix2, spacing, spacing, spacing, + spacing, 0xffffff00); + textstr = pixGetText(pix1); + if (textstr && strlen(textstr) > 0) { + snprintf(buf, sizeof(buf), "%s", textstr); + pix4 = pixAddSingleTextblock(pix3, bmf, buf, textcolor, + L_ADD_BELOW, NULL); + } else { + pix4 = pixClone(pix3); + } + pixaAddPix(pixad, pix4, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + } + bmfDestroy(&bmf); + + pixd = pixaDisplayTiledInRows(pixad, 32, maxwidth, scalefactor, + 0, 10, border); + pixaDestroy(&pixad); + return pixd; +} + + +/*! + * \brief pixaDisplayTiledByIndex() + * + * \param[in] pixa + * \param[in] na numa with indices corresponding to the pix in pixa + * \param[in] width each pix is scaled to this width + * \param[in] spacing between images, and on outside + * \param[in] border width of black border added to each image; + * use 0 for no border + * \param[in] fontsize 4, 6, ... 20 + * \param[in] textcolor 0xrrggbb00 + * \return pixd of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This renders a pixa to a single image with white
+ *          background color, where the pix are placed in columns
+ *          given by the index value in the numa.  Each pix
+ *          is separated by %spacing from the adjacent ones, and
+ *          an optional border is placed around them.
+ *      (2) Up to 127 chars of text in the pix text field are rendered
+ *          below each pix.  Use newlines in the text field to write
+ *          the text in multiple lines that fit within the pix width.
+ *      (3) To avoid having empty columns, if there are N different
+ *          index values, they should be in [0 ... N-1].
+ *      (4) All pix are converted to 32 bpp.
+ * 
+ */ +PIX * +pixaDisplayTiledByIndex(PIXA *pixa, + NUMA *na, + l_int32 width, + l_int32 spacing, + l_int32 border, + l_int32 fontsize, + l_uint32 textcolor) +{ +char buf[128]; +char *textstr; +l_int32 i, n, x, y, w, h, yval, index; +l_float32 maxindex; +L_BMF *bmf; +BOX *box; +NUMA *nay; /* top of the next pix to add in that column */ +PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixd; +PIXA *pixad; + + PROCNAME("pixaDisplayTiledByIndex"); + + if (!pixa) + return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); + if (!na) + return (PIX *)ERROR_PTR("na not defined", procName, NULL); + if ((n = pixaGetCount(pixa)) == 0) + return (PIX *)ERROR_PTR("no pixa components", procName, NULL); + if (n != numaGetCount(na)) + return (PIX *)ERROR_PTR("pixa and na counts differ", procName, NULL); + if (width <= 0) + return (PIX *)ERROR_PTR("invalid width", procName, NULL); + if (width < 20) + L_WARNING("very small width: %d\n", procName, width); + if (border < 0) + border = 0; + if (fontsize < 4 || fontsize > 20 || (fontsize & 1)) { + l_int32 fsize = L_MAX(L_MIN(fontsize, 20), 4); + if (fsize & 1) fsize--; + L_WARNING("changed fontsize from %d to %d\n", procName, + fontsize, fsize); + fontsize = fsize; + } + + /* The pix will be rendered in the order they occupy in pixa. */ + bmf = bmfCreate(NULL, fontsize); + pixad = pixaCreate(n); + numaGetMax(na, &maxindex, NULL); + nay = numaMakeConstant(spacing, lept_roundftoi(maxindex) + 1); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &index); + numaGetIValue(nay, index, &yval); + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pix3 = pixScaleToSize(pix2, width, 0); + pix4 = pixAddBorderGeneral(pix3, border, border, border, border, 0); + textstr = pixGetText(pix1); + if (textstr && strlen(textstr) > 0) { + snprintf(buf, sizeof(buf), "%s", textstr); + pix5 = pixAddTextlines(pix4, bmf, textstr, textcolor, L_ADD_BELOW); + } else { + pix5 = pixClone(pix4); + } + pixaAddPix(pixad, pix5, L_INSERT); + x = spacing + border + index * (2 * border + width + spacing); + y = yval; + pixGetDimensions(pix5, &w, &h, NULL); + yval += h + spacing; + numaSetValue(nay, index, yval); + box = boxCreate(x, y, w, h); + pixaAddBox(pixad, box, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + } + numaDestroy(&nay); + bmfDestroy(&bmf); + + pixd = pixaDisplay(pixad, 0, 0); + pixaDestroy(&pixad); + return pixd; +} + + + +/*---------------------------------------------------------------------* + * Pixaa Display * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaaDisplay() + * + * \param[in] paa + * \param[in] w, h if set to 0, the size is determined from the + * bounding box of the components in pixa + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) Each pix of the paa is displayed at the location given by
+ *          its box, translated by the box of the containing pixa
+ *          if it exists.
+ * 
+ */ +PIX * +pixaaDisplay(PIXAA *paa, + l_int32 w, + l_int32 h) +{ +l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb; +BOXA *boxa1; /* top-level boxa */ +BOXA *boxa; +PIX *pix1, *pixd; +PIXA *pixa; + + PROCNAME("pixaaDisplay"); + + if (!paa) + return (PIX *)ERROR_PTR("paa not defined", procName, NULL); + + n = pixaaGetCount(paa, NULL); + if (n == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + + /* If w and h not input, determine the minimum size required + * to contain the origin and all c.c. */ + boxa1 = pixaaGetBoxa(paa, L_CLONE); + nbox = boxaGetCount(boxa1); + if (w == 0 || h == 0) { + if (nbox == n) { + boxaGetExtent(boxa1, &w, &h, NULL); + } else { /* have to use the lower-level boxa for each pixa */ + wmax = hmax = 0; + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + boxa = pixaGetBoxa(pixa, L_CLONE); + boxaGetExtent(boxa, &w, &h, NULL); + wmax = L_MAX(wmax, w); + hmax = L_MAX(hmax, h); + pixaDestroy(&pixa); + boxaDestroy(&boxa); + } + w = wmax; + h = hmax; + } + } + + /* Get depth from first pix */ + pixa = pixaaGetPixa(paa, 0, L_CLONE); + pix1 = pixaGetPix(pixa, 0, L_CLONE); + d = pixGetDepth(pix1); + pixaDestroy(&pixa); + pixDestroy(&pix1); + + if ((pixd = pixCreate(w, h, d)) == NULL) { + boxaDestroy(&boxa1); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + x = y = 0; + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + if (nbox == n) + boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL); + na = pixaGetCount(pixa); + for (j = 0; j < na; j++) { + pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb); + pix1 = pixaGetPix(pixa, j, L_CLONE); + pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pix1, 0, 0); + pixDestroy(&pix1); + } + pixaDestroy(&pixa); + } + boxaDestroy(&boxa1); + + return pixd; +} + + +/*! + * \brief pixaaDisplayByPixa() + * + * \param[in] paa with pix that may have different depths + * \param[in] xspace between pix in pixa + * \param[in] yspace between pixa + * \param[in] maxw max width of output pix + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Displays each pixa on a line (or set of lines),
+ *          in order from top to bottom.  Within each pixa,
+ *          the pix are displayed in order from left to right.
+ *      (2) The sizes and depths of each pix can differ.  The output pix
+ *          has a depth equal to the max depth of all the pix.
+ *      (3) This ignores the boxa of the paa.
+ * 
+ */ +PIX * +pixaaDisplayByPixa(PIXAA *paa, + l_int32 xspace, + l_int32 yspace, + l_int32 maxw) +{ +l_int32 i, j, npixa, npix, same, use_maxw, x, y, w, h, hindex; +l_int32 maxwidth, maxd, width, lmaxh, lmaxw; +l_int32 *harray; +NUMA *nah; +PIX *pix, *pix1, *pixd; +PIXA *pixa; + + PROCNAME("pixaaDisplayByPixa"); + + if (!paa) + return (PIX *)ERROR_PTR("paa not defined", procName, NULL); + + if ((npixa = pixaaGetCount(paa, NULL)) == 0) + return (PIX *)ERROR_PTR("no components", procName, NULL); + pixaaVerifyDepth(paa, &same, &maxd); + if (!same && maxd < 8) + return (PIX *)ERROR_PTR("depths differ; max < 8", procName, NULL); + + /* Be sure the widest box fits in the output pix */ + pixaaSizeRange(paa, NULL, NULL, &maxwidth, NULL); + if (maxwidth > maxw) { + L_WARNING("maxwidth > maxw; using maxwidth\n", procName); + maxw = maxwidth; + } + + /* Get size of output pix. The width is the minimum of the + * maxw and the largest pixa line width. The height is whatever + * it needs to be to accommodate all pixa. */ + lmaxw = 0; /* widest line found */ + use_maxw = FALSE; + nah = numaCreate(0); /* store height of each line */ + y = yspace; + for (i = 0; i < npixa; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + npix = pixaGetCount(pixa); + if (npix == 0) { + pixaDestroy(&pixa); + continue; + } + x = xspace; + lmaxh = 0; /* max height found in the line */ + for (j = 0; j < npix; j++) { + pix = pixaGetPix(pixa, j, L_CLONE); + pixGetDimensions(pix, &w, &h, NULL); + if (x + w >= maxw) { /* start new line */ + x = xspace; + y += lmaxh + yspace; + numaAddNumber(nah, lmaxh); + lmaxh = 0; + use_maxw = TRUE; + } + x += w + xspace; + lmaxh = L_MAX(h, lmaxh); + lmaxw = L_MAX(lmaxw, x); + pixDestroy(&pix); + } + y += lmaxh + yspace; + numaAddNumber(nah, lmaxh); + pixaDestroy(&pixa); + } + width = (use_maxw) ? maxw : lmaxw; + + if ((pixd = pixCreate(width, y, maxd)) == NULL) { + numaDestroy(&nah); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + + /* Now layout the pix by pixa */ + y = yspace; + harray = numaGetIArray(nah); + hindex = 0; + for (i = 0; i < npixa; i++) { + x = xspace; + pixa = pixaaGetPixa(paa, i, L_CLONE); + npix = pixaGetCount(pixa); + if (npix == 0) { + pixaDestroy(&pixa); + continue; + } + for (j = 0; j < npix; j++) { + pix = pixaGetPix(pixa, j, L_CLONE); + if (pixGetDepth(pix) != maxd) { + if (maxd == 8) + pix1 = pixConvertTo8(pix, 0); + else /* 32 bpp */ + pix1 = pixConvertTo32(pix); + } else { + pix1 = pixClone(pix); + } + pixGetDimensions(pix1, &w, &h, NULL); + if (x + w >= maxw) { /* start new line */ + x = xspace; + y += harray[hindex++] + yspace; + } + pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix1, 0, 0); + pixDestroy(&pix); + pixDestroy(&pix1); + x += w + xspace; + } + y += harray[hindex++] + yspace; + pixaDestroy(&pixa); + } + LEPT_FREE(harray); + + numaDestroy(&nah); + return pixd; +} + + +/*! + * \brief pixaaDisplayTiledAndScaled() + * + * \param[in] paa + * \param[in] outdepth output depth: 1, 8 or 32 bpp + * \param[in] tilewidth each pix is scaled to this width + * \param[in] ncols number of tiles in each row + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \return pixa of tiled images, one image for each pixa in + * the paa, or NULL on error + * + *
+ * Notes:
+ *      (1) For each pixa, this generates from all the pix a
+ *          tiled/scaled output pix, and puts it in the output pixa.
+ *      (2) See comments in pixaDisplayTiledAndScaled().
+ * 
+ */ +PIXA * +pixaaDisplayTiledAndScaled(PIXAA *paa, + l_int32 outdepth, + l_int32 tilewidth, + l_int32 ncols, + l_int32 background, + l_int32 spacing, + l_int32 border) +{ +l_int32 i, n; +PIX *pix; +PIXA *pixa, *pixad; + + PROCNAME("pixaaDisplayTiledAndScaled"); + + if (!paa) + return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); + if (outdepth != 1 && outdepth != 8 && outdepth != 32) + return (PIXA *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); + if (ncols <= 0) + return (PIXA *)ERROR_PTR("ncols must be > 0", procName, NULL); + if (border < 0 || border > tilewidth / 5) + border = 0; + + if ((n = pixaaGetCount(paa, NULL)) == 0) + return (PIXA *)ERROR_PTR("no components", procName, NULL); + + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + pix = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols, + background, spacing, border); + pixaAddPix(pixad, pix, L_INSERT); + pixaDestroy(&pixa); + } + + return pixad; +} + + +/*---------------------------------------------------------------------* + * Conversion of all pix to specified type (e.g., depth) * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaConvertTo1() + * + * \param[in] pixas + * \param[in] thresh threshold for final binarization from 8 bpp gray + * \return pixad, or NULL on error + */ +PIXA * +pixaConvertTo1(PIXA *pixas, + l_int32 thresh) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaConvertTo1"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo1(pix1, thresh); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa = pixaGetBoxa(pixas, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + return pixad; +} + + +/*! + * \brief pixaConvertTo8() + * + * \param[in] pixas + * \param[in] cmapflag 1 to give pixd a colormap; 0 otherwise + * \return pixad each pix is 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes for pixConvertTo8(), applied to each pix in pixas.
+ * 
+ */ +PIXA * +pixaConvertTo8(PIXA *pixas, + l_int32 cmapflag) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaConvertTo8"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo8(pix1, cmapflag); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa = pixaGetBoxa(pixas, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + return pixad; +} + + +/*! + * \brief pixaConvertTo8Colormap() + * + * \param[in] pixas + * \param[in] dither 1 to dither if necessary; 0 otherwise + * \return pixad each pix is 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes for pixConvertTo8Colormap(), applied to each pix in pixas.
+ * 
+ */ +PIXA * +pixaConvertTo8Colormap(PIXA *pixas, + l_int32 dither) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaConvertTo8Colormap"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo8Colormap(pix1, dither); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa = pixaGetBoxa(pixas, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + return pixad; +} + + +/*! + * \brief pixaConvertTo32() + * + * \param[in] pixas + * \return pixad 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes for pixConvertTo32(), applied to each pix in pixas.
+ *      (2) This can be used to allow 1 bpp pix in a pixa to be displayed
+ *          with color.
+ * 
+ */ +PIXA * +pixaConvertTo32(PIXA *pixas) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaConvertTo32"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + boxa = pixaGetBoxa(pixas, L_COPY); + pixaSetBoxa(pixad, boxa, L_INSERT); + return pixad; +} + + +/*---------------------------------------------------------------------* + * Pixa constrained selection * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaConstrainedSelect() + * + * \param[in] pixas + * \param[in] first first index to choose; >= 0 + * \param[in] last biggest possible index to reach; + * use -1 to go to the end; otherwise, last >= first + * \param[in] nmax maximum number of pix to select; > 0 + * \param[in] use_pairs 1 = select pairs of adjacent pix; + * 0 = select individual pix + * \param[in] copyflag L_COPY, L_CLONE + * \return pixad if OK, NULL on error + * + *
+ * Notes:
+ *     (1) See notes in genConstrainedNumaInRange() for how selection
+ *         is made.
+ *     (2) This returns a selection of the pix in the input pixa.
+ *     (3) Use copyflag == L_COPY if you don't want changes in the pix
+ *         in the returned pixa to affect those in the input pixa.
+ * 
+ */ +PIXA * +pixaConstrainedSelect(PIXA *pixas, + l_int32 first, + l_int32 last, + l_int32 nmax, + l_int32 use_pairs, + l_int32 copyflag) +{ +l_int32 i, n, nselect, index; +NUMA *na; +PIX *pix1; +PIXA *pixad; + + PROCNAME("pixaConstrainedSelect"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + n = pixaGetCount(pixas); + first = L_MAX(0, first); + last = (last < 0) ? n - 1 : L_MIN(n - 1, last); + if (last < first) + return (PIXA *)ERROR_PTR("last < first!", procName, NULL); + if (nmax < 1) + return (PIXA *)ERROR_PTR("nmax < 1!", procName, NULL); + + na = genConstrainedNumaInRange(first, last, nmax, use_pairs); + nselect = numaGetCount(na); + pixad = pixaCreate(nselect); + for (i = 0; i < nselect; i++) { + numaGetIValue(na, i, &index); + pix1 = pixaGetPix(pixas, index, copyflag); + pixaAddPix(pixad, pix1, L_INSERT); + } + numaDestroy(&na); + return pixad; +} + + +/*! + * \brief pixaSelectToPdf() + * + * \param[in] pixas + * \param[in] first first index to choose; >= 0 + * \param[in] last biggest possible index to reach; + * use -1 to go to the end; otherwise, last >= first + * \param[in] res override the resolution of each input image, in ppi; + * use 0 to respect the resolution embedded in the input + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, or 0 for default + * \param[in] quality used for JPEG only; 0 for default (75) + * \param[in] color of numbers added to each image (e.g., 0xff000000) + * \param[in] fontsize to print number below each image. The valid set + * is {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This writes a pdf of the selected images from %pixas, one to
+ *          a page.  They are optionally scaled and annotated with the
+ *          index printed to the left of the image.
+ *      (2) If the input images are 1 bpp and you want the numbers to be
+ *          in color, first promote each pix to 8 bpp with a colormap:
+ *                pixa1 = pixaConvertTo8(pixas, 1);
+ *          and then call this function with the specified color
+ * 
+ */ +l_ok +pixaSelectToPdf(PIXA *pixas, + l_int32 first, + l_int32 last, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + l_uint32 color, + l_int32 fontsize, + const char *fileout) +{ +l_int32 n; +L_BMF *bmf; +NUMA *na; +PIXA *pixa1, *pixa2; + + PROCNAME("pixaSelectToPdf"); + + if (!pixas) + return ERROR_INT("pixas not defined", procName, 1); + if (type < 0 || type > L_FLATE_ENCODE) { + L_WARNING("invalid compression type; using default\n", procName); + type = 0; + } + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + /* Select from given range */ + n = pixaGetCount(pixas); + first = L_MAX(0, first); + last = (last < 0) ? n - 1 : L_MIN(n - 1, last); + if (first > last) { + L_ERROR("first = %d > last = %d\n", procName, first, last); + return 1; + } + pixa1 = pixaSelectRange(pixas, first, last, L_CLONE); + + /* Optionally add index numbers */ + bmf = (fontsize <= 0) ? NULL : bmfCreate(NULL, fontsize); + if (bmf) { + na = numaMakeSequence(first, 1.0, last - first + 1); + pixa2 = pixaAddTextNumber(pixa1, bmf, na, color, L_ADD_LEFT); + numaDestroy(&na); + } else { + pixa2 = pixaCopy(pixa1, L_CLONE); + } + pixaDestroy(&pixa1); + bmfDestroy(&bmf); + + pixaConvertToPdf(pixa2, res, scalefactor, type, quality, NULL, fileout); + pixaDestroy(&pixa2); + return 0; +} + + +/*---------------------------------------------------------------------* + * Generate pixa from tiled images * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaMakeFromTiledPixa() + * + * \param[in] pixas of mosaiced templates, one for each digit + * \param[in] w width of samples (use 0 for default = 20) + * \param[in] h height of samples (use 0 for default = 30) + * \param[in] nsamp number of requested samples (use 0 for default = 100) + * \return pixa of individual, scaled templates, or NULL on error + * + *
+ * Notes:
+ *      (1) This converts from a compressed representation of 1 bpp digit
+ *          templates to a pixa where each pix has a single labeled template.
+ *      (2) The mosaics hold 100 templates each, and the number of templates
+ *          %nsamp selected for each digit can be between 1 and 100.
+ *      (3) Each mosaic has the number of images written in the text field,
+ *          and the i-th pix contains samples of the i-th digit.  That value
+ *          is written into the text field of each template in the output.
+ * 
+ */ +PIXA * +pixaMakeFromTiledPixa(PIXA *pixas, + l_int32 w, + l_int32 h, + l_int32 nsamp) +{ +char buf[8]; +l_int32 ntiles, i; +PIX *pix1; +PIXA *pixad, *pixa1; + + PROCNAME("pixaMakeFromTiledPixa"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (nsamp > 1000) + return (PIXA *)ERROR_PTR("nsamp too large; typ. 100", procName, NULL); + + if (w <= 0) w = 20; + if (h <= 0) h = 30; + if (nsamp <= 0) nsamp = 100; + + /* pixas has 10 pix of mosaic'd digits. Each of these images + * must be extracted into a pixa of templates, where each template + * is labeled with the digit value, and then selectively + * concatenated into an output pixa. */ + pixad = pixaCreate(10 * nsamp); + for (i = 0; i < 10; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pixGetTileCount(pix1, &ntiles); + if (nsamp > ntiles) + L_WARNING("requested %d; only %d tiles\n", procName, nsamp, ntiles); + pixa1 = pixaMakeFromTiledPix(pix1, w, h, 0, nsamp, NULL); + snprintf(buf, sizeof(buf), "%d", i); + pixaSetText(pixa1, buf, NULL); + pixaJoin(pixad, pixa1, 0, -1); + pixaDestroy(&pixa1); + pixDestroy(&pix1); + } + return pixad; +} + + +/*! + * \brief pixaMakeFromTiledPix() + * + * \param[in] pixs any depth; colormap OK + * \param[in] w width of each tile + * \param[in] h height of each tile + * \param[in] start first tile to use + * \param[in] num number of tiles; use 0 to go to the end + * \param[in] boxa [optional] location of rectangular regions + * to be extracted + * \return pixa if OK, NULL on error + * + *
+ * Notes:
+ *      (1) Operations that generate a pix by tiling from a pixa, and
+ *          the inverse that generate a pixa from tiles of a pix,
+ *          are useful.  One such pair is pixaDisplayUnsplit() and
+ *          pixaSplitPix().  This function is a very simple one that
+ *          generates a pixa from tiles of a pix. There are two cases:
+ *            - the tiles can all be the same size (the inverse of
+ *              pixaDisplayOnLattice(), or
+ *            - the tiles can differ in size, where there is an
+ *              associated boxa (the inverse of pixaCreateFromBoxa().
+ *      (2) If all tiles are the same size, %w by %h, use %boxa = NULL.
+ *          If the tiles differ in size, use %boxa to extract the
+ *          individual images (%w and %h are then ignored).
+ *      (3) If the pix was made by pixaDisplayOnLattice(), the number
+ *          of tiled images is written into the text field, in the format
+ *               n = .
+ *      (4) Typical usage: a set of character templates all scaled to
+ *          the same size can be stored on a lattice of that size in
+ *          a pix, and this function can regenerate the pixa.  If the
+ *          templates differ in size, a boxa generated when the tiled
+ *          pix was made can be used to indicate the location of
+ *          the templates.
+ * 
+ */ +PIXA * +pixaMakeFromTiledPix(PIX *pixs, + l_int32 w, + l_int32 h, + l_int32 start, + l_int32 num, + BOXA *boxa) +{ +l_int32 i, j, k, ws, hs, d, nx, ny, n, n_isvalid, ntiles, nmax; +PIX *pix1; +PIXA *pixa1; +PIXCMAP *cmap; + + PROCNAME("pixaMakeFromTiledPix"); + + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (!boxa && (w <= 0 || h <= 0)) + return (PIXA *)ERROR_PTR("w and h must be > 0", procName, NULL); + + if (boxa) /* general case */ + return pixaCreateFromBoxa(pixs, boxa, start, num, NULL); + + /* All tiles are the same size */ + pixGetDimensions(pixs, &ws, &hs, &d); + nx = ws / w; + ny = hs / h; + if (nx < 1 || ny < 1) + return (PIXA *)ERROR_PTR("invalid dimensions", procName, NULL); + if (nx * w != ws || ny * h != hs) + L_WARNING("some tiles will be clipped\n", procName); + + /* Check the text field of the pix. It may tell how many + * tiles hold valid data. If a valid value is not found, + * assume all (nx * ny) tiles are valid. */ + pixGetTileCount(pixs, &n); + n_isvalid = (n <= nx * ny && n > nx * (ny - 1)) ? TRUE : FALSE; + ntiles = (n_isvalid) ? n : nx * ny; + nmax = ntiles - start; /* max available from start */ + num = (num == 0) ? nmax : L_MIN(num, nmax); + + /* Extract the tiles */ + if ((pixa1 = pixaCreate(num)) == NULL) { + return (PIXA *)ERROR_PTR("pixa1 not made", procName, NULL); + } + cmap = pixGetColormap(pixs); + for (i = 0, k = 0; i < ny; i++) { + for (j = 0; j < nx; j++, k++) { + if (k < start) continue; + if (k >= start + num) break; + pix1 = pixCreate(w, h, d); + if (cmap) pixSetColormap(pix1, pixcmapCopy(cmap)); + pixRasterop(pix1, 0, 0, w, h, PIX_SRC, pixs, j * w, i * h); + pixaAddPix(pixa1, pix1, L_INSERT); + } + } + return pixa1; +} + + +/*! + * \brief pixGetTileCount() + * + * \param[in] pix + * \param[out] *pn number embedded in pix text field + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If the pix was made by pixaDisplayOnLattice(), the number
+ *          of tiled images is written into the text field, in the format
+ *               n = .
+ *      (2) This returns 0 if the data is not in the text field, or on error.
+ * 
+ */ +l_ok +pixGetTileCount(PIX *pix, + l_int32 *pn) +{ +char *text; +l_int32 n; + + PROCNAME("pixGetTileCount"); + + if (!pn) + return ERROR_INT("&n not defined", procName, 1); + *pn = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + text = pixGetText(pix); + if (text && strlen(text) > 4) { + if (sscanf(text, "n = %d", &n) == 1) + *pn = n; + } + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixa display into multiple tiles * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaDisplayMultiTiled() + * + * \param[in] pixas + * \param[in] nx, ny in [1, ... 50], tiling factors in each direction + * \param[in] maxw, maxh max sizes to keep + * \param[in] scalefactor scale each image by this + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \return pixad if OK, NULL on error + * + *
+ * Notes:
+ *      (1) Each set of %nx * %ny images is optionally scaled and saved
+ *          into a new pix, and then aggregated.
+ *      (2) Set %maxw = %maxh = 0 if you want to include all pix from %pixs.
+ *      (3) This is useful for generating a pdf from the output pixa, where
+ *          each page is a tile of (%nx * %ny) images from the input pixa.
+ * 
+ */ +PIXA * +pixaDisplayMultiTiled(PIXA *pixas, + l_int32 nx, + l_int32 ny, + l_int32 maxw, + l_int32 maxh, + l_float32 scalefactor, + l_int32 spacing, + l_int32 border) +{ +l_int32 n, i, j, ntile, nout, index; +PIX *pix1, *pix2; +PIXA *pixa1, *pixa2, *pixad; + + PROCNAME("pixaDisplayMultiTiled"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (nx < 1 || ny < 1 || nx > 50 || ny > 50) + return (PIXA *)ERROR_PTR("invalid tiling factor(s)", procName, NULL); + if ((n = pixaGetCount(pixas)) == 0) + return (PIXA *)ERROR_PTR("pixas is empty", procName, NULL); + + /* Filter out large ones if requested */ + if (maxw == 0 && maxh == 0) { + pixa1 = pixaCopy(pixas, L_CLONE); + } else { + maxw = (maxw == 0) ? 1000000 : maxw; + maxh = (maxh == 0) ? 1000000 : maxh; + pixa1 = pixaSelectBySize(pixas, maxw, maxh, L_SELECT_IF_BOTH, + L_SELECT_IF_LTE, NULL); + n = pixaGetCount(pixa1); + } + + ntile = nx * ny; + nout = L_MAX(1, (n + ntile - 1) / ntile); + pixad = pixaCreate(nout); + for (i = 0, index = 0; i < nout; i++) { /* over tiles */ + pixa2 = pixaCreate(ntile); + for (j = 0; j < ntile && index < n; j++, index++) { + pix1 = pixaGetPix(pixa1, index, L_COPY); + pixaAddPix(pixa2, pix1, L_INSERT); + } + pix2 = pixaDisplayTiledInColumns(pixa2, nx, scalefactor, spacing, + border); + pixaAddPix(pixad, pix2, L_INSERT); + pixaDestroy(&pixa2); + } + pixaDestroy(&pixa1); + + return pixad; +} + + +/*---------------------------------------------------------------------* + * Split pixa into files * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaSplitIntoFiles() + * + * \param[in] pixas + * \param[in] nsplit split pixas into this number of pixa; >= 2 + * \param[in] scale scalefactor applied to each pix + * \param[in] outwidth the maxwidth parameter of tiled images + * for write_pix + * \param[in] write_pixa 1 to write the split pixa as separate files + * \param[in] write_pix 1 to write tiled images of the split pixa + * \param[in] write_pdf 1 to write pdfs of the split pixa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For each requested output, %nsplit files are written into
+ *          directory /tmp/lept/split/.
+ *      (2) This is useful when a pixa is so large that the images
+ *          are not conveniently displayed as a single tiled image at
+ *          full resolution.
+ * 
+ */ +l_ok +pixaSplitIntoFiles(PIXA *pixas, + l_int32 nsplit, + l_float32 scale, + l_int32 outwidth, + l_int32 write_pixa, + l_int32 write_pix, + l_int32 write_pdf) +{ +char buf[64]; +l_int32 i, j, index, n, nt; +PIX *pix1, *pix2; +PIXA *pixa1; + + PROCNAME("pixaSplitIntoFiles"); + + if (!pixas) + return ERROR_INT("pixas not defined", procName, 1); + if (nsplit <= 1) + return ERROR_INT("nsplit must be >= 2", procName, 1); + if ((nt = pixaGetCount(pixas)) == 0) + return ERROR_INT("pixas is empty", procName, 1); + if (!write_pixa && !write_pix && !write_pdf) + return ERROR_INT("no output is requested", procName, 1); + + lept_mkdir("lept/split"); + n = (nt + nsplit - 1) / nsplit; + fprintf(stderr, "nt = %d, n = %d, nsplit = %d\n", nt, n, nsplit); + for (i = 0, index = 0; i < nsplit; i++) { + pixa1 = pixaCreate(n); + for (j = 0; j < n && index < nt; j++, index++) { + pix1 = pixaGetPix(pixas, index, L_CLONE); + pix2 = pixScale(pix1, scale, scale); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + if (write_pixa) { + snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.pa", i + 1); + pixaWriteDebug(buf, pixa1); + } + if (write_pix) { + snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.tif", i + 1); + pix1 = pixaDisplayTiledInRows(pixa1, 1, outwidth, 1.0, 0, 20, 2); + pixWriteDebug(buf, pix1, IFF_TIFF_G4); + pixDestroy(&pix1); + } + if (write_pdf) { + snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.pdf", i + 1); + pixaConvertToPdf(pixa1, 0, 1.0, L_G4_ENCODE, 0, buf, buf); + } + pixaDestroy(&pixa1); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Tile N-Up * + *---------------------------------------------------------------------*/ +/*! + * \brief convertToNUpFiles() + * + * \param[in] dir full path to directory of images + * \param[in] substr [optional] can be null + * \param[in] nx, ny in [1, ... 50], tiling factors in each direction + * \param[in] tw target width, in pixels; must be >= 20 + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \param[in] fontsize to print tail of filename with image. Valid set is + * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \param[in] outdir subdirectory of /tmp to put N-up tiled images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Each set of %nx * %ny images is scaled and tiled into a single
+ *          image, that is written out to %outdir.
+ *      (2) All images in each %nx * %ny set are scaled to the same
+ *          width, %tw.  This is typically used when all images are
+ *          roughly the same size.
+ *      (3) This is useful for generating a pdf from the set of input
+ *          files, where each page is a tile of (%nx * %ny) input images.
+ *          Typical values for %nx and %ny are in the range [2 ... 5].
+ *      (4) If %fontsize != 0, each image has the tail of its filename
+ *          rendered below it.
+ * 
+ */ +l_ok +convertToNUpFiles(const char *dir, + const char *substr, + l_int32 nx, + l_int32 ny, + l_int32 tw, + l_int32 spacing, + l_int32 border, + l_int32 fontsize, + const char *outdir) +{ +l_int32 d, format; +char rootpath[256]; +PIXA *pixa; + + PROCNAME("convertToNUpFiles"); + + if (!dir) + return ERROR_INT("dir not defined", procName, 1); + if (nx < 1 || ny < 1 || nx > 50 || ny > 50) + return ERROR_INT("invalid tiling N-factor", procName, 1); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return ERROR_INT("invalid fontsize", procName, 1); + if (!outdir) + return ERROR_INT("outdir not defined", procName, 1); + + pixa = convertToNUpPixa(dir, substr, nx, ny, tw, spacing, border, + fontsize); + if (!pixa) + return ERROR_INT("pixa not made", procName, 1); + + lept_rmdir(outdir); + lept_mkdir(outdir); + pixaGetRenderingDepth(pixa, &d); + format = (d == 1) ? IFF_TIFF_G4 : IFF_JFIF_JPEG; + makeTempDirname(rootpath, 256, outdir); + modifyTrailingSlash(rootpath, 256, L_ADD_TRAIL_SLASH); + pixaWriteFiles(rootpath, pixa, format); + pixaDestroy(&pixa); + return 0; +} + + +/*! + * \brief convertToNUpPixa() + * + * \param[in] dir full path to directory of images + * \param[in] substr [optional] can be null + * \param[in] nx, ny in [1, ... 50], tiling factors in each direction + * \param[in] tw target width, in pixels; must be >= 20 + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \param[in] fontsize to print tail of filename with image. Valid set is + * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes for convertToNUpFiles()
+ * 
+ */ +PIXA * +convertToNUpPixa(const char *dir, + const char *substr, + l_int32 nx, + l_int32 ny, + l_int32 tw, + l_int32 spacing, + l_int32 border, + l_int32 fontsize) +{ +l_int32 i, n; +char *fname, *tail; +PIXA *pixa1, *pixa2; +SARRAY *sa1, *sa2; + + PROCNAME("convertToNUpPixa"); + + if (!dir) + return (PIXA *)ERROR_PTR("dir not defined", procName, NULL); + if (nx < 1 || ny < 1 || nx > 50 || ny > 50) + return (PIXA *)ERROR_PTR("invalid tiling N-factor", procName, NULL); + if (tw < 20) + return (PIXA *)ERROR_PTR("tw must be >= 20", procName, NULL); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return (PIXA *)ERROR_PTR("invalid fontsize", procName, NULL); + + sa1 = getSortedPathnamesInDirectory(dir, substr, 0, 0); + pixa1 = pixaReadFilesSA(sa1); + n = sarrayGetCount(sa1); + sa2 = sarrayCreate(n); + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa1, i, L_NOCOPY); + splitPathAtDirectory(fname, NULL, &tail); + sarrayAddString(sa2, tail, L_INSERT); + } + sarrayDestroy(&sa1); + pixa2 = pixaConvertToNUpPixa(pixa1, sa2, nx, ny, tw, spacing, + border, fontsize); + pixaDestroy(&pixa1); + sarrayDestroy(&sa2); + return pixa2; +} + + +/*! + * \brief pixaConvertToNUpPixa() + * + * \param[in] pixas + * \param[in] sa [optional] array of strings associated with each pix + * \param[in] nx, ny in [1, ... 50], tiling factors in each direction + * \param[in] tw target width, in pixels; must be >= 20 + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \param[in] fontsize to print string with each image. Valid set is + * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \return pixad, or NULL on error + * + *
+ * Notes:
+ *      (1) This takes an input pixa and an optional array of strings, and
+ *          generates a pixa of NUp tiles from the input, labeled with
+ *          the strings if they exist and %fontsize != 0.
+ *      (2) See notes for convertToNUpFiles()
+ * 
+ */ +PIXA * +pixaConvertToNUpPixa(PIXA *pixas, + SARRAY *sa, + l_int32 nx, + l_int32 ny, + l_int32 tw, + l_int32 spacing, + l_int32 border, + l_int32 fontsize) +{ +l_int32 i, j, k, nt, n2, nout, d; +char *str; +L_BMF *bmf; +PIX *pix1, *pix2, *pix3, *pix4; +PIXA *pixa1, *pixad; + + PROCNAME("pixaConvertToNUpPixa"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (nx < 1 || ny < 1 || nx > 50 || ny > 50) + return (PIXA *)ERROR_PTR("invalid tiling N-factor", procName, NULL); + if (tw < 20) + return (PIXA *)ERROR_PTR("tw must be >= 20", procName, NULL); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return (PIXA *)ERROR_PTR("invalid fontsize", procName, NULL); + + nt = pixaGetCount(pixas); + if (sa && (sarrayGetCount(sa) != nt)) { + L_WARNING("pixa size %d not equal to sarray size %d\n", procName, + nt, sarrayGetCount(sa)); + } + + n2 = nx * ny; + nout = (nt + n2 - 1) / n2; + pixad = pixaCreate(nout); + bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize); + for (i = 0, j = 0; i < nout; i++) { + pixa1 = pixaCreate(n2); + for (k = 0; k < n2 && j < nt; j++, k++) { + pix1 = pixaGetPix(pixas, j, L_CLONE); + pix2 = pixScaleToSize(pix1, tw, 0); /* all images have width tw */ + if (bmf && sa) { + str = sarrayGetString(sa, j, L_NOCOPY); + pix3 = pixAddTextlines(pix2, bmf, str, 0xff000000, + L_ADD_BELOW); + } else { + pix3 = pixClone(pix2); + } + pixaAddPix(pixa1, pix3, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + if (pixaGetCount(pixa1) == 0) { /* probably won't happen */ + pixaDestroy(&pixa1); + continue; + } + + /* Add 2 * border to image width to prevent scaling */ + pixaGetRenderingDepth(pixa1, &d); + pix4 = pixaDisplayTiledAndScaled(pixa1, d, tw + 2 * border, nx, 0, + spacing, border); + pixaAddPix(pixad, pix4, L_INSERT); + pixaDestroy(&pixa1); + } + + bmfDestroy(&bmf); + return pixad; +} + + +/*---------------------------------------------------------------------* + * Render two pixa side-by-side for comparison * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaCompareInPdf() + * + * \param[in] pixa1 + * \param[in] pixa2 + * \param[in] nx, ny in [1, ... 20], tiling factors in each direction + * \param[in] tw target width, in pixels; must be >= 20 + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image + * and on each pair; use 0 for no border + * \param[in] fontsize to print index of each pair of images. Valid set + * is {4,6,8,10,12,14,16,18,20}. Use 0 to disable. + * \param[in] fileout output pdf file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This takes two pixa and renders them interleaved, side-by-side
+ *          in a pdf.  A warning is issued if the input pixa arrays
+ *          have different lengths.
+ *      (2) %nx and %ny specify how many side-by-side pairs are displayed
+ *          on each pdf page.  For example, if %nx = 1 and %ny = 2, then
+ *          two pairs are shown, one above the other, on each page.
+ *      (3) The input pix are scaled to a target width of %tw, and
+ *          then paired with optional %spacing between and optional
+ *          black border of width %border.
+ *      (4) After a pixa is generated of these tiled images, it is
+ *          written to %fileout as a pdf.
+ *      (5) Typical numbers for the input parameters are:
+ *            %nx = small integer (1 - 4)
+ *            %ny = 2 * %nx
+ *            %tw = 200 - 500 pixels
+ *            %spacing = 10
+ *            %border = 2
+ *            %fontsize = 10
+ *      (6) If %fontsize != 0, the index of the pix pair in their pixa
+ *          is printed out below each pair.
+ * 
+ */ +l_ok +pixaCompareInPdf(PIXA *pixa1, + PIXA *pixa2, + l_int32 nx, + l_int32 ny, + l_int32 tw, + l_int32 spacing, + l_int32 border, + l_int32 fontsize, + const char *fileout) +{ +l_int32 n1, n2, npairs; +PIXA *pixa3, *pixa4, *pixa5; +SARRAY *sa; + + PROCNAME("pixaCompareInPdf"); + + if (!pixa1 || !pixa2) + return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); + if (nx < 1 || ny < 1 || nx > 20 || ny > 20) + return ERROR_INT("invalid tiling factors", procName, 1); + if (tw < 20) + return ERROR_INT("invalid tw; tw must be >= 20", procName, 1); + if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) + return ERROR_INT("invalid fontsize", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + n1 = pixaGetCount(pixa1); + n2 = pixaGetCount(pixa2); + if (n1 == 0 || n2 == 0) + return ERROR_INT("at least one pixa is empty", procName, 1); + if (n1 != n2) + L_WARNING("sizes (%d, %d) differ; using the minimum in interleave\n", + procName, n1, n2); + + /* Interleave the input pixa */ + if ((pixa3 = pixaInterleave(pixa1, pixa2, L_CLONE)) == NULL) + return ERROR_INT("pixa3 not made", procName, 1); + + /* Scale the images if necessary and pair them up side/by/side */ + pixa4 = pixaConvertToNUpPixa(pixa3, NULL, 2, 1, tw, spacing, border, 0); + pixaDestroy(&pixa3); + + /* Label the pairs and mosaic into pages without further scaling */ + npairs = pixaGetCount(pixa4); + sa = (fontsize > 0) ? sarrayGenerateIntegers(npairs) : NULL; + pixa5 = pixaConvertToNUpPixa(pixa4, sa, nx, ny, + 2 * tw + 4 * border + spacing, + spacing, border, fontsize); + pixaDestroy(&pixa4); + sarrayDestroy(&sa); + + /* Output as pdf without scaling */ + pixaConvertToPdf(pixa5, 0, 1.0, 0, 0, NULL, fileout); + pixaDestroy(&pixa5); + return 0; +} + + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixalloc.c b/hgdriver/3rdparty/hgOCR/leptonica/pixalloc.c new file mode 100644 index 0000000..771dfd6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixalloc.c @@ -0,0 +1,532 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixalloc.c + *
+ *
+ *      Custom memory storage with allocator and deallocator
+ *
+ *          l_int32       pmsCreate()
+ *          void          pmsDestroy()
+ *          void         *pmsCustomAlloc()
+ *          void          pmsCustomDealloc()
+ *          void         *pmsGetAlloc()
+ *          l_int32       pmsGetLevelForAlloc()
+ *          l_int32       pmsGetLevelForDealloc()
+ *          void          pmsLogInfo()
+ * 
+ */ + +#include "allheaders.h" + +/*-------------------------------------------------------------------------* + * Pix Memory Storage * + * * + * This is a simple utility for handling pix memory storage. It is * + * enabled by setting the PixMemoryManager allocators to the functions * + * that are defined here * + * pmsCustomAlloc() * + * pmsCustomDealloc() * + * Use pmsCreate() at the beginning to do the pre-allocation, and * + * pmsDestroy() at the end to clean it up. * + *-------------------------------------------------------------------------*/ +/* + * In the following, the "memory" refers to the image data + * field that is used within the pix. The memory store is a + * continuous block of memory, that is logically divided into + * smaller "chunks" starting with a set at a minimum size, and + * followed by sets of increasing size that are a power of 2 larger + * than the minimum size. You must specify the number of chunks + * of each size. + * + * A requested data chunk, if it exists, is borrowed from the memory + * storage, and returned after use. If the chunk is too small, or + * too large, or if all chunks in the appropriate size range are + * in use, the memory is allocated dynamically and freed after use. + * + * There are four parameters that determine the use of pre-allocated memory: + * + * minsize: any requested chunk smaller than this is allocated + * dynamically and destroyed after use. No preallocated + * memory is used. + * smallest: the size of the smallest pre-allocated memory chunk. + * nlevels: the number of different sizes of data chunks, each a + * power of 2 larger than 'smallest'. + * numalloc: a Numa of size 'nlevels' containing the number of data + * chunks for each size that are in the memory store. + * + * As an example, suppose: + * minsize = 0.5MB + * smallest = 1.0MB + * nlevels = 4 + * numalloc = {10, 5, 5, 5} + * Then the total amount of allocated memory (in MB) is + * 10 * 1 + 5 * 2 + 5 * 4 + 5 * 8 = 80 MB + * Any pix requiring less than 0.5 MB or more than 8 MB of memory will + * not come from the memory store. Instead, it will be dynamically + * allocated and freed after use. + * + * How is this implemented? + * + * At setup, the full data block size is computed and allocated. + * The addresses of the individual chunks are found, and the pointers + * are stored in a set of Ptra (generic pointer arrays), using one Ptra + * for each of the sizes of the chunks. When returning a chunk after + * use, it is necessary to determine from the address which size level + * (ptra) the chunk belongs to. This is done by comparing the address + * of the associated chunk. + * + * In the event that memory chunks need to be dynamically allocated, + * either (1) because they are too small or too large for the memory + * store or (2) because all the pix of that size (i.e., in the + * appropriate level) in the memory store are in use, the + * addresses generated will be outside the pre-allocated block. + * After use they won't be returned to a ptra; instead the deallocator + * will free them. + */ + +/*! Pix memory storage */ +struct PixMemoryStore +{ + struct L_Ptraa *paa; /*!< Holds ptrs to allocated memory */ + size_t minsize; /*!< Pix smaller than this (in bytes) */ + /*!< are allocated dynamically */ + size_t smallest; /*!< Smallest mem (in bytes) alloc'd */ + size_t largest; /*!< Larest mem (in bytes) alloc'd */ + size_t nbytes; /*!< Size of allocated block w/ all chunks */ + l_int32 nlevels; /*!< Num of power-of-2 sizes pre-alloc'd */ + size_t *sizes; /*!< Mem sizes at each power-of-2 level */ + l_int32 *allocarray; /*!< Number of mem alloc'd at each size */ + l_uint32 *baseptr; /*!< ptr to allocated array */ + l_uint32 *maxptr; /*!< ptr just beyond allocated memory */ + l_uint32 **firstptr; /*!< array of ptrs to first chunk in size */ + l_int32 *memused; /*!< log: total # of pix used (by level) */ + l_int32 *meminuse; /*!< log: # of pix in use (by level) */ + l_int32 *memmax; /*!< log: max # of pix in use (by level) */ + l_int32 *memempty; /*!< log: # of pix alloc'd because */ + /*!< the store was empty (by level) */ + char *logfile; /*!< log: set to null if no logging */ +}; +typedef struct PixMemoryStore L_PIX_MEM_STORE; + +static L_PIX_MEM_STORE *CustomPMS = NULL; + + +/*! + * \brief pmsCreate() + * + * \param[in] minsize of data chunk that can be supplied by pms + * \param[in] smallest bytes of the smallest pre-allocated data chunk. + * \param[in] numalloc array with the number of data chunks for each + * size that are in the memory store + * \param[in] logfile use for debugging; null otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This computes the size of the block of memory required
+ *          and allocates it.  Each chunk starts on a 32-bit word boundary.
+ *          The chunk sizes are in powers of 2, starting at %smallest,
+ *          and the number of levels and chunks at each level is
+ *          specified by %numalloc.
+ *      (2) This is intended to manage the image data for a small number
+ *          of relatively large pix.  The system malloc is expected to
+ *          handle very large numbers of small chunks efficiently.
+ *      (3) Important: set the allocators and call this function
+ *          before any pix have been allocated.  Destroy all the pix
+ *          in the normal way before calling pmsDestroy().
+ *      (4) The pms struct is stored in a static global, so this function
+ *          is not thread-safe.  When used, there must be only one thread
+ *          per process.
+ * 
+ */ +l_ok +pmsCreate(size_t minsize, + size_t smallest, + NUMA *numalloc, + const char *logfile) +{ +l_int32 nlevels, i, j, nbytes; +l_int32 *alloca; +l_float32 nchunks; +l_uint32 *baseptr, *data; +l_uint32 **firstptr; +size_t *sizes; +L_PIX_MEM_STORE *pms; +L_PTRA *pa; +L_PTRAA *paa; + + PROCNAME("createPMS"); + + if (!numalloc) + return ERROR_INT("numalloc not defined", procName, 1); + numaGetSum(numalloc, &nchunks); + if (nchunks > 1000.0) + L_WARNING("There are %.0f chunks\n", procName, nchunks); + + pms = (L_PIX_MEM_STORE *)LEPT_CALLOC(1, sizeof(L_PIX_MEM_STORE)); + CustomPMS = pms; + + /* Make sure that minsize and smallest are multiples of 32 bit words */ + if (minsize % 4 != 0) + minsize -= minsize % 4; + pms->minsize = minsize; + nlevels = numaGetCount(numalloc); + pms->nlevels = nlevels; + + if ((sizes = (size_t *)LEPT_CALLOC(nlevels, sizeof(size_t))) == NULL) + return ERROR_INT("sizes not made", procName, 1); + pms->sizes = sizes; + if (smallest % 4 != 0) + smallest += 4 - (smallest % 4); + pms->smallest = smallest; + for (i = 0; i < nlevels; i++) + sizes[i] = smallest * (1 << i); + pms->largest = sizes[nlevels - 1]; + + alloca = numaGetIArray(numalloc); + pms->allocarray = alloca; + if ((paa = ptraaCreate(nlevels)) == NULL) + return ERROR_INT("paa not made", procName, 1); + pms->paa = paa; + + for (i = 0, nbytes = 0; i < nlevels; i++) + nbytes += alloca[i] * sizes[i]; + pms->nbytes = nbytes; + + if ((baseptr = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32))) + == NULL) + return ERROR_INT("calloc fail for baseptr", procName, 1); + pms->baseptr = baseptr; + pms->maxptr = baseptr + nbytes / 4; /* just beyond the memory store */ + if ((firstptr = (l_uint32 **)LEPT_CALLOC(nlevels, sizeof(l_uint32 *))) + == NULL) + return ERROR_INT("calloc fail for firstptr", procName, 1); + pms->firstptr = firstptr; + + data = baseptr; + for (i = 0; i < nlevels; i++) { + if ((pa = ptraCreate(alloca[i])) == NULL) + return ERROR_INT("pa not made", procName, 1); + ptraaInsertPtra(paa, i, pa); + firstptr[i] = data; + for (j = 0; j < alloca[i]; j++) { + ptraAdd(pa, data); + data += sizes[i] / 4; + } + } + + if (logfile) { + pms->memused = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); + pms->meminuse = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); + pms->memmax = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); + pms->memempty = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); + pms->logfile = stringNew(logfile); + } + + return 0; +} + + +/*! + * \brief pmsDestroy() + * + *
+ * Notes:
+ *      (1) Important: call this function at the end of the program, after
+ *          the last pix has been destroyed.
+ * 
+ */ +void +pmsDestroy() +{ +L_PIX_MEM_STORE *pms; + + if ((pms = CustomPMS) == NULL) + return; + + ptraaDestroy(&pms->paa, FALSE, FALSE); /* don't touch the ptrs */ + LEPT_FREE(pms->baseptr); /* free the memory */ + + if (pms->logfile) { + pmsLogInfo(); + LEPT_FREE(pms->logfile); + LEPT_FREE(pms->memused); + LEPT_FREE(pms->meminuse); + LEPT_FREE(pms->memmax); + LEPT_FREE(pms->memempty); + } + + LEPT_FREE(pms->sizes); + LEPT_FREE(pms->allocarray); + LEPT_FREE(pms->firstptr); + LEPT_FREE(pms); + CustomPMS = NULL; + return; +} + + +/*! + * \brief pmsCustomAlloc() + * + * \param[in] nbytes min number of bytes in the chunk to be retrieved + * \return data ptr to chunk + * + *
+ * Notes:
+ *      (1) This attempts to find a suitable pre-allocated chunk.
+ *          If not found, it dynamically allocates the chunk.
+ *      (2) If logging is turned on, the allocations that are not taken
+ *          from the memory store, and are at least as large as the
+ *          minimum size the store can handle, are logged to file.
+ * 
+ */ +void * +pmsCustomAlloc(size_t nbytes) +{ +l_int32 level; +void *data; +L_PIX_MEM_STORE *pms; +L_PTRA *pa; + + PROCNAME("pmsCustomAlloc"); + + if ((pms = CustomPMS) == NULL) + return (void *)ERROR_PTR("pms not defined", procName, NULL); + + pmsGetLevelForAlloc(nbytes, &level); + + if (level < 0) { /* size range invalid; must alloc */ + if ((data = pmsGetAlloc(nbytes)) == NULL) + return (void *)ERROR_PTR("data not made", procName, NULL); + } else { /* get from store */ + pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); + data = ptraRemoveLast(pa); + if (data && pms->logfile) { + pms->memused[level]++; + pms->meminuse[level]++; + if (pms->meminuse[level] > pms->memmax[level]) + pms->memmax[level]++; + } + if (!data) { /* none left at this level */ + data = pmsGetAlloc(nbytes); + if (pms->logfile) + pms->memempty[level]++; + } + } + + return data; +} + + +/*! + * \brief pmsCustomDealloc() + * + * \param[in] data to be freed or returned to the storage + * \return void + */ +void +pmsCustomDealloc(void *data) +{ +l_int32 level; +L_PIX_MEM_STORE *pms; +L_PTRA *pa; + + PROCNAME("pmsCustomDealloc"); + + if ((pms = CustomPMS) == NULL) { + L_ERROR("pms not defined\n", procName); + return; + } + + if (pmsGetLevelForDealloc(data, &level) == 1) { + L_ERROR("level not found\n", procName); + return; + } + + if (level < 0) { /* no logging; just free the data */ + LEPT_FREE(data); + } else { /* return the data to the store */ + pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); + ptraAdd(pa, data); + if (pms->logfile) + pms->meminuse[level]--; + } + + return; +} + + +/*! + * \brief pmsGetAlloc() + * + * \param[in] nbytes + * \return data + * + *
+ * Notes:
+ *      (1) This is called when a request for pix data cannot be
+ *          obtained from the preallocated memory store.  After use it
+ *          is freed like normal memory.
+ *      (2) If logging is on, only write out allocs that are as large as
+ *          the minimum size handled by the memory store.
+ *      (3) size_t is %lu on 64 bit platforms and %u on 32 bit platforms.
+ *          The C99 platform-independent format specifier for size_t is %zu.
+ *          Windows since at least VC-2015 is conforming; we can now use %zu.
+ * 
+ */ +void * +pmsGetAlloc(size_t nbytes) +{ +void *data; +FILE *fp; +L_PIX_MEM_STORE *pms; + + PROCNAME("pmsGetAlloc"); + + if ((pms = CustomPMS) == NULL) + return (void *)ERROR_PTR("pms not defined", procName, NULL); + + if ((data = (void *)LEPT_CALLOC(nbytes, sizeof(char))) == NULL) + return (void *)ERROR_PTR("data not made", procName, NULL); + if (pms->logfile && nbytes >= pms->smallest) { + fp = fopenWriteStream(pms->logfile, "a"); + fprintf(fp, "Alloc %zu bytes at %p\n", nbytes, data); + fclose(fp); + } + + return data; +} + + +/*! + * \brief pmsGetLevelForAlloc() + * + * \param[in] nbytes min number of bytes in the chunk to be retrieved + * \param[out] plevel -1 if either too small or too large + * \return 0 if OK, 1 on error + */ +l_ok +pmsGetLevelForAlloc(size_t nbytes, + l_int32 *plevel) +{ +l_int32 i; +l_float64 ratio; +L_PIX_MEM_STORE *pms; + + PROCNAME("pmsGetLevelForAlloc"); + + if (!plevel) + return ERROR_INT("&level not defined", procName, 1); + *plevel = -1; + if ((pms = CustomPMS) == NULL) + return ERROR_INT("pms not defined", procName, 1); + + if (nbytes < pms->minsize || nbytes > pms->largest) + return 0; /* -1 */ + + ratio = (l_float64)nbytes / (l_float64)(pms->smallest); + for (i = 0; i < pms->nlevels; i++) { + if (ratio <= 1.0) + break; + ratio /= 2.0; + } + *plevel = i; + + return 0; +} + + +/*! + * \brief pmsGetLevelForDealloc() + * + * \param[in] data ptr to memory chunk + * \param[out] plevel level in memory store; -1 if allocated + * outside the store + * \return 0 if OK, 1 on error + */ +l_ok +pmsGetLevelForDealloc(void *data, + l_int32 *plevel) +{ +l_int32 i; +l_uint32 *first; +L_PIX_MEM_STORE *pms; + + PROCNAME("pmsGetLevelForDealloc"); + + if (!plevel) + return ERROR_INT("&level not defined", procName, 1); + *plevel = -1; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if ((pms = CustomPMS) == NULL) + return ERROR_INT("pms not defined", procName, 1); + + if (data < (void *)pms->baseptr || data >= (void *)pms->maxptr) + return 0; /* -1 */ + + for (i = 1; i < pms->nlevels; i++) { + first = pms->firstptr[i]; + if (data < (void *)first) + break; + } + *plevel = i - 1; + + return 0; +} + + +/*! + * \brief pmsLogInfo() + */ +void +pmsLogInfo() +{ +l_int32 i; +L_PIX_MEM_STORE *pms; + + if ((pms = CustomPMS) == NULL) + return; + + fprintf(stderr, "Total number of pix used at each level\n"); + for (i = 0; i < pms->nlevels; i++) + fprintf(stderr, " Level %d (%zu bytes): %d\n", i, + pms->sizes[i], pms->memused[i]); + + fprintf(stderr, "Max number of pix in use at any time in each level\n"); + for (i = 0; i < pms->nlevels; i++) + fprintf(stderr, " Level %d (%zu bytes): %d\n", i, + pms->sizes[i], pms->memmax[i]); + + fprintf(stderr, "Number of pix alloc'd because none were available\n"); + for (i = 0; i < pms->nlevels; i++) + fprintf(stderr, " Level %d (%zu bytes): %d\n", i, + pms->sizes[i], pms->memempty[i]); + + return; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixarith.c b/hgdriver/3rdparty/hgOCR/leptonica/pixarith.c new file mode 100644 index 0000000..cef1cb0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixarith.c @@ -0,0 +1,1531 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixarith.c + *
+ *
+ *      One-image grayscale arithmetic operations (8, 16, 32 bpp)
+ *           l_int32     pixAddConstantGray()
+ *           l_int32     pixMultConstantGray()
+ *
+ *      Two-image grayscale arithmetic operations (8, 16, 32 bpp)
+ *           PIX        *pixAddGray()
+ *           PIX        *pixSubtractGray()
+ *
+ *      Grayscale threshold operation (8, 16, 32 bpp)
+ *           PIX        *pixThresholdToValue()
+ *
+ *      Image accumulator arithmetic operations
+ *           PIX        *pixInitAccumulate()
+ *           PIX        *pixFinalAccumulate()
+ *           PIX        *pixFinalAccumulateThreshold()
+ *           l_int32     pixAccumulate()
+ *           l_int32     pixMultConstAccumulate()
+ *
+ *      Absolute value of difference
+ *           PIX        *pixAbsDifference()
+ *
+ *      Sum of color images
+ *           PIX        *pixAddRGB()
+ *
+ *      Two-image min and max operations (8 and 16 bpp)
+ *           PIX        *pixMinOrMax()
+ *
+ *      Scale pix for maximum dynamic range
+ *           PIX        *pixMaxDynamicRange()
+ *           PIX        *pixMaxDynamicRangeRGB()
+ *
+ *      RGB pixel value scaling
+ *           l_uint32    linearScaleRGBVal()
+ *           l_uint32    logScaleRGBVal()
+ *
+ *      Log base2 lookup
+ *           l_float32  *makeLogBase2Tab()
+ *           l_float32   getLogBase2()
+ *
+ *      The image accumulator operations are used when you expect
+ *      overflow from 8 bits on intermediate results.  For example,
+ *      you might want a tophat contrast operator which is
+ *         3*I - opening(I,S) - closing(I,S)
+ *      To use these operations, first use the init to generate
+ *      a 16 bpp image, use the accumulate to add or subtract 8 bpp
+ *      images from that, or the multiply constant to multiply
+ *      by a small constant (much less than 256 -- we don't want
+ *      overflow from the 16 bit images!), and when you're finished
+ *      use final to bring the result back to 8 bpp, clipped
+ *      if necessary.  There is also a divide function, which
+ *      can be used to divide one image by another, scaling the
+ *      result for maximum dynamic range, and giving back the
+ *      8 bpp result.
+ *
+ *      A simpler interface to the arithmetic operations is
+ *      provided in pixacc.c.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + +/*-------------------------------------------------------------* + * One-image grayscale arithmetic operations * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddConstantGray() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] val amount to add to each pixel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation.
+ *      (2) No clipping for 32 bpp.
+ *      (3) For 8 and 16 bpp, if val > 0 the result is clipped
+ *          to 0xff and 0xffff, rsp.
+ *      (4) For 8 and 16 bpp, if val < 0 the result is clipped to 0.
+ * 
+ */ +l_ok +pixAddConstantGray(PIX *pixs, + l_int32 val) +{ +l_int32 i, j, w, h, d, wpl, pval; +l_uint32 *data, *line; + + PROCNAME("pixAddConstantGray"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 8) { + if (val < 0) { + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = L_MAX(0, pval + val); + SET_DATA_BYTE(line, j, pval); + } + } else { /* val >= 0 */ + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = L_MIN(255, pval + val); + SET_DATA_BYTE(line, j, pval); + } + } + } else if (d == 16) { + if (val < 0) { + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = L_MAX(0, pval + val); + SET_DATA_TWO_BYTES(line, j, pval); + } + } else { /* val >= 0 */ + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = L_MIN(0xffff, pval + val); + SET_DATA_TWO_BYTES(line, j, pval); + } + } + } else { /* d == 32; no check for overflow (< 0 or > 0xffffffff) */ + for (j = 0; j < w; j++) + *(line + j) += val; + } + } + + return 0; +} + + +/*! + * \brief pixMultConstantGray() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] val >= 0.0; amount to multiply by each pixel + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place operation; val must be >= 0.
+ *      (2) No clipping for 32 bpp.
+ *      (3) For 8 and 16 bpp, the result is clipped to 0xff and 0xffff, rsp.
+ * 
+ */ +l_ok +pixMultConstantGray(PIX *pixs, + l_float32 val) +{ +l_int32 i, j, w, h, d, wpl, pval; +l_uint32 upval; +l_uint32 *data, *line; + + PROCNAME("pixMultConstantGray"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); + if (val < 0.0) + return ERROR_INT("val < 0.0", procName, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d == 8) { + for (j = 0; j < w; j++) { + pval = GET_DATA_BYTE(line, j); + pval = (l_int32)(val * pval); + pval = L_MIN(255, pval); + SET_DATA_BYTE(line, j, pval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + pval = GET_DATA_TWO_BYTES(line, j); + pval = (l_int32)(val * pval); + pval = L_MIN(0xffff, pval); + SET_DATA_TWO_BYTES(line, j, pval); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) { + upval = *(line + j); + upval = (l_uint32)(val * upval); + *(line + j) = upval; + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Two-image grayscale arithmetic ops * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddGray() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, or + * different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \return pixd always + * + *
+ * Notes:
+ *      (1) Arithmetic addition of two 8, 16 or 32 bpp images.
+ *      (2) For 8 and 16 bpp, we do explicit clipping to 0xff and 0xffff,
+ *          respectively.
+ *      (3) Alignment is to UL corner.
+ *      (4) There are 3 cases.  The result can go to a new dest,
+ *          in-place to pixs1, or to an existing input dest:
+ *          * pixd == null:   (src1 + src2) --> new pixd
+ *          * pixd == pixs1:  (src1 + src2) --> src1  (in-place)
+ *          * pixd != pixs1:  (src1 + src2) --> input pixd
+ *      (5) pixs2 must be different from both pixd and pixs1.
+ * 
+ */ +PIX * +pixAddGray(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, d, ws, hs, w, h, wpls, wpld, val, sum; +l_uint32 *datas, *datad, *lines, *lined; + + PROCNAME("pixAddGray"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixs2 == pixs1) + return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); + if (pixs2 == pixd) + return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); + d = pixGetDepth(pixs1); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); + if (pixd && (pixGetDepth(pixd) != d)) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); + + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal in size\n", procName); + if (pixd && !pixSizesEqual(pixs1, pixd)) + L_WARNING("pixs1 and pixd not equal in size\n", procName); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + /* pixd + pixs2 ==> pixd */ + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + lines = datas + i * wpls; + if (d == 8) { + for (j = 0; j < w; j++) { + sum = GET_DATA_BYTE(lines, j) + GET_DATA_BYTE(lined, j); + val = L_MIN(sum, 255); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + sum = GET_DATA_TWO_BYTES(lines, j) + + GET_DATA_TWO_BYTES(lined, j); + val = L_MIN(sum, 0xffff); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) + *(lined + j) += *(lines + j); + } + } + + return pixd; +} + + +/*! + * \brief pixSubtractGray() + * + * \param[in] pixd [optional]; this can be null, equal to pixs1, or + * different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \return pixd always + * + *
+ * Notes:
+ *      (1) Arithmetic subtraction of two 8, 16 or 32 bpp images.
+ *      (2) Source pixs2 is always subtracted from source pixs1.
+ *      (3) Do explicit clipping to 0.
+ *      (4) Alignment is to UL corner.
+ *      (5) There are 3 cases.  The result can go to a new dest,
+ *          in-place to pixs1, or to an existing input dest:
+ *          (a) pixd == null   (src1 - src2) --> new pixd
+ *          (b) pixd == pixs1  (src1 - src2) --> src1  (in-place)
+ *          (d) pixd != pixs1  (src1 - src2) --> input pixd
+ *      (6) pixs2 must be different from both pixd and pixs1.
+ * 
+ */ +PIX * +pixSubtractGray(PIX *pixd, + PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, ws, hs, d, wpls, wpld, val, diff; +l_uint32 *datas, *datad, *lines, *lined; + + PROCNAME("pixSubtractGray"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixs2 == pixs1) + return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); + if (pixs2 == pixd) + return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); + d = pixGetDepth(pixs1); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); + if (pixd && (pixGetDepth(pixd) != d)) + return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); + + if (!pixSizesEqual(pixs1, pixs2)) + L_WARNING("pixs1 and pixs2 not equal in size\n", procName); + if (pixd && !pixSizesEqual(pixs1, pixd)) + L_WARNING("pixs1 and pixd not equal in size\n", procName); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + /* pixd - pixs2 ==> pixd */ + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(ws, w); + h = L_MIN(hs, h); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + lines = datas + i * wpls; + if (d == 8) { + for (j = 0; j < w; j++) { + diff = GET_DATA_BYTE(lined, j) - GET_DATA_BYTE(lines, j); + val = L_MAX(diff, 0); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + diff = GET_DATA_TWO_BYTES(lined, j) + - GET_DATA_TWO_BYTES(lines, j); + val = L_MAX(diff, 0); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32; no clipping */ + for (j = 0; j < w; j++) + *(lined + j) -= *(lines + j); + } + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Grayscale threshold operation * + *-------------------------------------------------------------*/ +/*! + * \brief pixThresholdToValue() + * + * \param[in] pixd [optional]; if not null, must be equal to pixs + * \param[in] pixs 8, 16, 32 bpp + * \param[in] threshval + * \param[in] setval + * \return pixd always + * + *
+ * Notes:
+ *    ~ operation can be in-place (pixs == pixd) or to a new pixd
+ *    ~ if %setval > %threshval, sets pixels with a value >= threshval to setval
+ *    ~ if %setval < %threshval, sets pixels with a value <= threshval to setval
+ *    ~ if %setval == %threshval, no-op
+ * 
+ */ +PIX * +pixThresholdToValue(PIX *pixd, + PIX *pixs, + l_int32 threshval, + l_int32 setval) +{ +l_int32 i, j, w, h, d, wpld, setabove; +l_uint32 *datad, *lined; + + PROCNAME("pixThresholdToValue"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + d = pixGetDepth(pixs); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", procName, pixd); + if (pixd && (pixs != pixd)) + return (PIX *)ERROR_PTR("pixd exists and is not pixs", procName, pixd); + if (threshval < 0 || setval < 0) + return (PIX *)ERROR_PTR("threshval & setval not < 0", procName, pixd); + if (d == 8 && setval > 255) + return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", procName, pixd); + if (d == 16 && setval > 0xffff) + return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", procName, pixd); + + if (!pixd) + pixd = pixCopy(NULL, pixs); + if (setval == threshval) { + L_WARNING("setval == threshval; no operation\n", procName); + return pixd; + } + + datad = pixGetData(pixd); + pixGetDimensions(pixd, &w, &h, NULL); + wpld = pixGetWpl(pixd); + if (setval > threshval) + setabove = TRUE; + else + setabove = FALSE; + + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + if (setabove == TRUE) { + if (d == 8) { + for (j = 0; j < w; j++) { + if (GET_DATA_BYTE(lined, j) - threshval >= 0) + SET_DATA_BYTE(lined, j, setval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + if (GET_DATA_TWO_BYTES(lined, j) - threshval >= 0) + SET_DATA_TWO_BYTES(lined, j, setval); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + if (*(lined + j) >= threshval) + *(lined + j) = setval; + } + } + } else { /* set if below or at threshold */ + if (d == 8) { + for (j = 0; j < w; j++) { + if (GET_DATA_BYTE(lined, j) - threshval <= 0) + SET_DATA_BYTE(lined, j, setval); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + if (GET_DATA_TWO_BYTES(lined, j) - threshval <= 0) + SET_DATA_TWO_BYTES(lined, j, setval); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + if (*(lined + j) <= threshval) + *(lined + j) = setval; + } + } + } + } + + return pixd; +} + + +/*-------------------------------------------------------------* + * Image accumulator arithmetic operations * + *-------------------------------------------------------------*/ +/*! + * \brief pixInitAccumulate() + * + * \param[in] w, h of accumulate array + * \param[in] offset initialize the 32 bpp to have this + * value; not more than 0x40000000 + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) %offset must be >= 0.
+ *      (2) %offset is used so that we can do arithmetic
+ *          with negative number results on l_uint32 data; it
+ *          prevents the l_uint32 data from going negative.
+ *      (3) Because we use l_int32 intermediate data results,
+ *          these should never exceed the max of l_int32 (0x7fffffff).
+ *          We do not permit the offset to be above 0x40000000,
+ *          which is half way between 0 and the max of l_int32.
+ *      (4) The same offset should be used for initialization,
+ *          multiplication by a constant, and final extraction!
+ *      (5) If you're only adding positive values, %offset can be 0.
+ * 
+ */ +PIX * +pixInitAccumulate(l_int32 w, + l_int32 h, + l_uint32 offset) +{ +PIX *pixd; + + PROCNAME("pixInitAccumulate"); + + if ((pixd = pixCreate(w, h, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + pixSetAllArbitrary(pixd, offset); + return pixd; +} + + +/*! + * \brief pixFinalAccumulate() + * + * \param[in] pixs 32 bpp + * \param[in] offset same as used for initialization + * \param[in] depth 8, 16 or 32 bpp, of destination + * \return pixd 8, 16 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) %offset must be >= 0 and should not exceed 0x40000000.
+ *      (2) %offset is subtracted from the src 32 bpp image
+ *      (3) For 8 bpp dest, the result is clipped to [0, 0xff]
+ *      (4) For 16 bpp dest, the result is clipped to [0, 0xffff]
+ * 
+ */ +PIX * +pixFinalAccumulate(PIX *pixs, + l_uint32 offset, + l_int32 depth) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixFinalAccumulate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (depth != 8 && depth != 16 && depth != 32) + return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", procName, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + if (depth == 8) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + val = L_MAX(0, val); + val = L_MIN(255, val); + SET_DATA_BYTE(lined, j, (l_uint8)val); + } + } + } else if (depth == 16) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + val = L_MAX(0, val); + val = L_MIN(0xffff, val); + SET_DATA_TWO_BYTES(lined, j, (l_uint16)val); + } + } + } else { /* depth == 32 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) + lined[j] = lines[j] - offset; + } + } + + return pixd; +} + + +/*! + * \brief pixFinalAccumulateThreshold() + * + * \param[in] pixs 32 bpp + * \param[in] offset same as used for initialization + * \param[in] threshold values less than this are set in the destination + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) %offset must be >= 0 and should not exceed 0x40000000.
+ *      (2) %offset is subtracted from the src 32 bpp image
+ * 
+ */ +PIX * +pixFinalAccumulateThreshold(PIX *pixs, + l_uint32 offset, + l_uint32 threshold) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixFinalAccumulateThreshold"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = lines[j] - offset; + if (val >= threshold) { + SET_DATA_BIT(lined, j); + } + } + } + + return pixd; +} + + +/*! + * \brief pixAccumulate() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 1, 8, 16 or 32 bpp + * \param[in] op L_ARITH_ADD or L_ARITH_SUBTRACT + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This adds or subtracts each pixs value from pixd.
+ *      (2) This clips to the minimum of pixs and pixd, so they
+ *          do not need to be the same size.
+ *      (3) The alignment is to the origin [UL corner] of pixs & pixd.
+ * 
+ */ +l_ok +pixAccumulate(PIX *pixd, + PIX *pixs, + l_int32 op) +{ +l_int32 i, j, w, h, d, wd, hd, wpls, wpld; +l_uint32 *datas, *datad, *lines, *lined; + + + PROCNAME("pixAccumulate"); + + if (!pixd || (pixGetDepth(pixd) != 32)) + return ERROR_INT("pixd not defined or not 32 bpp", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + if (d != 1 && d != 8 && d != 16 && d != 32) + return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", procName, 1); + if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT) + return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}", + procName, 1); + + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs, &w, &h, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + w = L_MIN(w, wd); + h = L_MIN(h, hd); + if (d == 1) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_BIT(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_BIT(lines, j); + } + } + } else if (d == 8) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_BYTE(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_BYTE(lines, j); + } + } + } else if (d == 16) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += GET_DATA_TWO_BYTES(lines, j); + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= GET_DATA_TWO_BYTES(lines, j); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (op == L_ARITH_ADD) { + for (j = 0; j < w; j++) + lined[j] += lines[j]; + } else { /* op == L_ARITH_SUBTRACT */ + for (j = 0; j < w; j++) + lined[j] -= lines[j]; + } + } + } + + return 0; +} + + +/*! + * \brief pixMultConstAccumulate() + * + * \param[in] pixs 32 bpp + * \param[in] factor + * \param[in] offset same as used for initialization + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) %offset must be >= 0 and should not exceed 0x40000000.
+ *      (2) This multiplies each pixel, relative to offset, by %factor.
+ *      (3) The result is returned with %offset back in place.
+ * 
+ */ +l_ok +pixMultConstAccumulate(PIX *pixs, + l_float32 factor, + l_uint32 offset) +{ +l_int32 i, j, w, h, wpl, val; +l_uint32 *data, *line; + + PROCNAME("pixMultConstAccumulate"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", procName, 1); + if (offset > 0x40000000) + offset = 0x40000000; + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + val = line[j] - offset; + val = (l_int32)(val * factor); + val += offset; + line[j] = (l_uint32)val; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------------* + * Absolute value of difference * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixAbsDifference() + * + * \param[in] pixs1, pixs2 both either 8 or 16 bpp gray, or 32 bpp RGB + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The depth of pixs1 and pixs2 must be equal.
+ *      (2) Clips computation to the min size, aligning the UL corners
+ *      (3) For 8 and 16 bpp, assumes one gray component.
+ *      (4) For 32 bpp, assumes 3 color components, and ignores the
+ *          LSB of each word (the alpha channel)
+ *      (5) Computes the absolute value of the difference between
+ *          each component value.
+ * 
+ */ +PIX * +pixAbsDifference(PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff; +l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined; +PIX *pixd; + + PROCNAME("pixAbsDifference"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + d = pixGetDepth(pixs1); + if (d != pixGetDepth(pixs2)) + return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL); + + pixGetDimensions(pixs1, &w, &h, NULL); + pixGetDimensions(pixs2, &w2, &h2, NULL); + w = L_MIN(w, w2); + h = L_MIN(h, h2); + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs1); + datas1 = pixGetData(pixs1); + datas2 = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls1 = pixGetWpl(pixs1); + wpls2 = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + if (d == 8) { + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val1 = GET_DATA_BYTE(lines1, j); + val2 = GET_DATA_BYTE(lines2, j); + diff = L_ABS(val1 - val2); + SET_DATA_BYTE(lined, j, diff); + } + } + } else if (d == 16) { + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val1 = GET_DATA_TWO_BYTES(lines1, j); + val2 = GET_DATA_TWO_BYTES(lines2, j); + diff = L_ABS(val1 - val2); + SET_DATA_TWO_BYTES(lined, j, diff); + } + } + } else { /* d == 32 */ + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines1[j], &rval1, &gval1, &bval1); + extractRGBValues(lines2[j], &rval2, &gval2, &bval2); + rdiff = L_ABS(rval1 - rval2); + gdiff = L_ABS(gval1 - gval2); + bdiff = L_ABS(bval1 - bval2); + composeRGBPixel(rdiff, gdiff, bdiff, lined + j); + } + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Sum of color images * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixAddRGB() + * + * \param[in] pixs1, pixs2 32 bpp RGB, or colormapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Clips computation to the minimum size, aligning the UL corners.
+ *      (2) Removes any colormap to RGB, and ignores the LSB of each
+ *          pixel word (the alpha channel).
+ *      (3) Adds each component value, pixelwise, clipping to 255.
+ *      (4) This is useful to combine two images where most of the
+ *          pixels are essentially black, such as in pixPerceptualDiff().
+ * 
+ */ +PIX * +pixAddRGB(PIX *pixs1, + PIX *pixs2) +{ +l_int32 i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; +l_uint32 *datac1, *datac2, *datad, *linec1, *linec2, *lined; +PIX *pixc1, *pixc2, *pixd; + + PROCNAME("pixAddRGB"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); + pixGetDimensions(pixs1, &w, &h, &d); + pixGetDimensions(pixs2, &w2, &h2, &d2); + if (!pixGetColormap(pixs1) && d != 32) + return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", procName, NULL); + if (!pixGetColormap(pixs2) && d2 != 32) + return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", procName, NULL); + if (pixGetColormap(pixs1)) + pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc1 = pixClone(pixs1); + if (pixGetColormap(pixs2)) + pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR); + else + pixc2 = pixClone(pixs2); + + w = L_MIN(w, w2); + h = L_MIN(h, h2); + pixd = pixCreate(w, h, 32); + pixCopyResolution(pixd, pixs1); + datac1 = pixGetData(pixc1); + datac2 = pixGetData(pixc2); + datad = pixGetData(pixd); + wplc1 = pixGetWpl(pixc1); + wplc2 = pixGetWpl(pixc2); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linec1 = datac1 + i * wplc1; + linec2 = datac2 + i * wplc2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(linec1[j], &rval1, &gval1, &bval1); + extractRGBValues(linec2[j], &rval2, &gval2, &bval2); + rval = L_MIN(255, rval1 + rval2); + gval = L_MIN(255, gval1 + gval2); + bval = L_MIN(255, bval1 + bval2); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + pixDestroy(&pixc1); + pixDestroy(&pixc2); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Two-image min and max operations (8 and 16 bpp) * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMinOrMax() + * + * \param[in] pixd [optional] destination: this can be null, + * equal to pixs1, or different from pixs1 + * \param[in] pixs1 can be equal to pixd + * \param[in] pixs2 + * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX + * \return pixd always + * + *
+ * Notes:
+ *      (1) This gives the min or max of two images, component-wise.
+ *      (2) The depth can be 8 or 16 bpp for 1 component, and 32 bpp
+ *          for a 3 component image.  For 32 bpp, ignore the LSB
+ *          of each word (the alpha channel)
+ *      (3) There are 3 cases:
+ *          ~  if pixd == null,   Min(src1, src2) --> new pixd
+ *          ~  if pixd == pixs1,  Min(src1, src2) --> src1  (in-place)
+ *          ~  if pixd != pixs1,  Min(src1, src2) --> input pixd
+ * 
+ */ +PIX * +pixMinOrMax(PIX *pixd, + PIX *pixs1, + PIX *pixs2, + l_int32 type) +{ +l_int32 d, ws, hs, w, h, wpls, wpld, i, j, vals, vald, val; +l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; +l_uint32 *datas, *datad, *lines, *lined; + + PROCNAME("pixMinOrMax"); + + if (!pixs1) + return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); + if (!pixs2) + return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); + if (pixs1 == pixs2) + return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", procName, pixd); + if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) + return (PIX *)ERROR_PTR("invalid type", procName, pixd); + d = pixGetDepth(pixs1); + if (pixGetDepth(pixs2) != d) + return (PIX *)ERROR_PTR("depths unequal", procName, pixd); + if (d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not 8, 16 or 32 bpp", procName, pixd); + + if (pixs1 != pixd) + pixd = pixCopy(pixd, pixs1); + + pixGetDimensions(pixs2, &ws, &hs, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + w = L_MIN(w, ws); + h = L_MIN(h, hs); + datas = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (d == 8) { + for (j = 0; j < w; j++) { + vals = GET_DATA_BYTE(lines, j); + vald = GET_DATA_BYTE(lined, j); + if (type == L_CHOOSE_MIN) + val = L_MIN(vals, vald); + else /* type == L_CHOOSE_MAX */ + val = L_MAX(vals, vald); + SET_DATA_BYTE(lined, j, val); + } + } else if (d == 16) { + for (j = 0; j < w; j++) { + vals = GET_DATA_TWO_BYTES(lines, j); + vald = GET_DATA_TWO_BYTES(lined, j); + if (type == L_CHOOSE_MIN) + val = L_MIN(vals, vald); + else /* type == L_CHOOSE_MAX */ + val = L_MAX(vals, vald); + SET_DATA_TWO_BYTES(lined, j, val); + } + } else { /* d == 32 */ + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval1, &gval1, &bval1); + extractRGBValues(lined[j], &rval2, &gval2, &bval2); + if (type == L_CHOOSE_MIN) { + rval = L_MIN(rval1, rval2); + gval = L_MIN(gval1, gval2); + bval = L_MIN(bval1, bval2); + } else { /* type == L_CHOOSE_MAX */ + rval = L_MAX(rval1, rval2); + gval = L_MAX(gval1, gval2); + bval = L_MAX(bval1, bval2); + } + composeRGBPixel(rval, gval, bval, lined + j); + } + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Scale for maximum dynamic range * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixMaxDynamicRange() + * + * \param[in] pixs 4, 8, 16 or 32 bpp source + * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Scales pixel values to fit maximally within the dest 8 bpp pixd
+ *      (2) Assumes the source 'pixels' are a 1-component scalar.  For
+ *          a 32 bpp source, each pixel is treated as a single number --
+ *          not as a 3-component rgb pixel value.
+ *      (3) Uses a LUT for log scaling.
+ * 
+ */ +PIX * +pixMaxDynamicRange(PIX *pixs, + l_int32 type) +{ +l_uint8 dval; +l_int32 i, j, w, h, d, wpls, wpld, max; +l_uint32 *datas, *datad; +l_uint32 word, sval; +l_uint32 *lines, *lined; +l_float32 factor; +l_float32 *tab; +PIX *pixd; + + PROCNAME("pixMaxDynamicRange"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", procName, NULL); + if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + /* Get max */ + max = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + word = *(lines + j); + if (d == 4) { + max = L_MAX(max, word >> 28); + max = L_MAX(max, (word >> 24) & 0xf); + max = L_MAX(max, (word >> 20) & 0xf); + max = L_MAX(max, (word >> 16) & 0xf); + max = L_MAX(max, (word >> 12) & 0xf); + max = L_MAX(max, (word >> 8) & 0xf); + max = L_MAX(max, (word >> 4) & 0xf); + max = L_MAX(max, word & 0xf); + } else if (d == 8) { + max = L_MAX(max, word >> 24); + max = L_MAX(max, (word >> 16) & 0xff); + max = L_MAX(max, (word >> 8) & 0xff); + max = L_MAX(max, word & 0xff); + } else if (d == 16) { + max = L_MAX(max, word >> 16); + max = L_MAX(max, word & 0xffff); + } else { /* d == 32 (rgb) */ + max = L_MAX(max, word); + } + } + } + + /* Map to the full dynamic range */ + if (d == 4) { + if (type == L_LINEAR_SCALE) { + factor = 255. / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_QBIT(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255. / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else if (d == 8) { + if (type == L_LINEAR_SCALE) { + factor = 255. / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255. / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else if (d == 16) { + if (type == L_LINEAR_SCALE) { + factor = 255. / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_TWO_BYTES(lines, j); + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255. / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_TWO_BYTES(lines, j); + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } else { /* d == 32 */ + if (type == L_LINEAR_SCALE) { + factor = 255. / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = (l_uint8)(factor * (l_float32)sval + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255. / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); + SET_DATA_BYTE(lined, j, dval); + } + } + LEPT_FREE(tab); + } + } + + return pixd; +} + + +/*! + * \brief pixMaxDynamicRangeRGB() + * + * \param[in] pixs 32 bpp rgb source + * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Scales pixel values to fit maximally within a 32 bpp dest pixd
+ *      (2) All color components are scaled with the same factor, based
+ *          on the maximum r,g or b component in the image.  This should
+ *          not be used if the 32-bit value is a single number (e.g., a
+ *          count in a histogram generated by pixMakeHistoHS()).
+ *      (3) Uses a LUT for log scaling.
+ * 
+ */ +PIX * +pixMaxDynamicRangeRGB(PIX *pixs, + l_int32 type) +{ +l_int32 i, j, w, h, wpls, wpld, max; +l_uint32 sval, dval, word; +l_uint32 *datas, *datad; +l_uint32 *lines, *lined; +l_float32 factor; +l_float32 *tab; +PIX *pixd; + + PROCNAME("pixMaxDynamicRangeRGB"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + /* Get max */ + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + pixGetDimensions(pixs, &w, &h, NULL); + max = 0; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + word = lines[j]; + max = L_MAX(max, word >> 24); + max = L_MAX(max, (word >> 16) & 0xff); + max = L_MAX(max, (word >> 8) & 0xff); + } + } + + /* Map to the full dynamic range */ + if (type == L_LINEAR_SCALE) { + factor = 255. / (l_float32)max; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = linearScaleRGBVal(sval, factor); + lined[j] = dval; + } + } + } else { /* type == L_LOG_SCALE) */ + tab = makeLogBase2Tab(); + factor = 255. / getLogBase2(max, tab); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + sval = lines[j]; + dval = logScaleRGBVal(sval, tab, factor); + lined[j] = dval; + } + } + LEPT_FREE(tab); + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * RGB pixel value scaling * + *-----------------------------------------------------------------------*/ +/*! + * \brief linearScaleRGBVal() + * + * \param[in] sval 32-bit rgb pixel value + * \param[in] factor multiplication factor on each component + * \return dval linearly scaled version of %sval + * + *
+ * Notes:
+ *      (1) %factor must be chosen to be not greater than (255 / maxcomp),
+ *          where maxcomp is the maximum value of the pixel components.
+ *          Otherwise, the product will overflow a uint8.  In use, factor
+ *          is the same for all pixels in a pix.
+ *      (2) No scaling is performed on the transparency ("A") component.
+ * 
+ */ +l_uint32 +linearScaleRGBVal(l_uint32 sval, + l_float32 factor) +{ +l_uint32 dval; + + dval = ((l_uint8)(factor * (sval >> 24) + 0.5) << 24) | + ((l_uint8)(factor * ((sval >> 16) & 0xff) + 0.5) << 16) | + ((l_uint8)(factor * ((sval >> 8) & 0xff) + 0.5) << 8) | + (sval & 0xff); + return dval; +} + + +/*! + * \brief logScaleRGBVal() + * + * \param[in] sval 32-bit rgb pixel value + * \param[in] tab 256 entry log-base-2 table + * \param[in] factor multiplication factor on each component + * \return dval log scaled version of %sval + * + *
+ * Notes:
+ *      (1) %tab is made with makeLogBase2Tab().
+ *      (2) %factor must be chosen to be not greater than
+ *          255.0 / log[base2](maxcomp), where maxcomp is the maximum
+ *          value of the pixel components.  Otherwise, the product
+ *          will overflow a uint8.  In use, factor is the same for
+ *          all pixels in a pix.
+ *      (3) No scaling is performed on the transparency ("A") component.
+ * 
+ */ +l_uint32 +logScaleRGBVal(l_uint32 sval, + l_float32 *tab, + l_float32 factor) +{ +l_uint32 dval; + + dval = ((l_uint8)(factor * getLogBase2(sval >> 24, tab) + 0.5) << 24) | + ((l_uint8)(factor * getLogBase2(((sval >> 16) & 0xff), tab) + 0.5) + << 16) | + ((l_uint8)(factor * getLogBase2(((sval >> 8) & 0xff), tab) + 0.5) + << 8) | + (sval & 0xff); + return dval; +} + + +/*-----------------------------------------------------------------------* + * Log base2 lookup * + *-----------------------------------------------------------------------*/ +/* + * \brief makeLogBase2Tab() + * + * \return tab table giving the log[base2] of values from 1 to 255 + */ +l_float32 * +makeLogBase2Tab(void) +{ +l_int32 i; +l_float32 log2; +l_float32 *tab; + + PROCNAME("makeLogBase2Tab"); + + if ((tab = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32))) == NULL) + return (l_float32 *)ERROR_PTR("tab not made", procName, NULL); + + log2 = (l_float32)log((l_float32)2); + for (i = 0; i < 256; i++) + tab[i] = (l_float32)log((l_float32)i) / log2; + + return tab; +} + + +/* + * \brief getLogBase2() + * + * \param[in] val in range [0 ... 255] + * \param[in] logtab 256-entry table of logs + * \return logval log[base2] of %val, or 0 on error + */ +l_float32 +getLogBase2(l_int32 val, + l_float32 *logtab) +{ + PROCNAME("getLogBase2"); + + if (!logtab) + return ERROR_INT("logtab not defined", procName, 0); + + if (val < 0x100) + return logtab[val]; + else if (val < 0x10000) + return 8.0 + logtab[val >> 8]; + else if (val < 0x1000000) + return 16.0 + logtab[val >> 16]; + else + return 24.0 + logtab[val >> 24]; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixcomp.c b/hgdriver/3rdparty/hgOCR/leptonica/pixcomp.c new file mode 100644 index 0000000..6191ccd --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixcomp.c @@ -0,0 +1,2452 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixcomp.c + *
+ *
+ *      Pixcomp creation and destruction
+ *           PIXC     *pixcompCreateFromPix()
+ *           PIXC     *pixcompCreateFromString()
+ *           PIXC     *pixcompCreateFromFile()
+ *           void      pixcompDestroy()
+ *           PIXC     *pixcompCopy()
+
+ *      Pixcomp accessors
+ *           l_int32   pixcompGetDimensions()
+ *           l_int32   pixcompGetParameters()
+ *
+ *      Pixcomp compression selection
+ *           l_int32   pixcompDetermineFormat()
+ *
+ *      Pixcomp conversion to Pix
+ *           PIX      *pixCreateFromPixcomp()
+ *
+ *      Pixacomp creation and destruction
+ *           PIXAC    *pixacompCreate()
+ *           PIXAC    *pixacompCreateWithInit()
+ *           PIXAC    *pixacompCreateFromPixa()
+ *           PIXAC    *pixacompCreateFromFiles()
+ *           PIXAC    *pixacompCreateFromSA()
+ *           void      pixacompDestroy()
+ *
+ *      Pixacomp addition/replacement
+ *           l_int32   pixacompAddPix()
+ *           l_int32   pixacompAddPixcomp()
+ *           static l_int32  pixacompExtendArray()
+ *           l_int32   pixacompReplacePix()
+ *           l_int32   pixacompReplacePixcomp()
+ *           l_int32   pixacompAddBox()
+ *
+ *      Pixacomp accessors
+ *           l_int32   pixacompGetCount()
+ *           PIXC     *pixacompGetPixcomp()
+ *           PIX      *pixacompGetPix()
+ *           l_int32   pixacompGetPixDimensions()
+ *           BOXA     *pixacompGetBoxa()
+ *           l_int32   pixacompGetBoxaCount()
+ *           BOX      *pixacompGetBox()
+ *           l_int32   pixacompGetBoxGeometry()
+ *           l_int32   pixacompGetOffset()
+ *           l_int32   pixacompSetOffset()
+ *
+ *      Pixacomp conversion to Pixa
+ *           PIXA     *pixaCreateFromPixacomp()
+ *
+ *      Combining pixacomp
+ *           l_int32   pixacompJoin()
+ *           PIXAC    *pixacompInterleave()
+ *
+ *      Pixacomp serialized I/O
+ *           PIXAC    *pixacompRead()
+ *           PIXAC    *pixacompReadStream()
+ *           PIXAC    *pixacompReadMem()
+ *           l_int32   pixacompWrite()
+ *           l_int32   pixacompWriteStream()
+ *           l_int32   pixacompWriteMem()
+ *
+ *      Conversion to pdf
+ *           l_int32   pixacompConvertToPdf()
+ *           l_int32   pixacompConvertToPdfData()
+ *           l_int32   pixacompFastConvertToPdfData()
+ *
+ *      Output for debugging
+ *           l_int32   pixacompWriteStreamInfo()
+ *           l_int32   pixcompWriteStreamInfo()
+ *           PIX      *pixacompDisplayTiledAndScaled()
+ *           l_int32   pixacompWriteFiles()
+ *           l_int32   pixcompWriteFile()
+ *
+ *   The Pixacomp is an array of Pixcomp, where each Pixcomp is a compressed
+ *   string of the image.  We don't use reference counting here.
+ *   The basic application is to allow a large array of highly
+ *   compressible images to reside in memory.  We purposely don't
+ *   reuse the Pixa for this, to avoid confusion and programming errors.
+ *
+ *   Three compression formats are used: g4, png and jpeg.
+ *   The compression type can be either specified or defaulted.
+ *   If specified and it is not possible to compress (for example,
+ *   you specify a jpeg on a 1 bpp image or one with a colormap),
+ *   the compression type defaults to png.  The jpeg compression quality
+ *   can be specified using l_setJpegQuality(); otherwise the default is 75.
+ *
+ *   The serialized version of the Pixacomp is similar to that for
+ *   a Pixa, except that each Pixcomp can be compressed by one of
+ *   tiffg4, png, or jpeg.  Unlike serialization of the Pixa,
+ *   serialization of the Pixacomp does not require any imaging
+ *   libraries because it simply reads and writes the compressed data.
+ *
+ *   There are two modes of use in accumulating images:
+ *     (1) addition to the end of the array
+ *     (2) random insertion (replacement) into the array
+ *
+ *   In use, we assume that the array is fully populated up to the
+ *   index value (n - 1), where n is the value of the pixcomp field n.
+ *   Addition can only be made to the end of the fully populated array,
+ *   at the index value n.  Insertion can be made randomly, but again
+ *   only within the array of pixcomps; i.e., within the set of
+ *   indices {0 .... n-1}.  The functions are pixacompReplacePix()
+ *   and pixacompReplacePixcomp(), and they destroy the existing pixcomp.
+ *
+ *   For addition to the end of the array, initialize the pixacomp with
+ *   pixacompCreate(), which generates an empty array of pixcomps ptrs.
+ *   For random insertion and replacement of pixcomp into a pixacomp,
+ *   initialize a fully populated array using pixacompCreateWithInit().
+ *
+ *   The offset field allows you to use an offset-based index to
+ *   access the 0-based ptr array in the pixacomp.  This would typically
+ *   be used to map the pixacomp array index to a page number, or v.v.
+ *   By default, the offset is 0.  For example, suppose you have 50 images,
+ *   corresponding to page numbers 10 - 59.  Then you could use
+ *      pixac = pixacompCreateWithInit(50, 10, ...);
+ *   This would allocate an array of 50 pixcomps, but if you asked for
+ *   the pix at index 10, using pixacompGetPix(pixac, 10), it would
+ *   apply the offset internally, returning the pix at index 0 in the array.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 1000000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Bound on data size */ +static const size_t MaxDataSize = 1000000000; + + /* These two globals are defined in writefile.c */ +extern l_int32 NumImageFileFormatExtensions; +extern const char *ImageFileFormatExtensions[]; + + /* Static functions */ +static l_int32 pixacompExtendArray(PIXAC *pixac); +static l_int32 pixcompFastConvertToPdfData(PIXC *pixc, const char *title, + l_uint8 **pdata, size_t *pnbytes); + + +/*---------------------------------------------------------------------* + * Pixcomp creation and destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief pixcompCreateFromPix() + * + * \param[in] pix + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return pixc, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %comptype == IFF_DEFAULT to have the compression
+ *          type automatically determined.
+ *      (2) To compress jpeg with a quality other than the default (75), use
+ *             l_jpegSetQuality()
+ * 
+ */ +PIXC * +pixcompCreateFromPix(PIX *pix, + l_int32 comptype) +{ +size_t size; +char *text; +l_int32 ret, format; +l_uint8 *data; +PIXC *pixc; + + PROCNAME("pixcompCreateFromPix"); + + if (!pix) + return (PIXC *)ERROR_PTR("pix not defined", procName, NULL); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL); + + pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); + pixGetDimensions(pix, &pixc->w, &pixc->h, &pixc->d); + pixGetResolution(pix, &pixc->xres, &pixc->yres); + if (pixGetColormap(pix)) + pixc->cmapflag = 1; + if ((text = pixGetText(pix)) != NULL) + pixc->text = stringNew(text); + + pixcompDetermineFormat(comptype, pixc->d, pixc->cmapflag, &format); + pixc->comptype = format; + ret = pixWriteMem(&data, &size, pix, format); + if (ret) { + L_ERROR("write to memory failed\n", procName); + pixcompDestroy(&pixc); + return NULL; + } + pixc->data = data; + pixc->size = size; + + return pixc; +} + + +/*! + * \brief pixcompCreateFromString() + * + * \param[in] data compressed string + * \param[in] size number of bytes + * \param[in] copyflag L_INSERT or L_COPY + * \return pixc, or NULL on error + * + *
+ * Notes:
+ *      (1) This works when the compressed string is png, jpeg or tiffg4.
+ *      (2) The copyflag determines if the data in the new Pixcomp is
+ *          a copy of the input data.
+ * 
+ */ +PIXC * +pixcompCreateFromString(l_uint8 *data, + size_t size, + l_int32 copyflag) +{ +l_int32 format, w, h, d, bps, spp, iscmap; +PIXC *pixc; + + PROCNAME("pixcompCreateFromString"); + + if (!data) + return (PIXC *)ERROR_PTR("data not defined", procName, NULL); + if (copyflag != L_INSERT && copyflag != L_COPY) + return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL); + + if (pixReadHeaderMem(data, size, &format, &w, &h, &bps, &spp, &iscmap) == 1) + return (PIXC *)ERROR_PTR("header data not read", procName, NULL); + pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); + d = (spp == 3) ? 32 : bps * spp; + pixc->w = w; + pixc->h = h; + pixc->d = d; + pixc->comptype = format; + pixc->cmapflag = iscmap; + if (copyflag == L_INSERT) + pixc->data = data; + else + pixc->data = l_binaryCopy(data, size); + pixc->size = size; + return pixc; +} + + +/*! + * \brief pixcompCreateFromFile() + * + * \param[in] filename + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return pixc, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %comptype == IFF_DEFAULT to have the compression
+ *          type automatically determined.
+ *      (2) If the comptype is invalid for this file, the default will
+ *          be substituted.
+ * 
+ */ +PIXC * +pixcompCreateFromFile(const char *filename, + l_int32 comptype) +{ +l_int32 format; +size_t nbytes; +l_uint8 *data; +PIX *pix; +PIXC *pixc; + + PROCNAME("pixcompCreateFromFile"); + + if (!filename) + return (PIXC *)ERROR_PTR("filename not defined", procName, NULL); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL); + + findFileFormat(filename, &format); + if (format == IFF_UNKNOWN) { + L_ERROR("unreadable file: %s\n", procName, filename); + return NULL; + } + + /* Can we accept the encoded file directly? Remember that + * png is the "universal" compression type, so if requested + * it takes precedence. Otherwise, if the file is already + * compressed in g4 or jpeg, just accept the string. */ + if ((format == IFF_TIFF_G4 && comptype != IFF_PNG) || + (format == IFF_JFIF_JPEG && comptype != IFF_PNG)) + comptype = format; + if (comptype != IFF_DEFAULT && comptype == format) { + data = l_binaryRead(filename, &nbytes); + if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) { + LEPT_FREE(data); + return (PIXC *)ERROR_PTR("pixc not made (string)", procName, NULL); + } + return pixc; + } + + /* Need to recompress in the default format */ + if ((pix = pixRead(filename)) == NULL) + return (PIXC *)ERROR_PTR("pix not read", procName, NULL); + if ((pixc = pixcompCreateFromPix(pix, comptype)) == NULL) { + pixDestroy(&pix); + return (PIXC *)ERROR_PTR("pixc not made", procName, NULL); + } + pixDestroy(&pix); + return pixc; +} + + +/*! + * \brief pixcompDestroy() + * + * \param[in,out] ppixc use ptr address so it will be nulled + * \return void + * + *
+ * Notes:
+ *      (1) Always nulls the input ptr.
+ * 
+ */ +void +pixcompDestroy(PIXC **ppixc) +{ +PIXC *pixc; + + PROCNAME("pixcompDestroy"); + + if (!ppixc) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((pixc = *ppixc) == NULL) + return; + + LEPT_FREE(pixc->data); + if (pixc->text) + LEPT_FREE(pixc->text); + LEPT_FREE(pixc); + *ppixc = NULL; + return; +} + + +/*! + * \brief pixcompCopy() + * + * \param[in] pixcs + * \return pixcd, or NULL on error + */ +PIXC * +pixcompCopy(PIXC *pixcs) +{ +size_t size; +l_uint8 *datas, *datad; +PIXC *pixcd; + + PROCNAME("pixcompCopy"); + + if (!pixcs) + return (PIXC *)ERROR_PTR("pixcs not defined", procName, NULL); + + pixcd = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); + pixcd->w = pixcs->w; + pixcd->h = pixcs->h; + pixcd->d = pixcs->d; + pixcd->xres = pixcs->xres; + pixcd->yres = pixcs->yres; + pixcd->comptype = pixcs->comptype; + if (pixcs->text != NULL) + pixcd->text = stringNew(pixcs->text); + pixcd->cmapflag = pixcs->cmapflag; + + /* Copy image data */ + size = pixcs->size; + datas = pixcs->data; + if ((datad = (l_uint8 *)LEPT_CALLOC(size, sizeof(l_int8))) == NULL) { + pixcompDestroy(&pixcd); + return (PIXC *)ERROR_PTR("pixcd not made", procName, NULL); + } + memcpy(datad, datas, size); + pixcd->data = datad; + pixcd->size = size; + return pixcd; +} + + +/*---------------------------------------------------------------------* + * Pixcomp accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief pixcompGetDimensions() + * + * \param[in] pixc + * \param[out] pw, ph, pd [optional] + * \return 0 if OK, 1 on error + */ +l_ok +pixcompGetDimensions(PIXC *pixc, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd) +{ + PROCNAME("pixcompGetDimensions"); + + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + if (pw) *pw = pixc->w; + if (ph) *ph = pixc->h; + if (pd) *pd = pixc->d; + return 0; +} + + +/*! + * \brief pixcompGetParameters() + * + * \param[in] pixc + * \param[out] pxres, pyres, pcomptype, pcmapflag [optional] + * \return 0 if OK, 1 on error + */ +l_ok +pixcompGetParameters(PIXC *pixc, + l_int32 *pxres, + l_int32 *pyres, + l_int32 *pcomptype, + l_int32 *pcmapflag) +{ + PROCNAME("pixcompGetParameters"); + + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + if (pxres) *pxres = pixc->xres; + if (pyres) *pyres = pixc->yres; + if (pcomptype) *pcomptype = pixc->comptype; + if (pcmapflag) *pcmapflag = pixc->cmapflag; + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixcomp compression selection * + *---------------------------------------------------------------------*/ +/*! + * \brief pixcompDetermineFormat() + * + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \param[in] d pix depth + * \param[in] cmapflag 1 if pix to be compressed as a colormap; 0 otherwise + * \param[out] pformat IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This determines the best format for a pix, given both
+ *          the request (%comptype) and the image characteristics.
+ *      (2) If %comptype == IFF_DEFAULT, this does not necessarily result
+ *          in png encoding.  Instead, it returns one of the three formats
+ *          that is both valid and most likely to give best compression.
+ *      (3) If %d == 8 with no colormap and:
+ *          * you wish to compress with png, use %comptype == IFF_PNG
+ *          * you wish to compress with jpeg, use either
+ *            %comptype == IFF_JFIF_JPEG or %comptype == IFF_DEFAULT.
+ *      (4) If the pix cannot be compressed by the input value of
+ *          %comptype, this selects IFF_PNG, which can compress all pix.
+ * 
+ */ +l_ok +pixcompDetermineFormat(l_int32 comptype, + l_int32 d, + l_int32 cmapflag, + l_int32 *pformat) +{ + + PROCNAME("pixcompDetermineFormat"); + + if (!pformat) + return ERROR_INT("&format not defined", procName, 1); + *pformat = IFF_PNG; /* init value and default */ + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return ERROR_INT("invalid comptype", procName, 1); + + if (comptype == IFF_DEFAULT) { + if (d == 1) + *pformat = IFF_TIFF_G4; + else if (d == 16) + *pformat = IFF_PNG; + else if (d >= 8 && !cmapflag) + *pformat = IFF_JFIF_JPEG; + } else if (comptype == IFF_TIFF_G4 && d == 1) { + *pformat = IFF_TIFF_G4; + } else if (comptype == IFF_JFIF_JPEG && d >= 8 && !cmapflag) { + *pformat = IFF_JFIF_JPEG; + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixcomp conversion to Pix * + *---------------------------------------------------------------------*/ +/*! + * \brief pixCreateFromPixcomp() + * + * \param[in] pixc + * \return pix, or NULL on error + */ +PIX * +pixCreateFromPixcomp(PIXC *pixc) +{ +l_int32 w, h, d, cmapinpix, format; +PIX *pix; + + PROCNAME("pixCreateFromPixcomp"); + + if (!pixc) + return (PIX *)ERROR_PTR("pixc not defined", procName, NULL); + + if ((pix = pixReadMem(pixc->data, pixc->size)) == NULL) + return (PIX *)ERROR_PTR("pix not read", procName, NULL); + pixSetResolution(pix, pixc->xres, pixc->yres); + if (pixc->text) + pixSetText(pix, pixc->text); + + /* Check fields for consistency */ + pixGetDimensions(pix, &w, &h, &d); + if (pixc->w != w) { + L_INFO("pix width %d != pixc width %d\n", procName, w, pixc->w); + L_ERROR("pix width %d != pixc width\n", procName, w); + } + if (pixc->h != h) + L_ERROR("pix height %d != pixc height\n", procName, h); + if (pixc->d != d) { + if (pixc->d == 16) /* we strip 16 --> 8 bpp by default */ + L_WARNING("pix depth %d != pixc depth 16\n", procName, d); + else + L_ERROR("pix depth %d != pixc depth\n", procName, d); + } + cmapinpix = (pixGetColormap(pix) != NULL); + if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag)) + L_ERROR("pix cmap flag inconsistent\n", procName); + format = pixGetInputFormat(pix); + if (format != pixc->comptype) { + L_ERROR("pix comptype %d not equal to pixc comptype\n", + procName, format); + } + + return pix; +} + + +/*---------------------------------------------------------------------* + * Pixacomp creation and destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief pixacompCreate() + * + * \param[in] n initial number of ptrs + * \return pixac, or NULL on error + */ +PIXAC * +pixacompCreate(l_int32 n) +{ +PIXAC *pixac; + + PROCNAME("pixacompCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + pixac = (PIXAC *)LEPT_CALLOC(1, sizeof(PIXAC)); + pixac->n = 0; + pixac->nalloc = n; + pixac->offset = 0; + if ((pixac->pixc = (PIXC **)LEPT_CALLOC(n, sizeof(PIXC *))) == NULL) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL); + } + if ((pixac->boxa = boxaCreate(n)) == NULL) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL); + } + + return pixac; +} + + +/*! + * \brief pixacompCreateWithInit() + * + * \param[in] n initial number of ptrs + * \param[in] offset difference: accessor index - pixacomp array index + * \param[in] pix [optional] initialize each ptr in pixacomp + * to this pix; can be NULL + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return pixac, or NULL on error + * + *
+ * Notes:
+ *      (1) Initializes a pixacomp to be fully populated with %pix,
+ *          compressed using %comptype.  If %pix == NULL, %comptype
+ *          is ignored.
+ *      (2) Typically, the array is initialized with a tiny pix.
+ *          This is most easily done by setting %pix == NULL, causing
+ *          initialization of each array element with a tiny placeholder
+ *          pix (w = h = d = 1), using comptype = IFF_TIFF_G4 .
+ *      (3) Example usage:
+ *            // Generate pixacomp for pages 30 - 49.  This has an array
+ *            // size of 20 and the page number offset is 30.
+ *            PixaComp *pixac = pixacompCreateWithInit(20, 30, NULL,
+ *                                                     IFF_TIFF_G4);
+ *            // Now insert png-compressed images into the initialized array
+ *            for (pageno = 30; pageno < 50; pageno++) {
+ *                Pix *pixt = ...   // derived from image[pageno]
+ *                if (pixt)
+ *                    pixacompReplacePix(pixac, pageno, pixt, IFF_PNG);
+ *                pixDestroy(&pixt);
+ *            }
+ *          The result is a pixac with 20 compressed strings, and with
+ *          selected pixt replacing the placeholders.
+ *          To extract the image for page 38, which is decompressed
+ *          from element 8 in the array, use:
+ *            pixt = pixacompGetPix(pixac, 38);
+ * 
+ */ +PIXAC * +pixacompCreateWithInit(l_int32 n, + l_int32 offset, + PIX *pix, + l_int32 comptype) +{ +l_int32 i; +PIX *pixt; +PIXC *pixc; +PIXAC *pixac; + + PROCNAME("pixacompCreateWithInit"); + + if (n <= 0 || n > MaxPtrArraySize) + return (PIXAC *)ERROR_PTR("n out of valid bounds", procName, NULL); + if (pix) { + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); + } else { + comptype = IFF_TIFF_G4; + } + if (offset < 0) { + L_WARNING("offset < 0; setting to 0\n", procName); + offset = 0; + } + + if ((pixac = pixacompCreate(n)) == NULL) + return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); + pixacompSetOffset(pixac, offset); + if (pix) + pixt = pixClone(pix); + else + pixt = pixCreate(1, 1, 1); + for (i = 0; i < n; i++) { + pixc = pixcompCreateFromPix(pixt, comptype); + pixacompAddPixcomp(pixac, pixc, L_INSERT); + } + pixDestroy(&pixt); + + return pixac; +} + + +/*! + * \brief pixacompCreateFromPixa() + * + * \param[in] pixa + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If %format == IFF_DEFAULT, the conversion format for each
+ *          image is chosen automatically.  Otherwise, we use the
+ *          specified format unless it can't be done (e.g., jpeg
+ *          for a 1, 2 or 4 bpp pix, or a pix with a colormap),
+ *          in which case we use the default (assumed best) compression.
+ *      (2) %accesstype is used to extract a boxa from %pixa.
+ *      (3) To compress jpeg with a quality other than the default (75), use
+ *             l_jpegSetQuality()
+ * 
+ */ +PIXAC * +pixacompCreateFromPixa(PIXA *pixa, + l_int32 comptype, + l_int32 accesstype) +{ +l_int32 i, n; +BOXA *boxa; +PIX *pix; +PIXAC *pixac; + + PROCNAME("pixacompCreateFromPixa"); + + if (!pixa) + return (PIXAC *)ERROR_PTR("pixa not defined", procName, NULL); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE && + accesstype != L_COPY_CLONE) + return (PIXAC *)ERROR_PTR("invalid accesstype", procName, NULL); + + n = pixaGetCount(pixa); + if ((pixac = pixacompCreate(n)) == NULL) + return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixacompAddPix(pixac, pix, comptype); + pixDestroy(&pix); + } + if ((boxa = pixaGetBoxa(pixa, accesstype)) != NULL) { + boxaDestroy(&pixac->boxa); + pixac->boxa = boxa; + } + + return pixac; +} + + +/*! + * \brief pixacompCreateFromFiles() + * + * \param[in] dirname + * \param[in] substr [optional] substring filter on filenames; can be null + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return pixac, or NULL on error + * + *
+ * Notes:
+ *      (1) %dirname is the full path for the directory.
+ *      (2) %substr is the part of the file name (excluding
+ *          the directory) that is to be matched.  All matching
+ *          filenames are read into the Pixa.  If substr is NULL,
+ *          all filenames are read into the Pixa.
+ *      (3) Use %comptype == IFF_DEFAULT to have the compression
+ *          type automatically determined for each file.
+ *      (4) If the comptype is invalid for a file, the default will
+ *          be substituted.
+ * 
+ */ +PIXAC * +pixacompCreateFromFiles(const char *dirname, + const char *substr, + l_int32 comptype) +{ +PIXAC *pixac; +SARRAY *sa; + + PROCNAME("pixacompCreateFromFiles"); + + if (!dirname) + return (PIXAC *)ERROR_PTR("dirname not defined", procName, NULL); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return (PIXAC *)ERROR_PTR("sa not made", procName, NULL); + pixac = pixacompCreateFromSA(sa, comptype); + sarrayDestroy(&sa); + return pixac; +} + + +/*! + * \brief pixacompCreateFromSA() + * + * \param[in] sa full pathnames for all files + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return pixac, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %comptype == IFF_DEFAULT to have the compression
+ *          type automatically determined for each file.
+ *      (2) If the comptype is invalid for a file, the default will
+ *          be substituted.
+ * 
+ */ +PIXAC * +pixacompCreateFromSA(SARRAY *sa, + l_int32 comptype) +{ +char *str; +l_int32 i, n; +PIXC *pixc; +PIXAC *pixac; + + PROCNAME("pixacompCreateFromSA"); + + if (!sa) + return (PIXAC *)ERROR_PTR("sarray not defined", procName, NULL); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); + + n = sarrayGetCount(sa); + pixac = pixacompCreate(n); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + if ((pixc = pixcompCreateFromFile(str, comptype)) == NULL) { + L_ERROR("pixc not read from file: %s\n", procName, str); + continue; + } + pixacompAddPixcomp(pixac, pixc, L_INSERT); + } + return pixac; +} + + +/*! + * \brief pixacompDestroy() + * + * \param[in,out] ppixac use ptr address so it will be nulled + * \return void + * + *
+ * Notes:
+ *      (1) Always nulls the input ptr.
+ * 
+ */ +void +pixacompDestroy(PIXAC **ppixac) +{ +l_int32 i; +PIXAC *pixac; + + PROCNAME("pixacompDestroy"); + + if (ppixac == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((pixac = *ppixac) == NULL) + return; + + for (i = 0; i < pixac->n; i++) + pixcompDestroy(&pixac->pixc[i]); + LEPT_FREE(pixac->pixc); + boxaDestroy(&pixac->boxa); + LEPT_FREE(pixac); + + *ppixac = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Pixacomp addition * + *---------------------------------------------------------------------*/ +/*! + * \brief pixacompAddPix() + * + * \param[in] pixac + * \param[in] pix to be added + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The array is filled up to the (n-1)-th element, and this
+ *          converts the input pix to a pixc and adds it at
+ *          the n-th position.
+ *      (2) The pixc produced from the pix is owned by the pixac.
+ *          The input pix is not affected.
+ * 
+ */ +l_ok +pixacompAddPix(PIXAC *pixac, + PIX *pix, + l_int32 comptype) +{ +l_int32 cmapflag, format; +PIXC *pixc; + + PROCNAME("pixacompAddPix"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return ERROR_INT("invalid format", procName, 1); + + cmapflag = pixGetColormap(pix) ? 1 : 0; + pixcompDetermineFormat(comptype, pixGetDepth(pix), cmapflag, &format); + if ((pixc = pixcompCreateFromPix(pix, format)) == NULL) + return ERROR_INT("pixc not made", procName, 1); + pixacompAddPixcomp(pixac, pixc, L_INSERT); + return 0; +} + + +/*! + * \brief pixacompAddPixcomp() + * + * \param[in] pixac + * \param[in] pixc to be added by insertion + * \param[in] copyflag L_INSERT, L_COPY + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Anything added to a pixac is owned by the pixac.
+ *          So do not L_INSERT a pixc that is owned by another pixac,
+ *          or destroy a pixc that has been L_INSERTed.
+ * 
+ */ +l_ok +pixacompAddPixcomp(PIXAC *pixac, + PIXC *pixc, + l_int32 copyflag) +{ +l_int32 n; + + PROCNAME("pixacompAddPixcomp"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY) + return ERROR_INT("invalid copyflag", procName, 1); + + n = pixac->n; + if (n >= pixac->nalloc) + pixacompExtendArray(pixac); + if (copyflag == L_INSERT) + pixac->pixc[n] = pixc; + else /* L_COPY */ + pixac->pixc[n] = pixcompCopy(pixc); + pixac->n++; + + return 0; +} + + +/*! + * \brief pixacompExtendArray() + * + * \param[in] pixac + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) We extend the boxa array simultaneously.  This is
+ *          necessary in case we are NOT adding boxes simultaneously
+ *          with adding pixc.  We always want the sizes of the
+ *          pixac and boxa ptr arrays to be equal.
+ * 
+ */ +static l_int32 +pixacompExtendArray(PIXAC *pixac) +{ + PROCNAME("pixacompExtendArray"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + if ((pixac->pixc = (PIXC **)reallocNew((void **)&pixac->pixc, + sizeof(PIXC *) * pixac->nalloc, + 2 * sizeof(PIXC *) * pixac->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + pixac->nalloc = 2 * pixac->nalloc; + boxaExtendArray(pixac->boxa); + return 0; +} + + +/*! + * \brief pixacompReplacePix() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; includes offset + * \param[in] pix owned by the caller + * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ *      (2) The input %pix is converted to a pixc, which is then inserted
+ *          into the pixac.
+ * 
+ */ +l_ok +pixacompReplacePix(PIXAC *pixac, + l_int32 index, + PIX *pix, + l_int32 comptype) +{ +l_int32 n, aindex; +PIXC *pixc; + + PROCNAME("pixacompReplacePix"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + n = pixacompGetCount(pixac); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= n) + return ERROR_INT("array index out of bounds", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && + comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) + return ERROR_INT("invalid format", procName, 1); + + pixc = pixcompCreateFromPix(pix, comptype); + pixacompReplacePixcomp(pixac, index, pixc); + return 0; +} + + +/*! + * \brief pixacompReplacePixcomp() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; includes offset + * \param[in] pixc to replace existing one, which is destroyed + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ *      (2) The inserted %pixc is now owned by the pixac.  The caller
+ *          must not destroy it.
+ * 
+ */ +l_ok +pixacompReplacePixcomp(PIXAC *pixac, + l_int32 index, + PIXC *pixc) +{ +l_int32 n, aindex; +PIXC *pixct; + + PROCNAME("pixacompReplacePixcomp"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + n = pixacompGetCount(pixac); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= n) + return ERROR_INT("array index out of bounds", procName, 1); + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + + pixct = pixacompGetPixcomp(pixac, index, L_NOCOPY); /* use %index */ + pixcompDestroy(&pixct); + pixac->pixc[aindex] = pixc; /* replace; use array index */ + + return 0; +} + + +/*! + * \brief pixacompAddBox() + * + * \param[in] pixac + * \param[in] box + * \param[in] copyflag L_INSERT, L_COPY + * \return 0 if OK, 1 on error + */ +l_ok +pixacompAddBox(PIXAC *pixac, + BOX *box, + l_int32 copyflag) +{ + PROCNAME("pixacompAddBox"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY) + return ERROR_INT("invalid copyflag", procName, 1); + + boxaAddBox(pixac->boxa, box, copyflag); + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixacomp accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief pixacompGetCount() + * + * \param[in] pixac + * \return count, or 0 if no pixa + */ +l_int32 +pixacompGetCount(PIXAC *pixac) +{ + PROCNAME("pixacompGetCount"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 0); + + return pixac->n; +} + + +/*! + * \brief pixacompGetPixcomp() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; includes offset + * \param[in] copyflag L_NOCOPY, L_COPY + * \return pixc, or NULL on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ *      (2) If copyflag == L_NOCOPY, the pixc is owned by %pixac; do
+ *          not destroy.
+ * 
+ */ +PIXC * +pixacompGetPixcomp(PIXAC *pixac, + l_int32 index, + l_int32 copyflag) +{ +l_int32 aindex; + + PROCNAME("pixacompGetPixcomp"); + + if (!pixac) + return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL); + if (copyflag != L_NOCOPY && copyflag != L_COPY) + return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= pixac->n) + return (PIXC *)ERROR_PTR("array index not valid", procName, NULL); + + if (copyflag == L_NOCOPY) + return pixac->pixc[aindex]; + else /* L_COPY */ + return pixcompCopy(pixac->pixc[aindex]); +} + + +/*! + * \brief pixacompGetPix() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; includes offset + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ * 
+ */ +PIX * +pixacompGetPix(PIXAC *pixac, + l_int32 index) +{ +l_int32 aindex; +PIXC *pixc; + + PROCNAME("pixacompGetPix"); + + if (!pixac) + return (PIX *)ERROR_PTR("pixac not defined", procName, NULL); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= pixac->n) + return (PIX *)ERROR_PTR("array index not valid", procName, NULL); + + pixc = pixacompGetPixcomp(pixac, index, L_NOCOPY); + return pixCreateFromPixcomp(pixc); +} + + +/*! + * \brief pixacompGetPixDimensions() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; + * includes offset + * \param[out] pw, ph, pd [optional] each can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ * 
+ */ +l_ok +pixacompGetPixDimensions(PIXAC *pixac, + l_int32 index, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd) +{ +l_int32 aindex; +PIXC *pixc; + + PROCNAME("pixacompGetPixDimensions"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= pixac->n) + return ERROR_INT("array index not valid", procName, 1); + + if ((pixc = pixac->pixc[aindex]) == NULL) + return ERROR_INT("pixc not found!", procName, 1); + pixcompGetDimensions(pixc, pw, ph, pd); + return 0; +} + + +/*! + * \brief pixacompGetBoxa() + * + * \param[in] pixac + * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE + * \return boxa, or NULL on error + */ +BOXA * +pixacompGetBoxa(PIXAC *pixac, + l_int32 accesstype) +{ + PROCNAME("pixacompGetBoxa"); + + if (!pixac) + return (BOXA *)ERROR_PTR("pixac not defined", procName, NULL); + if (!pixac->boxa) + return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE && + accesstype != L_COPY_CLONE) + return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL); + + return boxaCopy(pixac->boxa, accesstype); +} + + +/*! + * \brief pixacompGetBoxaCount() + * + * \param[in] pixac + * \return count, or 0 on error + */ +l_int32 +pixacompGetBoxaCount(PIXAC *pixac) +{ + PROCNAME("pixacompGetBoxaCount"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 0); + + return boxaGetCount(pixac->boxa); +} + + +/*! + * \brief pixacompGetBox() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; + * includes offset + * \param[in] accesstype L_COPY or L_CLONE + * \return box if null, not automatically an error, or NULL on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ *      (2) There is always a boxa with a pixac, and it is initialized so
+ *          that each box ptr is NULL.
+ *      (3) In general, we expect that there is either a box associated
+ *          with each pixc, or no boxes at all in the boxa.
+ *      (4) Having no boxes is thus not an automatic error.  Whether it
+ *          is an actual error is determined by the calling program.
+ *          If the caller expects to get a box, it is an error; see, e.g.,
+ *          pixacGetBoxGeometry().
+ * 
+ */ +BOX * +pixacompGetBox(PIXAC *pixac, + l_int32 index, + l_int32 accesstype) +{ +l_int32 aindex; +BOX *box; + + PROCNAME("pixacompGetBox"); + + if (!pixac) + return (BOX *)ERROR_PTR("pixac not defined", procName, NULL); + if (!pixac->boxa) + return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= pixac->boxa->n) + return (BOX *)ERROR_PTR("array index not valid", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE) + return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL); + + box = pixac->boxa->box[aindex]; + if (box) { + if (accesstype == L_COPY) + return boxCopy(box); + else /* accesstype == L_CLONE */ + return boxClone(box); + } else { + return NULL; + } +} + + +/*! + * \brief pixacompGetBoxGeometry() + * + * \param[in] pixac + * \param[in] index caller's view of index within pixac; + * includes offset + * \param[out] px, py, pw, ph [optional] each can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The %index includes the offset, which must be subtracted
+ *          to get the actual index into the ptr array.
+ * 
+ */ +l_ok +pixacompGetBoxGeometry(PIXAC *pixac, + l_int32 index, + l_int32 *px, + l_int32 *py, + l_int32 *pw, + l_int32 *ph) +{ +l_int32 aindex; +BOX *box; + + PROCNAME("pixacompGetBoxGeometry"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + aindex = index - pixac->offset; + if (aindex < 0 || aindex >= pixac->n) + return ERROR_INT("array index not valid", procName, 1); + + if ((box = pixacompGetBox(pixac, aindex, L_CLONE)) == NULL) + return ERROR_INT("box not found!", procName, 1); + boxGetGeometry(box, px, py, pw, ph); + boxDestroy(&box); + return 0; +} + + +/*! + * \brief pixacompGetOffset() + * + * \param[in] pixac + * \return offset, or 0 on error + * + *
+ * Notes:
+ *      (1) The offset is the difference between the caller's view of
+ *          the index into the array and the actual array index.
+ *          By default it is 0.
+ * 
+ */ +l_int32 +pixacompGetOffset(PIXAC *pixac) +{ + PROCNAME("pixacompGetOffset"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 0); + return pixac->offset; +} + + +/*! + * \brief pixacompSetOffset() + * + * \param[in] pixac + * \param[in] offset non-negative + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The offset is the difference between the caller's view of
+ *          the index into the array and the actual array index.
+ *          By default it is 0.
+ * 
+ */ +l_ok +pixacompSetOffset(PIXAC *pixac, + l_int32 offset) +{ + PROCNAME("pixacompSetOffset"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + pixac->offset = L_MAX(0, offset); + return 0; +} + + +/*---------------------------------------------------------------------* + * Pixacomp conversion to Pixa * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaCreateFromPixacomp() + * + * \param[in] pixac + * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE; for boxa + * \return pixa if OK, or NULL on error + * + *
+ * Notes:
+ *      (1) Because the pixa has no notion of offset, the offset must
+ *          be set to 0 before the conversion, so that pixacompGetPix()
+ *          fetches all the pixcomps.  It is reset at the end.
+ * 
+ */ +PIXA * +pixaCreateFromPixacomp(PIXAC *pixac, + l_int32 accesstype) +{ +l_int32 i, n, offset; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixaCreateFromPixacomp"); + + if (!pixac) + return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL); + if (accesstype != L_COPY && accesstype != L_CLONE && + accesstype != L_COPY_CLONE) + return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL); + + n = pixacompGetCount(pixac); + offset = pixacompGetOffset(pixac); + pixacompSetOffset(pixac, 0); + if ((pixa = pixaCreate(n)) == NULL) + return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((pix = pixacompGetPix(pixac, i)) == NULL) { + L_WARNING("pix %d not made\n", procName, i); + continue; + } + pixaAddPix(pixa, pix, L_INSERT); + } + if (pixa->boxa) { + boxaDestroy(&pixa->boxa); + pixa->boxa = pixacompGetBoxa(pixac, accesstype); + } + pixacompSetOffset(pixac, offset); + + return pixa; +} + + +/*---------------------------------------------------------------------* + * Combining pixacomp + *---------------------------------------------------------------------*/ +/*! + * \brief pixacompJoin() + * + * \param[in] pixacd dest pixac; add to this one + * \param[in] pixacs [optional] source pixac; add from this one + * \param[in] istart starting index in pixacs + * \param[in] iend ending index in pixacs; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This appends a clone of each indicated pixc in pixcas to pixcad
+ *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (3) iend < 0 means 'read to the end'
+ *      (4) If pixacs is NULL or contains no pixc, this is a no-op.
+ * 
+ */ +l_ok +pixacompJoin(PIXAC *pixacd, + PIXAC *pixacs, + l_int32 istart, + l_int32 iend) +{ +l_int32 i, n, nb; +BOXA *boxas, *boxad; +PIXC *pixc; + + PROCNAME("pixacompJoin"); + + if (!pixacd) + return ERROR_INT("pixacd not defined", procName, 1); + if (!pixacs || ((n = pixacompGetCount(pixacs)) == 0)) + return 0; + + if (istart < 0) + istart = 0; + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; nothing to add", procName, 1); + + for (i = istart; i <= iend; i++) { + pixc = pixacompGetPixcomp(pixacs, i, L_NOCOPY); + pixacompAddPixcomp(pixacd, pixc, L_COPY); + } + + boxas = pixacompGetBoxa(pixacs, L_CLONE); + boxad = pixacompGetBoxa(pixacd, L_CLONE); + nb = pixacompGetBoxaCount(pixacs); + iend = L_MIN(iend, nb - 1); + boxaJoin(boxad, boxas, istart, iend); + boxaDestroy(&boxas); /* just the clones */ + boxaDestroy(&boxad); /* ditto */ + return 0; +} + + +/*! + * \brief pixacompInterleave() + * + * \param[in] pixac1 first src pixac + * \param[in] pixac2 second src pixac + * \return pixacd interleaved from sources, or NULL on error. + * + *
+ * Notes:
+ *      (1) If the two pixac have different sizes, a warning is issued,
+ *          and the number of pairs returned is the minimum size.
+ * 
+ */ +PIXAC * +pixacompInterleave(PIXAC *pixac1, + PIXAC *pixac2) +{ +l_int32 i, n1, n2, n, nb1, nb2; +BOX *box; +PIXC *pixc1, *pixc2; +PIXAC *pixacd; + + PROCNAME("pixacompInterleave"); + + if (!pixac1) + return (PIXAC *)ERROR_PTR("pixac1 not defined", procName, NULL); + if (!pixac2) + return (PIXAC *)ERROR_PTR("pixac2 not defined", procName, NULL); + n1 = pixacompGetCount(pixac1); + n2 = pixacompGetCount(pixac2); + n = L_MIN(n1, n2); + if (n == 0) + return (PIXAC *)ERROR_PTR("at least one input pixac is empty", + procName, NULL); + if (n1 != n2) + L_WARNING("counts differ: %d != %d\n", procName, n1, n2); + + pixacd = pixacompCreate(2 * n); + nb1 = pixacompGetBoxaCount(pixac1); + nb2 = pixacompGetBoxaCount(pixac2); + for (i = 0; i < n; i++) { + pixc1 = pixacompGetPixcomp(pixac1, i, L_COPY); + pixacompAddPixcomp(pixacd, pixc1, L_INSERT); + if (i < nb1) { + box = pixacompGetBox(pixac1, i, L_COPY); + pixacompAddBox(pixacd, box, L_INSERT); + } + pixc2 = pixacompGetPixcomp(pixac2, i, L_COPY); + pixacompAddPixcomp(pixacd, pixc2, L_INSERT); + if (i < nb2) { + box = pixacompGetBox(pixac2, i, L_COPY); + pixacompAddBox(pixacd, box, L_INSERT); + } + } + + return pixacd; +} + + +/*---------------------------------------------------------------------* + * Pixacomp serialized I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief pixacompRead() + * + * \param[in] filename + * \return pixac, or NULL on error + * + *
+ * Notes:
+ *      (1) Unlike the situation with serialized Pixa, where the image
+ *          data is stored in png format, the Pixacomp image data
+ *          can be stored in tiffg4, png and jpg formats.
+ * 
+ */ +PIXAC * +pixacompRead(const char *filename) +{ +FILE *fp; +PIXAC *pixac; + + PROCNAME("pixacompRead"); + + if (!filename) + return (PIXAC *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL); + pixac = pixacompReadStream(fp); + fclose(fp); + if (!pixac) + return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL); + return pixac; +} + + +/*! + * \brief pixacompReadStream() + * + * \param[in] fp file stream + * \return pixac, or NULL on error + */ +PIXAC * +pixacompReadStream(FILE *fp) +{ +char buf[256]; +l_uint8 *data; +l_int32 n, offset, i, w, h, d, ignore; +l_int32 comptype, cmapflag, version, xres, yres; +size_t size; +BOXA *boxa; +PIXC *pixc; +PIXAC *pixac; + + PROCNAME("pixacompReadStream"); + + if (!fp) + return (PIXAC *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nPixacomp Version %d\n", &version) != 1) + return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL); + if (version != PIXACOMP_VERSION_NUMBER) + return (PIXAC *)ERROR_PTR("invalid pixacomp version", procName, NULL); + if (fscanf(fp, "Number of pixcomp = %d\n", &n) != 1) + return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL); + if (fscanf(fp, "Offset of index into array = %d", &offset) != 1) + return (PIXAC *)ERROR_PTR("offset not read", procName, NULL); + + if ((pixac = pixacompCreate(n)) == NULL) + return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); + if ((boxa = boxaReadStream(fp)) == NULL) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL); + } + boxaDestroy(&pixac->boxa); /* empty */ + pixac->boxa = boxa; + pixacompSetOffset(pixac, offset); + + for (i = 0; i < n; i++) { + if (fscanf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n", + &ignore, &w, &h, &d) != 4) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("dimension reading", procName, NULL); + } + if (fscanf(fp, " comptype = %d, size = %zu, cmapflag = %d\n", + &comptype, &size, &cmapflag) != 3) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL); + } + if (size > MaxDataSize) { + pixacompDestroy(&pixac); + L_ERROR("data size = %zu is too big", procName, size); + return NULL; + } + + /* Use fgets() and sscanf(); not fscanf(), for the last + * bit of header data before the binary data. The reason is + * that fscanf throws away white space, and if the binary data + * happens to begin with ascii character(s) that are white + * space, it will swallow them and all will be lost! */ + if (fgets(buf, sizeof(buf), fp) == NULL) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("fgets read fail", procName, NULL); + } + if (sscanf(buf, " xres = %d, yres = %d\n", &xres, &yres) != 2) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("read fail for res", procName, NULL); + } + if ((data = (l_uint8 *)LEPT_CALLOC(1, size)) == NULL) { + pixacompDestroy(&pixac); + return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL); + } + if (fread(data, 1, size, fp) != size) { + pixacompDestroy(&pixac); + LEPT_FREE(data); + return (PIXAC *)ERROR_PTR("error reading data", procName, NULL); + } + fgetc(fp); /* swallow the ending nl */ + pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); + pixc->w = w; + pixc->h = h; + pixc->d = d; + pixc->xres = xres; + pixc->yres = yres; + pixc->comptype = comptype; + pixc->cmapflag = cmapflag; + pixc->data = data; + pixc->size = size; + pixacompAddPixcomp(pixac, pixc, L_INSERT); + } + return pixac; +} + + +/*! + * \brief pixacompReadMem() + * + * \param[in] data in pixacomp format + * \param[in] size of data + * \return pixac, or NULL on error + * + *
+ * Notes:
+ *      (1) Deseralizes a buffer of pixacomp data into a pixac in memory.
+ * 
+ */ +PIXAC * +pixacompReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +PIXAC *pixac; + + PROCNAME("pixacompReadMem"); + + if (!data) + return (PIXAC *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL); + + pixac = pixacompReadStream(fp); + fclose(fp); + if (!pixac) L_ERROR("pixac not read\n", procName); + return pixac; +} + + +/*! + * \brief pixacompWrite() + * + * \param[in] filename + * \param[in] pixac + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Unlike the situation with serialized Pixa, where the image
+ *          data is stored in png format, the Pixacomp image data
+ *          can be stored in tiffg4, png and jpg formats.
+ * 
+ */ +l_ok +pixacompWrite(const char *filename, + PIXAC *pixac) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixacompWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pixac) + return ERROR_INT("pixacomp not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixacompWriteStream(fp, pixac); + fclose(fp); + if (ret) + return ERROR_INT("pixacomp not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixacompWriteStream() + * + * \param[in] fp file stream + * \param[in] pixac + * \return 0 if OK, 1 on error + */ +l_ok +pixacompWriteStream(FILE *fp, + PIXAC *pixac) +{ +l_int32 n, i; +PIXC *pixc; + + PROCNAME("pixacompWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + n = pixacompGetCount(pixac); + fprintf(fp, "\nPixacomp Version %d\n", PIXACOMP_VERSION_NUMBER); + fprintf(fp, "Number of pixcomp = %d\n", n); + fprintf(fp, "Offset of index into array = %d", pixac->offset); + boxaWriteStream(fp, pixac->boxa); + for (i = 0; i < n; i++) { + if ((pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY)) + == NULL) + return ERROR_INT("pixc not found", procName, 1); + fprintf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n", + i, pixc->w, pixc->h, pixc->d); + fprintf(fp, " comptype = %d, size = %zu, cmapflag = %d\n", + pixc->comptype, pixc->size, pixc->cmapflag); + fprintf(fp, " xres = %d, yres = %d\n", pixc->xres, pixc->yres); + fwrite(pixc->data, 1, pixc->size, fp); + fprintf(fp, "\n"); + } + return 0; +} + + +/*! + * \brief pixacompWriteMem() + * + * \param[out] pdata serialized data of pixac + * \param[out] psize size of serialized data + * \param[in] pixac + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a pixac in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +pixacompWriteMem(l_uint8 **pdata, + size_t *psize, + PIXAC *pixac) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixacompWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!pixac) + return ERROR_INT("&pixac not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixacompWriteStream(fp, pixac); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = pixacompWriteStream(fp, pixac); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*--------------------------------------------------------------------* + * Conversion to pdf * + *--------------------------------------------------------------------*/ +/*! + * \brief pixacompConvertToPdf() + * + * \param[in] pixac containing images all at the same resolution + * \param[in] res override the resolution of each input image, + * in ppi; 0 to respect the resolution embedded + * in the input + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE, or + * L_DEFAULT_ENCODE for default) + * \param[in] quality used for JPEG only; 0 for default (75) + * \param[in] title [optional] pdf title + * \param[in] fileout pdf file of all images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This follows closely the function pixaConvertToPdf() in pdfio.c.
+ *      (2) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
+ *          colormap and many colors, or 32 bpp; FLATE for anything else.
+ *      (3) The scalefactor must be > 0.0; otherwise it is set to 1.0.
+ *      (4) Specifying one of the three encoding types for %type forces
+ *          all images to be compressed with that type.  Use 0 to have
+ *          the type determined for each image based on depth and whether
+ *          or not it has a colormap.
+ *      (5) If all images are jpeg compressed, don't require scaling
+ *          and have the same resolution, it is much faster to skip
+ *          transcoding with pixacompFastConvertToPdfData(), and then
+ *          write the data out to file.
+ * 
+ */ +l_ok +pixacompConvertToPdf(PIXAC *pixac, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + const char *fileout) +{ +l_uint8 *data; +l_int32 ret; +size_t nbytes; + + PROCNAME("pixacompConvertToPdf"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + ret = pixacompConvertToPdfData(pixac, res, scalefactor, type, quality, + title, &data, &nbytes); + if (ret) { + LEPT_FREE(data); + return ERROR_INT("conversion to pdf failed", procName, 1); + } + + ret = l_binaryWrite(fileout, "w", data, nbytes); + LEPT_FREE(data); + if (ret) + L_ERROR("pdf data not written to file\n", procName); + return ret; +} + + +/*! + * \brief pixacompConvertToPdfData() + * + * \param[in] pixac containing images all at the same resolution + * \param[in] res input resolution of all images + * \param[in] scalefactor scaling factor applied to each image; > 0.0 + * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, + * L_FLATE_ENCODE, L_JP2K_ENCODE, or + * L_DEFAULT_ENCODE for default) + * \param[in] quality used for JPEG only; 0 for default (75) + * \param[in] title [optional] pdf title + * \param[out] pdata output pdf data (of all images + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixacompConvertToPdf().
+ * 
+ */ +l_ok +pixacompConvertToPdfData(PIXAC *pixac, + l_int32 res, + l_float32 scalefactor, + l_int32 type, + l_int32 quality, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_uint8 *imdata; +l_int32 i, n, ret, scaledres, pagetype; +size_t imbytes; +L_BYTEA *ba; +PIX *pixs, *pix; +L_PTRA *pa_data; + + PROCNAME("pixacompConvertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + if (scalefactor <= 0.0) scalefactor = 1.0; + if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE && + type != L_G4_ENCODE && type != L_FLATE_ENCODE && + type != L_JP2K_ENCODE) { + L_WARNING("invalid compression type; using per-page default\n", + procName); + type = L_DEFAULT_ENCODE; + } + + /* Generate all the encoded pdf strings */ + n = pixacompGetCount(pixac); + pa_data = ptraCreate(n); + for (i = 0; i < n; i++) { + if ((pixs = + pixacompGetPix(pixac, pixacompGetOffset(pixac) + i)) == NULL) { + L_ERROR("pix[%d] not retrieved\n", procName, i); + continue; + } + if (pixGetWidth(pixs) == 1) { /* used sometimes as placeholders */ + L_INFO("placeholder image[%d] has w = 1\n", procName, i); + pixDestroy(&pixs); + continue; + } + if (scalefactor != 1.0) + pix = pixScale(pixs, scalefactor, scalefactor); + else + pix = pixClone(pixs); + pixDestroy(&pixs); + scaledres = (l_int32)(res * scalefactor); + + /* Select the encoding type */ + if (type != L_DEFAULT_ENCODE) { + pagetype = type; + } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { + L_ERROR("encoding type selection failed for pix[%d]\n", + procName, i); + pixDestroy(&pix); + continue; + } + + ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, + 0, 0, scaledres, title, NULL, 0); + pixDestroy(&pix); + if (ret) { + L_ERROR("pdf encoding failed for pix[%d]\n", procName, i); + continue; + } + ba = l_byteaInitFromMem(imdata, imbytes); + LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + ptraGetActualCount(pa_data, &n); + if (n == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate them */ + ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); + + ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ + for (i = 0; i < n; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + + +/*! + * \brief pixacompFastConvertToPdfData() + * + * \param[in] pixac containing images all at the same resolution + * \param[in] title [optional] pdf title + * \param[out] pdata output pdf data (of all images + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates the pdf without transcoding if all the
+ *          images in %pixac are compressed with jpeg.
+ *          Images not jpeg compressed are skipped.
+ *      (2) It assumes all images have the same resolution, and that
+ *          the resolution embedded in each jpeg file is correct.
+ * 
+ */ +l_ok +pixacompFastConvertToPdfData(PIXAC *pixac, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_uint8 *imdata; +l_int32 i, n, ret, comptype; +size_t imbytes; +L_BYTEA *ba; +PIXC *pixc; +L_PTRA *pa_data; + + PROCNAME("pixacompFastConvertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + /* Generate all the encoded pdf strings */ + n = pixacompGetCount(pixac); + pa_data = ptraCreate(n); + for (i = 0; i < n; i++) { + if ((pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY)) == NULL) { + L_ERROR("pixc[%d] not retrieved\n", procName, i); + continue; + } + pixcompGetParameters(pixc, NULL, NULL, &comptype, NULL); + if (comptype != IFF_JFIF_JPEG) { + L_ERROR("pixc[%d] not jpeg compressed\n", procName, i); + continue; + } + ret = pixcompFastConvertToPdfData(pixc, title, &imdata, &imbytes); + if (ret) { + L_ERROR("pdf encoding failed for pixc[%d]\n", procName, i); + continue; + } + ba = l_byteaInitFromMem(imdata, imbytes); + LEPT_FREE(imdata); + ptraAdd(pa_data, ba); + } + ptraGetActualCount(pa_data, &n); + if (n == 0) { + L_ERROR("no pdf files made\n", procName); + ptraDestroy(&pa_data, FALSE, FALSE); + return 1; + } + + /* Concatenate them */ + ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); + + /* Clean up */ + ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ + for (i = 0; i < n; i++) { + ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); + l_byteaDestroy(&ba); + } + ptraDestroy(&pa_data, FALSE, FALSE); + return ret; +} + + +/*! + * \brief pixcompFastConvertToPdfData() + * + * \param[in] pixc containing images all at the same resolution + * \param[in] title [optional] pdf title + * \param[out] pdata output pdf data (of all images + * \param[out] pnbytes size of output pdf data + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates the pdf without transcoding.
+ *      (2) It assumes all images are jpeg encoded, have the same
+ *          resolution, and that the resolution embedded in each
+ *          jpeg file is correct.  (It is transferred to the pdf
+ *          via the cid.)
+ * 
+ */ +static l_int32 +pixcompFastConvertToPdfData(PIXC *pixc, + const char *title, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_uint8 *data; +L_COMP_DATA *cid; + + PROCNAME("pixacompFastConvertToPdfData"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + *pdata = NULL; + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + + /* Make a copy of the data */ + data = l_binaryCopy(pixc->data, pixc->size); + cid = l_generateJpegDataMem(data, pixc->size, 0); + + /* Note: cid is destroyed, along with data, by this function */ + return cidConvertToPdfData(cid, title, pdata, pnbytes); +} + + +/*--------------------------------------------------------------------* + * Output for debugging * + *--------------------------------------------------------------------*/ +/*! + * \brief pixacompWriteStreamInfo() + * + * \param[in] fp file stream + * \param[in] pixac + * \param[in] text [optional] identifying string; can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixacompWriteStreamInfo(FILE *fp, + PIXAC *pixac, + const char *text) +{ +l_int32 i, n, nboxes; +PIXC *pixc; + + PROCNAME("pixacompWriteStreamInfo"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + if (text) + fprintf(fp, "Pixacomp Info for %s:\n", text); + else + fprintf(fp, "Pixacomp Info:\n"); + n = pixacompGetCount(pixac); + nboxes = pixacompGetBoxaCount(pixac); + fprintf(fp, "Number of pixcomp: %d\n", n); + fprintf(fp, "Size of pixcomp array alloc: %d\n", pixac->nalloc); + fprintf(fp, "Offset of index into array: %d\n", pixac->offset); + if (nboxes > 0) + fprintf(fp, "Boxa has %d boxes\n", nboxes); + else + fprintf(fp, "Boxa is empty\n"); + for (i = 0; i < n; i++) { + pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY); + pixcompWriteStreamInfo(fp, pixc, NULL); + } + return 0; +} + + +/*! + * \brief pixcompWriteStreamInfo() + * + * \param[in] fp file stream + * \param[in] pixc + * \param[in] text [optional] identifying string; can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixcompWriteStreamInfo(FILE *fp, + PIXC *pixc, + const char *text) +{ + PROCNAME("pixcompWriteStreamInfo"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + + if (text) + fprintf(fp, " Pixcomp Info for %s:", text); + else + fprintf(fp, " Pixcomp Info:"); + fprintf(fp, " width = %d, height = %d, depth = %d\n", + pixc->w, pixc->h, pixc->d); + fprintf(fp, " xres = %d, yres = %d, size in bytes = %zu\n", + pixc->xres, pixc->yres, pixc->size); + if (pixc->cmapflag) + fprintf(fp, " has colormap\n"); + else + fprintf(fp, " no colormap\n"); + if (pixc->comptype < NumImageFileFormatExtensions) { + fprintf(fp, " comptype = %s (%d)\n", + ImageFileFormatExtensions[pixc->comptype], pixc->comptype); + } else { + fprintf(fp, " Error!! Invalid comptype index: %d\n", pixc->comptype); + } + return 0; +} + + +/*! + * \brief pixacompDisplayTiledAndScaled() + * + * \param[in] pixac + * \param[in] outdepth output depth: 1, 8 or 32 bpp + * \param[in] tilewidth each pix is scaled to this width + * \param[in] ncols number of tiles in each row + * \param[in] background 0 for white, 1 for black; this is the color + * of the spacing between the images + * \param[in] spacing between images, and on outside + * \param[in] border width of additional black border on each image; + * use 0 for no border + * \return pix of tiled images, or NULL on error + * + *
+ * Notes:
+ *      (1) This is the same function as pixaDisplayTiledAndScaled(),
+ *          except it works on a Pixacomp instead of a Pix.  It is particularly
+ *          useful for showing the images in a Pixacomp at reduced resolution.
+ *      (2) See pixaDisplayTiledAndScaled() for details.
+ * 
+ */ +PIX * +pixacompDisplayTiledAndScaled(PIXAC *pixac, + l_int32 outdepth, + l_int32 tilewidth, + l_int32 ncols, + l_int32 background, + l_int32 spacing, + l_int32 border) +{ +PIX *pixd; +PIXA *pixa; + + PROCNAME("pixacompDisplayTiledAndScaled"); + + if (!pixac) + return (PIX *)ERROR_PTR("pixac not defined", procName, NULL); + + if ((pixa = pixaCreateFromPixacomp(pixac, L_COPY)) == NULL) + return (PIX *)ERROR_PTR("pixa not made", procName, NULL); + + pixd = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols, + background, spacing, border); + pixaDestroy(&pixa); + return pixd; +} + + +/*! + * \brief pixacompWriteFiles() + * + * \param[in] pixac + * \param[in] subdir subdirectory of /tmp + * \return 0 if OK, 1 on error + */ +l_ok +pixacompWriteFiles(PIXAC *pixac, + const char *subdir) +{ +char buf[128]; +l_int32 i, n; +PIXC *pixc; + + PROCNAME("pixacompWriteFiles"); + + if (!pixac) + return ERROR_INT("pixac not defined", procName, 1); + + if (lept_mkdir(subdir) > 0) + return ERROR_INT("invalid subdir", procName, 1); + + n = pixacompGetCount(pixac); + for (i = 0; i < n; i++) { + pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY); + snprintf(buf, sizeof(buf), "/tmp/%s/%03d", subdir, i); + pixcompWriteFile(buf, pixc); + } + return 0; +} + +extern const char *ImageFileFormatExtensions[]; + +/*! + * \brief pixcompWriteFile() + * + * \param[in] rootname + * \param[in] pixc + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The compressed data is written to file, and the filename is
+ *          generated by appending the format extension to %rootname.
+ * 
+ */ +l_ok +pixcompWriteFile(const char *rootname, + PIXC *pixc) +{ +char buf[128]; + + PROCNAME("pixcompWriteFile"); + + if (!pixc) + return ERROR_INT("pixc not defined", procName, 1); + + snprintf(buf, sizeof(buf), "%s.%s", rootname, + ImageFileFormatExtensions[pixc->comptype]); + l_binaryWrite(buf, "w", pixc->data, pixc->size); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixconv.c b/hgdriver/3rdparty/hgOCR/leptonica/pixconv.c new file mode 100644 index 0000000..38db0f0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixconv.c @@ -0,0 +1,4260 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixconv.c + *
+ *
+ *      These functions convert between images of different types
+ *      without scaling.
+ *
+ *      Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp
+ *           PIX        *pixThreshold8()
+ *
+ *      Conversion from colormap to full color or grayscale
+ *           PIX        *pixRemoveColormapGeneral()
+ *           PIX        *pixRemoveColormap()
+ *
+ *      Add colormap losslessly (8 to 8)
+ *           l_int32     pixAddGrayColormap8()
+ *           PIX        *pixAddMinimalGrayColormap8()
+ *
+ *      Conversion from RGB color to grayscale
+ *           PIX        *pixConvertRGBToLuminance()
+ *           PIX        *pixConvertRGBToGray()
+ *           PIX        *pixConvertRGBToGrayFast()
+ *           PIX        *pixConvertRGBToGrayMinMax()
+ *           PIX        *pixConvertRGBToGraySatBoost()
+ *           PIX        *pixConvertRGBToGrayArb()
+ *           PIX        *pixConvertRGBToBinaryArb()
+ *
+ *      Conversion from grayscale to colormap
+ *           PIX        *pixConvertGrayToColormap()  -- 2, 4, 8 bpp
+ *           PIX        *pixConvertGrayToColormap8()  -- 8 bpp only
+ *
+ *      Colorizing conversion from grayscale to color
+ *           PIX        *pixColorizeGray()  -- 8 bpp or cmapped
+ *
+ *      Conversion from RGB color to colormap
+ *           PIX        *pixConvertRGBToColormap()
+ *
+ *      Conversion from colormap to 1 bpp
+ *           PIX        *pixConvertCmapTo1()
+ *
+ *      Quantization for relatively small number of colors in source
+ *           l_int32     pixQuantizeIfFewColors()
+ *
+ *      Conversion from 16 bpp to 8 bpp
+ *           PIX        *pixConvert16To8()
+ *
+ *      Conversion from grayscale to false color
+ *           PIX        *pixConvertGrayToFalseColor()
+ *
+ *      Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp
+ *           PIX        *pixUnpackBinary()
+ *           PIX        *pixConvert1To16()
+ *           PIX        *pixConvert1To32()
+ *
+ *      Unpacking conversion from 1 bpp to 2 bpp
+ *           PIX        *pixConvert1To2Cmap()
+ *           PIX        *pixConvert1To2()
+ *
+ *      Unpacking conversion from 1 bpp to 4 bpp
+ *           PIX        *pixConvert1To4Cmap()
+ *           PIX        *pixConvert1To4()
+ *
+ *      Unpacking conversion from 1, 2 and 4 bpp to 8 bpp
+ *           PIX        *pixConvert1To8()
+ *           PIX        *pixConvert2To8()
+ *           PIX        *pixConvert4To8()
+ *
+ *      Unpacking conversion from 8 bpp to 16 bpp
+ *           PIX        *pixConvert8To16()
+ *
+ *      Top-level conversion to 1 bpp
+ *           PIX        *pixConvertTo1Adaptive()
+ *           PIX        *pixConvertTo1()
+ *           PIX        *pixConvertTo1BySampling()
+ *
+ *      Top-level conversion to 2 bpp
+ *           PIX        *pixConvertTo2()
+ *           PIX        *pixConvert8To2()
+ *
+ *      Top-level conversion to 4 bpp
+ *           PIX        *pixConvertTo4()
+ *           PIX        *pixConvert8To4()
+ *
+ *      Top-level conversion to 8 bpp
+ *           PIX        *pixConvertTo8()
+ *           PIX        *pixConvertTo8BySampling()
+ *           PIX        *pixConvertTo8Colormap()
+ *
+ *      Top-level conversion to 16 bpp
+ *           PIX        *pixConvertTo16()
+ *
+ *      Top-level conversion to 32 bpp (RGB)
+ *           PIX        *pixConvertTo32()   ***
+ *           PIX        *pixConvertTo32BySampling()   ***
+ *           PIX        *pixConvert8To32()  ***
+ *
+ *      Top-level conversion to 8 or 32 bpp, without colormap
+ *           PIX        *pixConvertTo8Or32
+ *
+ *      Conversion between 24 bpp and 32 bpp rgb
+ *           PIX        *pixConvert24To32()
+ *           PIX        *pixConvert32To24()
+ *
+ *      Conversion between 32 bpp (1 spp) and 16 or 8 bpp
+ *           PIX        *pixConvert32To16()
+ *           PIX        *pixConvert32To8()
+ *
+ *      Removal of alpha component by blending with white background
+ *           PIX        *pixRemoveAlpha()
+ *
+ *      Addition of alpha component to 1 bpp
+ *           PIX        *pixAddAlphaTo1bpp()
+ *
+ *      Lossless depth conversion (unpacking)
+ *           PIX        *pixConvertLossless()
+ *
+ *      Conversion for printing in PostScript
+ *           PIX        *pixConvertForPSWrap()
+ *
+ *      Scaling conversion to subpixel RGB
+ *           PIX        *pixConvertToSubpixelRGB()
+ *           PIX        *pixConvertGrayToSubpixelRGB()
+ *           PIX        *pixConvertColorToSubpixelRGB()
+ *
+ *      Setting neutral point for min/max boost conversion to gray
+ *          void         l_setNeutralBoostVal()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +/* ------- Set neutral point for min/max boost conversion to gray ------ */ + /* Call l_setNeutralBoostVal() to change this */ +static l_int32 var_NEUTRAL_BOOST_VAL = 180; + + +#ifndef NO_CONSOLE_IO +#define DEBUG_CONVERT_TO_COLORMAP 0 +#define DEBUG_UNROLLING 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp * + *-------------------------------------------------------------*/ +/*! + * \brief pixThreshold8() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] d destination depth: 1, 2, 4 or 8 + * \param[in] nlevels number of levels to be used for colormap + * \param[in] cmapflag 1 if makes colormap; 0 otherwise + * \return pixd thresholded with standard dest thresholds, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This uses, by default, equally spaced "target" values
+ *          that depend on the number of levels, with thresholds
+ *          halfway between.  For N levels, with separation (N-1)/255,
+ *          there are N-1 fixed thresholds.
+ *      (2) For 1 bpp destination, the number of levels can only be 2
+ *          and if a cmap is made, black is (0,0,0) and white
+ *          is (255,255,255), which is opposite to the convention
+ *          without a colormap.
+ *      (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap
+ *          is made; otherwise, we take the most significant bits
+ *          from the src that will fit in the dest.
+ *      (4) For 8 bpp, the input pixs is quantized to nlevels.  The
+ *          dest quantized with that mapping, either through a colormap
+ *          table or directly with 8 bit values.
+ *      (5) Typically you should not use make a colormap for 1 bpp dest.
+ *      (6) This is not dithering.  Each pixel is treated independently.
+ * 
+ */ +PIX * +pixThreshold8(PIX *pixs, + l_int32 d, + l_int32 nlevels, + l_int32 cmapflag) +{ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixThreshold8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (cmapflag && nlevels < 2) + return (PIX *)ERROR_PTR("nlevels must be at least 2", procName, NULL); + + switch (d) { + case 1: + pixd = pixThresholdToBinary(pixs, 128); + if (cmapflag) { + cmap = pixcmapCreateLinear(1, 2); + pixSetColormap(pixd, cmap); + } + break; + case 2: + pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag); + break; + case 4: + pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag); + break; + case 8: + pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag); + break; + default: + return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", procName, NULL); + } + + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*-------------------------------------------------------------* + * Conversion from colormapped pix * + *-------------------------------------------------------------*/ +/*! + * \brief pixRemoveColormapGeneral() + * + * \param[in] pixs any depth, with or without colormap + * \param[in] type REMOVE_CMAP_TO_BINARY, + * REMOVE_CMAP_TO_GRAYSCALE, + * REMOVE_CMAP_TO_FULL_COLOR, + * REMOVE_CMAP_WITH_ALPHA, + * REMOVE_CMAP_BASED_ON_SRC + * \param[in] ifnocmap L_CLONE, L_COPY + * \return pixd always a new pix; without colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Convenience function that allows choice between returning
+ *          a clone or a copy if pixs does not have a colormap.
+ *      (2) See pixRemoveColormap().
+ * 
+ */ +PIX * +pixRemoveColormapGeneral(PIX *pixs, + l_int32 type, + l_int32 ifnocmap) +{ + PROCNAME("pixRemoveColormapGeneral"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (ifnocmap != L_CLONE && ifnocmap != L_COPY) + return (PIX *)ERROR_PTR("invalid value for ifnocmap", procName, NULL); + + if (pixGetColormap(pixs)) + return pixRemoveColormap(pixs, type); + + if (ifnocmap == L_CLONE) + return pixClone(pixs); + else + return pixCopy(NULL, pixs); +} + + +/*! + * \brief pixRemoveColormap() + * + * \param[in] pixs see restrictions below + * \param[in] type REMOVE_CMAP_TO_BINARY, + * REMOVE_CMAP_TO_GRAYSCALE, + * REMOVE_CMAP_TO_FULL_COLOR, + * REMOVE_CMAP_WITH_ALPHA, + * REMOVE_CMAP_BASED_ON_SRC + * \return pixd without colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs does not have a colormap, a clone is returned.
+ *      (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp.
+ *      (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix.
+ *      (4) For grayscale conversion from RGB, use a weighted average
+ *          of RGB values, and always return an 8 bpp pix, regardless
+ *          of whether the input pixs depth is 2, 4 or 8 bpp.
+ *      (5) REMOVE_CMAP_TO_FULL_COLOR ignores the alpha component and
+ *          returns a 32 bpp pix with spp == 3 and the alpha bytes are 0.
+ *      (6) For REMOVE_CMAP_BASED_ON_SRC, if there is no color, this
+ *          returns either a 1 bpp or 8 bpp grayscale pix.
+ *          If there is color, this returns a 32 bpp pix, with either:
+ *           * 3 spp, if the alpha values are all 255 (opaque), or
+ *           * 4 spp (preserving the alpha), if any alpha values are not 255.
+ * 
+ */ +PIX * +pixRemoveColormap(PIX *pixs, + l_int32 type) +{ +l_int32 sval, rval, gval, bval, val0, val1; +l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, nalloc, count; +l_int32 opaque, colorfound, blackwhite; +l_int32 *rmap, *gmap, *bmap, *amap; +l_uint32 *datas, *lines, *datad, *lined, *lut, *graymap; +l_uint32 sword, dword; +PIXCMAP *cmap; +PIX *pixd; + + PROCNAME("pixRemoveColormap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if ((cmap = pixGetColormap(pixs)) == NULL) + return pixClone(pixs); + if (type != REMOVE_CMAP_TO_BINARY && + type != REMOVE_CMAP_TO_GRAYSCALE && + type != REMOVE_CMAP_TO_FULL_COLOR && + type != REMOVE_CMAP_WITH_ALPHA && + type != REMOVE_CMAP_BASED_ON_SRC) { + L_WARNING("Invalid type; converting based on src\n", procName); + type = REMOVE_CMAP_BASED_ON_SRC; + } + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", procName, NULL); + + ncolors = pixcmapGetCount(cmap); + nalloc = 1 << d; /* allocate for max size in case of pixel corruption */ + if (ncolors > nalloc) + return (PIX *)ERROR_PTR("too many colors for pixel depth", + procName, NULL); + + if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap)) + return (PIX *)ERROR_PTR("colormap arrays not made", procName, NULL); + + if (d != 1 && type == REMOVE_CMAP_TO_BINARY) { + L_WARNING("not 1 bpp; can't remove cmap to binary\n", procName); + type = REMOVE_CMAP_BASED_ON_SRC; + } + + /* Select output type depending on colormap content */ + if (type == REMOVE_CMAP_BASED_ON_SRC) { + pixcmapIsOpaque(cmap, &opaque); + pixcmapHasColor(cmap, &colorfound); + pixcmapIsBlackAndWhite(cmap, &blackwhite); + if (!opaque) { /* save the alpha */ + type = REMOVE_CMAP_WITH_ALPHA; + } else if (colorfound) { + type = REMOVE_CMAP_TO_FULL_COLOR; + } else { /* opaque and no color */ + if (d == 1 && blackwhite) /* can binarize without loss */ + type = REMOVE_CMAP_TO_BINARY; + else + type = REMOVE_CMAP_TO_GRAYSCALE; + } + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (type == REMOVE_CMAP_TO_BINARY) { + if ((pixd = pixCopy(NULL, pixs)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup_arrays; + } + pixcmapGetColor(cmap, 0, &rval, &gval, &bval); + val0 = rval + gval + bval; + pixcmapGetColor(cmap, 1, &rval, &gval, &bval); + val1 = rval + gval + bval; + if (val0 < val1) /* photometrically inverted from standard */ + pixInvert(pixd, pixd); + pixDestroyColormap(pixd); + } else if (type == REMOVE_CMAP_TO_GRAYSCALE) { + if ((pixd = pixCreate(w, h, 8)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup_arrays; + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + graymap = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); + for (i = 0; i < ncolors; i++) { + graymap[i] = (l_uint32)(L_RED_WEIGHT * rmap[i] + + L_GREEN_WEIGHT * gmap[i] + + L_BLUE_WEIGHT * bmap[i] + 0.5); + } + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + switch (d) /* depth test above; no default permitted */ + { + case 8: + /* Unrolled 4x */ + for (j = 0, count = 0; j + 3 < w; j += 4, count++) { + sword = lines[count]; + dword = (graymap[(sword >> 24) & 0xff] << 24) | + (graymap[(sword >> 16) & 0xff] << 16) | + (graymap[(sword >> 8) & 0xff] << 8) | + graymap[sword & 0xff]; + lined[count] = dword; + } + /* Cleanup partial word */ + for (; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + gval = graymap[sval]; + SET_DATA_BYTE(lined, j, gval); + } +#if DEBUG_UNROLLING +#define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \ + fprintf(stderr, "Error: mismatch at %d, %d vs %d\n", \ + j, GET_DATA_BYTE(a, b), c); } + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + gval = graymap[sval]; + CHECK_VALUE(lined, j, gval); + } +#endif + break; + case 4: + /* Unrolled 8x */ + for (j = 0, count = 0; j + 7 < w; j += 8, count++) { + sword = lines[count]; + dword = (graymap[(sword >> 28) & 0xf] << 24) | + (graymap[(sword >> 24) & 0xf] << 16) | + (graymap[(sword >> 20) & 0xf] << 8) | + graymap[(sword >> 16) & 0xf]; + lined[2 * count] = dword; + dword = (graymap[(sword >> 12) & 0xf] << 24) | + (graymap[(sword >> 8) & 0xf] << 16) | + (graymap[(sword >> 4) & 0xf] << 8) | + graymap[sword & 0xf]; + lined[2 * count + 1] = dword; + } + /* Cleanup partial word */ + for (; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + gval = graymap[sval]; + SET_DATA_BYTE(lined, j, gval); + } +#if DEBUG_UNROLLING + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + gval = graymap[sval]; + CHECK_VALUE(lined, j, gval); + } +#endif + break; + case 2: + /* Unrolled 16x */ + for (j = 0, count = 0; j + 15 < w; j += 16, count++) { + sword = lines[count]; + dword = (graymap[(sword >> 30) & 0x3] << 24) | + (graymap[(sword >> 28) & 0x3] << 16) | + (graymap[(sword >> 26) & 0x3] << 8) | + graymap[(sword >> 24) & 0x3]; + lined[4 * count] = dword; + dword = (graymap[(sword >> 22) & 0x3] << 24) | + (graymap[(sword >> 20) & 0x3] << 16) | + (graymap[(sword >> 18) & 0x3] << 8) | + graymap[(sword >> 16) & 0x3]; + lined[4 * count + 1] = dword; + dword = (graymap[(sword >> 14) & 0x3] << 24) | + (graymap[(sword >> 12) & 0x3] << 16) | + (graymap[(sword >> 10) & 0x3] << 8) | + graymap[(sword >> 8) & 0x3]; + lined[4 * count + 2] = dword; + dword = (graymap[(sword >> 6) & 0x3] << 24) | + (graymap[(sword >> 4) & 0x3] << 16) | + (graymap[(sword >> 2) & 0x3] << 8) | + graymap[sword & 0x3]; + lined[4 * count + 3] = dword; + } + /* Cleanup partial word */ + for (; j < w; j++) { + sval = GET_DATA_DIBIT(lines, j); + gval = graymap[sval]; + SET_DATA_BYTE(lined, j, gval); + } +#if DEBUG_UNROLLING + for (j = 0; j < w; j++) { + sval = GET_DATA_DIBIT(lines, j); + gval = graymap[sval]; + CHECK_VALUE(lined, j, gval); + } +#endif + break; + case 1: + /* Unrolled 8x */ + for (j = 0, count = 0; j + 31 < w; j += 32, count++) { + sword = lines[count]; + for (k = 0; k < 4; k++) { + /* The top byte is always the relevant one */ + dword = (graymap[(sword >> 31) & 0x1] << 24) | + (graymap[(sword >> 30) & 0x1] << 16) | + (graymap[(sword >> 29) & 0x1] << 8) | + graymap[(sword >> 28) & 0x1]; + lined[8 * count + 2 * k] = dword; + dword = (graymap[(sword >> 27) & 0x1] << 24) | + (graymap[(sword >> 26) & 0x1] << 16) | + (graymap[(sword >> 25) & 0x1] << 8) | + graymap[(sword >> 24) & 0x1]; + lined[8 * count + 2 * k + 1] = dword; + sword <<= 8; /* Move up the next byte */ + } + } + /* Cleanup partial word */ + for (; j < w; j++) { + sval = GET_DATA_BIT(lines, j); + gval = graymap[sval]; + SET_DATA_BYTE(lined, j, gval); + } +#if DEBUG_UNROLLING + for (j = 0; j < w; j++) { + sval = GET_DATA_BIT(lines, j); + gval = graymap[sval]; + CHECK_VALUE(lined, j, gval); + } +#undef CHECK_VALUE +#endif + break; + default: + return NULL; + } + } + if (graymap) + LEPT_FREE(graymap); + } else { /* type == REMOVE_CMAP_TO_FULL_COLOR or REMOVE_CMAP_WITH_ALPHA */ + if ((pixd = pixCreate(w, h, 32)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup_arrays; + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + if (type == REMOVE_CMAP_WITH_ALPHA) + pixSetSpp(pixd, 4); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + lut = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); + for (i = 0; i < ncolors; i++) { + if (type == REMOVE_CMAP_TO_FULL_COLOR) + composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i); + else /* full color plus alpha */ + composeRGBAPixel(rmap[i], gmap[i], bmap[i], amap[i], lut + i); + } + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + if (d == 8) + sval = GET_DATA_BYTE(lines, j); + else if (d == 4) + sval = GET_DATA_QBIT(lines, j); + else if (d == 2) + sval = GET_DATA_DIBIT(lines, j); + else /* (d == 1) */ + sval = GET_DATA_BIT(lines, j); + if (sval >= ncolors) + L_WARNING("pixel value out of bounds\n", procName); + else + lined[j] = lut[sval]; + } + } + LEPT_FREE(lut); + } + +cleanup_arrays: + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + LEPT_FREE(amap); + return pixd; +} + + +/*-------------------------------------------------------------* + * Add colormap losslessly (8 to 8) * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddGrayColormap8() + * + * \param[in] pixs 8 bpp + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, this is a no-op.
+ * 
+ */ +l_ok +pixAddGrayColormap8(PIX *pixs) +{ +PIXCMAP *cmap; + + PROCNAME("pixAddGrayColormap8"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (pixGetColormap(pixs)) + return 0; + + cmap = pixcmapCreateLinear(8, 256); + pixSetColormap(pixs, cmap); + return 0; +} + + +/*! + * \brief pixAddMinimalGrayColormap8() + * + * \param[in] pixs 8 bpp + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a colormapped version of the input image
+ *          that has the same number of colormap entries as the
+ *          input image has unique gray levels.
+ * 
+ */ +PIX * +pixAddMinimalGrayColormap8(PIX *pixs) +{ +l_int32 ncolors, w, h, i, j, wpl1, wpld, index, val; +l_int32 *inta, *revmap; +l_uint32 *data1, *datad, *line1, *lined; +PIX *pix1, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixAddMinimalGrayColormap8"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + /* Eliminate the easy cases */ + pixNumColors(pixs, 1, &ncolors); + cmap = pixGetColormap(pixs); + if (cmap) { + if (pixcmapGetCount(cmap) == ncolors) /* irreducible */ + return pixCopy(NULL, pixs); + else + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + } else { + if (ncolors == 256) { + pix1 = pixCopy(NULL, pixs); + pixAddGrayColormap8(pix1); + return pix1; + } + pix1 = pixClone(pixs); + } + + /* Find the gray levels and make a reverse map */ + pixGetDimensions(pix1, &w, &h, NULL); + data1 = pixGetData(pix1); + wpl1 = pixGetWpl(pix1); + inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl1; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(line1, j); + inta[val] = 1; + } + } + cmap = pixcmapCreate(8); + revmap = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0, index = 0; i < 256; i++) { + if (inta[i]) { + pixcmapAddColor(cmap, i, i, i); + revmap[i] = index++; + } + } + + /* Set all pixels in pixd to the colormap index */ + pixd = pixCreateTemplate(pix1); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl1; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(line1, j); + SET_DATA_BYTE(lined, j, revmap[val]); + } + } + + pixDestroy(&pix1); + LEPT_FREE(inta); + LEPT_FREE(revmap); + return pixd; +} + + +/*-------------------------------------------------------------* + * Conversion from RGB color to grayscale * + *-------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToLuminance() + * + * \param[in] pixs 32 bpp RGB + * \return 8 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) Use a standard luminance conversion.
+ * 
+ */ +PIX * +pixConvertRGBToLuminance(PIX *pixs) +{ + return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0); +} + + +/*! + * \brief pixConvertRGBToGray() + * + * \param[in] pixs 32 bpp RGB + * \param[in] rwt, gwt, bwt non-negative; these should add to 1.0, + * or use 0.0 for default + * \return 8 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) Use a weighted average of the RGB values.
+ * 
+ */ +PIX * +pixConvertRGBToGray(PIX *pixs, + l_float32 rwt, + l_float32 gwt, + l_float32 bwt) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 word; +l_uint32 *datas, *lines, *datad, *lined; +l_float32 sum; +PIX *pixd; + + PROCNAME("pixConvertRGBToGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) + return (PIX *)ERROR_PTR("weights not all >= 0.0", procName, NULL); + + /* Make sure the sum of weights is 1.0; otherwise, you can get + * overflow in the gray value. */ + if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { + rwt = L_RED_WEIGHT; + gwt = L_GREEN_WEIGHT; + bwt = L_BLUE_WEIGHT; + } + sum = rwt + gwt + bwt; + if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ + L_WARNING("weights don't sum to 1; maintaining ratios\n", procName); + rwt = rwt / sum; + gwt = gwt / sum; + bwt = bwt / sum; + } + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + word = *(lines + j); + val = (l_int32)(rwt * ((word >> L_RED_SHIFT) & 0xff) + + gwt * ((word >> L_GREEN_SHIFT) & 0xff) + + bwt * ((word >> L_BLUE_SHIFT) & 0xff) + 0.5); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertRGBToGrayFast() + * + * \param[in] pixs 32 bpp RGB + * \return 8 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This function should be used if speed of conversion
+ *          is paramount, and the green channel can be used as
+ *          a fair representative of the RGB intensity.  It is
+ *          several times faster than pixConvertRGBToGray().
+ *      (2) To combine RGB to gray conversion with subsampling,
+ *          use pixScaleRGBToGrayFast() instead.
+ * 
+ */ +PIX * +pixConvertRGBToGrayFast(PIX *pixs) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixd; + + PROCNAME("pixConvertRGBToGrayFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++, lines++) { + val = ((*lines) >> L_GREEN_SHIFT) & 0xff; + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertRGBToGrayMinMax() + * + * \param[in] pixs 32 bpp RGB + * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF, + * L_CHOOSE_MIN_BOOST, L_CHOOSE_MAX_BOOST + * \return 8 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This chooses various components or combinations of them,
+ *          from the three RGB sample values.  In addition to choosing
+ *          the min, max, and maxdiff (difference between max and min),
+ *          this also allows boosting the min and max about a reference
+ *          value.
+ *      (2) The default reference value for boosting the min and max
+ *          is 200.  This can be changed with l_setNeutralBoostVal()
+ * 
+ */ +PIX * +pixConvertRGBToGrayMinMax(PIX *pixs, + l_int32 type) +{ +l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val, minval, maxval; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixd; + + PROCNAME("pixConvertRGBToGrayMinMax"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && + type != L_CHOOSE_MAXDIFF && type != L_CHOOSE_MIN_BOOST && + type != L_CHOOSE_MAX_BOOST) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + if (type == L_CHOOSE_MIN || type == L_CHOOSE_MIN_BOOST) { + val = L_MIN(rval, gval); + val = L_MIN(val, bval); + if (type == L_CHOOSE_MIN_BOOST) + val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); + } else if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_BOOST) { + val = L_MAX(rval, gval); + val = L_MAX(val, bval); + if (type == L_CHOOSE_MAX_BOOST) + val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); + } else { /* L_CHOOSE_MAXDIFF */ + minval = L_MIN(rval, gval); + minval = L_MIN(minval, bval); + maxval = L_MAX(rval, gval); + maxval = L_MAX(maxval, bval); + val = maxval - minval; + } + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertRGBToGraySatBoost() + * + * \param[in] pixs 32 bpp rgb + * \param[in] refval between 1 and 255; typ. less than 128 + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns the max component value, boosted by
+ *          the saturation. The maximum boost occurs where
+ *          the maximum component value is equal to some reference value.
+ *          This particular weighting is due to Dany Qumsiyeh.
+ *      (2) For gray pixels (zero saturation), this returns
+ *          the intensity of any component.
+ *      (3) For fully saturated pixels ('fullsat'), this rises linearly
+ *          with the max value and has a slope equal to 255 divided
+ *          by the reference value; for a max value greater than
+ *          the reference value, it is clipped to 255.
+ *      (4) For saturation values in between, the output is a linear
+ *          combination of (2) and (3), weighted by saturation.
+ *          It falls between these two curves, and does not exceed 255.
+ *      (5) This can be useful for distinguishing an object that has nonzero
+ *          saturation from a gray background.  For this, the refval
+ *          should be chosen near the expected value of the background,
+ *          to achieve maximum saturation boost there.
+ * 
+ */ +PIX * +pixConvertRGBToGraySatBoost(PIX *pixs, + l_int32 refval) +{ +l_int32 w, h, d, i, j, wplt, wpld; +l_int32 rval, gval, bval, sval, minrg, maxrg, min, max, delta; +l_int32 fullsat, newval; +l_float32 *invmax, *ratio; +l_uint32 *linet, *lined, *datat, *datad; +PIX *pixt, *pixd; + + PROCNAME("pixConvertRGBToGraySatBoost"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not cmapped or rgb", procName, NULL); + if (refval < 1 || refval > 255) + return (PIX *)ERROR_PTR("refval not in [1 ... 255]", procName, NULL); + + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + pixd = pixCreate(w, h, 8); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + wplt = pixGetWpl(pixt); + datat = pixGetData(pixt); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + invmax = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); + ratio = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); + for (i = 1; i < 256; i++) { /* i == 0 --> delta = sval = newval = 0 */ + invmax[i] = 1.0 / (l_float32)i; + ratio[i] = (l_float32)i / (l_float32)refval; + } + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(linet[j], &rval, &gval, &bval); + minrg = L_MIN(rval, gval); + min = L_MIN(minrg, bval); + maxrg = L_MAX(rval, gval); + max = L_MAX(maxrg, bval); + delta = max - min; + if (delta == 0) /* gray; no chroma */ + sval = 0; + else + sval = (l_int32)(255. * (l_float32)delta * invmax[max] + 0.5); + + fullsat = L_MIN(255, 255 * ratio[max]); + newval = (sval * fullsat + (255 - sval) * max) / 255; + SET_DATA_BYTE(lined, j, newval); + } + } + + pixDestroy(&pixt); + LEPT_FREE(invmax); + LEPT_FREE(ratio); + return pixd; +} + + +/*! + * \brief pixConvertRGBToGrayArb() + * + * \param[in] pixs 32 bpp RGB + * \param[in] rc, gc, bc arithmetic factors; can be negative + * \return 8 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This converts to gray using an arbitrary linear combination
+ *          of the rgb color components.  It differs from pixConvertToGray(),
+ *          which uses only positive coefficients that sum to 1.
+ *      (2) The gray output values are clipped to 0 and 255.
+ * 
+ */ +PIX * +pixConvertRGBToGrayArb(PIX *pixs, + l_float32 rc, + l_float32 gc, + l_float32 bc) +{ +l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixd; + + PROCNAME("pixConvertRGBToGrayArb"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (rc <= 0 && gc <= 0 && bc <= 0) + return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + val = (l_int32)(rc * rval + gc * gval + bc * bval); + val = L_MIN(255, L_MAX(0, val)); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixConvertRGBToBinaryArb() + * + * \param[in] pixs 32 bpp RGB + * \param[in] rc, gc, bc arithmetic factors; can be negative + * \param[in] thresh binarization threshold + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return 1 bpp pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This makes a 1 bpp mask from an RGB image, using an arbitrary
+ *          linear combination of the rgb color components, along with
+ *          a threshold and a selection choice of the gray value relative
+ *          to %thresh.
+ * 
+ */ +PIX * +pixConvertRGBToBinaryArb(PIX *pixs, + l_float32 rc, + l_float32 gc, + l_float32 bc, + l_int32 thresh, + l_int32 relation) +{ +l_int32 threshold; +PIX *pix1, *pix2; + + PROCNAME("pixConvertRGBToBinaryArb"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (rc <= 0 && gc <= 0 && bc <= 0) + return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (PIX *)ERROR_PTR("invalid relation", procName, NULL); + + pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc); + threshold = (relation == L_SELECT_IF_LTE || relation == L_SELECT_IF_GT) ? + thresh : thresh + 1; + pix2 = pixThresholdToBinary(pix1, threshold); + if (relation == L_SELECT_IF_GT || relation == L_SELECT_IF_GTE) + pixInvert(pix2, pix2); + pixDestroy(&pix1); + return pix2; +} + + +/*---------------------------------------------------------------------------* + * Conversion from grayscale to colormap * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertGrayToColormap() + * + * \param[in] pixs 2, 4 or 8 bpp grayscale + * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a simple interface for adding a colormap to a
+ *          2, 4 or 8 bpp grayscale image without causing any
+ *          quantization.  There is some similarity to operations
+ *          in grayquant.c, such as pixThresholdOn8bpp(), where
+ *          the emphasis is on quantization with an arbitrary number
+ *          of levels, and a colormap is an option.
+ *      (2) Returns a copy if pixs already has a colormap.
+ *      (3) For 8 bpp src, this is a lossless transformation.
+ *      (4) For 2 and 4 bpp src, this generates a colormap that
+ *          assumes full coverage of the gray space, with equally spaced
+ *          levels: 4 levels for d = 2 and 16 levels for d = 4.
+ *      (5) In all cases, the depth of the dest is the same as the src.
+ * 
+ */ +PIX * +pixConvertGrayToColormap(PIX *pixs) +{ +l_int32 d; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertGrayToColormap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", procName, NULL); + + if (pixGetColormap(pixs)) { + L_INFO("pixs already has a colormap\n", procName); + return pixCopy(NULL, pixs); + } + + if (d == 8) /* lossless conversion */ + return pixConvertGrayToColormap8(pixs, 2); + + /* Build a cmap with equally spaced target values over the + * full 8 bpp range. */ + pixd = pixCopy(NULL, pixs); + cmap = pixcmapCreateLinear(d, 1 << d); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixConvertGrayToColormap8() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] mindepth of pixd; valid values are 2, 4 and 8 + * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns a copy if pixs already has a colormap.
+ *      (2) This is a lossless transformation; there is no quantization.
+ *          We compute the number of different gray values in pixs,
+ *          and construct a colormap that has exactly these values.
+ *      (3) 'mindepth' is the minimum depth of pixd.  If mindepth == 8,
+ *          pixd will always be 8 bpp.  Let the number of different
+ *          gray values in pixs be ngray.  If mindepth == 4, we attempt
+ *          to save pixd as a 4 bpp image, but if ngray > 16,
+ *          pixd must be 8 bpp.  Likewise, if mindepth == 2,
+ *          the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4
+ *          but <= 16.
+ * 
+ */ +PIX * +pixConvertGrayToColormap8(PIX *pixs, + l_int32 mindepth) +{ +l_int32 ncolors, w, h, depth, i, j, wpls, wpld; +l_int32 index, num, val, newval; +l_int32 array[256]; +l_uint32 *lines, *lined, *datas, *datad; +NUMA *na; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertGrayToColormap8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (mindepth != 2 && mindepth != 4 && mindepth != 8) { + L_WARNING("invalid value of mindepth; setting to 8\n", procName); + mindepth = 8; + } + + if (pixGetColormap(pixs)) { + L_INFO("pixs already has a colormap\n", procName); + return pixCopy(NULL, pixs); + } + + na = pixGetGrayHistogram(pixs, 1); + numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors); + if (mindepth == 8 || ncolors > 16) + depth = 8; + else if (mindepth == 4 || ncolors > 4) + depth = 4; + else + depth = 2; + + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, depth); + cmap = pixcmapCreate(depth); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + + index = 0; + for (i = 0; i < 256; i++) { + array[i] = 0; /* only to quiet the static checker */ + numaGetIValue(na, i, &num); + if (num > 0) { + pixcmapAddColor(cmap, i, i, i); + array[i] = index; + index++; + } + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + newval = array[val]; + if (depth == 2) + SET_DATA_DIBIT(lined, j, newval); + else if (depth == 4) + SET_DATA_QBIT(lined, j, newval); + else /* depth == 8 */ + SET_DATA_BYTE(lined, j, newval); + } + } + + numaDestroy(&na); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Colorizing conversion from grayscale to color * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixColorizeGray() + * + * \param[in] pixs 8 bpp gray; 2, 4 or 8 bpp colormapped + * \param[in] color 32 bit rgba pixel + * \param[in] cmapflag 1 for result to have colormap; 0 for RGB + * \return pixd 8 bpp colormapped or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) This applies the specific color to the grayscale image.
+ *      (2) If pixs already has a colormap, it is removed to gray
+ *          before colorizing.
+ * 
+ */ +PIX * +pixColorizeGray(PIX *pixs, + l_uint32 color, + l_int32 cmapflag) +{ +l_int32 i, j, w, h, wplt, wpld, val8; +l_uint32 *datad, *datat, *lined, *linet, *tab; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixColorizeGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL); + + if (pixGetColormap(pixs)) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixt = pixClone(pixs); + + cmap = pixcmapGrayToColor(color); + if (cmapflag) { + pixd = pixCopy(NULL, pixt); + pixSetColormap(pixd, cmap); + pixDestroy(&pixt); + return pixd; + } + + /* Make an RGB pix */ + pixcmapToRGBTable(cmap, &tab, NULL); + pixGetDimensions(pixt, &w, &h, NULL); + pixd = pixCreate(w, h, 32); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + linet = datat + i * wplt; + for (j = 0; j < w; j++) { + val8 = GET_DATA_BYTE(linet, j); + lined[j] = tab[val8]; + } + } + + pixDestroy(&pixt); + pixcmapDestroy(&cmap); + LEPT_FREE(tab); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion from RGB color to colormap * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertRGBToColormap() + * + * \param[in] pixs 32 bpp rgb + * \param[in] ditherflag 1 to dither, 0 otherwise + * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) This function has two relatively simple modes of color
+ *          quantization:
+ *            (a) If the image is made orthographically and has not more
+ *                than 256 'colors' at the level 4 octcube leaves,
+ *                it is quantized nearly exactly.  The ditherflag
+ *                is ignored.
+ *            (b) Most natural images have more than 256 different colors;
+ *                in that case we use adaptive octree quantization,
+ *                with dithering if requested.
+ *      (2) If there are not more than 256 occupied level 4 octcubes,
+ *          the color in the colormap that represents all pixels in
+ *          one of those octcubes is given by the first pixel that
+ *          falls into that octcube.
+ *      (3) If there are more than 256 colors, we use adaptive octree
+ *          color quantization.
+ *      (4) Dithering gives better visual results on images where
+ *          there is a color wash (a slow variation of color), but it
+ *          is about twice as slow and results in significantly larger
+ *          files when losslessly compressed (e.g., into png).
+ * 
+ */ +PIX * +pixConvertRGBToColormap(PIX *pixs, + l_int32 ditherflag) +{ +l_int32 ncolors; +NUMA *na; +PIX *pixd; + + PROCNAME("pixConvertRGBToColormap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (pixGetSpp(pixs) == 4) + L_WARNING("pixs has alpha; removing\n", procName); + + /* Get the histogram and count the number of occupied level 4 + * leaf octcubes. We don't yet know if this is the number of + * actual colors, but if it's not, all pixels falling into + * the same leaf octcube will be assigned to the color of the + * first pixel that lands there. */ + na = pixOctcubeHistogram(pixs, 4, &ncolors); + + /* If there are too many occupied leaf octcubes to be + * represented directly in a colormap, fall back to octree + * quantization, optionally with dithering. */ + if (ncolors > 256) { + numaDestroy(&na); + if (ditherflag) + L_INFO("More than 256 colors; using octree quant with dithering\n", + procName); + else + L_INFO("More than 256 colors; using octree quant; no dithering\n", + procName); + return pixOctreeColorQuant(pixs, 240, ditherflag); + } + + /* There are not more than 256 occupied leaf octcubes. + * Quantize to those octcubes. */ + pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL); + pixCopyInputFormat(pixd, pixs); + numaDestroy(&na); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion from colormap to 1 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertCmapTo1() + * + * \param[in] pixs cmapped + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is an extreme color quantizer.  It decides which
+ *          colors map to FG (black) and which to BG (white).
+ *      (2) This uses two heuristics to make the decision:
+ *          (a) colors similar to each other are likely to be in the same class
+ *          (b) there is usually much less FG than BG.
+ * 
+ */ +PIX * +pixConvertCmapTo1(PIX *pixs) +{ +l_int32 i, j, nc, w, h, imin, imax, factor, wpl1, wpld; +l_int32 index, rmin, gmin, bmin, rmax, gmax, bmax, dmin, dmax; +l_float32 minfract, ifract; +l_int32 *lut; +l_uint32 *line1, *lined, *data1, *datad; +NUMA *na1, *na2; /* histograms */ +PIX *pix1, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertCmapTo1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if ((cmap = pixGetColormap(pixs)) == NULL) + return (PIX *)ERROR_PTR("no colormap", procName, NULL); + + /* Select target colors for the two classes. Find the + * colors with smallest and largest average component values. + * The smallest is class 0 and the largest is class 1. */ + pixcmapGetRangeValues(cmap, L_SELECT_AVERAGE, NULL, NULL, &imin, &imax); + pixcmapGetColor(cmap, imin, &rmin, &gmin, &bmin); + pixcmapGetColor(cmap, imax, &rmax, &gmax, &bmax); + nc = pixcmapGetCount(cmap); + + /* Assign colors to the two classes. The histogram is + * initialized to 0, so any colors not found when computing + * the sampled histogram will get zero weight in minfract. */ + if ((lut = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL) + return (PIX *)ERROR_PTR("calloc fail for lut", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5)); + na1 = pixGetCmapHistogram(pixs, factor); + na2 = numaNormalizeHistogram(na1, 1.0); + minfract = 0.0; + for (i = 0; i < nc; i++) { + numaGetFValue(na2, i, &ifract); + pixcmapGetDistanceToColor(cmap, i, rmin, gmin, bmin, &dmin); + pixcmapGetDistanceToColor(cmap, i, rmax, gmax, bmax, &dmax); + if (dmin < dmax) { /* closer to dark extreme value */ + lut[i] = 1; /* black pixel in 1 bpp image */ + minfract += ifract; + } + } + numaDestroy(&na1); + numaDestroy(&na2); + + /* Generate the output binarized image */ + pix1 = pixConvertTo8(pixs, 1); + pixd = pixCreate(w, h, 1); + data1 = pixGetData(pix1); + datad = pixGetData(pixd); + wpl1 = pixGetWpl(pix1); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl1; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(line1, j); + if (lut[index] == 1) SET_DATA_BIT(lined, j); + } + } + pixDestroy(&pix1); + LEPT_FREE(lut); + + /* We expect minfract (the dark colors) to be less than 0.5. + * If that is not the case, invert pixd. */ + if (minfract > 0.5) { + L_INFO("minfract = %5.3f; inverting\n", procName, minfract); + pixInvert(pixd, pixd); + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Quantization for relatively small number of colors in source * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixQuantizeIfFewColors() + * + * \param[in] pixs 8 bpp gray or 32 bpp rgb + * \param[in] maxcolors max number of colors allowed to be returned + * from pixColorsForQuantization(); + * use 0 for default + * \param[in] mingraycolors min number of gray levels that a grayscale + * image is quantized to; use 0 for default + * \param[in] octlevel for octcube quantization: 3 or 4 + * \param[out] ppixd 2,4 or 8 bpp quantized; null if too many colors + * \return 0 if OK, 1 on error or if pixs can't be quantized into + * a small number of colors. + * + *
+ * Notes:
+ *      (1) This is a wrapper that tests if the pix can be quantized
+ *          with good quality using a small number of colors.  If so,
+ *          it does the quantization, defining a colormap and using
+ *          pixels whose value is an index into the colormap.
+ *      (2) If the image has color, it is quantized with 8 bpp pixels.
+ *          If the image is essentially grayscale, the pixels are
+ *          either 4 or 8 bpp, depending on the size of the required
+ *          colormap.
+ *      (3) %octlevel = 4 generates a larger colormap and larger
+ *          compressed image than %octlevel = 3.  If image quality is
+ *          important, you should use %octlevel = 4.
+ *      (4) If the image already has a colormap, it returns a clone.
+ * 
+ */ +l_ok +pixQuantizeIfFewColors(PIX *pixs, + l_int32 maxcolors, + l_int32 mingraycolors, + l_int32 octlevel, + PIX **ppixd) +{ +l_int32 d, ncolors, iscolor, graycolors; +PIX *pixg, *pixd; + + PROCNAME("pixQuantizeIfFewColors"); + + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetColormap(pixs) != NULL) { + *ppixd = pixClone(pixs); + return 0; + } + if (maxcolors <= 0) + maxcolors = 15; /* default */ + if (maxcolors > 50) + L_WARNING("maxcolors > 50; very large!\n", procName); + if (mingraycolors <= 0) + mingraycolors = 10; /* default */ + if (mingraycolors > 30) + L_WARNING("mingraycolors > 30; very large!\n", procName); + if (octlevel != 3 && octlevel != 4) { + L_WARNING("invalid octlevel; setting to 3\n", procName); + octlevel = 3; + } + + /* Test the number of colors. For color, the octcube leaves + * are at level 4. */ + pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0); + if (ncolors > maxcolors) + return ERROR_INT("too many colors", procName, 1); + + /* Quantize! + * (1) For color: + * If octlevel == 4, try to quantize to an octree where + * the octcube leaves are at level 4. If that fails, + * back off to level 3. + * If octlevel == 3, quantize to level 3 directly. + * For level 3, the quality is usually good enough and there + * is negligible chance of getting more than 256 colors. + * (2) For grayscale, multiply ncolors by 1.5 for extra quality, + * but use at least mingraycolors and not more than 256. */ + if (iscolor) { + pixd = pixFewColorsOctcubeQuant1(pixs, octlevel); + if (!pixd) { /* backoff */ + pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1); + if (octlevel == 3) /* shouldn't happen */ + L_WARNING("quantized at level 2; low quality\n", procName); + } + } else { /* image is really grayscale */ + if (d == 32) + pixg = pixConvertRGBToLuminance(pixs); + else + pixg = pixClone(pixs); + graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors)); + graycolors = L_MIN(graycolors, 256); + if (graycolors < 16) + pixd = pixThresholdTo4bpp(pixg, graycolors, 1); + else + pixd = pixThresholdOn8bpp(pixg, graycolors, 1); + pixDestroy(&pixg); + } + *ppixd = pixd; + + if (!pixd) + return ERROR_INT("pixd not made", procName, 1); + pixCopyInputFormat(pixd, pixs); + return 0; +} + + + +/*---------------------------------------------------------------------------* + * Conversion from 16 bpp to 8 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert16To8() + * + * \param[in] pixs 16 bpp + * \param[in] type L_LS_BYTE, L_MS_BYTE, L_AUTO_BYTE, L_CLIP_TO_FF + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) With L_AUTO_BYTE, if the max pixel value is greater than 255,
+ *          use the MSB; otherwise, use the LSB.
+ *      (2) With L_CLIP_TO_FF, use min(pixel-value, 0xff) for each
+ *          16-bit src pixel.
+ * 
+ */ +PIX * +pixConvert16To8(PIX *pixs, + l_int32 type) +{ +l_uint16 dword; +l_int32 w, h, wpls, wpld, i, j, val, use_lsb; +l_uint32 sword, first, second; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixConvert16To8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 16) + return (PIX *)ERROR_PTR("pixs not 16 bpp", procName, NULL); + if (type != L_LS_BYTE && type != L_MS_BYTE && + type != L_AUTO_BYTE && type != L_CLIP_TO_FF) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + if (type == L_AUTO_BYTE) { + use_lsb = TRUE; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + val = GET_DATA_TWO_BYTES(lines, j); + if (val > 255) { + use_lsb = FALSE; + break; + } + } + if (!use_lsb) break; + } + type = (use_lsb) ? L_LS_BYTE : L_MS_BYTE; + } + + /* Convert 2 pixels at a time */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (type == L_LS_BYTE) { + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + dword = ((sword >> 8) & 0xff00) | (sword & 0xff); + SET_DATA_TWO_BYTES(lined, j, dword); + } + } else if (type == L_MS_BYTE) { + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + dword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff); + SET_DATA_TWO_BYTES(lined, j, dword); + } + } else { /* type == L_CLIP_TO_FF */ + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + first = (sword >> 24) ? 255 : ((sword >> 16) & 0xff); + second = ((sword >> 8) & 0xff) ? 255 : (sword & 0xff); + dword = (first << 8) | second; + SET_DATA_TWO_BYTES(lined, j, dword); + } + } + } + + return pixd; +} + + + +/*---------------------------------------------------------------------------* + * Conversion from grayscale to false color + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertGrayToFalseColor() + * + * \param[in] pixs 8 or 16 bpp grayscale + * \param[in] gamma (factor) 0.0 or 1.0 for default; > 1.0 for brighter; + * 2.0 is quite nice + * \return pixd 8 bpp with colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) For 8 bpp input, this simply adds a colormap to the input image.
+ *      (2) For 16 bpp input, it first converts to 8 bpp, using the MSB,
+ *          and then adds the colormap.
+ *      (3) The colormap is modeled after the Matlab "jet" configuration.
+ * 
+ */ +PIX * +pixConvertGrayToFalseColor(PIX *pixs, + l_float32 gamma) +{ +l_int32 d, i, rval, bval, gval; +l_int32 *curve; +l_float32 invgamma, x; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertGrayToFalseColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 16) + return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); + + if (d == 16) { + pixd = pixConvert16To8(pixs, L_MS_BYTE); + } else { /* d == 8 */ + if (pixGetColormap(pixs)) + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixd = pixCopy(NULL, pixs); + } + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(8); + pixSetColormap(pixd, cmap); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Generate curve for transition part of color map */ + curve = (l_int32 *)LEPT_CALLOC(64, sizeof(l_int32)); + if (gamma == 0.0) gamma = 1.0; + invgamma = 1. / gamma; + for (i = 0; i < 64; i++) { + x = (l_float32)i / 64.; + curve[i] = (l_int32)(255. * powf(x, invgamma) + 0.5); + } + + for (i = 0; i < 256; i++) { + if (i < 32) { + rval = 0; + gval = 0; + bval = curve[i + 32]; + } else if (i < 96) { /* 32 - 95 */ + rval = 0; + gval = curve[i - 32]; + bval = 255; + } else if (i < 160) { /* 96 - 159 */ + rval = curve[i - 96]; + gval = 255; + bval = curve[159 - i]; + } else if (i < 224) { /* 160 - 223 */ + rval = 255; + gval = curve[223 - i]; + bval = 0; + } else { /* 224 - 255 */ + rval = curve[287 - i]; + gval = 0; + bval = 0; + } + pixcmapAddColor(cmap, rval, gval, bval); + } + + LEPT_FREE(curve); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixUnpackBinary() + * + * \param[in] pixs 1 bpp + * \param[in] depth of destination: 2, 4, 8, 16 or 32 bpp + * \param[in] invert 0: binary 0 --> grayscale 0 + * binary 1 --> grayscale 0xff... + * 1: binary 0 --> grayscale 0xff... + * binary 1 --> grayscale 0 + * \return pixd 2, 4, 8, 16 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This function calls special cases of pixConvert1To*(),
+ *          for 2, 4, 8, 16 and 32 bpp destinations.
+ * 
+ */ +PIX * +pixUnpackBinary(PIX *pixs, + l_int32 depth, + l_int32 invert) +{ +PIX *pixd; + + PROCNAME("pixUnpackBinary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32) + return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp", + procName, NULL); + + if (depth == 2) { + if (invert == 0) + pixd = pixConvert1To2(NULL, pixs, 0, 3); + else /* invert bits */ + pixd = pixConvert1To2(NULL, pixs, 3, 0); + } else if (depth == 4) { + if (invert == 0) + pixd = pixConvert1To4(NULL, pixs, 0, 15); + else /* invert bits */ + pixd = pixConvert1To4(NULL, pixs, 15, 0); + } else if (depth == 8) { + if (invert == 0) + pixd = pixConvert1To8(NULL, pixs, 0, 255); + else /* invert bits */ + pixd = pixConvert1To8(NULL, pixs, 255, 0); + } else if (depth == 16) { + if (invert == 0) + pixd = pixConvert1To16(NULL, pixs, 0, 0xffff); + else /* invert bits */ + pixd = pixConvert1To16(NULL, pixs, 0xffff, 0); + } else { + if (invert == 0) + pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff); + else /* invert bits */ + pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0); + } + + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixConvert1To16() + * + * \param[in] pixd [optional] 16 bpp, can be null + * \param[in] pixs 1 bpp + * \param[in] val0 16 bit value to be used for 0s in pixs + * \param[in] val1 16 bit value to be used for 1s in pixs + * \return pixd 16 bpp + * + *
+ * Notes:
+ *      (1) If pixd is null, a new pix is made.
+ *      (2) If pixd is not null, it must be of equal width and height
+ *          as pixs.  It is always returned.
+ * 
+ */ +PIX * +pixConvert1To16(PIX *pixd, + PIX *pixs, + l_uint16 val0, + l_uint16 val1) +{ +l_int32 w, h, i, j, dibit, ndibits, wpls, wpld; +l_uint16 val[2]; +l_uint32 index; +l_uint32 *tab, *datas, *datad, *lines, *lined; + + PROCNAME("pixConvert1To16"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); + if (pixGetDepth(pixd) != 16) + return (PIX *)ERROR_PTR("pixd not 16 bpp", procName, pixd); + } else { + if ((pixd = pixCreate(w, h, 16)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Use a table to convert 2 src bits at a time */ + tab = (l_uint32 *)LEPT_CALLOC(4, sizeof(l_uint32)); + val[0] = val0; + val[1] = val1; + for (index = 0; index < 4; index++) { + tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1]; + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + ndibits = (w + 1) / 2; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < ndibits; j++) { + dibit = GET_DATA_DIBIT(lines, j); + lined[j] = tab[dibit]; + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*! + * \brief pixConvert1To32() + * + * \param[in] pixd [optional] 32 bpp, can be null + * \param[in] pixs 1 bpp + * \param[in] val0 32 bit value to be used for 0s in pixs + * \param[in] val1 32 bit value to be used for 1s in pixs + * \return pixd 32 bpp + * + *
+ * Notes:
+ *      (1) If pixd is null, a new pix is made.
+ *      (2) If pixd is not null, it must be of equal width and height
+ *          as pixs.  It is always returned.
+ * 
+ */ +PIX * +pixConvert1To32(PIX *pixd, + PIX *pixs, + l_uint32 val0, + l_uint32 val1) +{ +l_int32 w, h, i, j, wpls, wpld, bit; +l_uint32 val[2]; +l_uint32 *datas, *datad, *lines, *lined; + + PROCNAME("pixConvert1To32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); + if (pixGetDepth(pixd) != 32) + return (PIX *)ERROR_PTR("pixd not 32 bpp", procName, pixd); + } else { + if ((pixd = pixCreate(w, h, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + val[0] = val0; + val[1] = val1; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j + * Notes: + * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) + *
+ */ +PIX * +pixConvert1To2Cmap(PIX *pixs) +{ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvert1To2Cmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(2); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + + return pixd; +} + + +/*! + * \brief pixConvert1To2() + * + * \param[in] pixd [optional] 2 bpp, can be null + * \param[in] pixs 1 bpp + * \param[in] val0 2 bit value to be used for 0s in pixs + * \param[in] val1 2 bit value to be used for 1s in pixs + * \return pixd 2 bpp + * + *
+ * Notes:
+ *      (1) If pixd is null, a new pix is made.
+ *      (2) If pixd is not null, it must be of equal width and height
+ *          as pixs.  It is always returned.
+ *      (3) A simple unpacking might use val0 = 0 and val1 = 3.
+ *      (4) If you want a colormapped pixd, use pixConvert1To2Cmap().
+ * 
+ */ +PIX * +pixConvert1To2(PIX *pixd, + PIX *pixs, + l_int32 val0, + l_int32 val1) +{ +l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; +l_uint8 val[2]; +l_uint32 index; +l_uint16 *tab; +l_uint32 *datas, *datad, *lines, *lined; + + PROCNAME("pixConvert1To2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); + if (pixGetDepth(pixd) != 2) + return (PIX *)ERROR_PTR("pixd not 2 bpp", procName, pixd); + } else { + if ((pixd = pixCreate(w, h, 2)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Use a table to convert 8 src bits to 16 dest bits */ + tab = (l_uint16 *)LEPT_CALLOC(256, sizeof(l_uint16)); + val[0] = val0; + val[1] = val1; + for (index = 0; index < 256; index++) { + tab[index] = (val[(index >> 7) & 1] << 14) | + (val[(index >> 6) & 1] << 12) | + (val[(index >> 5) & 1] << 10) | + (val[(index >> 4) & 1] << 8) | + (val[(index >> 3) & 1] << 6) | + (val[(index >> 2) & 1] << 4) | + (val[(index >> 1) & 1] << 2) | val[index & 1]; + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + nbytes = (w + 7) / 8; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < nbytes; j++) { + byteval = GET_DATA_BYTE(lines, j); + SET_DATA_TWO_BYTES(lined, j, tab[byteval]); + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion from 1 bpp to 4 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert1To4Cmap() + * + * \param[in] pixs 1 bpp + * \return pixd 4 bpp, cmapped + * + *
+ * Notes:
+ *      (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
+ * 
+ */ +PIX * +pixConvert1To4Cmap(PIX *pixs) +{ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvert1To4Cmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(4); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + + return pixd; +} + + +/*! + * \brief pixConvert1To4() + * + * \param[in] pixd [optional] 4 bpp, can be null + * \param[in] pixs 1 bpp + * \param[in] val0 4 bit value to be used for 0s in pixs + * \param[in] val1 4 bit value to be used for 1s in pixs + * \return pixd 4 bpp + * + *
+ * Notes:
+ *      (1) If pixd is null, a new pix is made.
+ *      (2) If pixd is not null, it must be of equal width and height
+ *          as pixs.  It is always returned.
+ *      (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v.
+ *      (4) If you want a colormapped pixd, use pixConvert1To4Cmap().
+ * 
+ */ +PIX * +pixConvert1To4(PIX *pixd, + PIX *pixs, + l_int32 val0, + l_int32 val1) +{ +l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; +l_uint8 val[2]; +l_uint32 index; +l_uint32 *tab, *datas, *datad, *lines, *lined; + + PROCNAME("pixConvert1To4"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); + if (pixGetDepth(pixd) != 4) + return (PIX *)ERROR_PTR("pixd not 4 bpp", procName, pixd); + } else { + if ((pixd = pixCreate(w, h, 4)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Use a table to convert 8 src bits to 32 bit dest word */ + tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + val[0] = val0; + val[1] = val1; + for (index = 0; index < 256; index++) { + tab[index] = (val[(index >> 7) & 1] << 28) | + (val[(index >> 6) & 1] << 24) | + (val[(index >> 5) & 1] << 20) | + (val[(index >> 4) & 1] << 16) | + (val[(index >> 3) & 1] << 12) | + (val[(index >> 2) & 1] << 8) | + (val[(index >> 1) & 1] << 4) | val[index & 1]; + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + nbytes = (w + 7) / 8; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < nbytes; j++) { + byteval = GET_DATA_BYTE(lines, j); + lined[j] = tab[byteval]; + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion from 1, 2 and 4 bpp to 8 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert1To8Cmap() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, cmapped + * + *
+ * Notes:
+ *      (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
+ * 
+ */ +PIX * +pixConvert1To8Cmap(PIX *pixs) +{ +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvert1To8Cmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + if ((pixd = pixConvert1To8(NULL, pixs, 0, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + cmap = pixcmapCreate(8); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixSetColormap(pixd, cmap); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixConvert1To8() + * + * \param[in] pixd [optional] 8 bpp, can be null + * \param[in] pixs 1 bpp + * \param[in] val0 8 bit value to be used for 0s in pixs + * \param[in] val1 8 bit value to be used for 1s in pixs + * \return pixd 8 bpp + * + *
+ * Notes:
+ *      (1) If pixd is null, a new pix is made.
+ *      (2) If pixd is not null, it must be of equal width and height
+ *          as pixs.  It is always returned.
+ *      (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v.
+ *      (4) To have a colormap associated with the 8 bpp pixd,
+ *          use pixConvert1To8Cmap().
+ * 
+ */ +PIX * +pixConvert1To8(PIX *pixd, + PIX *pixs, + l_uint8 val0, + l_uint8 val1) +{ +l_int32 w, h, i, j, qbit, nqbits, wpls, wpld; +l_uint8 val[2]; +l_uint32 index; +l_uint32 *tab, *datas, *datad, *lines, *lined; + + PROCNAME("pixConvert1To8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); + + pixGetDimensions(pixs, &w, &h, NULL); + if (pixd) { + if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) + return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); + if (pixGetDepth(pixd) != 8) + return (PIX *)ERROR_PTR("pixd not 8 bpp", procName, pixd); + } else { + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixSetPadBits(pixs, 0); + + /* Use a table to convert 4 src bits at a time */ + tab = (l_uint32 *)LEPT_CALLOC(16, sizeof(l_uint32)); + val[0] = val0; + val[1] = val1; + for (index = 0; index < 16; index++) { + tab[index] = ((l_uint32)val[(index >> 3) & 1] << 24) | + (val[(index >> 2) & 1] << 16) | + (val[(index >> 1) & 1] << 8) | val[index & 1]; + } + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + nqbits = (w + 3) / 4; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < nqbits; j++) { + qbit = GET_DATA_QBIT(lines, j); + lined[j] = tab[qbit]; + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*! + * \brief pixConvert2To8() + * + * \param[in] pixs 2 bpp + * \param[in] val0 8 bit value to be used for 00 in pixs + * \param[in] val1 8 bit value to be used for 01 in pixs + * \param[in] val2 8 bit value to be used for 10 in pixs + * \param[in] val3 8 bit value to be used for 11 in pixs + * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      ~ A simple unpacking might use val0 = 0,
+ *        val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255.
+ *      ~ If cmapflag is TRUE:
+ *          ~ The 8 bpp image is made with a colormap.
+ *          ~ If pixs has a colormap, the input values are ignored and
+ *            the 8 bpp image is made using the colormap
+ *          ~ If pixs does not have a colormap, the input values are
+ *            used to build the colormap.
+ *      ~ If cmapflag is FALSE:
+ *          ~ The 8 bpp image is made without a colormap.
+ *          ~ If pixs has a colormap, the input values are ignored,
+ *            the colormap is removed, and the values stored in the 8 bpp
+ *            image are from the colormap.
+ *          ~ If pixs does not have a colormap, the input values are
+ *            used to populate the 8 bpp image.
+ * 
+ */ +PIX * +pixConvert2To8(PIX *pixs, + l_uint8 val0, + l_uint8 val1, + l_uint8 val2, + l_uint8 val3, + l_int32 cmapflag) +{ +l_int32 w, h, i, j, nbytes, wpls, wpld, dibit, byte; +l_uint8 val[4]; +l_uint32 index; +l_uint32 *tab, *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmaps, *cmapd; + + PROCNAME("pixConvert2To8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 2) + return (PIX *)ERROR_PTR("pixs not 2 bpp", procName, NULL); + + cmaps = pixGetColormap(pixs); + if (cmaps && cmapflag == FALSE) + return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSetPadBits(pixs, 0); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + if (cmapflag == TRUE) { /* pixd will have a colormap */ + if (cmaps) { /* use the existing colormap from pixs */ + cmapd = pixcmapConvertTo8(cmaps); + } else { /* make a colormap from the input values */ + cmapd = pixcmapCreate(8); + pixcmapAddColor(cmapd, val0, val0, val0); + pixcmapAddColor(cmapd, val1, val1, val1); + pixcmapAddColor(cmapd, val2, val2, val2); + pixcmapAddColor(cmapd, val3, val3, val3); + } + pixSetColormap(pixd, cmapd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + dibit = GET_DATA_DIBIT(lines, j); + SET_DATA_BYTE(lined, j, dibit); + } + } + return pixd; + } + + /* Last case: no colormap in either pixs or pixd. + * Use input values and build a table to convert 1 src byte + * (4 src pixels) at a time */ + tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + val[0] = val0; + val[1] = val1; + val[2] = val2; + val[3] = val3; + for (index = 0; index < 256; index++) { + tab[index] = (val[(index >> 6) & 3] << 24) | + (val[(index >> 4) & 3] << 16) | + (val[(index >> 2) & 3] << 8) | val[index & 3]; + } + + nbytes = (w + 3) / 4; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < nbytes; j++) { + byte = GET_DATA_BYTE(lines, j); + lined[j] = tab[byte]; + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*! + * \brief pixConvert4To8() + * + * \param[in] pixs 4 bpp + * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      ~ If cmapflag is TRUE:
+ *          ~ pixd is made with a colormap.
+ *          ~ If pixs has a colormap, it is copied and the colormap
+ *            index values are placed in pixd.
+ *          ~ If pixs does not have a colormap, a colormap with linear
+ *            trc is built and the pixel values in pixs are placed in
+ *            pixd as colormap index values.
+ *      ~ If cmapflag is FALSE:
+ *          ~ pixd is made without a colormap.
+ *          ~ If pixs has a colormap, it is removed and the values stored
+ *            in pixd are from the colormap (converted to gray).
+ *          ~ If pixs does not have a colormap, the pixel values in pixs
+ *            are used, with shift replication, to populate pixd.
+ * 
+ */ +PIX * +pixConvert4To8(PIX *pixs, + l_int32 cmapflag) +{ +l_int32 w, h, i, j, wpls, wpld, byte, qbit; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmaps, *cmapd; + + PROCNAME("pixConvert4To8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 4) + return (PIX *)ERROR_PTR("pixs not 4 bpp", procName, NULL); + + cmaps = pixGetColormap(pixs); + if (cmaps && cmapflag == FALSE) + return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + if (cmapflag == TRUE) { /* pixd will have a colormap */ + if (cmaps) { /* use the existing colormap from pixs */ + cmapd = pixcmapConvertTo8(cmaps); + } else { /* make a colormap with a linear trc */ + cmapd = pixcmapCreate(8); + for (i = 0; i < 16; i++) + pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i); + } + pixSetColormap(pixd, cmapd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + qbit = GET_DATA_QBIT(lines, j); + SET_DATA_BYTE(lined, j, qbit); + } + } + return pixd; + } + + /* Last case: no colormap in either pixs or pixd. + * Replicate the qbit value into 8 bits. */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + qbit = GET_DATA_QBIT(lines, j); + byte = (qbit << 4) | qbit; + SET_DATA_BYTE(lined, j, byte); + } + } + return pixd; +} + + + +/*---------------------------------------------------------------------------* + * Unpacking conversion from 8 bpp to 16 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert8To16() + * + * \param[in] pixs 8 bpp; colormap removed to gray + * \param[in] leftshift number of bits: 0 is no shift; + * 8 replicates in MSB and LSB of dest + * \return pixd 16 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) For left shift of 8, the 8 bit value is replicated in both
+ *          the MSB and the LSB of the pixels in pixd.  That way, we get
+ *          proportional mapping, with a correct map from 8 bpp white
+ *          (0xff) to 16 bpp white (0xffff).
+ * 
+ */ +PIX * +pixConvert8To16(PIX *pixs, + l_int32 leftshift) +{ +l_int32 i, j, w, h, d, wplt, wpld, val; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixConvert8To16"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (leftshift < 0 || leftshift > 8) + return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", procName, NULL); + + if (pixGetColormap(pixs) != NULL) + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + else + pixt = pixClone(pixs); + + pixd = pixCreate(w, h, 16); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datat = pixGetData(pixt); + datad = pixGetData(pixd); + wplt = pixGetWpl(pixt); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(linet, j); + if (leftshift == 8) + val = val | (val << leftshift); + else + val <<= leftshift; + SET_DATA_TWO_BYTES(lined, j, val); + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 2 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo2() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed + * \return pixd 2 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, with simple default values
+ *          used in pixConvertTo8() if unpacking is necessary.
+ *      (2) Any existing colormap is removed; the result is always gray.
+ *      (3) If the input image has 2 bpp and no colormap, the operation is
+ *          lossless and a copy is returned.
+ * 
+ */ +PIX * +pixConvertTo2(PIX *pixs) +{ +l_int32 d; +PIX *pix1, *pix2, *pix3, *pixd; + + PROCNAME("pixConvertTo2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL); + + if (pixGetColormap(pixs) != NULL) { + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + d = pixGetDepth(pix1); + } else { + pix1 = pixCopy(NULL, pixs); + } + if (d == 32) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixDestroy(&pix1); + if (d == 1) { + pixd = pixConvert1To2(NULL, pix2, 3, 0); + } else if (d == 2) { + pixd = pixClone(pix2); + } else if (d == 4) { + pix3 = pixConvert4To8(pix2, FALSE); /* unpack to 8 */ + pixd = pixConvert8To2(pix3); + pixDestroy(&pix3); + } else { /* d == 8 */ + pixd = pixConvert8To2(pix2); + } + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief pixConvert8To2() + * + * \param[in] pix 8 bpp; colormap OK + * \return pixd 2 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Any existing colormap is removed to gray.
+ * 
+ */ +PIX * +pixConvert8To2(PIX *pix) +{ +l_int32 i, j, w, h, wpls, wpld; +l_uint32 word; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixs, *pixd; + + PROCNAME("pixConvert8To2"); + + if (!pix || pixGetDepth(pix) != 8) + return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); + + if (pixGetColormap(pix) != NULL) + pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); + else + pixs = pixClone(pix); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreate(w, h, 2); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wpls; j++) { /* march through 4 pixels at a time */ + word = lines[j] & 0xc0c0c0c0; /* top 2 bits of each byte */ + word = (word >> 24) | ((word & 0xff0000) >> 18) | + ((word & 0xff00) >> 12) | ((word & 0xff) >> 6); + SET_DATA_BYTE(lined, j, word); /* only LS byte is filled */ + } + } + pixDestroy(&pixs); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 4 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo4() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed + * \return pixd 4 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, with simple default values
+ *          used in pixConvertTo8() if unpacking is necessary.
+ *      (2) Any existing colormap is removed; the result is always gray.
+ *      (3) If the input image has 4 bpp and no colormap, the operation is
+ *          lossless and a copy is returned.
+ * 
+ */ +PIX * +pixConvertTo4(PIX *pixs) +{ +l_int32 d; +PIX *pix1, *pix2, *pix3, *pixd; + + PROCNAME("pixConvertTo4"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL); + + if (pixGetColormap(pixs) != NULL) { + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + d = pixGetDepth(pix1); + } else { + pix1 = pixCopy(NULL, pixs); + } + if (d == 32) + pix2 = pixConvertTo8(pix1, FALSE); + else + pix2 = pixClone(pix1); + pixDestroy(&pix1); + if (d == 1) { + pixd = pixConvert1To4(NULL, pix2, 15, 0); + } else if (d == 2) { + pix3 = pixConvert2To8(pix2, 0, 0x55, 0xaa, 0xff, FALSE); + pixd = pixConvert8To4(pix3); + pixDestroy(&pix3); + } else if (d == 4) { + pixd = pixClone(pix2); + } else { /* d == 8 */ + pixd = pixConvert8To4(pix2); + } + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief pixConvert8To4() + * + * \param[in] pix 8 bpp; colormap OK + * \return pixd 4 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Any existing colormap is removed to gray.
+ * 
+ */ +PIX * +pixConvert8To4(PIX *pix) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixs, *pixd; + + PROCNAME("pixConvert8To4"); + + if (!pix || pixGetDepth(pix) != 8) + return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); + + if (pixGetColormap(pix) != NULL) + pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); + else + pixs = pixClone(pix); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreate(w, h, 4); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + val = val >> 4; /* take top 4 bits */ + SET_DATA_QBIT(lined, j, val); + } + } + pixDestroy(&pixs); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 1 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo1Adaptive() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, that uses default values for
+ *          adaptive thresholding, if necessary.  Otherwise, it is the same as
+ *          pixConvertTo1(), which uses a global threshold for binarization.
+ * 
+ */ +PIX * +pixConvertTo1Adaptive(PIX *pixs) +{ +l_int32 d, color0, color1, rval, gval, bval; +PIX *pix1, *pix2, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertTo1Adaptive"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); + + cmap = pixGetColormap(pixs); + if (d == 1) { + if (!cmap) { + return pixCopy(NULL, pixs); + } else { /* strip the colormap off, and invert if reasonable + for standard binary photometry. */ + pixcmapGetColor(cmap, 0, &rval, &gval, &bval); + color0 = rval + gval + bval; + pixcmapGetColor(cmap, 1, &rval, &gval, &bval); + color1 = rval + gval + bval; + pixd = pixCopy(NULL, pixs); + pixDestroyColormap(pixd); + if (color1 > color0) + pixInvert(pixd, pixd); + return pixd; + } + } + + /* For all other depths, use 8 bpp as an intermediary */ + pix1 = pixConvertTo8(pixs, FALSE); + pix2 = pixBackgroundNormSimple(pix1, NULL, NULL); + pixd = pixThresholdToBinary(pix2, 180); + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief pixConvertTo1() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] threshold for final binarization, relative to 8 bpp + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, with simple default values
+ *          used in pixConvertTo8() if unpacking is necessary.
+ *      (2) Any existing colormap is removed.
+ *      (3) If the input image has 1 bpp and no colormap, the operation is
+ *          lossless and a copy is returned.
+ * 
+ */ +PIX * +pixConvertTo1(PIX *pixs, + l_int32 threshold) +{ +l_int32 d, color0, color1, rval, gval, bval; +PIX *pixg, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertTo1"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); + + cmap = pixGetColormap(pixs); + if (d == 1) { + if (!cmap) { + return pixCopy(NULL, pixs); + } else { /* strip the colormap off, and invert if reasonable + for standard binary photometry. */ + pixcmapGetColor(cmap, 0, &rval, &gval, &bval); + color0 = rval + gval + bval; + pixcmapGetColor(cmap, 1, &rval, &gval, &bval); + color1 = rval + gval + bval; + pixd = pixCopy(NULL, pixs); + pixDestroyColormap(pixd); + if (color1 > color0) + pixInvert(pixd, pixd); + return pixd; + } + } + + /* For all other depths, use 8 bpp as an intermediary */ + pixg = pixConvertTo8(pixs, FALSE); + pixd = pixThresholdToBinary(pixg, threshold); + pixDestroy(&pixg); + return pixd; +} + + +/*! + * \brief pixConvertTo1BySampling() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] factor submsampling factor; integer >= 1 + * \param[in] threshold for final binarization, relative to 8 bpp + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a quick and dirty, top-level converter.
+ *      (2) See pixConvertTo1() for default values.
+ * 
+ */ +PIX * +pixConvertTo1BySampling(PIX *pixs, + l_int32 factor, + l_int32 threshold) +{ +l_float32 scalefactor; +PIX *pixt, *pixd; + + PROCNAME("pixConvertTo1BySampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + + scalefactor = 1. / (l_float32)factor; + pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); + pixd = pixConvertTo1(pixt, threshold); + + pixDestroy(&pixt); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 8 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo8() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, with simple default values
+ *          for unpacking.
+ *      (2) The result, pixd, is made with a colormap if specified.
+ *          It is always a new image -- never a clone.  For example,
+ *          if d == 8, and cmapflag matches the existence of a cmap
+ *          in pixs, the operation is lossless and it returns a copy.
+ *      (3) The default values used are:
+ *          ~ 1 bpp: val0 = 255, val1 = 0
+ *          ~ 2 bpp: 4 bpp:  even increments over dynamic range
+ *          ~ 8 bpp: lossless if cmap matches cmapflag
+ *          ~ 16 bpp: use most significant byte
+ *      (4) If 32 bpp RGB, this is converted to gray.  If you want
+ *          to do color quantization, you must specify the type
+ *          explicitly, using the color quantization code.
+ * 
+ */ +PIX * +pixConvertTo8(PIX *pixs, + l_int32 cmapflag) +{ +l_int32 d; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertTo8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); + + if (d == 1) { + if (cmapflag) + return pixConvert1To8Cmap(pixs); + else + return pixConvert1To8(NULL, pixs, 255, 0); + } else if (d == 2) { + return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag); + } else if (d == 4) { + return pixConvert4To8(pixs, cmapflag); + } else if (d == 8) { + cmap = pixGetColormap(pixs); + if ((cmap && cmapflag) || (!cmap && !cmapflag)) { + return pixCopy(NULL, pixs); + } else if (cmap) { /* !cmapflag */ + return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + } else { /* !cmap && cmapflag; add colormap to pixd */ + pixd = pixCopy(NULL, pixs); + pixAddGrayColormap8(pixd); + return pixd; + } + } else if (d == 16) { + pixd = pixConvert16To8(pixs, L_MS_BYTE); + if (cmapflag) + pixAddGrayColormap8(pixd); + return pixd; + } else { /* d == 32 */ + pixd = pixConvertRGBToLuminance(pixs); + if (cmapflag) + pixAddGrayColormap8(pixd); + return pixd; + } +} + + +/*! + * \brief pixConvertTo8BySampling() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] factor submsampling factor; integer >= 1 + * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a fast, quick/dirty, top-level converter.
+ *      (2) See pixConvertTo8() for default values.
+ * 
+ */ +PIX * +pixConvertTo8BySampling(PIX *pixs, + l_int32 factor, + l_int32 cmapflag) +{ +l_float32 scalefactor; +PIX *pixt, *pixd; + + PROCNAME("pixConvertTo8BySampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + + scalefactor = 1. / (l_float32)factor; + pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); + pixd = pixConvertTo8(pixt, cmapflag); + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixConvertTo8Colormap() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] dither 1 to dither if necessary; 0 otherwise + * \return pixd 8 bpp, cmapped, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a top-level function, with simple default values
+ *          for unpacking.
+ *      (2) The result, pixd, is always made with a colormap.
+ *      (3) If d == 8, the operation is lossless and it returns a copy.
+ *      (4) The default values used for increasing depth are:
+ *          ~ 1 bpp: val0 = 255, val1 = 0
+ *          ~ 2 bpp: 4 bpp:  even increments over dynamic range
+ *      (5) For 16 bpp, use the most significant byte.
+ *      (6) For 32 bpp RGB, use octcube quantization with optional dithering.
+ * 
+ */ +PIX * +pixConvertTo8Colormap(PIX *pixs, + l_int32 dither) +{ +l_int32 d; + + PROCNAME("pixConvertTo8Colormap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); + + if (d != 32) + return pixConvertTo8(pixs, 1); + + return pixConvertRGBToColormap(pixs, dither); +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 16 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo16() + * + * \param[in] pixs 1, 8 bpp + * \return pixd 16 bpp, or NULL on error + * + * Usage: Top-level function, with simple default values for unpacking. + * 1 bpp: val0 = 0xffff, val1 = 0 + * 8 bpp: replicates the 8 bit value in both the MSB and LSB + * of the 16 bit pixel. + */ +PIX * +pixConvertTo16(PIX *pixs) +{ +l_int32 d; + + PROCNAME("pixConvertTo16"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + d = pixGetDepth(pixs); + if (d == 1) + return pixConvert1To16(NULL, pixs, 0xffff, 0); + else if (d == 8) + return pixConvert8To16(pixs, 8); + else + return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", procName, NULL); +} + + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 32 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo32() + * + * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp + * \return pixd 32 bpp, or NULL on error + * + * Usage: Top-level function, with simple default values for unpacking. + * 1 bpp: val0 = 255, val1 = 0 + * and then replication into R, G and B components + * 2 bpp: if colormapped, use the colormap values; otherwise, + * use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255 + * and replicate gray into R, G and B components + * 4 bpp: if colormapped, use the colormap values; otherwise, + * replicate 2 nybs into a byte, and then into R,G,B components + * 8 bpp: if colormapped, use the colormap values; otherwise, + * replicate gray values into R, G and B components + * 16 bpp: replicate MSB into R, G and B components + * 24 bpp: unpack the pixels, maintaining word alignment on each scanline + * 32 bpp: makes a copy + * + *
+ * Notes:
+ *      (1) Never returns a clone of pixs.
+ * 
+ */ +PIX * +pixConvertTo32(PIX *pixs) +{ +l_int32 d; +PIX *pix1, *pixd; + + PROCNAME("pixConvertTo32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + d = pixGetDepth(pixs); + if (d == 1) { + return pixConvert1To32(NULL, pixs, 0xffffffff, 0); + } else if (d == 2) { + pix1 = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE); + pixd = pixConvert8To32(pix1); + pixDestroy(&pix1); + return pixd; + } else if (d == 4) { + pix1 = pixConvert4To8(pixs, TRUE); + pixd = pixConvert8To32(pix1); + pixDestroy(&pix1); + return pixd; + } else if (d == 8) { + return pixConvert8To32(pixs); + } else if (d == 16) { + pix1 = pixConvert16To8(pixs, L_MS_BYTE); + pixd = pixConvert8To32(pix1); + pixDestroy(&pix1); + return pixd; + } else if (d == 24) { + return pixConvert24To32(pixs); + } else if (d == 32) { + return pixCopy(NULL, pixs); + } else { + return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp", + procName, NULL); + } +} + + +/*! + * \brief pixConvertTo32BySampling() + * + * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp + * \param[in] factor submsampling factor; integer >= 1 + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a fast, quick/dirty, top-level converter.
+ *      (2) See pixConvertTo32() for default values.
+ * 
+ */ +PIX * +pixConvertTo32BySampling(PIX *pixs, + l_int32 factor) +{ +l_float32 scalefactor; +PIX *pix1, *pixd; + + PROCNAME("pixConvertTo32BySampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + + scalefactor = 1. / (l_float32)factor; + pix1 = pixScaleBySampling(pixs, scalefactor, scalefactor); + pixd = pixConvertTo32(pix1); + + pixDestroy(&pix1); + return pixd; +} + + +/*! + * \brief pixConvert8To32() + * + * \param[in] pixs 8 bpp + * \return 32 bpp rgb pix, or NULL on error + * + *
+ * Notes:
+ *      (1) If there is no colormap, replicates the gray value
+ *          into the 3 MSB of the dest pixel.
+ * 
+ */ +PIX * +pixConvert8To32(PIX *pixs) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +l_uint32 *tab; +PIX *pixd; + + PROCNAME("pixConvert8To32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + if (pixGetColormap(pixs)) + return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(w, h, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Replication table gray --> rgb */ + tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); + for (i = 0; i < 256; i++) + tab[i] = (i << 24) | (i << 16) | (i << 8); + + /* Replicate 1 --> 4 bytes (alpha byte not set) */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines, j); + lined[j] = tab[val]; + } + } + + LEPT_FREE(tab); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Top-level conversion to 8 or 32 bpp, without colormap * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertTo8Or32() + * + * \param[in] pixs 1, 2, 4, 8, 16, with or without colormap; + * or 32 bpp rgb + * \param[in] copyflag L_CLONE or L_COPY + * \param[in] warnflag 1 to issue warning if colormap is removed; else 0 + * \return pixd 8 bpp grayscale or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, the colormap is removed to 8 or 32 bpp,
+ *          depending on whether the colors in the colormap are all gray.
+ *      (2) If the input is either rgb or 8 bpp without a colormap,
+ *          this returns either a clone or a copy, depending on %copyflag.
+ *      (3) Otherwise, the pix is converted to 8 bpp grayscale.
+ *          In all cases, pixd does not have a colormap.
+ * 
+ */ +PIX * +pixConvertTo8Or32(PIX *pixs, + l_int32 copyflag, + l_int32 warnflag) +{ +l_int32 d; +PIX *pixd; + + PROCNAME("pixConvertTo8Or32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (copyflag != L_CLONE && copyflag != L_COPY) + return (PIX *)ERROR_PTR("invalid copyflag", procName, NULL); + + d = pixGetDepth(pixs); + if (pixGetColormap(pixs)) { + if (warnflag) L_WARNING("pix has colormap; removing\n", procName); + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + } else if (d == 8 || d == 32) { + if (copyflag == L_CLONE) + pixd = pixClone(pixs); + else /* copyflag == L_COPY */ + pixd = pixCopy(NULL, pixs); + } else { + pixd = pixConvertTo8(pixs, 0); + } + + /* Sanity check on result */ + d = pixGetDepth(pixd); + if (d != 8 && d != 32) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion between 24 bpp and 32 bpp rgb * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert24To32() + * + * \param[in] pixs 24 bpp rgb + * \return pixd 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) 24 bpp rgb pix are not supported in leptonica, except for a small
+ *          number of formatted write operations.  The data is a byte array,
+ *          with pixels in order r,g,b, and padded to 32 bit boundaries
+ *          in each line.
+ *      (2) Because 24 bpp rgb pix are conveniently generated by programs
+ *          such as xpdf (which has SplashBitmaps that store the raster
+ *          data in consecutive 24-bit rgb pixels), it is useful to provide
+ *          24 bpp pix that simply incorporate that data.  The only things
+ *          we can do with these are:
+ *            (a) write them to file in png, jpeg, tiff and pnm
+ *            (b) interconvert between 24 and 32 bpp in memory (for testing).
+ * 
+ */ +PIX * +pixConvert24To32(PIX *pixs) +{ +l_uint8 *lines; +l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; +l_uint32 pixel; +l_uint32 *datas, *datad, *lined; +PIX *pixd; + + PROCNAME("pixConvert24to32"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 24) + return (PIX *)ERROR_PTR("pixs not 24 bpp", procName, NULL); + + pixd = pixCreateNoInit(w, h, 32); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = (l_uint8 *)(datas + i * wpls); + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + rval = *lines++; + gval = *lines++; + bval = *lines++; + composeRGBPixel(rval, gval, bval, &pixel); + lined[j] = pixel; + } + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixConvert32To24() + * + * \param[in] pixs 32 bpp rgb + * \return pixd 24 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixconvert24To32().
+ * 
+ */ +PIX * +pixConvert32To24(PIX *pixs) +{ +l_uint8 *rgbdata8; +l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; +l_uint32 *datas, *lines, *rgbdata; +PIX *pixd; + + PROCNAME("pixConvert32to24"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateNoInit(w, h, 24); + rgbdata = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + rgbdata8 = (l_uint8 *)(rgbdata + i * wpld); + for (j = 0; j < w; j++) { + extractRGBValues(lines[j], &rval, &gval, &bval); + *rgbdata8++ = rval; + *rgbdata8++ = gval; + *rgbdata8++ = bval; + } + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion between 32 bpp (1 spp) and 16 or 8 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvert32To16() + * + * \param[in] pixs 32 bpp, single component + * \param[in] type L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF + * \return pixd 16 bpp , or NULL on error + * + *
+ * Notes:
+ *      (1) The data in pixs is typically used for labelling.
+ *          It is an array of l_uint32 values, not rgb or rgba.
+ * 
+ */ +PIX * +pixConvert32To16(PIX *pixs, + l_int32 type) +{ +l_uint16 dword; +l_int32 w, h, i, j, wpls, wpld; +l_uint32 sword; +l_uint32 *datas, *lines, *datad, *lined; +PIX *pixd; + + PROCNAME("pixConvert32to16"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (type != L_LS_TWO_BYTES && type != L_MS_TWO_BYTES && + type != L_CLIP_TO_FFFF) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 16)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + if (type == L_LS_TWO_BYTES) { + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + dword = sword & 0xffff; + SET_DATA_TWO_BYTES(lined, j, dword); + } + } else if (type == L_MS_TWO_BYTES) { + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + dword = sword >> 16; + SET_DATA_TWO_BYTES(lined, j, dword); + } + } else { /* type == L_CLIP_TO_FFFF */ + for (j = 0; j < wpls; j++) { + sword = *(lines + j); + dword = (sword >> 16) ? 0xffff : (sword & 0xffff); + SET_DATA_TWO_BYTES(lined, j, dword); + } + } + } + + return pixd; +} + + +/*! + * \brief pixConvert32To8() + * + * \param[in] pixs 32 bpp, single component + * \param[in] type16 L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF + * \param[in] type8 L_LS_BYTE, L_MS_BYTE, L_CLIP_TO_FF + * \return pixd 8 bpp, or NULL on error + */ +PIX * +pixConvert32To8(PIX *pixs, + l_int32 type16, + l_int32 type8) +{ +PIX *pix1, *pixd; + + PROCNAME("pixConvert32to8"); + + if (!pixs || pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + if (type16 != L_LS_TWO_BYTES && type16 != L_MS_TWO_BYTES && + type16 != L_CLIP_TO_FFFF) + return (PIX *)ERROR_PTR("invalid type16", procName, NULL); + if (type8 != L_LS_BYTE && type8 != L_MS_BYTE && type8 != L_CLIP_TO_FF) + return (PIX *)ERROR_PTR("invalid type8", procName, NULL); + + pix1 = pixConvert32To16(pixs, type16); + pixd = pixConvert16To8(pix1, type8); + pixDestroy(&pix1); + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Removal of alpha component by blending with white background * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixRemoveAlpha() + * + * \param[in] pixs any depth + * \return pixd if 32 bpp rgba, pixs blended over a white background; + * a clone of pixs otherwise, and NULL on error + * + *
+ * Notes:
+ *      (1) This is a wrapper on pixAlphaBlendUniform()
+ * 
+ */ +PIX * +pixRemoveAlpha(PIX *pixs) +{ + PROCNAME("pixRemoveAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) + return pixAlphaBlendUniform(pixs, 0xffffff00); + else + return pixClone(pixs); +} + + +/*---------------------------------------------------------------------------* + * Addition of alpha component to 1 bpp * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixAddAlphaTo1bpp() + * + * \param[in] pixd [optional] 1 bpp, can be null or equal to pixs + * \param[in] pixs 1 bpp + * \return pixd 1 bpp with colormap and non-opaque alpha, + * or NULL on error + * + *
+ * Notes:
+ *      (1) We don't use 1 bpp colormapped images with alpha in leptonica,
+ *          but we support generating them (here), writing to png, and reading
+ *          the png.  On reading, they are converted to 32 bpp RGBA.
+ *      (2) The background (0) pixels in pixs become fully transparent, and the
+ *          foreground (1) pixels are fully opaque.  Thus, pixd is a 1 bpp
+ *          representation of a stencil, that can be used to paint over pixels
+ *          of a backing image that are masked by the foreground in pixs.
+ * 
+ */ +PIX * +pixAddAlphaTo1bpp(PIX *pixd, + PIX *pixs) +{ +PIXCMAP *cmap; + + PROCNAME("pixAddAlphaTo1bpp"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (pixd && (pixd != pixs)) + return (PIX *)ERROR_PTR("pixd defined but != pixs", procName, NULL); + + pixd = pixCopy(pixd, pixs); + cmap = pixcmapCreate(1); + pixSetColormap(pixd, cmap); + pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* 0 ==> white + transparent */ + pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* 1 ==> black + opaque */ + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Lossless depth conversion (unpacking) * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertLossless() + * + * \param[in] pixs 1, 2, 4, 8 bpp, not cmapped + * \param[in] d destination depth: 2, 4 or 8 + * \return pixd 2, 4 or 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a lossless unpacking (depth-increasing)
+ *          conversion.  If ds is the depth of pixs, then
+ *           ~ if d < ds, returns NULL
+ *           ~ if d == ds, returns a copy
+ *           ~ if d > ds, does the unpacking conversion
+ *      (2) If pixs has a colormap, this is an error.
+ * 
+ */ +PIX * +pixConvertLossless(PIX *pixs, + l_int32 d) +{ +l_int32 w, h, ds, wpls, wpld, i, j, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixConvertLossless"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + if (d != 2 && d != 4 && d != 8) + return (PIX *)ERROR_PTR("invalid dest depth", procName, NULL); + + pixGetDimensions(pixs, &w, &h, &ds); + if (d < ds) + return (PIX *)ERROR_PTR("depth > d", procName, NULL); + else if (d == ds) + return pixCopy(NULL, pixs); + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + + /* Unpack the bits */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + switch (ds) + { + case 1: + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(lines, j); + if (d == 8) + SET_DATA_BYTE(lined, j, val); + else if (d == 4) + SET_DATA_QBIT(lined, j, val); + else /* d == 2 */ + SET_DATA_DIBIT(lined, j, val); + } + break; + case 2: + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(lines, j); + if (d == 8) + SET_DATA_BYTE(lined, j, val); + else /* d == 4 */ + SET_DATA_QBIT(lined, j, val); + } + break; + case 4: + for (j = 0; j < w; j++) { + val = GET_DATA_DIBIT(lines, j); + SET_DATA_BYTE(lined, j, val); + } + break; + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Conversion for printing in PostScript * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertForPSWrap() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp + * \return pixd 1, 8, or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) For wrapping in PostScript, we convert pixs to
+ *          1 bpp, 8 bpp (gray) and 32 bpp (RGB color).
+ *      (2) Colormaps are removed.  For pixs with colormaps, the
+ *          images are converted to either 8 bpp gray or 32 bpp
+ *          RGB, depending on whether the colormap has color content.
+ *      (3) Images without colormaps, that are not 1 bpp or 32 bpp,
+ *          are converted to 8 bpp gray.
+ * 
+ */ +PIX * +pixConvertForPSWrap(PIX *pixs) +{ +l_int32 d; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertForPSWrap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + switch (d) + { + case 1: + case 32: + pixd = pixClone(pixs); + break; + case 2: + if (cmap) + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE); + break; + case 4: + if (cmap) + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pixd = pixConvert4To8(pixs, FALSE); + break; + case 8: + pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + break; + case 16: + pixd = pixConvert16To8(pixs, L_MS_BYTE); + break; + default: + fprintf(stderr, "depth not in {1, 2, 4, 8, 16, 32}"); + return NULL; + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Scaling conversion to subpixel RGB * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixConvertToSubpixelRGB() + * + * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped + * \param[in] scalex, scaley anisotropic scaling permitted between + * source and destination + * \param[in] order of subpixel rgb color components in + * composition of pixd: + * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, + * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, it is removed based on its contents
+ *          to either 8 bpp gray or rgb.
+ *      (2) For horizontal subpixel splitting, the input image
+ *          is rescaled by %scaley vertically and by 3.0 times
+ *          %scalex horizontally.  Then each horizontal triplet
+ *          of pixels is mapped back to a single rgb pixel, with the
+ *          r, g and b values being assigned based on the pixel triplet.
+ *          For gray triplets, the r, g, and b values are set equal to
+ *          the three gray values.  For color triplets, the r, g and b
+ *          values are set equal to the components from the appropriate
+ *          subpixel.  Vertical subpixel splitting is handled similarly.
+ *      (3) See pixConvertGrayToSubpixelRGB() and
+ *          pixConvertColorToSubpixelRGB() for further details.
+ * 
+ */ +PIX * +pixConvertToSubpixelRGB(PIX *pixs, + l_float32 scalex, + l_float32 scaley, + l_int32 order) +{ +l_int32 d; +PIX *pix1, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertToSubpixelRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d != 8 && d != 32 && !cmap) + return (PIX *)ERROR_PTR("pix not 8 or 32 bpp and not cmapped", + procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); + if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && + order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) + return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); + if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC)) == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + + d = pixGetDepth(pix1); + pixd = NULL; + if (d == 8) + pixd = pixConvertGrayToSubpixelRGB(pix1, scalex, scaley, order); + else if (d == 32) + pixd = pixConvertColorToSubpixelRGB(pix1, scalex, scaley, order); + else + L_ERROR("invalid depth %d\n", procName, d); + + pixDestroy(&pix1); + return pixd; +} + + +/*! + * \brief pixConvertGrayToSubpixelRGB() + * + * \param[in] pixs 8 bpp or colormapped + * \param[in] scalex, scaley + * \param[in] order of subpixel rgb color components in + * composition of pixd: + * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, + * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, it is removed to 8 bpp.
+ *      (2) For horizontal subpixel splitting, the input gray image
+ *          is rescaled by %scaley vertically and by 3.0 times
+ *          %scalex horizontally.  Then each horizontal triplet
+ *          of pixels is mapped back to a single rgb pixel, with the
+ *          r, g and b values being assigned from the triplet of gray values.
+ *          Similar operations are used for vertical subpixel splitting.
+ *      (3) This is a form of subpixel rendering that tends to give the
+ *          resulting text a sharper and somewhat chromatic display.
+ *          For horizontal subpixel splitting, the observable difference
+ *          between %order=L_SUBPIXEL_ORDER_RGB and
+ *          %order=L_SUBPIXEL_ORDER_BGR is reduced by optical diffusers
+ *          in the display that make the pixel color appear to emerge
+ *          from the entire pixel.
+ * 
+ */ +PIX * +pixConvertGrayToSubpixelRGB(PIX *pixs, + l_float32 scalex, + l_float32 scaley, + l_int32 order) +{ +l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pix1, *pix2, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertGrayToSubpixelRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d != 8 && !cmap) + return (PIX *)ERROR_PTR("pix not 8 bpp & not cmapped", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); + if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && + order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) + return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); + + direction = + (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) + ? L_HORIZ : L_VERT; + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); + if (direction == L_HORIZ) + pix2 = pixScale(pix1, 3.0 * scalex, scaley); + else /* L_VERT */ + pix2 = pixScale(pix1, scalex, 3.0 * scaley); + + pixGetDimensions(pix2, &w, &h, NULL); + wd = (direction == L_HORIZ) ? w / 3 : w; + hd = (direction == L_VERT) ? h / 3 : h; + pixd = pixCreate(wd, hd, 32); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datat = pixGetData(pix2); + wplt = pixGetWpl(pix2); + if (direction == L_HORIZ) { + for (i = 0; i < hd; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + rval = GET_DATA_BYTE(linet, 3 * j); + gval = GET_DATA_BYTE(linet, 3 * j + 1); + bval = GET_DATA_BYTE(linet, 3 * j + 2); + if (order == L_SUBPIXEL_ORDER_RGB) + composeRGBPixel(rval, gval, bval, &lined[j]); + else /* order BGR */ + composeRGBPixel(bval, gval, rval, &lined[j]); + } + } + } else { /* L_VERT */ + for (i = 0; i < hd; i++) { + linet = datat + 3 * i * wplt; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + rval = GET_DATA_BYTE(linet, j); + gval = GET_DATA_BYTE(linet + wplt, j); + bval = GET_DATA_BYTE(linet + 2 * wplt, j); + if (order == L_SUBPIXEL_ORDER_VRGB) + composeRGBPixel(rval, gval, bval, &lined[j]); + else /* order VBGR */ + composeRGBPixel(bval, gval, rval, &lined[j]); + } + } + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief pixConvertColorToSubpixelRGB() + * + * \param[in] pixs 32 bpp or colormapped + * \param[in] scalex, scaley + * \param[in] order of subpixel rgb color components in + * composition of pixd: + * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, + * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) If pixs has a colormap, it is removed to 32 bpp rgb.
+ *          If the colormap has no color, pixConvertGrayToSubpixelRGB()
+ *          should be called instead, because it will give the same result
+ *          more efficiently.  The function pixConvertToSubpixelRGB()
+ *          will do the best thing for all cases.
+ *      (2) For horizontal subpixel splitting, the input rgb image
+ *          is rescaled by %scaley vertically and by 3.0 times
+ *          %scalex horizontally.  Then for each horizontal triplet
+ *          of pixels, the r component of the final pixel is selected
+ *          from the r component of the appropriate pixel in the triplet,
+ *          and likewise for g and b.  Vertical subpixel splitting is
+ *          handled similarly.
+ * 
+ */ +PIX * +pixConvertColorToSubpixelRGB(PIX *pixs, + l_float32 scalex, + l_float32 scaley, + l_int32 order) +{ +l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; +l_uint32 *datat, *datad, *linet, *lined; +PIX *pix1, *pix2, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixConvertColorToSubpixelRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d != 32 && !cmap) + return (PIX *)ERROR_PTR("pix not 32 bpp & not cmapped", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); + if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && + order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) + return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); + + direction = + (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) + ? L_HORIZ : L_VERT; + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); + if (direction == L_HORIZ) + pix2 = pixScale(pix1, 3.0 * scalex, scaley); + else /* L_VERT */ + pix2 = pixScale(pix1, scalex, 3.0 * scaley); + + pixGetDimensions(pix2, &w, &h, NULL); + wd = (direction == L_HORIZ) ? w / 3 : w; + hd = (direction == L_VERT) ? h / 3 : h; + pixd = pixCreate(wd, hd, 32); + pixCopyInputFormat(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + datat = pixGetData(pix2); + wplt = pixGetWpl(pix2); + if (direction == L_HORIZ) { + for (i = 0; i < hd; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + if (order == L_SUBPIXEL_ORDER_RGB) { + extractRGBValues(linet[3 * j], &rval, NULL, NULL); + extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); + extractRGBValues(linet[3 * j + 2], NULL, NULL, &bval); + } else { /* order BGR */ + extractRGBValues(linet[3 * j], NULL, NULL, &bval); + extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); + extractRGBValues(linet[3 * j + 2], &rval, NULL, NULL); + } + composeRGBPixel(rval, gval, bval, &lined[j]); + } + } + } else { /* L_VERT */ + for (i = 0; i < hd; i++) { + linet = datat + 3 * i * wplt; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + if (order == L_SUBPIXEL_ORDER_VRGB) { + extractRGBValues(linet[j], &rval, NULL, NULL); + extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); + extractRGBValues((linet + 2 * wplt)[j], NULL, NULL, &bval); + } else { /* order VBGR */ + extractRGBValues(linet[j], NULL, NULL, &bval); + extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); + extractRGBValues((linet + 2 * wplt)[j], &rval, NULL, NULL); + } + composeRGBPixel(rval, gval, bval, &lined[j]); + } + } + } + + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); + + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + +/*---------------------------------------------------------------------* + * Setting neutral point for min/max boost conversion to gray * + *---------------------------------------------------------------------*/ +/*! + * \brief l_setNeutralBoostVal() + * + * \param[in] val between 1 and 255; typical value is 180 + * \return void + * + *
+ * Notes:
+ *      (1) This raises or lowers the selected min or max RGB component value,
+ *          depending on if that component is above or below this value.
+ * 
+ */ +void +l_setNeutralBoostVal(l_int32 val) +{ + PROCNAME("l_setNeutralBoostVal"); + + if (val <= 0) { + L_ERROR("invalid reference value for neutral boost\n", procName); + return; + } + var_NEUTRAL_BOOST_VAL = val; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixlabel.c b/hgdriver/3rdparty/hgOCR/leptonica/pixlabel.c new file mode 100644 index 0000000..504ec7d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixlabel.c @@ -0,0 +1,633 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pixlabel.c + *
+ *
+ *     Label pixels by an index for connected component membership
+ *           PIX         *pixConnCompTransform()
+ *
+ *     Label pixels by the area of their connected component
+ *           PIX         *pixConnCompAreaTransform()
+ *
+ *     Label pixels to allow incremental computation of connected components
+ *           l_int32      pixConnCompIncrInit()
+ *           l_int32      pixConnCompIncrAdd()
+ *           l_int32      pixGetSortedNeighborValues()
+ *
+ *     Label pixels with spatially-dependent color coding
+ *           PIX         *pixLocToColorTransform()
+ *
+ *  Pixels get labelled in various ways throughout the leptonica library,
+ *  but most of the labelling is implicit, where the new value isn't
+ *  even considered to be a label -- it is just a transformed pixel value
+ *  that may be transformed again by another operation.  Quantization
+ *  by thresholding, and dilation by a structuring element, are examples
+ *  of these typical image processing operations.
+ *
+ *  However, there are some explicit labelling procedures that are useful
+ *  as end-points of analysis, where it typically would not make sense
+ *  to do further image processing on the result.  Assigning false color
+ *  based on pixel properties is an example of such labelling operations.
+ *  Such operations typically have 1 bpp input images, and result
+ *  in grayscale or color images.
+ *
+ *  The procedures in this file are concerned with such explicit labelling.
+ *  Some of these labelling procedures are also in other places in leptonica:
+ *
+ *    runlength.c:
+ *       This file has two labelling transforms based on runlengths:
+ *       pixStrokeWidthTransform() and pixvRunlengthTransform().
+ *       The pixels are labelled based on the width of the "stroke" to
+ *       which they belong, or on the length of the horizontal or
+ *       vertical run in which they are a member.  Runlengths can easily
+ *       be filtered using a threshold.
+ *
+ *    pixafunc2.c:
+ *       This file has an operation, pixaDisplayRandomCmap(), that
+ *       randomly labels pix in a pixa (that are typically found using
+ *       pixConnComp) with up to 256 values, and assigns each value to
+ *       a random colormap color.
+ *
+ *    seedfill.c:
+ *       This file has pixDistanceFunction(), that labels each pixel with
+ *       its distance from either the foreground or the background.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +/*-----------------------------------------------------------------------* + * Label pixels by an index for connected component membership * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixConnCompTransform() + * + * \param[in] pixs 1 bpp + * \param[in] connect connectivity: 4 or 8 + * \param[in] depth of pixd: 8 or 16 bpp; use 0 for auto determination + * \return pixd 8, 16 or 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) pixd is 8, 16 or 32 bpp, and the pixel values label the
+ *          fg component, starting with 1.  Pixels in the bg are labelled 0.
+ *      (2) If %depth = 0, the depth of pixd is 8 if the number of c.c.
+ *          is less than 254, 16 if the number of c.c is less than 0xfffe,
+ *          and 32 otherwise.
+ *      (3) If %depth = 8, the assigned label for the n-th component is
+ *          1 + n % 254.  We use mod 254 because 0 is uniquely assigned
+ *          to black: e.g., see pixcmapCreateRandom().  Likewise,
+ *          if %depth = 16, the assigned label uses mod(2^16 - 2), and
+ *          if %depth = 32, no mod is taken.
+ * 
+ */ +PIX * +pixConnCompTransform(PIX *pixs, + l_int32 connect, + l_int32 depth) +{ +l_int32 i, n, index, w, h, xb, yb, wb, hb; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; + + PROCNAME("pixConnCompTransform"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connect != 4 && connect != 8) + return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); + if (depth != 0 && depth != 8 && depth != 16 && depth != 32) + return (PIX *)ERROR_PTR("depth must be 0, 8, 16 or 32", procName, NULL); + + boxa = pixConnComp(pixs, &pixa, connect); + n = pixaGetCount(pixa); + boxaDestroy(&boxa); + pixGetDimensions(pixs, &w, &h, NULL); + if (depth == 0) { + if (n < 254) + depth = 8; + else if (n < 0xfffe) + depth = 16; + else + depth = 32; + } + pixd = pixCreate(w, h, depth); + pixSetSpp(pixd, 1); + if (n == 0) { /* no fg */ + pixaDestroy(&pixa); + return pixd; + } + + /* Label each component and blit it in */ + for (i = 0; i < n; i++) { + pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); + pix1 = pixaGetPix(pixa, i, L_CLONE); + if (depth == 8) { + index = 1 + (i % 254); + pix2 = pixConvert1To8(NULL, pix1, 0, index); + } else if (depth == 16) { + index = 1 + (i % 0xfffe); + pix2 = pixConvert1To16(NULL, pix1, 0, index); + } else { /* depth == 32 */ + index = 1 + i; + pix2 = pixConvert1To32(NULL, pix1, 0, index); + } + pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixaDestroy(&pixa); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Label pixels by the area of their connected component * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixConnCompAreaTransform() + * + * \param[in] pixs 1 bpp + * \param[in] connect connectivity: 4 or 8 + * \return pixd 32 bpp, 1 spp, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixel values in pixd label the area of the fg component
+ *          to which the pixel belongs.  Pixels in the bg are labelled 0.
+ *      (2) For purposes of visualization, the output can be converted
+ *          to 8 bpp, using pixConvert32To8() or pixMaxDynamicRange().
+ * 
+ */ +PIX * +pixConnCompAreaTransform(PIX *pixs, + l_int32 connect) +{ +l_int32 i, n, npix, w, h, xb, yb, wb, hb; +l_int32 *tab8; +BOXA *boxa; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; + + PROCNAME("pixConnCompAreaTransform"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connect != 4 && connect != 8) + return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); + + boxa = pixConnComp(pixs, &pixa, connect); + n = pixaGetCount(pixa); + boxaDestroy(&boxa); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixCreate(w, h, 32); + pixSetSpp(pixd, 1); + if (n == 0) { /* no fg */ + pixaDestroy(&pixa); + return pixd; + } + + /* Label each component and blit it in */ + tab8 = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); + pix1 = pixaGetPix(pixa, i, L_CLONE); + pixCountPixels(pix1, &npix, tab8); + pix2 = pixConvert1To32(NULL, pix1, 0, npix); + pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + pixaDestroy(&pixa); + LEPT_FREE(tab8); + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Label pixels to allow incremental computation of connected components * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixConnCompIncrInit() + * + * \param[in] pixs 1 bpp + * \param[in] conn connectivity: 4 or 8 + * \param[out] ppixd 32 bpp, with c.c. labelled + * \param[out] pptaa with pixel locations indexed by c.c. + * \param[out] pncc initial number of c.c. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This labels the connected components in a 1 bpp pix, and
+ *          additionally sets up a ptaa that lists the locations of pixels
+ *          in each of the components.
+ *      (2) It can be used to initialize the output image and arrays for
+ *          an application that maintains information about connected
+ *          components incrementally as pixels are added.
+ *      (3) pixs can be empty or have some foreground pixels.
+ *      (4) The connectivity is stored in pixd->special.
+ *      (5) Always initialize with the first pta in ptaa being empty
+ *          and representing the background value (index 0) in the pix.
+ * 
+ */ +l_ok +pixConnCompIncrInit(PIX *pixs, + l_int32 conn, + PIX **ppixd, + PTAA **pptaa, + l_int32 *pncc) +{ +l_int32 empty, w, h, ncc; +PIX *pixd; +PTA *pta; +PTAA *ptaa; + + PROCNAME("pixConnCompIncrInit"); + + if (ppixd) *ppixd = NULL; + if (pptaa) *pptaa = NULL; + if (pncc) *pncc = 0; + if (!ppixd || !pptaa || !pncc) + return ERROR_INT("&pixd, &ptaa, &ncc not all defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); + if (conn != 4 && conn != 8) + return ERROR_INT("connectivity must be 4 or 8", procName, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + pixZero(pixs, &empty); + if (empty) { + *ppixd = pixCreate(w, h, 32); + pixSetSpp(*ppixd, 1); + pixSetSpecial(*ppixd, conn); + *pptaa = ptaaCreate(0); + pta = ptaCreate(1); + ptaaAddPta(*pptaa, pta, L_INSERT); /* reserve index 0 for background */ + return 0; + } + + /* Set up the initial labeled image and indexed pixel arrays */ + if ((pixd = pixConnCompTransform(pixs, conn, 32)) == NULL) + return ERROR_INT("pixd not made", procName, 1); + pixSetSpecial(pixd, conn); + *ppixd = pixd; + if ((ptaa = ptaaIndexLabeledPixels(pixd, &ncc)) == NULL) + return ERROR_INT("ptaa not made", procName, 1); + *pptaa = ptaa; + *pncc = ncc; + return 0; +} + + +/*! + * \brief pixConnCompIncrAdd() + * + * \param[in] pixs 32 bpp, with pixels labeled by c.c. + * \param[in] ptaa with each pta of pixel locations indexed by c.c. + * \param[out] pncc number of c.c + * \param[in] x,y location of added pixel + * \param[in] debug 0 for no output; otherwise output whenever + * debug <= nvals, up to debug == 3 + * \return -1 if nothing happens; 0 if a pixel is added; 1 on error + * + *
+ * Notes:
+ *      (1) This adds a pixel and updates the labeled connected components.
+ *          Before calling this function, initialize the process using
+ *          pixConnCompIncrInit().
+ *      (2) As a result of adding a pixel, one of the following can happen,
+ *          depending on the number of neighbors with non-zero value:
+ *          (a) nothing: the pixel is already a member of a c.c.
+ *          (b) no neighbors: a new component is added, increasing the
+ *              number of c.c.
+ *          (c) one neighbor: the pixel is added to an existing c.c.
+ *          (d) more than one neighbor: the added pixel causes joining of
+ *              two or more c.c., reducing the number of c.c.  A maximum
+ *              of 4 c.c. can be joined.
+ *      (3) When two c.c. are joined, the pixels in the larger index are
+ *          relabeled to those of the smaller in pixs, and their locations
+ *          are transferred to the pta with the smaller index in the ptaa.
+ *          The pta corresponding to the larger index is then deleted.
+ *      (4) This is an efficient implementation of a "union-find" operation,
+ *          which supports the generation and merging of disjoint sets
+ *          of pixels.  This function can be called about 1.3 million times
+ *          per second.
+ * 
+ */ +l_int32 +pixConnCompIncrAdd(PIX *pixs, + PTAA *ptaa, + l_int32 *pncc, + l_float32 x, + l_float32 y, + l_int32 debug) +{ +l_int32 conn, i, j, w, h, count, nvals, ns, firstindex; +l_uint32 val; +l_int32 *neigh; +PTA *ptas, *ptad; + + PROCNAME("pixConnCompIncrAdd"); + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (!pncc) + return ERROR_INT("&ncc not defined", procName, 1); + conn = pixs->special; + if (conn != 4 && conn != 8) + return ERROR_INT("connectivity must be 4 or 8", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (x < 0 || x >= w) + return ERROR_INT("invalid x pixel location", procName, 1); + if (y < 0 || y >= h) + return ERROR_INT("invalid y pixel location", procName, 1); + + pixGetPixel(pixs, x, y, &val); + if (val > 0) /* already belongs to a set */ + return -1; + + /* Find unique neighbor pixel values in increasing order of value. + * If %nvals > 0, these are returned in the %neigh array, which + * is of size %nvals. Note that the pixel values in each + * connected component are used as the index into the pta + * array of the ptaa, giving the pixel locations. */ + pixGetSortedNeighborValues(pixs, x, y, conn, &neigh, &nvals); + + /* If there are no neighbors, just add a new component */ + if (nvals == 0) { + count = ptaaGetCount(ptaa); + pixSetPixel(pixs, x, y, count); + ptas = ptaCreate(1); + ptaAddPt(ptas, x, y); + ptaaAddPta(ptaa, ptas, L_INSERT); + *pncc += 1; + LEPT_FREE(neigh); + return 0; + } + + /* Otherwise, there is at least one neighbor. Add the pixel + * to the first neighbor c.c. */ + firstindex = neigh[0]; + pixSetPixel(pixs, x, y, firstindex); + ptaaAddPt(ptaa, neigh[0], x, y); + if (nvals == 1) { + if (debug == 1) + fprintf(stderr, "nvals = %d: neigh = (%d)\n", nvals, neigh[0]); + LEPT_FREE(neigh); + return 0; + } + + /* If nvals > 1, there are at least 2 neighbors, so this pixel + * joins at least one pair of existing c.c. Join each component + * to the first component in the list, which is the one with + * the smallest integer label. This is done in two steps: + * (a) re-label the pixels in the component to the label of the + * first component, and + * (b) save the pixel locations in the pta for the first component. */ + if (nvals == 2) { + if (debug >= 1 && debug <= 2) { + fprintf(stderr, "nvals = %d: neigh = (%d,%d)\n", nvals, + neigh[0], neigh[1]); + } + } else if (nvals == 3) { + if (debug >= 1 && debug <= 3) { + fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d)\n", nvals, + neigh[0], neigh[1], neigh[2]); + } + } else { /* nvals == 4 */ + if (debug >= 1 && debug <= 4) { + fprintf(stderr, "nvals = %d: neigh = (%d,%d,%d,%d)\n", nvals, + neigh[0], neigh[1], neigh[2], neigh[3]); + } + } + ptad = ptaaGetPta(ptaa, firstindex, L_CLONE); + for (i = 1; i < nvals; i++) { + ptas = ptaaGetPta(ptaa, neigh[i], L_CLONE); + ns = ptaGetCount(ptas); + for (j = 0; j < ns; j++) { /* relabel pixels */ + ptaGetPt(ptas, j, &x, &y); + pixSetPixel(pixs, x, y, firstindex); + } + ptaJoin(ptad, ptas, 0, -1); /* add relabeled pixel locations */ + *pncc -= 1; + ptaDestroy(&ptaa->pta[neigh[i]]); + ptaDestroy(&ptas); /* the clone */ + } + ptaDestroy(&ptad); /* the clone */ + LEPT_FREE(neigh); + return 0; +} + + +/*! + * \brief pixGetSortedNeighborValues() + * + * \param[in] pixs 8, 16 or 32 bpp, with pixels labeled by c.c. + * \param[in] x, y location of pixel + * \param[in] conn 4 or 8 connected neighbors + * \param[out] pneigh array of integers, to be filled with + * the values of the neighbors, if any + * \param[out] pnvals the number of unique neighbor values found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned %neigh array is the unique set of neighboring
+ *          pixel values, of size nvals, sorted from smallest to largest.
+ *          The value 0, which represents background pixels that do
+ *          not belong to any set of connected components, is discarded.
+ *      (2) If there are no neighbors, this returns %neigh = NULL; otherwise,
+ *          the caller must free the array.
+ *      (3) For either 4 or 8 connectivity, the maximum number of unique
+ *          neighbor values is 4.
+ * 
+ */ +l_ok +pixGetSortedNeighborValues(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 conn, + l_int32 **pneigh, + l_int32 *pnvals) +{ +l_int32 i, npt, index; +l_int32 neigh[4]; +l_uint32 val; +l_float32 fx, fy; +L_ASET *aset; +L_ASET_NODE *node; +PTA *pta; +RB_TYPE key; + + PROCNAME("pixGetSortedNeighborValues"); + + if (pneigh) *pneigh = NULL; + if (pnvals) *pnvals = 0; + if (!pneigh || !pnvals) + return ERROR_INT("&neigh and &nvals not both defined", procName, 1); + if (!pixs || pixGetDepth(pixs) < 8) + return ERROR_INT("pixs not defined or depth < 8", procName, 1); + + /* Identify the locations of nearest neighbor pixels */ + if ((pta = ptaGetNeighborPixLocs(pixs, x, y, conn)) == NULL) + return ERROR_INT("pta of neighbors not made", procName, 1); + + /* Find the pixel values and insert into a set as keys */ + aset = l_asetCreate(L_UINT_TYPE); + npt = ptaGetCount(pta); + for (i = 0; i < npt; i++) { + ptaGetPt(pta, i, &fx, &fy); + pixGetPixel(pixs, (l_int32)fx, (l_int32)fy, &val); + key.utype = val; + l_asetInsert(aset, key); + } + + /* Extract the set keys and put them into the %neigh array. + * Omit the value 0, which indicates the pixel doesn't + * belong to one of the sets of connected components. */ + node = l_asetGetFirst(aset); + index = 0; + while (node) { + val = node->key.utype; + if (val > 0) + neigh[index++] = (l_int32)val; + node = l_asetGetNext(node); + } + *pnvals = index; + if (index > 0) { + *pneigh = (l_int32 *)LEPT_CALLOC(index, sizeof(l_int32)); + for (i = 0; i < index; i++) + (*pneigh)[i] = neigh[i]; + } + + ptaDestroy(&pta); + l_asetDestroy(&aset); + return 0; +} + + +/*-----------------------------------------------------------------------* + * Label pixels with spatially-dependent color coding * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixLocToColorTransform() + * + * \param[in] pixs 1 bpp + * \return pixd 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates an RGB image where each component value
+ *          is coded depending on the (x.y) location and the size
+ *          of the fg connected component that the pixel in pixs belongs to.
+ *          It is independent of the 4-fold orthogonal orientation, and
+ *          only weakly depends on translations and small angle rotations.
+ *          Background pixels are black.
+ *      (2) Such encodings can be compared between two 1 bpp images
+ *          by performing this transform and calculating the
+ *          "earth-mover" distance on the resulting R,G,B histograms.
+ * 
+ */ +PIX * +pixLocToColorTransform(PIX *pixs) +{ +l_int32 w, h, w2, h2, wpls, wplr, wplg, wplb, wplcc, i, j, rval, gval, bval; +l_float32 invw2, invh2; +l_uint32 *datas, *datar, *datag, *datab, *datacc; +l_uint32 *lines, *liner, *lineg, *lineb, *linecc; +PIX *pix1, *pixcc, *pixr, *pixg, *pixb, *pixd; + + PROCNAME("pixLocToColorTransform"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + /* Label each pixel with the area of the c.c. to which it belongs. + * Clip the result to 255 in an 8 bpp pix. This is used for + * the blue component of pixd. */ + pixGetDimensions(pixs, &w, &h, NULL); + w2 = w / 2; + h2 = h / 2; + invw2 = 255.0 / (l_float32)w2; + invh2 = 255.0 / (l_float32)h2; + pix1 = pixConnCompAreaTransform(pixs, 8); + pixcc = pixConvert32To8(pix1, L_LS_TWO_BYTES, L_CLIP_TO_FF); + pixDestroy(&pix1); + + /* Label the red and green components depending on the location + * of the fg pixels, in a way that is 4-fold rotationally invariant. */ + pixr = pixCreate(w, h, 8); + pixg = pixCreate(w, h, 8); + pixb = pixCreate(w, h, 8); + wpls = pixGetWpl(pixs); + wplr = pixGetWpl(pixr); + wplg = pixGetWpl(pixg); + wplb = pixGetWpl(pixb); + wplcc = pixGetWpl(pixcc); + datas = pixGetData(pixs); + datar = pixGetData(pixr); + datag = pixGetData(pixg); + datab = pixGetData(pixb); + datacc = pixGetData(pixcc); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + liner = datar + i * wplr; + lineg = datag + i * wplg; + lineb = datab + i * wplb; + linecc = datacc+ i * wplcc; + for (j = 0; j < w; j++) { + if (GET_DATA_BIT(lines, j) == 0) continue; + if (w < h) { + rval = invh2 * L_ABS((l_float32)(i - h2)); + gval = invw2 * L_ABS((l_float32)(j - w2)); + } else { + rval = invw2 * L_ABS((l_float32)(j - w2)); + gval = invh2 * L_ABS((l_float32)(i - h2)); + } + bval = GET_DATA_BYTE(linecc, j); + SET_DATA_BYTE(liner, j, rval); + SET_DATA_BYTE(lineg, j, gval); + SET_DATA_BYTE(lineb, j, bval); + } + } + pixd = pixCreateRGBImage(pixr, pixg, pixb); + + pixDestroy(&pixcc); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pixtiling.c b/hgdriver/3rdparty/hgOCR/leptonica/pixtiling.c new file mode 100644 index 0000000..bc7bc3d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pixtiling.c @@ -0,0 +1,421 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file pixtiling.c + *
+ *
+ *        PIXTILING       *pixTilingCreate()
+ *        void            *pixTilingDestroy()
+ *        l_int32          pixTilingGetCount()
+ *        l_int32          pixTilingGetSize()
+ *        PIX             *pixTilingGetTile()
+ *        l_int32          pixTilingNoStripOnPaint()
+ *        l_int32          pixTilingPaintTile()
+ *
+ *   This provides a simple way to split an image into tiles
+ *   and to perform operations independently on each tile.
+ *
+ *   The tile created with pixTilingGetTile() can have pixels in
+ *   adjacent tiles for computation.  The number of extra pixels
+ *   on each side of the tile is given by an 'overlap' parameter
+ *   to pixTilingCreate().  For tiles at the boundary of
+ *   the input image, quasi-overlap pixels are created by reflection
+ *   symmetry into the tile.
+ *
+ *   Here's a typical intended usage.  Suppose you want to parallelize
+ *   the operation on an image, by operating on tiles.  For each
+ *   tile, you want to generate an in-place image result at the same
+ *   resolution.  Suppose you choose a one-dimensional vertical tiling,
+ *   where the desired tile width is 256 pixels and the overlap is
+ *   30 pixels on left and right sides:
+ *
+ *     PIX *pixd = pixCreateTemplate(pixs);  // output
+ *     PIXTILING  *pt = pixTilingCreate(pixs, 0, 1, 256, 30, 0);
+ *     pixTilingGetCount(pt, &nx, NULL);
+ *     for (j = 0; j < nx; j++) {
+ *         PIX *pixt = pixTilingGetTile(pt, 0, j);
+ *         SomeInPlaceOperation(pixt, 30, 0, ...);
+ *         pixTilingPaintTile(pixd, 0, j, pixt, pt);
+ *         pixDestroy(&pixt);
+ *     }
+ *
+ *   In this example, note the following:
+ *    ~ The unspecfified in-place operation could instead generate
+ *      a new pix.  If this is done, the resulting pix must be the
+ *      same size as pixt, because pixTilingPaintTile() makes that
+ *      assumption, removing the overlap pixels before painting
+ *      into the destination.
+ *    ~ The 'overlap' parameters have been included in your function,
+ *      to indicate which pixels are not in the exterior overlap region.
+ *      You will need to change only pixels that are not in the overlap
+ *      region, because those are the pixels that will be painted
+ *      into the destination.
+ *    ~ For tiles on the outside of the image, mirrored pixels are
+ *      added to substitute for the overlap that is added to interior
+ *      tiles.  This allows you to implement your function without
+ *      reference to which tile it is; no special coding is necessary
+ *      for pixels that are near the image boundary.
+ *    ~ The tiles are labeled by (i, j) = (row, column),
+ *      and in this example there is one row and nx columns.
+ * 
+ */ + +#include "allheaders.h" + + +/*! + * \brief pixTilingCreate() + * + * \param[in] pixs pix to be tiled; any depth; colormap OK + * \param[in] nx number of tiles across image + * \param[in] ny number of tiles down image + * \param[in] w desired width of each tile + * \param[in] h desired height of each tile + * \param[in] xoverlap overlap into neighboring tiles on each side + * \param[in] yoverlap overlap into neighboring tiles above and below + * \return pixtiling, or NULL on error + * + *
+ * Notes:
+ *      (1) We put a clone of pixs in the PixTiling.
+ *      (2) The input to pixTilingCreate() for horizontal tiling can be
+ *          either the number of tiles across the image or the approximate
+ *          width of the tiles.  If the latter, the actual width will be
+ *          determined by making all tiles but the last of equal width, and
+ *          making the last as close to the others as possible.  The same
+ *          consideration is applied independently to the vertical tiling.
+ *          To specify tile width, set nx = 0; to specify the number of
+ *          tiles horizontally across the image, set w = 0.
+ *      (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for
+ *          vertical strips and nx = 1 for horizontal strips.
+ *      (4) The overlap must not be larger than the width or height of
+ *          the leftmost or topmost tile(s).
+ * 
+ */ +PIXTILING * +pixTilingCreate(PIX *pixs, + l_int32 nx, + l_int32 ny, + l_int32 w, + l_int32 h, + l_int32 xoverlap, + l_int32 yoverlap) +{ +l_int32 width, height; +PIXTILING *pt; + + PROCNAME("pixTilingCreate"); + + if (!pixs) + return (PIXTILING *)ERROR_PTR("pixs not defined", procName, NULL); + if (nx < 1 && w < 1) + return (PIXTILING *)ERROR_PTR("invalid width spec", procName, NULL); + if (ny < 1 && h < 1) + return (PIXTILING *)ERROR_PTR("invalid height spec", procName, NULL); + + /* Find the tile width and number of tiles. All tiles except the + * rightmost ones have the same width. The width of the + * rightmost ones are at least the width of the others and + * less than twice that width. Ditto for tile height. */ + pixGetDimensions(pixs, &width, &height, NULL); + if (nx == 0) + nx = L_MAX(1, width / w); + w = width / nx; /* possibly reset */ + if (ny == 0) + ny = L_MAX(1, height / h); + h = height / ny; /* possibly reset */ + if (xoverlap > w || yoverlap > h) { + L_INFO("tile width = %d, tile height = %d\n", procName, w, h); + return (PIXTILING *)ERROR_PTR("overlap too large", procName, NULL); + } + + pt = (PIXTILING *)LEPT_CALLOC(1, sizeof(PIXTILING)); + pt->pix = pixClone(pixs); + pt->xoverlap = xoverlap; + pt->yoverlap = yoverlap; + pt->nx = nx; + pt->ny = ny; + pt->w = w; + pt->h = h; + pt->strip = TRUE; + return pt; +} + + +/*! + * \brief pixTilingDestroy() + * + * \param[in,out] ppt will be set to null before returning + * \return void + */ +void +pixTilingDestroy(PIXTILING **ppt) +{ +PIXTILING *pt; + + PROCNAME("pixTilingDestroy"); + + if (ppt == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((pt = *ppt) == NULL) + return; + + pixDestroy(&pt->pix); + LEPT_FREE(pt); + *ppt = NULL; + return; +} + + +/*! + * \brief pixTilingGetCount() + * + * \param[in] pt pixtiling + * \param[out] pnx [optional] nx; can be null + * \param[out] pny [optional] ny; can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixTilingGetCount(PIXTILING *pt, + l_int32 *pnx, + l_int32 *pny) +{ + PROCNAME("pixTilingGetCount"); + + if (!pt) + return ERROR_INT("pt not defined", procName, 1); + if (pnx) *pnx = pt->nx; + if (pny) *pny = pt->ny; + return 0; +} + + +/*! + * \brief pixTilingGetSize() + * + * \param[in] pt pixtiling + * \param[out] pw [optional] tile width; can be null + * \param[out] ph [optional] tile height; can be null + * \return 0 if OK, 1 on error + */ +l_ok +pixTilingGetSize(PIXTILING *pt, + l_int32 *pw, + l_int32 *ph) +{ + PROCNAME("pixTilingGetSize"); + + if (!pt) + return ERROR_INT("pt not defined", procName, 1); + if (pw) *pw = pt->w; + if (ph) *ph = pt->h; + return 0; +} + + +/*! + * \brief pixTilingGetTile() + * + * \param[in] pt pixtiling + * \param[in] i tile row index + * \param[in] j tile column index + * \return pixd tile with appropriate boundary (overlap) pixels added, + * or NULL on error + */ +PIX * +pixTilingGetTile(PIXTILING *pt, + l_int32 i, + l_int32 j) +{ +l_int32 wpix, hpix, wt, ht, nx, ny; +l_int32 xoverlap, yoverlap, wtlast, htlast; +l_int32 left, top, xtraleft, xtraright, xtratop, xtrabot, width, height; +BOX *box; +PIX *pixs, *pixt, *pixd; + + PROCNAME("pixTilingGetTile"); + + if (!pt) + return (PIX *)ERROR_PTR("pt not defined", procName, NULL); + if ((pixs = pt->pix) == NULL) + return (PIX *)ERROR_PTR("pix not found", procName, NULL); + pixTilingGetCount(pt, &nx, &ny); + if (i < 0 || i >= ny) + return (PIX *)ERROR_PTR("invalid row index i", procName, NULL); + if (j < 0 || j >= nx) + return (PIX *)ERROR_PTR("invalid column index j", procName, NULL); + + /* Grab the tile with as much overlap as exists within the + * input pix. First, compute the (left, top) coordinates. */ + pixGetDimensions(pixs, &wpix, &hpix, NULL); + pixTilingGetSize(pt, &wt, &ht); + xoverlap = pt->xoverlap; + yoverlap = pt->yoverlap; + wtlast = wpix - wt * (nx - 1); + htlast = hpix - ht * (ny - 1); + left = L_MAX(0, j * wt - xoverlap); + top = L_MAX(0, i * ht - yoverlap); + + /* Get the width and height of the tile, including whatever + * overlap is available. */ + if (nx == 1) + width = wpix; + else if (j == 0) + width = wt + xoverlap; + else if (j == nx - 1) + width = wtlast + xoverlap; + else + width = wt + 2 * xoverlap; + + if (ny == 1) + height = hpix; + else if (i == 0) + height = ht + yoverlap; + else if (i == ny - 1) + height = htlast + yoverlap; + else + height = ht + 2 * yoverlap; + box = boxCreate(left, top, width, height); + pixt = pixClipRectangle(pixs, box, NULL); + boxDestroy(&box); + + /* If no overlap, do not add any special case borders */ + if (xoverlap == 0 && yoverlap == 0) + return pixt; + + /* Add overlap as a mirrored border, in the 8 special cases where + * the tile touches the border of the input pix. The xtratop (etc) + * parameters are required where the tile is either full width + * or full height. */ + xtratop = xtrabot = xtraleft = xtraright = 0; + if (nx == 1) + xtraleft = xtraright = xoverlap; + if (ny == 1) + xtratop = xtrabot = yoverlap; + if (i == 0 && j == 0) + pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, + yoverlap, xtrabot); + else if (i == 0 && j == nx - 1) + pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, + yoverlap, xtrabot); + else if (i == ny - 1 && j == 0) + pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, + xtratop, yoverlap); + else if (i == ny - 1 && j == nx - 1) + pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, + xtratop, yoverlap); + else if (i == 0) + pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot); + else if (i == ny - 1) + pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap); + else if (j == 0) + pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0); + else if (j == nx - 1) + pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0); + else + pixd = pixClone(pixt); + pixDestroy(&pixt); + + return pixd; +} + + +/*! + * \brief pixTilingNoStripOnPaint() + * + * \param[in] pt pixtiling + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The default for paint is to strip out the overlap pixels
+ *          that are added by pixTilingGetTile().  However, some
+ *          operations will generate an image with these pixels
+ *          stripped off.  This tells the paint operation not
+ *          to strip the added boundary pixels when painting.
+ * 
+ */ +l_ok +pixTilingNoStripOnPaint(PIXTILING *pt) +{ + PROCNAME("pixTilingNoStripOnPaint"); + + if (!pt) + return ERROR_INT("pt not defined", procName, 1); + pt->strip = FALSE; + return 0; +} + + +/*! + * \brief pixTilingPaintTile() + * + * \param[in] pixd dest: paint tile onto this, without overlap + * \param[in] i tile row index + * \param[in] j tile column index + * \param[in] pixs source: tile to be painted from + * \param[in] pt pixtiling struct + * \return 0 if OK, 1 on error + */ +l_ok +pixTilingPaintTile(PIX *pixd, + l_int32 i, + l_int32 j, + PIX *pixs, + PIXTILING *pt) +{ +l_int32 w, h; + + PROCNAME("pixTilingPaintTile"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pt) + return ERROR_INT("pt not defined", procName, 1); + if (i < 0 || i >= pt->ny) + return ERROR_INT("invalid row index i", procName, 1); + if (j < 0 || j >= pt->nx) + return ERROR_INT("invalid column index j", procName, 1); + + /* Strip added border pixels off if requested */ + pixGetDimensions(pixs, &w, &h, NULL); + if (pt->strip == TRUE) { + pixRasterop(pixd, j * pt->w, i * pt->h, + w - 2 * pt->xoverlap, h - 2 * pt->yoverlap, PIX_SRC, + pixs, pt->xoverlap, pt->yoverlap); + } else { + pixRasterop(pixd, j * pt->w, i * pt->h, w, h, PIX_SRC, pixs, 0, 0); + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pngio.c b/hgdriver/3rdparty/hgOCR/leptonica/pngio.c new file mode 100644 index 0000000..eaecc56 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pngio.c @@ -0,0 +1,2119 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - Copyright (C) 2017 Milner Technologies, Inc. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pngio.c + *
+ *
+ *    Reading png through stream
+ *          PIX        *pixReadStreamPng()
+ *
+ *    Reading png header
+ *          l_int32     readHeaderPng()
+ *          l_int32     freadHeaderPng()
+ *          l_int32     readHeaderMemPng()
+ *
+ *    Reading png metadata
+ *          l_int32     fgetPngResolution()
+ *          l_int32     isPngInterlaced()
+ *          l_int32     fgetPngColormapInfo()
+ *
+ *    Writing png through stream
+ *          l_int32     pixWritePng()  [ special top level ]
+ *          l_int32     pixWriteStreamPng()
+ *          l_int32     pixSetZlibCompression()
+ *
+ *    Set flag for special read mode
+ *          void        l_pngSetReadStrip16To8()
+ *
+ *    Low-level memio utility (thanks to T. D. Hintz)
+ *          static void memio_png_write_data()
+ *          static void memio_png_flush()
+ *          static void memio_png_read_data()
+ *          static void memio_free()
+ *
+ *    Reading png from memory
+ *          PIX        *pixReadMemPng()
+ *
+ *    Writing png to memory
+ *          l_int32     pixWriteMemPng()
+ *
+ *    Documentation: libpng.txt and example.c
+ *
+ *    On input (decompression from file), palette color images
+ *    are read into an 8 bpp Pix with a colormap, and 24 bpp
+ *    3 component color images are read into a 32 bpp Pix with
+ *    rgb samples.  On output (compression to file), palette color
+ *    images are written as 8 bpp with the colormap, and 32 bpp
+ *    full color images are written compressed as a 24 bpp,
+ *    3 component color image.
+ *
+ *    In the following, we use these abbreviations:
+ *       bps == bit/sample
+ *       spp == samples/pixel
+ *       bpp == bits/pixel of image in Pix (memory)
+ *    where each component is referred to as a "sample".
+ *
+ *    For reading and writing rgb and rgba images, we read and write
+ *    alpha if it exists (spp == 4) and do not read or write if
+ *    it doesn't (spp == 3).  The alpha component can be 'removed'
+ *    simply by setting spp to 3.  In leptonica, we make relatively
+ *    little explicit use of the alpha sample.  Note that the alpha
+ *    sample in the image is also called "alpha transparency",
+ *    "alpha component" and "alpha layer."
+ *
+ *    To change the zlib compression level, use pixSetZlibCompression()
+ *    before writing the file.  The default is for standard png compression.
+ *    The zlib compression value can be set [0 ... 9], with
+ *         0     no compression (huge files)
+ *         1     fastest compression
+ *         -1    default compression  (equivalent to 6 in latest version)
+ *         9     best compression
+ *    Note that if you are using the defined constants in zlib instead
+ *    of the compression integers given above, you must include zlib.h.
+ *
+ *    There is global for determining the size of retained samples:
+ *             var_PNG_STRIP_16_to_8
+ *    and a function l_pngSetReadStrip16To8() for setting it.
+ *    The default is TRUE, which causes pixRead() to strip each 16 bit
+ *    sample down to 8 bps:
+ *     ~ For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix
+ *     ~ For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix
+ *    If the variable is set to FALSE, the 16 bit gray samples
+ *    are saved when read; the 16 bit rgb samples return an error.
+ *    Note: results can be non-deterministic if used with
+ *    multi-threaded applications.
+ *
+ *    Thanks to a memory buffering utility contributed by T. D. Hintz,
+ *    encoding png directly into memory (and decoding from memory)
+ *    is now enabled without the use of any temp files.  Unlike with webp,
+ *    it is necessary to preserve the stream interface to enable writing
+ *    pixa to memory.  So there are two independent but very similar
+ *    implementations of png reading and writing.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if HAVE_LIBPNG /* defined in environ.h */ +/* --------------------------------------------*/ + +#include "png.h" + +#if HAVE_LIBZ +#include "zlib.h" +#else +#define Z_DEFAULT_COMPRESSION (-1) +#endif /* HAVE_LIBZ */ + +/* ------------------ Set default for read option -------------------- */ + /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping. + * If you don't strip, you can't read the gray-alpha spp = 2 images. */ +static l_int32 var_PNG_STRIP_16_TO_8 = 1; + +#ifndef NO_CONSOLE_IO +#define DEBUG_READ 0 +#define DEBUG_WRITE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*---------------------------------------------------------------------* + * Reading png through stream * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadStreamPng() + * + * \param[in] fp file stream + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) If called from pixReadStream(), the stream is positioned
+ *          at the beginning of the file.
+ *      (2) To do sequential reads of png format images from a stream,
+ *          use pixReadStreamPng()
+ *      (3) Any image with alpha is converted to RGBA (spp = 4, with
+ *          equal red, green and blue channels) on reading.
+ *          There are three important cases with alpha:
+ *          (a) grayscale-with-alpha (spp = 2), where bpp = 8, and each
+ *              pixel has an associated alpha (transparency) value
+ *              in the second component of the image data.
+ *          (b) spp = 1, d = 1 with colormap and alpha in the trans array.
+ *              Transparency is usually associated with the white background.
+ *          (c) spp = 1, d = 8 with colormap and alpha in the trans array.
+ *              Each color in the colormap has a separate transparency value.
+ *      (4) We use the high level png interface, where the transforms are set
+ *          up in advance and the header and image are read with a single
+ *          call.  The more complicated interface, where the header is
+ *          read first and the buffers for the raster image are user-
+ *          allocated before reading the image, works for single images,
+ *          but I could not get it to work properly for the successive
+ *          png reads that are required by pixaReadStream().
+ * 
+ */ +PIX * +pixReadStreamPng(FILE *fp) +{ +l_uint8 byte; +l_int32 rval, gval, bval; +l_int32 i, j, k, index, ncolors, bitval; +l_int32 wpl, d, spp, cindex, tRNS; +l_uint32 png_transforms; +l_uint32 *data, *line, *ppixel; +int num_palette, num_text, num_trans; +png_byte bit_depth, color_type, channels; +png_uint_32 w, h, rowbytes; +png_uint_32 xres, yres; +png_bytep rowptr, trans; +png_bytep *row_pointers; +png_structp png_ptr; +png_infop info_ptr, end_info; +png_colorp palette; +png_textp text_ptr; /* ptr to text_chunk */ +PIX *pix, *pix1; +PIXCMAP *cmap; + + PROCNAME("pixReadStreamPng"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + pix = NULL; + + /* Allocate the 3 data structures */ + if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); + + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); + } + + if ((end_info = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return (PIX *)ERROR_PTR("end_info not made", procName, NULL); + } + + /* Set up png setjmp error handling */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("internal png error", procName, NULL); + } + + png_init_io(png_ptr, fp); + + /* ---------------------------------------------------------- * + * - Set the transforms flags. Whatever happens here, + * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. + * - Do not use PNG_TRANSFORM_EXPAND, which would + * expand all images with bpp < 8 to 8 bpp. + * - Strip 16 --> 8 if reading 16-bit gray+alpha + * ---------------------------------------------------------- */ + /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ + if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ + png_transforms = PNG_TRANSFORM_STRIP_16; + } else { + png_transforms = PNG_TRANSFORM_IDENTITY; + L_INFO("not stripping 16 --> 8 in png reading\n", procName); + } + + /* Read it */ + png_read_png(png_ptr, info_ptr, png_transforms, NULL); + + row_pointers = png_get_rows(png_ptr, info_ptr); + w = png_get_image_width(png_ptr, info_ptr); + h = png_get_image_height(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + spp = channels; + tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; + + if (spp == 1) { + d = bit_depth; + } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ + d = 4 * bit_depth; + } + + /* Remove if/when this is implemented for all bit_depths */ + if (spp != 1 && bit_depth != 8) { + L_ERROR("spp = %d and bps = %d != 8\n" + "turn on 16 --> 8 stripping\n", procName, spp, bit_depth); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("not implemented for this image", + procName, NULL); + } + + cmap = NULL; + if (color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + cmap = pixcmapCreate(d); /* spp == 1 */ + for (cindex = 0; cindex < num_palette; cindex++) { + rval = palette[cindex].red; + gval = palette[cindex].green; + bval = palette[cindex].blue; + pixcmapAddColor(cmap, rval, gval, bval); + } + } + + if ((pix = pixCreate(w, h, d)) == NULL) { + pixcmapDestroy(&cmap); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + } + pixSetInputFormat(pix, IFF_PNG); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + pixSetColormap(pix, cmap); + pixSetSpp(pix, spp); + + if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0; j < rowbytes; j++) { + SET_DATA_BYTE(line, j, rowptr[j]); + } + } + } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ + L_INFO("converting (gray + alpha) ==> RGBA\n", procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = k = 0; j < w; j++) { + /* Copy gray value into r, g and b */ + SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); + ppixel++; + } + } + pixSetSpp(pix, 4); /* we do not support 2 spp pix */ + } else if (spp == 3 || spp == 4) { + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = k = 0; j < w; j++) { + SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); + if (spp == 3) /* set to opaque; some readers are buggy */ + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255); + else /* spp == 4 */ + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); + ppixel++; + } + } + } + + /* Special spp == 1 cases with transparency: + * (1) 8 bpp without colormap; assume full transparency + * (2) 1 bpp with colormap + trans array (for alpha) + * (3) 8 bpp with colormap + trans array (for alpha) + * These all require converting to RGBA */ + if (spp == 1 && tRNS) { + if (!cmap) { + /* Case 1: make fully transparent RGBA image */ + L_INFO("transparency, 1 spp, no colormap, no transparency array: " + "convention is fully transparent image\n", procName); + L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName); + pixDestroy(&pix); + pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ + pixSetSpp(pix, 4); + } else { + L_INFO("converting (cmap + alpha) ==> RGBA\n", procName); + + /* Grab the transparency array */ + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); + if (!trans) { /* invalid png file */ + pixDestroy(&pix); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", + procName, NULL); + } + + /* Save the cmap and destroy the pix */ + cmap = pixcmapCopy(pixGetColormap(pix)); + ncolors = pixcmapGetCount(cmap); + pixDestroy(&pix); + + /* Start over with 32 bit RGBA */ + pix = pixCreate(w, h, 32); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + pixSetSpp(pix, 4); + +#if DEBUG_READ + fprintf(stderr, "ncolors = %d, num_trans = %d\n", + ncolors, num_trans); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if (i < num_trans) { + fprintf(stderr, "(r,g,b,a) = (%d,%d,%d,%d)\n", + rval, gval, bval, trans[i]); + } else { + fprintf(stderr, "(r,g,b,a) = (%d,%d,%d,<<255>>)\n", + rval, gval, bval); + } + } +#endif /* DEBUG_READ */ + + /* Extract the data and convert to RGBA */ + if (d == 1) { + /* Case 2: 1 bpp with transparency (usually) behind white */ + L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName); + if (num_trans == 1) + L_INFO("num_trans = 1; second color opaque by default\n", + procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0, index = 0; j < rowbytes; j++) { + byte = rowptr[j]; + for (k = 0; k < 8 && index < w; k++, index++) { + bitval = (byte >> (7 - k)) & 1; + pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, ppixel); + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, + bitval < num_trans ? trans[bitval] : 255); + ppixel++; + } + } + } + } else if (d == 8) { + /* Case 3: 8 bpp with cmap and associated transparency */ + L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0; j < w; j++) { + index = rowptr[j]; + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, ppixel); + /* Assume missing entries to be 255 (opaque) + * according to the spec: + * http://www.w3.org/TR/PNG/#11tRNS */ + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, + index < num_trans ? trans[index] : 255); + ppixel++; + } + } + } else { + L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", + procName, d); + } + pixcmapDestroy(&cmap); + } + } + +#if DEBUG_READ + if (cmap) { + for (i = 0; i < 16; i++) { + fprintf(stderr, "[%d] = %d\n", i, + ((l_uint8 *)(cmap->array))[i]); + } + } +#endif /* DEBUG_READ */ + + /* Final adjustments for bpp = 1. + * + If there is no colormap, the image must be inverted because + * png stores black pixels as 0. + * + We have already handled the case of cmapped, 1 bpp pix + * with transparency, where the output pix is 32 bpp RGBA. + * If there is no transparency but the pix has a colormap, + * we remove the colormap, because functions operating on + * 1 bpp images in leptonica assume no colormap. + * + The colormap must be removed in such a way that the pixel + * values are not changed. If the values are only black and + * white, we return a 1 bpp image; if gray, return an 8 bpp pix; + * otherwise, return a 32 bpp rgb pix. + * + * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag + * to do the inversion, because that flag (since version 1.0.9) + * inverts 8 bpp grayscale as well, which we don't want to do. + * (It also doesn't work if there is a colormap.) + * + * Note that if the input png is a 1-bit with colormap and + * transparency, it has already been rendered as a 32 bpp, + * spp = 4 rgba pix. + */ + if (pixGetDepth(pix) == 1) { + if (!cmap) { + pixInvert(pix, pix); + } else { + L_INFO("removing opaque cmap from 1 bpp\n", procName); + pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + pixDestroy(&pix); + pix = pix1; + } + } + + xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); + yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); + pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ + pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ + + /* Get the text if there is any */ + png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); + if (num_text && text_ptr) + pixSetText(pix, text_ptr->text); + + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return pix; +} + + +/*---------------------------------------------------------------------* + * Reading png header * + *---------------------------------------------------------------------*/ +/*! + * \brief readHeaderPng() + * + * \param[in] filename + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] piscmap [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, iscmap is returned as 1; else 0.
+ *      (2) For gray+alpha, although the png records bps = 16, we
+ *          consider this as two 8 bpp samples (gray and alpha).
+ *          When a gray+alpha is read, it is converted to 32 bpp RGBA.
+ * 
+ */ +l_ok +readHeaderPng(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderPng"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderPng() + * + * \param[in] fp file stream + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] piscmap [optional] + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See readHeaderPng().  We only need the first 40 bytes in the file.
+ * 
+ */ +l_ok +freadHeaderPng(FILE *fp, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 nbytes, ret; +l_uint8 data[40]; + + PROCNAME("freadHeaderPng"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + + nbytes = fnbytesInFile(fp); + if (nbytes < 40) + return ERROR_INT("file too small to be png", procName, 1); + if (fread(data, 1, 40, fp) != 40) + return ERROR_INT("error reading data", procName, 1); + ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap); + return ret; +} + + +/*! + * \brief readHeaderMemPng() + * + * \param[in] data + * \param[in] size 40 bytes is sufficient + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \param[out] piscmap [optional] input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See readHeaderPng().
+ *      (2) png colortypes (see png.h: PNG_COLOR_TYPE_*):
+ *          0:  gray; fully transparent (with tRNS) (1 spp)
+ *          2:  RGB (3 spp)
+ *          3:  colormap; colormap+alpha (with tRNS) (1 spp)
+ *          4:  gray + alpha (2 spp)
+ *          6:  RGBA (4 spp)
+ *          Note:
+ *            0 and 3 have the alpha information in a tRNS chunk
+ *            4 and 6 have separate alpha samples with each pixel.
+ * 
+ */ +l_ok +readHeaderMemPng(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_uint16 twobytes; +l_uint16 *pshort; +l_int32 colortype, w, h, bps, spp; +l_uint32 *pword; + + PROCNAME("readHeaderMemPng"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (size < 40) + return ERROR_INT("size < 40", procName, 1); + + /* Check password */ + if (data[0] != 137 || data[1] != 80 || data[2] != 78 || + data[3] != 71 || data[4] != 13 || data[5] != 10 || + data[6] != 26 || data[7] != 10) + return ERROR_INT("not a valid png file", procName, 1); + + pword = (l_uint32 *)data; + pshort = (l_uint16 *)data; + w = convertOnLittleEnd32(pword[4]); + h = convertOnLittleEnd32(pword[5]); + if (w < 1 || h < 1) + return ERROR_INT("invalid w or h", procName, 1); + twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */ + /* and the color type */ + colortype = twobytes & 0xff; /* color type */ + bps = twobytes >> 8; /* bits/sample */ + + /* Special case with alpha that is extracted as RGBA. + * Note that the cmap+alpha is also extracted as RGBA, + * but only if the tRNS chunk exists, which we can't tell + * by this simple parser.*/ + if (colortype == 4) + L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", procName); + + if (colortype == 2) { /* RGB */ + spp = 3; + } else if (colortype == 6) { /* RGBA */ + spp = 4; + } else if (colortype == 4) { /* gray + alpha */ + spp = 2; + bps = 8; /* both the gray and alpha are 8-bit samples */ + } else { /* gray (0) or cmap (3) or cmap+alpha (3) */ + spp = 1; + } + if (bps < 1 || bps > 16) { + L_ERROR("invalid bps = %d\n", procName, bps); + return 1; + } + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (piscmap) { + if (colortype & 1) /* palette */ + *piscmap = 1; + else + *piscmap = 0; + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Reading png metadata * + *---------------------------------------------------------------------*/ +/* + * fgetPngResolution() + * + * Input: fp (file stream opened for read) + * &xres, &yres ( resolution in ppi) + * Return: 0 if OK; 1 on error + * + * Notes: + * (1) If neither resolution field is set, this is not an error; + * the returned resolution values are 0 (designating 'unknown'). + * (2) Side-effect: this rewinds the stream. + */ +l_int32 +fgetPngResolution(FILE *fp, + l_int32 *pxres, + l_int32 *pyres) +{ +png_uint_32 xres, yres; +png_structp png_ptr; +png_infop info_ptr; + + PROCNAME("fgetPngResolution"); + + if (pxres) *pxres = 0; + if (pyres) *pyres = 0; + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", procName, 1); + + /* Make the two required structs */ + if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return ERROR_INT("png_ptr not made", procName, 1); + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return ERROR_INT("info_ptr not made", procName, 1); + } + + /* Set up png setjmp error handling. + * Without this, an error calls exit. */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return ERROR_INT("internal png error", procName, 1); + } + + /* Read the metadata */ + rewind(fp); + png_init_io(png_ptr, fp); + png_read_info(png_ptr, info_ptr); + + xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); + yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); + *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */ + *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5); + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + rewind(fp); + return 0; +} + + +/*! + * \brief isPngInterlaced() + * + * \param[in] filename + * \param[out] pinterlaced 1 if interlaced png; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +isPngInterlaced(const char *filename, + l_int32 *pinterlaced) +{ +l_uint8 buf[32]; +FILE *fp; + + PROCNAME("isPngInterlaced"); + + if (!pinterlaced) + return ERROR_INT("&interlaced not defined", procName, 1); + *pinterlaced = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + if (fread(buf, 1, 32, fp) != 32) { + fclose(fp); + return ERROR_INT("data not read", procName, 1); + } + fclose(fp); + + *pinterlaced = (buf[28] == 0) ? 0 : 1; + return 0; +} + + +/* + * \brief fgetPngColormapInfo() + * + * \param[in] fp file stream opened for read + * \param[out] pcmap optional; use NULL to skip + * \param[out] ptransparency optional; 1 if colormapped with + * transparency, 0 otherwise; use NULL to skip + * \return 0 if OK, 1 on error + * + * Notes: + * (1) The transparency information in a png is in the tRNA array, + * which is separate from the colormap. If this array exists + * and if any element is less than 255, there exists some + * transparency. + * (2) Side-effect: this rewinds the stream. + */ +l_ok +fgetPngColormapInfo(FILE *fp, + PIXCMAP **pcmap, + l_int32 *ptransparency) +{ +l_int32 i, cindex, rval, gval, bval, num_palette, num_trans; +png_byte bit_depth, color_type; +png_bytep trans; +png_colorp palette; +png_structp png_ptr; +png_infop info_ptr; + + PROCNAME("fgetPngColormapInfo"); + + if (pcmap) *pcmap = NULL; + if (ptransparency) *ptransparency = 0; + if (!pcmap && !ptransparency) + return ERROR_INT("no output defined", procName, 1); + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + + /* Make the two required structs */ + if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return ERROR_INT("png_ptr not made", procName, 1); + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return ERROR_INT("info_ptr not made", procName, 1); + } + + /* Set up png setjmp error handling. + * Without this, an error calls exit. */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (pcmap && *pcmap) pixcmapDestroy(pcmap); + return ERROR_INT("internal png error", procName, 1); + } + + /* Read the metadata and check if there is a colormap */ + rewind(fp); + png_init_io(png_ptr, fp); + png_read_info(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + if (color_type != PNG_COLOR_TYPE_PALETTE && + color_type != PNG_COLOR_MASK_PALETTE) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 0; + } + + /* Optionally, read the colormap */ + if (pcmap) { + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */ + for (cindex = 0; cindex < num_palette; cindex++) { + rval = palette[cindex].red; + gval = palette[cindex].green; + bval = palette[cindex].blue; + pixcmapAddColor(*pcmap, rval, gval, bval); + } + } + + /* Optionally, look for transparency. Note that the colormap + * has been initialized to fully opaque. */ + if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); + if (trans) { + for (i = 0; i < num_trans; i++) { + if (trans[i] < 255) { /* not fully opaque */ + *ptransparency = 1; + if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]); + } + } + } else { + L_ERROR("transparency array not returned\n", procName); + } + } + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + rewind(fp); + return 0; +} + + +/*---------------------------------------------------------------------* + * Writing png through stream * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWritePng() + * + * \param[in] filename + * \param[in] pix + * \param[in] gamma + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Special version for writing png with a specified gamma.
+ *          When using pixWrite(), no field is given for gamma.
+ * 
+ */ +l_ok +pixWritePng(const char *filename, + PIX *pix, + l_float32 gamma) +{ +FILE *fp; + + PROCNAME("pixWritePng"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + + if (pixWriteStreamPng(fp, pix, gamma)) { + fclose(fp); + return ERROR_INT("pix not written to stream", procName, 1); + } + + fclose(fp); + return 0; +} + + +/*! + * \brief pixWriteStreamPng() + * + * \param[in] fp file stream + * \param[in] pix + * \param[in] gamma use 0.0 if gamma is not defined + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If called from pixWriteStream(), the stream is positioned
+ *          at the beginning of the file.
+ *      (2) To do sequential writes of png format images to a stream,
+ *          use pixWriteStreamPng() directly.
+ *      (3) gamma is an optional png chunk.  If no gamma value is to be
+ *          placed into the file, use gamma = 0.0.  Otherwise, if
+ *          gamma > 0.0, its value is written into the header.
+ *      (4) The use of gamma in png is highly problematic.  For an illuminating
+ *          discussion, see:  http://hsivonen.iki.fi/png-gamma/
+ *      (5) What is the effect/meaning of gamma in the png file?  This
+ *          gamma, which we can call the 'source' gamma, is the
+ *          inverse of the gamma that was used in enhance.c to brighten
+ *          or darken images.  The 'source' gamma is supposed to indicate
+ *          the intensity mapping that was done at the time the
+ *          image was captured.  Display programs typically apply a
+ *          'display' gamma of 2.2 to the output, which is intended
+ *          to linearize the intensity based on the response of
+ *          thermionic tubes (CRTs).  Flat panel LCDs have typically
+ *          been designed to give a similar response as CRTs (call it
+ *          "backward compatibility").  The 'display' gamma is
+ *          in some sense the inverse of the 'source' gamma.
+ *          jpeg encoders attached to scanners and cameras will lighten
+ *          the pixels, applying a gamma corresponding to approximately
+ *          a square-root relation of output vs input:
+ *                output = input^(gamma)
+ *          where gamma is often set near 0.4545  (1/gamma is 2.2).
+ *          This is stored in the image file.  Then if the display
+ *          program reads the gamma, it will apply a display gamma,
+ *          typically about 2.2; the product is 1.0, and the
+ *          display program produces a linear output.  This works because
+ *          the dark colors were appropriately boosted by the scanner,
+ *          as described by the 'source' gamma, so they should not
+ *          be further boosted by the display program.
+ *      (6) As an example, with xv and display, if no gamma is stored,
+ *          the program acts as if gamma were 0.4545, multiplies this by 2.2,
+ *          and does a linear rendering.  Taking this as a baseline
+ *          brightness, if the stored gamma is:
+ *              > 0.4545, the image is rendered lighter than baseline
+ *              < 0.4545, the image is rendered darker than baseline
+ *          In contrast, gqview seems to ignore the gamma chunk in png.
+ *      (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
+ *          and 32.  However, it is possible, and in some cases desirable,
+ *          to write out a png file using an rgb pix that has 24 bpp.
+ *          For example, the open source xpdf SplashBitmap class generates
+ *          24 bpp rgb images.  Consequently, we enable writing 24 bpp pix.
+ *          To generate such a pix, you can make a 24 bpp pix without data
+ *          and assign the data array to the pix; e.g.,
+ *              pix = pixCreateHeader(w, h, 24);
+ *              pixSetData(pix, rgbdata);
+ *          See pixConvert32To24() for an example, where we get rgbdata
+ *          from the 32 bpp pix.  Caution: do not call pixSetPadBits(),
+ *          because the alignment is wrong and you may erase part of the
+ *          last pixel on each line.
+ *      (8) If the pix has a colormap, it is written to file.  In most
+ *          situations, the alpha component is 255 for each colormap entry,
+ *          which is opaque and indicates that it should be ignored.
+ *          However, if any alpha component is not 255, it is assumed that
+ *          the alpha values are valid, and they are written to the png
+ *          file in a tRNS segment.  On readback, the tRNS segment is
+ *          identified, and the colormapped image with alpha is converted
+ *          to a 4 spp rgba image.
+ * 
+ */ +l_ok +pixWriteStreamPng(FILE *fp, + PIX *pix, + l_float32 gamma) +{ +char commentstring[] = "Comment"; +l_int32 i, j, k; +l_int32 wpl, d, spp, cmflag, opaque; +l_int32 ncolors, compval; +l_int32 *rmap, *gmap, *bmap, *amap; +l_uint32 *data, *ppixel; +png_byte bit_depth, color_type; +png_byte alpha[256]; +png_uint_32 w, h; +png_uint_32 xres, yres; +png_bytep *row_pointers; +png_bytep rowbuffer; +png_structp png_ptr; +png_infop info_ptr; +png_colorp palette; +PIX *pix1; +PIXCMAP *cmap; +char *text; + + PROCNAME("pixWriteStreamPng"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + /* Allocate the 2 data structures */ + if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return ERROR_INT("png_ptr not made", procName, 1); + + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return ERROR_INT("info_ptr not made", procName, 1); + } + + /* Set up png setjmp error handling */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return ERROR_INT("internal png error", procName, 1); + } + + png_init_io(png_ptr, fp); + + /* With best zlib compression (9), get between 1 and 10% improvement + * over default (6), but the compression is 3 to 10 times slower. + * Use the zlib default (6) as our default compression unless + * pix->special falls in the range [10 ... 19]; then subtract 10 + * to get the compression value. */ + compval = Z_DEFAULT_COMPRESSION; + if (pix->special >= 10 && pix->special < 20) + compval = pix->special - 10; + png_set_compression_level(png_ptr, compval); + + w = pixGetWidth(pix); + h = pixGetHeight(pix); + d = pixGetDepth(pix); + spp = pixGetSpp(pix); + if ((cmap = pixGetColormap(pix))) + cmflag = 1; + else + cmflag = 0; + pixSetPadBits(pix, 0); + + /* Set the color type and bit depth. */ + if (d == 32 && spp == 4) { + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ + cmflag = 0; /* ignore if it exists */ + } else if (d == 24 || d == 32) { + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; /* 2 */ + cmflag = 0; /* ignore if it exists */ + } else { + bit_depth = d; + color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ + } + if (cmflag) + color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ + +#if DEBUG_WRITE + fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", + cmflag, bit_depth, color_type); +#endif /* DEBUG_WRITE */ + + png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + /* Store resolution in ppm, if known */ + xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); + yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); + if ((xres == 0) || (yres == 0)) + png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); + else + png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); + + if (cmflag) { + pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); + ncolors = pixcmapGetCount(cmap); + pixcmapIsOpaque(cmap, &opaque); + + /* Make and save the palette */ + palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); + for (i = 0; i < ncolors; i++) { + palette[i].red = (png_byte)rmap[i]; + palette[i].green = (png_byte)gmap[i]; + palette[i].blue = (png_byte)bmap[i]; + alpha[i] = (png_byte)amap[i]; + } + + png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); + if (!opaque) /* alpha channel has some transparency; assume valid */ + png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, + (int)ncolors, NULL); + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + LEPT_FREE(amap); + } + + /* 0.4545 is treated as the default by some image + * display programs (not gqview). A value > 0.4545 will + * lighten an image as displayed by xv, display, etc. */ + if (gamma > 0.0) + png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); + + if ((text = pixGetText(pix))) { + png_text text_chunk; + text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; + text_chunk.key = commentstring; + text_chunk.text = text; + text_chunk.text_length = strlen(text); +#ifdef PNG_ITXT_SUPPORTED + text_chunk.itxt_length = 0; + text_chunk.lang = NULL; + text_chunk.lang_key = NULL; +#endif + png_set_text(png_ptr, info_ptr, &text_chunk, 1); + } + + /* Write header and palette info */ + png_write_info(png_ptr, info_ptr); + + if ((d != 32) && (d != 24)) { /* not rgb color */ + /* Generate a temporary pix with bytes swapped. + * For writing a 1 bpp image as png: + * ~ if no colormap, invert the data, because png writes + * black as 0 + * ~ if colormapped, do not invert the data; the two RGBA + * colors can have any value. */ + if (d == 1 && !cmap) { + pix1 = pixInvert(NULL, pix); + pixEndianByteSwap(pix1); + } else { + pix1 = pixEndianByteSwapNew(pix); + } + if (!pix1) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (cmflag) LEPT_FREE(palette); + return ERROR_INT("pix1 not made", procName, 1); + } + + /* Make and assign array of image row pointers */ + row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); + wpl = pixGetWpl(pix1); + data = pixGetData(pix1); + for (i = 0; i < h; i++) + row_pointers[i] = (png_bytep)(data + i * wpl); + png_set_rows(png_ptr, info_ptr, row_pointers); + + /* Transfer the data */ + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + + if (cmflag) LEPT_FREE(palette); + LEPT_FREE(row_pointers); + pixDestroy(&pix1); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; + } + + /* For rgb, compose and write a row at a time */ + data = pixGetData(pix); + wpl = pixGetWpl(pix); + if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); + } + } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either + * the pix has 4 spp or writing it is requested anyway */ + rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + for (j = k = 0; j < w; j++) { + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); + if (spp == 4) + rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); + ppixel++; + } + + png_write_rows(png_ptr, &rowbuffer, 1); + } + LEPT_FREE(rowbuffer); + } + + png_write_end(png_ptr, info_ptr); + + if (cmflag) + LEPT_FREE(palette); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; +} + + +/*! + * \brief pixSetZlibCompression() + * + * \param[in] pix + * \param[in] compval zlib compression value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Valid zlib compression values are in the interval [0 ... 9],
+ *          where, as defined in zlib.h:
+ *            0         Z_NO_COMPRESSION
+ *            1         Z_BEST_SPEED    (poorest compression)
+ *            9         Z_BEST_COMPRESSION
+ *          For the default value, use either of these:
+ *            6         Z_DEFAULT_COMPRESSION
+ *           -1         (resolves to Z_DEFAULT_COMPRESSION)
+ *      (2) If you use the defined constants in zlib.h instead of the
+ *          compression integers given above, you must include zlib.h.
+ * 
+ */ +l_ok +pixSetZlibCompression(PIX *pix, + l_int32 compval) +{ + PROCNAME("pixSetZlibCompression"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (compval < 0 || compval > 9) { + L_ERROR("Invalid zlib comp val; using default\n", procName); + compval = Z_DEFAULT_COMPRESSION; + } + pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */ + return 0; +} + + +/*---------------------------------------------------------------------* + * Set flag for stripping 16 bits on reading * + *---------------------------------------------------------------------*/ +/*! + * \brief l_pngSetReadStrip16To8() + * + * \param[in] flag 1 for stripping 16 bpp to 8 bpp on reading; + * 0 for leaving 16 bpp + * \return void + */ +void +l_pngSetReadStrip16To8(l_int32 flag) +{ + var_PNG_STRIP_16_TO_8 = flag; +} + + +/*-------------------------------------------------------------------------* + * Memio utility * + * libpng read/write callback replacements for performing memory I/O * + * * + * Copyright (C) 2017 Milner Technologies, Inc. This content is a * + * component of leptonica and is provided under the terms of the * + * Leptonica license. * + *-------------------------------------------------------------------------*/ + + /*! A node in a linked list of memory buffers that hold I/O content */ +struct MemIOData +{ + char* m_Buffer; /*!< pointer to this node's I/O content */ + l_int32 m_Count; /*!< number of I/O content bytes read or written */ + l_int32 m_Size; /*!< allocated size of m_buffer */ + struct MemIOData *m_Next; /*!< pointer to the next node in the list; */ + /*!< zero if this is the last node */ + struct MemIOData *m_Last; /*!< pointer to the last node in the linked */ + /*!< list. The last node is where new */ + /*!< content is written. */ +}; +typedef struct MemIOData MEMIODATA; + +static void memio_png_write_data(png_structp png_ptr, png_bytep data, + png_size_t length); +static void memio_png_flush(MEMIODATA* pthing); +static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes, + png_size_t byteCountToRead); +static void memio_free(MEMIODATA* pthing); + +static const l_int32 MEMIO_BUFFER_SIZE = 8192; /*! buffer alloc size */ + +/* + * \brief memio_png_write_data() + * + * \param[in] png_ptr + * \param[in] data + * \param[in] len size of array data in bytes + * + *
+ * Notes:
+ *      (1) This is a libpng callback for writing an image into a
+ *          linked list of memory buffers.
+ * 
+ */ +static void +memio_png_write_data(png_structp png_ptr, + png_bytep data, + png_size_t len) +{ +MEMIODATA *thing, *last; +l_int32 written = 0; +l_int32 remainingSpace, remainingToWrite; + + thing = (struct MemIOData*)png_get_io_ptr(png_ptr); + last = (struct MemIOData*)thing->m_Last; + if (last->m_Buffer == NULL) { + if (len > MEMIO_BUFFER_SIZE) { + last->m_Buffer = (char *)LEPT_MALLOC(len); + memcpy(last->m_Buffer, data, len); + last->m_Size = last->m_Count = len; + return; + } + + last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); + last->m_Size = MEMIO_BUFFER_SIZE; + } + + while (written < len) { + if (last->m_Count == last->m_Size) { + MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA)); + next->m_Next = NULL; + next->m_Count = 0; + next->m_Last = next; + + last->m_Next = next; + last = thing->m_Last = next; + + last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); + last->m_Size = MEMIO_BUFFER_SIZE; + } + + remainingSpace = last->m_Size - last->m_Count; + remainingToWrite = len - written; + if (remainingSpace < remainingToWrite) { + memcpy(last->m_Buffer + last->m_Count, data + written, + remainingSpace); + written += remainingSpace; + last->m_Count += remainingSpace; + } else { + memcpy(last->m_Buffer + last->m_Count, data + written, + remainingToWrite); + written += remainingToWrite; + last->m_Count += remainingToWrite; + } + } +} + + +/* + * \brief memio_png_flush() + * + * \param[in] pthing + * + *
+ * Notes:
+ *      (1) This consolidates write buffers into a single buffer at the
+ *          haed of the link list of buffers.
+ * 
+ */ +static void +memio_png_flush(MEMIODATA *pthing) +{ +l_int32 amount = 0; +l_int32 copied = 0; +MEMIODATA *buffer = 0; +char *data = 0; + + /* If the data is in one buffer, give the buffer to the user. */ + if (pthing->m_Next == NULL) return; + + /* Consolidate multiple buffers into one new one; add the buffer + * sizes together. */ + amount = pthing->m_Count; + buffer = pthing->m_Next; + while (buffer != NULL) { + amount += buffer->m_Count; + buffer = buffer->m_Next; + } + + /* Copy data to a new buffer. */ + data = (char *)LEPT_MALLOC(amount); + memcpy(data, pthing->m_Buffer, pthing->m_Count); + copied = pthing->m_Count; + + LEPT_FREE(pthing->m_Buffer); + pthing->m_Buffer = NULL; + + /* Don't delete original "thing" because we don't control it. */ + buffer = pthing->m_Next; + pthing->m_Next = NULL; + while (buffer != NULL && copied < amount) { + MEMIODATA* old; + memcpy(data + copied, buffer->m_Buffer, buffer->m_Count); + copied += buffer->m_Count; + + old = buffer; + buffer = buffer->m_Next; + + LEPT_FREE(old->m_Buffer); + LEPT_FREE(old); + } + + pthing->m_Buffer = data; + pthing->m_Count = copied; + pthing->m_Size = amount; + return; +} + + +/* + * \brief memio_png_read_data() + * + * \param[in] png_ptr + * \param[in] outBytes + * \param[in] byteCountToRead + * + *
+ * Notes:
+ *      (1) This is a libpng callback that reads an image from a single
+ *          memory buffer.
+ * 
+ */ +static void +memio_png_read_data(png_structp png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) +{ +MEMIODATA *thing; + + thing = (MEMIODATA *)png_get_io_ptr(png_ptr); + if (byteCountToRead > (thing->m_Size - thing->m_Count)) { + png_error(png_ptr, "read error in memio_png_read_data"); + } + memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead); + thing->m_Count += byteCountToRead; +} + + +/* + * \brief memio_free() + * + * \param[in] pthing + * + *
+ * Notes:
+ *      (1) This frees all the write buffers in the linked list.  It must
+ *          be done before exiting the pixWriteMemPng().
+ * 
+ */ +static void +memio_free(MEMIODATA* pthing) +{ +MEMIODATA *buffer, *old; + + if (pthing->m_Buffer != NULL) + LEPT_FREE(pthing->m_Buffer); + + pthing->m_Buffer = NULL; + buffer = pthing->m_Next; + while (buffer != NULL) { + old = buffer; + buffer = buffer->m_Next; + + if (old->m_Buffer != NULL) + LEPT_FREE(old->m_Buffer); + LEPT_FREE(old); + } +} + + +/*---------------------------------------------------------------------* + * Reading png from memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadMemPng() + * + * \param[in] filedata png compressed data in memory + * \param[in] filesize number of bytes in data + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixReastreamPng().
+ * 
+ */ +PIX * +pixReadMemPng(const l_uint8 *filedata, + size_t filesize) +{ +l_uint8 byte; +l_int32 rval, gval, bval; +l_int32 i, j, k, index, ncolors, bitval; +l_int32 wpl, d, spp, cindex, tRNS; +l_uint32 png_transforms; +l_uint32 *data, *line, *ppixel; +int num_palette, num_text, num_trans; +png_byte bit_depth, color_type, channels; +png_uint_32 w, h, rowbytes; +png_uint_32 xres, yres; +png_bytep rowptr, trans; +png_bytep *row_pointers; +png_structp png_ptr; +png_infop info_ptr, end_info; +png_colorp palette; +png_textp text_ptr; /* ptr to text_chunk */ +PIX *pix, *pix1; +PIXCMAP *cmap; +MEMIODATA state; + + PROCNAME("pixReadMemPng"); + + if (!filedata) + return (PIX *)ERROR_PTR("filedata not defined", procName, NULL); + if (filesize < 1) + return (PIX *)ERROR_PTR("invalid filesize", procName, NULL); + + state.m_Next = 0; + state.m_Count = 0; + state.m_Last = &state; + state.m_Buffer = (char*)filedata; + state.m_Size = filesize; + pix = NULL; + + /* Allocate the 3 data structures */ + if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); + + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); + } + + if ((end_info = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return (PIX *)ERROR_PTR("end_info not made", procName, NULL); + } + + /* Set up png setjmp error handling */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("internal png error", procName, NULL); + } + + png_set_read_fn(png_ptr, &state, memio_png_read_data); + + /* ---------------------------------------------------------- * + * Set the transforms flags. Whatever happens here, + * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. + * Also, do not use PNG_TRANSFORM_EXPAND, which would + * expand all images with bpp < 8 to 8 bpp. + * ---------------------------------------------------------- */ + /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ + if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ + png_transforms = PNG_TRANSFORM_STRIP_16; + } else { + png_transforms = PNG_TRANSFORM_IDENTITY; + L_INFO("not stripping 16 --> 8 in png reading\n", procName); + } + + /* Read it */ + png_read_png(png_ptr, info_ptr, png_transforms, NULL); + + row_pointers = png_get_rows(png_ptr, info_ptr); + w = png_get_image_width(png_ptr, info_ptr); + h = png_get_image_height(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + spp = channels; + tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; + + if (spp == 1) { + d = bit_depth; + } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ + d = 4 * bit_depth; + } + + /* Remove if/when this is implemented for all bit_depths */ + if (spp == 3 && bit_depth != 8) { + fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("not implemented for this depth", + procName, NULL); + } + + cmap = NULL; + if (color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + cmap = pixcmapCreate(d); /* spp == 1 */ + for (cindex = 0; cindex < num_palette; cindex++) { + rval = palette[cindex].red; + gval = palette[cindex].green; + bval = palette[cindex].blue; + pixcmapAddColor(cmap, rval, gval, bval); + } + } + + if ((pix = pixCreate(w, h, d)) == NULL) { + pixcmapDestroy(&cmap); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + pixcmapDestroy(&cmap); + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + } + pixSetInputFormat(pix, IFF_PNG); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + pixSetColormap(pix, cmap); + pixSetSpp(pix, spp); + + if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0; j < rowbytes; j++) { + SET_DATA_BYTE(line, j, rowptr[j]); + } + } + } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ + L_INFO("converting (gray + alpha) ==> RGBA\n", procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = k = 0; j < w; j++) { + /* Copy gray value into r, g and b */ + SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); + ppixel++; + } + } + pixSetSpp(pix, 4); /* we do not support 2 spp pix */ + } else if (spp == 3 || spp == 4) { + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = k = 0; j < w; j++) { + SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); + if (spp == 4) + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); + ppixel++; + } + } + } + + /* Special spp == 1 cases with transparency: + * (1) 8 bpp without colormap; assume full transparency + * (2) 1 bpp with colormap + trans array (for alpha) + * (3) 8 bpp with colormap + trans array (for alpha) + * These all require converting to RGBA */ + if (spp == 1 && tRNS) { + if (!cmap) { + /* Case 1: make fully transparent RGBA image */ + L_INFO("transparency, 1 spp, no colormap, no transparency array: " + "convention is fully transparent image\n", procName); + L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName); + pixDestroy(&pix); + pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ + pixSetSpp(pix, 4); + } else { + L_INFO("converting (cmap + alpha) ==> RGBA\n", procName); + + /* Grab the transparency array */ + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); + if (!trans) { /* invalid png file */ + pixDestroy(&pix); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", + procName, NULL); + } + + /* Save the cmap and destroy the pix */ + cmap = pixcmapCopy(pixGetColormap(pix)); + ncolors = pixcmapGetCount(cmap); + pixDestroy(&pix); + + /* Start over with 32 bit RGBA */ + pix = pixCreate(w, h, 32); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + pixSetSpp(pix, 4); + +#if DEBUG_READ + fprintf(stderr, "ncolors = %d, num_trans = %d\n", + ncolors, num_trans); + for (i = 0; i < ncolors; i++) { + pixcmapGetColor(cmap, i, &rval, &gval, &bval); + if (i < num_trans) { + fprintf(stderr, "(r,g,b,a) = (%d,%d,%d,%d)\n", + rval, gval, bval, trans[i]); + } else { + fprintf(stderr, "(r,g,b,a) = (%d,%d,%d,<<255>>)\n", + rval, gval, bval); + } + } +#endif /* DEBUG_READ */ + + /* Extract the data and convert to RGBA */ + if (d == 1) { + /* Case 2: 1 bpp with transparency (usually) behind white */ + L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName); + if (num_trans == 1) + L_INFO("num_trans = 1; second color opaque by default\n", + procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0, index = 0; j < rowbytes; j++) { + byte = rowptr[j]; + for (k = 0; k < 8 && index < w; k++, index++) { + bitval = (byte >> (7 - k)) & 1; + pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, ppixel); + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, + bitval < num_trans ? trans[bitval] : 255); + ppixel++; + } + } + } + } else if (d == 8) { + /* Case 3: 8 bpp with cmap and associated transparency */ + L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + rowptr = row_pointers[i]; + for (j = 0; j < w; j++) { + index = rowptr[j]; + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, ppixel); + /* Assume missing entries to be 255 (opaque) + * according to the spec: + * http://www.w3.org/TR/PNG/#11tRNS */ + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, + index < num_trans ? trans[index] : 255); + ppixel++; + } + } + } else { + L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", + procName, d); + } + pixcmapDestroy(&cmap); + } + } + +#if DEBUG_READ + if (cmap) { + for (i = 0; i < 16; i++) { + fprintf(stderr, "[%d] = %d\n", i, + ((l_uint8 *)(cmap->array))[i]); + } + } +#endif /* DEBUG_READ */ + + /* Final adjustments for bpp = 1. + * + If there is no colormap, the image must be inverted because + * png stores black pixels as 0. + * + We have already handled the case of cmapped, 1 bpp pix + * with transparency, where the output pix is 32 bpp RGBA. + * If there is no transparency but the pix has a colormap, + * we remove the colormap, because functions operating on + * 1 bpp images in leptonica assume no colormap. + * + The colormap must be removed in such a way that the pixel + * values are not changed. If the values are only black and + * white, we return a 1 bpp image; if gray, return an 8 bpp pix; + * otherwise, return a 32 bpp rgb pix. + * + * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag + * to do the inversion, because that flag (since version 1.0.9) + * inverts 8 bpp grayscale as well, which we don't want to do. + * (It also doesn't work if there is a colormap.) + * + * Note that if the input png is a 1-bit with colormap and + * transparency, it has already been rendered as a 32 bpp, + * spp = 4 rgba pix. + */ + if (pixGetDepth(pix) == 1) { + if (!cmap) { + pixInvert(pix, pix); + } else { + pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + pixDestroy(&pix); + pix = pix1; + } + } + + xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); + yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); + pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ + pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ + + /* Get the text if there is any */ + png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); + if (num_text && text_ptr) + pixSetText(pix, text_ptr->text); + + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return pix; +} + + +/*---------------------------------------------------------------------* + * Writing png to memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteMemPng() + * + * \param[out] pfiledata png encoded data of pix + * \param[out] pfilesize size of png encoded data + * \param[in] pix + * \param[in] gamma use 0.0 if gamma is not defined + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStreamPng()
+ * 
+ */ +l_ok +pixWriteMemPng(l_uint8 **pfiledata, + size_t *pfilesize, + PIX *pix, + l_float32 gamma) +{ +char commentstring[] = "Comment"; +l_int32 i, j, k; +l_int32 wpl, d, spp, cmflag, opaque; +l_int32 ncolors, compval; +l_int32 *rmap, *gmap, *bmap, *amap; +l_uint32 *data, *ppixel; +png_byte bit_depth, color_type; +png_byte alpha[256]; +png_uint_32 w, h; +png_uint_32 xres, yres; +png_bytep *row_pointers; +png_bytep rowbuffer; +png_structp png_ptr; +png_infop info_ptr; +png_colorp palette; +PIX *pix1; +PIXCMAP *cmap; +char *text; +MEMIODATA state; + + PROCNAME("pixWriteMemPng"); + + if (pfiledata) *pfiledata = NULL; + if (pfilesize) *pfilesize = 0; + if (!pfiledata) + return ERROR_INT("&filedata not defined", procName, 1); + if (!pfilesize) + return ERROR_INT("&filesize not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + state.m_Buffer = 0; + state.m_Size = 0; + state.m_Next = 0; + state.m_Count = 0; + state.m_Last = &state; + + /* Allocate the 2 data structures */ + if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, NULL, NULL)) == NULL) + return ERROR_INT("png_ptr not made", procName, 1); + + if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return ERROR_INT("info_ptr not made", procName, 1); + } + + /* Set up png setjmp error handling */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return ERROR_INT("internal png error", procName, 1); + } + + png_set_write_fn(png_ptr, &state, memio_png_write_data, + (png_flush_ptr)NULL); + + /* With best zlib compression (9), get between 1 and 10% improvement + * over default (6), but the compression is 3 to 10 times slower. + * Use the zlib default (6) as our default compression unless + * pix->special falls in the range [10 ... 19]; then subtract 10 + * to get the compression value. */ + compval = Z_DEFAULT_COMPRESSION; + if (pix->special >= 10 && pix->special < 20) + compval = pix->special - 10; + png_set_compression_level(png_ptr, compval); + + w = pixGetWidth(pix); + h = pixGetHeight(pix); + d = pixGetDepth(pix); + spp = pixGetSpp(pix); + if ((cmap = pixGetColormap(pix))) + cmflag = 1; + else + cmflag = 0; + + /* Set the color type and bit depth. */ + if (d == 32 && spp == 4) { + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ + cmflag = 0; /* ignore if it exists */ + } else if (d == 24 || d == 32) { + bit_depth = 8; + color_type = PNG_COLOR_TYPE_RGB; /* 2 */ + cmflag = 0; /* ignore if it exists */ + } else { + bit_depth = d; + color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ + } + if (cmflag) + color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ + +#if DEBUG_WRITE + fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", + cmflag, bit_depth, color_type); +#endif /* DEBUG_WRITE */ + + png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + /* Store resolution in ppm, if known */ + xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); + yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); + if ((xres == 0) || (yres == 0)) + png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); + else + png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); + + if (cmflag) { + pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); + ncolors = pixcmapGetCount(cmap); + pixcmapIsOpaque(cmap, &opaque); + + /* Make and save the palette */ + palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); + for (i = 0; i < ncolors; i++) { + palette[i].red = (png_byte)rmap[i]; + palette[i].green = (png_byte)gmap[i]; + palette[i].blue = (png_byte)bmap[i]; + alpha[i] = (png_byte)amap[i]; + } + + png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); + if (!opaque) /* alpha channel has some transparency; assume valid */ + png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, + (int)ncolors, NULL); + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + LEPT_FREE(amap); + } + + /* 0.4545 is treated as the default by some image + * display programs (not gqview). A value > 0.4545 will + * lighten an image as displayed by xv, display, etc. */ + if (gamma > 0.0) + png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); + + if ((text = pixGetText(pix))) { + png_text text_chunk; + text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; + text_chunk.key = commentstring; + text_chunk.text = text; + text_chunk.text_length = strlen(text); +#ifdef PNG_ITXT_SUPPORTED + text_chunk.itxt_length = 0; + text_chunk.lang = NULL; + text_chunk.lang_key = NULL; +#endif + png_set_text(png_ptr, info_ptr, &text_chunk, 1); + } + + /* Write header and palette info */ + png_write_info(png_ptr, info_ptr); + + if ((d != 32) && (d != 24)) { /* not rgb color */ + /* Generate a temporary pix with bytes swapped. + * For writing a 1 bpp image as png: + * ~ if no colormap, invert the data, because png writes + * black as 0 + * ~ if colormapped, do not invert the data; the two RGBA + * colors can have any value. */ + if (d == 1 && !cmap) { + pix1 = pixInvert(NULL, pix); + pixEndianByteSwap(pix1); + } else { + pix1 = pixEndianByteSwapNew(pix); + } + if (!pix1) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (cmflag) LEPT_FREE(palette); + memio_free(&state); + return ERROR_INT("pix1 not made", procName, 1); + } + + /* Make and assign array of image row pointers */ + row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); + wpl = pixGetWpl(pix1); + data = pixGetData(pix1); + for (i = 0; i < h; i++) + row_pointers[i] = (png_bytep)(data + i * wpl); + png_set_rows(png_ptr, info_ptr, row_pointers); + + /* Transfer the data */ + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + + if (cmflag) LEPT_FREE(palette); + LEPT_FREE(row_pointers); + pixDestroy(&pix1); + png_destroy_write_struct(&png_ptr, &info_ptr); + + memio_png_flush(&state); + *pfiledata = (l_uint8 *)state.m_Buffer; + state.m_Buffer = 0; + *pfilesize = state.m_Count; + memio_free(&state); + return 0; + } + + /* For rgb, compose and write a row at a time */ + data = pixGetData(pix); + wpl = pixGetWpl(pix); + if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); + } + } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either + * the pix has 4 spp or writing it is requested anyway */ + rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); + for (i = 0; i < h; i++) { + ppixel = data + i * wpl; + for (j = k = 0; j < w; j++) { + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); + rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); + if (spp == 4) + rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); + ppixel++; + } + + png_write_rows(png_ptr, &rowbuffer, 1); + } + LEPT_FREE(rowbuffer); + } + + png_write_end(png_ptr, info_ptr); + + if (cmflag) + LEPT_FREE(palette); + png_destroy_write_struct(&png_ptr, &info_ptr); + + memio_png_flush(&state); + *pfiledata = (l_uint8 *)state.m_Buffer; + state.m_Buffer = 0; + *pfilesize = state.m_Count; + memio_free(&state); + return 0; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBPNG */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pngiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/pngiostub.c new file mode 100644 index 0000000..314c63b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pngiostub.c @@ -0,0 +1,143 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pngiostub.c + *
+ *
+ *     Stubs for pngio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBPNG /* defined in environ.h */ +/* --------------------------------------------*/ + +PIX * pixReadStreamPng(FILE *fp) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamPng", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderPng(const char *filename, l_int32 *pwidth, l_int32 *pheight, + l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap) +{ + return ERROR_INT("function not present", "readHeaderPng", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok freadHeaderPng(FILE *fp, l_int32 *pwidth, l_int32 *pheight, + l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap) +{ + return ERROR_INT("function not present", "freadHeaderPng", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemPng(const l_uint8 *data, size_t size, l_int32 *pwidth, + l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, + l_int32 *piscmap) +{ + return ERROR_INT("function not present", "readHeaderMemPng", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_int32 fgetPngResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres) +{ + return ERROR_INT("function not present", "fgetPngResolution", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok isPngInterlaced(const char *filename, l_int32 *pinterlaced) +{ + return ERROR_INT("function not present", "isPngInterlaced", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok fgetPngColormapInfo(FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency) +{ + return ERROR_INT("function not present", "fgetPngColormapInfo", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWritePng(const char *filename, PIX *pix, l_float32 gamma) +{ + return ERROR_INT("function not present", "pixWritePng", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) +{ + return ERROR_INT("function not present", "pixWriteStreamPng", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixSetZlibCompression(PIX *pix, l_int32 compval) + +{ + return ERROR_INT("function not present", "pixSetZlibCompression", 1); +} + +/* ----------------------------------------------------------------------*/ + +void l_pngSetReadStrip16To8(l_int32 flag) +{ + L_ERROR("function not present\n", "l_pngSetReadStrip16To8"); + return; +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadMemPng", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemPng(l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, + l_float32 gamma) +{ + return ERROR_INT("function not present", "pixWriteMemPng", 1); +} + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBPNG */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pnmio.c b/hgdriver/3rdparty/hgOCR/leptonica/pnmio.c new file mode 100644 index 0000000..8f64bb4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pnmio.c @@ -0,0 +1,1474 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pnmio.c + *
+ *
+ *      Stream interface
+ *          PIX             *pixReadStreamPnm()
+ *          l_int32          readHeaderPnm()
+ *          l_int32          freadHeaderPnm()
+ *          l_int32          pixWriteStreamPnm()
+ *          l_int32          pixWriteStreamAsciiPnm()
+ *          l_int32          pixWriteStreamPam()
+ *
+ *      Read/write to memory
+ *          PIX             *pixReadMemPnm()
+ *          l_int32          readHeaderMemPnm()
+ *          l_int32          pixWriteMemPnm()
+ *          l_int32          pixWriteMemPam()
+ *
+ *      Local helpers
+ *          static l_int32   pnmReadNextAsciiValue();
+ *          static l_int32   pnmReadNextNumber();
+ *          static l_int32   pnmReadNextString();
+ *          static l_int32   pnmSkipCommentLines();
+ *
+ *      These are here by popular demand, with the help of Mattias
+ *      Kregert (mattias@kregert.se), who provided the first implementation.
+ *
+ *      The pnm formats are exceedingly simple, because they have
+ *      no compression and no colormaps.  They support images that
+ *      are 1 bpp; 2, 4, 8 and 16 bpp grayscale; and rgb.
+ *
+ *      The original pnm formats ("ASCII") are included for completeness,
+ *      but their use is deprecated for all but tiny iconic images.
+ *      They are extremely wasteful of memory; for example, the P1 binary
+ *      ASCII format is 16 times as big as the packed uncompressed
+ *      format, because 2 characters are used to represent every bit
+ *      (pixel) in the image.  Reading is slow because we check for extra
+ *      white space and EOL at every sample value.
+ *
+ *      The packed pnm formats ("raw") give file sizes similar to
+ *      bmp files, which are uncompressed packed.  However, bmp
+ *      are more flexible, because they can support colormaps.
+ *
+ *      We don't differentiate between the different types ("pbm",
+ *      "pgm", "ppm") at the interface level, because this is really a
+ *      "distinction without a difference."  You read a file, you get
+ *      the appropriate Pix.  You write a file from a Pix, you get the
+ *      appropriate type of file.  If there is a colormap on the Pix,
+ *      and the Pix is more than 1 bpp, you get either an 8 bpp pgm
+ *      or a 24 bpp RGB pnm, depending on whether the colormap colors
+ *      are gray or rgb, respectively.
+ *
+ *      This follows the general policy that the I/O routines don't
+ *      make decisions about the content of the image -- you do that
+ *      with image processing before you write it out to file.
+ *      The I/O routines just try to make the closest connection
+ *      possible between the file and the Pix in memory.
+ *
+ *      On systems like windows without fmemopen() and open_memstream(),
+ *      we write data to a temp file and read it back for operations
+ *      between pix and compressed-data, such as pixReadMemPnm() and
+ *      pixWriteMemPnm().
+ *
+ *      The P7 format is new. It introduced a header with multiple
+ *      lines containing distinct tags for the various fields.
+ *      See: http://netpbm.sourceforge.net/doc/pam.html
+ *
+ *        WIDTH          ; mandatory, exactly once
+ *        HEIGHT         ; mandatory, exactly once
+ *        DEPTH          ; mandatory, exactly once,
+ *                            ; its meaning is equivalent to spp
+ *        MAXVAL         ; mandatory, one of 1, 3, 15, 255 or 65535
+ *        TUPLTYPE    ; optional; BLACKANDWHITE, GRAYSCALE, RGB
+ *                            ; and optional suffix _ALPHA, e.g. RGB_ALPHA
+ *        ENDHDR              ; mandatory, last header line
+ *
+ *      Reading BLACKANDWHITE_ALPHA and GRAYSCALE_ALPHA, which have a DEPTH
+ *      value of 2, is supported. The original image is converted to a Pix
+ *      with 32-bpp and alpha channel (spp == 4).
+ *
+ *      Writing P7 format is currently selected for 32-bpp with alpha
+ *      channel, i.e. for Pix which have spp == 4, using pixWriteStreamPam().
+ *      J眉rgen Buchm眉ller provided the implementation for the P7 (pam) format.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if USE_PNMIO /* defined in environ.h */ +/* --------------------------------------------*/ + +static l_int32 pnmReadNextAsciiValue(FILE *fp, l_int32 *pval); +static l_int32 pnmReadNextNumber(FILE *fp, l_int32 *pval); +static l_int32 pnmReadNextString(FILE *fp, char *buff, l_int32 size); +static l_int32 pnmSkipCommentLines(FILE *fp); + + /* a sanity check on the size read from file */ +static const l_int32 MAX_PNM_WIDTH = 100000; +static const l_int32 MAX_PNM_HEIGHT = 100000; + + +/*--------------------------------------------------------------------* + * Stream interface * + *--------------------------------------------------------------------*/ +/*! + * \brief pixReadStreamPnm() + * + * \param[in] fp file stream opened for read + * \return pix, or NULL on error + */ +PIX * +pixReadStreamPnm(FILE *fp) +{ +l_uint8 val8, rval8, gval8, bval8, aval8, mask8; +l_uint16 val16, rval16, gval16, bval16, aval16; +l_int32 w, h, d, bps, spp, bpl, wpl, i, j, type; +l_int32 val, rval, gval, bval; +l_uint32 rgbval; +l_uint32 *line, *data; +PIX *pix; + + PROCNAME("pixReadStreamPnm"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + + if (freadHeaderPnm(fp, &w, &h, &d, &type, &bps, &spp)) + return (PIX *)ERROR_PTR( "header read failed", procName, NULL); + if (bps < 1 || bps > 16) + return (PIX *)ERROR_PTR( "invalid bps", procName, NULL); + if (spp < 1 || spp > 4) + return (PIX *)ERROR_PTR( "invalid spp", procName, NULL); + if ((pix = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR( "pix not made", procName, NULL); + pixSetInputFormat(pix, IFF_PNM); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + + /* If type == 6 and bps == 16, we use the code in type 7 + * to read 6 bytes/pixel from the input file. */ + if (type == 6 && bps == 16) + type = 7; + + switch (type) { + case 1: + case 2: + /* Old "ASCII" binary or gray format */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (pnmReadNextAsciiValue(fp, &val)) + return (PIX *)ERROR_PTR( "read abend", procName, pix); + pixSetPixel(pix, j, i, val); + } + } + break; + + case 3: + /* Old "ASCII" rgb format */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (pnmReadNextAsciiValue(fp, &rval)) + return (PIX *)ERROR_PTR("read abend", procName, pix); + if (pnmReadNextAsciiValue(fp, &gval)) + return (PIX *)ERROR_PTR("read abend", procName, pix); + if (pnmReadNextAsciiValue(fp, &bval)) + return (PIX *)ERROR_PTR("read abend", procName, pix); + composeRGBPixel(rval, gval, bval, &rgbval); + pixSetPixel(pix, j, i, rgbval); + } + } + break; + + case 4: + /* "raw" format for 1 bpp */ + bpl = (d * w + 7) / 8; + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < bpl; j++) { + if (fread(&val8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error in 4", procName, pix); + SET_DATA_BYTE(line, j, val8); + } + } + break; + + case 5: + /* "raw" format for grayscale */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + if (d != 16) { + for (j = 0; j < w; j++) { + if (fread(&val8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("error in 5", procName, pix); + if (d == 2) + SET_DATA_DIBIT(line, j, val8); + else if (d == 4) + SET_DATA_QBIT(line, j, val8); + else /* d == 8 */ + SET_DATA_BYTE(line, j, val8); + } + } else { /* d == 16 */ + for (j = 0; j < w; j++) { + if (fread(&val16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("16 bpp error", procName, pix); + SET_DATA_TWO_BYTES(line, j, val16); + } + } + } + break; + + case 6: + /* "raw" format, type == 6; 8 bps, rgb */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + if (fread(&rval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 6", + procName, pix); + if (fread(&gval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 6", + procName, pix); + if (fread(&bval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 6", + procName, pix); + composeRGBPixel(rval8, gval8, bval8, &rgbval); + line[j] = rgbval; + } + } + break; + + case 7: + /* "arbitrary" format; type == 7; */ + if (bps != 16) { + mask8 = (1 << bps) - 1; + switch (spp) { + case 1: /* 1, 2, 4, 8 bpp grayscale */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (fread(&val8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + val8 = val8 & mask8; + if (bps == 1) val8 ^= 1; /* white-is-1 photometry */ + pixSetPixel(pix, j, i, val8); + } + } + break; + + case 2: /* 1, 2, 4, 8 bpp grayscale + alpha */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (fread(&val8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&aval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + val8 = val8 & mask8; + aval8 = aval8 & mask8; + composeRGBAPixel(val8, val8, val8, aval8, &rgbval); + pixSetPixel(pix, j, i, rgbval); + } + } + pixSetSpp(pix, 4); + break; + + case 3: /* rgb */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + if (fread(&rval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&gval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&bval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + rval8 = rval8 & mask8; + gval8 = gval8 & mask8; + bval8 = bval8 & mask8; + composeRGBPixel(rval8, gval8, bval8, &rgbval); + line[j] = rgbval; + } + } + break; + + case 4: /* rgba */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + if (fread(&rval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&gval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&bval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&aval8, 1, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + rval8 = rval8 & mask8; + gval8 = gval8 & mask8; + bval8 = bval8 & mask8; + aval8 = aval8 & mask8; + composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval); + line[j] = rgbval; + } + } + pixSetSpp(pix, 4); + break; + } + } else { /* bps == 16 */ + /* I have only seen one example that is type 6, 16 bps. + * It was 3 spp (rgb), and the 8 bps of real data was stored + * in the second byte. In the following, I make the wild + * assumption that for all 16 bpp pnm/pam files, we can + * take the second byte. */ + switch (spp) { + case 1: /* 16 bps grayscale */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (fread(&val16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + val8 = val16 & 0xff; + pixSetPixel(pix, j, i, val8); + } + } + break; + + case 2: /* 16 bps grayscale + alpha */ + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + if (fread(&val16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&aval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + val8 = val16 & 0xff; + aval8 = aval16 & 0xff; + composeRGBAPixel(val8, val8, val8, aval8, &rgbval); + pixSetPixel(pix, j, i, rgbval); + } + } + pixSetSpp(pix, 4); + break; + + case 3: /* 16bps rgb */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + if (fread(&rval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&gval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&bval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + rval8 = rval16 & 0xff; + gval8 = gval16 & 0xff; + bval8 = bval16 & 0xff; + composeRGBPixel(rval8, gval8, bval8, &rgbval); + line[j] = rgbval; + } + } + break; + + case 4: /* 16bps rgba */ + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + if (fread(&rval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&gval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&bval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + if (fread(&aval16, 2, 1, fp) != 1) + return (PIX *)ERROR_PTR("read error type 7", + procName, pix); + rval8 = rval16 & 0xff; + gval8 = gval16 & 0xff; + bval8 = bval16 & 0xff; + aval8 = aval16 & 0xff; + composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval); + line[j] = rgbval; + } + } + pixSetSpp(pix, 4); + break; + } + } + break; + } + return pix; +} + + +/*! + * \brief readHeaderPnm() + * + * \param[in] filename + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pd [optional] + * \param[out] ptype [optional] pnm type + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderPnm(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd, + l_int32 *ptype, + l_int32 *pbps, + l_int32 *pspp) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderPnm"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pd) *pd = 0; + if (ptype) *ptype = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderPnm() + * + * \param[in] fp file stream opened for read + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pd [optional] + * \param[out] ptype [optional] pnm type + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + */ +l_ok +freadHeaderPnm(FILE *fp, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd, + l_int32 *ptype, + l_int32 *pbps, + l_int32 *pspp) +{ +char tag[16], tupltype[32]; +l_int32 i, w, h, d, bps, spp, type; +l_int32 maxval; +l_int32 ch; + + PROCNAME("freadHeaderPnm"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pd) *pd = 0; + if (ptype) *ptype = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + + if (fscanf(fp, "P%d\n", &type) != 1) + return ERROR_INT("invalid read for type", procName, 1); + if (type < 1 || type > 7) + return ERROR_INT("invalid pnm file", procName, 1); + + if (pnmSkipCommentLines(fp)) + return ERROR_INT("no data in file", procName, 1); + + if (type == 7) { + w = h = d = bps = spp = maxval = 0; + for (i = 0; i < 10; i++) { /* limit to 10 lines of this header */ + if (pnmReadNextString(fp, tag, sizeof(tag))) + return ERROR_INT("found no next tag", procName, 1); + if (!strcmp(tag, "WIDTH")) { + if (pnmReadNextNumber(fp, &w)) + return ERROR_INT("failed reading width", procName, 1); + continue; + } + if (!strcmp(tag, "HEIGHT")) { + if (pnmReadNextNumber(fp, &h)) + return ERROR_INT("failed reading height", procName, 1); + continue; + } + if (!strcmp(tag, "DEPTH")) { + if (pnmReadNextNumber(fp, &spp)) + return ERROR_INT("failed reading depth", procName, 1); + continue; + } + if (!strcmp(tag, "MAXVAL")) { + if (pnmReadNextNumber(fp, &maxval)) + return ERROR_INT("failed reading maxval", procName, 1); + continue; + } + if (!strcmp(tag, "TUPLTYPE")) { + if (pnmReadNextString(fp, tupltype, sizeof(tupltype))) + return ERROR_INT("failed reading tuple type", procName, 1); + continue; + } + if (!strcmp(tag, "ENDHDR")) { + if ('\n' != (ch = fgetc(fp))) + return ERROR_INT("missing LF after ENDHDR", procName, 1); + break; + } + } + if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) { + L_INFO("invalid size: w = %d, h = %d\n", procName, w, h); + return 1; + } + if (maxval == 1) { + d = bps = 1; + } else if (maxval == 3) { + d = bps = 2; + } else if (maxval == 15) { + d = bps = 4; + } else if (maxval == 255) { + d = bps = 8; + } else if (maxval == 0xffff) { + d = bps = 16; + } else { + L_INFO("invalid maxval = %d\n", procName, maxval); + return 1; + } + switch (spp) { + case 1: + /* d and bps are already set */ + break; + case 2: + case 3: + case 4: + /* create a 32 bpp Pix */ + d = 32; + break; + default: + L_INFO("invalid depth = %d\n", procName, spp); + return 1; + } + } else { + + if (fscanf(fp, "%d %d\n", &w, &h) != 2) + return ERROR_INT("invalid read for w,h", procName, 1); + if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) { + L_INFO("invalid size: w = %d, h = %d\n", procName, w, h); + return 1; + } + + /* Get depth of pix. For types 2 and 5, we use the maxval. + * Important implementation note: + * - You can't use fscanf(), which throws away whitespace, + * and will discard binary data if it starts with whitespace(s). + * - You can't use fgets(), which stops at newlines, but this + * dumb format doesn't require a newline after the maxval + * number -- it just requires one whitespace character. + * - Which leaves repeated calls to fgetc, including swallowing + * the single whitespace character. */ + if (type == 1 || type == 4) { + d = 1; + spp = 1; + bps = 1; + } else if (type == 2 || type == 5) { + if (pnmReadNextNumber(fp, &maxval)) + return ERROR_INT("invalid read for maxval (2,5)", procName, 1); + if (maxval == 3) { + d = 2; + } else if (maxval == 15) { + d = 4; + } else if (maxval == 255) { + d = 8; + } else if (maxval == 0xffff) { + d = 16; + } else { + fprintf(stderr, "maxval = %d\n", maxval); + return ERROR_INT("invalid maxval", procName, 1); + } + bps = d; + spp = 1; + } else { /* type == 3 || type == 6; this is rgb */ + if (pnmReadNextNumber(fp, &maxval)) + return ERROR_INT("invalid read for maxval (3,6)", procName, 1); + if (maxval != 255 && maxval != 0xffff) { + L_ERROR("unexpected maxval = %d\n", procName, maxval); + return 1; + } + bps = (maxval == 255) ? 8 : 16; + d = 32; + spp = 3; + } + } + if (pw) *pw = w; + if (ph) *ph = h; + if (pd) *pd = d; + if (ptype) *ptype = type; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + return 0; +} + + +/*! + * \brief pixWriteStreamPnm() + * + * \param[in] fp file stream opened for write + * \param[in] pix + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This writes "raw" packed format only:
+ *          1 bpp --> pbm (P4)
+ *          2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P5)
+ *          2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P6)
+ *      (2) 24 bpp rgb are not supported in leptonica, but this will
+ *          write them out as a packed array of bytes (3 to a pixel).
+ * 
+ */ +l_ok +pixWriteStreamPnm(FILE *fp, + PIX *pix) +{ +l_uint8 val8; +l_uint8 pel[4]; +l_uint16 val16; +l_int32 h, w, d, ds, i, j, wpls, bpl, filebpl, writeerror, maxval; +l_uint32 *pword, *datas, *lines; +PIX *pixs; + + PROCNAME("pixWriteStreamPnm"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) + return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); + if (d == 32 && pixGetSpp(pix) == 4) + return pixWriteStreamPam(fp, pix); + + /* If a colormap exists, remove and convert to grayscale or rgb */ + if (pixGetColormap(pix) != NULL) + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + else + pixs = pixClone(pix); + ds = pixGetDepth(pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + writeerror = 0; + + if (ds == 1) { /* binary */ + fprintf(fp, "P4\n# Raw PBM file written by leptonica " + "(www.leptonica.com)\n%d %d\n", w, h); + + bpl = (w + 7) / 8; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < bpl; j++) { + val8 = GET_DATA_BYTE(lines, j); + fwrite(&val8, 1, 1, fp); + } + } + } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ + maxval = (1 << ds) - 1; + fprintf(fp, "P5\n# Raw PGM file written by leptonica " + "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); + + if (ds != 16) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + if (ds == 2) + val8 = GET_DATA_DIBIT(lines, j); + else if (ds == 4) + val8 = GET_DATA_QBIT(lines, j); + else /* ds == 8 */ + val8 = GET_DATA_BYTE(lines, j); + fwrite(&val8, 1, 1, fp); + } + } + } else { /* ds == 16 */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val16 = GET_DATA_TWO_BYTES(lines, j); + fwrite(&val16, 2, 1, fp); + } + } + } + } else { /* rgb color */ + fprintf(fp, "P6\n# Raw PPM file written by leptonica " + "(www.leptonica.com)\n%d %d\n255\n", w, h); + + if (d == 24) { /* packed, 3 bytes to a pixel */ + filebpl = 3 * w; + for (i = 0; i < h; i++) { /* write out each raster line */ + lines = datas + i * wpls; + if (fwrite(lines, 1, filebpl, fp) != filebpl) + writeerror = 1; + } + } else { /* 32 bpp rgb */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + pword = lines + j; + pel[0] = GET_DATA_BYTE(pword, COLOR_RED); + pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); + pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); + if (fwrite(pel, 1, 3, fp) != 3) + writeerror = 1; + } + } + } + } + + pixDestroy(&pixs); + if (writeerror) + return ERROR_INT("image write fail", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteStreamAsciiPnm() + * + * \param[in] fp file stream opened for write + * \param[in] pix + * \return 0 if OK; 1 on error + * + * Writes "ASCII" format only: + * 1 bpp --> pbm P1 + * 2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm P2 + * 2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm P3 + */ +l_ok +pixWriteStreamAsciiPnm(FILE *fp, + PIX *pix) +{ +char buffer[256]; +l_uint8 cval[3]; +l_int32 h, w, d, ds, i, j, k, maxval, count; +l_uint32 val; +PIX *pixs; + + PROCNAME("pixWriteStreamAsciiPnm"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return ERROR_INT("d not in {1,2,4,8,16,32}", procName, 1); + + /* If a colormap exists, remove and convert to grayscale or rgb */ + if (pixGetColormap(pix) != NULL) + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + else + pixs = pixClone(pix); + ds = pixGetDepth(pixs); + + if (ds == 1) { /* binary */ + fprintf(fp, "P1\n# Ascii PBM file written by leptonica " + "(www.leptonica.com)\n%d %d\n", w, h); + + count = 0; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pixs, j, i, &val); + if (val == 0) + fputc('0', fp); + else /* val == 1 */ + fputc('1', fp); + fputc(' ', fp); + count += 2; + if (count >= 70) { + fputc('\n', fp); + count = 0; + } + } + } + } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ + maxval = (1 << ds) - 1; + fprintf(fp, "P2\n# Ascii PGM file written by leptonica " + "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); + + count = 0; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pixs, j, i, &val); + if (ds == 2) { + snprintf(buffer, sizeof(buffer), "%1d ", val); + fwrite(buffer, 1, 2, fp); + count += 2; + } else if (ds == 4) { + snprintf(buffer, sizeof(buffer), "%2d ", val); + fwrite(buffer, 1, 3, fp); + count += 3; + } else if (ds == 8) { + snprintf(buffer, sizeof(buffer), "%3d ", val); + fwrite(buffer, 1, 4, fp); + count += 4; + } else { /* ds == 16 */ + snprintf(buffer, sizeof(buffer), "%5d ", val); + fwrite(buffer, 1, 6, fp); + count += 6; + } + if (count >= 60) { + fputc('\n', fp); + count = 0; + } + } + } + } else { /* rgb color */ + fprintf(fp, "P3\n# Ascii PPM file written by leptonica " + "(www.leptonica.com)\n%d %d\n255\n", w, h); + count = 0; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pixs, j, i, &val); + cval[0] = GET_DATA_BYTE(&val, COLOR_RED); + cval[1] = GET_DATA_BYTE(&val, COLOR_GREEN); + cval[2] = GET_DATA_BYTE(&val, COLOR_BLUE); + for (k = 0; k < 3; k++) { + snprintf(buffer, sizeof(buffer), "%3d ", cval[k]); + fwrite(buffer, 1, 4, fp); + count += 4; + if (count >= 60) { + fputc('\n', fp); + count = 0; + } + } + } + } + } + + pixDestroy(&pixs); + return 0; +} + + +/*! + * \brief pixWriteStreamPam() + * + * \param[in] fp file stream opened for write + * \param[in] pix + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This writes arbitrary PAM (P7) packed format.
+ *      (2) 24 bpp rgb are not supported in leptonica, but this will
+ *          write them out as a packed array of bytes (3 to a pixel).
+ * 
+ */ +l_ok +pixWriteStreamPam(FILE *fp, + PIX *pix) +{ +l_uint8 val8; +l_uint8 pel[8]; +l_uint16 val16; +l_int32 h, w, d, ds, i, j; +l_int32 wpls, spps, filebpl, writeerror, maxval; +l_uint32 *pword, *datas, *lines; +PIX *pixs; + + PROCNAME("pixWriteStreamPam"); + + if (!fp) + return ERROR_INT("fp not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) + return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); + + /* If a colormap exists, remove and convert to grayscale or rgb */ + if (pixGetColormap(pix) != NULL) + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + else + pixs = pixClone(pix); + ds = pixGetDepth(pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + spps = pixGetSpp(pixs); + if (ds < 24) + maxval = (1 << ds) - 1; + else + maxval = 255; + + writeerror = 0; + fprintf(fp, "P7\n# Arbitrary PAM file written by leptonica " + "(www.leptonica.com)\n"); + fprintf(fp, "WIDTH %d\n", w); + fprintf(fp, "HEIGHT %d\n", h); + fprintf(fp, "DEPTH %d\n", spps); + fprintf(fp, "MAXVAL %d\n", maxval); + if (spps == 1 && ds == 1) + fprintf(fp, "TUPLTYPE BLACKANDWHITE\n"); + else if (spps == 1) + fprintf(fp, "TUPLTYPE GRAYSCALE\n"); + else if (spps == 3) + fprintf(fp, "TUPLTYPE RGB\n"); + else if (spps == 4) + fprintf(fp, "TUPLTYPE RGB_ALPHA\n"); + fprintf(fp, "ENDHDR\n"); + + switch (d) { + case 1: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val8 = GET_DATA_BIT(lines, j); + val8 ^= 1; /* pam apparently uses white-is-1 photometry */ + if (fwrite(&val8, 1, 1, fp) != 1) + writeerror = 1; + } + } + break; + + case 2: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val8 = GET_DATA_DIBIT(lines, j); + if (fwrite(&val8, 1, 1, fp) != 1) + writeerror = 1; + } + } + break; + + case 4: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val8 = GET_DATA_QBIT(lines, j); + if (fwrite(&val8, 1, 1, fp) != 1) + writeerror = 1; + } + } + break; + + case 8: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val8 = GET_DATA_BYTE(lines, j); + if (fwrite(&val8, 1, 1, fp) != 1) + writeerror = 1; + } + } + break; + + case 16: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val16 = GET_DATA_TWO_BYTES(lines, j); + if (fwrite(&val16, 2, 1, fp) != 1) + writeerror = 1; + } + } + break; + + case 24: + filebpl = 3 * w; + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + if (fwrite(lines, 1, filebpl, fp) != filebpl) + writeerror = 1; + } + break; + + case 32: + switch (spps) { + case 3: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + pword = lines + j; + pel[0] = GET_DATA_BYTE(pword, COLOR_RED); + pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); + pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); + if (fwrite(pel, 1, 3, fp) != 3) + writeerror = 1; + } + } + break; + case 4: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < wpls; j++) { + pword = lines + j; + pel[0] = GET_DATA_BYTE(pword, COLOR_RED); + pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); + pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); + pel[3] = GET_DATA_BYTE(pword, L_ALPHA_CHANNEL); + if (fwrite(pel, 1, 4, fp) != 4) + writeerror = 1; + } + } + break; + } + break; + } + + pixDestroy(&pixs); + if (writeerror) + return ERROR_INT("image write fail", procName, 1); + return 0; +} + + +/*---------------------------------------------------------------------* + * Read/write to memory * + *---------------------------------------------------------------------*/ + +/*! + * \brief pixReadMemPnm() + * + * \param[in] data const; pnm-encoded + * \param[in] size of data + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) The %size byte of %data must be a null character.
+ * 
+ */ +PIX * +pixReadMemPnm(const l_uint8 *data, + size_t size) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadMemPnm"); + + if (!data) + return (PIX *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PIX *)ERROR_PTR("stream not opened", procName, NULL); + pix = pixReadStreamPnm(fp); + fclose(fp); + if (!pix) L_ERROR("pix not read\n", procName); + return pix; +} + + +/*! + * \brief readHeaderMemPnm() + * + * \param[in] data const; pnm-encoded + * \param[in] size of data + * \param[out] pw [optional] + * \param[out] ph [optional] + * \param[out] pd [optional] + * \param[out] ptype [optional] pnm type + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderMemPnm(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pd, + l_int32 *ptype, + l_int32 *pbps, + l_int32 *pspp) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderMemPnm"); + + if (!data) + return ERROR_INT("data not defined", procName, 1); + + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp); + fclose(fp); + if (ret) + return ERROR_INT("header data read failed", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteMemPnm() + * + * \param[out] pdata data of PNM image + * \param[out] psize size of returned data + * \param[in] pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStreamPnm() for usage.  This version writes to
+ *          memory instead of to a file stream.
+ * 
+ */ +l_ok +pixWriteMemPnm(l_uint8 **pdata, + size_t *psize, + PIX *pix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixWriteMemPnm"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixWriteStreamPnm(fp, pix); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = pixWriteStreamPnm(fp, pix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief pixWriteMemPam() + * + * \param[out] pdata data of PAM image + * \param[out] psize size of returned data + * \param[in] pix + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStreamPnm() for usage.  This version writes to
+ *          memory instead of to a file stream.
+ * 
+ */ +l_ok +pixWriteMemPam(l_uint8 **pdata, + size_t *psize, + PIX *pix) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixWriteMemPam"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixWriteStreamPam(fp, pix); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = pixWriteStreamPam(fp, pix); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + + +/*--------------------------------------------------------------------* + * Static helpers * + *--------------------------------------------------------------------*/ +/*! + * \brief pnmReadNextAsciiValue() + * + * Return: 0 if OK, 1 on error or EOF. + * + * Notes: + * (1) This reads the next sample value in ASCII from the file. + */ +static l_int32 +pnmReadNextAsciiValue(FILE *fp, + l_int32 *pval) +{ +l_int32 c, ignore; + + PROCNAME("pnmReadNextAsciiValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!fp) + return ERROR_INT("stream not open", procName, 1); + do { /* skip whitespace */ + if ((c = fgetc(fp)) == EOF) + return 1; + } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + fseek(fp, -1L, SEEK_CUR); /* back up one byte */ + ignore = fscanf(fp, "%d", pval); + return 0; +} + + +/*! + * \brief pnmReadNextNumber() + * + * \param[in] fp file stream + * \param[out] pval value as an integer + * \return 0 if OK, 1 on error or EOF. + * + *
+ * Notes:
+ *      (1) This reads the next set of numeric chars, returning
+ *          the value and swallowing the trailing whitespace character.
+ *          This is needed to read the maxval in the header, which
+ *          precedes the binary data.
+ * 
+ */ +static l_int32 +pnmReadNextNumber(FILE *fp, + l_int32 *pval) +{ +char buf[8]; +l_int32 i, c, foundws; + + PROCNAME("pnmReadNextNumber"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!fp) + return ERROR_INT("stream not open", procName, 1); + + /* The ASCII characters for the number are followed by exactly + * one whitespace character. */ + foundws = FALSE; + for (i = 0; i < 8; i++) + buf[i] = '\0'; + for (i = 0; i < 8; i++) { + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + foundws = TRUE; + buf[i] = '\n'; + break; + } + if (!isdigit(c)) + return ERROR_INT("char read is not a digit", procName, 1); + buf[i] = c; + } + if (!foundws) + return ERROR_INT("no whitespace found", procName, 1); + if (sscanf(buf, "%d", pval) != 1) + return ERROR_INT("invalid read", procName, 1); + return 0; +} + +/*! + * \brief pnmReadNextString() + * + * \param[in] fp file stream + * \param[out] buff pointer to the string buffer + * \param[in] size max. number of charactes in buffer + * \return 0 if OK, 1 on error or EOF. + * + *
+ * Notes:
+ *      (1) This reads the next set of alphanumeric chars,
+ *          returning the string and swallowing the trailing
+ *          whitespace characters.
+ *          This is needed to read header lines, which precede
+ *          the P7 format binary data.
+ * 
+ */ +static l_int32 +pnmReadNextString(FILE *fp, + char *buff, + l_int32 size) +{ +l_int32 i, c; + + PROCNAME("pnmReadNextString"); + + if (!buff) + return ERROR_INT("buff not defined", procName, 1); + *buff = '\0'; + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (size <= 0) + return ERROR_INT("size is too small", procName, 1); + + do { /* skip whitespace */ + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + /* Comment lines are allowed to appear + * anywhere in the header lines */ + if (c == '#') { + do { /* each line starting with '#' */ + do { /* this entire line */ + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + } while (c != '\n'); + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + } while (c == '#'); + } + + /* The next string ends when there is + * a whitespace character following. */ + for (i = 0; i < size - 1; i++) { + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + buff[i] = c; + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + } + buff[i] = '\0'; + + /* Back up one byte */ + fseek(fp, -1L, SEEK_CUR); + if (i >= size - 1) + return ERROR_INT("buff size too small", procName, 1); + + /* Skip over trailing spaces and tabs */ + for (;;) { + if ((c = fgetc(fp)) == EOF) + return ERROR_INT("end of file reached", procName, 1); + if (c != ' ' && c != '\t') + break; + } + + /* Back up one byte */ + fseek(fp, -1L, SEEK_CUR); + return 0; +} + + +/*! + * \brief pnmSkipCommentLines() + * + * Return: 0 if OK, 1 on error or EOF + * + * Notes: + * (1) Comment lines begin with '#' + * (2) Usage: caller should check return value for EOF + */ +static l_int32 +pnmSkipCommentLines(FILE *fp) +{ +l_int32 c; + + PROCNAME("pnmSkipCommentLines"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if ((c = fgetc(fp)) == EOF) + return 1; + if (c == '#') { + do { /* each line starting with '#' */ + do { /* this entire line */ + if ((c = fgetc(fp)) == EOF) + return 1; + } while (c != '\n'); + if ((c = fgetc(fp)) == EOF) + return 1; + } while (c == '#'); + } + + /* Back up one byte */ + fseek(fp, -1L, SEEK_CUR); + return 0; +} + +/* --------------------------------------------*/ +#endif /* USE_PNMIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/pnmiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/pnmiostub.c new file mode 100644 index 0000000..5b471ee --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/pnmiostub.c @@ -0,0 +1,116 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file pnmiostub.c + *
+ *
+ *     Stubs for pnmio.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_PNMIO /* defined in environ.h */ +/* --------------------------------------------*/ + +PIX * pixReadStreamPnm(FILE *fp) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamPnm", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderPnm(const char *filename, l_int32 *pw, l_int32 *ph, + l_int32 *pd, l_int32 *ptype, l_int32 *pbps, + l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderPnm", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok freadHeaderPnm(FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pd, + l_int32 *ptype, l_int32 *pbps, l_int32 *pspp) +{ + return ERROR_INT("function not present", "freadHeaderPnm", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamPnm(FILE *fp, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteStreamPnm", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamAsciiPnm(FILE *fp, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteStreamAsciiPnm", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamPam(FILE *fp, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteStreamPam", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemPnm(const l_uint8 *cdata, size_t size) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadMemPnm", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemPnm(const l_uint8 *cdata, size_t size, l_int32 *pw, + l_int32 *ph, l_int32 *pd, l_int32 *ptype, + l_int32 *pbps, l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderMemPnm", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemPnm(l_uint8 **pdata, size_t *psize, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteMemPnm", 1); +} +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemPam(l_uint8 **pdata, size_t *psize, PIX *pix) +{ + return ERROR_INT("function not present", "pixWriteMemPam", 1); +} + + +/* --------------------------------------------*/ +#endif /* !USE_PNMIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/projective.c b/hgdriver/3rdparty/hgOCR/leptonica/projective.c new file mode 100644 index 0000000..b8393b4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/projective.c @@ -0,0 +1,923 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file projective.c + *
+ *
+ *      Projective (4 pt) image transformation using a sampled
+ *      (to nearest integer) transform on each dest point
+ *           PIX      *pixProjectiveSampledPta()
+ *           PIX      *pixProjectiveSampled()
+ *
+ *      Projective (4 pt) image transformation using interpolation
+ *      (or area mapping) for anti-aliasing images that are
+ *      2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB
+ *           PIX      *pixProjectivePta()
+ *           PIX      *pixProjective()
+ *           PIX      *pixProjectivePtaColor()
+ *           PIX      *pixProjectiveColor()
+ *           PIX      *pixProjectivePtaGray()
+ *           PIX      *pixProjectiveGray()
+ *
+ *      Projective transform including alpha (blend) component
+ *           PIX      *pixProjectivePtaWithAlpha()
+ *
+ *      Projective coordinate transformation
+ *           l_int32   getProjectiveXformCoeffs()
+ *           l_int32   projectiveXformSampledPt()
+ *           l_int32   projectiveXformPt()
+ *
+ *      A projective transform can be specified as a specific functional
+ *      mapping between 4 points in the source and 4 points in the dest.
+ *      It preserves straight lines, but is less stable than a bilinear
+ *      transform, because it contains a division by a quantity that
+ *      can get arbitrarily small.)
+ *
+ *      We give both a projective coordinate transformation and
+ *      two projective image transformations.
+ *
+ *      For the former, we ask for the coordinate value (x',y')
+ *      in the transformed space for any point (x,y) in the original
+ *      space.  The coefficients of the transformation are found by
+ *      solving 8 simultaneous equations for the 8 coordinates of
+ *      the 4 points in src and dest.  The transformation can then
+ *      be used to compute the associated image transform, by
+ *      computing, for each dest pixel, the relevant pixel(s) in
+ *      the source.  This can be done either by taking the closest
+ *      src pixel to each transformed dest pixel ("sampling") or
+ *      by doing an interpolation and averaging over 4 source
+ *      pixels with appropriate weightings ("interpolated").
+ *
+ *      A typical application would be to remove keystoning
+ *      due to a projective transform in the imaging system.
+ *
+ *      The projective transform is given by specifying two equations:
+ *
+ *          x' = (ax + by + c) / (gx + hy + 1)
+ *          y' = (dx + ey + f) / (gx + hy + 1)
+ *
+ *      where the eight coefficients have been computed from four
+ *      sets of these equations, each for two corresponding data pts.
+ *      In practice, once the coefficients are known, we use the
+ *      equations "backwards": for each point (x,y) in the dest image,
+ *      these two equations are used to compute the corresponding point
+ *      (x',y') in the src.  That computed point in the src is then used
+ *      to determine the corresponding dest pixel value in one of two ways:
+ *
+ *       ~ sampling: simply take the value of the src pixel in which this
+ *                   point falls
+ *       ~ interpolation: take appropriate linear combinations of the
+ *                        four src pixels that this dest pixel would
+ *                        overlap, with the coefficients proportional
+ *                        to the amount of overlap
+ *
+ *      For small warp where there is little scale change, (e.g.,
+ *      for rotation) area mapping is nearly equivalent to interpolation.
+ *
+ *      Typical relative timing of pointwise transforms (sampled = 1.0):
+ *      8 bpp:   sampled        1.0
+ *               interpolated   1.5
+ *      32 bpp:  sampled        1.0
+ *               interpolated   1.6
+ *      Additionally, the computation time/pixel is nearly the same
+ *      for 8 bpp and 32 bpp, for both sampled and interpolated.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +extern l_float32 AlphaMaskBorderVals[2]; + + +/*------------------------------------------------------------n + * Sampled projective image transformation * + *-------------------------------------------------------------*/ +/*! + * \brief pixProjectiveSampledPta() + * + * \param[in] pixs all depths + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) No 3 of the 4 points may be collinear.
+ *      (4) For 8 and 32 bpp pix, better quality is obtained by the
+ *          somewhat slower pixProjectivePta().  See that
+ *          function for relative timings between sampled and interpolated.
+ * 
+ */ +PIX * +pixProjectiveSampledPta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixProjectiveSampledPta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getProjectiveXformCoeffs(ptad, ptas, &vc); + pixd = pixProjectiveSampled(pixs, vc, incolor); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixProjectiveSampled() + * + * \param[in] pixs all depths + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary.
+ *      (2) Retains colormap, which you can do for a sampled transform..
+ *      (3) For 8 or 32 bpp, much better quality is obtained by the
+ *          somewhat slower pixProjective().  See that function
+ *          for relative timings between sampled and interpolated.
+ * 
+ */ +PIX * +pixProjectiveSampled(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; +l_uint32 val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; +PIXCMAP *cmap; + + PROCNAME("pixProjectiveSampled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); + + /* Init all dest pixels to color to be brought in from outside */ + pixd = pixCreateTemplate(pixs); + if ((cmap = pixGetColormap(pixs)) != NULL) { + if (incolor == L_BRING_IN_WHITE) + color = 1; + else + color = 0; + pixcmapAddBlackOrWhite(cmap, color, &cmapindex); + pixSetAllArbitrary(pixd, cmapindex); + } else { + if ((d == 1 && incolor == L_BRING_IN_WHITE) || + (d > 1 && incolor == L_BRING_IN_BLACK)) { + pixClearAll(pixd); + } else { + pixSetAll(pixd); + } + } + + /* Scan over the dest pixels */ + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + projectiveXformSampledPt(vc, j, i, &x, &y); + if (x < 0 || y < 0 || x >=w || y >= h) + continue; + lines = datas + y * wpls; + if (d == 1) { + val = GET_DATA_BIT(lines, x); + SET_DATA_BIT_VAL(lined, j, val); + } else if (d == 8) { + val = GET_DATA_BYTE(lines, x); + SET_DATA_BYTE(lined, j, val); + } else if (d == 32) { + lined[j] = lines[x]; + } else if (d == 2) { + val = GET_DATA_DIBIT(lines, x); + SET_DATA_DIBIT(lined, j, val); + } else if (d == 4) { + val = GET_DATA_QBIT(lines, x); + SET_DATA_QBIT(lined, j, val); + } + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------* + * Interpolated projective image transformation * + *---------------------------------------------------------------------*/ +/*! + * \brief pixProjectivePta() + * + * \param[in] pixs all depths; colormap ok + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixProjectivePta(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixProjectivePta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixProjectiveSampledPta(pixs, ptad, ptas, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixProjectivePtaGray(pixt2, ptad, ptas, colorval); + else /* d == 32 */ + pixd = pixProjectivePtaColor(pixt2, ptad, ptas, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixProjective() + * + * \param[in] pixs all depths; colormap ok + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Brings in either black or white pixels from the boundary
+ *      (2) Removes any existing colormap, if necessary, before transforming
+ * 
+ */ +PIX * +pixProjective(PIX *pixs, + l_float32 *vc, + l_int32 incolor) +{ +l_int32 d; +l_uint32 colorval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixProjective"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + if (pixGetDepth(pixs) == 1) + return pixProjectiveSampled(pixs, vc, incolor); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual color to bring in from edges */ + colorval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + colorval = 255; + else /* d == 32 */ + colorval = 0xffffff00; + } + + if (d == 8) + pixd = pixProjectiveGray(pixt2, vc, colorval); + else /* d == 32 */ + pixd = pixProjectiveColor(pixt2, vc, colorval); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixProjectivePtaColor() + * + * \param[in] pixs 32 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixProjectivePtaColor(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint32 colorval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixProjectivePtaColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getProjectiveXformCoeffs(ptad, ptas, &vc); + pixd = pixProjectiveColor(pixs, vc, colorval); + LEPT_FREE(vc); + + return pixd; +} + + +/*! + * \brief pixProjectiveColor() + * + * \param[in] pixs 32 bpp + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixProjectiveColor(PIX *pixs, + l_float32 *vc, + l_uint32 colorval) +{ +l_int32 i, j, w, h, d, wpls, wpld; +l_uint32 val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixProjectiveColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, colorval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + projectiveXformPt(vc, j, i, &x, &y); + linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, + &val); + *(lined + j) = val; + } + } + + /* If rgba, transform the pixs alpha channel and insert in pixd */ + if (pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixProjectiveGray(pix1, vc, 255); /* bring in opaque */ + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return pixd; +} + + +/*! + * \brief pixProjectivePtaGray() + * + * \param[in] pixs 8 bpp + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] grayval 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixProjectivePtaGray(PIX *pixs, + PTA *ptad, + PTA *ptas, + l_uint8 grayval) +{ +l_float32 *vc; +PIX *pixd; + + PROCNAME("pixProjectivePtaGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (ptaGetCount(ptas) != 4) + return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); + if (ptaGetCount(ptad) != 4) + return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); + + /* Get backwards transform from dest to src, and apply it */ + getProjectiveXformCoeffs(ptad, ptas, &vc); + pixd = pixProjectiveGray(pixs, vc, grayval); + LEPT_FREE(vc); + + return pixd; +} + + + +/*! + * \brief pixProjectiveGray() + * + * \param[in] pixs 8 bpp + * \param[in] vc vector of 8 coefficients for projective transform + * \param[in] grayval 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + */ +PIX * +pixProjectiveGray(PIX *pixs, + l_float32 *vc, + l_uint8 grayval) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +PIX *pixd; + + PROCNAME("pixProjectiveGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + if (!vc) + return (PIX *)ERROR_PTR("vc not defined", procName, NULL); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + pixSetAllArbitrary(pixd, grayval); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Iterate over destination pixels */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + /* Compute float src pixel location corresponding to (i,j) */ + projectiveXformPt(vc, j, i, &x, &y); + linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*---------------------------------------------------------------------------* + * Projective transform including alpha (blend) component * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixProjectivePtaWithAlpha() + * + * \param[in] pixs 32 bpp rgb + * \param[in] ptad 4 pts of final coordinate space + * \param[in] ptas 4 pts of initial coordinate space + * \param[in] pixg [optional] 8 bpp, for alpha channel, can be null + * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent + * and 1.0 fully opaque + * \param[in] border of pixels added to capture transformed source pixels + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The alpha channel is transformed separately from pixs,
+ *          and aligns with it, being fully transparent outside the
+ *          boundary of the transformed pixs.  For pixels that are fully
+ *          transparent, a blending function like pixBlendWithGrayMask()
+ *          will give zero weight to corresponding pixels in pixs.
+ *      (2) If pixg is NULL, it is generated as an alpha layer that is
+ *          partially opaque, using %fract.  Otherwise, it is cropped
+ *          to pixs if required and %fract is ignored.  The alpha channel
+ *          in pixs is never used.
+ *      (3) Colormaps are removed.
+ *      (4) When pixs is transformed, it doesn't matter what color is brought
+ *          in because the alpha channel will be transparent (0) there.
+ *      (5) To avoid losing source pixels in the destination, it may be
+ *          necessary to add a border to the source pix before doing
+ *          the projective transformation.  This can be any non-negative
+ *          number.
+ *      (6) The input %ptad and %ptas are in a coordinate space before
+ *          the border is added.  Internally, we compensate for this
+ *          before doing the projective transform on the image after
+ *          the border is added.
+ *      (7) The default setting for the border values in the alpha channel
+ *          is 0 (transparent) for the outermost ring of pixels and
+ *          (0.5 * fract * 255) for the second ring.  When blended over
+ *          a second image, this
+ *          (a) shrinks the visible image to make a clean overlap edge
+ *              with an image below, and
+ *          (b) softens the edges by weakening the aliasing there.
+ *          Use l_setAlphaMaskBorder() to change these values.
+ * 
+ */ +PIX * +pixProjectivePtaWithAlpha(PIX *pixs, + PTA *ptad, + PTA *ptas, + PIX *pixg, + l_float32 fract, + l_int32 border) +{ +l_int32 ws, hs, d; +PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; +PTA *ptad2, *ptas2; + + PROCNAME("pixProjectivePtaWithAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (d != 32 && pixGetColormap(pixs) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (pixg && pixGetDepth(pixg) != 8) { + L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", + procName); + pixg = NULL; + } + if (!pixg && (fract < 0.0 || fract > 1.0)) { + L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); + fract = 1.0; + } + if (!pixg && fract == 0.0) + L_WARNING("fully opaque alpha; image will not be blended\n", procName); + if (!ptad) + return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); + if (!ptas) + return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); + + /* Add border; the color doesn't matter */ + pixb1 = pixAddBorder(pixs, border, 0); + + /* Transform the ptr arrays to work on the bordered image */ + ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); + ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); + + /* Do separate projective transform of rgb channels of pixs + * and of pixg */ + pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); + if (!pixg) { + pixg2 = pixCreate(ws, hs, 8); + if (fract == 1.0) + pixSetAll(pixg2); + else + pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); + } else { + pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); + } + if (ws > 10 && hs > 10) { /* see note 7 */ + pixSetBorderRingVal(pixg2, 1, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); + pixSetBorderRingVal(pixg2, 2, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); + + } + pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ + pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); + pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); + pixSetSpp(pixd, 4); + + pixDestroy(&pixg2); + pixDestroy(&pixb1); + pixDestroy(&pixb2); + pixDestroy(&pixga); + ptaDestroy(&ptad2); + ptaDestroy(&ptas2); + return pixd; +} + + +/*-------------------------------------------------------------* + * Projective coordinate transformation * + *-------------------------------------------------------------*/ +/*! + * \brief getProjectiveXformCoeffs() + * + * \param[in] ptas source 4 points; unprimed + * \param[in] ptad transformed 4 points; primed + * \param[out] pvc vector of coefficients of transform + * \return 0 if OK; 1 on error + * + * We have a set of 8 equations, describing the projective + * transformation that takes 4 points ptas into 4 other + * points ptad. These equations are: + * + * x1' = c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1 + * y1' = c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1 + * x2' = c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1 + * y2' = c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1 + * x3' = c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1 + * y3' = c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1 + * x4' = c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1 + * y4' = c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1 + * + * Multiplying both sides of each eqn by the denominator, we get + * + * AC = B + * + * where B and C are column vectors + * + * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ] + * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ] + * + * and A is the 8x8 matrix + * + * x1 y1 1 0 0 0 -x1*x1' -y1*x1' + * 0 0 0 x1 y1 1 -x1*y1' -y1*y1' + * x2 y2 1 0 0 0 -x2*x2' -y2*x2' + * 0 0 0 x2 y2 1 -x2*y2' -y2*y2' + * x3 y3 1 0 0 0 -x3*x3' -y3*x3' + * 0 0 0 x3 y3 1 -x3*y3' -y3*y3' + * x4 y4 1 0 0 0 -x4*x4' -y4*x4' + * 0 0 0 x4 y4 1 -x4*y4' -y4*y4' + * + * These eight equations are solved here for the coefficients C. + * + * These eight coefficients can then be used to find the mapping + * x,y) --> (x',y': + * + * x' = c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1 + * y' = c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1 + * + * that is implemented in projectiveXformSampled and + * projectiveXFormInterpolated. + */ +l_ok +getProjectiveXformCoeffs(PTA *ptas, + PTA *ptad, + l_float32 **pvc) +{ +l_int32 i; +l_float32 x1, y1, x2, y2, x3, y3, x4, y4; +l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ +l_float32 *a[8]; /* 8x8 matrix A */ + + PROCNAME("getProjectiveXformCoeffs"); + + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (!ptad) + return ERROR_INT("ptad not defined", procName, 1); + if (!pvc) + return ERROR_INT("&vc not defined", procName, 1); + + b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); + *pvc = b; + ptaGetPt(ptas, 0, &x1, &y1); + ptaGetPt(ptas, 1, &x2, &y2); + ptaGetPt(ptas, 2, &x3, &y3); + ptaGetPt(ptas, 3, &x4, &y4); + ptaGetPt(ptad, 0, &b[0], &b[1]); + ptaGetPt(ptad, 1, &b[2], &b[3]); + ptaGetPt(ptad, 2, &b[4], &b[5]); + ptaGetPt(ptad, 3, &b[6], &b[7]); + + for (i = 0; i < 8; i++) + a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); + a[0][0] = x1; + a[0][1] = y1; + a[0][2] = 1.; + a[0][6] = -x1 * b[0]; + a[0][7] = -y1 * b[0]; + a[1][3] = x1; + a[1][4] = y1; + a[1][5] = 1; + a[1][6] = -x1 * b[1]; + a[1][7] = -y1 * b[1]; + a[2][0] = x2; + a[2][1] = y2; + a[2][2] = 1.; + a[2][6] = -x2 * b[2]; + a[2][7] = -y2 * b[2]; + a[3][3] = x2; + a[3][4] = y2; + a[3][5] = 1; + a[3][6] = -x2 * b[3]; + a[3][7] = -y2 * b[3]; + a[4][0] = x3; + a[4][1] = y3; + a[4][2] = 1.; + a[4][6] = -x3 * b[4]; + a[4][7] = -y3 * b[4]; + a[5][3] = x3; + a[5][4] = y3; + a[5][5] = 1; + a[5][6] = -x3 * b[5]; + a[5][7] = -y3 * b[5]; + a[6][0] = x4; + a[6][1] = y4; + a[6][2] = 1.; + a[6][6] = -x4 * b[6]; + a[6][7] = -y4 * b[6]; + a[7][3] = x4; + a[7][4] = y4; + a[7][5] = 1; + a[7][6] = -x4 * b[7]; + a[7][7] = -y4 * b[7]; + + gaussjordan(a, b, 8); + + for (i = 0; i < 8; i++) + LEPT_FREE(a[i]); + + return 0; +} + + +/*! + * \brief projectiveXformSampledPt() + * + * \param[in] vc vector of 8 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds the nearest pixel coordinates of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +projectiveXformSampledPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_int32 *pxp, + l_int32 *pyp) +{ +l_float32 factor; + + PROCNAME("projectiveXformSampledPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + factor = 1. / (vc[6] * x + vc[7] * y + 1.); + *pxp = (l_int32)(factor * (vc[0] * x + vc[1] * y + vc[2]) + 0.5); + *pyp = (l_int32)(factor * (vc[3] * x + vc[4] * y + vc[5]) + 0.5); + return 0; +} + + +/*! + * \brief projectiveXformPt() + * + * \param[in] vc vector of 8 coefficients + * \param[in] x, y initial point + * \param[out] pxp, pyp transformed point + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This computes the floating point location of the transformed point.
+ *      (2) It does not check ptrs for returned data!
+ * 
+ */ +l_ok +projectiveXformPt(l_float32 *vc, + l_int32 x, + l_int32 y, + l_float32 *pxp, + l_float32 *pyp) +{ +l_float32 factor; + + PROCNAME("projectiveXformPt"); + + if (!vc) + return ERROR_INT("vc not defined", procName, 1); + + factor = 1. / (vc[6] * x + vc[7] * y + 1.); + *pxp = factor * (vc[0] * x + vc[1] * y + vc[2]); + *pyp = factor * (vc[3] * x + vc[4] * y + vc[5]); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/psio1.c b/hgdriver/3rdparty/hgOCR/leptonica/psio1.c new file mode 100644 index 0000000..368a2b8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/psio1.c @@ -0,0 +1,1073 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file psio1.c + *
+ *
+ *    |=============================================================|
+ *    |                         Important note                      |
+ *    |=============================================================|
+ *    | Some of these functions require I/O libraries such as       |
+ *    | libtiff, libjpeg, and libz.  If you do not have these       |
+ *    | libraries, some calls will fail.                            |
+ *    |                                                             |
+ *    | You can manually deactivate all PostScript writing by       |
+ *    | setting this in environ.h:                                  |
+ *    | \code                                                       |
+ *    |     #define  USE_PSIO     0                                 |
+ *    | \endcode                                                    |
+ *    | in environ.h.  This will link psio1stub.c                   |
+ *    |=============================================================|
+ *
+ *     This is a PostScript "device driver" for wrapping images
+ *     in PostScript.  The images can be rendered by a PostScript
+ *     interpreter for viewing, using evince or gv.  They can also be
+ *     rasterized for printing, using gs or an embedded interpreter
+ *     in a PostScript printer.  And they can be converted to a pdf
+ *     using gs (ps2pdf).
+ *
+ *     Convert specified files to PS
+ *          l_int32          convertFilesToPS()
+ *          l_int32          sarrayConvertFilesToPS()
+ *          l_int32          convertFilesFittedToPS()
+ *          l_int32          sarrayConvertFilesFittedToPS()
+ *          l_int32          writeImageCompressedToPSFile()
+ *
+ *     Convert mixed text/image files to PS
+ *          l_int32          convertSegmentedPagesToPS()
+ *          l_int32          pixWriteSegmentedPageToPS()
+ *          l_int32          pixWriteMixedToPS()
+ *
+ *     Convert any image file to PS for embedding
+ *          l_int32          convertToPSEmbed()
+ *
+ *     Write all images in a pixa out to PS
+ *          l_int32          pixaWriteCompressedToPS()
+ *          l_int32          pixWriteCompressedToPS()
+ *
+ *  These PostScript converters are used in three different ways.
+ *
+ *  (1) For embedding a PS file in a program like TeX.
+ *      convertToPSEmbed() handles this for levels 1, 2 and 3 output,
+ *      and prog/converttops wraps this in an executable.
+ *      converttops is a generalization of Thomas Merz's jpeg2ps wrapper,
+ *      in that it works for all types (formats, depth, colormap)
+ *      of input images and gives PS output in one of these formats
+ *        * level 1 (uncompressed)
+ *        * level 2 (compressed ccittg4 or dct)
+ *        * level 3 (compressed flate)
+ *
+ *  (2) For composing a set of pages with any number of images
+ *      painted on them, in either level 2 or level 3 formats.
+ *
+ *  (3) For printing a page image or a set of page images, at a
+ *      resolution that optimally fills the page, using
+ *      convertFilesFittedToPS().
+ *
+ *  The top-level calls of utilities in category 2, which can compose
+ *  multiple images on a page, and which generate a PostScript file for
+ *  printing or display (e.g., conversion to pdf), are:
+ *      convertFilesToPS()
+ *      convertFilesFittedToPS()
+ *      convertSegmentedPagesToPS()
+ *
+ *  All images are output with page numbers.  Bounding box hints are
+ *  more subtle.  They must be included for embeding images in
+ *  TeX, for example, and the low-level writers include bounding
+ *  box hints by default.  However, these hints should not be included for
+ *  multi-page PostScript that is composed of a sequence of images;
+ *  consequently, they are not written when calling higher level
+ *  functions such as convertFilesToPS(), convertFilesFittedToPS()
+ *  and convertSegmentedPagesToPS().  The function l_psWriteBoundingBox()
+ *  sets a flag to give low-level control over this.
+ * 
+ */ + +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if USE_PSIO /* defined in environ.h */ + /* --------------------------------------------*/ + +/*-------------------------------------------------------------* + * Convert files in a directory to PS * + *-------------------------------------------------------------*/ +/* + * \brief convertFilesToPS() + * + * \param[in] dirin input directory + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] res typ. 300 or 600 ppi + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a PS file for all image files in a specified
+ *          directory that contain the substr pattern to be matched.
+ *      (2) Each image is written to a separate page in the output PS file.
+ *      (3) All images are written compressed:
+ *              * if tiffg4  -->  use ccittg4
+ *              * if jpeg    -->  use dct
+ *              * all others -->  use flate
+ *          If the image is jpeg or tiffg4, we use the existing compressed
+ *          strings for the encoding; otherwise, we read the image into
+ *          a pix and flate-encode the pieces.
+ *      (4) The resolution is often confusing.  It is interpreted
+ *          as the resolution of the output display device:  "If the
+ *          input image were digitized at 300 ppi, what would it
+ *          look like when displayed at res ppi."  So, for example,
+ *          if res = 100 ppi, then the display pixels are 3x larger
+ *          than the 300 ppi pixels, and the image will be rendered
+ *          3x larger.
+ *      (5) The size of the PostScript file is independent of the resolution,
+ *          because the entire file is encoded.  The res parameter just
+ *          tells the PS decomposer how to render the page.  Therefore,
+ *          for minimum file size without loss of visual information,
+ *          if the output res is less than 300, you should downscale
+ *          the image to the output resolution before wrapping in PS.
+ *      (6) The "canvas" on which the image is rendered, at the given
+ *          output resolution, is a standard page size (8.5 x 11 in).
+ * 
+ */ +l_ok +convertFilesToPS(const char *dirin, + const char *substr, + l_int32 res, + const char *fileout) +{ +SARRAY *sa; + + PROCNAME("convertFilesToPS"); + + if (!dirin) + return ERROR_INT("dirin not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (res <= 0) { + L_INFO("setting res to 300 ppi\n", procName); + res = 300; + } + if (res < 10 || res > 4000) + L_WARNING("res is typically in the range 300-600 ppi\n", procName); + + /* Get all filtered and sorted full pathnames. */ + sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0); + + /* Generate the PS file. Don't use bounding boxes. */ + l_psWriteBoundingBox(FALSE); + sarrayConvertFilesToPS(sa, res, fileout); + l_psWriteBoundingBox(TRUE); + sarrayDestroy(&sa); + return 0; +} + + +/* + + * \brief sarrayConvertFilesToPS() + * + * \param[in] sarray of full path names + * \param[in] res typ. 300 or 600 ppi + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) See convertFilesToPS()
+ * 
+ */ +l_ok +sarrayConvertFilesToPS(SARRAY *sa, + l_int32 res, + const char *fileout) +{ +char *fname; +l_int32 i, nfiles, index, ret, format; + + PROCNAME("sarrayConvertFilesToPS"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (res <= 0) { + L_INFO("setting res to 300 ppi\n", procName); + res = 300; + } + if (res < 10 || res > 4000) + L_WARNING("res is typically in the range 300-600 ppi\n", procName); + + nfiles = sarrayGetCount(sa); + for (i = 0, index = 0; i < nfiles; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + ret = pixReadHeader(fname, &format, NULL, NULL, NULL, NULL, NULL); + if (ret) continue; + if (format == IFF_UNKNOWN) + continue; + + writeImageCompressedToPSFile(fname, fileout, res, &index); + } + + return 0; +} + + +/* + * \brief convertFilesFittedToPS() + * + * \param[in] dirin input directory + * \param[in] substr [optional] substring filter on filenames; can be NULL) + * \param[in] xpts desired size in printer points; use 0 for default + * \param[in] ypts desired size in printer points; use 0 for default + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a PS file for all files in a specified directory
+ *          that contain the substr pattern to be matched.
+ *      (2) Each image is written to a separate page in the output PS file.
+ *      (3) All images are written compressed:
+ *              * if tiffg4  -->  use ccittg4
+ *              * if jpeg    -->  use dct
+ *              * all others -->  use flate
+ *          If the image is jpeg or tiffg4, we use the existing compressed
+ *          strings for the encoding; otherwise, we read the image into
+ *          a pix and flate-encode the pieces.
+ *      (4) The resolution is internally determined such that the images
+ *          are rendered, in at least one direction, at 100% of the given
+ *          size in printer points.  Use 0.0 for xpts or ypts to get
+ *          the default value, which is 612.0 or 792.0, rsp.
+ *      (5) The size of the PostScript file is independent of the resolution,
+ *          because the entire file is encoded.  The %xpts and %ypts
+ *          parameter tells the PS decomposer how to render the page.
+ * 
+ */ +l_ok +convertFilesFittedToPS(const char *dirin, + const char *substr, + l_float32 xpts, + l_float32 ypts, + const char *fileout) +{ +SARRAY *sa; + + PROCNAME("convertFilesFittedToPS"); + + if (!dirin) + return ERROR_INT("dirin not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (xpts <= 0.0) { + L_INFO("setting xpts to 612.0 ppi\n", procName); + xpts = 612.0; + } + if (ypts <= 0.0) { + L_INFO("setting ypts to 792.0 ppi\n", procName); + ypts = 792.0; + } + if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0) + L_WARNING("xpts,ypts are typically in the range 500-800\n", procName); + + /* Get all filtered and sorted full pathnames. */ + sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0); + + /* Generate the PS file. Don't use bounding boxes. */ + l_psWriteBoundingBox(FALSE); + sarrayConvertFilesFittedToPS(sa, xpts, ypts, fileout); + l_psWriteBoundingBox(TRUE); + sarrayDestroy(&sa); + return 0; +} + + +/* + * \brief sarrayConvertFilesFittedToPS() + * + * \param[in] sarray of full path names + * \param[in] xpts desired size in printer points; use 0 for default + * \param[in] ypts desired size in printer points; use 0 for default + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *     (1) See convertFilesFittedToPS()
+ * 
+ */ +l_ok +sarrayConvertFilesFittedToPS(SARRAY *sa, + l_float32 xpts, + l_float32 ypts, + const char *fileout) +{ +char *fname; +l_int32 ret, i, w, h, nfiles, index, format, res; + + PROCNAME("sarrayConvertFilesFittedToPS"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (xpts <= 0.0) { + L_INFO("setting xpts to 612.0\n", procName); + xpts = 612.0; + } + if (ypts <= 0.0) { + L_INFO("setting ypts to 792.0\n", procName); + ypts = 792.0; + } + if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0) + L_WARNING("xpts,ypts are typically in the range 500-800\n", procName); + + nfiles = sarrayGetCount(sa); + for (i = 0, index = 0; i < nfiles; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + ret = pixReadHeader(fname, &format, &w, &h, NULL, NULL, NULL); + if (ret) continue; + if (format == IFF_UNKNOWN) + continue; + + /* Be sure the entire image is wrapped */ + if (xpts * h < ypts * w) + res = (l_int32)((l_float32)w * 72.0 / xpts); + else + res = (l_int32)((l_float32)h * 72.0 / ypts); + + writeImageCompressedToPSFile(fname, fileout, res, &index); + } + + return 0; +} + + +/* + * \brief writeImageCompressedToPSFile() + * + * \param[in] filein input image file + * \param[in] fileout output ps file + * \param[in] res output printer resolution + * \param[in,out] pindex index of image in output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This wraps a single page image in PS.
+ *      (2) The input file can be in any format.  It is compressed as follows:
+ *             * if in tiffg4  -->  use ccittg4
+ *             * if in jpeg    -->  use dct
+ *             * all others    -->  use flate
+ *      (3) Before the first call, set %index = 0.  %index is incremented
+ *          if the page is successfully written.  It is used to decide
+ *          whether to write (index == 0) or append (index > 0) to the file.
+ * 
+ */ +l_ok +writeImageCompressedToPSFile(const char *filein, + const char *fileout, + l_int32 res, + l_int32 *pindex) +{ +const char *op; +l_int32 format, retval; + + PROCNAME("writeImageCompressedToPSFile"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + + findFileFormat(filein, &format); + if (format == IFF_UNKNOWN) { + L_ERROR("format of %s not known\n", procName, filein); + return 1; + } + + op = (*pindex == 0) ? "w" : "a"; + if (format == IFF_JFIF_JPEG) { + retval = convertJpegToPS(filein, fileout, op, 0, 0, + res, 1.0, *pindex + 1, TRUE); + } else if (format == IFF_TIFF_G4) { + retval = convertG4ToPS(filein, fileout, op, 0, 0, + res, 1.0, *pindex + 1, FALSE, TRUE); + } else { /* all other image formats */ + retval = convertFlateToPS(filein, fileout, op, 0, 0, + res, 1.0, *pindex + 1, TRUE); + } + if (retval == 0) (*pindex)++; + + return retval; +} + + +/*-------------------------------------------------------------* + * Convert mixed text/image files to PS * + *-------------------------------------------------------------*/ +/* + * \brief convertSegmentedPagesToPS() + * + * \param[in] pagedir input page image directory + * \param[in] pagestr [optional] substring filter on page filenames; + * can be NULL + * \param[in] page_numpre number of characters in page name before number + * \param[in] maskdir input mask image directory + * \param[in] maskstr [optional] substring filter on mask filenames; + * can be NULL + * \param[in] mask_numpre number of characters in mask name before number + * \param[in] numpost number of characters in names after number + * \param[in] maxnum only consider page numbers up to this value + * \param[in] textscale scale of text output relative to pixs + * \param[in] imagescale scale of image output relative to pixs + * \param[in] threshold for binarization; typ. about 190; 0 for default + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a PS file for all page image and mask files in two
+ *          specified directories and that contain the page numbers as
+ *          specified below.  The two directories can be the same, in which
+ *          case the page and mask files are differentiated by the two
+ *          substrings for string matches.
+ *      (2) The page images are taken in lexicographic order.
+ *          Mask images whose numbers match the page images are used to
+ *          segment the page images.  Page images without a matching
+ *          mask image are scaled, thresholded and rendered entirely as text.
+ *      (3) Each PS page is generated as a compressed representation of
+ *          the page image, where the part of the image under the mask
+ *          is suitably scaled and compressed as DCT (i.e., jpeg), and
+ *          the remaining part of the page is suitably scaled, thresholded,
+ *          compressed as G4 (i.e., tiff g4), and rendered by painting
+ *          black through the resulting text mask.
+ *      (4) The scaling is typically 2x down for the DCT component
+ *          (%imagescale = 0.5) and 2x up for the G4 component
+ *          (%textscale = 2.0).
+ *      (5) The resolution is automatically set to fit to a
+ *          letter-size (8.5 x 11 inch) page.
+ *      (6) Both the DCT and the G4 encoding are PostScript level 2.
+ *      (7) It is assumed that the page number is contained within
+ *          the basename (the filename without directory or extension).
+ *          %page_numpre is the number of characters in the page basename
+ *          preceding the actual page number; %mask_numpre is likewise for
+ *          the mask basename; %numpost is the number of characters
+ *          following the page number.  For example, for mask name
+ *          mask_006.tif, mask_numpre = 5 ("mask_).
+ *      (8) To render a page as is -- that is, with no thresholding
+ *          of any pixels -- use a mask in the mask directory that is
+ *          full size with all pixels set to 1.  If the page is 1 bpp,
+ *          it is not necessary to have a mask.
+ * 
+ */ +l_ok +convertSegmentedPagesToPS(const char *pagedir, + const char *pagestr, + l_int32 page_numpre, + const char *maskdir, + const char *maskstr, + l_int32 mask_numpre, + l_int32 numpost, + l_int32 maxnum, + l_float32 textscale, + l_float32 imagescale, + l_int32 threshold, + const char *fileout) +{ +l_int32 pageno, i, npages; +PIX *pixs, *pixm; +SARRAY *sapage, *samask; + + PROCNAME("convertSegmentedPagesToPS"); + + if (!pagedir) + return ERROR_INT("pagedir not defined", procName, 1); + if (!maskdir) + return ERROR_INT("maskdir not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (threshold <= 0) { + L_INFO("setting threshold to 190\n", procName); + threshold = 190; + } + + /* Get numbered full pathnames; max size of sarray is maxnum */ + sapage = getNumberedPathnamesInDirectory(pagedir, pagestr, + page_numpre, numpost, maxnum); + samask = getNumberedPathnamesInDirectory(maskdir, maskstr, + mask_numpre, numpost, maxnum); + sarrayPadToSameSize(sapage, samask, ""); + if ((npages = sarrayGetCount(sapage)) == 0) { + sarrayDestroy(&sapage); + sarrayDestroy(&samask); + return ERROR_INT("no matching pages found", procName, 1); + } + + /* Generate the PS file */ + pageno = 1; + for (i = 0; i < npages; i++) { + if ((pixs = pixReadIndexed(sapage, i)) == NULL) + continue; + pixm = pixReadIndexed(samask, i); + pixWriteSegmentedPageToPS(pixs, pixm, textscale, imagescale, + threshold, pageno, fileout); + pixDestroy(&pixs); + pixDestroy(&pixm); + pageno++; + } + + sarrayDestroy(&sapage); + sarrayDestroy(&samask); + return 0; +} + + +/* + * \brief pixWriteSegmentedPageToPS() + * + * \param[in] pixs all depths; colormap ok + * \param[in] pixm [optional] 1 bpp segmentation mask over image region + * \param[in] textscale scale of text output relative to pixs + * \param[in] imagescale scale of image output relative to pixs + * \param[in] threshold for binarization; typ. about 190; 0 for default + * \param[in] pageno page number in set; use 1 for new output file + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates the PS string for a mixed text/image page,
+ *          and adds it to an existing file if %pageno > 1.
+ *          The PS output is determined by fitting the result to
+ *          a letter-size (8.5 x 11 inch) page.
+ *      (2) The two images (pixs and pixm) are at the same resolution
+ *          (typically 300 ppi).  They are used to generate two compressed
+ *          images, pixb and pixc, that are put directly into the output
+ *          PS file.
+ *      (3) pixb is the text component.  In the PostScript world, we think of
+ *          it as a mask through which we paint black.  It is produced by
+ *          scaling pixs by %textscale, and thresholding to 1 bpp.
+ *      (4) pixc is the image component, which is that part of pixs under
+ *          the mask pixm.  It is scaled from pixs by %imagescale.
+ *      (5) Typical values are textscale = 2.0 and imagescale = 0.5.
+ *      (6) If pixm == NULL, the page has only text.  If it is all black,
+ *          the page is all image and has no text.
+ *      (7) This can be used to write a multi-page PS file, by using
+ *          sequential page numbers with the same output file.  It can
+ *          also be used to write separate PS files for each page,
+ *          by using different output files with %pageno = 0 or 1.
+ * 
+ */ +l_ok +pixWriteSegmentedPageToPS(PIX *pixs, + PIX *pixm, + l_float32 textscale, + l_float32 imagescale, + l_int32 threshold, + l_int32 pageno, + const char *fileout) +{ +l_int32 alltext, notext, d, ret; +l_uint32 val; +l_float32 scaleratio; +PIX *pixmi, *pixmis, *pixt, *pixg, *pixsc, *pixb, *pixc; + + PROCNAME("pixWriteSegmentedPageToPS"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (imagescale <= 0.0 || textscale <= 0.0) + return ERROR_INT("relative scales must be > 0.0", procName, 1); + + /* Analyze the page. Determine the ratio by which the + * binary text mask is scaled relative to the image part. + * If there is no image region (alltext == TRUE), the + * text mask will be rendered directly to fit the page, + * and scaleratio = 1.0. */ + alltext = TRUE; + notext = FALSE; + scaleratio = 1.0; + if (pixm) { + pixZero(pixm, &alltext); /* pixm empty: all text */ + if (alltext) { + pixm = NULL; /* treat it as not existing here */ + } else { + pixmi = pixInvert(NULL, pixm); + pixZero(pixmi, ¬ext); /* pixm full; no text */ + pixDestroy(&pixmi); + scaleratio = textscale / imagescale; + } + } + + if (pixGetDepth(pixs) == 1) { /* render tiff g4 */ + pixb = pixClone(pixs); + pixc = NULL; + } else { + pixt = pixConvertTo8Or32(pixs, L_CLONE, 0); /* clone if possible */ + + /* Get the binary text mask. Note that pixg cannot be a + * clone of pixs, because it may be altered by pixSetMasked(). */ + pixb = NULL; + if (notext == FALSE) { + d = pixGetDepth(pixt); + if (d == 8) + pixg = pixCopy(NULL, pixt); + else /* d == 32 */ + pixg = pixConvertRGBToLuminance(pixt); + if (pixm) /* clear out the image parts */ + pixSetMasked(pixg, pixm, 255); + if (textscale == 1.0) + pixsc = pixClone(pixg); + else if (textscale >= 0.7) + pixsc = pixScaleGrayLI(pixg, textscale, textscale); + else + pixsc = pixScaleAreaMap(pixg, textscale, textscale); + pixb = pixThresholdToBinary(pixsc, threshold); + pixDestroy(&pixg); + pixDestroy(&pixsc); + } + + /* Get the scaled image region */ + pixc = NULL; + if (pixm) { + if (imagescale == 1.0) + pixsc = pixClone(pixt); /* can possibly be a clone of pixs */ + else + pixsc = pixScale(pixt, imagescale, imagescale); + + /* If pixm is not full, clear the pixels in pixsc + * corresponding to bg in pixm, where there can be text + * that is written through the mask pixb. Note that + * we could skip this and use pixsc directly in + * pixWriteMixedToPS(); however, clearing these + * non-image regions to a white background will reduce + * the size of pixc (relative to pixsc), and hence + * reduce the size of the PS file that is generated. + * Use a copy so that we don't accidentally alter pixs. */ + if (notext == FALSE) { + pixmis = pixScale(pixm, imagescale, imagescale); + pixmi = pixInvert(NULL, pixmis); + val = (d == 8) ? 0xff : 0xffffff00; + pixc = pixCopy(NULL, pixsc); + pixSetMasked(pixc, pixmi, val); /* clear non-image part */ + pixDestroy(&pixmis); + pixDestroy(&pixmi); + } else { + pixc = pixClone(pixsc); + } + pixDestroy(&pixsc); + } + pixDestroy(&pixt); + } + + /* Generate the PS file. Don't use bounding boxes. */ + l_psWriteBoundingBox(FALSE); + ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout); + l_psWriteBoundingBox(TRUE); + pixDestroy(&pixb); + pixDestroy(&pixc); + return ret; +} + + +/* + * \brief pixWriteMixedToPS() + * + * \param[in] pixb [optional] 1 bpp mask; typically for text + * \param[in] pixc [optional] 8 or 32 bpp image regions + * \param[in] scale scale factor for rendering pixb, relative to pixc; + * typ. 4.0 + * \param[in] pageno page number in set; use 1 for new output file + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This low level function generates the PS string for a mixed
+ *          text/image page, and adds it to an existing file if
+ *          %pageno > 1.
+ *      (2) The two images (pixb and pixc) are typically generated at the
+ *          resolution that they will be rendered in the PS file.
+ *      (3) pixb is the text component.  In the PostScript world, we think of
+ *          it as a mask through which we paint black.
+ *      (4) pixc is the (typically halftone) image component.  It is
+ *          white in the rest of the page.  To minimize the size of the
+ *          PS file, it should be rendered at a resolution that is at
+ *          least equal to its actual resolution.
+ *      (5) %scale gives the ratio of resolution of pixb to pixc.
+ *          Typical resolutions are: 600 ppi for pixb, 150 ppi for pixc;
+ *          so %scale = 4.0.  If one of the images is not defined,
+ *          the value of %scale is ignored.
+ *      (6) We write pixc with DCT compression (jpeg).  This is followed
+ *          by painting the text as black through the mask pixb.  If
+ *          pixc doesn't exist (alltext), we write the text with the
+ *          PS "image" operator instead of the "imagemask" operator,
+ *          because ghostscript's ps2pdf is flaky when the latter is used.
+ *      (7) The actual output resolution is determined by fitting the
+ *          result to a letter-size (8.5 x 11 inch) page.
+ * 
+ */
+l_ok
+pixWriteMixedToPS(PIX         *pixb,
+                  PIX         *pixc,
+                  l_float32    scale,
+                  l_int32      pageno,
+                  const char  *fileout)
+{
+char        *tname;
+const char  *op;
+l_int32      resb, resc, endpage, maskop, ret;
+
+    PROCNAME("pixWriteMixedToPS");
+
+    if (!pixb && !pixc)
+        return ERROR_INT("pixb and pixc both undefined", procName, 1);
+    if (!fileout)
+        return ERROR_INT("fileout not defined", procName, 1);
+
+        /* Compute the resolution that fills a letter-size page. */
+    if (!pixc) {
+       resb = getResLetterPage(pixGetWidth(pixb), pixGetHeight(pixb), 0);
+    } else {
+       resc = getResLetterPage(pixGetWidth(pixc), pixGetHeight(pixc), 0);
+       if (pixb)
+           resb = (l_int32)(scale * resc);
+    }
+
+        /* Write the jpeg image first */
+    if (pixc) {
+        tname = l_makeTempFilename();
+        pixWrite(tname, pixc, IFF_JFIF_JPEG);
+        endpage = (pixb) ? FALSE : TRUE;
+        op = (pageno <= 1) ? "w" : "a";
+        ret = convertJpegToPS(tname, fileout, op, 0, 0, resc, 1.0,
+                              pageno, endpage);
+        lept_rmfile(tname);
+        LEPT_FREE(tname);
+        if (ret)
+            return ERROR_INT("jpeg data not written", procName, 1);
+    }
+
+        /* Write the binary data, either directly or, if there is
+         * a jpeg image on the page, through the mask. */
+    if (pixb) {
+        tname = l_makeTempFilename();
+        pixWrite(tname, pixb, IFF_TIFF_G4);
+        op = (pageno <= 1 && !pixc) ? "w" : "a";
+        maskop = (pixc) ? 1 : 0;
+        ret = convertG4ToPS(tname, fileout, op, 0, 0, resb, 1.0,
+                            pageno, maskop, 1);
+        lept_rmfile(tname);
+        LEPT_FREE(tname);
+        if (ret)
+            return ERROR_INT("tiff data not written", procName, 1);
+    }
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------*
+ *            Convert any image file to PS for embedding       *
+ *-------------------------------------------------------------*/
+/*
+ * \brief  convertToPSEmbed()
+ *
+ * \param[in]     filein    input image file, any format
+ * \param[in]     fileout   output ps file
+ * \param[in]     level     PostScript compression: 1 (uncompressed), 2 or 3
+ * \return  0 if OK, 1 on error
+ *
+ * 
+ * Notes:
+ *      (1) This is a wrapper function that generates a PS file with
+ *          a bounding box, from any input image file.
+ *      (2) Do the best job of compression given the specified level.
+ *          %level=3 does flate compression on anything that is not
+ *          tiffg4 (1 bpp) or jpeg (8 bpp or rgb).
+ *      (3) If %level=2 and the file is not tiffg4 or jpeg, it will
+ *          first be written to file as jpeg with quality = 75.
+ *          This will remove the colormap and cause some degradation
+ *          in the image.
+ *      (4) The bounding box is required when a program such as TeX
+ *          (through epsf) places and rescales the image.  It is
+ *          sized for fitting the image to an 8.5 x 11.0 inch page.
+ * 
+ */ +l_ok +convertToPSEmbed(const char *filein, + const char *fileout, + l_int32 level) +{ +char *tname; +l_int32 d, format; +PIX *pix, *pixs; + + PROCNAME("convertToPSEmbed"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (level != 1 && level != 2 && level != 3) { + L_ERROR("invalid level specified; using level 2\n", procName); + level = 2; + } + + if (level == 1) { /* no compression */ + pixWritePSEmbed(filein, fileout); + return 0; + } + + /* Find the format and write out directly if in jpeg or tiff g4 */ + findFileFormat(filein, &format); + if (format == IFF_JFIF_JPEG) { + convertJpegToPSEmbed(filein, fileout); + return 0; + } else if (format == IFF_TIFF_G4) { + convertG4ToPSEmbed(filein, fileout); + return 0; + } else if (format == IFF_UNKNOWN) { + L_ERROR("format of %s not known\n", procName, filein); + return 1; + } + + /* If level 3, flate encode. */ + if (level == 3) { + convertFlateToPSEmbed(filein, fileout); + return 0; + } + + /* OK, it's level 2, so we must convert to jpeg or tiff g4 */ + if ((pixs = pixRead(filein)) == NULL) + return ERROR_INT("image not read from file", procName, 1); + d = pixGetDepth(pixs); + if ((d == 2 || d == 4) && !pixGetColormap(pixs)) + pix = pixConvertTo8(pixs, 0); + else if (d == 16) + pix = pixConvert16To8(pixs, L_MS_BYTE); + else + pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + pixDestroy(&pixs); + if (!pix) + return ERROR_INT("converted pix not made", procName, 1); + + d = pixGetDepth(pix); + tname = l_makeTempFilename(); + if (d == 1) { + if (pixWrite(tname, pix, IFF_TIFF_G4)) { + LEPT_FREE(tname); + pixDestroy(&pix); + return ERROR_INT("g4 tiff not written", procName, 1); + } + convertG4ToPSEmbed(tname, fileout); + } else { + if (pixWrite(tname, pix, IFF_JFIF_JPEG)) { + LEPT_FREE(tname); + pixDestroy(&pix); + return ERROR_INT("jpeg not written", procName, 1); + } + convertJpegToPSEmbed(tname, fileout); + } + + lept_rmfile(tname); + LEPT_FREE(tname); + pixDestroy(&pix); + return 0; +} + + +/*-------------------------------------------------------------* + * Write all images in a pixa out to PS * + *-------------------------------------------------------------*/ +/* + * \brief pixaWriteCompressedToPS() + * + * \param[in] pixa any set of images + * \param[in] fileout output ps file + * \param[in] res resolution for the set of input images + * \param[in] level PostScript compression capability: 2 or 3 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a PostScript file of multiple page images,
+ *          all with bounding boxes.
+ *      (2) See pixWriteCompressedToPS() for details.
+ *      (3) To generate a pdf from %fileout, use:
+ *             ps2pdf  
+ * 
+ */ +l_ok +pixaWriteCompressedToPS(PIXA *pixa, + const char *fileout, + l_int32 res, + l_int32 level) +{ +l_int32 i, n, index, ret; +PIX *pix; + + PROCNAME("pixaWriteCompressedToPS"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (level != 2 && level != 3) { + L_ERROR("only levels 2 and 3 permitted; using level 2\n", procName); + level = 2; + } + + index = 0; + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + ret = pixWriteCompressedToPS(pix, fileout, res, level, &index); + if (ret) L_ERROR("PS string not written for image %d\n", procName, i); + pixDestroy(&pix); + } + return 0; +} + + +/* + * \brief pixWriteCompressedToPS() + * + * \param[in] pix any depth; colormap OK + * \param[in] fileout output ps file + * \param[in] res of input image + * \param[in] level PostScript compression capability: 2 or 3 + * \param[in,out] pindex index of image in output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates a PostScript string for %pix, and writes it
+ *          to a file, with a bounding box.
+ *      (2) *pindex keeps track of the number of images that have been
+ *          written to %fileout.  If this is the first image to be
+ *          converted, set *pindex == 0 before passing it in.  If the
+ *          PostScript string is successfully generated, this will increment
+ *          *pindex.  If *pindex > 0, the PostScript string will be
+ *          appended to %fileout.
+ *      (3) PostScript level 2 enables lossless tiffg4 and lossy jpeg
+ *          compression.  Level 3 adds lossless flate (essentially gzip)
+ *          compression.
+ *          * For images with a colormap, lossless flate is often better in
+ *            both quality and size than jpeg.
+ *          * The decision for images without a colormap affects compression
+ *            efficiency: %level2 (jpeg) is usually better than %level3 (flate)
+ *          * Because jpeg does not handle 16 bpp, if %level == 2, the image
+ *            is converted to 8 bpp (using MSB) and compressed with jpeg,
+ *              cmap + level2:        jpeg
+ *              cmap + level3:        flate
+ *              1 bpp:                tiffg4
+ *              2 or 4 bpp + level2:  jpeg
+ *              2 or 4 bpp + level3:  flate
+ *              8 bpp + level2:       jpeg
+ *              8 bpp + level3:       flate
+ *              16 bpp + level2:      jpeg   [converted to 8 bpp, with warning]
+ *              16 bpp + level3:      flate
+ *              32 bpp + level2:      jpeg
+ *              32 bpp + level3:      flate
+ * 
+ */ +l_ok +pixWriteCompressedToPS(PIX *pix, + const char *fileout, + l_int32 res, + l_int32 level, + l_int32 *pindex) +{ +char *tname; +l_int32 writeout, d; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixWriteCompressedToPS"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (level != 2 && level != 3) { + L_ERROR("only levels 2 and 3 permitted; using level 2\n", procName); + level = 2; + } + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + + tname = l_makeTempFilename(); + writeout = TRUE; + d = pixGetDepth(pix); + cmap = pixGetColormap(pix); + if (d == 1) { + if (pixWrite(tname, pix, IFF_TIFF_G4)) + writeout = FALSE; + } else if (level == 3) { + if (pixWrite(tname, pix, IFF_PNG)) + writeout = FALSE; + } else { /* level == 2 */ + if (cmap) { + pixt = pixConvertForPSWrap(pix); + if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) + writeout = FALSE; + pixDestroy(&pixt); + } else if (d == 16) { + L_WARNING("d = 16; converting to 8 bpp for jpeg\n", procName); + pixt = pixConvert16To8(pix, L_MS_BYTE); + if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) + writeout = FALSE; + pixDestroy(&pixt); + } else if (d == 2 || d == 4) { + pixt = pixConvertTo8(pix, 0); + if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) + writeout = FALSE; + pixDestroy(&pixt); + } else if (d == 8 || d == 32) { + if (pixWrite(tname, pix, IFF_JFIF_JPEG)) + writeout = FALSE; + } else { /* shouldn't happen */ + L_ERROR("invalid depth with level 2: %d\n", procName, d); + writeout = FALSE; + } + } + + if (writeout) + writeImageCompressedToPSFile(tname, fileout, res, pindex); + + if (lept_rmfile(tname) != 0) + L_ERROR("temp file %s was not deleted\n", procName, tname); + LEPT_FREE(tname); + return (writeout) ? 0 : 1; +} + +/* --------------------------------------------*/ +#endif /* USE_PSIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/psio1stub.c b/hgdriver/3rdparty/hgOCR/leptonica/psio1stub.c new file mode 100644 index 0000000..96a044c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/psio1stub.c @@ -0,0 +1,133 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file psio1stub.c + *
+ *
+ *     Stubs for psio1.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_PSIO /* defined in environ.h */ +/* --------------------------------------------*/ + +l_ok convertFilesToPS(const char *dirin, const char *substr, + l_int32 res, const char *fileout) +{ + return ERROR_INT("function not present", "convertFilesToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok sarrayConvertFilesToPS(SARRAY *sa, l_int32 res, const char *fileout) +{ + return ERROR_INT("function not present", "sarrayConvertFilesToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertFilesFittedToPS(const char *dirin, const char *substr, + l_float32 xpts, l_float32 ypts, + const char *fileout) +{ + return ERROR_INT("function not present", "convertFilesFittedToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok sarrayConvertFilesFittedToPS(SARRAY *sa, l_float32 xpts, + l_float32 ypts, const char *fileout) +{ + return ERROR_INT("function not present", "sarrayConvertFilesFittedToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok writeImageCompressedToPSFile(const char *filein, const char *fileout, + l_int32 res, l_int32 *pindex) +{ + return ERROR_INT("function not present", "writeImageCompressedToPSFile", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertSegmentedPagesToPS(const char *pagedir, const char *pagestr, + l_int32 page_numpre, const char *maskdir, + const char *maskstr, l_int32 mask_numpre, + l_int32 numpost, l_int32 maxnum, + l_float32 textscale, l_float32 imagescale, + l_int32 threshold, const char *fileout) +{ + return ERROR_INT("function not present", "convertSegmentedPagesToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteSegmentedPageToPS(PIX *pixs, PIX *pixm, l_float32 textscale, + l_float32 imagescale, l_int32 threshold, + l_int32 pageno, const char *fileout) +{ + return ERROR_INT("function not present", "pixWriteSegmentedPagesToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMixedToPS(PIX *pixb, PIX *pixc, l_float32 scale, + l_int32 pageno, const char *fileout) +{ + return ERROR_INT("function not present", "pixWriteMixedToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertToPSEmbed(const char *filein, const char *fileout, l_int32 level) +{ + return ERROR_INT("function not present", "convertToPSEmbed", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaWriteCompressedToPS(PIXA *pixa, const char *fileout, + l_int32 res, l_int32 level) +{ + return ERROR_INT("function not present", "pixaWriteCompressedtoPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteCompressedToPS(PIX *pix, const char *fileout, l_int32 res, + l_int32 level, l_int32 *pindex) +{ + return ERROR_INT("function not present", "pixWriteCompressedtoPS", 1); +} + +/* --------------------------------------------*/ +#endif /* !USE_PSIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/psio2.c b/hgdriver/3rdparty/hgOCR/leptonica/psio2.c new file mode 100644 index 0000000..de6d0a2 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/psio2.c @@ -0,0 +1,2041 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file psio2.c + *
+ *
+ *    |=============================================================|
+ *    |                         Important note                      |
+ *    |=============================================================|
+ *    | Some of these functions require I/O libraries such as       |
+ *    | libtiff, libjpeg, and libz.  If you do not have these       |
+ *    | libraries, some calls will fail.                            |
+ *    |                                                             |
+ *    | You can manually deactivate all PostScript writing by       |
+ *    | setting this in environ.h:                                  |
+ *    | \code                                                       |
+ *    |     #define  USE_PSIO     0                                 |
+ *    | \endcode                                                    |
+ *    | in environ.h.  This will link psio2stub.c                   |
+ *    |=============================================================|
+ *
+ *     These are lower-level functions that implement a PostScript
+ *     "device driver" for wrapping images in PostScript.  The images
+ *     can be rendered by a PostScript interpreter for viewing,
+ *     using evince or gv.  They can also be rasterized for printing,
+ *     using gs or an embedded interpreter in a PostScript printer.
+ *     And they can be converted to a pdf using gs (ps2pdf).
+ *
+ *     For uncompressed images
+ *          l_int32              pixWritePSEmbed()
+ *          l_int32              pixWriteStreamPS()
+ *          char                *pixWriteStringPS()
+ *          char                *generateUncompressedPS()
+ *          static void          getScaledParametersPS()
+ *          static l_int32       convertByteToHexAscii()
+ *
+ *     For jpeg compressed images (use dct compression)
+ *          l_int32              convertJpegToPSEmbed()
+ *          l_int32              convertJpegToPS()
+ *          static l_int32       convertJpegToPSString()
+ *          static char         *generateJpegPS()
+ *
+ *     For g4 fax compressed images (use ccitt g4 compression)
+ *          l_int32              convertG4ToPSEmbed()
+ *          l_int32              convertG4ToPS()
+ *          static l_int32       convertG4ToPSString()
+ *          static char         *generateG4PS()
+ *
+ *     For multipage tiff images
+ *          l_int32              convertTiffMultipageToPS()
+ *
+ *     For flate (gzip) compressed images (e.g., png)
+ *          l_int32              convertFlateToPSEmbed()
+ *          l_int32              convertFlateToPS()
+ *          static l_int32       convertFlateToPSString()
+ *          static char         *generateFlatePS()
+ *
+ *     Write to memory
+ *          l_int32              pixWriteMemPS()
+ *
+ *     Converting resolution
+ *          l_int32              getResLetterPage()
+ *          static l_int32       getResA4Page()
+ *
+ *     Setting flag for writing bounding box hint
+ *          void                 l_psWriteBoundingBox()
+ *
+ *  See psio1.c for higher-level functions and their usage.
+ * 
+ */ + +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if USE_PSIO /* defined in environ.h */ + /* --------------------------------------------*/ + + /* Set default for writing bounding box hint */ +static l_int32 var_PS_WRITE_BOUNDING_BOX = 1; + +//static const l_int32 Bufsize = 512; +#define Bufsize 512 +static const l_int32 DefaultInputRes = 300; /* typical scan res, ppi */ +static const l_int32 MinRes = 5; +static const l_int32 MaxRes = 3000; + + /* For computing resolution that fills page to desired amount */ +static const l_int32 LetterWidth = 612; /* points */ +static const l_int32 LetterHeight = 792; /* points */ +static const l_int32 A4Width = 595; /* points */ +static const l_int32 A4Height = 842; /* points */ +static const l_float32 DefaultFillFraction = 0.95; + +#ifndef NO_CONSOLE_IO +#define DEBUG_JPEG 0 +#define DEBUG_G4 0 +#define DEBUG_FLATE 0 +#endif /* ~NO_CONSOLE_IO */ + +/* Note that the bounding box hint at the top of the generated PostScript + * file is required for the "*Embed" functions. These generate a + * PostScript file for an individual image that can be translated and + * scaled by an application that embeds the image in its output + * (e.g., in the PS output from a TeX file). + * However, bounding box hints should not be embedded in any + * PostScript image that will be composited with other images, + * where more than one image may be placed in an arbitrary location + * on a page. */ + + /* Static helper functions */ +static void getScaledParametersPS(BOX *box, l_int32 wpix, l_int32 hpix, + l_int32 res, l_float32 scale, + l_float32 *pxpt, l_float32 *pypt, + l_float32 *pwpt, l_float32 *phpt); +static void convertByteToHexAscii(l_uint8 byteval, char *pnib1, char *pnib2); +static l_ok convertJpegToPSString(const char *filein, char **poutstr, + l_int32 *pnbytes, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, + l_int32 pageno, l_int32 endpage); +static char *generateJpegPS(const char *filein, L_COMP_DATA *cid, + l_float32 xpt, l_float32 ypt, l_float32 wpt, + l_float32 hpt, l_int32 pageno, l_int32 endpage); +static l_ok convertG4ToPSString(const char *filein, char **poutstr, + l_int32 *pnbytes, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, l_int32 pageno, + l_int32 maskflag, l_int32 endpage); +static char *generateG4PS(const char *filein, L_COMP_DATA *cid, l_float32 xpt, + l_float32 ypt, l_float32 wpt, l_float32 hpt, + l_int32 maskflag, l_int32 pageno, l_int32 endpage); +static l_ok convertFlateToPSString(const char *filein, char **poutstr, + l_int32 *pnbytes, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, + l_int32 pageno, l_int32 endpage); +static char *generateFlatePS(const char *filein, L_COMP_DATA *cid, + l_float32 xpt, l_float32 ypt, l_float32 wpt, + l_float32 hpt, l_int32 pageno, l_int32 endpage); + + +/*-------------------------------------------------------------* + * For uncompressed images * + *-------------------------------------------------------------*/ +/*! + * \brief pixWritePSEmbed() + * + * \param[in] filein input file, all depths, colormap OK + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a simple wrapper function that generates an
+ *          uncompressed PS file, with a bounding box.
+ *      (2) The bounding box is required when a program such as TeX
+ *          (through epsf) places and rescales the image.
+ *      (3) The bounding box is sized for fitting the image to an
+ *          8.5 x 11.0 inch page.
+ * 
+ */ +l_ok +pixWritePSEmbed(const char *filein, + const char *fileout) +{ +l_int32 w, h, ret; +l_float32 scale; +FILE *fp; +PIX *pix; + + PROCNAME("pixWritePSEmbed"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((pix = pixRead(filein)) == NULL) + return ERROR_INT("image not read from file", procName, 1); + w = pixGetWidth(pix); + h = pixGetHeight(pix); + if (w * 11.0 > h * 8.5) + scale = 8.5 * 300. / (l_float32)w; + else + scale = 11.0 * 300. / (l_float32)h; + + if ((fp = fopenWriteStream(fileout, "wb")) == NULL) + return ERROR_INT("file not opened for write", procName, 1); + ret = pixWriteStreamPS(fp, pix, NULL, 0, scale); + fclose(fp); + + pixDestroy(&pix); + return ret; +} + + +/*! + * \brief pixWriteStreamPS() + * + * \param[in] fp file stream + * \param[in] pix + * \param[in] box [optional] + * \param[in] res can use 0 for default of 300 ppi + * \param[in] scale to prevent scaling, use either 1.0 or 0.0 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This writes image in PS format, optionally scaled,
+ *          adjusted for the printer resolution, and with
+ *          a bounding box.
+ *      (2) For details on use of parameters, see pixWriteStringPS().
+ * 
+ */ +l_ok +pixWriteStreamPS(FILE *fp, + PIX *pix, + BOX *box, + l_int32 res, + l_float32 scale) +{ +char *outstr; +l_int32 length; +PIX *pixc; + + PROCNAME("pixWriteStreamPS"); + + if (!fp) + return (l_int32)ERROR_INT("stream not open", procName, 1); + if (!pix) + return (l_int32)ERROR_INT("pix not defined", procName, 1); + + if ((pixc = pixConvertForPSWrap(pix)) == NULL) + return (l_int32)ERROR_INT("pixc not made", procName, 1); + + if ((outstr = pixWriteStringPS(pixc, box, res, scale)) == NULL) { + pixDestroy(&pixc); + return (l_int32)ERROR_INT("outstr not made", procName, 1); + } + length = strlen(outstr); + fwrite(outstr, 1, length, fp); + LEPT_FREE(outstr); + pixDestroy(&pixc); + return 0; +} + + +/*! + * \brief pixWriteStringPS() + * + * \param[in] pixs all depths, colormap OK + * \param[in] box bounding box; can be NULL + * \param[in] res resolution, in printer ppi. Use 0 for default 300 ppi. + * \param[in] scale scale factor. If no scaling is desired, use + * either 1.0 or 0.0. Scaling just resets the resolution + * parameter; the actual scaling is done in the + * interpreter at rendering time. This is important: + * it allows you to scale the image up without + * increasing the file size. + * \return ps string if OK, or NULL on error + * + *
+ * a) If %box == NULL, image is placed, optionally scaled,
+ *      in a standard b.b. at the center of the page.
+ *      This is to be used when another program like
+ *      TeX through epsf places the image.
+ * b) If %box != NULL, image is placed without a
+ *      b.b. at the specified page location and with
+ *      optional scaling.  This is to be used when
+ *      you want to specify exactly where and optionally
+ *      how big you want the image to be.
+ *      Note that all coordinates are in PS convention,
+ *      with 0,0 at LL corner of the page:
+ *          x,y    location of LL corner of image, in mils.
+ *          w,h    scaled size, in mils.  Use 0 to
+ *                 scale with "scale" and "res" input.
+ *
+ * %scale: If no scaling is desired, use either 1.0 or 0.0.
+ * Scaling just resets the resolution parameter; the actual
+ * scaling is done in the interpreter at rendering time.
+ * This is important: * it allows you to scale the image up
+ * without increasing the file size.
+ *
+ * Notes:
+ *      (1) OK, this seems a bit complicated, because there are various
+ *          ways to scale and not to scale.  Here's a summary:
+ *      (2) If you don't want any scaling at all:
+ *           * if you are using a box:
+ *               set w = 0, h = 0, and use scale = 1.0; it will print
+ *               each pixel unscaled at printer resolution
+ *           * if you are not using a box:
+ *               set scale = 1.0; it will print at printer resolution
+ *      (3) If you want the image to be a certain size in inches:
+ *           * you must use a box and set the box (w,h) in mils
+ *      (4) If you want the image to be scaled by a scale factor != 1.0:
+ *           * if you are using a box:
+ *               set w = 0, h = 0, and use the desired scale factor;
+ *               the higher the printer resolution, the smaller the
+ *               image will actually appear.
+ *           * if you are not using a box:
+ *               set the desired scale factor; the higher the printer
+ *               resolution, the smaller the image will actually appear.
+ *      (5) Another complication is the proliferation of distance units:
+ *           * The interface distances are in milli-inches.
+ *           * Three different units are used internally:
+ *              ~ pixels  (units of 1/res inch)
+ *              ~ printer pts (units of 1/72 inch)
+ *              ~ inches
+ *           * Here is a quiz on volume units from a reviewer:
+ *             How many UK milli-cups in a US kilo-teaspoon?
+ *               (Hint: 1.0 US cup = 0.75 UK cup + 0.2 US gill;
+ *                      1.0 US gill = 24.0 US teaspoons)
+ * 
+ */ +char * +pixWriteStringPS(PIX *pixs, + BOX *box, + l_int32 res, + l_float32 scale) +{ +char nib1, nib2; +char *hexdata, *outstr; +l_uint8 byteval; +l_int32 i, j, k, w, h, d; +l_float32 wpt, hpt, xpt, ypt; +l_int32 wpl, psbpl, hexbytes, boxflag, bps; +l_uint32 *line, *data; +PIX *pix; + + PROCNAME("pixWriteStringPS"); + + if (!pixs) + return (char *)ERROR_PTR("pixs not defined", procName, NULL); + + if ((pix = pixConvertForPSWrap(pixs)) == NULL) + return (char *)ERROR_PTR("pix not made", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + + /* Get the factors by which PS scales and translates, in pts */ + if (!box) + boxflag = 0; /* no scaling; b.b. at center */ + else + boxflag = 1; /* no b.b., specify placement and optional scaling */ + getScaledParametersPS(box, w, h, res, scale, &xpt, &ypt, &wpt, &hpt); + + if (d == 1) + bps = 1; /* bits/sample */ + else /* d == 8 || d == 32 */ + bps = 8; + + /* Convert image data to hex string. psbpl is the number of + * bytes in each raster line when it is packed to the byte + * boundary (not the 32 bit word boundary, as with the pix). + * When converted to hex, the hex string has 2 bytes for + * every byte of raster data. */ + wpl = pixGetWpl(pix); + if (d == 1 || d == 8) + psbpl = (w * d + 7) / 8; + else /* d == 32 */ + psbpl = 3 * w; + data = pixGetData(pix); + hexbytes = 2 * psbpl * h; /* size of ps hex array */ + if ((hexdata = (char *)LEPT_CALLOC(hexbytes + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("hexdata not made", procName, NULL); + if (d == 1 || d == 8) { + for (i = 0, k = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < psbpl; j++) { + byteval = GET_DATA_BYTE(line, j); + convertByteToHexAscii(byteval, &nib1, &nib2); + hexdata[k++] = nib1; + hexdata[k++] = nib2; + } + } + } else { /* d == 32; hexdata bytes packed RGBRGB..., 2 per sample */ + for (i = 0, k = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + byteval = GET_DATA_BYTE(line + j, 0); /* red */ + convertByteToHexAscii(byteval, &nib1, &nib2); + hexdata[k++] = nib1; + hexdata[k++] = nib2; + byteval = GET_DATA_BYTE(line + j, 1); /* green */ + convertByteToHexAscii(byteval, &nib1, &nib2); + hexdata[k++] = nib1; + hexdata[k++] = nib2; + byteval = GET_DATA_BYTE(line + j, 2); /* blue */ + convertByteToHexAscii(byteval, &nib1, &nib2); + hexdata[k++] = nib1; + hexdata[k++] = nib2; + } + } + } + hexdata[k] = '\0'; + + outstr = generateUncompressedPS(hexdata, w, h, d, psbpl, bps, + xpt, ypt, wpt, hpt, boxflag); + pixDestroy(&pix); + if (!outstr) + return (char *)ERROR_PTR("outstr not made", procName, NULL); + return outstr; +} + + +/*! + * \brief generateUncompressedPS() + * + * \param[in] hexdata + * \param[in] w, h raster image size in pixels + * \param[in] d image depth in bpp; rgb is 32 + * \param[in] psbpl raster bytes/line, when packed to the byte boundary + * \param[in] bps bits/sample: either 1 or 8 + * \param[in] xpt, ypt location of LL corner of image, in pts, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] wpt, hpt rendered image size in pts + * \param[in] boxflag 1 to print out bounding box hint; 0 to skip + * \return PS string, or NULL on error + * + *
+ * Notes:
+ *      (1) Low-level function.
+ * 
+ */ +char * +generateUncompressedPS(char *hexdata, + l_int32 w, + l_int32 h, + l_int32 d, + l_int32 psbpl, + l_int32 bps, + l_float32 xpt, + l_float32 ypt, + l_float32 wpt, + l_float32 hpt, + l_int32 boxflag) +{ +char *outstr; +char bigbuf[Bufsize]; +SARRAY *sa; + + PROCNAME("generateUncompressedPS"); + + if (!hexdata) + return (char *)ERROR_PTR("hexdata not defined", procName, NULL); + + sa = sarrayCreate(0); + sarrayAddString(sa, "%!Adobe-PS", L_COPY); + if (boxflag == 0) { + snprintf(bigbuf, sizeof(bigbuf), + "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", + xpt, ypt, xpt + wpt, ypt + hpt); + sarrayAddString(sa, bigbuf, L_COPY); + } else { /* boxflag == 1 */ + sarrayAddString(sa, "gsave", L_COPY); + } + + if (d == 1) + sarrayAddString(sa, + "{1 exch sub} settransfer %invert binary", L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "/bpl %d string def %%bpl as a string", psbpl); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + "%d %d %d %%image dimensions in pixels", w, h, bps); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + "[%d %d %d %d %d %d] %%mapping matrix: [w 0 0 -h 0 h]", + w, 0, 0, -h, 0, h); + sarrayAddString(sa, bigbuf, L_COPY); + + if (boxflag == 0) { + if (d == 1 || d == 8) + sarrayAddString(sa, + "{currentfile bpl readhexstring pop} image", L_COPY); + else /* d == 32 */ + sarrayAddString(sa, + "{currentfile bpl readhexstring pop} false 3 colorimage", + L_COPY); + } else { /* boxflag == 1 */ + if (d == 1 || d == 8) + sarrayAddString(sa, + "{currentfile bpl readhexstring pop} bind image", L_COPY); + else /* d == 32 */ + sarrayAddString(sa, + "{currentfile bpl readhexstring pop} bind false 3 colorimage", + L_COPY); + } + + sarrayAddString(sa, hexdata, L_INSERT); + + if (boxflag == 0) + sarrayAddString(sa, "\nshowpage", L_COPY); + else /* boxflag == 1 */ + sarrayAddString(sa, "\ngrestore", L_COPY); + + outstr = sarrayToString(sa, 1); + sarrayDestroy(&sa); + if (!outstr) L_ERROR("outstr not made\n", procName); + return outstr; +} + + +/*! + * \brief getScaledParametersPS() + * + * \param[in] box [optional] location of image in mils; x,y is LL corner + * \param[in] wpix pix width in pixels + * \param[in] hpix pix height in pixels + * \param[in] res of printer; use 0 for default + * \param[in] scale use 1.0 or 0.0 for no scaling + * \param[out] pxpt location of llx in pts + * \param[out] pypt location of lly in pts + * \param[out] pwpt image width in pts + * \param[out] phpt image height in pts + * \return void no arg checking + * + *
+ * Notes:
+ *      (1) The image is always scaled, depending on res and scale.
+ *      (2) If no box, the image is centered on the page.
+ *      (3) If there is a box, the image is placed within it.
+ * 
+ */ +static void +getScaledParametersPS(BOX *box, + l_int32 wpix, + l_int32 hpix, + l_int32 res, + l_float32 scale, + l_float32 *pxpt, + l_float32 *pypt, + l_float32 *pwpt, + l_float32 *phpt) +{ +l_int32 bx, by, bw, bh; +l_float32 winch, hinch, xinch, yinch, fres; + + PROCNAME("getScaledParametersPS"); + + if (res == 0) + res = DefaultInputRes; + fres = (l_float32)res; + + /* Allow the PS interpreter to scale the resolution */ + if (scale == 0.0) + scale = 1.0; + if (scale != 1.0) { + fres = (l_float32)res / scale; + res = (l_int32)fres; + } + + /* Limit valid resolution interval */ + if (res < MinRes || res > MaxRes) { + L_WARNING("res %d out of bounds; using default res; no scaling\n", + procName, res); + res = DefaultInputRes; + fres = (l_float32)res; + } + + if (!box) { /* center on page */ + winch = (l_float32)wpix / fres; + hinch = (l_float32)hpix / fres; + xinch = (8.5 - winch) / 2.; + yinch = (11.0 - hinch) / 2.; + } else { + boxGetGeometry(box, &bx, &by, &bw, &bh); + if (bw == 0) + winch = (l_float32)wpix / fres; + else + winch = (l_float32)bw / 1000.; + if (bh == 0) + hinch = (l_float32)hpix / fres; + else + hinch = (l_float32)bh / 1000.; + xinch = (l_float32)bx / 1000.; + yinch = (l_float32)by / 1000.; + } + + if (xinch < 0) + L_WARNING("left edge < 0.0 inch\n", procName); + if (xinch + winch > 8.5) + L_WARNING("right edge > 8.5 inch\n", procName); + if (yinch < 0.0) + L_WARNING("bottom edge < 0.0 inch\n", procName); + if (yinch + hinch > 11.0) + L_WARNING("top edge > 11.0 inch\n", procName); + + *pwpt = 72. * winch; + *phpt = 72. * hinch; + *pxpt = 72. * xinch; + *pypt = 72. * yinch; + return; +} + + +/*! + * \brief convertByteToHexAscii() + * + * \param[in] byteval input byte + * \param[out] pnib1, pnib2 two hex ascii characters + * \return void + */ +static void +convertByteToHexAscii(l_uint8 byteval, + char *pnib1, + char *pnib2) +{ +l_uint8 nib; + + nib = byteval >> 4; + if (nib < 10) + *pnib1 = '0' + nib; + else + *pnib1 = 'a' + (nib - 10); + nib = byteval & 0xf; + if (nib < 10) + *pnib2 = '0' + nib; + else + *pnib2 = 'a' + (nib - 10); + return; +} + + +/*-------------------------------------------------------------* + * For jpeg compressed images * + *-------------------------------------------------------------*/ +/*! + * \brief convertJpegToPSEmbed() + * + * \param[in] filein input jpeg file + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function takes a jpeg file as input and generates a DCT
+ *          compressed, ascii85 encoded PS file, with a bounding box.
+ *      (2) The bounding box is required when a program such as TeX
+ *          (through epsf) places and rescales the image.
+ *      (3) The bounding box is sized for fitting the image to an
+ *          8.5 x 11.0 inch page.
+ * 
+ */ +l_ok +convertJpegToPSEmbed(const char *filein, + const char *fileout) +{ +char *outstr; +l_int32 w, h, nbytes, ret; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertJpegToPSEmbed"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + /* Generate the ascii encoded jpeg data */ + if ((cid = l_generateJpegData(filein, 1)) == NULL) + return ERROR_INT("jpeg data not made", procName, 1); + w = cid->w; + h = cid->h; + + /* Scale for 20 pt boundary and otherwise full filling + * in one direction on 8.5 x 11 inch device */ + xpt = 20.0; + ypt = 20.0; + if (w * 11.0 > h * 8.5) { + wpt = 572.0; /* 612 - 2 * 20 */ + hpt = wpt * (l_float32)h / (l_float32)w; + } else { + hpt = 752.0; /* 792 - 2 * 20 */ + wpt = hpt * (l_float32)w / (l_float32)h; + } + + /* Generate the PS. + * The bounding box information should be inserted (default). */ + outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + nbytes = strlen(outstr); + + ret = l_binaryWrite(fileout, "w", outstr, nbytes); + LEPT_FREE(outstr); + if (ret) L_ERROR("ps string not written to file\n", procName); + return ret; +} + + +/*! + * \brief convertJpegToPS() + * + * \param[in] filein input jpeg file + * \param[in] fileout output ps file + * \param[in] operation "w" for write; "a" for append + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; + * use 0 for default + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is simpler to use than pixWriteStringPS(), and
+ *          it outputs in level 2 PS as compressed DCT (overlaid
+ *          with ascii85 encoding).
+ *      (2) An output file can contain multiple pages, each with
+ *          multiple images.  The arguments to convertJpegToPS()
+ *          allow you to control placement of jpeg images on multiple
+ *          pages within a PostScript file.
+ *      (3) For the first image written to a file, use "w", which
+ *          opens for write and clears the file.  For all subsequent
+ *          images written to that file, use "a".
+ *      (4) The (x, y) parameters give the LL corner of the image
+ *          relative to the LL corner of the page.  They are in
+ *          units of pixels if scale = 1.0.  If you use (e.g.)
+ *          scale = 2.0, the image is placed at (2x, 2y) on the page,
+ *          and the image dimensions are also doubled.
+ *      (5) Display vs printed resolution:
+ *           * If your display is 75 ppi and your image was created
+ *             at a resolution of 300 ppi, you can get the image
+ *             to print at the same size as it appears on your display
+ *             by either setting scale = 4.0 or by setting  res = 75.
+ *             Both tell the printer to make a 4x enlarged image.
+ *           * If your image is generated at 150 ppi and you use scale = 1,
+ *             it will be rendered such that 150 pixels correspond
+ *             to 72 pts (1 inch on the printer).  This function does
+ *             the conversion from pixels (with or without scaling) to
+ *             pts, which are the units that the printer uses.
+ *           * The printer will choose its own resolution to use
+ *             in rendering the image, which will not affect the size
+ *             of the rendered image.  That is because the output
+ *             PostScript file describes the geometry in terms of pts,
+ *             which are defined to be 1/72 inch.  The printer will
+ *             only see the size of the image in pts, through the
+ *             scale and translate parameters and the affine
+ *             transform (the ImageMatrix) of the image.
+ *      (6) To render multiple images on the same page, set
+ *          endpage = FALSE for each image until you get to the
+ *          last, for which you set endpage = TRUE.  This causes the
+ *          "showpage" command to be invoked.  Showpage outputs
+ *          the entire page and clears the raster buffer for the
+ *          next page to be added.  Without a "showpage",
+ *          subsequent images from the next page will overlay those
+ *          previously put down.
+ *      (7) For multiple pages, increment the page number, starting
+ *          with page 1.  This allows PostScript (and PDF) to build
+ *          a page directory, which viewers use for navigation.
+ * 
+ */ +l_ok +convertJpegToPS(const char *filein, + const char *fileout, + const char *operation, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 endpage) +{ +char *outstr; +l_int32 nbytes; + + PROCNAME("convertJpegToPS"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (strcmp(operation, "w") && strcmp(operation, "a")) + return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); + + if (convertJpegToPSString(filein, &outstr, &nbytes, x, y, res, scale, + pageno, endpage)) + return ERROR_INT("ps string not made", procName, 1); + + if (l_binaryWrite(fileout, operation, outstr, nbytes)) { + LEPT_FREE(outstr); + return ERROR_INT("ps string not written to file", procName, 1); + } + + LEPT_FREE(outstr); + return 0; +} + + +/*! + * \brief convertJpegToPSString() + * + * Generates PS string in jpeg format from jpeg file + * + * \param[in] filein input jpeg file + * \param[out] poutstr PS string + * \param[out] pnbytes number of bytes in PS string + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; + * use 0 for default + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For usage, see convertJpegToPS()
+ * 
+ */ +static l_ok +convertJpegToPSString(const char *filein, + char **poutstr, + l_int32 *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 endpage) +{ +char *outstr; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertJpegToPSString"); + + if (!poutstr) + return ERROR_INT("&outstr not defined", procName, 1); + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *poutstr = NULL; + *pnbytes = 0; + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + + /* Generate the ascii encoded jpeg data */ + if ((cid = l_generateJpegData(filein, 1)) == NULL) + return ERROR_INT("jpeg data not made", procName, 1); + + /* Get scaled location in pts. Guess the input scan resolution + * based on the input parameter %res, the resolution data in + * the pix, and the size of the image. */ + if (scale == 0.0) + scale = 1.0; + if (res <= 0) { + if (cid->res > 0) + res = cid->res; + else + res = DefaultInputRes; + } + + /* Get scaled location in pts */ + if (scale == 0.0) + scale = 1.0; + xpt = scale * x * 72. / res; + ypt = scale * y * 72. / res; + wpt = scale * cid->w * 72. / res; + hpt = scale * cid->h * 72. / res; + + if (pageno == 0) + pageno = 1; + +#if DEBUG_JPEG + fprintf(stderr, "w = %d, h = %d, bps = %d, spp = %d\n", + cid->w, cid->h, cid->bps, cid->spp); + fprintf(stderr, "comp bytes = %ld, nbytes85 = %ld, ratio = %5.3f\n", + (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85, + (l_float32)cid->nbytes85 / (l_float32)cid->nbytescomp); + fprintf(stderr, "xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", + xpt, ypt, wpt, hpt); +#endif /* DEBUG_JPEG */ + + /* Generate the PS */ + outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + *poutstr = outstr; + *pnbytes = strlen(outstr); + return 0; +} + + +/*! + * \brief generateJpegPS() + * + * \param[in] filein [optional] input jpeg filename; can be null + * \param[in] cid jpeg compressed image data + * \param[in] xpt, ypt location of LL corner of image, in pts, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] wpt, hpt rendered image size in pts + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return PS string, or NULL on error + * + *
+ * Notes:
+ *      (1) Low-level function.
+ * 
+ */ +static char * +generateJpegPS(const char *filein, + L_COMP_DATA *cid, + l_float32 xpt, + l_float32 ypt, + l_float32 wpt, + l_float32 hpt, + l_int32 pageno, + l_int32 endpage) +{ +l_int32 w, h, bps, spp; +char *outstr; +char bigbuf[Bufsize]; +SARRAY *sa; + + PROCNAME("generateJpegPS"); + + if (!cid) + return (char *)ERROR_PTR("jpeg data not defined", procName, NULL); + w = cid->w; + h = cid->h; + bps = cid->bps; + spp = cid->spp; + + sa = sarrayCreate(50); + sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY); + sarrayAddString(sa, "%%Creator: leptonica", L_COPY); + if (filein) + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); + else + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Jpeg compressed PS"); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); + + if (var_PS_WRITE_BOUNDING_BOX == 1) { + snprintf(bigbuf, sizeof(bigbuf), + "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", + xpt, ypt, xpt + wpt, ypt + hpt); + sarrayAddString(sa, bigbuf, L_COPY); + } + + sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY); + sarrayAddString(sa, "%%EndComments", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); + sarrayAddString(sa, bigbuf, L_COPY); + + sarrayAddString(sa, "save", L_COPY); + sarrayAddString(sa, + "/RawData currentfile /ASCII85Decode filter def", L_COPY); + sarrayAddString(sa, "/Data RawData << >> /DCTDecode filter def", L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); + sarrayAddString(sa, bigbuf, L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); + sarrayAddString(sa, bigbuf, L_COPY); + + if (spp == 1) + sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); + else if (spp == 3) + sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY); + else /*spp == 4 */ + sarrayAddString(sa, "/DeviceCMYK setcolorspace", L_COPY); + + sarrayAddString(sa, "{ << /ImageType 1", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, " /DataSource Data", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /BitsPerComponent %d", bps); + sarrayAddString(sa, bigbuf, L_COPY); + + if (spp == 1) + sarrayAddString(sa, " /Decode [0 1]", L_COPY); + else if (spp == 3) + sarrayAddString(sa, " /Decode [0 1 0 1 0 1]", L_COPY); + else /* spp == 4 */ + sarrayAddString(sa, " /Decode [0 1 0 1 0 1 0 1]", L_COPY); + + sarrayAddString(sa, " >> image", L_COPY); + sarrayAddString(sa, " Data closefile", L_COPY); + sarrayAddString(sa, " RawData flushfile", L_COPY); + if (endpage == TRUE) + sarrayAddString(sa, " showpage", L_COPY); + sarrayAddString(sa, " restore", L_COPY); + sarrayAddString(sa, "} exec", L_COPY); + + /* Insert the ascii85 jpeg data; this is now owned by sa */ + sarrayAddString(sa, cid->data85, L_INSERT); + cid->data85 = NULL; /* it has been transferred and destroyed */ + + /* Generate and return the output string */ + outstr = sarrayToString(sa, 1); + sarrayDestroy(&sa); + return outstr; +} + + +/*-------------------------------------------------------------* + * For ccitt g4 compressed images * + *-------------------------------------------------------------*/ +/*! + * \brief convertG4ToPSEmbed() + * + * \param[in] filein input tiff file + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function takes a g4 compressed tif file as input and
+ *          generates a g4 compressed, ascii85 encoded PS file, with
+ *          a bounding box.
+ *      (2) The bounding box is required when a program such as TeX
+ *          (through epsf) places and rescales the image.
+ *      (3) The bounding box is sized for fitting the image to an
+ *          8.5 x 11.0 inch page.
+ *      (4) We paint this through a mask, over whatever is below.
+ * 
+ */ +l_ok +convertG4ToPSEmbed(const char *filein, + const char *fileout) +{ +char *outstr; +l_int32 w, h, nbytes, ret; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertG4ToPSEmbed"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((cid = l_generateG4Data(filein, 1)) == NULL) + return ERROR_INT("g4 data not made", procName, 1); + w = cid->w; + h = cid->h; + + /* Scale for 20 pt boundary and otherwise full filling + * in one direction on 8.5 x 11 inch device */ + xpt = 20.0; + ypt = 20.0; + if (w * 11.0 > h * 8.5) { + wpt = 572.0; /* 612 - 2 * 20 */ + hpt = wpt * (l_float32)h / (l_float32)w; + } else { + hpt = 752.0; /* 792 - 2 * 20 */ + wpt = hpt * (l_float32)w / (l_float32)h; + } + + /* Generate the PS, painting through the image mask. + * The bounding box information should be inserted (default). */ + outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1, 1); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + nbytes = strlen(outstr); + + ret = l_binaryWrite(fileout, "w", outstr, nbytes); + LEPT_FREE(outstr); + if (ret) L_ERROR("ps string not written to file\n", procName); + return ret; +} + + +/*! + * \brief convertG4ToPS() + * + * \param[in] filein input tiff g4 file + * \param[in] fileout output ps file + * \param[in] operation "w" for write; "a" for append + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; typ. values + * are 300 and 600; use 0 for automatic determination + * based on image size + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] maskflag boolean: use TRUE if just painting through fg; + * FALSE if painting both fg and bg. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See the usage comments in convertJpegToPS(), some of
+ *          which are repeated here.
+ *      (2) This is a wrapper for tiff g4.  The PostScript that
+ *          is generated is expanded by about 5/4 (due to the
+ *          ascii85 encoding.  If you convert to pdf (ps2pdf), the
+ *          ascii85 decoder is automatically invoked, so that the
+ *          pdf wrapped g4 file is essentially the same size as
+ *          the original g4 file.  It's useful to have the PS
+ *          file ascii85 encoded, because many printers will not
+ *          print binary PS files.
+ *      (3) For the first image written to a file, use "w", which
+ *          opens for write and clears the file.  For all subsequent
+ *          images written to that file, use "a".
+ *      (4) To render multiple images on the same page, set
+ *          endpage = FALSE for each image until you get to the
+ *          last, for which you set endpage = TRUE.  This causes the
+ *          "showpage" command to be invoked.  Showpage outputs
+ *          the entire page and clears the raster buffer for the
+ *          next page to be added.  Without a "showpage",
+ *          subsequent images from the next page will overlay those
+ *          previously put down.
+ *      (5) For multiple images to the same page, where you are writing
+ *          both jpeg and tiff-g4, you have two options:
+ *           (a) write the g4 first, as either image (maskflag == FALSE)
+ *               or imagemask (maskflag == TRUE), and then write the
+ *               jpeg over it.
+ *           (b) write the jpeg first and as the last item, write
+ *               the g4 as an imagemask (maskflag == TRUE), to paint
+ *               through the foreground only.
+ *          We have this flexibility with the tiff-g4 because it is 1 bpp.
+ *      (6) For multiple pages, increment the page number, starting
+ *          with page 1.  This allows PostScript (and PDF) to build
+ *          a page directory, which viewers use for navigation.
+ * 
+ */ +l_ok +convertG4ToPS(const char *filein, + const char *fileout, + const char *operation, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 maskflag, + l_int32 endpage) +{ +char *outstr; +l_int32 nbytes, ret; + + PROCNAME("convertG4ToPS"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (strcmp(operation, "w") && strcmp(operation, "a")) + return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); + + if (convertG4ToPSString(filein, &outstr, &nbytes, x, y, res, scale, + pageno, maskflag, endpage)) + return ERROR_INT("ps string not made", procName, 1); + + ret = l_binaryWrite(fileout, operation, outstr, nbytes); + LEPT_FREE(outstr); + if (ret) + return ERROR_INT("ps string not written to file", procName, 1); + return 0; +} + + +/*! + * \brief convertG4ToPSString() + * + * \param[in] filein input tiff g4 file + * \param[out] poutstr PS string + * \param[out] pnbytes number of bytes in PS string + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; typ. values + * are 300 and 600; use 0 for automatic determination + * based on image size + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] maskflag boolean: use TRUE if just painting through fg; + * FALSE if painting both fg and bg. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates PS string in G4 compressed tiff format from G4 tiff file.
+ *      (2) For usage, see convertG4ToPS().
+ * 
+ */ +static l_ok +convertG4ToPSString(const char *filein, + char **poutstr, + l_int32 *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 maskflag, + l_int32 endpage) +{ +char *outstr; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertG4ToPSString"); + + if (!poutstr) + return ERROR_INT("&outstr not defined", procName, 1); + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *poutstr = NULL; + *pnbytes = 0; + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + + if ((cid = l_generateG4Data(filein, 1)) == NULL) + return ERROR_INT("g4 data not made", procName, 1); + + /* Get scaled location in pts. Guess the input scan resolution + * based on the input parameter %res, the resolution data in + * the pix, and the size of the image. */ + if (scale == 0.0) + scale = 1.0; + if (res <= 0) { + if (cid->res > 0) { + res = cid->res; + } else { + if (cid->h <= 3509) /* A4 height at 300 ppi */ + res = 300; + else + res = 600; + } + } + xpt = scale * x * 72. / res; + ypt = scale * y * 72. / res; + wpt = scale * cid->w * 72. / res; + hpt = scale * cid->h * 72. / res; + + if (pageno == 0) + pageno = 1; + +#if DEBUG_G4 + fprintf(stderr, "w = %d, h = %d, minisblack = %d\n", + cid->w, cid->h, cid->minisblack); + fprintf(stderr, "comp bytes = %ld, nbytes85 = %ld\n", + (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85); + fprintf(stderr, "xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", + xpt, ypt, wpt, hpt); +#endif /* DEBUG_G4 */ + + /* Generate the PS */ + outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt, + maskflag, pageno, endpage); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + *poutstr = outstr; + *pnbytes = strlen(outstr); + return 0; +} + + +/*! + * \brief generateG4PS() + * + * \param[in] filein [optional] input tiff g4 file; can be null + * \param[in] cid g4 compressed image data + * \param[in] xpt, ypt location of LL corner of image, in pts, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] wpt, hpt rendered image size in pts + * \param[in] maskflag boolean: use TRUE if just painting through fg; + * FALSE if painting both fg and bg. + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return PS string, or NULL on error + * + *
+ * Notes:
+ *      (1) Low-level function.
+ * 
+ */ +static char * +generateG4PS(const char *filein, + L_COMP_DATA *cid, + l_float32 xpt, + l_float32 ypt, + l_float32 wpt, + l_float32 hpt, + l_int32 maskflag, + l_int32 pageno, + l_int32 endpage) +{ +l_int32 w, h; +char *outstr; +char bigbuf[Bufsize]; +SARRAY *sa; + + PROCNAME("generateG4PS"); + + if (!cid) + return (char *)ERROR_PTR("g4 data not defined", procName, NULL); + w = cid->w; + h = cid->h; + + sa = sarrayCreate(50); + sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY); + sarrayAddString(sa, "%%Creator: leptonica", L_COPY); + if (filein) + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); + else + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: G4 compressed PS"); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); + + if (var_PS_WRITE_BOUNDING_BOX == 1) { + snprintf(bigbuf, sizeof(bigbuf), + "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", + xpt, ypt, xpt + wpt, ypt + hpt); + sarrayAddString(sa, bigbuf, L_COPY); + } + + sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY); + sarrayAddString(sa, "%%EndComments", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); + sarrayAddString(sa, bigbuf, L_COPY); + + sarrayAddString(sa, "save", L_COPY); + sarrayAddString(sa, "100 dict begin", L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); + sarrayAddString(sa, bigbuf, L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); + sarrayAddString(sa, bigbuf, L_COPY); + + sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); + + sarrayAddString(sa, "{", L_COPY); + sarrayAddString(sa, + " /RawData currentfile /ASCII85Decode filter def", L_COPY); + sarrayAddString(sa, " << ", L_COPY); + sarrayAddString(sa, " /ImageType 1", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, " /BitsPerComponent 1", L_COPY); + sarrayAddString(sa, " /Interpolate true", L_COPY); + if (cid->minisblack) + sarrayAddString(sa, " /Decode [1 0]", L_COPY); + else /* miniswhite; typical for 1 bpp */ + sarrayAddString(sa, " /Decode [0 1]", L_COPY); + sarrayAddString(sa, " /DataSource RawData", L_COPY); + sarrayAddString(sa, " <<", L_COPY); + sarrayAddString(sa, " /K -1", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Columns %d", w); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Rows %d", h); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, " >> /CCITTFaxDecode filter", L_COPY); + if (maskflag == TRUE) /* just paint through the fg */ + sarrayAddString(sa, " >> imagemask", L_COPY); + else /* Paint full image */ + sarrayAddString(sa, " >> image", L_COPY); + sarrayAddString(sa, " RawData flushfile", L_COPY); + if (endpage == TRUE) + sarrayAddString(sa, " showpage", L_COPY); + sarrayAddString(sa, "}", L_COPY); + + sarrayAddString(sa, "%%BeginData:", L_COPY); + sarrayAddString(sa, "exec", L_COPY); + + /* Insert the ascii85 ccittg4 data; this is now owned by sa */ + sarrayAddString(sa, cid->data85, L_INSERT); + + /* Concat the trailing data */ + sarrayAddString(sa, "%%EndData", L_COPY); + sarrayAddString(sa, "end", L_COPY); + sarrayAddString(sa, "restore", L_COPY); + + outstr = sarrayToString(sa, 1); + sarrayDestroy(&sa); + cid->data85 = NULL; /* it has been transferred and destroyed */ + return outstr; +} + + +/*-------------------------------------------------------------* + * For tiff multipage files * + *-------------------------------------------------------------*/ +/*! + * \brief convertTiffMultipageToPS() + * + * \param[in] filein input tiff multipage file + * \param[in] fileout output ps file + * \param[in] fillfract factor for filling 8.5 x 11 inch page; + * use 0.0 for DefaultFillFraction + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This converts a multipage tiff file of binary page images
+ *          into a ccitt g4 compressed PS file.
+ *      (2) If the images are generated from a standard resolution fax,
+ *          the vertical resolution is doubled to give a normal-looking
+ *          aspect ratio.
+ * 
+ */ +l_ok +convertTiffMultipageToPS(const char *filein, + const char *fileout, + l_float32 fillfract) +{ +char *tempfile; +l_int32 i, npages, w, h, istiff; +l_float32 scale; +PIX *pix, *pixs; +FILE *fp; + + PROCNAME("convertTiffMultipageToPS"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((fp = fopenReadStream(filein)) == NULL) + return ERROR_INT("file not found", procName, 1); + istiff = fileFormatIsTiff(fp); + if (!istiff) { + fclose(fp); + return ERROR_INT("file not tiff format", procName, 1); + } + tiffGetCount(fp, &npages); + fclose(fp); + + if (fillfract == 0.0) + fillfract = DefaultFillFraction; + + for (i = 0; i < npages; i++) { + if ((pix = pixReadTiff(filein, i)) == NULL) + return ERROR_INT("pix not made", procName, 1); + + pixGetDimensions(pix, &w, &h, NULL); + if (w == 1728 && h < w) /* it's a std res fax */ + pixs = pixScale(pix, 1.0, 2.0); + else + pixs = pixClone(pix); + + tempfile = l_makeTempFilename(); + pixWrite(tempfile, pixs, IFF_TIFF_G4); + scale = L_MIN(fillfract * 2550 / w, fillfract * 3300 / h); + if (i == 0) + convertG4ToPS(tempfile, fileout, "w", 0, 0, 300, scale, + i + 1, FALSE, TRUE); + else + convertG4ToPS(tempfile, fileout, "a", 0, 0, 300, scale, + i + 1, FALSE, TRUE); + lept_rmfile(tempfile); + LEPT_FREE(tempfile); + pixDestroy(&pix); + pixDestroy(&pixs); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * For flate (gzip) compressed images (e.g., png) * + *---------------------------------------------------------------------*/ +/*! + * \brief convertFlateToPSEmbed() + * + * \param[in] filein input file -- any format + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function takes any image file as input and generates a
+ *          flate-compressed, ascii85 encoded PS file, with a bounding box.
+ *      (2) The bounding box is required when a program such as TeX
+ *          (through epsf) places and rescales the image.
+ *      (3) The bounding box is sized for fitting the image to an
+ *          8.5 x 11.0 inch page.
+ * 
+ */ +l_ok +convertFlateToPSEmbed(const char *filein, + const char *fileout) +{ +char *outstr; +l_int32 w, h, nbytes, ret; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertFlateToPSEmbed"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + if ((cid = l_generateFlateData(filein, 1)) == NULL) + return ERROR_INT("flate data not made", procName, 1); + w = cid->w; + h = cid->h; + + /* Scale for 20 pt boundary and otherwise full filling + * in one direction on 8.5 x 11 inch device */ + xpt = 20.0; + ypt = 20.0; + if (w * 11.0 > h * 8.5) { + wpt = 572.0; /* 612 - 2 * 20 */ + hpt = wpt * (l_float32)h / (l_float32)w; + } else { + hpt = 752.0; /* 792 - 2 * 20 */ + wpt = hpt * (l_float32)w / (l_float32)h; + } + + /* Generate the PS. + * The bounding box information should be inserted (default). */ + outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + nbytes = strlen(outstr); + + ret = l_binaryWrite(fileout, "w", outstr, nbytes); + LEPT_FREE(outstr); + if (ret) L_ERROR("ps string not written to file\n", procName); + return ret; +} + + +/*! + * \brief convertFlateToPS() + * + * \param[in] filein input file -- any format + * \param[in] fileout output ps file + * \param[in] operation "w" for write; "a" for append + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; + * use 0 for default + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This outputs level 3 PS as flate compressed (overlaid
+ *          with ascii85 encoding).
+ *      (2) An output file can contain multiple pages, each with
+ *          multiple images.  The arguments to convertFlateToPS()
+ *          allow you to control placement of png images on multiple
+ *          pages within a PostScript file.
+ *      (3) For the first image written to a file, use "w", which
+ *          opens for write and clears the file.  For all subsequent
+ *          images written to that file, use "a".
+ *      (4) The (x, y) parameters give the LL corner of the image
+ *          relative to the LL corner of the page.  They are in
+ *          units of pixels if scale = 1.0.  If you use (e.g.)
+ *          scale = 2.0, the image is placed at (2x, 2y) on the page,
+ *          and the image dimensions are also doubled.
+ *      (5) Display vs printed resolution:
+ *           * If your display is 75 ppi and your image was created
+ *             at a resolution of 300 ppi, you can get the image
+ *             to print at the same size as it appears on your display
+ *             by either setting scale = 4.0 or by setting  res = 75.
+ *             Both tell the printer to make a 4x enlarged image.
+ *           * If your image is generated at 150 ppi and you use scale = 1,
+ *             it will be rendered such that 150 pixels correspond
+ *             to 72 pts (1 inch on the printer).  This function does
+ *             the conversion from pixels (with or without scaling) to
+ *             pts, which are the units that the printer uses.
+ *           * The printer will choose its own resolution to use
+ *             in rendering the image, which will not affect the size
+ *             of the rendered image.  That is because the output
+ *             PostScript file describes the geometry in terms of pts,
+ *             which are defined to be 1/72 inch.  The printer will
+ *             only see the size of the image in pts, through the
+ *             scale and translate parameters and the affine
+ *             transform (the ImageMatrix) of the image.
+ *      (6) To render multiple images on the same page, set
+ *          endpage = FALSE for each image until you get to the
+ *          last, for which you set endpage = TRUE.  This causes the
+ *          "showpage" command to be invoked.  Showpage outputs
+ *          the entire page and clears the raster buffer for the
+ *          next page to be added.  Without a "showpage",
+ *          subsequent images from the next page will overlay those
+ *          previously put down.
+ *      (7) For multiple pages, increment the page number, starting
+ *          with page 1.  This allows PostScript (and PDF) to build
+ *          a page directory, which viewers use for navigation.
+ * 
+ */ +l_ok +convertFlateToPS(const char *filein, + const char *fileout, + const char *operation, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 endpage) +{ +char *outstr; +l_int32 nbytes, ret; + + PROCNAME("convertFlateToPS"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + if (strcmp(operation, "w") && strcmp(operation, "a")) + return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); + + if (convertFlateToPSString(filein, &outstr, &nbytes, x, y, res, scale, + pageno, endpage)) + return ERROR_INT("ps string not made", procName, 1); + + ret = l_binaryWrite(fileout, operation, outstr, nbytes); + LEPT_FREE(outstr); + if (ret) L_ERROR("ps string not written to file\n", procName); + return ret; +} + + +/*! + * \brief convertFlateToPSString() + * + * Generates level 3 PS string in flate compressed format. + * + * \param[in] filein input image file + * \param[out] poutstr PS string + * \param[out] pnbytes number of bytes in PS string + * \param[in] x, y location of LL corner of image, in pixels, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] res resolution of the input image, in ppi; + * use 0 for default + * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page. + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned PS character array is a null-terminated
+ *          ascii string.  All the raster data is ascii85 encoded, so
+ *          there are no null bytes embedded in it.
+ *      (2) The raster encoding is made with gzip, the same as that
+ *          in a png file that is compressed without prediction.
+ *          The raster data itself is 25% larger than that in the
+ *          binary form, due to the ascii85 encoding.
+ *
+ *  Usage:  See convertFlateToPS()
+ * 
+ */ +static l_ok +convertFlateToPSString(const char *filein, + char **poutstr, + l_int32 *pnbytes, + l_int32 x, + l_int32 y, + l_int32 res, + l_float32 scale, + l_int32 pageno, + l_int32 endpage) +{ +char *outstr; +l_float32 xpt, ypt, wpt, hpt; +L_COMP_DATA *cid; + + PROCNAME("convertFlateToPSString"); + + if (!poutstr) + return ERROR_INT("&outstr not defined", procName, 1); + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + *pnbytes = 0; + *poutstr = NULL; + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + + if ((cid = l_generateFlateData(filein, 1)) == NULL) + return ERROR_INT("flate data not made", procName, 1); + + /* Get scaled location in pts. Guess the input scan resolution + * based on the input parameter %res, the resolution data in + * the pix, and the size of the image. */ + if (scale == 0.0) + scale = 1.0; + if (res <= 0) { + if (cid->res > 0) + res = cid->res; + else + res = DefaultInputRes; + } + xpt = scale * x * 72. / res; + ypt = scale * y * 72. / res; + wpt = scale * cid->w * 72. / res; + hpt = scale * cid->h * 72. / res; + + if (pageno == 0) + pageno = 1; + +#if DEBUG_FLATE + fprintf(stderr, "w = %d, h = %d, bps = %d, spp = %d\n", + cid->w, cid->h, cid->bps, cid->spp); + fprintf(stderr, "uncomp bytes = %ld, comp bytes = %ld, nbytes85 = %ld\n", + (unsigned long)cid->nbytes, (unsigned long)cid->nbytescomp, + (unsigned long)cid->nbytes85); + fprintf(stderr, "xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", + xpt, ypt, wpt, hpt); +#endif /* DEBUG_FLATE */ + + /* Generate the PS */ + outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage); + l_CIDataDestroy(&cid); + if (!outstr) + return ERROR_INT("outstr not made", procName, 1); + *poutstr = outstr; + *pnbytes = strlen(outstr); + return 0; +} + + +/*! + * \brief generateFlatePS() + * + * \param[in] filein [optional] input filename; can be null + * \param[in] cid flate compressed image data + * \param[in] xpt, ypt location of LL corner of image, in pts, relative + * to the PostScript origin (0,0) at the LL corner + * of the page + * \param[in] wpt, hpt rendered image size in pts + * \param[in] pageno page number; must start with 1; you can use 0 + * if there is only one page + * \param[in] endpage boolean: use TRUE if this is the last image to be + * added to the page; FALSE otherwise + * \return PS string, or NULL on error + */ +static char * +generateFlatePS(const char *filein, + L_COMP_DATA *cid, + l_float32 xpt, + l_float32 ypt, + l_float32 wpt, + l_float32 hpt, + l_int32 pageno, + l_int32 endpage) +{ +l_int32 w, h, bps, spp; +char *outstr; +char bigbuf[Bufsize]; +SARRAY *sa; + + PROCNAME("generateFlatePS"); + + if (!cid) + return (char *)ERROR_PTR("flate data not defined", procName, NULL); + w = cid->w; + h = cid->h; + bps = cid->bps; + spp = cid->spp; + + sa = sarrayCreate(50); + sarrayAddString(sa, "%!PS-Adobe-3.0 EPSF-3.0", L_COPY); + sarrayAddString(sa, "%%Creator: leptonica", L_COPY); + if (filein) + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); + else + snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Flate compressed PS"); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); + + if (var_PS_WRITE_BOUNDING_BOX == 1) { + snprintf(bigbuf, sizeof(bigbuf), + "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", + xpt, ypt, xpt + wpt, ypt + hpt); + sarrayAddString(sa, bigbuf, L_COPY); + } + + sarrayAddString(sa, "%%LanguageLevel: 3", L_COPY); + sarrayAddString(sa, "%%EndComments", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); + sarrayAddString(sa, bigbuf, L_COPY); + + sarrayAddString(sa, "save", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); + sarrayAddString(sa, bigbuf, L_COPY); + + snprintf(bigbuf, sizeof(bigbuf), + "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); + sarrayAddString(sa, bigbuf, L_COPY); + + /* If there is a colormap, add the data; it is now owned by sa */ + if (cid->cmapdata85) { + snprintf(bigbuf, sizeof(bigbuf), + "[ /Indexed /DeviceRGB %d %%set colormap type/size", + cid->ncolors - 1); + sarrayAddString(sa, bigbuf, L_COPY); + sarrayAddString(sa, " <~", L_COPY); + sarrayAddString(sa, cid->cmapdata85, L_INSERT); + sarrayAddString(sa, " ] setcolorspace", L_COPY); + } else if (spp == 1) { + sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); + } else { /* spp == 3 */ + sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY); + } + + sarrayAddString(sa, + "/RawData currentfile /ASCII85Decode filter def", L_COPY); + sarrayAddString(sa, + "/Data RawData << >> /FlateDecode filter def", L_COPY); + + sarrayAddString(sa, "{ << /ImageType 1", L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), " /BitsPerComponent %d", bps); + sarrayAddString(sa, bigbuf, L_COPY); + snprintf(bigbuf, sizeof(bigbuf), + " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); + sarrayAddString(sa, bigbuf, L_COPY); + + if (cid->cmapdata85) { + sarrayAddString(sa, " /Decode [0 255]", L_COPY); + } else if (spp == 1) { + if (bps == 1) /* miniswhite photometry */ + sarrayAddString(sa, " /Decode [1 0]", L_COPY); + else /* bps > 1 */ + sarrayAddString(sa, " /Decode [0 1]", L_COPY); + } else { /* spp == 3 */ + sarrayAddString(sa, " /Decode [0 1 0 1 0 1]", L_COPY); + } + + sarrayAddString(sa, " /DataSource Data", L_COPY); + sarrayAddString(sa, " >> image", L_COPY); + sarrayAddString(sa, " Data closefile", L_COPY); + sarrayAddString(sa, " RawData flushfile", L_COPY); + if (endpage == TRUE) + sarrayAddString(sa, " showpage", L_COPY); + sarrayAddString(sa, " restore", L_COPY); + sarrayAddString(sa, "} exec", L_COPY); + + /* Insert the ascii85 gzipped data; this is now owned by sa */ + sarrayAddString(sa, cid->data85, L_INSERT); + + /* Generate and return the output string */ + outstr = sarrayToString(sa, 1); + sarrayDestroy(&sa); + cid->cmapdata85 = NULL; /* it has been transferred to sa and destroyed */ + cid->data85 = NULL; /* it has been transferred to sa and destroyed */ + return outstr; +} + + +/*---------------------------------------------------------------------* + * Write to memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteMemPS() + * + * \param[out] pdata data of tiff compressed image + * \param[out] psize size of returned data + * \param[in] pix + * \param[in] box [optional] + * \param[in] res can use 0 for default of 300 ppi + * \param[in] scale to prevent scaling, use either 1.0 or 0.0 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStringPS() for usage.
+ *      (2) This is just a wrapper for pixWriteStringPS(), which
+ *          writes uncompressed image data to memory.
+ * 
+ */ +l_ok +pixWriteMemPS(l_uint8 **pdata, + size_t *psize, + PIX *pix, + BOX *box, + l_int32 res, + l_float32 scale) +{ + PROCNAME("pixWriteMemPS"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + + *pdata = (l_uint8 *)pixWriteStringPS(pix, box, res, scale); + *psize = strlen((char *)(*pdata)); + return 0; +} + + +/*-------------------------------------------------------------* + * Converting resolution * + *-------------------------------------------------------------*/ +/*! + * \brief getResLetterPage() + * + * \param[in] w image width, pixels + * \param[in] h image height, pixels + * \param[in] fillfract fraction in linear dimension of full page, + * not to be exceeded; use 0 for default + * \return resolution + */ +l_int32 +getResLetterPage(l_int32 w, + l_int32 h, + l_float32 fillfract) +{ +l_int32 resw, resh, res; + + if (fillfract == 0.0) + fillfract = DefaultFillFraction; + resw = (l_int32)((w * 72.) / (LetterWidth * fillfract)); + resh = (l_int32)((h * 72.) / (LetterHeight * fillfract)); + res = L_MAX(resw, resh); + return res; +} + + +/*! + * \brief getResA4Page() + * + * \param[in] w image width, pixels + * \param[in] h image height, pixels + * \param[in] fillfract fraction in linear dimension of full page, + * not to be exceeded; use 0 for default + * \return resolution + */ +l_int32 +getResA4Page(l_int32 w, + l_int32 h, + l_float32 fillfract) +{ +l_int32 resw, resh, res; + + if (fillfract == 0.0) + fillfract = DefaultFillFraction; + resw = (l_int32)((w * 72.) / (A4Width * fillfract)); + resh = (l_int32)((h * 72.) / (A4Height * fillfract)); + res = L_MAX(resw, resh); + return res; +} + + +/*-------------------------------------------------------------* + * Setting flag for writing bounding box hint * + *-------------------------------------------------------------*/ +void +l_psWriteBoundingBox(l_int32 flag) +{ + var_PS_WRITE_BOUNDING_BOX = flag; +} + + +/* --------------------------------------------*/ +#endif /* USE_PSIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/psio2stub.c b/hgdriver/3rdparty/hgOCR/leptonica/psio2stub.c new file mode 100644 index 0000000..49603c0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/psio2stub.c @@ -0,0 +1,156 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file psio2stub.c + *
+ *
+ *     Stubs for psio2.c functions
+ * 
+ */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !USE_PSIO /* defined in environ.h */ +/* --------------------------------------------*/ + +l_ok pixWritePSEmbed(const char *filein, const char *fileout) +{ + return ERROR_INT("function not present", "pixWritePSEmbed", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamPS(FILE *fp, PIX *pix, BOX *box, l_int32 res, + l_float32 scale) +{ + return ERROR_INT("function not present", "pixWriteStreamPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +char * pixWriteStringPS(PIX *pixs, BOX *box, l_int32 res, l_float32 scale) +{ + return (char *)ERROR_PTR("function not present", "pixWriteStringPS", NULL); +} + +/* ----------------------------------------------------------------------*/ + +char * generateUncompressedPS(char *hexdata, l_int32 w, l_int32 h, l_int32 d, + l_int32 psbpl, l_int32 bps, l_float32 xpt, + l_float32 ypt, l_float32 wpt, l_float32 hpt, + l_int32 boxflag) +{ + return (char *)ERROR_PTR("function not present", + "generateUncompressedPS", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertJpegToPSEmbed(const char *filein, const char *fileout) +{ + return ERROR_INT("function not present", "convertJpegToPSEmbed", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertJpegToPS(const char *filein, const char *fileout, + const char *operation, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, l_int32 pageno, + l_int32 endpage) +{ + return ERROR_INT("function not present", "convertJpegToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertG4ToPSEmbed(const char *filein, const char *fileout) +{ + return ERROR_INT("function not present", "convertG4ToPSEmbed", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertG4ToPS(const char *filein, const char *fileout, + const char *operation, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, l_int32 pageno, + l_int32 maskflag, l_int32 endpage) +{ + return ERROR_INT("function not present", "convertG4ToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertTiffMultipageToPS(const char *filein, const char *fileout, + l_float32 fillfract) +{ + return ERROR_INT("function not present", "convertTiffMultipageToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertFlateToPSEmbed(const char *filein, const char *fileout) +{ + return ERROR_INT("function not present", "convertFlateToPSEmbed", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok convertFlateToPS(const char *filein, const char *fileout, + const char *operation, l_int32 x, l_int32 y, + l_int32 res, l_float32 scale, l_int32 pageno, + l_int32 endpage) +{ + return ERROR_INT("function not present", "convertFlateToPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemPS(l_uint8 **pdata, size_t *psize, PIX *pix, BOX *box, + l_int32 res, l_float32 scale) +{ + return ERROR_INT("function not present", "pixWriteMemPS", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_int32 getResLetterPage(l_int32 w, l_int32 h, l_float32 fillfract) +{ + return ERROR_INT("function not present", "getResLetterPage", 1); +} + +/* ----------------------------------------------------------------------*/ + +void l_psWriteBoundingBox(l_int32 flag) +{ + L_ERROR("function not present\n", "l_psWriteBoundingBox"); + return; +} + +/* --------------------------------------------*/ +#endif /* !USE_PSIO */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ptabasic.c b/hgdriver/3rdparty/hgOCR/leptonica/ptabasic.c new file mode 100644 index 0000000..ea2961a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ptabasic.c @@ -0,0 +1,1551 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file ptabasic.c + *
+ *
+ *      Pta creation, destruction, copy, clone, empty
+ *           PTA            *ptaCreate()
+ *           PTA            *ptaCreateFromNuma()
+ *           void            ptaDestroy()
+ *           PTA            *ptaCopy()
+ *           PTA            *ptaCopyRange()
+ *           PTA            *ptaClone()
+ *           l_int32         ptaEmpty()
+ *
+ *      Pta array extension
+ *           l_int32         ptaAddPt()
+ *           static l_int32  ptaExtendArrays()
+ *
+ *      Pta insertion and removal
+ *           l_int32         ptaInsertPt()
+ *           l_int32         ptaRemovePt()
+ *
+ *      Pta accessors
+ *           l_int32         ptaGetRefcount()
+ *           l_int32         ptaChangeRefcount()
+ *           l_int32         ptaGetCount()
+ *           l_int32         ptaGetPt()
+ *           l_int32         ptaGetIPt()
+ *           l_int32         ptaSetPt()
+ *           l_int32         ptaGetArrays()
+ *
+ *      Pta serialized for I/O
+ *           PTA            *ptaRead()
+ *           PTA            *ptaReadStream()
+ *           PTA            *ptaReadMem()
+ *           l_int32         ptaWriteDebug()
+ *           l_int32         ptaWrite()
+ *           l_int32         ptaWriteStream()
+ *           l_int32         ptaWriteMem()
+ *
+ *      Ptaa creation, destruction
+ *           PTAA           *ptaaCreate()
+ *           void            ptaaDestroy()
+ *
+ *      Ptaa array extension
+ *           l_int32         ptaaAddPta()
+ *           static l_int32  ptaaExtendArray()
+ *
+ *      Ptaa accessors
+ *           l_int32         ptaaGetCount()
+ *           l_int32         ptaaGetPta()
+ *           l_int32         ptaaGetPt()
+ *
+ *      Ptaa array modifiers
+ *           l_int32         ptaaInitFull()
+ *           l_int32         ptaaReplacePta()
+ *           l_int32         ptaaAddPt()
+ *           l_int32         ptaaTruncate()
+ *
+ *      Ptaa serialized for I/O
+ *           PTAA           *ptaaRead()
+ *           PTAA           *ptaaReadStream()
+ *           PTAA           *ptaaReadMem()
+ *           l_int32         ptaaWrite()
+ *           l_int32         ptaaWriteStream()
+ *           l_int32         ptaaWriteMem()
+ * 
+ */ + +#include +#include "allheaders.h" + +static const l_uint32 MaxPtrArraySize = 10000000; +static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 ptaExtendArrays(PTA *pta); +static l_int32 ptaaExtendArray(PTAA *ptaa); + + +/*---------------------------------------------------------------------* + * Pta creation, destruction, copy, clone * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaCreate() + * + * \param[in] n initial array sizes + * \return pta, or NULL on error. + */ +PTA * +ptaCreate(l_int32 n) +{ +PTA *pta; + + PROCNAME("ptaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + pta = (PTA *)LEPT_CALLOC(1, sizeof(PTA)); + pta->n = 0; + pta->nalloc = n; + ptaChangeRefcount(pta, 1); /* sets to 1 */ + pta->x = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32)); + pta->y = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32)); + if (!pta->x || !pta->y) { + ptaDestroy(&pta); + return (PTA *)ERROR_PTR("x and y arrays not both made", procName, NULL); + } + + return pta; +} + + +/*! + * \brief ptaCreateFromNuma() + * + * \param[in] nax [optional] can be null + * \param[in] nay + * \return pta, or NULL on error. + */ +PTA * +ptaCreateFromNuma(NUMA *nax, + NUMA *nay) +{ +l_int32 i, n; +l_float32 startx, delx, xval, yval; +PTA *pta; + + PROCNAME("ptaCreateFromNuma"); + + if (!nay) + return (PTA *)ERROR_PTR("nay not defined", procName, NULL); + n = numaGetCount(nay); + if (nax && numaGetCount(nax) != n) + return (PTA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); + + pta = ptaCreate(n); + numaGetParameters(nay, &startx, &delx); + for (i = 0; i < n; i++) { + if (nax) + numaGetFValue(nax, i, &xval); + else /* use implicit x values from nay */ + xval = startx + i * delx; + numaGetFValue(nay, i, &yval); + ptaAddPt(pta, xval, yval); + } + + return pta; +} + + +/*! + * \brief ptaDestroy() + * + * \param[in,out] ppta will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pta.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +ptaDestroy(PTA **ppta) +{ +PTA *pta; + + PROCNAME("ptaDestroy"); + + if (ppta == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((pta = *ppta) == NULL) + return; + + ptaChangeRefcount(pta, -1); + if (ptaGetRefcount(pta) <= 0) { + LEPT_FREE(pta->x); + LEPT_FREE(pta->y); + LEPT_FREE(pta); + } + + *ppta = NULL; + return; +} + + +/*! + * \brief ptaCopy() + * + * \param[in] pta + * \return copy of pta, or NULL on error + */ +PTA * +ptaCopy(PTA *pta) +{ +l_int32 i; +l_float32 x, y; +PTA *npta; + + PROCNAME("ptaCopy"); + + if (!pta) + return (PTA *)ERROR_PTR("pta not defined", procName, NULL); + + if ((npta = ptaCreate(pta->nalloc)) == NULL) + return (PTA *)ERROR_PTR("npta not made", procName, NULL); + + for (i = 0; i < pta->n; i++) { + ptaGetPt(pta, i, &x, &y); + ptaAddPt(npta, x, y); + } + + return npta; +} + + +/*! + * \brief ptaCopyRange() + * + * \param[in] ptas + * \param[in] istart starting index in ptas + * \param[in] iend ending index in ptas; use 0 to copy to end + * \return 0 if OK, 1 on error + */ +PTA * +ptaCopyRange(PTA *ptas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i, x, y; +PTA *ptad; + + PROCNAME("ptaCopyRange"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + n = ptaGetCount(ptas); + if (istart < 0) + istart = 0; + if (istart >= n) + return (PTA *)ERROR_PTR("istart out of bounds", procName, NULL); + if (iend <= 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return (PTA *)ERROR_PTR("istart > iend; no pts", procName, NULL); + + if ((ptad = ptaCreate(iend - istart + 1)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = istart; i <= iend; i++) { + ptaGetIPt(ptas, i, &x, &y); + ptaAddPt(ptad, x, y); + } + + return ptad; +} + + +/*! + * \brief ptaClone() + * + * \param[in] pta + * \return ptr to same pta, or NULL on error + */ +PTA * +ptaClone(PTA *pta) +{ + PROCNAME("ptaClone"); + + if (!pta) + return (PTA *)ERROR_PTR("pta not defined", procName, NULL); + + ptaChangeRefcount(pta, 1); + return pta; +} + + +/*! + * \brief ptaEmpty() + * + * \param[in] pta + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      This only resets the Pta::n field, for reuse
+ * 
+ */ +l_ok +ptaEmpty(PTA *pta) +{ + PROCNAME("ptaEmpty"); + + if (!pta) + return ERROR_INT("ptad not defined", procName, 1); + pta->n = 0; + return 0; +} + + +/*---------------------------------------------------------------------* + * Pta array extension * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaAddPt() + * + * \param[in] pta + * \param[in] x, y + * \return 0 if OK, 1 on error + */ +l_ok +ptaAddPt(PTA *pta, + l_float32 x, + l_float32 y) +{ +l_int32 n; + + PROCNAME("ptaAddPt"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + n = pta->n; + if (n >= pta->nalloc) + ptaExtendArrays(pta); + pta->x[n] = x; + pta->y[n] = y; + pta->n++; + + return 0; +} + + +/*! + * \brief ptaExtendArrays() + * + * \param[in] pta + * \return 0 if OK; 1 on error + */ +static l_int32 +ptaExtendArrays(PTA *pta) +{ + PROCNAME("ptaExtendArrays"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + if ((pta->x = (l_float32 *)reallocNew((void **)&pta->x, + sizeof(l_float32) * pta->nalloc, + 2 * sizeof(l_float32) * pta->nalloc)) == NULL) + return ERROR_INT("new x array not returned", procName, 1); + if ((pta->y = (l_float32 *)reallocNew((void **)&pta->y, + sizeof(l_float32) * pta->nalloc, + 2 * sizeof(l_float32) * pta->nalloc)) == NULL) + return ERROR_INT("new y array not returned", procName, 1); + + pta->nalloc = 2 * pta->nalloc; + return 0; +} + + +/*---------------------------------------------------------------------* + * Pta insertion and removal * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaInsertPt() + * + * \param[in] pta + * \param[in] index at which pt is to be inserted + * \param[in] x, y point values + * \return 0 if OK; 1 on error + */ +l_ok +ptaInsertPt(PTA *pta, + l_int32 index, + l_int32 x, + l_int32 y) +{ +l_int32 i, n; + + PROCNAME("ptaInsertPt"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + n = ptaGetCount(pta); + if (index < 0 || index > n) + return ERROR_INT("index not in {0...n}", procName, 1); + + if (n > pta->nalloc) + ptaExtendArrays(pta); + pta->n++; + for (i = n; i > index; i--) { + pta->x[i] = pta->x[i - 1]; + pta->y[i] = pta->y[i - 1]; + } + pta->x[index] = x; + pta->y[index] = y; + return 0; +} + + +/*! + * \brief ptaRemovePt() + * + * \param[in] pta + * \param[in] index of point to be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This shifts pta[i] --> pta[i - 1] for all i > index.
+ *      (2) It should not be used repeatedly on large arrays,
+ *          because the function is O(n).
+ * 
+ */ +l_ok +ptaRemovePt(PTA *pta, + l_int32 index) +{ +l_int32 i, n; + + PROCNAME("ptaRemovePt"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + n = ptaGetCount(pta); + if (index < 0 || index >= n) + return ERROR_INT("index not in {0...n - 1}", procName, 1); + + /* Remove the point */ + for (i = index + 1; i < n; i++) { + pta->x[i - 1] = pta->x[i]; + pta->y[i - 1] = pta->y[i]; + } + pta->n--; + return 0; +} + + +/*---------------------------------------------------------------------* + * Pta accessors * + *---------------------------------------------------------------------*/ +l_int32 +ptaGetRefcount(PTA *pta) +{ + PROCNAME("ptaGetRefcount"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + return pta->refcount; +} + + +l_int32 +ptaChangeRefcount(PTA *pta, + l_int32 delta) +{ + PROCNAME("ptaChangeRefcount"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + pta->refcount += delta; + return 0; +} + + +/*! + * \brief ptaGetCount() + * + * \param[in] pta + * \return count, or 0 if no pta + */ +l_int32 +ptaGetCount(PTA *pta) +{ + PROCNAME("ptaGetCount"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 0); + + return pta->n; +} + + +/*! + * \brief ptaGetPt() + * + * \param[in] pta + * \param[in] index into arrays + * \param[out] px [optional] float x value + * \param[out] py [optional] float y value + * \return 0 if OK; 1 on error + */ +l_ok +ptaGetPt(PTA *pta, + l_int32 index, + l_float32 *px, + l_float32 *py) +{ + PROCNAME("ptaGetPt"); + + if (px) *px = 0; + if (py) *py = 0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (index < 0 || index >= pta->n) + return ERROR_INT("invalid index", procName, 1); + + if (px) *px = pta->x[index]; + if (py) *py = pta->y[index]; + return 0; +} + + +/*! + * \brief ptaGetIPt() + * + * \param[in] pta + * \param[in] index into arrays + * \param[out] px [optional] integer x value + * \param[out] py [optional] integer y value + * \return 0 if OK; 1 on error + */ +l_ok +ptaGetIPt(PTA *pta, + l_int32 index, + l_int32 *px, + l_int32 *py) +{ + PROCNAME("ptaGetIPt"); + + if (px) *px = 0; + if (py) *py = 0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (index < 0 || index >= pta->n) + return ERROR_INT("invalid index", procName, 1); + + if (px) *px = (l_int32)(pta->x[index] + 0.5); + if (py) *py = (l_int32)(pta->y[index] + 0.5); + return 0; +} + + +/*! + * \brief ptaSetPt() + * + * \param[in] pta + * \param[in] index into arrays + * \param[in] x, y + * \return 0 if OK; 1 on error + */ +l_ok +ptaSetPt(PTA *pta, + l_int32 index, + l_float32 x, + l_float32 y) +{ + PROCNAME("ptaSetPt"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (index < 0 || index >= pta->n) + return ERROR_INT("invalid index", procName, 1); + + pta->x[index] = x; + pta->y[index] = y; + return 0; +} + + +/*! + * \brief ptaGetArrays() + * + * \param[in] pta + * \param[out] pnax [optional] numa of x array + * \param[out] pnay [optional] numa of y array + * \return 0 if OK; 1 on error or if pta is empty + * + *
+ * Notes:
+ *      (1) This copies the internal arrays into new Numas.
+ * 
+ */ +l_ok +ptaGetArrays(PTA *pta, + NUMA **pnax, + NUMA **pnay) +{ +l_int32 i, n; +NUMA *nax, *nay; + + PROCNAME("ptaGetArrays"); + + if (!pnax && !pnay) + return ERROR_INT("no output requested", procName, 1); + if (pnax) *pnax = NULL; + if (pnay) *pnay = NULL; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) == 0) + return ERROR_INT("pta is empty", procName, 1); + + if (pnax) { + if ((nax = numaCreate(n)) == NULL) + return ERROR_INT("nax not made", procName, 1); + *pnax = nax; + for (i = 0; i < n; i++) + nax->array[i] = pta->x[i]; + nax->n = n; + } + if (pnay) { + if ((nay = numaCreate(n)) == NULL) + return ERROR_INT("nay not made", procName, 1); + *pnay = nay; + for (i = 0; i < n; i++) + nay->array[i] = pta->y[i]; + nay->n = n; + } + return 0; +} + + +/*---------------------------------------------------------------------* + * Pta serialized for I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaRead() + * + * \param[in] filename + * \return pta, or NULL on error + */ +PTA * +ptaRead(const char *filename) +{ +FILE *fp; +PTA *pta; + + PROCNAME("ptaRead"); + + if (!filename) + return (PTA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PTA *)ERROR_PTR("stream not opened", procName, NULL); + pta = ptaReadStream(fp); + fclose(fp); + if (!pta) + return (PTA *)ERROR_PTR("pta not read", procName, NULL); + return pta; +} + + +/*! + * \brief ptaReadStream() + * + * \param[in] fp file stream + * \return pta, or NULL on error + */ +PTA * +ptaReadStream(FILE *fp) +{ +char typestr[128]; /* hardcoded below in fscanf */ +l_int32 i, n, ix, iy, type, version; +l_float32 x, y; +PTA *pta; + + PROCNAME("ptaReadStream"); + + if (!fp) + return (PTA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\n Pta Version %d\n", &version) != 1) + return (PTA *)ERROR_PTR("not a pta file", procName, NULL); + if (version != PTA_VERSION_NUMBER) + return (PTA *)ERROR_PTR("invalid pta version", procName, NULL); + if (fscanf(fp, " Number of pts = %d; format = %127s\n", &n, typestr) != 2) + return (PTA *)ERROR_PTR("not a pta file", procName, NULL); + if (!strcmp(typestr, "float")) + type = 0; + else /* typestr is "integer" */ + type = 1; + + if ((pta = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + for (i = 0; i < n; i++) { + if (type == 0) { /* data is float */ + if (fscanf(fp, " (%f, %f)\n", &x, &y) != 2) { + ptaDestroy(&pta); + return (PTA *)ERROR_PTR("error reading floats", procName, NULL); + } + ptaAddPt(pta, x, y); + } else { /* data is integer */ + if (fscanf(fp, " (%d, %d)\n", &ix, &iy) != 2) { + ptaDestroy(&pta); + return (PTA *)ERROR_PTR("error reading ints", procName, NULL); + } + ptaAddPt(pta, ix, iy); + } + } + + return pta; +} + + +/*! + * \brief ptaReadMem() + * + * \param[in] data serialization in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return pta, or NULL on error + */ +PTA * +ptaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +PTA *pta; + + PROCNAME("ptaReadMem"); + + if (!data) + return (PTA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PTA *)ERROR_PTR("stream not opened", procName, NULL); + + pta = ptaReadStream(fp); + fclose(fp); + if (!pta) L_ERROR("pta not read\n", procName); + return pta; +} + + +/*! + * \brief ptaWriteDebug() + * + * \param[in] filename + * \param[in] pta + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of ptaWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +ptaWriteDebug(const char *filename, + PTA *pta, + l_int32 type) +{ + PROCNAME("ptaWriteDebug"); + + if (LeptDebugOK) { + return ptaWrite(filename, pta, type); + } else { + L_INFO("write to named temp file %s is disabled\n", procName, filename); + return 0; + } +} + + +/*! + * \brief ptaWrite() + * + * \param[in] filename + * \param[in] pta + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + */ +l_ok +ptaWrite(const char *filename, + PTA *pta, + l_int32 type) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("ptaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = ptaWriteStream(fp, pta, type); + fclose(fp); + if (ret) + return ERROR_INT("pta not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief ptaWriteStream() + * + * \param[in] fp file stream + * \param[in] pta + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK; 1 on error + */ +l_ok +ptaWriteStream(FILE *fp, + PTA *pta, + l_int32 type) +{ +l_int32 i, n, ix, iy; +l_float32 x, y; + + PROCNAME("ptaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + n = ptaGetCount(pta); + fprintf(fp, "\n Pta Version %d\n", PTA_VERSION_NUMBER); + if (type == 0) + fprintf(fp, " Number of pts = %d; format = float\n", n); + else /* type == 1 */ + fprintf(fp, " Number of pts = %d; format = integer\n", n); + for (i = 0; i < n; i++) { + if (type == 0) { /* data is float */ + ptaGetPt(pta, i, &x, &y); + fprintf(fp, " (%f, %f)\n", x, y); + } else { /* data is integer */ + ptaGetIPt(pta, i, &ix, &iy); + fprintf(fp, " (%d, %d)\n", ix, iy); + } + } + + return 0; +} + + +/*! + * \brief ptaWriteMem() + * + * \param[out] pdata data of serialized pta; ascii + * \param[out] psize size of returned data + * \param[in] pta + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a pta in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +ptaWriteMem(l_uint8 **pdata, + size_t *psize, + PTA *pta, + l_int32 type) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("ptaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = ptaWriteStream(fp, pta, type); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = ptaWriteStream(fp, pta, type); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*---------------------------------------------------------------------* + * PTAA creation, destruction * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaaCreate() + * + * \param[in] n initial number of ptrs + * \return ptaa, or NULL on error + */ +PTAA * +ptaaCreate(l_int32 n) +{ +PTAA *ptaa; + + PROCNAME("ptaaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + ptaa = (PTAA *)LEPT_CALLOC(1, sizeof(PTAA)); + ptaa->n = 0; + ptaa->nalloc = n; + if ((ptaa->pta = (PTA **)LEPT_CALLOC(n, sizeof(PTA *))) == NULL) { + ptaaDestroy(&ptaa); + return (PTAA *)ERROR_PTR("pta ptrs not made", procName, NULL); + } + return ptaa; +} + + +/*! + * \brief ptaaDestroy() + * + * \param[in,out] pptaa will be set to null before returning + * \return void + */ +void +ptaaDestroy(PTAA **pptaa) +{ +l_int32 i; +PTAA *ptaa; + + PROCNAME("ptaaDestroy"); + + if (pptaa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + + if ((ptaa = *pptaa) == NULL) + return; + + for (i = 0; i < ptaa->n; i++) + ptaDestroy(&ptaa->pta[i]); + LEPT_FREE(ptaa->pta); + + LEPT_FREE(ptaa); + *pptaa = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * PTAA array extension * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaaAddPta() + * + * \param[in] ptaa + * \param[in] pta to be added + * \param[in] copyflag L_INSERT, L_COPY, L_CLONE + * \return 0 if OK, 1 on error + */ +l_ok +ptaaAddPta(PTAA *ptaa, + PTA *pta, + l_int32 copyflag) +{ +l_int32 n; +PTA *ptac; + + PROCNAME("ptaaAddPta"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + if (copyflag == L_INSERT) { + ptac = pta; + } else if (copyflag == L_COPY) { + if ((ptac = ptaCopy(pta)) == NULL) + return ERROR_INT("ptac not made", procName, 1); + } else if (copyflag == L_CLONE) { + if ((ptac = ptaClone(pta)) == NULL) + return ERROR_INT("pta clone not made", procName, 1); + } else { + return ERROR_INT("invalid copyflag", procName, 1); + } + + n = ptaaGetCount(ptaa); + if (n >= ptaa->nalloc) + ptaaExtendArray(ptaa); + ptaa->pta[n] = ptac; + ptaa->n++; + + return 0; +} + + +/*! + * \brief ptaaExtendArray() + * + * \param[in] ptaa + * \return 0 if OK, 1 on error + */ +static l_int32 +ptaaExtendArray(PTAA *ptaa) +{ + PROCNAME("ptaaExtendArray"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + if ((ptaa->pta = (PTA **)reallocNew((void **)&ptaa->pta, + sizeof(PTA *) * ptaa->nalloc, + 2 * sizeof(PTA *) * ptaa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + ptaa->nalloc = 2 * ptaa->nalloc; + return 0; +} + + +/*---------------------------------------------------------------------* + * Ptaa accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaaGetCount() + * + * \param[in] ptaa + * \return count, or 0 if no ptaa + */ +l_int32 +ptaaGetCount(PTAA *ptaa) +{ + PROCNAME("ptaaGetCount"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 0); + + return ptaa->n; +} + + +/*! + * \brief ptaaGetPta() + * + * \param[in] ptaa + * \param[in] index to the i-th pta + * \param[in] accessflag L_COPY or L_CLONE + * \return pta, or NULL on error + */ +PTA * +ptaaGetPta(PTAA *ptaa, + l_int32 index, + l_int32 accessflag) +{ + PROCNAME("ptaaGetPta"); + + if (!ptaa) + return (PTA *)ERROR_PTR("ptaa not defined", procName, NULL); + if (index < 0 || index >= ptaa->n) + return (PTA *)ERROR_PTR("index not valid", procName, NULL); + + if (accessflag == L_COPY) + return ptaCopy(ptaa->pta[index]); + else if (accessflag == L_CLONE) + return ptaClone(ptaa->pta[index]); + else + return (PTA *)ERROR_PTR("invalid accessflag", procName, NULL); +} + + +/*! + * \brief ptaaGetPt() + * + * \param[in] ptaa + * \param[in] ipta to the i-th pta + * \param[in] jpt index to the j-th pt in the pta + * \param[out] px [optional] float x value + * \param[out] py [optional] float y value + * \return 0 if OK; 1 on error + */ +l_ok +ptaaGetPt(PTAA *ptaa, + l_int32 ipta, + l_int32 jpt, + l_float32 *px, + l_float32 *py) +{ +PTA *pta; + + PROCNAME("ptaaGetPt"); + + if (px) *px = 0; + if (py) *py = 0; + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (ipta < 0 || ipta >= ptaa->n) + return ERROR_INT("index ipta not valid", procName, 1); + + pta = ptaaGetPta(ptaa, ipta, L_CLONE); + if (jpt < 0 || jpt >= pta->n) { + ptaDestroy(&pta); + return ERROR_INT("index jpt not valid", procName, 1); + } + + ptaGetPt(pta, jpt, px, py); + ptaDestroy(&pta); + return 0; +} + + +/*---------------------------------------------------------------------* + * Ptaa array modifiers * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaaInitFull() + * + * \param[in] ptaa can have non-null ptrs in the ptr array + * \param[in] pta to be replicated into the entire ptr array + * \return 0 if OK; 1 on error + */ +l_ok +ptaaInitFull(PTAA *ptaa, + PTA *pta) +{ +l_int32 n, i; +PTA *ptat; + + PROCNAME("ptaaInitFull"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + n = ptaa->nalloc; + ptaa->n = n; + for (i = 0; i < n; i++) { + ptat = ptaCopy(pta); + ptaaReplacePta(ptaa, i, ptat); + } + return 0; +} + + +/*! + * \brief ptaaReplacePta() + * + * \param[in] ptaa + * \param[in] index to the index-th pta + * \param[in] pta insert and replace any existing one + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Any existing pta is destroyed, and the input one
+ *          is inserted in its place.
+ *      (2) If %index is invalid, return 1 (error)
+ * 
+ */ +l_ok +ptaaReplacePta(PTAA *ptaa, + l_int32 index, + PTA *pta) +{ +l_int32 n; + + PROCNAME("ptaaReplacePta"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + n = ptaaGetCount(ptaa); + if (index < 0 || index >= n) + return ERROR_INT("index not valid", procName, 1); + + ptaDestroy(&ptaa->pta[index]); + ptaa->pta[index] = pta; + return 0; +} + + +/*! + * \brief ptaaAddPt() + * + * \param[in] ptaa + * \param[in] ipta to the i-th pta + * \param[in] x,y point coordinates + * \return 0 if OK; 1 on error + */ +l_ok +ptaaAddPt(PTAA *ptaa, + l_int32 ipta, + l_float32 x, + l_float32 y) +{ +PTA *pta; + + PROCNAME("ptaaAddPt"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + if (ipta < 0 || ipta >= ptaa->n) + return ERROR_INT("index ipta not valid", procName, 1); + + pta = ptaaGetPta(ptaa, ipta, L_CLONE); + ptaAddPt(pta, x, y); + ptaDestroy(&pta); + return 0; +} + + +/*! + * \brief ptaaTruncate() + * + * \param[in] ptaa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This identifies the largest index containing a pta that
+ *          has any points within it, destroys all pta above that index,
+ *          and resets the count.
+ * 
+ */ +l_ok +ptaaTruncate(PTAA *ptaa) +{ +l_int32 i, n, np; +PTA *pta; + + PROCNAME("ptaaTruncate"); + + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + n = ptaaGetCount(ptaa); + for (i = n - 1; i >= 0; i--) { + pta = ptaaGetPta(ptaa, i, L_CLONE); + if (!pta) { + ptaa->n--; + continue; + } + np = ptaGetCount(pta); + ptaDestroy(&pta); + if (np == 0) { + ptaDestroy(&ptaa->pta[i]); + ptaa->n--; + } else { + break; + } + } + return 0; +} + + +/*---------------------------------------------------------------------* + * Ptaa serialized for I/O * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaaRead() + * + * \param[in] filename + * \return ptaa, or NULL on error + */ +PTAA * +ptaaRead(const char *filename) +{ +FILE *fp; +PTAA *ptaa; + + PROCNAME("ptaaRead"); + + if (!filename) + return (PTAA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PTAA *)ERROR_PTR("stream not opened", procName, NULL); + ptaa = ptaaReadStream(fp); + fclose(fp); + if (!ptaa) + return (PTAA *)ERROR_PTR("ptaa not read", procName, NULL); + return ptaa; +} + + +/*! + * \brief ptaaReadStream() + * + * \param[in] fp file stream + * \return ptaa, or NULL on error + */ +PTAA * +ptaaReadStream(FILE *fp) +{ +l_int32 i, n, version; +PTA *pta; +PTAA *ptaa; + + PROCNAME("ptaaReadStream"); + + if (!fp) + return (PTAA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nPtaa Version %d\n", &version) != 1) + return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); + if (version != PTA_VERSION_NUMBER) + return (PTAA *)ERROR_PTR("invalid ptaa version", procName, NULL); + if (fscanf(fp, "Number of Pta = %d\n", &n) != 1) + return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); + + if ((ptaa = ptaaCreate(n)) == NULL) + return (PTAA *)ERROR_PTR("ptaa not made", procName, NULL); + for (i = 0; i < n; i++) { + if ((pta = ptaReadStream(fp)) == NULL) { + ptaaDestroy(&ptaa); + return (PTAA *)ERROR_PTR("error reading pta", procName, NULL); + } + ptaaAddPta(ptaa, pta, L_INSERT); + } + + return ptaa; +} + + +/*! + * \brief ptaaReadMem() + * + * \param[in] data serialization in ascii + * \param[in] size of data in bytes; can use strlen to get it + * \return ptaa, or NULL on error + */ +PTAA * +ptaaReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +PTAA *ptaa; + + PROCNAME("ptaaReadMem"); + + if (!data) + return (PTAA *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (PTAA *)ERROR_PTR("stream not opened", procName, NULL); + + ptaa = ptaaReadStream(fp); + fclose(fp); + if (!ptaa) L_ERROR("ptaa not read\n", procName); + return ptaa; +} + + +/*! + * \brief ptaaWriteDebug() + * + * \param[in] filename + * \param[in] ptaa + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of ptaaWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +ptaaWriteDebug(const char *filename, + PTAA *ptaa, + l_int32 type) +{ + PROCNAME("ptaaWriteDebug"); + + if (LeptDebugOK) { + return ptaaWrite(filename, ptaa, type); + } else { + L_INFO("write to named temp file %s is disabled\n", procName, filename); + return 0; + } +} + + +/*! + * \brief ptaaWrite() + * + * \param[in] filename + * \param[in] ptaa + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + */ +l_ok +ptaaWrite(const char *filename, + PTAA *ptaa, + l_int32 type) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("ptaaWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = ptaaWriteStream(fp, ptaa, type); + fclose(fp); + if (ret) + return ERROR_INT("ptaa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief ptaaWriteStream() + * + * \param[in] fp file stream + * \param[in] ptaa + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK; 1 on error + */ +l_ok +ptaaWriteStream(FILE *fp, + PTAA *ptaa, + l_int32 type) +{ +l_int32 i, n; +PTA *pta; + + PROCNAME("ptaaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + + n = ptaaGetCount(ptaa); + fprintf(fp, "\nPtaa Version %d\n", PTA_VERSION_NUMBER); + fprintf(fp, "Number of Pta = %d\n", n); + for (i = 0; i < n; i++) { + pta = ptaaGetPta(ptaa, i, L_CLONE); + ptaWriteStream(fp, pta, type); + ptaDestroy(&pta); + } + + return 0; +} + + +/*! + * \brief ptaaWriteMem() + * + * \param[out] pdata data of serialized ptaa; ascii + * \param[out] psize size of returned data + * \param[in] ptaa + * \param[in] type 0 for float values; 1 for integer values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes %ptaa in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +ptaaWriteMem(l_uint8 **pdata, + size_t *psize, + PTAA *ptaa, + l_int32 type) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("ptaaWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!ptaa) + return ERROR_INT("ptaa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = ptaaWriteStream(fp, ptaa, type); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = ptaaWriteStream(fp, ptaa, type); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ptafunc1.c b/hgdriver/3rdparty/hgOCR/leptonica/ptafunc1.c new file mode 100644 index 0000000..b8db958 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ptafunc1.c @@ -0,0 +1,2665 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file ptafunc1.c + *
+ *
+ *      --------------------------------------
+ *      This file has these Pta utilities:
+ *         - simple rearrangements
+ *         - geometric analysis
+ *         - min/max and filtering
+ *         - least squares fitting
+ *         - interconversions with Pix and Numa
+ *         - display into a pix
+ *      --------------------------------------
+ *
+ *      Simple rearrangements
+ *           PTA      *ptaSubsample()
+ *           l_int32   ptaJoin()
+ *           l_int32   ptaaJoin()
+ *           PTA      *ptaReverse()
+ *           PTA      *ptaTranspose()
+ *           PTA      *ptaCyclicPerm()
+ *           PTA      *ptaSelectRange()
+ *
+ *      Geometric
+ *           BOX      *ptaGetBoundingRegion()
+ *           l_int32  *ptaGetRange()
+ *           PTA      *ptaGetInsideBox()
+ *           PTA      *pixFindCornerPixels()
+ *           l_int32   ptaContainsPt()
+ *           l_int32   ptaTestIntersection()
+ *           PTA      *ptaTransform()
+ *           l_int32   ptaPtInsidePolygon()
+ *           l_float32 l_angleBetweenVectors()
+ *
+ *      Min/max and filtering
+ *           l_int32   ptaGetMinMax()
+ *           PTA      *ptaSelectByValue()
+ *           PTA      *ptaCropToMask()
+ *
+ *      Least Squares Fit
+ *           l_int32   ptaGetLinearLSF()
+ *           l_int32   ptaGetQuadraticLSF()
+ *           l_int32   ptaGetCubicLSF()
+ *           l_int32   ptaGetQuarticLSF()
+ *           l_int32   ptaNoisyLinearLSF()
+ *           l_int32   ptaNoisyQuadraticLSF()
+ *           l_int32   applyLinearFit()
+ *           l_int32   applyQuadraticFit()
+ *           l_int32   applyCubicFit()
+ *           l_int32   applyQuarticFit()
+ *
+ *      Interconversions with Pix
+ *           l_int32   pixPlotAlongPta()
+ *           PTA      *ptaGetPixelsFromPix()
+ *           PIX      *pixGenerateFromPta()
+ *           PTA      *ptaGetBoundaryPixels()
+ *           PTAA     *ptaaGetBoundaryPixels()
+ *           PTAA     *ptaaIndexLabeledPixels()
+ *           PTA      *ptaGetNeighborPixLocs()
+ *
+ *      Interconversion with Numa
+ *           PTA      *numaConvertToPta1()
+ *           PTA      *numaConvertToPta2()
+ *           l_int32   ptaConvertToNuma()
+ *
+ *      Display Pta and Ptaa
+ *           PIX      *pixDisplayPta()
+ *           PIX      *pixDisplayPtaaPattern()
+ *           PIX      *pixDisplayPtaPattern()
+ *           PTA      *ptaReplicatePattern()
+ *           PIX      *pixDisplayPtaa()
+ * 
+ */ + +#include +#include "allheaders.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + + +/*---------------------------------------------------------------------* + * Simple rearrangements * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaSubsample() + * + * \param[in] ptas + * \param[in] subfactor subsample factor, >= 1 + * \return ptad evenly sampled pt values from ptas, or NULL on error + */ +PTA * +ptaSubsample(PTA *ptas, + l_int32 subfactor) +{ +l_int32 n, i; +l_float32 x, y; +PTA *ptad; + + PROCNAME("pixSubsample"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (subfactor < 1) + return (PTA *)ERROR_PTR("subfactor < 1", procName, NULL); + + ptad = ptaCreate(0); + n = ptaGetCount(ptas); + for (i = 0; i < n; i++) { + if (i % subfactor != 0) continue; + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, x, y); + } + + return ptad; +} + + +/*! + * \brief ptaJoin() + * + * \param[in] ptad dest pta; add to this one + * \param[in] ptas source pta; add from this one + * \param[in] istart starting index in ptas + * \param[in] iend ending index in ptas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (2) iend < 0 means 'read to the end'
+ *      (3) if ptas == NULL, this is a no-op
+ * 
+ */ +l_ok +ptaJoin(PTA *ptad, + PTA *ptas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i, x, y; + + PROCNAME("ptaJoin"); + + if (!ptad) + return ERROR_INT("ptad not defined", procName, 1); + if (!ptas) + return 0; + + if (istart < 0) + istart = 0; + n = ptaGetCount(ptas); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; no pts", procName, 1); + + for (i = istart; i <= iend; i++) { + ptaGetIPt(ptas, i, &x, &y); + ptaAddPt(ptad, x, y); + } + + return 0; +} + + +/*! + * \brief ptaaJoin() + * + * \param[in] ptaad dest ptaa; add to this one + * \param[in] ptaas source ptaa; add from this one + * \param[in] istart starting index in ptaas + * \param[in] iend ending index in ptaas; use -1 to cat all + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ *      (2) iend < 0 means 'read to the end'
+ *      (3) if ptas == NULL, this is a no-op
+ * 
+ */ +l_ok +ptaaJoin(PTAA *ptaad, + PTAA *ptaas, + l_int32 istart, + l_int32 iend) +{ +l_int32 n, i; +PTA *pta; + + PROCNAME("ptaaJoin"); + + if (!ptaad) + return ERROR_INT("ptaad not defined", procName, 1); + if (!ptaas) + return 0; + + if (istart < 0) + istart = 0; + n = ptaaGetCount(ptaas); + if (iend < 0 || iend >= n) + iend = n - 1; + if (istart > iend) + return ERROR_INT("istart > iend; no pts", procName, 1); + + for (i = istart; i <= iend; i++) { + pta = ptaaGetPta(ptaas, i, L_CLONE); + ptaaAddPta(ptaad, pta, L_INSERT); + } + + return 0; +} + + +/*! + * \brief ptaReverse() + * + * \param[in] ptas + * \param[in] type 0 for float values; 1 for integer values + * \return ptad reversed pta, or NULL on error + */ +PTA * +ptaReverse(PTA *ptas, + l_int32 type) +{ +l_int32 n, i, ix, iy; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaReverse"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + n = ptaGetCount(ptas); + if ((ptad = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = n - 1; i >= 0; i--) { + if (type == 0) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, x, y); + } else { /* type == 1 */ + ptaGetIPt(ptas, i, &ix, &iy); + ptaAddPt(ptad, ix, iy); + } + } + + return ptad; +} + + +/*! + * \brief ptaTranspose() + * + * \param[in] ptas + * \return ptad with x and y values swapped, or NULL on error + */ +PTA * +ptaTranspose(PTA *ptas) +{ +l_int32 n, i; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaTranspose"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + n = ptaGetCount(ptas); + if ((ptad = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = 0; i < n; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, y, x); + } + + return ptad; +} + + +/*! + * \brief ptaCyclicPerm() + * + * \param[in] ptas + * \param[in] xs, ys start point; must be in ptas + * \return ptad cyclic permutation, starting and ending at (xs, ys, + * or NULL on error + * + *
+ * Notes:
+ *      (1) Check to insure that (a) ptas is a closed path where
+ *          the first and last points are identical, and (b) the
+ *          resulting pta also starts and ends on the same point
+ *          (which in this case is (xs, ys).
+ * 
+ */ +PTA * +ptaCyclicPerm(PTA *ptas, + l_int32 xs, + l_int32 ys) +{ +l_int32 n, i, x, y, j, index, state; +l_int32 x1, y1, x2, y2; +PTA *ptad; + + PROCNAME("ptaCyclicPerm"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + n = ptaGetCount(ptas); + + /* Verify input data */ + ptaGetIPt(ptas, 0, &x1, &y1); + ptaGetIPt(ptas, n - 1, &x2, &y2); + if (x1 != x2 || y1 != y2) + return (PTA *)ERROR_PTR("start and end pts not same", procName, NULL); + state = L_NOT_FOUND; + for (i = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + if (x == xs && y == ys) { + state = L_FOUND; + break; + } + } + if (state == L_NOT_FOUND) + return (PTA *)ERROR_PTR("start pt not in ptas", procName, NULL); + + if ((ptad = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (j = 0; j < n - 1; j++) { + if (i + j < n - 1) + index = i + j; + else + index = (i + j + 1) % n; + ptaGetIPt(ptas, index, &x, &y); + ptaAddPt(ptad, x, y); + } + ptaAddPt(ptad, xs, ys); + + return ptad; +} + + +/*! + * \brief ptaSelectRange() + * + * \param[in] ptas + * \param[in] first use 0 to select from the beginning + * \param[in] last use -1 to select to the end + * \return ptad, or NULL on error + */ +PTA * +ptaSelectRange(PTA *ptas, + l_int32 first, + l_int32 last) +{ +l_int32 n, npt, i; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaSelectRange"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if ((n = ptaGetCount(ptas)) == 0) { + L_WARNING("ptas is empty\n", procName); + return ptaCopy(ptas); + } + first = L_MAX(0, first); + if (last < 0) last = n - 1; + if (first >= n) + return (PTA *)ERROR_PTR("invalid first", procName, NULL); + if (last >= n) { + L_WARNING("last = %d is beyond max index = %d; adjusting\n", + procName, last, n - 1); + last = n - 1; + } + if (first > last) + return (PTA *)ERROR_PTR("first > last", procName, NULL); + + npt = last - first + 1; + ptad = ptaCreate(npt); + for (i = first; i <= last; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, x, y); + } + return ptad; +} + + +/*---------------------------------------------------------------------* + * Geometric * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaGetBoundingRegion() + * + * \param[in] pta + * \return box, or NULL on error + * + *
+ * Notes:
+ *      (1) This is used when the pta represents a set of points in
+ *          a two-dimensional image.  It returns the box of minimum
+ *          size containing the pts in the pta.
+ * 
+ */ +BOX * +ptaGetBoundingRegion(PTA *pta) +{ +l_int32 n, i, x, y, minx, maxx, miny, maxy; + + PROCNAME("ptaGetBoundingRegion"); + + if (!pta) + return (BOX *)ERROR_PTR("pta not defined", procName, NULL); + + minx = 10000000; + miny = 10000000; + maxx = -10000000; + maxy = -10000000; + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < minx) minx = x; + if (x > maxx) maxx = x; + if (y < miny) miny = y; + if (y > maxy) maxy = y; + } + + return boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); +} + + +/*! + * \brief ptaGetRange() + * + * \param[in] pta + * \param[out] pminx [optional] min value of x + * \param[out] pmaxx [optional] max value of x + * \param[out] pminy [optional] min value of y + * \param[out] pmaxy [optional] max value of y + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) We can use pts to represent pairs of floating values, that
+ *          are not necessarily tied to a two-dimension region.  For
+ *          example, the pts can represent a general function y(x).
+ * 
+ */ +l_ok +ptaGetRange(PTA *pta, + l_float32 *pminx, + l_float32 *pmaxx, + l_float32 *pminy, + l_float32 *pmaxy) +{ +l_int32 n, i; +l_float32 x, y, minx, maxx, miny, maxy; + + PROCNAME("ptaGetRange"); + + if (!pminx && !pmaxx && !pminy && !pmaxy) + return ERROR_INT("no output requested", procName, 1); + if (pminx) *pminx = 0; + if (pmaxx) *pmaxx = 0; + if (pminy) *pminy = 0; + if (pmaxy) *pmaxy = 0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) == 0) + return ERROR_INT("no points in pta", procName, 1); + + ptaGetPt(pta, 0, &x, &y); + minx = x; + maxx = x; + miny = y; + maxy = y; + for (i = 1; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + if (x < minx) minx = x; + if (x > maxx) maxx = x; + if (y < miny) miny = y; + if (y > maxy) maxy = y; + } + if (pminx) *pminx = minx; + if (pmaxx) *pmaxx = maxx; + if (pminy) *pminy = miny; + if (pmaxy) *pmaxy = maxy; + return 0; +} + + +/*! + * \brief ptaGetInsideBox() + * + * \param[in] ptas input pts + * \param[in] box + * \return ptad of pts in ptas that are inside the box, or NULL on error + */ +PTA * +ptaGetInsideBox(PTA *ptas, + BOX *box) +{ +PTA *ptad; +l_int32 n, i, contains; +l_float32 x, y; + + PROCNAME("ptaGetInsideBox"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (!box) + return (PTA *)ERROR_PTR("box not defined", procName, NULL); + + n = ptaGetCount(ptas); + ptad = ptaCreate(0); + for (i = 0; i < n; i++) { + ptaGetPt(ptas, i, &x, &y); + boxContainsPt(box, x, y, &contains); + if (contains) + ptaAddPt(ptad, x, y); + } + + return ptad; +} + + +/*! + * \brief pixFindCornerPixels() + * + * \param[in] pixs 1 bpp + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Finds the 4 corner-most pixels, as defined by a search
+ *          inward from each corner, using a 45 degree line.
+ * 
+ */ +PTA * +pixFindCornerPixels(PIX *pixs) +{ +l_int32 i, j, x, y, w, h, wpl, mindim, found; +l_uint32 *data, *line; +PTA *pta; + + PROCNAME("pixFindCornerPixels"); + + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + mindim = L_MIN(w, h); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + + if ((pta = ptaCreate(4)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + + for (found = FALSE, i = 0; i < mindim; i++) { + for (j = 0; j <= i; j++) { + y = i - j; + line = data + y * wpl; + if (GET_DATA_BIT(line, j)) { + ptaAddPt(pta, j, y); + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + + for (found = FALSE, i = 0; i < mindim; i++) { + for (j = 0; j <= i; j++) { + y = i - j; + line = data + y * wpl; + x = w - 1 - j; + if (GET_DATA_BIT(line, x)) { + ptaAddPt(pta, x, y); + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + + for (found = FALSE, i = 0; i < mindim; i++) { + for (j = 0; j <= i; j++) { + y = h - 1 - i + j; + line = data + y * wpl; + if (GET_DATA_BIT(line, j)) { + ptaAddPt(pta, j, y); + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + + for (found = FALSE, i = 0; i < mindim; i++) { + for (j = 0; j <= i; j++) { + y = h - 1 - i + j; + line = data + y * wpl; + x = w - 1 - j; + if (GET_DATA_BIT(line, x)) { + ptaAddPt(pta, x, y); + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + + return pta; +} + + +/*! + * \brief ptaContainsPt() + * + * \param[in] pta + * \param[in] x, y point + * \return 1 if contained, 0 otherwise or on error + */ +l_int32 +ptaContainsPt(PTA *pta, + l_int32 x, + l_int32 y) +{ +l_int32 i, n, ix, iy; + + PROCNAME("ptaContainsPt"); + + if (!pta) + return ERROR_INT("pta not defined", procName, 0); + + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &ix, &iy); + if (x == ix && y == iy) + return 1; + } + return 0; +} + + +/*! + * \brief ptaTestIntersection() + * + * \param[in] pta1, pta2 + * \return bval which is 1 if they have any elements in common; + * 0 otherwise or on error. + */ +l_int32 +ptaTestIntersection(PTA *pta1, + PTA *pta2) +{ +l_int32 i, j, n1, n2, x1, y1, x2, y2; + + PROCNAME("ptaTestIntersection"); + + if (!pta1) + return ERROR_INT("pta1 not defined", procName, 0); + if (!pta2) + return ERROR_INT("pta2 not defined", procName, 0); + + n1 = ptaGetCount(pta1); + n2 = ptaGetCount(pta2); + for (i = 0; i < n1; i++) { + ptaGetIPt(pta1, i, &x1, &y1); + for (j = 0; j < n2; j++) { + ptaGetIPt(pta2, i, &x2, &y2); + if (x1 == x2 && y1 == y2) + return 1; + } + } + + return 0; +} + + +/*! + * \brief ptaTransform() + * + * \param[in] ptas + * \param[in] shiftx, shifty + * \param[in] scalex, scaley + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Shift first, then scale.
+ * 
+ */ +PTA * +ptaTransform(PTA *ptas, + l_int32 shiftx, + l_int32 shifty, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 n, i, x, y; +PTA *ptad; + + PROCNAME("ptaTransform"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + x = (l_int32)(scalex * (x + shiftx) + 0.5); + y = (l_int32)(scaley * (y + shifty) + 0.5); + ptaAddPt(ptad, x, y); + } + + return ptad; +} + + +/*! + * \brief ptaPtInsidePolygon() + * + * \param[in] pta vertices of a polygon + * \param[in] x, y point to be tested + * \param[out] pinside 1 if inside; 0 if outside or on boundary + * \return 1 if OK, 0 on error + * + * The abs value of the sum of the angles subtended from a point by + * the sides of a polygon, when taken in order traversing the polygon, + * is 0 if the point is outside the polygon and 2*pi if inside. + * The sign will be positive if traversed cw and negative if ccw. + */ +l_int32 +ptaPtInsidePolygon(PTA *pta, + l_float32 x, + l_float32 y, + l_int32 *pinside) +{ +l_int32 i, n; +l_float32 sum, x1, y1, x2, y2, xp1, yp1, xp2, yp2; + + PROCNAME("ptaPtInsidePolygon"); + + if (!pinside) + return ERROR_INT("&inside not defined", procName, 1); + *pinside = 0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + /* Think of (x1,y1) as the end point of a vector that starts + * from the origin (0,0), and ditto for (x2,y2). */ + n = ptaGetCount(pta); + sum = 0.0; + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &xp1, &yp1); + ptaGetPt(pta, (i + 1) % n, &xp2, &yp2); + x1 = xp1 - x; + y1 = yp1 - y; + x2 = xp2 - x; + y2 = yp2 - y; + sum += l_angleBetweenVectors(x1, y1, x2, y2); + } + + if (L_ABS(sum) > M_PI) + *pinside = 1; + return 0; +} + + +/*! + * \brief l_angleBetweenVectors() + * + * \param[in] x1, y1 end point of first vector + * \param[in] x2, y2 end point of second vector + * \return angle radians, or 0.0 on error + * + *
+ * Notes:
+ *      (1) This gives the angle between two vectors, going between
+ *          vector1 (x1,y1) and vector2 (x2,y2).  The angle is swept
+ *          out from 1 --> 2.  If this is clockwise, the angle is
+ *          positive, but the result is folded into the interval [-pi, pi].
+ * 
+ */ +l_float32 +l_angleBetweenVectors(l_float32 x1, + l_float32 y1, + l_float32 x2, + l_float32 y2) +{ +l_float64 ang; + + ang = atan2(y2, x2) - atan2(y1, x1); + if (ang > M_PI) ang -= 2.0 * M_PI; + if (ang < -M_PI) ang += 2.0 * M_PI; + return ang; +} + + +/*---------------------------------------------------------------------* + * Min/max and filtering * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaGetMinMax() + * + * \param[in] pta + * \param[out] pxmin [optional] min of x + * \param[out] pymin [optional] min of y + * \param[out] pxmax [optional] max of x + * \param[out] pymax [optional] max of y + * \return 0 if OK, 1 on error. If pta is empty, requested + * values are returned as -1.0. + */ +l_ok +ptaGetMinMax(PTA *pta, + l_float32 *pxmin, + l_float32 *pymin, + l_float32 *pxmax, + l_float32 *pymax) +{ +l_int32 i, n; +l_float32 x, y, xmin, ymin, xmax, ymax; + + PROCNAME("ptaGetMinMax"); + + if (pxmin) *pxmin = -1.0; + if (pymin) *pymin = -1.0; + if (pxmax) *pxmax = -1.0; + if (pymax) *pymax = -1.0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (!pxmin && !pxmax && !pymin && !pymax) + return ERROR_INT("no output requested", procName, 1); + if ((n = ptaGetCount(pta)) == 0) { + L_WARNING("pta is empty\n", procName); + return 0; + } + + xmin = ymin = 1.0e20; + xmax = ymax = -1.0e20; + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + if (x < xmin) xmin = x; + if (y < ymin) ymin = y; + if (x > xmax) xmax = x; + if (y > ymax) ymax = y; + } + if (pxmin) *pxmin = xmin; + if (pymin) *pymin = ymin; + if (pxmax) *pxmax = xmax; + if (pymax) *pymax = ymax; + return 0; +} + + +/*! + * \brief ptaSelectByValue() + * + * \param[in] ptas + * \param[in] xth, yth threshold values + * \param[in] type L_SELECT_XVAL, L_SELECT_YVAL, + * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH + * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, + * L_SELECT_IF_LTE, L_SELECT_IF_GTE + * \return ptad filtered set, or NULL on error + */ +PTA * +ptaSelectByValue(PTA *ptas, + l_float32 xth, + l_float32 yth, + l_int32 type, + l_int32 relation) +{ +l_int32 i, n; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaSelectByValue"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (ptaGetCount(ptas) == 0) { + L_WARNING("ptas is empty\n", procName); + return ptaCopy(ptas); + } + if (type != L_SELECT_XVAL && type != L_SELECT_YVAL && + type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) + return (PTA *)ERROR_PTR("invalid type", procName, NULL); + if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && + relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) + return (PTA *)ERROR_PTR("invalid relation", procName, NULL); + + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(ptas, i, &x, &y); + if (type == L_SELECT_XVAL) { + if ((relation == L_SELECT_IF_LT && x < xth) || + (relation == L_SELECT_IF_GT && x > xth) || + (relation == L_SELECT_IF_LTE && x <= xth) || + (relation == L_SELECT_IF_GTE && x >= xth)) + ptaAddPt(ptad, x, y); + } else if (type == L_SELECT_YVAL) { + if ((relation == L_SELECT_IF_LT && y < yth) || + (relation == L_SELECT_IF_GT && y > yth) || + (relation == L_SELECT_IF_LTE && y <= yth) || + (relation == L_SELECT_IF_GTE && y >= yth)) + ptaAddPt(ptad, x, y); + } else if (type == L_SELECT_IF_EITHER) { + if (((relation == L_SELECT_IF_LT) && (x < xth || y < yth)) || + ((relation == L_SELECT_IF_GT) && (x > xth || y > yth)) || + ((relation == L_SELECT_IF_LTE) && (x <= xth || y <= yth)) || + ((relation == L_SELECT_IF_GTE) && (x >= xth || y >= yth))) + ptaAddPt(ptad, x, y); + } else { /* L_SELECT_IF_BOTH */ + if (((relation == L_SELECT_IF_LT) && (x < xth && y < yth)) || + ((relation == L_SELECT_IF_GT) && (x > xth && y > yth)) || + ((relation == L_SELECT_IF_LTE) && (x <= xth && y <= yth)) || + ((relation == L_SELECT_IF_GTE) && (x >= xth && y >= yth))) + ptaAddPt(ptad, x, y); + } + } + + return ptad; +} + + +/*! + * \brief ptaCropToMask() + * + * \param[in] ptas input pta + * \param[in] pixm 1 bpp mask + * \return ptad with only pts under the mask fg, or NULL on error + */ +PTA * +ptaCropToMask(PTA *ptas, + PIX *pixm) +{ +l_int32 i, n, x, y; +l_uint32 val; +PTA *ptad; + + PROCNAME("ptaCropToMask"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 1) + return (PTA *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); + if (ptaGetCount(ptas) == 0) { + L_INFO("ptas is empty\n", procName); + return ptaCopy(ptas); + } + + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + pixGetPixel(pixm, x, y, &val); + if (val == 1) + ptaAddPt(ptad, x, y); + } + return ptad; +} + + +/*---------------------------------------------------------------------* + * Least Squares Fit * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaGetLinearLSF() + * + * \param[in] pta + * \param[out] pa [optional] slope a of least square fit: y = ax + b + * \param[out] pb [optional] intercept b of least square fit + * \param[out] pnafit [optional] numa of least square fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Either or both &a and &b must be input.  They determine the
+ *          type of line that is fit.
+ *      (2) If both &a and &b are defined, this returns a and b that minimize:
+ *
+ *              sum (yi - axi -b)^2
+ *               i
+ *
+ *          The method is simple: differentiate this expression w/rt a and b,
+ *          and solve the resulting two equations for a and b in terms of
+ *          various sums over the input data (xi, yi).
+ *      (3) We also allow two special cases, where either a = 0 or b = 0:
+ *           (a) If &a is given and &b = null, find the linear LSF that
+ *               goes through the origin (b = 0).
+ *           (b) If &b is given and &a = null, find the linear LSF with
+ *               zero slope (a = 0).
+ *      (4) If &nafit is defined, this returns an array of fitted values,
+ *          corresponding to the two implicit Numa arrays (nax and nay) in pta.
+ *          Thus, just as you can plot the data in pta as nay vs. nax,
+ *          you can plot the linear least square fit as nafit vs. nax.
+ *          Get the nax array using ptaGetArrays(pta, &nax, NULL);
+ * 
+ */ +l_ok +ptaGetLinearLSF(PTA *pta, + l_float32 *pa, + l_float32 *pb, + NUMA **pnafit) +{ +l_int32 n, i; +l_float32 a, b, factor, sx, sy, sxx, sxy, val; +l_float32 *xa, *ya; + + PROCNAME("ptaGetLinearLSF"); + + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pnafit) *pnafit = NULL; + if (!pa && !pb && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) < 2) + return ERROR_INT("less than 2 pts found", procName, 1); + + xa = pta->x; /* not a copy */ + ya = pta->y; /* not a copy */ + sx = sy = sxx = sxy = 0.; + if (pa && pb) { /* general line */ + for (i = 0; i < n; i++) { + sx += xa[i]; + sy += ya[i]; + sxx += xa[i] * xa[i]; + sxy += xa[i] * ya[i]; + } + factor = n * sxx - sx * sx; + if (factor == 0.0) + return ERROR_INT("no solution found", procName, 1); + factor = 1. / factor; + + a = factor * ((l_float32)n * sxy - sx * sy); + b = factor * (sxx * sy - sx * sxy); + } else if (pa) { /* b = 0; line through origin */ + for (i = 0; i < n; i++) { + sxx += xa[i] * xa[i]; + sxy += xa[i] * ya[i]; + } + if (sxx == 0.0) + return ERROR_INT("no solution found", procName, 1); + a = sxy / sxx; + b = 0.0; + } else { /* a = 0; horizontal line */ + for (i = 0; i < n; i++) + sy += ya[i]; + a = 0.0; + b = sy / (l_float32)n; + } + + if (pnafit) { + *pnafit = numaCreate(n); + for (i = 0; i < n; i++) { + val = a * xa[i] + b; + numaAddNumber(*pnafit, val); + } + } + + if (pa) *pa = a; + if (pb) *pb = b; + return 0; +} + + +/*! + * \brief ptaGetQuadraticLSF() + * + * \param[in] pta + * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c + * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c + * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c + * \param[out] pnafit [optional] numa of least square fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a quadratic least square fit to the set of points
+ *          in %pta.  That is, it finds coefficients a, b and c that minimize:
+ *
+ *              sum (yi - a*xi*xi -b*xi -c)^2
+ *               i
+ *
+ *          The method is simple: differentiate this expression w/rt
+ *          a, b and c, and solve the resulting three equations for these
+ *          coefficients in terms of various sums over the input data (xi, yi).
+ *          The three equations are in the form:
+ *             f[0][0]a + f[0][1]b + f[0][2]c = g[0]
+ *             f[1][0]a + f[1][1]b + f[1][2]c = g[1]
+ *             f[2][0]a + f[2][1]b + f[2][2]c = g[2]
+ *      (2) If &nafit is defined, this returns an array of fitted values,
+ *          corresponding to the two implicit Numa arrays (nax and nay) in pta.
+ *          Thus, just as you can plot the data in pta as nay vs. nax,
+ *          you can plot the linear least square fit as nafit vs. nax.
+ *          Get the nax array using ptaGetArrays(pta, &nax, NULL);
+ * 
+ */ +l_ok +ptaGetQuadraticLSF(PTA *pta, + l_float32 *pa, + l_float32 *pb, + l_float32 *pc, + NUMA **pnafit) +{ +l_int32 n, i, ret; +l_float32 x, y, sx, sy, sx2, sx3, sx4, sxy, sx2y; +l_float32 *xa, *ya; +l_float32 *f[3]; +l_float32 g[3]; + + PROCNAME("ptaGetQuadraticLSF"); + + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pc) *pc = 0.0; + if (pnafit) *pnafit = NULL; + if (!pa && !pb && !pc && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) < 3) + return ERROR_INT("less than 3 pts found", procName, 1); + + xa = pta->x; /* not a copy */ + ya = pta->y; /* not a copy */ + sx = sy = sx2 = sx3 = sx4 = sxy = sx2y = 0.; + for (i = 0; i < n; i++) { + x = xa[i]; + y = ya[i]; + sx += x; + sy += y; + sx2 += x * x; + sx3 += x * x * x; + sx4 += x * x * x * x; + sxy += x * y; + sx2y += x * x * y; + } + + for (i = 0; i < 3; i++) + f[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); + f[0][0] = sx4; + f[0][1] = sx3; + f[0][2] = sx2; + f[1][0] = sx3; + f[1][1] = sx2; + f[1][2] = sx; + f[2][0] = sx2; + f[2][1] = sx; + f[2][2] = n; + g[0] = sx2y; + g[1] = sxy; + g[2] = sy; + + /* Solve for the unknowns, also putting f-inverse into f */ + ret = gaussjordan(f, g, 3); + for (i = 0; i < 3; i++) + LEPT_FREE(f[i]); + if (ret) + return ERROR_INT("quadratic solution failed", procName, 1); + + if (pa) *pa = g[0]; + if (pb) *pb = g[1]; + if (pc) *pc = g[2]; + if (pnafit) { + *pnafit = numaCreate(n); + for (i = 0; i < n; i++) { + x = xa[i]; + y = g[0] * x * x + g[1] * x + g[2]; + numaAddNumber(*pnafit, y); + } + } + return 0; +} + + +/*! + * \brief ptaGetCubicLSF() + * + * \param[in] pta + * \param[out] pa [optional] coeff a of LSF: y = ax^3 + bx^2 + cx + d + * \param[out] pb [optional] coeff b of LSF + * \param[out] pc [optional] coeff c of LSF + * \param[out] pd [optional] coeff d of LSF + * \param[out] pnafit [optional] numa of least square fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a cubic least square fit to the set of points
+ *          in %pta.  That is, it finds coefficients a, b, c and d
+ *          that minimize:
+ *
+ *              sum (yi - a*xi*xi*xi -b*xi*xi -c*xi - d)^2
+ *               i
+ *
+ *          Differentiate this expression w/rt a, b, c and d, and solve
+ *          the resulting four equations for these coefficients in
+ *          terms of various sums over the input data (xi, yi).
+ *          The four equations are in the form:
+ *             f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] = g[0]
+ *             f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] = g[1]
+ *             f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] = g[2]
+ *             f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] = g[3]
+ *      (2) If &nafit is defined, this returns an array of fitted values,
+ *          corresponding to the two implicit Numa arrays (nax and nay) in pta.
+ *          Thus, just as you can plot the data in pta as nay vs. nax,
+ *          you can plot the linear least square fit as nafit vs. nax.
+ *          Get the nax array using ptaGetArrays(pta, &nax, NULL);
+ * 
+ */ +l_ok +ptaGetCubicLSF(PTA *pta, + l_float32 *pa, + l_float32 *pb, + l_float32 *pc, + l_float32 *pd, + NUMA **pnafit) +{ +l_int32 n, i, ret; +l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sxy, sx2y, sx3y; +l_float32 *xa, *ya; +l_float32 *f[4]; +l_float32 g[4]; + + PROCNAME("ptaGetCubicLSF"); + + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pc) *pc = 0.0; + if (pd) *pd = 0.0; + if (pnafit) *pnafit = NULL; + if (!pa && !pb && !pc && !pd && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) < 4) + return ERROR_INT("less than 4 pts found", procName, 1); + + xa = pta->x; /* not a copy */ + ya = pta->y; /* not a copy */ + sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sxy = sx2y = sx3y = 0.; + for (i = 0; i < n; i++) { + x = xa[i]; + y = ya[i]; + sx += x; + sy += y; + sx2 += x * x; + sx3 += x * x * x; + sx4 += x * x * x * x; + sx5 += x * x * x * x * x; + sx6 += x * x * x * x * x * x; + sxy += x * y; + sx2y += x * x * y; + sx3y += x * x * x * y; + } + + for (i = 0; i < 4; i++) + f[i] = (l_float32 *)LEPT_CALLOC(4, sizeof(l_float32)); + f[0][0] = sx6; + f[0][1] = sx5; + f[0][2] = sx4; + f[0][3] = sx3; + f[1][0] = sx5; + f[1][1] = sx4; + f[1][2] = sx3; + f[1][3] = sx2; + f[2][0] = sx4; + f[2][1] = sx3; + f[2][2] = sx2; + f[2][3] = sx; + f[3][0] = sx3; + f[3][1] = sx2; + f[3][2] = sx; + f[3][3] = n; + g[0] = sx3y; + g[1] = sx2y; + g[2] = sxy; + g[3] = sy; + + /* Solve for the unknowns, also putting f-inverse into f */ + ret = gaussjordan(f, g, 4); + for (i = 0; i < 4; i++) + LEPT_FREE(f[i]); + if (ret) + return ERROR_INT("cubic solution failed", procName, 1); + + if (pa) *pa = g[0]; + if (pb) *pb = g[1]; + if (pc) *pc = g[2]; + if (pd) *pd = g[3]; + if (pnafit) { + *pnafit = numaCreate(n); + for (i = 0; i < n; i++) { + x = xa[i]; + y = g[0] * x * x * x + g[1] * x * x + g[2] * x + g[3]; + numaAddNumber(*pnafit, y); + } + } + return 0; +} + + +/*! + * \brief ptaGetQuarticLSF() + * + * \param[in] pta + * \param[out] pa [optional] coeff a of LSF: + * y = ax^4 + bx^3 + cx^2 + dx + e + * \param[out] pb [optional] coeff b of LSF + * \param[out] pc [optional] coeff c of LSF + * \param[out] pd [optional] coeff d of LSF + * \param[out] pe [optional] coeff e of LSF + * \param[out] pnafit [optional] numa of least square fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a quartic least square fit to the set of points
+ *          in %pta.  That is, it finds coefficients a, b, c, d and 3
+ *          that minimize:
+ *
+ *              sum (yi - a*xi*xi*xi*xi -b*xi*xi*xi -c*xi*xi - d*xi - e)^2
+ *               i
+ *
+ *          Differentiate this expression w/rt a, b, c, d and e, and solve
+ *          the resulting five equations for these coefficients in
+ *          terms of various sums over the input data (xi, yi).
+ *          The five equations are in the form:
+ *             f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] + f[0][4] = g[0]
+ *             f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] + f[1][4] = g[1]
+ *             f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] + f[2][4] = g[2]
+ *             f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] + f[3][4] = g[3]
+ *             f[4][0]a + f[4][1]b + f[4][2]c + f[4][3] + f[4][4] = g[4]
+ *      (2) If &nafit is defined, this returns an array of fitted values,
+ *          corresponding to the two implicit Numa arrays (nax and nay) in pta.
+ *          Thus, just as you can plot the data in pta as nay vs. nax,
+ *          you can plot the linear least square fit as nafit vs. nax.
+ *          Get the nax array using ptaGetArrays(pta, &nax, NULL);
+ * 
+ */ +l_ok +ptaGetQuarticLSF(PTA *pta, + l_float32 *pa, + l_float32 *pb, + l_float32 *pc, + l_float32 *pd, + l_float32 *pe, + NUMA **pnafit) +{ +l_int32 n, i, ret; +l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sx7, sx8; +l_float32 sxy, sx2y, sx3y, sx4y; +l_float32 *xa, *ya; +l_float32 *f[5]; +l_float32 g[5]; + + PROCNAME("ptaGetQuarticLSF"); + + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pc) *pc = 0.0; + if (pd) *pd = 0.0; + if (pe) *pe = 0.0; + if (pnafit) *pnafit = NULL; + if (!pa && !pb && !pc && !pd && !pe && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) < 5) + return ERROR_INT("less than 5 pts found", procName, 1); + + xa = pta->x; /* not a copy */ + ya = pta->y; /* not a copy */ + sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sx7 = sx8 = 0; + sxy = sx2y = sx3y = sx4y = 0.; + for (i = 0; i < n; i++) { + x = xa[i]; + y = ya[i]; + sx += x; + sy += y; + sx2 += x * x; + sx3 += x * x * x; + sx4 += x * x * x * x; + sx5 += x * x * x * x * x; + sx6 += x * x * x * x * x * x; + sx7 += x * x * x * x * x * x * x; + sx8 += x * x * x * x * x * x * x * x; + sxy += x * y; + sx2y += x * x * y; + sx3y += x * x * x * y; + sx4y += x * x * x * x * y; + } + + for (i = 0; i < 5; i++) + f[i] = (l_float32 *)LEPT_CALLOC(5, sizeof(l_float32)); + f[0][0] = sx8; + f[0][1] = sx7; + f[0][2] = sx6; + f[0][3] = sx5; + f[0][4] = sx4; + f[1][0] = sx7; + f[1][1] = sx6; + f[1][2] = sx5; + f[1][3] = sx4; + f[1][4] = sx3; + f[2][0] = sx6; + f[2][1] = sx5; + f[2][2] = sx4; + f[2][3] = sx3; + f[2][4] = sx2; + f[3][0] = sx5; + f[3][1] = sx4; + f[3][2] = sx3; + f[3][3] = sx2; + f[3][4] = sx; + f[4][0] = sx4; + f[4][1] = sx3; + f[4][2] = sx2; + f[4][3] = sx; + f[4][4] = n; + g[0] = sx4y; + g[1] = sx3y; + g[2] = sx2y; + g[3] = sxy; + g[4] = sy; + + /* Solve for the unknowns, also putting f-inverse into f */ + ret = gaussjordan(f, g, 5); + for (i = 0; i < 5; i++) + LEPT_FREE(f[i]); + if (ret) + return ERROR_INT("quartic solution failed", procName, 1); + + if (pa) *pa = g[0]; + if (pb) *pb = g[1]; + if (pc) *pc = g[2]; + if (pd) *pd = g[3]; + if (pe) *pe = g[4]; + if (pnafit) { + *pnafit = numaCreate(n); + for (i = 0; i < n; i++) { + x = xa[i]; + y = g[0] * x * x * x * x + g[1] * x * x * x + g[2] * x * x + + g[3] * x + g[4]; + numaAddNumber(*pnafit, y); + } + } + return 0; +} + + +/*! + * \brief ptaNoisyLinearLSF() + * + * \param[in] pta + * \param[in] factor reject outliers with error greater than this + * number of medians; typically ~ 3 + * \param[out] pptad [optional] with outliers removed + * \param[out] pa [optional] slope a of least square fit: y = ax + b + * \param[out] pb [optional] intercept b of least square fit + * \param[out] pmederr [optional] median error + * \param[out] pnafit [optional] numa of least square fit to ptad + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a linear least square fit to the set of points
+ *          in %pta.  It then evaluates the errors and removes points
+ *          whose error is >= factor * median_error.  It then re-runs
+ *          the linear LSF on the resulting points.
+ *      (2) Either or both &a and &b must be input.  They determine the
+ *          type of line that is fit.
+ *      (3) The median error can give an indication of how good the fit
+ *          is likely to be.
+ * 
+ */ +l_ok +ptaNoisyLinearLSF(PTA *pta, + l_float32 factor, + PTA **pptad, + l_float32 *pa, + l_float32 *pb, + l_float32 *pmederr, + NUMA **pnafit) +{ +l_int32 n, i, ret; +l_float32 x, y, yf, val, mederr; +NUMA *nafit, *naerror; +PTA *ptad; + + PROCNAME("ptaNoisyLinearLSF"); + + if (pptad) *pptad = NULL; + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pmederr) *pmederr = 0.0; + if (pnafit) *pnafit = NULL; + if (!pptad && !pa && !pb && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (factor <= 0.0) + return ERROR_INT("factor must be > 0.0", procName, 1); + if ((n = ptaGetCount(pta)) < 3) + return ERROR_INT("less than 2 pts found", procName, 1); + + if (ptaGetLinearLSF(pta, pa, pb, &nafit) != 0) + return ERROR_INT("error in linear LSF", procName, 1); + + /* Get the median error */ + naerror = numaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + numaGetFValue(nafit, i, &yf); + numaAddNumber(naerror, L_ABS(y - yf)); + } + numaGetMedian(naerror, &mederr); + if (pmederr) *pmederr = mederr; + numaDestroy(&nafit); + + /* Remove outliers */ + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + numaGetFValue(naerror, i, &val); + if (val <= factor * mederr) /* <= in case mederr = 0 */ + ptaAddPt(ptad, x, y); + } + numaDestroy(&naerror); + + /* Do LSF again */ + ret = ptaGetLinearLSF(ptad, pa, pb, pnafit); + if (pptad) + *pptad = ptad; + else + ptaDestroy(&ptad); + + return ret; +} + + +/*! + * \brief ptaNoisyQuadraticLSF() + * + * \param[in] pta + * \param[in] factor reject outliers with error greater than this + * number of medians; typically ~ 3 + * \param[out] pptad [optional] with outliers removed + * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c + * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c + * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c + * \param[out] pmederr [optional] median error + * \param[out] pnafit [optional] numa of least square fit to ptad + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a quadratic least square fit to the set of points
+ *          in %pta.  It then evaluates the errors and removes points
+ *          whose error is >= factor * median_error.  It then re-runs
+ *          a quadratic LSF on the resulting points.
+ * 
+ */ +l_ok +ptaNoisyQuadraticLSF(PTA *pta, + l_float32 factor, + PTA **pptad, + l_float32 *pa, + l_float32 *pb, + l_float32 *pc, + l_float32 *pmederr, + NUMA **pnafit) +{ +l_int32 n, i, ret; +l_float32 x, y, yf, val, mederr; +NUMA *nafit, *naerror; +PTA *ptad; + + PROCNAME("ptaNoisyQuadraticLSF"); + + if (pptad) *pptad = NULL; + if (pa) *pa = 0.0; + if (pb) *pb = 0.0; + if (pc) *pc = 0.0; + if (pmederr) *pmederr = 0.0; + if (pnafit) *pnafit = NULL; + if (!pptad && !pa && !pb && !pc && !pnafit) + return ERROR_INT("no output requested", procName, 1); + if (factor <= 0.0) + return ERROR_INT("factor must be > 0.0", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if ((n = ptaGetCount(pta)) < 3) + return ERROR_INT("less than 3 pts found", procName, 1); + + if (ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit) != 0) + return ERROR_INT("error in quadratic LSF", procName, 1); + + /* Get the median error */ + naerror = numaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + numaGetFValue(nafit, i, &yf); + numaAddNumber(naerror, L_ABS(y - yf)); + } + numaGetMedian(naerror, &mederr); + if (pmederr) *pmederr = mederr; + numaDestroy(&nafit); + + /* Remove outliers */ + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &x, &y); + numaGetFValue(naerror, i, &val); + if (val <= factor * mederr) /* <= in case mederr = 0 */ + ptaAddPt(ptad, x, y); + } + numaDestroy(&naerror); + n = ptaGetCount(ptad); + if ((n = ptaGetCount(ptad)) < 3) { + ptaDestroy(&ptad); + return ERROR_INT("less than 3 pts found", procName, 1); + } + + /* Do LSF again */ + ret = ptaGetQuadraticLSF(ptad, pa, pb, pc, pnafit); + if (pptad) + *pptad = ptad; + else + ptaDestroy(&ptad); + + return ret; +} + + +/*! + * \brief applyLinearFit() + * + * \param[in] a, b linear fit coefficients + * \param[in] x + * \param[out] py y = a * x + b + * \return 0 if OK, 1 on error + */ +l_ok +applyLinearFit(l_float32 a, + l_float32 b, + l_float32 x, + l_float32 *py) +{ + PROCNAME("applyLinearFit"); + + if (!py) + return ERROR_INT("&y not defined", procName, 1); + + *py = a * x + b; + return 0; +} + + +/*! + * \brief applyQuadraticFit() + * + * \param[in] a, b, c quadratic fit coefficients + * \param[in] x + * \param[out] py y = a * x^2 + b * x + c + * \return 0 if OK, 1 on error + */ +l_ok +applyQuadraticFit(l_float32 a, + l_float32 b, + l_float32 c, + l_float32 x, + l_float32 *py) +{ + PROCNAME("applyQuadraticFit"); + + if (!py) + return ERROR_INT("&y not defined", procName, 1); + + *py = a * x * x + b * x + c; + return 0; +} + + +/*! + * \brief applyCubicFit() + * + * \param[in] a, b, c, d cubic fit coefficients + * \param[in] x + * \param[out] py y = a * x^3 + b * x^2 + c * x + d + * \return 0 if OK, 1 on error + */ +l_ok +applyCubicFit(l_float32 a, + l_float32 b, + l_float32 c, + l_float32 d, + l_float32 x, + l_float32 *py) +{ + PROCNAME("applyCubicFit"); + + if (!py) + return ERROR_INT("&y not defined", procName, 1); + + *py = a * x * x * x + b * x * x + c * x + d; + return 0; +} + + +/*! + * \brief applyQuarticFit() + * + * \param[in] a, b, c, d, e quartic fit coefficients + * \param[in] x + * \param[out] py y = a * x^4 + b * x^3 + c * x^2 + d * x + e + * \return 0 if OK, 1 on error + */ +l_ok +applyQuarticFit(l_float32 a, + l_float32 b, + l_float32 c, + l_float32 d, + l_float32 e, + l_float32 x, + l_float32 *py) +{ +l_float32 x2; + + PROCNAME("applyQuarticFit"); + + if (!py) + return ERROR_INT("&y not defined", procName, 1); + + x2 = x * x; + *py = a * x2 * x2 + b * x2 * x + c * x2 + d * x + e; + return 0; +} + + +/*---------------------------------------------------------------------* + * Interconversions with Pix * + *---------------------------------------------------------------------*/ +/*! + * \brief pixPlotAlongPta() + * + * \param[in] pixs any depth + * \param[in] pta set of points on which to plot + * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX + * \param[in] title [optional] for plot; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a debugging function.
+ *      (2) Removes existing colormaps and clips the pta to the input %pixs.
+ *      (3) If the image is RGB, three separate plots are generated.
+ * 
+ */ +l_ok +pixPlotAlongPta(PIX *pixs, + PTA *pta, + l_int32 outformat, + const char *title) +{ +char buffer[128]; +char *rtitle, *gtitle, *btitle; +static l_int32 count = 0; /* require separate temp files for each call */ +l_int32 i, x, y, d, w, h, npts, rval, gval, bval; +l_uint32 val; +NUMA *na, *nar, *nag, *nab; +PIX *pixt; + + PROCNAME("pixPlotAlongPta"); + + lept_mkdir("lept/plot"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (outformat != GPLOT_PNG && outformat != GPLOT_PS && + outformat != GPLOT_EPS && outformat != GPLOT_LATEX) { + L_WARNING("outformat invalid; using GPLOT_PNG\n", procName); + outformat = GPLOT_PNG; + } + + pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt); + w = pixGetWidth(pixt); + h = pixGetHeight(pixt); + npts = ptaGetCount(pta); + if (d == 32) { + nar = numaCreate(npts); + nag = numaCreate(npts); + nab = numaCreate(npts); + for (i = 0; i < npts; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w) + continue; + if (y < 0 || y >= h) + continue; + pixGetPixel(pixt, x, y, &val); + rval = GET_DATA_BYTE(&val, COLOR_RED); + gval = GET_DATA_BYTE(&val, COLOR_GREEN); + bval = GET_DATA_BYTE(&val, COLOR_BLUE); + numaAddNumber(nar, rval); + numaAddNumber(nag, gval); + numaAddNumber(nab, bval); + } + + snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); + rtitle = stringJoin("Red: ", title); + gplotSimple1(nar, outformat, buffer, rtitle); + snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); + gtitle = stringJoin("Green: ", title); + gplotSimple1(nag, outformat, buffer, gtitle); + snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); + btitle = stringJoin("Blue: ", title); + gplotSimple1(nab, outformat, buffer, btitle); + numaDestroy(&nar); + numaDestroy(&nag); + numaDestroy(&nab); + LEPT_FREE(rtitle); + LEPT_FREE(gtitle); + LEPT_FREE(btitle); + } else { + na = numaCreate(npts); + for (i = 0; i < npts; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w) + continue; + if (y < 0 || y >= h) + continue; + pixGetPixel(pixt, x, y, &val); + numaAddNumber(na, (l_float32)val); + } + + snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); + gplotSimple1(na, outformat, buffer, title); + numaDestroy(&na); + } + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief ptaGetPixelsFromPix() + * + * \param[in] pixs 1 bpp + * \param[in] box [optional] can be null + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a pta of fg pixels in the pix, within the box.
+ *          If box == NULL, it uses the entire pix.
+ * 
+ */ +PTA * +ptaGetPixelsFromPix(PIX *pixs, + BOX *box) +{ +l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh; +l_uint32 *data, *line; +PTA *pta; + + PROCNAME("ptaGetPixelsFromPix"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + xstart = ystart = 0; + xend = w - 1; + yend = h - 1; + if (box) { + boxGetGeometry(box, &xstart, &ystart, &bw, &bh); + xend = xstart + bw - 1; + yend = ystart + bh - 1; + } + + if ((pta = ptaCreate(0)) == NULL) + return (PTA *)ERROR_PTR("pta not made", procName, NULL); + for (i = ystart; i <= yend; i++) { + line = data + i * wpl; + for (j = xstart; j <= xend; j++) { + if (GET_DATA_BIT(line, j)) + ptaAddPt(pta, j, i); + } + } + + return pta; +} + + +/*! + * \brief pixGenerateFromPta() + * + * \param[in] pta + * \param[in] w, h of pix + * \return pix 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) Points are rounded to nearest ints.
+ *      (2) Any points outside (w,h) are silently discarded.
+ *      (3) Output 1 bpp pix has values 1 for each point in the pta.
+ * 
+ */ +PIX * +pixGenerateFromPta(PTA *pta, + l_int32 w, + l_int32 h) +{ +l_int32 n, i, x, y; +PIX *pix; + + PROCNAME("pixGenerateFromPta"); + + if (!pta) + return (PIX *)ERROR_PTR("pta not defined", procName, NULL); + + if ((pix = pixCreate(w, h, 1)) == NULL) + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w || y < 0 || y >= h) + continue; + pixSetPixel(pix, x, y, 1); + } + + return pix; +} + + +/*! + * \brief ptaGetBoundaryPixels() + * + * \param[in] pixs 1 bpp + * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a pta of either fg or bg boundary pixels.
+ *      (2) See also pixGeneratePtaBoundary() for rendering of
+ *          fg boundary pixels.
+ * 
+ */ +PTA * +ptaGetBoundaryPixels(PIX *pixs, + l_int32 type) +{ +PIX *pixt; +PTA *pta; + + PROCNAME("ptaGetBoundaryPixels"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) + return (PTA *)ERROR_PTR("invalid type", procName, NULL); + + if (type == L_BOUNDARY_FG) + pixt = pixMorphSequence(pixs, "e3.3", 0); + else + pixt = pixMorphSequence(pixs, "d3.3", 0); + pixXor(pixt, pixt, pixs); + pta = ptaGetPixelsFromPix(pixt, NULL); + + pixDestroy(&pixt); + return pta; +} + + +/*! + * \brief ptaaGetBoundaryPixels() + * + * \param[in] pixs 1 bpp + * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG + * \param[in] connectivity 4 or 8 + * \param[out] pboxa [optional] bounding boxes of the c.c. + * \param[out] ppixa [optional] pixa of the c.c. + * \return ptaa, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a ptaa of either fg or bg boundary pixels,
+ *          where each pta has the boundary pixels for a connected
+ *          component.
+ *      (2) We can't simply find all the boundary pixels and then select
+ *          those within the bounding box of each component, because
+ *          bounding boxes can overlap.  It is necessary to extract and
+ *          dilate or erode each component separately.  Note also that
+ *          special handling is required for bg pixels when the
+ *          component touches the pix boundary.
+ * 
+ */ +PTAA * +ptaaGetBoundaryPixels(PIX *pixs, + l_int32 type, + l_int32 connectivity, + BOXA **pboxa, + PIXA **ppixa) +{ +l_int32 i, n, w, h, x, y, bw, bh, left, right, top, bot; +BOXA *boxa; +PIX *pixt1, *pixt2; +PIXA *pixa; +PTA *pta1, *pta2; +PTAA *ptaa; + + PROCNAME("ptaaGetBoundaryPixels"); + + if (pboxa) *pboxa = NULL; + if (ppixa) *ppixa = NULL; + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) + return (PTAA *)ERROR_PTR("invalid type", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PTAA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + boxa = pixConnComp(pixs, &pixa, connectivity); + n = boxaGetCount(boxa); + ptaa = ptaaCreate(0); + for (i = 0; i < n; i++) { + pixt1 = pixaGetPix(pixa, i, L_CLONE); + boxaGetBoxGeometry(boxa, i, &x, &y, &bw, &bh); + left = right = top = bot = 0; + if (type == L_BOUNDARY_BG) { + if (x > 0) left = 1; + if (y > 0) top = 1; + if (x + bw < w) right = 1; + if (y + bh < h) bot = 1; + pixt2 = pixAddBorderGeneral(pixt1, left, right, top, bot, 0); + } else { + pixt2 = pixClone(pixt1); + } + pta1 = ptaGetBoundaryPixels(pixt2, type); + pta2 = ptaTransform(pta1, x - left, y - top, 1.0, 1.0); + ptaaAddPta(ptaa, pta2, L_INSERT); + ptaDestroy(&pta1); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + } + + if (pboxa) + *pboxa = boxa; + else + boxaDestroy(&boxa); + if (ppixa) + *ppixa = pixa; + else + pixaDestroy(&pixa); + return ptaa; +} + + +/*! + * \brief ptaaIndexLabeledPixels() + * + * \param[in] pixs 32 bpp, of indices of c.c. + * \param[out] pncc [optional] number of connected components + * \return ptaa, or NULL on error + * + *
+ * Notes:
+ *      (1) The pixel values in %pixs are the index of the connected component
+ *          to which the pixel belongs; %pixs is typically generated from
+ *          a 1 bpp pix by pixConnCompTransform().  Background pixels in
+ *          the generating 1 bpp pix are represented in %pixs by 0.
+ *          We do not check that the pixel values are correctly labelled.
+ *      (2) Each pta in the returned ptaa gives the pixel locations
+ *          correspnding to a connected component, with the label of each
+ *          given by the index of the pta into the ptaa.
+ *      (3) Initialize with the first pta in ptaa being empty and
+ *          representing the background value (index 0) in the pix.
+ * 
+ */ +PTAA * +ptaaIndexLabeledPixels(PIX *pixs, + l_int32 *pncc) +{ +l_int32 wpl, index, i, j, w, h; +l_uint32 maxval; +l_uint32 *data, *line; +PTA *pta; +PTAA *ptaa; + + PROCNAME("ptaaIndexLabeledPixels"); + + if (pncc) *pncc = 0; + if (!pixs || (pixGetDepth(pixs) != 32)) + return (PTAA *)ERROR_PTR("pixs undef or not 32 bpp", procName, NULL); + + /* The number of c.c. is the maximum pixel value. Use this to + * initialize ptaa with sufficient pta arrays */ + pixGetMaxValueInRect(pixs, NULL, &maxval, NULL, NULL); + if (pncc) *pncc = maxval; + pta = ptaCreate(1); + ptaa = ptaaCreate(maxval + 1); + ptaaInitFull(ptaa, pta); + ptaDestroy(&pta); + + /* Sweep over %pixs, saving the pixel coordinates of each pixel + * with nonzero value in the appropriate pta, indexed by that value. */ + pixGetDimensions(pixs, &w, &h, NULL); + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + for (i = 0; i < h; i++) { + line = data + wpl * i; + for (j = 0; j < w; j++) { + index = line[j]; + if (index > 0) + ptaaAddPt(ptaa, index, j, i); + } + } + + return ptaa; +} + + +/*! + * \brief ptaGetNeighborPixLocs() + * + * \param[in] pixs any depth + * \param[in] x, y pixel from which we search for nearest neighbors + * \param[in] conn 4 or 8 connectivity + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) Generates a pta of all valid neighbor pixel locations,
+ *          or NULL on error.
+ * 
+ */ +PTA * +ptaGetNeighborPixLocs(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 conn) +{ +l_int32 w, h; +PTA *pta; + + PROCNAME("ptaGetNeighborPixLocs"); + + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (x < 0 || x >= w || y < 0 || y >= h) + return (PTA *)ERROR_PTR("(x,y) not in pixs", procName, NULL); + if (conn != 4 && conn != 8) + return (PTA *)ERROR_PTR("conn not 4 or 8", procName, NULL); + + pta = ptaCreate(conn); + if (x > 0) + ptaAddPt(pta, x - 1, y); + if (x < w - 1) + ptaAddPt(pta, x + 1, y); + if (y > 0) + ptaAddPt(pta, x, y - 1); + if (y < h - 1) + ptaAddPt(pta, x, y + 1); + if (conn == 8) { + if (x > 0) { + if (y > 0) + ptaAddPt(pta, x - 1, y - 1); + if (y < h - 1) + ptaAddPt(pta, x - 1, y + 1); + } + if (x < w - 1) { + if (y > 0) + ptaAddPt(pta, x + 1, y - 1); + if (y < h - 1) + ptaAddPt(pta, x + 1, y + 1); + } + } + + return pta; +} + + +/*---------------------------------------------------------------------* + * Interconversion with Numa * + *---------------------------------------------------------------------*/ +/*! + * \brief numaConvertToPta1() + * + * \param[in] na numa with implicit y(x) + * \return pta if OK; null on error + */ +PTA * +numaConvertToPta1(NUMA *na) +{ +l_int32 i, n; +l_float32 startx, delx, val; +PTA *pta; + + PROCNAME("numaConvertToPta1"); + + if (!na) + return (PTA *)ERROR_PTR("na not defined", procName, NULL); + + n = numaGetCount(na); + pta = ptaCreate(n); + numaGetParameters(na, &startx, &delx); + for (i = 0; i < n; i++) { + numaGetFValue(na, i, &val); + ptaAddPt(pta, startx + i * delx, val); + } + return pta; +} + + +/*! + * \brief numaConvertToPta2() + * + * \param[in] nax + * \param[in] nay + * \return pta if OK; null on error + */ +PTA * +numaConvertToPta2(NUMA *nax, + NUMA *nay) +{ +l_int32 i, n, nx, ny; +l_float32 valx, valy; +PTA *pta; + + PROCNAME("numaConvertToPta2"); + + if (!nax || !nay) + return (PTA *)ERROR_PTR("nax and nay not both defined", procName, NULL); + + nx = numaGetCount(nax); + ny = numaGetCount(nay); + n = L_MIN(nx, ny); + if (nx != ny) + L_WARNING("nx = %d does not equal ny = %d\n", procName, nx, ny); + pta = ptaCreate(n); + for (i = 0; i < n; i++) { + numaGetFValue(nax, i, &valx); + numaGetFValue(nay, i, &valy); + ptaAddPt(pta, valx, valy); + } + return pta; +} + + +/*! + * \brief ptaConvertToNuma() + * + * \param[in] pta + * \param[out] pnax addr of nax + * \param[out] pnay addr of nay + * \return 0 if OK, 1 on error + */ +l_ok +ptaConvertToNuma(PTA *pta, + NUMA **pnax, + NUMA **pnay) +{ +l_int32 i, n; +l_float32 valx, valy; + + PROCNAME("ptaConvertToNuma"); + + if (pnax) *pnax = NULL; + if (pnay) *pnay = NULL; + if (!pnax || !pnay) + return ERROR_INT("&nax and &nay not both defined", procName, 1); + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + + n = ptaGetCount(pta); + *pnax = numaCreate(n); + *pnay = numaCreate(n); + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &valx, &valy); + numaAddNumber(*pnax, valx); + numaAddNumber(*pnay, valy); + } + return 0; +} + + +/*---------------------------------------------------------------------* + * Display Pta and Ptaa * + *---------------------------------------------------------------------*/ +/*! + * \brief pixDisplayPta() + * + * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] pta of path to be plotted + * \return pixd 32 bpp RGB version of pixs, with path in green. + * + *
+ * Notes:
+ *      (1) To write on an existing pixs, pixs must be 32 bpp and
+ *          call with pixd == pixs:
+ *             pixDisplayPta(pixs, pixs, pta);
+ *          To write to a new pix, use pixd == NULL and call:
+ *             pixd = pixDisplayPta(NULL, pixs, pta);
+ *      (2) On error, returns pixd to avoid losing pixs if called as
+ *             pixs = pixDisplayPta(pixs, pixs, pta);
+ * 
+ */ +PIX * +pixDisplayPta(PIX *pixd, + PIX *pixs, + PTA *pta) +{ +l_int32 i, n, w, h, x, y; +l_uint32 rpixel, gpixel, bpixel; + + PROCNAME("pixDisplayPta"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!pta) + return (PIX *)ERROR_PTR("pta not defined", procName, pixd); + if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) + return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); + + if (!pixd) + pixd = pixConvertTo32(pixs); + pixGetDimensions(pixd, &w, &h, NULL); + composeRGBPixel(255, 0, 0, &rpixel); /* start point */ + composeRGBPixel(0, 255, 0, &gpixel); + composeRGBPixel(0, 0, 255, &bpixel); /* end point */ + + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + if (x < 0 || x >= w || y < 0 || y >= h) + continue; + if (i == 0) + pixSetPixel(pixd, x, y, rpixel); + else if (i < n - 1) + pixSetPixel(pixd, x, y, gpixel); + else + pixSetPixel(pixd, x, y, bpixel); + } + + return pixd; +} + + +/*! + * \brief pixDisplayPtaaPattern() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp; 32 bpp if in place + * \param[in] ptaa giving locations at which the pattern is displayed + * \param[in] pixp 1 bpp pattern to be placed such that its reference + * point co-locates with each point in pta + * \param[in] cx, cy reference point in pattern + * \return pixd 32 bpp RGB version of pixs. + * + *
+ * Notes:
+ *      (1) To write on an existing pixs, pixs must be 32 bpp and
+ *          call with pixd == pixs:
+ *             pixDisplayPtaPattern(pixs, pixs, pta, ...);
+ *          To write to a new pix, use pixd == NULL and call:
+ *             pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...);
+ *      (2) Puts a random color on each pattern associated with a pta.
+ *      (3) On error, returns pixd to avoid losing pixs if called as
+ *             pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...);
+ *      (4) A typical pattern to be used is a circle, generated with
+ *             generatePtaFilledCircle()
+ * 
+ */ +PIX * +pixDisplayPtaaPattern(PIX *pixd, + PIX *pixs, + PTAA *ptaa, + PIX *pixp, + l_int32 cx, + l_int32 cy) +{ +l_int32 i, n; +l_uint32 color; +PIXCMAP *cmap; +PTA *pta; + + PROCNAME("pixDisplayPtaaPattern"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!ptaa) + return (PIX *)ERROR_PTR("ptaa not defined", procName, pixd); + if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) + return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); + if (!pixp) + return (PIX *)ERROR_PTR("pixp not defined", procName, pixd); + + if (!pixd) + pixd = pixConvertTo32(pixs); + + /* Use 256 random colors */ + cmap = pixcmapCreateRandom(8, 0, 0); + n = ptaaGetCount(ptaa); + for (i = 0; i < n; i++) { + pixcmapGetColor32(cmap, i % 256, &color); + pta = ptaaGetPta(ptaa, i, L_CLONE); + pixDisplayPtaPattern(pixd, pixd, pta, pixp, cx, cy, color); + ptaDestroy(&pta); + } + + pixcmapDestroy(&cmap); + return pixd; +} + + +/*! + * \brief pixDisplayPtaPattern() + * + * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] pta giving locations at which the pattern is displayed + * \param[in] pixp 1 bpp pattern to be placed such that its reference + * point co-locates with each point in pta + * \param[in] cx, cy reference point in pattern + * \param[in] color in 0xrrggbb00 format + * \return pixd 32 bpp RGB version of pixs. + * + *
+ * Notes:
+ *      (1) To write on an existing pixs, pixs must be 32 bpp and
+ *          call with pixd == pixs:
+ *             pixDisplayPtaPattern(pixs, pixs, pta, ...);
+ *          To write to a new pix, use pixd == NULL and call:
+ *             pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...);
+ *      (2) On error, returns pixd to avoid losing pixs if called as
+ *             pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...);
+ *      (3) A typical pattern to be used is a circle, generated with
+ *             generatePtaFilledCircle()
+ * 
+ */ +PIX * +pixDisplayPtaPattern(PIX *pixd, + PIX *pixs, + PTA *pta, + PIX *pixp, + l_int32 cx, + l_int32 cy, + l_uint32 color) +{ +l_int32 i, n, w, h, x, y; +PTA *ptat; + + PROCNAME("pixDisplayPtaPattern"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (!pta) + return (PIX *)ERROR_PTR("pta not defined", procName, pixd); + if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) + return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); + if (!pixp) + return (PIX *)ERROR_PTR("pixp not defined", procName, pixd); + + if (!pixd) + pixd = pixConvertTo32(pixs); + pixGetDimensions(pixs, &w, &h, NULL); + ptat = ptaReplicatePattern(pta, pixp, NULL, cx, cy, w, h); + + n = ptaGetCount(ptat); + for (i = 0; i < n; i++) { + ptaGetIPt(ptat, i, &x, &y); + if (x < 0 || x >= w || y < 0 || y >= h) + continue; + pixSetPixel(pixd, x, y, color); + } + + ptaDestroy(&ptat); + return pixd; +} + + +/*! + * \brief ptaReplicatePattern() + * + * \param[in] ptas "sparse" input pta + * \param[in] pixp [optional] 1 bpp pattern, to be replicated + * in output pta + * \param[in] ptap [optional] set of pts, to be replicated in output pta + * \param[in] cx, cy reference point in pattern + * \param[in] w, h clipping sizes for output pta + * \return ptad with all points of replicated pattern, or NULL on error + * + *
+ * Notes:
+ *      (1) You can use either the image %pixp or the set of pts %ptap.
+ *      (2) The pattern is placed with its reference point at each point
+ *          in ptas, and all the fg pixels are colleced into ptad.
+ *          For %pixp, this is equivalent to blitting pixp at each point
+ *          in ptas, and then converting the resulting pix to a pta.
+ * 
+ */ +PTA * +ptaReplicatePattern(PTA *ptas, + PIX *pixp, + PTA *ptap, + l_int32 cx, + l_int32 cy, + l_int32 w, + l_int32 h) +{ +l_int32 i, j, n, np, x, y, xp, yp, xf, yf; +PTA *ptat, *ptad; + + PROCNAME("ptaReplicatePattern"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (!pixp && !ptap) + return (PTA *)ERROR_PTR("no pattern is defined", procName, NULL); + if (pixp && ptap) + L_WARNING("pixp and ptap defined; using ptap\n", procName); + + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + if (ptap) + ptat = ptaClone(ptap); + else + ptat = ptaGetPixelsFromPix(pixp, NULL); + np = ptaGetCount(ptat); + for (i = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + for (j = 0; j < np; j++) { + ptaGetIPt(ptat, j, &xp, &yp); + xf = x - cx + xp; + yf = y - cy + yp; + if (xf >= 0 && xf < w && yf >= 0 && yf < h) + ptaAddPt(ptad, xf, yf); + } + } + + ptaDestroy(&ptat); + return ptad; +} + + +/*! + * \brief pixDisplayPtaa() + * + * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp + * \param[in] ptaa array of paths to be plotted + * \return pixd 32 bpp RGB version of pixs, with paths plotted + * in different colors, or NULL on error + */ +PIX * +pixDisplayPtaa(PIX *pixs, + PTAA *ptaa) +{ +l_int32 i, j, w, h, npta, npt, x, y, rv, gv, bv; +l_uint32 *pixela; +NUMA *na1, *na2, *na3; +PIX *pixd; +PTA *pta; + + PROCNAME("pixDisplayPtaa"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (!ptaa) + return (PIX *)ERROR_PTR("ptaa not defined", procName, NULL); + npta = ptaaGetCount(ptaa); + if (npta == 0) + return (PIX *)ERROR_PTR("no pta", procName, NULL); + + if ((pixd = pixConvertTo32(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixGetDimensions(pixd, &w, &h, NULL); + + /* Make a colormap for the paths */ + if ((pixela = (l_uint32 *)LEPT_CALLOC(npta, sizeof(l_uint32))) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("calloc fail for pixela", procName, NULL); + } + na1 = numaPseudorandomSequence(256, 14657); + na2 = numaPseudorandomSequence(256, 34631); + na3 = numaPseudorandomSequence(256, 54617); + for (i = 0; i < npta; i++) { + numaGetIValue(na1, i % 256, &rv); + numaGetIValue(na2, i % 256, &gv); + numaGetIValue(na3, i % 256, &bv); + composeRGBPixel(rv, gv, bv, &pixela[i]); + } + numaDestroy(&na1); + numaDestroy(&na2); + numaDestroy(&na3); + + for (i = 0; i < npta; i++) { + pta = ptaaGetPta(ptaa, i, L_CLONE); + npt = ptaGetCount(pta); + for (j = 0; j < npt; j++) { + ptaGetIPt(pta, j, &x, &y); + if (x < 0 || x >= w || y < 0 || y >= h) + continue; + pixSetPixel(pixd, x, y, pixela[i]); + } + ptaDestroy(&pta); + } + + LEPT_FREE(pixela); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ptafunc2.c b/hgdriver/3rdparty/hgOCR/leptonica/ptafunc2.c new file mode 100644 index 0000000..7712241 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ptafunc2.c @@ -0,0 +1,756 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file ptafunc2.c + *
+ *
+ *      --------------------------------------
+ *      This file has these Pta utilities:
+ *         - sorting
+ *         - ordered set operations
+ *         - hash map operations
+ *      --------------------------------------
+ *
+ *      Sorting
+ *           PTA        *ptaSort()
+ *           l_int32     ptaGetSortIndex()
+ *           PTA        *ptaSortByIndex()
+ *           PTAA       *ptaaSortByIndex()
+ *           l_int32     ptaGetRankValue()
+ *
+ *      Set operations using aset (rbtree)
+ *           PTA        *ptaUnionByAset()
+ *           PTA        *ptaRemoveDupsByAset()
+ *           PTA        *ptaIntersectionByAset()
+ *           L_ASET     *l_asetCreateFromPta()
+ *
+ *      Set operations using hashing (dnahash)
+ *           PTA        *ptaUnionByHash()
+ *           l_int32     ptaRemoveDupsByHash()
+ *           PTA        *ptaIntersectionByHash();
+ *           l_int32     ptaFindPtByHash()
+ *           L_DNAHASH  *l_dnaHashCreateFromPta()
+ *
+ *
+ * We have two implementations of set operations on an array of points:
+ *
+ *   (1) Using an underlying tree (rbtree)
+ *       This uses a good 64 bit hashing function for the key,
+ *       that is not expected to have hash collisions (and we do
+ *       not test for them).  The tree is built up of the hash
+ *       values, and if the hash is found in the tree, it is
+ *       assumed that the point has already been found.
+ *
+ *   (2) Using an underlying hashing of the keys (dnahash)
+ *       This uses a fast 64 bit hashing function for the key,
+ *       which is then hashed into a bucket (a dna in a dnaHash).
+ *       Because hash collisions can occur, the index into the
+ *       pta for the point that gave rise to that key is stored,
+ *       and the dna (bucket) is traversed, using the stored indices
+ *       to determine if that point had already been seen.
+ *
+ * 
+ */ + +#include "allheaders.h" + + +/*---------------------------------------------------------------------* + * Sorting * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaSort() + * + * \param[in] ptas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex [optional] index of sorted order into + * original array + * \return ptad sorted version of ptas, or NULL on error + */ +PTA * +ptaSort(PTA *ptas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex) +{ +PTA *ptad; +NUMA *naindex; + + PROCNAME("ptaSort"); + + if (pnaindex) *pnaindex = NULL; + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) + return (PTA *)ERROR_PTR("invalid sort type", procName, NULL); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return (PTA *)ERROR_PTR("invalid sort order", procName, NULL); + + if (ptaGetSortIndex(ptas, sorttype, sortorder, &naindex) != 0) + return (PTA *)ERROR_PTR("naindex not made", procName, NULL); + + ptad = ptaSortByIndex(ptas, naindex); + if (pnaindex) + *pnaindex = naindex; + else + numaDestroy(&naindex); + if (!ptad) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + return ptad; +} + + +/*! + * \brief ptaGetSortIndex() + * + * \param[in] ptas + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y + * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING + * \param[out] pnaindex index of sorted order into original array + * \return 0 if OK, 1 on error + */ +l_ok +ptaGetSortIndex(PTA *ptas, + l_int32 sorttype, + l_int32 sortorder, + NUMA **pnaindex) +{ +l_int32 i, n; +l_float32 x, y; +NUMA *na; + + PROCNAME("ptaGetSortIndex"); + + if (!pnaindex) + return ERROR_INT("&naindex not defined", procName, 1); + *pnaindex = NULL; + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) + return ERROR_INT("invalid sort type", procName, 1); + if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) + return ERROR_INT("invalid sort order", procName, 1); + + /* Build up numa of specific data */ + n = ptaGetCount(ptas); + if ((na = numaCreate(n)) == NULL) + return ERROR_INT("na not made", procName, 1); + for (i = 0; i < n; i++) { + ptaGetPt(ptas, i, &x, &y); + if (sorttype == L_SORT_BY_X) + numaAddNumber(na, x); + else + numaAddNumber(na, y); + } + + /* Get the sort index for data array */ + *pnaindex = numaGetSortIndex(na, sortorder); + numaDestroy(&na); + if (!*pnaindex) + return ERROR_INT("naindex not made", procName, 1); + return 0; +} + + +/*! + * \brief ptaSortByIndex() + * + * \param[in] ptas + * \param[in] naindex na that maps from the new pta to the input pta + * \return ptad sorted, or NULL on error + */ +PTA * +ptaSortByIndex(PTA *ptas, + NUMA *naindex) +{ +l_int32 i, index, n; +l_float32 x, y; +PTA *ptad; + + PROCNAME("ptaSortByIndex"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + if (!naindex) + return (PTA *)ERROR_PTR("naindex not defined", procName, NULL); + + /* Build up sorted pta using sort index */ + n = numaGetCount(naindex); + if ((ptad = ptaCreate(n)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", procName, NULL); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + ptaGetPt(ptas, index, &x, &y); + ptaAddPt(ptad, x, y); + } + + return ptad; +} + + +/*! + * \brief ptaaSortByIndex() + * + * \param[in] ptaas + * \param[in] naindex na that maps from the new ptaa to the input ptaa + * \return ptaad sorted, or NULL on error + */ +PTAA * +ptaaSortByIndex(PTAA *ptaas, + NUMA *naindex) +{ +l_int32 i, n, index; +PTA *pta; +PTAA *ptaad; + + PROCNAME("ptaaSortByIndex"); + + if (!ptaas) + return (PTAA *)ERROR_PTR("ptaas not defined", procName, NULL); + if (!naindex) + return (PTAA *)ERROR_PTR("naindex not defined", procName, NULL); + + n = ptaaGetCount(ptaas); + if (numaGetCount(naindex) != n) + return (PTAA *)ERROR_PTR("numa and ptaa sizes differ", procName, NULL); + ptaad = ptaaCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + pta = ptaaGetPta(ptaas, index, L_COPY); + ptaaAddPta(ptaad, pta, L_INSERT); + } + + return ptaad; +} + + +/*! + * \brief ptaGetRankValue() + * + * \param[in] pta + * \param[in] fract use 0.0 for smallest, 1.0 for largest + * \param[in] ptasort [optional] version of %pta sorted by %sorttype + * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y + * \param[out] pval rankval: the x or y value at %fract + * \return 0 if OK, 1 on error + */ +l_ok +ptaGetRankValue(PTA *pta, + l_float32 fract, + PTA *ptasort, + l_int32 sorttype, + l_float32 *pval) +{ +l_int32 index, n; +PTA *ptas; + + PROCNAME("ptaGetRankValue"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) + return ERROR_INT("invalid sort type", procName, 1); + if (fract < 0.0 || fract > 1.0) + return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); + if ((n = ptaGetCount(pta)) == 0) + return ERROR_INT("pta empty", procName, 1); + + if (ptasort) + ptas = ptasort; + else + ptas = ptaSort(pta, sorttype, L_SORT_INCREASING, NULL); + + index = (l_int32)(fract * (l_float32)(n - 1) + 0.5); + if (sorttype == L_SORT_BY_X) + ptaGetPt(ptas, index, pval, NULL); + else /* sort by y */ + ptaGetPt(ptas, index, NULL, pval); + + if (!ptasort) ptaDestroy(&ptas); + return 0; +} + + +/*---------------------------------------------------------------------* + * Set operations using aset (rbtree) * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaUnionByAset() + * + * \param[in] pta1, pta2 + * \return ptad with the union of the set of points, or NULL on error + * + *
+ * Notes:
+ *      (1) See sarrayRemoveDupsByAset() for the approach.
+ *      (2) The key is a 64-bit hash from the (x,y) pair.
+ *      (3) This is slower than ptaUnionByHash(), mostly because of the
+ *          nlogn sort to build up the rbtree.  Do not use for large
+ *          numbers of points (say, > 1M).
+ *      (4) The *Aset() functions use the sorted l_Aset, which is just
+ *          an rbtree in disguise.
+ * 
+ */ +PTA * +ptaUnionByAset(PTA *pta1, + PTA *pta2) +{ +PTA *pta3, *ptad; + + PROCNAME("ptaUnionByAset"); + + if (!pta1) + return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); + if (!pta2) + return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); + + /* Join */ + pta3 = ptaCopy(pta1); + ptaJoin(pta3, pta2, 0, -1); + + /* Eliminate duplicates */ + ptad = ptaRemoveDupsByAset(pta3); + ptaDestroy(&pta3); + return ptad; +} + + +/*! + * \brief ptaRemoveDupsByAset() + * + * \param[in] ptas assumed to be integer values + * \return ptad with duplicates removed, or NULL on error + * + *
+ * Notes:
+ *      (1) This is slower than ptaRemoveDupsByHash(), mostly because
+ *          of the nlogn sort to build up the rbtree.  Do not use for
+ *          large numbers of points (say, > 1M).
+ * 
+ */ +PTA * +ptaRemoveDupsByAset(PTA *ptas) +{ +l_int32 i, n, x, y; +PTA *ptad; +l_uint64 hash; +L_ASET *set; +RB_TYPE key; + + PROCNAME("ptaRemoveDupsByAset"); + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); + + set = l_asetCreate(L_UINT_TYPE); + n = ptaGetCount(ptas); + ptad = ptaCreate(n); + for (i = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + l_hashPtToUint64(x, y, &hash); + key.utype = hash; + if (!l_asetFind(set, key)) { + ptaAddPt(ptad, x, y); + l_asetInsert(set, key); + } + } + + l_asetDestroy(&set); + return ptad; +} + + +/*! + * \brief ptaIntersectionByAset() + * + * \param[in] pta1, pta2 + * \return ptad intersection of the point sets, or NULL on error + * + *
+ * Notes:
+ *      (1) See sarrayIntersectionByAset() for the approach.
+ *      (2) The key is a 64-bit hash from the (x,y) pair.
+ *      (3) This is slower than ptaIntersectionByHash(), mostly because
+ *          of the nlogn sort to build up the rbtree.  Do not use for
+ *          large numbers of points (say, > 1M).
+ * 
+ */ +PTA * +ptaIntersectionByAset(PTA *pta1, + PTA *pta2) +{ +l_int32 n1, n2, i, n, x, y; +l_uint64 hash; +L_ASET *set1, *set2; +RB_TYPE key; +PTA *pta_small, *pta_big, *ptad; + + PROCNAME("ptaIntersectionByAset"); + + if (!pta1) + return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); + if (!pta2) + return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); + + /* Put the elements of the biggest array into a set */ + n1 = ptaGetCount(pta1); + n2 = ptaGetCount(pta2); + pta_small = (n1 < n2) ? pta1 : pta2; /* do not destroy pta_small */ + pta_big = (n1 < n2) ? pta2 : pta1; /* do not destroy pta_big */ + set1 = l_asetCreateFromPta(pta_big); + + /* Build up the intersection of points */ + ptad = ptaCreate(0); + n = ptaGetCount(pta_small); + set2 = l_asetCreate(L_UINT_TYPE); + for (i = 0; i < n; i++) { + ptaGetIPt(pta_small, i, &x, &y); + l_hashPtToUint64(x, y, &hash); + key.utype = hash; + if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { + ptaAddPt(ptad, x, y); + l_asetInsert(set2, key); + } + } + + l_asetDestroy(&set1); + l_asetDestroy(&set2); + return ptad; +} + + +/*! + * \brief l_asetCreateFromPta() + * + * \param[in] pta + * \return set using a 64-bit hash of (x,y) as the key + */ +L_ASET * +l_asetCreateFromPta(PTA *pta) +{ +l_int32 i, n, x, y; +l_uint64 hash; +L_ASET *set; +RB_TYPE key; + + PROCNAME("l_asetCreateFromPta"); + + if (!pta) + return (L_ASET *)ERROR_PTR("pta not defined", procName, NULL); + + set = l_asetCreate(L_UINT_TYPE); + n = ptaGetCount(pta); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + l_hashPtToUint64(x, y, &hash); + key.utype = hash; + l_asetInsert(set, key); + } + + return set; +} + + +/*---------------------------------------------------------------------* + * Set operations using hashing (rbtree) * + *---------------------------------------------------------------------*/ +/*! + * \brief ptaUnionByHash() + * + * \param[in] pta1, pta2 + * \return ptad with the union of the set of points, or NULL on error + * + *
+ * Notes:
+ *      (1) This is faster than ptaUnionByAset(), because the
+ *          bucket lookup is O(n).  It should be used if the pts are
+ *          integers (e.g., representing pixel positions).
+ * 
+ */ +PTA * +ptaUnionByHash(PTA *pta1, + PTA *pta2) +{ +PTA *pta3, *ptad; + + PROCNAME("ptaUnionByHash"); + + if (!pta1) + return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); + if (!pta2) + return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); + + /* Join */ + pta3 = ptaCopy(pta1); + ptaJoin(pta3, pta2, 0, -1); + + /* Eliminate duplicates */ + ptaRemoveDupsByHash(pta3, &ptad, NULL); + ptaDestroy(&pta3); + return ptad; +} + + +/*! + * \brief ptaRemoveDupsByHash() + * + * \param[in] ptas assumed to be integer values + * \param[out] pptad unique set of pts; duplicates removed + * \param[out] pdahash [optional] dnahash used for lookup + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates a pta with unique values.
+ *      (2) The dnahash is built up with ptad to assure uniqueness.
+ *          It can be used to find if a point is in the set:
+ *              ptaFindPtByHash(ptad, dahash, x, y, &index)
+ *      (3) The hash of the (x,y) location is simple and fast.  It scales
+ *          up with the number of buckets to insure a fairly random
+ *          bucket selection for adjacent points.
+ *      (4) A Dna is used rather than a Numa because we need accurate
+ *          representation of 32-bit integers that are indices into ptas.
+ *          Integer --> float --> integer conversion makes errors for
+ *          integers larger than 10M.
+ *      (5) This is faster than ptaRemoveDupsByAset(), because the
+ *          bucket lookup is O(n), although there is a double-loop
+ *          lookup within the dna in each bucket.
+ * 
+ */ +l_ok +ptaRemoveDupsByHash(PTA *ptas, + PTA **pptad, + L_DNAHASH **pdahash) +{ +l_int32 i, n, index, items, x, y; +l_uint32 nsize; +l_uint64 key; +PTA *ptad; +L_DNAHASH *dahash; + + PROCNAME("ptaRemoveDupsByHash"); + + if (pdahash) *pdahash = NULL; + if (!pptad) + return ERROR_INT("&ptad not defined", procName, 1); + *pptad = NULL; + if (!ptas) + return ERROR_INT("ptas not defined", procName, 1); + + n = ptaGetCount(ptas); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ + dahash = l_dnaHashCreate(nsize, 8); + ptad = ptaCreate(n); + *pptad = ptad; + for (i = 0, items = 0; i < n; i++) { + ptaGetIPt(ptas, i, &x, &y); + ptaFindPtByHash(ptad, dahash, x, y, &index); + if (index < 0) { /* not found */ + l_hashPtToUint64(x, y, &key); + l_dnaHashAdd(dahash, key, (l_float64)items); + ptaAddPt(ptad, x, y); + items++; + } + } + + if (pdahash) + *pdahash = dahash; + else + l_dnaHashDestroy(&dahash); + return 0; +} + + +/*! + * \brief ptaIntersectionByHash() + * + * \param[in] pta1, pta2 + * \return ptad intersection of the point sets, or NULL on error + * + *
+ * Notes:
+ *      (1) This is faster than ptaIntersectionByAset(), because the
+ *          bucket lookup is O(n).  It should be used if the pts are
+ *          integers (e.g., representing pixel positions).
+ * 
+ */ +PTA * +ptaIntersectionByHash(PTA *pta1, + PTA *pta2) +{ +l_int32 n1, n2, nsmall, i, x, y, index1, index2; +l_uint32 nsize2; +l_uint64 key; +L_DNAHASH *dahash1, *dahash2; +PTA *pta_small, *pta_big, *ptad; + + PROCNAME("ptaIntersectionByHash"); + + if (!pta1) + return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); + if (!pta2) + return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); + + /* Put the elements of the biggest pta into a dnahash */ + n1 = ptaGetCount(pta1); + n2 = ptaGetCount(pta2); + pta_small = (n1 < n2) ? pta1 : pta2; /* do not destroy pta_small */ + pta_big = (n1 < n2) ? pta2 : pta1; /* do not destroy pta_big */ + dahash1 = l_dnaHashCreateFromPta(pta_big); + + /* Build up the intersection of points. Add to ptad + * if the point is in pta_big (using dahash1) but hasn't + * yet been seen in the traversal of pta_small (using dahash2). */ + ptad = ptaCreate(0); + nsmall = ptaGetCount(pta_small); + findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ + dahash2 = l_dnaHashCreate(nsize2, 0); + for (i = 0; i < nsmall; i++) { + ptaGetIPt(pta_small, i, &x, &y); + ptaFindPtByHash(pta_big, dahash1, x, y, &index1); + if (index1 >= 0) { /* found */ + ptaFindPtByHash(pta_small, dahash2, x, y, &index2); + if (index2 == -1) { /* not found */ + ptaAddPt(ptad, x, y); + l_hashPtToUint64(x, y, &key); + l_dnaHashAdd(dahash2, key, (l_float64)i); + } + } + } + + l_dnaHashDestroy(&dahash1); + l_dnaHashDestroy(&dahash2); + return ptad; +} + + +/*! + * \brief ptaFindPtByHash() + * + * \param[in] pta + * \param[in] dahash built from pta + * \param[in] x, y arbitrary points + * \param[out] pindex index into pta if (x,y) is in pta; -1 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Fast lookup in dnaHash associated with a pta, to see if a
+ *          random point (x,y) is already stored in the hash table.
+ *      (2) We use a strong hash function to minimize the chance that
+ *          two different points hash to the same key value.
+ *      (3) We select the number of buckets to be about 5% of the size
+ *          of the input %pta, so that when fully populated, each
+ *          bucket (dna) will have about 20 entries, each being an index
+ *          into %pta.  In lookup, after hashing to the key, and then
+ *          again to the bucket, we traverse the bucket (dna), using the
+ *          index into %pta to check if the point (x,y) has been found before.
+ * 
+ */ +l_ok +ptaFindPtByHash(PTA *pta, + L_DNAHASH *dahash, + l_int32 x, + l_int32 y, + l_int32 *pindex) +{ +l_int32 i, nvals, index, xi, yi; +l_uint64 key; +L_DNA *da; + + PROCNAME("ptaFindPtByHash"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = -1; + if (!pta) + return ERROR_INT("pta not defined", procName, 1); + if (!dahash) + return ERROR_INT("dahash not defined", procName, 1); + + l_hashPtToUint64(x, y, &key); + da = l_dnaHashGetDna(dahash, key, L_NOCOPY); + if (!da) return 0; + + /* Run through the da, looking for this point */ + nvals = l_dnaGetCount(da); + for (i = 0; i < nvals; i++) { + l_dnaGetIValue(da, i, &index); + ptaGetIPt(pta, index, &xi, &yi); + if (x == xi && y == yi) { + *pindex = index; + return 0; + } + } + + return 0; +} + + +/*! + * \brief l_dnaHashCreateFromPta() + * + * \param[in] pta + * \return dahash, or NULL on error + */ +L_DNAHASH * +l_dnaHashCreateFromPta(PTA *pta) +{ +l_int32 i, n, x, y; +l_uint32 nsize; +l_uint64 key; +L_DNAHASH *dahash; + + PROCNAME("l_dnaHashCreateFromPta"); + + if (!pta) + return (L_DNAHASH *)ERROR_PTR("pta not defined", procName, NULL); + + /* Build up dnaHash of indices, hashed by a key that is + * a large linear combination of x and y values designed to + * randomize the key. Having about 20 pts in each bucket is + * roughly optimal for speed for large sets. */ + n = ptaGetCount(pta); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ +/* fprintf(stderr, "Prime used: %d\n", nsize); */ + + /* Add each point, using the hash as key and the index into + * %ptas as the value. Storing the index enables operations + * that check for duplicates. */ + dahash = l_dnaHashCreate(nsize, 8); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + l_hashPtToUint64(x, y, &key); + l_dnaHashAdd(dahash, key, (l_float64)i); + } + + return dahash; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ptra.c b/hgdriver/3rdparty/hgOCR/leptonica/ptra.c new file mode 100644 index 0000000..99e441f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ptra.c @@ -0,0 +1,1006 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file ptra.c + *
+ *
+ *      Ptra creation and destruction
+ *          L_PTRA      *ptraCreate()
+ *          void        *ptraDestroy()
+ *
+ *      Add/insert/remove/replace generic ptr object
+ *          l_int32      ptraAdd()
+ *          static l_int32  ptraExtendArray()
+ *          l_int32      ptraInsert()
+ *          void        *ptraRemove()
+ *          void        *ptraRemoveLast()
+ *          void        *ptraReplace()
+ *          l_int32      ptraSwap()
+ *          l_int32      ptraCompactArray()
+ *
+ *      Other array operations
+ *          l_int32      ptraReverse()
+ *          l_int32      ptraJoin()
+ *
+ *      Simple Ptra accessors
+ *          l_int32      ptraGetMaxIndex()
+ *          l_int32      ptraGetActualCount()
+ *          void        *ptraGetPtrToItem()
+ *
+ *      Ptraa creation and destruction
+ *          L_PTRAA     *ptraaCreate()
+ *          void        *ptraaDestroy()
+ *
+ *      Ptraa accessors
+ *          l_int32      ptraaGetSize()
+ *          l_int32      ptraaInsertPtra()
+ *          L_PTRA      *ptraaGetPtra()
+ *
+ *      Ptraa conversion
+ *          L_PTRA      *ptraaFlattenToPtra()
+ *
+ *    Notes on the Ptra:
+ *
+ *    (1) The Ptra is a struct, not an array.  Always use the accessors
+ *        in this file, never the fields directly.
+ *    (2) Items can be placed anywhere in the allocated ptr array,
+ *        including one index beyond the last ptr (in which case the
+ *        ptr array is realloc'd).
+ *    (3) Thus, the items on the ptr array need not be compacted.  In
+ *        general there will be null pointers in the ptr array.
+ *    (4) A compacted array will remain compacted on removal if
+ *        arbitrary items are removed with compaction, or if items
+ *        are removed from the end of the array.
+ *    (5) For addition to and removal from the end of the array, this
+ *        functions exactly like a stack, and with the same O(1) cost.
+ *    (6) This differs from the generic stack in that we allow
+ *        random access for insertion, removal and replacement.
+ *        Removal can be done without compacting the array.
+ *        Insertion into a null ptr in the array has no effect on
+ *        the other pointers, but insertion into a location already
+ *        occupied by an item has a cost proportional to the
+ *        distance to the next null ptr in the array.
+ *    (7) Null ptrs are valid input args for both insertion and
+ *        replacement; this allows arbitrary swapping.
+ *    (8) The item in the array with the largest index is at pa->imax.
+ *        This can be any value from -1 (initialized; all array ptrs
+ *        are null) up to pa->nalloc - 1 (the last ptr in the array).
+ *    (9) In referring to the array: the first ptr is the "top" or
+ *        "beginning"; the last pointer is the "bottom" or "end";
+ *        items are shifted "up" towards the top when compaction occurs;
+ *        and items are shifted "down" towards the bottom when forced to
+ *        move due to an insertion.
+ *   (10) It should be emphasized that insertion, removal and replacement
+ *        are general:
+ *         * You can insert an item into any ptr location in the
+ *           allocated ptr array, as well as into the next ptr address
+ *           beyond the allocated array (in which case a realloc will occur).
+ *         * You can remove or replace an item from any ptr location
+ *           in the allocated ptr array.
+ *         * When inserting into an occupied location, you have
+ *           three options for downshifting.
+ *         * When removing, you can either leave the ptr null or
+ *           compact the array.
+ *
+ *    Notes on the Ptraa:
+ *
+ *    (1) The Ptraa is a fixed size ptr array for holding Ptra.
+ *        In that respect, it is different from other pointer arrays, which
+ *        are extensible and grow using the *Add*() functions.
+ *    (2) In general, the Ptra ptrs in the Ptraa can be randomly occupied.
+ *        A typical usage is to allow an O(n) horizontal sort of Pix,
+ *        where the size of the Ptra array is the width of the image,
+ *        and each Ptra is an array of all the Pix at a specific x location.
+ * 
+ */ + +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Static function */ +static l_int32 ptraExtendArray(L_PTRA *pa); + + +/*--------------------------------------------------------------------------* + * Ptra creation and destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraCreate() + * + * \param[in] n size of ptr array to be alloc'd; use 0 for default + * \return pa, or NULL on error + */ +L_PTRA * +ptraCreate(l_int32 n) +{ +L_PTRA *pa; + + PROCNAME("ptraCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + pa = (L_PTRA *)LEPT_CALLOC(1, sizeof(L_PTRA)); + if ((pa->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { + ptraDestroy(&pa, 0, 0); + return (L_PTRA *)ERROR_PTR("ptr array not made", procName, NULL); + } + pa->nalloc = n; + pa->imax = -1; + pa->nactual = 0; + return pa; +} + + +/*! + * \brief ptraDestroy() + * + * \param[in,out] ppa will be set to null before returning + * \param[in] freeflag TRUE to free each remaining item in the array + * \param[in] warnflag TRUE to warn if any remaining items + * are not destroyed + * \return void + * + *
+ * Notes:
+ *      (1) If %freeflag == TRUE, frees each item in the array.
+ *      (2) If %freeflag == FALSE and %warnflag == TRUE, and there are
+ *          items on the array, this gives a warning and destroys the array.
+ *          If these items are not owned elsewhere, this will cause
+ *          a memory leak of all the items that were on the array.
+ *          So if the items are not owned elsewhere and require their
+ *          own destroy function, they must be destroyed before the ptra.
+ *      (3) If %warnflag == FALSE, no warnings will be issued.  This is
+ *          useful if the items are owned elsewhere, such as a
+ *          PixMemoryStore().
+ *      (4) To destroy the ptra, we destroy the ptr array, then
+ *          the ptra, and then null the contents of the input ptr.
+ * 
+ */ +void +ptraDestroy(L_PTRA **ppa, + l_int32 freeflag, + l_int32 warnflag) +{ +l_int32 i, nactual; +void *item; +L_PTRA *pa; + + PROCNAME("ptraDestroy"); + + if (ppa == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((pa = *ppa) == NULL) + return; + + ptraGetActualCount(pa, &nactual); + if (nactual > 0) { + if (freeflag) { + for (i = 0; i <= pa->imax; i++) { + if ((item = ptraRemove(pa, i, L_NO_COMPACTION)) != NULL) + LEPT_FREE(item); + } + } else if (warnflag) { + L_WARNING("potential memory leak of %d items in ptra\n", + procName, nactual); + } + } + + LEPT_FREE(pa->array); + LEPT_FREE(pa); + *ppa = NULL; + return; +} + + +/*--------------------------------------------------------------------------* + * Add/insert/remove/replace generic ptr object * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraAdd() + * + * \param[in] pa ptra + * \param[in] item generic ptr to a struct + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This adds the element to the next location beyond imax,
+ *          which is the largest occupied ptr in the array.  This is
+ *          what you expect from a stack, where all ptrs up to and
+ *          including imax are occupied, but here the occuption of
+ *          items in the array is entirely arbitrary.
+ * 
+ */ +l_ok +ptraAdd(L_PTRA *pa, + void *item) +{ +l_int32 imax; + + PROCNAME("ptraAdd"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (!item) + return ERROR_INT("item not defined", procName, 1); + + ptraGetMaxIndex(pa, &imax); + if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) + return ERROR_INT("extension failure", procName, 1); + pa->array[imax + 1] = (void *)item; + pa->imax++; + pa->nactual++; + return 0; +} + + +/*! + * \brief ptraExtendArray() + * + * \param[in] pa + * \return 0 if OK, 1 on error + */ +static l_int32 +ptraExtendArray(L_PTRA *pa) +{ + PROCNAME("ptraExtendArray"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + + if ((pa->array = (void **)reallocNew((void **)&pa->array, + sizeof(void *) * pa->nalloc, + 2 * sizeof(void *) * pa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + pa->nalloc *= 2; + return 0; +} + + +/*! + * \brief ptraInsert() + * + * \param[in] pa ptra + * \param[in] index location in ptra to insert new value + * \param[in] item generic ptr to a struct; can be null + * \param[in] shiftflag L_AUTO_DOWNSHIFT, L_MIN_DOWNSHIFT, L_FULL_DOWNSHIFT + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This checks first to see if the location is valid, and
+ *          then if there is presently an item there.  If there is not,
+ *          it is simply inserted into that location.
+ *      (2) If there is an item at the insert location, items must be
+ *          moved down to make room for the insert.  In the downward
+ *          shift there are three options, given by %shiftflag.
+ *            ~ If %shiftflag == L_AUTO_DOWNSHIFT, a decision is made
+ *              whether, in a cascade of items, to downshift a minimum
+ *              amount or for all items above %index.  The decision is
+ *              based on the expectation of finding holes (null ptrs)
+ *              between %index and the bottom of the array.
+ *              Assuming the holes are distributed uniformly, if 2 or more
+ *              holes are expected, we do a minimum shift.
+ *            ~ If %shiftflag == L_MIN_DOWNSHIFT, the downward shifting
+ *              cascade of items progresses a minimum amount, until
+ *              the first empty slot is reached.  This mode requires
+ *              some computation before the actual shifting is done.
+ *            ~ If %shiftflag == L_FULL_DOWNSHIFT, a shifting cascade is
+ *              performed where pa[i] --> pa[i + 1] for all i >= index.
+ *              Then, the item is inserted at pa[index].
+ *      (3) If you are not using L_AUTO_DOWNSHIFT, the rule of thumb is
+ *          to use L_FULL_DOWNSHIFT if the array is compacted (each
+ *          element points to an item), and to use L_MIN_DOWNSHIFT
+ *          if there are a significant number of null pointers.
+ *          There is no penalty to using L_MIN_DOWNSHIFT for a
+ *          compacted array, however, because the full shift is required
+ *          and we don't do the O(n) computation to look for holes.
+ *      (4) This should not be used repeatedly on large arrays,
+ *          because the function is generally O(n).
+ *      (5) However, it can be used repeatedly if we start with an empty
+ *          ptr array and insert only once at each location.  For example,
+ *          you can support an array of Numa, where at each ptr location
+ *          you store either 0 or 1 Numa, and the Numa can be added
+ *          randomly to the ptr array.
+ * 
+ */ +l_ok +ptraInsert(L_PTRA *pa, + l_int32 index, + void *item, + l_int32 shiftflag) +{ +l_int32 i, ihole, imax; +l_float32 nexpected; + + PROCNAME("ptraInsert"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (index < 0 || index > pa->nalloc) + return ERROR_INT("index not in [0 ... nalloc]", procName, 1); + if (shiftflag != L_AUTO_DOWNSHIFT && shiftflag != L_MIN_DOWNSHIFT && + shiftflag != L_FULL_DOWNSHIFT) + return ERROR_INT("invalid shiftflag", procName, 1); + + if (item) pa->nactual++; + if (index == pa->nalloc) { /* can happen when index == n */ + if (ptraExtendArray(pa)) + return ERROR_INT("extension failure", procName, 1); + } + + /* We are inserting into a hole or adding to the end of the array. + * No existing items are moved. */ + ptraGetMaxIndex(pa, &imax); + if (pa->array[index] == NULL) { + pa->array[index] = item; + if (item && index > imax) /* new item put beyond max so far */ + pa->imax = index; + return 0; + } + + /* We are inserting at the location of an existing item, + * forcing the existing item and those below to shift down. + * First, extend the array automatically if the last element + * (nalloc - 1) is occupied (imax). This may not be necessary + * in every situation, but only an anomalous sequence of insertions + * into the array would cause extra ptr allocation. */ + if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) + return ERROR_INT("extension failure", procName, 1); + + /* If there are no holes, do a full downshift. + * Otherwise, if L_AUTO_DOWNSHIFT, use the expected number + * of holes between index and n to determine the shift mode */ + if (imax + 1 == pa->nactual) { + shiftflag = L_FULL_DOWNSHIFT; + } else if (shiftflag == L_AUTO_DOWNSHIFT) { + if (imax < 10) { + shiftflag = L_FULL_DOWNSHIFT; /* no big deal */ + } else { + nexpected = (l_float32)(imax - pa->nactual) * + (l_float32)((imax - index) / imax); + shiftflag = (nexpected > 2.0) ? L_MIN_DOWNSHIFT : L_FULL_DOWNSHIFT; + } + } + + if (shiftflag == L_MIN_DOWNSHIFT) { /* run down looking for a hole */ + for (ihole = index + 1; ihole <= imax; ihole++) { + if (pa->array[ihole] == NULL) + break; + } + } else { /* L_FULL_DOWNSHIFT */ + ihole = imax + 1; + } + + for (i = ihole; i > index; i--) + pa->array[i] = pa->array[i - 1]; + pa->array[index] = (void *)item; + if (ihole == imax + 1) /* the last item was shifted down */ + pa->imax++; + + return 0; +} + + +/*! + * \brief ptraRemove() + * + * \param[in] pa ptra + * \param[in] index element to be removed + * \param[in] flag L_NO_COMPACTION, L_COMPACTION + * \return item, or NULL on error + * + *
+ * Notes:
+ *      (1) If flag == L_NO_COMPACTION, this removes the item and
+ *          nulls the ptr on the array.  If it takes the last item
+ *          in the array, pa->n is reduced to the next item.
+ *      (2) If flag == L_COMPACTION, this compacts the array for
+ *          for all i >= index.  It should not be used repeatedly on
+ *          large arrays, because compaction is O(n).
+ *      (3) The ability to remove without automatic compaction allows
+ *          removal with cost O(1).
+ * 
+ */ +void * +ptraRemove(L_PTRA *pa, + l_int32 index, + l_int32 flag) +{ +l_int32 i, imax, fromend, icurrent; +void *item; + + PROCNAME("ptraRemove"); + + if (!pa) + return (void *)ERROR_PTR("pa not defined", procName, NULL); + ptraGetMaxIndex(pa, &imax); + if (index < 0 || index > imax) + return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); + + item = pa->array[index]; + if (item) + pa->nactual--; + pa->array[index] = NULL; + + /* If we took the last item, need to reduce pa->n */ + fromend = (index == imax); + if (fromend) { + for (i = index - 1; i >= 0; i--) { + if (pa->array[i]) + break; + } + pa->imax = i; + } + + /* Compact from index to the end of the array */ + if (!fromend && flag == L_COMPACTION) { + for (icurrent = index, i = index + 1; i <= imax; i++) { + if (pa->array[i]) + pa->array[icurrent++] = pa->array[i]; + } + pa->imax = icurrent - 1; + } + return item; +} + + +/*! + * \brief ptraRemoveLast() + * + * \param[in] pa ptra + * \return item, or NULL on error or if the array is empty + */ +void * +ptraRemoveLast(L_PTRA *pa) +{ +l_int32 imax; + + PROCNAME("ptraRemoveLast"); + + if (!pa) + return (void *)ERROR_PTR("pa not defined", procName, NULL); + + /* Remove the last item in the array. No compaction is required. */ + ptraGetMaxIndex(pa, &imax); + if (imax >= 0) + return ptraRemove(pa, imax, L_NO_COMPACTION); + else /* empty */ + return NULL; +} + + +/*! + * \brief ptraReplace() + * + * \param[in] pa ptra + * \param[in] index element to be replaced + * \param[in] item new generic ptr to a struct; can be null + * \param[in] freeflag TRUE to free old item; FALSE to return it + * \return item old item, if it exists and is not freed, + * or NULL on error + */ +void * +ptraReplace(L_PTRA *pa, + l_int32 index, + void *item, + l_int32 freeflag) +{ +l_int32 imax; +void *olditem; + + PROCNAME("ptraReplace"); + + if (!pa) + return (void *)ERROR_PTR("pa not defined", procName, NULL); + ptraGetMaxIndex(pa, &imax); + if (index < 0 || index > imax) + return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); + + olditem = pa->array[index]; + pa->array[index] = item; + if (!item && olditem) + pa->nactual--; + else if (item && !olditem) + pa->nactual++; + + if (freeflag == FALSE) + return olditem; + + if (olditem) + LEPT_FREE(olditem); + return NULL; +} + + +/*! + * \brief ptraSwap() + * + * \param[in] pa ptra + * \param[in] index1 + * \param[in] index2 + * \return 0 if OK, 1 on error + */ +l_ok +ptraSwap(L_PTRA *pa, + l_int32 index1, + l_int32 index2) +{ +l_int32 imax; +void *item; + + PROCNAME("ptraSwap"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (index1 == index2) + return 0; + ptraGetMaxIndex(pa, &imax); + if (index1 < 0 || index1 > imax || index2 < 0 || index2 > imax) + return ERROR_INT("invalid index: not in [0 ... imax]", procName, 1); + + item = ptraRemove(pa, index1, L_NO_COMPACTION); + item = ptraReplace(pa, index2, item, FALSE); + ptraInsert(pa, index1, item, L_MIN_DOWNSHIFT); + return 0; +} + + +/*! + * \brief ptraCompactArray() + * + * \param[in] pa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This compacts the items on the array, filling any empty ptrs.
+ *      (2) This does not change the size of the array of ptrs.
+ * 
+ */ +l_ok +ptraCompactArray(L_PTRA *pa) +{ +l_int32 i, imax, nactual, index; + + PROCNAME("ptraCompactArray"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + ptraGetMaxIndex(pa, &imax); + ptraGetActualCount(pa, &nactual); + if (imax + 1 == nactual) return 0; + + /* Compact the array */ + for (i = 0, index = 0; i <= imax; i++) { + if (pa->array[i]) + pa->array[index++] = pa->array[i]; + } + pa->imax = index - 1; + if (nactual != index) + L_ERROR("index = %d; != nactual\n", procName, index); + + return 0; +} + + +/*----------------------------------------------------------------------* + * Other array operations * + *----------------------------------------------------------------------*/ +/*! + * \brief ptraReverse() + * + * \param[in] pa ptra + * \return 0 if OK, 1 on error + */ +l_ok +ptraReverse(L_PTRA *pa) +{ +l_int32 i, imax; + + PROCNAME("ptraReverse"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + ptraGetMaxIndex(pa, &imax); + + for (i = 0; i < (imax + 1) / 2; i++) + ptraSwap(pa, i, imax - i); + return 0; +} + + +/*! + * \brief ptraJoin() + * + * \param[in] pa1 add to this one + * \param[in] pa2 appended to pa1, and emptied of items; can be null + * \return 0 if OK, 1 on error + */ +l_ok +ptraJoin(L_PTRA *pa1, + L_PTRA *pa2) +{ +l_int32 i, imax; +void *item; + + PROCNAME("ptraJoin"); + + if (!pa1) + return ERROR_INT("pa1 not defined", procName, 1); + if (!pa2) + return 0; + + ptraGetMaxIndex(pa2, &imax); + for (i = 0; i <= imax; i++) { + item = ptraRemove(pa2, i, L_NO_COMPACTION); + ptraAdd(pa1, item); + } + + return 0; +} + + + +/*----------------------------------------------------------------------* + * Simple ptra accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief ptraGetMaxIndex() + * + * \param[in] pa ptra + * \param[out] pmaxindex index of last item in the array; + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The largest index to an item in the array is %maxindex.
+ *          %maxindex is one less than the number of items that would be
+ *          in the array if there were no null pointers between 0
+ *          and %maxindex - 1.  However, because the internal ptr array
+ *          need not be compacted, there may be NULL pointers at
+ *          indices below %maxindex; for example, if items have
+ *          been removed.
+ *      (2) When an item is added to the end of the array, it goes
+ *          into pa->array[maxindex + 1], and maxindex is then
+ *          incremented by 1.
+ *      (3) If there are no items in the array, this returns %maxindex = -1.
+ * 
+ */ +l_ok +ptraGetMaxIndex(L_PTRA *pa, + l_int32 *pmaxindex) +{ + PROCNAME("ptraGetMaxIndex"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (!pmaxindex) + return ERROR_INT("&maxindex not defined", procName, 1); + *pmaxindex = pa->imax; + return 0; +} + + +/*! + * \brief ptraGetActualCount() + * + * \param[in] pa ptra + * \param[out] pcount actual number of items on the ptr array + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) The actual number of items on the ptr array, pa->nactual,
+ *          will be smaller than pa->n if the array is not compacted.
+ * 
+ */ +l_ok +ptraGetActualCount(L_PTRA *pa, + l_int32 *pcount) +{ + PROCNAME("ptraGetActualCount"); + + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + if (!pcount) + return ERROR_INT("&count not defined", procName, 1); + *pcount = pa->nactual; + + return 0; +} + + +/*! + * \brief ptraGetPtrToItem() + * + * \param[in] pa ptra + * \param[in] index of element to be retrieved + * \return a ptr to the element, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a ptr to the item.  You must cast it to
+ *          the type of item.  Do not destroy it; the item belongs
+ *          to the Ptra.
+ *      (2) This can access all possible items on the ptr array.
+ *          If an item doesn't exist, it returns null.
+ * 
+ */ +void * +ptraGetPtrToItem(L_PTRA *pa, + l_int32 index) +{ + PROCNAME("ptraGetPtrToItem"); + + if (!pa) + return (void *)ERROR_PTR("pa not defined", procName, NULL); + if (index < 0 || index >= pa->nalloc) + return (void *)ERROR_PTR("index not in [0 ... nalloc-1]", + procName, NULL); + + return pa->array[index]; +} + + +/*--------------------------------------------------------------------------* + * Ptraa creation and destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaCreate() + * + * \param[in] n size of ptr array to be alloc'd + * \return paa, or NULL on error + * + *
+ * Notes:
+ *      (1) The ptraa is generated with a fixed size, that can not change.
+ *          The ptra can be generated and inserted randomly into this array.
+ * 
+ */ +L_PTRAA * +ptraaCreate(l_int32 n) +{ +L_PTRAA *paa; + + PROCNAME("ptraaCreate"); + + if (n <= 0) + return (L_PTRAA *)ERROR_PTR("n must be > 0", procName, NULL); + + paa = (L_PTRAA *)LEPT_CALLOC(1, sizeof(L_PTRAA)); + if ((paa->ptra = (L_PTRA **)LEPT_CALLOC(n, sizeof(L_PTRA *))) == NULL) { + ptraaDestroy(&paa, 0, 0); + return (L_PTRAA *)ERROR_PTR("ptr array not made", procName, NULL); + } + paa->nalloc = n; + return paa; +} + + +/*! + * \brief ptraaDestroy() + * + * \param[in,out] ppaa will be set to null before returning + * \param[in] freeflag TRUE to free each remaining item in each ptra + * \param[in] warnflag TRUE to warn if any remaining items + * are not destroyed + * \return void + * + *
+ * Notes:
+ *      (1) See ptraDestroy() for use of %freeflag and %warnflag.
+ *      (2) To destroy the ptraa, we destroy each ptra, then the ptr array,
+ *          then the ptraa, and then null the contents of the input ptr.
+ * 
+ */ +void +ptraaDestroy(L_PTRAA **ppaa, + l_int32 freeflag, + l_int32 warnflag) +{ +l_int32 i, n; +L_PTRA *pa; +L_PTRAA *paa; + + PROCNAME("ptraaDestroy"); + + if (ppaa == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((paa = *ppaa) == NULL) + return; + + ptraaGetSize(paa, &n); + for (i = 0; i < n; i++) { + pa = ptraaGetPtra(paa, i, L_REMOVE); + ptraDestroy(&pa, freeflag, warnflag); + } + + LEPT_FREE(paa->ptra); + LEPT_FREE(paa); + *ppaa = NULL; + return; +} + + +/*--------------------------------------------------------------------------* + * Ptraa accessors * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaGetSize() + * + * \param[in] paa + * \param[out] psize size of ptr array + * \return 0 if OK; 1 on error + */ +l_ok +ptraaGetSize(L_PTRAA *paa, + l_int32 *psize) +{ + PROCNAME("ptraaGetSize"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + *psize = paa->nalloc; + + return 0; +} + + +/*! + * \brief ptraaInsertPtra() + * + * \param[in] paa ptraa + * \param[in] index location in array for insertion + * \param[in] pa to be inserted + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Caller should check return value.  On success, the Ptra
+ *          is inserted in the Ptraa and is owned by it.  However,
+ *          on error, the Ptra remains owned by the caller.
+ * 
+ */ +l_ok +ptraaInsertPtra(L_PTRAA *paa, + l_int32 index, + L_PTRA *pa) +{ +l_int32 n; + + PROCNAME("ptraaInsertPtra"); + + if (!paa) + return ERROR_INT("paa not defined", procName, 1); + if (!pa) + return ERROR_INT("pa not defined", procName, 1); + ptraaGetSize(paa, &n); + if (index < 0 || index >= n) + return ERROR_INT("invalid index", procName, 1); + if (paa->ptra[index] != NULL) + return ERROR_INT("ptra already stored at index", procName, 1); + + paa->ptra[index] = pa; + return 0; +} + + +/*! + * \brief ptraaGetPtra() + * + * \param[in] paa ptraa + * \param[in] index location in array + * \param[in] accessflag L_HANDLE_ONLY, L_REMOVE + * \return ptra at index location, or NULL on error or if there + * is no ptra there. + * + *
+ * Notes:
+ *      (1) This returns the ptra ptr.  If %accessflag == L_HANDLE_ONLY,
+ *          the ptra is left on the ptraa.  If %accessflag == L_REMOVE,
+ *          the ptr in the ptraa is set to NULL, and the caller
+ *          is responsible for disposing of the ptra (either putting it
+ *          back on the ptraa, or destroying it).
+ *      (2) This returns NULL if there is no Ptra at the index location.
+ * 
+ */ +L_PTRA * +ptraaGetPtra(L_PTRAA *paa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 n; +L_PTRA *pa; + + PROCNAME("ptraaGetPtra"); + + if (!paa) + return (L_PTRA *)ERROR_PTR("paa not defined", procName, NULL); + ptraaGetSize(paa, &n); + if (index < 0 || index >= n) + return (L_PTRA *)ERROR_PTR("invalid index", procName, NULL); + if (accessflag != L_HANDLE_ONLY && accessflag != L_REMOVE) + return (L_PTRA *)ERROR_PTR("invalid accessflag", procName, NULL); + + pa = paa->ptra[index]; + if (accessflag == L_REMOVE) + paa->ptra[index] = NULL; + return pa; +} + + +/*--------------------------------------------------------------------------* + * Ptraa conversion * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaFlattenToPtra() + * + * \param[in] paa ptraa + * \return ptra, or NULL on error + * + *
+ * Notes:
+ *      (1) This 'flattens' the ptraa to a ptra, taking the items in
+ *          each ptra, in order, starting with the first ptra, etc.
+ *      (2) As a side-effect, the ptra are all removed from the ptraa
+ *          and destroyed, leaving an empty ptraa.
+ * 
+ */ +L_PTRA * +ptraaFlattenToPtra(L_PTRAA *paa) +{ +l_int32 i, n; +L_PTRA *pat, *pad; + + PROCNAME("ptraaFlattenToPtra"); + + if (!paa) + return (L_PTRA *)ERROR_PTR("paa not defined", procName, NULL); + + pad = ptraCreate(0); + ptraaGetSize(paa, &n); + for (i = 0; i < n; i++) { + pat = ptraaGetPtra(paa, i, L_REMOVE); + if (!pat) continue; + ptraJoin(pad, pat); + ptraDestroy(&pat, FALSE, FALSE); /* they're all empty */ + } + + return pad; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/ptra.h b/hgdriver/3rdparty/hgOCR/leptonica/ptra.h new file mode 100644 index 0000000..dc5216c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/ptra.h @@ -0,0 +1,95 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_PTRA_H +#define LEPTONICA_PTRA_H + +/*! + * \file ptra.h + * + *
+ *  Contains the following structs:
+ *      struct L_Ptra
+ *      struct L_Ptraa
+ *
+ *  Contains definitions for:
+ *      L_Ptra compaction flags for removal
+ *      L_Ptra shifting flags for insert
+ *      L_Ptraa accessor flags
+ * 
+ */ + + +/*------------------------------------------------------------------------* + * Generic Ptr Array Structs * + *------------------------------------------------------------------------*/ + + /*! Generic pointer array */ +struct L_Ptra +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 imax; /*!< greatest valid index */ + l_int32 nactual; /*!< actual number of stored elements */ + void **array; /*!< ptr array */ +}; +typedef struct L_Ptra L_PTRA; + + + /*! Array of generic pointer arrays */ +struct L_Ptraa +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + struct L_Ptra **ptra; /*!< array of ptra */ +}; +typedef struct L_Ptraa L_PTRAA; + + + +/*------------------------------------------------------------------------* + * Accessor and modifier flags for L_Ptra and L_Ptraa * + *------------------------------------------------------------------------*/ + +/*! Ptra Removal */ +enum { + L_NO_COMPACTION = 1, /*!< null the pointer only */ + L_COMPACTION = 2 /*!< compact the array */ +}; + +/*! Ptra Insertion */ +enum { + L_AUTO_DOWNSHIFT = 0, /*!< choose based on number of holes */ + L_MIN_DOWNSHIFT = 1, /*!< downshifts min # of ptrs below insert */ + L_FULL_DOWNSHIFT = 2 /*!< downshifts all ptrs below insert */ +}; + +/*! Ptraa Accessor */ +enum { + L_HANDLE_ONLY = 0, /*!< ptr to L_Ptra; caller can inspect only */ + L_REMOVE = 1 /*!< caller owns; destroy or save in L_Ptraa */ +}; + + +#endif /* LEPTONICA_PTRA_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/quadtree.c b/hgdriver/3rdparty/hgOCR/leptonica/quadtree.c new file mode 100644 index 0000000..bdcef13 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/quadtree.c @@ -0,0 +1,697 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file quadtree.c + *
+ *
+ *      Top level quadtree linear statistics
+ *          l_int32   pixQuadtreeMean()
+ *          l_int32   pixQuadtreeVariance()
+ *
+ *      Statistics in an arbitrary rectangle
+ *          l_int32   pixMeanInRectangle()
+ *          l_int32   pixVarianceInRectangle()
+ *
+ *      Quadtree regions
+ *          BOXAA    *boxaaQuadtreeRegions()
+ *
+ *      Quadtree access
+ *          l_int32   quadtreeGetParent()
+ *          l_int32   quadtreeGetChildren()
+ *          l_int32   quadtreeMaxLevels()
+ *
+ *      Display quadtree
+ *          PIX      *fpixaDisplayQuadtree()
+ *
+ *
+ *  There are many other statistical quantities that can be computed
+ *  in a quadtree, such as rank values, and these can be added as
+ *  the need arises.
+ *
+ *  Similar results that can approximate a single level of the quadtree
+ *  can be generated by pixGetAverageTiled().  There we specify the
+ *  tile size over which the mean, mean square, and root variance
+ *  are generated; the results are saved in a (reduced size) pix.
+ *  Because the tile dimensions are integers, it is usually not possible
+ *  to obtain tilings that are a power of 2, as required for quadtrees.
+ * 
+ */ + +#include +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_BOXES 0 +#endif /* !NO_CONSOLE_IO */ + + +/*----------------------------------------------------------------------* + * Top-level quadtree linear statistics * + *----------------------------------------------------------------------*/ +/*! + * \brief pixQuadtreeMean() + * + * \param[in] pixs 8 bpp, no colormap + * \param[in] nlevels in quadtree; max allowed depends on image size + * \param[in] pix_ma input mean accumulator; can be null + * \param[out] pfpixa mean values in quadtree + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned fpixa has %nlevels of fpix, each containing
+ *          the mean values at its level.  Level 0 has a
+ *          single value; level 1 has 4 values; level 2 has 16; etc.
+ * 
+ */ +l_ok +pixQuadtreeMean(PIX *pixs, + l_int32 nlevels, + PIX *pix_ma, + FPIXA **pfpixa) +{ +l_int32 i, j, w, h, size, n; +l_float32 val; +BOX *box; +BOXA *boxa; +BOXAA *baa; +FPIX *fpix; +PIX *pix_mac; + + PROCNAME("pixQuadtreeMean"); + + if (!pfpixa) + return ERROR_INT("&fpixa not defined", procName, 1); + *pfpixa = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (nlevels > quadtreeMaxLevels(w, h)) + return ERROR_INT("nlevels too large for image", procName, 1); + + if (!pix_ma) + pix_mac = pixBlockconvAccum(pixs); + else + pix_mac = pixClone(pix_ma); + if (!pix_mac) + return ERROR_INT("pix_mac not made", procName, 1); + + if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { + pixDestroy(&pix_mac); + return ERROR_INT("baa not made", procName, 1); + } + + *pfpixa = fpixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + size = 1 << i; + n = boxaGetCount(boxa); /* n == size * size */ + fpix = fpixCreate(size, size); + for (j = 0; j < n; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + pixMeanInRectangle(pixs, box, pix_mac, &val); + fpixSetPixel(fpix, j % size, j / size, val); + boxDestroy(&box); + } + fpixaAddFPix(*pfpixa, fpix, L_INSERT); + boxaDestroy(&boxa); + } + + pixDestroy(&pix_mac); + boxaaDestroy(&baa); + return 0; +} + + +/*! + * \brief pixQuadtreeVariance() + * + * \param[in] pixs 8 bpp, no colormap + * \param[in] nlevels in quadtree + * \param[in] pix_ma input mean accumulator; can be null + * \param[in] dpix_msa input mean square accumulator; can be null + * \param[out] pfpixa_v [optional] variance values in quadtree + * \param[out] pfpixa_rv [optional] root variance values in quadtree + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned fpixav and fpixarv have %nlevels of fpix,
+ *          each containing at the respective levels the variance
+ *          and root variance values.
+ * 
+ */ +l_ok +pixQuadtreeVariance(PIX *pixs, + l_int32 nlevels, + PIX *pix_ma, + DPIX *dpix_msa, + FPIXA **pfpixa_v, + FPIXA **pfpixa_rv) +{ +l_int32 i, j, w, h, size, n; +l_float32 var, rvar; +BOX *box; +BOXA *boxa; +BOXAA *baa; +FPIX *fpixv, *fpixrv; +PIX *pix_mac; /* copy of mean accumulator */ +DPIX *dpix_msac; /* msa clone */ + + PROCNAME("pixQuadtreeVariance"); + + if (!pfpixa_v && !pfpixa_rv) + return ERROR_INT("neither &fpixav nor &fpixarv defined", procName, 1); + if (pfpixa_v) *pfpixa_v = NULL; + if (pfpixa_rv) *pfpixa_rv = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (nlevels > quadtreeMaxLevels(w, h)) + return ERROR_INT("nlevels too large for image", procName, 1); + + if (!pix_ma) + pix_mac = pixBlockconvAccum(pixs); + else + pix_mac = pixClone(pix_ma); + if (!pix_mac) + return ERROR_INT("pix_mac not made", procName, 1); + if (!dpix_msa) + dpix_msac = pixMeanSquareAccum(pixs); + else + dpix_msac = dpixClone(dpix_msa); + if (!dpix_msac) { + pixDestroy(&pix_mac); + return ERROR_INT("dpix_msac not made", procName, 1); + } + + if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { + pixDestroy(&pix_mac); + dpixDestroy(&dpix_msac); + return ERROR_INT("baa not made", procName, 1); + } + + if (pfpixa_v) *pfpixa_v = fpixaCreate(nlevels); + if (pfpixa_rv) *pfpixa_rv = fpixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + size = 1 << i; + n = boxaGetCount(boxa); /* n == size * size */ + if (pfpixa_v) fpixv = fpixCreate(size, size); + if (pfpixa_rv) fpixrv = fpixCreate(size, size); + for (j = 0; j < n; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + pixVarianceInRectangle(pixs, box, pix_mac, dpix_msac, &var, &rvar); + if (pfpixa_v) fpixSetPixel(fpixv, j % size, j / size, var); + if (pfpixa_rv) fpixSetPixel(fpixrv, j % size, j / size, rvar); + boxDestroy(&box); + } + if (pfpixa_v) fpixaAddFPix(*pfpixa_v, fpixv, L_INSERT); + if (pfpixa_rv) fpixaAddFPix(*pfpixa_rv, fpixrv, L_INSERT); + boxaDestroy(&boxa); + } + + pixDestroy(&pix_mac); + dpixDestroy(&dpix_msac); + boxaaDestroy(&baa); + return 0; +} + + +/*----------------------------------------------------------------------* + * Statistics in an arbitrary rectangle * + *----------------------------------------------------------------------*/ +/*! + * \brief pixMeanInRectangle() + * + * \param[in] pixs 8 bpp + * \param[in] box region to compute mean value + * \param[in] pixma mean accumulator + * \param[out] pval mean value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function is intended to be used for many rectangles
+ *          on the same image.  It can find the mean within a
+ *          rectangle in O(1), independent of the size of the rectangle.
+ * 
+ */ +l_ok +pixMeanInRectangle(PIX *pixs, + BOX *box, + PIX *pixma, + l_float32 *pval) +{ +l_int32 w, h, bx, by, bw, bh; +l_uint32 val00, val01, val10, val11; +l_float32 norm; +BOX *boxc; + + PROCNAME("pixMeanInRectangle"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (!pixma) + return ERROR_INT("pixma not defined", procName, 1); + + /* Clip rectangle to image */ + pixGetDimensions(pixs, &w, &h, NULL); + boxc = boxClipToRectangle(box, w, h); + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + boxDestroy(&boxc); + + if (bw == 0 || bh == 0) + return ERROR_INT("no pixels in box", procName, 1); + + /* Use up to 4 points in the accumulator */ + norm = 1.0 / ((l_float32)(bw) * bh); + if (bx > 0 && by > 0) { + pixGetPixel(pixma, bx + bw - 1, by + bh - 1, &val11); + pixGetPixel(pixma, bx + bw - 1, by - 1, &val10); + pixGetPixel(pixma, bx - 1, by + bh - 1, &val01); + pixGetPixel(pixma, bx - 1, by - 1, &val00); + *pval = norm * (val11 - val01 + val00 - val10); + } else if (by > 0) { /* bx == 0 */ + pixGetPixel(pixma, bw - 1, by + bh - 1, &val11); + pixGetPixel(pixma, bw - 1, by - 1, &val10); + *pval = norm * (val11 - val10); + } else if (bx > 0) { /* by == 0 */ + pixGetPixel(pixma, bx + bw - 1, bh - 1, &val11); + pixGetPixel(pixma, bx - 1, bh - 1, &val01); + *pval = norm * (val11 - val01); + } else { /* bx == 0 && by == 0 */ + pixGetPixel(pixma, bw - 1, bh - 1, &val11); + *pval = norm * val11; + } + + return 0; +} + + +/*! + * \brief pixVarianceInRectangle() + * + * \param[in] pixs 8 bpp + * \param[in] box region to compute variance and/or root variance + * \param[in] pix_ma mean accumulator + * \param[in] dpix_msa mean square accumulator + * \param[out] pvar [optional] variance + * \param[out] prvar [optional] root variance + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function is intended to be used for many rectangles
+ *          on the same image.  It can find the variance and/or the
+ *          square root of the variance within a rectangle in O(1),
+ *          independent of the size of the rectangle.
+ * 
+ */ +l_ok +pixVarianceInRectangle(PIX *pixs, + BOX *box, + PIX *pix_ma, + DPIX *dpix_msa, + l_float32 *pvar, + l_float32 *prvar) +{ +l_int32 w, h, bx, by, bw, bh; +l_uint32 val00, val01, val10, val11; +l_float64 dval00, dval01, dval10, dval11, mval, msval, var, norm; +BOX *boxc; + + PROCNAME("pixVarianceInRectangle"); + + if (!pvar && !prvar) + return ERROR_INT("neither &var nor &rvar defined", procName, 1); + if (pvar) *pvar = 0.0; + if (prvar) *prvar = 0.0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined", procName, 1); + if (!box) + return ERROR_INT("box not defined", procName, 1); + if (!pix_ma) + return ERROR_INT("pix_ma not defined", procName, 1); + if (!dpix_msa) + return ERROR_INT("dpix_msa not defined", procName, 1); + + /* Clip rectangle to image */ + pixGetDimensions(pixs, &w, &h, NULL); + boxc = boxClipToRectangle(box, w, h); + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + boxDestroy(&boxc); + + if (bw == 0 || bh == 0) + return ERROR_INT("no pixels in box", procName, 1); + + /* Use up to 4 points in the accumulators */ + norm = 1.0 / ((l_float32)(bw) * bh); + if (bx > 0 && by > 0) { + pixGetPixel(pix_ma, bx + bw - 1, by + bh - 1, &val11); + pixGetPixel(pix_ma, bx + bw - 1, by - 1, &val10); + pixGetPixel(pix_ma, bx - 1, by + bh - 1, &val01); + pixGetPixel(pix_ma, bx - 1, by - 1, &val00); + dpixGetPixel(dpix_msa, bx + bw - 1, by + bh - 1, &dval11); + dpixGetPixel(dpix_msa, bx + bw - 1, by - 1, &dval10); + dpixGetPixel(dpix_msa, bx - 1, by + bh - 1, &dval01); + dpixGetPixel(dpix_msa, bx - 1, by - 1, &dval00); + mval = norm * (val11 - val01 + val00 - val10); + msval = norm * (dval11 - dval01 + dval00 - dval10); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else if (by > 0) { /* bx == 0 */ + pixGetPixel(pix_ma, bw - 1, by + bh - 1, &val11); + pixGetPixel(pix_ma, bw - 1, by - 1, &val10); + dpixGetPixel(dpix_msa, bw - 1, by + bh - 1, &dval11); + dpixGetPixel(dpix_msa, bw - 1, by - 1, &dval10); + mval = norm * (val11 - val10); + msval = norm * (dval11 - dval10); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else if (bx > 0) { /* by == 0 */ + pixGetPixel(pix_ma, bx + bw - 1, bh - 1, &val11); + pixGetPixel(pix_ma, bx - 1, bh - 1, &val01); + dpixGetPixel(dpix_msa, bx + bw - 1, bh - 1, &dval11); + dpixGetPixel(dpix_msa, bx - 1, bh - 1, &dval01); + mval = norm * (val11 - val01); + msval = norm * (dval11 - dval01); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else { /* bx == 0 && by == 0 */ + pixGetPixel(pix_ma, bw - 1, bh - 1, &val11); + dpixGetPixel(dpix_msa, bw - 1, bh - 1, &dval11); + mval = norm * val11; + msval = norm * dval11; + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Quadtree regions * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaQuadtreeRegions() + * + * \param[in] w, h size of pix that is being quadtree-ized + * \param[in] nlevels number of levels in quadtree + * \return baa for quadtree regions at each level, or NULL on error + * + *
+ * Notes:
+ *      (1) The returned boxaa has %nlevels of boxa, each containing
+ *          the set of rectangles at that level.  The rectangle at
+ *          level 0 is the entire region; at level 1 the region is
+ *          divided into 4 rectangles, and at level n there are n^4
+ *          rectangles.
+ *      (2) At each level, the rectangles in the boxa are in "raster"
+ *          order, with LR (fast scan) and TB (slow scan).
+ * 
+ */ +BOXAA * +boxaaQuadtreeRegions(l_int32 w, + l_int32 h, + l_int32 nlevels) +{ +l_int32 i, j, k, maxpts, nside, nbox, bw, bh; +l_int32 *xstart, *xend, *ystart, *yend; +BOX *box; +BOXA *boxa; +BOXAA *baa; + + PROCNAME("boxaaQuadtreeRegions"); + + if (nlevels < 1) + return (BOXAA *)ERROR_PTR("nlevels must be >= 1", procName, NULL); + if (w < (1 << (nlevels - 1))) + return (BOXAA *)ERROR_PTR("w doesn't support nlevels", procName, NULL); + if (h < (1 << (nlevels - 1))) + return (BOXAA *)ERROR_PTR("h doesn't support nlevels", procName, NULL); + + baa = boxaaCreate(nlevels); + maxpts = 1 << (nlevels - 1); + xstart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + xend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + ystart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + yend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + for (k = 0; k < nlevels; k++) { + nside = 1 << k; /* number of boxes in each direction */ + for (i = 0; i < nside; i++) { + xstart[i] = (w - 1) * i / nside; + if (i > 0) xstart[i]++; + xend[i] = (w - 1) * (i + 1) / nside; + ystart[i] = (h - 1) * i / nside; + if (i > 0) ystart[i]++; + yend[i] = (h - 1) * (i + 1) / nside; +#if DEBUG_BOXES + fprintf(stderr, + "k = %d, xs[%d] = %d, xe[%d] = %d, ys[%d] = %d, ye[%d] = %d\n", + k, i, xstart[i], i, xend[i], i, ystart[i], i, yend[i]); +#endif /* DEBUG_BOXES */ + } + nbox = 1 << (2 * k); + boxa = boxaCreate(nbox); + for (i = 0; i < nside; i++) { + bh = yend[i] - ystart[i] + 1; + for (j = 0; j < nside; j++) { + bw = xend[j] - xstart[j] + 1; + box = boxCreate(xstart[j], ystart[i], bw, bh); + boxaAddBox(boxa, box, L_INSERT); + } + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + + LEPT_FREE(xstart); + LEPT_FREE(xend); + LEPT_FREE(ystart); + LEPT_FREE(yend); + return baa; +} + + +/*----------------------------------------------------------------------* + * Quadtree access * + *----------------------------------------------------------------------*/ +/*! + * \brief quadtreeGetParent() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] level, x, y of current pixel + * \param[out] pval parent pixel value, or 0.0 on error + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Check return value for error.  On error, val is returned as 0.0.
+ *      (2) The parent is located at:
+ *             level - 1
+ *             (x/2, y/2)
+ * 
+ */ +l_ok +quadtreeGetParent(FPIXA *fpixa, + l_int32 level, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 n; + + PROCNAME("quadtreeGetParent"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + n = fpixaGetCount(fpixa); + if (level < 1 || level >= n) + return ERROR_INT("invalid level", procName, 1); + + if (fpixaGetPixel(fpixa, level - 1, x / 2, y / 2, pval) != 0) + return ERROR_INT("invalid coordinates", procName, 1); + return 0; +} + + +/*! + * \brief quadtreeGetChildren() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] level, x, y of current pixel + * \param[out] pval00, pval01, + * pval10, pval11 four child pixel values + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Check return value for error.  On error, all return vals are 0.0.
+ *      (2) The returned child pixels are located at:
+ *             level + 1
+ *             (2x, 2y), (2x+1, 2y), (2x, 2y+1), (2x+1, 2y+1)
+ * 
+ */ +l_ok +quadtreeGetChildren(FPIXA *fpixa, + l_int32 level, + l_int32 x, + l_int32 y, + l_float32 *pval00, + l_float32 *pval10, + l_float32 *pval01, + l_float32 *pval11) +{ +l_int32 n; + + PROCNAME("quadtreeGetChildren"); + + if (!pval00 || !pval01 || !pval10 || !pval11) + return ERROR_INT("&val* not all defined", procName, 1); + *pval00 = *pval10 = *pval01 = *pval11 = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", procName, 1); + n = fpixaGetCount(fpixa); + if (level < 0 || level >= n - 1) + return ERROR_INT("invalid level", procName, 1); + + if (fpixaGetPixel(fpixa, level + 1, 2 * x, 2 * y, pval00) != 0) + return ERROR_INT("invalid coordinates", procName, 1); + fpixaGetPixel(fpixa, level + 1, 2 * x + 1, 2 * y, pval10); + fpixaGetPixel(fpixa, level + 1, 2 * x, 2 * y + 1, pval01); + fpixaGetPixel(fpixa, level + 1, 2 * x + 1, 2 * y + 1, pval11); + return 0; +} + + +/*! + * \brief quadtreeMaxLevels() + * + * \param[in] w, h dimensions of image + * \return maxlevels maximum number of levels allowed, or -1 on error + * + *
+ * Notes:
+ *      (1) The criterion for maxlevels is that the subdivision not
+ *          go down below the single pixel level.  The 1.5 factor
+ *          is intended to keep any rectangle from accidentally
+ *          having zero dimension due to integer truncation.
+ * 
+ */ +l_int32 +quadtreeMaxLevels(l_int32 w, + l_int32 h) +{ +l_int32 i, minside; + + minside = L_MIN(w, h); + for (i = 0; i < 20; i++) { /* 2^10 = one million */ + if (minside < (1.5 * (1 << i))) + return i - 1; + } + + return -1; /* fail if the image has over a trillion pixels! */ +} + + +/*----------------------------------------------------------------------* + * Display quadtree * + *----------------------------------------------------------------------*/ +/*! + * \brief fpixaDisplayQuadtree() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] factor replication factor at lowest level + * \param[in] fontsize 4, ... 20 + * \return pixd 8 bpp, mosaic of quadtree images, or NULL on error + * + *
+ * Notes:
+ *      (1) The mean and root variance fall naturally in the 8 bpp range,
+ *          but the variance is typically outside the range.  This
+ *          function displays 8 bpp pix clipped to 255, so the image
+ *          pixels will mostly be 255 (white).
+ * 
+ */ +PIX * +fpixaDisplayQuadtree(FPIXA *fpixa, + l_int32 factor, + l_int32 fontsize) +{ +char buf[256]; +l_int32 nlevels, i, mag, w; +L_BMF *bmf; +FPIX *fpix; +PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixd; +PIXA *pixat; + + PROCNAME("fpixaDisplayQuadtree"); + + if (!fpixa) + return (PIX *)ERROR_PTR("fpixa not defined", procName, NULL); + + if ((nlevels = fpixaGetCount(fpixa)) == 0) + return (PIX *)ERROR_PTR("pixas empty", procName, NULL); + + if ((bmf = bmfCreate(NULL, fontsize)) == NULL) + L_ERROR("bmf not made; text will not be added", procName); + pixat = pixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + fpix = fpixaGetFPix(fpixa, i, L_CLONE); + pixt1 = fpixConvertToPix(fpix, 8, L_CLIP_TO_ZERO, 0); + mag = factor * (1 << (nlevels - i - 1)); + pixt2 = pixExpandReplicate(pixt1, mag); + pixt3 = pixConvertTo32(pixt2); + snprintf(buf, sizeof(buf), "Level %d\n", i); + pixt4 = pixAddSingleTextblock(pixt3, bmf, buf, 0xff000000, + L_ADD_BELOW, NULL); + pixaAddPix(pixat, pixt4, L_INSERT); + fpixDestroy(&fpix); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + } + w = pixGetWidth(pixt4); + pixd = pixaDisplayTiledInRows(pixat, 32, nlevels * (w + 80), 1.0, 0, 30, 2); + + pixaDestroy(&pixat); + bmfDestroy(&bmf); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/queue.c b/hgdriver/3rdparty/hgOCR/leptonica/queue.c new file mode 100644 index 0000000..568e68e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/queue.c @@ -0,0 +1,323 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file queue.c + *
+ *
+ *      Create/Destroy L_Queue
+ *          L_QUEUE        *lqueueCreate()
+ *          void           *lqueueDestroy()
+ *
+ *      Operations to add/remove to/from a L_Queue
+ *          l_int32         lqueueAdd()
+ *          static l_int32  lqueueExtendArray()
+ *          void           *lqueueRemove()
+ *
+ *      Accessors
+ *          l_int32         lqueueGetCount()
+ *
+ *      Debug output
+ *          l_int32         lqueuePrint()
+ *
+ *    The lqueue is a fifo that implements a queue of void* pointers.
+ *    It can be used to hold a queue of any type of struct.
+ *    Internally, it maintains two counters:
+ *        nhead:  location of head (in ptrs) from the beginning
+ *                of the buffer
+ *        nelem:  number of ptr elements stored in the queue
+ *    As items are added to the queue, nelem increases.
+ *    As items are removed, nhead increases and nelem decreases.
+ *    Any time the tail reaches the end of the allocated buffer,
+ *      all the pointers are shifted to the left, so that the head
+ *      is at the beginning of the array.
+ *    If the buffer becomes more than 3/4 full, it doubles in size.
+ *
+ *    [A circular queue would allow us to skip the shifting and
+ *    to resize only when the buffer is full.  For most applications,
+ *    the extra work we do for a linear queue is not significant.]
+ * 
+ */ + +#include +#include "allheaders.h" + +static const l_int32 MIN_BUFFER_SIZE = 20; /* n'importe quoi */ +static const l_int32 INITIAL_BUFFER_ARRAYSIZE = 1024; /* n'importe quoi */ + + /* Static function */ +static l_int32 lqueueExtendArray(L_QUEUE *lq); + + +/*--------------------------------------------------------------------------* + * L_Queue create/destroy * + *--------------------------------------------------------------------------*/ +/*! + * \brief lqueueCreate() + * + * \param[in] nalloc size of ptr array to be alloc'd; 0 for default + * \return lqueue, or NULL on error + * + *
+ * Notes:
+ *      (1) Allocates a ptr array of given size, and initializes counters.
+ * 
+ */ +L_QUEUE * +lqueueCreate(l_int32 nalloc) +{ +L_QUEUE *lq; + + PROCNAME("lqueueCreate"); + + if (nalloc < MIN_BUFFER_SIZE) + nalloc = INITIAL_BUFFER_ARRAYSIZE; + + lq = (L_QUEUE *)LEPT_CALLOC(1, sizeof(L_QUEUE)); + if ((lq->array = (void **)LEPT_CALLOC(nalloc, sizeof(void *))) == NULL) { + lqueueDestroy(&lq, 0); + return (L_QUEUE *)ERROR_PTR("ptr array not made", procName, NULL); + } + lq->nalloc = nalloc; + lq->nhead = lq->nelem = 0; + return lq; +} + + +/*! + * \brief lqueueDestroy() + * + * \param[in,out] plq will be set to null before returning + * \param[in] freeflag TRUE to free each remaining struct in the array + * \return void + * + *
+ * Notes:
+ *      (1) If freeflag is TRUE, frees each struct in the array.
+ *      (2) If freeflag is FALSE but there are elements on the array,
+ *          gives a warning and destroys the array.  This will
+ *          cause a memory leak of all the items that were on the queue.
+ *          So if the items require their own destroy function, they
+ *          must be destroyed before the queue.  The same applies to the
+ *          auxiliary stack, if it is used.
+ *      (3) To destroy the L_Queue, we destroy the ptr array, then
+ *          the lqueue, and then null the contents of the input ptr.
+ * 
+ */ +void +lqueueDestroy(L_QUEUE **plq, + l_int32 freeflag) +{ +void *item; +L_QUEUE *lq; + + PROCNAME("lqueueDestroy"); + + if (plq == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((lq = *plq) == NULL) + return; + + if (freeflag) { + while(lq->nelem > 0) { + item = lqueueRemove(lq); + LEPT_FREE(item); + } + } else if (lq->nelem > 0) { + L_WARNING("memory leak of %d items in lqueue!\n", procName, lq->nelem); + } + + if (lq->array) + LEPT_FREE(lq->array); + if (lq->stack) + lstackDestroy(&lq->stack, freeflag); + LEPT_FREE(lq); + *plq = NULL; + + return; +} + + +/*--------------------------------------------------------------------------* + * Accessors * + *--------------------------------------------------------------------------*/ +/*! + * \brief lqueueAdd() + * + * \param[in] lq lqueue + * \param[in] item to be added to the tail of the queue + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The algorithm is as follows.  If the queue is populated
+ *          to the end of the allocated array, shift all ptrs toward
+ *          the beginning of the array, so that the head of the queue
+ *          is at the beginning of the array.  Then, if the array is
+ *          more than 0.75 full, realloc with double the array size.
+ *          Finally, add the item to the tail of the queue.
+ * 
+ */ +l_ok +lqueueAdd(L_QUEUE *lq, + void *item) +{ + PROCNAME("lqueueAdd"); + + if (!lq) + return ERROR_INT("lq not defined", procName, 1); + if (!item) + return ERROR_INT("item not defined", procName, 1); + + /* If filled to the end and the ptrs can be shifted to the left, + * shift them. */ + if ((lq->nhead + lq->nelem >= lq->nalloc) && (lq->nhead != 0)) { + memmove(lq->array, lq->array + lq->nhead, sizeof(void *) * lq->nelem); + lq->nhead = 0; + } + + /* If necessary, expand the allocated array by a factor of 2 */ + if (lq->nelem > 0.75 * lq->nalloc) + lqueueExtendArray(lq); + + /* Now add the item */ + lq->array[lq->nhead + lq->nelem] = (void *)item; + lq->nelem++; + + return 0; +} + + +/*! + * \brief lqueueExtendArray() + * + * \param[in] lq lqueue + * \return 0 if OK, 1 on error + */ +static l_int32 +lqueueExtendArray(L_QUEUE *lq) +{ + PROCNAME("lqueueExtendArray"); + + if (!lq) + return ERROR_INT("lq not defined", procName, 1); + + if ((lq->array = (void **)reallocNew((void **)&lq->array, + sizeof(void *) * lq->nalloc, + 2 * sizeof(void *) * lq->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + lq->nalloc = 2 * lq->nalloc; + return 0; +} + + +/*! + * \brief lqueueRemove() + * + * \param[in] lq lqueue + * \return ptr to item popped from the head of the queue, + * or NULL if the queue is empty or on error + * + *
+ * Notes:
+ *      (1) If this is the last item on the queue, so that the queue
+ *          becomes empty, nhead is reset to the beginning of the array.
+ * 
+ */ +void * +lqueueRemove(L_QUEUE *lq) +{ +void *item; + + PROCNAME("lqueueRemove"); + + if (!lq) + return (void *)ERROR_PTR("lq not defined", procName, NULL); + + if (lq->nelem == 0) + return NULL; + item = lq->array[lq->nhead]; + lq->array[lq->nhead] = NULL; + if (lq->nelem == 1) + lq->nhead = 0; /* reset head ptr */ + else + (lq->nhead)++; /* can't go off end of array because nelem > 1 */ + lq->nelem--; + return item; +} + + +/*! + * \brief lqueueGetCount() + * + * \param[in] lq lqueue + * \return count, or 0 on error + */ +l_int32 +lqueueGetCount(L_QUEUE *lq) +{ + PROCNAME("lqueueGetCount"); + + if (!lq) + return ERROR_INT("lq not defined", procName, 0); + + return lq->nelem; +} + + +/*---------------------------------------------------------------------* + * Debug output * + *---------------------------------------------------------------------*/ +/*! + * \brief lqueuePrint() + * + * \param[in] fp file stream + * \param[in] lq lqueue + * \return 0 if OK; 1 on error + */ +l_ok +lqueuePrint(FILE *fp, + L_QUEUE *lq) +{ +l_int32 i; + + PROCNAME("lqueuePrint"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!lq) + return ERROR_INT("lq not defined", procName, 1); + + fprintf(fp, "\n L_Queue: nalloc = %d, nhead = %d, nelem = %d, array = %p\n", + lq->nalloc, lq->nhead, lq->nelem, lq->array); + for (i = lq->nhead; i < lq->nhead + lq->nelem; i++) + fprintf(fp, "array[%d] = %p\n", i, lq->array[i]); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/queue.h b/hgdriver/3rdparty/hgOCR/leptonica/queue.h new file mode 100644 index 0000000..fd380e8 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/queue.h @@ -0,0 +1,77 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_QUEUE_H +#define LEPTONICA_QUEUE_H + +/*! + * \file queue.h + * + *
+ *      Expandable pointer queue for arbitrary void* data.
+ *
+ *      The L_Queue is a fifo that implements a queue of void* pointers.
+ *      It can be used to hold a queue of any type of struct.
+ *
+ *      Internally, it maintains two counters:
+ *          nhead:  location of head (in ptrs) from the beginning
+ *                  of the array.
+ *          nelem:  number of ptr elements stored in the queue.
+ *
+ *      The element at the head of the queue, which is the next to
+ *      be removed, is array[nhead].  The location at the tail of the
+ *      queue to which the next element will be added is
+ *      array[nhead + nelem].
+ *
+ *      As items are added to the queue, nelem increases.
+ *      As items are removed, nhead increases and nelem decreases.
+ *      Any time the tail reaches the end of the allocated array,
+ *      all the pointers are shifted to the left, so that the head
+ *      is at the beginning of the array.
+ *      If the array becomes more than 3/4 full, it doubles in size.
+ *
+ *      The auxiliary stack can be used in a wrapper for re-using
+ *      items popped from the queue.  It is not made by default.
+ *
+ *      For further implementation details, see queue.c.
+ * 
+ */ + +/*! Expandable pointer queue for arbitrary void* data */ +struct L_Queue +{ + l_int32 nalloc; /*!< size of allocated ptr array */ + l_int32 nhead; /*!< location of head (in ptrs) from the */ + /*!< beginning of the array */ + l_int32 nelem; /*!< number of elements stored in the queue */ + void **array; /*!< ptr array */ + struct L_Stack *stack; /*!< auxiliary stack */ + +}; +typedef struct L_Queue L_QUEUE; + + +#endif /* LEPTONICA_QUEUE_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rank.c b/hgdriver/3rdparty/hgOCR/leptonica/rank.c new file mode 100644 index 0000000..90722a4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rank.c @@ -0,0 +1,540 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file rank.c + *
+ *
+ *      Rank filter (gray and rgb)
+ *          PIX      *pixRankFilter()
+ *          PIX      *pixRankFilterRGB()
+ *          PIX      *pixRankFilterGray()
+ *
+ *      Median filter
+ *          PIX      *pixMedianFilter()
+ *
+ *      Rank filter (accelerated with downscaling)
+ *          PIX      *pixRankFilterWithScaling()
+ *
+ *  What is a brick rank filter?
+ *
+ *    A brick rank order filter evaluates, for every pixel in the image,
+ *    a rectangular set of n = wf x hf pixels in its neighborhood (where the
+ *    pixel in question is at the "center" of the rectangle and is
+ *    included in the evaluation).  It determines the value of the
+ *    neighboring pixel that is the r-th smallest in the set,
+ *    where r is some integer between 1 and n.  The input rank parameter
+ *    is a fraction between 0.0 and 1.0, where 0.0 represents the
+ *    smallest value (r = 1) and 1.0 represents the largest value (r = n).
+ *    A median filter is a rank filter where rank = 0.5.
+ *
+ *    It is important to note that grayscale erosion is equivalent
+ *    to rank = 0.0, and grayscale dilation is equivalent to rank = 1.0.
+ *    These are much easier to calculate than the general rank value,
+ *    thanks to the van Herk/Gil-Werman algorithm:
+ *       http://www.leptonica.com/grayscale-morphology.html
+ *    so you should use pixErodeGray() and pixDilateGray() for
+ *    rank 0.0 and 1.0, rsp.  See notes below in the function header.
+ *
+ *  How is a rank filter implemented efficiently on an image?
+ *
+ *    Sorting will not work.
+ *
+ *      * The best sort algorithms are O(n*logn), where n is the number
+ *        of values to be sorted (the area of the filter).  For large
+ *        filters this is an impractically large number.
+ *
+ *      * Selection of the rank value is O(n).  (To understand why it's not
+ *        O(n*logn), see Numerical Recipes in C, 2nd edition, 1992,  p. 355ff).
+ *        This also still far too much computation for large filters.
+ *
+ *      * Suppose we get clever.  We really only need to do an incremental
+ *        selection or sorting, because, for example, moving the filter
+ *        down by one pixel causes one filter width of pixels to be added
+ *        and another to be removed.  Can we do this incrementally in
+ *        an efficient way?  Unfortunately, no.  The sorted values will be
+ *        in an array.  Even if the filter width is 1, we can expect to
+ *        have to move O(n) pixels, because insertion and deletion can happen
+ *        anywhere in the array.  By comparison, heapsort is excellent for
+ *        incremental sorting, where the cost for insertion or deletion
+ *        is O(logn), because the array itself doesn't need to
+ *        be sorted into strictly increasing order.  However, heapsort
+ *        only gives the max (or min) value, not the general rank value.
+ *
+ *    This leaves histograms.
+ *
+ *      * Represented as an array.  The problem with an array of 256
+ *        bins is that, in general, a significant fraction of the
+ *        entire histogram must be summed to find the rank value bin.
+ *        Suppose the filter size is 5x5.  You spend most of your time
+ *        adding zeroes.  Ouch!
+ *
+ *      * Represented as a linked list.  This would overcome the
+ *        summing-over-empty-bin problem, but you lose random access
+ *        for insertions and deletions.  No way.
+ *
+ *      * Two histogram solution.  Maintain two histograms with
+ *        bin sizes of 1 and 16.  Proceed from coarse to fine.
+ *        First locate the coarse bin for the given rank, of which
+ *        there are only 16.  Then, in the 256 entry (fine) histogram,
+ *        you need look at a maximum of 16 bins.  For each output
+ *        pixel, the average number of bins summed over, both in the
+ *        coarse and fine histograms, is thus 16.
+ *
+ *  If someone has a better method, please let me know!
+ *
+ *  The rank filtering operation is relatively expensive, compared to most
+ *  of the other imaging operations.  The speed is only weakly dependent
+ *  on the size of the rank filter.  On standard hardware, it runs at
+ *  about 10 Mpix/sec for a 50 x 50 filter, and 25 Mpix/sec for
+ *  a 5 x 5 filter.   For applications where the rank filter can be
+ *  performed on a downscaled image, significant speedup can be
+ *  achieved because the time goes as the square of the scaling factor.
+ *  We provide an interface that handles the details, and only
+ *  requires the amount of downscaling to be input.
+ * 
+ */ + +#include "allheaders.h" + +/*----------------------------------------------------------------------* + * Rank order filter * + *----------------------------------------------------------------------*/ +/*! + * \brief pixRankFilter() + * + * \param[in] pixs 8 or 32 bpp; no colormap + * \param[in] wf, hf width and height of filter; each is >= 1 + * \param[in] rank in [0.0 ... 1.0] + * \return pixd of rank values, or NULL on error + * + *
+ * Notes:
+ *      (1) This defines, for each pixel in pixs, a neighborhood of
+ *          pixels given by a rectangle "centered" on the pixel.
+ *          This set of wf*hf pixels has a distribution of values.
+ *          For each component, if the values are sorted in increasing
+ *          order, we choose the component such that rank*(wf*hf-1)
+ *          pixels have a lower or equal value and
+ *          (1-rank)*(wf*hf-1) pixels have an equal or greater value.
+ *      (2) See notes in pixRankFilterGray() for further details.
+ * 
+ */ +PIX * +pixRankFilter(PIX *pixs, + l_int32 wf, + l_int32 hf, + l_float32 rank) +{ +l_int32 d; + + PROCNAME("pixRankFilter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (wf < 1 || hf < 1) + return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); + if (wf == 1 && hf == 1) /* no-op */ + return pixCopy(NULL, pixs); + + if (d == 8) + return pixRankFilterGray(pixs, wf, hf, rank); + else /* d == 32 */ + return pixRankFilterRGB(pixs, wf, hf, rank); +} + + +/*! + * \brief pixRankFilterRGB() + * + * \param[in] pixs 32 bpp + * \param[in] wf, hf width and height of filter; each is >= 1 + * \param[in] rank in [0.0 ... 1.0] + * \return pixd of rank values, or NULL on error + * + *
+ * Notes:
+ *      (1) This defines, for each pixel in pixs, a neighborhood of
+ *          pixels given by a rectangle "centered" on the pixel.
+ *          This set of wf*hf pixels has a distribution of values.
+ *          For each component, if the values are sorted in increasing
+ *          order, we choose the component such that rank*(wf*hf-1)
+ *          pixels have a lower or equal value and
+ *          (1-rank)*(wf*hf-1) pixels have an equal or greater value.
+ *      (2) Apply gray rank filtering to each component independently.
+ *      (3) See notes in pixRankFilterGray() for further details.
+ * 
+ */ +PIX * +pixRankFilterRGB(PIX *pixs, + l_int32 wf, + l_int32 hf, + l_float32 rank) +{ +PIX *pixr, *pixg, *pixb, *pixrf, *pixgf, *pixbf, *pixd; + + PROCNAME("pixRankFilterRGB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (wf < 1 || hf < 1) + return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); + if (wf == 1 && hf == 1) /* no-op */ + return pixCopy(NULL, pixs); + + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + + pixrf = pixRankFilterGray(pixr, wf, hf, rank); + pixgf = pixRankFilterGray(pixg, wf, hf, rank); + pixbf = pixRankFilterGray(pixb, wf, hf, rank); + + pixd = pixCreateRGBImage(pixrf, pixgf, pixbf); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + pixDestroy(&pixrf); + pixDestroy(&pixgf); + pixDestroy(&pixbf); + return pixd; +} + + +/*! + * \brief pixRankFilterGray() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] wf, hf width and height of filter; each is >= 1 + * \param[in] rank in [0.0 ... 1.0] + * \return pixd of rank values, or NULL on error + * + *
+ * Notes:
+ *      (1) This defines, for each pixel in pixs, a neighborhood of
+ *          pixels given by a rectangle "centered" on the pixel.
+ *          This set of wf*hf pixels has a distribution of values,
+ *          and if they are sorted in increasing order, we choose
+ *          the pixel such that rank*(wf*hf-1) pixels have a lower
+ *          or equal value and (1-rank)*(wf*hf-1) pixels have an equal
+ *          or greater value.
+ *      (2) By this definition, the rank = 0.0 pixel has the lowest
+ *          value, and the rank = 1.0 pixel has the highest value.
+ *      (3) We add mirrored boundary pixels to avoid boundary effects,
+ *          and put the filter center at (0, 0).
+ *      (4) This dispatches to grayscale erosion or dilation if the
+ *          filter dimensions are odd and the rank is 0.0 or 1.0, rsp.
+ *      (5) Returns a copy if both wf and hf are 1.
+ *      (6) Uses row-major or column-major incremental updates to the
+ *          histograms depending on whether hf > wf or hv <= wf, rsp.
+ * 
+ */ +PIX * +pixRankFilterGray(PIX *pixs, + l_int32 wf, + l_int32 hf, + l_float32 rank) +{ +l_int32 w, h, d, i, j, k, m, n, rankloc, wplt, wpld, val, sum; +l_int32 *histo, *histo16; +l_uint32 *datat, *linet, *datad, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixRankFilterGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (wf < 1 || hf < 1) + return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); + if (wf == 1 && hf == 1) /* no-op */ + return pixCopy(NULL, pixs); + + /* For rank = 0.0, this is a grayscale erosion, and for rank = 1.0, + * a dilation. Grayscale morphology operations are implemented + * for filters of odd dimension, so we dispatch to grayscale + * morphology if both wf and hf are odd. Otherwise, we + * slightly adjust the rank (to get the correct behavior) and + * use the slower rank filter here. */ + if (wf % 2 && hf % 2) { + if (rank == 0.0) + return pixErodeGray(pixs, wf, hf); + else if (rank == 1.0) + return pixDilateGray(pixs, wf, hf); + } + if (rank == 0.0) rank = 0.0001; + if (rank == 1.0) rank = 0.9999; + + /* Add wf/2 to each side, and hf/2 to top and bottom of the + * image, mirroring for accuracy and to avoid special-casing + * the boundary. */ + if ((pixt = pixAddMirroredBorder(pixs, wf / 2, wf / 2, hf / 2, hf / 2)) + == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* Set up the two histogram arrays. */ + histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + histo16 = (l_int32 *)LEPT_CALLOC(16, sizeof(l_int32)); + rankloc = (l_int32)(rank * wf * hf); + + /* Place the filter center at (0, 0). This is just a + * convenient location, because it allows us to perform + * the rank filter over x:(0 ... w - 1) and y:(0 ... h - 1). */ + pixd = pixCreateTemplate(pixs); + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* If hf > wf, it's more efficient to use row-major scanning. + * Otherwise, traverse the image in use column-major order. */ + if (hf > wf) { + for (j = 0; j < w; j++) { /* row-major */ + /* Start each column with clean histogram arrays. */ + for (n = 0; n < 256; n++) + histo[n] = 0; + for (n = 0; n < 16; n++) + histo16[n] = 0; + + for (i = 0; i < h; i++) { /* fast scan on columns */ + /* Update the histos for the new location */ + lined = datad + i * wpld; + if (i == 0) { /* do full histo */ + for (k = 0; k < hf; k++) { + linet = datat + (i + k) * wplt; + for (m = 0; m < wf; m++) { + val = GET_DATA_BYTE(linet, j + m); + histo[val]++; + histo16[val >> 4]++; + } + } + } else { /* incremental update */ + linet = datat + (i - 1) * wplt; + for (m = 0; m < wf; m++) { /* remove top line */ + val = GET_DATA_BYTE(linet, j + m); + histo[val]--; + histo16[val >> 4]--; + } + linet = datat + (i + hf - 1) * wplt; + for (m = 0; m < wf; m++) { /* add bottom line */ + val = GET_DATA_BYTE(linet, j + m); + histo[val]++; + histo16[val >> 4]++; + } + } + + /* Find the rank value */ + sum = 0; + for (n = 0; n < 16; n++) { /* search over coarse histo */ + sum += histo16[n]; + if (sum > rankloc) { + sum -= histo16[n]; + break; + } + } + if (n == 16) { /* avoid accessing out of bounds */ + L_WARNING("n = 16; reducing\n", procName); + n = 15; + sum -= histo16[n]; + } + k = 16 * n; /* starting value in fine histo */ + for (m = 0; m < 16; m++) { + sum += histo[k]; + if (sum > rankloc) { + SET_DATA_BYTE(lined, j, k); + break; + } + k++; + } + } + } + } else { /* wf >= hf */ + for (i = 0; i < h; i++) { /* column-major */ + /* Start each row with clean histogram arrays. */ + for (n = 0; n < 256; n++) + histo[n] = 0; + for (n = 0; n < 16; n++) + histo16[n] = 0; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { /* fast scan on rows */ + /* Update the histos for the new location */ + if (j == 0) { /* do full histo */ + for (k = 0; k < hf; k++) { + linet = datat + (i + k) * wplt; + for (m = 0; m < wf; m++) { + val = GET_DATA_BYTE(linet, j + m); + histo[val]++; + histo16[val >> 4]++; + } + } + } else { /* incremental update at left and right sides */ + for (k = 0; k < hf; k++) { + linet = datat + (i + k) * wplt; + val = GET_DATA_BYTE(linet, j - 1); + histo[val]--; + histo16[val >> 4]--; + val = GET_DATA_BYTE(linet, j + wf - 1); + histo[val]++; + histo16[val >> 4]++; + } + } + + /* Find the rank value */ + sum = 0; + for (n = 0; n < 16; n++) { /* search over coarse histo */ + sum += histo16[n]; + if (sum > rankloc) { + sum -= histo16[n]; + break; + } + } + if (n == 16) { /* avoid accessing out of bounds */ + L_WARNING("n = 16; reducing\n", procName); + n = 15; + sum -= histo16[n]; + } + k = 16 * n; /* starting value in fine histo */ + for (m = 0; m < 16; m++) { + sum += histo[k]; + if (sum > rankloc) { + SET_DATA_BYTE(lined, j, k); + break; + } + k++; + } + } + } + } + + pixDestroy(&pixt); + LEPT_FREE(histo); + LEPT_FREE(histo16); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Median filter * + *----------------------------------------------------------------------*/ +/*! + * \brief pixMedianFilter() + * + * \param[in] pixs 8 or 32 bpp; no colormap + * \param[in] wf, hf width and height of filter; each is >= 1 + * \return pixd of median values, or NULL on error + */ +PIX * +pixMedianFilter(PIX *pixs, + l_int32 wf, + l_int32 hf) +{ + PROCNAME("pixMedianFilter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + return pixRankFilter(pixs, wf, hf, 0.5); +} + + +/*----------------------------------------------------------------------* + * Rank filter (accelerated with downscaling) * + *----------------------------------------------------------------------*/ +/*! + * \brief pixRankFilterWithScaling() + * + * \param[in] pixs 8 or 32 bpp; no colormap + * \param[in] wf, hf width and height of filter; each is >= 1 + * \param[in] rank in [0.0 ... 1.0] + * \param[in] scalefactor scale factor; must be >= 0.2 and <= 0.7 + * \return pixd of rank values, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a convenience function that downscales, does
+ *          the rank filtering, and upscales.  Because the down-
+ *          and up-scaling functions are very fast compared to
+ *          rank filtering, the time it takes is reduced from that
+ *          for the simple rank filtering operation by approximately
+ *          the square of the scaling factor.
+ * 
+ */ +PIX * +pixRankFilterWithScaling(PIX *pixs, + l_int32 wf, + l_int32 hf, + l_float32 rank, + l_float32 scalefactor) +{ +l_int32 w, h, d, wfs, hfs; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixRankFilterWithScaling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetColormap(pixs) != NULL) + return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); + d = pixGetDepth(pixs); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (wf < 1 || hf < 1) + return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); + if (rank < 0.0 || rank > 1.0) + return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); + if (wf == 1 && hf == 1) /* no-op */ + return pixCopy(NULL, pixs); + if (scalefactor < 0.2 || scalefactor > 0.7) { + L_ERROR("invalid scale factor; no scaling used\n", procName); + return pixRankFilter(pixs, wf, hf, rank); + } + + pix1 = pixScaleAreaMap(pixs, scalefactor, scalefactor); + wfs = L_MAX(1, (l_int32)(scalefactor * wf + 0.5)); + hfs = L_MAX(1, (l_int32)(scalefactor * hf + 0.5)); + pix2 = pixRankFilter(pix1, wfs, hfs, rank); + pixGetDimensions(pixs, &w, &h, NULL); + pixd = pixScaleToSize(pix2, w, h); + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rbtree.c b/hgdriver/3rdparty/hgOCR/leptonica/rbtree.c new file mode 100644 index 0000000..6da2924 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rbtree.c @@ -0,0 +1,899 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * Modified from the excellent code here: + * http://en.literateprograms.org/Red-black_tree_(C)?oldid=19567 + * which has been placed in the public domain under the Creative Commons + * CC0 1.0 waiver (http://creativecommons.org/publicdomain/zero/1.0/). + */ + +/*! + * \file rbtree.c + *
+ *
+ *  Basic functions for using red-black trees.  These are "nearly" balanced
+ *  sorted trees with ordering by key that allows insertion, lookup and
+ *  deletion of key/value pairs in log(n) time.
+ *
+ *  We use red-black trees to implement our version of:
+ *    * a map: a function that maps keys to values (e.g., int64 --> int64).
+ *    * a set: a collection that is sorted by unique keys (without
+ *      associated values)
+ *
+ *  There are 5 invariant properties of RB trees:
+ *  (1) Each node is either red or black.
+ *  (2) The root node is black.
+ *  (3) All leaves are black and contain no data (null).
+ *  (4) Every red node has two children and both are black.  This is
+ *      equivalent to requiring the parent of every red node to be black.
+ *  (5) All paths from any given node to its leaf nodes contain the
+ *      same number of black nodes.
+ *
+ *  Interface to red-black tree
+ *           L_RBTREE       *l_rbtreeCreate()
+ *           RB_TYPE        *l_rbtreeLookup()
+ *           void            l_rbtreeInsert()
+ *           void            l_rbtreeDelete()
+ *           void            l_rbtreeDestroy()
+ *           L_RBTREE_NODE  *l_rbtreeGetFirst()
+ *           L_RBTREE_NODE  *l_rbtreeGetNext()
+ *           L_RBTREE_NODE  *l_rbtreeGetLast()
+ *           L_RBTREE_NODE  *l_rbtreeGetPrev()
+ *           l_int32         l_rbtreeGetCount()
+ *           void            l_rbtreePrint()
+ *
+ *  General comparison function
+ *           static l_int32  compareKeys()
+ * 
+ */ + +#include "allheaders.h" + + /* The node color enum is only needed in the rbtree implementation */ +enum { + L_RED_NODE = 1, + L_BLACK_NODE = 2 +}; + + /* This makes it simpler to read the code */ +typedef L_RBTREE_NODE node; + + /* Lots of static helper functions */ +static void destroy_helper(node *n); +static void count_helper(node *n, l_int32 *pcount); +static void print_tree_helper(FILE *fp, node *n, l_int32 keytype, + l_int32 indent); + +static l_int32 compareKeys(l_int32 keytype, RB_TYPE left, RB_TYPE right); + +static node *grandparent(node *n); +static node *sibling(node *n); +static node *uncle(node *n); +static l_int32 node_color(node *n); +static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, + node *left, node *right); +static node *lookup_node(L_RBTREE *t, RB_TYPE key); +static void rotate_left(L_RBTREE *t, node *n); +static void rotate_right(L_RBTREE *t, node *n); +static void replace_node(L_RBTREE *t, node *oldn, node *newn); +static void insert_case1(L_RBTREE *t, node *n); +static void insert_case2(L_RBTREE *t, node *n); +static void insert_case3(L_RBTREE *t, node *n); +static void insert_case4(L_RBTREE *t, node *n); +static void insert_case5(L_RBTREE *t, node *n); +static node *maximum_node(node *root); +static void delete_case1(L_RBTREE *t, node *n); +static void delete_case2(L_RBTREE *t, node *n); +static void delete_case3(L_RBTREE *t, node *n); +static void delete_case4(L_RBTREE *t, node *n); +static void delete_case5(L_RBTREE *t, node *n); +static void delete_case6(L_RBTREE *t, node *n); +static void verify_properties(L_RBTREE *t); + +#ifndef NO_CONSOLE_IO +#define VERIFY_RBTREE 0 /* only for debugging */ +#endif /* ~NO_CONSOLE_IO */ + + +/* ------------------------------------------------------------- * + * Interface to Red-black Tree * + * ------------------------------------------------------------- */ +/*! + * \brief l_rbtreeCreate() + * + * \param[in] keytype defined by an enum for an RB_TYPE union + * \return rbtree container with empty ptr to the root + */ +L_RBTREE * +l_rbtreeCreate(l_int32 keytype) +{ + PROCNAME("l_rbtreeCreate"); + + if (keytype != L_INT_TYPE && keytype != L_UINT_TYPE && + keytype != L_FLOAT_TYPE && keytype) + return (L_RBTREE *)ERROR_PTR("invalid keytype", procName, NULL); + + L_RBTREE *t = (L_RBTREE *)LEPT_CALLOC(1, sizeof(L_RBTREE)); + t->keytype = keytype; + verify_properties(t); + return t; +} + +/*! + * \brief l_rbtreeLookup() + * + * \param[in] t rbtree, including root node + * \param[in] key find a node with this key + * \return &value a pointer to a union, if the node exists; else NULL + */ +RB_TYPE * +l_rbtreeLookup(L_RBTREE *t, + RB_TYPE key) +{ + PROCNAME("l_rbtreeLookup"); + + if (!t) + return (RB_TYPE *)ERROR_PTR("tree is null\n", procName, NULL); + + node *n = lookup_node(t, key); + return n == NULL ? NULL : &n->value; +} + +/*! + * \brief l_rbtreeInsert() + * + * \param[in] t rbtree, including root node + * \param[in] key insert a node with this key, if the key does not + * already exist in the tree + * \param[in] value typically an int, used for an index + * \return void + * + *
+ * Notes:
+ *      (1) If a node with the key already exists, this just updates the value.
+ * 
+ */ +void +l_rbtreeInsert(L_RBTREE *t, + RB_TYPE key, + RB_TYPE value) +{ +node *n, *inserted_node; + + PROCNAME("l_rbtreeInsert"); + + if (!t) { + L_ERROR("tree is null\n", procName); + return; + } + + inserted_node = new_node(key, value, L_RED_NODE, NULL, NULL); + if (t->root == NULL) { + t->root = inserted_node; + } else { + n = t->root; + while (1) { + int comp_result = compareKeys(t->keytype, key, n->key); + if (comp_result == 0) { + n->value = value; + LEPT_FREE(inserted_node); + return; + } else if (comp_result < 0) { + if (n->left == NULL) { + n->left = inserted_node; + break; + } else { + n = n->left; + } + } else { /* comp_result > 0 */ + if (n->right == NULL) { + n->right = inserted_node; + break; + } else { + n = n->right; + } + } + } + inserted_node->parent = n; + } + insert_case1(t, inserted_node); + verify_properties(t); +} + +/*! + * \brief l_rbtreeDelete() + * + * \param[in] t rbtree, including root node + * \param[in] key delete the node with this key + * \return void + */ +void +l_rbtreeDelete(L_RBTREE *t, + RB_TYPE key) +{ +node *n, *child; + + PROCNAME("l_rbtreeDelete"); + + if (!t) { + L_ERROR("tree is null\n", procName); + return; + } + + n = lookup_node(t, key); + if (n == NULL) return; /* Key not found, do nothing */ + if (n->left != NULL && n->right != NULL) { + /* Copy key/value from predecessor and then delete it instead */ + node *pred = maximum_node(n->left); + n->key = pred->key; + n->value = pred->value; + n = pred; + } + + /* n->left == NULL || n->right == NULL */ + child = n->right == NULL ? n->left : n->right; + if (node_color(n) == L_BLACK_NODE) { + n->color = node_color(child); + delete_case1(t, n); + } + replace_node(t, n, child); + if (n->parent == NULL && child != NULL) /* root should be black */ + child->color = L_BLACK_NODE; + LEPT_FREE(n); + + verify_properties(t); +} + +/*! + * \brief l_rbtreeDestroy() + * + * \param[in] pt pointer to tree; will be wet to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Destroys the tree and nulls the input tree ptr.
+ * 
+ */ +void +l_rbtreeDestroy(L_RBTREE **pt) +{ +node *n; + + if (!pt) return; + if (*pt == NULL) return; + n = (*pt)->root; + destroy_helper(n); + LEPT_FREE(*pt); + *pt = NULL; + return; +} + + /* postorder DFS */ +static void +destroy_helper(node *n) +{ + if (!n) return; + destroy_helper(n->left); + destroy_helper(n->right); + LEPT_FREE(n); +} + +/*! + * \brief l_rbtreeGetFirst() + * + * \param[in] t rbtree, including root node + * \return void + * + *
+ * Notes:
+ *      (1) This is the first node in an in-order traversal.
+ * 
+ */ +L_RBTREE_NODE * +l_rbtreeGetFirst(L_RBTREE *t) +{ +node *n; + + PROCNAME("l_rbtreeGetFirst"); + + if (!t) + return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); + if (t->root == NULL) { + L_INFO("tree is empty\n", procName); + return NULL; + } + + /* Just go down the left side as far as possible */ + n = t->root; + while (n && n->left) + n = n->left; + return n; +} + +/*! + * \brief l_rbtreeGetNext() + * + * \param[in] n current node + * \return next node, or NULL if it's the last node + * + *
+ * Notes:
+ *      (1) This finds the next node, in an in-order traversal, from
+ *          the current node.
+ *      (2) It is useful as an iterator for a map.
+ *      (3) Call l_rbtreeGetFirst() to get the first node.
+ * 
+ */ +L_RBTREE_NODE * +l_rbtreeGetNext(L_RBTREE_NODE *n) +{ + PROCNAME("l_rbtreeGetNext"); + + if (!n) + return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); + + /* If there is a right child, go to it, and then go left all the + * way to the end. Otherwise go up to the parent; continue upward + * as long as you're on the right branch, but stop at the parent + * when you hit it from the left branch. */ + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + return n; + } else { + while (n->parent && n->parent->right == n) + n = n->parent; + return n->parent; + } +} + +/*! + * \brief l_rbtreeGetLast() + * + * \param[in] t rbtree, including root node + * \return void + * + *
+ * Notes:
+ *      (1) This is the last node in an in-order traversal.
+ * 
+ */ +L_RBTREE_NODE * +l_rbtreeGetLast(L_RBTREE *t) +{ +node *n; + + PROCNAME("l_rbtreeGetLast"); + + if (!t) + return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); + if (t->root == NULL) { + L_INFO("tree is empty\n", procName); + return NULL; + } + + /* Just go down the right side as far as possible */ + n = t->root; + while (n && n->right) + n = n->right; + return n; +} + +/*! + * \brief l_rbtreeGetPrev() + * + * \param[in] n current node + * \return next node, or NULL if it's the first node + * + *
+ * Notes:
+ *      (1) This finds the previous node, in an in-order traversal, from
+ *          the current node.
+ *      (2) It is useful as an iterator for a map.
+ *      (3) Call l_rbtreeGetLast() to get the last node.
+ * 
+ */ +L_RBTREE_NODE * +l_rbtreeGetPrev(L_RBTREE_NODE *n) +{ + PROCNAME("l_rbtreeGetPrev"); + + if (!n) + return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); + + /* If there is a left child, go to it, and then go right all the + * way to the end. Otherwise go up to the parent; continue upward + * as long as you're on the left branch, but stop at the parent + * when you hit it from the right branch. */ + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + return n; + } else { + while (n->parent && n->parent->left == n) + n = n->parent; + return n->parent; + } +} + +/*! + * \brief l_rbtreeGetCount() + * + * \param[in] t rbtree + * \return count the number of nodes in the tree, or 0 on error + */ +l_int32 +l_rbtreeGetCount(L_RBTREE *t) +{ +l_int32 count = 0; +node *n; + + if (!t) return 0; + n = t->root; + count_helper(n, &count); + return count; +} + + /* preorder DFS */ +static void +count_helper(node *n, l_int32 *pcount) +{ + if (n) + (*pcount)++; + else + return; + + count_helper(n->left, pcount); + count_helper(n->right, pcount); +} + + +/*! + * \brief l_rbtreePrint() + * + * \param[in] fp file stream + * \param[in] t rbtree + * \return void + */ +void +l_rbtreePrint(FILE *fp, + L_RBTREE *t) +{ + PROCNAME("l_rbtreePrint"); + if (!fp) { + L_ERROR("stream not defined\n", procName); + return; + } + if (!t) { + L_ERROR("tree not defined\n", procName); + return; + } + + print_tree_helper(fp, t->root, t->keytype, 0); + fprintf(fp, "\n"); +} + +#define INDENT_STEP 4 + +static void +print_tree_helper(FILE *fp, + node *n, + l_int32 keytype, + l_int32 indent) +{ +l_int32 i; + + if (n == NULL) { + fprintf(fp, ""); + return; + } + if (n->right != NULL) { + print_tree_helper(fp, n->right, keytype, indent + INDENT_STEP); + } + for (i = 0; i < indent; i++) + fprintf(fp, " "); + if (n->color == L_BLACK_NODE) { + if (keytype == L_INT_TYPE) + fprintf(fp, "%lld\n", n->key.itype); + else if (keytype == L_UINT_TYPE) + fprintf(fp, "%llx\n", n->key.utype); + else if (keytype == L_FLOAT_TYPE) + fprintf(fp, "%f\n", n->key.ftype); + } else { + if (keytype == L_INT_TYPE) + fprintf(fp, "<%lld>\n", n->key.itype); + else if (keytype == L_UINT_TYPE) + fprintf(fp, "<%llx>\n", n->key.utype); + else if (keytype == L_FLOAT_TYPE) + fprintf(fp, "<%f>\n", n->key.ftype); + } + if (n->left != NULL) { + print_tree_helper(fp, n->left, keytype, indent + INDENT_STEP); + } +} + + +/* ------------------------------------------------------------- * + * Static key comparison function * + * ------------------------------------------------------------- */ +static l_int32 +compareKeys(l_int32 keytype, + RB_TYPE left, + RB_TYPE right) +{ +static char procName[] = "compareKeys"; + + if (keytype == L_INT_TYPE) { + if (left.itype < right.itype) + return -1; + else if (left.itype > right.itype) + return 1; + else { /* equality */ + return 0; + } + } else if (keytype == L_UINT_TYPE) { + if (left.utype < right.utype) + return -1; + else if (left.utype > right.utype) + return 1; + else { /* equality */ + return 0; + } + } else if (keytype == L_FLOAT_TYPE) { + if (left.ftype < right.ftype) + return -1; + else if (left.ftype > right.ftype) + return 1; + else { /* equality */ + return 0; + } + } else { + L_ERROR("unknown keytype %d\n", procName, keytype); + return 0; + } +} + + +/* ------------------------------------------------------------- * + * Static red-black tree helpers * + * ------------------------------------------------------------- */ +static node *grandparent(node *n) { + if (!n || !n->parent || !n->parent->parent) { + L_ERROR("root and child of root have no grandparent\n", "grandparent"); + return NULL; + } + return n->parent->parent; +} + +static node *sibling(node *n) { + if (!n || !n->parent) { + L_ERROR("root has no sibling\n", "sibling"); + return NULL; + } + if (n == n->parent->left) + return n->parent->right; + else + return n->parent->left; +} + +static node *uncle(node *n) { + if (!n || !n->parent || !n->parent->parent) { + L_ERROR("root and child of root have no uncle\n", "uncle"); + return NULL; + } + return sibling(n->parent); +} + +static l_int32 node_color(node *n) { + return n == NULL ? L_BLACK_NODE : n->color; +} + + +static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, + node *left, node *right) { + node *result = (node *)LEPT_CALLOC(1, sizeof(node)); + result->key = key; + result->value = value; + result->color = node_color; + result->left = left; + result->right = right; + if (left != NULL) left->parent = result; + if (right != NULL) right->parent = result; + result->parent = NULL; + return result; +} + +static node *lookup_node(L_RBTREE *t, RB_TYPE key) { + node *n = t->root; + while (n != NULL) { + int comp_result = compareKeys(t->keytype, key, n->key); + if (comp_result == 0) { + return n; + } else if (comp_result < 0) { + n = n->left; + } else { /* comp_result > 0 */ + n = n->right; + } + } + return n; +} + +static void rotate_left(L_RBTREE *t, node *n) { + node *r = n->right; + replace_node(t, n, r); + n->right = r->left; + if (r->left != NULL) { + r->left->parent = n; + } + r->left = n; + n->parent = r; +} + +static void rotate_right(L_RBTREE *t, node *n) { + node *L = n->left; + replace_node(t, n, L); + n->left = L->right; + if (L->right != NULL) { + L->right->parent = n; + } + L->right = n; + n->parent = L; +} + +static void replace_node(L_RBTREE *t, node *oldn, node *newn) { + if (oldn->parent == NULL) { + t->root = newn; + } else { + if (oldn == oldn->parent->left) + oldn->parent->left = newn; + else + oldn->parent->right = newn; + } + if (newn != NULL) { + newn->parent = oldn->parent; + } +} + +static void insert_case1(L_RBTREE *t, node *n) { + if (n->parent == NULL) + n->color = L_BLACK_NODE; + else + insert_case2(t, n); +} + +static void insert_case2(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_BLACK_NODE) + return; /* Tree is still valid */ + else + insert_case3(t, n); +} + +static void insert_case3(L_RBTREE *t, node *n) { + if (node_color(uncle(n)) == L_RED_NODE) { + n->parent->color = L_BLACK_NODE; + uncle(n)->color = L_BLACK_NODE; + grandparent(n)->color = L_RED_NODE; + insert_case1(t, grandparent(n)); + } else { + insert_case4(t, n); + } +} + +static void insert_case4(L_RBTREE *t, node *n) { + if (n == n->parent->right && n->parent == grandparent(n)->left) { + rotate_left(t, n->parent); + n = n->left; + } else if (n == n->parent->left && n->parent == grandparent(n)->right) { + rotate_right(t, n->parent); + n = n->right; + } + insert_case5(t, n); +} + +static void insert_case5(L_RBTREE *t, node *n) { + n->parent->color = L_BLACK_NODE; + grandparent(n)->color = L_RED_NODE; + if (n == n->parent->left && n->parent == grandparent(n)->left) { + rotate_right(t, grandparent(n)); + } else if (n == n->parent->right && n->parent == grandparent(n)->right) { + rotate_left(t, grandparent(n)); + } else { + L_ERROR("identity confusion\n", "insert_case5"); + } +} + +static node *maximum_node(node *n) { + if (!n) { + L_ERROR("n not defined\n", "maximum_node"); + return NULL; + } + while (n->right != NULL) { + n = n->right; + } + return n; +} + +static void delete_case1(L_RBTREE *t, node *n) { + if (n->parent == NULL) + return; + else + delete_case2(t, n); +} + +static void delete_case2(L_RBTREE *t, node *n) { + if (node_color(sibling(n)) == L_RED_NODE) { + n->parent->color = L_RED_NODE; + sibling(n)->color = L_BLACK_NODE; + if (n == n->parent->left) + rotate_left(t, n->parent); + else + rotate_right(t, n->parent); + } + delete_case3(t, n); +} + +static void delete_case3(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_BLACK_NODE && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + delete_case1(t, n->parent); + } else { + delete_case4(t, n); + } +} + +static void delete_case4(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_RED_NODE && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + n->parent->color = L_BLACK_NODE; + } else { + delete_case5(t, n); + } +} + +static void delete_case5(L_RBTREE *t, node *n) { + if (n == n->parent->left && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_RED_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + sibling(n)->left->color = L_BLACK_NODE; + rotate_right(t, sibling(n)); + } else if (n == n->parent->right && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_RED_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + sibling(n)->right->color = L_BLACK_NODE; + rotate_left(t, sibling(n)); + } + delete_case6(t, n); +} + +static void delete_case6(L_RBTREE *t, node *n) { + sibling(n)->color = node_color(n->parent); + n->parent->color = L_BLACK_NODE; + if (n == n->parent->left) { + if (node_color(sibling(n)->right) != L_RED_NODE) { + L_ERROR("right sibling is not RED", "delete_case6"); + return; + } + sibling(n)->right->color = L_BLACK_NODE; + rotate_left(t, n->parent); + } else { + if (node_color(sibling(n)->left) != L_RED_NODE) { + L_ERROR("left sibling is not RED", "delete_case6"); + return; + } + sibling(n)->left->color = L_BLACK_NODE; + rotate_right(t, n->parent); + } +} + + +/* ------------------------------------------------------------- * + * Debugging: verify if tree is valid * + * ------------------------------------------------------------- */ +#if VERIFY_RBTREE +static void verify_property_1(node *root); +static void verify_property_2(node *root); +static void verify_property_4(node *root); +static void verify_property_5(node *root); +static void verify_property_5_helper(node *n, int black_count, + int* black_count_path); +#endif + +static void verify_properties(L_RBTREE *t) { +#if VERIFY_RBTREE + verify_property_1(t->root); + verify_property_2(t->root); + /* Property 3 is implicit */ + verify_property_4(t->root); + verify_property_5(t->root); +#endif +} + +#if VERIFY_RBTREE +static void verify_property_1(node *n) { + if (node_color(n) != L_RED_NODE && node_color(n) != L_BLACK_NODE) { + L_ERROR("color neither RED nor BLACK\n", "verify_property_1"); + return; + } + if (n == NULL) return; + verify_property_1(n->left); + verify_property_1(n->right); +} + +static void verify_property_2(node *root) { + if (node_color(root) != L_BLACK_NODE) + L_ERROR("root is not black!\n", "verify_property_2"); +} + +static void verify_property_4(node *n) { + if (node_color(n) == L_RED_NODE) { + if (node_color(n->left) != L_BLACK_NODE || + node_color(n->right) != L_BLACK_NODE || + node_color(n->parent) != L_BLACK_NODE) { + L_ERROR("children & parent not all BLACK", "verify_property_4"); + return; + } + } + if (n == NULL) return; + verify_property_4(n->left); + verify_property_4(n->right); +} + +static void verify_property_5(node *root) { + int black_count_path = -1; + verify_property_5_helper(root, 0, &black_count_path); +} + +static void verify_property_5_helper(node *n, int black_count, + int* path_black_count) { + if (node_color(n) == L_BLACK_NODE) { + black_count++; + } + if (n == NULL) { + if (*path_black_count == -1) { + *path_black_count = black_count; + } else if (*path_black_count != black_count) { + L_ERROR("incorrect black count", "verify_property_5_helper"); + } + return; + } + verify_property_5_helper(n->left, black_count, path_black_count); + verify_property_5_helper(n->right, black_count, path_black_count); +} +#endif diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rbtree.h b/hgdriver/3rdparty/hgOCR/leptonica/rbtree.h new file mode 100644 index 0000000..6977d33 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rbtree.h @@ -0,0 +1,91 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * Modified from the excellent code here: + * http://en.literateprograms.org/Red-black_tree_(C)?oldid=19567 + * which has been placed in the public domain under the Creative Commons + * CC0 1.0 waiver (http://creativecommons.org/publicdomain/zero/1.0/). + * + * When the key is generated from a hash (e.g., string --> uint64), + * there is always the possibility of having collisions, but to make + * the collision probability very low requires using a large hash. + * For that reason, the key types are 64 bit quantities, which will result + * in a negligible probabililty of collisions for millions of hashed values. + * Using 8 byte keys instead of 4 byte keys requires a little more + * storage, but the simplification in being able to ignore collisions + * with the red-black trees for most applications is worth it. + */ + +#ifndef LEPTONICA_RBTREE_H +#define LEPTONICA_RBTREE_H + + /*! The three valid key types for red-black trees, maps and sets. */ +/*! RBTree Key Type */ +enum { + L_INT_TYPE = 1, + L_UINT_TYPE = 2, + L_FLOAT_TYPE = 3 +}; + + /*! + * Storage for keys and values for red-black trees, maps and sets. + *
+     * Note:
+     *   (1) Keys and values of the valid key types are all 64-bit
+     *   (2) (void *) can be used for values but not for keys.
+     * 
+ */ +union Rb_Type { + l_int64 itype; + l_uint64 utype; + l_float64 ftype; + void *ptype; +}; +typedef union Rb_Type RB_TYPE; + +struct L_Rbtree { + struct L_Rbtree_Node *root; + l_int32 keytype; +}; +typedef struct L_Rbtree L_RBTREE; +typedef struct L_Rbtree L_AMAP; /* hide underlying implementation for map */ +typedef struct L_Rbtree L_ASET; /* hide underlying implementation for set */ + +struct L_Rbtree_Node { + union Rb_Type key; + union Rb_Type value; + struct L_Rbtree_Node *left; + struct L_Rbtree_Node *right; + struct L_Rbtree_Node *parent; + l_int32 color; +}; +typedef struct L_Rbtree_Node L_RBTREE_NODE; +typedef struct L_Rbtree_Node L_AMAP_NODE; /* hide tree implementation */ +typedef struct L_Rbtree_Node L_ASET_NODE; /* hide tree implementation */ + + +#endif /* LEPTONICA_RBTREE_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.c b/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.c new file mode 100644 index 0000000..f4df42c --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.c @@ -0,0 +1,1494 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file readbarcode.c + *
+ *
+ *      Basic operations to locate and identify the line widths
+ *      in 1D barcodes.
+ *
+ *      Top level
+ *          SARRAY          *pixProcessBarcodes()
+ *
+ *      Next levels
+ *          PIXA            *pixExtractBarcodes()
+ *          SARRAY          *pixReadBarcodes()
+ *          l_int32          pixReadBarcodeWidths()
+ *
+ *      Location
+ *          BOXA            *pixLocateBarcodes()
+ *          static PIX      *pixGenerateBarcodeMask()
+ *
+ *      Extraction and deskew
+ *          PIXA            *pixDeskewBarcodes()
+ *
+ *      Process to get line widths
+ *          NUMA            *pixExtractBarcodeWidths1()
+ *          NUMA            *pixExtractBarcodeWidths2()
+ *          NUMA            *pixExtractBarcodeCrossings()
+ *
+ *      Average adjacent rasters
+ *          static NUMA     *pixAverageRasterScans()
+ *
+ *      Signal processing for barcode widths
+ *          NUMA            *numaQuantizeCrossingsByWidth()
+ *          static l_int32   numaGetCrossingDistances()
+ *          static NUMA     *numaLocatePeakRanges()
+ *          static NUMA     *numaGetPeakCentroids()
+ *          static NUMA     *numaGetPeakWidthLUT()
+ *          NUMA            *numaQuantizeCrossingsByWindow()
+ *          static l_int32   numaEvalBestWidthAndShift()
+ *          static l_int32   numaEvalSyncError()
+ *
+ *
+ *  NOTE CAREFULLY: This is "early beta" code.  It has not been tuned
+ *  to work robustly on a large database of barcode images.  I'm putting
+ *  it out so that people can play with it, find out how it breaks, and
+ *  contribute decoders for other barcode formats.  Both the functional
+ *  interfaces and ABI will almost certainly change in the coming
+ *  few months.  The actual decoder, in bardecode.c, at present only
+ *  works on the following codes: Code I2of5, Code 2of5, Code 39, Code 93
+ *  Codabar and UPCA.  To add another barcode format, it is necessary
+ *  to make changes in readbarcode.h and bardecode.c.
+ *  The program prog/barcodetest shows how to run from the top level
+ *  (image --> decoded data).
+ * 
+ */ + +#include +#include "allheaders.h" +#include "readbarcode.h" + + /* Parameters for pixGenerateBarcodeMask() */ +static const l_int32 MAX_SPACE_WIDTH = 19; /* was 15 */ +static const l_int32 MAX_NOISE_WIDTH = 50; /* smaller than barcode width */ +static const l_int32 MAX_NOISE_HEIGHT = 30; /* smaller than barcode height */ + + /* Static functions */ +static PIX *pixGenerateBarcodeMask(PIX *pixs, l_int32 maxspace, + l_int32 nwidth, l_int32 nheight); +static NUMA *pixAverageRasterScans(PIX *pixs, l_int32 nscans); +static l_int32 numaGetCrossingDistances(NUMA *nas, NUMA **pnaedist, + NUMA **pnaodist, l_float32 *pmindist, + l_float32 *pmaxdist); +static NUMA *numaLocatePeakRanges(NUMA *nas, l_float32 minfirst, + l_float32 minsep, l_float32 maxmin); +static NUMA *numaGetPeakCentroids(NUMA *nahist, NUMA *narange); +static NUMA *numaGetPeakWidthLUT(NUMA *narange, NUMA *nacent); +static l_int32 numaEvalBestWidthAndShift(NUMA *nas, l_int32 nwidth, + l_int32 nshift, l_float32 minwidth, + l_float32 maxwidth, + l_float32 *pbestwidth, + l_float32 *pbestshift, + l_float32 *pbestscore); +static l_int32 numaEvalSyncError(NUMA *nas, l_int32 ifirst, l_int32 ilast, + l_float32 width, l_float32 shift, + l_float32 *pscore, NUMA **pnad); + + +#ifndef NO_CONSOLE_IO +#define DEBUG_DESKEW 1 +#define DEBUG_WIDTHS 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------------* + * Top level * + *------------------------------------------------------------------------*/ +/*! + * \brief pixProcessBarcodes() + * + * \param[in] pixs any depth + * \param[in] format L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ... + * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS + * \param[out] psaw [optional] sarray of bar widths + * \param[in] debugflag use 1 to generate debug output + * \return sarray text of barcodes, or NULL if none found or on error + */ +SARRAY * +pixProcessBarcodes(PIX *pixs, + l_int32 format, + l_int32 method, + SARRAY **psaw, + l_int32 debugflag) +{ +PIX *pixg; +PIXA *pixa; +SARRAY *sad; + + PROCNAME("pixProcessBarcodes"); + + if (psaw) *psaw = NULL; + if (!pixs) + return (SARRAY *)ERROR_PTR("pixs not defined", procName, NULL); + if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) + return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); + if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) + return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); + + /* Get an 8 bpp image, no cmap */ + if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) + pixg = pixClone(pixs); + else + pixg = pixConvertTo8(pixs, 0); + + if ((pixa = pixExtractBarcodes(pixg, debugflag)) == NULL) { + pixDestroy(&pixg); + return (SARRAY *)ERROR_PTR("no barcode(s) found", procName, NULL); + } + + sad = pixReadBarcodes(pixa, format, method, psaw, debugflag); + + pixDestroy(&pixg); + pixaDestroy(&pixa); + return sad; +} + + +/*! + * \brief pixExtractBarcodes() + * + * \param[in] pixs 8 bpp, no colormap + * \param[in] debugflag use 1 to generate debug output + * \return pixa deskewed and cropped barcodes, or NULL if none found + * or on error + */ +PIXA * +pixExtractBarcodes(PIX *pixs, + l_int32 debugflag) +{ +l_int32 i, n; +l_float32 angle, conf; +BOX *box; +BOXA *boxa; +PIX *pixb, *pixm, *pixt; +PIXA *pixa; + + PROCNAME("pixExtractBarcodes"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIXA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + /* Locate them; use small threshold for edges. */ + boxa = pixLocateBarcodes(pixs, 20, &pixb, &pixm); + n = boxaGetCount(boxa); + L_INFO("%d possible barcode(s) found\n", procName, n); + if (n == 0) { + boxaDestroy(&boxa); + pixDestroy(&pixb); + pixDestroy(&pixm); + return NULL; + } + + if (debugflag) { + boxaWriteStream(stderr, boxa); + pixDisplay(pixb, 100, 100); + pixDisplay(pixm, 800, 100); + } + + /* Deskew each barcode individually */ + pixa = pixaCreate(n); + for (i = 0; i < n; i++) { + box = boxaGetBox(boxa, i, L_CLONE); + pixt = pixDeskewBarcode(pixs, pixb, box, 15, 20, &angle, &conf); + L_INFO("angle = %6.2f, conf = %6.2f\n", procName, angle, conf); + if (conf > 5.0) { + pixaAddPix(pixa, pixt, L_INSERT); + pixaAddBox(pixa, box, L_INSERT); + } else { + pixDestroy(&pixt); + boxDestroy(&box); + } + } + +#if DEBUG_DESKEW + pixt = pixaDisplayTiledInRows(pixa, 8, 1000, 1.0, 0, 30, 2); + pixWrite("junkpixt", pixt, IFF_PNG); + pixDestroy(&pixt); +#endif /* DEBUG_DESKEW */ + + pixDestroy(&pixb); + pixDestroy(&pixm); + boxaDestroy(&boxa); + return pixa; +} + + +/*! + * \brief pixReadBarcodes() + * + * \param[in] pixa of 8 bpp deskewed and cropped barcodes + * \param[in] format L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ... + * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS; + * \param[out] psaw [optional] sarray of bar widths + * \param[in] debugflag use 1 to generate debug output + * \return sa sarray of widths, one string for each barcode found, + * or NULL on error + */ +SARRAY * +pixReadBarcodes(PIXA *pixa, + l_int32 format, + l_int32 method, + SARRAY **psaw, + l_int32 debugflag) +{ +char *barstr, *data; +char emptystring[] = ""; +l_int32 i, j, n, nbars, ival; +NUMA *na; +PIX *pixt; +SARRAY *saw, *sad; + + PROCNAME("pixReadBarcodes"); + + if (psaw) *psaw = NULL; + if (!pixa) + return (SARRAY *)ERROR_PTR("pixa not defined", procName, NULL); + if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) + return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); + if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) + return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); + + n = pixaGetCount(pixa); + saw = sarrayCreate(n); + sad = sarrayCreate(n); + for (i = 0; i < n; i++) { + /* Extract the widths of the lines in each barcode */ + pixt = pixaGetPix(pixa, i, L_CLONE); + na = pixReadBarcodeWidths(pixt, method, debugflag); + pixDestroy(&pixt); + if (!na) { + ERROR_INT("valid barcode widths not returned", procName, 1); + continue; + } + + /* Save the widths as a string */ + nbars = numaGetCount(na); + barstr = (char *)LEPT_CALLOC(nbars + 1, sizeof(char)); + for (j = 0; j < nbars; j++) { + numaGetIValue(na, j, &ival); + barstr[j] = 0x30 + ival; + } + sarrayAddString(saw, barstr, L_INSERT); + numaDestroy(&na); + + /* Decode the width strings */ + data = barcodeDispatchDecoder(barstr, format, debugflag); + if (!data) { + ERROR_INT("barcode not decoded", procName, 1); + sarrayAddString(sad, emptystring, L_COPY); + continue; + } + sarrayAddString(sad, data, L_INSERT); + } + + /* If nothing found, clean up */ + if (sarrayGetCount(saw) == 0) { + sarrayDestroy(&saw); + sarrayDestroy(&sad); + return (SARRAY *)ERROR_PTR("no valid barcode data", procName, NULL); + } + + if (psaw) + *psaw = saw; + else + sarrayDestroy(&saw); + + return sad; +} + + +/*! + * \brief pixReadBarcodeWidths() + * + * \param[in] pixs of 8 bpp deskewed and cropped barcode + * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS; + * \param[in] debugflag use 1 to generate debug output + * \return na numa of widths (each in set {1,2,3,4}, or NULL on error + */ +NUMA * +pixReadBarcodeWidths(PIX *pixs, + l_int32 method, + l_int32 debugflag) +{ +l_float32 winwidth; +NUMA *na; + + PROCNAME("pixReadBarcodeWidths"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (NUMA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) + return (NUMA *)ERROR_PTR("invalid method", procName, NULL); + + /* Extract the widths of the lines in each barcode */ + if (method == L_USE_WIDTHS) + na = pixExtractBarcodeWidths1(pixs, 120, 0.25, NULL, NULL, + debugflag); + else /* method == L_USE_WINDOWS */ + na = pixExtractBarcodeWidths2(pixs, 120, &winwidth, + NULL, debugflag); +#if DEBUG_WIDTHS + if (method == L_USE_WINDOWS) + fprintf(stderr, "Window width for barcode: %7.3f\n", winwidth); + numaWriteStream(stderr, na); +#endif /* DEBUG_WIDTHS */ + + if (!na) + return (NUMA *)ERROR_PTR("barcode widths invalid", procName, NULL); + + return na; +} + + +/*------------------------------------------------------------------------* + * Locate barcode in image * + *------------------------------------------------------------------------*/ +/*! + * \brief pixLocateBarcodes() + * + * \param[in] pixs any depth + * \param[in] thresh for binarization of edge filter output; typ. 20 + * \param[out] ppixb [optional] binarized edge filtered input image + * \param[out] ppixm [optional] mask over barcodes + * \return boxa location of barcodes, or NULL if none found or on error + */ +BOXA * +pixLocateBarcodes(PIX *pixs, + l_int32 thresh, + PIX **ppixb, + PIX **ppixm) +{ +BOXA *boxa; +PIX *pix8, *pixe, *pixb, *pixm; + + PROCNAME("pixLocateBarcodes"); + + if (!pixs) + return (BOXA *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Get an 8 bpp image, no cmap */ + if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) + pix8 = pixClone(pixs); + else + pix8 = pixConvertTo8(pixs, 0); + + /* Get a 1 bpp image of the edges */ + pixe = pixSobelEdgeFilter(pix8, L_ALL_EDGES); + pixb = pixThresholdToBinary(pixe, thresh); + pixInvert(pixb, pixb); + pixDestroy(&pix8); + pixDestroy(&pixe); + + pixm = pixGenerateBarcodeMask(pixb, MAX_SPACE_WIDTH, MAX_NOISE_WIDTH, + MAX_NOISE_HEIGHT); + boxa = pixConnComp(pixm, NULL, 8); + + if (ppixb) + *ppixb = pixb; + else + pixDestroy(&pixb); + if (ppixm) + *ppixm = pixm; + else + pixDestroy(&pixm); + + return boxa; +} + + +/*! + * \brief pixGenerateBarcodeMask() + * + * \param[in] pixs 1 bpp + * \param[in] maxspace largest space in the barcode, in pixels + * \param[in] nwidth opening 'width' to remove noise + * \param[in] nheight opening 'height' to remove noise + * \return pixm mask over barcodes, or NULL if none found or on error + * + *
+ * Notes:
+ *      (1) For noise removal, 'width' and 'height' are referred to the
+ *          barcode orientation.
+ *      (2) If there is skew, the mask will not cover the barcode corners.
+ * 
+ */ +static PIX * +pixGenerateBarcodeMask(PIX *pixs, + l_int32 maxspace, + l_int32 nwidth, + l_int32 nheight) +{ +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixGenerateBarcodeMask"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Identify horizontal barcodes */ + pixt1 = pixCloseBrick(NULL, pixs, maxspace + 1, 1); + pixt2 = pixOpenBrick(NULL, pixs, maxspace + 1, 1); + pixXor(pixt2, pixt2, pixt1); + pixOpenBrick(pixt2, pixt2, nwidth, nheight); + pixDestroy(&pixt1); + + /* Identify vertical barcodes */ + pixt1 = pixCloseBrick(NULL, pixs, 1, maxspace + 1); + pixd = pixOpenBrick(NULL, pixs, 1, maxspace + 1); + pixXor(pixd, pixd, pixt1); + pixOpenBrick(pixd, pixd, nheight, nwidth); + pixDestroy(&pixt1); + + /* Combine to get all barcodes */ + pixOr(pixd, pixd, pixt2); + pixDestroy(&pixt2); + + return pixd; +} + + +/*------------------------------------------------------------------------* + * Extract and deskew barcode * + *------------------------------------------------------------------------*/ +/*! + * \brief pixDeskewBarcode() + * + * \param[in] pixs input image; 8 bpp + * \param[in] pixb binarized edge-filtered input image + * \param[in] box identified region containing barcode + * \param[in] margin of extra pixels around box to extract + * \param[in] threshold for binarization; ~20 + * \param[out] pangle [optional] in degrees, clockwise is positive + * \param[out] pconf [optional] confidence + * \return pixd deskewed barcode, or NULL on error + * + *
+ * Notes:
+ *     (1) The (optional) angle returned is the angle in degrees (cw positive)
+ *         necessary to rotate the image so that it is deskewed.
+ * 
+ */ +PIX * +pixDeskewBarcode(PIX *pixs, + PIX *pixb, + BOX *box, + l_int32 margin, + l_int32 threshold, + l_float32 *pangle, + l_float32 *pconf) +{ +l_int32 x, y, w, h, n; +l_float32 angle, angle1, angle2, conf, conf1, conf2, score1, score2, deg2rad; +BOX *boxe, *boxt; +BOXA *boxa, *boxat; +PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6, *pixd; + + PROCNAME("pixDeskewBarcode"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + if (!pixb || pixGetDepth(pixb) != 1) + return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL); + if (!box) + return (PIX *)ERROR_PTR("box not defined or 1 bpp", procName, NULL); + + /* Clip out */ + deg2rad = 3.1415926535 / 180.; + boxGetGeometry(box, &x, &y, &w, &h); + boxe = boxCreate(x - 25, y - 25, w + 51, h + 51); + pixt1 = pixClipRectangle(pixb, boxe, NULL); + pixt2 = pixClipRectangle(pixs, boxe, NULL); + boxDestroy(&boxe); + + /* Deskew, looking at all possible orientations over 180 degrees */ + pixt3 = pixRotateOrth(pixt1, 1); /* look for vertical bar lines */ + pixt4 = pixClone(pixt1); /* look for horizontal bar lines */ + pixFindSkewSweepAndSearchScore(pixt3, &angle1, &conf1, &score1, + 1, 1, 0.0, 45.0, 2.5, 0.01); + pixFindSkewSweepAndSearchScore(pixt4, &angle2, &conf2, &score2, + 1, 1, 0.0, 45.0, 2.5, 0.01); + + /* Because we're using the boundary pixels of the barcodes, + * the peak can be sharper (and the confidence ratio higher) + * from the signal across the top and bottom of the barcode. + * However, the max score, which is the magnitude of the signal + * at the optimum skew angle, will be smaller, so we use the + * max score as the primary indicator of orientation. */ + if (score1 >= score2) { + conf = conf1; + if (conf1 > 6.0 && L_ABS(angle1) > 0.1) { + angle = angle1; + pixt5 = pixRotate(pixt2, deg2rad * angle1, L_ROTATE_AREA_MAP, + L_BRING_IN_WHITE, 0, 0); + } else { + angle = 0.0; + pixt5 = pixClone(pixt2); + } + } else { /* score2 > score1 */ + conf = conf2; + pixt6 = pixRotateOrth(pixt2, 1); + if (conf2 > 6.0 && L_ABS(angle2) > 0.1) { + angle = 90.0 + angle2; + pixt5 = pixRotate(pixt6, deg2rad * angle2, L_ROTATE_AREA_MAP, + L_BRING_IN_WHITE, 0, 0); + } else { + angle = 90.0; + pixt5 = pixClone(pixt6); + } + pixDestroy(&pixt6); + } + pixDestroy(&pixt3); + pixDestroy(&pixt4); + + /* Extract barcode plus a margin around it */ + boxa = pixLocateBarcodes(pixt5, threshold, 0, 0); + if ((n = boxaGetCount(boxa)) != 1) { + L_WARNING("barcode mask in %d components\n", procName, n); + boxat = boxaSort(boxa, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); + } else { + boxat = boxaCopy(boxa, L_CLONE); + } + boxt = boxaGetBox(boxat, 0, L_CLONE); + boxGetGeometry(boxt, &x, &y, &w, &h); + boxe = boxCreate(x - margin, y - margin, w + 2 * margin, + h + 2 * margin); + pixd = pixClipRectangle(pixt5, boxe, NULL); + boxDestroy(&boxt); + boxDestroy(&boxe); + boxaDestroy(&boxa); + boxaDestroy(&boxat); + + if (pangle) *pangle = angle; + if (pconf) *pconf = conf; + + pixDestroy(&pixt1); + pixDestroy(&pixt2); + pixDestroy(&pixt5); + return pixd; +} + + +/*------------------------------------------------------------------------* + * Process to get line widths * + *------------------------------------------------------------------------*/ +/*! + * \brief pixExtractBarcodeWidths1() + * + * \param[in] pixs input image; 8 bpp + * \param[in] thresh estimated pixel threshold for crossing + * white <--> black; typ. ~120 + * \param[in] binfract histo binsize as a fraction of minsize; e.g., 0.25 + * \param[out] pnaehist [optional] histogram of black widths; NULL ok + * \param[out] pnaohist [optional] histogram of white widths; NULL ok + * \param[in] debugflag use 1 to generate debug output + * \return nad numa of barcode widths in encoded integer units, + * or NULL on error + * + *
+ * Notes:
+ *     (1) The widths are alternating black/white, starting with black
+ *         and ending with black.
+ *     (2) This method uses the widths of the bars directly, in terms
+ *         of the (float) number of pixels between transitions.
+ *         The histograms of these widths for black and white bars is
+ *         generated and interpreted.
+ * 
+ */ +NUMA * +pixExtractBarcodeWidths1(PIX *pixs, + l_float32 thresh, + l_float32 binfract, + NUMA **pnaehist, + NUMA **pnaohist, + l_int32 debugflag) +{ +NUMA *nac, *nad; + + PROCNAME("pixExtractBarcodeWidths1"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + /* Get the best estimate of the crossings, in pixel units */ + nac = pixExtractBarcodeCrossings(pixs, thresh, debugflag); + + /* Get the array of bar widths, starting with a black bar */ + nad = numaQuantizeCrossingsByWidth(nac, binfract, pnaehist, + pnaohist, debugflag); + + numaDestroy(&nac); + return nad; +} + + +/*! + * \brief pixExtractBarcodeWidths2() + * + * \param[in] pixs input image; 8 bpp + * \param[in] thresh estimated pixel threshold for crossing + * white <--> black; typ. ~120 + * \param[out] pwidth [optional] best decoding window width, in pixels + * \param[out] pnac [optional] number of transitions in each window + * \param[in] debugflag use 1 to generate debug output + * \return nad numa of barcode widths in encoded integer units, + * or NULL on error + * + *
+ * Notes:
+ *      (1) The widths are alternating black/white, starting with black
+ *          and ending with black.
+ *      (2) The optional best decoding window width is the width of the window
+ *          that is used to make a decision about whether a transition occurs.
+ *          It is approximately the average width in pixels of the narrowest
+ *          white and black bars (i.e., those corresponding to unit width).
+ *      (3) The optional return signal %nac is a sequence of 0s, 1s,
+ *          and perhaps a few 2s, giving the number of crossings in each window.
+ *          On the occasion where there is a '2', it is interpreted as
+ *          as ending two runs: the previous one and another one that has length 1.
+ * 
+ */ +NUMA * +pixExtractBarcodeWidths2(PIX *pixs, + l_float32 thresh, + l_float32 *pwidth, + NUMA **pnac, + l_int32 debugflag) +{ +NUMA *nacp, *nad; + + PROCNAME("pixExtractBarcodeWidths2"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + /* Get the best estimate of the crossings, in pixel units */ + nacp = pixExtractBarcodeCrossings(pixs, thresh, debugflag); + + /* Quantize the crossings to get actual windowed data */ + nad = numaQuantizeCrossingsByWindow(nacp, 2.0, pwidth, NULL, pnac, debugflag); + + numaDestroy(&nacp); + return nad; +} + + +/*! + * \brief pixExtractBarcodeCrossings() + * + * \param[in] pixs input image; 8 bpp + * \param[in] thresh estimated pixel threshold for crossing + * white <--> black; typ. ~120 + * \param[in] debugflag use 1 to generate debug output + * \return numa of crossings, in pixel units, or NULL on error + */ +NUMA * +pixExtractBarcodeCrossings(PIX *pixs, + l_float32 thresh, + l_int32 debugflag) +{ +l_int32 w; +l_float32 bestthresh; +NUMA *nas, *nax, *nay, *nad; + + PROCNAME("pixExtractBarcodeCrossings"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + /* Scan pixels horizontally and average results */ + nas = pixAverageRasterScans(pixs, 51); + + /* Interpolate to get 4x the number of values */ + w = pixGetWidth(pixs); + numaInterpolateEqxInterval(0.0, 1.0, nas, L_QUADRATIC_INTERP, 0.0, + (l_float32)(w - 1), 4 * w + 1, &nax, &nay); + + if (debugflag) { + lept_mkdir("lept/barcode"); + GPLOT *gplot = gplotCreate("/tmp/lept/barcode/signal", GPLOT_PNG, + "Pixel values", "dist in pixels", "value"); + gplotAddPlot(gplot, nax, nay, GPLOT_LINES, "plot 1"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } + + /* Locate the crossings. Run multiple times with different + * thresholds, and choose a threshold in the center of the + * run of thresholds that all give the maximum number of crossings. */ + numaSelectCrossingThreshold(nax, nay, thresh, &bestthresh); + + /* Get the crossings with the best threshold. */ + nad = numaCrossingsByThreshold(nax, nay, bestthresh); + + numaDestroy(&nas); + numaDestroy(&nax); + numaDestroy(&nay); + return nad; +} + + +/*------------------------------------------------------------------------* + * Average adjacent rasters * + *------------------------------------------------------------------------*/ +/*! + * \brief pixAverageRasterScans() + * + * \param[in] pixs input image; 8 bpp + * \param[in] nscans number of adjacent scans, about the center vertically + * \return numa of average pixel values across image, or NULL on error + */ +static NUMA * +pixAverageRasterScans(PIX *pixs, + l_int32 nscans) +{ +l_int32 w, h, first, last, i, j, wpl, val; +l_uint32 *line, *data; +l_float32 *array; +NUMA *nad; + + PROCNAME("pixAverageRasterScans"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (nscans <= h) { + first = 0; + last = h - 1; + nscans = h; + } else { + first = (h - nscans) / 2; + last = first + nscans - 1; + } + + nad = numaCreate(w); + numaSetCount(nad, w); + array = numaGetFArray(nad, L_NOCOPY); + wpl = pixGetWpl(pixs); + data = pixGetData(pixs); + for (j = 0; j < w; j++) { + for (i = first; i <= last; i++) { + line = data + i * wpl; + val = GET_DATA_BYTE(line, j); + array[j] += val; + } + array[j] = array[j] / (l_float32)nscans; + } + + return nad; +} + + +/*------------------------------------------------------------------------* + * Signal processing for barcode widths * + *------------------------------------------------------------------------*/ +/*! + * \brief numaQuantizeCrossingsByWidth() + * + * \param[in] nas numa of crossing locations, in pixel units + * \param[in] binfract histo binsize as a fraction of minsize; e.g., 0.25 + * \param[out] pnaehist [optional] histo of even (black) bar widths + * \param[out] pnaohist [optional] histo of odd (white) bar widths + * \param[in] debugflag 1 to generate plots of histograms of bar widths + * \return nad sequence of widths, in unit sizes, or NULL on error + * + *
+ * Notes:
+ *      (1) This first computes the histogram of black and white bar widths,
+ *          binned in appropriate units.  There should be well-defined
+ *          peaks, each corresponding to a specific width.  The sequence
+ *          of barcode widths (namely, the integers from the set {1,2,3,4})
+ *          is returned.
+ *      (2) The optional returned histograms are binned in width units
+ *          that are inversely proportional to %binfract.  For example,
+ *          if %binfract = 0.25, there are 4.0 bins in the distance of
+ *          the width of the narrowest bar.
+ * 
+ */ +NUMA * +numaQuantizeCrossingsByWidth(NUMA *nas, + l_float32 binfract, + NUMA **pnaehist, + NUMA **pnaohist, + l_int32 debugflag) +{ +l_int32 i, n, ned, nod, iw, width; +l_float32 val, minsize, maxsize, factor; +GPLOT *gplot; +NUMA *naedist, *naodist, *naehist, *naohist, *naecent, *naocent; +NUMA *naerange, *naorange, *naelut, *naolut, *nad; + + PROCNAME("numaQuantizeCrossingsByWidth"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + if (n < 2) + return (NUMA *)ERROR_PTR("n < 2", procName, NULL); + if (binfract <= 0.0) + return (NUMA *)ERROR_PTR("binfract <= 0.0", procName, NULL); + + /* Get even and odd crossing distances */ + numaGetCrossingDistances(nas, &naedist, &naodist, &minsize, &maxsize); + + /* Bin the spans in units of binfract * minsize. These + * units are convenient because they scale to make at least + * 1/binfract bins in the smallest span (width). We want this + * number to be large enough to clearly separate the + * widths, but small enough so that the histogram peaks + * have very few if any holes (zeroes) within them. */ + naehist = numaMakeHistogramClipped(naedist, binfract * minsize, + (1.25 / binfract) * maxsize); + naohist = numaMakeHistogramClipped(naodist, binfract * minsize, + (1.25 / binfract) * maxsize); + + if (debugflag) { + lept_mkdir("lept/barcode"); + gplot = gplotCreate("/tmp/lept/barcode/histw", GPLOT_PNG, + "Raw width histogram", "Width", "Number"); + gplotAddPlot(gplot, NULL, naehist, GPLOT_LINES, "plot black"); + gplotAddPlot(gplot, NULL, naohist, GPLOT_LINES, "plot white"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } + + /* Compute the peak ranges, still in units of binfract * minsize. */ + naerange = numaLocatePeakRanges(naehist, 1.0 / binfract, + 1.0 / binfract, 0.0); + naorange = numaLocatePeakRanges(naohist, 1.0 / binfract, + 1.0 / binfract, 0.0); + + /* Find the centroid values of each peak */ + naecent = numaGetPeakCentroids(naehist, naerange); + naocent = numaGetPeakCentroids(naohist, naorange); + + /* Generate the lookup tables that map from the bar width, in + * units of (binfract * minsize), to the integerized barcode + * units (1, 2, 3, 4), which are the output integer widths + * between transitions. */ + naelut = numaGetPeakWidthLUT(naerange, naecent); + naolut = numaGetPeakWidthLUT(naorange, naocent); + + /* Get the widths. Because the LUT accepts our funny units, + * we first must convert the pixel widths to these units, + * which is what 'factor' does. */ + nad = numaCreate(0); + ned = numaGetCount(naedist); + nod = numaGetCount(naodist); + if (nod != ned - 1) + L_WARNING("ned != nod + 1\n", procName); + factor = 1.0 / (binfract * minsize); /* for converting units */ + for (i = 0; i < ned - 1; i++) { + numaGetFValue(naedist, i, &val); + width = (l_int32)(factor * val); + numaGetIValue(naelut, width, &iw); + numaAddNumber(nad, iw); +/* fprintf(stderr, "even: val = %7.3f, width = %d, iw = %d\n", + val, width, iw); */ + numaGetFValue(naodist, i, &val); + width = (l_int32)(factor * val); + numaGetIValue(naolut, width, &iw); + numaAddNumber(nad, iw); +/* fprintf(stderr, "odd: val = %7.3f, width = %d, iw = %d\n", + val, width, iw); */ + } + numaGetFValue(naedist, ned - 1, &val); + width = (l_int32)(factor * val); + numaGetIValue(naelut, width, &iw); + numaAddNumber(nad, iw); + + if (debugflag) { + fprintf(stderr, " ---- Black bar widths (pixels) ------ \n"); + numaWriteStream(stderr, naedist); + fprintf(stderr, " ---- Histogram of black bar widths ------ \n"); + numaWriteStream(stderr, naehist); + fprintf(stderr, " ---- Peak ranges in black bar histogram bins --- \n"); + numaWriteStream(stderr, naerange); + fprintf(stderr, " ---- Peak black bar centroid width values ------ \n"); + numaWriteStream(stderr, naecent); + fprintf(stderr, " ---- Black bar lookup table ------ \n"); + numaWriteStream(stderr, naelut); + fprintf(stderr, " ---- White bar widths (pixels) ------ \n"); + numaWriteStream(stderr, naodist); + fprintf(stderr, " ---- Histogram of white bar widths ------ \n"); + numaWriteStream(stderr, naohist); + fprintf(stderr, " ---- Peak ranges in white bar histogram bins --- \n"); + numaWriteStream(stderr, naorange); + fprintf(stderr, " ---- Peak white bar centroid width values ------ \n"); + numaWriteStream(stderr, naocent); + fprintf(stderr, " ---- White bar lookup table ------ \n"); + numaWriteStream(stderr, naolut); + } + + numaDestroy(&naedist); + numaDestroy(&naodist); + numaDestroy(&naerange); + numaDestroy(&naorange); + numaDestroy(&naecent); + numaDestroy(&naocent); + numaDestroy(&naelut); + numaDestroy(&naolut); + if (pnaehist) + *pnaehist = naehist; + else + numaDestroy(&naehist); + if (pnaohist) + *pnaohist = naohist; + else + numaDestroy(&naohist); + return nad; +} + + +/*! + * \brief numaGetCrossingDistances() + * + * \param[in] nas numa of crossing locations + * \param[out] pnaedist [optional] even distances between crossings + * \param[out] pnaodist [optional] odd distances between crossings + * \param[out] pmindist [optional] min distance between crossings + * \param[out] pmaxdist [optional] max distance between crossings + * \return 0 if OK, 1 on error + */ +static l_int32 +numaGetCrossingDistances(NUMA *nas, + NUMA **pnaedist, + NUMA **pnaodist, + l_float32 *pmindist, + l_float32 *pmaxdist) +{ +l_int32 i, n; +l_float32 val, newval, mindist, maxdist, dist; +NUMA *naedist, *naodist; + + PROCNAME("numaGetCrossingDistances"); + + if (pnaedist) *pnaedist = NULL; + if (pnaodist) *pnaodist = NULL; + if (pmindist) *pmindist = 0.0; + if (pmaxdist) *pmaxdist = 0.0; + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if ((n = numaGetCount(nas)) < 2) + return ERROR_INT("n < 2", procName, 1); + + /* Get numas of distances between crossings. Separate these + * into even (e.g., black) and odd (e.g., white) spans. + * For barcodes, the black spans are 0, 2, etc. These + * distances are in pixel units. */ + naedist = numaCreate(n / 2 + 1); + naodist = numaCreate(n / 2); + numaGetFValue(nas, 0, &val); + for (i = 1; i < n; i++) { + numaGetFValue(nas, i, &newval); + if (i % 2) + numaAddNumber(naedist, newval - val); + else + numaAddNumber(naodist, newval - val); + val = newval; + } + + /* The mindist and maxdist of the spans are in pixel units. */ + numaGetMin(naedist, &mindist, NULL); + numaGetMin(naodist, &dist, NULL); + mindist = L_MIN(dist, mindist); + numaGetMax(naedist, &maxdist, NULL); + numaGetMax(naodist, &dist, NULL); + maxdist = L_MAX(dist, maxdist); + L_INFO("mindist = %7.3f, maxdist = %7.3f\n", procName, mindist, maxdist); + + if (pnaedist) + *pnaedist = naedist; + else + numaDestroy(&naedist); + if (pnaodist) + *pnaodist = naodist; + else + numaDestroy(&naodist); + if (pmindist) *pmindist = mindist; + if (pmaxdist) *pmaxdist = maxdist; + return 0; +} + + +/*! + * \brief numaLocatePeakRanges() + * + * \param[in] nas numa of histogram of crossing widths + * \param[in] minfirst min location of center of first peak + * \param[in] minsep min separation between peak range centers + * \param[in] maxmin max allowed value for min histo value between peaks + * \return nad ranges for each peak found, in pairs, or NULL on error + * + *
+ * Notes:
+ *      (1) Units of %minsep are the index into nas.
+ *          This puts useful constraints on peak-finding.
+ *      (2) If maxmin == 0.0, the value of nas[i] must go to 0.0 (or less)
+ *          between peaks.
+ *      (3) All calculations are done in units of the index into nas.
+ *          The resulting ranges are therefore integers.
+ *      (4) The output nad gives pairs of range values for successive peaks.
+ *          Any location [i] for which maxmin = nas[i] = 0.0 will NOT be
+ *          included in a peak range.  This works fine for histograms where
+ *          if nas[i] == 0.0, it means that there are no samples at [i].
+ *      (5) For barcodes, when this is used on a histogram of barcode
+ *          widths, use maxmin = 0.0.  This requires that there is at
+ *          least one histogram bin corresponding to a width value between
+ *          adjacent peak ranges that is unpopulated, making the separation
+ *          of the histogram peaks unambiguous.
+ * 
+ */ +static NUMA * +numaLocatePeakRanges(NUMA *nas, + l_float32 minfirst, + l_float32 minsep, + l_float32 maxmin) +{ +l_int32 i, n, inpeak, left; +l_float32 center, prevcenter, val; +NUMA *nad; + + PROCNAME("numaLocatePeakRanges"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + n = numaGetCount(nas); + nad = numaCreate(0); + + inpeak = FALSE; + prevcenter = minfirst - minsep - 1.0; + for (i = 0; i < n; i++) { + numaGetFValue(nas, i, &val); + if (inpeak == FALSE && val > maxmin) { + inpeak = TRUE; + left = i; + } else if (inpeak == TRUE && val <= maxmin) { /* end peak */ + center = (left + i - 1.0) / 2.0; + if (center - prevcenter >= minsep) { /* save new peak */ + inpeak = FALSE; + numaAddNumber(nad, left); + numaAddNumber(nad, i - 1); + prevcenter = center; + } else { /* attach to previous peak; revise the right edge */ + numaSetValue(nad, numaGetCount(nad) - 1, i - 1); + } + } + } + if (inpeak == TRUE) { /* save the last peak */ + numaAddNumber(nad, left); + numaAddNumber(nad, n - 1); + } + + return nad; +} + + +/*! + * \brief numaGetPeakCentroids() + * + * \param[in] nahist numa of histogram of crossing widths + * \param[in] narange numa of ranges of x-values for the peaks in %nahist + * \return nad centroids for each peak found; max of 4, corresponding + * to 4 different barcode line widths, or NULL on error + */ +static NUMA * +numaGetPeakCentroids(NUMA *nahist, + NUMA *narange) +{ +l_int32 i, j, nr, low, high; +l_float32 cent, sum, val; +NUMA *nad; + + PROCNAME("numaGetPeakCentroids"); + + if (!nahist) + return (NUMA *)ERROR_PTR("nahist not defined", procName, NULL); + if (!narange) + return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); + nr = numaGetCount(narange) / 2; + + nad = numaCreate(4); + for (i = 0; i < nr; i++) { + numaGetIValue(narange, 2 * i, &low); + numaGetIValue(narange, 2 * i + 1, &high); + cent = 0.0; + sum = 0.0; + for (j = low; j <= high; j++) { + numaGetFValue(nahist, j, &val); + cent += j * val; + sum += val; + } + numaAddNumber(nad, cent / sum); + } + + return nad; +} + + +/*! + * \brief numaGetPeakWidthLUT() + * + * \param[in] narange numa of x-val ranges for the histogram width peaks + * \param[in] nacent numa of centroids of each peak -- up to 4 + * \return nalut lookup table from the width of a bar to one of the four + * integerized barcode units, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates the lookup table that maps from a sequence of widths
+ *          (in some units) to the integerized barcode units (1, 2, 3, 4),
+ *          which are the output integer widths between transitions.
+ *      (2) The smallest width can be lost in float roundoff.  To avoid
+ *          losing it, we expand the peak range of the smallest width.
+ * 
+ */ +static NUMA * +numaGetPeakWidthLUT(NUMA *narange, + NUMA *nacent) +{ +l_int32 i, j, nc, low, high, imax; +l_int32 assign[4]; +l_float32 *warray; +l_float32 max, rat21, rat32, rat42; +NUMA *nalut; + + PROCNAME("numaGetPeakWidthLUT"); + + if (!narange) + return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); + if (!nacent) + return (NUMA *)ERROR_PTR("nacent not defined", procName, NULL); + nc = numaGetCount(nacent); /* half the size of narange */ + if (nc < 1 || nc > 4) + return (NUMA *)ERROR_PTR("nc must be 1, 2, 3, or 4", procName, NULL); + + /* Check the peak centroids for consistency with bar widths. + * The third peak can correspond to a width of either 3 or 4. + * Use ratios 3/2 and 4/2 instead of 3/1 and 4/1 because the + * former are more stable and closer to the expected ratio. */ + if (nc > 1) { + warray = numaGetFArray(nacent, L_NOCOPY); + if (warray[0] == 0) + return (NUMA *)ERROR_PTR("first peak has width 0.0", + procName, NULL); + rat21 = warray[1] / warray[0]; + if (rat21 < 1.5 || rat21 > 2.6) + L_WARNING("width ratio 2/1 = %f\n", procName, rat21); + if (nc > 2) { + rat32 = warray[2] / warray[1]; + if (rat32 < 1.3 || rat32 > 2.25) + L_WARNING("width ratio 3/2 = %f\n", procName, rat32); + } + if (nc == 4) { + rat42 = warray[3] / warray[1]; + if (rat42 < 1.7 || rat42 > 2.3) + L_WARNING("width ratio 4/2 = %f\n", procName, rat42); + } + } + + /* Set width assignments. + * The only possible ambiguity is with nc = 3 */ + for (i = 0; i < 4; i++) + assign[i] = i + 1; + if (nc == 3) { + if (rat32 > 1.75) + assign[2] = 4; + } + + /* Put widths into the LUT */ + numaGetMax(narange, &max, NULL); + imax = (l_int32)max; + nalut = numaCreate(imax + 1); + numaSetCount(nalut, imax + 1); /* fill the array with zeroes */ + for (i = 0; i < nc; i++) { + numaGetIValue(narange, 2 * i, &low); + if (i == 0) low--; /* catch smallest width */ + numaGetIValue(narange, 2 * i + 1, &high); + for (j = low; j <= high; j++) + numaSetValue(nalut, j, assign[i]); + } + + return nalut; +} + + +/*! + * \brief numaQuantizeCrossingsByWindow() + * + * \param[in] nas numa of crossing locations + * \param[in] ratio of max window size over min window size in search; + * typ. 2.0 + * \param[out] pwidth [optional] best window width + * \param[out] pfirstloc [optional] center of window for first xing + * \param[out] pnac [optional] array of window crossings (0, 1, 2) + * \param[in] debugflag 1 to generate various plots of intermediate results + * \return nad sequence of widths, in unit sizes, or NULL on error + * + *
+ * Notes:
+ *      (1) The minimum size of the window is set by the minimum
+ *          distance between zero crossings.
+ *      (2) The optional return signal %nac is a sequence of 0s, 1s,
+ *          and perhaps a few 2s, giving the number of crossings in each window.
+ *          On the occasion where there is a '2', it is interpreted as
+ *          ending two runs: the previous one and another one that has length 1.
+ * 
+ */ +NUMA * +numaQuantizeCrossingsByWindow(NUMA *nas, + l_float32 ratio, + l_float32 *pwidth, + l_float32 *pfirstloc, + NUMA **pnac, + l_int32 debugflag) +{ +l_int32 i, nw, started, count, trans; +l_float32 minsize, minwidth, minshift, xfirst; +NUMA *nac, *nad; + + PROCNAME("numaQuantizeCrossingsByWindow"); + + if (!nas) + return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); + if (numaGetCount(nas) < 2) + return (NUMA *)ERROR_PTR("nas size < 2", procName, NULL); + + /* Get the minsize, which is needed for the search for + * the window width (ultimately found as 'minwidth') */ + numaGetCrossingDistances(nas, NULL, NULL, &minsize, NULL); + + /* Compute the width and shift increments; start at minsize + * and go up to ratio * minsize */ + numaEvalBestWidthAndShift(nas, 100, 10, minsize, ratio * minsize, + &minwidth, &minshift, NULL); + + /* Refine width and shift calculation */ + numaEvalBestWidthAndShift(nas, 100, 10, 0.98 * minwidth, 1.02 * minwidth, + &minwidth, &minshift, NULL); + + L_INFO("best width = %7.3f, best shift = %7.3f\n", + procName, minwidth, minshift); + + /* Get the crossing array (0,1,2) for the best window width and shift */ + numaEvalSyncError(nas, 0, 0, minwidth, minshift, NULL, &nac); + if (pwidth) *pwidth = minwidth; + if (pfirstloc) { + numaGetFValue(nas, 0, &xfirst); + *pfirstloc = xfirst + minshift; + } + + /* Get the array of bar widths, starting with a black bar */ + nad = numaCreate(0); + nw = numaGetCount(nac); /* number of window measurements */ + started = FALSE; + count = 0; /* unnecessary init */ + for (i = 0; i < nw; i++) { + numaGetIValue(nac, i, &trans); + if (trans > 2) + L_WARNING("trans = %d > 2 !!!\n", procName, trans); + if (started) { + if (trans > 1) { /* i.e., when trans == 2 */ + numaAddNumber(nad, count); + trans--; + count = 1; + } + if (trans == 1) { + numaAddNumber(nad, count); + count = 1; + } else { + count++; + } + } + if (!started && trans) { + started = TRUE; + if (trans == 2) /* a whole bar in this window */ + numaAddNumber(nad, 1); + count = 1; + } + } + + if (pnac) + *pnac = nac; + else + numaDestroy(&nac); + return nad; +} + + +/*! + * \brief numaEvalBestWidthAndShift() + * + * \param[in] nas numa of crossing locations + * \param[in] nwidth number of widths to consider + * \param[in] nshift number of shifts to consider for each width + * \param[in] minwidth smallest width to consider + * \param[in] maxwidth largest width to consider + * \param[out] pbestwidth best size of window + * \param[out] pbestshift best shift for the window + * \param[out] pbestscore [optional] average squared error of dist + * of crossing signal from the center of the window + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a linear sweep of widths, evaluating at %nshift
+ *          shifts for each width, finding the (width, shift) pair that
+ *          gives the minimum score.
+ * 
+ */ +static l_int32 +numaEvalBestWidthAndShift(NUMA *nas, + l_int32 nwidth, + l_int32 nshift, + l_float32 minwidth, + l_float32 maxwidth, + l_float32 *pbestwidth, + l_float32 *pbestshift, + l_float32 *pbestscore) +{ +l_int32 i, j; +l_float32 delwidth, delshift, width, shift, score; +l_float32 bestwidth, bestshift, bestscore; + + PROCNAME("numaEvalBestWidthAndShift"); + + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if (!pbestwidth || !pbestshift) + return ERROR_INT("&bestwidth and &bestshift not defined", procName, 1); + + bestwidth = 0.0f; + bestshift = 0.0f; + bestscore = 1.0; + delwidth = (maxwidth - minwidth) / (nwidth - 1.0); + for (i = 0; i < nwidth; i++) { + width = minwidth + delwidth * i; + delshift = width / (l_float32)(nshift); + for (j = 0; j < nshift; j++) { + shift = -0.5 * (width - delshift) + j * delshift; + numaEvalSyncError(nas, 0, 0, width, shift, &score, NULL); + if (score < bestscore) { + bestscore = score; + bestwidth = width; + bestshift = shift; +#if DEBUG_FREQUENCY + fprintf(stderr, "width = %7.3f, shift = %7.3f, score = %7.3f\n", + width, shift, score); +#endif /* DEBUG_FREQUENCY */ + } + } + } + + *pbestwidth = bestwidth; + *pbestshift = bestshift; + if (pbestscore) + *pbestscore = bestscore; + return 0; +} + + +/*! + * \brief numaEvalSyncError() + * + * \param[in] nas numa of crossing locations + * \param[in] ifirst first crossing to use + * \param[in] ilast last crossing to use; use 0 for all crossings + * \param[in] width size of window + * \param[in] shift of center of window w/rt first crossing + * \param[out] pscore [optional] average squared error of dist + * of crossing signal from the center of the window + * \param[out] pnad [optional] numa of 1s and 0s for crossings + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The score is computed only on the part of the signal from the
+ *          %ifirst to %ilast crossings.  Use 0 for both of these to
+ *          use all the crossings.  The score is normalized for
+ *          the number of crossings and with half-width of the window.
+ *      (2) The optional return %nad is a sequence of 0s and 1s, where a '1'
+ *          indicates a crossing in the window.
+ * 
+ */ +static l_int32 +numaEvalSyncError(NUMA *nas, + l_int32 ifirst, + l_int32 ilast, + l_float32 width, + l_float32 shift, + l_float32 *pscore, + NUMA **pnad) +{ +l_int32 i, n, nc, nw, ival; +l_int32 iw; /* cell in which transition occurs */ +l_float32 score, xfirst, xlast, xleft, xc, xwc; +NUMA *nad; + + PROCNAME("numaEvalSyncError"); + + if (!nas) + return ERROR_INT("nas not defined", procName, 1); + if ((n = numaGetCount(nas)) < 2) + return ERROR_INT("nas size < 2", procName, 1); + if (ifirst < 0) ifirst = 0; + if (ilast <= 0) ilast = n - 1; + if (ifirst >= ilast) + return ERROR_INT("ifirst not < ilast", procName, 1); + nc = ilast - ifirst + 1; + + /* Set up an array corresponding to the (shifted) windows, + * and fill in the crossings. */ + score = 0.0; + numaGetFValue(nas, ifirst, &xfirst); + numaGetFValue(nas, ilast, &xlast); + nw = (l_int32) ((xlast - xfirst + 2.0 * width) / width); + nad = numaCreate(nw); + numaSetCount(nad, nw); /* init to all 0.0 */ + xleft = xfirst - width / 2.0 + shift; /* left edge of first window */ + for (i = ifirst; i <= ilast; i++) { + numaGetFValue(nas, i, &xc); + iw = (l_int32)((xc - xleft) / width); + xwc = xleft + (iw + 0.5) * width; /* center of cell iw */ + score += (xwc - xc) * (xwc - xc); + numaGetIValue(nad, iw, &ival); + numaSetValue(nad, iw, ival + 1); + } + + if (pscore) + *pscore = 4.0 * score / (width * width * (l_float32)nc); + if (pnad) + *pnad = nad; + else + numaDestroy(&nad); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.h b/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.h new file mode 100644 index 0000000..358ff4e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/readbarcode.h @@ -0,0 +1,239 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_READBARCODE_H +#define LEPTONICA_READBARCODE_H + + /* ----------------------------------------------------------------- * + * Flags for method of extracting barcode widths * + * ----------------------------------------------------------------- */ + +/*! Barcode Method */ +enum { + L_USE_WIDTHS = 1, /*!< use histogram of barcode widths */ + L_USE_WINDOWS = 2 /*!< find best window for decoding transitions */ +}; + + /* ----------------------------------------------------------------- * + * Flags for barcode formats * + * These are used both to identify a barcode format and to identify * + * the decoding method to use on a barcode. * + * ----------------------------------------------------------------- */ + +/*! Barcode Format */ +enum { + L_BF_UNKNOWN = 0, /*!< unknown format */ + L_BF_ANY = 1, /*!< try decoding with all known formats */ + L_BF_CODE128 = 2, /*!< decode with Code128 format */ + L_BF_EAN8 = 3, /*!< decode with EAN8 format */ + L_BF_EAN13 = 4, /*!< decode with EAN13 format */ + L_BF_CODE2OF5 = 5, /*!< decode with Code 2 of 5 format */ + L_BF_CODEI2OF5 = 6, /*!< decode with Interleaved 2 of 5 format */ + L_BF_CODE39 = 7, /*!< decode with Code39 format */ + L_BF_CODE93 = 8, /*!< decode with Code93 format */ + L_BF_CODABAR = 9, /*!< decode with Code93 format */ + L_BF_UPCA = 10 /*!< decode with UPC A format */ +}; + + /* ----------------------------------------------------------------- * + * Currently supported formats * + * Update these arrays as new formats are added. * + * ----------------------------------------------------------------- */ + +/*! Currently supported formats */ +static const l_int32 SupportedBarcodeFormat[] = { + L_BF_CODE2OF5, + L_BF_CODEI2OF5, + L_BF_CODE93, + L_BF_CODE39, + L_BF_CODABAR, + L_BF_UPCA, + L_BF_EAN13 +}; + +/*! Currently supported format names */ +static const char *SupportedBarcodeFormatName[] = { + "Code2of5", + "CodeI2of5", + "Code93", + "Code39", + "Codabar", + "Upca", + "Ean13" +}; +static const l_int32 NumSupportedBarcodeFormats = 7; /*!< Number of formats */ + + + /* ----------------------------------------------------------------- * + * Code 2 of 5 symbology * + * ----------------------------------------------------------------- */ +static const char *Code2of5[] = { + "111121211", "211111112", "112111112", "212111111", /* 0 - 3 */ + "111121112", "211121111", "112121111", "111111212", /* 4 - 7 */ + "211111211", "112111211", /* 8 - 9 */ + "21211", "21112" /* Start, Stop */ +}; + +static const l_int32 C25_START = 10; +static const l_int32 C25_STOP = 11; + + + /* ----------------------------------------------------------------- * + * Code Interleaved 2 of 5 symbology * + * ----------------------------------------------------------------- */ +static const char *CodeI2of5[] = { + "11221", "21112", "12112", "22111", "11212", /* 0 - 4 */ + "21211", "12211", "11122", "21121", "12121", /* 5 - 9 */ + "1111", "211" /* start, stop */ +}; + +static const l_int32 CI25_START = 10; +static const l_int32 CI25_STOP = 11; + + + /* ----------------------------------------------------------------- * + * Code 93 symbology * + * ----------------------------------------------------------------- */ +static const char *Code93[] = { + "131112", "111213", "111312", "111411", "121113", /* 0: 0 - 4 */ + "121212", "121311", "111114", "131211", "141111", /* 5: 5 - 9 */ + "211113", "211212", "211311", "221112", "221211", /* 10: A - E */ + "231111", "112113", "112212", "112311", "122112", /* 15: F - J */ + "132111", "111123", "111222", "111321", "121122", /* 20: K - O */ + "131121", "212112", "212211", "211122", "211221", /* 25: P - T */ + "221121", "222111", "112122", "112221", "122121", /* 30: U - Y */ + "123111", "121131", "311112", "311211", "321111", /* 35: Z,-,.,SP,$ */ + "112131", "113121", "211131", "131221", "312111", /* 40: /,+,%,($),(%) */ + "311121", "122211", "111141" /* 45: (/),(+), Start */ +}; + + /* Use "[]{}#" to represent special codes 43-47 */ +static const char Code93Val[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%[]{}#"; + +static const l_int32 C93_START = 47; +static const l_int32 C93_STOP = 47; + + + /* ----------------------------------------------------------------- * + * Code 39 symbology * + * ----------------------------------------------------------------- */ +static const char *Code39[] = { + "111221211", "211211112", "112211112", "212211111", /* 0: 0 - 3 */ + "111221112", "211221111", "112221111", "111211212", /* 4: 4 - 7 */ + "211211211", "112211211", "211112112", "112112112", /* 8: 8 - B */ + "212112111", "111122112", "211122111", "112122111", /* 12: C - F */ + "111112212", "211112211", "112112211", "111122211", /* 16: G - J */ + "211111122", "112111122", "212111121", "111121122", /* 20: K - N */ + "211121121", "112121121", "111111222", "211111221", /* 24: O - R */ + "112111221", "111121221", "221111112", "122111112", /* 28: S - V */ + "222111111", "121121112", "221121111", "122121111", /* 32: W - Z */ + "121111212", "221111211", "122111211", "121212111", /* 36: -,.,SP,$ */ + "121211121", "121112121", "111212121", "121121211" /* 40: /,+,%,* */ +}; + + /* Use "*" to represent the Start and Stop codes (43) */ +static const char Code39Val[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; + +static const l_int32 C39_START = 43; +static const l_int32 C39_STOP = 43; + + + /* ----------------------------------------------------------------- * + * Codabar symbology * + * ----------------------------------------------------------------- */ +static const char *Codabar[] = { + "1111122", "1111221", "1112112", "2211111", "1121121", /* 0: 0 - 4 */ + "2111121", "1211112", "1211211", "1221111", "2112111", /* 5: 5 - 9 */ + "1112211", "1122111", "2111212", "2121112", "2121211", /* 10: -,$,:,/,. */ + "1121212", "1122121", "1212112", "1112122", "1112221" /* 15: +,A,B,C,D */ +}; + + /* Ascii representations for codes 16-19: (A or T), (B or N), (C or *), + * (D or E). These are used in pairs for the Start and Stop codes. */ +static const char CodabarVal[] = "0123456789-$:/.+ABCD"; + + + /* ----------------------------------------------------------------- * + * UPC-A symbology * + * ----------------------------------------------------------------- */ +static const char *Upca[] = { + "3211", "2221", "2122", "1411", "1132", /* 0: 0 - 4 */ + "1231", "1114", "1312", "1213", "3112", /* 5: 5 - 9 */ + "111", "111", "11111" /* 10: Start, Stop, Mid */ +}; + +static const l_int32 UPCA_START = 10; +static const l_int32 UPCA_STOP = 11; +static const l_int32 UPCA_MID = 12; + + + /* ----------------------------------------------------------------- * + * Code128 symbology * + * ----------------------------------------------------------------- */ +static const char *Code128[] = { + "212222", "222122", "222221", "121223", "121322", /* 0 - 4 */ + "131222", "122213", "122312", "132212", "221213", /* 5 - 9 */ + "221312", "231212", "112232", "122132", "122231", /* 10 - 14 */ + "113222", "123122", "123221", "223211", "221132", /* 15 - 19 */ + "221231", "213212", "223112", "312131", "311222", /* 20 - 24 */ + "321122", "321221", "312212", "322112", "322211", /* 25 - 29 */ + "212123", "212321", "232121", "111323", "131123", /* 30 - 34 */ + "131321", "112313", "132113", "132311", "211313", /* 35 - 39 */ + "231113", "231311", "112133", "112331", "132131", /* 40 - 44 */ + "113123", "113321", "133121", "313121", "211331", /* 45 - 49 */ + "231131", "213113", "213311", "213131", "311123", /* 50 - 54 */ + "311321", "331121", "312113", "312311", "332111", /* 55 - 59 */ + "314111", "221411", "431111", "111224", "111422", /* 60 - 64 */ + "121124", "121421", "141122", "141221", "112214", /* 65 - 69 */ + "112412", "122114", "122411", "142112", "142211", /* 70 - 74 */ + "241211", "221114", "413111", "241112", "134111", /* 75 - 79 */ + "111242", "121142", "121241", "114212", "124112", /* 80 - 84 */ + "124211", "411212", "421112", "421211", "212141", /* 85 - 89 */ + "214121", "412121", "111143", "111341", "131141", /* 90 - 94 */ + "114113", "114311", "411113", "411311", "113141", /* 95 - 99 */ + "114131", "311141", "411131", "211412", "211214", /* 100 - 104 */ + "211232", "2331112" /* 105 - 106 */ +}; + +static const l_int32 C128_FUN_3 = 96; /* in A or B only; in C it is 96 */ +static const l_int32 C128_FUNC_2 = 97; /* in A or B only; in C it is 97 */ +static const l_int32 C128_SHIFT = 98; /* in A or B only; in C it is 98 */ +static const l_int32 C128_GOTO_C = 99; /* in A or B only; in C it is 99 */ +static const l_int32 C128_GOTO_B = 100; +static const l_int32 C128_GOTO_A = 101; +static const l_int32 C128_FUNC_1 = 102; +static const l_int32 C128_START_A = 103; +static const l_int32 C128_START_B = 104; +static const l_int32 C128_START_C = 105; +static const l_int32 C128_STOP = 106; + /* code 128 symbols are 11 units */ +static const l_int32 C128_SYMBOL_WIDTH = 11; + + + +#endif /* LEPTONICA_READBARCODE_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/readfile.c b/hgdriver/3rdparty/hgOCR/leptonica/readfile.c new file mode 100644 index 0000000..53dda86 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/readfile.c @@ -0,0 +1,1627 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file readfile.c: reads image on file into memory + *
+ *
+ *      Top-level functions for reading images from file
+ *           PIXA      *pixaReadFiles()
+ *           PIXA      *pixaReadFilesSA()
+ *           PIX       *pixRead()
+ *           PIX       *pixReadWithHint()
+ *           PIX       *pixReadIndexed()
+ *           PIX       *pixReadStream()
+ *
+ *      Read header information from file
+ *           l_int32    pixReadHeader()
+ *
+ *      Format finders
+ *           l_int32    findFileFormat()
+ *           l_int32    findFileFormatStream()
+ *           l_int32    findFileFormatBuffer()
+ *           l_int32    fileFormatIsTiff()
+ *
+ *      Read from memory
+ *           PIX       *pixReadMem()
+ *           l_int32    pixReadHeaderMem()
+ *
+ *      Output image file information
+ *           void       writeImageFileInfo()
+ *
+ *      Test function for I/O with different formats
+ *           l_int32    ioFormatTest()
+ *
+ *  Supported file formats:
+ *  (1) Reading is supported without any external libraries:
+ *          bmp
+ *          pnm   (including pbm, pgm, etc)
+ *          spix  (raw serialized)
+ *  (2) Reading is supported with installation of external libraries:
+ *          png
+ *          jpg   (standard jfif version)
+ *          tiff  (including most varieties of compression)
+ *          gif
+ *          webp
+ *          jp2 (jpeg 2000)
+ *  (3) Other file types will get an "unknown format" error.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "allheaders.h" + + /* Output files for ioFormatTest(). */ +static const char *FILE_BMP = "/tmp/lept/format/file.bmp"; +static const char *FILE_PNG = "/tmp/lept/format/file.png"; +static const char *FILE_PNM = "/tmp/lept/format/file.pnm"; +static const char *FILE_G3 = "/tmp/lept/format/file_g3.tif"; +static const char *FILE_G4 = "/tmp/lept/format/file_g4.tif"; +static const char *FILE_RLE = "/tmp/lept/format/file_rle.tif"; +static const char *FILE_PB = "/tmp/lept/format/file_packbits.tif"; +static const char *FILE_LZW = "/tmp/lept/format/file_lzw.tif"; +static const char *FILE_ZIP = "/tmp/lept/format/file_zip.tif"; +static const char *FILE_TIFF_JPEG = "/tmp/lept/format/file_jpeg.tif"; +static const char *FILE_TIFF = "/tmp/lept/format/file.tif"; +static const char *FILE_JPG = "/tmp/lept/format/file.jpg"; +static const char *FILE_GIF = "/tmp/lept/format/file.gif"; +static const char *FILE_WEBP = "/tmp/lept/format/file.webp"; +static const char *FILE_JP2K = "/tmp/lept/format/file.jp2"; + +static const unsigned char JP2K_CODESTREAM[4] = { 0xff, 0x4f, 0xff, 0x51 }; +static const unsigned char JP2K_IMAGE_DATA[12] = { 0x00, 0x00, 0x00, 0x0C, + 0x6A, 0x50, 0x20, 0x20, + 0x0D, 0x0A, 0x87, 0x0A }; + + +/*---------------------------------------------------------------------* + * Top-level functions for reading images from file * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaReadFiles() + * + * \param[in] dirname + * \param[in] substr [optional] substring filter on filenames; can be null + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) %dirname is the full path for the directory.
+ *      (2) %substr is the part of the file name (excluding
+ *          the directory) that is to be matched.  All matching
+ *          filenames are read into the Pixa.  If substr is NULL,
+ *          all filenames are read into the Pixa.
+ * 
+ */ +PIXA * +pixaReadFiles(const char *dirname, + const char *substr) +{ +PIXA *pixa; +SARRAY *sa; + + PROCNAME("pixaReadFiles"); + + if (!dirname) + return (PIXA *)ERROR_PTR("dirname not defined", procName, NULL); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return (PIXA *)ERROR_PTR("sa not made", procName, NULL); + + pixa = pixaReadFilesSA(sa); + sarrayDestroy(&sa); + return pixa; +} + + +/*! + * \brief pixaReadFilesSA() + * + * \param[in] sa full pathnames for all files + * \return pixa, or NULL on error + */ +PIXA * +pixaReadFilesSA(SARRAY *sa) +{ +char *str; +l_int32 i, n; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixaReadFilesSA"); + + if (!sa) + return (PIXA *)ERROR_PTR("sa not defined", procName, NULL); + + n = sarrayGetCount(sa); + pixa = pixaCreate(n); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + if ((pix = pixRead(str)) == NULL) { + L_WARNING("pix not read from file %s\n", procName, str); + continue; + } + pixaAddPix(pixa, pix, L_INSERT); + } + + return pixa; +} + + +/*! + * \brief pixRead() + * + * \param[in] filename with full pathname or in local directory + * \return pix if OK; NULL on error + * + *
+ * Notes:
+ *      (1) See at top of file for supported formats.
+ * 
+ */ +PIX * +pixRead(const char *filename) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixRead"); + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) { + L_ERROR("image file not found: %s\n", procName, filename); + return NULL; + } + pix = pixReadStream(fp, 0); + fclose(fp); + if (!pix) + return (PIX *)ERROR_PTR("pix not read", procName, NULL); + return pix; +} + + +/*! + * \brief pixReadWithHint() + * + * \param[in] filename with full pathname or in local directory + * \param[in] hint bitwise OR of L_HINT_* values for jpeg; + * use 0 for no hint + * \return pix if OK; NULL on error + * + *
+ * Notes:
+ *      (1) The hint is not binding, but may be used to optimize jpeg decoding.
+ *          Use 0 for no hinting.
+ * 
+ */ +PIX * +pixReadWithHint(const char *filename, + l_int32 hint) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadWithHint"); + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX *)ERROR_PTR("image file not found", procName, NULL); + pix = pixReadStream(fp, hint); + fclose(fp); + + if (!pix) + return (PIX *)ERROR_PTR("image not returned", procName, NULL); + return pix; +} + + +/*! + * \brief pixReadIndexed() + * + * \param[in] sa string array of full pathnames + * \param[in] index into pathname array + * \return pix if OK; null if not found + * + *
+ * Notes:
+ *      (1) This function is useful for selecting image files from a
+ *          directory, where the integer %index is embedded into
+ *          the file name.
+ *      (2) This is typically done by generating the sarray using
+ *          getNumberedPathnamesInDirectory(), so that the %index
+ *          pathname would have the number %index in it.  The size
+ *          of the sarray should be the largest number (plus 1) appearing
+ *          in the file names, respecting the constraints in the
+ *          call to getNumberedPathnamesInDirectory().
+ *      (3) Consequently, for some indices into the sarray, there may
+ *          be no pathnames in the directory containing that number.
+ *          By convention, we place empty C strings ("") in those
+ *          locations in the sarray, and it is not an error if such
+ *          a string is encountered and no pix is returned.
+ *          Therefore, the caller must verify that a pix is returned.
+ *      (4) See convertSegmentedPagesToPS() in src/psio1.c for an
+ *          example of usage.
+ * 
+ */ +PIX * +pixReadIndexed(SARRAY *sa, + l_int32 index) +{ +char *fname; +l_int32 n; +PIX *pix; + + PROCNAME("pixReadIndexed"); + + if (!sa) + return (PIX *)ERROR_PTR("sa not defined", procName, NULL); + n = sarrayGetCount(sa); + if (index < 0 || index >= n) + return (PIX *)ERROR_PTR("index out of bounds", procName, NULL); + + fname = sarrayGetString(sa, index, L_NOCOPY); + if (fname[0] == '\0') + return NULL; + + if ((pix = pixRead(fname)) == NULL) { + L_ERROR("pix not read from file %s\n", procName, fname); + return NULL; + } + + return pix; +} + + +/*! + * \brief pixReadStream() + * + * \param[in] fp file stream + * \param[in] hint bitwise OR of L_HINT_* values for jpeg; 0 for no hint + * \return pix if OK; NULL on error + * + *
+ * Notes:
+ *      (1) The hint only applies to jpeg.
+ * 
+ */ +PIX * +pixReadStream(FILE *fp, + l_int32 hint) +{ +l_int32 format, ret, valid; +l_uint8 *comment; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixReadStream"); + + if (!fp) + return (PIX *)ERROR_PTR("stream not defined", procName, NULL); + pix = NULL; + + findFileFormatStream(fp, &format); + switch (format) + { + case IFF_BMP: + if ((pix = pixReadStreamBmp(fp)) == NULL ) + return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL); + break; + + case IFF_JFIF_JPEG: + if ((pix = pixReadStreamJpeg(fp, 0, 1, NULL, hint)) == NULL) + return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL); + ret = fgetJpegComment(fp, &comment); + if (!ret && comment) + pixSetText(pix, (char *)comment); + LEPT_FREE(comment); + break; + + case IFF_PNG: + if ((pix = pixReadStreamPng(fp)) == NULL) + return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + if ((pix = pixReadStreamTiff(fp, 0)) == NULL) /* page 0 by default */ + return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL); + break; + + case IFF_PNM: + if ((pix = pixReadStreamPnm(fp)) == NULL) + return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL); + break; + + case IFF_GIF: + if ((pix = pixReadStreamGif(fp)) == NULL) + return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL); + break; + + case IFF_JP2: + if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0, 0)) == NULL) + return (PIX *)ERROR_PTR("jp2: no pix returned", procName, NULL); + break; + + case IFF_WEBP: + if ((pix = pixReadStreamWebP(fp)) == NULL) + return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL); + break; + + case IFF_PS: + L_ERROR("PostScript reading is not supported\n", procName); + return NULL; + + case IFF_LPDF: + L_ERROR("Pdf reading is not supported\n", procName); + return NULL; + + case IFF_SPIX: + if ((pix = pixReadStreamSpix(fp)) == NULL) + return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL); + break; + + case IFF_UNKNOWN: + return (PIX *)ERROR_PTR( "Unknown format: no pix returned", + procName, NULL); + break; + } + + if (pix) { + pixSetInputFormat(pix, format); + if ((cmap = pixGetColormap(pix))) { + pixcmapIsValid(cmap, &valid); + if (!valid) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("invalid colormap", procName, NULL); + } + } + } + return pix; +} + + + +/*---------------------------------------------------------------------* + * Read header information from file * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadHeader() + * + * \param[in] filename with full pathname or in local directory + * \param[out] pformat [optional] file format + * \param[out] pw, ph [optional] width and height + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel 1, 3 or 4 + * \param[out] piscmap [optional] 1 if cmap exists; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This reads the actual headers for jpeg, png, tiff and pnm.
+ *          For bmp and gif, we cheat and read the entire file into a pix,
+ *          from which we extract the "header" information.
+ * 
+ */ +l_ok +pixReadHeader(const char *filename, + l_int32 *pformat, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 format, ret, w, h, d, bps, spp, iscmap; +l_int32 type; /* ignored */ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadHeader"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (pformat) *pformat = 0; + iscmap = 0; /* init to false */ + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + findFileFormatStream(fp, &format); + fclose(fp); + + switch (format) + { + case IFF_BMP: /* cheating: reading the entire file */ + if ((pix = pixRead(filename)) == NULL) + return ERROR_INT( "bmp: pix not read", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (pixGetColormap(pix)) + iscmap = 1; + pixDestroy(&pix); + bps = (d == 32) ? 8 : d; + spp = (d == 32) ? 3 : 1; + break; + + case IFF_JFIF_JPEG: + ret = readHeaderJpeg(filename, &w, &h, &spp, NULL, NULL); + bps = 8; + if (ret) + return ERROR_INT( "jpeg: no header info returned", procName, 1); + break; + + case IFF_PNG: + ret = readHeaderPng(filename, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT( "png: no header info returned", procName, 1); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + /* Reading page 0 by default; possibly redefine format */ + ret = readHeaderTiff(filename, 0, &w, &h, &bps, &spp, NULL, &iscmap, + &format); + if (ret) + return ERROR_INT( "tiff: no header info returned", procName, 1); + break; + + case IFF_PNM: + ret = readHeaderPnm(filename, &w, &h, &d, &type, &bps, &spp); + if (ret) + return ERROR_INT( "pnm: no header info returned", procName, 1); + break; + + case IFF_GIF: /* cheating: reading the entire file */ + if ((pix = pixRead(filename)) == NULL) + return ERROR_INT( "gif: pix not read", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + iscmap = 1; /* always colormapped; max 256 colors */ + spp = 1; + bps = d; + break; + + case IFF_JP2: + ret = readHeaderJp2k(filename, &w, &h, &bps, &spp); + break; + + case IFF_WEBP: + if (readHeaderWebP(filename, &w, &h, &spp)) + return ERROR_INT( "webp: no header info returned", procName, 1); + bps = 8; + break; + + case IFF_PS: + if (pformat) *pformat = format; + return ERROR_INT("PostScript reading is not supported\n", procName, 1); + + case IFF_LPDF: + if (pformat) *pformat = format; + return ERROR_INT("Pdf reading is not supported\n", procName, 1); + + case IFF_SPIX: + ret = readHeaderSpix(filename, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT( "spix: no header info returned", procName, 1); + break; + + case IFF_UNKNOWN: + L_ERROR("unknown format in file %s\n", procName, filename); + return 1; + break; + } + + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (piscmap) *piscmap = iscmap; + if (pformat) *pformat = format; + return 0; +} + + +/*---------------------------------------------------------------------* + * Format finders * + *---------------------------------------------------------------------*/ +/*! + * \brief findFileFormat() + * + * \param[in] filename + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + */ +l_ok +findFileFormat(const char *filename, + l_int32 *pformat) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("findFileFormat"); + + if (!pformat) + return ERROR_INT("&format not defined", procName, 1); + *pformat = IFF_UNKNOWN; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = findFileFormatStream(fp, pformat); + fclose(fp); + return ret; +} + + +/*! + * \brief findFileFormatStream() + * + * \param[in] fp file stream + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + * + *
+ * Notes:
+ *      (1) Important: Side effect -- this resets fp to BOF.
+ * 
+ */ +l_ok +findFileFormatStream(FILE *fp, + l_int32 *pformat) +{ +l_uint8 firstbytes[12]; +l_int32 format; + + PROCNAME("findFileFormatStream"); + + if (!pformat) + return ERROR_INT("&format not defined", procName, 1); + *pformat = IFF_UNKNOWN; + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + + rewind(fp); + if (fnbytesInFile(fp) < 12) + return ERROR_INT("truncated file", procName, 1); + + if (fread(&firstbytes, 1, 12, fp) != 12) + return ERROR_INT("failed to read first 12 bytes of file", procName, 1); + rewind(fp); + + findFileFormatBuffer(firstbytes, &format); + if (format == IFF_TIFF) { + findTiffCompression(fp, &format); + rewind(fp); + } + *pformat = format; + if (format == IFF_UNKNOWN) + return 1; + else + return 0; +} + + +/*! + * \brief findFileFormatBuffer() + * + * \param[in] buf byte buffer at least 12 bytes in size; we can't check + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + * + *
+ * Notes:
+ *      (1) This determines the file format from the first 12 bytes in
+ *          the compressed data stream, which are stored in memory.
+ *      (2) For tiff files, this returns IFF_TIFF.  The specific tiff
+ *          compression is then determined using findTiffCompression().
+ * 
+ */ +l_ok +findFileFormatBuffer(const l_uint8 *buf, + l_int32 *pformat) +{ +l_uint16 twobytepw; + + PROCNAME("findFileFormatBuffer"); + + if (!pformat) + return ERROR_INT("&format not defined", procName, 1); + *pformat = IFF_UNKNOWN; + if (!buf) + return ERROR_INT("byte buffer not defined", procName, 0); + + /* Check the bmp and tiff 2-byte header ids */ + ((char *)(&twobytepw))[0] = buf[0]; + ((char *)(&twobytepw))[1] = buf[1]; + + if (convertOnBigEnd16(twobytepw) == BMP_ID) { + *pformat = IFF_BMP; + return 0; + } + + if (twobytepw == TIFF_BIGEND_ID || twobytepw == TIFF_LITTLEEND_ID) { + *pformat = IFF_TIFF; + return 0; + } + + /* Check for the p*m 2-byte header ids */ + if ((buf[0] == 'P' && buf[1] == '4') || /* newer packed */ + (buf[0] == 'P' && buf[1] == '1')) { /* old ASCII format */ + *pformat = IFF_PNM; + return 0; + } + + if ((buf[0] == 'P' && buf[1] == '5') || /* newer */ + (buf[0] == 'P' && buf[1] == '2')) { /* old */ + *pformat = IFF_PNM; + return 0; + } + + if ((buf[0] == 'P' && buf[1] == '6') || /* newer */ + (buf[0] == 'P' && buf[1] == '3')) { /* old */ + *pformat = IFF_PNM; + return 0; + } + + if (buf[0] == 'P' && buf[1] == '7') { /* new arbitrary (PAM) */ + *pformat = IFF_PNM; + return 0; + } + + /* Consider the first 11 bytes of the standard JFIF JPEG header: + * - The first two bytes are the most important: 0xffd8. + * - The next two bytes are the jfif marker: 0xffe0. + * Not all jpeg files have this marker. + * - The next two bytes are the header length. + * - The next 5 bytes are a null-terminated string. + * For JFIF, the string is "JFIF", naturally. For others it + * can be "Exif" or just about anything else. + * - Because of all this variability, we only check the first + * two byte marker. All jpeg files are identified as + * IFF_JFIF_JPEG. */ + if (buf[0] == 0xff && buf[1] == 0xd8) { + *pformat = IFF_JFIF_JPEG; + return 0; + } + + /* Check for the 8 byte PNG signature (png_signature in png.c): + * {137, 80, 78, 71, 13, 10, 26, 10} */ + if (buf[0] == 137 && buf[1] == 80 && buf[2] == 78 && buf[3] == 71 && + buf[4] == 13 && buf[5] == 10 && buf[6] == 26 && buf[7] == 10) { + *pformat = IFF_PNG; + return 0; + } + + /* Look for "GIF87a" or "GIF89a" */ + if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == '8' && + (buf[4] == '7' || buf[4] == '9') && buf[5] == 'a') { + *pformat = IFF_GIF; + return 0; + } + + /* Check for both types of jp2k file */ + if (memcmp(buf, JP2K_CODESTREAM, 4) == 0 || + memcmp(buf, JP2K_IMAGE_DATA, 12) == 0) { + *pformat = IFF_JP2; + return 0; + } + + /* Check for webp */ + if (buf[0] == 'R' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == 'F' && + buf[8] == 'W' && buf[9] == 'E' && buf[10] == 'B' && buf[11] == 'P') { + *pformat = IFF_WEBP; + return 0; + } + + /* Check for ps */ + if (buf[0] == '%' && buf[1] == '!' && buf[2] == 'P' && buf[3] == 'S' && + buf[4] == '-' && buf[5] == 'A' && buf[6] == 'd' && buf[7] == 'o' && + buf[8] == 'b' && buf[9] == 'e') { + *pformat = IFF_PS; + return 0; + } + + /* Check for pdf */ + if (buf[0] == '%' && buf[1] == 'P' && buf[2] == 'D' && buf[3] == 'F' && + buf[4] == '-' && buf[5] == '1') { + *pformat = IFF_LPDF; + return 0; + } + + /* Check for "spix" serialized pix */ + if (buf[0] == 's' && buf[1] == 'p' && buf[2] == 'i' && buf[3] == 'x') { + *pformat = IFF_SPIX; + return 0; + } + + /* File format identifier not found; unknown */ + return 1; +} + + +/*! + * \brief fileFormatIsTiff() + * + * \param[in] fp file stream + * \return 1 if file is tiff; 0 otherwise or on error + */ +l_int32 +fileFormatIsTiff(FILE *fp) +{ +l_int32 format; + + PROCNAME("fileFormatIsTiff"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 0); + + findFileFormatStream(fp, &format); + if (format == IFF_TIFF || format == IFF_TIFF_PACKBITS || + format == IFF_TIFF_RLE || format == IFF_TIFF_G3 || + format == IFF_TIFF_G4 || format == IFF_TIFF_LZW || + format == IFF_TIFF_ZIP || format == IFF_TIFF_JPEG) + return 1; + else + return 0; +} + + +/*---------------------------------------------------------------------* + * Read from memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadMem() + * + * \param[in] data const; encoded + * \param[in] size size of data + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a variation of pixReadStream(), where the data is read
+ *          from a memory buffer rather than a file.
+ *      (2) On windows, this only reads tiff formatted files directly from
+ *          memory.  For other formats, it writes to a temp file and
+ *          decompresses from file.
+ *      (3) findFileFormatBuffer() requires up to 12 bytes to decide on
+ *          the format.  That determines the constraint here.  But in
+ *          fact the data must contain the entire compressed string for
+ *          the image.
+ * 
+ */ +PIX * +pixReadMem(const l_uint8 *data, + size_t size) +{ +l_int32 format, valid; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixReadMem"); + + if (!data) + return (PIX *)ERROR_PTR("data not defined", procName, NULL); + if (size < 12) + return (PIX *)ERROR_PTR("size < 12", procName, NULL); + pix = NULL; + + findFileFormatBuffer(data, &format); + switch (format) + { + case IFF_BMP: + if ((pix = pixReadMemBmp(data, size)) == NULL ) + return (PIX *)ERROR_PTR( "bmp: no pix returned", procName, NULL); + break; + + case IFF_JFIF_JPEG: + if ((pix = pixReadMemJpeg(data, size, 0, 1, NULL, 0)) == NULL) + return (PIX *)ERROR_PTR( "jpeg: no pix returned", procName, NULL); + break; + + case IFF_PNG: + if ((pix = pixReadMemPng(data, size)) == NULL) + return (PIX *)ERROR_PTR("png: no pix returned", procName, NULL); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + /* Reading page 0 by default */ + if ((pix = pixReadMemTiff(data, size, 0)) == NULL) + return (PIX *)ERROR_PTR("tiff: no pix returned", procName, NULL); + break; + + case IFF_PNM: + if ((pix = pixReadMemPnm(data, size)) == NULL) + return (PIX *)ERROR_PTR("pnm: no pix returned", procName, NULL); + break; + + case IFF_GIF: + if ((pix = pixReadMemGif(data, size)) == NULL) + return (PIX *)ERROR_PTR("gif: no pix returned", procName, NULL); + break; + + case IFF_JP2: + if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0, 0)) == NULL) + return (PIX *)ERROR_PTR("jp2k: no pix returned", procName, NULL); + break; + + case IFF_WEBP: + if ((pix = pixReadMemWebP(data, size)) == NULL) + return (PIX *)ERROR_PTR("webp: no pix returned", procName, NULL); + break; + + case IFF_PS: + L_ERROR("PostScript reading is not supported\n", procName); + return NULL; + + case IFF_LPDF: + L_ERROR("Pdf reading is not supported\n", procName); + return NULL; + + case IFF_SPIX: + if ((pix = pixReadMemSpix(data, size)) == NULL) + return (PIX *)ERROR_PTR("spix: no pix returned", procName, NULL); + break; + + case IFF_UNKNOWN: + return (PIX *)ERROR_PTR("Unknown format: no pix returned", + procName, NULL); + break; + } + + /* Set the input format. For tiff reading from memory we lose + * the actual input format; for 1 bpp, default to G4. Also + * verify that the colormap is valid. */ + if (pix) { + if (format == IFF_TIFF && pixGetDepth(pix) == 1) + format = IFF_TIFF_G4; + pixSetInputFormat(pix, format); + if ((cmap = pixGetColormap(pix))) { + pixcmapIsValid(cmap, &valid); + if (!valid) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("invalid colormap", procName, NULL); + } + } + pixSetPadBits(pix, 0); + } + return pix; +} + + +/*! + * \brief pixReadHeaderMem() + * + * \param[in] data const; encoded + * \param[in] size size of data + * \param[out] pformat [optional] image format + * \param[out] pw, ph [optional] width and height + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel 1, 3 or 4 + * \param[out] piscmap [optional] 1 if cmap exists; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This reads the actual headers for jpeg, png, tiff, jp2k and pnm.
+ *          For bmp and gif, we cheat and read all the data into a pix,
+ *          from which we extract the "header" information.
+ *      (2) The amount of data required depends on the format.  For
+ *          png, it requires less than 30 bytes, but for jpeg it can
+ *          require most of the compressed file.  In practice, the data
+ *          is typically the entire compressed file in memory.
+ *      (3) findFileFormatBuffer() requires up to 8 bytes to decide on
+ *          the format, which we require.
+ * 
+ */ +l_ok +pixReadHeaderMem(const l_uint8 *data, + size_t size, + l_int32 *pformat, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 format, ret, w, h, d, bps, spp, iscmap; +l_int32 type; /* not used */ +PIX *pix; + + PROCNAME("pixReadHeaderMem"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (pformat) *pformat = 0; + iscmap = 0; /* init to false */ + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (size < 8) + return ERROR_INT("size < 8", procName, 1); + + findFileFormatBuffer(data, &format); + + switch (format) + { + case IFF_BMP: /* cheating: read the pix */ + if ((pix = pixReadMemBmp(data, size)) == NULL) + return ERROR_INT( "bmp: pix not read", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + bps = (d == 32) ? 8 : d; + spp = (d == 32) ? 3 : 1; + break; + + case IFF_JFIF_JPEG: + ret = readHeaderMemJpeg(data, size, &w, &h, &spp, NULL, NULL); + bps = 8; + if (ret) + return ERROR_INT( "jpeg: no header info returned", procName, 1); + break; + + case IFF_PNG: + ret = readHeaderMemPng(data, size, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT( "png: no header info returned", procName, 1); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + /* Reading page 0 by default; possibly redefine format */ + ret = readHeaderMemTiff(data, size, 0, &w, &h, &bps, &spp, + NULL, &iscmap, &format); + if (ret) + return ERROR_INT( "tiff: no header info returned", procName, 1); + break; + + case IFF_PNM: + ret = readHeaderMemPnm(data, size, &w, &h, &d, &type, &bps, &spp); + if (ret) + return ERROR_INT( "pnm: no header info returned", procName, 1); + break; + + case IFF_GIF: /* cheating: read the pix */ + if ((pix = pixReadMemGif(data, size)) == NULL) + return ERROR_INT( "gif: pix not read", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + iscmap = 1; /* always colormapped; max 256 colors */ + spp = 1; + bps = d; + break; + + case IFF_JP2: + ret = readHeaderMemJp2k(data, size, &w, &h, &bps, &spp); + break; + + case IFF_WEBP: + bps = 8; + ret = readHeaderMemWebP(data, size, &w, &h, &spp); + break; + + case IFF_PS: + if (pformat) *pformat = format; + return ERROR_INT("PostScript reading is not supported\n", procName, 1); + + case IFF_LPDF: + if (pformat) *pformat = format; + return ERROR_INT("Pdf reading is not supported\n", procName, 1); + + case IFF_SPIX: + ret = sreadHeaderSpix((l_uint32 *)data, &w, &h, &bps, + &spp, &iscmap); + if (ret) + return ERROR_INT( "pnm: no header info returned", procName, 1); + break; + + case IFF_UNKNOWN: + return ERROR_INT("unknown format; no data returned", procName, 1); + break; + } + + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (piscmap) *piscmap = iscmap; + if (pformat) *pformat = format; + return 0; +} + + +/*---------------------------------------------------------------------* + * Output image file information * + *---------------------------------------------------------------------*/ +extern const char *ImageFileFormatExtensions[]; + +/*! + * \brief writeImageFileInfo() + * + * \param[in] filename input file + * \param[in] fpout output file stream + * \param[in] headeronly 1 to read only the header; 0 to read both + * the header and the input file + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If headeronly == 0 and the image has spp == 4,this will
+ *          also call pixDisplayLayersRGBA() to display the image
+ *          in three views.
+ *      (2) This is a debug function that changes the value of
+ *          var_PNG_STRIP_16_TO_8 to 1 (the default).
+ * 
+ */ +l_ok +writeImageFileInfo(const char *filename, + FILE *fpout, + l_int32 headeronly) +{ +char *text; +l_int32 w, h, d, wpl, count, npages, color; +l_int32 format, bps, spp, iscmap, xres, yres, transparency; +FILE *fpin; +PIX *pix, *pixt; +PIXCMAP *cmap; + + PROCNAME("writeImageFileInfo"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!fpout) + return ERROR_INT("stream not defined", procName, 1); + + /* Read the header */ + if (pixReadHeader(filename, &format, &w, &h, &bps, &spp, &iscmap)) { + L_ERROR("failure to read header of %s\n", procName, filename); + return 1; + } + fprintf(fpout, "===============================================\n" + "Reading the header:\n"); + fprintf(fpout, " input image format type: %s\n", + ImageFileFormatExtensions[format]); + fprintf(fpout, " w = %d, h = %d, bps = %d, spp = %d, iscmap = %d\n", + w, h, bps, spp, iscmap); + + findFileFormat(filename, &format); + if (format == IFF_JP2) { + fpin = lept_fopen(filename, "rb"); + fgetJp2kResolution(fpin, &xres, &yres); + fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + } else if (format == IFF_PNG) { + fpin = lept_fopen(filename, "rb"); + fgetPngResolution(fpin, &xres, &yres); + fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + if (iscmap) { + fpin = lept_fopen(filename, "rb"); + fgetPngColormapInfo(fpin, &cmap, &transparency); + fclose(fpin); + if (transparency) + fprintf(fpout, " colormap has transparency\n"); + else + fprintf(fpout, " colormap does not have transparency\n"); + pixcmapWriteStream(fpout, cmap); + pixcmapDestroy(&cmap); + } + } else if (format == IFF_JFIF_JPEG) { + fpin = lept_fopen(filename, "rb"); + fgetJpegResolution(fpin, &xres, &yres); + fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + } + + if (headeronly) + return 0; + + /* Read the full image. Note that when we read an image that + * has transparency in a colormap, we convert it to RGBA. */ + fprintf(fpout, "===============================================\n" + "Reading the full image:\n"); + + /* Preserve 16 bpp if the format is png */ + if (format == IFF_PNG && bps == 16) + l_pngSetReadStrip16To8(0); + + if ((pix = pixRead(filename)) == NULL) { + L_ERROR("failure to read full image of %s\n", procName, filename); + return 1; + } + + format = pixGetInputFormat(pix); + pixGetDimensions(pix, &w, &h, &d); + wpl = pixGetWpl(pix); + spp = pixGetSpp(pix); + fprintf(fpout, " input image format type: %s\n", + ImageFileFormatExtensions[format]); + fprintf(fpout, " w = %d, h = %d, d = %d, spp = %d, wpl = %d\n", + w, h, d, spp, wpl); + fprintf(fpout, " xres = %d, yres = %d\n", + pixGetXRes(pix), pixGetYRes(pix)); + + text = pixGetText(pix); + if (text) /* not null */ + fprintf(fpout, " text: %s\n", text); + + cmap = pixGetColormap(pix); + if (cmap) { + pixcmapHasColor(cmap, &color); + if (color) + fprintf(fpout, " colormap exists and has color values:"); + else + fprintf(fpout, " colormap exists and has only gray values:"); + pixcmapWriteStream(fpout, pixGetColormap(pix)); + } + else + fprintf(fpout, " colormap does not exist\n"); + + if (format == IFF_TIFF || format == IFF_TIFF_G4 || + format == IFF_TIFF_G3 || format == IFF_TIFF_PACKBITS) { + fprintf(fpout, " Tiff header information:\n"); + fpin = lept_fopen(filename, "rb"); + tiffGetCount(fpin, &npages); + lept_fclose(fpin); + if (npages == 1) + fprintf(fpout, " One page in file\n"); + else + fprintf(fpout, " %d pages in file\n", npages); + fprintTiffInfo(fpout, filename); + } + + if (d == 1) { + pixCountPixels(pix, &count, NULL); + pixGetDimensions(pix, &w, &h, NULL); + fprintf(fpout, " 1 bpp: foreground pixel fraction ON/Total = %g\n", + (l_float32)count / (l_float32)(w * h)); + } + fprintf(fpout, "===============================================\n"); + + /* If there is an alpha component, visualize it. Note that when + * alpha == 0, the rgb layer is transparent. We visualize the + * result when a white background is visible through the + * transparency layer. */ + if (pixGetSpp(pix) == 4) { + pixt = pixDisplayLayersRGBA(pix, 0xffffff00, 600.0); + pixDisplay(pixt, 100, 100); + pixDestroy(&pixt); + } + + if (format == IFF_PNG && bps == 16) + l_pngSetReadStrip16To8(1); /* return to default if format is png */ + + pixDestroy(&pix); + return 0; +} + + +/*---------------------------------------------------------------------* + * Test function for I/O with different formats * + *---------------------------------------------------------------------*/ +/*! + * \brief ioFormatTest() + * + * \param[in] filename input image file + * \return 0 if OK; 1 on error or if the test fails + * + *
+ * Notes:
+ *      (1) This writes and reads a set of output files losslessly
+ *          in different formats to /tmp/format/, and tests that the
+ *          result before and after is unchanged.
+ *      (2) This should work properly on input images of any depth,
+ *          with and without colormaps.
+ *      (3) All supported formats are tested for bmp, png, tiff and
+ *          non-ascii pnm.  Ascii pnm also works (but who'd ever want
+ *          to use it?)   We allow 2 bpp bmp, although it's not
+ *          supported elsewhere.  And we don't support reading
+ *          16 bpp png, although this can be turned on in pngio.c.
+ *      (4) This silently skips png or tiff testing if HAVE_LIBPNG
+ *          or HAVE_LIBTIFF are 0, respectively.
+ * 
+ */ +l_ok +ioFormatTest(const char *filename) +{ +l_int32 w, h, d, depth, equal, problems; +l_float32 diff; +BOX *box; +PIX *pixs, *pixc, *pix1, *pix2; +PIXCMAP *cmap; + + PROCNAME("ioFormatTest"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + /* Read the input file and limit the size */ + if ((pix1 = pixRead(filename)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + pixGetDimensions(pix1, &w, &h, NULL); + if (w > 250 && h > 250) { /* take the central 250 x 250 region */ + box = boxCreate(w / 2 - 125, h / 2 - 125, 250, 250); + pixs = pixClipRectangle(pix1, box, NULL); + boxDestroy(&box); + } else { + pixs = pixClone(pix1); + } + pixDestroy(&pix1); + + lept_mkdir("lept/format"); + + /* Note that the reader automatically removes colormaps + * from 1 bpp BMP images, but not from 8 bpp BMP images. + * Therefore, if our 8 bpp image initially doesn't have a + * colormap, we are going to need to remove it from any + * pix read from a BMP file. */ + pixc = pixClone(pixs); /* laziness */ + + /* This does not test the alpha layer pixels, because most + * formats don't support it. Remove any alpha. */ + if (pixGetSpp(pixc) == 4) + pixSetSpp(pixc, 3); + cmap = pixGetColormap(pixc); /* colormap; can be NULL */ + d = pixGetDepth(pixc); + + problems = FALSE; + + /* ----------------------- BMP -------------------------- */ + + /* BMP works for 1, 2, 4, 8 and 32 bpp images. + * It always writes colormaps for 1 and 8 bpp, so we must + * remove it after readback if the input image doesn't have + * a colormap. Although we can write/read 2 bpp BMP, nobody + * else can read them! */ + if (d == 1 || d == 8) { + L_INFO("write/read bmp\n", procName); + pixWrite(FILE_BMP, pixc, IFF_BMP); + pix1 = pixRead(FILE_BMP); + if (!cmap) + pix2 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); + else + pix2 = pixClone(pix1); + pixEqual(pixc, pix2, &equal); + if (!equal) { + L_INFO(" **** bad bmp image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + if (d == 2 || d == 4 || d == 32) { + L_INFO("write/read bmp\n", procName); + pixWrite(FILE_BMP, pixc, IFF_BMP); + pix1 = pixRead(FILE_BMP); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad bmp image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + } + + /* ----------------------- PNG -------------------------- */ +#if HAVE_LIBPNG + /* PNG works for all depths, but here, because we strip + * 16 --> 8 bpp on reading, we don't test png for 16 bpp. */ + if (d != 16) { + L_INFO("write/read png\n", procName); + pixWrite(FILE_PNG, pixc, IFF_PNG); + pix1 = pixRead(FILE_PNG); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad png image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + } +#endif /* HAVE_LIBPNG */ + + /* ----------------------- TIFF -------------------------- */ +#if HAVE_LIBTIFF + /* TIFF works for 1, 2, 4, 8, 16 and 32 bpp images. + * Because 8 bpp tiff always writes 256 entry colormaps, the + * colormap sizes may be different for 8 bpp images with + * colormap; we are testing if the image content is the same. + * Likewise, the 2 and 4 bpp tiff images with colormaps + * have colormap sizes 4 and 16, rsp. This test should + * work properly on the content, regardless of the number + * of color entries in pixc. */ + + /* tiff uncompressed works for all pixel depths */ + L_INFO("write/read uncompressed tiff\n", procName); + pixWrite(FILE_TIFF, pixc, IFF_TIFF); + pix1 = pixRead(FILE_TIFF); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff uncompressed image: d = %d ****\n", + procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff lzw works for all pixel depths */ + L_INFO("write/read lzw compressed tiff\n", procName); + pixWrite(FILE_LZW, pixc, IFF_TIFF_LZW); + pix1 = pixRead(FILE_LZW); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff lzw compressed image: d = %d ****\n", + procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff adobe deflate (zip) works for all pixel depths */ + L_INFO("write/read zip compressed tiff\n", procName); + pixWrite(FILE_ZIP, pixc, IFF_TIFF_ZIP); + pix1 = pixRead(FILE_ZIP); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff zip compressed image: d = %d ****\n", + procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff jpeg encoding works for grayscale and rgb */ + if (d == 8 || d == 32) { + PIX *pixc1; + L_INFO("write/read jpeg compressed tiff\n", procName); + if (d == 8 && pixGetColormap(pixc)) { + pixc1 = pixRemoveColormap(pixc, REMOVE_CMAP_BASED_ON_SRC); + pixWrite(FILE_TIFF_JPEG, pixc1, IFF_TIFF_JPEG); + if ((pix1 = pixRead(FILE_TIFF_JPEG)) == NULL) { + L_INFO(" did not read FILE_TIFF_JPEG\n", procName); + problems = TRUE; + } + pixDestroy(&pixc1); + } else { + pixWrite(FILE_TIFF_JPEG, pixc, IFF_TIFF_JPEG); + pix1 = pixRead(FILE_TIFF_JPEG); + if (d == 8) { + pixCompareGray(pix1, pixc, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pixc, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + if (diff > 8.0) { + L_INFO(" **** bad tiff jpeg compressed image: " + "d = %d, diff = %5.2f ****\n", procName, d, diff); + problems = TRUE; + } + } + pixDestroy(&pix1); + } + + /* tiff g4, g3, rle and packbits work for 1 bpp */ + if (d == 1) { + L_INFO("write/read g4 compressed tiff\n", procName); + pixWrite(FILE_G4, pixc, IFF_TIFF_G4); + pix1 = pixRead(FILE_G4); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff g4 image ****\n", procName); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read g3 compressed tiff\n", procName); + pixWrite(FILE_G3, pixc, IFF_TIFF_G3); + pix1 = pixRead(FILE_G3); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff g3 image ****\n", procName); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read rle compressed tiff\n", procName); + pixWrite(FILE_RLE, pixc, IFF_TIFF_RLE); + pix1 = pixRead(FILE_RLE); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff rle image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read packbits compressed tiff\n", procName); + pixWrite(FILE_PB, pixc, IFF_TIFF_PACKBITS); + pix1 = pixRead(FILE_PB); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff packbits image: d = %d ****\n", + procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + } +#endif /* HAVE_LIBTIFF */ + + /* ----------------------- PNM -------------------------- */ + + /* pnm works for 1, 2, 4, 8, 16 and 32 bpp. + * pnm doesn't have colormaps, so when we write colormapped + * pix out as pnm, the colormap is removed. Thus for the test, + * we must remove the colormap from pixc before testing. */ + L_INFO("write/read pnm\n", procName); + pixWrite(FILE_PNM, pixc, IFF_PNM); + pix1 = pixRead(FILE_PNM); + if (cmap) + pix2 = pixRemoveColormap(pixc, REMOVE_CMAP_BASED_ON_SRC); + else + pix2 = pixClone(pixc); + pixEqual(pix1, pix2, &equal); + if (!equal) { + L_INFO(" **** bad pnm image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* ----------------------- GIF -------------------------- */ +#if HAVE_LIBGIF + /* GIF works for only 1 and 8 bpp, colormapped */ + if (d != 8 || !cmap) + pix1 = pixConvertTo8(pixc, 1); + else + pix1 = pixClone(pixc); + L_INFO("write/read gif\n", procName); + pixWrite(FILE_GIF, pix1, IFF_GIF); + pix2 = pixRead(FILE_GIF); + pixEqual(pix1, pix2, &equal); + if (!equal) { + L_INFO(" **** bad gif image: d = %d ****\n", procName, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBGIF */ + + /* ----------------------- JPEG ------------------------- */ +#if HAVE_LIBJPEG + /* JPEG works for only 8 bpp gray and rgb */ + if (cmap || d > 8) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixConvertTo8(pixc, 0); + depth = pixGetDepth(pix1); + L_INFO("write/read jpeg\n", procName); + pixWrite(FILE_JPG, pix1, IFF_JFIF_JPEG); + pix2 = pixRead(FILE_JPG); + if (depth == 8) { + pixCompareGray(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + if (diff > 8.0) { + L_INFO(" **** bad jpeg image: d = %d, diff = %5.2f ****\n", + procName, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBJPEG */ + + /* ----------------------- WEBP ------------------------- */ +#if HAVE_LIBWEBP + /* WEBP works for rgb and rgba */ + if (cmap || d <= 16) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixClone(pixc); + depth = pixGetDepth(pix1); + L_INFO("write/read webp\n", procName); + pixWrite(FILE_WEBP, pix1, IFF_WEBP); + pix2 = pixRead(FILE_WEBP); + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); + if (diff > 5.0) { + L_INFO(" **** bad webp image: d = %d, diff = %5.2f ****\n", + procName, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBWEBP */ + + /* ----------------------- JP2K ------------------------- */ +#if HAVE_LIBJP2K + /* JP2K works for only 8 bpp gray, rgb and rgba */ + if (cmap || d > 8) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixConvertTo8(pixc, 0); + depth = pixGetDepth(pix1); + L_INFO("write/read jp2k\n", procName); + pixWrite(FILE_JP2K, pix1, IFF_JP2); + pix2 = pixRead(FILE_JP2K); + if (depth == 8) { + pixCompareGray(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + fprintf(stderr, "diff = %7.3f\n", diff); + if (diff > 7.0) { + L_INFO(" **** bad jp2k image: d = %d, diff = %5.2f ****\n", + procName, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBJP2K */ + + if (problems == FALSE) + L_INFO("All formats read and written OK!\n", procName); + + pixDestroy(&pixc); + pixDestroy(&pixs); + return problems; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/recog.h b/hgdriver/3rdparty/hgOCR/leptonica/recog.h new file mode 100644 index 0000000..44e6aa1 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/recog.h @@ -0,0 +1,264 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_RECOG_H +#define LEPTONICA_RECOG_H + +/*! + * \file recog.h + * + *
+ *     This is a simple utility for training and recognizing individual
+ *     machine-printed text characters.  It is designed to be adapted
+ *     to a particular set of character images; e.g., from a book.
+ *
+ *     There are two methods of training the recognizer.  In the most
+ *     simple, a set of bitmaps has been labeled by some means, such
+ *     a generic OCR program.  This is input either one template at a time
+ *     or as a pixa of templates, to a function that creates a recog.
+ *     If in a pixa, the text string label must be embedded in the
+ *     text field of each pix.
+ *
+ *     If labeled data is not available, we start with a bootstrap
+ *     recognizer (BSR) that has labeled data from a variety of sources.
+ *     These images are scaled, typically to a fixed height, and then
+ *     fed similarly scaled unlabeled images from the source (e.g., book),
+ *     and the BSR attempts to identify them.  All images that have
+ *     a high enough correlation score with one of the templates in the
+ *     BSR are emitted in a pixa, which now holds unscaled and labeled
+ *     templates from the source.  This is the generator for a book adapted
+ *     recognizer (BAR).
+ *
+ *     The pixa should always be thought of as the primary structure.
+ *     It is the generator for the recog, because a recog is built
+ *     from a pixa of unscaled images.
+ *
+ *     New image templates can be added to a recog as long as it is
+ *     in training mode.  Once training is finished, to add templates
+ *     it is necessary to extract the generating pixa, add templates
+ *     to that pixa, and make a new recog.  Similarly, we do not
+ *     join two recog; instead, we simply join their generating pixa,
+ *     and make a recog from that.
+ *
+ *     To remove outliers from a pixa of labeled pix, make a recog,
+ *     determine the outliers, and generate a new pixa with the
+ *     outliers removed.  The outliers are determined by building
+ *     special templates for each character set that are scaled averages
+ *     of the individual templates.  Then a correlation score is found
+ *     between each template and the averaged templates.  There are
+ *     two implementations; outliers are determined as either:
+ *      (1) a template having a correlation score with its class average
+ *          that is below a threshold, or
+ *      (2) a template having a correlation score with its class average
+ *          that is smaller than the correlation score with the average
+ *          of another class.
+ *     Outliers are removed from the generating pixa.  Scaled averaging
+ *     is only performed for determining outliers and for splitting
+ *     characters; it is never used in a trained recognizer for identifying
+ *     unlabeled samples.
+ *
+ *     Two methods using averaged templates are provided for splitting
+ *     touching characters:
+ *      (1) greedy matching
+ *      (2) document image decoding (DID)
+ *     The DID method is the default.  It is about 5x faster and
+ *     possibly more accurate.
+ *
+ *     Once a BAR has been made, unlabeled sample images are identified
+ *     by finding the individual template in the BAR with highest
+ *     correlation.  The input images and images in the BAR can be
+ *     represented in two ways:
+ *      (1) as scanned, binarized to 1 bpp
+ *      (2) as a width-normalized outline formed by thinning to a
+ *          skeleton and then dilating by a fixed amount.
+ *
+ *     The recog can be serialized to file and read back.  The serialized
+ *     version holds the templates used for correlation (which may have
+ *     been modified by scaling and turning into lines from the unscaled
+ *     templates), plus, for arbitrary character sets, the UTF8
+ *     representation and the lookup table mapping from the character
+ *     representation to index.
+ *
+ *     Why do we not use averaged templates for recognition?
+ *     Letterforms can take on significantly different shapes (eg.,
+ *     the letters 'a' and 'g'), and it makes no sense to average these.
+ *     The previous version of this utility allowed multiple recognizers
+ *     to exist, but this is an unnecessary complication if recognition
+ *     is done on all samples instead of on averages.
+ * 
+ */ + +#define RECOG_VERSION_NUMBER 2 + +struct L_Recog { + l_int32 scalew; /*!< scale all examples to this width; */ + /*!< use 0 prevent horizontal scaling */ + l_int32 scaleh; /*!< scale all examples to this height; */ + /*!< use 0 prevent vertical scaling */ + l_int32 linew; /*!< use a value > 0 to convert the bitmap */ + /*!< to lines of fixed width; 0 to skip */ + l_int32 templ_use; /*!< template use: use either the average */ + /*!< or all temmplates (L_USE_AVERAGE or */ + /*!< L_USE_ALL) */ + l_int32 maxarraysize; /*!< initialize container arrays to this */ + l_int32 setsize; /*!< size of character set */ + l_int32 threshold; /*!< for binarizing if depth > 1 */ + l_int32 maxyshift; /*!< vertical jiggle on nominal centroid */ + /*!< alignment; typically 0 or 1 */ + l_int32 charset_type; /*!< one of L_ARABIC_NUMERALS, etc. */ + l_int32 charset_size; /*!< expected number of classes in charset */ + l_int32 min_nopad; /*!< min number of samples without padding */ + l_int32 num_samples; /*!< number of training samples */ + l_int32 minwidth_u; /*!< min width averaged unscaled templates */ + l_int32 maxwidth_u; /*!< max width averaged unscaled templates */ + l_int32 minheight_u; /*!< min height averaged unscaled templates */ + l_int32 maxheight_u; /*!< max height averaged unscaled templates */ + l_int32 minwidth; /*!< min width averaged scaled templates */ + l_int32 maxwidth; /*!< max width averaged scaled templates */ + l_int32 ave_done; /*!< set to 1 when averaged bitmaps are made */ + l_int32 train_done; /*!< set to 1 when training is complete or */ + /*!< identification has started */ + l_float32 max_wh_ratio; /*!< max width/height ratio to split */ + l_float32 max_ht_ratio; /*!< max of max/min template height ratio */ + l_int32 min_splitw; /*!< min component width kept in splitting */ + l_int32 max_splith; /*!< max component height kept in splitting */ + struct Sarray *sa_text; /*!< text array for arbitrary char set */ + struct L_Dna *dna_tochar; /*!< index-to-char lut for arbitrary charset */ + l_int32 *centtab; /*!< table for finding centroids */ + l_int32 *sumtab; /*!< table for finding pixel sums */ + struct Pixaa *pixaa_u; /*!< all unscaled templates for each class */ + struct Ptaa *ptaa_u; /*!< centroids of all unscaled templates */ + struct Numaa *naasum_u; /*!< area of all unscaled templates */ + struct Pixaa *pixaa; /*!< all (scaled) templates for each class */ + struct Ptaa *ptaa; /*!< centroids of all (scaledl) templates */ + struct Numaa *naasum; /*!< area of all (scaled) templates */ + struct Pixa *pixa_u; /*!< averaged unscaled templates per class */ + struct Pta *pta_u; /*!< centroids of unscaled ave. templates */ + struct Numa *nasum_u; /*!< area of unscaled averaged templates */ + struct Pixa *pixa; /*!< averaged (scaled) templates per class */ + struct Pta *pta; /*!< centroids of (scaled) ave. templates */ + struct Numa *nasum; /*!< area of (scaled) averaged templates */ + struct Pixa *pixa_tr; /*!< all input training images */ + struct Pixa *pixadb_ave; /*!< unscaled and scaled averaged bitmaps */ + struct Pixa *pixa_id; /*!< input images for identifying */ + struct Pix *pixdb_ave; /*!< debug: best match of input against ave. */ + struct Pix *pixdb_range; /*!< debug: best matches within range */ + struct Pixa *pixadb_boot; /*!< debug: bootstrap training results */ + struct Pixa *pixadb_split; /*!< debug: splitting results */ + struct L_Bmf *bmf; /*!< bmf fonts */ + l_int32 bmf_size; /*!< font size of bmf; default is 6 pt */ + struct L_Rdid *did; /*!< temp data used for image decoding */ + struct L_Rch *rch; /*!< temp data used for holding best char */ + struct L_Rcha *rcha; /*!< temp data used for array of best chars */ +}; +typedef struct L_Recog L_RECOG; + +/*! + * Data returned from correlation matching on a single character + */ +struct L_Rch { + l_int32 index; /*!< index of best template */ + l_float32 score; /*!< correlation score of best template */ + char *text; /*!< character string of best template */ + l_int32 sample; /*!< index of best sample (within the best */ + /*!< template class, if all samples are used) */ + l_int32 xloc; /*!< x-location of template (delx + shiftx) */ + l_int32 yloc; /*!< y-location of template (dely + shifty) */ + l_int32 width; /*!< width of best template */ +}; +typedef struct L_Rch L_RCH; + +/*! + * Data returned from correlation matching on an array of characters + */ +struct L_Rcha { + struct Numa *naindex; /*!< indices of best templates */ + struct Numa *nascore; /*!< correlation scores of best templates */ + struct Sarray *satext; /*!< character strings of best templates */ + struct Numa *nasample; /*!< indices of best samples */ + struct Numa *naxloc; /*!< x-locations of templates (delx + shiftx) */ + struct Numa *nayloc; /*!< y-locations of templates (dely + shifty) */ + struct Numa *nawidth; /*!< widths of best templates */ +}; +typedef struct L_Rcha L_RCHA; + +/*! + * Data used for decoding a line of characters. + */ +struct L_Rdid { + struct Pix *pixs; /*!< clone of pix to be decoded */ + l_int32 **counta; /*!< count array for each averaged template */ + l_int32 **delya; /*!< best y-shift array per average template */ + l_int32 narray; /*!< number of averaged templates */ + l_int32 size; /*!< size of count array (width of pixs) */ + l_int32 *setwidth; /*!< setwidths for each template */ + struct Numa *nasum; /*!< pixel count in pixs by column */ + struct Numa *namoment; /*!< first moment of pixels in pixs by cols */ + l_int32 fullarrays; /*!< 1 if full arrays are made; 0 otherwise */ + l_float32 *beta; /*!< channel coeffs for template fg term */ + l_float32 *gamma; /*!< channel coeffs for bit-and term */ + l_float32 *trellisscore; /*!< score on trellis */ + l_int32 *trellistempl; /*!< template on trellis (for backtrack) */ + struct Numa *natempl; /*!< indices of best path templates */ + struct Numa *naxloc; /*!< x locations of best path templates */ + struct Numa *nadely; /*!< y locations of best path templates */ + struct Numa *nawidth; /*!< widths of best path templates */ + struct Boxa *boxa; /*!< Viterbi result for splitting input pixs */ + struct Numa *nascore; /*!< correlation scores: best path templates */ + struct Numa *natempl_r; /*!< indices of best rescored templates */ + struct Numa *nasample_r; /*!< samples of best scored templates */ + struct Numa *naxloc_r; /*!< x locations of best rescoredtemplates */ + struct Numa *nadely_r; /*!< y locations of best rescoredtemplates */ + struct Numa *nawidth_r; /*!< widths of best rescoredtemplates */ + struct Numa *nascore_r; /*!< correlation scores: rescored templates */ +}; +typedef struct L_Rdid L_RDID; + + +/*-------------------------------------------------------------------------* + * Flags for describing limited character sets * + *-------------------------------------------------------------------------*/ +/*! Character Set */ +enum { + L_UNKNOWN = 0, /*!< character set type is not specified */ + L_ARABIC_NUMERALS = 1, /*!< 10 digits */ + L_LC_ROMAN_NUMERALS = 2, /*!< 7 lower-case letters (i,v,x,l,c,d,m) */ + L_UC_ROMAN_NUMERALS = 3, /*!< 7 upper-case letters (I,V,X,L,C,D,M) */ + L_LC_ALPHA = 4, /*!< 26 lower-case letters */ + L_UC_ALPHA = 5 /*!< 26 upper-case letters */ +}; + +/*-------------------------------------------------------------------------* + * Flags for selecting between using average and all templates: * + * recog->templ_use * + *-------------------------------------------------------------------------*/ +/*! Template Select */ +enum { + L_USE_ALL_TEMPLATES = 0, /*!< use all templates; default */ + L_USE_AVERAGE_TEMPLATES = 1 /*!< use average templates; special cases */ +}; + +#endif /* LEPTONICA_RECOG_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/recogbasic.c b/hgdriver/3rdparty/hgOCR/leptonica/recogbasic.c new file mode 100644 index 0000000..6e52815 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/recogbasic.c @@ -0,0 +1,1228 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file recogbasic.c + *
+ *
+ *      Recog creation, destruction and access
+ *         L_RECOG            *recogCreateFromRecog()
+ *         L_RECOG            *recogCreateFromPixa()
+ *         L_RECOG            *recogCreateFromPixaNoFinish()
+ *         L_RECOG            *recogCreate()
+ *         void                recogDestroy()
+ *
+ *      Recog accessors
+ *         l_int32             recogGetCount()
+ *         l_int32             recogSetParams()
+ *         static l_int32      recogGetCharsetSize()
+ *
+ *      Character/index lookup
+ *         l_int32             recogGetClassIndex()
+ *         l_int32             recogStringToIndex()
+ *         l_int32             recogGetClassString()
+ *         l_int32             l_convertCharstrToInt()
+ *
+ *      Serialization
+ *         L_RECOG            *recogRead()
+ *         L_RECOG            *recogReadStream()
+ *         L_RECOG            *recogReadMem()
+ *         l_int32             recogWrite()
+ *         l_int32             recogWriteStream()
+ *         l_int32             recogWriteMem()
+ *         PIXA               *recogExtractPixa()
+ *         static l_int32      recogAddCharstrLabels()
+ *         static l_int32      recogAddAllSamples()
+ *
+ *  The recognizer functionality is split into four files:
+ *    recogbasic.c: create, destroy, access, serialize
+ *    recogtrain.c: training on labeled and unlabeled data
+ *    recogident.c: running the recognizer(s) on input
+ *    recogdid.c:   running the recognizer(s) on input using a
+ *                  document image decoding (DID) hidden markov model
+ *
+ *  This is a content-adapted (or book-adapted) recognizer (BAR) application.
+ *  The recognizers here are typically assembled from data that has
+ *  been labeled by a generic recognition system, such as Tesseract.
+ *  The general procedure to create a recognizer (recog) from labeled data is
+ *  to add the labeled character bitmaps, either one at a time or
+ *  all together from a pixa with labeled pix.
+ *
+ *  The suggested use for a BAR that consists of labeled templates drawn
+ *  from a single source (e.g., a book) is to identify unlabeled samples
+ *  by using unscaled character templates in the BAR, picking the
+ *  template closest to the unlabeled sample.
+ *
+ *  Outliers can be removed from a pixa of labeled pix.  This is one of
+ *  two methods that use averaged templates (the other is greedy splitting
+ *  of characters).  See recogtrain.c for a discussion and the implementation.
+ *
+ *  A special bootstrap recognizer (BSR) can be used to make a BAR from
+ *  unlabeled book data.  This is done by comparing character images
+ *  from the book with labeled templates in the BSR, where all images
+ *  are scaled to h = 40.  The templates can be either the scanned images
+ *  or images consisting of width-normalized strokes derived from
+ *  the skeleton of the character bitmaps.
+ *
+ *  Two BARs of labeled character data, that have been made by
+ *  different recognizers, can be joined by extracting a pixa of the
+ *  labeled templates from each, joining the two pixa, and then
+ *  and regenerating a BAR from the joined set of templates.
+ *  If all the labeled character data is from a single source (e.g, a book),
+ *  identification can proceed using unscaled templates (either the input
+ *  image or width-normalized lines).  But if the labeled data comes from
+ *  more than one source, (a "hybrid" recognizer), the templates should
+ *  be scaled, and we recommend scaling to a fixed height.
+ *
+ *  Suppose it is not possible to generate a BAR with a sufficient number
+ *  of templates of each class taken from a single source.  In that case,
+ *  templates from the BSR itself can be added.  This is the condition
+ *  described above, where the labeled templates come from multiple
+ *  sources, and it is necessary to do all character matches using
+ *  templates that have been scaled to a fixed height (e.g., 40).
+ *  Likewise, the samples to be identified using this hybrid recognizer
+ *  must be modified in the same way.  See prog/recogtest3.c for an
+ *  example of the steps that can be taken in the construction of a BAR
+ *  using a BSR.
+ *
+ *  For training numeric input, an example set of calls that scales
+ *  each training input to fixed h and will use the line templates of
+ *  width linew for identifying unknown characters is:
+ *         L_Recog  *rec = recogCreate(0, h, linew, 128, 1);
+ *         for (i = 0; i < n; i++) {  // read in n training digits
+ *             Pix *pix = ...
+ *             recogTrainLabeled(rec, pix, NULL, text[i], 0);
+ *         }
+ *         recogTrainingFinished(&rec, 1, -1, -1.0);  // required
+ *
+ *  It is an error if any function that computes averages, removes
+ *  outliers or requests identification of an unlabeled character,
+ *  such as:
+ *     (1) computing the sample averages: recogAverageSamples()
+ *     (2) removing outliers: recogRemoveOutliers1() or recogRemoveOutliers2()
+ *     (3) requesting identification of an unlabeled character:
+ *         recogIdentifyPix()
+ *  is called before an explicit call to finish training.  Note that
+ *  to do further training on a "finished" recognizer, you can set
+ *         recog->train_done = FALSE;
+ *  add the new training samples, and again call
+ *         recogTrainingFinished(&rec, 1, -1, -1.0);  // required
+ *
+ *  If not scaling, using the images directly for identification, and
+ *  removing outliers, do something like this:
+ *      L_Recog  *rec = recogCreate(0, 0, 0, 128, 1);
+ *      for (i = 0; i < n; i++) {  // read in n training characters
+ *          Pix *pix = ...
+ *          recogTrainLabeled(rec, pix, NULL, text[i], 0);
+ *      }
+ *      recogTrainingFinished(&rec, 1, -1, -1.0);
+ *      if (!rec) ... [return]
+ *      // remove outliers
+ *      recogRemoveOutliers1(&rec, 0.7, 2, NULL, NULL);
+ *
+ *  You can generate a recognizer from a pixa where the text field in
+ *  each pix is the character string label for the pix.  For example,
+ *  the following recognizer will store unscaled line images:
+ *      L_Recog  *rec = recogCreateFromPixa(pixa, 0, 0, linew, 128, 1);
+ *  and in use, it is fed unscaled line images to identify.
+ *
+ *  For the following, assume that you have a pixa of labeled templates.
+ *  If it is likely that some of the input templates are mislabeled,
+ *  there are several things that can be done to remove them.
+ *  The first is to put a size and quantity filter on them; e.g.
+ *       Pixa *pixa2 = recogFilterPixaBySize(pixa1, 10, 15, 2.6);
+ *  Then you can remove outliers; e.g.,
+ *       Pixa *pixa3 = pixaRemoveOutliers2(pixa2, -1.0, -1, NULL, NULL);
+ *
+ *  To this point, all templates are from a single source, so you
+ *  can make a recognizer that uses the unscaled templates and optionally
+ *  attempts to split touching characters:
+ *       L_Recog *recog1 = recogCreateFromPixa(pixa3, ...);
+ *  Alternatively, if you need more templates for some of the classes,
+ *  you can pad with templates from a "bootstrap" recognizer (BSR).
+ *  If you pad, it is necessary to scale the templates and input
+ *  samples to a fixed height, and no attempt will be made to split
+ *  the input sample connected components:
+ *       L_Recog *recog1 = recogCreateFromPixa(pixa3, 0, 40, 0, 128, 0);
+ *       recogPadDigitTrainingSet(&recog1, 40, 0);
+ *
+ *  A special case is a pure BSR, that contains images scaled to a fixed
+ *  height (we use 40 in these examples).
+ *  For this,use either the scanned bitmap:
+ *      L_Recog  *recboot = recogCreateFromPixa(pixa, 0, 40, 0, 128, 1);
+ *  or width-normalized lines (use width of 5 here):
+ *      L_Recog  *recboot = recogCreateFromPixa(pixa, 0, 40, 5, 128, 1);
+ *
+ *  This can be used to train a new book adapted recognizer (BAC), on
+ *  unlabeled data from, e.g., a book.  To do this, the following is required:
+ *   (1) the input images from the book must be scaled in the same
+ *       way as those in the BSR, and
+ *   (2) both the BSR and the input images must be set up to be either
+ *       input scanned images or width-normalized lines.
+ *
+ * 
+ */ + +#include +#include "allheaders.h" + +static const l_int32 MaxExamplesInClass = 256; + + /* Default recog parameters that can be changed */ +static const l_int32 DefaultCharsetType = L_ARABIC_NUMERALS; +static const l_int32 DefaultMinNopad = 1; +static const l_float32 DefaultMaxWHRatio = 3.0; /* max allowed w/h + ratio for a component to be split */ +static const l_float32 DefaultMaxHTRatio = 2.6; /* max allowed ratio of + max/min unscaled averaged template heights */ +static const l_int32 DefaultThreshold = 150; /* for binarization */ +static const l_int32 DefaultMaxYShift = 1; /* for identification */ + + /* Static functions */ +static l_int32 recogGetCharsetSize(l_int32 type); +static l_int32 recogAddCharstrLabels(L_RECOG *recog); +static l_int32 recogAddAllSamples(L_RECOG **precog, PIXAA *paa, l_int32 debug); + + +/*------------------------------------------------------------------------* + * Recog: initialization and destruction * + *------------------------------------------------------------------------*/ +/*! + * \brief recogCreateFromRecog() + * + * \param[in] recs source recog with arbitrary input parameters + * \param[in] scalew scale all widths to this; use 0 otherwise + * \param[in] scaleh scale all heights to this; use 0 otherwise + * \param[in] linew width of normalized strokes; use 0 to skip + * \param[in] threshold for binarization; typically ~128 + * \param[in] maxyshift from nominal centroid alignment; default is 1 + * \return recd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a convenience function that generates a recog using
+ *          the unscaled training data in an existing recog.
+ *      (2) It is recommended to use %maxyshift = 1 (the default value)
+ *      (3) See recogCreate() for use of %scalew, %scaleh and %linew.
+ * 
+ */ +L_RECOG * +recogCreateFromRecog(L_RECOG *recs, + l_int32 scalew, + l_int32 scaleh, + l_int32 linew, + l_int32 threshold, + l_int32 maxyshift) +{ +L_RECOG *recd; +PIXA *pixa; + + PROCNAME("recogCreateFromRecog"); + + if (!recs) + return (L_RECOG *)ERROR_PTR("recs not defined", procName, NULL); + + pixa = recogExtractPixa(recs); + recd = recogCreateFromPixa(pixa, scalew, scaleh, linew, threshold, + maxyshift); + pixaDestroy(&pixa); + return recd; +} + + +/*! + * \brief recogCreateFromPixa() + * + * \param[in] pixa of labeled, 1 bpp images + * \param[in] scalew scale all widths to this; use 0 otherwise + * \param[in] scaleh scale all heights to this; use 0 otherwise + * \param[in] linew width of normalized strokes; use 0 to skip + * \param[in] threshold for binarization; typically ~150 + * \param[in] maxyshift from nominal centroid alignment; default is 1 + * \return recog, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a convenience function for training from labeled data.
+ *          The pixa can be read from file.
+ *      (2) The pixa should contain the unscaled bitmaps used for training.
+ *      (3) See recogCreate() for use of %scalew, %scaleh and %linew.
+ *      (4) It is recommended to use %maxyshift = 1 (the default value)
+ *      (5) All examples in the same class (i.e., with the same character
+ *          label) should be similar.  They can be made similar by invoking
+ *          recogRemoveOutliers[1,2]() on %pixa before calling this function.
+ * 
+ */ +L_RECOG * +recogCreateFromPixa(PIXA *pixa, + l_int32 scalew, + l_int32 scaleh, + l_int32 linew, + l_int32 threshold, + l_int32 maxyshift) +{ +L_RECOG *recog; + + PROCNAME("recogCreateFromPixa"); + + if (!pixa) + return (L_RECOG *)ERROR_PTR("pixa not defined", procName, NULL); + + recog = recogCreateFromPixaNoFinish(pixa, scalew, scaleh, linew, + threshold, maxyshift); + if (!recog) + return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); + + recogTrainingFinished(&recog, 1, -1, -1.0); + if (!recog) + return (L_RECOG *)ERROR_PTR("bad templates", procName, NULL); + return recog; +} + + +/*! + * \brief recogCreateFromPixaNoFinish() + * + * \param[in] pixa of labeled, 1 bpp images + * \param[in] scalew scale all widths to this; use 0 otherwise + * \param[in] scaleh scale all heights to this; use 0 otherwise + * \param[in] linew width of normalized strokes; use 0 to skip + * \param[in] threshold for binarization; typically ~150 + * \param[in] maxyshift from nominal centroid alignment; default is 1 + * \return recog, or NULL on error + * + *
+ * Notes:
+ *      (1) See recogCreateFromPixa() for details.
+ *      (2) This is also used to generate a pixaa with templates
+ *          in each class within a pixa.  For that, all args except for
+ *          %pixa are ignored.
+ * 
+ */ +L_RECOG * +recogCreateFromPixaNoFinish(PIXA *pixa, + l_int32 scalew, + l_int32 scaleh, + l_int32 linew, + l_int32 threshold, + l_int32 maxyshift) +{ +char *text; +l_int32 full, n, i, ntext, same, maxd; +PIX *pix; +L_RECOG *recog; + + PROCNAME("recogCreateFromPixaNoFinish"); + + if (!pixa) + return (L_RECOG *)ERROR_PTR("pixa not defined", procName, NULL); + pixaVerifyDepth(pixa, &same, &maxd); + if (maxd > 1) + return (L_RECOG *)ERROR_PTR("not all pix are 1 bpp", procName, NULL); + + pixaIsFull(pixa, &full, NULL); + if (!full) + return (L_RECOG *)ERROR_PTR("not all pix are present", procName, NULL); + + n = pixaGetCount(pixa); + pixaCountText(pixa, &ntext); + if (ntext == 0) + return (L_RECOG *)ERROR_PTR("no pix have text strings", procName, NULL); + if (ntext < n) + L_ERROR("%d text strings < %d pix\n", procName, ntext, n); + + recog = recogCreate(scalew, scaleh, linew, threshold, maxyshift); + if (!recog) + return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + text = pixGetText(pix); + if (!text || strlen(text) == 0) { + L_ERROR("pix[%d] has no text\n", procName, i); + pixDestroy(&pix); + continue; + } + recogTrainLabeled(recog, pix, NULL, text, 0); + pixDestroy(&pix); + } + + return recog; +} + + +/*! + * \brief recogCreate() + * + * \param[in] scalew scale all widths to this; use 0 otherwise + * \param[in] scaleh scale all heights to this; use 0 otherwise + * \param[in] linew width of normalized strokes; use 0 to skip + * \param[in] threshold for binarization; typically ~128; 0 for default + * \param[in] maxyshift from nominal centroid alignment; default is 1 + * \return recog, or NULL on error + * + *
+ * Notes:
+ *      (1) If %scalew == 0 and %scaleh == 0, no scaling is done.
+ *          If one of these is 0 and the other is > 0, scaling is isotropic
+ *          to the requested size.  We typically do not set both > 0.
+ *      (2) Use linew > 0 to convert the templates to images with fixed
+ *          width strokes.  linew == 0 skips the conversion.
+ *      (3) The only valid values for %maxyshift are 0, 1 and 2.
+ *          It is recommended to use %maxyshift == 1 (default value).
+ *          Using %maxyshift == 0 is much faster than %maxyshift == 1, but
+ *          it is much less likely to find the template with the best
+ *          correlation.  Use of anything but 1 results in a warning.
+ *      (4) Scaling is used for finding outliers and for training a
+ *          book-adapted recognizer (BAR) from a bootstrap recognizer (BSR).
+ *          Scaling the height to a fixed value and scaling the width
+ *          accordingly (e.g., %scaleh = 40, %scalew = 0) is recommended.
+ *      (5) The storage for most of the arrays is allocated when training
+ *          is finished.
+ * 
+ */ +L_RECOG * +recogCreate(l_int32 scalew, + l_int32 scaleh, + l_int32 linew, + l_int32 threshold, + l_int32 maxyshift) +{ +L_RECOG *recog; + + PROCNAME("recogCreate"); + + if (scalew < 0 || scaleh < 0) + return (L_RECOG *)ERROR_PTR("invalid scalew or scaleh", procName, NULL); + if (linew > 10) + return (L_RECOG *)ERROR_PTR("invalid linew > 10", procName, NULL); + if (threshold == 0) threshold = DefaultThreshold; + if (threshold < 0 || threshold > 255) { + L_WARNING("invalid threshold; using default\n", procName); + threshold = DefaultThreshold; + } + if (maxyshift < 0 || maxyshift > 2) { + L_WARNING("invalid maxyshift; using default value\n", procName); + maxyshift = DefaultMaxYShift; + } else if (maxyshift == 0) { + L_WARNING("Using maxyshift = 0; faster, worse correlation results\n", + procName); + } else if (maxyshift == 2) { + L_WARNING("Using maxyshift = 2; slower\n", procName); + } + + recog = (L_RECOG *)LEPT_CALLOC(1, sizeof(L_RECOG)); + recog->templ_use = L_USE_ALL_TEMPLATES; /* default */ + recog->threshold = threshold; + recog->scalew = scalew; + recog->scaleh = scaleh; + recog->linew = linew; + recog->maxyshift = maxyshift; + recogSetParams(recog, 1, -1, -1.0, -1.0); + recog->bmf = bmfCreate(NULL, 6); + recog->bmf_size = 6; + recog->maxarraysize = MaxExamplesInClass; + + /* Generate the LUTs */ + recog->centtab = makePixelCentroidTab8(); + recog->sumtab = makePixelSumTab8(); + recog->sa_text = sarrayCreate(0); + recog->dna_tochar = l_dnaCreate(0); + + /* Input default values for min component size for splitting. + * These are overwritten when pixTrainingFinished() is called. */ + recog->min_splitw = 6; + recog->max_splith = 60; + + /* Allocate the paa for the unscaled training bitmaps */ + recog->pixaa_u = pixaaCreate(recog->maxarraysize); + + /* Generate the storage for debugging */ + recog->pixadb_boot = pixaCreate(2); + recog->pixadb_split = pixaCreate(2); + return recog; +} + + +/*! + * \brief recogDestroy() + * + * \param[in,out] precog will be set to null before returning + * \return void + */ +void +recogDestroy(L_RECOG **precog) +{ +L_RECOG *recog; + + PROCNAME("recogDestroy"); + + if (!precog) { + L_WARNING("ptr address is null\n", procName); + return; + } + + if ((recog = *precog) == NULL) return; + + LEPT_FREE(recog->centtab); + LEPT_FREE(recog->sumtab); + sarrayDestroy(&recog->sa_text); + l_dnaDestroy(&recog->dna_tochar); + pixaaDestroy(&recog->pixaa_u); + pixaDestroy(&recog->pixa_u); + ptaaDestroy(&recog->ptaa_u); + ptaDestroy(&recog->pta_u); + numaDestroy(&recog->nasum_u); + numaaDestroy(&recog->naasum_u); + pixaaDestroy(&recog->pixaa); + pixaDestroy(&recog->pixa); + ptaaDestroy(&recog->ptaa); + ptaDestroy(&recog->pta); + numaDestroy(&recog->nasum); + numaaDestroy(&recog->naasum); + pixaDestroy(&recog->pixa_tr); + pixaDestroy(&recog->pixadb_ave); + pixaDestroy(&recog->pixa_id); + pixDestroy(&recog->pixdb_ave); + pixDestroy(&recog->pixdb_range); + pixaDestroy(&recog->pixadb_boot); + pixaDestroy(&recog->pixadb_split); + bmfDestroy(&recog->bmf); + rchDestroy(&recog->rch); + rchaDestroy(&recog->rcha); + recogDestroyDid(recog); + LEPT_FREE(recog); + *precog = NULL; + return; +} + + +/*------------------------------------------------------------------------* + * Recog accessors * + *------------------------------------------------------------------------*/ +/*! + * \brief recogGetCount() + * + * \param[in] recog + * \return count of classes in recog; 0 if no recog or on error + */ +l_int32 +recogGetCount(L_RECOG *recog) +{ + PROCNAME("recogGetCount"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 0); + return recog->setsize; +} + + +/*! + * \brief recogSetParams() + * + * \param[in] recog to be padded, if necessary + * \param[in] type type of char set; -1 for default; + * see enum in recog.h + * \param[in] min_nopad min number in a class without padding; + * use -1 for default + * \param[in] max_wh_ratio max width/height ratio allowed for splitting; + * use -1.0 for default + * \param[in] max_ht_ratio max of max/min averaged template height ratio; + * use -1.0 for default + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is called when a recog is created.
+ *      (2) Default %min_nopad value allows for some padding.
+ *          To disable padding, set %min_nopad = 0.  To pad only when
+ *          no samples are available for the class, set %min_nopad = 1.
+ *      (3) The %max_wh_ratio limits the width/height ratio for components
+ *          that we attempt to split.  Splitting long components is expensive.
+ *      (4) The %max_ht_ratio is a quality requirement on the training data.
+ *          The recognizer will not run if the averages are computed and
+ *          the templates do not satisfy it.
+ * 
+ */ +l_ok +recogSetParams(L_RECOG *recog, + l_int32 type, + l_int32 min_nopad, + l_float32 max_wh_ratio, + l_float32 max_ht_ratio) +{ + PROCNAME("recogSetParams"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + recog->charset_type = (type >= 0) ? type : DefaultCharsetType; + recog->charset_size = recogGetCharsetSize(recog->charset_type); + recog->min_nopad = (min_nopad >= 0) ? min_nopad : DefaultMinNopad; + recog->max_wh_ratio = (max_wh_ratio > 0.0) ? max_wh_ratio : + DefaultMaxWHRatio; + recog->max_ht_ratio = (max_ht_ratio > 1.0) ? max_ht_ratio : + DefaultMaxHTRatio; + return 0; +} + + +/*! + * \brief recogGetCharsetSize() + * + * \param[in] type of charset + * \return size of charset, or 0 if unknown or on error + */ +static l_int32 +recogGetCharsetSize(l_int32 type) +{ + PROCNAME("recogGetCharsetSize"); + + switch (type) { + case L_UNKNOWN: + return 0; + case L_ARABIC_NUMERALS: + return 10; + case L_LC_ROMAN_NUMERALS: + return 7; + case L_UC_ROMAN_NUMERALS: + return 7; + case L_LC_ALPHA: + return 26; + case L_UC_ALPHA: + return 26; + default: + L_ERROR("invalid charset_type %d\n", procName, type); + return 0; + } + return 0; /* shouldn't happen */ +} + + +/*------------------------------------------------------------------------* + * Character/index lookup * + *------------------------------------------------------------------------*/ +/*! + * \brief recogGetClassIndex() + * + * \param[in] recog with LUT's pre-computed + * \param[in] val integer value; can be up to 3 bytes for UTF-8 + * \param[in] text text from which %val was derived; used if not found + * \param[out] pindex index into dna_tochar + * \return 0 if found; 1 if not found and added; 2 on error. + * + *
+ * Notes:
+ *      (1) This is used during training.  There is one entry in
+ *          recog->dna_tochar (integer value, e.g., ascii) and
+ *          one in recog->sa_text (e.g, ascii letter in a string)
+ *          for each character class.
+ *      (2) This searches the dna character array for %val.  If it is
+ *          not found, the template represents a character class not
+ *          already seen: it increments setsize (the number of character
+ *          classes) by 1, and augments both the index (dna_tochar)
+ *          and text (sa_text) arrays.
+ *      (3) Returns the index in &index, except on error.
+ *      (4) Caller must check the function return value.
+ * 
+ */ +l_int32 +recogGetClassIndex(L_RECOG *recog, + l_int32 val, + char *text, + l_int32 *pindex) +{ +l_int32 i, n, ival; + + PROCNAME("recogGetClassIndex"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 2); + *pindex = -1; + if (!recog) + return ERROR_INT("recog not defined", procName, 2); + if (!text) + return ERROR_INT("text not defined", procName, 2); + + /* Search existing characters */ + n = l_dnaGetCount(recog->dna_tochar); + for (i = 0; i < n; i++) { + l_dnaGetIValue(recog->dna_tochar, i, &ival); + if (val == ival) { /* found */ + *pindex = i; + return 0; + } + } + + /* If not found... */ + l_dnaAddNumber(recog->dna_tochar, val); + sarrayAddString(recog->sa_text, text, L_COPY); + recog->setsize++; + *pindex = n; + return 1; +} + + +/*! + * \brief recogStringToIndex() + * + * \param[in] recog + * \param[in] text text string for some class + * \param[out] pindex index for that class; -1 if not found + * \return 0 if OK, 1 on error not finding the string is an error + */ +l_ok +recogStringToIndex(L_RECOG *recog, + char *text, + l_int32 *pindex) +{ +char *charstr; +l_int32 i, n, diff; + + PROCNAME("recogStringtoIndex"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = -1; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!text) + return ERROR_INT("text not defined", procName, 1); + + /* Search existing characters */ + n = recog->setsize; + for (i = 0; i < n; i++) { + recogGetClassString(recog, i, &charstr); + if (!charstr) { + L_ERROR("string not found for index %d\n", procName, i); + continue; + } + diff = strcmp(text, charstr); + LEPT_FREE(charstr); + if (diff) continue; + *pindex = i; + return 0; + } + + return 1; /* not found */ +} + + +/*! + * \brief recogGetClassString() + * + * \param[in] recog + * \param[in] index into array of char types + * \param[out] pcharstr string representation; + * returns an empty string on error + * \return 0 if found, 1 on error + * + *
+ * Notes:
+ *      (1) Extracts a copy of the string from sa_text, which
+ *          the caller must free.
+ *      (2) Caller must check the function return value.
+ * 
+ */ +l_int32 +recogGetClassString(L_RECOG *recog, + l_int32 index, + char **pcharstr) +{ + PROCNAME("recogGetClassString"); + + if (!pcharstr) + return ERROR_INT("&charstr not defined", procName, 1); + *pcharstr = stringNew(""); + if (!recog) + return ERROR_INT("recog not defined", procName, 2); + + if (index < 0 || index >= recog->setsize) + return ERROR_INT("invalid index", procName, 1); + LEPT_FREE(*pcharstr); + *pcharstr = sarrayGetString(recog->sa_text, index, L_COPY); + return 0; +} + + +/*! + * \brief l_convertCharstrToInt() + * + * \param[in] str input string representing one UTF-8 character; + * not more than 4 bytes + * \param[out] pval integer value for the input. Think of it + * as a 1-to-1 hash code. + * \return 0 if OK, 1 on error + */ +l_ok +l_convertCharstrToInt(const char *str, + l_int32 *pval) +{ +l_int32 size, val; + + PROCNAME("l_convertCharstrToInt"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (!str) + return ERROR_INT("str not defined", procName, 1); + size = strlen(str); + if (size == 0) + return ERROR_INT("empty string", procName, 1); + if (size > 4) + return ERROR_INT("invalid string: > 4 bytes", procName, 1); + + val = (l_int32)str[0]; + if (size > 1) + val = (val << 8) + (l_int32)str[1]; + if (size > 2) + val = (val << 8) + (l_int32)str[2]; + if (size > 3) + val = (val << 8) + (l_int32)str[3]; + *pval = val; + return 0; +} + + +/*------------------------------------------------------------------------* + * Serialization * + *------------------------------------------------------------------------*/ +/*! + * \brief recogRead() + * + * \param[in] filename + * \return recog, or NULL on error + * + *
+ * Notes:
+ *      (1) When a recog is serialized, a pixaa of the templates that are
+ *          actually used for correlation is saved in the pixaa_u array
+ *          of the recog.  These can be different from the templates that
+ *          were used to generate the recog, because those original templates
+ *          can be scaled and turned into normalized lines.  When recog1
+ *          is deserialized to recog2, these templates are put in both the
+ *          unscaled array (pixaa_u) and the modified array (pixaa) in recog2.
+ *          Why not put it in only the unscaled array and let
+ *          recogTrainingFinalized() regenerate the modified templates?
+ *          The reason is that with normalized lines, the operation of
+ *          thinning to a skeleton and dilating back to a fixed width
+ *          is not idempotent.  Thinning to a skeleton saves pixels at
+ *          the end of a line segment, and thickening the skeleton puts
+ *          additional pixels at the end of the lines.  This tends to
+ *          close gaps.
+ * 
+ */ +L_RECOG * +recogRead(const char *filename) +{ +FILE *fp; +L_RECOG *recog; + + PROCNAME("recogRead"); + + if (!filename) + return (L_RECOG *)ERROR_PTR("filename not defined", procName, NULL); + if ((fp = fopenReadStream(filename)) == NULL) + return (L_RECOG *)ERROR_PTR("stream not opened", procName, NULL); + + if ((recog = recogReadStream(fp)) == NULL) { + fclose(fp); + return (L_RECOG *)ERROR_PTR("recog not read", procName, NULL); + } + + fclose(fp); + return recog; +} + + +/*! + * \brief recogReadStream() + * + * \param[in] fp file stream + * \return recog, or NULL on error + */ +L_RECOG * +recogReadStream(FILE *fp) +{ +l_int32 version, setsize, threshold, scalew, scaleh, linew; +l_int32 maxyshift, nc; +L_DNA *dna_tochar; +PIXAA *paa; +L_RECOG *recog; +SARRAY *sa_text; + + PROCNAME("recogReadStream"); + + if (!fp) + return (L_RECOG *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nRecog Version %d\n", &version) != 1) + return (L_RECOG *)ERROR_PTR("not a recog file", procName, NULL); + if (version != RECOG_VERSION_NUMBER) + return (L_RECOG *)ERROR_PTR("invalid recog version", procName, NULL); + if (fscanf(fp, "Size of character set = %d\n", &setsize) != 1) + return (L_RECOG *)ERROR_PTR("setsize not read", procName, NULL); + if (fscanf(fp, "Binarization threshold = %d\n", &threshold) != 1) + return (L_RECOG *)ERROR_PTR("binary thresh not read", procName, NULL); + if (fscanf(fp, "Maxyshift = %d\n", &maxyshift) != 1) + return (L_RECOG *)ERROR_PTR("maxyshift not read", procName, NULL); + if (fscanf(fp, "Scale to width = %d\n", &scalew) != 1) + return (L_RECOG *)ERROR_PTR("width not read", procName, NULL); + if (fscanf(fp, "Scale to height = %d\n", &scaleh) != 1) + return (L_RECOG *)ERROR_PTR("height not read", procName, NULL); + if (fscanf(fp, "Normalized line width = %d\n", &linew) != 1) + return (L_RECOG *)ERROR_PTR("line width not read", procName, NULL); + if ((recog = recogCreate(scalew, scaleh, linew, threshold, + maxyshift)) == NULL) + return (L_RECOG *)ERROR_PTR("recog not made", procName, NULL); + + if (fscanf(fp, "\nLabels for character set:\n") != 0) { + recogDestroy(&recog); + return (L_RECOG *)ERROR_PTR("label intro not read", procName, NULL); + } + l_dnaDestroy(&recog->dna_tochar); + if ((dna_tochar = l_dnaReadStream(fp)) == NULL) { + recogDestroy(&recog); + return (L_RECOG *)ERROR_PTR("dna_tochar not read", procName, NULL); + } + recog->dna_tochar = dna_tochar; + sarrayDestroy(&recog->sa_text); + if ((sa_text = sarrayReadStream(fp)) == NULL) { + recogDestroy(&recog); + return (L_RECOG *)ERROR_PTR("sa_text not read", procName, NULL); + } + recog->sa_text = sa_text; + + if (fscanf(fp, "\nPixaa of all samples in the training set:\n") != 0) { + recogDestroy(&recog); + return (L_RECOG *)ERROR_PTR("pixaa intro not read", procName, NULL); + } + if ((paa = pixaaReadStream(fp)) == NULL) { + recogDestroy(&recog); + return (L_RECOG *)ERROR_PTR("pixaa not read", procName, NULL); + } + recog->setsize = setsize; + nc = pixaaGetCount(paa, NULL); + if (nc != setsize) { + recogDestroy(&recog); + pixaaDestroy(&paa); + L_ERROR("(setsize = %d) != (paa count = %d)\n", procName, + setsize, nc); + return NULL; + } + + recogAddAllSamples(&recog, paa, 0); /* this finishes */ + pixaaDestroy(&paa); + if (!recog) + return (L_RECOG *)ERROR_PTR("bad templates", procName, NULL); + return recog; +} + + +/*! + * \brief recogReadMem() + * + * \param[in] data serialization of recog (not ascii) + * \param[in] size of data in bytes + * \return recog, or NULL on error + */ +L_RECOG * +recogReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +L_RECOG *recog; + + PROCNAME("recogReadMem"); + + if (!data) + return (L_RECOG *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (L_RECOG *)ERROR_PTR("stream not opened", procName, NULL); + + recog = recogReadStream(fp); + fclose(fp); + if (!recog) L_ERROR("recog not read\n", procName); + return recog; +} + + +/*! + * \brief recogWrite() + * + * \param[in] filename + * \param[in] recog + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pixaa of templates that is written is the modified one
+ *          in the pixaa field. It is the pixaa that is actually used
+ *          for correlation. This is not the unscaled array of labeled
+ *          bitmaps, in pixaa_u, that was used to generate the recog in the
+ *          first place.  See the notes in recogRead() for the rationale.
+ * 
+ */ +l_ok +recogWrite(const char *filename, + L_RECOG *recog) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("recogWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = recogWriteStream(fp, recog); + fclose(fp); + if (ret) + return ERROR_INT("recog not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief recogWriteStream() + * + * \param[in] fp file stream opened for "wb" + * \param[in] recog + * \return 0 if OK, 1 on error + */ +l_ok +recogWriteStream(FILE *fp, + L_RECOG *recog) +{ + PROCNAME("recogWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + fprintf(fp, "\nRecog Version %d\n", RECOG_VERSION_NUMBER); + fprintf(fp, "Size of character set = %d\n", recog->setsize); + fprintf(fp, "Binarization threshold = %d\n", recog->threshold); + fprintf(fp, "Maxyshift = %d\n", recog->maxyshift); + fprintf(fp, "Scale to width = %d\n", recog->scalew); + fprintf(fp, "Scale to height = %d\n", recog->scaleh); + fprintf(fp, "Normalized line width = %d\n", recog->linew); + fprintf(fp, "\nLabels for character set:\n"); + l_dnaWriteStream(fp, recog->dna_tochar); + sarrayWriteStream(fp, recog->sa_text); + fprintf(fp, "\nPixaa of all samples in the training set:\n"); + pixaaWriteStream(fp, recog->pixaa); + + return 0; +} + + +/*! + * \brief recogWriteMem() + * + * \param[out] pdata data of serialized recog (not ascii) + * \param[out] psize size of returned data + * \param[in] recog + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a recog in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +recogWriteMem(l_uint8 **pdata, + size_t *psize, + L_RECOG *recog) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("recogWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = recogWriteStream(fp, recog); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = recogWriteStream(fp, recog); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief recogExtractPixa() + * + * \param[in] recog + * \return pixa if OK, NULL on error + * + *
+ * Notes:
+ *      (1) This generates a pixa of all the unscaled images in the
+ *          recognizer, where each one has its character class label in
+ *          the pix text field, by flattening pixaa_u to a pixa.
+ * 
+ */ +PIXA * +recogExtractPixa(L_RECOG *recog) +{ + PROCNAME("recogExtractPixa"); + + if (!recog) + return (PIXA *)ERROR_PTR("recog not defined", procName, NULL); + + recogAddCharstrLabels(recog); + return pixaaFlattenToPixa(recog->pixaa_u, NULL, L_CLONE); +} + + +/*! + * \brief recogAddCharstrLabels() + * + * \param[in] recog + * \return 0 if OK, 1 on error + */ +static l_int32 +recogAddCharstrLabels(L_RECOG *recog) +{ +char *text; +l_int32 i, j, n1, n2; +PIX *pix; +PIXA *pixa; +PIXAA *paa; + + PROCNAME("recogAddCharstrLabels"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + /* Add the labels to each unscaled pix */ + paa = recog->pixaa_u; + n1 = pixaaGetCount(paa, NULL); + for (i = 0; i < n1; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + text = sarrayGetString(recog->sa_text, i, L_NOCOPY); + n2 = pixaGetCount(pixa); + for (j = 0; j < n2; j++) { + pix = pixaGetPix(pixa, j, L_CLONE); + pixSetText(pix, text); + pixDestroy(&pix); + } + pixaDestroy(&pixa); + } + + return 0; +} + + +/*! + * \brief recogAddAllSamples() + * + * \param[in] precog addr of recog + * \param[in] paa pixaa from previously trained recog + * \param[in] debug + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) On error, the input recog is destroyed.
+ *      (2) This is used with the serialization routine recogRead(),
+ *          where each pixa in the pixaa represents a set of characters
+ *          in a different class.  Before calling this function, we have
+ *          verified that the number of character classes, given by the
+ *          setsize field in %recog, equals the number of pixa in the paa.
+ *          The character labels for each set are in the sa_text field.
+ * 
+ */ +static l_int32 +recogAddAllSamples(L_RECOG **precog, + PIXAA *paa, + l_int32 debug) +{ +char *text; +l_int32 i, j, nc, ns; +PIX *pix; +PIXA *pixa, *pixa1; +L_RECOG *recog; + + PROCNAME("recogAddAllSamples"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if ((recog = *precog) == NULL) + return ERROR_INT("recog not defined", procName, 1); + if (!paa) { + recogDestroy(&recog); + return ERROR_INT("paa not defined", procName, 1); + } + + nc = pixaaGetCount(paa, NULL); + for (i = 0; i < nc; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + ns = pixaGetCount(pixa); + text = sarrayGetString(recog->sa_text, i, L_NOCOPY); + pixa1 = pixaCreate(ns); + pixaaAddPixa(recog->pixaa_u, pixa1, L_INSERT); + for (j = 0; j < ns; j++) { + pix = pixaGetPix(pixa, j, L_CLONE); + if (debug) fprintf(stderr, "pix[%d,%d]: text = %s\n", i, j, text); + pixaaAddPix(recog->pixaa_u, i, pix, NULL, L_INSERT); + } + pixaDestroy(&pixa); + } + + recogTrainingFinished(&recog, 0, -1, -1.0); /* For second parameter, + see comment in recogRead() */ + if (!recog) + return ERROR_INT("bad templates; recog destroyed", procName, 1); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/recogdid.c b/hgdriver/3rdparty/hgOCR/leptonica/recogdid.c new file mode 100644 index 0000000..105bc3e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/recogdid.c @@ -0,0 +1,1074 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file recogdid.c + *
+ *
+ *      Top-level identification
+ *         BOXA             *recogDecode()
+ *
+ *      Generate decoding arrays
+ *         static l_int32    recogPrepareForDecoding()
+ *         static l_int32    recogMakeDecodingArray()
+ *
+ *      Dynamic programming for best path
+ *         static l_int32    recogRunViterbi()
+ *         static l_int32    recogRescoreDidResult()
+ *         static PIX       *recogShowPath()
+ *
+ *      Create/destroy temporary DID data
+ *         l_int32           recogCreateDid()
+ *         l_int32           recogDestroyDid()
+ *
+ *      Various helpers
+ *         l_int32           recogDidExists()
+ *         L_RDID           *recogGetDid()
+ *         static l_int32    recogGetWindowedArea()
+ *         l_int32           recogSetChannelParams()
+ *         static l_int32    recogTransferRchToDid()
+ *
+ *  See recogbasic.c for examples of training a recognizer, which is
+ *  required before it can be used for document image decoding.
+ *
+ *  Gary Kopec pioneered this hidden markov approach to "Document Image
+ *  Decoding" (DID) in the early 1990s.  It is based on estimation
+ *  using a generative model of the image generation process, and
+ *  provides the most likely decoding of an image if the model is correct.
+ *  Given the model, it finds the maximum a posteriori (MAP) "message"
+ *  given the observed image.  The model describes how to generate
+ *  an image from a message, and the MAP message is derived from the
+ *  observed image using Bayes' theorem.  This approach can also be used
+ *  to build the model, using the iterative expectation/maximization
+ *  method from labeled but errorful data.
+ *
+ *  In a little more detail: The model comprises three things: the ideal
+ *  printed character templates, the independent bit-flip noise model, and
+ *  the character setwidths.  When a character is printed, the setwidth
+ *  is the distance in pixels that you move forward before being able
+ *  to print the next character.  It is typically slightly less than the
+ *  width of the character template: if too small, an extra character can be
+ *  hallucinated; if too large, it will not be able to match the next
+ *  character template on the line.  The model assumes that the probabilities
+ *  of bit flip depend only on the assignment of the pixel to background
+ *  or template foreground.  The multilevel templates have different
+ *  bit flip probabilities for each level.  Because a character image
+ *  is composed of many pixels, each of which can be independently flipped,
+ *  the actual probability of seeing any rendering is exceedingly small,
+ *  being composed of the product of the probabilities for each pixel.
+ *  The log likelihood is used both to avoid numeric underflow and,
+ *  more importantly, because it results in a summation of independent
+ *  pixel probabilities.  That summation can be shown, in Kopec's
+ *  original paper, to consist of a sum of two terms: (a) the number of
+ *  fg pixels in the bit-and of the observed image with the ideal
+ *  template and (b) the number of fg pixels in the template.  Each
+ *  has a coefficient that depends only on the bit-flip probabilities
+ *  for the fg and bg.  A beautiful result, and computationally simple!
+ *  One nice feature of this approach is that the result of the decoding
+ *  is not very sensitive to the values  used for the bit flip probabilities.
+ *
+ *  The procedure for finding the best decoding (MAP) for a given image goes
+ *  under several names: Viterbi, dynamic programming, hidden markov model.
+ *  It is called a "hidden markov model" because the templates are assumed
+ *  to be printed serially and we don't know what they are -- the identity
+ *  of the templates must be inferred from the observed image.
+ *  The possible decodings form a dense trellis over the pixel positions,
+ *  where at each pixel position you have the possibility of having any
+ *  of the characters printed there (with some reference point) or having
+ *  a single pixel wide space inserted there.  Thus, before the trellis
+ *  can be traversed, we must do the work of finding the log probability,
+ *  at each pixel location, that each of the templates was printed there.
+ *  Armed with those arrays of data, the dynamic programming procedure
+ *  moves from left to right, one pixel at a time, recursively finding
+ *  the path with the highest log probability that gets to that pixel
+ *  position (and noting which template was printed to arrive there).
+ *  After reaching the right side of the image, we can simply backtrack
+ *  along the path, jumping over each template that lies on the highest
+ *  scoring path.  This best path thus only goes through a few of the
+ *  pixel positions.
+ *
+ *  There are two refinements to the original Kopec paper.  In the first,
+ *  one uses multiple, non-overlapping fg templates, each with its own
+ *  bit flip probability.  This makes sense, because the probability
+ *  that a fg boundary pixel flips to bg is greater than that of a fg
+ *  pixel not on the boundary.  And the flip probability of a fg boundary
+ *  pixel is smaller than that of a bg boundary pixel, which in turn
+ *  is greater than that of a bg pixel not on a boundary (the latter
+ *  is taken to be the true background).  Then the simplest realistic
+ *  multiple template model has three templates that are not background.
+ *
+ *  In the second refinement, a heuristic (strict upper bound) is used
+ *  iteratively in the Viterbi process to compute the log probabilities.
+ *  Using the heuristic, you find the best path, and then score all nodes
+ *  on that path with the actual probability, which is guaranteed to
+ *  be a smaller number.  You run this iteratively, rescoring just the best
+ *  found path each time.  After each rescoring, the path may change because
+ *  the local scores have been reduced.  However, the process converges
+ *  rapidly, and when it doesn't change, it must be the best path because
+ *  it is properly scored (even if neighboring paths are heuristically
+ *  scored).  The heuristic score is found column-wise by assuming
+ *  that all the fg pixels in the template are on fg pixels in the image --
+ *  we just take the minimum of the number of pixels in the template
+ *  and image column.  This can easily give a 10-fold reduction in
+ *  computation because the heuristic score can be computed much faster
+ *  than the exact score.
+ *
+ *  For reference, the classic paper on the approach by Kopec is:
+ *  * "Document Image Decoding Using Markov Source Models", IEEE Trans.
+ *    PAMI, Vol 16, No. 6, June 1994, pp 602-617.
+ *  A refinement of the method for multilevel templates by Kopec is:
+ *  * "Multilevel Character Templates for Document Image Decoding",
+ *    Proc. SPIE 3027, Document Recognition IV, p. 168ff, 1997.
+ *  Further refinements for more efficient decoding are given in these
+ *  two papers, which are both stored on leptonica.org:
+ *  * "Document Image Decoding using Iterated Complete Path Search", Minka,
+ *    Bloomberg and Popat, Proc. SPIE Vol 4307, p. 250-258, Document
+ *    Recognition and Retrieval VIII, San Jose, CA 2001.
+ *  * "Document Image Decoding using Iterated Complete Path Search with
+ *    Subsampled Heuristic Scoring", Bloomberg, Minka and Popat, ICDAR 2001,
+ *    p. 344-349, Sept. 2001, Seattle.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +static l_int32 recogPrepareForDecoding(L_RECOG *recog, PIX *pixs, + l_int32 debug); +static l_int32 recogMakeDecodingArray(L_RECOG *recog, l_int32 index, + l_int32 debug); +static l_int32 recogRunViterbi(L_RECOG *recog, PIX **ppixdb); +static l_int32 recogRescoreDidResult(L_RECOG *recog, PIX **ppixdb); +static PIX *recogShowPath(L_RECOG *recog, l_int32 select); +static l_int32 recogGetWindowedArea(L_RECOG *recog, l_int32 index, + l_int32 x, l_int32 *pdely, l_int32 *pwsum); +static l_int32 recogTransferRchToDid(L_RECOG *recog, l_int32 x, l_int32 y); + + /* Parameters for modeling the decoding */ +static const l_float32 SetwidthFraction = 0.95; +static const l_int32 MaxYShift = 1; + + /* Channel parameters. alpha[0] is the probability that a bg pixel + * is OFF. alpha[1] is the probability that level 1 fg is ON. + * The actual values are not too critical, but they must be larger + * than 0.5 and smaller than 1.0. For more accuracy in template + * matching, use a 4-level template, where levels 2 and 3 are + * boundary pixels in the fg and bg, respectively. */ +static const l_float32 DefaultAlpha2[] = {0.95f, 0.9f}; +static const l_float32 DefaultAlpha4[] = {0.95f, 0.9f, 0.75f, 0.25f}; + + +/*------------------------------------------------------------------------* + * Top-level identification * + *------------------------------------------------------------------------*/ +/*! + * \brief recogDecode() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs typically of multiple touching characters, 1 bpp + * \param[in] nlevels of templates; 2 for now + * \param[out] ppixdb [optional] debug result; can be null + * \return boxa segmentation of pixs into characters, or NULL on error + * + *
+ * Notes:
+ *      (1) The input pixs has been filtered so that it is likely to be
+ *          composed of more than one touching character.  Specifically,
+ *          its height can only slightly exceed that of the tallest
+ *          unscaled template, the width is somewhat larger than the
+ *          width of the widest unscaled template, and the w/h aspect ratio
+ *          is bounded by max_wh_ratio.
+ *      (2) This uses the DID mechanism with labeled templates to
+ *          segment the input %pixs.  The resulting segmentation is
+ *          returned.  (It is given by did->boxa).
+ *      (3) In debug mode, the Viterbi path is rescored based on all
+ *          the templates.  In non-debug mode, the same procedure is
+ *          carried out by recogIdentifyPix() on the result of the
+ *          segmentation.
+ * 
+ */ +BOXA * +recogDecode(L_RECOG *recog, + PIX *pixs, + l_int32 nlevels, + PIX **ppixdb) +{ +l_int32 debug; +PIX *pix1; +PIXA *pixa; + + PROCNAME("recogDecode"); + + if (ppixdb) *ppixdb = NULL; + if (!recog) + return (BOXA *)ERROR_PTR("recog not defined", procName, NULL); + if (!pixs || pixGetDepth(pixs) != 1) + return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (!recog->train_done) + return (BOXA *)ERROR_PTR("training not finished", procName, NULL); + if (nlevels != 2) + return (BOXA *)ERROR_PTR("nlevels != 2 (for now)", procName, NULL); + + debug = (ppixdb) ? 1 : 0; + if (recogPrepareForDecoding(recog, pixs, debug)) + return (BOXA *)ERROR_PTR("error making arrays", procName, NULL); + recogSetChannelParams(recog, nlevels); + + /* Normal path; just run Viterbi */ + if (!debug) { + if (recogRunViterbi(recog, NULL) == 0) + return boxaCopy(recog->did->boxa, L_COPY); + else + return (BOXA *)ERROR_PTR("error in Viterbi", procName, NULL); + } + + /* Debug path */ + if (recogRunViterbi(recog, &pix1)) + return (BOXA *)ERROR_PTR("error in viterbi", procName, NULL); + pixa = pixaCreate(2); + pixaAddPix(pixa, pix1, L_INSERT); + if (recogRescoreDidResult(recog, &pix1)) { + pixaDestroy(&pixa); + return (BOXA *)ERROR_PTR("error in rescoring", procName, NULL); + } + pixaAddPix(pixa, pix1, L_INSERT); + *ppixdb = pixaDisplayTiledInRows(pixa, 32, 2 * pixGetWidth(pix1) + 100, + 1.0, 0, 30, 2); + pixaDestroy(&pixa); + return boxaCopy(recog->did->boxa, L_COPY); +} + + +/*------------------------------------------------------------------------* + * Generate decoding arrays * + *------------------------------------------------------------------------*/ +/*! + * \brief recogPrepareForDecoding() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs typically of multiple touching characters, 1 bpp + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Binarizes and crops input %pixs.
+ *      (2) Removes previous L_RDID struct and makes a new one.
+ *      (3) Generates the bit-and sum arrays for each character template
+ *          at each pixel position in %pixs.  These are used in the
+ *          Viterbi dynamic programming step.
+ *      (4) The values are saved in the scoring arrays at the left edge
+ *          of the template.  They are used in the Viterbi process
+ *          at the setwidth position (which is near the RHS of the template
+ *          as it is positioned on pixs) in the generated trellis.
+ * 
+ */ +static l_int32 +recogPrepareForDecoding(L_RECOG *recog, + PIX *pixs, + l_int32 debug) +{ +l_int32 i; +PIX *pix1; +L_RDID *did; + + PROCNAME("recogPrepareForDecoding"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!recog->train_done) + return ERROR_INT("training not finished", procName, 1); + + if (!recog->ave_done) + recogAverageSamples(&recog, 0); + + /* Binarize and crop to foreground if necessary */ + if ((pix1 = recogProcessToIdentify(recog, pixs, 0)) == NULL) + return ERROR_INT("pix1 not made", procName, 1); + + /* Remove any existing RecogDID and set up a new one */ + recogDestroyDid(recog); + if (recogCreateDid(recog, pix1)) { + pixDestroy(&pix1); + return ERROR_INT("decoder not made", procName, 1); + } + + /* Compute vertical sum and first moment arrays */ + did = recogGetDid(recog); /* owned by recog */ + did->nasum = pixCountPixelsByColumn(pix1); + did->namoment = pixGetMomentByColumn(pix1, 1); + + /* Generate the arrays */ + for (i = 0; i < recog->did->narray; i++) + recogMakeDecodingArray(recog, i, debug); + + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief recogMakeDecodingArray() + * + * \param[in] recog + * \param[in] index of averaged template + * \param[in] debug 1 for debug output; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates the bit-and sum array for a character template along pixs.
+ *      (2) The values are saved in the scoring arrays at the left edge
+ *          of the template as it is positioned on pixs.
+ * 
+ */ +static l_int32 +recogMakeDecodingArray(L_RECOG *recog, + l_int32 index, + l_int32 debug) +{ +l_int32 i, j, w1, h1, w2, h2, nx, ycent2, count, maxcount, maxdely; +l_int32 sum, moment, dely, shifty; +l_int32 *counta, *delya, *ycent1, *arraysum, *arraymoment, *sumtab; +NUMA *nasum, *namoment; +PIX *pix1, *pix2, *pix3; +L_RDID *did; + + PROCNAME("recogMakeDecodingArray"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if (index < 0 || index >= did->narray) + return ERROR_INT("invalid index", procName, 1); + + /* Check that pix1 is large enough for this template. */ + pix1 = did->pixs; /* owned by did; do not destroy */ + pixGetDimensions(pix1, &w1, &h1, NULL); + pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE); + pixGetDimensions(pix2, &w2, &h2, NULL); + if (w1 < w2) { + L_INFO("w1 = %d < w2 = %d for index %d\n", procName, w1, w2, index); + pixDestroy(&pix2); + return 0; + } + + nasum = did->nasum; + namoment = did->namoment; + ptaGetIPt(recog->pta_u, index, NULL, &ycent2); + sumtab = recog->sumtab; + counta = did->counta[index]; + delya = did->delya[index]; + + /* Set up the array for ycent1. This gives the y-centroid location + * for a window of width w2, starting at location i. */ + nx = w1 - w2 + 1; /* number of positions w2 can be placed in w1 */ + ycent1 = (l_int32 *)LEPT_CALLOC(nx, sizeof(l_int32)); + arraysum = numaGetIArray(nasum); + arraymoment = numaGetIArray(namoment); + for (i = 0, sum = 0, moment = 0; i < w2; i++) { + sum += arraysum[i]; + moment += arraymoment[i]; + } + for (i = 0; i < nx - 1; i++) { + ycent1[i] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum; + sum += arraysum[w2 + i] - arraysum[i]; + moment += arraymoment[w2 + i] - arraymoment[i]; + } + ycent1[nx - 1] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum; + + /* Compute the bit-and sum between the template pix2 and pix1, at + * locations where the left side of pix2 goes from 0 to nx - 1 + * in pix1. Do this around the vertical alignment of the pix2 + * centroid and the windowed pix1 centroid. + * (1) Start with pix3 cleared and approximately equal in size to pix1. + * (2) Blit the y-shifted pix2 onto pix3. Then all ON pixels + * are within the intersection of pix1 and the shifted pix2. + * (3) AND pix1 with pix3. */ + pix3 = pixCreate(w2, h1, 1); + for (i = 0; i < nx; i++) { + shifty = (l_int32)(ycent1[i] - ycent2 + 0.5); + maxcount = 0; + maxdely = 0; + for (j = -MaxYShift; j <= MaxYShift; j++) { + pixClearAll(pix3); + dely = shifty + j; /* amount pix2 is shifted relative to pix1 */ + pixRasterop(pix3, 0, dely, w2, h2, PIX_SRC, pix2, 0, 0); + pixRasterop(pix3, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, i, 0); + pixCountPixels(pix3, &count, sumtab); + if (count > maxcount) { + maxcount = count; + maxdely = dely; + } + } + counta[i] = maxcount; + delya[i] = maxdely; + } + did->fullarrays = TRUE; + + pixDestroy(&pix2); + pixDestroy(&pix3); + LEPT_FREE(ycent1); + LEPT_FREE(arraysum); + LEPT_FREE(arraymoment); + return 0; +} + + +/*------------------------------------------------------------------------* + * Dynamic programming for best path + *------------------------------------------------------------------------*/ +/*! + * \brief recogRunViterbi() + * + * \param[in] recog with LUT's pre-computed + * \param[out] ppixdb [optional] debug result; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This can be used when the templates are unscaled.  It works by
+ *          matching the average, unscaled templates of each class to
+ *          all positions.
+ *      (2) It is recursive, in that
+ *          (a) we compute the score successively at all pixel positions x,
+ *          (b) to compute the score at x in the trellis, for each
+ *              template we look backwards to (x - setwidth) to get the
+ *              score if that template were to be printed with its
+ *              setwidth location at x.  We save at x the template and
+ *              score that maximizes the sum of the score at (x - setwidth)
+ *              and the log-likelihood for the template to be printed with
+ *              its LHS there.
+ *      (3) The primary output is a boxa of the locations for splitting
+ *          the input image.  These locations are used later to split the
+ *          image and send the pieces individually for recognition.
+ *          This can be done in either recogIdentifyMultiple(), or
+ *          for debugging in recogRescoreDidResult().
+ * 
+ */ +static l_int32 +recogRunViterbi(L_RECOG *recog, + PIX **ppixdb) +{ +l_int32 i, w1, w2, h1, xnz, x, narray, minsetw; +l_int32 first, templ, xloc, dely, counts, area1; +l_int32 besttempl, spacetempl; +l_int32 *setw, *didtempl; +l_int32 *area2; /* must be freed */ +l_float32 prevscore, matchscore, maxscore, correl; +l_float32 *didscore; +BOX *box; +PIX *pix1; +L_RDID *did; + + PROCNAME("recogRunViterbi"); + + if (ppixdb) *ppixdb = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if (did->fullarrays == 0) + return ERROR_INT("did full arrays not made", procName, 1); + + /* Compute the minimum setwidth. Bad templates with very small + * width can cause havoc because the setwidth is too small. */ + w1 = did->size; + narray = did->narray; + spacetempl = narray; + setw = did->setwidth; + minsetw = 100000; + for (i = 0; i < narray; i++) { + if (setw[i] < minsetw) + minsetw = setw[i]; + } + if (minsetw <= 2) + return ERROR_INT("minsetw <= 2; bad templates", procName, 1); + + /* The score array is initialized to 0.0. As we proceed to + * the left, the log likelihood for the partial paths goes + * negative, and we prune for the max (least negative) path. + * No matches will be computed until we reach x = min(setwidth); + * until then first == TRUE after looping over templates. */ + didscore = did->trellisscore; + didtempl = did->trellistempl; + area2 = numaGetIArray(recog->nasum_u); + besttempl = 0; /* just tells compiler it is initialized */ + maxscore = 0.0; /* ditto */ + for (x = minsetw; x < w1; x++) { /* will always get a score */ + first = TRUE; + for (i = 0; i < narray; i++) { + if (x - setw[i] < 0) continue; + matchscore = didscore[x - setw[i]] + + did->gamma[1] * did->counta[i][x - setw[i]] + + did->beta[1] * area2[i]; + if (first) { + maxscore = matchscore; + besttempl = i; + first = FALSE; + } else { + if (matchscore > maxscore) { + maxscore = matchscore; + besttempl = i; + } + } + } + + /* We can also put down a single pixel space, with no cost + * because all pixels are bg. */ + prevscore = didscore[x - 1]; + if (prevscore > maxscore) { /* 1 pixel space is best */ + maxscore = prevscore; + besttempl = spacetempl; + } + didscore[x] = maxscore; + didtempl[x] = besttempl; + } + + /* Backtrack to get the best path. + * Skip over (i.e., ignore) all single pixel spaces. */ + for (x = w1 - 1; x >= 0; x--) { + if (didtempl[x] != spacetempl) break; + } + h1 = pixGetHeight(did->pixs); + while (x > 0) { + if (didtempl[x] == spacetempl) { /* skip over spaces */ + x--; + continue; + } + templ = didtempl[x]; + xloc = x - setw[templ]; + if (xloc < 0) break; + counts = did->counta[templ][xloc]; /* bit-and counts */ + recogGetWindowedArea(recog, templ, xloc, &dely, &area1); + correl = ((l_float32)(counts) * counts) / + (l_float32)(area2[templ] * area1); + pix1 = pixaGetPix(recog->pixa_u, templ, L_CLONE); + w2 = pixGetWidth(pix1); + numaAddNumber(did->natempl, templ); + numaAddNumber(did->naxloc, xloc); + numaAddNumber(did->nadely, dely); + numaAddNumber(did->nawidth, pixGetWidth(pix1)); + numaAddNumber(did->nascore, correl); + xnz = L_MAX(xloc, 0); + box = boxCreate(xnz, dely, w2, h1); + boxaAddBox(did->boxa, box, L_INSERT); + pixDestroy(&pix1); + x = xloc; + } + + if (ppixdb) { + numaWriteStream(stderr, did->natempl); + numaWriteStream(stderr, did->naxloc); + numaWriteStream(stderr, did->nadely); + numaWriteStream(stderr, did->nawidth); + numaWriteStream(stderr, did->nascore); + boxaWriteStream(stderr, did->boxa); + *ppixdb = recogShowPath(recog, 0); + } + + LEPT_FREE(area2); + return 0; +} + + +/*! + * \brief recogRescoreDidResult() + * + * \param[in] recog with LUT's pre-computed + * \param[out] ppixdb [optional] debug result; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does correlation matching with all unscaled templates,
+ *          using the character segmentation determined by the Viterbi path.
+ * 
+ */ +static l_int32 +recogRescoreDidResult(L_RECOG *recog, + PIX **ppixdb) +{ +l_int32 i, n, sample, x, dely, index; +char *text; +l_float32 score; +BOX *box1; +PIX *pixs, *pix1; +L_RDID *did; + + PROCNAME("recogRescoreDidResult"); + + if (ppixdb) *ppixdb = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if (did->fullarrays == 0) + return ERROR_INT("did full arrays not made", procName, 1); + if ((n = numaGetCount(did->naxloc)) == 0) + return ERROR_INT("no elements in path", procName, 1); + + pixs = did->pixs; + for (i = 0; i < n; i++) { + box1 = boxaGetBox(did->boxa, i, L_COPY); + boxGetGeometry(box1, &x, &dely, NULL, NULL); + pix1 = pixClipRectangle(pixs, box1, NULL); + recogIdentifyPix(recog, pix1, NULL); + recogTransferRchToDid(recog, x, dely); + if (ppixdb) { + rchExtract(recog->rch, &index, &score, &text, + &sample, NULL, NULL, NULL); + fprintf(stderr, "text = %s, index = %d, sample = %d," + " score = %5.3f\n", text, index, sample, score); + } + pixDestroy(&pix1); + boxDestroy(&box1); + LEPT_FREE(text); + } + + if (ppixdb) + *ppixdb = recogShowPath(recog, 1); + + return 0; +} + + +/*! + * \brief recogShowPath() + * + * \param[in] recog with LUT's pre-computed + * \param[in] select 0 for Viterbi; 1 for rescored + * \return pix debug output), or NULL on error + */ +static PIX * +recogShowPath(L_RECOG *recog, + l_int32 select) +{ +char textstr[16]; +l_int32 i, j, n, index, xloc, dely; +l_float32 score; +L_BMF *bmf; +NUMA *natempl_s, *nasample_s, *nascore_s, *naxloc_s, *nadely_s; +PIX *pixs, *pix0, *pix1, *pix2, *pix3, *pix4, *pix5; +L_RDID *did; + + PROCNAME("recogShowPath"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + if ((did = recogGetDid(recog)) == NULL) + return (PIX *)ERROR_PTR("did not defined", procName, NULL); + + bmf = bmfCreate(NULL, 8); + pixs = pixScale(did->pixs, 4.0, 4.0); + pix0 = pixAddBorderGeneral(pixs, 0, 0, 0, 40, 0); + pix1 = pixConvertTo32(pix0); + if (select == 0) { /* Viterbi */ + natempl_s = did->natempl; + nascore_s = did->nascore; + naxloc_s = did->naxloc; + nadely_s = did->nadely; + } else { /* rescored */ + natempl_s = did->natempl_r; + nasample_s = did->nasample_r; + nascore_s = did->nascore_r; + naxloc_s = did->naxloc_r; + nadely_s = did->nadely_r; + } + + n = numaGetCount(natempl_s); + for (i = 0; i < n; i++) { + numaGetIValue(natempl_s, i, &index); + if (select == 0) { + pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE); + } else { + numaGetIValue(nasample_s, i, &j); + pix2 = pixaaGetPix(recog->pixaa_u, index, j, L_CLONE); + } + pix3 = pixScale(pix2, 4.0, 4.0); + pix4 = pixErodeBrick(NULL, pix3, 5, 5); + pixXor(pix4, pix4, pix3); + numaGetFValue(nascore_s, i, &score); + snprintf(textstr, sizeof(textstr), "%5.3f", score); + pix5 = pixAddTextlines(pix4, bmf, textstr, 1, L_ADD_BELOW); + numaGetIValue(naxloc_s, i, &xloc); + numaGetIValue(nadely_s, i, &dely); + pixPaintThroughMask(pix1, pix5, 4 * xloc, 4 * dely, 0xff000000); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + pixDestroy(&pix5); + } + pixDestroy(&pixs); + pixDestroy(&pix0); + bmfDestroy(&bmf); + return pix1; +} + + +/*------------------------------------------------------------------------* + * Create/destroy temporary DID data * + *------------------------------------------------------------------------*/ +/*! + * \brief recogCreateDid() + * + * \param[in] recog + * \param[in] pixs of 1 bpp image to match + * \return 0 if OK, 1 on error + */ +l_ok +recogCreateDid(L_RECOG *recog, + PIX *pixs) +{ +l_int32 i; +PIX *pix1; +L_RDID *did; + + PROCNAME("recogCreateDid"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + recogDestroyDid(recog); + + did = (L_RDID *)LEPT_CALLOC(1, sizeof(L_RDID)); + recog->did = did; + did->pixs = pixClone(pixs); + did->narray = recog->setsize; + did->size = pixGetWidth(pixs); + did->natempl = numaCreate(5); + did->naxloc = numaCreate(5); + did->nadely = numaCreate(5); + did->nawidth = numaCreate(5); + did->boxa = boxaCreate(5); + did->nascore = numaCreate(5); + did->natempl_r = numaCreate(5); + did->nasample_r = numaCreate(5); + did->naxloc_r = numaCreate(5); + did->nadely_r = numaCreate(5); + did->nawidth_r = numaCreate(5); + did->nascore_r = numaCreate(5); + + /* Make the arrays */ + did->setwidth = (l_int32 *)LEPT_CALLOC(did->narray, sizeof(l_int32)); + did->counta = (l_int32 **)LEPT_CALLOC(did->narray, sizeof(l_int32 *)); + did->delya = (l_int32 **)LEPT_CALLOC(did->narray, sizeof(l_int32 *)); + did->beta = (l_float32 *)LEPT_CALLOC(5, sizeof(l_float32)); + did->gamma = (l_float32 *)LEPT_CALLOC(5, sizeof(l_float32)); + did->trellisscore = (l_float32 *)LEPT_CALLOC(did->size, sizeof(l_float32)); + did->trellistempl = (l_int32 *)LEPT_CALLOC(did->size, sizeof(l_int32)); + for (i = 0; i < did->narray; i++) { + did->counta[i] = (l_int32 *)LEPT_CALLOC(did->size, sizeof(l_int32)); + did->delya[i] = (l_int32 *)LEPT_CALLOC(did->size, sizeof(l_int32)); + } + + /* Populate the setwidth array */ + for (i = 0; i < did->narray; i++) { + pix1 = pixaGetPix(recog->pixa_u, i, L_CLONE); + did->setwidth[i] = (l_int32)(SetwidthFraction * pixGetWidth(pix1)); + pixDestroy(&pix1); + } + + return 0; +} + + +/*! + * \brief recogDestroyDid() + * + * \param[in] recog + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) As the signature indicates, this is owned by the recog, and can
+ *          only be destroyed using this function.
+ * 
+ */ +l_ok +recogDestroyDid(L_RECOG *recog) +{ +l_int32 i; +L_RDID *did; + + PROCNAME("recogDestroyDid"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + if ((did = recog->did) == NULL) return 0; + if (!did->counta || !did->delya) + return ERROR_INT("ptr array is null; shouldn't happen!", procName, 1); + + for (i = 0; i < did->narray; i++) { + LEPT_FREE(did->counta[i]); + LEPT_FREE(did->delya[i]); + } + LEPT_FREE(did->setwidth); + LEPT_FREE(did->counta); + LEPT_FREE(did->delya); + LEPT_FREE(did->beta); + LEPT_FREE(did->gamma); + LEPT_FREE(did->trellisscore); + LEPT_FREE(did->trellistempl); + pixDestroy(&did->pixs); + numaDestroy(&did->nasum); + numaDestroy(&did->namoment); + numaDestroy(&did->natempl); + numaDestroy(&did->naxloc); + numaDestroy(&did->nadely); + numaDestroy(&did->nawidth); + boxaDestroy(&did->boxa); + numaDestroy(&did->nascore); + numaDestroy(&did->natempl_r); + numaDestroy(&did->nasample_r); + numaDestroy(&did->naxloc_r); + numaDestroy(&did->nadely_r); + numaDestroy(&did->nawidth_r); + numaDestroy(&did->nascore_r); + LEPT_FREE(did); + recog->did = NULL; + return 0; +} + + +/*------------------------------------------------------------------------* + * Various helpers * + *------------------------------------------------------------------------*/ +/*! + * \brief recogDidExists() + * + * \param[in] recog + * \return 1 if recog->did exists; 0 if not or on error. + */ +l_int32 +recogDidExists(L_RECOG *recog) +{ + PROCNAME("recogDidExists"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 0); + return (recog->did) ? 1 : 0; +} + + +/*! + * \brief recogGetDid() + * + * \param[in] recog + * \return did still owned by the recog, or NULL on error + * + *
+ * Notes:
+ *      (1) This also makes sure the arrays are defined.
+ * 
+ */ +L_RDID * +recogGetDid(L_RECOG *recog) +{ +l_int32 i; +L_RDID *did; + + PROCNAME("recogGetDid"); + + if (!recog) + return (L_RDID *)ERROR_PTR("recog not defined", procName, NULL); + if ((did = recog->did) == NULL) + return (L_RDID *)ERROR_PTR("did not defined", procName, NULL); + if (!did->counta || !did->delya) + return (L_RDID *)ERROR_PTR("did array ptrs not defined", + procName, NULL); + for (i = 0; i < did->narray; i++) { + if (!did->counta[i] || !did->delya[i]) + return (L_RDID *)ERROR_PTR("did arrays not defined", + procName, NULL); + } + + return did; +} + + +/*! + * \brief recogGetWindowedArea() + * + * \param[in] recog + * \param[in] index of template + * \param[in] x pixel position of left hand edge of template + * \param[out] pdely y shift of template relative to pix1 + * \param[out] pwsum number of fg pixels in window of pixs + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is called after the best path has been found through
+ *          the trellis, in order to produce a correlation that can be used
+ *          to evaluate the confidence we have in the identification.
+ *          The correlation is |1 & 2|^2 / (|1| * |2|).
+ *          |1 & 2| is given by the count array, |2| is found from
+ *          nasum_u[], and |1| is wsum returned from this function.
+ * 
+ */ +static l_int32 +recogGetWindowedArea(L_RECOG *recog, + l_int32 index, + l_int32 x, + l_int32 *pdely, + l_int32 *pwsum) +{ +l_int32 w1, h1, w2, h2; +PIX *pix1, *pix2, *pixt; +L_RDID *did; + + PROCNAME("recogGetWindowedArea"); + + if (pdely) *pdely = 0; + if (pwsum) *pwsum = 0; + if (!pdely || !pwsum) + return ERROR_INT("&dely and &wsum not both defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if (index < 0 || index >= did->narray) + return ERROR_INT("invalid index", procName, 1); + pix1 = did->pixs; + pixGetDimensions(pix1, &w1, &h1, NULL); + if (x >= w1) + return ERROR_INT("invalid x position", procName, 1); + + pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE); + pixGetDimensions(pix2, &w2, &h2, NULL); + if (w1 < w2) { + L_INFO("template %d too small\n", procName, index); + pixDestroy(&pix2); + return 0; + } + + *pdely = did->delya[index][x]; + pixt = pixCreate(w2, h1, 1); + pixRasterop(pixt, 0, *pdely, w2, h2, PIX_SRC, pix2, 0, 0); + pixRasterop(pixt, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, x, 0); + pixCountPixels(pixt, pwsum, recog->sumtab); + pixDestroy(&pix2); + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief recogSetChannelParams() + * + * \param[in] recog + * \param[in] nlevels + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This converts the independent bit-flip probabilities in the
+ *          "channel" into log-likelihood coefficients on image sums.
+ *          These coefficients are only defined for the non-background
+ *          template levels.  Thus for nlevels = 2 (one fg, one bg),
+ *          only beta[1] and gamma[1] are used.  For nlevels = 4 (three
+ *          fg templates), we use beta[1-3] and gamma[1-3].
+ * 
+ */ +l_ok +recogSetChannelParams(L_RECOG *recog, + l_int32 nlevels) +{ +l_int32 i; +const l_float32 *da; +L_RDID *did; + + PROCNAME("recogSetChannelParams"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if (nlevels == 2) + da = DefaultAlpha2; + else if (nlevels == 4) + da = DefaultAlpha4; + else + return ERROR_INT("nlevels not 2 or 4", procName, 1); + + for (i = 1; i < nlevels; i++) { + did->beta[i] = log((1.0 - da[i]) / da[0]); + did->gamma[i] = log(da[0] * da[i] / ((1.0 - da[0]) * (1.0 - da[i]))); +/* fprintf(stderr, "beta[%d] = %7.3f, gamma[%d] = %7.3f\n", + i, did->beta[i], i, did->gamma[i]); */ + } + + return 0; +} + + +/*! + * \brief recogTransferRchToDid() + * + * \param[in] recog with rch and did defined + * \param[in] x left edge of extracted region, relative to decoded line + * \param[in] y top edge of extracted region, relative to input image + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used to transfer the results for a single character match
+ *          to the rescored did arrays.
+ * 
+ */ +static l_int32 +recogTransferRchToDid(L_RECOG *recog, + l_int32 x, + l_int32 y) +{ +L_RDID *did; +L_RCH *rch; + + PROCNAME("recogTransferRchToDid"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if ((did = recogGetDid(recog)) == NULL) + return ERROR_INT("did not defined", procName, 1); + if ((rch = recog->rch) == NULL) + return ERROR_INT("rch not defined", procName, 1); + + numaAddNumber(did->natempl_r, rch->index); + numaAddNumber(did->nasample_r, rch->sample); + numaAddNumber(did->naxloc_r, rch->xloc + x); + numaAddNumber(did->nadely_r, rch->yloc + y); + numaAddNumber(did->nawidth_r, rch->width); + numaAddNumber(did->nascore_r, rch->score); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/recogident.c b/hgdriver/3rdparty/hgOCR/leptonica/recogident.c new file mode 100644 index 0000000..44a8d07 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/recogident.c @@ -0,0 +1,1882 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file recogident.c + *
+ *
+ *      Top-level identification
+ *         l_int32             recogIdentifyMultiple()
+ *
+ *      Segmentation and noise removal
+ *         l_int32             recogSplitIntoCharacters()
+ *
+ *      Greedy character splitting
+ *         l_int32             recogCorrelationBestRow()
+ *         l_int32             recogCorrelationBestChar()
+ *         static l_int32      pixCorrelationBestShift()
+ *
+ *      Low-level identification of single characters
+ *         l_int32             recogIdentifyPixa()
+ *         l_int32             recogIdentifyPix()
+ *         l_int32             recogSkipIdentify()
+ *
+ *      Operations for handling identification results
+ *         static L_RCHA      *rchaCreate()
+ *         l_int32            *rchaDestroy()
+ *         static L_RCH       *rchCreate()
+ *         l_int32            *rchDestroy()
+ *         l_int32             rchaExtract()
+ *         l_int32             rchExtract()
+ *         static l_int32      transferRchToRcha()
+ *
+ *      Preprocessing and filtering
+ *         l_int32             recogProcessToIdentify()
+ *         static PIX         *recogPreSplittingFilter()
+ *         static PIX         *recogSplittingFilter()
+ *
+ *      Postprocessing
+ *         SARRAY             *recogExtractNumbers()
+ *         PIX                *showExtractNumbers()
+ *
+ *      Static debug helper
+ *         static void         l_showIndicatorSplitValues()
+ *
+ *  See recogbasic.c for examples of training a recognizer, which is
+ *  required before it can be used for identification.
+ *
+ *  The character splitter repeatedly does a greedy correlation with each
+ *  averaged unscaled template, at all pixel locations along the text to
+ *  be identified.  The vertical alignment is between the template
+ *  centroid and the (moving) windowed centroid, including a delta of
+ *  1 pixel above and below.  The best match then removes part of the
+ *  input image, leaving 1 or 2 pieces, which, after filtering,
+ *  are put in a queue.  The process ends when the queue is empty.
+ *  The filtering is based on the size and aspect ratio of the
+ *  remaining pieces; the intent is to remove anything that is
+ *  unlikely to be text, such as small pieces and line graphics.
+ *
+ *  After splitting, the selected segments are identified using
+ *  the input parameters that were initially specified for the
+ *  recognizer.  Unlike the splitter, which uses the averaged
+ *  templates from the unscaled input, the recognizer can use
+ *  either all training examples or averaged templates, and these
+ *  can be either scaled or unscaled.  These choices are specified
+ *  when the recognizer is constructed.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* There are two methods for splitting characters: DID and greedy. + * The default method is DID. */ +#define SPLIT_WITH_DID 1 + + /* Padding on pix1: added before correlations and removed from result */ +static const l_int32 LeftRightPadding = 32; + + /* Parameters for filtering and sorting connected components in splitter */ +static const l_float32 MinFillFactor = 0.10; +static const l_int32 DefaultMinHeight = 15; /* min unscaled height */ +static const l_int32 MinOverlap1 = 6; /* in pass 1 of boxaSort2d() */ +static const l_int32 MinOverlap2 = 6; /* in pass 2 of boxaSort2d() */ +static const l_int32 MinHeightPass1 = 5; /* min height to start pass 1 */ + + +static l_int32 pixCorrelationBestShift(PIX *pix1, PIX *pix2, NUMA *nasum1, + NUMA *namoment1, l_int32 area2, + l_int32 ycent2, l_int32 maxyshift, + l_int32 *tab8, l_int32 *pdelx, + l_int32 *pdely, l_float32 *pscore, + l_int32 debugflag ); +static L_RCH *rchCreate(l_int32 index, l_float32 score, char *text, + l_int32 sample, l_int32 xloc, l_int32 yloc, + l_int32 width); +static L_RCHA *rchaCreate(); +static l_int32 transferRchToRcha(L_RCH *rch, L_RCHA *rcha); +static PIX *recogPreSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 minh, + l_float32 minaf, l_int32 debug); +static l_int32 recogSplittingFilter(L_RECOG *recog, PIX *pixs, l_int32 min, + l_float32 minaf, l_int32 *premove, + l_int32 debug); +static void l_showIndicatorSplitValues(NUMA *na1, NUMA *na2, NUMA *na3, + NUMA *na4, NUMA *na5, NUMA *na6); + +/*------------------------------------------------------------------------* + * Identification + *------------------------------------------------------------------------*/ +/*! + * \brief recogIdentifyMultiple() + * + * \param[in] recog with training finished + * \param[in] pixs containing typically a small number of characters + * \param[in] minh remove shorter components; use 0 for default + * \param[in] skipsplit 1 to skip the splitting step + * \param[out] pboxa [optional] locations of identified components + * \param[out] ppixa [optional] images of identified components + * \param[out] ppixdb [optional] debug pix: inputs and best fits + * \param[in] debugsplit 1 returns pix split debugging images + * \return 0 if OK; 1 if nothing is found; 2 for other errors. + * + *
+ * Notes:
+ *      (1) This filters the input pixa and calls recogIdentifyPixa()
+ *      (2) Splitting is relatively slow, because it tries to match all
+ *          character templates to all locations.  This step can be skipped.
+ *      (3) An attempt is made to order the (optionally) returned images
+ *          and boxes in 2-dimensional sorted order.  These can then
+ *          be used to aggregate identified characters into numbers or words.
+ *          One typically wants the pixa, which contains a boxa of the
+ *          extracted subimages.
+ * 
+ */ +l_ok +recogIdentifyMultiple(L_RECOG *recog, + PIX *pixs, + l_int32 minh, + l_int32 skipsplit, + BOXA **pboxa, + PIXA **ppixa, + PIX **ppixdb, + l_int32 debugsplit) +{ +l_int32 n; +BOXA *boxa; +PIX *pixb; +PIXA *pixa; + + PROCNAME("recogIdentifyMultiple"); + + if (pboxa) *pboxa = NULL; + if (ppixa) *ppixa = NULL; + if (ppixdb) *ppixdb = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 2); + if (!recog->train_done) + return ERROR_INT("training not finished", procName, 2); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 2); + + /* Binarize if necessary */ + if (pixGetDepth(pixs) > 1) + pixb = pixConvertTo1(pixs, recog->threshold); + else + pixb = pixClone(pixs); + + /* Noise removal and splitting of touching characters */ + recogSplitIntoCharacters(recog, pixb, minh, skipsplit, &boxa, &pixa, + debugsplit); + pixDestroy(&pixb); + if (!pixa || (n = pixaGetCount(pixa)) == 0) { + pixaDestroy(&pixa); + boxaDestroy(&boxa); + L_WARNING("nothing found\n", procName); + return 1; + } + + recogIdentifyPixa(recog, pixa, ppixdb); + if (pboxa) + *pboxa = boxa; + else + boxaDestroy(&boxa); + if (ppixa) + *ppixa = pixa; + else + pixaDestroy(&pixa); + return 0; +} + + +/*------------------------------------------------------------------------* + * Segmentation and noise removal * + *------------------------------------------------------------------------*/ +/*! + * \brief recogSplitIntoCharacters() + * + * \param[in] recog + * \param[in] pixs 1 bpp, contains only mostly deskewed text + * \param[in] minh remove shorter components; use 0 for default + * \param[in] skipsplit 1 to skip the splitting step + * \param[out] pboxa character bounding boxes + * \param[out] ppixa character images + * \param[in] debug 1 for results written to pixadb_split + * \return 0 if OK, 1 on error or if no components are returned + * + *
+ * Notes:
+ *      (1) This can be given an image that has an arbitrary number
+ *          of text characters.  It optionally splits connected
+ *          components based on document image decoding in recogDecode().
+ *          The returned pixa includes the boxes from which the
+ *          (possibly split) components are extracted.
+ *      (2) After noise filtering, the resulting components are put in
+ *          row-major (2D) order, and the smaller of overlapping
+ *          components are removed if they satisfy conditions of
+ *          relative size and fractional overlap.
+ *      (3) Note that the splitting function uses unscaled templates
+ *          and does not bother returning the class results and scores.
+ *          These are more accurately found later using the scaled templates.
+ * 
+ */ +l_ok +recogSplitIntoCharacters(L_RECOG *recog, + PIX *pixs, + l_int32 minh, + l_int32 skipsplit, + BOXA **pboxa, + PIXA **ppixa, + l_int32 debug) +{ +static l_int32 ind = 0; +char buf[32]; +l_int32 i, xoff, yoff, empty, maxw, bw, ncomp, scaling; +BOX *box; +BOXA *boxa1, *boxa2, *boxa3, *boxa4, *boxad; +BOXAA *baa; +PIX *pix, *pix1, *pix2, *pix3; +PIXA *pixa; + + PROCNAME("recogSplitIntoCharacters"); + + lept_mkdir("lept/recog"); + + if (pboxa) *pboxa = NULL; + if (ppixa) *ppixa = NULL; + if (!pboxa || !ppixa) + return ERROR_INT("&boxa and &pixa not defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!recog->train_done) + return ERROR_INT("training not finished", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (minh <= 0) minh = DefaultMinHeight; + pixZero(pixs, &empty); + if (empty) return 1; + + /* Small vertical close for consolidation. Don't do a horizontal + * closing, because it might join separate characters. */ + pix1 = pixMorphSequence(pixs, "c1.3", 0); + + /* Carefully filter out noise */ + pix2 = recogPreSplittingFilter(recog, pix1, minh, MinFillFactor, debug); + pixDestroy(&pix1); + + /* Get the 8-connected components to be split/identified */ + boxa1 = pixConnComp(pix2, NULL, 8); + pixDestroy(&pix2); + ncomp = boxaGetCount(boxa1); + if (ncomp == 0) { + boxaDestroy(&boxa1); + L_WARNING("all components removed\n", procName); + return 1; + } + + /* Save everything and split the large components */ + boxa2 = boxaCreate(ncomp); + maxw = recog->maxwidth_u + 5; + scaling = (recog->scalew > 0 || recog->scaleh > 0) ? TRUE : FALSE; + pixa = (debug) ? pixaCreate(ncomp) : NULL; + for (i = 0; i < ncomp; i++) { + box = boxaGetBox(boxa1, i, L_CLONE); + boxGetGeometry(box, &xoff, &yoff, &bw, NULL); + /* Treat as one character if it is small, if the images + * have been scaled, or if splitting is not to be run. */ + if (bw <= maxw || scaling || skipsplit) { + boxaAddBox(boxa2, box, L_INSERT); + } else { + pix = pixClipRectangle(pixs, box, NULL); +#if SPLIT_WITH_DID + if (!debug) { + boxa3 = recogDecode(recog, pix, 2, NULL); + } else { + boxa3 = recogDecode(recog, pix, 2, &pix2); + pixaAddPix(pixa, pix2, L_INSERT); + } +#else /* use greedy splitting */ + recogCorrelationBestRow(recog, pix, &boxa3, NULL, NULL, + NULL, debug); + if (debug) { + pix2 = pixConvertTo32(pix); + pixRenderBoxaArb(pix2, boxa3, 2, 255, 0, 0); + pixaAddPix(pixa, pix2, L_INSERT); + } +#endif /* SPLIT_WITH_DID */ + pixDestroy(&pix); + boxDestroy(&box); + if (!boxa3) { + L_ERROR("boxa3 not found for component %d\n", procName, i); + } else { + boxa4 = boxaTransform(boxa3, xoff, yoff, 1.0, 1.0); + boxaJoin(boxa2, boxa4, 0, -1); + boxaDestroy(&boxa3); + boxaDestroy(&boxa4); + } + } + } + boxaDestroy(&boxa1); + if (pixa) { /* debug */ + pix3 = pixaDisplayTiledInColumns(pixa, 1, 1.0, 20, 2); + snprintf(buf, sizeof(buf), "/tmp/lept/recog/decode-%d.png", ind++); + pixWrite(buf, pix3, IFF_PNG); + pixaDestroy(&pixa); + pixDestroy(&pix3); + } + + /* Do a 2D sort on the bounding boxes, and flatten the result to 1D. + * For the 2D sort, to add a box to an existing boxa, we require + * specified minimum vertical overlaps for the first two passes + * of the 2D sort. In pass 1, only components with sufficient + * height can start a new boxa. */ + baa = boxaSort2d(boxa2, NULL, MinOverlap1, MinOverlap2, MinHeightPass1); + boxa3 = boxaaFlattenToBoxa(baa, NULL, L_CLONE); + boxaaDestroy(&baa); + boxaDestroy(&boxa2); + + /* Remove smaller components of overlapping pairs. + * We only remove the small component if the overlap is + * at least half its area and if its area is no more + * than 30% of the area of the large component. Because the + * components are in a flattened 2D sort, we don't need to + * look far ahead in the array to find all overlapping boxes; + * 10 boxes is plenty. */ + boxad = boxaHandleOverlaps(boxa3, L_COMBINE, 10, 0.5, 0.3, NULL); + boxaDestroy(&boxa3); + + /* Extract and save the image pieces from the input image. */ + *ppixa = pixClipRectangles(pixs, boxad); + *pboxa = boxad; + return 0; +} + + +/*------------------------------------------------------------------------* + * Greedy character splitting * + *------------------------------------------------------------------------*/ +/*! + * \brief recogCorrelationBestRow() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs typically of multiple touching characters, 1 bpp + * \param[out] pboxa bounding boxs of best fit character + * \param[out] pnascore [optional] correlation scores + * \param[out] pnaindex [optional] indices of classes + * \param[out] psachar [optional] array of character strings + * \param[in] debug 1 for results written to pixadb_split + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Supervises character matching for (in general) a c.c with
+ *          multiple touching characters.  Finds the best match greedily.
+ *          Rejects small parts that are left over after splitting.
+ *      (2) Matching is to the average, and without character scaling.
+ * 
+ */ +l_ok +recogCorrelationBestRow(L_RECOG *recog, + PIX *pixs, + BOXA **pboxa, + NUMA **pnascore, + NUMA **pnaindex, + SARRAY **psachar, + l_int32 debug) +{ +char *charstr; +l_int32 index, remove, w, h, bx, bw, bxc, bwc, w1, w2, w3; +l_float32 score; +BOX *box, *boxc, *boxtrans, *boxl, *boxr, *boxlt, *boxrt; +BOXA *boxat; +NUMA *nascoret, *naindext, *nasort; +PIX *pixb, *pixc, *pixl, *pixr, *pixdb, *pixd; +PIXA *pixar, *pixadb; +SARRAY *sachart; + +l_int32 iter; + + PROCNAME("recogCorrelationBestRow"); + + if (pnascore) *pnascore = NULL; + if (pnaindex) *pnaindex = NULL; + if (psachar) *psachar = NULL; + if (!pboxa) + return ERROR_INT("&boxa not defined", procName, 1); + *pboxa = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (pixGetWidth(pixs) < recog->minwidth_u - 4) + return ERROR_INT("pixs too narrow", procName, 1); + if (!recog->train_done) + return ERROR_INT("training not finished", procName, 1); + + /* Binarize and crop to foreground if necessary */ + pixb = recogProcessToIdentify(recog, pixs, 0); + + /* Initialize the arrays */ + boxat = boxaCreate(4); + nascoret = numaCreate(4); + naindext = numaCreate(4); + sachart = sarrayCreate(4); + pixadb = (debug) ? pixaCreate(4) : NULL; + + /* Initialize the images remaining to be processed with the input. + * These are stored in pixar, which is used here as a queue, + * on which we only put image fragments that are large enough to + * contain at least one character. */ + pixar = pixaCreate(1); + pixGetDimensions(pixb, &w, &h, NULL); + box = boxCreate(0, 0, w, h); + pixaAddPix(pixar, pixb, L_INSERT); + pixaAddBox(pixar, box, L_INSERT); + + /* Successively split on the best match until nothing is left. + * To be safe, we limit the search to 10 characters. */ + for (iter = 0; iter < 11; iter++) { + if (pixaGetCount(pixar) == 0) + break; + if (iter == 10) { + L_WARNING("more than 10 chars; ending search\n", procName); + break; + } + + /* Pop one from the queue */ + pixaRemovePixAndSave(pixar, 0, &pixc, &boxc); + boxGetGeometry(boxc, &bxc, NULL, &bwc, NULL); + + /* This is a single component; if noise, remove it */ + recogSplittingFilter(recog, pixc, 0, MinFillFactor, &remove, debug); + if (debug) + fprintf(stderr, "iter = %d, removed = %d\n", iter, remove); + if (remove) { + pixDestroy(&pixc); + boxDestroy(&boxc); + continue; + } + + /* Find the best character match */ + if (debug) { + recogCorrelationBestChar(recog, pixc, &box, &score, + &index, &charstr, &pixdb); + pixaAddPix(pixadb, pixdb, L_INSERT); + } else { + recogCorrelationBestChar(recog, pixc, &box, &score, + &index, &charstr, NULL); + } + + /* Find the box in original coordinates, and append + * the results to the arrays. */ + boxtrans = boxTransform(box, bxc, 0, 1.0, 1.0); + boxaAddBox(boxat, boxtrans, L_INSERT); + numaAddNumber(nascoret, score); + numaAddNumber(naindext, index); + sarrayAddString(sachart, charstr, L_INSERT); + + /* Split the current pixc into three regions and save + * each region if it is large enough. */ + boxGetGeometry(box, &bx, NULL, &bw, NULL); + w1 = bx; + w2 = bw; + w3 = bwc - bx - bw; + if (debug) + fprintf(stderr, " w1 = %d, w2 = %d, w3 = %d\n", w1, w2, w3); + if (w1 < recog->minwidth_u - 4) { + if (debug) L_INFO("discarding width %d on left\n", procName, w1); + } else { /* extract and save left region */ + boxl = boxCreate(0, 0, bx + 1, h); + pixl = pixClipRectangle(pixc, boxl, NULL); + boxlt = boxTransform(boxl, bxc, 0, 1.0, 1.0); + pixaAddPix(pixar, pixl, L_INSERT); + pixaAddBox(pixar, boxlt, L_INSERT); + boxDestroy(&boxl); + } + if (w3 < recog->minwidth_u - 4) { + if (debug) L_INFO("discarding width %d on right\n", procName, w3); + } else { /* extract and save left region */ + boxr = boxCreate(bx + bw - 1, 0, w3 + 1, h); + pixr = pixClipRectangle(pixc, boxr, NULL); + boxrt = boxTransform(boxr, bxc, 0, 1.0, 1.0); + pixaAddPix(pixar, pixr, L_INSERT); + pixaAddBox(pixar, boxrt, L_INSERT); + boxDestroy(&boxr); + } + pixDestroy(&pixc); + boxDestroy(&box); + boxDestroy(&boxc); + } + pixaDestroy(&pixar); + + + /* Sort the output results by left-to-right in the boxa */ + *pboxa = boxaSort(boxat, L_SORT_BY_X, L_SORT_INCREASING, &nasort); + if (pnascore) + *pnascore = numaSortByIndex(nascoret, nasort); + if (pnaindex) + *pnaindex = numaSortByIndex(naindext, nasort); + if (psachar) + *psachar = sarraySortByIndex(sachart, nasort); + numaDestroy(&nasort); + boxaDestroy(&boxat); + numaDestroy(&nascoret); + numaDestroy(&naindext); + sarrayDestroy(&sachart); + + /* Final debug output */ + if (debug) { + pixd = pixaDisplayTiledInRows(pixadb, 32, 2000, 1.0, 0, 15, 2); + pixDisplay(pixd, 400, 400); + pixaAddPix(recog->pixadb_split, pixd, L_INSERT); + pixaDestroy(&pixadb); + } + return 0; +} + + +/*! + * \brief recogCorrelationBestChar() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs can be of multiple touching characters, 1 bpp + * \param[out] pbox bounding box of best fit character + * \param[out] pscore correlation score + * \param[out] pindex [optional] index of class + * \param[out] pcharstr [optional] character string of class + * \param[out] ppixdb [optional] debug pix showing input and best fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Basic matching character splitter.  Finds the best match among
+ *          all templates to some region of the image.  This can result
+ *          in splitting the image into two parts.  This is "image decoding"
+ *          without dynamic programming, because we don't use a setwidth
+ *          and compute the best matching score for the entire image.
+ *      (2) Matching is to the average templates, without character scaling.
+ * 
+ */ +l_ok +recogCorrelationBestChar(L_RECOG *recog, + PIX *pixs, + BOX **pbox, + l_float32 *pscore, + l_int32 *pindex, + char **pcharstr, + PIX **ppixdb) +{ +l_int32 i, n, w1, h1, w2, area2, ycent2, delx, dely; +l_int32 bestdelx, bestdely, bestindex; +l_float32 score, bestscore; +BOX *box; +BOXA *boxa; +NUMA *nasum, *namoment; +PIX *pix1, *pix2; + + PROCNAME("recogCorrelationBestChar"); + + if (pindex) *pindex = 0; + if (pcharstr) *pcharstr = NULL; + if (ppixdb) *ppixdb = NULL; + if (pbox) *pbox = NULL; + if (pscore) *pscore = 0.0; + if (!pbox || !pscore) + return ERROR_INT("&box and &score not both defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (!recog->train_done) + return ERROR_INT("training not finished", procName, 1); + + /* Binarize and crop to foreground if necessary. Add padding + * to both the left and right side; this is compensated for + * when reporting the bounding box of the best matched character. */ + pix1 = recogProcessToIdentify(recog, pixs, LeftRightPadding); + pixGetDimensions(pix1, &w1, &h1, NULL); + + /* Compute vertical sum and moment arrays */ + nasum = pixCountPixelsByColumn(pix1); + namoment = pixGetMomentByColumn(pix1, 1); + + /* Do shifted correlation against all averaged templates. */ + n = recog->setsize; + boxa = boxaCreate(n); /* location of best fits for each character */ + bestscore = 0.0; + bestindex = bestdelx = bestdely = 0; + for (i = 0; i < n; i++) { + pix2 = pixaGetPix(recog->pixa_u, i, L_CLONE); + w2 = pixGetWidth(pix2); + /* Note that the slightly expended w1 is typically larger + * than w2 (the template). */ + if (w1 >= w2) { + numaGetIValue(recog->nasum_u, i, &area2); + ptaGetIPt(recog->pta_u, i, NULL, &ycent2); + pixCorrelationBestShift(pix1, pix2, nasum, namoment, area2, ycent2, + recog->maxyshift, recog->sumtab, &delx, + &dely, &score, 1); + if (ppixdb) { + fprintf(stderr, + "Best match template %d: (x,y) = (%d,%d), score = %5.3f\n", + i, delx, dely, score); + } + /* Compensate for padding */ + box = boxCreate(delx - LeftRightPadding, 0, w2, h1); + if (score > bestscore) { + bestscore = score; + bestdelx = delx - LeftRightPadding; + bestdely = dely; + bestindex = i; + } + } else { + box = boxCreate(0, 0, 1, 1); /* placeholder */ + if (ppixdb) + fprintf(stderr, "Component too thin: w1 = %d, w2 = %d\n", + w1, w2); + } + boxaAddBox(boxa, box, L_INSERT); + pixDestroy(&pix2); + } + + *pscore = bestscore; + *pbox = boxaGetBox(boxa, bestindex, L_COPY); + if (pindex) *pindex = bestindex; + if (pcharstr) + recogGetClassString(recog, bestindex, pcharstr); + + if (ppixdb) { + L_INFO("Best match: class %d; shifts (%d, %d)\n", + procName, bestindex, bestdelx, bestdely); + pix2 = pixaGetPix(recog->pixa_u, bestindex, L_CLONE); + *ppixdb = recogShowMatch(recog, pix1, pix2, NULL, -1, 0.0); + pixDestroy(&pix2); + } + + pixDestroy(&pix1); + boxaDestroy(&boxa); + numaDestroy(&nasum); + numaDestroy(&namoment); + return 0; +} + + +/*! + * \brief pixCorrelationBestShift() + * + * \param[in] pix1 1 bpp, the unknown image; typically larger + * \param[in] pix2 1 bpp, the matching template image) + * \param[in] nasum1 vertical column pixel sums for pix1 + * \param[in] namoment1 vertical column first moment of pixels for pix1 + * \param[in] area2 number of on pixels in pix2 + * \param[in] ycent2 y component of centroid of pix2 + * \param[in] maxyshift max y shift of pix2 around the location where + * the centroids of pix2 and a windowed part of pix1 + * are vertically aligned + * \param[in] tab8 [optional] sum tab for ON pixels in byte; + * can be NULL + * \param[out] pdelx [optional] best x shift of pix2 relative to pix1 + * \param[out] pdely [optional] best y shift of pix2 relative to pix1 + * \param[out] pscore [optional] maximum score found; can be NULL + * \param[in] debugflag <= 0 to skip; positive to generate output; + * the integer is used to label the debug image. + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This maximizes the correlation score between two 1 bpp images,
+ *          one of which is typically wider.  In a typical example,
+ *          pix1 is a bitmap of 2 or more touching characters and pix2 is
+ *          a single character template.  This finds the location of pix2
+ *          that gives the largest correlation.
+ *      (2) The windowed area of fg pixels and windowed first moment
+ *          in the y direction are computed from the input sum and moment
+ *          column arrays, %nasum1 and %namoment1
+ *      (3) This is a brute force operation.  We compute the correlation
+ *          at every x shift for which pix2 fits entirely within pix1,
+ *          and where the centroid of pix2 is aligned, within +-maxyshift,
+ *          with the centroid of a window of pix1 of the same width.
+ *          The correlation is taken over the full height of pix1.
+ *          This can be made more efficient.
+ * 
+ */ +static l_int32 +pixCorrelationBestShift(PIX *pix1, + PIX *pix2, + NUMA *nasum1, + NUMA *namoment1, + l_int32 area2, + l_int32 ycent2, + l_int32 maxyshift, + l_int32 *tab8, + l_int32 *pdelx, + l_int32 *pdely, + l_float32 *pscore, + l_int32 debugflag) +{ +l_int32 w1, w2, h1, h2, i, j, nx, shifty, delx, dely; +l_int32 sum, moment, count; +l_int32 *tab, *area1, *arraysum, *arraymoment; +l_float32 maxscore, score; +l_float32 *ycent1; +FPIX *fpix; +PIX *pixt, *pixt1, *pixt2; + + PROCNAME("pixCorrelationBestShift"); + + if (pdelx) *pdelx = 0; + if (pdely) *pdely = 0; + if (pscore) *pscore = 0.0; + if (!pix1 || pixGetDepth(pix1) != 1) + return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); + if (!pix2 || pixGetDepth(pix2) != 1) + return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); + if (!nasum1 || !namoment1) + return ERROR_INT("nasum1 and namoment1 not both defined", procName, 1); + if (area2 <= 0 || ycent2 <= 0) + return ERROR_INT("area2 and ycent2 must be > 0", procName, 1); + + /* If pix1 (the unknown image) is narrower than pix2, + * don't bother to try the match. pix1 is already padded with + * 2 pixels on each side. */ + pixGetDimensions(pix1, &w1, &h1, NULL); + pixGetDimensions(pix2, &w2, &h2, NULL); + if (w1 < w2) { + if (debugflag > 0) { + L_INFO("skipping match with w1 = %d and w2 = %d\n", + procName, w1, w2); + } + return 0; + } + nx = w1 - w2 + 1; + + if (debugflag > 0) + fpix = fpixCreate(nx, 2 * maxyshift + 1); + if (!tab8) + tab = makePixelSumTab8(); + else + tab = tab8; + + /* Set up the arrays for area1 and ycent1. We have to do this + * for each template (pix2) because the window width is w2. */ + area1 = (l_int32 *)LEPT_CALLOC(nx, sizeof(l_int32)); + ycent1 = (l_float32 *)LEPT_CALLOC(nx, sizeof(l_int32)); + arraysum = numaGetIArray(nasum1); + arraymoment = numaGetIArray(namoment1); + for (i = 0, sum = 0, moment = 0; i < w2; i++) { + sum += arraysum[i]; + moment += arraymoment[i]; + } + for (i = 0; i < nx - 1; i++) { + area1[i] = sum; + ycent1[i] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum; + sum += arraysum[w2 + i] - arraysum[i]; + moment += arraymoment[w2 + i] - arraymoment[i]; + } + area1[nx - 1] = sum; + ycent1[nx - 1] = (sum == 0) ? ycent2 : (l_float32)moment / (l_float32)sum; + + /* Find the best match location for pix2. At each location, + * to insure that pixels are ON only within the intersection of + * pix and the shifted pix2: + * (1) Start with pixt cleared and equal in size to pix1. + * (2) Blit the shifted pix2 onto pixt. Then all ON pixels + * are within the intersection of pix1 and the shifted pix2. + * (3) AND pix1 with pixt. */ + pixt = pixCreate(w2, h1, 1); + maxscore = 0; + delx = 0; + dely = 0; /* amount to shift pix2 relative to pix1 to get alignment */ + for (i = 0; i < nx; i++) { + shifty = (l_int32)(ycent1[i] - ycent2 + 0.5); + for (j = -maxyshift; j <= maxyshift; j++) { + pixClearAll(pixt); + pixRasterop(pixt, 0, shifty + j, w2, h2, PIX_SRC, pix2, 0, 0); + pixRasterop(pixt, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, i, 0); + pixCountPixels(pixt, &count, tab); + score = (l_float32)count * (l_float32)count / + ((l_float32)area1[i] * (l_float32)area2); + if (score > maxscore) { + maxscore = score; + delx = i; + dely = shifty + j; + } + + if (debugflag > 0) + fpixSetPixel(fpix, i, maxyshift + j, 1000.0 * score); + } + } + + if (debugflag > 0) { + lept_mkdir("lept/recog"); + char buf[128]; + pixt1 = fpixDisplayMaxDynamicRange(fpix); + pixt2 = pixExpandReplicate(pixt1, 5); + snprintf(buf, sizeof(buf), "/tmp/lept/recog/junkbs_%d.png", debugflag); + pixWrite(buf, pixt2, IFF_PNG); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + fpixDestroy(&fpix); + } + + if (pdelx) *pdelx = delx; + if (pdely) *pdely = dely; + if (pscore) *pscore = maxscore; + if (!tab8) LEPT_FREE(tab); + LEPT_FREE(area1); + LEPT_FREE(ycent1); + LEPT_FREE(arraysum); + LEPT_FREE(arraymoment); + pixDestroy(&pixt); + return 0; +} + + +/*------------------------------------------------------------------------* + * Low-level identification * + *------------------------------------------------------------------------*/ +/*! + * \brief recogIdentifyPixa() + * + * \param[in] recog + * \param[in] pixa of 1 bpp images to match + * \param[out] ppixdb [optional] pix showing inputs and best fits + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This should be called by recogIdentifyMuliple(), which
+ *          binarizes and splits characters before sending %pixa here.
+ *      (2) This calls recogIdentifyPix(), which does the same operation
+ *          on each pix in %pixa, and optionally returns the arrays
+ *          of results (scores, class index and character string)
+ *          for the best correlation match.
+ * 
+ */ +l_ok +recogIdentifyPixa(L_RECOG *recog, + PIXA *pixa, + PIX **ppixdb) +{ +char *text; +l_int32 i, n, fail, index, depth; +l_float32 score; +PIX *pix1, *pix2, *pix3; +PIXA *pixa1; +L_RCH *rch; + + PROCNAME("recogIdentifyPixa"); + + if (ppixdb) *ppixdb = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + /* Run the recognizer on the set of images. This writes + * the text string into each pix in pixa. */ + n = pixaGetCount(pixa); + rchaDestroy(&recog->rcha); + recog->rcha = rchaCreate(); + pixa1 = (ppixdb) ? pixaCreate(n) : NULL; + depth = 1; + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = NULL; + fail = FALSE; + if (!ppixdb) + fail = recogIdentifyPix(recog, pix1, NULL); + else + fail = recogIdentifyPix(recog, pix1, &pix2); + if (fail) + recogSkipIdentify(recog); + if ((rch = recog->rch) == NULL) { + L_ERROR("rch not found for char %d\n", procName, i); + pixDestroy(&pix1); + pixDestroy(&pix2); + continue; + } + rchExtract(rch, NULL, NULL, &text, NULL, NULL, NULL, NULL); + pixSetText(pix1, text); + LEPT_FREE(text); + if (ppixdb) { + rchExtract(rch, &index, &score, NULL, NULL, NULL, NULL, NULL); + pix3 = recogShowMatch(recog, pix2, NULL, NULL, index, score); + if (i == 0) depth = pixGetDepth(pix3); + pixaAddPix(pixa1, pix3, L_INSERT); + pixDestroy(&pix2); + } + transferRchToRcha(rch, recog->rcha); + pixDestroy(&pix1); + } + + /* Package the images for debug */ + if (ppixdb) { + *ppixdb = pixaDisplayTiledInRows(pixa1, depth, 2500, 1.0, 0, 20, 1); + pixaDestroy(&pixa1); + } + + return 0; +} + + +/*! + * \brief recogIdentifyPix() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs of a single character, 1 bpp + * \param[out] ppixdb [optional] debug pix showing input and best fit + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Basic recognition function for a single character.
+ *      (2) If templ_use == L_USE_ALL_TEMPLATES, which is the default
+ *          situation, matching is attempted to every bitmap in the recog,
+ *          and the identify of the best match is returned.
+ *      (3) For finding outliers, templ_use == L_USE_AVERAGE_TEMPLATES, and
+ *          matching is only attemplted to the averaged bitmaps.  For this
+ *          case, the index of the bestsample is meaningless (0 is returned
+ *          if requested).
+ *      (4) The score is related to the confidence (probability of correct
+ *          identification), in that a higher score is correlated with
+ *          a higher probability.  However, the actual relation between
+ *          the correlation (score) and the probability is not known;
+ *          we call this a "score" because "confidence" can be misinterpreted
+ *          as an actual probability.
+ * 
+ */ +l_ok +recogIdentifyPix(L_RECOG *recog, + PIX *pixs, + PIX **ppixdb) +{ +char *text; +l_int32 i, j, n, bestindex, bestsample, area1, area2; +l_int32 shiftx, shifty, bestdelx, bestdely, bestwidth, maxyshift; +l_float32 x1, y1, x2, y2, delx, dely, score, maxscore; +NUMA *numa; +PIX *pix0, *pix1, *pix2; +PIXA *pixa; +PTA *pta; + + PROCNAME("recogIdentifyPix"); + + if (ppixdb) *ppixdb = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + /* Do the averaging if required and not yet done. */ + if (recog->templ_use == L_USE_AVERAGE_TEMPLATES && !recog->ave_done) { + recogAverageSamples(&recog, 0); + if (!recog) + return ERROR_INT("averaging failed", procName, 1); + } + + /* Binarize and crop to foreground if necessary */ + if ((pix0 = recogProcessToIdentify(recog, pixs, 0)) == NULL) + return ERROR_INT("no fg pixels in pix0", procName, 1); + + /* Optionally scale and/or convert to fixed stroke width */ + pix1 = recogModifyTemplate(recog, pix0); + pixDestroy(&pix0); + if (!pix1) + return ERROR_INT("no fg pixels in pix1", procName, 1); + + /* Do correlation at all positions within +-maxyshift of + * the nominal centroid alignment. */ + pixCountPixels(pix1, &area1, recog->sumtab); + pixCentroid(pix1, recog->centtab, recog->sumtab, &x1, &y1); + bestindex = bestsample = bestdelx = bestdely = bestwidth = 0; + maxscore = 0.0; + maxyshift = recog->maxyshift; + if (recog->templ_use == L_USE_AVERAGE_TEMPLATES) { + for (i = 0; i < recog->setsize; i++) { + numaGetIValue(recog->nasum, i, &area2); + if (area2 == 0) continue; /* no template available */ + pix2 = pixaGetPix(recog->pixa, i, L_CLONE); + ptaGetPt(recog->pta, i, &x2, &y2); + delx = x1 - x2; + dely = y1 - y2; + for (shifty = -maxyshift; shifty <= maxyshift; shifty++) { + for (shiftx = -maxyshift; shiftx <= maxyshift; shiftx++) { + pixCorrelationScoreSimple(pix1, pix2, area1, area2, + delx + shiftx, dely + shifty, + 5, 5, recog->sumtab, &score); + if (score > maxscore) { + bestindex = i; + bestdelx = delx + shiftx; + bestdely = dely + shifty; + maxscore = score; + } + } + } + pixDestroy(&pix2); + } + } else { /* use all the samples */ + for (i = 0; i < recog->setsize; i++) { + pixa = pixaaGetPixa(recog->pixaa, i, L_CLONE); + n = pixaGetCount(pixa); + if (n == 0) { + pixaDestroy(&pixa); + continue; + } + numa = numaaGetNuma(recog->naasum, i, L_CLONE); + pta = ptaaGetPta(recog->ptaa, i, L_CLONE); + for (j = 0; j < n; j++) { + pix2 = pixaGetPix(pixa, j, L_CLONE); + numaGetIValue(numa, j, &area2); + ptaGetPt(pta, j, &x2, &y2); + delx = x1 - x2; + dely = y1 - y2; + for (shifty = -maxyshift; shifty <= maxyshift; shifty++) { + for (shiftx = -maxyshift; shiftx <= maxyshift; shiftx++) { + pixCorrelationScoreSimple(pix1, pix2, area1, area2, + delx + shiftx, dely + shifty, + 5, 5, recog->sumtab, &score); + if (score > maxscore) { + bestindex = i; + bestsample = j; + bestdelx = delx + shiftx; + bestdely = dely + shifty; + maxscore = score; + bestwidth = pixGetWidth(pix2); + } + } + } + pixDestroy(&pix2); + } + pixaDestroy(&pixa); + numaDestroy(&numa); + ptaDestroy(&pta); + } + } + + /* Package up the results */ + recogGetClassString(recog, bestindex, &text); + rchDestroy(&recog->rch); + recog->rch = rchCreate(bestindex, maxscore, text, bestsample, + bestdelx, bestdely, bestwidth); + + if (ppixdb) { + if (recog->templ_use == L_USE_AVERAGE_TEMPLATES) { + L_INFO("Best match: str %s; class %d; sh (%d, %d); score %5.3f\n", + procName, text, bestindex, bestdelx, bestdely, maxscore); + pix2 = pixaGetPix(recog->pixa, bestindex, L_CLONE); + } else { /* L_USE_ALL_TEMPLATES */ + L_INFO("Best match: str %s; sample %d in class %d; score %5.3f\n", + procName, text, bestsample, bestindex, maxscore); + if (maxyshift > 0 && (L_ABS(bestdelx) > 0 || L_ABS(bestdely) > 0)) { + L_INFO(" Best shift: (%d, %d)\n", + procName, bestdelx, bestdely); + } + pix2 = pixaaGetPix(recog->pixaa, bestindex, bestsample, L_CLONE); + } + *ppixdb = recogShowMatch(recog, pix1, pix2, NULL, -1, 0.0); + pixDestroy(&pix2); + } + + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief recogSkipIdentify() + * + * \param[in] recog + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This just writes a "dummy" result with 0 score and empty
+ *          string id into the rch.
+ * 
+ */ +l_ok +recogSkipIdentify(L_RECOG *recog) +{ + PROCNAME("recogSkipIdentify"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + /* Package up placeholder results */ + rchDestroy(&recog->rch); + recog->rch = rchCreate(0, 0.0, stringNew(""), 0, 0, 0, 0); + return 0; +} + + +/*------------------------------------------------------------------------* + * Operations for handling identification results * + *------------------------------------------------------------------------*/ +/*! + * \brief rchaCreate() + * + * Return: 0 if OK, 1 on error + * + * Notes: + * (1) Be sure to destroy any existing rcha before assigning this. + */ +static L_RCHA * +rchaCreate() +{ +L_RCHA *rcha; + + rcha = (L_RCHA *)LEPT_CALLOC(1, sizeof(L_RCHA)); + rcha->naindex = numaCreate(0); + rcha->nascore = numaCreate(0); + rcha->satext = sarrayCreate(0); + rcha->nasample = numaCreate(0); + rcha->naxloc = numaCreate(0); + rcha->nayloc = numaCreate(0); + rcha->nawidth = numaCreate(0); + return rcha; +} + + +/*! + * \brief rchaDestroy() + * + * \param[in,out] prcha to be nulled + */ +void +rchaDestroy(L_RCHA **prcha) +{ +L_RCHA *rcha; + + PROCNAME("rchaDestroy"); + + if (prcha == NULL) { + L_WARNING("&rcha is null!\n", procName); + return; + } + if ((rcha = *prcha) == NULL) + return; + + numaDestroy(&rcha->naindex); + numaDestroy(&rcha->nascore); + sarrayDestroy(&rcha->satext); + numaDestroy(&rcha->nasample); + numaDestroy(&rcha->naxloc); + numaDestroy(&rcha->nayloc); + numaDestroy(&rcha->nawidth); + LEPT_FREE(rcha); + *prcha = NULL; + return; +} + + +/*! + * \brief rchCreate() + * + * \param[in] index index of best template + * \param[in] score correlation score of best template + * \param[in] text character string of best template + * \param[in] sample index of best sample; -1 if averages are used + * \param[in] xloc x-location of template: delx + shiftx + * \param[in] yloc y-location of template: dely + shifty + * \param[in] width width of best template + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Be sure to destroy any existing rch before assigning this.
+ *      (2) This stores the text string, not a copy of it, so the
+ *          caller must not destroy the string.
+ * 
+ */ +static L_RCH * +rchCreate(l_int32 index, + l_float32 score, + char *text, + l_int32 sample, + l_int32 xloc, + l_int32 yloc, + l_int32 width) +{ +L_RCH *rch; + + rch = (L_RCH *)LEPT_CALLOC(1, sizeof(L_RCH)); + rch->index = index; + rch->score = score; + rch->text = text; + rch->sample = sample; + rch->xloc = xloc; + rch->yloc = yloc; + rch->width = width; + return rch; +} + + +/*! + * \brief rchDestroy() + * + * \param[in,out] prch to be nulled + */ +void +rchDestroy(L_RCH **prch) +{ +L_RCH *rch; + + PROCNAME("rchDestroy"); + + if (prch == NULL) { + L_WARNING("&rch is null!\n", procName); + return; + } + if ((rch = *prch) == NULL) + return; + LEPT_FREE(rch->text); + LEPT_FREE(rch); + *prch = NULL; + return; +} + + +/*! + * \brief rchaExtract() + * + * \param[in] rcha + * \param[out] pnaindex [optional] indices of best templates + * \param[out] pnascore [optional] correl scores of best templates + * \param[out] psatext [optional] character strings of best templates + * \param[out] pnasample [optional] indices of best samples + * \param[out] pnaxloc [optional] x-locations of templates + * \param[out] pnayloc [optional] y-locations of templates + * \param[out] pnawidth [optional] widths of best templates + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This returns clones of the number and string arrays.  They must
+ *          be destroyed by the caller.
+ * 
+ */ +l_ok +rchaExtract(L_RCHA *rcha, + NUMA **pnaindex, + NUMA **pnascore, + SARRAY **psatext, + NUMA **pnasample, + NUMA **pnaxloc, + NUMA **pnayloc, + NUMA **pnawidth) +{ + PROCNAME("rchaExtract"); + + if (pnaindex) *pnaindex = NULL; + if (pnascore) *pnascore = NULL; + if (psatext) *psatext = NULL; + if (pnasample) *pnasample = NULL; + if (pnaxloc) *pnaxloc = NULL; + if (pnayloc) *pnayloc = NULL; + if (pnawidth) *pnawidth = NULL; + if (!rcha) + return ERROR_INT("rcha not defined", procName, 1); + + if (pnaindex) *pnaindex = numaClone(rcha->naindex); + if (pnascore) *pnascore = numaClone(rcha->nascore); + if (psatext) *psatext = sarrayClone(rcha->satext); + if (pnasample) *pnasample = numaClone(rcha->nasample); + if (pnaxloc) *pnaxloc = numaClone(rcha->naxloc); + if (pnayloc) *pnayloc = numaClone(rcha->nayloc); + if (pnawidth) *pnawidth = numaClone(rcha->nawidth); + return 0; +} + + +/*! + * \brief rchExtract() + * + * \param[in] rch + * \param[out] pindex [optional] index of best template + * \param[out] pscore [optional] correlation score of best template + * \param[out] ptext [optional] character string of best template + * \param[out] psample [optional] index of best sample + * \param[out] pxloc [optional] x-location of template + * \param[out] pyloc [optional] y-location of template + * \param[out] pwidth [optional] width of best template + * \return 0 if OK, 1 on error + */ +l_ok +rchExtract(L_RCH *rch, + l_int32 *pindex, + l_float32 *pscore, + char **ptext, + l_int32 *psample, + l_int32 *pxloc, + l_int32 *pyloc, + l_int32 *pwidth) +{ + PROCNAME("rchExtract"); + + if (pindex) *pindex = 0; + if (pscore) *pscore = 0.0; + if (ptext) *ptext = NULL; + if (psample) *psample = 0; + if (pxloc) *pxloc = 0; + if (pyloc) *pyloc = 0; + if (pwidth) *pwidth = 0; + if (!rch) + return ERROR_INT("rch not defined", procName, 1); + + if (pindex) *pindex = rch->index; + if (pscore) *pscore = rch->score; + if (ptext) *ptext = stringNew(rch->text); /* new string: owned by caller */ + if (psample) *psample = rch->sample; + if (pxloc) *pxloc = rch->xloc; + if (pyloc) *pyloc = rch->yloc; + if (pwidth) *pwidth = rch->width; + return 0; +} + + +/*! + * \brief transferRchToRcha() + * + * \param[in] rch source of data + * \param[in] rcha append to arrays in this destination + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is used to transfer the results of a single character
+ *          identification to an rcha array for the array of characters.
+ * 
+ */ +static l_int32 +transferRchToRcha(L_RCH *rch, + L_RCHA *rcha) +{ + + PROCNAME("transferRchToRcha"); + + if (!rch) + return ERROR_INT("rch not defined", procName, 1); + if (!rcha) + return ERROR_INT("rcha not defined", procName, 1); + + numaAddNumber(rcha->naindex, rch->index); + numaAddNumber(rcha->nascore, rch->score); + sarrayAddString(rcha->satext, rch->text, L_COPY); + numaAddNumber(rcha->nasample, rch->sample); + numaAddNumber(rcha->naxloc, rch->xloc); + numaAddNumber(rcha->nayloc, rch->yloc); + numaAddNumber(rcha->nawidth, rch->width); + return 0; +} + + +/*------------------------------------------------------------------------* + * Preprocessing and filtering * + *------------------------------------------------------------------------*/ +/*! + * \brief recogProcessToIdentify() + * + * \param[in] recog with LUT's pre-computed + * \param[in] pixs typ. single character, possibly d > 1 and uncropped + * \param[in] pad extra pixels added to left and right sides + * \return pixd 1 bpp, clipped to foreground, or NULL if there + * are no fg pixels or on error. + * + *
+ * Notes:
+ *      (1) This is a lightweight operation to insure that the input
+ *          image is 1 bpp, properly cropped, and padded on each side.
+ *          If bpp > 1, the image is thresholded.
+ * 
+ */ +PIX * +recogProcessToIdentify(L_RECOG *recog, + PIX *pixs, + l_int32 pad) +{ +l_int32 canclip; +PIX *pix1, *pix2, *pixd; + + PROCNAME("recogProcessToIdentify"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (pixGetDepth(pixs) != 1) + pix1 = pixThresholdToBinary(pixs, recog->threshold); + else + pix1 = pixClone(pixs); + pixTestClipToForeground(pix1, &canclip); + if (canclip) + pixClipToForeground(pix1, &pix2, NULL); + else + pix2 = pixClone(pix1); + pixDestroy(&pix1); + if (!pix2) + return (PIX *)ERROR_PTR("no foreground pixels", procName, NULL); + + pixd = pixAddBorderGeneral(pix2, pad, pad, 0, 0, 0); + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief recogPreSplittingFilter() + * + * \param[in] recog + * \param[in] pixs 1 bpp, many connected components + * \param[in] minh minimum height of components to be retained + * \param[in] minaf minimum area fraction (|fg|/(w*h)) to be retained + * \param[in] debug 1 to output indicator arrays + * \return pixd with filtered components removed or NULL on error + */ +static PIX * +recogPreSplittingFilter(L_RECOG *recog, + PIX *pixs, + l_int32 minh, + l_float32 minaf, + l_int32 debug) +{ +l_int32 scaling, minsplitw, maxsplith, maxasp; +BOXA *boxas; +NUMA *naw, *nah, *na1, *na1c, *na2, *na3, *na4, *na5, *na6, *na7; +PIX *pixd; +PIXA *pixas; + + PROCNAME("recogPreSplittingFilter"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* If there is scaling, do not remove components based on the + * values of min_splitw and max_splith. */ + scaling = (recog->scalew > 0 || recog->scaleh > 0) ? TRUE : FALSE; + minsplitw = (scaling) ? 1 : recog->min_splitw - 3; + maxsplith = (scaling) ? 150 : recog->max_splith; + maxasp = recog->max_wh_ratio; + + /* Generate an indicator array of connected components to remove: + * short stuff + * tall stuff + * components with large width/height ratio + * components with small area fill fraction */ + boxas = pixConnComp(pixs, &pixas, 8); + pixaFindDimensions(pixas, &naw, &nah); + na1 = numaMakeThresholdIndicator(naw, minsplitw, L_SELECT_IF_LT); + na1c = numaCopy(na1); + na2 = numaMakeThresholdIndicator(nah, minh, L_SELECT_IF_LT); + na3 = numaMakeThresholdIndicator(nah, maxsplith, L_SELECT_IF_GT); + na4 = pixaFindWidthHeightRatio(pixas); + na5 = numaMakeThresholdIndicator(na4, maxasp, L_SELECT_IF_GT); + na6 = pixaFindAreaFraction(pixas); + na7 = numaMakeThresholdIndicator(na6, minaf, L_SELECT_IF_LT); + numaLogicalOp(na1, na1, na2, L_UNION); + numaLogicalOp(na1, na1, na3, L_UNION); + numaLogicalOp(na1, na1, na5, L_UNION); + numaLogicalOp(na1, na1, na7, L_UNION); + pixd = pixCopy(NULL, pixs); + pixRemoveWithIndicator(pixd, pixas, na1); + if (debug) + l_showIndicatorSplitValues(na1c, na2, na3, na5, na7, na1); + numaDestroy(&naw); + numaDestroy(&nah); + numaDestroy(&na1); + numaDestroy(&na1c); + numaDestroy(&na2); + numaDestroy(&na3); + numaDestroy(&na4); + numaDestroy(&na5); + numaDestroy(&na6); + numaDestroy(&na7); + boxaDestroy(&boxas); + pixaDestroy(&pixas); + return pixd; +} + + +/*! + * \brief recogSplittingFilter() + * + * \param[in] recog + * \param[in] pixs 1 bpp, single connected component + * \param[in] minh minimum height of component; 0 for default + * \param[in] minaf minimum area fraction (|fg|/(w*h)) to be retained + * \param[out] premove 0 to save, 1 to remove + * \param[in] debug 1 to output indicator arrays + * \return 0 if OK, 1 on error + */ +static l_int32 +recogSplittingFilter(L_RECOG *recog, + PIX *pixs, + l_int32 minh, + l_float32 minaf, + l_int32 *premove, + l_int32 debug) +{ +l_int32 w, h; +l_float32 aspratio, fract; + + PROCNAME("recogSplittingFilter"); + + if (!premove) + return ERROR_INT("&remove not defined", procName, 1); + *premove = 0; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (minh <= 0) minh = DefaultMinHeight; + + /* Remove from further consideration: + * small stuff + * components with large width/height ratio + * components with small area fill fraction */ + pixGetDimensions(pixs, &w, &h, NULL); + if (w < recog->min_splitw) { + if (debug) L_INFO("w = %d < %d\n", procName, w, recog->min_splitw); + *premove = 1; + return 0; + } + if (h < minh) { + if (debug) L_INFO("h = %d < %d\n", procName, h, minh); + *premove = 1; + return 0; + } + aspratio = (l_float32)w / (l_float32)h; + if (aspratio > recog->max_wh_ratio) { + if (debug) L_INFO("w/h = %5.3f too large\n", procName, aspratio); + *premove = 1; + return 0; + } + pixFindAreaFraction(pixs, recog->sumtab, &fract); + if (fract < minaf) { + if (debug) L_INFO("area fill fract %5.3f < %5.3f\n", + procName, fract, minaf); + *premove = 1; + return 0; + } + + return 0; +} + + +/*------------------------------------------------------------------------* + * Postprocessing * + *------------------------------------------------------------------------*/ +/*! + * \brief recogExtractNumbers() + * + * \param[in] recog + * \param[in] boxas location of components + * \param[in] scorethresh min score for which we accept a component + * \param[in] spacethresh max horizontal distance allowed between digits; + * use -1 for default + * \param[out] pbaa [optional] bounding boxes of identified numbers + * \param[out] pnaa [optional] scores of identified digits + * \return sa of identified numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) This extracts digit data after recogaIdentifyMultiple() or
+ *          lower-level identification has taken place.
+ *      (2) Each string in the returned sa contains a sequence of ascii
+ *          digits in a number.
+ *      (3) The horizontal distance between boxes (limited by %spacethresh)
+ *          is the negative of the horizontal overlap.
+ *      (4) Components with a score less than %scorethresh, which may
+ *          be hyphens or other small characters, will signal the
+ *          end of the current sequence of digits in the number.  A typical
+ *          value for %scorethresh is 0.60.
+ *      (5) We allow two digits to be combined if these conditions apply:
+ *            (a) the first is to the left of the second
+ *            (b) the second has a horizontal separation less than %spacethresh
+ *            (c) the vertical overlap >= 0 (vertical separation < 0)
+ *            (d) both have a score that exceeds %scorethresh
+ *      (6) Each numa in the optionally returned naa contains the digit
+ *          scores of a number.  Each boxa in the optionally returned baa
+ *          contains the bounding boxes of the digits in the number.
+ * 
+ */ +SARRAY * +recogExtractNumbers(L_RECOG *recog, + BOXA *boxas, + l_float32 scorethresh, + l_int32 spacethresh, + BOXAA **pbaa, + NUMAA **pnaa) +{ +char *str, *text; +l_int32 i, n, x1, x2, h_ovl, v_ovl, h_sep, v_sep; +l_float32 score; +BOX *box, *prebox; +BOXA *ba; +BOXAA *baa; +NUMA *nascore, *na; +NUMAA *naa; +SARRAY *satext, *sa, *saout; + + PROCNAME("recogExtractNumbers"); + + if (pbaa) *pbaa = NULL; + if (pnaa) *pnaa = NULL; + if (!recog || !recog->rcha) + return (SARRAY *)ERROR_PTR("recog and rcha not both defined", + procName, NULL); + if (!boxas) + return (SARRAY *)ERROR_PTR("boxas not defined", procName, NULL); + + if (spacethresh < 0) + spacethresh = L_MAX(recog->maxheight_u, 20); + rchaExtract(recog->rcha, NULL, &nascore, &satext, NULL, NULL, NULL, NULL); + if (!nascore || !satext) { + numaDestroy(&nascore); + sarrayDestroy(&satext); + return (SARRAY *)ERROR_PTR("nascore and satext not both returned", + procName, NULL); + } + + saout = sarrayCreate(0); + naa = numaaCreate(0); + baa = boxaaCreate(0); + prebox = NULL; + n = numaGetCount(nascore); + for (i = 0; i < n; i++) { + numaGetFValue(nascore, i, &score); + text = sarrayGetString(satext, i, L_NOCOPY); + if (prebox == NULL) { /* no current run */ + if (score < scorethresh) { + continue; + } else { /* start a number run */ + sa = sarrayCreate(0); + ba = boxaCreate(0); + na = numaCreate(0); + sarrayAddString(sa, text, L_COPY); + prebox = boxaGetBox(boxas, i, L_CLONE); + boxaAddBox(ba, prebox, L_COPY); + numaAddNumber(na, score); + } + } else { /* in a current number run */ + box = boxaGetBox(boxas, i, L_CLONE); + boxGetGeometry(prebox, &x1, NULL, NULL, NULL); + boxGetGeometry(box, &x2, NULL, NULL, NULL); + boxOverlapDistance(box, prebox, &h_ovl, &v_ovl); + h_sep = -h_ovl; + v_sep = -v_ovl; + boxDestroy(&prebox); + if (x1 < x2 && h_sep <= spacethresh && + v_sep < 0 && score >= scorethresh) { /* add to number */ + sarrayAddString(sa, text, L_COPY); + boxaAddBox(ba, box, L_COPY); + numaAddNumber(na, score); + prebox = box; + } else { /* save the completed number */ + str = sarrayToString(sa, 0); + sarrayAddString(saout, str, L_INSERT); + sarrayDestroy(&sa); + boxaaAddBoxa(baa, ba, L_INSERT); + numaaAddNuma(naa, na, L_INSERT); + boxDestroy(&box); + if (score >= scorethresh) { /* start a new number */ + i--; + continue; + } + } + } + } + + if (prebox) { /* save the last number */ + str = sarrayToString(sa, 0); + sarrayAddString(saout, str, L_INSERT); + boxaaAddBoxa(baa, ba, L_INSERT); + numaaAddNuma(naa, na, L_INSERT); + sarrayDestroy(&sa); + boxDestroy(&prebox); + } + + numaDestroy(&nascore); + sarrayDestroy(&satext); + if (sarrayGetCount(saout) == 0) { + sarrayDestroy(&saout); + boxaaDestroy(&baa); + numaaDestroy(&naa); + L_INFO("saout has no identified text\n", procName); + return NULL; + } + + if (pbaa) + *pbaa = baa; + else + boxaaDestroy(&baa); + if (pnaa) + *pnaa = naa; + else + numaaDestroy(&naa); + return saout; +} + +/*! + * \brief showExtractNumbers() + * + * \param[in] pixs input 1 bpp image + * \param[in] sa recognized text strings + * \param[in] baa boxa array for location of characters in each string + * \param[in] naa numa array for scores of characters in each string + * \param[out] ppixdb [optional] input pixs with identified chars outlined + * \return pixa of identified strings with text and scores, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a debugging routine on digit identification; e.g.:
+ *            recogIdentifyMultiple(recog, pixs, 0, 1, &boxa, NULL, NULL, 0);
+ *            sa = recogExtractNumbers(recog, boxa, 0.8, -1, &baa, &naa);
+ *            pixa = showExtractNumbers(pixs, sa, baa, naa, NULL);
+ * 
+ */ +PIXA * +showExtractNumbers(PIX *pixs, + SARRAY *sa, + BOXAA *baa, + NUMAA *naa, + PIX **ppixdb) +{ +char buf[128]; +char *textstr, *scorestr; +l_int32 i, j, n, nchar, len; +l_float32 score; +L_BMF *bmf; +BOX *box1, *box2; +BOXA *ba; +NUMA *na; +PIX *pix1, *pix2, *pix3, *pix4; +PIXA *pixa; + + PROCNAME("showExtractNumbers"); + + if (ppixdb) *ppixdb = NULL; + if (!pixs) + return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); + if (!sa) + return (PIXA *)ERROR_PTR("sa not defined", procName, NULL); + if (!baa) + return (PIXA *)ERROR_PTR("baa not defined", procName, NULL); + if (!naa) + return (PIXA *)ERROR_PTR("naa not defined", procName, NULL); + + n = sarrayGetCount(sa); + pixa = pixaCreate(n); + bmf = bmfCreate(NULL, 6); + if (ppixdb) *ppixdb = pixConvertTo8(pixs, 1); + for (i = 0; i < n; i++) { + textstr = sarrayGetString(sa, i, L_NOCOPY); + ba = boxaaGetBoxa(baa, i, L_CLONE); + na = numaaGetNuma(naa, i, L_CLONE); + boxaGetExtent(ba, NULL, NULL, &box1); + box2 = boxAdjustSides(NULL, box1, -5, 5, -5, 5); + if (ppixdb) pixRenderBoxArb(*ppixdb, box2, 3, 255, 0, 0); + pix1 = pixClipRectangle(pixs, box1, NULL); + len = strlen(textstr) + 1; + pix2 = pixAddBlackOrWhiteBorder(pix1, 14 * len, 14 * len, + 5, 3, L_SET_WHITE); + pix3 = pixConvertTo8(pix2, 1); + nchar = numaGetCount(na); + scorestr = NULL; + for (j = 0; j < nchar; j++) { + numaGetFValue(na, j, &score); + snprintf(buf, sizeof(buf), "%d", (l_int32)(100 * score)); + stringJoinIP(&scorestr, buf); + if (j < nchar - 1) stringJoinIP(&scorestr, ","); + } + snprintf(buf, sizeof(buf), "%s: %s\n", textstr, scorestr); + pix4 = pixAddTextlines(pix3, bmf, buf, 0xff000000, L_ADD_BELOW); + pixaAddPix(pixa, pix4, L_INSERT); + boxDestroy(&box1); + boxDestroy(&box2); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + boxaDestroy(&ba); + numaDestroy(&na); + LEPT_FREE(scorestr); + } + + bmfDestroy(&bmf); + return pixa; +} + + +/*------------------------------------------------------------------------* + * Static debug helper * + *------------------------------------------------------------------------*/ +/*! + * \brief l_showIndicatorSplitValues() + * + * \param[in] na1, na2, na3, na4, na5, na6 6 indicator array + * + *
+ * Notes:
+ *      (1) The values indicate that specific criteria has been met
+ *          for component removal by pre-splitting filter..
+ *          The 'result' line shows which components have been removed.
+ * 
+ */ +static void +l_showIndicatorSplitValues(NUMA *na1, + NUMA *na2, + NUMA *na3, + NUMA *na4, + NUMA *na5, + NUMA *na6) +{ +l_int32 i, n; + + n = numaGetCount(na1); + fprintf(stderr, "================================================\n"); + fprintf(stderr, "lt minw: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na1->array[i]); + fprintf(stderr, "\nlt minh: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na2->array[i]); + fprintf(stderr, "\ngt maxh: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na3->array[i]); + fprintf(stderr, "\ngt maxasp: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na4->array[i]); + fprintf(stderr, "\nlt minaf: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na5->array[i]); + fprintf(stderr, "\n------------------------------------------------"); + fprintf(stderr, "\nresult: "); + for (i = 0; i < n; i++) + fprintf(stderr, "%4d ", (l_int32)na6->array[i]); + fprintf(stderr, "\n================================================\n"); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/recogtrain.c b/hgdriver/3rdparty/hgOCR/leptonica/recogtrain.c new file mode 100644 index 0000000..e6f207e --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/recogtrain.c @@ -0,0 +1,2478 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file recogtrain.c + *
+ *
+ *      Training on labeled data
+ *         l_int32             recogTrainLabeled()
+ *         PIX                *recogProcessLabeled()
+ *         l_int32             recogAddSample()
+ *         PIX                *recogModifyTemplate()
+ *         l_int32             recogAverageSamples()
+ *         l_int32             pixaAccumulateSamples()
+ *         l_int32             recogTrainingFinished()
+ *         static l_int32      recogTemplatesAreOK()
+ *         PIXA               *recogFilterPixaBySize()
+ *         PIXAA              *recogSortPixaByClass()
+ *         l_int32             recogRemoveOutliers1()
+ *         PIXA               *pixaRemoveOutliers1()
+ *         l_int32             recogRemoveOutliers2()
+ *         PIXA               *pixaRemoveOutliers2()
+ *
+ *      Training on unlabeled data
+ *         L_RECOG             recogTrainFromBoot()
+ *
+ *      Padding the digit training set
+ *         l_int32             recogPadDigitTrainingSet()
+ *         l_int32             recogIsPaddingNeeded()
+ *         static SARRAY      *recogAddMissingClassStrings()
+ *         PIXA               *recogAddDigitPadTemplates()
+ *         static l_int32      recogCharsetAvailable()
+ *
+ *      Making a boot digit recognizer
+ *         L_RECOG            *recogMakeBootDigitRecog()
+ *         PIXA               *recogMakeBootDigitTemplates()
+ *
+ *      Debugging
+ *         l_int32             recogShowContent()
+ *         l_int32             recogDebugAverages()
+ *         l_int32             recogShowAverageTemplates()
+ *         static PIX         *pixDisplayOutliers()
+ *         PIX                *recogDisplayOutlier()
+ *         PIX                *recogShowMatchesInRange()
+ *         PIX                *recogShowMatch()
+ *
+ *  These abbreviations are for the type of template to be used:
+ *    * SI (for the scanned images)
+ *    * WNL (for width-normalized lines, formed by first skeletonizing
+ *           the scanned images, and then dilating to a fixed width)
+ *  These abbreviations are for the type of recognizer:
+ *    * BAR (book-adapted recognizer; the best type; can do identification
+ *           with unscaled images and separation of touching characters.
+ *    * BSR (bootstrap recognizer; used if more labeled templates are
+ *           required for a BAR, either for finding more templates from
+ *           the book, or making a hybrid BAR/BSR.
+ *
+ *  The recog struct typically holds two versions of the input templates
+ *  (e.g. from a pixa) that were used to generate it.  One version is
+ *  the unscaled input templates.  The other version is the one that
+ *  will be used by the recog to identify unlabeled data.  That version
+ *  depends on the input parameters when the recog is created.  The choices
+ *  for the latter version, and their suggested use, are:
+ *  (1) unscaled SI -- typical for BAR, generated from book images
+ *  (2) unscaled WNL -- ditto
+ *  (3) scaled SI -- typical for recognizers containing template
+ *      images from sources other than the book to be recognized
+ *  (4) scaled WNL -- ditto
+ *  For cases (3) and (4), we recommend scaling to fixed height; e.g.,
+ *  scalew = 0, scaleh = 40.
+ *  When using WNL, we recommend using a width of 5 in the template
+ *  and 4 in the unlabeled data.
+ *  It appears that better results for a BAR are usually obtained using
+ *  SI than WNL, but more experimentation is needed.
+ *
+ *  This utility is designed to build recognizers that are specifically
+ *  adapted from a large amount of material, such as a book.  These
+ *  use labeled templates taken from the material, and not scaled.
+ *  In addition, two special recognizers are useful:
+ *  (1) Bootstrap recognizer (BSR).  This uses height-scaled templates,
+ *      that have been extended with several repetitions in one of two ways:
+ *      (a) aniotropic width scaling (for either SI or WNL)
+ *      (b) iterative erosions/dilations (for SI).
+ *  (2) Outlier removal.  This uses height scaled templates.  It can be
+ *      implemented without using templates that are aligned averages of all
+ *      templates in a class.
+ *
+ *  Recognizers are inexpensive to generate, for example, from a pixa
+ *  of labeled templates.  The general process of building a BAR is
+ *  to start with labeled templates, e.g., in a pixa, make a BAR, and
+ *  analyze new samples from the book to augment the BAR until it has
+ *  enough samples for each character class.  Along the way, samples
+ *  from a BSR may be added for help in training.  If not enough samples
+ *  are available for the BAR, it can finally be augmented with BSR
+ *  samples, in which case the resulting hybrid BAR/BSR recognizer
+ *  must work on scaled images.
+ *
+ *  Here are the steps in doing recog training:
+ *  A. Generate a BAR from any existing labeled templates
+ *    (1) Create a recog and add the templates, using recogAddSample().
+ *        This stores the unscaled templates.
+ *        [Note: this can be done in one step if the labeled templates are put
+ *         into a pixa:
+ *           L_Recog *rec = recogCreateFromPixa(pixa, ...);  ]
+ *    (2) Call recogTrainingFinished() to generate the (sometimes modified)
+ *        templates to be used for correlation.
+ *    (3) Optionally, remove outliers.
+ *    If there are sufficient samples in the classes, we're done. Otherwise,
+ *  B. Try to get more samples from the book to pad the BAR.
+ *     (1) Save the unscaled, labeled templates from the BAR.
+ *     (2) Supplement the BAR with bootstrap templates to make a hybrid BAR/BSR.
+ *     (3) Do recognition on more unlabeled images, scaled to a fixed height
+ *     (4) Add the unscaled, labeled images to the saved set.
+ *     (5) Optionally, remove outliers.
+ *     If there are sufficient samples in the classes, we're done. Otherwise,
+ *  C. For classes without a sufficient number of templates, we can
+ *     supplement the BAR with templates from a BSR (a hybrid RAR/BSR),
+ *     and do recognition scaled to a fixed height.
+ *
+ *  Here are several methods that can be used for identifying outliers:
+ *  (1) Compute average templates for each class and remove a candidate
+ *      that is poorly correlated with the average.  This is the most
+ *      simple method.  recogRemoveOutliers1() uses this, supplemented with
+ *      a second threshold and a target number of templates to be saved.
+ *  (2) Compute average templates for each class and remove a candidate
+ *      that is more highly correlated with the average of some other class.
+ *      This does not require setting a threshold for the correlation.
+ *      recogRemoveOutliers2() uses this method, supplemented with a minimum
+ *      correlation score.
+ *  (3) For each candidate, find the average correlation with other
+ *      members of its class, and remove those that have a relatively
+ *      low average correlation.  This is similar to (1), gives comparable
+ *      results and because it does not use average templates, it requires
+ *      a bit more computation.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Static functions */ +static l_int32 recogTemplatesAreOK(L_RECOG *recog, l_int32 minsize, + l_float32 minfract, l_int32 *pok); +static SARRAY *recogAddMissingClassStrings(L_RECOG *recog); +static l_int32 recogCharsetAvailable(l_int32 type); +static PIX *pixDisplayOutliers(PIXA *pixas, NUMA *nas); +static PIX *recogDisplayOutlier(L_RECOG *recog, l_int32 iclass, l_int32 jsamp, + l_int32 maxclass, l_float32 maxscore); + + /* Default parameters that are used in recogTemplatesAreOK() and + * in outlier removal functions, and that use template set size + * to decide if the set of templates (before outliers are removed) + * is valid. Values are set to accept most sets of sample templates. */ +static const l_int32 DefaultMinSetSize = 1; /* minimum number of + samples for a valid class */ +static const l_float32 DefaultMinSetFract = 0.4; /* minimum fraction + of classes required for a valid recog */ + + /* Defaults in pixaRemoveOutliers1() and pixaRemoveOutliers2() */ +static const l_float32 DefaultMinScore = 0.75; /* keep everything above */ +static const l_int32 DefaultMinTarget = 3; /* to be kept if possible */ +static const l_float32 LowerScoreThreshold = 0.5; /* templates can be + * kept down to this score to if needed to retain the + * desired minimum number of templates */ + + +/*------------------------------------------------------------------------* + * Training * + *------------------------------------------------------------------------*/ +/*! + * \brief recogTrainLabeled() + * + * \param[in] recog in training mode + * \param[in] pixs if depth > 1, will be thresholded to 1 bpp + * \param[in] box [optional] cropping box + * \param[in] text [optional] if null, use text field in pix + * \param[in] debug 1 to display images of samples not captured + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Training is restricted to the addition of a single
+ *          character in an arbitrary (e.g., UTF8) charset
+ *      (2) If box != null, it should represent the location in %pixs
+ *          of the character image.
+ * 
+ */ +l_ok +recogTrainLabeled(L_RECOG *recog, + PIX *pixs, + BOX *box, + char *text, + l_int32 debug) +{ +l_int32 ret; +PIX *pix; + + PROCNAME("recogTrainLabeled"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Prepare the sample to be added. This step also acts + * as a filter, and can invalidate pixs as a template. */ + ret = recogProcessLabeled(recog, pixs, box, text, &pix); + if (ret) { + pixDestroy(&pix); + L_WARNING("failure to get sample '%s' for training\n", procName, + text); + return 1; + } + + recogAddSample(recog, pix, debug); + pixDestroy(&pix); + return 0; +} + + +/*! + * \brief recogProcessLabeled() + * + * \param[in] recog in training mode + * \param[in] pixs if depth > 1, will be thresholded to 1 bpp + * \param[in] box [optional] cropping box + * \param[in] text [optional] if null, use text field in pix + * \param[out] ppix addr of pix, 1 bpp, labeled + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This crops and binarizes the input image, generating a pix
+ *          of one character where the charval is inserted into the pix.
+ * 
+ */ +l_ok +recogProcessLabeled(L_RECOG *recog, + PIX *pixs, + BOX *box, + char *text, + PIX **ppix) +{ +char *textdata; +l_int32 textinpix, textin, nsets; +NUMA *na; +PIX *pix1, *pix2, *pix3, *pix4; + + PROCNAME("recogProcessLabeled"); + + if (!ppix) + return ERROR_INT("&pix not defined", procName, 1); + *ppix = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Find the text; this will be stored with the output images */ + textin = text && (text[0] != '\0'); + textinpix = (pixs->text && (pixs->text[0] != '\0')); + if (!textin && !textinpix) { + L_ERROR("no text: %d\n", procName, recog->num_samples); + return 1; + } + textdata = (textin) ? text : pixs->text; /* do not free */ + + /* Crop and binarize if necessary */ + if (box) + pix1 = pixClipRectangle(pixs, box, NULL); + else + pix1 = pixClone(pixs); + if (pixGetDepth(pix1) > 1) + pix2 = pixConvertTo1(pix1, recog->threshold); + else + pix2 = pixClone(pix1); + pixDestroy(&pix1); + + /* Remove isolated noise, using as a criterion all components + * that are removed by a vertical opening of size 5. */ + pix3 = pixMorphSequence(pix2, "o1.5", 0); /* seed */ + pixSeedfillBinary(pix3, pix3, pix2, 8); /* fill from seed; clip to pix2 */ + pixDestroy(&pix2); + + /* Clip to foreground */ + pixClipToForeground(pix3, &pix4, NULL); + pixDestroy(&pix3); + if (!pix4) + return ERROR_INT("pix4 is empty", procName, 1); + + /* Verify that if there is more than 1 c.c., they all have + * horizontal overlap */ + na = pixCountByColumn(pix4, NULL); + numaCountNonzeroRuns(na, &nsets); + numaDestroy(&na); + if (nsets > 1) { + L_WARNING("found %d sets of horiz separated c.c.; skipping\n", + procName, nsets); + pixDestroy(&pix4); + return 1; + } + + pixSetText(pix4, textdata); + *ppix = pix4; + return 0; +} + + +/*! + * \brief recogAddSample() + * + * \param[in] recog + * \param[in] pix a single character, 1 bpp + * \param[in] debug + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The pix is 1 bpp, with the character string label embedded.
+ *      (2) The pixaa_u array of the recog is initialized to accept
+ *          up to 256 different classes.  When training is finished,
+ *          the arrays are truncated to the actual number of classes.
+ *          To pad an existing recog from the boot recognizers, training
+ *          is started again; if samples from a new class are added,
+ *          the pixaa_u array is extended by adding a pixa to hold them.
+ * 
+ */ +l_ok +recogAddSample(L_RECOG *recog, + PIX *pix, + l_int32 debug) +{ +char *text; +l_int32 npa, charint, index; +PIXA *pixa1; +PIXAA *paa; + + PROCNAME("recogAddSample"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp\n", procName, 1); + if (recog->train_done) + return ERROR_INT("not added: training has been completed", procName, 1); + paa = recog->pixaa_u; + + /* Make sure the character is in the set */ + text = pixGetText(pix); + if (l_convertCharstrToInt(text, &charint) == 1) { + L_ERROR("invalid text: %s\n", procName, text); + return 1; + } + + /* Determine the class array index. Check if the class + * alreadly exists, and if not, add it. */ + if (recogGetClassIndex(recog, charint, text, &index) == 1) { + /* New class must be added */ + npa = pixaaGetCount(paa, NULL); + if (index > npa) { + L_ERROR("oops: bad index %d > npa %d!!\n", procName, index, npa); + return 1; + } + if (index == npa) { /* paa needs to be extended */ + L_INFO("Adding new class and pixa: index = %d, text = %s\n", + procName, index, text); + pixa1 = pixaCreate(10); + pixaaAddPixa(paa, pixa1, L_INSERT); + } + } + if (debug) { + L_INFO("Identified text label: %s\n", procName, text); + L_INFO("Identified: charint = %d, index = %d\n", + procName, charint, index); + } + + /* Insert the unscaled character image into the right pixa. + * (Unscaled images are required to split touching characters.) */ + recog->num_samples++; + pixaaAddPix(paa, index, pix, NULL, L_COPY); + return 0; +} + + +/*! + * \brief recogModifyTemplate() + * + * \param[in] recog + * \param[in] pixs 1 bpp, to be optionally scaled and turned into + * strokes of fixed width + * \return pixd modified pix if OK, NULL on error + */ +PIX * +recogModifyTemplate(L_RECOG *recog, + PIX *pixs) +{ +l_int32 w, h, empty; +PIX *pix1, *pix2; + + PROCNAME("recogModifyTemplate"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Scale first */ + pixGetDimensions(pixs, &w, &h, NULL); + if ((recog->scalew == 0 || recog->scalew == w) && + (recog->scaleh == 0 || recog->scaleh == h)) { /* no scaling */ + pix1 = pixCopy(NULL, pixs); + } else { + pix1 = pixScaleToSize(pixs, recog->scalew, recog->scaleh); + } + if (!pix1) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + + /* Then optionally convert to lines */ + if (recog->linew <= 0) { + pix2 = pixClone(pix1); + } else { + pix2 = pixSetStrokeWidth(pix1, recog->linew, 1, 8); + } + pixDestroy(&pix1); + if (!pix2) + return (PIX *)ERROR_PTR("pix2 not made", procName, NULL); + + /* Make sure we still have some pixels */ + pixZero(pix2, &empty); + if (empty) { + pixDestroy(&pix2); + return (PIX *)ERROR_PTR("modified template has no pixels", + procName, NULL); + } + return pix2; +} + + +/*! + * \brief recogAverageSamples() + * + * \param[in] precog addr of existing recog; may be destroyed + * \param[in] debug + * \return 0 on success, 1 on failure + * + *
+ * Notes:
+ *      (1) This is only called in two situations:
+ *          (a) When splitting characters using either the DID method
+ *              recogDecode() or the the greedy splitter
+ *              recogCorrelationBestRow()
+ *          (b) By a special recognizer that is used to remove outliers.
+ *          Both unscaled and scaled inputs are averaged.
+ *      (2) If the data in any class is nonexistent (no samples), or
+ *          very bad (no fg pixels in the average), or if the ratio
+ *          of max/min average unscaled class template heights is
+ *          greater than max_ht_ratio, this destroys the recog.
+ *          The caller must check the return value of the recog.
+ *      (3) Set debug = 1 to view the resulting templates and their centroids.
+ * 
+ */ +l_int32 +recogAverageSamples(L_RECOG **precog, + l_int32 debug) +{ +l_int32 i, nsamp, size, area, bx, by, badclass; +l_float32 x, y, hratio; +BOX *box; +PIXA *pixa1; +PIX *pix1, *pix2, *pix3; +PTA *pta1; +L_RECOG *recog; + + PROCNAME("recogAverageSamples"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if ((recog = *precog) == NULL) + return ERROR_INT("recog not defined", procName, 1); + + if (recog->ave_done) { + if (debug) /* always do this if requested */ + recogShowAverageTemplates(recog); + return 0; + } + + /* Remove any previous averaging data */ + size = recog->setsize; + pixaDestroy(&recog->pixa_u); + ptaDestroy(&recog->pta_u); + numaDestroy(&recog->nasum_u); + recog->pixa_u = pixaCreate(size); + recog->pta_u = ptaCreate(size); + recog->nasum_u = numaCreate(size); + + pixaDestroy(&recog->pixa); + ptaDestroy(&recog->pta); + numaDestroy(&recog->nasum); + recog->pixa = pixaCreate(size); + recog->pta = ptaCreate(size); + recog->nasum = numaCreate(size); + + /* Unscaled bitmaps: compute averaged bitmap, centroid, and fg area. + * Note that when we threshold to 1 bpp the 8 bpp averaged template + * that is returned from the accumulator, it will not be cropped + * to the foreground. We must crop it, because the correlator + * makes that assumption and will return a zero value if the + * width or height of the two images differs by several pixels. + * But cropping to fg can cause the value of the centroid to + * change, if bx > 0 or by > 0. */ + badclass = FALSE; + for (i = 0; i < size; i++) { + pixa1 = pixaaGetPixa(recog->pixaa_u, i, L_CLONE); + pta1 = ptaaGetPta(recog->ptaa_u, i, L_CLONE); + nsamp = pixaGetCount(pixa1); + nsamp = L_MIN(nsamp, 256); /* we only use the first 256 */ + if (nsamp == 0) { /* no information for this class */ + L_ERROR("no samples in class %d\n", procName, i); + badclass = TRUE; + pixaDestroy(&pixa1); + ptaDestroy(&pta1); + break; + } else { + pixaAccumulateSamples(pixa1, pta1, &pix1, &x, &y); + pix2 = pixThresholdToBinary(pix1, L_MAX(1, nsamp / 2)); + pixInvert(pix2, pix2); + pixClipToForeground(pix2, &pix3, &box); + if (!box) { + L_ERROR("no fg pixels in average for uclass %d\n", procName, i); + badclass = TRUE; + pixDestroy(&pix1); + pixDestroy(&pix2); + pixaDestroy(&pixa1); + ptaDestroy(&pta1); + break; + } else { + boxGetGeometry(box, &bx, &by, NULL, NULL); + pixaAddPix(recog->pixa_u, pix3, L_INSERT); + ptaAddPt(recog->pta_u, x - bx, y - by); /* correct centroid */ + pixCountPixels(pix3, &area, recog->sumtab); + numaAddNumber(recog->nasum_u, area); /* foreground */ + boxDestroy(&box); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + } + pixaDestroy(&pixa1); + ptaDestroy(&pta1); + } + + /* Are any classes bad? If so, destroy the recog and return an error */ + if (badclass) { + recogDestroy(precog); + return ERROR_INT("at least 1 bad class; destroying recog", procName, 1); + } + + /* Get the range of sizes of the unscaled average templates. + * Reject if the height ratio is too large. */ + pixaSizeRange(recog->pixa_u, &recog->minwidth_u, &recog->minheight_u, + &recog->maxwidth_u, &recog->maxheight_u); + hratio = (l_float32)recog->maxheight_u / (l_float32)recog->minheight_u; + if (hratio > recog->max_ht_ratio) { + L_ERROR("ratio of max/min height of average templates = %4.1f;" + " destroying recog\n", procName, hratio); + recogDestroy(precog); + return 1; + } + + /* Scaled bitmaps: compute averaged bitmap, centroid, and fg area */ + for (i = 0; i < size; i++) { + pixa1 = pixaaGetPixa(recog->pixaa, i, L_CLONE); + pta1 = ptaaGetPta(recog->ptaa, i, L_CLONE); + nsamp = pixaGetCount(pixa1); + nsamp = L_MIN(nsamp, 256); /* we only use the first 256 */ + pixaAccumulateSamples(pixa1, pta1, &pix1, &x, &y); + pix2 = pixThresholdToBinary(pix1, L_MAX(1, nsamp / 2)); + pixInvert(pix2, pix2); + pixClipToForeground(pix2, &pix3, &box); + if (!box) { + L_ERROR("no fg pixels in average for sclass %d\n", procName, i); + badclass = TRUE; + pixDestroy(&pix1); + pixDestroy(&pix2); + pixaDestroy(&pixa1); + ptaDestroy(&pta1); + break; + } else { + boxGetGeometry(box, &bx, &by, NULL, NULL); + pixaAddPix(recog->pixa, pix3, L_INSERT); + ptaAddPt(recog->pta, x - bx, y - by); /* correct centroid */ + pixCountPixels(pix3, &area, recog->sumtab); + numaAddNumber(recog->nasum, area); /* foreground */ + boxDestroy(&box); + } + pixDestroy(&pix1); + pixDestroy(&pix2); + pixaDestroy(&pixa1); + ptaDestroy(&pta1); + } + + if (badclass) { + recogDestroy(precog); + return ERROR_INT("at least 1 bad class; destroying recog", procName, 1); + } + + /* Get the range of widths of the scaled average templates */ + pixaSizeRange(recog->pixa, &recog->minwidth, NULL, &recog->maxwidth, NULL); + + /* Get dimensions useful for splitting */ + recog->min_splitw = L_MAX(5, recog->minwidth_u - 5); + recog->max_splith = recog->maxheight_u + 12; /* allow for skew */ + + if (debug) + recogShowAverageTemplates(recog); + + recog->ave_done = TRUE; + return 0; +} + + +/*! + * \brief pixaAccumulateSamples() + * + * \param[in] pixa of samples from the same class, 1 bpp + * \param[in] pta [optional] of centroids of the samples + * \param[out] ppixd accumulated samples, 8 bpp + * \param[out] px [optional] average x coordinate of centroids + * \param[out] py [optional] average y coordinate of centroids + * \return 0 on success, 1 on failure + * + *
+ * Notes:
+ *      (1) This generates an aligned (by centroid) sum of the input pix.
+ *      (2) We use only the first 256 samples; that's plenty.
+ *      (3) If pta is not input, we generate two tables, and discard
+ *          after use.  If this is called many times, it is better
+ *          to precompute the pta.
+ * 
+ */ +l_int32 +pixaAccumulateSamples(PIXA *pixa, + PTA *pta, + PIX **ppixd, + l_float32 *px, + l_float32 *py) +{ +l_int32 i, n, maxw, maxh, xdiff, ydiff; +l_int32 *centtab, *sumtab; +l_float32 xc, yc, xave, yave; +PIX *pix1, *pix2, *pixsum; +PTA *ptac; + + PROCNAME("pixaAccumulateSamples"); + + if (px) *px = 0; + if (py) *py = 0; + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + if (pta && ptaGetCount(pta) != n) + return ERROR_INT("pta count differs from pixa count", procName, 1); + n = L_MIN(n, 256); /* take the first 256 only */ + if (n == 0) + return ERROR_INT("pixa array empty", procName, 1); + + /* Find the centroids */ + if (pta) { + ptac = ptaClone(pta); + } else { /* generate them here */ + ptac = ptaCreate(n); + centtab = makePixelCentroidTab8(); + sumtab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + pixCentroid(pix1, centtab, sumtab, &xc, &yc); + ptaAddPt(ptac, xc, yc); + } + LEPT_FREE(centtab); + LEPT_FREE(sumtab); + } + + /* Find the average value of the centroids */ + xave = yave = 0; + for (i = 0; i < n; i++) { + ptaGetPt(pta, i, &xc, &yc); + xave += xc; + yave += yc; + } + xave = xave / (l_float32)n; + yave = yave / (l_float32)n; + if (px) *px = xave; + if (py) *py = yave; + + /* Place all pix with their centroids located at the average + * centroid value, and sum the results. Make the accumulator + * image slightly larger than the largest sample to insure + * that all pixels are represented in the accumulator. */ + pixaSizeRange(pixa, NULL, NULL, &maxw, &maxh); + pixsum = pixInitAccumulate(maxw + 5, maxh + 5, 0); + pix1 = pixCreate(maxw, maxh, 1); + for (i = 0; i < n; i++) { + pix2 = pixaGetPix(pixa, i, L_CLONE); + ptaGetPt(ptac, i, &xc, &yc); + xdiff = (l_int32)(xave - xc); + ydiff = (l_int32)(yave - yc); + pixClearAll(pix1); + pixRasterop(pix1, xdiff, ydiff, maxw, maxh, PIX_SRC, + pix2, 0, 0); + pixAccumulate(pixsum, pix1, L_ARITH_ADD); + pixDestroy(&pix2); + } + *ppixd = pixFinalAccumulate(pixsum, 0, 8); + + pixDestroy(&pix1); + pixDestroy(&pixsum); + ptaDestroy(&ptac); + return 0; +} + + +/*! + * \brief recogTrainingFinished() + * + * \param[in] precog addr of recog + * \param[in] modifyflag 1 to use recogModifyTemplate(); 0 otherwise + * \param[in] minsize set to -1 for default + * \param[in] minfract set to -1.0 for default + * \return 0 if OK, 1 on error (input recog will be destroyed) + * + *
+ * Notes:
+ *      (1) This must be called after all training samples have been added.
+ *      (2) If the templates are not good enough, the recog input is destroyed.
+ *      (3) Usually, %modifyflag == 1, because we want to apply
+ *          recogModifyTemplate() to generate the actual templates
+ *          that will be used.  The one exception is when reading a
+ *          serialized recog: there we want to put the same set of
+ *          templates in both the unscaled and modified pixaa.
+ *          See recogReadStream() to see why we do this.
+ *      (4) See recogTemplatesAreOK() for %minsize and %minfract usage.
+ *      (5) The following things are done here:
+ *          (a) Allocate (or reallocate) storage for (possibly) modified
+ *              bitmaps, centroids, and fg areas.
+ *          (b) Generate the (possibly) modified bitmaps.
+ *          (c) Compute centroid and fg area data for both unscaled and
+ *              modified bitmaps.
+ *          (d) Truncate the pixaa, ptaa and numaa arrays down from
+ *              256 to the actual size.
+ *      (6) Putting these operations here makes it simple to recompute
+ *          the recog with different modifications on the bitmaps.
+ *      (7) Call recogShowContent() to display the templates, both
+ *          unscaled and modified.
+ * 
+ */ +l_ok +recogTrainingFinished(L_RECOG **precog, + l_int32 modifyflag, + l_int32 minsize, + l_float32 minfract) +{ +l_int32 ok, i, j, size, nc, ns, area; +l_float32 xave, yave; +PIX *pix, *pixd; +PIXA *pixa; +PIXAA *paa; +PTA *pta; +PTAA *ptaa; +L_RECOG *recog; + + PROCNAME("recogTrainingFinished"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if ((recog = *precog) == NULL) + return ERROR_INT("recog not defined", procName, 1); + if (recog->train_done) return 0; + + /* Test the input templates */ + recogTemplatesAreOK(recog, minsize, minfract, &ok); + if (!ok) { + recogDestroy(precog); + return ERROR_INT("bad templates", procName, 1); + } + + /* Generate the storage for the possibly-scaled training bitmaps */ + size = recog->maxarraysize; + paa = pixaaCreate(size); + pixa = pixaCreate(1); + pixaaInitFull(paa, pixa); + pixaDestroy(&pixa); + pixaaDestroy(&recog->pixaa); + recog->pixaa = paa; + + /* Generate the storage for the unscaled centroid training data */ + ptaa = ptaaCreate(size); + pta = ptaCreate(0); + ptaaInitFull(ptaa, pta); + ptaaDestroy(&recog->ptaa_u); + recog->ptaa_u = ptaa; + + /* Generate the storage for the possibly-scaled centroid data */ + ptaa = ptaaCreate(size); + ptaaInitFull(ptaa, pta); + ptaDestroy(&pta); + ptaaDestroy(&recog->ptaa); + recog->ptaa = ptaa; + + /* Generate the storage for the fg area data */ + numaaDestroy(&recog->naasum_u); + numaaDestroy(&recog->naasum); + recog->naasum_u = numaaCreateFull(size, 0); + recog->naasum = numaaCreateFull(size, 0); + + paa = recog->pixaa_u; + nc = recog->setsize; + for (i = 0; i < nc; i++) { + pixa = pixaaGetPixa(paa, i, L_CLONE); + ns = pixaGetCount(pixa); + for (j = 0; j < ns; j++) { + /* Save centroid and area data for the unscaled pix */ + pix = pixaGetPix(pixa, j, L_CLONE); + pixCentroid(pix, recog->centtab, recog->sumtab, &xave, &yave); + ptaaAddPt(recog->ptaa_u, i, xave, yave); + pixCountPixels(pix, &area, recog->sumtab); + numaaAddNumber(recog->naasum_u, i, area); /* foreground */ + + /* Insert the (optionally) scaled character image, and + * save centroid and area data for it */ + if (modifyflag == 1) + pixd = recogModifyTemplate(recog, pix); + else + pixd = pixClone(pix); + if (pixd) { + pixaaAddPix(recog->pixaa, i, pixd, NULL, L_INSERT); + pixCentroid(pixd, recog->centtab, recog->sumtab, &xave, &yave); + ptaaAddPt(recog->ptaa, i, xave, yave); + pixCountPixels(pixd, &area, recog->sumtab); + numaaAddNumber(recog->naasum, i, area); + } else { + L_ERROR("failed: modified template for class %d, sample %d\n", + procName, i, j); + } + pixDestroy(&pix); + } + pixaDestroy(&pixa); + } + + /* Truncate the arrays to those with non-empty containers */ + pixaaTruncate(recog->pixaa_u); + pixaaTruncate(recog->pixaa); + ptaaTruncate(recog->ptaa_u); + ptaaTruncate(recog->ptaa); + numaaTruncate(recog->naasum_u); + numaaTruncate(recog->naasum); + + recog->train_done = TRUE; + return 0; +} + + +/*! + * \brief recogTemplatesAreOK() + * + * \param[in] recog + * \param[in] minsize set to -1 for default + * \param[in] minfract set to -1.0 for default + * \param[out] pok set to 1 if template set is valid; 0 otherwise + * \return 1 on error; 0 otherwise. An invalid template set is not an error. + * + *
+ * Notes:
+ *      (1) This is called by recogTrainingFinished().  A return value of 0
+ *          will cause recogTrainingFinished() to destroy the recog.
+ *      (2) %minsize is the minimum number of samples required for
+ *          the class; -1 uses the default
+ *      (3) %minfract is the minimum fraction of classes required for
+ *          the recog to be usable; -1.0 uses the default
+ * 
+ */ +static l_int32 +recogTemplatesAreOK(L_RECOG *recog, + l_int32 minsize, + l_float32 minfract, + l_int32 *pok) +{ +l_int32 i, n, validsets, nt; +l_float32 ratio; +NUMA *na; + + PROCNAME("recogTemplatesAreOK"); + + if (!pok) + return ERROR_INT("&ok not defined", procName, 1); + *pok = 0; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + minsize = (minsize < 0) ? DefaultMinSetSize : minsize; + minfract = (minfract < 0) ? DefaultMinSetFract : minfract; + n = pixaaGetCount(recog->pixaa_u, &na); + validsets = 0; + for (i = 0, validsets = 0; i < n; i++) { + numaGetIValue(na, i, &nt); + if (nt >= minsize) + validsets++; + } + numaDestroy(&na); + ratio = (l_float32)validsets / (l_float32)recog->charset_size; + *pok = (ratio >= minfract) ? 1 : 0; + return 0; +} + + +/*! + * \brief recogFilterPixaBySize() + * + * \param[in] pixas labeled templates + * \param[in] setsize size of character set (number of classes) + * \param[in] maxkeep max number of templates to keep in a class + * \param[in] max_ht_ratio max allowed height ratio (see below) + * \param[out] pna [optional] debug output, giving the number + * in each class after filtering; use NULL to skip + * \return pixa filtered templates, or NULL on error + * + *
+ * Notes:
+ *      (1) The basic assumption is that the most common and larger
+ *          templates in each class are more likely to represent the
+ *          characters we are interested in.  For example, larger digits
+ *          are more likely to represent page numbers, and smaller digits
+ *          could be data in tables.  Therefore, we bias the first
+ *          stage of filtering toward the larger characters by removing
+ *          very small ones, and select based on proximity of the
+ *          remaining characters to median height.
+ *      (2) For each of the %setsize classes, order the templates
+ *          increasingly by height.  Take the rank 0.9 height.  Eliminate
+ *          all templates that are shorter by more than %max_ht_ratio.
+ *          Of the remaining ones, select up to %maxkeep that are closest
+ *          in rank order height to the median template.
+ * 
+ */ +PIXA * +recogFilterPixaBySize(PIXA *pixas, + l_int32 setsize, + l_int32 maxkeep, + l_float32 max_ht_ratio, + NUMA **pna) +{ +l_int32 i, j, h90, hj, j1, j2, j90, n, nc; +l_float32 ratio; +NUMA *na; +PIXA *pixa1, *pixa2, *pixa3, *pixa4, *pixa5; +PIXAA *paa; + + PROCNAME("recogFilterPixaBySize"); + + if (pna) *pna = NULL; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + if ((paa = recogSortPixaByClass(pixas, setsize)) == NULL) + return (PIXA *)ERROR_PTR("paa not made", procName, NULL); + nc = pixaaGetCount(paa, NULL); + na = (pna) ? numaCreate(0) : NULL; + if (pna) *pna = na; + pixa5 = pixaCreate(0); + for (i = 0; i < nc; i++) { + pixa1 = pixaaGetPixa(paa, i, L_CLONE); + if ((n = pixaGetCount(pixa1)) == 0) { + pixaDestroy(&pixa1); + continue; + } + pixa2 = pixaSort(pixa1, L_SORT_BY_HEIGHT, L_SORT_INCREASING, NULL, + L_COPY); + j90 = (l_int32)(0.9 * n); + pixaGetPixDimensions(pixa2, j90, NULL, &h90, NULL); + pixa3 = pixaCreate(n); + for (j = 0; j < n; j++) { + pixaGetPixDimensions(pixa2, j, NULL, &hj, NULL); + ratio = (l_float32)h90 / (l_float32)hj; + if (ratio <= max_ht_ratio) + pixaAddPix(pixa3, pixaGetPix(pixa2, j, L_COPY), L_INSERT); + } + n = pixaGetCount(pixa3); + if (n <= maxkeep) { + pixa4 = pixaCopy(pixa3, L_CLONE); + } else { + j1 = (n - maxkeep) / 2; + j2 = j1 + maxkeep - 1; + pixa4 = pixaSelectRange(pixa3, j1, j2, L_CLONE); + } + if (na) numaAddNumber(na, pixaGetCount(pixa4)); + pixaJoin(pixa5, pixa4, 0, -1); + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + pixaDestroy(&pixa3); + pixaDestroy(&pixa4); + } + + pixaaDestroy(&paa); + return pixa5; +} + + +/*! + * \brief recogSortPixaByClass() + * + * \param[in] pixa labeled templates + * \param[in] setsize size of character set (number of classes) + * \return paa pixaa where each pixa has templates for one class, + * or null on error + */ +PIXAA * +recogSortPixaByClass(PIXA *pixa, + l_int32 setsize) +{ +PIXAA *paa; +L_RECOG *recog; + + PROCNAME("recogSortPixaByClass"); + + if (!pixa) + return (PIXAA *)ERROR_PTR("pixa not defined", procName, NULL); + + if ((recog = recogCreateFromPixaNoFinish(pixa, 0, 0, 0, 0, 0)) == NULL) + return (PIXAA *)ERROR_PTR("recog not made", procName, NULL); + paa = recog->pixaa_u; /* grab the paa of unscaled templates */ + recog->pixaa_u = NULL; + recogDestroy(&recog); + return paa; +} + + +/*! + * \brief recogRemoveOutliers1() + * + * \param[in] precog addr of recog with unscaled labeled templates + * \param[in] minscore keep everything with at least this score + * \param[in] mintarget minimum desired number to retain if possible + * \param[in] minsize minimum number of samples required for a class + * \param[out] ppixsave [optional debug] saved templates, with scores + * \param[out] ppixrem [optional debug] removed templates, with scores + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This is a convenience wrapper when using default parameters
+ *          for the recog.  See pixaRemoveOutliers1() for details.
+ *      (2) If this succeeds, the new recog replaces the input recog;
+ *          if it fails, the input recog is destroyed.
+ * 
+ */ +l_ok +recogRemoveOutliers1(L_RECOG **precog, + l_float32 minscore, + l_int32 mintarget, + l_int32 minsize, + PIX **ppixsave, + PIX **ppixrem) +{ +PIXA *pixa1, *pixa2; +L_RECOG *recog; + + PROCNAME("recogRemoveOutliers1"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if (*precog == NULL) + return ERROR_INT("recog not defined", procName, 1); + + /* Extract the unscaled templates */ + pixa1 = recogExtractPixa(*precog); + recogDestroy(precog); + + pixa2 = pixaRemoveOutliers1(pixa1, minscore, mintarget, minsize, + ppixsave, ppixrem); + pixaDestroy(&pixa1); + if (!pixa2) + return ERROR_INT("failure to remove outliers", procName, 1); + + recog = recogCreateFromPixa(pixa2, 0, 0, 0, 150, 1); + pixaDestroy(&pixa2); + if (!recog) + return ERROR_INT("failure to make recog from pixa sans outliers", + procName, 1); + + *precog = recog; + return 0; +} + + +/*! + * \brief pixaRemoveOutliers1() + * + * \param[in] pixas unscaled labeled templates + * \param[in] minscore keep everything with at least this score; + * use -1.0 for default. + * \param[in] mintarget minimum desired number to retain if possible; + * use -1 for default. + * \param[in] minsize minimum number of samples required for a class; + * use -1 for default. + * \param[out] ppixsave [optional debug] saved templates, with scores + * \param[out] ppixrem [optional debug] removed templates, with scores + * \return pixa of unscaled templates to be kept, or NULL on error + * + *
+ * Notes:
+ *      (1) Removing outliers is particularly important when recognition
+ *          goes against all the samples in the training set, as opposed
+ *          to the averages for each class.  The reason is that we get
+ *          an identification error if a mislabeled template is a best
+ *          match for an input sample.
+ *      (2) Because the score values depend strongly on the quality
+ *          of the character images, to avoid losing too many samples
+ *          we supplement a minimum score for retention with a score
+ *          necessary to acquire the minimum target number of templates.
+ *          To do this we are willing to use a lower threshold,
+ *          LowerScoreThreshold, on the score.  Consequently, with
+ *          poor quality templates, we may keep samples with a score
+ *          less than %minscore, but never less than LowerScoreThreshold.
+ *          And if the number of samples is less than %minsize, we do
+ *          not use any.
+ *      (3) This is meant to be used on a BAR, where the templates all
+ *          come from the same book; use minscore ~0.75.
+ *      (4) Method: make a scaled recog from the input %pixas.  Then,
+ *          for each class: generate the averages, match each
+ *          scaled template against the average, and save unscaled
+ *          templates that had a sufficiently good match.
+ * 
+ */ +PIXA * +pixaRemoveOutliers1(PIXA *pixas, + l_float32 minscore, + l_int32 mintarget, + l_int32 minsize, + PIX **ppixsave, + PIX **ppixrem) +{ +l_int32 i, j, debug, n, area1, area2; +l_float32 x1, y1, x2, y2, minfract, score, rankscore, threshscore; +NUMA *nasum, *narem, *nasave, *nascore; +PIX *pix1, *pix2; +PIXA *pixa, *pixarem, *pixad; +PTA *pta; +L_RECOG *recog; + + PROCNAME("pixaRemoveOutliers1"); + + if (ppixsave) *ppixsave = NULL; + if (ppixrem) *ppixrem = NULL; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + minscore = L_MIN(minscore, 1.0); + if (minscore <= 0.0) + minscore = DefaultMinScore; + mintarget = L_MIN(mintarget, 3); + if (mintarget <= 0) + mintarget = DefaultMinTarget; + if (minsize < 0) + minsize = DefaultMinSetSize; + + /* Make a special height-scaled recognizer with average templates */ + debug = (ppixsave || ppixrem) ? 1 : 0; + recog = recogCreateFromPixa(pixas, 0, 40, 0, 128, 1); + if (!recog) + return (PIXA *)ERROR_PTR("bad pixas; recog not made", procName, NULL); + recogAverageSamples(&recog, debug); + if (!recog) + return (PIXA *)ERROR_PTR("bad templates", procName, NULL); + + nasave = (ppixsave) ? numaCreate(0) : NULL; + pixarem = (ppixrem) ? pixaCreate(0) : NULL; + narem = (ppixrem) ? numaCreate(0) : NULL; + + pixad = pixaCreate(0); + for (i = 0; i < recog->setsize; i++) { + /* Access the average template and values for scaled + * images in this class */ + pix1 = pixaGetPix(recog->pixa, i, L_CLONE); + ptaGetPt(recog->pta, i, &x1, &y1); + numaGetIValue(recog->nasum, i, &area1); + + /* Get the scores for each sample in the class */ + pixa = pixaaGetPixa(recog->pixaa, i, L_CLONE); + pta = ptaaGetPta(recog->ptaa, i, L_CLONE); /* centroids */ + nasum = numaaGetNuma(recog->naasum, i, L_CLONE); /* fg areas */ + n = pixaGetCount(pixa); + nascore = numaCreate(n); + for (j = 0; j < n; j++) { + pix2 = pixaGetPix(pixa, j, L_CLONE); + ptaGetPt(pta, j, &x2, &y2); /* centroid average */ + numaGetIValue(nasum, j, &area2); /* fg sum average */ + pixCorrelationScoreSimple(pix1, pix2, area1, area2, + x1 - x2, y1 - y2, 5, 5, + recog->sumtab, &score); + numaAddNumber(nascore, score); + if (debug && score == 0.0) /* typ. large size difference */ + fprintf(stderr, "Got 0 score for i = %d, j = %d\n", i, j); + pixDestroy(&pix2); + } + pixDestroy(&pix1); + + /* Find the rankscore, corresponding to the 1.0 - minfract. + * To attempt to maintain the minfract of templates, use as a + * cutoff the minimum of minscore and the rank score. However, + * no template is saved with an actual score less than + * that at least one template is kept. */ + minfract = (l_float32)mintarget / (l_float32)n; + numaGetRankValue(nascore, 1.0 - minfract, NULL, 0, &rankscore); + threshscore = L_MAX(LowerScoreThreshold, + L_MIN(minscore, rankscore)); + if (debug) { + L_INFO("minscore = %4.2f, rankscore = %4.2f, threshscore = %4.2f\n", + procName, minscore, rankscore, threshscore); + } + + /* Save templates that are at or above threshold. + * Toss any classes with less than %minsize templates. */ + for (j = 0; j < n; j++) { + numaGetFValue(nascore, j, &score); + pix1 = pixaaGetPix(recog->pixaa_u, i, j, L_COPY); + if (score >= threshscore && n >= minsize) { + pixaAddPix(pixad, pix1, L_INSERT); + if (nasave) numaAddNumber(nasave, score); + } else if (debug) { + pixaAddPix(pixarem, pix1, L_INSERT); + numaAddNumber(narem, score); + } else { + pixDestroy(&pix1); + } + } + + pixaDestroy(&pixa); + ptaDestroy(&pta); + numaDestroy(&nasum); + numaDestroy(&nascore); + } + + if (ppixsave) { + *ppixsave = pixDisplayOutliers(pixad, nasave); + numaDestroy(&nasave); + } + if (ppixrem) { + *ppixrem = pixDisplayOutliers(pixarem, narem); + pixaDestroy(&pixarem); + numaDestroy(&narem); + } + recogDestroy(&recog); + return pixad; +} + + +/*! + * \brief recogRemoveOutliers2() + * + * \param[in] precog addr of recog with unscaled labeled templates + * \param[in] minscore keep everything with at least this score + * \param[in] minsize minimum number of samples required for a class + * \param[out] ppixsave [optional debug] saved templates, with scores + * \param[out] ppixrem [optional debug] removed templates, with scores + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This is a convenience wrapper when using default parameters
+ *          for the recog.  See pixaRemoveOutliers2() for details.
+ *      (2) If this succeeds, the new recog replaces the input recog;
+ *          if it fails, the input recog is destroyed.
+ * 
+ */ +l_ok +recogRemoveOutliers2(L_RECOG **precog, + l_float32 minscore, + l_int32 minsize, + PIX **ppixsave, + PIX **ppixrem) +{ +PIXA *pixa1, *pixa2; +L_RECOG *recog; + + PROCNAME("recogRemoveOutliers2"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if (*precog == NULL) + return ERROR_INT("recog not defined", procName, 1); + + /* Extract the unscaled templates */ + pixa1 = recogExtractPixa(*precog); + recogDestroy(precog); + + pixa2 = pixaRemoveOutliers2(pixa1, minscore, minsize, ppixsave, ppixrem); + pixaDestroy(&pixa1); + if (!pixa2) + return ERROR_INT("failure to remove outliers", procName, 1); + + recog = recogCreateFromPixa(pixa2, 0, 0, 0, 150, 1); + pixaDestroy(&pixa2); + if (!recog) + return ERROR_INT("failure to make recog from pixa sans outliers", + procName, 1); + + *precog = recog; + return 0; +} + + +/*! + * \brief pixaRemoveOutliers2() + * + * \param[in] pixas unscaled labeled templates + * \param[in] minscore keep everything with at least this score; + * use -1.0 for default. + * \param[in] minsize minimum number of samples required for a class; + * use -1 for default. + * \param[out] ppixsave [optional debug] saved templates, with scores + * \param[out] ppixrem [optional debug] removed templates, with scores + * \return pixa of unscaled templates to be kept, or NULL on error + * + *
+ * Notes:
+ *      (1) Removing outliers is particularly important when recognition
+ *          goes against all the samples in the training set, as opposed
+ *          to the averages for each class.  The reason is that we get
+ *          an identification error if a mislabeled template is a best
+ *          match for an input sample.
+ *      (2) This method compares each template against the average templates
+ *          of each class, and discards any template that has a higher
+ *          correlation to a class different from its own.  It also
+ *          sets a lower bound on correlation scores with its class average.
+ *      (3) This is meant to be used on a BAR, where the templates all
+ *          come from the same book; use minscore ~0.75.
+ * 
+ */ +PIXA * +pixaRemoveOutliers2(PIXA *pixas, + l_float32 minscore, + l_int32 minsize, + PIX **ppixsave, + PIX **ppixrem) +{ +l_int32 i, j, k, n, area1, area2, maxk, debug; +l_float32 x1, y1, x2, y2, score, maxscore; +NUMA *nan, *nascore, *nasave; +PIX *pix1, *pix2, *pix3; +PIXA *pixarem, *pixad; +L_RECOG *recog; + + PROCNAME("pixaRemoveOutliers2"); + + if (ppixsave) *ppixsave = NULL; + if (ppixrem) *ppixrem = NULL; + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + minscore = L_MIN(minscore, 1.0); + if (minscore <= 0.0) + minscore = DefaultMinScore; + if (minsize < 0) + minsize = DefaultMinSetSize; + + /* Make a special height-scaled recognizer with average templates */ + debug = (ppixsave || ppixrem) ? 1 : 0; + recog = recogCreateFromPixa(pixas, 0, 40, 0, 128, 1); + if (!recog) + return (PIXA *)ERROR_PTR("bad pixas; recog not made", procName, NULL); + recogAverageSamples(&recog, debug); + if (!recog) + return (PIXA *)ERROR_PTR("bad templates", procName, NULL); + + nasave = (ppixsave) ? numaCreate(0) : NULL; + pixarem = (ppixrem) ? pixaCreate(0) : NULL; + + pixad = pixaCreate(0); + pixaaGetCount(recog->pixaa, &nan); /* number of templates in each class */ + for (i = 0; i < recog->setsize; i++) { + /* Get the scores for each sample in the class, when comparing + * with averages from all the classes. */ + numaGetIValue(nan, i, &n); + for (j = 0; j < n; j++) { + pix1 = pixaaGetPix(recog->pixaa, i, j, L_CLONE); + ptaaGetPt(recog->ptaa, i, j, &x1, &y1); /* centroid */ + numaaGetValue(recog->naasum, i, j, NULL, &area1); /* fg sum */ + nascore = numaCreate(n); + for (k = 0; k < recog->setsize; k++) { /* average templates */ + pix2 = pixaGetPix(recog->pixa, k, L_CLONE); + ptaGetPt(recog->pta, k, &x2, &y2); /* average centroid */ + numaGetIValue(recog->nasum, k, &area2); /* average fg sum */ + pixCorrelationScoreSimple(pix1, pix2, area1, area2, + x1 - x2, y1 - y2, 5, 5, + recog->sumtab, &score); + numaAddNumber(nascore, score); + pixDestroy(&pix2); + } + + /* Save templates that are in the correct class and + * at or above threshold. Toss any classes with less + * than %minsize templates. */ + numaGetMax(nascore, &maxscore, &maxk); + if (maxk == i && maxscore >= minscore && n >= minsize) { + /* save it */ + pix3 = pixaaGetPix(recog->pixaa_u, i, j, L_COPY); + pixaAddPix(pixad, pix3, L_INSERT); + if (nasave) numaAddNumber(nasave, maxscore); + } else if (ppixrem) { /* outlier */ + pix3 = recogDisplayOutlier(recog, i, j, maxk, maxscore); + pixaAddPix(pixarem, pix3, L_INSERT); + } + numaDestroy(&nascore); + pixDestroy(&pix1); + } + } + + if (ppixsave) { + *ppixsave = pixDisplayOutliers(pixad, nasave); + numaDestroy(&nasave); + } + if (ppixrem) { + *ppixrem = pixaDisplayTiledInRows(pixarem, 32, 1500, 1.0, 0, 20, 2); + pixaDestroy(&pixarem); + } + + numaDestroy(&nan); + recogDestroy(&recog); + return pixad; +} + + +/*------------------------------------------------------------------------* + * Training on unlabeled data * + *------------------------------------------------------------------------*/ +/*! + * \brief recogTrainFromBoot() + * + * \param[in] recogboot labeled boot recognizer + * \param[in] pixas set of unlabeled input characters + * \param[in] minscore min score for accepting the example; e.g., 0.75 + * \param[in] threshold for binarization, if needed + * \param[in] debug 1 for debug output saved to recogboot; 0 otherwise + * \return pixad labeled version of input pixas, trained on a BSR, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This takes %pixas of unscaled single characters and %recboot,
+ *          a bootstrep recognizer (BSR) that has been set up with parameters
+ *            * scaleh: scale all templates to this height
+ *            * linew: width of normalized strokes, or 0 if using
+ *              the input image
+ *          It modifies the pix in %pixas accordingly and correlates
+ *          with the templates in the BSR.  It returns those input
+ *          images in %pixas whose best correlation with the BSR is at
+ *          or above %minscore.  The returned pix have added text labels
+ *          for the text string of the class to which the best
+ *          correlated template belongs.
+ *      (2) Identification occurs in scaled mode (typically with h = 40),
+ *          optionally using a width-normalized line images derived
+ *          from those in %pixas.
+ * 
+ */ +PIXA * +recogTrainFromBoot(L_RECOG *recogboot, + PIXA *pixas, + l_float32 minscore, + l_int32 threshold, + l_int32 debug) +{ +char *text; +l_int32 i, n, same, maxd, scaleh, linew; +l_float32 score; +PIX *pix1, *pix2, *pixdb; +PIXA *pixa1, *pixa2, *pixa3, *pixad; + + PROCNAME("recogTrainFromBoot"); + + if (!recogboot) + return (PIXA *)ERROR_PTR("recogboot not defined", procName, NULL); + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + + /* Make sure all input pix are 1 bpp */ + if ((n = pixaGetCount(pixas)) == 0) + return (PIXA *)ERROR_PTR("no pix in pixa", procName, NULL); + pixaVerifyDepth(pixas, &same, &maxd); + if (maxd == 1) { + pixa1 = pixaCopy(pixas, L_COPY); + } else { + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixConvertTo1(pix1, threshold); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + } + + /* Scale the input images to match the BSR */ + scaleh = recogboot->scaleh; + linew = recogboot->linew; + pixa2 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa1, i, L_CLONE); + pix2 = pixScaleToSize(pix1, 0, scaleh); + pixaAddPix(pixa2, pix2, L_INSERT); + pixDestroy(&pix1); + } + pixaDestroy(&pixa1); + + /* Optionally convert to width-normalized line */ + if (linew > 0) + pixa3 = pixaSetStrokeWidth(pixa2, linew, 4, 8); + else + pixa3 = pixaCopy(pixa2, L_CLONE); + pixaDestroy(&pixa2); + + /* Identify using recogboot */ + n = pixaGetCount(pixa3); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa3, i, L_COPY); + pixSetText(pix1, NULL); /* remove any existing text or labelling */ + if (!debug) { + recogIdentifyPix(recogboot, pix1, NULL); + } else { + recogIdentifyPix(recogboot, pix1, &pixdb); + pixaAddPix(recogboot->pixadb_boot, pixdb, L_INSERT); + } + rchExtract(recogboot->rch, NULL, &score, &text, NULL, NULL, NULL, NULL); + if (score >= minscore) { + pix2 = pixaGetPix(pixas, i, L_COPY); + pixSetText(pix2, text); + pixaAddPix(pixad, pix2, L_INSERT); + pixaAddPix(recogboot->pixadb_boot, pixdb, L_COPY); + } + LEPT_FREE(text); + pixDestroy(&pix1); + } + pixaDestroy(&pixa3); + + return pixad; +} + + +/*------------------------------------------------------------------------* + * Padding the digit training set * + *------------------------------------------------------------------------*/ +/*! + * \brief recogPadDigitTrainingSet() + * + * \param[in,out] precog trained; if padding is needed, it is replaced + * by a a new padded recog + * \param[in] scaleh must be > 0; suggest ~40. + * \param[in] linew use 0 for original scanned images + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a no-op if padding is not needed.  However,
+ *          if it is, this replaces the input recog with a new recog,
+ *          padded appropriately with templates from a boot recognizer,
+ *          and set up with correlation templates derived from
+ *          %scaleh and %linew.
+ * 
+ */ +l_ok +recogPadDigitTrainingSet(L_RECOG **precog, + l_int32 scaleh, + l_int32 linew) +{ +PIXA *pixa; +L_RECOG *recog1, *recog2; +SARRAY *sa; + + PROCNAME("recogPadDigitTrainingSet"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + recog1 = *precog; + + recogIsPaddingNeeded(recog1, &sa); + if (!sa) return 0; + + /* Get a new pixa with the padding templates added */ + pixa = recogAddDigitPadTemplates(recog1, sa); + sarrayDestroy(&sa); + if (!pixa) + return ERROR_INT("pixa not made", procName, 1); + + /* Need to use templates that are scaled to a fixed height. */ + if (scaleh <= 0) { + L_WARNING("templates must be scaled to fixed height; using %d\n", + procName, 40); + scaleh = 40; + } + + /* Create a hybrid recog, composed of templates from both + * the original and bootstrap sources. */ + recog2 = recogCreateFromPixa(pixa, 0, scaleh, linew, recog1->threshold, + recog1->maxyshift); + pixaDestroy(&pixa); + recogDestroy(precog); + *precog = recog2; + return 0; +} + + +/*! + * \brief recogIsPaddingNeeded() + * + * \param[in] recog trained + * \param[out] psa addr of returned string containing text value + * \return 1 on error; 0 if OK, whether or not additional padding + * templates are required. + * + *
+ * Notes:
+ *      (1) This returns a string array in &sa containing character values
+ *          for which extra templates are needed; this sarray is
+ *          used by recogGetPadTemplates().  It returns NULL
+ *          if no padding templates are needed.
+ * 
+ */ +l_int32 +recogIsPaddingNeeded(L_RECOG *recog, + SARRAY **psa) +{ +char *str; +l_int32 i, nt, min_nopad, nclass, allclasses; +l_float32 minval; +NUMA *naclass; +SARRAY *sa; + + PROCNAME("recogIsPaddingNeeded"); + + if (!psa) + return ERROR_INT("&sa not defined", procName, 1); + *psa = NULL; + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + /* Do we have samples from all classes? */ + nclass = pixaaGetCount(recog->pixaa_u, &naclass); /* unscaled bitmaps */ + allclasses = (nclass == recog->charset_size) ? 1 : 0; + + /* Are there enough samples in each class already? */ + min_nopad = recog->min_nopad; + numaGetMin(naclass, &minval, NULL); + if (allclasses && (minval >= min_nopad)) { + numaDestroy(&naclass); + return 0; + } + + /* Are any classes not represented? */ + sa = recogAddMissingClassStrings(recog); + *psa = sa; + + /* Are any other classes under-represented? */ + for (i = 0; i < nclass; i++) { + numaGetIValue(naclass, i, &nt); + if (nt < min_nopad) { + str = sarrayGetString(recog->sa_text, i, L_COPY); + sarrayAddString(sa, str, L_INSERT); + } + } + numaDestroy(&naclass); + return 0; +} + + +/*! + * \brief recogAddMissingClassStrings() + * + * \param[in] recog trained + * \return sa of class string missing in %recog, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns an empty %sa if there is at least one template
+ *          in each class in %recog.
+ * 
+ */ +static SARRAY * +recogAddMissingClassStrings(L_RECOG *recog) +{ +char *text; +char str[4]; +l_int32 i, nclass, index, ival; +NUMA *na; +SARRAY *sa; + + PROCNAME("recogAddMissingClassStrings"); + + if (!recog) + return (SARRAY *)ERROR_PTR("recog not defined", procName, NULL); + + /* Only handling digits */ + nclass = pixaaGetCount(recog->pixaa_u, NULL); /* unscaled bitmaps */ + if (recog->charset_type != 1 || nclass == 10) + return sarrayCreate(0); /* empty */ + + /* Make an indicator array for missing classes */ + na = numaCreate(0); + sa = sarrayCreate(0); + for (i = 0; i < recog->charset_size; i++) + numaAddNumber(na, 1); + for (i = 0; i < nclass; i++) { + text = sarrayGetString(recog->sa_text, i, L_NOCOPY); + index = text[0] - '0'; + numaSetValue(na, index, 0); + } + + /* Convert to string and add to output */ + for (i = 0; i < nclass; i++) { + numaGetIValue(na, i, &ival); + if (ival == 1) { + str[0] = '0' + i; + str[1] = '\0'; + sarrayAddString(sa, str, L_COPY); + } + } + numaDestroy(&na); + return sa; +} + + +/*! + * \brief recogAddDigitPadTemplates() + * + * \param[in] recog trained + * \param[in] sa set of text strings that need to be padded + * \return pixa of all templates from %recog and the additional pad + * templates from a boot recognizer; or NULL on error + * + *
+ * Notes:
+ *      (1) Call recogIsPaddingNeeded() first, which returns %sa of
+ *          template text strings for classes where more templates
+ *          are needed.
+ * 
+ */ +PIXA * +recogAddDigitPadTemplates(L_RECOG *recog, + SARRAY *sa) +{ +char *str, *text; +l_int32 i, j, n, nt; +PIX *pix; +PIXA *pixa1, *pixa2; + + PROCNAME("recogAddDigitPadTemplates"); + + if (!recog) + return (PIXA *)ERROR_PTR("recog not defined", procName, NULL); + if (!sa) + return (PIXA *)ERROR_PTR("sa not defined", procName, NULL); + if (recogCharsetAvailable(recog->charset_type) == FALSE) + return (PIXA *)ERROR_PTR("boot charset not available", procName, NULL); + + /* Make boot recog templates */ + pixa1 = recogMakeBootDigitTemplates(0, 0); + n = pixaGetCount(pixa1); + + /* Extract the unscaled templates from %recog */ + pixa2 = recogExtractPixa(recog); + + /* Add selected boot recog templates based on the text strings in sa */ + nt = sarrayGetCount(sa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa1, i, L_CLONE); + text = pixGetText(pix); + for (j = 0; j < nt; j++) { + str = sarrayGetString(sa, j, L_NOCOPY); + if (!strcmp(text, str)) { + pixaAddPix(pixa2, pix, L_COPY); + break; + } + } + pixDestroy(&pix); + } + + pixaDestroy(&pixa1); + return pixa2; +} + + +/*! + * \brief recogCharsetAvailable() + * + * \param[in] type of charset for padding + * \return 1 if available; 0 if not. + */ +static l_int32 +recogCharsetAvailable(l_int32 type) +{ +l_int32 ret; + + PROCNAME("recogCharsetAvailable"); + + switch (type) + { + case L_ARABIC_NUMERALS: + ret = TRUE; + break; + case L_LC_ROMAN_NUMERALS: + case L_UC_ROMAN_NUMERALS: + case L_LC_ALPHA: + case L_UC_ALPHA: + L_INFO("charset type %d not available\n", procName, type); + ret = FALSE; + break; + default: + L_INFO("charset type %d is unknown\n", procName, type); + ret = FALSE; + break; + } + + return ret; +} + + +/*------------------------------------------------------------------------* + * Making a boot digit recognizer * + *------------------------------------------------------------------------*/ +/*! + * \brief recogMakeBootDigitRecog() + * + * \param[in] nsamp number of samples of each digit; or 0 + * \param[in] scaleh scale all heights to this; typ. use 40 + * \param[in] linew normalized line width; typ. use 5; 0 to skip + * \param[in] maxyshift from nominal centroid alignment; typically 0 or 1 + * \param[in] debug 1 for showing templates; 0 otherwise + * \return recog, or NULL on error + * + *
+ * Notes:
+ *     (1) This takes a set of pre-computed, labeled pixa of single
+ *         digits, and generates a recognizer from them.
+ *         The templates used in the recognizer can be modified by:
+ *         - scaling (isotropically to fixed height)
+ *         - generating a skeleton and thickening so that all strokes
+ *           have the same width.
+ *     (2) The resulting templates are scaled versions of either the
+ *         input bitmaps or images with fixed line widths.  To use the
+ *         input bitmaps, set %linew = 0; otherwise, set %linew to the
+ *         desired line width.
+ *     (3) If %nsamp == 0, this uses and extends the output from
+ *         three boot generators:
+ *            l_bootnum_gen1, l_bootnum_gen2, l_bootnum_gen3.
+ *         Otherwise, it uses exactly %nsamp templates of each digit,
+ *         extracted by l_bootnum_gen4.
+ * 
+ */ +L_RECOG * +recogMakeBootDigitRecog(l_int32 nsamp, + l_int32 scaleh, + l_int32 linew, + l_int32 maxyshift, + l_int32 debug) + +{ +PIXA *pixa; +L_RECOG *recog; + + /* Get the templates, extended by horizontal scaling */ + pixa = recogMakeBootDigitTemplates(nsamp, debug); + + /* Make the boot recog; recogModifyTemplate() will scale the + * templates and optionally turn them into strokes of fixed width. */ + recog = recogCreateFromPixa(pixa, 0, scaleh, linew, 128, maxyshift); + pixaDestroy(&pixa); + if (debug) + recogShowContent(stderr, recog, 0, 1); + + return recog; +} + + +/*! + * \brief recogMakeBootDigitTemplates() + * + * \param[in] nsamp number of samples of each digit; or 0 + * \param[in] debug 1 for display of templates + * \return pixa of templates; or NULL on error + * + *
+ * Notes:
+ *     (1) See recogMakeBootDigitRecog().
+ * 
+ */ +PIXA * +recogMakeBootDigitTemplates(l_int32 nsamp, + l_int32 debug) +{ +NUMA *na1; +PIX *pix1, *pix2, *pix3; +PIXA *pixa1, *pixa2, *pixa3; + + if (nsamp > 0) { + pixa1 = l_bootnum_gen4(nsamp); + if (debug) { + pix1 = pixaDisplayTiledWithText(pixa1, 1500, 1.0, 10, + 2, 6, 0xff000000); + pixDisplay(pix1, 0, 0); + pixDestroy(&pix1); + } + return pixa1; + } + + /* Else, generate from 3 pixa */ + pixa1 = l_bootnum_gen1(); + pixa2 = l_bootnum_gen2(); + pixa3 = l_bootnum_gen3(); + if (debug) { + pix1 = pixaDisplayTiledWithText(pixa1, 1500, 1.0, 10, 2, 6, 0xff000000); + pix2 = pixaDisplayTiledWithText(pixa2, 1500, 1.0, 10, 2, 6, 0xff000000); + pix3 = pixaDisplayTiledWithText(pixa3, 1500, 1.0, 10, 2, 6, 0xff000000); + pixDisplay(pix1, 0, 0); + pixDisplay(pix2, 600, 0); + pixDisplay(pix3, 1200, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + } + pixaJoin(pixa1, pixa2, 0, -1); + pixaJoin(pixa1, pixa3, 0, -1); + pixaDestroy(&pixa2); + pixaDestroy(&pixa3); + + /* Extend by horizontal scaling */ + na1 = numaCreate(4); + numaAddNumber(na1, 0.9); + numaAddNumber(na1, 1.1); + numaAddNumber(na1, 1.2); + pixa2 = pixaExtendByScaling(pixa1, na1, L_HORIZ, 1); + + pixaDestroy(&pixa1); + numaDestroy(&na1); + return pixa2; +} + + +/*------------------------------------------------------------------------* + * Debugging * + *------------------------------------------------------------------------*/ +/*! + * \brief recogShowContent() + * + * \param[in] fp file stream + * \param[in] recog + * \param[in] index for naming of output files of template images + * \param[in] display 1 for showing template images; 0 otherwise + * \return 0 if OK, 1 on error + */ +l_ok +recogShowContent(FILE *fp, + L_RECOG *recog, + l_int32 index, + l_int32 display) +{ +char buf[128]; +l_int32 i, val, count; +PIX *pix; +NUMA *na; + + PROCNAME("recogShowContent"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + fprintf(fp, "Debug print of recog contents\n"); + fprintf(fp, " Setsize: %d\n", recog->setsize); + fprintf(fp, " Binarization threshold: %d\n", recog->threshold); + fprintf(fp, " Maximum matching y-jiggle: %d\n", recog->maxyshift); + if (recog->linew <= 0) + fprintf(fp, " Using image templates for matching\n"); + else + fprintf(fp, " Using templates with fixed line width for matching\n"); + if (recog->scalew == 0) + fprintf(fp, " No width scaling of templates\n"); + else + fprintf(fp, " Template width scaled to %d\n", recog->scalew); + if (recog->scaleh == 0) + fprintf(fp, " No height scaling of templates\n"); + else + fprintf(fp, " Template height scaled to %d\n", recog->scaleh); + fprintf(fp, " Number of samples in each class:\n"); + pixaaGetCount(recog->pixaa_u, &na); + for (i = 0; i < recog->setsize; i++) { + l_dnaGetIValue(recog->dna_tochar, i, &val); + numaGetIValue(na, i, &count); + if (val < 128) + fprintf(fp, " class %d, char %c: %d\n", i, val, count); + else + fprintf(fp, " class %d, val %d: %d\n", i, val, count); + } + numaDestroy(&na); + + if (display) { + lept_mkdir("lept/recog"); + pix = pixaaDisplayByPixa(recog->pixaa_u, 20, 20, 1000); + snprintf(buf, sizeof(buf), "/tmp/lept/recog/templates_u.%d.png", index); + pixWriteDebug(buf, pix, IFF_PNG); + pixDisplay(pix, 0, 200 * index); + pixDestroy(&pix); + if (recog->train_done) { + pix = pixaaDisplayByPixa(recog->pixaa, 20, 20, 1000); + snprintf(buf, sizeof(buf), + "/tmp/lept/recog/templates.%d.png", index); + pixWriteDebug(buf, pix, IFF_PNG); + pixDisplay(pix, 800, 200 * index); + pixDestroy(&pix); + } + } + return 0; +} + + +/*! + * \brief recogDebugAverages() + * + * \param[in] precog addr of recog + * \param[in] debug 0 no output; 1 for images; 2 for text; 3 for both + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates an image that pairs each of the input images used
+ *          in training with the average template that it is best
+ *          correlated to.  This is written into the recog.
+ *      (2) It also generates pixa_tr of all the input training images,
+ *          which can be used, e.g., in recogShowMatchesInRange().
+ *      (3) Destroys the recog if the averaging function finds any bad classes.
+ * 
+ */ +l_ok +recogDebugAverages(L_RECOG **precog, + l_int32 debug) +{ +l_int32 i, j, n, np, index; +l_float32 score; +PIX *pix1, *pix2, *pix3; +PIXA *pixa, *pixat; +PIXAA *paa1, *paa2; +L_RECOG *recog; + + PROCNAME("recogDebugAverages"); + + if (!precog) + return ERROR_INT("&recog not defined", procName, 1); + if ((recog = *precog) == NULL) + return ERROR_INT("recog not defined", procName, 1); + + /* Mark the training as finished if necessary, and make sure + * that the average templates have been built. */ + recogAverageSamples(&recog, 0); + if (!recog) + return ERROR_INT("averaging failed; recog destroyed", procName, 1); + + /* Save a pixa of all the training examples */ + paa1 = recog->pixaa; + if (!recog->pixa_tr) + recog->pixa_tr = pixaaFlattenToPixa(paa1, NULL, L_CLONE); + + /* Destroy any existing image and make a new one */ + if (recog->pixdb_ave) + pixDestroy(&recog->pixdb_ave); + n = pixaaGetCount(paa1, NULL); + paa2 = pixaaCreate(n); + for (i = 0; i < n; i++) { + pixa = pixaCreate(0); + pixat = pixaaGetPixa(paa1, i, L_CLONE); + np = pixaGetCount(pixat); + for (j = 0; j < np; j++) { + pix1 = pixaaGetPix(paa1, i, j, L_CLONE); + recogIdentifyPix(recog, pix1, &pix2); + rchExtract(recog->rch, &index, &score, NULL, NULL, NULL, + NULL, NULL); + if (debug >= 2) + fprintf(stderr, "index = %d, score = %7.3f\n", index, score); + pix3 = pixAddBorder(pix2, 2, 1); + pixaAddPix(pixa, pix3, L_INSERT); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + pixaaAddPixa(paa2, pixa, L_INSERT); + pixaDestroy(&pixat); + } + recog->pixdb_ave = pixaaDisplayByPixa(paa2, 20, 20, 2500); + if (debug % 2) { + lept_mkdir("lept/recog"); + pixWriteDebug("/tmp/lept/recog/templ_match.png", recog->pixdb_ave, + IFF_PNG); + pixDisplay(recog->pixdb_ave, 100, 100); + } + + pixaaDestroy(&paa2); + return 0; +} + + +/*! + * \brief recogShowAverageTemplates() + * + * \param[in] recog + * \return 0 on success, 1 on failure + * + *
+ * Notes:
+ *      (1) This debug routine generates a display of the averaged templates,
+ *          both scaled and unscaled, with the centroid visible in red.
+ * 
+ */ +l_int32 +recogShowAverageTemplates(L_RECOG *recog) +{ +l_int32 i, size; +l_float32 x, y; +PIX *pix1, *pix2, *pixr; +PIXA *pixat, *pixadb; + + PROCNAME("recogShowAverageTemplates"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + + fprintf(stderr, "min/max width_u = (%d,%d); min/max height_u = (%d,%d)\n", + recog->minwidth_u, recog->maxwidth_u, + recog->minheight_u, recog->maxheight_u); + fprintf(stderr, "min splitw = %d, max splith = %d\n", + recog->min_splitw, recog->max_splith); + + pixaDestroy(&recog->pixadb_ave); + + pixr = pixCreate(3, 3, 32); /* 3x3 red square for centroid location */ + pixSetAllArbitrary(pixr, 0xff000000); + pixadb = pixaCreate(2); + + /* Unscaled bitmaps */ + size = recog->setsize; + pixat = pixaCreate(size); + for (i = 0; i < size; i++) { + if ((pix1 = pixaGetPix(recog->pixa_u, i, L_CLONE)) == NULL) + continue; + pix2 = pixConvertTo32(pix1); + ptaGetPt(recog->pta_u, i, &x, &y); + pixRasterop(pix2, (l_int32)(x - 0.5), (l_int32)(y - 0.5), 3, 3, + PIX_SRC, pixr, 0, 0); + pixaAddPix(pixat, pix2, L_INSERT); + pixDestroy(&pix1); + } + pix1 = pixaDisplayTiledInRows(pixat, 32, 3000, 1.0, 0, 20, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + pixDisplay(pix1, 100, 100); + pixaDestroy(&pixat); + + /* Scaled bitmaps */ + pixat = pixaCreate(size); + for (i = 0; i < size; i++) { + if ((pix1 = pixaGetPix(recog->pixa, i, L_CLONE)) == NULL) + continue; + pix2 = pixConvertTo32(pix1); + ptaGetPt(recog->pta, i, &x, &y); + pixRasterop(pix2, (l_int32)(x - 0.5), (l_int32)(y - 0.5), 3, 3, + PIX_SRC, pixr, 0, 0); + pixaAddPix(pixat, pix2, L_INSERT); + pixDestroy(&pix1); + } + pix1 = pixaDisplayTiledInRows(pixat, 32, 3000, 1.0, 0, 20, 0); + pixaAddPix(pixadb, pix1, L_INSERT); + pixDisplay(pix1, 100, 100); + pixaDestroy(&pixat); + pixDestroy(&pixr); + recog->pixadb_ave = pixadb; + return 0; +} + + +/*! + * \brief pixDisplayOutliers() + * + * \param[in] pixas unscaled labeled templates + * \param[in] nas scores of templates (against class averages) + * \return pix tiled pixa with text and scores, or NULL on failure + * + *
+ * Notes:
+ *      (1) This debug routine is called from recogRemoveOutliers2(),
+ *          and takes the saved templates and their scores as input.
+ * 
+ */ +static PIX * +pixDisplayOutliers(PIXA *pixas, + NUMA *nas) +{ +char *text; +char buf[16]; +l_int32 i, n; +l_float32 fval; +PIX *pix1, *pix2; +PIXA *pixa1; + + PROCNAME("pixDisplayOutliers"); + + if (!pixas) + return (PIX *)ERROR_PTR("pixas not defined", procName, NULL); + if (!nas) + return (PIX *)ERROR_PTR("nas not defined", procName, NULL); + n = pixaGetCount(pixas); + if (numaGetCount(nas) != n) + return (PIX *)ERROR_PTR("pixas and nas sizes differ", procName, NULL); + + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixAddBlackOrWhiteBorder(pix1, 25, 25, 0, 0, L_GET_WHITE_VAL); + text = pixGetText(pix1); + numaGetFValue(nas, i, &fval); + snprintf(buf, sizeof(buf), "'%s': %5.2f", text, fval); + pixSetText(pix2, buf); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + pix1 = pixaDisplayTiledWithText(pixa1, 1500, 1.0, 20, 2, 6, 0xff000000); + pixaDestroy(&pixa1); + return pix1; +} + + +/*! + * \brief recogDisplayOutlier() + * + * \param[in] recog + * \param[in] iclass sample is in this class + * \param[in] jsamp index of sample is class i + * \param[in] maxclass index of class with closest average to sample + * \param[in] maxscore score of sample with average of class %maxclass + * \return pix sample and template images, with score, or NULL on error + * + *
+ * Notes:
+ *      (1) This shows three templates, side-by-side:
+ *          - The outlier sample
+ *          - The average template from the same class
+ *          - The average class template that best matched the outlier sample
+ * 
+ */ +static PIX * +recogDisplayOutlier(L_RECOG *recog, + l_int32 iclass, + l_int32 jsamp, + l_int32 maxclass, + l_float32 maxscore) +{ +char buf[64]; +PIX *pix1, *pix2, *pix3, *pix4, *pix5; +PIXA *pixa; + + PROCNAME("recogDisplayOutlier"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + + pix1 = pixaaGetPix(recog->pixaa, iclass, jsamp, L_CLONE); + pix2 = pixaGetPix(recog->pixa, iclass, L_CLONE); + pix3 = pixaGetPix(recog->pixa, maxclass, L_CLONE); + pixa = pixaCreate(3); + pixaAddPix(pixa, pix1, L_INSERT); + pixaAddPix(pixa, pix2, L_INSERT); + pixaAddPix(pixa, pix3, L_INSERT); + pix4 = pixaDisplayTiledInRows(pixa, 32, 400, 2.0, 0, 12, 2); + snprintf(buf, sizeof(buf), "C=%d, BAC=%d, S=%4.2f", iclass, maxclass, + maxscore); + pix5 = pixAddSingleTextblock(pix4, recog->bmf, buf, 0xff000000, + L_ADD_BELOW, NULL); + pixDestroy(&pix4); + pixaDestroy(&pixa); + return pix5; +} + + +/*! + * \brief recogShowMatchesInRange() + * + * \param[in] recog + * \param[in] pixa of 1 bpp images to match + * \param[in] minscore min score to include output + * \param[in] maxscore max score to include output + * \param[in] display 1 to display the result + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives a visual output of the best matches for a given
+ *          range of scores.  Each pair of images can optionally be
+ *          labeled with the index of the best match and the correlation.
+ *      (2) To use this, save a set of 1 bpp images (labeled or
+ *          unlabeled) that can be given to a recognizer in a pixa.
+ *          Then call this function with the pixa and parameters
+ *          to filter a range of scores.
+ * 
+ */ +l_ok +recogShowMatchesInRange(L_RECOG *recog, + PIXA *pixa, + l_float32 minscore, + l_float32 maxscore, + l_int32 display) +{ +l_int32 i, n, index, depth; +l_float32 score; +NUMA *nascore, *naindex; +PIX *pix1, *pix2; +PIXA *pixa1, *pixa2; + + PROCNAME("recogShowMatchesInRange"); + + if (!recog) + return ERROR_INT("recog not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + /* Run the recognizer on the set of images */ + n = pixaGetCount(pixa); + nascore = numaCreate(n); + naindex = numaCreate(n); + pixa1 = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixa, i, L_CLONE); + recogIdentifyPix(recog, pix1, &pix2); + rchExtract(recog->rch, &index, &score, NULL, NULL, NULL, NULL, NULL); + numaAddNumber(nascore, score); + numaAddNumber(naindex, index); + pixaAddPix(pixa1, pix2, L_INSERT); + pixDestroy(&pix1); + } + + /* Filter the set and optionally add text to each */ + pixa2 = pixaCreate(n); + depth = 1; + for (i = 0; i < n; i++) { + numaGetFValue(nascore, i, &score); + if (score < minscore || score > maxscore) continue; + pix1 = pixaGetPix(pixa1, i, L_CLONE); + numaGetIValue(naindex, i, &index); + pix2 = recogShowMatch(recog, pix1, NULL, NULL, index, score); + if (i == 0) depth = pixGetDepth(pix2); + pixaAddPix(pixa2, pix2, L_INSERT); + pixDestroy(&pix1); + } + + /* Package it up */ + pixDestroy(&recog->pixdb_range); + if (pixaGetCount(pixa2) > 0) { + recog->pixdb_range = + pixaDisplayTiledInRows(pixa2, depth, 2500, 1.0, 0, 20, 1); + if (display) + pixDisplay(recog->pixdb_range, 300, 100); + } else { + L_INFO("no character matches in the range of scores\n", procName); + } + + pixaDestroy(&pixa1); + pixaDestroy(&pixa2); + numaDestroy(&nascore); + numaDestroy(&naindex); + return 0; +} + + +/*! + * \brief recogShowMatch() + * + * \param[in] recog + * \param[in] pix1 input pix; several possibilities + * \param[in] pix2 [optional] matching template + * \param[in] box [optional] region in pix1 for which pix2 matches + * \param[in] index index of matching template; use -1 to disable printing + * \param[in] score score of match + * \return pixd pair of images, showing input pix and best template, + * optionally with matching information, or NULL on error. + * + *
+ * Notes:
+ *      (1) pix1 can be one of these:
+ *          (a) The input pix alone, which can be either a single character
+ *              (box == NULL) or several characters that need to be
+ *              segmented.  If more than character is present, the box
+ *              region is displayed with an outline.
+ *          (b) Both the input pix and the matching template.  In this case,
+ *              pix2 and box will both be null.
+ *      (2) If the bmf has been made (by a call to recogMakeBmf())
+ *          and the index >= 0, the text field, match score and index
+ *          will be rendered; otherwise their values will be ignored.
+ * 
+ */ +PIX * +recogShowMatch(L_RECOG *recog, + PIX *pix1, + PIX *pix2, + BOX *box, + l_int32 index, + l_float32 score) +{ +char buf[32]; +char *text; +L_BMF *bmf; +PIX *pix3, *pix4, *pix5, *pixd; +PIXA *pixa; + + PROCNAME("recogShowMatch"); + + if (!recog) + return (PIX *)ERROR_PTR("recog not defined", procName, NULL); + if (!pix1) + return (PIX *)ERROR_PTR("pix1 not defined", procName, NULL); + + bmf = (recog->bmf && index >= 0) ? recog->bmf : NULL; + if (!pix2 && !box && !bmf) /* nothing to do */ + return pixCopy(NULL, pix1); + + pix3 = pixConvertTo32(pix1); + if (box) + pixRenderBoxArb(pix3, box, 1, 255, 0, 0); + + if (pix2) { + pixa = pixaCreate(2); + pixaAddPix(pixa, pix3, L_CLONE); + pixaAddPix(pixa, pix2, L_CLONE); + pix4 = pixaDisplayTiledInRows(pixa, 1, 500, 1.0, 0, 15, 0); + pixaDestroy(&pixa); + } else { + pix4 = pixCopy(NULL, pix3); + } + pixDestroy(&pix3); + + if (bmf) { + pix5 = pixAddBorderGeneral(pix4, 55, 55, 0, 0, 0xffffff00); + recogGetClassString(recog, index, &text); + snprintf(buf, sizeof(buf), "C=%s, S=%4.3f, I=%d", text, score, index); + pixd = pixAddSingleTextblock(pix5, bmf, buf, 0xff000000, + L_ADD_BELOW, NULL); + pixDestroy(&pix5); + LEPT_FREE(text); + } else { + pixd = pixClone(pix4); + } + pixDestroy(&pix4); + + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/regutils.c b/hgdriver/3rdparty/hgOCR/leptonica/regutils.c new file mode 100644 index 0000000..38e5dc4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/regutils.c @@ -0,0 +1,882 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file regutils.c + *
+ *
+ *       Regression test utilities
+ *           l_int32    regTestSetup()
+ *           l_int32    regTestCleanup()
+ *           l_int32    regTestCompareValues()
+ *           l_int32    regTestCompareStrings()
+ *           l_int32    regTestComparePix()
+ *           l_int32    regTestCompareSimilarPix()
+ *           l_int32    regTestCheckFile()
+ *           l_int32    regTestCompareFiles()
+ *           l_int32    regTestWritePixAndCheck()
+ *           l_int32    regTestWriteDataAndCheck()
+ *           char      *regTestGenLocalFilename()
+ *
+ *       Static function
+ *           char      *getRootNameFromArgv0()
+ *
+ *  These functions are for testing and development.  They are not intended
+ *  for use with programs that run in a production environment, such as a
+ *  cloud service with unrestricted access.
+ *
+ *  See regutils.h for how to use this.  Here is a minimal setup:
+ *
+ *  main(int argc, char **argv) {
+ *  ...
+ *  L_REGPARAMS  *rp;
+ *
+ *      if (regTestSetup(argc, argv, &rp))
+ *          return 1;
+ *      ...
+ *      regTestWritePixAndCheck(rp, pix, IFF_PNG);  // 0
+ *      ...
+ *      return regTestCleanup(rp);
+ *  }
+ * 
+ */ + +#include +#include "allheaders.h" + +extern l_int32 NumImageFileFormatExtensions; +extern const char *ImageFileFormatExtensions[]; + +static char *getRootNameFromArgv0(const char *argv0); + + +/*--------------------------------------------------------------------* + * Regression test utilities * + *--------------------------------------------------------------------*/ +/*! + * \brief regTestSetup() + * + * \param[in] argc from invocation; can be either 1 or 2 + * \param[in] argv to regtest: %argv[1] is one of these: + * "generate", "compare", "display" + * \param[out] prp all regression params + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Call this function with the args to the reg test.  The first arg
+ *          is the name of the reg test.  There are three cases:
+ *          Case 1:
+ *              There is either only one arg, or the second arg is "compare".
+ *              This is the mode in which you run a regression test
+ *              (or a set of them), looking for failures and logging
+ *              the results to a file.  The output, which includes
+ *              logging of all reg test failures plus a SUCCESS or
+ *              FAILURE summary for each test, is appended to the file
+ *              "/tmp/lept/reg_results.txt.  For this case, as in Case 2,
+ *              the display field in rp is set to FALSE, preventing
+ *              image display.
+ *          Case 2:
+ *              The second arg is "generate".  This will cause
+ *              generation of new golden files for the reg test.
+ *              The results of the reg test are not recorded, and
+ *              the display field in rp is set to FALSE.
+ *          Case 3:
+ *              The second arg is "display".  The test will run and
+ *              files will be written.  Comparisons with golden files
+ *              will not be carried out, so the only notion of success
+ *              or failure is with tests that do not involve golden files.
+ *              The display field in rp is TRUE, and this is used by
+ *              pixDisplayWithTitle().
+ *      (2) See regutils.h for examples of usage.
+ * 
+ */ +l_ok +regTestSetup(l_int32 argc, + char **argv, + L_REGPARAMS **prp) +{ +char *testname, *vers; +char errormsg[64]; +L_REGPARAMS *rp; + + PROCNAME("regTestSetup"); + + if (argc != 1 && argc != 2) { + snprintf(errormsg, sizeof(errormsg), + "Syntax: %s [ [compare] | generate | display ]", argv[0]); + return ERROR_INT(errormsg, procName, 1); + } + + if ((testname = getRootNameFromArgv0(argv[0])) == NULL) + return ERROR_INT("invalid root", procName, 1); + + setLeptDebugOK(1); /* required for testing */ + + rp = (L_REGPARAMS *)LEPT_CALLOC(1, sizeof(L_REGPARAMS)); + *prp = rp; + rp->testname = testname; + rp->index = -1; /* increment before each test */ + + /* Initialize to true. A failure in any test is registered + * as a failure of the regression test. */ + rp->success = TRUE; + + /* Make sure the lept/regout subdirectory exists */ + lept_mkdir("lept/regout"); + + /* Only open a stream to a temp file for the 'compare' case */ + if (argc == 1 || !strcmp(argv[1], "compare")) { + rp->mode = L_REG_COMPARE; + rp->tempfile = stringNew("/tmp/lept/regout/regtest_output.txt"); + rp->fp = fopenWriteStream(rp->tempfile, "wb"); + if (rp->fp == NULL) { + rp->success = FALSE; + return ERROR_INT("stream not opened for tempfile", procName, 1); + } + } else if (!strcmp(argv[1], "generate")) { + rp->mode = L_REG_GENERATE; + lept_mkdir("lept/golden"); + } else if (!strcmp(argv[1], "display")) { + rp->mode = L_REG_DISPLAY; + rp->display = TRUE; + } else { + LEPT_FREE(rp); + snprintf(errormsg, sizeof(errormsg), + "Syntax: %s [ [generate] | compare | display ]", argv[0]); + return ERROR_INT(errormsg, procName, 1); + } + + /* Print out test name and both the leptonica and + * image libarary versions */ + fprintf(stderr, "\n////////////////////////////////////////////////\n" + "//////////////// %s_reg ///////////////\n" + "////////////////////////////////////////////////\n", + rp->testname); + vers = getLeptonicaVersion(); + fprintf(stderr, "%s : ", vers); + LEPT_FREE(vers); + vers = getImagelibVersions(); + fprintf(stderr, "%s\n", vers); + LEPT_FREE(vers); + + rp->tstart = startTimerNested(); + return 0; +} + + +/*! + * \brief regTestCleanup() + * + * \param[in] rp regression test parameters + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This copies anything written to the temporary file to the
+ *          output file /tmp/lept/reg_results.txt.
+ * 
+ */ +l_ok +regTestCleanup(L_REGPARAMS *rp) +{ +char result[512]; +char *results_file; /* success/failure output in 'compare' mode */ +char *text, *message; +l_int32 retval; +size_t nbytes; + + PROCNAME("regTestCleanup"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + + fprintf(stderr, "Time: %7.3f sec\n", stopTimerNested(rp->tstart)); + + /* If generating golden files or running in display mode, release rp */ + if (!rp->fp) { + LEPT_FREE(rp->testname); + LEPT_FREE(rp->tempfile); + LEPT_FREE(rp); + return 0; + } + + /* Compare mode: read back data from temp file */ + fclose(rp->fp); + text = (char *)l_binaryRead(rp->tempfile, &nbytes); + LEPT_FREE(rp->tempfile); + if (!text) { + rp->success = FALSE; + LEPT_FREE(rp->testname); + LEPT_FREE(rp); + return ERROR_INT("text not returned", procName, 1); + } + + /* Prepare result message */ + if (rp->success) + snprintf(result, sizeof(result), "SUCCESS: %s_reg\n", rp->testname); + else + snprintf(result, sizeof(result), "FAILURE: %s_reg\n", rp->testname); + message = stringJoin(text, result); + LEPT_FREE(text); + results_file = stringNew("/tmp/lept/reg_results.txt"); + fileAppendString(results_file, message); + retval = (rp->success) ? 0 : 1; + LEPT_FREE(results_file); + LEPT_FREE(message); + + LEPT_FREE(rp->testname); + LEPT_FREE(rp); + return retval; +} + + +/*! + * \brief regTestCompareValues() + * + * \param[in] rp regtest parameters + * \param[in] val1 typ. the golden value + * \param[in] val2 typ. the value computed + * \param[in] delta allowed max absolute difference + * \return 0 if OK, 1 on error a failure in comparison is not an error + */ +l_ok +regTestCompareValues(L_REGPARAMS *rp, + l_float32 val1, + l_float32 val2, + l_float32 delta) +{ +l_float32 diff; + + PROCNAME("regTestCompareValues"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + + rp->index++; + diff = L_ABS(val2 - val1); + + /* Record on failure */ + if (diff > delta) { + if (rp->fp) { + fprintf(rp->fp, + "Failure in %s_reg: value comparison for index %d\n" + "difference = %f but allowed delta = %f\n", + rp->testname, rp->index, diff, delta); + } + fprintf(stderr, + "Failure in %s_reg: value comparison for index %d\n" + "difference = %f but allowed delta = %f\n", + rp->testname, rp->index, diff, delta); + rp->success = FALSE; + } + return 0; +} + + +/*! + * \brief regTestCompareStrings() + * + * \param[in] rp regtest parameters + * \param[in] string1 typ. the expected string + * \param[in] bytes1 size of string1 + * \param[in] string2 typ. the computed string + * \param[in] bytes2 size of string2 + * \return 0 if OK, 1 on error a failure in comparison is not an error + */ +l_ok +regTestCompareStrings(L_REGPARAMS *rp, + l_uint8 *string1, + size_t bytes1, + l_uint8 *string2, + size_t bytes2) +{ +l_int32 same; +char buf[256]; + + PROCNAME("regTestCompareStrings"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + + rp->index++; + l_binaryCompare(string1, bytes1, string2, bytes2, &same); + + /* Output on failure */ + if (!same) { + /* Write the two strings to file */ + snprintf(buf, sizeof(buf), "/tmp/lept/regout/string1_%d_%zu", + rp->index, bytes1); + l_binaryWrite(buf, "w", string1, bytes1); + snprintf(buf, sizeof(buf), "/tmp/lept/regout/string2_%d_%zu", + rp->index, bytes2); + l_binaryWrite(buf, "w", string2, bytes2); + + /* Report comparison failure */ + snprintf(buf, sizeof(buf), "/tmp/lept/regout/string*_%d_*", rp->index); + if (rp->fp) { + fprintf(rp->fp, + "Failure in %s_reg: string comp for index %d; " + "written to %s\n", rp->testname, rp->index, buf); + } + fprintf(stderr, + "Failure in %s_reg: string comp for index %d; " + "written to %s\n", rp->testname, rp->index, buf); + rp->success = FALSE; + } + return 0; +} + + +/*! + * \brief regTestComparePix() + * + * \param[in] rp regtest parameters + * \param[in] pix1, pix2 to be tested for equality + * \return 0 if OK, 1 on error a failure in comparison is not an error + * + *
+ * Notes:
+ *      (1) This function compares two pix for equality.  On failure,
+ *          this writes to stderr.
+ * 
+ */ +l_ok +regTestComparePix(L_REGPARAMS *rp, + PIX *pix1, + PIX *pix2) +{ +l_int32 same; + + PROCNAME("regTestComparePix"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (!pix1 || !pix2) { + rp->success = FALSE; + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + } + + rp->index++; + pixEqual(pix1, pix2, &same); + + /* Record on failure */ + if (!same) { + if (rp->fp) { + fprintf(rp->fp, "Failure in %s_reg: pix comparison for index %d\n", + rp->testname, rp->index); + } + fprintf(stderr, "Failure in %s_reg: pix comparison for index %d\n", + rp->testname, rp->index); + rp->success = FALSE; + } + return 0; +} + + +/*! + * \brief regTestCompareSimilarPix() + * + * \param[in] rp regtest parameters + * \param[in] pix1, pix2 to be tested for near equality + * \param[in] mindiff minimum pixel difference to be counted; > 0 + * \param[in] maxfract maximum fraction of pixels allowed to have + * diff greater than or equal to mindiff + * \param[in] printstats use 1 to print normalized histogram to stderr + * \return 0 if OK, 1 on error a failure in similarity comparison + * is not an error + * + *
+ * Notes:
+ *      (1) This function compares two pix for near equality.  On failure,
+ *          this writes to stderr.
+ *      (2) The pix are similar if the fraction of non-conforming pixels
+ *          does not exceed %maxfract.  Pixels are non-conforming if
+ *          the difference in pixel values equals or exceeds %mindiff.
+ *          Typical values might be %mindiff = 15 and %maxfract = 0.01.
+ *      (3) The input images must have the same size and depth.  The
+ *          pixels for comparison are typically subsampled from the images.
+ *      (4) Normally, use %printstats = 0.  In debugging mode, to see
+ *          the relation between %mindiff and the minimum value of
+ *          %maxfract for success, set this to 1.
+ * 
+ */ +l_ok +regTestCompareSimilarPix(L_REGPARAMS *rp, + PIX *pix1, + PIX *pix2, + l_int32 mindiff, + l_float32 maxfract, + l_int32 printstats) +{ +l_int32 w, h, factor, similar; + + PROCNAME("regTestCompareSimilarPix"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (!pix1 || !pix2) { + rp->success = FALSE; + return ERROR_INT("pix1 and pix2 not both defined", procName, 1); + } + + rp->index++; + pixGetDimensions(pix1, &w, &h, NULL); + factor = L_MAX(w, h) / 400; + factor = L_MAX(1, L_MIN(factor, 4)); /* between 1 and 4 */ + pixTestForSimilarity(pix1, pix2, factor, mindiff, maxfract, 0.0, + &similar, printstats); + + /* Record on failure */ + if (!similar) { + if (rp->fp) { + fprintf(rp->fp, + "Failure in %s_reg: pix similarity comp for index %d\n", + rp->testname, rp->index); + } + fprintf(stderr, "Failure in %s_reg: pix similarity comp for index %d\n", + rp->testname, rp->index); + rp->success = FALSE; + } + return 0; +} + + +/*! + * \brief regTestCheckFile() + * + * \param[in] rp regtest parameters + * \param[in] localname name of output file from reg test + * \return 0 if OK, 1 on error a failure in comparison is not an error + * + *
+ * Notes:
+ *      (1) This function does one of three things, depending on the mode:
+ *           * "generate": makes a "golden" file as a copy of %localname.
+ *           * "compare": compares %localname contents with the golden file
+ *           * "display": this does nothing
+ *      (2) The canonical format of the golden filenames is:
+ *            /tmp/lept/golden/[root of main name]_golden.[index].
+ *                                                       [ext of localname]
+ *          e.g.,
+ *             /tmp/lept/golden/maze_golden.0.png
+ *      (3) The local file can be made in any subdirectory of /tmp/lept,
+ *          including /tmp/lept/regout/.
+ *      (4) It is important to add an extension to the local name, such as
+ *             /tmp/lept/maze/file1.png    (extension ".png")
+ *          because the extension is added to the name of the golden file.
+ * 
+ */ +l_ok +regTestCheckFile(L_REGPARAMS *rp, + const char *localname) +{ +char *ext; +char namebuf[256]; +l_int32 ret, same, format; +PIX *pix1, *pix2; + + PROCNAME("regTestCheckFile"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (!localname) { + rp->success = FALSE; + return ERROR_INT("local name not defined", procName, 1); + } + if (rp->mode != L_REG_GENERATE && rp->mode != L_REG_COMPARE && + rp->mode != L_REG_DISPLAY) { + rp->success = FALSE; + return ERROR_INT("invalid mode", procName, 1); + } + rp->index++; + + /* If display mode, no generation and no testing */ + if (rp->mode == L_REG_DISPLAY) return 0; + + /* Generate the golden file name; used in 'generate' and 'compare' */ + splitPathAtExtension(localname, NULL, &ext); + snprintf(namebuf, sizeof(namebuf), "/tmp/lept/golden/%s_golden.%02d%s", + rp->testname, rp->index, ext); + LEPT_FREE(ext); + + /* Generate mode. No testing. */ + if (rp->mode == L_REG_GENERATE) { + /* Save the file as a golden file */ + ret = fileCopy(localname, namebuf); +#if 0 /* Enable for details on writing of golden files */ + if (!ret) { + char *local = genPathname(localname, NULL); + char *golden = genPathname(namebuf, NULL); + L_INFO("Copy: %s to %s\n", procName, local, golden); + LEPT_FREE(local); + LEPT_FREE(golden); + } +#endif + return ret; + } + + /* Compare mode: test and record on failure. This can be used + * for all image formats, as well as for all files of serialized + * data, such as boxa, pta, etc. In all cases except for + * GIF compressed images, we compare the files to see if they + * are identical. GIF doesn't support RGB images; to write + * a 32 bpp RGB image in GIF, we do a lossy quantization to + * 256 colors, so the cycle read-RGB/write-GIF is not idempotent. + * And although the read/write cycle for GIF images with bpp <= 8 + * is idempotent in the image pixels, it is not idempotent in the + * actual file bytes; tests comparing file bytes before and after + * a GIF read/write cycle will fail. So for GIF we uncompress + * the two images and compare the actual pixels. PNG is both + * lossless and idempotent in file bytes on read/write, so it is + * not necessary to compare pixels. (Comparing pixels requires + * decompression, and thus would increase the regression test + * time. JPEG is lossy and not idempotent in the image pixels, + * so no tests are constructed that would require it. */ + findFileFormat(localname, &format); + if (format == IFF_GIF) { + same = 0; + pix1 = pixRead(localname); + pix2 = pixRead(namebuf); + pixEqual(pix1, pix2, &same); + pixDestroy(&pix1); + pixDestroy(&pix2); + } else { + filesAreIdentical(localname, namebuf, &same); + } + if (!same) { + fprintf(rp->fp, "Failure in %s_reg, index %d: comparing %s with %s\n", + rp->testname, rp->index, localname, namebuf); + fprintf(stderr, "Failure in %s_reg, index %d: comparing %s with %s\n", + rp->testname, rp->index, localname, namebuf); + rp->success = FALSE; + } + + return 0; +} + + +/*! + * \brief regTestCompareFiles() + * + * \param[in] rp regtest parameters + * \param[in] index1 of one output file from reg test + * \param[in] index2 of another output file from reg test + * \return 0 if OK, 1 on error a failure in comparison is not an error + * + *
+ * Notes:
+ *      (1) This only does something in "compare" mode.
+ *      (2) The canonical format of the golden filenames is:
+ *            /tmp/lept/golden/[root of main name]_golden.[index].
+ *                                                      [ext of localname]
+ *          e.g.,
+ *            /tmp/lept/golden/maze_golden.0.png
+ * 
+ */ +l_ok +regTestCompareFiles(L_REGPARAMS *rp, + l_int32 index1, + l_int32 index2) +{ +char *name1, *name2; +char namebuf[256]; +l_int32 same; +SARRAY *sa; + + PROCNAME("regTestCompareFiles"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (index1 < 0 || index2 < 0) { + rp->success = FALSE; + return ERROR_INT("index1 and/or index2 is negative", procName, 1); + } + if (index1 == index2) { + rp->success = FALSE; + return ERROR_INT("index1 must differ from index2", procName, 1); + } + + rp->index++; + if (rp->mode != L_REG_COMPARE) return 0; + + /* Generate the golden file names */ + snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index1); + sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0); + if (sarrayGetCount(sa) != 1) { + sarrayDestroy(&sa); + rp->success = FALSE; + L_ERROR("golden file %s not found\n", procName, namebuf); + return 1; + } + name1 = sarrayGetString(sa, 0, L_COPY); + sarrayDestroy(&sa); + + snprintf(namebuf, sizeof(namebuf), "%s_golden.%02d", rp->testname, index2); + sa = getSortedPathnamesInDirectory("/tmp/lept/golden", namebuf, 0, 0); + if (sarrayGetCount(sa) != 1) { + sarrayDestroy(&sa); + rp->success = FALSE; + LEPT_FREE(name1); + L_ERROR("golden file %s not found\n", procName, namebuf); + return 1; + } + name2 = sarrayGetString(sa, 0, L_COPY); + sarrayDestroy(&sa); + + /* Test and record on failure */ + filesAreIdentical(name1, name2, &same); + if (!same) { + fprintf(rp->fp, + "Failure in %s_reg, index %d: comparing %s with %s\n", + rp->testname, rp->index, name1, name2); + fprintf(stderr, + "Failure in %s_reg, index %d: comparing %s with %s\n", + rp->testname, rp->index, name1, name2); + rp->success = FALSE; + } + + LEPT_FREE(name1); + LEPT_FREE(name2); + return 0; +} + + +/*! + * \brief regTestWritePixAndCheck() + * + * \param[in] rp regtest parameters + * \param[in] pix to be written + * \param[in] format of output pix + * \return 0 if OK, 1 on error a failure in comparison is not an error + * + *
+ * Notes:
+ *      (1) This function makes it easy to write the pix in a numbered
+ *          sequence of files, and either to:
+ *             (a) write the golden file ("generate" arg to regression test)
+ *             (b) make a local file and "compare" with the golden file
+ *             (c) make a local file and "display" the results
+ *      (2) The canonical format of the local filename is:
+ *            /tmp/lept/regout/[root of main name].[count].[format extension]
+ *          e.g., for scale_reg,
+ *            /tmp/lept/regout/scale.0.png
+ *          The golden file name mirrors this in the usual way.
+ *      (3) The check is done between the written files, which requires
+ *          the files to be identical. The exception is for GIF, which
+ *          only requires that all pixels in the decoded pix are identical.
+ * 
+ */ +l_ok +regTestWritePixAndCheck(L_REGPARAMS *rp, + PIX *pix, + l_int32 format) +{ +char namebuf[256]; + + PROCNAME("regTestWritePixAndCheck"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (!pix) { + rp->success = FALSE; + return ERROR_INT("pix not defined", procName, 1); + } + if (format < 0 || format >= NumImageFileFormatExtensions) { + rp->success = FALSE; + return ERROR_INT("invalid format", procName, 1); + } + + /* Generate the local file name */ + snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s", + rp->testname, rp->index + 1, ImageFileFormatExtensions[format]); + + /* Write the local file */ + if (pixGetDepth(pix) < 8) + pixSetPadBits(pix, 0); + pixWrite(namebuf, pix, format); + + /* Either write the golden file ("generate") or check the + local file against an existing golden file ("compare") */ + regTestCheckFile(rp, namebuf); + + return 0; +} + + +/*! + * \brief regTestWriteDataAndCheck() + * + * \param[in] rp regtest parameters + * \param[in] data to be written + * \param[in] nbytes of data to be written + * \param[in] ext filename extension (e.g.: "ba", "pta") + * \return 0 if OK, 1 on error a failure in comparison is not an error + * + *
+ * Notes:
+ *      (1) This function makes it easy to write data in a numbered
+ *          sequence of files, and either to:
+ *             (a) write the golden file ("generate" arg to regression test)
+ *             (b) make a local file and "compare" with the golden file
+ *             (c) make a local file and "display" the results
+ *      (2) The canonical format of the local filename is:
+ *            /tmp/lept/regout/[root of main name].[count].[ext]
+ *          e.g., for the first boxaa in quadtree_reg,
+ *            /tmp/lept/regout/quadtree.0.baa
+ *          The golden file name mirrors this in the usual way.
+ *      (3) The data can be anything.  It is most useful for serialized
+ *          output of data, such as boxa, pta, etc.
+ *      (4) The file extension is arbitrary.  It is included simply
+ *          to make the content type obvious when examining written files.
+ *      (5) The check is done between the written files, which requires
+ *          the files to be identical.
+ * 
+ */ +l_ok +regTestWriteDataAndCheck(L_REGPARAMS *rp, + void *data, + size_t nbytes, + const char *ext) +{ +char namebuf[256]; + + PROCNAME("regTestWriteDataAndCheck"); + + if (!rp) + return ERROR_INT("rp not defined", procName, 1); + if (!data || nbytes == 0) { + rp->success = FALSE; + return ERROR_INT("data not defined or size == 0", procName, 1); + } + + /* Generate the local file name */ + snprintf(namebuf, sizeof(namebuf), "/tmp/lept/regout/%s.%02d.%s", + rp->testname, rp->index + 1, ext); + + /* Write the local file */ + l_binaryWrite(namebuf, "w", data, nbytes); + + /* Either write the golden file ("generate") or check the + local file against an existing golden file ("compare") */ + regTestCheckFile(rp, namebuf); + return 0; +} + + +/*! + * \brief regTestGenLocalFilename() + * + * \param[in] rp regtest parameters + * \param[in] index use -1 for current index + * \param[in] format of image; e.g., IFF_PNG + * \return filename if OK, or NULL on error + * + *
+ * Notes:
+ *      (1) This is used to get the name of a file in the regout
+ *          subdirectory, that has been made and is used to test against
+ *          the golden file.  You can either specify a particular index
+ *          value, or with %index == -1, this returns the most recently
+ *          written file.  The latter case lets you read a pix from a
+ *          file that has just been written with regTestWritePixAndCheck(),
+ *          which is useful for testing formatted read/write functions.
+ *
+ * 
+ */ +char * +regTestGenLocalFilename(L_REGPARAMS *rp, + l_int32 index, + l_int32 format) +{ +char buf[64]; +l_int32 ind; + + PROCNAME("regTestGenLocalFilename"); + + if (!rp) + return (char *)ERROR_PTR("rp not defined", procName, NULL); + + ind = (index >= 0) ? index : rp->index; + snprintf(buf, sizeof(buf), "/tmp/lept/regout/%s.%02d.%s", + rp->testname, ind, ImageFileFormatExtensions[format]); + return stringNew(buf); +} + + +/*! + * \brief getRootNameFromArgv0() + * + * \param[in] argv0 + * \return root name without the '_reg', or NULL on error + * + *
+ * Notes:
+ *      (1) For example, from psioseg_reg, we want to extract
+ *          just 'psioseg' as the root.
+ *      (2) In unix with autotools, the executable is not X,
+ *          but ./.libs/lt-X.   So in addition to stripping out the
+ *          last 4 characters of the tail, we have to check for
+ *          the '-' and strip out the "lt-" prefix if we find it.
+ * 
+ */ +static char * +getRootNameFromArgv0(const char *argv0) +{ +l_int32 len; +char *root; + + PROCNAME("getRootNameFromArgv0"); + + splitPathAtDirectory(argv0, NULL, &root); + if ((len = strlen(root)) <= 4) { + LEPT_FREE(root); + return (char *)ERROR_PTR("invalid argv0; too small", procName, NULL); + } + +#ifndef _WIN32 + { + char *newroot; + l_int32 loc; + if (stringFindSubstr(root, "-", &loc)) { + newroot = stringNew(root + loc + 1); /* strip out "lt-" */ + LEPT_FREE(root); + root = newroot; + len = strlen(root); + } + len -= 4; /* remove the "_reg" suffix */ + } +#else + if (strstr(root, ".exe") != NULL) + len -= 4; + if (strstr(root, "_reg") == root + len - 4) + len -= 4; +#endif /* ! _WIN32 */ + + root[len] = '\0'; /* terminate */ + return root; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/regutils.h b/hgdriver/3rdparty/hgOCR/leptonica/regutils.h new file mode 100644 index 0000000..2f1d5e4 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/regutils.h @@ -0,0 +1,141 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_REGUTILS_H +#define LEPTONICA_REGUTILS_H + +/*! + * \file regutils.h + * + *
+ *   Contains this regression test parameter packaging struct
+ *       struct L_RegParams
+ *
+ *   The regression test utility allows you to write regression tests
+ *   that compare results with existing "golden files" and with
+ *   compiled in data.
+ *
+ *   Regression tests can be called in three ways.
+ *   For example, for distance_reg:
+ *
+ *       Case 1: distance_reg [compare]
+ *           This runs the test against the set of golden files.  It
+ *           appends to 'outfile.txt' either "SUCCESS" or "FAILURE",
+ *           as well as the details of any parts of the test that failed.
+ *           It writes to a temporary file stream (fp).
+ *           Using 'compare' on the command line is optional.
+ *
+ *       Case 2: distance_reg generate
+ *           This generates golden files in /tmp for the reg test.
+ *
+ *       Case 3: distance_reg display
+ *           This runs the test but makes no comparison of the output
+ *           against the set of golden files.  In addition, this displays
+ *           images and plots that are specified in the test under
+ *           control of the display variable.  Display is enabled only
+ *           for this case.
+ *
+ *   Regression tests follow the pattern given below.  Tests are
+ *   automatically numbered sequentially, and it is convenient to
+ *   comment each with a number to keep track (for comparison tests
+ *   and for debugging).  In an actual case, comparisons of pix and
+ *   of files can occur in any order.  We give a specific order here
+ *   for clarity.
+ *
+ *       L_REGPARAMS  *rp;  // holds data required by the test functions
+ *
+ *       // Setup variables; optionally open stream
+ *       if (regTestSetup(argc, argv, &rp))
+ *           return 1;
+ *
+ *       // Test pairs of generated pix for identity.  This compares
+ *       // two pix; no golden file is generated.
+ *       regTestComparePix(rp, pix1, pix2);  // 0
+ *
+ *       // Test pairs of generated pix for similarity.  This compares
+ *       // two pix; no golden file is generated.  The last arg determines
+ *       // if stats are to be written to stderr.
+ *       regTestCompareSimilarPix(rp, pix1, pix2, 15, 0.001, 0);  // 1
+ *
+ *       // Generation of  outputs and testing for identity
+ *       // These files can be anything, of course.
+ *       regTestCheckFile(rp, );  // 2
+ *       regTestCheckFile(rp, );  // 3
+ *
+ *       // Test pairs of output golden files for identity.  Here we
+ *       // are comparing golden files 2 and 3.
+ *       regTestCompareFiles(rp, 2, 3);  // 4
+ *
+ *       // "Write and check".  This writes a pix using a canonical
+ *       // formulation for the local filename and either:
+ *       //     case 1: generates a golden file
+ *       //     case 2: compares the local file with a golden file
+ *       //     case 3: generates local files and displays
+ *       // Here we write the pix compressed with png and jpeg, respectively;
+ *       // Then check against the golden file.  The internal %index
+ *       // is incremented; it is embedded in the local filename and,
+ *       // if generating, in the golden file as well.
+ *       regTestWritePixAndCheck(rp, pix1, IFF_PNG);  // 5
+ *       regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG);  // 6
+ *
+ *       // Display if reg test was called in 'display' mode
+ *       pixDisplayWithTitle(pix1, 100, 100, NULL, rp->display);
+ *
+ *       // Clean up and output result
+ *       regTestCleanup(rp);
+ * 
+ */ + +/*----------------------------------------------------------------------------* + * Regression test parameter packer * + *----------------------------------------------------------------------------*/ + +/*! Regression test parameter packer */ +struct L_RegParams +{ + FILE *fp; /*!< stream to temporary output file for compare mode */ + char *testname; /*!< name of test, without '_reg' */ + char *tempfile; /*!< name of temp file for compare mode output */ + l_int32 mode; /*!< generate, compare or display */ + l_int32 index; /*!< index into saved files for this test; 0-based */ + l_int32 success; /*!< overall result of the test */ + l_int32 display; /*!< 1 if in display mode; 0 otherwise */ + L_TIMER tstart; /*!< marks beginning of the reg test */ +}; +typedef struct L_RegParams L_REGPARAMS; + + + /*! Running modes for the test */ +/*! Regtest Mode */ +enum { + L_REG_GENERATE = 0, + L_REG_COMPARE = 1, + L_REG_DISPLAY = 2 +}; + + +#endif /* LEPTONICA_REGUTILS_H */ + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rop.c b/hgdriver/3rdparty/hgOCR/leptonica/rop.c new file mode 100644 index 0000000..e20751b --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rop.c @@ -0,0 +1,516 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file rop.c + *
+ *      General rasterop
+ *           l_int32    pixRasterop()
+ *
+ *      In-place full band translation
+ *           l_int32    pixRasteropVip()
+ *           l_int32    pixRasteropHip()
+ *
+ *      Full image translation (general and in-place)
+ *           l_int32    pixTranslate()
+ *           l_int32    pixRasteropIP()
+ *
+ *      Full image rasterop with no translation
+ *           l_int32    pixRasteropFullImage()
+ * 
+ */ + +#include +#include "allheaders.h" + +/*--------------------------------------------------------------------* + * General rasterop (basic pix interface) * + *--------------------------------------------------------------------*/ +/*! + * \brief pixRasterop() + * + * \param[in] pixd dest pix + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \param[in] pixs src pix + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return 0 if OK; 1 on error. + * + *
+ * Notes:
+ *      (1) This has the standard set of 9 args for rasterop.
+ *          This function is your friend; it is worth memorizing!
+ *      (2) If the operation involves only dest, this calls
+ *          rasteropUniLow().  Otherwise, checks depth of the
+ *          src and dest, and if they match, calls rasteropLow().
+ *      (3) For the two-image operation, where both pixs and pixd
+ *          are defined, they are typically different images.  However
+ *          there are cases, such as pixSetMirroredBorder(), where
+ *          in-place operations can be done, blitting pixels from
+ *          one part of pixd to another.  Consequently, we permit
+ *          such operations.  If you use them, be sure that there
+ *          is no overlap between the source and destination rectangles
+ *          in pixd (!)
+ *
+ *  Background:
+ *  -----------
+ *
+ *  There are 18 operations, described by the op codes in pix.h.
+ *
+ *  One, PIX_DST, is a no-op.
+ *
+ *  Three, PIX_CLR, PIX_SET, and PIX_NOT(PIX_DST) operate only on the dest.
+ *  These are handled by the low-level rasteropUniLow().
+ *
+ *  The other 14 involve the both the src and the dest, and depend on
+ *  the bit values of either just the src or the bit values of both
+ *  src and dest.  They are handled by rasteropLow():
+ *
+ *          PIX_SRC                             s
+ *          PIX_NOT(PIX_SRC)                   ~s
+ *          PIX_SRC | PIX_DST                   s | d
+ *          PIX_SRC & PIX_DST                   s & d
+ *          PIX_SRC ^ PIX_DST                   s ^ d
+ *          PIX_NOT(PIX_SRC) | PIX_DST         ~s | d
+ *          PIX_NOT(PIX_SRC) & PIX_DST         ~s & d
+ *          PIX_NOT(PIX_SRC) ^ PIX_DST         ~s ^ d
+ *          PIX_SRC | PIX_NOT(PIX_DST)          s | ~d
+ *          PIX_SRC & PIX_NOT(PIX_DST)          s & ~d
+ *          PIX_SRC ^ PIX_NOT(PIX_DST)          s ^ ~d
+ *          PIX_NOT(PIX_SRC | PIX_DST)         ~(s | d)
+ *          PIX_NOT(PIX_SRC & PIX_DST)         ~(s & d)
+ *          PIX_NOT(PIX_SRC ^ PIX_DST)         ~(s ^ d)
+ *
+ *  Each of these is implemented with one of three low-level
+ *  functions, depending on the alignment of the left edge
+ *  of the src and dest rectangles:
+ *      * a fastest implementation if both left edges are
+ *        (32-bit) word aligned
+ *      * a very slightly slower implementation if both left
+ *        edges have the same relative (32-bit) word alignment
+ *      * the general routine that is invoked when
+ *        both left edges have different word alignment
+ *
+ *  Of the 14 binary rasterops above, only 12 are unique
+ *  logical combinations (out of a possible 16) of src
+ *  and dst bits:
+ *
+ *        (sd)         (11)   (10)   (01)   (00)
+ *   -----------------------------------------------
+ *         s            1      1      0      0
+ *        ~s            0      1      0      1
+ *       s | d          1      1      1      0
+ *       s & d          1      0      0      0
+ *       s ^ d          0      1      1      0
+ *      ~s | d          1      0      1      1
+ *      ~s & d          0      0      1      0
+ *      ~s ^ d          1      0      0      1
+ *       s | ~d         1      1      0      1
+ *       s & ~d         0      1      0      0
+ *       s ^ ~d         1      0      0      1
+ *      ~(s | d)        0      0      0      1
+ *      ~(s & d)        0      1      1      1
+ *      ~(s ^ d)        1      0      0      1
+ *
+ *  Note that the following three operations are equivalent:
+ *      ~(s ^ d)
+ *      ~s ^ d
+ *      s ^ ~d
+ *  and in the implementation, we call them out with the first form;
+ *  namely, ~(s ^ d).
+ *
+ *  Of the 16 possible binary combinations of src and dest bits,
+ *  the remaining 4 unique ones are independent of the src bit.
+ *  They depend on either just the dest bit or on neither
+ *  the src nor dest bits:
+ *
+ *         d            1      0      1      0    (indep. of s)
+ *        ~d            0      1      0      1    (indep. of s)
+ *        CLR           0      0      0      0    (indep. of both s & d)
+ *        SET           1      1      1      1    (indep. of both s & d)
+ *
+ *  As mentioned above, three of these are implemented by
+ *  rasteropUniLow(), and one is a no-op.
+ *
+ *  How can these operation codes be represented by bits
+ *  in such a way that when the basic operations are performed
+ *  on the bits the results are unique for unique
+ *  operations, and mimic the logic table given above?
+ *
+ *  The answer is to choose a particular order of the pairings:
+ *         (sd)         (11)   (10)   (01)   (00)
+ *  (which happens to be the same as in the above table)
+ *  and to translate the result into 4-bit representations
+ *  of s and d.  For example, the Sun rasterop choice
+ *  (omitting the extra bit for clipping) is
+ *
+ *      PIX_SRC      0xc
+ *      PIX_DST      0xa
+ *
+ *  This corresponds to our pairing order given above:
+ *         (sd)         (11)   (10)   (01)   (00)
+ *  where for s = 1 we get the bit pattern
+ *       PIX_SRC:        1      1      0      0     (0xc)
+ *  and for d = 1 we get the pattern
+ *       PIX_DST:         1      0      1      0    (0xa)
+ *
+ *  OK, that's the pairing order that Sun chose.  How many different
+ *  ways can we assign bit patterns to PIX_SRC and PIX_DST to get
+ *  the boolean ops to work out?  Any of the 4 pairs can be put
+ *  in the first position, any of the remaining 3 pairs can go
+ *  in the second; and one of the remaining 2 pairs can go the the third.
+ *  There is a total of 4*3*2 = 24 ways these pairs can be permuted.
+ * 
+ */ +l_ok +pixRasterop(PIX *pixd, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op, + PIX *pixs, + l_int32 sx, + l_int32 sy) +{ +l_int32 dd; + + PROCNAME("pixRasterop"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + + if (op == PIX_DST) /* no-op */ + return 0; + + /* Check if operation is only on dest */ + dd = pixGetDepth(pixd); + if (op == PIX_CLR || op == PIX_SET || op == PIX_NOT(PIX_DST)) { + rasteropUniLow(pixGetData(pixd), + pixGetWidth(pixd), pixGetHeight(pixd), dd, + pixGetWpl(pixd), + dx, dy, dw, dh, + op); + return 0; + } + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Check depth of src and dest; these must agree */ + if (dd != pixGetDepth(pixs)) + return ERROR_INT("depths of pixs and pixd differ", procName, 1); + + rasteropLow(pixGetData(pixd), + pixGetWidth(pixd), pixGetHeight(pixd), dd, + pixGetWpl(pixd), + dx, dy, dw, dh, + op, + pixGetData(pixs), + pixGetWidth(pixs), pixGetHeight(pixs), + pixGetWpl(pixs), + sx, sy); + + return 0; +} + + +/*--------------------------------------------------------------------* + * In-place full band translation * + *--------------------------------------------------------------------*/ +/*! + * \brief pixRasteropVip() + * + * \param[in] pixd in-place + * \param[in] bx left edge of vertical band + * \param[in] bw width of vertical band + * \param[in] vshift vertical shift of band; vshift > 0 is down + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This rasterop translates a vertical band of the
+ *          image either up or down, bringing in either white
+ *          or black pixels from outside the image.
+ *      (2) The vertical band extends the full height of pixd.
+ *      (3) If a colormap exists, the nearest color to white or black
+ *          is brought in.
+ * 
+ */ +l_ok +pixRasteropVip(PIX *pixd, + l_int32 bx, + l_int32 bw, + l_int32 vshift, + l_int32 incolor) +{ +l_int32 w, h, d, index, op; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixRasteropVip"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid value for incolor", procName, 1); + if (bw <= 0) + return ERROR_INT("bw must be > 0", procName, 1); + + if (vshift == 0) + return 0; + + pixGetDimensions(pixd, &w, &h, &d); + rasteropVipLow(pixGetData(pixd), w, h, d, pixGetWpl(pixd), bx, bw, vshift); + + cmap = pixGetColormap(pixd); + if (!cmap) { + if ((d == 1 && incolor == L_BRING_IN_BLACK) || + (d > 1 && incolor == L_BRING_IN_WHITE)) + op = PIX_SET; + else + op = PIX_CLR; + + /* Set the pixels brought in at top or bottom */ + if (vshift > 0) + pixRasterop(pixd, bx, 0, bw, vshift, op, NULL, 0, 0); + else /* vshift < 0 */ + pixRasterop(pixd, bx, h + vshift, bw, -vshift, op, NULL, 0, 0); + return 0; + } + + /* Get the nearest index and fill with that */ + if (incolor == L_BRING_IN_BLACK) + pixcmapGetRankIntensity(cmap, 0.0, &index); + else /* white */ + pixcmapGetRankIntensity(cmap, 1.0, &index); + pixt = pixCreate(bw, L_ABS(vshift), d); + pixSetAllArbitrary(pixt, index); + if (vshift > 0) + pixRasterop(pixd, bx, 0, bw, vshift, PIX_SRC, pixt, 0, 0); + else /* vshift < 0 */ + pixRasterop(pixd, bx, h + vshift, bw, -vshift, PIX_SRC, pixt, 0, 0); + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixRasteropHip() + * + * \param[in] pixd in-place operation + * \param[in] by top of horizontal band + * \param[in] bh height of horizontal band + * \param[in] hshift horizontal shift of band; hshift > 0 is to right + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This rasterop translates a horizontal band of the
+ *          image either left or right, bringing in either white
+ *          or black pixels from outside the image.
+ *      (2) The horizontal band extends the full width of pixd.
+ *      (3) If a colormap exists, the nearest color to white or black
+ *          is brought in.
+ * 
+ */ +l_ok +pixRasteropHip(PIX *pixd, + l_int32 by, + l_int32 bh, + l_int32 hshift, + l_int32 incolor) +{ +l_int32 w, h, d, index, op; +PIX *pixt; +PIXCMAP *cmap; + + PROCNAME("pixRasteropHip"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid value for incolor", procName, 1); + if (bh <= 0) + return ERROR_INT("bh must be > 0", procName, 1); + + if (hshift == 0) + return 0; + + pixGetDimensions(pixd, &w, &h, &d); + rasteropHipLow(pixGetData(pixd), h, d, pixGetWpl(pixd), by, bh, hshift); + + cmap = pixGetColormap(pixd); + if (!cmap) { + if ((d == 1 && incolor == L_BRING_IN_BLACK) || + (d > 1 && incolor == L_BRING_IN_WHITE)) + op = PIX_SET; + else + op = PIX_CLR; + + /* Set the pixels brought in at left or right */ + if (hshift > 0) + pixRasterop(pixd, 0, by, hshift, bh, op, NULL, 0, 0); + else /* hshift < 0 */ + pixRasterop(pixd, w + hshift, by, -hshift, bh, op, NULL, 0, 0); + return 0; + } + + /* Get the nearest index and fill with that */ + if (incolor == L_BRING_IN_BLACK) + pixcmapGetRankIntensity(cmap, 0.0, &index); + else /* white */ + pixcmapGetRankIntensity(cmap, 1.0, &index); + pixt = pixCreate(L_ABS(hshift), bh, d); + pixSetAllArbitrary(pixt, index); + if (hshift > 0) + pixRasterop(pixd, 0, by, hshift, bh, PIX_SRC, pixt, 0, 0); + else /* hshift < 0 */ + pixRasterop(pixd, w + hshift, by, -hshift, bh, PIX_SRC, pixt, 0, 0); + pixDestroy(&pixt); + return 0; +} + + +/*--------------------------------------------------------------------* + * Full image translation (general and in-place) * + *--------------------------------------------------------------------*/ +/*! + * \brief pixTranslate() + * + * \param[in] pixd [optional] destination: this can be null, + * equal to pixs, or different from pixs + * \param[in] pixs + * \param[in] hshift horizontal shift; hshift > 0 is to right + * \param[in] vshift vertical shift; vshift > 0 is down + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) The general pattern is:
+ *            pixd = pixTranslate(pixd, pixs, ...);
+ *          For clarity, when you know the case, use one of these:
+ *            pixd = pixTranslate(NULL, pixs, ...);  // new
+ *            pixTranslate(pixs, pixs, ...);         // in-place
+ *            pixTranslate(pixd, pixs, ...);         // to existing pixd
+ *      (2) If an existing pixd is not the same size as pixs, the
+ *          image data will be reallocated.
+ * 
+ */ +PIX * +pixTranslate(PIX *pixd, + PIX *pixs, + l_int32 hshift, + l_int32 vshift, + l_int32 incolor) +{ + PROCNAME("pixTranslate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + pixRasteropIP(pixd, hshift, vshift, incolor); + return pixd; +} + + +/*! + * \brief pixRasteropIP() + * + * \param[in] pixd in-place translation + * \param[in] hshift horizontal shift; hshift > 0 is to right + * \param[in] vshift vertical shift; vshift > 0 is down + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return 0 if OK; 1 on error + */ +l_ok +pixRasteropIP(PIX *pixd, + l_int32 hshift, + l_int32 vshift, + l_int32 incolor) +{ +l_int32 w, h; + + PROCNAME("pixRasteropIP"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + + pixGetDimensions(pixd, &w, &h, NULL); + pixRasteropHip(pixd, 0, h, hshift, incolor); + pixRasteropVip(pixd, 0, w, vshift, incolor); + + return 0; +} + + +/*--------------------------------------------------------------------* + * Full image rasterop with no shifts * + *--------------------------------------------------------------------*/ +/*! + * \brief pixRasteropFullImage() + * + * \param[in] pixd + * \param[in] pixs + * \param[in] op any of the op-codes + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      ~ this is a wrapper for a common 2-image raster operation
+ *      ~ both pixs and pixd must be defined
+ *      ~ the operation is performed with aligned UL corners of pixs and pixd
+ *      ~ the operation clips to the smallest pix; if the width or height
+ *        of pixd is larger than pixs, some pixels in pixd will be unchanged
+ * 
+ */ +l_ok +pixRasteropFullImage(PIX *pixd, + PIX *pixs, + l_int32 op) +{ + PROCNAME("pixRasteropFullImage"); + + if (!pixd) + return ERROR_INT("pixd not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), op, + pixs, 0, 0); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/roplow.c b/hgdriver/3rdparty/hgOCR/leptonica/roplow.c new file mode 100644 index 0000000..924fc5f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/roplow.c @@ -0,0 +1,2479 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file roplow.c + *
+ *      Low level dest-only
+ *           void            rasteropUniLow()
+ *           static void     rasteropUniWordAlignedlLow()
+ *           static void     rasteropUniGeneralLow()
+ *
+ *      Low level src and dest
+ *           void            rasteropLow()
+ *           static void     rasteropWordAlignedLow()
+ *           static void     rasteropVAlignedLow()
+ *           static void     rasteropGeneralLow()
+ *
+ *      Low level in-place full height vertical block transfer
+ *           void            rasteropVipLow()
+ *
+ *      Low level in-place full width horizontal block transfer
+ *           void            rasteropHipLow()
+ *           static void     shiftDataHorizontalLow()
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Static helpers */ +static void rasteropUniWordAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx, + l_int32 dy, l_int32 dw, l_int32 dh, + l_int32 op); +static void rasteropUniGeneralLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx, + l_int32 dy, l_int32 dw, l_int32 dh, + l_int32 op); +static void rasteropWordAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx, + l_int32 dy, l_int32 dw, l_int32 dh, + l_int32 op, l_uint32 *datas, l_int32 swpl, + l_int32 sx, l_int32 sy); +static void rasteropVAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx, + l_int32 dy, l_int32 dw, l_int32 dh, + l_int32 op, l_uint32 *datas, l_int32 swpl, + l_int32 sx, l_int32 sy); +static void rasteropGeneralLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx, + l_int32 dy, l_int32 dw, l_int32 dh, + l_int32 op, l_uint32 *datas, l_int32 swpl, + l_int32 sx, l_int32 sy); +static void shiftDataHorizontalLow(l_uint32 *datad, l_int32 wpld, + l_uint32 *datas, l_int32 wpls, + l_int32 shift); + +#define COMBINE_PARTIAL(d, s, m) ( ((d) & ~(m)) | ((s) & (m)) ) + +static const l_int32 SHIFT_LEFT = 0; +static const l_int32 SHIFT_RIGHT = 1; + +static const l_uint32 lmask32[] = {0x0, + 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, + 0xf8000000, 0xfc000000, 0xfe000000, 0xff000000, + 0xff800000, 0xffc00000, 0xffe00000, 0xfff00000, + 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, + 0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000, + 0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00, + 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, + 0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff}; + +static const l_uint32 rmask32[] = {0x0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, + 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, + 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, + 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, + 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; + + +/*--------------------------------------------------------------------* + * Low-level dest-only rasterops * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropUniLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dpixw width of dest + * \param[in] dpixh height of dest + * \param[in] depth depth of src and dest + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \return void + * + * Action: scales width, performs clipping, checks alignment, and + * dispatches for the rasterop. + */ +void +rasteropUniLow(l_uint32 *datad, + l_int32 dpixw, + l_int32 dpixh, + l_int32 depth, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op) +{ +l_int32 dhangw, dhangh; + + /* -------------------------------------------------------* + * scale horizontal dimensions by depth + * -------------------------------------------------------*/ + if (depth != 1) { + dpixw *= depth; + dx *= depth; + dw *= depth; + } + + /* -------------------------------------------------------* + * clip rectangle to dest image + * -------------------------------------------------------*/ + /* first, clip horizontally (dx, dw) */ + if (dx < 0) { + dw += dx; /* reduce dw */ + dx = 0; + } + dhangw = dx + dw - dpixw; /* rect ovhang dest to right */ + if (dhangw > 0) + dw -= dhangw; /* reduce dw */ + + /* then, clip vertically (dy, dh) */ + if (dy < 0) { + dh += dy; /* reduce dh */ + dy = 0; + } + dhangh = dy + dh - dpixh; /* rect ovhang dest below */ + if (dhangh > 0) + dh -= dhangh; /* reduce dh */ + + /* if clipped entirely, quit */ + if ((dw <= 0) || (dh <= 0)) + return; + + /* -------------------------------------------------------* + * dispatch to aligned or non-aligned blitters + * -------------------------------------------------------*/ + if ((dx & 31) == 0) + rasteropUniWordAlignedLow(datad, dwpl, dx, dy, dw, dh, op); + else + rasteropUniGeneralLow(datad, dwpl, dx, dy, dw, dh, op); +} + + + +/*--------------------------------------------------------------------* + * Static low-level uni rasterop with word alignment * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropUniWordAlignedLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \return void + * + * This is called when the dest rect is left aligned + * on 32-bit word boundaries. That is: dx & 31 == 0. + * + * We make an optimized implementation of this because + * it is a common case: e.g., operating on a full dest image. + */ +static void +rasteropUniWordAlignedLow(l_uint32 *datad, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op) +{ +l_int32 nfullw; /* number of full words */ +l_uint32 *pfword; /* ptr to first word */ +l_int32 lwbits; /* number of ovrhang bits in last partial word */ +l_uint32 lwmask; /* mask for last partial word */ +l_uint32 *lined; +l_int32 i, j; + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + nfullw = dw >> 5; + lwbits = dw & 31; + if (lwbits) + lwmask = lmask32[lwbits]; + pfword = datad + dwpl * dy + (dx >> 5); + + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + switch (op) + { + case PIX_CLR: + for (i = 0; i < dh; i++) { + lined = pfword + i * dwpl; + for (j = 0; j < nfullw; j++) + *lined++ = 0x0; + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, 0x0, lwmask); + } + break; + case PIX_SET: + for (i = 0; i < dh; i++) { + lined = pfword + i * dwpl; + for (j = 0; j < nfullw; j++) + *lined++ = 0xffffffff; + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, 0xffffffff, lwmask); + } + break; + case PIX_NOT(PIX_DST): + for (i = 0; i < dh; i++) { + lined = pfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = ~(*lined); + lined++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, ~(*lined), lwmask); + } + break; + default: + fprintf(stderr, "Operation %d not permitted here!\n", op); + } +} + + +/*--------------------------------------------------------------------* + * Static low-level uni rasterop without word alignment * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropUniGeneralLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \return void + */ +static void +rasteropUniGeneralLow(l_uint32 *datad, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op) +{ +l_int32 dfwpartb; /* boolean (1, 0) if first dest word is partial */ +l_int32 dfwpart2b; /* boolean (1, 0) if first dest word is doubly partial */ +l_uint32 dfwmask; /* mask for first partial dest word */ +l_int32 dfwbits; /* first word dest bits in ovrhang */ +l_uint32 *pdfwpart; /* ptr to first partial dest word */ +l_int32 dfwfullb; /* boolean (1, 0) if there exists a full dest word */ +l_int32 dnfullw; /* number of full words in dest */ +l_uint32 *pdfwfull; /* ptr to first full dest word */ +l_int32 dlwpartb; /* boolean (1, 0) if last dest word is partial */ +l_uint32 dlwmask; /* mask for last partial dest word */ +l_int32 dlwbits; /* last word dest bits in ovrhang */ +l_uint32 *pdlwpart; /* ptr to last partial dest word */ +l_int32 i, j; + + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + /* is the first word partial? */ + dfwmask = 0; + if ((dx & 31) == 0) { /* if not */ + dfwpartb = 0; + dfwbits = 0; + } else { /* if so */ + dfwpartb = 1; + dfwbits = 32 - (dx & 31); + dfwmask = rmask32[dfwbits]; + pdfwpart = datad + dwpl * dy + (dx >> 5); + } + + /* is the first word doubly partial? */ + if (dw >= dfwbits) { /* if not */ + dfwpart2b = 0; + } else { /* if so */ + dfwpart2b = 1; + dfwmask &= lmask32[32 - dfwbits + dw]; + } + + /* is there a full dest word? */ + if (dfwpart2b == 1) { /* not */ + dfwfullb = 0; + dnfullw = 0; + } else { + dnfullw = (dw - dfwbits) >> 5; + if (dnfullw == 0) { /* if not */ + dfwfullb = 0; + } else { /* if so */ + dfwfullb = 1; + if (dfwpartb) + pdfwfull = pdfwpart + 1; + else + pdfwfull = datad + dwpl * dy + (dx >> 5); + } + } + + /* is the last word partial? */ + dlwbits = (dx + dw) & 31; + if (dfwpart2b == 1 || dlwbits == 0) { /* if not */ + dlwpartb = 0; + } else { + dlwpartb = 1; + dlwmask = lmask32[dlwbits]; + if (dfwpartb) + pdlwpart = pdfwpart + 1 + dnfullw; + else + pdlwpart = datad + dwpl * dy + (dx >> 5) + dnfullw; + } + + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + switch (op) + { + case PIX_CLR: + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0x0, dfwmask); + pdfwpart += dwpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = 0x0; + pdfwfull += dwpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0x0, dlwmask); + pdlwpart += dwpl; + } + } + break; + case PIX_SET: + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0xffffffff, dfwmask); + pdfwpart += dwpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = 0xffffffff; + pdfwfull += dwpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0xffffffff, dlwmask); + pdlwpart += dwpl; + } + } + break; + case PIX_NOT(PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~(*pdfwpart), dfwmask); + pdfwpart += dwpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = ~(*(pdfwfull + j)); + pdfwfull += dwpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~(*pdlwpart), dlwmask); + pdlwpart += dwpl; + } + } + break; + default: + fprintf(stderr, "Operation %d not permitted here!\n", op); + } +} + + +/*--------------------------------------------------------------------* + * Low-level src and dest rasterops * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dpixw width of dest + * \param[in] dpixh height of dest + * \param[in] depth depth of src and dest + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \param[in] datas ptr to src image data + * \param[in] spixw width of src + * \param[in] spixh height of src + * \param[in] swpl wpl of src + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return void + * + * Action: Scales width, performs clipping, checks alignment, and + * dispatches for the rasterop. + * + * Warning: the two images must have equal depth. This is not checked. + */ +void +rasteropLow(l_uint32 *datad, + l_int32 dpixw, + l_int32 dpixh, + l_int32 depth, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op, + l_uint32 *datas, + l_int32 spixw, + l_int32 spixh, + l_int32 swpl, + l_int32 sx, + l_int32 sy) +{ +l_int32 dhangw, shangw, dhangh, shangh; + + /* -------------------------------------------------------* + * scale horizontal dimensions by depth + * -------------------------------------------------------*/ + if (depth != 1) { + dpixw *= depth; + dx *= depth; + dw *= depth; + spixw *= depth; + sx *= depth; + } + + + /* -------------------------------------------------------* + * clip to max rectangle within both src and dest + * -------------------------------------------------------*/ + /* first, clip horizontally (sx, dx, dw) */ + if (dx < 0) { + sx -= dx; /* increase sx */ + dw += dx; /* reduce dw */ + dx = 0; + } + if (sx < 0) { + dx -= sx; /* increase dx */ + dw += sx; /* reduce dw */ + sx = 0; + } + dhangw = dx + dw - dpixw; /* rect ovhang dest to right */ + if (dhangw > 0) + dw -= dhangw; /* reduce dw */ + shangw = sx + dw - spixw; /* rect ovhang src to right */ + if (shangw > 0) + dw -= shangw; /* reduce dw */ + + /* then, clip vertically (sy, dy, dh) */ + if (dy < 0) { + sy -= dy; /* increase sy */ + dh += dy; /* reduce dh */ + dy = 0; + } + if (sy < 0) { + dy -= sy; /* increase dy */ + dh += sy; /* reduce dh */ + sy = 0; + } + dhangh = dy + dh - dpixh; /* rect ovhang dest below */ + if (dhangh > 0) + dh -= dhangh; /* reduce dh */ + shangh = sy + dh - spixh; /* rect ovhang src below */ + if (shangh > 0) + dh -= shangh; /* reduce dh */ + + /* if clipped entirely, quit */ + if ((dw <= 0) || (dh <= 0)) + return; + + /* -------------------------------------------------------* + * dispatch to aligned or non-aligned blitters + * -------------------------------------------------------*/ + if (((dx & 31) == 0) && ((sx & 31) == 0)) + rasteropWordAlignedLow(datad, dwpl, dx, dy, dw, dh, op, + datas, swpl, sx, sy); + else if ((dx & 31) == (sx & 31)) + rasteropVAlignedLow(datad, dwpl, dx, dy, dw, dh, op, + datas, swpl, sx, sy); + else + rasteropGeneralLow(datad, dwpl, dx, dy, dw, dh, op, + datas, swpl, sx, sy); +} + + +/*--------------------------------------------------------------------* + * Static low-level rasterop with vertical word alignment * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropWordAlignedLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \param[in] datas ptr to src image data + * \param[in] swpl wpl of src + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return void + * + * This is called when both the src and dest rects + * are left aligned on 32-bit word boundaries. + * That is: dx & 31 == 0 and sx & 31 == 0 + * + * We make an optimized implementation of this because + * it is a common case: e.g., two images are rasterop'd + * starting from their UL corners 0,0. + */ +static void +rasteropWordAlignedLow(l_uint32 *datad, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op, + l_uint32 *datas, + l_int32 swpl, + l_int32 sx, + l_int32 sy) +{ +l_int32 nfullw; /* number of full words */ +l_uint32 *psfword; /* ptr to first src word */ +l_uint32 *pdfword; /* ptr to first dest word */ +l_int32 lwbits; /* number of ovrhang bits in last partial word */ +l_uint32 lwmask; /* mask for last partial word */ +l_uint32 *lines, *lined; +l_int32 i, j; + + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + nfullw = dw >> 5; + lwbits = dw & 31; + if (lwbits) + lwmask = lmask32[lwbits]; + psfword = datas + swpl * sy + (sx >> 5); + pdfword = datad + dwpl * dy + (dx >> 5); + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + switch (op) + { + case PIX_SRC: + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = *lines; + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, *lines, lwmask); + } + break; + case PIX_NOT(PIX_SRC): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = ~(*lines); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, ~(*lines), lwmask); + } + break; + case (PIX_SRC | PIX_DST): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (*lines | *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (*lines | *lined), lwmask); + } + break; + case (PIX_SRC & PIX_DST): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (*lines & *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (*lines & *lined), lwmask); + } + break; + case (PIX_SRC ^ PIX_DST): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (*lines ^ *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (*lines ^ *lined), lwmask); + } + break; + case (PIX_NOT(PIX_SRC) | PIX_DST): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (~(*lines) | *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (~(*lines) | *lined), lwmask); + } + break; + case (PIX_NOT(PIX_SRC) & PIX_DST): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (~(*lines) & *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (~(*lines) & *lined), lwmask); + } + break; + case (PIX_SRC | PIX_NOT(PIX_DST)): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (*lines | ~(*lined)); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (*lines | ~(*lined)), lwmask); + } + break; + case (PIX_SRC & PIX_NOT(PIX_DST)): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = (*lines & ~(*lined)); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, (*lines & ~(*lined)), lwmask); + } + break; + case (PIX_NOT(PIX_SRC | PIX_DST)): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = ~(*lines | *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, ~(*lines | *lined), lwmask); + } + break; + case (PIX_NOT(PIX_SRC & PIX_DST)): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = ~(*lines & *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, ~(*lines & *lined), lwmask); + } + break; + /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d */ + case (PIX_NOT(PIX_SRC ^ PIX_DST)): + for (i = 0; i < dh; i++) { + lines = psfword + i * swpl; + lined = pdfword + i * dwpl; + for (j = 0; j < nfullw; j++) { + *lined = ~(*lines ^ *lined); + lined++; + lines++; + } + if (lwbits) + *lined = COMBINE_PARTIAL(*lined, ~(*lines ^ *lined), lwmask); + } + break; + default: + fprintf(stderr, "Operation %d invalid\n", op); + } +} + + + +/*--------------------------------------------------------------------* + * Static low-level rasterop with vertical word alignment * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropVAlignedLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \param[in] datas ptr to src image data + * \param[in] swpl wpl of src + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return void + * + * This is called when the left side of the src and dest + * rects have the same alignment relative to 32-bit word + * boundaries; i.e., dx & 31) == (sx & 31 + */ +static void +rasteropVAlignedLow(l_uint32 *datad, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op, + l_uint32 *datas, + l_int32 swpl, + l_int32 sx, + l_int32 sy) +{ +l_int32 dfwpartb; /* boolean (1, 0) if first dest word is partial */ +l_int32 dfwpart2b; /* boolean (1, 0) if first dest word is doubly partial */ +l_uint32 dfwmask; /* mask for first partial dest word */ +l_int32 dfwbits; /* first word dest bits in ovrhang */ +l_uint32 *pdfwpart; /* ptr to first partial dest word */ +l_uint32 *psfwpart; /* ptr to first partial src word */ +l_int32 dfwfullb; /* boolean (1, 0) if there exists a full dest word */ +l_int32 dnfullw; /* number of full words in dest */ +l_uint32 *pdfwfull; /* ptr to first full dest word */ +l_uint32 *psfwfull; /* ptr to first full src word */ +l_int32 dlwpartb; /* boolean (1, 0) if last dest word is partial */ +l_uint32 dlwmask; /* mask for last partial dest word */ +l_int32 dlwbits; /* last word dest bits in ovrhang */ +l_uint32 *pdlwpart; /* ptr to last partial dest word */ +l_uint32 *pslwpart; /* ptr to last partial src word */ +l_int32 i, j; + + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + /* is the first word partial? */ + dfwmask = 0; + if ((dx & 31) == 0) { /* if not */ + dfwpartb = 0; + dfwbits = 0; + } else { /* if so */ + dfwpartb = 1; + dfwbits = 32 - (dx & 31); + dfwmask = rmask32[dfwbits]; + pdfwpart = datad + dwpl * dy + (dx >> 5); + psfwpart = datas + swpl * sy + (sx >> 5); + } + + /* is the first word doubly partial? */ + if (dw >= dfwbits) { /* if not */ + dfwpart2b = 0; + } else { /* if so */ + dfwpart2b = 1; + dfwmask &= lmask32[32 - dfwbits + dw]; + } + + /* is there a full dest word? */ + if (dfwpart2b == 1) { /* not */ + dfwfullb = 0; + dnfullw = 0; + } else { + dnfullw = (dw - dfwbits) >> 5; + if (dnfullw == 0) { /* if not */ + dfwfullb = 0; + } else { /* if so */ + dfwfullb = 1; + if (dfwpartb) { + pdfwfull = pdfwpart + 1; + psfwfull = psfwpart + 1; + } else { + pdfwfull = datad + dwpl * dy + (dx >> 5); + psfwfull = datas + swpl * sy + (sx >> 5); + } + } + } + + /* is the last word partial? */ + dlwbits = (dx + dw) & 31; + if (dfwpart2b == 1 || dlwbits == 0) { /* if not */ + dlwpartb = 0; + } else { + dlwpartb = 1; + dlwmask = lmask32[dlwbits]; + if (dfwpartb) { + pdlwpart = pdfwpart + 1 + dnfullw; + pslwpart = psfwpart + 1 + dnfullw; + } else { + pdlwpart = datad + dwpl * dy + (dx >> 5) + dnfullw; + pslwpart = datas + swpl * sy + (sx >> 5) + dnfullw; + } + } + + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + switch (op) + { + case PIX_SRC: + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, *psfwpart, dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = *(psfwfull + j); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, *pslwpart, dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case PIX_NOT(PIX_SRC): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~(*psfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = ~(*(psfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~(*pslwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC | PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (*psfwpart | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) |= *(psfwfull + j); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (*pslwpart | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC & PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (*psfwpart & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) &= *(psfwfull + j); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (*pslwpart & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC ^ PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (*psfwpart ^ *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) ^= *(psfwfull + j); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (*pslwpart ^ *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC) | PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (~(*psfwpart) | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) |= ~(*(psfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (~(*pslwpart) | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC) & PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (~(*psfwpart) & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) &= ~(*(psfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (~(*pslwpart) & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC | PIX_NOT(PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (*psfwpart | ~(*pdfwpart)), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = *(psfwfull + j) | ~(*(pdfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (*pslwpart | ~(*pdlwpart)), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC & PIX_NOT(PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (*psfwpart & ~(*pdfwpart)), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = *(psfwfull + j) & ~(*(pdfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (*pslwpart & ~(*pdlwpart)), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC | PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(*psfwpart | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = ~(*(psfwfull + j) | *(pdfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(*pslwpart | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC & PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(*psfwpart & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = ~(*(psfwfull + j) & *(pdfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(*pslwpart & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d */ + case (PIX_NOT(PIX_SRC ^ PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(*psfwpart ^ *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) + *(pdfwfull + j) = ~(*(psfwfull + j) ^ *(pdfwfull + j)); + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(*pslwpart ^ *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + default: + fprintf(stderr, "Operation %x invalid\n", op); + } +} + + +/*--------------------------------------------------------------------* + * Static low-level rasterop without vertical word alignment * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropGeneralLow() + * + * \param[in] datad ptr to dest image data + * \param[in] dwpl wpl of dest + * \param[in] dx x val of UL corner of dest rectangle + * \param[in] dy y val of UL corner of dest rectangle + * \param[in] dw width of dest rectangle + * \param[in] dh height of dest rectangle + * \param[in] op op code + * \param[in] datas ptr to src image data + * \param[in] swpl wpl of src + * \param[in] sx x val of UL corner of src rectangle + * \param[in] sy y val of UL corner of src rectangle + * \return void + * + * This is called when the src and dest rects are + * do not have the same 32-bit word alignment. + * + * The method is a generalization of rasteropVAlignLow. + * There, the src image pieces were directly merged + * with the dest. Here, we shift the source bits + * to fill words that are aligned with the dest, and + * then use those "source words" exactly in place + * of the source words that were used in rasteropVAlignLow. + * + * The critical parameter is thus the shift required + * for the src. Consider the left edge of the rectangle. + * The overhang into the src and dest words are found, + * and the difference is exactly this shift. There are + * two separate cases, depending on whether the src pixels + * are shifted left or right. If the src overhang is + * larger than the dest overhang, the src is shifted to + * the right, a number of pixels equal to the shift are + * left over for filling the next dest word, if necessary. + * But if the dest overhang is larger than the src, + * the src is shifted to the left, and it may also be + * necessary to shift an equal number of pixels in from + * the next src word. However, in both cases, after + * the first partial or complete dest word has been + * filled, the next src pixels will come from a left + * shift that exhausts the pixels in the src word. + */ +static void +rasteropGeneralLow(l_uint32 *datad, + l_int32 dwpl, + l_int32 dx, + l_int32 dy, + l_int32 dw, + l_int32 dh, + l_int32 op, + l_uint32 *datas, + l_int32 swpl, + l_int32 sx, + l_int32 sy) +{ +l_int32 dfwpartb; /* boolean (1, 0) if first dest word is partial */ +l_int32 dfwpart2b; /* boolean (1, 0) if 1st dest word is doubly partial */ +l_uint32 dfwmask; /* mask for first partial dest word */ +l_int32 dfwbits; /* first word dest bits in overhang; 0-31 */ +l_int32 dhang; /* dest overhang in first partial word, */ + /* or 0 if dest is word aligned (same as dfwbits) */ +l_uint32 *pdfwpart; /* ptr to first partial dest word */ +l_uint32 *psfwpart; /* ptr to first partial src word */ +l_int32 dfwfullb; /* boolean (1, 0) if there exists a full dest word */ +l_int32 dnfullw; /* number of full words in dest */ +l_uint32 *pdfwfull; /* ptr to first full dest word */ +l_uint32 *psfwfull; /* ptr to first full src word */ +l_int32 dlwpartb; /* boolean (1, 0) if last dest word is partial */ +l_uint32 dlwmask; /* mask for last partial dest word */ +l_int32 dlwbits; /* last word dest bits in ovrhang */ +l_uint32 *pdlwpart; /* ptr to last partial dest word */ +l_uint32 *pslwpart; /* ptr to last partial src word */ +l_uint32 sword; /* compose src word aligned with the dest words */ +l_int32 sfwbits; /* first word src bits in overhang (1-32), */ + /* or 32 if src is word aligned */ +l_int32 shang; /* source overhang in the first partial word, */ + /* or 0 if src is word aligned (not same as sfwbits) */ +l_int32 sleftshift; /* bits to shift left for source word to align */ + /* with the dest. Also the number of bits that */ + /* get shifted to the right to align with the dest. */ +l_int32 srightshift; /* bits to shift right for source word to align */ + /* with dest. Also, the number of bits that get */ + /* shifted left to align with the dest. */ +l_int32 srightmask; /* mask for selecting sleftshift bits that have */ + /* been shifted right by srightshift bits */ +l_int32 sfwshiftdir; /* either SHIFT_LEFT or SHIFT_RIGHT */ +l_int32 sfwaddb; /* boolean: do we need an additional sfw right shift? */ +l_int32 slwaddb; /* boolean: do we need an additional slw right shift? */ +l_int32 i, j; + + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + /* To get alignment of src with dst (e.g., in the + * full words) the src must do a left shift of its + * relative overhang in the current src word, + * and OR that with a right shift of + * (31 - relative overhang) from the next src word. + * We find the absolute overhangs, the relative overhangs, + * the required shifts and the src mask */ + if ((sx & 31) == 0) + shang = 0; + else + shang = 32 - (sx & 31); + if ((dx & 31) == 0) + dhang = 0; + else + dhang = 32 - (dx & 31); + + if (shang == 0 && dhang == 0) { /* this should be treated by an + aligned operation, not by + this general rasterop! */ + sleftshift = 0; + srightshift = 0; + srightmask = rmask32[0]; + } else { + if (dhang > shang) + sleftshift = dhang - shang; + else + sleftshift = 32 - (shang - dhang); + srightshift = 32 - sleftshift; + srightmask = rmask32[sleftshift]; + } + + /* is the first dest word partial? */ + dfwmask = 0; + if ((dx & 31) == 0) { /* if not */ + dfwpartb = 0; + dfwbits = 0; + } else { /* if so */ + dfwpartb = 1; + dfwbits = 32 - (dx & 31); + dfwmask = rmask32[dfwbits]; + pdfwpart = datad + dwpl * dy + (dx >> 5); + psfwpart = datas + swpl * sy + (sx >> 5); + sfwbits = 32 - (sx & 31); + if (dfwbits > sfwbits) { + sfwshiftdir = SHIFT_LEFT; /* and shift by sleftshift */ + if (dw < shang) + sfwaddb = 0; + else + sfwaddb = 1; /* and rshift in next src word by srightshift */ + } else { + sfwshiftdir = SHIFT_RIGHT; /* and shift by srightshift */ + } + } + + /* is the first dest word doubly partial? */ + if (dw >= dfwbits) { /* if not */ + dfwpart2b = 0; + } else { /* if so */ + dfwpart2b = 1; + dfwmask &= lmask32[32 - dfwbits + dw]; + } + + /* is there a full dest word? */ + if (dfwpart2b == 1) { /* not */ + dfwfullb = 0; + dnfullw = 0; + } else { + dnfullw = (dw - dfwbits) >> 5; + if (dnfullw == 0) { /* if not */ + dfwfullb = 0; + } else { /* if so */ + dfwfullb = 1; + pdfwfull = datad + dwpl * dy + ((dx + dhang) >> 5); + psfwfull = datas + swpl * sy + ((sx + dhang) >> 5); /* yes, dhang */ + } + } + + /* is the last dest word partial? */ + dlwbits = (dx + dw) & 31; + if (dfwpart2b == 1 || dlwbits == 0) { /* if not */ + dlwpartb = 0; + } else { + dlwpartb = 1; + dlwmask = lmask32[dlwbits]; + pdlwpart = datad + dwpl * dy + ((dx + dhang) >> 5) + dnfullw; + pslwpart = datas + swpl * sy + ((sx + dhang) >> 5) + dnfullw; + if (dlwbits <= srightshift) /* must be <= here !!! */ + slwaddb = 0; /* we got enough bits from current src word */ + else + slwaddb = 1; /* must rshift in next src word by srightshift */ + } + + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + switch (op) + { + case PIX_SRC: + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, sword, dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, sword, dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case PIX_NOT(PIX_SRC): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~sword, dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = ~sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~sword, dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC | PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (sword | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) |= sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (sword | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC & PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (sword & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) &= sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (sword & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC ^ PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (sword ^ *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) ^= sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (sword ^ *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC) | PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (~sword | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) |= ~sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (~sword | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC) & PIX_DST): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (~sword & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) &= ~sword; + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (~sword & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC | PIX_NOT(PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (sword | ~(*pdfwpart)), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = sword | ~(*(pdfwfull + j)); + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (sword | ~(*pdlwpart)), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_SRC & PIX_NOT(PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + (sword & ~(*pdfwpart)), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = sword & ~(*(pdfwfull + j)); + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + (sword & ~(*pdlwpart)), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC | PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(sword | *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = ~(sword | *(pdfwfull + j)); + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(sword | *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + case (PIX_NOT(PIX_SRC & PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(sword & *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = ~(sword & *(pdfwfull + j)); + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(sword & *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d */ + case (PIX_NOT(PIX_SRC ^ PIX_DST)): + /* do the first partial word */ + if (dfwpartb) { + for (i = 0; i < dh; i++) + { + if (sfwshiftdir == SHIFT_LEFT) { + sword = *psfwpart << sleftshift; + if (sfwaddb) + sword = COMBINE_PARTIAL(sword, + *(psfwpart + 1) >> srightshift, + srightmask); + } else { /* shift right */ + sword = *psfwpart >> srightshift; + } + + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, + ~(sword ^ *pdfwpart), dfwmask); + pdfwpart += dwpl; + psfwpart += swpl; + } + } + + /* do the full words */ + if (dfwfullb) { + for (i = 0; i < dh; i++) { + for (j = 0; j < dnfullw; j++) { + sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift, + *(psfwfull + j + 1) >> srightshift, + srightmask); + *(pdfwfull + j) = ~(sword ^ *(pdfwfull + j)); + } + pdfwfull += dwpl; + psfwfull += swpl; + } + } + + /* do the last partial word */ + if (dlwpartb) { + for (i = 0; i < dh; i++) { + sword = *pslwpart << sleftshift; + if (slwaddb) + sword = COMBINE_PARTIAL(sword, + *(pslwpart + 1) >> srightshift, + srightmask); + + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, + ~(sword ^ *pdlwpart), dlwmask); + pdlwpart += dwpl; + pslwpart += swpl; + } + } + break; + default: + fprintf(stderr, "Operation %x invalid\n", op); + } +} + + +/*--------------------------------------------------------------------* + * Low level in-place full height vertical block transfer * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropVipLow() + * + * \param[in] data ptr to image data + * \param[in] pixw width + * \param[in] pixh height + * \param[in] depth depth + * \param[in] wpl wpl + * \param[in] x x val of UL corner of rectangle + * \param[in] w width of rectangle + * \param[in] shift + shifts data downward in vertical column + * \return 0 if OK; 1 on error. + * + *
+ * Notes:
+ *      (1) This clears the pixels that are left exposed after the
+ *          translation.  You can consider them as pixels that are
+ *          shifted in from outside the image.  This can be later
+ *          overridden by the incolor parameter in higher-level functions
+ *          that call this.  For example, for images with depth > 1,
+ *          these pixels are cleared to black; to be white they
+ *          must later be SET to white.  See, e.g., pixRasteropVip().
+ *      (2) This function scales the width to accommodate any depth,
+ *          performs clipping, and then does the in-place rasterop.
+ * 
+ */ +void +rasteropVipLow(l_uint32 *data, + l_int32 pixw, + l_int32 pixh, + l_int32 depth, + l_int32 wpl, + l_int32 x, + l_int32 w, + l_int32 shift) +{ +l_int32 fwpartb; /* boolean (1, 0) if first word is partial */ +l_int32 fwpart2b; /* boolean (1, 0) if first word is doubly partial */ +l_uint32 fwmask; /* mask for first partial word */ +l_int32 fwbits; /* first word bits in ovrhang */ +l_uint32 *pdfwpart; /* ptr to first partial dest word */ +l_uint32 *psfwpart; /* ptr to first partial src word */ +l_int32 fwfullb; /* boolean (1, 0) if there exists a full word */ +l_int32 nfullw; /* number of full words */ +l_uint32 *pdfwfull; /* ptr to first full dest word */ +l_uint32 *psfwfull; /* ptr to first full src word */ +l_int32 lwpartb; /* boolean (1, 0) if last word is partial */ +l_uint32 lwmask; /* mask for last partial word */ +l_int32 lwbits; /* last word bits in ovrhang */ +l_uint32 *pdlwpart; /* ptr to last partial dest word */ +l_uint32 *pslwpart; /* ptr to last partial src word */ +l_int32 dirwpl; /* directed wpl (-wpl * sign(shift)) */ +l_int32 absshift; /* absolute value of shift; for use in iterator */ +l_int32 vlimit; /* vertical limit value for iterations */ +l_int32 i, j; + + + /*--------------------------------------------------------* + * Scale horizontal dimensions by depth * + *--------------------------------------------------------*/ + if (depth != 1) { + pixw *= depth; + x *= depth; + w *= depth; + } + + + /*--------------------------------------------------------* + * Clip horizontally * + *--------------------------------------------------------*/ + if (x < 0) { + w += x; /* reduce w */ + x = 0; /* clip to x = 0 */ + } + if (x >= pixw || w <= 0) /* no part of vertical slice is in the image */ + return; + + if (x + w > pixw) + w = pixw - x; /* clip to x + w = pixw */ + + /*--------------------------------------------------------* + * Preliminary calculations * + *--------------------------------------------------------*/ + /* is the first word partial? */ + if ((x & 31) == 0) { /* if not */ + fwpartb = 0; + fwbits = 0; + } else { /* if so */ + fwpartb = 1; + fwbits = 32 - (x & 31); + fwmask = rmask32[fwbits]; + if (shift >= 0) { /* go up from bottom */ + pdfwpart = data + wpl * (pixh - 1) + (x >> 5); + psfwpart = data + wpl * (pixh - 1 - shift) + (x >> 5); + } else { /* go down from top */ + pdfwpart = data + (x >> 5); + psfwpart = data - wpl * shift + (x >> 5); + } + } + + /* is the first word doubly partial? */ + if (w >= fwbits) { /* if not */ + fwpart2b = 0; + } else { /* if so */ + fwpart2b = 1; + fwmask &= lmask32[32 - fwbits + w]; + } + + /* is there a full dest word? */ + if (fwpart2b == 1) { /* not */ + fwfullb = 0; + nfullw = 0; + } else { + nfullw = (w - fwbits) >> 5; + if (nfullw == 0) { /* if not */ + fwfullb = 0; + } else { /* if so */ + fwfullb = 1; + if (fwpartb) { + pdfwfull = pdfwpart + 1; + psfwfull = psfwpart + 1; + } else if (shift >= 0) { /* go up from bottom */ + pdfwfull = data + wpl * (pixh - 1) + (x >> 5); + psfwfull = data + wpl * (pixh - 1 - shift) + (x >> 5); + } else { /* go down from top */ + pdfwfull = data + (x >> 5); + psfwfull = data - wpl * shift + (x >> 5); + } + } + } + + /* is the last word partial? */ + lwbits = (x + w) & 31; + if (fwpart2b == 1 || lwbits == 0) { /* if not */ + lwpartb = 0; + } else { + lwpartb = 1; + lwmask = lmask32[lwbits]; + if (fwpartb) { + pdlwpart = pdfwpart + 1 + nfullw; + pslwpart = psfwpart + 1 + nfullw; + } else if (shift >= 0) { /* go up from bottom */ + pdlwpart = data + wpl * (pixh - 1) + (x >> 5) + nfullw; + pslwpart = data + wpl * (pixh - 1 - shift) + (x >> 5) + nfullw; + } else { /* go down from top */ + pdlwpart = data + (x >> 5) + nfullw; + pslwpart = data - wpl * shift + (x >> 5) + nfullw; + } + } + + /* determine the direction of flow from the shift + * If the shift >= 0, data flows downard from src + * to dest, starting at the bottom and working up. + * If shift < 0, data flows upward from src to + * dest, starting at the top and working down. */ + dirwpl = (shift >= 0) ? -wpl : wpl; + absshift = L_ABS(shift); + vlimit = L_MAX(0, pixh - absshift); + + + /*--------------------------------------------------------* + * Now we're ready to do the ops * + *--------------------------------------------------------*/ + + /* Do the first partial word */ + if (fwpartb) { + for (i = 0; i < vlimit; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, *psfwpart, fwmask); + pdfwpart += dirwpl; + psfwpart += dirwpl; + } + + /* Clear the incoming pixels */ + for (i = vlimit; i < pixh; i++) { + *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0x0, fwmask); + pdfwpart += dirwpl; + } + } + + /* Do the full words */ + if (fwfullb) { + for (i = 0; i < vlimit; i++) { + for (j = 0; j < nfullw; j++) + *(pdfwfull + j) = *(psfwfull + j); + pdfwfull += dirwpl; + psfwfull += dirwpl; + } + + /* Clear the incoming pixels */ + for (i = vlimit; i < pixh; i++) { + for (j = 0; j < nfullw; j++) + *(pdfwfull + j) = 0x0; + pdfwfull += dirwpl; + } + } + + /* Do the last partial word */ + if (lwpartb) { + for (i = 0; i < vlimit; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, *pslwpart, lwmask); + pdlwpart += dirwpl; + pslwpart += dirwpl; + } + + /* Clear the incoming pixels */ + for (i = vlimit; i < pixh; i++) { + *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0x0, lwmask); + pdlwpart += dirwpl; + } + } +} + + + +/*--------------------------------------------------------------------* + * Low level in-place full width horizontal block transfer * + *--------------------------------------------------------------------*/ +/*! + * \brief rasteropHipLow() + * + * \param[in] data ptr to image data + * \param[in] pixh height + * \param[in] depth depth + * \param[in] wpl wpl + * \param[in] y y val of UL corner of rectangle + * \param[in] h height of rectangle + * \param[in] shift + shifts data to the left in a horizontal column + * \return 0 if OK; 1 on error. + * + *
+ * Notes:
+ *      (1) This clears the pixels that are left exposed after the rasterop.
+ *          Therefore, for Pix with depth > 1, these pixels become black,
+ *          and must be subsequently SET if they are to be white.
+ *          For example, see pixRasteropHip().
+ *      (2) This function performs clipping and calls shiftDataHorizontalLow()
+ *          to do the in-place rasterop on each line.
+ * 
+ */ +void +rasteropHipLow(l_uint32 *data, + l_int32 pixh, + l_int32 depth, + l_int32 wpl, + l_int32 y, + l_int32 h, + l_int32 shift) +{ +l_int32 i; +l_uint32 *line; + + /* clip band if necessary */ + if (y < 0) { + h += y; /* reduce h */ + y = 0; /* clip to y = 0 */ + } + if (h <= 0 || y > pixh) /* no part of horizontal slice is in the image */ + return; + + if (y + h > pixh) + h = pixh - y; /* clip to y + h = pixh */ + + for (i = y; i < y + h; i++) { + line = data + i * wpl; + shiftDataHorizontalLow(line, wpl, line, wpl, shift * depth); + } +} + + +/*! + * \brief shiftDataHorizontalLow() + * + * \param[in] datad ptr to beginning of dest line + * \param[in] wpld wpl of dest + * \param[in] datas ptr to beginning of src line + * \param[in] wpls wpl of src + * \param[in] shift horizontal shift of block; >0 is to right + * \return void + * + *
+ * Notes:
+ *      (1) This can also be used for in-place operation; see, e.g.,
+ *          rasteropHipLow().
+ *      (2) We are clearing the pixels that are shifted in from
+ *          outside the image.  This can be overridden by the
+ *          incolor parameter in higher-level functions that call this.
+ * 
+ */ +static void +shiftDataHorizontalLow(l_uint32 *datad, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 shift) +{ +l_int32 j, firstdw, wpl, rshift, lshift; +l_uint32 *lined, *lines; + + lined = datad; + lines = datas; + + if (shift >= 0) { /* src shift to right; data flows to + * right, starting at right edge and + * progressing leftward. */ + firstdw = shift / 32; + wpl = L_MIN(wpls, wpld - firstdw); + lined += firstdw + wpl - 1; + lines += wpl - 1; + rshift = shift & 31; + if (rshift == 0) { + for (j = 0; j < wpl; j++) + *lined-- = *lines--; + + /* clear out the rest to the left edge */ + for (j = 0; j < firstdw; j++) + *lined-- = 0; + } else { + lshift = 32 - rshift; + for (j = 1; j < wpl; j++) { + *lined-- = *(lines - 1) << lshift | *lines >> rshift; + lines--; + } + *lined = *lines >> rshift; /* partial first */ + + /* clear out the rest to the left edge */ + *lined &= ~lmask32[rshift]; + lined--; + for (j = 0; j < firstdw; j++) + *lined-- = 0; + } + } else { /* src shift to left; data flows to left, starting + * at left edge and progressing rightward. */ + firstdw = (-shift) / 32; + wpl = L_MIN(wpls - firstdw, wpld); + lines += firstdw; + lshift = (-shift) & 31; + if (lshift == 0) { + for (j = 0; j < wpl; j++) + *lined++ = *lines++; + + /* clear out the rest to the right edge */ + for (j = 0; j < firstdw; j++) + *lined++ = 0; + } else { + rshift = 32 - lshift; + for (j = 1; j < wpl; j++) { + *lined++ = *lines << lshift | *(lines + 1) >> rshift; + lines++; + } + *lined = *lines << lshift; /* partial last */ + + /* clear out the rest to the right edge */ + /* first clear the lshift pixels of this partial word */ + *lined &= ~rmask32[lshift]; + lined++; + /* then the remaining words to the right edge */ + for (j = 0; j < firstdw; j++) + *lined++ = 0; + } + } +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rotate.c b/hgdriver/3rdparty/hgOCR/leptonica/rotate.c new file mode 100644 index 0000000..c9aed99 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rotate.c @@ -0,0 +1,596 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file rotate.c + *
+ *
+ *     General rotation about image center
+ *              PIX     *pixRotate()
+ *              PIX     *pixEmbedForRotation()
+ *
+ *     General rotation by sampling
+ *              PIX     *pixRotateBySampling()
+ *
+ *     Nice (slow) rotation of 1 bpp image
+ *              PIX     *pixRotateBinaryNice()
+ *
+ *     Rotation including alpha (blend) component
+ *              PIX     *pixRotateWithAlpha()
+ *
+ *     Rotations are measured in radians; clockwise is positive.
+ *
+ *     The general rotation pixRotate() does the best job for
+ *     rotating about the image center.  For 1 bpp, it uses shear;
+ *     for others, it uses either shear or area mapping.
+ *     If requested, it expands the output image so that no pixels are lost
+ *     in the rotation, and this can be done on multiple successive shears
+ *     without expanding beyond the maximum necessary size.
+ * 
+ */ + +#include +#include "allheaders.h" + +extern l_float32 AlphaMaskBorderVals[2]; +static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */ +static const l_float32 Max1BppShearAngle = 0.06; /* radians; ~3 deg */ +static const l_float32 LimitShearAngle = 0.35; /* radians; ~20 deg */ + + +/*------------------------------------------------------------------* + * General rotation about the center * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotate() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp rgb + * \param[in] angle radians; clockwise is positive + * \param[in] type L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \param[in] width original width; use 0 to avoid embedding + * \param[in] height original height; use 0 to avoid embedding + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a high-level, simple interface for rotating images
+ *          about their center.
+ *      (2) For very small rotations, just return a clone.
+ *      (3) Rotation brings either white or black pixels in
+ *          from outside the image.
+ *      (4) The rotation type is adjusted if necessary for the image
+ *          depth and size of rotation angle.  For 1 bpp images, we
+ *          rotate either by shear or sampling.
+ *      (5) Colormaps are removed for rotation by area mapping.
+ *      (6) The dest can be expanded so that no image pixels
+ *          are lost.  To invoke expansion, input the original
+ *          width and height.  For repeated rotation, use of the
+ *          original width and height allows the expansion to
+ *          stop at the maximum required size, which is a square
+ *          with side = sqrt(w*w + h*h).
+ * 
+ */ +PIX * +pixRotate(PIX *pixs, + l_float32 angle, + l_int32 type, + l_int32 incolor, + l_int32 width, + l_int32 height) +{ +l_int32 w, h, d; +l_uint32 fillval; +PIX *pix1, *pix2, *pix3, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixRotate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && + type != L_ROTATE_SAMPLING) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + /* Adjust rotation type if necessary: + * - If d == 1 bpp and the angle is more than about 6 degrees, + * rotate by sampling; otherwise rotate by shear. + * - If d > 1, only allow shear rotation up to about 20 degrees; + * beyond that, default a shear request to sampling. */ + if (pixGetDepth(pixs) == 1) { + if (L_ABS(angle) > Max1BppShearAngle) { + if (type != L_ROTATE_SAMPLING) + L_INFO("1 bpp, large angle; rotate by sampling\n", procName); + type = L_ROTATE_SAMPLING; + } else if (type != L_ROTATE_SHEAR) { + L_INFO("1 bpp; rotate by shear\n", procName); + type = L_ROTATE_SHEAR; + } + } else if (L_ABS(angle) > LimitShearAngle && type == L_ROTATE_SHEAR) { + L_INFO("large angle; rotate by sampling\n", procName); + type = L_ROTATE_SAMPLING; + } + + /* Remove colormap if we rotate by area mapping. */ + cmap = pixGetColormap(pixs); + if (cmap && type == L_ROTATE_AREA_MAP) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix1 = pixClone(pixs); + cmap = pixGetColormap(pix1); + + /* Otherwise, if there is a colormap and we're not embedding, + * add white color if it doesn't exist. */ + if (cmap && width == 0) { /* no embedding; generate %incolor */ + if (incolor == L_BRING_IN_BLACK) + pixcmapAddBlackOrWhite(cmap, 0, NULL); + else /* L_BRING_IN_WHITE */ + pixcmapAddBlackOrWhite(cmap, 1, NULL); + } + + /* Request to embed in a larger image; do if necessary */ + pix2 = pixEmbedForRotation(pix1, angle, incolor, width, height); + + /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and + * area map rotation is requested, convert to 8 bpp. */ + d = pixGetDepth(pix2); + if (type == L_ROTATE_AREA_MAP && d < 8) + pix3 = pixConvertTo8(pix2, FALSE); + else + pix3 = pixClone(pix2); + + /* Do the rotation: shear, sampling or area mapping */ + pixGetDimensions(pix3, &w, &h, &d); + if (type == L_ROTATE_SHEAR) { + pixd = pixRotateShearCenter(pix3, angle, incolor); + } else if (type == L_ROTATE_SAMPLING) { + pixd = pixRotateBySampling(pix3, w / 2, h / 2, angle, incolor); + } else { /* rotate by area mapping */ + fillval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + fillval = 255; + else /* d == 32 */ + fillval = 0xffffff00; + } + if (d == 8) + pixd = pixRotateAMGray(pix3, angle, fillval); + else /* d == 32 */ + pixd = pixRotateAMColor(pix3, angle, fillval); + } + + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + return pixd; +} + + +/*! + * \brief pixEmbedForRotation() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp rgb + * \param[in] angle radians; clockwise is positive + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \param[in] width original width; use 0 to avoid embedding + * \param[in] height original height; use 0 to avoid embedding + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) For very small rotations, just return a clone.
+ *      (2) Generate larger image to embed pixs if necessary, and
+ *          place the center of the input image in the center.
+ *      (3) Rotation brings either white or black pixels in
+ *          from outside the image.  For colormapped images where
+ *          there is no white or black, a new color is added if
+ *          possible for these pixels; otherwise, either the
+ *          lightest or darkest color is used.  In most cases,
+ *          the colormap will be removed prior to rotation.
+ *      (4) The dest is to be expanded so that no image pixels
+ *          are lost after rotation.  Input of the original width
+ *          and height allows the expansion to stop at the maximum
+ *          required size, which is a square with side equal to
+ *          sqrt(w*w + h*h).
+ *      (5) For an arbitrary angle, the expansion can be found by
+ *          considering the UL and UR corners.  As the image is
+ *          rotated, these move in an arc centered at the center of
+ *          the image.  Normalize to a unit circle by dividing by half
+ *          the image diagonal.  After a rotation of T radians, the UL
+ *          and UR corners are at points T radians along the unit
+ *          circle.  Compute the x and y coordinates of both these
+ *          points and take the max of absolute values; these represent
+ *          the half width and half height of the containing rectangle.
+ *          The arithmetic is done using formulas for sin(a+b) and cos(a+b),
+ *          where b = T.  For the UR corner, sin(a) = h/d and cos(a) = w/d.
+ *          For the UL corner, replace a by (pi - a), and you have
+ *          sin(pi - a) = h/d, cos(pi - a) = -w/d.  The equations
+ *          given below follow directly.
+ * 
+ */ +PIX * +pixEmbedForRotation(PIX *pixs, + l_float32 angle, + l_int32 incolor, + l_int32 width, + l_int32 height) +{ +l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor; +l_float64 sina, cosa, fw, fh; +PIX *pixd; + + PROCNAME("pixEmbedForRotation"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + /* Test if big enough to hold any rotation of the original image */ + pixGetDimensions(pixs, &w, &h, &d); + maxside = (l_int32)(sqrt((l_float64)(width * width) + + (l_float64)(height * height)) + 0.5); + if (w >= maxside && h >= maxside) /* big enough */ + return pixClone(pixs); + + /* Find the new sizes required to hold the image after rotation. + * Note that the new dimensions must be at least as large as those + * of pixs, because we're rasterop-ing into it before rotation. */ + cosa = cos(angle); + sina = sin(angle); + fw = (l_float64)w; + fh = (l_float64)h; + w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5); + w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5); + h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5); + h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5); + wnew = L_MAX(w, L_MAX(w1, w2)); + hnew = L_MAX(h, L_MAX(h1, h2)); + + if ((pixd = pixCreate(wnew, hnew, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + pixCopySpp(pixd, pixs); + pixCopyText(pixd, pixs); + xoff = (wnew - w) / 2; + yoff = (hnew - h) / 2; + + /* Set background to color to be rotated in */ + setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE; + pixSetBlackOrWhite(pixd, setcolor); + + /* Rasterop automatically handles all 4 channels for rgba */ + pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0); + return pixd; +} + + +/*------------------------------------------------------------------* + * General rotation by sampling * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateBySampling() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp rgb; can be cmapped + * \param[in] xcen x value of center of rotation + * \param[in] ycen y value of center of rotation + * \param[in] angle radians; clockwise is positive + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) For very small rotations, just return a clone.
+ *      (2) Rotation brings either white or black pixels in
+ *          from outside the image.
+ *      (3) Colormaps are retained.
+ * 
+ */ +PIX * +pixRotateBySampling(PIX *pixs, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 incolor) +{ +l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld; +l_uint32 val; +l_float32 sina, cosa; +l_uint32 *datad, *lined; +void **lines; +PIX *pixd; + + PROCNAME("pixRotateBySampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("invalid depth", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSetBlackOrWhite(pixd, incolor); + + sina = sin(angle); + cosa = cos(angle); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + wm1 = w - 1; + hm1 = h - 1; + lines = pixGetLinePtrs(pixs, NULL); + + /* Treat 1 bpp case specially */ + if (d == 1) { + for (i = 0; i < h; i++) { /* scan over pixd */ + lined = datad + i * wpld; + ydif = ycen - i; + for (j = 0; j < w; j++) { + xdif = xcen - j; + x = xcen + (l_int32)(-xdif * cosa - ydif * sina); + if (x < 0 || x > wm1) continue; + y = ycen + (l_int32)(-ydif * cosa + xdif * sina); + if (y < 0 || y > hm1) continue; + if (incolor == L_BRING_IN_WHITE) { + if (GET_DATA_BIT(lines[y], x)) + SET_DATA_BIT(lined, j); + } else { + if (!GET_DATA_BIT(lines[y], x)) + CLEAR_DATA_BIT(lined, j); + } + } + } + LEPT_FREE(lines); + return pixd; + } + + for (i = 0; i < h; i++) { /* scan over pixd */ + lined = datad + i * wpld; + ydif = ycen - i; + for (j = 0; j < w; j++) { + xdif = xcen - j; + x = xcen + (l_int32)(-xdif * cosa - ydif * sina); + if (x < 0 || x > wm1) continue; + y = ycen + (l_int32)(-ydif * cosa + xdif * sina); + if (y < 0 || y > hm1) continue; + switch (d) + { + case 8: + val = GET_DATA_BYTE(lines[y], x); + SET_DATA_BYTE(lined, j, val); + break; + case 32: + val = GET_DATA_FOUR_BYTES(lines[y], x); + SET_DATA_FOUR_BYTES(lined, j, val); + break; + case 2: + val = GET_DATA_DIBIT(lines[y], x); + SET_DATA_DIBIT(lined, j, val); + break; + case 4: + val = GET_DATA_QBIT(lines[y], x); + SET_DATA_QBIT(lined, j, val); + break; + case 16: + val = GET_DATA_TWO_BYTES(lines[y], x); + SET_DATA_TWO_BYTES(lined, j, val); + break; + default: + return (PIX *)ERROR_PTR("invalid depth", procName, NULL); + } + } + } + + LEPT_FREE(lines); + return pixd; +} + + +/*------------------------------------------------------------------* + * Nice (slow) rotation of 1 bpp image * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateBinaryNice() + * + * \param[in] pixs 1 bpp + * \param[in] angle radians; clockwise is positive; about the center + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) For very small rotations, just return a clone.
+ *      (2) This does a computationally expensive rotation of 1 bpp images.
+ *          The fastest rotators (using shears or subsampling) leave
+ *          visible horizontal and vertical shear lines across which
+ *          the image shear changes by one pixel.  To ameliorate the
+ *          visual effect one can introduce random dithering.  One
+ *          way to do this in a not-too-random fashion is given here.
+ *          We convert to 8 bpp, do a very small blur, rotate using
+ *          linear interpolation (same as area mapping), do a
+ *          small amount of sharpening to compensate for the initial
+ *          blur, and threshold back to binary.  The shear lines
+ *          are magically removed.
+ *      (3) This operation is about 5x slower than rotation by sampling.
+ * 
+ */ +PIX * +pixRotateBinaryNice(PIX *pixs, + l_float32 angle, + l_int32 incolor) +{ +PIX *pix1, *pix2, *pix3, *pix4, *pixd; + + PROCNAME("pixRotateBinaryNice"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + pix1 = pixConvertTo8(pixs, 0); + pix2 = pixBlockconv(pix1, 1, 1); /* smallest blur allowed */ + pix3 = pixRotateAM(pix2, angle, incolor); + pix4 = pixUnsharpMasking(pix3, 1, 1.0); /* sharpen a bit */ + pixd = pixThresholdToBinary(pix4, 128); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + return pixd; +} + + +/*------------------------------------------------------------------* + * Rotation including alpha (blend) component * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateWithAlpha() + * + * \param[in] pixs 32 bpp rgb or cmapped + * \param[in] angle radians; clockwise is positive + * \param[in] pixg [optional] 8 bpp, can be null + * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent + * and 1.0 fully opaque + * \return pixd 32 bpp rgba, or NULL on error + * + *
+ * Notes:
+ *      (1) The alpha channel is transformed separately from pixs,
+ *          and aligns with it, being fully transparent outside the
+ *          boundary of the transformed pixs.  For pixels that are fully
+ *          transparent, a blending function like pixBlendWithGrayMask()
+ *          will give zero weight to corresponding pixels in pixs.
+ *      (2) Rotation is about the center of the image; for very small
+ *          rotations, just return a clone.  The dest is automatically
+ *          expanded so that no image pixels are lost.
+ *      (3) Rotation is by area mapping.  It doesn't matter what
+ *          color is brought in because the alpha channel will
+ *          be transparent (black) there.
+ *      (4) If pixg is NULL, it is generated as an alpha layer that is
+ *          partially opaque, using %fract.  Otherwise, it is cropped
+ *          to pixs if required and %fract is ignored.  The alpha
+ *          channel in pixs is never used.
+ *      (4) Colormaps are removed to 32 bpp.
+ *      (5) The default setting for the border values in the alpha channel
+ *          is 0 (transparent) for the outermost ring of pixels and
+ *          (0.5 * fract * 255) for the second ring.  When blended over
+ *          a second image, this
+ *          (a) shrinks the visible image to make a clean overlap edge
+ *              with an image below, and
+ *          (b) softens the edges by weakening the aliasing there.
+ *          Use l_setAlphaMaskBorder() to change these values.
+ *      (6) A subtle use of gamma correction is to remove gamma correction
+ *          before rotation and restore it afterwards.  This is done
+ *          by sandwiching this function between a gamma/inverse-gamma
+ *          photometric transform:
+ *              pixt = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255);
+ *              pixd = pixRotateWithAlpha(pixt, angle, NULL, fract);
+ *              pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255);
+ *              pixDestroy(&pixt);
+ *          This has the side-effect of producing artifacts in the very
+ *          dark regions.
+ * 
+ */ +PIX * +pixRotateWithAlpha(PIX *pixs, + l_float32 angle, + PIX *pixg, + l_float32 fract) +{ +l_int32 ws, hs, d, spp; +PIX *pixd, *pix32, *pixg2, *pixgr; + + PROCNAME("pixRotateWithAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (d != 32 && pixGetColormap(pixs) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (pixg && pixGetDepth(pixg) != 8) { + L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", + procName); + pixg = NULL; + } + if (!pixg && (fract < 0.0 || fract > 1.0)) { + L_WARNING("invalid fract; using fully opaque\n", procName); + fract = 1.0; + } + if (!pixg && fract == 0.0) + L_WARNING("transparent alpha; image will not be blended\n", procName); + + /* Make sure input to rotation is 32 bpp rgb, and rotate it */ + if (d != 32) + pix32 = pixConvertTo32(pixs); + else + pix32 = pixClone(pixs); + spp = pixGetSpp(pix32); + pixSetSpp(pix32, 3); /* ignore the alpha channel for the rotation */ + pixd = pixRotate(pix32, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs); + pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */ + pixDestroy(&pix32); + + /* Set up alpha layer with a fading border and rotate it */ + if (!pixg) { + pixg2 = pixCreate(ws, hs, 8); + if (fract == 1.0) + pixSetAll(pixg2); + else if (fract > 0.0) + pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); + } else { + pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); + } + if (ws > 10 && hs > 10) { /* see note 8 */ + pixSetBorderRingVal(pixg2, 1, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); + pixSetBorderRingVal(pixg2, 2, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); + } + pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP, + L_BRING_IN_BLACK, ws, hs); + + /* Combine into a 4 spp result */ + pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL); + + pixDestroy(&pixg2); + pixDestroy(&pixgr); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rotateam.c b/hgdriver/3rdparty/hgOCR/leptonica/rotateam.c new file mode 100644 index 0000000..2a88216 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rotateam.c @@ -0,0 +1,1128 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file rotateam.c + *
+ *
+ *     Grayscale and color rotation for area mapping (== interpolation)
+ *
+ *         Rotation about the image center
+ *                PIX         *pixRotateAM()
+ *                PIX         *pixRotateAMColor()
+ *                PIX         *pixRotateAMGray()
+ *                static void  rotateAMColorLow()
+ *                static void  rotateAMGrayLow()
+ *
+ *         Rotation about the UL corner of the image
+ *                PIX         *pixRotateAMCorner()
+ *                PIX         *pixRotateAMColorCorner()
+ *                PIX         *pixRotateAMGrayCorner()
+ *                static void  rotateAMColorCornerLow()
+ *                static void  rotateAMGrayCornerLow()
+ *
+ *         Faster color rotation about the image center
+ *                PIX         *pixRotateAMColorFast()
+ *                static void  rotateAMColorFastLow()
+ *
+ *     Rotations are measured in radians; clockwise is positive.
+ *
+ *     The basic area mapping grayscale rotation works on 8 bpp images.
+ *     For color, the same method is applied to each color separately.
+ *     This can be done in two ways: (1) as here, computing each dest
+ *     rgb pixel from the appropriate four src rgb pixels, or (2) separating
+ *     the color image into three 8 bpp images, rotate each of these,
+ *     and then combine the result.  Method (1) is about 2.5x faster.
+ *     We have also implemented a fast approximation for color area-mapping
+ *     rotation (pixRotateAMColorFast()), which is about 25% faster
+ *     than the standard color rotator.  If you need the extra speed,
+ *     use it.
+ *
+ *     Area mapping works as follows.  For each dest
+ *     pixel you find the 4 source pixels that it partially
+ *     covers.  You then compute the dest pixel value as
+ *     the area-weighted average of those 4 source pixels.
+ *     We make two simplifying approximations:
+ *
+ *       ~  For simplicity, compute the areas as if the dest
+ *          pixel were translated but not rotated.
+ *
+ *       ~  Compute area overlaps on a discrete sub-pixel grid.
+ *          Because we are using 8 bpp images with 256 levels,
+ *          it is convenient to break each pixel into a
+ *          16x16 sub-pixel grid, and count the number of
+ *          overlapped sub-pixels.
+ *
+ *     It is interesting to note that the digital filter that
+ *     implements the area mapping algorithm for rotation
+ *     is identical to the digital filter used for linear
+ *     interpolation when arbitrarily scaling grayscale images.
+ *
+ *     The advantage of area mapping over pixel sampling
+ *     in grayscale rotation is that the former naturally
+ *     blurs sharp edges ("anti-aliasing"), so that stair-step
+ *     artifacts are not introduced.  The disadvantage is that
+ *     it is significantly slower.
+ *
+ *     But it is still pretty fast.  With standard 3 GHz hardware,
+ *     the anti-aliased (area-mapped) color rotation speed is
+ *     about 15 million pixels/sec.
+ *
+ *     The function pixRotateAMColorFast() is about 10-20% faster
+ *     than pixRotateAMColor().  The quality is slightly worse,
+ *     and if you make many successive small rotations, with a
+ *     total angle of 360 degrees, it has been noted that the
+ *     center wanders -- it seems to be doing a 1 pixel translation
+ *     in addition to the rotation.
+ *
+ *     Consider again the comparison of image quality between sampling
+ *     and area mapping.  With sampling, sharp edges such as found in
+ *     text images remain sharp.  However, sampling artifacts such as
+ *     characters randomly bouncing up and down by one pixel, or
+ *     one pixel horizontal shear lines going through a line of text
+ *     (causing the characters to look like badly rendered italic),
+ *     are highly visible.  It does not help to sample the source pixel
+ *     with the largest area covering each dest pixel; the result has
+ *     the same ugly sampling artifacts.
+ *
+ *     With area mapping, these annoying artifacts are avoided, but the
+ *     blurring of edges makes small text a bit more difficult to read.
+ *     However, if you are willing to do more computation, you can have
+ *     the best of both worlds: no sampling artifacts and sharp edges.
+ *     Use area mapping to avoid sampling issues, and follow it with
+ *     unsharp masking.  Experiment with the sharpening parameters.
+ *     I have found that a small amount of sharpening is sufficient to
+ *     restore the sharp edges in text; e.g.,
+ *         pix2 = pixUnsharpMasking(pix1, 1, 0.3);
+ * 
+ */ + +#include +#include /* required for sin and tan */ +#include "allheaders.h" + +static void rotateAMColorLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_float32 angle, l_uint32 colorval); +static void rotateAMGrayLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_float32 angle, l_uint8 grayval); +static void rotateAMColorCornerLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, + l_int32 wpls, l_float32 angle, + l_uint32 colorval); +static void rotateAMGrayCornerLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_float32 angle, l_uint8 grayval); + +static void rotateAMColorFastLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_float32 angle, l_uint32 colorval); + +static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */ + + +/*------------------------------------------------------------------* + * Rotation about the center * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateAM() + * + * \param[in] pixs 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB + * \param[in] angle radians; clockwise is positive + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates about image center.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Brings in either black or white pixels from the boundary.
+ * 
+ */ +PIX * +pixRotateAM(PIX *pixs, + l_float32 angle, + l_int32 incolor) +{ +l_int32 d; +l_uint32 fillval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixRotateAM"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) == 1) + return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual incoming color */ + fillval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + fillval = 255; + else /* d == 32 */ + fillval = 0xffffff00; + } + + if (d == 8) + pixd = pixRotateAMGray(pixt2, angle, fillval); + else /* d == 32 */ + pixd = pixRotateAMColor(pixt2, angle, fillval); + + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixRotateAMColor() + * + * \param[in] pixs 32 bpp + * \param[in] angle radians; clockwise is positive + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates about image center.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Specify the color to be brought in from outside the image.
+ * 
+ */ +PIX * +pixRotateAMColor(PIX *pixs, + l_float32 angle, + l_uint32 colorval) +{ +l_int32 w, h, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixRotateAMColor"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + rotateAMColorLow(datad, w, h, wpld, datas, wpls, angle, colorval); + if (pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixRotateAMGray(pix1, angle, 255); /* bring in opaque */ + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return pixd; +} + + +/*! + * \brief pixRotateAMGray() + * + * \param[in] pixs 8 bpp + * \param[in] angle radians; clockwise is positive + * \param[in] grayval 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates about image center.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Specify the grayvalue to be brought in from outside the image.
+ * 
+ */ +PIX * +pixRotateAMGray(PIX *pixs, + l_float32 angle, + l_uint8 grayval) +{ +l_int32 w, h, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixRotateAMGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + rotateAMGrayLow(datad, w, h, wpld, datas, wpls, angle, grayval); + + return pixd; +} + + +static void +rotateAMColorLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 angle, + l_uint32 colorval) +{ +l_int32 i, j, xcen, ycen, wm2, hm2; +l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf; +l_int32 rval, gval, bval; +l_uint32 word00, word01, word10, word11; +l_uint32 *lines, *lined; +l_float32 sina, cosa; + + xcen = w / 2; + wm2 = w - 2; + ycen = h / 2; + hm2 = h - 2; + sina = 16. * sin(angle); + cosa = 16. * cos(angle); + + for (i = 0; i < h; i++) { + ydif = ycen - i; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + xdif = xcen - j; + xpm = (l_int32)(-xdif * cosa - ydif * sina); + ypm = (l_int32)(-ydif * cosa + xdif * sina); + xp = xcen + (xpm >> 4); + yp = ycen + (ypm >> 4); + xf = xpm & 0x0f; + yf = ypm & 0x0f; + + /* if off the edge, write input colorval */ + if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { + *(lined + j) = colorval; + continue; + } + + lines = datas + yp * wpls; + + /* do area weighting. Without this, we would + * simply do: + * *(lined + j) = *(lines + xp); + * which is faster but gives lousy results! + */ + word00 = *(lines + xp); + word10 = *(lines + xp + 1); + word01 = *(lines + wpls + xp); + word11 = *(lines + wpls + xp + 1); + rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256; + gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256; + bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256; + composeRGBPixel(rval, gval, bval, lined + j); + } + } +} + + +static void +rotateAMGrayLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 angle, + l_uint8 grayval) +{ +l_int32 i, j, xcen, ycen, wm2, hm2; +l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf; +l_int32 v00, v01, v10, v11; +l_uint8 val; +l_uint32 *lines, *lined; +l_float32 sina, cosa; + + xcen = w / 2; + wm2 = w - 2; + ycen = h / 2; + hm2 = h - 2; + sina = 16. * sin(angle); + cosa = 16. * cos(angle); + + for (i = 0; i < h; i++) { + ydif = ycen - i; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + xdif = xcen - j; + xpm = (l_int32)(-xdif * cosa - ydif * sina); + ypm = (l_int32)(-ydif * cosa + xdif * sina); + xp = xcen + (xpm >> 4); + yp = ycen + (ypm >> 4); + xf = xpm & 0x0f; + yf = ypm & 0x0f; + + /* if off the edge, write input grayval */ + if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { + SET_DATA_BYTE(lined, j, grayval); + continue; + } + + lines = datas + yp * wpls; + + /* do area weighting. Without this, we would + * simply do: + * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp)); + * which is faster but gives lousy results! + */ + v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); + v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1); + v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); + v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1); + val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256); + SET_DATA_BYTE(lined, j, val); + } + } +} + + +/*------------------------------------------------------------------* + * Rotation about the UL corner * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateAMCorner() + * + * \param[in] pixs 1, 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB + * \param[in] angle radians; clockwise is positive + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates about the UL corner of the image.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Brings in either black or white pixels from the boundary.
+ * 
+ */ +PIX * +pixRotateAMCorner(PIX *pixs, + l_float32 angle, + l_int32 incolor) +{ +l_int32 d; +l_uint32 fillval; +PIX *pixt1, *pixt2, *pixd; + + PROCNAME("pixRotateAMCorner"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ + pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixt1); + if (d < 8) + pixt2 = pixConvertTo8(pixt1, FALSE); + else + pixt2 = pixClone(pixt1); + d = pixGetDepth(pixt2); + + /* Compute actual incoming color */ + fillval = 0; + if (incolor == L_BRING_IN_WHITE) { + if (d == 8) + fillval = 255; + else /* d == 32 */ + fillval = 0xffffff00; + } + + if (d == 8) + pixd = pixRotateAMGrayCorner(pixt2, angle, fillval); + else /* d == 32 */ + pixd = pixRotateAMColorCorner(pixt2, angle, fillval); + + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; +} + + +/*! + * \brief pixRotateAMColorCorner() + * + * \param[in] pixs + * \param[in] angle radians; clockwise is positive + * \param[in] fillval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates the image about the UL corner.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Specify the color to be brought in from outside the image.
+ * 
+ */ +PIX * +pixRotateAMColorCorner(PIX *pixs, + l_float32 angle, + l_uint32 fillval) +{ +l_int32 w, h, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixRotateAMColorCorner"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + rotateAMColorCornerLow(datad, w, h, wpld, datas, wpls, angle, fillval); + if (pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixRotateAMGrayCorner(pix1, angle, 255); /* bring in opaque */ + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + return pixd; +} + + +/*! + * \brief pixRotateAMGrayCorner() + * + * \param[in] pixs + * \param[in] angle radians; clockwise is positive + * \param[in] grayval 0 to bring in BLACK, 255 for WHITE + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Rotates the image about the UL corner.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) Specify the grayvalue to be brought in from outside the image.
+ * 
+ */ +PIX * +pixRotateAMGrayCorner(PIX *pixs, + l_float32 angle, + l_uint8 grayval) +{ +l_int32 w, h, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixRotateAMGrayCorner"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + rotateAMGrayCornerLow(datad, w, h, wpld, datas, wpls, angle, grayval); + + return pixd; +} + + +static void +rotateAMColorCornerLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 angle, + l_uint32 colorval) +{ +l_int32 i, j, wm2, hm2; +l_int32 xpm, ypm, xp, yp, xf, yf; +l_int32 rval, gval, bval; +l_uint32 word00, word01, word10, word11; +l_uint32 *lines, *lined; +l_float32 sina, cosa; + + wm2 = w - 2; + hm2 = h - 2; + sina = 16. * sin(angle); + cosa = 16. * cos(angle); + + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + xpm = (l_int32)(j * cosa + i * sina); + ypm = (l_int32)(i * cosa - j * sina); + xp = xpm >> 4; + yp = ypm >> 4; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + + /* if off the edge, write input colorval */ + if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { + *(lined + j) = colorval; + continue; + } + + lines = datas + yp * wpls; + + /* do area weighting. Without this, we would + * simply do: + * *(lined + j) = *(lines + xp); + * which is faster but gives lousy results! + */ + word00 = *(lines + xp); + word10 = *(lines + xp + 1); + word01 = *(lines + wpls + xp); + word11 = *(lines + wpls + xp + 1); + rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256; + gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256; + bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + + xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + + (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + + xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256; + composeRGBPixel(rval, gval, bval, lined + j); + } + } +} + + +static void +rotateAMGrayCornerLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 angle, + l_uint8 grayval) +{ +l_int32 i, j, wm2, hm2; +l_int32 xpm, ypm, xp, yp, xf, yf; +l_int32 v00, v01, v10, v11; +l_uint8 val; +l_uint32 *lines, *lined; +l_float32 sina, cosa; + + wm2 = w - 2; + hm2 = h - 2; + sina = 16. * sin(angle); + cosa = 16. * cos(angle); + + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + xpm = (l_int32)(j * cosa + i * sina); + ypm = (l_int32)(i * cosa - j * sina); + xp = xpm >> 4; + yp = ypm >> 4; + xf = xpm & 0x0f; + yf = ypm & 0x0f; + + /* if off the edge, write input grayval */ + if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { + SET_DATA_BYTE(lined, j, grayval); + continue; + } + + lines = datas + yp * wpls; + + /* do area weighting. Without this, we would + * simply do: + * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp)); + * which is faster but gives lousy results! + */ + v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); + v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1); + v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); + v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1); + val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256); + SET_DATA_BYTE(lined, j, val); + } + } +} + + +/*------------------------------------------------------------------* + * Fast RGB color rotation about center * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateAMColorFast() + * + * \param[in] pixs + * \param[in] angle radians; clockwise is positive + * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This rotates a color image about the image center.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) It uses area mapping, dividing each pixel into
+ *          16 subpixels.
+ *      (4) It is about 10% to 20% faster than the more accurate linear
+ *          interpolation function pixRotateAMColor(),
+ *          which uses 256 subpixels.
+ *      (5) For some reason it shifts the image center.
+ *          No attempt is made to rotate the alpha component.
+ * 
+ */ +PIX * +pixRotateAMColorFast(PIX *pixs, + l_float32 angle, + l_uint32 colorval) +{ +l_int32 w, h, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixRotateAMColorFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); + + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreateTemplate(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + rotateAMColorFastLow(datad, w, h, wpld, datas, wpls, angle, colorval); + return pixd; +} + + +/*! + * \brief rotateAMColorFastLow() + * + * This is a special simplification of area mapping with division + * of each pixel into 16 sub-pixels. The exact coefficients that + * should be used are the same as for the 4x linear interpolation + * scaling case, and are given there. I tried to approximate these + * as weighted coefficients with a maximum sum of 4, which + * allows us to do the arithmetic in parallel for the R, G and B + * components in a 32 bit pixel. However, there are three reasons + * for not doing that: + * (1) the loss of accuracy in the parallel implementation + * is visually significant + * (2) the parallel implementation (described below) is slower + * (3) the parallel implementation requires allocation of + * a temporary color image + * + * There are 16 cases for the choice of the subpixel, and + * for each, the mapping to the relevant source + * pixels is as follows: + * + * subpixel src pixel weights + * -------- ----------------- + * 0 sp1 + * 1 (3 * sp1 + sp2) / 4 + * 2 (sp1 + sp2) / 2 + * 3 (sp1 + 3 * sp2) / 4 + * 4 (3 * sp1 + sp3) / 4 + * 5 (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16 + * 6 (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8 + * 7 (3 * sp1 + 9 * sp2 + sp3 + 3 * sp4) / 16 + * 8 (sp1 + sp3) / 2 + * 9 (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8 + * 10 (sp1 + sp2 + sp3 + sp4) / 4 + * 11 (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8 + * 12 (sp1 + 3 * sp3) / 4 + * 13 (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16 + * 14 (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8 + * 15 (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16 + * + * Another way to visualize this is to consider the area mapping + * (or linear interpolation) coefficients for the pixel sp1. + * Expressed in fourths, they can be written as asymmetric matrix: + * + * 4 3 2 1 + * 3 2.25 1.5 0.75 + * 2 1.5 1 0.5 + * 1 0.75 0.5 0.25 + * + * The coefficients for the three neighboring pixels can be + * similarly written. + * + * This is implemented here, where, for each color component, + * we inline its extraction from each participating word, + * construct the linear combination, and combine the results + * into the destination 32 bit RGB pixel, using the appropriate shifts. + * + * It is interesting to note that an alternative method, where + * we do the arithmetic on the 32 bit pixels directly (after + * shifting the components so they won't overflow into each other) + * is significantly inferior. Because we have only 8 bits for + * internal overflows, which can be distributed as 2, 3, 3, it + * is impossible to add these with the correct linear + * interpolation coefficients, which require a sum of up to 16. + * Rounding off to a sum of 4 causes appreciable visual artifacts + * in the rotated image. The code for the inferior method + * can be found in prog/rotatefastalt.c, for reference. + */ +static void +rotateAMColorFastLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 angle, + l_uint32 colorval) +{ +l_int32 i, j, xcen, ycen, wm2, hm2; +l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf; +l_uint32 word1, word2, word3, word4, red, blue, green; +l_uint32 *pword, *lines, *lined; +l_float32 sina, cosa; + + xcen = w / 2; + wm2 = w - 2; + ycen = h / 2; + hm2 = h - 2; + sina = 4. * sin(angle); + cosa = 4. * cos(angle); + + for (i = 0; i < h; i++) { + ydif = ycen - i; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + xdif = xcen - j; + xpm = (l_int32)(-xdif * cosa - ydif * sina); + ypm = (l_int32)(-ydif * cosa + xdif * sina); + xp = xcen + (xpm >> 2); + yp = ycen + (ypm >> 2); + xf = xpm & 0x03; + yf = ypm & 0x03; + + /* if off the edge, write input grayval */ + if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { + *(lined + j) = colorval; + continue; + } + + lines = datas + yp * wpls; + pword = lines + xp; + + switch (xf + 4 * yf) + { + case 0: + *(lined + j) = *pword; + break; + case 1: + word1 = *pword; + word2 = *(pword + 1); + red = 3 * (word1 >> 24) + (word2 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + + ((word2 >> 16) & 0xff); + blue = 3 * ((word1 >> 8) & 0xff) + + ((word2 >> 8) & 0xff); + *(lined + j) = ((red << 22) & 0xff000000) | + ((green << 14) & 0x00ff0000) | + ((blue << 6) & 0x0000ff00); + break; + case 2: + word1 = *pword; + word2 = *(pword + 1); + red = (word1 >> 24) + (word2 >> 24); + green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff); + *(lined + j) = ((red << 23) & 0xff000000) | + ((green << 15) & 0x00ff0000) | + ((blue << 7) & 0x0000ff00); + break; + case 3: + word1 = *pword; + word2 = *(pword + 1); + red = (word1 >> 24) + 3 * (word2 >> 24); + green = ((word1 >> 16) & 0xff) + + 3 * ((word2 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + + 3 * ((word2 >> 8) & 0xff); + *(lined + j) = ((red << 22) & 0xff000000) | + ((green << 14) & 0x00ff0000) | + ((blue << 6) & 0x0000ff00); + break; + case 4: + word1 = *pword; + word3 = *(pword + wpls); + red = 3 * (word1 >> 24) + (word3 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + + ((word3 >> 16) & 0xff); + blue = 3 * ((word1 >> 8) & 0xff) + + ((word3 >> 8) & 0xff); + *(lined + j) = ((red << 22) & 0xff000000) | + ((green << 14) & 0x00ff0000) | + ((blue << 6) & 0x0000ff00); + break; + case 5: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = 9 * (word1 >> 24) + 3 * (word2 >> 24) + + 3 * (word3 >> 24) + (word4 >> 24); + green = 9 * ((word1 >> 16) & 0xff) + + 3 * ((word2 >> 16) & 0xff) + + 3 * ((word3 >> 16) & 0xff) + + ((word4 >> 16) & 0xff); + blue = 9 * ((word1 >> 8) & 0xff) + + 3 * ((word2 >> 8) & 0xff) + + 3 * ((word3 >> 8) & 0xff) + + ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 20) & 0xff000000) | + ((green << 12) & 0x00ff0000) | + ((blue << 4) & 0x0000ff00); + break; + case 6: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = 3 * (word1 >> 24) + 3 * (word2 >> 24) + + (word3 >> 24) + (word4 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + + 3 * ((word2 >> 16) & 0xff) + + ((word3 >> 16) & 0xff) + + ((word4 >> 16) & 0xff); + blue = 3 * ((word1 >> 8) & 0xff) + + 3 * ((word2 >> 8) & 0xff) + + ((word3 >> 8) & 0xff) + + ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 21) & 0xff000000) | + ((green << 13) & 0x00ff0000) | + ((blue << 5) & 0x0000ff00); + break; + case 7: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = 3 * (word1 >> 24) + 9 * (word2 >> 24) + + (word3 >> 24) + 3 * (word4 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + + 9 * ((word2 >> 16) & 0xff) + + ((word3 >> 16) & 0xff) + + 3 * ((word4 >> 16) & 0xff); + blue = 3 * ((word1 >> 8) & 0xff) + + 9 * ((word2 >> 8) & 0xff) + + ((word3 >> 8) & 0xff) + + 3 * ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 20) & 0xff000000) | + ((green << 12) & 0x00ff0000) | + ((blue << 4) & 0x0000ff00); + break; + case 8: + word1 = *pword; + word3 = *(pword + wpls); + red = (word1 >> 24) + (word3 >> 24); + green = ((word1 >> 16) & 0xff) + ((word3 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + ((word3 >> 8) & 0xff); + *(lined + j) = ((red << 23) & 0xff000000) | + ((green << 15) & 0x00ff0000) | + ((blue << 7) & 0x0000ff00); + break; + case 9: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = 3 * (word1 >> 24) + (word2 >> 24) + + 3 * (word3 >> 24) + (word4 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + + 3 * ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); + blue = 3 * ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + + 3 * ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 21) & 0xff000000) | + ((green << 13) & 0x00ff0000) | + ((blue << 5) & 0x0000ff00); + break; + case 10: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = (word1 >> 24) + (word2 >> 24) + + (word3 >> 24) + (word4 >> 24); + green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + + ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + + ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 22) & 0xff000000) | + ((green << 14) & 0x00ff0000) | + ((blue << 6) & 0x0000ff00); + break; + case 11: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = (word1 >> 24) + 3 * (word2 >> 24) + + (word3 >> 24) + 3 * (word4 >> 24); + green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + + ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + + ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 21) & 0xff000000) | + ((green << 13) & 0x00ff0000) | + ((blue << 5) & 0x0000ff00); + break; + case 12: + word1 = *pword; + word3 = *(pword + wpls); + red = (word1 >> 24) + 3 * (word3 >> 24); + green = ((word1 >> 16) & 0xff) + + 3 * ((word3 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + + 3 * ((word3 >> 8) & 0xff); + *(lined + j) = ((red << 22) & 0xff000000) | + ((green << 14) & 0x00ff0000) | + ((blue << 6) & 0x0000ff00); + break; + case 13: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = 3 * (word1 >> 24) + (word2 >> 24) + + 9 * (word3 >> 24) + 3 * (word4 >> 24); + green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + + 9 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); + blue = 3 *((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + + 9 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 20) & 0xff000000) | + ((green << 12) & 0x00ff0000) | + ((blue << 4) & 0x0000ff00); + break; + case 14: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = (word1 >> 24) + (word2 >> 24) + + 3 * (word3 >> 24) + 3 * (word4 >> 24); + green = ((word1 >> 16) & 0xff) +((word2 >> 16) & 0xff) + + 3 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + + 3 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 21) & 0xff000000) | + ((green << 13) & 0x00ff0000) | + ((blue << 5) & 0x0000ff00); + break; + case 15: + word1 = *pword; + word2 = *(pword + 1); + word3 = *(pword + wpls); + word4 = *(pword + wpls + 1); + red = (word1 >> 24) + 3 * (word2 >> 24) + + 3 * (word3 >> 24) + 9 * (word4 >> 24); + green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + + 3 * ((word3 >> 16) & 0xff) + 9 * ((word4 >> 16) & 0xff); + blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + + 3 * ((word3 >> 8) & 0xff) + 9 * ((word4 >> 8) & 0xff); + *(lined + j) = ((red << 20) & 0xff000000) | + ((green << 12) & 0x00ff0000) | + ((blue << 4) & 0x0000ff00); + break; + default: + fprintf(stderr, "shouldn't get here\n"); + break; + } + } + } +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rotateorth.c b/hgdriver/3rdparty/hgOCR/leptonica/rotateorth.c new file mode 100644 index 0000000..bb0e0c0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rotateorth.c @@ -0,0 +1,712 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file rotateorth.c + *
+ *
+ *      Top-level rotation by multiples of 90 degrees
+ *            PIX             *pixRotateOrth()
+ *
+ *      180-degree rotation
+ *            PIX             *pixRotate180()
+ *
+ *      90-degree rotation (both directions)
+ *            PIX             *pixRotate90()
+ *
+ *      Left-right flip
+ *            PIX             *pixFlipLR()
+ *
+ *      Top-bottom flip
+ *            PIX             *pixFlipTB()
+ *
+ *      Byte reverse tables
+ *            static l_uint8  *makeReverseByteTab1()
+ *            static l_uint8  *makeReverseByteTab2()
+ *            static l_uint8  *makeReverseByteTab4()
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_uint8 *makeReverseByteTab1(void); +static l_uint8 *makeReverseByteTab2(void); +static l_uint8 *makeReverseByteTab4(void); + + +/*------------------------------------------------------------------* + * Top-level rotation by multiples of 90 degrees * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateOrth() + * + * \param[in] pixs all depths + * \param[in] quads 0-3; number of 90 degree cw rotations + * \return pixd, or NULL on error + */ +PIX * +pixRotateOrth(PIX *pixs, + l_int32 quads) +{ + PROCNAME("pixRotateOrth"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (quads < 0 || quads > 3) + return (PIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL); + + if (quads == 0) + return pixCopy(NULL, pixs); + else if (quads == 1) + return pixRotate90(pixs, 1); + else if (quads == 2) + return pixRotate180(NULL, pixs); + else /* quads == 3 */ + return pixRotate90(pixs, -1); +} + + +/*------------------------------------------------------------------* + * 180 degree rotation * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotate180() + * + * \param[in] pixd [optional]; can be null, equal to pixs, + * or different from pixs + * \param[in] pixs all depths + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a 180 rotation of the image about the center,
+ *          which is equivalent to a left-right flip about a vertical
+ *          line through the image center, followed by a top-bottom
+ *          flip about a horizontal line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) pixd == null (creates a new pixd)
+ *          (b) pixd == pixs (in-place operation)
+ *          (c) pixd != pixs (existing pixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) pixd = pixRotate180(NULL, pixs);
+ *          (b) pixRotate180(pixs, pixs);
+ *          (c) pixRotate180(pixd, pixs);
+ * 
+ */ +PIX * +pixRotate180(PIX *pixd, + PIX *pixs) +{ +l_int32 d; + + PROCNAME("pixRotate180"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", + procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + pixFlipLR(pixd, pixd); + pixFlipTB(pixd, pixd); + return pixd; +} + + +/*------------------------------------------------------------------* + * 90 degree rotation * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotate90() + * + * \param[in] pixs all depths + * \param[in] direction clockwise = 1, counterclockwise = -1 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a 90 degree rotation of the image about the center,
+ *          either cw or ccw, returning a new pix.
+ *      (2) The direction must be either 1 (cw) or -1 (ccw).
+ * 
+ */ +PIX * +pixRotate90(PIX *pixs, + l_int32 direction) +{ +l_int32 wd, hd, d, wpls, wpld; +l_int32 i, j, k, m, iend, nswords; +l_uint32 val, word; +l_uint32 *lines, *datas, *lined, *datad; +PIX *pixd; + + PROCNAME("pixRotate90"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &hd, &wd, &d); /* note: reversed */ + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", + procName, NULL); + if (direction != 1 && direction != -1) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyColormap(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixCopySpp(pixd, pixs); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + if (direction == 1) { /* clockwise */ + switch (d) + { + case 32: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + lined[j] = lines[i]; + lines -= wpls; + } + } + break; + case 16: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_TWO_BYTES(lines, i))) + SET_DATA_TWO_BYTES(lined, j, val); + lines -= wpls; + } + } + break; + case 8: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_BYTE(lines, i))) + SET_DATA_BYTE(lined, j, val); + lines -= wpls; + } + } + break; + case 4: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_QBIT(lines, i))) + SET_DATA_QBIT(lined, j, val); + lines -= wpls; + } + } + break; + case 2: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas + (wd - 1) * wpls; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_DIBIT(lines, i))) + SET_DATA_DIBIT(lined, j, val); + lines -= wpls; + } + } + break; + case 1: + nswords = hd / 32; + for (j = 0; j < wd; j++) { + lined = datad; + lines = datas + (wd - 1 - j) * wpls; + for (k = 0; k < nswords; k++) { + word = lines[k]; + if (!word) { + lined += 32 * wpld; + continue; + } else { + iend = 32 * (k + 1); + for (m = 0, i = 32 * k; i < iend; i++, m++) { + if ((word << m) & 0x80000000) + SET_DATA_BIT(lined, j); + lined += wpld; + } + } + } + for (i = 32 * nswords; i < hd; i++) { + if (GET_DATA_BIT(lines, i)) + SET_DATA_BIT(lined, j); + lined += wpld; + } + } + break; + default: + pixDestroy(&pixd); + L_ERROR("illegal depth: %d\n", procName, d); + break; + } + } else { /* direction counter-clockwise */ + switch (d) + { + case 32: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + lined[j] = lines[hd - 1 - i]; + lines += wpls; + } + } + break; + case 16: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_TWO_BYTES(lines, hd - 1 - i))) + SET_DATA_TWO_BYTES(lined, j, val); + lines += wpls; + } + } + break; + case 8: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_BYTE(lines, hd - 1 - i))) + SET_DATA_BYTE(lined, j, val); + lines += wpls; + } + } + break; + case 4: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_QBIT(lines, hd - 1 - i))) + SET_DATA_QBIT(lined, j, val); + lines += wpls; + } + } + break; + case 2: + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + lines = datas; + for (j = 0; j < wd; j++) { + if ((val = GET_DATA_DIBIT(lines, hd - 1 - i))) + SET_DATA_DIBIT(lined, j, val); + lines += wpls; + } + } + break; + case 1: + nswords = hd / 32; + for (j = 0; j < wd; j++) { + lined = datad + (hd - 1) * wpld; + lines = datas + (wd - 1 - j) * wpls; + for (k = 0; k < nswords; k++) { + word = lines[k]; + if (!word) { + lined -= 32 * wpld; + continue; + } else { + iend = 32 * (k + 1); + for (m = 0, i = 32 * k; i < iend; i++, m++) { + if ((word << m) & 0x80000000) + SET_DATA_BIT(lined, wd - 1 - j); + lined -= wpld; + } + } + } + for (i = 32 * nswords; i < hd; i++) { + if (GET_DATA_BIT(lines, i)) + SET_DATA_BIT(lined, wd - 1 - j); + lined -= wpld; + } + } + break; + default: + pixDestroy(&pixd); + L_ERROR("illegal depth: %d\n", procName, d); + break; + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Left-right flip * + *------------------------------------------------------------------*/ +/*! + * \brief pixFlipLR() + * + * \param[in] pixd [optional]; can be null, equal to pixs, + * or different from pixs + * \param[in] pixs all depths + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a left-right flip of the image, which is
+ *          equivalent to a rotation out of the plane about a
+ *          vertical line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) pixd == null (creates a new pixd)
+ *          (b) pixd == pixs (in-place operation)
+ *          (c) pixd != pixs (existing pixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) pixd = pixFlipLR(NULL, pixs);
+ *          (b) pixFlipLR(pixs, pixs);
+ *          (c) pixFlipLR(pixd, pixs);
+ *      (4) If an existing pixd is not the same size as pixs, the
+ *          image data will be reallocated.
+ *      (5) The pixel access routines allow a trivial implementation.
+ *          However, for d < 8, it is more efficient to right-justify
+ *          each line to a 32-bit boundary and then extract bytes and
+ *          do pixel reversing.   In those cases, as in the 180 degree
+ *          rotation, we right-shift the data (if necessary) to
+ *          right-justify on the 32 bit boundary, and then read the
+ *          bytes off each raster line in reverse order, reversing
+ *          the pixels in each byte using a table.  These functions
+ *          for 1, 2 and 4 bpp were tested against the "trivial"
+ *          version (shown here for 4 bpp):
+ *              for (i = 0; i < h; i++) {
+ *                  line = data + i * wpl;
+ *                  memcpy(buffer, line, bpl);
+ *                    for (j = 0; j < w; j++) {
+ *                      val = GET_DATA_QBIT(buffer, w - 1 - j);
+ *                        SET_DATA_QBIT(line, j, val);
+ *                  }
+ *              }
+ * 
+ */ +PIX * +pixFlipLR(PIX *pixd, + PIX *pixs) +{ +l_uint8 *tab; +l_int32 w, h, d, wpl; +l_int32 extra, shift, databpl, bpl, i, j; +l_uint32 val; +l_uint32 *line, *data, *buffer; + + PROCNAME("pixFlipLR"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", + procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + switch (d) + { + case 1: + tab = makeReverseByteTab1(); + break; + case 2: + tab = makeReverseByteTab2(); + break; + case 4: + tab = makeReverseByteTab4(); + break; + default: + tab = NULL; + break; + } + + /* Possibly inplace assigning return val, so on failure return pixd */ + if ((buffer = (l_uint32 *)LEPT_CALLOC(wpl, sizeof(l_uint32))) == NULL) { + if (tab) LEPT_FREE(tab); + return (PIX *)ERROR_PTR("buffer not made", procName, pixd); + } + + bpl = 4 * wpl; + switch (d) + { + case 32: + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < w; j++) + line[j] = buffer[w - 1 - j]; + } + break; + case 16: + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < w; j++) { + val = GET_DATA_TWO_BYTES(buffer, w - 1 - j); + SET_DATA_TWO_BYTES(line, j, val); + } + } + break; + case 8: + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(buffer, w - 1 - j); + SET_DATA_BYTE(line, j, val); + } + } + break; + case 4: + extra = (w * d) & 31; + if (extra) + shift = 8 - extra / 4; + else + shift = 0; + if (shift) + rasteropHipLow(data, h, d, wpl, 0, h, shift); + + databpl = (w + 1) / 2; + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < databpl; j++) { + val = GET_DATA_BYTE(buffer, bpl - 1 - j); + SET_DATA_BYTE(line, j, tab[val]); + } + } + break; + case 2: + extra = (w * d) & 31; + if (extra) + shift = 16 - extra / 2; + else + shift = 0; + if (shift) + rasteropHipLow(data, h, d, wpl, 0, h, shift); + + databpl = (w + 3) / 4; + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < databpl; j++) { + val = GET_DATA_BYTE(buffer, bpl - 1 - j); + SET_DATA_BYTE(line, j, tab[val]); + } + } + break; + case 1: + extra = (w * d) & 31; + if (extra) + shift = 32 - extra; + else + shift = 0; + if (shift) + rasteropHipLow(data, h, d, wpl, 0, h, shift); + + databpl = (w + 7) / 8; + for (i = 0; i < h; i++) { + line = data + i * wpl; + memcpy(buffer, line, bpl); + for (j = 0; j < databpl; j++) { + val = GET_DATA_BYTE(buffer, bpl - 1 - j); + SET_DATA_BYTE(line, j, tab[val]); + } + } + break; + default: + pixDestroy(&pixd); + L_ERROR("illegal depth: %d\n", procName, d); + break; + } + + LEPT_FREE(buffer); + if (tab) LEPT_FREE(tab); + return pixd; +} + + +/*------------------------------------------------------------------* + * Top-bottom flip * + *------------------------------------------------------------------*/ +/*! + * \brief pixFlipTB() + * + * \param[in] pixd [optional]; can be null, equal to pixs, + * or different from pixs + * \param[in] pixs all depths + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This does a top-bottom flip of the image, which is
+ *          equivalent to a rotation out of the plane about a
+ *          horizontal line through the image center.
+ *      (2) There are 3 cases for input:
+ *          (a) pixd == null (creates a new pixd)
+ *          (b) pixd == pixs (in-place operation)
+ *          (c) pixd != pixs (existing pixd)
+ *      (3) For clarity, use these three patterns, respectively:
+ *          (a) pixd = pixFlipTB(NULL, pixs);
+ *          (b) pixFlipTB(pixs, pixs);
+ *          (c) pixFlipTB(pixd, pixs);
+ *      (4) If an existing pixd is not the same size as pixs, the
+ *          image data will be reallocated.
+ *      (5) This is simple and fast.  We use the memcpy function
+ *          to do all the work on aligned data, regardless of pixel
+ *          depth.
+ * 
+ */ +PIX * +pixFlipTB(PIX *pixd, + PIX *pixs) +{ +l_int32 h, d, wpl, i, k, h2, bpl; +l_uint32 *linet, *lineb; +l_uint32 *data, *buffer; + + PROCNAME("pixFlipTB"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, NULL, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", + procName, NULL); + + /* Prepare pixd for in-place operation */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + data = pixGetData(pixd); + wpl = pixGetWpl(pixd); + if ((buffer = (l_uint32 *)LEPT_CALLOC(wpl, sizeof(l_uint32))) == NULL) + return (PIX *)ERROR_PTR("buffer not made", procName, pixd); + + h2 = h / 2; + bpl = 4 * wpl; + for (i = 0, k = h - 1; i < h2; i++, k--) { + linet = data + i * wpl; + lineb = data + k * wpl; + memcpy(buffer, linet, bpl); + memcpy(linet, lineb, bpl); + memcpy(lineb, buffer, bpl); + } + + LEPT_FREE(buffer); + return pixd; +} + + +/*------------------------------------------------------------------* + * Static byte reverse tables * + *------------------------------------------------------------------*/ +/*! + * \brief makeReverseByteTab1() + * + * Notes: + * (1) This generates an 8 bit lookup table for reversing + * the order of eight 1-bit pixels. + */ +static l_uint8 * +makeReverseByteTab1(void) +{ +l_int32 i; +l_uint8 *tab; + + tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); + for (i = 0; i < 256; i++) + tab[i] = ((0x80 & i) >> 7) | + ((0x40 & i) >> 5) | + ((0x20 & i) >> 3) | + ((0x10 & i) >> 1) | + ((0x08 & i) << 1) | + ((0x04 & i) << 3) | + ((0x02 & i) << 5) | + ((0x01 & i) << 7); + return tab; +} + + +/*! + * \brief makeReverseByteTab2() + * + * Notes: + * (1) This generates an 8 bit lookup table for reversing + * the order of four 2-bit pixels. + */ +static l_uint8 * +makeReverseByteTab2(void) +{ +l_int32 i; +l_uint8 *tab; + + tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); + for (i = 0; i < 256; i++) + tab[i] = ((0xc0 & i) >> 6) | + ((0x30 & i) >> 2) | + ((0x0c & i) << 2) | + ((0x03 & i) << 6); + return tab; +} + + +/*! + * \brief makeReverseByteTab4() + * + * Notes: + * (1) This generates an 8 bit lookup table for reversing + * the order of two 4-bit pixels. + */ +static l_uint8 * +makeReverseByteTab4(void) +{ +l_int32 i; +l_uint8 *tab; + + tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); + for (i = 0; i < 256; i++) + tab[i] = ((0xf0 & i) >> 4) | ((0x0f & i) << 4); + return tab; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/rotateshear.c b/hgdriver/3rdparty/hgOCR/leptonica/rotateshear.c new file mode 100644 index 0000000..6cf7bc6 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/rotateshear.c @@ -0,0 +1,494 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file rotateshear.c + *
+ *
+ *      Shear rotation about arbitrary point using 2 and 3 shears
+ *
+ *              PIX      *pixRotateShear()
+ *              PIX      *pixRotate2Shear()
+ *              PIX      *pixRotate3Shear()
+ *
+ *      Shear rotation in-place about arbitrary point using 3 shears
+ *              l_int32   pixRotateShearIP()
+ *
+ *      Shear rotation around the image center
+ *              PIX      *pixRotateShearCenter()    (2 or 3 shears)
+ *              l_int32   pixRotateShearCenterIP()  (3 shears)
+ *
+ *  Rotation is measured in radians; clockwise rotations are positive.
+ *
+ *  Rotation by shear works on images of any depth,
+ *  including 8 bpp color paletted images and 32 bpp
+ *  rgb images.  It works by translating each src pixel
+ *  value to the appropriate pixel in the rotated dest.
+ *  For 8 bpp grayscale images, it is about 10-15x faster
+ *  than rotation by area-mapping.
+ *
+ *  This speed and flexibility comes at the following cost,
+ *  relative to area-mapped rotation:
+ *
+ *    ~  Jaggies are created on edges of straight lines
+ *
+ *    ~  For large angles, where you must use 3 shears,
+ *       there is some extra clipping from the shears.
+ *
+ *  For small angles, typically less than 0.05 radians,
+ *  rotation can be done with 2 orthogonal shears.
+ *  Two such continuous shears (as opposed to the discrete
+ *  shears on a pixel lattice that we have here) give
+ *  a rotated image that has a distortion in the lengths
+ *  of the two rotated and still-perpendicular axes.  The
+ *  length/width ratio changes by a fraction
+ *
+ *       0.5 * (angle)**2
+ *
+ *  For an angle of 0.05 radians, this is about 1 part in
+ *  a thousand.  This distortion is absent when you use
+ *  3 continuous shears with the correct angles (see below).
+ *
+ *  Of course, the image is on a discrete pixel lattice.
+ *  Rotation by shear gives an approximation to a continuous
+ *  rotation, leaving pixel jaggies at sharp boundaries.
+ *  For very small rotations, rotating from a corner gives
+ *  better sensitivity than rotating from the image center.
+ *  Here's why.  Define the shear "center" to be the line such
+ *  that the image is sheared in opposite directions on
+ *  each side of and parallel to the line.  For small
+ *  rotations there is a "dead space" on each side of the
+ *  shear center of width equal to half the shear angle,
+ *  in radians.  Thus, when the image is sheared about the center,
+ *  the dead space width equals the shear angle, but when
+ *  the image is sheared from a corner, the dead space
+ *  width is only half the shear angle.
+ *
+ *  All horizontal and vertical shears are implemented by
+ *  rasterop.  The in-place rotation uses special in-place
+ *  shears that copy rows sideways or columns vertically
+ *  without buffering, and then rewrite old pixels that are
+ *  no longer covered by sheared pixels.  For that rewriting,
+ *  you have the choice of using white or black pixels.
+ *  When not in-place, the new pix is initialized with white or black
+ *  pixels by pixSetBlackOrWhite(), which also works for cmapped pix.
+ *  But for in-place, this initialization is not possible, so
+ *  in-place shear operations on cmapped pix are not allowed.
+ *
+ *  Rotation by shear is fast and depth-independent.  However, it
+ *  does not work well for large rotation angles.  In fact, for
+ *  rotation angles greater than about 7 degrees, more pixels are
+ *  lost at the edges than when using pixRotationBySampling(), which
+ *  only loses pixels because they are rotated out of the image.
+ *  For larger rotations, use pixRotationBySampling() or, for
+ *  more accuracy when d > 1 bpp, pixRotateAM().
+ *
+ *  For small angles, when comparing the quality of rotation by
+ *  sampling and by shear, you can see that rotation by sampling
+ *  is slightly more accurate.  However, the difference in
+ *  accuracy of rotation by sampling when compared to 3-shear and
+ *  (for angles less than 2 degrees, when compared to 2-shear) is
+ *  less than 1 pixel at any point.  For very small angles, rotation by
+ *  sampling is much slower than rotation by shear.  The speed difference
+ *  depends on the pixel depth and the rotation angle.  Rotation
+ *  by shear is very fast for small angles and for small depth (esp. 1 bpp).
+ *  Rotation by sampling speed is independent of angle and relatively
+ *  more efficient for 8 and 32 bpp images.  Here are some timings
+ *  for the ratio of rotation times: (time for sampling)/ (time for shear)
+  *
+ *       depth (bpp)       ratio (2 deg)       ratio (10 deg)
+ *       -----------------------------------------------------
+ *          1                  25                  6
+ *          8                   5                  2.6
+ *          32                  1.6                1.0
+ *
+ *  In summary:
+ *    * For d == 1 and small angles, use rotation by shear.  By default
+ *      this will use 2-shear rotations, because 3-shears cause more
+ *      visible artifacts in straight lines and, for small angles, the
+ *      distortion in asperity ratio is small.
+ *    * For d > 1, shear is faster than sampling, which is faster than
+ *      area mapping.  However, area mapping gives the best results.
+ *  These results are used in selecting the rotation methods in
+ *  pixRotateShear().
+ *
+ *  There has been some work on what is called a "quasishear
+ *  rotation" ("The Quasi-Shear Rotation, Eric Andres,
+ *  DGCI 1996, pp. 307-314).  I believe they use a 3-shear
+ *  approximation to the continuous rotation, exactly as
+ *  we do here.  The approximation is due to being on
+ *  a square pixel lattice.  They also use integers to specify
+ *  the rotation angle and center offset, but that makes
+ *  little sense on a machine where you have a few GFLOPS
+ *  and only a few hundred floating point operations to do (!)
+ *  They also allow subpixel specification of the center of
+ *  rotation, which I haven't bothered with, and claim that
+ *  better results are possible if each of the 4 quadrants is
+ *  handled separately.
+ *
+ *  But the bottom line is that you are going to see shear lines when
+ *  you rotate 1 bpp images.  Although the 3-shear rotation is
+ *  mathematically exact in the limit of infinitesimal pixels, artifacts
+ *  will be evident in real images.  One might imagine using dithering
+ *  to break up the horizontal and vertical shear lines, but this
+ *  is hard with block shears, where you need to dither on the block
+ *  boundaries.  Dithering (by accumulation of 'error') with sampling
+ *  makes more sense, but I haven't tried to do this.  There is only
+ *  so much you can do with 1 bpp images!
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Angle limits: + * angle < MinAngleToRotate ==> clone + * angle > MaxTwoShearAngle ==> warning for 2-angle shears + * angle > MaxThreeShearAngle ==> warning for 3-angle shears + * angle > MaxShearAngle ==> error + */ +static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */ +static const l_float32 MaxTwoShearAngle = 0.06; /* radians; ~3 deg */ +static const l_float32 MaxThreeShearAngle = 0.35; /* radians; ~20 deg */ +static const l_float32 MaxShearAngle = 0.50; /* radians; ~29 deg */ + +/*------------------------------------------------------------------* + * Rotations about an arbitrary point * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateShear() + * + * \param[in] pixs any depth; cmap ok + * \param[in] xcen x value for which there is no horizontal shear + * \param[in] ycen y value for which there is no vertical shear + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) This rotates an image about the given point, using
+ *          either 2 or 3 shears.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) This brings in 'incolor' pixels from outside the image.
+ *      (4) For rotation angles larger than about 0.35 radians, we issue
+ *          a warning because you should probably be using another method
+ *          (either sampling or area mapping)
+ * 
+ */ +PIX * +pixRotateShear(PIX *pixs, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 incolor) +{ + PROCNAME("pixRotateShear"); + + if (!pixs) + return (PIX *)(PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + + if (L_ABS(angle) > MaxShearAngle) { + L_ERROR("%6.2f radians; too large for shear rotation\n", procName, + L_ABS(angle)); + return NULL; + } + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + + if (L_ABS(angle) <= MaxTwoShearAngle) + return pixRotate2Shear(pixs, xcen, ycen, angle, incolor); + else + return pixRotate3Shear(pixs, xcen, ycen, angle, incolor); +} + + +/*! + * \brief pixRotate2Shear() + * + * \param[in] pixs any depth; cmap ok + * \param[in] xcen, ycen center of rotation + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) This rotates the image about the given point, using the 2-shear
+ *          method.  It should only be used for angles no larger than
+ *          MaxTwoShearAngle.  For larger angles, a warning is issued.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) 2-shear rotation by a specified angle is equivalent
+ *          to the sequential transformations
+ *             x' = x + tan(angle) * (y - ycen)     for x-shear
+ *             y' = y + tan(angle) * (x - xcen)     for y-shear
+ *      (4) Computation of tan(angle) is performed within the shear operation.
+ *      (5) This brings in 'incolor' pixels from outside the image.
+ *      (6) If the image has an alpha layer, it is rotated separately by
+ *          two shears.
+ * 
+ */ +PIX * +pixRotate2Shear(PIX *pixs, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 incolor) +{ +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixRotate2Shear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + + if (L_ABS(angle) > MaxShearAngle) { + L_ERROR("%6.2f radians; too large for shear rotation\n", procName, + L_ABS(angle)); + return NULL; + } + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + if (L_ABS(angle) > MaxTwoShearAngle) + L_WARNING("%6.2f radians; large angle for 2-shear rotation\n", + procName, L_ABS(angle)); + + if ((pix1 = pixHShear(NULL, pixs, ycen, angle, incolor)) == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + pixd = pixVShear(NULL, pix1, xcen, angle, incolor); + pixDestroy(&pix1); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + /* L_BRING_IN_WHITE brings in opaque for the alpha component */ + pix2 = pixRotate2Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + return pixd; +} + + +/*! + * \brief pixRotate3Shear() + * + * \param[in] pixs any depth; cmap ok + * \param[in] xcen, ycen center of rotation + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) This rotates the image about the given point, using the 3-shear
+ *          method.  It should only be used for angles smaller than
+ *          MaxThreeShearAngle.  For larger angles, a warning is issued.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) 3-shear rotation by a specified angle is equivalent
+ *          to the sequential transformations
+ *            y' = y + tan(angle/2) * (x - xcen)     for first y-shear
+ *            x' = x + sin(angle) * (y - ycen)       for x-shear
+ *            y' = y + tan(angle/2) * (x - xcen)     for second y-shear
+ *      (4) Computation of tan(angle) is performed in the shear operations.
+ *      (5) This brings in 'incolor' pixels from outside the image.
+ *      (6) If the image has an alpha layer, it is rotated separately by
+ *          two shears.
+ *      (7) The algorithm was published by Alan Paeth: "A Fast Algorithm
+ *          for General Raster Rotation," Graphics Interface '86,
+ *          pp. 77-81, May 1986.  A description of the method, along with
+ *          an implementation, can be found in Graphics Gems, p. 179,
+ *          edited by Andrew Glassner, published by Academic Press, 1990.
+ * 
+ */ +PIX * +pixRotate3Shear(PIX *pixs, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 incolor) +{ +l_float32 hangle; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixRotate3Shear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + + if (L_ABS(angle) > MaxShearAngle) { + L_ERROR("%6.2f radians; too large for shear rotation\n", procName, + L_ABS(angle)); + return NULL; + } + if (L_ABS(angle) < MinAngleToRotate) + return pixClone(pixs); + if (L_ABS(angle) > MaxThreeShearAngle) { + L_WARNING("%6.2f radians; large angle for 3-shear rotation\n", + procName, L_ABS(angle)); + } + + hangle = atan(sin(angle)); + if ((pixd = pixVShear(NULL, pixs, xcen, angle / 2., incolor)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + if ((pix1 = pixHShear(NULL, pixd, ycen, hangle, incolor)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); + } + pixVShear(pixd, pix1, xcen, angle / 2., incolor); + pixDestroy(&pix1); + + if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) { + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + /* L_BRING_IN_WHITE brings in opaque for the alpha component */ + pix2 = pixRotate3Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + } + return pixd; +} + + +/*------------------------------------------------------------------* + * Rotations in-place about an arbitrary point * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateShearIP() + * + * \param[in] pixs any depth; no cmap + * \param[in] xcen, ycen center of rotation + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This does an in-place rotation of the image about the
+ *          specified point, using the 3-shear method.  It should only
+ *          be used for angles smaller than MaxThreeShearAngle.
+ *          For larger angles, a warning is issued.
+ *      (2) A positive angle gives a clockwise rotation.
+ *      (3) 3-shear rotation by a specified angle is equivalent
+ *          to the sequential transformations
+ *            y' = y + tan(angle/2) * (x - xcen)      for first y-shear
+ *            x' = x + sin(angle) * (y - ycen)        for x-shear
+ *            y' = y + tan(angle/2) * (x - xcen)      for second y-shear
+ *      (4) Computation of tan(angle) is performed in the shear operations.
+ *      (5) This brings in 'incolor' pixels from outside the image.
+ *      (6) The pix cannot be colormapped, because the in-place operation
+ *          only blits in 0 or 1 bits, not an arbitrary colormap index.
+ * 
+ */ +l_ok +pixRotateShearIP(PIX *pixs, + l_int32 xcen, + l_int32 ycen, + l_float32 angle, + l_int32 incolor) +{ +l_float32 hangle; + + PROCNAME("pixRotateShearIP"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid value for incolor", procName, 1); + if (pixGetColormap(pixs) != NULL) + return ERROR_INT("pixs is colormapped", procName, 1); + + if (angle == 0.0) + return 0; + if (L_ABS(angle) > MaxThreeShearAngle) { + L_WARNING("%6.2f radians; large angle for in-place 3-shear rotation\n", + procName, L_ABS(angle)); + } + + hangle = atan(sin(angle)); + pixHShearIP(pixs, ycen, angle / 2., incolor); + pixVShearIP(pixs, xcen, hangle, incolor); + pixHShearIP(pixs, ycen, angle / 2., incolor); + return 0; +} + + +/*------------------------------------------------------------------* + * Rotations about the image center * + *------------------------------------------------------------------*/ +/*! + * \brief pixRotateShearCenter() + * + * \param[in] pixs any depth; cmap ok + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return pixd, or NULL on error + */ +PIX * +pixRotateShearCenter(PIX *pixs, + l_float32 angle, + l_int32 incolor) +{ + PROCNAME("pixRotateShearCenter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + return pixRotateShear(pixs, pixGetWidth(pixs) / 2, + pixGetHeight(pixs) / 2, angle, incolor); +} + + +/*! + * \brief pixRotateShearCenterIP() + * + * \param[in] pixs any depth; no cmap + * \param[in] angle radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK + * \return 0 if OK, 1 on error + */ +l_ok +pixRotateShearCenterIP(PIX *pixs, + l_float32 angle, + l_int32 incolor) +{ + PROCNAME("pixRotateShearCenterIP"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + return pixRotateShearIP(pixs, pixGetWidth(pixs) / 2, + pixGetHeight(pixs) / 2, angle, incolor); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/runlength.c b/hgdriver/3rdparty/hgOCR/leptonica/runlength.c new file mode 100644 index 0000000..43c8542 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/runlength.c @@ -0,0 +1,811 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file runlength.c + *
+ *
+ *     Label pixels by membership in runs
+ *           PIX         *pixStrokeWidthTransform()
+ *           static PIX  *pixFindMinRunsOrthogonal()
+ *           PIX         *pixRunlengthTransform()
+ *
+ *     Find runs along horizontal and vertical lines
+ *           l_int32      pixFindHorizontalRuns()
+ *           l_int32      pixFindVerticalRuns()
+ *
+ *     Find max runs along horizontal and vertical lines
+ *           l_int32      pixFindMaxRuns()
+ *           l_int32      pixFindMaxHorizontalRunOnLine()
+ *           l_int32      pixFindMaxVerticalRunOnLine()
+ *
+ *     Compute runlength-to-membership transform on a line
+ *           l_int32      runlengthMembershipOnLine()
+ *
+ *     Make byte position LUT
+ *           l_int32      makeMSBitLocTab()
+ *
+ *  Here we're handling runs of either black or white pixels on 1 bpp
+ *  images.  The directions of the runs in the stroke width transform
+ *  are selectable from given sets of angles.  Most of the other runs
+ *  are oriented either horizontally along the raster lines or
+ *  vertically along pixel columns.
+ * 
+ */ + +#include +#include +#include "allheaders.h" + +static PIX *pixFindMinRunsOrthogonal(PIX *pixs, l_float32 angle, l_int32 depth); + + +/*-----------------------------------------------------------------------* + * Label pixels by membership in runs * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixStrokeWidthTransform() + * + * \param[in] pixs 1 bpp + * \param[in] color 0 for white runs, 1 for black runs + * \param[in] depth of pixd: 8 or 16 bpp + * \param[in] nangles 2, 4, 6 or 8 + * \return pixd 8 or 16 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) The dest Pix is 8 or 16 bpp, with the pixel values
+ *          equal to the stroke width in which it is a member.
+ *          The values are clipped to the max pixel value if necessary.
+ *      (2) %color determines if we're labelling white or black strokes.
+ *      (3) A pixel that is not a member of the chosen color gets
+ *          value 0; it belongs to a width of length 0 of the
+ *          chosen color.
+ *      (4) This chooses, for each dest pixel, the minimum of sets
+ *          of runlengths through each pixel.  Here are the sets:
+ *            nangles    increment          set
+ *            -------    ---------    --------------------------------
+ *               2          90       {0, 90}
+ *               4          45       {0, 45, 90, 135}
+ *               6          30       {0, 30, 60, 90, 120, 150}
+ *               8          22.5     {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5}
+ *      (5) Runtime scales linearly with (%nangles - 2).
+ * 
+ */ +PIX * +pixStrokeWidthTransform(PIX *pixs, + l_int32 color, + l_int32 depth, + l_int32 nangles) +{ +l_float32 angle, pi; +PIX *pixh, *pixv, *pixt, *pixg1, *pixg2, *pixg3, *pixg4; + + PROCNAME("pixStrokeWidthTransform"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (depth != 8 && depth != 16) + return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL); + if (nangles != 2 && nangles != 4 && nangles != 6 && nangles != 8) + return (PIX *)ERROR_PTR("nangles not in {2,4,6,8}", procName, NULL); + + /* Use fg runs for evaluation */ + if (color == 0) + pixt = pixInvert(NULL, pixs); + else + pixt = pixClone(pixs); + + /* Find min length at 0 and 90 degrees */ + pixh = pixRunlengthTransform(pixt, 1, L_HORIZONTAL_RUNS, depth); + pixv = pixRunlengthTransform(pixt, 1, L_VERTICAL_RUNS, depth); + pixg1 = pixMinOrMax(NULL, pixh, pixv, L_CHOOSE_MIN); + pixDestroy(&pixh); + pixDestroy(&pixv); + + pixg2 = pixg3 = pixg4 = NULL; + pi = 3.1415926535; + if (nangles == 4 || nangles == 8) { + /* Find min length at +45 and -45 degrees */ + angle = pi / 4.0; + pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth); + } + + if (nangles == 6) { + /* Find min length at +30 and -60 degrees */ + angle = pi / 6.0; + pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth); + + /* Find min length at +60 and -30 degrees */ + angle = pi / 3.0; + pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth); + } + + if (nangles == 8) { + /* Find min length at +22.5 and -67.5 degrees */ + angle = pi / 8.0; + pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth); + + /* Find min length at +67.5 and -22.5 degrees */ + angle = 3.0 * pi / 8.0; + pixg4 = pixFindMinRunsOrthogonal(pixt, angle, depth); + } + pixDestroy(&pixt); + + if (nangles > 2) + pixMinOrMax(pixg1, pixg1, pixg2, L_CHOOSE_MIN); + if (nangles > 4) + pixMinOrMax(pixg1, pixg1, pixg3, L_CHOOSE_MIN); + if (nangles > 6) + pixMinOrMax(pixg1, pixg1, pixg4, L_CHOOSE_MIN); + pixDestroy(&pixg2); + pixDestroy(&pixg3); + pixDestroy(&pixg4); + return pixg1; +} + + +/*! + * \brief pixFindMinRunsOrthogonal() + * + * \param[in] pixs 1 bpp + * \param[in] angle in radians + * \param[in] depth of pixd: 8 or 16 bpp + * \return pixd 8 or 16 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This computes, for each fg pixel in pixs, the minimum of
+ *          the runlengths going through that pixel in two orthogonal
+ *          directions: at %angle and at (90 + %angle).
+ *      (2) We use rotation by shear because the forward and backward
+ *          rotations by the same angle are exact inverse operations.
+ *          As a result, the nonzero pixels in pixd correspond exactly
+ *          to the fg pixels in pixs.  This is not the case with
+ *          sampled rotation, due to spatial quantization.  Nevertheless,
+ *          the result suffers from lack of exact correspondence
+ *          between original and rotated pixels, also due to spatial
+ *          quantization, causing some boundary pixels to be
+ *          shifted from bg to fg or v.v.
+ * 
+ */ +static PIX * +pixFindMinRunsOrthogonal(PIX *pixs, + l_float32 angle, + l_int32 depth) +{ +l_int32 w, h, diag, xoff, yoff; +PIX *pixb, *pixr, *pixh, *pixv, *pixg1, *pixg2, *pixd; +BOX *box; + + PROCNAME("pixFindMinRunsOrthogonal"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + /* Rasterop into the center of a sufficiently large image + * so we don't lose pixels for any rotation angle. */ + pixGetDimensions(pixs, &w, &h, NULL); + diag = (l_int32)(sqrt((l_float64)(w * w + h * h)) + 2.5); + xoff = (diag - w) / 2; + yoff = (diag - h) / 2; + pixb = pixCreate(diag, diag, 1); + pixRasterop(pixb, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0); + + /* Rotate about the 'center', get the min of orthogonal transforms, + * rotate back, and crop the part corresponding to pixs. */ + pixr = pixRotateShear(pixb, diag / 2, diag / 2, angle, L_BRING_IN_WHITE); + pixh = pixRunlengthTransform(pixr, 1, L_HORIZONTAL_RUNS, depth); + pixv = pixRunlengthTransform(pixr, 1, L_VERTICAL_RUNS, depth); + pixg1 = pixMinOrMax(NULL, pixh, pixv, L_CHOOSE_MIN); + pixg2 = pixRotateShear(pixg1, diag / 2, diag / 2, -angle, L_BRING_IN_WHITE); + box = boxCreate(xoff, yoff, w, h); + pixd = pixClipRectangle(pixg2, box, NULL); + + pixDestroy(&pixb); + pixDestroy(&pixr); + pixDestroy(&pixh); + pixDestroy(&pixv); + pixDestroy(&pixg1); + pixDestroy(&pixg2); + boxDestroy(&box); + return pixd; +} + + +/*! + * \brief pixRunlengthTransform() + * + * \param[in] pixs 1 bpp + * \param[in] color 0 for white runs, 1 for black runs + * \param[in] direction L_HORIZONTAL_RUNS, L_VERTICAL_RUNS + * \param[in] depth 8 or 16 bpp + * \return pixd 8 or 16 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) The dest Pix is 8 or 16 bpp, with the pixel values
+ *          equal to the runlength in which it is a member.
+ *          The length is clipped to the max pixel value if necessary.
+ *      (2) %color determines if we're labelling white or black runs.
+ *      (3) A pixel that is not a member of the chosen color gets
+ *          value 0; it belongs to a run of length 0 of the
+ *          chosen color.
+ *      (4) To convert for maximum dynamic range, either linear or
+ *          log, use pixMaxDynamicRange().
+ * 
+ */ +PIX * +pixRunlengthTransform(PIX *pixs, + l_int32 color, + l_int32 direction, + l_int32 depth) +{ +l_int32 i, j, w, h, wpld, bufsize, maxsize, n; +l_int32 *start, *end, *buffer; +l_uint32 *datad, *lined; +PIX *pixt, *pixd; + + PROCNAME("pixRunlengthTransform"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (depth != 8 && depth != 16) + return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (direction == L_HORIZONTAL_RUNS) + maxsize = 1 + w / 2; + else if (direction == L_VERTICAL_RUNS) + maxsize = 1 + h / 2; + else + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + bufsize = L_MAX(w, h); + if (bufsize > 1000000) { + L_ERROR("largest image dimension = %d; too big\n", procName, bufsize); + return NULL; + } + + if ((pixd = pixCreate(w, h, depth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + start = (l_int32 *)LEPT_CALLOC(maxsize, sizeof(l_int32)); + end = (l_int32 *)LEPT_CALLOC(maxsize, sizeof(l_int32)); + buffer = (l_int32 *)LEPT_CALLOC(bufsize, sizeof(l_int32)); + + /* Use fg runs for evaluation */ + if (color == 0) + pixt = pixInvert(NULL, pixs); + else + pixt = pixClone(pixs); + + if (direction == L_HORIZONTAL_RUNS) { + for (i = 0; i < h; i++) { + pixFindHorizontalRuns(pixt, i, start, end, &n); + runlengthMembershipOnLine(buffer, w, depth, start, end, n); + lined = datad + i * wpld; + if (depth == 8) { + for (j = 0; j < w; j++) + SET_DATA_BYTE(lined, j, buffer[j]); + } else { /* depth == 16 */ + for (j = 0; j < w; j++) + SET_DATA_TWO_BYTES(lined, j, buffer[j]); + } + } + } else { /* L_VERTICAL_RUNS */ + for (j = 0; j < w; j++) { + pixFindVerticalRuns(pixt, j, start, end, &n); + runlengthMembershipOnLine(buffer, h, depth, start, end, n); + if (depth == 8) { + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + SET_DATA_BYTE(lined, j, buffer[i]); + } + } else { /* depth == 16 */ + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + SET_DATA_TWO_BYTES(lined, j, buffer[i]); + } + } + } + } + + pixDestroy(&pixt); + LEPT_FREE(start); + LEPT_FREE(end); + LEPT_FREE(buffer); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Find runs along horizontal and vertical lines * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixFindHorizontalRuns() + * + * \param[in] pix 1 bpp + * \param[in] y line to traverse + * \param[in] xstart returns array of start positions for fg runs + * \param[in] xend returns array of end positions for fg runs + * \param[out] pn the number of runs found + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds foreground horizontal runs on a single scanline.
+ *      (2) To find background runs, use pixInvert() before applying
+ *          this function.
+ *      (3) %xstart and %xend arrays are input.  They should be
+ *          of size w/2 + 1 to insure that they can hold
+ *          the maximum number of runs in the raster line.
+ * 
+ */ +l_ok +pixFindHorizontalRuns(PIX *pix, + l_int32 y, + l_int32 *xstart, + l_int32 *xend, + l_int32 *pn) +{ +l_int32 inrun; /* boolean */ +l_int32 index, w, h, d, j, wpl, val; +l_uint32 *line; + + PROCNAME("pixFindHorizontalRuns"); + + if (!pn) + return ERROR_INT("&n not defined", procName, 1); + *pn = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1) + return ERROR_INT("pix not 1 bpp", procName, 1); + if (y < 0 || y >= h) + return ERROR_INT("y not in [0 ... h - 1]", procName, 1); + if (!xstart) + return ERROR_INT("xstart not defined", procName, 1); + if (!xend) + return ERROR_INT("xend not defined", procName, 1); + + wpl = pixGetWpl(pix); + line = pixGetData(pix) + y * wpl; + + inrun = FALSE; + index = 0; + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(line, j); + if (!inrun) { + if (val) { + xstart[index] = j; + inrun = TRUE; + } + } else { + if (!val) { + xend[index++] = j - 1; + inrun = FALSE; + } + } + } + + /* Finish last run if necessary */ + if (inrun) + xend[index++] = w - 1; + + *pn = index; + return 0; +} + + +/*! + * \brief pixFindVerticalRuns() + * + * \param[in] pix 1 bpp + * \param[in] x line to traverse + * \param[in] ystart returns array of start positions for fg runs + * \param[in] yend returns array of end positions for fg runs + * \param[out] pn the number of runs found + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds foreground vertical runs on a single scanline.
+ *      (2) To find background runs, use pixInvert() before applying
+ *          this function.
+ *      (3) %ystart and %yend arrays are input.  They should be
+ *          of size h/2 + 1 to insure that they can hold
+ *          the maximum number of runs in the raster line.
+ * 
+ */ +l_ok +pixFindVerticalRuns(PIX *pix, + l_int32 x, + l_int32 *ystart, + l_int32 *yend, + l_int32 *pn) +{ +l_int32 inrun; /* boolean */ +l_int32 index, w, h, d, i, wpl, val; +l_uint32 *data, *line; + + PROCNAME("pixFindVerticalRuns"); + + if (!pn) + return ERROR_INT("&n not defined", procName, 1); + *pn = 0; + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1) + return ERROR_INT("pix not 1 bpp", procName, 1); + if (x < 0 || x >= w) + return ERROR_INT("x not in [0 ... w - 1]", procName, 1); + if (!ystart) + return ERROR_INT("ystart not defined", procName, 1); + if (!yend) + return ERROR_INT("yend not defined", procName, 1); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + + inrun = FALSE; + index = 0; + for (i = 0; i < h; i++) { + line = data + i * wpl; + val = GET_DATA_BIT(line, x); + if (!inrun) { + if (val) { + ystart[index] = i; + inrun = TRUE; + } + } else { + if (!val) { + yend[index++] = i - 1; + inrun = FALSE; + } + } + } + + /* Finish last run if necessary */ + if (inrun) + yend[index++] = h - 1; + + *pn = index; + return 0; +} + + +/*-----------------------------------------------------------------------* + * Find max runs along horizontal and vertical lines * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixFindMaxRuns() + * + * \param[in] pix 1 bpp + * \param[in] direction L_HORIZONTAL_RUNS or L_VERTICAL_RUNS + * \param[out] pnastart [optional] start locations of longest runs + * \return na of lengths of runs, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds the longest foreground runs by row or column
+ *      (2) To find background runs, use pixInvert() before applying
+ *          this function.
+ * 
+ */ +NUMA * +pixFindMaxRuns(PIX *pix, + l_int32 direction, + NUMA **pnastart) +{ +l_int32 w, h, i, start, size; +NUMA *nasize; + + PROCNAME("pixFindMaxRuns"); + + if (pnastart) *pnastart = NULL; + if (direction != L_HORIZONTAL_RUNS && direction != L_VERTICAL_RUNS) + return (NUMA *)ERROR_PTR("direction invalid", procName, NULL); + if (!pix || pixGetDepth(pix) != 1) + return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); + + pixGetDimensions(pix, &w, &h, NULL); + nasize = numaCreate(w); + if (pnastart) *pnastart = numaCreate(w); + if (direction == L_HORIZONTAL_RUNS) { + for (i = 0; i < h; i++) { + pixFindMaxHorizontalRunOnLine(pix, i, &start, &size); + numaAddNumber(nasize, size); + if (pnastart) numaAddNumber(*pnastart, start); + } + } else { /* vertical scans */ + for (i = 0; i < w; i++) { + pixFindMaxVerticalRunOnLine(pix, i, &start, &size); + numaAddNumber(nasize, size); + if (pnastart) numaAddNumber(*pnastart, start); + } + } + + return nasize; +} + + +/*! + * \brief pixFindMaxHorizontalRunOnLine() + * + * \param[in] pix 1 bpp + * \param[in] y line to traverse + * \param[out] pxstart [optional] start position + * \param[out] psize the size of the run + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds the longest foreground horizontal run on a scanline.
+ *      (2) To find background runs, use pixInvert() before applying
+ *          this function.
+ * 
+ */ +l_ok +pixFindMaxHorizontalRunOnLine(PIX *pix, + l_int32 y, + l_int32 *pxstart, + l_int32 *psize) +{ +l_int32 inrun; /* boolean */ +l_int32 w, h, j, wpl, val, maxstart, maxsize, length, start; +l_uint32 *line; + + PROCNAME("pixFindMaxHorizontalRunOnLine"); + + if (pxstart) *pxstart = 0; + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + *psize = 0; + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp", procName, 1); + pixGetDimensions(pix, &w, &h, NULL); + if (y < 0 || y >= h) + return ERROR_INT("y not in [0 ... h - 1]", procName, 1); + + wpl = pixGetWpl(pix); + line = pixGetData(pix) + y * wpl; + inrun = FALSE; + start = 0; + maxstart = 0; + maxsize = 0; + for (j = 0; j < w; j++) { + val = GET_DATA_BIT(line, j); + if (!inrun) { + if (val) { + start = j; + inrun = TRUE; + } + } else if (!val) { /* run just ended */ + length = j - start; + if (length > maxsize) { + maxsize = length; + maxstart = start; + } + inrun = FALSE; + } + } + + if (inrun) { /* a run has continued to the end of the row */ + length = j - start; + if (length > maxsize) { + maxsize = length; + maxstart = start; + } + } + if (pxstart) *pxstart = maxstart; + *psize = maxsize; + return 0; +} + + +/*! + * \brief pixFindMaxVerticalRunOnLine() + * + * \param[in] pix 1 bpp + * \param[in] x column to traverse + * \param[out] pystart [optional] start position + * \param[out] psize the size of the run + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This finds the longest foreground vertical run on a scanline.
+ *      (2) To find background runs, use pixInvert() before applying
+ *          this function.
+ * 
+ */ +l_ok +pixFindMaxVerticalRunOnLine(PIX *pix, + l_int32 x, + l_int32 *pystart, + l_int32 *psize) +{ +l_int32 inrun; /* boolean */ +l_int32 w, h, i, wpl, val, maxstart, maxsize, length, start; +l_uint32 *data, *line; + + PROCNAME("pixFindMaxVerticalRunOnLine"); + + if (pystart) *pystart = 0; + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + *psize = 0; + if (!pix || pixGetDepth(pix) != 1) + return ERROR_INT("pix not defined or not 1 bpp", procName, 1); + pixGetDimensions(pix, &w, &h, NULL); + if (x < 0 || x >= w) + return ERROR_INT("x not in [0 ... w - 1]", procName, 1); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + inrun = FALSE; + start = 0; + maxstart = 0; + maxsize = 0; + for (i = 0; i < h; i++) { + line = data + i * wpl; + val = GET_DATA_BIT(line, x); + if (!inrun) { + if (val) { + start = i; + inrun = TRUE; + } + } else if (!val) { /* run just ended */ + length = i - start; + if (length > maxsize) { + maxsize = length; + maxstart = start; + } + inrun = FALSE; + } + } + + if (inrun) { /* a run has continued to the end of the column */ + length = i - start; + if (length > maxsize) { + maxsize = length; + maxstart = start; + } + } + if (pystart) *pystart = maxstart; + *psize = maxsize; + return 0; +} + + +/*-----------------------------------------------------------------------* + * Compute runlength-to-membership transform on a line * + *-----------------------------------------------------------------------*/ +/*! + * \brief runlengthMembershipOnLine() + * + * \param[in] buffer into which full line of data is placed + * \param[in] size full size of line; w or h + * \param[in] depth 8 or 16 bpp + * \param[in] start array of start positions for fg runs + * \param[in] end array of end positions for fg runs + * \param[in] n the number of runs + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Converts a set of runlengths into a buffer of
+ *          runlength membership values.
+ *      (2) Initialization of the array gives pixels that are
+ *          not within a run the value 0.
+ * 
+ */ +l_ok +runlengthMembershipOnLine(l_int32 *buffer, + l_int32 size, + l_int32 depth, + l_int32 *start, + l_int32 *end, + l_int32 n) +{ +l_int32 i, j, first, last, diff, max; + + PROCNAME("runlengthMembershipOnLine"); + + if (!buffer) + return ERROR_INT("buffer not defined", procName, 1); + if (!start) + return ERROR_INT("start not defined", procName, 1); + if (!end) + return ERROR_INT("end not defined", procName, 1); + + if (depth == 8) + max = 0xff; + else /* depth == 16 */ + max = 0xffff; + + memset(buffer, 0, 4 * size); + for (i = 0; i < n; i++) { + first = start[i]; + last = end[i]; + diff = last - first + 1; + diff = L_MIN(diff, max); + for (j = first; j <= last; j++) + buffer[j] = diff; + } + + return 0; +} + + +/*-----------------------------------------------------------------------* + * Make byte position LUT * + *-----------------------------------------------------------------------*/ +/*! + * \brief makeMSBitLocTab() + * + * \param[in] bitval either 0 or 1 + * \return table: for an input byte, the MS bit location, starting at 0 + * with the MSBit in the byte, or NULL on error. + * + *
+ * Notes:
+ *      (1) If %bitval == 1, it finds the leftmost ON pixel in a byte;
+ *          otherwise if %bitval == 0, it finds the leftmost OFF pixel.
+ *      (2) If there are no pixels of the indicated color in the byte,
+ *          this returns 8.
+ * 
+ */ +l_int32 * +makeMSBitLocTab(l_int32 bitval) +{ +l_int32 i, j; +l_int32 *tab; +l_uint8 byte, mask; + + tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); + for (i = 0; i < 256; i++) { + byte = (l_uint8)i; + if (bitval == 0) + byte = ~byte; + tab[i] = 8; + mask = 0x80; + for (j = 0; j < 8; j++) { + if (byte & mask) { + tab[i] = j; + break; + } + mask >>= 1; + } + } + return tab; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sarray1.c b/hgdriver/3rdparty/hgOCR/leptonica/sarray1.c new file mode 100644 index 0000000..7716071 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sarray1.c @@ -0,0 +1,1965 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file sarray1.c + *
+ *
+ *      Create/Destroy/Copy
+ *          SARRAY    *sarrayCreate()
+ *          SARRAY    *sarrayCreateInitialized()
+ *          SARRAY    *sarrayCreateWordsFromString()
+ *          SARRAY    *sarrayCreateLinesFromString()
+ *          void      *sarrayDestroy()
+ *          SARRAY    *sarrayCopy()
+ *          SARRAY    *sarrayClone()
+ *
+ *      Add/Remove string
+ *          l_int32    sarrayAddString()
+ *          static l_int32  sarrayExtendArray()
+ *          char      *sarrayRemoveString()
+ *          l_int32    sarrayReplaceString()
+ *          l_int32    sarrayClear()
+ *
+ *      Accessors
+ *          l_int32    sarrayGetCount()
+ *          char     **sarrayGetArray()
+ *          char      *sarrayGetString()
+ *          l_int32    sarrayGetRefcount()
+ *          l_int32    sarrayChangeRefcount()
+ *
+ *      Conversion back to string
+ *          char      *sarrayToString()
+ *          char      *sarrayToStringRange()
+ *
+ *      Join 2 sarrays
+ *          l_int32    sarrayJoin()
+ *          l_int32    sarrayAppendRange()
+ *
+ *      Pad an sarray to be the same size as another sarray
+ *          l_int32    sarrayPadToSameSize()
+ *
+ *      Convert word sarray to (formatted) line sarray
+ *          SARRAY    *sarrayConvertWordsToLines()
+ *
+ *      Split string on separator list
+ *          SARRAY    *sarraySplitString()
+ *
+ *      Filter sarray
+ *          SARRAY    *sarraySelectBySubstring()
+ *          SARRAY    *sarraySelectByRange()
+ *          l_int32    sarrayParseRange()
+ *
+ *      Serialize for I/O
+ *          SARRAY    *sarrayRead()
+ *          SARRAY    *sarrayReadStream()
+ *          SARRAY    *sarrayReadMem()
+ *          l_int32    sarrayWrite()
+ *          l_int32    sarrayWriteStream()
+ *          l_int32    sarrayWriteMem()
+ *          l_int32    sarrayAppend()
+ *
+ *      Directory filenames
+ *          SARRAY    *getNumberedPathnamesInDirectory()
+ *          SARRAY    *getSortedPathnamesInDirectory()
+ *          SARRAY    *convertSortedToNumberedPathnames()
+ *          SARRAY    *getFilenamesInDirectory()
+ *
+ *      These functions are important for efficient manipulation
+ *      of string data, and they have found widespread use in
+ *      leptonica.  For example:
+ *         (1) to generate text files: e.g., PostScript and PDF
+ *             wrappers around sets of images
+ *         (2) to parse text files: e.g., extracting prototypes
+ *             from the source to generate allheaders.h
+ *         (3) to generate code for compilation: e.g., the fast
+ *             dwa code for arbitrary structuring elements.
+ *
+ *      Comments on usage:
+ *
+ *          The user is responsible for correctly disposing of strings
+ *          that have been extracted from sarrays.  In the following,
+ *          "str_not_owned" means the returned handle does not own the string,
+ *          and "str_owned" means the returned handle owns the string.
+ *            - To extract a string from an Sarray in order to inspect it
+ *              or to make a copy of it later, get a handle to it:
+ *                  copyflag = L_NOCOPY.
+ *              In this case, you must neither free the string nor put it
+ *              directly in another array:
+ *                 str-not-owned = sarrayGetString(sa, index, L_NOCOPY);
+ *            - To extract a copy of a string from an Sarray, use:
+ *                 str-owned = sarrayGetString(sa, index, L_COPY);
+ *            ~ To insert a string that is in one array into another
+ *              array (always leaving the first array intact), there are
+ *              two options:
+ *                 (1) use copyflag = L_COPY to make an immediate copy,
+ *                     which you then add to the second array by insertion:
+ *                       str-owned = sarrayGetString(sa, index, L_COPY);
+ *                       sarrayAddString(sa, str-owned, L_INSERT);
+ *                 (2) use copyflag = L_NOCOPY to get another handle to
+ *                     the string; you then add a copy of it to the
+ *                     second string array:
+ *                       str-not-owned = sarrayGetString(sa, index, L_NOCOPY);
+ *                       sarrayAddString(sa, str-not-owned, L_COPY).
+ *              sarrayAddString() transfers ownership to the Sarray, so never
+ *              use L_INSERT if the string is owned by another array.
+ *
+ *              In all cases, when you use copyflag = L_COPY to extract
+ *              a string from an array, you must either free it
+ *              or insert it in an array that will be freed later.
+ * 
+ */ + +#include +#ifndef _WIN32 +#include /* unix only */ +#include +#include /* needed for realpath() */ +#include /* needed for realpath() */ +#endif /* ! _WIN32 */ +#include "allheaders.h" + +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */ + + /* Static functions */ +static l_int32 sarrayExtendArray(SARRAY *sa); + + +/*--------------------------------------------------------------------------* + * String array create/destroy/copy/extend * + *--------------------------------------------------------------------------*/ +/*! + * \brief sarrayCreate() + * + * \param[in] n size of string ptr array to be alloc'd; use 0 for default + * \return sarray, or NULL on error + */ +SARRAY * +sarrayCreate(l_int32 n) +{ +SARRAY *sa; + + PROCNAME("sarrayCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + sa = (SARRAY *)LEPT_CALLOC(1, sizeof(SARRAY)); + if ((sa->array = (char **)LEPT_CALLOC(n, sizeof(char *))) == NULL) { + sarrayDestroy(&sa); + return (SARRAY *)ERROR_PTR("ptr array not made", procName, NULL); + } + + sa->nalloc = n; + sa->n = 0; + sa->refcount = 1; + return sa; +} + + +/*! + * \brief sarrayCreateInitialized() + * + * \param[in] n size of string ptr array to be alloc'd + * \param[in] initstr string to be initialized on the full array + * \return sarray, or NULL on error + */ +SARRAY * +sarrayCreateInitialized(l_int32 n, + const char *initstr) +{ +l_int32 i; +SARRAY *sa; + + PROCNAME("sarrayCreateInitialized"); + + if (n <= 0) + return (SARRAY *)ERROR_PTR("n must be > 0", procName, NULL); + if (!initstr) + return (SARRAY *)ERROR_PTR("initstr not defined", procName, NULL); + + sa = sarrayCreate(n); + for (i = 0; i < n; i++) + sarrayAddString(sa, initstr, L_COPY); + return sa; +} + + +/*! + * \brief sarrayCreateWordsFromString() + * + * \param[in] string + * \return sarray, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds the number of word substrings, creates an sarray
+ *          of this size, and puts copies of each substring into the sarray.
+ * 
+ */ +SARRAY * +sarrayCreateWordsFromString(const char *string) +{ +char separators[] = " \n\t"; +l_int32 i, nsub, size, inword; +SARRAY *sa; + + PROCNAME("sarrayCreateWordsFromString"); + + if (!string) + return (SARRAY *)ERROR_PTR("textstr not defined", procName, NULL); + + /* Find the number of words */ + size = strlen(string); + nsub = 0; + inword = FALSE; + for (i = 0; i < size; i++) { + if (inword == FALSE && + (string[i] != ' ' && string[i] != '\t' && string[i] != '\n')) { + inword = TRUE; + nsub++; + } else if (inword == TRUE && + (string[i] == ' ' || string[i] == '\t' || string[i] == '\n')) { + inword = FALSE; + } + } + + if ((sa = sarrayCreate(nsub)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + sarraySplitString(sa, string, separators); + + return sa; +} + + +/*! + * \brief sarrayCreateLinesFromString() + * + * \param[in] string + * \param[in] blankflag 0 to exclude blank lines; 1 to include + * \return sarray, or NULL on error + * + *
+ * Notes:
+ *      (1) This finds the number of line substrings, each of which
+ *          ends with a newline, and puts a copy of each substring
+ *          in a new sarray.
+ *      (2) The newline characters are removed from each substring.
+ * 
+ */ +SARRAY * +sarrayCreateLinesFromString(const char *string, + l_int32 blankflag) +{ +l_int32 i, nsub, size, startptr; +char *cstring, *substring; +SARRAY *sa; + + PROCNAME("sarrayCreateLinesFromString"); + + if (!string) + return (SARRAY *)ERROR_PTR("textstr not defined", procName, NULL); + + /* Find the number of lines */ + size = strlen(string); + nsub = 0; + for (i = 0; i < size; i++) { + if (string[i] == '\n') + nsub++; + } + + if ((sa = sarrayCreate(nsub)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + + if (blankflag) { /* keep blank lines as null strings */ + /* Make a copy for munging */ + if ((cstring = stringNew(string)) == NULL) { + sarrayDestroy(&sa); + return (SARRAY *)ERROR_PTR("cstring not made", procName, NULL); + } + /* We'll insert nulls like strtok */ + startptr = 0; + for (i = 0; i < size; i++) { + if (cstring[i] == '\n') { + cstring[i] = '\0'; + if (i > 0 && cstring[i - 1] == '\r') + cstring[i - 1] = '\0'; /* also remove Windows CR */ + if ((substring = stringNew(cstring + startptr)) == NULL) { + sarrayDestroy(&sa); + LEPT_FREE(cstring); + return (SARRAY *)ERROR_PTR("substring not made", + procName, NULL); + } + sarrayAddString(sa, substring, L_INSERT); +/* fprintf(stderr, "substring = %s\n", substring); */ + startptr = i + 1; + } + } + if (startptr < size) { /* no newline at end of last line */ + if ((substring = stringNew(cstring + startptr)) == NULL) { + sarrayDestroy(&sa); + LEPT_FREE(cstring); + return (SARRAY *)ERROR_PTR("substring not made", + procName, NULL); + } + sarrayAddString(sa, substring, L_INSERT); +/* fprintf(stderr, "substring = %s\n", substring); */ + } + LEPT_FREE(cstring); + } else { /* remove blank lines; use strtok */ + sarraySplitString(sa, string, "\r\n"); + } + + return sa; +} + + +/*! + * \brief sarrayDestroy() + * + * \param[in,out] psa will be set to null before returning + * \return void + * + *
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the sarray.
+ *      (2) Always nulls the input ptr.
+ * 
+ */ +void +sarrayDestroy(SARRAY **psa) +{ +l_int32 i; +SARRAY *sa; + + PROCNAME("sarrayDestroy"); + + if (psa == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + if ((sa = *psa) == NULL) + return; + + sarrayChangeRefcount(sa, -1); + if (sarrayGetRefcount(sa) <= 0) { + if (sa->array) { + for (i = 0; i < sa->n; i++) { + if (sa->array[i]) + LEPT_FREE(sa->array[i]); + } + LEPT_FREE(sa->array); + } + LEPT_FREE(sa); + } + + *psa = NULL; + return; +} + + +/*! + * \brief sarrayCopy() + * + * \param[in] sa string array + * \return copy of sarray, or NULL on error + */ +SARRAY * +sarrayCopy(SARRAY *sa) +{ +l_int32 i; +SARRAY *csa; + + PROCNAME("sarrayCopy"); + + if (!sa) + return (SARRAY *)ERROR_PTR("sa not defined", procName, NULL); + + if ((csa = sarrayCreate(sa->nalloc)) == NULL) + return (SARRAY *)ERROR_PTR("csa not made", procName, NULL); + + for (i = 0; i < sa->n; i++) + sarrayAddString(csa, sa->array[i], L_COPY); + + return csa; +} + + +/*! + * \brief sarrayClone() + * + * \param[in] sa string array + * \return ptr to same sarray, or NULL on error + */ +SARRAY * +sarrayClone(SARRAY *sa) +{ + PROCNAME("sarrayClone"); + + if (!sa) + return (SARRAY *)ERROR_PTR("sa not defined", procName, NULL); + sarrayChangeRefcount(sa, 1); + return sa; +} + + +/*! + * \brief sarrayAddString() + * + * \param[in] sa string array + * \param[in] string string to be added + * \param[in] copyflag L_INSERT, L_NOCOPY or L_COPY + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See usage comments at the top of this file.  L_INSERT is
+ *          equivalent to L_NOCOPY.
+ * 
+ */ +l_ok +sarrayAddString(SARRAY *sa, + const char *string, + l_int32 copyflag) +{ +l_int32 n; + + PROCNAME("sarrayAddString"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!string) + return ERROR_INT("string not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_NOCOPY && copyflag != L_COPY) + return ERROR_INT("invalid copyflag", procName, 1); + + n = sarrayGetCount(sa); + if (n >= sa->nalloc) + sarrayExtendArray(sa); + + if (copyflag == L_COPY) + sa->array[n] = stringNew(string); + else /* L_INSERT or L_NOCOPY */ + sa->array[n] = (char *)string; + sa->n++; + return 0; +} + + +/*! + * \brief sarrayExtendArray() + * + * \param[in] sa string array + * \return 0 if OK, 1 on error + */ +static l_int32 +sarrayExtendArray(SARRAY *sa) +{ + PROCNAME("sarrayExtendArray"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + if ((sa->array = (char **)reallocNew((void **)&sa->array, + sizeof(char *) * sa->nalloc, + 2 * sizeof(char *) * sa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + sa->nalloc *= 2; + return 0; +} + + +/*! + * \brief sarrayRemoveString() + * + * \param[in] sa string array + * \param[in] index of string within sarray + * \return removed string, or NULL on error + */ +char * +sarrayRemoveString(SARRAY *sa, + l_int32 index) +{ +char *string; +char **array; +l_int32 i, n, nalloc; + + PROCNAME("sarrayRemoveString"); + + if (!sa) + return (char *)ERROR_PTR("sa not defined", procName, NULL); + + if ((array = sarrayGetArray(sa, &nalloc, &n)) == NULL) + return (char *)ERROR_PTR("array not returned", procName, NULL); + + if (index < 0 || index >= n) + return (char *)ERROR_PTR("array index out of bounds", procName, NULL); + + string = array[index]; + + /* If removed string is not at end of array, shift + * to fill in, maintaining original ordering. + * Note: if we didn't care about the order, we could + * put the last string array[n - 1] directly into the hole. */ + for (i = index; i < n - 1; i++) + array[i] = array[i + 1]; + + sa->n--; + return string; +} + + +/*! + * \brief sarrayReplaceString() + * + * \param[in] sa string array + * \param[in] index of string within sarray to be replaced + * \param[in] newstr string to replace existing one + * \param[in] copyflag L_INSERT, L_COPY + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This destroys an existing string and replaces it with
+ *          the new string or a copy of it.
+ *      (2) By design, an sarray is always compacted, so there are
+ *          never any holes (null ptrs) in the ptr array up to the
+ *          current count.
+ * 
+ */ +l_ok +sarrayReplaceString(SARRAY *sa, + l_int32 index, + char *newstr, + l_int32 copyflag) +{ +char *str; +l_int32 n; + + PROCNAME("sarrayReplaceString"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + n = sarrayGetCount(sa); + if (index < 0 || index >= n) + return ERROR_INT("array index out of bounds", procName, 1); + if (!newstr) + return ERROR_INT("newstr not defined", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY) + return ERROR_INT("invalid copyflag", procName, 1); + + LEPT_FREE(sa->array[index]); + if (copyflag == L_INSERT) + str = newstr; + else /* L_COPY */ + str = stringNew(newstr); + sa->array[index] = str; + return 0; +} + + +/*! + * \brief sarrayClear() + * + * \param[in] sa string array + * \return 0 if OK; 1 on error + */ +l_ok +sarrayClear(SARRAY *sa) +{ +l_int32 i; + + PROCNAME("sarrayClear"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + for (i = 0; i < sa->n; i++) { /* free strings and null ptrs */ + LEPT_FREE(sa->array[i]); + sa->array[i] = NULL; + } + sa->n = 0; + return 0; +} + + +/*----------------------------------------------------------------------* + * Accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayGetCount() + * + * \param[in] sa string array + * \return count, or 0 if no strings or on error + */ +l_int32 +sarrayGetCount(SARRAY *sa) +{ + PROCNAME("sarrayGetCount"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 0); + return sa->n; +} + + +/*! + * \brief sarrayGetArray() + * + * \param[in] sa string array + * \param[out] pnalloc [optional] number allocated string ptrs + * \param[out] pn [optional] number allocated strings + * \return ptr to string array, or NULL on error + * + *
+ * Notes:
+ *      (1) Caution: the returned array is not a copy, so caller
+ *          must not destroy it!
+ * 
+ */ +char ** +sarrayGetArray(SARRAY *sa, + l_int32 *pnalloc, + l_int32 *pn) +{ +char **array; + + PROCNAME("sarrayGetArray"); + + if (!sa) + return (char **)ERROR_PTR("sa not defined", procName, NULL); + + array = sa->array; + if (pnalloc) *pnalloc = sa->nalloc; + if (pn) *pn = sa->n; + + return array; +} + + +/*! + * \brief sarrayGetString() + * + * \param[in] sa string array + * \param[in] index to the index-th string + * \param[in] copyflag L_NOCOPY or L_COPY + * \return string, or NULL on error + * + *
+ * Notes:
+ *      (1) See usage comments at the top of this file.
+ *      (2) To get a pointer to the string itself, use L_NOCOPY.
+ *          To get a copy of the string, use L_COPY.
+ * 
+ */ +char * +sarrayGetString(SARRAY *sa, + l_int32 index, + l_int32 copyflag) +{ + PROCNAME("sarrayGetString"); + + if (!sa) + return (char *)ERROR_PTR("sa not defined", procName, NULL); + if (index < 0 || index >= sa->n) + return (char *)ERROR_PTR("index not valid", procName, NULL); + if (copyflag != L_NOCOPY && copyflag != L_COPY) + return (char *)ERROR_PTR("invalid copyflag", procName, NULL); + + if (copyflag == L_NOCOPY) + return sa->array[index]; + else /* L_COPY */ + return stringNew(sa->array[index]); +} + + +/*! + * \brief sarrayGetRefCount() + * + * \param[in] sa string array + * \return refcount, or UNDEF on error + */ +l_int32 +sarrayGetRefcount(SARRAY *sa) +{ + PROCNAME("sarrayGetRefcount"); + + if (!sa) + return ERROR_INT("sa not defined", procName, UNDEF); + return sa->refcount; +} + + +/*! + * \brief sarrayChangeRefCount() + * + * \param[in] sa string array + * \param[in] delta change to be applied + * \return 0 if OK, 1 on error + */ +l_ok +sarrayChangeRefcount(SARRAY *sa, + l_int32 delta) +{ + PROCNAME("sarrayChangeRefcount"); + + if (!sa) + return ERROR_INT("sa not defined", procName, UNDEF); + sa->refcount += delta; + return 0; +} + + +/*----------------------------------------------------------------------* + * Conversion to string * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayToString() + * + * \param[in] sa string array + * \param[in] addnlflag flag: 0 adds nothing to each substring + * 1 adds '\n' to each substring + * 2 adds ' ' to each substring + * \return dest string, or NULL on error + * + *
+ * Notes:
+ *      (1) Concatenates all the strings in the sarray, preserving
+ *          all white space.
+ *      (2) If addnlflag != 0, adds either a '\n' or a ' ' after
+ *          each substring.
+ *      (3) This function was NOT implemented as:
+ *            for (i = 0; i < n; i++)
+ *                     strcat(dest, sarrayGetString(sa, i, L_NOCOPY));
+ *          Do you see why?
+ * 
+ */ +char * +sarrayToString(SARRAY *sa, + l_int32 addnlflag) +{ + PROCNAME("sarrayToString"); + + if (!sa) + return (char *)ERROR_PTR("sa not defined", procName, NULL); + + return sarrayToStringRange(sa, 0, 0, addnlflag); +} + + +/*! + * \brief sarrayToStringRange() + * + * \param[in] sa string array + * \param[in] first index of first string to use; starts with 0 + * \param[in] nstrings number of strings to append into the result; use + * 0 to append to the end of the sarray + * \param[in] addnlflag flag: 0 adds nothing to each substring + * 1 adds '\n' to each substring + * 2 adds ' ' to each substring + * \return dest string, or NULL on error + * + *
+ * Notes:
+ *      (1) Concatenates the specified strings inthe sarray, preserving
+ *          all white space.
+ *      (2) If addnlflag != 0, adds either a '\n' or a ' ' after
+ *          each substring.
+ *      (3) If the sarray is empty, this returns a string with just
+ *          the character corresponding to %addnlflag.
+ * 
+ */ +char * +sarrayToStringRange(SARRAY *sa, + l_int32 first, + l_int32 nstrings, + l_int32 addnlflag) +{ +char *dest, *src, *str; +l_int32 n, i, last, size, index, len; + + PROCNAME("sarrayToStringRange"); + + if (!sa) + return (char *)ERROR_PTR("sa not defined", procName, NULL); + if (addnlflag != 0 && addnlflag != 1 && addnlflag != 2) + return (char *)ERROR_PTR("invalid addnlflag", procName, NULL); + + n = sarrayGetCount(sa); + + /* Empty sa; return char corresponding to addnlflag only */ + if (n == 0) { + if (first == 0) { + if (addnlflag == 0) + return stringNew(""); + if (addnlflag == 1) + return stringNew("\n"); + else /* addnlflag == 2) */ + return stringNew(" "); + } else { + return (char *)ERROR_PTR("first not valid", procName, NULL); + } + } + + if (first < 0 || first >= n) + return (char *)ERROR_PTR("first not valid", procName, NULL); + if (nstrings == 0 || (nstrings > n - first)) + nstrings = n - first; /* no overflow */ + last = first + nstrings - 1; + + size = 0; + for (i = first; i <= last; i++) { + if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) + return (char *)ERROR_PTR("str not found", procName, NULL); + size += strlen(str) + 2; + } + + if ((dest = (char *)LEPT_CALLOC(size + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("dest not made", procName, NULL); + + index = 0; + for (i = first; i <= last; i++) { + src = sarrayGetString(sa, i, L_NOCOPY); + len = strlen(src); + memcpy(dest + index, src, len); + index += len; + if (addnlflag == 1) { + dest[index] = '\n'; + index++; + } else if (addnlflag == 2) { + dest[index] = ' '; + index++; + } + } + + return dest; +} + + +/*----------------------------------------------------------------------* + * Join 2 sarrays * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayJoin() + * + * \param[in] sa1 to be added to + * \param[in] sa2 append to sa1 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Copies of the strings in sarray2 are added to sarray1.
+ * 
+ */ +l_ok +sarrayJoin(SARRAY *sa1, + SARRAY *sa2) +{ +char *str; +l_int32 n, i; + + PROCNAME("sarrayJoin"); + + if (!sa1) + return ERROR_INT("sa1 not defined", procName, 1); + if (!sa2) + return ERROR_INT("sa2 not defined", procName, 1); + + n = sarrayGetCount(sa2); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa2, i, L_NOCOPY); + sarrayAddString(sa1, str, L_COPY); + } + + return 0; +} + + +/*! + * \brief sarrayAppendRange() + * + * \param[in] sa1 to be added to + * \param[in] sa2 append specified range of strings in sa2 to sa1 + * \param[in] start index of first string of sa2 to append + * \param[in] end index of last string of sa2 to append; + * -1 to append to end of array + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Copies of the strings in sarray2 are added to sarray1.
+ *      (2) The [start ... end] range is truncated if necessary.
+ *      (3) Use end == -1 to append to the end of sa2.
+ * 
+ */ +l_ok +sarrayAppendRange(SARRAY *sa1, + SARRAY *sa2, + l_int32 start, + l_int32 end) +{ +char *str; +l_int32 n, i; + + PROCNAME("sarrayAppendRange"); + + if (!sa1) + return ERROR_INT("sa1 not defined", procName, 1); + if (!sa2) + return ERROR_INT("sa2 not defined", procName, 1); + + if (start < 0) + start = 0; + n = sarrayGetCount(sa2); + if (end < 0 || end >= n) + end = n - 1; + if (start > end) + return ERROR_INT("start > end", procName, 1); + + for (i = start; i <= end; i++) { + str = sarrayGetString(sa2, i, L_NOCOPY); + sarrayAddString(sa1, str, L_COPY); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Pad an sarray to be the same size as another sarray * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayPadToSameSize() + * + * \param[in] sa1, sa2 + * \param[in] padstring + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If two sarrays have different size, this adds enough
+ *          instances of %padstring to the smaller so that they are
+ *          the same size.  It is useful when two or more sarrays
+ *          are being sequenced in parallel, and it is necessary to
+ *          find a valid string at each index.
+ * 
+ */ +l_ok +sarrayPadToSameSize(SARRAY *sa1, + SARRAY *sa2, + const char *padstring) +{ +l_int32 i, n1, n2; + + PROCNAME("sarrayPadToSameSize"); + + if (!sa1 || !sa2) + return ERROR_INT("both sa1 and sa2 not defined", procName, 1); + + n1 = sarrayGetCount(sa1); + n2 = sarrayGetCount(sa2); + if (n1 < n2) { + for (i = n1; i < n2; i++) + sarrayAddString(sa1, padstring, L_COPY); + } else if (n1 > n2) { + for (i = n2; i < n1; i++) + sarrayAddString(sa2, padstring, L_COPY); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Convert word sarray to line sarray * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayConvertWordsToLines() + * + * \param[in] sa sa of individual words + * \param[in] linesize max num of chars in each line + * \return saout sa of formatted lines, or NULL on error + * + *
+ * Notes:
+ *      (1) This is useful for re-typesetting text to a specific maximum
+ *          line length.  The individual words in the input sarray
+ *          are concatenated into textlines.  An input word string of zero
+ *          length is taken to be a paragraph separator.  Each time
+ *          such a string is found, the current line is ended and
+ *          a new line is also produced that contains just the
+ *          string of zero length "".  When the output sarray
+ *          of lines is eventually converted to a string with newlines
+ *          typically appended to each line string, the empty
+ *          strings are just converted to newlines, producing the visible
+ *          paragraph separation.
+ *      (2) What happens when a word is larger than linesize?
+ *          We write it out as a single line anyway!  Words preceding
+ *          or following this long word are placed on lines preceding
+ *          or following the line with the long word.  Why this choice?
+ *          Long "words" found in text documents are typically URLs, and
+ *          it's often desirable not to put newlines in the middle of a URL.
+ *          The text display program e.g., text editor will typically
+ *          wrap the long "word" to fit in the window.
+ * 
+ */ +SARRAY * +sarrayConvertWordsToLines(SARRAY *sa, + l_int32 linesize) +{ +char *wd, *strl; +char emptystring[] = ""; +l_int32 n, i, len, totlen; +SARRAY *sal, *saout; + + PROCNAME("sarrayConvertWordsToLines"); + + if (!sa) + return (SARRAY *)ERROR_PTR("sa not defined", procName, NULL); + + saout = sarrayCreate(0); + n = sarrayGetCount(sa); + totlen = 0; + sal = NULL; + for (i = 0; i < n; i++) { + if (!sal) + sal = sarrayCreate(0); + wd = sarrayGetString(sa, i, L_NOCOPY); + len = strlen(wd); + if (len == 0) { /* end of paragraph: end line & insert blank line */ + if (totlen > 0) { + strl = sarrayToString(sal, 2); + sarrayAddString(saout, strl, L_INSERT); + } + sarrayAddString(saout, emptystring, L_COPY); + sarrayDestroy(&sal); + totlen = 0; + } else if (totlen == 0 && len + 1 > linesize) { /* long word! */ + sarrayAddString(saout, wd, L_COPY); /* copy to one line */ + } else if (totlen + len + 1 > linesize) { /* end line & start new */ + strl = sarrayToString(sal, 2); + sarrayAddString(saout, strl, L_INSERT); + sarrayDestroy(&sal); + sal = sarrayCreate(0); + sarrayAddString(sal, wd, L_COPY); + totlen = len + 1; + } else { /* add to current line */ + sarrayAddString(sal, wd, L_COPY); + totlen += len + 1; + } + } + if (totlen > 0) { /* didn't end with blank line; output last line */ + strl = sarrayToString(sal, 2); + sarrayAddString(saout, strl, L_INSERT); + sarrayDestroy(&sal); + } + + return saout; +} + + +/*----------------------------------------------------------------------* + * Split string on separator list * + *----------------------------------------------------------------------*/ +/* + * \brief sarraySplitString() + * + * \param[in] sa to append to; typically empty initially + * \param[in] str string to split; not changed + * \param[in] separators characters that split input string + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This uses strtokSafe().  See the notes there in utils.c.
+ * 
+ */ +l_int32 +sarraySplitString(SARRAY *sa, + const char *str, + const char *separators) +{ +char *cstr, *substr, *saveptr; + + PROCNAME("sarraySplitString"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!str) + return ERROR_INT("str not defined", procName, 1); + if (!separators) + return ERROR_INT("separators not defined", procName, 1); + + cstr = stringNew(str); /* preserves const-ness of input str */ + saveptr = NULL; + substr = strtokSafe(cstr, separators, &saveptr); + if (substr) + sarrayAddString(sa, substr, L_INSERT); + while ((substr = strtokSafe(NULL, separators, &saveptr))) + sarrayAddString(sa, substr, L_INSERT); + LEPT_FREE(cstr); + + return 0; +} + + +/*----------------------------------------------------------------------* + * Filter sarray * + *----------------------------------------------------------------------*/ +/*! + * \brief sarraySelectBySubstring() + * + * \param[in] sain input sarray + * \param[in] substr [optional] substring for matching; can be NULL + * \return saout output sarray, filtered with substring or NULL on error + * + *
+ * Notes:
+ *      (1) This selects all strings in sain that have substr as a substring.
+ *          Note that we can't use strncmp() because we're looking for
+ *          a match to the substring anywhere within each filename.
+ *      (2) If substr == NULL, returns a copy of the sarray.
+ * 
+ */ +SARRAY * +sarraySelectBySubstring(SARRAY *sain, + const char *substr) +{ +char *str; +l_int32 n, i, offset, found; +SARRAY *saout; + + PROCNAME("sarraySelectBySubstring"); + + if (!sain) + return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); + + n = sarrayGetCount(sain); + if (!substr || n == 0) + return sarrayCopy(sain); + + saout = sarrayCreate(n); + for (i = 0; i < n; i++) { + str = sarrayGetString(sain, i, L_NOCOPY); + arrayFindSequence((l_uint8 *)str, strlen(str), (l_uint8 *)substr, + strlen(substr), &offset, &found); + if (found) + sarrayAddString(saout, str, L_COPY); + } + + return saout; +} + + +/*! + * \brief sarraySelectByRange() + * + * \param[in] sain input sarray + * \param[in] first index of first string to be selected + * \param[in] last index of last string to be selected; + * use 0 to go to the end of the sarray + * \return saout output sarray, or NULL on error + * + *
+ * Notes:
+ *      (1) This makes %saout consisting of copies of all strings in %sain
+ *          in the index set [first ... last].  Use %last == 0 to get all
+ *          strings from %first to the last string in the sarray.
+ * 
+ */ +SARRAY * +sarraySelectByRange(SARRAY *sain, + l_int32 first, + l_int32 last) +{ +char *str; +l_int32 n, i; +SARRAY *saout; + + PROCNAME("sarraySelectByRange"); + + if (!sain) + return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); + if (first < 0) first = 0; + n = sarrayGetCount(sain); + if (last <= 0) last = n - 1; + if (last >= n) { + L_WARNING("last > n - 1; setting to n - 1\n", procName); + last = n - 1; + } + if (first > last) + return (SARRAY *)ERROR_PTR("first must be >= last", procName, NULL); + + saout = sarrayCreate(0); + for (i = first; i <= last; i++) { + str = sarrayGetString(sain, i, L_COPY); + sarrayAddString(saout, str, L_INSERT); + } + + return saout; +} + + +/*! + * \brief sarrayParseRange() + * + * \param[in] sa input sarray + * \param[in] start index to start range search + * \param[out] pactualstart index of actual start; may be > 'start' + * \param[out] pend index of end + * \param[out] pnewstart index of start of next range + * \param[in] substr substring for matching at beginning of string + * \param[in] loc byte offset within the string for the pattern; + * use -1 if the location does not matter. + * \return 0 if valid range found; 1 otherwise + * + *
+ * Notes:
+ *      (1) This finds the range of the next set of strings in SA,
+ *          beginning the search at 'start', that does NOT have
+ *          the substring 'substr' either at the indicated location
+ *          in the string or anywhere in the string.  The input
+ *          variable 'loc' is the specified offset within the string;
+ *          use -1 to indicate 'anywhere in the string'.
+ *      (2) Always check the return value to verify that a valid range
+ *          was found.
+ *      (3) If a valid range is not found, the values of actstart,
+ *          end and newstart are all set to the size of sa.
+ *      (4) If this is the last valid range, newstart returns the value n.
+ *          In use, this should be tested before calling the function.
+ *      (5) Usage example.  To find all the valid ranges in a file
+ *          where the invalid lines begin with two dashes, copy each
+ *          line in the file to a string in an sarray, and do:
+ *             start = 0;
+ *             while (!sarrayParseRange(sa, start, &actstart, &end, &start,
+ *                    "--", 0))
+ *                 fprintf(stderr, "start = %d, end = %d\n", actstart, end);
+ * 
+ */ +l_int32 +sarrayParseRange(SARRAY *sa, + l_int32 start, + l_int32 *pactualstart, + l_int32 *pend, + l_int32 *pnewstart, + const char *substr, + l_int32 loc) +{ +char *str; +l_int32 n, i, offset, found; + + PROCNAME("sarrayParseRange"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!pactualstart || !pend || !pnewstart) + return ERROR_INT("not all range addresses defined", procName, 1); + n = sarrayGetCount(sa); + *pactualstart = *pend = *pnewstart = n; + if (!substr) + return ERROR_INT("substr not defined", procName, 1); + + /* Look for the first string without the marker */ + if (start < 0 || start >= n) + return 1; + for (i = start; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + arrayFindSequence((l_uint8 *)str, strlen(str), (l_uint8 *)substr, + strlen(substr), &offset, &found); + if (loc < 0) { + if (!found) break; + } else { + if (!found || offset != loc) break; + } + } + start = i; + if (i == n) /* couldn't get started */ + return 1; + + /* Look for the last string without the marker */ + *pactualstart = start; + for (i = start + 1; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + arrayFindSequence((l_uint8 *)str, strlen(str), (l_uint8 *)substr, + strlen(substr), &offset, &found); + if (loc < 0) { + if (found) break; + } else { + if (found && offset == loc) break; + } + } + *pend = i - 1; + start = i; + if (i == n) /* no further range */ + return 0; + + /* Look for the first string after *pend without the marker. + * This will start the next run of strings, if it exists. */ + for (i = start; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + arrayFindSequence((l_uint8 *)str, strlen(str), (l_uint8 *)substr, + strlen(substr), &offset, &found); + if (loc < 0) { + if (!found) break; + } else { + if (!found || offset != loc) break; + } + } + if (i < n) + *pnewstart = i; + + return 0; +} + + +/*----------------------------------------------------------------------* + * Serialize for I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayRead() + * + * \param[in] filename + * \return sarray, or NULL on error + */ +SARRAY * +sarrayRead(const char *filename) +{ +FILE *fp; +SARRAY *sa; + + PROCNAME("sarrayRead"); + + if (!filename) + return (SARRAY *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (SARRAY *)ERROR_PTR("stream not opened", procName, NULL); + sa = sarrayReadStream(fp); + fclose(fp); + if (!sa) + return (SARRAY *)ERROR_PTR("sa not read", procName, NULL); + return sa; +} + + +/*! + * \brief sarrayReadStream() + * + * \param[in] fp file stream + * \return sarray, or NULL on error + * + *
+ * Notes:
+ *      (1) We store the size of each string along with the string.
+ *          The limit on the number of strings is 2^24.
+ *          The limit on the size of any string is 2^30 bytes.
+ *      (2) This allows a string to have embedded newlines.  By reading
+ *          the entire string, as determined by its size, we are
+ *          not affected by any number of embedded newlines.
+ * 
+ */ +SARRAY * +sarrayReadStream(FILE *fp) +{ +char *stringbuf; +l_int32 i, n, size, index, bufsize, version, ignore, success; +SARRAY *sa; + + PROCNAME("sarrayReadStream"); + + if (!fp) + return (SARRAY *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nSarray Version %d\n", &version) != 1) + return (SARRAY *)ERROR_PTR("not an sarray file", procName, NULL); + if (version != SARRAY_VERSION_NUMBER) + return (SARRAY *)ERROR_PTR("invalid sarray version", procName, NULL); + if (fscanf(fp, "Number of strings = %d\n", &n) != 1) + return (SARRAY *)ERROR_PTR("error on # strings", procName, NULL); + if (n > (1 << 24)) + return (SARRAY *)ERROR_PTR("more than 2^24 strings!", procName, NULL); + + success = TRUE; + if ((sa = sarrayCreate(n)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + bufsize = 512 + 1; + stringbuf = (char *)LEPT_CALLOC(bufsize, sizeof(char)); + + for (i = 0; i < n; i++) { + /* Get the size of the stored string */ + if ((fscanf(fp, "%d[%d]:", &index, &size) != 2) || (size > (1 << 30))) { + success = FALSE; + L_ERROR("error on string size\n", procName); + goto cleanup; + } + /* Expand the string buffer if necessary */ + if (size > bufsize - 5) { + LEPT_FREE(stringbuf); + bufsize = (l_int32)(1.5 * size); + stringbuf = (char *)LEPT_CALLOC(bufsize, sizeof(char)); + } + /* Read the stored string, plus leading spaces and trailing \n */ + if (fread(stringbuf, 1, size + 3, fp) != size + 3) { + success = FALSE; + L_ERROR("error reading string\n", procName); + goto cleanup; + } + /* Remove the \n that was added by sarrayWriteStream() */ + stringbuf[size + 2] = '\0'; + /* Copy it in, skipping the 2 leading spaces */ + sarrayAddString(sa, stringbuf + 2, L_COPY); + } + ignore = fscanf(fp, "\n"); + +cleanup: + LEPT_FREE(stringbuf); + if (!success) sarrayDestroy(&sa); + return sa; +} + + +/*! + * \brief sarrayReadMem() + * + * \param[in] data serialization in ascii + * \param[in] size of data; can use strlen to get it + * \return sarray, or NULL on error + */ +SARRAY * +sarrayReadMem(const l_uint8 *data, + size_t size) +{ +FILE *fp; +SARRAY *sa; + + PROCNAME("sarrayReadMem"); + + if (!data) + return (SARRAY *)ERROR_PTR("data not defined", procName, NULL); + if ((fp = fopenReadFromMemory(data, size)) == NULL) + return (SARRAY *)ERROR_PTR("stream not opened", procName, NULL); + + sa = sarrayReadStream(fp); + fclose(fp); + if (!sa) L_ERROR("sarray not read\n", procName); + return sa; +} + + +/*! + * \brief sarrayWrite() + * + * \param[in] filename + * \param[in] sa string array + * \return 0 if OK; 1 on error + */ +l_ok +sarrayWrite(const char *filename, + SARRAY *sa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("sarrayWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "w")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = sarrayWriteStream(fp, sa); + fclose(fp); + if (ret) + return ERROR_INT("sa not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief sarrayWriteStream() + * + * \param[in] fp file stream + * \param[in] sa string array + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This appends a '\n' to each string, which is stripped
+ *          off by sarrayReadStream().
+ * 
+ */ +l_ok +sarrayWriteStream(FILE *fp, + SARRAY *sa) +{ +l_int32 i, n, len; + + PROCNAME("sarrayWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + n = sarrayGetCount(sa); + fprintf(fp, "\nSarray Version %d\n", SARRAY_VERSION_NUMBER); + fprintf(fp, "Number of strings = %d\n", n); + for (i = 0; i < n; i++) { + len = strlen(sa->array[i]); + fprintf(fp, " %d[%d]: %s\n", i, len, sa->array[i]); + } + fprintf(fp, "\n"); + + return 0; +} + + +/*! + * \brief sarrayWriteMem() + * + * \param[out] pdata data of serialized sarray; ascii + * \param[out] psize size of returned data + * \param[in] sa + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Serializes a sarray in memory and puts the result in a buffer.
+ * 
+ */ +l_ok +sarrayWriteMem(l_uint8 **pdata, + size_t *psize, + SARRAY *sa) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("sarrayWriteMem"); + + if (pdata) *pdata = NULL; + if (psize) *psize = 0; + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + +#if HAVE_FMEMOPEN + if ((fp = open_memstream((char **)pdata, psize)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = sarrayWriteStream(fp, sa); +#else + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); + #endif /* _WIN32 */ + ret = sarrayWriteStream(fp, sa); + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); +#endif /* HAVE_FMEMOPEN */ + fclose(fp); + return ret; +} + + +/*! + * \brief sarrayAppend() + * + * \param[in] filename + * \param[in] sa + * \return 0 if OK; 1 on error + */ +l_ok +sarrayAppend(const char *filename, + SARRAY *sa) +{ +FILE *fp; + + PROCNAME("sarrayAppend"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "a")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + if (sarrayWriteStream(fp, sa)) { + fclose(fp); + return ERROR_INT("sa not appended to stream", procName, 1); + } + + fclose(fp); + return 0; +} + + +/*---------------------------------------------------------------------* + * Directory filenames * + *---------------------------------------------------------------------*/ +/*! + * \brief getNumberedPathnamesInDirectory() + * + * \param[in] dirname directory name + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] numpre number of characters in name before number + * \param[in] numpost number of characters in name after the number, + * up to a dot before an extension + * \param[in] maxnum only consider page numbers up to this value + * \return sarray of numbered pathnames, or NULL on error + * + *
+ * Notes:
+ *      (1) Returns the full pathnames of the numbered filenames in
+ *          the directory.  The number in the filename is the index
+ *          into the sarray.  For indices for which there are no filenames,
+ *          an empty string ("") is placed into the sarray.
+ *          This makes reading numbered files very simple.  For example,
+ *          the image whose filename includes number N can be retrieved using
+ *               pixReadIndexed(sa, N);
+ *      (2) If %substr is not NULL, only filenames that contain
+ *          the substring can be included.  If %substr is NULL,
+ *          all matching filenames are used.
+ *      (3) If no numbered files are found, it returns an empty sarray,
+ *          with no initialized strings.
+ *      (4) It is assumed that the page number is contained within
+ *          the basename (the filename without directory or extension).
+ *          %numpre is the number of characters in the basename
+ *          preceding the actual page number; %numpost is the number
+ *          following the page number, up to either the end of the
+ *          basename or a ".", whichever comes first.
+ *      (5) This is useful when all filenames contain numbers that are
+ *          not necessarily consecutive.  0-padding is not required.
+ *      (6) To use a O(n) matching algorithm, the largest page number
+ *          is found and two internal arrays of this size are created.
+ *          This maximum is constrained not to exceed %maxsum,
+ *          to make sure that an unrealistically large number is not
+ *          accidentally used to determine the array sizes.
+ * 
+ */ +SARRAY * +getNumberedPathnamesInDirectory(const char *dirname, + const char *substr, + l_int32 numpre, + l_int32 numpost, + l_int32 maxnum) +{ +l_int32 nfiles; +SARRAY *sa, *saout; + + PROCNAME("getNumberedPathnamesInDirectory"); + + if (!dirname) + return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + if ((nfiles = sarrayGetCount(sa)) == 0) { + sarrayDestroy(&sa); + return sarrayCreate(1); + } + + saout = convertSortedToNumberedPathnames(sa, numpre, numpost, maxnum); + sarrayDestroy(&sa); + return saout; +} + + +/*! + * \brief getSortedPathnamesInDirectory() + * + * \param[in] dirname directory name + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] first 0-based + * \param[in] nfiles use 0 for all to the end + * \return sarray of sorted pathnames, or NULL on error + * + *
+ * Notes:
+ *      (1) Use %substr to filter filenames in the directory.  If
+ *          %substr == NULL, this takes all files.
+ *      (2) The files in the directory, after optional filtering by
+ *          the substring, are lexically sorted in increasing order.
+ *          Use %first and %nfiles to select a contiguous set of files.
+ *      (3) The full pathnames are returned for the requested sequence.
+ *          If no files are found after filtering, returns an empty sarray.
+ * 
+ */ +SARRAY * +getSortedPathnamesInDirectory(const char *dirname, + const char *substr, + l_int32 first, + l_int32 nfiles) +{ +char *fname, *fullname; +l_int32 i, n, last; +SARRAY *sa, *safiles, *saout; + + PROCNAME("getSortedPathnamesInDirectory"); + + if (!dirname) + return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); + + if ((sa = getFilenamesInDirectory(dirname)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + safiles = sarraySelectBySubstring(sa, substr); + sarrayDestroy(&sa); + n = sarrayGetCount(safiles); + if (n == 0) { + L_WARNING("no files found\n", procName); + return safiles; + } + + sarraySort(safiles, safiles, L_SORT_INCREASING); + + first = L_MIN(L_MAX(first, 0), n - 1); + if (nfiles == 0) + nfiles = n - first; + last = L_MIN(first + nfiles - 1, n - 1); + + saout = sarrayCreate(last - first + 1); + for (i = first; i <= last; i++) { + fname = sarrayGetString(safiles, i, L_NOCOPY); + fullname = pathJoin(dirname, fname); + sarrayAddString(saout, fullname, L_INSERT); + } + + sarrayDestroy(&safiles); + return saout; +} + + +/*! + * \brief convertSortedToNumberedPathnames() + * + * \param[in] sa sorted pathnames including zero-padded integers + * \param[in] numpre number of characters in name before number + * \param[in] numpost number of characters in name after the number, + * up to a dot before an extension + * \param[in] maxnum only consider page numbers up to this value + * \return sarray of numbered pathnames, or NULL on error + * + *
+ * Notes:
+ *      (1) Typically, numpre = numpost = 0; e.g., when the filename
+ *          just has a number followed by an optional extension.
+ * 
+ */ +SARRAY * +convertSortedToNumberedPathnames(SARRAY *sa, + l_int32 numpre, + l_int32 numpost, + l_int32 maxnum) +{ +char *fname, *str; +l_int32 i, nfiles, num, index; +SARRAY *saout; + + PROCNAME("convertSortedToNumberedPathnames"); + + if (!sa) + return (SARRAY *)ERROR_PTR("sa not defined", procName, NULL); + if ((nfiles = sarrayGetCount(sa)) == 0) + return sarrayCreate(1); + + /* Find the last file in the sorted array that has a number + * that (a) matches the count pattern and (b) does not + * exceed %maxnum. %maxnum sets an upper limit on the size + * of the sarray. */ + num = 0; + for (i = nfiles - 1; i >= 0; i--) { + fname = sarrayGetString(sa, i, L_NOCOPY); + num = extractNumberFromFilename(fname, numpre, numpost); + if (num < 0) continue; + num = L_MIN(num + 1, maxnum); + break; + } + + if (num <= 0) /* none found */ + return sarrayCreate(1); + + /* Insert pathnames into the output sarray. + * Ignore numbers that are out of the range of sarray. */ + saout = sarrayCreateInitialized(num, ""); + for (i = 0; i < nfiles; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + index = extractNumberFromFilename(fname, numpre, numpost); + if (index < 0 || index >= num) continue; + str = sarrayGetString(saout, index, L_NOCOPY); + if (str[0] != '\0') { + L_WARNING("\n Multiple files with same number: %d\n", + procName, index); + } + sarrayReplaceString(saout, index, fname, L_COPY); + } + + return saout; +} + + +/*! + * \brief getFilenamesInDirectory() + * + * \param[in] dirname directory name + * \return sarray of file names, or NULL on error + * + *
+ * Notes:
+ *      (1) The versions compiled under unix and cygwin use the POSIX C
+ *          library commands for handling directories.  For windows,
+ *          there is a separate implementation.
+ *      (2) It returns an array of filename tails; i.e., only the part of
+ *          the path after the last slash.
+ *      (3) Use of the d_type field of dirent is not portable:
+ *          "According to POSIX, the dirent structure contains a field
+ *          char d_name[] of unspecified size, with at most NAME_MAX
+ *          characters preceding the terminating null character.  Use
+ *          of other fields will harm the portability of your programs."
+ *      (4) As a consequence of (3), we note several things:
+ *           ~ MINGW doesn't have a d_type member.
+ *           ~ Older versions of gcc (e.g., 2.95.3) return DT_UNKNOWN
+ *             for d_type from all files.
+ *          On these systems, this function will return directories
+ *          (except for '.' and '..', which are eliminated using
+ *          the d_name field).
+ * 
+ */ + +#ifndef _WIN32 + +SARRAY * +getFilenamesInDirectory(const char *dirname) +{ +char dir[PATH_MAX + 1]; +char *realdir, *stat_path, *ignore; +size_t size; +SARRAY *safiles; +DIR *pdir; +struct dirent *pdirentry; +int dfd, stat_ret; +struct stat st; + + PROCNAME("getFilenamesInDirectory"); + + if (!dirname) + return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); + + /* It's nice to ignore directories. fstatat() works with relative + directory paths, but stat() requires using the absolute path. + Also, do not pass NULL as the second parameter to realpath(); + use a buffer of sufficient size. */ + ignore = realpath(dirname, dir); /* see note above */ + realdir = genPathname(dir, NULL); + if ((pdir = opendir(realdir)) == NULL) { + LEPT_FREE(realdir); + return (SARRAY *)ERROR_PTR("pdir not opened", procName, NULL); + } + safiles = sarrayCreate(0); + dfd = dirfd(pdir); + while ((pdirentry = readdir(pdir))) { +#if HAVE_FSTATAT + stat_ret = fstatat(dfd, pdirentry->d_name, &st, 0); +#else + size = strlen(realdir) + strlen(pdirentry->d_name) + 2; + if (size > PATH_MAX) { + L_ERROR("size = %zu too large; skipping\n", procName, size); + continue; + } + stat_path = (char *)LEPT_CALLOC(size, 1); + snprintf(stat_path, size, "%s/%s", realdir, pdirentry->d_name); + stat_ret = stat(stat_path, &st); + LEPT_FREE(stat_path); +#endif + if (stat_ret == 0 && S_ISDIR(st.st_mode)) + continue; + sarrayAddString(safiles, pdirentry->d_name, L_COPY); + } + closedir(pdir); + LEPT_FREE(realdir); + return safiles; +} + +#else /* _WIN32 */ + + /* http://msdn2.microsoft.com/en-us/library/aa365200(VS.85).aspx */ +#include + +SARRAY * +getFilenamesInDirectory(const char *dirname) +{ +char *pszDir; +char *realdir; +HANDLE hFind = INVALID_HANDLE_VALUE; +SARRAY *safiles; +WIN32_FIND_DATAA ffd; + + PROCNAME("getFilenamesInDirectory"); + + if (!dirname) + return (SARRAY *)ERROR_PTR("dirname not defined", procName, NULL); + + realdir = genPathname(dirname, NULL); + pszDir = stringJoin(realdir, "\\*"); + LEPT_FREE(realdir); + + if (strlen(pszDir) + 1 > MAX_PATH) { + LEPT_FREE(pszDir); + return (SARRAY *)ERROR_PTR("dirname is too long", procName, NULL); + } + + if ((safiles = sarrayCreate(0)) == NULL) { + LEPT_FREE(pszDir); + return (SARRAY *)ERROR_PTR("safiles not made", procName, NULL); + } + + hFind = FindFirstFileA(pszDir, &ffd); + if (INVALID_HANDLE_VALUE == hFind) { + sarrayDestroy(&safiles); + LEPT_FREE(pszDir); + return (SARRAY *)ERROR_PTR("hFind not opened", procName, NULL); + } + + while (FindNextFileA(hFind, &ffd) != 0) { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* skip dirs */ + continue; + convertSepCharsInPath(ffd.cFileName, UNIX_PATH_SEPCHAR); + sarrayAddString(safiles, ffd.cFileName, L_COPY); + } + + FindClose(hFind); + LEPT_FREE(pszDir); + return safiles; +} +#endif /* _WIN32 */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sarray2.c b/hgdriver/3rdparty/hgOCR/leptonica/sarray2.c new file mode 100644 index 0000000..7dea899 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sarray2.c @@ -0,0 +1,726 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file sarray2.c + *
+ *
+ *      Sort
+ *          SARRAY     *sarraySort()
+ *          SARRAY     *sarraySortByIndex()
+ *          l_int32     stringCompareLexical()
+ *
+ *      Set operations using aset (rbtree)
+ *          SARRAY     *sarrayUnionByAset()
+ *          SARRAY     *sarrayRemoveDupsByAset()
+ *          SARRAY     *sarrayIntersectionByAset()
+ *          L_ASET     *l_asetCreateFromSarray()
+ *
+ *      Set operations using hashing (dnahash)
+ *          l_int32     sarrayRemoveDupsByHash()
+ *          SARRAY     *sarrayIntersectionByHash()
+ *          l_int32     sarrayFindStringByHash()
+ *          L_DNAHASH  *l_dnaHashCreateFromSarray()
+ *
+ *      Miscellaneous operations
+ *          SARRAY     *sarrayGenerateIntegers()
+ *          l_int32     sarrayLookupCSKV()
+ *
+ *
+ * We have two implementations of set operations on an array of strings:
+ *
+ *   (1) Using an underlying tree (rbtree)
+ *       This uses a good 64 bit hashing function for the key,
+ *       that is not expected to have hash collisions (and we do
+ *       not test for them).  The tree is built up of the hash
+ *       values, and if the hash is found in the tree, it is
+ *       assumed that the string has already been found.
+ *
+ *   (2) Using an underlying hashing of the keys (dnahash)
+ *       This uses a fast 64 bit hashing function for the key,
+ *       which is then hashed into a bucket (a dna in a dnaHash).
+ *       Because hash collisions can occur, the index into the
+ *       sarray for the string that gave rise to that key is stored,
+ *       and the dna (bucket) is traversed, using the stored indices
+ *       to determine if that string had already been seen.
+ *
+ * 
+ */ + +#include +#include "allheaders.h" + +/*----------------------------------------------------------------------* + * Sort * + *----------------------------------------------------------------------*/ +/*! + * \brief sarraySort() + * + * \param[in] saout output sarray; can be NULL or equal to sain + * \param[in] sain input sarray + * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING + * \return saout output sarray, sorted by ascii value, or NULL on error + * + *
+ * Notes:
+ *      (1) Set saout = sain for in-place; otherwise, set naout = NULL.
+ *      (2) Shell sort, modified from K&R, 2nd edition, p.62.
+ *          Slow but simple O(n logn) sort.
+ * 
+ */ +SARRAY * +sarraySort(SARRAY *saout, + SARRAY *sain, + l_int32 sortorder) +{ +char **array; +char *tmp; +l_int32 n, i, j, gap; + + PROCNAME("sarraySort"); + + if (!sain) + return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); + + /* Make saout if necessary; otherwise do in-place */ + if (!saout) + saout = sarrayCopy(sain); + else if (sain != saout) + return (SARRAY *)ERROR_PTR("invalid: not in-place", procName, NULL); + array = saout->array; /* operate directly on the array */ + n = sarrayGetCount(saout); + + /* Shell sort */ + for (gap = n/2; gap > 0; gap = gap / 2) { + for (i = gap; i < n; i++) { + for (j = i - gap; j >= 0; j -= gap) { + if ((sortorder == L_SORT_INCREASING && + stringCompareLexical(array[j], array[j + gap])) || + (sortorder == L_SORT_DECREASING && + stringCompareLexical(array[j + gap], array[j]))) + { + tmp = array[j]; + array[j] = array[j + gap]; + array[j + gap] = tmp; + } + } + } + } + + return saout; +} + + +/*! + * \brief sarraySortByIndex() + * + * \param[in] sain + * \param[in] naindex na that maps from the new sarray to the input sarray + * \return saout sorted, or NULL on error + */ +SARRAY * +sarraySortByIndex(SARRAY *sain, + NUMA *naindex) +{ +char *str; +l_int32 i, n, index; +SARRAY *saout; + + PROCNAME("sarraySortByIndex"); + + if (!sain) + return (SARRAY *)ERROR_PTR("sain not defined", procName, NULL); + if (!naindex) + return (SARRAY *)ERROR_PTR("naindex not defined", procName, NULL); + + n = sarrayGetCount(sain); + saout = sarrayCreate(n); + for (i = 0; i < n; i++) { + numaGetIValue(naindex, i, &index); + str = sarrayGetString(sain, index, L_COPY); + sarrayAddString(saout, str, L_INSERT); + } + + return saout; +} + + +/*! + * \brief stringCompareLexical() + * + * \param[in] str1 + * \param[in] str2 + * \return 1 if str1 > str2 lexically; 0 otherwise + * + *
+ * Notes:
+ *      (1) If the lexical values are identical, return a 0, to
+ *          indicate that no swapping is required to sort the strings.
+ * 
+ */ +l_int32 +stringCompareLexical(const char *str1, + const char *str2) +{ +l_int32 i, len1, len2, len; + + PROCNAME("sarrayCompareLexical"); + + if (!str1) + return ERROR_INT("str1 not defined", procName, 1); + if (!str2) + return ERROR_INT("str2 not defined", procName, 1); + + len1 = strlen(str1); + len2 = strlen(str2); + len = L_MIN(len1, len2); + + for (i = 0; i < len; i++) { + if (str1[i] == str2[i]) + continue; + if (str1[i] > str2[i]) + return 1; + else + return 0; + } + + if (len1 > len2) + return 1; + else + return 0; +} + + +/*----------------------------------------------------------------------* + * Set operations using aset (rbtree) * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayUnionByAset() + * + * \param[in] sa1, sa2 + * \return sad with the union of the string set, or NULL on error + * + *
+ * Notes:
+ *      (1) Duplicates are removed from the concatenation of the two arrays.
+ *      (2) The key for each string is a 64-bit hash.
+ *      (2) Algorithm: Concatenate the two sarrays.  Then build a set,
+ *          using hashed strings as keys.  As the set is built, first do
+ *          a find; if not found, add the key to the set and add the string
+ *          to the output sarray.  This is O(nlogn).
+ * 
+ */ +SARRAY * +sarrayUnionByAset(SARRAY *sa1, + SARRAY *sa2) +{ +SARRAY *sa3, *sad; + + PROCNAME("sarrayUnionByAset"); + + if (!sa1) + return (SARRAY *)ERROR_PTR("sa1 not defined", procName, NULL); + if (!sa2) + return (SARRAY *)ERROR_PTR("sa2 not defined", procName, NULL); + + /* Join */ + sa3 = sarrayCopy(sa1); + sarrayJoin(sa3, sa2); + + /* Eliminate duplicates */ + sad = sarrayRemoveDupsByAset(sa3); + sarrayDestroy(&sa3); + return sad; +} + + +/*! + * \brief sarrayRemoveDupsByAset() + * + * \param[in] sas + * \return sad with duplicates removed, or NULL on error + * + *
+ * Notes:
+ *      (1) This is O(nlogn), considerably slower than
+ *          sarrayRemoveDupsByHash() for large string arrays.
+ *      (2) The key for each string is a 64-bit hash.
+ *      (3) Build a set, using hashed strings as keys.  As the set is
+ *          built, first do a find; if not found, add the key to the
+ *          set and add the string to the output sarray.
+ * 
+ */ +SARRAY * +sarrayRemoveDupsByAset(SARRAY *sas) +{ +char *str; +l_int32 i, n; +l_uint64 hash; +L_ASET *set; +RB_TYPE key; +SARRAY *sad; + + PROCNAME("sarrayRemoveDupsByAset"); + + if (!sas) + return (SARRAY *)ERROR_PTR("sas not defined", procName, NULL); + + set = l_asetCreate(L_UINT_TYPE); + sad = sarrayCreate(0); + n = sarrayGetCount(sas); + for (i = 0; i < n; i++) { + str = sarrayGetString(sas, i, L_NOCOPY); + l_hashStringToUint64(str, &hash); + key.utype = hash; + if (!l_asetFind(set, key)) { + sarrayAddString(sad, str, L_COPY); + l_asetInsert(set, key); + } + } + + l_asetDestroy(&set); + return sad; +} + + +/*! + * \brief sarrayIntersectionByAset() + * + * \param[in] sa1, sa2 + * \return sad with the intersection of the string set, or NULL on error + * + *
+ * Notes:
+ *      (1) Algorithm: put the larger sarray into a set, using the string
+ *          hashes as the key values.  Then run through the smaller sarray,
+ *          building an output sarray and a second set from the strings
+ *          in the larger array: if a string is in the first set but
+ *          not in the second, add the string to the output sarray and hash
+ *          it into the second set.  The second set is required to make
+ *          sure only one instance of each string is put into the output sarray.
+ *          This is O(mlogn), {m,n} = sizes of {smaller,larger} input arrays.
+ * 
+ */ +SARRAY * +sarrayIntersectionByAset(SARRAY *sa1, + SARRAY *sa2) +{ +char *str; +l_int32 n1, n2, i, n; +l_uint64 hash; +L_ASET *set1, *set2; +RB_TYPE key; +SARRAY *sa_small, *sa_big, *sad; + + PROCNAME("sarrayIntersectionByAset"); + + if (!sa1) + return (SARRAY *)ERROR_PTR("sa1 not defined", procName, NULL); + if (!sa2) + return (SARRAY *)ERROR_PTR("sa2 not defined", procName, NULL); + + /* Put the elements of the biggest array into a set */ + n1 = sarrayGetCount(sa1); + n2 = sarrayGetCount(sa2); + sa_small = (n1 < n2) ? sa1 : sa2; /* do not destroy sa_small */ + sa_big = (n1 < n2) ? sa2 : sa1; /* do not destroy sa_big */ + set1 = l_asetCreateFromSarray(sa_big); + + /* Build up the intersection of strings */ + sad = sarrayCreate(0); + n = sarrayGetCount(sa_small); + set2 = l_asetCreate(L_UINT_TYPE); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa_small, i, L_NOCOPY); + l_hashStringToUint64(str, &hash); + key.utype = hash; + if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { + sarrayAddString(sad, str, L_COPY); + l_asetInsert(set2, key); + } + } + + l_asetDestroy(&set1); + l_asetDestroy(&set2); + return sad; +} + + +/*! + * \brief l_asetCreateFromSarray() + * + * \param[in] sa + * \return set using a string hash into a uint64 as the key + */ +L_ASET * +l_asetCreateFromSarray(SARRAY *sa) +{ +char *str; +l_int32 i, n; +l_uint64 hash; +L_ASET *set; +RB_TYPE key; + + PROCNAME("l_asetCreateFromSarray"); + + if (!sa) + return (L_ASET *)ERROR_PTR("sa not defined", procName, NULL); + + set = l_asetCreate(L_UINT_TYPE); + n = sarrayGetCount(sa); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + l_hashStringToUint64(str, &hash); + key.utype = hash; + l_asetInsert(set, key); + } + + return set; +} + + +/*----------------------------------------------------------------------* + * Set operations using hashing (dnahash) * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayRemoveDupsByHash() + * + * \param[in] sas + * \param[out] psad unique set of strings; duplicates removed + * \param[out] pdahash [optional] dnahash used for lookup + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Generates a sarray with unique values.
+ *      (2) The dnahash is built up with sad to assure uniqueness.
+ *          It can be used to find if a string is in the set:
+ *              sarrayFindValByHash(sad, dahash, str, &index)
+ *      (3) The hash of the string location is simple and fast.  It scales
+ *          up with the number of buckets to insure a fairly random
+ *          bucket selection input strings.
+ *      (4) This is faster than sarrayRemoveDupsByAset(), because the
+ *          bucket lookup is O(n), although there is a double-loop
+ *          lookup within the dna in each bucket.
+ * 
+ */ +l_ok +sarrayRemoveDupsByHash(SARRAY *sas, + SARRAY **psad, + L_DNAHASH **pdahash) +{ +char *str; +l_int32 i, n, index, items; +l_uint32 nsize; +l_uint64 key; +SARRAY *sad; +L_DNAHASH *dahash; + + PROCNAME("sarrayRemoveDupsByHash"); + + if (pdahash) *pdahash = NULL; + if (!psad) + return ERROR_INT("&sad not defined", procName, 1); + *psad = NULL; + if (!sas) + return ERROR_INT("sas not defined", procName, 1); + + n = sarrayGetCount(sas); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ + dahash = l_dnaHashCreate(nsize, 8); + sad = sarrayCreate(n); + *psad = sad; + for (i = 0, items = 0; i < n; i++) { + str = sarrayGetString(sas, i, L_NOCOPY); + sarrayFindStringByHash(sad, dahash, str, &index); + if (index < 0) { /* not found */ + l_hashStringToUint64(str, &key); + l_dnaHashAdd(dahash, key, (l_float64)items); + sarrayAddString(sad, str, L_COPY); + items++; + } + } + + if (pdahash) + *pdahash = dahash; + else + l_dnaHashDestroy(&dahash); + return 0; +} + + +/*! + * \brief sarrayIntersectionByHash() + * + * \param[in] sa1, sa2 + * \return sad intersection of the strings, or NULL on error + * + *
+ * Notes:
+ *      (1) This is faster than sarrayIntersectionByAset(), because the
+ *          bucket lookup is O(n).
+ * 
+ */ +SARRAY * +sarrayIntersectionByHash(SARRAY *sa1, + SARRAY *sa2) +{ +char *str; +l_int32 n1, n2, nsmall, i, index1, index2; +l_uint32 nsize2; +l_uint64 key; +L_DNAHASH *dahash1, *dahash2; +SARRAY *sa_small, *sa_big, *sad; + + PROCNAME("sarrayIntersectionByHash"); + + if (!sa1) + return (SARRAY *)ERROR_PTR("sa1 not defined", procName, NULL); + if (!sa2) + return (SARRAY *)ERROR_PTR("sa2 not defined", procName, NULL); + + /* Put the elements of the biggest sarray into a dnahash */ + n1 = sarrayGetCount(sa1); + n2 = sarrayGetCount(sa2); + sa_small = (n1 < n2) ? sa1 : sa2; /* do not destroy sa_small */ + sa_big = (n1 < n2) ? sa2 : sa1; /* do not destroy sa_big */ + dahash1 = l_dnaHashCreateFromSarray(sa_big); + + /* Build up the intersection of strings. Add to %sad + * if the string is in sa_big (using dahash1) but hasn't + * yet been seen in the traversal of sa_small (using dahash2). */ + sad = sarrayCreate(0); + nsmall = sarrayGetCount(sa_small); + findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ + dahash2 = l_dnaHashCreate(nsize2, 0); + for (i = 0; i < nsmall; i++) { + str = sarrayGetString(sa_small, i, L_NOCOPY); + sarrayFindStringByHash(sa_big, dahash1, str, &index1); + if (index1 >= 0) { + sarrayFindStringByHash(sa_small, dahash2, str, &index2); + if (index2 == -1) { + sarrayAddString(sad, str, L_COPY); + l_hashStringToUint64(str, &key); + l_dnaHashAdd(dahash2, key, (l_float64)i); + } + } + } + + l_dnaHashDestroy(&dahash1); + l_dnaHashDestroy(&dahash2); + return sad; +} + + +/*! + * \brief sarrayFindStringByHash() + * + * \param[in] sa + * \param[in] dahash built from sa + * \param[in] str arbitrary string + * \param[out] pindex index into %sa if %str is in %sa; -1 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Fast lookup in dnaHash associated with a sarray, to see if a
+ *          random string %str is already stored in the hash table.
+ *      (2) We use a strong hash function to minimize the chance that
+ *          two different strings hash to the same key value.
+ *      (3) We select the number of buckets to be about 5% of the size
+ *          of the input sarray, so that when fully populated, each
+ *          bucket (dna) will have about 20 entries, each being an index
+ *          into sa.  In lookup, after hashing to the key, and then
+ *          again to the bucket, we traverse the bucket (dna), using the
+ *          index into sa to check if %str has been found before.
+ * 
+ */ +l_ok +sarrayFindStringByHash(SARRAY *sa, + L_DNAHASH *dahash, + const char *str, + l_int32 *pindex) +{ +char *stri; +l_int32 i, nvals, index; +l_uint64 key; +L_DNA *da; + + PROCNAME("sarrayFindStringByHash"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = -1; + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!dahash) + return ERROR_INT("dahash not defined", procName, 1); + + l_hashStringToUint64(str, &key); + da = l_dnaHashGetDna(dahash, key, L_NOCOPY); + if (!da) return 0; + + /* Run through the da, looking for this string */ + nvals = l_dnaGetCount(da); + for (i = 0; i < nvals; i++) { + l_dnaGetIValue(da, i, &index); + stri = sarrayGetString(sa, index, L_NOCOPY); + if (!strcmp(str, stri)) { /* duplicate */ + *pindex = index; + return 0; + } + } + + return 0; +} + + +/*! + * \brief l_dnaHashCreateFromSarray() + * + * \param[in] sa + * \return dahash, or NULL on error + */ +L_DNAHASH * +l_dnaHashCreateFromSarray(SARRAY *sa) +{ +char *str; +l_int32 i, n; +l_uint32 nsize; +l_uint64 key; +L_DNAHASH *dahash; + + /* Build up dnaHash of indices, hashed by a 64-bit key that + * should randomize the lower bits used in bucket selection. + * Having about 20 pts in each bucket is roughly optimal. */ + n = sarrayGetCount(sa); + findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ +/* fprintf(stderr, "Prime used: %d\n", nsize); */ + + /* Add each string, using the hash as key and the index into %sa + * as the value. Storing the index enables operations that check + * for duplicates. */ + dahash = l_dnaHashCreate(nsize, 8); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + l_hashStringToUint64(str, &key); + l_dnaHashAdd(dahash, key, (l_float64)i); + } + + return dahash; +} + + +/*----------------------------------------------------------------------* + * Miscellaneous operations * + *----------------------------------------------------------------------*/ +/*! + * \brief sarrayGenerateIntegers() + * + * \param[in] n + * \return sa of printed numbers, 1 - n, or NULL on error + */ +SARRAY * +sarrayGenerateIntegers(l_int32 n) +{ +char buf[32]; +l_int32 i; +SARRAY *sa; + + PROCNAME("sarrayGenerateIntegers"); + + if ((sa = sarrayCreate(n)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + for (i = 0; i < n; i++) { + snprintf(buf, sizeof(buf), "%d", i); + sarrayAddString(sa, buf, L_COPY); + } + return sa; +} + + +/*! + * \brief sarrayLookupCSKV() + * + * \param[in] sa of strings, each being a comma-separated pair + * of strings, the first being a key and the + * second a value + * \param[in] keystring an input string to match with each key in %sa + * \param[out] pvalstring the returned value string corresponding to the + * input key string, if found; otherwise NULL + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input %sa can have other strings that are not in
+ *          comma-separated key-value format.  These will be ignored.
+ *      (2) This returns a copy of the first value string in %sa whose
+ *          key string matches the input %keystring.
+ *      (3) White space is not ignored; all white space before the ','
+ *          is used for the keystring in matching.  This allows the
+ *          key and val strings to have white space (e.g., multiple words).
+ * 
+ */ +l_ok +sarrayLookupCSKV(SARRAY *sa, + const char *keystring, + char **pvalstring) +{ +char *key, *val, *str; +l_int32 i, n; +SARRAY *sa1; + + PROCNAME("sarrayLookupCSKV"); + + if (!pvalstring) + return ERROR_INT("&valstring not defined", procName, 1); + *pvalstring = NULL; + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!keystring) + return ERROR_INT("keystring not defined", procName, 1); + + n = sarrayGetCount(sa); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + sa1 = sarrayCreate(2); + sarraySplitString(sa1, str, ","); + if (sarrayGetCount(sa1) != 2) { + sarrayDestroy(&sa1); + continue; + } + key = sarrayGetString(sa1, 0, L_NOCOPY); + val = sarrayGetString(sa1, 1, L_NOCOPY); + if (!strcmp(key, keystring)) { + *pvalstring = stringNew(val); + sarrayDestroy(&sa1); + return 0; + } + sarrayDestroy(&sa1); + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/scale1.c b/hgdriver/3rdparty/hgOCR/leptonica/scale1.c new file mode 100644 index 0000000..4008319 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/scale1.c @@ -0,0 +1,3730 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file scale1.c + *
+ *         Top-level scaling
+ *               PIX      *pixScale()
+ *               PIX      *pixScaleToSizeRel()
+ *               PIX      *pixScaleToSize()
+ *               PIX      *pixScaleToResolution()
+ *               PIX      *pixScaleGeneral()
+ *
+ *         Linearly interpreted (usually up-) scaling
+ *               PIX      *pixScaleLI()
+ *               PIX      *pixScaleColorLI()
+ *               PIX      *pixScaleColor2xLI()
+ *               PIX      *pixScaleColor4xLI()
+ *               PIX      *pixScaleGrayLI()
+ *               PIX      *pixScaleGray2xLI()
+ *               PIX      *pixScaleGray4xLI()
+ *
+ *         Upscale 2x followed by binarization
+ *               PIX      *pixScaleGray2xLIThresh()
+ *               PIX      *pixScaleGray2xLIDither()
+ *
+ *         Upscale 4x followed by binarization
+ *               PIX      *pixScaleGray4xLIThresh()
+ *               PIX      *pixScaleGray4xLIDither()
+ *
+ *         Scaling by closest pixel sampling
+ *               PIX      *pixScaleBySampling()
+ *               PIX      *pixScaleBySamplingToSize()
+ *               PIX      *pixScaleByIntSampling()
+ *
+ *         Fast integer factor subsampling RGB to gray and to binary
+ *               PIX      *pixScaleRGBToGrayFast()
+ *               PIX      *pixScaleRGBToBinaryFast()
+ *               PIX      *pixScaleGrayToBinaryFast()
+ *
+ *         Downscaling with (antialias) smoothing
+ *               PIX      *pixScaleSmooth()
+ *               PIX      *pixScaleSmoothToSize()
+ *               PIX      *pixScaleRGBToGray2()   [special 2x reduction to gray]
+ *
+ *         Downscaling with (antialias) area mapping
+ *               PIX      *pixScaleAreaMap()
+ *               PIX      *pixScaleAreaMap2()
+ *               PIX      *pixScaleAreaMapToSize()
+ *
+ *         Binary scaling by closest pixel sampling
+ *               PIX      *pixScaleBinary()
+ *
+ *     Low-level static functions:
+ *
+ *         Color (interpolated) scaling: general case
+ *               static void       scaleColorLILow()
+ *
+ *         Grayscale (interpolated) scaling: general case
+ *               static void       scaleGrayLILow()
+ *
+ *         Color (interpolated) scaling: 2x upscaling
+ *               static void       scaleColor2xLILow()
+ *               static void       scaleColor2xLILineLow()
+ *
+ *         Grayscale (interpolated) scaling: 2x upscaling
+ *               static void       scaleGray2xLILow()
+ *               static void       scaleGray2xLILineLow()
+ *
+ *         Grayscale (interpolated) scaling: 4x upscaling
+ *               static void       scaleGray4xLILow()
+ *               static void       scaleGray4xLILineLow()
+ *
+ *         Grayscale and color scaling by closest pixel sampling
+ *               static l_int32    scaleBySamplingLow()
+ *
+ *         Color and grayscale downsampling with (antialias) lowpass filter
+ *               static l_int32    scaleSmoothLow()
+ *               static void       scaleRGBToGray2Low()
+ *
+ *         Color and grayscale downsampling with (antialias) area mapping
+ *               static l_int32    scaleColorAreaMapLow()
+ *               static l_int32    scaleGrayAreaMapLow()
+ *               static l_int32    scaleAreaMapLow2()
+ *
+ *         Binary scaling by closest pixel sampling
+ *               static l_int32    scaleBinaryLow()
+ * 
+ */ + +#include +#include "allheaders.h" + +static void scaleColorLILow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 wpls); +static void scaleGrayLILow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 wpls); +static void scaleColor2xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, + l_int32 ws, l_int32 hs, l_int32 wpls); +static void scaleColor2xLILineLow(l_uint32 *lined, l_int32 wpld, + l_uint32 *lines, l_int32 ws, l_int32 wpls, + l_int32 lastlineflag); +static void scaleGray2xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, + l_int32 ws, l_int32 hs, l_int32 wpls); +static void scaleGray2xLILineLow(l_uint32 *lined, l_int32 wpld, + l_uint32 *lines, l_int32 ws, l_int32 wpls, + l_int32 lastlineflag); +static void scaleGray4xLILow(l_uint32 *datad, l_int32 wpld, l_uint32 *datas, + l_int32 ws, l_int32 hs, l_int32 wpls); +static void scaleGray4xLILineLow(l_uint32 *lined, l_int32 wpld, + l_uint32 *lines, l_int32 ws, l_int32 wpls, + l_int32 lastlineflag); +static l_int32 scaleBySamplingLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 d, l_int32 wpls); +static l_int32 scaleSmoothLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 d, l_int32 wpls, + l_int32 size); +static void scaleRGBToGray2Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_float32 rwt, l_float32 gwt, l_float32 bwt); +static void scaleColorAreaMapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 wpls); +static void scaleGrayAreaMapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 wpls); +static void scaleAreaMapLow2(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 d, + l_int32 wpls); +static l_int32 scaleBinaryLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 ws, + l_int32 hs, l_int32 wpls); + +#ifndef NO_CONSOLE_IO +#define DEBUG_OVERFLOW 0 +#define DEBUG_UNROLLING 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*------------------------------------------------------------------* + * Top level scaling dispatcher * + *------------------------------------------------------------------*/ +/*! + * \brief pixScale() + * + * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp + * \param[in] scalex, scaley + * \return pixd, or NULL on error + * + * This function scales 32 bpp RGB; 2, 4 or 8 bpp palette color; + * 2, 4, 8 or 16 bpp gray; and binary images. + * + * When the input has palette color, the colormap is removed and + * the result is either 8 bpp gray or 32 bpp RGB, depending on whether + * the colormap has color entries. Images with 2, 4 or 16 bpp are + * converted to 8 bpp. + * + * Because pixScale is meant to be a very simple interface to a + * number of scaling functions, including the use of unsharp masking, + * the type of scaling and the sharpening parameters are chosen + * by default. Grayscale and color images are scaled using one + * of four methods, depending on the scale factors: + * 1 antialiased subsampling (lowpass filtering followed by + * subsampling, implemented here by area mapping), for scale factors + * less than 0.2 + * 2 antialiased subsampling with sharpening, for scale factors + * between 0.2 and 0.7 + * 3 linear interpolation with sharpening, for scale factors between + * 0.7 and 1.4 + * 4 linear interpolation without sharpening, for scale factors >= 1.4. + * + * One could use subsampling for scale factors very close to 1.0, + * because it preserves sharp edges. Linear interpolation blurs + * edges because the dest pixels will typically straddle two src edge + * pixels. Subsmpling removes entire columns and rows, so the edge is + * not blurred. However, there are two reasons for not doing this. + * First, it moves edges, so that a straight line at a large angle to + * both horizontal and vertical will have noticeable kinks where + * horizontal and vertical rasters are removed. Second, although it + * is very fast, you get good results on sharp edges by applying + * a sharpening filter. + * + * For images with sharp edges, sharpening substantially improves the + * image quality for scale factors between about 0.2 and about 2.0. + * pixScale uses a small amount of sharpening by default because + * it strengthens edge pixels that are weak due to anti-aliasing. + * The default sharpening factors are: + * * for scaling factors < 0.7: sharpfract = 0.2 sharpwidth = 1 + * * for scaling factors >= 0.7: sharpfract = 0.4 sharpwidth = 2 + * The cases where the sharpening halfwidth is 1 or 2 have special + * implementations and are about twice as fast as the general case. + * + * However, sharpening is computationally expensive, and one needs + * to consider the speed-quality tradeoff: + * * For upscaling of RGB images, linear interpolation plus default + * sharpening is about 5 times slower than upscaling alone. + * * For downscaling, area mapping plus default sharpening is + * about 10 times slower than downscaling alone. + * When the scale factor is larger than 1.4, the cost of sharpening, + * which is proportional to image area, is very large compared to the + * incremental quality improvement, so we cut off the default use of + * sharpening at 1.4. Thus, for scale factors greater than 1.4, + * pixScale only does linear interpolation. + * + * In many situations you will get a satisfactory result by scaling + * without sharpening: call pixScaleGeneral with %sharpfract = 0.0. + * Alternatively, if you wish to sharpen but not use the default + * value, first call pixScaleGeneral with %sharpfract = 0.0, and + * then sharpen explicitly using pixUnsharpMasking. + * + * Binary images are scaled to binary by sampling the closest pixel, + * without any low-pass filtering averaging of neighboring pixels. + * This will introduce aliasing for reductions. Aliasing can be + * prevented by using pixScaleToGray instead. + */ +PIX * +pixScale(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 sharpwidth; +l_float32 maxscale, sharpfract; + + PROCNAME("pixScale"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Reduce the default sharpening factors by 2 if maxscale < 0.7 */ + maxscale = L_MAX(scalex, scaley); + sharpfract = (maxscale < 0.7) ? 0.2 : 0.4; + sharpwidth = (maxscale < 0.7) ? 1 : 2; + + return pixScaleGeneral(pixs, scalex, scaley, sharpfract, sharpwidth); +} + + +/*! + * \brief pixScaleToSizeRel() + * + * \param[in] pixs + * \param[in] delw change in width, in pixels; 0 means no change + * \param[in] delh change in height, in pixels; 0 means no change + * \return pixd, or NULL on error + */ +PIX * +pixScaleToSizeRel(PIX *pixs, + l_int32 delw, + l_int32 delh) +{ +l_int32 w, h, wd, hd; + + PROCNAME("pixScaleToSizeRel"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + if (delw == 0 && delh == 0) + return pixCopy(NULL, pixs); + + pixGetDimensions(pixs, &w, &h, NULL); + wd = w + delw; + hd = h + delh; + if (wd <= 0 || hd <= 0) + return (PIX *)ERROR_PTR("pix dimension reduced to 0", procName, NULL); + + return pixScaleToSize(pixs, wd, hd); +} + + +/*! + * \brief pixScaleToSize() + * + * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The output scaled image has the dimension(s) you specify:
+ *          * To specify the width with isotropic scaling, set %hd = 0.
+ *          * To specify the height with isotropic scaling, set %wd = 0.
+ *          * If both %wd and %hd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *          * It is an error to set both %wd and %hd to 0.
+ * 
+ */ +PIX * +pixScaleToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h; +l_float32 scalex, scaley; + + PROCNAME("pixScaleToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (wd <= 0 && hd <= 0) + return (PIX *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (wd <= 0) { + scaley = (l_float32)hd / (l_float32)h; + scalex = scaley; + } else if (hd <= 0) { + scalex = (l_float32)wd / (l_float32)w; + scaley = scalex; + } else { + scalex = (l_float32)wd / (l_float32)w; + scaley = (l_float32)hd / (l_float32)h; + } + + return pixScale(pixs, scalex, scaley); +} + + +/*! + * \brief pixScaleToResolution() + * + * \param[in] pixs + * \param[in] target desired resolution + * \param[in] assumed assumed resolution if not defined; typ. 300. + * \param[out] pscalefact [optional] actual scaling factor used + * \return pixd, or NULL on error + */ +PIX * +pixScaleToResolution(PIX *pixs, + l_float32 target, + l_float32 assumed, + l_float32 *pscalefact) +{ +l_int32 xres; +l_float32 factor; + + PROCNAME("pixScaleToResolution"); + + if (pscalefact) *pscalefact = 1.0; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (target <= 0) + return (PIX *)ERROR_PTR("target resolution <= 0", procName, NULL); + + xres = pixGetXRes(pixs); + if (xres <= 0) { + if (assumed == 0) + return pixCopy(NULL, pixs); + xres = assumed; + } + factor = target / (l_float32)xres; + if (pscalefact) *pscalefact = factor; + + return pixScale(pixs, factor, factor); +} + + +/*! + * \brief pixScaleGeneral() + * + * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp + * \param[in] scalex must be > 0.0 + * \param[in] scaley must be > 0.0 + * \param[in] sharpfract use 0.0 to skip sharpening + * \param[in] sharpwidth halfwidth of low-pass filter; typ. 1 or 2 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixScale() for usage.
+ *      (2) This interface may change in the future, as other special
+ *          cases are added.
+ *      (3) The actual sharpening factors used depend on the maximum
+ *          of the two scale factors (maxscale):
+ *            maxscale <= 0.2:        no sharpening
+ *            0.2 < maxscale < 1.4:   uses the input parameters
+ *            maxscale >= 1.4:        no sharpening
+ *      (4) To avoid sharpening for grayscale and color images with
+ *          scaling factors between 0.2 and 1.4, call this function
+ *          with %sharpfract == 0.0.
+ *      (5) To use arbitrary sharpening in conjunction with scaling,
+ *          call this function with %sharpfract = 0.0, and follow this
+ *          with a call to pixUnsharpMasking() with your chosen parameters.
+ * 
+ */ +PIX * +pixScaleGeneral(PIX *pixs, + l_float32 scalex, + l_float32 scaley, + l_float32 sharpfract, + l_int32 sharpwidth) +{ +l_int32 d; +l_float32 maxscale; +PIX *pixt, *pixt2, *pixd; + + PROCNAME("pixScaleGeneral"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not {1,2,4,8,16,32} bpp", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factor <= 0", procName, NULL); + if (scalex == 1.0 && scaley == 1.0) + return pixCopy(NULL, pixs); + + if (d == 1) + return pixScaleBinary(pixs, scalex, scaley); + + /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ + if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + /* Scale (up or down) */ + d = pixGetDepth(pixt); + maxscale = L_MAX(scalex, scaley); + if (maxscale < 0.7) { /* area mapping for anti-aliasing */ + pixt2 = pixScaleAreaMap(pixt, scalex, scaley); + if (maxscale > 0.2 && sharpfract > 0.0 && sharpwidth > 0) + pixd = pixUnsharpMasking(pixt2, sharpwidth, sharpfract); + else + pixd = pixClone(pixt2); + } else { /* use linear interpolation */ + if (d == 8) + pixt2 = pixScaleGrayLI(pixt, scalex, scaley); + else /* d == 32 */ + pixt2 = pixScaleColorLI(pixt, scalex, scaley); + if (maxscale < 1.4 && sharpfract > 0.0 && sharpwidth > 0) + pixd = pixUnsharpMasking(pixt2, sharpwidth, sharpfract); + else + pixd = pixClone(pixt2); + } + + pixDestroy(&pixt); + pixDestroy(&pixt2); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*------------------------------------------------------------------* + * Scaling by linear interpolation * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleLI() + * + * \param[in] pixs 2, 4, 8 or 32 bpp; with or without colormap + * \param[in] scalex must be >= 0.7 + * \param[in] scaley must be >= 0.7 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function should only be used when the scale factors are
+ *          greater than or equal to 0.7, and typically greater than 1.
+ *          If either scale factor is larger than 0.7, we issue a warning
+ *          and call pixScaleGeneral(), which will invoke area mapping
+ *          without sharpening.
+ *      (2) This works on 2, 4, 8, 16 and 32 bpp images, as well as on
+ *          2, 4 and 8 bpp images that have a colormap.  If there is a
+ *          colormap, it is removed to either gray or RGB, depending
+ *          on the colormap.
+ *      (3) This does a linear interpolation on the src image.
+ *      (4) It dispatches to much faster implementations for
+ *          the special cases of 2x and 4x expansion.
+ * 
+ */ +PIX * +pixScaleLI(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 d; +l_float32 maxscale; +PIX *pixt, *pixd; + + PROCNAME("pixScaleLI"); + + if (!pixs || (pixGetDepth(pixs) == 1)) + return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); + maxscale = L_MAX(scalex, scaley); + if (maxscale < 0.7) { + L_WARNING("scaling factors < 0.7; do regular scaling\n", procName); + return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); + } + d = pixGetDepth(pixs); + if (d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("pixs not {2,4,8,16,32} bpp", procName, NULL); + + /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ + if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + + d = pixGetDepth(pixt); + if (d == 8) + pixd = pixScaleGrayLI(pixt, scalex, scaley); + else /* d == 32 */ + pixd = pixScaleColorLI(pixt, scalex, scaley); + + pixDestroy(&pixt); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixScaleColorLI() + * + * \param[in] pixs 32 bpp, representing rgb + * \param[in] scalex must be >= 0.7 + * \param[in] scaley must be >= 0.7 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) If either scale factor is larger than 0.7, we issue a warning
+ *          and call pixScaleGeneral(), which will invoke area mapping
+ *          without sharpening.  This is particularly important for
+ *          document images with sharp edges.
+ *      (2) For the general case, it's about 4x faster to manipulate
+ *          the color pixels directly, rather than to make images
+ *          out of each of the 3 components, scale each component
+ *          using the pixScaleGrayLI(), and combine the results back
+ *          into an rgb image.
+ *      (3) The speed on intel hardware for the general case (not 2x)
+ *          is about 10 * 10^6 dest-pixels/sec/GHz.  (The special 2x
+ *          case runs at about 80 * 10^6 dest-pixels/sec/GHz.)
+ * 
+ */ +PIX * +pixScaleColorLI(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, wpls, wd, hd, wpld; +l_uint32 *datas, *datad; +l_float32 maxscale; +PIX *pixd; + + PROCNAME("pixScaleColorLI"); + + if (!pixs || (pixGetDepth(pixs) != 32)) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + maxscale = L_MAX(scalex, scaley); + if (maxscale < 0.7) { + L_WARNING("scaling factors < 0.7; do regular scaling\n", procName); + return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); + } + + /* Do fast special cases if possible */ + if (scalex == 1.0 && scaley == 1.0) + return pixCopy(NULL, pixs); + if (scalex == 2.0 && scaley == 2.0) + return pixScaleColor2xLI(pixs); + if (scalex == 4.0 && scaley == 4.0) + return pixScaleColor4xLI(pixs); + + /* General case */ + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if ((pixd = pixCreate(wd, hd, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleColorLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); + + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixScaleColor2xLI() + * + * \param[in] pixs 32 bpp, representing rgb + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a special case of linear interpolated scaling,
+ *          for 2x upscaling.  It is about 8x faster than using
+ *          the generic pixScaleColorLI(), and about 4x faster than
+ *          using the special 2x scale function pixScaleGray2xLI()
+ *          on each of the three components separately.
+ *      (2) The speed on intel hardware is about
+ *          80 * 10^6 dest-pixels/sec/GHz.
+ * 
+ */ +PIX * +pixScaleColor2xLI(PIX *pixs) +{ +l_int32 ws, hs, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleColor2xLI"); + + if (!pixs || (pixGetDepth(pixs) != 32)) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(2 * ws, 2 * hs, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 2.0, 2.0); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleColor2xLILow(datad, wpld, datas, ws, hs, wpls); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 2.0, 2.0); + + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixScaleColor4xLI() + * + * \param[in] pixs 32 bpp, representing rgb + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a special case of color linear interpolated scaling,
+ *          for 4x upscaling.  It is about 3x faster than using
+ *          the generic pixScaleColorLI().
+ *      (2) The speed on intel hardware is about
+ *          30 * 10^6 dest-pixels/sec/GHz
+ *      (3) This scales each component separately, using pixScaleGray4xLI().
+ *          It would be about 4x faster to inline the color code properly,
+ *          in analogy to scaleColor4xLILow(), and I leave this as
+ *          an exercise for someone who really needs it.
+ * 
+ */ +PIX * +pixScaleColor4xLI(PIX *pixs) +{ +PIX *pixr, *pixg, *pixb; +PIX *pixrs, *pixgs, *pixbs; +PIX *pixd; + + PROCNAME("pixScaleColor4xLI"); + + if (!pixs || (pixGetDepth(pixs) != 32)) + return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); + + pixr = pixGetRGBComponent(pixs, COLOR_RED); + pixrs = pixScaleGray4xLI(pixr); + pixDestroy(&pixr); + pixg = pixGetRGBComponent(pixs, COLOR_GREEN); + pixgs = pixScaleGray4xLI(pixg); + pixDestroy(&pixg); + pixb = pixGetRGBComponent(pixs, COLOR_BLUE); + pixbs = pixScaleGray4xLI(pixb); + pixDestroy(&pixb); + + if ((pixd = pixCreateRGBImage(pixrs, pixgs, pixbs)) == NULL) { + L_ERROR("pixd not made\n", procName); + } else { + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 4.0, 4.0); + pixCopyInputFormat(pixd, pixs); + } + + pixDestroy(&pixrs); + pixDestroy(&pixgs); + pixDestroy(&pixbs); + return pixd; +} + + +/*! + * \brief pixScaleGrayLI() + * + * \param[in] pixs 8 bpp grayscale, no cmap + * \param[in] scalex must be >= 0.7 + * \param[in] scaley must be >= 0.7 + * \return pixd, or NULL on error + * + * This function is appropriate for upscaling magnification, where the + * scale factor is > 1, as well as for a small amount of downscaling + * reduction, with scale factor > 0.7. If the scale factor is < 0.7, + * the best result is obtained by area mapping, but this is relatiely + * expensive. A less expensive alternative with scale factor < 0.7 + * is low-pass filtering followed by subsampling (pixScaleSmooth()), + * which is effectively a cheap form of area mapping. + * + * Some more details follow. + * + * For each pixel in the dest, this does a linear + * interpolation of 4 neighboring pixels in the src. + * Specifically, consider the UL corner of src and + * dest pixels. The UL corner of the dest falls within + * a src pixel, whose four corners are the UL corners + * of 4 adjacent src pixels. The value of the dest + * is taken by linear interpolation using the values of + * the four src pixels and the distance of the UL corner + * of the dest from each corner. + * + * If the image is expanded so that the dest pixel is + * smaller than the src pixel, such interpolation + * is a reasonable approach. This interpolation is + * also good for a small image reduction factor that + * is not more than a 2x reduction. + * + * Note that the linear interpolation algorithm for scaling + * is identical in form to the area-mapping algorithm + * for grayscale rotation. The latter corresponds to a + * translation of each pixel without scaling. + * + * This function is NOT optimal if the scaling involves + * a large reduction. If the image is significantly + * reduced, so that the dest pixel is much larger than + * the src pixels, this interpolation, which is over src + * pixels only near the UL corner of the dest pixel, + * is not going to give a good area-mapping average. + * Because area mapping for image scaling is considerably + * more computationally intensive than linear interpolation, + * we choose not to use it. For large image reduction, + * linear interpolation over adjacent src pixels + * degenerates asymptotically to subsampling. But + * subsampling without a low-pass pre-filter causes + * aliasing by the nyquist theorem. To avoid aliasing, + * a low-pass filter e.g., an averaging filter of + * size roughly equal to the dest pixel i.e., the + * reduction factor should be applied to the src before + * subsampling. + * + * As an alternative to low-pass filtering and subsampling + * for large reduction factors, linear interpolation can + * also be done between the widely separated src pixels in + * which the corners of the dest pixel lie. This also is + * not optimal, as it samples src pixels only near the + * corners of the dest pixel, and it is not implemented. + * + * The speed on circa 2005 Intel hardware for the general case (not 2x) + * is about 13 * 10^6 dest-pixels/sec/GHz. The special 2x case runs + * at about 100 * 10^6 dest-pixels/sec/GHz. + */ +PIX * +pixScaleGrayLI(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, wpls, wd, hd, wpld; +l_uint32 *datas, *datad; +l_float32 maxscale; +PIX *pixd; + + PROCNAME("pixScaleGrayLI"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", + procName, NULL); + maxscale = L_MAX(scalex, scaley); + if (maxscale < 0.7) { + L_WARNING("scaling factors < 0.7; do regular scaling\n", procName); + return pixScaleGeneral(pixs, scalex, scaley, 0.0, 0); + } + + /* Do fast special cases if possible */ + if (scalex == 1.0 && scaley == 1.0) + return pixCopy(NULL, pixs); + if (scalex == 2.0 && scaley == 2.0) + return pixScaleGray2xLI(pixs); + if (scalex == 4.0 && scaley == 4.0) + return pixScaleGray4xLI(pixs); + + /* General case */ + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyText(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleGrayLILow(datad, wd, hd, wpld, datas, ws, hs, wpls); + return pixd; +} + + +/*! + * \brief pixScaleGray2xLI() + * + * \param[in] pixs 8 bpp grayscale, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a special case of gray linear interpolated scaling,
+ *          for 2x upscaling.  It is about 6x faster than using
+ *          the generic pixScaleGrayLI().
+ *      (2) The speed on intel hardware is about
+ *          100 * 10^6 dest-pixels/sec/GHz
+ * 
+ */ +PIX * +pixScaleGray2xLI(PIX *pixs) +{ +l_int32 ws, hs, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleGray2xLI"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(2 * ws, 2 * hs, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixScaleResolution(pixd, 2.0, 2.0); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleGray2xLILow(datad, wpld, datas, ws, hs, wpls); + return pixd; +} + + +/*! + * \brief pixScaleGray4xLI() + * + * \param[in] pixs 8 bpp grayscale, not cmapped + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a special case of gray linear interpolated scaling,
+ *          for 4x upscaling.  It is about 12x faster than using
+ *          the generic pixScaleGrayLI().
+ *      (2) The speed on intel hardware is about
+ *          160 * 10^6 dest-pixels/sec/GHz.
+ * 
+ */ +PIX * +pixScaleGray4xLI(PIX *pixs) +{ +l_int32 ws, hs, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleGray4xLI"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, cmapped or not 8 bpp", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if ((pixd = pixCreate(4 * ws, 4 * hs, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixScaleResolution(pixd, 4.0, 4.0); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleGray4xLILow(datad, wpld, datas, ws, hs, wpls); + return pixd; +} + + +/*------------------------------------------------------------------* + * Scale 2x followed by binarization * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleGray2xLIThresh() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] thresh between 0 and 256 + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does 2x upscale on pixs, using linear interpolation,
+ *          followed by thresholding to binary.
+ *      (2) Buffers are used to avoid making a large grayscale image.
+ * 
+ */ +PIX * +pixScaleGray2xLIThresh(PIX *pixs, + l_int32 thresh) +{ +l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; +l_uint32 *datas, *datad, *lines, *lined, *lineb; +PIX *pixd; + + PROCNAME("pixScaleGray2xLIThresh"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + if (thresh < 0 || thresh > 256) + return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = 2 * ws; + hd = 2 * hs; + hsm = hs - 1; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Make line buffer for 2 lines of virtual intermediate image */ + wplb = (wd + 3) / 4; + if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) + return (PIX *)ERROR_PTR("lineb not made", procName, NULL); + + /* Make dest binary image */ + if ((pixd = pixCreate(wd, hd, 1)) == NULL) { + LEPT_FREE(lineb); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 2.0, 2.0); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + /* Do all but last src line */ + for (i = 0; i < hsm; i++) { + lines = datas + i * wpls; + lined = datad + 2 * i * wpld; /* do 2 dest lines at a time */ + scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 0); + thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); + thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); + } + + /* Do last src line */ + lines = datas + hsm * wpls; + lined = datad + 2 * hsm * wpld; + scaleGray2xLILineLow(lineb, wplb, lines, ws, wpls, 1); + thresholdToBinaryLineLow(lined, wd, lineb, 8, thresh); + thresholdToBinaryLineLow(lined + wpld, wd, lineb + wplb, 8, thresh); + + LEPT_FREE(lineb); + return pixd; +} + + +/*! + * \brief pixScaleGray2xLIDither() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does 2x upscale on pixs, using linear interpolation,
+ *          followed by Floyd-Steinberg dithering to binary.
+ *      (2) Buffers are used to avoid making a large grayscale image.
+ *          ~ Two line buffers are used for the src, required for the 2x
+ *            LI upscale.
+ *          ~ Three line buffers are used for the intermediate image.
+ *            Two are filled with each 2xLI row operation; the third is
+ *            needed because the upscale and dithering ops are out of sync.
+ * 
+ */ +PIX * +pixScaleGray2xLIDither(PIX *pixs) +{ +l_int32 i, ws, hs, hsm, wd, hd, wpls, wplb, wpld; +l_uint32 *datas, *datad; +l_uint32 *lined; +l_uint32 *lineb = NULL; /* 2 intermediate buffer lines */ +l_uint32 *linebp = NULL; /* 1 intermediate buffer line */ +l_uint32 *bufs = NULL; /* 2 source buffer lines */ +PIX *pixd = NULL; + + PROCNAME("pixScaleGray2xLIDither"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = 2 * ws; + hd = 2 * hs; + hsm = hs - 1; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Make line buffers for 2 lines of src image */ + if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) + return (PIX *)ERROR_PTR("bufs not made", procName, NULL); + + /* Make line buffer for 2 lines of virtual intermediate image */ + wplb = (wd + 3) / 4; + if ((lineb = (l_uint32 *)LEPT_CALLOC(2 * wplb, sizeof(l_uint32))) == NULL) { + L_ERROR("lineb not made\n", procName); + goto cleanup; + } + + /* Make line buffer for 1 line of virtual intermediate image */ + if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) { + L_ERROR("linebp not made\n", procName); + goto cleanup; + } + + /* Make dest binary image */ + if ((pixd = pixCreate(wd, hd, 1)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup; + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 2.0, 2.0); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + /* Start with the first src and the first dest line */ + memcpy(bufs, datas, 4 * wpls); /* first src line */ + memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ + scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ + lined = datad; + ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* 1st d line */ + + /* Do all but last src line */ + for (i = 1; i < hsm; i++) { + memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ + memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); + memcpy(linebp, lineb + wplb, 4 * wplb); + scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 2 i lines */ + lined = datad + 2 * i * wpld; + ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* odd dest line */ + ditherToBinaryLineLow(lined, wd, lineb, lineb + wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* even dest line */ + } + + /* Do the last src line and the last 3 dest lines */ + memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ + memcpy(linebp, lineb + wplb, 4 * wplb); /* 1 i line */ + scaleGray2xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 2 i lines */ + ditherToBinaryLineLow(lined + wpld, wd, linebp, lineb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* odd dest line */ + ditherToBinaryLineLow(lined + 2 * wpld, wd, lineb, lineb + wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* even dest line */ + ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + wplb, NULL, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); + /* last dest line */ + +cleanup: + LEPT_FREE(bufs); + LEPT_FREE(lineb); + LEPT_FREE(linebp); + return pixd; +} + + +/*------------------------------------------------------------------* + * Scale 4x followed by binarization * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleGray4xLIThresh() + * + * \param[in] pixs 8 bpp + * \param[in] thresh between 0 and 256 + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does 4x upscale on pixs, using linear interpolation,
+ *          followed by thresholding to binary.
+ *      (2) Buffers are used to avoid making a large grayscale image.
+ *      (3) If a full 4x expanded grayscale image can be kept in memory,
+ *          this function is only about 10% faster than separately doing
+ *          a linear interpolation to a large grayscale image, followed
+ *          by thresholding to binary.
+ * 
+ */ +PIX * +pixScaleGray4xLIThresh(PIX *pixs, + l_int32 thresh) +{ +l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; +l_uint32 *datas, *datad, *lines, *lined, *lineb; +PIX *pixd; + + PROCNAME("pixScaleGray4xLIThresh"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + if (thresh < 0 || thresh > 256) + return (PIX *)ERROR_PTR("thresh must be in [0, ... 256]", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = 4 * ws; + hd = 4 * hs; + hsm = hs - 1; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Make line buffer for 4 lines of virtual intermediate image */ + wplb = (wd + 3) / 4; + if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) + return (PIX *)ERROR_PTR("lineb not made", procName, NULL); + + /* Make dest binary image */ + if ((pixd = pixCreate(wd, hd, 1)) == NULL) { + LEPT_FREE(lineb); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 4.0, 4.0); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + /* Do all but last src line */ + for (i = 0; i < hsm; i++) { + lines = datas + i * wpls; + lined = datad + 4 * i * wpld; /* do 4 dest lines at a time */ + scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 0); + for (j = 0; j < 4; j++) { + thresholdToBinaryLineLow(lined + j * wpld, wd, + lineb + j * wplb, 8, thresh); + } + } + + /* Do last src line */ + lines = datas + hsm * wpls; + lined = datad + 4 * hsm * wpld; + scaleGray4xLILineLow(lineb, wplb, lines, ws, wpls, 1); + for (j = 0; j < 4; j++) { + thresholdToBinaryLineLow(lined + j * wpld, wd, + lineb + j * wplb, 8, thresh); + } + + LEPT_FREE(lineb); + return pixd; +} + + +/*! + * \brief pixScaleGray4xLIDither() + * + * \param[in] pixs 8 bpp, not cmapped + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does 4x upscale on pixs, using linear interpolation,
+ *          followed by Floyd-Steinberg dithering to binary.
+ *      (2) Buffers are used to avoid making a large grayscale image.
+ *          ~ Two line buffers are used for the src, required for the
+ *            4xLI upscale.
+ *          ~ Five line buffers are used for the intermediate image.
+ *            Four are filled with each 4xLI row operation; the fifth
+ *            is needed because the upscale and dithering ops are
+ *            out of sync.
+ *      (3) If a full 4x expanded grayscale image can be kept in memory,
+ *          this function is only about 5% faster than separately doing
+ *          a linear interpolation to a large grayscale image, followed
+ *          by error-diffusion dithering to binary.
+ * 
+ */ +PIX * +pixScaleGray4xLIDither(PIX *pixs) +{ +l_int32 i, j, ws, hs, hsm, wd, hd, wpls, wplb, wpld; +l_uint32 *datas, *datad; +l_uint32 *lined; +l_uint32 *lineb = NULL; /* 4 intermediate buffer lines */ +l_uint32 *linebp = NULL; /* 1 intermediate buffer line */ +l_uint32 *bufs = NULL; /* 2 source buffer lines */ +PIX *pixd = NULL; + + PROCNAME("pixScaleGray4xLIDither"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = 4 * ws; + hd = 4 * hs; + hsm = hs - 1; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + /* Make line buffers for 2 lines of src image */ + if ((bufs = (l_uint32 *)LEPT_CALLOC(2 * wpls, sizeof(l_uint32))) == NULL) + return (PIX *)ERROR_PTR("bufs not made", procName, NULL); + + /* Make line buffer for 4 lines of virtual intermediate image */ + wplb = (wd + 3) / 4; + if ((lineb = (l_uint32 *)LEPT_CALLOC(4 * wplb, sizeof(l_uint32))) == NULL) { + L_ERROR("lineb not made\n", procName); + goto cleanup; + } + + /* Make line buffer for 1 line of virtual intermediate image */ + if ((linebp = (l_uint32 *)LEPT_CALLOC(wplb, sizeof(l_uint32))) == NULL) { + L_ERROR("linebp not made\n", procName); + goto cleanup; + } + + /* Make dest binary image */ + if ((pixd = pixCreate(wd, hd, 1)) == NULL) { + L_ERROR("pixd not made\n", procName); + goto cleanup; + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 4.0, 4.0); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + + /* Start with the first src and the first 3 dest lines */ + memcpy(bufs, datas, 4 * wpls); /* first src line */ + memcpy(bufs + wpls, datas + wpls, 4 * wpls); /* 2nd src line */ + scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ + lined = datad; + for (j = 0; j < 3; j++) { /* first 3 d lines of Q */ + ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, + lineb + (j + 1) * wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + } + + /* Do all but last src line */ + for (i = 1; i < hsm; i++) { + memcpy(bufs, datas + i * wpls, 4 * wpls); /* i-th src line */ + memcpy(bufs + wpls, datas + (i + 1) * wpls, 4 * wpls); + memcpy(linebp, lineb + 3 * wplb, 4 * wplb); + scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 0); /* 4 b lines */ + lined = datad + 4 * i * wpld; + ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* 4th dest line of Q */ + for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ + ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, + lineb + (j + 1) * wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + } + } + + /* Do the last src line and the last 5 dest lines */ + memcpy(bufs, datas + hsm * wpls, 4 * wpls); /* hsm-th src line */ + memcpy(linebp, lineb + 3 * wplb, 4 * wplb); /* 1 b line */ + scaleGray4xLILineLow(lineb, wplb, bufs, ws, wpls, 1); /* 4 b lines */ + lined = datad + 4 * hsm * wpld; + ditherToBinaryLineLow(lined - wpld, wd, linebp, lineb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + /* 4th dest line of Q */ + for (j = 0; j < 3; j++) { /* next 3 d lines of Quad */ + ditherToBinaryLineLow(lined + j * wpld, wd, lineb + j * wplb, + lineb + (j + 1) * wplb, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 0); + } + /* And finally, the last dest line */ + ditherToBinaryLineLow(lined + 3 * wpld, wd, lineb + 3 * wplb, NULL, + DEFAULT_CLIP_LOWER_1, DEFAULT_CLIP_UPPER_1, 1); + +cleanup: + LEPT_FREE(bufs); + LEPT_FREE(lineb); + LEPT_FREE(linebp); + return pixd; +} + + +/*------------------------------------------------------------------* + * Scaling by closest pixel sampling * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleBySampling() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp + * \param[in] scalex must be > 0.0 + * \param[in] scaley must be > 0.0 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function samples from the source without
+ *          filtering.  As a result, aliasing will occur for
+ *          subsampling (%scalex and/or %scaley < 1.0).
+ *      (2) If %scalex == 1.0 and %scaley == 1.0, returns a copy.
+ * 
+ */ +PIX * +pixScaleBySampling(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, d, wpls, wd, hd, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleBySampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factor <= 0", procName, NULL); + if (scalex == 1.0 && scaley == 1.0) + return pixCopy(NULL, pixs); + if ((d = pixGetDepth(pixs)) == 1) + return pixScaleBinary(pixs, scalex, scaley); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixCopySpp(pixd, pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleBySamplingLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls); + if (d == 32 && pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); + + return pixd; +} + + +/*! + * \brief pixScaleBySamplingToSize() + * + * \param[in] pixs 1, 2, 4, 8, 16 and 32 bpp + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This guarantees that the output scaled image has the
+ *          dimension(s) you specify.
+ *           ~ To specify the width with isotropic scaling, set %hd = 0.
+ *           ~ To specify the height with isotropic scaling, set %wd = 0.
+ *           ~ If both %wd and %hd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *           ~ It is an error to set both %wd and %hd to 0.
+ * 
+ */ +PIX * +pixScaleBySamplingToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h; +l_float32 scalex, scaley; + + PROCNAME("pixScaleBySamplingToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (wd <= 0 && hd <= 0) + return (PIX *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (wd <= 0) { + scaley = (l_float32)hd / (l_float32)h; + scalex = scaley; + } else if (hd <= 0) { + scalex = (l_float32)wd / (l_float32)w; + scaley = scalex; + } else { + scalex = (l_float32)wd / (l_float32)w; + scaley = (l_float32)hd / (l_float32)h; + } + + return pixScaleBySampling(pixs, scalex, scaley); +} + + +/*! + * \brief pixScaleByIntSampling() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp + * \param[in] factor integer subsampling + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) Simple interface to pixScaleBySampling(), for
+ *          isotropic integer reduction.
+ *      (2) If %factor == 1, returns a copy.
+ * 
+ */ +PIX * +pixScaleByIntSampling(PIX *pixs, + l_int32 factor) +{ +l_float32 scale; + + PROCNAME("pixScaleByIntSampling"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor <= 1) { + if (factor < 1) + L_ERROR("factor must be >= 1; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + scale = 1. / (l_float32)factor; + return pixScaleBySampling(pixs, scale, scale); +} + + +/*------------------------------------------------------------------* + * Fast integer factor subsampling RGB to gray * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleRGBToGrayFast() + * + * \param[in] pixs 32 bpp rgb + * \param[in] factor integer reduction factor >= 1 + * \param[in] color one of COLOR_RED, COLOR_GREEN, COLOR_BLUE + * \return pixd 8 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does simultaneous subsampling by an integer factor and
+ *          extraction of the color from the RGB pix.
+ *      (2) It is designed for maximum speed, and is used for quickly
+ *          generating a downsized grayscale image from a higher resolution
+ *          RGB image.  This would typically be used for image analysis.
+ *      (3) The standard color byte order (RGBA) is assumed.
+ * 
+ */ +PIX * +pixScaleRGBToGrayFast(PIX *pixs, + l_int32 factor, + l_int32 color) +{ +l_int32 byteval, shift; +l_int32 i, j, ws, hs, wd, hd, wpls, wpld; +l_uint32 *datas, *words, *datad, *lined; +l_float32 scale; +PIX *pixd; + + PROCNAME("pixScaleRGBToGrayFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + + if (color == COLOR_RED) + shift = L_RED_SHIFT; + else if (color == COLOR_GREEN) + shift = L_GREEN_SHIFT; + else if (color == COLOR_BLUE) + shift = L_BLUE_SHIFT; + else + return (PIX *)ERROR_PTR("invalid color", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + wd = ws / factor; + hd = hs / factor; + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + scale = 1. / (l_float32) factor; + pixScaleResolution(pixd, scale, scale); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < hd; i++) { + words = datas + i * factor * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++, words += factor) { + byteval = ((*words) >> shift) & 0xff; + SET_DATA_BYTE(lined, j, byteval); + } + } + + return pixd; +} + + +/*! + * \brief pixScaleRGBToBinaryFast() + * + * \param[in] pixs 32 bpp RGB + * \param[in] factor integer reduction factor >= 1 + * \param[in] thresh binarization threshold + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does simultaneous subsampling by an integer factor and
+ *          conversion from RGB to gray to binary.
+ *      (2) It is designed for maximum speed, and is used for quickly
+ *          generating a downsized binary image from a higher resolution
+ *          RGB image.  This would typically be used for image analysis.
+ *      (3) It uses the green channel to represent the RGB pixel intensity.
+ * 
+ */ +PIX * +pixScaleRGBToBinaryFast(PIX *pixs, + l_int32 factor, + l_int32 thresh) +{ +l_int32 byteval; +l_int32 i, j, ws, hs, wd, hd, wpls, wpld; +l_uint32 *datas, *words, *datad, *lined; +l_float32 scale; +PIX *pixd; + + PROCNAME("pixScaleRGBToBinaryFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + wd = ws / factor; + hd = hs / factor; + if ((pixd = pixCreate(wd, hd, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + scale = 1. / (l_float32) factor; + pixScaleResolution(pixd, scale, scale); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < hd; i++) { + words = datas + i * factor * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++, words += factor) { + byteval = ((*words) >> L_GREEN_SHIFT) & 0xff; + if (byteval < thresh) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*! + * \brief pixScaleGrayToBinaryFast() + * + * \param[in] pixs 8 bpp grayscale + * \param[in] factor integer reduction factor >= 1 + * \param[in] thresh binarization threshold + * \return pixd 1 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This does simultaneous subsampling by an integer factor and
+ *          thresholding from gray to binary.
+ *      (2) It is designed for maximum speed, and is used for quickly
+ *          generating a downsized binary image from a higher resolution
+ *          gray image.  This would typically be used for image analysis.
+ * 
+ */ +PIX * +pixScaleGrayToBinaryFast(PIX *pixs, + l_int32 factor, + l_int32 thresh) +{ +l_int32 byteval; +l_int32 i, j, ws, hs, wd, hd, wpls, wpld, sj; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 scale; +PIX *pixd; + + PROCNAME("pixScaleGrayToBinaryFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (factor < 1) + return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + + wd = ws / factor; + hd = hs / factor; + if ((pixd = pixCreate(wd, hd, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + scale = 1. / (l_float32) factor; + pixScaleResolution(pixd, scale, scale); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < hd; i++) { + lines = datas + i * factor * wpls; + lined = datad + i * wpld; + for (j = 0, sj = 0; j < wd; j++, sj += factor) { + byteval = GET_DATA_BYTE(lines, sj); + if (byteval < thresh) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*------------------------------------------------------------------* + * Downscaling with (antialias) smoothing * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleSmooth() + * + * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap + * \param[in] scalex must be < 0.7 + * \param[in] scaley must be < 0.7 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function should only be used when the scale factors are less
+ *          than or equal to 0.7 (i.e., more than about 1.42x reduction).
+ *          If either scale factor is larger than 0.7, we issue a warning
+ *          and call pixScaleGeneral(), which will invoke linear
+ *          interpolation without sharpening.
+ *      (2) This works only on 2, 4, 8 and 32 bpp images, and if there is
+ *          a colormap, it is removed by converting to RGB.  In other
+ *          cases, we issue a warning and call pixScaleGeneral().
+ *      (3) It does simple (flat filter) convolution, with a filter size
+ *          commensurate with the amount of reduction, to avoid antialiasing.
+ *      (4) It does simple subsampling after smoothing, which is appropriate
+ *          for this range of scaling.  Linear interpolation gives essentially
+ *          the same result with more computation for these scale factors,
+ *          so we don't use it.
+ *      (5) The result is the same as doing a full block convolution followed by
+ *          subsampling, but this is faster because the results of the block
+ *          convolution are only computed at the subsampling locations.
+ *          In fact, the computation time is approximately independent of
+ *          the scale factor, because the convolution kernel is adjusted
+ *          so that each source pixel is summed approximately once.
+ * 
+ */ +PIX * +pixScaleSmooth(PIX *pix, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, d, wd, hd, wpls, wpld, isize; +l_uint32 *datas, *datad; +l_float32 minscale, size; +PIX *pixs, *pixd; + + PROCNAME("pixScaleSmooth"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + if (scalex >= 0.7 || scaley >= 0.7) { + L_WARNING("scaling factor not < 0.7; do regular scaling\n", procName); + return pixScaleGeneral(pix, scalex, scaley, 0.0, 0); + } + + /* Remove colormap if necessary. + * If 2 bpp or 4 bpp gray, convert to 8 bpp */ + d = pixGetDepth(pix); + if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { + L_WARNING("pix has colormap; removing\n", procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixs); + } else if (d == 2 || d == 4) { + pixs = pixConvertTo8(pix, FALSE); + d = 8; + } else { + pixs = pixClone(pix); + } + + if (d != 8 && d != 32) { /* d == 1 or d == 16 */ + L_WARNING("depth not 8 or 32 bpp; do regular scaling\n", procName); + pixDestroy(&pixs); + return pixScaleGeneral(pix, scalex, scaley, 0.0, 0); + } + + /* If 1.42 < 1/minscale < 2.5, use isize = 2 + * If 2.5 =< 1/minscale < 3.5, use isize = 3, etc. + * Under no conditions use isize < 2 */ + minscale = L_MIN(scalex, scaley); + size = 1.0 / minscale; /* ideal filter full width */ + isize = L_MAX(2, (l_int32)(size + 0.5)); + + pixGetDimensions(pixs, &ws, &hs, NULL); + if ((ws < isize) || (hs < isize)) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + } + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if (wd < 1 || hd < 1) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixd too small", procName, NULL); + } + if ((pixd = pixCreate(wd, hd, d)) == NULL) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleSmoothLow(datad, wd, hd, wpld, datas, ws, hs, d, wpls, isize); + if (d == 32 && pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); + + pixDestroy(&pixs); + return pixd; +} + + +/*! + * \brief pixScaleSmoothToSize() + * + * \param[in] pixs 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes in pixScaleSmooth().
+ *      (2) The output scaled image has the dimension(s) you specify:
+ *          * To specify the width with isotropic scaling, set %hd = 0.
+ *          * To specify the height with isotropic scaling, set %wd = 0.
+ *          * If both %wd and %hd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *          * It is an error to set both %wd and %hd to 0.
+ * 
+ */ +PIX * +pixScaleSmoothToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h; +l_float32 scalex, scaley; + + PROCNAME("pixScaleSmoothToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (wd <= 0 && hd <= 0) + return (PIX *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (wd <= 0) { + scaley = (l_float32)hd / (l_float32)h; + scalex = scaley; + } else if (hd <= 0) { + scalex = (l_float32)wd / (l_float32)w; + scaley = scalex; + } else { + scalex = (l_float32)wd / (l_float32)w; + scaley = (l_float32)hd / (l_float32)h; + } + + return pixScaleSmooth(pixs, scalex, scaley); +} + + +/*! + * \brief pixScaleRGBToGray2() + * + * \param[in] pixs 32 bpp rgb + * \param[in] rwt, gwt, bwt must sum to 1.0 + * \return pixd, 8 bpp, 2x reduced, or NULL on error + */ +PIX * +pixScaleRGBToGray2(PIX *pixs, + l_float32 rwt, + l_float32 gwt, + l_float32 bwt) +{ +l_int32 wd, hd, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleRGBToGray2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); + if (rwt + gwt + bwt < 0.98 || rwt + gwt + bwt > 1.02) + return (PIX *)ERROR_PTR("sum of wts should be 1.0", procName, NULL); + + wd = pixGetWidth(pixs) / 2; + hd = pixGetHeight(pixs) / 2; + wpls = pixGetWpl(pixs); + datas = pixGetData(pixs); + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyResolution(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixScaleResolution(pixd, 0.5, 0.5); + wpld = pixGetWpl(pixd); + datad = pixGetData(pixd); + scaleRGBToGray2Low(datad, wd, hd, wpld, datas, wpls, rwt, gwt, bwt); + return pixd; +} + + +/*------------------------------------------------------------------* + * Downscaling with (antialias) area mapping * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleAreaMap() + * + * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap + * \param[in] scalex must be <= 0.7 + * \param[in] scaley must be <= 0.7 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function should only be used when the scale factors are less
+ *          than or equal to 0.7 (i.e., more than about 1.42x reduction).
+ *          If either scale factor is larger than 0.7, we issue a warning
+ *          and call pixScaleGeneral(), which will invoke linear
+ *          interpolation without sharpening.
+ *      (2) This works only on 2, 4, 8 and 32 bpp images.  If there is
+ *          a colormap, it is removed by converting to RGB.  In other
+ *          cases, we issue a warning and call pixScaleGeneral().
+ *      (3) This is faster than pixScale() because it does not do sharpening.
+ *      (4) It does a relatively expensive area mapping computation, to
+ *          avoid antialiasing.  It is about 2x slower than pixScaleSmooth(),
+ *          but the results are much better on fine text.
+ *      (5) This is typically about 20% faster for the special cases of
+ *          2x, 4x, 8x and 16x reduction.
+ *      (6) Surprisingly, there is no speedup (and a slight quality
+ *          impairment) if you do as many successive 2x reductions as
+ *          possible, ending with a reduction with a scale factor larger
+ *          than 0.5.
+ * 
+ */ +PIX * +pixScaleAreaMap(PIX *pix, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, d, wd, hd, wpls, wpld; +l_uint32 *datas, *datad; +l_float32 maxscale; +PIX *pixs, *pixd, *pixt1, *pixt2, *pixt3; + + PROCNAME("pixScaleAreaMap"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + d = pixGetDepth(pix); + if (d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL); + maxscale = L_MAX(scalex, scaley); + if (maxscale >= 0.7) { + L_WARNING("scaling factors not < 0.7; do regular scaling\n", procName); + return pixScaleGeneral(pix, scalex, scaley, 0.0, 0); + } + + /* Special cases: 2x, 4x, 8x, 16x reduction */ + if (scalex == 0.5 && scaley == 0.5) + return pixScaleAreaMap2(pix); + if (scalex == 0.25 && scaley == 0.25) { + pixt1 = pixScaleAreaMap2(pix); + pixd = pixScaleAreaMap2(pixt1); + pixDestroy(&pixt1); + return pixd; + } + if (scalex == 0.125 && scaley == 0.125) { + pixt1 = pixScaleAreaMap2(pix); + pixt2 = pixScaleAreaMap2(pixt1); + pixd = pixScaleAreaMap2(pixt2); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + return pixd; + } + if (scalex == 0.0625 && scaley == 0.0625) { + pixt1 = pixScaleAreaMap2(pix); + pixt2 = pixScaleAreaMap2(pixt1); + pixt3 = pixScaleAreaMap2(pixt2); + pixd = pixScaleAreaMap2(pixt3); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + return pixd; + } + + /* Remove colormap if necessary. + * If 2 bpp or 4 bpp gray, convert to 8 bpp */ + if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { + L_WARNING("pix has colormap; removing\n", procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixs); + } else if (d == 2 || d == 4) { + pixs = pixConvertTo8(pix, FALSE); + d = 8; + } else { + pixs = pixClone(pix); + } + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if (wd < 1 || hd < 1) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixd too small", procName, NULL); + } + if ((pixd = pixCreate(wd, hd, d)) == NULL) { + pixDestroy(&pixs); + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + if (d == 8) { + scaleGrayAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); + } else { /* RGB, d == 32 */ + scaleColorAreaMapLow(datad, wd, hd, wpld, datas, ws, hs, wpls); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); + } + + pixDestroy(&pixs); + return pixd; +} + + +/*! + * \brief pixScaleAreaMap2() + * + * \param[in] pix 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function does an area mapping (average) for 2x
+ *          reduction.
+ *      (2) This works only on 2, 4, 8 and 32 bpp images.  If there is
+ *          a colormap, it is removed by converting to RGB.
+ *      (3) Speed on 3 GHz processor:
+ *             Color: 160 Mpix/sec
+ *             Gray: 700 Mpix/sec
+ *          This contrasts with the speed of the general pixScaleAreaMap():
+ *             Color: 35 Mpix/sec
+ *             Gray: 50 Mpix/sec
+ *      (4) From (3), we see that this special function is about 4.5x
+ *          faster for color and 14x faster for grayscale
+ *      (5) Consequently, pixScaleAreaMap2() is incorporated into the
+ *          general area map scaling function, for the special cases
+ *          of 2x, 4x, 8x and 16x reduction.
+ * 
+ */ +PIX * +pixScaleAreaMap2(PIX *pix) +{ +l_int32 wd, hd, d, wpls, wpld; +l_uint32 *datas, *datad; +PIX *pixs, *pixd; + + PROCNAME("pixScaleAreaMap2"); + + if (!pix) + return (PIX *)ERROR_PTR("pix not defined", procName, NULL); + d = pixGetDepth(pix); + if (d != 2 && d != 4 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pix not 2, 4, 8 or 32 bpp", procName, NULL); + + /* Remove colormap if necessary. + * If 2 bpp or 4 bpp gray, convert to 8 bpp */ + if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { + L_WARNING("pix has colormap; removing\n", procName); + pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); + d = pixGetDepth(pixs); + } else if (d == 2 || d == 4) { + pixs = pixConvertTo8(pix, FALSE); + d = 8; + } else { + pixs = pixClone(pix); + } + + wd = pixGetWidth(pixs) / 2; + hd = pixGetHeight(pixs) / 2; + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + pixd = pixCreate(wd, hd, d); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.5, 0.5); + scaleAreaMapLow2(datad, wd, hd, wpld, datas, d, wpls); + if (pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, 0.5, 0.5); + pixDestroy(&pixs); + return pixd; +} + + +/*! + * \brief pixScaleAreaMapToSize() + * + * \param[in] pixs 2, 4, 8 or 32 bpp; and 2, 4, 8 bpp with colormap + * \param[in] wd target width; use 0 if using height as target + * \param[in] hd target height; use 0 if using width as target + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes in pixScaleAreaMap().
+ *      (2) The output scaled image has the dimension(s) you specify:
+ *          * To specify the width with isotropic scaling, set %hd = 0.
+ *          * To specify the height with isotropic scaling, set %wd = 0.
+ *          * If both %wd and %hd are specified, the image is scaled
+ *             (in general, anisotropically) to that size.
+ *          * It is an error to set both %wd and %hd to 0.
+ * 
+ */ +PIX * +pixScaleAreaMapToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h; +l_float32 scalex, scaley; + + PROCNAME("pixScaleAreaMapToSize"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (wd <= 0 && hd <= 0) + return (PIX *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if (wd <= 0) { + scaley = (l_float32)hd / (l_float32)h; + scalex = scaley; + } else if (hd <= 0) { + scalex = (l_float32)wd / (l_float32)w; + scaley = scalex; + } else { + scalex = (l_float32)wd / (l_float32)w; + scaley = (l_float32)hd / (l_float32)h; + } + + return pixScaleAreaMap(pixs, scalex, scaley); +} + + +/*------------------------------------------------------------------* + * Binary scaling by closest pixel sampling * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleBinary() + * + * \param[in] pixs 1 bpp + * \param[in] scalex must be > 0.0 + * \param[in] scaley must be > 0.0 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This function samples from the source without
+ *          filtering.  As a result, aliasing will occur for
+ *          subsampling (scalex and scaley < 1.0).
+ * 
+ */ +PIX * +pixScaleBinary(PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 ws, hs, wpls, wd, hd, wpld; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleBinary"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factor <= 0", procName, NULL); + if (scalex == 1.0 && scaley == 1.0) + return pixCopy(NULL, pixs); + + pixGetDimensions(pixs, &ws, &hs, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + wd = (l_int32)(scalex * (l_float32)ws + 0.5); + hd = (l_int32)(scaley * (l_float32)hs + 0.5); + if ((pixd = pixCreate(wd, hd, 1)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyColormap(pixd, pixs); + pixCopyText(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, scalex, scaley); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + scaleBinaryLow(datad, wd, hd, wpld, datas, ws, hs, wpls); + return pixd; +} + + +/* ================================================================ * + * Low level static functions * + * ================================================================ */ + +/*------------------------------------------------------------------* + * General linear interpolated color scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleColorLILow() + * + * We choose to divide each pixel into 16 x 16 sub-pixels. + * Linear interpolation is equivalent to finding the + * fractional area (i.e., number of sub-pixels divided + * by 256) associated with each of the four nearest src pixels, + * and weighting each pixel value by this fractional area. + * + * P3 speed is about 7 x 10^6 dst pixels/sec/GHz + */ +static void +scaleColorLILow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, j, wm2, hm2; +l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */ +l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */ +l_int32 v00r, v01r, v10r, v11r, v00g, v01g, v10g, v11g; +l_int32 v00b, v01b, v10b, v11b, area00, area01, area10, area11; +l_uint32 pixels1, pixels2, pixels3, pixels4, pixel; +l_uint32 *lines, *lined; +l_float32 scx, scy; + + /* (scx, scy) are scaling factors that are applied to the + * dest coords to get the corresponding src coords. + * We need them because we iterate over dest pixels + * and must find the corresponding set of src pixels. */ + scx = 16. * (l_float32)ws / (l_float32)wd; + scy = 16. * (l_float32)hs / (l_float32)hd; + wm2 = ws - 2; + hm2 = hs - 2; + + /* Iterate over the destination pixels */ + for (i = 0; i < hd; i++) { + ypm = (l_int32)(scy * (l_float32)i); + yp = ypm >> 4; + yf = ypm & 0x0f; + lined = datad + i * wpld; + lines = datas + yp * wpls; + for (j = 0; j < wd; j++) { + xpm = (l_int32)(scx * (l_float32)j); + xp = xpm >> 4; + xf = xpm & 0x0f; + + /* Do bilinear interpolation. This is a simple + * generalization of the calculation in scaleGrayLILow(). + * Without this, we could simply subsample: + * *(lined + j) = *(lines + xp); + * which is faster but gives lousy results! */ + pixels1 = *(lines + xp); + + if (xp > wm2 || yp > hm2) { + if (yp > hm2 && xp <= wm2) { /* pixels near bottom */ + pixels2 = *(lines + xp + 1); + pixels3 = pixels1; + pixels4 = pixels2; + } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */ + pixels2 = pixels1; + pixels3 = *(lines + wpls + xp); + pixels4 = pixels3; + } else { /* pixels at LR corner */ + pixels4 = pixels3 = pixels2 = pixels1; + } + } else { + pixels2 = *(lines + xp + 1); + pixels3 = *(lines + wpls + xp); + pixels4 = *(lines + wpls + xp + 1); + } + + area00 = (16 - xf) * (16 - yf); + area10 = xf * (16 - yf); + area01 = (16 - xf) * yf; + area11 = xf * yf; + v00r = area00 * ((pixels1 >> L_RED_SHIFT) & 0xff); + v00g = area00 * ((pixels1 >> L_GREEN_SHIFT) & 0xff); + v00b = area00 * ((pixels1 >> L_BLUE_SHIFT) & 0xff); + v10r = area10 * ((pixels2 >> L_RED_SHIFT) & 0xff); + v10g = area10 * ((pixels2 >> L_GREEN_SHIFT) & 0xff); + v10b = area10 * ((pixels2 >> L_BLUE_SHIFT) & 0xff); + v01r = area01 * ((pixels3 >> L_RED_SHIFT) & 0xff); + v01g = area01 * ((pixels3 >> L_GREEN_SHIFT) & 0xff); + v01b = area01 * ((pixels3 >> L_BLUE_SHIFT) & 0xff); + v11r = area11 * ((pixels4 >> L_RED_SHIFT) & 0xff); + v11g = area11 * ((pixels4 >> L_GREEN_SHIFT) & 0xff); + v11b = area11 * ((pixels4 >> L_BLUE_SHIFT) & 0xff); + pixel = (((v00r + v10r + v01r + v11r + 128) << 16) & 0xff000000) | + (((v00g + v10g + v01g + v11g + 128) << 8) & 0x00ff0000) | + ((v00b + v10b + v01b + v11b + 128) & 0x0000ff00); + *(lined + j) = pixel; + } + } +} + + +/*------------------------------------------------------------------* + * General linear interpolated gray scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleGrayLILow() + * + * We choose to divide each pixel into 16 x 16 sub-pixels. + * Linear interpolation is equivalent to finding the + * fractional area (i.e., number of sub-pixels divided + * by 256) associated with each of the four nearest src pixels, + * and weighting each pixel value by this fractional area. + */ +static void +scaleGrayLILow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, j, wm2, hm2; +l_int32 xpm, ypm; /* location in src image, to 1/16 of a pixel */ +l_int32 xp, yp, xf, yf; /* src pixel and pixel fraction coordinates */ +l_int32 v00, v01, v10, v11, v00_val, v01_val, v10_val, v11_val; +l_uint8 val; +l_uint32 *lines, *lined; +l_float32 scx, scy; + + /* (scx, scy) are scaling factors that are applied to the + * dest coords to get the corresponding src coords. + * We need them because we iterate over dest pixels + * and must find the corresponding set of src pixels. */ + scx = 16. * (l_float32)ws / (l_float32)wd; + scy = 16. * (l_float32)hs / (l_float32)hd; + wm2 = ws - 2; + hm2 = hs - 2; + + /* Iterate over the destination pixels */ + for (i = 0; i < hd; i++) { + ypm = (l_int32)(scy * (l_float32)i); + yp = ypm >> 4; + yf = ypm & 0x0f; + lined = datad + i * wpld; + lines = datas + yp * wpls; + for (j = 0; j < wd; j++) { + xpm = (l_int32)(scx * (l_float32)j); + xp = xpm >> 4; + xf = xpm & 0x0f; + + /* Do bilinear interpolation. Without this, we could + * simply subsample: + * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp)); + * which is faster but gives lousy results! */ + v00_val = GET_DATA_BYTE(lines, xp); + if (xp > wm2 || yp > hm2) { + if (yp > hm2 && xp <= wm2) { /* pixels near bottom */ + v01_val = v00_val; + v10_val = GET_DATA_BYTE(lines, xp + 1); + v11_val = v10_val; + } else if (xp > wm2 && yp <= hm2) { /* pixels near rt side */ + v01_val = GET_DATA_BYTE(lines + wpls, xp); + v10_val = v00_val; + v11_val = v01_val; + } else { /* pixels at LR corner */ + v10_val = v01_val = v11_val = v00_val; + } + } else { + v10_val = GET_DATA_BYTE(lines, xp + 1); + v01_val = GET_DATA_BYTE(lines + wpls, xp); + v11_val = GET_DATA_BYTE(lines + wpls, xp + 1); + } + + v00 = (16 - xf) * (16 - yf) * v00_val; + v10 = xf * (16 - yf) * v10_val; + v01 = (16 - xf) * yf * v01_val; + v11 = xf * yf * v11_val; + + val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256); + SET_DATA_BYTE(lined, j, val); + } + } +} + + +/*------------------------------------------------------------------* + * 2x linear interpolated color scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleColor2xLILow() + * + * This is a special case of 2x expansion by linear + * interpolation. Each src pixel contains 4 dest pixels. + * The 4 dest pixels in src pixel 1 are numbered at + * their UL corners. The 4 dest pixels in src pixel 1 + * are related to that src pixel and its 3 neighboring + * src pixels as follows: + * + * 1-----2-----|-----|-----| + * | | | | | + * | | | | | + * src 1 --> 3-----4-----| | | <-- src 2 + * | | | | | + * | | | | | + * |-----|-----|-----|-----| + * | | | | | + * | | | | | + * src 3 --> | | | | | <-- src 4 + * | | | | | + * | | | | | + * |-----|-----|-----|-----| + * + * dest src + * ---- --- + * dp1 = sp1 + * dp2 = (sp1 + sp2) / 2 + * dp3 = (sp1 + sp3) / 2 + * dp4 = (sp1 + sp2 + sp3 + sp4) / 4 + * + * We iterate over the src pixels, and unroll the calculation + * for each set of 4 dest pixels corresponding to that src + * pixel, caching pixels for the next src pixel whenever possible. + * The method is exactly analogous to the one we use for + * scaleGray2xLILow() and its line version. + * + * P3 speed is about 5 x 10^7 dst pixels/sec/GHz + */ +static void +scaleColor2xLILow(l_uint32 *datad, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, hsm; +l_uint32 *lines, *lined; + + hsm = hs - 1; + + /* We're taking 2 src and 2 dest lines at a time, + * and for each src line, we're computing 2 dest lines. + * Call these 2 dest lines: destline1 and destline2. + * The first src line is used for destline 1. + * On all but the last src line, both src lines are + * used in the linear interpolation for destline2. + * On the last src line, both destline1 and destline2 + * are computed using only that src line (because there + * isn't a lower src line). */ + + /* iterate over all but the last src line */ + for (i = 0; i < hsm; i++) { + lines = datas + i * wpls; + lined = datad + 2 * i * wpld; + scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 0); + } + + /* last src line */ + lines = datas + hsm * wpls; + lined = datad + 2 * hsm * wpld; + scaleColor2xLILineLow(lined, wpld, lines, ws, wpls, 1); +} + + +/*! + * \brief scaleColor2xLILineLow() + * + * \param[in] lined ptr to top destline, to be made from current src line + * \param[in] wpld + * \param[in] lines ptr to current src line + * \param[in] ws + * \param[in] wpls + * \param[in] lastlineflag 1 if last src line; 0 otherwise + * \return void + */ +static void +scaleColor2xLILineLow(l_uint32 *lined, + l_int32 wpld, + l_uint32 *lines, + l_int32 ws, + l_int32 wpls, + l_int32 lastlineflag) +{ +l_int32 j, jd, wsm; +l_uint32 rval1, rval2, rval3, rval4, gval1, gval2, gval3, gval4; +l_uint32 bval1, bval2, bval3, bval4; +l_uint32 pixels1, pixels2, pixels3, pixels4, pixel; +l_uint32 *linesp, *linedp; + + wsm = ws - 1; + + if (lastlineflag == 0) { + linesp = lines + wpls; + linedp = lined + wpld; + pixels1 = *lines; + pixels3 = *linesp; + + /* initialize with v(2) and v(4) */ + rval2 = pixels1 >> 24; + gval2 = (pixels1 >> 16) & 0xff; + bval2 = (pixels1 >> 8) & 0xff; + rval4 = pixels3 >> 24; + gval4 = (pixels3 >> 16) & 0xff; + bval4 = (pixels3 >> 8) & 0xff; + + for (j = 0, jd = 0; j < wsm; j++, jd += 2) { + /* shift in previous src values */ + rval1 = rval2; + gval1 = gval2; + bval1 = bval2; + rval3 = rval4; + gval3 = gval4; + bval3 = bval4; + /* get new src values */ + pixels2 = *(lines + j + 1); + pixels4 = *(linesp + j + 1); + rval2 = pixels2 >> 24; + gval2 = (pixels2 >> 16) & 0xff; + bval2 = (pixels2 >> 8) & 0xff; + rval4 = pixels4 >> 24; + gval4 = (pixels4 >> 16) & 0xff; + bval4 = (pixels4 >> 8) & 0xff; + /* save dest values */ + pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); + *(lined + jd) = pixel; /* pix 1 */ + pixel = ((((rval1 + rval2) << 23) & 0xff000000) | + (((gval1 + gval2) << 15) & 0x00ff0000) | + (((bval1 + bval2) << 7) & 0x0000ff00)); + *(lined + jd + 1) = pixel; /* pix 2 */ + pixel = ((((rval1 + rval3) << 23) & 0xff000000) | + (((gval1 + gval3) << 15) & 0x00ff0000) | + (((bval1 + bval3) << 7) & 0x0000ff00)); + *(linedp + jd) = pixel; /* pix 3 */ + pixel = ((((rval1 + rval2 + rval3 + rval4) << 22) & 0xff000000) | + (((gval1 + gval2 + gval3 + gval4) << 14) & 0x00ff0000) | + (((bval1 + bval2 + bval3 + bval4) << 6) & 0x0000ff00)); + *(linedp + jd + 1) = pixel; /* pix 4 */ + } + /* last src pixel on line */ + rval1 = rval2; + gval1 = gval2; + bval1 = bval2; + rval3 = rval4; + gval3 = gval4; + bval3 = bval4; + pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); + *(lined + 2 * wsm) = pixel; /* pix 1 */ + *(lined + 2 * wsm + 1) = pixel; /* pix 2 */ + pixel = ((((rval1 + rval3) << 23) & 0xff000000) | + (((gval1 + gval3) << 15) & 0x00ff0000) | + (((bval1 + bval3) << 7) & 0x0000ff00)); + *(linedp + 2 * wsm) = pixel; /* pix 3 */ + *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */ + } else { /* last row of src pixels: lastlineflag == 1 */ + linedp = lined + wpld; + pixels2 = *lines; + rval2 = pixels2 >> 24; + gval2 = (pixels2 >> 16) & 0xff; + bval2 = (pixels2 >> 8) & 0xff; + for (j = 0, jd = 0; j < wsm; j++, jd += 2) { + rval1 = rval2; + gval1 = gval2; + bval1 = bval2; + pixels2 = *(lines + j + 1); + rval2 = pixels2 >> 24; + gval2 = (pixels2 >> 16) & 0xff; + bval2 = (pixels2 >> 8) & 0xff; + pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); + *(lined + jd) = pixel; /* pix 1 */ + *(linedp + jd) = pixel; /* pix 2 */ + pixel = ((((rval1 + rval2) << 23) & 0xff000000) | + (((gval1 + gval2) << 15) & 0x00ff0000) | + (((bval1 + bval2) << 7) & 0x0000ff00)); + *(lined + jd + 1) = pixel; /* pix 3 */ + *(linedp + jd + 1) = pixel; /* pix 4 */ + } + rval1 = rval2; + gval1 = gval2; + bval1 = bval2; + pixel = (rval1 << 24 | gval1 << 16 | bval1 << 8); + *(lined + 2 * wsm) = pixel; /* pix 1 */ + *(lined + 2 * wsm + 1) = pixel; /* pix 2 */ + *(linedp + 2 * wsm) = pixel; /* pix 3 */ + *(linedp + 2 * wsm + 1) = pixel; /* pix 4 */ + } +} + + +/*------------------------------------------------------------------* + * 2x linear interpolated gray scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleGray2xLILow() + * + * This is a special case of 2x expansion by linear + * interpolation. Each src pixel contains 4 dest pixels. + * The 4 dest pixels in src pixel 1 are numbered at + * their UL corners. The 4 dest pixels in src pixel 1 + * are related to that src pixel and its 3 neighboring + * src pixels as follows: + * + * 1-----2-----|-----|-----| + * | | | | | + * | | | | | + * src 1 --> 3-----4-----| | | <-- src 2 + * | | | | | + * | | | | | + * |-----|-----|-----|-----| + * | | | | | + * | | | | | + * src 3 --> | | | | | <-- src 4 + * | | | | | + * | | | | | + * |-----|-----|-----|-----| + * + * dest src + * ---- --- + * dp1 = sp1 + * dp2 = (sp1 + sp2) / 2 + * dp3 = (sp1 + sp3) / 2 + * dp4 = (sp1 + sp2 + sp3 + sp4) / 4 + * + * We iterate over the src pixels, and unroll the calculation + * for each set of 4 dest pixels corresponding to that src + * pixel, caching pixels for the next src pixel whenever possible. + */ +static void +scaleGray2xLILow(l_uint32 *datad, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, hsm; +l_uint32 *lines, *lined; + + hsm = hs - 1; + + /* We're taking 2 src and 2 dest lines at a time, + * and for each src line, we're computing 2 dest lines. + * Call these 2 dest lines: destline1 and destline2. + * The first src line is used for destline 1. + * On all but the last src line, both src lines are + * used in the linear interpolation for destline2. + * On the last src line, both destline1 and destline2 + * are computed using only that src line (because there + * isn't a lower src line). */ + + /* iterate over all but the last src line */ + for (i = 0; i < hsm; i++) { + lines = datas + i * wpls; + lined = datad + 2 * i * wpld; + scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 0); + } + + /* last src line */ + lines = datas + hsm * wpls; + lined = datad + 2 * hsm * wpld; + scaleGray2xLILineLow(lined, wpld, lines, ws, wpls, 1); +} + + +/*! + * \brief scaleGray2xLILineLow() + * + * \param[in] lined ptr to top destline, to be made from current src line + * \param[in] wpld + * \param[in] lines ptr to current src line + * \param[in] ws + * \param[in] wpls + * \param[in] lastlineflag 1 if last src line; 0 otherwise + * \return void + */ +static void +scaleGray2xLILineLow(l_uint32 *lined, + l_int32 wpld, + l_uint32 *lines, + l_int32 ws, + l_int32 wpls, + l_int32 lastlineflag) +{ +l_int32 j, jd, wsm, w; +l_uint32 sval1, sval2, sval3, sval4; +l_uint32 *linesp, *linedp; +l_uint32 words, wordsp, wordd, worddp; + + wsm = ws - 1; + + if (lastlineflag == 0) { + linesp = lines + wpls; + linedp = lined + wpld; + + /* Unroll the loop 4x and work on full words */ + words = lines[0]; + wordsp = linesp[0]; + sval2 = (words >> 24) & 0xff; + sval4 = (wordsp >> 24) & 0xff; + for (j = 0, jd = 0, w = 0; j + 3 < wsm; j += 4, jd += 8, w++) { + /* At the top of the loop, + * words == lines[w], wordsp == linesp[w] + * and the top bytes of those have been loaded into + * sval2 and sval4. */ + sval1 = sval2; + sval2 = (words >> 16) & 0xff; + sval3 = sval4; + sval4 = (wordsp >> 16) & 0xff; + wordd = (sval1 << 24) | (((sval1 + sval2) >> 1) << 16); + worddp = (((sval1 + sval3) >> 1) << 24) | + (((sval1 + sval2 + sval3 + sval4) >> 2) << 16); + + sval1 = sval2; + sval2 = (words >> 8) & 0xff; + sval3 = sval4; + sval4 = (wordsp >> 8) & 0xff; + wordd |= (sval1 << 8) | ((sval1 + sval2) >> 1); + worddp |= (((sval1 + sval3) >> 1) << 8) | + ((sval1 + sval2 + sval3 + sval4) >> 2); + lined[w * 2] = wordd; + linedp[w * 2] = worddp; + + sval1 = sval2; + sval2 = words & 0xff; + sval3 = sval4; + sval4 = wordsp & 0xff; + wordd = (sval1 << 24) | /* pix 1 */ + (((sval1 + sval2) >> 1) << 16); /* pix 2 */ + worddp = (((sval1 + sval3) >> 1) << 24) | /* pix 3 */ + (((sval1 + sval2 + sval3 + sval4) >> 2) << 16); /* pix 4 */ + + /* Load the next word as we need its first byte */ + words = lines[w + 1]; + wordsp = linesp[w + 1]; + sval1 = sval2; + sval2 = (words >> 24) & 0xff; + sval3 = sval4; + sval4 = (wordsp >> 24) & 0xff; + wordd |= (sval1 << 8) | /* pix 1 */ + ((sval1 + sval2) >> 1); /* pix 2 */ + worddp |= (((sval1 + sval3) >> 1) << 8) | /* pix 3 */ + ((sval1 + sval2 + sval3 + sval4) >> 2); /* pix 4 */ + lined[w * 2 + 1] = wordd; + linedp[w * 2 + 1] = worddp; + } + + /* Finish up the last word */ + for (; j < wsm; j++, jd += 2) { + sval1 = sval2; + sval3 = sval4; + sval2 = GET_DATA_BYTE(lines, j + 1); + sval4 = GET_DATA_BYTE(linesp, j + 1); + SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */ + SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ + SET_DATA_BYTE(linedp, jd, (sval1 + sval3) / 2); /* pix 3 */ + SET_DATA_BYTE(linedp, jd + 1, + (sval1 + sval2 + sval3 + sval4) / 4); /* pix 4 */ + } + sval1 = sval2; + sval3 = sval4; + SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ + SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ + SET_DATA_BYTE(linedp, 2 * wsm, (sval1 + sval3) / 2); /* pix 3 */ + SET_DATA_BYTE(linedp, 2 * wsm + 1, (sval1 + sval3) / 2); /* pix 4 */ + +#if DEBUG_UNROLLING +#define CHECK_BYTE(a, b, c) if (GET_DATA_BYTE(a, b) != c) {\ + fprintf(stderr, "Error: mismatch at %d, %d vs %d\n", \ + j, GET_DATA_BYTE(a, b), c); } + + sval2 = GET_DATA_BYTE(lines, 0); + sval4 = GET_DATA_BYTE(linesp, 0); + for (j = 0, jd = 0; j < wsm; j++, jd += 2) { + sval1 = sval2; + sval3 = sval4; + sval2 = GET_DATA_BYTE(lines, j + 1); + sval4 = GET_DATA_BYTE(linesp, j + 1); + CHECK_BYTE(lined, jd, sval1); /* pix 1 */ + CHECK_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ + CHECK_BYTE(linedp, jd, (sval1 + sval3) / 2); /* pix 3 */ + CHECK_BYTE(linedp, jd + 1, + (sval1 + sval2 + sval3 + sval4) / 4); /* pix 4 */ + } + sval1 = sval2; + sval3 = sval4; + CHECK_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ + CHECK_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ + CHECK_BYTE(linedp, 2 * wsm, (sval1 + sval3) / 2); /* pix 3 */ + CHECK_BYTE(linedp, 2 * wsm + 1, (sval1 + sval3) / 2); /* pix 4 */ +#undef CHECK_BYTE +#endif + } else { /* last row of src pixels: lastlineflag == 1 */ + linedp = lined + wpld; + sval2 = GET_DATA_BYTE(lines, 0); + for (j = 0, jd = 0; j < wsm; j++, jd += 2) { + sval1 = sval2; + sval2 = GET_DATA_BYTE(lines, j + 1); + SET_DATA_BYTE(lined, jd, sval1); /* pix 1 */ + SET_DATA_BYTE(linedp, jd, sval1); /* pix 3 */ + SET_DATA_BYTE(lined, jd + 1, (sval1 + sval2) / 2); /* pix 2 */ + SET_DATA_BYTE(linedp, jd + 1, (sval1 + sval2) / 2); /* pix 4 */ + } + sval1 = sval2; + SET_DATA_BYTE(lined, 2 * wsm, sval1); /* pix 1 */ + SET_DATA_BYTE(lined, 2 * wsm + 1, sval1); /* pix 2 */ + SET_DATA_BYTE(linedp, 2 * wsm, sval1); /* pix 3 */ + SET_DATA_BYTE(linedp, 2 * wsm + 1, sval1); /* pix 4 */ + } +} + + +/*------------------------------------------------------------------* + * 4x linear interpolated gray scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleGray4xLILow() + * + * This is a special case of 4x expansion by linear + * interpolation. Each src pixel contains 16 dest pixels. + * The 16 dest pixels in src pixel 1 are numbered at + * their UL corners. The 16 dest pixels in src pixel 1 + * are related to that src pixel and its 3 neighboring + * src pixels as follows: + * + * 1---2---3---4---|---|---|---|---| + * | | | | | | | | | + * 5---6---7---8---|---|---|---|---| + * | | | | | | | | | + * src 1 --> 9---a---b---c---|---|---|---|---| <-- src 2 + * | | | | | | | | | + * d---e---f---g---|---|---|---|---| + * | | | | | | | | | + * |===|===|===|===|===|===|===|===| + * | | | | | | | | | + * |---|---|---|---|---|---|---|---| + * | | | | | | | | | + * src 3 --> |---|---|---|---|---|---|---|---| <-- src 4 + * | | | | | | | | | + * |---|---|---|---|---|---|---|---| + * | | | | | | | | | + * |---|---|---|---|---|---|---|---| + * + * dest src + * ---- --- + * dp1 = sp1 + * dp2 = (3 * sp1 + sp2) / 4 + * dp3 = (sp1 + sp2) / 2 + * dp4 = (sp1 + 3 * sp2) / 4 + * dp5 = (3 * sp1 + sp3) / 4 + * dp6 = (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16 + * dp7 = (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8 + * dp8 = (3 * sp1 + 9 * sp2 + 1 * sp3 + 3 * sp4) / 16 + * dp9 = (sp1 + sp3) / 2 + * dp10 = (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8 + * dp11 = (sp1 + sp2 + sp3 + sp4) / 4 + * dp12 = (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8 + * dp13 = (sp1 + 3 * sp3) / 4 + * dp14 = (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16 + * dp15 = (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8 + * dp16 = (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16 + * + * We iterate over the src pixels, and unroll the calculation + * for each set of 16 dest pixels corresponding to that src + * pixel, caching pixels for the next src pixel whenever possible. + */ +static void +scaleGray4xLILow(l_uint32 *datad, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, hsm; +l_uint32 *lines, *lined; + + hsm = hs - 1; + + /* We're taking 2 src and 4 dest lines at a time, + * and for each src line, we're computing 4 dest lines. + * Call these 4 dest lines: destline1 - destline4. + * The first src line is used for destline 1. + * Two src lines are used for all other dest lines, + * except for the last 4 dest lines, which are computed + * using only the last src line. */ + + /* iterate over all but the last src line */ + for (i = 0; i < hsm; i++) { + lines = datas + i * wpls; + lined = datad + 4 * i * wpld; + scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 0); + } + + /* last src line */ + lines = datas + hsm * wpls; + lined = datad + 4 * hsm * wpld; + scaleGray4xLILineLow(lined, wpld, lines, ws, wpls, 1); +} + + +/*! + * \brief scaleGray4xLILineLow() + * + * \param[in] lined ptr to top destline, to be made from current src line + * \param[in] wpld + * \param[in] lines ptr to current src line + * \param[in] ws + * \param[in] wpls + * \param[in] lastlineflag 1 if last src line; 0 otherwise + * \return void + */ +static void +scaleGray4xLILineLow(l_uint32 *lined, + l_int32 wpld, + l_uint32 *lines, + l_int32 ws, + l_int32 wpls, + l_int32 lastlineflag) +{ +l_int32 j, jd, wsm, wsm4; +l_int32 s1, s2, s3, s4, s1t, s2t, s3t, s4t; +l_uint32 *linesp, *linedp1, *linedp2, *linedp3; + + wsm = ws - 1; + wsm4 = 4 * wsm; + + if (lastlineflag == 0) { + linesp = lines + wpls; + linedp1 = lined + wpld; + linedp2 = lined + 2 * wpld; + linedp3 = lined + 3 * wpld; + s2 = GET_DATA_BYTE(lines, 0); + s4 = GET_DATA_BYTE(linesp, 0); + for (j = 0, jd = 0; j < wsm; j++, jd += 4) { + s1 = s2; + s3 = s4; + s2 = GET_DATA_BYTE(lines, j + 1); + s4 = GET_DATA_BYTE(linesp, j + 1); + s1t = 3 * s1; + s2t = 3 * s2; + s3t = 3 * s3; + s4t = 3 * s4; + SET_DATA_BYTE(lined, jd, s1); /* d1 */ + SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4); /* d2 */ + SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2); /* d3 */ + SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4); /* d4 */ + SET_DATA_BYTE(linedp1, jd, (s1t + s3) / 4); /* d5 */ + SET_DATA_BYTE(linedp1, jd + 1, (9*s1 + s2t + s3t + s4) / 16); /*d6*/ + SET_DATA_BYTE(linedp1, jd + 2, (s1t + s2t + s3 + s4) / 8); /* d7 */ + SET_DATA_BYTE(linedp1, jd + 3, (s1t + 9*s2 + s3 + s4t) / 16);/*d8*/ + SET_DATA_BYTE(linedp2, jd, (s1 + s3) / 2); /* d9 */ + SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2 + s3t + s4) / 8);/* d10 */ + SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2 + s3 + s4) / 4); /* d11 */ + SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t + s3 + s4t) / 8);/* d12 */ + SET_DATA_BYTE(linedp3, jd, (s1 + s3t) / 4); /* d13 */ + SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2 + 9*s3 + s4t) / 16);/*d14*/ + SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2 + s3t + s4t) / 8); /* d15 */ + SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t + s3t + 9*s4) / 16);/*d16*/ + } + s1 = s2; + s3 = s4; + s1t = 3 * s1; + s3t = 3 * s3; + SET_DATA_BYTE(lined, wsm4, s1); /* d1 */ + SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */ + SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */ + SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */ + SET_DATA_BYTE(linedp1, wsm4, (s1t + s3) / 4); /* d5 */ + SET_DATA_BYTE(linedp1, wsm4 + 1, (s1t + s3) / 4); /* d6 */ + SET_DATA_BYTE(linedp1, wsm4 + 2, (s1t + s3) / 4); /* d7 */ + SET_DATA_BYTE(linedp1, wsm4 + 3, (s1t + s3) / 4); /* d8 */ + SET_DATA_BYTE(linedp2, wsm4, (s1 + s3) / 2); /* d9 */ + SET_DATA_BYTE(linedp2, wsm4 + 1, (s1 + s3) / 2); /* d10 */ + SET_DATA_BYTE(linedp2, wsm4 + 2, (s1 + s3) / 2); /* d11 */ + SET_DATA_BYTE(linedp2, wsm4 + 3, (s1 + s3) / 2); /* d12 */ + SET_DATA_BYTE(linedp3, wsm4, (s1 + s3t) / 4); /* d13 */ + SET_DATA_BYTE(linedp3, wsm4 + 1, (s1 + s3t) / 4); /* d14 */ + SET_DATA_BYTE(linedp3, wsm4 + 2, (s1 + s3t) / 4); /* d15 */ + SET_DATA_BYTE(linedp3, wsm4 + 3, (s1 + s3t) / 4); /* d16 */ + } else { /* last row of src pixels: lastlineflag == 1 */ + linedp1 = lined + wpld; + linedp2 = lined + 2 * wpld; + linedp3 = lined + 3 * wpld; + s2 = GET_DATA_BYTE(lines, 0); + for (j = 0, jd = 0; j < wsm; j++, jd += 4) { + s1 = s2; + s2 = GET_DATA_BYTE(lines, j + 1); + s1t = 3 * s1; + s2t = 3 * s2; + SET_DATA_BYTE(lined, jd, s1); /* d1 */ + SET_DATA_BYTE(lined, jd + 1, (s1t + s2) / 4 ); /* d2 */ + SET_DATA_BYTE(lined, jd + 2, (s1 + s2) / 2 ); /* d3 */ + SET_DATA_BYTE(lined, jd + 3, (s1 + s2t) / 4 ); /* d4 */ + SET_DATA_BYTE(linedp1, jd, s1); /* d5 */ + SET_DATA_BYTE(linedp1, jd + 1, (s1t + s2) / 4 ); /* d6 */ + SET_DATA_BYTE(linedp1, jd + 2, (s1 + s2) / 2 ); /* d7 */ + SET_DATA_BYTE(linedp1, jd + 3, (s1 + s2t) / 4 ); /* d8 */ + SET_DATA_BYTE(linedp2, jd, s1); /* d9 */ + SET_DATA_BYTE(linedp2, jd + 1, (s1t + s2) / 4 ); /* d10 */ + SET_DATA_BYTE(linedp2, jd + 2, (s1 + s2) / 2 ); /* d11 */ + SET_DATA_BYTE(linedp2, jd + 3, (s1 + s2t) / 4 ); /* d12 */ + SET_DATA_BYTE(linedp3, jd, s1); /* d13 */ + SET_DATA_BYTE(linedp3, jd + 1, (s1t + s2) / 4 ); /* d14 */ + SET_DATA_BYTE(linedp3, jd + 2, (s1 + s2) / 2 ); /* d15 */ + SET_DATA_BYTE(linedp3, jd + 3, (s1 + s2t) / 4 ); /* d16 */ + } + s1 = s2; + SET_DATA_BYTE(lined, wsm4, s1); /* d1 */ + SET_DATA_BYTE(lined, wsm4 + 1, s1); /* d2 */ + SET_DATA_BYTE(lined, wsm4 + 2, s1); /* d3 */ + SET_DATA_BYTE(lined, wsm4 + 3, s1); /* d4 */ + SET_DATA_BYTE(linedp1, wsm4, s1); /* d5 */ + SET_DATA_BYTE(linedp1, wsm4 + 1, s1); /* d6 */ + SET_DATA_BYTE(linedp1, wsm4 + 2, s1); /* d7 */ + SET_DATA_BYTE(linedp1, wsm4 + 3, s1); /* d8 */ + SET_DATA_BYTE(linedp2, wsm4, s1); /* d9 */ + SET_DATA_BYTE(linedp2, wsm4 + 1, s1); /* d10 */ + SET_DATA_BYTE(linedp2, wsm4 + 2, s1); /* d11 */ + SET_DATA_BYTE(linedp2, wsm4 + 3, s1); /* d12 */ + SET_DATA_BYTE(linedp3, wsm4, s1); /* d13 */ + SET_DATA_BYTE(linedp3, wsm4 + 1, s1); /* d14 */ + SET_DATA_BYTE(linedp3, wsm4 + 2, s1); /* d15 */ + SET_DATA_BYTE(linedp3, wsm4 + 3, s1); /* d16 */ + } +} + + +/*------------------------------------------------------------------* + * Grayscale and color scaling by closest pixel sampling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleBySamplingLow() + * + * Notes: + * (1) The dest must be cleared prior to this operation, + * and we clear it here in the low-level code. + * (2) We reuse dest pixels and dest pixel rows whenever + * possible. This speeds the upscaling; downscaling + * is done by strict subsampling and is unaffected. + * (3) Because we are sampling and not interpolating, this + * routine works directly, without conversion to full + * RGB color, for 2, 4 or 8 bpp palette color images. + */ +static l_int32 +scaleBySamplingLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 d, + l_int32 wpls) +{ +l_int32 i, j; +l_int32 xs, prevxs, sval; +l_int32 *srow, *scol; +l_uint32 csval; +l_uint32 *lines, *prevlines, *lined, *prevlined; +l_float32 wratio, hratio; + + PROCNAME("scaleBySamplingLow"); + + if (d != 2 && d != 4 && d !=8 && d != 16 && d != 32) + return ERROR_INT("pixel depth not supported", procName, 1); + + /* Clear dest */ + memset(datad, 0, 4LL * hd * wpld); + + /* the source row corresponding to dest row i ==> srow[i] + * the source col corresponding to dest col j ==> scol[j] */ + if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) + return ERROR_INT("srow not made", procName, 1); + if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { + LEPT_FREE(srow); + return ERROR_INT("scol not made", procName, 1); + } + + wratio = (l_float32)ws / (l_float32)wd; + hratio = (l_float32)hs / (l_float32)hd; + for (i = 0; i < hd; i++) + srow[i] = L_MIN((l_int32)(hratio * i + 0.5), hs - 1); + for (j = 0; j < wd; j++) + scol[j] = L_MIN((l_int32)(wratio * j + 0.5), ws - 1); + + prevlines = NULL; + for (i = 0; i < hd; i++) { + lines = datas + srow[i] * wpls; + lined = datad + i * wpld; + if (lines != prevlines) { /* make dest from new source row */ + prevxs = -1; + sval = 0; + csval = 0; + if (d == 2) { + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + sval = GET_DATA_DIBIT(lines, xs); + SET_DATA_DIBIT(lined, j, sval); + prevxs = xs; + } else { /* copy prev dest pix */ + SET_DATA_DIBIT(lined, j, sval); + } + } + } else if (d == 4) { + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + sval = GET_DATA_QBIT(lines, xs); + SET_DATA_QBIT(lined, j, sval); + prevxs = xs; + } else { /* copy prev dest pix */ + SET_DATA_QBIT(lined, j, sval); + } + } + } else if (d == 8) { + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + sval = GET_DATA_BYTE(lines, xs); + SET_DATA_BYTE(lined, j, sval); + prevxs = xs; + } else { /* copy prev dest pix */ + SET_DATA_BYTE(lined, j, sval); + } + } + } else if (d == 16) { + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + sval = GET_DATA_TWO_BYTES(lines, xs); + SET_DATA_TWO_BYTES(lined, j, sval); + prevxs = xs; + } else { /* copy prev dest pix */ + SET_DATA_TWO_BYTES(lined, j, sval); + } + } + } else { /* d == 32 */ + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + csval = lines[xs]; + lined[j] = csval; + prevxs = xs; + } else { /* copy prev dest pix */ + lined[j] = csval; + } + } + } + } else { /* lines == prevlines; copy prev dest row */ + prevlined = lined - wpld; + memcpy(lined, prevlined, 4 * wpld); + } + prevlines = lines; + } + + LEPT_FREE(srow); + LEPT_FREE(scol); + return 0; +} + + +/*------------------------------------------------------------------* + * Color and grayscale downsampling with (antialias) smoothing * + *------------------------------------------------------------------*/ +/*! + * \brief scaleSmoothLow() + * + * Notes: + * (1) This function is called on 8 or 32 bpp src and dest images. + * (2) size is the full width of the lowpass smoothing filter. + * It is correlated with the reduction ratio, being the + * nearest integer such that size is approximately equal to hs / hd. + */ +static l_int32 +scaleSmoothLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 d, + l_int32 wpls, + l_int32 size) +{ +l_int32 i, j, m, n, xstart; +l_int32 val, rval, gval, bval; +l_int32 *srow, *scol; +l_uint32 *lines, *lined, *line, *ppixel; +l_uint32 pixel; +l_float32 wratio, hratio, norm; + + PROCNAME("scaleSmoothLow"); + + /* Clear dest */ + memset(datad, 0, 4LL * wpld * hd); + + /* Each dest pixel at (j,i) is computed as the average + of size^2 corresponding src pixels. + We store the UL corner location of the square of + src pixels that correspond to dest pixel (j,i). + The are labeled by the arrays srow[i] and scol[j]. */ + if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) + return ERROR_INT("srow not made", procName, 1); + if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { + LEPT_FREE(srow); + return ERROR_INT("scol not made", procName, 1); + } + + norm = 1. / (l_float32)(size * size); + wratio = (l_float32)ws / (l_float32)wd; + hratio = (l_float32)hs / (l_float32)hd; + for (i = 0; i < hd; i++) + srow[i] = L_MIN((l_int32)(hratio * i), hs - size); + for (j = 0; j < wd; j++) + scol[j] = L_MIN((l_int32)(wratio * j), ws - size); + + /* For each dest pixel, compute average */ + if (d == 8) { + for (i = 0; i < hd; i++) { + lines = datas + srow[i] * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + xstart = scol[j]; + val = 0; + for (m = 0; m < size; m++) { + line = lines + m * wpls; + for (n = 0; n < size; n++) { + val += GET_DATA_BYTE(line, xstart + n); + } + } + val = (l_int32)((l_float32)val * norm); + SET_DATA_BYTE(lined, j, val); + } + } + } else { /* d == 32 */ + for (i = 0; i < hd; i++) { + lines = datas + srow[i] * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + xstart = scol[j]; + rval = gval = bval = 0; + for (m = 0; m < size; m++) { + ppixel = lines + m * wpls + xstart; + for (n = 0; n < size; n++) { + pixel = *(ppixel + n); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + } + } + rval = (l_int32)((l_float32)rval * norm); + gval = (l_int32)((l_float32)gval * norm); + bval = (l_int32)((l_float32)bval * norm); + composeRGBPixel(rval, gval, bval, lined + j); + } + } + } + + LEPT_FREE(srow); + LEPT_FREE(scol); + return 0; +} + + +/*! + * \brief scaleRGBToGray2Low() + * + * Notes: + * (1) This function is called with 32 bpp RGB src and 8 bpp, + * half-resolution dest. The weights should add to 1.0. + */ +static void +scaleRGBToGray2Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_float32 rwt, + l_float32 gwt, + l_float32 bwt) +{ +l_int32 i, j, val, rval, gval, bval; +l_uint32 *lines, *lined; +l_uint32 pixel; + + rwt *= 0.25; + gwt *= 0.25; + bwt *= 0.25; + for (i = 0; i < hd; i++) { + lines = datas + 2 * i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + /* Sum each of the color components from 4 src pixels */ + pixel = *(lines + 2 * j); + rval = (pixel >> L_RED_SHIFT) & 0xff; + gval = (pixel >> L_GREEN_SHIFT) & 0xff; + bval = (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + 2 * j + 1); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + wpls + 2 * j); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + wpls + 2 * j + 1); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + /* Generate the dest byte as a weighted sum of the averages */ + val = (l_int32)(rwt * rval + gwt * gval + bwt * bval); + SET_DATA_BYTE(lined, j, val); + } + } +} + + +/*------------------------------------------------------------------* + * General area mapped gray scaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleColorAreaMapLow() + * + * This should only be used for downscaling. + * We choose to divide each pixel into 16 x 16 sub-pixels. + * This is much slower than scaleSmoothLow(), but it gives a + * better representation, esp. for downscaling factors between + * 1.5 and 5. All src pixels are subdivided into 256 sub-pixels, + * and are weighted by the number of sub-pixels covered by + * the dest pixel. This is about 2x slower than scaleSmoothLow(), + * but the results are significantly better on small text. + */ +static void +scaleColorAreaMapLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, j, k, m, wm2, hm2; +l_int32 area00, area10, area01, area11, areal, arear, areat, areab; +l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */ +l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */ +l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */ +l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */ +l_int32 delx, dely, area; +l_int32 v00r, v00g, v00b; /* contrib. from UL src pixel */ +l_int32 v01r, v01g, v01b; /* contrib. from LL src pixel */ +l_int32 v10r, v10g, v10b; /* contrib from UR src pixel */ +l_int32 v11r, v11g, v11b; /* contrib from LR src pixel */ +l_int32 vinr, ving, vinb; /* contrib from all full interior src pixels */ +l_int32 vmidr, vmidg, vmidb; /* contrib from side parts */ +l_int32 rval, gval, bval; +l_uint32 pixel00, pixel10, pixel01, pixel11, pixel; +l_uint32 *lines, *lined; +l_float32 scx, scy; + + /* (scx, scy) are scaling factors that are applied to the + * dest coords to get the corresponding src coords. + * We need them because we iterate over dest pixels + * and must find the corresponding set of src pixels. */ + scx = 16. * (l_float32)ws / (l_float32)wd; + scy = 16. * (l_float32)hs / (l_float32)hd; + wm2 = ws - 2; + hm2 = hs - 2; + + /* Iterate over the destination pixels */ + for (i = 0; i < hd; i++) { + yu = (l_int32)(scy * i); + yl = (l_int32)(scy * (i + 1.0)); + yup = yu >> 4; + yuf = yu & 0x0f; + ylp = yl >> 4; + ylf = yl & 0x0f; + dely = ylp - yup; + lined = datad + i * wpld; + lines = datas + yup * wpls; + for (j = 0; j < wd; j++) { + xu = (l_int32)(scx * j); + xl = (l_int32)(scx * (j + 1.0)); + xup = xu >> 4; + xuf = xu & 0x0f; + xlp = xl >> 4; + xlf = xl & 0x0f; + delx = xlp - xup; + + /* If near the edge, just use a src pixel value */ + if (xlp > wm2 || ylp > hm2) { + *(lined + j) = *(lines + xup); + continue; + } + + /* Area summed over, in subpixels. This varies + * due to the quantization, so we can't simply take + * the area to be a constant: area = scx * scy. */ + area = ((16 - xuf) + 16 * (delx - 1) + xlf) * + ((16 - yuf) + 16 * (dely - 1) + ylf); + + /* Do area map summation */ + pixel00 = *(lines + xup); + pixel10 = *(lines + xlp); + pixel01 = *(lines + dely * wpls + xup); + pixel11 = *(lines + dely * wpls + xlp); + area00 = (16 - xuf) * (16 - yuf); + area10 = xlf * (16 - yuf); + area01 = (16 - xuf) * ylf; + area11 = xlf * ylf; + v00r = area00 * ((pixel00 >> L_RED_SHIFT) & 0xff); + v00g = area00 * ((pixel00 >> L_GREEN_SHIFT) & 0xff); + v00b = area00 * ((pixel00 >> L_BLUE_SHIFT) & 0xff); + v10r = area10 * ((pixel10 >> L_RED_SHIFT) & 0xff); + v10g = area10 * ((pixel10 >> L_GREEN_SHIFT) & 0xff); + v10b = area10 * ((pixel10 >> L_BLUE_SHIFT) & 0xff); + v01r = area01 * ((pixel01 >> L_RED_SHIFT) & 0xff); + v01g = area01 * ((pixel01 >> L_GREEN_SHIFT) & 0xff); + v01b = area01 * ((pixel01 >> L_BLUE_SHIFT) & 0xff); + v11r = area11 * ((pixel11 >> L_RED_SHIFT) & 0xff); + v11g = area11 * ((pixel11 >> L_GREEN_SHIFT) & 0xff); + v11b = area11 * ((pixel11 >> L_BLUE_SHIFT) & 0xff); + vinr = ving = vinb = 0; + for (k = 1; k < dely; k++) { /* for full src pixels */ + for (m = 1; m < delx; m++) { + pixel = *(lines + k * wpls + xup + m); + vinr += 256 * ((pixel >> L_RED_SHIFT) & 0xff); + ving += 256 * ((pixel >> L_GREEN_SHIFT) & 0xff); + vinb += 256 * ((pixel >> L_BLUE_SHIFT) & 0xff); + } + } + vmidr = vmidg = vmidb = 0; + areal = (16 - xuf) * 16; + arear = xlf * 16; + areat = 16 * (16 - yuf); + areab = 16 * ylf; + for (k = 1; k < dely; k++) { /* for left side */ + pixel = *(lines + k * wpls + xup); + vmidr += areal * ((pixel >> L_RED_SHIFT) & 0xff); + vmidg += areal * ((pixel >> L_GREEN_SHIFT) & 0xff); + vmidb += areal * ((pixel >> L_BLUE_SHIFT) & 0xff); + } + for (k = 1; k < dely; k++) { /* for right side */ + pixel = *(lines + k * wpls + xlp); + vmidr += arear * ((pixel >> L_RED_SHIFT) & 0xff); + vmidg += arear * ((pixel >> L_GREEN_SHIFT) & 0xff); + vmidb += arear * ((pixel >> L_BLUE_SHIFT) & 0xff); + } + for (m = 1; m < delx; m++) { /* for top side */ + pixel = *(lines + xup + m); + vmidr += areat * ((pixel >> L_RED_SHIFT) & 0xff); + vmidg += areat * ((pixel >> L_GREEN_SHIFT) & 0xff); + vmidb += areat * ((pixel >> L_BLUE_SHIFT) & 0xff); + } + for (m = 1; m < delx; m++) { /* for bottom side */ + pixel = *(lines + dely * wpls + xup + m); + vmidr += areab * ((pixel >> L_RED_SHIFT) & 0xff); + vmidg += areab * ((pixel >> L_GREEN_SHIFT) & 0xff); + vmidb += areab * ((pixel >> L_BLUE_SHIFT) & 0xff); + } + + /* Sum all the contributions */ + rval = (v00r + v01r + v10r + v11r + vinr + vmidr + 128) / area; + gval = (v00g + v01g + v10g + v11g + ving + vmidg + 128) / area; + bval = (v00b + v01b + v10b + v11b + vinb + vmidb + 128) / area; +#if DEBUG_OVERFLOW + if (rval > 255) fprintf(stderr, "rval ovfl: %d\n", rval); + if (gval > 255) fprintf(stderr, "gval ovfl: %d\n", gval); + if (bval > 255) fprintf(stderr, "bval ovfl: %d\n", bval); +#endif /* DEBUG_OVERFLOW */ + composeRGBPixel(rval, gval, bval, lined + j); + } + } +} + + +/*! + * \brief scaleGrayAreaMapLow() + * + * This should only be used for downscaling. + * We choose to divide each pixel into 16 x 16 sub-pixels. + * This is about 2x slower than scaleSmoothLow(), but the results + * are significantly better on small text, esp. for downscaling + * factors between 1.5 and 5. All src pixels are subdivided + * into 256 sub-pixels, and are weighted by the number of + * sub-pixels covered by the dest pixel. + */ +static void +scaleGrayAreaMapLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, j, k, m, wm2, hm2; +l_int32 xu, yu; /* UL corner in src image, to 1/16 of a pixel */ +l_int32 xl, yl; /* LR corner in src image, to 1/16 of a pixel */ +l_int32 xup, yup, xuf, yuf; /* UL src pixel: integer and fraction */ +l_int32 xlp, ylp, xlf, ylf; /* LR src pixel: integer and fraction */ +l_int32 delx, dely, area; +l_int32 v00; /* contrib. from UL src pixel */ +l_int32 v01; /* contrib. from LL src pixel */ +l_int32 v10; /* contrib from UR src pixel */ +l_int32 v11; /* contrib from LR src pixel */ +l_int32 vin; /* contrib from all full interior src pixels */ +l_int32 vmid; /* contrib from side parts that are full in 1 direction */ +l_int32 val; +l_uint32 *lines, *lined; +l_float32 scx, scy; + + /* (scx, scy) are scaling factors that are applied to the + * dest coords to get the corresponding src coords. + * We need them because we iterate over dest pixels + * and must find the corresponding set of src pixels. */ + scx = 16. * (l_float32)ws / (l_float32)wd; + scy = 16. * (l_float32)hs / (l_float32)hd; + wm2 = ws - 2; + hm2 = hs - 2; + + /* Iterate over the destination pixels */ + for (i = 0; i < hd; i++) { + yu = (l_int32)(scy * i); + yl = (l_int32)(scy * (i + 1.0)); + yup = yu >> 4; + yuf = yu & 0x0f; + ylp = yl >> 4; + ylf = yl & 0x0f; + dely = ylp - yup; + lined = datad + i * wpld; + lines = datas + yup * wpls; + for (j = 0; j < wd; j++) { + xu = (l_int32)(scx * j); + xl = (l_int32)(scx * (j + 1.0)); + xup = xu >> 4; + xuf = xu & 0x0f; + xlp = xl >> 4; + xlf = xl & 0x0f; + delx = xlp - xup; + + /* If near the edge, just use a src pixel value */ + if (xlp > wm2 || ylp > hm2) { + SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xup)); + continue; + } + + /* Area summed over, in subpixels. This varies + * due to the quantization, so we can't simply take + * the area to be a constant: area = scx * scy. */ + area = ((16 - xuf) + 16 * (delx - 1) + xlf) * + ((16 - yuf) + 16 * (dely - 1) + ylf); + + /* Do area map summation */ + v00 = (16 - xuf) * (16 - yuf) * GET_DATA_BYTE(lines, xup); + v10 = xlf * (16 - yuf) * GET_DATA_BYTE(lines, xlp); + v01 = (16 - xuf) * ylf * GET_DATA_BYTE(lines + dely * wpls, xup); + v11 = xlf * ylf * GET_DATA_BYTE(lines + dely * wpls, xlp); + for (vin = 0, k = 1; k < dely; k++) { /* for full src pixels */ + for (m = 1; m < delx; m++) { + vin += 256 * GET_DATA_BYTE(lines + k * wpls, xup + m); + } + } + for (vmid = 0, k = 1; k < dely; k++) /* for left side */ + vmid += (16 - xuf) * 16 * GET_DATA_BYTE(lines + k * wpls, xup); + for (k = 1; k < dely; k++) /* for right side */ + vmid += xlf * 16 * GET_DATA_BYTE(lines + k * wpls, xlp); + for (m = 1; m < delx; m++) /* for top side */ + vmid += 16 * (16 - yuf) * GET_DATA_BYTE(lines, xup + m); + for (m = 1; m < delx; m++) /* for bottom side */ + vmid += 16 * ylf * GET_DATA_BYTE(lines + dely * wpls, xup + m); + val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area; +#if DEBUG_OVERFLOW + if (val > 255) fprintf(stderr, "val overflow: %d\n", val); +#endif /* DEBUG_OVERFLOW */ + SET_DATA_BYTE(lined, j, val); + } + } +} + + +/*------------------------------------------------------------------* + * 2x area mapped downscaling * + *------------------------------------------------------------------*/ +/*! + * \brief scaleAreaMapLow2() + * + * Notes: + * This function is called with either 8 bpp gray or 32 bpp RGB. + * The result is a 2x reduced dest. + */ +static void +scaleAreaMapLow2(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 d, + l_int32 wpls) +{ +l_int32 i, j, val, rval, gval, bval; +l_uint32 *lines, *lined; +l_uint32 pixel; + + if (d == 8) { + for (i = 0; i < hd; i++) { + lines = datas + 2 * i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + /* Average each dest pixel using 4 src pixels */ + val = GET_DATA_BYTE(lines, 2 * j); + val += GET_DATA_BYTE(lines, 2 * j + 1); + val += GET_DATA_BYTE(lines + wpls, 2 * j); + val += GET_DATA_BYTE(lines + wpls, 2 * j + 1); + val >>= 2; + SET_DATA_BYTE(lined, j, val); + } + } + } else { /* d == 32 */ + for (i = 0; i < hd; i++) { + lines = datas + 2 * i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + /* Average each of the color components from 4 src pixels */ + pixel = *(lines + 2 * j); + rval = (pixel >> L_RED_SHIFT) & 0xff; + gval = (pixel >> L_GREEN_SHIFT) & 0xff; + bval = (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + 2 * j + 1); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + wpls + 2 * j); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + pixel = *(lines + wpls + 2 * j + 1); + rval += (pixel >> L_RED_SHIFT) & 0xff; + gval += (pixel >> L_GREEN_SHIFT) & 0xff; + bval += (pixel >> L_BLUE_SHIFT) & 0xff; + composeRGBPixel(rval >> 2, gval >> 2, bval >> 2, &pixel); + *(lined + j) = pixel; + } + } + } +} + + +/*------------------------------------------------------------------* + * Binary scaling by closest pixel sampling * + *------------------------------------------------------------------*/ +/* + * scaleBinaryLow() + * + * Notes: + * (1) The dest must be cleared prior to this operation, + * and we clear it here in the low-level code. + * (2) We reuse dest pixels and dest pixel rows whenever + * possible for upscaling; downscaling is done by + * strict subsampling. + */ +static l_int32 +scaleBinaryLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 ws, + l_int32 hs, + l_int32 wpls) +{ +l_int32 i, j; +l_int32 xs, prevxs, sval; +l_int32 *srow, *scol; +l_uint32 *lines, *prevlines, *lined, *prevlined; +l_float32 wratio, hratio; + + PROCNAME("scaleBinaryLow"); + + /* Clear dest */ + memset(datad, 0, 4LL * hd * wpld); + + /* The source row corresponding to dest row i ==> srow[i] + * The source col corresponding to dest col j ==> scol[j] */ + if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) + return ERROR_INT("srow not made", procName, 1); + if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { + LEPT_FREE(srow); + return ERROR_INT("scol not made", procName, 1); + } + + wratio = (l_float32)ws / (l_float32)wd; + hratio = (l_float32)hs / (l_float32)hd; + for (i = 0; i < hd; i++) + srow[i] = L_MIN((l_int32)(hratio * i + 0.5), hs - 1); + for (j = 0; j < wd; j++) + scol[j] = L_MIN((l_int32)(wratio * j + 0.5), ws - 1); + + prevlines = NULL; + prevxs = -1; + sval = 0; + for (i = 0; i < hd; i++) { + lines = datas + srow[i] * wpls; + lined = datad + i * wpld; + if (lines != prevlines) { /* make dest from new source row */ + for (j = 0; j < wd; j++) { + xs = scol[j]; + if (xs != prevxs) { /* get dest pix from source col */ + if ((sval = GET_DATA_BIT(lines, xs))) + SET_DATA_BIT(lined, j); + prevxs = xs; + } else { /* copy prev dest pix, if set */ + if (sval) + SET_DATA_BIT(lined, j); + } + } + } else { /* lines == prevlines; copy prev dest row */ + prevlined = lined - wpld; + memcpy(lined, prevlined, 4 * wpld); + } + prevlines = lines; + } + + LEPT_FREE(srow); + LEPT_FREE(scol); + return 0; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/scale2.c b/hgdriver/3rdparty/hgOCR/leptonica/scale2.c new file mode 100644 index 0000000..f3d6be0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/scale2.c @@ -0,0 +1,2320 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file scale2.c + *
+ *         Scale-to-gray (1 bpp --> 8 bpp; arbitrary downscaling)
+ *               PIX      *pixScaleToGray()
+ *               PIX      *pixScaleToGrayFast()
+ *
+ *         Scale-to-gray (1 bpp --> 8 bpp; integer downscaling)
+ *               PIX      *pixScaleToGray2()
+ *               PIX      *pixScaleToGray3()
+ *               PIX      *pixScaleToGray4()
+ *               PIX      *pixScaleToGray6()
+ *               PIX      *pixScaleToGray8()
+ *               PIX      *pixScaleToGray16()
+ *
+ *         Scale-to-gray by mipmap(1 bpp --> 8 bpp, arbitrary reduction)
+ *               PIX      *pixScaleToGrayMipmap()
+ *
+ *         Grayscale scaling using mipmap
+ *               PIX      *pixScaleMipmap()
+ *
+ *         Replicated (integer) expansion (all depths)
+ *               PIX      *pixExpandReplicate()
+ *
+ *         Grayscale downscaling using min and max
+ *               PIX      *pixScaleGrayMinMax()
+ *               PIX      *pixScaleGrayMinMax2()
+ *
+ *         Grayscale downscaling using rank value
+ *               PIX      *pixScaleGrayRankCascade()
+ *               PIX      *pixScaleGrayRank2()
+ *
+ *         Helper function for transferring alpha with scaling
+ *               l_int32   pixScaleAndTransferAlpha()
+ *
+ *         RGB scaling including alpha (blend) component
+ *               PIX      *pixScaleWithAlpha()
+ *
+ *     Low-level static functions:
+ *
+ *         Scale-to-gray 2x
+ *                  static void       scaleToGray2Low()
+ *                  static l_uint32  *makeSumTabSG2()
+ *                  static l_uint8   *makeValTabSG2()
+ *
+ *         Scale-to-gray 3x
+ *                  static void       scaleToGray3Low()
+ *                  static l_uint32  *makeSumTabSG3()
+ *                  static l_uint8   *makeValTabSG3()
+ *
+ *         Scale-to-gray 4x
+ *                  static void       scaleToGray4Low()
+ *                  static l_uint32  *makeSumTabSG4()
+ *                  static l_uint8   *makeValTabSG4()
+ *
+ *         Scale-to-gray 6x
+ *                  static void       scaleToGray6Low()
+ *                  static l_uint8   *makeValTabSG6()
+ *
+ *         Scale-to-gray 8x
+ *                  static void       scaleToGray8Low()
+ *                  static l_uint8   *makeValTabSG8()
+ *
+ *         Scale-to-gray 16x
+ *                  static void       scaleToGray16Low()
+ *
+ *         Grayscale mipmap
+ *                  static l_int32    scaleMipmapLow()
+ * 
+ */ + +#include +#include "allheaders.h" + +static void scaleToGray2Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_uint32 *sumtab, l_uint8 *valtab); +static l_uint32 *makeSumTabSG2(void); +static l_uint8 *makeValTabSG2(void); +static void scaleToGray3Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_uint32 *sumtab, l_uint8 *valtab); +static l_uint32 *makeSumTabSG3(void); +static l_uint8 *makeValTabSG3(void); +static void scaleToGray4Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_uint32 *sumtab, l_uint8 *valtab); +static l_uint32 *makeSumTabSG4(void); +static l_uint8 *makeValTabSG4(void); +static void scaleToGray6Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 *tab8, l_uint8 *valtab); +static l_uint8 *makeValTabSG6(void); +static void scaleToGray8Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 *tab8, l_uint8 *valtab); +static l_uint8 *makeValTabSG8(void); +static void scaleToGray16Low(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas, l_int32 wpls, + l_int32 *tab8); +static l_int32 scaleMipmapLow(l_uint32 *datad, l_int32 wd, l_int32 hd, + l_int32 wpld, l_uint32 *datas1, l_int32 wpls1, + l_uint32 *datas2, l_int32 wpls2, l_float32 red); + +extern l_float32 AlphaMaskBorderVals[2]; + + +/*------------------------------------------------------------------* + * Scale-to-gray (1 bpp --> 8 bpp; arbitrary downscaling) * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleToGray() + * + * \param[in] pixs 1 bpp + * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 + * \return pixd 8 bpp, scaled down by scalefactor in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *
+ *  For faster scaling in the range of scalefactors from 0.0625 to 0.5,
+ *  with very little difference in quality, use pixScaleToGrayFast().
+ *
+ *  Binary images have sharp edges, so they intrinsically have very
+ *  high frequency content.  To avoid aliasing, they must be low-pass
+ *  filtered, which tends to blur the edges.  How can we keep relatively
+ *  crisp edges without aliasing?  The trick is to do binary upscaling
+ *  followed by a power-of-2 scaleToGray.  For large reductions, where
+ *  you don't end up with much detail, some corners can be cut.
+ *
+ *  The intent here is to get high quality reduced grayscale
+ *  images with relatively little computation.  We do binary
+ *  pre-scaling followed by scaleToGrayN() for best results,
+ *  esp. to avoid excess blur when the scale factor is near
+ *  an inverse power of 2.  Where a low-pass filter is required,
+ *  we use simple convolution kernels: either the hat filter for
+ *  linear interpolation or a flat filter for larger downscaling.
+ *  Other choices, such as a perfect bandpass filter with infinite extent
+ *  (the sinc) or various approximations to it (e.g., lanczos), are
+ *  unnecessarily expensive.
+ *
+ *  The choices made are as follows:
+ *      (1) Do binary upscaling before scaleToGrayN() for scalefactors > 1/8
+ *      (2) Do binary downscaling before scaleToGray8() for scalefactors
+ *          between 1/16 and 1/8.
+ *      (3) Use scaleToGray16() before grayscale downscaling for
+ *          scalefactors less than 1/16
+ *  Another reasonable choice would be to start binary downscaling
+ *  for scalefactors below 1/4, rather than below 1/8 as we do here.
+ *
+ *  The general scaling rules, not all of which are used here, go as follows:
+ *      (1) For grayscale upscaling, use pixScaleGrayLI().  However,
+ *          note that edges will be visibly blurred for scalefactors
+ *          near (but above) 1.0.  Replication will avoid edge blur,
+ *          and should be considered for factors very near 1.0.
+ *      (2) For grayscale downscaling with a scale factor larger than
+ *          about 0.7, use pixScaleGrayLI().  For scalefactors near
+ *          (but below) 1.0, you tread between Scylla and Charybdis.
+ *          pixScaleGrayLI() again gives edge blurring, but
+ *          pixScaleBySampling() gives visible aliasing.
+ *      (3) For grayscale downscaling with a scale factor smaller than
+ *          about 0.7, use pixScaleSmooth()
+ *      (4) For binary input images, do as much scale to gray as possible
+ *          using the special integer functions (2, 3, 4, 8 and 16).
+ *      (5) It is better to upscale in binary, followed by scaleToGrayN()
+ *          than to do scaleToGrayN() followed by an upscale using either
+ *          LI or oversampling.
+ *      (6) It may be better to downscale in binary, followed by
+ *          scaleToGrayN() than to first use scaleToGrayN() followed by
+ *          downscaling.  For downscaling between 8x and 16x, this is
+ *          a reasonable option.
+ *      (7) For reductions greater than 16x, it's reasonable to use
+ *          scaleToGray16() followed by further grayscale downscaling.
+ * 
+ */ +PIX * +pixScaleToGray(PIX *pixs, + l_float32 scalefactor) +{ +l_int32 w, h, minsrc, mindest; +l_float32 mag, red; +PIX *pixt, *pixd; + + PROCNAME("pixScaleToGray"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (scalefactor <= 0.0) + return (PIX *)ERROR_PTR("scalefactor <= 0.0", procName, NULL); + if (scalefactor >= 1.0) + return (PIX *)ERROR_PTR("scalefactor >= 1.0", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + minsrc = L_MIN(w, h); + mindest = (l_int32)((l_float32)minsrc * scalefactor); + if (mindest < 2) + return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL); + + if (scalefactor > 0.5) { /* see note (5) */ + mag = 2.0 * scalefactor; /* will be < 2.0 */ +/* fprintf(stderr, "2x with mag %7.3f\n", mag); */ + if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray2(pixt); + } else if (scalefactor == 0.5) { + return pixd = pixScaleToGray2(pixs); + } else if (scalefactor > 0.33333) { /* see note (5) */ + mag = 3.0 * scalefactor; /* will be < 1.5 */ +/* fprintf(stderr, "3x with mag %7.3f\n", mag); */ + if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray3(pixt); + } else if (scalefactor > 0.25) { /* see note (5) */ + mag = 4.0 * scalefactor; /* will be < 1.3333 */ +/* fprintf(stderr, "4x with mag %7.3f\n", mag); */ + if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray4(pixt); + } else if (scalefactor == 0.25) { + return pixd = pixScaleToGray4(pixs); + } else if (scalefactor > 0.16667) { /* see note (5) */ + mag = 6.0 * scalefactor; /* will be < 1.5 */ +/* fprintf(stderr, "6x with mag %7.3f\n", mag); */ + if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray6(pixt); + } else if (scalefactor == 0.16667) { + return pixd = pixScaleToGray6(pixs); + } else if (scalefactor > 0.125) { /* see note (5) */ + mag = 8.0 * scalefactor; /* will be < 1.3333 */ +/* fprintf(stderr, "8x with mag %7.3f\n", mag); */ + if ((pixt = pixScaleBinary(pixs, mag, mag)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray8(pixt); + } else if (scalefactor == 0.125) { + return pixd = pixScaleToGray8(pixs); + } else if (scalefactor > 0.0625) { /* see note (6) */ + red = 8.0 * scalefactor; /* will be > 0.5 */ +/* fprintf(stderr, "8x with red %7.3f\n", red); */ + if ((pixt = pixScaleBinary(pixs, red, red)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray8(pixt); + } else if (scalefactor == 0.0625) { + return pixd = pixScaleToGray16(pixs); + } else { /* see note (7) */ + red = 16.0 * scalefactor; /* will be <= 1.0 */ +/* fprintf(stderr, "16x with red %7.3f\n", red); */ + if ((pixt = pixScaleToGray16(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + if (red < 0.7) + pixd = pixScaleSmooth(pixt, red, red); /* see note (3) */ + else + pixd = pixScaleGrayLI(pixt, red, red); /* see note (2) */ + } + + pixDestroy(&pixt); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*! + * \brief pixScaleToGrayFast() + * + * \param[in] pixs 1 bpp + * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 + * \return pixd 8 bpp, scaled down by scalefactor in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) See notes in pixScaleToGray() for the basic approach.
+ *      (2) This function is considerably less expensive than pixScaleToGray()
+ *          for scalefactor in the range (0.0625 ... 0.5), and the
+ *          quality is nearly as good.
+ *      (3) Unlike pixScaleToGray(), which does binary upscaling before
+ *          downscaling for scale factors >= 0.0625, pixScaleToGrayFast()
+ *          first downscales in binary for all scale factors < 0.5, and
+ *          then does a 2x scale-to-gray as the final step.  For
+ *          scale factors < 0.0625, both do a 16x scale-to-gray, followed
+ *          by further grayscale reduction.
+ * 
+ */ +PIX * +pixScaleToGrayFast(PIX *pixs, + l_float32 scalefactor) +{ +l_int32 w, h, minsrc, mindest; +l_float32 eps, factor; +PIX *pixt, *pixd; + + PROCNAME("pixScaleToGrayFast"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (scalefactor <= 0.0) + return (PIX *)ERROR_PTR("scalefactor <= 0.0", procName, NULL); + if (scalefactor >= 1.0) + return (PIX *)ERROR_PTR("scalefactor >= 1.0", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + minsrc = L_MIN(w, h); + mindest = (l_int32)((l_float32)minsrc * scalefactor); + if (mindest < 2) + return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL); + eps = 0.0001; + + /* Handle the special cases */ + if (scalefactor > 0.5 - eps && scalefactor < 0.5 + eps) + return pixScaleToGray2(pixs); + else if (scalefactor > 0.33333 - eps && scalefactor < 0.33333 + eps) + return pixScaleToGray3(pixs); + else if (scalefactor > 0.25 - eps && scalefactor < 0.25 + eps) + return pixScaleToGray4(pixs); + else if (scalefactor > 0.16666 - eps && scalefactor < 0.16666 + eps) + return pixScaleToGray6(pixs); + else if (scalefactor > 0.125 - eps && scalefactor < 0.125 + eps) + return pixScaleToGray8(pixs); + else if (scalefactor > 0.0625 - eps && scalefactor < 0.0625 + eps) + return pixScaleToGray16(pixs); + + if (scalefactor > 0.0625) { /* scale binary first */ + factor = 2.0 * scalefactor; + if ((pixt = pixScaleBinary(pixs, factor, factor)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + pixd = pixScaleToGray2(pixt); + } else { /* scalefactor < 0.0625; scale-to-gray first */ + factor = 16.0 * scalefactor; /* will be < 1.0 */ + if ((pixt = pixScaleToGray16(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + if (factor < 0.7) + pixd = pixScaleSmooth(pixt, factor, factor); + else + pixd = pixScaleGrayLI(pixt, factor, factor); + } + pixDestroy(&pixt); + if (!pixd) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Scale-to-gray (1 bpp --> 8 bpp; integer downscaling) * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixScaleToGray2() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 2x in each direction, + * or NULL on error. + */ +PIX * +pixScaleToGray2(PIX *pixs) +{ +l_uint8 *valtab; +l_int32 ws, hs, wd, hd; +l_int32 wpld, wpls; +l_uint32 *sumtab; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray2"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = ws / 2; + hd = hs / 2; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.5, 0.5); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + sumtab = makeSumTabSG2(); + valtab = makeValTabSG2(); + scaleToGray2Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); + LEPT_FREE(sumtab); + LEPT_FREE(valtab); + return pixd; +} + + +/*! + * \brief pixScaleToGray3() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 3x in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) Speed is about 100 x 10^6 src-pixels/sec/GHz.
+ *          Another way to express this is it processes 1 src pixel
+ *          in about 10 cycles.
+ *      (2) The width of pixd is truncated is truncated to a factor of 8.
+ * 
+ */ +PIX * +pixScaleToGray3(PIX *pixs) +{ +l_uint8 *valtab; +l_int32 ws, hs, wd, hd; +l_int32 wpld, wpls; +l_uint32 *sumtab; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray3"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = (ws / 3) & 0xfffffff8; /* truncate to factor of 8 */ + hd = hs / 3; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.33333, 0.33333); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + sumtab = makeSumTabSG3(); + valtab = makeValTabSG3(); + scaleToGray3Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); + LEPT_FREE(sumtab); + LEPT_FREE(valtab); + return pixd; +} + + +/*! + * \brief pixScaleToGray4() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 4x in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) The width of pixd is truncated is truncated to a factor of 2.
+ * 
+ */ +PIX * +pixScaleToGray4(PIX *pixs) +{ +l_uint8 *valtab; +l_int32 ws, hs, wd, hd; +l_int32 wpld, wpls; +l_uint32 *sumtab; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray4"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = (ws / 4) & 0xfffffffe; /* truncate to factor of 2 */ + hd = hs / 4; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.25, 0.25); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + sumtab = makeSumTabSG4(); + valtab = makeValTabSG4(); + scaleToGray4Low(datad, wd, hd, wpld, datas, wpls, sumtab, valtab); + LEPT_FREE(sumtab); + LEPT_FREE(valtab); + return pixd; +} + + + +/*! + * \brief pixScaleToGray6() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 6x in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) The width of pixd is truncated is truncated to a factor of 8.
+ * 
+ */ +PIX * +pixScaleToGray6(PIX *pixs) +{ +l_uint8 *valtab; +l_int32 ws, hs, wd, hd, wpld, wpls; +l_int32 *tab8; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray6"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = (ws / 6) & 0xfffffff8; /* truncate to factor of 8 */ + hd = hs / 6; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.16667, 0.16667); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + tab8 = makePixelSumTab8(); + valtab = makeValTabSG6(); + scaleToGray6Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); + LEPT_FREE(tab8); + LEPT_FREE(valtab); + return pixd; +} + + +/*! + * \brief pixScaleToGray8() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 8x in each direction, + * or NULL on error + */ +PIX * +pixScaleToGray8(PIX *pixs) +{ +l_uint8 *valtab; +l_int32 ws, hs, wd, hd; +l_int32 wpld, wpls; +l_int32 *tab8; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray8"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = ws / 8; /* truncate to nearest dest byte */ + hd = hs / 8; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.125, 0.125); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + tab8 = makePixelSumTab8(); + valtab = makeValTabSG8(); + scaleToGray8Low(datad, wd, hd, wpld, datas, wpls, tab8, valtab); + LEPT_FREE(tab8); + LEPT_FREE(valtab); + return pixd; +} + + +/*! + * \brief pixScaleToGray16() + * + * \param[in] pixs 1 bpp + * \return pixd 8 bpp, scaled down by 16x in each direction, + * or NULL on error. + */ +PIX * +pixScaleToGray16(PIX *pixs) +{ +l_int32 ws, hs, wd, hd; +l_int32 wpld, wpls; +l_int32 *tab8; +l_uint32 *datas, *datad; +PIX *pixd; + + PROCNAME("pixScaleToGray16"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, NULL); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = ws / 16; + hd = hs / 16; + if (wd == 0 || hd == 0) + return (PIX *)ERROR_PTR("pixs too small", procName, NULL); + + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, 0.0625, 0.0625); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + + tab8 = makePixelSumTab8(); + scaleToGray16Low(datad, wd, hd, wpld, datas, wpls, tab8); + LEPT_FREE(tab8); + return pixd; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray mipmap(1 bpp --> 8 bpp, arbitrary reduction) * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleToGrayMipmap() + * + * \param[in] pixs 1 bpp + * \param[in] scalefactor reduction: must be > 0.0 and < 1.0 + * \return pixd 8 bpp, scaled down by scalefactor in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *
+ *  This function is here mainly for pedagogical reasons.
+ *  Mip-mapping is widely used in graphics for texture mapping, because
+ *  the texture changes smoothly with scale.  This is accomplished by
+ *  constructing a multiresolution pyramid and, for each pixel,
+ *  doing a linear interpolation between corresponding pixels in
+ *  the two planes of the pyramid that bracket the desired resolution.
+ *  The computation is very efficient, and is implemented in hardware
+ *  in high-end graphics cards.
+ *
+ *  We can use mip-mapping for scale-to-gray by using two scale-to-gray
+ *  reduced images (we don't need the entire pyramid) selected from
+ *  the set {2x, 4x, ... 16x}, and interpolating.  However, we get
+ *  severe aliasing, probably because we are subsampling from the
+ *  higher resolution image.  The method is very fast, but the result
+ *  is very poor.  In fact, the results don't look any better than
+ *  either subsampling off the higher-res grayscale image or oversampling
+ *  on the lower-res image.  Consequently, this method should NOT be used
+ *  for generating reduced images, scale-to-gray or otherwise.
+ * 
+ */ +PIX * +pixScaleToGrayMipmap(PIX *pixs, + l_float32 scalefactor) +{ +l_int32 w, h, minsrc, mindest; +l_float32 red; +PIX *pixs1, *pixs2, *pixt, *pixd; + + PROCNAME("pixScaleToGrayMipmap"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (scalefactor <= 0.0) + return (PIX *)ERROR_PTR("scalefactor <= 0.0", procName, NULL); + if (scalefactor >= 1.0) + return (PIX *)ERROR_PTR("scalefactor >= 1.0", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + minsrc = L_MIN(w, h); + mindest = (l_int32)((l_float32)minsrc * scalefactor); + if (mindest < 2) + return (PIX *)ERROR_PTR("scalefactor too small", procName, NULL); + + if (scalefactor > 0.5) { + pixs1 = pixConvert1To8(NULL, pixs, 255, 0); + pixs2 = pixScaleToGray2(pixs); + red = scalefactor; + } else if (scalefactor == 0.5) { + return pixScaleToGray2(pixs); + } else if (scalefactor > 0.25) { + pixs1 = pixScaleToGray2(pixs); + pixs2 = pixScaleToGray4(pixs); + red = 2. * scalefactor; + } else if (scalefactor == 0.25) { + return pixScaleToGray4(pixs); + } else if (scalefactor > 0.125) { + pixs1 = pixScaleToGray4(pixs); + pixs2 = pixScaleToGray8(pixs); + red = 4. * scalefactor; + } else if (scalefactor == 0.125) { + return pixScaleToGray8(pixs); + } else if (scalefactor > 0.0625) { + pixs1 = pixScaleToGray8(pixs); + pixs2 = pixScaleToGray16(pixs); + red = 8. * scalefactor; + } else if (scalefactor == 0.0625) { + return pixScaleToGray16(pixs); + } else { /* end of the pyramid; just do it */ + red = 16.0 * scalefactor; /* will be <= 1.0 */ + if ((pixt = pixScaleToGray16(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, NULL); + if (red < 0.7) + pixd = pixScaleSmooth(pixt, red, red); + else + pixd = pixScaleGrayLI(pixt, red, red); + pixDestroy(&pixt); + return pixd; + } + + pixd = pixScaleMipmap(pixs1, pixs2, red); + pixCopyInputFormat(pixd, pixs); + + pixDestroy(&pixs1); + pixDestroy(&pixs2); + return pixd; +} + + +/*------------------------------------------------------------------* + * Grayscale scaling using mipmap * + *------------------------------------------------------------------*/ +/*! + * \brief pixScaleMipmap() + * + * \param[in] pixs1 high res 8 bpp, no cmap + * \param[in] pixs2 low res -- 2x reduced -- 8 bpp, no cmap + * \param[in] scale reduction with respect to high res image, > 0.5 + * \return 8 bpp pix, scaled down by reduction in each direction, + * or NULL on error. + * + *
+ * Notes:
+ *      (1) See notes in pixScaleToGrayMipmap().
+ *      (2) This function suffers from aliasing effects that are
+ *          easily seen in document images.
+ * 
+ */ +PIX * +pixScaleMipmap(PIX *pixs1, + PIX *pixs2, + l_float32 scale) +{ +l_int32 ws1, hs1, ws2, hs2, wd, hd, wpls1, wpls2, wpld; +l_uint32 *datas1, *datas2, *datad; +PIX *pixd; + + PROCNAME("pixScaleMipmap"); + + if (!pixs1 || pixGetDepth(pixs1) != 8 || pixGetColormap(pixs1)) + return (PIX *)ERROR_PTR("pixs1 underdefined, not 8 bpp, or cmapped", + procName, NULL); + if (!pixs2 || pixGetDepth(pixs2) != 8 || pixGetColormap(pixs2)) + return (PIX *)ERROR_PTR("pixs2 underdefined, not 8 bpp, or cmapped", + procName, NULL); + pixGetDimensions(pixs1, &ws1, &hs1, NULL); + pixGetDimensions(pixs2, &ws2, &hs2, NULL); + if (scale > 1.0 || scale < 0.5) + return (PIX *)ERROR_PTR("scale not in [0.5, 1.0]", procName, NULL); + if (ws1 < 2 * ws2) + return (PIX *)ERROR_PTR("invalid width ratio", procName, NULL); + if (hs1 < 2 * hs2) + return (PIX *)ERROR_PTR("invalid height ratio", procName, NULL); + + /* Generate wd and hd from the lower resolution dimensions, + * to guarantee staying within both src images */ + datas1 = pixGetData(pixs1); + wpls1 = pixGetWpl(pixs1); + datas2 = pixGetData(pixs2); + wpls2 = pixGetWpl(pixs2); + wd = (l_int32)(2. * scale * pixGetWidth(pixs2)); + hd = (l_int32)(2. * scale * pixGetHeight(pixs2)); + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs1); + pixCopyResolution(pixd, pixs1); + pixScaleResolution(pixd, scale, scale); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + scaleMipmapLow(datad, wd, hd, wpld, datas1, wpls1, datas2, wpls2, scale); + return pixd; +} + + +/*------------------------------------------------------------------* + * Replicated (integer) expansion * + *------------------------------------------------------------------*/ +/*! + * \brief pixExpandReplicate() + * + * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp + * \param[in] factor integer scale factor for replicative expansion + * \return pixd scaled up, or NULL on error. + */ +PIX * +pixExpandReplicate(PIX *pixs, + l_int32 factor) +{ +l_int32 w, h, d, wd, hd, wpls, wpld, start, i, j, k; +l_uint8 sval; +l_uint16 sval16; +l_uint32 sval32; +l_uint32 *lines, *datas, *lined, *datad; +PIX *pixd; + + PROCNAME("pixExpandReplicate"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return (PIX *)ERROR_PTR("depth not in {1,2,4,8,16,32}", procName, NULL); + if (factor <= 0) + return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL); + if (factor == 1) + return pixCopy(NULL, pixs); + + if (d == 1) + return pixExpandBinaryReplicate(pixs, factor, factor); + + wd = factor * w; + hd = factor * h; + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyColormap(pixd, pixs); + pixCopyInputFormat(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + switch (d) { + case 2: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + factor * i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_DIBIT(lines, j); + start = factor * j; + for (k = 0; k < factor; k++) + SET_DATA_DIBIT(lined, start + k, sval); + } + for (k = 1; k < factor; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + break; + case 4: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + factor * i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_QBIT(lines, j); + start = factor * j; + for (k = 0; k < factor; k++) + SET_DATA_QBIT(lined, start + k, sval); + } + for (k = 1; k < factor; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + break; + case 8: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + factor * i * wpld; + for (j = 0; j < w; j++) { + sval = GET_DATA_BYTE(lines, j); + start = factor * j; + for (k = 0; k < factor; k++) + SET_DATA_BYTE(lined, start + k, sval); + } + for (k = 1; k < factor; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + break; + case 16: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + factor * i * wpld; + for (j = 0; j < w; j++) { + sval16 = GET_DATA_TWO_BYTES(lines, j); + start = factor * j; + for (k = 0; k < factor; k++) + SET_DATA_TWO_BYTES(lined, start + k, sval16); + } + for (k = 1; k < factor; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + break; + case 32: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + factor * i * wpld; + for (j = 0; j < w; j++) { + sval32 = *(lines + j); + start = factor * j; + for (k = 0; k < factor; k++) + *(lined + start + k) = sval32; + } + for (k = 1; k < factor; k++) + memcpy(lined + k * wpld, lined, 4 * wpld); + } + break; + default: + fprintf(stderr, "invalid depth\n"); + } + + if (d == 32 && pixGetSpp(pixs) == 4) + pixScaleAndTransferAlpha(pixd, pixs, (l_float32)factor, + (l_float32)factor); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Downscaling using min or max * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixScaleGrayMinMax() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] xfact x downscaling factor; integer + * \param[in] yfact y downscaling factor; integer + * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF + * \return pixd 8 bpp + * + *
+ * Notes:
+ *      (1) The downscaled pixels in pixd are the min, max or (max - min)
+ *          of the corresponding set of xfact * yfact pixels in pixs.
+ *      (2) Using L_CHOOSE_MIN is equivalent to a grayscale erosion,
+ *          using a brick Sel of size (xfact * yfact), followed by
+ *          subsampling within each (xfact * yfact) cell.  Using
+ *          L_CHOOSE_MAX is equivalent to the corresponding dilation.
+ *      (3) Using L_CHOOSE_MAXDIFF finds the difference between max
+ *          and min values in each cell.
+ *      (4) For the special case of downscaling by 2x in both directions,
+ *          pixScaleGrayMinMax2() is about 2x more efficient.
+ * 
+ */ +PIX * +pixScaleGrayMinMax(PIX *pixs, + l_int32 xfact, + l_int32 yfact, + l_int32 type) +{ +l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k, m; +l_int32 minval, maxval, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixScaleGrayMinMax"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + pixGetDimensions(pixs, &ws, &hs, NULL); + if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && + type != L_CHOOSE_MAXDIFF) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (xfact < 1 || yfact < 1) + return (PIX *)ERROR_PTR("xfact and yfact must be >= 1", procName, NULL); + + if (xfact == 2 && yfact == 2) + return pixScaleGrayMinMax2(pixs, type); + + wd = ws / xfact; + if (wd == 0) { /* single tile */ + wd = 1; + xfact = ws; + } + hd = hs / yfact; + if (hd == 0) { /* single tile */ + hd = 1; + yfact = hs; + } + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < hd; i++) { + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAXDIFF) { + minval = 255; + for (k = 0; k < yfact; k++) { + lines = datas + (yfact * i + k) * wpls; + for (m = 0; m < xfact; m++) { + val = GET_DATA_BYTE(lines, xfact * j + m); + if (val < minval) + minval = val; + } + } + } + if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAXDIFF) { + maxval = 0; + for (k = 0; k < yfact; k++) { + lines = datas + (yfact * i + k) * wpls; + for (m = 0; m < xfact; m++) { + val = GET_DATA_BYTE(lines, xfact * j + m); + if (val > maxval) + maxval = val; + } + } + } + if (type == L_CHOOSE_MIN) + SET_DATA_BYTE(lined, j, minval); + else if (type == L_CHOOSE_MAX) + SET_DATA_BYTE(lined, j, maxval); + else /* type == L_CHOOSE_MAXDIFF */ + SET_DATA_BYTE(lined, j, maxval - minval); + } + } + + return pixd; +} + + +/*! + * \brief pixScaleGrayMinMax2() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF + * \return pixd 8 bpp downscaled by 2x + * + *
+ * Notes:
+ *      (1) Special version for 2x reduction.  The downscaled pixels
+ *          in pixd are the min, max or (max - min) of the corresponding
+ *          set of 4 pixels in pixs.
+ *      (2) The max and min operations are a special case (for levels 1
+ *          and 4) of grayscale analog to the binary rank scaling operation
+ *          pixReduceRankBinary2().  Note, however, that because of
+ *          the photometric definition that higher gray values are
+ *          lighter, the erosion-like L_CHOOSE_MIN will darken
+ *          the resulting image, corresponding to a threshold level 1
+ *          in the binary case.  Likewise, L_CHOOSE_MAX will lighten
+ *          the pixd, corresponding to a threshold level of 4.
+ *      (3) To choose any of the four rank levels in a 2x grayscale
+ *          reduction, use pixScaleGrayRank2().
+ *      (4) This runs at about 70 MPix/sec/GHz of source data for
+ *          erosion and dilation.
+ * 
+ */ +PIX * +pixScaleGrayMinMax2(PIX *pixs, + l_int32 type) +{ +l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k; +l_int32 minval, maxval; +l_int32 val[4]; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixScaleGrayMinMax2"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + pixGetDimensions(pixs, &ws, &hs, NULL); + if (ws < 2 || hs < 2) + return (PIX *)ERROR_PTR("too small: ws < 2 or hs < 2", procName, NULL); + if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && + type != L_CHOOSE_MAXDIFF) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + + wd = ws / 2; + hd = hs / 2; + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < hd; i++) { + lines = datas + 2 * i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + val[0] = GET_DATA_BYTE(lines, 2 * j); + val[1] = GET_DATA_BYTE(lines, 2 * j + 1); + val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); + val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); + if (type == L_CHOOSE_MIN || type == L_CHOOSE_MAXDIFF) { + minval = 255; + for (k = 0; k < 4; k++) { + if (val[k] < minval) + minval = val[k]; + } + } + if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAXDIFF) { + maxval = 0; + for (k = 0; k < 4; k++) { + if (val[k] > maxval) + maxval = val[k]; + } + } + if (type == L_CHOOSE_MIN) + SET_DATA_BYTE(lined, j, minval); + else if (type == L_CHOOSE_MAX) + SET_DATA_BYTE(lined, j, maxval); + else /* type == L_CHOOSE_MAXDIFF */ + SET_DATA_BYTE(lined, j, maxval - minval); + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Grayscale downscaling using rank value * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixScaleGrayRankCascade() + * + * \param[in] pixs 8 bpp, not cmapped + * \param[in] level1, level2 ... + * \param[in] level3, level4 rank thresholds, in set {0, 1, 2, 3, 4} + * \return pixd 8 bpp, downscaled by up to 16x + * + *
+ * Notes:
+ *      (1) This performs up to four cascaded 2x rank reductions.
+ *      (2) Use level = 0 to truncate the cascade.
+ * 
+ */ +PIX * +pixScaleGrayRankCascade(PIX *pixs, + l_int32 level1, + l_int32 level2, + l_int32 level3, + l_int32 level4) +{ +PIX *pixt1, *pixt2, *pixt3, *pixt4; + + PROCNAME("pixScaleGrayRankCascade"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4) + return (PIX *)ERROR_PTR("levels must not exceed 4", procName, NULL); + + if (level1 <= 0) { + L_WARNING("no reduction because level1 not > 0\n", procName); + return pixCopy(NULL, pixs); + } + + pixt1 = pixScaleGrayRank2(pixs, level1); + if (level2 <= 0) + return pixt1; + + pixt2 = pixScaleGrayRank2(pixt1, level2); + pixDestroy(&pixt1); + if (level3 <= 0) + return pixt2; + + pixt3 = pixScaleGrayRank2(pixt2, level3); + pixDestroy(&pixt2); + if (level4 <= 0) + return pixt3; + + pixt4 = pixScaleGrayRank2(pixt3, level4); + pixDestroy(&pixt3); + return pixt4; +} + + +/*! + * \brief pixScaleGrayRank2() + * + * \param[in] pixs 8 bpp, no cmap + * \param[in] rank 1 (darkest), 2, 3, 4 (lightest) + * \return pixd 8 bpp, downscaled by 2x + * + *
+ * Notes:
+ *      (1) Rank 2x reduction.  If rank == 1(4), the downscaled pixels
+ *          in pixd are the min(max) of the corresponding set of
+ *          4 pixels in pixs.  Values 2 and 3 are intermediate.
+ *      (2) This is the grayscale analog to the binary rank scaling operation
+ *          pixReduceRankBinary2().  Here, because of the photometric
+ *          definition that higher gray values are lighter, rank 1 gives
+ *          the darkest pixel, whereas rank 4 gives the lightest pixel.
+ *          This is opposite to the binary rank operation.
+ *      (3) For rank = 1 and 4, this calls pixScaleGrayMinMax2(),
+ *          which runs at about 70 MPix/sec/GHz of source data.
+ *          For rank 2 and 3, this runs 3x slower, at about 25 MPix/sec/GHz.
+ * 
+ */ +PIX * +pixScaleGrayRank2(PIX *pixs, + l_int32 rank) +{ +l_int32 ws, hs, wd, hd, wpls, wpld, i, j, k, m; +l_int32 minval, maxval, rankval, minindex, maxindex; +l_int32 val[4]; +l_int32 midval[4]; /* should only use 2 of these */ +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixScaleGrayRank2"); + + if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs undefined, not 8 bpp, or cmapped", + procName, NULL); + if (rank < 1 || rank > 4) + return (PIX *)ERROR_PTR("invalid rank", procName, NULL); + + if (rank == 1) + return pixScaleGrayMinMax2(pixs, L_CHOOSE_MIN); + if (rank == 4) + return pixScaleGrayMinMax2(pixs, L_CHOOSE_MAX); + + pixGetDimensions(pixs, &ws, &hs, NULL); + wd = ws / 2; + hd = hs / 2; + if ((pixd = pixCreate(wd, hd, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixCopyInputFormat(pixd, pixs); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < hd; i++) { + lines = datas + 2 * i * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + val[0] = GET_DATA_BYTE(lines, 2 * j); + val[1] = GET_DATA_BYTE(lines, 2 * j + 1); + val[2] = GET_DATA_BYTE(lines + wpls, 2 * j); + val[3] = GET_DATA_BYTE(lines + wpls, 2 * j + 1); + minval = maxval = val[0]; + minindex = maxindex = 0; + for (k = 1; k < 4; k++) { + if (val[k] < minval) { + minval = val[k]; + minindex = k; + continue; + } + if (val[k] > maxval) { + maxval = val[k]; + maxindex = k; + } + } + for (k = 0, m = 0; k < 4; k++) { + if (k == minindex || k == maxindex) + continue; + midval[m++] = val[k]; + } + if (m > 2) /* minval == maxval; all val[k] are the same */ + rankval = minval; + else if (rank == 2) + rankval = L_MIN(midval[0], midval[1]); + else /* rank == 3 */ + rankval = L_MAX(midval[0], midval[1]); + SET_DATA_BYTE(lined, j, rankval); + } + } + + return pixd; +} + + +/*------------------------------------------------------------------------* + * Helper function for transferring alpha with scaling * + *------------------------------------------------------------------------*/ +/*! + * \brief pixScaleAndTransferAlpha() + * + * \param[in] pixd 32 bpp, scaled image + * \param[in] pixs 32 bpp, original unscaled image + * \param[in] scalex must be > 0.0 + * \param[in] scaley must be > 0.0 + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This scales the alpha component of pixs and inserts into pixd.
+ * 
+ */ +l_ok +pixScaleAndTransferAlpha(PIX *pixd, + PIX *pixs, + l_float32 scalex, + l_float32 scaley) +{ +PIX *pix1, *pix2; + + PROCNAME("pixScaleAndTransferAlpha"); + + if (!pixs || !pixd) + return ERROR_INT("pixs and pixd not both defined", procName, 1); + if (pixGetDepth(pixs) != 32 || pixGetSpp(pixs) != 4) + return ERROR_INT("pixs not 32 bpp and 4 spp", procName, 1); + if (pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not 32 bpp", procName, 1); + + if (scalex == 1.0 && scaley == 1.0) { + pixCopyRGBComponent(pixd, pixs, L_ALPHA_CHANNEL); + return 0; + } + + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixScale(pix1, scalex, scaley); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + return 0; +} + + +/*------------------------------------------------------------------------* + * RGB scaling including alpha (blend) component and gamma transform * + *------------------------------------------------------------------------*/ +/*! + * \brief pixScaleWithAlpha() + * + * \param[in] pixs 32 bpp rgb or cmapped + * \param[in] scalex must be > 0.0 + * \param[in] scaley must be > 0.0 + * \param[in] pixg [optional] 8 bpp, can be null + * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent + * and 1.0 fully opaque + * \return pixd 32 bpp rgba, or NULL on error + * + *
+ * Notes:
+ *      (1) The alpha channel is transformed separately from pixs,
+ *          and aligns with it, being fully transparent outside the
+ *          boundary of the transformed pixs.  For pixels that are fully
+ *          transparent, a blending function like pixBlendWithGrayMask()
+ *          will give zero weight to corresponding pixels in pixs.
+ *      (2) Scaling is done with area mapping or linear interpolation,
+ *          depending on the scale factors.  Default sharpening is done.
+ *      (3) If pixg is NULL, it is generated as an alpha layer that is
+ *          partially opaque, using %fract.  Otherwise, it is cropped
+ *          to pixs if required, and %fract is ignored.  The alpha
+ *          channel in pixs is never used.
+ *      (4) Colormaps are removed to 32 bpp.
+ *      (5) The default setting for the border values in the alpha channel
+ *          is 0 (transparent) for the outermost ring of pixels and
+ *          (0.5 * fract * 255) for the second ring.  When blended over
+ *          a second image, this
+ *          (a) shrinks the visible image to make a clean overlap edge
+ *              with an image below, and
+ *          (b) softens the edges by weakening the aliasing there.
+ *          Use l_setAlphaMaskBorder() to change these values.
+ *      (6) A subtle use of gamma correction is to remove gamma correction
+ *          before scaling and restore it afterwards.  This is done
+ *          by sandwiching this function between a gamma/inverse-gamma
+ *          photometric transform:
+ *              pixt = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255);
+ *              pixd = pixScaleWithAlpha(pixt, scalex, scaley, NULL, fract);
+ *              pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255);
+ *              pixDestroy(&pixt);
+ *          This has the side-effect of producing artifacts in the very
+ *          dark regions.
+ * 
+ */ +PIX * +pixScaleWithAlpha(PIX *pixs, + l_float32 scalex, + l_float32 scaley, + PIX *pixg, + l_float32 fract) +{ +l_int32 ws, hs, d, spp; +PIX *pixd, *pix32, *pixg2, *pixgs; + + PROCNAME("pixScaleWithAlpha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &ws, &hs, &d); + if (d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); + if (scalex <= 0.0 || scaley <= 0.0) + return (PIX *)ERROR_PTR("scale factor <= 0.0", procName, NULL); + if (pixg && pixGetDepth(pixg) != 8) { + L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", + procName); + pixg = NULL; + } + if (!pixg && (fract < 0.0 || fract > 1.0)) { + L_WARNING("invalid fract; using fully opaque\n", procName); + fract = 1.0; + } + if (!pixg && fract == 0.0) + L_WARNING("transparent alpha; image will not be blended\n", procName); + + /* Make sure input to scaling is 32 bpp rgb, and scale it */ + if (d != 32) + pix32 = pixConvertTo32(pixs); + else + pix32 = pixClone(pixs); + spp = pixGetSpp(pix32); + pixSetSpp(pix32, 3); /* ignore the alpha channel for scaling */ + pixd = pixScale(pix32, scalex, scaley); + pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */ + pixDestroy(&pix32); + + /* Set up alpha layer with a fading border and scale it */ + if (!pixg) { + pixg2 = pixCreate(ws, hs, 8); + if (fract == 1.0) + pixSetAll(pixg2); + else if (fract > 0.0) + pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); + } else { + pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); + } + if (ws > 10 && hs > 10) { /* see note 4 */ + pixSetBorderRingVal(pixg2, 1, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); + pixSetBorderRingVal(pixg2, 2, + (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); + } + pixgs = pixScaleGeneral(pixg2, scalex, scaley, 0.0, 0); + + /* Combine into a 4 spp result */ + pixSetRGBComponent(pixd, pixgs, L_ALPHA_CHANNEL); + pixCopyInputFormat(pixd, pixs); + + pixDestroy(&pixg2); + pixDestroy(&pixgs); + return pixd; +} + + +/* ================================================================ * + * Low level static functions * + * ================================================================ */ + +/*------------------------------------------------------------------* + * Scale-to-gray 2x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray2Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] sumtab made from makeSumTabSG2() + * \param[in] valtab made from makeValTabSG2() + * \return 0 if OK; 1 on error. + * + * The output is processed in sets of 4 output bytes on a row, + * corresponding to 4 2x2 bit-blocks in the input image. + * Two lookup tables are used. The first, sumtab, gets the + * sum of ON pixels in 4 sets of two adjacent bits, + * storing the result in 4 adjacent bytes. After sums from + * two rows have been added, the second table, valtab, + * converts from the sum of ON pixels in the 2x2 block to + * an 8 bpp grayscale value between 0 for 4 bits ON + * and 255 for 0 bits ON. + */ +static void +scaleToGray2Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *sumtab, + l_uint8 *valtab) +{ +l_int32 i, j, l, k, m, wd4, extra; +l_uint32 sbyte1, sbyte2, sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * l indexes the source lines + * j indexes the dest bytes + * k indexes the source bytes + * We take two bytes from the source (in 2 lines of 8 pixels + * each) and convert them into four 8 bpp bytes of the dest. */ + wd4 = wd & 0xfffffffc; + extra = wd - wd4; + for (i = 0, l = 0; i < hd; i++, l += 2) { + lines = datas + l * wpls; + lined = datad + i * wpld; + for (j = 0, k = 0; j < wd4; j += 4, k++) { + sbyte1 = GET_DATA_BYTE(lines, k); + sbyte2 = GET_DATA_BYTE(lines + wpls, k); + sum = sumtab[sbyte1] + sumtab[sbyte2]; + SET_DATA_BYTE(lined, j, valtab[sum >> 24]); + SET_DATA_BYTE(lined, j + 1, valtab[(sum >> 16) & 0xff]); + SET_DATA_BYTE(lined, j + 2, valtab[(sum >> 8) & 0xff]); + SET_DATA_BYTE(lined, j + 3, valtab[sum & 0xff]); + } + if (extra > 0) { + sbyte1 = GET_DATA_BYTE(lines, k); + sbyte2 = GET_DATA_BYTE(lines + wpls, k); + sum = sumtab[sbyte1] + sumtab[sbyte2]; + for (m = 0; m < extra; m++) { + SET_DATA_BYTE(lined, j + m, + valtab[((sum >> (24 - 8 * m)) & 0xff)]); + } + } + + } + + return; +} + + +/*! + * \brief makeSumTabSG2() + * + * Returns a table of 256 l_uint32s, giving the four output + * 8-bit grayscale sums corresponding to 8 input bits of a binary + * image, for a 2x scale-to-gray op. The sums from two + * adjacent scanlines are then added and transformed to + * output four 8 bpp pixel values, using makeValTabSG2(). + */ +static l_uint32 * +makeSumTabSG2(void) +{ +l_int32 i; +l_int32 sum[] = {0, 1, 1, 2}; +l_uint32 *tab; + + PROCNAME("makeSumTabSG2"); + + if ((tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32))) == NULL) + return (l_uint32 *)ERROR_PTR("tab not made", procName, NULL); + + /* Pack the four sums separately in four bytes */ + for (i = 0; i < 256; i++) { + tab[i] = (sum[i & 0x3] | sum[(i >> 2) & 0x3] << 8 | + sum[(i >> 4) & 0x3] << 16 | sum[(i >> 6) & 0x3] << 24); + } + return tab; +} + + +/*! + * \brief makeValTabSG2() + * + * Returns an 8 bit value for the sum of ON pixels + * in a 2x2 square, according to + * + * val = 255 - (255 * sum)/4 + * + * where sum is in set {0,1,2,3,4} + */ +static l_uint8 * +makeValTabSG2(void) +{ +l_int32 i; +l_uint8 *tab; + + PROCNAME("makeValTabSG2"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(5, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + for (i = 0; i < 5; i++) + tab[i] = 255 - (i * 255) / 4; + return tab; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray 3x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray3Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] sumtab made from makeSumTabSG3() + * \param[in] valtab made from makeValTabSG3() + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *  Each set of 8 3x3 bit-blocks in the source image, which
+ *  consist of 72 pixels arranged 24 pixels wide by 3 scanlines,
+ *  is converted to a row of 8 8-bit pixels in the dest image.
+ *  These 72 pixels of the input image are runs of 24 pixels
+ *  in three adjacent scanlines.  Each run of 24 pixels is
+ *  stored in the 24 LSbits of a 32-bit word.  We use 2 LUTs.
+ *  The first, sumtab, takes 6 of these bits and stores
+ *  sum, taken 3 bits at a time, in two bytes.  (See
+ *  makeSumTabSG3).  This is done for each of the 3 scanlines,
+ *  and the results are added.  We now have the sum of ON pixels
+ *  in the first two 3x3 blocks in two bytes.  The valtab LUT
+ *  then converts these values (which go from 0 to 9) to
+ *  grayscale values between between 255 and 0.  (See makeValTabSG3).
+ *  This process is repeated for each of the other 3 sets of
+ *  6x3 input pixels, giving 8 output pixels in total.
+ *
+ *  Note: because the input image is processed in groups of
+ *        24 x 3 pixels, the process clips the input height to
+ *        (h - h % 3) and the input width to (w - w % 24).
+ * 
+ */ +static void +scaleToGray3Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *sumtab, + l_uint8 *valtab) +{ +l_int32 i, j, l, k; +l_uint32 threebytes1, threebytes2, threebytes3, sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * l indexes the source lines + * j indexes the dest bytes + * k indexes the source bytes + * We take 9 bytes from the source (72 binary pixels + * in three lines of 24 pixels each) and convert it + * into 8 bytes of the dest (8 8bpp pixels in one line) */ + for (i = 0, l = 0; i < hd; i++, l += 3) { + lines = datas + l * wpls; + lined = datad + i * wpld; + for (j = 0, k = 0; j < wd; j += 8, k += 3) { + threebytes1 = (GET_DATA_BYTE(lines, k) << 16) | + (GET_DATA_BYTE(lines, k + 1) << 8) | + GET_DATA_BYTE(lines, k + 2); + threebytes2 = (GET_DATA_BYTE(lines + wpls, k) << 16) | + (GET_DATA_BYTE(lines + wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + wpls, k + 2); + threebytes3 = (GET_DATA_BYTE(lines + 2 * wpls, k) << 16) | + (GET_DATA_BYTE(lines + 2 * wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + 2 * wpls, k + 2); + + sum = sumtab[(threebytes1 >> 18)] + + sumtab[(threebytes2 >> 18)] + + sumtab[(threebytes3 >> 18)]; + SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 2)]); + SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); + + sum = sumtab[((threebytes1 >> 12) & 0x3f)] + + sumtab[((threebytes2 >> 12) & 0x3f)] + + sumtab[((threebytes3 >> 12) & 0x3f)]; + SET_DATA_BYTE(lined, j + 2, valtab[GET_DATA_BYTE(&sum, 2)]); + SET_DATA_BYTE(lined, j + 3, valtab[GET_DATA_BYTE(&sum, 3)]); + + sum = sumtab[((threebytes1 >> 6) & 0x3f)] + + sumtab[((threebytes2 >> 6) & 0x3f)] + + sumtab[((threebytes3 >> 6) & 0x3f)]; + SET_DATA_BYTE(lined, j + 4, valtab[GET_DATA_BYTE(&sum, 2)]); + SET_DATA_BYTE(lined, j + 5, valtab[GET_DATA_BYTE(&sum, 3)]); + + sum = sumtab[(threebytes1 & 0x3f)] + + sumtab[(threebytes2 & 0x3f)] + + sumtab[(threebytes3 & 0x3f)]; + SET_DATA_BYTE(lined, j + 6, valtab[GET_DATA_BYTE(&sum, 2)]); + SET_DATA_BYTE(lined, j + 7, valtab[GET_DATA_BYTE(&sum, 3)]); + } + } + + return; +} + + + +/*! + * \brief makeSumTabSG3() + * + * Returns a table of 64 l_uint32s, giving the two output + * 8-bit grayscale sums corresponding to 6 input bits of a binary + * image, for a 3x scale-to-gray op. In practice, this would + * be used three times (on adjacent scanlines), and the sums would + * be added and then transformed to output 8 bpp pixel values, + * using makeValTabSG3(). + */ +static l_uint32 * +makeSumTabSG3(void) +{ +l_int32 i; +l_int32 sum[] = {0, 1, 1, 2, 1, 2, 2, 3}; +l_uint32 *tab; + + PROCNAME("makeSumTabSG3"); + + if ((tab = (l_uint32 *)LEPT_CALLOC(64, sizeof(l_uint32))) == NULL) + return (l_uint32 *)ERROR_PTR("tab not made", procName, NULL); + + /* Pack the two sums separately in two bytes */ + for (i = 0; i < 64; i++) { + tab[i] = (sum[i & 0x07]) | (sum[(i >> 3) & 0x07] << 8); + } + return tab; +} + + +/*! + * \brief makeValTabSG3() + * + * Returns an 8 bit value for the sum of ON pixels + * in a 3x3 square, according to + * val = 255 - (255 * sum)/9 + * where sum is in set {0, ... ,9} + */ +static l_uint8 * +makeValTabSG3(void) +{ +l_int32 i; +l_uint8 *tab; + + PROCNAME("makeValTabSG3"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(10, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + for (i = 0; i < 10; i++) + tab[i] = 0xff - (i * 255) / 9; + return tab; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray 4x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray4Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] sumtab made from makeSumTabSG4() + * \param[in] valtab made from makeValTabSG4() + * \return 0 if OK; 1 on error. + * + * The output is processed in sets of 2 output bytes on a row, + * corresponding to 2 4x4 bit-blocks in the input image. + * Two lookup tables are used. The first, sumtab, gets the + * sum of ON pixels in two sets of four adjacent bits, + * storing the result in 2 adjacent bytes. After sums from + * four rows have been added, the second table, valtab, + * converts from the sum of ON pixels in the 4x4 block to + * an 8 bpp grayscale value between 0 for 16 bits ON + * and 255 for 0 bits ON. + */ +static void +scaleToGray4Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_uint32 *sumtab, + l_uint8 *valtab) +{ +l_int32 i, j, l, k; +l_uint32 sbyte1, sbyte2, sbyte3, sbyte4, sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * l indexes the source lines + * j indexes the dest bytes + * k indexes the source bytes + * We take four bytes from the source (in 4 lines of 8 pixels + * each) and convert it into two 8 bpp bytes of the dest. */ + for (i = 0, l = 0; i < hd; i++, l += 4) { + lines = datas + l * wpls; + lined = datad + i * wpld; + for (j = 0, k = 0; j < wd; j += 2, k++) { + sbyte1 = GET_DATA_BYTE(lines, k); + sbyte2 = GET_DATA_BYTE(lines + wpls, k); + sbyte3 = GET_DATA_BYTE(lines + 2 * wpls, k); + sbyte4 = GET_DATA_BYTE(lines + 3 * wpls, k); + sum = sumtab[sbyte1] + sumtab[sbyte2] + + sumtab[sbyte3] + sumtab[sbyte4]; + SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 2)]); + SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); + } + } + + return; +} + + +/*! + * \brief makeSumTabSG4() + * + * Returns a table of 256 l_uint32s, giving the two output + * 8-bit grayscale sums corresponding to 8 input bits of a binary + * image, for a 4x scale-to-gray op. The sums from four + * adjacent scanlines are then added and transformed to + * output 8 bpp pixel values, using makeValTabSG4(). + */ +static l_uint32 * +makeSumTabSG4(void) +{ +l_int32 i; +l_int32 sum[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; +l_uint32 *tab; + + PROCNAME("makeSumTabSG4"); + + if ((tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32))) == NULL) + return (l_uint32 *)ERROR_PTR("tab not made", procName, NULL); + + /* Pack the two sums separately in two bytes */ + for (i = 0; i < 256; i++) { + tab[i] = (sum[i & 0xf]) | (sum[(i >> 4) & 0xf] << 8); + } + return tab; +} + + +/*! + * \brief makeValTabSG4() + * + * Returns an 8 bit value for the sum of ON pixels + * in a 4x4 square, according to + * + * val = 255 - (255 * sum)/16 + * + * where sum is in set {0, ... ,16} + */ +static l_uint8 * +makeValTabSG4(void) +{ +l_int32 i; +l_uint8 *tab; + + PROCNAME("makeValTabSG4"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(17, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + for (i = 0; i < 17; i++) + tab[i] = 0xff - (i * 255) / 16; + return tab; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray 6x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray6Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] tab8 made from makePixelSumTab8() + * \param[in] valtab made from makeValTabSG6() + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *  Each set of 4 6x6 bit-blocks in the source image, which
+ *  consist of 144 pixels arranged 24 pixels wide by 6 scanlines,
+ *  is converted to a row of 4 8-bit pixels in the dest image.
+ *  These 144 pixels of the input image are runs of 24 pixels
+ *  in six adjacent scanlines.  Each run of 24 pixels is
+ *  stored in the 24 LSbits of a 32-bit word.  We use 2 LUTs.
+ *  The first, tab8, takes 6 of these bits and stores
+ *  sum in one byte.  This is done for each of the 6 scanlines,
+ *  and the results are added.
+ *  We now have the sum of ON pixels in the first 6x6 block.  The
+ *  valtab LUT then converts these values (which go from 0 to 36) to
+ *  grayscale values between between 255 and 0.  (See makeValTabSG6).
+ *  This process is repeated for each of the other 3 sets of
+ *  6x6 input pixels, giving 4 output pixels in total.
+ *
+ *  Note: because the input image is processed in groups of
+ *        24 x 6 pixels, the process clips the input height to
+ *        (h - h % 6) and the input width to (w - w % 24).
+ *
+ * 
+ */ +static void +scaleToGray6Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 *tab8, + l_uint8 *valtab) +{ +l_int32 i, j, l, k; +l_uint32 threebytes1, threebytes2, threebytes3; +l_uint32 threebytes4, threebytes5, threebytes6, sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * l indexes the source lines + * j indexes the dest bytes + * k indexes the source bytes + * We take 18 bytes from the source (144 binary pixels + * in six lines of 24 pixels each) and convert it + * into 4 bytes of the dest (four 8 bpp pixels in one line) */ + for (i = 0, l = 0; i < hd; i++, l += 6) { + lines = datas + l * wpls; + lined = datad + i * wpld; + for (j = 0, k = 0; j < wd; j += 4, k += 3) { + /* First grab the 18 bytes, 3 at a time, and put each set + * of 3 bytes into the LS bytes of a 32-bit word. */ + threebytes1 = (GET_DATA_BYTE(lines, k) << 16) | + (GET_DATA_BYTE(lines, k + 1) << 8) | + GET_DATA_BYTE(lines, k + 2); + threebytes2 = (GET_DATA_BYTE(lines + wpls, k) << 16) | + (GET_DATA_BYTE(lines + wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + wpls, k + 2); + threebytes3 = (GET_DATA_BYTE(lines + 2 * wpls, k) << 16) | + (GET_DATA_BYTE(lines + 2 * wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + 2 * wpls, k + 2); + threebytes4 = (GET_DATA_BYTE(lines + 3 * wpls, k) << 16) | + (GET_DATA_BYTE(lines + 3 * wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + 3 * wpls, k + 2); + threebytes5 = (GET_DATA_BYTE(lines + 4 * wpls, k) << 16) | + (GET_DATA_BYTE(lines + 4 * wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + 4 * wpls, k + 2); + threebytes6 = (GET_DATA_BYTE(lines + 5 * wpls, k) << 16) | + (GET_DATA_BYTE(lines + 5 * wpls, k + 1) << 8) | + GET_DATA_BYTE(lines + 5 * wpls, k + 2); + + /* Sum first set of 36 bits and convert to 0-255 */ + sum = tab8[(threebytes1 >> 18)] + + tab8[(threebytes2 >> 18)] + + tab8[(threebytes3 >> 18)] + + tab8[(threebytes4 >> 18)] + + tab8[(threebytes5 >> 18)] + + tab8[(threebytes6 >> 18)]; + SET_DATA_BYTE(lined, j, valtab[GET_DATA_BYTE(&sum, 3)]); + + /* Ditto for second set */ + sum = tab8[((threebytes1 >> 12) & 0x3f)] + + tab8[((threebytes2 >> 12) & 0x3f)] + + tab8[((threebytes3 >> 12) & 0x3f)] + + tab8[((threebytes4 >> 12) & 0x3f)] + + tab8[((threebytes5 >> 12) & 0x3f)] + + tab8[((threebytes6 >> 12) & 0x3f)]; + SET_DATA_BYTE(lined, j + 1, valtab[GET_DATA_BYTE(&sum, 3)]); + + sum = tab8[((threebytes1 >> 6) & 0x3f)] + + tab8[((threebytes2 >> 6) & 0x3f)] + + tab8[((threebytes3 >> 6) & 0x3f)] + + tab8[((threebytes4 >> 6) & 0x3f)] + + tab8[((threebytes5 >> 6) & 0x3f)] + + tab8[((threebytes6 >> 6) & 0x3f)]; + SET_DATA_BYTE(lined, j + 2, valtab[GET_DATA_BYTE(&sum, 3)]); + + sum = tab8[(threebytes1 & 0x3f)] + + tab8[(threebytes2 & 0x3f)] + + tab8[(threebytes3 & 0x3f)] + + tab8[(threebytes4 & 0x3f)] + + tab8[(threebytes5 & 0x3f)] + + tab8[(threebytes6 & 0x3f)]; + SET_DATA_BYTE(lined, j + 3, valtab[GET_DATA_BYTE(&sum, 3)]); + } + } + return; +} + + +/*! + * \brief makeValTabSG6() + * + * Returns an 8 bit value for the sum of ON pixels + * in a 6x6 square, according to + * val = 255 - (255 * sum)/36 + * where sum is in set {0, ... ,36} + */ +static l_uint8 * +makeValTabSG6(void) +{ +l_int32 i; +l_uint8 *tab; + + PROCNAME("makeValTabSG6"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(37, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + for (i = 0; i < 37; i++) + tab[i] = 0xff - (i * 255) / 36; + return tab; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray 8x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray8Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] tab8 made from makePixelSumTab8() + * \param[in] valtab made from makeValTabSG8() + * \return 0 if OK; 1 on error. + * + * The output is processed one dest byte at a time, + * corresponding to 8 rows of src bytes in the input image. + * Two lookup tables are used. The first, tab8, gets the + * sum of ON pixels in a byte. After sums from 8 rows have + * been added, the second table, valtab, converts from this + * value which is between 0 and 64 to an 8 bpp grayscale + * value between 0 for all 64 bits ON) and 255 (for 0 bits ON. + */ +static void +scaleToGray8Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 *tab8, + l_uint8 *valtab) +{ +l_int32 i, j, k; +l_int32 sbyte0, sbyte1, sbyte2, sbyte3, sbyte4, sbyte5, sbyte6, sbyte7, sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * k indexes the source lines + * j indexes the src and dest bytes + * We take 8 bytes from the source (in 8 lines of 8 pixels + * each) and convert it into one 8 bpp byte of the dest. */ + for (i = 0, k = 0; i < hd; i++, k += 8) { + lines = datas + k * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + sbyte0 = GET_DATA_BYTE(lines, j); + sbyte1 = GET_DATA_BYTE(lines + wpls, j); + sbyte2 = GET_DATA_BYTE(lines + 2 * wpls, j); + sbyte3 = GET_DATA_BYTE(lines + 3 * wpls, j); + sbyte4 = GET_DATA_BYTE(lines + 4 * wpls, j); + sbyte5 = GET_DATA_BYTE(lines + 5 * wpls, j); + sbyte6 = GET_DATA_BYTE(lines + 6 * wpls, j); + sbyte7 = GET_DATA_BYTE(lines + 7 * wpls, j); + sum = tab8[sbyte0] + tab8[sbyte1] + + tab8[sbyte2] + tab8[sbyte3] + + tab8[sbyte4] + tab8[sbyte5] + + tab8[sbyte6] + tab8[sbyte7]; + SET_DATA_BYTE(lined, j, valtab[sum]); + } + } + + return; +} + + +/*! + * \brief makeValTabSG8() + * + * Returns an 8 bit value for the sum of ON pixels + * in an 8x8 square, according to + * val = 255 - (255 * sum)/64 + * where sum is in set {0, ... ,64} + */ +static l_uint8 * +makeValTabSG8(void) +{ +l_int32 i; +l_uint8 *tab; + + PROCNAME("makeValTabSG8"); + + if ((tab = (l_uint8 *)LEPT_CALLOC(65, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); + for (i = 0; i < 65; i++) + tab[i] = 0xff - (i * 255) / 64; + return tab; +} + + +/*------------------------------------------------------------------* + * Scale-to-gray 16x * + *------------------------------------------------------------------*/ +/*! + * \brief scaleToGray16Low() + * + * \param[in] datad dest data + * \param[in] wd, hd dest width, height + * \param[in] wpld dest words/line + * \param[in] datas src data + * \param[in] wpls src words/line + * \param[in] tab8 made from makePixelSumTab8() + * \return 0 if OK; 1 on error. + * + * The output is processed one dest byte at a time, corresponding + * to 16 rows consisting each of 2 src bytes in the input image. + * This uses one lookup table, tab8, which gives the sum of + * ON pixels in a byte. After summing for all ON pixels in the + * 32 src bytes, which is between 0 and 256, this is converted + * to an 8 bpp grayscale value between 0 for 255 or 256 bits ON + * and 255 for 0 bits ON. + */ +static void +scaleToGray16Low(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas, + l_int32 wpls, + l_int32 *tab8) +{ +l_int32 i, j, k, m; +l_int32 sum; +l_uint32 *lines, *lined; + + /* i indexes the dest lines + * k indexes the source lines + * j indexes the dest bytes + * m indexes the src bytes + * We take 32 bytes from the source (in 16 lines of 16 pixels + * each) and convert it into one 8 bpp byte of the dest. */ + for (i = 0, k = 0; i < hd; i++, k += 16) { + lines = datas + k * wpls; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + m = 2 * j; + sum = tab8[GET_DATA_BYTE(lines, m)]; + sum += tab8[GET_DATA_BYTE(lines, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 2 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 2 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 3 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 3 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 4 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 4 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 5 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 5 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 6 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 6 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 7 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 7 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 8 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 8 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 9 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 9 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 10 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 10 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 11 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 11 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 12 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 12 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 13 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 13 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 14 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 14 * wpls, m + 1)]; + sum += tab8[GET_DATA_BYTE(lines + 15 * wpls, m)]; + sum += tab8[GET_DATA_BYTE(lines + 15 * wpls, m + 1)]; + sum = L_MIN(sum, 255); + SET_DATA_BYTE(lined, j, 255 - sum); + } + } + + return; +} + + + +/*------------------------------------------------------------------* + * Grayscale mipmap * + *------------------------------------------------------------------*/ +/*! + * \brief scaleMipmapLow() + * + * See notes in scale.c for pixScaleToGrayMipmap(). This function + * is here for pedagogical reasons. It gives poor results on document + * images because of aliasing. + */ +static l_int32 +scaleMipmapLow(l_uint32 *datad, + l_int32 wd, + l_int32 hd, + l_int32 wpld, + l_uint32 *datas1, + l_int32 wpls1, + l_uint32 *datas2, + l_int32 wpls2, + l_float32 red) +{ +l_int32 i, j, val1, val2, val, row2, col2; +l_int32 *srow, *scol; +l_uint32 *lines1, *lines2, *lined; +l_float32 ratio, w1, w2; + + PROCNAME("scaleMipmapLow"); + + /* Clear dest */ + memset(datad, 0, 4LL * wpld * hd); + + /* Each dest pixel at (j,i) is computed by interpolating + between the two src images at the corresponding location. + We store the UL corner locations of the square of + src pixels in thelower-resolution image that correspond + to dest pixel (j,i). The are labeled by the arrays + srow[i], scol[j]. The UL corner locations of the higher + resolution src pixels are obtained from these arrays + by multiplying by 2. */ + if ((srow = (l_int32 *)LEPT_CALLOC(hd, sizeof(l_int32))) == NULL) + return ERROR_INT("srow not made", procName, 1); + if ((scol = (l_int32 *)LEPT_CALLOC(wd, sizeof(l_int32))) == NULL) { + LEPT_FREE(srow); + return ERROR_INT("scol not made", procName, 1); + } + ratio = 1. / (2. * red); /* 0.5 for red = 1, 1 for red = 0.5 */ + for (i = 0; i < hd; i++) + srow[i] = (l_int32)(ratio * i); + for (j = 0; j < wd; j++) + scol[j] = (l_int32)(ratio * j); + + /* Get weights for linear interpolation: these are the + * 'distances' of the dest image plane from the two + * src image planes. */ + w1 = 2. * red - 1.; /* w1 --> 1 as red --> 1 */ + w2 = 1. - w1; + + /* For each dest pixel, compute linear interpolation */ + for (i = 0; i < hd; i++) { + row2 = srow[i]; + lines1 = datas1 + 2 * row2 * wpls1; + lines2 = datas2 + row2 * wpls2; + lined = datad + i * wpld; + for (j = 0; j < wd; j++) { + col2 = scol[j]; + val1 = GET_DATA_BYTE(lines1, 2 * col2); + val2 = GET_DATA_BYTE(lines2, col2); + val = (l_int32)(w1 * val1 + w2 * val2); + SET_DATA_BYTE(lined, j, val); + } + } + + LEPT_FREE(srow); + LEPT_FREE(scol); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/seedfill.c b/hgdriver/3rdparty/hgOCR/leptonica/seedfill.c new file mode 100644 index 0000000..a703099 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/seedfill.c @@ -0,0 +1,3454 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file seedfill.c + *
+ *
+ *      Binary seedfill (source: Luc Vincent)
+ *               PIX         *pixSeedfillBinary()
+ *               PIX         *pixSeedfillBinaryRestricted()
+ *               static void  seedfillBinaryLow()
+ *
+ *      Applications of binary seedfill to find and fill holes,
+ *      remove c.c. touching the border and fill bg from border:
+ *               PIX         *pixHolesByFilling()
+ *               PIX         *pixFillClosedBorders()
+ *               PIX         *pixExtractBorderConnComps()
+ *               PIX         *pixRemoveBorderConnComps()
+ *               PIX         *pixFillBgFromBorder()
+ *
+ *      Hole-filling of components to bounding rectangle
+ *               PIX         *pixFillHolesToBoundingRect()
+ *
+ *      Gray seedfill (source: Luc Vincent:fast-hybrid-grayscale-reconstruction)
+ *               l_int32      pixSeedfillGray()
+ *               l_int32      pixSeedfillGrayInv()
+ *               static void  seedfillGrayLow()
+ *               static void  seedfillGrayInvLow()
+
+ *
+ *      Gray seedfill (source: Luc Vincent: sequential-reconstruction algorithm)
+ *               l_int32      pixSeedfillGraySimple()
+ *               l_int32      pixSeedfillGrayInvSimple()
+ *               static void  seedfillGrayLowSimple()
+ *               static void  seedfillGrayInvLowSimple()
+ *
+ *      Gray seedfill variations
+ *               PIX         *pixSeedfillGrayBasin()
+ *
+ *      Distance function (source: Luc Vincent)
+ *               PIX         *pixDistanceFunction()
+ *               static void  distanceFunctionLow()
+ *
+ *      Seed spread (based on distance function)
+ *               PIX         *pixSeedspread()
+ *               static void  seedspreadLow()
+ *
+ *      Local extrema:
+ *               l_int32      pixLocalExtrema()
+ *            static l_int32  pixQualifyLocalMinima()
+ *               l_int32      pixSelectedLocalExtrema()
+ *               PIX         *pixFindEqualValues()
+ *
+ *      Selection of minima in mask of connected components
+ *               PTA         *pixSelectMinInConnComp()
+ *
+ *      Removal of seeded connected components from a mask
+ *               PIX         *pixRemoveSeededComponents()
+ *
+ *
+ *           ITERATIVE RASTER-ORDER SEEDFILL
+ *
+ *      The basic method in the Vincent seedfill (aka reconstruction)
+ *      algorithm is simple.  We describe here the situation for
+ *      binary seedfill.  Pixels are sampled in raster order in
+ *      the seed image.  If they are 4-connected to ON pixels
+ *      either directly above or to the left, and are not masked
+ *      out by the mask image, they are turned on (or remain on).
+ *      (Ditto for 8-connected, except you need to check 3 pixels
+ *      on the previous line as well as the pixel to the left
+ *      on the current line.  This is extra computational work
+ *      for relatively little gain, so it is preferable
+ *      in most situations to use the 4-connected version.)
+ *      The algorithm proceeds from UR to LL of the image, and
+ *      then reverses and sweeps up from LL to UR.
+ *      These double sweeps are iterated until there is no change.
+ *      At this point, the seed has entirely filled the region it
+ *      is allowed to, as delimited by the mask image.
+ *
+ *      The grayscale seedfill is a straightforward generalization
+ *      of the binary seedfill, and is described in seedfillLowGray().
+ *
+ *      For some applications, the filled seed will later be OR'd
+ *      with the negative of the mask.   This is used, for example,
+ *      when you flood fill into a 4-connected region of OFF pixels
+ *      and you want the result after those pixels are turned ON.
+ *
+ *      Note carefully that the mask we use delineates which pixels
+ *      are allowed to be ON as the seed is filled.  We will call this
+ *      a "filling mask".  As the seed expands, it is repeatedly
+ *      ANDed with the filling mask: s & fm.  The process can equivalently
+ *      be formulated using the inverse of the filling mask, which
+ *      we will call a "blocking mask": bm = ~fm.   As the seed
+ *      expands, the blocking mask is repeatedly used to prevent
+ *      the seed from expanding into the blocking mask.  This is done
+ *      by set subtracting the blocking mask from the expanded seed:
+ *      s - bm.  Set subtraction of the blocking mask is equivalent
+ *      to ANDing with the inverse of the blocking mask: s & (~bm).
+ *      But from the inverse relation between blocking and filling
+ *      masks, this is equal to s & fm, which proves the equivalence.
+ *
+ *      For efficiency, the pixels can be taken in larger units
+ *      for processing, but still in raster order.  It is natural
+ *      to take them in 32-bit words.  The outline of the work
+ *      to be done for 4-cc (not including special cases for boundary
+ *      words, such as the first line or the last word in each line)
+ *      is as follows.  Let the filling mask be m.  The
+ *      seed is to fill "under" the mask; i.e., limited by an AND
+ *      with the mask.  Let the current word be w, the word
+ *      in the line above be wa, and the previous word in the
+ *      current line be wp.   Let t be a temporary word that
+ *      is used in computation.  Note that masking is performed by
+ *      w & m.  (If we had instead used a "blocking" mask, we
+ *      would perform masking by the set subtraction operation,
+ *      w - m, which is defined to be w & ~m.)
+ *
+ *      The entire operation can be implemented with shifts,
+ *      logical operations and tests.  For each word in the seed image
+ *      there are two steps.  The first step is to OR the word with
+ *      the word above and with the rightmost pixel in wp (call it "x").
+ *      Because wp is shifted one pixel to its right, "x" is ORed
+ *      to the leftmost pixel of w.  We then clip to the ON pixels in
+ *      the mask.  The result is
+ *               t  <--  (w | wa | x000... ) & m
+ *      We've now finished taking data from above and to the left.
+ *      The second step is to allow filling to propagate horizontally
+ *      in t, always making sure that it is properly masked at each
+ *      step.  So if filling can be done (i.e., t is neither all 0s
+ *      nor all 1s), iteratively take:
+ *           t  <--  (t | (t >> 1) | (t << 1)) & m
+ *      until t stops changing.  Then write t back into w.
+ *
+ *      Finally, the boundary conditions require we note that in doing
+ *      the above steps:
+ *          (a) The words in the first row have no wa
+ *          (b) The first word in each row has no wp in that row
+ *          (c) The last word in each row must be masked so that
+ *              pixels don't propagate beyond the right edge of the
+ *              actual image.  (This is easily accomplished by
+ *              setting the out-of-bound pixels in m to OFF.)
+ * 
+ */ + +#include +#include "allheaders.h" + +struct L_Pixel +{ + l_int32 x; + l_int32 y; +}; +typedef struct L_Pixel L_PIXEL; + +static void seedfillBinaryLow(l_uint32 *datas, l_int32 hs, l_int32 wpls, + l_uint32 *datam, l_int32 hm, l_int32 wplm, + l_int32 connectivity); +static void seedfillGrayLow(l_uint32 *datas, l_int32 w, l_int32 h, + l_int32 wpls, l_uint32 *datam, l_int32 wplm, + l_int32 connectivity); +static void seedfillGrayInvLow(l_uint32 *datas, l_int32 w, l_int32 h, + l_int32 wpls, l_uint32 *datam, l_int32 wplm, + l_int32 connectivity); +static void seedfillGrayLowSimple(l_uint32 *datas, l_int32 w, l_int32 h, + l_int32 wpls, l_uint32 *datam, l_int32 wplm, + l_int32 connectivity); +static void seedfillGrayInvLowSimple(l_uint32 *datas, l_int32 w, l_int32 h, + l_int32 wpls, l_uint32 *datam, + l_int32 wplm, l_int32 connectivity); +static void distanceFunctionLow(l_uint32 *datad, l_int32 w, l_int32 h, + l_int32 d, l_int32 wpld, l_int32 connectivity); +static void seedspreadLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, + l_uint32 *datat, l_int32 wplt, l_int32 connectivity); + + +static l_int32 pixQualifyLocalMinima(PIX *pixs, PIX *pixm, l_int32 maxval); + +#ifndef NO_CONSOLE_IO +#define DEBUG_PRINT_ITERS 0 +#endif /* ~NO_CONSOLE_IO */ + + /* Two-way (UL --> LR, LR --> UL) sweep iterations; typically need only 4 */ +static const l_int32 MaxIters = 40; + + +/*-----------------------------------------------------------------------* + * Vincent's Iterative Binary Seedfill method * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSeedfillBinary() + * + * \param[in] pixd [optional]; can be null, equal to pixs, + * or different from pixs; 1 bpp + * \param[in] pixs 1 bpp seed + * \param[in] pixm 1 bpp filling mask + * \param[in] connectivity 4 or 8 + * \return pixd always + * + *
+ * Notes:
+ *      (1) This is for binary seedfill (aka "binary reconstruction").
+ *      (2) There are 3 cases:
+ *            (a) pixd == null (make a new pixd)
+ *            (b) pixd == pixs (in-place)
+ *            (c) pixd != pixs
+ *      (3) If you know the case, use these patterns for clarity:
+ *            (a) pixd = pixSeedfillBinary(NULL, pixs, ...);
+ *            (b) pixSeedfillBinary(pixs, pixs, ...);
+ *            (c) pixSeedfillBinary(pixd, pixs, ...);
+ *      (4) The resulting pixd contains the filled seed.  For some
+ *          applications you want to OR it with the inverse of
+ *          the filling mask.
+ *      (5) The input seed and mask images can be different sizes, but
+ *          in typical use the difference, if any, would be only
+ *          a few pixels in each direction.  If the sizes differ,
+ *          the clipping is handled by the low-level function
+ *          seedfillBinaryLow().
+ * 
+ */ +PIX * +pixSeedfillBinary(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_int32 connectivity) +{ +l_int32 i, boolval; +l_int32 hd, hm, wpld, wplm; +l_uint32 *datad, *datam; +PIX *pixt; + + PROCNAME("pixSeedfillBinary"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd); + if (!pixm || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, pixd); + + /* Prepare pixd as a copy of pixs if not identical */ + if ((pixd = pixCopy(pixd, pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + + /* pixt is used to test for completion */ + if ((pixt = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixt not made", procName, pixd); + + hd = pixGetHeight(pixd); + hm = pixGetHeight(pixm); /* included so seedfillBinaryLow() can clip */ + datad = pixGetData(pixd); + datam = pixGetData(pixm); + wpld = pixGetWpl(pixd); + wplm = pixGetWpl(pixm); + + pixSetPadBits(pixm, 0); + + for (i = 0; i < MaxIters; i++) { + pixCopy(pixt, pixd); + seedfillBinaryLow(datad, hd, wpld, datam, hm, wplm, connectivity); + pixEqual(pixd, pixt, &boolval); + if (boolval == 1) { +#if DEBUG_PRINT_ITERS + fprintf(stderr, "Binary seed fill converged: %d iters\n", i + 1); +#endif /* DEBUG_PRINT_ITERS */ + break; + } + } + + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief pixSeedfillBinaryRestricted() + * + * \param[in] pixd [optional]; can be null, equal to pixs, + * or different from pixs; 1 bpp + * \param[in] pixs 1 bpp seed + * \param[in] pixm 1 bpp filling mask + * \param[in] connectivity 4 or 8 + * \param[in] xmax max distance in x direction of fill into mask + * \param[in] ymax max distance in y direction of fill into mask + * \return pixd always + * + *
+ * Notes:
+ *      (1) See usage for pixSeedfillBinary(), which has unrestricted fill.
+ *          In pixSeedfillBinary(), the filling distance is unrestricted
+ *          and can be larger than pixs, depending on the topology of
+ *          th mask.
+ *      (2) There are occasions where it is useful not to permit the
+ *          fill to go more than a certain distance into the mask.
+ *          %xmax specifies the maximum horizontal distance allowed
+ *          in the fill; %ymax does likewise in the vertical direction.
+ *      (3) Operationally, the max "distance" allowed for the fill
+ *          is a linear distance from the original seed, independent
+ *          of the actual mask topology.
+ *      (4) Another formulation of this problem, not implemented,
+ *          would use the manhattan distance from the seed, as
+ *          determined by a breadth-first search starting at the seed
+ *          boundaries and working outward where the mask fg allows.
+ *          How this might use the constraints of separate xmax and ymax
+ *          is not clear.
+ * 
+ */ +PIX * +pixSeedfillBinaryRestricted(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_int32 connectivity, + l_int32 xmax, + l_int32 ymax) +{ +l_int32 w, h; +PIX *pix1, *pix2; + + PROCNAME("pixSeedfillBinaryRestricted"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd); + if (!pixm || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, pixd); + if (xmax == 0 && ymax == 0) /* no filling permitted */ + return pixClone(pixs); + if (xmax < 0 || ymax < 0) { + L_ERROR("xmax and ymax must be non-negative", procName); + return pixClone(pixs); + } + + /* Full fill from the seed into the mask. */ + if ((pix1 = pixSeedfillBinary(NULL, pixs, pixm, connectivity)) == NULL) + return (PIX *)ERROR_PTR("pix1 not made", procName, pixd); + + /* Dilate the seed. This gives the maximal region where changes + * are permitted. Invert to get the region where pixs is + * not allowed to change. */ + pix2 = pixDilateCompBrick(NULL, pixs, 2 * xmax + 1, 2 * ymax + 1); + pixInvert(pix2, pix2); + + /* Blank the region of pix1 specified by the fg of pix2. + * This is not yet the final result, because it may have fg pixels + * that are not accessible from the seed in the restricted distance. + * For example, such pixels may be connected to the original seed, + * but through a path that goes outside the permitted region. */ + pixGetDimensions(pixs, &w, &h, NULL); + pixRasterop(pix1, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); + + /* To get the accessible pixels in the restricted region, do + * a second seedfill from the original seed, using pix1 as + * a mask. The result, in pixd, will not have any bad fg + * pixels that were in pix1. */ + pixd = pixSeedfillBinary(pixd, pixs, pix1, connectivity); + + pixDestroy(&pix1); + pixDestroy(&pix2); + return pixd; +} + + +/*! + * \brief seedfillBinaryLow() + * + * Notes: + * (1) This is an in-place fill, where the seed image is + * filled, clipping to the filling mask, in one full + * cycle of UL -> LR and LR -> UL raster scans. + * (2) Assume the mask is a filling mask, not a blocking mask. + * (3) Assume that the RHS pad bits of the mask + * are properly set to 0. + * (4) Clip to the smallest dimensions to avoid invalid reads. + */ +static void +seedfillBinaryLow(l_uint32 *datas, + l_int32 hs, + l_int32 wpls, + l_uint32 *datam, + l_int32 hm, + l_int32 wplm, + l_int32 connectivity) +{ +l_int32 i, j, h, wpl; +l_uint32 word, mask; +l_uint32 wordabove, wordleft, wordbelow, wordright; +l_uint32 wordprev; /* test against this in previous iteration */ +l_uint32 *lines, *linem; + + PROCNAME("seedfillBinaryLow"); + + h = L_MIN(hs, hm); + wpl = L_MIN(wpls, wplm); + + switch (connectivity) + { + case 4: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < wpl; j++) { + word = *(lines + j); + mask = *(linem + j); + + /* OR from word above and from word to left; mask */ + if (i > 0) { + wordabove = *(lines - wpls + j); + word |= wordabove; + } + if (j > 0) { + wordleft = *(lines + j - 1); + word |= wordleft << 31; + } + word &= mask; + + /* No need to fill horizontally? */ + if (!word || !(~word)) { + *(lines + j) = word; + continue; + } + + while (1) { + wordprev = word; + word = (word | (word >> 1) | (word << 1)) & mask; + if ((word ^ wordprev) == 0) { + *(lines + j) = word; + break; + } + } + } + } + + /* LR --> UL scan */ + for (i = h - 1; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = wpl - 1; j >= 0; j--) { + word = *(lines + j); + mask = *(linem + j); + + /* OR from word below and from word to right; mask */ + if (i < h - 1) { + wordbelow = *(lines + wpls + j); + word |= wordbelow; + } + if (j < wpl - 1) { + wordright = *(lines + j + 1); + word |= wordright >> 31; + } + word &= mask; + + /* No need to fill horizontally? */ + if (!word || !(~word)) { + *(lines + j) = word; + continue; + } + + while (1) { + wordprev = word; + word = (word | (word >> 1) | (word << 1)) & mask; + if ((word ^ wordprev) == 0) { + *(lines + j) = word; + break; + } + } + } + } + break; + + case 8: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < wpl; j++) { + word = *(lines + j); + mask = *(linem + j); + + /* OR from words above and from word to left; mask */ + if (i > 0) { + wordabove = *(lines - wpls + j); + word |= (wordabove | (wordabove << 1) | (wordabove >> 1)); + if (j > 0) + word |= (*(lines - wpls + j - 1)) << 31; + if (j < wpl - 1) + word |= (*(lines - wpls + j + 1)) >> 31; + } + if (j > 0) { + wordleft = *(lines + j - 1); + word |= wordleft << 31; + } + word &= mask; + + /* No need to fill horizontally? */ + if (!word || !(~word)) { + *(lines + j) = word; + continue; + } + + while (1) { + wordprev = word; + word = (word | (word >> 1) | (word << 1)) & mask; + if ((word ^ wordprev) == 0) { + *(lines + j) = word; + break; + } + } + } + } + + /* LR --> UL scan */ + for (i = h - 1; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = wpl - 1; j >= 0; j--) { + word = *(lines + j); + mask = *(linem + j); + + /* OR from words below and from word to right; mask */ + if (i < h - 1) { + wordbelow = *(lines + wpls + j); + word |= (wordbelow | (wordbelow << 1) | (wordbelow >> 1)); + if (j > 0) + word |= (*(lines + wpls + j - 1)) << 31; + if (j < wpl - 1) + word |= (*(lines + wpls + j + 1)) >> 31; + } + if (j < wpl - 1) { + wordright = *(lines + j + 1); + word |= wordright >> 31; + } + word &= mask; + + /* No need to fill horizontally? */ + if (!word || !(~word)) { + *(lines + j) = word; + continue; + } + + while (1) { + wordprev = word; + word = (word | (word >> 1) | (word << 1)) & mask; + if ((word ^ wordprev) == 0) { + *(lines + j) = word; + break; + } + } + } + } + break; + + default: + L_ERROR("connectivity must be 4 or 8\n", procName); + return; + } +} + + +/*! + * \brief pixHolesByFilling() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \return pixd inverted image of all holes, or NULL on error + * + * Action: + * 1 Start with 1-pixel black border on otherwise white pixd + * 2 Use the inverted pixs as the filling mask to fill in + * all the pixels from the border to the pixs foreground + * 3 OR the result with pixs to have an image with all + * ON pixels except for the holes. + * 4 Invert the result to get the holes as foreground + * + *
+ * Notes:
+ *     (1) To get 4-c.c. holes of the 8-c.c. as foreground, use
+ *         4-connected filling; to get 8-c.c. holes of the 4-c.c.
+ *         as foreground, use 8-connected filling.
+ * 
+ */ +PIX * +pixHolesByFilling(PIX *pixs, + l_int32 connectivity) +{ +PIX *pixsi, *pixd; + + PROCNAME("pixHolesByFilling"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + if ((pixsi = pixInvert(NULL, pixs)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pixsi not made", procName, NULL); + } + + pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET); + pixSeedfillBinary(pixd, pixd, pixsi, connectivity); + pixOr(pixd, pixd, pixs); + pixInvert(pixd, pixd); + pixDestroy(&pixsi); + return pixd; +} + + +/*! + * \brief pixFillClosedBorders() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity filling connectivity 4 or 8 + * \return pixd all topologically outer closed borders are filled + * as connected comonents, or NULL on error + * + *
+ * Notes:
+ *      (1) Start with 1-pixel black border on otherwise white pixd
+ *      (2) Subtract input pixs to remove border pixels that were
+ *          also on the closed border
+ *      (3) Use the inverted pixs as the filling mask to fill in
+ *          all the pixels from the outer border to the closed border
+ *          on pixs
+ *      (4) Invert the result to get the filled component, including
+ *          the input border
+ *      (5) If the borders are 4-c.c., use 8-c.c. filling, and v.v.
+ *      (6) Closed borders within c.c. that represent holes, etc., are filled.
+ * 
+ */ +PIX * +pixFillClosedBorders(PIX *pixs, + l_int32 connectivity) +{ +PIX *pixsi, *pixd; + + PROCNAME("pixFillClosedBorders"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET); + pixSubtract(pixd, pixd, pixs); + if ((pixsi = pixInvert(NULL, pixs)) == NULL) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("pixsi not made", procName, NULL); + } + + pixSeedfillBinary(pixd, pixd, pixsi, connectivity); + pixInvert(pixd, pixd); + pixDestroy(&pixsi); + + return pixd; +} + + +/*! + * \brief pixExtractBorderConnComps() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity filling connectivity 4 or 8 + * \return pixd all pixels in the src that are in connected + * components touching the border, or NULL on error + */ +PIX * +pixExtractBorderConnComps(PIX *pixs, + l_int32 connectivity) +{ +PIX *pixd; + + PROCNAME("pixExtractBorderConnComps"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + /* Start with 1 pixel wide black border as seed in pixd */ + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET); + + /* Fill in pixd from the seed, using pixs as the filling mask. + * This fills all components from pixs that are touching the border. */ + pixSeedfillBinary(pixd, pixd, pixs, connectivity); + + return pixd; +} + + +/*! + * \brief pixRemoveBorderConnComps() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity filling connectivity 4 or 8 + * \return pixd all pixels in the src that are not touching the + * border or NULL on error + * + *
+ * Notes:
+ *      (1) This removes all fg components touching the border.
+ * 
+ */ +PIX * +pixRemoveBorderConnComps(PIX *pixs, + l_int32 connectivity) +{ +PIX *pixd; + + PROCNAME("pixRemoveBorderConnComps"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + /* Fill from a 1 pixel wide seed at the border into all components + * in pixs (the filling mask) that are touching the border */ + pixd = pixExtractBorderConnComps(pixs, connectivity); + + /* Save in pixd only those components in pixs not touching the border */ + pixXor(pixd, pixd, pixs); + return pixd; +} + + +/*! + * \brief pixFillBgFromBorder() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity filling connectivity 4 or 8 + * \return pixd with the background c.c. touching the border + * filled to foreground, or NULL on error + * + *
+ * Notes:
+ *      (1) This fills all bg components touching the border to fg.
+ *          It is the photometric inverse of pixRemoveBorderConnComps().
+ *      (2) Invert the result to get the "holes" left after this fill.
+ *          This can be done multiple times, extracting holes within
+ *          holes after each pair of fillings.  Specifically, this code
+ *          peels away n successive embeddings of components:
+ * \code
+ *              pix1 = 
+ *              for (i = 0; i < 2 * n; i++) {
+ *                   pix2 = pixFillBgFromBorder(pix1, 8);
+ *                   pixInvert(pix2, pix2);
+ *                   pixDestroy(&pix1);
+ *                   pix1 = pix2;
+ *              }
+ * \endcode
+ * 
+ */ +PIX * +pixFillBgFromBorder(PIX *pixs, + l_int32 connectivity) +{ +PIX *pixd; + + PROCNAME("pixFillBgFromBorder"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + /* Invert to turn bg touching the border to a fg component. + * Extract this by filling from a 1 pixel wide seed at the border. */ + pixInvert(pixs, pixs); + pixd = pixExtractBorderConnComps(pixs, connectivity); + pixInvert(pixs, pixs); /* restore pixs */ + + /* Bit-or the filled bg component with pixs */ + pixOr(pixd, pixd, pixs); + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Hole-filling of components to bounding rectangle * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixFillHolesToBoundingRect() + * + * \param[in] pixs 1 bpp + * \param[in] minsize min number of pixels in the hole + * \param[in] maxhfract max hole area as fraction of fg pixels in the cc + * \param[in] minfgfract min fg area as fraction of bounding rectangle + * \return pixd with some holes possibly filled and some c.c. possibly + * expanded to their bounding rects, or NULL on error + * + *
+ * Notes:
+ *      (1) This does not fill holes that are smaller in area than 'minsize'.
+ *      (2) This does not fill holes with an area larger than
+ *          'maxhfract' times the fg area of the c.c.
+ *      (3) This does not expand the fg of the c.c. to bounding rect if
+ *          the fg area is less than 'minfgfract' times the area of the
+ *          bounding rect.
+ *      (4) The decisions are made as follows:
+ *           ~ Decide if we are filling the holes; if so, when using
+ *             the fg area, include the filled holes.
+ *           ~ Decide based on the fg area if we are filling to a bounding rect.
+ *             If so, do it.
+ *             If not, fill the holes if the condition is satisfied.
+ *      (5) The choice of minsize depends on the resolution.
+ *      (6) For solidifying image mask regions on printed materials,
+ *          which tend to be rectangular, values for maxhfract
+ *          and minfgfract around 0.5 are reasonable.
+ * 
+ */ +PIX * +pixFillHolesToBoundingRect(PIX *pixs, + l_int32 minsize, + l_float32 maxhfract, + l_float32 minfgfract) +{ +l_int32 i, x, y, w, h, n, nfg, nh, ntot, area; +l_int32 *tab; +l_float32 hfract; /* measured hole fraction */ +l_float32 fgfract; /* measured fg fraction */ +BOXA *boxa; +PIX *pixd, *pixfg, *pixh; +PIXA *pixa; + + PROCNAME("pixFillHolesToBoundingRect"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + + pixd = pixCopy(NULL, pixs); + boxa = pixConnComp(pixd, &pixa, 8); + n = boxaGetCount(boxa); + tab = makePixelSumTab8(); + for (i = 0; i < n; i++) { + boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); + area = w * h; + if (area < minsize) + continue; + pixfg = pixaGetPix(pixa, i, L_COPY); + pixh = pixHolesByFilling(pixfg, 4); /* holes only */ + pixCountPixels(pixfg, &nfg, tab); + pixCountPixels(pixh, &nh, tab); + hfract = (l_float32)nh / (l_float32)nfg; + ntot = nfg; + if (hfract <= maxhfract) /* we will fill the holes (at least) */ + ntot = nfg + nh; + fgfract = (l_float32)ntot / (l_float32)area; + if (fgfract >= minfgfract) { /* fill to bounding rect */ + pixSetAll(pixfg); + pixRasterop(pixd, x, y, w, h, PIX_SRC, pixfg, 0, 0); + } else if (hfract <= maxhfract) { /* fill just the holes */ + pixRasterop(pixd, x, y, w, h, PIX_DST | PIX_SRC , pixh, 0, 0); + } + pixDestroy(&pixfg); + pixDestroy(&pixh); + } + boxaDestroy(&boxa); + pixaDestroy(&pixa); + LEPT_FREE(tab); + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Vincent's hybrid Grayscale Seedfill method * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSeedfillGray() + * + * \param[in] pixs 8 bpp seed; filled in place + * \param[in] pixm 8 bpp filling mask + * \param[in] connectivity 4 or 8 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place filling operation on the seed, pixs,
+ *          where the clipping mask is always above or at the level
+ *          of the seed as it is filled.
+ *      (2) For details of the operation, see the description in
+ *          seedfillGrayLow() and the code there.
+ *      (3) As an example of use, see the description in pixHDome().
+ *          There, the seed is an image where each pixel is a fixed
+ *          amount smaller than the corresponding mask pixel.
+ *      (4) Reference paper :
+ *            L. Vincent, Morphological grayscale reconstruction in image
+ *            analysis: applications and efficient algorithms, IEEE Transactions
+ *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
+ * 
+ */ +l_ok +pixSeedfillGray(PIX *pixs, + PIX *pixm, + l_int32 connectivity) +{ +l_int32 h, w, wpls, wplm; +l_uint32 *datas, *datam; + + PROCNAME("pixSeedfillGray"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 8) + return ERROR_INT("pixm not defined or not 8 bpp", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not in {4,8}", procName, 1); + + /* Make sure the sizes of seed and mask images are the same */ + if (pixSizesEqual(pixs, pixm) == 0) + return ERROR_INT("pixs and pixm sizes differ", procName, 1); + + datas = pixGetData(pixs); + datam = pixGetData(pixm); + wpls = pixGetWpl(pixs); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixs, &w, &h, NULL); + seedfillGrayLow(datas, w, h, wpls, datam, wplm, connectivity); + + return 0; +} + + +/*! + * \brief pixSeedfillGrayInv() + * + * \param[in] pixs 8 bpp seed; filled in place + * \param[in] pixm 8 bpp filling mask + * \param[in] connectivity 4 or 8 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place filling operation on the seed, pixs,
+ *          where the clipping mask is always below or at the level
+ *          of the seed as it is filled.  Think of filling up a basin
+ *          to a particular level, given by the maximum seed value
+ *          in the basin.  Outside the filled region, the mask
+ *          is above the filling level.
+ *      (2) Contrast this with pixSeedfillGray(), where the clipping mask
+ *          is always above or at the level of the fill.  An example
+ *          of its use is the hdome fill, where the seed is an image
+ *          where each pixel is a fixed amount smaller than the
+ *          corresponding mask pixel.
+ *      (3) The basin fill, pixSeedfillGrayBasin(), is a special case
+ *          where the seed pixel values are generated from the mask,
+ *          and where the implementation uses pixSeedfillGray() by
+ *          inverting both the seed and mask.
+ * 
+ */ +l_ok +pixSeedfillGrayInv(PIX *pixs, + PIX *pixm, + l_int32 connectivity) +{ +l_int32 h, w, wpls, wplm; +l_uint32 *datas, *datam; + + PROCNAME("pixSeedfillGrayInv"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 8) + return ERROR_INT("pixm not defined or not 8 bpp", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not in {4,8}", procName, 1); + + /* Make sure the sizes of seed and mask images are the same */ + if (pixSizesEqual(pixs, pixm) == 0) + return ERROR_INT("pixs and pixm sizes differ", procName, 1); + + datas = pixGetData(pixs); + datam = pixGetData(pixm); + wpls = pixGetWpl(pixs); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixs, &w, &h, NULL); + seedfillGrayInvLow(datas, w, h, wpls, datam, wplm, connectivity); + + return 0; +} + + +/*! + * \brief seedfillGrayLow() + * + * Notes: + * (1) The pixels are numbered as follows: + * 1 2 3 + * 4 x 5 + * 6 7 8 + * This low-level filling operation consists of two scans, + * raster and anti-raster, covering the entire seed image. + * This is followed by a breadth-first propagation operation to + * complete the fill. + * During the anti-raster scan, every pixel p whose current value + * could still be propagated after the anti-raster scan is put into + * the FIFO queue. + * The propagation step is a breadth-first fill to completion. + * Unlike the simple grayscale seedfill pixSeedfillGraySimple(), + * where at least two full raster/anti-raster iterations are required + * for completion and verification, the hybrid method uses only a + * single raster/anti-raster set of scans. + * (2) The filling action can be visualized from the following example. + * Suppose the mask, which clips the fill, is a sombrero-shaped + * surface, where the highest point is 200 and the low pixels + * around the rim are 30. Beyond the rim, the mask goes up a bit. + * Suppose the seed, which is filled, consists of a single point + * of height 150, located below the max of the mask, with + * the rest 0. Then in the raster scan, nothing happens until + * the high seed point is encountered, and then this value is + * propagated right and down, until it hits the side of the + * sombrero. The seed can never exceed the mask, so it fills + * to the rim, going lower along the mask surface. When it + * passes the rim, the seed continues to fill at the rim + * height to the edge of the seed image. Then on the + * anti-raster scan, the seed fills flat inside the + * sombrero to the upper and left, and then out from the + * rim as before. The final result has a seed that is + * flat outside the rim, and inside it fills the sombrero + * but only up to 150. If the rim height varies, the + * filled seed outside the rim will be at the highest + * point on the rim, which is a saddle point on the rim. + * (3) Reference paper : + * L. Vincent, Morphological grayscale reconstruction in image + * analysis: applications and efficient algorithms, IEEE Transactions + * on Image Processing, vol. 2, no. 2, pp. 176-201, 1993. + */ +static void +seedfillGrayLow(l_uint32 *datas, + l_int32 w, + l_int32 h, + l_int32 wpls, + l_uint32 *datam, + l_int32 wplm, + l_int32 connectivity) +{ +l_uint8 val1, val2, val3, val4, val5, val6, val7, val8; +l_uint8 val, maxval, maskval, boolval; +l_int32 i, j, imax, jmax, queue_size; +l_uint32 *lines, *linem; +L_PIXEL *pixel; +L_QUEUE *lq_pixel; + + PROCNAME("seedfillGrayLow"); + + if (connectivity != 4 && connectivity != 8) { + L_ERROR("connectivity must be 4 or 8\n", procName); + return; + } + + imax = h - 1; + jmax = w - 1; + + /* In the worst case, most of the pixels could be pushed + * onto the FIFO queue during anti-raster scan. However this + * will rarely happen, and we initialize the queue ptr size to + * the image perimeter. */ + lq_pixel = lqueueCreate(2 * (w + h)); + + switch (connectivity) + { + case 4: + /* UL --> LR scan (Raster Order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * J(p) <- (max{J(p) union J(p) neighbors in raster order}) + * intersection I(p) */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i > 0) + maxval = GET_DATA_BYTE(lines - wpls, j); + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + + /* LR --> UL scan (anti-raster order) + * Let p be the currect pixel; + * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order}) + * intersection I(p) */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + boolval = FALSE; + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i < imax) + maxval = GET_DATA_BYTE(lines + wpls, j); + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + + /* + * If there exists a point (q) which belongs to J(p) + * neighbors in anti-raster order such that J(q) < J(p) + * and J(q) < I(q) then + * fifo_add(p) */ + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + if ((val7 < val) && + (val7 < GET_DATA_BYTE(linem + wplm, j))) { + boolval = TRUE; + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + if (!boolval && (val5 < val) && + (val5 < GET_DATA_BYTE(linem, j + 1))) { + boolval = TRUE; + } + } + if (boolval) { + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + } + } + + /* Propagation step: + * while fifo_empty = false + * p <- fifo_first() + * for every pixel (q) belong to neighbors of (p) + * if J(q) < J(p) and I(q) != J(q) + * J(q) <- min(J(p), I(q)); + * fifo_add(q); + * end + * end + * end */ + queue_size = lqueueGetCount(lq_pixel); + while (queue_size) { + pixel = (L_PIXEL *)lqueueRemove(lq_pixel); + i = pixel->x; + j = pixel->y; + LEPT_FREE(pixel); + lines = datas + i * wpls; + linem = datam + i * wplm; + + if ((val = GET_DATA_BYTE(lines, j)) > 0) { + if (i > 0) { + val2 = GET_DATA_BYTE(lines - wpls, j); + maskval = GET_DATA_BYTE(linem - wplm, j); + if (val > val2 && val2 != maskval) { + SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maskval = GET_DATA_BYTE(linem, j - 1); + if (val > val4 && val4 != maskval) { + SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + maskval = GET_DATA_BYTE(linem + wplm, j); + if (val > val7 && val7 != maskval) { + SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maskval = GET_DATA_BYTE(linem, j + 1); + if (val > val5 && val5 != maskval) { + SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + } + + queue_size = lqueueGetCount(lq_pixel); + } + + break; + + case 8: + /* UL --> LR scan (Raster Order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * J(p) <- (max{J(p) union J(p) neighbors in raster order}) + * intersection I(p) */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i > 0) { + if (j > 0) + maxval = GET_DATA_BYTE(lines - wpls, j - 1); + if (j < jmax) { + val3 = GET_DATA_BYTE(lines - wpls, j + 1); + maxval = L_MAX(maxval, val3); + } + val2 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val2); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + + /* LR --> UL scan (anti-raster order) + * Let p be the currect pixel; + * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order}) + * intersection I(p) */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + boolval = FALSE; + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i < imax) { + if (j > 0) { + maxval = GET_DATA_BYTE(lines + wpls, j - 1); + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maxval = L_MAX(maxval, val8); + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + + /* If there exists a point (q) which belongs to J(p) + * neighbors in anti-raster order such that J(q) < J(p) + * and J(q) < I(q) then + * fifo_add(p) */ + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + if ((val6 < val) && + (val6 < GET_DATA_BYTE(linem + wplm, j - 1))) { + boolval = TRUE; + } + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + if (!boolval && (val8 < val) && + (val8 < GET_DATA_BYTE(linem + wplm, j + 1))) { + boolval = TRUE; + } + } + val7 = GET_DATA_BYTE(lines + wpls, j); + if (!boolval && (val7 < val) && + (val7 < GET_DATA_BYTE(linem + wplm, j))) { + boolval = TRUE; + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + if (!boolval && (val5 < val) && + (val5 < GET_DATA_BYTE(linem, j + 1))) { + boolval = TRUE; + } + } + if (boolval) { + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + } + } + + /* Propagation step: + * while fifo_empty = false + * p <- fifo_first() + * for every pixel (q) belong to neighbors of (p) + * if J(q) < J(p) and I(q) != J(q) + * J(q) <- min(J(p), I(q)); + * fifo_add(q); + * end + * end + * end */ + queue_size = lqueueGetCount(lq_pixel); + while (queue_size) { + pixel = (L_PIXEL *)lqueueRemove(lq_pixel); + i = pixel->x; + j = pixel->y; + LEPT_FREE(pixel); + lines = datas + i * wpls; + linem = datam + i * wplm; + + if ((val = GET_DATA_BYTE(lines, j)) > 0) { + if (i > 0) { + if (j > 0) { + val1 = GET_DATA_BYTE(lines - wpls, j - 1); + maskval = GET_DATA_BYTE(linem - wplm, j - 1); + if (val > val1 && val1 != maskval) { + SET_DATA_BYTE(lines - wpls, j - 1, + L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val3 = GET_DATA_BYTE(lines - wpls, j + 1); + maskval = GET_DATA_BYTE(linem - wplm, j + 1); + if (val > val3 && val3 != maskval) { + SET_DATA_BYTE(lines - wpls, j + 1, + L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + val2 = GET_DATA_BYTE(lines - wpls, j); + maskval = GET_DATA_BYTE(linem - wplm, j); + if (val > val2 && val2 != maskval) { + SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maskval = GET_DATA_BYTE(linem, j - 1); + if (val > val4 && val4 != maskval) { + SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + maskval = GET_DATA_BYTE(linem + wplm, j - 1); + if (val > val6 && val6 != maskval) { + SET_DATA_BYTE(lines + wpls, j - 1, + L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maskval = GET_DATA_BYTE(linem + wplm, j + 1); + if (val > val8 && val8 != maskval) { + SET_DATA_BYTE(lines + wpls, j + 1, + L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maskval = GET_DATA_BYTE(linem + wplm, j); + if (val > val7 && val7 != maskval) { + SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maskval = GET_DATA_BYTE(linem, j + 1); + if (val > val5 && val5 != maskval) { + SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval)); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + } + + queue_size = lqueueGetCount(lq_pixel); + } + break; + + default: + L_ERROR("shouldn't get here!\n", procName); + break; + } + + lqueueDestroy(&lq_pixel, TRUE); +} + + +/*! + * \brief seedfillGrayInvLow() + * + * Notes: + * (1) The pixels are numbered as follows: + * 1 2 3 + * 4 x 5 + * 6 7 8 + * This low-level filling operation consists of two scans, + * raster and anti-raster, covering the entire seed image. + * During the anti-raster scan, every pixel p such that its + * current value could still be propagated during the next + * raster scanning is put into the FIFO-queue. + * Next step is the propagation step where where we update + * and propagate the values using FIFO structure created in + * anti-raster scan. + * (2) The "Inv" signifies the fact that in this case, filling + * of the seed only takes place when the seed value is + * greater than the mask value. The mask will act to stop + * the fill when it is higher than the seed level. (This is + * in contrast to conventional grayscale filling where the + * seed always fills below the mask.) + * (3) An example of use is a basin, described by the mask (pixm), + * where within the basin, the seed pix (pixs) gets filled to the + * height of the highest seed pixel that is above its + * corresponding max pixel. Filling occurs while the + * propagating seed pixels in pixs are larger than the + * corresponding mask values in pixm. + * (4) Reference paper : + * L. Vincent, Morphological grayscale reconstruction in image + * analysis: applications and efficient algorithms, IEEE Transactions + * on Image Processing, vol. 2, no. 2, pp. 176-201, 1993. + */ +static void +seedfillGrayInvLow(l_uint32 *datas, + l_int32 w, + l_int32 h, + l_int32 wpls, + l_uint32 *datam, + l_int32 wplm, + l_int32 connectivity) +{ +l_uint8 val1, val2, val3, val4, val5, val6, val7, val8; +l_uint8 val, maxval, maskval, boolval; +l_int32 i, j, imax, jmax, queue_size; +l_uint32 *lines, *linem; +L_PIXEL *pixel; +L_QUEUE *lq_pixel; + + PROCNAME("seedfillGrayInvLow"); + + if (connectivity != 4 && connectivity != 8) { + L_ERROR("connectivity must be 4 or 8\n", procName); + return; + } + + imax = h - 1; + jmax = w - 1; + + /* In the worst case, most of the pixels could be pushed + * onto the FIFO queue during anti-raster scan. However this + * will rarely happen, and we initialize the queue ptr size to + * the image perimeter. */ + lq_pixel = lqueueCreate(2 * (w + h)); + + switch (connectivity) + { + case 4: + /* UL --> LR scan (Raster Order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * tmp <- max{J(p) union J(p) neighbors in raster order} + * if (tmp > I(p)) + * J(p) <- tmp + * end */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i > 0) { + val2 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val2); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + + /* LR --> UL scan (anti-raster order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * tmp <- max{J(p) union J(p) neighbors in anti-raster order} + * if (tmp > I(p)) + * J(p) <- tmp + * end */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + boolval = FALSE; + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + val = maxval = GET_DATA_BYTE(lines, j); + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + val = GET_DATA_BYTE(lines, j); + + /* + * If there exists a point (q) which belongs to J(p) + * neighbors in anti-raster order such that J(q) < J(p) + * and J(p) > I(q) then + * fifo_add(p) */ + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + if ((val7 < val) && + (val > GET_DATA_BYTE(linem + wplm, j))) { + boolval = TRUE; + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + if (!boolval && (val5 < val) && + (val > GET_DATA_BYTE(linem, j + 1))) { + boolval = TRUE; + } + } + if (boolval) { + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + } + } + + /* Propagation step: + * while fifo_empty = false + * p <- fifo_first() + * for every pixel (q) belong to neighbors of (p) + * if J(q) < J(p) and J(p) > I(q) + * J(q) <- min(J(p), I(q)); + * fifo_add(q); + * end + * end + * end */ + queue_size = lqueueGetCount(lq_pixel); + while (queue_size) { + pixel = (L_PIXEL *)lqueueRemove(lq_pixel); + i = pixel->x; + j = pixel->y; + LEPT_FREE(pixel); + lines = datas + i * wpls; + linem = datam + i * wplm; + + if ((val = GET_DATA_BYTE(lines, j)) > 0) { + if (i > 0) { + val2 = GET_DATA_BYTE(lines - wpls, j); + maskval = GET_DATA_BYTE(linem - wplm, j); + if (val > val2 && val > maskval) { + SET_DATA_BYTE(lines - wpls, j, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maskval = GET_DATA_BYTE(linem, j - 1); + if (val > val4 && val > maskval) { + SET_DATA_BYTE(lines, j - 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + maskval = GET_DATA_BYTE(linem + wplm, j); + if (val > val7 && val > maskval) { + SET_DATA_BYTE(lines + wpls, j, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maskval = GET_DATA_BYTE(linem, j + 1); + if (val > val5 && val > maskval) { + SET_DATA_BYTE(lines, j + 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + } + + queue_size = lqueueGetCount(lq_pixel); + } + + break; + + case 8: + /* UL --> LR scan (Raster Order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * tmp <- max{J(p) union J(p) neighbors in raster order} + * if (tmp > I(p)) + * J(p) <- tmp + * end */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i > 0) { + if (j > 0) { + val1 = GET_DATA_BYTE(lines - wpls, j - 1); + maxval = L_MAX(maxval, val1); + } + if (j < jmax) { + val3 = GET_DATA_BYTE(lines - wpls, j + 1); + maxval = L_MAX(maxval, val3); + } + val2 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val2); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + + /* LR --> UL scan (anti-raster order) + * If I : mask image + * J : marker image + * Let p be the currect pixel; + * tmp <- max{J(p) union J(p) neighbors in anti-raster order} + * if (tmp > I(p)) + * J(p) <- tmp + * end */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + boolval = FALSE; + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + maxval = L_MAX(maxval, val6); + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maxval = L_MAX(maxval, val8); + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + val = GET_DATA_BYTE(lines, j); + + /* + * If there exists a point (q) which belongs to J(p) + * neighbors in anti-raster order such that J(q) < J(p) + * and J(p) > I(q) then + * fifo_add(p) */ + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + if ((val6 < val) && + (val > GET_DATA_BYTE(linem + wplm, j - 1))) { + boolval = TRUE; + } + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + if (!boolval && (val8 < val) && + (val > GET_DATA_BYTE(linem + wplm, j + 1))) { + boolval = TRUE; + } + } + val7 = GET_DATA_BYTE(lines + wpls, j); + if (!boolval && (val7 < val) && + (val > GET_DATA_BYTE(linem + wplm, j))) { + boolval = TRUE; + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + if (!boolval && (val5 < val) && + (val > GET_DATA_BYTE(linem, j + 1))) { + boolval = TRUE; + } + } + if (boolval) { + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + } + } + + /* Propagation step: + * while fifo_empty = false + * p <- fifo_first() + * for every pixel (q) belong to neighbors of (p) + * if J(q) < J(p) and J(p) > I(q) + * J(q) <- min(J(p), I(q)); + * fifo_add(q); + * end + * end + * end */ + queue_size = lqueueGetCount(lq_pixel); + while (queue_size) { + pixel = (L_PIXEL *)lqueueRemove(lq_pixel); + i = pixel->x; + j = pixel->y; + LEPT_FREE(pixel); + lines = datas + i * wpls; + linem = datam + i * wplm; + + if ((val = GET_DATA_BYTE(lines, j)) > 0) { + if (i > 0) { + if (j > 0) { + val1 = GET_DATA_BYTE(lines - wpls, j - 1); + maskval = GET_DATA_BYTE(linem - wplm, j - 1); + if (val > val1 && val > maskval) { + SET_DATA_BYTE(lines - wpls, j - 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val3 = GET_DATA_BYTE(lines - wpls, j + 1); + maskval = GET_DATA_BYTE(linem - wplm, j + 1); + if (val > val3 && val > maskval) { + SET_DATA_BYTE(lines - wpls, j + 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + val2 = GET_DATA_BYTE(lines - wpls, j); + maskval = GET_DATA_BYTE(linem - wplm, j); + if (val > val2 && val > maskval) { + SET_DATA_BYTE(lines - wpls, j, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i - 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maskval = GET_DATA_BYTE(linem, j - 1); + if (val > val4 && val > maskval) { + SET_DATA_BYTE(lines, j - 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + maskval = GET_DATA_BYTE(linem + wplm, j - 1); + if (val > val6 && val > maskval) { + SET_DATA_BYTE(lines + wpls, j - 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j - 1; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maskval = GET_DATA_BYTE(linem + wplm, j + 1); + if (val > val8 && val > maskval) { + SET_DATA_BYTE(lines + wpls, j + 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maskval = GET_DATA_BYTE(linem + wplm, j); + if (val > val7 && val > maskval) { + SET_DATA_BYTE(lines + wpls, j, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i + 1; + pixel->y = j; + lqueueAdd(lq_pixel, pixel); + } + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maskval = GET_DATA_BYTE(linem, j + 1); + if (val > val5 && val > maskval) { + SET_DATA_BYTE(lines, j + 1, val); + pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL)); + pixel->x = i; + pixel->y = j + 1; + lqueueAdd(lq_pixel, pixel); + } + } + } + + queue_size = lqueueGetCount(lq_pixel); + } + break; + + default: + L_ERROR("shouldn't get here!\n", procName); + break; + } + + lqueueDestroy(&lq_pixel, TRUE); +} + + +/*-----------------------------------------------------------------------* + * Vincent's Iterative Grayscale Seedfill method * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSeedfillGraySimple() + * + * \param[in] pixs 8 bpp seed; filled in place + * \param[in] pixm 8 bpp filling mask + * \param[in] connectivity 4 or 8 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place filling operation on the seed, pixs,
+ *          where the clipping mask is always above or at the level
+ *          of the seed as it is filled.
+ *      (2) For details of the operation, see the description in
+ *          seedfillGrayLowSimple() and the code there.
+ *      (3) As an example of use, see the description in pixHDome().
+ *          There, the seed is an image where each pixel is a fixed
+ *          amount smaller than the corresponding mask pixel.
+ *      (4) Reference paper :
+ *            L. Vincent, Morphological grayscale reconstruction in image
+ *            analysis: applications and efficient algorithms, IEEE Transactions
+ *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
+ * 
+ */ +l_ok +pixSeedfillGraySimple(PIX *pixs, + PIX *pixm, + l_int32 connectivity) +{ +l_int32 i, h, w, wpls, wplm, boolval; +l_uint32 *datas, *datam; +PIX *pixt; + + PROCNAME("pixSeedfillGraySimple"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 8) + return ERROR_INT("pixm not defined or not 8 bpp", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not in {4,8}", procName, 1); + + /* Make sure the sizes of seed and mask images are the same */ + if (pixSizesEqual(pixs, pixm) == 0) + return ERROR_INT("pixs and pixm sizes differ", procName, 1); + + /* This is used to test for completion */ + if ((pixt = pixCreateTemplate(pixs)) == NULL) + return ERROR_INT("pixt not made", procName, 1); + + datas = pixGetData(pixs); + datam = pixGetData(pixm); + wpls = pixGetWpl(pixs); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixs, &w, &h, NULL); + for (i = 0; i < MaxIters; i++) { + pixCopy(pixt, pixs); + seedfillGrayLowSimple(datas, w, h, wpls, datam, wplm, connectivity); + pixEqual(pixs, pixt, &boolval); + if (boolval == 1) { +#if DEBUG_PRINT_ITERS + L_INFO("Gray seed fill converged: %d iters\n", procName, i + 1); +#endif /* DEBUG_PRINT_ITERS */ + break; + } + } + + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief pixSeedfillGrayInvSimple() + * + * \param[in] pixs 8 bpp seed; filled in place + * \param[in] pixm 8 bpp filling mask + * \param[in] connectivity 4 or 8 + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place filling operation on the seed, pixs,
+ *          where the clipping mask is always below or at the level
+ *          of the seed as it is filled.  Think of filling up a basin
+ *          to a particular level, given by the maximum seed value
+ *          in the basin.  Outside the filled region, the mask
+ *          is above the filling level.
+ *      (2) Contrast this with pixSeedfillGraySimple(), where the clipping mask
+ *          is always above or at the level of the fill.  An example
+ *          of its use is the hdome fill, where the seed is an image
+ *          where each pixel is a fixed amount smaller than the
+ *          corresponding mask pixel.
+ * 
+ */ +l_ok +pixSeedfillGrayInvSimple(PIX *pixs, + PIX *pixm, + l_int32 connectivity) +{ +l_int32 i, h, w, wpls, wplm, boolval; +l_uint32 *datas, *datam; +PIX *pixt; + + PROCNAME("pixSeedfillGrayInvSimple"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 8) + return ERROR_INT("pixm not defined or not 8 bpp", procName, 1); + if (connectivity != 4 && connectivity != 8) + return ERROR_INT("connectivity not in {4,8}", procName, 1); + + /* Make sure the sizes of seed and mask images are the same */ + if (pixSizesEqual(pixs, pixm) == 0) + return ERROR_INT("pixs and pixm sizes differ", procName, 1); + + /* This is used to test for completion */ + if ((pixt = pixCreateTemplate(pixs)) == NULL) + return ERROR_INT("pixt not made", procName, 1); + + datas = pixGetData(pixs); + datam = pixGetData(pixm); + wpls = pixGetWpl(pixs); + wplm = pixGetWpl(pixm); + pixGetDimensions(pixs, &w, &h, NULL); + for (i = 0; i < MaxIters; i++) { + pixCopy(pixt, pixs); + seedfillGrayInvLowSimple(datas, w, h, wpls, datam, wplm, connectivity); + pixEqual(pixs, pixt, &boolval); + if (boolval == 1) { +#if DEBUG_PRINT_ITERS + L_INFO("Gray seed fill converged: %d iters\n", procName, i + 1); +#endif /* DEBUG_PRINT_ITERS */ + break; + } + } + + pixDestroy(&pixt); + return 0; +} + + +/*! + * \brief seedfillGrayLowSimple() + * + * Notes: + * (1) The pixels are numbered as follows: + * 1 2 3 + * 4 x 5 + * 6 7 8 + * This low-level filling operation consists of two scans, + * raster and anti-raster, covering the entire seed image. + * The caller typically iterates until the filling is + * complete. + * (2) The filling action can be visualized from the following example. + * Suppose the mask, which clips the fill, is a sombrero-shaped + * surface, where the highest point is 200 and the low pixels + * around the rim are 30. Beyond the rim, the mask goes up a bit. + * Suppose the seed, which is filled, consists of a single point + * of height 150, located below the max of the mask, with + * the rest 0. Then in the raster scan, nothing happens until + * the high seed point is encountered, and then this value is + * propagated right and down, until it hits the side of the + * sombrero. The seed can never exceed the mask, so it fills + * to the rim, going lower along the mask surface. When it + * passes the rim, the seed continues to fill at the rim + * height to the edge of the seed image. Then on the + * anti-raster scan, the seed fills flat inside the + * sombrero to the upper and left, and then out from the + * rim as before. The final result has a seed that is + * flat outside the rim, and inside it fills the sombrero + * but only up to 150. If the rim height varies, the + * filled seed outside the rim will be at the highest + * point on the rim, which is a saddle point on the rim. + */ +static void +seedfillGrayLowSimple(l_uint32 *datas, + l_int32 w, + l_int32 h, + l_int32 wpls, + l_uint32 *datam, + l_int32 wplm, + l_int32 connectivity) +{ +l_uint8 val2, val3, val4, val5, val7, val8; +l_uint8 val, maxval, maskval; +l_int32 i, j, imax, jmax; +l_uint32 *lines, *linem; + + PROCNAME("seedfillGrayLowSimple"); + + imax = h - 1; + jmax = w - 1; + + switch (connectivity) + { + case 4: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i > 0) + maxval = GET_DATA_BYTE(lines - wpls, j); + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + + /* LR --> UL scan */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i < imax) + maxval = GET_DATA_BYTE(lines + wpls, j); + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + break; + + case 8: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i > 0) { + if (j > 0) + maxval = GET_DATA_BYTE(lines - wpls, j - 1); + if (j < jmax) { + val2 = GET_DATA_BYTE(lines - wpls, j + 1); + maxval = L_MAX(maxval, val2); + } + val3 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val3); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + + /* LR --> UL scan */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + if ((maskval = GET_DATA_BYTE(linem, j)) > 0) { + maxval = 0; + if (i < imax) { + if (j > 0) + maxval = GET_DATA_BYTE(lines + wpls, j - 1); + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maxval = L_MAX(maxval, val8); + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + val = GET_DATA_BYTE(lines, j); + maxval = L_MAX(maxval, val); + val = L_MIN(maxval, maskval); + SET_DATA_BYTE(lines, j, val); + } + } + } + break; + + default: + L_ERROR("connectivity must be 4 or 8\n", procName); + } +} + + +/*! + * \brief seedfillGrayInvLowSimple() + * + * Notes: + * (1) The pixels are numbered as follows: + * 1 2 3 + * 4 x 5 + * 6 7 8 + * This low-level filling operation consists of two scans, + * raster and anti-raster, covering the entire seed image. + * The caller typically iterates until the filling is + * complete. + * (2) The "Inv" signifies the fact that in this case, filling + * of the seed only takes place when the seed value is + * greater than the mask value. The mask will act to stop + * the fill when it is higher than the seed level. (This is + * in contrast to conventional grayscale filling where the + * seed always fills below the mask.) + * (3) An example of use is a basin, described by the mask (pixm), + * where within the basin, the seed pix (pixs) gets filled to the + * height of the highest seed pixel that is above its + * corresponding max pixel. Filling occurs while the + * propagating seed pixels in pixs are larger than the + * corresponding mask values in pixm. + */ +static void +seedfillGrayInvLowSimple(l_uint32 *datas, + l_int32 w, + l_int32 h, + l_int32 wpls, + l_uint32 *datam, + l_int32 wplm, + l_int32 connectivity) +{ +l_uint8 val1, val2, val3, val4, val5, val6, val7, val8; +l_uint8 maxval, maskval; +l_int32 i, j, imax, jmax; +l_uint32 *lines, *linem; + + PROCNAME("seedfillGrayInvLowSimple"); + + imax = h - 1; + jmax = w - 1; + + switch (connectivity) + { + case 4: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i > 0) { + val2 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val2); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + + /* LR --> UL scan */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i < imax) { + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + break; + + case 8: + /* UL --> LR scan */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = 0; j < w; j++) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i > 0) { + if (j > 0) { + val1 = GET_DATA_BYTE(lines - wpls, j - 1); + maxval = L_MAX(maxval, val1); + } + if (j < jmax) { + val2 = GET_DATA_BYTE(lines - wpls, j + 1); + maxval = L_MAX(maxval, val2); + } + val3 = GET_DATA_BYTE(lines - wpls, j); + maxval = L_MAX(maxval, val3); + } + if (j > 0) { + val4 = GET_DATA_BYTE(lines, j - 1); + maxval = L_MAX(maxval, val4); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + + /* LR --> UL scan */ + for (i = imax; i >= 0; i--) { + lines = datas + i * wpls; + linem = datam + i * wplm; + for (j = jmax; j >= 0; j--) { + if ((maskval = GET_DATA_BYTE(linem, j)) < 255) { + maxval = GET_DATA_BYTE(lines, j); + if (i < imax) { + if (j > 0) { + val6 = GET_DATA_BYTE(lines + wpls, j - 1); + maxval = L_MAX(maxval, val6); + } + if (j < jmax) { + val8 = GET_DATA_BYTE(lines + wpls, j + 1); + maxval = L_MAX(maxval, val8); + } + val7 = GET_DATA_BYTE(lines + wpls, j); + maxval = L_MAX(maxval, val7); + } + if (j < jmax) { + val5 = GET_DATA_BYTE(lines, j + 1); + maxval = L_MAX(maxval, val5); + } + if (maxval > maskval) + SET_DATA_BYTE(lines, j, maxval); + } + } + } + break; + + default: + L_ERROR("connectivity must be 4 or 8\n", procName); + } +} + + +/*-----------------------------------------------------------------------* + * Gray seedfill variations * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSeedfillGrayBasin() + * + * \param[in] pixb binary mask giving seed locations + * \param[in] pixm 8 bpp basin-type filling mask + * \param[in] delta amount of seed value above mask + * \param[in] connectivity 4 or 8 + * \return pixd filled seed if OK, NULL on error + * + *
+ * Notes:
+ *      (1) This fills from a seed within basins defined by a filling mask.
+ *          The seed value(s) are greater than the corresponding
+ *          filling mask value, and the result has the bottoms of
+ *          the basins raised by the initial seed value.
+ *      (2) The seed has value 255 except where pixb has fg (1), which
+ *          are the seed 'locations'.  At the seed locations, the seed
+ *          value is the corresponding value of the mask pixel in pixm
+ *          plus %delta.  If %delta == 0, we return a copy of pixm.
+ *      (3) The actual filling is done using the standard grayscale filling
+ *          operation on the inverse of the mask and using the inverse
+ *          of the seed image.  After filling, we return the inverse of
+ *          the filled seed.
+ *      (4) As an example of use: pixm can describe a grayscale image
+ *          of text, where the (dark) text pixels are basins of
+ *          low values; pixb can identify the local minima in pixm (say, at
+ *          the bottom of the basins); and delta is the amount that we wish
+ *          to raise (lighten) the basins.  We construct the seed
+ *          (a.k.a marker) image from pixb, pixm and %delta.
+ * 
+ */ +PIX * +pixSeedfillGrayBasin(PIX *pixb, + PIX *pixm, + l_int32 delta, + l_int32 connectivity) +{ +PIX *pixbi, *pixmi, *pixsd; + + PROCNAME("pixSeedfillGrayBasin"); + + if (!pixb || pixGetDepth(pixb) != 1) + return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL); + if (!pixm || pixGetDepth(pixm) != 8) + return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL); + + if (delta <= 0) { + L_WARNING("delta <= 0; returning a copy of pixm\n", procName); + return pixCopy(NULL, pixm); + } + + /* Add delta to every pixel in pixm */ + pixsd = pixCopy(NULL, pixm); + pixAddConstantGray(pixsd, delta); + + /* Prepare the seed. Write 255 in all pixels of + * ([pixm] + delta) where pixb is 0. */ + pixbi = pixInvert(NULL, pixb); + pixSetMasked(pixsd, pixbi, 255); + + /* Fill the inverse seed, using the inverse clipping mask */ + pixmi = pixInvert(NULL, pixm); + pixInvert(pixsd, pixsd); + pixSeedfillGray(pixsd, pixmi, connectivity); + + /* Re-invert the filled seed */ + pixInvert(pixsd, pixsd); + + pixDestroy(&pixbi); + pixDestroy(&pixmi); + return pixsd; +} + + +/*-----------------------------------------------------------------------* + * Vincent's Distance Function method * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixDistanceFunction() + * + * \param[in] pixs 1 bpp + * \param[in] connectivity 4 or 8 + * \param[in] outdepth 8 or 16 bits for pixd + * \param[in] boundcond L_BOUNDARY_BG, L_BOUNDARY_FG + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This computes the distance of each pixel from the nearest
+ *          background pixel.  All bg pixels therefore have a distance of 0,
+ *          and the fg pixel distances increase linearly from 1 at the
+ *          boundary.  It can also be used to compute the distance of
+ *          each pixel from the nearest fg pixel, by inverting the input
+ *          image before calling this function.  Then all fg pixels have
+ *          a distance 0 and the bg pixel distances increase linearly
+ *          from 1 at the boundary.
+ *      (2) The algorithm, described in Leptonica on the page on seed
+ *          filling and connected components, is due to Luc Vincent.
+ *          In brief, we generate an 8 or 16 bpp image, initialized
+ *          with the fg pixels of the input pix set to 1 and the
+ *          1-boundary pixels (i.e., the boundary pixels of width 1 on
+ *          the four sides set as either:
+ *            * L_BOUNDARY_BG: 0
+ *            * L_BOUNDARY_FG:  max
+ *          where max = 0xff for 8 bpp and 0xffff for 16 bpp.
+ *          Then do raster/anti-raster sweeps over all pixels interior
+ *          to the 1-boundary, where the value of each new pixel is
+ *          taken to be 1 more than the minimum of the previously-seen
+ *          connected pixels (using either 4 or 8 connectivity).
+ *          Finally, set the 1-boundary pixels using the mirrored method;
+ *          this removes the max values there.
+ *      (3) Using L_BOUNDARY_BG clamps the distance to 0 at the
+ *          boundary.  Using L_BOUNDARY_FG allows the distance
+ *          at the image boundary to "float".
+ *      (4) For 4-connected, one could initialize only the left and top
+ *          1-boundary pixels, and go all the way to the right
+ *          and bottom; then coming back reset left and top.  But we
+ *          instead use a method that works for both 4- and 8-connected.
+ * 
+ */ +PIX * +pixDistanceFunction(PIX *pixs, + l_int32 connectivity, + l_int32 outdepth, + l_int32 boundcond) +{ +l_int32 w, h, wpld; +l_uint32 *datad; +PIX *pixd; + + PROCNAME("pixDistanceFunction"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("!pixs or pixs not 1 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + if (outdepth != 8 && outdepth != 16) + return (PIX *)ERROR_PTR("outdepth not 8 or 16 bpp", procName, NULL); + if (boundcond != L_BOUNDARY_BG && boundcond != L_BOUNDARY_FG) + return (PIX *)ERROR_PTR("invalid boundcond", procName, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, outdepth)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + /* Initialize the fg pixels to 1 and the bg pixels to 0 */ + pixSetMasked(pixd, pixs, 1); + + if (boundcond == L_BOUNDARY_BG) { + distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity); + } else { /* L_BOUNDARY_FG: set boundary pixels to max val */ + pixRasterop(pixd, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */ + pixRasterop(pixd, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */ + pixRasterop(pixd, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */ + pixRasterop(pixd, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */ + + distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity); + + /* Set each boundary pixel equal to the pixel next to it */ + pixSetMirroredBorder(pixd, 1, 1, 1, 1); + } + + return pixd; +} + + +/*! + * \brief distanceFunctionLow() + */ +static void +distanceFunctionLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 d, + l_int32 wpld, + l_int32 connectivity) +{ +l_int32 val1, val2, val3, val4, val5, val6, val7, val8, minval, val; +l_int32 i, j, imax, jmax; +l_uint32 *lined; + + PROCNAME("distanceFunctionLow"); + + /* One raster scan followed by one anti-raster scan. + * This does not re-set the 1-boundary of pixels that + * were initialized to either 0 or maxval. */ + imax = h - 1; + jmax = w - 1; + switch (connectivity) + { + case 4: + if (d == 8) { + /* UL --> LR scan */ + for (i = 1; i < imax; i++) { + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((val = GET_DATA_BYTE(lined, j)) > 0) { + val2 = GET_DATA_BYTE(lined - wpld, j); + val4 = GET_DATA_BYTE(lined, j - 1); + minval = L_MIN(val2, val4); + minval = L_MIN(minval, 254); + SET_DATA_BYTE(lined, j, minval + 1); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((val = GET_DATA_BYTE(lined, j)) > 0) { + val7 = GET_DATA_BYTE(lined + wpld, j); + val5 = GET_DATA_BYTE(lined, j + 1); + minval = L_MIN(val5, val7); + minval = L_MIN(minval + 1, val); + SET_DATA_BYTE(lined, j, minval); + } + } + } + } else { /* d == 16 */ + /* UL --> LR scan */ + for (i = 1; i < imax; i++) { + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) { + val2 = GET_DATA_TWO_BYTES(lined - wpld, j); + val4 = GET_DATA_TWO_BYTES(lined, j - 1); + minval = L_MIN(val2, val4); + minval = L_MIN(minval, 0xfffe); + SET_DATA_TWO_BYTES(lined, j, minval + 1); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) { + val7 = GET_DATA_TWO_BYTES(lined + wpld, j); + val5 = GET_DATA_TWO_BYTES(lined, j + 1); + minval = L_MIN(val5, val7); + minval = L_MIN(minval + 1, val); + SET_DATA_TWO_BYTES(lined, j, minval); + } + } + } + } + break; + + case 8: + if (d == 8) { + /* UL --> LR scan */ + for (i = 1; i < imax; i++) { + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((val = GET_DATA_BYTE(lined, j)) > 0) { + val1 = GET_DATA_BYTE(lined - wpld, j - 1); + val2 = GET_DATA_BYTE(lined - wpld, j); + val3 = GET_DATA_BYTE(lined - wpld, j + 1); + val4 = GET_DATA_BYTE(lined, j - 1); + minval = L_MIN(val1, val2); + minval = L_MIN(minval, val3); + minval = L_MIN(minval, val4); + minval = L_MIN(minval, 254); + SET_DATA_BYTE(lined, j, minval + 1); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((val = GET_DATA_BYTE(lined, j)) > 0) { + val8 = GET_DATA_BYTE(lined + wpld, j + 1); + val7 = GET_DATA_BYTE(lined + wpld, j); + val6 = GET_DATA_BYTE(lined + wpld, j - 1); + val5 = GET_DATA_BYTE(lined, j + 1); + minval = L_MIN(val8, val7); + minval = L_MIN(minval, val6); + minval = L_MIN(minval, val5); + minval = L_MIN(minval + 1, val); + SET_DATA_BYTE(lined, j, minval); + } + } + } + } else { /* d == 16 */ + /* UL --> LR scan */ + for (i = 1; i < imax; i++) { + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) { + val1 = GET_DATA_TWO_BYTES(lined - wpld, j - 1); + val2 = GET_DATA_TWO_BYTES(lined - wpld, j); + val3 = GET_DATA_TWO_BYTES(lined - wpld, j + 1); + val4 = GET_DATA_TWO_BYTES(lined, j - 1); + minval = L_MIN(val1, val2); + minval = L_MIN(minval, val3); + minval = L_MIN(minval, val4); + minval = L_MIN(minval, 0xfffe); + SET_DATA_TWO_BYTES(lined, j, minval + 1); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) { + val8 = GET_DATA_TWO_BYTES(lined + wpld, j + 1); + val7 = GET_DATA_TWO_BYTES(lined + wpld, j); + val6 = GET_DATA_TWO_BYTES(lined + wpld, j - 1); + val5 = GET_DATA_TWO_BYTES(lined, j + 1); + minval = L_MIN(val8, val7); + minval = L_MIN(minval, val6); + minval = L_MIN(minval, val5); + minval = L_MIN(minval + 1, val); + SET_DATA_TWO_BYTES(lined, j, minval); + } + } + } + } + break; + + default: + L_ERROR("connectivity must be 4 or 8\n", procName); + break; + } +} + + +/*-----------------------------------------------------------------------* + * Seed spread (based on distance function) * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSeedspread() + * + * \param[in] pixs 8 bpp + * \param[in] connectivity 4 or 8 + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) The raster/anti-raster method for implementing this filling
+ *          operation was suggested by Ray Smith.
+ *      (2) This takes an arbitrary set of nonzero pixels in pixs, which
+ *          can be sparse, and spreads (extrapolates) the values to
+ *          fill all the pixels in pixd with the nonzero value it is
+ *          closest to in pixs.  This is similar (though not completely
+ *          equivalent) to doing a Voronoi tiling of the image, with a
+ *          tile surrounding each pixel that has a nonzero value.
+ *          All pixels within a tile are then closer to its "central"
+ *          pixel than to any others.  Then assign the value of the
+ *          "central" pixel to each pixel in the tile.
+ *      (3) This is implemented by computing a distance function in parallel
+ *          with the fill.  The distance function uses free boundary
+ *          conditions (assumed maxval outside), and it controls the
+ *          propagation of the pixels in pixd away from the nonzero
+ *          (seed) values.  This is done in 2 traversals (raster/antiraster).
+ *          In the raster direction, whenever the distance function
+ *          is nonzero, the spread pixel takes on the value of its
+ *          predecessor that has the minimum distance value.  In the
+ *          antiraster direction, whenever the distance function is nonzero
+ *          and its value is replaced by a smaller value, the spread
+ *          pixel takes the value of the predecessor with the minimum
+ *          distance value.
+ *      (4) At boundaries where a pixel is equidistant from two
+ *          nearest nonzero (seed) pixels, the decision of which value
+ *          to use is arbitrary (greedy in search for minimum distance).
+ *          This can give rise to strange-looking results, particularly
+ *          for 4-connectivity where the L1 distance is computed from
+ *          steps in N,S,E and W directions (no diagonals).
+ * 
+ */ +PIX * +pixSeedspread(PIX *pixs, + l_int32 connectivity) +{ +l_int32 w, h, wplt, wplg; +l_uint32 *datat, *datag; +PIX *pixm, *pixt, *pixg, *pixd; + + PROCNAME("pixSeedspread"); + + if (!pixs || pixGetDepth(pixs) != 8) + return (PIX *)ERROR_PTR("!pixs or pixs not 8 bpp", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + /* Add a 4 byte border to pixs. This simplifies the computation. */ + pixg = pixAddBorder(pixs, 4, 0); + pixGetDimensions(pixg, &w, &h, NULL); + + /* Initialize distance function pixt. Threshold pixs to get + * a 0 at the seed points where the pixs pixel is nonzero, and + * a 1 at all points that need to be filled. Use this as a + * mask to set a 1 in pixt at all non-seed points. Also, set all + * pixt pixels in an interior boundary of width 1 to the + * maximum value. For debugging, to view the distance function, + * use pixConvert16To8(pixt, L_LS_BYTE) on small images. */ + pixm = pixThresholdToBinary(pixg, 1); + pixt = pixCreate(w, h, 16); + pixSetMasked(pixt, pixm, 1); + pixRasterop(pixt, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */ + pixRasterop(pixt, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */ + pixRasterop(pixt, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */ + pixRasterop(pixt, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */ + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + + /* Do the interpolation and remove the border. */ + datag = pixGetData(pixg); + wplg = pixGetWpl(pixg); + seedspreadLow(datag, w, h, wplg, datat, wplt, connectivity); + pixd = pixRemoveBorder(pixg, 4); + + pixDestroy(&pixm); + pixDestroy(&pixg); + pixDestroy(&pixt); + return pixd; +} + + +/*! + * \brief seedspreadLow() + * + * See pixSeedspread() for a brief description of the algorithm here. + */ +static void +seedspreadLow(l_uint32 *datad, + l_int32 w, + l_int32 h, + l_int32 wpld, + l_uint32 *datat, + l_int32 wplt, + l_int32 connectivity) +{ +l_int32 val1t, val2t, val3t, val4t, val5t, val6t, val7t, val8t; +l_int32 i, j, imax, jmax, minval, valt, vald; +l_uint32 *linet, *lined; + + PROCNAME("seedspreadLow"); + + /* One raster scan followed by one anti-raster scan. + * pixt is initialized to have 0 on pixels where the + * input is specified in pixd, and to have 1 on all + * other pixels. We only change pixels in pixt and pixd + * that are non-zero in pixt. */ + imax = h - 1; + jmax = w - 1; + switch (connectivity) + { + case 4: + /* UL --> LR scan */ + for (i = 1; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) { + val2t = GET_DATA_TWO_BYTES(linet - wplt, j); + val4t = GET_DATA_TWO_BYTES(linet, j - 1); + minval = L_MIN(val2t, val4t); + minval = L_MIN(minval, 0xfffe); + SET_DATA_TWO_BYTES(linet, j, minval + 1); + if (val2t < val4t) + vald = GET_DATA_BYTE(lined - wpld, j); + else + vald = GET_DATA_BYTE(lined, j - 1); + SET_DATA_BYTE(lined, j, vald); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) { + val7t = GET_DATA_TWO_BYTES(linet + wplt, j); + val5t = GET_DATA_TWO_BYTES(linet, j + 1); + minval = L_MIN(val5t, val7t); + minval = L_MIN(minval + 1, valt); + if (valt > minval) { /* replace */ + SET_DATA_TWO_BYTES(linet, j, minval); + if (val5t < val7t) + vald = GET_DATA_BYTE(lined, j + 1); + else + vald = GET_DATA_BYTE(lined + wplt, j); + SET_DATA_BYTE(lined, j, vald); + } + } + } + } + break; + case 8: + /* UL --> LR scan */ + for (i = 1; i < h; i++) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = 1; j < jmax; j++) { + if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) { + val1t = GET_DATA_TWO_BYTES(linet - wplt, j - 1); + val2t = GET_DATA_TWO_BYTES(linet - wplt, j); + val3t = GET_DATA_TWO_BYTES(linet - wplt, j + 1); + val4t = GET_DATA_TWO_BYTES(linet, j - 1); + minval = L_MIN(val1t, val2t); + minval = L_MIN(minval, val3t); + minval = L_MIN(minval, val4t); + minval = L_MIN(minval, 0xfffe); + SET_DATA_TWO_BYTES(linet, j, minval + 1); + if (minval == val1t) + vald = GET_DATA_BYTE(lined - wpld, j - 1); + else if (minval == val2t) + vald = GET_DATA_BYTE(lined - wpld, j); + else if (minval == val3t) + vald = GET_DATA_BYTE(lined - wpld, j + 1); + else /* minval == val4t */ + vald = GET_DATA_BYTE(lined, j - 1); + SET_DATA_BYTE(lined, j, vald); + } + } + } + + /* LR --> UL scan */ + for (i = imax - 1; i > 0; i--) { + linet = datat + i * wplt; + lined = datad + i * wpld; + for (j = jmax - 1; j > 0; j--) { + if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) { + val8t = GET_DATA_TWO_BYTES(linet + wplt, j + 1); + val7t = GET_DATA_TWO_BYTES(linet + wplt, j); + val6t = GET_DATA_TWO_BYTES(linet + wplt, j - 1); + val5t = GET_DATA_TWO_BYTES(linet, j + 1); + minval = L_MIN(val8t, val7t); + minval = L_MIN(minval, val6t); + minval = L_MIN(minval, val5t); + minval = L_MIN(minval + 1, valt); + if (valt > minval) { /* replace */ + SET_DATA_TWO_BYTES(linet, j, minval); + if (minval == val5t + 1) + vald = GET_DATA_BYTE(lined, j + 1); + else if (minval == val6t + 1) + vald = GET_DATA_BYTE(lined + wpld, j - 1); + else if (minval == val7t + 1) + vald = GET_DATA_BYTE(lined + wpld, j); + else /* minval == val8t + 1 */ + vald = GET_DATA_BYTE(lined + wpld, j + 1); + SET_DATA_BYTE(lined, j, vald); + } + } + } + } + break; + default: + L_ERROR("connectivity must be 4 or 8\n", procName); + break; + } +} + + +/*-----------------------------------------------------------------------* + * Local extrema * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixLocalExtrema() + * + * \param[in] pixs 8 bpp + * \param[in] maxmin max allowed for the min in a 3x3 neighborhood; + * use 0 for default which is to have no upper bound + * \param[in] minmax min allowed for the max in a 3x3 neighborhood; + * use 0 for default which is to have no lower bound + * \param[out] ppixmin [optional] mask of local minima + * \param[out] ppixmax [optional] mask of local maxima + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This gives the actual local minima and maxima.
+ *          A local minimum is a pixel whose surrounding pixels all
+ *          have values at least as large, and likewise for a local
+ *          maximum.  For the local minima, %maxmin is the upper
+ *          bound for the value of pixs.  Likewise, for the local maxima,
+ *          %minmax is the lower bound for the value of pixs.
+ *      (2) The minima are found by starting with the erosion-and-equality
+ *          approach of pixSelectedLocalExtrema().  This is followed
+ *          by a qualification step, where each c.c. in the resulting
+ *          minimum mask is extracted, the pixels bordering it are
+ *          located, and they are queried.  If all of those pixels
+ *          are larger than the value of that minimum, it is a true
+ *          minimum and its c.c. is saved; otherwise the c.c. is
+ *          rejected.  Note that if a bordering pixel has the
+ *          same value as the minimum, it must then have a
+ *          neighbor that is smaller, so the component is not a
+ *          true minimum.
+ *      (3) The maxima are found by inverting the image and looking
+ *          for the minima there.
+ *      (4) The generated masks can be used as markers for
+ *          further operations.
+ * 
+ */ +l_ok +pixLocalExtrema(PIX *pixs, + l_int32 maxmin, + l_int32 minmax, + PIX **ppixmin, + PIX **ppixmax) +{ +PIX *pixmin, *pixmax, *pixt1, *pixt2; + + PROCNAME("pixLocalExtrema"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!ppixmin && !ppixmax) + return ERROR_INT("neither &pixmin, &pixmax are defined", procName, 1); + if (maxmin <= 0) maxmin = 254; + if (minmax <= 0) minmax = 1; + + if (ppixmin) { + pixt1 = pixErodeGray(pixs, 3, 3); + pixmin = pixFindEqualValues(pixs, pixt1); + pixDestroy(&pixt1); + pixQualifyLocalMinima(pixs, pixmin, maxmin); + *ppixmin = pixmin; + } + + if (ppixmax) { + pixt1 = pixInvert(NULL, pixs); + pixt2 = pixErodeGray(pixt1, 3, 3); + pixmax = pixFindEqualValues(pixt1, pixt2); + pixDestroy(&pixt2); + pixQualifyLocalMinima(pixt1, pixmax, 255 - minmax); + *ppixmax = pixmax; + pixDestroy(&pixt1); + } + + return 0; +} + + +/*! + * \brief pixQualifyLocalMinima() + * + * \param[in] pixs 8 bpp image from which pixm has been extracted + * \param[in] pixm 1 bpp mask of values equal to min in 3x3 neighborhood + * \param[in] maxval max allowed for the min in a 3x3 neighborhood; + * use 0 for default which is to have no upper bound + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function acts in-place to remove all c.c. in pixm
+ *          that are not true local minima in pixs.  As seen in
+ *          pixLocalExtrema(), the input pixm are found by selecting those
+ *          pixels of pixs whose values do not change with a 3x3
+ *          grayscale erosion.  Here, we require that for each c.c.
+ *          in pixm, all pixels in pixs that correspond to the exterior
+ *          boundary pixels of the c.c. have values that are greater
+ *          than the value within the c.c.
+ *      (2) The maximum allowed value for each local minimum can be
+ *          bounded with %maxval.  Use 0 for default, which is to have
+ *          no upper bound (equivalent to maxval == 254).
+ * 
+ */ +static l_int32 +pixQualifyLocalMinima(PIX *pixs, + PIX *pixm, + l_int32 maxval) +{ +l_int32 n, i, j, k, x, y, w, h, xc, yc, wc, hc, xon, yon; +l_int32 vals, wpls, wplc, ismin; +l_uint32 val; +l_uint32 *datas, *datac, *lines, *linec; +BOXA *boxa; +PIX *pix1, *pix2, *pix3; +PIXA *pixa; + + PROCNAME("pixQualifyLocalMinima"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm not defined or not 1 bpp", procName, 1); + if (maxval <= 0) maxval = 254; + + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + boxa = pixConnComp(pixm, &pixa, 8); + n = pixaGetCount(pixa); + for (k = 0; k < n; k++) { + boxaGetBoxGeometry(boxa, k, &xc, &yc, &wc, &hc); + pix1 = pixaGetPix(pixa, k, L_COPY); + pix2 = pixAddBorder(pix1, 1, 0); + pix3 = pixDilateBrick(NULL, pix2, 3, 3); + pixXor(pix3, pix3, pix2); /* exterior boundary pixels */ + datac = pixGetData(pix3); + wplc = pixGetWpl(pix3); + nextOnPixelInRaster(pix1, 0, 0, &xon, &yon); + pixGetPixel(pixs, xc + xon, yc + yon, &val); + if (val > maxval) { /* too large; erase */ + pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + continue; + } + ismin = TRUE; + + /* Check all values in pixs that correspond to the exterior + * boundary pixels of the c.c. in pixm. Verify that the + * value in the c.c. is always less. */ + for (i = 0, y = yc - 1; i < hc + 2 && y >= 0 && y < h; i++, y++) { + lines = datas + y * wpls; + linec = datac + i * wplc; + for (j = 0, x = xc - 1; j < wc + 2 && x >= 0 && x < w; j++, x++) { + if (GET_DATA_BIT(linec, j)) { + vals = GET_DATA_BYTE(lines, x); + if (vals <= val) { /* not a minimum! */ + ismin = FALSE; + break; + } + } + } + if (!ismin) + break; + } + if (!ismin) /* erase it */ + pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + return 0; +} + + +/*! + * \brief pixSelectedLocalExtrema() + * + * \param[in] pixs 8 bpp + * \param[in] mindist -1 for keeping all pixels; >= 0 specifies distance + * \param[out] ppixmin mask of local minima + * \param[out] ppixmax mask of local maxima + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This selects those local 3x3 minima that are at least a
+ *          specified distance from the nearest local 3x3 maxima, and v.v.
+ *          for the selected set of local 3x3 maxima.
+ *          The local 3x3 minima is the set of pixels whose value equals
+ *          the value after a 3x3 brick erosion, and the local 3x3 maxima
+ *          is the set of pixels whose value equals the value after
+ *          a 3x3 brick dilation.
+ *      (2) mindist is the minimum distance allowed between
+ *          local 3x3 minima and local 3x3 maxima, in an 8-connected sense.
+ *          mindist == 1 keeps all pixels found in step 1.
+ *          mindist == 0 removes all pixels from each mask that are
+ *          both a local 3x3 minimum and a local 3x3 maximum.
+ *          mindist == 1 removes any local 3x3 minimum pixel that touches a
+ *          local 3x3 maximum pixel, and likewise for the local maxima.
+ *          To make the decision, visualize each local 3x3 minimum pixel
+ *          as being surrounded by a square of size (2 * mindist + 1)
+ *          on each side, such that no local 3x3 maximum pixel is within
+ *          that square; and v.v.
+ *      (3) The generated masks can be used as markers for further operations.
+ * 
+ */ +l_ok +pixSelectedLocalExtrema(PIX *pixs, + l_int32 mindist, + PIX **ppixmin, + PIX **ppixmax) +{ +PIX *pixmin, *pixmax, *pixt, *pixtmin, *pixtmax; + + PROCNAME("pixSelectedLocalExtrema"); + + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); + if (!ppixmin || !ppixmax) + return ERROR_INT("&pixmin and &pixmax not both defined", procName, 1); + + pixt = pixErodeGray(pixs, 3, 3); + pixmin = pixFindEqualValues(pixs, pixt); + pixDestroy(&pixt); + pixt = pixDilateGray(pixs, 3, 3); + pixmax = pixFindEqualValues(pixs, pixt); + pixDestroy(&pixt); + + /* Remove all points that are within the prescribed distance + * from each other. */ + if (mindist < 0) { /* remove no points */ + *ppixmin = pixmin; + *ppixmax = pixmax; + } else if (mindist == 0) { /* remove points belonging to both sets */ + pixt = pixAnd(NULL, pixmin, pixmax); + *ppixmin = pixSubtract(pixmin, pixmin, pixt); + *ppixmax = pixSubtract(pixmax, pixmax, pixt); + pixDestroy(&pixt); + } else { + pixtmin = pixDilateBrick(NULL, pixmin, + 2 * mindist + 1, 2 * mindist + 1); + pixtmax = pixDilateBrick(NULL, pixmax, + 2 * mindist + 1, 2 * mindist + 1); + *ppixmin = pixSubtract(pixmin, pixmin, pixtmax); + *ppixmax = pixSubtract(pixmax, pixmax, pixtmin); + pixDestroy(&pixtmin); + pixDestroy(&pixtmax); + } + return 0; +} + + +/*! + * \brief pixFindEqualValues() + * + * \param[in] pixs1 8 bpp + * \param[in] pixs2 8 bpp + * \return pixd 1 bpp mask, or NULL on error + * + *
+ * Notes:
+ *      (1) The two images are aligned at the UL corner, and the returned
+ *          image has ON pixels where the pixels in pixs1 and pixs2
+ *          have equal values.
+ * 
+ */ +PIX * +pixFindEqualValues(PIX *pixs1, + PIX *pixs2) +{ +l_int32 w1, h1, w2, h2, w, h; +l_int32 i, j, val1, val2, wpls1, wpls2, wpld; +l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined; +PIX *pixd; + + PROCNAME("pixFindEqualValues"); + + if (!pixs1 || pixGetDepth(pixs1) != 8) + return (PIX *)ERROR_PTR("pixs1 undefined or not 8 bpp", procName, NULL); + if (!pixs2 || pixGetDepth(pixs2) != 8) + return (PIX *)ERROR_PTR("pixs2 undefined or not 8 bpp", procName, NULL); + pixGetDimensions(pixs1, &w1, &h1, NULL); + pixGetDimensions(pixs2, &w2, &h2, NULL); + w = L_MIN(w1, w2); + h = L_MIN(h1, h2); + pixd = pixCreate(w, h, 1); + datas1 = pixGetData(pixs1); + datas2 = pixGetData(pixs2); + datad = pixGetData(pixd); + wpls1 = pixGetWpl(pixs1); + wpls2 = pixGetWpl(pixs2); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lines1 = datas1 + i * wpls1; + lines2 = datas2 + i * wpls2; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val1 = GET_DATA_BYTE(lines1, j); + val2 = GET_DATA_BYTE(lines2, j); + if (val1 == val2) + SET_DATA_BIT(lined, j); + } + } + + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Selection of minima in mask connected components * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixSelectMinInConnComp() + * + * \param[in] pixs 8 bpp + * \param[in] pixm 1 bpp + * \param[out] ppta pta of min pixel locations + * \param[out] pnav [optional] numa of minima values + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) For each 8 connected component in pixm, this finds
+ *          a pixel in pixs that has the lowest value, and saves
+ *          it in a Pta.  If several pixels in pixs have the same
+ *          minimum value, it picks the first one found.
+ *      (2) For a mask pixm of true local minima, all pixels in each
+ *          connected component have the same value in pixs, so it is
+ *          fastest to select one of them using a special seedfill
+ *          operation.  Not yet implemented.
+ * 
+ */ +l_ok +pixSelectMinInConnComp(PIX *pixs, + PIX *pixm, + PTA **ppta, + NUMA **pnav) +{ +l_int32 bx, by, bw, bh, i, j, c, n; +l_int32 xs, ys, minx, miny, wpls, wplt, val, minval; +l_uint32 *datas, *datat, *lines, *linet; +BOXA *boxa; +NUMA *nav; +PIX *pixt, *pixs2, *pixm2; +PIXA *pixa; +PTA *pta; + + PROCNAME("pixSelectMinInConnComp"); + + if (!ppta) + return ERROR_INT("&pta not defined", procName, 1); + *ppta = NULL; + if (pnav) *pnav = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); + if (!pixm || pixGetDepth(pixm) != 1) + return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); + + /* Crop to the min size if necessary */ + if (pixCropToMatch(pixs, pixm, &pixs2, &pixm2)) { + pixDestroy(&pixs2); + pixDestroy(&pixm2); + return ERROR_INT("cropping failure", procName, 1); + } + + /* Find value and location of min value pixel in each component */ + boxa = pixConnComp(pixm2, &pixa, 8); + n = boxaGetCount(boxa); + pta = ptaCreate(n); + *ppta = pta; + nav = numaCreate(n); + datas = pixGetData(pixs2); + wpls = pixGetWpl(pixs2); + for (c = 0; c < n; c++) { + pixt = pixaGetPix(pixa, c, L_CLONE); + boxaGetBoxGeometry(boxa, c, &bx, &by, &bw, &bh); + if (bw == 1 && bh == 1) { + ptaAddPt(pta, bx, by); + numaAddNumber(nav, GET_DATA_BYTE(datas + by * wpls, bx)); + pixDestroy(&pixt); + continue; + } + datat = pixGetData(pixt); + wplt = pixGetWpl(pixt); + minx = miny = 1000000; + minval = 256; + for (i = 0; i < bh; i++) { + ys = by + i; + lines = datas + ys * wpls; + linet = datat + i * wplt; + for (j = 0; j < bw; j++) { + xs = bx + j; + if (GET_DATA_BIT(linet, j)) { + val = GET_DATA_BYTE(lines, xs); + if (val < minval) { + minval = val; + minx = xs; + miny = ys; + } + } + } + } + ptaAddPt(pta, minx, miny); + numaAddNumber(nav, GET_DATA_BYTE(datas + miny * wpls, minx)); + pixDestroy(&pixt); + } + + boxaDestroy(&boxa); + pixaDestroy(&pixa); + if (pnav) + *pnav = nav; + else + numaDestroy(&nav); + pixDestroy(&pixs2); + pixDestroy(&pixm2); + return 0; +} + + +/*-----------------------------------------------------------------------* + * Removal of seeded connected components from a mask * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixRemoveSeededComponents() + * + * \param[in] pixd [optional]; can be null or equal to pixm; 1 bpp + * \param[in] pixs 1 bpp seed + * \param[in] pixm 1 bpp filling mask + * \param[in] connectivity 4 or 8 + * \param[in] bordersize amount of border clearing + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) This removes each component in pixm for which there is
+ *          at least one seed in pixs.  If pixd == NULL, this returns
+ *          the result in a new pixd.  Otherwise, it is an in-place
+ *          operation on pixm.  In no situation is pixs altered,
+ *          because we do the filling with a copy of pixs.
+ *      (2) If bordersize > 0, it also clears all pixels within a
+ *          distance %bordersize of the edge of pixd.  This is here
+ *          because pixLocalExtrema() typically finds local minima
+ *          at the border.  Use %bordersize >= 2 to remove these.
+ * 
+ */ +PIX * +pixRemoveSeededComponents(PIX *pixd, + PIX *pixs, + PIX *pixm, + l_int32 connectivity, + l_int32 bordersize) +{ +PIX *pixt; + + PROCNAME("pixRemoveSeededComponents"); + + if (!pixs || pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd); + if (!pixm || pixGetDepth(pixm) != 1) + return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd); + if (pixd && pixd != pixm) + return (PIX *)ERROR_PTR("operation not inplace", procName, pixd); + + pixt = pixCopy(NULL, pixs); + pixSeedfillBinary(pixt, pixt, pixm, connectivity); + pixd = pixXor(pixd, pixm, pixt); + if (bordersize > 0) + pixSetOrClearBorder(pixd, bordersize, bordersize, bordersize, + bordersize, PIX_CLR); + pixDestroy(&pixt); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sel1.c b/hgdriver/3rdparty/hgOCR/leptonica/sel1.c new file mode 100644 index 0000000..e630972 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sel1.c @@ -0,0 +1,2376 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file sel1.c + *
+ *
+ *      Basic ops on Sels and Selas
+ *
+ *         Create/destroy/copy:
+ *            SELA      *selaCreate()
+ *            void       selaDestroy()
+ *            SEL       *selCreate()
+ *            void       selDestroy()
+ *            SEL       *selCopy()
+ *            SEL       *selCreateBrick()
+ *            SEL       *selCreateComb()
+ *
+ *         Helper proc:
+ *            l_int32  **create2dIntArray()
+ *
+ *         Extension of sela:
+ *            SELA      *selaAddSel()
+ *            static l_int32  selaExtendArray()
+ *
+ *         Accessors:
+ *            l_int32    selaGetCount()
+ *            SEL       *selaGetSel()
+ *            char      *selGetName()
+ *            l_int32    selSetName()
+ *            l_int32    selaFindSelByName()
+ *            l_int32    selGetElement()
+ *            l_int32    selSetElement()
+ *            l_int32    selGetParameters()
+ *            l_int32    selSetOrigin()
+ *            l_int32    selGetTypeAtOrigin()
+ *            char      *selaGetBrickName()
+ *            char      *selaGetCombName()
+ *     static char      *selaComputeCompositeParameters()
+ *            l_int32    getCompositeParameters()
+ *            SARRAY    *selaGetSelnames()
+ *
+ *         Max translations for erosion and hmt
+ *            l_int32    selFindMaxTranslations()
+ *
+ *         Rotation by multiples of 90 degrees
+ *            SEL       *selRotateOrth()
+ *
+ *         Sela and Sel serialized I/O
+ *            SELA      *selaRead()
+ *            SELA      *selaReadStream()
+ *            SEL       *selRead()
+ *            SEL       *selReadStream()
+ *            l_int32    selaWrite()
+ *            l_int32    selaWriteStream()
+ *            l_int32    selWrite()
+ *            l_int32    selWriteStream()
+ *
+ *         Building custom hit-miss sels from compiled strings
+ *            SEL       *selCreateFromString()
+ *            char      *selPrintToString()     [for debugging]
+ *
+ *         Building custom hit-miss sels from a simple file format
+ *            SELA      *selaCreateFromFile()
+ *            static SEL *selCreateFromSArray()
+ *
+ *         Making hit-only sels from Pta and Pix
+ *            SEL       *selCreateFromPta()
+ *            SEL       *selCreateFromPix()
+ *
+ *         Making hit-miss sels from Pix and image files
+ *            SEL       *selReadFromColorImage()
+ *            SEL       *selCreateFromColorPix()
+ *
+ *         Printable display of sel
+ *            PIX       *selDisplayInPix()
+ *            PIX       *selaDisplayInPix()
+ *
+ *     Usage notes:
+ *        In this file we have seven functions that make sels:
+ *          (1)  selCreate(), with input (h, w, [name])
+ *               The generic function.  Roll your own, using selSetElement().
+ *          (2)  selCreateBrick(), with input (h, w, cy, cx, val)
+ *               The most popular function.  Makes a rectangular sel of
+ *               all hits, misses or don't-cares.  We have many morphology
+ *               operations that create a sel of all hits, use it, and
+ *               destroy it.
+ *          (3)  selCreateFromString() with input (text, h, w, [name])
+ *               Adam Langley's clever function, allows you to make a hit-miss
+ *               sel from a string in code that is geometrically laid out
+ *               just like the actual sel.
+ *          (4)  selaCreateFromFile() with input (filename)
+ *               This parses a simple file format to create an array of
+ *               hit-miss sels.  The sel data uses the same encoding
+ *               as in (3), with geometrical layout enforced.
+ *          (5)  selCreateFromPta() with input (pta, cy, cx, [name])
+ *               Another way to make a sel with only hits.
+ *          (6)  selCreateFromPix() with input (pix, cy, cx, [name])
+ *               Yet another way to make a sel from hits.
+ *          (7)  selCreateFromColorPix() with input (pix, name).
+ *               Another way to make a general hit-miss sel, starting with
+ *               an image editor.
+ *        In addition, there are three functions in selgen.c that
+ *        automatically generate a hit-miss sel from a pix and
+ *        a number of parameters.  This is useful for problems like
+ *        "find all patterns that look like this one."
+ *
+ *        Consistency, being the hobgoblin of small minds,
+ *        is adhered to here in the dimensioning and accessing of sels.
+ *        Everything is done in standard matrix (row, column) order.
+ *        When we set specific elements in a sel, we likewise use
+ *        (row, col) ordering:
+ *             selSetElement(), with input (row, col, type)
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Bounds on sel ptr array size */ +static const l_uint32 MaxPtrArraySize = 10000; +static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */ + + /* Bounds on kernel size */ +static const l_uint32 MaxKernelSize = 10000; + + /* Static functions */ +static l_int32 selaExtendArray(SELA *sela); +static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last); + +struct CompParameterMap +{ + l_int32 size; + l_int32 size1; + l_int32 size2; + char selnameh1[20]; + char selnameh2[20]; + char selnamev1[20]; + char selnamev2[20]; +}; + +static const struct CompParameterMap comp_parameter_map[] = + { { 2, 2, 1, "sel_2h", "", "sel_2v", "" }, + { 3, 3, 1, "sel_3h", "", "sel_3v", "" }, + { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" }, + { 5, 5, 1, "sel_5h", "", "sel_5v", "" }, + { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" }, + { 7, 7, 1, "sel_7h", "", "sel_7v", "" }, + { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" }, + { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" }, + { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" }, + { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, + { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, + { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, + { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" }, + { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" }, + { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, + { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, + { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" }, + { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, + { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, + { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" }, + { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" }, + { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, + { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, + { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, + { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, + { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" }, + { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" }, + { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, + { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, + { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, + { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" }, + { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" }, + { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, + { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, + { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, + { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, + { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, + { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" }, + { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" }, + { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, + { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, + { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, + { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" }, + { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, + { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, + { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, + { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, + { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" }, + { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, + { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, + { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" }, + { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, + { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, + { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" }, + { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, + { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, + { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, + { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, + { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, + { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, + { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" }, + { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } }; + + + +/*------------------------------------------------------------------------* + * Create / Destroy / Copy * + *------------------------------------------------------------------------*/ +/*! + * \brief selaCreate() + * + * \param[in] n initial number of sel ptrs; use 0 for default + * \return sela, or NULL on error + */ +SELA * +selaCreate(l_int32 n) +{ +SELA *sela; + + PROCNAME("selaCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + /* Make array of sel ptrs */ + sela = (SELA *)LEPT_CALLOC(1, sizeof(SELA)); + sela->nalloc = n; + sela->n = 0; + if ((sela->sel = (SEL **)LEPT_CALLOC(n, sizeof(SEL *))) == NULL) { + LEPT_FREE(sela); + return (SELA *)ERROR_PTR("sel ptrs not made", procName, NULL); + } + return sela; +} + + +/*! + * \brief selaDestroy() + * + * \param[in,out] psela will be set to null before returning + * \return void + */ +void +selaDestroy(SELA **psela) +{ +SELA *sela; +l_int32 i; + + if (!psela) return; + if ((sela = *psela) == NULL) + return; + + for (i = 0; i < sela->n; i++) + selDestroy(&sela->sel[i]); + LEPT_FREE(sela->sel); + LEPT_FREE(sela); + *psela = NULL; + return; +} + + +/*! + * \brief selCreate() + * + * \param[in] height + * \param[in] width + * \param[in] name [optional] sel name; can be null + * \return sel, or NULL on error + * + *
+ * Notes:
+ *      (1) selCreate() initializes all values to 0.
+ *      (2) After this call, (cy,cx) and nonzero data values must be
+ *          assigned.  If a text name is not assigned here, it will
+ *          be needed later when the sel is put into a sela.
+ * 
+ */ +SEL * +selCreate(l_int32 height, + l_int32 width, + const char *name) +{ +SEL *sel; + + PROCNAME("selCreate"); + + if ((sel = (SEL *)LEPT_CALLOC(1, sizeof(SEL))) == NULL) + return (SEL *)ERROR_PTR("sel not made", procName, NULL); + if (name) + sel->name = stringNew(name); + sel->sy = height; + sel->sx = width; + if ((sel->data = create2dIntArray(height, width)) == NULL) { + LEPT_FREE(sel->name); + LEPT_FREE(sel); + return (SEL *)ERROR_PTR("data not allocated", procName, NULL); + } + + return sel; +} + + +/*! + * \brief selDestroy() + * + * \param[in,out] psel will be set to null before returning + * \return void + */ +void +selDestroy(SEL **psel) +{ +l_int32 i; +SEL *sel; + + PROCNAME("selDestroy"); + + if (psel == NULL) { + L_WARNING("ptr address is NULL!\n", procName); + return; + } + if ((sel = *psel) == NULL) + return; + + for (i = 0; i < sel->sy; i++) + LEPT_FREE(sel->data[i]); + LEPT_FREE(sel->data); + if (sel->name) + LEPT_FREE(sel->name); + LEPT_FREE(sel); + + *psel = NULL; + return; +} + + +/*! + * \brief selCopy() + * + * \param[in] sel + * \return a copy of the sel, or NULL on error + */ +SEL * +selCopy(SEL *sel) +{ +l_int32 sx, sy, cx, cy, i, j; +SEL *csel; + + PROCNAME("selCopy"); + + if (!sel) + return (SEL *)ERROR_PTR("sel not defined", procName, NULL); + + if ((csel = (SEL *)LEPT_CALLOC(1, sizeof(SEL))) == NULL) + return (SEL *)ERROR_PTR("csel not made", procName, NULL); + selGetParameters(sel, &sy, &sx, &cy, &cx); + csel->sy = sy; + csel->sx = sx; + csel->cy = cy; + csel->cx = cx; + + if ((csel->data = create2dIntArray(sy, sx)) == NULL) { + LEPT_FREE(csel); + return (SEL *)ERROR_PTR("sel data not made", procName, NULL); + } + + for (i = 0; i < sy; i++) + for (j = 0; j < sx; j++) + csel->data[i][j] = sel->data[i][j]; + + if (sel->name) + csel->name = stringNew(sel->name); + + return csel; +} + + +/*! + * \brief selCreateBrick() + * + * \param[in] h, w height, width + * \param[in] cy, cx origin, relative to UL corner at 0,0 + * \param[in] type SEL_HIT, SEL_MISS, or SEL_DONT_CARE + * \return sel, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a rectangular sel of all hits, misses or don't cares.
+ * 
+ */ +SEL * +selCreateBrick(l_int32 h, + l_int32 w, + l_int32 cy, + l_int32 cx, + l_int32 type) +{ +l_int32 i, j; +SEL *sel; + + PROCNAME("selCreateBrick"); + + if (h <= 0 || w <= 0) + return (SEL *)ERROR_PTR("h and w must both be > 0", procName, NULL); + if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) + return (SEL *)ERROR_PTR("invalid sel element type", procName, NULL); + + if ((sel = selCreate(h, w, NULL)) == NULL) + return (SEL *)ERROR_PTR("sel not made", procName, NULL); + selSetOrigin(sel, cy, cx); + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + sel->data[i][j] = type; + + return sel; +} + + +/*! + * \brief selCreateComb() + * + * \param[in] factor1 contiguous space between comb tines + * \param[in] factor2 number of comb tines + * \param[in] direction L_HORIZ, L_VERT + * \return sel, or NULL on error + * + *
+ * Notes:
+ *      (1) This generates a comb Sel of hits with the origin as
+ *          near the center as possible.
+ *      (2) In use, this is complemented by a brick sel of size %factor1,
+ *          Both brick and comb sels are made by selectComposableSels().
+ * 
+ */ +SEL * +selCreateComb(l_int32 factor1, + l_int32 factor2, + l_int32 direction) +{ +l_int32 i, size, z; +SEL *sel; + + PROCNAME("selCreateComb"); + + if (factor1 < 1 || factor2 < 1) + return (SEL *)ERROR_PTR("factors must be >= 1", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT) + return (SEL *)ERROR_PTR("invalid direction", procName, NULL); + + size = factor1 * factor2; + if (direction == L_HORIZ) { + sel = selCreate(1, size, NULL); + selSetOrigin(sel, 0, size / 2); + } else { + sel = selCreate(size, 1, NULL); + selSetOrigin(sel, size / 2, 0); + } + + /* Lay down the elements of the comb */ + for (i = 0; i < factor2; i++) { + z = factor1 / 2 + i * factor1; +/* fprintf(stderr, "i = %d, factor1 = %d, factor2 = %d, z = %d\n", + i, factor1, factor2, z); */ + if (direction == L_HORIZ) + selSetElement(sel, 0, z, SEL_HIT); + else + selSetElement(sel, z, 0, SEL_HIT); + } + + return sel; +} + + +/*! + * \brief create2dIntArray() + * + * \param[in] sy rows == height + * \param[in] sx columns == width + * \return doubly indexed array i.e., an array of sy row pointers, + * each of which points to an array of sx ints + * + *
+ * Notes:
+ *      (1) The array[sy][sx] is indexed in standard "matrix notation",
+ *          with the row index first.
+ * 
+ */ +l_int32 ** +create2dIntArray(l_int32 sy, + l_int32 sx) +{ +l_int32 i, j, success; +l_int32 **array; + + PROCNAME("create2dIntArray"); + + if (sx <= 0 || sx > MaxKernelSize) + return (l_int32 **)ERROR_PTR("sx out of bounds", procName, NULL); + if (sy <= 0 || sy > MaxKernelSize) + return (l_int32 **)ERROR_PTR("sy out of bounds", procName, NULL); + + if ((array = (l_int32 **)LEPT_CALLOC(sy, sizeof(l_int32 *))) == NULL) + return (l_int32 **)ERROR_PTR("ptr array not made", procName, NULL); + success = TRUE; + for (i = 0; i < sy; i++) { + if ((array[i] = (l_int32 *)LEPT_CALLOC(sx, sizeof(l_int32))) == NULL) { + success = FALSE; + break; + } + } + if (success) return array; + + /* Cleanup after error */ + for (j = 0; j < i; j++) + LEPT_FREE(array[j]); + LEPT_FREE(array); + return (l_int32 **)ERROR_PTR("array not made", procName, NULL); +} + + + +/*------------------------------------------------------------------------* + * Extension of sela * + *------------------------------------------------------------------------*/ +/*! + * \brief selaAddSel() + * + * \param[in] sela + * \param[in] sel to be added + * \param[in] selname ignored if already defined in sel; + * req'd in sel when added to a sela + * \param[in] copyflag L_INSERT or L_COPY + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This adds a sel, either inserting or making a copy.
+ *      (2) Because every sel in a sela must have a name, it copies
+ *          the input name if necessary.  You can input NULL for
+ *          selname if the sel already has a name.
+ * 
+ */ +l_ok +selaAddSel(SELA *sela, + SEL *sel, + const char *selname, + l_int32 copyflag) +{ +l_int32 n; +SEL *csel; + + PROCNAME("selaAddSel"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + if (!sel->name && !selname) + return ERROR_INT("added sel must have name", procName, 1); + if (copyflag != L_INSERT && copyflag != L_COPY) + return ERROR_INT("invalid copyflag", procName, 1); + + if (copyflag == L_COPY) { + if ((csel = selCopy(sel)) == NULL) + return ERROR_INT("csel not made", procName, 1); + } else { /* copyflag == L_INSERT */ + csel = sel; + } + if (!csel->name) + csel->name = stringNew(selname); + + n = selaGetCount(sela); + if (n >= sela->nalloc) + selaExtendArray(sela); + sela->sel[n] = csel; + sela->n++; + + return 0; +} + + +/*! + * \brief selaExtendArray() + * + * \param[in] sela + * \return 0 if OK; 1 on error + */ +static l_int32 +selaExtendArray(SELA *sela) +{ + PROCNAME("selaExtendArray"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + + if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel, + sizeof(SEL *) * sela->nalloc, + 2 * sizeof(SEL *) * sela->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + sela->nalloc = 2 * sela->nalloc; + return 0; +} + + + +/*----------------------------------------------------------------------* + * Accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief selaGetCount() + * + * \param[in] sela + * \return count, or 0 on error + */ +l_int32 +selaGetCount(SELA *sela) +{ + PROCNAME("selaGetCount"); + + if (!sela) + return ERROR_INT("sela not defined", procName, 0); + + return sela->n; +} + + +/*! + * \brief selaGetSel() + * + * \param[in] sela + * \param[in] i index of sel to be retrieved not copied + * \return sel, or NULL on error + * + *
+ * Notes:
+ *      (1) This returns a ptr to the sel, not a copy, so the caller
+ *          must not destroy it!
+ * 
+ */ +SEL * +selaGetSel(SELA *sela, + l_int32 i) +{ + PROCNAME("selaGetSel"); + + if (!sela) + return (SEL *)ERROR_PTR("sela not defined", procName, NULL); + + if (i < 0 || i >= sela->n) + return (SEL *)ERROR_PTR("invalid index", procName, NULL); + return sela->sel[i]; +} + + +/*! + * \brief selGetName() + * + * \param[in] sel + * \return sel name not copied, or NULL if no name or on error + */ +char * +selGetName(SEL *sel) +{ + PROCNAME("selGetName"); + + if (!sel) + return (char *)ERROR_PTR("sel not defined", procName, NULL); + + return sel->name; +} + + +/*! + * \brief selSetName() + * + * \param[in] sel + * \param[in] name [optional]; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Always frees the existing sel name, if defined.
+ *      (2) If name is not defined, just clears any existing sel name.
+ * 
+ */ +l_ok +selSetName(SEL *sel, + const char *name) +{ + PROCNAME("selSetName"); + + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + + return stringReplace(&sel->name, name); +} + + +/*! + * \brief selaFindSelByName() + * + * \param[in] sela + * \param[in] name sel name + * \param[out] pindex [optional] + * \param[in] psel [optional] sel (not a copy) + * \return 0 if OK; 1 on error + */ +l_ok +selaFindSelByName(SELA *sela, + const char *name, + l_int32 *pindex, + SEL **psel) +{ +l_int32 i, n; +char *sname; +SEL *sel; + + PROCNAME("selaFindSelByName"); + + if (pindex) *pindex = -1; + if (psel) *psel = NULL; + + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + + n = selaGetCount(sela); + for (i = 0; i < n; i++) + { + if ((sel = selaGetSel(sela, i)) == NULL) { + L_WARNING("missing sel\n", procName); + continue; + } + + sname = selGetName(sel); + if (sname && (!strcmp(name, sname))) { + if (pindex) + *pindex = i; + if (psel) + *psel = sel; + return 0; + } + } + + return 1; +} + + +/*! + * \brief selGetElement() + * + * \param[in] sel + * \param[in] row + * \param[in] col + * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE + * \return 0 if OK; 1 on error + */ +l_ok +selGetElement(SEL *sel, + l_int32 row, + l_int32 col, + l_int32 *ptype) +{ + PROCNAME("selGetElement"); + + if (!ptype) + return ERROR_INT("&type not defined", procName, 1); + *ptype = SEL_DONT_CARE; + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + if (row < 0 || row >= sel->sy) + return ERROR_INT("sel row out of bounds", procName, 1); + if (col < 0 || col >= sel->sx) + return ERROR_INT("sel col out of bounds", procName, 1); + + *ptype = sel->data[row][col]; + return 0; +} + + +/*! + * \brief selSetElement() + * + * \param[in] sel + * \param[in] row + * \param[in] col + * \param[in] type SEL_HIT, SEL_MISS, SEL_DONT_CARE + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Because we use row and column to index into an array,
+ *          they are always non-negative.  The location of the origin
+ *          (and the type of operation) determine the actual
+ *          direction of the rasterop.
+ * 
+ */ +l_ok +selSetElement(SEL *sel, + l_int32 row, + l_int32 col, + l_int32 type) +{ + PROCNAME("selSetElement"); + + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) + return ERROR_INT("invalid sel element type", procName, 1); + if (row < 0 || row >= sel->sy) + return ERROR_INT("sel row out of bounds", procName, 1); + if (col < 0 || col >= sel->sx) + return ERROR_INT("sel col out of bounds", procName, 1); + + sel->data[row][col] = type; + return 0; +} + + +/*! + * \brief selGetParameters() + * + * \param[in] sel + * \param[out] psy, psx, pcy, pcx [optional] each can be null + * \return 0 if OK, 1 on error + */ +l_ok +selGetParameters(SEL *sel, + l_int32 *psy, + l_int32 *psx, + l_int32 *pcy, + l_int32 *pcx) +{ + PROCNAME("selGetParameters"); + + if (psy) *psy = 0; + if (psx) *psx = 0; + if (pcy) *pcy = 0; + if (pcx) *pcx = 0; + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + if (psy) *psy = sel->sy; + if (psx) *psx = sel->sx; + if (pcy) *pcy = sel->cy; + if (pcx) *pcx = sel->cx; + return 0; +} + + +/*! + * \brief selSetOrigin() + * + * \param[in] sel + * \param[in] cy, cx + * \return 0 if OK; 1 on error + */ +l_ok +selSetOrigin(SEL *sel, + l_int32 cy, + l_int32 cx) +{ + PROCNAME("selSetOrigin"); + + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + sel->cy = cy; + sel->cx = cx; + return 0; +} + + +/*! + * \brief selGetTypeAtOrigin() + * + * \param[in] sel + * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE + * \return 0 if OK; 1 on error or if origin is not found + */ +l_ok +selGetTypeAtOrigin(SEL *sel, + l_int32 *ptype) +{ +l_int32 sx, sy, cx, cy, i, j; + + PROCNAME("selGetTypeAtOrigin"); + + if (!ptype) + return ERROR_INT("&type not defined", procName, 1); + *ptype = SEL_DONT_CARE; /* init */ + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + + selGetParameters(sel, &sy, &sx, &cy, &cx); + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + if (i == cy && j == cx) { + selGetElement(sel, i, j, ptype); + return 0; + } + } + } + + return ERROR_INT("sel origin not found", procName, 1); +} + + +/*! + * \brief selaGetBrickName() + * + * \param[in] sela + * \param[in] hsize, vsize of brick sel + * \return sel name new string, or NULL if no name or on error + */ +char * +selaGetBrickName(SELA *sela, + l_int32 hsize, + l_int32 vsize) +{ +l_int32 i, nsels, sx, sy; +SEL *sel; + + PROCNAME("selaGetBrickName"); + + if (!sela) + return (char *)ERROR_PTR("sela not defined", procName, NULL); + + nsels = selaGetCount(sela); + for (i = 0; i < nsels; i++) { + sel = selaGetSel(sela, i); + selGetParameters(sel, &sy, &sx, NULL, NULL); + if (hsize == sx && vsize == sy) + return stringNew(selGetName(sel)); + } + + return (char *)ERROR_PTR("sel not found", procName, NULL); +} + + +/*! + * \brief selaGetCombName() + * + * \param[in] sela + * \param[in] size the product of sizes of the brick and comb parts + * \param[in] direction L_HORIZ, L_VERT + * \return sel name new string, or NULL if name not found or on error + * + *
+ * Notes:
+ *      (1) Combs are by definition 1-dimensional, either horiz or vert.
+ *      (2) Use this with comb Sels; e.g., from selaAddDwaCombs().
+ * 
+ */ +char * +selaGetCombName(SELA *sela, + l_int32 size, + l_int32 direction) +{ +char *selname; +char combname[256]; +l_int32 i, nsels, sx, sy, found; +SEL *sel; + + PROCNAME("selaGetCombName"); + + if (!sela) + return (char *)ERROR_PTR("sela not defined", procName, NULL); + if (direction != L_HORIZ && direction != L_VERT) + return (char *)ERROR_PTR("invalid direction", procName, NULL); + + /* Derive the comb name we're looking for */ + if (direction == L_HORIZ) + snprintf(combname, sizeof(combname), "sel_comb_%dh", size); + else /* direction == L_VERT */ + snprintf(combname, sizeof(combname), "sel_comb_%dv", size); + + found = FALSE; + nsels = selaGetCount(sela); + for (i = 0; i < nsels; i++) { + sel = selaGetSel(sela, i); + selGetParameters(sel, &sy, &sx, NULL, NULL); + if (sy != 1 && sx != 1) /* 2-D; not a comb */ + continue; + selname = selGetName(sel); + if (!strcmp(selname, combname)) { + found = TRUE; + break; + } + } + + if (found) + return stringNew(selname); + else + return (char *)ERROR_PTR("sel not found", procName, NULL); +} + + +/* --------- Function used to generate code in this file ---------- */ +#if 0 +static void selaComputeCompositeParameters(const char *fileout); + +/*! + * \brief selaComputeCompParameters() + * + * \param[in] fileout + * \return void + * + *
+ * Notes:
+ *      (1) This static function was used to construct the comp_parameter_map[]
+ *          array at the top of this file.  It is static because it does
+ *          not need to be called again.  It remains here to show how
+ *          the composite parameter map was computed.
+ *      (2) The output file was pasted directly into comp_parameter_map[].
+ *          The composite parameter map is used to quickly determine
+ *          the linear decomposition parameters and sel names.
+ * 
+ */ +static void +selaComputeCompositeParameters(const char *fileout) +{ +char *str, *nameh1, *nameh2, *namev1, *namev2; +char buf[256]; +l_int32 size, size1, size2, len; +SARRAY *sa; +SELA *selabasic, *selacomb; + + selabasic = selaAddBasic(NULL); + selacomb = selaAddDwaCombs(NULL); + sa = sarrayCreate(64); + for (size = 2; size < 64; size++) { + selectComposableSizes(size, &size1, &size2); + nameh1 = selaGetBrickName(selabasic, size1, 1); + namev1 = selaGetBrickName(selabasic, 1, size1); + if (size2 > 1) { + nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ); + namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT); + } else { + nameh2 = stringNew(""); + namev2 = stringNew(""); + } + snprintf(buf, sizeof(buf), + " { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },", + size, size1, size2, nameh1, nameh2, namev1, namev2); + sarrayAddString(sa, buf, L_COPY); + LEPT_FREE(nameh1); + LEPT_FREE(nameh2); + LEPT_FREE(namev1); + LEPT_FREE(namev2); + } + str = sarrayToString(sa, 1); + len = strlen(str); + l_binaryWrite(fileout, "w", str, len + 1); + LEPT_FREE(str); + sarrayDestroy(&sa); + selaDestroy(&selabasic); + selaDestroy(&selacomb); + return; +} +#endif +/* -------------------------------------------------------------------- */ + + +/*! + * \brief getCompositeParameters() + * + * \param[in] size + * \param[out] psize1 [optional] brick factor size + * \param[out] psize2 [optional] comb factor size + * \param[out] pnameh1 [optional] name of horiz brick + * \param[out] pnameh2 [optional] name of horiz comb + * \param[out] pnamev1 [optional] name of vert brick + * \param[out] pnamev2 [optional] name of vert comb + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This uses the big lookup table at the top of this file.
+ *      (2) All returned strings are copies that must be freed.
+ * 
+ */ +l_ok +getCompositeParameters(l_int32 size, + l_int32 *psize1, + l_int32 *psize2, + char **pnameh1, + char **pnameh2, + char **pnamev1, + char **pnamev2) +{ +l_int32 index; + + PROCNAME("selaGetSelnames"); + + if (psize1) *psize1 = 0; + if (psize2) *psize2 = 0; + if (pnameh1) *pnameh1 = NULL; + if (pnameh2) *pnameh2 = NULL; + if (pnamev1) *pnamev1 = NULL; + if (pnamev2) *pnamev2 = NULL; + if (size < 2 || size > 63) + return ERROR_INT("valid size range is {2 ... 63}", procName, 1); + index = size - 2; + if (psize1) + *psize1 = comp_parameter_map[index].size1; + if (psize2) + *psize2 = comp_parameter_map[index].size2; + if (pnameh1) + *pnameh1 = stringNew(comp_parameter_map[index].selnameh1); + if (pnameh2) + *pnameh2 = stringNew(comp_parameter_map[index].selnameh2); + if (pnamev1) + *pnamev1 = stringNew(comp_parameter_map[index].selnamev1); + if (pnamev2) + *pnamev2 = stringNew(comp_parameter_map[index].selnamev2); + return 0; +} + + +/*! + * \brief selaGetSelnames() + * + * \param[in] sela + * \return sa of all sel names, or NULL on error + */ +SARRAY * +selaGetSelnames(SELA *sela) +{ +char *selname; +l_int32 i, n; +SEL *sel; +SARRAY *sa; + + PROCNAME("selaGetSelnames"); + + if (!sela) + return (SARRAY *)ERROR_PTR("sela not defined", procName, NULL); + if ((n = selaGetCount(sela)) == 0) + return (SARRAY *)ERROR_PTR("no sels in sela", procName, NULL); + + if ((sa = sarrayCreate(n)) == NULL) + return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); + for (i = 0; i < n; i++) { + sel = selaGetSel(sela, i); + selname = selGetName(sel); + sarrayAddString(sa, selname, L_COPY); + } + + return sa; +} + + + +/*----------------------------------------------------------------------* + * Max translations for erosion and hmt * + *----------------------------------------------------------------------*/ +/*! + * \brief selFindMaxTranslations() + * + * \param[in] sel + * \param[out] pxp, pyp, pxn, pyn max shifts + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+          These are the maximum shifts for the erosion operation.
+ *        For example, when j < cx, the shift of the image
+ *        is +x to the cx.  This is a positive xp shift.
+ * 
+ */ +l_ok +selFindMaxTranslations(SEL *sel, + l_int32 *pxp, + l_int32 *pyp, + l_int32 *pxn, + l_int32 *pyn) +{ +l_int32 sx, sy, cx, cy, i, j; +l_int32 maxxp, maxyp, maxxn, maxyn; + + PROCNAME("selaFindMaxTranslations"); + + if (!pxp || !pyp || !pxn || !pyn) + return ERROR_INT("&xp (etc) defined", procName, 1); + *pxp = *pyp = *pxn = *pyn = 0; + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + selGetParameters(sel, &sy, &sx, &cy, &cx); + + maxxp = maxyp = maxxn = maxyn = 0; + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + if (sel->data[i][j] == 1) { + maxxp = L_MAX(maxxp, cx - j); + maxyp = L_MAX(maxyp, cy - i); + maxxn = L_MAX(maxxn, j - cx); + maxyn = L_MAX(maxyn, i - cy); + } + } + } + + *pxp = maxxp; + *pyp = maxyp; + *pxn = maxxn; + *pyn = maxyn; + + return 0; +} + + +/*----------------------------------------------------------------------* + * Rotation by multiples of 90 degrees * + *----------------------------------------------------------------------*/ +/*! + * \brief selRotateOrth() + * + * \param[in] sel + * \param[in] quads 0 - 4; number of 90 degree cw rotations + * \return seld, or NULL on error + */ +SEL * +selRotateOrth(SEL *sel, + l_int32 quads) +{ +l_int32 i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type; +SEL *seld; + + PROCNAME("selRotateOrth"); + + if (!sel) + return (SEL *)ERROR_PTR("sel not defined", procName, NULL); + if (quads < 0 || quads > 4) + return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", procName, NULL); + if (quads == 0 || quads == 4) + return selCopy(sel); + + selGetParameters(sel, &sy, &sx, &cy, &cx); + if (quads == 1) { /* 90 degrees cw */ + nsx = sy; + nsy = sx; + ncx = sy - cy - 1; + ncy = cx; + } else if (quads == 2) { /* 180 degrees cw */ + nsx = sx; + nsy = sy; + ncx = sx - cx - 1; + ncy = sy - cy - 1; + } else { /* 270 degrees cw */ + nsx = sy; + nsy = sx; + ncx = cy; + ncy = sx - cx - 1; + } + seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE); + if (sel->name) + seld->name = stringNew(sel->name); + + for (i = 0; i < sy; i++) { + for (j = 0; j < sx; j++) { + selGetElement(sel, i, j, &type); + if (quads == 1) { + ni = j; + nj = sy - i - 1; + } else if (quads == 2) { + ni = sy - i - 1; + nj = sx - j - 1; + } else { /* quads == 3 */ + ni = sx - j - 1; + nj = i; + } + selSetElement(seld, ni, nj, type); + } + } + + return seld; +} + + +/*----------------------------------------------------------------------* + * Sela and Sel serialized I/O * + *----------------------------------------------------------------------*/ +/*! + * \brief selaRead() + * + * \param[in] fname filename + * \return sela, or NULL on error + */ +SELA * +selaRead(const char *fname) +{ +FILE *fp; +SELA *sela; + + PROCNAME("selaRead"); + + if (!fname) + return (SELA *)ERROR_PTR("fname not defined", procName, NULL); + + if ((fp = fopenReadStream(fname)) == NULL) + return (SELA *)ERROR_PTR("stream not opened", procName, NULL); + if ((sela = selaReadStream(fp)) == NULL) { + fclose(fp); + return (SELA *)ERROR_PTR("sela not returned", procName, NULL); + } + fclose(fp); + + return sela; +} + + +/*! + * \brief selaReadStream() + * + * \param[in] fp file stream + * \return sela, or NULL on error + */ +SELA * +selaReadStream(FILE *fp) +{ +l_int32 i, n, version; +SEL *sel; +SELA *sela; + + PROCNAME("selaReadStream"); + + if (!fp) + return (SELA *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, "\nSela Version %d\n", &version) != 1) + return (SELA *)ERROR_PTR("not a sela file", procName, NULL); + if (version != SEL_VERSION_NUMBER) + return (SELA *)ERROR_PTR("invalid sel version", procName, NULL); + if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1) + return (SELA *)ERROR_PTR("not a sela file", procName, NULL); + + if ((sela = selaCreate(n)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + sela->nalloc = n; + + for (i = 0; i < n; i++) { + if ((sel = selReadStream(fp)) == NULL) { + selaDestroy(&sela); + return (SELA *)ERROR_PTR("sel not read", procName, NULL); + } + selaAddSel(sela, sel, NULL, 0); + } + + return sela; +} + + +/*! + * \brief selRead() + * + * \param[in] fname filename + * \return sel, or NULL on error + */ +SEL * +selRead(const char *fname) +{ +FILE *fp; +SEL *sel; + + PROCNAME("selRead"); + + if (!fname) + return (SEL *)ERROR_PTR("fname not defined", procName, NULL); + + if ((fp = fopenReadStream(fname)) == NULL) + return (SEL *)ERROR_PTR("stream not opened", procName, NULL); + if ((sel = selReadStream(fp)) == NULL) { + fclose(fp); + return (SEL *)ERROR_PTR("sela not returned", procName, NULL); + } + fclose(fp); + + return sel; +} + + +/*! + * \brief selReadStream() + * + * \param[in] fp file stream + * \return sel, or NULL on error + */ +SEL * +selReadStream(FILE *fp) +{ +char *selname; +char linebuf[256]; +l_int32 sy, sx, cy, cx, i, j, version, ignore; +SEL *sel; + + PROCNAME("selReadStream"); + + if (!fp) + return (SEL *)ERROR_PTR("stream not defined", procName, NULL); + + if (fscanf(fp, " Sel Version %d\n", &version) != 1) + return (SEL *)ERROR_PTR("not a sel file", procName, NULL); + if (version != SEL_VERSION_NUMBER) + return (SEL *)ERROR_PTR("invalid sel version", procName, NULL); + + if (fgets(linebuf, sizeof(linebuf), fp) == NULL) + return (SEL *)ERROR_PTR("error reading into linebuf", procName, NULL); + selname = stringNew(linebuf); + sscanf(linebuf, " ------ %200s ------", selname); + + if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", + &sy, &sx, &cy, &cx) != 4) { + LEPT_FREE(selname); + return (SEL *)ERROR_PTR("dimensions not read", procName, NULL); + } + + if ((sel = selCreate(sy, sx, selname)) == NULL) { + LEPT_FREE(selname); + return (SEL *)ERROR_PTR("sel not made", procName, NULL); + } + selSetOrigin(sel, cy, cx); + + for (i = 0; i < sy; i++) { + ignore = fscanf(fp, " "); + for (j = 0; j < sx; j++) + ignore = fscanf(fp, "%1d", &sel->data[i][j]); + ignore = fscanf(fp, "\n"); + } + ignore = fscanf(fp, "\n"); + + LEPT_FREE(selname); + return sel; +} + + +/*! + * \brief selaWrite() + * + * \param[in] fname filename + * \param[in] sela + * \return 0 if OK, 1 on error + */ +l_ok +selaWrite(const char *fname, + SELA *sela) +{ +FILE *fp; + + PROCNAME("selaWrite"); + + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + + if ((fp = fopenWriteStream(fname, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + selaWriteStream(fp, sela); + fclose(fp); + + return 0; +} + + +/*! + * \brief selaWriteStream() + * + * \param[in] fp file stream + * \param[in] sela + * \return 0 if OK, 1 on error + */ +l_ok +selaWriteStream(FILE *fp, + SELA *sela) +{ +l_int32 i, n; +SEL *sel; + + PROCNAME("selaWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!sela) + return ERROR_INT("sela not defined", procName, 1); + + n = selaGetCount(sela); + fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER); + fprintf(fp, "Number of Sels = %d\n\n", n); + for (i = 0; i < n; i++) { + if ((sel = selaGetSel(sela, i)) == NULL) + continue; + selWriteStream(fp, sel); + } + return 0; +} + + +/*! + * \brief selWrite() + * + * \param[in] fname filename + * \param[in] sel + * \return 0 if OK, 1 on error + */ +l_ok +selWrite(const char *fname, + SEL *sel) +{ +FILE *fp; + + PROCNAME("selWrite"); + + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + + if ((fp = fopenWriteStream(fname, "wb")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + selWriteStream(fp, sel); + fclose(fp); + + return 0; +} + + +/*! + * \brief selWriteStream() + * + * \param[in] fp file stream + * \param[in] sel + * \return 0 if OK, 1 on error + */ +l_ok +selWriteStream(FILE *fp, + SEL *sel) +{ +l_int32 sx, sy, cx, cy, i, j; + + PROCNAME("selWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!sel) + return ERROR_INT("sel not defined", procName, 1); + selGetParameters(sel, &sy, &sx, &cy, &cx); + + fprintf(fp, " Sel Version %d\n", SEL_VERSION_NUMBER); + fprintf(fp, " ------ %s ------\n", selGetName(sel)); + fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); + for (i = 0; i < sy; i++) { + fprintf(fp, " "); + for (j = 0; j < sx; j++) + fprintf(fp, "%d", sel->data[i][j]); + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + + return 0; +} + + +/*----------------------------------------------------------------------* + * Building custom hit-miss sels from compiled strings * + *----------------------------------------------------------------------*/ +/*! + * \brief selCreateFromString() + * + * \param[in] text + * \param[in] h, w height, width + * \param[in] name [optional] sel name; can be null + * \return sel of the given size, or NULL on error + * + *
+ * Notes:
+ *      (1) The text is an array of chars (in row-major order) where
+ *          each char can be one of the following:
+ *             'x': hit
+ *             'o': miss
+ *             ' ': don't-care
+ *      (2) When the origin falls on a hit or miss, use an upper case
+ *          char (e.g., 'X' or 'O') to indicate it.  When the origin
+ *          falls on a don't-care, indicate this with a 'C'.
+ *          The string must have exactly one origin specified.
+ *      (3) The advantage of this method is that the text can be input
+ *          in a format that shows the 2D layout of the Sel; e.g.,
+ * \code
+ *              static const char *seltext = "x    "
+ *                                           "x Oo "
+ *                                           "x    "
+ *                                           "xxxxx";
+ * \endcode
+ * 
+ */ +SEL * +selCreateFromString(const char *text, + l_int32 h, + l_int32 w, + const char *name) +{ +SEL *sel; +l_int32 y, x, norig; +char ch; + + PROCNAME("selCreateFromString"); + + if (!text || text[0] == '\0') + return (SEL *)ERROR_PTR("text undefined or empty", procName, NULL); + if (h < 1) + return (SEL *)ERROR_PTR("height must be > 0", procName, NULL); + if (w < 1) + return (SEL *)ERROR_PTR("width must be > 0", procName, NULL); + if (strlen(text) != (size_t)w * h) + return (SEL *)ERROR_PTR("text size != w * h", procName, NULL); + + sel = selCreate(h, w, name); + norig = 0; + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + ch = *(text++); + switch (ch) + { + case 'X': + norig++; + selSetOrigin(sel, y, x); + case 'x': + selSetElement(sel, y, x, SEL_HIT); + break; + + case 'O': + norig++; + selSetOrigin(sel, y, x); + case 'o': + selSetElement(sel, y, x, SEL_MISS); + break; + + case 'C': + norig++; + selSetOrigin(sel, y, x); + case ' ': + selSetElement(sel, y, x, SEL_DONT_CARE); + break; + + case '\n': + /* ignored */ + continue; + + default: + selDestroy(&sel); + return (SEL *)ERROR_PTR("unknown char", procName, NULL); + } + } + } + if (norig != 1) { + L_ERROR("Exactly one origin must be specified; this string has %d\n", + procName, norig); + selDestroy(&sel); + } + + return sel; +} + + +/*! + * \brief selPrintToString() + * + * \param[in] sel + * \return str string; caller must free + * + *
+ * Notes:
+ *      (1) This is an inverse function of selCreateFromString.
+ *          It prints a textual representation of the SEL to a malloc'd
+ *          string.  The format is the same as selCreateFromString
+ *          except that newlines are inserted into the output
+ *          between rows.
+ *      (2) This is useful for debugging.  However, if you want to
+ *          save some Sels in a file, put them in a Sela and write
+ *          them out with selaWrite().  They can then be read in
+ *          with selaRead().
+ * 
+ */ +char * +selPrintToString(SEL *sel) +{ +char is_center; +char *str, *strptr; +l_int32 type; +l_int32 sx, sy, cx, cy, x, y; + + PROCNAME("selPrintToString"); + + if (!sel) + return (char *)ERROR_PTR("sel not defined", procName, NULL); + + selGetParameters(sel, &sy, &sx, &cy, &cx); + if ((str = (char *)LEPT_CALLOC(1, sy * (sx + 1) + 1)) == NULL) + return (char *)ERROR_PTR("calloc fail for str", procName, NULL); + strptr = str; + + for (y = 0; y < sy; ++y) { + for (x = 0; x < sx; ++x) { + selGetElement(sel, y, x, &type); + is_center = (x == cx && y == cy); + switch (type) { + case SEL_HIT: + *(strptr++) = is_center ? 'X' : 'x'; + break; + case SEL_MISS: + *(strptr++) = is_center ? 'O' : 'o'; + break; + case SEL_DONT_CARE: + *(strptr++) = is_center ? 'C' : ' '; + break; + } + } + *(strptr++) = '\n'; + } + + return str; +} + + +/*----------------------------------------------------------------------* + * Building custom hit-miss sels from a simple file format * + *----------------------------------------------------------------------*/ +/*! + * \brief selaCreateFromFile() + * + * \param[in] filename + * \return sela, or NULL on error + * + *
+ * Notes:
+ *      (1) The file contains a sequence of Sel descriptions.
+ *      (2) Each Sel is formatted as follows:
+ *           ~ Any number of comment lines starting with '#' are ignored
+ *           ~ The next line contains the selname
+ *           ~ The next lines contain the Sel data.  They must be
+ *             formatted similarly to the string format in
+ *             selCreateFromString(), with each line beginning and
+ *             ending with a double-quote, and showing the 2D layout.
+ *           ~ Each Sel ends when a blank line, a comment line, or
+ *             the end of file is reached.
+ *      (3) See selCreateFromString() for a description of the string
+ *          format for the Sel data.  As an example, here are the lines
+ *          of is a valid file for a single Sel.  In the file, all lines
+ *          are left-justified:
+ *                    # diagonal sel
+ *                    sel_5diag
+ *                    "x    "
+ *                    " x   "
+ *                    "  X  "
+ *                    "   x "
+ *                    "    x"
+ * 
+ */ +SELA * +selaCreateFromFile(const char *filename) +{ +char *filestr, *line; +l_int32 i, n, first, last, nsel, insel; +size_t nbytes; +NUMA *nafirst, *nalast; +SARRAY *sa; +SEL *sel; +SELA *sela; + + PROCNAME("selaCreateFromFile"); + + if (!filename) + return (SELA *)ERROR_PTR("filename not defined", procName, NULL); + + filestr = (char *)l_binaryRead(filename, &nbytes); + sa = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + n = sarrayGetCount(sa); + sela = selaCreate(0); + + /* Find the start and end lines for each Sel. + * We allow the "blank" lines to be null strings or + * to have standard whitespace (' ','\t',\'n') or be '#'. */ + nafirst = numaCreate(0); + nalast = numaCreate(0); + insel = FALSE; + for (i = 0; i < n; i++) { + line = sarrayGetString(sa, i, L_NOCOPY); + if (!insel && + (line[0] != '\0' && line[0] != ' ' && + line[0] != '\t' && line[0] != '\n' && line[0] != '#')) { + numaAddNumber(nafirst, i); + insel = TRUE; + continue; + } + if (insel && + (line[0] == '\0' || line[0] == ' ' || + line[0] == '\t' || line[0] == '\n' || line[0] == '#')) { + numaAddNumber(nalast, i - 1); + insel = FALSE; + continue; + } + } + if (insel) /* fell off the end of the file */ + numaAddNumber(nalast, n - 1); + + /* Extract sels */ + nsel = numaGetCount(nafirst); + for (i = 0; i < nsel; i++) { + numaGetIValue(nafirst, i, &first); + numaGetIValue(nalast, i, &last); + if ((sel = selCreateFromSArray(sa, first, last)) == NULL) { + fprintf(stderr, "Error reading sel from %d to %d\n", first, last); + selaDestroy(&sela); + sarrayDestroy(&sa); + numaDestroy(&nafirst); + numaDestroy(&nalast); + return (SELA *)ERROR_PTR("bad sela file", procName, NULL); + } + selaAddSel(sela, sel, NULL, 0); + } + + numaDestroy(&nafirst); + numaDestroy(&nalast); + sarrayDestroy(&sa); + return sela; +} + + +/*! + * \brief selCreateFromSArray() + * + * \param[in] sa + * \param[in] first line of sarray where Sel begins + * \param[in] last line of sarray where Sel ends + * \return sela, or NULL on error + * + *
+ * Notes:
+ *      (1) The Sel contains the following lines:
+ *          ~ The first line is the selname
+ *          ~ The remaining lines contain the Sel data.  They must
+ *            be formatted similarly to the string format in
+ *            selCreateFromString(), with each line beginning and
+ *            ending with a double-quote, and showing the 2D layout.
+ *          ~ 'last' gives the last line in the Sel data.
+ *      (2) See selCreateFromString() for a description of the string
+ *          format for the Sel data.  As an example, here are the lines
+ *          of is a valid file for a single Sel.  In the file, all lines
+ *          are left-justified:
+ *                    # diagonal sel
+ *                    sel_5diag
+ *                    "x    "
+ *                    " x   "
+ *                    "  X  "
+ *                    "   x "
+ *                    "    x"
+ * 
+ */ +static SEL * +selCreateFromSArray(SARRAY *sa, + l_int32 first, + l_int32 last) +{ +char ch; +char *name, *line; +l_int32 n, len, i, w, h, y, x; +SEL *sel; + + PROCNAME("selCreateFromSArray"); + + if (!sa) + return (SEL *)ERROR_PTR("sa not defined", procName, NULL); + n = sarrayGetCount(sa); + if (first < 0 || first >= n || last <= first || last >= n) + return (SEL *)ERROR_PTR("invalid range", procName, NULL); + + name = sarrayGetString(sa, first, L_NOCOPY); + h = last - first; + line = sarrayGetString(sa, first + 1, L_NOCOPY); + len = strlen(line); + if (line[0] != '"' || line[len - 1] != '"') + return (SEL *)ERROR_PTR("invalid format", procName, NULL); + w = len - 2; + if ((sel = selCreate(h, w, name)) == NULL) + return (SEL *)ERROR_PTR("sel not made", procName, NULL); + for (i = first + 1; i <= last; i++) { + line = sarrayGetString(sa, i, L_NOCOPY); + y = i - first - 1; + for (x = 0; x < w; ++x) { + ch = line[x + 1]; /* skip the leading double-quote */ + switch (ch) + { + case 'X': + selSetOrigin(sel, y, x); /* set origin and hit */ + case 'x': + selSetElement(sel, y, x, SEL_HIT); + break; + + case 'O': + selSetOrigin(sel, y, x); /* set origin and miss */ + case 'o': + selSetElement(sel, y, x, SEL_MISS); + break; + + case 'C': + selSetOrigin(sel, y, x); /* set origin and don't-care */ + case ' ': + selSetElement(sel, y, x, SEL_DONT_CARE); + break; + + default: + selDestroy(&sel); + return (SEL *)ERROR_PTR("unknown char", procName, NULL); + } + } + } + + return sel; +} + + +/*----------------------------------------------------------------------* + * Making hit-only SELs from Pta and Pix * + *----------------------------------------------------------------------*/ +/*! + * \brief selCreateFromPta() + * + * \param[in] pta + * \param[in] cy, cx origin of sel + * \param[in] name [optional] sel name; can be null + * \return sel of minimum required size, or NULL on error + * + *
+ * Notes:
+ *      (1) The origin and all points in the pta must be positive.
+ * 
+ */ +SEL * +selCreateFromPta(PTA *pta, + l_int32 cy, + l_int32 cx, + const char *name) +{ +l_int32 i, n, x, y, w, h; +BOX *box; +SEL *sel; + + PROCNAME("selCreateFromPta"); + + if (!pta) + return (SEL *)ERROR_PTR("pta not defined", procName, NULL); + if (cy < 0 || cx < 0) + return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL); + n = ptaGetCount(pta); + if (n == 0) + return (SEL *)ERROR_PTR("no pts in pta", procName, NULL); + + box = ptaGetBoundingRegion(pta); + boxGetGeometry(box, &x, &y, &w, &h); + boxDestroy(&box); + if (x < 0 || y < 0) + return (SEL *)ERROR_PTR("not all x and y >= 0", procName, NULL); + + sel = selCreate(y + h, x + w, name); + selSetOrigin(sel, cy, cx); + for (i = 0; i < n; i++) { + ptaGetIPt(pta, i, &x, &y); + selSetElement(sel, y, x, SEL_HIT); + } + + return sel; +} + + +/*! + * \brief selCreateFromPix() + * + * \param[in] pix + * \param[in] cy, cx origin of sel + * \param[in] name [optional] sel name; can be null + * \return sel, or NULL on error + * + *
+ * Notes:
+ *      (1) The origin must be positive.
+ * 
+ */ +SEL * +selCreateFromPix(PIX *pix, + l_int32 cy, + l_int32 cx, + const char *name) +{ +SEL *sel; +l_int32 i, j, w, h, d; +l_uint32 val; + + PROCNAME("selCreateFromPix"); + + if (!pix) + return (SEL *)ERROR_PTR("pix not defined", procName, NULL); + if (cy < 0 || cx < 0) + return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1) + return (SEL *)ERROR_PTR("pix not 1 bpp", procName, NULL); + + sel = selCreate(h, w, name); + selSetOrigin(sel, cy, cx); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pix, j, i, &val); + if (val) + selSetElement(sel, i, j, SEL_HIT); + } + } + + return sel; +} + + +/*----------------------------------------------------------------------* + * Making hit-miss sels from color Pix and image files * + *----------------------------------------------------------------------*/ +/*! + * + * selReadFromColorImage() + * + * \param[in] pathname + * \return sel if OK; NULL on error + * + *
+ * Notes:
+ *      (1) Loads an image from a file and creates a (hit-miss) sel.
+ *      (2) The sel name is taken from the pathname without the directory
+ *          and extension.
+ * 
+ */ +SEL * +selReadFromColorImage(const char *pathname) +{ +PIX *pix; +SEL *sel; +char *basename, *selname; + + PROCNAME("selReadFromColorImage"); + + splitPathAtExtension (pathname, &basename, NULL); + splitPathAtDirectory (basename, NULL, &selname); + LEPT_FREE(basename); + + if ((pix = pixRead(pathname)) == NULL) { + LEPT_FREE(selname); + return (SEL *)ERROR_PTR("pix not returned", procName, NULL); + } + if ((sel = selCreateFromColorPix(pix, selname)) == NULL) + L_ERROR("sel not made\n", procName); + + LEPT_FREE(selname); + pixDestroy(&pix); + return sel; +} + + +/*! + * + * selCreateFromColorPix() + * + * \param[in] pixs cmapped or rgb + * \param[in] selname [optional] sel name; can be null + * \return sel if OK, NULL on error + * + *
+ * Notes:
+ *      (1) The sel size is given by the size of pixs.
+ *      (2) In pixs, hits are represented by green pixels, misses by red
+ *          pixels, and don't-cares by white pixels.
+ *      (3) In pixs, there may be no misses, but there must be at least 1 hit.
+ *      (4) At most there can be only one origin pixel, which is optionally
+ *          specified by using a lower-intensity pixel:
+ *            if a hit:  dark green
+ *            if a miss: dark red
+ *            if a don't care: gray
+ *          If there is no such pixel, the origin defaults to the approximate
+ *          center of the sel.
+ * 
+ */ +SEL * +selCreateFromColorPix(PIX *pixs, + const char *selname) +{ +PIXCMAP *cmap; +SEL *sel; +l_int32 hascolor, hasorigin, nohits; +l_int32 w, h, d, i, j, red, green, blue; +l_uint32 pixval; + + PROCNAME("selCreateFromColorPix"); + + if (!pixs) + return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); + + hascolor = FALSE; + cmap = pixGetColormap(pixs); + if (cmap) + pixcmapHasColor(cmap, &hascolor); + pixGetDimensions(pixs, &w, &h, &d); + if (hascolor == FALSE && d != 32) + return (SEL *)ERROR_PTR("pixs has no color", procName, NULL); + + if ((sel = selCreate (h, w, NULL)) == NULL) + return (SEL *)ERROR_PTR ("sel not made", procName, NULL); + selSetOrigin (sel, h / 2, w / 2); + selSetName(sel, selname); + + hasorigin = FALSE; + nohits = TRUE; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel (pixs, j, i, &pixval); + + if (cmap) { + pixcmapGetColor (cmap, pixval, &red, &green, &blue); + } else { + red = GET_DATA_BYTE (&pixval, COLOR_RED); + green = GET_DATA_BYTE (&pixval, COLOR_GREEN); + blue = GET_DATA_BYTE (&pixval, COLOR_BLUE); + } + + if (red < 255 && green < 255 && blue < 255) { + if (hasorigin) + L_WARNING("multiple origins in sel image\n", procName); + selSetOrigin (sel, i, j); + hasorigin = TRUE; + } + if (!red && green && !blue) { + nohits = FALSE; + selSetElement (sel, i, j, SEL_HIT); + } else if (red && !green && !blue) { + selSetElement (sel, i, j, SEL_MISS); + } else if (red && green && blue) { + selSetElement (sel, i, j, SEL_DONT_CARE); + } else { + selDestroy(&sel); + return (SEL *)ERROR_PTR("invalid color", procName, NULL); + } + } + } + + if (nohits) { + selDestroy(&sel); + return (SEL *)ERROR_PTR("no hits in sel", procName, NULL); + } + return sel; +} + + +/*----------------------------------------------------------------------* + * Printable display of sel * + *----------------------------------------------------------------------*/ +/*! + * \brief selDisplayInPix() + * + * \param[in] sel + * \param[in] size of grid interiors; odd; minimum size of 13 is enforced + * \param[in] gthick grid thickness; minimum size of 2 is enforced + * \return pix display of sel, or NULL on error + * + *
+ * Notes:
+ *      (1) This gives a visual representation of a general (hit-miss) sel.
+ *      (2) The empty sel is represented by a grid of intersecting lines.
+ *      (3) Three different patterns are generated for the sel elements:
+ *          ~ hit (solid black circle)
+ *          ~ miss (black ring; inner radius is radius2)
+ *          ~ origin (cross, XORed with whatever is there)
+ * 
+ */ +PIX * +selDisplayInPix(SEL *sel, + l_int32 size, + l_int32 gthick) +{ +l_int32 i, j, w, h, sx, sy, cx, cy, type, width; +l_int32 radius1, radius2, shift1, shift2, x0, y0; +PIX *pixd, *pix2, *pixh, *pixm, *pixorig; +PTA *pta1, *pta2, *pta1t, *pta2t; + + PROCNAME("selDisplayInPix"); + + if (!sel) + return (PIX *)ERROR_PTR("sel not defined", procName, NULL); + if (size < 13) { + L_WARNING("size < 13; setting to 13\n", procName); + size = 13; + } + if (size % 2 == 0) + size++; + if (gthick < 2) { + L_WARNING("grid thickness < 2; setting to 2\n", procName); + gthick = 2; + } + selGetParameters(sel, &sy, &sx, &cy, &cx); + w = size * sx + gthick * (sx + 1); + h = size * sy + gthick * (sy + 1); + pixd = pixCreate(w, h, 1); + + /* Generate grid lines */ + for (i = 0; i <= sy; i++) + pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), + w - 1, gthick / 2 + i * (size + gthick), + gthick, L_SET_PIXELS); + for (j = 0; j <= sx; j++) + pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, + gthick / 2 + j * (size + gthick), h - 1, + gthick, L_SET_PIXELS); + + /* Generate hit and miss patterns */ + radius1 = (l_int32)(0.85 * ((size - 1) / 2.0) + 0.5); /* of hit */ + radius2 = (l_int32)(0.65 * ((size - 1) / 2.0) + 0.5); /* of inner miss */ + pta1 = generatePtaFilledCircle(radius1); + pta2 = generatePtaFilledCircle(radius2); + shift1 = (size - 1) / 2 - radius1; /* center circle in square */ + shift2 = (size - 1) / 2 - radius2; + pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0); + pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0); + pixh = pixGenerateFromPta(pta1t, size, size); /* hits */ + pix2 = pixGenerateFromPta(pta2t, size, size); + pixm = pixSubtract(NULL, pixh, pix2); + + /* Generate crossed lines for origin pattern */ + pixorig = pixCreate(size, size, 1); + width = size / 8; + pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size), + size / 2, (l_int32)(0.88 * size), + width, L_SET_PIXELS); + pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2, + (l_int32)(0.85 * size), size / 2, + width, L_FLIP_PIXELS); + pixRasterop(pixorig, size / 2 - width, size / 2 - width, + 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); + + /* Specialize origin pattern for this sel */ + selGetTypeAtOrigin(sel, &type); + if (type == SEL_HIT) + pixXor(pixorig, pixorig, pixh); + else if (type == SEL_MISS) + pixXor(pixorig, pixorig, pixm); + + /* Paste the patterns in */ + y0 = gthick; + for (i = 0; i < sy; i++) { + x0 = gthick; + for (j = 0; j < sx; j++) { + selGetElement(sel, i, j, &type); + if (i == cy && j == cx) /* origin */ + pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0); + else if (type == SEL_HIT) + pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0); + else if (type == SEL_MISS) + pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0); + x0 += size + gthick; + } + y0 += size + gthick; + } + + pixDestroy(&pix2); + pixDestroy(&pixh); + pixDestroy(&pixm); + pixDestroy(&pixorig); + ptaDestroy(&pta1); + ptaDestroy(&pta1t); + ptaDestroy(&pta2); + ptaDestroy(&pta2t); + return pixd; +} + + +/*! + * \brief selaDisplayInPix() + * + * \param[in] sela + * \param[in] size of grid interiors; odd; minimum size of 13 is enforced + * \param[in] gthick grid thickness; minimum size of 2 is enforced + * \param[in] spacing between sels, both horizontally and vertically + * \param[in] ncols number of sels per "line" + * \return pix display of all sels in sela, or NULL on error + * + *
+ * Notes:
+ *      (1) This gives a visual representation of all the sels in a sela.
+ *      (2) See notes in selDisplayInPix() for display params of each sel.
+ *      (3) This gives the nicest results when all sels in the sela
+ *          are the same size.
+ * 
+ */ +PIX * +selaDisplayInPix(SELA *sela, + l_int32 size, + l_int32 gthick, + l_int32 spacing, + l_int32 ncols) +{ +l_int32 nsels, i, w, width; +PIX *pixt, *pixd; +PIXA *pixa; +SEL *sel; + + PROCNAME("selaDisplayInPix"); + + if (!sela) + return (PIX *)ERROR_PTR("sela not defined", procName, NULL); + if (size < 13) { + L_WARNING("size < 13; setting to 13\n", procName); + size = 13; + } + if (size % 2 == 0) + size++; + if (gthick < 2) { + L_WARNING("grid thickness < 2; setting to 2\n", procName); + gthick = 2; + } + if (spacing < 5) { + L_WARNING("spacing < 5; setting to 5\n", procName); + spacing = 5; + } + + /* Accumulate the pix of each sel */ + nsels = selaGetCount(sela); + pixa = pixaCreate(nsels); + for (i = 0; i < nsels; i++) { + sel = selaGetSel(sela, i); + pixt = selDisplayInPix(sel, size, gthick); + pixaAddPix(pixa, pixt, L_INSERT); + } + + /* Find the tiled output width, using just the first + * ncols pix in the pixa. If all pix have the same width, + * they will align properly in columns. */ + width = 0; + ncols = L_MIN(nsels, ncols); + for (i = 0; i < ncols; i++) { + pixt = pixaGetPix(pixa, i, L_CLONE); + pixGetDimensions(pixt, &w, NULL, NULL); + width += w; + pixDestroy(&pixt); + } + width += (ncols + 1) * spacing; /* add spacing all around as well */ + + pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0); + pixaDestroy(&pixa); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sel2.c b/hgdriver/3rdparty/hgOCR/leptonica/sel2.c new file mode 100644 index 0000000..d1f6f6a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sel2.c @@ -0,0 +1,845 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file sel2.c + *
+ *
+ *      Contains definitions of simple structuring elements
+ *
+ *      Basic brick structuring elements
+ *          SELA    *selaAddBasic()
+ *               Linear horizontal and vertical
+ *               Square
+ *               Diagonals
+ *
+ *      Simple hit-miss structuring elements
+ *          SELA    *selaAddHitMiss()
+ *               Isolated foreground pixel
+ *               Horizontal and vertical edges
+ *               Slanted edge
+ *               Corners
+ *
+ *      Structuring elements for comparing with DWA operations
+ *          SELA    *selaAddDwaLinear()
+ *          SELA    *selaAddDwaCombs()
+ *
+ *      Structuring elements for the intersection of lines
+ *          SELA    *selaAddCrossJunctions()
+ *          SELA    *selaAddTJunctions()
+ *
+ *      Structuring elements for connectivity-preserving thinning operations
+ *          SELA    *sela4ccThin()
+ *          SELA    *sela8ccThin()
+ *          SELA    *sela4and8ccThin()
+ * 
+ */ + +#include +#include "allheaders.h" + +//static const l_int32 L_BUF_SIZE = 512; +#define L_BUF_SIZE 512 + + /* Linear brick sel sizes, including all those that are required + * for decomposable sels up to size 63. */ +static const l_int32 num_linear = 25; +static const l_int32 basic_linear[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 20, 21, 25, 30, 31, 35, 40, 41, 45, 50, 51}; + + +/* ------------------------------------------------------------------- * + * Basic brick structuring elements * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddBasic() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds the following sels:
+ *            ~ all linear (horiz, vert) brick sels that are
+ *              necessary for decomposable sels up to size 63
+ *            ~ square brick sels up to size 10
+ *            ~ 4 diagonal sels
+ * 
+ */ +SELA * +selaAddBasic(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i, size; +SEL *sel; + + PROCNAME("selaAddBasic"); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + + /*--------------------------------------------------------------* + * Linear horizontal and vertical sels * + *--------------------------------------------------------------*/ + for (i = 0; i < num_linear; i++) { + size = basic_linear[i]; + sel = selCreateBrick(1, size, 0, size / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%dh", size); + selaAddSel(sela, sel, name, 0); + } + for (i = 0; i < num_linear; i++) { + size = basic_linear[i]; + sel = selCreateBrick(size, 1, size / 2, 0, 1); + snprintf(name, L_BUF_SIZE, "sel_%dv", size); + selaAddSel(sela, sel, name, 0); + } + + /*-----------------------------------------------------------* + * 2-d Bricks * + *-----------------------------------------------------------*/ + for (i = 2; i <= 5; i++) { + sel = selCreateBrick(i, i, i / 2, i / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%d", i); + selaAddSel(sela, sel, name, 0); + } + + /*-----------------------------------------------------------* + * Diagonals * + *-----------------------------------------------------------*/ + /* 0c 1 + 1 0 */ + sel = selCreateBrick(2, 2, 0, 0, 1); + selSetElement(sel, 0, 0, 0); + selSetElement(sel, 1, 1, 0); + selaAddSel(sela, sel, "sel_2dp", 0); + + /* 1c 0 + 0 1 */ + sel = selCreateBrick(2, 2, 0, 0, 1); + selSetElement(sel, 0, 1, 0); + selSetElement(sel, 1, 0, 0); + selaAddSel(sela, sel, "sel_2dm", 0); + + /* Diagonal, slope +, size 5 */ + sel = selCreate(5, 5, "sel_5dp"); + selSetOrigin(sel, 2, 2); + selSetElement(sel, 0, 4, 1); + selSetElement(sel, 1, 3, 1); + selSetElement(sel, 2, 2, 1); + selSetElement(sel, 3, 1, 1); + selSetElement(sel, 4, 0, 1); + selaAddSel(sela, sel, "sel_5dp", 0); + + /* Diagonal, slope -, size 5 */ + sel = selCreate(5, 5, "sel_5dm"); + selSetOrigin(sel, 2, 2); + selSetElement(sel, 0, 0, 1); + selSetElement(sel, 1, 1, 1); + selSetElement(sel, 2, 2, 1); + selSetElement(sel, 3, 3, 1); + selSetElement(sel, 4, 4, 1); + selaAddSel(sela, sel, "sel_5dm", 0); + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Simple hit-miss structuring elements * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddHitMiss() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + */ +SELA * +selaAddHitMiss(SELA *sela) +{ +SEL *sel; + + PROCNAME("selaAddHitMiss"); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + +#if 0 /* use just for testing */ + sel = selCreateBrick(3, 3, 1, 1, 2); + selaAddSel(sela, sel, "sel_bad", 0); +#endif + + + /*--------------------------------------------------------------* + * Isolated foreground pixel * + *--------------------------------------------------------------*/ + sel = selCreateBrick(3, 3, 1, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_HIT); + selaAddSel(sela, sel, "sel_3hm", 0); + + /*--------------------------------------------------------------* + * Horizontal and vertical edges * + *--------------------------------------------------------------*/ + sel = selCreateBrick(2, 3, 0, 1, SEL_HIT); + selSetElement(sel, 1, 0, SEL_MISS); + selSetElement(sel, 1, 1, SEL_MISS); + selSetElement(sel, 1, 2, SEL_MISS); + selaAddSel(sela, sel, "sel_3de", 0); + + sel = selCreateBrick(2, 3, 1, 1, SEL_HIT); + selSetElement(sel, 0, 0, SEL_MISS); + selSetElement(sel, 0, 1, SEL_MISS); + selSetElement(sel, 0, 2, SEL_MISS); + selaAddSel(sela, sel, "sel_3ue", 0); + + sel = selCreateBrick(3, 2, 1, 0, SEL_HIT); + selSetElement(sel, 0, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_MISS); + selSetElement(sel, 2, 1, SEL_MISS); + selaAddSel(sela, sel, "sel_3re", 0); + + sel = selCreateBrick(3, 2, 1, 1, SEL_HIT); + selSetElement(sel, 0, 0, SEL_MISS); + selSetElement(sel, 1, 0, SEL_MISS); + selSetElement(sel, 2, 0, SEL_MISS); + selaAddSel(sela, sel, "sel_3le", 0); + + /*--------------------------------------------------------------* + * Slanted edge * + *--------------------------------------------------------------*/ + sel = selCreateBrick(13, 6, 6, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 3, SEL_MISS); + selSetElement(sel, 0, 5, SEL_HIT); + selSetElement(sel, 4, 2, SEL_MISS); + selSetElement(sel, 4, 4, SEL_HIT); + selSetElement(sel, 8, 1, SEL_MISS); + selSetElement(sel, 8, 3, SEL_HIT); + selSetElement(sel, 12, 0, SEL_MISS); + selSetElement(sel, 12, 2, SEL_HIT); + selaAddSel(sela, sel, "sel_sl1", 0); + + /*--------------------------------------------------------------* + * Corners * + * This allows for up to 3 missing edge pixels at the corner * + *--------------------------------------------------------------*/ + sel = selCreateBrick(4, 4, 1, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 3, SEL_HIT); + selSetElement(sel, 2, 2, SEL_HIT); + selSetElement(sel, 2, 3, SEL_HIT); + selSetElement(sel, 3, 1, SEL_HIT); + selSetElement(sel, 3, 2, SEL_HIT); + selSetElement(sel, 3, 3, SEL_HIT); + selaAddSel(sela, sel, "sel_ulc", 0); + + sel = selCreateBrick(4, 4, 1, 2, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 1, 0, SEL_HIT); + selSetElement(sel, 2, 0, SEL_HIT); + selSetElement(sel, 2, 1, SEL_HIT); + selSetElement(sel, 3, 0, SEL_HIT); + selSetElement(sel, 3, 1, SEL_HIT); + selSetElement(sel, 3, 2, SEL_HIT); + selaAddSel(sela, sel, "sel_urc", 0); + + sel = selCreateBrick(4, 4, 2, 1, SEL_MISS); + selSetElement(sel, 1, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 1, SEL_HIT); + selSetElement(sel, 0, 2, SEL_HIT); + selSetElement(sel, 0, 3, SEL_HIT); + selSetElement(sel, 1, 2, SEL_HIT); + selSetElement(sel, 1, 3, SEL_HIT); + selSetElement(sel, 2, 3, SEL_HIT); + selaAddSel(sela, sel, "sel_llc", 0); + + sel = selCreateBrick(4, 4, 2, 2, SEL_MISS); + selSetElement(sel, 1, 2, SEL_DONT_CARE); + selSetElement(sel, 2, 1, SEL_DONT_CARE); + selSetElement(sel, 2, 2, SEL_DONT_CARE); + selSetElement(sel, 0, 0, SEL_HIT); + selSetElement(sel, 0, 1, SEL_HIT); + selSetElement(sel, 0, 2, SEL_HIT); + selSetElement(sel, 1, 0, SEL_HIT); + selSetElement(sel, 1, 1, SEL_HIT); + selSetElement(sel, 2, 0, SEL_HIT); + selaAddSel(sela, sel, "sel_lrc", 0); + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Structuring elements for comparing with DWA operations * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddDwaLinear() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds all linear (horizontal, vertical) sels from
+ *          2 to 63 pixels in length, which are the sizes over
+ *          which dwa code can be generated.
+ * 
+ */ +SELA * +selaAddDwaLinear(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i; +SEL *sel; + + PROCNAME("selaAddDwaLinear"); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + + for (i = 2; i < 64; i++) { + sel = selCreateBrick(1, i, 0, i / 2, 1); + snprintf(name, L_BUF_SIZE, "sel_%dh", i); + selaAddSel(sela, sel, name, 0); + } + for (i = 2; i < 64; i++) { + sel = selCreateBrick(i, 1, i / 2, 0, 1); + snprintf(name, L_BUF_SIZE, "sel_%dv", i); + selaAddSel(sela, sel, name, 0); + } + return sela; +} + + +/*! + * \brief selaAddDwaCombs() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds all comb (horizontal, vertical) Sels that are
+ *          used in composite linear morphological operations
+ *          up to 63 pixels in length, which are the sizes over
+ *          which dwa code can be generated.
+ * 
+ */ +SELA * +selaAddDwaCombs(SELA *sela) +{ +char name[L_BUF_SIZE]; +l_int32 i, f1, f2, prevsize, size; +SEL *selh, *selv; + + PROCNAME("selaAddDwaCombs"); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + + prevsize = 0; + for (i = 4; i < 64; i++) { + selectComposableSizes(i, &f1, &f2); + size = f1 * f2; + if (size == prevsize) + continue; + selectComposableSels(i, L_HORIZ, NULL, &selh); + selectComposableSels(i, L_VERT, NULL, &selv); + snprintf(name, L_BUF_SIZE, "sel_comb_%dh", size); + selaAddSel(sela, selh, name, 0); + snprintf(name, L_BUF_SIZE, "sel_comb_%dv", size); + selaAddSel(sela, selv, name, 0); + prevsize = size; + } + + return sela; +} + + +/* ------------------------------------------------------------------- * + * Structuring elements for the intersection of lines * + * ------------------------------------------------------------------- */ +/*! + * \brief selaAddCrossJunctions() + * + * \param[in] sela [optional] + * \param[in] hlsize length of each line of hits from origin + * \param[in] mdist distance of misses from the origin + * \param[in] norient number of orientations; max of 8 + * \param[in] debugflag 1 for debug output + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds hitmiss Sels for the intersection of two lines.
+ *          If the lines are very thin, they must be nearly orthogonal
+ *          to register.
+ *      (2) The number of Sels generated is equal to %norient.
+ *      (3) If %norient == 2, this generates 2 Sels of crosses, each with
+ *          two perpendicular lines of hits.  One Sel has horizontal and
+ *          vertical hits; the other has hits along lines at +-45 degrees.
+ *          Likewise, if %norient == 3, this generates 3 Sels of crosses
+ *          oriented at 30 degrees with each other.
+ *      (4) It is suggested that %hlsize be chosen at least 1 greater
+ *          than %mdist.  Try values of (%hlsize, %mdist) such as
+ *          (6,5), (7,6), (8,7), (9,7), etc.
+ * 
+ */ +SELA * +selaAddCrossJunctions(SELA *sela, + l_float32 hlsize, + l_float32 mdist, + l_int32 norient, + l_int32 debugflag) +{ +char name[L_BUF_SIZE]; +l_int32 i, j, w, xc, yc; +l_float64 pi, halfpi, radincr, radang; +l_float64 angle; +PIX *pixc, *pixm, *pixt; +PIXA *pixa; +PTA *pta1, *pta2, *pta3, *pta4; +SEL *sel; + + PROCNAME("selaAddCrossJunctions"); + + if (hlsize <= 0) + return (SELA *)ERROR_PTR("hlsize not > 0", procName, NULL); + if (norient < 1 || norient > 8) + return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + + pi = 3.1415926535; + halfpi = 3.1415926535 / 2.0; + radincr = halfpi / (l_float64)norient; + w = (l_int32)(2.2 * (L_MAX(hlsize, mdist) + 0.5)); + if (w % 2 == 0) + w++; + xc = w / 2; + yc = w / 2; + + pixa = pixaCreate(norient); + for (i = 0; i < norient; i++) { + + /* Set the don't cares */ + pixc = pixCreate(w, w, 32); + pixSetAll(pixc); + + /* Add the green lines of hits */ + pixm = pixCreate(w, w, 1); + radang = (l_float32)i * radincr; + pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang); + pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + halfpi); + pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi); + pta4 = generatePtaLineFromPt(xc, yc, hlsize + 1, radang + pi + halfpi); + ptaJoin(pta1, pta2, 0, -1); + ptaJoin(pta1, pta3, 0, -1); + ptaJoin(pta1, pta4, 0, -1); + pixRenderPta(pixm, pta1, L_SET_PIXELS); + pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + ptaDestroy(&pta3); + ptaDestroy(&pta4); + + /* Add red misses between the lines */ + for (j = 0; j < 4; j++) { + angle = radang + (j - 0.5) * halfpi; + pixSetPixel(pixc, xc + (l_int32)(mdist * cos(angle)), + yc + (l_int32)(mdist * sin(angle)), 0xff000000); + } + + /* Add dark green for origin */ + pixSetPixel(pixc, xc, yc, 0x00550000); + + /* Generate the sel */ + sel = selCreateFromColorPix(pixc, NULL); + snprintf(name, sizeof(name), "sel_cross_%d", i); + selaAddSel(sela, sel, name, 0); + + if (debugflag) { + pixt = pixScaleBySampling(pixc, 10.0, 10.0); + pixaAddPix(pixa, pixt, L_INSERT); + } + pixDestroy(&pixm); + pixDestroy(&pixc); + } + + if (debugflag) { + l_int32 w; + lept_mkdir("lept/sel"); + pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); + pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 1, 0, 10, 2); + pixWriteDebug("/tmp/lept/sel/xsel1.png", pixt, IFF_PNG); + pixDisplay(pixt, 0, 100); + pixDestroy(&pixt); + pixt = selaDisplayInPix(sela, 15, 2, 20, 1); + pixWriteDebug("/tmp/lept/sel/xsel2.png", pixt, IFF_PNG); + pixDisplay(pixt, 500, 100); + pixDestroy(&pixt); + selaWriteStream(stderr, sela); + } + pixaDestroy(&pixa); + + return sela; +} + + +/*! + * \brief selaAddTJunctions() + * + * \param[in] sela [optional] + * \param[in] hlsize length of each line of hits from origin + * \param[in] mdist distance of misses from the origin + * \param[in] norient number of orientations; max of 8 + * \param[in] debugflag 1 for debug output + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds hitmiss Sels for the T-junction of two lines.
+ *          If the lines are very thin, they must be nearly orthogonal
+ *          to register.
+ *      (2) The number of Sels generated is 4 * %norient.
+ *      (3) It is suggested that %hlsize be chosen at least 1 greater
+ *          than %mdist.  Try values of (%hlsize, %mdist) such as
+ *          (6,5), (7,6), (8,7), (9,7), etc.
+ * 
+ */ +SELA * +selaAddTJunctions(SELA *sela, + l_float32 hlsize, + l_float32 mdist, + l_int32 norient, + l_int32 debugflag) +{ +char name[L_BUF_SIZE]; +l_int32 i, j, k, w, xc, yc; +l_float64 pi, halfpi, radincr, jang, radang; +l_float64 angle[3], dist[3]; +PIX *pixc, *pixm, *pixt; +PIXA *pixa; +PTA *pta1, *pta2, *pta3; +SEL *sel; + + PROCNAME("selaAddTJunctions"); + + if (hlsize <= 2) + return (SELA *)ERROR_PTR("hlsizel not > 1", procName, NULL); + if (norient < 1 || norient > 8) + return (SELA *)ERROR_PTR("norient not in [1, ... 8]", procName, NULL); + + if (!sela) { + if ((sela = selaCreate(0)) == NULL) + return (SELA *)ERROR_PTR("sela not made", procName, NULL); + } + + pi = 3.1415926535; + halfpi = 3.1415926535 / 2.0; + radincr = halfpi / (l_float32)norient; + w = (l_int32)(2.4 * (L_MAX(hlsize, mdist) + 0.5)); + if (w % 2 == 0) + w++; + xc = w / 2; + yc = w / 2; + + pixa = pixaCreate(4 * norient); + for (i = 0; i < norient; i++) { + for (j = 0; j < 4; j++) { /* 4 orthogonal orientations */ + jang = (l_float32)j * halfpi; + + /* Set the don't cares */ + pixc = pixCreate(w, w, 32); + pixSetAll(pixc); + + /* Add the green lines of hits */ + pixm = pixCreate(w, w, 1); + radang = (l_float32)i * radincr; + pta1 = generatePtaLineFromPt(xc, yc, hlsize + 1, jang + radang); + pta2 = generatePtaLineFromPt(xc, yc, hlsize + 1, + jang + radang + halfpi); + pta3 = generatePtaLineFromPt(xc, yc, hlsize + 1, + jang + radang + pi); + ptaJoin(pta1, pta2, 0, -1); + ptaJoin(pta1, pta3, 0, -1); + pixRenderPta(pixm, pta1, L_SET_PIXELS); + pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); + ptaDestroy(&pta1); + ptaDestroy(&pta2); + ptaDestroy(&pta3); + + /* Add red misses between the lines */ + angle[0] = radang + jang - halfpi; + angle[1] = radang + jang + 0.5 * halfpi; + angle[2] = radang + jang + 1.5 * halfpi; + dist[0] = 0.8 * mdist; + dist[1] = dist[2] = mdist; + for (k = 0; k < 3; k++) { + pixSetPixel(pixc, xc + (l_int32)(dist[k] * cos(angle[k])), + yc + (l_int32)(dist[k] * sin(angle[k])), + 0xff000000); + } + + /* Add dark green for origin */ + pixSetPixel(pixc, xc, yc, 0x00550000); + + /* Generate the sel */ + sel = selCreateFromColorPix(pixc, NULL); + snprintf(name, sizeof(name), "sel_cross_%d", 4 * i + j); + selaAddSel(sela, sel, name, 0); + + if (debugflag) { + pixt = pixScaleBySampling(pixc, 10.0, 10.0); + pixaAddPix(pixa, pixt, L_INSERT); + } + pixDestroy(&pixm); + pixDestroy(&pixc); + } + } + + if (debugflag) { + l_int32 w; + lept_mkdir("lept/sel"); + pixaGetPixDimensions(pixa, 0, &w, NULL, NULL); + pixt = pixaDisplayTiledAndScaled(pixa, 32, w, 4, 0, 10, 2); + pixWriteDebug("/tmp/lept/sel/tsel1.png", pixt, IFF_PNG); + pixDisplay(pixt, 0, 100); + pixDestroy(&pixt); + pixt = selaDisplayInPix(sela, 15, 2, 20, 4); + pixWriteDebug("/tmp/lept/sel/tsel2.png", pixt, IFF_PNG); + pixDisplay(pixt, 500, 100); + pixDestroy(&pixt); + selaWriteStream(stderr, sela); + } + pixaDestroy(&pixa); + + return sela; +} + + +/* -------------------------------------------------------------------------- * + * Structuring elements for connectivity-preserving thinning operations * + * -------------------------------------------------------------------------- */ + + /* ------------------------------------------------------------ + * These sels (and their rotated counterparts) are the useful + * 3x3 Sels for thinning. The notation is based on + * "Connectivity-preserving morphological image transformations," + * a version of which can be found at + * http://www.leptonica.com/papers/conn.pdf + * ------------------------------------------------------------ */ + + /* Sels for 4-connected thinning */ +static const char *sel_4_1 = " x" + "oCx" + " x"; +static const char *sel_4_2 = " x" + "oCx" + " o "; +static const char *sel_4_3 = " o " + "oCx" + " x"; +static const char *sel_4_4 = " o " + "oCx" + " o "; +static const char *sel_4_5 = " ox" + "oCx" + " o "; +static const char *sel_4_6 = " o " + "oCx" + " ox"; +static const char *sel_4_7 = " xx" + "oCx" + " o "; +static const char *sel_4_8 = " x" + "oCx" + "o x"; +static const char *sel_4_9 = "o x" + "oCx" + " x"; + + /* Sels for 8-connected thinning */ +static const char *sel_8_1 = " x " + "oCx" + " x "; +static const char *sel_8_2 = " x " + "oCx" + "o "; +static const char *sel_8_3 = "o " + "oCx" + " x "; +static const char *sel_8_4 = "o " + "oCx" + "o "; +static const char *sel_8_5 = "o x" + "oCx" + "o "; +static const char *sel_8_6 = "o " + "oCx" + "o x"; +static const char *sel_8_7 = " x " + "oCx" + "oo "; +static const char *sel_8_8 = " x " + "oCx" + "ox "; +static const char *sel_8_9 = "ox " + "oCx" + " x "; + + /* Sels for both 4 and 8-connected thinning */ +static const char *sel_48_1 = " xx" + "oCx" + "oo "; +static const char *sel_48_2 = "o x" + "oCx" + "o x"; + + +/*! + * \brief sela4ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds the 9 basic sels for 4-cc thinning.
+ * 
+ */ +SELA * +sela4ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(9); + + sel = selCreateFromString(sel_4_1, 3, 3, "sel_4_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_2, 3, 3, "sel_4_2"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_3, 3, 3, "sel_4_3"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_4, 3, 3, "sel_4_4"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_5, 3, 3, "sel_4_5"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_6, 3, 3, "sel_4_6"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_7, 3, 3, "sel_4_7"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_8, 3, 3, "sel_4_8"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_4_9, 3, 3, "sel_4_9"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + + +/*! + * \brief sela8ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds the 9 basic sels for 8-cc thinning.
+ * 
+ */ +SELA * +sela8ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(9); + + sel = selCreateFromString(sel_8_1, 3, 3, "sel_8_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_2, 3, 3, "sel_8_2"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_3, 3, 3, "sel_8_3"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_4, 3, 3, "sel_8_4"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_5, 3, 3, "sel_8_5"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_6, 3, 3, "sel_8_6"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_7, 3, 3, "sel_8_7"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_8, 3, 3, "sel_8_8"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_8_9, 3, 3, "sel_8_9"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + + +/*! + * \brief sela4and8ccThin() + * + * \param[in] sela [optional] + * \return sela with additional sels, or NULL on error + * + *
+ * Notes:
+ *      (1) Adds the 2 basic sels for either 4-cc or 8-cc thinning.
+ * 
+ */ +SELA * +sela4and8ccThin(SELA *sela) +{ +SEL *sel; + + if (!sela) sela = selaCreate(2); + + sel = selCreateFromString(sel_48_1, 3, 3, "sel_48_1"); + selaAddSel(sela, sel, NULL, 0); + sel = selCreateFromString(sel_48_2, 3, 3, "sel_48_2"); + selaAddSel(sela, sel, NULL, 0); + + return sela; +} + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/selgen.c b/hgdriver/3rdparty/hgOCR/leptonica/selgen.c new file mode 100644 index 0000000..321d1ca --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/selgen.c @@ -0,0 +1,984 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file selgen.c + *
+ *
+ *      This file contains functions that generate hit-miss Sels
+ *      for doing a loose match to a small bitmap.  The hit-miss
+ *      Sel is made from a given bitmap.  Several "knobs"
+ *      are available to control the looseness of the match.
+ *      In general, a tight match will have fewer false positives
+ *      (bad matches) but more false negatives (missed patterns).
+ *      The values to be used depend on the quality and variation
+ *      of the image in which the pattern is to be searched,
+ *      and the relative penalties of false positives and
+ *      false negatives.  Default values for the three knobs --
+ *      minimum distance to boundary pixels, number of extra pixels
+ *      added to selected sides, and minimum acceptable runlength
+ *      in eroded version -- are provided.
+ *
+ *      The generated hit-miss Sels can always be used in the
+ *      rasterop implementation of binary morphology (in morph.h).
+ *      If they are small enough (not more than 31 pixels extending
+ *      in any direction from the Sel origin), they can also be used
+ *      to auto-generate dwa code (fmorphauto.c).
+ *
+ *
+ *      Generate a subsampled structuring element
+ *            SEL     *pixGenerateSelWithRuns()
+ *            SEL     *pixGenerateSelRandom()
+ *            SEL     *pixGenerateSelBoundary()
+ *
+ *      Accumulate data on runs along lines
+ *            NUMA    *pixGetRunCentersOnLine()
+ *            NUMA    *pixGetRunsOnLine()
+ *
+ *      Subsample boundary pixels in relatively ordered way
+ *            PTA     *pixSubsampleBoundaryPixels()
+ *            PTA     *adjacentOnPixelInRaster()
+ *
+ *      Display generated sel with originating image
+ *            PIX     *pixDisplayHitMissSel()
+ * 
+ */ + +#include "allheaders.h" + + + /* Default minimum distance of a hit-miss pixel element to + * a boundary pixel of its color. */ +static const l_int32 DefaultDistanceToBoundary = 1; +static const l_int32 MaxDistanceToBoundary = 4; + + /* Default min runlength to accept a hit or miss element located + * at its center */ +static const l_int32 DefaultMinRunlength = 3; + + /* Default scalefactor for displaying image and hit-miss sel + * that is derived from it */ +static const l_int32 DefaultSelScalefactor = 7; +static const l_int32 MaxSelScalefactor = 31; /* should be big enough */ + +#ifndef NO_CONSOLE_IO +#define DEBUG_DISPLAY_HM_SEL 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-----------------------------------------------------------------* + * Generate a subsampled structuring element * + *-----------------------------------------------------------------*/ +/*! + * \brief pixGenerateSelWithRuns() + * + * \param[in] pixs 1 bpp, typically small, to be used as a pattern + * \param[in] nhlines number of hor lines along which elements are found + * \param[in] nvlines number of vert lines along which elements are found + * \param[in] distance min distance from boundary pixel; use 0 for default + * \param[in] minlength min runlength to set hit or miss; use 0 for default + * \param[in] toppix number of extra pixels of bg added above + * \param[in] botpix number of extra pixels of bg added below + * \param[in] leftpix number of extra pixels of bg added to left + * \param[in] rightpix number of extra pixels of bg added to right + * \param[out] ppixe [optional] input pix expanded by extra pixels + * \return sel hit-miss for input pattern, or NULL on error + * + *
+ * Notes:
+ *    (1) The horizontal and vertical lines along which elements are
+ *        selected are roughly equally spaced.  The actual locations of
+ *        the hits and misses are the centers of respective run-lengths.
+ *    (2) No elements are selected that are less than 'distance' pixels away
+ *        from a boundary pixel of the same color.  This makes the
+ *        match much more robust to edge noise.  Valid inputs of
+ *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
+ *        greater than 4, we reset it to the default value.
+ *    (3) The 4 numbers for adding rectangles of pixels outside the fg
+ *        can be use if the pattern is expected to be surrounded by bg
+ *        (white) pixels.  On the other hand, if the pattern may be near
+ *        other fg (black) components on some sides, use 0 for those sides.
+ *    (4) The pixels added to a side allow you to have miss elements there.
+ *        There is a constraint between distance, minlength, and
+ *        the added pixels for this to work.  We illustrate using the
+ *        default values.  If you add 5 pixels to the top, and use a
+ *        distance of 1, then you end up with a vertical run of at least
+ *        4 bg pixels along the top edge of the image.  If you use a
+ *        minimum runlength of 3, each vertical line will always find
+ *        a miss near the center of its run.  However, if you use a
+ *        minimum runlength of 5, you will not get a miss on every vertical
+ *        line.  As another example, if you have 7 added pixels and a
+ *        distance of 2, you can use a runlength up to 5 to guarantee
+ *        that the miss element is recorded.  We give a warning if the
+ *        constraint does not guarantee a miss element outside the
+ *        image proper.
+ *    (5) The input pix, as extended by the extra pixels on selected sides,
+ *        can optionally be returned.  For debugging, call
+ *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
+ *        on the generating bitmap.
+ * 
+ */ +SEL * +pixGenerateSelWithRuns(PIX *pixs, + l_int32 nhlines, + l_int32 nvlines, + l_int32 distance, + l_int32 minlength, + l_int32 toppix, + l_int32 botpix, + l_int32 leftpix, + l_int32 rightpix, + PIX **ppixe) +{ +l_int32 ws, hs, w, h, x, y, xval, yval, i, j, nh, nm; +l_float32 delh, delw; +NUMA *nah, *nam; +PIX *pixt1, *pixt2, *pixfg, *pixbg; +PTA *ptah, *ptam; +SEL *seld, *sel; + + PROCNAME("pixGenerateSelWithRuns"); + + if (ppixe) *ppixe = NULL; + if (!pixs) + return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (nhlines < 1 && nvlines < 1) + return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL); + + if (distance <= 0) + distance = DefaultDistanceToBoundary; + if (minlength <= 0) + minlength = DefaultMinRunlength; + if (distance > MaxDistanceToBoundary) { + L_WARNING("distance too large; setting to max value\n", procName); + distance = MaxDistanceToBoundary; + } + + /* Locate the foreground */ + pixClipToForeground(pixs, &pixt1, NULL); + if (!pixt1) + return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); + ws = pixGetWidth(pixt1); + hs = pixGetHeight(pixt1); + w = ws; + h = hs; + + /* Crop out a region including the foreground, and add pixels + * on sides depending on the side flags */ + if (toppix || botpix || leftpix || rightpix) { + x = y = 0; + if (toppix) { + h += toppix; + y = toppix; + if (toppix < distance + minlength) + L_WARNING("no miss elements in added top pixels\n", procName); + } + if (botpix) { + h += botpix; + if (botpix < distance + minlength) + L_WARNING("no miss elements in added bot pixels\n", procName); + } + if (leftpix) { + w += leftpix; + x = leftpix; + if (leftpix < distance + minlength) + L_WARNING("no miss elements in added left pixels\n", procName); + } + if (rightpix) { + w += rightpix; + if (rightpix < distance + minlength) + L_WARNING("no miss elements in added right pixels\n", procName); + } + pixt2 = pixCreate(w, h, 1); + pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); + } else { + pixt2 = pixClone(pixt1); + } + if (ppixe) + *ppixe = pixClone(pixt2); + pixDestroy(&pixt1); + + /* Identify fg and bg pixels that are at least 'distance' pixels + * away from the boundary pixels in their set */ + seld = selCreateBrick(2 * distance + 1, 2 * distance + 1, + distance, distance, SEL_HIT); + pixfg = pixErode(NULL, pixt2, seld); + pixbg = pixDilate(NULL, pixt2, seld); + pixInvert(pixbg, pixbg); + selDestroy(&seld); + pixDestroy(&pixt2); + + /* Accumulate hit and miss points */ + ptah = ptaCreate(0); + ptam = ptaCreate(0); + if (nhlines >= 1) { + delh = (l_float32)h / (l_float32)(nhlines + 1); + for (i = 0, y = 0; i < nhlines; i++) { + y += (l_int32)(delh + 0.5); + nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength); + nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength); + nh = numaGetCount(nah); + nm = numaGetCount(nam); + for (j = 0; j < nh; j++) { + numaGetIValue(nah, j, &xval); + ptaAddPt(ptah, xval, y); + } + for (j = 0; j < nm; j++) { + numaGetIValue(nam, j, &xval); + ptaAddPt(ptam, xval, y); + } + numaDestroy(&nah); + numaDestroy(&nam); + } + } + if (nvlines >= 1) { + delw = (l_float32)w / (l_float32)(nvlines + 1); + for (i = 0, x = 0; i < nvlines; i++) { + x += (l_int32)(delw + 0.5); + nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength); + nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength); + nh = numaGetCount(nah); + nm = numaGetCount(nam); + for (j = 0; j < nh; j++) { + numaGetIValue(nah, j, &yval); + ptaAddPt(ptah, x, yval); + } + for (j = 0; j < nm; j++) { + numaGetIValue(nam, j, &yval); + ptaAddPt(ptam, x, yval); + } + numaDestroy(&nah); + numaDestroy(&nam); + } + } + + /* Make the Sel with those points */ + sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); + nh = ptaGetCount(ptah); + for (i = 0; i < nh; i++) { + ptaGetIPt(ptah, i, &x, &y); + selSetElement(sel, y, x, SEL_HIT); + } + nm = ptaGetCount(ptam); + for (i = 0; i < nm; i++) { + ptaGetIPt(ptam, i, &x, &y); + selSetElement(sel, y, x, SEL_MISS); + } + + pixDestroy(&pixfg); + pixDestroy(&pixbg); + ptaDestroy(&ptah); + ptaDestroy(&ptam); + return sel; +} + + +/*! + * \brief pixGenerateSelRandom() + * + * \param[in] pixs 1 bpp, typically small, to be used as a pattern + * \param[in] hitfract fraction of allowable fg pixels that are hits + * \param[in] missfract fraction of allowable bg pixels that are misses + * \param[in] distance min distance from boundary pixel; use 0 for default + * \param[in] toppix number of extra pixels of bg added above + * \param[in] botpix number of extra pixels of bg added below + * \param[in] leftpix number of extra pixels of bg added to left + * \param[in] rightpix number of extra pixels of bg added to right + * \param[out] ppixe [optional] input pix expanded by extra pixels + * \return sel hit-miss for input pattern, or NULL on error + * + *
+ * Notes:
+ *    (1) Either of hitfract and missfract can be zero.  If both are zero,
+ *        the sel would be empty, and NULL is returned.
+ *    (2) No elements are selected that are less than 'distance' pixels away
+ *        from a boundary pixel of the same color.  This makes the
+ *        match much more robust to edge noise.  Valid inputs of
+ *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
+ *        greater than 4, we reset it to the default value.
+ *    (3) The 4 numbers for adding rectangles of pixels outside the fg
+ *        can be use if the pattern is expected to be surrounded by bg
+ *        (white) pixels.  On the other hand, if the pattern may be near
+ *        other fg (black) components on some sides, use 0 for those sides.
+ *    (4) The input pix, as extended by the extra pixels on selected sides,
+ *        can optionally be returned.  For debugging, call
+ *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
+ *        on the generating bitmap.
+ * 
+ */ +SEL * +pixGenerateSelRandom(PIX *pixs, + l_float32 hitfract, + l_float32 missfract, + l_int32 distance, + l_int32 toppix, + l_int32 botpix, + l_int32 leftpix, + l_int32 rightpix, + PIX **ppixe) +{ +l_int32 ws, hs, w, h, x, y, i, j, thresh; +l_uint32 val; +PIX *pixt1, *pixt2, *pixfg, *pixbg; +SEL *seld, *sel; + + PROCNAME("pixGenerateSelRandom"); + + if (ppixe) *ppixe = NULL; + if (!pixs) + return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (hitfract <= 0.0 && missfract <= 0.0) + return (SEL *)ERROR_PTR("no hits or misses", procName, NULL); + if (hitfract > 1.0 || missfract > 1.0) + return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL); + + if (distance <= 0) + distance = DefaultDistanceToBoundary; + if (distance > MaxDistanceToBoundary) { + L_WARNING("distance too large; setting to max value\n", procName); + distance = MaxDistanceToBoundary; + } + + /* Locate the foreground */ + pixClipToForeground(pixs, &pixt1, NULL); + if (!pixt1) + return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); + ws = pixGetWidth(pixt1); + hs = pixGetHeight(pixt1); + w = ws; + h = hs; + + /* Crop out a region including the foreground, and add pixels + * on sides depending on the side flags */ + if (toppix || botpix || leftpix || rightpix) { + x = y = 0; + if (toppix) { + h += toppix; + y = toppix; + } + if (botpix) + h += botpix; + if (leftpix) { + w += leftpix; + x = leftpix; + } + if (rightpix) + w += rightpix; + pixt2 = pixCreate(w, h, 1); + pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); + } else { + pixt2 = pixClone(pixt1); + } + if (ppixe) + *ppixe = pixClone(pixt2); + pixDestroy(&pixt1); + + /* Identify fg and bg pixels that are at least 'distance' pixels + * away from the boundary pixels in their set */ + seld = selCreateBrick(2 * distance + 1, 2 * distance + 1, + distance, distance, SEL_HIT); + pixfg = pixErode(NULL, pixt2, seld); + pixbg = pixDilate(NULL, pixt2, seld); + pixInvert(pixbg, pixbg); + selDestroy(&seld); + pixDestroy(&pixt2); + + /* Generate the sel from a random selection of these points */ + sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); + if (hitfract > 0.0) { + thresh = (l_int32)(hitfract * (l_float64)RAND_MAX); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pixfg, j, i, &val); + if (val) { + if (rand() < thresh) + selSetElement(sel, i, j, SEL_HIT); + } + } + } + } + if (missfract > 0.0) { + thresh = (l_int32)(missfract * (l_float64)RAND_MAX); + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + pixGetPixel(pixbg, j, i, &val); + if (val) { + if (rand() < thresh) + selSetElement(sel, i, j, SEL_MISS); + } + } + } + } + + pixDestroy(&pixfg); + pixDestroy(&pixbg); + return sel; +} + + +/*! + * \brief pixGenerateSelBoundary() + * + * \param[in] pixs 1 bpp, typically small, to be used as a pattern + * \param[in] hitdist min distance from fg boundary pixel + * \param[in] missdist min distance from bg boundary pixel + * \param[in] hitskip number of boundary pixels skipped between hits + * \param[in] missskip number of boundary pixels skipped between misses + * \param[in] topflag flag for extra pixels of bg added above + * \param[in] botflag flag for extra pixels of bg added below + * \param[in] leftflag flag for extra pixels of bg added to left + * \param[in] rightflag flag for extra pixels of bg added to right + * \param[out] ppixe [optional] input pix expanded by extra pixels + * \return sel hit-miss for input pattern, or NULL on error + * + *
+ * Notes:
+ *    (1) All fg elements selected are exactly hitdist pixels away from
+ *        the nearest fg boundary pixel, and ditto for bg elements.
+ *        Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4.
+ *        For example, a hitdist of 0 puts the hits at the fg boundary.
+ *        Usually, the distances should be > 0 avoid the effect of
+ *        noise at the boundary.
+ *    (2) Set hitskip < 0 if no hits are to be used.  Ditto for missskip.
+ *        If both hitskip and missskip are < 0, the sel would be empty,
+ *        and NULL is returned.
+ *    (3) The 4 flags determine whether the sel is increased on that side
+ *        to allow bg misses to be placed all along that boundary.
+ *        The increase in sel size on that side is the minimum necessary
+ *        to allow the misses to be placed at mindist.  For text characters,
+ *        the topflag and botflag are typically set to 1, and the leftflag
+ *        and rightflag to 0.
+ *    (4) The input pix, as extended by the extra pixels on selected sides,
+ *        can optionally be returned.  For debugging, call
+ *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
+ *        on the generating bitmap.
+ *    (5) This is probably the best of the three sel generators, in the
+ *        sense that you have the most flexibility with the smallest number
+ *        of hits and misses.
+ * 
+ */ +SEL * +pixGenerateSelBoundary(PIX *pixs, + l_int32 hitdist, + l_int32 missdist, + l_int32 hitskip, + l_int32 missskip, + l_int32 topflag, + l_int32 botflag, + l_int32 leftflag, + l_int32 rightflag, + PIX **ppixe) +{ +l_int32 ws, hs, w, h, x, y, ix, iy, i, npt; +PIX *pixt1, *pixt2, *pixt3, *pixfg, *pixbg; +SEL *selh, *selm, *sel_3, *sel; +PTA *ptah, *ptam; + + PROCNAME("pixGenerateSelBoundary"); + + if (ppixe) *ppixe = NULL; + if (!pixs) + return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4) + return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL); + if (hitskip < 0 && missskip < 0) + return (SEL *)ERROR_PTR("no hits or misses", procName, NULL); + + /* Locate the foreground */ + pixClipToForeground(pixs, &pixt1, NULL); + if (!pixt1) + return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); + ws = pixGetWidth(pixt1); + hs = pixGetHeight(pixt1); + w = ws; + h = hs; + + /* Crop out a region including the foreground, and add pixels + * on sides depending on the side flags */ + if (topflag || botflag || leftflag || rightflag) { + x = y = 0; + if (topflag) { + h += missdist + 1; + y = missdist + 1; + } + if (botflag) + h += missdist + 1; + if (leftflag) { + w += missdist + 1; + x = missdist + 1; + } + if (rightflag) + w += missdist + 1; + pixt2 = pixCreate(w, h, 1); + pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); + } else { + pixt2 = pixClone(pixt1); + } + if (ppixe) + *ppixe = pixClone(pixt2); + pixDestroy(&pixt1); + + /* Identify fg and bg pixels that are exactly hitdist and + * missdist (rsp) away from the boundary pixels in their set. + * Then get a subsampled set of these points. */ + sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT); + if (hitskip >= 0) { + selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1, + hitdist, hitdist, SEL_HIT); + pixt3 = pixErode(NULL, pixt2, selh); + pixfg = pixErode(NULL, pixt3, sel_3); + pixXor(pixfg, pixfg, pixt3); + ptah = pixSubsampleBoundaryPixels(pixfg, hitskip); + pixDestroy(&pixt3); + pixDestroy(&pixfg); + selDestroy(&selh); + } + if (missskip >= 0) { + selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1, + missdist, missdist, SEL_HIT); + pixt3 = pixDilate(NULL, pixt2, selm); + pixbg = pixDilate(NULL, pixt3, sel_3); + pixXor(pixbg, pixbg, pixt3); + ptam = pixSubsampleBoundaryPixels(pixbg, missskip); + pixDestroy(&pixt3); + pixDestroy(&pixbg); + selDestroy(&selm); + } + selDestroy(&sel_3); + pixDestroy(&pixt2); + + /* Generate the hit-miss sel from these point */ + sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); + if (hitskip >= 0) { + npt = ptaGetCount(ptah); + for (i = 0; i < npt; i++) { + ptaGetIPt(ptah, i, &ix, &iy); + selSetElement(sel, iy, ix, SEL_HIT); + } + } + if (missskip >= 0) { + npt = ptaGetCount(ptam); + for (i = 0; i < npt; i++) { + ptaGetIPt(ptam, i, &ix, &iy); + selSetElement(sel, iy, ix, SEL_MISS); + } + } + + ptaDestroy(&ptah); + ptaDestroy(&ptam); + return sel; +} + + +/*-----------------------------------------------------------------* + * Accumulate data on runs along lines * + *-----------------------------------------------------------------*/ +/*! + * \brief pixGetRunCentersOnLine() + * + * \param[in] pixs 1 bpp + * \param[in] x, y set one of these to -1; see notes + * \param[in] minlength minimum length of acceptable run + * \return numa of fg runs, or NULL on error + * + *
+ * Notes:
+ *      (1) Action: this function computes the fg (black) and bg (white)
+ *          pixel runlengths along the specified horizontal or vertical line,
+ *          and returns a Numa of the "center" pixels of each fg run
+ *          whose length equals or exceeds the minimum length.
+ *      (2) This only works on horizontal and vertical lines.
+ *      (3) For horizontal runs, set x = -1 and y to the value
+ *          for all points along the raster line.  For vertical runs,
+ *          set y = -1 and x to the value for all points along the
+ *          pixel column.
+ *      (4) For horizontal runs, the points in the Numa are the x
+ *          values in the center of fg runs that are of length at
+ *          least 'minlength'.  For vertical runs, the points in the
+ *          Numa are the y values in the center of fg runs, again
+ *          of length 'minlength' or greater.
+ *      (5) If there are no fg runs along the line that satisfy the
+ *          minlength constraint, the returned Numa is empty.  This
+ *          is not an error.
+ * 
+ */ +NUMA * +pixGetRunCentersOnLine(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 minlength) +{ +l_int32 w, h, i, r, nruns, len; +NUMA *naruns, *nad; + + PROCNAME("pixGetRunCentersOnLine"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (x != -1 && y != -1) + return (NUMA *)ERROR_PTR("x or y must be -1", procName, NULL); + if (x == -1 && y == -1) + return (NUMA *)ERROR_PTR("x or y cannot both be -1", procName, NULL); + + if ((nad = numaCreate(0)) == NULL) + return (NUMA *)ERROR_PTR("nad not made", procName, NULL); + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + if (x == -1) { /* horizontal run */ + if (y < 0 || y >= h) + return nad; + naruns = pixGetRunsOnLine(pixs, 0, y, w - 1, y); + } else { /* vertical run */ + if (x < 0 || x >= w) + return nad; + naruns = pixGetRunsOnLine(pixs, x, 0, x, h - 1); + } + nruns = numaGetCount(naruns); + + /* extract run center values; the first run is always bg */ + r = 0; /* cumulative distance along line */ + for (i = 0; i < nruns; i++) { + if (i % 2 == 0) { /* bg run */ + numaGetIValue(naruns, i, &len); + r += len; + continue; + } else { + numaGetIValue(naruns, i, &len); + if (len >= minlength) + numaAddNumber(nad, r + len / 2); + r += len; + } + } + + numaDestroy(&naruns); + return nad; +} + + +/*! + * \brief pixGetRunsOnLine() + * + * \param[in] pixs 1 bpp + * \param[in] x1, y1, x2, y2 + * \return numa, or NULL on error + * + *
+ * Notes:
+ *      (1) Action: this function uses the bresenham algorithm to compute
+ *          the pixels along the specified line.  It returns a Numa of the
+ *          runlengths of the fg (black) and bg (white) runs, always
+ *          starting with a white run.
+ *      (2) If the first pixel on the line is black, the length of the
+ *          first returned run (which is white) is 0.
+ * 
+ */ +NUMA * +pixGetRunsOnLine(PIX *pixs, + l_int32 x1, + l_int32 y1, + l_int32 x2, + l_int32 y2) +{ +l_int32 w, h, x, y, npts; +l_int32 i, runlen, preval; +l_uint32 val; +NUMA *numa; +PTA *pta; + + PROCNAME("pixGetRunsOnLine"); + + if (!pixs) + return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + if (x1 < 0 || x1 >= w) + return (NUMA *)ERROR_PTR("x1 not valid", procName, NULL); + if (x2 < 0 || x2 >= w) + return (NUMA *)ERROR_PTR("x2 not valid", procName, NULL); + if (y1 < 0 || y1 >= h) + return (NUMA *)ERROR_PTR("y1 not valid", procName, NULL); + if (y2 < 0 || y2 >= h) + return (NUMA *)ERROR_PTR("y2 not valid", procName, NULL); + + if ((pta = generatePtaLine(x1, y1, x2, y2)) == NULL) + return (NUMA *)ERROR_PTR("pta not made", procName, NULL); + if ((npts = ptaGetCount(pta)) == 0) { + ptaDestroy(&pta); + return (NUMA *)ERROR_PTR("pta has no pts", procName, NULL); + } + if ((numa = numaCreate(0)) == NULL) { + ptaDestroy(&pta); + return (NUMA *)ERROR_PTR("numa not made", procName, NULL); + } + + for (i = 0; i < npts; i++) { + ptaGetIPt(pta, i, &x, &y); + pixGetPixel(pixs, x, y, &val); + if (i == 0) { + if (val == 1) { /* black pixel; append white run of size 0 */ + numaAddNumber(numa, 0); + } + preval = val; + runlen = 1; + continue; + } + if (val == preval) { /* extend current run */ + preval = val; + runlen++; + } else { /* end previous run */ + numaAddNumber(numa, runlen); + preval = val; + runlen = 1; + } + } + numaAddNumber(numa, runlen); /* append last run */ + + ptaDestroy(&pta); + return numa; +} + + +/*-----------------------------------------------------------------* + * Subsample boundary pixels in relatively ordered way * + *-----------------------------------------------------------------*/ +/*! + * \brief pixSubsampleBoundaryPixels() + * + * \param[in] pixs 1 bpp, with only boundary pixels in fg + * \param[in] skip number to skip between samples as you traverse boundary + * \return pta, or NULL on error + * + *
+ * Notes:
+ *      (1) If skip = 0, we take all the fg pixels.
+ *      (2) We try to traverse the boundaries in a regular way.
+ *          Some pixels may be missed, and these are then subsampled
+ *          randomly with a fraction determined by 'skip'.
+ *      (3) The most natural approach is to use a depth first (stack-based)
+ *          method to find the fg pixels.  However, the pixel runs are
+ *          4-connected and there are relatively few branches.  So
+ *          instead of doing a proper depth-first search, we get nearly
+ *          the same result using two nested while loops: the outer
+ *          one continues a raster-based search for the next fg pixel,
+ *          and the inner one does a reasonable job running along
+ *          each 4-connected coutour.
+ * 
+ */ +PTA * +pixSubsampleBoundaryPixels(PIX *pixs, + l_int32 skip) +{ +l_int32 x, y, xn, yn, xs, ys, xa, ya, count; +PIX *pixt; +PTA *pta; + + PROCNAME("pixSubsampleBoundaryPixels"); + + if (!pixs) + return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (skip < 0) + return (PTA *)ERROR_PTR("skip < 0", procName, NULL); + + if (skip == 0) + return ptaGetPixelsFromPix(pixs, NULL); + + pta = ptaCreate(0); + pixt = pixCopy(NULL, pixs); + xs = ys = 0; + while (nextOnPixelInRaster(pixt, xs, ys, &xn, &yn)) { /* new series */ + xs = xn; + ys = yn; + + /* Add first point in this series */ + ptaAddPt(pta, xs, ys); + + /* Trace out boundary, erasing all and saving every (skip + 1)th */ + x = xs; + y = ys; + pixSetPixel(pixt, x, y, 0); + count = 0; + while (adjacentOnPixelInRaster(pixt, x, y, &xa, &ya)) { + x = xa; + y = ya; + pixSetPixel(pixt, x, y, 0); + if (count == skip) { + ptaAddPt(pta, x, y); + count = 0; + } else { + count++; + } + } + } + + pixDestroy(&pixt); + return pta; +} + + +/*! + * \brief adjacentOnPixelInRaster() + * + * \param[in] pixs 1 bpp + * \param[in] x, y current pixel + * \param[out] pxa, pya adjacent ON pixel, found by simple CCW search + * \return 1 if a pixel is found; 0 otherwise or on error + * + *
+ * Notes:
+ *      (1) Search is in 4-connected directions first; then on diagonals.
+ *          This allows traversal along a 4-connected boundary.
+ * 
+ */ +l_int32 +adjacentOnPixelInRaster(PIX *pixs, + l_int32 x, + l_int32 y, + l_int32 *pxa, + l_int32 *pya) +{ +l_int32 w, h, i, xa, ya, found; +l_int32 xdel[] = {-1, 0, 1, 0, -1, 1, 1, -1}; +l_int32 ydel[] = {0, 1, 0, -1, 1, 1, -1, -1}; +l_uint32 val; + + PROCNAME("adjacentOnPixelInRaster"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 0); + if (pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not 1 bpp", procName, 0); + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + found = 0; + for (i = 0; i < 8; i++) { + xa = x + xdel[i]; + ya = y + ydel[i]; + if (xa < 0 || xa >= w || ya < 0 || ya >= h) + continue; + pixGetPixel(pixs, xa, ya, &val); + if (val == 1) { + found = 1; + *pxa = xa; + *pya = ya; + break; + } + } + return found; +} + + + +/*-----------------------------------------------------------------* + * Display generated sel with originating image * + *-----------------------------------------------------------------*/ +/*! + * \brief pixDisplayHitMissSel() + * + * \param[in] pixs 1 bpp + * \param[in] sel hit-miss in general + * \param[in] scalefactor an integer >= 1; use 0 for default + * \param[in] hitcolor RGB0 color for center of hit pixels + * \param[in] misscolor RGB0 color for center of miss pixels + * \return pixd RGB showing both pixs and sel, or NULL on error + *
+ * Notes:
+ *    (1) We don't allow scalefactor to be larger than MaxSelScalefactor
+ *    (2) The colors are conveniently given as 4 bytes in hex format,
+ *        such as 0xff008800.  The least significant byte is ignored.
+ * 
+ */ +PIX * +pixDisplayHitMissSel(PIX *pixs, + SEL *sel, + l_int32 scalefactor, + l_uint32 hitcolor, + l_uint32 misscolor) +{ +l_int32 i, j, type; +l_float32 fscale; +PIX *pixt, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixDisplayHitMissSel"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (pixGetDepth(pixs) != 1) + return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); + if (!sel) + return (PIX *)ERROR_PTR("sel not defined", procName, NULL); + + if (scalefactor <= 0) + scalefactor = DefaultSelScalefactor; + if (scalefactor > MaxSelScalefactor) { + L_WARNING("scalefactor too large; using max value\n", procName); + scalefactor = MaxSelScalefactor; + } + + /* Generate a version of pixs with a colormap */ + pixt = pixConvert1To8(NULL, pixs, 0, 1); + cmap = pixcmapCreate(8); + pixcmapAddColor(cmap, 255, 255, 255); + pixcmapAddColor(cmap, 0, 0, 0); + pixcmapAddColor(cmap, hitcolor >> 24, (hitcolor >> 16) & 0xff, + (hitcolor >> 8) & 0xff); + pixcmapAddColor(cmap, misscolor >> 24, (misscolor >> 16) & 0xff, + (misscolor >> 8) & 0xff); + pixSetColormap(pixt, cmap); + + /* Color the hits and misses */ + for (i = 0; i < sel->sy; i++) { + for (j = 0; j < sel->sx; j++) { + selGetElement(sel, i, j, &type); + if (type == SEL_DONT_CARE) + continue; + if (type == SEL_HIT) + pixSetPixel(pixt, j, i, 2); + else /* type == SEL_MISS */ + pixSetPixel(pixt, j, i, 3); + } + } + + /* Scale it up */ + fscale = (l_float32)scalefactor; + pixd = pixScaleBySampling(pixt, fscale, fscale); + + pixDestroy(&pixt); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/shear.c b/hgdriver/3rdparty/hgOCR/leptonica/shear.c new file mode 100644 index 0000000..a9c0000 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/shear.c @@ -0,0 +1,850 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file shear.c + *
+ *
+ *    About arbitrary lines
+ *           PIX      *pixHShear()
+ *           PIX      *pixVShear()
+ *
+ *    About special 'points': UL corner and center
+ *           PIX      *pixHShearCorner()
+ *           PIX      *pixVShearCorner()
+ *           PIX      *pixHShearCenter()
+ *           PIX      *pixVShearCenter()
+ *
+ *    In place about arbitrary lines
+ *           l_int32   pixHShearIP()
+ *           l_int32   pixVShearIP()
+ *
+ *    Linear interpolated shear about arbitrary lines
+ *           PIX      *pixHShearLI()
+ *           PIX      *pixVShearLI()
+ *
+ *    Static helper
+ *      static l_float32  normalizeAngleForShear()
+ * 
+ */ + +#include +#include +#include "allheaders.h" + + /* Shear angle must not get too close to -pi/2 or pi/2 */ +static const l_float32 MinDiffFromHalfPi = 0.04; + +static l_float32 normalizeAngleForShear(l_float32 radang, l_float32 mindif); + + +#ifndef NO_CONSOLE_IO +#define DEBUG 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * About arbitrary lines * + *-------------------------------------------------------------*/ +/*! + * \brief pixHShear() + * + * \param[in] pixd [optional] this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs any depth; cmap ok + * \param[in] yloc location of horizontal line, measured from origin + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, always + * + *
+ * Notes:
+ *      (1) There are 3 cases:
+ *            (a) pixd == null (make a new pixd)
+ *            (b) pixd == pixs (in-place)
+ *            (c) pixd != pixs
+ *      (2) For these three cases, use these patterns, respectively:
+ *              pixd = pixHShear(NULL, pixs, ...);
+ *              pixHShear(pixs, pixs, ...);
+ *              pixHShear(pixd, pixs, ...);
+ *      (3) This shear leaves the horizontal line of pixels at y = yloc
+ *          invariant.  For a positive shear angle, pixels above this
+ *          line are shoved to the right, and pixels below this line
+ *          move to the left.
+ *      (4) With positive shear angle, this can be used, along with
+ *          pixVShear(), to perform a cw rotation, either with 2 shears
+ *          (for small angles) or in the general case with 3 shears.
+ *      (5) Changing the value of yloc is equivalent to translating
+ *          the result horizontally.
+ *      (6) This brings in %incolor pixels from outside the image.
+ *      (7) In-place shears do not work on cmapped pix, because the
+ *          in-place operation cannot initialize to the requested %incolor,
+ *          so we shear from a copy.
+ *      (8) The angle is brought into the range [-pi, -pi].  It is
+ *          not permitted to be within MinDiffFromHalfPi radians
+ *          from either -pi/2 or pi/2.
+ * 
+ */ +PIX * +pixHShear(PIX *pixd, + PIX *pixs, + l_int32 yloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 sign, w, h; +l_int32 y, yincr, inityincr, hshift; +l_float32 tanangle, invangle; + + PROCNAME("pixHShear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", procName, pixd); + + if (pixd == pixs) { /* in place */ + if (!pixGetColormap(pixs)) { + pixHShearIP(pixd, yloc, radang, incolor); + } else { /* can't do in-place with a colormap */ + PIX *pix1 = pixCopy(NULL, pixs); + pixHShear(pixd, pix1, yloc, radang, incolor); + pixDestroy(&pix1); + } + return pixd; + } + + /* Make sure pixd exists and is same size as pixs */ + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } else { /* pixd != pixs */ + pixResizeImageData(pixd, pixs); + } + + /* Normalize angle. If no rotation, return a copy */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) + return pixCopy(pixd, pixs); + + /* Initialize to value of incoming pixels */ + pixSetBlackOrWhite(pixd, incolor); + + pixGetDimensions(pixs, &w, &h, NULL); + sign = L_SIGN(radang); + tanangle = tan(radang); + invangle = L_ABS(1. / tanangle); + inityincr = (l_int32)(invangle / 2.); + yincr = (l_int32)invangle; + pixRasterop(pixd, 0, yloc - inityincr, w, 2 * inityincr, PIX_SRC, + pixs, 0, yloc - inityincr); + + for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { + yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); + if (h - y < yincr) /* reduce for last one if req'd */ + yincr = h - y; + pixRasterop(pixd, -sign*hshift, y, w, yincr, PIX_SRC, pixs, 0, y); +#if DEBUG + fprintf(stderr, "y = %d, hshift = %d, yincr = %d\n", y, hshift, yincr); +#endif /* DEBUG */ + y += yincr; + } + + for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { + yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); + if (y < yincr) /* reduce for last one if req'd */ + yincr = y; + pixRasterop(pixd, -sign*hshift, y - yincr, w, yincr, PIX_SRC, + pixs, 0, y - yincr); +#if DEBUG + fprintf(stderr, "y = %d, hshift = %d, yincr = %d\n", + y - yincr, hshift, yincr); +#endif /* DEBUG */ + y -= yincr; + } + + return pixd; +} + + +/*! + * \brief pixVShear() + * + * \param[in] pixd [optional], this can be null, equal to pixs, + * or different from pixs + * \param[in] pixs any depth; cmap ok + * \param[in] xloc location of vertical line, measured from origin + * \param[in] radang angle in radians; not too close to +-(pi / 2) + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error + * + *
+ * Notes:
+ *      (1) There are 3 cases:
+ *            (a) pixd == null (make a new pixd)
+ *            (b) pixd == pixs (in-place)
+ *            (c) pixd != pixs
+ *      (2) For these three cases, use these patterns, respectively:
+ *              pixd = pixVShear(NULL, pixs, ...);
+ *              pixVShear(pixs, pixs, ...);
+ *              pixVShear(pixd, pixs, ...);
+ *      (3) This shear leaves the vertical line of pixels at x = xloc
+ *          invariant.  For a positive shear angle, pixels to the right
+ *          of this line are shoved downward, and pixels to the left
+ *          of the line move upward.
+ *      (4) With positive shear angle, this can be used, along with
+ *          pixHShear(), to perform a cw rotation, either with 2 shears
+ *          (for small angles) or in the general case with 3 shears.
+ *      (5) Changing the value of xloc is equivalent to translating
+ *          the result vertically.
+ *      (6) This brings in %incolor pixels from outside the image.
+ *      (7) In-place shears do not work on cmapped pix, because the
+ *          in-place operation cannot initialize to the requested %incolor,
+ *          so we shear from a copy.
+ *      (8) The angle is brought into the range [-pi, -pi].  It is
+ *          not permitted to be within MinDiffFromHalfPi radians
+ *          from either -pi/2 or pi/2.
+ * 
+ */ +PIX * +pixVShear(PIX *pixd, + PIX *pixs, + l_int32 xloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 sign, w, h; +l_int32 x, xincr, initxincr, vshift; +l_float32 tanangle, invangle; + + PROCNAME("pixVShear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + + if (pixd == pixs) { /* in place */ + if (!pixGetColormap(pixs)) { + pixVShearIP(pixd, xloc, radang, incolor); + } else { /* can't do in-place with a colormap */ + PIX *pix1 = pixCopy(NULL, pixs); + pixVShear(pixd, pix1, xloc, radang, incolor); + pixDestroy(&pix1); + } + return pixd; + } + + /* Make sure pixd exists and is same size as pixs */ + if (!pixd) { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", procName, NULL); + } else { /* pixd != pixs */ + pixResizeImageData(pixd, pixs); + } + + /* Normalize angle. If no rotation, return a copy */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) + return pixCopy(pixd, pixs); + + /* Initialize to value of incoming pixels */ + pixSetBlackOrWhite(pixd, incolor); + + pixGetDimensions(pixs, &w, &h, NULL); + sign = L_SIGN(radang); + tanangle = tan(radang); + invangle = L_ABS(1. / tanangle); + initxincr = (l_int32)(invangle / 2.); + xincr = (l_int32)invangle; + pixRasterop(pixd, xloc - initxincr, 0, 2 * initxincr, h, PIX_SRC, + pixs, xloc - initxincr, 0); + + for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { + xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); + if (w - x < xincr) /* reduce for last one if req'd */ + xincr = w - x; + pixRasterop(pixd, x, sign*vshift, xincr, h, PIX_SRC, pixs, x, 0); +#if DEBUG + fprintf(stderr, "x = %d, vshift = %d, xincr = %d\n", x, vshift, xincr); +#endif /* DEBUG */ + x += xincr; + } + + for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { + xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); + if (x < xincr) /* reduce for last one if req'd */ + xincr = x; + pixRasterop(pixd, x - xincr, sign*vshift, xincr, h, PIX_SRC, + pixs, x - xincr, 0); +#if DEBUG + fprintf(stderr, "x = %d, vshift = %d, xincr = %d\n", + x - xincr, vshift, xincr); +#endif /* DEBUG */ + x -= xincr; + } + + return pixd; +} + + + +/*-------------------------------------------------------------* + * Shears about UL corner and center * + *-------------------------------------------------------------*/ +/*! + * \brief pixHShearCorner() + * + * \param[in] pixd [optional], if not null, must be equal to pixs + * \param[in] pixs any depth + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) See pixHShear() for usage.
+ *      (2) This does a horizontal shear about the UL corner, with (+) shear
+ *          pushing increasingly leftward (-x) with increasing y.
+ * 
+ */ +PIX * +pixHShearCorner(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + PROCNAME("pixHShearCorner"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + + return pixHShear(pixd, pixs, 0, radang, incolor); +} + + +/*! + * \brief pixVShearCorner() + * + * \param[in] pixd [optional], if not null, must be equal to pixs + * \param[in] pixs any depth + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) See pixVShear() for usage.
+ *      (2) This does a vertical shear about the UL corner, with (+) shear
+ *          pushing increasingly downward (+y) with increasing x.
+ * 
+ */ +PIX * +pixVShearCorner(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + PROCNAME("pixVShearCorner"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + + return pixVShear(pixd, pixs, 0, radang, incolor); +} + + +/*! + * \brief pixHShearCenter() + * + * \param[in] pixd [optional] if not null, must be equal to pixs + * \param[in] pixs any depth + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) See pixHShear() for usage.
+ *      (2) This does a horizontal shear about the center, with (+) shear
+ *          pushing increasingly leftward (-x) with increasing y.
+ * 
+ */ +PIX * +pixHShearCenter(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + PROCNAME("pixHShearCenter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + + return pixHShear(pixd, pixs, pixGetHeight(pixs) / 2, radang, incolor); +} + + +/*! + * \brief pixVShearCenter() + * + * \param[in] pixd [optional] if not null, must be equal to pixs + * \param[in] pixs any depth + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd, or NULL on error. + * + *
+ * Notes:
+ *      (1) See pixVShear() for usage.
+ *      (2) This does a vertical shear about the center, with (+) shear
+ *          pushing increasingly downward (+y) with increasing x.
+ * 
+ */ +PIX * +pixVShearCenter(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + PROCNAME("pixVShearCenter"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); + + return pixVShear(pixd, pixs, pixGetWidth(pixs) / 2, radang, incolor); +} + + + +/*--------------------------------------------------------------------------* + * In place about arbitrary lines * + *--------------------------------------------------------------------------*/ +/*! + * \brief pixHShearIP() + * + * \param[in] pixs any depth; no cmap + * \param[in] yloc location of horizontal line, measured from origin + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place version of pixHShear(); see comments there.
+ *      (2) This brings in 'incolor' pixels from outside the image.
+ *      (3) pixs cannot be colormapped, because the in-place operation
+ *          only blits in 0 or 1 bits, not an arbitrary colormap index.
+ *      (4) Does a horizontal full-band shear about the line with (+) shear
+ *          pushing increasingly leftward (-x) with increasing y.
+ * 
+ */ +l_ok +pixHShearIP(PIX *pixs, + l_int32 yloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 sign, w, h; +l_int32 y, yincr, inityincr, hshift; +l_float32 tanangle, invangle; + + PROCNAME("pixHShearIP"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid incolor value", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + + /* Normalize angle */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) + return 0; + + sign = L_SIGN(radang); + pixGetDimensions(pixs, &w, &h, NULL); + tanangle = tan(radang); + invangle = L_ABS(1. / tanangle); + inityincr = (l_int32)(invangle / 2.); + yincr = (l_int32)invangle; + + if (inityincr > 0) + pixRasteropHip(pixs, yloc - inityincr, 2 * inityincr, 0, incolor); + + for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { + yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); + if (yincr == 0) continue; + if (h - y < yincr) /* reduce for last one if req'd */ + yincr = h - y; + pixRasteropHip(pixs, y, yincr, -sign*hshift, incolor); + y += yincr; + } + + for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { + yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); + if (yincr == 0) continue; + if (y < yincr) /* reduce for last one if req'd */ + yincr = y; + pixRasteropHip(pixs, y - yincr, yincr, -sign*hshift, incolor); + y -= yincr; + } + + return 0; +} + + +/*! + * \brief pixVShearIP() + * + * \param[in] pixs any depth; no cmap + * \param[in] xloc location of vertical line, measured from origin + * \param[in] radang angle in radians + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is an in-place version of pixVShear(); see comments there.
+ *      (2) This brings in 'incolor' pixels from outside the image.
+ *      (3) pixs cannot be colormapped, because the in-place operation
+ *          only blits in 0 or 1 bits, not an arbitrary colormap index.
+ *      (4) Does a vertical full-band shear about the line with (+) shear
+ *          pushing increasingly downward (+y) with increasing x.
+ * 
+ */ +l_ok +pixVShearIP(PIX *pixs, + l_int32 xloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 sign, w, h; +l_int32 x, xincr, initxincr, vshift; +l_float32 tanangle, invangle; + + PROCNAME("pixVShearIP"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid incolor value", procName, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", procName, 1); + + /* Normalize angle */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) + return 0; + + sign = L_SIGN(radang); + pixGetDimensions(pixs, &w, &h, NULL); + tanangle = tan(radang); + invangle = L_ABS(1. / tanangle); + initxincr = (l_int32)(invangle / 2.); + xincr = (l_int32)invangle; + + if (initxincr > 0) + pixRasteropVip(pixs, xloc - initxincr, 2 * initxincr, 0, incolor); + + for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { + xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); + if (xincr == 0) continue; + if (w - x < xincr) /* reduce for last one if req'd */ + xincr = w - x; + pixRasteropVip(pixs, x, xincr, sign*vshift, incolor); + x += xincr; + } + + for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { + xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); + if (xincr == 0) continue; + if (x < xincr) /* reduce for last one if req'd */ + xincr = x; + pixRasteropVip(pixs, x - xincr, xincr, sign*vshift, incolor); + x -= xincr; + } + + return 0; +} + + +/*-------------------------------------------------------------------------* + * Linear interpolated shear about arbitrary lines * + *-------------------------------------------------------------------------*/ +/*! + * \brief pixHShearLI() + * + * \param[in] pixs 8 bpp or 32 bpp, or colormapped + * \param[in] yloc location of horizontal line, measured from origin + * \param[in] radang angle in radians, in range (-pi/2 ... pi/2) + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd sheared, or NULL on error + * + *
+ * Notes:
+ *      (1) This does horizontal shear with linear interpolation for
+ *          accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
+ *          It is relatively slow compared to the sampled version
+ *          implemented by rasterop, but the result is much smoother.
+ *      (2) This shear leaves the horizontal line of pixels at y = yloc
+ *          invariant.  For a positive shear angle, pixels above this
+ *          line are shoved to the right, and pixels below this line
+ *          move to the left.
+ *      (3) Any colormap is removed.
+ *      (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
+ *          where del == MinDiffFromHalfPi.
+ * 
+ */ +PIX * +pixHShearLI(PIX *pixs, + l_int32 yloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 i, jd, x, xp, xf, w, h, d, wm, wpls, wpld, val, rval, gval, bval; +l_uint32 word0, word1; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 tanangle, xshift; +PIX *pix, *pixd; + + PROCNAME("pixHShearLI"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + if (yloc < 0 || yloc >= h) + return (PIX *)ERROR_PTR("yloc not in [0 ... h-1]", procName, NULL); + + if (pixGetColormap(pixs)) + pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix = pixClone(pixs); + + /* Normalize angle. If no rotation, return a copy */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) { + pixDestroy(&pix); + return pixCopy(NULL, pixs); + } + + /* Initialize to value of incoming pixels */ + pixd = pixCreateTemplate(pix); + pixSetBlackOrWhite(pixd, incolor); + + /* Standard linear interp: subdivide each pixel into 64 parts */ + d = pixGetDepth(pixd); /* 8 or 32 */ + datas = pixGetData(pix); + datad = pixGetData(pixd); + wpls = pixGetWpl(pix); + wpld = pixGetWpl(pixd); + tanangle = tan(radang); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + xshift = (yloc - i) * tanangle; + for (jd = 0; jd < w; jd++) { + x = (l_int32)(64.0 * (-xshift + jd) + 0.5); + xp = x / 64; + xf = x & 63; + wm = w - 1; + if (xp < 0 || xp > wm) continue; + if (d == 8) { + if (xp < wm) { + val = ((63 - xf) * GET_DATA_BYTE(lines, xp) + + xf * GET_DATA_BYTE(lines, xp + 1) + 31) / 63; + } else { /* xp == wm */ + val = GET_DATA_BYTE(lines, xp); + } + SET_DATA_BYTE(lined, jd, val); + } else { /* d == 32 */ + if (xp < wm) { + word0 = *(lines + xp); + word1 = *(lines + xp + 1); + rval = ((63 - xf) * ((word0 >> L_RED_SHIFT) & 0xff) + + xf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; + gval = ((63 - xf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + + xf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; + bval = ((63 - xf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + + xf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; + composeRGBPixel(rval, gval, bval, lined + jd); + } else { /* xp == wm */ + lined[jd] = lines[xp]; + } + } + } + } + + pixDestroy(&pix); + return pixd; +} + + +/*! + * \brief pixVShearLI() + * + * \param[in] pixs 8 bpp or 32 bpp, or colormapped + * \param[in] xloc location of vertical line, measured from origin + * \param[in] radang angle in radians, in range (-pi/2 ... pi/2) + * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; + * \return pixd sheared, or NULL on error + * + *
+ * Notes:
+ *      (1) This does vertical shear with linear interpolation for
+ *          accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
+ *          It is relatively slow compared to the sampled version
+ *          implemented by rasterop, but the result is much smoother.
+ *      (2) This shear leaves the vertical line of pixels at x = xloc
+ *          invariant.  For a positive shear angle, pixels to the right
+ *          of this line are shoved downward, and pixels to the left
+ *          of the line move upward.
+ *      (3) Any colormap is removed.
+ *      (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
+ *          where del == MinDiffFromHalfPi.
+ * 
+ */ +PIX * +pixVShearLI(PIX *pixs, + l_int32 xloc, + l_float32 radang, + l_int32 incolor) +{ +l_int32 id, y, yp, yf, j, w, h, d, hm, wpls, wpld, val, rval, gval, bval; +l_uint32 word0, word1; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 tanangle, yshift; +PIX *pix, *pixd; + + PROCNAME("pixVShearLI"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); + if (xloc < 0 || xloc >= w) + return (PIX *)ERROR_PTR("xloc not in [0 ... w-1]", procName, NULL); + + if (pixGetColormap(pixs)) + pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix = pixClone(pixs); + + /* Normalize angle. If no rotation, return a copy */ + radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); + if (radang == 0.0 || tan(radang) == 0.0) { + pixDestroy(&pix); + return pixCopy(NULL, pixs); + } + + /* Initialize to value of incoming pixels */ + pixd = pixCreateTemplate(pix); + pixSetBlackOrWhite(pixd, incolor); + + /* Standard linear interp: subdivide each pixel into 64 parts */ + d = pixGetDepth(pixd); /* 8 or 32 */ + datas = pixGetData(pix); + datad = pixGetData(pixd); + wpls = pixGetWpl(pix); + wpld = pixGetWpl(pixd); + tanangle = tan(radang); + for (j = 0; j < w; j++) { + yshift = (j - xloc) * tanangle; + for (id = 0; id < h; id++) { + y = (l_int32)(64.0 * (-yshift + id) + 0.5); + yp = y / 64; + yf = y & 63; + hm = h - 1; + if (yp < 0 || yp > hm) continue; + lines = datas + yp * wpls; + lined = datad + id * wpld; + if (d == 8) { + if (yp < hm) { + val = ((63 - yf) * GET_DATA_BYTE(lines, j) + + yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63; + } else { /* yp == hm */ + val = GET_DATA_BYTE(lines, j); + } + SET_DATA_BYTE(lined, j, val); + } else { /* d == 32 */ + if (yp < hm) { + word0 = *(lines + j); + word1 = *(lines + wpls + j); + rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) + + yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; + gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + + yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; + bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + + yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; + composeRGBPixel(rval, gval, bval, lined + j); + } else { /* yp == hm */ + lined[j] = lines[j]; + } + } + } + } + + pixDestroy(&pix); + return pixd; +} + + +/*-------------------------------------------------------------------------* + * Angle normalization * + *-------------------------------------------------------------------------*/ +static l_float32 +normalizeAngleForShear(l_float32 radang, + l_float32 mindif) +{ +l_float32 pi2; + + PROCNAME("normalizeAngleForShear"); + + /* Bring angle into range [-pi/2, pi/2] */ + pi2 = 3.14159265 / 2.0; + if (radang < -pi2 || radang > pi2) + radang = radang - (l_int32)(radang / pi2) * pi2; + + /* If angle is too close to pi/2 or -pi/2, move it */ + if (radang > pi2 - mindif) { + L_WARNING("angle close to pi/2; shifting away\n", procName); + radang = pi2 - mindif; + } else if (radang < -pi2 + mindif) { + L_WARNING("angle close to -pi/2; shifting away\n", procName); + radang = -pi2 + mindif; + } + + return radang; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/skew.c b/hgdriver/3rdparty/hgOCR/leptonica/skew.c new file mode 100644 index 0000000..df8a70a --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/skew.c @@ -0,0 +1,1243 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file skew.c + *
+ *
+ *      Top-level deskew interfaces
+ *          PIX       *pixDeskewBoth()
+ *          PIX       *pixDeskew()
+ *          PIX       *pixFindSkewAndDeskew()
+ *          PIX       *pixDeskewGeneral()
+ *
+ *      Top-level angle-finding interface
+ *          l_int32    pixFindSkew()
+ *
+ *      Basic angle-finding functions
+ *          l_int32    pixFindSkewSweep()
+ *          l_int32    pixFindSkewSweepAndSearch()
+ *          l_int32    pixFindSkewSweepAndSearchScore()
+ *          l_int32    pixFindSkewSweepAndSearchScorePivot()
+ *
+ *      Search over arbitrary range of angles in orthogonal directions
+ *          l_int32    pixFindSkewOrthogonalRange()
+ *
+ *      Differential square sum function for scoring
+ *          l_int32    pixFindDifferentialSquareSum()
+ *
+ *      Measures of variance of row sums
+ *          l_int32    pixFindNormalizedSquareSum()
+ *
+ *
+ *      ==============================================================
+ *      Page skew detection
+ *
+ *      Skew is determined by pixel profiles, which are computed
+ *      as pixel sums along the raster line for each line in the
+ *      image.  By vertically shearing the image by a given angle,
+ *      the sums can be computed quickly along the raster lines
+ *      rather than along lines at that angle.  The score is
+ *      computed from these line sums by taking the square of
+ *      the DIFFERENCE between adjacent line sums, summed over
+ *      all lines.  The skew angle is then found as the angle
+ *      that maximizes the score.  The actual computation for
+ *      any sheared image is done in the function
+ *      pixFindDifferentialSquareSum().
+ *
+ *      The search for the angle that maximizes this score is
+ *      most efficiently performed by first sweeping coarsely
+ *      over angles, using a significantly reduced image (say, 4x
+ *      reduction), to find the approximate maximum within a half
+ *      degree or so, and then doing an interval-halving binary
+ *      search at higher resolution to get the skew angle to
+ *      within 1/20 degree or better.
+ *
+ *      The differential signal is used (rather than just using
+ *      that variance of line sums) because it rejects the
+ *      background noise due to total number of black pixels,
+ *      and has maximum contributions from the baselines and
+ *      x-height lines of text when the textlines are aligned
+ *      with the raster lines.  It also works well in multicolumn
+ *      pages where the textlines do not line up across columns.
+ *
+ *      The method is fast, accurate to within an angle (in radians)
+ *      of approximately the inverse width in pixels of the image,
+ *      and will work on a surprisingly small amount of text data
+ *      (just a couple of text lines).  Consequently, it can
+ *      also be used to find local skew if the skew were to vary
+ *      significantly over the page.  Local skew determination
+ *      is not very important except for locating lines of
+ *      handwritten text that may be mixed with printed text.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Default sweep angle parameters for pixFindSkew() */ +static const l_float32 DefaultSweepRange = 7.0; /* degrees */ +static const l_float32 DefaultSweepDelta = 1.0; /* degrees */ + + /* Default final angle difference parameter for binary + * search in pixFindSkew(). The expected accuracy is + * not better than the inverse image width in pixels, + * say, 1/2000 radians, or about 0.03 degrees. */ +static const l_float32 DefaultMinbsDelta = 0.01; /* degrees */ + + /* Default scale factors for pixFindSkew() */ +static const l_int32 DefaultSweepReduction = 4; /* sweep part; 4 is good */ +static const l_int32 DefaultBsReduction = 2; /* binary search part */ + + /* Minimum angle for deskewing in pixDeskew() */ +static const l_float32 MinDeskewAngle = 0.1; /* degree */ + + /* Minimum allowed confidence (ratio) for deskewing in pixDeskew() */ +static const l_float32 MinAllowedConfidence = 3.0; + + /* Minimum allowed maxscore to give nonzero confidence */ +static const l_int32 MinValidMaxscore = 10000; + + /* Constant setting threshold for minimum allowed minscore + * to give nonzero confidence; multiply this constant by + * (height * width^2) */ +static const l_float32 MinscoreThreshFactor = 0.000002; + + /* Default binarization threshold value */ +static const l_int32 DefaultBinaryThreshold = 130; + +#ifndef NO_CONSOLE_IO +#define DEBUG_PRINT_SCORES 0 +#define DEBUG_PRINT_SWEEP 0 +#define DEBUG_PRINT_BINARY 0 +#define DEBUG_PRINT_ORTH 0 +#define DEBUG_THRESHOLD 0 +#define DEBUG_PLOT_SCORES 0 /* requires the gnuplot executable */ +#endif /* ~NO_CONSOLE_IO */ + + + +/*-----------------------------------------------------------------------* + * Top-level deskew interfaces * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixDeskewBoth() + * + * \param[in] pixs any depth + * \param[in] redsearch for binary search: reduction factor = 1, 2 or 4; + * use 0 for default + * \return pixd deskewed pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This binarizes if necessary and does both horizontal
+ *          and vertical deskewing, using the default parameters in
+ *          the underlying pixDeskew().  See usage there.
+ *      (2) This may return a clone.
+ * 
+ */ +PIX * +pixDeskewBoth(PIX *pixs, + l_int32 redsearch) +{ +PIX *pix1, *pix2, *pix3, *pix4; + + PROCNAME("pixDeskewBoth"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (redsearch == 0) + redsearch = DefaultBsReduction; + else if (redsearch != 1 && redsearch != 2 && redsearch != 4) + return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); + + pix1 = pixDeskew(pixs, redsearch); + pix2 = pixRotate90(pix1, 1); + pix3 = pixDeskew(pix2, redsearch); + pix4 = pixRotate90(pix3, -1); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + return pix4; +} + + +/*! + * \brief pixDeskew() + * + * \param[in] pixs any depth + * \param[in] redsearch for binary search: reduction factor = 1, 2 or 4; + * use 0 for default + * \return pixd deskewed pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This binarizes if necessary and finds the skew angle.  If the
+ *          angle is large enough and there is sufficient confidence,
+ *          it returns a deskewed image; otherwise, it returns a clone.
+ *      (2) Typical values at 300 ppi for %redsearch are 2 and 4.
+ *          At 75 ppi, one should use %redsearch = 1.
+ * 
+ */ +PIX * +pixDeskew(PIX *pixs, + l_int32 redsearch) +{ + PROCNAME("pixDeskew"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (redsearch == 0) + redsearch = DefaultBsReduction; + else if (redsearch != 1 && redsearch != 2 && redsearch != 4) + return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); + + return pixDeskewGeneral(pixs, 0, 0.0, 0.0, redsearch, 0, NULL, NULL); +} + + +/*! + * \brief pixFindSkewAndDeskew() + * + * \param[in] pixs any depth + * \param[in] redsearch for binary search: reduction factor = 1, 2 or 4; + * use 0 for default + * \param[out] pangle [optional] angle required to deskew, + * in degrees; use NULL to skip + * \param[out] pconf [optional] conf value is ratio + * of max/min scores; use NULL to skip + * \return pixd deskewed pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This binarizes if necessary and finds the skew angle.  If the
+ *          angle is large enough and there is sufficient confidence,
+ *          it returns a deskewed image; otherwise, it returns a clone.
+ * 
+ */ +PIX * +pixFindSkewAndDeskew(PIX *pixs, + l_int32 redsearch, + l_float32 *pangle, + l_float32 *pconf) +{ + PROCNAME("pixFindSkewAndDeskew"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (redsearch == 0) + redsearch = DefaultBsReduction; + else if (redsearch != 1 && redsearch != 2 && redsearch != 4) + return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); + + return pixDeskewGeneral(pixs, 0, 0.0, 0.0, redsearch, 0, pangle, pconf); +} + + +/*! + * \brief pixDeskewGeneral() + * + * \param[in] pixs any depth + * \param[in] redsweep for linear search: reduction factor = 1, 2 or 4; + * use 0 for default + * \param[in] sweeprange in degrees in each direction from 0; + * use 0.0 for default + * \param[in] sweepdelta in degrees; use 0.0 for default + * \param[in] redsearch for binary search: reduction factor = 1, 2 or 4; + * use 0 for default; + * \param[in] thresh for binarizing the image; use 0 for default + * \param[out] pangle [optional] angle required to deskew, + * in degrees; use NULL to skip + * \param[out] pconf [optional] conf value is ratio + * of max/min scores; use NULL to skip + * \return pixd deskewed pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This binarizes if necessary and finds the skew angle.  If the
+ *          angle is large enough and there is sufficient confidence,
+ *          it returns a deskewed image; otherwise, it returns a clone.
+ * 
+ */ +PIX * +pixDeskewGeneral(PIX *pixs, + l_int32 redsweep, + l_float32 sweeprange, + l_float32 sweepdelta, + l_int32 redsearch, + l_int32 thresh, + l_float32 *pangle, + l_float32 *pconf) +{ +l_int32 ret, depth; +l_float32 angle, conf, deg2rad; +PIX *pixb, *pixd; + + PROCNAME("pixDeskewGeneral"); + + if (pangle) *pangle = 0.0; + if (pconf) *pconf = 0.0; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (redsweep == 0) + redsweep = DefaultSweepReduction; + else if (redsweep != 1 && redsweep != 2 && redsweep != 4) + return (PIX *)ERROR_PTR("redsweep not in {1,2,4}", procName, NULL); + if (sweeprange == 0.0) + sweeprange = DefaultSweepRange; + if (sweepdelta == 0.0) + sweepdelta = DefaultSweepDelta; + if (redsearch == 0) + redsearch = DefaultBsReduction; + else if (redsearch != 1 && redsearch != 2 && redsearch != 4) + return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); + if (thresh == 0) + thresh = DefaultBinaryThreshold; + + deg2rad = 3.1415926535 / 180.; + + /* Binarize if necessary */ + depth = pixGetDepth(pixs); + if (depth == 1) + pixb = pixClone(pixs); + else + pixb = pixConvertTo1(pixs, thresh); + + /* Use the 1 bpp image to find the skew */ + ret = pixFindSkewSweepAndSearch(pixb, &angle, &conf, redsweep, redsearch, + sweeprange, sweepdelta, + DefaultMinbsDelta); + pixDestroy(&pixb); + if (pangle) *pangle = angle; + if (pconf) *pconf = conf; + if (ret) + return pixClone(pixs); + + if (L_ABS(angle) < MinDeskewAngle || conf < MinAllowedConfidence) + return pixClone(pixs); + + if ((pixd = pixRotate(pixs, deg2rad * angle, L_ROTATE_AREA_MAP, + L_BRING_IN_WHITE, 0, 0)) == NULL) + return pixClone(pixs); + else + return pixd; +} + + +/*-----------------------------------------------------------------------* + * Simple top-level angle-finding interface * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixFindSkew() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew, in degrees + * \param[out] pconf confidence value is ratio max/min scores + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) This is a simple high-level interface, that uses default
+ *          values of the parameters for reasonable speed and accuracy.
+ *      (2) The angle returned is the negative of the skew angle of
+ *          the image.  It is the angle required for deskew.
+ *          Clockwise rotations are positive angles.
+ * 
+ */ +l_ok +pixFindSkew(PIX *pixs, + l_float32 *pangle, + l_float32 *pconf) +{ + PROCNAME("pixFindSkew"); + + if (pangle) *pangle = 0.0; + if (pconf) *pconf = 0.0; + if (!pangle || !pconf) + return ERROR_INT("&angle and/or &conf not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not 1 bpp", procName, 1); + + return pixFindSkewSweepAndSearch(pixs, pangle, pconf, + DefaultSweepReduction, + DefaultBsReduction, + DefaultSweepRange, + DefaultSweepDelta, + DefaultMinbsDelta); +} + + +/*-----------------------------------------------------------------------* + * Basic angle-finding functions * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixFindSkewSweep() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew, in degrees + * \param[in] reduction factor = 1, 2, 4 or 8 + * \param[in] sweeprange half the full range; assumed about 0; in degrees + * \param[in] sweepdelta angle increment of sweep; in degrees + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) This examines the 'score' for skew angles with equal intervals.
+ *      (2) Caller must check the return value for validity of the result.
+ * 
+ */ +l_ok +pixFindSkewSweep(PIX *pixs, + l_float32 *pangle, + l_int32 reduction, + l_float32 sweeprange, + l_float32 sweepdelta) +{ +l_int32 ret, bzero, i, nangles; +l_float32 deg2rad, theta; +l_float32 sum, maxscore, maxangle; +NUMA *natheta, *nascore; +PIX *pix, *pixt; + + PROCNAME("pixFindSkewSweep"); + + if (!pangle) + return ERROR_INT("&angle not defined", procName, 1); + *pangle = 0.0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not 1 bpp", procName, 1); + if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) + return ERROR_INT("reduction must be in {1,2,4,8}", procName, 1); + + deg2rad = 3.1415926535 / 180.; + ret = 0; + + /* Generate reduced image, if requested */ + if (reduction == 1) + pix = pixClone(pixs); + else if (reduction == 2) + pix = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); + else if (reduction == 4) + pix = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); + else /* reduction == 8 */ + pix = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0); + + pixZero(pix, &bzero); + if (bzero) { + pixDestroy(&pix); + return 1; + } + + nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1); + natheta = numaCreate(nangles); + nascore = numaCreate(nangles); + pixt = pixCreateTemplate(pix); + + if (!pix || !pixt) { + ret = ERROR_INT("pix and pixt not both made", procName, 1); + goto cleanup; + } + if (!natheta || !nascore) { + ret = ERROR_INT("natheta and nascore not both made", procName, 1); + goto cleanup; + } + + for (i = 0; i < nangles; i++) { + theta = -sweeprange + i * sweepdelta; /* degrees */ + + /* Shear pix about the UL corner and put the result in pixt */ + pixVShearCorner(pixt, pix, deg2rad * theta, L_BRING_IN_WHITE); + + /* Get score */ + pixFindDifferentialSquareSum(pixt, &sum); + +#if DEBUG_PRINT_SCORES + L_INFO("sum(%7.2f) = %7.0f\n", procName, theta, sum); +#endif /* DEBUG_PRINT_SCORES */ + + /* Save the result in the output arrays */ + numaAddNumber(nascore, sum); + numaAddNumber(natheta, theta); + } + + /* Find the location of the maximum (i.e., the skew angle) + * by fitting the largest data point and its two neighbors + * to a quadratic, using lagrangian interpolation. */ + numaFitMax(nascore, &maxscore, natheta, &maxangle); + *pangle = maxangle; + +#if DEBUG_PRINT_SWEEP + L_INFO(" From sweep: angle = %7.3f, score = %7.3f\n", procName, + maxangle, maxscore); +#endif /* DEBUG_PRINT_SWEEP */ + +#if DEBUG_PLOT_SCORES + /* Plot the result -- the scores versus rotation angle -- + * using gnuplot with GPLOT_LINES (lines connecting data points). + * The GPLOT data structure is first created, with the + * appropriate data incorporated from the two input NUMAs, + * and then the function gplotMakeOutput() uses gnuplot to + * generate the output plot. This can be either a .png file + * or a .ps file, depending on whether you use GPLOT_PNG + * or GPLOT_PS. */ + {GPLOT *gplot; + gplot = gplotCreate("sweep_output", GPLOT_PNG, + "Sweep. Variance of difference of ON pixels vs. angle", + "angle (deg)", "score"); + gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES, "plot1"); + gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot2"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } +#endif /* DEBUG_PLOT_SCORES */ + +cleanup: + pixDestroy(&pix); + pixDestroy(&pixt); + numaDestroy(&nascore); + numaDestroy(&natheta); + return ret; +} + + +/*! + * \brief pixFindSkewSweepAndSearch() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew; in degrees + * \param[out] pconf confidence given by ratio of max/min score + * \param[in] redsweep sweep reduction factor = 1, 2, 4 or 8 + * \param[in] redsearch binary search reduction factor = 1, 2, 4 or 8; + * and must not exceed redsweep + * \param[in] sweeprange half the full range, assumed about 0; in degrees + * \param[in] sweepdelta angle increment of sweep; in degrees + * \param[in] minbsdelta min binary search increment angle; in degrees + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) This finds the skew angle, doing first a sweep through a set
+ *          of equal angles, and then doing a binary search until
+ *          convergence.
+ *      (2) Caller must check the return value for validity of the result.
+ *      (3) In computing the differential line sum variance score, we sum
+ *          the result over scanlines, but we always skip:
+ *           ~ at least one scanline
+ *           ~ not more than 10% of the image height
+ *           ~ not more than 5% of the image width
+ *      (4) See also notes in pixFindSkewSweepAndSearchScore()
+ * 
+ */ +l_ok +pixFindSkewSweepAndSearch(PIX *pixs, + l_float32 *pangle, + l_float32 *pconf, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta) +{ + return pixFindSkewSweepAndSearchScore(pixs, pangle, pconf, NULL, + redsweep, redsearch, 0.0, sweeprange, + sweepdelta, minbsdelta); +} + + +/*! + * \brief pixFindSkewSweepAndSearchScore() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew; in degrees + * \param[out] pconf confidence given by ratio of max/min score + * \param[out] pendscore [optional] max score; use NULL to ignore + * \param[in] redsweep sweep reduction factor = 1, 2, 4 or 8 + * \param[in] redsearch binary search reduction factor = 1, 2, 4 or 8; + * and must not exceed redsweep + * \param[in] sweepcenter angle about which sweep is performed; in degrees + * \param[in] sweeprange half the full range, taken about sweepcenter; + * in degrees + * \param[in] sweepdelta angle increment of sweep; in degrees + * \param[in] minbsdelta min binary search increment angle; in degrees + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) This finds the skew angle, doing first a sweep through a set
+ *          of equal angles, and then doing a binary search until convergence.
+ *      (2) There are two built-in constants that determine if the
+ *          returned confidence is nonzero:
+ *            ~ MinValidMaxscore (minimum allowed maxscore)
+ *            ~ MinscoreThreshFactor (determines minimum allowed
+ *                 minscore, by multiplying by (height * width^2)
+ *          If either of these conditions is not satisfied, the returned
+ *          confidence value will be zero.  The maxscore is optionally
+ *          returned in this function to allow evaluation of the
+ *          resulting angle by a method that is independent of the
+ *          returned confidence value.
+ *      (3) The larger the confidence value, the greater the probability
+ *          that the proper alignment is given by the angle that maximizes
+ *          variance.  It should be compared to a threshold, which depends
+ *          on the application.  Values between 3.0 and 6.0 are common.
+ *      (4) By default, the shear is about the UL corner.
+ * 
+ */ +l_ok +pixFindSkewSweepAndSearchScore(PIX *pixs, + l_float32 *pangle, + l_float32 *pconf, + l_float32 *pendscore, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweepcenter, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta) +{ + return pixFindSkewSweepAndSearchScorePivot(pixs, pangle, pconf, pendscore, + redsweep, redsearch, 0.0, + sweeprange, sweepdelta, + minbsdelta, + L_SHEAR_ABOUT_CORNER); +} + + +/*! + * \brief pixFindSkewSweepAndSearchScorePivot() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew; in degrees + * \param[out] pconf confidence given by ratio of max/min score + * \param[out] pendscore [optional] max score; use NULL to ignore + * \param[in] redsweep sweep reduction factor = 1, 2, 4 or 8 + * \param[in] redsearch binary search reduction factor = 1, 2, 4 or 8; + * and must not exceed redsweep + * \param[in] sweepcenter angle about which sweep is performed; in degrees + * \param[in] sweeprange half the full range, taken about sweepcenter; + * in degrees + * \param[in] sweepdelta angle increment of sweep; in degrees + * \param[in] minbsdelta min binary search increment angle; in degrees + * \param[in] pivot L_SHEAR_ABOUT_CORNER, L_SHEAR_ABOUT_CENTER + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) See notes in pixFindSkewSweepAndSearchScore().
+ *      (2) This allows choice of shear pivoting from either the UL corner
+ *          or the center.  For small angles, the ability to discriminate
+ *          angles is better with shearing from the UL corner.  However,
+ *          for large angles (say, greater than 20 degrees), it is better
+ *          to shear about the center because a shear from the UL corner
+ *          loses too much of the image.
+ * 
+ */ +l_ok +pixFindSkewSweepAndSearchScorePivot(PIX *pixs, + l_float32 *pangle, + l_float32 *pconf, + l_float32 *pendscore, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweepcenter, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta, + l_int32 pivot) +{ +l_int32 ret, bzero, i, nangles, n, ratio, maxindex, minloc; +l_int32 width, height; +l_float32 deg2rad, theta, delta; +l_float32 sum, maxscore, maxangle; +l_float32 centerangle, leftcenterangle, rightcenterangle; +l_float32 lefttemp, righttemp; +l_float32 bsearchscore[5]; +l_float32 minscore, minthresh; +l_float32 rangeleft; +NUMA *natheta, *nascore; +PIX *pixsw, *pixsch, *pixt1, *pixt2; + + PROCNAME("pixFindSkewSweepAndSearchScorePivot"); + + if (pendscore) *pendscore = 0.0; + if (pangle) *pangle = 0.0; + if (pconf) *pconf = 0.0; + if (!pangle || !pconf) + return ERROR_INT("&angle and/or &conf not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + if (redsweep != 1 && redsweep != 2 && redsweep != 4 && redsweep != 8) + return ERROR_INT("redsweep must be in {1,2,4,8}", procName, 1); + if (redsearch != 1 && redsearch != 2 && redsearch != 4 && redsearch != 8) + return ERROR_INT("redsearch must be in {1,2,4,8}", procName, 1); + if (redsearch > redsweep) + return ERROR_INT("redsearch must not exceed redsweep", procName, 1); + if (pivot != L_SHEAR_ABOUT_CORNER && pivot != L_SHEAR_ABOUT_CENTER) + return ERROR_INT("invalid pivot", procName, 1); + + deg2rad = 3.1415926535 / 180.; + ret = 0; + + /* Generate reduced image for binary search, if requested */ + if (redsearch == 1) + pixsch = pixClone(pixs); + else if (redsearch == 2) + pixsch = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); + else if (redsearch == 4) + pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); + else /* redsearch == 8 */ + pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0); + + pixZero(pixsch, &bzero); + if (bzero) { + pixDestroy(&pixsch); + return 1; + } + + /* Generate reduced image for sweep, if requested */ + ratio = redsweep / redsearch; + if (ratio == 1) { + pixsw = pixClone(pixsch); + } else { /* ratio > 1 */ + if (ratio == 2) + pixsw = pixReduceRankBinaryCascade(pixsch, 1, 0, 0, 0); + else if (ratio == 4) + pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 0, 0); + else /* ratio == 8 */ + pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 2, 0); + } + + pixt1 = pixCreateTemplate(pixsw); + if (ratio == 1) + pixt2 = pixClone(pixt1); + else + pixt2 = pixCreateTemplate(pixsch); + + nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1); + natheta = numaCreate(nangles); + nascore = numaCreate(nangles); + + if (!pixsch || !pixsw) { + ret = ERROR_INT("pixsch and pixsw not both made", procName, 1); + goto cleanup; + } + if (!pixt1 || !pixt2) { + ret = ERROR_INT("pixt1 and pixt2 not both made", procName, 1); + goto cleanup; + } + if (!natheta || !nascore) { + ret = ERROR_INT("natheta and nascore not both made", procName, 1); + goto cleanup; + } + + /* Do sweep */ + rangeleft = sweepcenter - sweeprange; + for (i = 0; i < nangles; i++) { + theta = rangeleft + i * sweepdelta; /* degrees */ + + /* Shear pix and put the result in pixt1 */ + if (pivot == L_SHEAR_ABOUT_CORNER) + pixVShearCorner(pixt1, pixsw, deg2rad * theta, L_BRING_IN_WHITE); + else + pixVShearCenter(pixt1, pixsw, deg2rad * theta, L_BRING_IN_WHITE); + + /* Get score */ + pixFindDifferentialSquareSum(pixt1, &sum); + +#if DEBUG_PRINT_SCORES + L_INFO("sum(%7.2f) = %7.0f\n", procName, theta, sum); +#endif /* DEBUG_PRINT_SCORES */ + + /* Save the result in the output arrays */ + numaAddNumber(nascore, sum); + numaAddNumber(natheta, theta); + } + + /* Find the largest of the set (maxscore at maxangle) */ + numaGetMax(nascore, &maxscore, &maxindex); + numaGetFValue(natheta, maxindex, &maxangle); + +#if DEBUG_PRINT_SWEEP + L_INFO(" From sweep: angle = %7.3f, score = %7.3f\n", procName, + maxangle, maxscore); +#endif /* DEBUG_PRINT_SWEEP */ + +#if DEBUG_PLOT_SCORES + /* Plot the sweep result -- the scores versus rotation angle -- + * using gnuplot with GPLOT_LINES (lines connecting data points). */ + {GPLOT *gplot; + gplot = gplotCreate("sweep_output", GPLOT_PNG, + "Sweep. Variance of difference of ON pixels vs. angle", + "angle (deg)", "score"); + gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES, "plot1"); + gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot2"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } +#endif /* DEBUG_PLOT_SCORES */ + + /* Check if the max is at the end of the sweep. */ + n = numaGetCount(natheta); + if (maxindex == 0 || maxindex == n - 1) { + L_WARNING("max found at sweep edge\n", procName); + goto cleanup; + } + + /* Empty the numas for re-use */ + numaEmpty(nascore); + numaEmpty(natheta); + + /* Do binary search to find skew angle. + * First, set up initial three points. */ + centerangle = maxangle; + if (pivot == L_SHEAR_ABOUT_CORNER) { + pixVShearCorner(pixt2, pixsch, deg2rad * centerangle, L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[2]); + pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle - sweepdelta), + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[0]); + pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle + sweepdelta), + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[4]); + } else { + pixVShearCenter(pixt2, pixsch, deg2rad * centerangle, L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[2]); + pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle - sweepdelta), + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[0]); + pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle + sweepdelta), + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[4]); + } + + numaAddNumber(nascore, bsearchscore[2]); + numaAddNumber(natheta, centerangle); + numaAddNumber(nascore, bsearchscore[0]); + numaAddNumber(natheta, centerangle - sweepdelta); + numaAddNumber(nascore, bsearchscore[4]); + numaAddNumber(natheta, centerangle + sweepdelta); + + /* Start the search */ + delta = 0.5 * sweepdelta; + while (delta >= minbsdelta) + { + /* Get the left intermediate score */ + leftcenterangle = centerangle - delta; + if (pivot == L_SHEAR_ABOUT_CORNER) + pixVShearCorner(pixt2, pixsch, deg2rad * leftcenterangle, + L_BRING_IN_WHITE); + else + pixVShearCenter(pixt2, pixsch, deg2rad * leftcenterangle, + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[1]); + numaAddNumber(nascore, bsearchscore[1]); + numaAddNumber(natheta, leftcenterangle); + + /* Get the right intermediate score */ + rightcenterangle = centerangle + delta; + if (pivot == L_SHEAR_ABOUT_CORNER) + pixVShearCorner(pixt2, pixsch, deg2rad * rightcenterangle, + L_BRING_IN_WHITE); + else + pixVShearCenter(pixt2, pixsch, deg2rad * rightcenterangle, + L_BRING_IN_WHITE); + pixFindDifferentialSquareSum(pixt2, &bsearchscore[3]); + numaAddNumber(nascore, bsearchscore[3]); + numaAddNumber(natheta, rightcenterangle); + + /* Find the maximum of the five scores and its location. + * Note that the maximum must be in the center + * three values, not in the end two. */ + maxscore = bsearchscore[1]; + maxindex = 1; + for (i = 2; i < 4; i++) { + if (bsearchscore[i] > maxscore) { + maxscore = bsearchscore[i]; + maxindex = i; + } + } + + /* Set up score array to interpolate for the next iteration */ + lefttemp = bsearchscore[maxindex - 1]; + righttemp = bsearchscore[maxindex + 1]; + bsearchscore[2] = maxscore; + bsearchscore[0] = lefttemp; + bsearchscore[4] = righttemp; + + /* Get new center angle and delta for next iteration */ + centerangle = centerangle + delta * (maxindex - 2); + delta = 0.5 * delta; + } + *pangle = centerangle; + +#if DEBUG_PRINT_SCORES + L_INFO(" Binary search score = %7.3f\n", procName, bsearchscore[2]); +#endif /* DEBUG_PRINT_SCORES */ + + if (pendscore) /* save if requested */ + *pendscore = bsearchscore[2]; + + /* Return the ratio of Max score over Min score + * as a confidence value. Don't trust if the Min score + * is too small, which can happen if the image is all black + * with only a few white pixels interspersed. In that case, + * we get a contribution from the top and bottom edges when + * vertically sheared, but this contribution becomes zero when + * the shear angle is zero. For zero shear angle, the only + * contribution will be from the white pixels. We expect that + * the signal goes as the product of the (height * width^2), + * so we compute a (hopefully) normalized minimum threshold as + * a function of these dimensions. */ + numaGetMin(nascore, &minscore, &minloc); + width = pixGetWidth(pixsch); + height = pixGetHeight(pixsch); + minthresh = MinscoreThreshFactor * width * width * height; + +#if DEBUG_THRESHOLD + L_INFO(" minthresh = %10.2f, minscore = %10.2f\n", procName, + minthresh, minscore); + L_INFO(" maxscore = %10.2f\n", procName, maxscore); +#endif /* DEBUG_THRESHOLD */ + + if (minscore > minthresh) + *pconf = maxscore / minscore; + else + *pconf = 0.0; + + /* Don't trust it if too close to the edge of the sweep + * range or if maxscore is small */ + if ((centerangle > rangeleft + 2 * sweeprange - sweepdelta) || + (centerangle < rangeleft + sweepdelta) || + (maxscore < MinValidMaxscore)) + *pconf = 0.0; + +#if DEBUG_PRINT_BINARY + fprintf(stderr, "Binary search: angle = %7.3f, score ratio = %6.2f\n", + *pangle, *pconf); + fprintf(stderr, " max score = %8.0f\n", maxscore); +#endif /* DEBUG_PRINT_BINARY */ + +#if DEBUG_PLOT_SCORES + /* Plot the result -- the scores versus rotation angle -- + * using gnuplot with GPLOT_POINTS. Because the data + * points are not ordered by theta (increasing or decreasing), + * using GPLOT_LINES would be confusing! */ + {GPLOT *gplot; + gplot = gplotCreate("search_output", GPLOT_PNG, + "Binary search. Variance of difference of ON pixels vs. angle", + "angle (deg)", "score"); + gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot1"); + gplotMakeOutput(gplot); + gplotDestroy(&gplot); + } +#endif /* DEBUG_PLOT_SCORES */ + +cleanup: + pixDestroy(&pixsw); + pixDestroy(&pixsch); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + numaDestroy(&nascore); + numaDestroy(&natheta); + return ret; +} + + +/*---------------------------------------------------------------------* + * Search over arbitrary range of angles in orthogonal directions * + *---------------------------------------------------------------------*/ +/* + * \brief pixFindSkewOrthogonalRange() + * + * \param[in] pixs 1 bpp + * \param[out] pangle angle required to deskew; in degrees cw + * \param[out] pconf confidence given by ratio of max/min score + * \param[in] redsweep sweep reduction factor = 1, 2, 4 or 8 + * \param[in] redsearch binary search reduction factor = 1, 2, 4 or 8; + * and must not exceed redsweep + * \param[in] sweeprange half the full range in each orthogonal + * direction, taken about 0, in degrees + * \param[in] sweepdelta angle increment of sweep; in degrees + * \param[in] minbsdelta min binary search increment angle; in degrees + * \param[in] confprior amount by which confidence of 90 degree rotated + * result is reduced when comparing with unrotated + * confidence value + * \return 0 if OK, 1 on error or if angle measurement not valid + * + *
+ * Notes:
+ *      (1) This searches for the skew angle, first in the range
+ *          [-sweeprange, sweeprange], and then in
+ *          [90 - sweeprange, 90 + sweeprange], with angles measured
+ *          clockwise.  For exploring the full range of possibilities,
+ *          suggest using sweeprange = 47.0 degrees, giving some overlap
+ *          at 45 and 135 degrees.  From these results, and discounting
+ *          the the second confidence by %confprior, it selects the
+ *          angle for maximal differential variance.  If the angle
+ *          is larger than pi/4, the angle found after 90 degree rotation
+ *          is selected.
+ *      (2) The larger the confidence value, the greater the probability
+ *          that the proper alignment is given by the angle that maximizes
+ *          variance.  It should be compared to a threshold, which depends
+ *          on the application.  Values between 3.0 and 6.0 are common.
+ *      (3) Allowing for both portrait and landscape searches is more
+ *          difficult, because if the signal from the text lines is weak,
+ *          a signal from vertical rules can be larger!
+ *          The most difficult documents to deskew have some or all of:
+ *            (a) Multiple columns, not aligned
+ *            (b) Black lines along the vertical edges
+ *            (c) Text from two pages, and at different angles
+ *          Rule of thumb for resolution:
+ *            (a) If the margins are clean, you can work at 75 ppi,
+ *                although 100 ppi is safer.
+ *            (b) If there are vertical lines in the margins, do not
+ *                work below 150 ppi.  The signal from the text lines must
+ *                exceed that from the margin lines.
+ *      (4) Choosing the %confprior parameter depends on knowing something
+ *          about the source of image.  However, we're not using
+ *          real probabilities here, so its use is qualitative.
+ *          If landscape and portrait are equally likely, use
+ *          %confprior = 0.0.  If the likelihood of portrait (non-rotated)
+ *          is 100 times higher than that of landscape, we want to reduce
+ *          the chance that we rotate to landscape in a situation where
+ *          the landscape signal is accidentally larger than the
+ *          portrait signal.  To do this use a positive value of
+ *          %confprior; say 1.5.
+ * 
+ */ +l_int32 +pixFindSkewOrthogonalRange(PIX *pixs, + l_float32 *pangle, + l_float32 *pconf, + l_int32 redsweep, + l_int32 redsearch, + l_float32 sweeprange, + l_float32 sweepdelta, + l_float32 minbsdelta, + l_float32 confprior) +{ +l_float32 angle1, conf1, score1, angle2, conf2, score2; +PIX *pixr; + + PROCNAME("pixFindSkewOrthogonalRange"); + + if (pangle) *pangle = 0.0; + if (pconf) *pconf = 0.0; + if (!pangle || !pconf) + return ERROR_INT("&angle and/or &conf not defined", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + + pixFindSkewSweepAndSearchScorePivot(pixs, &angle1, &conf1, &score1, + redsweep, redsearch, 0.0, + sweeprange, sweepdelta, minbsdelta, + L_SHEAR_ABOUT_CORNER); + pixr = pixRotateOrth(pixs, 1); + pixFindSkewSweepAndSearchScorePivot(pixr, &angle2, &conf2, &score2, + redsweep, redsearch, 0.0, + sweeprange, sweepdelta, minbsdelta, + L_SHEAR_ABOUT_CORNER); + pixDestroy(&pixr); + + if (conf1 > conf2 - confprior) { + *pangle = angle1; + *pconf = conf1; + } else { + *pangle = -90.0 + angle2; + *pconf = conf2; + } + +#if DEBUG_PRINT_ORTH + fprintf(stderr, " About 0: angle1 = %7.3f, conf1 = %7.3f, score1 = %f\n", + angle1, conf1, score1); + fprintf(stderr, " About 90: angle2 = %7.3f, conf2 = %7.3f, score2 = %f\n", + angle2, conf2, score2); + fprintf(stderr, " Final: angle = %7.3f, conf = %7.3f\n", *pangle, *pconf); +#endif /* DEBUG_PRINT_ORTH */ + + return 0; +} + + + +/*----------------------------------------------------------------* + * Differential square sum function * + *----------------------------------------------------------------*/ +/*! + * \brief pixFindDifferentialSquareSum() + * + * \param[in] pixs + * \param[out] psum result + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) At the top and bottom, we skip:
+ *           ~ at least one scanline
+ *           ~ not more than 10% of the image height
+ *           ~ not more than 5% of the image width
+ * 
+ */ +l_ok +pixFindDifferentialSquareSum(PIX *pixs, + l_float32 *psum) +{ +l_int32 i, n; +l_int32 w, h, skiph, skip, nskip; +l_float32 val1, val2, diff, sum; +NUMA *na; + + PROCNAME("pixFindDifferentialSquareSum"); + + if (!psum) + return ERROR_INT("&sum not defined", procName, 1); + *psum = 0.0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + /* Generate a number array consisting of the sum + * of pixels in each row of pixs */ + if ((na = pixCountPixelsByRow(pixs, NULL)) == NULL) + return ERROR_INT("na not made", procName, 1); + + /* Compute the number of rows at top and bottom to omit. + * We omit these to avoid getting a spurious signal from + * the top and bottom of a (nearly) all black image. */ + w = pixGetWidth(pixs); + h = pixGetHeight(pixs); + skiph = (l_int32)(0.05 * w); /* skip for max shear of 0.025 radians */ + skip = L_MIN(h / 10, skiph); /* don't remove more than 10% of image */ + nskip = L_MAX(skip / 2, 1); /* at top & bot; skip at least one line */ + + /* Sum the squares of differential row sums, on the + * allowed rows. Note that nskip must be >= 1. */ + n = numaGetCount(na); + sum = 0.0; + for (i = nskip; i < n - nskip; i++) { + numaGetFValue(na, i - 1, &val1); + numaGetFValue(na, i, &val2); + diff = val2 - val1; + sum += diff * diff; + } + numaDestroy(&na); + *psum = sum; + return 0; +} + + +/*----------------------------------------------------------------* + * Normalized square sum * + *----------------------------------------------------------------*/ +/*! + * \brief pixFindNormalizedSquareSum() + * + * \param[in] pixs + * \param[out] phratio [optional] ratio of normalized horiz square sum + * to result if the pixel distribution were uniform + * \param[out] pvratio [optional] ratio of normalized vert square sum + * to result if the pixel distribution were uniform + * \param[out] pfract [optional] ratio of fg pixels to total pixels + * \return 0 if OK, 1 on error or if there are no fg pixels + * + *
+ * Notes:
+ *      (1) Let the image have h scanlines and N fg pixels.
+ *          If the pixels were uniformly distributed on scanlines,
+ *          the sum of squares of fg pixels on each scanline would be
+ *          h * (N / h)^2.  However, if the pixels are not uniformly
+ *          distributed (e.g., for text), the sum of squares of fg
+ *          pixels will be larger.  We return in hratio and vratio the
+ *          ratio of these two values.
+ *      (2) If there are no fg pixels, hratio and vratio are returned as 0.0.
+ * 
+ */ +l_ok +pixFindNormalizedSquareSum(PIX *pixs, + l_float32 *phratio, + l_float32 *pvratio, + l_float32 *pfract) +{ +l_int32 i, w, h, empty; +l_float32 sum, sumsq, uniform, val; +NUMA *na; +PIX *pixt; + + PROCNAME("pixFindNormalizedSquareSum"); + + if (phratio) *phratio = 0.0; + if (pvratio) *pvratio = 0.0; + if (pfract) *pfract = 0.0; + if (!phratio && !pvratio) + return ERROR_INT("nothing to do", procName, 1); + if (!pixs || pixGetDepth(pixs) != 1) + return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); + pixGetDimensions(pixs, &w, &h, NULL); + + empty = 0; + if (phratio) { + na = pixCountPixelsByRow(pixs, NULL); + numaGetSum(na, &sum); /* fg pixels */ + if (pfract) *pfract = sum / (l_float32)(w * h); + if (sum != 0.0) { + uniform = sum * sum / h; /* h*(sum / h)^2 */ + sumsq = 0.0; + for (i = 0; i < h; i++) { + numaGetFValue(na, i, &val); + sumsq += val * val; + } + *phratio = sumsq / uniform; + } else { + empty = 1; + } + numaDestroy(&na); + } + + if (pvratio) { + if (empty == 1) return 1; + pixt = pixRotateOrth(pixs, 1); + na = pixCountPixelsByRow(pixt, NULL); + numaGetSum(na, &sum); + if (pfract) *pfract = sum / (l_float32)(w * h); + if (sum != 0.0) { + uniform = sum * sum / w; + sumsq = 0.0; + for (i = 0; i < w; i++) { + numaGetFValue(na, i, &val); + sumsq += val * val; + } + *pvratio = sumsq / uniform; + } else { + empty = 1; + } + pixDestroy(&pixt); + numaDestroy(&na); + } + + return empty; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/spixio.c b/hgdriver/3rdparty/hgOCR/leptonica/spixio.c new file mode 100644 index 0000000..d110206 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/spixio.c @@ -0,0 +1,493 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file spixio.c + *
+ *
+ *    This does fast serialization of a pix in memory to file,
+ *    copying the raw data for maximum speed.  The underlying
+ *    function serializes it to memory, and it is wrapped to be
+ *    callable from standard pixRead() and pixWrite() file functions.
+ *
+ *      Reading spix from file
+ *           PIX        *pixReadStreamSpix()
+ *           l_int32     readHeaderSpix()
+ *           l_int32     freadHeaderSpix()
+ *           l_int32     sreadHeaderSpix()
+ *
+ *      Writing spix to file
+ *           l_int32     pixWriteStreamSpix()
+ *
+ *      Low-level serialization of pix to/from memory (uncompressed)
+ *           PIX        *pixReadMemSpix()
+ *           l_int32     pixWriteMemSpix()
+ *           l_int32     pixSerializeToMemory()
+ *           PIX        *pixDeserializeFromMemory()
+ *
+ *    Note: these functions have not been extensively tested for fuzzing
+ *    (bad input data that can result in, e.g., memory faults).
+ *    The spix serialization format is only defined here, in leptonica.
+ *    The image data is uncompressed and the serialization is not intended
+ *    to be a secure file format from untrusted sources.
+ * 
+ */ + +#include +#include "allheaders.h" + + /* Image dimension limits */ +static const l_int32 MaxAllowedWidth = 1000000; +static const l_int32 MaxAllowedHeight = 1000000; +static const l_int64 MaxAllowedArea = 400000000LL; + +#ifndef NO_CONSOLE_IO +#define DEBUG_SERIALIZE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-----------------------------------------------------------------------* + * Reading spix from file * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixReadStreamSpix() + * + * \param[in] fp file stream + * \return pix, or NULL on error. + * + *
+ * Notes:
+ *      (1) If called from pixReadStream(), the stream is positioned
+ *          at the beginning of the file.
+ * 
+ */ +PIX * +pixReadStreamSpix(FILE *fp) +{ +size_t nbytes; +l_uint8 *data; +PIX *pix; + + PROCNAME("pixReadStreamSpix"); + + if (!fp) + return (PIX *)ERROR_PTR("stream not defined", procName, NULL); + + if ((data = l_binaryReadStream(fp, &nbytes)) == NULL) + return (PIX *)ERROR_PTR("data not read", procName, NULL); + pix = pixReadMemSpix(data, nbytes); + LEPT_FREE(data); + if (!pix) + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + return pix; +} + + +/*! + * \brief readHeaderSpix() + * + * \param[in] filename + * \param[out] pwidth width + * \param[out] pheight height + * \param[out] pbps bits/sample + * \param[out] pspp samples/pixel + * \param[out] piscmap [optional] input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, iscmap is returned as 1; else 0.
+ * 
+ */ +l_ok +readHeaderSpix(const char *filename, + l_int32 *pwidth, + l_int32 *pheight, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderSpix"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pwidth || !pheight || !pbps || !pspp) + return ERROR_INT("input ptr(s) not defined", procName, 1); + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderSpix(fp, pwidth, pheight, pbps, pspp, piscmap); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderSpix() + * + * \param[in] fp file stream + * \param[out] pwidth width + * \param[out] pheight height + * \param[out] pbps bits/sample + * \param[out] pspp samples/pixel + * \param[out] piscmap [optional] input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, iscmap is returned as 1; else 0.
+ * 
+ */ +l_ok +freadHeaderSpix(FILE *fp, + l_int32 *pwidth, + l_int32 *pheight, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 nbytes, ret; +l_uint32 data[6]; + + PROCNAME("freadHeaderSpix"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pwidth || !pheight || !pbps || !pspp) + return ERROR_INT("input ptr(s) not defined", procName, 1); + + nbytes = fnbytesInFile(fp); + if (nbytes < 32) + return ERROR_INT("file too small to be spix", procName, 1); + if (fread(data, 4, 6, fp) != 6) + return ERROR_INT("error reading data", procName, 1); + ret = sreadHeaderSpix(data, pwidth, pheight, pbps, pspp, piscmap); + return ret; +} + + +/*! + * \brief sreadHeaderSpix() + * + * \param[in] data + * \param[out] pwidth width + * \param[out] pheight height + * \param[out] pbps bits/sample + * \param[out] pspp samples/pixel + * \param[out] piscmap [optional] input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, iscmap is returned as 1; else 0.
+ * 
+ */ +l_ok +sreadHeaderSpix(const l_uint32 *data, + l_int32 *pwidth, + l_int32 *pheight, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +char *id; +l_int32 d, ncolors; + + PROCNAME("sreadHeaderSpix"); + + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (!pwidth || !pheight || !pbps || !pspp) + return ERROR_INT("input ptr(s) not defined", procName, 1); + *pwidth = *pheight = *pbps = *pspp = 0; + if (piscmap) + *piscmap = 0; + + /* Check file id */ + id = (char *)data; + if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x') + return ERROR_INT("not a valid spix file", procName, 1); + + *pwidth = data[1]; + *pheight = data[2]; + d = data[3]; + if (d <= 16) { + *pbps = d; + *pspp = 1; + } else { + *pbps = 8; + *pspp = d / 8; /* if the pix is 32 bpp, call it 4 samples */ + } + ncolors = data[5]; + if (piscmap) + *piscmap = (ncolors == 0) ? 0 : 1; + + return 0; +} + + +/*-----------------------------------------------------------------------* + * Writing spix to file * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixWriteStreamSpix() + * + * \param[in] fp file stream + * \param[in] pix + * \return 0 if OK; 1 on error + */ +l_ok +pixWriteStreamSpix(FILE *fp, + PIX *pix) +{ +l_uint8 *data; +size_t size; + + PROCNAME("pixWriteStreamSpix"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (pixWriteMemSpix(&data, &size, pix)) + return ERROR_INT("failure to write pix to memory", procName, 1); + fwrite(data, 1, size, fp); + LEPT_FREE(data); + return 0; +} + + +/*-----------------------------------------------------------------------* + * Low-level serialization of pix to/from memory (uncompressed) * + *-----------------------------------------------------------------------*/ +/*! + * \brief pixReadMemSpix() + * + * \param[in] data const; uncompressed + * \param[in] size bytes of data + * \return pix, or NULL on error + */ +PIX * +pixReadMemSpix(const l_uint8 *data, + size_t size) +{ + return pixDeserializeFromMemory((l_uint32 *)data, size); +} + + +/*! + * \brief pixWriteMemSpix() + * + * \param[out] pdata data of serialized, uncompressed pix + * \param[out] psize size of returned data + * \param[in] pix all depths; colormap OK + * \return 0 if OK, 1 on error + */ +l_ok +pixWriteMemSpix(l_uint8 **pdata, + size_t *psize, + PIX *pix) +{ + return pixSerializeToMemory(pix, (l_uint32 **)pdata, psize); +} + + +/*! + * \brief pixSerializeToMemory() + * + * \param[in] pixs all depths, colormap OK + * \param[out] pdata serialized data in memory + * \param[out] pnbytes number of bytes in data string + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This does a fast serialization of the principal elements
+ *          of the pix, as follows:
+ *            "spix"    (4 bytes) -- ID for file type
+ *            w         (4 bytes)
+ *            h         (4 bytes)
+ *            d         (4 bytes)
+ *            wpl       (4 bytes)
+ *            ncolors   (4 bytes) -- in colormap; 0 if there is no colormap
+ *            cdata     (4 * ncolors)  -- size of serialized colormap array
+ *            rdatasize (4 bytes) -- size of serialized raster data
+ *                                   = 4 * wpl * h
+ *            rdata     (rdatasize)
+ * 
+ */ +l_ok +pixSerializeToMemory(PIX *pixs, + l_uint32 **pdata, + size_t *pnbytes) +{ +char *id; +l_int32 w, h, d, wpl, rdatasize, ncolors, nbytes, index; +l_uint8 *cdata; /* data in colormap array (4 bytes/color table entry) */ +l_uint32 *data; +l_uint32 *rdata; /* data in pix raster */ +PIXCMAP *cmap; + + PROCNAME("pixSerializeToMemory"); + + if (!pdata || !pnbytes) + return ERROR_INT("&data and &nbytes not both defined", procName, 1); + *pdata = NULL; + *pnbytes = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pixGetDimensions(pixs, &w, &h, &d); + wpl = pixGetWpl(pixs); + rdata = pixGetData(pixs); + rdatasize = 4 * wpl * h; + ncolors = 0; + cdata = NULL; + if ((cmap = pixGetColormap(pixs)) != NULL) + pixcmapSerializeToMemory(cmap, 4, &ncolors, &cdata); + + nbytes = 24 + 4 * ncolors + 4 + rdatasize; + if ((data = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32))) + == NULL) { + LEPT_FREE(cdata); + return ERROR_INT("data not made", procName, 1); + } + *pdata = data; + *pnbytes = nbytes; + id = (char *)data; + id[0] = 's'; + id[1] = 'p'; + id[2] = 'i'; + id[3] = 'x'; + data[1] = w; + data[2] = h; + data[3] = d; + data[4] = wpl; + data[5] = ncolors; + if (ncolors > 0) + memcpy(data + 6, cdata, 4 * ncolors); + index = 6 + ncolors; + data[index] = rdatasize; + memcpy(data + index + 1, rdata, rdatasize); + +#if DEBUG_SERIALIZE + fprintf(stderr, "Serialize: " + "raster size = %d, ncolors in cmap = %d, total bytes = %d\n", + rdatasize, ncolors, nbytes); +#endif /* DEBUG_SERIALIZE */ + + LEPT_FREE(cdata); + return 0; +} + + +/*! + * \brief pixDeserializeFromMemory() + * + * \param[in] data serialized data in memory + * \param[in] nbytes number of bytes in data string + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixSerializeToMemory() for the binary format.
+ *      (2) Note the image size limits.
+ * 
+ */ +PIX * +pixDeserializeFromMemory(const l_uint32 *data, + size_t nbytes) +{ +char *id; +l_int32 w, h, d, pixdata_size, memdata_size, imdata_size, ncolors; +l_uint32 *imdata; /* data in pix raster */ +PIX *pix1, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixDeserializeFromMemory"); + + if (!data) + return (PIX *)ERROR_PTR("data not defined", procName, NULL); + if (nbytes < 28 || nbytes > ((1LL << 31) - 1)) { + L_ERROR("invalid nbytes = %zu\n", procName, nbytes); + return NULL; + } + + id = (char *)data; + if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x') + return (PIX *)ERROR_PTR("invalid id string", procName, NULL); + w = data[1]; + h = data[2]; + d = data[3]; + ncolors = data[5]; + + /* Sanity checks on the amount of image data */ + if (w < 1 || w > MaxAllowedWidth) + return (PIX *)ERROR_PTR("invalid width", procName, NULL); + if (h < 1 || h > MaxAllowedHeight) + return (PIX *)ERROR_PTR("invalid height", procName, NULL); + if (1LL * w * h > MaxAllowedArea) + return (PIX *)ERROR_PTR("area too large", procName, NULL); + if (ncolors < 0 || ncolors > 256 || ncolors + 6 >= nbytes/sizeof(l_int32)) + return (PIX *)ERROR_PTR("invalid ncolors", procName, NULL); + if ((pix1 = pixCreateHeader(w, h, d)) == NULL) /* just make the header */ + return (PIX *)ERROR_PTR("failed to make header", procName, NULL); + pixdata_size = 4 * h * pixGetWpl(pix1); + memdata_size = nbytes - 24 - 4 * ncolors - 4; + imdata_size = data[6 + ncolors]; + pixDestroy(&pix1); + if (pixdata_size != memdata_size || pixdata_size != imdata_size) { + L_ERROR("pixdata_size = %d, memdata_size = %d, imdata_size = %d " + "not all equal!\n", procName, pixdata_size, memdata_size, + imdata_size); + return NULL; + } + + if ((pixd = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + if (ncolors > 0) { + cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), 4, ncolors); + if (!cmap) { + pixDestroy(&pixd); + return (PIX *)ERROR_PTR("cmap not made", procName, NULL); + } + pixSetColormap(pixd, cmap); + } + + imdata = pixGetData(pixd); + memcpy(imdata, data + 7 + ncolors, imdata_size); + +#if DEBUG_SERIALIZE + fprintf(stderr, "Deserialize: " + "raster size = %d, ncolors in cmap = %d, total bytes = %zu\n", + imdata_size, ncolors, nbytes); +#endif /* DEBUG_SERIALIZE */ + + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/stack.c b/hgdriver/3rdparty/hgOCR/leptonica/stack.c new file mode 100644 index 0000000..86dc85d --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/stack.c @@ -0,0 +1,289 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file stack.c + *
+ *
+ *      Generic stack
+ *
+ *      The lstack is an array of void * ptrs, onto which
+ *      objects can be stored.  At any time, the number of
+ *      stored objects is lstack->n.  The object at the bottom
+ *      of the lstack is at array[0]; the object at the top of
+ *      the lstack is at array[n-1].  New objects are added
+ *      to the top of the lstack; i.e., the first available
+ *      location, which is at array[n].  The lstack is expanded
+ *      by doubling, when needed.  Objects are removed
+ *      from the top of the lstack.  When an attempt is made
+ *      to remove an object from an empty lstack, the result is null.
+ *
+ *      Create/Destroy
+ *           L_STACK        *lstackCreate()
+ *           void            lstackDestroy()
+ *
+ *      Accessors
+ *           l_int32         lstackAdd()
+ *           void           *lstackRemove()
+ *           static l_int32  lstackExtendArray()
+ *           l_int32         lstackGetCount()
+ *
+ *      Text description
+ *           l_int32         lstackPrint()
+ * 
+ */ + +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + + /* Static function */ +static l_int32 lstackExtendArray(L_STACK *lstack); + + +/*---------------------------------------------------------------------* + * Create/Destroy * + *---------------------------------------------------------------------*/ +/*! + * \brief lstackCreate() + * + * \param[in] n initial ptr array size; use 0 for default + * \return lstack, or NULL on error + */ +L_STACK * +lstackCreate(l_int32 n) +{ +L_STACK *lstack; + + PROCNAME("lstackCreate"); + + if (n <= 0 || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + lstack = (L_STACK *)LEPT_CALLOC(1, sizeof(L_STACK)); + lstack->array = (void **)LEPT_CALLOC(n, sizeof(void *)); + if (!lstack->array) { + lstackDestroy(&lstack, FALSE); + return (L_STACK *)ERROR_PTR("lstack array not made", procName, NULL); + } + + lstack->nalloc = n; + lstack->n = 0; + return lstack; +} + + +/*! + * \brief lstackDestroy() + * + * \param[in,out] plstack will be set to null before returning + * \param[in] freeflag TRUE to free each remaining struct in the array + * \return void + * + *
+ * Notes:
+ *      (1) If %freeflag is TRUE, frees each struct in the array.
+ *      (2) If %freeflag is FALSE but there are elements on the array,
+ *          gives a warning and destroys the array.  This will
+ *          cause a memory leak of all the items that were on the lstack.
+ *          So if the items require their own destroy function, they
+ *          must be destroyed before the lstack.
+ *      (3) To destroy the lstack, we destroy the ptr array, then
+ *          the lstack, and then null the contents of the input ptr.
+ * 
+ */ +void +lstackDestroy(L_STACK **plstack, + l_int32 freeflag) +{ +void *item; +L_STACK *lstack; + + PROCNAME("lstackDestroy"); + + if (plstack == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((lstack = *plstack) == NULL) + return; + + if (freeflag) { + while(lstack->n > 0) { + item = lstackRemove(lstack); + LEPT_FREE(item); + } + } else if (lstack->n > 0) { + L_WARNING("memory leak of %d items in lstack\n", procName, lstack->n); + } + + if (lstack->auxstack) + lstackDestroy(&lstack->auxstack, freeflag); + + if (lstack->array) + LEPT_FREE(lstack->array); + LEPT_FREE(lstack); + *plstack = NULL; +} + + + +/*---------------------------------------------------------------------* + * Accessors * + *---------------------------------------------------------------------*/ +/*! + * \brief lstackAdd() + * + * \param[in] lstack + * \param[in] item to be added to the lstack + * \return 0 if OK; 1 on error. + */ +l_ok +lstackAdd(L_STACK *lstack, + void *item) +{ + PROCNAME("lstackAdd"); + + if (!lstack) + return ERROR_INT("lstack not defined", procName, 1); + if (!item) + return ERROR_INT("item not defined", procName, 1); + + /* Do we need to extend the array? */ + if (lstack->n >= lstack->nalloc) + lstackExtendArray(lstack); + + /* Store the new pointer */ + lstack->array[lstack->n] = (void *)item; + lstack->n++; + + return 0; +} + + +/*! + * \brief lstackRemove() + * + * \param[in] lstack + * \return ptr to item popped from the top of the lstack, + * or NULL if the lstack is empty or on error + */ +void * +lstackRemove(L_STACK *lstack) +{ +void *item; + + PROCNAME("lstackRemove"); + + if (!lstack) + return ERROR_PTR("lstack not defined", procName, NULL); + + if (lstack->n == 0) + return NULL; + + lstack->n--; + item = lstack->array[lstack->n]; + + return item; +} + + +/*! + * \brief lstackExtendArray() + * + * \param[in] lstack + * \return 0 if OK; 1 on error + */ +static l_int32 +lstackExtendArray(L_STACK *lstack) +{ + PROCNAME("lstackExtendArray"); + + if (!lstack) + return ERROR_INT("lstack not defined", procName, 1); + + if ((lstack->array = (void **)reallocNew((void **)&lstack->array, + sizeof(void *) * lstack->nalloc, + 2 * sizeof(void *) * lstack->nalloc)) == NULL) + return ERROR_INT("new lstack array not defined", procName, 1); + + lstack->nalloc = 2 * lstack->nalloc; + return 0; +} + + +/*! + * \brief lstackGetCount() + * + * \param[in] lstack + * \return count, or 0 on error + */ +l_int32 +lstackGetCount(L_STACK *lstack) +{ + PROCNAME("lstackGetCount"); + + if (!lstack) + return ERROR_INT("lstack not defined", procName, 1); + + return lstack->n; +} + + + +/*---------------------------------------------------------------------* + * Debug output * + *---------------------------------------------------------------------*/ +/*! + * \brief lstackPrint() + * + * \param[in] fp file stream + * \param[in] lstack + * \return 0 if OK; 1 on error + */ +l_ok +lstackPrint(FILE *fp, + L_STACK *lstack) +{ +l_int32 i; + + PROCNAME("lstackPrint"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!lstack) + return ERROR_INT("lstack not defined", procName, 1); + + fprintf(fp, "\n Stack: nalloc = %d, n = %d, array = %p\n", + lstack->nalloc, lstack->n, lstack->array); + for (i = 0; i < lstack->n; i++) + fprintf(fp, "array[%d] = %p\n", i, lstack->array[i]); + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/stack.h b/hgdriver/3rdparty/hgOCR/leptonica/stack.h new file mode 100644 index 0000000..4fa6114 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/stack.h @@ -0,0 +1,70 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_STACK_H +#define LEPTONICA_STACK_H + +/*! + * \file 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.
+ * 
+ */ + + + /*! Expandable pointer stack for arbitrary void* data. + * 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 */ + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/stringcode.c b/hgdriver/3rdparty/hgOCR/leptonica/stringcode.c new file mode 100644 index 0000000..0e8c6a0 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/stringcode.c @@ -0,0 +1,804 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file stringcode.c + *
+ *
+ *   Generation of code for storing and extracting serializable
+ *   leptonica objects (such as pixa, recog, ...).
+ *
+ *   The input is a set of files with serialized data.
+ *   The output is two files, that must be compiled and linked:
+ *     ~ autogen.*.c: code for base64 unencoding the strings and
+ *                    deserializing the result.
+ *     ~ autogen.*.h: function prototypes and base64 encoded strings
+ *                    of the input data
+ *
+ *   This should work for any data structures in leptonica that have
+ *   *Write() and *Read() serialization functions.  An array of 20
+ *   of these, including the Pix, is given below.  (The Pix is a special
+ *   case, because it is serialized by standardized compression
+ *   techniques, instead of a file format determined by leptonica.)
+ *
+ *   Each time the generator function is invoked, three sets of strings are
+ *   produced, which are written into their respective string arrays:
+ *     ~ string of serialized, gzipped and base 64 encoded data
+ *     ~ case string for base64 decoding, gunzipping and deserialization,
+ *       to return the data struct in memory
+ *     ~ description string for selecting which struct to return
+ *   To create the two output files, a finalize function is invoked.
+ *
+ *   There are two ways to do this, both shown in prog/autogentest1.c.
+ *     ~ Explicitly call strcodeGenerate() for each file with the
+ *       serialized data structure, followed by strcodeFinalize().
+ *     ~ Put the filenames of the serialized data structures in a file,
+ *       and call strcodeCreateFromFile().
+ *
+ *   The generated code in autogen.X.c and autogen.X.h (where X is an
+ *   integer supplied to strcodeCreate()) is then compiled, and the
+ *   original data can be regenerated using the function l_autodecode_X().
+ *   A test example is found in the two prog files:
+ *       prog/autogentest1.c  -- generates autogen.137.c, autogen.137.h
+ *       prog/autogentest2.c  -- uses autogen.137.c, autogen.137.h
+ *   In general, the generator (e.g., autogentest1) would be compiled and
+ *   run before compiling and running the application (e.g., autogentest2).
+ *
+ *       L_STRCODE       *strcodeCreate()
+ *       static void      strcodeDestroy()    (called as part of finalize)
+ *       void             strcodeCreateFromFile()
+ *       l_int32          strcodeGenerate()
+ *       void             strcodeFinalize()
+ *       l_int32          l_getStructStrFromFile()   (useful externally)
+ *
+ *   Static helpers
+ *       static l_int32   l_getIndexFromType()
+ *       static l_int32   l_getIndexFromStructname()
+ *       static l_int32   l_getIndexFromFile()
+ *       static char     *l_genDataString()
+ *       static char     *l_genCaseString()
+ *       static char     *l_genDescrString()
+ * 
+ */ + +#include +#include "allheaders.h" +#include "stringcode.h" + +#define TEMPLATE1 "stringtemplate1.txt" /* for assembling autogen.*.c */ +#define TEMPLATE2 "stringtemplate2.txt" /* for assembling autogen.*.h */ + + /*! Associations between names and functions */ +struct L_GenAssoc +{ + l_int32 index; + char type[16]; /* e.g., "PIXA" */ + char structname[16]; /* e.g., "Pixa" */ + char reader[16]; /* e.g., "pixaRead" */ + char memreader[20]; /* e.g., "pixaReadMem" */ +}; + + /*! Number of serializable data types */ +static const l_int32 l_ntypes = 19; + /*! Serializable data types */ +static const struct L_GenAssoc l_assoc[] = { + {0, "INVALID", "invalid", "invalid", "invalid" }, + {1, "BOXA", "Boxa", "boxaRead", "boxaReadMem" }, + {2, "BOXAA", "Boxaa", "boxaaRead", "boxaaReadMem" }, + {3, "L_DEWARP", "Dewarp", "dewarpRead", "dewarpReadMem" }, + {4, "L_DEWARPA", "Dewarpa", "dewarpaRead", "dewarpaReadMem" }, + {5, "L_DNA", "L_Dna", "l_dnaRead", "l_dnaReadMem" }, + {6, "L_DNAA", "L_Dnaa", "l_dnaaRead", "l_dnaaReadMem" }, + {7, "DPIX", "DPix", "dpixRead", "dpixReadMem" }, + {8, "FPIX", "FPix", "fpixRead", "fpixReadMem" }, + {9, "NUMA", "Numa", "numaRead", "numaReadMem" }, + {10, "NUMAA", "Numaa", "numaaRead", "numaaReadMem" }, + {11, "PIX", "Pix", "pixRead", "pixReadMem" }, + {12, "PIXA", "Pixa", "pixaRead", "pixaReadMem" }, + {13, "PIXAA", "Pixaa", "pixaaRead", "pixaaReadMem" }, + {14, "PIXACOMP", "Pixacomp", "pixacompRead", "pixacompReadMem" }, + {15, "PIXCMAP", "Pixcmap", "pixcmapRead", "pixcmapReadMem" }, + {16, "PTA", "Pta", "ptaRead", "ptaReadMem" }, + {17, "PTAA", "Ptaa", "ptaaRead", "ptaaReadMem" }, + {18, "RECOG", "Recog", "recogRead", "recogReadMem" }, + {19, "SARRAY", "Sarray", "sarrayRead", "sarrayReadMem" } +}; + +static l_int32 l_getIndexFromType(const char *type, l_int32 *pindex); +static l_int32 l_getIndexFromStructname(const char *sn, l_int32 *pindex); +static l_int32 l_getIndexFromFile(const char *file, l_int32 *pindex); +static char *l_genDataString(const char *filein, l_int32 ifunc); +static char *l_genCaseString(l_int32 ifunc, l_int32 itype); +static char *l_genDescrString(const char *filein, l_int32 ifunc, l_int32 itype); + + +/*---------------------------------------------------------------------*/ +/* Stringcode functions */ +/*---------------------------------------------------------------------*/ +/*! + * \brief strcodeCreate() + * + * \param[in] fileno integer that labels the two output files + * \return initialized L_StrCode, or NULL on error + * + *
+ * Notes:
+ *      (1) This struct exists to build two files containing code for
+ *          any number of data objects.  The two files are named
+ *             autogen.[fileno].c
+ *             autogen.[fileno].h
+ * 
+ */ +L_STRCODE * +strcodeCreate(l_int32 fileno) +{ +L_STRCODE *strcode; + + PROCNAME("strcodeCreate"); + + lept_mkdir("lept/auto"); + + if ((strcode = (L_STRCODE *)LEPT_CALLOC(1, sizeof(L_STRCODE))) == NULL) + return (L_STRCODE *)ERROR_PTR("strcode not made", procName, NULL); + + strcode->fileno = fileno; + strcode->function = sarrayCreate(0); + strcode->data = sarrayCreate(0); + strcode->descr = sarrayCreate(0); + return strcode; +} + + +/*! + * \brief strcodeDestroy() + * + * \param[out] pstrcode will be set to null after destroying the sarrays + * \return void + */ +static void +strcodeDestroy(L_STRCODE **pstrcode) +{ +L_STRCODE *strcode; + + PROCNAME("strcodeDestroy"); + + if (pstrcode == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((strcode = *pstrcode) == NULL) + return; + + sarrayDestroy(&strcode->function); + sarrayDestroy(&strcode->data); + sarrayDestroy(&strcode->descr); + LEPT_FREE(strcode); + *pstrcode = NULL; + return; +} + + +/*! + * \brief strcodeCreateFromFile() + * + * \param[in] filein containing filenames of serialized data + * \param[in] fileno integer that labels the two output files + * \param[in] outdir [optional] if null, files are made in /tmp/lept/auto + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The %filein has one filename on each line.
+ *          Comment lines begin with "#".
+ *      (2) The output is 2 files:
+ *             autogen.[fileno].c
+ *             autogen.[fileno].h
+ * 
+ */ +l_ok +strcodeCreateFromFile(const char *filein, + l_int32 fileno, + const char *outdir) +{ +char *fname; +const char *type; +l_uint8 *data; +size_t nbytes; +l_int32 i, n, index; +SARRAY *sa; +L_STRCODE *strcode; + + PROCNAME("strcodeCreateFromFile"); + + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + + if ((data = l_binaryRead(filein, &nbytes)) == NULL) + return ERROR_INT("data not read from file", procName, 1); + sa = sarrayCreateLinesFromString((char *)data, 0); + LEPT_FREE(data); + if (!sa) + return ERROR_INT("sa not made", procName, 1); + if ((n = sarrayGetCount(sa)) == 0) { + sarrayDestroy(&sa); + return ERROR_INT("no filenames in the file", procName, 1); + } + + strcode = strcodeCreate(fileno); + + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + if (fname[0] == '#') continue; + if (l_getIndexFromFile(fname, &index)) { + L_ERROR("File %s has no recognizable type\n", procName, fname); + } else { + type = l_assoc[index].type; + L_INFO("File %s is type %s\n", procName, fname, type); + strcodeGenerate(strcode, fname, type); + } + } + strcodeFinalize(&strcode, outdir); + sarrayDestroy(&sa); + return 0; +} + + +/*! + * \brief strcodeGenerate() + * + * \param[in] strcode for accumulating data + * \param[in] filein input file with serialized data + * \param[in] type of data; use the typedef string + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) The generated function name is
+ *            l_autodecode_[fileno]()
+ *          where [fileno] is the index label for the pair of output files.
+ *      (2) To deserialize this data, the function is called with the
+ *          argument 'ifunc', which increments each time strcodeGenerate()
+ *          is called.
+ * 
+ */ +l_ok +strcodeGenerate(L_STRCODE *strcode, + const char *filein, + const char *type) +{ +char *strdata, *strfunc, *strdescr; +l_int32 itype; + + PROCNAME("strcodeGenerate"); + + if (!strcode) + return ERROR_INT("strcode not defined", procName, 1); + if (!filein) + return ERROR_INT("filein not defined", procName, 1); + if (!type) + return ERROR_INT("type not defined", procName, 1); + + /* Get the index corresponding to type and validate */ + if (l_getIndexFromType(type, &itype) == 1) + return ERROR_INT("data type unknown", procName, 1); + + /* Generate the encoded data string */ + if ((strdata = l_genDataString(filein, strcode->ifunc)) == NULL) + return ERROR_INT("strdata not made", procName, 1); + sarrayAddString(strcode->data, strdata, L_INSERT); + + /* Generate the case data for the decoding function */ + strfunc = l_genCaseString(strcode->ifunc, itype); + sarrayAddString(strcode->function, strfunc, L_INSERT); + + /* Generate row of table for function type selection */ + strdescr = l_genDescrString(filein, strcode->ifunc, itype); + sarrayAddString(strcode->descr, strdescr, L_INSERT); + + strcode->n++; + strcode->ifunc++; + return 0; +} + + +/*! + * \brief strcodeFinalize() + * + * \param[in,out] pstrcode destroys and sets to null after .c and .h files + * have been generated + * \param[in] outdir [optional] if NULL, make files in /tmp/lept/auto + * \return void + */ +l_int32 +strcodeFinalize(L_STRCODE **pstrcode, + const char *outdir) +{ +char buf[256]; +char *filestr, *casestr, *descr, *datastr, *realoutdir; +l_int32 actstart, end, newstart, fileno, nbytes; +size_t size; +L_STRCODE *strcode; +SARRAY *sa1, *sa2, *sa3; + + PROCNAME("strcodeFinalize"); + + lept_mkdir("lept/auto"); + + if (!pstrcode || *pstrcode == NULL) + return ERROR_INT("No input data", procName, 1); + strcode = *pstrcode; + if (!outdir) { + L_INFO("no outdir specified; writing to /tmp/lept/auto\n", procName); + realoutdir = stringNew("/tmp/lept/auto"); + } else { + realoutdir = stringNew(outdir); + } + + /* ------------------------------------------------------- */ + /* Make the output autogen.*.c file */ + /* ------------------------------------------------------- */ + + /* Make array of textlines from TEMPLATE1 */ + filestr = (char *)l_binaryRead(TEMPLATE1, &size); + sa1 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + sa3 = sarrayCreate(0); + + /* Copyright notice */ + sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* File name comment */ + fileno = strcode->fileno; + snprintf(buf, sizeof(buf), " * autogen.%d.c", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* More text */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Description of function types by index */ + descr = sarrayToString(strcode->descr, 1); + descr[strlen(descr) - 1] = '\0'; + sarrayAddString(sa3, descr, L_INSERT); + + /* Includes */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + snprintf(buf, sizeof(buf), "#include \"autogen.%d.h\"", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Header for auto-generated deserializers */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Function name (as comment) */ + snprintf(buf, sizeof(buf), " * \\brief l_autodecode_%d()", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Input and return values */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Function name */ + snprintf(buf, sizeof(buf), "l_autodecode_%d(l_int32 index)", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Stack vars */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Declaration of nfunc on stack */ + snprintf(buf, sizeof(buf), "l_int32 nfunc = %d;\n", strcode->n); + sarrayAddString(sa3, buf, L_COPY); + + /* Declaration of PROCNAME */ + snprintf(buf, sizeof(buf), " PROCNAME(\"l_autodecode_%d\");", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Test input variables */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Insert case string */ + casestr = sarrayToString(strcode->function, 0); + casestr[strlen(casestr) - 1] = '\0'; + sarrayAddString(sa3, casestr, L_INSERT); + + /* End of function */ + sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa1, actstart, end); + + /* Flatten to string and output to autogen*.c file */ + filestr = sarrayToString(sa3, 1); + nbytes = strlen(filestr); + snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno); + l_binaryWrite(buf, "w", filestr, nbytes); + LEPT_FREE(filestr); + sarrayDestroy(&sa1); + sarrayDestroy(&sa3); + + /* ------------------------------------------------------- */ + /* Make the output autogen.*.h file */ + /* ------------------------------------------------------- */ + + /* Make array of textlines from TEMPLATE2 */ + filestr = (char *)l_binaryRead(TEMPLATE2, &size); + sa2 = sarrayCreateLinesFromString(filestr, 1); + LEPT_FREE(filestr); + sa3 = sarrayCreate(0); + + /* Copyright notice */ + sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* File name comment */ + snprintf(buf, sizeof(buf), " * autogen.%d.h", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* More text */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Beginning header protection */ + snprintf(buf, sizeof(buf), "#ifndef LEPTONICA_AUTOGEN_%d_H\n" + "#define LEPTONICA_AUTOGEN_%d_H", + fileno, fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Prototype header text */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Prototype declaration */ + snprintf(buf, sizeof(buf), "void *l_autodecode_%d(l_int32 index);", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Prototype trailer text */ + sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); + sarrayAppendRange(sa3, sa2, actstart, end); + + /* Insert serialized data strings */ + datastr = sarrayToString(strcode->data, 1); + datastr[strlen(datastr) - 1] = '\0'; + sarrayAddString(sa3, datastr, L_INSERT); + + /* End header protection */ + snprintf(buf, sizeof(buf), "#endif /* LEPTONICA_AUTOGEN_%d_H */", fileno); + sarrayAddString(sa3, buf, L_COPY); + + /* Flatten to string and output to autogen*.h file */ + filestr = sarrayToString(sa3, 1); + nbytes = strlen(filestr); + snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno); + l_binaryWrite(buf, "w", filestr, nbytes); + LEPT_FREE(filestr); + LEPT_FREE(realoutdir); + sarrayDestroy(&sa2); + sarrayDestroy(&sa3); + + /* Cleanup */ + strcodeDestroy(pstrcode); + return 0; +} + + +/*! + * \brief l_getStructStrFromFile() + * + * \param[in] filename + * \param[in] field (L_STR_TYPE, L_STR_NAME, L_STR_READER, L_STR_MEMREADER) + * \param[out] pstr struct string for this file + * \return 0 if found, 1 on error. + * + *
+ * Notes:
+ *      (1) For example, if %field == L_STR_NAME, and the file is a serialized
+ *          pixa, this will return "Pixa", the name of the struct.
+ *      (2) Caller must free the returned string.
+ * 
+ */ +l_int32 +l_getStructStrFromFile(const char *filename, + l_int32 field, + char **pstr) +{ +l_int32 index; + + PROCNAME("l_getStructStrFromFile"); + + if (!pstr) + return ERROR_INT("&str not defined", procName, 1); + *pstr = NULL; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (field != L_STR_TYPE && field != L_STR_NAME && + field != L_STR_READER && field != L_STR_MEMREADER) + return ERROR_INT("invalid field", procName, 1); + + if (l_getIndexFromFile(filename, &index)) + return ERROR_INT("index not retrieved", procName, 1); + if (field == L_STR_TYPE) + *pstr = stringNew(l_assoc[index].type); + else if (field == L_STR_NAME) + *pstr = stringNew(l_assoc[index].structname); + else if (field == L_STR_READER) + *pstr = stringNew(l_assoc[index].reader); + else /* field == L_STR_MEMREADER */ + *pstr = stringNew(l_assoc[index].memreader); + return 0; +} + + +/*---------------------------------------------------------------------*/ +/* Static helpers */ +/*---------------------------------------------------------------------*/ +/*! + * \brief l_getIndexFromType() + * + * \param[in] type e.g., "PIXA" + * \param[out] pindex found index + * \return 0 if found, 1 if not. + * + *
+ * Notes:
+ *      (1) For valid type, %found == true and %index > 0.
+ * 
+ */ +static l_int32 +l_getIndexFromType(const char *type, + l_int32 *pindex) +{ +l_int32 i, found; + + PROCNAME("l_getIndexFromType"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!type) + return ERROR_INT("type string not defined", procName, 1); + + found = 0; + for (i = 1; i <= l_ntypes; i++) { + if (strcmp(type, l_assoc[i].type) == 0) { + found = 1; + *pindex = i; + break; + } + } + return !found; +} + + +/*! + * \brief l_getIndexFromStructname() + * + * \param[in] sn structname e.g., "Pixa" + * \param[out] pindex found index + * \return 0 if found, 1 if not. + * + *
+ * Notes:
+ *      (1) This is used to identify the type of serialized file;
+ *          the first word in the file is the structname.
+ *      (2) For valid structname, %found == true and %index > 0.
+ * 
+ */ +static l_int32 +l_getIndexFromStructname(const char *sn, + l_int32 *pindex) +{ +l_int32 i, found; + + PROCNAME("l_getIndexFromStructname"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!sn) + return ERROR_INT("sn string not defined", procName, 1); + + found = 0; + for (i = 1; i <= l_ntypes; i++) { + if (strcmp(sn, l_assoc[i].structname) == 0) { + found = 1; + *pindex = i; + break; + } + } + return !found; +} + + +/*! + * \brief l_getIndexFromFile() + * + * \param[in] filename + * \param[out] pindex found index + * \return 0 if found, 1 on error. + */ +static l_int32 +l_getIndexFromFile(const char *filename, + l_int32 *pindex) +{ +char buf[256]; +char *word; +FILE *fp; +l_int32 notfound, format; +SARRAY *sa; + + PROCNAME("l_getIndexFromFile"); + + if (!pindex) + return ERROR_INT("&index not defined", procName, 1); + *pindex = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + /* Open the stream, read lines until you find one with more + * than a newline, and grab the first word. */ + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + do { + if ((fgets(buf, sizeof(buf), fp)) == NULL) { + fclose(fp); + return ERROR_INT("fgets read fail", procName, 1); + } + } while (buf[0] == '\n'); + fclose(fp); + sa = sarrayCreateWordsFromString(buf); + word = sarrayGetString(sa, 0, L_NOCOPY); + + /* Find the index associated with the word. If it is not + * found, test to see if the file is a compressed pix. */ + notfound = l_getIndexFromStructname(word, pindex); + sarrayDestroy(&sa); + if (notfound) { /* maybe a Pix */ + if (findFileFormat(filename, &format) == 0) { + l_getIndexFromStructname("Pix", pindex); + } else { + return ERROR_INT("no file type identified", procName, 1); + } + } + + return 0; +} + + +/*! + * \brief l_genDataString() + * + * \param[in] filein input file of serialized data + * \param[in] ifunc index into set of functions in output file + * \return encoded ascii data string, or NULL on error reading from file + */ +static char * +l_genDataString(const char *filein, + l_int32 ifunc) +{ +char buf[80]; +char *cdata1, *cdata2, *cdata3; +l_uint8 *data1, *data2; +l_int32 csize1, csize2; +size_t size1, size2; +SARRAY *sa; + + PROCNAME("l_genDataString"); + + if (!filein) + return (char *)ERROR_PTR("filein not defined", procName, NULL); + + /* Read it in, gzip it, encode, and reformat. We gzip because some + * serialized data has a significant amount of ascii content. */ + if ((data1 = l_binaryRead(filein, &size1)) == NULL) + return (char *)ERROR_PTR("bindata not returned", procName, NULL); + data2 = zlibCompress(data1, size1, &size2); + cdata1 = encodeBase64(data2, size2, &csize1); + cdata2 = reformatPacked64(cdata1, csize1, 4, 72, 1, &csize2); + LEPT_FREE(data1); + LEPT_FREE(data2); + LEPT_FREE(cdata1); + + /* Prepend the string declaration signature and put it together */ + sa = sarrayCreate(3); + snprintf(buf, sizeof(buf), "static const char *l_strdata_%d =\n", ifunc); + sarrayAddString(sa, buf, L_COPY); + sarrayAddString(sa, cdata2, L_INSERT); + sarrayAddString(sa, ";\n", L_COPY); + cdata3 = sarrayToString(sa, 0); + sarrayDestroy(&sa); + return cdata3; +} + + +/*! + * \brief l_genCaseString() + * + * \param[in] ifunc index into set of functions in generated file + * \param[in] itype index into type of function to be used + * \return case string for this decoding function + * + *
+ * Notes:
+ *      (1) %ifunc and %itype have been validated, so no error can occur
+ * 
+ */ +static char * +l_genCaseString(l_int32 ifunc, + l_int32 itype) +{ +char buf[256]; +char *code = NULL; + + snprintf(buf, sizeof(buf), " case %d:\n", ifunc); + stringJoinIP(&code, buf); + snprintf(buf, sizeof(buf), + " data1 = decodeBase64(l_strdata_%d, strlen(l_strdata_%d), " + "&size1);\n", ifunc, ifunc); + stringJoinIP(&code, buf); + stringJoinIP(&code, + " data2 = zlibUncompress(data1, size1, &size2);\n"); + snprintf(buf, sizeof(buf), + " result = (void *)%s(data2, size2);\n", + l_assoc[itype].memreader); + stringJoinIP(&code, buf); + stringJoinIP(&code, " lept_free(data1);\n"); + stringJoinIP(&code, " lept_free(data2);\n"); + stringJoinIP(&code, " break;\n"); + return code; +} + + +/*! + * \brief l_genDescrString() + * + * \param[in] filein input file of serialized data + * \param[in] ifunc index into set of functions in generated file + * \param[in] itype index into type of function to be used + * \return description string for this decoding function + */ +static char * +l_genDescrString(const char *filein, + l_int32 ifunc, + l_int32 itype) +{ +char buf[256]; +char *tail; + + PROCNAME("l_genDescrString"); + + if (!filein) + return (char *)ERROR_PTR("filein not defined", procName, NULL); + + splitPathAtDirectory(filein, NULL, &tail); + snprintf(buf, sizeof(buf), " * %-2d %-10s %-14s %s", + ifunc, l_assoc[itype].type, l_assoc[itype].reader, tail); + + LEPT_FREE(tail); + return stringNew(buf); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/stringcode.h b/hgdriver/3rdparty/hgOCR/leptonica/stringcode.h new file mode 100644 index 0000000..4510bdb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/stringcode.h @@ -0,0 +1,61 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_STRINGCODE_H +#define LEPTONICA_STRINGCODE_H + +/*! + * \file stringcode.h + * + * Data structure to hold accumulating generated code for storing + * and extracting serializable leptonica objects (e.g., pixa, recog). + * + * Also a flag for selecting a string from the L_GenAssoc struct + * in stringcode. + */ + +struct L_StrCode +{ + l_int32 fileno; /*!< index for function and output file names */ + l_int32 ifunc; /*!< index into struct currently being stored */ + SARRAY *function; /*!< store case code for extraction */ + SARRAY *data; /*!< store base64 encoded data as strings */ + SARRAY *descr; /*!< store line in description table */ + l_int32 n; /*!< number of data strings */ +}; +typedef struct L_StrCode L_STRCODE; + + + /*! Select string in stringcode for a specific serializable data type */ +/*! Stringcode Select */ +enum { + L_STR_TYPE = 0, /*!< typedef for the data type */ + L_STR_NAME = 1, /*!< name of the data type */ + L_STR_READER = 2, /*!< reader to get the data type from file */ + L_STR_MEMREADER = 3 /*!< reader to get the compressed string in memory */ +}; + +#endif /* LEPTONICA_STRINGCODE_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/strokes.c b/hgdriver/3rdparty/hgOCR/leptonica/strokes.c new file mode 100644 index 0000000..cf57314 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/strokes.c @@ -0,0 +1,435 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file strokes.c + *
+ *
+ *      Operations on 1 bpp images to:
+ *      (1) measure stroke parameters, such as length and average width
+ *      (2) change the average stroke width to a given value by eroding
+ *          or dilating the image.
+ *
+ *      These operations are intended to operate on a single text
+ *      character, to regularize the stroke width. It is expected
+ *      that character matching by correlation, as used in the recog
+ *      application, can often be improved by pre-processing both
+ *      template and character images to a fixed stroke width.
+ *
+ *      Stroke parameter measurement
+ *            l_int32      pixFindStrokeLength()
+ *            l_int32      pixFindStrokeWidth()
+ *            NUMA        *pixaFindStrokeWidth()
+ *
+ *      Stroke width regulation
+ *            PIXA        *pixaModifyStrokeWidth()
+ *            PIX         *pixModifyStrokeWidth()
+ *            PIXA        *pixaSetStrokeWidth()
+ *            PIX         *pixSetStrokeWidth()
+ * 
+ */ + +#include "allheaders.h" + +/*-----------------------------------------------------------------* + * Stroke parameter measurement * + *-----------------------------------------------------------------*/ +/*! + * \brief pixFindStrokeLength() + * + * \param[in] pixs 1 bpp + * \param[in] tab8 [optional] table for counting fg pixels; can be NULL + * \param[out] plength estimated length of the strokes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Returns half the number of fg boundary pixels.
+ * 
+ */ +l_ok +pixFindStrokeLength(PIX *pixs, + l_int32 *tab8, + l_int32 *plength) +{ +l_int32 n; +l_int32 *tab; +PIX *pix1; + + PROCNAME("pixFindStrokeLength"); + + if (!plength) + return ERROR_INT("&length not defined", procName, 1); + *plength = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pix1 = pixExtractBoundary(pixs, 1); + tab = (tab8) ? tab8 : makePixelSumTab8(); + pixCountPixels(pix1, &n, tab); + *plength = n / 2; + if (!tab8) LEPT_FREE(tab); + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief pixFindStrokeWidth() + * + * \param[in] pixs 1 bpp + * \param[in] thresh fractional count threshold relative to distance 1 + * \param[in] tab8 [optional] table for counting fg pixels; can be NULL + * \param[out] pwidth estimated width of the strokes + * \param[out] pnahisto [optional] histo of pixel distances from bg + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This uses two methods to estimate the stroke width:
+ *          (a) half the fg boundary length
+ *          (b) a value derived from the histogram of the fg distance transform
+ *      (2) Distance is measured in 8-connected
+ *      (3) %thresh is the minimum fraction N(dist=d)/N(dist=1) of pixels
+ *          required to determine if the pixels at distance d are above
+ *          the noise. It is typically about 0.15.
+ * 
+ */ +l_ok +pixFindStrokeWidth(PIX *pixs, + l_float32 thresh, + l_int32 *tab8, + l_float32 *pwidth, + NUMA **pnahisto) +{ +l_int32 i, n, count, length, first, last; +l_int32 *tab; +l_float32 width1, width2, ratio, extra; +l_float32 *fa; +NUMA *na1, *na2; +PIX *pix1; + + PROCNAME("pixFindStrokeWidth"); + + if (!pwidth) + return ERROR_INT("&width not defined", procName, 1); + *pwidth = 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + + /* ------- Method 1: via boundary length ------- */ + /* The computed stroke length is a bit larger than that actual + * length, because of the addition of the 'caps' at the + * stroke ends. Therefore the computed width is a bit + * smaller than the average width. */ + pixFindStrokeLength(pixs, tab8, &length); + pixCountPixels(pixs, &count, tab8); + width1 = (l_float32)count / (l_float32)length; + + /* ------- Method 2: via distance transform ------- */ + /* First get the histogram of distances */ + pix1 = pixDistanceFunction(pixs, 8, 8, L_BOUNDARY_BG); + na1 = pixGetGrayHistogram(pix1, 1); + pixDestroy(&pix1); + numaGetNonzeroRange(na1, 0.1, &first, &last); + na2 = numaClipToInterval(na1, 0, last); + numaWriteStream(stderr, na2); + + /* Find the bucket with the largest distance whose contents + * exceed the threshold. */ + fa = numaGetFArray(na2, L_NOCOPY); + n = numaGetCount(na2); + for (i = n - 1; i > 0; i--) { + ratio = fa[i] / fa[1]; + if (ratio > thresh) break; + } + /* Let the last skipped bucket contribute to the stop bucket. + * This is the 'extra' term below. The result may be a slight + * over-correction, so the computed width may be a bit larger + * than the average width. */ + extra = (i < n - 1) ? fa[i + 1] / fa[1] : 0; + width2 = 2.0 * (i - 1.0 + ratio + extra); + fprintf(stderr, "width1 = %5.2f, width2 = %5.2f\n", width1, width2); + + /* Average the two results */ + *pwidth = (width1 + width2) / 2.0; + + if (!tab8) LEPT_FREE(tab); + numaDestroy(&na1); + if (pnahisto) + *pnahisto = na2; + else + numaDestroy(&na2); + return 0; +} + + +/*! + * \brief pixaFindStrokeWidth() + * + * \param[in] pixa of 1 bpp images + * \param[in] thresh fractional count threshold relative to distance 1 + * \param[in] tab8 [optional] table for counting fg pixels; can be NULL + * \param[in] debug 1 for debug output; 0 to skip + * \return na array of stroke widths for each pix in %pixa; NULL on error + * + *
+ * Notes:
+ *      (1) See pixFindStrokeWidth() for details.
+ * 
+ */ +NUMA * +pixaFindStrokeWidth(PIXA *pixa, + l_float32 thresh, + l_int32 *tab8, + l_int32 debug) +{ +l_int32 i, n, same, maxd; +l_int32 *tab; +l_float32 width; +NUMA *na; +PIX *pix; + + PROCNAME("pixaFindStrokeWidth"); + + if (!pixa) + return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); + pixaVerifyDepth(pixa, &same, &maxd); + if (maxd > 1) + return (NUMA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); + + tab = (tab8) ? tab8 : makePixelSumTab8(); + + n = pixaGetCount(pixa); + na = numaCreate(n); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixFindStrokeWidth(pix, thresh, tab8, &width, NULL); + numaAddNumber(na, width); + pixDestroy(&pix); + } + + if (!tab8) LEPT_FREE(tab); + return na; +} + + +/*-----------------------------------------------------------------* + * Change stroke width * + *-----------------------------------------------------------------*/ +/*! + * \brief pixaModifyStrokeWidth() + * + * \param[in] pixas of 1 bpp pix + * \param[out] targetw desired width for strokes in each pix + * \return pixa with modified stroke widths, or NULL on error + */ +PIXA * +pixaModifyStrokeWidth(PIXA *pixas, + l_float32 targetw) +{ +l_int32 i, n, same, maxd; +l_float32 width; +NUMA *na; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaModifyStrokeWidth"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (targetw < 1) + return (PIXA *)ERROR_PTR("target width < 1", procName, NULL); + pixaVerifyDepth(pixas, &same, &maxd); + if (maxd > 1) + return (PIXA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); + + na = pixaFindStrokeWidth(pixas, 0.1, NULL, 0); + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + numaGetFValue(na, i, &width); + pix2 = pixModifyStrokeWidth(pix1, width, targetw); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + numaDestroy(&na); + return pixad; +} + + +/*! + * \brief pixModifyStrokeWidth() + * + * \param[in] pixs of 1 bpp pix + * \param[in] width measured average stroke width + * \param[in] targetw desired stroke width + * \return pix with modified stroke width, or NULL on error + */ +PIX * +pixModifyStrokeWidth(PIX *pixs, + l_float32 width, + l_float32 targetw) +{ +char buf[32]; +l_int32 diff, size; + + PROCNAME("pixModifyStrokeWidth"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (targetw < 1) + return (PIX *)ERROR_PTR("target width < 1", procName, NULL); + + diff = lept_roundftoi(targetw - width); + if (diff == 0) return pixCopy(NULL, pixs); + + size = L_ABS(diff) + 1; + if (diff < 0) /* erode */ + snprintf(buf, sizeof(buf), "e%d.%d", size, size); + else /* diff > 0; dilate */ + snprintf(buf, sizeof(buf), "d%d.%d", size, size); + return pixMorphSequence(pixs, buf, 0); +} + + +/*! + * \brief pixaSetStrokeWidth() + * + * \param[in] pixas of 1 bpp pix + * \param[in] width set stroke width to this value, in [1 ... 100]. + * \param[in] thinfirst 1 to thin all pix to a skeleton first; 0 to skip + * \param[in] connectivity 4 or 8, to be used if %thinfirst == 1 + * \return pixa with all stroke widths being %width, or NULL on error + * + *
+ * Notes:
+ *      (1) If %thinfirst == 1, thin to a skeleton using the specified
+ *          %connectivity.  Use %thinfirst == 0 if all pix in pixas
+ *          have already been thinned as far as possible.
+ *      (2) The image is dilated to the required %width.  This dilation
+ *          is not connectivity preserving, so this is typically
+ *          used in a situation where merging of c.c. in the individual
+ *          pix is not a problem; e.g., where each pix is a single c.c.
+ * 
+ */ +PIXA * +pixaSetStrokeWidth(PIXA *pixas, + l_int32 width, + l_int32 thinfirst, + l_int32 connectivity) +{ +l_int32 i, n, maxd, same; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaSetStrokeWidth"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (width < 1 || width > 100) + return (PIXA *)ERROR_PTR("width not in [1 ... 100]", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + pixaVerifyDepth(pixas, &same, &maxd); + if (maxd > 1) + return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + pix2 = pixSetStrokeWidth(pix1, width, thinfirst, connectivity); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + return pixad; +} + + +/*! + * \brief pixSetStrokeWidth() + * + * \param[in] pixs 1 bpp + * \param[in] width set stroke width to this value, in [1 ... 100]. + * \param[in] thinfirst 1 to thin all pix to a skeleton first; 0 to skip + * \param[in] connectivity 4 or 8, to be used if %thinfirst == 1 + * \return pixd with stroke width set to %width, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes in pixaSetStrokeWidth().
+ *      (2) A white border of sufficient width to avoid boundary
+ *          artifacts in the thickening step is added before thinning.
+ *      (3) %connectivity == 8 usually gives a slightly smoother result.
+ * 
+ */ +PIX * +pixSetStrokeWidth(PIX *pixs, + l_int32 width, + l_int32 thinfirst, + l_int32 connectivity) +{ +char buf[16]; +l_int32 border; +PIX *pix1, *pix2, *pixd; + + PROCNAME("pixSetStrokeWidth"); + + if (!pixs || (pixGetDepth(pixs) != 1)) + return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); + if (width < 1 || width > 100) + return (PIX *)ERROR_PTR("width not in [1 ... 100]", procName, NULL); + if (connectivity != 4 && connectivity != 8) + return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); + + if (!thinfirst && width == 1) /* nothing to do */ + return pixCopy(NULL, pixs); + + /* Add a white border */ + border = width / 2; + pix1 = pixAddBorder(pixs, border, 0); + + /* Thin to a skeleton */ + if (thinfirst) + pix2 = pixThinConnected(pix1, L_THIN_FG, connectivity, 0); + else + pix2 = pixClone(pix1); + pixDestroy(&pix1); + + /* Dilate */ + snprintf(buf, sizeof(buf), "D%d.%d", width, width); + pixd = pixMorphSequence(pix2, buf, 0); + pixCopyText(pixd, pixs); + pixDestroy(&pix2); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sudoku.c b/hgdriver/3rdparty/hgOCR/leptonica/sudoku.c new file mode 100644 index 0000000..9bc1200 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sudoku.c @@ -0,0 +1,881 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file sudoku.c + *
+ *
+ *      Solve a sudoku by brute force search
+ *
+ *      Read input data from file or string
+ *          l_int32         *sudokuReadFile()
+ *          l_int32         *sudokuReadString()
+ *
+ *      Create/destroy
+ *          L_SUDOKU        *sudokuCreate()
+ *          void             sudokuDestroy()
+ *
+ *      Solve the puzzle
+ *          l_int32          sudokuSolve()
+ *          static l_int32   sudokuValidState()
+ *          static l_int32   sudokuNewGuess()
+ *          static l_int32   sudokuTestState()
+ *
+ *      Test for uniqueness
+ *          l_int32          sudokuTestUniqueness()
+ *          static l_int32   sudokuCompareState()
+ *          static l_int32  *sudokuRotateArray()
+ *
+ *      Generation
+ *          L_SUDOKU        *sudokuGenerate()
+ *
+ *      Output
+ *          l_int32          sudokuOutput()
+ *
+ *  Solving sudokus is a somewhat addictive pastime.  The rules are
+ *  simple but it takes just enough concentration to make it rewarding
+ *  when you find a number.  And you get 50 to 60 such rewards each time
+ *  you complete one.  The downside is that you could have been doing
+ *  something more creative, like keying out a new plant, staining
+ *  the deck, or even writing a computer program to discourage your
+ *  wife from doing sudokus.
+ *
+ *  My original plan for the sudoku solver was somewhat grandiose.
+ *  The program would model the way a person solves the problem.
+ *  It would examine each empty position and determine how many possible
+ *  numbers could fit.  The empty positions would be entered in a priority
+ *  queue keyed on the number of possible numbers that could fit.
+ *  If there existed a position where only a single number would work,
+ *  it would greedily take it.  Otherwise it would consider a
+ *  positions that could accept two and make a guess, with backtracking
+ *  if an impossible state were reached.  And so on.
+ *
+ *  Then one of my colleagues announced she had solved the problem
+ *  by brute force and it was fast.  At that point the original plan was
+ *  dead in the water, because the two top requirements for a leptonica
+ *  algorithm are (1) as simple as possible and (2) fast.  The brute
+ *  force approach starts at the UL corner, and in succession at each
+ *  blank position it finds the first valid number (testing in
+ *  sequence from 1 to 9).  When no number will fit a blank position
+ *  it backtracks, choosing the next valid number in the previous
+ *  blank position.
+ *
+ *  This is an inefficient method for pruning the space of solutions
+ *  (imagine backtracking from the LR corner back to the UL corner
+ *  and starting over with a new guess), but it nevertheless gets
+ *  the job done quickly.  I have made no effort to optimize
+ *  it, because it is fast: a 5-star (highest difficulty) sudoku might
+ *  require a million guesses and take 0.05 sec.  (This BF implementation
+ *  does about 20M guesses/sec at 3 GHz.)
+ *
+ *  Proving uniqueness of a sudoku solution is tricker than finding
+ *  a solution (or showing that no solution exists).  A good indication
+ *  that a solution is unique is if we get the same result solving
+ *  by brute force when the puzzle is also rotated by 90, 180 and 270
+ *  degrees.  If there are multiple solutions, it seems unlikely
+ *  that you would get the same solution four times in a row, using a
+ *  brute force method that increments guesses and scans LR/TB.
+ *  The function sudokuTestUniqueness() does this.
+ *
+ *  And given a function that can determine uniqueness, it is
+ *  easy to generate valid sudokus.  We provide sudokuGenerate(),
+ *  which starts with some valid initial solution, and randomly
+ *  removes numbers, stopping either when a minimum number of non-zero
+ *  elements are left, or when it becomes difficult to remove another
+ *  element without destroying the uniqueness of the solution.
+ *
+ *  For further reading, see the Wikipedia articles:
+ *     (1) http://en.wikipedia.org/wiki/Algorithmics_of_sudoku
+ *     (2) http://en.wikipedia.org/wiki/Sudoku
+ *
+ *  How many 9x9 sudokus are there?  Here are the numbers.
+ *   ~ From ref(1), there are about 6 x 10^27 "latin squares", where
+ *     each row and column has all 9 digits.
+ *   ~ There are 7.2 x 10^21 actual solutions, having the added
+ *     constraint in each of the 9 3x3 squares.  (The constraint
+ *     reduced the number by the fraction 1.2 x 10^(-6).)
+ *   ~ There are a mere 5.5 billion essentially different solutions (EDS),
+ *     when symmetries (rotation, reflection, permutation and relabelling)
+ *     are removed.
+ *   ~ Thus there are 1.3 x 10^12 solutions that can be derived by
+ *     symmetry from each EDS.  Can we account for these?
+ *   ~ Sort-of.  From an EDS, you can derive (3!)^8 = 1.7 million solutions
+ *     by simply permuting rows and columns.  (Do you see why it is
+ *     not (3!)^6 ?)
+ *   ~ Also from an EDS, you can derive 9! solutions by relabelling,
+ *     and 4 solutions by rotation, for a total of 1.45 million solutions
+ *     by relabelling and rotation.  Then taking the product, by symmetry
+ *     we can derive 1.7M x 1.45M = 2.45 trillion solutions from each EDS.
+ *     (Something is off by about a factor of 2 -- close enough.)
+ *
+ *  Another interesting fact is that there are apparently 48K EDS sudokus
+ *  (with unique solutions) that have only 17 givens.  No sudokus are known
+ *  with less than 17, but there exists no proof that this is the minimum.
+ * 
+ */ + +#include "allheaders.h" + + +static l_int32 sudokuValidState(l_int32 *state); +static l_int32 sudokuNewGuess(L_SUDOKU *sud); +static l_int32 sudokuTestState(l_int32 *state, l_int32 index); +static l_int32 sudokuCompareState(L_SUDOKU *sud1, L_SUDOKU *sud2, + l_int32 quads, l_int32 *psame); +static l_int32 *sudokuRotateArray(l_int32 *array, l_int32 quads); + +/* --------------------------------------------------------------- */ +/* An example of a valid solution */ +/* --------------------------------------------------------------- * +static const char valid_solution[] = "3 8 7 2 6 4 1 9 5 " + "2 6 5 8 9 1 4 3 7 " + "1 4 9 5 3 7 6 8 2 " + "5 2 3 7 1 6 8 4 9 " + "7 1 6 9 4 8 2 5 3 " + "8 9 4 3 5 2 7 1 6 " + "9 7 2 1 8 5 3 6 4 " + "4 3 1 6 7 9 5 2 8 " + "6 5 8 4 2 3 9 7 1 "; +*/ + + +/*---------------------------------------------------------------------* + * Read input data from file or string * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuReadFile() + * + * \param[in] filename formatted sudoku file + * \return array of 81 numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) The file format has:
+ *          * any number of comment lines beginning with '#'
+ *          * a set of 9 lines, each having 9 digits (0-9) separated
+ *            by a space
+ * 
+ */ +l_int32 * +sudokuReadFile(const char *filename) +{ +char *str, *strj; +l_uint8 *data; +l_int32 i, j, nlines, val, index, error; +l_int32 *array; +size_t size; +SARRAY *saline, *sa1, *sa2; + + PROCNAME("sudokuReadFile"); + + if (!filename) + return (l_int32 *)ERROR_PTR("filename not defined", procName, NULL); + data = l_binaryRead(filename, &size); + sa1 = sarrayCreateLinesFromString((char *)data, 0); + sa2 = sarrayCreate(9); + + /* Filter out the comment lines; verify that there are 9 data lines */ + nlines = sarrayGetCount(sa1); + for (i = 0; i < nlines; i++) { + str = sarrayGetString(sa1, i, L_NOCOPY); + if (str[0] != '#') + sarrayAddString(sa2, str, L_COPY); + } + LEPT_FREE(data); + sarrayDestroy(&sa1); + nlines = sarrayGetCount(sa2); + if (nlines != 9) { + sarrayDestroy(&sa2); + L_ERROR("file has %d lines\n", procName, nlines); + return (l_int32 *)ERROR_PTR("invalid file", procName, NULL); + } + + /* Read the data into the array, verifying that each data + * line has 9 numbers. */ + error = FALSE; + array = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + for (i = 0, index = 0; i < 9; i++) { + str = sarrayGetString(sa2, i, L_NOCOPY); + saline = sarrayCreateWordsFromString(str); + if (sarrayGetCount(saline) != 9) { + error = TRUE; + sarrayDestroy(&saline); + break; + } + for (j = 0; j < 9; j++) { + strj = sarrayGetString(saline, j, L_NOCOPY); + if (sscanf(strj, "%d", &val) != 1) + error = TRUE; + else + array[index++] = val; + } + sarrayDestroy(&saline); + if (error) break; + } + sarrayDestroy(&sa2); + + if (error) { + LEPT_FREE(array); + return (l_int32 *)ERROR_PTR("invalid data", procName, NULL); + } + + return array; +} + + +/*! + * \brief sudokuReadString() + * + * \param[in] str formatted input data + * \return array of 81 numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) The string is formatted as 81 single digits, each separated
+ *          by 81 spaces.
+ * 
+ */ +l_int32 * +sudokuReadString(const char *str) +{ +l_int32 i; +l_int32 *array; + + PROCNAME("sudokuReadString"); + + if (!str) + return (l_int32 *)ERROR_PTR("str not defined", procName, NULL); + + /* Read in the initial solution */ + array = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + for (i = 0; i < 81; i++) { + if (sscanf(str + 2 * i, "%d ", &array[i]) != 1) { + LEPT_FREE(array); + return (l_int32 *)ERROR_PTR("invalid format", procName, NULL); + } + } + + return array; +} + + +/*---------------------------------------------------------------------* + * Create/destroy sudoku * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuCreate() + * + * \param[in] array 81 numbers, 9 rows of 9 numbers each + * \return l_sudoku, or NULL on error + * + *
+ * Notes:
+ *      (1) The input array has 0 for the unknown values, and 1-9
+ *          for the known initial values.  It is generated from
+ *          a file using sudokuReadInput(), which checks that the file
+ *          data has 81 numbers in 9 rows.
+ * 
+ */ +L_SUDOKU * +sudokuCreate(l_int32 *array) +{ +l_int32 i, val, locs_index; +L_SUDOKU *sud; + + PROCNAME("sudokuCreate"); + + if (!array) + return (L_SUDOKU *)ERROR_PTR("array not defined", procName, NULL); + + locs_index = 0; /* into locs array */ + sud = (L_SUDOKU *)LEPT_CALLOC(1, sizeof(L_SUDOKU)); + sud->locs = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + sud->init = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + sud->state = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + for (i = 0; i < 81; i++) { + val = array[i]; + sud->init[i] = val; + sud->state[i] = val; + if (val == 0) + sud->locs[locs_index++] = i; + } + sud->num = locs_index; + sud->failure = FALSE; + sud->finished = FALSE; + return sud; +} + + +/*! + * \brief sudokuDestroy() + * + * \param[in,out] psud will be set to null before returning + * \return void + */ +void +sudokuDestroy(L_SUDOKU **psud) +{ +L_SUDOKU *sud; + + PROCNAME("sudokuDestroy"); + + if (psud == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((sud = *psud) == NULL) + return; + + LEPT_FREE(sud->locs); + LEPT_FREE(sud->init); + LEPT_FREE(sud->state); + LEPT_FREE(sud); + + *psud = NULL; + return; +} + + +/*---------------------------------------------------------------------* + * Solve the puzzle * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuSolve() + * + * \param[in] sud l_sudoku starting in initial state + * \return 1 on success, 0 on failure to solve note reversal of + * typical unix returns + */ +l_int32 +sudokuSolve(L_SUDOKU *sud) +{ + PROCNAME("sudokuSolve"); + + if (!sud) + return ERROR_INT("sud not defined", procName, 0); + + if (!sudokuValidState(sud->init)) + return ERROR_INT("initial state not valid", procName, 0); + + while (1) { + if (sudokuNewGuess(sud)) + break; + if (sud->finished == TRUE) + break; + } + + if (sud->failure == TRUE) { + fprintf(stderr, "Failure after %d guesses\n", sud->nguess); + return 0; + } + + fprintf(stderr, "Solved after %d guesses\n", sud->nguess); + return 1; +} + + +/*! + * \brief sudokuValidState() + * + * \param[in] state array of size 81 + * \return 1 if valid, 0 if invalid + * + *
+ * Notes:
+ *      (1) This can be used on either the initial state (init)
+ *          or on the current state (state) of the l_soduku.
+ *          All values of 0 are ignored.
+ * 
+ */ +static l_int32 +sudokuValidState(l_int32 *state) +{ +l_int32 i; + + PROCNAME("sudokuValidState"); + + if (!state) + return ERROR_INT("state not defined", procName, 0); + + for (i = 0; i < 81; i++) { + if (!sudokuTestState(state, i)) + return 0; + } + + return 1; +} + + +/*! + * \brief sudokuNewGuess() + * + * \param[in] sud l_sudoku + * \return 0 if OK; 1 if no solution is possible + * + *
+ * Notes:
+ *      (1) This attempts to increment the number in the current
+ *          location.  If it can't, it backtracks (sets the number
+ *          in the current location to zero and decrements the
+ *          current location).  If it can, it tests that number,
+ *          and if the number is valid, moves forward to the next
+ *          empty location (increments the current location).
+ *      (2) If there is no solution, backtracking will eventually
+ *          exhaust possibilities for the first location.
+ * 
+ */ +static l_int32 +sudokuNewGuess(L_SUDOKU *sud) +{ +l_int32 index, val, valid; +l_int32 *locs, *state; + + locs = sud->locs; + state = sud->state; + index = locs[sud->current]; /* 0 to 80 */ + val = state[index]; + if (val == 9) { /* backtrack or give up */ + if (sud->current == 0) { + sud->failure = TRUE; + return 1; + } + state[index] = 0; + sud->current--; + } else { /* increment current value and test */ + sud->nguess++; + state[index]++; + valid = sudokuTestState(state, index); + if (valid) { + if (sud->current == sud->num - 1) { /* we're done */ + sud->finished = TRUE; + return 0; + } else { /* advance to next position */ + sud->current++; + } + } + } + + return 0; +} + + +/*! + * \brief sudokuTestState() + * + * \param[in] state current state: array of 81 values + * \param[in] index into state element that we are testing + * \return 1 if valid; 0 if invalid no error checking + */ +static l_int32 +sudokuTestState(l_int32 *state, + l_int32 index) +{ +l_int32 i, j, val, row, rowstart, rowend, col; +l_int32 blockrow, blockcol, blockstart, rowindex, locindex; + + if ((val = state[index]) == 0) /* automatically valid */ + return 1; + + /* Test row. Test val is at (x, y) = (index % 9, index / 9) */ + row = index / 9; + rowstart = 9 * row; + for (i = rowstart; i < index; i++) { + if (state[i] == val) + return 0; + } + rowend = rowstart + 9; + for (i = index + 1; i < rowend; i++) { + if (state[i] == val) + return 0; + } + + /* Test column */ + col = index % 9; + for (j = col; j < index; j += 9) { + if (state[j] == val) + return 0; + } + for (j = index + 9; j < 81; j += 9) { + if (state[j] == val) + return 0; + } + + /* Test local 3x3 block */ + blockrow = 3 * (row / 3); + blockcol = 3 * (col / 3); + blockstart = 9 * blockrow + blockcol; + for (i = 0; i < 3; i++) { + rowindex = blockstart + 9 * i; + for (j = 0; j < 3; j++) { + locindex = rowindex + j; + if (index == locindex) continue; + if (state[locindex] == val) + return 0; + } + } + + return 1; +} + + +/*---------------------------------------------------------------------* + * Test for uniqueness * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuTestUniqueness() + * + * \param[in] array of 81 numbers, 9 lines of 9 numbers each + * \param[out] punique 1 if unique, 0 if not + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This applies the brute force method to all four 90 degree
+ *          rotations.  If there is more than one solution, it is highly
+ *          unlikely that all four results will be the same;
+ *          consequently, if they are the same, the solution is
+ *          most likely to be unique.
+ * 
+ */ +l_ok +sudokuTestUniqueness(l_int32 *array, + l_int32 *punique) +{ +l_int32 same1, same2, same3; +l_int32 *array1, *array2, *array3; +L_SUDOKU *sud, *sud1, *sud2, *sud3; + + PROCNAME("sudokuTestUniqueness"); + + if (!punique) + return ERROR_INT("&unique not defined", procName, 1); + *punique = 0; + if (!array) + return ERROR_INT("array not defined", procName, 1); + + sud = sudokuCreate(array); + sudokuSolve(sud); + array1 = sudokuRotateArray(array, 1); + sud1 = sudokuCreate(array1); + sudokuSolve(sud1); + array2 = sudokuRotateArray(array, 2); + sud2 = sudokuCreate(array2); + sudokuSolve(sud2); + array3 = sudokuRotateArray(array, 3); + sud3 = sudokuCreate(array3); + sudokuSolve(sud3); + + sudokuCompareState(sud, sud1, 1, &same1); + sudokuCompareState(sud, sud2, 2, &same2); + sudokuCompareState(sud, sud3, 3, &same3); + *punique = (same1 && same2 && same3); + + sudokuDestroy(&sud); + sudokuDestroy(&sud1); + sudokuDestroy(&sud2); + sudokuDestroy(&sud3); + LEPT_FREE(array1); + LEPT_FREE(array2); + LEPT_FREE(array3); + return 0; +} + + +/*! + * \brief sudokuCompareState() + * + * \param[in] sud1, sud2 two l_Sudoku states (solutions) + * \param[in] quads rotation of sud2 input with respect to sud1, + * in units of 90 degrees cw + * \param[out] psame 1 if all 4 results are identical; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input to sud2 has been rotated by %quads relative to the
+ *          input to sud1.  Therefore, we must rotate the solution to
+ *          sud1 by the same amount before comparing it to the
+ *          solution to sud2.
+ * 
+ */ +static l_int32 +sudokuCompareState(L_SUDOKU *sud1, + L_SUDOKU *sud2, + l_int32 quads, + l_int32 *psame) +{ +l_int32 i, same; +l_int32 *array; + + PROCNAME("sudokuCompareState"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!sud1) + return ERROR_INT("sud1 not defined", procName, 1); + if (!sud2) + return ERROR_INT("sud1 not defined", procName, 1); + if (quads < 1 || quads > 3) + return ERROR_INT("valid quads in {1,2,3}", procName, 1); + + same = TRUE; + if ((array = sudokuRotateArray(sud1->state, quads)) == NULL) + return ERROR_INT("array not made", procName, 1); + for (i = 0; i < 81; i++) { + if (array[i] != sud2->state[i]) { + same = FALSE; + break; + } + } + *psame = same; + LEPT_FREE(array); + return 0; +} + + +/*! + * \brief sudokuRotateArray() + * + * \param[in] array 81 numbers; 9 lines of 9 numbers each + * \param[in] quads 1-3; number of 90 degree cw rotations + * \return rarray rotated array, or NULL on error + */ +static l_int32 * +sudokuRotateArray(l_int32 *array, + l_int32 quads) +{ +l_int32 i, j, sindex, dindex; +l_int32 *rarray; + + PROCNAME("sudokuRotateArray"); + + if (!array) + return (l_int32 *)ERROR_PTR("array not defined", procName, NULL); + if (quads < 1 || quads > 3) + return (l_int32 *)ERROR_PTR("valid quads in {1,2,3}", procName, NULL); + + rarray = (l_int32 *)LEPT_CALLOC(81, sizeof(l_int32)); + if (quads == 1) { + for (j = 0, dindex = 0; j < 9; j++) { + for (i = 8; i >= 0; i--) { + sindex = 9 * i + j; + rarray[dindex++] = array[sindex]; + } + } + } else if (quads == 2) { + for (i = 8, dindex = 0; i >= 0; i--) { + for (j = 8; j >= 0; j--) { + sindex = 9 * i + j; + rarray[dindex++] = array[sindex]; + } + } + } else { /* quads == 3 */ + for (j = 8, dindex = 0; j >= 0; j--) { + for (i = 0; i < 9; i++) { + sindex = 9 * i + j; + rarray[dindex++] = array[sindex]; + } + } + } + + return rarray; +} + + +/*---------------------------------------------------------------------* + * Generation * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuGenerate() + * + * \param[in] array 81 numbers, 9 rows of 9 numbers each + * \param[in] seed random number + * \param[in] minelems min non-zero elements allowed; <= 80 + * \param[in] maxtries max tries to remove a number and get a valid sudoku + * \return l_sudoku, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a brute force generator.  It starts with a completed
+ *          sudoku solution and, by removing elements (setting them to 0),
+ *          generates a valid (unique) sudoku initial condition.
+ *      (2) The process stops when either %minelems, the minimum
+ *          number of non-zero elements, is reached, or when the
+ *          number of attempts to remove the next element exceeds %maxtries.
+ *      (3) No sudoku is known with less than 17 nonzero elements.
+ * 
+ */ +L_SUDOKU * +sudokuGenerate(l_int32 *array, + l_int32 seed, + l_int32 minelems, + l_int32 maxtries) +{ +l_int32 index, sector, nzeros, removefirst, tries, val, oldval, unique; +L_SUDOKU *sud, *testsud; + + PROCNAME("sudokuGenerate"); + + if (!array) + return (L_SUDOKU *)ERROR_PTR("array not defined", procName, NULL); + if (minelems > 80) + return (L_SUDOKU *)ERROR_PTR("minelems must be < 81", procName, NULL); + + /* Remove up to 30 numbers at random from the solution. + * Test if the solution is valid -- the initial 'solution' may + * have been invalid. Then test if the sudoku with 30 zeroes + * is unique -- it almost always will be. */ + srand(seed); + nzeros = 0; + sector = 0; + removefirst = L_MIN(30, 81 - minelems); + while (nzeros < removefirst) { + genRandomIntegerInRange(9, 0, &val); + index = 27 * (sector / 3) + 3 * (sector % 3) + + 9 * (val / 3) + (val % 3); + if (array[index] == 0) continue; + array[index] = 0; + nzeros++; + sector++; + sector %= 9; + } + testsud = sudokuCreate(array); + sudokuSolve(testsud); + if (testsud->failure) { + sudokuDestroy(&testsud); + L_ERROR("invalid initial solution\n", procName); + return NULL; + } + sudokuTestUniqueness(testsud->init, &unique); + sudokuDestroy(&testsud); + if (!unique) { + L_ERROR("non-unique result with 30 zeroes\n", procName); + return NULL; + } + + /* Remove more numbers, testing at each removal for uniqueness. */ + tries = 0; + sector = 0; + while (1) { + if (tries > maxtries) break; + if (81 - nzeros <= minelems) break; + + if (tries == 0) { + fprintf(stderr, "Trying %d zeros\n", nzeros); + tries = 1; + } + + /* Choose an element to be zeroed. We choose one + * at random in succession from each of the nine sectors. */ + genRandomIntegerInRange(9, 0, &val); + index = 27 * (sector / 3) + 3 * (sector % 3) + + 9 * (val / 3) + (val % 3); + sector++; + sector %= 9; + if (array[index] == 0) continue; + + /* Save the old value in case we need to revert */ + oldval = array[index]; + + /* Is there a solution? If not, try again. */ + array[index] = 0; + testsud = sudokuCreate(array); + sudokuSolve(testsud); + if (testsud->failure == TRUE) { + sudokuDestroy(&testsud); + array[index] = oldval; /* revert */ + tries++; + continue; + } + + /* Is the solution unique? If not, try again. */ + sudokuTestUniqueness(testsud->init, &unique); + sudokuDestroy(&testsud); + if (!unique) { /* revert and try again */ + array[index] = oldval; + tries++; + } else { /* accept this */ + tries = 0; + fprintf(stderr, "Have %d zeros\n", nzeros); + nzeros++; + } + } + fprintf(stderr, "Final: nelems = %d\n", 81 - nzeros); + + /* Show that we can recover the solution */ + sud = sudokuCreate(array); + sudokuOutput(sud, L_SUDOKU_INIT); + sudokuSolve(sud); + sudokuOutput(sud, L_SUDOKU_STATE); + + return sud; +} + + +/*---------------------------------------------------------------------* + * Output * + *---------------------------------------------------------------------*/ +/*! + * \brief sudokuOutput() + * + * \param[in] sud l_sudoku at any stage + * \param[in] arraytype L_SUDOKU_INIT, L_SUDOKU_STATE + * \return void + * + *
+ * Notes:
+ *      (1) Prints either the initial array or the current state
+ *          of the solution.
+ * 
+ */ +l_int32 +sudokuOutput(L_SUDOKU *sud, + l_int32 arraytype) +{ +l_int32 i, j; +l_int32 *array; + + PROCNAME("sudokuOutput"); + + if (!sud) + return ERROR_INT("sud not defined", procName, 1); + if (arraytype == L_SUDOKU_INIT) + array = sud->init; + else if (arraytype == L_SUDOKU_STATE) + array = sud->state; + else + return ERROR_INT("invalid arraytype", procName, 1); + + for (i = 0; i < 9; i++) { + for (j = 0; j < 9; j++) + fprintf(stderr, "%d ", array[9 * i + j]); + fprintf(stderr, "\n"); + } + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/sudoku.h b/hgdriver/3rdparty/hgOCR/leptonica/sudoku.h new file mode 100644 index 0000000..5abb7cb --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/sudoku.h @@ -0,0 +1,77 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 SUDOKU_H_INCLUDED +#define SUDOKU_H_INCLUDED + +/*! + * \file sudoku.h + * + *
+ *    The L_Sudoku holds all the information of the current state.
+ *
+ *    The input to sudokuCreate() is a file with any number of lines
+ *    starting with '#', followed by 9 lines consisting of 9 numbers
+ *    in each line.  These have the known values and use 0 for the unknowns.
+ *    Blank lines are ignored.
+ *
+ *    The %locs array holds the indices of the unknowns, numbered
+ *    left-to-right and top-to-bottom from 0 to 80.  The array size
+ *    is initialized to %num.  %current is the index into the %locs
+ *    array of the current guess: locs[current].
+ *
+ *    The %state array is used to determine the validity of each guess.
+ *    It is of size 81, and is initialized by setting the unknowns to 0
+ *    and the knowns to their input values.
+ * 
+ */ + +struct L_Sudoku +{ + l_int32 num; /*!< number of unknowns */ + l_int32 *locs; /*!< location of unknowns */ + l_int32 current; /*!< index into %locs of current location */ + l_int32 *init; /*!< initial state, with 0 representing */ + /*!< the unknowns */ + l_int32 *state; /*!< present state, including inits and */ + /*!< guesses of unknowns up to %current */ + l_int32 nguess; /*!< shows current number of guesses */ + l_int32 finished; /*!< set to 1 when solved */ + l_int32 failure; /*!< set to 1 if no solution is possible */ +}; +typedef struct L_Sudoku L_SUDOKU; + + + /*! For printing out array data */ +/*! Sudoku Output */ +enum { + L_SUDOKU_INIT = 0, + L_SUDOKU_STATE = 1 +}; + +#endif /* SUDOKU_H_INCLUDED */ + + diff --git a/hgdriver/3rdparty/hgOCR/leptonica/textops.c b/hgdriver/3rdparty/hgOCR/leptonica/textops.c new file mode 100644 index 0000000..06b0a19 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/textops.c @@ -0,0 +1,1125 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file textops.c + *
+ *
+ *    Font layout
+ *       PIX             *pixAddSingleTextblock()
+ *       PIX             *pixAddTextlines()
+ *       l_int32          pixSetTextblock()
+ *       l_int32          pixSetTextline()
+ *       PIXA            *pixaAddTextNumber()
+ *       PIXA            *pixaAddTextlines()
+ *       l_int32          pixaAddPixWithText()
+ *
+ *    Text size estimation and partitioning
+ *       SARRAY          *bmfGetLineStrings()
+ *       NUMA            *bmfGetWordWidths()
+ *       l_int32          bmfGetStringWidth()
+ *
+ *    Text splitting
+ *       SARRAY          *splitStringToParagraphs()
+ *       static l_int32   stringAllWhitespace()
+ *       static l_int32   stringLeadingWhitespace()
+ *
+ *    This is a simple utility to put text on images.  One font and style
+ *    is provided, with a variety of pt sizes.  For example, to put a
+ *    line of green 10 pt text on an image, with the beginning baseline
+ *    at (50, 50):
+ *        L_Bmf  *bmf = bmfCreate(NULL, 10);
+ *        const char *textstr = "This is a funny cat";
+ *        pixSetTextline(pixs, bmf, textstr, 0x00ff0000, 50, 50, NULL, NULL);
+ *
+ *    The simplest interfaces for adding text to an image are
+ *    pixAddTextlines() and pixAddSingleTextblock().
+ *    For example, to add the same text in red, centered, below the image:
+ *        Pix *pixd = pixAddTextlines(pixs, bmf, textstr, 0xff000000,
+ *                                    L_ADD_BELOW);  // red text
+ *
+ *    To add text to all pix in a pixa, generating a new pixa, use
+ *    either an sarray to hold the strings for each pix, or use the
+ *    strings in the text field of each pix; e.g.,
+ *        Pixa *pixa2 = pixaAddTextlines(pixa1, bmf, sa, 0x0000ff00,
+ *                                    L_ADD_LEFT);  // blue text
+ *        Pixa *pixa2 = pixaAddTextlines(pixa1, bmf, NULL, 0x00ff0000,
+ *                                    L_ADD_RIGHT);  // green text
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_int32 stringAllWhitespace(char *textstr, l_int32 *pval); +static l_int32 stringLeadingWhitespace(char *textstr, l_int32 *pval); + + +/*---------------------------------------------------------------------* + * Font layout * + *---------------------------------------------------------------------*/ +/*! + * \brief pixAddSingleTextblock() + * + * \param[in] pixs input pix; colormap ok + * \param[in] bmf bitmap font data + * \param[in] textstr [optional] text string to be added + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_AT_TOP, + * L_ADD_AT_BOT, L_ADD_BELOW + * \param[out] poverflow [optional] 1 if text overflows allocated + * region and is clipped; 0 otherwise + * \return pixd new pix with rendered text, or either a copy, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This function paints a set of lines of text over an image.
+ *          If %location is L_ADD_ABOVE or L_ADD_BELOW, the pix size
+ *          is expanded with a border and rendered over the border.
+ *      (2) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *      (3) If textstr == NULL, use the text field in the pix.
+ *      (4) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ *      (5) Typical usage is for labelling a pix with some text data.
+ * 
+ */ +PIX * +pixAddSingleTextblock(PIX *pixs, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 location, + l_int32 *poverflow) +{ +char *linestr; +l_int32 w, h, d, i, y, xstart, ystart, extra, spacer, rval, gval, bval; +l_int32 nlines, htext, ovf, overflow, offset, index; +l_uint32 textcolor; +PIX *pixd; +PIXCMAP *cmap, *cmapd; +SARRAY *salines; + + PROCNAME("pixAddSingleTextblock"); + + if (poverflow) *poverflow = 0; + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (location != L_ADD_ABOVE && location != L_ADD_AT_TOP && + location != L_ADD_AT_BOT && location != L_ADD_BELOW) + return (PIX *)ERROR_PTR("invalid location", procName, NULL); + if (!bmf) { + L_ERROR("no bitmap fonts; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + if (!textstr) + textstr = pixGetText(pixs); + if (!textstr) { + L_WARNING("no textstring defined; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + + /* Make sure the "color" value for the text will work + * for the pix. If the pix is not colormapped and the + * value is out of range, set it to mid-range. */ + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (d == 1 && val > 1) + val = 1; + else if (d == 2 && val > 3 && !cmap) + val = 2; + else if (d == 4 && val > 15 && !cmap) + val = 8; + else if (d == 8 && val > 0xff && !cmap) + val = 128; + else if (d == 16 && val > 0xffff) + val = 0x8000; + else if (d == 32 && val < 256) + val = 0x80808000; + + xstart = (l_int32)(0.1 * w); + salines = bmfGetLineStrings(bmf, textstr, w - 2 * xstart, 0, &htext); + if (!salines) + return (PIX *)ERROR_PTR("line string sa not made", procName, NULL); + nlines = sarrayGetCount(salines); + + /* Add white border if required */ + spacer = 10; /* pixels away from image boundary or added border */ + if (location == L_ADD_ABOVE || location == L_ADD_BELOW) { + extra = htext + 2 * spacer; + pixd = pixCreate(w, h + extra, d); + pixCopyColormap(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyText(pixd, pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + if (location == L_ADD_ABOVE) + pixRasterop(pixd, 0, extra, w, h, PIX_SRC, pixs, 0, 0); + else /* add below */ + pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); + } else { + pixd = pixCopy(NULL, pixs); + } + cmapd = pixGetColormap(pixd); + + /* bmf->baselinetab[93] is the approximate distance from + * the top of the tallest character to the baseline. 93 was chosen + * at random, as all the baselines are essentially equal for + * each character in a font. */ + offset = bmf->baselinetab[93]; + if (location == L_ADD_ABOVE || location == L_ADD_AT_TOP) + ystart = offset + spacer; + else if (location == L_ADD_AT_BOT) + ystart = h - htext - spacer + offset; + else /* add below */ + ystart = h + offset + spacer; + + /* If cmapped, add the color if necessary to the cmap. If the + * cmap is full, use the nearest color to the requested color. */ + if (cmapd) { + extractRGBValues(val, &rval, &gval, &bval); + pixcmapAddNearestColor(cmapd, rval, gval, bval, &index); + pixcmapGetColor(cmapd, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &textcolor); + } else { + textcolor = val; + } + + /* Keep track of overflow condition on line width */ + overflow = 0; + for (i = 0, y = ystart; i < nlines; i++) { + linestr = sarrayGetString(salines, i, L_NOCOPY); + pixSetTextline(pixd, bmf, linestr, textcolor, + xstart, y, NULL, &ovf); + y += bmf->lineheight + bmf->vertlinesep; + if (ovf) + overflow = 1; + } + + /* Also consider vertical overflow where there is too much text to + * fit inside the image: the cases L_ADD_AT_TOP and L_ADD_AT_BOT. + * The text requires a total of htext + 2 * spacer vertical pixels. */ + if (location == L_ADD_AT_TOP || location == L_ADD_AT_BOT) { + if (h < htext + 2 * spacer) + overflow = 1; + } + if (poverflow) *poverflow = overflow; + + sarrayDestroy(&salines); + return pixd; +} + + +/*! + * \brief pixAddTextlines() + * + * \param[in] pixs input pix; colormap ok + * \param[in] bmf bitmap font data + * \param[in] textstr [optional] text string to be added + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT + * \return pixd new pix with rendered text, or either a copy, + * or NULL on error + * + *
+ * Notes:
+ *      (1) This function expands an image as required to paint one or
+ *          more lines of text adjacent to the image.  If %bmf == NULL,
+ *          this returns a copy.  If above or below, the lines are
+ *          centered with respect to the image; if left or right, they
+ *          are left justified.
+ *      (2) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *      (3) If textstr == NULL, use the text field in the pix.  The
+ *          text field contains one or most "lines" of text, where newlines
+ *          are used as line separators.
+ *      (4) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ *      (5) Typical usage is for labelling a pix with some text data.
+ * 
+ */ +PIX * +pixAddTextlines(PIX *pixs, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 location) +{ +char *str; +l_int32 i, w, h, d, rval, gval, bval, index; +l_int32 wline, wtext, htext, wadd, hadd, spacer, hbaseline, nlines; +l_uint32 textcolor; +PIX *pixd; +PIXCMAP *cmap, *cmapd; +SARRAY *sa; + + PROCNAME("pixAddTextlines"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (location != L_ADD_ABOVE && location != L_ADD_BELOW && + location != L_ADD_LEFT && location != L_ADD_RIGHT) + return (PIX *)ERROR_PTR("invalid location", procName, NULL); + if (!bmf) { + L_ERROR("no bitmap fonts; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + if (!textstr) { + textstr = pixGetText(pixs); + if (!textstr) { + L_WARNING("no textstring defined; returning a copy\n", procName); + return pixCopy(NULL, pixs); + } + } + + /* Make sure the "color" value for the text will work + * for the pix. If the pix is not colormapped and the + * value is out of range, set it to mid-range. */ + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (d == 1 && val > 1) + val = 1; + else if (d == 2 && val > 3 && !cmap) + val = 2; + else if (d == 4 && val > 15 && !cmap) + val = 8; + else if (d == 8 && val > 0xff && !cmap) + val = 128; + else if (d == 16 && val > 0xffff) + val = 0x8000; + else if (d == 32 && val < 256) + val = 0x80808000; + + /* Get the text in each line */ + sa = sarrayCreateLinesFromString(textstr, 0); + nlines = sarrayGetCount(sa); + + /* Get the necessary text size */ + wtext = 0; + for (i = 0; i < nlines; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + bmfGetStringWidth(bmf, str, &wline); + if (wline > wtext) + wtext = wline; + } + hbaseline = bmf->baselinetab[93]; + htext = 1.5 * hbaseline * nlines; + + /* Add white border */ + spacer = 10; /* pixels away from the added border */ + if (location == L_ADD_ABOVE || location == L_ADD_BELOW) { + hadd = htext + 2 * spacer; + pixd = pixCreate(w, h + hadd, d); + pixCopyColormap(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyText(pixd, pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + if (location == L_ADD_ABOVE) + pixRasterop(pixd, 0, hadd, w, h, PIX_SRC, pixs, 0, 0); + else /* add below */ + pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); + } else { /* L_ADD_LEFT or L_ADD_RIGHT */ + wadd = wtext + 2 * spacer; + pixd = pixCreate(w + wadd, h, d); + pixCopyColormap(pixd, pixs); + pixCopyResolution(pixd, pixs); + pixCopyText(pixd, pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + if (location == L_ADD_LEFT) + pixRasterop(pixd, wadd, 0, w, h, PIX_SRC, pixs, 0, 0); + else /* add to right */ + pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); + } + + /* If cmapped, add the color if necessary to the cmap. If the + * cmap is full, use the nearest color to the requested color. */ + cmapd = pixGetColormap(pixd); + if (cmapd) { + extractRGBValues(val, &rval, &gval, &bval); + pixcmapAddNearestColor(cmapd, rval, gval, bval, &index); + pixcmapGetColor(cmapd, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &textcolor); + } else { + textcolor = val; + } + + /* Add the text */ + for (i = 0; i < nlines; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + bmfGetStringWidth(bmf, str, &wtext); + if (location == L_ADD_ABOVE) + pixSetTextline(pixd, bmf, str, textcolor, + (w - wtext) / 2, spacer + hbaseline * (1 + 1.5 * i), + NULL, NULL); + else if (location == L_ADD_BELOW) + pixSetTextline(pixd, bmf, str, textcolor, + (w - wtext) / 2, h + spacer + + hbaseline * (1 + 1.5 * i), NULL, NULL); + else if (location == L_ADD_LEFT) + pixSetTextline(pixd, bmf, str, textcolor, + spacer, (h - htext) / 2 + hbaseline * (1 + 1.5 * i), + NULL, NULL); + else /* location == L_ADD_RIGHT */ + pixSetTextline(pixd, bmf, str, textcolor, + w + spacer, (h - htext) / 2 + + hbaseline * (1 + 1.5 * i), NULL, NULL); + } + + sarrayDestroy(&sa); + return pixd; +} + + +/*! + * \brief pixSetTextblock() + * + * \param[in] pixs input image + * \param[in] bmf bitmap font data + * \param[in] textstr block text string to be set + * \param[in] val color to set the text + * \param[in] x0 left edge for each line of text + * \param[in] y0 baseline location for the first text line + * \param[in] wtext max width of each line of generated text + * \param[in] firstindent indentation of first line, in x-widths + * \param[out] poverflow [optional] 0 if text is contained in input pix; + * 1 if it is clipped + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function paints a set of lines of text over an image.
+ *      (2) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *          The last two hex digits are 00 (byte value 0), assigned to
+ *          the A component.  Note that, as usual, RGBA proceeds from
+ *          left to right in the order from MSB to LSB (see pix.h
+ *          for details).
+ *      (3) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ * 
+ */ +l_ok +pixSetTextblock(PIX *pixs, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 x0, + l_int32 y0, + l_int32 wtext, + l_int32 firstindent, + l_int32 *poverflow) +{ +char *linestr; +l_int32 d, h, i, w, x, y, nlines, htext, xwidth, wline, ovf, overflow; +SARRAY *salines; +PIXCMAP *cmap; + + PROCNAME("pixSetTextblock"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + if (!textstr) + return ERROR_INT("textstr not defined", procName, 1); + + /* Make sure the "color" value for the text will work + * for the pix. If the pix is not colormapped and the + * value is out of range, set it to mid-range. */ + pixGetDimensions(pixs, &w, &h, &d); + cmap = pixGetColormap(pixs); + if (d == 1 && val > 1) + val = 1; + else if (d == 2 && val > 3 && !cmap) + val = 2; + else if (d == 4 && val > 15 && !cmap) + val = 8; + else if (d == 8 && val > 0xff && !cmap) + val = 128; + else if (d == 16 && val > 0xffff) + val = 0x8000; + else if (d == 32 && val < 256) + val = 0x80808000; + + if (w < x0 + wtext) { + L_WARNING("reducing width of textblock\n", procName); + wtext = w - x0 - w / 10; + if (wtext <= 0) + return ERROR_INT("wtext too small; no room for text", procName, 1); + } + + salines = bmfGetLineStrings(bmf, textstr, wtext, firstindent, &htext); + if (!salines) + return ERROR_INT("line string sa not made", procName, 1); + nlines = sarrayGetCount(salines); + bmfGetWidth(bmf, 'x', &xwidth); + + y = y0; + overflow = 0; + for (i = 0; i < nlines; i++) { + if (i == 0) + x = x0 + firstindent * xwidth; + else + x = x0; + linestr = sarrayGetString(salines, i, L_NOCOPY); + pixSetTextline(pixs, bmf, linestr, val, x, y, &wline, &ovf); + y += bmf->lineheight + bmf->vertlinesep; + if (ovf) + overflow = 1; + } + + /* (y0 - baseline) is the top of the printed text. Character + * 93 was chosen at random, as all the baselines are essentially + * equal for each character in a font. */ + if (h < y0 - bmf->baselinetab[93] + htext) + overflow = 1; + if (poverflow) + *poverflow = overflow; + + sarrayDestroy(&salines); + return 0; +} + + +/*! + * \brief pixSetTextline() + * + * \param[in] pixs input image + * \param[in] bmf bitmap font data + * \param[in] textstr text string to be set on the line + * \param[in] val color to set the text + * \param[in] x0 left edge for first char + * \param[in] y0 baseline location for all text on line + * \param[out] pwidth [optional] width of generated text + * \param[out] poverflow [optional] 0 if text is contained in input pix; + * 1 if it is clipped + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This function paints a line of text over an image.
+ *      (2) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *          The last two hex digits are 00 (byte value 0), assigned to
+ *          the A component.  Note that, as usual, RGBA proceeds from
+ *          left to right in the order from MSB to LSB (see pix.h
+ *          for details).
+ *      (3) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ * 
+ */ +l_ok +pixSetTextline(PIX *pixs, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 x0, + l_int32 y0, + l_int32 *pwidth, + l_int32 *poverflow) +{ +char chr; +l_int32 d, i, x, w, nchar, baseline, index, rval, gval, bval; +l_uint32 textcolor; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixSetTextline"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + if (!textstr) + return ERROR_INT("teststr not defined", procName, 1); + + d = pixGetDepth(pixs); + cmap = pixGetColormap(pixs); + if (d == 1 && val > 1) + val = 1; + else if (d == 2 && val > 3 && !cmap) + val = 2; + else if (d == 4 && val > 15 && !cmap) + val = 8; + else if (d == 8 && val > 0xff && !cmap) + val = 128; + else if (d == 16 && val > 0xffff) + val = 0x8000; + else if (d == 32 && val < 256) + val = 0x80808000; + + /* If cmapped, add the color if necessary to the cmap. If the + * cmap is full, use the nearest color to the requested color. */ + if (cmap) { + extractRGBValues(val, &rval, &gval, &bval); + pixcmapAddNearestColor(cmap, rval, gval, bval, &index); + pixcmapGetColor(cmap, index, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, &textcolor); + } else + textcolor = val; + + nchar = strlen(textstr); + x = x0; + for (i = 0; i < nchar; i++) { + chr = textstr[i]; + if ((l_int32)chr == 10) continue; /* NL */ + pix = bmfGetPix(bmf, chr); + bmfGetBaseline(bmf, chr, &baseline); + pixPaintThroughMask(pixs, pix, x, y0 - baseline, textcolor); + w = pixGetWidth(pix); + x += w + bmf->kernwidth; + pixDestroy(&pix); + } + + if (pwidth) + *pwidth = x - bmf->kernwidth - x0; + if (poverflow) + *poverflow = (x > pixGetWidth(pixs) - 1) ? 1 : 0; + return 0; +} + + +/*! + * \brief pixaAddTextNumber() + * + * \param[in] pixas input pixa; colormap ok + * \param[in] bmf bitmap font data + * \param[in] na [optional] number array; use 1 ... n if null + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT + * \return pixad new pixa with rendered numbers, or NULL on error + * + *
+ * Notes:
+ *      (1) Typical usage is for labelling each pix in a pixa with a number.
+ *      (2) This function paints numbers external to each pix, in a position
+ *          given by %location.  In all cases, the pix is expanded on
+ *          on side and the number is painted over white in the added region.
+ *      (3) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *      (4) If na == NULL, number each pix sequentially, starting with 1.
+ *      (5) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ * 
+ */ +PIXA * +pixaAddTextNumber(PIXA *pixas, + L_BMF *bmf, + NUMA *na, + l_uint32 val, + l_int32 location) +{ +char textstr[128]; +l_int32 i, n, index; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaAddTextNumber"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!bmf) + return (PIXA *)ERROR_PTR("bmf not defined", procName, NULL); + if (location != L_ADD_ABOVE && location != L_ADD_BELOW && + location != L_ADD_LEFT && location != L_ADD_RIGHT) + return (PIXA *)ERROR_PTR("invalid location", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + if (na) + numaGetIValue(na, i, &index); + else + index = i + 1; + snprintf(textstr, sizeof(textstr), "%d", index); + pix2 = pixAddTextlines(pix1, bmf, textstr, val, location); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + return pixad; +} + + +/*! + * \brief pixaAddTextlines() + * + * \param[in] pixas input pixa; colormap ok + * \param[in] bmf bitmap font data + * \param[in] sa [optional] sarray; use text embedded in + * each pix if null + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT + * \return pixad new pixa with rendered text, or NULL on error + * + *
+ * Notes:
+ *      (1) This function adds one or more lines of text externally to
+ *          each pix, in a position given by %location.  In all cases,
+ *          the pix is expanded as necessary to accommodate the text.
+ *      (2) %val is the pixel value to be painted through the font mask.
+ *          It should be chosen to agree with the depth of pixs.
+ *          If it is out of bounds, an intermediate value is chosen.
+ *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
+ *          hex representation of the red intensity, etc.
+ *      (3) If sa == NULL, use the text embedded in each pix.  In all
+ *          cases, newlines in the text string are used to separate the
+ *          lines of text that are added to the pix.
+ *      (4) If sa has a smaller count than pixa, issue a warning
+ *          and do not use any embedded text.
+ *      (5) If there is a colormap, this does the best it can to use
+ *          the requested color, or something similar to it.
+ * 
+ */ +PIXA * +pixaAddTextlines(PIXA *pixas, + L_BMF *bmf, + SARRAY *sa, + l_uint32 val, + l_int32 location) +{ +char *textstr; +l_int32 i, n, nstr; +PIX *pix1, *pix2; +PIXA *pixad; + + PROCNAME("pixaAddTextlines"); + + if (!pixas) + return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); + if (!bmf) + return (PIXA *)ERROR_PTR("bmf not defined", procName, NULL); + if (location != L_ADD_ABOVE && location != L_ADD_BELOW && + location != L_ADD_LEFT && location != L_ADD_RIGHT) + return (PIXA *)ERROR_PTR("invalid location", procName, NULL); + + n = pixaGetCount(pixas); + pixad = pixaCreate(n); + nstr = (sa) ? sarrayGetCount(sa) : 0; + if (nstr > 0 && nstr < n) + L_WARNING("There are %d strings and %d pix\n", procName, nstr, n); + for (i = 0; i < n; i++) { + pix1 = pixaGetPix(pixas, i, L_CLONE); + if (i < nstr) + textstr = sarrayGetString(sa, i, L_NOCOPY); + else + textstr = pixGetText(pix1); + pix2 = pixAddTextlines(pix1, bmf, textstr, val, location); + pixaAddPix(pixad, pix2, L_INSERT); + pixDestroy(&pix1); + } + + return pixad; +} + + +/*! + * \brief pixaAddPixWithText() + * + * \param[in] pixa + * \param[in] pixs any depth, colormap ok + * \param[in] reduction integer subsampling factor + * \param[in] bmf [optional] bitmap font data + * \param[in] textstr [optional] text string to be added + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) This function generates a new pix with added text, and adds
+ *          it by insertion into the pixa.
+ *      (2) If the input pixs is not cmapped and not 32 bpp, it is
+ *          converted to 32 bpp rgb.  %val is a standard 32 bpp pixel,
+ *          expressed as 0xrrggbb00.  If there is a colormap, this does
+ *          the best it can to use the requested color, or something close.
+ *      (3) if %bmf == NULL, generate an 8 pt font; this takes about 5 msec.
+ *      (4) If %textstr == NULL, use the text field in the pix.
+ *      (5) In general, the text string can be written in multiple lines;
+ *          use newlines as the separators.
+ *      (6) Typical usage is for debugging, where the pixa of labeled images
+ *          is used to generate a pdf.  Suggest using 1.0 for scalefactor.
+ * 
+ */ +l_ok +pixaAddPixWithText(PIXA *pixa, + PIX *pixs, + l_int32 reduction, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 location) +{ +l_int32 d; +L_BMF *bmf8; +PIX *pix1, *pix2, *pix3; +PIXCMAP *cmap; + + PROCNAME("pixaAddPixWithText"); + + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (location != L_ADD_ABOVE && location != L_ADD_BELOW && + location != L_ADD_LEFT && location != L_ADD_RIGHT) + return ERROR_INT("invalid location", procName, 1); + + if (!textstr) { + textstr = pixGetText(pixs); + if (!textstr) { + L_WARNING("no textstring defined; inserting copy", procName); + pixaAddPix(pixa, pixs, L_COPY); + return 0; + } + } + + /* Default font size is 8. */ + bmf8 = (bmf) ? bmf : bmfCreate(NULL, 8); + + if (reduction != 1) + pix1 = pixScaleByIntSampling(pixs, reduction); + else + pix1 = pixClone(pixs); + + /* We want the text to be rendered in color. This works + * automatically if pixs is cmapped or 32 bpp rgb; otherwise, + * we need to convert to rgb. */ + cmap = pixGetColormap(pix1); + d = pixGetDepth(pix1); + if (!cmap && d != 32) + pix2 = pixConvertTo32(pix1); + else + pix2 = pixClone(pix1); + + pix3 = pixAddTextlines(pix2, bmf, textstr, val, location); + pixDestroy(&pix1); + pixDestroy(&pix2); + if (!bmf) bmfDestroy(&bmf8); + if (!pix3) + return ERROR_INT("pix3 not made", procName, 1); + + pixaAddPix(pixa, pix3, L_INSERT); + return 0; +} + + +/*---------------------------------------------------------------------* + * Text size estimation and partitioning * + *---------------------------------------------------------------------*/ +/*! + * \brief bmfGetLineStrings() + * + * \param[in] bmf + * \param[in] textstr + * \param[in] maxw max width of a text line in pixels + * \param[in] firstindent indentation of first line, in x-widths + * \param[out] ph height required to hold text bitmap + * \return sarray of text strings for each line, or NULL on error + * + *
+ * Notes:
+ *      (1) Divides the input text string into an array of text strings,
+ *          each of which will fit within maxw bits of width.
+ * 
+ */ +SARRAY * +bmfGetLineStrings(L_BMF *bmf, + const char *textstr, + l_int32 maxw, + l_int32 firstindent, + l_int32 *ph) +{ +char *linestr; +l_int32 i, ifirst, sumw, newsum, w, nwords, nlines, len, xwidth; +NUMA *na; +SARRAY *sa, *sawords; + + PROCNAME("bmfGetLineStrings"); + + if (!bmf) + return (SARRAY *)ERROR_PTR("bmf not defined", procName, NULL); + if (!textstr) + return (SARRAY *)ERROR_PTR("teststr not defined", procName, NULL); + + if ((sawords = sarrayCreateWordsFromString(textstr)) == NULL) + return (SARRAY *)ERROR_PTR("sawords not made", procName, NULL); + + if ((na = bmfGetWordWidths(bmf, textstr, sawords)) == NULL) { + sarrayDestroy(&sawords); + return (SARRAY *)ERROR_PTR("na not made", procName, NULL); + } + nwords = numaGetCount(na); + if (nwords == 0) { + sarrayDestroy(&sawords); + numaDestroy(&na); + return (SARRAY *)ERROR_PTR("no words in textstr", procName, NULL); + } + bmfGetWidth(bmf, 'x', &xwidth); + + sa = sarrayCreate(0); + ifirst = 0; + numaGetIValue(na, 0, &w); + sumw = firstindent * xwidth + w; + for (i = 1; i < nwords; i++) { + numaGetIValue(na, i, &w); + newsum = sumw + bmf->spacewidth + w; + if (newsum > maxw) { + linestr = sarrayToStringRange(sawords, ifirst, i - ifirst, 2); + if (!linestr) + continue; + len = strlen(linestr); + if (len > 0) /* it should always be */ + linestr[len - 1] = '\0'; /* remove the last space */ + sarrayAddString(sa, linestr, L_INSERT); + ifirst = i; + sumw = w; + } + else + sumw += bmf->spacewidth + w; + } + linestr = sarrayToStringRange(sawords, ifirst, nwords - ifirst, 2); + if (linestr) + sarrayAddString(sa, linestr, L_INSERT); + nlines = sarrayGetCount(sa); + *ph = nlines * bmf->lineheight + (nlines - 1) * bmf->vertlinesep; + + sarrayDestroy(&sawords); + numaDestroy(&na); + return sa; +} + + +/*! + * \brief bmfGetWordWidths() + * + * \param[in] bmf + * \param[in] textstr + * \param[in] sa of individual words + * \return numa of word lengths in pixels for the font represented + * by the bmf, or NULL on error + */ +NUMA * +bmfGetWordWidths(L_BMF *bmf, + const char *textstr, + SARRAY *sa) +{ +char *wordstr; +l_int32 i, nwords, width; +NUMA *na; + + PROCNAME("bmfGetWordWidths"); + + if (!bmf) + return (NUMA *)ERROR_PTR("bmf not defined", procName, NULL); + if (!textstr) + return (NUMA *)ERROR_PTR("teststr not defined", procName, NULL); + if (!sa) + return (NUMA *)ERROR_PTR("sa not defined", procName, NULL); + + nwords = sarrayGetCount(sa); + if ((na = numaCreate(nwords)) == NULL) + return (NUMA *)ERROR_PTR("na not made", procName, NULL); + + for (i = 0; i < nwords; i++) { + wordstr = sarrayGetString(sa, i, L_NOCOPY); + bmfGetStringWidth(bmf, wordstr, &width); + numaAddNumber(na, width); + } + + return na; +} + + +/*! + * \brief bmfGetStringWidth() + * + * \param[in] bmf + * \param[in] textstr + * \param[out] pw width of text string, in pixels for the + * font represented by the bmf + * \return 0 if OK, 1 on error + */ +l_ok +bmfGetStringWidth(L_BMF *bmf, + const char *textstr, + l_int32 *pw) +{ +char chr; +l_int32 i, w, width, nchar; + + PROCNAME("bmfGetStringWidth"); + + if (!bmf) + return ERROR_INT("bmf not defined", procName, 1); + if (!textstr) + return ERROR_INT("teststr not defined", procName, 1); + if (!pw) + return ERROR_INT("&w not defined", procName, 1); + + nchar = strlen(textstr); + w = 0; + for (i = 0; i < nchar; i++) { + chr = textstr[i]; + bmfGetWidth(bmf, chr, &width); + if (width != UNDEF) + w += width + bmf->kernwidth; + } + w -= bmf->kernwidth; /* remove last one */ + + *pw = w; + return 0; +} + + + +/*---------------------------------------------------------------------* + * Text splitting * + *---------------------------------------------------------------------*/ +/*! + * \brief splitStringToParagraphs() + * + * \param[in] textstr text string + * \param[in] splitflag see enum in bmf.h; valid values in {1,2,3} + * \return sarray where each string is a paragraph of the input, + * or NULL on error. + */ +SARRAY * +splitStringToParagraphs(char *textstr, + l_int32 splitflag) +{ +char *linestr, *parastring; +l_int32 nlines, i, allwhite, leadwhite; +SARRAY *salines, *satemp, *saout; + + PROCNAME("splitStringToParagraphs"); + + if (!textstr) + return (SARRAY *)ERROR_PTR("textstr not defined", procName, NULL); + + if ((salines = sarrayCreateLinesFromString(textstr, 1)) == NULL) + return (SARRAY *)ERROR_PTR("salines not made", procName, NULL); + nlines = sarrayGetCount(salines); + saout = sarrayCreate(0); + satemp = sarrayCreate(0); + + linestr = sarrayGetString(salines, 0, L_NOCOPY); + sarrayAddString(satemp, linestr, L_COPY); + for (i = 1; i < nlines; i++) { + linestr = sarrayGetString(salines, i, L_NOCOPY); + stringAllWhitespace(linestr, &allwhite); + stringLeadingWhitespace(linestr, &leadwhite); + if ((splitflag == SPLIT_ON_LEADING_WHITE && leadwhite) || + (splitflag == SPLIT_ON_BLANK_LINE && allwhite) || + (splitflag == SPLIT_ON_BOTH && (allwhite || leadwhite))) { + parastring = sarrayToString(satemp, 1); /* add nl to each line */ + sarrayAddString(saout, parastring, L_INSERT); + sarrayDestroy(&satemp); + satemp = sarrayCreate(0); + } + sarrayAddString(satemp, linestr, L_COPY); + } + parastring = sarrayToString(satemp, 1); /* add nl to each line */ + sarrayAddString(saout, parastring, L_INSERT); + sarrayDestroy(&satemp); + sarrayDestroy(&salines); + return saout; +} + + +/*! + * \brief stringAllWhitespace() + * + * \param[in] textstr text string + * \param[out] pval 1 if all whitespace; 0 otherwise + * \return 0 if OK, 1 on error + */ +static l_int32 +stringAllWhitespace(char *textstr, + l_int32 *pval) +{ +l_int32 len, i; + + PROCNAME("stringAllWhitespace"); + + if (!textstr) + return ERROR_INT("textstr not defined", procName, 1); + if (!pval) + return ERROR_INT("&va not defined", procName, 1); + + len = strlen(textstr); + *pval = 1; + for (i = 0; i < len; i++) { + if (textstr[i] != ' ' && textstr[i] != '\t' && textstr[i] != '\n') { + *pval = 0; + return 0; + } + } + return 0; +} + + +/*! + * \brief stringLeadingWhitespace() + * + * \param[in] textstr text string + * \param[out] pval 1 if leading char is [space] or [tab]; 0 otherwise + * \return 0 if OK, 1 on error + */ +static l_int32 +stringLeadingWhitespace(char *textstr, + l_int32 *pval) +{ + PROCNAME("stringLeadingWhitespace"); + + if (!textstr) + return ERROR_INT("textstr not defined", procName, 1); + if (!pval) + return ERROR_INT("&va not defined", procName, 1); + + *pval = 0; + if (textstr[0] == ' ' || textstr[0] == '\t') + *pval = 1; + + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/tiffio.c b/hgdriver/3rdparty/hgOCR/leptonica/tiffio.c new file mode 100644 index 0000000..5a761ff --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/tiffio.c @@ -0,0 +1,2843 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file tiffio.c + *
+ *
+ *     TIFFClientOpen() wrappers for FILE*:
+ *      static tsize_t    lept_read_proc()
+ *      static tsize_t    lept_write_proc()
+ *      static toff_t     lept_seek_proc()
+ *      static int        lept_close_proc()
+ *      static toff_t     lept_size_proc()
+ *
+ *     Reading tiff:
+ *             PIX       *pixReadTiff()             [ special top level ]
+ *             PIX       *pixReadStreamTiff()
+ *      static PIX       *pixReadFromTiffStream()
+ *
+ *     Writing tiff:
+ *             l_int32    pixWriteTiff()            [ special top level ]
+ *             l_int32    pixWriteTiffCustom()      [ special top level ]
+ *             l_int32    pixWriteStreamTiff()
+ *             l_int32    pixWriteStreamTiffWA()
+ *      static l_int32    pixWriteToTiffStream()
+ *      static l_int32    writeCustomTiffTags()
+ *
+ *     Reading and writing multipage tiff
+ *             PIX       *pixReadFromMultipageTiff()
+ *             PIXA      *pixaReadMultipageTiff()   [ special top level ]
+ *             l_int32    pixaWriteMultipageTiff()  [ special top level ]
+ *             l_int32    writeMultipageTiff()      [ special top level ]
+ *             l_int32    writeMultipageTiffSA()
+ *
+ *     Information about tiff file
+ *             l_int32    fprintTiffInfo()
+ *             l_int32    tiffGetCount()
+ *             l_int32    getTiffResolution()
+ *      static l_int32    getTiffStreamResolution()
+ *             l_int32    readHeaderTiff()
+ *             l_int32    freadHeaderTiff()
+ *             l_int32    readHeaderMemTiff()
+ *      static l_int32    tiffReadHeaderTiff()
+ *             l_int32    findTiffCompression()
+ *      static l_int32    getTiffCompressedFormat()
+ *
+ *     Extraction of tiff g4 data:
+ *             l_int32    extractG4DataFromFile()
+ *
+ *     Open tiff stream from file stream
+ *      static TIFF      *fopenTiff()
+ *
+ *     Wrapper for TIFFOpen:
+ *      static TIFF      *openTiff()
+ *
+ *     Memory I/O: reading memory --> pix and writing pix --> memory
+ *             [10 static helper functions]
+ *             PIX       *pixReadMemTiff();
+ *             PIX       *pixReadMemFromMultipageTiff();
+ *             PIXA      *pixaReadMemMultipageTiff()    [ special top level ]
+ *             l_int32    pixaWriteMemMultipageTiff()   [ special top level ]
+ *             l_int32    pixWriteMemTiff();
+ *             l_int32    pixWriteMemTiffCustom();
+ *
+ *  Note:  To include all necessary functions, use libtiff version 3.7.4
+ *         (or later)
+ *  Note:  On Windows with 2 bpp or 4 bpp images, the bytes in the
+ *         tiff-compressed file depend on the pad bits (but not the
+ *         decoded raster image when read).  Because it is sometimes
+ *         convenient to use a golden file with a byte-by-byte check
+ *         to verify invariance, we set the pad bits to 0 before writing,
+ *         in pixWriteToTiffStream().
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include /* for isnan */ +#include +#ifndef _MSC_VER +#include +#else /* _MSC_VER */ +#include +#endif /* _MSC_VER */ +#include +#include "allheaders.h" + +/* --------------------------------------------*/ +#if HAVE_LIBTIFF /* defined in environ.h */ +/* --------------------------------------------*/ + +#include "tiff.h" +#include "tiffio.h" + +static const l_int32 DefaultResolution = 300; /* ppi */ +static const l_int32 ManyPagesInTiffFile = 3000; /* warn if big */ +static const l_uint32 MaxTiffBufferSize = 1 << 24; /* 16MiB */ + + + /* All functions with TIFF interfaces are static. */ +static PIX *pixReadFromTiffStream(TIFF *tif); +static l_int32 getTiffStreamResolution(TIFF *tif, l_int32 *pxres, + l_int32 *pyres); +static l_int32 tiffReadHeaderTiff(TIFF *tif, l_int32 *pwidth, + l_int32 *pheight, l_int32 *pbps, + l_int32 *pspp, l_int32 *pres, + l_int32 *pcmap, l_int32 *pformat); +static l_int32 writeCustomTiffTags(TIFF *tif, NUMA *natags, + SARRAY *savals, SARRAY *satypes, + NUMA *nasizes); +static l_int32 pixWriteToTiffStream(TIFF *tif, PIX *pix, l_int32 comptype, + NUMA *natags, SARRAY *savals, + SARRAY *satypes, NUMA *nasizes); +static TIFF *fopenTiff(FILE *fp, const char *modestring); +static TIFF *openTiff(const char *filename, const char *modestring); + + /* Static helper for tiff compression type */ +static l_int32 getTiffCompressedFormat(l_uint16 tiffcomp); + + /* Static function for memory I/O */ +static TIFF *fopenTiffMemstream(const char *filename, const char *operation, + l_uint8 **pdata, size_t *pdatasize); + + /* This structure defines a transform to be performed on a TIFF image + * (note that the same transformation can be represented in + * several different ways using this structure since + * vflip + hflip + counterclockwise == clockwise). */ +struct tiff_transform { + int vflip; /* if non-zero, image needs a vertical fip */ + int hflip; /* if non-zero, image needs a horizontal flip */ + int rotate; /* -1 -> counterclockwise 90-degree rotation, + 0 -> no rotation + 1 -> clockwise 90-degree rotation */ +}; + + /* This describes the transformations needed for a given orientation + * tag. The tag values start at 1, so you need to subtract 1 to get a + * valid index into this array. It is only valid when not using + * TIFFReadRGBAImageOriented(). */ +static struct tiff_transform tiff_orientation_transforms[] = { + {0, 0, 0}, + {0, 1, 0}, + {1, 1, 0}, + {1, 0, 0}, + {0, 1, -1}, + {0, 0, 1}, + {0, 1, 1}, + {0, 0, -1} +}; + + /* Same as above, except that test transformations are only valid + * when using TIFFReadRGBAImageOriented(). Transformations + * were determined empirically. See the libtiff mailing list for + * more discussion: http://www.asmail.be/msg0054683875.html */ +static struct tiff_transform tiff_partial_orientation_transforms[] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 1, -1}, + {0, 1, 1}, + {1, 0, 1}, + {0, 1, -1} +}; + + +/*-----------------------------------------------------------------------* + * TIFFClientOpen() wrappers for FILE* * + * Provided by J眉rgen Buchm眉ller * + * * + * We previously used TIFFFdOpen(), which used low-level file * + * descriptors. It had portability issues with Windows, along * + * with other limitations from lack of stream control operations. * + * These callbacks to TIFFClientOpen() avoid the problems. * + * * + * J眉rgen made the functions use 64 bit file operations where possible * + * or required, namely for seek and size. On Windows there are specific * + * _fseeki64() and _ftelli64() functions, whereas on unix it is * + * common to look for a macro _LARGEFILE_SOURCE being defined and * + * use fseeko() and ftello() in this case. * + *-----------------------------------------------------------------------*/ +static tsize_t +lept_read_proc(thandle_t cookie, + tdata_t buff, + tsize_t size) +{ + FILE* fp = (FILE *)cookie; + tsize_t done; + if (!buff || !cookie || !fp) + return (tsize_t)-1; + done = fread(buff, 1, size, fp); + return done; +} + +static tsize_t +lept_write_proc(thandle_t cookie, + tdata_t buff, + tsize_t size) +{ + FILE* fp = (FILE *)cookie; + tsize_t done; + if (!buff || !cookie || !fp) + return (tsize_t)-1; + done = fwrite(buff, 1, size, fp); + return done; +} + +static toff_t +lept_seek_proc(thandle_t cookie, + toff_t offs, + int whence) +{ + FILE* fp = (FILE *)cookie; +#if defined(_MSC_VER) + __int64 pos = 0; + if (!cookie || !fp) + return (tsize_t)-1; + switch (whence) { + case SEEK_SET: + pos = 0; + break; + case SEEK_CUR: + pos = ftell(fp); + break; + case SEEK_END: + _fseeki64(fp, 0, SEEK_END); + pos = _ftelli64(fp); + break; + } + pos = (__int64)(pos + offs); + _fseeki64(fp, pos, SEEK_SET); + if (pos == _ftelli64(fp)) + return (tsize_t)pos; +#elif defined(_LARGEFILE_SOURCE) + off64_t pos = 0; + if (!cookie || !fp) + return (tsize_t)-1; + switch (whence) { + case SEEK_SET: + pos = 0; + break; + case SEEK_CUR: + pos = ftello(fp); + break; + case SEEK_END: + fseeko(fp, 0, SEEK_END); + pos = ftello(fp); + break; + } + pos = (off64_t)(pos + offs); + fseeko(fp, pos, SEEK_SET); + if (pos == ftello(fp)) + return (tsize_t)pos; +#else + off_t pos = 0; + if (!cookie || !fp) + return (tsize_t)-1; + switch (whence) { + case SEEK_SET: + pos = 0; + break; + case SEEK_CUR: + pos = ftell(fp); + break; + case SEEK_END: + fseek(fp, 0, SEEK_END); + pos = ftell(fp); + break; + } + pos = (off_t)(pos + offs); + fseek(fp, pos, SEEK_SET); + if (pos == ftell(fp)) + return (tsize_t)pos; +#endif + return (tsize_t)-1; +} + +static int +lept_close_proc(thandle_t cookie) +{ + FILE* fp = (FILE *)cookie; + if (!cookie || !fp) + return 0; + fseek(fp, 0, SEEK_SET); + return 0; +} + +static toff_t +lept_size_proc(thandle_t cookie) +{ + FILE* fp = (FILE *)cookie; +#if defined(_MSC_VER) + __int64 pos; + __int64 size; + if (!cookie || !fp) + return (tsize_t)-1; + pos = _ftelli64(fp); + _fseeki64(fp, 0, SEEK_END); + size = _ftelli64(fp); + _fseeki64(fp, pos, SEEK_SET); +#elif defined(_LARGEFILE_SOURCE) + off64_t pos; + off64_t size; + if (!fp) + return (tsize_t)-1; + pos = ftello(fp); + fseeko(fp, 0, SEEK_END); + size = ftello(fp); + fseeko(fp, pos, SEEK_SET); +#else + off_t pos; + off_t size; + if (!cookie || !fp) + return (tsize_t)-1; + pos = ftell(fp); + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, pos, SEEK_SET); +#endif + return (toff_t)size; +} + + +/*--------------------------------------------------------------* + * Reading from file * + *--------------------------------------------------------------*/ +/*! + * \brief pixReadTiff() + * + * \param[in] filename + * \param[in] n page number 0 based + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a version of pixRead(), specialized for tiff
+ *          files, that allows specification of the page to be returned
+ *      (2) No warning messages on failure, because of how multi-page
+ *          TIFF reading works. You are supposed to keep trying until
+ *          it stops working.
+ * 
+ */ +PIX * +pixReadTiff(const char *filename, + l_int32 n) +{ +FILE *fp; +PIX *pix; + + PROCNAME("pixReadTiff"); + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX *)ERROR_PTR("image file not found", procName, NULL); + pix = pixReadStreamTiff(fp, n); + fclose(fp); + return pix; +} + + +/*--------------------------------------------------------------* + * Reading from stream * + *--------------------------------------------------------------*/ +/*! + * \brief pixReadStreamTiff() + * + * \param[in] fp file stream + * \param[in] n page number: 0 based + * \return pix, or NULL on error or if there are no more images in the file + * + *
+ * Notes:
+ *      (1) No warning messages on failure, because of how multi-page
+ *          TIFF reading works. You are supposed to keep trying until
+ *          it stops working.
+ * 
+ */ +PIX * +pixReadStreamTiff(FILE *fp, + l_int32 n) +{ +PIX *pix; +TIFF *tif; + + PROCNAME("pixReadStreamTiff"); + + if (!fp) + return (PIX *)ERROR_PTR("stream not defined", procName, NULL); + + if ((tif = fopenTiff(fp, "r")) == NULL) + return (PIX *)ERROR_PTR("tif not opened", procName, NULL); + + if (TIFFSetDirectory(tif, n) == 0) { + TIFFCleanup(tif); + return NULL; + } + if ((pix = pixReadFromTiffStream(tif)) == NULL) { + TIFFCleanup(tif); + return NULL; + } + TIFFCleanup(tif); + return pix; +} + + +/*! + * \brief pixReadFromTiffStream() + * + * \param[in] tif TIFF handle + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) We can read the following images (up to 32 bits/pixel):
+ *          1 spp (grayscale): 1, 2, 4, 8, 16 bps
+ *          1 spp (colormapped): 1, 2, 4, 8 bps
+ *          2 spp (gray+alpha): 8 bps
+ *          3 spp (rgb) and 4 spp (rgba): 8 or 16 bps
+ *      (2) We do not handle 16 bps for spp == 2.
+ *      (3) 2 bpp gray+alpha are rasterized as 32 bit/pixel rgba, with
+ *          the gray value replicated in r, g and b.
+ *      (4) For colormapped images, we support 8 bits/color in the palette.
+ *          Tiff colormaps have 16 bits/color, and we reduce them to 8.
+ *      (5) Quoting the libtiff documentation at
+ *               http://libtiff.maptools.org/libtiff.html
+ *          "libtiff provides a high-level interface for reading image data
+ *          from a TIFF file. This interface handles the details of data
+ *          organization and format for a wide variety of TIFF files;
+ *          at least the large majority of those files that one would
+ *          normally encounter. Image data is, by default, returned as
+ *          ABGR pixels packed into 32-bit words (8 bits per sample).
+ *          Rectangular rasters can be read or data can be intercepted
+ *          at an intermediate level and packed into memory in a format
+ *          more suitable to the application. The library handles all
+ *          the details of the format of data stored on disk and,
+ *          in most cases, if any colorspace conversions are required:
+ *          bilevel to RGB, greyscale to RGB, CMYK to RGB, YCbCr to RGB,
+ *          16-bit samples to 8-bit samples, associated/unassociated alpha,
+ *          etc."
+ * 
+ */ +static PIX * +pixReadFromTiffStream(TIFF *tif) +{ +char *text; +l_uint8 *linebuf, *data, *rowptr; +l_uint16 spp, bps, photometry, tiffcomp, orientation, sample_fmt; +l_uint16 *redmap, *greenmap, *bluemap; +l_int32 d, wpl, bpl, comptype, i, j, k, ncolors, rval, gval, bval, aval; +l_int32 xres, yres; +l_uint32 w, h, tiffbpl, tiffword, read_oriented; +l_uint32 *line, *ppixel, *tiffdata, *pixdata; +PIX *pix; +PIXCMAP *cmap; + + PROCNAME("pixReadFromTiffStream"); + + if (!tif) + return (PIX *)ERROR_PTR("tif not defined", procName, NULL); + + read_oriented = 0; + + /* Only accept uint image data: + * SAMPLEFORMAT_UINT = 1; + * SAMPLEFORMAT_INT = 2; + * SAMPLEFORMAT_IEEEFP = 3; + * SAMPLEFORMAT_VOID = 4; */ + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_fmt); + if (sample_fmt != SAMPLEFORMAT_UINT) { + L_ERROR("sample format = %d is not uint\n", procName, sample_fmt); + return NULL; + } + + /* Can't read tiff in tiled format. For what is involved, see, e.g: + * https://www.cs.rochester.edu/~nelson/courses/vision/\ + * resources/tiff/libtiff.html#Tiles + * A tiled tiff can be converted to a normal (strip) tif: + * tiffcp -s */ + if (TIFFIsTiled(tif)) { + L_ERROR("tiled format is not supported\n", procName); + return NULL; + } + + /* Use default fields for bps and spp */ + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps != 16) { + L_ERROR("invalid bps = %d\n", procName, bps); + return NULL; + } + if (spp == 2 && bps != 8) { + L_WARNING("only handle 8 bps for 2 spp\n", procName); + return NULL; + } + if (spp == 1) + d = bps; + else if (spp == 2) /* gray plus alpha */ + d = 32; /* will convert to RGBA */ + else if (spp == 3 || spp == 4) + d = 32; + else + return (PIX *)ERROR_PTR("spp not in set {1,2,3,4}", procName, NULL); + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); + tiffbpl = TIFFScanlineSize(tif); + if (tiffbpl < (bps * spp * w + 7) / 8) + return (PIX *)ERROR_PTR("bad tiff file: tiffbpl is too small", + procName, NULL); + if (tiffbpl > MaxTiffBufferSize) + return (PIX *)ERROR_PTR("bad tiff file: tiffbpl is too large", + procName, NULL); + + if ((pix = pixCreate(w, h, d)) == NULL) + return (PIX *)ERROR_PTR("pix not made", procName, NULL); + pixSetInputFormat(pix, IFF_TIFF); + data = (l_uint8 *)pixGetData(pix); + wpl = pixGetWpl(pix); + bpl = 4 * wpl; + + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp); + + /* Thanks to Jeff Breidenbach, we now support reading 8 bpp + * images encoded in the long-deprecated old jpeg format, + * COMPRESSION_OJPEG. TIFFReadScanline() fails on this format, + * so we use RGBA reading, which generates a 4 spp image, and + * pull out the red component. */ + if (spp == 1 && tiffcomp != COMPRESSION_OJPEG) { + linebuf = (l_uint8 *)LEPT_CALLOC(tiffbpl + 1, sizeof(l_uint8)); + for (i = 0; i < h; i++) { + if (TIFFReadScanline(tif, linebuf, i, 0) < 0) { + LEPT_FREE(linebuf); + pixDestroy(&pix); + return (PIX *)ERROR_PTR("line read fail", procName, NULL); + } + memcpy(data, linebuf, tiffbpl); + data += bpl; + } + if (bps <= 8) + pixEndianByteSwap(pix); + else /* bps == 16 */ + pixEndianTwoByteSwap(pix); + LEPT_FREE(linebuf); + } else if (spp == 2 && bps == 8) { /* gray plus alpha */ + L_INFO("gray+alpha is not supported; converting to RGBA\n", procName); + pixSetSpp(pix, 4); + linebuf = (l_uint8 *)LEPT_CALLOC(tiffbpl + 1, sizeof(l_uint8)); + pixdata = pixGetData(pix); + for (i = 0; i < h; i++) { + if (TIFFReadScanline(tif, linebuf, i, 0) < 0) { + LEPT_FREE(linebuf); + pixDestroy(&pix); + return (PIX *)ERROR_PTR("line read fail", procName, NULL); + } + rowptr = linebuf; + ppixel = pixdata + i * wpl; + for (j = k = 0; j < w; j++) { + /* Copy gray value into r, g and b */ + SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); + SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); + SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); + ppixel++; + } + } + LEPT_FREE(linebuf); + } else { /* rgb, rgba, or old jpeg */ + if ((tiffdata = (l_uint32 *)LEPT_CALLOC((size_t)w * h, + sizeof(l_uint32))) == NULL) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("calloc fail for tiffdata", procName, NULL); + } + /* TIFFReadRGBAImageOriented() converts to 8 bps */ + if (!TIFFReadRGBAImageOriented(tif, w, h, (uint32 *)tiffdata, + ORIENTATION_TOPLEFT, 0)) { + LEPT_FREE(tiffdata); + pixDestroy(&pix); + return (PIX *)ERROR_PTR("failed to read tiffdata", procName, NULL); + } else { + read_oriented = 1; + } + + if (spp == 1) { /* 8 bpp, old jpeg format */ + pixdata = pixGetData(pix); + for (i = 0; i < h; i++) { + line = pixdata + i * wpl; + for (j = 0; j < w; j++) { + tiffword = tiffdata[i * w + j]; + rval = TIFFGetR(tiffword); + SET_DATA_BYTE(line, j, rval); + } + } + } else { /* rgb or rgba */ + if (spp == 4) pixSetSpp(pix, 4); + line = pixGetData(pix); + for (i = 0; i < h; i++, line += wpl) { + for (j = 0, ppixel = line; j < w; j++) { + /* TIFFGet* are macros */ + tiffword = tiffdata[i * w + j]; + rval = TIFFGetR(tiffword); + gval = TIFFGetG(tiffword); + bval = TIFFGetB(tiffword); + if (spp == 3) { + composeRGBPixel(rval, gval, bval, ppixel); + } else { /* spp == 4 */ + aval = TIFFGetA(tiffword); + composeRGBAPixel(rval, gval, bval, aval, ppixel); + } + ppixel++; + } + } + } + LEPT_FREE(tiffdata); + } + + if (getTiffStreamResolution(tif, &xres, &yres) == 0) { + pixSetXRes(pix, xres); + pixSetYRes(pix, yres); + } + + /* Find and save the compression type */ + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp); + comptype = getTiffCompressedFormat(tiffcomp); + pixSetInputFormat(pix, comptype); + + if (TIFFGetField(tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) { + /* Save the colormap as a pix cmap. Because the + * tiff colormap components are 16 bit unsigned, + * and go from black (0) to white (0xffff), the + * the pix cmap takes the most significant byte. */ + if (bps > 8) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("colormap size > 256", procName, NULL); + } + if ((cmap = pixcmapCreate(bps)) == NULL) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("colormap not made", procName, NULL); + } + ncolors = 1 << bps; + for (i = 0; i < ncolors; i++) + pixcmapAddColor(cmap, redmap[i] >> 8, greenmap[i] >> 8, + bluemap[i] >> 8); + pixSetColormap(pix, cmap); + } else { /* No colormap: check photometry and invert if necessary */ + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometry)) { + /* Guess default photometry setting. Assume min_is_white + * if compressed 1 bpp; min_is_black otherwise. */ + if (tiffcomp == COMPRESSION_CCITTFAX3 || + tiffcomp == COMPRESSION_CCITTFAX4 || + tiffcomp == COMPRESSION_CCITTRLE || + tiffcomp == COMPRESSION_CCITTRLEW) { + photometry = PHOTOMETRIC_MINISWHITE; + } else { + photometry = PHOTOMETRIC_MINISBLACK; + } + } + if ((d == 1 && photometry == PHOTOMETRIC_MINISBLACK) || + (d == 8 && photometry == PHOTOMETRIC_MINISWHITE)) + pixInvert(pix, pix); + } + + if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) { + if (orientation >= 1 && orientation <= 8) { + struct tiff_transform *transform = (read_oriented) ? + &tiff_partial_orientation_transforms[orientation - 1] : + &tiff_orientation_transforms[orientation - 1]; + if (transform->vflip) pixFlipTB(pix, pix); + if (transform->hflip) pixFlipLR(pix, pix); + if (transform->rotate) { + PIX *oldpix = pix; + pix = pixRotate90(oldpix, transform->rotate); + pixDestroy(&oldpix); + } + } + } + + text = NULL; + TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &text); + if (text) pixSetText(pix, text); + return pix; +} + + + +/*--------------------------------------------------------------* + * Writing to file * + *--------------------------------------------------------------*/ +/*! + * \brief pixWriteTiff() + * + * \param[in] filename to write to + * \param[in] pix any depth, colormap will be removed + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \param[in] modestr "a" or "w" + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For multipage tiff, write the first pix with mode "w" and
+ *          all subsequent pix with mode "a".
+ *      (2) For multipage tiff, there is considerable overhead in the
+ *          machinery to append an image and add the directory entry,
+ *          and the time required for each image increases linearly
+ *          with the number of images in the file.
+ * 
+ */ +l_ok +pixWriteTiff(const char *filename, + PIX *pix, + l_int32 comptype, + const char *modestr) +{ + return pixWriteTiffCustom(filename, pix, comptype, modestr, + NULL, NULL, NULL, NULL); +} + + +/*! + * \brief pixWriteTiffCustom() + * + * \param[in] filename to write to + * \param[in] pix + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \param[in] modestr "a" or "w" + * \param[in] natags [optional] NUMA of custom tiff tags + * \param[in] savals [optional] SARRAY of values + * \param[in] satypes [optional] SARRAY of types + * \param[in] nasizes [optional] NUMA of sizes + * \return 0 if OK, 1 on error + * + * Usage: + * 1 This writes a page image to a tiff file, with optional + * extra tags defined in tiff.h + * 2 For multipage tiff, write the first pix with mode "w" and + * all subsequent pix with mode "a". + * 3 For the custom tiff tags: + * a The three arrays {natags, savals, satypes} must all be + * either NULL or defined and of equal size. + * b If they are defined, the tags are an array of integers, + * the vals are an array of values in string format, and + * the types are an array of types in string format. + * c All valid tags are definined in tiff.h. + * d The types allowed are the set of strings: + * "char*" + * "l_uint8*" + * "l_uint16" + * "l_uint32" + * "l_int32" + * "l_float64" + * "l_uint16-l_uint16" note the dash; use it between the + * two l_uint16 vals in the val string + * Of these, "char*" and "l_uint16" are the most commonly used. + * e The last array, nasizes, is also optional. It is for + * tags that take an array of bytes for a value, a number of + * elements in the array, and a type that is either "char*" + * or "l_uint8*" probably either will work. + * Use NULL if there are no such tags. + * f VERY IMPORTANT: if there are any tags that require the + * extra size value, stored in nasizes, they must be + * written first! + */ +l_ok +pixWriteTiffCustom(const char *filename, + PIX *pix, + l_int32 comptype, + const char *modestr, + NUMA *natags, + SARRAY *savals, + SARRAY *satypes, + NUMA *nasizes) +{ +l_int32 ret; +TIFF *tif; + + PROCNAME("pixWriteTiffCustom"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if ((tif = openTiff(filename, modestr)) == NULL) + return ERROR_INT("tif not opened", procName, 1); + ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals, + satypes, nasizes); + TIFFClose(tif); + return ret; +} + + +/*--------------------------------------------------------------* + * Writing to stream * + *--------------------------------------------------------------*/ +/*! + * \brief pixWriteStreamTiff() + * + * \param[in] fp file stream + * \param[in] pix + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This writes a single image to a file stream opened for writing.
+ *      (2) If the pix has a colormap, it is preserved in the output file.
+ *      (3) For images with bpp > 1, this resets the comptype, if
+ *          necessary, to write uncompressed data.
+ *      (4) G3 and G4 are only defined for 1 bpp.
+ *      (5) We only allow PACKBITS for bpp = 1, because for bpp > 1
+ *          it typically expands images that are not synthetically generated.
+ *      (6) G4 compression is typically about twice as good as G3.
+ *          G4 is excellent for binary compression of text/line-art,
+ *          but terrible for halftones and dithered patterns.  (In
+ *          fact, G4 on halftones can give a file that is larger
+ *          than uncompressed!)  If a binary image has dithered
+ *          regions, it is usually better to compress with png.
+ * 
+ */ +l_ok +pixWriteStreamTiff(FILE *fp, + PIX *pix, + l_int32 comptype) +{ + return pixWriteStreamTiffWA(fp, pix, comptype, "w"); +} + + +/*! + * \brief pixWriteStreamTiffWA() + * + * \param[in] fp file stream opened for append or write + * \param[in] pix + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \param[in] modestr "w" or "a" + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteStreamTiff()
+ * 
+ */ +l_ok +pixWriteStreamTiffWA(FILE *fp, + PIX *pix, + l_int32 comptype, + const char *modestr) +{ +TIFF *tif; + + PROCNAME("pixWriteStreamTiffWA"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1 ); + if (!pix) + return ERROR_INT("pix not defined", procName, 1 ); + if (strcmp(modestr, "w") && strcmp(modestr, "a")) + return ERROR_INT("modestr not 'w' or 'a'", procName, 1 ); + + if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF && + comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP && + comptype != IFF_TIFF_JPEG) { + L_WARNING("invalid compression type for bpp > 1\n", procName); + comptype = IFF_TIFF_ZIP; + } + + if ((tif = fopenTiff(fp, modestr)) == NULL) + return ERROR_INT("tif not opened", procName, 1); + + if (pixWriteToTiffStream(tif, pix, comptype, NULL, NULL, NULL, NULL)) { + TIFFCleanup(tif); + return ERROR_INT("tif write error", procName, 1); + } + + TIFFCleanup(tif); + return 0; +} + + +/*! + * \brief pixWriteToTiffStream() + * + * \param[in] tif data structure, opened to a file + * \param[in] pix + * \param[in] comptype IFF_TIFF: for any image; no compression + * IFF_TIFF_RLE, IFF_TIFF_PACKBITS: for 1 bpp only + * IFF_TIFF_G4 and IFF_TIFF_G3: for 1 bpp only + * IFF_TIFF_LZW, IFF_TIFF_ZIP: lossless for any image + * IFF_TIFF_JPEG: lossy 8 bpp gray or rgb + * \param[in] natags [optional] NUMA of custom tiff tags + * \param[in] savals [optional] SARRAY of values + * \param[in] satypes [optional] SARRAY of types + * \param[in] nasizes [optional] NUMA of sizes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This static function should only be called through higher
+ *          level functions in this file; namely, pixWriteTiffCustom(),
+ *          pixWriteTiff(), pixWriteStreamTiff(), pixWriteMemTiff()
+ *          and pixWriteMemTiffCustom().
+ *      (2) We only allow PACKBITS for bpp = 1, because for bpp > 1
+ *          it typically expands images that are not synthetically generated.
+ *      (3) See pixWriteTiffCustom() for details on how to use
+ *          the last four parameters for customized tiff tags.
+ *      (4) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
+ *          and 32.  However, it is possible, and in some cases desirable,
+ *          to write out a tiff file using an rgb pix that has 24 bpp.
+ *          This can be created by appending the raster data for a 24 bpp
+ *          image (with proper scanline padding) directly to a 24 bpp
+ *          pix that was created without a data array.  See note in
+ *          pixWriteStreamPng() for an example.
+ * 
+ */ +static l_int32 +pixWriteToTiffStream(TIFF *tif, + PIX *pix, + l_int32 comptype, + NUMA *natags, + SARRAY *savals, + SARRAY *satypes, + NUMA *nasizes) +{ +l_uint8 *linebuf, *data; +l_uint16 redmap[256], greenmap[256], bluemap[256]; +l_int32 w, h, d, spp, i, j, k, wpl, bpl, tiffbpl, ncolors, cmapsize; +l_int32 *rmap, *gmap, *bmap; +l_int32 xres, yres; +l_uint32 *line, *ppixel; +PIX *pixt; +PIXCMAP *cmap; +char *text; + + PROCNAME("pixWriteToTiffStream"); + + if (!tif) + return ERROR_INT("tif stream not defined", procName, 1); + if (!pix) + return ERROR_INT( "pix not defined", procName, 1 ); + + pixSetPadBits(pix, 0); + pixGetDimensions(pix, &w, &h, &d); + spp = pixGetSpp(pix); + xres = pixGetXRes(pix); + yres = pixGetYRes(pix); + if (xres == 0) xres = DefaultResolution; + if (yres == 0) yres = DefaultResolution; + + /* ------------------ Write out the header ------------- */ + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (l_uint32)RESUNIT_INCH); + TIFFSetField(tif, TIFFTAG_XRESOLUTION, (l_float64)xres); + TIFFSetField(tif, TIFFTAG_YRESOLUTION, (l_float64)yres); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (l_uint32)w); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (l_uint32)h); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + + if ((text = pixGetText(pix)) != NULL) + TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, text); + + if (d == 1) + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); + else if ((d == 32 && spp == 3) || d == 24) { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)3); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, + (l_uint16)8, (l_uint16)8, (l_uint16)8); + } else if (d == 32 && spp == 4) { + l_uint16 val[1]; + val[0] = EXTRASAMPLE_ASSOCALPHA; + TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (l_uint16)1, &val); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)4); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, + (l_uint16)8, (l_uint16)8, (l_uint16)8, (l_uint16)8); + } else if (d == 16) { /* we only support spp = 1, bps = 16 */ + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } else if ((cmap = pixGetColormap(pix)) == NULL) { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } else { /* Save colormap in the tiff; not more than 256 colors */ + if (d > 8) { + L_ERROR("d = %d > 8 with colormap!; reducing to 8\n", procName, d); + d = 8; + } + pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL); + ncolors = pixcmapGetCount(cmap); + ncolors = L_MIN(256, ncolors); /* max 256 */ + cmapsize = 1 << d; + cmapsize = L_MIN(256, cmapsize); /* power of 2; max 256 */ + if (ncolors > cmapsize) { + L_WARNING("too many colors in cmap for tiff; truncating\n", + procName); + ncolors = cmapsize; + } + for (i = 0; i < ncolors; i++) { + redmap[i] = (rmap[i] << 8) | rmap[i]; + greenmap[i] = (gmap[i] << 8) | gmap[i]; + bluemap[i] = (bmap[i] << 8) | bmap[i]; + } + for (i = ncolors; i < cmapsize; i++) /* init, even though not used */ + redmap[i] = greenmap[i] = bluemap[i] = 0; + LEPT_FREE(rmap); + LEPT_FREE(gmap); + LEPT_FREE(bmap); + + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d); + TIFFSetField(tif, TIFFTAG_COLORMAP, redmap, greenmap, bluemap); + } + + if (d <= 16) { + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1); + } + + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + if (comptype == IFF_TIFF) { /* no compression */ + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + } else if (comptype == IFF_TIFF_G4) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4); + } else if (comptype == IFF_TIFF_G3) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3); + } else if (comptype == IFF_TIFF_RLE) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE); + } else if (comptype == IFF_TIFF_PACKBITS) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS); + } else if (comptype == IFF_TIFF_LZW) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + } else if (comptype == IFF_TIFF_ZIP) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); + } else if (comptype == IFF_TIFF_JPEG) { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); + } else { + L_WARNING("unknown tiff compression; using none\n", procName); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + } + + /* This is a no-op if arrays are NULL */ + writeCustomTiffTags(tif, natags, savals, satypes, nasizes); + + /* ------------- Write out the image data ------------- */ + tiffbpl = TIFFScanlineSize(tif); + wpl = pixGetWpl(pix); + bpl = 4 * wpl; + if (tiffbpl > bpl) + fprintf(stderr, "Big trouble: tiffbpl = %d, bpl = %d\n", tiffbpl, bpl); + if ((linebuf = (l_uint8 *)LEPT_CALLOC(1, bpl)) == NULL) + return ERROR_INT("calloc fail for linebuf", procName, 1); + + /* Use single strip for image */ + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, h); + + if (d != 24 && d != 32) { + if (d == 16) + pixt = pixEndianTwoByteSwapNew(pix); + else + pixt = pixEndianByteSwapNew(pix); + data = (l_uint8 *)pixGetData(pixt); + for (i = 0; i < h; i++, data += bpl) { + memcpy(linebuf, data, tiffbpl); + if (TIFFWriteScanline(tif, linebuf, i, 0) < 0) + break; + } + pixDestroy(&pixt); + } else if (d == 24) { /* See note 4 above: special case of 24 bpp rgb */ + for (i = 0; i < h; i++) { + line = pixGetData(pix) + i * wpl; + if (TIFFWriteScanline(tif, (l_uint8 *)line, i, 0) < 0) + break; + } + } else { /* 32 bpp rgb or rgba */ + for (i = 0; i < h; i++) { + line = pixGetData(pix) + i * wpl; + for (j = 0, k = 0, ppixel = line; j < w; j++) { + linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); + linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); + linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); + if (spp == 4) + linebuf[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); + ppixel++; + } + if (TIFFWriteScanline(tif, linebuf, i, 0) < 0) + break; + } + } + +/* TIFFWriteDirectory(tif); */ + LEPT_FREE(linebuf); + + return 0; +} + + +/*! + * \brief writeCustomTiffTags() + * + * \param[in] tif + * \param[in] natags [optional] NUMA of custom tiff tags + * \param[in] savals [optional] SARRAY of values + * \param[in] satypes [optional] SARRAY of types + * \param[in] nasizes [optional] NUMA of sizes + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This static function should be called indirectly through
+ *          higher level functions, such as pixWriteTiffCustom(),
+ *          which call pixWriteToTiffStream().  See details in
+ *          pixWriteTiffCustom() for using the 4 input arrays.
+ *      (2) This is a no-op if the first 3 arrays are all NULL.
+ *      (3) Otherwise, the first 3 arrays must be defined and all
+ *          of equal size.
+ *      (4) The fourth array is always optional.
+ *      (5) The most commonly used types are "char*" and "u_int16".
+ *          See tiff.h for a full listing of the tiff tags.
+ *          Note that many of these tags, in particular the bit tags,
+ *          are intended to be private, and cannot be set by this function.
+ *          Examples are the STRIPOFFSETS and STRIPBYTECOUNTS tags,
+ *          which are bit tags that are automatically set in the header,
+ *          and can be extracted using tiffdump.
+ * 
+ */ +static l_int32 +writeCustomTiffTags(TIFF *tif, + NUMA *natags, + SARRAY *savals, + SARRAY *satypes, + NUMA *nasizes) +{ +char *sval, *type; +l_int32 i, n, ns, size, tagval, val; +l_float64 dval; +l_uint32 uval, uval2; + + PROCNAME("writeCustomTiffTags"); + + if (!tif) + return ERROR_INT("tif stream not defined", procName, 1); + if (!natags && !savals && !satypes) + return 0; + if (!natags || !savals || !satypes) + return ERROR_INT("not all arrays defined", procName, 1); + n = numaGetCount(natags); + if ((sarrayGetCount(savals) != n) || (sarrayGetCount(satypes) != n)) + return ERROR_INT("not all sa the same size", procName, 1); + + /* The sized arrays (4 args to TIFFSetField) are written first */ + if (nasizes) { + ns = numaGetCount(nasizes); + if (ns > n) + return ERROR_INT("too many 4-arg tag calls", procName, 1); + for (i = 0; i < ns; i++) { + numaGetIValue(natags, i, &tagval); + sval = sarrayGetString(savals, i, L_NOCOPY); + type = sarrayGetString(satypes, i, L_NOCOPY); + numaGetIValue(nasizes, i, &size); + if (strcmp(type, "char*") && strcmp(type, "l_uint8*")) + L_WARNING("array type not char* or l_uint8*; ignore\n", + procName); + TIFFSetField(tif, tagval, size, sval); + } + } else { + ns = 0; + } + + /* The typical tags (3 args to TIFFSetField) are now written */ + for (i = ns; i < n; i++) { + numaGetIValue(natags, i, &tagval); + sval = sarrayGetString(savals, i, L_NOCOPY); + type = sarrayGetString(satypes, i, L_NOCOPY); + if (!strcmp(type, "char*")) { + TIFFSetField(tif, tagval, sval); + } else if (!strcmp(type, "l_uint16")) { + if (sscanf(sval, "%u", &uval) == 1) { + TIFFSetField(tif, tagval, (l_uint16)uval); + } else { + fprintf(stderr, "val %s not of type %s\n", sval, type); + return ERROR_INT("custom tag(s) not written", procName, 1); + } + } else if (!strcmp(type, "l_uint32")) { + if (sscanf(sval, "%u", &uval) == 1) { + TIFFSetField(tif, tagval, uval); + } else { + fprintf(stderr, "val %s not of type %s\n", sval, type); + return ERROR_INT("custom tag(s) not written", procName, 1); + } + } else if (!strcmp(type, "l_int32")) { + if (sscanf(sval, "%d", &val) == 1) { + TIFFSetField(tif, tagval, val); + } else { + fprintf(stderr, "val %s not of type %s\n", sval, type); + return ERROR_INT("custom tag(s) not written", procName, 1); + } + } else if (!strcmp(type, "l_float64")) { + if (sscanf(sval, "%lf", &dval) == 1) { + TIFFSetField(tif, tagval, dval); + } else { + fprintf(stderr, "val %s not of type %s\n", sval, type); + return ERROR_INT("custom tag(s) not written", procName, 1); + } + } else if (!strcmp(type, "l_uint16-l_uint16")) { + if (sscanf(sval, "%u-%u", &uval, &uval2) == 2) { + TIFFSetField(tif, tagval, (l_uint16)uval, (l_uint16)uval2); + } else { + fprintf(stderr, "val %s not of type %s\n", sval, type); + return ERROR_INT("custom tag(s) not written", procName, 1); + } + } else { + return ERROR_INT("unknown type; tag(s) not written", procName, 1); + } + } + return 0; +} + + +/*--------------------------------------------------------------* + * Reading and writing multipage tiff * + *--------------------------------------------------------------*/ +/*! + * \brief pixReadFromMultipageTiff() + * + * \param[in] fname filename + * \param[in,out] poffset set offset to 0 for first image + * \return pix, or NULL on error or if previous call returned the last image + * + *
+ * Notes:
+ *      (1) This allows overhead for traversal of a multipage tiff file
+ *          to be linear in the number of images.  This will also work
+ *          with a singlepage tiff file.
+ *      (2) No TIFF internal data structures are exposed to the caller
+ *          (thanks to Jeff Breidenbach).
+ *      (3) offset is the byte offset of a particular image in a multipage
+ *          tiff file. To get the first image in the file, input the
+ *          special offset value of 0.
+ *      (4) The offset is updated to point to the next image, for a
+ *          subsequent call.
+ *      (5) On the last image, the offset returned is 0.  Exit the loop
+ *          when the returned offset is 0.
+ *      (6) For reading a multipage tiff from a memory buffer, see
+ *            pixReadMemFromMultipageTiff()
+ *      (7) Example usage for reading all the images in the tif file:
+ *            size_t offset = 0;
+ *            do {
+ *                Pix *pix = pixReadFromMultipageTiff(filename, &offset);
+ *                // do something with pix
+ *            } while (offset != 0);
+ * 
+ */ +PIX * +pixReadFromMultipageTiff(const char *fname, + size_t *poffset) +{ +l_int32 retval; +size_t offset; +PIX *pix; +TIFF *tif; + + PROCNAME("pixReadFromMultipageTiff"); + + if (!fname) + return (PIX *)ERROR_PTR("fname not defined", procName, NULL); + if (!poffset) + return (PIX *)ERROR_PTR("&offset not defined", procName, NULL); + + if ((tif = openTiff(fname, "r")) == NULL) { + L_ERROR("tif open failed for %s\n", procName, fname); + return NULL; + } + + /* Set ptrs in the TIFF to the beginning of the image */ + offset = *poffset; + retval = (offset == 0) ? TIFFSetDirectory(tif, 0) + : TIFFSetSubDirectory(tif, offset); + if (retval == 0) { + TIFFCleanup(tif); + return NULL; + } + + if ((pix = pixReadFromTiffStream(tif)) == NULL) { + TIFFCleanup(tif); + return NULL; + } + + /* Advance to the next image and return the new offset */ + TIFFReadDirectory(tif); + *poffset = TIFFCurrentDirOffset(tif); + TIFFClose(tif); + return pix; +} + + +/*! + * \brief pixaReadMultipageTiff() + * + * \param[in] filename input tiff file + * \return pixa of page images, or NULL on error + */ +PIXA * +pixaReadMultipageTiff(const char *filename) +{ +l_int32 i, npages; +FILE *fp; +PIX *pix; +PIXA *pixa; +TIFF *tif; + + PROCNAME("pixaReadMultipageTiff"); + + if (!filename) + return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); + if (fileFormatIsTiff(fp)) { + tiffGetCount(fp, &npages); + L_INFO(" Tiff: %d pages\n", procName, npages); + } else { + return (PIXA *)ERROR_PTR("file not tiff", procName, NULL); + } + + if ((tif = fopenTiff(fp, "r")) == NULL) + return (PIXA *)ERROR_PTR("tif not opened", procName, NULL); + + pixa = pixaCreate(npages); + pix = NULL; + for (i = 0; i < npages; i++) { + if ((pix = pixReadFromTiffStream(tif)) != NULL) { + pixaAddPix(pixa, pix, L_INSERT); + } else { + L_WARNING("pix not read for page %d\n", procName, i); + } + + /* Advance to the next directory (i.e., the next image) */ + if (TIFFReadDirectory(tif) == 0) + break; + } + + fclose(fp); + TIFFCleanup(tif); + return pixa; +} + + +/*! + * \brief pixaWriteMultipageTiff() + * + * \param[in] fname input tiff file + * \param[in] pixa any depth; colormap will be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The tiff directory overhead is O(n^2).  I have not been
+ *          able to reduce it to O(n).  The overhead for n = 2000 is
+ *          about 1 second.
+ * 
+ */ +l_ok +pixaWriteMultipageTiff(const char *fname, + PIXA *pixa) +{ +const char *modestr; +l_int32 i, n; +PIX *pix1; + + PROCNAME("pixaWriteMultipageTiff"); + + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + modestr = (i == 0) ? "w" : "a"; + pix1 = pixaGetPix(pixa, i, L_CLONE); + if (pixGetDepth(pix1) == 1) + pixWriteTiff(fname, pix1, IFF_TIFF_G4, modestr); + else + pixWriteTiff(fname, pix1, IFF_TIFF_ZIP, modestr); + pixDestroy(&pix1); + } + + return 0; +} + + +/*! + * \brief writeMultipageTiff() + * + * \param[in] dirin input directory + * \param[in] substr [optional] substring filter on filenames; can be NULL + * \param[in] fileout output multipage tiff file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This writes a set of image files in a directory out
+ *          as a multipage tiff file.  The images can be in any
+ *          initial file format.
+ *      (2) Images with a colormap have the colormap removed before
+ *          re-encoding as tiff.
+ *      (3) All images are encoded losslessly.  Those with 1 bpp are
+ *          encoded 'g4'.  The rest are encoded as 'zip' (flate encoding).
+ *          Because it is lossless, this is an expensive method for
+ *          saving most rgb images.
+ *      (4) The tiff directory overhead is quadratic in the number of
+ *          images.  To avoid this for very large numbers of images to be
+ *          written, apply the method used in pixaWriteMultipageTiff().
+ * 
+ */ +l_ok +writeMultipageTiff(const char *dirin, + const char *substr, + const char *fileout) +{ +SARRAY *sa; + + PROCNAME("writeMultipageTiff"); + + if (!dirin) + return ERROR_INT("dirin not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + /* Get all filtered and sorted full pathnames. */ + sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0); + + /* Generate the tiff file */ + writeMultipageTiffSA(sa, fileout); + sarrayDestroy(&sa); + return 0; +} + + +/*! + * \brief writeMultipageTiffSA() + * + * \param[in] sa string array of full path names + * \param[in] fileout output ps file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See writeMultipageTiff()
+ * 
+ */ +l_ok +writeMultipageTiffSA(SARRAY *sa, + const char *fileout) +{ +char *fname; +const char *op; +l_int32 i, nfiles, firstfile, format; +PIX *pix; + + PROCNAME("writeMultipageTiffSA"); + + if (!sa) + return ERROR_INT("sa not defined", procName, 1); + if (!fileout) + return ERROR_INT("fileout not defined", procName, 1); + + nfiles = sarrayGetCount(sa); + firstfile = TRUE; + for (i = 0; i < nfiles; i++) { + op = (firstfile) ? "w" : "a"; + fname = sarrayGetString(sa, i, L_NOCOPY); + findFileFormat(fname, &format); + if (format == IFF_UNKNOWN) { + L_INFO("format of %s not known\n", procName, fname); + continue; + } + + if ((pix = pixRead(fname)) == NULL) { + L_WARNING("pix not made for file: %s\n", procName, fname); + continue; + } + if (pixGetDepth(pix) == 1) + pixWriteTiff(fileout, pix, IFF_TIFF_G4, op); + else + pixWriteTiff(fileout, pix, IFF_TIFF_ZIP, op); + firstfile = FALSE; + pixDestroy(&pix); + } + + return 0; +} + + +/*--------------------------------------------------------------* + * Print info to stream * + *--------------------------------------------------------------*/ +/*! + * \brief fprintTiffInfo() + * + * \param[in] fpout stream for output of tag data + * \param[in] tiffile input + * \return 0 if OK; 1 on error + */ +l_ok +fprintTiffInfo(FILE *fpout, + const char *tiffile) +{ +TIFF *tif; + + PROCNAME("fprintTiffInfo"); + + if (!tiffile) + return ERROR_INT("tiffile not defined", procName, 1); + if (!fpout) + return ERROR_INT("stream out not defined", procName, 1); + + if ((tif = openTiff(tiffile, "rb")) == NULL) + return ERROR_INT("tif not open for read", procName, 1); + + TIFFPrintDirectory(tif, fpout, 0); + TIFFClose(tif); + + return 0; +} + + +/*--------------------------------------------------------------* + * Get page count * + *--------------------------------------------------------------*/ +/*! + * \brief tiffGetCount() + * + * \param[in] fp file stream opened for read + * \param[out] pn number of images + * \return 0 if OK; 1 on error + */ +l_ok +tiffGetCount(FILE *fp, + l_int32 *pn) +{ +l_int32 i; +TIFF *tif; + + PROCNAME("tiffGetCount"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pn) + return ERROR_INT("&n not defined", procName, 1); + *pn = 0; + + if ((tif = fopenTiff(fp, "r")) == NULL) + return ERROR_INT("tif not open for read", procName, 1); + + for (i = 1; ; i++) { + if (TIFFReadDirectory(tif) == 0) + break; + if (i == ManyPagesInTiffFile + 1) { + L_WARNING("big file: more than %d pages\n", procName, + ManyPagesInTiffFile); + } + } + *pn = i; + TIFFCleanup(tif); + return 0; +} + + +/*--------------------------------------------------------------* + * Get resolution from tif * + *--------------------------------------------------------------*/ +/*! + * \brief getTiffResolution() + * + * \param[in] fp file stream opened for read + * \param[out] pxres, pyres resolution in ppi + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If neither resolution field is set, this is not an error;
+ *          the returned resolution values are 0 (designating 'unknown').
+ * 
+ */ +l_ok +getTiffResolution(FILE *fp, + l_int32 *pxres, + l_int32 *pyres) +{ +TIFF *tif; + + PROCNAME("getTiffResolution"); + + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", procName, 1); + *pxres = *pyres = 0; + if (!fp) + return ERROR_INT("stream not opened", procName, 1); + + if ((tif = fopenTiff(fp, "r")) == NULL) + return ERROR_INT("tif not open for read", procName, 1); + getTiffStreamResolution(tif, pxres, pyres); + TIFFCleanup(tif); + return 0; +} + + +/*! + * \brief getTiffStreamResolution() + * + * \param[in] tif TIFF handle opened for read + * \param[out] pxres, pyres resolution in ppi + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) If neither resolution field is set, this is not an error;
+ *          the returned resolution values are 0 (designating 'unknown').
+ * 
+ */ +static l_int32 +getTiffStreamResolution(TIFF *tif, + l_int32 *pxres, + l_int32 *pyres) +{ +l_uint16 resunit; +l_int32 foundxres, foundyres; +l_float32 fxres, fyres; + + PROCNAME("getTiffStreamResolution"); + + if (!tif) + return ERROR_INT("tif not opened", procName, 1); + if (!pxres || !pyres) + return ERROR_INT("&xres and &yres not both defined", procName, 1); + *pxres = *pyres = 0; + + TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit); + foundxres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &fxres); + foundyres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &fyres); + if (!foundxres && !foundyres) return 1; + if (isnan(fxres) || isnan(fyres)) return 1; + if (!foundxres && foundyres) + fxres = fyres; + else if (foundxres && !foundyres) + fyres = fxres; + + /* Avoid overflow into int32; set max fxres and fyres to 5 x 10^8 */ + if (fxres < 0 || fxres > (1L << 29) || fyres < 0 || fyres > (1L << 29)) + return ERROR_INT("fxres and/or fyres values are invalid", procName, 1); + + if (resunit == RESUNIT_CENTIMETER) { /* convert to ppi */ + *pxres = (l_int32)(2.54 * fxres + 0.5); + *pyres = (l_int32)(2.54 * fyres + 0.5); + } else { + *pxres = (l_int32)fxres; + *pyres = (l_int32)fyres; + } + + return 0; +} + + +/*--------------------------------------------------------------* + * Get some tiff header information * + *--------------------------------------------------------------*/ +/*! + * \brief readHeaderTiff() + * + * \param[in] filename + * \param[in] n page image number: 0-based + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbps [optional] bits per sample -- 1, 2, 4 or 8 + * \param[out] pspp [optional] samples per pixel -- 1 or 3 + * \param[out] pres [optional] resolution in x dir; NULL to ignore + * \param[out] pcmap [optional] colormap exists; input NULL to ignore + * \param[out] pformat [optional] tiff format; input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, cmap is returned as 1; else 0.
+ *      (2) If %n is equal to or greater than the number of images, returns 1.
+ * 
+ */ +l_ok +readHeaderTiff(const char *filename, + l_int32 n, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pres, + l_int32 *pcmap, + l_int32 *pformat) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("readHeaderTiff"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (pres) *pres = 0; + if (pcmap) *pcmap = 0; + if (pformat) *pformat = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat) + return ERROR_INT("no results requested", procName, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + ret = freadHeaderTiff(fp, n, pw, ph, pbps, pspp, pres, pcmap, pformat); + fclose(fp); + return ret; +} + + +/*! + * \brief freadHeaderTiff() + * + * \param[in] fp file stream + * \param[in] n page image number: 0-based + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbps [optional] bits per sample -- 1, 2, 4 or 8 + * \param[out] pspp [optional] samples per pixel -- 1 or 3 + * \param[out] pres [optional] resolution in x dir; NULL to ignore + * \param[out] pcmap [optional] colormap exists; input NULL to ignore + * \param[out] pformat [optional] tiff format; input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If there is a colormap, cmap is returned as 1; else 0.
+ *      (2) If %n is equal to or greater than the number of images, returns 1.
+ * 
+ */ +l_ok +freadHeaderTiff(FILE *fp, + l_int32 n, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pres, + l_int32 *pcmap, + l_int32 *pformat) +{ +l_int32 i, ret, format; +TIFF *tif; + + PROCNAME("freadHeaderTiff"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (pres) *pres = 0; + if (pcmap) *pcmap = 0; + if (pformat) *pformat = 0; + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (n < 0) + return ERROR_INT("image index must be >= 0", procName, 1); + if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat) + return ERROR_INT("no results requested", procName, 1); + + findFileFormatStream(fp, &format); + if (format != IFF_TIFF && + format != IFF_TIFF_G3 && format != IFF_TIFF_G4 && + format != IFF_TIFF_RLE && format != IFF_TIFF_PACKBITS && + format != IFF_TIFF_LZW && format != IFF_TIFF_ZIP && + format != IFF_TIFF_JPEG) + return ERROR_INT("file not tiff format", procName, 1); + + if ((tif = fopenTiff(fp, "r")) == NULL) + return ERROR_INT("tif not open for read", procName, 1); + + for (i = 0; i < n; i++) { + if (TIFFReadDirectory(tif) == 0) + return ERROR_INT("image n not found in file", procName, 1); + } + + ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat); + TIFFCleanup(tif); + return ret; +} + + +/*! + * \brief readHeaderMemTiff() + * + * \param[in] cdata const; tiff-encoded + * \param[in] size size of data + * \param[in] n page image number: 0-based + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbps [optional] bits per sample -- 1, 2, 4 or 8 + * \param[out] pspp [optional] samples per pixel -- 1 or 3 + * \param[out] pres [optional] resolution in x dir; NULL to ignore + * \param[out] pcmap [optional] colormap exists; input NULL to ignore + * \param[out] pformat [optional] tiff format; input NULL to ignore + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Use TIFFClose(); TIFFCleanup() doesn't free internal memstream.
+ * 
+ */ +l_ok +readHeaderMemTiff(const l_uint8 *cdata, + size_t size, + l_int32 n, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pres, + l_int32 *pcmap, + l_int32 *pformat) +{ +l_uint8 *data; +l_int32 i, ret; +TIFF *tif; + + PROCNAME("readHeaderMemTiff"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (pres) *pres = 0; + if (pcmap) *pcmap = 0; + if (pformat) *pformat = 0; + if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat) + return ERROR_INT("no results requested", procName, 1); + if (!cdata) + return ERROR_INT("cdata not defined", procName, 1); + + /* Open a tiff stream to memory */ + data = (l_uint8 *)cdata; /* we're really not going to change this */ + if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL) + return ERROR_INT("tiff stream not opened", procName, 1); + + for (i = 0; i < n; i++) { + if (TIFFReadDirectory(tif) == 0) { + TIFFClose(tif); + return ERROR_INT("image n not found in file", procName, 1); + } + } + + ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat); + TIFFClose(tif); + return ret; +} + + +/*! + * \brief tiffReadHeaderTiff() + * + * \param[in] tif + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \param[out] pbps [optional] bits per sample -- 1, 2, 4 or 8 + * \param[out] pspp [optional] samples per pixel -- 1 or 3 + * \param[out] pres [optional] resolution in x dir; NULL to ignore + * \param[out] pcmap [optional] cmap exists; input NULL to ignore + * \param[out] pformat [optional] tiff format; input NULL to ignore + * \return 0 if OK, 1 on error + */ +static l_int32 +tiffReadHeaderTiff(TIFF *tif, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *pres, + l_int32 *pcmap, + l_int32 *pformat) +{ +l_uint16 tiffcomp; +l_uint16 bps, spp; +l_uint16 *rmap, *gmap, *bmap; +l_int32 xres, yres; +l_uint32 w, h; + + PROCNAME("tiffReadHeaderTiff"); + + if (!tif) + return ERROR_INT("tif not opened", procName, 1); + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + if (w < 1 || h < 1) + return ERROR_INT("tif w and h not both > 0", procName, 1); + if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps > 16) + return ERROR_INT("bps not in set {1,2,4,8,16}", procName, 1); + if (spp != 1 && spp != 2 && spp != 3 && spp != 4) + return ERROR_INT("spp not in set {1,2,3,4}", procName, 1); + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (pres) { + *pres = 300; /* default ppi */ + if (getTiffStreamResolution(tif, &xres, &yres) == 0) + *pres = (l_int32)xres; + } + if (pcmap) { + *pcmap = 0; + if (TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) + *pcmap = 1; + } + if (pformat) { + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp); + *pformat = getTiffCompressedFormat(tiffcomp); + } + return 0; +} + + +/*! + * \brief findTiffCompression() + * + * \param[in] fp file stream; must be rewound to BOF + * \param[out] pcomptype compression type + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The returned compression type is that defined in
+ *          the enum in imageio.h.  It is not the tiff flag value.
+ *      (2) The compression type is initialized to IFF_UNKNOWN.
+ *          If it is not one of the specified types, the returned
+ *          type is IFF_TIFF, which indicates no compression.
+ *      (3) When this function is called, the stream must be at BOF.
+ *          If the opened stream is to be used again to read the
+ *          file, it must be rewound to BOF after calling this function.
+ * 
+ */ +l_ok +findTiffCompression(FILE *fp, + l_int32 *pcomptype) +{ +l_uint16 tiffcomp; +TIFF *tif; + + PROCNAME("findTiffCompression"); + + if (!pcomptype) + return ERROR_INT("&comptype not defined", procName, 1); + *pcomptype = IFF_UNKNOWN; /* init */ + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + + if ((tif = fopenTiff(fp, "r")) == NULL) + return ERROR_INT("tif not opened", procName, 1); + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp); + *pcomptype = getTiffCompressedFormat(tiffcomp); + TIFFCleanup(tif); + return 0; +} + + +/*! + * \brief getTiffCompressedFormat() + * + * \param[in] tiffcomp defined in tiff.h + * \return compression format defined in imageio.h + * + *
+ * Notes:
+ *      (1) The input must be the actual tiff compression type
+ *          returned by a tiff library call.  It should always be
+ *          a valid tiff type.
+ *      (2) The return type is defined in the enum in imageio.h.
+ * 
+ */ +static l_int32 +getTiffCompressedFormat(l_uint16 tiffcomp) +{ +l_int32 comptype; + + switch (tiffcomp) + { + case COMPRESSION_CCITTFAX4: + comptype = IFF_TIFF_G4; + break; + case COMPRESSION_CCITTFAX3: + comptype = IFF_TIFF_G3; + break; + case COMPRESSION_CCITTRLE: + comptype = IFF_TIFF_RLE; + break; + case COMPRESSION_PACKBITS: + comptype = IFF_TIFF_PACKBITS; + break; + case COMPRESSION_LZW: + comptype = IFF_TIFF_LZW; + break; + case COMPRESSION_ADOBE_DEFLATE: + comptype = IFF_TIFF_ZIP; + break; + case COMPRESSION_JPEG: + comptype = IFF_TIFF_JPEG; + break; + default: + comptype = IFF_TIFF; + break; + } + return comptype; +} + + +/*--------------------------------------------------------------* + * Extraction of tiff g4 data * + *--------------------------------------------------------------*/ +/*! + * \brief extractG4DataFromFile() + * + * \param[in] filein + * \param[out] pdata binary data of ccitt g4 encoded stream + * \param[out] pnbytes size of binary data + * \param[out] pw [optional] image width + * \param[out] ph [optional] image height + * \param[out] pminisblack [optional] boolean + * \return 0 if OK, 1 on error + */ +l_ok +extractG4DataFromFile(const char *filein, + l_uint8 **pdata, + size_t *pnbytes, + l_int32 *pw, + l_int32 *ph, + l_int32 *pminisblack) +{ +l_uint8 *inarray, *data; +l_uint16 minisblack, comptype; /* accessors require l_uint16 */ +l_int32 istiff; +l_uint32 w, h, rowsperstrip; /* accessors require l_uint32 */ +l_uint32 diroff; +size_t fbytes, nbytes; +FILE *fpin; +TIFF *tif; + + PROCNAME("extractG4DataFromFile"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!pnbytes) + return ERROR_INT("&nbytes not defined", procName, 1); + if (!pw && !ph && !pminisblack) + return ERROR_INT("no output data requested", procName, 1); + *pdata = NULL; + *pnbytes = 0; + + if ((fpin = fopenReadStream(filein)) == NULL) + return ERROR_INT("stream not opened to file", procName, 1); + istiff = fileFormatIsTiff(fpin); + fclose(fpin); + if (!istiff) + return ERROR_INT("filein not tiff", procName, 1); + + if ((inarray = l_binaryRead(filein, &fbytes)) == NULL) + return ERROR_INT("inarray not made", procName, 1); + + /* Get metadata about the image */ + if ((tif = openTiff(filein, "rb")) == NULL) { + LEPT_FREE(inarray); + return ERROR_INT("tif not open for read", procName, 1); + } + TIFFGetField(tif, TIFFTAG_COMPRESSION, &comptype); + if (comptype != COMPRESSION_CCITTFAX4) { + LEPT_FREE(inarray); + TIFFClose(tif); + return ERROR_INT("filein is not g4 compressed", procName, 1); + } + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); + TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + if (h != rowsperstrip) + L_WARNING("more than 1 strip\n", procName); + TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &minisblack); /* for 1 bpp */ +/* TIFFPrintDirectory(tif, stderr, 0); */ + TIFFClose(tif); + if (pw) *pw = (l_int32)w; + if (ph) *ph = (l_int32)h; + if (pminisblack) *pminisblack = (l_int32)minisblack; + + /* The header has 8 bytes: the first 2 are the magic number, + * the next 2 are the version, and the last 4 are the + * offset to the first directory. That's what we want here. + * We have to test the byte order before decoding 4 bytes! */ + if (inarray[0] == 0x4d) { /* big-endian */ + diroff = (inarray[4] << 24) | (inarray[5] << 16) | + (inarray[6] << 8) | inarray[7]; + } else { /* inarray[0] == 0x49 : little-endian */ + diroff = (inarray[7] << 24) | (inarray[6] << 16) | + (inarray[5] << 8) | inarray[4]; + } +/* fprintf(stderr, " diroff = %d, %x\n", diroff, diroff); */ + + /* Extract the ccittg4 encoded data from the tiff file. + * We skip the 8 byte header and take nbytes of data, + * up to the beginning of the directory (at diroff) */ + nbytes = diroff - 8; + *pnbytes = nbytes; + if ((data = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) { + LEPT_FREE(inarray); + return ERROR_INT("data not allocated", procName, 1); + } + *pdata = data; + memcpy(data, inarray + 8, nbytes); + LEPT_FREE(inarray); + + return 0; +} + + +/*--------------------------------------------------------------* + * Open tiff stream from file stream * + *--------------------------------------------------------------*/ +/*! + * \brief fopenTiff() + * + * \param[in] fp file stream + * \param[in] modestring "r", "w", ... + * \return tiff data structure, opened for a file descriptor + * + *
+ * Notes:
+ *      (1) Why is this here?  Leffler did not provide a function that
+ *          takes a stream and gives a TIFF.  He only gave one that
+ *          generates a TIFF starting with a file descriptor.  So we
+ *          need to make it here, because it is useful to have functions
+ *          that take a stream as input.
+ *      (2) We use TIFFClientOpen() together with a set of static wrapper
+ *          functions which map TIFF read, write, seek, close and size.
+ *          to functions expecting a cookie of type stream (i.e. FILE *).
+ *          This implementation was contributed by J眉rgen Buchm眉ller.
+ * 
+ */ +static TIFF * +fopenTiff(FILE *fp, + const char *modestring) +{ + PROCNAME("fopenTiff"); + + if (!fp) + return (TIFF *)ERROR_PTR("stream not opened", procName, NULL); + if (!modestring) + return (TIFF *)ERROR_PTR("modestring not defined", procName, NULL); + + TIFFSetWarningHandler(NULL); /* disable warnings */ + TIFFSetErrorHandler(NULL); /* disable error messages */ + + fseek(fp, 0, SEEK_SET); + return TIFFClientOpen("TIFFstream", modestring, (thandle_t)fp, + lept_read_proc, lept_write_proc, lept_seek_proc, + lept_close_proc, lept_size_proc, NULL, NULL); +} + + +/*--------------------------------------------------------------* + * Wrapper for TIFFOpen * + *--------------------------------------------------------------*/ +/*! + * \brief openTiff() + * + * \param[in] filename + * \param[in] modestring "r", "w", ... + * \return tiff data structure + * + *
+ * Notes:
+ *      (1) This handles multi-platform file naming.
+ * 
+ */ +static TIFF * +openTiff(const char *filename, + const char *modestring) +{ +char *fname; +TIFF *tif; + + PROCNAME("openTiff"); + + if (!filename) + return (TIFF *)ERROR_PTR("filename not defined", procName, NULL); + if (!modestring) + return (TIFF *)ERROR_PTR("modestring not defined", procName, NULL); + + TIFFSetWarningHandler(NULL); /* disable warnings */ + TIFFSetErrorHandler(NULL); /* disable error messages */ + + fname = genPathname(filename, NULL); + tif = TIFFOpen(fname, modestring); + LEPT_FREE(fname); + return tif; +} + + +/*----------------------------------------------------------------------* + * Memory I/O: reading memory --> pix and writing pix --> memory * + *----------------------------------------------------------------------*/ +/* It would be nice to use open_memstream() and fmemopen() + * for writing and reading to memory, rsp. These functions manage + * memory for writes and reads that use a file streams interface. + * Unfortunately, the tiff library only has an interface for reading + * and writing to file descriptors, not to file streams. The tiff + * library procedure is to open a "tiff stream" and read/write to it. + * The library provides a client interface for managing the I/O + * from memory, which requires seven callbacks. See the TIFFClientOpen + * man page for callback signatures. Adam Langley provided the code + * to do this. */ + +/*! + * \brief Memory stream buffer used with TIFFClientOpen() + * + * The L_Memstram %buffer has different functions in writing and reading. + * + * * In reading, it is assigned to the data and read from as + * the tiff library uncompresses the data and generates the pix. + * The %offset points to the current read position in the data, + * and the %hw always gives the number of bytes of data. + * The %outdata and %outsize ptrs are not used. + * When finished, tiffCloseCallback() simply frees the L_Memstream. + * + * * In writing, it accepts the data that the tiff library + * produces when a pix is compressed. the buffer points to a + * malloced area of %bufsize bytes. The current writing position + * in the buffer is %offset and the most ever written is %hw. + * The buffer is expanded as necessary. When finished, + * tiffCloseCallback() assigns the %outdata and %outsize ptrs + * to the %buffer and %bufsize results, and frees the L_Memstream. + */ +struct L_Memstream +{ + l_uint8 *buffer; /* expands to hold data when written to; */ + /* fixed size when read from. */ + size_t bufsize; /* current size allocated when written to; */ + /* fixed size of input data when read from. */ + size_t offset; /* byte offset from beginning of buffer. */ + size_t hw; /* high-water mark; max bytes in buffer. */ + l_uint8 **poutdata; /* input param for writing; data goes here. */ + size_t *poutsize; /* input param for writing; data size goes here. */ +}; +typedef struct L_Memstream L_MEMSTREAM; + + + /* These are static functions for memory I/O */ +static L_MEMSTREAM *memstreamCreateForRead(l_uint8 *indata, size_t pinsize); +static L_MEMSTREAM *memstreamCreateForWrite(l_uint8 **poutdata, + size_t *poutsize); +static tsize_t tiffReadCallback(thandle_t handle, tdata_t data, tsize_t length); +static tsize_t tiffWriteCallback(thandle_t handle, tdata_t data, + tsize_t length); +static toff_t tiffSeekCallback(thandle_t handle, toff_t offset, l_int32 whence); +static l_int32 tiffCloseCallback(thandle_t handle); +static toff_t tiffSizeCallback(thandle_t handle); +static l_int32 tiffMapCallback(thandle_t handle, tdata_t *data, toff_t *length); +static void tiffUnmapCallback(thandle_t handle, tdata_t data, toff_t length); + + +static L_MEMSTREAM * +memstreamCreateForRead(l_uint8 *indata, + size_t insize) +{ +L_MEMSTREAM *mstream; + + mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM)); + mstream->buffer = indata; /* handle to input data array */ + mstream->bufsize = insize; /* amount of input data */ + mstream->hw = insize; /* high-water mark fixed at input data size */ + mstream->offset = 0; /* offset always starts at 0 */ + return mstream; +} + + +static L_MEMSTREAM * +memstreamCreateForWrite(l_uint8 **poutdata, + size_t *poutsize) +{ +L_MEMSTREAM *mstream; + + mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM)); + mstream->buffer = (l_uint8 *)LEPT_CALLOC(8 * 1024, 1); + mstream->bufsize = 8 * 1024; + mstream->poutdata = poutdata; /* used only at end of write */ + mstream->poutsize = poutsize; /* ditto */ + mstream->hw = mstream->offset = 0; + return mstream; +} + + +static tsize_t +tiffReadCallback(thandle_t handle, + tdata_t data, + tsize_t length) +{ +L_MEMSTREAM *mstream; +size_t amount; + + mstream = (L_MEMSTREAM *)handle; + amount = L_MIN((size_t)length, mstream->hw - mstream->offset); + + /* Fuzzed files can create this condition! */ + if (mstream->offset + amount < amount || /* overflow */ + mstream->offset + amount > mstream->hw) { + fprintf(stderr, "Bad file: amount too big: %zu\n", amount); + return 0; + } + + memcpy(data, mstream->buffer + mstream->offset, amount); + mstream->offset += amount; + return amount; +} + + +static tsize_t +tiffWriteCallback(thandle_t handle, + tdata_t data, + tsize_t length) +{ +L_MEMSTREAM *mstream; +size_t newsize; + + /* reallocNew() uses calloc to initialize the array. + * If malloc is used instead, for some of the encoding methods, + * not all the data in 'bufsize' bytes in the buffer will + * have been initialized by the end of the compression. */ + mstream = (L_MEMSTREAM *)handle; + if (mstream->offset + length > mstream->bufsize) { + newsize = 2 * (mstream->offset + length); + mstream->buffer = (l_uint8 *)reallocNew((void **)&mstream->buffer, + mstream->hw, newsize); + mstream->bufsize = newsize; + } + + memcpy(mstream->buffer + mstream->offset, data, length); + mstream->offset += length; + mstream->hw = L_MAX(mstream->offset, mstream->hw); + return length; +} + + +static toff_t +tiffSeekCallback(thandle_t handle, + toff_t offset, + l_int32 whence) +{ +L_MEMSTREAM *mstream; + + PROCNAME("tiffSeekCallback"); + mstream = (L_MEMSTREAM *)handle; + switch (whence) { + case SEEK_SET: +/* fprintf(stderr, "seek_set: offset = %d\n", offset); */ + if((size_t)offset != offset) { /* size_t overflow on uint32 */ + return (toff_t)ERROR_INT("too large offset value", procName, 1); + } + mstream->offset = offset; + break; + case SEEK_CUR: +/* fprintf(stderr, "seek_cur: offset = %d\n", offset); */ + mstream->offset += offset; + break; + case SEEK_END: +/* fprintf(stderr, "seek end: hw = %d, offset = %d\n", + mstream->hw, offset); */ + mstream->offset = mstream->hw - offset; /* offset >= 0 */ + break; + default: + return (toff_t)ERROR_INT("bad whence value", procName, + mstream->offset); + } + + return mstream->offset; +} + + +static l_int32 +tiffCloseCallback(thandle_t handle) +{ +L_MEMSTREAM *mstream; + + mstream = (L_MEMSTREAM *)handle; + if (mstream->poutdata) { /* writing: save the output data */ + *mstream->poutdata = mstream->buffer; + *mstream->poutsize = mstream->hw; + } + LEPT_FREE(mstream); /* never free the buffer! */ + return 0; +} + + +static toff_t +tiffSizeCallback(thandle_t handle) +{ +L_MEMSTREAM *mstream; + + mstream = (L_MEMSTREAM *)handle; + return mstream->hw; +} + + +static l_int32 +tiffMapCallback(thandle_t handle, + tdata_t *data, + toff_t *length) +{ +L_MEMSTREAM *mstream; + + mstream = (L_MEMSTREAM *)handle; + *data = mstream->buffer; + *length = mstream->hw; + return 0; +} + + +static void +tiffUnmapCallback(thandle_t handle, + tdata_t data, + toff_t length) +{ + return; +} + + +/*! + * \brief fopenTiffMemstream() + * + * \param[in] filename for error output; can be "" + * \param[in] operation "w" for write, "r" for read + * \param[out] pdata written data + * \param[out] pdatasize size of written data + * \return tiff data structure, opened for write to memory + * + *
+ * Notes:
+ *      (1) This wraps up a number of callbacks for either:
+ *            * reading from tiff in memory buffer --> pix
+ *            * writing from pix --> tiff in memory buffer
+ *      (2) After use, the memstream is automatically destroyed when
+ *          TIFFClose() is called.  TIFFCleanup() doesn't free the memstream.
+ *      (3) This does not work in append mode, and in write mode it
+ *          does not append.
+ * 
+ */ +static TIFF * +fopenTiffMemstream(const char *filename, + const char *operation, + l_uint8 **pdata, + size_t *pdatasize) +{ +L_MEMSTREAM *mstream; +TIFF *tif; + + PROCNAME("fopenTiffMemstream"); + + if (!filename) + return (TIFF *)ERROR_PTR("filename not defined", procName, NULL); + if (!operation) + return (TIFF *)ERROR_PTR("operation not defined", procName, NULL); + if (!pdata) + return (TIFF *)ERROR_PTR("&data not defined", procName, NULL); + if (!pdatasize) + return (TIFF *)ERROR_PTR("&datasize not defined", procName, NULL); + if (strcmp(operation, "r") && strcmp(operation, "w")) + return (TIFF *)ERROR_PTR("op not 'r' or 'w'", procName, NULL); + + if (!strcmp(operation, "r")) + mstream = memstreamCreateForRead(*pdata, *pdatasize); + else + mstream = memstreamCreateForWrite(pdata, pdatasize); + + TIFFSetWarningHandler(NULL); /* disable warnings */ + TIFFSetErrorHandler(NULL); /* disable error messages */ + + tif = TIFFClientOpen(filename, operation, (thandle_t)mstream, + tiffReadCallback, tiffWriteCallback, + tiffSeekCallback, tiffCloseCallback, + tiffSizeCallback, tiffMapCallback, + tiffUnmapCallback); + if (!tif) + LEPT_FREE(mstream); + return tif; +} + + +/*! + * \brief pixReadMemTiff() + * + * \param[in] cdata const; tiff-encoded + * \param[in] size size of cdata + * \param[in] n page image number: 0-based + * \return pix, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a version of pixReadTiff(), where the data is read
+ *          from a memory buffer and uncompressed.
+ *      (2) Use TIFFClose(); TIFFCleanup() doesn't free internal memstream.
+ *      (3) No warning messages on failure, because of how multi-page
+ *          TIFF reading works. You are supposed to keep trying until
+ *          it stops working.
+ *      (4) Tiff directory overhead is linear in the input page number.
+ *          If reading many images, use pixReadMemFromMultipageTiff().
+ * 
+ */ +PIX * +pixReadMemTiff(const l_uint8 *cdata, + size_t size, + l_int32 n) +{ +l_uint8 *data; +l_int32 i; +PIX *pix; +TIFF *tif; + + PROCNAME("pixReadMemTiff"); + + if (!cdata) + return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); + + data = (l_uint8 *)cdata; /* we're really not going to change this */ + if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL) + return (PIX *)ERROR_PTR("tiff stream not opened", procName, NULL); + + pix = NULL; + for (i = 0; ; i++) { + if (i == n) { + if ((pix = pixReadFromTiffStream(tif)) == NULL) { + TIFFClose(tif); + return NULL; + } + pixSetInputFormat(pix, IFF_TIFF); + break; + } + if (TIFFReadDirectory(tif) == 0) + break; + if (i == ManyPagesInTiffFile + 1) { + L_WARNING("big file: more than %d pages\n", procName, + ManyPagesInTiffFile); + } + } + + TIFFClose(tif); + return pix; +} + + +/*! + * \brief pixReadMemFromMultipageTiff() + * + * \param[in] cdata const; tiff-encoded + * \param[in] size size of cdata + * \param[in,out] poffset set offset to 0 for first image + * \return pix, or NULL on error or if previous call returned the last image + * + *
+ * Notes:
+ *      (1) This is a read-from-memory version of pixReadFromMultipageTiff().
+ *          See that function for usage.
+ *      (2) If reading sequentially from the tiff data, this is more
+ *          efficient than pixReadMemTiff(), which has an overhead
+ *          proportional to the image index n.
+ *      (3) Example usage for reading all the images:
+ *            size_t offset = 0;
+ *            do {
+ *                Pix *pix = pixReadMemFromMultipageTiff(data, size, &offset);
+ *                // do something with pix
+ *            } while (offset != 0);
+ * 
+ */ +PIX * +pixReadMemFromMultipageTiff(const l_uint8 *cdata, + size_t size, + size_t *poffset) +{ +l_uint8 *data; +l_int32 retval; +size_t offset; +PIX *pix; +TIFF *tif; + + PROCNAME("pixReadMemFromMultipageTiff"); + + if (!cdata) + return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); + if (!poffset) + return (PIX *)ERROR_PTR("&offset not defined", procName, NULL); + + data = (l_uint8 *)cdata; /* we're really not going to change this */ + if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL) + return (PIX *)ERROR_PTR("tiff stream not opened", procName, NULL); + + /* Set ptrs in the TIFF to the beginning of the image */ + offset = *poffset; + retval = (offset == 0) ? TIFFSetDirectory(tif, 0) + : TIFFSetSubDirectory(tif, offset); + if (retval == 0) { + TIFFClose(tif); + return NULL; + } + + if ((pix = pixReadFromTiffStream(tif)) == NULL) { + TIFFClose(tif); + return NULL; + } + + /* Advance to the next image and return the new offset */ + TIFFReadDirectory(tif); + *poffset = TIFFCurrentDirOffset(tif); + TIFFClose(tif); + return pix; +} + + +/*! + * \brief pixaReadMemMultipageTiff() + * + * \param[in] data const; multiple pages; tiff-encoded + * \param[in] size size of cdata + * \return pixa, or NULL on error + * + *
+ * Notes:
+ *      (1) This is an O(n) read-from-memory version of pixaReadMultipageTiff().
+ * 
+ */ +PIXA * +pixaReadMemMultipageTiff(const l_uint8 *data, + size_t size) +{ +size_t offset; +PIX *pix; +PIXA *pixa; + + PROCNAME("pixaReadMemMultipageTiff"); + + if (!data) + return (PIXA *)ERROR_PTR("data not defined", procName, NULL); + + offset = 0; + pixa = pixaCreate(0); + do { + pix = pixReadMemFromMultipageTiff(data, size, &offset); + pixaAddPix(pixa, pix, L_INSERT); + } while (offset != 0); + return pixa; +} + + +/*! + * \brief pixaWriteMemMultipageTiff() + * + * \param[out] pdata const; tiff-encoded + * \param[out] psize size of data + * \param[in] pixa any depth; colormap will be removed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) fopenTiffMemstream() does not work in append mode, so we
+ *          must work-around with a temporary file.
+ *      (2) Getting a file stream from
+ *            open_memstream((char **)pdata, psize)
+ *          does not work with the tiff directory.
+ * 
+ */ +l_ok +pixaWriteMemMultipageTiff(l_uint8 **pdata, + size_t *psize, + PIXA *pixa) +{ +const char *modestr; +l_int32 i, n; +FILE *fp; +PIX *pix1; + + PROCNAME("pixaWriteMemMultipageTiff"); + + if (pdata) *pdata = NULL; + if (!pdata) + return ERROR_INT("pdata not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + +#ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#else + if ((fp = tmpfile()) == NULL) + return ERROR_INT("tmpfile stream not opened", procName, 1); +#endif /* _WIN32 */ + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + modestr = (i == 0) ? "w" : "a"; + pix1 = pixaGetPix(pixa, i, L_CLONE); + if (pixGetDepth(pix1) == 1) + pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_G4, modestr); + else + pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_ZIP, modestr); + pixDestroy(&pix1); + } + + rewind(fp); + *pdata = l_binaryReadStream(fp, psize); + fclose(fp); + return 0; +} + + +/*! + * \brief pixWriteMemTiff() + * + * \param[out] pdata data of tiff compressed image + * \param[out] psize size of returned data + * \param[in] pix + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \return 0 if OK, 1 on error + * + * Usage: + * 1) See pixWriteTiff(. This version writes to + * memory instead of to a file. + */ +l_ok +pixWriteMemTiff(l_uint8 **pdata, + size_t *psize, + PIX *pix, + l_int32 comptype) +{ + return pixWriteMemTiffCustom(pdata, psize, pix, comptype, + NULL, NULL, NULL, NULL); +} + + +/*! + * \brief pixWriteMemTiffCustom() + * + * \param[out] pdata data of tiff compressed image + * \param[out] psize size of returned data + * \param[in] pix + * \param[in] comptype IFF_TIFF, IFF_TIFF_RLE, IFF_TIFF_PACKBITS, + * IFF_TIFF_G3, IFF_TIFF_G4, + * IFF_TIFF_LZW, IFF_TIFF_ZIP, IFF_TIFF_JPEG + * \param[in] natags [optional] NUMA of custom tiff tags + * \param[in] savals [optional] SARRAY of values + * \param[in] satypes [optional] SARRAY of types + * \param[in] nasizes [optional] NUMA of sizes + * \return 0 if OK, 1 on error + * + * Usage: + * 1) See pixWriteTiffCustom(. This version writes to + * memory instead of to a file. + * 2) Use TIFFClose(); TIFFCleanup( doesn't free internal memstream. + */ +l_ok +pixWriteMemTiffCustom(l_uint8 **pdata, + size_t *psize, + PIX *pix, + l_int32 comptype, + NUMA *natags, + SARRAY *savals, + SARRAY *satypes, + NUMA *nasizes) +{ +l_int32 ret; +TIFF *tif; + + PROCNAME("pixWriteMemTiffCustom"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1); + if (!psize) + return ERROR_INT("&size not defined", procName, 1); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1); + if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF && + comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP && + comptype != IFF_TIFF_JPEG) { + L_WARNING("invalid compression type for bpp > 1\n", procName); + comptype = IFF_TIFF_ZIP; + } + + if ((tif = fopenTiffMemstream("tifferror", "w", pdata, psize)) == NULL) + return ERROR_INT("tiff stream not opened", procName, 1); + ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals, + satypes, nasizes); + + TIFFClose(tif); + return ret; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBTIFF */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/tiffiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/tiffiostub.c new file mode 100644 index 0000000..647df65 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/tiffiostub.c @@ -0,0 +1,242 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file tiffiostub.c + *
+ *
+ *     Stubs for tiffio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBTIFF /* defined in environ.h */ +/* --------------------------------------------*/ + +PIX * pixReadTiff(const char *filename, l_int32 n) +{ + return (PIX *)ERROR_PTR("function not present", "pixReadTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadStreamTiff(FILE *fp, l_int32 n) +{ + return (PIX *)ERROR_PTR("function not present", "pixReadStreamTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteTiff(const char *filename, PIX *pix, l_int32 comptype, + const char *modestring) +{ + return ERROR_INT("function not present", "pixWriteTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteTiffCustom(const char *filename, PIX *pix, l_int32 comptype, + const char *modestring, NUMA *natags, + SARRAY *savals, SARRAY *satypes, NUMA *nasizes) +{ + return ERROR_INT("function not present", "pixWriteTiffCustom", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamTiff(FILE *fp, PIX *pix, l_int32 comptype) +{ + return ERROR_INT("function not present", "pixWriteStreamTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamTiffWA(FILE *fp, PIX *pix, l_int32 comptype, + const char *modestr) +{ + return ERROR_INT("function not present", "pixWriteStreamTiffWA", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadFromMultipageTiff(const char *filename, size_t *poffset) +{ + return (PIX *)ERROR_PTR("function not present", + "pixReadFromMultipageTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIXA * pixaReadMultipageTiff(const char *filename) +{ + return (PIXA *)ERROR_PTR("function not present", + "pixaReadMultipageTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaWriteMultipageTiff(const char *filename, PIXA *pixa) +{ + return ERROR_INT("function not present", "pixaWriteMultipageTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok writeMultipageTiff(const char *dirin, const char *substr, + const char *fileout) +{ + return ERROR_INT("function not present", "writeMultipageTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok writeMultipageTiffSA(SARRAY *sa, const char *fileout) +{ + return ERROR_INT("function not present", "writeMultipageTiffSA", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok fprintTiffInfo(FILE *fpout, const char *tiffile) +{ + return ERROR_INT("function not present", "fprintTiffInfo", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok tiffGetCount(FILE *fp, l_int32 *pn) +{ + return ERROR_INT("function not present", "tiffGetCount", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok getTiffResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres) +{ + return ERROR_INT("function not present", "getTiffResolution", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderTiff(const char *filename, l_int32 n, l_int32 *pwidth, + l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, + l_int32 *pres, l_int32 *pcmap, l_int32 *pformat) +{ + return ERROR_INT("function not present", "readHeaderTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok freadHeaderTiff(FILE *fp, l_int32 n, l_int32 *pwidth, + l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, + l_int32 *pres, l_int32 *pcmap, l_int32 *pformat) +{ + return ERROR_INT("function not present", "freadHeaderTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemTiff(const l_uint8 *cdata, size_t size, l_int32 n, + l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, + l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, + l_int32 *pformat) +{ + return ERROR_INT("function not present", "readHeaderMemTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok findTiffCompression(FILE *fp, l_int32 *pcomptype) +{ + return ERROR_INT("function not present", "findTiffCompression", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok extractG4DataFromFile(const char *filein, l_uint8 **pdata, + size_t *pnbytes, l_int32 *pw, + l_int32 *ph, l_int32 *pminisblack) +{ + return ERROR_INT("function not present", "extractG4DataFromFile", 1); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemTiff(const l_uint8 *cdata, size_t size, l_int32 n) +{ + return (PIX *)ERROR_PTR("function not present", "pixReadMemTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemFromMultipageTiff(const l_uint8 *cdata, size_t size, + size_t *poffset) +{ + return (PIX *)ERROR_PTR("function not present", + "pixReadMemFromMultipageTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIXA * pixaReadMemMultipageTiff(const l_uint8 *data, size_t size) +{ + return (PIXA *)ERROR_PTR("function not present", + "pixaReadMemMultipageTiff", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaWriteMemMultipageTiff(l_uint8 **pdata, size_t *psize, PIXA *pixa) +{ + return ERROR_INT("function not present", "pixaWriteMemMultipageTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemTiff(l_uint8 **pdata, size_t *psize, PIX *pix, + l_int32 comptype) +{ + return ERROR_INT("function not present", "pixWriteMemTiff", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemTiffCustom(l_uint8 **pdata, size_t *psize, PIX *pix, + l_int32 comptype, NUMA *natags, SARRAY *savals, + SARRAY *satypes, NUMA *nasizes) +{ + return ERROR_INT("function not present", "pixWriteMemTiffCustom", 1); +} + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBTIFF */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/utils1.c b/hgdriver/3rdparty/hgOCR/leptonica/utils1.c new file mode 100644 index 0000000..86359cc --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/utils1.c @@ -0,0 +1,1266 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file utils1.c + *
+ *
+ *       ------------------------------------------
+ *       This file has these utilities:
+ *         - error, warning and info messages
+ *         - low-level endian conversions
+ *         - file corruption operations
+ *         - random and prime number operations
+ *         - 64-bit hash functions
+ *         - leptonica version number accessor
+ *         - timing and date operations
+ *       ------------------------------------------
+ *
+ *       Control of error, warning and info messages
+ *           l_int32    setMsgSeverity()
+ *
+ *       Error return functions, invoked by macros
+ *           l_int32    returnErrorInt()
+ *           l_float32  returnErrorFloat()
+ *           void      *returnErrorPtr()
+ *
+ *       Test files for equivalence
+ *           l_int32    filesAreIdentical()
+ *
+ *       Byte-swapping data conversion
+ *           l_uint16   convertOnBigEnd16()
+ *           l_uint32   convertOnBigEnd32()
+ *           l_uint16   convertOnLittleEnd16()
+ *           l_uint32   convertOnLittleEnd32()
+ *
+ *       File corruption and byte replacement operations
+ *           l_int32    fileCorruptByDeletion()
+ *           l_int32    fileCorruptByMutation()
+ *           l_int32    fileReplaceBytes()
+ *
+ *       Generate random integer in given range
+ *           l_int32    genRandomIntegerInRange()
+ *
+ *       Simple math function
+ *           l_int32    lept_roundftoi()
+ *
+ *       64-bit hash functions
+ *           l_int32    l_hashStringToUint64()
+ *           l_int32    l_hashPtToUint64()
+ *           l_int32    l_hashFloat64ToUint64()
+ *
+ *       Prime finders
+ *           l_int32    findNextLargerPrime()
+ *           l_int32    lept_isPrime()
+ *
+ *       Gray code conversion
+ *           l_uint32   convertIntToGrayCode()
+ *           l_uint32   convertGrayCodeToInt()
+ *
+ *       Leptonica version number
+ *           char      *getLeptonicaVersion()
+ *
+ *       Timing
+ *           void       startTimer()
+ *           l_float32  stopTimer()
+ *           L_TIMER    startTimerNested()
+ *           l_float32  stopTimerNested()
+ *           void       l_getCurrentTime()
+ *           L_WALLTIMER  *startWallTimer()
+ *           l_float32  stopWallTimer()
+ *           void       l_getFormattedDate()
+ *
+ *  For all issues with cross-platform development, see utils2.c.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + +#include +#include "allheaders.h" +#include + + /* Global for controlling message output at runtime */ +LEPT_DLL l_int32 LeptMsgSeverity = DEFAULT_SEVERITY; + +#define DEBUG_SEV 0 + +/*----------------------------------------------------------------------* + * Control of error, warning and info messages * + *----------------------------------------------------------------------*/ +/*! + * \brief setMsgSeverity() + * + * \param[in] newsev + * \return oldsev + * + *
+ * Notes:
+ *      (1) setMsgSeverity() allows the user to specify the desired
+ *          message severity threshold.  Messages of equal or greater
+ *          severity will be output.  The previous message severity is
+ *          returned when the new severity is set.
+ *      (2) If L_SEVERITY_EXTERNAL is passed, then the severity will be
+ *          obtained from the LEPT_MSG_SEVERITY environment variable.
+ * 
+ */ +l_int32 +setMsgSeverity(l_int32 newsev) +{ +l_int32 oldsev; +char *envsev; + + oldsev = LeptMsgSeverity; + if (newsev == L_SEVERITY_EXTERNAL) { + envsev = getenv("LEPT_MSG_SEVERITY"); + if (envsev) { + LeptMsgSeverity = atoi(envsev); +#if DEBUG_SEV + L_INFO("message severity set to external\n", "setMsgSeverity"); +#endif /* DEBUG_SEV */ + } else { +#if DEBUG_SEV + L_WARNING("environment var LEPT_MSG_SEVERITY not defined\n", + "setMsgSeverity"); +#endif /* DEBUG_SEV */ + } + } else { + LeptMsgSeverity = newsev; +#if DEBUG_SEV + L_INFO("message severity set to %d\n", "setMsgSeverity", newsev); +#endif /* DEBUG_SEV */ + } + + return oldsev; +} + + +/*----------------------------------------------------------------------* + * Error return functions, invoked by macros * + * * + * (1) These error functions print messages to stderr and allow * + * exit from the function that called them. * + * (2) They must be invoked only by the macros ERROR_INT, * + * ERROR_FLOAT and ERROR_PTR, which are in environ.h * + * (3) The print output can be disabled at compile time, either * + * by using -DNO_CONSOLE_IO or by setting LeptMsgSeverity. * + *----------------------------------------------------------------------*/ +/*! + * \brief returnErrorInt() + * + * \param[in] msg error message + * \param[in] procname + * \param[in] ival return error val + * \return ival typically 1 for an error return + */ +l_int32 +returnErrorInt(const char *msg, + const char *procname, + l_int32 ival) +{ + fprintf(stderr, "Error in %s: %s\n", procname, msg); + return ival; +} + + +/*! + * \brief returnErrorFloat() + * + * \param[in] msg error message + * \param[in] procname + * \param[in] fval return error val + * \return fval + */ +l_float32 +returnErrorFloat(const char *msg, + const char *procname, + l_float32 fval) +{ + fprintf(stderr, "Error in %s: %s\n", procname, msg); + return fval; +} + + +/*! + * \brief returnErrorPtr() + * + * \param[in] msg error message + * \param[in] procname + * \param[in] pval return error val + * \return pval typically null for an error return + */ +void * +returnErrorPtr(const char *msg, + const char *procname, + void *pval) +{ + fprintf(stderr, "Error in %s: %s\n", procname, msg); + return pval; +} + + +/*--------------------------------------------------------------------* + * Test files for equivalence * + *--------------------------------------------------------------------*/ +/*! + * \brief filesAreIdentical() + * + * \param[in] fname1 + * \param[in] fname2 + * \param[out] psame 1 if identical; 0 if different + * \return 0 if OK, 1 on error + */ +l_ok +filesAreIdentical(const char *fname1, + const char *fname2, + l_int32 *psame) +{ +l_int32 i, same; +size_t nbytes1, nbytes2; +l_uint8 *array1, *array2; + + PROCNAME("filesAreIdentical"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = 0; + if (!fname1 || !fname2) + return ERROR_INT("both names not defined", procName, 1); + + nbytes1 = nbytesInFile(fname1); + nbytes2 = nbytesInFile(fname2); + if (nbytes1 != nbytes2) + return 0; + + if ((array1 = l_binaryRead(fname1, &nbytes1)) == NULL) + return ERROR_INT("array1 not read", procName, 1); + if ((array2 = l_binaryRead(fname2, &nbytes2)) == NULL) { + LEPT_FREE(array1); + return ERROR_INT("array2 not read", procName, 1); + } + same = 1; + for (i = 0; i < nbytes1; i++) { + if (array1[i] != array2[i]) { + same = 0; + break; + } + } + LEPT_FREE(array1); + LEPT_FREE(array2); + *psame = same; + + return 0; +} + + +/*--------------------------------------------------------------------------* + * 16 and 32 bit byte-swapping on big endian and little endian machines * + * * + * These are typically used for I/O conversions: * + * (1) endian conversion for data that was read from a file * + * (2) endian conversion on data before it is written to a file * + *--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------* + * 16-bit byte swapping * + *--------------------------------------------------------------------*/ +#ifdef L_BIG_ENDIAN + +l_uint16 +convertOnBigEnd16(l_uint16 shortin) +{ + return ((shortin << 8) | (shortin >> 8)); +} + +l_uint16 +convertOnLittleEnd16(l_uint16 shortin) +{ + return shortin; +} + +#else /* L_LITTLE_ENDIAN */ + +l_uint16 +convertOnLittleEnd16(l_uint16 shortin) +{ + return ((shortin << 8) | (shortin >> 8)); +} + +l_uint16 +convertOnBigEnd16(l_uint16 shortin) +{ + return shortin; +} + +#endif /* L_BIG_ENDIAN */ + + +/*--------------------------------------------------------------------* + * 32-bit byte swapping * + *--------------------------------------------------------------------*/ +#ifdef L_BIG_ENDIAN + +l_uint32 +convertOnBigEnd32(l_uint32 wordin) +{ + return ((wordin << 24) | ((wordin << 8) & 0x00ff0000) | + ((wordin >> 8) & 0x0000ff00) | (wordin >> 24)); +} + +l_uint32 +convertOnLittleEnd32(l_uint32 wordin) +{ + return wordin; +} + +#else /* L_LITTLE_ENDIAN */ + +l_uint32 +convertOnLittleEnd32(l_uint32 wordin) +{ + return ((wordin << 24) | ((wordin << 8) & 0x00ff0000) | + ((wordin >> 8) & 0x0000ff00) | (wordin >> 24)); +} + +l_uint32 +convertOnBigEnd32(l_uint32 wordin) +{ + return wordin; +} + +#endif /* L_BIG_ENDIAN */ + + +/*---------------------------------------------------------------------* + * File corruption and byte replacement operations * + *---------------------------------------------------------------------*/ +/*! + * \brief fileCorruptByDeletion() + * + * \param[in] filein + * \param[in] loc fractional location of start of deletion + * \param[in] size fractional size of deletion + * \param[in] fileout corrupted file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) %loc and %size are expressed as a fraction of the file size.
+ *      (2) This makes a copy of the data in %filein, where bytes in the
+ *          specified region have deleted.
+ *      (3) If (%loc + %size) >= 1.0, this deletes from the position
+ *          represented by %loc to the end of the file.
+ *      (4) It is useful for testing robustness of I/O wrappers when the
+ *          data is corrupted, by simulating data corruption by deletion.
+ * 
+ */ +l_ok +fileCorruptByDeletion(const char *filein, + l_float32 loc, + l_float32 size, + const char *fileout) +{ +l_int32 i, locb, sizeb, rembytes; +size_t inbytes, outbytes; +l_uint8 *datain, *dataout; + + PROCNAME("fileCorruptByDeletion"); + + if (!filein || !fileout) + return ERROR_INT("filein and fileout not both specified", procName, 1); + if (loc < 0.0 || loc >= 1.0) + return ERROR_INT("loc must be in [0.0 ... 1.0)", procName, 1); + if (size <= 0.0) + return ERROR_INT("size must be > 0.0", procName, 1); + if (loc + size > 1.0) + size = 1.0 - loc; + + datain = l_binaryRead(filein, &inbytes); + locb = (l_int32)(loc * inbytes + 0.5); + locb = L_MIN(locb, inbytes - 1); + sizeb = (l_int32)(size * inbytes + 0.5); + sizeb = L_MAX(1, sizeb); + sizeb = L_MIN(sizeb, inbytes - locb); /* >= 1 */ + L_INFO("Removed %d bytes at location %d\n", procName, sizeb, locb); + rembytes = inbytes - locb - sizeb; /* >= 0; to be copied, after excision */ + + outbytes = inbytes - sizeb; + dataout = (l_uint8 *)LEPT_CALLOC(outbytes, 1); + for (i = 0; i < locb; i++) + dataout[i] = datain[i]; + for (i = 0; i < rembytes; i++) + dataout[locb + i] = datain[locb + sizeb + i]; + l_binaryWrite(fileout, "w", dataout, outbytes); + + LEPT_FREE(datain); + LEPT_FREE(dataout); + return 0; +} + + +/*! + * \brief fileCorruptByMutation() + * + * \param[in] filein + * \param[in] loc fractional location of start of randomization + * \param[in] size fractional size of randomization + * \param[in] fileout corrupted file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) %loc and %size are expressed as a fraction of the file size.
+ *      (2) This makes a copy of the data in %filein, where bytes in the
+ *          specified region have been replaced by random data.
+ *      (3) If (%loc + %size) >= 1.0, this modifies data from the position
+ *          represented by %loc to the end of the file.
+ *      (4) It is useful for testing robustness of I/O wrappers when the
+ *          data is corrupted, by simulating data corruption.
+ * 
+ */ +l_ok +fileCorruptByMutation(const char *filein, + l_float32 loc, + l_float32 size, + const char *fileout) +{ +l_int32 i, locb, sizeb; +size_t bytes; +l_uint8 *data; + + PROCNAME("fileCorruptByMutation"); + + if (!filein || !fileout) + return ERROR_INT("filein and fileout not both specified", procName, 1); + if (loc < 0.0 || loc >= 1.0) + return ERROR_INT("loc must be in [0.0 ... 1.0)", procName, 1); + if (size <= 0.0) + return ERROR_INT("size must be > 0.0", procName, 1); + if (loc + size > 1.0) + size = 1.0 - loc; + + data = l_binaryRead(filein, &bytes); + locb = (l_int32)(loc * bytes + 0.5); + locb = L_MIN(locb, bytes - 1); + sizeb = (l_int32)(size * bytes + 0.5); + sizeb = L_MAX(1, sizeb); + sizeb = L_MIN(sizeb, bytes - locb); /* >= 1 */ + L_INFO("Randomizing %d bytes at location %d\n", procName, sizeb, locb); + + /* Make an array of random bytes and do the substitution */ + for (i = 0; i < sizeb; i++) { + data[locb + i] = + (l_uint8)(255.9 * ((l_float64)rand() / (l_float64)RAND_MAX)); + } + + l_binaryWrite(fileout, "w", data, bytes); + LEPT_FREE(data); + return 0; +} + + +/*! + * \brief fileReplaceBytes() + * + * \param[in] filein input file + * \param[in] start start location for replacement + * \param[in] nbytes number of bytes to be removed + * \param[in] newdata replacement bytes + * \param[in] newsize size of replacement bytes + * \param[in] fileout output file + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) To remove %nbytes without replacement, set %newdata == NULL.
+ *      (2) One use is for replacing the date/time in a pdf file by a
+ *          string of 12 '0's, effectively removing the date without
+ *          invalidating the byte counters in the pdf file:
+ *              fileReplaceBytes(filein 86 12 (char *)"000000000000" 12 fileout
+ * 
+ */ +l_ok +fileReplaceBytes(const char *filein, + l_int32 start, + l_int32 nbytes, + l_uint8 *newdata, + size_t newsize, + const char *fileout) +{ +l_int32 i, index; +size_t inbytes, outbytes; +l_uint8 *datain, *dataout; + + PROCNAME("fileReplaceBytes"); + + if (!filein || !fileout) + return ERROR_INT("filein and fileout not both specified", procName, 1); + + datain = l_binaryRead(filein, &inbytes); + if (start + nbytes > inbytes) + L_WARNING("start + nbytes > length(filein) = %zu\n", procName, inbytes); + + if (!newdata) newsize = 0; + outbytes = inbytes - nbytes + newsize; + if ((dataout = (l_uint8 *)LEPT_CALLOC(outbytes, 1)) == NULL) { + LEPT_FREE(datain); + return ERROR_INT("calloc fail for dataout", procName, 1); + } + + for (i = 0; i < start; i++) + dataout[i] = datain[i]; + for (i = start; i < start + newsize; i++) + dataout[i] = newdata[i - start]; + index = start + nbytes; /* for datain */ + start += newsize; /* for dataout */ + for (i = start; i < outbytes; i++, index++) + dataout[i] = datain[index]; + l_binaryWrite(fileout, "w", dataout, outbytes); + + LEPT_FREE(datain); + LEPT_FREE(dataout); + return 0; +} + + +/*---------------------------------------------------------------------* + * Generate random integer in given range * + *---------------------------------------------------------------------*/ +/*! + * \brief genRandomIntegerInRange() + * + * \param[in] range size of range; must be >= 2 + * \param[in] seed use 0 to skip; otherwise call srand + * \param[out] pval random integer in range {0 ... range-1} + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) For example, to choose a rand integer between 0 and 99,
+ *          use %range = 100.
+ * 
+ */ +l_ok +genRandomIntegerInRange(l_int32 range, + l_int32 seed, + l_int32 *pval) +{ + PROCNAME("genRandomIntegerInRange"); + + if (!pval) + return ERROR_INT("&val not defined", procName, 1); + *pval = 0; + if (range < 2) + return ERROR_INT("range must be >= 2", procName, 1); + + if (seed > 0) srand(seed); + *pval = (l_int32)((l_float64)range * + ((l_float64)rand() / (l_float64)RAND_MAX)); + return 0; +} + + +/*---------------------------------------------------------------------* + * Simple math function * + *---------------------------------------------------------------------*/ +/*! + * \brief lept_roundftoi() + * + * \param[in] fval + * \return value rounded to int + * + *
+ * Notes:
+ *      (1) For fval >= 0, fval --> round(fval) == floor(fval + 0.5)
+ *          For fval < 0, fval --> -round(-fval))
+ *          This is symmetric around 0.
+ *          e.g., for fval in (-0.5 ... 0.5), fval --> 0
+ * 
+ */ +l_int32 +lept_roundftoi(l_float32 fval) +{ + return (fval >= 0.0) ? (l_int32)(fval + 0.5) : (l_int32)(fval - 0.5); +} + + +/*---------------------------------------------------------------------* + * 64-bit hash functions * + *---------------------------------------------------------------------*/ +/*! + * \brief l_hashStringToUint64() + * + * \param[in] str + * \param[out] phash hash value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The intent of the hash is to avoid collisions by mapping
+ *          the string as randomly as possible into 64 bits.
+ *      (2) To the extent that the hashes are random, the probability of
+ *          a collision can be approximated by the square of the number
+ *          of strings divided by 2^64.  For 1 million strings, the
+ *          collision probability is about 1 in 16 million.
+ *      (3) I expect non-randomness of the distribution to be most evident
+ *          for small text strings.  This hash function has been tested
+ *          for all 5-character text strings composed of 26 letters,
+ *          of which there are 26^5 = 12356630.  There are no hash
+ *          collisions for this set.
+ * 
+ */ +l_ok +l_hashStringToUint64(const char *str, + l_uint64 *phash) +{ +l_uint64 hash, mulp; + + PROCNAME("l_hashStringToUint64"); + + if (phash) *phash = 0; + if (!str || (str[0] == '\0')) + return ERROR_INT("str not defined or empty", procName, 1); + if (!phash) + return ERROR_INT("&hash not defined", procName, 1); + + mulp = 26544357894361247; /* prime, about 1/700 of the max uint64 */ + hash = 104395301; + while (*str) { + hash += (*str++ * mulp) ^ (hash >> 7); /* shift [1...23] are ok */ + } + *phash = hash ^ (hash << 37); + return 0; +} + + +/*! + * \brief l_hashPtToUint64() + * + * \param[in] x, y + * \param[out] phash hash value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This simple hash function has no collisions for
+ *          any of 400 million points with x and y up to 20000.
+ *      (2) Previously used a much more complicated and slower function:
+ *            mulp = 26544357894361;
+ *            hash = 104395301;
+ *            hash += (x * mulp) ^ (hash >> 5);
+ *            hash ^= (hash << 7);
+ *            hash += (y * mulp) ^ (hash >> 7);
+ *            hash = hash ^ (hash << 11);
+ *          Such logical gymnastics to get coverage over the 2^64
+ *          values are not required.
+ * 
+ */ +l_ok +l_hashPtToUint64(l_int32 x, + l_int32 y, + l_uint64 *phash) +{ + PROCNAME("l_hashPtToUint64"); + + if (!phash) + return ERROR_INT("&hash not defined", procName, 1); + + *phash = (l_uint64)(2173249142.3849 * x + 3763193258.6227 * y); + return 0; +} + + +/*! + * \brief l_hashFloat64ToUint64() + * + * \param[in] nbuckets + * \param[in] val + * \param[out] phash hash value + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Simple, fast hash for using dnaHash with 64-bit data
+ *          (e.g., sets and histograms).
+ *      (2) The resulting hash is called a "key" in a lookup
+ *          operation.  The bucket for %val in a dnaHash is simply
+ *          found by taking the mod of the hash with the number of
+ *          buckets (which is prime).  What gets stored in the
+ *          dna in that bucket could depend on use, but for the most
+ *          flexibility, we store an index into the associated dna.
+ *          This is all that is required for generating either a hash set
+ *          or a histogram (an example of a hash map).
+ *      (3) For example, to generate a histogram, the histogram dna,
+ *          a histogram of unique values aligned with the histogram dna,
+ *          and a dnahash hashmap are built.  See l_dnaMakeHistoByHash().
+ * 
+ */ +l_ok +l_hashFloat64ToUint64(l_int32 nbuckets, + l_float64 val, + l_uint64 *phash) +{ + PROCNAME("l_hashFloatToUint64"); + + if (!phash) + return ERROR_INT("&hash not defined", procName, 1); + *phash = (l_uint64)((21.732491 * nbuckets) * val); + return 0; +} + + +/*---------------------------------------------------------------------* + * Prime finders * + *---------------------------------------------------------------------*/ +/*! + * \brief findNextLargerPrime() + * + * \param[in] start + * \param[out] pprime first prime larger than %start + * \return 0 if OK, 1 on error + */ +l_ok +findNextLargerPrime(l_int32 start, + l_uint32 *pprime) +{ +l_int32 i, is_prime; + + PROCNAME("findNextLargerPrime"); + + if (!pprime) + return ERROR_INT("&prime not defined", procName, 1); + *pprime = 0; + if (start <= 0) + return ERROR_INT("start must be > 0", procName, 1); + + for (i = start + 1; ; i++) { + lept_isPrime(i, &is_prime, NULL); + if (is_prime) { + *pprime = i; + return 0; + } + } + + return ERROR_INT("prime not found!", procName, 1); +} + + +/*! + * \brief lept_isPrime() + * + * \param[in] n 64-bit unsigned + * \param[out] pis_prime 1 if prime, 0 otherwise + * \param[out] pfactor [optional] smallest divisor, or 0 on error + * or if prime + * \return 0 if OK, 1 on error + */ +l_ok +lept_isPrime(l_uint64 n, + l_int32 *pis_prime, + l_uint32 *pfactor) +{ +l_uint32 div; +l_uint64 limit, ratio; + + PROCNAME("lept_isPrime"); + + if (pis_prime) *pis_prime = 0; + if (pfactor) *pfactor = 0; + if (!pis_prime) + return ERROR_INT("&is_prime not defined", procName, 1); + if (n <= 0) + return ERROR_INT("n must be > 0", procName, 1); + + if (n % 2 == 0) { + if (pfactor) *pfactor = 2; + return 0; + } + + limit = (l_uint64)sqrt((l_float64)n); + for (div = 3; div < limit; div += 2) { + ratio = n / div; + if (ratio * div == n) { + if (pfactor) *pfactor = div; + return 0; + } + } + + *pis_prime = 1; + return 0; +} + + +/*---------------------------------------------------------------------* + * Gray code conversion * + *---------------------------------------------------------------------*/ +/*! + * \brief convertIntToGrayCode() + * + * \param[in] val integer value + * \return corresponding gray code value + * + *
+ * Notes:
+ *      (1) Gray code values corresponding to integers differ by
+ *          only one bit transition between successive integers.
+ * 
+ */ +l_uint32 +convertIntToGrayCode(l_uint32 val) +{ + return (val >> 1) ^ val; +} + + +/*! + * \brief convertGrayCodeToInt() + * + * \param[in] val gray code value + * \return corresponding integer value + */ +l_uint32 +convertGrayCodeToInt(l_uint32 val) +{ +l_uint32 shift; + + for (shift = 1; shift < 32; shift <<= 1) + val ^= val >> shift; + return val; +} + + +/*---------------------------------------------------------------------* + * Leptonica version number * + *---------------------------------------------------------------------*/ +/*! + * \brief getLeptonicaVersion() + * + * Return: string of version number (e.g., 'leptonica-1.74.2') + * + * Notes: + * (1) The caller has responsibility to free the memory. + */ +char * +getLeptonicaVersion() +{ +size_t bufsize = 100; + + char *version = (char *)LEPT_CALLOC(bufsize, sizeof(char)); + +#ifdef _MSC_VER + #ifdef _USRDLL + char dllStr[] = "DLL"; + #else + char dllStr[] = "LIB"; + #endif + #ifdef _DEBUG + char debugStr[] = "Debug"; + #else + char debugStr[] = "Release"; + #endif + #ifdef _M_IX86 + char bitStr[] = " x86"; + #elif _M_X64 + char bitStr[] = " x64"; + #else + char bitStr[] = ""; + #endif + snprintf(version, bufsize, "leptonica-%d.%d.%d (%s, %s) [MSC v.%d %s %s%s]", + LIBLEPT_MAJOR_VERSION, LIBLEPT_MINOR_VERSION, LIBLEPT_PATCH_VERSION, + __DATE__, __TIME__, _MSC_VER, dllStr, debugStr, bitStr); + +#else + + snprintf(version, bufsize, "leptonica-%d.%d.%d", LIBLEPT_MAJOR_VERSION, + LIBLEPT_MINOR_VERSION, LIBLEPT_PATCH_VERSION); + +#endif /* _MSC_VER */ + return version; +} + + +/*---------------------------------------------------------------------* + * Timing procs * + *---------------------------------------------------------------------*/ +#if !defined(_WIN32) && !defined(__Fuchsia__) + +#include +#include + +static struct rusage rusage_before; +static struct rusage rusage_after; + +/*! + * \brief startTimer(), stopTimer() + * + * Notes: + * (1) These measure the cpu time elapsed between the two calls: + * startTimer(); + * .... + * fprintf(stderr, "Elapsed time = %7.3f sec\n", stopTimer()); + */ +void +startTimer(void) +{ + getrusage(RUSAGE_SELF, &rusage_before); +} + +l_float32 +stopTimer(void) +{ +l_int32 tsec, tusec; + + getrusage(RUSAGE_SELF, &rusage_after); + + tsec = rusage_after.ru_utime.tv_sec - rusage_before.ru_utime.tv_sec; + tusec = rusage_after.ru_utime.tv_usec - rusage_before.ru_utime.tv_usec; + return (tsec + ((l_float32)tusec) / 1000000.0); +} + + +/*! + * \brief startTimerNested(), stopTimerNested() + * + * Example of usage: + * + * L_TIMER t1 = startTimerNested(); + * .... + * L_TIMER t2 = startTimerNested(); + * .... + * fprintf(stderr, "Elapsed time 2 = %7.3f sec\n", stopTimerNested(t2)); + * .... + * fprintf(stderr, "Elapsed time 1 = %7.3f sec\n", stopTimerNested(t1)); + */ +L_TIMER +startTimerNested(void) +{ +struct rusage *rusage_start; + + rusage_start = (struct rusage *)LEPT_CALLOC(1, sizeof(struct rusage)); + getrusage(RUSAGE_SELF, rusage_start); + return rusage_start; +} + +l_float32 +stopTimerNested(L_TIMER rusage_start) +{ +l_int32 tsec, tusec; +struct rusage rusage_stop; + + getrusage(RUSAGE_SELF, &rusage_stop); + + tsec = rusage_stop.ru_utime.tv_sec - + ((struct rusage *)rusage_start)->ru_utime.tv_sec; + tusec = rusage_stop.ru_utime.tv_usec - + ((struct rusage *)rusage_start)->ru_utime.tv_usec; + LEPT_FREE(rusage_start); + return (tsec + ((l_float32)tusec) / 1000000.0); +} + + +/*! + * \brief l_getCurrentTime() + * + * \param[out] sec [optional] in seconds since birth of Unix + * \param[out] usec [optional] in microseconds since birth of Unix + * \return void + */ +void +l_getCurrentTime(l_int32 *sec, + l_int32 *usec) +{ +struct timeval tv; + + gettimeofday(&tv, NULL); + if (sec) *sec = (l_int32)tv.tv_sec; + if (usec) *usec = (l_int32)tv.tv_usec; + return; +} + +#elif defined(__Fuchsia__) /* resource.h not implemented on Fuchsia. */ + + /* Timer functions are used for testing and debugging, and + * are stubbed out. If they are needed in the future, they + * can be implemented in Fuchsia using the zircon syscall + * zx_object_get_info() in ZX_INFOR_THREAD_STATS mode. */ + +void +startTimer(void) +{ +} + +l_float32 +stopTimer(void) +{ + return 0.0; +} + +L_TIMER +startTimerNested(void) +{ + return NULL; +} + +l_float32 +stopTimerNested(L_TIMER rusage_start) +{ + return 0.0; +} + +void +l_getCurrentTime(l_int32 *sec, + l_int32 *usec) +{ +} + +#else /* _WIN32 : resource.h not implemented under Windows */ + + /* Note: if division by 10^7 seems strange, the time is expressed + * as the number of 100-nanosecond intervals that have elapsed + * since 12:00 A.M. January 1, 1601. */ + +static ULARGE_INTEGER utime_before; +static ULARGE_INTEGER utime_after; + +void +startTimer(void) +{ +HANDLE this_process; +FILETIME start, stop, kernel, user; + + this_process = GetCurrentProcess(); + + GetProcessTimes(this_process, &start, &stop, &kernel, &user); + + utime_before.LowPart = user.dwLowDateTime; + utime_before.HighPart = user.dwHighDateTime; +} + +l_float32 +stopTimer(void) +{ +HANDLE this_process; +FILETIME start, stop, kernel, user; +ULONGLONG hnsec; /* in units of hecto-nanosecond (100 ns) intervals */ + + this_process = GetCurrentProcess(); + + GetProcessTimes(this_process, &start, &stop, &kernel, &user); + + utime_after.LowPart = user.dwLowDateTime; + utime_after.HighPart = user.dwHighDateTime; + hnsec = utime_after.QuadPart - utime_before.QuadPart; + return (l_float32)(signed)hnsec / 10000000.0; +} + +L_TIMER +startTimerNested(void) +{ +HANDLE this_process; +FILETIME start, stop, kernel, user; +ULARGE_INTEGER *utime_start; + + this_process = GetCurrentProcess(); + + GetProcessTimes (this_process, &start, &stop, &kernel, &user); + + utime_start = (ULARGE_INTEGER *)LEPT_CALLOC(1, sizeof(ULARGE_INTEGER)); + utime_start->LowPart = user.dwLowDateTime; + utime_start->HighPart = user.dwHighDateTime; + return utime_start; +} + +l_float32 +stopTimerNested(L_TIMER utime_start) +{ +HANDLE this_process; +FILETIME start, stop, kernel, user; +ULARGE_INTEGER utime_stop; +ULONGLONG hnsec; /* in units of 100 ns intervals */ + + this_process = GetCurrentProcess (); + + GetProcessTimes (this_process, &start, &stop, &kernel, &user); + + utime_stop.LowPart = user.dwLowDateTime; + utime_stop.HighPart = user.dwHighDateTime; + hnsec = utime_stop.QuadPart - ((ULARGE_INTEGER *)utime_start)->QuadPart; + LEPT_FREE(utime_start); + return (l_float32)(signed)hnsec / 10000000.0; +} + +void +l_getCurrentTime(l_int32 *sec, + l_int32 *usec) +{ +ULARGE_INTEGER utime, birthunix; +FILETIME systemtime; +LONGLONG birthunixhnsec = 116444736000000000; /*in units of 100 ns */ +LONGLONG usecs; + + GetSystemTimeAsFileTime(&systemtime); + utime.LowPart = systemtime.dwLowDateTime; + utime.HighPart = systemtime.dwHighDateTime; + + birthunix.LowPart = (DWORD) birthunixhnsec; + birthunix.HighPart = birthunixhnsec >> 32; + + usecs = (LONGLONG) ((utime.QuadPart - birthunix.QuadPart) / 10); + + if (sec) *sec = (l_int32) (usecs / 1000000); + if (usec) *usec = (l_int32) (usecs % 1000000); + return; +} + +#endif + + +/*! + * \brief startWallTimer() + * + * \return walltimer-ptr + * + *
+ * Notes:
+ *      (1) These measure the wall clock time  elapsed between the two calls:
+ *            L_WALLTIMER *timer = startWallTimer();
+ *            ....
+ *            fprintf(stderr, "Elapsed time = %f sec\n", stopWallTimer(&timer);
+ *      (2) Note that the timer object is destroyed by stopWallTimer().
+ * 
+ */ +L_WALLTIMER * +startWallTimer(void) +{ +L_WALLTIMER *timer; + + timer = (L_WALLTIMER *)LEPT_CALLOC(1, sizeof(L_WALLTIMER)); + l_getCurrentTime(&timer->start_sec, &timer->start_usec); + return timer; +} + +/*! + * \brief stopWallTimer() + * + * \param[in,out] ptimer walltimer pointer + * \return time wall time elapsed in seconds + */ +l_float32 +stopWallTimer(L_WALLTIMER **ptimer) +{ +l_int32 tsec, tusec; +L_WALLTIMER *timer; + + PROCNAME("stopWallTimer"); + + if (!ptimer) + return (l_float32)ERROR_FLOAT("&timer not defined", procName, 0.0); + timer = *ptimer; + if (!timer) + return (l_float32)ERROR_FLOAT("timer not defined", procName, 0.0); + + l_getCurrentTime(&timer->stop_sec, &timer->stop_usec); + tsec = timer->stop_sec - timer->start_sec; + tusec = timer->stop_usec - timer->start_usec; + LEPT_FREE(timer); + *ptimer = NULL; + return (tsec + ((l_float32)tusec) / 1000000.0); +} + + +/*! + * \brief l_getFormattedDate() + * + * \return formatted date string, or NULL on error + * + *
+ * Notes:
+ *      (1) This is used in pdf, in the form specified in section 3.8.2 of
+ *          http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf
+ *      (2) Contributed by Dave Bryan.  Works on all platforms.
+ * 
+ */ +char * +l_getFormattedDate() +{ +char buf[128] = "", sep = 'Z'; +l_int32 gmt_offset, relh, relm; +time_t ut, lt; +struct tm Tm; +struct tm *tptr = &Tm; + + ut = time(NULL); + + /* This generates a second "time_t" value by calling "gmtime" to + fill in a "tm" structure expressed as UTC and then calling + "mktime", which expects a "tm" structure expressed as the + local time. The result is a value that is offset from the + value returned by the "time" function by the local UTC offset. + "tm_isdst" is set to -1 to tell "mktime" to determine for + itself whether DST is in effect. This is necessary because + "gmtime" always sets "tm_isdst" to 0, which would tell + "mktime" to presume that DST is not in effect. */ +#ifdef _WIN32 + #ifdef _MSC_VER + gmtime_s(tptr, &ut); + #else /* mingw */ + tptr = gmtime(&ut); + #endif +#else + gmtime_r(&ut, tptr); +#endif + tptr->tm_isdst = -1; + lt = mktime(tptr); + + /* Calls "difftime" to obtain the resulting difference in seconds, + * because "time_t" is an opaque type, per the C standard. */ + gmt_offset = (l_int32) difftime(ut, lt); + if (gmt_offset > 0) + sep = '+'; + else if (gmt_offset < 0) + sep = '-'; + relh = L_ABS(gmt_offset) / 3600; + relm = (L_ABS(gmt_offset) % 3600) / 60; + +#ifdef _WIN32 + #ifdef _MSC_VER + localtime_s(tptr, &ut); + #else /* mingw */ + tptr = localtime(&ut); + #endif +#else + localtime_r(&ut, tptr); +#endif + strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", tptr); + sprintf(buf + 14, "%c%02d'%02d'", sep, relh, relm); + return stringNew(buf); +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/utils2.c b/hgdriver/3rdparty/hgOCR/leptonica/utils2.c new file mode 100644 index 0000000..a73f810 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/utils2.c @@ -0,0 +1,3364 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file utils2.c + *
+ *
+ *      ------------------------------------------
+ *      This file has these utilities:
+ *         - safe string operations
+ *         - find/replace operations on strings
+ *         - read/write between file and memory
+ *         - multi-platform file and directory operations
+ *         - file name operations
+ *      ------------------------------------------
+ *
+ *       Safe string procs
+ *           char      *stringNew()
+ *           l_int32    stringCopy()
+ *           l_int32    stringCopySegment()
+ *           l_int32    stringReplace()
+ *           l_int32    stringLength()
+ *           l_int32    stringCat()
+ *           char      *stringConcatNew()
+ *           char      *stringJoin()
+ *           l_int32    stringJoinIP()
+ *           char      *stringReverse()
+ *           char      *strtokSafe()
+ *           l_int32    stringSplitOnToken()
+ *
+ *       Find and replace string and array procs
+ *           l_int32    stringCheckForChars()
+ *           char      *stringRemoveChars()
+ *           char      *stringReplaceEachSubstr()
+ *           char      *stringReplaceSubstr()
+ *           L_DNA     *stringFindEachSubstr()
+ *           l_int32    stringFindSubstr()
+ *           l_uint8   *arrayReplaceEachSequence()
+ *           L_DNA     *arrayFindEachSequence()
+ *           l_int32    arrayFindSequence()
+ *
+ *       Safe realloc
+ *           void      *reallocNew()
+ *
+ *       Read and write between file and memory
+ *           l_uint8   *l_binaryRead()
+ *           l_uint8   *l_binaryReadStream()
+ *           l_uint8   *l_binaryReadSelect()
+ *           l_uint8   *l_binaryReadSelectStream()
+ *           l_int32    l_binaryWrite()
+ *           l_int32    nbytesInFile()
+ *           l_int32    fnbytesInFile()
+ *
+ *       Copy and compare in memory
+ *           l_uint8   *l_binaryCopy()
+ *           l_uint8   *l_binaryCompare()
+ *
+ *       File copy operations
+ *           l_int32    fileCopy()
+ *           l_int32    fileConcatenate()
+ *           l_int32    fileAppendString()
+ *
+ *       Multi-platform functions for opening file streams
+ *           FILE      *fopenReadStream()
+ *           FILE      *fopenWriteStream()
+ *           FILE      *fopenReadFromMemory()
+ *
+ *       Opening a windows tmpfile for writing
+ *           FILE      *fopenWriteWinTempfile()
+ *
+ *       Multi-platform functions that avoid C-runtime boundary crossing
+ *       with Windows DLLs
+ *           FILE      *lept_fopen()
+ *           l_int32    lept_fclose()
+ *           void       lept_calloc()
+ *           void       lept_free()
+ *
+ *       Multi-platform file system operations in temp directories
+ *           l_int32    lept_mkdir()
+ *           l_int32    lept_rmdir()
+ *           l_int32    lept_direxists()
+ *           l_int32    lept_mv()
+ *           l_int32    lept_rm_match()
+ *           l_int32    lept_rm()
+ *           l_int32    lept_rmfile()
+ *           l_int32    lept_cp()
+ *
+ *       Special debug/test function for calling 'system'
+ *           void       callSystemDebug()
+ *
+ *       General file name operations
+ *           l_int32    splitPathAtDirectory()
+ *           l_int32    splitPathAtExtension()
+ *           char      *pathJoin()
+ *           char      *appendSubdirs()
+ *
+ *       Special file name operations
+ *           l_int32    convertSepCharsInPath()
+ *           char      *genPathname()
+ *           l_int32    makeTempDirname()
+ *           l_int32    modifyTrailingSlash()
+ *           char      *l_makeTempFilename()
+ *           l_int32    extractNumberFromFilename()
+ *
+ *
+ *  Notes on multi-platform development
+ *  -----------------------------------
+ *  This is important:
+ *  (1) With the exception of splitPathAtDirectory(), splitPathAtExtension()
+  *     and genPathname(), all input pathnames must have unix separators.
+ *  (2) On Windows, when you specify a read or write to "/tmp/...",
+ *      the filename is rewritten to use the Windows temp directory:
+ *         /tmp  ==>   [Temp]...    (windows)
+ *  (3) This filename rewrite, along with the conversion from unix
+ *      to windows pathnames, happens in genPathname().
+ *  (4) Use fopenReadStream() and fopenWriteStream() to open files,
+ *      because these use genPathname() to find the platform-dependent
+ *      filenames.  Likewise for l_binaryRead() and l_binaryWrite().
+ *  (5) For moving, copying and removing files and directories that are in
+ *      subdirectories of /tmp, use the lept_*() file system shell wrappers:
+ *         lept_mkdir(), lept_rmdir(), lept_mv(), lept_rm() and lept_cp().
+ *  (6) Use the lept_*() C library wrappers.  These work properly on
+ *      Windows, where the same DLL must perform complementary operations
+ *      on file streams (open/close) and heap memory (malloc/free):
+ *         lept_fopen(), lept_fclose(), lept_calloc() and lept_free().
+ *  (7) Why read and write files to temp directories?
+ *      The library needs the ability to read and write ephemeral
+ *      files to default places, both for generating debugging output
+ *      and for supporting regression tests.  Applications also need
+ *      this ability for debugging.
+ *  (8) Why do the pathname rewrite on Windows?
+ *      The goal is to have the library, and programs using the library,
+ *      run on multiple platforms without changes.  The location of
+ *      temporary files depends on the platform as well as the user's
+ *      configuration.  Temp files on Windows are in some directory
+ *      not known a priori.  To make everything work seamlessly on
+ *      Windows, every time you open a file for reading or writing,
+ *      use a special function such as fopenReadStream() or
+ *      fopenWriteStream(); these call genPathname() to ensure that
+ *      if it is a temp file, the correct path is used.  To indicate
+ *      that this is a temp file, the application is written with the
+ *      root directory of the path in a canonical form: "/tmp".
+ *  (9) Why is it that multi-platform directory functions like lept_mkdir()
+ *      and lept_rmdir(), as well as associated file functions like
+ *      lept_rm(), lept_mv() and lept_cp(), only work in the temp dir?
+ *      These functions were designed to provide easy manipulation of
+ *      temp files.  The restriction to temp files is for safety -- to
+ *      prevent an accidental deletion of important files.  For example,
+ *      lept_rmdir() first deletes all files in a specified subdirectory
+ *      of temp, and then removes the directory.
+ *
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef _MSC_VER +#include +#include +#define getcwd _getcwd /* fix MSVC warning */ +#else +#include +#endif /* _MSC_VER */ + +#ifdef _WIN32 +#include +#include /* _O_CREAT, ... */ +#include /* _open */ +#include /* _S_IREAD, _S_IWRITE */ +#else +#include /* for stat, mkdir(2) */ +#include +#endif + +#ifdef OS_IOS +#include +#include +#endif + +#include +#include +#include "allheaders.h" + + +/*--------------------------------------------------------------------* + * Safe string operations * + *--------------------------------------------------------------------*/ +/*! + * \brief stringNew() + * + * \param[in] src + * \return dest copy of %src string, or NULL on error + */ +char * +stringNew(const char *src) +{ +l_int32 len; +char *dest; + + PROCNAME("stringNew"); + + if (!src) { + L_WARNING("src not defined\n", procName); + return NULL; + } + + len = strlen(src); + if ((dest = (char *)LEPT_CALLOC(len + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("dest not made", procName, NULL); + + stringCopy(dest, src, len); + return dest; +} + + +/*! + * \brief stringCopy() + * + * \param[in] dest existing byte buffer + * \param[in] src string [optional] can be null + * \param[in] n max number of characters to copy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Relatively safe wrapper for strncpy, that checks the input,
+ *          and does not complain if %src is null or %n < 1.
+ *          If %n < 1, this is a no-op.
+ *      (2) %dest needs to be at least %n bytes in size.
+ *      (3) We don't call strncpy() because valgrind complains about
+ *          use of uninitialized values.
+ * 
+ */ +l_ok +stringCopy(char *dest, + const char *src, + l_int32 n) +{ +l_int32 i; + + PROCNAME("stringCopy"); + + if (!dest) + return ERROR_INT("dest not defined", procName, 1); + if (!src || n < 1) + return 0; + + /* Implementation of strncpy that valgrind doesn't complain about */ + for (i = 0; i < n && src[i] != '\0'; i++) + dest[i] = src[i]; + for (; i < n; i++) + dest[i] = '\0'; + return 0; +} + + +/*! + * \brief stringCopySegment() + * + * + * \param[in] src string + * \param[in] start byte position at start of segment + * \param[in] nbytes number of bytes in the segment; use 0 to go to end + * \return copy of segment, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a variant of stringNew() that makes a new string
+ *          from a segment of the input string.  The segment is specified
+ *          by the starting position and the number of bytes.
+ *      (2) The start location %start must be within the string %src.
+ *      (3) The copy is truncated to the end of the source string.
+ *          Use %nbytes = 0 to copy to the end of %src.
+ * 
+ */ +char * +stringCopySegment(const char *src, + l_int32 start, + l_int32 nbytes) +{ +char *dest; +l_int32 len; + + PROCNAME("stringCopySegment"); + + if (!src) + return (char *)ERROR_PTR("src not defined", procName, NULL); + len = strlen(src); + if (start < 0 || start > len - 1) + return (char *)ERROR_PTR("invalid start", procName, NULL); + if (nbytes <= 0) /* copy to the end */ + nbytes = len - start; + if (start + nbytes > len) /* truncate to the end */ + nbytes = len - start; + if ((dest = (char *)LEPT_CALLOC(nbytes + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("dest not made", procName, NULL); + stringCopy(dest, src + start, nbytes); + return dest; +} + + +/*! + * \brief stringReplace() + * + * \param[out] pdest string copy + * \param[in] src [optional] string; can be null + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Frees any existing dest string
+ *      (2) Puts a copy of src string in the dest
+ *      (3) If either or both strings are null, does something reasonable.
+ * 
+ */ +l_ok +stringReplace(char **pdest, + const char *src) +{ + PROCNAME("stringReplace"); + + if (!pdest) + return ERROR_INT("pdest not defined", procName, 1); + + if (*pdest) + LEPT_FREE(*pdest); + + if (src) + *pdest = stringNew(src); + else + *pdest = NULL; + return 0; +} + + +/*! + * \brief stringLength() + * + * \param[in] src string can be null or NULL-terminated string + * \param[in] size size of src buffer + * \return length of src in bytes. + * + *
+ * Notes:
+ *      (1) Safe implementation of strlen that only checks size bytes
+ *          for trailing NUL.
+ *      (2) Valid returned string lengths are between 0 and size - 1.
+ *          If size bytes are checked without finding a NUL byte, then
+ *          an error is indicated by returning size.
+ * 
+ */ +l_int32 +stringLength(const char *src, + size_t size) +{ +l_int32 i; + + PROCNAME("stringLength"); + + if (!src) + return ERROR_INT("src not defined", procName, 0); + if (size < 1) + return 0; + + for (i = 0; i < size; i++) { + if (src[i] == '\0') + return i; + } + return size; /* didn't find a NUL byte */ +} + + +/*! + * \brief stringCat() + * + * \param[in] dest null-terminated byte buffer + * \param[in] size size of dest + * \param[in] src string can be null or NULL-terminated string + * \return number of bytes added to dest; -1 on error + * + *
+ * Notes:
+ *      (1) Alternative implementation of strncat, that checks the input,
+ *          is easier to use (since the size of the dest buffer is specified
+ *          rather than the number of bytes to copy), and does not complain
+ *          if %src is null.
+ *      (2) Never writes past end of dest.
+ *      (3) If it can't append src (an error), it does nothing.
+ *      (4) N.B. The order of 2nd and 3rd args is reversed from that in
+ *          strncat, as in the Windows function strcat_s().
+ * 
+ */ +l_int32 +stringCat(char *dest, + size_t size, + const char *src) +{ +l_int32 i, n; +l_int32 lendest, lensrc; + + PROCNAME("stringCat"); + + if (!dest) + return ERROR_INT("dest not defined", procName, -1); + if (size < 1) + return ERROR_INT("size < 1; too small", procName, -1); + if (!src) + return 0; + + lendest = stringLength(dest, size); + if (lendest == size) + return ERROR_INT("no terminating nul byte", procName, -1); + lensrc = stringLength(src, size); + if (lensrc == 0) + return 0; + n = (lendest + lensrc > size - 1 ? size - lendest - 1 : lensrc); + if (n < 1) + return ERROR_INT("dest too small for append", procName, -1); + + for (i = 0; i < n; i++) + dest[lendest + i] = src[i]; + dest[lendest + n] = '\0'; + return n; +} + + +/*! + * \brief stringConcatNew() + * + * \param[in] first first string in list + * \param[in] ... NULL-terminated list of strings + * \return result new string concatenating the input strings, or + * NULL if first == NULL + * + *
+ * Notes:
+ *      (1) The last arg in the list of strings must be NULL.
+ *      (2) Caller must free the returned string.
+ * 
+ */ +char * +stringConcatNew(const char *first, ...) +{ +size_t len; +char *result, *ptr; +const char *arg; +va_list args; + + if (!first) return NULL; + + /* Find the length of the output string */ + va_start(args, first); + len = strlen(first); + while ((arg = va_arg(args, const char *)) != NULL) + len += strlen(arg); + va_end(args); + result = (char *)LEPT_CALLOC(len + 1, sizeof(char)); + + /* Concatenate the args */ + va_start(args, first); + ptr = result; + arg = first; + while (*arg) + *ptr++ = *arg++; + while ((arg = va_arg(args, const char *)) != NULL) { + while (*arg) + *ptr++ = *arg++; + } + va_end(args); + return result; +} + + +/*! + * \brief stringJoin() + * + * \param[in] src1 [optional] string; can be null + * \param[in] src2 [optional] string; can be null + * \return concatenated string, or NULL on error + * + *
+ * Notes:
+ *      (1) This is a safe version of strcat; it makes a new string.
+ *      (2) It is not an error if either or both of the strings
+ *          are empty, or if either or both of the pointers are null.
+ * 
+ */ +char * +stringJoin(const char *src1, + const char *src2) +{ +char *dest; +l_int32 srclen1, srclen2, destlen; + + PROCNAME("stringJoin"); + + srclen1 = (src1) ? strlen(src1) : 0; + srclen2 = (src2) ? strlen(src2) : 0; + destlen = srclen1 + srclen2 + 3; + + if ((dest = (char *)LEPT_CALLOC(destlen, sizeof(char))) == NULL) + return (char *)ERROR_PTR("calloc fail for dest", procName, NULL); + + if (src1) + stringCopy(dest, src1, srclen1); + if (src2) + strncat(dest, src2, srclen2); + return dest; +} + + +/*! + * \brief stringJoinIP() + * + * \param[in,out] psrc1 address of string src1; cannot be on the stack + * \param[in] src2 [optional] string; can be null + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a safe in-place version of strcat.  The contents of
+ *          src1 is replaced by the concatenation of src1 and src2.
+ *      (2) It is not an error if either or both of the strings
+ *          are empty (""), or if the pointers to the strings (*psrc1, src2)
+ *          are null.
+ *      (3) src1 should be initialized to null or an empty string
+ *          before the first call.  Use one of these:
+ *              char *src1 = NULL;
+ *              char *src1 = stringNew("");
+ *          Then call with:
+ *              stringJoinIP(&src1, src2);
+ *      (4) This can also be implemented as a macro:
+ * \code
+ *              #define stringJoinIP(src1, src2) \
+ *                  {tmpstr = stringJoin((src1),(src2)); \
+ *                  LEPT_FREE(src1); \
+ *                  (src1) = tmpstr;}
+ * \endcode
+ *      (5) Another function to consider for joining many strings is
+ *          stringConcatNew().
+ * 
+ */ +l_ok +stringJoinIP(char **psrc1, + const char *src2) +{ +char *tmpstr; + + PROCNAME("stringJoinIP"); + + if (!psrc1) + return ERROR_INT("&src1 not defined", procName, 1); + + tmpstr = stringJoin(*psrc1, src2); + LEPT_FREE(*psrc1); + *psrc1 = tmpstr; + return 0; +} + + +/*! + * \brief stringReverse() + * + * \param[in] src string + * \return dest newly-allocated reversed string + */ +char * +stringReverse(const char *src) +{ +char *dest; +l_int32 i, len; + + PROCNAME("stringReverse"); + + if (!src) + return (char *)ERROR_PTR("src not defined", procName, NULL); + len = strlen(src); + if ((dest = (char *)LEPT_CALLOC(len + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("calloc fail for dest", procName, NULL); + for (i = 0; i < len; i++) + dest[i] = src[len - 1 - i]; + + return dest; +} + + +/*! + * \brief strtokSafe() + * + * \param[in] cstr input string to be sequentially parsed; + * use NULL after the first call + * \param[in] seps a string of character separators + * \param[out] psaveptr ptr to the next char after + * the last encountered separator + * \return substr a new string that is copied from the previous + * saveptr up to but not including the next + * separator character, or NULL if end of cstr. + * + *
+ * Notes:
+ *      (1) This is a thread-safe implementation of strtok.
+ *      (2) It has the same interface as strtok_r.
+ *      (3) It differs from strtok_r in usage in two respects:
+ *          (a) the input string is not altered
+ *          (b) each returned substring is newly allocated and must
+ *              be freed after use.
+ *      (4) Let me repeat that.  This is "safe" because the input
+ *          string is not altered and because each returned string
+ *          is newly allocated on the heap.
+ *      (5) It is here because, surprisingly, some C libraries don't
+ *          include strtok_r.
+ *      (6) Important usage points:
+ *          ~ Input the string to be parsed on the first invocation.
+ *          ~ Then input NULL after that; the value returned in saveptr
+ *            is used in all subsequent calls.
+ *      (7) This is only slightly slower than strtok_r.
+ * 
+ */ +char * +strtokSafe(char *cstr, + const char *seps, + char **psaveptr) +{ +char nextc; +char *start, *substr; +l_int32 istart, i, j, nchars; + + PROCNAME("strtokSafe"); + + if (!seps) + return (char *)ERROR_PTR("seps not defined", procName, NULL); + if (!psaveptr) + return (char *)ERROR_PTR("&saveptr not defined", procName, NULL); + + if (!cstr) { + start = *psaveptr; + } else { + start = cstr; + *psaveptr = NULL; + } + if (!start) /* nothing to do */ + return NULL; + + /* First time, scan for the first non-sep character */ + istart = 0; + if (cstr) { + for (istart = 0;; istart++) { + if ((nextc = start[istart]) == '\0') { + *psaveptr = NULL; /* in case caller doesn't check ret value */ + return NULL; + } + if (!strchr(seps, nextc)) + break; + } + } + + /* Scan through, looking for a sep character; if none is + * found, 'i' will be at the end of the string. */ + for (i = istart;; i++) { + if ((nextc = start[i]) == '\0') + break; + if (strchr(seps, nextc)) + break; + } + + /* Save the substring */ + nchars = i - istart; + substr = (char *)LEPT_CALLOC(nchars + 1, sizeof(char)); + stringCopy(substr, start + istart, nchars); + + /* Look for the next non-sep character. + * If this is the last substring, return a null saveptr. */ + for (j = i;; j++) { + if ((nextc = start[j]) == '\0') { + *psaveptr = NULL; /* no more non-sep characters */ + break; + } + if (!strchr(seps, nextc)) { + *psaveptr = start + j; /* start here on next call */ + break; + } + } + + return substr; +} + + +/*! + * \brief stringSplitOnToken() + * + * \param[in] cstr input string to be split; not altered + * \param[in] seps a string of character separators + * \param[out] phead ptr to copy of the input string, up to + * the first separator token encountered + * \param[out] ptail ptr to copy of the part of the input string + * starting with the first non-separator character + * that occurs after the first separator is found + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The input string is not altered; all split parts are new strings.
+ *      (2) The split occurs around the first consecutive sequence of
+ *          tokens encountered.
+ *      (3) The head goes from the beginning of the string up to
+ *          but not including the first token found.
+ *      (4) The tail contains the second part of the string, starting
+ *          with the first char in that part that is NOT a token.
+ *      (5) If no separator token is found, 'head' contains a copy
+ *          of the input string and 'tail' is null.
+ * 
+ */ +l_ok +stringSplitOnToken(char *cstr, + const char *seps, + char **phead, + char **ptail) +{ +char *saveptr; + + PROCNAME("stringSplitOnToken"); + + if (!phead) + return ERROR_INT("&head not defined", procName, 1); + if (!ptail) + return ERROR_INT("&tail not defined", procName, 1); + *phead = *ptail = NULL; + if (!cstr) + return ERROR_INT("cstr not defined", procName, 1); + if (!seps) + return ERROR_INT("seps not defined", procName, 1); + + *phead = strtokSafe(cstr, seps, &saveptr); + if (saveptr) + *ptail = stringNew(saveptr); + return 0; +} + + +/*--------------------------------------------------------------------* + * Find and replace procs * + *--------------------------------------------------------------------*/ +/*! + * \brief stringCheckForChars() + * + * \param[in] src input string; can be of zero length + * \param[in] chars string of chars to be searched for in %src + * \param[out] pfound 1 if any characters are found; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This can be used to sanitize an operation by checking for
+ *          special characters that don't belong in a string.
+ * 
+ */ +l_ok +stringCheckForChars(const char *src, + const char *chars, + l_int32 *pfound) +{ +char ch; +l_int32 i, n; + + PROCNAME("stringCheckForChars"); + + if (!pfound) + return ERROR_INT("&found not defined", procName, 1); + *pfound = FALSE; + if (!src || !chars) + return ERROR_INT("src and chars not both defined", procName, 1); + + n = strlen(src); + for (i = 0; i < n; i++) { + ch = src[i]; + if (strchr(chars, ch)) { + *pfound = TRUE; + break; + } + } + return 0; +} + + +/*! + * \brief stringRemoveChars() + * + * \param[in] src input string; can be of zero length + * \param[in] remchars string of chars to be removed from src + * \return dest string with specified chars removed, or NULL on error + */ +char * +stringRemoveChars(const char *src, + const char *remchars) +{ +char ch; +char *dest; +l_int32 nsrc, i, k; + + PROCNAME("stringRemoveChars"); + + if (!src) + return (char *)ERROR_PTR("src not defined", procName, NULL); + if (!remchars) + return stringNew(src); + + if ((dest = (char *)LEPT_CALLOC(strlen(src) + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("dest not made", procName, NULL); + nsrc = strlen(src); + for (i = 0, k = 0; i < nsrc; i++) { + ch = src[i]; + if (!strchr(remchars, ch)) + dest[k++] = ch; + } + + return dest; +} + + +/*! + * \brief stringReplaceEachSubstr() + * + * \param[in] src input string; can be of zero length + * \param[in] sub1 substring to be replaced + * \param[in] sub2 substring to put in; can be "" + * \param[out] pcount [optional] the number of times that sub1 + * is found in src; 0 if not found + * \return dest string with substring replaced, or NULL if the + * substring not found or on error. + * + *
+ * Notes:
+ *      (1) This is a wrapper for simple string substitution that uses
+ *          the more general function arrayReplaceEachSequence().
+ *      (2) This finds every non-overlapping occurrence of %sub1 in
+ *          %src, and replaces it with %sub2.  By "non-overlapping"
+ *          we mean that after it finds each match, it removes the
+ *          matching characters, replaces with the substitution string
+ *          (if not empty), and continues.  For example, if you replace
+ *          'aa' by 'X' in 'baaabbb', you find one match at position 1
+ *          and return 'bXabbb'.
+ *      (3) To only remove each instance of sub1, use "" for sub2
+ *      (4) Returns a copy of %src if sub1 and sub2 are the same.
+ *      (5) If the input %src is binary data that can have null characters,
+ *          use arrayReplaceEachSequence() directly.
+ * 
+ */ +char * +stringReplaceEachSubstr(const char *src, + const char *sub1, + const char *sub2, + l_int32 *pcount) +{ +size_t datalen; + + PROCNAME("stringReplaceEachSubstr"); + + if (pcount) *pcount = 0; + if (!src || !sub1 || !sub2) + return (char *)ERROR_PTR("src, sub1, sub2 not all defined", + procName, NULL); + + if (strlen(sub2) > 0) { + return (char *)arrayReplaceEachSequence( + (const l_uint8 *)src, strlen(src), + (const l_uint8 *)sub1, strlen(sub1), + (const l_uint8 *)sub2, strlen(sub2), + &datalen, pcount); + } else { /* empty replacement string; removal only */ + return (char *)arrayReplaceEachSequence( + (const l_uint8 *)src, strlen(src), + (const l_uint8 *)sub1, strlen(sub1), + NULL, 0, &datalen, pcount); + } +} + + +/*! + * \brief stringReplaceSubstr() + * + * \param[in] src input string; can be of zero length + * \param[in] sub1 substring to be replaced + * \param[in] sub2 substring to put in; can be "" + * \param[in,out] ploc [optional] input start location for search; + * returns the loc after replacement + * \param[out] pfound [optional] 1 if sub1 is found; 0 otherwise + * \return dest string with substring replaced, or NULL on error. + * + *
+ * Notes:
+ *      (1) Replaces the first instance.
+ *      (2) To remove sub1 without replacement, use "" for sub2.
+ *      (3) Returns a copy of %src if either no instance of %sub1 is found,
+ *          or if %sub1 and %sub2 are the same.
+ *      (4) If %ploc == NULL, the search will start at the beginning of %src.
+ *          If %ploc != NULL, *ploc must be initialized to the byte offset
+ *          within %src from which the search starts.  To search the
+ *          string from the beginning, set %loc = 0 and input &loc.
+ *          After finding %sub1 and replacing it with %sub2, %loc will be
+ *          returned as the next position after %sub2 in the output string.
+ *      (5) Note that the output string also includes all the characters
+ *          from the input string that occur after the single substitution.
+ * 
+ */ +char * +stringReplaceSubstr(const char *src, + const char *sub1, + const char *sub2, + l_int32 *ploc, + l_int32 *pfound) +{ +const char *ptr; +char *dest; +l_int32 nsrc, nsub1, nsub2, len, npre, loc; + + PROCNAME("stringReplaceSubstr"); + + if (pfound) *pfound = 0; + if (!src || !sub1 || !sub2) + return (char *)ERROR_PTR("src, sub1, sub2 not all defined", + procName, NULL); + + if (ploc) + loc = *ploc; + else + loc = 0; + if (!strcmp(sub1, sub2)) + return stringNew(src); + if ((ptr = strstr(src + loc, sub1)) == NULL) + return stringNew(src); + if (pfound) *pfound = 1; + + nsrc = strlen(src); + nsub1 = strlen(sub1); + nsub2 = strlen(sub2); + len = nsrc + nsub2 - nsub1; + if ((dest = (char *)LEPT_CALLOC(len + 1, sizeof(char))) == NULL) + return (char *)ERROR_PTR("dest not made", procName, NULL); + npre = ptr - src; + memcpy(dest, src, npre); + strcpy(dest + npre, sub2); + strcpy(dest + npre + nsub2, ptr + nsub1); + if (ploc) *ploc = npre + nsub2; + return dest; +} + + +/*! + * \brief stringFindEachSubstr() + * + * \param[in] src input string; can be of zero length + * \param[in] sub substring to be searched for + * \return dna of offsets where the sequence is found, or NULL if + * none are found or on error + * + *
+ * Notes:
+ *      (1) This finds every non-overlapping occurrence in %src of %sub.
+ *          After it finds each match, it moves forward in %src by the length
+ *          of %sub before continuing the search.  So for example,
+ *          if you search for the sequence 'aa' in the data 'baaabbb',
+ *          you find one match at position 1.
+
+ * 
+ */ +L_DNA * +stringFindEachSubstr(const char *src, + const char *sub) +{ + PROCNAME("stringFindEachSubstr"); + + if (!src || !sub) + return (L_DNA *)ERROR_PTR("src, sub not both defined", procName, NULL); + + return arrayFindEachSequence((const l_uint8 *)src, strlen(src), + (const l_uint8 *)sub, strlen(sub)); +} + + +/*! + * \brief stringFindSubstr() + * + * \param[in] src input string; can be of zero length + * \param[in] sub substring to be searched for; must not be empty + * \param[out] ploc [optional] location of substring in src + * \return 1 if found; 0 if not found or on error + * + *
+ * Notes:
+ *      (1) This is a wrapper around strstr().  It finds the first
+ *          instance of %sub in %src.  If the substring is not found
+ *          and the location is returned, it has the value -1.
+ *      (2) Both %src and %sub must be defined, and %sub must have
+ *          length of at least 1.
+ * 
+ */ +l_int32 +stringFindSubstr(const char *src, + const char *sub, + l_int32 *ploc) +{ +const char *ptr; + + PROCNAME("stringFindSubstr"); + + if (ploc) *ploc = -1; + if (!src || !sub) + return ERROR_INT("src and sub not both defined", procName, 0); + if (strlen(sub) == 0) + return ERROR_INT("substring length 0", procName, 0); + if (strlen(src) == 0) + return 0; + + if ((ptr = strstr(src, sub)) == NULL) /* not found */ + return 0; + + if (ploc) + *ploc = ptr - src; + return 1; +} + + +/*! + * \brief arrayReplaceEachSequence() + * + * \param[in] datas source byte array + * \param[in] dataslen length of source data, in bytes + * \param[in] seq subarray of bytes to find in source data + * \param[in] seqlen length of subarray, in bytes + * \param[in] newseq replacement subarray; can be null + * \param[in] newseqlen length of replacement subarray, in bytes + * \param[out] pdatadlen length of dest byte array, in bytes + * \param[out] pcount [optional] the number of times that sub1 + * is found in src; 0 if not found + * \return datad with all all subarrays replaced (or removed) + * + *
+ * Notes:
+ *      (1) The byte arrays %datas, %seq and %newseq are not C strings,
+ *          because they can contain null bytes.  Therefore, for each
+ *          we must give the length of the array.
+ *      (2) If %newseq == NULL, this just removes all instances of %seq.
+ *          Otherwise, it replaces every non-overlapping occurrence of
+ *          %seq in %datas with %newseq. A new array %datad and its
+ *          size are returned.  See arrayFindEachSequence() for more
+ *          details on finding non-overlapping occurrences.
+ *      (3) If no instances of %seq are found, this returns a copy of %datas.
+ *      (4) The returned %datad is null terminated.
+ *      (5) Can use stringReplaceEachSubstr() if using C strings.
+ * 
+ */ +l_uint8 * +arrayReplaceEachSequence(const l_uint8 *datas, + size_t dataslen, + const l_uint8 *seq, + size_t seqlen, + const l_uint8 *newseq, + size_t newseqlen, + size_t *pdatadlen, + l_int32 *pcount) +{ +l_uint8 *datad; +size_t newsize; +l_int32 n, i, j, di, si, index, incr; +L_DNA *da; + + PROCNAME("arrayReplaceEachSequence"); + + if (pcount) *pcount = 0; + if (!datas || !seq) + return (l_uint8 *)ERROR_PTR("datas & seq not both defined", + procName, NULL); + if (!pdatadlen) + return (l_uint8 *)ERROR_PTR("&datadlen not defined", procName, NULL); + *pdatadlen = 0; + + /* Identify the locations of the sequence. If there are none, + * return a copy of %datas. */ + if ((da = arrayFindEachSequence(datas, dataslen, seq, seqlen)) == NULL) { + *pdatadlen = dataslen; + return l_binaryCopy(datas, dataslen); + } + + /* Allocate the output data; insure null termination */ + n = l_dnaGetCount(da); + if (pcount) *pcount = n; + if (!newseq) newseqlen = 0; + newsize = dataslen + n * (newseqlen - seqlen) + 4; + if ((datad = (l_uint8 *)LEPT_CALLOC(newsize, sizeof(l_uint8))) == NULL) { + l_dnaDestroy(&da); + return (l_uint8 *)ERROR_PTR("datad not made", procName, NULL); + } + + /* Replace each sequence instance with a new sequence */ + l_dnaGetIValue(da, 0, &si); + for (i = 0, di = 0, index = 0; i < dataslen; i++) { + if (i == si) { + index++; + if (index < n) { + l_dnaGetIValue(da, index, &si); + incr = L_MIN(seqlen, si - i); /* amount to remove from datas */ + } else { + incr = seqlen; + } + i += incr - 1; /* jump over the matched sequence in datas */ + if (newseq) { /* add new sequence to datad */ + for (j = 0; j < newseqlen; j++) + datad[di++] = newseq[j]; + } + } else { + datad[di++] = datas[i]; + } + } + + *pdatadlen = di; + l_dnaDestroy(&da); + return datad; +} + + +/*! + * \brief arrayFindEachSequence() + * + * \param[in] data byte array + * \param[in] datalen length of data, in bytes + * \param[in] sequence subarray of bytes to find in data + * \param[in] seqlen length of sequence, in bytes + * \return dna of offsets where the sequence is found, or NULL if + * none are found or on error + * + *
+ * Notes:
+ *      (1) The byte arrays %data and %sequence are not C strings,
+ *          because they can contain null bytes.  Therefore, for each
+ *          we must give the length of the array.
+ *      (2) This finds every non-overlapping occurrence in %data of %sequence.
+ *          After it finds each match, it moves forward by the length
+ *          of the sequence before continuing the search.  So for example,
+ *          if you search for the sequence 'aa' in the data 'baaabbb',
+ *          you find one match at position 1.
+ * 
+ */ +L_DNA * +arrayFindEachSequence(const l_uint8 *data, + size_t datalen, + const l_uint8 *sequence, + size_t seqlen) +{ +l_int32 start, offset, realoffset, found; +L_DNA *da; + + PROCNAME("arrayFindEachSequence"); + + if (!data || !sequence) + return (L_DNA *)ERROR_PTR("data & sequence not both defined", + procName, NULL); + + da = l_dnaCreate(0); + start = 0; + while (1) { + arrayFindSequence(data + start, datalen - start, sequence, seqlen, + &offset, &found); + if (found == FALSE) + break; + + realoffset = start + offset; + l_dnaAddNumber(da, realoffset); + start = realoffset + seqlen; + if (start >= datalen) + break; + } + + if (l_dnaGetCount(da) == 0) + l_dnaDestroy(&da); + return da; +} + + +/*! + * \brief arrayFindSequence() + * + * \param[in] data byte array + * \param[in] datalen length of data, in bytes + * \param[in] sequence subarray of bytes to find in data + * \param[in] seqlen length of sequence, in bytes + * \param[out] poffset offset from beginning of + * data where the sequence begins + * \param[out] pfound 1 if sequence is found; 0 otherwise + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The byte arrays 'data' and 'sequence' are not C strings,
+ *          because they can contain null bytes.  Therefore, for each
+ *          we must give the length of the array.
+ *      (2) This searches for the first occurrence in %data of %sequence,
+ *          which consists of %seqlen bytes.  The parameter %seqlen
+ *          must not exceed the actual length of the %sequence byte array.
+ *      (3) If the sequence is not found, the offset will be 0, so you
+ *          must check %found.
+ * 
+ */ +l_ok +arrayFindSequence(const l_uint8 *data, + size_t datalen, + const l_uint8 *sequence, + size_t seqlen, + l_int32 *poffset, + l_int32 *pfound) +{ +l_int32 i, j, found, lastpos; + + PROCNAME("arrayFindSequence"); + + if (poffset) *poffset = 0; + if (pfound) *pfound = FALSE; + if (!data || !sequence) + return ERROR_INT("data & sequence not both defined", procName, 1); + if (!poffset || !pfound) + return ERROR_INT("&offset and &found not defined", procName, 1); + + lastpos = datalen - seqlen + 1; + found = FALSE; + for (i = 0; i < lastpos; i++) { + for (j = 0; j < seqlen; j++) { + if (data[i + j] != sequence[j]) + break; + if (j == seqlen - 1) + found = TRUE; + } + if (found == TRUE) + break; + } + + if (found == TRUE) { + *poffset = i; + *pfound = TRUE; + } + return 0; +} + + +/*--------------------------------------------------------------------* + * Safe realloc * + *--------------------------------------------------------------------*/ +/*! + * \brief reallocNew() + * + * \param[in,out] pindata nulls indata before reallocing + * \param[in] oldsize size of input data to be copied, in bytes + * \param[in] newsize size of buffer to be reallocated in bytes + * \return ptr to new data, or NULL on error + * + * Action: !N.B. 3) and (4! + * 1 Allocates memory, initialized to 0 + * 2 Copies as much of the input data as possible + * to the new block, truncating the copy if necessary + * 3 Frees the input data + * 4 Zeroes the input data ptr + * + *
+ * Notes:
+ *      (1) If newsize <=0, just frees input data and nulls ptr
+ *      (2) If input data is null, just callocs new memory
+ *      (3) This differs from realloc in that it always allocates
+ *          new memory (if newsize > 0) and initializes it to 0,
+ *          it requires the amount of old data to be copied,
+ *          and it takes the address of the input ptr and
+ *          nulls the handle.
+ * 
+ */ +void * +reallocNew(void **pindata, + l_int32 oldsize, + l_int32 newsize) +{ +l_int32 minsize; +void *indata; +void *newdata; + + PROCNAME("reallocNew"); + + if (!pindata) + return ERROR_PTR("input data not defined", procName, NULL); + indata = *pindata; + + if (newsize <= 0) { /* nonstandard usage */ + if (indata) { + LEPT_FREE(indata); + *pindata = NULL; + } + return NULL; + } + + if (!indata) { /* nonstandard usage */ + if ((newdata = (void *)LEPT_CALLOC(1, newsize)) == NULL) + return ERROR_PTR("newdata not made", procName, NULL); + return newdata; + } + + /* Standard usage */ + if ((newdata = (void *)LEPT_CALLOC(1, newsize)) == NULL) + return ERROR_PTR("newdata not made", procName, NULL); + minsize = L_MIN(oldsize, newsize); + memcpy(newdata, indata, minsize); + LEPT_FREE(indata); + *pindata = NULL; + + return newdata; +} + + +/*--------------------------------------------------------------------* + * Read and write between file and memory * + *--------------------------------------------------------------------*/ +/*! + * \brief l_binaryRead() + * + * \param[in] filename + * \param[out] pnbytes number of bytes read + * \return data, or NULL on error + */ +l_uint8 * +l_binaryRead(const char *filename, + size_t *pnbytes) +{ +l_uint8 *data; +FILE *fp; + + PROCNAME("l_binaryRead"); + + if (!pnbytes) + return (l_uint8 *)ERROR_PTR("pnbytes not defined", procName, NULL); + *pnbytes = 0; + if (!filename) + return (l_uint8 *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL); + data = l_binaryReadStream(fp, pnbytes); + fclose(fp); + return data; +} + + +/*! + * \brief l_binaryReadStream() + * + * \param[in] fp file stream opened to read; can be stdin + * \param[out] pnbytes number of bytes read + * \return null-terminated array, or NULL on error; reading 0 bytes + * is not an error + * + *
+ * Notes:
+ *      (1) The returned array is terminated with a null byte so that it can
+ *          be used to read ascii data from a file into a proper C string.
+ *      (2) This can be used to capture data that is piped in via stdin,
+ *          because it does not require seeking within the file.
+ *      (3) For example, you can read an image from stdin into memory
+ *          using shell redirection, with one of these shell commands:
+ * \code
+ *             cat  | readprog
+ *             readprog < 
+ * \endcode
+ *          where readprog is:
+ * \code
+ *             l_uint8 *data = l_binaryReadStream(stdin, &nbytes);
+ *             Pix *pix = pixReadMem(data, nbytes);
+ * \endcode
+ * 
+ */ +l_uint8 * +l_binaryReadStream(FILE *fp, + size_t *pnbytes) +{ +l_uint8 *data; +l_int32 seekable, navail, nadd, nread; +L_BBUFFER *bb; + + PROCNAME("l_binaryReadStream"); + + if (!pnbytes) + return (l_uint8 *)ERROR_PTR("&nbytes not defined", procName, NULL); + *pnbytes = 0; + if (!fp) + return (l_uint8 *)ERROR_PTR("fp not defined", procName, NULL); + + /* Test if the stream is seekable, by attempting to seek to + * the start of data. This is a no-op. If it is seekable, use + * l_binaryReadSelectStream() to determine the size of the + * data to be read in advance. */ + seekable = (ftell(fp) == 0) ? 1 : 0; + if (seekable) + return l_binaryReadSelectStream(fp, 0, 0, pnbytes); + + /* If it is not seekable, use the bbuffer to realloc memory + * as needed during reading. */ + bb = bbufferCreate(NULL, 4096); + while (1) { + navail = bb->nalloc - bb->n; + if (navail < 4096) { + nadd = L_MAX(bb->nalloc, 4096); + bbufferExtendArray(bb, nadd); + } + nread = fread((void *)(bb->array + bb->n), 1, 4096, fp); + bb->n += nread; + if (nread != 4096) break; + } + + /* Copy the data to a new array sized for the data, because + * the bbuffer array can be nearly twice the size we need. */ + if ((data = (l_uint8 *)LEPT_CALLOC(bb->n + 1, sizeof(l_uint8))) != NULL) { + memcpy(data, bb->array, bb->n); + *pnbytes = bb->n; + } else { + L_ERROR("calloc fail for data\n", procName); + } + + bbufferDestroy(&bb); + return data; +} + + +/*! + * \brief l_binaryReadSelect() + * + * \param[in] filename + * \param[in] start first byte to read + * \param[in] nbytes number of bytes to read; use 0 to read to end of file + * \param[out] pnread number of bytes actually read + * \return data, or NULL on error + * + *
+ * Notes:
+ *      (1) The returned array is terminated with a null byte so that it can
+ *          be used to read ascii data from a file into a proper C string.
+ * 
+ */ +l_uint8 * +l_binaryReadSelect(const char *filename, + size_t start, + size_t nbytes, + size_t *pnread) +{ +l_uint8 *data; +FILE *fp; + + PROCNAME("l_binaryReadSelect"); + + if (!pnread) + return (l_uint8 *)ERROR_PTR("pnread not defined", procName, NULL); + *pnread = 0; + if (!filename) + return (l_uint8 *)ERROR_PTR("filename not defined", procName, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL); + data = l_binaryReadSelectStream(fp, start, nbytes, pnread); + fclose(fp); + return data; +} + + +/*! + * \brief l_binaryReadSelectStream() + * + * \param[in] fp file stream + * \param[in] start first byte to read + * \param[in] nbytes number of bytes to read; use 0 to read to end of file + * \param[out] pnread number of bytes actually read + * \return null-terminated array, or NULL on error; reading 0 bytes + * is not an error + * + *
+ * Notes:
+ *      (1) The returned array is terminated with a null byte so that it can
+ *          be used to read ascii data from a file into a proper C string.
+ *          If the file to be read is empty and %start == 0, an array
+ *          with a single null byte is returned.
+ *      (2) Side effect: the stream pointer is re-positioned to the
+ *          beginning of the file.
+ * 
+ */ +l_uint8 * +l_binaryReadSelectStream(FILE *fp, + size_t start, + size_t nbytes, + size_t *pnread) +{ +l_uint8 *data; +size_t bytesleft, bytestoread, nread, filebytes; + + PROCNAME("l_binaryReadSelectStream"); + + if (!pnread) + return (l_uint8 *)ERROR_PTR("&nread not defined", procName, NULL); + *pnread = 0; + if (!fp) + return (l_uint8 *)ERROR_PTR("stream not defined", procName, NULL); + + /* Verify and adjust the parameters if necessary */ + fseek(fp, 0, SEEK_END); /* EOF */ + filebytes = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (start > filebytes) { + L_ERROR("start = %zu but filebytes = %zu\n", procName, + start, filebytes); + return NULL; + } + if (filebytes == 0) /* start == 0; nothing to read; return null byte */ + return (l_uint8 *)LEPT_CALLOC(1, 1); + bytesleft = filebytes - start; /* greater than 0 */ + if (nbytes == 0) nbytes = bytesleft; + bytestoread = (bytesleft >= nbytes) ? nbytes : bytesleft; + + /* Read the data */ + if ((data = (l_uint8 *)LEPT_CALLOC(1, bytestoread + 1)) == NULL) + return (l_uint8 *)ERROR_PTR("calloc fail for data", procName, NULL); + fseek(fp, start, SEEK_SET); + nread = fread(data, 1, bytestoread, fp); + if (nbytes != nread) + L_INFO("%zu bytes requested; %zu bytes read\n", procName, + nbytes, nread); + *pnread = nread; + fseek(fp, 0, SEEK_SET); + return data; +} + + +/*! + * \brief l_binaryWrite() + * + * \param[in] filename output file + * \param[in] operation "w" for write; "a" for append + * \param[in] data binary data to be written + * \param[in] nbytes size of data array + * \return 0 if OK; 1 on error + */ +l_ok +l_binaryWrite(const char *filename, + const char *operation, + const void *data, + size_t nbytes) +{ +char actualOperation[20]; +FILE *fp; + + PROCNAME("l_binaryWrite"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!operation) + return ERROR_INT("operation not defined", procName, 1); + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (nbytes <= 0) + return ERROR_INT("nbytes must be > 0", procName, 1); + + if (strcmp(operation, "w") && strcmp(operation, "a")) + return ERROR_INT("operation not one of {'w','a'}", procName, 1); + + /* The 'b' flag to fopen() is ignored for all POSIX + * conforming systems. However, Windows needs the 'b' flag. */ + stringCopy(actualOperation, operation, 2); + strncat(actualOperation, "b", 2); + + if ((fp = fopenWriteStream(filename, actualOperation)) == NULL) + return ERROR_INT("stream not opened", procName, 1); + fwrite(data, 1, nbytes, fp); + fclose(fp); + return 0; +} + + +/*! + * \brief nbytesInFile() + * + * \param[in] filename + * \return nbytes in file; 0 on error + */ +size_t +nbytesInFile(const char *filename) +{ +size_t nbytes; +FILE *fp; + + PROCNAME("nbytesInFile"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 0); + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("stream not opened", procName, 0); + nbytes = fnbytesInFile(fp); + fclose(fp); + return nbytes; +} + + +/*! + * \brief fnbytesInFile() + * + * \param[in] fp file stream + * \return nbytes in file; 0 on error + */ +size_t +fnbytesInFile(FILE *fp) +{ +l_int64 pos, nbytes; + + PROCNAME("fnbytesInFile"); + + if (!fp) + return ERROR_INT("stream not open", procName, 0); + + pos = ftell(fp); /* initial position */ + if (pos < 0) + return ERROR_INT("seek position must be > 0", procName, 0); + fseek(fp, 0, SEEK_END); /* EOF */ + nbytes = ftell(fp); + fseek(fp, pos, SEEK_SET); /* back to initial position */ + return nbytes; +} + + +/*--------------------------------------------------------------------* + * Copy and compare in memory * + *--------------------------------------------------------------------*/ +/*! + * \brief l_binaryCopy() + * + * \param[in] datas + * \param[in] size of data array + * \return datad on heap, or NULL on error + * + *
+ * Notes:
+ *      (1) We add 4 bytes to the zeroed output because in some cases
+ *          (e.g., string handling) it is important to have the data
+ *          be null terminated.  This guarantees that after the memcpy,
+ *          the result is automatically null terminated.
+ * 
+ */ +l_uint8 * +l_binaryCopy(const l_uint8 *datas, + size_t size) +{ +l_uint8 *datad; + + PROCNAME("l_binaryCopy"); + + if (!datas) + return (l_uint8 *)ERROR_PTR("datas not defined", procName, NULL); + + if ((datad = (l_uint8 *)LEPT_CALLOC(size + 4, sizeof(l_uint8))) == NULL) + return (l_uint8 *)ERROR_PTR("datad not made", procName, NULL); + memcpy(datad, datas, size); + return datad; +} + + +l_ok +l_binaryCompare(const l_uint8 *data1, + size_t size1, + const l_uint8 *data2, + size_t size2, + l_int32 *psame) +{ +l_int32 i; + + PROCNAME("l_binaryCompare"); + + if (!psame) + return ERROR_INT("&same not defined", procName, 1); + *psame = FALSE; + if (!data1 || !data2) + return ERROR_INT("data1 and data2 not both defined", procName, 1); + if (size1 != size2) return 0; + for (i = 0; i < size1; i++) { + if (data1[i] != data2[i]) + return 0; + } + *psame = TRUE; + return 0; +} + +/*--------------------------------------------------------------------* + * File copy operations * + *--------------------------------------------------------------------*/ +/*! + * \brief fileCopy() + * + * \param[in] srcfile copy from this file + * \param[in] newfile copy to this file + * \return 0 if OK, 1 on error + */ +l_ok +fileCopy(const char *srcfile, + const char *newfile) +{ +l_int32 ret; +size_t nbytes; +l_uint8 *data; + + PROCNAME("fileCopy"); + + if (!srcfile) + return ERROR_INT("srcfile not defined", procName, 1); + if (!newfile) + return ERROR_INT("newfile not defined", procName, 1); + + if ((data = l_binaryRead(srcfile, &nbytes)) == NULL) + return ERROR_INT("data not returned", procName, 1); + ret = l_binaryWrite(newfile, "w", data, nbytes); + LEPT_FREE(data); + return ret; +} + + +/*! + * \brief fileConcatenate() + * + * \param[in] srcfile append data from this file + * \param[in] destfile add data to this file + * \return 0 if OK, 1 on error + */ +l_ok +fileConcatenate(const char *srcfile, + const char *destfile) +{ +size_t nbytes; +l_uint8 *data; + + PROCNAME("fileConcatenate"); + + if (!srcfile) + return ERROR_INT("srcfile not defined", procName, 1); + if (!destfile) + return ERROR_INT("destfile not defined", procName, 1); + + data = l_binaryRead(srcfile, &nbytes); + l_binaryWrite(destfile, "a", data, nbytes); + LEPT_FREE(data); + return 0; +} + + +/*! + * \brief fileAppendString() + * + * \param[in] filename + * \param[in] str string to append to file + * \return 0 if OK, 1 on error + */ +l_ok +fileAppendString(const char *filename, + const char *str) +{ +FILE *fp; + + PROCNAME("fileAppendString"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!str) + return ERROR_INT("str not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "a")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + fprintf(fp, "%s", str); + fclose(fp); + return 0; +} + + +/*--------------------------------------------------------------------* + * Multi-platform functions for opening file streams * + *--------------------------------------------------------------------*/ +/*! + * \brief fopenReadStream() + * + * \param[in] filename + * \return stream, or NULL on error + * + *
+ * Notes:
+ *      (1) This should be used whenever you want to run fopen() to
+ *          read from a stream.  Never call fopen() directory.
+ *      (2) This handles the temp directory pathname conversion on windows:
+ *              /tmp  ==>  [Windows Temp directory]
+ * 
+ */ +FILE * +fopenReadStream(const char *filename) +{ +char *fname, *tail; +FILE *fp; + + PROCNAME("fopenReadStream"); + + if (!filename) + return (FILE *)ERROR_PTR("filename not defined", procName, NULL); + + /* Try input filename */ + fname = genPathname(filename, NULL); + fp = fopen(fname, "rb"); + LEPT_FREE(fname); + if (fp) return fp; + + /* Else, strip directory and try locally */ + splitPathAtDirectory(filename, NULL, &tail); + fp = fopen(tail, "rb"); + LEPT_FREE(tail); + + if (!fp) + return (FILE *)ERROR_PTR("file not found", procName, NULL); + return fp; +} + + +/*! + * \brief fopenWriteStream() + * + * \param[in] filename + * \param[in] modestring + * \return stream, or NULL on error + * + *
+ * Notes:
+ *      (1) This should be used whenever you want to run fopen() to
+ *          write or append to a stream.  Never call fopen() directory.
+ *      (2) This handles the temp directory pathname conversion on windows:
+ *              /tmp  ==>  [Windows Temp directory]
+ * 
+ */ +FILE * +fopenWriteStream(const char *filename, + const char *modestring) +{ +char *fname; +FILE *fp; + + PROCNAME("fopenWriteStream"); + + if (!filename) + return (FILE *)ERROR_PTR("filename not defined", procName, NULL); + + fname = genPathname(filename, NULL); + fp = fopen(fname, modestring); + LEPT_FREE(fname); + if (!fp) + return (FILE *)ERROR_PTR("stream not opened", procName, NULL); + return fp; +} + + +/*! + * \brief fopenReadFromMemory() + * + * \param[in] data, size + * \return file stream, or NULL on error + * + *
+ * Notes:
+ *      (1) Work-around if fmemopen() not available.
+ *      (2) Windows tmpfile() writes into the root C:\ directory, which
+ *          requires admin privileges.  This also works around that.
+ * 
+ */ +FILE * +fopenReadFromMemory(const l_uint8 *data, + size_t size) +{ +FILE *fp; + + PROCNAME("fopenReadFromMemory"); + + if (!data) + return (FILE *)ERROR_PTR("data not defined", procName, NULL); + +#if HAVE_FMEMOPEN + if ((fp = fmemopen((void *)data, size, "rb")) == NULL) + return (FILE *)ERROR_PTR("stream not opened", procName, NULL); +#else /* write to tmp file */ + L_INFO("work-around: writing to a temp file\n", procName); + #ifdef _WIN32 + if ((fp = fopenWriteWinTempfile()) == NULL) + return (FILE *)ERROR_PTR("tmpfile stream not opened", procName, NULL); + #else + if ((fp = tmpfile()) == NULL) + return (FILE *)ERROR_PTR("tmpfile stream not opened", procName, NULL); + #endif /* _WIN32 */ + fwrite(data, 1, size, fp); + rewind(fp); +#endif /* HAVE_FMEMOPEN */ + + return fp; +} + + +/*--------------------------------------------------------------------* + * Opening a windows tmpfile for writing * + *--------------------------------------------------------------------*/ +/*! + * \brief fopenWriteWinTempfile() + * + * \return file stream, or NULL on error + * + *
+ * Notes:
+ *      (1) The Windows version of tmpfile() writes into the root
+ *          C:\ directory, which requires admin privileges.  This
+ *          function provides an alternative implementation.
+ * 
+ */ +FILE * +fopenWriteWinTempfile() +{ +#ifdef _WIN32 +l_int32 handle; +FILE *fp; +char *filename; + + PROCNAME("fopenWriteWinTempfile"); + + if ((filename = l_makeTempFilename()) == NULL) { + L_ERROR("l_makeTempFilename failed, %s\n", procName, strerror(errno)); + return NULL; + } + + handle = _open(filename, _O_CREAT | _O_RDWR | _O_SHORT_LIVED | + _O_TEMPORARY | _O_BINARY, _S_IREAD | _S_IWRITE); + lept_free(filename); + if (handle == -1) { + L_ERROR("_open failed, %s\n", procName, strerror(errno)); + return NULL; + } + + if ((fp = _fdopen(handle, "r+b")) == NULL) { + L_ERROR("_fdopen failed, %s\n", procName, strerror(errno)); + return NULL; + } + + return fp; +#else + return NULL; +#endif /* _WIN32 */ +} + + +/*--------------------------------------------------------------------* + * Multi-platform functions that avoid C-runtime boundary * + * crossing for applications with Windows DLLs * + *--------------------------------------------------------------------*/ +/* + * Problems arise when pointers to streams and data are passed + * between two Windows DLLs that have been generated with different + * C runtimes. To avoid this, leptonica provides wrappers for + * several C library calls. + */ +/*! + * \brief lept_fopen() + * + * \param[in] filename + * \param[in] mode same as for fopen(); e.g., "rb" + * \return stream or NULL on error + * + *
+ * Notes:
+ *      (1) This must be used by any application that passes
+ *          a file handle to a leptonica Windows DLL.
+ * 
+ */ +FILE * +lept_fopen(const char *filename, + const char *mode) +{ + PROCNAME("lept_fopen"); + + if (!filename) + return (FILE *)ERROR_PTR("filename not defined", procName, NULL); + if (!mode) + return (FILE *)ERROR_PTR("mode not defined", procName, NULL); + + if (stringFindSubstr(mode, "r", NULL)) + return fopenReadStream(filename); + else + return fopenWriteStream(filename, mode); +} + + +/*! + * \brief lept_fclose() + * + * \param[in] fp file stream + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This should be used by any application that accepts
+ *          a file handle generated by a leptonica Windows DLL.
+ * 
+ */ +l_ok +lept_fclose(FILE *fp) +{ + PROCNAME("lept_fclose"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + + return fclose(fp); +} + + +/*! + * \brief lept_calloc() + * + * \param[in] nmemb number of members + * \param[in] size of each member + * \return void ptr, or NULL on error + * + *
+ * Notes:
+ *      (1) For safety with windows DLLs, this can be used in conjunction
+ *          with lept_free() to avoid C-runtime boundary problems.
+ *          Just use these two functions throughout your application.
+ * 
+ */ +void * +lept_calloc(size_t nmemb, + size_t size) +{ + if (nmemb <= 0 || size <= 0) + return NULL; + return LEPT_CALLOC(nmemb, size); +} + + +/*! + * \brief lept_free() + * + * \param[in] ptr + * + *
+ * Notes:
+ *      (1) This should be used by any application that accepts
+ *          heap data allocated by a leptonica Windows DLL.
+ * 
+ */ +void +lept_free(void *ptr) +{ + if (!ptr) return; + LEPT_FREE(ptr); + return; +} + + +/*--------------------------------------------------------------------* + * Multi-platform file system operations * + * [ These only write to /tmp or its subdirectories ] * + *--------------------------------------------------------------------*/ +/*! + * \brief lept_mkdir() + * + * \param[in] subdir of /tmp or its equivalent on Windows + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) %subdir is a partial path that can consist of one or more
+ *          directories.
+ *      (2) This makes any subdirectories of /tmp that are required.
+ *      (3) The root temp directory is:
+ *            /tmp    (unix)  [default]
+ *            [Temp]  (windows)
+ * 
+ */ +l_int32 +lept_mkdir(const char *subdir) +{ +char *dir, *tmpdir; +l_int32 i, n; +l_int32 ret = 0; +SARRAY *sa; +#ifdef _WIN32 +l_uint32 attributes; +#endif /* _WIN32 */ + + PROCNAME("lept_mkdir"); + + if (!LeptDebugOK) { + L_INFO("making named temp subdirectory %s is disabled\n", + procName, subdir); + return 0; + } + + if (!subdir) + return ERROR_INT("subdir not defined", procName, 1); + if ((strlen(subdir) == 0) || (subdir[0] == '.') || (subdir[0] == '/')) + return ERROR_INT("subdir not an actual subdirectory", procName, 1); + + sa = sarrayCreate(0); + sarraySplitString(sa, subdir, "/"); + n = sarrayGetCount(sa); + dir = genPathname("/tmp", NULL); + /* Make sure the tmp directory exists */ +#ifndef _WIN32 + ret = mkdir(dir, 0777); +#else + attributes = GetFileAttributes(dir); + if (attributes == INVALID_FILE_ATTRIBUTES) + ret = (CreateDirectory(dir, NULL) ? 0 : 1); +#endif + /* Make all the subdirectories */ + for (i = 0; i < n; i++) { + tmpdir = pathJoin(dir, sarrayGetString(sa, i, L_NOCOPY)); +#ifndef _WIN32 + ret += mkdir(tmpdir, 0777); +#else + if (CreateDirectory(tmpdir, NULL) == 0) + ret += (GetLastError () != ERROR_ALREADY_EXISTS); +#endif + LEPT_FREE(dir); + dir = tmpdir; + } + LEPT_FREE(dir); + sarrayDestroy(&sa); + if (ret > 0) + L_ERROR("failure to create %d directories\n", procName, ret); + return ret; +} + + +/*! + * \brief lept_rmdir() + * + * \param[in] subdir of /tmp or its equivalent on Windows + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) %subdir is a partial path that can consist of one or more
+ *          directories.
+ *      (2) This removes all files from the specified subdirectory of
+ *          the root temp directory:
+ *            /tmp    (unix)
+ *            [Temp]  (windows)
+ *          and then removes the subdirectory.
+ *      (3) The combination
+ *            lept_rmdir(subdir);
+ *            lept_mkdir(subdir);
+ *          is guaranteed to give you an empty subdirectory.
+ * 
+ */ +l_int32 +lept_rmdir(const char *subdir) +{ +char *dir, *realdir, *fname, *fullname; +l_int32 exists, ret, i, nfiles; +SARRAY *sa; +#ifdef _WIN32 +char *newpath; +#endif /* _WIN32 */ + + PROCNAME("lept_rmdir"); + + if (!subdir) + return ERROR_INT("subdir not defined", procName, 1); + if ((strlen(subdir) == 0) || (subdir[0] == '.') || (subdir[0] == '/')) + return ERROR_INT("subdir not an actual subdirectory", procName, 1); + + /* Find the temp subdirectory */ + dir = pathJoin("/tmp", subdir); + if (!dir) + return ERROR_INT("directory name not made", procName, 1); + lept_direxists(dir, &exists); + if (!exists) { /* fail silently */ + LEPT_FREE(dir); + return 0; + } + + /* List all the files in that directory */ + if ((sa = getFilenamesInDirectory(dir)) == NULL) { + L_ERROR("directory %s does not exist!\n", procName, dir); + LEPT_FREE(dir); + return 1; + } + nfiles = sarrayGetCount(sa); + + for (i = 0; i < nfiles; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + fullname = genPathname(dir, fname); + remove(fullname); + LEPT_FREE(fullname); + } + +#ifndef _WIN32 + realdir = genPathname("/tmp", subdir); + ret = rmdir(realdir); + LEPT_FREE(realdir); +#else + newpath = genPathname(dir, NULL); + ret = (RemoveDirectory(newpath) ? 0 : 1); + LEPT_FREE(newpath); +#endif /* !_WIN32 */ + + sarrayDestroy(&sa); + LEPT_FREE(dir); + return ret; +} + + +/*! + * \brief lept_direxists() + * + * \param[in] dir + * \param[out] pexists 1 if it exists; 0 otherwise + * \return void + * + *
+ * Notes:
+ *      (1) Always use unix pathname separators.
+ *      (2) By calling genPathname(), if the pathname begins with "/tmp"
+ *          this does an automatic directory translation on windows
+ *          to a path in the windows [Temp] directory:
+ *             "/tmp"  ==>  [Temp] (windows)
+ * 
+ */ +void +lept_direxists(const char *dir, + l_int32 *pexists) +{ +char *realdir; + + if (!pexists) return; + *pexists = 0; + if (!dir) return; + if ((realdir = genPathname(dir, NULL)) == NULL) + return; + +#ifndef _WIN32 + { + struct stat s; + l_int32 err = stat(realdir, &s); + if (err != -1 && S_ISDIR(s.st_mode)) + *pexists = 1; + } +#else /* _WIN32 */ + l_uint32 attributes; + attributes = GetFileAttributes(realdir); + if (attributes != INVALID_FILE_ATTRIBUTES && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + *pexists = 1; + } +#endif /* _WIN32 */ + + LEPT_FREE(realdir); + return; +} + + +/*! + * \brief lept_rm_match() + * + * \param[in] subdir [optional] if NULL, the removed files are in /tmp + * \param[in] substr [optional] pattern to match in filename + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) This removes the matched files in /tmp or a subdirectory of /tmp.
+ *          Use NULL for %subdir if the files are in /tmp.
+ *      (2) If %substr == NULL, this removes all files in the directory.
+ *          If %substr == "" (empty), this removes no files.
+ *          If both %subdir == NULL and %substr == NULL, this removes
+ *          all files in /tmp.
+ *      (3) Use unix pathname separators.
+ *      (4) By calling genPathname(), if the pathname begins with "/tmp"
+ *          this does an automatic directory translation on windows
+ *          to a path in the windows [Temp] directory:
+ *             "/tmp"  ==>  [Temp] (windows)
+ *      (5) Error conditions:
+ *            * returns -1 if the directory is not found
+ *            * returns the number of files (> 0) that it was unable to remove.
+ * 
+ */ +l_int32 +lept_rm_match(const char *subdir, + const char *substr) +{ +char *path, *fname; +char tempdir[256]; +l_int32 i, n, ret; +SARRAY *sa; + + PROCNAME("lept_rm_match"); + + makeTempDirname(tempdir, sizeof(tempdir), subdir); + if ((sa = getSortedPathnamesInDirectory(tempdir, substr, 0, 0)) == NULL) + return ERROR_INT("sa not made", procName, -1); + n = sarrayGetCount(sa); + if (n == 0) { + L_WARNING("no matching files found\n", procName); + sarrayDestroy(&sa); + return 0; + } + + ret = 0; + for (i = 0; i < n; i++) { + fname = sarrayGetString(sa, i, L_NOCOPY); + path = genPathname(fname, NULL); + if (lept_rmfile(path) != 0) { + L_ERROR("failed to remove %s\n", procName, path); + ret++; + } + LEPT_FREE(path); + } + sarrayDestroy(&sa); + return ret; +} + + +/*! + * \brief lept_rm() + * + * \param[in] subdir [optional] subdir of '/tmp'; can be NULL + * \param[in] tail filename without the directory + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) By calling genPathname(), this does an automatic directory
+ *          translation on windows to a path in the windows [Temp] directory:
+ *             "/tmp/..."  ==>  [Temp]/... (windows)
+ * 
+ */ +l_int32 +lept_rm(const char *subdir, + const char *tail) +{ +char *path; +char newtemp[256]; +l_int32 ret; + + PROCNAME("lept_rm"); + + if (!tail || strlen(tail) == 0) + return ERROR_INT("tail undefined or empty", procName, 1); + + if (makeTempDirname(newtemp, sizeof(newtemp), subdir)) + return ERROR_INT("temp dirname not made", procName, 1); + path = genPathname(newtemp, tail); + ret = lept_rmfile(path); + LEPT_FREE(path); + return ret; +} + + +/*! + * \brief + * + * lept_rmfile() + * + * \param[in] filepath full path to file including the directory + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) This removes the named file.
+ *      (2) Use unix pathname separators.
+ *      (3) There is no name translation.
+ *      (4) Unlike the other lept_* functions in this section, this can remove
+ *          any file -- it is not restricted to files that are in /tmp or a
+ *          subdirectory of it.
+ * 
+ */ +l_int32 +lept_rmfile(const char *filepath) +{ +l_int32 ret; + + PROCNAME("lept_rmfile"); + + if (!filepath || strlen(filepath) == 0) + return ERROR_INT("filepath undefined or empty", procName, 1); + +#ifndef _WIN32 + ret = remove(filepath); +#else + /* Set attributes to allow deletion of read-only files */ + SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL); + ret = DeleteFile(filepath) ? 0 : 1; +#endif /* !_WIN32 */ + + return ret; +} + + +/*! + * \brief lept_mv() + * + * \param[in] srcfile + * \param[in] newdir [optional]; can be NULL + * \param[in] newtail [optional]; can be NULL + * \param[out] pnewpath [optional] of actual path; can be NULL + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) This moves %srcfile to /tmp or to a subdirectory of /tmp.
+ *      (2) %srcfile can either be a full path or relative to the
+ *          current directory.
+ *      (3) %newdir can either specify an existing subdirectory of /tmp
+ *          or can be NULL.  In the latter case, the file will be written
+ *          into /tmp.
+ *      (4) %newtail can either specify a filename tail or, if NULL,
+ *          the filename is taken from src-tail, the tail of %srcfile.
+ *      (5) For debugging, the computed newpath can be returned.  It must
+ *          be freed by the caller.
+ *      (6) Reminders:
+ *          (a) specify files using unix pathnames
+ *          (b) for windows, translates
+ *                 /tmp  ==>  [Temp]
+ *              where [Temp] is the windows temp directory
+ *      (7) Examples:
+ *          * newdir = NULL,    newtail = NULL    ==> /tmp/src-tail
+ *          * newdir = NULL,    newtail = abc     ==> /tmp/abc
+ *          * newdir = def/ghi, newtail = NULL    ==> /tmp/def/ghi/src-tail
+ *          * newdir = def/ghi, newtail = abc     ==> /tmp/def/ghi/abc
+ * 
+ */ +l_int32 +lept_mv(const char *srcfile, + const char *newdir, + const char *newtail, + char **pnewpath) +{ +char *srcpath, *newpath, *dir, *srctail; +char newtemp[256]; +l_int32 ret; + + PROCNAME("lept_mv"); + + if (!srcfile) + return ERROR_INT("srcfile not defined", procName, 1); + + /* Require output pathname to be in /tmp/ or a subdirectory */ + if (makeTempDirname(newtemp, sizeof(newtemp), newdir) == 1) + return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1); + + /* Get canonical src pathname */ + splitPathAtDirectory(srcfile, &dir, &srctail); + +#ifndef _WIN32 + srcpath = pathJoin(dir, srctail); + LEPT_FREE(dir); + + /* Generate output pathname */ + if (!newtail || newtail[0] == '\0') + newpath = pathJoin(newtemp, srctail); + else + newpath = pathJoin(newtemp, newtail); + LEPT_FREE(srctail); + + /* Overwrite any existing file at 'newpath' */ + ret = fileCopy(srcpath, newpath); + if (!ret) { /* and remove srcfile */ + char *realpath = genPathname(srcpath, NULL); + remove(realpath); + LEPT_FREE(realpath); + } +#else + srcpath = genPathname(dir, srctail); + LEPT_FREE(dir); + + /* Generate output pathname */ + if (!newtail || newtail[0] == '\0') + newpath = genPathname(newtemp, srctail); + else + newpath = genPathname(newtemp, newtail); + LEPT_FREE(srctail); + + /* Overwrite any existing file at 'newpath' */ + ret = MoveFileEx(srcpath, newpath, + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) ? 0 : 1; +#endif /* ! _WIN32 */ + + LEPT_FREE(srcpath); + if (pnewpath) + *pnewpath = newpath; + else + LEPT_FREE(newpath); + return ret; +} + + +/*! + * \brief lept_cp() + * + * \param[in] srcfile + * \param[in] newdir [optional]; can be NULL + * \param[in] newtail [optional]; can be NULL + * \param[out] pnewpath [optional] of actual path; can be NULL + * \return 0 on success, non-zero on failure + * + *
+ * Notes:
+ *      (1) This copies %srcfile to /tmp or to a subdirectory of /tmp.
+ *      (2) %srcfile can either be a full path or relative to the
+ *          current directory.
+ *      (3) %newdir can either specify an existing subdirectory of /tmp,
+ *          or can be NULL.  In the latter case, the file will be written
+ *          into /tmp.
+ *      (4) %newtail can either specify a filename tail or, if NULL,
+ *          the filename is taken from src-tail, the tail of %srcfile.
+ *      (5) For debugging, the computed newpath can be returned.  It must
+ *          be freed by the caller.
+ *      (6) Reminders:
+ *          (a) specify files using unix pathnames
+ *          (b) for windows, translates
+ *                 /tmp  ==>  [Temp]
+ *              where [Temp] is the windows temp directory
+ *      (7) Examples:
+ *          * newdir = NULL,    newtail = NULL    ==> /tmp/src-tail
+ *          * newdir = NULL,    newtail = abc     ==> /tmp/abc
+ *          * newdir = def/ghi, newtail = NULL    ==> /tmp/def/ghi/src-tail
+ *          * newdir = def/ghi, newtail = abc     ==> /tmp/def/ghi/abc
+ *
+ * 
+ */ +l_int32 +lept_cp(const char *srcfile, + const char *newdir, + const char *newtail, + char **pnewpath) +{ +char *srcpath, *newpath, *dir, *srctail; +char newtemp[256]; +l_int32 ret; + + PROCNAME("lept_cp"); + + if (!srcfile) + return ERROR_INT("srcfile not defined", procName, 1); + + /* Require output pathname to be in /tmp or a subdirectory */ + if (makeTempDirname(newtemp, sizeof(newtemp), newdir) == 1) + return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1); + + /* Get canonical src pathname */ + splitPathAtDirectory(srcfile, &dir, &srctail); + +#ifndef _WIN32 + srcpath = pathJoin(dir, srctail); + LEPT_FREE(dir); + + /* Generate output pathname */ + if (!newtail || newtail[0] == '\0') + newpath = pathJoin(newtemp, srctail); + else + newpath = pathJoin(newtemp, newtail); + LEPT_FREE(srctail); + + /* Overwrite any existing file at 'newpath' */ + ret = fileCopy(srcpath, newpath); +#else + srcpath = genPathname(dir, srctail); + LEPT_FREE(dir); + + /* Generate output pathname */ + if (!newtail || newtail[0] == '\0') + newpath = genPathname(newtemp, srctail); + else + newpath = genPathname(newtemp, newtail); + LEPT_FREE(srctail); + + /* Overwrite any existing file at 'newpath' */ + ret = CopyFile(srcpath, newpath, FALSE) ? 0 : 1; +#endif /* !_WIN32 */ + + LEPT_FREE(srcpath); + if (pnewpath) + *pnewpath = newpath; + else + LEPT_FREE(newpath); + return ret; +} + + +/*--------------------------------------------------------------------* + * Special debug/test function for calling 'system' * + *--------------------------------------------------------------------*/ +#if defined(__APPLE__) + #include "TargetConditionals.h" +#endif /* __APPLE__ */ + +/*! + * \brief callSystemDebug() + * + * \param[in] cmd command to be exec'd + * \return void + * + *
+ * Notes:
+ *      (1) The C library 'system' call is only made through this function.
+ *          It only works in debug/test mode, where the global variable
+ *          LeptDebugOK == TRUE.  This variable is set to FALSE in the
+ *          library as distributed, and calling this function will
+ *          generate an error message.
+ * 
+ */ +void +callSystemDebug(const char *cmd) +{ +l_int32 ret; + + PROCNAME("callSystemDebug"); + + if (!cmd) { + L_ERROR("cmd not defined\n", procName); + return; + } + if (LeptDebugOK == FALSE) { + L_INFO("'system' calls are disabled\n", procName); + return; + } + +#if defined(__APPLE__) /* iOS 11 does not support system() */ + + #if TARGET_OS_OSX /* Mac OS X */ + ret = system(cmd); + #elif TARGET_OS_IPHONE || defined(OS_IOS) /* iOS */ + L_ERROR("iOS 11 does not support system()\n", procName); + #endif /* TARGET_OS_OSX */ + +#else /* ! __APPLE__ */ + + ret = system(cmd); + +#endif /* __APPLE__ */ +} + + +/*--------------------------------------------------------------------* + * General file name operations * + *--------------------------------------------------------------------*/ +/*! + * \brief splitPathAtDirectory() + * + * \param[in] pathname full path; can be a directory + * \param[out] pdir [optional] root directory name of + * input path, including trailing '/' + * \param[out] ptail [optional] path tail, which is either + * the file name within the root directory or + * the last sub-directory in the path + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If you only want the tail, input null for the root directory ptr.
+ *      (2) If you only want the root directory name, input null for the
+ *          tail ptr.
+ *      (3) This function makes decisions based only on the lexical
+ *          structure of the input.  Examples:
+ *            /usr/tmp/abc.d  -->  dir: /usr/tmp/       tail: abc.d
+ *            /usr/tmp/       -->  dir: /usr/tmp/       tail: [empty string]
+ *            /usr/tmp        -->  dir: /usr/           tail: tmp
+ *            abc.d           -->  dir: [empty string]  tail: abc.d
+ *      (4  Consider the first example above: /usr/tmp/abc.d.
+ *          Suppose you want the stem of the file, abc, without either
+ *          the directory or the extension.  This can be extracted in two steps:
+ *              splitPathAtDirectory("usr/tmp/abc.d", NULL, &tail);
+ *                   [sets tail: "abc.d"]
+ *              splitPathAtExtension(tail, &basename, NULL);
+ *                   [sets basename: "abc"]
+ *      (5) The input can have either forward (unix) or backward (win)
+ *          slash separators.  The output has unix separators.
+ *          Note that Win32 pathname functions generally accept both
+ *          slash forms, but the windows command line interpreter
+ *          only accepts backward slashes, because forward slashes are
+ *          used to demarcate switches (vs. dashes in unix).
+ * 
+ */ +l_ok +splitPathAtDirectory(const char *pathname, + char **pdir, + char **ptail) +{ +char *cpathname, *lastslash; + + PROCNAME("splitPathAtDirectory"); + + if (!pdir && !ptail) + return ERROR_INT("null input for both strings", procName, 1); + if (pdir) *pdir = NULL; + if (ptail) *ptail = NULL; + if (!pathname) + return ERROR_INT("pathname not defined", procName, 1); + + cpathname = stringNew(pathname); + convertSepCharsInPath(cpathname, UNIX_PATH_SEPCHAR); + lastslash = strrchr(cpathname, '/'); + if (lastslash) { + if (ptail) + *ptail = stringNew(lastslash + 1); + if (pdir) { + *(lastslash + 1) = '\0'; + *pdir = cpathname; + } else { + LEPT_FREE(cpathname); + } + } else { /* no directory */ + if (pdir) + *pdir = stringNew(""); + if (ptail) + *ptail = cpathname; + else + LEPT_FREE(cpathname); + } + + return 0; +} + + +/*! + * \brief splitPathAtExtension() + * + * \param[in] pathname full path; can be a directory + * \param[out] pbasename [optional] pathname not including the + * last dot and characters after that + * \param[out] pextension [optional] path extension, which is + * the last dot and the characters after it. If + * there is no extension, it returns the empty string + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) If you only want the extension, input null for the basename ptr.
+ *      (2) If you only want the basename without extension, input null
+ *          for the extension ptr.
+ *      (3) This function makes decisions based only on the lexical
+ *          structure of the input.  Examples:
+ *            /usr/tmp/abc.jpg  -->  basename: /usr/tmp/abc    ext: .jpg
+ *            /usr/tmp/.jpg     -->  basename: /usr/tmp/       ext: .jpg
+ *            /usr/tmp.jpg/     -->  basename: /usr/tmp.jpg/   ext: [empty str]
+ *            ./.jpg            -->  basename: ./              ext: .jpg
+ *      (4) The input can have either forward (unix) or backward (win)
+ *          slash separators.  The output has unix separators.
+ *      (5) Note that basename, as used here, is different from the result
+ *          of the unix program 'basename'.  Here, basename is the entire
+ *          pathname up to a final extension and its preceding dot.
+ * 
+ */ +l_ok +splitPathAtExtension(const char *pathname, + char **pbasename, + char **pextension) +{ +char *tail, *dir, *lastdot; +char empty[4] = ""; + + PROCNAME("splitPathExtension"); + + if (!pbasename && !pextension) + return ERROR_INT("null input for both strings", procName, 1); + if (pbasename) *pbasename = NULL; + if (pextension) *pextension = NULL; + if (!pathname) + return ERROR_INT("pathname not defined", procName, 1); + + /* Split out the directory first */ + splitPathAtDirectory(pathname, &dir, &tail); + + /* Then look for a "." in the tail part. + * This way we ignore all "." in the directory. */ + if ((lastdot = strrchr(tail, '.'))) { + if (pextension) + *pextension = stringNew(lastdot); + if (pbasename) { + *lastdot = '\0'; + *pbasename = stringJoin(dir, tail); + } + } else { + if (pextension) + *pextension = stringNew(empty); + if (pbasename) + *pbasename = stringNew(pathname); + } + LEPT_FREE(dir); + LEPT_FREE(tail); + return 0; +} + + +/*! + * \brief pathJoin() + * + * \param[in] dir [optional] can be null + * \param[in] fname [optional] can be null + * \return specially concatenated path, or NULL on error + * + *
+ * Notes:
+ *      (1) Use unix-style pathname separators ('/').
+ *      (2) %fname can be the entire path, or part of the path containing
+ *          at least one directory, or a tail without a directory, or NULL.
+ *      (3) It produces a path that strips multiple slashes to a single
+ *          slash, joins %dir and %fname by a slash, and has no trailing
+ *          slashes (except in the cases where %dir == "/" and
+ *          %fname == NULL, or v.v.).
+ *      (4) If both %dir and %fname are null, produces an empty string.
+ *      (5) Neither %dir nor %fname can begin with '..'.
+ *      (6) The result is not canonicalized or tested for correctness:
+ *          garbage in (e.g., /&%), garbage out.
+ *      (7) Examples:
+ *             //tmp// + //abc/  -->  /tmp/abc
+ *             tmp/ + /abc/      -->  tmp/abc
+ *             tmp/ + abc/       -->  tmp/abc
+ *             /tmp/ + ///       -->  /tmp
+ *             /tmp/ + NULL      -->  /tmp
+ *             // + /abc//       -->  /abc
+ *             // + NULL         -->  /
+ *             NULL + /abc/def/  -->  /abc/def
+ *             NULL + abc//      -->  abc
+ *             NULL + //         -->  /
+ *             NULL + NULL       -->  (empty string)
+ *             "" + ""           -->  (empty string)
+ *             "" + /            -->  /
+ *             ".." + /etc/foo   -->  NULL
+ *             /tmp + ".."       -->  NULL
+ * 
+ */ +char * +pathJoin(const char *dir, + const char *fname) +{ +const char *slash = "/"; +char *str, *dest; +l_int32 i, n1, n2, emptydir; +size_t size; +SARRAY *sa1, *sa2; +L_BYTEA *ba; + + PROCNAME("pathJoin"); + + if (!dir && !fname) + return stringNew(""); + if (dir && strlen(dir) >= 2 && dir[0] == '.' && dir[1] == '.') + return (char *)ERROR_PTR("dir starts with '..'", procName, NULL); + if (fname && strlen(fname) >= 2 && fname[0] == '.' && fname[1] == '.') + return (char *)ERROR_PTR("fname starts with '..'", procName, NULL); + + sa1 = sarrayCreate(0); + sa2 = sarrayCreate(0); + ba = l_byteaCreate(4); + + /* Process %dir */ + if (dir && strlen(dir) > 0) { + if (dir[0] == '/') + l_byteaAppendString(ba, slash); + sarraySplitString(sa1, dir, "/"); /* removes all slashes */ + n1 = sarrayGetCount(sa1); + for (i = 0; i < n1; i++) { + str = sarrayGetString(sa1, i, L_NOCOPY); + l_byteaAppendString(ba, str); + l_byteaAppendString(ba, slash); + } + } + + /* Special case to add leading slash: dir NULL or empty string */ + emptydir = dir && strlen(dir) == 0; + if ((!dir || emptydir) && fname && strlen(fname) > 0 && fname[0] == '/') + l_byteaAppendString(ba, slash); + + /* Process %fname */ + if (fname && strlen(fname) > 0) { + sarraySplitString(sa2, fname, "/"); + n2 = sarrayGetCount(sa2); + for (i = 0; i < n2; i++) { + str = sarrayGetString(sa2, i, L_NOCOPY); + l_byteaAppendString(ba, str); + l_byteaAppendString(ba, slash); + } + } + + /* Remove trailing slash */ + dest = (char *)l_byteaCopyData(ba, &size); + if (size > 1 && dest[size - 1] == '/') + dest[size - 1] = '\0'; + + sarrayDestroy(&sa1); + sarrayDestroy(&sa2); + l_byteaDestroy(&ba); + return dest; +} + + +/*! + * \brief appendSubdirs() + * + * \param[in] basedir + * \param[in] subdirs + * \return concatenated full directory path without trailing slash, + * or NULL on error + * + *
+ * Notes:
+ *      (1) Use unix pathname separators
+ *      (2) Allocates a new string:  [basedir]/[subdirs]
+ * 
+ */ +char * +appendSubdirs(const char *basedir, + const char *subdirs) +{ +char *newdir; +size_t len1, len2, len3, len4; + + PROCNAME("appendSubdirs"); + + if (!basedir || !subdirs) + return (char *)ERROR_PTR("basedir and subdirs not both defined", + procName, NULL); + + len1 = strlen(basedir); + len2 = strlen(subdirs); + len3 = len1 + len2 + 6; + if ((newdir = (char *)LEPT_CALLOC(len3 + 1, 1)) == NULL) + return (char *)ERROR_PTR("newdir not made", procName, NULL); + strncat(newdir, basedir, len3); /* add basedir */ + if (newdir[len1 - 1] != '/') /* add '/' if necessary */ + newdir[len1] = '/'; + if (subdirs[0] == '/') /* add subdirs, stripping leading '/' */ + strncat(newdir, subdirs + 1, len3); + else + strncat(newdir, subdirs, len3); + len4 = strlen(newdir); + if (newdir[len4 - 1] == '/') /* strip trailing '/' */ + newdir[len4 - 1] = '\0'; + + return newdir; +} + + +/*--------------------------------------------------------------------* + * Special file name operations * + *--------------------------------------------------------------------*/ +/*! + * \brief convertSepCharsInPath() + * + * \param[in] path + * \param[in] type UNIX_PATH_SEPCHAR, WIN_PATH_SEPCHAR + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) In-place conversion.
+ *      (2) Type is the resulting type:
+ *            * UNIX_PATH_SEPCHAR:  '\\' ==> '/'
+ *            * WIN_PATH_SEPCHAR:   '/' ==> '\\'
+ *      (3) Virtually all path operations in leptonica use unix separators.
+ * 
+ */ +l_ok +convertSepCharsInPath(char *path, + l_int32 type) +{ +l_int32 i; +size_t len; + + PROCNAME("convertSepCharsInPath"); + if (!path) + return ERROR_INT("path not defined", procName, 1); + if (type != UNIX_PATH_SEPCHAR && type != WIN_PATH_SEPCHAR) + return ERROR_INT("invalid type", procName, 1); + + len = strlen(path); + if (type == UNIX_PATH_SEPCHAR) { + for (i = 0; i < len; i++) { + if (path[i] == '\\') + path[i] = '/'; + } + } else { /* WIN_PATH_SEPCHAR */ + for (i = 0; i < len; i++) { + if (path[i] == '/') + path[i] = '\\'; + } + } + return 0; +} + + +/*! + * \brief genPathname() + * + * \param[in] dir [optional] directory or full path name, + * with or without the trailing '/' + * \param[in] fname [optional] file name within a directory + * \return pathname either a directory or full path, or NULL on error + * + *
+ * Notes:
+ *      (1) This function generates actual paths in the following ways:
+ *            * from two sub-parts (e.g., a directory and a file name).
+ *            * from a single path full path, placed in %dir, with
+ *              %fname == NULL.
+ *            * from the name of a file in the local directory placed in
+ *              %fname, with %dir == NULL.
+ *            * if in a "/tmp" directory and on windows, the windows
+ *              temp directory is used.
+ *      (2) On windows, if the root of %dir is '/tmp', this does a name
+ *          translation:
+ *             "/tmp"  ==>  [Temp] (windows)
+ *          where [Temp] is the windows temp directory.
+ *      (3) On unix, the TMPDIR variable is ignored.  No rewriting
+ *          of temp directories is permitted.
+ *      (4) There are four cases for the input:
+ *          (a) %dir is a directory and %fname is defined: result is a full path
+ *          (b) %dir is a directory and %fname is null: result is a directory
+ *          (c) %dir is a full path and %fname is null: result is a full path
+ *          (d) %dir is null or an empty string: start in the current dir;
+ *              result is a full path
+ *      (5) In all cases, the resulting pathname is not terminated with a slash
+ *      (6) The caller is responsible for freeing the returned pathname.
+ * 
+ */ +char * +genPathname(const char *dir, + const char *fname) +{ +l_int32 is_win32 = FALSE; +char *cdir, *pathout; +l_int32 dirlen, namelen, size; + + PROCNAME("genPathname"); + + if (!dir && !fname) + return (char *)ERROR_PTR("no input", procName, NULL); + + /* Handle the case where we start from the current directory */ + if (!dir || dir[0] == '\0') { + if ((cdir = getcwd(NULL, 0)) == NULL) + return (char *)ERROR_PTR("no current dir found", procName, NULL); + } else { + cdir = stringNew(dir); + } + + /* Convert to unix path separators, and remove the trailing + * slash in the directory, except when dir == "/" */ + convertSepCharsInPath(cdir, UNIX_PATH_SEPCHAR); + dirlen = strlen(cdir); + if (cdir[dirlen - 1] == '/' && dirlen != 1) { + cdir[dirlen - 1] = '\0'; + dirlen--; + } + + namelen = (fname) ? strlen(fname) : 0; + size = dirlen + namelen + 256; + if ((pathout = (char *)LEPT_CALLOC(size, sizeof(char))) == NULL) { + LEPT_FREE(cdir); + return (char *)ERROR_PTR("pathout not made", procName, NULL); + } + +#ifdef _WIN32 + is_win32 = TRUE; +#endif /* _WIN32 */ + + /* First handle %dir (which may be a full pathname). + * There is no path rewriting on unix, and on win32, we do not + * rewrite unless the specified directory is /tmp or + * a subdirectory of /tmp */ + if (!is_win32 || dirlen < 4 || + (dirlen == 4 && strncmp(cdir, "/tmp", 4) != 0) || /* not in "/tmp" */ + (dirlen > 4 && strncmp(cdir, "/tmp/", 5) != 0)) { /* not in "/tmp/" */ + stringCopy(pathout, cdir, dirlen); + } else { /* Rewrite for win32 with "/tmp" specified for the directory. */ +#ifdef _WIN32 + l_int32 tmpdirlen; + char tmpdir[MAX_PATH]; + GetTempPath(sizeof(tmpdir), tmpdir); /* get the windows temp dir */ + tmpdirlen = strlen(tmpdir); + if (tmpdirlen > 0 && tmpdir[tmpdirlen - 1] == '\\') { + tmpdir[tmpdirlen - 1] = '\0'; /* trim the trailing '\' */ + } + tmpdirlen = strlen(tmpdir); + stringCopy(pathout, tmpdir, tmpdirlen); + + /* Add the rest of cdir */ + if (dirlen > 4) + stringCat(pathout, size, cdir + 4); +#endif /* _WIN32 */ + } + + /* Now handle %fname */ + if (fname && strlen(fname) > 0) { + dirlen = strlen(pathout); + pathout[dirlen] = '/'; + strncat(pathout, fname, namelen); + } + + LEPT_FREE(cdir); + return pathout; +} + + +/*! + * \brief makeTempDirname() + * + * \param[in] result preallocated on stack or heap and passed in + * \param[in] nbytes size of %result array, in bytes + * \param[in] subdir [optional]; can be NULL or an empty string + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This generates the directory path for output temp files,
+ *          written into %result with unix separators.
+ *      (2) Caller allocates %result, large enough to hold the path,
+ *          which is:
+ *            /tmp/%subdir       (unix)
+ *            [Temp]/%subdir     (windows, mac, ios)
+ *          where [Temp] is a path determined
+ *             - on windows, mac: by GetTempPath()
+ *             - on ios: by confstr() (see man page)
+ *          and %subdir is in general a set of nested subdirectories:
+ *            dir1/dir2/.../dirN
+ *          which in use would not typically exceed 2 levels.
+ *      (3) Usage example:
+ * \code
+ *           char  result[256];
+ *           makeTempDirname(result, sizeof(result), "lept/golden");
+ * \endcode
+ * 
+ */ +l_ok +makeTempDirname(char *result, + size_t nbytes, + const char *subdir) +{ +char *dir, *path; +l_int32 ret = 0; +size_t pathlen; + + PROCNAME("makeTempDirname"); + + if (!result) + return ERROR_INT("result not defined", procName, 1); + if (subdir && ((subdir[0] == '.') || (subdir[0] == '/'))) + return ERROR_INT("subdir not an actual subdirectory", procName, 1); + + memset(result, 0, nbytes); + +#ifdef OS_IOS + { + size_t n = confstr(_CS_DARWIN_USER_TEMP_DIR, result, nbytes); + if (n == 0) { + L_ERROR("failed to find tmp dir, %s\n", procName, strerror(errno)); + return 1; + } else if (n > nbytes) { + return ERROR_INT("result array too small for path\n", procName, 1); + } + dir = pathJoin(result, subdir); + } +#else + dir = pathJoin("/tmp", subdir); +#endif /* ~ OS_IOS */ + +#ifndef _WIN32 + path = stringNew(dir); +#else + path = genPathname(dir, NULL); +#endif /* ~ _WIN32 */ + pathlen = strlen(path); + if (pathlen < nbytes - 1) { + strncpy(result, path, pathlen); + } else { + L_ERROR("result array too small for path\n", procName); + ret = 1; + } + + LEPT_FREE(dir); + LEPT_FREE(path); + return ret; +} + + +/*! + * \brief modifyTrailingSlash() + * + * \param[in] path preallocated on stack or heap and passed in + * \param[in] nbytes size of %path array, in bytes + * \param[in] flag L_ADD_TRAIL_SLASH or L_REMOVE_TRAIL_SLASH + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This carries out the requested action if necessary.
+ * 
+ */ +l_ok +modifyTrailingSlash(char *path, + size_t nbytes, + l_int32 flag) +{ +char lastchar; +size_t len; + + PROCNAME("modifyTrailingSlash"); + + if (!path) + return ERROR_INT("path not defined", procName, 1); + if (flag != L_ADD_TRAIL_SLASH && flag != L_REMOVE_TRAIL_SLASH) + return ERROR_INT("invalid flag", procName, 1); + + len = strlen(path); + lastchar = path[len - 1]; + if (flag == L_ADD_TRAIL_SLASH && lastchar != '/' && len < nbytes - 2) { + path[len] = '/'; + path[len + 1] = '\0'; + } else if (flag == L_REMOVE_TRAIL_SLASH && lastchar == '/') { + path[len - 1] = '\0'; + } + return 0; +} + + +/*! + * \brief l_makeTempFilename() + * + * \return fname : heap allocated filename; returns NULL on failure. + * + *
+ * Notes:
+ *      (1) On unix, this makes a filename of the form
+ *               "/tmp/lept.XXXXXX",
+ *          where each X is a random character.
+ *      (2) On windows, this makes a filename of the form
+ *               "/[Temp]/lp.XXXXXX".
+ *      (3) On all systems, this fails if the file is not writable.
+ *      (4) Safest usage is to write to a subdirectory in debug code.
+ *      (5) The returned filename must be freed by the caller, using lept_free.
+ *      (6) The tail of the filename has a '.', so that cygwin interprets
+ *          the file as having an extension.  Otherwise, cygwin assumes it
+ *          is an executable and appends ".exe" to the filename.
+ *      (7) On unix, whenever possible use tmpfile() instead.  tmpfile()
+ *          hides the file name, returns a stream opened for write,
+ *          and deletes the temp file when the stream is closed.
+ * 
+ */ +char * +l_makeTempFilename() +{ +char dirname[240]; + + PROCNAME("l_makeTempFilename"); + + if (makeTempDirname(dirname, sizeof(dirname), NULL) == 1) + return (char *)ERROR_PTR("failed to make dirname", procName, NULL); + +#ifndef _WIN32 +{ + char *pattern; + l_int32 fd; + pattern = stringConcatNew(dirname, "/lept.XXXXXX", NULL); + fd = mkstemp(pattern); + if (fd == -1) { + LEPT_FREE(pattern); + return (char *)ERROR_PTR("mkstemp failed", procName, NULL); + } + close(fd); + return pattern; +} +#else +{ + char fname[MAX_PATH]; + FILE *fp; + if (GetTempFileName(dirname, "lp.", 0, fname) == 0) + return (char *)ERROR_PTR("GetTempFileName failed", procName, NULL); + if ((fp = fopen(fname, "wb")) == NULL) + return (char *)ERROR_PTR("file cannot be written to", procName, NULL); + fclose(fp); + return stringNew(fname); +} +#endif /* ~ _WIN32 */ +} + + +/*! + * \brief extractNumberFromFilename() + * + * \param[in] fname + * \param[in] numpre number of characters before the digits to be found + * \param[in] numpost number of characters after the digits to be found + * \return num number embedded in the filename; -1 on error or if + * not found + * + *
+ * Notes:
+ *      (1) The number is to be found in the basename, which is the
+ *          filename without either the directory or the last extension.
+ *      (2) When a number is found, it is non-negative.  If no number
+ *          is found, this returns -1, without an error message.  The
+ *          caller needs to check.
+ * 
+ */ +l_int32 +extractNumberFromFilename(const char *fname, + l_int32 numpre, + l_int32 numpost) +{ +char *tail, *basename; +l_int32 len, nret, num; + + PROCNAME("extractNumberFromFilename"); + + if (!fname) + return ERROR_INT("fname not defined", procName, -1); + + splitPathAtDirectory(fname, NULL, &tail); + splitPathAtExtension(tail, &basename, NULL); + LEPT_FREE(tail); + + len = strlen(basename); + if (numpre + numpost > len - 1) { + LEPT_FREE(basename); + return ERROR_INT("numpre + numpost too big", procName, -1); + } + + basename[len - numpost] = '\0'; + nret = sscanf(basename + numpre, "%d", &num); + LEPT_FREE(basename); + + if (nret == 1) + return num; + else + return -1; /* not found */ +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/warper.c b/hgdriver/3rdparty/hgOCR/leptonica/warper.c new file mode 100644 index 0000000..3a735fa --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/warper.c @@ -0,0 +1,1390 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file warper.c + *
+ *
+ *      High-level captcha interface
+ *          PIX               *pixSimpleCaptcha()
+ *
+ *      Random sinusoidal warping
+ *          PIX               *pixRandomHarmonicWarp()
+ *
+ *      Helper functions
+ *          static l_float64  *generateRandomNumberArray()
+ *          static l_int32     applyWarpTransform()
+ *
+ *      Version using a LUT for sin
+ *          PIX               *pixRandomHarmonicWarpLUT()
+ *          static l_int32     applyWarpTransformLUT()
+ *          static l_int32     makeSinLUT()
+ *          static l_float32   getSinFromLUT()
+ *
+ *      Stereoscopic warping
+ *          PIX               *pixWarpStereoscopic()
+ *
+ *      Linear and quadratic horizontal stretching
+ *          PIX               *pixStretchHorizontal()
+ *          PIX               *pixStretchHorizontalSampled()
+ *          PIX               *pixStretchHorizontalLI()
+ *
+ *      Quadratic vertical shear
+ *          PIX               *pixQuadraticVShear()
+ *          PIX               *pixQuadraticVShearSampled()
+ *          PIX               *pixQuadraticVShearLI()
+ *
+ *      Stereo from a pair of images
+ *          PIX               *pixStereoFromPair()
+ * 
+ */ + +#include +#include "allheaders.h" + +static l_float64 *generateRandomNumberArray(l_int32 size); +static l_int32 applyWarpTransform(l_float32 xmag, l_float32 ymag, + l_float32 xfreq, l_float32 yfreq, + l_float64 *randa, l_int32 nx, l_int32 ny, + l_int32 xp, l_int32 yp, + l_float32 *px, l_float32 *py); + +#define USE_SIN_TABLE 0 + + /* Suggested input to pixStereoFromPair(). These are weighting + * factors for input to the red channel from the left image. */ +static const l_float32 DefaultRedWeight = 0.0; +static const l_float32 DefaultGreenWeight = 0.7; +static const l_float32 DefaultBlueWeight = 0.3; + + +/*----------------------------------------------------------------------* + * High-level example captcha interface * + *----------------------------------------------------------------------*/ +/*! + * \brief pixSimpleCaptcha() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] border added white pixels on each side + * \param[in] nterms number of x and y harmonic terms + * \param[in] seed of random number generator + * \param[in] color for colorizing; in 0xrrggbb00 format; use 0 for black + * \param[in] cmapflag 1 for colormap output; 0 for rgb + * \return pixd 8 bpp cmap or 32 bpp rgb, or NULL on error + * + *
+ * Notes:
+ *      (1) This uses typical default values for generating captchas.
+ *          The magnitudes of the harmonic warp are typically to be
+ *          smaller when more terms are used, even though the phases
+ *          are random.  See, for example, prog/warptest.c.
+ * 
+ */ +PIX * +pixSimpleCaptcha(PIX *pixs, + l_int32 border, + l_int32 nterms, + l_uint32 seed, + l_uint32 color, + l_int32 cmapflag) +{ +l_int32 k; +l_float32 xmag[] = {7.0f, 5.0f, 4.0f, 3.0f}; +l_float32 ymag[] = {10.0f, 8.0f, 6.0f, 5.0f}; +l_float32 xfreq[] = {0.12f, 0.10f, 0.10f, 0.11f}; +l_float32 yfreq[] = {0.15f, 0.13f, 0.13f, 0.11f}; +PIX *pixg, *pixgb, *pixw, *pixd; + + PROCNAME("pixSimpleCaptcha"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + if (nterms < 1 || nterms > 4) + return (PIX *)ERROR_PTR("nterms must be in {1,2,3,4}", procName, NULL); + + k = nterms - 1; + pixg = pixConvertTo8(pixs, 0); + pixgb = pixAddBorder(pixg, border, 255); + pixw = pixRandomHarmonicWarp(pixgb, xmag[k], ymag[k], xfreq[k], yfreq[k], + nterms, nterms, seed, 255); + pixd = pixColorizeGray(pixw, color, cmapflag); + + pixDestroy(&pixg); + pixDestroy(&pixgb); + pixDestroy(&pixw); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Random sinusoidal warping * + *----------------------------------------------------------------------*/ +/*! + * \brief pixRandomHarmonicWarp() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] xmag, ymag maximum magnitude of x and y distortion + * \param[in] xfreq, yfreq maximum magnitude of x and y frequency + * \param[in] nx, ny number of x and y harmonic terms + * \param[in] seed of random number generator + * \param[in] grayval color brought in from the outside; + * 0 for black, 255 for white + * \return pixd 8 bpp; no colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) To generate the warped image p(x',y'), set up the transforms
+ *          that are in getWarpTransform().  For each (x',y') in the
+ *          dest, the warp function computes the originating location
+ *          (x, y) in the src.  The differences (x - x') and (y - y')
+ *          are given as a sum of products of sinusoidal terms.  Each
+ *          term is multiplied by a maximum amplitude (in pixels), and the
+ *          angle is determined by a frequency and phase, and depends
+ *          on the (x', y') value of the dest.  Random numbers with
+ *          a variable input seed are used to allow the warping to be
+ *          unpredictable.  A linear interpolation is used to find
+ *          the value for the source at (x, y); this value is written
+ *          into the dest.
+ *      (2) This can be used to generate 'captcha's, which are somewhat
+ *          randomly distorted images of text.  A typical set of parameters
+ *          for a captcha are:
+ *                    xmag = 4.0     ymag = 6.0
+ *                    xfreq = 0.10   yfreq = 0.13
+ *                    nx = 3         ny = 3
+ *          Other examples can be found in prog/warptest.c.
+ * 
+ */ +PIX * +pixRandomHarmonicWarp(PIX *pixs, + l_float32 xmag, + l_float32 ymag, + l_float32 xfreq, + l_float32 yfreq, + l_int32 nx, + l_int32 ny, + l_uint32 seed, + l_int32 grayval) +{ +l_int32 w, h, d, i, j, wpls, wpld, val; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +l_float64 *randa; +PIX *pixd; + + PROCNAME("pixRandomHarmonicWarp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + /* Compute filter output at each location. We iterate over + * the destination pixels. For each dest pixel, use the + * warp function to compute the four source pixels that + * contribute, at the location (x, y). Each source pixel + * is divided into 16 x 16 subpixels to get an approximate value. */ + srand(seed); + randa = generateRandomNumberArray(5 * (nx + ny)); + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + applyWarpTransform(xmag, ymag, xfreq, yfreq, randa, nx, ny, + j, i, &x, &y); + linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); + SET_DATA_BYTE(lined, j, val); + } + } + + LEPT_FREE(randa); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Static helper functions * + *----------------------------------------------------------------------*/ +static l_float64 * +generateRandomNumberArray(l_int32 size) +{ +l_int32 i; +l_float64 *randa; + + PROCNAME("generateRandomNumberArray"); + + if ((randa = (l_float64 *)LEPT_CALLOC(size, sizeof(l_float64))) == NULL) + return (l_float64 *)ERROR_PTR("calloc fail for randa", procName, NULL); + + /* Return random values between 0.5 and 1.0 */ + for (i = 0; i < size; i++) + randa[i] = 0.5 * (1.0 + (l_float64)rand() / (l_float64)RAND_MAX); + return randa; +} + + +/*! + * \brief applyWarpTransform() + * + * Notes: + * (1) Uses the internal sin function. + */ +static l_int32 +applyWarpTransform(l_float32 xmag, + l_float32 ymag, + l_float32 xfreq, + l_float32 yfreq, + l_float64 *randa, + l_int32 nx, + l_int32 ny, + l_int32 xp, + l_int32 yp, + l_float32 *px, + l_float32 *py) +{ +l_int32 i; +l_float64 twopi, x, y, anglex, angley; + + twopi = 6.283185; + for (i = 0, x = xp; i < nx; i++) { + anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; + angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; + x += xmag * randa[3 * i] * sin(anglex) * sin(angley); + } + for (i = nx, y = yp; i < nx + ny; i++) { + angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; + anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; + y += ymag * randa[3 * i] * sin(angley) * sin(anglex); + } + + *px = (l_float32)x; + *py = (l_float32)y; + return 0; +} + + +#if USE_SIN_TABLE +/*----------------------------------------------------------------------* + * Version using a LUT for sin * + *----------------------------------------------------------------------*/ +static l_int32 applyWarpTransformLUT(l_float32 xmag, l_float32 ymag, + l_float32 xfreq, l_float32 yfreq, + l_float64 *randa, l_int32 nx, l_int32 ny, + l_int32 xp, l_int32 yp, l_float32 *lut, + l_int32 npts, l_float32 *px, l_float32 *py); +static l_int32 makeSinLUT(l_int32 npts, NUMA **pna); +static l_float32 getSinFromLUT(l_float32 *tab, l_int32 npts, + l_float32 radang); + +/*! + * \brief pixRandomHarmonicWarpLUT() + * + * \param[in] pixs 8 bpp; no colormap + * \param[in] xmag, ymag maximum magnitude of x and y distortion + * \param[in] xfreq, yfreq maximum magnitude of x and y frequency + * \param[in] nx, ny number of x and y harmonic terms + * \param[in] seed of random number generator + * \param[in] grayval color brought in from the outside; + * 0 for black, 255 for white + * \return pixd 8 bpp; no colormap, or NULL on error + * + *
+ * Notes:
+ *      (1) See notes and inline comments in pixRandomHarmonicWarp().
+ *          This version uses a LUT for the sin function.  It is not
+ *          appreciably faster than using the built-in sin function,
+ *          and is here for comparison only.
+ * 
+ */ +PIX * +pixRandomHarmonicWarpLUT(PIX *pixs, + l_float32 xmag, + l_float32 ymag, + l_float32 xfreq, + l_float32 yfreq, + l_int32 nx, + l_int32 ny, + l_uint32 seed, + l_int32 grayval) +{ +l_int32 w, h, d, i, j, wpls, wpld, val, npts; +l_uint32 *datas, *datad, *lined; +l_float32 x, y; +l_float32 *lut; +l_float64 *randa; +NUMA *na; +PIX *pixd; + + PROCNAME("pixRandomHarmonicWarp"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8) + return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); + + /* Compute filter output at each location. We iterate over + * the destination pixels. For each dest pixel, use the + * warp function to compute the four source pixels that + * contribute, at the location (x, y). Each source pixel + * is divided into 16 x 16 subpixels to get an approximate value. */ + srand(seed); + randa = generateRandomNumberArray(5 * (nx + ny)); + pixd = pixCreateTemplate(pixs); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + datad = pixGetData(pixd); + wpld = pixGetWpl(pixd); + + npts = 100; + makeSinLUT(npts, &na); + lut = numaGetFArray(na, L_NOCOPY); + for (i = 0; i < h; i++) { + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + applyWarpTransformLUT(xmag, ymag, xfreq, yfreq, randa, nx, ny, + j, i, lut, npts, &x, &y); + linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); + SET_DATA_BYTE(lined, j, val); + } + } + + numaDestroy(&na); + LEPT_FREE(randa); + return pixd; +} + + +/*! + * \brief applyWarpTransformLUT() + * + * Notes: + * (1) Uses an LUT for computing sin(theta). There is little speed + * advantage to using the LUT. + */ +static l_int32 +applyWarpTransformLUT(l_float32 xmag, + l_float32 ymag, + l_float32 xfreq, + l_float32 yfreq, + l_float64 *randa, + l_int32 nx, + l_int32 ny, + l_int32 xp, + l_int32 yp, + l_float32 *lut, + l_int32 npts, + l_float32 *px, + l_float32 *py) +{ +l_int32 i; +l_float64 twopi, x, y, anglex, angley, sanglex, sangley; + + twopi = 6.283185; + for (i = 0, x = xp; i < nx; i++) { + anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; + angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; + sanglex = getSinFromLUT(lut, npts, anglex); + sangley = getSinFromLUT(lut, npts, angley); + x += xmag * randa[3 * i] * sanglex * sangley; + } + for (i = nx, y = yp; i < nx + ny; i++) { + angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; + anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; + sanglex = getSinFromLUT(lut, npts, anglex); + sangley = getSinFromLUT(lut, npts, angley); + y += ymag * randa[3 * i] * sangley * sanglex; + } + + *px = (l_float32)x; + *py = (l_float32)y; + return 0; +} + + +static l_int32 +makeSinLUT(l_int32 npts, + NUMA **pna) +{ +l_int32 i, n; +l_float32 delx, fval; +NUMA *na; + + PROCNAME("makeSinLUT"); + + if (!pna) + return ERROR_INT("&na not defined", procName, 1); + *pna = NULL; + if (npts < 2) + return ERROR_INT("npts < 2", procName, 1); + n = 2 * npts + 1; + na = numaCreate(n); + *pna = na; + delx = 3.14159265 / (l_float32)npts; + numaSetParameters(na, 0.0, delx); + for (i = 0; i < n / 2; i++) + numaAddNumber(na, (l_float32)sin((l_float64)i * delx)); + for (i = 0; i < n / 2; i++) { + numaGetFValue(na, i, &fval); + numaAddNumber(na, -fval); + } + numaAddNumber(na, 0); + + return 0; +} + + +static l_float32 +getSinFromLUT(l_float32 *tab, + l_int32 npts, + l_float32 radang) +{ +l_int32 index; +l_float32 twopi, invtwopi, findex, diff; + + /* Restrict radang to [0, 2pi] */ + twopi = 6.283185; + invtwopi = 0.1591549; + if (radang < 0.0) + radang += twopi * (1.0 - (l_int32)(-radang * invtwopi)); + else if (radang > 0.0) + radang -= twopi * (l_int32)(radang * invtwopi); + + /* Interpolate */ + findex = (2.0 * (l_float32)npts) * (radang * invtwopi); + index = (l_int32)findex; + if (index == 2 * npts) + return tab[index]; + diff = findex - index; + return (1.0 - diff) * tab[index] + diff * tab[index + 1]; +} +#endif /* USE_SIN_TABLE */ + + + +/*---------------------------------------------------------------------------* + * Stereoscopic warping * + *---------------------------------------------------------------------------*/ +/*! + * \brief pixWarpStereoscopic() + * + * \param[in] pixs any depth, colormap ok + * \param[in] zbend horizontal separation in pixels of red and cyan + * at the left and right sides, that gives rise to + * quadratic curvature out of the image plane + * \param[in] zshiftt uniform pixel translation difference between + * red and cyan, that pushes the top of the image + * plane away from the viewer (zshiftt > 0) or + * towards the viewer (zshiftt < 0) + * \param[in] zshiftb uniform pixel translation difference between + * red and cyan, that pushes the bottom of the image + * plane away from the viewer (zshiftb > 0) or + * towards the viewer (zshiftb < 0) + * \param[in] ybendt multiplicative parameter for in-plane vertical + * displacement at the left or right edge at the top: + * y = ybendt * (2x/w - 1)^2 + * \param[in] ybendb same as ybendt, except at the left or right edge + * at the bottom + * \param[in] redleft 1 if the red filter is on the left; 0 otherwise + * \return pixd 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) This function splits out the red channel, mucks around with
+ *          it, then recombines with the unmolested cyan channel.
+ *      (2) By using a quadratically increasing shift of the red
+ *          pixels horizontally and away from the vertical centerline,
+ *          the image appears to bend quadratically out of the image
+ *          plane, symmetrically with respect to the vertical center
+ *          line.  A positive value of %zbend causes the plane to be
+ *          curved away from the viewer.  We use linearly interpolated
+ *          stretching to avoid the appearance of kinks in the curve.
+ *      (3) The parameters %zshiftt and %zshiftb tilt the image plane
+ *          about a horizontal line through the center, and at the
+ *          same time move that line either in toward the viewer or away.
+ *          This is implemented by a combination of horizontal shear
+ *          about the center line (for the tilt) and horizontal
+ *          translation (to move the entire plane in or out).
+ *          A positive value of %zshiftt moves the top of the plane
+ *          away from the viewer, and a positive value of %zshiftb
+ *          moves the bottom of the plane away.  We use linear interpolated
+ *          shear to avoid visible vertical steps in the tilted image.
+ *      (4) The image can be bent in the plane and about the vertical
+ *          centerline.  The centerline does not shift, and the
+ *          parameter %ybend gives the relative shift at left and right
+ *          edges, with a downward shift for positive values of %ybend.
+ *      (6) When writing out a steroscopic (red/cyan) image in jpeg,
+ *          first call pixSetChromaSampling(pix, 0) to get sufficient
+ *          resolution in the red channel.
+ *      (7) Typical values are:
+ *             zbend = 20
+ *             zshiftt = 15
+ *             zshiftb = -15
+ *             ybendt = 30
+ *             ybendb = 0
+ *          If the disparity z-values are too large, it is difficult for
+ *          the brain to register the two images.
+ *      (8) This function has been cleverly reimplemented by Jeff Breidenbach.
+ *          The original implementation used two 32 bpp rgb images,
+ *          and merged them at the end.  The result is somewhat faded,
+ *          and has a parameter "thresh" that controls the amount of
+ *          color in the result.  (The present implementation avoids these
+ *          two problems, skipping both the colorization and the alpha
+ *          blending at the end, and is about 3x faster)
+ *          The basic operations with 32 bpp are as follows:
+ *               // Immediate conversion to 32 bpp
+ *            Pix *pixt1 = pixConvertTo32(pixs);
+ *               // Do vertical shear
+ *            Pix *pixr = pixQuadraticVerticalShear(pixt1, L_WARP_TO_RIGHT,
+ *                                                  ybendt, ybendb,
+ *                                                  L_BRING_IN_WHITE);
+ *               // Colorize two versions, toward red and cyan
+ *            Pix *pixc = pixCopy(NULL, pixr);
+ *            l_int32 thresh = 150;  // if higher, get less original color
+ *            pixColorGray(pixr, NULL, L_PAINT_DARK, thresh, 255, 0, 0);
+ *            pixColorGray(pixc, NULL, L_PAINT_DARK, thresh, 0, 255, 255);
+ *               // Shift the red pixels; e.g., by stretching
+ *            Pix *pixrs = pixStretchHorizontal(pixr, L_WARP_TO_RIGHT,
+ *                                              L_QUADRATIC_WARP, zbend,
+ *                                              L_INTERPOLATED,
+ *                                              L_BRING_IN_WHITE);
+ *               // Blend the shifted red and unshifted cyan 50:50
+ *            Pix *pixg = pixCreate(w, h, 8);
+ *            pixSetAllArbitrary(pixg, 128);
+ *            pixd = pixBlendWithGrayMask(pixrs, pixc, pixg, 0, 0);
+ * 
+ */ +PIX * +pixWarpStereoscopic(PIX *pixs, + l_int32 zbend, + l_int32 zshiftt, + l_int32 zshiftb, + l_int32 ybendt, + l_int32 ybendb, + l_int32 redleft) +{ +l_int32 w, h, zshift; +l_float32 angle; +BOX *boxleft, *boxright; +PIX *pix1, *pix2, *pix3, *pix4, *pixr, *pixg, *pixb; +PIX *pixv1, *pixv2, *pixv3, *pixv4; +PIX *pixrs, *pixrss; +PIX *pixd; + + PROCNAME("pixWarpStereoscopic"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + + /* Convert to the output depth, 32 bpp. */ + pix1 = pixConvertTo32(pixs); + + /* If requested, do a quad vertical shearing, pushing pixels up + * or down, depending on their distance from the centerline. */ + pixGetDimensions(pixs, &w, &h, NULL); + boxleft = boxCreate(0, 0, w / 2, h); + boxright = boxCreate(w / 2, 0, w - w / 2, h); + if (ybendt != 0 || ybendb != 0) { + pixv1 = pixClipRectangle(pix1, boxleft, NULL); + pixv2 = pixClipRectangle(pix1, boxright, NULL); + pixv3 = pixQuadraticVShear(pixv1, L_WARP_TO_LEFT, ybendt, + ybendb, L_INTERPOLATED, + L_BRING_IN_WHITE); + pixv4 = pixQuadraticVShear(pixv2, L_WARP_TO_RIGHT, ybendt, + ybendb, L_INTERPOLATED, + L_BRING_IN_WHITE); + pix2 = pixCreate(w, h, 32); + pixRasterop(pix2, 0, 0, w / 2, h, PIX_SRC, pixv3, 0, 0); + pixRasterop(pix2, w / 2, 0, w - w / 2, h, PIX_SRC, pixv4, 0, 0); + pixDestroy(&pixv1); + pixDestroy(&pixv2); + pixDestroy(&pixv3); + pixDestroy(&pixv4); + } else { + pix2 = pixClone(pix1); + } + pixDestroy(&pix1); + + /* Split out the 3 components */ + pixr = pixGetRGBComponent(pix2, COLOR_RED); + pixg = pixGetRGBComponent(pix2, COLOR_GREEN); + pixb = pixGetRGBComponent(pix2, COLOR_BLUE); + pixDestroy(&pix2); + + /* The direction of the stereo disparity below is set + * for the red filter to be over the left eye. If the red + * filter is over the right eye, invert the horizontal shifts. */ + if (redleft) { + zbend = -zbend; + zshiftt = -zshiftt; + zshiftb = -zshiftb; + } + + /* Shift the red pixels horizontally by an amount that + * increases quadratically from the centerline. */ + if (zbend == 0) { + pixrs = pixClone(pixr); + } else { + pix1 = pixClipRectangle(pixr, boxleft, NULL); + pix2 = pixClipRectangle(pixr, boxright, NULL); + pix3 = pixStretchHorizontal(pix1, L_WARP_TO_LEFT, L_QUADRATIC_WARP, + zbend, L_INTERPOLATED, L_BRING_IN_WHITE); + pix4 = pixStretchHorizontal(pix2, L_WARP_TO_RIGHT, L_QUADRATIC_WARP, + zbend, L_INTERPOLATED, L_BRING_IN_WHITE); + pixrs = pixCreate(w, h, 8); + pixRasterop(pixrs, 0, 0, w / 2, h, PIX_SRC, pix3, 0, 0); + pixRasterop(pixrs, w / 2, 0, w - w / 2, h, PIX_SRC, pix4, 0, 0); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + } + + /* Perform a combination of horizontal shift and shear of + * red pixels. The causes the plane of the image to tilt and + * also move forward or backward. */ + if (zshiftt == 0 && zshiftb == 0) { + pixrss = pixClone(pixrs); + } else if (zshiftt == zshiftb) { + pixrss = pixTranslate(NULL, pixrs, zshiftt, 0, L_BRING_IN_WHITE); + } else { + angle = (l_float32)(zshiftb - zshiftt) / + L_MAX(1.0, (l_float32)pixGetHeight(pixrs)); + zshift = (zshiftt + zshiftb) / 2; + pix1 = pixTranslate(NULL, pixrs, zshift, 0, L_BRING_IN_WHITE); + pixrss = pixHShearLI(pix1, h / 2, angle, L_BRING_IN_WHITE); + pixDestroy(&pix1); + } + + /* Combine the unchanged cyan (g,b) image with the shifted red */ + pixd = pixCreateRGBImage(pixrss, pixg, pixb); + + boxDestroy(&boxleft); + boxDestroy(&boxright); + pixDestroy(&pixrs); + pixDestroy(&pixrss); + pixDestroy(&pixr); + pixDestroy(&pixg); + pixDestroy(&pixb); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Linear and quadratic horizontal stretching * + *----------------------------------------------------------------------*/ +/*! + * \brief pixStretchHorizontal() + * + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP + * \param[in] hmax horizontal displacement at edge + * \param[in] operation L_SAMPLED or L_INTERPOLATED + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched/compressed, or NULL on error + * + *
+ * Notes:
+ *      (1) If %hmax > 0, this is an increase in the coordinate value of
+ *          pixels in pixd, relative to the same pixel in pixs.
+ *      (2) If %dir == L_WARP_TO_LEFT, the pixels on the right edge of
+ *          the image are not moved. So, for example, if %hmax > 0
+ *          and %dir == L_WARP_TO_LEFT, the pixels in pixd are
+ *          contracted toward the right edge of the image, relative
+ *          to those in pixs.
+ *      (3) If %type == L_LINEAR_WARP, the pixel positions are moved
+ *          to the left or right by an amount that varies linearly with
+ *          the horizontal location.
+ *      (4) If %operation == L_SAMPLED, the dest pixels are taken from
+ *          the nearest src pixel.  Otherwise, we use linear interpolation
+ *          between pairs of sampled pixels.
+ * 
+ */ +PIX * +pixStretchHorizontal(PIX *pixs, + l_int32 dir, + l_int32 type, + l_int32 hmax, + l_int32 operation, + l_int32 incolor) +{ +l_int32 d; + + PROCNAME("pixStretchHorizontal"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + d = pixGetDepth(pixs); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (operation != L_SAMPLED && operation != L_INTERPOLATED) + return (PIX *)ERROR_PTR("invalid operation", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + if (d == 1 && operation == L_INTERPOLATED) { + L_WARNING("Using sampling for 1 bpp\n", procName); + operation = L_INTERPOLATED; + } + + if (operation == L_SAMPLED) + return pixStretchHorizontalSampled(pixs, dir, type, hmax, incolor); + else + return pixStretchHorizontalLI(pixs, dir, type, hmax, incolor); +} + + +/*! + * \brief pixStretchHorizontalSampled() + * + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP + * \param[in] hmax horizontal displacement at edge + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched/compressed, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixStretchHorizontal() for details.
+ * 
+ */ +PIX * +pixStretchHorizontalSampled(PIX *pixs, + l_int32 dir, + l_int32 type, + l_int32 hmax, + l_int32 incolor) +{ +l_int32 i, j, jd, w, wm, h, d, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixStretchHorizontalSampled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + pixd = pixCreateTemplate(pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + wm = w - 1; + for (jd = 0; jd < w; jd++) { + if (dir == L_WARP_TO_LEFT) { + if (type == L_LINEAR_WARP) + j = jd - (hmax * (wm - jd)) / wm; + else /* L_QUADRATIC_WARP */ + j = jd - (hmax * (wm - jd) * (wm - jd)) / (wm * wm); + } else if (dir == L_WARP_TO_RIGHT) { + if (type == L_LINEAR_WARP) + j = jd - (hmax * jd) / wm; + else /* L_QUADRATIC_WARP */ + j = jd - (hmax * jd * jd) / (wm * wm); + } + if (j < 0 || j > w - 1) continue; + + switch (d) + { + case 1: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + val = GET_DATA_BIT(lines, j); + if (val) + SET_DATA_BIT(lined, jd); + } + break; + case 8: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + val = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(lined, jd, val); + } + break; + case 32: + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + lined[jd] = lines[j]; + } + break; + default: + L_ERROR("invalid depth: %d\n", procName, d); + pixDestroy(&pixd); + return NULL; + } + } + + return pixd; +} + + +/*! + * \brief pixStretchHorizontalLI() + * + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP + * \param[in] hmax horizontal displacement at edge + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched/compressed, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixStretchHorizontal() for details.
+ * 
+ */ +PIX * +pixStretchHorizontalLI(PIX *pixs, + l_int32 dir, + l_int32 type, + l_int32 hmax, + l_int32 incolor) +{ +l_int32 i, j, jd, jp, jf, w, wm, h, d, wpls, wpld, val, rval, gval, bval; +l_uint32 word0, word1; +l_uint32 *datas, *datad, *lines, *lined; +PIX *pixd; + + PROCNAME("pixStretchHorizontalLI"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) + return (PIX *)ERROR_PTR("invalid type", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + /* Standard linear interpolation, subdividing each pixel into 64 */ + pixd = pixCreateTemplate(pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + wm = w - 1; + for (jd = 0; jd < w; jd++) { + if (dir == L_WARP_TO_LEFT) { + if (type == L_LINEAR_WARP) + j = 64 * jd - 64 * (hmax * (wm - jd)) / wm; + else /* L_QUADRATIC_WARP */ + j = 64 * jd - 64 * (hmax * (wm - jd) * (wm - jd)) / (wm * wm); + } else if (dir == L_WARP_TO_RIGHT) { + if (type == L_LINEAR_WARP) + j = 64 * jd - 64 * (hmax * jd) / wm; + else /* L_QUADRATIC_WARP */ + j = 64 * jd - 64 * (hmax * jd * jd) / (wm * wm); + } + jp = j / 64; + jf = j & 0x3f; + if (jp < 0 || jp > wm) continue; + + switch (d) + { + case 8: + if (jp < wm) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + val = ((63 - jf) * GET_DATA_BYTE(lines, jp) + + jf * GET_DATA_BYTE(lines, jp + 1) + 31) / 63; + SET_DATA_BYTE(lined, jd, val); + } + } else { /* jp == wm */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + val = GET_DATA_BYTE(lines, jp); + SET_DATA_BYTE(lined, jd, val); + } + } + break; + case 32: + if (jp < wm) { + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + word0 = *(lines + jp); + word1 = *(lines + jp + 1); + rval = ((63 - jf) * ((word0 >> L_RED_SHIFT) & 0xff) + + jf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; + gval = ((63 - jf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + + jf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; + bval = ((63 - jf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + + jf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; + composeRGBPixel(rval, gval, bval, lined + jd); + } + } else { /* jp == wm */ + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + lined[jd] = lines[jp]; + } + } + break; + default: + L_ERROR("invalid depth: %d\n", procName, d); + pixDestroy(&pixd); + return NULL; + } + } + + return pixd; +} + + +/*----------------------------------------------------------------------* + * Quadratic vertical shear * + *----------------------------------------------------------------------*/ +/*! + * \brief pixQuadraticVShear() + * + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] vmaxt max vertical displacement at edge and at top + * \param[in] vmaxb max vertical displacement at edge and at bottom + * \param[in] operation L_SAMPLED or L_INTERPOLATED + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched, or NULL on error + * + *
+ * Notes:
+ *      (1) This gives a quadratic bending, upward or downward, as you
+ *          move to the left or right.
+ *      (2) If %dir == L_WARP_TO_LEFT, the right edge is unchanged, and
+ *          the left edge pixels are moved maximally up or down.
+ *      (3) Parameters %vmaxt and %vmaxb control the maximum amount of
+ *          vertical pixel shear at the top and bottom, respectively.
+ *          If %vmaxt > 0, the vertical displacement of pixels at the
+ *          top is downward.  Likewise, if %vmaxb > 0, the vertical
+ *          displacement of pixels at the bottom is downward.
+ *      (4) If %operation == L_SAMPLED, the dest pixels are taken from
+ *          the nearest src pixel.  Otherwise, we use linear interpolation
+ *          between pairs of sampled pixels.
+ *      (5) This is for quadratic shear.  For uniform (linear) shear,
+ *          use the standard shear operators.
+ * 
+ */ +PIX * +pixQuadraticVShear(PIX *pixs, + l_int32 dir, + l_int32 vmaxt, + l_int32 vmaxb, + l_int32 operation, + l_int32 incolor) +{ +l_int32 w, h, d; + + PROCNAME("pixQuadraticVShear"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (operation != L_SAMPLED && operation != L_INTERPOLATED) + return (PIX *)ERROR_PTR("invalid operation", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + if (vmaxt == 0 && vmaxb == 0) + return pixCopy(NULL, pixs); + + if (operation == L_INTERPOLATED && d == 1) { + L_WARNING("no interpolation for 1 bpp; using sampling\n", procName); + operation = L_SAMPLED; + } + + if (operation == L_SAMPLED) + return pixQuadraticVShearSampled(pixs, dir, vmaxt, vmaxb, incolor); + else /* operation == L_INTERPOLATED */ + return pixQuadraticVShearLI(pixs, dir, vmaxt, vmaxb, incolor); +} + + +/*! + * \brief pixQuadraticVShearSampled() + * + * \param[in] pixs 1, 8 or 32 bpp + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] vmaxt max vertical displacement at edge and at top + * \param[in] vmaxb max vertical displacement at edge and at bottom + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixQuadraticVShear() for details.
+ * 
+ */ +PIX * +pixQuadraticVShearSampled(PIX *pixs, + l_int32 dir, + l_int32 vmaxt, + l_int32 vmaxb, + l_int32 incolor) +{ +l_int32 i, j, id, w, h, d, wm, hm, wpls, wpld, val; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 delrowt, delrowb, denom1, denom2, dely; +PIX *pixd; + + PROCNAME("pixQuadraticVShearSampled"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 8 && d != 32) + return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + if (vmaxt == 0 && vmaxb == 0) + return pixCopy(NULL, pixs); + + pixd = pixCreateTemplate(pixs); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + wm = w - 1; + hm = h - 1; + denom1 = 1. / (l_float32)h; + denom2 = 1. / (l_float32)(wm * wm); + for (j = 0; j < w; j++) { + if (dir == L_WARP_TO_LEFT) { + delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2; + delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2; + } else if (dir == L_WARP_TO_RIGHT) { + delrowt = (l_float32)(vmaxt * j * j) * denom2; + delrowb = (l_float32)(vmaxb * j * j) * denom2; + } + switch (d) + { + case 1: + for (id = 0; id < h; id++) { + dely = (delrowt * (hm - id) + delrowb * id) * denom1; + i = id - (l_int32)(dely + 0.5); + if (i < 0 || i > hm) continue; + lines = datas + i * wpls; + lined = datad + id * wpld; + val = GET_DATA_BIT(lines, j); + if (val) + SET_DATA_BIT(lined, j); + } + break; + case 8: + for (id = 0; id < h; id++) { + dely = (delrowt * (hm - id) + delrowb * id) * denom1; + i = id - (l_int32)(dely + 0.5); + if (i < 0 || i > hm) continue; + lines = datas + i * wpls; + lined = datad + id * wpld; + val = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(lined, j, val); + } + break; + case 32: + for (id = 0; id < h; id++) { + dely = (delrowt * (hm - id) + delrowb * id) * denom1; + i = id - (l_int32)(dely + 0.5); + if (i < 0 || i > hm) continue; + lines = datas + i * wpls; + lined = datad + id * wpld; + lined[j] = lines[j]; + } + break; + default: + L_ERROR("invalid depth: %d\n", procName, d); + pixDestroy(&pixd); + return NULL; + } + } + + return pixd; +} + + +/*! + * \brief pixQuadraticVShearLI() + * + * \param[in] pixs 8 or 32 bpp, or colormapped + * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT + * \param[in] vmaxt max vertical displacement at edge and at top + * \param[in] vmaxb max vertical displacement at edge and at bottom + * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK + * \return pixd stretched, or NULL on error + * + *
+ * Notes:
+ *      (1) See pixQuadraticVShear() for details.
+ * 
+ */ +PIX * +pixQuadraticVShearLI(PIX *pixs, + l_int32 dir, + l_int32 vmaxt, + l_int32 vmaxb, + l_int32 incolor) +{ +l_int32 i, j, id, yp, yf, w, h, d, wm, hm, wpls, wpld; +l_int32 val, rval, gval, bval; +l_uint32 word0, word1; +l_uint32 *datas, *datad, *lines, *lined; +l_float32 delrowt, delrowb, denom1, denom2, dely; +PIX *pix, *pixd; +PIXCMAP *cmap; + + PROCNAME("pixQuadraticVShearLI"); + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d == 1) + return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); + cmap = pixGetColormap(pixs); + if (d != 8 && d != 32 && !cmap) + return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", procName, NULL); + if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) + return (PIX *)ERROR_PTR("invalid direction", procName, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); + + if (vmaxt == 0 && vmaxb == 0) + return pixCopy(NULL, pixs); + + /* Remove any existing colormap */ + if (cmap) + pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); + else + pix = pixClone(pixs); + d = pixGetDepth(pix); + if (d != 8 && d != 32) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("invalid depth", procName, NULL); + } + + /* Standard linear interp: subdivide each pixel into 64 parts */ + pixd = pixCreateTemplate(pix); + pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); + datas = pixGetData(pix); + datad = pixGetData(pixd); + wpls = pixGetWpl(pix); + wpld = pixGetWpl(pixd); + wm = w - 1; + hm = h - 1; + denom1 = 1.0 / (l_float32)h; + denom2 = 1.0 / (l_float32)(wm * wm); + for (j = 0; j < w; j++) { + if (dir == L_WARP_TO_LEFT) { + delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2; + delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2; + } else if (dir == L_WARP_TO_RIGHT) { + delrowt = (l_float32)(vmaxt * j * j) * denom2; + delrowb = (l_float32)(vmaxb * j * j) * denom2; + } + switch (d) + { + case 8: + for (id = 0; id < h; id++) { + dely = (delrowt * (hm - id) + delrowb * id) * denom1; + i = 64 * id - (l_int32)(64.0 * dely); + yp = i / 64; + yf = i & 63; + if (yp < 0 || yp > hm) continue; + lines = datas + yp * wpls; + lined = datad + id * wpld; + if (yp < hm) { + val = ((63 - yf) * GET_DATA_BYTE(lines, j) + + yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63; + } else { /* yp == hm */ + val = GET_DATA_BYTE(lines, j); + } + SET_DATA_BYTE(lined, j, val); + } + break; + case 32: + for (id = 0; id < h; id++) { + dely = (delrowt * (hm - id) + delrowb * id) * denom1; + i = 64 * id - (l_int32)(64.0 * dely); + yp = i / 64; + yf = i & 63; + if (yp < 0 || yp > hm) continue; + lines = datas + yp * wpls; + lined = datad + id * wpld; + if (yp < hm) { + word0 = *(lines + j); + word1 = *(lines + wpls + j); + rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) + + yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; + gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + + yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; + bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + + yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; + composeRGBPixel(rval, gval, bval, lined + j); + } else { /* yp == hm */ + lined[j] = lines[j]; + } + } + break; + default: + L_ERROR("invalid depth: %d\n", procName, d); + pixDestroy(&pix); + pixDestroy(&pixd); + return NULL; + } + } + + pixDestroy(&pix); + return pixd; +} + + +/*----------------------------------------------------------------------* + * Stereo from a pair of images * + *----------------------------------------------------------------------*/ +/*! + * \brief pixStereoFromPair() + * + * \param[in] pix1 32 bpp rgb + * \param[in] pix2 32 bpp rgb + * \param[in] rwt, gwt, bwt weighting factors used for each component in + pix1 to determine the output red channel + * \return pixd stereo enhanced, or NULL on error + * + *
+ * Notes:
+ *      (1) pix1 and pix2 are a pair of stereo images, ideally taken
+ *          concurrently in the same plane, with some lateral translation.
+ *      (2) The output red channel is determined from %pix1.
+ *          The output green and blue channels are taken from the green
+ *          and blue channels, respectively, of %pix2.
+ *      (3) The weights determine how much of each component in %pix1
+ *          goes into the output red channel.  The sum of weights
+ *          must be 1.0.  If it's not, we scale the weights to
+ *          satisfy this criterion.
+ *      (4) The most general pixel mapping allowed here is:
+ *            rval = rwt * r1 + gwt * g1 + bwt * b1  (from pix1)
+ *            gval = g2   (from pix2)
+ *            bval = b2   (from pix2)
+ *      (5) The simplest method is to use rwt = 1.0, gwt = 0.0, bwt = 0.0,
+ *          but this causes unpleasant visual artifacts with red in the image.
+ *          Use of green and blue from %pix1 in the red channel,
+ *          instead of red, tends to fix that problem.
+ * 
+ */ +PIX * +pixStereoFromPair(PIX *pix1, + PIX *pix2, + l_float32 rwt, + l_float32 gwt, + l_float32 bwt) +{ +l_int32 i, j, w, h, wpl1, wpl2, rval, gval, bval; +l_uint32 word1, word2; +l_uint32 *data1, *data2, *datad, *line1, *line2, *lined; +l_float32 sum; +PIX *pixd; + + PROCNAME("pixStereoFromPair"); + + if (!pix1 || !pix2) + return (PIX *)ERROR_PTR("pix1, pix2 not both defined", procName, NULL); + if (pixGetDepth(pix1) != 32 || pixGetDepth(pix2) != 32) + return (PIX *)ERROR_PTR("pix1, pix2 not both 32 bpp", procName, NULL); + + /* Make sure the sum of weights is 1.0; otherwise, you can get + * overflow in the gray value. */ + if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { + rwt = DefaultRedWeight; + gwt = DefaultGreenWeight; + bwt = DefaultBlueWeight; + } + sum = rwt + gwt + bwt; + if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ + L_WARNING("weights don't sum to 1; maintaining ratios\n", procName); + rwt = rwt / sum; + gwt = gwt / sum; + bwt = bwt / sum; + } + + pixGetDimensions(pix1, &w, &h, NULL); + pixd = pixCreateTemplate(pix1); + data1 = pixGetData(pix1); + data2 = pixGetData(pix2); + datad = pixGetData(pixd); + wpl1 = pixGetWpl(pix1); + wpl2 = pixGetWpl(pix2); + for (i = 0; i < h; i++) { + line1 = data1 + i * wpl1; + line2 = data2 + i * wpl2; + lined = datad + i * wpl1; /* wpl1 works for pixd */ + for (j = 0; j < w; j++) { + word1 = *(line1 + j); + word2 = *(line2 + j); + rval = (l_int32)(rwt * ((word1 >> L_RED_SHIFT) & 0xff) + + gwt * ((word1 >> L_GREEN_SHIFT) & 0xff) + + bwt * ((word1 >> L_BLUE_SHIFT) & 0xff) + 0.5); + gval = (word2 >> L_GREEN_SHIFT) & 0xff; + bval = (word2 >> L_BLUE_SHIFT) & 0xff; + composeRGBPixel(rval, gval, bval, lined + j); + } + } + + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/watershed.c b/hgdriver/3rdparty/hgOCR/leptonica/watershed.c new file mode 100644 index 0000000..a858e80 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/watershed.c @@ -0,0 +1,1130 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file watershed.c + *
+ *
+ *      Top-level
+ *            L_WSHED         *wshedCreate()
+ *            void             wshedDestroy()
+ *            l_int32          wshedApply()
+ *
+ *      Helpers
+ *            static l_int32   identifyWatershedBasin()
+ *            static l_int32   mergeLookup()
+ *            static l_int32   wshedGetHeight()
+ *            static void      pushNewPixel()
+ *            static void      popNewPixel()
+ *            static void      pushWSPixel()
+ *            static void      popWSPixel()
+ *            static void      debugPrintLUT()
+ *            static void      debugWshedMerge()
+ *
+ *      Output
+ *            l_int32          wshedBasins()
+ *            PIX             *wshedRenderFill()
+ *            PIX             *wshedRenderColors()
+ *
+ *  The watershed function identifies the "catch basins" of the input
+ *  8 bpp image, with respect to the specified seeds or "markers".
+ *  The use is in segmentation, but the selection of the markers is
+ *  critical to getting meaningful results.
+ *
+ *  How are the markers selected?  You can't simply use the local
+ *  minima, because a typical image has sufficient noise so that
+ *  a useful catch basin can easily have multiple local minima.  However
+ *  they are selected, the question for the watershed function is
+ *  how to handle local minima that are not markers.  The reason
+ *  this is important is because of the algorithm used to find the
+ *  watersheds, which is roughly like this:
+ *
+ *    (1) Identify the markers and the local minima, and enter them
+ *        into a priority queue based on the pixel value.  Each marker
+ *        is shrunk to a single pixel, if necessary, before the
+ *        operation starts.
+ *    (2) Feed the priority queue with neighbors of pixels that are
+ *        popped off the queue.  Each of these queue pixels is labeled
+ *        with the index value of its parent.
+ *    (3) Each pixel is also labeled, in a 32-bit image, with the marker
+ *        or local minimum index, from which it was originally derived.
+ *    (4) There are actually 3 classes of labels: seeds, minima, and
+ *        fillers.  The fillers are labels of regions that have already
+ *        been identified as watersheds and are continuing to fill, for
+ *        the purpose of finding higher watersheds.
+ *    (5) When a pixel is popped that has already been labeled in the
+ *        32-bit image and that label differs from the label of its
+ *        parent (stored in the queue pixel), a boundary has been crossed.
+ *        There are several cases:
+ *         (a) Both parents are derived from markers but at least one
+ *             is not deep enough to become a watershed.  Absorb the
+ *             shallower basin into the deeper one, fixing the LUT to
+ *             redirect the shallower index to the deeper one.
+ *         (b) Both parents are derived from markers and both are deep
+ *             enough.  Identify and save the watershed for each marker.
+ *         (c) One parent was derived from a marker and the other from
+ *             a minima: absorb the minima basin into the marker basin.
+ *         (d) One parent was derived from a marker and the other is
+ *             a filler: identify and save the watershed for the marker.
+ *         (e) Both parents are derived from minima: merge them.
+ *         (f) One parent is a filler and the other is derived from a
+ *             minima: merge the minima into the filler.
+ *    (6) The output of the watershed operation consists of:
+ *         ~ a pixa of the basins
+ *         ~ a pta of the markers
+ *         ~ a numa of the watershed levels
+ *
+ *  Typical usage:
+ *      L_WShed *wshed = wshedCreate(pixs, pixseed, mindepth, 0);
+ *      wshedApply(wshed);
+ *
+ *      wshedBasins(wshed, &pixa, &nalevels);
+ *        ... do something with pixa, nalevels ...
+ *      pixaDestroy(&pixa);
+ *      numaDestroy(&nalevels);
+ *
+ *      Pix *pixd = wshedRenderFill(wshed);
+ *
+ *      wshedDestroy(&wshed);
+ * 
+ */ + +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_WATERSHED 0 +#endif /* ~NO_CONSOLE_IO */ + +static const l_uint32 MAX_LABEL_VALUE = 0x7fffffff; /* largest l_int32 */ + +/*! New pixel coordinates */ +struct L_NewPixel +{ + l_int32 x; /*!< x coordinate */ + l_int32 y; /*!< y coordinate */ +}; +typedef struct L_NewPixel L_NEWPIXEL; + +/*! Wartshed pixel */ +struct L_WSPixel +{ + l_float32 val; /*!< pixel value */ + l_int32 x; /*!< x coordinate */ + l_int32 y; /*!< y coordinate */ + l_int32 index; /*!< label for set to which pixel belongs */ +}; +typedef struct L_WSPixel L_WSPIXEL; + + + /* Static functions for obtaining bitmap of watersheds */ +static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level); + +static l_int32 identifyWatershedBasin(L_WSHED *wshed, + l_int32 index, l_int32 level, + BOX **pbox, PIX **ppixd); + + /* Static function for merging lut and backlink arrays */ +static l_int32 mergeLookup(L_WSHED *wshed, l_int32 sindex, l_int32 dindex); + + /* Static function for finding the height of the current pixel + above its seed or minima in the watershed. */ +static l_int32 wshedGetHeight(L_WSHED *wshed, l_int32 val, l_int32 label, + l_int32 *pheight); + + /* Static accessors for NewPixel on a queue */ +static void pushNewPixel(L_QUEUE *lq, l_int32 x, l_int32 y, + l_int32 *pminx, l_int32 *pmaxx, + l_int32 *pminy, l_int32 *pmaxy); +static void popNewPixel(L_QUEUE *lq, l_int32 *px, l_int32 *py); + + /* Static accessors for WSPixel on a heap */ +static void pushWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 val, + l_int32 x, l_int32 y, l_int32 index); +static void popWSPixel(L_HEAP *lh, L_STACK *stack, l_int32 *pval, + l_int32 *px, l_int32 *py, l_int32 *pindex); + + /* Static debug print output */ +static void debugPrintLUT(l_int32 *lut, l_int32 size, l_int32 debug); + +static void debugWshedMerge(L_WSHED *wshed, char *descr, l_int32 x, + l_int32 y, l_int32 label, l_int32 index); + + +/*-----------------------------------------------------------------------* + * Top-level watershed * + *-----------------------------------------------------------------------*/ +/*! + * \brief wshedCreate() + * + * \param[in] pixs 8 bpp source + * \param[in] pixm 1 bpp 'marker' seed + * \param[in] mindepth minimum depth; anything less is not saved + * \param[in] debugflag 1 for debug output + * \return WShed, or NULL on error + * + *
+ * Notes:
+ *      (1) It is not necessary for the fg pixels in the seed image
+ *          be at minima, or that they be isolated.  We extract a
+ *          single pixel from each connected component, and a seed
+ *          anywhere in a watershed will eventually label the watershed
+ *          when the filling level reaches it.
+ *      (2) Set mindepth to some value to ignore noise in pixs that
+ *          can create small local minima.  Any watershed shallower
+ *          than mindepth, even if it has a seed, will not be saved;
+ *          It will either be incorporated in another watershed or
+ *          eliminated.
+ * 
+ */ +L_WSHED * +wshedCreate(PIX *pixs, + PIX *pixm, + l_int32 mindepth, + l_int32 debugflag) +{ +l_int32 w, h; +L_WSHED *wshed; + + PROCNAME("wshedCreate"); + + if (!pixs) + return (L_WSHED *)ERROR_PTR("pixs is not defined", procName, NULL); + if (pixGetDepth(pixs) != 8) + return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", procName, NULL); + if (!pixm) + return (L_WSHED *)ERROR_PTR("pixm is not defined", procName, NULL); + if (pixGetDepth(pixm) != 1) + return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", procName, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h) + return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", procName, NULL); + + if ((wshed = (L_WSHED *)LEPT_CALLOC(1, sizeof(L_WSHED))) == NULL) + return (L_WSHED *)ERROR_PTR("wshed not made", procName, NULL); + + wshed->pixs = pixClone(pixs); + wshed->pixm = pixClone(pixm); + wshed->mindepth = L_MAX(1, mindepth); + wshed->pixlab = pixCreate(w, h, 32); + pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE); + wshed->pixt = pixCreate(w, h, 1); + wshed->lines8 = pixGetLinePtrs(pixs, NULL); + wshed->linem1 = pixGetLinePtrs(pixm, NULL); + wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL); + wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL); + wshed->debug = debugflag; + return wshed; +} + + +/*! + * \brief wshedDestroy() + * + * \param[in,out] pwshed will be set to null before returning + * \return void + */ +void +wshedDestroy(L_WSHED **pwshed) +{ +l_int32 i; +L_WSHED *wshed; + + PROCNAME("wshedDestroy"); + + if (pwshed == NULL) { + L_WARNING("ptr address is null!\n", procName); + return; + } + + if ((wshed = *pwshed) == NULL) + return; + + pixDestroy(&wshed->pixs); + pixDestroy(&wshed->pixm); + pixDestroy(&wshed->pixlab); + pixDestroy(&wshed->pixt); + if (wshed->lines8) LEPT_FREE(wshed->lines8); + if (wshed->linem1) LEPT_FREE(wshed->linem1); + if (wshed->linelab32) LEPT_FREE(wshed->linelab32); + if (wshed->linet1) LEPT_FREE(wshed->linet1); + pixaDestroy(&wshed->pixad); + ptaDestroy(&wshed->ptas); + numaDestroy(&wshed->nash); + numaDestroy(&wshed->nasi); + numaDestroy(&wshed->namh); + numaDestroy(&wshed->nalevels); + if (wshed->lut) + LEPT_FREE(wshed->lut); + if (wshed->links) { + for (i = 0; i < wshed->arraysize; i++) + numaDestroy(&wshed->links[i]); + LEPT_FREE(wshed->links); + } + LEPT_FREE(wshed); + *pwshed = NULL; + return; +} + + +/*! + * \brief wshedApply() + * + * \param[in] wshed generated from wshedCreate() + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) N.B. This is buggy!  It seems to locate watersheds that are
+ *          duplicates.  The watershed extraction after complete fill
+ *          grabs some regions belonging to existing watersheds.
+ *          See prog/watershedtest.c for testing.
+ * 
+ */ +l_ok +wshedApply(L_WSHED *wshed) +{ +char two_new_watersheds[] = "Two new watersheds"; +char seed_absorbed_into_seeded_basin[] = "Seed absorbed into seeded basin"; +char one_new_watershed_label[] = "One new watershed (label)"; +char one_new_watershed_index[] = "One new watershed (index)"; +char minima_absorbed_into_seeded_basin[] = + "Minima absorbed into seeded basin"; +char minima_absorbed_by_filler_or_another[] = + "Minima absorbed by filler or another"; +l_int32 nseeds, nother, nboth, arraysize; +l_int32 i, j, val, x, y, w, h, index, mindepth; +l_int32 imin, imax, jmin, jmax, cindex, clabel, nindex; +l_int32 hindex, hlabel, hmin, hmax, minhindex, maxhindex; +l_int32 *lut; +l_uint32 ulabel, uval; +void **lines8, **linelab32; +NUMA *nalut, *nalevels, *nash, *namh, *nasi; +NUMA **links; +L_HEAP *lh; +PIX *pixmin, *pixsd; +PIXA *pixad; +L_STACK *rstack; +PTA *ptas, *ptao; + + PROCNAME("wshedApply"); + + if (!wshed) + return ERROR_INT("wshed not defined", procName, 1); + + /* ------------------------------------------------------------ * + * Initialize priority queue and pixlab with seeds and minima * + * ------------------------------------------------------------ */ + + lh = lheapCreate(0, L_SORT_INCREASING); /* remove lowest values first */ + rstack = lstackCreate(0); /* for reusing the WSPixels */ + pixGetDimensions(wshed->pixs, &w, &h, NULL); + lines8 = wshed->lines8; /* wshed owns this */ + linelab32 = wshed->linelab32; /* ditto */ + + /* Identify seed (marker) pixels, 1 for each c.c. in pixm */ + pixSelectMinInConnComp(wshed->pixs, wshed->pixm, &ptas, &nash); + pixsd = pixGenerateFromPta(ptas, w, h); + nseeds = ptaGetCount(ptas); + for (i = 0; i < nseeds; i++) { + ptaGetIPt(ptas, i, &x, &y); + uval = GET_DATA_BYTE(lines8[y], x); + pushWSPixel(lh, rstack, (l_int32)uval, x, y, i); + } + wshed->ptas = ptas; + nasi = numaMakeConstant(1, nseeds); /* indicator array */ + wshed->nasi = nasi; + wshed->nash = nash; + wshed->nseeds = nseeds; + + /* Identify minima that are not seeds. Use these 4 steps: + * (1) Get the local minima, which can have components + * of arbitrary size. This will be a clipping mask. + * (2) Get the image of the actual seeds (pixsd) + * (3) Remove all elements of the clipping mask that have a seed. + * (4) Shrink each of the remaining elements of the minima mask + * to a single pixel. */ + pixLocalExtrema(wshed->pixs, 200, 0, &pixmin, NULL); + pixRemoveSeededComponents(pixmin, pixsd, pixmin, 8, 2); + pixSelectMinInConnComp(wshed->pixs, pixmin, &ptao, &namh); + nother = ptaGetCount(ptao); + for (i = 0; i < nother; i++) { + ptaGetIPt(ptao, i, &x, &y); + uval = GET_DATA_BYTE(lines8[y], x); + pushWSPixel(lh, rstack, (l_int32)uval, x, y, nseeds + i); + } + wshed->namh = namh; + + /* ------------------------------------------------------------ * + * Initialize merging lookup tables * + * ------------------------------------------------------------ */ + + /* nalut should always give the current after-merging index. + * links are effectively backpointers: they are numas associated with + * a dest index of all indices in nalut that point to that index. */ + mindepth = wshed->mindepth; + nboth = nseeds + nother; + arraysize = 2 * nboth; + wshed->arraysize = arraysize; + nalut = numaMakeSequence(0, 1, arraysize); + lut = numaGetIArray(nalut); + wshed->lut = lut; /* wshed owns this */ + links = (NUMA **)LEPT_CALLOC(arraysize, sizeof(NUMA *)); + wshed->links = links; /* wshed owns this */ + nindex = nseeds + nother; /* the next unused index value */ + + /* ------------------------------------------------------------ * + * Fill the basins, using the priority queue * + * ------------------------------------------------------------ */ + + pixad = pixaCreate(nseeds); + wshed->pixad = pixad; /* wshed owns this */ + nalevels = numaCreate(nseeds); + wshed->nalevels = nalevels; /* wshed owns this */ + L_INFO("nseeds = %d, nother = %d\n", procName, nseeds, nother); + while (lheapGetCount(lh) > 0) { + popWSPixel(lh, rstack, &val, &x, &y, &index); +/* fprintf(stderr, "x = %d, y = %d, index = %d\n", x, y, index); */ + ulabel = GET_DATA_FOUR_BYTES(linelab32[y], x); + if (ulabel == MAX_LABEL_VALUE) + clabel = ulabel; + else + clabel = lut[ulabel]; + cindex = lut[index]; + if (clabel == cindex) continue; /* have already seen this one */ + if (clabel == MAX_LABEL_VALUE) { /* new one; assign index and try to + * propagate to all neighbors */ + SET_DATA_FOUR_BYTES(linelab32[y], x, cindex); + imin = L_MAX(0, y - 1); + imax = L_MIN(h - 1, y + 1); + jmin = L_MAX(0, x - 1); + jmax = L_MIN(w - 1, x + 1); + for (i = imin; i <= imax; i++) { + for (j = jmin; j <= jmax; j++) { + if (i == y && j == x) continue; + uval = GET_DATA_BYTE(lines8[i], j); + pushWSPixel(lh, rstack, (l_int32)uval, j, i, cindex); + } + } + } else { /* pixel is already labeled (differently); must resolve */ + + /* If both indices are seeds, check if the min height is + * greater than mindepth. If so, we have two new watersheds; + * locate them and assign to both regions a new index + * for further waterfill. If not, absorb the shallower + * watershed into the deeper one and continue filling it. */ + pixGetPixel(pixsd, x, y, &uval); + if (clabel < nseeds && cindex < nseeds) { + wshedGetHeight(wshed, val, clabel, &hlabel); + wshedGetHeight(wshed, val, cindex, &hindex); + hmin = L_MIN(hlabel, hindex); + hmax = L_MAX(hlabel, hindex); + if (hmin == hmax) { + hmin = hlabel; + hmax = hindex; + } + if (wshed->debug) { + fprintf(stderr, "clabel,hlabel = %d,%d\n", clabel, hlabel); + fprintf(stderr, "hmin = %d, hmax = %d\n", hmin, hmax); + fprintf(stderr, "cindex,hindex = %d,%d\n", cindex, hindex); + if (hmin < mindepth) + fprintf(stderr, "Too shallow!\n"); + } + + if (hmin >= mindepth) { + debugWshedMerge(wshed, two_new_watersheds, + x, y, clabel, cindex); + wshedSaveBasin(wshed, cindex, val - 1); + wshedSaveBasin(wshed, clabel, val - 1); + numaSetValue(nasi, cindex, 0); + numaSetValue(nasi, clabel, 0); + + if (wshed->debug) fprintf(stderr, "nindex = %d\n", nindex); + debugPrintLUT(lut, nindex, wshed->debug); + mergeLookup(wshed, clabel, nindex); + debugPrintLUT(lut, nindex, wshed->debug); + mergeLookup(wshed, cindex, nindex); + debugPrintLUT(lut, nindex, wshed->debug); + nindex++; + } else /* extraneous seed within seeded basin; absorb */ { + debugWshedMerge(wshed, seed_absorbed_into_seeded_basin, + x, y, clabel, cindex); + } + maxhindex = clabel; /* TODO: is this part of above 'else'? */ + minhindex = cindex; + if (hindex > hlabel) { + maxhindex = cindex; + minhindex = clabel; + } + mergeLookup(wshed, minhindex, maxhindex); + } else if (clabel < nseeds && cindex >= nboth) { + /* If one index is a seed and the other is a merge of + * 2 watersheds, generate a single watershed. */ + debugWshedMerge(wshed, one_new_watershed_label, + x, y, clabel, cindex); + wshedSaveBasin(wshed, clabel, val - 1); + numaSetValue(nasi, clabel, 0); + mergeLookup(wshed, clabel, cindex); + } else if (cindex < nseeds && clabel >= nboth) { + debugWshedMerge(wshed, one_new_watershed_index, + x, y, clabel, cindex); + wshedSaveBasin(wshed, cindex, val - 1); + numaSetValue(nasi, cindex, 0); + mergeLookup(wshed, cindex, clabel); + } else if (clabel < nseeds) { /* cindex from minima; absorb */ + /* If one index is a seed and the other is from a minimum, + * merge the minimum wshed into the seed wshed. */ + debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, + x, y, clabel, cindex); + mergeLookup(wshed, cindex, clabel); + } else if (cindex < nseeds) { /* clabel from minima; absorb */ + debugWshedMerge(wshed, minima_absorbed_into_seeded_basin, + x, y, clabel, cindex); + mergeLookup(wshed, clabel, cindex); + } else { /* If neither index is a seed, just merge */ + debugWshedMerge(wshed, minima_absorbed_by_filler_or_another, + x, y, clabel, cindex); + mergeLookup(wshed, clabel, cindex); + } + } + } + +#if 0 + /* Use the indicator array to save any watersheds that fill + * to the maximum value. This seems to screw things up! */ + for (i = 0; i < nseeds; i++) { + numaGetIValue(nasi, i, &ival); + if (ival == 1) { + wshedSaveBasin(wshed, lut[i], val - 1); + numaSetValue(nasi, i, 0); + } + } +#endif + + numaDestroy(&nalut); + pixDestroy(&pixmin); + pixDestroy(&pixsd); + ptaDestroy(&ptao); + lheapDestroy(&lh, TRUE); + lstackDestroy(&rstack, TRUE); + return 0; +} + + +/*-----------------------------------------------------------------------* + * Helpers * + *-----------------------------------------------------------------------*/ +/*! + * \brief wshedSaveBasin() + * + * \param[in] wshed + * \param[in] index index of basin to be located + * \param[in] level filling level reached at the time this function + * is called + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This identifies a single watershed.  It does not change
+ *          the LUT, which must be done subsequently.
+ *      (2) The fill level of a basin is taken to be %level - 1.
+ * 
+ */ +static void +wshedSaveBasin(L_WSHED *wshed, + l_int32 index, + l_int32 level) +{ +BOX *box; +PIX *pix; + + PROCNAME("wshedSaveBasin"); + + if (!wshed) { + L_ERROR("wshed not defined\n", procName); + return; + } + + if (identifyWatershedBasin(wshed, index, level, &box, &pix) == 0) { + pixaAddPix(wshed->pixad, pix, L_INSERT); + pixaAddBox(wshed->pixad, box, L_INSERT); + numaAddNumber(wshed->nalevels, level - 1); + } + return; +} + + +/*! + * \brief identifyWatershedBasin() + * + * \param[in] wshed + * \param[in] index index of basin to be located + * \param[in] level of basin at point at which the two basins met + * \param[out] pbox bounding box of basin + * \param[out] ppixd pix of basin, cropped to its bounding box + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) This is a static function, so we assume pixlab, pixs and pixt
+ *          exist and are the same size.
+ *      (2) It selects all pixels that have the label %index in pixlab
+ *          and that have a value in pixs that is less than %level.
+ *      (3) It is used whenever two seeded basins meet (typically at a saddle),
+ *          or when one seeded basin meets a 'filler'.  All identified
+ *          basins are saved as a watershed.
+ * 
+ */ +static l_int32 +identifyWatershedBasin(L_WSHED *wshed, + l_int32 index, + l_int32 level, + BOX **pbox, + PIX **ppixd) +{ +l_int32 imin, imax, jmin, jmax, minx, miny, maxx, maxy; +l_int32 bw, bh, i, j, w, h, x, y; +l_int32 *lut; +l_uint32 label, bval, lval; +void **lines8, **linelab32, **linet1; +BOX *box; +PIX *pixs, *pixt, *pixd; +L_QUEUE *lq; + + PROCNAME("identifyWatershedBasin"); + + if (!pbox) + return ERROR_INT("&box not defined", procName, 1); + *pbox = NULL; + if (!ppixd) + return ERROR_INT("&pixd not defined", procName, 1); + *ppixd = NULL; + if (!wshed) + return ERROR_INT("wshed not defined", procName, 1); + + /* Make a queue and an auxiliary stack */ + lq = lqueueCreate(0); + lq->stack = lstackCreate(0); + + pixs = wshed->pixs; + pixt = wshed->pixt; + lines8 = wshed->lines8; + linelab32 = wshed->linelab32; + linet1 = wshed->linet1; + lut = wshed->lut; + pixGetDimensions(pixs, &w, &h, NULL); + + /* Prime the queue with the seed pixel for this watershed. */ + minx = miny = 1000000; + maxx = maxy = 0; + ptaGetIPt(wshed->ptas, index, &x, &y); + pixSetPixel(pixt, x, y, 1); + pushNewPixel(lq, x, y, &minx, &maxx, &miny, &maxy); + if (wshed->debug) fprintf(stderr, "prime: (x,y) = (%d, %d)\n", x, y); + + /* Each pixel in a spreading breadth-first search is inspected. + * It is accepted as part of this watershed, and pushed on + * the search queue, if: + * (1) It has a label value equal to %index + * (2) The pixel value is less than %level, the overflow + * height at which the two basins join. + * (3) It has not yet been seen in this search. */ + while (lqueueGetCount(lq) > 0) { + popNewPixel(lq, &x, &y); + imin = L_MAX(0, y - 1); + imax = L_MIN(h - 1, y + 1); + jmin = L_MAX(0, x - 1); + jmax = L_MIN(w - 1, x + 1); + for (i = imin; i <= imax; i++) { + for (j = jmin; j <= jmax; j++) { + if (j == x && i == y) continue; /* parent */ + label = GET_DATA_FOUR_BYTES(linelab32[i], j); + if (label == MAX_LABEL_VALUE || lut[label] != index) continue; + bval = GET_DATA_BIT(linet1[i], j); + if (bval == 1) continue; /* already seen */ + lval = GET_DATA_BYTE(lines8[i], j); + if (lval >= level) continue; /* too high */ + SET_DATA_BIT(linet1[i], j); + pushNewPixel(lq, j, i, &minx, &maxx, &miny, &maxy); + } + } + } + + /* Extract the box and pix, and clear pixt */ + bw = maxx - minx + 1; + bh = maxy - miny + 1; + box = boxCreate(minx, miny, bw, bh); + pixd = pixClipRectangle(pixt, box, NULL); + pixRasterop(pixt, minx, miny, bw, bh, PIX_SRC ^ PIX_DST, pixd, 0, 0); + *pbox = box; + *ppixd = pixd; + + lqueueDestroy(&lq, 1); + return 0; +} + + +/*! + * \brief mergeLookup() + * + * \param[in] wshed + * \param[in] sindex primary index being changed in the merge + * \param[in] dindex index that %sindex will point to after the merge + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The links are a sparse array of Numas showing current back-links.
+ *          The lut gives the current index (of the seed or the minima
+ *          for the wshed  in which it is located.
+ *      (2) Think of each entry in the lut.  There are two types:
+ *             owner:     lut[index] = index
+ *             redirect:  lut[index] != index
+ *      (3) This is called each time a merge occurs.  It puts the lut
+ *          and backlinks in a canonical form after the merge, where
+ *          all entries in the lut point to the current "owner", which
+ *          has all backlinks.  That is, every "redirect" in the lut
+ *          points to an "owner".  The lut always gives the index of
+ *          the current owner.
+ * 
+ */ +static l_int32 +mergeLookup(L_WSHED *wshed, + l_int32 sindex, + l_int32 dindex) +{ +l_int32 i, n, size, index; +l_int32 *lut; +NUMA *na; +NUMA **links; + + PROCNAME("mergeLookup"); + + if (!wshed) + return ERROR_INT("wshed not defined", procName, 1); + size = wshed->arraysize; + if (sindex < 0 || sindex >= size) + return ERROR_INT("invalid sindex", procName, 1); + if (dindex < 0 || dindex >= size) + return ERROR_INT("invalid dindex", procName, 1); + + /* Redirect links in the lut */ + n = 0; + links = wshed->links; + lut = wshed->lut; + if ((na = links[sindex]) != NULL) { + n = numaGetCount(na); + for (i = 0; i < n; i++) { + numaGetIValue(na, i, &index); + lut[index] = dindex; + } + } + lut[sindex] = dindex; + + /* Shift the backlink arrays from sindex to dindex. + * sindex should have no backlinks because all entries in the + * lut that were previously pointing to it have been redirected + * to dindex. */ + if (!links[dindex]) + links[dindex] = numaCreate(n); + numaJoin(links[dindex], links[sindex], 0, -1); + numaAddNumber(links[dindex], sindex); + numaDestroy(&links[sindex]); + + return 0; +} + + +/*! + * \brief wshedGetHeight() + * + * \param[in] wshed array of current indices + * \param[in] val value of current pixel popped off queue + * \param[in] label of pixel or 32 bpp label image + * \param[out] pheight height of current value from seed + * or minimum of watershed + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) It is only necessary to find the height for a watershed
+ *          that is indexed by a seed or a minima.  This function should
+ *          not be called on a finished watershed (that continues to fill).
+ * 
+ */ +static l_int32 +wshedGetHeight(L_WSHED *wshed, + l_int32 val, + l_int32 label, + l_int32 *pheight) +{ +l_int32 minval; + + PROCNAME("wshedGetHeight"); + + if (!pheight) + return ERROR_INT("&height not defined", procName, 1); + *pheight = 0; + if (!wshed) + return ERROR_INT("wshed not defined", procName, 1); + + if (label < wshed->nseeds) + numaGetIValue(wshed->nash, label, &minval); + else if (label < wshed->nseeds + wshed->nother) + numaGetIValue(wshed->namh, label, &minval); + else + return ERROR_INT("finished watershed; should not call", procName, 1); + + *pheight = val - minval; + return 0; +} + + +/* + * \brief pushNewPixel() + * + * \param[in] lqueue + * \param[in] x, y pixel coordinates + * \param[out] pminx, pmaxx, pminy, pmaxy bounding box update + * \return void + * + *
+ * Notes:
+ *      (1) This is a wrapper for adding a NewPixel to a queue, which
+ *          updates the bounding box for all pixels on that queue and
+ *          uses the storage stack to retrieve a NewPixel.
+ * 
+ */ +static void +pushNewPixel(L_QUEUE *lq, + l_int32 x, + l_int32 y, + l_int32 *pminx, + l_int32 *pmaxx, + l_int32 *pminy, + l_int32 *pmaxy) +{ +L_NEWPIXEL *np; + + PROCNAME("pushNewPixel"); + + if (!lq) { + L_ERROR("queue not defined\n", procName); + return; + } + + /* Adjust bounding box */ + *pminx = L_MIN(*pminx, x); + *pmaxx = L_MAX(*pmaxx, x); + *pminy = L_MIN(*pminy, y); + *pmaxy = L_MAX(*pmaxy, y); + + /* Get a newpixel to use */ + if (lstackGetCount(lq->stack) > 0) + np = (L_NEWPIXEL *)lstackRemove(lq->stack); + else + np = (L_NEWPIXEL *)LEPT_CALLOC(1, sizeof(L_NEWPIXEL)); + + np->x = x; + np->y = y; + lqueueAdd(lq, np); + return; +} + + +/* + * \brief popNewPixel() + * + * \param[in] lqueue + * \param[out] px, py pixel coordinates + * \return void + * + *
+ * Notes:
+ *      (1) This is a wrapper for removing a NewPixel from a queue,
+ *          which returns the pixel coordinates and saves the NewPixel
+ *          on the storage stack.
+ * 
+ */ +static void +popNewPixel(L_QUEUE *lq, + l_int32 *px, + l_int32 *py) +{ +L_NEWPIXEL *np; + + PROCNAME("popNewPixel"); + + if (!lq) { + L_ERROR("lqueue not defined\n", procName); + return; + } + + if ((np = (L_NEWPIXEL *)lqueueRemove(lq)) == NULL) + return; + *px = np->x; + *py = np->y; + lstackAdd(lq->stack, np); /* save for re-use */ + return; +} + + +/* + * \brief pushWSPixel() + * + * \param[in] lh priority queue + * \param[in] stack of reusable WSPixels + * \param[in] val pixel value: used for ordering the heap + * \param[in] x, y pixel coordinates + * \param[in] index label for set to which pixel belongs + * \return void + * + *
+ * Notes:
+ *      (1) This is a wrapper for adding a WSPixel to a heap.  It
+ *          uses the storage stack to retrieve a WSPixel.
+ * 
+ */ +static void +pushWSPixel(L_HEAP *lh, + L_STACK *stack, + l_int32 val, + l_int32 x, + l_int32 y, + l_int32 index) +{ +L_WSPIXEL *wsp; + + PROCNAME("pushWSPixel"); + + if (!lh) { + L_ERROR("heap not defined\n", procName); + return; + } + if (!stack) { + L_ERROR("stack not defined\n", procName); + return; + } + + /* Get a wspixel to use */ + if (lstackGetCount(stack) > 0) + wsp = (L_WSPIXEL *)lstackRemove(stack); + else + wsp = (L_WSPIXEL *)LEPT_CALLOC(1, sizeof(L_WSPIXEL)); + + wsp->val = (l_float32)val; + wsp->x = x; + wsp->y = y; + wsp->index = index; + lheapAdd(lh, wsp); + return; +} + + +/* + * \brief popWSPixel() + * + * \param[in] lh priority queue + * \param[in] stack of reusable WSPixels + * \param[out] pval pixel value + * \param[out] px, py pixel coordinates + * \param[out] pindex label for set to which pixel belongs + * \return void + * + *
+ * Notes:
+ *      (1) This is a wrapper for removing a WSPixel from a heap,
+ *          which returns the WSPixel data and saves the WSPixel
+ *          on the storage stack.
+ * 
+ */ +static void +popWSPixel(L_HEAP *lh, + L_STACK *stack, + l_int32 *pval, + l_int32 *px, + l_int32 *py, + l_int32 *pindex) +{ +L_WSPIXEL *wsp; + + PROCNAME("popWSPixel"); + + if (!lh) { + L_ERROR("lheap not defined\n", procName); + return; + } + if (!stack) { + L_ERROR("stack not defined\n", procName); + return; + } + if (!pval || !px || !py || !pindex) { + L_ERROR("data can't be returned\n", procName); + return; + } + + if ((wsp = (L_WSPIXEL *)lheapRemove(lh)) == NULL) + return; + *pval = (l_int32)wsp->val; + *px = wsp->x; + *py = wsp->y; + *pindex = wsp->index; + lstackAdd(stack, wsp); /* save for re-use */ + return; +} + + +static void +debugPrintLUT(l_int32 *lut, + l_int32 size, + l_int32 debug) +{ +l_int32 i; + + if (!debug) return; + fprintf(stderr, "lut: "); + for (i = 0; i < size; i++) + fprintf(stderr, "%d ", lut[i]); + fprintf(stderr, "\n"); + return; +} + + +static void +debugWshedMerge(L_WSHED *wshed, + char *descr, + l_int32 x, + l_int32 y, + l_int32 label, + l_int32 index) +{ + if (!wshed || (wshed->debug == 0)) + return; + fprintf(stderr, "%s:\n", descr); + fprintf(stderr, " (x, y) = (%d, %d)\n", x, y); + fprintf(stderr, " clabel = %d, cindex = %d\n", label, index); + return; +} + + +/*-----------------------------------------------------------------------* + * Output * + *-----------------------------------------------------------------------*/ +/*! + * \brief wshedBasins() + * + * \param[in] wshed + * \param[out] ppixa [optional] mask of watershed basins + * \param[out] pnalevels [optional] watershed levels + * \return 0 if OK, 1 on error + */ +l_ok +wshedBasins(L_WSHED *wshed, + PIXA **ppixa, + NUMA **pnalevels) +{ + PROCNAME("wshedBasins"); + + if (!wshed) + return ERROR_INT("wshed not defined", procName, 1); + + if (ppixa) + *ppixa = pixaCopy(wshed->pixad, L_CLONE); + if (pnalevels) + *pnalevels = numaClone(wshed->nalevels); + return 0; +} + + +/*! + * \brief wshedRenderFill() + * + * \param[in] wshed + * \return pixd initial image with all basins filled, or NULL on error + */ +PIX * +wshedRenderFill(L_WSHED *wshed) +{ +l_int32 i, n, level, bx, by; +NUMA *na; +PIX *pix, *pixd; +PIXA *pixa; + + PROCNAME("wshedRenderFill"); + + if (!wshed) + return (PIX *)ERROR_PTR("wshed not defined", procName, NULL); + + wshedBasins(wshed, &pixa, &na); + pixd = pixCopy(NULL, wshed->pixs); + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); + numaGetIValue(na, i, &level); + pixPaintThroughMask(pixd, pix, bx, by, level); + pixDestroy(&pix); + } + + pixaDestroy(&pixa); + numaDestroy(&na); + return pixd; +} + + +/*! + * \brief wshedRenderColors() + * + * \param[in] wshed + * \return pixd initial image with all basins filled, or null on error + */ +PIX * +wshedRenderColors(L_WSHED *wshed) +{ +l_int32 w, h; +PIX *pixg, *pixt, *pixc, *pixm, *pixd; +PIXA *pixa; + + PROCNAME("wshedRenderColors"); + + if (!wshed) + return (PIX *)ERROR_PTR("wshed not defined", procName, NULL); + + wshedBasins(wshed, &pixa, NULL); + pixg = pixCopy(NULL, wshed->pixs); + pixGetDimensions(wshed->pixs, &w, &h, NULL); + pixd = pixConvertTo32(pixg); + pixt = pixaDisplayRandomCmap(pixa, w, h); + pixc = pixConvertTo32(pixt); + pixm = pixaDisplay(pixa, w, h); + pixCombineMasked(pixd, pixc, pixm); + + pixDestroy(&pixg); + pixDestroy(&pixt); + pixDestroy(&pixc); + pixDestroy(&pixm); + pixaDestroy(&pixa); + return pixd; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/watershed.h b/hgdriver/3rdparty/hgOCR/leptonica/watershed.h new file mode 100644 index 0000000..d6b2077 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/watershed.h @@ -0,0 +1,64 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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 LEPTONICA_WATERSHED_H +#define LEPTONICA_WATERSHED_H + +/*! + * \file watershed.h + * + * Simple data structure to hold watershed data. + * All data here is owned by the L_WShed and must be freed. + */ + +/*! Simple data structure to hold watershed data. */ +struct L_WShed +{ + struct Pix *pixs; /*!< clone of input 8 bpp pixs */ + struct Pix *pixm; /*!< clone of input 1 bpp seed (marker) pixm */ + l_int32 mindepth; /*!< minimum depth allowed for a watershed */ + struct Pix *pixlab; /*!< 16 bpp label pix */ + struct Pix *pixt; /*!< scratch pix for computing wshed regions */ + void **lines8; /*!< line ptrs for pixs */ + void **linem1; /*!< line ptrs for pixm */ + void **linelab32; /*!< line ptrs for pixlab */ + void **linet1; /*!< line ptrs for pixt */ + struct Pixa *pixad; /*!< result: 1 bpp pixa of watersheds */ + struct Pta *ptas; /*!< pta of initial seed pixels */ + struct Numa *nasi; /*!< numa of seed indicators; 0 if completed */ + struct Numa *nash; /*!< numa of initial seed heights */ + struct Numa *namh; /*!< numa of initial minima heights */ + struct Numa *nalevels; /*!< result: numa of watershed levels */ + l_int32 nseeds; /*!< number of seeds (markers) */ + l_int32 nother; /*!< number of minima different from seeds */ + l_int32 *lut; /*!< lut for pixel indices */ + struct Numa **links; /*!< back-links into lut, for updates */ + l_int32 arraysize; /*!< size of links array */ + l_int32 debug; /*!< set to 1 for debug output */ +}; +typedef struct L_WShed L_WSHED; + +#endif /* LEPTONICA_WATERSHED_H */ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/webpanimio.c b/hgdriver/3rdparty/hgOCR/leptonica/webpanimio.c new file mode 100644 index 0000000..e9f7696 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/webpanimio.c @@ -0,0 +1,273 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file webpanimio.c + *
+ *
+ *    Writing animated WebP
+ *          l_int32          pixaWriteWebPAnim()
+ *          l_int32          pixaWriteStreamWebPAnim()
+ *          l_int32          pixaWriteMemWebPAnim()
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* -----------------------------------------------*/ +#if HAVE_LIBWEBP_ANIM /* defined in environ.h */ +/* -----------------------------------------------*/ +#include "webp/decode.h" +#include "webp/encode.h" +#include "webp/mux.h" +#include "webp/demux.h" + +/*---------------------------------------------------------------------* + * Writing animated WebP * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaWriteWebPAnim() + * + * \param[in] filename + * \param[in] pixa with images of all depths; cmap OK + * \param[in] loopcount [0 for infinite] + * \param[in] duration in ms, for each image + * \param[in] quality 0 - 100 for lossy; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Special top-level function allowing specification of quality.
+ * 
+ */ +l_ok +pixaWriteWebPAnim(const char *filename, + PIXA *pixa, + l_int32 loopcount, + l_int32 duration, + l_int32 quality, + l_int32 lossless) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixaWriteWebPAnim"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixaWriteStreamWebPAnim(fp, pixa, loopcount, duration, + quality, lossless); + fclose(fp); + if (ret) + return ERROR_INT("pixs not compressed to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixaWriteStreamWebPAnim() + * + * \param[in] fp file stream + * \param[in] pixa with images of all depths; cmap OK + * \param[in] loopcount [0 for infinite] + * \param[in] duration in ms, for each image + * \param[in] quality 0 - 100 for lossy; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteMemWebP() for details.
+ *      (2) Use 'free', and not leptonica's 'LEPT_FREE', for all heap data
+ *          that is returned from the WebP library.
+ * 
+ */ +l_ok +pixaWriteStreamWebPAnim(FILE *fp, + PIXA *pixa, + l_int32 loopcount, + l_int32 duration, + l_int32 quality, + l_int32 lossless) +{ +l_uint8 *filedata; +size_t filebytes, nbytes; + + PROCNAME("pixaWriteStreamWebpAnim"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + filedata = NULL; + pixaWriteMemWebPAnim(&filedata, &filebytes, pixa, loopcount, + duration, quality, lossless); + rewind(fp); + if (!filedata) + return ERROR_INT("filedata not made", procName, 1); + nbytes = fwrite(filedata, 1, filebytes, fp); + free(filedata); + if (nbytes != filebytes) + return ERROR_INT("Write error", procName, 1); + return 0; +} + + +/*! + * \brief pixaWriteMemWebPAnim() + * + * \param[out] pencdata webp encoded data of pixs + * \param[out] pencsize size of webp encoded data + * \param[in] pixa with images of any depth, cmapped OK + * \param[in] loopcount [0 for infinite] + * \param[in] duration in ms, for each image + * \param[in] quality 0 - 100 for lossy; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteMemWebP() for details of webp encoding of images.
+ * 
+ */ +l_ok +pixaWriteMemWebPAnim(l_uint8 **pencdata, + size_t *pencsize, + PIXA *pixa, + l_int32 loopcount, + l_int32 duration, + l_int32 quality, + l_int32 lossless) +{ +l_int32 i, n, same, w, h, wpl, ret; +l_uint8 *data; +PIX *pix1, *pix2; +WebPAnimEncoder *enc; +WebPAnimEncoderOptions enc_options; +WebPConfig config; +WebPData webp_data; +WebPMux *mux = NULL; +WebPMuxAnimParams newparams; +WebPPicture frame; + + PROCNAME("pixaWriteMemWebPAnim"); + + if (!pencdata) + return ERROR_INT("&encdata not defined", procName, 1); + *pencdata = NULL; + if (!pencsize) + return ERROR_INT("&encsize not defined", procName, 1); + *pencsize = 0; + if (!pixa) + return ERROR_INT("&pixa not defined", procName, 1); + if ((n = pixaGetCount(pixa)) == 0) + return ERROR_INT("no images in pixa", procName, 1); + if (loopcount < 0) loopcount = 0; + if (lossless == 0 && (quality < 0 || quality > 100)) + return ERROR_INT("quality not in [0 ... 100]", procName, 1); + + pixaVerifyDimensions(pixa, &same, &w, &h); + if (!same) + return ERROR_INT("sizes of all pix are not the same", procName, 1); + + /* Set up the encoder */ + WebPAnimEncoderOptionsInit(&enc_options); + enc = WebPAnimEncoderNew(w, h, &enc_options); + + for (i = 0; i < n; i++) { + /* Make a frame for each image. Convert the pix to RGBA with + * an opaque alpha layer, and put the raster data in the frame. */ + pix1 = pixaGetPix(pixa, i, L_CLONE); + pix2 = pixConvertTo32(pix1); + pixSetComponentArbitrary(pix2, L_ALPHA_CHANNEL, 255); + pixEndianByteSwap(pix2); + data = (l_uint8 *)pixGetData(pix2); + wpl = pixGetWpl(pix2); + WebPPictureInit(&frame); + frame.width = w; + frame.height = h; + WebPPictureImportRGBA(&frame, data, 4 * wpl); + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* Add the frame data to the encoder, and clear its memory */ + WebPConfigInit(&config); + config.lossless = lossless; + config.quality = quality; + WebPAnimEncoderAdd(enc, &frame, duration * i, &config); + WebPPictureFree(&frame); + } + WebPAnimEncoderAdd(enc, NULL, duration * i, NULL); /* add a blank frame */ + WebPAnimEncoderAssemble(enc, &webp_data); /* encode the data */ + WebPAnimEncoderDelete(enc); + + /* Set the loopcount if requested. Note that when you make a mux, + * it imports the webp_data that was previously made, including + * the webp encoded images. Before you re-export that data using + * WebPMuxAssemble(), free the heap data in webp_data. There is an + * example for setting the loop count in the webp distribution; + * see gif2webp.c. */ + if (loopcount > 0) { + mux = WebPMuxCreate(&webp_data, 1); + if (!mux) { + L_ERROR("could not re-mux to add loop count\n", procName); + } else { + ret = WebPMuxGetAnimationParams(mux, &newparams); + if (ret != WEBP_MUX_OK) { + L_ERROR("failed to get loop count\n", procName); + } else { + newparams.loop_count = loopcount; + ret = WebPMuxSetAnimationParams(mux, &newparams); + if (ret != WEBP_MUX_OK) + L_ERROR("failed to set loop count\n", procName); + } + WebPDataClear(&webp_data); + WebPMuxAssemble(mux, &webp_data); + WebPMuxDelete(mux); + } + } + + *pencdata = (l_uint8 *)webp_data.bytes; + *pencsize = webp_data.size; + L_INFO("data size = %zu\n", procName, webp_data.size); + return 0; +} + + +/* --------------------------------------------*/ +#endif /* HAVE_LIBWEBP_ANIM */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/webpanimiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/webpanimiostub.c new file mode 100644 index 0000000..10fa133 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/webpanimiostub.c @@ -0,0 +1,71 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file webpanimiostub.c + *
+ *
+ *     Stubs for webpanimio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* -----------------------------------------------*/ +#if !HAVE_LIBWEBP_ANIM /* defined in environ.h */ +/* -----------------------------------------------*/ + +l_ok pixaWriteWebPAnim(const char *filename, PIXA *pixa, l_int32 loopcount, + l_int32 duration, l_int32 quality, l_int32 lossless) +{ + return ERROR_INT("function not present", "pixaWriteWebPAnim", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaWriteStreamWebPAnim(FILE *fp, PIXA *pixa, l_int32 loopcount, + l_int32 duration, l_int32 quality, + l_int32 lossless) +{ + return ERROR_INT("function not present", "pixaWriteStreamWebPAnim", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixaWriteMemWebPAnim(l_uint8 **pencdata, size_t *pencsize, PIXA *pixa, + l_int32 loopcount, l_int32 duration, + l_int32 quality, l_int32 lossless) +{ + return ERROR_INT("function not present", "pixaWriteMemWebPAnim", 1); +} + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBWEBP_ANIM */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/webpio.c b/hgdriver/3rdparty/hgOCR/leptonica/webpio.c new file mode 100644 index 0000000..8d00bed --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/webpio.c @@ -0,0 +1,417 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file webpio.c + *
+ *
+ *    Reading WebP
+ *          PIX             *pixReadStreamWebP()
+ *          PIX             *pixReadMemWebP()
+ *
+ *    Reading WebP header
+ *          l_int32          readHeaderWebP()
+ *          l_int32          readHeaderMemWebP()
+ *
+ *    Writing WebP
+ *          l_int32          pixWriteWebP()  [ special top level ]
+ *          l_int32          pixWriteStreamWebP()
+ *          l_int32          pixWriteMemWebP()
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if HAVE_LIBWEBP /* defined in environ.h */ +/* --------------------------------------------*/ +#include "webp/decode.h" +#include "webp/encode.h" + +/*---------------------------------------------------------------------* + * Reading WebP * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadStreamWebP() + * + * \param[in] fp file stream corresponding to WebP image + * \return pix 32 bpp, or NULL on error + */ +PIX * +pixReadStreamWebP(FILE *fp) +{ +l_uint8 *filedata; +size_t filesize; +PIX *pix; + + PROCNAME("pixReadStreamWebP"); + + if (!fp) + return (PIX *)ERROR_PTR("fp not defined", procName, NULL); + + /* Read data from file and decode into Y,U,V arrays */ + rewind(fp); + if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL) + return (PIX *)ERROR_PTR("filedata not read", procName, NULL); + + pix = pixReadMemWebP(filedata, filesize); + LEPT_FREE(filedata); + return pix; +} + + +/*! + * \brief pixReadMemWebP() + * + * \param[in] filedata webp compressed data in memory + * \param[in] filesize number of bytes in data + * \return pix 32 bpp, or NULL on error + * + *
+ * Notes:
+ *      (1) When the encoded data only has 3 channels (no alpha),
+ *          WebPDecodeRGBAInto() generates a raster of 32-bit pixels, with
+ *          the alpha channel set to opaque (255).
+ *      (2) We don't need to use the gnu runtime functions like fmemopen()
+ *          for redirecting data from a stream to memory, because
+ *          the webp library has been written with memory-to-memory
+ *          functions at the lowest level (which is good!).  And, in
+ *          any event, fmemopen() doesn't work with l_binaryReadStream().
+ * 
+ */ +PIX * +pixReadMemWebP(const l_uint8 *filedata, + size_t filesize) +{ +l_uint8 *out = NULL; +l_int32 w, h, has_alpha, wpl, stride; +l_uint32 *data; +size_t size; +PIX *pix; +WebPBitstreamFeatures features; + + PROCNAME("pixReadMemWebP"); + + if (!filedata) + return (PIX *)ERROR_PTR("filedata not defined", procName, NULL); + + if (WebPGetFeatures(filedata, filesize, &features)) + return (PIX *)ERROR_PTR("Invalid WebP file", procName, NULL); + w = features.width; + h = features.height; + has_alpha = features.has_alpha; + + /* Write from compressed Y,U,V arrays to pix raster data */ + pix = pixCreate(w, h, 32); + pixSetInputFormat(pix, IFF_WEBP); + if (has_alpha) pixSetSpp(pix, 4); + data = pixGetData(pix); + wpl = pixGetWpl(pix); + stride = wpl * 4; + size = (size_t)stride * h; + out = WebPDecodeRGBAInto(filedata, filesize, (uint8_t *)data, size, + stride); + if (out == NULL) { /* error: out should also point to data */ + pixDestroy(&pix); + return (PIX *)ERROR_PTR("WebP decode failed", procName, NULL); + } + + /* The WebP API expects data in RGBA order. The pix stores + * in host-dependent order with R as the MSB and A as the LSB. + * On little-endian machines, the bytes in the word must + * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). + * No swapping is necessary for big-endians. */ + pixEndianByteSwap(pix); + return pix; +} + + +/*! + * \brief readHeaderWebP() + * + * \param[in] filename + * \param[out] pw width + * \param[out] ph height + * \param[out] pspp spp (3 or 4) + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderWebP(const char *filename, + l_int32 *pw, + l_int32 *ph, + l_int32 *pspp) +{ +l_uint8 data[100]; /* expect size info within the first 50 bytes or so */ +l_int32 nbytes, bytesread; +size_t filesize; +FILE *fp; + + PROCNAME("readHeaderWebP"); + + if (!pw || !ph || !pspp) + return ERROR_INT("input ptr(s) not defined", procName, 1); + *pw = *ph = *pspp = 0; + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + /* Read no more than 100 bytes from the file */ + if ((filesize = nbytesInFile(filename)) == 0) + return ERROR_INT("no file size found", procName, 1); + if (filesize < 100) + L_WARNING("very small webp file\n", procName); + nbytes = L_MIN(filesize, 100); + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT("image file not found", procName, 1); + bytesread = fread(data, 1, nbytes, fp); + fclose(fp); + if (bytesread != nbytes) + return ERROR_INT("failed to read requested data", procName, 1); + + return readHeaderMemWebP(data, nbytes, pw, ph, pspp); +} + + +/*! + * \brief readHeaderMemWebP() + * + * \param[in] data + * \param[in] size 100 bytes is sufficient + * \param[out] pw width + * \param[out] ph height + * \param[out] pspp spp (3 or 4) + * \return 0 if OK, 1 on error + */ +l_ok +readHeaderMemWebP(const l_uint8 *data, + size_t size, + l_int32 *pw, + l_int32 *ph, + l_int32 *pspp) +{ +WebPBitstreamFeatures features; + + PROCNAME("readHeaderWebP"); + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pspp) *pspp = 0; + if (!data) + return ERROR_INT("data not defined", procName, 1); + if (!pw || !ph || !pspp) + return ERROR_INT("input ptr(s) not defined", procName, 1); + + if (WebPGetFeatures(data, (l_int32)size, &features)) + return ERROR_INT("invalid WebP file", procName, 1); + *pw = features.width; + *ph = features.height; + *pspp = (features.has_alpha) ? 4 : 3; + return 0; +} + + +/*---------------------------------------------------------------------* + * Writing WebP * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteWebP() + * + * \param[in] filename + * \param[in] pixs + * \param[in] quality 0 - 100; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Special top-level function allowing specification of quality.
+ * 
+ */ +l_ok +pixWriteWebP(const char *filename, + PIX *pixs, + l_int32 quality, + l_int32 lossless) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixWriteWebP"); + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if ((fp = fopenWriteStream(filename, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + ret = pixWriteStreamWebP(fp, pixs, quality, lossless); + fclose(fp); + if (ret) + return ERROR_INT("pixs not compressed to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteStreampWebP() + * + * \param[in] fp file stream + * \param[in] pixs all depths + * \param[in] quality 0 - 100; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) See pixWriteMemWebP() for details.
+ *      (2) Use 'free', and not leptonica's 'LEPT_FREE', for all heap data
+ *          that is returned from the WebP library.
+ * 
+ */ +l_ok +pixWriteStreamWebP(FILE *fp, + PIX *pixs, + l_int32 quality, + l_int32 lossless) +{ +l_uint8 *filedata; +size_t filebytes, nbytes; + + PROCNAME("pixWriteStreamWebP"); + + if (!fp) + return ERROR_INT("stream not open", procName, 1); + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + + pixSetPadBits(pixs, 0); + pixWriteMemWebP(&filedata, &filebytes, pixs, quality, lossless); + rewind(fp); + nbytes = fwrite(filedata, 1, filebytes, fp); + free(filedata); + if (nbytes != filebytes) + return ERROR_INT("Write error", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteMemWebP() + * + * \param[out] pencdata webp encoded data of pixs + * \param[out] pencsize size of webp encoded data + * \param[in] pixs any depth, cmapped OK + * \param[in] quality 0 - 100; default ~80 + * \param[in] lossless use 1 for lossless; 0 for lossy + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) Lossless and lossy encoding are entirely different in webp.
+ *          %quality applies to lossy, and is ignored for lossless.
+ *      (2) The input image is converted to RGB if necessary.  If spp == 3,
+ *          we set the alpha channel to fully opaque (255), and
+ *          WebPEncodeRGBA() then removes the alpha chunk when encoding,
+ *          setting the internal header field has_alpha to 0.
+ * 
+ */ +l_ok +pixWriteMemWebP(l_uint8 **pencdata, + size_t *pencsize, + PIX *pixs, + l_int32 quality, + l_int32 lossless) +{ +l_int32 w, h, d, wpl, stride; +l_uint32 *data; +PIX *pix1, *pix2; + + PROCNAME("pixWriteMemWebP"); + + if (!pencdata) + return ERROR_INT("&encdata not defined", procName, 1); + *pencdata = NULL; + if (!pencsize) + return ERROR_INT("&encsize not defined", procName, 1); + *pencsize = 0; + if (!pixs) + return ERROR_INT("&pixs not defined", procName, 1); + if (lossless == 0 && (quality < 0 || quality > 100)) + return ERROR_INT("quality not in [0 ... 100]", procName, 1); + + if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) + return ERROR_INT("failure to remove color map", procName, 1); + + /* Convert to rgb if not 32 bpp; pix2 must not be a clone of pixs. */ + if (pixGetDepth(pix1) != 32) + pix2 = pixConvertTo32(pix1); + else + pix2 = pixCopy(NULL, pix1); + pixDestroy(&pix1); + pixGetDimensions(pix2, &w, &h, &d); + if (w <= 0 || h <= 0 || d != 32) { + pixDestroy(&pix2); + return ERROR_INT("pix2 not 32 bpp or of 0 size", procName, 1); + } + + /* If spp == 3, need to set alpha layer to opaque (all 1s). */ + if (pixGetSpp(pix2) == 3) + pixSetComponentArbitrary(pix2, L_ALPHA_CHANNEL, 255); + + /* The WebP API expects data in RGBA order. The pix stores + * in host-dependent order with R as the MSB and A as the LSB. + * On little-endian machines, the bytes in the word must + * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). + * No swapping is necessary for big-endians. */ + pixEndianByteSwap(pix2); + wpl = pixGetWpl(pix2); + data = pixGetData(pix2); + stride = wpl * 4; + if (lossless) { + *pencsize = WebPEncodeLosslessRGBA((uint8_t *)data, w, h, + stride, pencdata); + } else { + *pencsize = WebPEncodeRGBA((uint8_t *)data, w, h, stride, + quality, pencdata); + } + pixDestroy(&pix2); + + if (*pencsize == 0) { + free(*pencdata); + *pencdata = NULL; + return ERROR_INT("webp encoding failed", procName, 1); + } + + return 0; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBWEBP */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/webpiostub.c b/hgdriver/3rdparty/hgOCR/leptonica/webpiostub.c new file mode 100644 index 0000000..1730f2f --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/webpiostub.c @@ -0,0 +1,99 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file webpiostub.c + *
+ *
+ *     Stubs for webpio.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBWEBP /* defined in environ.h */ +/* --------------------------------------------*/ + +PIX * pixReadStreamWebP(FILE *fp) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadStreamWebP", NULL); +} + +/* ----------------------------------------------------------------------*/ + +PIX * pixReadMemWebP(const l_uint8 *filedata, size_t filesize) +{ + return (PIX * )ERROR_PTR("function not present", "pixReadMemWebP", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderWebP(const char *filename, l_int32 *pw, l_int32 *ph, + l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderWebP", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok readHeaderMemWebP(const l_uint8 *data, size_t size, + l_int32 *pw, l_int32 *ph, l_int32 *pspp) +{ + return ERROR_INT("function not present", "readHeaderMemWebP", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteWebP(const char *filename, PIX *pixs, l_int32 quality, + l_int32 lossless) +{ + return ERROR_INT("function not present", "pixWriteWebP", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteStreamWebP(FILE *fp, PIX *pixs, l_int32 quality, + l_int32 lossless) +{ + return ERROR_INT("function not present", "pixWriteStreamWebP", 1); +} + +/* ----------------------------------------------------------------------*/ + +l_ok pixWriteMemWebP(l_uint8 **pencdata, size_t *pencsize, PIX *pixs, + l_int32 quality, l_int32 lossless) +{ + return ERROR_INT("function not present", "pixWriteMemWebP", 1); +} + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBWEBP */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/writefile.c b/hgdriver/3rdparty/hgOCR/leptonica/writefile.c new file mode 100644 index 0000000..a1a01ad --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/writefile.c @@ -0,0 +1,1409 @@ +/*====================================================================* + - Copyright (C) 2001-2016 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/* + * writefile.c + * + * Set jpeg quality for pixWrite() and pixWriteMem() + * l_int32 l_jpegSetQuality() + * + * Set global variable LeptDebugOK for writing to named temp files + * l_int32 setLeptDebugOK() + * + * High-level procedures for writing images to file: + * l_int32 pixaWriteFiles() + * l_int32 pixWriteDebug() + * l_int32 pixWrite() + * l_int32 pixWriteAutoFormat() + * l_int32 pixWriteStream() + * l_int32 pixWriteImpliedFormat() + * + * Selection of output format if default is requested + * l_int32 pixChooseOutputFormat() + * l_int32 getImpliedFileFormat() + * l_int32 pixGetAutoFormat() + * const char *getFormatExtension() + * + * Write to memory + * l_int32 pixWriteMem() + * + * Image display for debugging + * l_int32 l_fileDisplay() + * l_int32 pixDisplay() + * l_int32 pixDisplayWithTitle() + * l_int32 pixSaveTiled() + * l_int32 pixSaveTiledOutline() + * l_int32 pixSaveTiledWithText() + * void l_chooseDisplayProg() + * + * Deprecated pix output for debugging (still used in tesseract 3.05) + * l_int32 pixDisplayWrite() + * + * Supported file formats: + * (1) Writing is supported without any external libraries: + * bmp + * pnm (including pbm, pgm, etc) + * spix (raw serialized) + * (2) Writing is supported with installation of external libraries: + * png + * jpg (standard jfif version) + * tiff (including most varieties of compression) + * gif + * webp + * jp2 (jpeg2000) + * (3) Writing is supported through special interfaces: + * ps (PostScript, in psio1.c, psio2.c): + * level 1 (uncompressed) + * level 2 (g4 and dct encoding: requires tiff, jpg) + * level 3 (g4, dct and flate encoding: requires tiff, jpg, zlib) + * pdf (PDF, in pdfio.c): + * level 1 (g4 and dct encoding: requires tiff, jpg) + * level 2 (g4, dct and flate encoding: requires tiff, jpg, zlib) + */ + +#include +#include "allheaders.h" + + /* Display program (xv, xli, xzgv, open) to be invoked by pixDisplay() */ +#ifdef _WIN32 +static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_IV; /* default */ +#elif defined(__APPLE__) +static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN; /* default */ +#else +static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV; /* default */ +#endif /* _WIN32 */ + +//static const l_int32 Bufsize = 512; +#define Bufsize 512 +static const l_int32 MaxDisplayWidth = 1000; +static const l_int32 MaxDisplayHeight = 800; +static const l_int32 MaxSizeForPng = 200; + + /* PostScript output for printing */ +static const l_float32 DefaultScaling = 1.0; + + /* Global array of image file format extension names. */ + /* This is in 1-1 corrspondence with format enum in imageio.h. */ + /* The empty string at the end represents the serialized format, */ + /* which has no recognizable extension name, but the array must */ + /* be padded to agree with the format enum. */ + /* (Note on 'const': The size of the array can't be defined 'const' */ + /* because that makes it static. The 'const' in the definition of */ + /* the array refers to the strings in the array; the ptr to the */ + /* array is not const and can be used 'extern' in other files.) */ +LEPT_DLL l_int32 NumImageFileFormatExtensions = 20; /* array size */ +LEPT_DLL const char *ImageFileFormatExtensions[] = + {"unknown", + "bmp", + "jpg", + "png", + "tif", + "tif", + "tif", + "tif", + "tif", + "tif", + "tif", + "pnm", + "ps", + "gif", + "jp2", + "webp", + "pdf", + "tif", + "default", + ""}; + + /* Local map of image file name extension to output format */ +struct ExtensionMap +{ + char extension[8]; + l_int32 format; +}; +static const struct ExtensionMap extension_map[] = + { { ".bmp", IFF_BMP }, + { ".jpg", IFF_JFIF_JPEG }, + { ".jpeg", IFF_JFIF_JPEG }, + { ".png", IFF_PNG }, + { ".tif", IFF_TIFF }, + { ".tiff", IFF_TIFF }, + { ".pnm", IFF_PNM }, + { ".gif", IFF_GIF }, + { ".jp2", IFF_JP2 }, + { ".ps", IFF_PS }, + { ".pdf", IFF_LPDF }, + { ".webp", IFF_WEBP } }; + + +/*---------------------------------------------------------------------* + * Set jpeg quality for pixWrite() and pixWriteMem() * + *---------------------------------------------------------------------*/ + /* Parameter that controls jpeg quality for high-level calls. */ +static l_int32 var_JPEG_QUALITY = 75; /* default */ + +/*! + * \brief l_jpegSetQuality() + * + * \param[in] new_quality 1 - 100; 75 is default; 0 defaults to 75 + * \return prev previous quality + * + *
+ * Notes:
+ *      (1) This variable is used in pixWriteStream() and pixWriteMem(),
+ *          to control the jpeg quality.  The default is 75.
+ *      (2) It returns the previous quality, so for example:
+ *           l_int32  prev = l_jpegSetQuality(85);  //sets to 85
+ *           pixWriteStream(...);
+ *           l_jpegSetQuality(prev);   // resets to previous value
+ *      (3) On error, logs a message and does not change the variable.
+ */
+l_int32
+l_jpegSetQuality(l_int32  new_quality)
+{
+l_int32  prevq, newq;
+
+    PROCNAME("l_jpeqSetQuality");
+
+    prevq = var_JPEG_QUALITY;
+    newq = (new_quality == 0) ? 75 : new_quality;
+    if (newq < 1 || newq > 100)
+        L_ERROR("invalid jpeg quality; unchanged\n", procName);
+    else
+        var_JPEG_QUALITY = newq;
+    return prevq;
+}
+
+
+/*----------------------------------------------------------------------*
+ *    Set global variable LeptDebugOK for writing to named temp files   *
+ *----------------------------------------------------------------------*/
+l_int32 LeptDebugOK = 0;  /* default value */
+/*!
+ * \brief   setLeptDebugOK()
+ *
+ * \param[in]    allow     TRUE (1) or FALSE (0)
+ * \return       void
+ *
+ * 
+ * Notes:
+ *      (1) This sets or clears the global variable LeptDebugOK, to
+ *          control writing files in a temp directory with names that
+ *          are compiled in.
+ *      (2) The default in the library distribution is 0.  Call with
+ *          %allow = 1 for development and debugging.
+ */
+void
+setLeptDebugOK(l_int32  allow)
+{
+    if (allow != 0) allow = 1;
+    LeptDebugOK = allow;
+}
+
+
+/*---------------------------------------------------------------------*
+ *           Top-level procedures for writing images to file           *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixaWriteFiles()
+ *
+ * \param[in]    rootname
+ * \param[in]    pixa
+ * \param[in]    format  defined in imageio.h; see notes for default
+ * \return  0 if OK; 1 on error
+ *
+ * 
+ * Notes:
+ *      (1) Use %format = IFF_DEFAULT to decide the output format
+ *          individually for each pix.
+ * 
+ */ +l_ok +pixaWriteFiles(const char *rootname, + PIXA *pixa, + l_int32 format) +{ +char bigbuf[Bufsize]; +l_int32 i, n, pixformat; +PIX *pix; + + PROCNAME("pixaWriteFiles"); + + if (!rootname) + return ERROR_INT("rootname not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + if (format < 0 || format == IFF_UNKNOWN || + format >= NumImageFileFormatExtensions) + return ERROR_INT("invalid format", procName, 1); + + n = pixaGetCount(pixa); + for (i = 0; i < n; i++) { + pix = pixaGetPix(pixa, i, L_CLONE); + if (format == IFF_DEFAULT) + pixformat = pixChooseOutputFormat(pix); + else + pixformat = format; + snprintf(bigbuf, Bufsize, "%s%03d.%s", rootname, i, + ImageFileFormatExtensions[pixformat]); + pixWrite(bigbuf, pix, pixformat); + pixDestroy(&pix); + } + + return 0; +} + + +/*! + * \brief pixWriteDebug() + * + * \param[in] fname + * \param[in] pix + * \param[in] format defined in imageio.h + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of pixWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * 
+ */ +l_ok +pixWriteDebug(const char *fname, + PIX *pix, + l_int32 format) +{ + PROCNAME("pixWriteDebug"); + + if (LeptDebugOK) { + return pixWrite(fname, pix, format); + } else { + L_INFO("write to named temp file %s is disabled\n", procName, fname); + return 0; + } +} + + +/*! + * \brief pixWrite() + * + * \param[in] fname + * \param[in] pix + * \param[in] format defined in imageio.h + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) Open for write using binary mode (with the "b" flag)
+ *          to avoid having Windows automatically translate the NL
+ *          into CRLF, which corrupts image files.  On non-windows
+ *          systems this flag should be ignored, per ISO C90.
+ *          Thanks to Dave Bryan for pointing this out.
+ *      (2) If the default image format IFF_DEFAULT is requested:
+ *          use the input format if known; otherwise, use a lossless format.
+ *      (3) The default jpeg quality is 75.  For some other value,
+ *          Use l_jpegSetQuality().
+ * 
+ */ +l_ok +pixWrite(const char *fname, + PIX *pix, + l_int32 format) +{ +l_int32 ret; +FILE *fp; + + PROCNAME("pixWrite"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!fname) + return ERROR_INT("fname not defined", procName, 1); + + if ((fp = fopenWriteStream(fname, "wb+")) == NULL) + return ERROR_INT("stream not opened", procName, 1); + + ret = pixWriteStream(fp, pix, format); + fclose(fp); + if (ret) + return ERROR_INT("pix not written to stream", procName, 1); + return 0; +} + + +/*! + * \brief pixWriteAutoFormat() + * + * \param[in] filename + * \param[in] pix + * \return 0 if OK; 1 on error + */ +l_ok +pixWriteAutoFormat(const char *filename, + PIX *pix) +{ +l_int32 format; + + PROCNAME("pixWriteAutoFormat"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + + if (pixGetAutoFormat(pix, &format)) + return ERROR_INT("auto format not returned", procName, 1); + return pixWrite(filename, pix, format); +} + + +/*! + * \brief pixWriteStream() + * + * \param[in] fp file stream + * \param[in] pix + * \param[in] format + * \return 0 if OK; 1 on error. + */ +l_ok +pixWriteStream(FILE *fp, + PIX *pix, + l_int32 format) +{ + PROCNAME("pixWriteStream"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + if (format == IFF_DEFAULT) + format = pixChooseOutputFormat(pix); + + switch(format) + { + case IFF_BMP: + pixWriteStreamBmp(fp, pix); + break; + + case IFF_JFIF_JPEG: /* default quality; baseline sequential */ + return pixWriteStreamJpeg(fp, pix, var_JPEG_QUALITY, 0); + break; + + case IFF_PNG: /* no gamma value stored */ + return pixWriteStreamPng(fp, pix, 0.0); + break; + + case IFF_TIFF: /* uncompressed */ + case IFF_TIFF_PACKBITS: /* compressed, binary only */ + case IFF_TIFF_RLE: /* compressed, binary only */ + case IFF_TIFF_G3: /* compressed, binary only */ + case IFF_TIFF_G4: /* compressed, binary only */ + case IFF_TIFF_LZW: /* compressed, all depths */ + case IFF_TIFF_ZIP: /* compressed, all depths */ + case IFF_TIFF_JPEG: /* compressed, 8 bpp gray and 32 bpp rgb */ + return pixWriteStreamTiff(fp, pix, format); + break; + + case IFF_PNM: + return pixWriteStreamPnm(fp, pix); + break; + + case IFF_PS: + return pixWriteStreamPS(fp, pix, NULL, 0, DefaultScaling); + break; + + case IFF_GIF: + return pixWriteStreamGif(fp, pix); + break; + + case IFF_JP2: + return pixWriteStreamJp2k(fp, pix, 34, 4, 0, 0); + break; + + case IFF_WEBP: + return pixWriteStreamWebP(fp, pix, 80, 0); + break; + + case IFF_LPDF: + return pixWriteStreamPdf(fp, pix, 0, NULL); + break; + + case IFF_SPIX: + return pixWriteStreamSpix(fp, pix); + break; + + default: + return ERROR_INT("unknown format", procName, 1); + break; + } + + return 0; +} + + +/*! + * \brief pixWriteImpliedFormat() + * + * \param[in] filename + * \param[in] pix + * \param[in] quality iff JPEG; 1 - 100, 0 for default + * \param[in] progressive iff JPEG; 0 for baseline seq., 1 for progressive + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This determines the output format from the filename extension.
+ *      (2) The last two args are ignored except for requests for jpeg files.
+ *      (3) The jpeg default quality is 75.
+ * 
+ */ +l_ok +pixWriteImpliedFormat(const char *filename, + PIX *pix, + l_int32 quality, + l_int32 progressive) +{ +l_int32 format; + + PROCNAME("pixWriteImpliedFormat"); + + if (!filename) + return ERROR_INT("filename not defined", procName, 1); + if (!pix) + return ERROR_INT("pix not defined", procName, 1); + + /* Determine output format */ + format = getImpliedFileFormat(filename); + if (format == IFF_UNKNOWN) { + format = IFF_PNG; + } else if (format == IFF_TIFF) { + if (pixGetDepth(pix) == 1) + format = IFF_TIFF_G4; + else +#ifdef _WIN32 + format = IFF_TIFF_LZW; /* poor compression */ +#else + format = IFF_TIFF_ZIP; /* native windows tools can't handle this */ +#endif /* _WIN32 */ + } + + if (format == IFF_JFIF_JPEG) { + quality = L_MIN(quality, 100); + quality = L_MAX(quality, 0); + if (progressive != 0 && progressive != 1) { + progressive = 0; + L_WARNING("invalid progressive; setting to baseline\n", procName); + } + if (quality == 0) + quality = 75; + pixWriteJpeg (filename, pix, quality, progressive); + } else { + pixWrite(filename, pix, format); + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Selection of output format if default is requested * + *---------------------------------------------------------------------*/ +/*! + * \brief pixChooseOutputFormat() + * + * \param[in] pix + * \return output format, or 0 on error + * + *
+ * Notes:
+ *      (1) This should only be called if the requested format is IFF_DEFAULT.
+ *      (2) If the pix wasn't read from a file, its input format value
+ *          will be IFF_UNKNOWN, and in that case it is written out
+ *          in a compressed but lossless format.
+ * 
+ */ +l_int32 +pixChooseOutputFormat(PIX *pix) +{ +l_int32 d, format; + + PROCNAME("pixChooseOutputFormat"); + + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + d = pixGetDepth(pix); + format = pixGetInputFormat(pix); + if (format == IFF_UNKNOWN) { /* output lossless */ + if (d == 1) + format = IFF_TIFF_G4; + else + format = IFF_PNG; + } + + return format; +} + + +/*! + * \brief getImpliedFileFormat() + * + * \param[in] filename + * \return output format, or IFF_UNKNOWN on error or invalid extension. + * + *
+ * Notes:
+ *      (1) This determines the output file format from the extension
+ *          of the input filename.
+ * 
+ */ +l_int32 +getImpliedFileFormat(const char *filename) +{ +char *extension; +int i, numext; +l_int32 format = IFF_UNKNOWN; + + if (splitPathAtExtension (filename, NULL, &extension)) + return IFF_UNKNOWN; + + numext = sizeof(extension_map) / sizeof(extension_map[0]); + for (i = 0; i < numext; i++) { + if (!strcmp(extension, extension_map[i].extension)) { + format = extension_map[i].format; + break; + } + } + + LEPT_FREE(extension); + return format; +} + + +/*! + * \brief pixGetAutoFormat() + * + * \param[in] pix + * \param[in] &format + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) The output formats are restricted to tiff, jpeg and png
+ *          because these are the most commonly used image formats and
+ *          the ones that are typically installed with leptonica.
+ *      (2) This decides what compression to use based on the pix.
+ *          It chooses tiff-g4 if 1 bpp without a colormap, jpeg with
+ *          quality 75 if grayscale, rgb or rgba (where it loses
+ *          the alpha layer), and lossless png for all other situations.
+ * 
+ */ +l_ok +pixGetAutoFormat(PIX *pix, + l_int32 *pformat) +{ +l_int32 d; +PIXCMAP *cmap; + + PROCNAME("pixGetAutoFormat"); + + if (!pformat) + return ERROR_INT("&format not defined", procName, 0); + *pformat = IFF_UNKNOWN; + if (!pix) + return ERROR_INT("pix not defined", procName, 0); + + d = pixGetDepth(pix); + cmap = pixGetColormap(pix); + if (d == 1 && !cmap) { + *pformat = IFF_TIFF_G4; + } else if ((d == 8 && !cmap) || d == 24 || d == 32) { + *pformat = IFF_JFIF_JPEG; + } else { + *pformat = IFF_PNG; + } + + return 0; +} + + +/*! + * \brief getFormatExtension() + * + * \param[in] format integer + * \return extension string, or NULL if format is out of range + * + *
+ * Notes:
+ *      (1) This string is NOT owned by the caller; it is just a pointer
+ *          to a global string.  Do not free it.
+ * 
+ */ +const char * +getFormatExtension(l_int32 format) +{ + PROCNAME("getFormatExtension"); + + if (format < 0 || format >= NumImageFileFormatExtensions) + return (const char *)ERROR_PTR("invalid format", procName, NULL); + + return ImageFileFormatExtensions[format]; +} + + +/*---------------------------------------------------------------------* + * Write to memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixWriteMem() + * + * \param[out] pdata data of tiff compressed image + * \param[out] psize size of returned data + * \param[in] pix + * \param[in] format defined in imageio.h + * \return 0 if OK, 1 on error + * + *
+ * Notes:
+ *      (1) On windows, this will only write tiff and PostScript to memory.
+ *          For other formats, it requires open_memstream(3).
+ *      (2) PostScript output is uncompressed, in hex ascii.
+ *          Most printers support level 2 compression (tiff_g4 for 1 bpp,
+ *          jpeg for 8 and 32 bpp).
+ *      (3) The default jpeg quality is 75.  For some other value,
+ *          Use l_jpegSetQuality().
+ * 
+ */ +l_ok +pixWriteMem(l_uint8 **pdata, + size_t *psize, + PIX *pix, + l_int32 format) +{ +l_int32 ret; + + PROCNAME("pixWriteMem"); + + if (!pdata) + return ERROR_INT("&data not defined", procName, 1 ); + if (!psize) + return ERROR_INT("&size not defined", procName, 1 ); + if (!pix) + return ERROR_INT("&pix not defined", procName, 1 ); + + if (format == IFF_DEFAULT) + format = pixChooseOutputFormat(pix); + + switch(format) + { + case IFF_BMP: + ret = pixWriteMemBmp(pdata, psize, pix); + break; + + case IFF_JFIF_JPEG: /* default quality; baseline sequential */ + ret = pixWriteMemJpeg(pdata, psize, pix, var_JPEG_QUALITY, 0); + break; + + case IFF_PNG: /* no gamma value stored */ + ret = pixWriteMemPng(pdata, psize, pix, 0.0); + break; + + case IFF_TIFF: /* uncompressed */ + case IFF_TIFF_PACKBITS: /* compressed, binary only */ + case IFF_TIFF_RLE: /* compressed, binary only */ + case IFF_TIFF_G3: /* compressed, binary only */ + case IFF_TIFF_G4: /* compressed, binary only */ + case IFF_TIFF_LZW: /* compressed, all depths */ + case IFF_TIFF_ZIP: /* compressed, all depths */ + ret = pixWriteMemTiff(pdata, psize, pix, format); + break; + + case IFF_PNM: + ret = pixWriteMemPnm(pdata, psize, pix); + break; + + case IFF_PS: + ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DefaultScaling); + break; + + case IFF_GIF: + ret = pixWriteMemGif(pdata, psize, pix); + break; + + case IFF_JP2: + ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0); + break; + + case IFF_WEBP: + ret = pixWriteMemWebP(pdata, psize, pix, 80, 0); + break; + + case IFF_LPDF: + ret = pixWriteMemPdf(pdata, psize, pix, 0, NULL); + break; + + case IFF_SPIX: + ret = pixWriteMemSpix(pdata, psize, pix); + break; + + default: + return ERROR_INT("unknown format", procName, 1); + break; + } + + return ret; +} + + +/*---------------------------------------------------------------------* + * Image display for debugging * + *---------------------------------------------------------------------*/ +/*! + * \brief l_fileDisplay() + * + * \param[in] fname + * \param[in] x, y location of display frame on the screen + * \param[in] scale scale factor (use 0 to skip display) + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is a convenient wrapper for displaying image files.
+ *      (2) It does nothing unless LeptDebugOK == TRUE.
+ *      (2) Set %scale = 0 to disable display.
+ *      (3) This downscales 1 bpp to gray.
+ * 
+ */ +l_ok +l_fileDisplay(const char *fname, + l_int32 x, + l_int32 y, + l_float32 scale) +{ +PIX *pixs, *pixd; + + PROCNAME("l_fileDisplay"); + + if (!LeptDebugOK) { + L_INFO("displaying files is disabled; " + "use setLeptDebugOK(1) to enable\n", procName); + return 0; + } + if (scale == 0.0) + return 0; + if (scale < 0.0) + return ERROR_INT("invalid scale factor", procName, 1); + if ((pixs = pixRead(fname)) == NULL) + return ERROR_INT("pixs not read", procName, 1); + + if (scale == 1.0) { + pixd = pixClone(pixs); + } else { + if (scale < 1.0 && pixGetDepth(pixs) == 1) + pixd = pixScaleToGray(pixs, scale); + else + pixd = pixScale(pixs, scale, scale); + } + pixDisplay(pixd, x, y); + pixDestroy(&pixs); + pixDestroy(&pixd); + return 0; +} + + +/*! + * \brief pixDisplay() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] x, y location of display frame on the screen + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) This is debugging code that displays an image on the screen.
+ *          It uses a static internal variable to number the output files
+ *          written by a single process.  Behavior with a shared library
+ *          may be unpredictable.
+ *      (2) It does nothing unless LeptDebugOK == TRUE.
+ *      (3) It uses these programs to display the image:
+ *             On Unix: xzgv, xli or xv
+ *             On Windows: i_view
+ *          The display program must be on your $PATH variable.  It is
+ *          chosen by setting the global var_DISPLAY_PROG, using
+ *          l_chooseDisplayProg().  Default on Unix is xzgv.
+ *      (4) Images with dimensions larger than MaxDisplayWidth or
+ *          MaxDisplayHeight are downscaled to fit those constraints.
+ *          This is particularly important for displaying 1 bpp images
+ *          with xv, because xv automatically downscales large images
+ *          by subsampling, which looks poor.  For 1 bpp, we use
+ *          scale-to-gray to get decent-looking anti-aliased images.
+ *          In all cases, we write a temporary file to /tmp/lept/disp,
+ *          that is read by the display program.
+ *      (5) The temporary file is written as png if, after initial
+ *          processing for special cases, any of these obtain:
+ *            * pix dimensions are smaller than some thresholds
+ *            * pix depth is less than 8 bpp
+ *            * pix is colormapped
+ *      (6) For spp == 4, we call pixDisplayLayersRGBA() to show 3
+ *          versions of the image: the image with a fully opaque
+ *          alpha, the alpha, and the image as it would appear with
+ *          a white background.
+ * 
+ */ +l_ok +pixDisplay(PIX *pixs, + l_int32 x, + l_int32 y) +{ + return pixDisplayWithTitle(pixs, x, y, NULL, 1); +} + + +/*! + * \brief pixDisplayWithTitle() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] x, y location of display frame + * \param[in] title [optional] on frame; can be NULL; + * \param[in] dispflag 1 to write, else disabled + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (1) See notes for pixDisplay().
+ *      (2) This displays the image if dispflag == 1; otherwise it punts.
+ * 
+ */ +l_ok +pixDisplayWithTitle(PIX *pixs, + l_int32 x, + l_int32 y, + const char *title, + l_int32 dispflag) +{ +char *tempname; +char buffer[Bufsize]; +static l_int32 index = 0; /* caution: not .so or thread safe */ +l_int32 w, h, d, spp, maxheight, opaque, threeviews; +l_float32 ratw, rath, ratmin; +PIX *pix0, *pix1, *pix2; +PIXCMAP *cmap; +#ifndef _WIN32 +l_int32 wt, ht; +#else +char *pathname; +char fullpath[_MAX_PATH]; +#endif /* _WIN32 */ + + PROCNAME("pixDisplayWithTitle"); + + if (!LeptDebugOK) { + L_INFO("displaying images is disabled;\n " + "use setLeptDebugOK(1) to enable\n", procName); + return 0; + } + +#ifdef OS_IOS /* iOS 11 does not support system() */ + return ERROR_INT("iOS 11 does not support system()", procName, 1); +#endif /* OS_IOS */ + + if (dispflag != 1) return 0; + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV && + var_DISPLAY_PROG != L_DISPLAY_WITH_XLI && + var_DISPLAY_PROG != L_DISPLAY_WITH_XV && + var_DISPLAY_PROG != L_DISPLAY_WITH_IV && + var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) { + return ERROR_INT("no program chosen for display", procName, 1); + } + + /* Display with three views if either spp = 4 or if colormapped + * and the alpha component is not fully opaque */ + opaque = TRUE; + if ((cmap = pixGetColormap(pixs)) != NULL) + pixcmapIsOpaque(cmap, &opaque); + spp = pixGetSpp(pixs); + threeviews = (spp == 4 || !opaque) ? TRUE : FALSE; + + /* If colormapped and not opaque, remove the colormap to RGBA */ + if (!opaque) + pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA); + else + pix0 = pixClone(pixs); + + /* Scale if necessary; this will also remove a colormap */ + pixGetDimensions(pix0, &w, &h, &d); + maxheight = (threeviews) ? MaxDisplayHeight / 3 : MaxDisplayHeight; + if (w <= MaxDisplayWidth && h <= maxheight) { + if (d == 16) /* take MSB */ + pix1 = pixConvert16To8(pix0, L_MS_BYTE); + else + pix1 = pixClone(pix0); + } else { + ratw = (l_float32)MaxDisplayWidth / (l_float32)w; + rath = (l_float32)maxheight / (l_float32)h; + ratmin = L_MIN(ratw, rath); + if (ratmin < 0.125 && d == 1) + pix1 = pixScaleToGray8(pix0); + else if (ratmin < 0.25 && d == 1) + pix1 = pixScaleToGray4(pix0); + else if (ratmin < 0.33 && d == 1) + pix1 = pixScaleToGray3(pix0); + else if (ratmin < 0.5 && d == 1) + pix1 = pixScaleToGray2(pix0); + else + pix1 = pixScale(pix0, ratmin, ratmin); + } + pixDestroy(&pix0); + if (!pix1) + return ERROR_INT("pix1 not made", procName, 1); + + /* Generate the three views if required */ + if (threeviews) + pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0); + else + pix2 = pixClone(pix1); + + if (index == 0) { /* erase any existing images */ + lept_rmdir("lept/disp"); + lept_mkdir("lept/disp"); + } + + index++; + if (pixGetDepth(pix2) < 8 || pixGetColormap(pix2) || + (w < MaxSizeForPng && h < MaxSizeForPng)) { + snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.png", index); + pixWrite(buffer, pix2, IFF_PNG); + } else { + snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.jpg", index); + pixWrite(buffer, pix2, IFF_JFIF_JPEG); + } + tempname = genPathname(buffer, NULL); + +#ifndef _WIN32 + + /* Unix */ + if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) { + /* no way to display title */ + pixGetDimensions(pix2, &wt, &ht, NULL); + snprintf(buffer, Bufsize, + "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10, + x, y, tempname); + } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) { + if (title) { + snprintf(buffer, Bufsize, + "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &", + x, y, title, tempname); + } else { + snprintf(buffer, Bufsize, + "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &", + x, y, tempname); + } + } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) { + if (title) { + snprintf(buffer, Bufsize, + "xv -quit -geometry +%d+%d -name \"%s\" %s &", + x, y, title, tempname); + } else { + snprintf(buffer, Bufsize, + "xv -quit -geometry +%d+%d %s &", x, y, tempname); + } + } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_OPEN) { + snprintf(buffer, Bufsize, "open %s &", tempname); + } + callSystemDebug(buffer); + +#else /* _WIN32 */ + + /* Windows: L_DISPLAY_WITH_IV */ + pathname = genPathname(tempname, NULL); + _fullpath(fullpath, pathname, sizeof(fullpath)); + if (title) { + snprintf(buffer, Bufsize, + "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"", + fullpath, x, y, title); + } else { + snprintf(buffer, Bufsize, "i_view32.exe \"%s\" /pos=(%d,%d)", + fullpath, x, y); + } + callSystemDebug(buffer); + LEPT_FREE(pathname); + +#endif /* _WIN32 */ + + pixDestroy(&pix1); + pixDestroy(&pix2); + LEPT_FREE(tempname); + return 0; +} + + +/*! + * \brief pixSaveTiled() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp + * \param[in] pixa the pix are accumulated here + * \param[in] scalefactor 0.0 to disable; otherwise this is a scale factor + * \param[in] newrow 0 if placed on the same row as previous; 1 otherwise + * \param[in] space horizontal and vertical spacing, in pixels + * \param[in] dp depth of pixa; 8 or 32 bpp; only used on first call + * \return 0 if OK, 1 on error. + */ +l_ok +pixSaveTiled(PIX *pixs, + PIXA *pixa, + l_float32 scalefactor, + l_int32 newrow, + l_int32 space, + l_int32 dp) +{ + /* Save without an outline */ + return pixSaveTiledOutline(pixs, pixa, scalefactor, newrow, space, 0, dp); +} + + +/*! + * \brief pixSaveTiledOutline() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp + * \param[in] pixa the pix are accumulated here + * \param[in] scalefactor 0.0 to disable; otherwise this is a scale factor + * \param[in] newrow 0 if placed on the same row as previous; 1 otherwise + * \param[in] space horizontal and vertical spacing, in pixels + * \param[in] linewidth width of added outline for image; 0 for no outline + * \param[in] dp depth of pixa; 8 or 32 bpp; only used on first call + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) Before calling this function for the first time, use
+ *          pixaCreate() to make the %pixa that will accumulate the pix.
+ *          This is passed in each time pixSaveTiled() is called.
+ *      (2) %scalefactor scales the input image.  After scaling and
+ *          possible depth conversion, the image is saved in the input
+ *          pixa, along with a box that specifies the location to
+ *          place it when tiled later.  Disable saving the pix by
+ *          setting %scalefactor == 0.0.
+ *      (3) %newrow and %space specify the location of the new pix
+ *          with respect to the last one(s) that were entered.
+ *      (4) %dp specifies the depth at which all pix are saved.  It can
+ *          be only 8 or 32 bpp.  Any colormap is removed.  This is only
+ *          used at the first invocation.
+ *      (5) This function uses two variables from call to call.
+ *          If they were static, the function would not be .so or thread
+ *          safe, and furthermore, there would be interference with two or
+ *          more pixa accumulating images at a time.  Consequently,
+ *          we use the first pix in the pixa to store and obtain both
+ *          the depth and the current position of the bottom (one pixel
+ *          below the lowest image raster line when laid out using
+ *          the boxa).  The bottom variable is stored in the input format
+ *          field, which is the only field available for storing an int.
+ * 
+ */ +l_ok +pixSaveTiledOutline(PIX *pixs, + PIXA *pixa, + l_float32 scalefactor, + l_int32 newrow, + l_int32 space, + l_int32 linewidth, + l_int32 dp) +{ +l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; +BOX *box; +PIX *pix1, *pix2, *pix3, *pix4; + + PROCNAME("pixSaveTiledOutline"); + + if (scalefactor == 0.0) return 0; + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + n = pixaGetCount(pixa); + if (n == 0) { + bottom = 0; + if (dp != 8 && dp != 32) { + L_WARNING("dp not 8 or 32 bpp; using 32\n", procName); + depth = 32; + } else { + depth = dp; + } + } else { /* extract the depth and bottom params from the first pix */ + pix1 = pixaGetPix(pixa, 0, L_CLONE); + depth = pixGetDepth(pix1); + bottom = pixGetInputFormat(pix1); /* not typical usage! */ + pixDestroy(&pix1); + } + + /* Remove colormap if it exists; otherwise a copy. This + * guarantees that pix4 is not a clone of pixs. */ + pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); + + /* Scale and convert to output depth */ + if (scalefactor == 1.0) { + pix2 = pixClone(pix1); + } else if (scalefactor > 1.0) { + pix2 = pixScale(pix1, scalefactor, scalefactor); + } else { /* scalefactor < 1.0) */ + if (pixGetDepth(pix1) == 1) + pix2 = pixScaleToGray(pix1, scalefactor); + else + pix2 = pixScale(pix1, scalefactor, scalefactor); + } + pixDestroy(&pix1); + if (depth == 8) + pix3 = pixConvertTo8(pix2, 0); + else + pix3 = pixConvertTo32(pix2); + pixDestroy(&pix2); + + /* Add black outline */ + if (linewidth > 0) + pix4 = pixAddBorder(pix3, linewidth, 0); + else + pix4 = pixClone(pix3); + pixDestroy(&pix3); + + /* Find position of current pix (UL corner plus size) */ + if (n == 0) { + top = 0; + left = 0; + } else if (newrow == 1) { + top = bottom + space; + left = 0; + } else { /* n > 0 */ + pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); + top = by; + left = bx + bw + space; + } + + pixGetDimensions(pix4, &w, &h, NULL); + bottom = L_MAX(bottom, top + h); + box = boxCreate(left, top, w, h); + pixaAddPix(pixa, pix4, L_INSERT); + pixaAddBox(pixa, box, L_INSERT); + + /* Save the new bottom value */ + pix1 = pixaGetPix(pixa, 0, L_CLONE); + pixSetInputFormat(pix1, bottom); /* not typical usage! */ + pixDestroy(&pix1); + return 0; +} + + +/*! + * \brief pixSaveTiledWithText() + * + * \param[in] pixs 1, 2, 4, 8, 32 bpp + * \param[in] pixa the pix are accumulated here; as 32 bpp + * \param[in] outwidth in pixels; use 0 to disable entirely + * \param[in] newrow 1 to start a new row; 0 to go on same row as previous + * \param[in] space horizontal and vertical spacing, in pixels + * \param[in] linewidth width of added outline for image; 0 for no outline + * \param[in] bmf [optional] font struct + * \param[in] textstr [optional] text string to be added + * \param[in] val color to set the text + * \param[in] location L_ADD_ABOVE, L_ADD_AT_TOP, L_ADD_AT_BOT, L_ADD_BELOW + * \return 0 if OK, 1 on error. + * + *
+ * Notes:
+ *      (1) Before calling this function for the first time, use
+ *          pixaCreate() to make the %pixa that will accumulate the pix.
+ *          This is passed in each time pixSaveTiled() is called.
+ *      (2) %outwidth is the scaled width.  After scaling, the image is
+ *          saved in the input pixa, along with a box that specifies
+ *          the location to place it when tiled later.  Disable saving
+ *          the pix by setting %outwidth == 0.
+ *      (3) %newrow and %space specify the location of the new pix
+ *          with respect to the last one(s) that were entered.
+ *      (4) All pix are saved as 32 bpp RGB.
+ *      (5) If both %bmf and %textstr are defined, this generates a pix
+ *          with the additional text; otherwise, no text is written.
+ *      (6) The text is written before scaling, so it is properly
+ *          antialiased in the scaled pix.  However, if the pix on
+ *          different calls have different widths, the size of the
+ *          text will vary.
+ *      (7) See pixSaveTiledOutline() for other implementation details.
+ * 
+ */ +l_ok +pixSaveTiledWithText(PIX *pixs, + PIXA *pixa, + l_int32 outwidth, + l_int32 newrow, + l_int32 space, + l_int32 linewidth, + L_BMF *bmf, + const char *textstr, + l_uint32 val, + l_int32 location) +{ +PIX *pix1, *pix2, *pix3, *pix4; + + PROCNAME("pixSaveTiledWithText"); + + if (outwidth == 0) return 0; + + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (!pixa) + return ERROR_INT("pixa not defined", procName, 1); + + pix1 = pixConvertTo32(pixs); + if (linewidth > 0) + pix2 = pixAddBorder(pix1, linewidth, 0); + else + pix2 = pixClone(pix1); + if (bmf && textstr) + pix3 = pixAddSingleTextblock(pix2, bmf, textstr, val, location, NULL); + else + pix3 = pixClone(pix2); + pix4 = pixScaleToSize(pix3, outwidth, 0); + pixSaveTiled(pix4, pixa, 1.0, newrow, space, 32); + pixDestroy(&pix1); + pixDestroy(&pix2); + pixDestroy(&pix3); + pixDestroy(&pix4); + return 0; +} + + +void +l_chooseDisplayProg(l_int32 selection) +{ + if (selection == L_DISPLAY_WITH_XLI || + selection == L_DISPLAY_WITH_XZGV || + selection == L_DISPLAY_WITH_XV || + selection == L_DISPLAY_WITH_IV || + selection == L_DISPLAY_WITH_OPEN) { + var_DISPLAY_PROG = selection; + } else { + L_ERROR("invalid display program\n", "l_chooseDisplayProg"); + } + return; +} + + +/*---------------------------------------------------------------------* + * Deprecated pix output for debugging * + *---------------------------------------------------------------------*/ +/*! + * \brief pixDisplayWrite() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] reduction -1 to reset/erase; 0 to disable; + * otherwise this is a reduction factor + * \return 0 if OK; 1 on error + * + *
+ * Notes:
+ *      (0) Deprecated.
+ *      (1) This is a simple interface for writing a set of files.
+ *      (2) This uses jpeg output for pix that are 32 bpp or 8 bpp
+ *          without a colormap; otherwise, it uses png.
+ *      (3) To erase any previously written files in the output directory:
+ *             pixDisplayWrite(NULL, -1);
+ *      (4) If reduction > 1 and depth == 1, this does a scale-to-gray
+ *          reduction.
+ *      (5) This function uses a static internal variable to number
+ *          output files written by a single process.  Behavior
+ *          with a shared library may be unpredictable.
+ *      (6) For 16 bpp, this displays the full dynamic range with log scale.
+ *          Alternative image transforms to generate 8 bpp pix are:
+ *             pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
+ *             pix8 = pixConvert16To8(pixt, L_LS_BYTE);  // low order byte
+ *             pix8 = pixConvert16To8(pixt, L_MS_BYTE);  // high order byte
+ * 
+ */ +l_ok +pixDisplayWrite(PIX *pixs, + l_int32 reduction) +{ +char buf[Bufsize]; +char *fname; +l_float32 scale; +PIX *pix1, *pix2; +static l_int32 index = 0; /* caution: not .so or thread safe */ + + PROCNAME("pixDisplayWrite"); + + fprintf(stderr, "\n######################################################" + "\n Notice:\n" + " pixDisplayWrite() has been deprecated in leptonica \n" + " since version 1.74. It will become a non-functioning\n" + " stub in 1.80.\n" + "######################################################" + "\n\n\n"); + + if (reduction == 0) return 0; + if (reduction < 0) { /* initialize */ + lept_rmdir("lept/display"); + index = 0; + return 0; + } + if (!pixs) + return ERROR_INT("pixs not defined", procName, 1); + if (index == 0) + lept_mkdir("lept/display"); + index++; + + if (reduction == 1) { + pix1 = pixClone(pixs); + } else { + scale = 1. / (l_float32)reduction; + if (pixGetDepth(pixs) == 1) + pix1 = pixScaleToGray(pixs, scale); + else + pix1 = pixScale(pixs, scale, scale); + } + + if (pixGetDepth(pix1) == 16) { + pix2 = pixMaxDynamicRange(pix1, L_LOG_SCALE); + snprintf(buf, Bufsize, "file.%03d.png", index); + fname = pathJoin("/tmp/lept/display", buf); + pixWrite(fname, pix2, IFF_PNG); + pixDestroy(&pix2); + } else if (pixGetDepth(pix1) < 8 || pixGetColormap(pix1)) { + snprintf(buf, Bufsize, "file.%03d.png", index); + fname = pathJoin("/tmp/lept/display", buf); + pixWrite(fname, pix1, IFF_PNG); + } else { + snprintf(buf, Bufsize, "file.%03d.jpg", index); + fname = pathJoin("/tmp/lept/display", buf); + pixWrite(fname, pix1, IFF_JFIF_JPEG); + } + LEPT_FREE(fname); + pixDestroy(&pix1); + return 0; +} diff --git a/hgdriver/3rdparty/hgOCR/leptonica/zlibmem.c b/hgdriver/3rdparty/hgOCR/leptonica/zlibmem.c new file mode 100644 index 0000000..806d064 --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/zlibmem.c @@ -0,0 +1,282 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + + +/*! + * \file zlibmem.c + *
+ *
+ *      zlib operations in memory, using bbuffer
+ *          l_uint8   *zlibCompress()
+ *          l_uint8   *zlibUncompress()
+ *
+ *
+ *    This provides an example use of the byte buffer utility
+ *    (see bbuffer.c for details of how the bbuffer works internally).
+ *    We use zlib to compress and decompress a byte array from
+ *    one memory buffer to another.  The standard method uses streams,
+ *    but here we use the bbuffer as an expandable queue of pixels
+ *    for both the reading and writing sides of each operation.
+ *
+ *    With memory mapping, one should be able to compress between
+ *    memory buffers by using the file system to buffer everything in
+ *    the background, but the bbuffer implementation is more portable.
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if 1 /* defined in environ.h */ +/* --------------------------------------------*/ + +#include "zlib.h" + +static const l_int32 L_BUF_SIZE = 32768; +static const l_int32 ZLIB_COMPRESSION_LEVEL = 6; + +#ifndef NO_CONSOLE_IO +#define DEBUG 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*! + * \brief zlibCompress() + * + * \param[in] datain byte buffer with input data + * \param[in] nin number of bytes of input data + * \param[out] pnout number of bytes of output data + * \return dataout compressed data, or NULL on error + * + *
+ * Notes:
+ *      (1) We repeatedly read in and fill up an input buffer,
+ *          compress the data, and read it back out.  zlib
+ *          uses two byte buffers internally in the z_stream
+ *          data structure.  We use the bbuffers to feed data
+ *          into the fixed bufferin, and feed it out of bufferout,
+ *          in the same way that a pair of streams would normally
+ *          be used if the data were being read from one file
+ *          and written to another.  This is done iteratively,
+ *          compressing L_BUF_SIZE bytes of input data at a time.
+ * 
+ */ +l_uint8 * +zlibCompress(l_uint8 *datain, + size_t nin, + size_t *pnout) +{ +l_uint8 *dataout; +l_int32 status, success; +l_int32 flush; +size_t nbytes; +l_uint8 *bufferin, *bufferout; +L_BBUFFER *bbin, *bbout; +z_stream z; + + PROCNAME("zlibCompress"); + + if (!datain) + return (l_uint8 *)ERROR_PTR("datain not defined", procName, NULL); + + /* Set up fixed size buffers used in z_stream */ + bufferin = (l_uint8 *)LEPT_CALLOC(L_BUF_SIZE, sizeof(l_uint8)); + bufferout = (l_uint8 *)LEPT_CALLOC(L_BUF_SIZE, sizeof(l_uint8)); + + /* Set up bbuffers and load bbin with the data */ + bbin = bbufferCreate(datain, nin); + bbout = bbufferCreate(NULL, 0); + + success = TRUE; + if (!bufferin || !bufferout || !bbin || !bbout) { + L_ERROR("calloc fail for buffer\n", procName); + success = FALSE; + goto cleanup_arrays; + } + + z.zalloc = (alloc_func)0; + z.zfree = (free_func)0; + z.opaque = (voidpf)0; + + z.next_in = bufferin; + z.avail_in = 0; + z.next_out = bufferout; + z.avail_out = L_BUF_SIZE; + + status = deflateInit(&z, ZLIB_COMPRESSION_LEVEL); + if (status != Z_OK) { + L_ERROR("deflateInit failed\n", procName); + success = FALSE; + goto cleanup_arrays; + } + + do { + if (z.avail_in == 0) { + z.next_in = bufferin; + bbufferWrite(bbin, bufferin, L_BUF_SIZE, &nbytes); +#if DEBUG + fprintf(stderr, " wrote %zu bytes to bufferin\n", nbytes); +#endif /* DEBUG */ + z.avail_in = nbytes; + } + flush = (bbin->n) ? Z_SYNC_FLUSH : Z_FINISH; + status = deflate(&z, flush); +#if DEBUG + fprintf(stderr, " status is %d, bytesleft = %u, totalout = %zu\n", + status, z.avail_out, z.total_out); +#endif /* DEBUG */ + nbytes = L_BUF_SIZE - z.avail_out; + if (nbytes) { + bbufferRead(bbout, bufferout, nbytes); +#if DEBUG + fprintf(stderr, " read %zu bytes from bufferout\n", nbytes); +#endif /* DEBUG */ + } + z.next_out = bufferout; + z.avail_out = L_BUF_SIZE; + } while (flush != Z_FINISH); + + deflateEnd(&z); + +cleanup_arrays: + if (success) { + dataout = bbufferDestroyAndSaveData(&bbout, pnout); + } else { + dataout = NULL; + bbufferDestroy(&bbout); + } + bbufferDestroy(&bbin); + LEPT_FREE(bufferin); + LEPT_FREE(bufferout); + return dataout; +} + + +/*! + * \brief zlibUncompress() + * + * \param[in] datain byte buffer with compressed input data + * \param[in] nin number of bytes of input data + * \param[out] pnout number of bytes of output data + * \return dataout uncompressed data, or NULL on error + * + *
+ * Notes:
+ *      (1) See zlibCompress().
+ * 
+ */ +l_uint8 * +zlibUncompress(l_uint8 *datain, + size_t nin, + size_t *pnout) +{ +l_uint8 *dataout; +l_uint8 *bufferin, *bufferout; +l_int32 status, success; +size_t nbytes; +L_BBUFFER *bbin, *bbout; +z_stream z; + + PROCNAME("zlibUncompress"); + + if (!datain) + return (l_uint8 *)ERROR_PTR("datain not defined", procName, NULL); + + /* Set up fixed size buffers used in z_stream */ + bufferin = (l_uint8 *)LEPT_CALLOC(L_BUF_SIZE, sizeof(l_uint8)); + bufferout = (l_uint8 *)LEPT_CALLOC(L_BUF_SIZE, sizeof(l_uint8)); + + /* Set up bbuffers and load bbin with the data */ + bbin = bbufferCreate(datain, nin); + bbout = bbufferCreate(NULL, 0); + + success = TRUE; + if (!bufferin || !bufferout || !bbin || !bbout) { + L_ERROR("calloc fail for buffer\n", procName); + success = FALSE; + goto cleanup_arrays; + } + + z.zalloc = (alloc_func)0; + z.zfree = (free_func)0; + + z.next_in = bufferin; + z.avail_in = 0; + z.next_out = bufferout; + z.avail_out = L_BUF_SIZE; + + inflateInit(&z); + + + for ( ; ; ) { + if (z.avail_in == 0) { + z.next_in = bufferin; + bbufferWrite(bbin, bufferin, L_BUF_SIZE, &nbytes); +#if DEBUG + fprintf(stderr, " wrote %d bytes to bufferin\n", nbytes); +#endif /* DEBUG */ + z.avail_in = nbytes; + } + if (z.avail_in == 0) + break; + status = inflate(&z, Z_SYNC_FLUSH); +#if DEBUG + fprintf(stderr, " status is %d, bytesleft = %d, totalout = %d\n", + status, z.avail_out, z.total_out); +#endif /* DEBUG */ + nbytes = L_BUF_SIZE - z.avail_out; + if (nbytes) { + bbufferRead(bbout, bufferout, nbytes); +#if DEBUG + fprintf(stderr, " read %d bytes from bufferout\n", nbytes); +#endif /* DEBUG */ + } + z.next_out = bufferout; + z.avail_out = L_BUF_SIZE; + } + + inflateEnd(&z); + +cleanup_arrays: + if (success) { + dataout = bbufferDestroyAndSaveData(&bbout, pnout); + } else { + dataout = NULL; + bbufferDestroy(&bbout); + } + bbufferDestroy(&bbin); + LEPT_FREE(bufferin); + LEPT_FREE(bufferout); + return dataout; +} + +/* --------------------------------------------*/ +#endif /* HAVE_LIBZ */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/leptonica/zlibmemstub.c b/hgdriver/3rdparty/hgOCR/leptonica/zlibmemstub.c new file mode 100644 index 0000000..eb03fed --- /dev/null +++ b/hgdriver/3rdparty/hgOCR/leptonica/zlibmemstub.c @@ -0,0 +1,59 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - 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. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``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 ANY + - CONTRIBUTORS 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. + *====================================================================*/ + +/*! + * \file zlibmemstub.c + *
+ *
+ *     Stubs for zlibmem.c functions
+ * 
+ */ + +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + +/* --------------------------------------------*/ +#if !HAVE_LIBZ /* defined in environ.h */ +/* --------------------------------------------*/ + +l_uint8 * zlibCompress(l_uint8 *datain, size_t nin, size_t *pnout) +{ + return (l_uint8 *)ERROR_PTR("function not present", "zlibCompress", NULL); +} + +/* ----------------------------------------------------------------------*/ + +l_uint8 * zlibUncompress(l_uint8 *datain, size_t nin, size_t *pnout) +{ + return (l_uint8 *)ERROR_PTR("function not present", "zlibUncompress", NULL); +} + +/* --------------------------------------------*/ +#endif /* !HAVE_LIBZ */ +/* --------------------------------------------*/ diff --git a/hgdriver/3rdparty/hgOCR/tessdata/osd.traineddata b/hgdriver/3rdparty/hgOCR/tessdata/osd.traineddata new file mode 100644 index 0000000..183644a Binary files /dev/null and b/hgdriver/3rdparty/hgOCR/tessdata/osd.traineddata differ diff --git a/hgdriver/3rdparty/lib/x86_64/libIlmImf.a b/hgdriver/3rdparty/lib/x86_64/libIlmImf.a new file mode 100644 index 0000000..f63e23f Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/libIlmImf.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/libittnotify.a b/hgdriver/3rdparty/lib/x86_64/libittnotify.a new file mode 100644 index 0000000..5b07788 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/libittnotify.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibjasper.a b/hgdriver/3rdparty/lib/x86_64/liblibjasper.a new file mode 100644 index 0000000..96e02f6 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibjasper.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibjpeg-turbo.a b/hgdriver/3rdparty/lib/x86_64/liblibjpeg-turbo.a new file mode 100644 index 0000000..c6b7007 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibjpeg-turbo.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibpng.a b/hgdriver/3rdparty/lib/x86_64/liblibpng.a new file mode 100644 index 0000000..d7d7fd9 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibpng.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibprotobuf.a b/hgdriver/3rdparty/lib/x86_64/liblibprotobuf.a new file mode 100644 index 0000000..db523d0 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibprotobuf.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibtiff.a b/hgdriver/3rdparty/lib/x86_64/liblibtiff.a new file mode 100644 index 0000000..d57c200 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibtiff.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/liblibwebp.a b/hgdriver/3rdparty/lib/x86_64/liblibwebp.a new file mode 100644 index 0000000..e867916 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/liblibwebp.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/libopencv_world.a b/hgdriver/3rdparty/lib/x86_64/libopencv_world.a new file mode 100644 index 0000000..2866c76 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/libopencv_world.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/libquirc.a b/hgdriver/3rdparty/lib/x86_64/libquirc.a new file mode 100644 index 0000000..3c527e3 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/libquirc.a differ diff --git a/hgdriver/3rdparty/lib/x86_64/libzlib.a b/hgdriver/3rdparty/lib/x86_64/libzlib.a new file mode 100644 index 0000000..6af0e88 Binary files /dev/null and b/hgdriver/3rdparty/lib/x86_64/libzlib.a differ diff --git a/hgdriver/3rdparty/libtiff/include/tiff.h b/hgdriver/3rdparty/libtiff/include/tiff.h new file mode 100644 index 0000000..5b0a0c9 --- /dev/null +++ b/hgdriver/3rdparty/libtiff/include/tiff.h @@ -0,0 +1,695 @@ +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFF_ +#define _TIFF_ + +#include "tiffconf.h" + +/* + * Tag Image File Format (TIFF) + * + * Based on Rev 6.0 from: + * Developer's Desk + * Aldus Corporation + * 411 First Ave. South + * Suite 200 + * Seattle, WA 98104 + * 206-622-5500 + * + * (http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf) + * + * For BigTIFF design notes see the following links + * http://www.remotesensing.org/libtiff/bigtiffdesign.html + * http://www.awaresystems.be/imaging/tiff/bigtiff.html + */ + +#define TIFF_VERSION_CLASSIC 42 +#define TIFF_VERSION_BIG 43 + +#define TIFF_BIGENDIAN 0x4d4d +#define TIFF_LITTLEENDIAN 0x4949 +#define MDI_LITTLEENDIAN 0x5045 +#define MDI_BIGENDIAN 0x4550 + +/* + * Intrinsic data types required by the file format: + * + * 8-bit quantities int8/uint8 + * 16-bit quantities int16/uint16 + * 32-bit quantities int32/uint32 + * 64-bit quantities int64/uint64 + * strings unsigned char* + */ + +typedef TIFF_INT8_T int8; +typedef TIFF_UINT8_T uint8; + +typedef TIFF_INT16_T int16; +typedef TIFF_UINT16_T uint16; + +typedef TIFF_INT32_T int32; +typedef TIFF_UINT32_T uint32; + +typedef TIFF_INT64_T int64; +typedef TIFF_UINT64_T uint64; + +/* + * Some types as promoted in a variable argument list + * We use uint16_vap rather then directly using int, because this way + * we document the type we actually want to pass through, conceptually, + * rather then confusing the issue by merely stating the type it gets + * promoted to + */ + +typedef int uint16_vap; + +/* + * TIFF header. + */ +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ +} TIFFHeaderCommon; +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ + uint32 tiff_diroff; /* byte offset to first directory */ +} TIFFHeaderClassic; +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ + uint16 tiff_offsetsize; /* size of offsets, should be 8 */ + uint16 tiff_unused; /* unused word, should be 0 */ + uint64 tiff_diroff; /* byte offset to first directory */ +} TIFFHeaderBig; + + +/* + * NB: In the comments below, + * - items marked with a + are obsoleted by revision 5.0, + * - items marked with a ! are introduced in revision 6.0. + * - items marked with a % are introduced post revision 6.0. + * - items marked with a $ are obsoleted by revision 6.0. + * - items marked with a & are introduced by Adobe DNG specification. + */ + +/* + * Tag data type information. + * + * Note: RATIONALs are the ratio of two 32-bit integer values. + */ +typedef enum { + TIFF_NOTYPE = 0, /* placeholder */ + TIFF_BYTE = 1, /* 8-bit unsigned integer */ + TIFF_ASCII = 2, /* 8-bit bytes w/ last byte null */ + TIFF_SHORT = 3, /* 16-bit unsigned integer */ + TIFF_LONG = 4, /* 32-bit unsigned integer */ + TIFF_RATIONAL = 5, /* 64-bit unsigned fraction */ + TIFF_SBYTE = 6, /* !8-bit signed integer */ + TIFF_UNDEFINED = 7, /* !8-bit untyped data */ + TIFF_SSHORT = 8, /* !16-bit signed integer */ + TIFF_SLONG = 9, /* !32-bit signed integer */ + TIFF_SRATIONAL = 10, /* !64-bit signed fraction */ + TIFF_FLOAT = 11, /* !32-bit IEEE floating point */ + TIFF_DOUBLE = 12, /* !64-bit IEEE floating point */ + TIFF_IFD = 13, /* %32-bit unsigned integer (offset) */ + TIFF_LONG8 = 16, /* BigTIFF 64-bit unsigned integer */ + TIFF_SLONG8 = 17, /* BigTIFF 64-bit signed integer */ + TIFF_IFD8 = 18 /* BigTIFF 64-bit unsigned integer (offset) */ +} TIFFDataType; + +/* + * TIFF Tag Definitions. + */ +#define TIFFTAG_SUBFILETYPE 254 /* subfile data descriptor */ +#define FILETYPE_REDUCEDIMAGE 0x1 /* reduced resolution version */ +#define FILETYPE_PAGE 0x2 /* one page of many */ +#define FILETYPE_MASK 0x4 /* transparency mask */ +#define TIFFTAG_OSUBFILETYPE 255 /* +kind of data in subfile */ +#define OFILETYPE_IMAGE 1 /* full resolution image data */ +#define OFILETYPE_REDUCEDIMAGE 2 /* reduced size image data */ +#define OFILETYPE_PAGE 3 /* one page of many */ +#define TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */ +#define TIFFTAG_IMAGELENGTH 257 /* image height in pixels */ +#define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */ +#define TIFFTAG_COMPRESSION 259 /* data compression technique */ +#define COMPRESSION_NONE 1 /* dump mode */ +#define COMPRESSION_CCITTRLE 2 /* CCITT modified Huffman RLE */ +#define COMPRESSION_CCITTFAX3 3 /* CCITT Group 3 fax encoding */ +#define COMPRESSION_CCITT_T4 3 /* CCITT T.4 (TIFF 6 name) */ +#define COMPRESSION_CCITTFAX4 4 /* CCITT Group 4 fax encoding */ +#define COMPRESSION_CCITT_T6 4 /* CCITT T.6 (TIFF 6 name) */ +#define COMPRESSION_LZW 5 /* Lempel-Ziv & Welch */ +#define COMPRESSION_OJPEG 6 /* !6.0 JPEG */ +#define COMPRESSION_JPEG 7 /* %JPEG DCT compression */ +#define COMPRESSION_T85 9 /* !TIFF/FX T.85 JBIG compression */ +#define COMPRESSION_T43 10 /* !TIFF/FX T.43 colour by layered JBIG compression */ +#define COMPRESSION_NEXT 32766 /* NeXT 2-bit RLE */ +#define COMPRESSION_CCITTRLEW 32771 /* #1 w/ word alignment */ +#define COMPRESSION_PACKBITS 32773 /* Macintosh RLE */ +#define COMPRESSION_THUNDERSCAN 32809 /* ThunderScan RLE */ +/* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT */ +#define COMPRESSION_DCS 32947 /* Kodak DCS encoding */ +#define COMPRESSION_JBIG 34661 /* ISO JBIG */ +#define COMPRESSION_SGILOG 34676 /* SGI Log Luminance RLE */ +#define COMPRESSION_SGILOG24 34677 /* SGI Log 24-bit packed */ +#define COMPRESSION_JP2000 34712 /* Leadtools JPEG2000 */ +#define COMPRESSION_LERC 34887 /* ESRI Lerc codec: https://github.com/Esri/lerc */ +/* compression codes 34887-34889 are reserved for ESRI */ +#define COMPRESSION_LZMA 34925 /* LZMA2 */ +#define COMPRESSION_ZSTD 50000 /* ZSTD: WARNING not registered in Adobe-maintained registry */ +#define COMPRESSION_WEBP 50001 /* WEBP: WARNING not registered in Adobe-maintained registry */ +#define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */ +#define PHOTOMETRIC_MINISWHITE 0 /* min value is white */ +#define PHOTOMETRIC_MINISBLACK 1 /* min value is black */ +#define PHOTOMETRIC_RGB 2 /* RGB color model */ +#define PHOTOMETRIC_PALETTE 3 /* color map indexed */ +#define PHOTOMETRIC_MASK 4 /* $holdout mask */ +#define PHOTOMETRIC_SEPARATED 5 /* !color separations */ +#define PHOTOMETRIC_YCBCR 6 /* !CCIR 601 */ +#define PHOTOMETRIC_CIELAB 8 /* !1976 CIE L*a*b* */ +#define PHOTOMETRIC_ICCLAB 9 /* ICC L*a*b* [Adobe TIFF Technote 4] */ +#define PHOTOMETRIC_ITULAB 10 /* ITU L*a*b* */ +#define PHOTOMETRIC_CFA 32803 /* color filter array */ +#define PHOTOMETRIC_LOGL 32844 /* CIE Log2(L) */ +#define PHOTOMETRIC_LOGLUV 32845 /* CIE Log2(L) (u',v') */ +#define TIFFTAG_THRESHHOLDING 263 /* +thresholding used on data */ +#define THRESHHOLD_BILEVEL 1 /* b&w art scan */ +#define THRESHHOLD_HALFTONE 2 /* or dithered scan */ +#define THRESHHOLD_ERRORDIFFUSE 3 /* usually floyd-steinberg */ +#define TIFFTAG_CELLWIDTH 264 /* +dithering matrix width */ +#define TIFFTAG_CELLLENGTH 265 /* +dithering matrix height */ +#define TIFFTAG_FILLORDER 266 /* data order within a byte */ +#define FILLORDER_MSB2LSB 1 /* most significant -> least */ +#define FILLORDER_LSB2MSB 2 /* least significant -> most */ +#define TIFFTAG_DOCUMENTNAME 269 /* name of doc. image is from */ +#define TIFFTAG_IMAGEDESCRIPTION 270 /* info about image */ +#define TIFFTAG_MAKE 271 /* scanner manufacturer name */ +#define TIFFTAG_MODEL 272 /* scanner model name/number */ +#define TIFFTAG_STRIPOFFSETS 273 /* offsets to data strips */ +#define TIFFTAG_ORIENTATION 274 /* +image orientation */ +#define ORIENTATION_TOPLEFT 1 /* row 0 top, col 0 lhs */ +#define ORIENTATION_TOPRIGHT 2 /* row 0 top, col 0 rhs */ +#define ORIENTATION_BOTRIGHT 3 /* row 0 bottom, col 0 rhs */ +#define ORIENTATION_BOTLEFT 4 /* row 0 bottom, col 0 lhs */ +#define ORIENTATION_LEFTTOP 5 /* row 0 lhs, col 0 top */ +#define ORIENTATION_RIGHTTOP 6 /* row 0 rhs, col 0 top */ +#define ORIENTATION_RIGHTBOT 7 /* row 0 rhs, col 0 bottom */ +#define ORIENTATION_LEFTBOT 8 /* row 0 lhs, col 0 bottom */ +#define TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */ +#define TIFFTAG_ROWSPERSTRIP 278 /* rows per strip of data */ +#define TIFFTAG_STRIPBYTECOUNTS 279 /* bytes counts for strips */ +#define TIFFTAG_MINSAMPLEVALUE 280 /* +minimum sample value */ +#define TIFFTAG_MAXSAMPLEVALUE 281 /* +maximum sample value */ +#define TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */ +#define TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */ +#define TIFFTAG_PLANARCONFIG 284 /* storage organization */ +#define PLANARCONFIG_CONTIG 1 /* single image plane */ +#define PLANARCONFIG_SEPARATE 2 /* separate planes of data */ +#define TIFFTAG_PAGENAME 285 /* page name image is from */ +#define TIFFTAG_XPOSITION 286 /* x page offset of image lhs */ +#define TIFFTAG_YPOSITION 287 /* y page offset of image lhs */ +#define TIFFTAG_FREEOFFSETS 288 /* +byte offset to free block */ +#define TIFFTAG_FREEBYTECOUNTS 289 /* +sizes of free blocks */ +#define TIFFTAG_GRAYRESPONSEUNIT 290 /* $gray scale curve accuracy */ +#define GRAYRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define GRAYRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define GRAYRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define GRAYRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define GRAYRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_GRAYRESPONSECURVE 291 /* $gray scale response curve */ +#define TIFFTAG_GROUP3OPTIONS 292 /* 32 flag bits */ +#define TIFFTAG_T4OPTIONS 292 /* TIFF 6.0 proper name alias */ +#define GROUP3OPT_2DENCODING 0x1 /* 2-dimensional coding */ +#define GROUP3OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define GROUP3OPT_FILLBITS 0x4 /* fill to byte boundary */ +#define TIFFTAG_GROUP4OPTIONS 293 /* 32 flag bits */ +#define TIFFTAG_T6OPTIONS 293 /* TIFF 6.0 proper name */ +#define GROUP4OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */ +#define RESUNIT_NONE 1 /* no meaningful units */ +#define RESUNIT_INCH 2 /* english */ +#define RESUNIT_CENTIMETER 3 /* metric */ +#define TIFFTAG_PAGENUMBER 297 /* page numbers of multi-page */ +#define TIFFTAG_COLORRESPONSEUNIT 300 /* $color curve accuracy */ +#define COLORRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define COLORRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define COLORRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define COLORRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define COLORRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_TRANSFERFUNCTION 301 /* !colorimetry info */ +#define TIFFTAG_SOFTWARE 305 /* name & release */ +#define TIFFTAG_DATETIME 306 /* creation date and time */ +#define TIFFTAG_ARTIST 315 /* creator of image */ +#define TIFFTAG_HOSTCOMPUTER 316 /* machine where created */ +#define TIFFTAG_PREDICTOR 317 /* prediction scheme w/ LZW */ +#define PREDICTOR_NONE 1 /* no prediction scheme used */ +#define PREDICTOR_HORIZONTAL 2 /* horizontal differencing */ +#define PREDICTOR_FLOATINGPOINT 3 /* floating point predictor */ +#define TIFFTAG_WHITEPOINT 318 /* image white point */ +#define TIFFTAG_PRIMARYCHROMATICITIES 319 /* !primary chromaticities */ +#define TIFFTAG_COLORMAP 320 /* RGB map for palette image */ +#define TIFFTAG_HALFTONEHINTS 321 /* !highlight+shadow info */ +#define TIFFTAG_TILEWIDTH 322 /* !tile width in pixels */ +#define TIFFTAG_TILELENGTH 323 /* !tile height in pixels */ +#define TIFFTAG_TILEOFFSETS 324 /* !offsets to data tiles */ +#define TIFFTAG_TILEBYTECOUNTS 325 /* !byte counts for tiles */ +#define TIFFTAG_BADFAXLINES 326 /* lines w/ wrong pixel count */ +#define TIFFTAG_CLEANFAXDATA 327 /* regenerated line info */ +#define CLEANFAXDATA_CLEAN 0 /* no errors detected */ +#define CLEANFAXDATA_REGENERATED 1 /* receiver regenerated lines */ +#define CLEANFAXDATA_UNCLEAN 2 /* uncorrected errors exist */ +#define TIFFTAG_CONSECUTIVEBADFAXLINES 328 /* max consecutive bad lines */ +#define TIFFTAG_SUBIFD 330 /* subimage descriptors */ +#define TIFFTAG_INKSET 332 /* !inks in separated image */ +#define INKSET_CMYK 1 /* !cyan-magenta-yellow-black color */ +#define INKSET_MULTIINK 2 /* !multi-ink or hi-fi color */ +#define TIFFTAG_INKNAMES 333 /* !ascii names of inks */ +#define TIFFTAG_NUMBEROFINKS 334 /* !number of inks */ +#define TIFFTAG_DOTRANGE 336 /* !0% and 100% dot codes */ +#define TIFFTAG_TARGETPRINTER 337 /* !separation target */ +#define TIFFTAG_EXTRASAMPLES 338 /* !info about extra samples */ +#define EXTRASAMPLE_UNSPECIFIED 0 /* !unspecified data */ +#define EXTRASAMPLE_ASSOCALPHA 1 /* !associated alpha data */ +#define EXTRASAMPLE_UNASSALPHA 2 /* !unassociated alpha data */ +#define TIFFTAG_SAMPLEFORMAT 339 /* !data sample format */ +#define SAMPLEFORMAT_UINT 1 /* !unsigned integer data */ +#define SAMPLEFORMAT_INT 2 /* !signed integer data */ +#define SAMPLEFORMAT_IEEEFP 3 /* !IEEE floating point data */ +#define SAMPLEFORMAT_VOID 4 /* !untyped data */ +#define SAMPLEFORMAT_COMPLEXINT 5 /* !complex signed int */ +#define SAMPLEFORMAT_COMPLEXIEEEFP 6 /* !complex ieee floating */ +#define TIFFTAG_SMINSAMPLEVALUE 340 /* !variable MinSampleValue */ +#define TIFFTAG_SMAXSAMPLEVALUE 341 /* !variable MaxSampleValue */ +#define TIFFTAG_CLIPPATH 343 /* %ClipPath + [Adobe TIFF technote 2] */ +#define TIFFTAG_XCLIPPATHUNITS 344 /* %XClipPathUnits + [Adobe TIFF technote 2] */ +#define TIFFTAG_YCLIPPATHUNITS 345 /* %YClipPathUnits + [Adobe TIFF technote 2] */ +#define TIFFTAG_INDEXED 346 /* %Indexed + [Adobe TIFF Technote 3] */ +#define TIFFTAG_JPEGTABLES 347 /* %JPEG table stream */ +#define TIFFTAG_OPIPROXY 351 /* %OPI Proxy [Adobe TIFF technote] */ +/* Tags 400-435 are from the TIFF/FX spec */ +#define TIFFTAG_GLOBALPARAMETERSIFD 400 /* ! */ +#define TIFFTAG_PROFILETYPE 401 /* ! */ +#define PROFILETYPE_UNSPECIFIED 0 /* ! */ +#define PROFILETYPE_G3_FAX 1 /* ! */ +#define TIFFTAG_FAXPROFILE 402 /* ! */ +#define FAXPROFILE_S 1 /* !TIFF/FX FAX profile S */ +#define FAXPROFILE_F 2 /* !TIFF/FX FAX profile F */ +#define FAXPROFILE_J 3 /* !TIFF/FX FAX profile J */ +#define FAXPROFILE_C 4 /* !TIFF/FX FAX profile C */ +#define FAXPROFILE_L 5 /* !TIFF/FX FAX profile L */ +#define FAXPROFILE_M 6 /* !TIFF/FX FAX profile LM */ +#define TIFFTAG_CODINGMETHODS 403 /* !TIFF/FX coding methods */ +#define CODINGMETHODS_T4_1D (1 << 1) /* !T.4 1D */ +#define CODINGMETHODS_T4_2D (1 << 2) /* !T.4 2D */ +#define CODINGMETHODS_T6 (1 << 3) /* !T.6 */ +#define CODINGMETHODS_T85 (1 << 4) /* !T.85 JBIG */ +#define CODINGMETHODS_T42 (1 << 5) /* !T.42 JPEG */ +#define CODINGMETHODS_T43 (1 << 6) /* !T.43 colour by layered JBIG */ +#define TIFFTAG_VERSIONYEAR 404 /* !TIFF/FX version year */ +#define TIFFTAG_MODENUMBER 405 /* !TIFF/FX mode number */ +#define TIFFTAG_DECODE 433 /* !TIFF/FX decode */ +#define TIFFTAG_IMAGEBASECOLOR 434 /* !TIFF/FX image base colour */ +#define TIFFTAG_T82OPTIONS 435 /* !TIFF/FX T.82 options */ +/* + * Tags 512-521 are obsoleted by Technical Note #2 which specifies a + * revised JPEG-in-TIFF scheme. + */ +#define TIFFTAG_JPEGPROC 512 /* !JPEG processing algorithm */ +#define JPEGPROC_BASELINE 1 /* !baseline sequential */ +#define JPEGPROC_LOSSLESS 14 /* !Huffman coded lossless */ +#define TIFFTAG_JPEGIFOFFSET 513 /* !pointer to SOI marker */ +#define TIFFTAG_JPEGIFBYTECOUNT 514 /* !JFIF stream length */ +#define TIFFTAG_JPEGRESTARTINTERVAL 515 /* !restart interval length */ +#define TIFFTAG_JPEGLOSSLESSPREDICTORS 517 /* !lossless proc predictor */ +#define TIFFTAG_JPEGPOINTTRANSFORM 518 /* !lossless point transform */ +#define TIFFTAG_JPEGQTABLES 519 /* !Q matrix offsets */ +#define TIFFTAG_JPEGDCTABLES 520 /* !DCT table offsets */ +#define TIFFTAG_JPEGACTABLES 521 /* !AC coefficient offsets */ +#define TIFFTAG_YCBCRCOEFFICIENTS 529 /* !RGB -> YCbCr transform */ +#define TIFFTAG_YCBCRSUBSAMPLING 530 /* !YCbCr subsampling factors */ +#define TIFFTAG_YCBCRPOSITIONING 531 /* !subsample positioning */ +#define YCBCRPOSITION_CENTERED 1 /* !as in PostScript Level 2 */ +#define YCBCRPOSITION_COSITED 2 /* !as in CCIR 601-1 */ +#define TIFFTAG_REFERENCEBLACKWHITE 532 /* !colorimetry info */ +#define TIFFTAG_STRIPROWCOUNTS 559 /* !TIFF/FX strip row counts */ +#define TIFFTAG_XMLPACKET 700 /* %XML packet + [Adobe XMP Specification, + January 2004 */ +#define TIFFTAG_OPIIMAGEID 32781 /* %OPI ImageID + [Adobe TIFF technote] */ +/* tags 32952-32956 are private tags registered to Island Graphics */ +#define TIFFTAG_REFPTS 32953 /* image reference points */ +#define TIFFTAG_REGIONTACKPOINT 32954 /* region-xform tack point */ +#define TIFFTAG_REGIONWARPCORNERS 32955 /* warp quadrilateral */ +#define TIFFTAG_REGIONAFFINE 32956 /* affine transformation mat */ +/* tags 32995-32999 are private tags registered to SGI */ +#define TIFFTAG_MATTEING 32995 /* $use ExtraSamples */ +#define TIFFTAG_DATATYPE 32996 /* $use SampleFormat */ +#define TIFFTAG_IMAGEDEPTH 32997 /* z depth of image */ +#define TIFFTAG_TILEDEPTH 32998 /* z depth/data tile */ +/* tags 33300-33309 are private tags registered to Pixar */ +/* + * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH + * are set when an image has been cropped out of a larger image. + * They reflect the size of the original uncropped image. + * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used + * to determine the position of the smaller image in the larger one. + */ +#define TIFFTAG_PIXAR_IMAGEFULLWIDTH 33300 /* full image size in x */ +#define TIFFTAG_PIXAR_IMAGEFULLLENGTH 33301 /* full image size in y */ + /* Tags 33302-33306 are used to identify special image modes and data + * used by Pixar's texture formats. + */ +#define TIFFTAG_PIXAR_TEXTUREFORMAT 33302 /* texture map format */ +#define TIFFTAG_PIXAR_WRAPMODES 33303 /* s & t wrap modes */ +#define TIFFTAG_PIXAR_FOVCOT 33304 /* cotan(fov) for env. maps */ +#define TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN 33305 +#define TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA 33306 +/* tag 33405 is a private tag registered to Eastman Kodak */ +#define TIFFTAG_WRITERSERIALNUMBER 33405 /* device serial number */ +#define TIFFTAG_CFAREPEATPATTERNDIM 33421 /* dimensions of CFA pattern */ +#define TIFFTAG_CFAPATTERN 33422 /* color filter array pattern */ +/* tag 33432 is listed in the 6.0 spec w/ unknown ownership */ +#define TIFFTAG_COPYRIGHT 33432 /* copyright string */ +/* IPTC TAG from RichTIFF specifications */ +#define TIFFTAG_RICHTIFFIPTC 33723 +/* 34016-34029 are reserved for ANSI IT8 TIFF/IT */ +#define TIFFTAG_STONITS 37439 /* Sample value to Nits */ +/* tag 34929 is a private tag registered to FedEx */ +#define TIFFTAG_FEDEX_EDR 34929 /* unknown use */ +#define TIFFTAG_INTEROPERABILITYIFD 40965 /* Pointer to Interoperability private directory */ +/* tags 50674 to 50677 are reserved for ESRI */ +#define TIFFTAG_LERC_PARAMETERS 50674 /* Stores LERC version and additional compression method */ +/* Adobe Digital Negative (DNG) format tags */ +#define TIFFTAG_DNGVERSION 50706 /* &DNG version number */ +#define TIFFTAG_DNGBACKWARDVERSION 50707 /* &DNG compatibility version */ +#define TIFFTAG_UNIQUECAMERAMODEL 50708 /* &name for the camera model */ +#define TIFFTAG_LOCALIZEDCAMERAMODEL 50709 /* &localized camera model + name */ +#define TIFFTAG_CFAPLANECOLOR 50710 /* &CFAPattern->LinearRaw space + mapping */ +#define TIFFTAG_CFALAYOUT 50711 /* &spatial layout of the CFA */ +#define TIFFTAG_LINEARIZATIONTABLE 50712 /* &lookup table description */ +#define TIFFTAG_BLACKLEVELREPEATDIM 50713 /* &repeat pattern size for + the BlackLevel tag */ +#define TIFFTAG_BLACKLEVEL 50714 /* &zero light encoding level */ +#define TIFFTAG_BLACKLEVELDELTAH 50715 /* &zero light encoding level + differences (columns) */ +#define TIFFTAG_BLACKLEVELDELTAV 50716 /* &zero light encoding level + differences (rows) */ +#define TIFFTAG_WHITELEVEL 50717 /* &fully saturated encoding + level */ +#define TIFFTAG_DEFAULTSCALE 50718 /* &default scale factors */ +#define TIFFTAG_DEFAULTCROPORIGIN 50719 /* &origin of the final image + area */ +#define TIFFTAG_DEFAULTCROPSIZE 50720 /* &size of the final image + area */ +#define TIFFTAG_COLORMATRIX1 50721 /* &XYZ->reference color space + transformation matrix 1 */ +#define TIFFTAG_COLORMATRIX2 50722 /* &XYZ->reference color space + transformation matrix 2 */ +#define TIFFTAG_CAMERACALIBRATION1 50723 /* &calibration matrix 1 */ +#define TIFFTAG_CAMERACALIBRATION2 50724 /* &calibration matrix 2 */ +#define TIFFTAG_REDUCTIONMATRIX1 50725 /* &dimensionality reduction + matrix 1 */ +#define TIFFTAG_REDUCTIONMATRIX2 50726 /* &dimensionality reduction + matrix 2 */ +#define TIFFTAG_ANALOGBALANCE 50727 /* &gain applied the stored raw + values*/ +#define TIFFTAG_ASSHOTNEUTRAL 50728 /* &selected white balance in + linear reference space */ +#define TIFFTAG_ASSHOTWHITEXY 50729 /* &selected white balance in + x-y chromaticity + coordinates */ +#define TIFFTAG_BASELINEEXPOSURE 50730 /* &how much to move the zero + point */ +#define TIFFTAG_BASELINENOISE 50731 /* &relative noise level */ +#define TIFFTAG_BASELINESHARPNESS 50732 /* &relative amount of + sharpening */ +#define TIFFTAG_BAYERGREENSPLIT 50733 /* &how closely the values of + the green pixels in the + blue/green rows track the + values of the green pixels + in the red/green rows */ +#define TIFFTAG_LINEARRESPONSELIMIT 50734 /* &non-linear encoding range */ +#define TIFFTAG_CAMERASERIALNUMBER 50735 /* &camera's serial number */ +#define TIFFTAG_LENSINFO 50736 /* info about the lens */ +#define TIFFTAG_CHROMABLURRADIUS 50737 /* &chroma blur radius */ +#define TIFFTAG_ANTIALIASSTRENGTH 50738 /* &relative strength of the + camera's anti-alias filter */ +#define TIFFTAG_SHADOWSCALE 50739 /* &used by Adobe Camera Raw */ +#define TIFFTAG_DNGPRIVATEDATA 50740 /* &manufacturer's private data */ +#define TIFFTAG_MAKERNOTESAFETY 50741 /* &whether the EXIF MakerNote + tag is safe to preserve + along with the rest of the + EXIF data */ +#define TIFFTAG_CALIBRATIONILLUMINANT1 50778 /* &illuminant 1 */ +#define TIFFTAG_CALIBRATIONILLUMINANT2 50779 /* &illuminant 2 */ +#define TIFFTAG_BESTQUALITYSCALE 50780 /* &best quality multiplier */ +#define TIFFTAG_RAWDATAUNIQUEID 50781 /* &unique identifier for + the raw image data */ +#define TIFFTAG_ORIGINALRAWFILENAME 50827 /* &file name of the original + raw file */ +#define TIFFTAG_ORIGINALRAWFILEDATA 50828 /* &contents of the original + raw file */ +#define TIFFTAG_ACTIVEAREA 50829 /* &active (non-masked) pixels + of the sensor */ +#define TIFFTAG_MASKEDAREAS 50830 /* &list of coordinates + of fully masked pixels */ +#define TIFFTAG_ASSHOTICCPROFILE 50831 /* &these two tags used to */ +#define TIFFTAG_ASSHOTPREPROFILEMATRIX 50832 /* map cameras's color space + into ICC profile space */ +#define TIFFTAG_CURRENTICCPROFILE 50833 /* & */ +#define TIFFTAG_CURRENTPREPROFILEMATRIX 50834 /* & */ +/* tag 65535 is an undefined tag used by Eastman Kodak */ +#define TIFFTAG_DCSHUESHIFTVALUES 65535 /* hue shift correction data */ + +/* + * The following are ``pseudo tags'' that can be used to control + * codec-specific functionality. These tags are not written to file. + * Note that these values start at 0xffff+1 so that they'll never + * collide with Aldus-assigned tags. + * + * If you want your private pseudo tags ``registered'' (i.e. added to + * this file), please post a bug report via the tracking system at + * http://www.remotesensing.org/libtiff/bugs.html with the appropriate + * C definitions to add. + */ +#define TIFFTAG_FAXMODE 65536 /* Group 3/4 format control */ +#define FAXMODE_CLASSIC 0x0000 /* default, include RTC */ +#define FAXMODE_NORTC 0x0001 /* no RTC at end of data */ +#define FAXMODE_NOEOL 0x0002 /* no EOL code at end of row */ +#define FAXMODE_BYTEALIGN 0x0004 /* byte align row */ +#define FAXMODE_WORDALIGN 0x0008 /* word align row */ +#define FAXMODE_CLASSF FAXMODE_NORTC /* TIFF Class F */ +#define TIFFTAG_JPEGQUALITY 65537 /* Compression quality level */ +/* Note: quality level is on the IJG 0-100 scale. Default value is 75 */ +#define TIFFTAG_JPEGCOLORMODE 65538 /* Auto RGB<=>YCbCr convert? */ +#define JPEGCOLORMODE_RAW 0x0000 /* no conversion (default) */ +#define JPEGCOLORMODE_RGB 0x0001 /* do auto conversion */ +#define TIFFTAG_JPEGTABLESMODE 65539 /* What to put in JPEGTables */ +#define JPEGTABLESMODE_QUANT 0x0001 /* include quantization tbls */ +#define JPEGTABLESMODE_HUFF 0x0002 /* include Huffman tbls */ +/* Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF */ +#define TIFFTAG_FAXFILLFUNC 65540 /* G3/G4 fill function */ +#define TIFFTAG_PIXARLOGDATAFMT 65549 /* PixarLogCodec I/O data sz */ +#define PIXARLOGDATAFMT_8BIT 0 /* regular u_char samples */ +#define PIXARLOGDATAFMT_8BITABGR 1 /* ABGR-order u_chars */ +#define PIXARLOGDATAFMT_11BITLOG 2 /* 11-bit log-encoded (raw) */ +#define PIXARLOGDATAFMT_12BITPICIO 3 /* as per PICIO (1.0==2048) */ +#define PIXARLOGDATAFMT_16BIT 4 /* signed short samples */ +#define PIXARLOGDATAFMT_FLOAT 5 /* IEEE float samples */ +/* 65550-65556 are allocated to Oceana Matrix */ +#define TIFFTAG_DCSIMAGERTYPE 65550 /* imager model & filter */ +#define DCSIMAGERMODEL_M3 0 /* M3 chip (1280 x 1024) */ +#define DCSIMAGERMODEL_M5 1 /* M5 chip (1536 x 1024) */ +#define DCSIMAGERMODEL_M6 2 /* M6 chip (3072 x 2048) */ +#define DCSIMAGERFILTER_IR 0 /* infrared filter */ +#define DCSIMAGERFILTER_MONO 1 /* monochrome filter */ +#define DCSIMAGERFILTER_CFA 2 /* color filter array */ +#define DCSIMAGERFILTER_OTHER 3 /* other filter */ +#define TIFFTAG_DCSINTERPMODE 65551 /* interpolation mode */ +#define DCSINTERPMODE_NORMAL 0x0 /* whole image, default */ +#define DCSINTERPMODE_PREVIEW 0x1 /* preview of image (384x256) */ +#define TIFFTAG_DCSBALANCEARRAY 65552 /* color balance values */ +#define TIFFTAG_DCSCORRECTMATRIX 65553 /* color correction values */ +#define TIFFTAG_DCSGAMMA 65554 /* gamma value */ +#define TIFFTAG_DCSTOESHOULDERPTS 65555 /* toe & shoulder points */ +#define TIFFTAG_DCSCALIBRATIONFD 65556 /* calibration file desc */ +/* Note: quality level is on the ZLIB 1-9 scale. Default value is -1 */ +#define TIFFTAG_ZIPQUALITY 65557 /* compression quality level */ +#define TIFFTAG_PIXARLOGQUALITY 65558 /* PixarLog uses same scale */ +/* 65559 is allocated to Oceana Matrix */ +#define TIFFTAG_DCSCLIPRECTANGLE 65559 /* area of image to acquire */ +#define TIFFTAG_SGILOGDATAFMT 65560 /* SGILog user data format */ +#define SGILOGDATAFMT_FLOAT 0 /* IEEE float samples */ +#define SGILOGDATAFMT_16BIT 1 /* 16-bit samples */ +#define SGILOGDATAFMT_RAW 2 /* uninterpreted data */ +#define SGILOGDATAFMT_8BIT 3 /* 8-bit RGB monitor values */ +#define TIFFTAG_SGILOGENCODE 65561 /* SGILog data encoding control*/ +#define SGILOGENCODE_NODITHER 0 /* do not dither encoded values*/ +#define SGILOGENCODE_RANDITHER 1 /* randomly dither encd values */ +#define TIFFTAG_LZMAPRESET 65562 /* LZMA2 preset (compression level) */ +#define TIFFTAG_PERSAMPLE 65563 /* interface for per sample tags */ +#define PERSAMPLE_MERGED 0 /* present as a single value */ +#define PERSAMPLE_MULTI 1 /* present as multiple values */ +#define TIFFTAG_ZSTD_LEVEL 65564 /* ZSTD compression level */ +#define TIFFTAG_LERC_VERSION 65565 /* LERC version */ +#define LERC_VERSION_2_4 4 +#define TIFFTAG_LERC_ADD_COMPRESSION 65566 /* LERC additional compression */ +#define LERC_ADD_COMPRESSION_NONE 0 +#define LERC_ADD_COMPRESSION_DEFLATE 1 +#define LERC_ADD_COMPRESSION_ZSTD 2 +#define TIFFTAG_LERC_MAXZERROR 65567 /* LERC maximum error */ +#define TIFFTAG_WEBP_LEVEL 65568 /* WebP compression level: WARNING not registered in Adobe-maintained registry */ +#define TIFFTAG_WEBP_LOSSLESS 65569 /* WebP lossless/lossy : WARNING not registered in Adobe-maintained registry */ + +/* + * EXIF tags + */ +#define EXIFTAG_EXPOSURETIME 33434 /* Exposure time */ +#define EXIFTAG_FNUMBER 33437 /* F number */ +#define EXIFTAG_EXPOSUREPROGRAM 34850 /* Exposure program */ +#define EXIFTAG_SPECTRALSENSITIVITY 34852 /* Spectral sensitivity */ +#define EXIFTAG_ISOSPEEDRATINGS 34855 /* ISO speed rating */ +#define EXIFTAG_OECF 34856 /* Optoelectric conversion + factor */ +#define EXIFTAG_EXIFVERSION 36864 /* Exif version */ +#define EXIFTAG_DATETIMEORIGINAL 36867 /* Date and time of original + data generation */ +#define EXIFTAG_DATETIMEDIGITIZED 36868 /* Date and time of digital + data generation */ +#define EXIFTAG_COMPONENTSCONFIGURATION 37121 /* Meaning of each component */ +#define EXIFTAG_COMPRESSEDBITSPERPIXEL 37122 /* Image compression mode */ +#define EXIFTAG_SHUTTERSPEEDVALUE 37377 /* Shutter speed */ +#define EXIFTAG_APERTUREVALUE 37378 /* Aperture */ +#define EXIFTAG_BRIGHTNESSVALUE 37379 /* Brightness */ +#define EXIFTAG_EXPOSUREBIASVALUE 37380 /* Exposure bias */ +#define EXIFTAG_MAXAPERTUREVALUE 37381 /* Maximum lens aperture */ +#define EXIFTAG_SUBJECTDISTANCE 37382 /* Subject distance */ +#define EXIFTAG_METERINGMODE 37383 /* Metering mode */ +#define EXIFTAG_LIGHTSOURCE 37384 /* Light source */ +#define EXIFTAG_FLASH 37385 /* Flash */ +#define EXIFTAG_FOCALLENGTH 37386 /* Lens focal length */ +#define EXIFTAG_SUBJECTAREA 37396 /* Subject area */ +#define EXIFTAG_MAKERNOTE 37500 /* Manufacturer notes */ +#define EXIFTAG_USERCOMMENT 37510 /* User comments */ +#define EXIFTAG_SUBSECTIME 37520 /* DateTime subseconds */ +#define EXIFTAG_SUBSECTIMEORIGINAL 37521 /* DateTimeOriginal subseconds */ +#define EXIFTAG_SUBSECTIMEDIGITIZED 37522 /* DateTimeDigitized subseconds */ +#define EXIFTAG_FLASHPIXVERSION 40960 /* Supported Flashpix version */ +#define EXIFTAG_COLORSPACE 40961 /* Color space information */ +#define EXIFTAG_PIXELXDIMENSION 40962 /* Valid image width */ +#define EXIFTAG_PIXELYDIMENSION 40963 /* Valid image height */ +#define EXIFTAG_RELATEDSOUNDFILE 40964 /* Related audio file */ +#define EXIFTAG_FLASHENERGY 41483 /* Flash energy */ +#define EXIFTAG_SPATIALFREQUENCYRESPONSE 41484 /* Spatial frequency response */ +#define EXIFTAG_FOCALPLANEXRESOLUTION 41486 /* Focal plane X resolution */ +#define EXIFTAG_FOCALPLANEYRESOLUTION 41487 /* Focal plane Y resolution */ +#define EXIFTAG_FOCALPLANERESOLUTIONUNIT 41488 /* Focal plane resolution unit */ +#define EXIFTAG_SUBJECTLOCATION 41492 /* Subject location */ +#define EXIFTAG_EXPOSUREINDEX 41493 /* Exposure index */ +#define EXIFTAG_SENSINGMETHOD 41495 /* Sensing method */ +#define EXIFTAG_FILESOURCE 41728 /* File source */ +#define EXIFTAG_SCENETYPE 41729 /* Scene type */ +#define EXIFTAG_CFAPATTERN 41730 /* CFA pattern */ +#define EXIFTAG_CUSTOMRENDERED 41985 /* Custom image processing */ +#define EXIFTAG_EXPOSUREMODE 41986 /* Exposure mode */ +#define EXIFTAG_WHITEBALANCE 41987 /* White balance */ +#define EXIFTAG_DIGITALZOOMRATIO 41988 /* Digital zoom ratio */ +#define EXIFTAG_FOCALLENGTHIN35MMFILM 41989 /* Focal length in 35 mm film */ +#define EXIFTAG_SCENECAPTURETYPE 41990 /* Scene capture type */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_CONTRAST 41992 /* Contrast */ +#define EXIFTAG_SATURATION 41993 /* Saturation */ +#define EXIFTAG_SHARPNESS 41994 /* Sharpness */ +#define EXIFTAG_DEVICESETTINGDESCRIPTION 41995 /* Device settings description */ +#define EXIFTAG_SUBJECTDISTANCERANGE 41996 /* Subject distance range */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_IMAGEUNIQUEID 42016 /* Unique image ID */ + +#endif /* _TIFF_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/hgdriver/3rdparty/libtiff/include/tiffconf.h b/hgdriver/3rdparty/libtiff/include/tiffconf.h new file mode 100644 index 0000000..ad6588a --- /dev/null +++ b/hgdriver/3rdparty/libtiff/include/tiffconf.h @@ -0,0 +1,121 @@ +/* + Configuration defines for installed libtiff. + This file maintained for backward compatibility. Do not use definitions + from this file in your programs. +*/ + +#ifndef _TIFFCONF_ +#define _TIFFCONF_ + +/* Signed 16-bit type */ +#define TIFF_INT16_T signed short + +/* Signed 32-bit type */ +#define TIFF_INT32_T signed int + +/* Signed 64-bit type */ +#define TIFF_INT64_T signed long long + +/* Signed 8-bit type */ +#define TIFF_INT8_T signed char + +/* Unsigned 16-bit type */ +#define TIFF_UINT16_T unsigned short + +/* Unsigned 32-bit type */ +#define TIFF_UINT32_T unsigned int + +/* Unsigned 64-bit type */ +#define TIFF_UINT64_T unsigned long long + +/* Unsigned 8-bit type */ +#define TIFF_UINT8_T unsigned char + +/* Unsigned size type */ +#define TIFF_SIZE_T unsigned long long + +/* Signed size type */ +#define TIFF_SSIZE_T signed long long + +/* Pointer difference type */ +#define TIFF_PTRDIFF_T ptrdiff_t + +/* Compatibility stuff. */ + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Native cpu byte order: 1 if big-endian (Motorola) or 0 if little-endian + (Intel) */ +#define HOST_BIGENDIAN 0 + +/* Support CCITT Group 3 & 4 algorithms */ +#define CCITT_SUPPORT 1 + +/* Support JPEG compression (requires IJG JPEG library) */ +/* #undef JPEG_SUPPORT */ + +/* Support JBIG compression (requires JBIG-KIT library) */ +/* #undef JBIG_SUPPORT */ + +/* Support LogLuv high dynamic range encoding */ +#define LOGLUV_SUPPORT 1 + +/* Support LZW algorithm */ +#define LZW_SUPPORT 1 + +/* Support NeXT 2-bit RLE algorithm */ +#define NEXT_SUPPORT 1 + +/* Support Old JPEG compresson (read contrib/ojpeg/README first! Compilation + fails with unpatched IJG JPEG library) */ +/* #undef OJPEG_SUPPORT */ + +/* Support Macintosh PackBits algorithm */ +#define PACKBITS_SUPPORT 1 + +/* Support Pixar log-format algorithm (requires Zlib) */ +/* #undef PIXARLOG_SUPPORT */ + +/* Support ThunderScan 4-bit RLE algorithm */ +#define THUNDER_SUPPORT 1 + +/* Support Deflate compression */ +/* #undef ZIP_SUPPORT */ + +/* Support strip chopping (whether or not to convert single-strip uncompressed + images to mutiple strips of ~8Kb to reduce memory usage) */ +#define STRIPCHOP_DEFAULT 1 + +/* Enable SubIFD tag (330) support */ +#define SUBIFD_SUPPORT 1 + +/* Treat extra sample as alpha (default enabled). The RGBA interface will + treat a fourth sample with no EXTRASAMPLE_ value as being ASSOCALPHA. Many + packages produce RGBA files but don't mark the alpha properly. */ +#define DEFAULT_EXTRASAMPLE_AS_ALPHA 1 + +/* Pick up YCbCr subsampling info from the JPEG data stream to support files + lacking the tag (default enabled). */ +#define CHECK_JPEG_YCBCR_SUBSAMPLING 1 + +/* Support MS MDI magic number files as TIFF */ +#define MDI_SUPPORT 1 + +/* + * Feature support definitions. + * XXX: These macros are obsoleted. Don't use them in your apps! + * Macros stays here for backward compatibility and should be always defined. + */ +#define COLORIMETRY_SUPPORT +#define YCBCR_SUPPORT +#define CMYK_SUPPORT +#define ICC_SUPPORT +#define PHOTOSHOP_SUPPORT +#define IPTC_SUPPORT + +#endif /* _TIFFCONF_ */ diff --git a/hgdriver/3rdparty/libtiff/include/tiffio.h b/hgdriver/3rdparty/libtiff/include/tiffio.h new file mode 100644 index 0000000..198481d --- /dev/null +++ b/hgdriver/3rdparty/libtiff/include/tiffio.h @@ -0,0 +1,568 @@ +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFIO_ +#define _TIFFIO_ + +/* + * TIFF I/O Library Definitions. + */ +#include "tiff.h" +#include "tiffvers.h" + +/* + * TIFF is defined as an incomplete type to hide the + * library's internal data structures from clients. + */ +typedef struct tiff TIFF; + +/* + * The following typedefs define the intrinsic size of + * data types used in the *exported* interfaces. These + * definitions depend on the proper definition of types + * in tiff.h. Note also that the varargs interface used + * to pass tag types and values uses the types defined in + * tiff.h directly. + * + * NB: ttag_t is unsigned int and not unsigned short because + * ANSI C requires that the type before the ellipsis be a + * promoted type (i.e. one of int, unsigned int, pointer, + * or double) and because we defined pseudo-tags that are + * outside the range of legal Aldus-assigned tags. + * NB: tsize_t is signed and not unsigned because some functions + * return -1. + * NB: toff_t is not off_t for many reasons; TIFFs max out at + * 32-bit file offsets, and BigTIFF maxes out at 64-bit + * offsets being the most important, and to ensure use of + * a consistently unsigned type across architectures. + * Prior to libtiff 4.0, this was an unsigned 32 bit type. + */ +/* + * this is the machine addressing size type, only it's signed, so make it + * int32 on 32bit machines, int64 on 64bit machines + */ +typedef TIFF_SSIZE_T tmsize_t; +typedef uint64 toff_t; /* file offset */ +/* the following are deprecated and should be replaced by their defining + counterparts */ +typedef uint32 ttag_t; /* directory tag */ +typedef uint16 tdir_t; /* directory index */ +typedef uint16 tsample_t; /* sample number */ +typedef uint32 tstrile_t; /* strip or tile number */ +typedef tstrile_t tstrip_t; /* strip number */ +typedef tstrile_t ttile_t; /* tile number */ +typedef tmsize_t tsize_t; /* i/o size in bytes */ +typedef void* tdata_t; /* image data ref */ + +#if !defined(__WIN32__) && (defined(_WIN32) || defined(WIN32)) +#define __WIN32__ +#endif + +/* + * On windows you should define USE_WIN32_FILEIO if you are using tif_win32.c + * or AVOID_WIN32_FILEIO if you are using something else (like tif_unix.c). + * + * By default tif_unix.c is assumed. + */ + +#if defined(_WINDOWS) || defined(__WIN32__) || defined(_Windows) +# if !defined(__CYGWIN) && !defined(AVOID_WIN32_FILEIO) && !defined(USE_WIN32_FILEIO) +# define AVOID_WIN32_FILEIO +# endif +#endif + +#if defined(USE_WIN32_FILEIO) +# define VC_EXTRALEAN +# include +# ifdef __WIN32__ +DECLARE_HANDLE(thandle_t); /* Win32 file handle */ +# else +typedef HFILE thandle_t; /* client data handle */ +# endif /* __WIN32__ */ +#else +typedef void* thandle_t; /* client data handle */ +#endif /* USE_WIN32_FILEIO */ + +/* + * Flags to pass to TIFFPrintDirectory to control + * printing of data structures that are potentially + * very large. Bit-or these flags to enable printing + * multiple items. + */ +#define TIFFPRINT_NONE 0x0 /* no extra info */ +#define TIFFPRINT_STRIPS 0x1 /* strips/tiles info */ +#define TIFFPRINT_CURVES 0x2 /* color/gray response curves */ +#define TIFFPRINT_COLORMAP 0x4 /* colormap */ +#define TIFFPRINT_JPEGQTABLES 0x100 /* JPEG Q matrices */ +#define TIFFPRINT_JPEGACTABLES 0x200 /* JPEG AC tables */ +#define TIFFPRINT_JPEGDCTABLES 0x200 /* JPEG DC tables */ + +/* + * Colour conversion stuff + */ + +/* reference white */ +#define D65_X0 (95.0470F) +#define D65_Y0 (100.0F) +#define D65_Z0 (108.8827F) + +#define D50_X0 (96.4250F) +#define D50_Y0 (100.0F) +#define D50_Z0 (82.4680F) + +/* Structure for holding information about a display device. */ + +typedef unsigned char TIFFRGBValue; /* 8-bit samples */ + +typedef struct { + float d_mat[3][3]; /* XYZ -> luminance matrix */ + float d_YCR; /* Light o/p for reference white */ + float d_YCG; + float d_YCB; + uint32 d_Vrwr; /* Pixel values for ref. white */ + uint32 d_Vrwg; + uint32 d_Vrwb; + float d_Y0R; /* Residual light for black pixel */ + float d_Y0G; + float d_Y0B; + float d_gammaR; /* Gamma values for the three guns */ + float d_gammaG; + float d_gammaB; +} TIFFDisplay; + +typedef struct { /* YCbCr->RGB support */ + TIFFRGBValue* clamptab; /* range clamping table */ + int* Cr_r_tab; + int* Cb_b_tab; + int32* Cr_g_tab; + int32* Cb_g_tab; + int32* Y_tab; +} TIFFYCbCrToRGB; + +typedef struct { /* CIE Lab 1976->RGB support */ + int range; /* Size of conversion table */ +#define CIELABTORGB_TABLE_RANGE 1500 + float rstep, gstep, bstep; + float X0, Y0, Z0; /* Reference white point */ + TIFFDisplay display; + float Yr2r[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yr to r */ + float Yg2g[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yg to g */ + float Yb2b[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yb to b */ +} TIFFCIELabToRGB; + +/* + * RGBA-style image support. + */ +typedef struct _TIFFRGBAImage TIFFRGBAImage; +/* + * The image reading and conversion routines invoke + * ``put routines'' to copy/image/whatever tiles of + * raw image data. A default set of routines are + * provided to convert/copy raw image data to 8-bit + * packed ABGR format rasters. Applications can supply + * alternate routines that unpack the data into a + * different format or, for example, unpack the data + * and draw the unpacked raster on the display. + */ +typedef void (*tileContigRoutine) + (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32, + unsigned char*); +typedef void (*tileSeparateRoutine) + (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32, + unsigned char*, unsigned char*, unsigned char*, unsigned char*); +/* + * RGBA-reader state. + */ +struct _TIFFRGBAImage { + TIFF* tif; /* image handle */ + int stoponerr; /* stop on read error */ + int isContig; /* data is packed/separate */ + int alpha; /* type of alpha data present */ + uint32 width; /* image width */ + uint32 height; /* image height */ + uint16 bitspersample; /* image bits/sample */ + uint16 samplesperpixel; /* image samples/pixel */ + uint16 orientation; /* image orientation */ + uint16 req_orientation; /* requested orientation */ + uint16 photometric; /* image photometric interp */ + uint16* redcmap; /* colormap palette */ + uint16* greencmap; + uint16* bluecmap; + /* get image data routine */ + int (*get)(TIFFRGBAImage*, uint32*, uint32, uint32); + /* put decoded strip/tile */ + union { + void (*any)(TIFFRGBAImage*); + tileContigRoutine contig; + tileSeparateRoutine separate; + } put; + TIFFRGBValue* Map; /* sample mapping array */ + uint32** BWmap; /* black&white map */ + uint32** PALmap; /* palette image map */ + TIFFYCbCrToRGB* ycbcr; /* YCbCr conversion state */ + TIFFCIELabToRGB* cielab; /* CIE L*a*b conversion state */ + + uint8* UaToAa; /* Unassociated alpha to associated alpha conversion LUT */ + uint8* Bitdepth16To8; /* LUT for conversion from 16bit to 8bit values */ + + int row_offset; + int col_offset; +}; + +/* + * Macros for extracting components from the + * packed ABGR form returned by TIFFReadRGBAImage. + */ +#define TIFFGetR(abgr) ((abgr) & 0xff) +#define TIFFGetG(abgr) (((abgr) >> 8) & 0xff) +#define TIFFGetB(abgr) (((abgr) >> 16) & 0xff) +#define TIFFGetA(abgr) (((abgr) >> 24) & 0xff) + +/* + * A CODEC is a software package that implements decoding, + * encoding, or decoding+encoding of a compression algorithm. + * The library provides a collection of builtin codecs. + * More codecs may be registered through calls to the library + * and/or the builtin implementations may be overridden. + */ +typedef int (*TIFFInitMethod)(TIFF*, int); +typedef struct { + char* name; + uint16 scheme; + TIFFInitMethod init; +} TIFFCodec; + +#include +#include + +/* share internal LogLuv conversion routines? */ +#ifndef LOGLUV_PUBLIC +#define LOGLUV_PUBLIC 1 +#endif + +#if !defined(__GNUC__) && !defined(__attribute__) +# define __attribute__(x) /*nothing*/ +#endif + +#if defined(c_plusplus) || defined(__cplusplus) +extern "C" { +#endif +typedef void (*TIFFErrorHandler)(const char*, const char*, va_list); +typedef void (*TIFFErrorHandlerExt)(thandle_t, const char*, const char*, va_list); +typedef tmsize_t (*TIFFReadWriteProc)(thandle_t, void*, tmsize_t); +typedef toff_t (*TIFFSeekProc)(thandle_t, toff_t, int); +typedef int (*TIFFCloseProc)(thandle_t); +typedef toff_t (*TIFFSizeProc)(thandle_t); +typedef int (*TIFFMapFileProc)(thandle_t, void** base, toff_t* size); +typedef void (*TIFFUnmapFileProc)(thandle_t, void* base, toff_t size); +typedef void (*TIFFExtendProc)(TIFF*); + +extern const char* TIFFGetVersion(void); + +extern const TIFFCodec* TIFFFindCODEC(uint16); +extern TIFFCodec* TIFFRegisterCODEC(uint16, const char*, TIFFInitMethod); +extern void TIFFUnRegisterCODEC(TIFFCodec*); +extern int TIFFIsCODECConfigured(uint16); +extern TIFFCodec* TIFFGetConfiguredCODECs(void); + +/* + * Auxiliary functions. + */ + +extern void* _TIFFmalloc(tmsize_t s); +extern void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz); +extern void* _TIFFrealloc(void* p, tmsize_t s); +extern void _TIFFmemset(void* p, int v, tmsize_t c); +extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c); +extern int _TIFFmemcmp(const void* p1, const void* p2, tmsize_t c); +extern void _TIFFfree(void* p); + +/* +** Stuff, related to tag handling and creating custom tags. +*/ +extern int TIFFGetTagListCount( TIFF * ); +extern uint32 TIFFGetTagListEntry( TIFF *, int tag_index ); + +#define TIFF_ANY TIFF_NOTYPE /* for field descriptor searching */ +#define TIFF_VARIABLE -1 /* marker for variable length tags */ +#define TIFF_SPP -2 /* marker for SamplesPerPixel tags */ +#define TIFF_VARIABLE2 -3 /* marker for uint32 var-length tags */ + +#define FIELD_CUSTOM 65 + +typedef struct _TIFFField TIFFField; +typedef struct _TIFFFieldArray TIFFFieldArray; + +extern const TIFFField* TIFFFindField(TIFF *, uint32, TIFFDataType); +extern const TIFFField* TIFFFieldWithTag(TIFF*, uint32); +extern const TIFFField* TIFFFieldWithName(TIFF*, const char *); + +extern uint32 TIFFFieldTag(const TIFFField*); +extern const char* TIFFFieldName(const TIFFField*); +extern TIFFDataType TIFFFieldDataType(const TIFFField*); +extern int TIFFFieldPassCount(const TIFFField*); +extern int TIFFFieldReadCount(const TIFFField*); +extern int TIFFFieldWriteCount(const TIFFField*); + +typedef int (*TIFFVSetMethod)(TIFF*, uint32, va_list); +typedef int (*TIFFVGetMethod)(TIFF*, uint32, va_list); +typedef void (*TIFFPrintMethod)(TIFF*, FILE*, long); + +typedef struct { + TIFFVSetMethod vsetfield; /* tag set routine */ + TIFFVGetMethod vgetfield; /* tag get routine */ + TIFFPrintMethod printdir; /* directory print routine */ +} TIFFTagMethods; + +extern TIFFTagMethods *TIFFAccessTagMethods(TIFF *); +extern void *TIFFGetClientInfo(TIFF *, const char *); +extern void TIFFSetClientInfo(TIFF *, void *, const char *); + +extern void TIFFCleanup(TIFF* tif); +extern void TIFFClose(TIFF* tif); +extern int TIFFFlush(TIFF* tif); +extern int TIFFFlushData(TIFF* tif); +extern int TIFFGetField(TIFF* tif, uint32 tag, ...); +extern int TIFFVGetField(TIFF* tif, uint32 tag, va_list ap); +extern int TIFFGetFieldDefaulted(TIFF* tif, uint32 tag, ...); +extern int TIFFVGetFieldDefaulted(TIFF* tif, uint32 tag, va_list ap); +extern int TIFFReadDirectory(TIFF* tif); +extern int TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, const TIFFFieldArray* infoarray); +extern int TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff); +extern uint64 TIFFScanlineSize64(TIFF* tif); +extern tmsize_t TIFFScanlineSize(TIFF* tif); +extern uint64 TIFFRasterScanlineSize64(TIFF* tif); +extern tmsize_t TIFFRasterScanlineSize(TIFF* tif); +extern uint64 TIFFStripSize64(TIFF* tif); +extern tmsize_t TIFFStripSize(TIFF* tif); +extern uint64 TIFFRawStripSize64(TIFF* tif, uint32 strip); +extern tmsize_t TIFFRawStripSize(TIFF* tif, uint32 strip); +extern uint64 TIFFVStripSize64(TIFF* tif, uint32 nrows); +extern tmsize_t TIFFVStripSize(TIFF* tif, uint32 nrows); +extern uint64 TIFFTileRowSize64(TIFF* tif); +extern tmsize_t TIFFTileRowSize(TIFF* tif); +extern uint64 TIFFTileSize64(TIFF* tif); +extern tmsize_t TIFFTileSize(TIFF* tif); +extern uint64 TIFFVTileSize64(TIFF* tif, uint32 nrows); +extern tmsize_t TIFFVTileSize(TIFF* tif, uint32 nrows); +extern uint32 TIFFDefaultStripSize(TIFF* tif, uint32 request); +extern void TIFFDefaultTileSize(TIFF*, uint32*, uint32*); +extern int TIFFFileno(TIFF*); +extern int TIFFSetFileno(TIFF*, int); +extern thandle_t TIFFClientdata(TIFF*); +extern thandle_t TIFFSetClientdata(TIFF*, thandle_t); +extern int TIFFGetMode(TIFF*); +extern int TIFFSetMode(TIFF*, int); +extern int TIFFIsTiled(TIFF*); +extern int TIFFIsByteSwapped(TIFF*); +extern int TIFFIsUpSampled(TIFF*); +extern int TIFFIsMSB2LSB(TIFF*); +extern int TIFFIsBigEndian(TIFF*); +extern TIFFReadWriteProc TIFFGetReadProc(TIFF*); +extern TIFFReadWriteProc TIFFGetWriteProc(TIFF*); +extern TIFFSeekProc TIFFGetSeekProc(TIFF*); +extern TIFFCloseProc TIFFGetCloseProc(TIFF*); +extern TIFFSizeProc TIFFGetSizeProc(TIFF*); +extern TIFFMapFileProc TIFFGetMapFileProc(TIFF*); +extern TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF*); +extern uint32 TIFFCurrentRow(TIFF*); +extern uint16 TIFFCurrentDirectory(TIFF*); +extern uint16 TIFFNumberOfDirectories(TIFF*); +extern uint64 TIFFCurrentDirOffset(TIFF*); +extern uint32 TIFFCurrentStrip(TIFF*); +extern uint32 TIFFCurrentTile(TIFF* tif); +extern int TIFFReadBufferSetup(TIFF* tif, void* bp, tmsize_t size); +extern int TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size); +extern int TIFFSetupStrips(TIFF *); +extern int TIFFWriteCheck(TIFF*, int, const char *); +extern void TIFFFreeDirectory(TIFF*); +extern int TIFFCreateDirectory(TIFF*); +extern int TIFFCreateCustomDirectory(TIFF*,const TIFFFieldArray*); +extern int TIFFCreateEXIFDirectory(TIFF*); +extern int TIFFLastDirectory(TIFF*); +extern int TIFFSetDirectory(TIFF*, uint16); +extern int TIFFSetSubDirectory(TIFF*, uint64); +extern int TIFFUnlinkDirectory(TIFF*, uint16); +extern int TIFFSetField(TIFF*, uint32, ...); +extern int TIFFVSetField(TIFF*, uint32, va_list); +extern int TIFFUnsetField(TIFF*, uint32); +extern int TIFFWriteDirectory(TIFF *); +extern int TIFFWriteCustomDirectory(TIFF *, uint64 *); +extern int TIFFCheckpointDirectory(TIFF *); +extern int TIFFRewriteDirectory(TIFF *); +extern int TIFFDeferStrileArrayWriting(TIFF *); +extern int TIFFForceStrileArrayWriting(TIFF* ); + +#if defined(c_plusplus) || defined(__cplusplus) +extern void TIFFPrintDirectory(TIFF*, FILE*, long = 0); +extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0); +extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0); +extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int = 0); +extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, + int = ORIENTATION_BOTLEFT, int = 0); +#else +extern void TIFFPrintDirectory(TIFF*, FILE*, long); +extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample); +extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample); +extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int); +extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, int, int); +#endif + +extern int TIFFReadRGBAStrip(TIFF*, uint32, uint32 * ); +extern int TIFFReadRGBATile(TIFF*, uint32, uint32, uint32 * ); +extern int TIFFReadRGBAStripExt(TIFF*, uint32, uint32 *, int stop_on_error ); +extern int TIFFReadRGBATileExt(TIFF*, uint32, uint32, uint32 *, int stop_on_error ); +extern int TIFFRGBAImageOK(TIFF*, char [1024]); +extern int TIFFRGBAImageBegin(TIFFRGBAImage*, TIFF*, int, char [1024]); +extern int TIFFRGBAImageGet(TIFFRGBAImage*, uint32*, uint32, uint32); +extern void TIFFRGBAImageEnd(TIFFRGBAImage*); +extern TIFF* TIFFOpen(const char*, const char*); +# ifdef __WIN32__ +extern TIFF* TIFFOpenW(const wchar_t*, const char*); +# endif /* __WIN32__ */ +extern TIFF* TIFFFdOpen(int, const char*, const char*); +extern TIFF* TIFFClientOpen(const char*, const char*, + thandle_t, + TIFFReadWriteProc, TIFFReadWriteProc, + TIFFSeekProc, TIFFCloseProc, + TIFFSizeProc, + TIFFMapFileProc, TIFFUnmapFileProc); +extern const char* TIFFFileName(TIFF*); +extern const char* TIFFSetFileName(TIFF*, const char *); +extern void TIFFError(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3))); +extern void TIFFErrorExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4))); +extern void TIFFWarning(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3))); +extern void TIFFWarningExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4))); +extern TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler); +extern TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt); +extern TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler); +extern TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt); +extern TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc); +extern uint32 TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s); +extern int TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s); +extern uint32 TIFFNumberOfTiles(TIFF*); +extern tmsize_t TIFFReadTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s); +extern tmsize_t TIFFWriteTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s); +extern uint32 TIFFComputeStrip(TIFF*, uint32, uint16); +extern uint32 TIFFNumberOfStrips(TIFF*); +extern tmsize_t TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size); +extern tmsize_t TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size); +extern tmsize_t TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size); +extern tmsize_t TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size); +extern int TIFFReadFromUserBuffer(TIFF* tif, uint32 strile, + void* inbuf, tmsize_t insize, + void* outbuf, tmsize_t outsize); +extern tmsize_t TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteRawTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc); +extern int TIFFDataWidth(TIFFDataType); /* table of tag datatype widths */ +extern void TIFFSetWriteOffset(TIFF* tif, toff_t off); +extern void TIFFSwabShort(uint16*); +extern void TIFFSwabLong(uint32*); +extern void TIFFSwabLong8(uint64*); +extern void TIFFSwabFloat(float*); +extern void TIFFSwabDouble(double*); +extern void TIFFSwabArrayOfShort(uint16* wp, tmsize_t n); +extern void TIFFSwabArrayOfTriples(uint8* tp, tmsize_t n); +extern void TIFFSwabArrayOfLong(uint32* lp, tmsize_t n); +extern void TIFFSwabArrayOfLong8(uint64* lp, tmsize_t n); +extern void TIFFSwabArrayOfFloat(float* fp, tmsize_t n); +extern void TIFFSwabArrayOfDouble(double* dp, tmsize_t n); +extern void TIFFReverseBits(uint8* cp, tmsize_t n); +extern const unsigned char* TIFFGetBitRevTable(int); + +extern uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile); +extern uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile); +extern uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr); +extern uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr); + +#ifdef LOGLUV_PUBLIC +#define U_NEU 0.210526316 +#define V_NEU 0.473684211 +#define UVSCALE 410. +extern double LogL16toY(int); +extern double LogL10toY(int); +extern void XYZtoRGB24(float*, uint8*); +extern int uv_decode(double*, double*, int); +extern void LogLuv24toXYZ(uint32, float*); +extern void LogLuv32toXYZ(uint32, float*); +#if defined(c_plusplus) || defined(__cplusplus) +extern int LogL16fromY(double, int = SGILOGENCODE_NODITHER); +extern int LogL10fromY(double, int = SGILOGENCODE_NODITHER); +extern int uv_encode(double, double, int = SGILOGENCODE_NODITHER); +extern uint32 LogLuv24fromXYZ(float*, int = SGILOGENCODE_NODITHER); +extern uint32 LogLuv32fromXYZ(float*, int = SGILOGENCODE_NODITHER); +#else +extern int LogL16fromY(double, int); +extern int LogL10fromY(double, int); +extern int uv_encode(double, double, int); +extern uint32 LogLuv24fromXYZ(float*, int); +extern uint32 LogLuv32fromXYZ(float*, int); +#endif +#endif /* LOGLUV_PUBLIC */ + +extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB*, const TIFFDisplay *, float*); +extern void TIFFCIELabToXYZ(TIFFCIELabToRGB *, uint32, int32, int32, + float *, float *, float *); +extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float, + uint32 *, uint32 *, uint32 *); + +extern int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB*, float*, float*); +extern void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *, uint32, int32, int32, + uint32 *, uint32 *, uint32 *); + +/**************************************************************************** + * O B S O L E T E D I N T E R F A C E S + * + * Don't use this stuff in your applications, it may be removed in the future + * libtiff versions. + ****************************************************************************/ +typedef struct { + ttag_t field_tag; /* field's tag */ + short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */ + short field_writecount; /* write count/TIFF_VARIABLE */ + TIFFDataType field_type; /* type of associated data */ + unsigned short field_bit; /* bit in fieldsset bit vector */ + unsigned char field_oktochange; /* if true, can change while writing */ + unsigned char field_passcount; /* if true, pass dir count on set */ + char *field_name; /* ASCII name */ +} TIFFFieldInfo; + +extern int TIFFMergeFieldInfo(TIFF*, const TIFFFieldInfo[], uint32); + +#if defined(c_plusplus) || defined(__cplusplus) +} +#endif + +#endif /* _TIFFIO_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/hgdriver/3rdparty/libtiff/include/tiffvers.h b/hgdriver/3rdparty/libtiff/include/tiffvers.h new file mode 100644 index 0000000..aa3f613 --- /dev/null +++ b/hgdriver/3rdparty/libtiff/include/tiffvers.h @@ -0,0 +1,9 @@ +#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.1.0\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc." +/* + * This define can be used in code that requires + * compilation-related definitions specific to a + * version or versions of the library. Runtime + * version checking should be done based on the + * string returned by TIFFGetVersion. + */ +#define TIFFLIB_VERSION 20191103 diff --git a/hgdriver/3rdparty/log4cplus/include/Makefile.am b/hgdriver/3rdparty/log4cplus/include/Makefile.am new file mode 100644 index 0000000..0596b56 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/Makefile.am @@ -0,0 +1,81 @@ +## Generated by Autogen from Makefile.am.tpl +log4cplusincdir = $(includedir) + +nobase_log4cplusinc_HEADERS = \ + log4cplus/appender.h \ + log4cplus/asyncappender.h \ + log4cplus/boost/deviceappender.hxx \ + log4cplus/callbackappender.h \ + log4cplus/clfsappender.h \ + log4cplus/clogger.h \ + log4cplus/config.hxx \ + log4cplus/config/defines.hxx \ + log4cplus/config/macosx.h \ + log4cplus/config/win32.h \ + log4cplus/config/windowsh-inc-full.h \ + log4cplus/config/windowsh-inc.h \ + log4cplus/configurator.h \ + log4cplus/consoleappender.h \ + log4cplus/fileappender.h \ + log4cplus/fstreams.h \ + log4cplus/helpers/appenderattachableimpl.h \ + log4cplus/helpers/connectorthread.h \ + log4cplus/helpers/fileinfo.h \ + log4cplus/helpers/lockfile.h \ + log4cplus/helpers/loglog.h \ + log4cplus/helpers/pointer.h \ + log4cplus/helpers/property.h \ + log4cplus/helpers/queue.h \ + log4cplus/helpers/snprintf.h \ + log4cplus/helpers/socket.h \ + log4cplus/helpers/socketbuffer.h \ + log4cplus/helpers/stringhelper.h \ + log4cplus/helpers/thread-config.h \ + log4cplus/helpers/timehelper.h \ + log4cplus/hierarchy.h \ + log4cplus/hierarchylocker.h \ + log4cplus/initializer.h \ + log4cplus/internal/customloglevelmanager.h \ + log4cplus/internal/cygwin-win32.h \ + log4cplus/internal/env.h \ + log4cplus/internal/internal.h \ + log4cplus/internal/socket.h \ + log4cplus/layout.h \ + log4cplus/log4cplus.h \ + log4cplus/log4judpappender.h \ + log4cplus/logger.h \ + log4cplus/loggingmacros.h \ + log4cplus/loglevel.h \ + log4cplus/mdc.h \ + log4cplus/msttsappender.h \ + log4cplus/ndc.h \ + log4cplus/nteventlogappender.h \ + log4cplus/nullappender.h \ + log4cplus/qt4debugappender.h \ + log4cplus/qt5debugappender.h \ + log4cplus/socketappender.h \ + log4cplus/spi/appenderattachable.h \ + log4cplus/spi/factory.h \ + log4cplus/spi/filter.h \ + log4cplus/spi/loggerfactory.h \ + log4cplus/spi/loggerimpl.h \ + log4cplus/spi/loggingevent.h \ + log4cplus/spi/objectregistry.h \ + log4cplus/spi/rootlogger.h \ + log4cplus/streams.h \ + log4cplus/syslogappender.h \ + log4cplus/tchar.h \ + log4cplus/thread/impl/syncprims-cxx11.h \ + log4cplus/thread/impl/syncprims-impl.h \ + log4cplus/thread/impl/syncprims-pmsm.h \ + log4cplus/thread/impl/threads-impl.h \ + log4cplus/thread/impl/tls.h \ + log4cplus/thread/syncprims-pub-impl.h \ + log4cplus/thread/syncprims.h \ + log4cplus/thread/threads.h \ + log4cplus/tracelogger.h \ + log4cplus/tstring.h \ + log4cplus/version.h \ + log4cplus/win32consoleappender.h \ + log4cplus/win32debugappender.h + diff --git a/hgdriver/3rdparty/log4cplus/include/Makefile.am.def b/hgdriver/3rdparty/log4cplus/include/Makefile.am.def new file mode 100644 index 0000000..0676555 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/Makefile.am.def @@ -0,0 +1,3 @@ +AutoGen definitions Makefile.am.tpl; + +src-dirs = { name = log4cplus; }; diff --git a/hgdriver/3rdparty/log4cplus/include/Makefile.am.tpl b/hgdriver/3rdparty/log4cplus/include/Makefile.am.tpl new file mode 100644 index 0000000..bcfbc91 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/Makefile.am.tpl @@ -0,0 +1,25 @@ +[= AutoGen5 template -*- Mode: scheme -*- +am +=][= +(use-modules (ice-9 ftw)) +=]## Generated by Autogen from [= (tpl-file) =] +log4cplusincdir = $(includedir) + +nobase_log4cplusinc_HEADERS = \ +[= FOR src-dirs =][= +(let ((files (list))) + (define (emit-am-file-ftw-cb filename statinfo flag) + (begin + (if (or (string-suffix-ci? ".h" filename) + (string-suffix-ci? ".hxx" filename)) + (set! files (append! files (list filename)))) + #t)) + (begin + (ftw (get "name") emit-am-file-ftw-cb) + ;; Add the generated header as it will not be found by file search. + (append! files (list "log4cplus/config/defines.hxx")) + (set! files (sort! files string-ci&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/m4/ax_c_ifdef.m4 \ + $(top_srcdir)/m4/ax_append_flag.m4 \ + $(top_srcdir)/m4/ax_cflags_warn_all.m4 \ + $(top_srcdir)/m4/ax_type_socklen_t.m4 \ + $(top_srcdir)/m4/ax_compiler_vendor.m4 \ + $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \ + $(top_srcdir)/m4/ax_cflags_sun_option.m4 \ + $(top_srcdir)/m4/ax_cflags_aix_option.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/ax_declspec.m4 \ + $(top_srcdir)/m4/ax_tls_support.m4 \ + $(top_srcdir)/m4/ax_macro_function.m4 \ + $(top_srcdir)/m4/ax_gethostbyname_r.m4 \ + $(top_srcdir)/m4/ax_getaddrinfo.m4 \ + $(top_srcdir)/m4/ax_log4cplus_wrappers.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/ax_pkg_swig.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/ax_swig_multi_module_support.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_gcc_var_attribute.m4 \ + $(top_srcdir)/tests/configure.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(nobase_log4cplusinc_HEADERS) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/log4cplus/config.h \ + $(top_builddir)/include/log4cplus/config/defines.hxx +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(log4cplusincdir)" +HEADERS = $(nobase_log4cplusinc_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +AX_SWIG_PYTHON_CPPFLAGS = @AX_SWIG_PYTHON_CPPFLAGS@ +AX_SWIG_PYTHON_OPT = @AX_SWIG_PYTHON_OPT@ +BUILD_WITH_WCHAR_T_SUPPORT = @BUILD_WITH_WCHAR_T_SUPPORT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_THREADS = @ENABLE_THREADS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX17 = @HAVE_CXX17@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_AIX_XLC_LDFLAGS = @LOG4CPLUS_AIX_XLC_LDFLAGS@ +LOG4CPLUS_LTO_CXXFLAGS = @LOG4CPLUS_LTO_CXXFLAGS@ +LOG4CPLUS_LTO_LDFLAGS = @LOG4CPLUS_LTO_LDFLAGS@ +LOG4CPLUS_NDEBUG = @LOG4CPLUS_NDEBUG@ +LOG4CPLUS_PROFILING_CXXFLAGS = @LOG4CPLUS_PROFILING_CXXFLAGS@ +LOG4CPLUS_PROFILING_LDFLAGS = @LOG4CPLUS_PROFILING_LDFLAGS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_RELEASE = @LT_RELEASE@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LT_VERSION = @LT_VERSION@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PTHREAD_CXXFLAGS = @PTHREAD_CXXFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +QT5_CFLAGS = @QT5_CFLAGS@ +QT5_LIBS = @QT5_LIBS@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SWIG = @SWIG@ +SWIG_FLAGS = @SWIG_FLAGS@ +SWIG_LIB = @SWIG_LIB@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +log4cplusincdir = $(includedir) +nobase_log4cplusinc_HEADERS = \ + log4cplus/appender.h \ + log4cplus/asyncappender.h \ + log4cplus/boost/deviceappender.hxx \ + log4cplus/callbackappender.h \ + log4cplus/clfsappender.h \ + log4cplus/clogger.h \ + log4cplus/config.hxx \ + log4cplus/config/defines.hxx \ + log4cplus/config/macosx.h \ + log4cplus/config/win32.h \ + log4cplus/config/windowsh-inc-full.h \ + log4cplus/config/windowsh-inc.h \ + log4cplus/configurator.h \ + log4cplus/consoleappender.h \ + log4cplus/fileappender.h \ + log4cplus/fstreams.h \ + log4cplus/helpers/appenderattachableimpl.h \ + log4cplus/helpers/connectorthread.h \ + log4cplus/helpers/fileinfo.h \ + log4cplus/helpers/lockfile.h \ + log4cplus/helpers/loglog.h \ + log4cplus/helpers/pointer.h \ + log4cplus/helpers/property.h \ + log4cplus/helpers/queue.h \ + log4cplus/helpers/snprintf.h \ + log4cplus/helpers/socket.h \ + log4cplus/helpers/socketbuffer.h \ + log4cplus/helpers/stringhelper.h \ + log4cplus/helpers/thread-config.h \ + log4cplus/helpers/timehelper.h \ + log4cplus/hierarchy.h \ + log4cplus/hierarchylocker.h \ + log4cplus/initializer.h \ + log4cplus/internal/customloglevelmanager.h \ + log4cplus/internal/cygwin-win32.h \ + log4cplus/internal/env.h \ + log4cplus/internal/internal.h \ + log4cplus/internal/socket.h \ + log4cplus/layout.h \ + log4cplus/log4cplus.h \ + log4cplus/log4judpappender.h \ + log4cplus/logger.h \ + log4cplus/loggingmacros.h \ + log4cplus/loglevel.h \ + log4cplus/mdc.h \ + log4cplus/msttsappender.h \ + log4cplus/ndc.h \ + log4cplus/nteventlogappender.h \ + log4cplus/nullappender.h \ + log4cplus/qt4debugappender.h \ + log4cplus/qt5debugappender.h \ + log4cplus/socketappender.h \ + log4cplus/spi/appenderattachable.h \ + log4cplus/spi/factory.h \ + log4cplus/spi/filter.h \ + log4cplus/spi/loggerfactory.h \ + log4cplus/spi/loggerimpl.h \ + log4cplus/spi/loggingevent.h \ + log4cplus/spi/objectregistry.h \ + log4cplus/spi/rootlogger.h \ + log4cplus/streams.h \ + log4cplus/syslogappender.h \ + log4cplus/tchar.h \ + log4cplus/thread/impl/syncprims-cxx11.h \ + log4cplus/thread/impl/syncprims-impl.h \ + log4cplus/thread/impl/syncprims-pmsm.h \ + log4cplus/thread/impl/threads-impl.h \ + log4cplus/thread/impl/tls.h \ + log4cplus/thread/syncprims-pub-impl.h \ + log4cplus/thread/syncprims.h \ + log4cplus/thread/threads.h \ + log4cplus/tracelogger.h \ + log4cplus/tstring.h \ + log4cplus/version.h \ + log4cplus/win32consoleappender.h \ + log4cplus/win32debugappender.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_log4cplusincHEADERS: $(nobase_log4cplusinc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_log4cplusinc_HEADERS)'; test -n "$(log4cplusincdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(log4cplusincdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(log4cplusincdir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(log4cplusincdir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(log4cplusincdir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(log4cplusincdir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(log4cplusincdir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_log4cplusincHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_log4cplusinc_HEADERS)'; test -n "$(log4cplusincdir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(log4cplusincdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(log4cplusincdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_log4cplusincHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_log4cplusincHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags dvi dvi-am \ + html html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-nobase_log4cplusincHEADERS \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-nobase_log4cplusincHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/appender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/appender.h new file mode 100644 index 0000000..972359a --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/appender.h @@ -0,0 +1,340 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: appender.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_APPENDER_HEADER_ +#define LOG4CPLUS_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace log4cplus { + + + namespace helpers + { + + class Properties; + + } + + + /** + * This class is used to "handle" errors encountered in an {@link + * log4cplus::Appender}. + */ + class LOG4CPLUS_EXPORT ErrorHandler + { + public: + ErrorHandler (); + virtual ~ErrorHandler() = 0; + virtual void error(const log4cplus::tstring& err) = 0; + virtual void reset() = 0; + }; + + + class LOG4CPLUS_EXPORT OnlyOnceErrorHandler + : public ErrorHandler + { + public: + // Ctor + OnlyOnceErrorHandler(); + virtual ~OnlyOnceErrorHandler (); + virtual void error(const log4cplus::tstring& err); + virtual void reset(); + + private: + bool firstTime; + }; + + + /** + * Extend this class for implementing your own strategies for printing log + * statements. + * + *

Properties

+ *
+ * + *
layout
+ *
This property specifies message layout used by + * Appender. + * \sa Layout + *
+ * + *
filters
+ *
This property specifies possibly multiple filters used by + * Appender. Each of multple filters and its properties is under a + * numbered subkey of filters key. E.g.: + * filters.1=log4cplus::spi::LogLevelMatchFilter. Filter + * subkey numbers must be consecutive.
+ * + *
Threshold
+ *
This property specifies log level threshold. Events with + * lower log level than the threshold will not be logged by + * appender.
+ * + *
UseLockFile
+ *
Set this property to true if you want your output + * through this appender to be synchronized between multiple + * processes. When this property is set to true then log4cplus + * uses OS specific facilities (e.g., lockf()) to + * provide inter-process locking. With the exception of + * FileAppender and its derived classes, it is also necessary to + * provide path to a lock file using the LockFile + * property. + * \sa FileAppender + *
+ * + *
LockFile
+ *
This property specifies lock file, file used for + * inter-process synchronization of log file access. The property + * is only used when UseLockFile is set to true. Then it + * is mandatory. + * \sa FileAppender + *
+ * + *
AsyncAppend
+ *
Set this property to true if you want all appends using + * this appender to be done asynchronously. Default is false.
+ * + *
+ */ + class LOG4CPLUS_EXPORT Appender + : public virtual log4cplus::helpers::SharedObject + { + public: + // Ctor + Appender(); + Appender(const log4cplus::helpers::Properties & properties); + + // Dtor + virtual ~Appender(); + + /** + * This function is for derived appenders to call from their + * destructors. All classes derived from `Appender` class + * _must_ call this function from their destructors. It + * ensures that appenders will get properly closed during + * shutdown by call to `close()` function before they are + * destroyed. + */ + void destructorImpl(); + + // Methods + /** + * Release any resources allocated within the appender such as file + * handles, network connections, etc. + * + * It is a programming error to append to a closed appender. + */ + virtual void close() = 0; + + /** + * Check if this appender is in closed state. + */ + bool isClosed() const; + + /** + * This method performs threshold checks and invokes filters before + * delegating actual logging to the subclasses specific {@link + * #append} method. + */ + void syncDoAppend(const log4cplus::spi::InternalLoggingEvent& event); + + /** + * This method performs book keeping related to asynchronous logging + * and executes `syncDoAppend()` to do the actual logging. + */ + + void asyncDoAppend(const log4cplus::spi::InternalLoggingEvent& event); + + /** + * This function checks `async` flag. It either executes + * `syncDoAppend()` directly or enqueues its execution to thread pool + * thread. + */ + void doAppend(const log4cplus::spi::InternalLoggingEvent& event); + + /** + * Get the name of this appender. The name uniquely identifies the + * appender. + */ + virtual log4cplus::tstring getName(); + + /** + * Set the name of this appender. The name is used by other + * components to identify this appender. + */ + virtual void setName(const log4cplus::tstring& name); + + /** + * Set the {@link ErrorHandler} for this Appender. + */ + virtual void setErrorHandler(std::unique_ptr eh); + + /** + * Return the currently set {@link ErrorHandler} for this + * Appender. + */ + virtual ErrorHandler* getErrorHandler(); + + /** + * Set the layout for this appender. Note that some appenders have + * their own (fixed) layouts or do not use one. For example, the + * SocketAppender ignores the layout set here. + */ + virtual void setLayout(std::unique_ptr layout); + + /** + * Returns the layout of this appender. The value may be NULL. + * + * This class owns the returned pointer. + */ + virtual Layout* getLayout(); + + /** + * Set the filter chain on this Appender. + */ + void setFilter(log4cplus::spi::FilterPtr f); + + /** + * Get the filter chain on this Appender. + */ + log4cplus::spi::FilterPtr getFilter() const; + + /** + * Add filter at the end of the filters chain. + */ + void addFilter (log4cplus::spi::FilterPtr f); + + /** + * Add filter at the end of the filters chain. + */ + void addFilter (std::function< + spi::FilterResult (const log4cplus::spi::InternalLoggingEvent &)>); + + /** + * Returns this appenders threshold LogLevel. See the {@link + * #setThreshold} method for the meaning of this option. + */ + LogLevel getThreshold() const { return threshold; } + + /** + * Set the threshold LogLevel. All log events with lower LogLevel + * than the threshold LogLevel are ignored by the appender. + * + * In configuration files this option is specified by setting the + * value of the Threshold option to a LogLevel + * string, such as "DEBUG", "INFO" and so on. + */ + void setThreshold(LogLevel th) { threshold = th; } + + /** + * Check whether the message LogLevel is below the appender's + * threshold. If there is no threshold set, then the return value is + * always true. + */ + bool isAsSevereAsThreshold(LogLevel ll) const { + return ((ll != NOT_SET_LOG_LEVEL) && (ll >= threshold)); + } + + /** + * This method waits for all events that are being asynchronously + * logged to finish. + */ + void waitToFinishAsyncLogging(); + + protected: + // Methods + /** + * Subclasses of Appender should implement this + * method to perform actual logging. + * @see doAppend method. + */ + virtual void append(const log4cplus::spi::InternalLoggingEvent& event) = 0; + + tstring & formatEvent (const log4cplus::spi::InternalLoggingEvent& event) const; + + // Data + /** The layout variable does not need to be set if the appender + * implementation has its own layout. */ + std::unique_ptr layout; + + /** Appenders are named. */ + log4cplus::tstring name; + + /** There is no LogLevel threshold filtering by default. */ + LogLevel threshold; + + /** The first filter in the filter chain. Set to null + * initially. */ + log4cplus::spi::FilterPtr filter; + + /** It is assumed and enforced that errorHandler is never null. */ + std::unique_ptr errorHandler; + + //! Optional system wide synchronization lock. + std::unique_ptr lockFile; + + //! Use lock file for inter-process synchronization of access + //! to log file. + bool useLockFile; + + //! Asynchronous append. + bool async; +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::atomic in_flight; + std::mutex in_flight_mutex; + std::condition_variable in_flight_condition; +#endif + + /** Is this appender closed? */ + bool closed; + + private: +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + void subtract_in_flight(); +#endif + }; + + /** This is a pointer to an Appender. */ + typedef helpers::SharedObjectPtr SharedAppenderPtr; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/asyncappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/asyncappender.h new file mode 100644 index 0000000..84b1ea1 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/asyncappender.h @@ -0,0 +1,97 @@ +// -*- C++ -*- +// Module: Log4cplus +// File: asyncappender.h +// Created: 1/2009 +// Author: Vaclav Haisman +// +// +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +// + +/** @file */ + +#ifndef LOG4CPLUS_ASYNCAPPENDER_H +#define LOG4CPLUS_ASYNCAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#ifndef LOG4CPLUS_SINGLE_THREADED + +#include +#include +#include +#include + + +namespace log4cplus +{ + + +/** + This `Appender` is a wrapper to which other appenders can be attached. The + attached appendres are then appended to from a separate thread which reads + events appended to this appender from a queue. + + \sa helpers::AppenderAttachableImpl + */ +class LOG4CPLUS_EXPORT AsyncAppender + : public Appender + , public helpers::AppenderAttachableImpl +{ +public: + AsyncAppender (SharedAppenderPtr const & app, unsigned max_len); + AsyncAppender (helpers::Properties const &); + virtual ~AsyncAppender (); + + virtual void close (); + +protected: + virtual void append (spi::InternalLoggingEvent const &); + + void init_queue_thread (unsigned); + + thread::AbstractThreadPtr queue_thread; + thread::QueuePtr queue; + +private: + AsyncAppender (AsyncAppender const &); + AsyncAppender & operator = (AsyncAppender const &); +}; + + +typedef helpers::SharedObjectPtr AsyncAppenderPtr; + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_SINGLE_THREADED + +#endif // LOG4CPLUS_ASYNCAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/boost/deviceappender.hxx b/hgdriver/3rdparty/log4cplus/include/log4cplus/boost/deviceappender.hxx new file mode 100644 index 0000000..eb41edf --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/boost/deviceappender.hxx @@ -0,0 +1,201 @@ +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_BOOST_DEVICEAPPENDER_HXX +#define LOG4CPLUS_BOOST_DEVICEAPPENDER_HXX + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + + +namespace log4cplus +{ + + +namespace device_appender_detail +{ + + +template +struct device_type_traits +{ + typedef T & device_type; + + static + device_type + unwrap (device_type x) + { + return x; + } +}; + + +template +struct device_type_traits > +{ + typedef boost::shared_ptr device_type; + + static + T & + unwrap (device_type const & ptr) + { + return *ptr; + } +}; + + +} // namespace device_appender_detail + + +template +class DeviceAppender + : public Appender +{ +public: + typedef device_appender_detail::device_type_traits device_traits; + typedef typename device_traits::device_type device_type; + + template + DeviceAppender (D & d, bool close_device = true) + : device (d) + , close_flag (close_device) + { } + + template + DeviceAppender (boost::shared_ptr const & d, bool close_device = true) + : device (d) + , close_flag (close_device) + { } + + template + DeviceAppender (D & d, const helpers::Properties & props) + : Appender (props) + , device (d) + { + if (props.exists (LOG4CPLUS_TEXT ("CloseDevice"))) + close_flag = true; + else + close_flag = false; + } + + template + DeviceAppender (boost::shared_ptr const & d, + const helpers::Properties & props) + : Appender (props) + , device (d) + { + if (props.exists (LOG4CPLUS_TEXT ("CloseDevice"))) + close_flag = true; + else + close_flag = false; + } + + virtual + ~DeviceAppender () + { } + + virtual + void + close () + { + if (close_flag) + boost::iostreams::close (device_traits::unwrap (device)); + } + +protected: + virtual + void + append (log4cplus::spi::InternalLoggingEvent const & event) + { + tstring & str = formatEvent (event); + boost::iostreams::write (device_traits::unwrap (device), + str.c_str (), str.size ()); + } + + device_type device; + bool close_flag; + +private: + DeviceAppender (DeviceAppender const &); + DeviceAppender & operator = (DeviceAppender const &); +}; + + +template +inline +SharedAppenderPtr +make_device_appender (T & d, bool close_device = true) +{ + SharedAppenderPtr app (new DeviceAppender (d, close_device)); + return app; +} + + +template +inline +SharedAppenderPtr +make_device_appender (T & d, const helpers::Properties & props) +{ + SharedAppenderPtr app (new DeviceAppender (d, props)); + return app; +} + + +template +inline +SharedAppenderPtr +make_device_appender_sp (boost::shared_ptr const & p, + bool close_device = true) +{ + SharedAppenderPtr app ( + new DeviceAppender > (p, close_device)); + return app; +} + + +template +inline +SharedAppenderPtr +make_device_appender_sp (boost::shared_ptr const & p, + const helpers::Properties & props) +{ + SharedAppenderPtr app ( + new DeviceAppender > (p, props)); + return app; +} + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_BOOST_DEVICEAPPENDER_HXX diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/callbackappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/callbackappender.h new file mode 100644 index 0000000..0c61bed --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/callbackappender.h @@ -0,0 +1,73 @@ +// -*- C++ -*- +// Copyright (C) 2015-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +/** @file */ + +#ifndef LOG4CPLUS_CALLBACK_APPENDER_HEADER_ +#define LOG4CPLUS_CALLBACK_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + + +namespace log4cplus { + +/** +* Send log events to a C function callback. +*/ +class LOG4CPLUS_EXPORT CallbackAppender + : public Appender { +public: + CallbackAppender(); + CallbackAppender(log4cplus_log_event_callback_t callback, void * cookie); + CallbackAppender(const log4cplus::helpers::Properties&); + + virtual ~CallbackAppender(); + virtual void close(); + + void setCookie(void *); + void setCallback(log4cplus_log_event_callback_t); + +protected: + virtual void append(const log4cplus::spi::InternalLoggingEvent& event); + +private: + log4cplus_log_event_callback_t callback; + void * cookie; + + // Disallow copying of instances of this class + CallbackAppender(const CallbackAppender&) = delete; + CallbackAppender& operator=(const CallbackAppender&) = delete; +}; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_CALLBACK_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/clfsappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/clfsappender.h new file mode 100644 index 0000000..e764315 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/clfsappender.h @@ -0,0 +1,98 @@ +// -*- C++ -*- +// Module: Log4cplus +// File: clfsappender.h +// Created: 5/2012 +// Author: Vaclav Zeman +// +// +// Copyright (C) 2012-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +// + +/** @file */ + +#ifndef LOG4CPLUS_CLFSAPPENDER_H +#define LOG4CPLUS_CLFSAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +#if defined (LOG4CPLUS_CLFSAPPENDER_BUILD_DLL) +# if defined (INSIDE_LOG4CPLUS_CLFSAPPENDER) +# define LOG4CPLUS_CLFSAPPENDER_EXPORT __declspec(dllexport) +# else +# define LOG4CPLUS_CLFSAPPENDER_EXPORT __declspec(dllimport) +# endif +#else +# define LOG4CPLUS_CLFSAPPENDER_EXPORT +#endif + + +namespace log4cplus +{ + + +class LOG4CPLUS_CLFSAPPENDER_EXPORT CLFSAppender + : public Appender +{ +public: + CLFSAppender (tstring const & logname, unsigned long logsize, + unsigned long buffersize); + explicit CLFSAppender (helpers::Properties const &); + virtual ~CLFSAppender (); + + virtual void close (); + + static void registerAppender (); + +protected: + virtual void append (spi::InternalLoggingEvent const &); + + void init (tstring const & logname, unsigned long logsize, + unsigned long buffersize); + + struct Data; + + Data * data; + +private: + CLFSAppender (CLFSAppender const &); + CLFSAppender & operator = (CLFSAppender const &); +}; + + +typedef helpers::SharedObjectPtr CLFSAppenderPtr; + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_CLFSAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/clogger.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/clogger.h new file mode 100644 index 0000000..b8b2a43 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/clogger.h @@ -0,0 +1,126 @@ +// -*- C -*- +/** + * Module: Log4CPLUS + * File: clogger.h + * Created: 01/2011 + * Author: Jens Rehsack + * + * + * Copyright 2011-2017 Jens Rehsack & Tad E. 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. + */ + +/** @file + * This header defines the C API for log4cplus and the logging macros. */ + +#ifndef LOG4CPLUS_CLOGGERHEADER_ +#define LOG4CPLUS_CLOGGERHEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif + +// TODO UNICDE capable + +typedef void * log4cplus_logger_t; +typedef log4cplus_logger_t logger_t; + +typedef int log4cplus_loglevel_t; +typedef log4cplus_loglevel_t loglevel_t; + +#define L4CP_OFF_LOG_LEVEL 60000 +#define L4CP_FATAL_LOG_LEVEL 50000 +#define L4CP_ERROR_LOG_LEVEL 40000 +#define L4CP_WARN_LOG_LEVEL 30000 +#define L4CP_INFO_LOG_LEVEL 20000 +#define L4CP_DEBUG_LOG_LEVEL 10000 +#define L4CP_TRACE_LOG_LEVEL 0 +#define L4CP_ALL_LOG_LEVEL TRACE_LOG_LEVEL +#define L4CP_NOT_SET_LOG_LEVEL -1 + +#ifdef UNICODE +typedef wchar_t log4cplus_char_t; +#else +typedef char log4cplus_char_t; +#endif // UNICODE + +#if ! defined (LOG4CPLUS_TEXT) +#ifdef UNICODE +# define LOG4CPLUS_TEXT2(STRING) L##STRING +#else +# define LOG4CPLUS_TEXT2(STRING) STRING +#endif // UNICODE +#define LOG4CPLUS_TEXT(STRING) LOG4CPLUS_TEXT2(STRING) +#endif // LOG4CPLUS_TEXT + +LOG4CPLUS_EXPORT void * log4cplus_initialize(void); +LOG4CPLUS_EXPORT int log4cplus_deinitialize(void * initializer); + +LOG4CPLUS_EXPORT int log4cplus_file_configure(const log4cplus_char_t *pathname); +LOG4CPLUS_EXPORT int log4cplus_file_reconfigure(const log4cplus_char_t *pathname); +LOG4CPLUS_EXPORT int log4cplus_str_configure(const log4cplus_char_t *config); +LOG4CPLUS_EXPORT int log4cplus_str_reconfigure(const log4cplus_char_t *config); +LOG4CPLUS_EXPORT int log4cplus_basic_configure(void); +LOG4CPLUS_EXPORT int log4cplus_basic_reconfigure(int logToStdErr); +LOG4CPLUS_EXPORT void log4cplus_shutdown(void); + +LOG4CPLUS_EXPORT int log4cplus_logger_exists(const log4cplus_char_t *name); +LOG4CPLUS_EXPORT int log4cplus_logger_is_enabled_for( + const log4cplus_char_t *name, log4cplus_loglevel_t ll); + +LOG4CPLUS_EXPORT int log4cplus_logger_log(const log4cplus_char_t *name, + log4cplus_loglevel_t ll, const log4cplus_char_t *msgfmt, ...) + LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 3, 4); + +LOG4CPLUS_EXPORT int log4cplus_logger_log_str(const log4cplus_char_t *name, + log4cplus_loglevel_t ll, const log4cplus_char_t *msg); + +LOG4CPLUS_EXPORT int log4cplus_logger_force_log(const log4cplus_char_t *name, + log4cplus_loglevel_t ll, const log4cplus_char_t *msgfmt, ...) + LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 3, 4); + +LOG4CPLUS_EXPORT int log4cplus_logger_force_log_str(const log4cplus_char_t *name, + log4cplus_loglevel_t ll, const log4cplus_char_t *msg); + +//! CallbackAppender callback type. +typedef void (* log4cplus_log_event_callback_t)(void * cookie, + log4cplus_char_t const * message, log4cplus_char_t const * loggerName, + log4cplus_loglevel_t ll, log4cplus_char_t const * thread, + log4cplus_char_t const * thread2, + unsigned long long timestamp_secs, unsigned long timestamp_usecs, + log4cplus_char_t const * file, log4cplus_char_t const * function, int line); + +LOG4CPLUS_EXPORT int log4cplus_add_callback_appender( + const log4cplus_char_t * logger, log4cplus_log_event_callback_t callback, + void * cookie); + +// Custom LogLevel +LOG4CPLUS_EXPORT int log4cplus_add_log_level(unsigned int ll, + const log4cplus_char_t *ll_name); +LOG4CPLUS_EXPORT int log4cplus_remove_log_level(unsigned int ll, + const log4cplus_char_t *ll_name); + +#ifdef __cplusplus +} +#endif + +#endif /*?LOG4CPLUS_CLOGGERHEADER_*/ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.cmake.in b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.cmake.in new file mode 100644 index 0000000..2bf4342 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.cmake.in @@ -0,0 +1,335 @@ +/* include/log4cplus/config.h.in. Generated from configure.in by autoheader. */ + +#ifndef LOG4CPLUS_CONFIG_H + +#define LOG4CPLUS_CONFIG_H + +/* Defined if the compiler supports C99 style variadic macros with + __VA_ARGS__. */ +/* #undef HAS_C99_VARIADIC_MACROS */ + +/* Defined if the compiler supports GNU style variadic macros. */ +/* #undef HAS_GNU_VARIADIC_MACROS */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `ftime' function. */ +#cmakedefine HAVE_FTIME 1 + +/* */ +#cmakedefine HAVE_GETADDRINFO 1 + +/* */ +#cmakedefine HAVE_GETHOSTBYNAME_R 1 + +/* Define to 1 if you have the `getpid' function. */ +#cmakedefine HAVE_GETPID 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `gmtime_r' function. */ +#cmakedefine HAVE_GMTIME_R 1 + +/* Define to 1 if you have the `htonl' function. */ +#cmakedefine HAVE_HTONL 1 + +/* Define to 1 if you have the `htons' function. */ +#cmakedefine HAVE_HTONS 1 + +/* Define to 1 if you have the `iconv' function. */ +#cmakedefine HAVE_ICONV 1 + +/* Define to 1 if you have the `iconv_close' function. */ +#cmakedefine HAVE_ICONV_CLOSE 1 + +/* Define to 1 if you have the `iconv_open' function. */ +#cmakedefine HAVE_ICONV_OPEN 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `advapi32' library (-ladvapi32). */ +#cmakedefine HAVE_LIBADVAPI32 1 + +/* Define to 1 if you have the `libiconv' function. */ +/* #undef HAVE_LIBICONV */ + +/* Define to 1 if you have the `libiconv_close' function. */ +/* #undef HAVE_LIBICONV_CLOSE */ + +/* Define to 1 if you have the `libiconv_open' function. */ +/* #undef HAVE_LIBICONV_OPEN */ + +/* Define to 1 if you have the `kernel32' library (-lkernel32). */ +#cmakedefine HAVE_LIBKERNEL32 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#cmakedefine HAVE_LIBNSL 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +#cmakedefine HAVE_LIBRT 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#cmakedefine HAVE_LIBSOCKET 1 + +/* Define to 1 if you have the `ws2_32' library (-lws2_32). */ +#cmakedefine HAVE_LIBWS2_32 1 + +/* Define to 1 if you have the `localtime_r' function. */ +#cmakedefine HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the `lstat' function. */ +#cmakedefine HAVE_LSTAT 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `ntohl' function. */ +#cmakedefine HAVE_NTOHL 1 + +/* Define to 1 if you have the `ntohs' function. */ +#cmakedefine HAVE_NTOHS 1 + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the `stat' function. */ +#cmakedefine HAVE_STAT 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfprintf_s' function. */ +#cmakedefine HAVE_VFPRINTF_S 1 + +/* Define to 1 if you have the `vfwprintf_s' function. */ +#cmakedefine HAVE_VFWPRINTF_S 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#cmakedefine HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsprintf_s' function. */ +#cmakedefine HAVE_VSPRINTF_S 1 + +/* Define to 1 if you have the `vswprintf_s' function. */ +#cmakedefine HAVE_VSWPRINTF_S 1 + +/* Define to 1 if you have the `_vsnprintf' function. */ +#cmakedefine HAVE__VSNPRINTF 1 + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +#cmakedefine HAVE__VSNPRINTF_S 1 + +/* Define to 1 if you have the `_vsnwprintf_s' function. */ +#cmakedefine HAVE__VSNWPRINTF_S 1 + +/* Defined if the compiler supports __FUNCTION__ macro. */ +#cmakedefine HAVE___FUNCTION___MACRO 1 + +/* Defined if the compiler supports __PRETTY_FUNCTION__ macro. */ +#cmakedefine HAVE___PRETTY_FUNCTION___MACRO 1 + +/* Defined if the compiler provides __sync_add_and_fetch(). */ +#cmakedefine HAVE___SYNC_ADD_AND_FETCH 1 + +/* Defined if the compiler provides __sync_sub_and_fetch(). */ +#cmakedefine HAVE___SYNC_SUB_AND_FETCH 1 + +/* Defined for --enable-debugging builds. */ +#undef LOG4CPLUS_DEBUGGING + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) construct. */ +#define LOG4CPLUS_DECLSPEC_EXPORT @LOG4CPLUS_DECLSPEC_EXPORT@ + +/* Defined if the compiler understands __declspec(dllexport) or construct. */ +#define LOG4CPLUS_DECLSPEC_IMPORT @LOG4CPLUS_DECLSPEC_IMPORT@ /**/ + +/* */ +#cmakedefine LOG4CPLUS_HAVE_CLOCK_GETTIME 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ENAMETOOLONG 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ERRNO_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_FTIME 1 + +/* */ +#define LOG4CPLUS_HAVE_FUNCTION_MACRO 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GETADDRINFO 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GETHOSTBYNAME_R 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GETPID 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GETTID 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GETTIMEOFDAY 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_GMTIME_R 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_HTONL 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_HTONS 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ICONV 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ICONV_CLOSE 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ICONV_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_ICONV_OPEN 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_LIMITS_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_LOCALTIME_R 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_LSTAT 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_NETDB_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_NETINET_IN_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_NETINET_TCP_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_NTOHL 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_NTOHS 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_STAT 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_STDARG_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_STDIO_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_STDLIB_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYSLOG_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_SOCKET_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_STAT_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_SYSCALL_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_TIMEB_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_TIME_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_SYS_TYPES_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_TIME_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_UNISTD_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_VFPRINTF_S 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_VFWPRINTF_S 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_VSNPRINTF 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_VSPRINTF_S 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_VSWPRINTF_S 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE_WCHAR_H 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE__VSNPRINTF 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE__VSNPRINTF_S 1 + +/* */ +#cmakedefine LOG4CPLUS_HAVE__VSNWPRINTF_S 1 + +/* Define if this is a single-threaded library. */ +#undef LOG4CPLUS_SINGLE_THREADED + +/* */ +#undef LOG4CPLUS_USE_PTHREADS + +/* Define for compilers/standard libraries that support more than just the "C" + locale. */ +#undef LOG4CPLUS_WORKING_LOCALE + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. Seems to be unused*/ +#cmakedefine STDC_HEADERS 1 + +/* Define to int if undefined. */ +#cmakedefine socklen_t int + +#endif // LOG4CPLUS_CONFIG_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.in b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.in new file mode 100644 index 0000000..9e62eb6 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.h.in @@ -0,0 +1,471 @@ +/* include/log4cplus/config.h.in. Generated from configure.ac by autoheader. */ + +#ifndef LOG4CPLUS_CONFIG_H + +#define LOG4CPLUS_CONFIG_H + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `fcntl' function. */ +#undef HAVE_FCNTL + +/* Define to 1 if you have the `flock' function. */ +#undef HAVE_FLOCK + +/* Define to 1 if you have the `ftime' function. */ +#undef HAVE_FTIME + +/* Define to 1 if the system has the `constructor' function attribute */ +#undef HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR + +/* Define to 1 if the system has the `constructor_priority' function attribute + */ +#undef HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY + +/* */ +#undef HAVE_GETADDRINFO + +/* */ +#undef HAVE_GETHOSTBYNAME_R + +/* Define to 1 if you have the `getpid' function. */ +#undef HAVE_GETPID + +/* Define to 1 if you have the `gmtime_r' function. */ +#undef HAVE_GMTIME_R + +/* Define to 1 if you have the `htonl' function. */ +#undef HAVE_HTONL + +/* Define to 1 if you have the `htons' function. */ +#undef HAVE_HTONS + +/* Define to 1 if you have the `iconv' function. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the `iconv_close' function. */ +#undef HAVE_ICONV_CLOSE + +/* Define to 1 if you have the `iconv_open' function. */ +#undef HAVE_ICONV_OPEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `advapi32' library (-ladvapi32). */ +#undef HAVE_LIBADVAPI32 + +/* Define to 1 if you have the `libiconv' function. */ +#undef HAVE_LIBICONV + +/* Define to 1 if you have the `libiconv_close' function. */ +#undef HAVE_LIBICONV_CLOSE + +/* Define to 1 if you have the `libiconv_open' function. */ +#undef HAVE_LIBICONV_OPEN + +/* Define to 1 if you have the `kernel32' library (-lkernel32). */ +#undef HAVE_LIBKERNEL32 + +/* Define to 1 if you have the `oleaut32' library (-loleaut32). */ +#undef HAVE_LIBOLEAUT32 + +/* Define to 1 if you have the `ws2_32' library (-lws2_32). */ +#undef HAVE_LIBWS2_32 + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +/* Define to 1 if you have the `lockf' function. */ +#undef HAVE_LOCKF + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the `mbstowcs' function. */ +#undef HAVE_MBSTOWCS + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `ntohl' function. */ +#undef HAVE_NTOHL + +/* Define to 1 if you have the `ntohs' function. */ +#undef HAVE_NTOHS + +/* Define to 1 if you have the `OutputDebugStringW' function. */ +#undef HAVE_OUTPUTDEBUGSTRINGW + +/* Define to 1 if you have the `pipe' function. */ +#undef HAVE_PIPE + +/* Define to 1 if you have the `pipe2' function. */ +#undef HAVE_PIPE2 + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + +/* If available, contains the Python version number currently in use. */ +#undef HAVE_PYTHON + +/* Define to 1 if you have the `shutdown' function. */ +#undef HAVE_SHUTDOWN + +/* Define to 1 if you have the `stat' function. */ +#undef HAVE_STAT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Defined if the compiler understands __thread or __declspec(thread) + construct. */ +#undef HAVE_TLS_SUPPORT + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the `init_priority' variable attribute */ +#undef HAVE_VAR_ATTRIBUTE_INIT_PRIORITY + +/* Define to 1 if you have the `vfprintf_s' function. */ +#undef HAVE_VFPRINTF_S + +/* Define to 1 if you have the `vfwprintf_s' function. */ +#undef HAVE_VFWPRINTF_S + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `vsnwprintf' function. */ +#undef HAVE_VSNWPRINTF + +/* Define to 1 if you have the `vsprintf_s' function. */ +#undef HAVE_VSPRINTF_S + +/* Define to 1 if you have the `vswprintf_s' function. */ +#undef HAVE_VSWPRINTF_S + +/* Define to 1 if you have the `wcstombs' function. */ +#undef HAVE_WCSTOMBS + +/* Define to 1 if you have the `_vsnprintf' function. */ +#undef HAVE__VSNPRINTF + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +#undef HAVE__VSNPRINTF_S + +/* Define to 1 if you have the `_vsnwprintf' function. */ +#undef HAVE__VSNWPRINTF + +/* Define to 1 if you have the `_vsnwprintf_s' function. */ +#undef HAVE__VSNWPRINTF_S + +/* Defined if the compiler supports __FUNCTION__ macro. */ +#undef HAVE___FUNCTION___MACRO + +/* Defined if the compiler supports __func__ symbol. */ +#undef HAVE___FUNC___SYMBOL + +/* Defined if the compiler supports __PRETTY_FUNCTION__ macro. */ +#undef HAVE___PRETTY_FUNCTION___MACRO + +/* Defined for --enable-debugging builds. */ +#undef LOG4CPLUS_DEBUGGING + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) or __global construct. */ +#undef LOG4CPLUS_DECLSPEC_EXPORT + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) or __global construct. */ +#undef LOG4CPLUS_DECLSPEC_IMPORT + +/* Defined if the compiler understands __attribute__((visibility("hidden"))) + or __hidden construct. */ +#undef LOG4CPLUS_DECLSPEC_PRIVATE + +/* */ +#undef LOG4CPLUS_HAVE_ARPA_INET_H + +/* */ +#undef LOG4CPLUS_HAVE_ENAMETOOLONG + +/* */ +#undef LOG4CPLUS_HAVE_ERRNO_H + +/* */ +#undef LOG4CPLUS_HAVE_FCNTL + +/* */ +#undef LOG4CPLUS_HAVE_FCNTL_H + +/* */ +#undef LOG4CPLUS_HAVE_FLOCK + +/* */ +#undef LOG4CPLUS_HAVE_FTIME + +/* */ +#undef LOG4CPLUS_HAVE_FUNCTION_MACRO + +/* */ +#undef LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR + +/* */ +#undef LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY + +/* */ +#undef LOG4CPLUS_HAVE_FUNC_SYMBOL + +/* */ +#undef LOG4CPLUS_HAVE_GETADDRINFO + +/* */ +#undef LOG4CPLUS_HAVE_GETHOSTBYNAME_R + +/* */ +#undef LOG4CPLUS_HAVE_GETPID + +/* */ +#undef LOG4CPLUS_HAVE_GETTID + +/* */ +#undef LOG4CPLUS_HAVE_GMTIME_R + +/* */ +#undef LOG4CPLUS_HAVE_HTONL + +/* */ +#undef LOG4CPLUS_HAVE_HTONS + +/* */ +#undef LOG4CPLUS_HAVE_ICONV + +/* */ +#undef LOG4CPLUS_HAVE_ICONV_CLOSE + +/* */ +#undef LOG4CPLUS_HAVE_ICONV_H + +/* */ +#undef LOG4CPLUS_HAVE_ICONV_OPEN + +/* */ +#undef LOG4CPLUS_HAVE_LIMITS_H + +/* */ +#undef LOG4CPLUS_HAVE_LOCALTIME_R + +/* */ +#undef LOG4CPLUS_HAVE_LOCKF + +/* */ +#undef LOG4CPLUS_HAVE_LSTAT + +/* */ +#undef LOG4CPLUS_HAVE_MBSTOWCS + +/* */ +#undef LOG4CPLUS_HAVE_NETDB_H + +/* */ +#undef LOG4CPLUS_HAVE_NETINET_IN_H + +/* */ +#undef LOG4CPLUS_HAVE_NETINET_TCP_H + +/* */ +#undef LOG4CPLUS_HAVE_NTOHL + +/* */ +#undef LOG4CPLUS_HAVE_NTOHS + +/* */ +#undef LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING + +/* */ +#undef LOG4CPLUS_HAVE_PIPE + +/* */ +#undef LOG4CPLUS_HAVE_PIPE2 + +/* */ +#undef LOG4CPLUS_HAVE_POLL + +/* */ +#undef LOG4CPLUS_HAVE_POLL_H + +/* */ +#undef LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO + +/* */ +#undef LOG4CPLUS_HAVE_SHUTDOWN + +/* */ +#undef LOG4CPLUS_HAVE_STAT + +/* */ +#undef LOG4CPLUS_HAVE_STDARG_H + +/* */ +#undef LOG4CPLUS_HAVE_STDIO_H + +/* */ +#undef LOG4CPLUS_HAVE_STDLIB_H + +/* */ +#undef LOG4CPLUS_HAVE_SYSLOG_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_FILE_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_SOCKET_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_STAT_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_SYSCALL_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TIMEB_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TIME_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TYPES_H + +/* */ +#undef LOG4CPLUS_HAVE_TIME_H + +/* */ +#undef LOG4CPLUS_HAVE_TLS_SUPPORT + +/* */ +#undef LOG4CPLUS_HAVE_UNISTD_H + +/* */ +#undef LOG4CPLUS_HAVE_VAR_ATTRIBUTE_INIT_PRIORITY + +/* */ +#undef LOG4CPLUS_HAVE_VFPRINTF_S + +/* */ +#undef LOG4CPLUS_HAVE_VFWPRINTF_S + +/* */ +#undef LOG4CPLUS_HAVE_VSNPRINTF + +/* */ +#undef LOG4CPLUS_HAVE_VSNWPRINTF + +/* */ +#undef LOG4CPLUS_HAVE_VSPRINTF_S + +/* */ +#undef LOG4CPLUS_HAVE_VSWPRINTF_S + +/* */ +#undef LOG4CPLUS_HAVE_WCHAR_H + +/* */ +#undef LOG4CPLUS_HAVE_WCSTOMBS + +/* */ +#undef LOG4CPLUS_HAVE__VSNPRINTF + +/* */ +#undef LOG4CPLUS_HAVE__VSNPRINTF_S + +/* */ +#undef LOG4CPLUS_HAVE__VSNWPRINTF + +/* */ +#undef LOG4CPLUS_HAVE__VSNWPRINTF_S + +/* Define if this is a single-threaded library. */ +#undef LOG4CPLUS_SINGLE_THREADED + +/* */ +#undef LOG4CPLUS_THREAD_LOCAL_VAR + +/* */ +#undef LOG4CPLUS_USE_PTHREADS + +/* Define when iconv() is available. */ +#undef LOG4CPLUS_WITH_ICONV + +/* Defined to enable unit tests. */ +#undef LOG4CPLUS_WITH_UNIT_TESTS + +/* Define for C99 compilers/standard libraries that support more than just the + "C" locale. */ +#undef LOG4CPLUS_WORKING_C_LOCALE + +/* Define for compilers/standard libraries that support more than just the "C" + locale. */ +#undef LOG4CPLUS_WORKING_LOCALE + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Defined to the actual TLS support construct. */ +#undef TLS_SUPPORT_CONSTRUCT + +/* Substitute for socklen_t */ +#undef socklen_t + +#endif // LOG4CPLUS_CONFIG_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config.hxx b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.hxx new file mode 100644 index 0000000..4563321 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config.hxx @@ -0,0 +1,211 @@ +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_CONFIG_HXX +#define LOG4CPLUS_CONFIG_HXX + +#if defined (_WIN32) +# include +#elif (defined(__MWERKS__) && defined(__MACOS__)) +# include +#else +# include +#endif + +# if ! defined (LOG4CPLUS_WORKING_LOCALE) \ + && ! defined (LOG4CPLUS_WORKING_C_LOCALE) \ + && ! defined (LOG4CPLUS_WITH_ICONV) +# define LOG4CPLUS_POOR_MANS_CHCONV +#endif + +#ifndef LOG4CPLUS_DECLSPEC_EXPORT +#define LOG4CPLUS_DECLSPEC_EXPORT /* empty */ +#endif + +#ifndef LOG4CPLUS_DECLSPEC_IMPORT +#define LOG4CPLUS_DECLSPEC_IMPORT /* empty */ +#endif + +#ifndef LOG4CPLUS_DECLSPEC_PRIVATE +#define LOG4CPLUS_DECLSPEC_PRIVATE /* empty */ +#endif + +#define LOG4CPLUS_PRIVATE LOG4CPLUS_DECLSPEC_PRIVATE + +#if !defined(_WIN32) +# define LOG4CPLUS_USE_BSD_SOCKETS +# if !defined(LOG4CPLUS_SINGLE_THREADED) +# define LOG4CPLUS_USE_PTHREADS +# endif +# if defined (INSIDE_LOG4CPLUS) +# define LOG4CPLUS_EXPORT LOG4CPLUS_DECLSPEC_EXPORT +# else +# define LOG4CPLUS_EXPORT LOG4CPLUS_DECLSPEC_IMPORT +# endif // defined (INSIDE_LOG4CPLUS) + +#endif // !_WIN32 + +#if defined (LOG4CPLUS_INLINES_ARE_EXPORTED) \ + && defined (LOG4CPLUS_BUILD_DLL) +# define LOG4CPLUS_INLINE_EXPORT inline +#else +# define LOG4CPLUS_INLINE_EXPORT +#endif + +#if defined (UNICODE) +# if defined (_MSC_VER) && _MSC_VER >= 1400 +# define LOG4CPLUS_FSTREAM_ACCEPTS_WCHAR_T +# endif +# if defined (_MSC_VER) && _MSC_VER >= 1600 +# define LOG4CPLUS_HAVE_CODECVT_UTF8_FACET +# define LOG4CPLUS_HAVE_CODECVT_UTF16_FACET +# endif +#endif + +// C++11 stuff + +#if ! defined (__has_feature) +//! __has_feature(X) is Clangs way for testing features. +//! Define it to 0 if it does not exist. +# define __has_feature(X) 0 +#endif + +#if __has_feature (cxx_noexcept) \ + || (defined (__GNUC__) \ + && (__GNUC__ > 4 \ + || __GNUC__ == 4 && __GNUC_MINOR__ >= 6)) \ + || (defined (_MSC_VER) && _MSC_VER >= 1900) +# define LOG4CPLUS_NOEXCEPT noexcept +# define LOG4CPLUS_NOEXCEPT_FALSE noexcept(false) +#else +# define LOG4CPLUS_NOEXCEPT /* empty */ +# define LOG4CPLUS_NOEXCEPT_FALSE /* empty */ +#endif + +#if ! defined (UNICODE) && defined (__GNUC__) && __GNUC__ >= 3 +# define LOG4CPLUS_FORMAT_ATTRIBUTE(archetype, format_index, first_arg_index) \ + __attribute__ ((format (archetype, format_index, first_arg_index))) +#else +# define LOG4CPLUS_FORMAT_ATTRIBUTE(archetype, fmt_index, first_arg_index) \ + /* empty */ +#endif + +#if defined (__GNUC__) \ + && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) \ + && ! defined (__INTEL_COMPILER) \ + && ! defined (__CUDACC__) +# define LOG4CPLUS_CALLER_FILE() __builtin_FILE () +# define LOG4CPLUS_CALLER_LINE() __builtin_LINE () +# define LOG4CPLUS_CALLER_FUNCTION() __builtin_FUNCTION () +#else +# define LOG4CPLUS_CALLER_FILE() (nullptr) +# define LOG4CPLUS_CALLER_LINE() (-1) +# define LOG4CPLUS_CALLER_FUNCTION() (nullptr) +#endif + +#if defined (__GNUC__) && __GNUC__ >= 3 +# define LOG4CPLUS_ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +# define LOG4CPLUS_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# define LOG4CPLUS_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__)) +# define LOG4CPLUS_BUILTIN_EXPECT(exp, c) __builtin_expect ((exp), (c)) +#else +# if ! defined (LOG4CPLUS_ATTRIBUTE_NORETURN) +# define LOG4CPLUS_ATTRIBUTE_NORETURN /* empty */ +# endif +# define LOG4CPLUS_ATTRIBUTE_PURE /* empty */ +# define LOG4CPLUS_ATTRIBUTE_DEPRECATED /* empty */ +# define LOG4CPLUS_BUILTIN_EXPECT(exp, c) (exp) +#endif + +#define LOG4CPLUS_LIKELY(cond) LOG4CPLUS_BUILTIN_EXPECT(!! (cond), 1) +#define LOG4CPLUS_UNLIKELY(cond) LOG4CPLUS_BUILTIN_EXPECT(!! (cond), 0) + +#if defined (_MSC_VER) \ + || (defined (__BORLANDC__) && __BORLANDC__ >= 0x0650) \ + || (defined (__COMO__) && __COMO_VERSION__ >= 400) /* ??? */ \ + || (defined (__DMC__) && __DMC__ >= 0x700) /* ??? */ \ + || (defined (__clang__) && __clang_major__ >= 3) \ + || (defined (__GNUC__) && (__GNUC__ >= 4 \ + || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) +# define LOG4CPLUS_HAVE_PRAGMA_ONCE +# pragma once +#endif + +#if defined (LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY) +# define LOG4CPLUS_CONSTRUCTOR_FUNC(prio) \ + __attribute__ ((__constructor__ ((prio)))) +#elif defined (LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR) +# define LOG4CPLUS_CONSTRUCTOR_FUNC(prio) \ + __attribute__ ((__constructor__)) +#else +# define LOG4CPLUS_CONSTRUCTOR_FUNC(prio) /* empty */ +#endif + +#if defined (LOG4CPLUS_HAVE_VAR_ATTRIBUTE_INIT_PRIORITY) +# define LOG4CPLUS_INIT_PRIORITY(prio) \ + __attribute__ ((__init_priority__ ((prio)))) +#else +# define LOG4CPLUS_INIT_PRIORITY(prio) /* empty */ +#endif + +#define LOG4CPLUS_INIT_PRIORITY_BASE (65535 / 2) + +#include + +#if defined (LOG4CPLUS_SINGLE_THREADED) +#define LOG4CPLUS_THREADED(x) +#else +#define LOG4CPLUS_THREADED(x) x +#endif + +#if defined(__cplusplus) +#include + +namespace log4cplus +{ + +//! Per thread cleanup function. Users should call this function before +//! a thread ends its execution. It frees resources allocated in thread local +//! storage. It is important only for multi-threaded static library builds +//! of log4cplus and user threads. In all other cases the clean up is provided +//! automatically by other means. +LOG4CPLUS_EXPORT void threadCleanup (); + +//! Initializes log4cplus. +//! +//! \note using `log4cplus::Initializer` is preferred +LOG4CPLUS_EXPORT void initialize (); + +//! Deinitializes log4cplus. +//! +//! \note using `log4cplus::Initializer` is preferred +LOG4CPLUS_EXPORT void deinitialize (); + +//! Set thread pool size. +LOG4CPLUS_EXPORT void setThreadPoolSize (std::size_t pool_size); + +} // namespace log4cplus + +#endif + +#endif // LOG4CPLUS_CONFIG_HXX diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config/defines.hxx.in b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/defines.hxx.in new file mode 100644 index 0000000..8eafb35 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/defines.hxx.in @@ -0,0 +1,250 @@ +#ifndef LOG4CPLUS_CONFIG_DEFINES_HXX +#define LOG4CPLUS_CONFIG_DEFINES_HXX + +/* */ +#undef LOG4CPLUS_HAVE_SYSLOG_H + +/* */ +#undef LOG4CPLUS_HAVE_ARPA_INET_H + +/* */ +#undef LOG4CPLUS_HAVE_NETINET_IN_H + +/* */ +#undef LOG4CPLUS_HAVE_NETINET_TCP_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TIMEB_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TIME_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_TYPES_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_STAT_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_SYSCALL_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_FILE_H + +/* */ +#undef LOG4CPLUS_HAVE_TIME_H + +/* */ +#undef LOG4CPLUS_HAVE_SYS_SOCKET_H + +/* */ +#undef LOG4CPLUS_HAVE_NETDB_H + +/* */ +#undef LOG4CPLUS_HAVE_UNISTD_H + +/* */ +#undef LOG4CPLUS_HAVE_FCNTL_H + +/* */ +#undef LOG4CPLUS_HAVE_STDARG_H + +/* */ +#undef LOG4CPLUS_HAVE_STDIO_H + +/* */ +#undef LOG4CPLUS_HAVE_STDLIB_H + +/* */ +#undef LOG4CPLUS_HAVE_ERRNO_H + +/* */ +#undef LOG4CPLUS_HAVE_WCHAR_H + +/* */ +#undef LOG4CPLUS_HAVE_ICONV_H + +/* */ +#undef LOG4CPLUS_HAVE_LIMITS_H + +/* */ +#undef LOG4CPLUS_HAVE_FTIME + +/* */ +#undef LOG4CPLUS_HAVE_GETADDRINFO + +/* */ +#undef LOG4CPLUS_HAVE_GETHOSTBYNAME_R + +/* */ +#undef LOG4CPLUS_HAVE_GETPID + +/* */ +#undef LOG4CPLUS_HAVE_GMTIME_R + +/* */ +#undef LOG4CPLUS_HAVE_HTONL + +/* */ +#undef LOG4CPLUS_HAVE_HTONS + +/* */ +#undef LOG4CPLUS_HAVE_LOCALTIME_R + +/* */ +#undef LOG4CPLUS_HAVE_LSTAT + +/* */ +#undef LOG4CPLUS_HAVE_FCNTL + +/* */ +#undef LOG4CPLUS_HAVE_LOCKF + +/* */ +#undef LOG4CPLUS_HAVE_FLOCK + +/* */ +#undef LOG4CPLUS_HAVE_NTOHL + +/* */ +#undef LOG4CPLUS_HAVE_NTOHS + +/* Define to 1 if you have the `shutdown' function. */ +#undef LOG4CPLUS_HAVE_SHUTDOWN + +/* */ +#undef LOG4CPLUS_HAVE_PIPE + +/* */ +#undef LOG4CPLUS_HAVE_PIPE2 + +/* */ +#undef LOG4CPLUS_HAVE_ACCEPT4 + +/* */ +#undef LOG4CPLUS_HAVE_POLL + +/* */ +#undef LOG4CPLUS_HAVE_POLL_H + +/* */ +#undef LOG4CPLUS_HAVE_STAT + +/* Define if this is a single-threaded library. */ +#undef LOG4CPLUS_SINGLE_THREADED + +/* */ +#undef LOG4CPLUS_USE_PTHREADS + +/* Define for compilers/standard libraries that support more than just the "C" + locale. */ +#undef LOG4CPLUS_WORKING_LOCALE + +/* Define for C99 compilers/standard libraries that support more than just the + "C" locale. */ +#undef LOG4CPLUS_WORKING_C_LOCALE + +/* Define to int if undefined. */ +#undef socklen_t + +/* Defined for --enable-debugging builds. */ +#undef LOG4CPLUS_DEBUGGING + +/* Defined if the compiler understands __declspec(dllexport) or + __attribute__((visibility("default"))) construct. */ +#undef LOG4CPLUS_DECLSPEC_EXPORT + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) construct. */ +#undef LOG4CPLUS_DECLSPEC_IMPORT + +/* Defined if the compiler understands + __attribute__((visibility("hidden"))) construct. */ +#undef LOG4CPLUS_DECLSPEC_PRIVATE + +/* */ +#undef LOG4CPLUS_HAVE_TLS_SUPPORT + +/* */ +#undef LOG4CPLUS_THREAD_LOCAL_VAR + +/* Defined if the host OS provides ENAMETOOLONG errno value. */ +#undef LOG4CPLUS_HAVE_ENAMETOOLONG + +/* */ +#undef LOG4CPLUS_HAVE_VSNPRINTF + +/* Define to 1 if you have the `vsnwprintf' function. */ +#undef LOG4CPLUS_HAVE_VSNWPRINTF + +/* Define to 1 if you have the `_vsnwprintf' function. */ +#undef LOG4CPLUS_HAVE__VSNWPRINTF + +/* */ +#undef LOG4CPLUS_HAVE__VSNPRINTF + +/* Define to 1 if you have the `vfprintf_s' function. */ +#undef LOG4CPLUS_HAVE_VFPRINTF_S + +/* Define to 1 if you have the `vfwprintf_s' function. */ +#undef LOG4CPLUS_HAVE_VFWPRINTF_S + +/* Define to 1 if you have the `vsprintf_s' function. */ +#undef LOG4CPLUS_HAVE_VSPRINTF_S + +/* Define to 1 if you have the `vswprintf_s' function. */ +#undef LOG4CPLUS_HAVE_VSWPRINTF_S + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +#undef LOG4CPLUS_HAVE__VSNPRINTF_S + +/* Define to 1 if you have the `_vsnwprintf_s' function. */ +#undef LOG4CPLUS_HAVE__VSNWPRINTF_S + +/* Defined if the compiler supports __FUNCTION__ macro. */ +#undef LOG4CPLUS_HAVE_FUNCTION_MACRO + +/* Defined if the compiler supports __PRETTY_FUNCTION__ macro. */ +#undef LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO + +/* Defined if the compiler supports __func__ symbol. */ +#undef LOG4CPLUS_HAVE_FUNC_SYMBOL + +/* Define to 1 if you have the `mbstowcs' function. */ +#undef LOG4CPLUS_HAVE_MBSTOWCS + +/* Define to 1 if you have the `wcstombs' function. */ +#undef LOG4CPLUS_HAVE_WCSTOMBS + +/* Define to 1 if you have Linux style syscall(SYS_gettid). */ +#undef LOG4CPLUS_HAVE_GETTID + +/* Define when iconv() is available. */ +#undef LOG4CPLUS_WITH_ICONV + +/* Define to 1 if you have the `iconv' function. */ +#undef LOG4CPLUS_HAVE_ICONV + +/* Define to 1 if you have the `iconv_close' function. */ +#undef LOG4CPLUS_HAVE_ICONV_CLOSE + +/* Define to 1 if you have the `iconv_open' function. */ +#undef LOG4CPLUS_HAVE_ICONV_OPEN + +/* Define to 1 if you have the `OutputDebugString' function. */ +#undef LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING + +/* Define to 1 if the system has the `constructor' function attribute + with priority */ +#undef LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY + +/* Define to 1 if the system has the `constructor' function attribute */ +#undef LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR + +/* Define to 1 if the system has the `init_priority' variable attribute */ +#undef LOG4CPLUS_HAVE_VAR_ATTRIBUTE_INIT_PRIORITY + +/* Defined to enable unit tests. */ +#undef LOG4CPLUS_WITH_UNIT_TESTS + +#endif // LOG4CPLUS_CONFIG_DEFINES_HXX diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config/macosx.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/macosx.h new file mode 100644 index 0000000..3f793c5 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/macosx.h @@ -0,0 +1,37 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: config-macosx.h +// Created: 7/2003 +// Author: Christopher R. Bailey +// +// +// Copyright 2003-2017 Christopher R. Bailey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @file */ + +#ifndef LOG4CPLUS_CONFIG_MACOSX_HEADER_ +#define LOG4CPLUS_CONFIG_MACOSX_HEADER_ + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if (defined(__APPLE__) || (defined(__MWERKS__) && defined(__MACOS__))) + +#define LOG4CPLUS_HAVE_GETTIMEOFDAY 1 +#define socklen_t int + +#endif // MACOSX +#endif // LOG4CPLUS_CONFIG_MACOSX_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config/win32.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/win32.h new file mode 100644 index 0000000..5d3a0b4 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/win32.h @@ -0,0 +1,194 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: config-win32.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_CONFIG_WIN32_HEADER_ +#define LOG4CPLUS_CONFIG_WIN32_HEADER_ + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (__MINGW32__) || defined (__MINGW64__) +# include <_mingw.h> +#endif + +#ifdef _WIN32 + +#if (defined (_MSC_VER) && _MSC_VER > 1400) \ + || (defined (__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 3) +# define LOG4CPLUS_HAVE_INTRIN_H +#endif + +// Time related functions and headers. +#define LOG4CPLUS_HAVE_TIME_H +#define LOG4CPLUS_HAVE_SYS_TIMEB_H +#define LOG4CPLUS_HAVE_FTIME +#if defined (_MSC_VER) || defined (__BORLANDC__) +#define LOG4CPLUS_HAVE_GMTIME_S +#endif + +// Use Winsock on Windows. +#define LOG4CPLUS_USE_WINSOCK + +// Enable Win32DebugAppender +#define LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING + +// Enable Win32ConsoleAppender. +#define LOG4CPLUS_HAVE_WIN32_CONSOLE + +#define LOG4CPLUS_HAVE_SYS_TYPES_H +#define LOG4CPLUS_HAVE_SYS_LOCKING_H +#define LOG4CPLUS_HAVE_FCNTL_H +#define LOG4CPLUS_HAVE_IO_H +#define LOG4CPLUS_HAVE_STDIO_H +#define LOG4CPLUS_HAVE_WCHAR_H +#define LOG4CPLUS_HAVE_STDARG_H +#define LOG4CPLUS_HAVE_STDLIB_H +#define LOG4CPLUS_HAVE_ERRNO_H +#define LOG4CPLUS_HAVE_SYS_STAT_H +#define LOG4CPLUS_HAVE_TIME_H +#define LOG4CPLUS_HAVE_STDLIB_H +#define LOG4CPLUS_HAVE_DIRECT_H + +// MSVC has both and so does MinGW. +#define LOG4CPLUS_HAVE_VSNPRINTF +#define LOG4CPLUS_HAVE__VSNPRINTF +#define LOG4CPLUS_HAVE__VSNWPRINTF + +// Limit the use of foo_s() functions to builds using Visual Studio +// 2005 and its run time library. In MinGW land, limit the foo_s() +// functions to MinGw-w64 toolchain and __MSVCRT_VERSION__ >= 0x0900. +#if (defined (_MSC_VER) && _MSC_VER >= 1400) \ + || (defined (__MSVCRT_VERSION__) && __MSVCRT_VERSION__ >= 0x0900 \ + && defined (__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 2) +// MS secure versions of vprintf(). +# define LOG4CPLUS_HAVE_VSPRINTF_S +# define LOG4CPLUS_HAVE_VSWPRINTF_S + +// MS secure versions of vfprintf(). +# define LOG4CPLUS_HAVE_VFPRINTF_S +# define LOG4CPLUS_HAVE_VFWPRINTF_S + +// MS secure versions of vsnprintf(). +# define LOG4CPLUS_HAVE_VSNPRINTF_S +# define LOG4CPLUS_HAVE__VSNPRINTF_S +# define LOG4CPLUS_HAVE__VSNWPRINTF_S + +// MS secure version of _tsopen(). +# define LOG4CPLUS_HAVE__TSOPEN_S +#endif + +#if defined (_MSC_VER) && _MSC_VER >= 1400 +// MS printf-like functions supporting positional parameters. +# define LOG4CPLUS_HAVE__VSPRINTF_P +# define LOG4CPLUS_HAVE__VSWPRINTF_P +#endif + +#if defined (_MSC_VER) +# define LOG4CPLUS_HAVE_LOCALTIME_S +#endif + +#define LOG4CPLUS_HAVE__TSOPEN + +#define LOG4CPLUS_DLLMAIN_HINSTANCE HINSTANCE +#define LOG4CPLUS_HAVE_NT_EVENT_LOG + +// log4cplus_EXPORTS is used by the CMake build system. DLL_EXPORT is +// used by the autotools build system. +#if (defined (log4cplus_EXPORTS) || defined (log4cplusU_EXPORTS) \ + || (defined (DLL_EXPORT) && defined (INSIDE_LOG4CPLUS))) \ + && ! defined (LOG4CPLUS_STATIC) +# undef LOG4CPLUS_BUILD_DLL +# define LOG4CPLUS_BUILD_DLL +#endif + +#if ! defined (LOG4CPLUS_BUILD_DLL) +# undef LOG4CPLUS_STATIC +# define LOG4CPLUS_STATIC +#endif + +#if defined (LOG4CPLUS_STATIC) && defined (LOG4CPLUS_BUILD_DLL) +# error LOG4CPLUS_STATIC and LOG4CPLUS_BUILD_DLL cannot be defined both. +#endif + +#if defined (LOG4CPLUS_BUILD_DLL) +# if defined (INSIDE_LOG4CPLUS) +# define LOG4CPLUS_EXPORT __declspec(dllexport) +# else +# define LOG4CPLUS_EXPORT __declspec(dllimport) +# endif +#else +# define LOG4CPLUS_EXPORT +#endif + +#ifndef LOG4CPLUS_SINGLE_THREADED +# define LOG4CPLUS_USE_WIN32_THREADS +#endif + +#if defined(_MSC_VER) + // Warning about: identifier was truncated to '255' characters in the debug information +# pragma warning( disable : 4786 ) + // Warning about: needs to have dll-interface to be used by clients of class +# pragma warning( disable : 4251 ) + +# define LOG4CPLUS_INLINES_ARE_EXPORTED + +# if _MSC_VER >= 1400 +# define LOG4CPLUS_WORKING_LOCALE +# define LOG4CPLUS_HAVE_FUNCTION_MACRO +# define LOG4CPLUS_HAVE_FUNCSIG_MACRO +# define LOG4CPLUS_ATTRIBUTE_NORETURN __declspec(noreturn) +# endif +#endif + +#if defined (__GNUC__) +# undef LOG4CPLUS_INLINES_ARE_EXPORTED +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO +# define LOG4CPLUS_HAVE_FUNC_SYMBOL +# endif +// This has worked for some versions of MinGW with GCC 4.7+ but it +// appears to be broken again in 4.8.x. Thus, we disable this for GCC +// completely forever. +// +//# define LOG4CPLUS_INLINES_ARE_EXPORTED +# define LOG4CPLUS_HAVE_FUNCTION_MACRO +# if defined (__MINGW32__) +# define LOG4CPLUS_WORKING_C_LOCALE +# endif +#endif + +#if defined (__BORLANDC__) && __BORLANDC__ >= 0x0650 +# define LOG4CPLUS_HAVE_FUNCTION_MACRO +#endif // __BORLANDC__ + +#if ! defined (LOG4CPLUS_DISABLE_DLL_RUNTIME_WARNING) +# if defined (LOG4CPLUS_STATIC) && defined (_MSC_VER) && ! defined (_DLL) +# pragma message("You are not using DLL C run time library. " \ + "You must call log4cplus::initialize() once before " \ + "you use any other log4cplus API.") +# endif +#endif + +#endif // _WIN32 +#endif // LOG4CPLUS_CONFIG_WIN32_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc-full.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc-full.h new file mode 100644 index 0000000..bff5925 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc-full.h @@ -0,0 +1,42 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: windowsh-inc.h +// Created: 9/2018 +// Author: Vaclav Haisman +// +// +// Copyright (C) 2018, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +// NOTE: This file is a fragment intentionally left without include guards. + +#if defined (_WIN32) +#include +#include +#include +#if defined (LOG4CPLUS_HAVE_INTRIN_H) +#include +#endif +#endif + +// NOTE: This file is a fragment intentionally left without include guards. diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc.h new file mode 100644 index 0000000..0ed1c50 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/config/windowsh-inc.h @@ -0,0 +1,159 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: windowsh-inc.h +// Created: 4/2010 +// Author: Vaclav Zeman +// +// +// Copyright (C) 2010-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +// NOTE: This file is a fragment intentionally left without include guards. + +#if defined (_WIN32) +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN + +#undef NOGDICAPMASKS +#define NOGDICAPMASKS + +#undef NOVIRTUALKEYCODES +#define NOVIRTUALKEYCODES + +#undef NOWINMESSAGES +#define NOWINMESSAGES + +#undef NOWINSTYLES +#define NOWINSTYLES + +#undef NOSYSMETRICS +#define NOSYSMETRICS + +#undef NOMENUS +#define NOMENUS + +#undef NOICONS +#define NOICONS + +#undef NOKEYSTATES +#define NOKEYSTATES + +#undef NOSYSCOMMANDS +#define NOSYSCOMMANDS + +#undef NORASTEROPS +#define NORASTEROPS + +#undef NOSHOWWINDOW +#define NOSHOWWINDOW + +#undef NOATOM +#define NOATOM + +#undef NOCLIPBOARD +#define NOCLIPBOARD + +#undef NOCOLOR +#define NOCOLOR + +#undef NOCTLMGR +#define NOCTLMGR + +#undef NODRAWTEXT +#define NODRAWTEXT + +#undef NOGDI +#define NOGDI + +#undef NOKERNEL +#define NOKERNEL + +#undef NOUSER +#define NOUSER + +#undef NONLS +#define NONLS + +#undef NOMB +#define NOMB + +#undef NOMEMMGR +#define NOMEMMGR + +#undef NOMETAFILE +#define NOMETAFILE + +#undef NOMINMAX +#define NOMINMAX + +#undef NOMSG +#define NOMSG + +#undef NOOPENFILE +#define NOOPENFILE + +#undef NOSCROLL +#define NOSCROLL + +#undef NOSERVICE +#define NOSERVICE + +#undef NOSOUND +#define NOSOUND + +#undef NOTEXTMETRIC +#define NOTEXTMETRIC + +#undef NOWH +#define NOWH + +#undef NOWINOFFSETS +#define NOWINOFFSETS + +#undef NOCOMM +#define NOCOMM + +#undef NOKANJI +#define NOKANJI + +#undef NOHELP +#define NOHELP + +#undef NOPROFILER +#define NOPROFILER + +#undef NODEFERWINDOWPOS +#define NODEFERWINDOWPOS + +#undef NOMCX +#define NOMCX + +#include +#include +#include +#if defined (LOG4CPLUS_HAVE_INTRIN_H) +#include +#endif +#endif + +// NOTE: This file is a fragment intentionally left without include guards. diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/configurator.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/configurator.h new file mode 100644 index 0000000..0e8b226 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/configurator.h @@ -0,0 +1,378 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: configurator.h +// Created: 3/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_CONFIGURATOR_HEADER_ +#define LOG4CPLUS_CONFIGURATOR_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +#include + + +namespace log4cplus +{ + class Hierarchy; + + + /** + * Provides configuration from an external file. See configure() for + * the expected format. + * + * All option values admit variable substitution. For + * example, if userhome environment property is set to + * /home/xyz and the File option is set to the string + * ${userhome}/test.log, then File option will be + * interpreted as the string /home/xyz/test.log. + * + * The syntax of variable substitution is similar to that of UNIX + * shells. The string between an opening "${" and + * closing "}" is interpreted as a key. Its value is + * searched in the environment properties. The corresponding value replaces + * the ${variableName} sequence. + * + * Configuration files also recognize include + * file.properties directive that allow composing + * configuration from multiple files. There is no cyclic includes + * detection mechanism to stop unbound recursion. + */ + class LOG4CPLUS_EXPORT PropertyConfigurator + { + public: + enum PCFlags + { + fRecursiveExpansion = (1 << 0) + , fShadowEnvironment = (1 << 1) + , fAllowEmptyVars = (1 << 2) + + // These encoding related options occupy 2 bits of the flags + // and are mutually exclusive. These flags are synchronized with + // PFlags in Properties. + + , fEncodingShift = 3 + , fEncodingMask = 0x3 + , fUnspecEncoding = (0 << fEncodingShift) +#if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) && defined (UNICODE) + , fUTF8 = (1 << fEncodingShift) +#endif +#if (defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) || defined (_WIN32)) \ + && defined (UNICODE) + , fUTF16 = (2 << fEncodingShift) +#endif +#if defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET) && defined (UNICODE) + , fUTF32 = (3 << fEncodingShift) +#endif + }; + + // ctor and dtor + PropertyConfigurator(const log4cplus::tstring& propertyFile, + Hierarchy& h = Logger::getDefaultHierarchy(), unsigned flags = 0); + PropertyConfigurator(const log4cplus::helpers::Properties& props, + Hierarchy& h = Logger::getDefaultHierarchy(), unsigned flags = 0); + PropertyConfigurator(log4cplus::tistream& propertyStream, + Hierarchy& h = Logger::getDefaultHierarchy(), unsigned flags = 0); + virtual ~PropertyConfigurator(); + + /** + * This method eliminates the need to create a temporary + * PropertyConfigurator to configure log4cplus. + * It is equivalent to the following:
+ * + * PropertyConfigurator config("filename"); + * config.configure(); + * + */ + static void doConfigure(const log4cplus::tstring& configFilename, + Hierarchy& h = Logger::getDefaultHierarchy(), unsigned flags = 0); + + /** + * Read configuration from a file. The existing configuration is + * not cleared nor reset. If you require a different behavior, + * then call {@link Hierarchy::resetConfiguration + * resetConfiguration} method before calling + * doConfigure. + * + * The configuration file consists of statements in the format + * key=value. The syntax of different configuration + * elements are discussed below. + * + *

Appender configuration

+ * + * Appender configuration syntax is: + *
+         * # For appender named appenderName, set its class.
+         * # Note: The appender name can contain dots.
+         * log4cplus.appender.appenderName=fully.qualified.name.of.appender.class
+         *
+         * # Set appender specific options.
+         * log4cplus.appender.appenderName.option1=value1
+         * ...
+         * log4cplus.appender.appenderName.optionN=valueN
+         * 
+ * + * For each named appender you can configure its {@link Layout}. The + * syntax for configuring an appender's layout is: + *
+         * log4cplus.appender.appenderName.layout=fully.qualified.name.of.layout.class
+         * log4cplus.appender.appenderName.layout.option1=value1
+         * ....
+         * log4cplus.appender.appenderName.layout.optionN=valueN
+         * 
+ * + *

Configuring loggers

+ * + * The syntax for configuring the root logger is: + *
+         * log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
+         * 
+ * + * This syntax means that an optional LogLevel value can + * be supplied followed by appender names separated by commas. + * + * The LogLevel value can consist of the string values FATAL, + * ERROR, WARN, INFO, DEBUG or a custom LogLevel value. + * + * If a LogLevel value is specified, then the root LogLevel is set + * to the corresponding LogLevel. If no LogLevel value is specified, + * then the root LogLevel remains untouched. + * + * The root logger can be assigned multiple appenders. + * + * Each appenderName (separated by commas) will be added to + * the root logger. The named appender is defined using the + * appender syntax defined above. + * + * For non-root loggers the syntax is almost the same: + *
+         * log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
+         * 
+ * + * The meaning of the optional LogLevel value is discussed above + * in relation to the root logger. In addition however, the value + * INHERITED can be specified meaning that the named logger should + * inherit its LogLevel from the logger hierarchy. + * + * By default loggers inherit their LogLevel from the + * hierarchy. However, if you set the LogLevel of a logger and + * later decide that that logger should inherit its LogLevel, then + * you should specify INHERITED as the value for the LogLevel value. + * + * Similar to the root logger syntax, each appenderName + * (separated by commas) will be attached to the named logger. + * + * See the appender + * additivity rule in the user manual for the meaning of the + * additivity flag. + * + * The user can override any of the {@link + * Hierarchy#disable} family of methods by setting the a key + * "log4cplus.disableOverride" to true or any value other + * than false. As in
log4cplus.disableOverride=true 
+ * + *

Global configuration

+ * + * Property
log4cplus.threadPoolSize
can be used to adjust + * size of log4cplus' internal thread pool. + * + *

Example

+ * + * An example configuration is given below. + * + *
+         *
+         * # Set options for appender named "A1".
+         * # Appender "A1" will be a SyslogAppender
+         * log4cplus.appender.A1=log4cplus::SyslogAppender
+         *
+         * # The syslog daemon resides on www.abc.net
+         * log4cplus.appender.A1.SyslogHost=www.abc.net
+         *
+         * # A1's layout is a PatternLayout, using the conversion pattern
+         * # %r %-5p %c{2} %M.%L %x - %m\n. Thus, the log output will
+         * # include # the relative time since the start of the application in
+         * # milliseconds, followed by the LogLevel of the log request,
+         * # followed by the two rightmost components of the logger name,
+         * # followed by the callers method name, followed by the line number,
+         * # the nested disgnostic context and finally the message itself.
+         * # Refer to the documentation of {@link PatternLayout} for further information
+         * # on the syntax of the ConversionPattern key.
+         * log4cplus.appender.A1.layout=log4cplus::PatternLayout
+         * log4cplus.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
+         *
+         * # Set options for appender named "A2"
+         * # A2 should be a RollingFileAppender, with maximum file size of 10 MB
+         * # using at most one backup file. A2's layout is TTCC, using the
+         * # ISO8061 date format with context printing enabled.
+         * log4cplus.appender.A2=log4cplus::RollingFileAppender
+         * log4cplus.appender.A2.MaxFileSize=10MB
+         * log4cplus.appender.A2.MaxBackupIndex=1
+         * log4cplus.appender.A2.layout=log4cplus::TTCCLayout
+         * log4cplus.appender.A2.layout.ContextPrinting=true
+         * log4cplus.appender.A2.layout.DateFormat=ISO8601
+         *
+         * # Root logger set to DEBUG using the A2 appender defined above.
+         * log4cplus.rootLogger=DEBUG, A2
+         *
+         * # Logger definitions:
+         * # The SECURITY logger inherits is LogLevel from root. However, it's output
+         * # will go to A1 appender defined above. It's additivity is non-cumulative.
+         * log4cplus.logger.SECURITY=INHERIT, A1
+         * log4cplus.additivity.SECURITY=false
+         *
+         * # Only warnings or above will be logged for the logger "SECURITY.access".
+         * # Output will go to A1.
+         * log4cplus.logger.SECURITY.access=WARN
+         *
+         *
+         * # The logger "class.of.the.day" inherits its LogLevel from the
+         * # logger hierarchy.  Output will go to the appender's of the root
+         * # logger, A2 in this case.
+         * log4cplus.logger.class.of.the.day=INHERIT
+         * 
+ * + * Refer to the setOption method in each Appender and + * Layout for class specific options. + * + * Use the # character at the beginning of a line + * for comments. + */ + virtual void configure(); + + /** + * \return The return value is reference to Properties + * container of properties with the "log4cplus." + * prefix removed and references to other properties and/or + * environment variables expanded. + */ + log4cplus::helpers::Properties const & getProperties () const; + + /** + * \return The return value is a reference to log4cplus::tstring + * containing filename of properties source file. It will be + * string "UNAVAILABLE" if the PropertyConfigurator instance has been + * constructed using one of the other constructors that do not take + * filename as parameter. + */ + log4cplus::tstring const & getPropertyFilename () const; + + protected: + // Methods + void init(); // called by the ctor + void reconfigure(); + void replaceEnvironVariables(); + void configureLoggers(); + void configureLogger(log4cplus::Logger logger, const log4cplus::tstring& config); + void configureAppenders(); + void configureAdditivity(); + + virtual Logger getLogger(const log4cplus::tstring& name); + virtual void addAppender(Logger &logger, log4cplus::SharedAppenderPtr& appender); + + // Types + typedef std::map AppenderMap; + + // Data + Hierarchy& h; + log4cplus::tstring propertyFilename; + log4cplus::helpers::Properties properties; + AppenderMap appenders; + unsigned flags; + + private: + // Disable copy + PropertyConfigurator(const PropertyConfigurator&); + PropertyConfigurator& operator=(PropertyConfigurator&); + }; + + + + /** + * Use this class to quickly configure the package. For file based + * configuration see PropertyConfigurator. BasicConfigurator + * automatically attaches ConsoleAppender to + * rootLogger, with output going to standard output, + * using DEBUG LogLevel value. The additional parameter + * logToStdErr may redirect the output to standard error. + */ + class LOG4CPLUS_EXPORT BasicConfigurator : public PropertyConfigurator { + public: + // ctor and dtor + BasicConfigurator(Hierarchy& h = Logger::getDefaultHierarchy(), + bool logToStdErr = false); + virtual ~BasicConfigurator(); + + /** + * This method eliminates the need to create a temporary + * BasicConfigurator object to configure log4cplus. + * It is equivalent to the following:
+ *
+         * BasicConfigurator config;
+         * config.configure();
+         * 
+ */ + static void doConfigure(Hierarchy& h = Logger::getDefaultHierarchy(), + bool logToStdErr = false); + + //! Property name for disable override. + static log4cplus::tstring const DISABLE_OVERRIDE_KEY; + + private: + // Disable copy + BasicConfigurator(const BasicConfigurator&); + BasicConfigurator& operator=(BasicConfigurator&); + }; + + +#if !defined(LOG4CPLUS_SINGLE_THREADED) + // Forward Declarations + class ConfigurationWatchDogThread; + + + class LOG4CPLUS_EXPORT ConfigureAndWatchThread { + public: + // ctor and dtor + ConfigureAndWatchThread(const log4cplus::tstring& propertyFile, + unsigned int millis = 60 * 1000); + virtual ~ConfigureAndWatchThread(); + + private: + // Disallow copying of instances of this class + ConfigureAndWatchThread(const ConfigureAndWatchThread&); + ConfigureAndWatchThread& operator=(const ConfigureAndWatchThread&); + + // Data + ConfigurationWatchDogThread * watchDogThread; + }; +#endif + +} // end namespace log4cplus + +#endif // LOG4CPLUS_CONFIGURATOR_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/consoleappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/consoleappender.h new file mode 100644 index 0000000..dfbcd24 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/consoleappender.h @@ -0,0 +1,85 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: consoleappender.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_CONSOLE_APPENDER_HEADER_ +#define LOG4CPLUS_CONSOLE_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +namespace log4cplus { + /** + * ConsoleAppender appends log events to std::cout or + * std::cerr using a layout specified by the + * user. The default target is std::cout. + * + *

Properties

+ *
+ *
logToStdErr
+ *
When it is set true, the output stream will be + * std::cerr instead of std::cout.
+ * + *
ImmediateFlush
+ *
When it is set true, output stream will be flushed after + * each appended event.
+ * + *
+ * \sa Appender + */ + class LOG4CPLUS_EXPORT ConsoleAppender : public Appender { + public: + // Ctors + ConsoleAppender(bool logToStdErr = false, bool immediateFlush = false); + ConsoleAppender(const log4cplus::helpers::Properties & properties); + + // Dtor + ~ConsoleAppender(); + + // Methods + virtual void close(); + + //! This mutex is used by ConsoleAppender and helpers::LogLog + //! classes to synchronize output to console. + static log4cplus::thread::Mutex const & getOutputMutex(); + + protected: + virtual void append(const spi::InternalLoggingEvent& event); + + // Data + bool logToStdErr; + /** + * Immediate flush means that the underlying output stream + * will be flushed at the end of each append operation. + */ + bool immediateFlush; + }; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_CONSOLE_APPENDER_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/fileappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/fileappender.h new file mode 100644 index 0000000..36f6d35 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/fileappender.h @@ -0,0 +1,428 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: fileappender.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_FILE_APPENDER_HEADER_ +#define LOG4CPLUS_FILE_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + + +namespace log4cplus +{ + + /** + * Base class for Appenders writing log events to a file. + * It is constructed with uninitialized file object, so all + * classes derived from FileAppenderBase _must_ call init() method. + * + *

Properties

+ *
+ *
File
+ *
This property specifies output file name.
+ * + *
ImmediateFlush
+ *
When it is set true, output stream will be flushed after + * each appended event.
+ * + *
Append
+ *
When it is set true, output file will be appended to + * instead of being truncated at opening.
+ * + *
ReopenDelay
+ *
This property sets a delay after which the appender will try + * to reopen log file again, after last logging failure. + *
+ * + *
BufferSize
+ *
Non-zero value of this property sets up buffering of output + * stream using a buffer of given size. + *
+ * + *
UseLockFile
+ *
Set this property to true if you want your output + * to go into a log file shared by multiple processes. When this + * property is set to true then log4cplus uses OS specific + * facilities (e.g., lockf()) to provide + * inter-process file locking. + * \sa Appender + *
+ * + *
LockFile
+ *
This property specifies lock file, file used for + * inter-process synchronization of log file access. When this + * property is not specified, the value is derived from + * File property by addition of ".lock" suffix. The + * property is only used when UseLockFile is set to true. + * \sa Appender + *
+ * + *
Locale
+ *
This property specifies a locale name that will be imbued + * into output stream. Locale can be specified either by system + * specific locale name, e.g., en_US.UTF-8, or by one of + * four recognized keywords: GLOBAL, DEFAULT + * (which is an alias for GLOBAL), USER and + * CLASSIC. When specified locale is not available, + * GLOBAL is used instead. It is possible to register + * additional locale keywords by registering an instance of + * spi::LocaleFactory in + * spi::LocaleFactoryRegistry. + * \sa spi::getLocaleFactoryRegistry() + *
+ * + *
CreateDirs
+ *
Set this property to true if you want to create + * missing directories in path leading to log file and lock file. + *
+ * + *
TextMode
+ *
Set this property to Binary if the underlying stream should + * not translate EOLs to OS specific character sequence. The default value + * is Text and the underlying stream will be opened in text + * mode.
+ *
+ */ + class LOG4CPLUS_EXPORT FileAppenderBase : public Appender { + public: + // Methods + virtual void close(); + + //! Redefine default locale for output stream. It may be a good idea to + //! provide UTF-8 locale in case UNICODE macro is defined. + virtual std::locale imbue(std::locale const& loc); + + //! \returns Locale imbued in fstream. + virtual std::locale getloc () const; + + protected: + // Ctors + FileAppenderBase(const log4cplus::tstring& filename, + std::ios_base::openmode mode = std::ios_base::trunc, + bool immediateFlush = true, + bool createDirs = false); + FileAppenderBase(const log4cplus::helpers::Properties& properties, + std::ios_base::openmode mode = std::ios_base::trunc); + + void init(); + + virtual void append(const spi::InternalLoggingEvent& event); + + virtual void open(std::ios_base::openmode mode); + bool reopen(); + + // Data + /** + * Immediate flush means that the underlying writer or output stream + * will be flushed at the end of each append operation. Immediate + * flush is slower but ensures that each append request is actually + * written. If immediateFlush is set to + * false, then there is a good chance that the last few + * logs events are not actually written to persistent media if and + * when the application crashes. + * + * The immediateFlush variable is set to + * true by default. + */ + bool immediateFlush; + + /** + * When this variable is true, FileAppender will try to create + * missing directories in path leading to log file. + * + * The `createDirs` variable is set to `false` by default. + */ + bool createDirs; + + /** + * When any append operation fails, reopenDelay says + * for how many seconds the next attempt to re-open the log file and + * resume logging will be delayed. If reopenDelay is zero, + * each failed append operation will cause log file to be re-opened. + * By default, reopenDelay is 1 second. + */ + int reopenDelay; + + unsigned long bufferSize; + std::unique_ptr buffer; + + log4cplus::tofstream out; + log4cplus::tstring filename; + log4cplus::tstring localeName; + log4cplus::tstring lockFileName; + std::ios_base::openmode fileOpenMode; + + log4cplus::helpers::Time reopen_time; + + private: + // Disallow copying of instances of this class + FileAppenderBase(const FileAppenderBase&); + FileAppenderBase& operator=(const FileAppenderBase&); + }; + + + /** + * Appends log events to a file. + * + *

Properties

+ *

It has no properties additional to {@link FileAppenderBase}. + */ + class LOG4CPLUS_EXPORT FileAppender : public FileAppenderBase { + public: + // Ctors + FileAppender(const log4cplus::tstring& filename, + std::ios_base::openmode mode = std::ios_base::trunc, + bool immediateFlush = true, + bool createDirs = false); + FileAppender(const log4cplus::helpers::Properties& properties, + std::ios_base::openmode mode = std::ios_base::trunc); + + // Dtor + virtual ~FileAppender(); + + protected: + void init(); + }; + + typedef helpers::SharedObjectPtr SharedFileAppenderPtr; + + + + /** + * RollingFileAppender extends FileAppender to backup the log + * files when they reach a certain size. + * + *

Properties

+ *

Properties additional to {@link FileAppender}'s properties: + * + *

+ *
MaxFileSize
+ *
This property specifies maximal size of output file. The + * value is in bytes. It is possible to use MB and + * KB suffixes to specify the value in megabytes or + * kilobytes instead.
+ * + *
MaxBackupIndex
+ *
This property limits the number of backup output + * files; e.g. how many log.1, log.2 etc. files + * will be kept.
+ *
+ */ + class LOG4CPLUS_EXPORT RollingFileAppender : public FileAppender { + public: + // Ctors + RollingFileAppender(const log4cplus::tstring& filename, + long maxFileSize = 10*1024*1024, // 10 MB + int maxBackupIndex = 1, + bool immediateFlush = true, + bool createDirs = false); + RollingFileAppender(const log4cplus::helpers::Properties& properties); + + // Dtor + virtual ~RollingFileAppender(); + + protected: + virtual void append(const spi::InternalLoggingEvent& event); + void rollover(bool alreadyLocked = false); + + // Data + long maxFileSize; + int maxBackupIndex; + + private: + LOG4CPLUS_PRIVATE void init(long maxFileSize, int maxBackupIndex); + }; + + + typedef helpers::SharedObjectPtr + SharedRollingFileAppenderPtr; + + + enum DailyRollingFileSchedule { MONTHLY, WEEKLY, DAILY, + TWICE_DAILY, HOURLY, MINUTELY}; + + /** + * DailyRollingFileAppender extends {@link FileAppender} so that the + * underlying file is rolled over at a user chosen frequency. + * + *

Properties

+ *

Properties additional to {@link FileAppender}'s properties: + * + *

+ *
Schedule
+ *
This property specifies rollover schedule. The possible + * values are MONTHLY, WEEKLY, DAILY, + * TWICE_DAILY, HOURLY and + * MINUTELY.
+ * + *
MaxBackupIndex
+ *
This property limits how many backup files are kept per + * single logging period; e.g. how many log.2009-11-07.1, + * log.2009-11-07.2 etc. files are kept.
+ * + *
RollOnClose
+ *
This property specifies whether to rollover log files upon + * shutdown. By default it's set to true to retain compatibility + * with legacy code, however it may lead to undesired behaviour + * as described in the github issue #120.
+ * + *
DatePattern
+ *
This property specifies filename suffix pattern to use for + * periodical backups of the logfile. The patern should be in + * format supported by {@link log4cplus::helpers::Time::getFormatterTime()}. + * Please notice that the format of the pattern is similar but not identical + * to the one used for this option in the corresponding Log4J class. + * If the property isn't specified a reasonable default for a given + * schedule type is used.
+ * + *
+ */ + class LOG4CPLUS_EXPORT DailyRollingFileAppender : public FileAppender { + public: + // Ctors + DailyRollingFileAppender(const log4cplus::tstring& filename, + DailyRollingFileSchedule schedule = DAILY, + bool immediateFlush = true, + int maxBackupIndex = 10, + bool createDirs = false, + bool rollOnClose = true, + const log4cplus::tstring& datePattern = log4cplus::tstring()); + DailyRollingFileAppender(const log4cplus::helpers::Properties& properties); + + // Dtor + virtual ~DailyRollingFileAppender(); + + // Methods + virtual void close(); + + protected: + virtual void append(const spi::InternalLoggingEvent& event); + void rollover(bool alreadyLocked = false); + log4cplus::helpers::Time calculateNextRolloverTime(const log4cplus::helpers::Time& t) const; + log4cplus::tstring getFilename(const log4cplus::helpers::Time& t) const; + + // Data + DailyRollingFileSchedule schedule; + log4cplus::tstring scheduledFilename; + log4cplus::helpers::Time nextRolloverTime; + int maxBackupIndex; + bool rollOnClose; + log4cplus::tstring datePattern; + + private: + LOG4CPLUS_PRIVATE void init(DailyRollingFileSchedule schedule); + }; + + typedef helpers::SharedObjectPtr + SharedDailyRollingFileAppenderPtr; + + + /** + * TimeBasedRollingFileAppender extends {@link FileAppenderBase} so that + * the underlying file is rolled over at a user chosen frequency while also + * keeping in check a total maximum number of produced files. + * + *

Properties

+ *

Properties additional to {@link FileAppenderBase}'s properties: + * + *

+ * + *
FilenamePattern
+ *
The mandatory fileNamePattern property defines the name of the + * rolled-over (archived) log files. Its value should consist of the name + * of the file, plus a suitably placed %d conversion specifier. The %d + * conversion specifier may contain a date-and-time pattern as specified by + * the java's SimpleDateFormat. The rollover period is inferred from the + * value of fileNamePattern.
+ * + *
MaxHistory
+ *
The optional maxHistory property controls the maximum number of + * archive files to keep, deleting older files.
+ * + *
CleanHistoryOnStart
+ *
If set to true, archive removal will be executed on appender start + * up. By default this property is set to false.
+ * + *
RollOnClose
+ *
This property specifies whether to rollover log files upon + * shutdown. By default it's set to true to retain compatibility + * with legacy code, however it may lead to undesired behaviour + * as described in the github issue #120.
+ * + *
+ */ + class LOG4CPLUS_EXPORT TimeBasedRollingFileAppender : public FileAppenderBase { + public: + // Ctors + TimeBasedRollingFileAppender(const tstring& filename = LOG4CPLUS_TEXT(""), + const tstring& filenamePattern = LOG4CPLUS_TEXT("%d.log"), + int maxHistory = 10, + bool cleanHistoryOnStart = false, + bool immediateFlush = true, + bool createDirs = false, + bool rollOnClose = true); + TimeBasedRollingFileAppender(const helpers::Properties& properties); + + // Dtor + ~TimeBasedRollingFileAppender(); + + protected: + void append(const spi::InternalLoggingEvent& event); + void open(std::ios_base::openmode mode); + void close(); + void rollover(bool alreadyLocked = false); + void clean(helpers::Time time); + helpers::Time::duration getRolloverPeriodDuration() const; + helpers::Time calculateNextRolloverTime(const helpers::Time& t) const; + + // Data + tstring filenamePattern; + DailyRollingFileSchedule schedule; + tstring scheduledFilename; + int maxHistory; + bool cleanHistoryOnStart; + log4cplus::helpers::Time lastHeartBeat; + log4cplus::helpers::Time nextRolloverTime; + bool rollOnClose; + + private: + LOG4CPLUS_PRIVATE void init(); + }; + + typedef helpers::SharedObjectPtr + SharedTimeBasedRollingFileAppenderPtr; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_FILE_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/fstreams.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/fstreams.h new file mode 100644 index 0000000..a7dcd66 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/fstreams.h @@ -0,0 +1,56 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: fstreams.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_FSTREAMS_HEADER_ +#define LOG4CPLUS_FSTREAMS_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + + +namespace log4cplus +{ + + +typedef std::basic_ofstream tofstream; +typedef std::basic_ifstream tifstream; + +//! \def LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(X) +//! \brief Expands into expression that picks the right type for +//! std::fstream file name parameter. +#if defined (LOG4CPLUS_FSTREAM_ACCEPTS_WCHAR_T) && defined (UNICODE) +# define LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(X) (X) +#else +# define LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(X) (LOG4CPLUS_TSTRING_TO_STRING(X)) +#endif + + +} + +#endif // LOG4CPLUS_FSTREAMS_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/appenderattachableimpl.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/appenderattachableimpl.h new file mode 100644 index 0000000..52ed096 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/appenderattachableimpl.h @@ -0,0 +1,119 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: appenderattachableimpl.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_APPENDER_ATTACHABLE_IMPL_HEADER_ +#define LOG4CPLUS_HELPERS_APPENDER_ATTACHABLE_IMPL_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +#include +#include + + +namespace log4cplus { + namespace helpers { + + /** + * This Interface is for attaching Appenders to objects. + */ + class LOG4CPLUS_EXPORT AppenderAttachableImpl + : public log4cplus::spi::AppenderAttachable + { + public: + // Data + thread::Mutex appender_list_mutex; + + // Ctors + AppenderAttachableImpl(); + + // Dtor + virtual ~AppenderAttachableImpl(); + + // Methods + /** + * Add an appender. If the appender is already in the list in + * won't be added again. + */ + virtual void addAppender(SharedAppenderPtr newAppender); + + /** + * Get all previously added appenders as an vectory. + */ + virtual SharedAppenderPtrList getAllAppenders(); + + /** + * Look for an attached appender named as name. + * + * Return the appender with that name if in the list. Return null + * otherwise. + */ + virtual SharedAppenderPtr getAppender(const log4cplus::tstring& name); + + /** + * Remove all previously added appenders. + */ + virtual void removeAllAppenders(); + + /** + * Remove the appender passed as parameter from the list of appenders. + */ + virtual void removeAppender(SharedAppenderPtr appender); + + /** + * Remove the appender with the name passed as parameter from the + * list of appenders. + */ + virtual void removeAppender(const log4cplus::tstring& name); + + /** + * Call the doAppend method on all attached appenders. + */ + int appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const; + + protected: + // Types + typedef std::vector ListType; + + // Data + /** Array of appenders. */ + ListType appenderList; + + private: + AppenderAttachableImpl(AppenderAttachableImpl const &); + AppenderAttachableImpl & operator = (AppenderAttachableImpl const &); + }; // end class AppenderAttachableImpl + + } // end namespace helpers +} // end namespace log4cplus + +#endif // LOG4CPLUS_HELPERS_APPENDER_ATTACHABLE_IMPL_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/connectorthread.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/connectorthread.h new file mode 100644 index 0000000..6df3ce5 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/connectorthread.h @@ -0,0 +1,107 @@ +// -*- C++ -*- +// Copyright (C) 2013-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_HELPERS_CONNECTORTHREAD_H +#define LOG4CPLUS_HELPERS_CONNECTORTHREAD_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + +namespace log4cplus { namespace helpers { + + +class LOG4CPLUS_EXPORT ConnectorThread; + +//! Interface implemented by users of ConnectorThread. +class LOG4CPLUS_EXPORT IConnectorThreadClient +{ +protected: + virtual ~IConnectorThreadClient (); + + //! \return Mutex for synchronization between ConnectorThread and + //! its client object. This is usually SharedObject::access_mutex. + virtual thread::Mutex const & ctcGetAccessMutex () const = 0; + + //! \return Socket variable in ConnectorThread client to maintain. + virtual helpers::Socket & ctcGetSocket () = 0; + + //! \return ConnectorThread client's function returning connected + //! socket. + virtual helpers::Socket ctcConnect () = 0; + + //! Sets connected flag to true in ConnectorThread's client. + virtual void ctcSetConnected () = 0; + + friend class LOG4CPLUS_EXPORT ConnectorThread; +}; + + +//! This class is used by SocketAppender and (remote) SysLogAppender +//! to provide asynchronous re-connection. +class LOG4CPLUS_EXPORT ConnectorThread + : public thread::AbstractThread +{ +public: + //! \param client reference to ConnectorThread's client object + ConnectorThread (IConnectorThreadClient & client); + virtual ~ConnectorThread (); + + virtual void run(); + + //! Call this function to terminate ConnectorThread. The function + //! sets `exit_flag` and then triggers `trigger_ev` to wake up the + //! ConnectorThread. + void terminate (); + + //! This function triggers (`trigger_ev`) connection check and + //! attempt to re-connect a broken connection, when necessary. + void trigger (); + +protected: + //! reference to ConnectorThread's client + IConnectorThreadClient & ctc; + + //! This event is the re-connection trigger. + thread::ManualResetEvent trigger_ev; + + //! When this variable set to true when ConnectorThread is signaled to + bool exit_flag; +}; + + +} } // namespace log4cplus { namespace helpers { + +#endif // ! defined (LOG4CPLUS_SINGLE_THREADED) + +#endif // LOG4CPLUS_HELPERS_CONNECTORTHREAD_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/fileinfo.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/fileinfo.h new file mode 100644 index 0000000..88004ee --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/fileinfo.h @@ -0,0 +1,59 @@ +// -*- C++ -*- +// +// Copyright (C) 2012-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +#if ! defined (LOG4CPLUS_HELPERS_FILEINFO_H) +#define LOG4CPLUS_HELPERS_FILEINFO_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#ifdef LOG4CPLUS_HAVE_SYS_TYPES_H +#include +#endif + + +namespace log4cplus { namespace helpers { + +//! FileInfo structure is OS independent abstraction of the +//! stat() function. +struct LOG4CPLUS_EXPORT FileInfo +{ + helpers::Time mtime; + bool is_link; + off_t size; +}; + + +//! OS independent abstraction of stat() function. +LOG4CPLUS_EXPORT int getFileInfo (FileInfo * fi, tstring const & name); + + +} } // namespace log4cplus { namespace helpers { + +#endif // LOG4CPLUS_HELPERS_FILEINFO_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/lockfile.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/lockfile.h new file mode 100644 index 0000000..3856dcd --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/lockfile.h @@ -0,0 +1,69 @@ +// -*- C++ -*- +// +// Copyright (C) 2012-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +#if ! defined (LOG4CPLUS_HELPERS_LOCKFILE_H) +#define LOG4CPLUS_HELPERS_LOCKFILE_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + + +namespace log4cplus { namespace helpers { + + +class LOG4CPLUS_EXPORT LockFile +{ +public: + LockFile (tstring const & lock_file, bool create_dirs = false); + ~LockFile (); + + void lock () const; + void unlock () const; + +private: + void open (int) const; + void close () const; + + struct Impl; + + tstring lock_file_name; + Impl * data; + bool create_dirs; +}; + + +typedef log4cplus::thread::SyncGuard LockFileGuard; + + +} } // namespace log4cplus { namespace helpers { + + +#endif // LOG4CPLUS_HELPERS_LOCKFILE_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/loglog.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/loglog.h new file mode 100644 index 0000000..4f92ef8 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/loglog.h @@ -0,0 +1,145 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loglog.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_LOGLOG +#define LOG4CPLUS_HELPERS_LOGLOG + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + + +namespace log4cplus { + namespace helpers { + + /** + * This class used to output log statements from within the log4cplus package. + * + * Log4cplus components cannot make log4cplus logging calls. However, it is + * sometimes useful for the user to learn about what log4cplus is + * doing. You can enable log4cplus internal logging by defining the + * log4cplus.configDebug variable. + * + * All log4cplus internal debug calls go to cout + * where as internal error messages are sent to + * cerr. All internal messages are prepended with + * the string "log4clus: ". + */ + class LOG4CPLUS_EXPORT LogLog + { + public: + //! Return type of getLogLog(). + typedef LogLog * Ptr; + + /** + * Returns a reference to the LogLog singleton. + */ + static Ptr getLogLog(); + + + /** + * Allows to enable/disable log4cplus internal logging. + */ + void setInternalDebugging(bool enabled); + + /** + * In quite mode no LogLog generates strictly no output, not even + * for errors. + * + * @param quietMode A true for not + */ + void setQuietMode(bool quietMode); + + /** + * This method is used to output log4cplus internal debug + * statements. Output goes to std::cout. + */ + void debug(const log4cplus::tstring& msg) const; + void debug(tchar const * msg) const; + + /** + * This method is used to output log4cplus internal error + * statements. There is no way to disable error + * statements. Output goes to + * std::cerr. Optionally, this method can + * throw std::runtime_error exception too. + */ + void error(const log4cplus::tstring& msg, bool throw_flag = false) const; + void error(tchar const * msg, bool throw_flag = false) const; + + /** + * This method is used to output log4cplus internal warning + * statements. There is no way to disable warning statements. + * Output goes to std::cerr. + */ + void warn(const log4cplus::tstring& msg) const; + void warn(tchar const * msg) const; + + // Public ctor and dtor to be used only by internal::DefaultContext. + LogLog(); + virtual ~LogLog(); + + private: + enum TriState + { + TriUndef = -1, + TriFalse, + TriTrue + }; + + template + LOG4CPLUS_PRIVATE + void logging_worker (tostream & os, + bool (LogLog:: * cond) () const, tchar const *, + StringType const &, bool throw_flag = false) const; + + LOG4CPLUS_PRIVATE static void set_tristate_from_env (TriState *, + tchar const * envvar); + + LOG4CPLUS_PRIVATE bool get_quiet_mode () const; + LOG4CPLUS_PRIVATE bool get_not_quiet_mode () const; + LOG4CPLUS_PRIVATE bool get_debug_mode () const; + + // Data + mutable TriState debugEnabled; + mutable TriState quietMode; + thread::Mutex mutex; + + LOG4CPLUS_PRIVATE LogLog(const LogLog&); + LOG4CPLUS_PRIVATE LogLog & operator = (LogLog const &); + }; + + LOG4CPLUS_EXPORT LogLog & getLogLog (); + + } // end namespace helpers +} // end namespace log4cplus + + +#endif // LOG4CPLUS_HELPERS_LOGLOG + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/pointer.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/pointer.h new file mode 100644 index 0000000..d9ccfd4 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/pointer.h @@ -0,0 +1,210 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: pointer.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +// +// Note: Some of this code uses ideas from "More Effective C++" by Scott +// Myers, Addison Wesley Longmain, Inc., (c) 1996, Chapter 29, pp. 183-213 +// + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_POINTERS_HEADER_ +#define LOG4CPLUS_HELPERS_POINTERS_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#if ! defined (LOG4CPLUS_SINGLE_THREADED) +#include +#endif + + +namespace log4cplus { + namespace helpers { + + /****************************************************************************** + * Class SharedObject (from pp. 204-205) * + ******************************************************************************/ + + class LOG4CPLUS_EXPORT SharedObject + { + public: + void addReference() const LOG4CPLUS_NOEXCEPT; + void removeReference() const; + + protected: + // Ctor + SharedObject() + : access_mutex() + , count__(0) + { } + + SharedObject(const SharedObject&) + : access_mutex() + , count__(0) + { } + + SharedObject(SharedObject &&) + : access_mutex() + , count__(0) + { } + + // Dtor + virtual ~SharedObject(); + + // Operators + SharedObject& operator=(const SharedObject&) LOG4CPLUS_NOEXCEPT { return *this; } + SharedObject& operator=(SharedObject &&) LOG4CPLUS_NOEXCEPT { return *this; } + + public: + thread::Mutex access_mutex; + + private: +#if defined (LOG4CPLUS_SINGLE_THREADED) + typedef unsigned count_type; +#else + typedef std::atomic count_type; +#endif + mutable count_type count__; + }; + + + /****************************************************************************** + * Template Class SharedObjectPtr (from pp. 203, 206) * + ******************************************************************************/ + template + class SharedObjectPtr + { + public: + // Ctor + explicit + SharedObjectPtr(T* realPtr = 0) LOG4CPLUS_NOEXCEPT + : pointee(realPtr) + { + addref (); + } + + SharedObjectPtr(const SharedObjectPtr& rhs) LOG4CPLUS_NOEXCEPT + : pointee(rhs.pointee) + { + addref (); + } + + SharedObjectPtr(SharedObjectPtr && rhs) LOG4CPLUS_NOEXCEPT + : pointee (std::move (rhs.pointee)) + { + rhs.pointee = 0; + } + + SharedObjectPtr & operator = (SharedObjectPtr && rhs) LOG4CPLUS_NOEXCEPT + { + rhs.swap (*this); + return *this; + } + + // Dtor + ~SharedObjectPtr() + { + if (pointee) + pointee->removeReference(); + } + + // Operators + bool operator==(const SharedObjectPtr& rhs) const + { return (pointee == rhs.pointee); } + bool operator!=(const SharedObjectPtr& rhs) const + { return (pointee != rhs.pointee); } + bool operator==(const T* rhs) const { return (pointee == rhs); } + bool operator!=(const T* rhs) const { return (pointee != rhs); } + T* operator->() const {assert (pointee); return pointee; } + T& operator*() const {assert (pointee); return *pointee; } + + SharedObjectPtr& operator=(const SharedObjectPtr& rhs) + { + return this->operator = (rhs.pointee); + } + + SharedObjectPtr& operator=(T* rhs) + { + SharedObjectPtr (rhs).swap (*this); + return *this; + } + + // Methods + T* get() const { return pointee; } + + void swap (SharedObjectPtr & other) LOG4CPLUS_NOEXCEPT + { + std::swap (pointee, other.pointee); + } + + typedef T * (SharedObjectPtr:: * unspec_bool_type) () const; + operator unspec_bool_type () const + { + return pointee ? &SharedObjectPtr::get : 0; + } + + bool operator ! () const + { + return ! pointee; + } + + private: + // Methods + void addref() const LOG4CPLUS_NOEXCEPT + { + if (pointee) + pointee->addReference(); + } + + // Data + T* pointee; + }; + + + //! Boost `intrusive_ptr` helpers. + //! @{ + inline + void + intrusive_ptr_add_ref (SharedObject const * so) + { + so->addReference(); + } + + inline + void + intrusive_ptr_release (SharedObject const * so) + { + so->removeReference(); + } + //! @} + + } // end namespace helpers +} // end namespace log4cplus + + +#endif // LOG4CPLUS_HELPERS_POINTERS_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/property.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/property.h new file mode 100644 index 0000000..a6c08cf --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/property.h @@ -0,0 +1,162 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: property.h +// Created: 2/2002 +// Author: Tad E. Smith +// +// +// Copyright 2002-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_PROPERTY_HEADER_ +#define LOG4CPLUS_HELPERS_PROPERTY_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + + +namespace log4cplus { + namespace helpers { + + //! \sa log4cplus::PropertyConfigurator + class LOG4CPLUS_EXPORT Properties { + public: + enum PFlags + { + // These encoding related options occupy 2 bits of the flags + // and are mutually exclusive. These flags are synchronized + // with PCFlags in PropertyConfigurator. + + fEncodingShift = 3 + , fEncodingMask = 0x3 + , fUnspecEncoding = (0 << fEncodingShift) +#if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) && defined (UNICODE) + , fUTF8 = (1 << fEncodingShift) +#endif +#if (defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) || defined (_WIN32)) \ + && defined (UNICODE) + , fUTF16 = (2 << fEncodingShift) +#endif +#if defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET) && defined (UNICODE) + , fUTF32 = (3 << fEncodingShift) +#endif + }; + + Properties(); + explicit Properties(log4cplus::tistream& input); + explicit Properties(const log4cplus::tstring& inputFile, unsigned flags = 0); + virtual ~Properties(); + + // constants + static const tchar PROPERTIES_COMMENT_CHAR; + + // methods + /** + * Tests to see if key can be found in this map. + */ + bool exists(const log4cplus::tstring& key) const; + bool exists(tchar const * key) const; + + /** + * Returns the number of entries in this map. + */ + std::size_t size() const + { + return data.size(); + } + + /** + * Searches for the property with the specified key in this property + * list. If the key is not found in this property list, the default + * property list, and its defaults, recursively, are then checked. + * The method returns null if the property is not found. + */ + log4cplus::tstring const & getProperty(const log4cplus::tstring& key) const; + log4cplus::tstring const & getProperty(tchar const * key) const; + + /** + * Searches for the property with the specified key in this property + * list. If the key is not found in this property list, the default + * property list, and its defaults, recursively, are then checked. + * The method returns the default value argument if the property is + * not found. + */ + log4cplus::tstring getProperty(const log4cplus::tstring& key, + const log4cplus::tstring& defaultVal) const; + + /** + * Returns all the keys in this property list. + */ + std::vector propertyNames() const; + + /** + * Inserts value into this map indexed by key. + */ + void setProperty(const log4cplus::tstring& key, const log4cplus::tstring& value); + + /** + * Removed the property index by key from this map. + */ + bool removeProperty(const log4cplus::tstring& key); + + /** + * Returns a subset of the "properties" whose keys start with + * "prefix". The returned "properties" have "prefix" trimmed from + * their keys. + */ + Properties getPropertySubset(const log4cplus::tstring& prefix) const; + + bool getInt (int & val, log4cplus::tstring const & key) const; + bool getUInt (unsigned & val, log4cplus::tstring const & key) const; + bool getLong (long & val, log4cplus::tstring const & key) const; + bool getULong (unsigned long & val, log4cplus::tstring const & key) const; + bool getBool (bool & val, log4cplus::tstring const & key) const; + bool getString (log4cplus::tstring & val, log4cplus::tstring const & key) const; + + protected: + // Types + typedef std::map StringMap; + + // Methods + void init(log4cplus::tistream& input); + + // Data + StringMap data; + unsigned flags; + + private: + template + log4cplus::tstring const & get_property_worker ( + StringType const & key) const; + + template + bool get_type_val_worker (ValType & val, + log4cplus::tstring const & key) const; + }; + } // end namespace helpers + +} + + +#endif // LOG4CPLUS_HELPERS_PROPERTY_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/queue.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/queue.h new file mode 100644 index 0000000..113d695 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/queue.h @@ -0,0 +1,158 @@ +// -*- C++ -*- +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_HELPERS_QUEUE_H +#define LOG4CPLUS_HELPERS_QUEUE_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + +#include +#include +#include +#include + + +namespace log4cplus { namespace thread { + + +//! Single consumer, multiple producers queue. +class LOG4CPLUS_EXPORT Queue + : public virtual helpers::SharedObject +{ +public: + //! Type of the state flags field. + typedef unsigned flags_type; + + //! Queue storage type. + typedef std::deque queue_storage_type; + + explicit Queue (unsigned len = 100); + virtual ~Queue (); + + // Producers' methods. + + //! Puts event ev into queue, sets QUEUE flag and + //! sets internal event object into signaled state. If the EXIT + //! flags is already set upon entering the function, nothing is + //! inserted into the queue. The function can block on internal + //! semaphore if the queue has reached maximal allowed + //! length. Calling thread is unblocked either by consumer thread + //! removing item from queue or by any other thread calling + //! signal_exit(). + //! + //! \param ev spi::InternalLoggingEvent to be put into the queue. + //! \return Flags. + flags_type put_event (spi::InternalLoggingEvent const & ev); + + //! Sets EXIT flag and DRAIN flag and sets internal event object + //! into signaled state. + //! \param drain If true, DRAIN flag will be set, otherwise unset. + //! \return Flags, ERROR_BIT can be set upon error. + flags_type signal_exit (bool drain = true); + + // Consumer's methods. + + //! The get_events() function is used by queue's consumer. It + //! fills buf argument and sets EVENT flag in return + //! value. If EXIT flag is already set in flags member upon + //! entering the function then depending on DRAIN flag it either + //! fills buf argument or does not fill the argument, + //! if the queue is non-empty. The function blocks by waiting for + //! internal event object to be signaled if the queue is empty, + //! unless EXIT flag is set. The calling thread is unblocked when + //! items are added into the queue or when exit is signaled using + //! the signal_exit() function. + //! + //! + //! Upon error, return value has one of the error flags set. + //! + //! \param buf Pointer to storage of spi::InternalLoggingEvent + //! instances to be filled from queue. + //! \return Flags. + flags_type get_events (queue_storage_type * buf); + + //! Possible state flags. + enum Flags + { + //! EVENT flag is set in return value of get_event() call if + //! the ev argument is filled with event from the queue. + EVENT = 0x0001, + + //! QUEUE flag is set by producers when they put item into the + //! queue. + QUEUE = 0x0002, + + //! EXIT flag is set by signal_exit() call, signaling that the + //! queue worker thread should end itself. + EXIT = 0x0004, + + //! When DRAIN flag is set together with EXIT flag, the queue + //! worker thread will first drain the queue before exiting. + DRAIN = 0x0008, + + //! ERROR_BIT signals error. + ERROR_BIT = 0x0010, + + //! ERROR_AFTER signals error that has occurred after queue has + //! already been touched. + ERROR_AFTER = 0x0020 + }; + +protected: + //! Queue storage. + queue_storage_type queue; + + //! Mutex protecting queue and flags. + Mutex mutex; + + //! Event on which consumer can wait if it finds queue empty. + ManualResetEvent ev_consumer; + + //! Semaphore that limits the queue length. + Semaphore sem; + + //! State flags. + flags_type flags; + +private: + Queue (Queue const &); + Queue & operator = (Queue const &); +}; + + +typedef helpers::SharedObjectPtr QueuePtr; + + +} } // namespace log4cplus { namespace thread { + + +#endif // LOG4CPLUS_SINGLE_THREADED + +#endif // LOG4CPLUS_HELPERS_QUEUE_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/snprintf.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/snprintf.h new file mode 100644 index 0000000..bf56935 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/snprintf.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_HELPERS_SNPRINTF_H +#define LOG4CPLUS_HELPERS_SNPRINTF_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + + +namespace log4cplus { namespace helpers { + + +class LOG4CPLUS_EXPORT snprintf_buf +{ +public: + snprintf_buf (); + + tchar const * print (tchar const * fmt, ...) + LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 2, 3); + + int print_va_list (tchar const * & str, tchar const * fmt, std::va_list) + LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 3, 0); + +private: + std::vector buf; +}; + + +} } // namespace log4cplus { namespace helpers + + + +#endif // LOG4CPLUS_HELPERS_SNPRINTF_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socket.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socket.h new file mode 100644 index 0000000..1924bfe --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socket.h @@ -0,0 +1,163 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: socket.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_SOCKET_HEADER_ +#define LOG4CPLUS_HELPERS_SOCKET_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include +#include + + +namespace log4cplus { + namespace helpers { + + enum SocketState { ok, + not_opened, + bad_address, + connection_failed, + broken_pipe, + invalid_access_mode, + message_truncated, + accept_interrupted + }; + + typedef std::ptrdiff_t SOCKET_TYPE; + + extern LOG4CPLUS_EXPORT SOCKET_TYPE const INVALID_SOCKET_VALUE; + + class LOG4CPLUS_EXPORT AbstractSocket { + public: + AbstractSocket(); + AbstractSocket(SOCKET_TYPE sock, SocketState state, int err); + AbstractSocket(AbstractSocket const &) = delete; + AbstractSocket(AbstractSocket &&) LOG4CPLUS_NOEXCEPT; + virtual ~AbstractSocket() = 0; + + /// Close socket + virtual void close(); + virtual bool isOpen() const; + virtual void shutdown(); + AbstractSocket & operator = (AbstractSocket && rhs) LOG4CPLUS_NOEXCEPT; + + void swap (AbstractSocket &); + + protected: + SOCKET_TYPE sock; + SocketState state; + int err; + }; + + + + /** + * This class implements client sockets (also called just "sockets"). + * A socket is an endpoint for communication between two machines. + */ + class LOG4CPLUS_EXPORT Socket : public AbstractSocket { + public: + // ctor and dtor + Socket(); + Socket(SOCKET_TYPE sock, SocketState state, int err); + Socket(const tstring& address, unsigned short port, + bool udp = false, bool ipv6 = false); + Socket(Socket &&) LOG4CPLUS_NOEXCEPT; + virtual ~Socket(); + + Socket & operator = (Socket &&) LOG4CPLUS_NOEXCEPT; + + // methods + virtual bool read(SocketBuffer& buffer); + virtual bool write(const SocketBuffer& buffer); + virtual bool write(const std::string & buffer); + virtual bool write(std::size_t bufferCount, + SocketBuffer const * const * buffers); + + template + static bool write(Socket & socket, Args &&... args) + { + SocketBuffer const * const buffers[sizeof... (args)] { + (&args)... }; + return socket.write (sizeof... (args), buffers); + } + }; + + + + /** + * This class implements server sockets. A server socket waits for + * requests to come in over the network. It performs some operation + * based on that request, and then possibly returns a result to the + * requester. + */ + class LOG4CPLUS_EXPORT ServerSocket : public AbstractSocket { + public: + ServerSocket(unsigned short port, bool udp = false, + bool ipv6 = false, tstring const & host = tstring ()); + ServerSocket(ServerSocket &&) LOG4CPLUS_NOEXCEPT; + virtual ~ServerSocket(); + + ServerSocket & operator = (ServerSocket &&) LOG4CPLUS_NOEXCEPT; + + Socket accept(); + void interruptAccept (); + void swap (ServerSocket &); + + protected: + std::array interruptHandles; + }; + + + LOG4CPLUS_EXPORT SOCKET_TYPE openSocket(unsigned short port, bool udp, + bool ipv6, SocketState& state); + LOG4CPLUS_EXPORT SOCKET_TYPE openSocket(tstring const & host, + unsigned short port, bool udp, bool ipv6, SocketState& state); + + LOG4CPLUS_EXPORT SOCKET_TYPE connectSocket(const log4cplus::tstring& hostn, + unsigned short port, bool udp, bool ipv6, SocketState& state); + LOG4CPLUS_EXPORT SOCKET_TYPE acceptSocket(SOCKET_TYPE sock, SocketState& state); + LOG4CPLUS_EXPORT int closeSocket(SOCKET_TYPE sock); + LOG4CPLUS_EXPORT int shutdownSocket(SOCKET_TYPE sock); + + LOG4CPLUS_EXPORT long read(SOCKET_TYPE sock, SocketBuffer& buffer); + LOG4CPLUS_EXPORT long write(SOCKET_TYPE sock, + const SocketBuffer& buffer); + LOG4CPLUS_EXPORT long write(SOCKET_TYPE sock, std::size_t bufferCount, + SocketBuffer const * const * buffers); + LOG4CPLUS_EXPORT long write(SOCKET_TYPE sock, + const std::string & buffer); + + LOG4CPLUS_EXPORT tstring getHostname (bool fqdn); + LOG4CPLUS_EXPORT int setTCPNoDelay (SOCKET_TYPE, bool); + + } // end namespace helpers +} // end namespace log4cplus + +#endif // LOG4CPLUS_HELPERS_SOCKET_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socketbuffer.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socketbuffer.h new file mode 100644 index 0000000..d58b643 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/socketbuffer.h @@ -0,0 +1,79 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: socketbuffer.h +// Created: 5/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_SOCKET_BUFFER_HEADER_ +#define LOG4CPLUS_HELPERS_SOCKET_BUFFER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +namespace log4cplus { +namespace helpers { + +/** + * + */ +class LOG4CPLUS_EXPORT SocketBuffer +{ +public: + explicit SocketBuffer(std::size_t max); + virtual ~SocketBuffer(); + + char *getBuffer() const { return buffer; } + std::size_t getMaxSize() const { return maxsize; } + std::size_t getSize() const { return size; } + void setSize(std::size_t s) { size = s; } + std::size_t getPos() const { return pos; } + + unsigned char readByte(); + unsigned short readShort(); + unsigned int readInt(); + tstring readString(unsigned char sizeOfChar); + + void appendByte(unsigned char val); + void appendShort(unsigned short val); + void appendInt(unsigned int val); + void appendString(const tstring& str); + void appendBuffer(const SocketBuffer& buffer); + +private: + // Data + std::size_t maxsize; + std::size_t size; + std::size_t pos; + char *buffer; + + SocketBuffer(SocketBuffer const & rhs); + SocketBuffer& operator= (SocketBuffer const& rhs); +}; + +} // end namespace helpers +} // end namespace log4cplus + +#endif // LOG4CPLUS_HELPERS_SOCKET_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/stringhelper.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/stringhelper.h new file mode 100644 index 0000000..b5ff806 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/stringhelper.h @@ -0,0 +1,271 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: stringhelper.h +// Created: 3/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_STRINGHELPER_HEADER_ +#define LOG4CPLUS_HELPERS_STRINGHELPER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include +#include + + +namespace log4cplus { + namespace helpers { + + /** + * Returns s in upper case. + */ + LOG4CPLUS_EXPORT log4cplus::tstring toUpper(const log4cplus::tstring_view& s); + LOG4CPLUS_EXPORT tchar toUpper(tchar); + + + /** + * Returns s in lower case. + */ + LOG4CPLUS_EXPORT log4cplus::tstring toLower(const log4cplus::tstring_view& s); + LOG4CPLUS_EXPORT tchar toLower(tchar); + + + /** + * Tokenize s using c as the delimiter and + * put the resulting tokens in _result. If + * collapseTokens is false, multiple adjacent delimiters + * will result in zero length tokens. + * + * Example: + *
+         *   string s = // Set string with '.' as delimiters
+         *   list tokens;
+         *   tokenize(s, '.', back_insert_iterator >(tokens));
+         * 
+ */ + template + inline + void + tokenize(const StringType& s, typename StringType::value_type c, + OutputIter result, bool collapseTokens = true) + { + typedef typename StringType::size_type size_type; + size_type const slen = s.length(); + size_type first = 0; + size_type i = 0; + for (i=0; i < slen; ++i) + { + if (s[i] == c) + { + *result = StringType (s, first, i - first); + ++result; + if (collapseTokens) + while (i+1 < slen && s[i+1] == c) + ++i; + first = i + 1; + } + } + if (first != i) + *result = StringType (s, first, i - first); + else if (! collapseTokens && first == i) + *result = StringType (); + } + + + template + struct ConvertIntegerToStringHelper; + + + template + struct ConvertIntegerToStringHelper + { + static inline + void + step1 (charType * & it, intType & value) + { + // The sign of the result of the modulo operator is + // implementation defined. That's why we work with + // positive counterpart instead. Also, in twos + // complement arithmetic the smallest negative number + // does not have positive counterpart; the range is + // asymetric. That's why we handle the case of value + // == min() specially here. + if (LOG4CPLUS_UNLIKELY ( + value == (std::numeric_limits::min) ())) + { + intType const r = value / 10; + intType const a = (-r) * 10; + intType const mod = -(a + value); + value = -r; + + *(it - 1) + = static_cast(LOG4CPLUS_TEXT('0') + mod); + --it; + } + else + value = -value; + } + + static + bool + is_negative (intType val) + { + return val < 0; + } + }; + + + template + struct ConvertIntegerToStringHelper + { + static inline + void + step1 (charType * &, intType &) + { + // This will never be called for unsigned types. + } + + static + bool + is_negative (intType) + { + return false; + } + }; + + + template + inline + void + convertIntegerToString (stringType & str, intType value) + { + typedef std::numeric_limits intTypeLimits; + typedef typename stringType::value_type charType; + typedef ConvertIntegerToStringHelper HelperType; + + charType buffer[intTypeLimits::digits10 + 2]; + const std::size_t buffer_size + = sizeof (buffer) / sizeof (charType); + + charType * it = &buffer[buffer_size]; + charType const * const buf_end = &buffer[buffer_size]; + + if (LOG4CPLUS_UNLIKELY (value == 0)) + { + --it; + *it = LOG4CPLUS_TEXT('0'); + } + else + { + bool const negative = HelperType::is_negative (value); + if (negative) + HelperType::step1 (it, value); + + for (; value != 0; --it) + { + intType mod = value % 10; + value = value / 10; + *(it - 1) = static_cast(LOG4CPLUS_TEXT('0') + + mod); + } + + if (negative) + { + --it; + *it = LOG4CPLUS_TEXT('-'); + } + } + + str.assign (static_cast(it), buf_end); + } + + + template + inline + tstring + convertIntegerToString (intType value) + { + tstring result; + convertIntegerToString (result, value); + return result; + } + + + template + inline + std::string + convertIntegerToNarrowString (intType value) + { + std::string result; + convertIntegerToString (result, value); + return result; + } + + + //! Join a list of items into a string. + template + inline + void + join_worker (tstring & result, Iterator & start, Iterator & last, + Separator const & sep) + { + if (start != last) + result = *start++; + + for (; start != last; ++start) + { + result += sep; + result += *start; + } + } + + //! Join a list of items into a string. + template + inline + void + join (tstring & result, Iterator start, Iterator last, + tstring_view const & sep) + { + join_worker (result, start, last, sep); + } + + //! Join a list of items into a string. + template + inline + void + join (tstring & result, Iterator start, Iterator last, + tstring::value_type sep) + { + join_worker (result, start, last, sep); + } + + + } // namespace helpers + +} // namespace log4cplus + +#endif // LOG4CPLUS_HELPERS_STRINGHELPER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/thread-config.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/thread-config.h new file mode 100644 index 0000000..0d89762 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/thread-config.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: thread-config.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_THREAD_CONFIG_HEADER_ +#define LOG4CPLUS_HELPERS_THREAD_CONFIG_HEADER_ + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (LOG4CPLUS_USE_PTHREADS) +# if defined (__APPLE__) +# define LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE +# endif + +#elif defined(LOG4CPLUS_USE_WIN32_THREADS) +# define LOG4CPLUS_USE_SRW_LOCK +//# define LOG4CPLUS_POOR_MANS_SHAREDMUTEX +# undef LOG4CPLUS_HAVE_TLS_SUPPORT +# undef LOG4CPLUS_THREAD_LOCAL_VAR +# if defined (_MSC_VER) +// The __declspec(thread) functionality is not compatible with LoadLibrary(). +// For more information why see and "Windows and TLS" note in README. +// . +# define LOG4CPLUS_HAVE_TLS_SUPPORT 1 +# define LOG4CPLUS_THREAD_LOCAL_VAR __declspec(thread) +# endif + +#elif defined(LOG4CPLUS_SINGLE_THREADED) +# undef LOG4CPLUS_HAVE_TLS_SUPPORT +# undef LOG4CPLUS_THREAD_LOCAL_VAR + +#else +# error "You Must define a Threading model" + +#endif + + +#endif // LOG4CPLUS_HELPERS_THREAD_CONFIG_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/timehelper.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/timehelper.h new file mode 100644 index 0000000..40f3b42 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/helpers/timehelper.h @@ -0,0 +1,169 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: timehelper.h +// Created: 6/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HELPERS_TIME_HELPER_HEADER_ +#define LOG4CPLUS_HELPERS_TIME_HELPER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#if defined (LOG4CPLUS_HAVE_TIME_H) +#include +#endif + +#include +#include + + +namespace log4cplus { + +namespace helpers { + + +using std::time_t; +using std::tm; +namespace chrono = std::chrono; + +typedef chrono::system_clock Clock; +typedef chrono::duration Duration; +typedef chrono::time_point Time; + + +template +inline +Time +time_cast (chrono::time_point const & tp) +{ + return chrono::time_point_cast (tp); +} + + +inline +Time +now () +{ + return time_cast (Clock::now ()); +} + + +inline +Time +from_time_t (time_t t_time) +{ + return time_cast (Clock::from_time_t (t_time)); +} + + +inline +time_t +to_time_t (Time const & the_time) +{ + // This is based on . It is + // possible that to_time_t() returns rounded time and we want truncation. + + time_t time = Clock::to_time_t (the_time); + auto const rounded_time = from_time_t (time); + if (rounded_time > the_time) + --time; + + return time; +} + + +LOG4CPLUS_EXPORT Time from_struct_tm (tm * t); + + +inline +Time +truncate_fractions (Time const & the_time) +{ + return from_time_t (to_time_t (the_time)); +} + + +inline +long +microseconds_part (Time const & the_time) +{ + static_assert ((std::ratio_equal::value), + "microseconds"); + + // This is based on + return static_cast( + (the_time - from_time_t (to_time_t (the_time))).count ()); +} + + +inline +Time +time_from_parts (time_t tv_sec, long tv_usec) +{ + return from_time_t (tv_sec) + chrono::microseconds (tv_usec); +} + + +/** + * Populates tm using the gmtime() + * function. + */ + +LOG4CPLUS_EXPORT +void gmTime (tm* t, Time const &); + +/** + * Populates tm using the localtime() + * function. + */ + +LOG4CPLUS_EXPORT +void localTime (tm* t, Time const &); + +/** + * Returns a string with a "formatted time" specified by + * fmt. It used the strftime() + * function to do this. + * + * Look at your platform's strftime() documentation + * for the formatting options available. + * + * The following additional options are provided:
+ * %q - 3 character field that provides milliseconds + * %Q - 7 character field that provides fractional + * milliseconds. + */ +LOG4CPLUS_EXPORT +log4cplus::tstring getFormattedTime (log4cplus::tstring const & fmt, + Time const & the_time, bool use_gmtime = false); + + +} // namespace helpers + +} // namespace log4cplus + + +#endif // LOG4CPLUS_HELPERS_TIME_HELPER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchy.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchy.h new file mode 100644 index 0000000..3076161 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchy.h @@ -0,0 +1,325 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: hierarchy.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HIERARCHY_HEADER_ +#define LOG4CPLUS_HIERARCHY_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include + + +namespace log4cplus { + // Forward Declarations + class HierarchyLocker; + + /** + * This class is specialized in retrieving loggers by name and + * also maintaining the logger hierarchy. + * + * The casual user should not have to deal with this class + * directly. However, if you are in an environment where + * multiple applications run in the same process, then read on. + * + * The structure of the logger hierarchy is maintained by the + * {@link #getInstance} method. The hierarchy is such that children + * link to their parent but parents do not have any pointers to their + * children. Moreover, loggers can be instantiated in any order, in + * particular descendant before ancestor. + * + * In case a descendant is created before a particular ancestor, + * then it creates a provision node for the ancestor and adds itself + * to the provision node. Other descendants of the same ancestor add + * themselves to the previously created provision node. + */ + class LOG4CPLUS_EXPORT Hierarchy + { + public: + // DISABLE_OFF should be set to a value lower than all possible + // priorities. + static const LogLevel DISABLE_OFF; + static const LogLevel DISABLE_OVERRIDE; + + // Ctors + /** + * Create a new Logger hierarchy. + */ + Hierarchy(); + + // Dtor + virtual ~Hierarchy(); + + // Methods + /** + * This call will clear all logger definitions from the internal + * hashtable. Invoking this method will irrevocably mess up the + * logger hierarchy. + * + * You should really know what you are doing before + * invoking this method. + */ + virtual void clear(); + + /** + * Returns true if the named logger exists + * (in the default hierarchy). + * + * @param name The name of the logger to search for. + */ + virtual bool exists(const log4cplus::tstring_view& name); + + /** + * Similar to {@link #disable(LogLevel)} except that the LogLevel + * argument is given as a log4cplus::tstring. + */ + virtual void disable(const log4cplus::tstring_view& loglevelStr); + + /** + * Disable all logging requests of LogLevel equal to or + * below the ll parameter p, for + * all loggers in this hierarchy. Logging requests of + * higher LogLevel then p remain unaffected. + * + * Nevertheless, if the + * BasicConfigurator::DISABLE_OVERRIDE_KEY property is set to + * true, then logging requests are evaluated as usual. + * + * The "disable" family of methods are there for speed. They + * allow printing methods such as debug, info, etc. to return + * immediately after an integer comparison without walking the + * logger hierarchy. In most modern computers an integer + * comparison is measured in nanoseconds where as a logger walk is + * measured in units of microseconds. + */ + virtual void disable(LogLevel ll); + + /** + * Disable all logging requests regardless of logger and LogLevel. + * This method is equivalent to calling {@link #disable} with the + * argument FATAL_LOG_LEVEL, the highest possible LogLevel. + */ + virtual void disableAll(); + + /** + * Disable all Debug logging requests regardless of logger. + * This method is equivalent to calling {@link #disable} with the + * argument DEBUG_LOG_LEVEL. + */ + virtual void disableDebug(); + + /** + * Disable all Info logging requests regardless of logger. + * This method is equivalent to calling {@link #disable} with the + * argument INFO_LOG_LEVEL. + */ + virtual void disableInfo(); + + /** + * Undoes the effect of calling any of {@link #disable}, {@link + * #disableAll}, {@link #disableDebug} and {@link #disableInfo} + * methods. More precisely, invoking this method sets the Logger + * class internal variable called disable to its + * default "off" value. + */ + virtual void enableAll(); + + /** + * Return a new logger instance named as the first parameter using + * the default factory. + * + * If a logger of that name already exists, then it will be + * returned. Otherwise, a new logger will be instantiated and + * then linked with its existing ancestors as well as children. + * + * @param name The name of the logger to retrieve. + */ + virtual Logger getInstance(const log4cplus::tstring_view& name); + + /** + * Return a new logger instance named as the first parameter using + * factory. + * + * If a logger of that name already exists, then it will be + * returned. Otherwise, a new logger will be instantiated by the + * factory parameter and linked with its existing + * ancestors as well as children. + * + * @param name The name of the logger to retrieve. + * @param factory The factory that will make the new logger instance. + */ + virtual Logger getInstance(const log4cplus::tstring_view& name, + spi::LoggerFactory& factory); + + /** + * Returns all the currently defined loggers in this hierarchy. + * + * The root logger is not included in the returned list. + */ + virtual LoggerList getCurrentLoggers(); + + /** + * Is the LogLevel specified by level enabled? + */ + virtual bool isDisabled(LogLevel level); + + /** + * Get the root of this hierarchy. + */ + virtual Logger getRoot() const; + + /** + * Reset all values contained in this hierarchy instance to their + * default. This removes all appenders from all loggers, sets + * the LogLevel of all non-root loggers to NOT_SET_LOG_LEVEL, + * sets their additivity flag to true and sets the LogLevel + * of the root logger to DEBUG_LOG_LEVEL. Moreover, message disabling + * is set its default "off" value. + * + * Existing loggers are not removed. They are just reset. + * + * This method should be used sparingly and with care as it will + * block all logging until it is completed.

+ */ + virtual void resetConfiguration(); + + /** + * Set the default LoggerFactory instance. + */ + virtual void setLoggerFactory(std::unique_ptr factory); + + /** + * Returns the default LoggerFactory instance. + */ + virtual spi::LoggerFactory* getLoggerFactory(); + + /** + * Shutting down a hierarchy will safely close and remove + * all appenders in all loggers including the root logger. + * + * Some appenders such as SocketAppender need to be closed before the + * application exits. Otherwise, pending logging events might be + * lost. + * + * The shutdown method is careful to close nested + * appenders before closing regular appenders. This is allows + * configurations where a regular appender is attached to a logger + * and again to a nested appender. + */ + virtual void shutdown(); + + private: + // Types + typedef std::vector ProvisionNode; + typedef std::map> ProvisionNodeMap; + typedef std::map> LoggerMap; + + // Methods + /** + * This is the implementation of the getInstance() method. + * NOTE: This method does not lock the hashtable_mutex. + */ + LOG4CPLUS_PRIVATE + Logger getInstanceImpl(const log4cplus::tstring_view& name, + spi::LoggerFactory& factory); + + /** + * This is the implementation of the getCurrentLoggers(). + * NOTE: This method does not lock the hashtable_mutex. + */ + LOG4CPLUS_PRIVATE + void initializeLoggerList(LoggerList& list) const; + + /** + * This method loops through all the *potential* parents of + * logger'. There 3 possible cases: + * + * 1) No entry for the potential parent of 'logger' exists + * + * We create a ProvisionNode for this potential parent and insert + * 'logger' in that provision node. + * + * 2) There is an entry of type Logger for the potential parent. + * + * The entry is 'logger's nearest existing parent. We update logger's + * parent field with this entry. We also break from the loop + * because updating our parent's parent is our parent's + * responsibility. + * + * 3) There entry is of type ProvisionNode for this potential parent. + * + * We add 'logger' to the list of children for this potential parent. + */ + LOG4CPLUS_PRIVATE void updateParents(Logger const & logger); + + /** + * We update the links for all the children that placed themselves + * in the provision node 'pn'. The second argument 'logger' is a + * reference for the newly created Logger, parent of all the + * children in 'pn' + * + * We loop on all the children 'c' in 'pn': + * + * If the child 'c' has been already linked to a child of + * 'logger' then there is no need to update 'c'. + * + * Otherwise, we set logger's parent field to c's parent and set + * c's parent field to logger. + */ + LOG4CPLUS_PRIVATE void updateChildren(ProvisionNode& pn, + Logger const & logger); + + // Data + thread::Mutex hashtable_mutex; + std::unique_ptr defaultFactory; + ProvisionNodeMap provisionNodes; + LoggerMap loggerPtrs; + Logger root; + + int disableValue; + + bool emittedNoAppenderWarning; + + // Disallow copying of instances of this class + Hierarchy(const Hierarchy&); + Hierarchy& operator=(const Hierarchy&); + + // Friends + friend class log4cplus::spi::LoggerImpl; + friend class log4cplus::HierarchyLocker; + }; + + + LOG4CPLUS_EXPORT Hierarchy & getDefaultHierarchy (); + + +} // end namespace log4cplus + +#endif // LOG4CPLUS_HIERARCHY_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchylocker.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchylocker.h new file mode 100644 index 0000000..20fa9aa --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/hierarchylocker.h @@ -0,0 +1,79 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: hierarchylocker.h +// Created: 8/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_HIERARCHY_LOCKER_HEADER_ +#define LOG4CPLUS_HIERARCHY_LOCKER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + + +namespace log4cplus +{ + + class Hierarchy; + + + /** + * This is used to lock a Hierarchy. The dtor unlocks the Hierarchy. + */ + class LOG4CPLUS_EXPORT HierarchyLocker { + public: + // ctor & dtor + HierarchyLocker(Hierarchy& h); + ~HierarchyLocker() LOG4CPLUS_NOEXCEPT_FALSE; + + /** + * Calls the resetConfiguration() method on the locked Hierarchy. + */ + void resetConfiguration(); + + /** + * Calls the getInstance() method on the locked Hierarchy. + */ + Logger getInstance(const log4cplus::tstring& name); + + /** + * Calls the getInstance() method on the locked Hierarchy. + */ + Logger getInstance(const log4cplus::tstring& name, spi::LoggerFactory& factory); + + void addAppender(Logger &logger, log4cplus::SharedAppenderPtr& appender); + + private: + // Data + Hierarchy& h; + log4cplus::thread::MutexGuard hierarchyLocker; + LoggerList loggerList; + }; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_HIERARCHY_LOCKER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/initializer.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/initializer.h new file mode 100644 index 0000000..92245de --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/initializer.h @@ -0,0 +1,62 @@ +// Copyright (C) 2015-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_INITIALIZER_HXX +#define LOG4CPLUS_INITIALIZER_HXX + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +namespace log4cplus +{ + +/** + This class helps with initialization and shutdown of log4cplus. Its + constructor calls `log4cplus::initialize()` and its destructor calls + `log4cplus::Logger::shutdown()`. Use this class as the first thing in your + `main()`. It will ensure shutdown of log4cplus at the end of + `main()`. This is particularly important on Windows, where shutdown of + standard threads outside `main()` is impossible. + */ +class LOG4CPLUS_EXPORT Initializer +{ +public: + Initializer (); + ~Initializer (); + + Initializer (Initializer const &) = delete; + Initializer (Initializer &&) = delete; + Initializer & operator = (Initializer const &) = delete; + Initializer & operator = (Initializer &&) = delete; +}; + +} // namespace log4cplus + + +#endif // LOG4CPLUS_INITIALIZER_HXX diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/customloglevelmanager.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/customloglevelmanager.h new file mode 100644 index 0000000..ddef050 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/customloglevelmanager.h @@ -0,0 +1,98 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: customloglevelmanager.h +// Created: 12/2018 +// Author: Jens Rehsack +// Author: V谩clav Haisman +// +// +// Copyright (C) 2018, Jens Rehsack. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +/** @file + * This header contains declaration internal to log4cplus. They must never be + * visible from user accesible headers or exported in DLL/shared library. + */ + + +#ifndef LOG4CPLUS_INTERNAL_CUSTOMLOGLEVELMANAGER_HEADER_ +#define LOG4CPLUS_INTERNAL_CUSTOMLOGLEVELMANAGER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + +#include +#include +#include +#if ! defined (LOG4CPLUS_SINGLE_THREADED) +#include +#endif + +namespace log4cplus { + +namespace internal { + + +/** + * Custom log level manager used by C API. + */ +class LOG4CPLUS_PRIVATE CustomLogLevelManager + : virtual public LogLevelTranslator +{ +protected: +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + mutable std::shared_mutex mtx; +#endif + bool pushed_methods; + std::map ll2nm; + std::map> nm2ll; + +public: + CustomLogLevelManager (); + virtual ~CustomLogLevelManager (); + + bool add(LogLevel ll, tstring const &nm); + + bool remove(LogLevel ll, tstring const &nm); + +protected: + virtual log4cplus::tstring const & toString (LogLevel ll) const; + + virtual LogLevel fromString (const log4cplus::tstring_view& s) const; +}; + +LOG4CPLUS_PRIVATE CustomLogLevelManager & getCustomLogLevelManager (); + +} // namespace internal + +} // namespace log4cplus + + +#endif // LOG4CPLUS_INTERNAL_CUSTOMLOGLEVELMANAGER_HEADER diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/cygwin-win32.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/cygwin-win32.h new file mode 100644 index 0000000..1b8ae6e --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/cygwin-win32.h @@ -0,0 +1,55 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: cygwin-win32.h +// Created: 7/2011 +// Author: Vaclav Zeman +// +// Copyright (C) 2011-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +#if ! defined (LOG4CPLUS_CONFIG_CYGWIN_WIN32_H) +#define LOG4CPLUS_CONFIG_CYGWIN_WIN32_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (__CYGWIN__) + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + + +namespace log4cplus { namespace cygwin { + +unsigned long get_current_win32_thread_id (); +void output_debug_stringW (wchar_t const *); + +} } // namespace log4cplus { namespace cygwin { + + +#endif // defined (__CYGWIN__) +#endif // LOG4CPLUS_CONFIG_CYGWIN_WIN32_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/env.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/env.h new file mode 100644 index 0000000..615e6f0 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/env.h @@ -0,0 +1,98 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: env.h +// Created: 7/2010 +// Author: Vaclav Haisman +// +// +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_INTERNAL_ENV_H +#define LOG4CPLUS_INTERNAL_ENV_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + +#if defined (_WIN32) +#include +#endif +#ifdef LOG4CPLUS_HAVE_SYS_TYPES_H +#include +#endif +#ifdef LOG4CPLUS_HAVE_UNISTD_H +#include +#endif + + +namespace log4cplus { namespace internal { + + +//! Get environment variable value. +bool get_env_var (tstring & value, tstring const & name); + +//! Parse a string as a boolean value. +bool parse_bool (bool & val, tstring const & str); + +//! Parse a path into path components. +bool split_path (std::vector & components, std::size_t & special, + tstring const & path); + +//! Makes directories leading to file. +void make_dirs (tstring const & file_path); + +inline +#if defined (_WIN32) +DWORD +get_process_id () +{ + return GetCurrentProcessId (); +} + +#elif defined (LOG4CPLUS_HAVE_GETPID) +pid_t +get_process_id () +{ + return getpid (); +} + +#else +int +get_process_id () +{ + return 0; +} + +#endif + + +} } // namespace log4cplus { namespace internal { + + +#endif // LOG4CPLUS_INTERNAL_ENV_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/internal.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/internal.h new file mode 100644 index 0000000..2483d05 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/internal.h @@ -0,0 +1,244 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: internal.h +// Created: 1/2009 +// Author: Vaclav Haisman +// +// +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +/** @file + * This header contains declaration internal to log4cplus. They must never be + * visible from user accesible headers or exported in DLL/shared libray. + */ + + +#ifndef LOG4CPLUS_INTERNAL_INTERNAL_HEADER_ +#define LOG4CPLUS_INTERNAL_INTERNAL_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace log4cplus { + +namespace internal { + + +//! Canonical empty string. It is used when the need to return empty string +//! by reference arises. +extern log4cplus::tstring const empty_str; + + +struct gft_scratch_pad +{ + gft_scratch_pad (); + ~gft_scratch_pad (); + + void + reset () + { + uc_q_str_valid = false; + q_str_valid = false; + s_str_valid = false; + ret.clear (); + } + + log4cplus::tstring q_str; + log4cplus::tstring uc_q_str; + log4cplus::tstring s_str; + log4cplus::tstring ret; + log4cplus::tstring fmt; + log4cplus::tstring tmp; + std::vector buffer; + bool uc_q_str_valid; + bool q_str_valid; + bool s_str_valid; +}; + + +struct appender_sratch_pad +{ + appender_sratch_pad (); + ~appender_sratch_pad (); + + tostringstream oss; + tstring str; + std::string chstr; +}; + + +//! Per thread data. +struct per_thread_data +{ + per_thread_data (); + ~per_thread_data (); + + tstring macros_str; + tostringstream macros_oss; + tostringstream layout_oss; + DiagnosticContextStack ndc_dcs; + MappedDiagnosticContextMap mdc_map; + log4cplus::tstring thread_name; + log4cplus::tstring thread_name2; + gft_scratch_pad gft_sp; + appender_sratch_pad appender_sp; + log4cplus::tstring faa_str; + log4cplus::tstring ll_str; + spi::InternalLoggingEvent forced_log_ev; + std::FILE * fnull; + log4cplus::helpers::snprintf_buf snprintf_buf; +}; + + +per_thread_data * alloc_ptd (); + +// TLS key whose value is pointer struct per_thread_data. +extern log4cplus::thread::impl::tls_key_type tls_storage_key; + + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) \ + && defined (LOG4CPLUS_THREAD_LOCAL_VAR) + +extern LOG4CPLUS_THREAD_LOCAL_VAR per_thread_data * ptd; + + +inline +void +set_ptd (per_thread_data * p) +{ + ptd = p; +} + + +inline +per_thread_data * +get_ptd (bool alloc = true) +{ + if (LOG4CPLUS_UNLIKELY (! ptd && alloc)) + return alloc_ptd (); + + // The assert() does not belong here. get_ptd() might be called by + // cleanup code that can handle the returned NULL pointer. + //assert (ptd); + + return ptd; +} + + +#else // defined (LOG4CPLUS_THREAD_LOCAL_VAR) + + +inline +void +set_ptd (per_thread_data * p) +{ + thread::impl::tls_set_value (tls_storage_key, p); +} + + +inline +per_thread_data * +get_ptd (bool alloc = true) +{ + per_thread_data * ptd + = reinterpret_cast( + thread::impl::tls_get_value (tls_storage_key)); + + if (LOG4CPLUS_UNLIKELY (! ptd && alloc)) + return alloc_ptd (); + + return ptd; +} + + +#endif // defined (LOG4CPLUS_THREAD_LOCAL_VAR) + + +inline +tstring & +get_thread_name_str () +{ + return get_ptd ()->thread_name; +} + + +inline +tstring & +get_thread_name2_str () +{ + return get_ptd ()->thread_name2; +} + + +inline +gft_scratch_pad & +get_gft_scratch_pad () +{ + return get_ptd ()->gft_sp; +} + + +inline +appender_sratch_pad & +get_appender_sp () +{ + return get_ptd ()->appender_sp; +} + + +} // namespace internal { + + +namespace detail +{ + +LOG4CPLUS_EXPORT void clear_tostringstream (tostringstream &); + +} // namespace detail + + +} // namespace log4cplus { + + +#endif // LOG4CPLUS_INTERNAL_INTERNAL_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/socket.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/socket.h new file mode 100644 index 0000000..058b48f --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/internal/socket.h @@ -0,0 +1,219 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: socket.h +// Created: 1/2010 +// Author: Vaclav Haisman +// +// +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +/** @file + * This header contains declaration internal to log4cplus. They must never be + * visible from user accesible headers or exported in DLL/shared libray. + */ + + +#ifndef LOG4CPLUS_INTERNAL_SOCKET_H_ +#define LOG4CPLUS_INTERNAL_SOCKET_H_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + +#if defined(_WIN32) +#include +#endif +#include + +#include +#ifdef LOG4CPLUS_HAVE_ERRNO_H +#include +#endif + +#ifdef LOG4CPLUS_HAVE_UNISTD_H +#include +#endif + +#if defined (LOG4CPLUS_HAVE_NETDB_H) +#include +#endif + + +namespace log4cplus { + +namespace helpers { + +#if defined(_WIN32) +typedef SOCKET os_socket_type; +os_socket_type const INVALID_OS_SOCKET_VALUE = INVALID_SOCKET; + +struct ADDRINFOT_deleter +{ + void + operator () (ADDRINFOA * ptr) const + { + FreeAddrInfoA(ptr); + } + + void + operator () (ADDRINFOW * ptr) const + { + FreeAddrInfoW(ptr); + } +}; + + +struct socket_closer +{ + void + operator () (SOCKET s) + { + if (s && s != INVALID_OS_SOCKET_VALUE) + { + DWORD const eno = WSAGetLastError(); + ::closesocket(s); + WSASetLastError(eno); + } + } +}; + + +#else +typedef int os_socket_type; +os_socket_type const INVALID_OS_SOCKET_VALUE = -1; + + +struct addrinfo_deleter +{ + void + operator () (struct addrinfo * ptr) const + { + freeaddrinfo(ptr); + } +}; + + +struct socket_closer +{ + void + operator () (os_socket_type s) + { + if (s >= 0) + { + int const eno = errno; + close(s); + errno = eno; + } + } +}; + +#endif + + +struct socket_holder +{ + os_socket_type sock; + + socket_holder() + : sock(INVALID_OS_SOCKET_VALUE) + { } + + socket_holder(os_socket_type s) + : sock(s) + { } + + ~socket_holder() + { + socket_closer()(sock); + } + + void + reset(os_socket_type s = INVALID_OS_SOCKET_VALUE) + { + if (sock != INVALID_OS_SOCKET_VALUE) + socket_closer()(sock); + + sock = s; + } + + os_socket_type + detach() + { + os_socket_type s = sock; + sock = INVALID_OS_SOCKET_VALUE; + return s; + } + + socket_holder(socket_holder &&) = delete; + socket_holder(socket_holder const &) = delete; + + socket_holder operator = (socket_holder &&) = delete; + socket_holder operator = (socket_holder const &) = delete; +}; + + +static inline +os_socket_type +to_os_socket (SOCKET_TYPE const & x) +{ + return static_cast(x); +} + + +static inline +SOCKET_TYPE +to_log4cplus_socket (os_socket_type const & x) +{ + return static_cast(x); +} + + +static inline +void +set_last_socket_error (int err) +{ + errno = err; +} + + +static inline +int +get_last_socket_error () +{ + return errno; +} + + +} // namespace helpers { + +} // namespace log4cplus { + + +#endif // LOG4CPLUS_INTERNAL_SOCKET_H_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/layout.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/layout.h new file mode 100644 index 0000000..a0110e4 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/layout.h @@ -0,0 +1,645 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: Layout.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_LAYOUT_HEADER_ +#define LOG4CPLUS_LAYOUT_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +#include +#include + + +namespace log4cplus { + + // Forward Declarations + namespace pattern + { + + class PatternConverter; + + } + + + namespace helpers + { + + class Properties; + + } + + + namespace spi + { + + class InternalLoggingEvent; + + } + + + /** + * This class is used to layout strings sent to an {@link + * log4cplus::Appender}. + */ + class LOG4CPLUS_EXPORT Layout + { + public: + Layout(); + Layout(const helpers::Properties& properties); + virtual ~Layout() = 0; + + virtual void formatAndAppend(log4cplus::tostream& output, + const log4cplus::spi::InternalLoggingEvent& event) = 0; + + protected: + LogLevelManager& llmCache; + + private: + // Disable copy + Layout(const Layout&); + Layout& operator=(Layout const &); + }; + + + + /** + * SimpleLayout consists of the LogLevel of the log statement, + * followed by " - " and then the log message itself. For example, + * + *
+     *         DEBUG - Hello world
+     * 
+ * + * {@link PatternLayout} offers a much more powerful alternative. + */ + class LOG4CPLUS_EXPORT SimpleLayout + : public Layout + { + public: + SimpleLayout(); + SimpleLayout(const log4cplus::helpers::Properties& properties); + virtual ~SimpleLayout(); + + virtual void formatAndAppend(log4cplus::tostream& output, + const log4cplus::spi::InternalLoggingEvent& event); + + private: + // Disallow copying of instances of this class + SimpleLayout(const SimpleLayout&); + SimpleLayout& operator=(const SimpleLayout&); + }; + + + + /** + * TTCC layout format consists of time, thread, Logger and nested + * diagnostic context information, hence the name. + * + * The time format depends on the DateFormat used. Use the + * Use_gmtime to specify whether messages should be logged + * using localtime or gmtime. There are also + * ThreadPrinting, CategoryPrefixing and + * ContextPrinting properties to turn on and off thread name, + * logger name and NDC context printing respectively. + * + * Here is an example TTCCLayout output: + * + * ~~~~ + * 1 [0x60004dca0] WARN test.TestThread <> - Thread-3 TestThread.run()- Starting... + * 1 [0x60004dca0] TRACE SlowObject - ENTER: SlowObject::doSomething() + * 2 [0x60004b030] INFO SlowObject - Actually doing something...1, 2, 3, testing...DONE + * 2 [0x60004b130] INFO SlowObject - Actually doing something... + * 2 [0x60004b030] TRACE SlowObject - EXIT: SlowObject::doSomething() + * 2 [0x60004b030] TRACE SlowObject - ENTER: SlowObject::doSomething() + * 3 [0x60004b130] INFO SlowObject - Actually doing something...1, 2, 3, testing...DONE + * 3 [0x60004cad0] INFO SlowObject - Actually doing something... + * ~~~~ + * + * The first field is the number of milliseconds elapsed since + * the start of the program. + * + * The second field is the thread outputting the log + * statement. (The value is the same as that of the `t` formatter + * for PatternLayout.) + * + * The third field is the LogLevel. + * + * The fourth field is the logger to which the statement belongs. + * + * The fifth field (just before the '-') is the nested + * diagnostic context. Note the nested diagnostic context may be + * empty as in the first two statements. The text after the '-' + * is the message of the statement. + * + * PatternLayout offers a much more flexible alternative. + */ + class LOG4CPLUS_EXPORT TTCCLayout + : public Layout + { + public: + TTCCLayout(bool use_gmtime = false, bool thread_printing = true, + bool category_prefixes = true, bool context_printing = true); + TTCCLayout(const log4cplus::helpers::Properties& properties); + virtual ~TTCCLayout(); + + virtual void formatAndAppend(log4cplus::tostream& output, + const log4cplus::spi::InternalLoggingEvent& event); + + bool getThreadPrinting() const; + void setThreadPrinting(bool); + + bool getCategoryPrefixing() const; + void setCategoryPrefixing(bool); + + bool getContextPrinting() const; + void setContextPrinting(bool); + + protected: + log4cplus::tstring dateFormat; + bool use_gmtime = false; + bool thread_printing = true; + bool category_prefixing = true; + bool context_printing = true; + + private: + // Disallow copying of instances of this class + TTCCLayout(const TTCCLayout&); + TTCCLayout& operator=(const TTCCLayout&); + }; + + + LOG4CPLUS_EXPORT helpers::Time const & getTTCCLayoutTimeBase (); + + + /** + * A flexible layout configurable with pattern string. + * + * The goal of this class is to format a InternalLoggingEvent and return + * the results as a string. The results depend on the conversion + * pattern. + * + * The conversion pattern is closely related to the conversion + * pattern of the printf function in C. A conversion pattern is + * composed of literal text and format control expressions called + * conversion specifiers. + * + * You are free to insert any literal text within the conversion + * pattern. + * + * Each conversion specifier starts with a percent sign (%%) and is + * followed by optional format modifiers and a conversion + * character. The conversion character specifies the type of + * data, e.g. Logger, LogLevel, date, thread name. The format + * modifiers control such things as field width, padding, left and + * right justification. The following is a simple example. + * + * Let the conversion pattern be `"%-5p [%t]: %m%n"` and assume + * that the log4cplus environment was set to use a PatternLayout. Then the + * statements + * + * ~~~~{.c} + * Logger root = Logger.getRoot(); + * LOG4CPLUS_DEBUG(root, "Message 1"); + * LOG4CPLUS_WARN(root, "Message 2"); + * ~~~~ + * + * would yield the output + * + * ~~~~ + * DEBUG [main]: Message 1 + * WARN [main]: Message 2 + * ~~~~ + * + * Note that there is no explicit separator between text and + * conversion specifiers. The pattern parser knows when it has reached + * the end of a conversion specifier when it reads a conversion + * character. In the example above the conversion specifier + * "%-5p" means the LogLevel of the logging event should be left + * justified to a width of five characters. + * + * The recognized conversion characters are + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Conversion CharacterEffect
bUsed to output file name component of path name. + * E.g. main.cxx from path ../../main.cxx.
cUsed to output the logger of the logging event. The + * logger conversion specifier can be optionally followed by + * precision specifier, that is a decimal constant in + * brackets. + * + * If a precision specifier is given, then only the corresponding + * number of right most components of the logger name will be + * printed. By default the logger name is printed in full. + * + * For example, for the logger name "a.b.c" the pattern + * %c{2} will output "b.c". + * + *
dUsed to output the date of the logging event in UTC. + * + * The date conversion specifier may be followed by a date format + * specifier enclosed between braces. For example, %%d{%%H:%%M:%%s} + * or %%d{%%d %%b %%Y %%H:%%M:%%s}. If no date format + * specifier is given then %%d{%%d %%m %%Y %%H:%%M:%%s} + * is assumed. + * + * The Following format options are possible: + *
    + *
  • %%a -- Abbreviated weekday name
  • + *
  • %%A -- Full weekday name
  • + *
  • %%b -- Abbreviated month name
  • + *
  • %%B -- Full month name
  • + *
  • %%c -- Standard date and time string
  • + *
  • %%d -- Day of month as a decimal(1-31)
  • + *
  • %%H -- Hour(0-23)
  • + *
  • %%I -- Hour(1-12)
  • + *
  • %%j -- Day of year as a decimal(1-366)
  • + *
  • %%m -- Month as decimal(1-12)
  • + *
  • %%M -- Minute as decimal(0-59)
  • + *
  • %%p -- Locale's equivalent of AM or PM
  • + *
  • %%q -- milliseconds as decimal(0-999) -- Log4CPLUS specific + *
  • %%Q -- fractional milliseconds as decimal(0-999.999) -- Log4CPLUS specific + *
  • %%S -- Second as decimal(0-59)
  • + *
  • %%U -- Week of year, Sunday being first day(0-53)
  • + *
  • %%w -- Weekday as a decimal(0-6, Sunday being 0)
  • + *
  • %%W -- Week of year, Monday being first day(0-53)
  • + *
  • %%x -- Standard date string
  • + *
  • %%X -- Standard time string
  • + *
  • %%y -- Year in decimal without century(0-99)
  • + *
  • %%Y -- Year including century as decimal
  • + *
  • %%Z -- Time zone name
  • + *
  • %% -- The percent sign
  • + *
+ * + * Lookup the documentation for the strftime() function + * found in the <ctime> header for more information. + *
DUsed to output the date of the logging event in local time. + * + * All of the above information applies. + *
EUsed to output the value of a given environment variable. The + * name of is supplied as an argument in brackets. If the variable does + * exist then empty string will be used. + * + * For example, the pattern %E{HOME} will output the contents + * of the HOME environment variable. + *
FUsed to output the file name where the logging request was + * issued. + * + * NOTE Unlike log4j, there is no performance penalty for + * calling this method.
hUsed to output the hostname of this system (as returned + * by gethostname(2)). + * + * NOTE The hostname is only retrieved once at + * initialization. + * + *
HUsed to output the fully-qualified domain name of this + * system (as returned by gethostbyname(2) for the hostname + * returned by gethostname(2)). + * + * NOTE The hostname is only retrieved once at + * initialization. + * + *
lEquivalent to using "%F:%L" + * + * NOTE: Unlike log4j, there is no performance penalty for + * calling this method. + * + *
LUsed to output the line number from where the logging request + * was issued. + * + * NOTE: Unlike log4j, there is no performance penalty for + * calling this method. + * + *
mUsed to output the application supplied message associated with + * the logging event.
MUsed to output function name using + * __FUNCTION__ or similar macro. + * + * NOTE The __FUNCTION__ macro is not + * standard but it is common extension provided by all compilers + * (as of 2010). In case it is missing or in case this feature + * is disabled using the + * LOG4CPLUS_DISABLE_FUNCTION_MACRO macro, %M + * expands to an empty string.
nOutputs the platform dependent line separator character or + * characters. + *
pUsed to output the LogLevel of the logging event.
rUsed to output miliseconds since program start + * of the logging event.
tUsed to output the thread ID of the thread that generated + * the logging event. (This is either `pthread_t` value returned + * by `pthread_self()` on POSIX platforms or thread ID returned + * by `GetCurrentThreadId()` on Windows.)
TUsed to output alternative name of the thread that generated the + * logging event.
iUsed to output the process ID of the process that generated the + * logging event.
xUsed to output the NDC (nested diagnostic context) associated + * with the thread that generated the logging event. + *
XUsed to output the MDC (mapped diagnostic context) + * associated with the thread that generated the logging + * event. It takes optional key parameter. Without the key + * paramter (%%X), it outputs the whole MDC map. With the key + * (%%X{key}), it outputs just the key's value. + *
"%%"The sequence "%%" outputs a single percent sign. + *
+ * + * By default the relevant information is output as is. However, + * with the aid of format modifiers it is possible to change the + * minimum field width, the maximum field width and justification. + * + * The optional format modifier is placed between the percent sign + * and the conversion character. + * + * The first optional format modifier is the left justification + * flag which is just the minus (-) character. Then comes the + * optional minimum field width modifier. This is a decimal + * constant that represents the minimum number of characters to + * output. If the data item requires fewer characters, it is padded on + * either the left or the right until the minimum width is + * reached. The default is to pad on the left (right justify) but you + * can specify right padding with the left justification flag. The + * padding character is space. If the data item is larger than the + * minimum field width, the field is expanded to accommodate the + * data. The value is never truncated. + * + * This behavior can be changed using the maximum field + * width modifier which is designated by a period followed by a + * decimal constant. If the data item is longer than the maximum + * field, then the extra characters are removed from the + * beginning of the data item and not from the end. For + * example, it the maximum field width is eight and the data item is + * ten characters long, then the first two characters of the data item + * are dropped. This behavior deviates from the printf function in C + * where truncation is done from the end. + * + * Below are various format modifier examples for the logger + * conversion specifier. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Format modifierleft justifyminimum widthmaximum widthcomment
%20cfalse20noneLeft pad with spaces if the logger name is less than 20 + * characters long. + *
%-20c true 20 none Right pad with + * spaces if the logger name is less than 20 characters long. + *
%.30cNAnone30Truncate from the beginning if the logger name is longer than 30 + * characters. + *
%20.30cfalse2030Left pad with spaces if the logger name is shorter than 20 + * characters. However, if logger name is longer than 30 characters, + * then truncate from the beginning. + *
%-20.30ctrue2030Right pad with spaces if the logger name is shorter than 20 + * characters. However, if logger name is longer than 30 characters, + * then truncate from the beginning. + *
+ * + * Below are some examples of conversion patterns. + * + *
+ * + *
"%r [%t] %-5p %c %x - %m%n" + *
This is essentially the TTCC layout. + * + *
"%-6r [%15.15t] %-5p %30.30c %x - %m%n" + * + *
Similar to the TTCC layout except that the relative time is + * right padded if less than 6 digits, thread name is right padded if + * less than 15 characters and truncated if longer and the logger + * name is left padded if shorter than 30 characters and truncated if + * longer. + * + *
+ * + * The above text is largely inspired from Peter A. Darnell and + * Philip E. Margolis' highly recommended book "C -- a Software + * Engineering Approach", ISBN 0-387-97389-3. + * + *

Properties

+ * + *
+ *
NDCMaxDepth
+ *
This property limits how many deepest NDC components will + * be printed by %%x specifier.
+ * + *
ConversionPattern
+ *
This property specifies conversion pattern.
+ *
+ * + */ + class LOG4CPLUS_EXPORT PatternLayout + : public Layout + { + public: + // Ctors and dtor + PatternLayout(const log4cplus::tstring& pattern); + PatternLayout(const log4cplus::helpers::Properties& properties); + virtual ~PatternLayout(); + + virtual void formatAndAppend(log4cplus::tostream& output, + const log4cplus::spi::InternalLoggingEvent& event); + + protected: + void init(const log4cplus::tstring& pattern, unsigned ndcMaxDepth = 0); + + // Data + log4cplus::tstring pattern; + std::vector > parsedPattern; + + private: + // Disallow copying of instances of this class + PatternLayout(const PatternLayout&); + PatternLayout& operator=(const PatternLayout&); + }; + + + +} // end namespace log4cplus + +#endif // LOG4CPLUS_LAYOUT_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/log4cplus.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/log4cplus.h new file mode 100644 index 0000000..e3ddb32 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/log4cplus.h @@ -0,0 +1,66 @@ +// Copyright (C) 2015-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_LOG4CPLUS_HXX +#define LOG4CPLUS_LOG4CPLUS_HXX + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined (LOG4CPLUS_WITH_UNIT_TESTS) +namespace log4cplus +{ + +LOG4CPLUS_EXPORT int unit_tests_main (int argc, char* argv[]); + +} // namespace log4cplus +#endif + +#endif // LOG4CPLUS_LOG4CPLUS_HXX diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/log4judpappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/log4judpappender.h new file mode 100644 index 0000000..a6422d5 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/log4judpappender.h @@ -0,0 +1,91 @@ +// -*- C++ -*- +// Module: LOG4CPLUS +// File: log4judpappender.h +// Created: 7/2012 +// Author: Siva Chandran P +// +// +// Copyright 2012-2017 Siva Chandran P +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @file */ + +#ifndef LOG4CPLUS_LOG4J_UDP_APPENDER_HEADER_ +#define LOG4CPLUS_LOG4J_UDP_APPENDER_HEADER_ + +#include +#include +#include + +namespace log4cplus { + + /** + * Sends log events as Log4j XML to a remote a log server. + * + * The Log4jUdpAppender has the following properties: + * + *
    + *
  • Remote logging is non-intrusive as far as the log event + * is concerned. In other words, the event will be logged with + * the same time stamp, NDC, location info as if it were logged + * locally by the client.
  • + * + *
  • Remote logging uses the UDP protocol.
  • + *
+ * + *

Properties

+ *
+ *
host
+ *
Remote host name to connect and send events to.
+ * + *
port
+ *
Port on remote host to send events to. Default is 5000.
+ * + *
IPv6
+ *
Boolean value specifying whether to use IPv6 (true) or IPv4 + * (false). Default value is false.
+ * + *
+ */ + class LOG4CPLUS_EXPORT Log4jUdpAppender : public Appender { + public: + // Ctors + Log4jUdpAppender(const log4cplus::tstring& host, int port, + bool ipv6 = false); + Log4jUdpAppender(const log4cplus::helpers::Properties & properties); + + // Dtor + ~Log4jUdpAppender(); + + // Methods + virtual void close(); + + protected: + void openSocket(); + virtual void append(const spi::InternalLoggingEvent& event); + + // Data + log4cplus::helpers::Socket socket; + log4cplus::tstring host; + int port; + bool ipv6 = false; + + private: + // Disallow copying of instances of this class + Log4jUdpAppender(const Log4jUdpAppender&); + Log4jUdpAppender& operator=(const Log4jUdpAppender&); + }; +} // end namespace log4cplus + +#endif // LOG4CPLUS_LOG4J_UDP_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/logger.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/logger.h new file mode 100644 index 0000000..de0b70c --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/logger.h @@ -0,0 +1,325 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: logger.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file + * This header defines the Logger class and the logging macros. */ + +#ifndef LOG4CPLUS_LOGGERHEADER_ +#define LOG4CPLUS_LOGGERHEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +#include + + +namespace log4cplus +{ + // Forward declarations + + class Appender; + class Hierarchy; + class HierarchyLocker; + class DefaultLoggerFactory; + + namespace spi + { + + class LoggerImpl; + + } + + + /** \typedef std::vector LoggerList + * This is a list of {@link Logger Loggers}. */ + typedef std::vector LoggerList; + + + /** + * This is the central class in the log4cplus package. One of the + * distintive features of log4cplus are hierarchical loggers and their + * evaluation. + */ + class LOG4CPLUS_EXPORT Logger + : public log4cplus::spi::AppenderAttachable + { + public: + // Static Methods + /** + * Returns true if the named logger exists + * (in the default hierarchy). + * + * @param name The name of the logger to search for. + */ + static bool exists(const log4cplus::tstring_view& name); + + /* + * Returns all the currently defined loggers in the default + * hierarchy. + * + * The root logger is not included in the returned + * list. + */ + static LoggerList getCurrentLoggers(); + + /** + * Return the default Hierarchy instance. + */ + static Hierarchy& getDefaultHierarchy(); + + /** + * Retrieve a logger with name name. If the named + * logger already exists, then the existing instance will be returned. + * Otherwise, a new instance is created. + * + * By default, loggers do not have a set LogLevel but inherit + * it from the hierarchy. This is one of the central features of + * log4cplus. + * + * @param name The name of the logger to retrieve. + */ + static Logger getInstance(const log4cplus::tstring_view& name); + + /** + * Like getInstance() except that the type of logger + * instantiated depends on the type returned by the {@link + * spi::LoggerFactory#makeNewLoggerInstance} method of the + * factory parameter. + * + * This method is intended to be used by sub-classes. + * + * @param name The name of the logger to retrieve. + * @param factory A {@link spi::LoggerFactory} implementation that will + * actually create a new Instance. + */ + static Logger getInstance(const log4cplus::tstring_view& name, + spi::LoggerFactory& factory); + + /** + * Return the root of the default logger hierrachy. + * + * The root logger is always instantiated and available. It's + * name is "root". + * + * Nevertheless, calling {@link #getInstance + * Logger.getInstance("root")} does not retrieve the root logger + * but a logger just under root named "root". + */ + static Logger getRoot(); + + /** + * Calling this method will safely close and remove all + * appenders in all the loggers including root contained in the + * default hierachy. + * + * Some appenders such as SocketAppender need to be closed before the + * application exits. Otherwise, pending logging events might be + * lost. + * + * The shutdown method is careful to close nested + * appenders before closing regular appenders. This is allows + * configurations where a regular appender is attached to a logger + * and again to a nested appender. + */ + static void shutdown(); + + // Non-Static Methods + /** + * If assertionVal parameter is false, then + * logs msg with FATAL_LOG_LEVEL log level. + * + * @param assertionVal Truth value of assertion condition. + * @param msg The message to print if assertion is + * false. + */ + void assertion(bool assertionVal, const log4cplus::tstring_view& msg) const; + + /** + * Close all attached appenders implementing the AppenderAttachable + * interface. + */ + void closeNestedAppenders() const; + + /** + * Check whether this logger is enabled for a given + * LogLevel passed as parameter. + * + * @return boolean True if this logger is enabled for ll. + */ + bool isEnabledFor(LogLevel ll) const; + + /** + * This generic form is intended to be used by wrappers. + */ + void log(LogLevel ll, const log4cplus::tstring_view& message, + const char* file = LOG4CPLUS_CALLER_FILE (), + int line = LOG4CPLUS_CALLER_LINE (), + const char* function = LOG4CPLUS_CALLER_FUNCTION ()) const; + + void log(spi::InternalLoggingEvent const &) const; + + /** + * This method creates a new logging event and logs the event + * without further checks. + */ + void forcedLog(LogLevel ll, const log4cplus::tstring_view& message, + const char* file = LOG4CPLUS_CALLER_FILE (), + int line = LOG4CPLUS_CALLER_LINE (), + const char* function = LOG4CPLUS_CALLER_FUNCTION ()) const; + + void forcedLog(spi::InternalLoggingEvent const &) const; + + /** + * Call the appenders in the hierrachy starting at + * this. If no appenders could be found, emit a + * warning. + * + * This method calls all the appenders inherited from the + * hierarchy circumventing any evaluation of whether to log or not + * to log the particular log request. + * + * @param event the event to log. + */ + void callAppenders(const spi::InternalLoggingEvent& event) const; + + /** + * Starting from this logger, search the logger hierarchy for a + * "set" LogLevel and return it. Otherwise, return the LogLevel of the + * root logger. + * + * The Logger class is designed so that this method executes as + * quickly as possible. + */ + LogLevel getChainedLogLevel() const; + + /** + * Returns the assigned LogLevel, if any, for this Logger. + * + * @return LogLevel - the assigned LogLevel, can be NOT_SET_LOG_LEVEL. + */ + LogLevel getLogLevel() const; + + /** + * Set the LogLevel of this Logger. + */ + void setLogLevel(LogLevel ll); + + /** + * Return the the {@link Hierarchy} where this Logger instance is + * attached. + */ + Hierarchy& getHierarchy() const; + + /** + * Return the logger name. + */ + log4cplus::tstring const & getName() const; + + /** + * Get the additivity flag for this Logger instance. + */ + bool getAdditivity() const; + + /** + * Set the additivity flag for this Logger instance. + */ + void setAdditivity(bool additive); + + // AppenderAttachable Methods + virtual void addAppender(SharedAppenderPtr newAppender); + + virtual SharedAppenderPtrList getAllAppenders(); + + virtual SharedAppenderPtr getAppender(const log4cplus::tstring& name); + + virtual void removeAllAppenders(); + + virtual void removeAppender(SharedAppenderPtr appender); + + virtual void removeAppender(const log4cplus::tstring& name); + + Logger () LOG4CPLUS_NOEXCEPT; + Logger(const Logger& rhs) LOG4CPLUS_NOEXCEPT; + Logger& operator=(const Logger& rhs) LOG4CPLUS_NOEXCEPT; + Logger (Logger && rhs) LOG4CPLUS_NOEXCEPT; + Logger & operator = (Logger && rhs) LOG4CPLUS_NOEXCEPT; + virtual ~Logger(); + + void swap (Logger &) LOG4CPLUS_NOEXCEPT; + + /** + * Used to retrieve the parent of this Logger in the + * Logger tree. + */ + Logger getParent() const; + + protected: + // Data + /** This is a pointer to the implementation class. */ + spi::LoggerImpl * value = nullptr; + + private: + // Ctors + /** + * This constructor created a new Logger instance + * with a pointer to a Logger implementation. + * + * You should not create loggers directly. + * + * @param ptr A pointer to the Logger implementation. This value + * cannot be NULL. + */ + Logger(spi::LoggerImpl * ptr) LOG4CPLUS_NOEXCEPT; + + // Friends + friend class log4cplus::spi::LoggerImpl; + friend class log4cplus::Hierarchy; + friend class log4cplus::HierarchyLocker; + friend class log4cplus::DefaultLoggerFactory; + }; + + + /** + * This class is used to create the default implementation of + * the Logger class. + */ + class LOG4CPLUS_EXPORT DefaultLoggerFactory : public spi::LoggerFactory { + public: + Logger makeNewLoggerInstance(const log4cplus::tstring_view& name, Hierarchy& h); + + protected: + virtual spi::LoggerImpl * makeNewLoggerImplInstance( + const log4cplus::tstring_view& name, Hierarchy& h); + }; + + +} // end namespace log4cplus + + +#endif // LOG4CPLUS_LOGGERHEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/loggingmacros.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/loggingmacros.h new file mode 100644 index 0000000..cd33918 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/loggingmacros.h @@ -0,0 +1,405 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loggingmacros.h +// Created: 8/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file + * This header defines the logging macros. */ + +#ifndef LOG4CPLUS_LOGGING_MACROS_HEADER_ +#define LOG4CPLUS_LOGGING_MACROS_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + + +#if defined(_MSC_VER) +#define LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + __pragma (warning (push)) \ + __pragma (warning (disable:4127)) + +#define LOG4CPLUS_RESTORE_DOWHILE_WARNING() \ + __pragma (warning (pop)) + +#else +#define LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() /* empty */ +#define LOG4CPLUS_RESTORE_DOWHILE_WARNING() /* empty */ + +#endif + +#define LOG4CPLUS_DOWHILE_NOTHING() \ + LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + do { } while (0) \ + LOG4CPLUS_RESTORE_DOWHILE_WARNING() + +#if defined(LOG4CPLUS_DISABLE_FATAL) && !defined(LOG4CPLUS_DISABLE_ERROR) +#define LOG4CPLUS_DISABLE_ERROR +#endif +#if defined(LOG4CPLUS_DISABLE_ERROR) && !defined(LOG4CPLUS_DISABLE_WARN) +#define LOG4CPLUS_DISABLE_WARN +#endif +#if defined(LOG4CPLUS_DISABLE_WARN) && !defined(LOG4CPLUS_DISABLE_INFO) +#define LOG4CPLUS_DISABLE_INFO +#endif +#if defined(LOG4CPLUS_DISABLE_INFO) && !defined(LOG4CPLUS_DISABLE_DEBUG) +#define LOG4CPLUS_DISABLE_DEBUG +#endif +#if defined(LOG4CPLUS_DISABLE_DEBUG) && !defined(LOG4CPLUS_DISABLE_TRACE) +#define LOG4CPLUS_DISABLE_TRACE +#endif + + +namespace log4cplus +{ + +namespace detail +{ + + +inline +Logger +macros_get_logger (Logger const & logger) +{ + return logger; +} + + +inline +Logger const & +macros_get_logger (Logger & logger) +{ + return logger; +} + + +inline +Logger +macros_get_logger (Logger && logger) +{ + return std::move (logger); +} + +inline +Logger +macros_get_logger (tstring_view const & logger) +{ + return Logger::getInstance (logger); +} + + +inline +Logger +macros_get_logger (tchar const * logger) +{ + return Logger::getInstance (logger); +} + + +LOG4CPLUS_EXPORT void clear_tostringstream (tostringstream &); + + +LOG4CPLUS_EXPORT log4cplus::tostringstream & get_macro_body_oss (); +LOG4CPLUS_EXPORT log4cplus::helpers::snprintf_buf & get_macro_body_snprintf_buf (); +LOG4CPLUS_EXPORT void macro_forced_log (log4cplus::Logger const &, + log4cplus::LogLevel, log4cplus::tstring_view const &, char const *, int, + char const *); +LOG4CPLUS_EXPORT void macro_forced_log (log4cplus::Logger const &, + log4cplus::LogLevel, log4cplus::tchar const *, char const *, int, + char const *); + + + +} // namespace detail + +} // namespace log4cplus + + +#undef LOG4CPLUS_MACRO_FUNCTION +#define LOG4CPLUS_MACRO_FUNCTION() nullptr +#if ! defined (LOG4CPLUS_DISABLE_FUNCTION_MACRO) +# if defined (LOG4CPLUS_HAVE_FUNCSIG_MACRO) +# undef LOG4CPLUS_MACRO_FUNCTION +# define LOG4CPLUS_MACRO_FUNCTION() __FUNCSIG__ +# elif defined (LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO) +# undef LOG4CPLUS_MACRO_FUNCTION +# define LOG4CPLUS_MACRO_FUNCTION() __PRETTY_FUNCTION__ +# elif defined (LOG4CPLUS_HAVE_FUNCTION_MACRO) +# undef LOG4CPLUS_MACRO_FUNCTION +# define LOG4CPLUS_MACRO_FUNCTION() __FUNCTION__ +# elif defined (LOG4CPLUS_HAVE_FUNC_SYMBOL) +# undef LOG4CPLUS_MACRO_FUNCTION +# define LOG4CPLUS_MACRO_FUNCTION() __func__ +# endif +#endif + +#undef LOG4CPLUS_MACRO_FILE +#define LOG4CPLUS_MACRO_FILE() nullptr +#if ! defined (LOG4CPLUS_DISABLE_FILE_MACRO) +# undef LOG4CPLUS_MACRO_FILE +# define LOG4CPLUS_MACRO_FILE() __FILE__ +#endif + + +// Make TRACE and DEBUG log level unlikely and INFO, WARN, ERROR and +// FATAL log level likely. +#define LOG4CPLUS_MACRO_TRACE_LOG_LEVEL(pred) \ + LOG4CPLUS_UNLIKELY (pred) +#define LOG4CPLUS_MACRO_DEBUG_LOG_LEVEL(pred) \ + LOG4CPLUS_UNLIKELY (pred) +#define LOG4CPLUS_MACRO_INFO_LOG_LEVEL(pred) \ + LOG4CPLUS_LIKELY (pred) +#define LOG4CPLUS_MACRO_WARN_LOG_LEVEL(pred) \ + LOG4CPLUS_LIKELY (pred) +#define LOG4CPLUS_MACRO_ERROR_LOG_LEVEL(pred) \ + LOG4CPLUS_LIKELY (pred) +#define LOG4CPLUS_MACRO_FATAL_LOG_LEVEL(pred) \ + LOG4CPLUS_LIKELY (pred) + + +//! Dispatch to LOG4CPLUS_MACRO_LOGLEVEL_* depending on log level. +#define LOG4CPLUS_MACRO_LOGLEVEL_PRED(pred, logLevel) \ + LOG4CPLUS_MACRO_ ## logLevel (pred) + + +// Either use temporary instances of ostringstream +// and snprintf_buf, or use thread-local instances. +#if defined (LOG4CPLUS_MACRO_DISABLE_TLS) +# define LOG4CPLUS_MACRO_INSTANTIATE_OSTRINGSTREAM(var) \ + log4cplus::tostringstream var + +# define LOG4CPLUS_MACRO_INSTANTIATE_SNPRINTF_BUF(var) \ + log4cplus::helpers::snprintf_buf var + +#else +# define LOG4CPLUS_MACRO_INSTANTIATE_OSTRINGSTREAM(var) \ + log4cplus::tostringstream & var \ + = log4cplus::detail::get_macro_body_oss () + +# define LOG4CPLUS_MACRO_INSTANTIATE_SNPRINTF_BUF(var) \ + log4cplus::helpers::snprintf_buf & var \ + = log4cplus::detail::get_macro_body_snprintf_buf () + +#endif + + +#define LOG4CPLUS_MACRO_BODY(logger, logEvent, logLevel) \ + LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + do { \ + log4cplus::Logger const & _l \ + = log4cplus::detail::macros_get_logger (logger); \ + if (LOG4CPLUS_MACRO_LOGLEVEL_PRED ( \ + _l.isEnabledFor (log4cplus::logLevel), logLevel)) { \ + LOG4CPLUS_MACRO_INSTANTIATE_OSTRINGSTREAM (_log4cplus_buf); \ + _log4cplus_buf << logEvent; \ + log4cplus::detail::macro_forced_log (_l, \ + log4cplus::logLevel, _log4cplus_buf.str(), \ + LOG4CPLUS_MACRO_FILE (), __LINE__, \ + LOG4CPLUS_MACRO_FUNCTION ()); \ + } \ + } while (0) \ + LOG4CPLUS_RESTORE_DOWHILE_WARNING() + + +#define LOG4CPLUS_MACRO_STR_BODY(logger, logEvent, logLevel) \ + LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + do { \ + log4cplus::Logger const & _l \ + = log4cplus::detail::macros_get_logger (logger); \ + if (LOG4CPLUS_MACRO_LOGLEVEL_PRED ( \ + _l.isEnabledFor (log4cplus::logLevel), logLevel)) { \ + log4cplus::detail::macro_forced_log (_l, \ + log4cplus::logLevel, logEvent, \ + LOG4CPLUS_MACRO_FILE (), __LINE__, \ + LOG4CPLUS_MACRO_FUNCTION ()); \ + } \ + } while(0) \ + LOG4CPLUS_RESTORE_DOWHILE_WARNING() + +#define LOG4CPLUS_MACRO_FMT_BODY(logger, logLevel, ...) \ + LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + do { \ + log4cplus::Logger const & _l \ + = log4cplus::detail::macros_get_logger (logger); \ + if (LOG4CPLUS_MACRO_LOGLEVEL_PRED ( \ + _l.isEnabledFor (log4cplus::logLevel), logLevel)) { \ + LOG4CPLUS_MACRO_INSTANTIATE_SNPRINTF_BUF (_snpbuf); \ + log4cplus::tchar const * _logEvent \ + = _snpbuf.print (__VA_ARGS__); \ + log4cplus::detail::macro_forced_log (_l, \ + log4cplus::logLevel, _logEvent, \ + LOG4CPLUS_MACRO_FILE (), __LINE__, \ + LOG4CPLUS_MACRO_FUNCTION ()); \ + } \ + } while(0) \ + LOG4CPLUS_RESTORE_DOWHILE_WARNING() + +/** + * @def LOG4CPLUS_TRACE(logger, logEvent) This macro creates a + * TraceLogger to log a TRACE_LOG_LEVEL message to logger + * upon entry and exiting of a method. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_TRACE) +#define LOG4CPLUS_TRACE_METHOD(logger, logEvent) \ + log4cplus::TraceLogger _log4cplus_trace_logger(logger, logEvent, \ + LOG4CPLUS_MACRO_FILE (), __LINE__, \ + LOG4CPLUS_MACRO_FUNCTION ()); +#define LOG4CPLUS_TRACE(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, TRACE_LOG_LEVEL) +#define LOG4CPLUS_TRACE_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, TRACE_LOG_LEVEL) +#define LOG4CPLUS_TRACE_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, TRACE_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_TRACE_METHOD(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_TRACE(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_TRACE_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_TRACE_FMT(logger, logFmt, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +/** + * @def LOG4CPLUS_DEBUG(logger, logEvent) This macro is used to log a + * DEBUG_LOG_LEVEL message to logger. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_DEBUG) +#define LOG4CPLUS_DEBUG(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, DEBUG_LOG_LEVEL) +#define LOG4CPLUS_DEBUG_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, DEBUG_LOG_LEVEL) +#define LOG4CPLUS_DEBUG_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, DEBUG_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_DEBUG(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_DEBUG_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_DEBUG_FMT(logger, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +/** + * @def LOG4CPLUS_INFO(logger, logEvent) This macro is used to log a + * INFO_LOG_LEVEL message to logger. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_INFO) +#define LOG4CPLUS_INFO(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, INFO_LOG_LEVEL) +#define LOG4CPLUS_INFO_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, INFO_LOG_LEVEL) +#define LOG4CPLUS_INFO_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, INFO_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_INFO(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_INFO_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_INFO_FMT(logger, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +/** + * @def LOG4CPLUS_WARN(logger, logEvent) This macro is used to log a + * WARN_LOG_LEVEL message to logger. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_WARN) +#define LOG4CPLUS_WARN(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, WARN_LOG_LEVEL) +#define LOG4CPLUS_WARN_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, WARN_LOG_LEVEL) +#define LOG4CPLUS_WARN_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, WARN_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_WARN(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_WARN_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_WARN_FMT(logger, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +/** + * @def LOG4CPLUS_ERROR(logger, logEvent) This macro is used to log a + * ERROR_LOG_LEVEL message to logger. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_ERROR) +#define LOG4CPLUS_ERROR(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, ERROR_LOG_LEVEL) +#define LOG4CPLUS_ERROR_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, ERROR_LOG_LEVEL) +#define LOG4CPLUS_ERROR_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, ERROR_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_ERROR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_ERROR_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_ERROR_FMT(logger, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +/** + * @def LOG4CPLUS_FATAL(logger, logEvent) This macro is used to log a + * FATAL_LOG_LEVEL message to logger. + * logEvent will be streamed into an ostream. + */ +#if !defined(LOG4CPLUS_DISABLE_FATAL) +#define LOG4CPLUS_FATAL(logger, logEvent) \ + LOG4CPLUS_MACRO_BODY (logger, logEvent, FATAL_LOG_LEVEL) +#define LOG4CPLUS_FATAL_STR(logger, logEvent) \ + LOG4CPLUS_MACRO_STR_BODY (logger, logEvent, FATAL_LOG_LEVEL) +#define LOG4CPLUS_FATAL_FMT(logger, ...) \ + LOG4CPLUS_MACRO_FMT_BODY (logger, FATAL_LOG_LEVEL, __VA_ARGS__) + +#else +#define LOG4CPLUS_FATAL(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_FATAL_STR(logger, logEvent) LOG4CPLUS_DOWHILE_NOTHING() +#define LOG4CPLUS_FATAL_FMT(logger, ...) LOG4CPLUS_DOWHILE_NOTHING() + +#endif + +//! Helper macro for LOG4CPLUS_ASSERT() macro. +#define LOG4CPLUS_ASSERT_STRINGIFY(X) #X + +//! If the condition given in second parameter evaluates false, this +//! macro logs it using FATAL log level, including the condition's +//! source text. +#define LOG4CPLUS_ASSERT(logger, condition) \ + LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \ + do { \ + if (LOG4CPLUS_UNLIKELY(! (condition))) \ + LOG4CPLUS_FATAL_STR ((logger), \ + LOG4CPLUS_TEXT ("failed condition: ") \ + LOG4CPLUS_TEXT (LOG4CPLUS_ASSERT_STRINGIFY (condition))); \ + } while (0) \ + LOG4CPLUS_RESTORE_DOWHILE_WARNING() + + +#endif /* LOG4CPLUS_LOGGING_MACROS_HEADER_ */ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/loglevel.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/loglevel.h new file mode 100644 index 0000000..bf17572 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/loglevel.h @@ -0,0 +1,191 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loglevel.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file + * This header defines the LogLevel type. + */ + +#ifndef LOG4CPLUS_LOGLEVEL_HEADER_ +#define LOG4CPLUS_LOGLEVEL_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#if ! defined (LOG4CPLUS_SINGLE_THREADED) +#include +#endif +#include +#include + + +namespace log4cplus { + + /** + * \typedef int LogLevel + * Defines the minimum set of priorities recognized by the system, + * that is {@link #FATAL_LOG_LEVEL}, {@link #ERROR_LOG_LEVEL}, {@link + * #WARN_LOG_LEVEL}, {@link #INFO_LOG_LEVEL}, {@link #DEBUG_LOG_LEVEL}, + * and {@link #TRACE_LOG_LEVEL}. + */ + typedef int LogLevel; + + /** \var const LogLevel OFF_LOG_LEVEL + * The OFF_LOG_LEVEL LogLevel is used during configuration to + * turn off logging. */ + const LogLevel OFF_LOG_LEVEL = 60000; + + /** \var const LogLevel FATAL_LOG_LEVEL + * The FATAL_LOG_LEVEL LogLevel designates very severe error + * events that will presumably lead the application to abort. */ + const LogLevel FATAL_LOG_LEVEL = 50000; + + /** \var const LogLevel ERROR_LOG_LEVEL + * The ERROR_LOG_LEVEL LogLevel designates error events that + * might still allow the application to continue running. */ + const LogLevel ERROR_LOG_LEVEL = 40000; + + /** \var const LogLevel WARN_LOG_LEVEL + * The WARN_LOG_LEVEL LogLevel designates potentially harmful + * situations. */ + const LogLevel WARN_LOG_LEVEL = 30000; + + /** \var const LogLevel INFO_LOG_LEVEL + * The INFO_LOG_LEVEL LogLevel designates informational + * messages that highlight the progress of the application at + * coarse-grained level. */ + const LogLevel INFO_LOG_LEVEL = 20000; + + /** \var const LogLevel DEBUG_LOG_LEVEL + * The DEBUG_LOG_LEVEL LogLevel designates fine-grained + * informational events that are most useful to debug an application. */ + const LogLevel DEBUG_LOG_LEVEL = 10000; + + /** \var const LogLevel TRACE_LOG_LEVEL + * The TRACE_LOG_LEVEL LogLevel is used to "trace" entry + * and exiting of methods. */ + const LogLevel TRACE_LOG_LEVEL = 0; + + /** \var const LogLevel ALL_LOG_LEVEL + * The ALL_LOG_LEVEL LogLevel is used during configuration to + * turn on all logging. */ + const LogLevel ALL_LOG_LEVEL = TRACE_LOG_LEVEL; + + /** \var const LogLevel NOT_SET_LOG_LEVEL + * The NOT_SET_LOG_LEVEL LogLevel is used to indicated that + * no particular LogLevel is desired and that the default should be used. + */ + const LogLevel NOT_SET_LOG_LEVEL = -1; + + + /** + * This is a base class used by `LogLevelManager` to translate between + * numeric `LogLevel` and log level name. + */ + class LOG4CPLUS_EXPORT LogLevelTranslator + : public virtual helpers::SharedObject { + public: + LogLevelTranslator (); + virtual ~LogLevelTranslator () = 0; + + /** + * This method is called by all `LogLevelManager` classes to convert a + * `LogLevel` into a string. + */ + virtual log4cplus::tstring const & toString (LogLevel ll) const = 0; + + /** + * This method is called by `LogLevelManager` to convert a string into + * a `LogLevel`. + */ + virtual LogLevel fromString (const log4cplus::tstring_view& arg) const = 0; + }; + + using SharedLogLevelTranslatorPtr = helpers::SharedObjectPtr; + + + /** + * This class is used to "manage" LogLevel definitions. This class is also + * how "derived" LogLevels are created. Here are the steps to creating a + * "derived" LogLevel: + *
    + *
  1. Create a LogLevel constant (greater than 0)
  2. + *
  3. Define a string to represent that constant
  4. + *
  5. Implement a LogLevelToStringMethod method.
  6. + *
  7. Implement a StringToLogLevelMethod method.
  8. + *
  9. create a "static initializer" that registers those 2 methods + * with the LogLevelManager singleton.
  10. + *
+ */ + class LOG4CPLUS_EXPORT LogLevelManager { + public: + LogLevelManager(); + ~LogLevelManager(); + + /** + * This method is called by all Layout classes to convert a LogLevel + * into a string. + * + * Note: It traverses the list of LogLevelToStringMethod + * to do this, so all "derived" LogLevels are recognized as well. + */ + log4cplus::tstring const & toString(LogLevel ll) const; + + /** + * This method is called by all classes internally to log4cplus to + * convert a string into a LogLevel. + * + * Note: It traverses the list of StringToLogLevelMethod + * to do this, so all "derived" LogLevels are recognized as well. + */ + LogLevel fromString(const log4cplus::tstring_view& arg) const; + + + void pushLogLevel(LogLevel ll, const log4cplus::tstring_view & name); + + void pushLogLevelTranslator(SharedLogLevelTranslatorPtr); + + private: +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + mutable std::shared_mutex mtx; +#endif + + typedef std::vector LogLevelTranslatorList; + LogLevelTranslatorList translator_list; + + // Disable Copy + LogLevelManager(const LogLevelManager&); + LogLevelManager& operator=(const LogLevelManager&); + }; + + /** + * Returns the singleton LogLevelManager. + */ + LOG4CPLUS_EXPORT LogLevelManager& getLogLevelManager(); + +} + + +#endif // LOG4CPLUS_LOGLEVEL_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/mdc.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/mdc.h new file mode 100644 index 0000000..4951298 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/mdc.h @@ -0,0 +1,77 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_MDC_H_HEADER +#define LOG4CPLUS_MDC_H_HEADER + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include + + +namespace log4cplus +{ + + +typedef std::map MappedDiagnosticContextMap; + + +class LOG4CPLUS_EXPORT MDC +{ +public: + /** + * Clear any nested diagnostic information if any. This method is + * useful in cases where the same thread can be potentially used + * over and over in different unrelated contexts. + */ + void clear(); + + void put (tstring const & key, tstring const & value); + bool get (tstring * value, tstring const & key) const; + void remove (tstring const & key); + + MappedDiagnosticContextMap const & getContext () const; + + // Public ctor and dtor but only to be used by internal::DefaultContext. + MDC (); + virtual ~MDC (); + +private: + LOG4CPLUS_PRIVATE static MappedDiagnosticContextMap * getPtr (); +}; + + +LOG4CPLUS_EXPORT MDC & getMDC (); + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_MDC_H_HEADER diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/msttsappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/msttsappender.h new file mode 100644 index 0000000..3479725 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/msttsappender.h @@ -0,0 +1,112 @@ +// -*- C++ -*- +// Module: Log4cplus +// File: msttsappender.h +// Created: 10/2012 +// Author: Vaclav Zeman +// +// +// Copyright (C) 2012-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +// + +/** @file */ + +#ifndef LOG4CPLUS_MSTTSAPPENDER_H +#define LOG4CPLUS_MSTTSAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +#if defined (_WIN32) + #if defined (log4cplusqt4debugappender_EXPORTS) \ + || defined (log4cplusqt4debugappenderU_EXPORTS) \ + || (defined (DLL_EXPORT) && defined (INSIDE_LOG4CPLUS_MSTTSAPPENDER)) + #undef LOG4CPLUS_MSTTSAPPENDER_BUILD_DLL + #define LOG4CPLUS_MSTTSAPPENDER_BUILD_DLL + #endif + #if defined (LOG4CPLUS_MSTTSAPPENDER_BUILD_DLL) + #if defined (INSIDE_LOG4CPLUS_MSTTSAPPENDER) + #define LOG4CPLUS_MSTTSAPPENDER_EXPORT __declspec(dllexport) + #else + #define LOG4CPLUS_MSTTSAPPENDER_EXPORT __declspec(dllimport) + #endif + #else + #define LOG4CPLUS_MSTTSAPPENDER_EXPORT + #endif +#else + #if defined (INSIDE_LOG4CPLUS_MSTTSAPPENDER) + #define LOG4CPLUS_MSTTSAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_EXPORT + #else + #define LOG4CPLUS_MSTTSAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_IMPORT + #endif // defined (INSIDE_LOG4CPLUS_MSTTSAPPENDER) +#endif // !_WIN32 + + +namespace log4cplus +{ + + +class LOG4CPLUS_MSTTSAPPENDER_EXPORT MSTTSAppender + : public Appender +{ +public: + MSTTSAppender (); + explicit MSTTSAppender (helpers::Properties const &); + virtual ~MSTTSAppender (); + + virtual void close (); + + static void registerAppender (); + +protected: + virtual void append (spi::InternalLoggingEvent const &); + + struct Data; + + Data * data; + +private: + LOG4CPLUS_PRIVATE void init (long const * rate = 0, + unsigned long const * volume = 0, bool speak_punc = false, + bool async = false); + + MSTTSAppender (MSTTSAppender const &); + MSTTSAppender & operator = (MSTTSAppender const &); +}; + + +typedef helpers::SharedObjectPtr MSTTSAppenderPtr; + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_MSTTSAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/ndc.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/ndc.h new file mode 100644 index 0000000..4fbf214 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/ndc.h @@ -0,0 +1,329 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: ndc.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file + * This header defined the NDC class. + */ + +#ifndef _LO4CPLUS_NDC_HEADER_ +#define _LO4CPLUS_NDC_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include +#include + + +namespace log4cplus { + // Forward declarations + struct DiagnosticContext; + typedef std::deque DiagnosticContextStack; + + /** + * The NDC class implements nested diagnostic contexts as + * defined by Neil Harrison in the article "Patterns for Logging + * Diagnostic Messages" part of the book "Pattern Languages of + * Program Design 3" edited by Martin et al. + * + * A Nested Diagnostic Context, or NDC in short, is an instrument + * to distinguish interleaved log output from different sources. Log + * output is typically interleaved when a server handles multiple + * clients near-simultaneously. + * + * Interleaved log output can still be meaningful if each log entry + * from different contexts had a distinctive stamp. This is where NDCs + * come into play. + * + * Note that NDCs are managed on a per thread + * basis. NDC operations such as {@link #push}, {@link + * #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth} + * affect the NDC of the current thread only. NDCs of other + * threads remain unaffected. + * + * For example, a server can build a per client request NDC + * consisting the clients host name and other information contained in + * the the request. Cookies are another source of distinctive + * information. To build an NDC one uses the {@link #push} + * operation. Simply put, + * + * - Contexts can be nested. + * - When entering a context, call `push()`. As a side effect, if + * there is no nested diagnostic context for the current thread, + * this method will create it. + * - When leaving a context, call `pop()`. + * - When exiting a thread make sure to call `remove()`. + * + * There is no penalty for forgetting to match each push() + * operation with a corresponding pop(), except the obvious + * mismatch between the real application context and the context + * set in the NDC. Use of the NDCContextCreator class can + * automate this process and make your code exception-safe. + * + * If configured to do so, {@link log4cplus::PatternLayout} and + * {@link log4cplus::TTCCLayout} instances automatically retrieve + * the nested diagnostic context for the current thread without + * any user intervention. Hence, even if a server is serving + * multiple clients simultaneously, the logs emanating from the + * same code (belonging to the same logger) can still be + * distinguished because each client request will have a different + * NDC tag. + * + * Heavy duty systems should call the {@link #remove} method when + * leaving the run method of a thread. This ensures that the memory + * used by the thread can be freed. + * + * A thread may inherit the nested diagnostic context of another + * (possibly parent) thread using the {@link #inherit inherit} + * method. A thread may obtain a copy of its NDC with the {@link + * #cloneStack cloneStack} method and pass the reference to any other + * thread, in particular to a child. + */ + class LOG4CPLUS_EXPORT NDC + { + public: + /** + * Clear any nested diagnostic information if any. This method is + * useful in cases where the same thread can be potentially used + * over and over in different unrelated contexts. + * + * This method is equivalent to calling the {@link #setMaxDepth} + * method with a zero maxDepth argument. + */ + void clear(); + + /** + * Clone the diagnostic context for the current thread. + * + * Internally a diagnostic context is represented as a stack. A + * given thread can supply the stack (i.e. diagnostic context) to a + * child thread so that the child can inherit the parent thread's + * diagnostic context. + * + * The child thread uses the {@link #inherit inherit} method to + * inherit the parent's diagnostic context. + * + * @return Stack A clone of the current thread's diagnostic context. + */ + DiagnosticContextStack cloneStack() const; + + /** + * Inherit the diagnostic context of another thread. + * + * The parent thread can obtain a reference to its diagnostic + * context using the {@link #cloneStack} method. It should + * communicate this information to its child so that it may inherit + * the parent's diagnostic context. + * + * The parent's diagnostic context is cloned before being + * inherited. In other words, once inherited, the two diagnostic + * contexts can be managed independently. + * + * @param stack The diagnostic context of the parent thread. + */ + void inherit(const DiagnosticContextStack& stack); + + /** + * Used when printing the diagnostic context. + */ + log4cplus::tstring const & get() const; + + /** + * Get the current nesting depth of this diagnostic context. + * + * @see #setMaxDepth + */ + std::size_t getDepth() const; + + /** + * Clients should call this method before leaving a diagnostic + * context. + * + * The returned value is the value that was pushed last. If no + * context is available, then the empty string is returned. If + * each call to `push()` is paired with a call to `pop()` + * (even in presence of thrown exceptions), the last `pop()` + * call frees the memory used by NDC for this + * thread. Otherwise, `remove()` must be called at the end of + * the thread to free the memory used by NDC for the thread. + * + * @return String The innermost diagnostic context. + * + * @see NDCContextCreator, remove(), push() + */ + log4cplus::tstring pop(); + + /** + * Same as pop() but without the return value. + */ + void pop_void (); + + /** + * Looks at the last diagnostic context at the top of this NDC + * without removing it. + * + * The returned value is the value that was pushed last. If no + * context is available, then the empty string is returned. + * + * @return String The innermost diagnostic context. + */ + log4cplus::tstring const & peek() const; + + /** + * Push new diagnostic context information for the current thread. + * + * The contents of the message parameter is + * determined solely by the client. Each call to push() should + * be paired with a call to pop(). + * + * @param message The new diagnostic context information. + * + * @see NDCContextCreator, pop(), remove() + */ + void push(const log4cplus::tstring& message); + void push(tchar const * message); + + /** + * Remove the diagnostic context for this thread. + * + * Each thread that created a diagnostic context by calling + * push() should call this method before exiting. Otherwise, + * the memory used by the thread cannot be reclaimed. It is + * possible to omit this call if and only if each push() call + * is always paired with a pop() call (even in presence of + * thrown exceptions). Then the memory used by NDC will be + * returned by the last pop() call and a call to remove() will + * be no-op. + */ + void remove(); + + /** + * Set maximum depth of this diagnostic context. If the + * current depth is smaller or equal to `maxDepth`, then no + * action is taken. + * + * This method is a convenient alternative to multiple `pop()` + * calls. Moreover, it is often the case that at the end of + * complex call sequences, the depth of the NDC is + * unpredictable. The `setMaxDepth()` method circumvents this + * problem. + * + * For example, the combination + * + * ~~~~{.c} + * void foo() { + * NDC & ndc = getNDC(); + * std::size_t depth = ndc.getDepth(); + * //... complex sequence of calls + * ndc.setMaxDepth(depth); + * } + * ~~~~ + * + * ensures that between the entry and exit of foo the depth of the + * diagnostic stack is conserved. + * + * \note Use of the NDCContextCreator class will solve this + * particular problem. + * + * \see NDC::getDepth() + */ + void setMaxDepth(std::size_t maxDepth); + + // Public ctor but only to be used by internal::DefaultContext. + NDC(); + + // Dtor + virtual ~NDC(); + + private: + // Methods + LOG4CPLUS_PRIVATE static DiagnosticContextStack* getPtr(); + + template + LOG4CPLUS_PRIVATE + void push_worker (StringType const &); + + // Disallow construction (and copying) except by getNDC() + NDC(const NDC&); + NDC& operator=(const NDC&); + }; + + + /** + * Return a reference to the singleton object. + */ + LOG4CPLUS_EXPORT NDC& getNDC(); + + + /** + * This is the internal object that is stored on the NDC stack. + */ + struct LOG4CPLUS_EXPORT DiagnosticContext + { + // Ctors + DiagnosticContext(const log4cplus::tstring& message, + DiagnosticContext const * parent); + DiagnosticContext(tchar const * message, + DiagnosticContext const * parent); + DiagnosticContext(const log4cplus::tstring& message); + DiagnosticContext(tchar const * message); + DiagnosticContext(DiagnosticContext const &); + DiagnosticContext & operator = (DiagnosticContext const &); + DiagnosticContext(DiagnosticContext &&); + DiagnosticContext & operator = (DiagnosticContext &&); + + void swap (DiagnosticContext &); + + // Data + log4cplus::tstring message; /*!< The message at this context level. */ + log4cplus::tstring fullMessage; /*!< The entire message stack. */ + }; + + + /** + * This class ensures that a `NDC::push()` call is always matched + * with a `NDC::pop()` call even in the face of exceptions. + */ + class LOG4CPLUS_EXPORT NDCContextCreator { + public: + /** Pushes msg onto the NDC stack. */ + explicit NDCContextCreator(const log4cplus::tstring& msg); + explicit NDCContextCreator(tchar const * msg); + + NDCContextCreator() = delete; + NDCContextCreator(NDCContextCreator const &) = delete; + NDCContextCreator(NDCContextCreator &&) = delete; + NDCContextCreator & operator= (NDCContextCreator const &) = delete; + NDCContextCreator & operator= (NDCContextCreator &&) = delete; + + /** Pops the NDC stack. */ + ~NDCContextCreator(); + }; + +} // end namespace log4cplus + + +#endif // _LO4CPLUS_NDC_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/nteventlogappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/nteventlogappender.h new file mode 100644 index 0000000..2d8829f --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/nteventlogappender.h @@ -0,0 +1,84 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: nteventlogappender.h +// Created: 4/2003 +// Author: Michael CATANZARITI +// +// Copyright 2003-2017 Michael CATANZARITI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @file */ + +#ifndef LOG4CPLUS_NT_EVENT_LOG_APPENDER_HEADER_ +#define LOG4CPLUS_NT_EVENT_LOG_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (LOG4CPLUS_HAVE_NT_EVENT_LOG) + +#include +#include + + +namespace log4cplus { + + /** + * Appends log events to NT EventLog. + */ + class LOG4CPLUS_EXPORT NTEventLogAppender : public Appender { + public: + // ctors + NTEventLogAppender(const log4cplus::tstring& server, + const log4cplus::tstring& log, + const log4cplus::tstring& source); + NTEventLogAppender(const log4cplus::helpers::Properties & properties); + + // dtor + virtual ~NTEventLogAppender(); + + // public Methods + virtual void close(); + + protected: + virtual void append(const spi::InternalLoggingEvent& event); + virtual WORD getEventType(const spi::InternalLoggingEvent& event); + virtual WORD getEventCategory(const spi::InternalLoggingEvent& event); + void init(); + + /* + * Add this source with appropriate configuration keys to the registry. + */ + void addRegistryInfo(); + + // Data + log4cplus::tstring server; + log4cplus::tstring log; + log4cplus::tstring source; + HANDLE hEventLog; + SID* pCurrentUserSID; + + private: + // Disallow copying of instances of this class + NTEventLogAppender(const NTEventLogAppender&); + NTEventLogAppender& operator=(const NTEventLogAppender&); + }; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_HAVE_NT_EVENT_LOG +#endif //LOG4CPLUS_NT_EVENT_LOG_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/nullappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/nullappender.h new file mode 100644 index 0000000..18e7696 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/nullappender.h @@ -0,0 +1,65 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: nullappender.h +// Created: 6/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_NULL_APPENDER_HEADER_ +#define LOG4CPLUS_NULL_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +namespace log4cplus { + + /** + * Appends log events to a file. + */ + class LOG4CPLUS_EXPORT NullAppender : public Appender { + public: + // Ctors + NullAppender(); + NullAppender(const log4cplus::helpers::Properties&); + + // Dtor + virtual ~NullAppender(); + + // Methods + virtual void close(); + + protected: + virtual void append(const log4cplus::spi::InternalLoggingEvent& event); + + private: + // Disallow copying of instances of this class + NullAppender(const NullAppender&); + NullAppender& operator=(const NullAppender&); + }; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_NULL_APPENDER_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/qt4debugappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/qt4debugappender.h new file mode 100644 index 0000000..177dbda --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/qt4debugappender.h @@ -0,0 +1,103 @@ +// -*- C++ -*- +// Module: Log4cplus +// File: qt4debugappender.h +// Created: 5/2012 +// Author: Vaclav Zeman +// +// +// Copyright (C) 2012-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +// + +/** @file */ + +#ifndef LOG4CPLUS_QT4DEBUGAPPENDER_H +#define LOG4CPLUS_QT4DEBUGAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#if defined (_WIN32) + #if defined (log4cplusqt4debugappender_EXPORTS) \ + || defined (log4cplusqt4debugappenderU_EXPORTS) \ + || (defined (DLL_EXPORT) && defined (INSIDE_LOG4CPLUS_QT4DEBUGAPPENDER)) + #undef LOG4CPLUS_QT4DEBUGAPPENDER_BUILD_DLL + #define LOG4CPLUS_QT4DEBUGAPPENDER_BUILD_DLL + #endif + #if defined (LOG4CPLUS_QT4DEBUGAPPENDER_BUILD_DLL) + #if defined (INSIDE_LOG4CPLUS_QT4DEBUGAPPENDER) + #define LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT __declspec(dllexport) + #else + #define LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT __declspec(dllimport) + #endif + #else + #define LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT + #endif +#else + #if defined (INSIDE_LOG4CPLUS_QT4DEBUGAPPENDER) + #define LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_EXPORT + #else + #define LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_IMPORT + #endif // defined (INSIDE_LOG4CPLUS_QT4DEBUGAPPENDER) +#endif // !_WIN32 + + +namespace log4cplus +{ + + +class LOG4CPLUS_QT4DEBUGAPPENDER_EXPORT Qt4DebugAppender + : public Appender +{ +public: + Qt4DebugAppender (); + explicit Qt4DebugAppender (helpers::Properties const &); + virtual ~Qt4DebugAppender (); + + virtual void close (); + + static void registerAppender (); + +protected: + virtual void append (spi::InternalLoggingEvent const &); + +private: + Qt4DebugAppender (Qt4DebugAppender const &); + Qt4DebugAppender & operator = (Qt4DebugAppender const &); +}; + + +typedef helpers::SharedObjectPtr Qt4DebugAppenderPtr; + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_QT4DEBUGAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/qt5debugappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/qt5debugappender.h new file mode 100644 index 0000000..e14bf23 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/qt5debugappender.h @@ -0,0 +1,103 @@ +// -*- C++ -*- +// Module: Log4cplus +// File: qt5debugappender.h +// Created: 4/2013 +// Author: Vaclav Zeman +// +// +// Copyright (C) 2013-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +// + +/** @file */ + +#ifndef LOG4CPLUS_QT5DEBUGAPPENDER_H +#define LOG4CPLUS_QT5DEBUGAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#if defined (_WIN32) + #if defined (log4cplusqt5debugappender_EXPORTS) \ + || defined (log4cplusqt5debugappenderU_EXPORTS) \ + || (defined (DLL_EXPORT) && defined (INSIDE_LOG4CPLUS_QT5DEBUGAPPENDER)) + #undef LOG4CPLUS_QT5DEBUGAPPENDER_BUILD_DLL + #define LOG4CPLUS_QT5DEBUGAPPENDER_BUILD_DLL + #endif + #if defined (LOG4CPLUS_QT5DEBUGAPPENDER_BUILD_DLL) + #if defined (INSIDE_LOG4CPLUS_QT5DEBUGAPPENDER) + #define LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT __declspec(dllexport) + #else + #define LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT __declspec(dllimport) + #endif + #else + #define LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT + #endif +#else + #if defined (INSIDE_LOG4CPLUS_QT5DEBUGAPPENDER) + #define LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_EXPORT + #else + #define LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT LOG4CPLUS_DECLSPEC_IMPORT + #endif // defined (INSIDE_LOG4CPLUS_QT5DEBUGAPPENDER) +#endif // !_WIN32 + + +namespace log4cplus +{ + + +class LOG4CPLUS_QT5DEBUGAPPENDER_EXPORT Qt5DebugAppender + : public Appender +{ +public: + Qt5DebugAppender (); + explicit Qt5DebugAppender (helpers::Properties const &); + virtual ~Qt5DebugAppender (); + + virtual void close (); + + static void registerAppender (); + +protected: + virtual void append (spi::InternalLoggingEvent const &); + +private: + Qt5DebugAppender (Qt5DebugAppender const &); + Qt5DebugAppender & operator = (Qt5DebugAppender const &); +}; + + +typedef helpers::SharedObjectPtr Qt5DebugAppenderPtr; + + +} // namespace log4cplus + + +#endif // LOG4CPLUS_QT5DEBUGAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/socketappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/socketappender.h new file mode 100644 index 0000000..f0dd564 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/socketappender.h @@ -0,0 +1,164 @@ +// -*- C++ -*- +// Module: LOG4CPLUS +// File: socketappender.h +// Created: 5/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SOCKET_APPENDER_HEADER_ +#define LOG4CPLUS_SOCKET_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include + + +namespace log4cplus +{ + +#ifndef UNICODE + std::size_t const LOG4CPLUS_MAX_MESSAGE_SIZE = 8*1024; +#else + std::size_t const LOG4CPLUS_MAX_MESSAGE_SIZE = 2*8*1024; +#endif + + + /** + * Sends {@link spi::InternalLoggingEvent} objects to a remote a log server. + * + * The SocketAppender has the following properties: + * + *
    + * + *
  • Remote logging is non-intrusive as far as the log event + * is concerned. In other words, the event will be logged with + * the same time stamp, NDC, location info as if it were logged + * locally by the client. + * + *
  • SocketAppenders do not use a layout. + * + *
  • Remote logging uses the TCP protocol. Consequently, if + * the server is reachable, then log events will eventually arrive + * at the server. + * + *
  • If the remote server is down, the logging requests are + * simply dropped. However, if and when the server comes back up, + * then event transmission is resumed transparently. This + * transparent reconneciton is performed by a connector + * thread which periodically attempts to connect to the server. + * + *
  • Logging events are automatically buffered by the + * native TCP implementation. This means that if the link to server + * is slow but still faster than the rate of (log) event production + * by the client, the client will not be affected by the slow + * network connection. However, if the network connection is slower + * then the rate of event production, then the client can only + * progress at the network rate. In particular, if the network link + * to the the server is down, the client will be blocked. + * + *
  • On the other hand, if the network link is up, but the server + * is down, the client will not be blocked when making log requests + * but the log events will be lost due to server unavailability. + *
+ * + *

Properties

+ *
+ *
host
+ *
Remote host name to connect and send events to.
+ * + *
port
+ *
Port on remote host to send events to.
+ * + *
ServerName
+ *
Host name of event's origin prepended to each event.
+ * + *
IPv6
+ *
Boolean value specifying whether to use IPv6 (true) or IPv4 + * (false). Default value is false.
+ * + *
+ */ + class LOG4CPLUS_EXPORT SocketAppender + : public Appender +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + , protected virtual helpers::IConnectorThreadClient +#endif + { + public: + // Ctors + SocketAppender(const log4cplus::tstring& host, unsigned short port, + const log4cplus::tstring& serverName = tstring(), + bool ipv6 = false); + SocketAppender(const log4cplus::helpers::Properties & properties); + + // Dtor + ~SocketAppender(); + + // Methods + virtual void close(); + + protected: + void openSocket(); + void initConnector (); + virtual void append(const spi::InternalLoggingEvent& event); + + // Data + log4cplus::helpers::Socket socket; + log4cplus::tstring host; + unsigned int port; + log4cplus::tstring serverName; + bool ipv6 = false; + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + virtual thread::Mutex const & ctcGetAccessMutex () const; + virtual helpers::Socket & ctcGetSocket (); + virtual helpers::Socket ctcConnect (); + virtual void ctcSetConnected (); + + volatile bool connected; + helpers::SharedObjectPtr connector; +#endif + + private: + // Disallow copying of instances of this class + SocketAppender(const SocketAppender&); + SocketAppender& operator=(const SocketAppender&); + }; + + namespace helpers { + LOG4CPLUS_EXPORT + void convertToBuffer (SocketBuffer & buffer, + const log4cplus::spi::InternalLoggingEvent& event, + const log4cplus::tstring& serverName); + + LOG4CPLUS_EXPORT + log4cplus::spi::InternalLoggingEvent readFromBuffer(SocketBuffer& buffer); + } // end namespace helpers + +} // end namespace log4cplus + +#endif // LOG4CPLUS_SOCKET_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/appenderattachable.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/appenderattachable.h new file mode 100644 index 0000000..d0ff6fb --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/appenderattachable.h @@ -0,0 +1,88 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: appenderattachable.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_APPENDER_ATTACHABLE_HEADER_ +#define LOG4CPLUS_SPI_APPENDER_ATTACHABLE_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +namespace log4cplus { + // Forward Declarations + typedef std::vector SharedAppenderPtrList; + + namespace spi { + + /** + * This Interface is for attaching Appenders to objects. + */ + class LOG4CPLUS_EXPORT AppenderAttachable { + public: + // Methods + /** + * Add an appender. + */ + virtual void addAppender(SharedAppenderPtr newAppender) = 0; + + /** + * Get all previously added appenders as an Enumeration. + */ + virtual SharedAppenderPtrList getAllAppenders() = 0; + + /** + * Get an appender by name. + */ + virtual SharedAppenderPtr getAppender(const log4cplus::tstring& name) = 0; + + /** + * Remove all previously added appenders. + */ + virtual void removeAllAppenders() = 0; + + /** + * Remove the appender passed as parameter from the list of appenders. + */ + virtual void removeAppender(SharedAppenderPtr appender) = 0; + + /** + * Remove the appender with the name passed as parameter from the + * list of appenders. + */ + virtual void removeAppender(const log4cplus::tstring& name) = 0; + + // Dtor + virtual ~AppenderAttachable() = 0; + }; + + } // end namespace spi +} // end namespace log4cplus + +#endif // LOG4CPLUS_SPI_APPENDER_ATTACHABLE_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/factory.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/factory.h new file mode 100644 index 0000000..8c5234b --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/factory.h @@ -0,0 +1,275 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: factory.h +// Created: 2/2002 +// Author: Tad E. Smith +// +// +// Copyright 2002-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_FACTORY_HEADER_ +#define LOG4CPLUS_SPI_FACTORY_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace log4cplus { + namespace spi { + + /** + * This is the base class for all factories. + */ + class LOG4CPLUS_EXPORT BaseFactory { + public: + virtual ~BaseFactory() = 0; + + /** + * Returns the typename of the objects this factory creates. + */ + virtual log4cplus::tstring const & getTypeName() const = 0; + }; + + + /** + * This abstract class defines the "Factory" interface to create "Appender" + * objects. + */ + class LOG4CPLUS_EXPORT AppenderFactory : public BaseFactory { + public: + typedef Appender ProductType; + typedef SharedAppenderPtr ProductPtr; + + AppenderFactory(); + virtual ~AppenderFactory() = 0; + + /** + * Create an "Appender" object. + */ + virtual SharedAppenderPtr createObject(const log4cplus::helpers::Properties& props) = 0; + }; + + + + /** + * This abstract class defines the "Factory" interface to create "Layout" + * objects. + */ + class LOG4CPLUS_EXPORT LayoutFactory : public BaseFactory { + public: + typedef Layout ProductType; + typedef std::unique_ptr ProductPtr; + + LayoutFactory(); + virtual ~LayoutFactory() = 0; + + /** + * Create a "Layout" object. + */ + virtual std::unique_ptr createObject(const log4cplus::helpers::Properties& props) = 0; + }; + + + + /** + * This abstract class defines the "Factory" interface to create "Appender" + * objects. + */ + class LOG4CPLUS_EXPORT FilterFactory : public BaseFactory { + public: + typedef Filter ProductType; + typedef FilterPtr ProductPtr; + + FilterFactory(); + virtual ~FilterFactory() = 0; + + /** + * Create a "Filter" object. + */ + virtual FilterPtr createObject(const log4cplus::helpers::Properties& props) = 0; + }; + + + /** + * This abstract class defines the "Factory" interface to + * create std::locale instances. + */ + class LOG4CPLUS_EXPORT LocaleFactory + : public BaseFactory + { + public: + typedef std::locale ProductType; + typedef std::locale ProductPtr; + + LocaleFactory(); + virtual ~LocaleFactory() = 0; + + //! \returns std::locale instance + virtual ProductPtr createObject ( + const log4cplus::helpers::Properties & props) = 0; + }; + + + /** + * This template class is used as a "Factory Registry". Objects are + * "entered" into the registry with a "name" using the + * put() method. (The registry then owns the object.) + * These object can then be retrieved using the get() + * method. + * + * Note: This class is Thread-safe. + */ + template + class LOG4CPLUS_EXPORT FactoryRegistry + : public ObjectRegistryBase + { + public: + typedef T product_type; + + virtual ~FactoryRegistry() { + clear(); + } + + // public methods + /** + * Used to enter an object into the registry. (The registry now + * owns object.) + */ + bool put(std::unique_ptr object) { + bool putValResult = putVal(object->getTypeName(), object.get()); + object.release(); + return putValResult; + } + + /** + * Used to retrieve an object from the registry. (The registry + * owns the returned pointer.) + */ + T* get(const log4cplus::tstring& name) const { + return static_cast(getVal(name)); + } + + protected: + virtual void deleteObject(void *object) const { + delete static_cast(object); + } + }; + + + typedef FactoryRegistry AppenderFactoryRegistry; + typedef FactoryRegistry LayoutFactoryRegistry; + typedef FactoryRegistry FilterFactoryRegistry; + typedef FactoryRegistry LocaleFactoryRegistry; + + + /** + * Returns the "singleton" AppenderFactoryRegistry. + */ + LOG4CPLUS_EXPORT AppenderFactoryRegistry& getAppenderFactoryRegistry(); + + /** + * Returns the "singleton" LayoutFactoryRegistry. + */ + LOG4CPLUS_EXPORT LayoutFactoryRegistry& getLayoutFactoryRegistry(); + + /** + * Returns the "singleton" FilterFactoryRegistry. + */ + LOG4CPLUS_EXPORT FilterFactoryRegistry& getFilterFactoryRegistry(); + + /** + * Returns the "singleton" LocaleFactoryRegistry. + */ + LOG4CPLUS_EXPORT LocaleFactoryRegistry& getLocaleFactoryRegistry(); + + + template + class LocalFactoryBase + : public ProductFactoryBase + { + public: + LocalFactoryBase (tchar const * n) + : name (n) + { } + + virtual log4cplus::tstring const & getTypeName() const + { + return name; + } + + private: + log4cplus::tstring name; + }; + + + template + class FactoryTempl + : public LocalFactoryBase + { + public: + typedef typename ProductFactoryBase::ProductPtr ProductPtr; + + FactoryTempl (tchar const * n) + : LocalFactoryBase (n) + { } + + virtual ProductPtr createObject (helpers::Properties const & props) + { + return ProductPtr (new LocalProduct (props)); + } + }; + + + #define LOG4CPLUS_REG_PRODUCT(reg, productprefix, productname, productns, productfact) \ + reg.put ( \ + std::unique_ptr ( \ + new log4cplus::spi::FactoryTempl ( \ + LOG4CPLUS_TEXT(productprefix) \ + LOG4CPLUS_TEXT(#productname)))) + + #define LOG4CPLUS_REG_APPENDER(reg, appendername) \ + LOG4CPLUS_REG_PRODUCT (reg, "log4cplus::", appendername, log4cplus::, \ + log4cplus::spi::AppenderFactory) + + #define LOG4CPLUS_REG_LAYOUT(reg, layoutname) \ + LOG4CPLUS_REG_PRODUCT (reg, "log4cplus::", layoutname, log4cplus::, \ + log4cplus::spi::LayoutFactory) + + #define LOG4CPLUS_REG_FILTER(reg, filtername) \ + LOG4CPLUS_REG_PRODUCT (reg, "log4cplus::spi::", filtername, log4cplus::spi::, \ + log4cplus::spi::FilterFactory) + + #define LOG4CPLUS_REG_LOCALE(reg, name, factory) \ + reg.put (std::unique_ptr ( \ + new factory (name))) + } // namespace spi +} + + +#endif // LOG4CPLUS_SPI_FACTORY_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/filter.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/filter.h new file mode 100644 index 0000000..e778c75 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/filter.h @@ -0,0 +1,404 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: filter.h +// Created: 5/2003 +// Author: Tad E. Smith +// +// +// Copyright 1999-2017 Tad E. 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. + +/** @file + * This header defines Filter and all of it's subclasses. */ + +#ifndef LOG4CPLUS_SPI_FILTER_HEADER_ +#define LOG4CPLUS_SPI_FILTER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include +#include + + +namespace log4cplus { + + namespace helpers + { + + class Properties; + + } + + namespace spi { + + + enum FilterResult { DENY, /**< The log event must be dropped immediately + * without consulting with the remaining + * filters, if any, in the chain. */ + NEUTRAL, /**< This filter is neutral with respect to + * the log event; the remaining filters, if + * if any, should be consulted for a final + * decision. */ + ACCEPT /**< The log event must be logged immediately + * without consulting with the remaining + * filters, if any, in the chain. */ + }; + + // Forward Declarations + class Filter; + class InternalLoggingEvent; + + + /** + * This method is used to filter an InternalLoggingEvent. + * + * Note: filter can be NULL. + */ + LOG4CPLUS_EXPORT FilterResult checkFilter(const Filter* filter, + const InternalLoggingEvent& event); + + typedef helpers::SharedObjectPtr FilterPtr; + + + /** + * Users should extend this class to implement customized logging + * event filtering. Note that the {@link Logger} and {@link + * Appender} classes have built-in filtering rules. It is suggested + * that you first use and understand the built-in rules before rushing + * to write your own custom filters. + * + * This abstract class assumes and also imposes that filters be + * organized in a linear chain. The {@link #decide + * decide(LoggingEvent)} method of each filter is called sequentially, + * in the order of their addition to the chain. + * + * If the value {@link #DENY} is returned, then the log event is + * dropped immediately without consulting with the remaining + * filters. + * + * If the value {@link #NEUTRAL} is returned, then the next filter + * in the chain is consulted. If there are no more filters in the + * chain, then the log event is logged. Thus, in the presence of no + * filters, the default behaviour is to log all logging events. + * + * If the value {@link #ACCEPT} is returned, then the log + * event is logged without consulting the remaining filters. + * + * The philosophy of log4cplus filters is largely inspired from the + * Linux ipchains. + */ + class LOG4CPLUS_EXPORT Filter + : public virtual log4cplus::helpers::SharedObject + { + public: + // ctor and dtor + Filter(); + virtual ~Filter(); + + // Methods + /** + * Appends filter to the end of this filter chain. + */ + void appendFilter(FilterPtr filter); + + /** + * If the decision is DENY, then the event will be + * dropped. If the decision is NEUTRAL, then the next + * filter, if any, will be invoked. If the decision is ACCEPT then + * the event will be logged without consulting with other filters in + * the chain. + * + * @param event The LoggingEvent to decide upon. + * @return The decision of the filter. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const = 0; + + // Data + /** + * Points to the next filter in the filter chain. + */ + FilterPtr next; + }; + + + + /** + * This filter drops all logging events. + * + * You can add this filter to the end of a filter chain to + * switch from the default "accept all unless instructed otherwise" + * filtering behaviour to a "deny all unless instructed otherwise" + * behaviour. + */ + class LOG4CPLUS_EXPORT DenyAllFilter : public Filter { + public: + DenyAllFilter (); + DenyAllFilter (const log4cplus::helpers::Properties&); + + /** + * Always returns the {@link #DENY} regardless of the + * {@link InternalLoggingEvent} parameter. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + }; + + + /** + * This is a very simple filter based on LogLevel matching. + * + * The filter admits two options LogLevelToMatch and + * AcceptOnMatch. If there is an exact match between the value + * of the LogLevelToMatch option and the LogLevel of the {@link + * spi::InternalLoggingEvent}, then the {@link #decide} method returns + * {@link #ACCEPT} in case the AcceptOnMatch option value is set + * to true, if it is false then {@link #DENY} + * is returned. If there is no match, {@link #NEUTRAL} is returned. + */ + class LOG4CPLUS_EXPORT LogLevelMatchFilter : public Filter { + public: + LogLevelMatchFilter(); + LogLevelMatchFilter(const log4cplus::helpers::Properties& p); + + /** + * Return the decision of this filter. + * + * Returns {@link #NEUTRAL} if the LogLevelToMatch + * option is not set or if there is no match. Otherwise, if + * there is a match, then the returned decision is {@link #ACCEPT} + * if the AcceptOnMatch property is set to true. + * The returned decision is {@link #DENY} if the AcceptOnMatch + * property is set to false. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + + private: + // Methods + LOG4CPLUS_PRIVATE void init(); + + // Data + /** Do we return ACCEPT when a match occurs. Default is true. */ + bool acceptOnMatch; + LogLevel logLevelToMatch; + }; + + + + /** + * This is a very simple filter based on LogLevel matching, which can be + * used to reject messages with LogLevels outside a certain range. + * + * The filter admits three options LogLevelMin, LogLevelMax + * and AcceptOnMatch. + * + * If the LogLevel of the Logging event is not between Min and Max + * (inclusive), then {@link #DENY} is returned. + * + * If the Logging event LogLevel is within the specified range, then if + * AcceptOnMatch is true, {@link #ACCEPT} is returned, and if + * AcceptOnMatch is false, {@link #NEUTRAL} is returned. + * + * If LogLevelMin is not defined, then there is no + * minimum acceptable LogLevel (ie a LogLevel is never rejected for + * being too "low"/unimportant). If LogLevelMax is not + * defined, then there is no maximum acceptable LogLevel (ie a + * LogLevel is never rejected for beeing too "high"/important). + * + * Refer to the {@link + * Appender#setThreshold setThreshold} method + * available to all appenders for a more convenient way to + * filter out events by LogLevel. + */ + class LOG4CPLUS_EXPORT LogLevelRangeFilter : public Filter { + public: + // ctors + LogLevelRangeFilter(); + LogLevelRangeFilter(const log4cplus::helpers::Properties& p); + + /** + * Return the decision of this filter. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + + private: + // Methods + LOG4CPLUS_PRIVATE void init(); + + // Data + /** Do we return ACCEPT when a match occurs. Default is true. */ + bool acceptOnMatch; + LogLevel logLevelMin; + LogLevel logLevelMax; + }; + + + + /** + * This is a very simple filter based on string matching. + * + * The filter admits two options StringToMatch and + * AcceptOnMatch. If there is a match between the value of the + * StringToMatch option and the message of the Logging event, + * then the {@link #decide} method returns {@link #ACCEPT} if + * the AcceptOnMatch option value is true, if it is false then + * {@link #DENY} is returned. If there is no match, {@link #NEUTRAL} + * is returned. + */ + class LOG4CPLUS_EXPORT StringMatchFilter : public Filter { + public: + // ctors + StringMatchFilter(); + StringMatchFilter(const log4cplus::helpers::Properties& p); + + /** + * Returns {@link #NEUTRAL} is there is no string match. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + + private: + // Methods + LOG4CPLUS_PRIVATE void init(); + + // Data + /** Do we return ACCEPT when a match occurs. Default is true. */ + bool acceptOnMatch; + log4cplus::tstring stringToMatch; + }; + + /** + * This filter allows using `std::function`. + */ + class LOG4CPLUS_EXPORT FunctionFilter + : public Filter + { + public: + typedef std::function + Function; + + FunctionFilter (Function); + + /** + * Returns result returned by `function`. + */ + virtual FilterResult decide(const InternalLoggingEvent&) const; + + private: + Function function; + }; + + /** + * This is a simple filter based on the string returned by event.getNDC(). + * + * The filter admits three options NeutralOnEmpty, NDCToMatch + * and AcceptOnMatch. + * + * If NeutralOnEmpty is true and NDCToMatch is empty + * then {@link #NEUTRAL} is returned. + * + * If NeutralOnEmpty is true and the value returned by event.getNDC() is empty + * then {@link #NEUTRAL} is returned. + * + * If the string returned by event.getNDC() matches NDCToMatch, then if + * AcceptOnMatch is true, {@link #ACCEPT} is returned, and if + * AcceptOnMatch is false, {@link #DENY} is returned. + * + * If the string returned by event.getNDC() does not match NDCToMatch, then if + * AcceptOnMatch is true, {@link #DENY} is returned, and if + * AcceptOnMatch is false, {@link #ACCEPT} is returned. + * + */ + + class LOG4CPLUS_EXPORT NDCMatchFilter : public Filter + { + public: + // ctors + NDCMatchFilter(); + NDCMatchFilter(const log4cplus::helpers::Properties& p); + + /** + * Returns {@link #NEUTRAL} is there is no string match. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + + private: + // Methods + LOG4CPLUS_PRIVATE void init(); + + // Data + /** Do we return ACCEPT when a match occurs. Default is true. */ + bool acceptOnMatch; + /** return NEUTRAL if event.getNDC() is empty or ndcToMatch is empty. Default is true. */ + bool neutralOnEmpty; + log4cplus::tstring ndcToMatch; + }; + + /** + * This is a simple filter based on the key/value pair stored in MDC. + * + * The filter admits four options NeutralOnEmpty, MDCKeyToMatch + * MDCValueToMatch and AcceptOnMatch. + * + * If NeutralOnEmpty is true and MDCKeyToMatch or MDCValueToMatch + * is empty then {@link #NEUTRAL} is returned. + * + * If NeutralOnEmpty is true and the string returned by event.getMDC(MDCKeyToMatch) is empty + * then {@link #NEUTRAL} is returned. + * + * If the string returned by event.getMDC(MDCKeyToMatch) matches MDCValueToMatch, then if + * AcceptOnMatch is true, {@link #ACCEPT} is returned, and if + * AcceptOnMatch is false, {@link #DENY} is returned. + * + * If the string returned by event.getMDC(MDCKeyToMatch) does not match MDCValueToMatch, then if + * AcceptOnMatch is true, {@link #DENY} is returned, and if + * AcceptOnMatch is false, {@link #ACCEPT} is returned. + * + */ + + class LOG4CPLUS_EXPORT MDCMatchFilter : public Filter + { + public: + // ctors + MDCMatchFilter(); + MDCMatchFilter(const log4cplus::helpers::Properties& p); + + /** + * Returns {@link #NEUTRAL} is there is no string match. + */ + virtual FilterResult decide(const InternalLoggingEvent& event) const; + + private: + // Methods + LOG4CPLUS_PRIVATE void init(); + + // Data + /** Do we return ACCEPT when a match occurs. Default is true. */ + bool acceptOnMatch; + /** return NEUTRAL if mdcKeyToMatch is empty or event::getMDC(mdcKeyValue) is empty or mdcValueToMatch is empty. Default is true. */ + bool neutralOnEmpty; + /** The MDC key to retrieve **/ + log4cplus::tstring mdcKeyToMatch; + /** the MDC value to match **/ + log4cplus::tstring mdcValueToMatch; + }; + + } // end namespace spi +} // end namespace log4cplus + +#endif /* LOG4CPLUS_SPI_FILTER_HEADER_ */ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerfactory.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerfactory.h new file mode 100644 index 0000000..252af4e --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerfactory.h @@ -0,0 +1,65 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loggerfactory.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_LOGGER_FACTORY_HEADER +#define LOG4CPLUS_SPI_LOGGER_FACTORY_HEADER + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +namespace log4cplus { + // Forward Declarations + class Logger; + class Hierarchy; + + namespace spi { + class LoggerImpl; + + /** + * Implement this interface to create new instances of Logger or + * a sub-class of Logger. + */ + class LOG4CPLUS_EXPORT LoggerFactory { + public: + /** + * Creates a new Logger object. + */ + virtual Logger makeNewLoggerInstance(const log4cplus::tstring_view& name, + Hierarchy& h) = 0; + virtual ~LoggerFactory() = 0; + + protected: + virtual LoggerImpl * makeNewLoggerImplInstance( + const log4cplus::tstring_view& name, Hierarchy& h) = 0; + }; + + } // end namespace spi +} // end namespace log4cplus + +#endif // LOG4CPLUS_SPI_LOGGER_FACTORY_HEADER diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerimpl.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerimpl.h new file mode 100644 index 0000000..359b15e --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggerimpl.h @@ -0,0 +1,216 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loggerimpl.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_LOGGER_HEADER_ +#define LOG4CPLUS_SPI_LOGGER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + + +namespace log4cplus { + class DefaultLoggerFactory; + + namespace spi { + + /** + * This is the central class in the log4cplus package. One of the + * distintive features of log4cplus are hierarchical loggers and their + * evaluation. + */ + class LOG4CPLUS_EXPORT LoggerImpl + : public virtual log4cplus::helpers::SharedObject, + public log4cplus::helpers::AppenderAttachableImpl + { + public: + typedef helpers::SharedObjectPtr SharedLoggerImplPtr; + + // Methods + + /** + * Call the appenders in the hierrachy starting at + * this. If no appenders could be found, emit a + * warning. + * + * This method calls all the appenders inherited from the + * hierarchy circumventing any evaluation of whether to log or not + * to log the particular log request. + * + * @param event The event to log. + */ + virtual void callAppenders(const InternalLoggingEvent& event); + + /** + * Close all attached appenders implementing the AppenderAttachable + * interface. + */ + virtual void closeNestedAppenders(); + + /** + * Check whether this logger is enabled for a given LogLevel passed + * as parameter. + * + * @return boolean True if this logger is enabled for ll. + */ + virtual bool isEnabledFor(LogLevel ll) const; + + /** + * This generic form is intended to be used by wrappers. + */ + virtual void log(LogLevel ll, const log4cplus::tstring_view& message, + const char* file=nullptr, int line=-1, + const char* function=nullptr); + + virtual void log(spi::InternalLoggingEvent const &); + + /** + * Starting from this logger, search the logger hierarchy for a + * "set" LogLevel and return it. Otherwise, return the LogLevel of the + * root logger. + * + * The Logger class is designed so that this method executes as + * quickly as possible. + */ + virtual LogLevel getChainedLogLevel() const; + + /** + * Returns the assigned LogLevel, if any, for this Logger. + * + * @return LogLevel - the assigned LogLevel. + */ + LogLevel getLogLevel() const { return this->ll; } + + /** + * Set the LogLevel of this Logger. + */ + void setLogLevel(LogLevel _ll) { this->ll = _ll; } + + /** + * Return the the {@link Hierarchy} where this Logger + * instance is attached. + */ + virtual Hierarchy& getHierarchy() const; + + /** + * Return the logger name. + */ + log4cplus::tstring const & getName() const { return name; } + + /** + * Get the additivity flag for this Logger instance. + */ + bool getAdditivity() const; + + /** + * Set the additivity flag for this Logger instance. + */ + void setAdditivity(bool additive); + + virtual ~LoggerImpl(); + + protected: + // Ctors + /** + * This constructor created a new Logger instance and + * sets its name. + * + * It is intended to be used by sub-classes only. You should not + * create loggers directly. + * + * @param name The name of the logger. + * @param h Hierarchy + */ + LoggerImpl(const log4cplus::tstring_view& name, Hierarchy& h); + + + // Methods + /** + * This method creates a new logging event and logs the event + * without further checks. + */ + virtual void forcedLog(LogLevel ll, + const log4cplus::tstring_view& message, + const char* file, + int line, + const char* function); + + virtual void forcedLog(spi::InternalLoggingEvent const & ev); + + + // Data + /** The name of this logger */ + log4cplus::tstring name; + + /** + * The assigned LogLevel of this logger. + */ + LogLevel ll; + + /** + * The parent of this logger. All loggers have at least one + * ancestor which is the root logger. + */ + SharedLoggerImplPtr parent; + + /** + * Additivity is set to true by default, that is children inherit + * the appenders of their ancestors by default. If this variable is + * set to false then the appenders found in the + * ancestors of this logger are not used. However, the children + * of this logger will inherit its appenders, unless the children + * have their additivity flag set to false too. See + * the user manual for more details. + */ + bool additive; + + private: + // Data + /** Loggers need to know what Hierarchy they are in. */ + Hierarchy& hierarchy; + + // Disallow copying of instances of this class + LoggerImpl(const LoggerImpl&) = delete; + LoggerImpl& operator=(const LoggerImpl&) = delete; + + // Friends + friend class log4cplus::Logger; + friend class log4cplus::DefaultLoggerFactory; + friend class log4cplus::Hierarchy; + }; + + typedef LoggerImpl::SharedLoggerImplPtr SharedLoggerImplPtr; + + } // end namespace spi +} // end namespace log4cplus + +#endif // LOG4CPLUS_SPI_LOGGER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggingevent.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggingevent.h new file mode 100644 index 0000000..7357dc4 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/loggingevent.h @@ -0,0 +1,241 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: loggingevent.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_INTERNAL_LOGGING_EVENT_HEADER_ +#define LOG4CPLUS_SPI_INTERNAL_LOGGING_EVENT_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace log4cplus { + namespace spi { + /** + * The internal representation of logging events. When an affirmative + * decision is made to log then a InternalLoggingEvent + * instance is created. This instance is passed around to the + * different log4cplus components. + * + * This class is of concern to those wishing to extend log4cplus. + */ + class LOG4CPLUS_EXPORT InternalLoggingEvent { + public: + // Ctors + /** + * Instantiate a LoggingEvent from the supplied parameters. + * + * @param logger The logger of this event. + * @param loglevel The LogLevel of this event. + * @param message The message of this event. + * @param filename Name of file where this event has occurred, + * can be NULL. + * @param line Line number in file specified by + * the filename parameter. + * @param function Name of function that is logging this event. + */ + InternalLoggingEvent(const log4cplus::tstring_view& logger, + LogLevel loglevel, const log4cplus::tstring_view& message, + const char* filename, int line, const char * function = nullptr); + + InternalLoggingEvent(const log4cplus::tstring_view& logger, + LogLevel loglevel, const log4cplus::tstring_view& ndc, + MappedDiagnosticContextMap const & mdc, + const log4cplus::tstring_view& message, + const log4cplus::tstring_view& thread, + log4cplus::helpers::Time time, + const log4cplus::tstring_view& file, + int line, const log4cplus::tstring_view & function + = log4cplus::tstring_view ()) LOG4CPLUS_ATTRIBUTE_DEPRECATED; + + InternalLoggingEvent(const log4cplus::tstring_view& logger, + LogLevel loglevel, const log4cplus::tstring_view& ndc, + MappedDiagnosticContextMap const & mdc, + const log4cplus::tstring_view& message, + const log4cplus::tstring_view& thread, + const log4cplus::tstring_view& thread2, + log4cplus::helpers::Time time, + const log4cplus::tstring_view& file, + int line, const log4cplus::tstring_view & function + = log4cplus::tstring_view ()); + + InternalLoggingEvent (); + + InternalLoggingEvent( + const log4cplus::spi::InternalLoggingEvent& rhs); + + virtual ~InternalLoggingEvent(); + + void setLoggingEvent (const log4cplus::tstring_view & logger, + LogLevel ll, const log4cplus::tstring_view & message, + const char * filename, int line, + const char * function = nullptr); + + void setFunction (char const * func); + void setFunction (log4cplus::tstring_view const &); + + + // public virtual methods + /** The application supplied message of logging event. */ + virtual const log4cplus::tstring& getMessage() const; + + /** Returns the 'type' of InternalLoggingEvent. Derived classes + * should override this method. (NOTE: Values <= 1000 are + * reserved for log4cplus and should not be used.) + */ + virtual unsigned int getType() const; + + /** Returns a copy of this object. Derived classes + * should override this method. + */ + virtual std::unique_ptr clone() const; + + + // public methods + /** The logger of the logging event. It is set by + * the LoggingEvent constructor. + */ + const log4cplus::tstring& getLoggerName() const + { + return loggerName; + } + + /** LogLevel of logging event. */ + LogLevel getLogLevel() const + { + return ll; + } + + /** The nested diagnostic context (NDC) of logging event. */ + const log4cplus::tstring& getNDC() const + { + if (!ndcCached) + { + ndc = log4cplus::getNDC().get(); + ndcCached = true; + } + return ndc; + } + + MappedDiagnosticContextMap const & getMDCCopy () const + { + if (!mdcCached) + { + mdc = log4cplus::getMDC().getContext (); + mdcCached = true; + } + return mdc; + } + + tstring const & getMDC (tstring const & key) const; + + /** The name of thread in which this logging event was generated. */ + const log4cplus::tstring& getThread() const + { + if (! threadCached) + { + thread = thread::getCurrentThreadName (); + threadCached = true; + } + return thread; + } + + //! The alternative name of thread in which this logging event + //! was generated. + const log4cplus::tstring& getThread2() const + { + if (! thread2Cached) + { + thread2 = thread::getCurrentThreadName2 (); + thread2Cached = true; + } + return thread2; + } + + + /** Time stamp when the event was created. */ + const log4cplus::helpers::Time& getTimestamp() const + { + return timestamp; + } + + /** The is the file where this log statement was written */ + const log4cplus::tstring& getFile() const + { + return file; + } + + /** The is the line where this log statement was written */ + int getLine() const { return line; } + + log4cplus::tstring const & getFunction () const + { + return function; + } + + void gatherThreadSpecificData () const; + + void swap (InternalLoggingEvent &); + + // public operators + log4cplus::spi::InternalLoggingEvent& + operator=(const log4cplus::spi::InternalLoggingEvent& rhs); + + // static methods + static unsigned int getDefaultType(); + + protected: + // Data + log4cplus::tstring message; + log4cplus::tstring loggerName; + LogLevel ll; + mutable log4cplus::tstring ndc; + mutable MappedDiagnosticContextMap mdc; + mutable log4cplus::tstring thread; + mutable log4cplus::tstring thread2; + log4cplus::helpers::Time timestamp; + log4cplus::tstring file; + log4cplus::tstring function; + int line; + /** Indicates whether or not the Threadname has been retrieved. */ + mutable bool threadCached; + mutable bool thread2Cached; + /** Indicates whether or not the NDC has been retrieved. */ + mutable bool ndcCached; + /** Indicates whether or not the MDC has been retrieved. */ + mutable bool mdcCached; + }; + + } // end namespace spi +} // end namespace log4cplus + +#endif // LOG4CPLUS_SPI_INTERNAL_LOGGING_EVENT_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/objectregistry.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/objectregistry.h new file mode 100644 index 0000000..a63cecd --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/objectregistry.h @@ -0,0 +1,113 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: objectregistry.h +// Created: 3/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_OBJECT_REGISTRY_HEADER_ +#define LOG4CPLUS_SPI_OBJECT_REGISTRY_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include + + +namespace log4cplus { + namespace spi { + + /** + * This is the base class used to implement the functionality required + * by the ObjectRegistry template class. + */ + class LOG4CPLUS_EXPORT ObjectRegistryBase { + public: + // public methods + /** + * Tests to see whether or not an object is bound in the + * registry as name. + */ + bool exists(const log4cplus::tstring& name) const; + + /** + * Returns the names of all registered objects. + */ + std::vector getAllNames() const; + + //! This function is internal implementation detail. + //! It is related to work-around needed for initialization when + //! using C++11 threads and mutexes. + void _enableLocking (bool); + + protected: + // Ctor and Dtor + ObjectRegistryBase(); + virtual ~ObjectRegistryBase(); + + // protected methods + /** + * Used to enter an object into the registry. (The registry now + * owns object.) + */ + bool putVal(const log4cplus::tstring& name, void* object); + + /** + * Used to retrieve an object from the registry. (The registry + * owns the returned pointer.) + */ + void* getVal(const log4cplus::tstring& name) const; + + /** + * Deletes object. + */ + virtual void deleteObject(void *object) const = 0; + + /** + * Deletes all objects from this registry. + */ + virtual void clear(); + + // Types + typedef std::map ObjectMap; + + // Data + thread::Mutex mutex; + ObjectMap data; + + private: + ObjectRegistryBase (ObjectRegistryBase const &); + ObjectRegistryBase & operator = (ObjectRegistryBase const &); + + bool volatile locking; + }; + + } +} + + +#endif // LOG4CPLUS_SPI_OBJECT_REGISTRY_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/rootlogger.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/rootlogger.h new file mode 100644 index 0000000..91d3a31 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/spi/rootlogger.h @@ -0,0 +1,75 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: rootlogger.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SPI_ROOT_LOGGER_HEADER_ +#define LOG4CPLUS_SPI_ROOT_LOGGER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +namespace log4cplus { + namespace spi { + + /** + * RootLogger sits at the top of the logger hierachy. It is a + * regular logger except that it provides several guarantees. + * + * First, it cannot be assigned a NOT_SET_LOG_LEVEL + * LogLevel. Second, since root logger cannot have a parent, the + * getChainedLogLevel method always returns the value of the + * ll field without walking the hierarchy. + */ + class LOG4CPLUS_EXPORT RootLogger : public LoggerImpl { + public: + // Ctors + /** + * The root logger names itself as "root". However, the root + * logger cannot be retrieved by name. + */ + RootLogger(Hierarchy& h, LogLevel ll); + + // Methods + /** + * Return the assigned LogLevel value without walking the logger + * hierarchy. + */ + virtual LogLevel getChainedLogLevel() const; + + /** + * Setting a NOT_SET_LOG_LEVEL value to the LogLevel of the root logger + * may have catastrophic results. We prevent this here. + */ + void setLogLevel(LogLevel); + + }; + + } // end namespace spi +} // end namespace log4cplus + +#endif // LOG4CPLUS_SPI_ROOT_LOGGER_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/streams.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/streams.h new file mode 100644 index 0000000..5d61010 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/streams.h @@ -0,0 +1,55 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: streams.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_STREAMS_HEADER_ +#define LOG4CPLUS_STREAMS_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#include + + +namespace log4cplus +{ + typedef std::basic_ostream tostream; + typedef std::basic_istream tistream; + typedef std::basic_ostringstream tostringstream; + typedef std::basic_istringstream tistringstream; + extern LOG4CPLUS_EXPORT tostream & tcout; + extern LOG4CPLUS_EXPORT tostream & tcerr; +} + +#if defined (UNICODE) && defined (LOG4CPLUS_ENABLE_GLOBAL_C_STRING_STREAM_INSERTER) + +LOG4CPLUS_EXPORT log4cplus::tostream& operator <<(log4cplus::tostream&, const char* psz ); + +#endif + +#endif // LOG4CPLUS_STREAMS_HEADER_ + diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/syslogappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/syslogappender.h new file mode 100644 index 0000000..eb9846a --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/syslogappender.h @@ -0,0 +1,160 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: syslogappender.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_SYSLOG_APPENDER_HEADER_ +#define LOG4CPLUS_SYSLOG_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + + +namespace log4cplus +{ + + /** + * Appends log events to a file. + * + *

Properties

+ *
+ *
ident
+ *
First argument to openlog(), a string that + * will be prepended to every message.
+ * + *
facility
+ *
Facility is used in combination with syslog level in first + * argument to syslog(). It can be one of the supported facility + * names (case insensitive), e.g. auth, cron, kern, mail, news + * etc.
+ * + *
host
+ *
Destination syslog host. When this property is specified, + * messages are sent using UDP to destination host, otherwise + * messages are logged to local syslog.
+ * + *
port
+ *
Destination port of syslog service on host specified by the + * host property. The default value is port 514.
+ * + *
udp
When the syslog is remote, this + * property picks the IP protocol. When the value is true, UDP is + * used. When the value is false, TCP is used. The default value + * is true.
+ * + *
IPv6
+ *
Boolean value specifying whether to use IPv6 (true) or IPv4 + * (false). Default value is false.
+ * + *
+ * + * \note Messages sent to remote syslog using UDP are conforming + * to RFC5424. Messages sent to remote syslog using TCP are + * using octet counting as described in RFC6587. + */ + class LOG4CPLUS_EXPORT SysLogAppender + : public Appender +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + , protected virtual helpers::IConnectorThreadClient +#endif + { + public: + //! Remote syslog IP protocol type. + enum RemoteSyslogType + { + RSTUdp, + RSTTcp + }; + + // Ctors +#if defined (LOG4CPLUS_HAVE_SYSLOG_H) + SysLogAppender(const tstring& ident); +#endif + SysLogAppender(const tstring& ident, const tstring & host, + int port = 514, const tstring & facility = tstring (), + RemoteSyslogType remoteSyslogType = RSTUdp, bool ipv6 = false); + SysLogAppender(const log4cplus::helpers::Properties & properties); + + // Dtor + virtual ~SysLogAppender(); + + // Methods + virtual void close(); + + protected: + virtual int getSysLogLevel(const LogLevel& ll) const; + virtual void append(const spi::InternalLoggingEvent& event); +#if defined (LOG4CPLUS_HAVE_SYSLOG_H) + //! Local syslog (served by `syslog()`) worker function. + void appendLocal(const spi::InternalLoggingEvent& event); +#endif + //! Remote syslog worker function. + void appendRemote(const spi::InternalLoggingEvent& event); + + // Data + tstring ident; + int facility; + + typedef void (SysLogAppender:: * AppendFuncType) ( + const spi::InternalLoggingEvent&); + AppendFuncType appendFunc; + + tstring host; + int port; + RemoteSyslogType remoteSyslogType; + helpers::Socket syslogSocket; + bool connected; + bool ipv6 = false; + + static tstring const remoteTimeFormat; + + void initConnector (); + void openSocket (); + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + virtual thread::Mutex const & ctcGetAccessMutex () const; + virtual helpers::Socket & ctcGetSocket (); + virtual helpers::Socket ctcConnect (); + virtual void ctcSetConnected (); + + helpers::SharedObjectPtr connector; +#endif + + private: + // Disallow copying of instances of this class + SysLogAppender(const SysLogAppender&); + SysLogAppender& operator=(const SysLogAppender&); + + std::string identStr; + tstring hostname; + }; + +} // end namespace log4cplus + + +#endif // LOG4CPLUS_SYSLOG_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/tchar.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/tchar.h new file mode 100644 index 0000000..1c99ba6 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/tchar.h @@ -0,0 +1,63 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +/** @file */ + +#ifndef LOG4CPLUS_TCHAR_HEADER_ +#define LOG4CPLUS_TCHAR_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (_WIN32) +#include +#endif + + +#ifdef UNICODE +# define LOG4CPLUS_TEXT2(STRING) L##STRING +#else +# define LOG4CPLUS_TEXT2(STRING) STRING +#endif // UNICODE +#define LOG4CPLUS_TEXT(STRING) LOG4CPLUS_TEXT2(STRING) + + +namespace log4cplus +{ + +#if defined (UNICODE) +typedef wchar_t tchar; + +#else +typedef char tchar; + +#endif + +} // namespace log4cplus + + +#endif // LOG4CPLUS_TCHAR_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-cxx11.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-cxx11.h new file mode 100644 index 0000000..be3cc0d --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-cxx11.h @@ -0,0 +1,35 @@ +// -*- C++ -*- +// Copyright (C) 2013-2017, Vaclav Zeman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +//! @file +//! This file contains implementations of synchronization +//! primitives using the C++11 API. It does not contain any include +//! guards because it is only a fragment to be included by +//! syncprims.h. + +namespace log4cplus { namespace thread { namespace impl { + +#include "log4cplus/thread/impl/syncprims-pmsm.h" + +} } } // namespace log4cplus { namespace thread { namespace impl { diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-impl.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-impl.h new file mode 100644 index 0000000..2222a01 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-impl.h @@ -0,0 +1,90 @@ +// -*- C++ -*- +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_THREAD_SYNCPRIMS_IMPL_H +#define LOG4CPLUS_THREAD_SYNCPRIMS_IMPL_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + +#include +#include +#include +#include +#include + + +namespace log4cplus { namespace thread { namespace impl { + + +LOG4CPLUS_EXPORT void LOG4CPLUS_ATTRIBUTE_NORETURN + syncprims_throw_exception (char const * const msg, + char const * const file, int line); + + +class SharedMutex + : public SharedMutexImplBase +{ +public: + SharedMutex (); + ~SharedMutex (); + + void rdlock () const; + void wrlock () const; + void rdunlock () const; + void wrunlock () const; + +private: + Mutex m1; + Mutex m2; + Mutex m3; + Semaphore w; + mutable unsigned writer_count; + Semaphore r; + mutable unsigned reader_count; + + SharedMutex (SharedMutex const &); + SharedMutex & operator = (SharedMutex const &); +}; + + +} } } // namespace log4cplus { namespace thread { namespace impl { + + +// Include the appropriate implementations of the classes declared +// above. + +#include + +#undef LOG4CPLUS_THROW_RTE + + +#endif // LOG4CPLUS_THREAD_SYNCPRIMS_IMPL_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-pmsm.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-pmsm.h new file mode 100644 index 0000000..bba97ea --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/syncprims-pmsm.h @@ -0,0 +1,119 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + +//! @file +//! This file contains implementations of reader-writer locking +//! primitive using other primitives, IOW poor man's rwlock. +//! It does not contain any include guards because it is only a fragment +//! to be included by syncprims-{pthreads,win32}.h. + + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + + +// This implements algorithm described in "Concurrent Control with "Readers" +// and "Writers"; P.J. Courtois, F. Heymans, and D.L. Parnas; +// MBLE Research Laboratory; Brussels, Belgium" + + +inline +SharedMutex::SharedMutex () + : m1 () + , m2 () + , m3 () + , w (1, 1) + , writer_count (0) + , r (1, 1) + , reader_count (0) +{ } + + +inline +SharedMutex::~SharedMutex () +{ } + + +inline +void +SharedMutex::rdlock () const +{ + MutexGuard m3_guard (m3); + SemaphoreGuard r_guard (r); + MutexGuard m1_guard (m1); + if (reader_count + 1 == 1) + w.lock (); + + reader_count += 1; +} + + +inline +void +SharedMutex::rdunlock () const +{ + MutexGuard m1_guard (m1); + if (reader_count - 1 == 0) + w.unlock (); + + reader_count -= 1; +} + + +inline +void +SharedMutex::wrlock () const +{ + { + MutexGuard m2_guard (m2); + if (writer_count + 1 == 1) + r.lock (); + + writer_count += 1; + } + try + { + w.lock (); + } + catch (...) + { + MutexGuard m2_guard (m2); + writer_count -= 1; + throw; + } +} + + +inline +void +SharedMutex::wrunlock () const +{ + w.unlock (); + MutexGuard m2_guard (m2); + if (writer_count - 1 == 0) + r.unlock (); + + writer_count -= 1; +} diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/threads-impl.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/threads-impl.h new file mode 100644 index 0000000..2c6ae4f --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/threads-impl.h @@ -0,0 +1,96 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: threads.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_IMPL_THREADS_IMPL_HEADER_ +#define LOG4CPLUS_IMPL_THREADS_IMPL_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (_WIN32) +#include +#endif +#include +#include +#include + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + + +namespace log4cplus { namespace thread { namespace impl { + + +#if defined (LOG4CPLUS_USE_PTHREADS) + +typedef pthread_t os_handle_type; +typedef pthread_t os_id_type; + + +inline +pthread_t +getCurrentThreadId () +{ + return pthread_self (); +} + + +#elif defined (LOG4CPLUS_USE_WIN32_THREADS) + +typedef HANDLE os_handle_type; +typedef DWORD os_id_type; + + +inline +DWORD +getCurrentThreadId () +{ + return GetCurrentThreadId (); +} + + +#elif defined (LOG4CPLUS_SINGLE_THREADED) + +typedef void * os_handle_type; +typedef int os_id_type; + + +inline +int +getCurrentThreadId () +{ + return 1; +} + + +#endif + + +} } } // namespace log4cplus { namespace thread { namespace impl { + + +#endif // LOG4CPLUS_IMPL_THREADS_IMPL_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/tls.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/tls.h new file mode 100644 index 0000000..2c1270e --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/impl/tls.h @@ -0,0 +1,193 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_THREAD_IMPL_TLS_H +#define LOG4CPLUS_THREAD_IMPL_TLS_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + +#if ! defined (INSIDE_LOG4CPLUS) +# error "This header must not be be used outside log4cplus' implementation files." +#endif + +#ifdef LOG4CPLUS_USE_PTHREADS +# include + +#elif defined (LOG4CPLUS_USE_WIN32_THREADS) +# include + +#elif defined (LOG4CPLUS_SINGLE_THREADED) +# include + +#endif + + +namespace log4cplus { namespace thread { namespace impl { + + +typedef void * tls_value_type; + +#ifdef LOG4CPLUS_USE_PTHREADS +typedef pthread_key_t * tls_key_type; +typedef void (* tls_init_cleanup_func_type)(void *); + +#elif defined (LOG4CPLUS_USE_WIN32_THREADS) +typedef DWORD tls_key_type; +typedef PFLS_CALLBACK_FUNCTION tls_init_cleanup_func_type; + +#elif defined (LOG4CPLUS_SINGLE_THREADED) +typedef std::size_t tls_key_type; +typedef void (* tls_init_cleanup_func_type)(void *); + +#endif + + +inline tls_key_type tls_init (tls_init_cleanup_func_type); +inline tls_value_type tls_get_value (tls_key_type); +inline void tls_set_value (tls_key_type, tls_value_type); +inline void tls_cleanup (tls_key_type); + + +#if defined (LOG4CPLUS_USE_PTHREADS) +tls_key_type +tls_init (tls_init_cleanup_func_type cleanupfunc) +{ + pthread_key_t * key = new pthread_key_t; + int ret = pthread_key_create (key, cleanupfunc); + if (LOG4CPLUS_UNLIKELY (ret != 0)) + throw std::system_error(ret, std::system_category (), + "pthread_key_create() failed"); + return key; +} + + +tls_value_type +tls_get_value (tls_key_type key) +{ + return pthread_getspecific (*key); +} + + +void +tls_set_value (tls_key_type key, tls_value_type value) +{ + pthread_setspecific(*key, value); +} + + +void +tls_cleanup (tls_key_type key) +{ + pthread_key_delete (*key); + delete key; +} + + +#elif defined (LOG4CPLUS_USE_WIN32_THREADS) +tls_key_type +tls_init (tls_init_cleanup_func_type cleanupfunc) +{ + DWORD const slot = FlsAlloc (cleanupfunc); + if (LOG4CPLUS_UNLIKELY (slot == FLS_OUT_OF_INDEXES)) + { + DWORD const eno = GetLastError (); + throw std::system_error (static_cast(eno), + std::system_category (), "FlsAlloc() failed"); + } + return slot; +} + + +tls_value_type tls_get_value (tls_key_type k) +{ + return FlsGetValue (k); +} + + +void +tls_set_value (tls_key_type k, tls_value_type value) +{ + FlsSetValue (k, value); +} + + +void +tls_cleanup (tls_key_type k) +{ + FlsFree (k); +} + + +#elif defined (LOG4CPLUS_SINGLE_THREADED) +extern std::vector * tls_single_threaded_values; + + +tls_key_type +tls_init (tls_init_cleanup_func_type) +{ + if (! tls_single_threaded_values) + tls_single_threaded_values = new std::vector; + tls_key_type key = tls_single_threaded_values->size (); + tls_single_threaded_values->resize (key + 1); + return key; +} + + +tls_value_type +tls_get_value (tls_key_type k) +{ + assert (k < tls_single_threaded_values->size ()); + return (*tls_single_threaded_values)[k]; +} + + +void +tls_set_value (tls_key_type k, tls_value_type val) +{ + assert (k < tls_single_threaded_values->size ()); + (*tls_single_threaded_values)[k] = val; +} + + +void +tls_cleanup (tls_key_type k) +{ + assert (k < tls_single_threaded_values->size ()); + (*tls_single_threaded_values)[k] = 0; +} + +#endif + + +} } } // namespace log4cplus { namespace thread { namespace impl { + +#endif // LOG4CPLUS_THREAD_IMPL_TLS_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims-pub-impl.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims-pub-impl.h new file mode 100644 index 0000000..c549d3d --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims-pub-impl.h @@ -0,0 +1,317 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_THREAD_SYNCPRIMS_PUB_IMPL_H +#define LOG4CPLUS_THREAD_SYNCPRIMS_PUB_IMPL_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + +#if (defined (LOG4CPLUS_INLINES_ARE_EXPORTED) \ + && defined (LOG4CPLUS_BUILD_DLL)) \ + || defined (LOG4CPLUS_ENABLE_SYNCPRIMS_PUB_IMPL) +#include + +#if ! defined (LOG4CPLUS_SINGLE_THREADED) +# include +#endif + +#define LOG4CPLUS_THROW_RTE(msg) \ + do { log4cplus::thread::impl::syncprims_throw_exception (msg, __FILE__, \ + __LINE__); } while (0) + +namespace log4cplus { namespace thread { + +namespace impl +{ + +LOG4CPLUS_EXPORT void LOG4CPLUS_ATTRIBUTE_NORETURN + syncprims_throw_exception(char const * const msg, + char const * const file, int line); + +} + +// +// +// + +LOG4CPLUS_INLINE_EXPORT +Mutex::Mutex () + LOG4CPLUS_THREADED (: mtx ()) +{ } + + +LOG4CPLUS_INLINE_EXPORT +Mutex::~Mutex () +{ } + + +LOG4CPLUS_INLINE_EXPORT +void +Mutex::lock () const +{ + LOG4CPLUS_THREADED (mtx.lock ()); +} + + +LOG4CPLUS_INLINE_EXPORT +void +Mutex::unlock () const +{ + LOG4CPLUS_THREADED (mtx.unlock ()); +} + + +// +// +// + +LOG4CPLUS_INLINE_EXPORT +Semaphore::Semaphore (unsigned LOG4CPLUS_THREADED (max), + unsigned LOG4CPLUS_THREADED (initial)) +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + : max_ (max) + , val ((std::min) (max_, initial)) +#endif +{ } + + +LOG4CPLUS_INLINE_EXPORT +Semaphore::~Semaphore () +{ } + + +LOG4CPLUS_INLINE_EXPORT +void +Semaphore::unlock () const +{ +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::lock_guard guard (mtx); + + if (val >= max_) + LOG4CPLUS_THROW_RTE ("Semaphore::unlock(): val >= max"); + + ++val; + cv.notify_all (); +#endif +} + + +LOG4CPLUS_INLINE_EXPORT +void +Semaphore::lock () const +{ +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::unique_lock guard (mtx); + + if (LOG4CPLUS_UNLIKELY(val > max_)) + LOG4CPLUS_THROW_RTE ("Semaphore::unlock(): val > max"); + + while (val == 0) + cv.wait (guard); + + --val; + + if (LOG4CPLUS_UNLIKELY(val >= max_)) + LOG4CPLUS_THROW_RTE ("Semaphore::unlock(): val >= max"); +#endif +} + + +// +// +// + +LOG4CPLUS_INLINE_EXPORT +ManualResetEvent::ManualResetEvent (bool LOG4CPLUS_THREADED (sig)) +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + : signaled (sig) + , sigcount (0) +#endif +{ } + + +LOG4CPLUS_INLINE_EXPORT +ManualResetEvent::~ManualResetEvent () +{ } + + +LOG4CPLUS_INLINE_EXPORT +void +ManualResetEvent::signal () const +{ +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::unique_lock guard (mtx); + + signaled = true; + sigcount += 1; + cv.notify_all (); +#endif +} + + +LOG4CPLUS_INLINE_EXPORT +void +ManualResetEvent::wait () const +{ +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::unique_lock guard (mtx); + + if (! signaled) + { + unsigned prev_count = sigcount; + do + { + cv.wait (guard); + } + while (prev_count == sigcount); + } +#endif +} + + +LOG4CPLUS_INLINE_EXPORT +bool +ManualResetEvent::timed_wait (unsigned long LOG4CPLUS_THREADED (msec)) const +{ +#if defined (LOG4CPLUS_SINGLE_THREADED) + return true; + +#else + std::unique_lock guard (mtx); + + if (! signaled) + { + unsigned prev_count = sigcount; + + std::chrono::steady_clock::time_point const wait_until_time + = std::chrono::steady_clock::now () + + std::chrono::milliseconds (msec); + + do + { + int ret = static_cast( + cv.wait_until (guard, wait_until_time)); + switch (ret) + { + case static_cast(std::cv_status::no_timeout): + break; + + case static_cast(std::cv_status::timeout): + return false; + + default: + guard.unlock (); + guard.release (); + LOG4CPLUS_THROW_RTE ("ManualResetEvent::timed_wait"); + } + } + while (prev_count == sigcount); + } + + return true; +#endif +} + + +LOG4CPLUS_INLINE_EXPORT +void +ManualResetEvent::reset () const +{ +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + std::lock_guard guard (mtx); + + signaled = false; +#endif +} + + +// +// +// + +LOG4CPLUS_INLINE_EXPORT +SharedMutexImplBase::~SharedMutexImplBase () +{ } + + +// +// +// + +LOG4CPLUS_INLINE_EXPORT +SharedMutex::SharedMutex () + : sm (LOG4CPLUS_THREADED (new impl::SharedMutex)) +{ } + + +LOG4CPLUS_INLINE_EXPORT +SharedMutex::~SharedMutex () +{ + LOG4CPLUS_THREADED (delete static_cast(sm)); +} + + +LOG4CPLUS_INLINE_EXPORT +void +SharedMutex::rdlock () const +{ + LOG4CPLUS_THREADED (static_cast(sm)->rdlock ()); +} + + +LOG4CPLUS_INLINE_EXPORT +void +SharedMutex::wrlock () const +{ + LOG4CPLUS_THREADED (static_cast(sm)->wrlock ()); +} + + +LOG4CPLUS_INLINE_EXPORT +void +SharedMutex::rdunlock () const +{ + LOG4CPLUS_THREADED (static_cast(sm)->rdunlock ()); +} + + +LOG4CPLUS_INLINE_EXPORT +void +SharedMutex::wrunlock () const +{ + LOG4CPLUS_THREADED (static_cast(sm)->wrunlock ()); +} + + +} } // namespace log4cplus { namespace thread { + +#endif // LOG4CPLUS_ENABLE_SYNCPRIMS_PUB_IMPL + +#endif // LOG4CPLUS_THREAD_SYNCPRIMS_PUB_IMPL_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims.h new file mode 100644 index 0000000..611d32a --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/syncprims.h @@ -0,0 +1,335 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_THREAD_SYNCPRIMS_H +#define LOG4CPLUS_THREAD_SYNCPRIMS_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + + +namespace log4cplus { namespace thread { + + +template +class SyncGuard +{ +public: + SyncGuard (); + SyncGuard (SyncPrim const &); + ~SyncGuard (); + SyncGuard (SyncGuard const &) = delete; + SyncGuard & operator = (SyncGuard const &) = delete; + + + void lock (); + void unlock (); + void attach (SyncPrim const &); + void attach_and_lock (SyncPrim const &); + void detach (); + +private: + SyncPrim const * sp; +}; + + +class LOG4CPLUS_EXPORT Mutex +{ +public: + Mutex (); + ~Mutex (); + Mutex (Mutex const &) = delete; + Mutex & operator = (Mutex const &) = delete; + + void lock () const; + void unlock () const; + +private: + LOG4CPLUS_THREADED (mutable std::recursive_mutex mtx;) +}; + + +typedef SyncGuard MutexGuard; + + +class LOG4CPLUS_EXPORT Semaphore +{ +public: + Semaphore (unsigned max, unsigned initial); + ~Semaphore (); + Semaphore (Semaphore const &) = delete; + Semaphore & operator = (Semaphore const &) = delete; + + void lock () const; + void unlock () const; + +private: +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + mutable std::mutex mtx; + mutable std::condition_variable cv; + mutable unsigned max_; + mutable unsigned val; +#endif +}; + + +typedef SyncGuard SemaphoreGuard; + + +class LOG4CPLUS_EXPORT ManualResetEvent +{ +public: + explicit ManualResetEvent (bool = false); + ~ManualResetEvent (); + ManualResetEvent (ManualResetEvent const &) = delete; + ManualResetEvent & operator = (ManualResetEvent const &) = delete; + + void signal () const; + void wait () const; + bool timed_wait (unsigned long msec) const; + void reset () const; + +private: +#if ! defined (LOG4CPLUS_SINGLE_THREADED) + mutable std::mutex mtx; + mutable std::condition_variable cv; + mutable bool signaled; + mutable unsigned sigcount; +#endif +}; + + +class SharedMutexImplBase +{ +protected: + ~SharedMutexImplBase (); +}; + + +template +class SyncGuardFunc +{ +public: + SyncGuardFunc (SyncPrim const &); + ~SyncGuardFunc (); + + void lock (); + void unlock (); + void attach (SyncPrim const &); + void detach (); + +private: + SyncPrim const * sp; + + SyncGuardFunc (SyncGuardFunc const &); + SyncGuardFunc & operator = (SyncGuardFunc const &); +}; + + +class LOG4CPLUS_EXPORT SharedMutex +{ +public: + SharedMutex (); + ~SharedMutex (); + + void rdlock () const; + void rdunlock () const; + + void wrlock () const; + void wrunlock () const; + +private: + SharedMutexImplBase * sm; + + SharedMutex (SharedMutex const &); + SharedMutex & operator = (SharedMutex const &); +}; + + +typedef SyncGuardFunc SharedMutexReaderGuard; + + +typedef SyncGuardFunc SharedMutexWriterGuard; + + +// +// +// + +template +inline +SyncGuard::SyncGuard () + : sp (0) +{ } + + +template +inline +SyncGuard::SyncGuard (SyncPrim const & m) + : sp (&m) +{ + sp->lock (); +} + + +template +inline +SyncGuard::~SyncGuard () +{ + if (sp) + sp->unlock (); +} + + +template +inline +void +SyncGuard::lock () +{ + sp->lock (); +} + + +template +inline +void +SyncGuard::unlock () +{ + sp->unlock (); +} + + +template +inline +void +SyncGuard::attach (SyncPrim const & m) +{ + sp = &m; +} + + +template +inline +void +SyncGuard::attach_and_lock (SyncPrim const & m) +{ + attach (m); + try + { + lock(); + } + catch (...) + { + detach (); + throw; + } +} + + +template +inline +void +SyncGuard::detach () +{ + sp = 0; +} + + +// +// +// + +template +inline +SyncGuardFunc::SyncGuardFunc (SyncPrim const & m) + : sp (&m) +{ + (sp->*lock_func) (); +} + + +template +inline +SyncGuardFunc::~SyncGuardFunc () +{ + if (sp) + (sp->*unlock_func) (); +} + + +template +inline +void +SyncGuardFunc::lock () +{ + (sp->*lock_func) (); +} + + +template +inline +void +SyncGuardFunc::unlock () +{ + (sp->*unlock_func) (); +} + + +template +inline +void +SyncGuardFunc::attach (SyncPrim const & m) +{ + sp = &m; +} + + +template +inline +void +SyncGuardFunc::detach () +{ + sp = 0; +} + + +} } // namespace log4cplus { namespace thread { + + +#endif // LOG4CPLUS_THREAD_SYNCPRIMS_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/threads.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/threads.h new file mode 100644 index 0000000..c9ce7d4 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/thread/threads.h @@ -0,0 +1,113 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: threads.h +// Created: 6/2001 +// Author: Tad E. Smith +// +// +// Copyright 2001-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_THREADS_HEADER_ +#define LOG4CPLUS_THREADS_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include + +#include +#include + + +namespace log4cplus { namespace thread { + + +LOG4CPLUS_EXPORT log4cplus::tstring const & getCurrentThreadName(); +LOG4CPLUS_EXPORT log4cplus::tstring const & getCurrentThreadName2(); +LOG4CPLUS_EXPORT void setCurrentThreadName(const log4cplus::tstring & name); +LOG4CPLUS_EXPORT void setCurrentThreadName2(const log4cplus::tstring & name); +LOG4CPLUS_EXPORT void yield(); +LOG4CPLUS_EXPORT void blockAllSignals(); + +/** + * This class blocks all POSIX signals when created and unblocks them when + * destroyed. + */ +class LOG4CPLUS_EXPORT SignalsBlocker +{ +public: + SignalsBlocker(); + ~SignalsBlocker(); + +private: + struct SignalsBlockerImpl; + std::unique_ptr impl; +}; + + +#ifndef LOG4CPLUS_SINGLE_THREADED + + +/** + * There are many cross-platform C++ Threading libraries. The goal of + * this class is not to replace (or match in functionality) those + * libraries. The goal of this class is to provide a simple Threading + * class with basic functionality. + */ +class LOG4CPLUS_EXPORT AbstractThread + : public virtual log4cplus::helpers::SharedObject +{ +public: + AbstractThread(); + // Disallow copying of instances of this class. + AbstractThread(const AbstractThread&) = delete; + AbstractThread& operator=(const AbstractThread&) = delete; + + bool isRunning() const; + virtual void start(); + void join () const; + virtual void run() = 0; + +protected: + // Force objects to be constructed on the heap + virtual ~AbstractThread(); + +private: + enum Flags + { + fRUNNING = 1, + fJOINED = 2 + }; + + std::unique_ptr thread; + mutable std::atomic flags; +}; + +typedef helpers::SharedObjectPtr AbstractThreadPtr; + + +#endif // LOG4CPLUS_SINGLE_THREADED + + +} } // namespace log4cplus { namespace thread { + + +#endif // LOG4CPLUS_THREADS_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/tracelogger.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/tracelogger.h new file mode 100644 index 0000000..4fdbcce --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/tracelogger.h @@ -0,0 +1,87 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: tracelogger.h +// Created: 1/2009 +// Author: Vaclav Haisman +// +// +// Copyright 2009-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_TRACELOGGER_H +#define LOG4CPLUS_TRACELOGGER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include + + +namespace log4cplus +{ + + +/** + * This class is used to produce "Trace" logging. When an instance of + * this class is created, it will log a "ENTER: " + msg + * log message if TRACE_LOG_LEVEL is enabled for logger. + * When an instance of this class is destroyed, it will log a + * "ENTER: " + msg log message if TRACE_LOG_LEVEL is enabled + * for logger. + *

+ * @see LOG4CPLUS_TRACE + */ +class TraceLogger +{ +public: + TraceLogger(Logger l, log4cplus::tstring _msg, + const char* _file = LOG4CPLUS_CALLER_FILE (), + int _line = LOG4CPLUS_CALLER_LINE (), + char const * _function = LOG4CPLUS_CALLER_FUNCTION ()) + : logger(std::move (l)), msg(std::move (_msg)), file(_file), + function(_function), line(_line) + { + if(logger.isEnabledFor(TRACE_LOG_LEVEL)) + logger.forcedLog(TRACE_LOG_LEVEL, LOG4CPLUS_TEXT("ENTER: ") + msg, + file, line, function); + } + + ~TraceLogger() + { + if(logger.isEnabledFor(TRACE_LOG_LEVEL)) + logger.forcedLog(TRACE_LOG_LEVEL, LOG4CPLUS_TEXT("EXIT: ") + msg, + file, line, function); + } + +private: + TraceLogger (TraceLogger const &); + TraceLogger & operator = (TraceLogger const &); + + Logger logger; + log4cplus::tstring msg; + const char* file; + const char* function; + int line; +}; + + +} // log4cplus + + +#endif // LOG4CPLUS_TRACELOGGER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/tstring.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/tstring.h new file mode 100644 index 0000000..6eb7ee7 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/tstring.h @@ -0,0 +1,154 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: tstring.h +// Created: 4/2003 +// Author: Tad E. Smith +// +// +// Copyright 2003-2017 Tad E. 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. + +/** @file */ + +#ifndef LOG4CPLUS_TSTRING_HEADER_ +#define LOG4CPLUS_TSTRING_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +namespace log4cplus +{ + +using tstring = std::basic_string; +using tstring_view = std::basic_string_view; + +namespace helpers +{ + +inline +std::string +tostring (char const * str) +{ + return std::string (str); +} + +template +inline +std::enable_if_t< + std::is_convertible_v + && std::is_convertible_v, + std::string> +tostring (SV const & sv) +{ + return std::string (sv); +} + +inline +std::string +tostring (std::string const & str) +{ + return str; +} + +inline +std::string const & +tostring (std::string & str) +{ + return str; +} + +inline +std::string +tostring (std::string && str) +{ + return std::move (str); +} + + +inline +std::wstring +towstring (wchar_t const * str) +{ + return std::wstring (str); +} + +template +inline +std::enable_if_t< + std::is_convertible_v + && std::is_convertible_v, + std::wstring> +towstring (SV const & sv) +{ + return std::wstring (sv); +} + +inline +std::wstring +towstring (std::wstring const & str) +{ + return str; +} + +inline +std::wstring const & +towstring (std::wstring & str) +{ + return str; +} + +inline +std::wstring +towstring (std::wstring && str) +{ + return std::move (str); +} + + +LOG4CPLUS_EXPORT std::string tostring(const std::wstring&); +LOG4CPLUS_EXPORT std::string tostring(const std::wstring_view&); +LOG4CPLUS_EXPORT std::string tostring(wchar_t const *); + +LOG4CPLUS_EXPORT std::wstring towstring(const std::string&); +LOG4CPLUS_EXPORT std::wstring towstring(const std::string_view&); +LOG4CPLUS_EXPORT std::wstring towstring(char const *); + +} // namespace helpers + +#ifdef UNICODE + +#define LOG4CPLUS_C_STR_TO_TSTRING(STRING) log4cplus::helpers::towstring(STRING) +#define LOG4CPLUS_STRING_TO_TSTRING(STRING) log4cplus::helpers::towstring(STRING) +#define LOG4CPLUS_TSTRING_TO_STRING(STRING) log4cplus::helpers::tostring(STRING) + +#else // UNICODE + +#define LOG4CPLUS_C_STR_TO_TSTRING(STRING) (std::string(STRING)) +#define LOG4CPLUS_STRING_TO_TSTRING(STRING) STRING +#define LOG4CPLUS_TSTRING_TO_STRING(STRING) STRING + +#endif // UNICODE + +} // namespace log4cplus + + +#endif // LOG4CPLUS_TSTRING_HEADER_ diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/version.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/version.h new file mode 100644 index 0000000..79d4f07 --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/version.h @@ -0,0 +1,58 @@ +// -*- C++ -*- +// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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. + + +#if ! defined (LOG4CPLUS_VERSION_H) +#define LOG4CPLUS_VERSION_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#define LOG4CPLUS_MAKE_VERSION(major, minor, point) \ + (major * 1000 * 1000u + minor * 1000u + point) + +#define LOG4CPLUS_MAKE_VERSION_STR(major, minor, point) \ + #major "." #minor "." #point + +//! This is log4cplus version number as unsigned integer. This must +//! be kept on a single line. It is used by Autotool and CMake build +//! systems to parse version number. +#define LOG4CPLUS_VERSION LOG4CPLUS_MAKE_VERSION(3, 0, 0) + +//! This is log4cplus version number as a string. +#define LOG4CPLUS_VERSION_STR LOG4CPLUS_MAKE_VERSION_STR(3, 0, 0) + + +namespace log4cplus +{ + +extern LOG4CPLUS_EXPORT unsigned const version; +extern LOG4CPLUS_EXPORT char const versionStr[]; + +} + +#endif diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/win32consoleappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/win32consoleappender.h new file mode 100644 index 0000000..0339c7f --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/win32consoleappender.h @@ -0,0 +1,93 @@ +// -*- C++ -*- +// Copyright (C) 2009-2017, Vaclav Haisman. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modifica- +// tion, 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. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 +// APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- +// DING, 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 LOG4CPLUS_WIN32CONSOLEAPPENDER_H +#define LOG4CPLUS_WIN32CONSOLEAPPENDER_H + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined(_WIN32) && defined (LOG4CPLUS_HAVE_WIN32_CONSOLE) + +#include + + +namespace log4cplus +{ + + /** + * Prints events to Win32 console. + * + *

Properties

+ *
+ *
AllocConsole
+ *
This boolean property specifies whether or not this appender + * will try to allocate new console using the + * AllocConsole() Win32 function.
+ * + *
logToStdErr
+ *
When it is set true, the output will be into + * STD_ERROR_HANDLE instead of STD_OUTPUT_HANDLE. + *
+ * + *
TextColor
+ *
See MSDN documentation for + * + * Character Attributes. + *
+ */ + class LOG4CPLUS_EXPORT Win32ConsoleAppender + : public Appender + { + public: + explicit Win32ConsoleAppender (bool allocConsole = true, + bool logToStdErr = false, unsigned int textColor = 0); + Win32ConsoleAppender (helpers::Properties const & properties); + virtual ~Win32ConsoleAppender (); + + virtual void close (); + + protected: + virtual void append (spi::InternalLoggingEvent const &); + + void write_handle (void *, tchar const *, std::size_t); + void write_console (void *, tchar const *, std::size_t); + + bool alloc_console; + bool log_to_std_err; + unsigned int text_color; + + private: + Win32ConsoleAppender (Win32ConsoleAppender const &); + Win32ConsoleAppender & operator = (Win32ConsoleAppender const &); + }; + +} // namespace log4cplus + +#endif + +#endif // LOG4CPLUS_WIN32CONSOLEAPPENDER_H diff --git a/hgdriver/3rdparty/log4cplus/include/log4cplus/win32debugappender.h b/hgdriver/3rdparty/log4cplus/include/log4cplus/win32debugappender.h new file mode 100644 index 0000000..31cbc3d --- /dev/null +++ b/hgdriver/3rdparty/log4cplus/include/log4cplus/win32debugappender.h @@ -0,0 +1,69 @@ +// -*- C++ -*- +// Module: Log4CPLUS +// File: win32debugappender.h +// Created: 12/2003 +// Author: Eduardo Francos, Odalio SARL +// +// +// Copyright 2003-2017 Odalio SARL +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @file */ + +#ifndef LOG4CPLUS_WIN32DEBUG_APPENDER_HEADER_ +#define LOG4CPLUS_WIN32DEBUG_APPENDER_HEADER_ + +#include + +#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE) +#pragma once +#endif + +#if defined (LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING) + +#include + + +namespace log4cplus { + + /** + * Prints log events using OutputDebugString(). + */ + class LOG4CPLUS_EXPORT Win32DebugAppender + : public Appender + { + public: + // Ctors + Win32DebugAppender(); + Win32DebugAppender(const log4cplus::helpers::Properties& properties); + + // Dtor + virtual ~Win32DebugAppender(); + + // Methods + virtual void close(); + + protected: + virtual void append(const log4cplus::spi::InternalLoggingEvent& event); + + private: + // Disallow copying of instances of this class + Win32DebugAppender(const Win32DebugAppender&); + Win32DebugAppender& operator=(const Win32DebugAppender&); + }; + +} // end namespace log4cplus + +#endif // LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING +#endif // LOG4CPLUS_WIN32DEBUG_APPENDER_HEADER_ diff --git a/hgdriver/3rdparty/nick/StopWatch.h b/hgdriver/3rdparty/nick/StopWatch.h new file mode 100644 index 0000000..afa4ee9 --- /dev/null +++ b/hgdriver/3rdparty/nick/StopWatch.h @@ -0,0 +1,34 @@ +#pragma once +#include + +class StopWatch +{ +public: + StopWatch() { + _start = std::chrono::steady_clock::now(); + } + + void reset() { + _start = std::chrono::steady_clock::now(); + } + + double elapsed_s() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_ms() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_us() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + + double elapsed_ns() { + return std::chrono::duration(std::chrono::steady_clock::now() - _start).count(); + } + +private: + std::chrono::steady_clock::time_point _start; +}; + diff --git a/hgdriver/3rdparty/nick/callbackdefines.h b/hgdriver/3rdparty/nick/callbackdefines.h new file mode 100644 index 0000000..74dcc41 --- /dev/null +++ b/hgdriver/3rdparty/nick/callbackdefines.h @@ -0,0 +1,10 @@ +锘#ifndef CALLBACKDEFINESH +#define CALLBACKDEFINESH + +typedef void(*usbreport_callback)(int conditioncode,void* usrdata); + +typedef void(*usbcallback)(int conditioncode,void* usrdata); + +typedef void(*onimagecallback)(void* mat, int bpp, int statuscode); + +#endif diff --git a/hgdriver/3rdparty/nick/filetools.h b/hgdriver/3rdparty/nick/filetools.h new file mode 100644 index 0000000..b203ad1 --- /dev/null +++ b/hgdriver/3rdparty/nick/filetools.h @@ -0,0 +1,65 @@ +#pragma once +#include +#ifdef WIN32 +#include +#endif +#include +#include + +class FileTools +{ +public: + + + static std::vector getFiles(std::string path) + { + std::vector files; + getFiles(path, files); + return files; + } + + static void write_log(std::string filename, std::string log) + { + //std::string savepath; + //std::string str = "D:"; + //savepath = str+"\\"+filename; + std::ofstream ofs(filename, std::ios::app); + + time_t timp; + struct tm* p; + time(&timp); + p=localtime(&timp); + ofs << p->tm_year+1900 << "/" << p->tm_mon+1 << "/" << p->tm_mday << " " << p->tm_hour << ":" << p->tm_min << ":" << p->tm_sec << " "<& files) + { +#ifdef WIN32 + //文件句柄 + long hFile = 0; + //文件信息 + struct _finddata_t fileinfo; + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo))!=-1) + { + do + { + //如果是目录,迭代之 + //如果不是,加入列表 + if ((fileinfo.attrib & _A_SUBDIR)) + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) + getFiles(p.assign(path).append("\\").append(fileinfo.name), files); + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } +#endif + } + +}; diff --git a/hgdriver/3rdparty/nick/snowflake.h b/hgdriver/3rdparty/nick/snowflake.h new file mode 100644 index 0000000..963aa69 --- /dev/null +++ b/hgdriver/3rdparty/nick/snowflake.h @@ -0,0 +1,106 @@ +#pragma once +#include +#include +#include +#include + +class snowflake_nonlock +{ +public: + void lock() + { + } + void unlock() + { + } +}; + +template +class snowflake +{ + using lock_type = Lock; + static constexpr int64_t TWEPOCH = Twepoch; + static constexpr int64_t WORKER_ID_BITS = 5L; + static constexpr int64_t DATACENTER_ID_BITS = 5L; + static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1; + static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1; + static constexpr int64_t SEQUENCE_BITS = 12L; + static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS; + static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; + static constexpr int64_t TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; + static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1; + + using time_point = std::chrono::time_point; + + time_point start_time_point_ = std::chrono::steady_clock::now(); + int64_t start_millsecond_ = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + + int64_t last_timestamp_ = -1; + int64_t workerid_ = 0; + int64_t datacenterid_ = 0; + int64_t sequence_ = 0; + lock_type lock_; +public: + snowflake() = default; + + snowflake(const snowflake&) = delete; + + snowflake& operator=(const snowflake&) = delete; + + void init(int64_t workerid, int64_t datacenterid) + { + if (workerid > MAX_WORKER_ID || workerid < 0) { + throw std::runtime_error("worker Id can't be greater than 31 or less than 0"); + } + + if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) { + throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0"); + } + + workerid_ = workerid; + datacenterid_ = datacenterid; + } + + int64_t nextid() + { + std::lock_guard lock(lock_); + //std::chrono::steady_clock cannot decrease as physical time moves forward + auto timestamp = millsecond(); + if (last_timestamp_ == timestamp) + { + sequence_ = (sequence_ + 1)&SEQUENCE_MASK; + if (sequence_ == 0) + { + timestamp = wait_next_millis(last_timestamp_); + } + } + else + { + sequence_ = 0; + } + + last_timestamp_ = timestamp; + + return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) + | (datacenterid_ << DATACENTER_ID_SHIFT) + | (workerid_ << WORKER_ID_SHIFT) + | sequence_; + } + +private: + int64_t millsecond() const noexcept + { + auto diff = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time_point_); + return start_millsecond_ + diff.count(); + } + + int64_t wait_next_millis(int64_t last) const noexcept + { + auto timestamp = millsecond(); + while (timestamp <= last) + { + timestamp = millsecond(); + } + return timestamp; + } +}; \ No newline at end of file diff --git a/hgdriver/3rdparty/opencv/include/opencv/cv.h b/hgdriver/3rdparty/opencv/include/opencv/cv.h new file mode 100644 index 0000000..19a74e2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cv.h @@ -0,0 +1,73 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CV_H +#define OPENCV_OLD_CV_H + +#if defined(_MSC_VER) + #define CV_DO_PRAGMA(x) __pragma(x) + #define __CVSTR2__(x) #x + #define __CVSTR1__(x) __CVSTR2__(x) + #define __CVMSVCLOC__ __FILE__ "("__CVSTR1__(__LINE__)") : " + #define CV_MSG_PRAGMA(_msg) CV_DO_PRAGMA(message (__CVMSVCLOC__ _msg)) +#elif defined(__GNUC__) + #define CV_DO_PRAGMA(x) _Pragma (#x) + #define CV_MSG_PRAGMA(_msg) CV_DO_PRAGMA(message (_msg)) +#else + #define CV_DO_PRAGMA(x) + #define CV_MSG_PRAGMA(_msg) +#endif +#define CV_WARNING(x) CV_MSG_PRAGMA("Warning: " #x) + +//CV_WARNING("This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module") + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/photo/photo_c.h" +#include "opencv2/video/tracking_c.h" +#include "opencv2/objdetect/objdetect_c.h" + +#if !defined(CV_IMPL) +#define CV_IMPL extern "C" +#endif //CV_IMPL + +#endif // __OPENCV_OLD_CV_H_ diff --git a/hgdriver/3rdparty/opencv/include/opencv/cv.hpp b/hgdriver/3rdparty/opencv/include/opencv/cv.hpp new file mode 100644 index 0000000..8673956 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cv.hpp @@ -0,0 +1,60 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CV_HPP +#define OPENCV_OLD_CV_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cv.h" +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/photo.hpp" +#include "opencv2/video.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/calib3d.hpp" +#include "opencv2/objdetect.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cvaux.h b/hgdriver/3rdparty/opencv/include/opencv/cvaux.h new file mode 100644 index 0000000..c0367cc --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cvaux.h @@ -0,0 +1,57 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_AUX_H +#define OPENCV_OLD_AUX_H + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/photo/photo_c.h" +#include "opencv2/video/tracking_c.h" +#include "opencv2/objdetect/objdetect_c.h" + +#endif + +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/opencv/cvaux.hpp b/hgdriver/3rdparty/opencv/include/opencv/cvaux.hpp new file mode 100644 index 0000000..4888eef --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cvaux.hpp @@ -0,0 +1,52 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_AUX_HPP +#define OPENCV_OLD_AUX_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cvaux.h" +#include "opencv2/core/utility.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cvwimage.h b/hgdriver/3rdparty/opencv/include/opencv/cvwimage.h new file mode 100644 index 0000000..ec0ab14 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cvwimage.h @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to +// this license. If you do not agree to this license, do not download, +// install, copy or use the software. +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2008, Google, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation or contributors may not be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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 OPENCV_OLD_WIMAGE_HPP +#define OPENCV_OLD_WIMAGE_HPP + +#include "opencv2/core/wimage.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cxcore.h b/hgdriver/3rdparty/opencv/include/opencv/cxcore.h new file mode 100644 index 0000000..dc070c7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cxcore.h @@ -0,0 +1,52 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CXCORE_H +#define OPENCV_OLD_CXCORE_H + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "opencv2/core/core_c.h" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cxcore.hpp b/hgdriver/3rdparty/opencv/include/opencv/cxcore.hpp new file mode 100644 index 0000000..c371677 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cxcore.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CXCORE_HPP +#define OPENCV_OLD_CXCORE_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cxcore.h" +#include "opencv2/core.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cxeigen.hpp b/hgdriver/3rdparty/opencv/include/opencv/cxeigen.hpp new file mode 100644 index 0000000..1d3df91 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cxeigen.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_EIGEN_HPP +#define OPENCV_OLD_EIGEN_HPP + +#include "opencv2/core/eigen.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/cxmisc.h b/hgdriver/3rdparty/opencv/include/opencv/cxmisc.h new file mode 100644 index 0000000..9b9bc82 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/cxmisc.h @@ -0,0 +1,8 @@ +#ifndef OPENCV_OLD_CXMISC_H +#define OPENCV_OLD_CXMISC_H + +#ifdef __cplusplus +# include "opencv2/core/utility.hpp" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/highgui.h b/hgdriver/3rdparty/opencv/include/opencv/highgui.h new file mode 100644 index 0000000..69b394e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/highgui.h @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_HIGHGUI_H +#define OPENCV_OLD_HIGHGUI_H + +#include "opencv2/core/core_c.h" +#include "opencv2/highgui/highgui_c.h" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv/ml.h b/hgdriver/3rdparty/opencv/include/opencv/ml.h new file mode 100644 index 0000000..0c376ba --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv/ml.h @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_ML_H +#define OPENCV_OLD_ML_H + +#include "opencv2/core/core_c.h" +#include "opencv2/ml.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core.hpp new file mode 100644 index 0000000..c9ae4b9 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core.hpp @@ -0,0 +1,3288 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +// Copyright (C) 2015, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_HPP +#define OPENCV_CORE_HPP + +#ifndef __cplusplus +# error core.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/version.hpp" +#include "opencv2/core/base.hpp" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/traits.hpp" +#include "opencv2/core/matx.hpp" +#include "opencv2/core/types.hpp" +#include "opencv2/core/mat.hpp" +#include "opencv2/core/persistence.hpp" + +/** +@defgroup core Core functionality +@{ + @defgroup core_basic Basic structures + @defgroup core_c C structures and operations + @{ + @defgroup core_c_glue Connections with C++ + @} + @defgroup core_array Operations on arrays + @defgroup core_async Asynchronous API + @defgroup core_xml XML/YAML Persistence + @defgroup core_cluster Clustering + @defgroup core_utils Utility and system functions and macros + @{ + @defgroup core_logging Logging facilities + @defgroup core_utils_sse SSE utilities + @defgroup core_utils_neon NEON utilities + @defgroup core_utils_vsx VSX utilities + @defgroup core_utils_softfloat Softfloat support + @defgroup core_utils_samples Utility functions for OpenCV samples + @} + @defgroup core_opengl OpenGL interoperability + @defgroup core_ipp Intel IPP Asynchronous C/C++ Converters + @defgroup core_optim Optimization Algorithms + @defgroup core_directx DirectX interoperability + @defgroup core_eigen Eigen support + @defgroup core_opencl OpenCL support + @defgroup core_va_intel Intel VA-API/OpenCL (CL-VA) interoperability + @defgroup core_hal Hardware Acceleration Layer + @{ + @defgroup core_hal_functions Functions + @defgroup core_hal_interface Interface + @defgroup core_hal_intrin Universal intrinsics + @{ + @defgroup core_hal_intrin_impl Private implementation helpers + @} + @} +@} + */ + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +/*! @brief Class passed to an error. + +This class encapsulates all or almost all necessary +information about the error happened in the program. The exception is +usually constructed and thrown implicitly via CV_Error and CV_Error_ macros. +@see error + */ +class CV_EXPORTS Exception : public std::exception +{ +public: + /*! + Default constructor + */ + Exception(); + /*! + Full constructor. Normally the constructor is not called explicitly. + Instead, the macros CV_Error(), CV_Error_() and CV_Assert() are used. + */ + Exception(int _code, const String& _err, const String& _func, const String& _file, int _line); + virtual ~Exception() throw(); + + /*! + \return the error description and the context as a text string. + */ + virtual const char *what() const throw() CV_OVERRIDE; + void formatMessage(); + + String msg; ///< the formatted error message + + int code; ///< error code @see CVStatus + String err; ///< error description + String func; ///< function name. Available only when the compiler supports getting it + String file; ///< source file name where the error has occurred + int line; ///< line number in the source file where the error has occurred +}; + +/*! @brief Signals an error and raises the exception. + +By default the function prints information about the error to stderr, +then it either stops if cv::setBreakOnError() had been called before or raises the exception. +It is possible to alternate error processing by using #redirectError(). +@param exc the exception raisen. +@deprecated drop this version + */ +CV_EXPORTS void error( const Exception& exc ); + +enum SortFlags { SORT_EVERY_ROW = 0, //!< each matrix row is sorted independently + SORT_EVERY_COLUMN = 1, //!< each matrix column is sorted + //!< independently; this flag and the previous one are + //!< mutually exclusive. + SORT_ASCENDING = 0, //!< each matrix row is sorted in the ascending + //!< order. + SORT_DESCENDING = 16 //!< each matrix row is sorted in the + //!< descending order; this flag and the previous one are also + //!< mutually exclusive. + }; + +//! @} core_utils + +//! @addtogroup core +//! @{ + +//! Covariation flags +enum CovarFlags { + /** The output covariance matrix is calculated as: + \f[\texttt{scale} \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...]^T \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...],\f] + The covariance matrix will be nsamples x nsamples. Such an unusual covariance matrix is used + for fast PCA of a set of very large vectors (see, for example, the EigenFaces technique for + face recognition). Eigenvalues of this "scrambled" matrix match the eigenvalues of the true + covariance matrix. The "true" eigenvectors can be easily calculated from the eigenvectors of + the "scrambled" covariance matrix. */ + COVAR_SCRAMBLED = 0, + /**The output covariance matrix is calculated as: + \f[\texttt{scale} \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...] \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...]^T,\f] + covar will be a square matrix of the same size as the total number of elements in each input + vector. One and only one of #COVAR_SCRAMBLED and #COVAR_NORMAL must be specified.*/ + COVAR_NORMAL = 1, + /** If the flag is specified, the function does not calculate mean from + the input vectors but, instead, uses the passed mean vector. This is useful if mean has been + pre-calculated or known in advance, or if the covariance matrix is calculated by parts. In + this case, mean is not a mean vector of the input sub-set of vectors but rather the mean + vector of the whole set.*/ + COVAR_USE_AVG = 2, + /** If the flag is specified, the covariance matrix is scaled. In the + "normal" mode, scale is 1./nsamples . In the "scrambled" mode, scale is the reciprocal of the + total number of elements in each input vector. By default (if the flag is not specified), the + covariance matrix is not scaled ( scale=1 ).*/ + COVAR_SCALE = 4, + /** If the flag is + specified, all the input vectors are stored as rows of the samples matrix. mean should be a + single-row vector in this case.*/ + COVAR_ROWS = 8, + /** If the flag is + specified, all the input vectors are stored as columns of the samples matrix. mean should be a + single-column vector in this case.*/ + COVAR_COLS = 16 +}; + +//! k-Means flags +enum KmeansFlags { + /** Select random initial centers in each attempt.*/ + KMEANS_RANDOM_CENTERS = 0, + /** Use kmeans++ center initialization by Arthur and Vassilvitskii [Arthur2007].*/ + KMEANS_PP_CENTERS = 2, + /** During the first (and possibly the only) attempt, use the + user-supplied labels instead of computing them from the initial centers. For the second and + further attempts, use the random or semi-random centers. Use one of KMEANS_\*_CENTERS flag + to specify the exact method.*/ + KMEANS_USE_INITIAL_LABELS = 1 +}; + +//! type of line +enum LineTypes { + FILLED = -1, + LINE_4 = 4, //!< 4-connected line + LINE_8 = 8, //!< 8-connected line + LINE_AA = 16 //!< antialiased line +}; + +//! Only a subset of Hershey fonts are supported +enum HersheyFonts { + FONT_HERSHEY_SIMPLEX = 0, //!< normal size sans-serif font + FONT_HERSHEY_PLAIN = 1, //!< small size sans-serif font + FONT_HERSHEY_DUPLEX = 2, //!< normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX) + FONT_HERSHEY_COMPLEX = 3, //!< normal size serif font + FONT_HERSHEY_TRIPLEX = 4, //!< normal size serif font (more complex than FONT_HERSHEY_COMPLEX) + FONT_HERSHEY_COMPLEX_SMALL = 5, //!< smaller version of FONT_HERSHEY_COMPLEX + FONT_HERSHEY_SCRIPT_SIMPLEX = 6, //!< hand-writing style font + FONT_HERSHEY_SCRIPT_COMPLEX = 7, //!< more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX + FONT_ITALIC = 16 //!< flag for italic font +}; + +enum ReduceTypes { REDUCE_SUM = 0, //!< the output is the sum of all rows/columns of the matrix. + REDUCE_AVG = 1, //!< the output is the mean vector of all rows/columns of the matrix. + REDUCE_MAX = 2, //!< the output is the maximum (column/row-wise) of all rows/columns of the matrix. + REDUCE_MIN = 3 //!< the output is the minimum (column/row-wise) of all rows/columns of the matrix. + }; + + +/** @brief Swaps two matrices +*/ +CV_EXPORTS void swap(Mat& a, Mat& b); +/** @overload */ +CV_EXPORTS void swap( UMat& a, UMat& b ); + +//! @} core + +//! @addtogroup core_array +//! @{ + +/** @brief Computes the source location of an extrapolated pixel. + +The function computes and returns the coordinate of a donor pixel corresponding to the specified +extrapolated pixel when using the specified extrapolation border mode. For example, if you use +cv::BORDER_WRAP mode in the horizontal direction, cv::BORDER_REFLECT_101 in the vertical direction and +want to compute value of the "virtual" pixel Point(-5, 100) in a floating-point image img , it +looks like: +@code{.cpp} + float val = img.at(borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101), + borderInterpolate(-5, img.cols, cv::BORDER_WRAP)); +@endcode +Normally, the function is not called directly. It is used inside filtering functions and also in +copyMakeBorder. +@param p 0-based coordinate of the extrapolated pixel along one of the axes, likely \<0 or \>= len +@param len Length of the array along the corresponding axis. +@param borderType Border type, one of the #BorderTypes, except for #BORDER_TRANSPARENT and +#BORDER_ISOLATED . When borderType==#BORDER_CONSTANT , the function always returns -1, regardless +of p and len. + +@sa copyMakeBorder +*/ +CV_EXPORTS_W int borderInterpolate(int p, int len, int borderType); + +/** @example samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp +An example using copyMakeBorder function. +Check @ref tutorial_copyMakeBorder "the corresponding tutorial" for more details +*/ + +/** @brief Forms a border around an image. + +The function copies the source image into the middle of the destination image. The areas to the +left, to the right, above and below the copied source image will be filled with extrapolated +pixels. This is not what filtering functions based on it do (they extrapolate pixels on-fly), but +what other more complex functions, including your own, may do to simplify image boundary handling. + +The function supports the mode when src is already in the middle of dst . In this case, the +function does not copy src itself but simply constructs the border, for example: + +@code{.cpp} + // let border be the same in all directions + int border=2; + // constructs a larger image to fit both the image and the border + Mat gray_buf(rgb.rows + border*2, rgb.cols + border*2, rgb.depth()); + // select the middle part of it w/o copying data + Mat gray(gray_canvas, Rect(border, border, rgb.cols, rgb.rows)); + // convert image from RGB to grayscale + cvtColor(rgb, gray, COLOR_RGB2GRAY); + // form a border in-place + copyMakeBorder(gray, gray_buf, border, border, + border, border, BORDER_REPLICATE); + // now do some custom filtering ... + ... +@endcode +@note When the source image is a part (ROI) of a bigger image, the function will try to use the +pixels outside of the ROI to form a border. To disable this feature and always do extrapolation, as +if src was not a ROI, use borderType | #BORDER_ISOLATED. + +@param src Source image. +@param dst Destination image of the same type as src and the size Size(src.cols+left+right, +src.rows+top+bottom) . +@param top the top pixels +@param bottom the bottom pixels +@param left the left pixels +@param right Parameter specifying how many pixels in each direction from the source image rectangle +to extrapolate. For example, top=1, bottom=1, left=1, right=1 mean that 1 pixel-wide border needs +to be built. +@param borderType Border type. See borderInterpolate for details. +@param value Border value if borderType==BORDER_CONSTANT . + +@sa borderInterpolate +*/ +CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst, + int top, int bottom, int left, int right, + int borderType, const Scalar& value = Scalar() ); + +/** @brief Calculates the per-element sum of two arrays or an array and a scalar. + +The function add calculates: +- Sum of two arrays when both input arrays have the same size and the same number of channels: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) + \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f] +- Sum of an array and a scalar when src2 is constructed from Scalar or has the same number of +elements as `src1.channels()`: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) + \texttt{src2} ) \quad \texttt{if mask}(I) \ne0\f] +- Sum of a scalar and an array when src1 is constructed from Scalar or has the same number of +elements as `src2.channels()`: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1} + \texttt{src2}(I) ) \quad \texttt{if mask}(I) \ne0\f] +where `I` is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +The first function in the list above can be replaced with matrix expressions: +@code{.cpp} + dst = src1 + src2; + dst += src1; // equivalent to add(dst, src1, dst); +@endcode +The input arrays and the output array can all have the same or different depths. For example, you +can add a 16-bit unsigned array to a 8-bit signed array and store the sum as a 32-bit +floating-point array. Depth of the output array is determined by the dtype parameter. In the second +and third cases above, as well as in the first case, when src1.depth() == src2.depth(), dtype can +be set to the default -1. In this case, the output array will have the same depth as the input +array, be it src1, src2 or both. +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and number of channels as the input array(s); the +depth is defined by dtype or src1/src2. +@param mask optional operation mask - 8-bit single channel array, that specifies elements of the +output array to be changed. +@param dtype optional depth of the output array (see the discussion below). +@sa subtract, addWeighted, scaleAdd, Mat::convertTo +*/ +CV_EXPORTS_W void add(InputArray src1, InputArray src2, OutputArray dst, + InputArray mask = noArray(), int dtype = -1); + +/** @brief Calculates the per-element difference between two arrays or array and a scalar. + +The function subtract calculates: +- Difference between two arrays, when both input arrays have the same size and the same number of +channels: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) - \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f] +- Difference between an array and a scalar, when src2 is constructed from Scalar or has the same +number of elements as `src1.channels()`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) - \texttt{src2} ) \quad \texttt{if mask}(I) \ne0\f] +- Difference between a scalar and an array, when src1 is constructed from Scalar or has the same +number of elements as `src2.channels()`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1} - \texttt{src2}(I) ) \quad \texttt{if mask}(I) \ne0\f] +- The reverse difference between a scalar and an array in the case of `SubRS`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src2} - \texttt{src1}(I) ) \quad \texttt{if mask}(I) \ne0\f] +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +The first function in the list above can be replaced with matrix expressions: +@code{.cpp} + dst = src1 - src2; + dst -= src1; // equivalent to subtract(dst, src1, dst); +@endcode +The input arrays and the output array can all have the same or different depths. For example, you +can subtract to 8-bit unsigned arrays and store the difference in a 16-bit signed array. Depth of +the output array is determined by dtype parameter. In the second and third cases above, as well as +in the first case, when src1.depth() == src2.depth(), dtype can be set to the default -1. In this +case the output array will have the same depth as the input array, be it src1, src2 or both. +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array of the same size and the same number of channels as the input array. +@param mask optional operation mask; this is an 8-bit single channel array that specifies elements +of the output array to be changed. +@param dtype optional depth of the output array +@sa add, addWeighted, scaleAdd, Mat::convertTo + */ +CV_EXPORTS_W void subtract(InputArray src1, InputArray src2, OutputArray dst, + InputArray mask = noArray(), int dtype = -1); + + +/** @brief Calculates the per-element scaled product of two arrays. + +The function multiply calculates the per-element product of two arrays: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{scale} \cdot \texttt{src1} (I) \cdot \texttt{src2} (I))\f] + +There is also a @ref MatrixExpressions -friendly variant of the first function. See Mat::mul . + +For a not-per-element matrix product, see gemm . + +@note Saturation is not applied when the output array has the depth +CV_32S. You may even get result of an incorrect sign in the case of +overflow. +@param src1 first input array. +@param src2 second input array of the same size and the same type as src1. +@param dst output array of the same size and type as src1. +@param scale optional scale factor. +@param dtype optional depth of the output array +@sa add, subtract, divide, scaleAdd, addWeighted, accumulate, accumulateProduct, accumulateSquare, +Mat::convertTo +*/ +CV_EXPORTS_W void multiply(InputArray src1, InputArray src2, + OutputArray dst, double scale = 1, int dtype = -1); + +/** @brief Performs per-element division of two arrays or a scalar by an array. + +The function cv::divide divides one array by another: +\f[\texttt{dst(I) = saturate(src1(I)*scale/src2(I))}\f] +or a scalar by an array when there is no src1 : +\f[\texttt{dst(I) = saturate(scale/src2(I))}\f] + +When src2(I) is zero, dst(I) will also be zero. Different channels of +multi-channel arrays are processed independently. + +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array. +@param src2 second input array of the same size and type as src1. +@param scale scalar factor. +@param dst output array of the same size and type as src2. +@param dtype optional depth of the output array; if -1, dst will have depth src2.depth(), but in +case of an array-by-array division, you can only pass -1 when src1.depth()==src2.depth(). +@sa multiply, add, subtract +*/ +CV_EXPORTS_W void divide(InputArray src1, InputArray src2, OutputArray dst, + double scale = 1, int dtype = -1); + +/** @overload */ +CV_EXPORTS_W void divide(double scale, InputArray src2, + OutputArray dst, int dtype = -1); + +/** @brief Calculates the sum of a scaled array and another array. + +The function scaleAdd is one of the classical primitive linear algebra operations, known as DAXPY +or SAXPY in [BLAS](http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms). It calculates +the sum of a scaled array and another array: +\f[\texttt{dst} (I)= \texttt{scale} \cdot \texttt{src1} (I) + \texttt{src2} (I)\f] +The function can also be emulated with a matrix expression, for example: +@code{.cpp} + Mat A(3, 3, CV_64F); + ... + A.row(0) = A.row(1)*2 + A.row(2); +@endcode +@param src1 first input array. +@param alpha scale factor for the first array. +@param src2 second input array of the same size and type as src1. +@param dst output array of the same size and type as src1. +@sa add, addWeighted, subtract, Mat::dot, Mat::convertTo +*/ +CV_EXPORTS_W void scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst); + +/** @example samples/cpp/tutorial_code/HighGUI/AddingImagesTrackbar.cpp +Check @ref tutorial_trackbar "the corresponding tutorial" for more details +*/ + +/** @brief Calculates the weighted sum of two arrays. + +The function addWeighted calculates the weighted sum of two arrays as follows: +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} + \texttt{src2} (I)* \texttt{beta} + \texttt{gamma} )\f] +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. +The function can be replaced with a matrix expression: +@code{.cpp} + dst = src1*alpha + src2*beta + gamma; +@endcode +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array. +@param alpha weight of the first array elements. +@param src2 second input array of the same size and channel number as src1. +@param beta weight of the second array elements. +@param gamma scalar added to each sum. +@param dst output array that has the same size and number of channels as the input arrays. +@param dtype optional depth of the output array; when both input arrays have the same depth, dtype +can be set to -1, which will be equivalent to src1.depth(). +@sa add, subtract, scaleAdd, Mat::convertTo +*/ +CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2, + double beta, double gamma, OutputArray dst, int dtype = -1); + +/** @brief Scales, calculates absolute values, and converts the result to 8-bit. + +On each element of the input array, the function convertScaleAbs +performs three operations sequentially: scaling, taking an absolute +value, conversion to an unsigned 8-bit type: +\f[\texttt{dst} (I)= \texttt{saturate\_cast} (| \texttt{src} (I)* \texttt{alpha} + \texttt{beta} |)\f] +In case of multi-channel arrays, the function processes each channel +independently. When the output is not 8-bit, the operation can be +emulated by calling the Mat::convertTo method (or by using matrix +expressions) and then by calculating an absolute value of the result. +For example: +@code{.cpp} + Mat_ A(30,30); + randu(A, Scalar(-100), Scalar(100)); + Mat_ B = A*5 + 3; + B = abs(B); + // Mat_ B = abs(A*5+3) will also do the job, + // but it will allocate a temporary matrix +@endcode +@param src input array. +@param dst output array. +@param alpha optional scale factor. +@param beta optional delta added to the scaled values. +@sa Mat::convertTo, cv::abs(const Mat&) +*/ +CV_EXPORTS_W void convertScaleAbs(InputArray src, OutputArray dst, + double alpha = 1, double beta = 0); + +/** @brief Converts an array to half precision floating number. + +This function converts FP32 (single precision floating point) from/to FP16 (half precision floating point). CV_16S format is used to represent FP16 data. +There are two use modes (src -> dst): CV_32F -> CV_16S and CV_16S -> CV_32F. The input array has to have type of CV_32F or +CV_16S to represent the bit depth. If the input array is neither of them, the function will raise an error. +The format of half precision floating point is defined in IEEE 754-2008. + +@param src input array. +@param dst output array. +*/ +CV_EXPORTS_W void convertFp16(InputArray src, OutputArray dst); + +/** @brief Performs a look-up table transform of an array. + +The function LUT fills the output array with values from the look-up table. Indices of the entries +are taken from the input array. That is, the function processes each element of src as follows: +\f[\texttt{dst} (I) \leftarrow \texttt{lut(src(I) + d)}\f] +where +\f[d = \fork{0}{if \(\texttt{src}\) has depth \(\texttt{CV_8U}\)}{128}{if \(\texttt{src}\) has depth \(\texttt{CV_8S}\)}\f] +@param src input array of 8-bit elements. +@param lut look-up table of 256 elements; in case of multi-channel input array, the table should +either have a single channel (in this case the same table is used for all channels) or the same +number of channels as in the input array. +@param dst output array of the same size and number of channels as src, and the same depth as lut. +@sa convertScaleAbs, Mat::convertTo +*/ +CV_EXPORTS_W void LUT(InputArray src, InputArray lut, OutputArray dst); + +/** @brief Calculates the sum of array elements. + +The function cv::sum calculates and returns the sum of array elements, +independently for each channel. +@param src input array that must have from 1 to 4 channels. +@sa countNonZero, mean, meanStdDev, norm, minMaxLoc, reduce +*/ +CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src); + +/** @brief Counts non-zero array elements. + +The function returns the number of non-zero elements in src : +\f[\sum _{I: \; \texttt{src} (I) \ne0 } 1\f] +@param src single-channel array. +@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix +*/ +CV_EXPORTS_W int countNonZero( InputArray src ); + +/** @brief Returns the list of locations of non-zero pixels + +Given a binary matrix (likely returned from an operation such +as threshold(), compare(), >, ==, etc, return all of +the non-zero indices as a cv::Mat or std::vector (x,y) +For example: +@code{.cpp} + cv::Mat binaryImage; // input, binary image + cv::Mat locations; // output, locations of non-zero pixels + cv::findNonZero(binaryImage, locations); + + // access pixel coordinates + Point pnt = locations.at(i); +@endcode +or +@code{.cpp} + cv::Mat binaryImage; // input, binary image + vector locations; // output, locations of non-zero pixels + cv::findNonZero(binaryImage, locations); + + // access pixel coordinates + Point pnt = locations[i]; +@endcode +@param src single-channel array (type CV_8UC1) +@param idx the output array, type of cv::Mat or std::vector, corresponding to non-zero indices in the input +*/ +CV_EXPORTS_W void findNonZero( InputArray src, OutputArray idx ); + +/** @brief Calculates an average (mean) of array elements. + +The function cv::mean calculates the mean value M of array elements, +independently for each channel, and return it: +\f[\begin{array}{l} N = \sum _{I: \; \texttt{mask} (I) \ne 0} 1 \\ M_c = \left ( \sum _{I: \; \texttt{mask} (I) \ne 0}{ \texttt{mtx} (I)_c} \right )/N \end{array}\f] +When all the mask elements are 0's, the function returns Scalar::all(0) +@param src input array that should have from 1 to 4 channels so that the result can be stored in +Scalar_ . +@param mask optional operation mask. +@sa countNonZero, meanStdDev, norm, minMaxLoc +*/ +CV_EXPORTS_W Scalar mean(InputArray src, InputArray mask = noArray()); + +/** Calculates a mean and standard deviation of array elements. + +The function cv::meanStdDev calculates the mean and the standard deviation M +of array elements independently for each channel and returns it via the +output parameters: +\f[\begin{array}{l} N = \sum _{I, \texttt{mask} (I) \ne 0} 1 \\ \texttt{mean} _c = \frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \texttt{src} (I)_c}{N} \\ \texttt{stddev} _c = \sqrt{\frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \left ( \texttt{src} (I)_c - \texttt{mean} _c \right )^2}{N}} \end{array}\f] +When all the mask elements are 0's, the function returns +mean=stddev=Scalar::all(0). +@note The calculated standard deviation is only the diagonal of the +complete normalized covariance matrix. If the full matrix is needed, you +can reshape the multi-channel array M x N to the single-channel array +M\*N x mtx.channels() (only possible when the matrix is continuous) and +then pass the matrix to calcCovarMatrix . +@param src input array that should have from 1 to 4 channels so that the results can be stored in +Scalar_ 's. +@param mean output parameter: calculated mean value. +@param stddev output parameter: calculated standard deviation. +@param mask optional operation mask. +@sa countNonZero, mean, norm, minMaxLoc, calcCovarMatrix +*/ +CV_EXPORTS_W void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, + InputArray mask=noArray()); + +/** @brief Calculates the absolute norm of an array. + +This version of #norm calculates the absolute norm of src1. The type of norm to calculate is specified using #NormTypes. + +As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$. +The \f$ L_{1}, L_{2} \f$ and \f$ L_{\infty} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$ +is calculated as follows +\f{align*} + \| r(-1) \|_{L_1} &= |-1| + |2| = 3 \\ + \| r(-1) \|_{L_2} &= \sqrt{(-1)^{2} + (2)^{2}} = \sqrt{5} \\ + \| r(-1) \|_{L_\infty} &= \max(|-1|,|2|) = 2 +\f} +and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is +\f{align*} + \| r(0.5) \|_{L_1} &= |0.5| + |0.5| = 1 \\ + \| r(0.5) \|_{L_2} &= \sqrt{(0.5)^{2} + (0.5)^{2}} = \sqrt{0.5} \\ + \| r(0.5) \|_{L_\infty} &= \max(|0.5|,|0.5|) = 0.5. +\f} +The following graphic shows all values for the three norm functions \f$\| r(x) \|_{L_1}, \| r(x) \|_{L_2}\f$ and \f$\| r(x) \|_{L_\infty}\f$. +It is notable that the \f$ L_{1} \f$ norm forms the upper and the \f$ L_{\infty} \f$ norm forms the lower border for the example function \f$ r(x) \f$. +![Graphs for the different norm functions from the above example](pics/NormTypes_OneArray_1-2-INF.png) + +When the mask parameter is specified and it is not empty, the norm is + +If normType is not specified, #NORM_L2 is used. +calculated only over the region specified by the mask. + +Multi-channel input arrays are treated as single-channel arrays, that is, +the results for all channels are combined. + +Hamming norms can only be calculated with CV_8U depth arrays. + +@param src1 first input array. +@param normType type of the norm (see #NormTypes). +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +*/ +CV_EXPORTS_W double norm(InputArray src1, int normType = NORM_L2, InputArray mask = noArray()); + +/** @brief Calculates an absolute difference norm or a relative difference norm. + +This version of cv::norm calculates the absolute difference norm +or the relative difference norm of arrays src1 and src2. +The type of norm to calculate is specified using #NormTypes. + +@param src1 first input array. +@param src2 second input array of the same size and the same type as src1. +@param normType type of the norm (see #NormTypes). +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +*/ +CV_EXPORTS_W double norm(InputArray src1, InputArray src2, + int normType = NORM_L2, InputArray mask = noArray()); +/** @overload +@param src first input array. +@param normType type of the norm (see #NormTypes). +*/ +CV_EXPORTS double norm( const SparseMat& src, int normType ); + +/** @brief Computes the Peak Signal-to-Noise Ratio (PSNR) image quality metric. + +This function calculates the Peak Signal-to-Noise Ratio (PSNR) image quality metric in decibels (dB), between two input arrays src1 and src2. Arrays must have depth CV_8U. + +The PSNR is calculated as follows: + +\f[ +\texttt{PSNR} = 10 \cdot \log_{10}{\left( \frac{R^2}{MSE} \right) } +\f] + +where R is the maximum integer value of depth CV_8U (255) and MSE is the mean squared error between the two arrays. + +@param src1 first input array. +@param src2 second input array of the same size as src1. + + */ +CV_EXPORTS_W double PSNR(InputArray src1, InputArray src2); + +/** @brief naive nearest neighbor finder + +see http://en.wikipedia.org/wiki/Nearest_neighbor_search +@todo document + */ +CV_EXPORTS_W void batchDistance(InputArray src1, InputArray src2, + OutputArray dist, int dtype, OutputArray nidx, + int normType = NORM_L2, int K = 0, + InputArray mask = noArray(), int update = 0, + bool crosscheck = false); + +/** @brief Normalizes the norm or value range of an array. + +The function cv::normalize normalizes scale and shift the input array elements so that +\f[\| \texttt{dst} \| _{L_p}= \texttt{alpha}\f] +(where p=Inf, 1 or 2) when normType=NORM_INF, NORM_L1, or NORM_L2, respectively; or so that +\f[\min _I \texttt{dst} (I)= \texttt{alpha} , \, \, \max _I \texttt{dst} (I)= \texttt{beta}\f] + +when normType=NORM_MINMAX (for dense arrays only). The optional mask specifies a sub-array to be +normalized. This means that the norm or min-n-max are calculated over the sub-array, and then this +sub-array is modified to be normalized. If you want to only use the mask to calculate the norm or +min-max but modify the whole array, you can use norm and Mat::convertTo. + +In case of sparse matrices, only the non-zero values are analyzed and transformed. Because of this, +the range transformation for sparse matrices is not allowed since it can shift the zero level. + +Possible usage with some positive example data: +@code{.cpp} + vector positiveData = { 2.0, 8.0, 10.0 }; + vector normalizedData_l1, normalizedData_l2, normalizedData_inf, normalizedData_minmax; + + // Norm to probability (total count) + // sum(numbers) = 20.0 + // 2.0 0.1 (2.0/20.0) + // 8.0 0.4 (8.0/20.0) + // 10.0 0.5 (10.0/20.0) + normalize(positiveData, normalizedData_l1, 1.0, 0.0, NORM_L1); + + // Norm to unit vector: ||positiveData|| = 1.0 + // 2.0 0.15 + // 8.0 0.62 + // 10.0 0.77 + normalize(positiveData, normalizedData_l2, 1.0, 0.0, NORM_L2); + + // Norm to max element + // 2.0 0.2 (2.0/10.0) + // 8.0 0.8 (8.0/10.0) + // 10.0 1.0 (10.0/10.0) + normalize(positiveData, normalizedData_inf, 1.0, 0.0, NORM_INF); + + // Norm to range [0.0;1.0] + // 2.0 0.0 (shift to left border) + // 8.0 0.75 (6.0/8.0) + // 10.0 1.0 (shift to right border) + normalize(positiveData, normalizedData_minmax, 1.0, 0.0, NORM_MINMAX); +@endcode + +@param src input array. +@param dst output array of the same size as src . +@param alpha norm value to normalize to or the lower range boundary in case of the range +normalization. +@param beta upper range boundary in case of the range normalization; it is not used for the norm +normalization. +@param norm_type normalization type (see cv::NormTypes). +@param dtype when negative, the output array has the same type as src; otherwise, it has the same +number of channels as src and the depth =CV_MAT_DEPTH(dtype). +@param mask optional operation mask. +@sa norm, Mat::convertTo, SparseMat::convertTo +*/ +CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, + int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray()); + +/** @overload +@param src input array. +@param dst output array of the same size as src . +@param alpha norm value to normalize to or the lower range boundary in case of the range +normalization. +@param normType normalization type (see cv::NormTypes). +*/ +CV_EXPORTS void normalize( const SparseMat& src, SparseMat& dst, double alpha, int normType ); + +/** @brief Finds the global minimum and maximum in an array. + +The function cv::minMaxLoc finds the minimum and maximum element values and their positions. The +extremums are searched across the whole array or, if mask is not an empty array, in the specified +array region. + +The function do not work with multi-channel arrays. If you need to find minimum or maximum +elements across all the channels, use Mat::reshape first to reinterpret the array as +single-channel. Or you may extract the particular channel using either extractImageCOI , or +mixChannels , or split . +@param src input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required. +@param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required. +@param mask optional mask used to select a sub-array. +@sa max, min, compare, inRange, extractImageCOI, mixChannels, split, Mat::reshape +*/ +CV_EXPORTS_W void minMaxLoc(InputArray src, CV_OUT double* minVal, + CV_OUT double* maxVal = 0, CV_OUT Point* minLoc = 0, + CV_OUT Point* maxLoc = 0, InputArray mask = noArray()); + + +/** @brief Finds the global minimum and maximum in an array + +The function cv::minMaxIdx finds the minimum and maximum element values and their positions. The +extremums are searched across the whole array or, if mask is not an empty array, in the specified +array region. The function does not work with multi-channel arrays. If you need to find minimum or +maximum elements across all the channels, use Mat::reshape first to reinterpret the array as +single-channel. Or you may extract the particular channel using either extractImageCOI , or +mixChannels , or split . In case of a sparse matrix, the minimum is found among non-zero elements +only. +@note When minIdx is not NULL, it must have at least 2 elements (as well as maxIdx), even if src is +a single-row or single-column matrix. In OpenCV (following MATLAB) each array has at least 2 +dimensions, i.e. single-column matrix is Mx1 matrix (and therefore minIdx/maxIdx will be +(i1,0)/(i2,0)) and single-row matrix is 1xN matrix (and therefore minIdx/maxIdx will be +(0,j1)/(0,j2)). +@param src input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minIdx pointer to the returned minimum location (in nD case); NULL is used if not required; +Otherwise, it must point to an array of src.dims elements, the coordinates of the minimum element +in each dimension are stored there sequentially. +@param maxIdx pointer to the returned maximum location (in nD case). NULL is used if not required. +@param mask specified array region +*/ +CV_EXPORTS void minMaxIdx(InputArray src, double* minVal, double* maxVal = 0, + int* minIdx = 0, int* maxIdx = 0, InputArray mask = noArray()); + +/** @overload +@param a input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minIdx pointer to the returned minimum location (in nD case); NULL is used if not required; +Otherwise, it must point to an array of src.dims elements, the coordinates of the minimum element +in each dimension are stored there sequentially. +@param maxIdx pointer to the returned maximum location (in nD case). NULL is used if not required. +*/ +CV_EXPORTS void minMaxLoc(const SparseMat& a, double* minVal, + double* maxVal, int* minIdx = 0, int* maxIdx = 0); + +/** @brief Reduces a matrix to a vector. + +The function #reduce reduces the matrix to a vector by treating the matrix rows/columns as a set of +1D vectors and performing the specified operation on the vectors until a single row/column is +obtained. For example, the function can be used to compute horizontal and vertical projections of a +raster image. In case of #REDUCE_MAX and #REDUCE_MIN , the output image should have the same type as the source one. +In case of #REDUCE_SUM and #REDUCE_AVG , the output may have a larger element bit-depth to preserve accuracy. +And multi-channel arrays are also supported in these two reduction modes. + +The following code demonstrates its usage for a single channel matrix. +@snippet snippets/core_reduce.cpp example + +And the following code demonstrates its usage for a two-channel matrix. +@snippet snippets/core_reduce.cpp example2 + +@param src input 2D matrix. +@param dst output vector. Its size and type is defined by dim and dtype parameters. +@param dim dimension index along which the matrix is reduced. 0 means that the matrix is reduced to +a single row. 1 means that the matrix is reduced to a single column. +@param rtype reduction operation that could be one of #ReduceTypes +@param dtype when negative, the output vector will have the same type as the input matrix, +otherwise, its type will be CV_MAKE_TYPE(CV_MAT_DEPTH(dtype), src.channels()). +@sa repeat +*/ +CV_EXPORTS_W void reduce(InputArray src, OutputArray dst, int dim, int rtype, int dtype = -1); + +/** @brief Creates one multi-channel array out of several single-channel ones. + +The function cv::merge merges several arrays to make a single multi-channel array. That is, each +element of the output array will be a concatenation of the elements of the input arrays, where +elements of i-th input array are treated as mv[i].channels()-element vectors. + +The function cv::split does the reverse operation. If you need to shuffle channels in some other +advanced way, use cv::mixChannels. + +The following example shows how to merge 3 single channel matrices into a single 3-channel matrix. +@snippet snippets/core_merge.cpp example + +@param mv input array of matrices to be merged; all the matrices in mv must have the same +size and the same depth. +@param count number of input matrices when mv is a plain C array; it must be greater than zero. +@param dst output array of the same size and the same depth as mv[0]; The number of channels will +be equal to the parameter count. +@sa mixChannels, split, Mat::reshape +*/ +CV_EXPORTS void merge(const Mat* mv, size_t count, OutputArray dst); + +/** @overload +@param mv input vector of matrices to be merged; all the matrices in mv must have the same +size and the same depth. +@param dst output array of the same size and the same depth as mv[0]; The number of channels will +be the total number of channels in the matrix array. + */ +CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst); + +/** @brief Divides a multi-channel array into several single-channel arrays. + +The function cv::split splits a multi-channel array into separate single-channel arrays: +\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f] +If you need to extract a single channel or do some other sophisticated channel permutation, use +mixChannels . + +The following example demonstrates how to split a 3-channel matrix into 3 single channel matrices. +@snippet snippets/core_split.cpp example + +@param src input multi-channel array. +@param mvbegin output array; the number of arrays must match src.channels(); the arrays themselves are +reallocated, if needed. +@sa merge, mixChannels, cvtColor +*/ +CV_EXPORTS void split(const Mat& src, Mat* mvbegin); + +/** @overload +@param m input multi-channel array. +@param mv output vector of arrays; the arrays themselves are reallocated, if needed. +*/ +CV_EXPORTS_W void split(InputArray m, OutputArrayOfArrays mv); + +/** @brief Copies specified channels from input arrays to the specified channels of +output arrays. + +The function cv::mixChannels provides an advanced mechanism for shuffling image channels. + +cv::split,cv::merge,cv::extractChannel,cv::insertChannel and some forms of cv::cvtColor are partial cases of cv::mixChannels. + +In the example below, the code splits a 4-channel BGRA image into a 3-channel BGR (with B and R +channels swapped) and a separate alpha-channel image: +@code{.cpp} + Mat bgra( 100, 100, CV_8UC4, Scalar(255,0,0,255) ); + Mat bgr( bgra.rows, bgra.cols, CV_8UC3 ); + Mat alpha( bgra.rows, bgra.cols, CV_8UC1 ); + + // forming an array of matrices is a quite efficient operation, + // because the matrix data is not copied, only the headers + Mat out[] = { bgr, alpha }; + // bgra[0] -> bgr[2], bgra[1] -> bgr[1], + // bgra[2] -> bgr[0], bgra[3] -> alpha[0] + int from_to[] = { 0,2, 1,1, 2,0, 3,3 }; + mixChannels( &bgra, 1, out, 2, from_to, 4 ); +@endcode +@note Unlike many other new-style C++ functions in OpenCV (see the introduction section and +Mat::create ), cv::mixChannels requires the output arrays to be pre-allocated before calling the +function. +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param nsrcs number of matrices in `src`. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in `src[0]`. +@param ndsts number of matrices in `dst`. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +@param npairs number of index pairs in `fromTo`. +@sa split, merge, extractChannel, insertChannel, cvtColor +*/ +CV_EXPORTS void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, + const int* fromTo, size_t npairs); + +/** @overload +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in src[0]. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +@param npairs number of index pairs in fromTo. +*/ +CV_EXPORTS void mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst, + const int* fromTo, size_t npairs); + +/** @overload +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in src[0]. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +*/ +CV_EXPORTS_W void mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst, + const std::vector& fromTo); + +/** @brief Extracts a single channel from src (coi is 0-based index) +@param src input array +@param dst output array +@param coi index of channel to extract +@sa mixChannels, split +*/ +CV_EXPORTS_W void extractChannel(InputArray src, OutputArray dst, int coi); + +/** @brief Inserts a single channel to dst (coi is 0-based index) +@param src input array +@param dst output array +@param coi index of channel for insertion +@sa mixChannels, merge +*/ +CV_EXPORTS_W void insertChannel(InputArray src, InputOutputArray dst, int coi); + +/** @brief Flips a 2D array around vertical, horizontal, or both axes. + +The function cv::flip flips the array in one of three different ways (row +and column indices are 0-based): +\f[\texttt{dst} _{ij} = +\left\{ +\begin{array}{l l} +\texttt{src} _{\texttt{src.rows}-i-1,j} & if\; \texttt{flipCode} = 0 \\ +\texttt{src} _{i, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} > 0 \\ +\texttt{src} _{ \texttt{src.rows} -i-1, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} < 0 \\ +\end{array} +\right.\f] +The example scenarios of using the function are the following: +* Vertical flipping of the image (flipCode == 0) to switch between + top-left and bottom-left image origin. This is a typical operation + in video processing on Microsoft Windows\* OS. +* Horizontal flipping of the image with the subsequent horizontal + shift and absolute difference calculation to check for a + vertical-axis symmetry (flipCode \> 0). +* Simultaneous horizontal and vertical flipping of the image with + the subsequent shift and absolute difference calculation to check + for a central symmetry (flipCode \< 0). +* Reversing the order of point arrays (flipCode \> 0 or + flipCode == 0). +@param src input array. +@param dst output array of the same size and type as src. +@param flipCode a flag to specify how to flip the array; 0 means +flipping around the x-axis and positive value (for example, 1) means +flipping around y-axis. Negative value (for example, -1) means flipping +around both axes. +@sa transpose , repeat , completeSymm +*/ +CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode); + +enum RotateFlags { + ROTATE_90_CLOCKWISE = 0, //! A = (cv::Mat_(3, 2) << 1, 4, + 2, 5, + 3, 6); + cv::Mat_ B = (cv::Mat_(3, 2) << 7, 10, + 8, 11, + 9, 12); + + cv::Mat C; + cv::hconcat(A, B, C); + //C: + //[1, 4, 7, 10; + // 2, 5, 8, 11; + // 3, 6, 9, 12] + @endcode + @param src1 first input array to be considered for horizontal concatenation. + @param src2 second input array to be considered for horizontal concatenation. + @param dst output array. It has the same number of rows and depth as the src1 and src2, and the sum of cols of the src1 and src2. + */ +CV_EXPORTS void hconcat(InputArray src1, InputArray src2, OutputArray dst); +/** @overload + @code{.cpp} + std::vector matrices = { cv::Mat(4, 1, CV_8UC1, cv::Scalar(1)), + cv::Mat(4, 1, CV_8UC1, cv::Scalar(2)), + cv::Mat(4, 1, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::hconcat( matrices, out ); + //out: + //[1, 2, 3; + // 1, 2, 3; + // 1, 2, 3; + // 1, 2, 3] + @endcode + @param src input array or vector of matrices. all of the matrices must have the same number of rows and the same depth. + @param dst output array. It has the same number of rows and depth as the src, and the sum of cols of the src. +same depth. + */ +CV_EXPORTS_W void hconcat(InputArrayOfArrays src, OutputArray dst); + +/** @brief Applies vertical concatenation to given matrices. + +The function vertically concatenates two or more cv::Mat matrices (with the same number of cols). +@code{.cpp} + cv::Mat matArray[] = { cv::Mat(1, 4, CV_8UC1, cv::Scalar(1)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(2)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::vconcat( matArray, 3, out ); + //out: + //[1, 1, 1, 1; + // 2, 2, 2, 2; + // 3, 3, 3, 3] +@endcode +@param src input array or vector of matrices. all of the matrices must have the same number of cols and the same depth. +@param nsrc number of matrices in src. +@param dst output array. It has the same number of cols and depth as the src, and the sum of rows of the src. +@sa cv::hconcat(const Mat*, size_t, OutputArray), @sa cv::hconcat(InputArrayOfArrays, OutputArray) and @sa cv::hconcat(InputArray, InputArray, OutputArray) +*/ +CV_EXPORTS void vconcat(const Mat* src, size_t nsrc, OutputArray dst); +/** @overload + @code{.cpp} + cv::Mat_ A = (cv::Mat_(3, 2) << 1, 7, + 2, 8, + 3, 9); + cv::Mat_ B = (cv::Mat_(3, 2) << 4, 10, + 5, 11, + 6, 12); + + cv::Mat C; + cv::vconcat(A, B, C); + //C: + //[1, 7; + // 2, 8; + // 3, 9; + // 4, 10; + // 5, 11; + // 6, 12] + @endcode + @param src1 first input array to be considered for vertical concatenation. + @param src2 second input array to be considered for vertical concatenation. + @param dst output array. It has the same number of cols and depth as the src1 and src2, and the sum of rows of the src1 and src2. + */ +CV_EXPORTS void vconcat(InputArray src1, InputArray src2, OutputArray dst); +/** @overload + @code{.cpp} + std::vector matrices = { cv::Mat(1, 4, CV_8UC1, cv::Scalar(1)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(2)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::vconcat( matrices, out ); + //out: + //[1, 1, 1, 1; + // 2, 2, 2, 2; + // 3, 3, 3, 3] + @endcode + @param src input array or vector of matrices. all of the matrices must have the same number of cols and the same depth + @param dst output array. It has the same number of cols and depth as the src, and the sum of rows of the src. +same depth. + */ +CV_EXPORTS_W void vconcat(InputArrayOfArrays src, OutputArray dst); + +/** @brief computes bitwise conjunction of the two arrays (dst = src1 & src2) +Calculates the per-element bit-wise conjunction of two arrays or an +array and a scalar. + +The function cv::bitwise_and calculates the per-element bit-wise logical conjunction for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the second and third cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_and(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Calculates the per-element bit-wise disjunction of two arrays or an +array and a scalar. + +The function cv::bitwise_or calculates the per-element bit-wise logical disjunction for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \vee \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \vee \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \vee \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the second and third cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_or(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Calculates the per-element bit-wise "exclusive or" operation on two +arrays or an array and a scalar. + +The function cv::bitwise_xor calculates the per-element bit-wise logical "exclusive-or" +operation for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \oplus \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \oplus \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \oplus \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the 2nd and 3rd cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_xor(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Inverts every bit of an array. + +The function cv::bitwise_not calculates per-element bit-wise inversion of the input +array: +\f[\texttt{dst} (I) = \neg \texttt{src} (I)\f] +In case of a floating-point input array, its machine-specific bit +representation (usually IEEE754-compliant) is used for the operation. In +case of multi-channel arrays, each channel is processed independently. +@param src input array. +@param dst output array that has the same size and type as the input +array. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_not(InputArray src, OutputArray dst, + InputArray mask = noArray()); + +/** @brief Calculates the per-element absolute difference between two arrays or between an array and a scalar. + +The function cv::absdiff calculates: +* Absolute difference between two arrays when they have the same + size and type: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{src2}(I)|)\f] +* Absolute difference between an array and a scalar when the second + array is constructed from Scalar or has as many elements as the + number of channels in `src1`: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{src2} |)\f] +* Absolute difference between a scalar and an array when the first + array is constructed from Scalar or has as many elements as the + number of channels in `src2`: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1} - \texttt{src2}(I) |)\f] + where I is a multi-dimensional index of array elements. In case of + multi-channel arrays, each channel is processed independently. +@note Saturation is not applied when the arrays have the depth CV_32S. +You may even get a negative value in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as input arrays. +@sa cv::abs(const Mat&) +*/ +CV_EXPORTS_W void absdiff(InputArray src1, InputArray src2, OutputArray dst); + +/** @brief Checks if array elements lie between the elements of two other arrays. + +The function checks the range as follows: +- For every element of a single-channel input array: + \f[\texttt{dst} (I)= \texttt{lowerb} (I)_0 \leq \texttt{src} (I)_0 \leq \texttt{upperb} (I)_0\f] +- For two-channel arrays: + \f[\texttt{dst} (I)= \texttt{lowerb} (I)_0 \leq \texttt{src} (I)_0 \leq \texttt{upperb} (I)_0 \land \texttt{lowerb} (I)_1 \leq \texttt{src} (I)_1 \leq \texttt{upperb} (I)_1\f] +- and so forth. + +That is, dst (I) is set to 255 (all 1 -bits) if src (I) is within the +specified 1D, 2D, 3D, ... box and 0 otherwise. + +When the lower and/or upper boundary parameters are scalars, the indexes +(I) at lowerb and upperb in the above formulas should be omitted. +@param src first input array. +@param lowerb inclusive lower boundary array or a scalar. +@param upperb inclusive upper boundary array or a scalar. +@param dst output array of the same size as src and CV_8U type. +*/ +CV_EXPORTS_W void inRange(InputArray src, InputArray lowerb, + InputArray upperb, OutputArray dst); + +/** @brief Performs the per-element comparison of two arrays or an array and scalar value. + +The function compares: +* Elements of two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \,\texttt{cmpop}\, \texttt{src2} (I)\f] +* Elements of src1 with a scalar src2 when src2 is constructed from + Scalar or has a single element: + \f[\texttt{dst} (I) = \texttt{src1}(I) \,\texttt{cmpop}\, \texttt{src2}\f] +* src1 with elements of src2 when src1 is constructed from Scalar or + has a single element: + \f[\texttt{dst} (I) = \texttt{src1} \,\texttt{cmpop}\, \texttt{src2} (I)\f] +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: +@code{.cpp} + Mat dst1 = src1 >= src2; + Mat dst2 = src1 < 8; + ... +@endcode +@param src1 first input array or a scalar; when it is an array, it must have a single channel. +@param src2 second input array or a scalar; when it is an array, it must have a single channel. +@param dst output array of type ref CV_8U that has the same size and the same number of channels as + the input arrays. +@param cmpop a flag, that specifies correspondence between the arrays (cv::CmpTypes) +@sa checkRange, min, max, threshold +*/ +CV_EXPORTS_W void compare(InputArray src1, InputArray src2, OutputArray dst, int cmpop); + +/** @brief Calculates per-element minimum of two arrays or an array and a scalar. + +The function cv::min calculates the per-element minimum of two arrays: +\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{src2} (I))\f] +or array and a scalar: +\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{value} )\f] +@param src1 first input array. +@param src2 second input array of the same size and type as src1. +@param dst output array of the same size and type as src1. +@sa max, compare, inRange, minMaxLoc +*/ +CV_EXPORTS_W void min(InputArray src1, InputArray src2, OutputArray dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void min(const Mat& src1, const Mat& src2, Mat& dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void min(const UMat& src1, const UMat& src2, UMat& dst); + +/** @brief Calculates per-element maximum of two arrays or an array and a scalar. + +The function cv::max calculates the per-element maximum of two arrays: +\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{src2} (I))\f] +or array and a scalar: +\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{value} )\f] +@param src1 first input array. +@param src2 second input array of the same size and type as src1 . +@param dst output array of the same size and type as src1. +@sa min, compare, inRange, minMaxLoc, @ref MatrixExpressions +*/ +CV_EXPORTS_W void max(InputArray src1, InputArray src2, OutputArray dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void max(const Mat& src1, const Mat& src2, Mat& dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void max(const UMat& src1, const UMat& src2, UMat& dst); + +/** @brief Calculates a square root of array elements. + +The function cv::sqrt calculates a square root of each input array element. +In case of multi-channel arrays, each channel is processed +independently. The accuracy is approximately the same as of the built-in +std::sqrt . +@param src input floating-point array. +@param dst output array of the same size and type as src. +*/ +CV_EXPORTS_W void sqrt(InputArray src, OutputArray dst); + +/** @brief Raises every array element to a power. + +The function cv::pow raises every element of the input array to power : +\f[\texttt{dst} (I) = \fork{\texttt{src}(I)^{power}}{if \(\texttt{power}\) is integer}{|\texttt{src}(I)|^{power}}{otherwise}\f] + +So, for a non-integer power exponent, the absolute values of input array +elements are used. However, it is possible to get true values for +negative values using some extra operations. In the example below, +computing the 5th root of array src shows: +@code{.cpp} + Mat mask = src < 0; + pow(src, 1./5, dst); + subtract(Scalar::all(0), dst, dst, mask); +@endcode +For some values of power, such as integer values, 0.5 and -0.5, +specialized faster algorithms are used. + +Special values (NaN, Inf) are not handled. +@param src input array. +@param power exponent of power. +@param dst output array of the same size and type as src. +@sa sqrt, exp, log, cartToPolar, polarToCart +*/ +CV_EXPORTS_W void pow(InputArray src, double power, OutputArray dst); + +/** @brief Calculates the exponent of every array element. + +The function cv::exp calculates the exponent of every element of the input +array: +\f[\texttt{dst} [I] = e^{ src(I) }\f] + +The maximum relative error is about 7e-6 for single-precision input and +less than 1e-10 for double-precision input. Currently, the function +converts denormalized values to zeros on output. Special values (NaN, +Inf) are not handled. +@param src input array. +@param dst output array of the same size and type as src. +@sa log , cartToPolar , polarToCart , phase , pow , sqrt , magnitude +*/ +CV_EXPORTS_W void exp(InputArray src, OutputArray dst); + +/** @brief Calculates the natural logarithm of every array element. + +The function cv::log calculates the natural logarithm of every element of the input array: +\f[\texttt{dst} (I) = \log (\texttt{src}(I)) \f] + +Output on zero, negative and special (NaN, Inf) values is undefined. + +@param src input array. +@param dst output array of the same size and type as src . +@sa exp, cartToPolar, polarToCart, phase, pow, sqrt, magnitude +*/ +CV_EXPORTS_W void log(InputArray src, OutputArray dst); + +/** @brief Calculates x and y coordinates of 2D vectors from their magnitude and angle. + +The function cv::polarToCart calculates the Cartesian coordinates of each 2D +vector represented by the corresponding elements of magnitude and angle: +\f[\begin{array}{l} \texttt{x} (I) = \texttt{magnitude} (I) \cos ( \texttt{angle} (I)) \\ \texttt{y} (I) = \texttt{magnitude} (I) \sin ( \texttt{angle} (I)) \\ \end{array}\f] + +The relative accuracy of the estimated coordinates is about 1e-6. +@param magnitude input floating-point array of magnitudes of 2D vectors; +it can be an empty matrix (=Mat()), in this case, the function assumes +that all the magnitudes are =1; if it is not empty, it must have the +same size and type as angle. +@param angle input floating-point array of angles of 2D vectors. +@param x output array of x-coordinates of 2D vectors; it has the same +size and type as angle. +@param y output array of y-coordinates of 2D vectors; it has the same +size and type as angle. +@param angleInDegrees when true, the input angles are measured in +degrees, otherwise, they are measured in radians. +@sa cartToPolar, magnitude, phase, exp, log, pow, sqrt +*/ +CV_EXPORTS_W void polarToCart(InputArray magnitude, InputArray angle, + OutputArray x, OutputArray y, bool angleInDegrees = false); + +/** @brief Calculates the magnitude and angle of 2D vectors. + +The function cv::cartToPolar calculates either the magnitude, angle, or both +for every 2D vector (x(I),y(I)): +\f[\begin{array}{l} \texttt{magnitude} (I)= \sqrt{\texttt{x}(I)^2+\texttt{y}(I)^2} , \\ \texttt{angle} (I)= \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))[ \cdot180 / \pi ] \end{array}\f] + +The angles are calculated with accuracy about 0.3 degrees. For the point +(0,0), the angle is set to 0. +@param x array of x-coordinates; this must be a single-precision or +double-precision floating-point array. +@param y array of y-coordinates, that must have the same size and same type as x. +@param magnitude output array of magnitudes of the same size and type as x. +@param angle output array of angles that has the same size and type as +x; the angles are measured in radians (from 0 to 2\*Pi) or in degrees (0 to 360 degrees). +@param angleInDegrees a flag, indicating whether the angles are measured +in radians (which is by default), or in degrees. +@sa Sobel, Scharr +*/ +CV_EXPORTS_W void cartToPolar(InputArray x, InputArray y, + OutputArray magnitude, OutputArray angle, + bool angleInDegrees = false); + +/** @brief Calculates the rotation angle of 2D vectors. + +The function cv::phase calculates the rotation angle of each 2D vector that +is formed from the corresponding elements of x and y : +\f[\texttt{angle} (I) = \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))\f] + +The angle estimation accuracy is about 0.3 degrees. When x(I)=y(I)=0 , +the corresponding angle(I) is set to 0. +@param x input floating-point array of x-coordinates of 2D vectors. +@param y input array of y-coordinates of 2D vectors; it must have the +same size and the same type as x. +@param angle output array of vector angles; it has the same size and +same type as x . +@param angleInDegrees when true, the function calculates the angle in +degrees, otherwise, they are measured in radians. +*/ +CV_EXPORTS_W void phase(InputArray x, InputArray y, OutputArray angle, + bool angleInDegrees = false); + +/** @brief Calculates the magnitude of 2D vectors. + +The function cv::magnitude calculates the magnitude of 2D vectors formed +from the corresponding elements of x and y arrays: +\f[\texttt{dst} (I) = \sqrt{\texttt{x}(I)^2 + \texttt{y}(I)^2}\f] +@param x floating-point array of x-coordinates of the vectors. +@param y floating-point array of y-coordinates of the vectors; it must +have the same size as x. +@param magnitude output array of the same size and type as x. +@sa cartToPolar, polarToCart, phase, sqrt +*/ +CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude); + +/** @brief Checks every element of an input array for invalid values. + +The function cv::checkRange checks that every array element is neither NaN nor infinite. When minVal \> +-DBL_MAX and maxVal \< DBL_MAX, the function also checks that each value is between minVal and +maxVal. In case of multi-channel arrays, each channel is processed independently. If some values +are out of range, position of the first outlier is stored in pos (when pos != NULL). Then, the +function either returns false (when quiet=true) or throws an exception. +@param a input array. +@param quiet a flag, indicating whether the functions quietly return false when the array elements +are out of range or they throw an exception. +@param pos optional output parameter, when not NULL, must be a pointer to array of src.dims +elements. +@param minVal inclusive lower boundary of valid values range. +@param maxVal exclusive upper boundary of valid values range. +*/ +CV_EXPORTS_W bool checkRange(InputArray a, bool quiet = true, CV_OUT Point* pos = 0, + double minVal = -DBL_MAX, double maxVal = DBL_MAX); + +/** @brief converts NaN's to the given number +*/ +CV_EXPORTS_W void patchNaNs(InputOutputArray a, double val = 0); + +/** @brief Performs generalized matrix multiplication. + +The function cv::gemm performs generalized matrix multiplication similar to the +gemm functions in BLAS level 3. For example, +`gemm(src1, src2, alpha, src3, beta, dst, GEMM_1_T + GEMM_3_T)` +corresponds to +\f[\texttt{dst} = \texttt{alpha} \cdot \texttt{src1} ^T \cdot \texttt{src2} + \texttt{beta} \cdot \texttt{src3} ^T\f] + +In case of complex (two-channel) data, performed a complex matrix +multiplication. + +The function can be replaced with a matrix expression. For example, the +above call can be replaced with: +@code{.cpp} + dst = alpha*src1.t()*src2 + beta*src3.t(); +@endcode +@param src1 first multiplied input matrix that could be real(CV_32FC1, +CV_64FC1) or complex(CV_32FC2, CV_64FC2). +@param src2 second multiplied input matrix of the same type as src1. +@param alpha weight of the matrix product. +@param src3 third optional delta matrix added to the matrix product; it +should have the same type as src1 and src2. +@param beta weight of src3. +@param dst output matrix; it has the proper size and the same type as +input matrices. +@param flags operation flags (cv::GemmFlags) +@sa mulTransposed , transform +*/ +CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha, + InputArray src3, double beta, OutputArray dst, int flags = 0); + +/** @brief Calculates the product of a matrix and its transposition. + +The function cv::mulTransposed calculates the product of src and its +transposition: +\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} )^T ( \texttt{src} - \texttt{delta} )\f] +if aTa=true , and +\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} ) ( \texttt{src} - \texttt{delta} )^T\f] +otherwise. The function is used to calculate the covariance matrix. With +zero delta, it can be used as a faster substitute for general matrix +product A\*B when B=A' +@param src input single-channel matrix. Note that unlike gemm, the +function can multiply not only floating-point matrices. +@param dst output square matrix. +@param aTa Flag specifying the multiplication ordering. See the +description below. +@param delta Optional delta matrix subtracted from src before the +multiplication. When the matrix is empty ( delta=noArray() ), it is +assumed to be zero, that is, nothing is subtracted. If it has the same +size as src , it is simply subtracted. Otherwise, it is "repeated" (see +repeat ) to cover the full src and then subtracted. Type of the delta +matrix, when it is not empty, must be the same as the type of created +output matrix. See the dtype parameter description below. +@param scale Optional scale factor for the matrix product. +@param dtype Optional type of the output matrix. When it is negative, +the output matrix will have the same type as src . Otherwise, it will be +type=CV_MAT_DEPTH(dtype) that should be either CV_32F or CV_64F . +@sa calcCovarMatrix, gemm, repeat, reduce +*/ +CV_EXPORTS_W void mulTransposed( InputArray src, OutputArray dst, bool aTa, + InputArray delta = noArray(), + double scale = 1, int dtype = -1 ); + +/** @brief Transposes a matrix. + +The function cv::transpose transposes the matrix src : +\f[\texttt{dst} (i,j) = \texttt{src} (j,i)\f] +@note No complex conjugation is done in case of a complex matrix. It +should be done separately if needed. +@param src input array. +@param dst output array of the same type as src. +*/ +CV_EXPORTS_W void transpose(InputArray src, OutputArray dst); + +/** @brief Performs the matrix transformation of every array element. + +The function cv::transform performs the matrix transformation of every +element of the array src and stores the results in dst : +\f[\texttt{dst} (I) = \texttt{m} \cdot \texttt{src} (I)\f] +(when m.cols=src.channels() ), or +\f[\texttt{dst} (I) = \texttt{m} \cdot [ \texttt{src} (I); 1]\f] +(when m.cols=src.channels()+1 ) + +Every element of the N -channel array src is interpreted as N -element +vector that is transformed using the M x N or M x (N+1) matrix m to +M-element vector - the corresponding element of the output array dst . + +The function may be used for geometrical transformation of +N -dimensional points, arbitrary linear color space transformation (such +as various kinds of RGB to YUV transforms), shuffling the image +channels, and so forth. +@param src input array that must have as many channels (1 to 4) as +m.cols or m.cols-1. +@param dst output array of the same size and depth as src; it has as +many channels as m.rows. +@param m transformation 2x2 or 2x3 floating-point matrix. +@sa perspectiveTransform, getAffineTransform, estimateAffine2D, warpAffine, warpPerspective +*/ +CV_EXPORTS_W void transform(InputArray src, OutputArray dst, InputArray m ); + +/** @brief Performs the perspective matrix transformation of vectors. + +The function cv::perspectiveTransform transforms every element of src by +treating it as a 2D or 3D vector, in the following way: +\f[(x, y, z) \rightarrow (x'/w, y'/w, z'/w)\f] +where +\f[(x', y', z', w') = \texttt{mat} \cdot \begin{bmatrix} x & y & z & 1 \end{bmatrix}\f] +and +\f[w = \fork{w'}{if \(w' \ne 0\)}{\infty}{otherwise}\f] + +Here a 3D vector transformation is shown. In case of a 2D vector +transformation, the z component is omitted. + +@note The function transforms a sparse set of 2D or 3D vectors. If you +want to transform an image using perspective transformation, use +warpPerspective . If you have an inverse problem, that is, you want to +compute the most probable perspective transformation out of several +pairs of corresponding points, you can use getPerspectiveTransform or +findHomography . +@param src input two-channel or three-channel floating-point array; each +element is a 2D/3D vector to be transformed. +@param dst output array of the same size and type as src. +@param m 3x3 or 4x4 floating-point transformation matrix. +@sa transform, warpPerspective, getPerspectiveTransform, findHomography +*/ +CV_EXPORTS_W void perspectiveTransform(InputArray src, OutputArray dst, InputArray m ); + +/** @brief Copies the lower or the upper half of a square matrix to its another half. + +The function cv::completeSymm copies the lower or the upper half of a square matrix to +its another half. The matrix diagonal remains unchanged: + - \f$\texttt{m}_{ij}=\texttt{m}_{ji}\f$ for \f$i > j\f$ if + lowerToUpper=false + - \f$\texttt{m}_{ij}=\texttt{m}_{ji}\f$ for \f$i < j\f$ if + lowerToUpper=true + +@param m input-output floating-point square matrix. +@param lowerToUpper operation flag; if true, the lower half is copied to +the upper half. Otherwise, the upper half is copied to the lower half. +@sa flip, transpose +*/ +CV_EXPORTS_W void completeSymm(InputOutputArray m, bool lowerToUpper = false); + +/** @brief Initializes a scaled identity matrix. + +The function cv::setIdentity initializes a scaled identity matrix: +\f[\texttt{mtx} (i,j)= \fork{\texttt{value}}{ if \(i=j\)}{0}{otherwise}\f] + +The function can also be emulated using the matrix initializers and the +matrix expressions: +@code + Mat A = Mat::eye(4, 3, CV_32F)*5; + // A will be set to [[5, 0, 0], [0, 5, 0], [0, 0, 5], [0, 0, 0]] +@endcode +@param mtx matrix to initialize (not necessarily square). +@param s value to assign to diagonal elements. +@sa Mat::zeros, Mat::ones, Mat::setTo, Mat::operator= +*/ +CV_EXPORTS_W void setIdentity(InputOutputArray mtx, const Scalar& s = Scalar(1)); + +/** @brief Returns the determinant of a square floating-point matrix. + +The function cv::determinant calculates and returns the determinant of the +specified matrix. For small matrices ( mtx.cols=mtx.rows\<=3 ), the +direct method is used. For larger matrices, the function uses LU +factorization with partial pivoting. + +For symmetric positively-determined matrices, it is also possible to use +eigen decomposition to calculate the determinant. +@param mtx input matrix that must have CV_32FC1 or CV_64FC1 type and +square size. +@sa trace, invert, solve, eigen, @ref MatrixExpressions +*/ +CV_EXPORTS_W double determinant(InputArray mtx); + +/** @brief Returns the trace of a matrix. + +The function cv::trace returns the sum of the diagonal elements of the +matrix mtx . +\f[\mathrm{tr} ( \texttt{mtx} ) = \sum _i \texttt{mtx} (i,i)\f] +@param mtx input matrix. +*/ +CV_EXPORTS_W Scalar trace(InputArray mtx); + +/** @brief Finds the inverse or pseudo-inverse of a matrix. + +The function cv::invert inverts the matrix src and stores the result in dst +. When the matrix src is singular or non-square, the function calculates +the pseudo-inverse matrix (the dst matrix) so that norm(src\*dst - I) is +minimal, where I is an identity matrix. + +In case of the #DECOMP_LU method, the function returns non-zero value if +the inverse has been successfully calculated and 0 if src is singular. + +In case of the #DECOMP_SVD method, the function returns the inverse +condition number of src (the ratio of the smallest singular value to the +largest singular value) and 0 if src is singular. The SVD method +calculates a pseudo-inverse matrix if src is singular. + +Similarly to #DECOMP_LU, the method #DECOMP_CHOLESKY works only with +non-singular square matrices that should also be symmetrical and +positively defined. In this case, the function stores the inverted +matrix in dst and returns non-zero. Otherwise, it returns 0. + +@param src input floating-point M x N matrix. +@param dst output matrix of N x M size and the same type as src. +@param flags inversion method (cv::DecompTypes) +@sa solve, SVD +*/ +CV_EXPORTS_W double invert(InputArray src, OutputArray dst, int flags = DECOMP_LU); + +/** @brief Solves one or more linear systems or least-squares problems. + +The function cv::solve solves a linear system or least-squares problem (the +latter is possible with SVD or QR methods, or by specifying the flag +#DECOMP_NORMAL ): +\f[\texttt{dst} = \arg \min _X \| \texttt{src1} \cdot \texttt{X} - \texttt{src2} \|\f] + +If #DECOMP_LU or #DECOMP_CHOLESKY method is used, the function returns 1 +if src1 (or \f$\texttt{src1}^T\texttt{src1}\f$ ) is non-singular. Otherwise, +it returns 0. In the latter case, dst is not valid. Other methods find a +pseudo-solution in case of a singular left-hand side part. + +@note If you want to find a unity-norm solution of an under-defined +singular system \f$\texttt{src1}\cdot\texttt{dst}=0\f$ , the function solve +will not do the work. Use SVD::solveZ instead. + +@param src1 input matrix on the left-hand side of the system. +@param src2 input matrix on the right-hand side of the system. +@param dst output solution. +@param flags solution (matrix inversion) method (#DecompTypes) +@sa invert, SVD, eigen +*/ +CV_EXPORTS_W bool solve(InputArray src1, InputArray src2, + OutputArray dst, int flags = DECOMP_LU); + +/** @brief Sorts each row or each column of a matrix. + +The function cv::sort sorts each matrix row or each matrix column in +ascending or descending order. So you should pass two operation flags to +get desired behaviour. If you want to sort matrix rows or columns +lexicographically, you can use STL std::sort generic function with the +proper comparison predicate. + +@param src input single-channel array. +@param dst output array of the same size and type as src. +@param flags operation flags, a combination of #SortFlags +@sa sortIdx, randShuffle +*/ +CV_EXPORTS_W void sort(InputArray src, OutputArray dst, int flags); + +/** @brief Sorts each row or each column of a matrix. + +The function cv::sortIdx sorts each matrix row or each matrix column in the +ascending or descending order. So you should pass two operation flags to +get desired behaviour. Instead of reordering the elements themselves, it +stores the indices of sorted elements in the output array. For example: +@code + Mat A = Mat::eye(3,3,CV_32F), B; + sortIdx(A, B, SORT_EVERY_ROW + SORT_ASCENDING); + // B will probably contain + // (because of equal elements in A some permutations are possible): + // [[1, 2, 0], [0, 2, 1], [0, 1, 2]] +@endcode +@param src input single-channel array. +@param dst output integer array of the same size as src. +@param flags operation flags that could be a combination of cv::SortFlags +@sa sort, randShuffle +*/ +CV_EXPORTS_W void sortIdx(InputArray src, OutputArray dst, int flags); + +/** @brief Finds the real roots of a cubic equation. + +The function solveCubic finds the real roots of a cubic equation: +- if coeffs is a 4-element vector: +\f[\texttt{coeffs} [0] x^3 + \texttt{coeffs} [1] x^2 + \texttt{coeffs} [2] x + \texttt{coeffs} [3] = 0\f] +- if coeffs is a 3-element vector: +\f[x^3 + \texttt{coeffs} [0] x^2 + \texttt{coeffs} [1] x + \texttt{coeffs} [2] = 0\f] + +The roots are stored in the roots array. +@param coeffs equation coefficients, an array of 3 or 4 elements. +@param roots output array of real roots that has 1 or 3 elements. +@return number of real roots. It can be 0, 1 or 2. +*/ +CV_EXPORTS_W int solveCubic(InputArray coeffs, OutputArray roots); + +/** @brief Finds the real or complex roots of a polynomial equation. + +The function cv::solvePoly finds real and complex roots of a polynomial equation: +\f[\texttt{coeffs} [n] x^{n} + \texttt{coeffs} [n-1] x^{n-1} + ... + \texttt{coeffs} [1] x + \texttt{coeffs} [0] = 0\f] +@param coeffs array of polynomial coefficients. +@param roots output (complex) array of roots. +@param maxIters maximum number of iterations the algorithm does. +*/ +CV_EXPORTS_W double solvePoly(InputArray coeffs, OutputArray roots, int maxIters = 300); + +/** @brief Calculates eigenvalues and eigenvectors of a symmetric matrix. + +The function cv::eigen calculates just eigenvalues, or eigenvalues and eigenvectors of the symmetric +matrix src: +@code + src*eigenvectors.row(i).t() = eigenvalues.at(i)*eigenvectors.row(i).t() +@endcode + +@note Use cv::eigenNonSymmetric for calculation of real eigenvalues and eigenvectors of non-symmetric matrix. + +@param src input matrix that must have CV_32FC1 or CV_64FC1 type, square size and be symmetrical +(src ^T^ == src). +@param eigenvalues output vector of eigenvalues of the same type as src; the eigenvalues are stored +in the descending order. +@param eigenvectors output matrix of eigenvectors; it has the same size and type as src; the +eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding +eigenvalues. +@sa eigenNonSymmetric, completeSymm , PCA +*/ +CV_EXPORTS_W bool eigen(InputArray src, OutputArray eigenvalues, + OutputArray eigenvectors = noArray()); + +/** @brief Calculates eigenvalues and eigenvectors of a non-symmetric matrix (real eigenvalues only). + +@note Assumes real eigenvalues. + +The function calculates eigenvalues and eigenvectors (optional) of the square matrix src: +@code + src*eigenvectors.row(i).t() = eigenvalues.at(i)*eigenvectors.row(i).t() +@endcode + +@param src input matrix (CV_32FC1 or CV_64FC1 type). +@param eigenvalues output vector of eigenvalues (type is the same type as src). +@param eigenvectors output matrix of eigenvectors (type is the same type as src). The eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding eigenvalues. +@sa eigen +*/ +CV_EXPORTS_W void eigenNonSymmetric(InputArray src, OutputArray eigenvalues, + OutputArray eigenvectors); + +/** @brief Calculates the covariance matrix of a set of vectors. + +The function cv::calcCovarMatrix calculates the covariance matrix and, optionally, the mean vector of +the set of input vectors. +@param samples samples stored as separate matrices +@param nsamples number of samples +@param covar output covariance matrix of the type ctype and square size. +@param mean input or output (depending on the flags) array as the average value of the input vectors. +@param flags operation flags as a combination of #CovarFlags +@param ctype type of the matrixl; it equals 'CV_64F' by default. +@sa PCA, mulTransposed, Mahalanobis +@todo InputArrayOfArrays +*/ +CV_EXPORTS void calcCovarMatrix( const Mat* samples, int nsamples, Mat& covar, Mat& mean, + int flags, int ctype = CV_64F); + +/** @overload +@note use #COVAR_ROWS or #COVAR_COLS flag +@param samples samples stored as rows/columns of a single matrix. +@param covar output covariance matrix of the type ctype and square size. +@param mean input or output (depending on the flags) array as the average value of the input vectors. +@param flags operation flags as a combination of #CovarFlags +@param ctype type of the matrixl; it equals 'CV_64F' by default. +*/ +CV_EXPORTS_W void calcCovarMatrix( InputArray samples, OutputArray covar, + InputOutputArray mean, int flags, int ctype = CV_64F); + +/** wrap PCA::operator() */ +CV_EXPORTS_W void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, int maxComponents = 0); + +/** wrap PCA::operator() and add eigenvalues output parameter */ +CV_EXPORTS_AS(PCACompute2) void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, OutputArray eigenvalues, + int maxComponents = 0); + +/** wrap PCA::operator() */ +CV_EXPORTS_W void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, double retainedVariance); + +/** wrap PCA::operator() and add eigenvalues output parameter */ +CV_EXPORTS_AS(PCACompute2) void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, OutputArray eigenvalues, + double retainedVariance); + +/** wrap PCA::project */ +CV_EXPORTS_W void PCAProject(InputArray data, InputArray mean, + InputArray eigenvectors, OutputArray result); + +/** wrap PCA::backProject */ +CV_EXPORTS_W void PCABackProject(InputArray data, InputArray mean, + InputArray eigenvectors, OutputArray result); + +/** wrap SVD::compute */ +CV_EXPORTS_W void SVDecomp( InputArray src, OutputArray w, OutputArray u, OutputArray vt, int flags = 0 ); + +/** wrap SVD::backSubst */ +CV_EXPORTS_W void SVBackSubst( InputArray w, InputArray u, InputArray vt, + InputArray rhs, OutputArray dst ); + +/** @brief Calculates the Mahalanobis distance between two vectors. + +The function cv::Mahalanobis calculates and returns the weighted distance between two vectors: +\f[d( \texttt{vec1} , \texttt{vec2} )= \sqrt{\sum_{i,j}{\texttt{icovar(i,j)}\cdot(\texttt{vec1}(I)-\texttt{vec2}(I))\cdot(\texttt{vec1(j)}-\texttt{vec2(j)})} }\f] +The covariance matrix may be calculated using the #calcCovarMatrix function and then inverted using +the invert function (preferably using the #DECOMP_SVD method, as the most accurate). +@param v1 first 1D input vector. +@param v2 second 1D input vector. +@param icovar inverse covariance matrix. +*/ +CV_EXPORTS_W double Mahalanobis(InputArray v1, InputArray v2, InputArray icovar); + +/** @brief Performs a forward or inverse Discrete Fourier transform of a 1D or 2D floating-point array. + +The function cv::dft performs one of the following: +- Forward the Fourier transform of a 1D vector of N elements: + \f[Y = F^{(N)} \cdot X,\f] + where \f$F^{(N)}_{jk}=\exp(-2\pi i j k/N)\f$ and \f$i=\sqrt{-1}\f$ +- Inverse the Fourier transform of a 1D vector of N elements: + \f[\begin{array}{l} X'= \left (F^{(N)} \right )^{-1} \cdot Y = \left (F^{(N)} \right )^* \cdot y \\ X = (1/N) \cdot X, \end{array}\f] + where \f$F^*=\left(\textrm{Re}(F^{(N)})-\textrm{Im}(F^{(N)})\right)^T\f$ +- Forward the 2D Fourier transform of a M x N matrix: + \f[Y = F^{(M)} \cdot X \cdot F^{(N)}\f] +- Inverse the 2D Fourier transform of a M x N matrix: + \f[\begin{array}{l} X'= \left (F^{(M)} \right )^* \cdot Y \cdot \left (F^{(N)} \right )^* \\ X = \frac{1}{M \cdot N} \cdot X' \end{array}\f] + +In case of real (single-channel) data, the output spectrum of the forward Fourier transform or input +spectrum of the inverse Fourier transform can be represented in a packed format called *CCS* +(complex-conjugate-symmetrical). It was borrowed from IPL (Intel\* Image Processing Library). Here +is how 2D *CCS* spectrum looks: +\f[\begin{bmatrix} Re Y_{0,0} & Re Y_{0,1} & Im Y_{0,1} & Re Y_{0,2} & Im Y_{0,2} & \cdots & Re Y_{0,N/2-1} & Im Y_{0,N/2-1} & Re Y_{0,N/2} \\ Re Y_{1,0} & Re Y_{1,1} & Im Y_{1,1} & Re Y_{1,2} & Im Y_{1,2} & \cdots & Re Y_{1,N/2-1} & Im Y_{1,N/2-1} & Re Y_{1,N/2} \\ Im Y_{1,0} & Re Y_{2,1} & Im Y_{2,1} & Re Y_{2,2} & Im Y_{2,2} & \cdots & Re Y_{2,N/2-1} & Im Y_{2,N/2-1} & Im Y_{1,N/2} \\ \hdotsfor{9} \\ Re Y_{M/2-1,0} & Re Y_{M-3,1} & Im Y_{M-3,1} & \hdotsfor{3} & Re Y_{M-3,N/2-1} & Im Y_{M-3,N/2-1}& Re Y_{M/2-1,N/2} \\ Im Y_{M/2-1,0} & Re Y_{M-2,1} & Im Y_{M-2,1} & \hdotsfor{3} & Re Y_{M-2,N/2-1} & Im Y_{M-2,N/2-1}& Im Y_{M/2-1,N/2} \\ Re Y_{M/2,0} & Re Y_{M-1,1} & Im Y_{M-1,1} & \hdotsfor{3} & Re Y_{M-1,N/2-1} & Im Y_{M-1,N/2-1}& Re Y_{M/2,N/2} \end{bmatrix}\f] + +In case of 1D transform of a real vector, the output looks like the first row of the matrix above. + +So, the function chooses an operation mode depending on the flags and size of the input array: +- If #DFT_ROWS is set or the input array has a single row or single column, the function + performs a 1D forward or inverse transform of each row of a matrix when #DFT_ROWS is set. + Otherwise, it performs a 2D transform. +- If the input array is real and #DFT_INVERSE is not set, the function performs a forward 1D or + 2D transform: + - When #DFT_COMPLEX_OUTPUT is set, the output is a complex matrix of the same size as + input. + - When #DFT_COMPLEX_OUTPUT is not set, the output is a real matrix of the same size as + input. In case of 2D transform, it uses the packed format as shown above. In case of a + single 1D transform, it looks like the first row of the matrix above. In case of + multiple 1D transforms (when using the #DFT_ROWS flag), each row of the output matrix + looks like the first row of the matrix above. +- If the input array is complex and either #DFT_INVERSE or #DFT_REAL_OUTPUT are not set, the + output is a complex array of the same size as input. The function performs a forward or + inverse 1D or 2D transform of the whole input array or each row of the input array + independently, depending on the flags DFT_INVERSE and DFT_ROWS. +- When #DFT_INVERSE is set and the input array is real, or it is complex but #DFT_REAL_OUTPUT + is set, the output is a real array of the same size as input. The function performs a 1D or 2D + inverse transformation of the whole input array or each individual row, depending on the flags + #DFT_INVERSE and #DFT_ROWS. + +If #DFT_SCALE is set, the scaling is done after the transformation. + +Unlike dct , the function supports arrays of arbitrary size. But only those arrays are processed +efficiently, whose sizes can be factorized in a product of small prime numbers (2, 3, and 5 in the +current implementation). Such an efficient DFT size can be calculated using the getOptimalDFTSize +method. + +The sample below illustrates how to calculate a DFT-based convolution of two 2D real arrays: +@code + void convolveDFT(InputArray A, InputArray B, OutputArray C) + { + // reallocate the output array if needed + C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type()); + Size dftSize; + // calculate the size of DFT transform + dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1); + dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1); + + // allocate temporary buffers and initialize them with 0's + Mat tempA(dftSize, A.type(), Scalar::all(0)); + Mat tempB(dftSize, B.type(), Scalar::all(0)); + + // copy A and B to the top-left corners of tempA and tempB, respectively + Mat roiA(tempA, Rect(0,0,A.cols,A.rows)); + A.copyTo(roiA); + Mat roiB(tempB, Rect(0,0,B.cols,B.rows)); + B.copyTo(roiB); + + // now transform the padded A & B in-place; + // use "nonzeroRows" hint for faster processing + dft(tempA, tempA, 0, A.rows); + dft(tempB, tempB, 0, B.rows); + + // multiply the spectrums; + // the function handles packed spectrum representations well + mulSpectrums(tempA, tempB, tempA); + + // transform the product back from the frequency domain. + // Even though all the result rows will be non-zero, + // you need only the first C.rows of them, and thus you + // pass nonzeroRows == C.rows + dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows); + + // now copy the result back to C. + tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C); + + // all the temporary buffers will be deallocated automatically + } +@endcode +To optimize this sample, consider the following approaches: +- Since nonzeroRows != 0 is passed to the forward transform calls and since A and B are copied to + the top-left corners of tempA and tempB, respectively, it is not necessary to clear the whole + tempA and tempB. It is only necessary to clear the tempA.cols - A.cols ( tempB.cols - B.cols) + rightmost columns of the matrices. +- This DFT-based convolution does not have to be applied to the whole big arrays, especially if B + is significantly smaller than A or vice versa. Instead, you can calculate convolution by parts. + To do this, you need to split the output array C into multiple tiles. For each tile, estimate + which parts of A and B are required to calculate convolution in this tile. If the tiles in C are + too small, the speed will decrease a lot because of repeated work. In the ultimate case, when + each tile in C is a single pixel, the algorithm becomes equivalent to the naive convolution + algorithm. If the tiles are too big, the temporary arrays tempA and tempB become too big and + there is also a slowdown because of bad cache locality. So, there is an optimal tile size + somewhere in the middle. +- If different tiles in C can be calculated in parallel and, thus, the convolution is done by + parts, the loop can be threaded. + +All of the above improvements have been implemented in #matchTemplate and #filter2D . Therefore, by +using them, you can get the performance even better than with the above theoretically optimal +implementation. Though, those two functions actually calculate cross-correlation, not convolution, +so you need to "flip" the second convolution operand B vertically and horizontally using flip . +@note +- An example using the discrete fourier transform can be found at + opencv_source_code/samples/cpp/dft.cpp +- (Python) An example using the dft functionality to perform Wiener deconvolution can be found + at opencv_source/samples/python/deconvolution.py +- (Python) An example rearranging the quadrants of a Fourier image can be found at + opencv_source/samples/python/dft.py +@param src input array that could be real or complex. +@param dst output array whose size and type depends on the flags . +@param flags transformation flags, representing a combination of the #DftFlags +@param nonzeroRows when the parameter is not zero, the function assumes that only the first +nonzeroRows rows of the input array (#DFT_INVERSE is not set) or only the first nonzeroRows of the +output array (#DFT_INVERSE is set) contain non-zeros, thus, the function can handle the rest of the +rows more efficiently and save some time; this technique is very useful for calculating array +cross-correlation or convolution using DFT. +@sa dct , getOptimalDFTSize , mulSpectrums, filter2D , matchTemplate , flip , cartToPolar , +magnitude , phase +*/ +CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0); + +/** @brief Calculates the inverse Discrete Fourier Transform of a 1D or 2D array. + +idft(src, dst, flags) is equivalent to dft(src, dst, flags | #DFT_INVERSE) . +@note None of dft and idft scales the result by default. So, you should pass #DFT_SCALE to one of +dft or idft explicitly to make these transforms mutually inverse. +@sa dft, dct, idct, mulSpectrums, getOptimalDFTSize +@param src input floating-point real or complex array. +@param dst output array whose size and type depend on the flags. +@param flags operation flags (see dft and #DftFlags). +@param nonzeroRows number of dst rows to process; the rest of the rows have undefined content (see +the convolution sample in dft description. +*/ +CV_EXPORTS_W void idft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0); + +/** @brief Performs a forward or inverse discrete Cosine transform of 1D or 2D array. + +The function cv::dct performs a forward or inverse discrete Cosine transform (DCT) of a 1D or 2D +floating-point array: +- Forward Cosine transform of a 1D vector of N elements: + \f[Y = C^{(N)} \cdot X\f] + where + \f[C^{(N)}_{jk}= \sqrt{\alpha_j/N} \cos \left ( \frac{\pi(2k+1)j}{2N} \right )\f] + and + \f$\alpha_0=1\f$, \f$\alpha_j=2\f$ for *j \> 0*. +- Inverse Cosine transform of a 1D vector of N elements: + \f[X = \left (C^{(N)} \right )^{-1} \cdot Y = \left (C^{(N)} \right )^T \cdot Y\f] + (since \f$C^{(N)}\f$ is an orthogonal matrix, \f$C^{(N)} \cdot \left(C^{(N)}\right)^T = I\f$ ) +- Forward 2D Cosine transform of M x N matrix: + \f[Y = C^{(N)} \cdot X \cdot \left (C^{(N)} \right )^T\f] +- Inverse 2D Cosine transform of M x N matrix: + \f[X = \left (C^{(N)} \right )^T \cdot X \cdot C^{(N)}\f] + +The function chooses the mode of operation by looking at the flags and size of the input array: +- If (flags & #DCT_INVERSE) == 0 , the function does a forward 1D or 2D transform. Otherwise, it + is an inverse 1D or 2D transform. +- If (flags & #DCT_ROWS) != 0 , the function performs a 1D transform of each row. +- If the array is a single column or a single row, the function performs a 1D transform. +- If none of the above is true, the function performs a 2D transform. + +@note Currently dct supports even-size arrays (2, 4, 6 ...). For data analysis and approximation, you +can pad the array when necessary. +Also, the function performance depends very much, and not monotonically, on the array size (see +getOptimalDFTSize ). In the current implementation DCT of a vector of size N is calculated via DFT +of a vector of size N/2 . Thus, the optimal DCT size N1 \>= N can be calculated as: +@code + size_t getOptimalDCTSize(size_t N) { return 2*getOptimalDFTSize((N+1)/2); } + N1 = getOptimalDCTSize(N); +@endcode +@param src input floating-point array. +@param dst output array of the same size and type as src . +@param flags transformation flags as a combination of cv::DftFlags (DCT_*) +@sa dft , getOptimalDFTSize , idct +*/ +CV_EXPORTS_W void dct(InputArray src, OutputArray dst, int flags = 0); + +/** @brief Calculates the inverse Discrete Cosine Transform of a 1D or 2D array. + +idct(src, dst, flags) is equivalent to dct(src, dst, flags | DCT_INVERSE). +@param src input floating-point single-channel array. +@param dst output array of the same size and type as src. +@param flags operation flags. +@sa dct, dft, idft, getOptimalDFTSize +*/ +CV_EXPORTS_W void idct(InputArray src, OutputArray dst, int flags = 0); + +/** @brief Performs the per-element multiplication of two Fourier spectrums. + +The function cv::mulSpectrums performs the per-element multiplication of the two CCS-packed or complex +matrices that are results of a real or complex Fourier transform. + +The function, together with dft and idft , may be used to calculate convolution (pass conjB=false ) +or correlation (pass conjB=true ) of two arrays rapidly. When the arrays are complex, they are +simply multiplied (per element) with an optional conjugation of the second-array elements. When the +arrays are real, they are assumed to be CCS-packed (see dft for details). +@param a first input array. +@param b second input array of the same size and type as src1 . +@param c output array of the same size and type as src1 . +@param flags operation flags; currently, the only supported flag is cv::DFT_ROWS, which indicates that +each row of src1 and src2 is an independent 1D Fourier spectrum. If you do not want to use this flag, then simply add a `0` as value. +@param conjB optional flag that conjugates the second input array before the multiplication (true) +or not (false). +*/ +CV_EXPORTS_W void mulSpectrums(InputArray a, InputArray b, OutputArray c, + int flags, bool conjB = false); + +/** @brief Returns the optimal DFT size for a given vector size. + +DFT performance is not a monotonic function of a vector size. Therefore, when you calculate +convolution of two arrays or perform the spectral analysis of an array, it usually makes sense to +pad the input data with zeros to get a bit larger array that can be transformed much faster than the +original one. Arrays whose size is a power-of-two (2, 4, 8, 16, 32, ...) are the fastest to process. +Though, the arrays whose size is a product of 2's, 3's, and 5's (for example, 300 = 5\*5\*3\*2\*2) +are also processed quite efficiently. + +The function cv::getOptimalDFTSize returns the minimum number N that is greater than or equal to vecsize +so that the DFT of a vector of size N can be processed efficiently. In the current implementation N += 2 ^p^ \* 3 ^q^ \* 5 ^r^ for some integer p, q, r. + +The function returns a negative number if vecsize is too large (very close to INT_MAX ). + +While the function cannot be used directly to estimate the optimal vector size for DCT transform +(since the current DCT implementation supports only even-size vectors), it can be easily processed +as getOptimalDFTSize((vecsize+1)/2)\*2. +@param vecsize vector size. +@sa dft , dct , idft , idct , mulSpectrums +*/ +CV_EXPORTS_W int getOptimalDFTSize(int vecsize); + +/** @brief Returns the default random number generator. + +The function cv::theRNG returns the default random number generator. For each thread, there is a +separate random number generator, so you can use the function safely in multi-thread environments. +If you just need to get a single random number using this generator or initialize an array, you can +use randu or randn instead. But if you are going to generate many random numbers inside a loop, it +is much faster to use this function to retrieve the generator and then use RNG::operator _Tp() . +@sa RNG, randu, randn +*/ +CV_EXPORTS RNG& theRNG(); + +/** @brief Sets state of default random number generator. + +The function cv::setRNGSeed sets state of default random number generator to custom value. +@param seed new state for default random number generator +@sa RNG, randu, randn +*/ +CV_EXPORTS_W void setRNGSeed(int seed); + +/** @brief Generates a single uniformly-distributed random number or an array of random numbers. + +Non-template variant of the function fills the matrix dst with uniformly-distributed +random numbers from the specified range: +\f[\texttt{low} _c \leq \texttt{dst} (I)_c < \texttt{high} _c\f] +@param dst output array of random numbers; the array must be pre-allocated. +@param low inclusive lower boundary of the generated random numbers. +@param high exclusive upper boundary of the generated random numbers. +@sa RNG, randn, theRNG +*/ +CV_EXPORTS_W void randu(InputOutputArray dst, InputArray low, InputArray high); + +/** @brief Fills the array with normally distributed random numbers. + +The function cv::randn fills the matrix dst with normally distributed random numbers with the specified +mean vector and the standard deviation matrix. The generated random numbers are clipped to fit the +value range of the output array data type. +@param dst output array of random numbers; the array must be pre-allocated and have 1 to 4 channels. +@param mean mean value (expectation) of the generated random numbers. +@param stddev standard deviation of the generated random numbers; it can be either a vector (in +which case a diagonal standard deviation matrix is assumed) or a square matrix. +@sa RNG, randu +*/ +CV_EXPORTS_W void randn(InputOutputArray dst, InputArray mean, InputArray stddev); + +/** @brief Shuffles the array elements randomly. + +The function cv::randShuffle shuffles the specified 1D array by randomly choosing pairs of elements and +swapping them. The number of such swap operations will be dst.rows\*dst.cols\*iterFactor . +@param dst input/output numerical 1D array. +@param iterFactor scale factor that determines the number of random swap operations (see the details +below). +@param rng optional random number generator used for shuffling; if it is zero, theRNG () is used +instead. +@sa RNG, sort +*/ +CV_EXPORTS_W void randShuffle(InputOutputArray dst, double iterFactor = 1., RNG* rng = 0); + +/** @brief Principal Component Analysis + +The class is used to calculate a special basis for a set of vectors. The +basis will consist of eigenvectors of the covariance matrix calculated +from the input set of vectors. The class %PCA can also transform +vectors to/from the new coordinate space defined by the basis. Usually, +in this new coordinate system, each vector from the original set (and +any linear combination of such vectors) can be quite accurately +approximated by taking its first few components, corresponding to the +eigenvectors of the largest eigenvalues of the covariance matrix. +Geometrically it means that you calculate a projection of the vector to +a subspace formed by a few eigenvectors corresponding to the dominant +eigenvalues of the covariance matrix. And usually such a projection is +very close to the original vector. So, you can represent the original +vector from a high-dimensional space with a much shorter vector +consisting of the projected vector's coordinates in the subspace. Such a +transformation is also known as Karhunen-Loeve Transform, or KLT. +See http://en.wikipedia.org/wiki/Principal_component_analysis + +The sample below is the function that takes two matrices. The first +function stores a set of vectors (a row per vector) that is used to +calculate PCA. The second function stores another "test" set of vectors +(a row per vector). First, these vectors are compressed with PCA, then +reconstructed back, and then the reconstruction error norm is computed +and printed for each vector. : + +@code{.cpp} +using namespace cv; + +PCA compressPCA(const Mat& pcaset, int maxComponents, + const Mat& testset, Mat& compressed) +{ + PCA pca(pcaset, // pass the data + Mat(), // we do not have a pre-computed mean vector, + // so let the PCA engine to compute it + PCA::DATA_AS_ROW, // indicate that the vectors + // are stored as matrix rows + // (use PCA::DATA_AS_COL if the vectors are + // the matrix columns) + maxComponents // specify, how many principal components to retain + ); + // if there is no test data, just return the computed basis, ready-to-use + if( !testset.data ) + return pca; + CV_Assert( testset.cols == pcaset.cols ); + + compressed.create(testset.rows, maxComponents, testset.type()); + + Mat reconstructed; + for( int i = 0; i < testset.rows; i++ ) + { + Mat vec = testset.row(i), coeffs = compressed.row(i), reconstructed; + // compress the vector, the result will be stored + // in the i-th row of the output matrix + pca.project(vec, coeffs); + // and then reconstruct it + pca.backProject(coeffs, reconstructed); + // and measure the error + printf("%d. diff = %g\n", i, norm(vec, reconstructed, NORM_L2)); + } + return pca; +} +@endcode +@sa calcCovarMatrix, mulTransposed, SVD, dft, dct +*/ +class CV_EXPORTS PCA +{ +public: + enum Flags { DATA_AS_ROW = 0, //!< indicates that the input samples are stored as matrix rows + DATA_AS_COL = 1, //!< indicates that the input samples are stored as matrix columns + USE_AVG = 2 //! + }; + + /** @brief default constructor + + The default constructor initializes an empty %PCA structure. The other + constructors initialize the structure and call PCA::operator()(). + */ + PCA(); + + /** @overload + @param data input samples stored as matrix rows or matrix columns. + @param mean optional mean value; if the matrix is empty (@c noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout (PCA::Flags) + @param maxComponents maximum number of components that %PCA should + retain; by default, all the components are retained. + */ + PCA(InputArray data, InputArray mean, int flags, int maxComponents = 0); + + /** @overload + @param data input samples stored as matrix rows or matrix columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout (PCA::Flags) + @param retainedVariance Percentage of variance that PCA should retain. + Using this parameter will let the PCA decided how many components to + retain but it will always keep at least 2. + */ + PCA(InputArray data, InputArray mean, int flags, double retainedVariance); + + /** @brief performs %PCA + + The operator performs %PCA of the supplied dataset. It is safe to reuse + the same PCA structure for multiple datasets. That is, if the structure + has been previously used with another dataset, the existing internal + data is reclaimed and the new @ref eigenvalues, @ref eigenvectors and @ref + mean are allocated and computed. + + The computed @ref eigenvalues are sorted from the largest to the smallest and + the corresponding @ref eigenvectors are stored as eigenvectors rows. + + @param data input samples stored as the matrix rows or as the matrix + columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout. (Flags) + @param maxComponents maximum number of components that PCA should + retain; by default, all the components are retained. + */ + PCA& operator()(InputArray data, InputArray mean, int flags, int maxComponents = 0); + + /** @overload + @param data input samples stored as the matrix rows or as the matrix + columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout. (PCA::Flags) + @param retainedVariance Percentage of variance that %PCA should retain. + Using this parameter will let the %PCA decided how many components to + retain but it will always keep at least 2. + */ + PCA& operator()(InputArray data, InputArray mean, int flags, double retainedVariance); + + /** @brief Projects vector(s) to the principal component subspace. + + The methods project one or more vectors to the principal component + subspace, where each vector projection is represented by coefficients in + the principal component basis. The first form of the method returns the + matrix that the second form writes to the result. So the first form can + be used as a part of expression while the second form can be more + efficient in a processing loop. + @param vec input vector(s); must have the same dimensionality and the + same layout as the input data used at %PCA phase, that is, if + DATA_AS_ROW are specified, then `vec.cols==data.cols` + (vector dimensionality) and `vec.rows` is the number of vectors to + project, and the same is true for the PCA::DATA_AS_COL case. + */ + Mat project(InputArray vec) const; + + /** @overload + @param vec input vector(s); must have the same dimensionality and the + same layout as the input data used at PCA phase, that is, if + DATA_AS_ROW are specified, then `vec.cols==data.cols` + (vector dimensionality) and `vec.rows` is the number of vectors to + project, and the same is true for the PCA::DATA_AS_COL case. + @param result output vectors; in case of PCA::DATA_AS_COL, the + output matrix has as many columns as the number of input vectors, this + means that `result.cols==vec.cols` and the number of rows match the + number of principal components (for example, `maxComponents` parameter + passed to the constructor). + */ + void project(InputArray vec, OutputArray result) const; + + /** @brief Reconstructs vectors from their PC projections. + + The methods are inverse operations to PCA::project. They take PC + coordinates of projected vectors and reconstruct the original vectors. + Unless all the principal components have been retained, the + reconstructed vectors are different from the originals. But typically, + the difference is small if the number of components is large enough (but + still much smaller than the original vector dimensionality). As a + result, PCA is used. + @param vec coordinates of the vectors in the principal component + subspace, the layout and size are the same as of PCA::project output + vectors. + */ + Mat backProject(InputArray vec) const; + + /** @overload + @param vec coordinates of the vectors in the principal component + subspace, the layout and size are the same as of PCA::project output + vectors. + @param result reconstructed vectors; the layout and size are the same as + of PCA::project input vectors. + */ + void backProject(InputArray vec, OutputArray result) const; + + /** @brief write PCA objects + + Writes @ref eigenvalues @ref eigenvectors and @ref mean to specified FileStorage + */ + void write(FileStorage& fs) const; + + /** @brief load PCA objects + + Loads @ref eigenvalues @ref eigenvectors and @ref mean from specified FileNode + */ + void read(const FileNode& fn); + + Mat eigenvectors; //!< eigenvectors of the covariation matrix + Mat eigenvalues; //!< eigenvalues of the covariation matrix + Mat mean; //!< mean value subtracted before the projection and added after the back projection +}; + +/** @example samples/cpp/pca.cpp +An example using %PCA for dimensionality reduction while maintaining an amount of variance +*/ + +/** @example samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp +Check @ref tutorial_introduction_to_pca "the corresponding tutorial" for more details +*/ + +/** +@brief Linear Discriminant Analysis +@todo document this class +*/ +class CV_EXPORTS LDA +{ +public: + /** @brief constructor + Initializes a LDA with num_components (default 0). + */ + explicit LDA(int num_components = 0); + + /** Initializes and performs a Discriminant Analysis with Fisher's + Optimization Criterion on given data in src and corresponding labels + in labels. If 0 (or less) number of components are given, they are + automatically determined for given data in computation. + */ + LDA(InputArrayOfArrays src, InputArray labels, int num_components = 0); + + /** Serializes this object to a given filename. + */ + void save(const String& filename) const; + + /** Deserializes this object from a given filename. + */ + void load(const String& filename); + + /** Serializes this object to a given cv::FileStorage. + */ + void save(FileStorage& fs) const; + + /** Deserializes this object from a given cv::FileStorage. + */ + void load(const FileStorage& node); + + /** destructor + */ + ~LDA(); + + /** Compute the discriminants for data in src (row aligned) and labels. + */ + void compute(InputArrayOfArrays src, InputArray labels); + + /** Projects samples into the LDA subspace. + src may be one or more row aligned samples. + */ + Mat project(InputArray src); + + /** Reconstructs projections from the LDA subspace. + src may be one or more row aligned projections. + */ + Mat reconstruct(InputArray src); + + /** Returns the eigenvectors of this LDA. + */ + Mat eigenvectors() const { return _eigenvectors; } + + /** Returns the eigenvalues of this LDA. + */ + Mat eigenvalues() const { return _eigenvalues; } + + static Mat subspaceProject(InputArray W, InputArray mean, InputArray src); + static Mat subspaceReconstruct(InputArray W, InputArray mean, InputArray src); + +protected: + bool _dataAsRow; // unused, but needed for 3.0 ABI compatibility. + int _num_components; + Mat _eigenvectors; + Mat _eigenvalues; + void lda(InputArrayOfArrays src, InputArray labels); +}; + +/** @brief Singular Value Decomposition + +Class for computing Singular Value Decomposition of a floating-point +matrix. The Singular Value Decomposition is used to solve least-square +problems, under-determined linear systems, invert matrices, compute +condition numbers, and so on. + +If you want to compute a condition number of a matrix or an absolute value of +its determinant, you do not need `u` and `vt`. You can pass +flags=SVD::NO_UV|... . Another flag SVD::FULL_UV indicates that full-size u +and vt must be computed, which is not necessary most of the time. + +@sa invert, solve, eigen, determinant +*/ +class CV_EXPORTS SVD +{ +public: + enum Flags { + /** allow the algorithm to modify the decomposed matrix; it can save space and speed up + processing. currently ignored. */ + MODIFY_A = 1, + /** indicates that only a vector of singular values `w` is to be processed, while u and vt + will be set to empty matrices */ + NO_UV = 2, + /** when the matrix is not square, by default the algorithm produces u and vt matrices of + sufficiently large size for the further A reconstruction; if, however, FULL_UV flag is + specified, u and vt will be full-size square orthogonal matrices.*/ + FULL_UV = 4 + }; + + /** @brief the default constructor + + initializes an empty SVD structure + */ + SVD(); + + /** @overload + initializes an empty SVD structure and then calls SVD::operator() + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param flags operation flags (SVD::Flags) + */ + SVD( InputArray src, int flags = 0 ); + + /** @brief the operator that performs SVD. The previously allocated u, w and vt are released. + + The operator performs the singular value decomposition of the supplied + matrix. The u,`vt` , and the vector of singular values w are stored in + the structure. The same SVD structure can be reused many times with + different matrices. Each time, if needed, the previous u,`vt` , and w + are reclaimed and the new matrices are created, which is all handled by + Mat::create. + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param flags operation flags (SVD::Flags) + */ + SVD& operator ()( InputArray src, int flags = 0 ); + + /** @brief decomposes matrix and stores the results to user-provided matrices + + The methods/functions perform SVD of matrix. Unlike SVD::SVD constructor + and SVD::operator(), they store the results to the user-provided + matrices: + + @code{.cpp} + Mat A, w, u, vt; + SVD::compute(A, w, u, vt); + @endcode + + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param w calculated singular values + @param u calculated left singular vectors + @param vt transposed matrix of right singular vectors + @param flags operation flags - see SVD::Flags. + */ + static void compute( InputArray src, OutputArray w, + OutputArray u, OutputArray vt, int flags = 0 ); + + /** @overload + computes singular values of a matrix + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param w calculated singular values + @param flags operation flags - see SVD::Flags. + */ + static void compute( InputArray src, OutputArray w, int flags = 0 ); + + /** @brief performs back substitution + */ + static void backSubst( InputArray w, InputArray u, + InputArray vt, InputArray rhs, + OutputArray dst ); + + /** @brief solves an under-determined singular linear system + + The method finds a unit-length solution x of a singular linear system + A\*x = 0. Depending on the rank of A, there can be no solutions, a + single solution or an infinite number of solutions. In general, the + algorithm solves the following problem: + \f[dst = \arg \min _{x: \| x \| =1} \| src \cdot x \|\f] + @param src left-hand-side matrix. + @param dst found solution. + */ + static void solveZ( InputArray src, OutputArray dst ); + + /** @brief performs a singular value back substitution. + + The method calculates a back substitution for the specified right-hand + side: + + \f[\texttt{x} = \texttt{vt} ^T \cdot diag( \texttt{w} )^{-1} \cdot \texttt{u} ^T \cdot \texttt{rhs} \sim \texttt{A} ^{-1} \cdot \texttt{rhs}\f] + + Using this technique you can either get a very accurate solution of the + convenient linear system, or the best (in the least-squares terms) + pseudo-solution of an overdetermined linear system. + + @param rhs right-hand side of a linear system (u\*w\*v')\*dst = rhs to + be solved, where A has been previously decomposed. + + @param dst found solution of the system. + + @note Explicit SVD with the further back substitution only makes sense + if you need to solve many linear systems with the same left-hand side + (for example, src ). If all you need is to solve a single system + (possibly with multiple rhs immediately available), simply call solve + add pass #DECOMP_SVD there. It does absolutely the same thing. + */ + void backSubst( InputArray rhs, OutputArray dst ) const; + + /** @todo document */ + template static + void compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w, Matx<_Tp, m, nm>& u, Matx<_Tp, n, nm>& vt ); + + /** @todo document */ + template static + void compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w ); + + /** @todo document */ + template static + void backSubst( const Matx<_Tp, nm, 1>& w, const Matx<_Tp, m, nm>& u, const Matx<_Tp, n, nm>& vt, const Matx<_Tp, m, nb>& rhs, Matx<_Tp, n, nb>& dst ); + + Mat u, w, vt; +}; + +/** @brief Random Number Generator + +Random number generator. It encapsulates the state (currently, a 64-bit +integer) and has methods to return scalar random values and to fill +arrays with random values. Currently it supports uniform and Gaussian +(normal) distributions. The generator uses Multiply-With-Carry +algorithm, introduced by G. Marsaglia ( + ). +Gaussian-distribution random numbers are generated using the Ziggurat +algorithm ( ), +introduced by G. Marsaglia and W. W. Tsang. +*/ +class CV_EXPORTS RNG +{ +public: + enum { UNIFORM = 0, + NORMAL = 1 + }; + + /** @brief constructor + + These are the RNG constructors. The first form sets the state to some + pre-defined value, equal to 2\*\*32-1 in the current implementation. The + second form sets the state to the specified value. If you passed state=0 + , the constructor uses the above default value instead to avoid the + singular random number sequence, consisting of all zeros. + */ + RNG(); + /** @overload + @param state 64-bit value used to initialize the RNG. + */ + RNG(uint64 state); + /**The method updates the state using the MWC algorithm and returns the + next 32-bit random number.*/ + unsigned next(); + + /**Each of the methods updates the state using the MWC algorithm and + returns the next random number of the specified type. In case of integer + types, the returned number is from the available value range for the + specified type. In case of floating-point types, the returned value is + from [0,1) range. + */ + operator uchar(); + /** @overload */ + operator schar(); + /** @overload */ + operator ushort(); + /** @overload */ + operator short(); + /** @overload */ + operator unsigned(); + /** @overload */ + operator int(); + /** @overload */ + operator float(); + /** @overload */ + operator double(); + + /** @brief returns a random integer sampled uniformly from [0, N). + + The methods transform the state using the MWC algorithm and return the + next random number. The first form is equivalent to RNG::next . The + second form returns the random number modulo N , which means that the + result is in the range [0, N) . + */ + unsigned operator ()(); + /** @overload + @param N upper non-inclusive boundary of the returned random number. + */ + unsigned operator ()(unsigned N); + + /** @brief returns uniformly distributed integer random number from [a,b) range + + The methods transform the state using the MWC algorithm and return the + next uniformly-distributed random number of the specified type, deduced + from the input parameter type, from the range [a, b) . There is a nuance + illustrated by the following sample: + + @code{.cpp} + RNG rng; + + // always produces 0 + double a = rng.uniform(0, 1); + + // produces double from [0, 1) + double a1 = rng.uniform((double)0, (double)1); + + // produces float from [0, 1) + float b = rng.uniform(0.f, 1.f); + + // produces double from [0, 1) + double c = rng.uniform(0., 1.); + + // may cause compiler error because of ambiguity: + // RNG::uniform(0, (int)0.999999)? or RNG::uniform((double)0, 0.99999)? + double d = rng.uniform(0, 0.999999); + @endcode + + The compiler does not take into account the type of the variable to + which you assign the result of RNG::uniform . The only thing that + matters to the compiler is the type of a and b parameters. So, if you + want a floating-point random number, but the range boundaries are + integer numbers, either put dots in the end, if they are constants, or + use explicit type cast operators, as in the a1 initialization above. + @param a lower inclusive boundary of the returned random number. + @param b upper non-inclusive boundary of the returned random number. + */ + int uniform(int a, int b); + /** @overload */ + float uniform(float a, float b); + /** @overload */ + double uniform(double a, double b); + + /** @brief Fills arrays with random numbers. + + @param mat 2D or N-dimensional matrix; currently matrices with more than + 4 channels are not supported by the methods, use Mat::reshape as a + possible workaround. + @param distType distribution type, RNG::UNIFORM or RNG::NORMAL. + @param a first distribution parameter; in case of the uniform + distribution, this is an inclusive lower boundary, in case of the normal + distribution, this is a mean value. + @param b second distribution parameter; in case of the uniform + distribution, this is a non-inclusive upper boundary, in case of the + normal distribution, this is a standard deviation (diagonal of the + standard deviation matrix or the full standard deviation matrix). + @param saturateRange pre-saturation flag; for uniform distribution only; + if true, the method will first convert a and b to the acceptable value + range (according to the mat datatype) and then will generate uniformly + distributed random numbers within the range [saturate(a), saturate(b)), + if saturateRange=false, the method will generate uniformly distributed + random numbers in the original range [a, b) and then will saturate them, + it means, for example, that + theRNG().fill(mat_8u, RNG::UNIFORM, -DBL_MAX, DBL_MAX) will likely + produce array mostly filled with 0's and 255's, since the range (0, 255) + is significantly smaller than [-DBL_MAX, DBL_MAX). + + Each of the methods fills the matrix with the random values from the + specified distribution. As the new numbers are generated, the RNG state + is updated accordingly. In case of multiple-channel images, every + channel is filled independently, which means that RNG cannot generate + samples from the multi-dimensional Gaussian distribution with + non-diagonal covariance matrix directly. To do that, the method + generates samples from multi-dimensional standard Gaussian distribution + with zero mean and identity covariation matrix, and then transforms them + using transform to get samples from the specified Gaussian distribution. + */ + void fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange = false ); + + /** @brief Returns the next random number sampled from the Gaussian distribution + @param sigma standard deviation of the distribution. + + The method transforms the state using the MWC algorithm and returns the + next random number from the Gaussian distribution N(0,sigma) . That is, + the mean value of the returned random numbers is zero and the standard + deviation is the specified sigma . + */ + double gaussian(double sigma); + + uint64 state; + + bool operator ==(const RNG& other) const; +}; + +/** @brief Mersenne Twister random number generator + +Inspired by http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c +@todo document +*/ +class CV_EXPORTS RNG_MT19937 +{ +public: + RNG_MT19937(); + RNG_MT19937(unsigned s); + void seed(unsigned s); + + unsigned next(); + + operator int(); + operator unsigned(); + operator float(); + operator double(); + + unsigned operator ()(unsigned N); + unsigned operator ()(); + + /** @brief returns uniformly distributed integer random number from [a,b) range*/ + int uniform(int a, int b); + /** @brief returns uniformly distributed floating-point random number from [a,b) range*/ + float uniform(float a, float b); + /** @brief returns uniformly distributed double-precision floating-point random number from [a,b) range*/ + double uniform(double a, double b); + +private: + enum PeriodParameters {N = 624, M = 397}; + unsigned state[N]; + int mti; +}; + +//! @} core_array + +//! @addtogroup core_cluster +//! @{ + +/** @example samples/cpp/kmeans.cpp +An example on K-means clustering +*/ + +/** @brief Finds centers of clusters and groups input samples around the clusters. + +The function kmeans implements a k-means algorithm that finds the centers of cluster_count clusters +and groups the input samples around the clusters. As an output, \f$\texttt{bestLabels}_i\f$ contains a +0-based cluster index for the sample stored in the \f$i^{th}\f$ row of the samples matrix. + +@note +- (Python) An example on K-means clustering can be found at + opencv_source_code/samples/python/kmeans.py +@param data Data for clustering. An array of N-Dimensional points with float coordinates is needed. +Examples of this array can be: +- Mat points(count, 2, CV_32F); +- Mat points(count, 1, CV_32FC2); +- Mat points(1, count, CV_32FC2); +- std::vector\ points(sampleCount); +@param K Number of clusters to split the set by. +@param bestLabels Input/output integer array that stores the cluster indices for every sample. +@param criteria The algorithm termination criteria, that is, the maximum number of iterations and/or +the desired accuracy. The accuracy is specified as criteria.epsilon. As soon as each of the cluster +centers moves by less than criteria.epsilon on some iteration, the algorithm stops. +@param attempts Flag to specify the number of times the algorithm is executed using different +initial labellings. The algorithm returns the labels that yield the best compactness (see the last +function parameter). +@param flags Flag that can take values of cv::KmeansFlags +@param centers Output matrix of the cluster centers, one row per each cluster center. +@return The function returns the compactness measure that is computed as +\f[\sum _i \| \texttt{samples} _i - \texttt{centers} _{ \texttt{labels} _i} \| ^2\f] +after every attempt. The best (minimum) value is chosen and the corresponding labels and the +compactness value are returned by the function. Basically, you can use only the core of the +function, set the number of attempts to 1, initialize labels each time using a custom algorithm, +pass them with the ( flags = #KMEANS_USE_INITIAL_LABELS ) flag, and then choose the best +(most-compact) clustering. +*/ +CV_EXPORTS_W double kmeans( InputArray data, int K, InputOutputArray bestLabels, + TermCriteria criteria, int attempts, + int flags, OutputArray centers = noArray() ); + +//! @} core_cluster + +//! @addtogroup core_basic +//! @{ + +/////////////////////////////// Formatted output of cv::Mat /////////////////////////// + +/** @todo document */ +class CV_EXPORTS Formatted +{ +public: + virtual const char* next() = 0; + virtual void reset() = 0; + virtual ~Formatted(); +}; + +/** @todo document */ +class CV_EXPORTS Formatter +{ +public: + enum { FMT_DEFAULT = 0, + FMT_MATLAB = 1, + FMT_CSV = 2, + FMT_PYTHON = 3, + FMT_NUMPY = 4, + FMT_C = 5 + }; + + virtual ~Formatter(); + + virtual Ptr format(const Mat& mtx) const = 0; + + virtual void set32fPrecision(int p = 8) = 0; + virtual void set64fPrecision(int p = 16) = 0; + virtual void setMultiline(bool ml = true) = 0; + + static Ptr get(int fmt = FMT_DEFAULT); + +}; + +static inline +String& operator << (String& out, Ptr fmtd) +{ + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + out += cv::String(str); + return out; +} + +static inline +String& operator << (String& out, const Mat& mtx) +{ + return out << Formatter::get()->format(mtx); +} + +//////////////////////////////////////// Algorithm //////////////////////////////////// + +class CV_EXPORTS Algorithm; + +template struct ParamType {}; + + +/** @brief This is a base class for all more or less complex algorithms in OpenCV + +especially for classes of algorithms, for which there can be multiple implementations. The examples +are stereo correspondence (for which there are algorithms like block matching, semi-global block +matching, graph-cut etc.), background subtraction (which can be done using mixture-of-gaussians +models, codebook-based algorithm etc.), optical flow (block matching, Lucas-Kanade, Horn-Schunck +etc.). + +Here is example of SimpleBlobDetector use in your application via Algorithm interface: +@snippet snippets/core_various.cpp Algorithm +*/ +class CV_EXPORTS_W Algorithm +{ +public: + Algorithm(); + virtual ~Algorithm(); + + /** @brief Clears the algorithm state + */ + CV_WRAP virtual void clear() {} + + /** @brief Stores algorithm parameters in a file storage + */ + virtual void write(FileStorage& fs) const { CV_UNUSED(fs); } + + /** @brief simplified API for language bindings + * @overload + */ + CV_WRAP void write(const Ptr& fs, const String& name = String()) const; + + /** @brief Reads algorithm parameters from a file storage + */ + CV_WRAP virtual void read(const FileNode& fn) { CV_UNUSED(fn); } + + /** @brief Returns true if the Algorithm is empty (e.g. in the very beginning or after unsuccessful read + */ + CV_WRAP virtual bool empty() const { return false; } + + /** @brief Reads algorithm from the file node + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + cv::FileStorage fsRead("example.xml", FileStorage::READ); + Ptr svm = Algorithm::read(fsRead.root()); + @endcode + In order to make this method work, the derived class must overwrite Algorithm::read(const + FileNode& fn) and also have static create() method without parameters + (or with all the optional parameters) + */ + template static Ptr<_Tp> read(const FileNode& fn) + { + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** @brief Loads algorithm from the file + + @param filename Name of the file to read. + @param objname The optional name of the node to read (if empty, the first top-level node will be used) + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + Ptr svm = Algorithm::load("my_svm_model.xml"); + @endcode + In order to make this method work, the derived class must overwrite Algorithm::read(const + FileNode& fn). + */ + template static Ptr<_Tp> load(const String& filename, const String& objname=String()) + { + FileStorage fs(filename, FileStorage::READ); + CV_Assert(fs.isOpened()); + FileNode fn = objname.empty() ? fs.getFirstTopLevelNode() : fs[objname]; + if (fn.empty()) return Ptr<_Tp>(); + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** @brief Loads algorithm from a String + + @param strModel The string variable containing the model you want to load. + @param objname The optional name of the node to read (if empty, the first top-level node will be used) + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + Ptr svm = Algorithm::loadFromString(myStringModel); + @endcode + */ + template static Ptr<_Tp> loadFromString(const String& strModel, const String& objname=String()) + { + FileStorage fs(strModel, FileStorage::READ + FileStorage::MEMORY); + FileNode fn = objname.empty() ? fs.getFirstTopLevelNode() : fs[objname]; + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** Saves the algorithm to a file. + In order to make this method work, the derived class must implement Algorithm::write(FileStorage& fs). */ + CV_WRAP virtual void save(const String& filename) const; + + /** Returns the algorithm string identifier. + This string is used as top level xml/yml node tag when the object is saved to a file or string. */ + CV_WRAP virtual String getDefaultName() const; + +protected: + void writeFormat(FileStorage& fs) const; +}; + +struct Param { + enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6, FLOAT=7, + UNSIGNED_INT=8, UINT64=9, UCHAR=11, SCALAR=12 }; +}; + + + +template<> struct ParamType +{ + typedef bool const_param_type; + typedef bool member_type; + + enum { type = Param::BOOLEAN }; +}; + +template<> struct ParamType +{ + typedef int const_param_type; + typedef int member_type; + + enum { type = Param::INT }; +}; + +template<> struct ParamType +{ + typedef double const_param_type; + typedef double member_type; + + enum { type = Param::REAL }; +}; + +template<> struct ParamType +{ + typedef const String& const_param_type; + typedef String member_type; + + enum { type = Param::STRING }; +}; + +template<> struct ParamType +{ + typedef const Mat& const_param_type; + typedef Mat member_type; + + enum { type = Param::MAT }; +}; + +template<> struct ParamType > +{ + typedef const std::vector& const_param_type; + typedef std::vector member_type; + + enum { type = Param::MAT_VECTOR }; +}; + +template<> struct ParamType +{ + typedef const Ptr& const_param_type; + typedef Ptr member_type; + + enum { type = Param::ALGORITHM }; +}; + +template<> struct ParamType +{ + typedef float const_param_type; + typedef float member_type; + + enum { type = Param::FLOAT }; +}; + +template<> struct ParamType +{ + typedef unsigned const_param_type; + typedef unsigned member_type; + + enum { type = Param::UNSIGNED_INT }; +}; + +template<> struct ParamType +{ + typedef uint64 const_param_type; + typedef uint64 member_type; + + enum { type = Param::UINT64 }; +}; + +template<> struct ParamType +{ + typedef uchar const_param_type; + typedef uchar member_type; + + enum { type = Param::UCHAR }; +}; + +template<> struct ParamType +{ + typedef const Scalar& const_param_type; + typedef Scalar member_type; + + enum { type = Param::SCALAR }; +}; + +//! @} core_basic + +} //namespace cv + +#include "opencv2/core/operations.hpp" +#include "opencv2/core/cvstd.inl.hpp" +#include "opencv2/core/utility.hpp" +#include "opencv2/core/optim.hpp" +#include "opencv2/core/ovx.hpp" + +#endif /*OPENCV_CORE_HPP*/ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/affine.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/affine.hpp new file mode 100644 index 0000000..7e2ed30 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/affine.hpp @@ -0,0 +1,678 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_AFFINE3_HPP +#define OPENCV_CORE_AFFINE3_HPP + +#ifdef __cplusplus + +#include + +namespace cv +{ + +//! @addtogroup core +//! @{ + + /** @brief Affine transform + * + * It represents a 4x4 homogeneous transformation matrix \f$T\f$ + * + * \f[T = + * \begin{bmatrix} + * R & t\\ + * 0 & 1\\ + * \end{bmatrix} + * \f] + * + * where \f$R\f$ is a 3x3 rotation matrix and \f$t\f$ is a 3x1 translation vector. + * + * You can specify \f$R\f$ either by a 3x3 rotation matrix or by a 3x1 rotation vector, + * which is converted to a 3x3 rotation matrix by the Rodrigues formula. + * + * To construct a matrix \f$T\f$ representing first rotation around the axis \f$r\f$ with rotation + * angle \f$|r|\f$ in radian (right hand rule) and then translation by the vector \f$t\f$, you can use + * + * @code + * cv::Vec3f r, t; + * cv::Affine3f T(r, t); + * @endcode + * + * If you already have the rotation matrix \f$R\f$, then you can use + * + * @code + * cv::Matx33f R; + * cv::Affine3f T(R, t); + * @endcode + * + * To extract the rotation matrix \f$R\f$ from \f$T\f$, use + * + * @code + * cv::Matx33f R = T.rotation(); + * @endcode + * + * To extract the translation vector \f$t\f$ from \f$T\f$, use + * + * @code + * cv::Vec3f t = T.translation(); + * @endcode + * + * To extract the rotation vector \f$r\f$ from \f$T\f$, use + * + * @code + * cv::Vec3f r = T.rvec(); + * @endcode + * + * Note that since the mapping from rotation vectors to rotation matrices + * is many to one. The returned rotation vector is not necessarily the one + * you used before to set the matrix. + * + * If you have two transformations \f$T = T_1 * T_2\f$, use + * + * @code + * cv::Affine3f T, T1, T2; + * T = T2.concatenate(T1); + * @endcode + * + * To get the inverse transform of \f$T\f$, use + * + * @code + * cv::Affine3f T, T_inv; + * T_inv = T.inv(); + * @endcode + * + */ + template + class Affine3 + { + public: + typedef T float_type; + typedef Matx Mat3; + typedef Matx Mat4; + typedef Vec Vec3; + + //! Default constructor. It represents a 4x4 identity matrix. + Affine3(); + + //! Augmented affine matrix + Affine3(const Mat4& affine); + + /** + * The resulting 4x4 matrix is + * + * \f[ + * \begin{bmatrix} + * R & t\\ + * 0 & 1\\ + * \end{bmatrix} + * \f] + * + * @param R 3x3 rotation matrix. + * @param t 3x1 translation vector. + */ + Affine3(const Mat3& R, const Vec3& t = Vec3::all(0)); + + /** + * Rodrigues vector. + * + * The last row of the current matrix is set to [0,0,0,1]. + * + * @param rvec 3x1 rotation vector. Its direction indicates the rotation axis and its length + * indicates the rotation angle in radian (using right hand rule). + * @param t 3x1 translation vector. + */ + Affine3(const Vec3& rvec, const Vec3& t = Vec3::all(0)); + + /** + * Combines all constructors above. Supports 4x4, 3x4, 3x3, 1x3, 3x1 sizes of data matrix. + * + * The last row of the current matrix is set to [0,0,0,1] when data is not 4x4. + * + * @param data 1-channel matrix. + * when it is 4x4, it is copied to the current matrix and t is not used. + * When it is 3x4, it is copied to the upper part 3x4 of the current matrix and t is not used. + * When it is 3x3, it is copied to the upper left 3x3 part of the current matrix. + * When it is 3x1 or 1x3, it is treated as a rotation vector and the Rodrigues formula is used + * to compute a 3x3 rotation matrix. + * @param t 3x1 translation vector. It is used only when data is neither 4x4 nor 3x4. + */ + explicit Affine3(const Mat& data, const Vec3& t = Vec3::all(0)); + + //! From 16-element array + explicit Affine3(const float_type* vals); + + //! Create an 4x4 identity transform + static Affine3 Identity(); + + /** + * Rotation matrix. + * + * Copy the rotation matrix to the upper left 3x3 part of the current matrix. + * The remaining elements of the current matrix are not changed. + * + * @param R 3x3 rotation matrix. + * + */ + void rotation(const Mat3& R); + + /** + * Rodrigues vector. + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param rvec 3x1 rotation vector. The direction indicates the rotation axis and + * its length indicates the rotation angle in radian (using the right thumb convention). + */ + void rotation(const Vec3& rvec); + + /** + * Combines rotation methods above. Supports 3x3, 1x3, 3x1 sizes of data matrix. + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param data 1-channel matrix. + * When it is a 3x3 matrix, it sets the upper left 3x3 part of the current matrix. + * When it is a 1x3 or 3x1 matrix, it is used as a rotation vector. The Rodrigues formula + * is used to compute the rotation matrix and sets the upper left 3x3 part of the current matrix. + */ + void rotation(const Mat& data); + + /** + * Copy the 3x3 matrix L to the upper left part of the current matrix + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param L 3x3 matrix. + */ + void linear(const Mat3& L); + + /** + * Copy t to the first three elements of the last column of the current matrix + * + * It sets the upper right 3x1 part of the matrix. The remaining part is unaffected. + * + * @param t 3x1 translation vector. + */ + void translation(const Vec3& t); + + //! @return the upper left 3x3 part + Mat3 rotation() const; + + //! @return the upper left 3x3 part + Mat3 linear() const; + + //! @return the upper right 3x1 part + Vec3 translation() const; + + //! Rodrigues vector. + //! @return a vector representing the upper left 3x3 rotation matrix of the current matrix. + //! @warning Since the mapping between rotation vectors and rotation matrices is many to one, + //! this function returns only one rotation vector that represents the current rotation matrix, + //! which is not necessarily the same one set by `rotation(const Vec3& rvec)`. + Vec3 rvec() const; + + //! @return the inverse of the current matrix. + Affine3 inv(int method = cv::DECOMP_SVD) const; + + //! a.rotate(R) is equivalent to Affine(R, 0) * a; + Affine3 rotate(const Mat3& R) const; + + //! a.rotate(rvec) is equivalent to Affine(rvec, 0) * a; + Affine3 rotate(const Vec3& rvec) const; + + //! a.translate(t) is equivalent to Affine(E, t) * a, where E is an identity matrix + Affine3 translate(const Vec3& t) const; + + //! a.concatenate(affine) is equivalent to affine * a; + Affine3 concatenate(const Affine3& affine) const; + + template operator Affine3() const; + + template Affine3 cast() const; + + Mat4 matrix; + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + Affine3(const Eigen::Transform& affine); + Affine3(const Eigen::Transform& affine); + operator Eigen::Transform() const; + operator Eigen::Transform() const; +#endif + }; + + template static + Affine3 operator*(const Affine3& affine1, const Affine3& affine2); + + //! V is a 3-element vector with member fields x, y and z + template static + V operator*(const Affine3& affine, const V& vector); + + typedef Affine3 Affine3f; + typedef Affine3 Affine3d; + + static Vec3f operator*(const Affine3f& affine, const Vec3f& vector); + static Vec3d operator*(const Affine3d& affine, const Vec3d& vector); + + template class DataType< Affine3<_Tp> > + { + public: + typedef Affine3<_Tp> value_type; + typedef Affine3::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 16, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; + }; + + namespace traits { + template + struct Depth< Affine3<_Tp> > { enum { value = Depth<_Tp>::value }; }; + template + struct Type< Affine3<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 16) }; }; + } // namespace + +//! @} core + +} + +//! @cond IGNORED + +/////////////////////////////////////////////////////////////////////////////////// +// Implementation + +template inline +cv::Affine3::Affine3() + : matrix(Mat4::eye()) +{} + +template inline +cv::Affine3::Affine3(const Mat4& affine) + : matrix(affine) +{} + +template inline +cv::Affine3::Affine3(const Mat3& R, const Vec3& t) +{ + rotation(R); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const Vec3& _rvec, const Vec3& t) +{ + rotation(_rvec); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const cv::Mat& data, const Vec3& t) +{ + CV_Assert(data.type() == cv::traits::Type::value); + CV_Assert(data.channels() == 1); + + if (data.cols == 4 && data.rows == 4) + { + data.copyTo(matrix); + return; + } + else if (data.cols == 4 && data.rows == 3) + { + rotation(data(Rect(0, 0, 3, 3))); + translation(data(Rect(3, 0, 1, 3))); + } + else + { + rotation(data); + translation(t); + } + + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const float_type* vals) : matrix(vals) +{} + +template inline +cv::Affine3 cv::Affine3::Identity() +{ + return Affine3(cv::Affine3::Mat4::eye()); +} + +template inline +void cv::Affine3::rotation(const Mat3& R) +{ + linear(R); +} + +template inline +void cv::Affine3::rotation(const Vec3& _rvec) +{ + double theta = norm(_rvec); + + if (theta < DBL_EPSILON) + rotation(Mat3::eye()); + else + { + double c = std::cos(theta); + double s = std::sin(theta); + double c1 = 1. - c; + double itheta = (theta != 0) ? 1./theta : 0.; + + Point3_ r = _rvec*itheta; + + Mat3 rrt( r.x*r.x, r.x*r.y, r.x*r.z, r.x*r.y, r.y*r.y, r.y*r.z, r.x*r.z, r.y*r.z, r.z*r.z ); + Mat3 r_x( 0, -r.z, r.y, r.z, 0, -r.x, -r.y, r.x, 0 ); + + // R = cos(theta)*I + (1 - cos(theta))*r*rT + sin(theta)*[r_x] + // where [r_x] is [0 -rz ry; rz 0 -rx; -ry rx 0] + Mat3 R = c*Mat3::eye() + c1*rrt + s*r_x; + + rotation(R); + } +} + +//Combines rotation methods above. Supports 3x3, 1x3, 3x1 sizes of data matrix; +template inline +void cv::Affine3::rotation(const cv::Mat& data) +{ + CV_Assert(data.type() == cv::traits::Type::value); + CV_Assert(data.channels() == 1); + + if (data.cols == 3 && data.rows == 3) + { + Mat3 R; + data.copyTo(R); + rotation(R); + } + else if ((data.cols == 3 && data.rows == 1) || (data.cols == 1 && data.rows == 3)) + { + Vec3 _rvec; + data.reshape(1, 3).copyTo(_rvec); + rotation(_rvec); + } + else + CV_Error(Error::StsError, "Input matrix can only be 3x3, 1x3 or 3x1"); +} + +template inline +void cv::Affine3::linear(const Mat3& L) +{ + matrix.val[0] = L.val[0]; matrix.val[1] = L.val[1]; matrix.val[ 2] = L.val[2]; + matrix.val[4] = L.val[3]; matrix.val[5] = L.val[4]; matrix.val[ 6] = L.val[5]; + matrix.val[8] = L.val[6]; matrix.val[9] = L.val[7]; matrix.val[10] = L.val[8]; +} + +template inline +void cv::Affine3::translation(const Vec3& t) +{ + matrix.val[3] = t[0]; matrix.val[7] = t[1]; matrix.val[11] = t[2]; +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::rotation() const +{ + return linear(); +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::linear() const +{ + typename cv::Affine3::Mat3 R; + R.val[0] = matrix.val[0]; R.val[1] = matrix.val[1]; R.val[2] = matrix.val[ 2]; + R.val[3] = matrix.val[4]; R.val[4] = matrix.val[5]; R.val[5] = matrix.val[ 6]; + R.val[6] = matrix.val[8]; R.val[7] = matrix.val[9]; R.val[8] = matrix.val[10]; + return R; +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::translation() const +{ + return Vec3(matrix.val[3], matrix.val[7], matrix.val[11]); +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::rvec() const +{ + cv::Vec3d w; + cv::Matx33d u, vt, R = rotation(); + cv::SVD::compute(R, w, u, vt, cv::SVD::FULL_UV + cv::SVD::MODIFY_A); + R = u * vt; + + double rx = R.val[7] - R.val[5]; + double ry = R.val[2] - R.val[6]; + double rz = R.val[3] - R.val[1]; + + double s = std::sqrt((rx*rx + ry*ry + rz*rz)*0.25); + double c = (R.val[0] + R.val[4] + R.val[8] - 1) * 0.5; + c = c > 1.0 ? 1.0 : c < -1.0 ? -1.0 : c; + double theta = acos(c); + + if( s < 1e-5 ) + { + if( c > 0 ) + rx = ry = rz = 0; + else + { + double t; + t = (R.val[0] + 1) * 0.5; + rx = std::sqrt(std::max(t, 0.0)); + t = (R.val[4] + 1) * 0.5; + ry = std::sqrt(std::max(t, 0.0)) * (R.val[1] < 0 ? -1.0 : 1.0); + t = (R.val[8] + 1) * 0.5; + rz = std::sqrt(std::max(t, 0.0)) * (R.val[2] < 0 ? -1.0 : 1.0); + + if( fabs(rx) < fabs(ry) && fabs(rx) < fabs(rz) && (R.val[5] > 0) != (ry*rz > 0) ) + rz = -rz; + theta /= std::sqrt(rx*rx + ry*ry + rz*rz); + rx *= theta; + ry *= theta; + rz *= theta; + } + } + else + { + double vth = 1/(2*s); + vth *= theta; + rx *= vth; ry *= vth; rz *= vth; + } + + return cv::Vec3d(rx, ry, rz); +} + +template inline +cv::Affine3 cv::Affine3::inv(int method) const +{ + return matrix.inv(method); +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Mat3& R) const +{ + Mat3 Lc = linear(); + Vec3 tc = translation(); + Mat4 result; + result.val[12] = result.val[13] = result.val[14] = 0; + result.val[15] = 1; + + for(int j = 0; j < 3; ++j) + { + for(int i = 0; i < 3; ++i) + { + float_type value = 0; + for(int k = 0; k < 3; ++k) + value += R(j, k) * Lc(k, i); + result(j, i) = value; + } + + result(j, 3) = R.row(j).dot(tc.t()); + } + return result; +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Vec3& _rvec) const +{ + return rotate(Affine3f(_rvec).rotation()); +} + +template inline +cv::Affine3 cv::Affine3::translate(const Vec3& t) const +{ + Mat4 m = matrix; + m.val[ 3] += t[0]; + m.val[ 7] += t[1]; + m.val[11] += t[2]; + return m; +} + +template inline +cv::Affine3 cv::Affine3::concatenate(const Affine3& affine) const +{ + return (*this).rotate(affine.rotation()).translate(affine.translation()); +} + +template template inline +cv::Affine3::operator Affine3() const +{ + return Affine3(matrix); +} + +template template inline +cv::Affine3 cv::Affine3::cast() const +{ + return Affine3(matrix); +} + +template inline +cv::Affine3 cv::operator*(const cv::Affine3& affine1, const cv::Affine3& affine2) +{ + return affine2.concatenate(affine1); +} + +template inline +V cv::operator*(const cv::Affine3& affine, const V& v) +{ + const typename Affine3::Mat4& m = affine.matrix; + + V r; + r.x = m.val[0] * v.x + m.val[1] * v.y + m.val[ 2] * v.z + m.val[ 3]; + r.y = m.val[4] * v.x + m.val[5] * v.y + m.val[ 6] * v.z + m.val[ 7]; + r.z = m.val[8] * v.x + m.val[9] * v.y + m.val[10] * v.z + m.val[11]; + return r; +} + +static inline +cv::Vec3f cv::operator*(const cv::Affine3f& affine, const cv::Vec3f& v) +{ + const cv::Matx44f& m = affine.matrix; + cv::Vec3f r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + +static inline +cv::Vec3d cv::operator*(const cv::Affine3d& affine, const cv::Vec3d& v) +{ + const cv::Matx44d& m = affine.matrix; + cv::Vec3d r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + + + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + cv::Mat(4, 4, cv::traits::Type::value, affine.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + Eigen::Transform a = affine; + cv::Mat(4, 4, cv::traits::Type::value, a.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + Eigen::Transform r; + cv::Mat hdr(4, 4, cv::traits::Type::value, r.matrix().data()); + cv::Mat(matrix, false).copyTo(hdr); + return r; +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + return this->operator Eigen::Transform(); +} + +#endif /* defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H */ + +//! @endcond + +#endif /* __cplusplus */ + +#endif /* OPENCV_CORE_AFFINE3_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/async.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/async.hpp new file mode 100644 index 0000000..54560c7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/async.hpp @@ -0,0 +1,105 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ASYNC_HPP +#define OPENCV_CORE_ASYNC_HPP + +#include + +#ifdef CV_CXX11 +//#include +#include +#endif + +namespace cv { + +/** @addtogroup core_async + +@{ +*/ + + +/** @brief Returns result of asynchronous operations + +Object has attached asynchronous state. +Assignment operator doesn't clone asynchronous state (it is shared between all instances). + +Result can be fetched via get() method only once. + +*/ +class CV_EXPORTS_W AsyncArray +{ +public: + ~AsyncArray() CV_NOEXCEPT; + CV_WRAP AsyncArray() CV_NOEXCEPT; + AsyncArray(const AsyncArray& o) CV_NOEXCEPT; + AsyncArray& operator=(const AsyncArray& o) CV_NOEXCEPT; + CV_WRAP void release() CV_NOEXCEPT; + + /** Fetch the result. + @param[out] dst destination array + + Waits for result until container has valid result. + Throws exception if exception was stored as a result. + + Throws exception on invalid container state. + + @note Result or stored exception can be fetched only once. + */ + CV_WRAP void get(OutputArray dst) const; + + /** Retrieving the result with timeout + @param[out] dst destination array + @param[in] timeoutNs timeout in nanoseconds, -1 for infinite wait + + @returns true if result is ready, false if the timeout has expired + + @note Result or stored exception can be fetched only once. + */ + bool get(OutputArray dst, int64 timeoutNs) const; + + CV_WRAP inline + bool get(OutputArray dst, double timeoutNs) const { return get(dst, (int64)timeoutNs); } + + bool wait_for(int64 timeoutNs) const; + + CV_WRAP inline + bool wait_for(double timeoutNs) const { return wait_for((int64)timeoutNs); } + + CV_WRAP bool valid() const CV_NOEXCEPT; + +#ifdef CV_CXX11 + inline AsyncArray(AsyncArray&& o) { p = o.p; o.p = NULL; } + inline AsyncArray& operator=(AsyncArray&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; } + + template + inline bool get(OutputArray dst, const std::chrono::duration<_Rep, _Period>& timeout) + { + return get(dst, (int64)(std::chrono::nanoseconds(timeout).count())); + } + + template + inline bool wait_for(const std::chrono::duration<_Rep, _Period>& timeout) + { + return wait_for((int64)(std::chrono::nanoseconds(timeout).count())); + } + +#if 0 + std::future getFutureMat() const; + std::future getFutureUMat() const; +#endif +#endif + + + // PImpl + struct Impl; friend struct Impl; + inline void* _getImpl() const CV_NOEXCEPT { return p; } +protected: + Impl* p; +}; + + +//! @} +} // namespace +#endif // OPENCV_CORE_ASYNC_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/base.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/base.hpp new file mode 100644 index 0000000..546140e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/base.hpp @@ -0,0 +1,707 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2014, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_BASE_HPP +#define OPENCV_CORE_BASE_HPP + +#ifndef __cplusplus +# error base.hpp header must be compiled as C++ +#endif + +#include "opencv2/opencv_modules.hpp" + +#include +#include + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +namespace Error { +//! error codes +enum Code { + StsOk= 0, //!< everything is ok + StsBackTrace= -1, //!< pseudo error for back trace + StsError= -2, //!< unknown /unspecified error + StsInternal= -3, //!< internal error (bad state) + StsNoMem= -4, //!< insufficient memory + StsBadArg= -5, //!< function arg/param is bad + StsBadFunc= -6, //!< unsupported function + StsNoConv= -7, //!< iteration didn't converge + StsAutoTrace= -8, //!< tracing + HeaderIsNull= -9, //!< image header is NULL + BadImageSize= -10, //!< image size is invalid + BadOffset= -11, //!< offset is invalid + BadDataPtr= -12, //!< + BadStep= -13, //!< image step is wrong, this may happen for a non-continuous matrix. + BadModelOrChSeq= -14, //!< + BadNumChannels= -15, //!< bad number of channels, for example, some functions accept only single channel matrices. + BadNumChannel1U= -16, //!< + BadDepth= -17, //!< input image depth is not supported by the function + BadAlphaChannel= -18, //!< + BadOrder= -19, //!< number of dimensions is out of range + BadOrigin= -20, //!< incorrect input origin + BadAlign= -21, //!< incorrect input align + BadCallBack= -22, //!< + BadTileSize= -23, //!< + BadCOI= -24, //!< input COI is not supported + BadROISize= -25, //!< incorrect input roi + MaskIsTiled= -26, //!< + StsNullPtr= -27, //!< null pointer + StsVecLengthErr= -28, //!< incorrect vector length + StsFilterStructContentErr= -29, //!< incorrect filter structure content + StsKernelStructContentErr= -30, //!< incorrect transform kernel content + StsFilterOffsetErr= -31, //!< incorrect filter offset value + StsBadSize= -201, //!< the input/output structure size is incorrect + StsDivByZero= -202, //!< division by zero + StsInplaceNotSupported= -203, //!< in-place operation is not supported + StsObjectNotFound= -204, //!< request can't be completed + StsUnmatchedFormats= -205, //!< formats of input/output arrays differ + StsBadFlag= -206, //!< flag is wrong or not supported + StsBadPoint= -207, //!< bad CvPoint + StsBadMask= -208, //!< bad format of mask (neither 8uC1 nor 8sC1) + StsUnmatchedSizes= -209, //!< sizes of input/output structures do not match + StsUnsupportedFormat= -210, //!< the data format/type is not supported by the function + StsOutOfRange= -211, //!< some of parameters are out of range + StsParseError= -212, //!< invalid syntax/structure of the parsed file + StsNotImplemented= -213, //!< the requested function/feature is not implemented + StsBadMemBlock= -214, //!< an allocated block has been corrupted + StsAssert= -215, //!< assertion failed + GpuNotSupported= -216, //!< no CUDA support + GpuApiCallError= -217, //!< GPU API call error + OpenGlNotSupported= -218, //!< no OpenGL support + OpenGlApiCallError= -219, //!< OpenGL API call error + OpenCLApiCallError= -220, //!< OpenCL API call error + OpenCLDoubleNotSupported= -221, + OpenCLInitError= -222, //!< OpenCL initialization error + OpenCLNoAMDBlasFft= -223 +}; +} //Error + +//! @} core_utils + +//! @addtogroup core_array +//! @{ + +//! matrix decomposition types +enum DecompTypes { + /** Gaussian elimination with the optimal pivot element chosen. */ + DECOMP_LU = 0, + /** singular value decomposition (SVD) method; the system can be over-defined and/or the matrix + src1 can be singular */ + DECOMP_SVD = 1, + /** eigenvalue decomposition; the matrix src1 must be symmetrical */ + DECOMP_EIG = 2, + /** Cholesky \f$LL^T\f$ factorization; the matrix src1 must be symmetrical and positively + defined */ + DECOMP_CHOLESKY = 3, + /** QR factorization; the system can be over-defined and/or the matrix src1 can be singular */ + DECOMP_QR = 4, + /** while all the previous flags are mutually exclusive, this flag can be used together with + any of the previous; it means that the normal equations + \f$\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}\f$ are + solved instead of the original system + \f$\texttt{src1}\cdot\texttt{dst}=\texttt{src2}\f$ */ + DECOMP_NORMAL = 16 +}; + +/** norm types + +src1 and src2 denote input arrays. +*/ + +enum NormTypes { + /** + \f[ + norm = \forkthree + {\|\texttt{src1}\|_{L_{\infty}} = \max _I | \texttt{src1} (I)|}{if \(\texttt{normType} = \texttt{NORM_INF}\) } + {\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}} = \max _I | \texttt{src1} (I) - \texttt{src2} (I)|}{if \(\texttt{normType} = \texttt{NORM_INF}\) } + {\frac{\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}} }{\|\texttt{src2}\|_{L_{\infty}} }}{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_INF}\) } + \f] + */ + NORM_INF = 1, + /** + \f[ + norm = \forkthree + {\| \texttt{src1} \| _{L_1} = \sum _I | \texttt{src1} (I)|}{if \(\texttt{normType} = \texttt{NORM_L1}\)} + { \| \texttt{src1} - \texttt{src2} \| _{L_1} = \sum _I | \texttt{src1} (I) - \texttt{src2} (I)|}{if \(\texttt{normType} = \texttt{NORM_L1}\) } + { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_1} }{\|\texttt{src2}\|_{L_1}} }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L1}\) } + \f]*/ + NORM_L1 = 2, + /** + \f[ + norm = \forkthree + { \| \texttt{src1} \| _{L_2} = \sqrt{\sum_I \texttt{src1}(I)^2} }{if \(\texttt{normType} = \texttt{NORM_L2}\) } + { \| \texttt{src1} - \texttt{src2} \| _{L_2} = \sqrt{\sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2} }{if \(\texttt{normType} = \texttt{NORM_L2}\) } + { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}} }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2}\) } + \f] + */ + NORM_L2 = 4, + /** + \f[ + norm = \forkthree + { \| \texttt{src1} \| _{L_2} ^{2} = \sum_I \texttt{src1}(I)^2} {if \(\texttt{normType} = \texttt{NORM_L2SQR}\)} + { \| \texttt{src1} - \texttt{src2} \| _{L_2} ^{2} = \sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2 }{if \(\texttt{normType} = \texttt{NORM_L2SQR}\) } + { \left(\frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}}\right)^2 }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2SQR}\) } + \f] + */ + NORM_L2SQR = 5, + /** + In the case of one input array, calculates the Hamming distance of the array from zero, + In the case of two input arrays, calculates the Hamming distance between the arrays. + */ + NORM_HAMMING = 6, + /** + Similar to NORM_HAMMING, but in the calculation, each two bits of the input sequence will + be added and treated as a single bit to be used in the same calculation as NORM_HAMMING. + */ + NORM_HAMMING2 = 7, + NORM_TYPE_MASK = 7, //!< bit-mask which can be used to separate norm type from norm flags + NORM_RELATIVE = 8, //!< flag + NORM_MINMAX = 32 //!< flag + }; + +//! comparison types +enum CmpTypes { CMP_EQ = 0, //!< src1 is equal to src2. + CMP_GT = 1, //!< src1 is greater than src2. + CMP_GE = 2, //!< src1 is greater than or equal to src2. + CMP_LT = 3, //!< src1 is less than src2. + CMP_LE = 4, //!< src1 is less than or equal to src2. + CMP_NE = 5 //!< src1 is unequal to src2. + }; + +//! generalized matrix multiplication flags +enum GemmFlags { GEMM_1_T = 1, //!< transposes src1 + GEMM_2_T = 2, //!< transposes src2 + GEMM_3_T = 4 //!< transposes src3 + }; + +enum DftFlags { + /** performs an inverse 1D or 2D transform instead of the default forward + transform. */ + DFT_INVERSE = 1, + /** scales the result: divide it by the number of array elements. Normally, it is + combined with DFT_INVERSE. */ + DFT_SCALE = 2, + /** performs a forward or inverse transform of every individual row of the input + matrix; this flag enables you to transform multiple vectors simultaneously and can be used to + decrease the overhead (which is sometimes several times larger than the processing itself) to + perform 3D and higher-dimensional transformations and so forth.*/ + DFT_ROWS = 4, + /** performs a forward transformation of 1D or 2D real array; the result, + though being a complex array, has complex-conjugate symmetry (*CCS*, see the function + description below for details), and such an array can be packed into a real array of the same + size as input, which is the fastest option and which is what the function does by default; + however, you may wish to get a full complex array (for simpler spectrum analysis, and so on) - + pass the flag to enable the function to produce a full-size complex output array. */ + DFT_COMPLEX_OUTPUT = 16, + /** performs an inverse transformation of a 1D or 2D complex array; the + result is normally a complex array of the same size, however, if the input array has + conjugate-complex symmetry (for example, it is a result of forward transformation with + DFT_COMPLEX_OUTPUT flag), the output is a real array; while the function itself does not + check whether the input is symmetrical or not, you can pass the flag and then the function + will assume the symmetry and produce the real output array (note that when the input is packed + into a real array and inverse transformation is executed, the function treats the input as a + packed complex-conjugate symmetrical array, and the output will also be a real array). */ + DFT_REAL_OUTPUT = 32, + /** specifies that input is complex input. If this flag is set, the input must have 2 channels. + On the other hand, for backwards compatibility reason, if input has 2 channels, input is + already considered complex. */ + DFT_COMPLEX_INPUT = 64, + /** performs an inverse 1D or 2D transform instead of the default forward transform. */ + DCT_INVERSE = DFT_INVERSE, + /** performs a forward or inverse transform of every individual row of the input + matrix. This flag enables you to transform multiple vectors simultaneously and can be used to + decrease the overhead (which is sometimes several times larger than the processing itself) to + perform 3D and higher-dimensional transforms and so forth.*/ + DCT_ROWS = DFT_ROWS +}; + +//! Various border types, image boundaries are denoted with `|` +//! @see borderInterpolate, copyMakeBorder +enum BorderTypes { + BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i` + BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh` + BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb` + BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg` + BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba` + BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno` + + BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_ISOLATED = 16 //!< do not look outside of ROI +}; + +//! @} core_array + +//! @addtogroup core_utils +//! @{ + +/*! @brief Signals an error and raises the exception. + +By default the function prints information about the error to stderr, +then it either stops if setBreakOnError() had been called before or raises the exception. +It is possible to alternate error processing by using redirectError(). +@param _code - error code (Error::Code) +@param _err - error description +@param _func - function name. Available only when the compiler supports getting it +@param _file - source file name where the error has occurred +@param _line - line number in the source file where the error has occurred +@see CV_Error, CV_Error_, CV_Assert, CV_DbgAssert + */ +CV_EXPORTS void error(int _code, const String& _err, const char* _func, const char* _file, int _line); + +#ifdef __GNUC__ +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Winvalid-noreturn" +# endif +#endif + +/** same as cv::error, but does not return */ +CV_INLINE CV_NORETURN void errorNoReturn(int _code, const String& _err, const char* _func, const char* _file, int _line) +{ + error(_code, _err, _func, _file, _line); +#ifdef __GNUC__ +# if !defined __clang__ && !defined __APPLE__ + // this suppresses this warning: "noreturn" function does return [enabled by default] + __builtin_trap(); + // or use infinite loop: for (;;) {} +# endif +#endif +} +#ifdef __GNUC__ +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic pop +# endif +#endif + +#ifdef CV_STATIC_ANALYSIS + +// In practice, some macro are not processed correctly (noreturn is not detected). +// We need to use simplified definition for them. +#define CV_Error(code, msg) do { (void)(code); (void)(msg); abort(); } while (0) +#define CV_Error_(code, args) do { (void)(code); (void)(cv::format args); abort(); } while (0) +#define CV_Assert( expr ) do { if (!(expr)) abort(); } while (0) +#define CV_ErrorNoReturn CV_Error +#define CV_ErrorNoReturn_ CV_Error_ + +#else // CV_STATIC_ANALYSIS + +/** @brief Call the error handler. + +Currently, the error handler prints the error code and the error message to the standard +error stream `stderr`. In the Debug configuration, it then provokes memory access violation, so that +the execution stack and all the parameters can be analyzed by the debugger. In the Release +configuration, the exception is thrown. + +@param code one of Error::Code +@param msg error message +*/ +#define CV_Error( code, msg ) cv::error( code, msg, CV_Func, __FILE__, __LINE__ ) + +/** @brief Call the error handler. + +This macro can be used to construct an error message on-fly to include some dynamic information, +for example: +@code + // note the extra parentheses around the formatted text message + CV_Error_(Error::StsOutOfRange, + ("the value at (%d, %d)=%g is out of range", badPt.x, badPt.y, badValue)); +@endcode +@param code one of Error::Code +@param args printf-like formatted error message in parentheses +*/ +#define CV_Error_( code, args ) cv::error( code, cv::format args, CV_Func, __FILE__, __LINE__ ) + +/** @brief Checks a condition at runtime and throws exception if it fails + +The macros CV_Assert (and CV_DbgAssert(expr)) evaluate the specified expression. If it is 0, the macros +raise an error (see cv::error). The macro CV_Assert checks the condition in both Debug and Release +configurations while CV_DbgAssert is only retained in the Debug configuration. +*/ +#define CV_Assert( expr ) do { if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) + +//! @cond IGNORED +#define CV__ErrorNoReturn( code, msg ) cv::errorNoReturn( code, msg, CV_Func, __FILE__, __LINE__ ) +#define CV__ErrorNoReturn_( code, args ) cv::errorNoReturn( code, cv::format args, CV_Func, __FILE__, __LINE__ ) +#ifdef __OPENCV_BUILD +#undef CV_Error +#define CV_Error CV__ErrorNoReturn +#undef CV_Error_ +#define CV_Error_ CV__ErrorNoReturn_ +#undef CV_Assert +#define CV_Assert( expr ) do { if(!!(expr)) ; else cv::errorNoReturn( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) +#else +// backward compatibility +#define CV_ErrorNoReturn CV__ErrorNoReturn +#define CV_ErrorNoReturn_ CV__ErrorNoReturn_ +#endif +//! @endcond + +#endif // CV_STATIC_ANALYSIS + +//! @cond IGNORED + +#if defined OPENCV_FORCE_MULTIARG_ASSERT_CHECK && defined CV_STATIC_ANALYSIS +#warning "OPENCV_FORCE_MULTIARG_ASSERT_CHECK can't be used with CV_STATIC_ANALYSIS" +#undef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#endif + +#ifdef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#define CV_Assert_1( expr ) do { if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) +#else +#define CV_Assert_1 CV_Assert +#endif +#define CV_Assert_2( expr1, expr2 ) CV_Assert_1(expr1); CV_Assert_1(expr2) +#define CV_Assert_3( expr1, expr2, expr3 ) CV_Assert_2(expr1, expr2); CV_Assert_1(expr3) +#define CV_Assert_4( expr1, expr2, expr3, expr4 ) CV_Assert_3(expr1, expr2, expr3); CV_Assert_1(expr4) +#define CV_Assert_5( expr1, expr2, expr3, expr4, expr5 ) CV_Assert_4(expr1, expr2, expr3, expr4); CV_Assert_1(expr5) +#define CV_Assert_6( expr1, expr2, expr3, expr4, expr5, expr6 ) CV_Assert_5(expr1, expr2, expr3, expr4, expr5); CV_Assert_1(expr6) +#define CV_Assert_7( expr1, expr2, expr3, expr4, expr5, expr6, expr7 ) CV_Assert_6(expr1, expr2, expr3, expr4, expr5, expr6 ); CV_Assert_1(expr7) +#define CV_Assert_8( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8 ) CV_Assert_7(expr1, expr2, expr3, expr4, expr5, expr6, expr7 ); CV_Assert_1(expr8) +#define CV_Assert_9( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9 ) CV_Assert_8(expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8 ); CV_Assert_1(expr9) +#define CV_Assert_10( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9, expr10 ) CV_Assert_9(expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9 ); CV_Assert_1(expr10) + +#define CV_Assert_N(...) do { __CV_CAT(CV_Assert_, __CV_VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__); } while(0) + +#ifdef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#undef CV_Assert +#define CV_Assert CV_Assert_N +#endif +//! @endcond + +#if defined _DEBUG || defined CV_STATIC_ANALYSIS +# define CV_DbgAssert(expr) CV_Assert(expr) +#else +/** replaced with CV_Assert(expr) in Debug configuration */ +# define CV_DbgAssert(expr) +#endif + +/* + * Hamming distance functor - counts the bit differences between two strings - useful for the Brief descriptor + * bit count of A exclusive XOR'ed with B + */ +struct CV_EXPORTS Hamming +{ + enum { normType = NORM_HAMMING }; + typedef unsigned char ValueType; + typedef int ResultType; + + /** this will count the bits in a ^ b + */ + ResultType operator()( const unsigned char* a, const unsigned char* b, int size ) const; +}; + +typedef Hamming HammingLUT; + +/////////////////////////////////// inline norms //////////////////////////////////// + +template inline _Tp cv_abs(_Tp x) { return std::abs(x); } +inline int cv_abs(uchar x) { return x; } +inline int cv_abs(schar x) { return std::abs(x); } +inline int cv_abs(ushort x) { return x; } +inline int cv_abs(short x) { return std::abs(x); } + +template static inline +_AccTp normL2Sqr(const _Tp* a, int n) +{ + _AccTp s = 0; + int i=0; +#if CV_ENABLE_UNROLLED + for( ; i <= n - 4; i += 4 ) + { + _AccTp v0 = a[i], v1 = a[i+1], v2 = a[i+2], v3 = a[i+3]; + s += v0*v0 + v1*v1 + v2*v2 + v3*v3; + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = a[i]; + s += v*v; + } + return s; +} + +template static inline +_AccTp normL1(const _Tp* a, int n) +{ + _AccTp s = 0; + int i = 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + s += (_AccTp)cv_abs(a[i]) + (_AccTp)cv_abs(a[i+1]) + + (_AccTp)cv_abs(a[i+2]) + (_AccTp)cv_abs(a[i+3]); + } +#endif + for( ; i < n; i++ ) + s += cv_abs(a[i]); + return s; +} + +template static inline +_AccTp normInf(const _Tp* a, int n) +{ + _AccTp s = 0; + for( int i = 0; i < n; i++ ) + s = std::max(s, (_AccTp)cv_abs(a[i])); + return s; +} + +template static inline +_AccTp normL2Sqr(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + int i= 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + _AccTp v0 = _AccTp(a[i] - b[i]), v1 = _AccTp(a[i+1] - b[i+1]), v2 = _AccTp(a[i+2] - b[i+2]), v3 = _AccTp(a[i+3] - b[i+3]); + s += v0*v0 + v1*v1 + v2*v2 + v3*v3; + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = _AccTp(a[i] - b[i]); + s += v*v; + } + return s; +} + +static inline float normL2Sqr(const float* a, const float* b, int n) +{ + float s = 0.f; + for( int i = 0; i < n; i++ ) + { + float v = a[i] - b[i]; + s += v*v; + } + return s; +} + +template static inline +_AccTp normL1(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + int i= 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + _AccTp v0 = _AccTp(a[i] - b[i]), v1 = _AccTp(a[i+1] - b[i+1]), v2 = _AccTp(a[i+2] - b[i+2]), v3 = _AccTp(a[i+3] - b[i+3]); + s += std::abs(v0) + std::abs(v1) + std::abs(v2) + std::abs(v3); + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = _AccTp(a[i] - b[i]); + s += std::abs(v); + } + return s; +} + +inline float normL1(const float* a, const float* b, int n) +{ + float s = 0.f; + for( int i = 0; i < n; i++ ) + { + s += std::abs(a[i] - b[i]); + } + return s; +} + +inline int normL1(const uchar* a, const uchar* b, int n) +{ + int s = 0; + for( int i = 0; i < n; i++ ) + { + s += std::abs(a[i] - b[i]); + } + return s; +} + +template static inline +_AccTp normInf(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + for( int i = 0; i < n; i++ ) + { + _AccTp v0 = a[i] - b[i]; + s = std::max(s, std::abs(v0)); + } + return s; +} + +/** @brief Computes the cube root of an argument. + + The function cubeRoot computes \f$\sqrt[3]{\texttt{val}}\f$. Negative arguments are handled correctly. + NaN and Inf are not handled. The accuracy approaches the maximum possible accuracy for + single-precision data. + @param val A function argument. + */ +CV_EXPORTS_W float cubeRoot(float val); + +/** @brief Calculates the angle of a 2D vector in degrees. + + The function fastAtan2 calculates the full-range angle of an input 2D vector. The angle is measured + in degrees and varies from 0 to 360 degrees. The accuracy is about 0.3 degrees. + @param x x-coordinate of the vector. + @param y y-coordinate of the vector. + */ +CV_EXPORTS_W float fastAtan2(float y, float x); + +/** proxy for hal::LU */ +CV_EXPORTS int LU(float* A, size_t astep, int m, float* b, size_t bstep, int n); +/** proxy for hal::LU */ +CV_EXPORTS int LU(double* A, size_t astep, int m, double* b, size_t bstep, int n); +/** proxy for hal::Cholesky */ +CV_EXPORTS bool Cholesky(float* A, size_t astep, int m, float* b, size_t bstep, int n); +/** proxy for hal::Cholesky */ +CV_EXPORTS bool Cholesky(double* A, size_t astep, int m, double* b, size_t bstep, int n); + +////////////////// forward declarations for important OpenCV types ////////////////// + +//! @cond IGNORED + +template class Vec; +template class Matx; + +template class Complex; +template class Point_; +template class Point3_; +template class Size_; +template class Rect_; +template class Scalar_; + +class CV_EXPORTS RotatedRect; +class CV_EXPORTS Range; +class CV_EXPORTS TermCriteria; +class CV_EXPORTS KeyPoint; +class CV_EXPORTS DMatch; +class CV_EXPORTS RNG; + +class CV_EXPORTS Mat; +class CV_EXPORTS MatExpr; + +class CV_EXPORTS UMat; + +class CV_EXPORTS SparseMat; +typedef Mat MatND; + +template class Mat_; +template class SparseMat_; + +class CV_EXPORTS MatConstIterator; +class CV_EXPORTS SparseMatIterator; +class CV_EXPORTS SparseMatConstIterator; +template class MatIterator_; +template class MatConstIterator_; +template class SparseMatIterator_; +template class SparseMatConstIterator_; + +namespace ogl +{ + class CV_EXPORTS Buffer; + class CV_EXPORTS Texture2D; + class CV_EXPORTS Arrays; +} + +namespace cuda +{ + class CV_EXPORTS GpuMat; + class CV_EXPORTS HostMem; + class CV_EXPORTS Stream; + class CV_EXPORTS Event; +} + +namespace cudev +{ + template class GpuMat_; +} + +namespace ipp +{ +#if OPENCV_ABI_COMPATIBILITY > 300 +CV_EXPORTS unsigned long long getIppFeatures(); +#else +CV_EXPORTS int getIppFeatures(); +#endif +CV_EXPORTS void setIppStatus(int status, const char * const funcname = NULL, const char * const filename = NULL, + int line = 0); +CV_EXPORTS int getIppStatus(); +CV_EXPORTS String getIppErrorLocation(); +CV_EXPORTS_W bool useIPP(); +CV_EXPORTS_W void setUseIPP(bool flag); +CV_EXPORTS_W String getIppVersion(); + +// IPP Not-Exact mode. This function may force use of IPP then both IPP and OpenCV provide proper results +// but have internal accuracy differences which have too much direct or indirect impact on accuracy tests. +CV_EXPORTS_W bool useIPP_NotExact(); +CV_EXPORTS_W void setUseIPP_NotExact(bool flag); +#if OPENCV_ABI_COMPATIBILITY < 400 +CV_EXPORTS_W bool useIPP_NE(); +CV_EXPORTS_W void setUseIPP_NE(bool flag); +#endif + +} // ipp + +//! @endcond + +//! @} core_utils + + + + +} // cv + +#include "opencv2/core/neon_utils.hpp" +#include "opencv2/core/vsx_utils.hpp" +#include "opencv2/core/check.hpp" + +#endif //OPENCV_CORE_BASE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/bindings_utils.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/bindings_utils.hpp new file mode 100644 index 0000000..f693dc8 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/bindings_utils.hpp @@ -0,0 +1,87 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_BINDINGS_UTILS_HPP +#define OPENCV_CORE_BINDINGS_UTILS_HPP + +#include +#include + +namespace cv { namespace utils { +//! @addtogroup core_utils +//! @{ + +CV_EXPORTS_W String dumpInputArray(InputArray argument); + +CV_EXPORTS_W String dumpInputArrayOfArrays(InputArrayOfArrays argument); + +CV_EXPORTS_W String dumpInputOutputArray(InputOutputArray argument); + +CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argument); + +CV_WRAP static inline +String dumpBool(bool argument) +{ + return (argument) ? String("Bool: True") : String("Bool: False"); +} + +CV_WRAP static inline +String dumpInt(int argument) +{ + return cv::format("Int: %d", argument); +} + +CV_WRAP static inline +String dumpSizeT(size_t argument) +{ + std::ostringstream oss("size_t: ", std::ios::ate); + oss << argument; + return oss.str(); +} + +CV_WRAP static inline +String dumpFloat(float argument) +{ + return cv::format("Float: %.2f", argument); +} + +CV_WRAP static inline +String dumpDouble(double argument) +{ + return cv::format("Double: %.2f", argument); +} + +CV_WRAP static inline +String dumpCString(const char* argument) +{ + return cv::format("String: %s", argument); +} + +CV_WRAP static inline +AsyncArray testAsyncArray(InputArray argument) +{ + AsyncPromise p; + p.setValue(argument); + return p.getArrayResult(); +} + +CV_WRAP static inline +AsyncArray testAsyncException() +{ + AsyncPromise p; + try + { + CV_Error(Error::StsOk, "Test: Generated async error"); + } + catch (const cv::Exception& e) + { + p.setException(e); + } + return p.getArrayResult(); +} + +//! @} +}} // namespace + +#endif // OPENCV_CORE_BINDINGS_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/bufferpool.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/bufferpool.hpp new file mode 100644 index 0000000..4698e5d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/bufferpool.hpp @@ -0,0 +1,40 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved. + +#ifndef OPENCV_CORE_BUFFER_POOL_HPP +#define OPENCV_CORE_BUFFER_POOL_HPP + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4265) +#endif + +namespace cv +{ + +//! @addtogroup core +//! @{ + +class BufferPoolController +{ +protected: + ~BufferPoolController() { } +public: + virtual size_t getReservedSize() const = 0; + virtual size_t getMaxReservedSize() const = 0; + virtual void setMaxReservedSize(size_t size) = 0; + virtual void freeAllReservedBuffers() = 0; +}; + +//! @} + +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // OPENCV_CORE_BUFFER_POOL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/check.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/check.hpp new file mode 100644 index 0000000..0e0c7cb --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/check.hpp @@ -0,0 +1,160 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_CHECK_HPP +#define OPENCV_CORE_CHECK_HPP + +#include + +namespace cv { + +/** Returns string of cv::Mat depth value: CV_8U -> "CV_8U" or "" */ +CV_EXPORTS const char* depthToString(int depth); + +/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or "" */ +CV_EXPORTS const String typeToString(int type); + + +//! @cond IGNORED +namespace detail { + +/** Returns string of cv::Mat depth value: CV_8U -> "CV_8U" or NULL */ +CV_EXPORTS const char* depthToString_(int depth); + +/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or cv::String() */ +CV_EXPORTS const cv::String typeToString_(int type); + +enum TestOp { + TEST_CUSTOM = 0, + TEST_EQ = 1, + TEST_NE = 2, + TEST_LE = 3, + TEST_LT = 4, + TEST_GE = 5, + TEST_GT = 6, + CV__LAST_TEST_OP +}; + +struct CheckContext { + const char* func; + const char* file; + int line; + enum TestOp testOp; + const char* message; + const char* p1_str; + const char* p2_str; +}; + +#ifndef CV__CHECK_FILENAME +# define CV__CHECK_FILENAME __FILE__ +#endif + +#ifndef CV__CHECK_FUNCTION +# if defined _MSC_VER +# define CV__CHECK_FUNCTION __FUNCSIG__ +# elif defined __GNUC__ +# define CV__CHECK_FUNCTION __PRETTY_FUNCTION__ +# else +# define CV__CHECK_FUNCTION "" +# endif +#endif + +#define CV__CHECK_LOCATION_VARNAME(id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_check_, id), __LINE__) +#define CV__DEFINE_CHECK_CONTEXT(id, message, testOp, p1_str, p2_str) \ + static const cv::detail::CheckContext CV__CHECK_LOCATION_VARNAME(id) = \ + { CV__CHECK_FUNCTION, CV__CHECK_FILENAME, __LINE__, testOp, message, p1_str, p2_str } + +CV_EXPORTS void CV_NORETURN check_failed_auto(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v1, const size_t v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const float v1, const float v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const double v1, const double v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v1, const Size_ v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v1, const int v2, const CheckContext& ctx); + +CV_EXPORTS void CV_NORETURN check_failed_auto(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const float v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const double v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const std::string& v1, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v, const CheckContext& ctx); + + +#define CV__TEST_EQ(v1, v2) ((v1) == (v2)) +#define CV__TEST_NE(v1, v2) ((v1) != (v2)) +#define CV__TEST_LE(v1, v2) ((v1) <= (v2)) +#define CV__TEST_LT(v1, v2) ((v1) < (v2)) +#define CV__TEST_GE(v1, v2) ((v1) >= (v2)) +#define CV__TEST_GT(v1, v2) ((v1) > (v2)) + +#define CV__CHECK(id, op, type, v1, v2, v1_str, v2_str, msg_str) do { \ + if(CV__TEST_##op((v1), (v2))) ; else { \ + CV__DEFINE_CHECK_CONTEXT(id, msg_str, cv::detail::TEST_ ## op, v1_str, v2_str); \ + cv::detail::check_failed_ ## type((v1), (v2), CV__CHECK_LOCATION_VARNAME(id)); \ + } \ +} while (0) + +#define CV__CHECK_CUSTOM_TEST(id, type, v, test_expr, v_str, test_expr_str, msg_str) do { \ + if(!!(test_expr)) ; else { \ + CV__DEFINE_CHECK_CONTEXT(id, msg_str, cv::detail::TEST_CUSTOM, v_str, test_expr_str); \ + cv::detail::check_failed_ ## type((v), CV__CHECK_LOCATION_VARNAME(id)); \ + } \ +} while (0) + +} // namespace +//! @endcond + + +/// Supported values of these types: int, float, double +#define CV_CheckEQ(v1, v2, msg) CV__CHECK(_, EQ, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckNE(v1, v2, msg) CV__CHECK(_, NE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckLE(v1, v2, msg) CV__CHECK(_, LE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckLT(v1, v2, msg) CV__CHECK(_, LT, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckGE(v1, v2, msg) CV__CHECK(_, GE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckGT(v1, v2, msg) CV__CHECK(_, GT, auto, v1, v2, #v1, #v2, msg) + +/// Check with additional "decoding" of type values in error message +#define CV_CheckTypeEQ(t1, t2, msg) CV__CHECK(_, EQ, MatType, t1, t2, #t1, #t2, msg) +/// Check with additional "decoding" of depth values in error message +#define CV_CheckDepthEQ(d1, d2, msg) CV__CHECK(_, EQ, MatDepth, d1, d2, #d1, #d2, msg) + +#define CV_CheckChannelsEQ(c1, c2, msg) CV__CHECK(_, EQ, MatChannels, c1, c2, #c1, #c2, msg) + +/// Example: type == CV_8UC1 || type == CV_8UC3 +#define CV_CheckType(t, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, MatType, t, (test_expr), #t, #test_expr, msg) + +/// Example: depth == CV_32F || depth == CV_64F +#define CV_CheckDepth(t, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, MatDepth, t, (test_expr), #t, #test_expr, msg) + +/// Example: v == A || v == B +#define CV_Check(v, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, auto, v, (test_expr), #v, #test_expr, msg) + +/// Some complex conditions: CV_Check(src2, src2.empty() || (src2.type() == src1.type() && src2.size() == src1.size()), "src2 should have same size/type as src1") +// TODO define pretty-printers + +#ifndef NDEBUG +#define CV_DbgCheck(v, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, auto, v, (test_expr), #v, #test_expr, msg) +#define CV_DbgCheckEQ(v1, v2, msg) CV__CHECK(_, EQ, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckNE(v1, v2, msg) CV__CHECK(_, NE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckLE(v1, v2, msg) CV__CHECK(_, LE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckLT(v1, v2, msg) CV__CHECK(_, LT, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckGE(v1, v2, msg) CV__CHECK(_, GE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckGT(v1, v2, msg) CV__CHECK(_, GT, auto, v1, v2, #v1, #v2, msg) +#else +#define CV_DbgCheck(v, test_expr, msg) do { } while (0) +#define CV_DbgCheckEQ(v1, v2, msg) do { } while (0) +#define CV_DbgCheckNE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckLE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckLT(v1, v2, msg) do { } while (0) +#define CV_DbgCheckGE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckGT(v1, v2, msg) do { } while (0) +#endif + +} // namespace + +#endif // OPENCV_CORE_CHECK_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/core.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/core.hpp new file mode 100644 index 0000000..4389183 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/core.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/core.hpp" diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/core_c.h b/hgdriver/3rdparty/opencv/include/opencv2/core/core_c.h new file mode 100644 index 0000000..95a98cf --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/core_c.h @@ -0,0 +1,3175 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + + +#ifndef OPENCV_CORE_C_H +#define OPENCV_CORE_C_H + +#include "opencv2/core/types_c.h" + +#ifdef __cplusplus +# ifdef _MSC_VER +/* disable warning C4190: 'function' has C-linkage specified, but returns UDT 'typename' + which is incompatible with C + + It is OK to disable it because we only extend few plain structures with + C++ constructors for simpler interoperability with C++ API of the library +*/ +# pragma warning(disable:4190) +# elif defined __clang__ && __clang_major__ >= 3 +# pragma GCC diagnostic ignored "-Wreturn-type-c-linkage" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup core_c + @{ +*/ + +/****************************************************************************************\ +* Array allocation, deallocation, initialization and access to elements * +\****************************************************************************************/ + +/** `malloc` wrapper. + If there is no enough memory, the function + (as well as other OpenCV functions that call cvAlloc) + raises an error. */ +CVAPI(void*) cvAlloc( size_t size ); + +/** `free` wrapper. + Here and further all the memory releasing functions + (that all call cvFree) take double pointer in order to + to clear pointer to the data after releasing it. + Passing pointer to NULL pointer is Ok: nothing happens in this case +*/ +CVAPI(void) cvFree_( void* ptr ); +#define cvFree(ptr) (cvFree_(*(ptr)), *(ptr)=0) + +/** @brief Creates an image header but does not allocate the image data. + +@param size Image width and height +@param depth Image depth (see cvCreateImage ) +@param channels Number of channels (see cvCreateImage ) + */ +CVAPI(IplImage*) cvCreateImageHeader( CvSize size, int depth, int channels ); + +/** @brief Initializes an image header that was previously allocated. + +The returned IplImage\* points to the initialized header. +@param image Image header to initialize +@param size Image width and height +@param depth Image depth (see cvCreateImage ) +@param channels Number of channels (see cvCreateImage ) +@param origin Top-left IPL_ORIGIN_TL or bottom-left IPL_ORIGIN_BL +@param align Alignment for image rows, typically 4 or 8 bytes + */ +CVAPI(IplImage*) cvInitImageHeader( IplImage* image, CvSize size, int depth, + int channels, int origin CV_DEFAULT(0), + int align CV_DEFAULT(4)); + +/** @brief Creates an image header and allocates the image data. + +This function call is equivalent to the following code: +@code + header = cvCreateImageHeader(size, depth, channels); + cvCreateData(header); +@endcode +@param size Image width and height +@param depth Bit depth of image elements. See IplImage for valid depths. +@param channels Number of channels per pixel. See IplImage for details. This function only creates +images with interleaved channels. + */ +CVAPI(IplImage*) cvCreateImage( CvSize size, int depth, int channels ); + +/** @brief Deallocates an image header. + +This call is an analogue of : +@code + if(image ) + { + iplDeallocate(*image, IPL_IMAGE_HEADER | IPL_IMAGE_ROI); + *image = 0; + } +@endcode +but it does not use IPL functions by default (see the CV_TURN_ON_IPL_COMPATIBILITY macro). +@param image Double pointer to the image header + */ +CVAPI(void) cvReleaseImageHeader( IplImage** image ); + +/** @brief Deallocates the image header and the image data. + +This call is a shortened form of : +@code + if(*image ) + { + cvReleaseData(*image); + cvReleaseImageHeader(image); + } +@endcode +@param image Double pointer to the image header +*/ +CVAPI(void) cvReleaseImage( IplImage** image ); + +/** Creates a copy of IPL image (widthStep may differ) */ +CVAPI(IplImage*) cvCloneImage( const IplImage* image ); + +/** @brief Sets the channel of interest in an IplImage. + +If the ROI is set to NULL and the coi is *not* 0, the ROI is allocated. Most OpenCV functions do +*not* support the COI setting, so to process an individual image/matrix channel one may copy (via +cvCopy or cvSplit) the channel to a separate image/matrix, process it and then copy the result +back (via cvCopy or cvMerge) if needed. +@param image A pointer to the image header +@param coi The channel of interest. 0 - all channels are selected, 1 - first channel is selected, +etc. Note that the channel indices become 1-based. + */ +CVAPI(void) cvSetImageCOI( IplImage* image, int coi ); + +/** @brief Returns the index of the channel of interest. + +Returns the channel of interest of in an IplImage. Returned values correspond to the coi in +cvSetImageCOI. +@param image A pointer to the image header + */ +CVAPI(int) cvGetImageCOI( const IplImage* image ); + +/** @brief Sets an image Region Of Interest (ROI) for a given rectangle. + +If the original image ROI was NULL and the rect is not the whole image, the ROI structure is +allocated. + +Most OpenCV functions support the use of ROI and treat the image rectangle as a separate image. For +example, all of the pixel coordinates are counted from the top-left (or bottom-left) corner of the +ROI, not the original image. +@param image A pointer to the image header +@param rect The ROI rectangle + */ +CVAPI(void) cvSetImageROI( IplImage* image, CvRect rect ); + +/** @brief Resets the image ROI to include the entire image and releases the ROI structure. + +This produces a similar result to the following, but in addition it releases the ROI structure. : +@code + cvSetImageROI(image, cvRect(0, 0, image->width, image->height )); + cvSetImageCOI(image, 0); +@endcode +@param image A pointer to the image header + */ +CVAPI(void) cvResetImageROI( IplImage* image ); + +/** @brief Returns the image ROI. + +If there is no ROI set, cvRect(0,0,image-\>width,image-\>height) is returned. +@param image A pointer to the image header + */ +CVAPI(CvRect) cvGetImageROI( const IplImage* image ); + +/** @brief Creates a matrix header but does not allocate the matrix data. + +The function allocates a new matrix header and returns a pointer to it. The matrix data can then be +allocated using cvCreateData or set explicitly to user-allocated data via cvSetData. +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type Type of the matrix elements, see cvCreateMat + */ +CVAPI(CvMat*) cvCreateMatHeader( int rows, int cols, int type ); + +#define CV_AUTOSTEP 0x7fffffff + +/** @brief Initializes a pre-allocated matrix header. + +This function is often used to process raw data with OpenCV matrix functions. For example, the +following code computes the matrix product of two matrices, stored as ordinary arrays: +@code + double a[] = { 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 }; + + double b[] = { 1, 5, 9, + 2, 6, 10, + 3, 7, 11, + 4, 8, 12 }; + + double c[9]; + CvMat Ma, Mb, Mc ; + + cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a); + cvInitMatHeader(&Mb, 4, 3, CV_64FC1, b); + cvInitMatHeader(&Mc, 3, 3, CV_64FC1, c); + + cvMatMulAdd(&Ma, &Mb, 0, &Mc); + // the c array now contains the product of a (3x4) and b (4x3) +@endcode +@param mat A pointer to the matrix header to be initialized +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type Type of the matrix elements, see cvCreateMat . +@param data Optional: data pointer assigned to the matrix header +@param step Optional: full row width in bytes of the assigned data. By default, the minimal +possible step is used which assumes there are no gaps between subsequent rows of the matrix. + */ +CVAPI(CvMat*) cvInitMatHeader( CvMat* mat, int rows, int cols, + int type, void* data CV_DEFAULT(NULL), + int step CV_DEFAULT(CV_AUTOSTEP) ); + +/** @brief Creates a matrix header and allocates the matrix data. + +The function call is equivalent to the following code: +@code + CvMat* mat = cvCreateMatHeader(rows, cols, type); + cvCreateData(mat); +@endcode +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type The type of the matrix elements in the form +CV_\\C\ , where S=signed, U=unsigned, F=float. For +example, CV _ 8UC1 means the elements are 8-bit unsigned and the there is 1 channel, and CV _ +32SC2 means the elements are 32-bit signed and there are 2 channels. + */ +CVAPI(CvMat*) cvCreateMat( int rows, int cols, int type ); + +/** @brief Deallocates a matrix. + +The function decrements the matrix data reference counter and deallocates matrix header. If the data +reference counter is 0, it also deallocates the data. : +@code + if(*mat ) + cvDecRefData(*mat); + cvFree((void**)mat); +@endcode +@param mat Double pointer to the matrix + */ +CVAPI(void) cvReleaseMat( CvMat** mat ); + +/** @brief Decrements an array data reference counter. + +The function decrements the data reference counter in a CvMat or CvMatND if the reference counter + +pointer is not NULL. If the counter reaches zero, the data is deallocated. In the current +implementation the reference counter is not NULL only if the data was allocated using the +cvCreateData function. The counter will be NULL in other cases such as: external data was assigned +to the header using cvSetData, header is part of a larger matrix or image, or the header was +converted from an image or n-dimensional matrix header. +@param arr Pointer to an array header + */ +CV_INLINE void cvDecRefData( CvArr* arr ) +{ + if( CV_IS_MAT( arr )) + { + CvMat* mat = (CvMat*)arr; + mat->data.ptr = NULL; + if( mat->refcount != NULL && --*mat->refcount == 0 ) + cvFree( &mat->refcount ); + mat->refcount = NULL; + } + else if( CV_IS_MATND( arr )) + { + CvMatND* mat = (CvMatND*)arr; + mat->data.ptr = NULL; + if( mat->refcount != NULL && --*mat->refcount == 0 ) + cvFree( &mat->refcount ); + mat->refcount = NULL; + } +} + +/** @brief Increments array data reference counter. + +The function increments CvMat or CvMatND data reference counter and returns the new counter value if +the reference counter pointer is not NULL, otherwise it returns zero. +@param arr Array header + */ +CV_INLINE int cvIncRefData( CvArr* arr ) +{ + int refcount = 0; + if( CV_IS_MAT( arr )) + { + CvMat* mat = (CvMat*)arr; + if( mat->refcount != NULL ) + refcount = ++*mat->refcount; + } + else if( CV_IS_MATND( arr )) + { + CvMatND* mat = (CvMatND*)arr; + if( mat->refcount != NULL ) + refcount = ++*mat->refcount; + } + return refcount; +} + + +/** Creates an exact copy of the input matrix (except, may be, step value) */ +CVAPI(CvMat*) cvCloneMat( const CvMat* mat ); + + +/** @brief Returns matrix header corresponding to the rectangular sub-array of input image or matrix. + +The function returns header, corresponding to a specified rectangle of the input array. In other + +words, it allows the user to treat a rectangular part of input array as a stand-alone array. ROI is +taken into account by the function so the sub-array of ROI is actually extracted. +@param arr Input array +@param submat Pointer to the resultant sub-array header +@param rect Zero-based coordinates of the rectangle of interest + */ +CVAPI(CvMat*) cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect ); +#define cvGetSubArr cvGetSubRect + +/** @brief Returns array row or row span. + +The function returns the header, corresponding to a specified row/row span of the input array. +cvGetRow(arr, submat, row) is a shortcut for cvGetRows(arr, submat, row, row+1). +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param start_row Zero-based index of the starting row (inclusive) of the span +@param end_row Zero-based index of the ending row (exclusive) of the span +@param delta_row Index step in the row span. That is, the function extracts every delta_row -th +row from start_row and up to (but not including) end_row . + */ +CVAPI(CvMat*) cvGetRows( const CvArr* arr, CvMat* submat, + int start_row, int end_row, + int delta_row CV_DEFAULT(1)); + +/** @overload +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param row Zero-based index of the selected row +*/ +CV_INLINE CvMat* cvGetRow( const CvArr* arr, CvMat* submat, int row ) +{ + return cvGetRows( arr, submat, row, row + 1, 1 ); +} + + +/** @brief Returns one of more array columns. + +The function returns the header, corresponding to a specified column span of the input array. That + +is, no data is copied. Therefore, any modifications of the submatrix will affect the original array. +If you need to copy the columns, use cvCloneMat. cvGetCol(arr, submat, col) is a shortcut for +cvGetCols(arr, submat, col, col+1). +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param start_col Zero-based index of the starting column (inclusive) of the span +@param end_col Zero-based index of the ending column (exclusive) of the span + */ +CVAPI(CvMat*) cvGetCols( const CvArr* arr, CvMat* submat, + int start_col, int end_col ); + +/** @overload +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param col Zero-based index of the selected column +*/ +CV_INLINE CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col ) +{ + return cvGetCols( arr, submat, col, col + 1 ); +} + +/** @brief Returns one of array diagonals. + +The function returns the header, corresponding to a specified diagonal of the input array. +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param diag Index of the array diagonal. Zero value corresponds to the main diagonal, -1 +corresponds to the diagonal above the main, 1 corresponds to the diagonal below the main, and so +forth. + */ +CVAPI(CvMat*) cvGetDiag( const CvArr* arr, CvMat* submat, + int diag CV_DEFAULT(0)); + +/** low-level scalar <-> raw data conversion functions */ +CVAPI(void) cvScalarToRawData( const CvScalar* scalar, void* data, int type, + int extend_to_12 CV_DEFAULT(0) ); + +CVAPI(void) cvRawDataToScalar( const void* data, int type, CvScalar* scalar ); + +/** @brief Creates a new matrix header but does not allocate the matrix data. + +The function allocates a header for a multi-dimensional dense array. The array data can further be +allocated using cvCreateData or set explicitly to user-allocated data via cvSetData. +@param dims Number of array dimensions +@param sizes Array of dimension sizes +@param type Type of array elements, see cvCreateMat + */ +CVAPI(CvMatND*) cvCreateMatNDHeader( int dims, const int* sizes, int type ); + +/** @brief Creates the header and allocates the data for a multi-dimensional dense array. + +This function call is equivalent to the following code: +@code + CvMatND* mat = cvCreateMatNDHeader(dims, sizes, type); + cvCreateData(mat); +@endcode +@param dims Number of array dimensions. This must not exceed CV_MAX_DIM (32 by default, but can be +changed at build time). +@param sizes Array of dimension sizes. +@param type Type of array elements, see cvCreateMat . + */ +CVAPI(CvMatND*) cvCreateMatND( int dims, const int* sizes, int type ); + +/** @brief Initializes a pre-allocated multi-dimensional array header. + +@param mat A pointer to the array header to be initialized +@param dims The number of array dimensions +@param sizes An array of dimension sizes +@param type Type of array elements, see cvCreateMat +@param data Optional data pointer assigned to the matrix header + */ +CVAPI(CvMatND*) cvInitMatNDHeader( CvMatND* mat, int dims, const int* sizes, + int type, void* data CV_DEFAULT(NULL) ); + +/** @brief Deallocates a multi-dimensional array. + +The function decrements the array data reference counter and releases the array header. If the +reference counter reaches 0, it also deallocates the data. : +@code + if(*mat ) + cvDecRefData(*mat); + cvFree((void**)mat); +@endcode +@param mat Double pointer to the array + */ +CV_INLINE void cvReleaseMatND( CvMatND** mat ) +{ + cvReleaseMat( (CvMat**)mat ); +} + +/** Creates a copy of CvMatND (except, may be, steps) */ +CVAPI(CvMatND*) cvCloneMatND( const CvMatND* mat ); + +/** @brief Creates sparse array. + +The function allocates a multi-dimensional sparse array. Initially the array contain no elements, +that is PtrND and other related functions will return 0 for every index. +@param dims Number of array dimensions. In contrast to the dense matrix, the number of dimensions is +practically unlimited (up to \f$2^{16}\f$ ). +@param sizes Array of dimension sizes +@param type Type of array elements. The same as for CvMat + */ +CVAPI(CvSparseMat*) cvCreateSparseMat( int dims, const int* sizes, int type ); + +/** @brief Deallocates sparse array. + +The function releases the sparse array and clears the array pointer upon exit. +@param mat Double pointer to the array + */ +CVAPI(void) cvReleaseSparseMat( CvSparseMat** mat ); + +/** Creates a copy of CvSparseMat (except, may be, zero items) */ +CVAPI(CvSparseMat*) cvCloneSparseMat( const CvSparseMat* mat ); + +/** @brief Initializes sparse array elements iterator. + +The function initializes iterator of sparse array elements and returns pointer to the first element, +or NULL if the array is empty. +@param mat Input array +@param mat_iterator Initialized iterator + */ +CVAPI(CvSparseNode*) cvInitSparseMatIterator( const CvSparseMat* mat, + CvSparseMatIterator* mat_iterator ); + +/** @brief Returns the next sparse matrix element + +The function moves iterator to the next sparse matrix element and returns pointer to it. In the +current version there is no any particular order of the elements, because they are stored in the +hash table. The sample below demonstrates how to iterate through the sparse matrix: +@code + // print all the non-zero sparse matrix elements and compute their sum + double sum = 0; + int i, dims = cvGetDims(sparsemat); + CvSparseMatIterator it; + CvSparseNode* node = cvInitSparseMatIterator(sparsemat, &it); + + for(; node != 0; node = cvGetNextSparseNode(&it)) + { + int* idx = CV_NODE_IDX(array, node); + float val = *(float*)CV_NODE_VAL(array, node); + printf("M"); + for(i = 0; i < dims; i++ ) + printf("[%d]", idx[i]); + printf("=%g\n", val); + + sum += val; + } + + printf("nTotal sum = %g\n", sum); +@endcode +@param mat_iterator Sparse array iterator + */ +CV_INLINE CvSparseNode* cvGetNextSparseNode( CvSparseMatIterator* mat_iterator ) +{ + if( mat_iterator->node->next ) + return mat_iterator->node = mat_iterator->node->next; + else + { + int idx; + for( idx = ++mat_iterator->curidx; idx < mat_iterator->mat->hashsize; idx++ ) + { + CvSparseNode* node = (CvSparseNode*)mat_iterator->mat->hashtable[idx]; + if( node ) + { + mat_iterator->curidx = idx; + return mat_iterator->node = node; + } + } + return NULL; + } +} + + +#define CV_MAX_ARR 10 + +/** matrix iterator: used for n-ary operations on dense arrays */ +typedef struct CvNArrayIterator +{ + int count; /**< number of arrays */ + int dims; /**< number of dimensions to iterate */ + CvSize size; /**< maximal common linear size: { width = size, height = 1 } */ + uchar* ptr[CV_MAX_ARR]; /**< pointers to the array slices */ + int stack[CV_MAX_DIM]; /**< for internal use */ + CvMatND* hdr[CV_MAX_ARR]; /**< pointers to the headers of the + matrices that are processed */ +} +CvNArrayIterator; + +#define CV_NO_DEPTH_CHECK 1 +#define CV_NO_CN_CHECK 2 +#define CV_NO_SIZE_CHECK 4 + +/** initializes iterator that traverses through several arrays simultaneously + (the function together with cvNextArraySlice is used for + N-ari element-wise operations) */ +CVAPI(int) cvInitNArrayIterator( int count, CvArr** arrs, + const CvArr* mask, CvMatND* stubs, + CvNArrayIterator* array_iterator, + int flags CV_DEFAULT(0) ); + +/** returns zero value if iteration is finished, non-zero (slice length) otherwise */ +CVAPI(int) cvNextNArraySlice( CvNArrayIterator* array_iterator ); + + +/** @brief Returns type of array elements. + +The function returns type of the array elements. In the case of IplImage the type is converted to +CvMat-like representation. For example, if the image has been created as: +@code + IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3); +@endcode +The code cvGetElemType(img) will return CV_8UC3. +@param arr Input array + */ +CVAPI(int) cvGetElemType( const CvArr* arr ); + +/** @brief Return number of array dimensions + +The function returns the array dimensionality and the array of dimension sizes. In the case of +IplImage or CvMat it always returns 2 regardless of number of image/matrix rows. For example, the +following code calculates total number of array elements: +@code + int sizes[CV_MAX_DIM]; + int i, total = 1; + int dims = cvGetDims(arr, size); + for(i = 0; i < dims; i++ ) + total *= sizes[i]; +@endcode +@param arr Input array +@param sizes Optional output vector of the array dimension sizes. For 2d arrays the number of rows +(height) goes first, number of columns (width) next. + */ +CVAPI(int) cvGetDims( const CvArr* arr, int* sizes CV_DEFAULT(NULL) ); + + +/** @brief Returns array size along the specified dimension. + +@param arr Input array +@param index Zero-based dimension index (for matrices 0 means number of rows, 1 means number of +columns; for images 0 means height, 1 means width) + */ +CVAPI(int) cvGetDimSize( const CvArr* arr, int index ); + + +/** @brief Return pointer to a particular array element. + +The functions return a pointer to a specific array element. Number of array dimension should match +to the number of indices passed to the function except for cvPtr1D function that can be used for +sequential access to 1D, 2D or nD dense arrays. + +The functions can be used for sparse arrays as well - if the requested node does not exist they +create it and set it to zero. + +All these as well as other functions accessing array elements ( cvGetND , cvGetRealND , cvSet +, cvSetND , cvSetRealND ) raise an error in case if the element index is out of range. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param type Optional output parameter: type of matrix elements + */ +CVAPI(uchar*) cvPtr1D( const CvArr* arr, int idx0, int* type CV_DEFAULT(NULL)); +/** @overload */ +CVAPI(uchar*) cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type CV_DEFAULT(NULL) ); +/** @overload */ +CVAPI(uchar*) cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, + int* type CV_DEFAULT(NULL)); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param type Optional output parameter: type of matrix elements +@param create_node Optional input parameter for sparse matrices. Non-zero value of the parameter +means that the requested element is created if it does not exist already. +@param precalc_hashval Optional input parameter for sparse matrices. If the pointer is not NULL, +the function does not recalculate the node hash value, but takes it from the specified location. +It is useful for speeding up pair-wise operations (TODO: provide an example) +*/ +CVAPI(uchar*) cvPtrND( const CvArr* arr, const int* idx, int* type CV_DEFAULT(NULL), + int create_node CV_DEFAULT(1), + unsigned* precalc_hashval CV_DEFAULT(NULL)); + +/** @brief Return a specific array element. + +The functions return a specific array element. In the case of a sparse array the functions return 0 +if the requested node does not exist (no new node is created by the functions). +@param arr Input array +@param idx0 The first zero-based component of the element index + */ +CVAPI(CvScalar) cvGet1D( const CvArr* arr, int idx0 ); +/** @overload */ +CVAPI(CvScalar) cvGet2D( const CvArr* arr, int idx0, int idx1 ); +/** @overload */ +CVAPI(CvScalar) cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); +/** @overload +@param arr Input array +@param idx Array of the element indices +*/ +CVAPI(CvScalar) cvGetND( const CvArr* arr, const int* idx ); + +/** @brief Return a specific element of single-channel 1D, 2D, 3D or nD array. + +Returns a specific element of a single-channel array. If the array has multiple channels, a runtime +error is raised. Note that Get?D functions can be used safely for both single-channel and +multiple-channel arrays though they are a bit slower. + +In the case of a sparse array the functions return 0 if the requested node does not exist (no new +node is created by the functions). +@param arr Input array. Must have a single channel. +@param idx0 The first zero-based component of the element index + */ +CVAPI(double) cvGetReal1D( const CvArr* arr, int idx0 ); +/** @overload */ +CVAPI(double) cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); +/** @overload */ +CVAPI(double) cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); +/** @overload +@param arr Input array. Must have a single channel. +@param idx Array of the element indices +*/ +CVAPI(double) cvGetRealND( const CvArr* arr, const int* idx ); + +/** @brief Change the particular array element. + +The functions assign the new value to a particular array element. In the case of a sparse array the +functions create the node if it does not exist yet. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param value The assigned value + */ +CVAPI(void) cvSet1D( CvArr* arr, int idx0, CvScalar value ); +/** @overload */ +CVAPI(void) cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); +/** @overload */ +CVAPI(void) cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value ); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param value The assigned value +*/ +CVAPI(void) cvSetND( CvArr* arr, const int* idx, CvScalar value ); + +/** @brief Change a specific array element. + +The functions assign a new value to a specific element of a single-channel array. If the array has +multiple channels, a runtime error is raised. Note that the Set\*D function can be used safely for +both single-channel and multiple-channel arrays, though they are a bit slower. + +In the case of a sparse array the functions create the node if it does not yet exist. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param value The assigned value + */ +CVAPI(void) cvSetReal1D( CvArr* arr, int idx0, double value ); +/** @overload */ +CVAPI(void) cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); +/** @overload */ +CVAPI(void) cvSetReal3D( CvArr* arr, int idx0, + int idx1, int idx2, double value ); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param value The assigned value +*/ +CVAPI(void) cvSetRealND( CvArr* arr, const int* idx, double value ); + +/** clears element of ND dense array, + in case of sparse arrays it deletes the specified node */ +CVAPI(void) cvClearND( CvArr* arr, const int* idx ); + +/** @brief Returns matrix header for arbitrary array. + +The function returns a matrix header for the input array that can be a matrix - CvMat, an image - +IplImage, or a multi-dimensional dense array - CvMatND (the third option is allowed only if +allowND != 0) . In the case of matrix the function simply returns the input pointer. In the case of +IplImage\* or CvMatND it initializes the header structure with parameters of the current image ROI +and returns &header. Because COI is not supported by CvMat, it is returned separately. + +The function provides an easy way to handle both types of arrays - IplImage and CvMat using the same +code. Input array must have non-zero data pointer, otherwise the function will report an error. + +@note If the input array is IplImage with planar data layout and COI set, the function returns the +pointer to the selected plane and COI == 0. This feature allows user to process IplImage structures +with planar data layout, even though OpenCV does not support such images. +@param arr Input array +@param header Pointer to CvMat structure used as a temporary buffer +@param coi Optional output parameter for storing COI +@param allowND If non-zero, the function accepts multi-dimensional dense arrays (CvMatND\*) and +returns 2D matrix (if CvMatND has two dimensions) or 1D matrix (when CvMatND has 1 dimension or +more than 2 dimensions). The CvMatND array must be continuous. +@sa cvGetImage, cvarrToMat. + */ +CVAPI(CvMat*) cvGetMat( const CvArr* arr, CvMat* header, + int* coi CV_DEFAULT(NULL), + int allowND CV_DEFAULT(0)); + +/** @brief Returns image header for arbitrary array. + +The function returns the image header for the input array that can be a matrix (CvMat) or image +(IplImage). In the case of an image the function simply returns the input pointer. In the case of +CvMat it initializes an image_header structure with the parameters of the input matrix. Note that +if we transform IplImage to CvMat using cvGetMat and then transform CvMat back to IplImage using +this function, we will get different headers if the ROI is set in the original image. +@param arr Input array +@param image_header Pointer to IplImage structure used as a temporary buffer + */ +CVAPI(IplImage*) cvGetImage( const CvArr* arr, IplImage* image_header ); + + +/** @brief Changes the shape of a multi-dimensional array without copying the data. + +The function is an advanced version of cvReshape that can work with multi-dimensional arrays as +well (though it can work with ordinary images and matrices) and change the number of dimensions. + +Below are the two samples from the cvReshape description rewritten using cvReshapeMatND: +@code + IplImage* color_img = cvCreateImage(cvSize(320,240), IPL_DEPTH_8U, 3); + IplImage gray_img_hdr, *gray_img; + gray_img = (IplImage*)cvReshapeMatND(color_img, sizeof(gray_img_hdr), &gray_img_hdr, 1, 0, 0); + ... + int size[] = { 2, 2, 2 }; + CvMatND* mat = cvCreateMatND(3, size, CV_32F); + CvMat row_header, *row; + row = (CvMat*)cvReshapeMatND(mat, sizeof(row_header), &row_header, 0, 1, 0); +@endcode +In C, the header file for this function includes a convenient macro cvReshapeND that does away with +the sizeof_header parameter. So, the lines containing the call to cvReshapeMatND in the examples +may be replaced as follow: +@code + gray_img = (IplImage*)cvReshapeND(color_img, &gray_img_hdr, 1, 0, 0); + ... + row = (CvMat*)cvReshapeND(mat, &row_header, 0, 1, 0); +@endcode +@param arr Input array +@param sizeof_header Size of output header to distinguish between IplImage, CvMat and CvMatND +output headers +@param header Output header to be filled +@param new_cn New number of channels. new_cn = 0 means that the number of channels remains +unchanged. +@param new_dims New number of dimensions. new_dims = 0 means that the number of dimensions +remains the same. +@param new_sizes Array of new dimension sizes. Only new_dims-1 values are used, because the +total number of elements must remain the same. Thus, if new_dims = 1, new_sizes array is not +used. + */ +CVAPI(CvArr*) cvReshapeMatND( const CvArr* arr, + int sizeof_header, CvArr* header, + int new_cn, int new_dims, int* new_sizes ); + +#define cvReshapeND( arr, header, new_cn, new_dims, new_sizes ) \ + cvReshapeMatND( (arr), sizeof(*(header)), (header), \ + (new_cn), (new_dims), (new_sizes)) + +/** @brief Changes shape of matrix/image without copying data. + +The function initializes the CvMat header so that it points to the same data as the original array +but has a different shape - different number of channels, different number of rows, or both. + +The following example code creates one image buffer and two image headers, the first is for a +320x240x3 image and the second is for a 960x240x1 image: +@code + IplImage* color_img = cvCreateImage(cvSize(320,240), IPL_DEPTH_8U, 3); + CvMat gray_mat_hdr; + IplImage gray_img_hdr, *gray_img; + cvReshape(color_img, &gray_mat_hdr, 1); + gray_img = cvGetImage(&gray_mat_hdr, &gray_img_hdr); +@endcode +And the next example converts a 3x3 matrix to a single 1x9 vector: +@code + CvMat* mat = cvCreateMat(3, 3, CV_32F); + CvMat row_header, *row; + row = cvReshape(mat, &row_header, 0, 1); +@endcode +@param arr Input array +@param header Output header to be filled +@param new_cn New number of channels. 'new_cn = 0' means that the number of channels remains +unchanged. +@param new_rows New number of rows. 'new_rows = 0' means that the number of rows remains +unchanged unless it needs to be changed according to new_cn value. +*/ +CVAPI(CvMat*) cvReshape( const CvArr* arr, CvMat* header, + int new_cn, int new_rows CV_DEFAULT(0) ); + +/** Repeats source 2d array several times in both horizontal and + vertical direction to fill destination array */ +CVAPI(void) cvRepeat( const CvArr* src, CvArr* dst ); + +/** @brief Allocates array data + +The function allocates image, matrix or multi-dimensional dense array data. Note that in the case of +matrix types OpenCV allocation functions are used. In the case of IplImage they are used unless +CV_TURN_ON_IPL_COMPATIBILITY() has been called before. In the latter case IPL functions are used +to allocate the data. +@param arr Array header + */ +CVAPI(void) cvCreateData( CvArr* arr ); + +/** @brief Releases array data. + +The function releases the array data. In the case of CvMat or CvMatND it simply calls +cvDecRefData(), that is the function can not deallocate external data. See also the note to +cvCreateData . +@param arr Array header + */ +CVAPI(void) cvReleaseData( CvArr* arr ); + +/** @brief Assigns user data to the array header. + +The function assigns user data to the array header. Header should be initialized before using +cvCreateMatHeader, cvCreateImageHeader, cvCreateMatNDHeader, cvInitMatHeader, +cvInitImageHeader or cvInitMatNDHeader. +@param arr Array header +@param data User data +@param step Full row length in bytes + */ +CVAPI(void) cvSetData( CvArr* arr, void* data, int step ); + +/** @brief Retrieves low-level information about the array. + +The function fills output variables with low-level information about the array data. All output + +parameters are optional, so some of the pointers may be set to NULL. If the array is IplImage with +ROI set, the parameters of ROI are returned. + +The following example shows how to get access to array elements. It computes absolute values of the +array elements : +@code + float* data; + int step; + CvSize size; + + cvGetRawData(array, (uchar**)&data, &step, &size); + step /= sizeof(data[0]); + + for(int y = 0; y < size.height; y++, data += step ) + for(int x = 0; x < size.width; x++ ) + data[x] = (float)fabs(data[x]); +@endcode +@param arr Array header +@param data Output pointer to the whole image origin or ROI origin if ROI is set +@param step Output full row length in bytes +@param roi_size Output ROI size + */ +CVAPI(void) cvGetRawData( const CvArr* arr, uchar** data, + int* step CV_DEFAULT(NULL), + CvSize* roi_size CV_DEFAULT(NULL)); + +/** @brief Returns size of matrix or image ROI. + +The function returns number of rows (CvSize::height) and number of columns (CvSize::width) of the +input matrix or image. In the case of image the size of ROI is returned. +@param arr array header + */ +CVAPI(CvSize) cvGetSize( const CvArr* arr ); + +/** @brief Copies one array to another. + +The function copies selected elements from an input array to an output array: + +\f[\texttt{dst} (I)= \texttt{src} (I) \quad \text{if} \quad \texttt{mask} (I) \ne 0.\f] + +If any of the passed arrays is of IplImage type, then its ROI and COI fields are used. Both arrays +must have the same type, the same number of dimensions, and the same size. The function can also +copy sparse arrays (mask is not supported in this case). +@param src The source array +@param dst The destination array +@param mask Operation mask, 8-bit single channel array; specifies elements of the destination array +to be changed + */ +CVAPI(void) cvCopy( const CvArr* src, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Sets every element of an array to a given value. + +The function copies the scalar value to every selected element of the destination array: +\f[\texttt{arr} (I)= \texttt{value} \quad \text{if} \quad \texttt{mask} (I) \ne 0\f] +If array arr is of IplImage type, then is ROI used, but COI must not be set. +@param arr The destination array +@param value Fill value +@param mask Operation mask, 8-bit single channel array; specifies elements of the destination +array to be changed + */ +CVAPI(void) cvSet( CvArr* arr, CvScalar value, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Clears the array. + +The function clears the array. In the case of dense arrays (CvMat, CvMatND or IplImage), +cvZero(array) is equivalent to cvSet(array,cvScalarAll(0),0). In the case of sparse arrays all the +elements are removed. +@param arr Array to be cleared + */ +CVAPI(void) cvSetZero( CvArr* arr ); +#define cvZero cvSetZero + + +/** Splits a multi-channel array into the set of single-channel arrays or + extracts particular [color] plane */ +CVAPI(void) cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1, + CvArr* dst2, CvArr* dst3 ); + +/** Merges a set of single-channel arrays into the single multi-channel array + or inserts one particular [color] plane to the array */ +CVAPI(void) cvMerge( const CvArr* src0, const CvArr* src1, + const CvArr* src2, const CvArr* src3, + CvArr* dst ); + +/** Copies several channels from input arrays to + certain channels of output arrays */ +CVAPI(void) cvMixChannels( const CvArr** src, int src_count, + CvArr** dst, int dst_count, + const int* from_to, int pair_count ); + +/** @brief Converts one array to another with optional linear transformation. + +The function has several different purposes, and thus has several different names. It copies one +array to another with optional scaling, which is performed first, and/or optional type conversion, +performed after: + +\f[\texttt{dst} (I) = \texttt{scale} \texttt{src} (I) + ( \texttt{shift} _0, \texttt{shift} _1,...)\f] + +All the channels of multi-channel arrays are processed independently. + +The type of conversion is done with rounding and saturation, that is if the result of scaling + +conversion can not be represented exactly by a value of the destination array element type, it is +set to the nearest representable value on the real axis. +@param src Source array +@param dst Destination array +@param scale Scale factor +@param shift Value added to the scaled source array elements + */ +CVAPI(void) cvConvertScale( const CvArr* src, CvArr* dst, + double scale CV_DEFAULT(1), + double shift CV_DEFAULT(0) ); +#define cvCvtScale cvConvertScale +#define cvScale cvConvertScale +#define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 ) + + +/** Performs linear transformation on every source array element, + stores absolute value of the result: + dst(x,y,c) = abs(scale*src(x,y,c)+shift). + destination array must have 8u type. + In other cases one may use cvConvertScale + cvAbsDiffS */ +CVAPI(void) cvConvertScaleAbs( const CvArr* src, CvArr* dst, + double scale CV_DEFAULT(1), + double shift CV_DEFAULT(0) ); +#define cvCvtScaleAbs cvConvertScaleAbs + + +/** checks termination criteria validity and + sets eps to default_eps (if it is not set), + max_iter to default_max_iters (if it is not set) +*/ +CVAPI(CvTermCriteria) cvCheckTermCriteria( CvTermCriteria criteria, + double default_eps, + int default_max_iters ); + +/****************************************************************************************\ +* Arithmetic, logic and comparison operations * +\****************************************************************************************/ + +/** dst(mask) = src1(mask) + src2(mask) */ +CVAPI(void) cvAdd( const CvArr* src1, const CvArr* src2, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src(mask) + value */ +CVAPI(void) cvAddS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src1(mask) - src2(mask) */ +CVAPI(void) cvSub( const CvArr* src1, const CvArr* src2, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src(mask) - value = src(mask) + (-value) */ +CV_INLINE void cvSubS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)) +{ + cvAddS( src, cvScalar( -value.val[0], -value.val[1], -value.val[2], -value.val[3]), + dst, mask ); +} + +/** dst(mask) = value - src(mask) */ +CVAPI(void) cvSubRS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) * src2(idx) * scale + (scaled element-wise multiplication of 2 arrays) */ +CVAPI(void) cvMul( const CvArr* src1, const CvArr* src2, + CvArr* dst, double scale CV_DEFAULT(1) ); + +/** element-wise division/inversion with scaling: + dst(idx) = src1(idx) * scale / src2(idx) + or dst(idx) = scale / src2(idx) if src1 == 0 */ +CVAPI(void) cvDiv( const CvArr* src1, const CvArr* src2, + CvArr* dst, double scale CV_DEFAULT(1)); + +/** dst = src1 * scale + src2 */ +CVAPI(void) cvScaleAdd( const CvArr* src1, CvScalar scale, + const CvArr* src2, CvArr* dst ); +#define cvAXPY( A, real_scalar, B, C ) cvScaleAdd(A, cvRealScalar(real_scalar), B, C) + +/** dst = src1 * alpha + src2 * beta + gamma */ +CVAPI(void) cvAddWeighted( const CvArr* src1, double alpha, + const CvArr* src2, double beta, + double gamma, CvArr* dst ); + +/** @brief Calculates the dot product of two arrays in Euclidean metrics. + +The function calculates and returns the Euclidean dot product of two arrays. + +\f[src1 \bullet src2 = \sum _I ( \texttt{src1} (I) \texttt{src2} (I))\f] + +In the case of multiple channel arrays, the results for all channels are accumulated. In particular, +cvDotProduct(a,a) where a is a complex vector, will return \f$||\texttt{a}||^2\f$. The function can +process multi-dimensional arrays, row by row, layer by layer, and so on. +@param src1 The first source array +@param src2 The second source array + */ +CVAPI(double) cvDotProduct( const CvArr* src1, const CvArr* src2 ); + +/** dst(idx) = src1(idx) & src2(idx) */ +CVAPI(void) cvAnd( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) & value */ +CVAPI(void) cvAndS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) | src2(idx) */ +CVAPI(void) cvOr( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) | value */ +CVAPI(void) cvOrS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) ^ src2(idx) */ +CVAPI(void) cvXor( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) ^ value */ +CVAPI(void) cvXorS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = ~src(idx) */ +CVAPI(void) cvNot( const CvArr* src, CvArr* dst ); + +/** dst(idx) = lower(idx) <= src(idx) < upper(idx) */ +CVAPI(void) cvInRange( const CvArr* src, const CvArr* lower, + const CvArr* upper, CvArr* dst ); + +/** dst(idx) = lower <= src(idx) < upper */ +CVAPI(void) cvInRangeS( const CvArr* src, CvScalar lower, + CvScalar upper, CvArr* dst ); + +#define CV_CMP_EQ 0 +#define CV_CMP_GT 1 +#define CV_CMP_GE 2 +#define CV_CMP_LT 3 +#define CV_CMP_LE 4 +#define CV_CMP_NE 5 + +/** The comparison operation support single-channel arrays only. + Destination image should be 8uC1 or 8sC1 */ + +/** dst(idx) = src1(idx) _cmp_op_ src2(idx) */ +CVAPI(void) cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op ); + +/** dst(idx) = src1(idx) _cmp_op_ value */ +CVAPI(void) cvCmpS( const CvArr* src, double value, CvArr* dst, int cmp_op ); + +/** dst(idx) = min(src1(idx),src2(idx)) */ +CVAPI(void) cvMin( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(idx) = max(src1(idx),src2(idx)) */ +CVAPI(void) cvMax( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(idx) = min(src(idx),value) */ +CVAPI(void) cvMinS( const CvArr* src, double value, CvArr* dst ); + +/** dst(idx) = max(src(idx),value) */ +CVAPI(void) cvMaxS( const CvArr* src, double value, CvArr* dst ); + +/** dst(x,y,c) = abs(src1(x,y,c) - src2(x,y,c)) */ +CVAPI(void) cvAbsDiff( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(x,y,c) = abs(src(x,y,c) - value(c)) */ +CVAPI(void) cvAbsDiffS( const CvArr* src, CvArr* dst, CvScalar value ); +#define cvAbs( src, dst ) cvAbsDiffS( (src), (dst), cvScalarAll(0)) + +/****************************************************************************************\ +* Math operations * +\****************************************************************************************/ + +/** Does cartesian->polar coordinates conversion. + Either of output components (magnitude or angle) is optional */ +CVAPI(void) cvCartToPolar( const CvArr* x, const CvArr* y, + CvArr* magnitude, CvArr* angle CV_DEFAULT(NULL), + int angle_in_degrees CV_DEFAULT(0)); + +/** Does polar->cartesian coordinates conversion. + Either of output components (magnitude or angle) is optional. + If magnitude is missing it is assumed to be all 1's */ +CVAPI(void) cvPolarToCart( const CvArr* magnitude, const CvArr* angle, + CvArr* x, CvArr* y, + int angle_in_degrees CV_DEFAULT(0)); + +/** Does powering: dst(idx) = src(idx)^power */ +CVAPI(void) cvPow( const CvArr* src, CvArr* dst, double power ); + +/** Does exponention: dst(idx) = exp(src(idx)). + Overflow is not handled yet. Underflow is handled. + Maximal relative error is ~7e-6 for single-precision input */ +CVAPI(void) cvExp( const CvArr* src, CvArr* dst ); + +/** Calculates natural logarithms: dst(idx) = log(abs(src(idx))). + Logarithm of 0 gives large negative number(~-700) + Maximal relative error is ~3e-7 for single-precision output +*/ +CVAPI(void) cvLog( const CvArr* src, CvArr* dst ); + +/** Fast arctangent calculation */ +CVAPI(float) cvFastArctan( float y, float x ); + +/** Fast cubic root calculation */ +CVAPI(float) cvCbrt( float value ); + +#define CV_CHECK_RANGE 1 +#define CV_CHECK_QUIET 2 +/** Checks array values for NaNs, Infs or simply for too large numbers + (if CV_CHECK_RANGE is set). If CV_CHECK_QUIET is set, + no runtime errors is raised (function returns zero value in case of "bad" values). + Otherwise cvError is called */ +CVAPI(int) cvCheckArr( const CvArr* arr, int flags CV_DEFAULT(0), + double min_val CV_DEFAULT(0), double max_val CV_DEFAULT(0)); +#define cvCheckArray cvCheckArr + +#define CV_RAND_UNI 0 +#define CV_RAND_NORMAL 1 + +/** @brief Fills an array with random numbers and updates the RNG state. + +The function fills the destination array with uniformly or normally distributed random numbers. +@param rng CvRNG state initialized by cvRNG +@param arr The destination array +@param dist_type Distribution type +> - **CV_RAND_UNI** uniform distribution +> - **CV_RAND_NORMAL** normal or Gaussian distribution +@param param1 The first parameter of the distribution. In the case of a uniform distribution it is +the inclusive lower boundary of the random numbers range. In the case of a normal distribution it +is the mean value of the random numbers. +@param param2 The second parameter of the distribution. In the case of a uniform distribution it +is the exclusive upper boundary of the random numbers range. In the case of a normal distribution +it is the standard deviation of the random numbers. +@sa randu, randn, RNG::fill. + */ +CVAPI(void) cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, + CvScalar param1, CvScalar param2 ); + +CVAPI(void) cvRandShuffle( CvArr* mat, CvRNG* rng, + double iter_factor CV_DEFAULT(1.)); + +#define CV_SORT_EVERY_ROW 0 +#define CV_SORT_EVERY_COLUMN 1 +#define CV_SORT_ASCENDING 0 +#define CV_SORT_DESCENDING 16 + +CVAPI(void) cvSort( const CvArr* src, CvArr* dst CV_DEFAULT(NULL), + CvArr* idxmat CV_DEFAULT(NULL), + int flags CV_DEFAULT(0)); + +/** Finds real roots of a cubic equation */ +CVAPI(int) cvSolveCubic( const CvMat* coeffs, CvMat* roots ); + +/** Finds all real and complex roots of a polynomial equation */ +CVAPI(void) cvSolvePoly(const CvMat* coeffs, CvMat *roots2, + int maxiter CV_DEFAULT(20), int fig CV_DEFAULT(100)); + +/****************************************************************************************\ +* Matrix operations * +\****************************************************************************************/ + +/** @brief Calculates the cross product of two 3D vectors. + +The function calculates the cross product of two 3D vectors: +\f[\texttt{dst} = \texttt{src1} \times \texttt{src2}\f] +or: +\f[\begin{array}{l} \texttt{dst} _1 = \texttt{src1} _2 \texttt{src2} _3 - \texttt{src1} _3 \texttt{src2} _2 \\ \texttt{dst} _2 = \texttt{src1} _3 \texttt{src2} _1 - \texttt{src1} _1 \texttt{src2} _3 \\ \texttt{dst} _3 = \texttt{src1} _1 \texttt{src2} _2 - \texttt{src1} _2 \texttt{src2} _1 \end{array}\f] +@param src1 The first source vector +@param src2 The second source vector +@param dst The destination vector + */ +CVAPI(void) cvCrossProduct( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** Matrix transform: dst = A*B + C, C is optional */ +#define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( (src1), (src2), 1., (src3), 1., (dst), 0 ) +#define cvMatMul( src1, src2, dst ) cvMatMulAdd( (src1), (src2), NULL, (dst)) + +#define CV_GEMM_A_T 1 +#define CV_GEMM_B_T 2 +#define CV_GEMM_C_T 4 +/** Extended matrix transform: + dst = alpha*op(A)*op(B) + beta*op(C), where op(X) is X or X^T */ +CVAPI(void) cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, + const CvArr* src3, double beta, CvArr* dst, + int tABC CV_DEFAULT(0)); +#define cvMatMulAddEx cvGEMM + +/** Transforms each element of source array and stores + resultant vectors in destination array */ +CVAPI(void) cvTransform( const CvArr* src, CvArr* dst, + const CvMat* transmat, + const CvMat* shiftvec CV_DEFAULT(NULL)); +#define cvMatMulAddS cvTransform + +/** Does perspective transform on every element of input array */ +CVAPI(void) cvPerspectiveTransform( const CvArr* src, CvArr* dst, + const CvMat* mat ); + +/** Calculates (A-delta)*(A-delta)^T (order=0) or (A-delta)^T*(A-delta) (order=1) */ +CVAPI(void) cvMulTransposed( const CvArr* src, CvArr* dst, int order, + const CvArr* delta CV_DEFAULT(NULL), + double scale CV_DEFAULT(1.) ); + +/** Transposes matrix. Square matrices can be transposed in-place */ +CVAPI(void) cvTranspose( const CvArr* src, CvArr* dst ); +#define cvT cvTranspose + +/** Completes the symmetric matrix from the lower (LtoR=0) or from the upper (LtoR!=0) part */ +CVAPI(void) cvCompleteSymm( CvMat* matrix, int LtoR CV_DEFAULT(0) ); + +/** Mirror array data around horizontal (flip=0), + vertical (flip=1) or both(flip=-1) axises: + cvFlip(src) flips images vertically and sequences horizontally (inplace) */ +CVAPI(void) cvFlip( const CvArr* src, CvArr* dst CV_DEFAULT(NULL), + int flip_mode CV_DEFAULT(0)); +#define cvMirror cvFlip + + +#define CV_SVD_MODIFY_A 1 +#define CV_SVD_U_T 2 +#define CV_SVD_V_T 4 + +/** Performs Singular Value Decomposition of a matrix */ +CVAPI(void) cvSVD( CvArr* A, CvArr* W, CvArr* U CV_DEFAULT(NULL), + CvArr* V CV_DEFAULT(NULL), int flags CV_DEFAULT(0)); + +/** Performs Singular Value Back Substitution (solves A*X = B): + flags must be the same as in cvSVD */ +CVAPI(void) cvSVBkSb( const CvArr* W, const CvArr* U, + const CvArr* V, const CvArr* B, + CvArr* X, int flags ); + +#define CV_LU 0 +#define CV_SVD 1 +#define CV_SVD_SYM 2 +#define CV_CHOLESKY 3 +#define CV_QR 4 +#define CV_NORMAL 16 + +/** Inverts matrix */ +CVAPI(double) cvInvert( const CvArr* src, CvArr* dst, + int method CV_DEFAULT(CV_LU)); +#define cvInv cvInvert + +/** Solves linear system (src1)*(dst) = (src2) + (returns 0 if src1 is a singular and CV_LU method is used) */ +CVAPI(int) cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, + int method CV_DEFAULT(CV_LU)); + +/** Calculates determinant of input matrix */ +CVAPI(double) cvDet( const CvArr* mat ); + +/** Calculates trace of the matrix (sum of elements on the main diagonal) */ +CVAPI(CvScalar) cvTrace( const CvArr* mat ); + +/** Finds eigen values and vectors of a symmetric matrix */ +CVAPI(void) cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, + double eps CV_DEFAULT(0), + int lowindex CV_DEFAULT(-1), + int highindex CV_DEFAULT(-1)); + +///* Finds selected eigen values and vectors of a symmetric matrix */ +//CVAPI(void) cvSelectedEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, +// int lowindex, int highindex ); + +/** Makes an identity matrix (mat_ij = i == j) */ +CVAPI(void) cvSetIdentity( CvArr* mat, CvScalar value CV_DEFAULT(cvRealScalar(1)) ); + +/** Fills matrix with given range of numbers */ +CVAPI(CvArr*) cvRange( CvArr* mat, double start, double end ); + +/** @anchor core_c_CovarFlags +@name Flags for cvCalcCovarMatrix +@see cvCalcCovarMatrix + @{ +*/ + +/** flag for cvCalcCovarMatrix, transpose([v1-avg, v2-avg,...]) * [v1-avg,v2-avg,...] */ +#define CV_COVAR_SCRAMBLED 0 + +/** flag for cvCalcCovarMatrix, [v1-avg, v2-avg,...] * transpose([v1-avg,v2-avg,...]) */ +#define CV_COVAR_NORMAL 1 + +/** flag for cvCalcCovarMatrix, do not calc average (i.e. mean vector) - use the input vector instead + (useful for calculating covariance matrix by parts) */ +#define CV_COVAR_USE_AVG 2 + +/** flag for cvCalcCovarMatrix, scale the covariance matrix coefficients by number of the vectors */ +#define CV_COVAR_SCALE 4 + +/** flag for cvCalcCovarMatrix, all the input vectors are stored in a single matrix, as its rows */ +#define CV_COVAR_ROWS 8 + +/** flag for cvCalcCovarMatrix, all the input vectors are stored in a single matrix, as its columns */ +#define CV_COVAR_COLS 16 + +/** @} */ + +/** Calculates covariation matrix for a set of vectors +@see @ref core_c_CovarFlags "flags" +*/ +CVAPI(void) cvCalcCovarMatrix( const CvArr** vects, int count, + CvArr* cov_mat, CvArr* avg, int flags ); + +#define CV_PCA_DATA_AS_ROW 0 +#define CV_PCA_DATA_AS_COL 1 +#define CV_PCA_USE_AVG 2 +CVAPI(void) cvCalcPCA( const CvArr* data, CvArr* mean, + CvArr* eigenvals, CvArr* eigenvects, int flags ); + +CVAPI(void) cvProjectPCA( const CvArr* data, const CvArr* mean, + const CvArr* eigenvects, CvArr* result ); + +CVAPI(void) cvBackProjectPCA( const CvArr* proj, const CvArr* mean, + const CvArr* eigenvects, CvArr* result ); + +/** Calculates Mahalanobis(weighted) distance */ +CVAPI(double) cvMahalanobis( const CvArr* vec1, const CvArr* vec2, const CvArr* mat ); +#define cvMahalonobis cvMahalanobis + +/****************************************************************************************\ +* Array Statistics * +\****************************************************************************************/ + +/** Finds sum of array elements */ +CVAPI(CvScalar) cvSum( const CvArr* arr ); + +/** Calculates number of non-zero pixels */ +CVAPI(int) cvCountNonZero( const CvArr* arr ); + +/** Calculates mean value of array elements */ +CVAPI(CvScalar) cvAvg( const CvArr* arr, const CvArr* mask CV_DEFAULT(NULL) ); + +/** Calculates mean and standard deviation of pixel values */ +CVAPI(void) cvAvgSdv( const CvArr* arr, CvScalar* mean, CvScalar* std_dev, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** Finds global minimum, maximum and their positions */ +CVAPI(void) cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val, + CvPoint* min_loc CV_DEFAULT(NULL), + CvPoint* max_loc CV_DEFAULT(NULL), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @anchor core_c_NormFlags + @name Flags for cvNorm and cvNormalize + @{ +*/ +#define CV_C 1 +#define CV_L1 2 +#define CV_L2 4 +#define CV_NORM_MASK 7 +#define CV_RELATIVE 8 +#define CV_DIFF 16 +#define CV_MINMAX 32 + +#define CV_DIFF_C (CV_DIFF | CV_C) +#define CV_DIFF_L1 (CV_DIFF | CV_L1) +#define CV_DIFF_L2 (CV_DIFF | CV_L2) +#define CV_RELATIVE_C (CV_RELATIVE | CV_C) +#define CV_RELATIVE_L1 (CV_RELATIVE | CV_L1) +#define CV_RELATIVE_L2 (CV_RELATIVE | CV_L2) +/** @} */ + +/** Finds norm, difference norm or relative difference norm for an array (or two arrays) +@see ref core_c_NormFlags "flags" +*/ +CVAPI(double) cvNorm( const CvArr* arr1, const CvArr* arr2 CV_DEFAULT(NULL), + int norm_type CV_DEFAULT(CV_L2), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @see ref core_c_NormFlags "flags" */ +CVAPI(void) cvNormalize( const CvArr* src, CvArr* dst, + double a CV_DEFAULT(1.), double b CV_DEFAULT(0.), + int norm_type CV_DEFAULT(CV_L2), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @anchor core_c_ReduceFlags + @name Flags for cvReduce + @{ +*/ +#define CV_REDUCE_SUM 0 +#define CV_REDUCE_AVG 1 +#define CV_REDUCE_MAX 2 +#define CV_REDUCE_MIN 3 +/** @} */ + +/** @see @ref core_c_ReduceFlags "flags" */ +CVAPI(void) cvReduce( const CvArr* src, CvArr* dst, int dim CV_DEFAULT(-1), + int op CV_DEFAULT(CV_REDUCE_SUM) ); + +/****************************************************************************************\ +* Discrete Linear Transforms and Related Functions * +\****************************************************************************************/ + +/** @anchor core_c_DftFlags + @name Flags for cvDFT, cvDCT and cvMulSpectrums + @{ + */ +#define CV_DXT_FORWARD 0 +#define CV_DXT_INVERSE 1 +#define CV_DXT_SCALE 2 /**< divide result by size of array */ +#define CV_DXT_INV_SCALE (CV_DXT_INVERSE + CV_DXT_SCALE) +#define CV_DXT_INVERSE_SCALE CV_DXT_INV_SCALE +#define CV_DXT_ROWS 4 /**< transform each row individually */ +#define CV_DXT_MUL_CONJ 8 /**< conjugate the second argument of cvMulSpectrums */ +/** @} */ + +/** Discrete Fourier Transform: + complex->complex, + real->ccs (forward), + ccs->real (inverse) +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvDFT( const CvArr* src, CvArr* dst, int flags, + int nonzero_rows CV_DEFAULT(0) ); +#define cvFFT cvDFT + +/** Multiply results of DFTs: DFT(X)*DFT(Y) or DFT(X)*conj(DFT(Y)) +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvMulSpectrums( const CvArr* src1, const CvArr* src2, + CvArr* dst, int flags ); + +/** Finds optimal DFT vector size >= size0 */ +CVAPI(int) cvGetOptimalDFTSize( int size0 ); + +/** Discrete Cosine Transform +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvDCT( const CvArr* src, CvArr* dst, int flags ); + +/****************************************************************************************\ +* Dynamic data structures * +\****************************************************************************************/ + +/** Calculates length of sequence slice (with support of negative indices). */ +CVAPI(int) cvSliceLength( CvSlice slice, const CvSeq* seq ); + + +/** Creates new memory storage. + block_size == 0 means that default, + somewhat optimal size, is used (currently, it is 64K) */ +CVAPI(CvMemStorage*) cvCreateMemStorage( int block_size CV_DEFAULT(0)); + + +/** Creates a memory storage that will borrow memory blocks from parent storage */ +CVAPI(CvMemStorage*) cvCreateChildMemStorage( CvMemStorage* parent ); + + +/** Releases memory storage. All the children of a parent must be released before + the parent. A child storage returns all the blocks to parent when it is released */ +CVAPI(void) cvReleaseMemStorage( CvMemStorage** storage ); + + +/** Clears memory storage. This is the only way(!!!) (besides cvRestoreMemStoragePos) + to reuse memory allocated for the storage - cvClearSeq,cvClearSet ... + do not free any memory. + A child storage returns all the blocks to the parent when it is cleared */ +CVAPI(void) cvClearMemStorage( CvMemStorage* storage ); + +/** Remember a storage "free memory" position */ +CVAPI(void) cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos ); + +/** Restore a storage "free memory" position */ +CVAPI(void) cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos ); + +/** Allocates continuous buffer of the specified size in the storage */ +CVAPI(void*) cvMemStorageAlloc( CvMemStorage* storage, size_t size ); + +/** Allocates string in memory storage */ +CVAPI(CvString) cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, + int len CV_DEFAULT(-1) ); + +/** Creates new empty sequence that will reside in the specified storage */ +CVAPI(CvSeq*) cvCreateSeq( int seq_flags, size_t header_size, + size_t elem_size, CvMemStorage* storage ); + +/** Changes default size (granularity) of sequence blocks. + The default size is ~1Kbyte */ +CVAPI(void) cvSetSeqBlockSize( CvSeq* seq, int delta_elems ); + + +/** Adds new element to the end of sequence. Returns pointer to the element */ +CVAPI(schar*) cvSeqPush( CvSeq* seq, const void* element CV_DEFAULT(NULL)); + + +/** Adds new element to the beginning of sequence. Returns pointer to it */ +CVAPI(schar*) cvSeqPushFront( CvSeq* seq, const void* element CV_DEFAULT(NULL)); + + +/** Removes the last element from sequence and optionally saves it */ +CVAPI(void) cvSeqPop( CvSeq* seq, void* element CV_DEFAULT(NULL)); + + +/** Removes the first element from sequence and optioanally saves it */ +CVAPI(void) cvSeqPopFront( CvSeq* seq, void* element CV_DEFAULT(NULL)); + + +#define CV_FRONT 1 +#define CV_BACK 0 +/** Adds several new elements to the end of sequence */ +CVAPI(void) cvSeqPushMulti( CvSeq* seq, const void* elements, + int count, int in_front CV_DEFAULT(0) ); + +/** Removes several elements from the end of sequence and optionally saves them */ +CVAPI(void) cvSeqPopMulti( CvSeq* seq, void* elements, + int count, int in_front CV_DEFAULT(0) ); + +/** Inserts a new element in the middle of sequence. + cvSeqInsert(seq,0,elem) == cvSeqPushFront(seq,elem) */ +CVAPI(schar*) cvSeqInsert( CvSeq* seq, int before_index, + const void* element CV_DEFAULT(NULL)); + +/** Removes specified sequence element */ +CVAPI(void) cvSeqRemove( CvSeq* seq, int index ); + + +/** Removes all the elements from the sequence. The freed memory + can be reused later only by the same sequence unless cvClearMemStorage + or cvRestoreMemStoragePos is called */ +CVAPI(void) cvClearSeq( CvSeq* seq ); + + +/** Retrieves pointer to specified sequence element. + Negative indices are supported and mean counting from the end + (e.g -1 means the last sequence element) */ +CVAPI(schar*) cvGetSeqElem( const CvSeq* seq, int index ); + +/** Calculates index of the specified sequence element. + Returns -1 if element does not belong to the sequence */ +CVAPI(int) cvSeqElemIdx( const CvSeq* seq, const void* element, + CvSeqBlock** block CV_DEFAULT(NULL) ); + +/** Initializes sequence writer. The new elements will be added to the end of sequence */ +CVAPI(void) cvStartAppendToSeq( CvSeq* seq, CvSeqWriter* writer ); + + +/** Combination of cvCreateSeq and cvStartAppendToSeq */ +CVAPI(void) cvStartWriteSeq( int seq_flags, int header_size, + int elem_size, CvMemStorage* storage, + CvSeqWriter* writer ); + +/** Closes sequence writer, updates sequence header and returns pointer + to the resultant sequence + (which may be useful if the sequence was created using cvStartWriteSeq)) +*/ +CVAPI(CvSeq*) cvEndWriteSeq( CvSeqWriter* writer ); + + +/** Updates sequence header. May be useful to get access to some of previously + written elements via cvGetSeqElem or sequence reader */ +CVAPI(void) cvFlushSeqWriter( CvSeqWriter* writer ); + + +/** Initializes sequence reader. + The sequence can be read in forward or backward direction */ +CVAPI(void) cvStartReadSeq( const CvSeq* seq, CvSeqReader* reader, + int reverse CV_DEFAULT(0) ); + + +/** Returns current sequence reader position (currently observed sequence element) */ +CVAPI(int) cvGetSeqReaderPos( CvSeqReader* reader ); + + +/** Changes sequence reader position. It may seek to an absolute or + to relative to the current position */ +CVAPI(void) cvSetSeqReaderPos( CvSeqReader* reader, int index, + int is_relative CV_DEFAULT(0)); + +/** Copies sequence content to a continuous piece of memory */ +CVAPI(void*) cvCvtSeqToArray( const CvSeq* seq, void* elements, + CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ) ); + +/** Creates sequence header for array. + After that all the operations on sequences that do not alter the content + can be applied to the resultant sequence */ +CVAPI(CvSeq*) cvMakeSeqHeaderForArray( int seq_type, int header_size, + int elem_size, void* elements, int total, + CvSeq* seq, CvSeqBlock* block ); + +/** Extracts sequence slice (with or without copying sequence elements) */ +CVAPI(CvSeq*) cvSeqSlice( const CvSeq* seq, CvSlice slice, + CvMemStorage* storage CV_DEFAULT(NULL), + int copy_data CV_DEFAULT(0)); + +CV_INLINE CvSeq* cvCloneSeq( const CvSeq* seq, CvMemStorage* storage CV_DEFAULT(NULL)) +{ + return cvSeqSlice( seq, CV_WHOLE_SEQ, storage, 1 ); +} + +/** Removes sequence slice */ +CVAPI(void) cvSeqRemoveSlice( CvSeq* seq, CvSlice slice ); + +/** Inserts a sequence or array into another sequence */ +CVAPI(void) cvSeqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr ); + +/** a < b ? -1 : a > b ? 1 : 0 */ +typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void* b, void* userdata ); + +/** Sorts sequence in-place given element comparison function */ +CVAPI(void) cvSeqSort( CvSeq* seq, CvCmpFunc func, void* userdata CV_DEFAULT(NULL) ); + +/** Finds element in a [sorted] sequence */ +CVAPI(schar*) cvSeqSearch( CvSeq* seq, const void* elem, CvCmpFunc func, + int is_sorted, int* elem_idx, + void* userdata CV_DEFAULT(NULL) ); + +/** Reverses order of sequence elements in-place */ +CVAPI(void) cvSeqInvert( CvSeq* seq ); + +/** Splits sequence into one or more equivalence classes using the specified criteria */ +CVAPI(int) cvSeqPartition( const CvSeq* seq, CvMemStorage* storage, + CvSeq** labels, CvCmpFunc is_equal, void* userdata ); + +/************ Internal sequence functions ************/ +CVAPI(void) cvChangeSeqBlock( void* reader, int direction ); +CVAPI(void) cvCreateSeqBlock( CvSeqWriter* writer ); + + +/** Creates a new set */ +CVAPI(CvSet*) cvCreateSet( int set_flags, int header_size, + int elem_size, CvMemStorage* storage ); + +/** Adds new element to the set and returns pointer to it */ +CVAPI(int) cvSetAdd( CvSet* set_header, CvSetElem* elem CV_DEFAULT(NULL), + CvSetElem** inserted_elem CV_DEFAULT(NULL) ); + +/** Fast variant of cvSetAdd */ +CV_INLINE CvSetElem* cvSetNew( CvSet* set_header ) +{ + CvSetElem* elem = set_header->free_elems; + if( elem ) + { + set_header->free_elems = elem->next_free; + elem->flags = elem->flags & CV_SET_ELEM_IDX_MASK; + set_header->active_count++; + } + else + cvSetAdd( set_header, NULL, &elem ); + return elem; +} + +/** Removes set element given its pointer */ +CV_INLINE void cvSetRemoveByPtr( CvSet* set_header, void* elem ) +{ + CvSetElem* _elem = (CvSetElem*)elem; + assert( _elem->flags >= 0 /*&& (elem->flags & CV_SET_ELEM_IDX_MASK) < set_header->total*/ ); + _elem->next_free = set_header->free_elems; + _elem->flags = (_elem->flags & CV_SET_ELEM_IDX_MASK) | CV_SET_ELEM_FREE_FLAG; + set_header->free_elems = _elem; + set_header->active_count--; +} + +/** Removes element from the set by its index */ +CVAPI(void) cvSetRemove( CvSet* set_header, int index ); + +/** Returns a set element by index. If the element doesn't belong to the set, + NULL is returned */ +CV_INLINE CvSetElem* cvGetSetElem( const CvSet* set_header, int idx ) +{ + CvSetElem* elem = (CvSetElem*)(void *)cvGetSeqElem( (CvSeq*)set_header, idx ); + return elem && CV_IS_SET_ELEM( elem ) ? elem : 0; +} + +/** Removes all the elements from the set */ +CVAPI(void) cvClearSet( CvSet* set_header ); + +/** Creates new graph */ +CVAPI(CvGraph*) cvCreateGraph( int graph_flags, int header_size, + int vtx_size, int edge_size, + CvMemStorage* storage ); + +/** Adds new vertex to the graph */ +CVAPI(int) cvGraphAddVtx( CvGraph* graph, const CvGraphVtx* vtx CV_DEFAULT(NULL), + CvGraphVtx** inserted_vtx CV_DEFAULT(NULL) ); + + +/** Removes vertex from the graph together with all incident edges */ +CVAPI(int) cvGraphRemoveVtx( CvGraph* graph, int index ); +CVAPI(int) cvGraphRemoveVtxByPtr( CvGraph* graph, CvGraphVtx* vtx ); + + +/** Link two vertices specified by indices or pointers if they + are not connected or return pointer to already existing edge + connecting the vertices. + Functions return 1 if a new edge was created, 0 otherwise */ +CVAPI(int) cvGraphAddEdge( CvGraph* graph, + int start_idx, int end_idx, + const CvGraphEdge* edge CV_DEFAULT(NULL), + CvGraphEdge** inserted_edge CV_DEFAULT(NULL) ); + +CVAPI(int) cvGraphAddEdgeByPtr( CvGraph* graph, + CvGraphVtx* start_vtx, CvGraphVtx* end_vtx, + const CvGraphEdge* edge CV_DEFAULT(NULL), + CvGraphEdge** inserted_edge CV_DEFAULT(NULL) ); + +/** Remove edge connecting two vertices */ +CVAPI(void) cvGraphRemoveEdge( CvGraph* graph, int start_idx, int end_idx ); +CVAPI(void) cvGraphRemoveEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, + CvGraphVtx* end_vtx ); + +/** Find edge connecting two vertices */ +CVAPI(CvGraphEdge*) cvFindGraphEdge( const CvGraph* graph, int start_idx, int end_idx ); +CVAPI(CvGraphEdge*) cvFindGraphEdgeByPtr( const CvGraph* graph, + const CvGraphVtx* start_vtx, + const CvGraphVtx* end_vtx ); +#define cvGraphFindEdge cvFindGraphEdge +#define cvGraphFindEdgeByPtr cvFindGraphEdgeByPtr + +/** Remove all vertices and edges from the graph */ +CVAPI(void) cvClearGraph( CvGraph* graph ); + + +/** Count number of edges incident to the vertex */ +CVAPI(int) cvGraphVtxDegree( const CvGraph* graph, int vtx_idx ); +CVAPI(int) cvGraphVtxDegreeByPtr( const CvGraph* graph, const CvGraphVtx* vtx ); + + +/** Retrieves graph vertex by given index */ +#define cvGetGraphVtx( graph, idx ) (CvGraphVtx*)cvGetSetElem((CvSet*)(graph), (idx)) + +/** Retrieves index of a graph vertex given its pointer */ +#define cvGraphVtxIdx( graph, vtx ) ((vtx)->flags & CV_SET_ELEM_IDX_MASK) + +/** Retrieves index of a graph edge given its pointer */ +#define cvGraphEdgeIdx( graph, edge ) ((edge)->flags & CV_SET_ELEM_IDX_MASK) + +#define cvGraphGetVtxCount( graph ) ((graph)->active_count) +#define cvGraphGetEdgeCount( graph ) ((graph)->edges->active_count) + +#define CV_GRAPH_VERTEX 1 +#define CV_GRAPH_TREE_EDGE 2 +#define CV_GRAPH_BACK_EDGE 4 +#define CV_GRAPH_FORWARD_EDGE 8 +#define CV_GRAPH_CROSS_EDGE 16 +#define CV_GRAPH_ANY_EDGE 30 +#define CV_GRAPH_NEW_TREE 32 +#define CV_GRAPH_BACKTRACKING 64 +#define CV_GRAPH_OVER -1 + +#define CV_GRAPH_ALL_ITEMS -1 + +/** flags for graph vertices and edges */ +#define CV_GRAPH_ITEM_VISITED_FLAG (1 << 30) +#define CV_IS_GRAPH_VERTEX_VISITED(vtx) \ + (((CvGraphVtx*)(vtx))->flags & CV_GRAPH_ITEM_VISITED_FLAG) +#define CV_IS_GRAPH_EDGE_VISITED(edge) \ + (((CvGraphEdge*)(edge))->flags & CV_GRAPH_ITEM_VISITED_FLAG) +#define CV_GRAPH_SEARCH_TREE_NODE_FLAG (1 << 29) +#define CV_GRAPH_FORWARD_EDGE_FLAG (1 << 28) + +typedef struct CvGraphScanner +{ + CvGraphVtx* vtx; /* current graph vertex (or current edge origin) */ + CvGraphVtx* dst; /* current graph edge destination vertex */ + CvGraphEdge* edge; /* current edge */ + + CvGraph* graph; /* the graph */ + CvSeq* stack; /* the graph vertex stack */ + int index; /* the lower bound of certainly visited vertices */ + int mask; /* event mask */ +} +CvGraphScanner; + +/** Creates new graph scanner. */ +CVAPI(CvGraphScanner*) cvCreateGraphScanner( CvGraph* graph, + CvGraphVtx* vtx CV_DEFAULT(NULL), + int mask CV_DEFAULT(CV_GRAPH_ALL_ITEMS)); + +/** Releases graph scanner. */ +CVAPI(void) cvReleaseGraphScanner( CvGraphScanner** scanner ); + +/** Get next graph element */ +CVAPI(int) cvNextGraphItem( CvGraphScanner* scanner ); + +/** Creates a copy of graph */ +CVAPI(CvGraph*) cvCloneGraph( const CvGraph* graph, CvMemStorage* storage ); + + +/** Does look-up transformation. Elements of the source array + (that should be 8uC1 or 8sC1) are used as indexes in lutarr 256-element table */ +CVAPI(void) cvLUT( const CvArr* src, CvArr* dst, const CvArr* lut ); + + +/******************* Iteration through the sequence tree *****************/ +typedef struct CvTreeNodeIterator +{ + const void* node; + int level; + int max_level; +} +CvTreeNodeIterator; + +CVAPI(void) cvInitTreeNodeIterator( CvTreeNodeIterator* tree_iterator, + const void* first, int max_level ); +CVAPI(void*) cvNextTreeNode( CvTreeNodeIterator* tree_iterator ); +CVAPI(void*) cvPrevTreeNode( CvTreeNodeIterator* tree_iterator ); + +/** Inserts sequence into tree with specified "parent" sequence. + If parent is equal to frame (e.g. the most external contour), + then added contour will have null pointer to parent. */ +CVAPI(void) cvInsertNodeIntoTree( void* node, void* parent, void* frame ); + +/** Removes contour from tree (together with the contour children). */ +CVAPI(void) cvRemoveNodeFromTree( void* node, void* frame ); + +/** Gathers pointers to all the sequences, + accessible from the `first`, to the single sequence */ +CVAPI(CvSeq*) cvTreeToNodeSeq( const void* first, int header_size, + CvMemStorage* storage ); + +/** The function implements the K-means algorithm for clustering an array of sample + vectors in a specified number of classes */ +#define CV_KMEANS_USE_INITIAL_LABELS 1 +CVAPI(int) cvKMeans2( const CvArr* samples, int cluster_count, CvArr* labels, + CvTermCriteria termcrit, int attempts CV_DEFAULT(1), + CvRNG* rng CV_DEFAULT(0), int flags CV_DEFAULT(0), + CvArr* _centers CV_DEFAULT(0), double* compactness CV_DEFAULT(0) ); + +/****************************************************************************************\ +* System functions * +\****************************************************************************************/ + +/** Loads optimized functions from IPP, MKL etc. or switches back to pure C code */ +CVAPI(int) cvUseOptimized( int on_off ); + +typedef IplImage* (CV_STDCALL* Cv_iplCreateImageHeader) + (int,int,int,char*,char*,int,int,int,int,int, + IplROI*,IplImage*,void*,IplTileInfo*); +typedef void (CV_STDCALL* Cv_iplAllocateImageData)(IplImage*,int,int); +typedef void (CV_STDCALL* Cv_iplDeallocate)(IplImage*,int); +typedef IplROI* (CV_STDCALL* Cv_iplCreateROI)(int,int,int,int,int); +typedef IplImage* (CV_STDCALL* Cv_iplCloneImage)(const IplImage*); + +/** @brief Makes OpenCV use IPL functions for allocating IplImage and IplROI structures. + +Normally, the function is not called directly. Instead, a simple macro +CV_TURN_ON_IPL_COMPATIBILITY() is used that calls cvSetIPLAllocators and passes there pointers +to IPL allocation functions. : +@code + ... + CV_TURN_ON_IPL_COMPATIBILITY() + ... +@endcode +@param create_header pointer to a function, creating IPL image header. +@param allocate_data pointer to a function, allocating IPL image data. +@param deallocate pointer to a function, deallocating IPL image. +@param create_roi pointer to a function, creating IPL image ROI (i.e. Region of Interest). +@param clone_image pointer to a function, cloning an IPL image. + */ +CVAPI(void) cvSetIPLAllocators( Cv_iplCreateImageHeader create_header, + Cv_iplAllocateImageData allocate_data, + Cv_iplDeallocate deallocate, + Cv_iplCreateROI create_roi, + Cv_iplCloneImage clone_image ); + +#define CV_TURN_ON_IPL_COMPATIBILITY() \ + cvSetIPLAllocators( iplCreateImageHeader, iplAllocateImage, \ + iplDeallocate, iplCreateROI, iplCloneImage ) + +/****************************************************************************************\ +* Data Persistence * +\****************************************************************************************/ + +/********************************** High-level functions ********************************/ + +/** @brief Opens file storage for reading or writing data. + +The function opens file storage for reading or writing data. In the latter case, a new file is +created or an existing file is rewritten. The type of the read or written file is determined by the +filename extension: .xml for XML, .yml or .yaml for YAML and .json for JSON. + +At the same time, it also supports adding parameters like "example.xml?base64". + +The function returns a pointer to the CvFileStorage structure. +If the file cannot be opened then the function returns NULL. +@param filename Name of the file associated with the storage +@param memstorage Memory storage used for temporary data and for +: storing dynamic structures, such as CvSeq or CvGraph . If it is NULL, a temporary memory + storage is created and used. +@param flags Can be one of the following: +> - **CV_STORAGE_READ** the storage is open for reading +> - **CV_STORAGE_WRITE** the storage is open for writing + (use **CV_STORAGE_WRITE | CV_STORAGE_WRITE_BASE64** to write rawdata in Base64) +@param encoding + */ +CVAPI(CvFileStorage*) cvOpenFileStorage( const char* filename, CvMemStorage* memstorage, + int flags, const char* encoding CV_DEFAULT(NULL) ); + +/** @brief Releases file storage. + +The function closes the file associated with the storage and releases all the temporary structures. +It must be called after all I/O operations with the storage are finished. +@param fs Double pointer to the released file storage + */ +CVAPI(void) cvReleaseFileStorage( CvFileStorage** fs ); + +/** returns attribute value or 0 (NULL) if there is no such attribute */ +CVAPI(const char*) cvAttrValue( const CvAttrList* attr, const char* attr_name ); + +/** @brief Starts writing a new structure. + +The function starts writing a compound structure (collection) that can be a sequence or a map. After +all the structure fields, which can be scalars or structures, are written, cvEndWriteStruct should +be called. The function can be used to group some objects or to implement the write function for a +some user object (see CvTypeInfo). +@param fs File storage +@param name Name of the written structure. The structure can be accessed by this name when the +storage is read. +@param struct_flags A combination one of the following values: +- **CV_NODE_SEQ** the written structure is a sequence (see discussion of CvFileStorage ), + that is, its elements do not have a name. +- **CV_NODE_MAP** the written structure is a map (see discussion of CvFileStorage ), that + is, all its elements have names. +One and only one of the two above flags must be specified +- **CV_NODE_FLOW** the optional flag that makes sense only for YAML streams. It means that + the structure is written as a flow (not as a block), which is more compact. It is + recommended to use this flag for structures or arrays whose elements are all scalars. +@param type_name Optional parameter - the object type name. In + case of XML it is written as a type_id attribute of the structure opening tag. In the case of + YAML it is written after a colon following the structure name (see the example in + CvFileStorage description). In case of JSON it is written as a name/value pair. + Mainly it is used with user objects. When the storage is read, the + encoded type name is used to determine the object type (see CvTypeInfo and cvFindType ). +@param attributes This parameter is not used in the current implementation + */ +CVAPI(void) cvStartWriteStruct( CvFileStorage* fs, const char* name, + int struct_flags, const char* type_name CV_DEFAULT(NULL), + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Finishes writing to a file node collection. +@param fs File storage +@sa cvStartWriteStruct. + */ +CVAPI(void) cvEndWriteStruct( CvFileStorage* fs ); + +/** @brief Writes an integer value. + +The function writes a single integer value (with or without a name) to the file storage. +@param fs File storage +@param name Name of the written value. Should be NULL if and only if the parent structure is a +sequence. +@param value The written value + */ +CVAPI(void) cvWriteInt( CvFileStorage* fs, const char* name, int value ); + +/** @brief Writes a floating-point value. + +The function writes a single floating-point value (with or without a name) to file storage. Special +values are encoded as follows: NaN (Not A Number) as .NaN, infinity as +.Inf or -.Inf. + +The following example shows how to use the low-level writing functions to store custom structures, +such as termination criteria, without registering a new type. : +@code + void write_termcriteria( CvFileStorage* fs, const char* struct_name, + CvTermCriteria* termcrit ) + { + cvStartWriteStruct( fs, struct_name, CV_NODE_MAP, NULL, cvAttrList(0,0)); + cvWriteComment( fs, "termination criteria", 1 ); // just a description + if( termcrit->type & CV_TERMCRIT_ITER ) + cvWriteInteger( fs, "max_iterations", termcrit->max_iter ); + if( termcrit->type & CV_TERMCRIT_EPS ) + cvWriteReal( fs, "accuracy", termcrit->epsilon ); + cvEndWriteStruct( fs ); + } +@endcode +@param fs File storage +@param name Name of the written value. Should be NULL if and only if the parent structure is a +sequence. +@param value The written value +*/ +CVAPI(void) cvWriteReal( CvFileStorage* fs, const char* name, double value ); + +/** @brief Writes a text string. + +The function writes a text string to file storage. +@param fs File storage +@param name Name of the written string . Should be NULL if and only if the parent structure is a +sequence. +@param str The written text string +@param quote If non-zero, the written string is put in quotes, regardless of whether they are +required. Otherwise, if the flag is zero, quotes are used only when they are required (e.g. when +the string starts with a digit or contains spaces). + */ +CVAPI(void) cvWriteString( CvFileStorage* fs, const char* name, + const char* str, int quote CV_DEFAULT(0) ); + +/** @brief Writes a comment. + +The function writes a comment into file storage. The comments are skipped when the storage is read. +@param fs File storage +@param comment The written comment, single-line or multi-line +@param eol_comment If non-zero, the function tries to put the comment at the end of current line. +If the flag is zero, if the comment is multi-line, or if it does not fit at the end of the current +line, the comment starts a new line. + */ +CVAPI(void) cvWriteComment( CvFileStorage* fs, const char* comment, + int eol_comment ); + +/** @brief Writes an object to file storage. + +The function writes an object to file storage. First, the appropriate type info is found using +cvTypeOf. Then, the write method associated with the type info is called. + +Attributes are used to customize the writing procedure. The standard types support the following +attributes (all the dt attributes have the same format as in cvWriteRawData): + +-# CvSeq + - **header_dt** description of user fields of the sequence header that follow CvSeq, or + CvChain (if the sequence is a Freeman chain) or CvContour (if the sequence is a contour or + point sequence) + - **dt** description of the sequence elements. + - **recursive** if the attribute is present and is not equal to "0" or "false", the whole + tree of sequences (contours) is stored. +-# CvGraph + - **header_dt** description of user fields of the graph header that follows CvGraph; + - **vertex_dt** description of user fields of graph vertices + - **edge_dt** description of user fields of graph edges (note that the edge weight is + always written, so there is no need to specify it explicitly) + +Below is the code that creates the YAML file shown in the CvFileStorage description: +@code + #include "cxcore.h" + + int main( int argc, char** argv ) + { + CvMat* mat = cvCreateMat( 3, 3, CV_32F ); + CvFileStorage* fs = cvOpenFileStorage( "example.yml", 0, CV_STORAGE_WRITE ); + + cvSetIdentity( mat ); + cvWrite( fs, "A", mat, cvAttrList(0,0) ); + + cvReleaseFileStorage( &fs ); + cvReleaseMat( &mat ); + return 0; + } +@endcode +@param fs File storage +@param name Name of the written object. Should be NULL if and only if the parent structure is a +sequence. +@param ptr Pointer to the object +@param attributes The attributes of the object. They are specific for each particular type (see +the discussion below). + */ +CVAPI(void) cvWrite( CvFileStorage* fs, const char* name, const void* ptr, + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Starts the next stream. + +The function finishes the currently written stream and starts the next stream. In the case of XML +the file with multiple streams looks like this: +@code{.xml} + + + + + + + ... +@endcode +The YAML file will look like this: +@code{.yaml} + %YAML 1.0 + # stream #1 data + ... + --- + # stream #2 data +@endcode +This is useful for concatenating files or for resuming the writing process. +@param fs File storage + */ +CVAPI(void) cvStartNextStream( CvFileStorage* fs ); + +/** @brief Writes multiple numbers. + +The function writes an array, whose elements consist of single or multiple numbers. The function +call can be replaced with a loop containing a few cvWriteInt and cvWriteReal calls, but a single +call is more efficient. Note that because none of the elements have a name, they should be written +to a sequence rather than a map. +@param fs File storage +@param src Pointer to the written array +@param len Number of the array elements to write +@param dt Specification of each array element, see @ref format_spec "format specification" + */ +CVAPI(void) cvWriteRawData( CvFileStorage* fs, const void* src, + int len, const char* dt ); + +/** @brief Writes multiple numbers in Base64. + +If either CV_STORAGE_WRITE_BASE64 or cv::FileStorage::WRITE_BASE64 is used, +this function will be the same as cvWriteRawData. If neither, the main +difference is that it outputs a sequence in Base64 encoding rather than +in plain text. + +This function can only be used to write a sequence with a type "binary". + +@param fs File storage +@param src Pointer to the written array +@param len Number of the array elements to write +@param dt Specification of each array element, see @ref format_spec "format specification" +*/ +CVAPI(void) cvWriteRawDataBase64( CvFileStorage* fs, const void* src, + int len, const char* dt ); + +/** @brief Returns a unique pointer for a given name. + +The function returns a unique pointer for each particular file node name. This pointer can be then +passed to the cvGetFileNode function that is faster than cvGetFileNodeByName because it compares +text strings by comparing pointers rather than the strings' content. + +Consider the following example where an array of points is encoded as a sequence of 2-entry maps: +@code + points: + - { x: 10, y: 10 } + - { x: 20, y: 20 } + - { x: 30, y: 30 } + # ... +@endcode +Then, it is possible to get hashed "x" and "y" pointers to speed up decoding of the points. : +@code + #include "cxcore.h" + + int main( int argc, char** argv ) + { + CvFileStorage* fs = cvOpenFileStorage( "points.yml", 0, CV_STORAGE_READ ); + CvStringHashNode* x_key = cvGetHashedNode( fs, "x", -1, 1 ); + CvStringHashNode* y_key = cvGetHashedNode( fs, "y", -1, 1 ); + CvFileNode* points = cvGetFileNodeByName( fs, 0, "points" ); + + if( CV_NODE_IS_SEQ(points->tag) ) + { + CvSeq* seq = points->data.seq; + int i, total = seq->total; + CvSeqReader reader; + cvStartReadSeq( seq, &reader, 0 ); + for( i = 0; i < total; i++ ) + { + CvFileNode* pt = (CvFileNode*)reader.ptr; + #if 1 // faster variant + CvFileNode* xnode = cvGetFileNode( fs, pt, x_key, 0 ); + CvFileNode* ynode = cvGetFileNode( fs, pt, y_key, 0 ); + assert( xnode && CV_NODE_IS_INT(xnode->tag) && + ynode && CV_NODE_IS_INT(ynode->tag)); + int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); + int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); + #elif 1 // slower variant; does not use x_key & y_key + CvFileNode* xnode = cvGetFileNodeByName( fs, pt, "x" ); + CvFileNode* ynode = cvGetFileNodeByName( fs, pt, "y" ); + assert( xnode && CV_NODE_IS_INT(xnode->tag) && + ynode && CV_NODE_IS_INT(ynode->tag)); + int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); + int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); + #else // the slowest yet the easiest to use variant + int x = cvReadIntByName( fs, pt, "x", 0 ); + int y = cvReadIntByName( fs, pt, "y", 0 ); + #endif + CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); + printf(" + } + } + cvReleaseFileStorage( &fs ); + return 0; + } +@endcode +Please note that whatever method of accessing a map you are using, it is still much slower than +using plain sequences; for example, in the above example, it is more efficient to encode the points +as pairs of integers in a single numeric sequence. +@param fs File storage +@param name Literal node name +@param len Length of the name (if it is known apriori), or -1 if it needs to be calculated +@param create_missing Flag that specifies, whether an absent key should be added into the hash table +*/ +CVAPI(CvStringHashNode*) cvGetHashedKey( CvFileStorage* fs, const char* name, + int len CV_DEFAULT(-1), + int create_missing CV_DEFAULT(0)); + +/** @brief Retrieves one of the top-level nodes of the file storage. + +The function returns one of the top-level file nodes. The top-level nodes do not have a name, they +correspond to the streams that are stored one after another in the file storage. If the index is out +of range, the function returns a NULL pointer, so all the top-level nodes can be iterated by +subsequent calls to the function with stream_index=0,1,..., until the NULL pointer is returned. +This function can be used as a base for recursive traversal of the file storage. +@param fs File storage +@param stream_index Zero-based index of the stream. See cvStartNextStream . In most cases, +there is only one stream in the file; however, there can be several. + */ +CVAPI(CvFileNode*) cvGetRootFileNode( const CvFileStorage* fs, + int stream_index CV_DEFAULT(0) ); + +/** @brief Finds a node in a map or file storage. + +The function finds a file node. It is a faster version of cvGetFileNodeByName (see +cvGetHashedKey discussion). Also, the function can insert a new node, if it is not in the map yet. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. If both map and +key are NULLs, the function returns the root file node - a map that contains top-level nodes. +@param key Unique pointer to the node name, retrieved with cvGetHashedKey +@param create_missing Flag that specifies whether an absent node should be added to the map + */ +CVAPI(CvFileNode*) cvGetFileNode( CvFileStorage* fs, CvFileNode* map, + const CvStringHashNode* key, + int create_missing CV_DEFAULT(0) ); + +/** @brief Finds a node in a map or file storage. + +The function finds a file node by name. The node is searched either in map or, if the pointer is +NULL, among the top-level file storage nodes. Using this function for maps and cvGetSeqElem (or +sequence reader) for sequences, it is possible to navigate through the file storage. To speed up +multiple queries for a certain key (e.g., in the case of an array of structures) one may use a +combination of cvGetHashedKey and cvGetFileNode. +@param fs File storage +@param map The parent map. If it is NULL, the function searches in all the top-level nodes +(streams), starting with the first one. +@param name The file node name + */ +CVAPI(CvFileNode*) cvGetFileNodeByName( const CvFileStorage* fs, + const CvFileNode* map, + const char* name ); + +/** @brief Retrieves an integer value from a file node. + +The function returns an integer that is represented by the file node. If the file node is NULL, the +default_value is returned (thus, it is convenient to call the function right after cvGetFileNode +without checking for a NULL pointer). If the file node has type CV_NODE_INT, then node-\>data.i is +returned. If the file node has type CV_NODE_REAL, then node-\>data.f is converted to an integer +and returned. Otherwise the error is reported. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE int cvReadInt( const CvFileNode* node, int default_value CV_DEFAULT(0) ) +{ + return !node ? default_value : + CV_NODE_IS_INT(node->tag) ? node->data.i : + CV_NODE_IS_REAL(node->tag) ? cvRound(node->data.f) : 0x7fffffff; +} + +/** @brief Finds a file node and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadInt. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE int cvReadIntByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, int default_value CV_DEFAULT(0) ) +{ + return cvReadInt( cvGetFileNodeByName( fs, map, name ), default_value ); +} + +/** @brief Retrieves a floating-point value from a file node. + +The function returns a floating-point value that is represented by the file node. If the file node +is NULL, the default_value is returned (thus, it is convenient to call the function right after +cvGetFileNode without checking for a NULL pointer). If the file node has type CV_NODE_REAL , +then node-\>data.f is returned. If the file node has type CV_NODE_INT , then node-:math:\>data.f +is converted to floating-point and returned. Otherwise the result is not determined. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE double cvReadReal( const CvFileNode* node, double default_value CV_DEFAULT(0.) ) +{ + return !node ? default_value : + CV_NODE_IS_INT(node->tag) ? (double)node->data.i : + CV_NODE_IS_REAL(node->tag) ? node->data.f : 1e300; +} + +/** @brief Finds a file node and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadReal . +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE double cvReadRealByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, double default_value CV_DEFAULT(0.) ) +{ + return cvReadReal( cvGetFileNodeByName( fs, map, name ), default_value ); +} + +/** @brief Retrieves a text string from a file node. + +The function returns a text string that is represented by the file node. If the file node is NULL, +the default_value is returned (thus, it is convenient to call the function right after +cvGetFileNode without checking for a NULL pointer). If the file node has type CV_NODE_STR , then +node-:math:\>data.str.ptr is returned. Otherwise the result is not determined. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE const char* cvReadString( const CvFileNode* node, + const char* default_value CV_DEFAULT(NULL) ) +{ + return !node ? default_value : CV_NODE_IS_STRING(node->tag) ? node->data.str.ptr : 0; +} + +/** @brief Finds a file node by its name and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadString . +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE const char* cvReadStringByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, const char* default_value CV_DEFAULT(NULL) ) +{ + return cvReadString( cvGetFileNodeByName( fs, map, name ), default_value ); +} + + +/** @brief Decodes an object and returns a pointer to it. + +The function decodes a user object (creates an object in a native representation from the file +storage subtree) and returns it. The object to be decoded must be an instance of a registered type +that supports the read method (see CvTypeInfo). The type of the object is determined by the type +name that is encoded in the file. If the object is a dynamic structure, it is created either in +memory storage and passed to cvOpenFileStorage or, if a NULL pointer was passed, in temporary +memory storage, which is released when cvReleaseFileStorage is called. Otherwise, if the object is +not a dynamic structure, it is created in a heap and should be released with a specialized function +or by using the generic cvRelease. +@param fs File storage +@param node The root object node +@param attributes Unused parameter + */ +CVAPI(void*) cvRead( CvFileStorage* fs, CvFileNode* node, + CvAttrList* attributes CV_DEFAULT(NULL)); + +/** @brief Finds an object by name and decodes it. + +The function is a simple superposition of cvGetFileNodeByName and cvRead. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param attributes Unused parameter + */ +CV_INLINE void* cvReadByName( CvFileStorage* fs, const CvFileNode* map, + const char* name, CvAttrList* attributes CV_DEFAULT(NULL) ) +{ + return cvRead( fs, cvGetFileNodeByName( fs, map, name ), attributes ); +} + + +/** @brief Initializes the file node sequence reader. + +The function initializes the sequence reader to read data from a file node. The initialized reader +can be then passed to cvReadRawDataSlice. +@param fs File storage +@param src The file node (a sequence) to read numbers from +@param reader Pointer to the sequence reader + */ +CVAPI(void) cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src, + CvSeqReader* reader ); + +/** @brief Initializes file node sequence reader. + +The function reads one or more elements from the file node, representing a sequence, to a +user-specified array. The total number of read sequence elements is a product of total and the +number of components in each array element. For example, if dt=2if, the function will read total\*3 +sequence elements. As with any sequence, some parts of the file node sequence can be skipped or read +repeatedly by repositioning the reader using cvSetSeqReaderPos. +@param fs File storage +@param reader The sequence reader. Initialize it with cvStartReadRawData . +@param count The number of elements to read +@param dst Pointer to the destination array +@param dt Specification of each array element. It has the same format as in cvWriteRawData . + */ +CVAPI(void) cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader, + int count, void* dst, const char* dt ); + +/** @brief Reads multiple numbers. + +The function reads elements from a file node that represents a sequence of scalars. +@param fs File storage +@param src The file node (a sequence) to read numbers from +@param dst Pointer to the destination array +@param dt Specification of each array element. It has the same format as in cvWriteRawData . + */ +CVAPI(void) cvReadRawData( const CvFileStorage* fs, const CvFileNode* src, + void* dst, const char* dt ); + +/** @brief Writes a file node to another file storage. + +The function writes a copy of a file node to file storage. Possible applications of the function are +merging several file storages into one and conversion between XML, YAML and JSON formats. +@param fs Destination file storage +@param new_node_name New name of the file node in the destination file storage. To keep the +existing name, use cvcvGetFileNodeName +@param node The written node +@param embed If the written node is a collection and this parameter is not zero, no extra level of +hierarchy is created. Instead, all the elements of node are written into the currently written +structure. Of course, map elements can only be embedded into another map, and sequence elements +can only be embedded into another sequence. + */ +CVAPI(void) cvWriteFileNode( CvFileStorage* fs, const char* new_node_name, + const CvFileNode* node, int embed ); + +/** @brief Returns the name of a file node. + +The function returns the name of a file node or NULL, if the file node does not have a name or if +node is NULL. +@param node File node + */ +CVAPI(const char*) cvGetFileNodeName( const CvFileNode* node ); + +/*********************************** Adding own types ***********************************/ + +/** @brief Registers a new type. + +The function registers a new type, which is described by info . The function creates a copy of the +structure, so the user should delete it after calling the function. +@param info Type info structure + */ +CVAPI(void) cvRegisterType( const CvTypeInfo* info ); + +/** @brief Unregisters the type. + +The function unregisters a type with a specified name. If the name is unknown, it is possible to +locate the type info by an instance of the type using cvTypeOf or by iterating the type list, +starting from cvFirstType, and then calling cvUnregisterType(info-\>typeName). +@param type_name Name of an unregistered type + */ +CVAPI(void) cvUnregisterType( const char* type_name ); + +/** @brief Returns the beginning of a type list. + +The function returns the first type in the list of registered types. Navigation through the list can +be done via the prev and next fields of the CvTypeInfo structure. + */ +CVAPI(CvTypeInfo*) cvFirstType(void); + +/** @brief Finds a type by its name. + +The function finds a registered type by its name. It returns NULL if there is no type with the +specified name. +@param type_name Type name + */ +CVAPI(CvTypeInfo*) cvFindType( const char* type_name ); + +/** @brief Returns the type of an object. + +The function finds the type of a given object. It iterates through the list of registered types and +calls the is_instance function/method for every type info structure with that object until one of +them returns non-zero or until the whole list has been traversed. In the latter case, the function +returns NULL. +@param struct_ptr The object pointer + */ +CVAPI(CvTypeInfo*) cvTypeOf( const void* struct_ptr ); + +/** @brief Releases an object. + +The function finds the type of a given object and calls release with the double pointer. +@param struct_ptr Double pointer to the object + */ +CVAPI(void) cvRelease( void** struct_ptr ); + +/** @brief Makes a clone of an object. + +The function finds the type of a given object and calls clone with the passed object. Of course, if +you know the object type, for example, struct_ptr is CvMat\*, it is faster to call the specific +function, like cvCloneMat. +@param struct_ptr The object to clone + */ +CVAPI(void*) cvClone( const void* struct_ptr ); + +/** @brief Saves an object to a file. + +The function saves an object to a file. It provides a simple interface to cvWrite . +@param filename File name +@param struct_ptr Object to save +@param name Optional object name. If it is NULL, the name will be formed from filename . +@param comment Optional comment to put in the beginning of the file +@param attributes Optional attributes passed to cvWrite + */ +CVAPI(void) cvSave( const char* filename, const void* struct_ptr, + const char* name CV_DEFAULT(NULL), + const char* comment CV_DEFAULT(NULL), + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Loads an object from a file. + +The function loads an object from a file. It basically reads the specified file, find the first +top-level node and calls cvRead for that node. If the file node does not have type information or +the type information can not be found by the type name, the function returns NULL. After the object +is loaded, the file storage is closed and all the temporary buffers are deleted. Thus, to load a +dynamic structure, such as a sequence, contour, or graph, one should pass a valid memory storage +destination to the function. +@param filename File name +@param memstorage Memory storage for dynamic structures, such as CvSeq or CvGraph . It is not used +for matrices or images. +@param name Optional object name. If it is NULL, the first top-level object in the storage will be +loaded. +@param real_name Optional output parameter that will contain the name of the loaded object +(useful if name=NULL ) + */ +CVAPI(void*) cvLoad( const char* filename, + CvMemStorage* memstorage CV_DEFAULT(NULL), + const char* name CV_DEFAULT(NULL), + const char** real_name CV_DEFAULT(NULL) ); + +/*********************************** Measuring Execution Time ***************************/ + +/** helper functions for RNG initialization and accurate time measurement: + uses internal clock counter on x86 */ +CVAPI(int64) cvGetTickCount( void ); +CVAPI(double) cvGetTickFrequency( void ); + +/*********************************** CPU capabilities ***********************************/ + +CVAPI(int) cvCheckHardwareSupport(int feature); + +/*********************************** Multi-Threading ************************************/ + +/** retrieve/set the number of threads used in OpenMP implementations */ +CVAPI(int) cvGetNumThreads( void ); +CVAPI(void) cvSetNumThreads( int threads CV_DEFAULT(0) ); +/** get index of the thread being executed */ +CVAPI(int) cvGetThreadNum( void ); + + +/********************************** Error Handling **************************************/ + +/** Get current OpenCV error status */ +CVAPI(int) cvGetErrStatus( void ); + +/** Sets error status silently */ +CVAPI(void) cvSetErrStatus( int status ); + +#define CV_ErrModeLeaf 0 /* Print error and exit program */ +#define CV_ErrModeParent 1 /* Print error and continue */ +#define CV_ErrModeSilent 2 /* Don't print and continue */ + +/** Retrieves current error processing mode */ +CVAPI(int) cvGetErrMode( void ); + +/** Sets error processing mode, returns previously used mode */ +CVAPI(int) cvSetErrMode( int mode ); + +/** Sets error status and performs some additional actions (displaying message box, + writing message to stderr, terminating application etc.) + depending on the current error mode */ +CVAPI(void) cvError( int status, const char* func_name, + const char* err_msg, const char* file_name, int line ); + +/** Retrieves textual description of the error given its code */ +CVAPI(const char*) cvErrorStr( int status ); + +/** Retrieves detailed information about the last error occurred */ +CVAPI(int) cvGetErrInfo( const char** errcode_desc, const char** description, + const char** filename, int* line ); + +/** Maps IPP error codes to the counterparts from OpenCV */ +CVAPI(int) cvErrorFromIppStatus( int ipp_status ); + +typedef int (CV_CDECL *CvErrorCallback)( int status, const char* func_name, + const char* err_msg, const char* file_name, int line, void* userdata ); + +/** Assigns a new error-handling function */ +CVAPI(CvErrorCallback) cvRedirectError( CvErrorCallback error_handler, + void* userdata CV_DEFAULT(NULL), + void** prev_userdata CV_DEFAULT(NULL) ); + +/** Output nothing */ +CVAPI(int) cvNulDevReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +/** Output to console(fprintf(stderr,...)) */ +CVAPI(int) cvStdErrReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +/** Output to MessageBox(WIN32) */ +CVAPI(int) cvGuiBoxReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +#define OPENCV_ERROR(status,func,context) \ +cvError((status),(func),(context),__FILE__,__LINE__) + +#define OPENCV_ASSERT(expr,func,context) \ +{if (! (expr)) \ +{OPENCV_ERROR(CV_StsInternal,(func),(context));}} + +#define OPENCV_CALL( Func ) \ +{ \ +Func; \ +} + + +/** CV_FUNCNAME macro defines icvFuncName constant which is used by CV_ERROR macro */ +#ifdef CV_NO_FUNC_NAMES +#define CV_FUNCNAME( Name ) +#define cvFuncName "" +#else +#define CV_FUNCNAME( Name ) \ +static char cvFuncName[] = Name +#endif + + +/** + CV_ERROR macro unconditionally raises error with passed code and message. + After raising error, control will be transferred to the exit label. + */ +#define CV_ERROR( Code, Msg ) \ +{ \ + cvError( (Code), cvFuncName, Msg, __FILE__, __LINE__ ); \ + __CV_EXIT__; \ +} + +/** + CV_CHECK macro checks error status after CV (or IPL) + function call. If error detected, control will be transferred to the exit + label. + */ +#define CV_CHECK() \ +{ \ + if( cvGetErrStatus() < 0 ) \ + CV_ERROR( CV_StsBackTrace, "Inner function failed." ); \ +} + + +/** + CV_CALL macro calls CV (or IPL) function, checks error status and + signals a error if the function failed. Useful in "parent node" + error processing mode + */ +#define CV_CALL( Func ) \ +{ \ + Func; \ + CV_CHECK(); \ +} + + +/** Runtime assertion macro */ +#define CV_ASSERT( Condition ) \ +{ \ + if( !(Condition) ) \ + CV_ERROR( CV_StsInternal, "Assertion: " #Condition " failed" ); \ +} + +#define __CV_BEGIN__ { +#define __CV_END__ goto exit; exit: ; } +#define __CV_EXIT__ goto exit + +/** @} core_c */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef __cplusplus + +//! @addtogroup core_c_glue +//! @{ + +//! class for automatic module/RTTI data registration/unregistration +struct CV_EXPORTS CvType +{ + CvType( const char* type_name, + CvIsInstanceFunc is_instance, CvReleaseFunc release=0, + CvReadFunc read=0, CvWriteFunc write=0, CvCloneFunc clone=0 ); + ~CvType(); + CvTypeInfo* info; + + static CvTypeInfo* first; + static CvTypeInfo* last; +}; + +//! @} + +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @addtogroup core_c_glue +//! @{ + +/////////////////////////////////////////// glue /////////////////////////////////////////// + +//! converts array (CvMat or IplImage) to cv::Mat +CV_EXPORTS Mat cvarrToMat(const CvArr* arr, bool copyData=false, + bool allowND=true, int coiMode=0, + AutoBuffer* buf=0); + +static inline Mat cvarrToMatND(const CvArr* arr, bool copyData=false, int coiMode=0) +{ + return cvarrToMat(arr, copyData, true, coiMode); +} + + +//! extracts Channel of Interest from CvMat or IplImage and makes cv::Mat out of it. +CV_EXPORTS void extractImageCOI(const CvArr* arr, OutputArray coiimg, int coi=-1); +//! inserts single-channel cv::Mat into a multi-channel CvMat or IplImage +CV_EXPORTS void insertImageCOI(InputArray coiimg, CvArr* arr, int coi=-1); + + + +////// specialized implementations of DefaultDeleter::operator() for classic OpenCV types ////// + +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMat* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(IplImage* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMatND* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvSparseMat* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMemStorage* obj) const; + +////////////// convenient wrappers for operating old-style dynamic structures ////////////// + +template class SeqIterator; + +typedef Ptr MemStorage; + +/*! + Template Sequence Class derived from CvSeq + + The class provides more convenient access to sequence elements, + STL-style operations and iterators. + + \note The class is targeted for simple data types, + i.e. no constructors or destructors + are called for the sequence elements. +*/ +template class Seq +{ +public: + typedef SeqIterator<_Tp> iterator; + typedef SeqIterator<_Tp> const_iterator; + + //! the default constructor + Seq(); + //! the constructor for wrapping CvSeq structure. The real element type in CvSeq should match _Tp. + Seq(const CvSeq* seq); + //! creates the empty sequence that resides in the specified storage + Seq(MemStorage& storage, int headerSize = sizeof(CvSeq)); + //! returns read-write reference to the specified element + _Tp& operator [](int idx); + //! returns read-only reference to the specified element + const _Tp& operator[](int idx) const; + //! returns iterator pointing to the beginning of the sequence + SeqIterator<_Tp> begin() const; + //! returns iterator pointing to the element following the last sequence element + SeqIterator<_Tp> end() const; + //! returns the number of elements in the sequence + size_t size() const; + //! returns the type of sequence elements (CV_8UC1 ... CV_64FC(CV_CN_MAX) ...) + int type() const; + //! returns the depth of sequence elements (CV_8U ... CV_64F) + int depth() const; + //! returns the number of channels in each sequence element + int channels() const; + //! returns the size of each sequence element + size_t elemSize() const; + //! returns index of the specified sequence element + size_t index(const _Tp& elem) const; + //! appends the specified element to the end of the sequence + void push_back(const _Tp& elem); + //! appends the specified element to the front of the sequence + void push_front(const _Tp& elem); + //! appends zero or more elements to the end of the sequence + void push_back(const _Tp* elems, size_t count); + //! appends zero or more elements to the front of the sequence + void push_front(const _Tp* elems, size_t count); + //! inserts the specified element to the specified position + void insert(int idx, const _Tp& elem); + //! inserts zero or more elements to the specified position + void insert(int idx, const _Tp* elems, size_t count); + //! removes element at the specified position + void remove(int idx); + //! removes the specified subsequence + void remove(const Range& r); + + //! returns reference to the first sequence element + _Tp& front(); + //! returns read-only reference to the first sequence element + const _Tp& front() const; + //! returns reference to the last sequence element + _Tp& back(); + //! returns read-only reference to the last sequence element + const _Tp& back() const; + //! returns true iff the sequence contains no elements + bool empty() const; + + //! removes all the elements from the sequence + void clear(); + //! removes the first element from the sequence + void pop_front(); + //! removes the last element from the sequence + void pop_back(); + //! removes zero or more elements from the beginning of the sequence + void pop_front(_Tp* elems, size_t count); + //! removes zero or more elements from the end of the sequence + void pop_back(_Tp* elems, size_t count); + + //! copies the whole sequence or the sequence slice to the specified vector + void copyTo(std::vector<_Tp>& vec, const Range& range=Range::all()) const; + //! returns the vector containing all the sequence elements + operator std::vector<_Tp>() const; + + CvSeq* seq; +}; + + +/*! + STL-style Sequence Iterator inherited from the CvSeqReader structure +*/ +template class SeqIterator : public CvSeqReader +{ +public: + //! the default constructor + SeqIterator(); + //! the constructor setting the iterator to the beginning or to the end of the sequence + SeqIterator(const Seq<_Tp>& seq, bool seekEnd=false); + //! positions the iterator within the sequence + void seek(size_t pos); + //! reports the current iterator position + size_t tell() const; + //! returns reference to the current sequence element + _Tp& operator *(); + //! returns read-only reference to the current sequence element + const _Tp& operator *() const; + //! moves iterator to the next sequence element + SeqIterator& operator ++(); + //! moves iterator to the next sequence element + SeqIterator operator ++(int) const; + //! moves iterator to the previous sequence element + SeqIterator& operator --(); + //! moves iterator to the previous sequence element + SeqIterator operator --(int) const; + + //! moves iterator forward by the specified offset (possibly negative) + SeqIterator& operator +=(int); + //! moves iterator backward by the specified offset (possibly negative) + SeqIterator& operator -=(int); + + // this is index of the current element module seq->total*2 + // (to distinguish between 0 and seq->total) + int index; +}; + + + +// bridge C++ => C Seq API +CV_EXPORTS schar* seqPush( CvSeq* seq, const void* element=0); +CV_EXPORTS schar* seqPushFront( CvSeq* seq, const void* element=0); +CV_EXPORTS void seqPop( CvSeq* seq, void* element=0); +CV_EXPORTS void seqPopFront( CvSeq* seq, void* element=0); +CV_EXPORTS void seqPopMulti( CvSeq* seq, void* elements, + int count, int in_front=0 ); +CV_EXPORTS void seqRemove( CvSeq* seq, int index ); +CV_EXPORTS void clearSeq( CvSeq* seq ); +CV_EXPORTS schar* getSeqElem( const CvSeq* seq, int index ); +CV_EXPORTS void seqRemoveSlice( CvSeq* seq, CvSlice slice ); +CV_EXPORTS void seqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr ); + +template inline Seq<_Tp>::Seq() : seq(0) {} +template inline Seq<_Tp>::Seq( const CvSeq* _seq ) : seq((CvSeq*)_seq) +{ + CV_Assert(!_seq || _seq->elem_size == sizeof(_Tp)); +} + +template inline Seq<_Tp>::Seq( MemStorage& storage, + int headerSize ) +{ + CV_Assert(headerSize >= (int)sizeof(CvSeq)); + seq = cvCreateSeq(DataType<_Tp>::type, headerSize, sizeof(_Tp), storage); +} + +template inline _Tp& Seq<_Tp>::operator [](int idx) +{ return *(_Tp*)getSeqElem(seq, idx); } + +template inline const _Tp& Seq<_Tp>::operator [](int idx) const +{ return *(_Tp*)getSeqElem(seq, idx); } + +template inline SeqIterator<_Tp> Seq<_Tp>::begin() const +{ return SeqIterator<_Tp>(*this); } + +template inline SeqIterator<_Tp> Seq<_Tp>::end() const +{ return SeqIterator<_Tp>(*this, true); } + +template inline size_t Seq<_Tp>::size() const +{ return seq ? seq->total : 0; } + +template inline int Seq<_Tp>::type() const +{ return seq ? CV_MAT_TYPE(seq->flags) : 0; } + +template inline int Seq<_Tp>::depth() const +{ return seq ? CV_MAT_DEPTH(seq->flags) : 0; } + +template inline int Seq<_Tp>::channels() const +{ return seq ? CV_MAT_CN(seq->flags) : 0; } + +template inline size_t Seq<_Tp>::elemSize() const +{ return seq ? seq->elem_size : 0; } + +template inline size_t Seq<_Tp>::index(const _Tp& elem) const +{ return cvSeqElemIdx(seq, &elem); } + +template inline void Seq<_Tp>::push_back(const _Tp& elem) +{ cvSeqPush(seq, &elem); } + +template inline void Seq<_Tp>::push_front(const _Tp& elem) +{ cvSeqPushFront(seq, &elem); } + +template inline void Seq<_Tp>::push_back(const _Tp* elem, size_t count) +{ cvSeqPushMulti(seq, elem, (int)count, 0); } + +template inline void Seq<_Tp>::push_front(const _Tp* elem, size_t count) +{ cvSeqPushMulti(seq, elem, (int)count, 1); } + +template inline _Tp& Seq<_Tp>::back() +{ return *(_Tp*)getSeqElem(seq, -1); } + +template inline const _Tp& Seq<_Tp>::back() const +{ return *(const _Tp*)getSeqElem(seq, -1); } + +template inline _Tp& Seq<_Tp>::front() +{ return *(_Tp*)getSeqElem(seq, 0); } + +template inline const _Tp& Seq<_Tp>::front() const +{ return *(const _Tp*)getSeqElem(seq, 0); } + +template inline bool Seq<_Tp>::empty() const +{ return !seq || seq->total == 0; } + +template inline void Seq<_Tp>::clear() +{ if(seq) clearSeq(seq); } + +template inline void Seq<_Tp>::pop_back() +{ seqPop(seq); } + +template inline void Seq<_Tp>::pop_front() +{ seqPopFront(seq); } + +template inline void Seq<_Tp>::pop_back(_Tp* elem, size_t count) +{ seqPopMulti(seq, elem, (int)count, 0); } + +template inline void Seq<_Tp>::pop_front(_Tp* elem, size_t count) +{ seqPopMulti(seq, elem, (int)count, 1); } + +template inline void Seq<_Tp>::insert(int idx, const _Tp& elem) +{ seqInsert(seq, idx, &elem); } + +template inline void Seq<_Tp>::insert(int idx, const _Tp* elems, size_t count) +{ + CvMat m = cvMat(1, count, DataType<_Tp>::type, elems); + seqInsertSlice(seq, idx, &m); +} + +template inline void Seq<_Tp>::remove(int idx) +{ seqRemove(seq, idx); } + +template inline void Seq<_Tp>::remove(const Range& r) +{ seqRemoveSlice(seq, cvSlice(r.start, r.end)); } + +template inline void Seq<_Tp>::copyTo(std::vector<_Tp>& vec, const Range& range) const +{ + size_t len = !seq ? 0 : range == Range::all() ? seq->total : range.end - range.start; + vec.resize(len); + if( seq && len ) + cvCvtSeqToArray(seq, &vec[0], cvSlice(range)); +} + +template inline Seq<_Tp>::operator std::vector<_Tp>() const +{ + std::vector<_Tp> vec; + copyTo(vec); + return vec; +} + +template inline SeqIterator<_Tp>::SeqIterator() +{ memset(this, 0, sizeof(*this)); } + +template inline SeqIterator<_Tp>::SeqIterator(const Seq<_Tp>& _seq, bool seekEnd) +{ + cvStartReadSeq(_seq.seq, this); + index = seekEnd ? _seq.seq->total : 0; +} + +template inline void SeqIterator<_Tp>::seek(size_t pos) +{ + cvSetSeqReaderPos(this, (int)pos, false); + index = pos; +} + +template inline size_t SeqIterator<_Tp>::tell() const +{ return index; } + +template inline _Tp& SeqIterator<_Tp>::operator *() +{ return *(_Tp*)ptr; } + +template inline const _Tp& SeqIterator<_Tp>::operator *() const +{ return *(const _Tp*)ptr; } + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator ++() +{ + CV_NEXT_SEQ_ELEM(sizeof(_Tp), *this); + if( ++index >= seq->total*2 ) + index = 0; + return *this; +} + +template inline SeqIterator<_Tp> SeqIterator<_Tp>::operator ++(int) const +{ + SeqIterator<_Tp> it = *this; + ++*this; + return it; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator --() +{ + CV_PREV_SEQ_ELEM(sizeof(_Tp), *this); + if( --index < 0 ) + index = seq->total*2-1; + return *this; +} + +template inline SeqIterator<_Tp> SeqIterator<_Tp>::operator --(int) const +{ + SeqIterator<_Tp> it = *this; + --*this; + return it; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator +=(int delta) +{ + cvSetSeqReaderPos(this, delta, 1); + index += delta; + int n = seq->total*2; + if( index < 0 ) + index += n; + if( index >= n ) + index -= n; + return *this; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator -=(int delta) +{ + return (*this += -delta); +} + +template inline ptrdiff_t operator - (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + ptrdiff_t delta = a.index - b.index, n = a.seq->total; + if( delta > n || delta < -n ) + delta += delta < 0 ? n : -n; + return delta; +} + +template inline bool operator == (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + return a.seq == b.seq && a.index == b.index; +} + +template inline bool operator != (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + return !(a == b); +} + +//! @} + +} // cv + +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.hpp new file mode 100644 index 0000000..7d7bb62 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.hpp @@ -0,0 +1,1049 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_HPP +#define OPENCV_CORE_CUDA_HPP + +#ifndef __cplusplus +# error cuda.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "opencv2/core/cuda_types.hpp" + +/** + @defgroup cuda CUDA-accelerated Computer Vision + @{ + @defgroup cudacore Core part + @{ + @defgroup cudacore_init Initialization and Information + @defgroup cudacore_struct Data Structures + @} + @} + */ + +namespace cv { namespace cuda { + +//! @addtogroup cudacore_struct +//! @{ + +//=================================================================================== +// GpuMat +//=================================================================================== + +/** @brief Base storage class for GPU memory with reference counting. + +Its interface matches the Mat interface with the following limitations: + +- no arbitrary dimensions support (only 2D) +- no functions that return references to their data (because references on GPU are not valid for + CPU) +- no expression templates technique support + +Beware that the latter limitation may lead to overloaded matrix operators that cause memory +allocations. The GpuMat class is convertible to cuda::PtrStepSz and cuda::PtrStep so it can be +passed directly to the kernel. + +@note In contrast with Mat, in most cases GpuMat::isContinuous() == false . This means that rows are +aligned to a size depending on the hardware. Single-row GpuMat is always a continuous matrix. + +@note You are not recommended to leave static or global GpuMat variables allocated, that is, to rely +on its destructor. The destruction order of such variables and CUDA context is undefined. GPU memory +release function returns error if the CUDA context has been destroyed before. + +Some member functions are described as a "Blocking Call" while some are described as a +"Non-Blocking Call". Blocking functions are synchronous to host. It is guaranteed that the GPU +operation is finished when the function returns. However, non-blocking functions are asynchronous to +host. Those functions may return even if the GPU operation is not finished. + +Compared to their blocking counterpart, non-blocking functions accept Stream as an additional +argument. If a non-default stream is passed, the GPU operation may overlap with operations in other +streams. + +@sa Mat + */ +class CV_EXPORTS GpuMat +{ +public: + class CV_EXPORTS Allocator + { + public: + virtual ~Allocator() {} + + // allocator must fill data, step and refcount fields + virtual bool allocate(GpuMat* mat, int rows, int cols, size_t elemSize) = 0; + virtual void free(GpuMat* mat) = 0; + }; + + //! default allocator + static Allocator* defaultAllocator(); + static void setDefaultAllocator(Allocator* allocator); + + //! default constructor + explicit GpuMat(Allocator* allocator = defaultAllocator()); + + //! constructs GpuMat of the specified size and type + GpuMat(int rows, int cols, int type, Allocator* allocator = defaultAllocator()); + GpuMat(Size size, int type, Allocator* allocator = defaultAllocator()); + + //! constructs GpuMat and fills it with the specified value _s + GpuMat(int rows, int cols, int type, Scalar s, Allocator* allocator = defaultAllocator()); + GpuMat(Size size, int type, Scalar s, Allocator* allocator = defaultAllocator()); + + //! copy constructor + GpuMat(const GpuMat& m); + + //! constructor for GpuMat headers pointing to user-allocated data + GpuMat(int rows, int cols, int type, void* data, size_t step = Mat::AUTO_STEP); + GpuMat(Size size, int type, void* data, size_t step = Mat::AUTO_STEP); + + //! creates a GpuMat header for a part of the bigger matrix + GpuMat(const GpuMat& m, Range rowRange, Range colRange); + GpuMat(const GpuMat& m, Rect roi); + + //! builds GpuMat from host memory (Blocking call) + explicit GpuMat(InputArray arr, Allocator* allocator = defaultAllocator()); + + //! destructor - calls release() + ~GpuMat(); + + //! assignment operators + GpuMat& operator =(const GpuMat& m); + + //! allocates new GpuMat data unless the GpuMat already has specified size and type + void create(int rows, int cols, int type); + void create(Size size, int type); + + //! decreases reference counter, deallocate the data when reference counter reaches 0 + void release(); + + //! swaps with other smart pointer + void swap(GpuMat& mat); + + /** @brief Performs data upload to GpuMat (Blocking call) + + This function copies data from host memory to device memory. As being a blocking call, it is + guaranteed that the copy operation is finished when this function returns. + */ + void upload(InputArray arr); + + /** @brief Performs data upload to GpuMat (Non-Blocking call) + + This function copies data from host memory to device memory. As being a non-blocking call, this + function may return even if the copy operation is not finished. + + The copy operation may be overlapped with operations in other non-default streams if \p stream is + not the default stream and \p dst is HostMem allocated with HostMem::PAGE_LOCKED option. + */ + void upload(InputArray arr, Stream& stream); + + /** @brief Performs data download from GpuMat (Blocking call) + + This function copies data from device memory to host memory. As being a blocking call, it is + guaranteed that the copy operation is finished when this function returns. + */ + void download(OutputArray dst) const; + + /** @brief Performs data download from GpuMat (Non-Blocking call) + + This function copies data from device memory to host memory. As being a non-blocking call, this + function may return even if the copy operation is not finished. + + The copy operation may be overlapped with operations in other non-default streams if \p stream is + not the default stream and \p dst is HostMem allocated with HostMem::PAGE_LOCKED option. + */ + void download(OutputArray dst, Stream& stream) const; + + //! returns deep copy of the GpuMat, i.e. the data is copied + GpuMat clone() const; + + //! copies the GpuMat content to device memory (Blocking call) + void copyTo(OutputArray dst) const; + + //! copies the GpuMat content to device memory (Non-Blocking call) + void copyTo(OutputArray dst, Stream& stream) const; + + //! copies those GpuMat elements to "m" that are marked with non-zero mask elements (Blocking call) + void copyTo(OutputArray dst, InputArray mask) const; + + //! copies those GpuMat elements to "m" that are marked with non-zero mask elements (Non-Blocking call) + void copyTo(OutputArray dst, InputArray mask, Stream& stream) const; + + //! sets some of the GpuMat elements to s (Blocking call) + GpuMat& setTo(Scalar s); + + //! sets some of the GpuMat elements to s (Non-Blocking call) + GpuMat& setTo(Scalar s, Stream& stream); + + //! sets some of the GpuMat elements to s, according to the mask (Blocking call) + GpuMat& setTo(Scalar s, InputArray mask); + + //! sets some of the GpuMat elements to s, according to the mask (Non-Blocking call) + GpuMat& setTo(Scalar s, InputArray mask, Stream& stream); + + //! converts GpuMat to another datatype (Blocking call) + void convertTo(OutputArray dst, int rtype) const; + + //! converts GpuMat to another datatype (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, Stream& stream) const; + + //! converts GpuMat to another datatype with scaling (Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, double beta = 0.0) const; + + //! converts GpuMat to another datatype with scaling (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const; + + //! converts GpuMat to another datatype with scaling (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, double beta, Stream& stream) const; + + void assignTo(GpuMat& m, int type=-1) const; + + //! returns pointer to y-th row + uchar* ptr(int y = 0); + const uchar* ptr(int y = 0) const; + + //! template version of the above method + template _Tp* ptr(int y = 0); + template const _Tp* ptr(int y = 0) const; + + template operator PtrStepSz<_Tp>() const; + template operator PtrStep<_Tp>() const; + + //! returns a new GpuMat header for the specified row + GpuMat row(int y) const; + + //! returns a new GpuMat header for the specified column + GpuMat col(int x) const; + + //! ... for the specified row span + GpuMat rowRange(int startrow, int endrow) const; + GpuMat rowRange(Range r) const; + + //! ... for the specified column span + GpuMat colRange(int startcol, int endcol) const; + GpuMat colRange(Range r) const; + + //! extracts a rectangular sub-GpuMat (this is a generalized form of row, rowRange etc.) + GpuMat operator ()(Range rowRange, Range colRange) const; + GpuMat operator ()(Rect roi) const; + + //! creates alternative GpuMat header for the same data, with different + //! number of channels and/or different number of rows + GpuMat reshape(int cn, int rows = 0) const; + + //! locates GpuMat header within a parent GpuMat + void locateROI(Size& wholeSize, Point& ofs) const; + + //! moves/resizes the current GpuMat ROI inside the parent GpuMat + GpuMat& adjustROI(int dtop, int dbottom, int dleft, int dright); + + //! returns true iff the GpuMat data is continuous + //! (i.e. when there are no gaps between successive rows) + bool isContinuous() const; + + //! returns element size in bytes + size_t elemSize() const; + + //! returns the size of element channel in bytes + size_t elemSize1() const; + + //! returns element type + int type() const; + + //! returns element type + int depth() const; + + //! returns number of channels + int channels() const; + + //! returns step/elemSize1() + size_t step1() const; + + //! returns GpuMat size : width == number of columns, height == number of rows + Size size() const; + + //! returns true if GpuMat data is NULL + bool empty() const; + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + + //! the number of rows and columns + int rows, cols; + + //! a distance between successive rows in bytes; includes the gap if any + size_t step; + + //! pointer to the data + uchar* data; + + //! pointer to the reference counter; + //! when GpuMat points to user-allocated data, the pointer is NULL + int* refcount; + + //! helper fields used in locateROI and adjustROI + uchar* datastart; + const uchar* dataend; + + //! allocator + Allocator* allocator; +}; + +/** @brief Creates a continuous matrix. + +@param rows Row count. +@param cols Column count. +@param type Type of the matrix. +@param arr Destination matrix. This parameter changes only if it has a proper type and area ( +\f$\texttt{rows} \times \texttt{cols}\f$ ). + +Matrix is called continuous if its elements are stored continuously, that is, without gaps at the +end of each row. + */ +CV_EXPORTS void createContinuous(int rows, int cols, int type, OutputArray arr); + +/** @brief Ensures that the size of a matrix is big enough and the matrix has a proper type. + +@param rows Minimum desired number of rows. +@param cols Minimum desired number of columns. +@param type Desired matrix type. +@param arr Destination matrix. + +The function does not reallocate memory if the matrix has proper attributes already. + */ +CV_EXPORTS void ensureSizeIsEnough(int rows, int cols, int type, OutputArray arr); + +/** @brief BufferPool for use with CUDA streams + +BufferPool utilizes Stream's allocator to create new buffers for GpuMat's. It is +only useful when enabled with #setBufferPoolUsage. + +@code + setBufferPoolUsage(true); +@endcode + +@note #setBufferPoolUsage must be called \em before any Stream declaration. + +Users may specify custom allocator for Stream and may implement their own stream based +functions utilizing the same underlying GPU memory management. + +If custom allocator is not specified, BufferPool utilizes StackAllocator by +default. StackAllocator allocates a chunk of GPU device memory beforehand, +and when GpuMat is declared later on, it is given the pre-allocated memory. +This kind of strategy reduces the number of calls for memory allocating APIs +such as cudaMalloc or cudaMallocPitch. + +Below is an example that utilizes BufferPool with StackAllocator: + +@code + #include + + using namespace cv; + using namespace cv::cuda + + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + setBufferPoolConfig(getDevice(), 1024 * 1024 * 64, 2); // Allocate 64 MB, 2 stacks (default is 10 MB, 5 stacks) + + Stream stream1, stream2; // Each stream uses 1 stack + BufferPool pool1(stream1), pool2(stream2); + + GpuMat d_src1 = pool1.getBuffer(4096, 4096, CV_8UC1); // 16MB + GpuMat d_dst1 = pool1.getBuffer(4096, 4096, CV_8UC3); // 48MB, pool1 is now full + + GpuMat d_src2 = pool2.getBuffer(1024, 1024, CV_8UC1); // 1MB + GpuMat d_dst2 = pool2.getBuffer(1024, 1024, CV_8UC3); // 3MB + + cvtColor(d_src1, d_dst1, CV_GRAY2BGR, 0, stream1); + cvtColor(d_src2, d_dst2, CV_GRAY2BGR, 0, stream2); + } +@endcode + +If we allocate another GpuMat on pool1 in the above example, it will be carried out by +the DefaultAllocator since the stack for pool1 is full. + +@code + GpuMat d_add1 = pool1.getBuffer(1024, 1024, CV_8UC1); // Stack for pool1 is full, memory is allocated with DefaultAllocator +@endcode + +If a third stream is declared in the above example, allocating with #getBuffer +within that stream will also be carried out by the DefaultAllocator because we've run out of +stacks. + +@code + Stream stream3; // Only 2 stacks were allocated, we've run out of stacks + BufferPool pool3(stream3); + GpuMat d_src3 = pool3.getBuffer(1024, 1024, CV_8UC1); // Memory is allocated with DefaultAllocator +@endcode + +@warning When utilizing StackAllocator, deallocation order is important. + +Just like a stack, deallocation must be done in LIFO order. Below is an example of +erroneous usage that violates LIFO rule. If OpenCV is compiled in Debug mode, this +sample code will emit CV_Assert error. + +@code + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + Stream stream; // A default size (10 MB) stack is allocated to this stream + BufferPool pool(stream); + + GpuMat mat1 = pool.getBuffer(1024, 1024, CV_8UC1); // Allocate mat1 (1MB) + GpuMat mat2 = pool.getBuffer(1024, 1024, CV_8UC1); // Allocate mat2 (1MB) + + mat1.release(); // erroneous usage : mat2 must be deallocated before mat1 + } +@endcode + +Since C++ local variables are destroyed in the reverse order of construction, +the code sample below satisfies the LIFO rule. Local GpuMat's are deallocated +and the corresponding memory is automatically returned to the pool for later usage. + +@code + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + setBufferPoolConfig(getDevice(), 1024 * 1024 * 64, 2); // Allocate 64 MB, 2 stacks (default is 10 MB, 5 stacks) + + Stream stream1, stream2; // Each stream uses 1 stack + BufferPool pool1(stream1), pool2(stream2); + + for (int i = 0; i < 10; i++) + { + GpuMat d_src1 = pool1.getBuffer(4096, 4096, CV_8UC1); // 16MB + GpuMat d_dst1 = pool1.getBuffer(4096, 4096, CV_8UC3); // 48MB, pool1 is now full + + GpuMat d_src2 = pool2.getBuffer(1024, 1024, CV_8UC1); // 1MB + GpuMat d_dst2 = pool2.getBuffer(1024, 1024, CV_8UC3); // 3MB + + d_src1.setTo(Scalar(i), stream1); + d_src2.setTo(Scalar(i), stream2); + + cvtColor(d_src1, d_dst1, CV_GRAY2BGR, 0, stream1); + cvtColor(d_src2, d_dst2, CV_GRAY2BGR, 0, stream2); + // The order of destruction of the local variables is: + // d_dst2 => d_src2 => d_dst1 => d_src1 + // LIFO rule is satisfied, this code runs without error + } + } +@endcode + */ +class CV_EXPORTS BufferPool +{ +public: + + //! Gets the BufferPool for the given stream. + explicit BufferPool(Stream& stream); + + //! Allocates a new GpuMat of given size and type. + GpuMat getBuffer(int rows, int cols, int type); + + //! Allocates a new GpuMat of given size and type. + GpuMat getBuffer(Size size, int type) { return getBuffer(size.height, size.width, type); } + + //! Returns the allocator associated with the stream. + Ptr getAllocator() const { return allocator_; } + +private: + Ptr allocator_; +}; + +//! BufferPool management (must be called before Stream creation) +CV_EXPORTS void setBufferPoolUsage(bool on); +CV_EXPORTS void setBufferPoolConfig(int deviceId, size_t stackSize, int stackCount); + +//=================================================================================== +// HostMem +//=================================================================================== + +/** @brief Class with reference counting wrapping special memory type allocation functions from CUDA. + +Its interface is also Mat-like but with additional memory type parameters. + +- **PAGE_LOCKED** sets a page locked memory type used commonly for fast and asynchronous + uploading/downloading data from/to GPU. +- **SHARED** specifies a zero copy memory allocation that enables mapping the host memory to GPU + address space, if supported. +- **WRITE_COMBINED** sets the write combined buffer that is not cached by CPU. Such buffers are + used to supply GPU with data when GPU only reads it. The advantage is a better CPU cache + utilization. + +@note Allocation size of such memory types is usually limited. For more details, see *CUDA 2.2 +Pinned Memory APIs* document or *CUDA C Programming Guide*. + */ +class CV_EXPORTS HostMem +{ +public: + enum AllocType { PAGE_LOCKED = 1, SHARED = 2, WRITE_COMBINED = 4 }; + + static MatAllocator* getAllocator(AllocType alloc_type = PAGE_LOCKED); + + explicit HostMem(AllocType alloc_type = PAGE_LOCKED); + + HostMem(const HostMem& m); + + HostMem(int rows, int cols, int type, AllocType alloc_type = PAGE_LOCKED); + HostMem(Size size, int type, AllocType alloc_type = PAGE_LOCKED); + + //! creates from host memory with coping data + explicit HostMem(InputArray arr, AllocType alloc_type = PAGE_LOCKED); + + ~HostMem(); + + HostMem& operator =(const HostMem& m); + + //! swaps with other smart pointer + void swap(HostMem& b); + + //! returns deep copy of the matrix, i.e. the data is copied + HostMem clone() const; + + //! allocates new matrix data unless the matrix already has specified size and type. + void create(int rows, int cols, int type); + void create(Size size, int type); + + //! creates alternative HostMem header for the same data, with different + //! number of channels and/or different number of rows + HostMem reshape(int cn, int rows = 0) const; + + //! decrements reference counter and released memory if needed. + void release(); + + //! returns matrix header with disabled reference counting for HostMem data. + Mat createMatHeader() const; + + /** @brief Maps CPU memory to GPU address space and creates the cuda::GpuMat header without reference counting + for it. + + This can be done only if memory was allocated with the SHARED flag and if it is supported by the + hardware. Laptops often share video and CPU memory, so address spaces can be mapped, which + eliminates an extra copy. + */ + GpuMat createGpuMatHeader() const; + + // Please see cv::Mat for descriptions + bool isContinuous() const; + size_t elemSize() const; + size_t elemSize1() const; + int type() const; + int depth() const; + int channels() const; + size_t step1() const; + Size size() const; + bool empty() const; + + // Please see cv::Mat for descriptions + int flags; + int rows, cols; + size_t step; + + uchar* data; + int* refcount; + + uchar* datastart; + const uchar* dataend; + + AllocType alloc_type; +}; + +/** @brief Page-locks the memory of matrix and maps it for the device(s). + +@param m Input matrix. + */ +CV_EXPORTS void registerPageLocked(Mat& m); + +/** @brief Unmaps the memory of matrix and makes it pageable again. + +@param m Input matrix. + */ +CV_EXPORTS void unregisterPageLocked(Mat& m); + +//=================================================================================== +// Stream +//=================================================================================== + +/** @brief This class encapsulates a queue of asynchronous calls. + +@note Currently, you may face problems if an operation is enqueued twice with different data. Some +functions use the constant GPU memory, and next call may update the memory before the previous one +has been finished. But calling different operations asynchronously is safe because each operation +has its own constant buffer. Memory copy/upload/download/set operations to the buffers you hold are +also safe. + +@note The Stream class is not thread-safe. Please use different Stream objects for different CPU threads. + +@code +void thread1() +{ + cv::cuda::Stream stream1; + cv::cuda::func1(..., stream1); +} + +void thread2() +{ + cv::cuda::Stream stream2; + cv::cuda::func2(..., stream2); +} +@endcode + +@note By default all CUDA routines are launched in Stream::Null() object, if the stream is not specified by user. +In multi-threading environment the stream objects must be passed explicitly (see previous note). + */ +class CV_EXPORTS Stream +{ + typedef void (Stream::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + +public: + typedef void (*StreamCallback)(int status, void* userData); + + //! creates a new asynchronous stream + Stream(); + + //! creates a new asynchronous stream with custom allocator + Stream(const Ptr& allocator); + + /** @brief Returns true if the current stream queue is finished. Otherwise, it returns false. + */ + bool queryIfComplete() const; + + /** @brief Blocks the current CPU thread until all operations in the stream are complete. + */ + void waitForCompletion(); + + /** @brief Makes a compute stream wait on an event. + */ + void waitEvent(const Event& event); + + /** @brief Adds a callback to be called on the host after all currently enqueued items in the stream have + completed. + + @note Callbacks must not make any CUDA API calls. Callbacks must not perform any synchronization + that may depend on outstanding device work or other callbacks that are not mandated to run earlier. + Callbacks without a mandated order (in independent streams) execute in undefined order and may be + serialized. + */ + void enqueueHostCallback(StreamCallback callback, void* userData); + + //! return Stream object for default CUDA stream + static Stream& Null(); + + //! returns true if stream object is not default (!= 0) + operator bool_type() const; + + class Impl; + +private: + Ptr impl_; + Stream(const Ptr& impl); + + friend struct StreamAccessor; + friend class BufferPool; + friend class DefaultDeviceInitializer; +}; + +class CV_EXPORTS Event +{ +public: + enum CreateFlags + { + DEFAULT = 0x00, /**< Default event flag */ + BLOCKING_SYNC = 0x01, /**< Event uses blocking synchronization */ + DISABLE_TIMING = 0x02, /**< Event will not record timing data */ + INTERPROCESS = 0x04 /**< Event is suitable for interprocess use. DisableTiming must be set */ + }; + + explicit Event(CreateFlags flags = DEFAULT); + + //! records an event + void record(Stream& stream = Stream::Null()); + + //! queries an event's status + bool queryIfComplete() const; + + //! waits for an event to complete + void waitForCompletion(); + + //! computes the elapsed time between events + static float elapsedTime(const Event& start, const Event& end); + + class Impl; + +private: + Ptr impl_; + Event(const Ptr& impl); + + friend struct EventAccessor; +}; + +//! @} cudacore_struct + +//=================================================================================== +// Initialization & Info +//=================================================================================== + +//! @addtogroup cudacore_init +//! @{ + +/** @brief Returns the number of installed CUDA-enabled devices. + +Use this function before any other CUDA functions calls. If OpenCV is compiled without CUDA support, +this function returns 0. If the CUDA driver is not installed, or is incompatible, this function +returns -1. + */ +CV_EXPORTS int getCudaEnabledDeviceCount(); + +/** @brief Sets a device and initializes it for the current thread. + +@param device System index of a CUDA device starting with 0. + +If the call of this function is omitted, a default device is initialized at the fist CUDA usage. + */ +CV_EXPORTS void setDevice(int device); + +/** @brief Returns the current device index set by cuda::setDevice or initialized by default. + */ +CV_EXPORTS int getDevice(); + +/** @brief Explicitly destroys and cleans up all resources associated with the current device in the current +process. + +Any subsequent API call to this device will reinitialize the device. + */ +CV_EXPORTS void resetDevice(); + +/** @brief Enumeration providing CUDA computing features. + */ +enum FeatureSet +{ + FEATURE_SET_COMPUTE_10 = 10, + FEATURE_SET_COMPUTE_11 = 11, + FEATURE_SET_COMPUTE_12 = 12, + FEATURE_SET_COMPUTE_13 = 13, + FEATURE_SET_COMPUTE_20 = 20, + FEATURE_SET_COMPUTE_21 = 21, + FEATURE_SET_COMPUTE_30 = 30, + FEATURE_SET_COMPUTE_32 = 32, + FEATURE_SET_COMPUTE_35 = 35, + FEATURE_SET_COMPUTE_50 = 50, + + GLOBAL_ATOMICS = FEATURE_SET_COMPUTE_11, + SHARED_ATOMICS = FEATURE_SET_COMPUTE_12, + NATIVE_DOUBLE = FEATURE_SET_COMPUTE_13, + WARP_SHUFFLE_FUNCTIONS = FEATURE_SET_COMPUTE_30, + DYNAMIC_PARALLELISM = FEATURE_SET_COMPUTE_35 +}; + +//! checks whether current device supports the given feature +CV_EXPORTS bool deviceSupports(FeatureSet feature_set); + +/** @brief Class providing a set of static methods to check what NVIDIA\* card architecture the CUDA module was +built for. + +According to the CUDA C Programming Guide Version 3.2: "PTX code produced for some specific compute +capability can always be compiled to binary code of greater or equal compute capability". + */ +class CV_EXPORTS TargetArchs +{ +public: + /** @brief The following method checks whether the module was built with the support of the given feature: + + @param feature_set Features to be checked. See :ocvcuda::FeatureSet. + */ + static bool builtWith(FeatureSet feature_set); + + /** @brief There is a set of methods to check whether the module contains intermediate (PTX) or binary CUDA + code for the given architecture(s): + + @param major Major compute capability version. + @param minor Minor compute capability version. + */ + static bool has(int major, int minor); + static bool hasPtx(int major, int minor); + static bool hasBin(int major, int minor); + + static bool hasEqualOrLessPtx(int major, int minor); + static bool hasEqualOrGreater(int major, int minor); + static bool hasEqualOrGreaterPtx(int major, int minor); + static bool hasEqualOrGreaterBin(int major, int minor); +}; + +/** @brief Class providing functionality for querying the specified GPU properties. + */ +class CV_EXPORTS DeviceInfo +{ +public: + //! creates DeviceInfo object for the current GPU + DeviceInfo(); + + /** @brief The constructors. + + @param device_id System index of the CUDA device starting with 0. + + Constructs the DeviceInfo object for the specified device. If device_id parameter is missed, it + constructs an object for the current device. + */ + DeviceInfo(int device_id); + + /** @brief Returns system index of the CUDA device starting with 0. + */ + int deviceID() const; + + //! ASCII string identifying device + const char* name() const; + + //! global memory available on device in bytes + size_t totalGlobalMem() const; + + //! shared memory available per block in bytes + size_t sharedMemPerBlock() const; + + //! 32-bit registers available per block + int regsPerBlock() const; + + //! warp size in threads + int warpSize() const; + + //! maximum pitch in bytes allowed by memory copies + size_t memPitch() const; + + //! maximum number of threads per block + int maxThreadsPerBlock() const; + + //! maximum size of each dimension of a block + Vec3i maxThreadsDim() const; + + //! maximum size of each dimension of a grid + Vec3i maxGridSize() const; + + //! clock frequency in kilohertz + int clockRate() const; + + //! constant memory available on device in bytes + size_t totalConstMem() const; + + //! major compute capability + int majorVersion() const; + + //! minor compute capability + int minorVersion() const; + + //! alignment requirement for textures + size_t textureAlignment() const; + + //! pitch alignment requirement for texture references bound to pitched memory + size_t texturePitchAlignment() const; + + //! number of multiprocessors on device + int multiProcessorCount() const; + + //! specified whether there is a run time limit on kernels + bool kernelExecTimeoutEnabled() const; + + //! device is integrated as opposed to discrete + bool integrated() const; + + //! device can map host memory with cudaHostAlloc/cudaHostGetDevicePointer + bool canMapHostMemory() const; + + enum ComputeMode + { + ComputeModeDefault, /**< default compute mode (Multiple threads can use cudaSetDevice with this device) */ + ComputeModeExclusive, /**< compute-exclusive-thread mode (Only one thread in one process will be able to use cudaSetDevice with this device) */ + ComputeModeProhibited, /**< compute-prohibited mode (No threads can use cudaSetDevice with this device) */ + ComputeModeExclusiveProcess /**< compute-exclusive-process mode (Many threads in one process will be able to use cudaSetDevice with this device) */ + }; + + //! compute mode + ComputeMode computeMode() const; + + //! maximum 1D texture size + int maxTexture1D() const; + + //! maximum 1D mipmapped texture size + int maxTexture1DMipmap() const; + + //! maximum size for 1D textures bound to linear memory + int maxTexture1DLinear() const; + + //! maximum 2D texture dimensions + Vec2i maxTexture2D() const; + + //! maximum 2D mipmapped texture dimensions + Vec2i maxTexture2DMipmap() const; + + //! maximum dimensions (width, height, pitch) for 2D textures bound to pitched memory + Vec3i maxTexture2DLinear() const; + + //! maximum 2D texture dimensions if texture gather operations have to be performed + Vec2i maxTexture2DGather() const; + + //! maximum 3D texture dimensions + Vec3i maxTexture3D() const; + + //! maximum Cubemap texture dimensions + int maxTextureCubemap() const; + + //! maximum 1D layered texture dimensions + Vec2i maxTexture1DLayered() const; + + //! maximum 2D layered texture dimensions + Vec3i maxTexture2DLayered() const; + + //! maximum Cubemap layered texture dimensions + Vec2i maxTextureCubemapLayered() const; + + //! maximum 1D surface size + int maxSurface1D() const; + + //! maximum 2D surface dimensions + Vec2i maxSurface2D() const; + + //! maximum 3D surface dimensions + Vec3i maxSurface3D() const; + + //! maximum 1D layered surface dimensions + Vec2i maxSurface1DLayered() const; + + //! maximum 2D layered surface dimensions + Vec3i maxSurface2DLayered() const; + + //! maximum Cubemap surface dimensions + int maxSurfaceCubemap() const; + + //! maximum Cubemap layered surface dimensions + Vec2i maxSurfaceCubemapLayered() const; + + //! alignment requirements for surfaces + size_t surfaceAlignment() const; + + //! device can possibly execute multiple kernels concurrently + bool concurrentKernels() const; + + //! device has ECC support enabled + bool ECCEnabled() const; + + //! PCI bus ID of the device + int pciBusID() const; + + //! PCI device ID of the device + int pciDeviceID() const; + + //! PCI domain ID of the device + int pciDomainID() const; + + //! true if device is a Tesla device using TCC driver, false otherwise + bool tccDriver() const; + + //! number of asynchronous engines + int asyncEngineCount() const; + + //! device shares a unified address space with the host + bool unifiedAddressing() const; + + //! peak memory clock frequency in kilohertz + int memoryClockRate() const; + + //! global memory bus width in bits + int memoryBusWidth() const; + + //! size of L2 cache in bytes + int l2CacheSize() const; + + //! maximum resident threads per multiprocessor + int maxThreadsPerMultiProcessor() const; + + //! gets free and total device memory + void queryMemory(size_t& totalMemory, size_t& freeMemory) const; + size_t freeMemory() const; + size_t totalMemory() const; + + /** @brief Provides information on CUDA feature support. + + @param feature_set Features to be checked. See cuda::FeatureSet. + + This function returns true if the device has the specified CUDA feature. Otherwise, it returns false + */ + bool supports(FeatureSet feature_set) const; + + /** @brief Checks the CUDA module and device compatibility. + + This function returns true if the CUDA module can be run on the specified device. Otherwise, it + returns false . + */ + bool isCompatible() const; + +private: + int device_id_; +}; + +CV_EXPORTS void printCudaDeviceInfo(int device); +CV_EXPORTS void printShortCudaDeviceInfo(int device); + +/** @brief Converts an array to half precision floating number. + +@param _src input array. +@param _dst output array. +@param stream Stream for the asynchronous version. +@sa convertFp16 +*/ +CV_EXPORTS void convertFp16(InputArray _src, OutputArray _dst, Stream& stream = Stream::Null()); + +//! @} cudacore_init + +}} // namespace cv { namespace cuda { + + +#include "opencv2/core/cuda.inl.hpp" + +#endif /* OPENCV_CORE_CUDA_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.inl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.inl.hpp new file mode 100644 index 0000000..35ae2e4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda.inl.hpp @@ -0,0 +1,631 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDAINL_HPP +#define OPENCV_CORE_CUDAINL_HPP + +#include "opencv2/core/cuda.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { + +//=================================================================================== +// GpuMat +//=================================================================================== + +inline +GpuMat::GpuMat(Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{} + +inline +GpuMat::GpuMat(int rows_, int cols_, int type_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (rows_ > 0 && cols_ > 0) + create(rows_, cols_, type_); +} + +inline +GpuMat::GpuMat(Size size_, int type_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (size_.height > 0 && size_.width > 0) + create(size_.height, size_.width, type_); +} + +inline +GpuMat::GpuMat(int rows_, int cols_, int type_, Scalar s_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (rows_ > 0 && cols_ > 0) + { + create(rows_, cols_, type_); + setTo(s_); + } +} + +inline +GpuMat::GpuMat(Size size_, int type_, Scalar s_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (size_.height > 0 && size_.width > 0) + { + create(size_.height, size_.width, type_); + setTo(s_); + } +} + +inline +GpuMat::GpuMat(const GpuMat& m) + : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), data(m.data), refcount(m.refcount), datastart(m.datastart), dataend(m.dataend), allocator(m.allocator) +{ + if (refcount) + CV_XADD(refcount, 1); +} + +inline +GpuMat::GpuMat(InputArray arr, Allocator* allocator_) : + flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + upload(arr); +} + +inline +GpuMat::~GpuMat() +{ + release(); +} + +inline +GpuMat& GpuMat::operator =(const GpuMat& m) +{ + if (this != &m) + { + GpuMat temp(m); + swap(temp); + } + + return *this; +} + +inline +void GpuMat::create(Size size_, int type_) +{ + create(size_.height, size_.width, type_); +} + +inline +void GpuMat::swap(GpuMat& b) +{ + std::swap(flags, b.flags); + std::swap(rows, b.rows); + std::swap(cols, b.cols); + std::swap(step, b.step); + std::swap(data, b.data); + std::swap(datastart, b.datastart); + std::swap(dataend, b.dataend); + std::swap(refcount, b.refcount); + std::swap(allocator, b.allocator); +} + +inline +GpuMat GpuMat::clone() const +{ + GpuMat m; + copyTo(m); + return m; +} + +inline +void GpuMat::copyTo(OutputArray dst, InputArray mask) const +{ + copyTo(dst, mask, Stream::Null()); +} + +inline +GpuMat& GpuMat::setTo(Scalar s) +{ + return setTo(s, Stream::Null()); +} + +inline +GpuMat& GpuMat::setTo(Scalar s, InputArray mask) +{ + return setTo(s, mask, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype) const +{ + convertTo(dst, rtype, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, double beta) const +{ + convertTo(dst, rtype, alpha, beta, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const +{ + convertTo(dst, rtype, alpha, 0.0, stream); +} + +inline +void GpuMat::assignTo(GpuMat& m, int _type) const +{ + if (_type < 0) + m = *this; + else + convertTo(m, _type); +} + +inline +uchar* GpuMat::ptr(int y) +{ + CV_DbgAssert( (unsigned)y < (unsigned)rows ); + return data + step * y; +} + +inline +const uchar* GpuMat::ptr(int y) const +{ + CV_DbgAssert( (unsigned)y < (unsigned)rows ); + return data + step * y; +} + +template inline +_Tp* GpuMat::ptr(int y) +{ + return (_Tp*)ptr(y); +} + +template inline +const _Tp* GpuMat::ptr(int y) const +{ + return (const _Tp*)ptr(y); +} + +template inline +GpuMat::operator PtrStepSz() const +{ + return PtrStepSz(rows, cols, (T*)data, step); +} + +template inline +GpuMat::operator PtrStep() const +{ + return PtrStep((T*)data, step); +} + +inline +GpuMat GpuMat::row(int y) const +{ + return GpuMat(*this, Range(y, y+1), Range::all()); +} + +inline +GpuMat GpuMat::col(int x) const +{ + return GpuMat(*this, Range::all(), Range(x, x+1)); +} + +inline +GpuMat GpuMat::rowRange(int startrow, int endrow) const +{ + return GpuMat(*this, Range(startrow, endrow), Range::all()); +} + +inline +GpuMat GpuMat::rowRange(Range r) const +{ + return GpuMat(*this, r, Range::all()); +} + +inline +GpuMat GpuMat::colRange(int startcol, int endcol) const +{ + return GpuMat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +GpuMat GpuMat::colRange(Range r) const +{ + return GpuMat(*this, Range::all(), r); +} + +inline +GpuMat GpuMat::operator ()(Range rowRange_, Range colRange_) const +{ + return GpuMat(*this, rowRange_, colRange_); +} + +inline +GpuMat GpuMat::operator ()(Rect roi) const +{ + return GpuMat(*this, roi); +} + +inline +bool GpuMat::isContinuous() const +{ + return (flags & Mat::CONTINUOUS_FLAG) != 0; +} + +inline +size_t GpuMat::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t GpuMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int GpuMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int GpuMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int GpuMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t GpuMat::step1() const +{ + return step / elemSize1(); +} + +inline +Size GpuMat::size() const +{ + return Size(cols, rows); +} + +inline +bool GpuMat::empty() const +{ + return data == 0; +} + +static inline +GpuMat createContinuous(int rows, int cols, int type) +{ + GpuMat m; + createContinuous(rows, cols, type, m); + return m; +} + +static inline +void createContinuous(Size size, int type, OutputArray arr) +{ + createContinuous(size.height, size.width, type, arr); +} + +static inline +GpuMat createContinuous(Size size, int type) +{ + GpuMat m; + createContinuous(size, type, m); + return m; +} + +static inline +void ensureSizeIsEnough(Size size, int type, OutputArray arr) +{ + ensureSizeIsEnough(size.height, size.width, type, arr); +} + +static inline +void swap(GpuMat& a, GpuMat& b) +{ + a.swap(b); +} + +//=================================================================================== +// HostMem +//=================================================================================== + +inline +HostMem::HostMem(AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ +} + +inline +HostMem::HostMem(const HostMem& m) + : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), data(m.data), refcount(m.refcount), datastart(m.datastart), dataend(m.dataend), alloc_type(m.alloc_type) +{ + if( refcount ) + CV_XADD(refcount, 1); +} + +inline +HostMem::HostMem(int rows_, int cols_, int type_, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + if (rows_ > 0 && cols_ > 0) + create(rows_, cols_, type_); +} + +inline +HostMem::HostMem(Size size_, int type_, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + if (size_.height > 0 && size_.width > 0) + create(size_.height, size_.width, type_); +} + +inline +HostMem::HostMem(InputArray arr, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + arr.getMat().copyTo(*this); +} + +inline +HostMem::~HostMem() +{ + release(); +} + +inline +HostMem& HostMem::operator =(const HostMem& m) +{ + if (this != &m) + { + HostMem temp(m); + swap(temp); + } + + return *this; +} + +inline +void HostMem::swap(HostMem& b) +{ + std::swap(flags, b.flags); + std::swap(rows, b.rows); + std::swap(cols, b.cols); + std::swap(step, b.step); + std::swap(data, b.data); + std::swap(datastart, b.datastart); + std::swap(dataend, b.dataend); + std::swap(refcount, b.refcount); + std::swap(alloc_type, b.alloc_type); +} + +inline +HostMem HostMem::clone() const +{ + HostMem m(size(), type(), alloc_type); + createMatHeader().copyTo(m); + return m; +} + +inline +void HostMem::create(Size size_, int type_) +{ + create(size_.height, size_.width, type_); +} + +inline +Mat HostMem::createMatHeader() const +{ + return Mat(size(), type(), data, step); +} + +inline +bool HostMem::isContinuous() const +{ + return (flags & Mat::CONTINUOUS_FLAG) != 0; +} + +inline +size_t HostMem::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t HostMem::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int HostMem::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int HostMem::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int HostMem::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t HostMem::step1() const +{ + return step / elemSize1(); +} + +inline +Size HostMem::size() const +{ + return Size(cols, rows); +} + +inline +bool HostMem::empty() const +{ + return data == 0; +} + +static inline +void swap(HostMem& a, HostMem& b) +{ + a.swap(b); +} + +//=================================================================================== +// Stream +//=================================================================================== + +inline +Stream::Stream(const Ptr& impl) + : impl_(impl) +{ +} + +//=================================================================================== +// Event +//=================================================================================== + +inline +Event::Event(const Ptr& impl) + : impl_(impl) +{ +} + +//=================================================================================== +// Initialization & Info +//=================================================================================== + +inline +bool TargetArchs::has(int major, int minor) +{ + return hasPtx(major, minor) || hasBin(major, minor); +} + +inline +bool TargetArchs::hasEqualOrGreater(int major, int minor) +{ + return hasEqualOrGreaterPtx(major, minor) || hasEqualOrGreaterBin(major, minor); +} + +inline +DeviceInfo::DeviceInfo() +{ + device_id_ = getDevice(); +} + +inline +DeviceInfo::DeviceInfo(int device_id) +{ + CV_Assert( device_id >= 0 && device_id < getCudaEnabledDeviceCount() ); + device_id_ = device_id; +} + +inline +int DeviceInfo::deviceID() const +{ + return device_id_; +} + +inline +size_t DeviceInfo::freeMemory() const +{ + size_t _totalMemory = 0, _freeMemory = 0; + queryMemory(_totalMemory, _freeMemory); + return _freeMemory; +} + +inline +size_t DeviceInfo::totalMemory() const +{ + size_t _totalMemory = 0, _freeMemory = 0; + queryMemory(_totalMemory, _freeMemory); + return _totalMemory; +} + +inline +bool DeviceInfo::supports(FeatureSet feature_set) const +{ + int version = majorVersion() * 10 + minorVersion(); + return version >= feature_set; +} + + +}} // namespace cv { namespace cuda { + +//=================================================================================== +// Mat +//=================================================================================== + +namespace cv { + +inline +Mat::Mat(const cuda::GpuMat& m) + : flags(0), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows) +{ + m.download(*this); +} + +} + +//! @endcond + +#endif // OPENCV_CORE_CUDAINL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/block.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/block.hpp new file mode 100644 index 0000000..c277f0e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/block.hpp @@ -0,0 +1,211 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_BLOCK_HPP +#define OPENCV_CUDA_DEVICE_BLOCK_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Block + { + static __device__ __forceinline__ unsigned int id() + { + return blockIdx.x; + } + + static __device__ __forceinline__ unsigned int stride() + { + return blockDim.x * blockDim.y * blockDim.z; + } + + static __device__ __forceinline__ void sync() + { + __syncthreads(); + } + + static __device__ __forceinline__ int flattenedThreadId() + { + return threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x; + } + + template + static __device__ __forceinline__ void fill(It beg, It end, const T& value) + { + int STRIDE = stride(); + It t = beg + flattenedThreadId(); + + for(; t < end; t += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ void yota(OutIt beg, OutIt end, T value) + { + int STRIDE = stride(); + int tid = flattenedThreadId(); + value += tid; + + for(OutIt t = beg + tid; t < end; t += STRIDE, value += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ void copy(InIt beg, InIt end, OutIt out) + { + int STRIDE = stride(); + InIt t = beg + flattenedThreadId(); + OutIt o = out + (t - beg); + + for(; t < end; t += STRIDE, o += STRIDE) + *o = *t; + } + + template + static __device__ __forceinline__ void transform(InIt beg, InIt end, OutIt out, UnOp op) + { + int STRIDE = stride(); + InIt t = beg + flattenedThreadId(); + OutIt o = out + (t - beg); + + for(; t < end; t += STRIDE, o += STRIDE) + *o = op(*t); + } + + template + static __device__ __forceinline__ void transform(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) + { + int STRIDE = stride(); + InIt1 t1 = beg1 + flattenedThreadId(); + InIt2 t2 = beg2 + flattenedThreadId(); + OutIt o = out + (t1 - beg1); + + for(; t1 < end1; t1 += STRIDE, t2 += STRIDE, o += STRIDE) + *o = op(*t1, *t2); + } + + template + static __device__ __forceinline__ void reduce(volatile T* buffer, BinOp op) + { + int tid = flattenedThreadId(); + T val = buffer[tid]; + + if (CTA_SIZE >= 1024) { if (tid < 512) buffer[tid] = val = op(val, buffer[tid + 512]); __syncthreads(); } + if (CTA_SIZE >= 512) { if (tid < 256) buffer[tid] = val = op(val, buffer[tid + 256]); __syncthreads(); } + if (CTA_SIZE >= 256) { if (tid < 128) buffer[tid] = val = op(val, buffer[tid + 128]); __syncthreads(); } + if (CTA_SIZE >= 128) { if (tid < 64) buffer[tid] = val = op(val, buffer[tid + 64]); __syncthreads(); } + + if (tid < 32) + { + if (CTA_SIZE >= 64) { buffer[tid] = val = op(val, buffer[tid + 32]); } + if (CTA_SIZE >= 32) { buffer[tid] = val = op(val, buffer[tid + 16]); } + if (CTA_SIZE >= 16) { buffer[tid] = val = op(val, buffer[tid + 8]); } + if (CTA_SIZE >= 8) { buffer[tid] = val = op(val, buffer[tid + 4]); } + if (CTA_SIZE >= 4) { buffer[tid] = val = op(val, buffer[tid + 2]); } + if (CTA_SIZE >= 2) { buffer[tid] = val = op(val, buffer[tid + 1]); } + } + } + + template + static __device__ __forceinline__ T reduce(volatile T* buffer, T init, BinOp op) + { + int tid = flattenedThreadId(); + T val = buffer[tid] = init; + __syncthreads(); + + if (CTA_SIZE >= 1024) { if (tid < 512) buffer[tid] = val = op(val, buffer[tid + 512]); __syncthreads(); } + if (CTA_SIZE >= 512) { if (tid < 256) buffer[tid] = val = op(val, buffer[tid + 256]); __syncthreads(); } + if (CTA_SIZE >= 256) { if (tid < 128) buffer[tid] = val = op(val, buffer[tid + 128]); __syncthreads(); } + if (CTA_SIZE >= 128) { if (tid < 64) buffer[tid] = val = op(val, buffer[tid + 64]); __syncthreads(); } + + if (tid < 32) + { + if (CTA_SIZE >= 64) { buffer[tid] = val = op(val, buffer[tid + 32]); } + if (CTA_SIZE >= 32) { buffer[tid] = val = op(val, buffer[tid + 16]); } + if (CTA_SIZE >= 16) { buffer[tid] = val = op(val, buffer[tid + 8]); } + if (CTA_SIZE >= 8) { buffer[tid] = val = op(val, buffer[tid + 4]); } + if (CTA_SIZE >= 4) { buffer[tid] = val = op(val, buffer[tid + 2]); } + if (CTA_SIZE >= 2) { buffer[tid] = val = op(val, buffer[tid + 1]); } + } + __syncthreads(); + return buffer[0]; + } + + template + static __device__ __forceinline__ void reduce_n(T* data, unsigned int n, BinOp op) + { + int ftid = flattenedThreadId(); + int sft = stride(); + + if (sft < n) + { + for (unsigned int i = sft + ftid; i < n; i += sft) + data[ftid] = op(data[ftid], data[i]); + + __syncthreads(); + + n = sft; + } + + while (n > 1) + { + unsigned int half = n/2; + + if (ftid < half) + data[ftid] = op(data[ftid], data[n - ftid - 1]); + + __syncthreads(); + + n = n - half; + } + } + }; +}}} + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_BLOCK_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/border_interpolate.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/border_interpolate.hpp new file mode 100644 index 0000000..874f705 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/border_interpolate.hpp @@ -0,0 +1,722 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_BORDER_INTERPOLATE_HPP +#define OPENCV_CUDA_BORDER_INTERPOLATE_HPP + +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "vec_math.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + ////////////////////////////////////////////////////////////// + // BrdConstant + + template struct BrdRowConstant + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowConstant(int width_, const D& val_ = VecTraits::all(0)) : width(width_), val(val_) {} + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return x >= 0 ? saturate_cast(data[x]) : val; + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return x < width ? saturate_cast(data[x]) : val; + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return (x >= 0 && x < width) ? saturate_cast(data[x]) : val; + } + + int width; + D val; + }; + + template struct BrdColConstant + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColConstant(int height_, const D& val_ = VecTraits::all(0)) : height(height_), val(val_) {} + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return y >= 0 ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return y < height ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return (y >= 0 && y < height) ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + int height; + D val; + }; + + template struct BrdConstant + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdConstant(int height_, int width_, const D& val_ = VecTraits::all(0)) : height(height_), width(width_), val(val_) + { + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(((const T*)((const uchar*)data + y * step))[x]) : val; + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(src(y, x)) : val; + } + + int height; + int width; + D val; + }; + + ////////////////////////////////////////////////////////////// + // BrdReplicate + + template struct BrdRowReplicate + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReplicate(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReplicate(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::max(x, 0); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::min(x, last_col); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReplicate + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReplicate(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReplicate(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::max(y, 0); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::min(y, last_row); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReplicate + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReplicate(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReplicate(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::max(y, 0); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::min(y, last_row); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::max(x, 0); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::min(x, last_col); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdReflect101 + + template struct BrdRowReflect101 + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReflect101(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReflect101(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::abs(x) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReflect101 + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReflect101(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReflect101(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::abs(y) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReflect101 + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReflect101(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReflect101(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::abs(y) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::abs(x) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdReflect + + template struct BrdRowReflect + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReflect(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReflect(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (::abs(x) - (x < 0)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x) + (x > last_col)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(::abs(x) - (x < 0)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReflect + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReflect(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReflect(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (::abs(y) - (y < 0)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y) + (y > last_row)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(::abs(y) - (y < 0)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReflect + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReflect(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReflect(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (::abs(y) - (y < 0)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return /*::abs*/(last_row - ::abs(last_row - y) + (y > last_row)) /*% (last_row + 1)*/; + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (::abs(x) - (x < 0)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (last_col - ::abs(last_col - x) + (x > last_col)); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdWrap + + template struct BrdRowWrap + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowWrap(int width_) : width(width_) {} + template __host__ __device__ __forceinline__ BrdRowWrap(int width_, U) : width(width_) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (x >= 0) * x + (x < 0) * (x - ((x - width + 1) / width) * width); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (x < width) * x + (x >= width) * (x % width); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(idx_col_low(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int width; + }; + + template struct BrdColWrap + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColWrap(int height_) : height(height_) {} + template __host__ __device__ __forceinline__ BrdColWrap(int height_, U) : height(height_) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (y >= 0) * y + (y < 0) * (y - ((y - height + 1) / height) * height); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return (y < height) * y + (y >= height) * (y % height); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(idx_row_low(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int height; + }; + + template struct BrdWrap + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdWrap(int height_, int width_) : + height(height_), width(width_) + { + } + template + __host__ __device__ __forceinline__ BrdWrap(int height_, int width_, U) : + height(height_), width(width_) + { + } + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (y >= 0) ? y : (y - ((y - height + 1) / height) * height); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return (y < height) ? y : (y % height); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(idx_row_low(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (x >= 0) ? x : (x - ((x - width + 1) / width) * width); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (x < width) ? x : (x % width); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(idx_col_low(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int height; + int width; + }; + + ////////////////////////////////////////////////////////////// + // BorderReader + + template struct BorderReader + { + typedef typename B::result_type elem_type; + typedef typename Ptr2D::index_type index_type; + + __host__ __device__ __forceinline__ BorderReader(const Ptr2D& ptr_, const B& b_) : ptr(ptr_), b(b_) {} + + __device__ __forceinline__ elem_type operator ()(index_type y, index_type x) const + { + return b.at(y, x, ptr); + } + + Ptr2D ptr; + B b; + }; + + // under win32 there is some bug with templated types that passed as kernel parameters + // with this specialization all works fine + template struct BorderReader< Ptr2D, BrdConstant > + { + typedef typename BrdConstant::result_type elem_type; + typedef typename Ptr2D::index_type index_type; + + __host__ __device__ __forceinline__ BorderReader(const Ptr2D& src_, const BrdConstant& b) : + src(src_), height(b.height), width(b.width), val(b.val) + { + } + + __device__ __forceinline__ D operator ()(index_type y, index_type x) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(src(y, x)) : val; + } + + Ptr2D src; + int height; + int width; + D val; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_BORDER_INTERPOLATE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/color.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/color.hpp new file mode 100644 index 0000000..dcce280 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/color.hpp @@ -0,0 +1,309 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COLOR_HPP +#define OPENCV_CUDA_COLOR_HPP + +#include "detail/color_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // All OPENCV_CUDA_IMPLEMENT_*_TRAITS(ColorSpace1_to_ColorSpace2, ...) macros implements + // template class ColorSpace1_to_ColorSpace2_traits + // { + // typedef ... functor_type; + // static __host__ __device__ functor_type create_functor(); + // }; + + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_rgba, 4, 4, 2) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgr_to_bgr555, 3, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgr_to_bgr565, 3, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgb_to_bgr555, 3, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgb_to_bgr565, 3, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgra_to_bgr555, 4, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgra_to_bgr565, 4, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgba_to_bgr555, 4, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgba_to_bgr565, 4, 2, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_rgb, 3, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_rgb, 3, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_bgr, 3, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_bgr, 3, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_rgba, 4, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_rgba, 4, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_bgra, 4, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_bgra, 4, 0, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(gray_to_bgr, 3) + OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(gray_to_bgra, 4) + + #undef OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(gray_to_bgr555, 5) + OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(gray_to_bgr565, 6) + + #undef OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(bgr555_to_gray, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(bgr565_to_gray, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(rgb_to_gray, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(bgr_to_gray, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(rgba_to_gray, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(bgra_to_gray, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgb_to_yuv, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgba_to_yuv, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgb_to_yuv4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgba_to_yuv4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgr_to_yuv, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgra_to_yuv, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgr_to_yuv4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgra_to_yuv4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS + + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgb_to_YCrCb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgba_to_YCrCb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgb_to_YCrCb4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgba_to_YCrCb4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgr_to_YCrCb, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgra_to_YCrCb, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgr_to_YCrCb4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgra_to_YCrCb4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS + + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgb_to_xyz, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgba_to_xyz, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgb_to_xyz4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgba_to_xyz4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgr_to_xyz, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgra_to_xyz, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgr_to_xyz4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgra_to_xyz4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS + + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgb_to_hsv, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgba_to_hsv, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgb_to_hsv4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgba_to_hsv4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgr_to_hsv, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgra_to_hsv, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgr_to_hsv4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgra_to_hsv4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS + + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgb_to_hls, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgba_to_hls, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgb_to_hls4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgba_to_hls4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgr_to_hls, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgra_to_hls, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgr_to_hls4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgra_to_hls4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS + + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgb_to_lab, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgba_to_lab, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgb_to_lab4, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgba_to_lab4, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgr_to_lab, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgra_to_lab, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgr_to_lab4, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgra_to_lab4, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgb_to_lab, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgba_to_lab, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgb_to_lab4, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgba_to_lab4, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgr_to_lab, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgra_to_lab, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgr_to_lab4, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgra_to_lab4, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS + + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_rgb, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_rgb, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_rgba, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_rgba, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_bgr, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_bgr, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_bgra, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_bgra, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lrgb, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lrgb, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lrgba, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lrgba, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lbgr, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lbgr, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lbgra, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lbgra, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgb_to_luv, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgba_to_luv, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgb_to_luv4, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgba_to_luv4, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgr_to_luv, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgra_to_luv, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgr_to_luv4, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgra_to_luv4, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgb_to_luv, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgba_to_luv, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgb_to_luv4, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgba_to_luv4, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgr_to_luv, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgra_to_luv, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgr_to_luv4, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgra_to_luv4, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS + + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_rgb, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_rgb, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_rgba, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_rgba, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_bgr, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_bgr, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_bgra, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_bgra, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lrgb, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lrgb, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lrgba, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lrgba, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lbgr, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lbgr, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lbgra, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lbgra, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_COLOR_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/common.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/common.hpp new file mode 100644 index 0000000..14b1f3f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/common.hpp @@ -0,0 +1,109 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COMMON_HPP +#define OPENCV_CUDA_COMMON_HPP + +#include +#include "opencv2/core/cuda_types.hpp" +#include "opencv2/core/cvdef.h" +#include "opencv2/core/base.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +#ifndef CV_PI_F + #ifndef CV_PI + #define CV_PI_F 3.14159265f + #else + #define CV_PI_F ((float)CV_PI) + #endif +#endif + +namespace cv { namespace cuda { + static inline void checkCudaError(cudaError_t err, const char* file, const int line, const char* func) + { + if (cudaSuccess != err) + cv::error(cv::Error::GpuApiCallError, cudaGetErrorString(err), func, file, line); + } +}} + +#ifndef cudaSafeCall + #define cudaSafeCall(expr) cv::cuda::checkCudaError(expr, __FILE__, __LINE__, CV_Func) +#endif + +namespace cv { namespace cuda +{ + template static inline bool isAligned(const T* ptr, size_t size) + { + return reinterpret_cast(ptr) % size == 0; + } + + static inline bool isAligned(size_t step, size_t size) + { + return step % size == 0; + } +}} + +namespace cv { namespace cuda +{ + namespace device + { + __host__ __device__ __forceinline__ int divUp(int total, int grain) + { + return (total + grain - 1) / grain; + } + + template inline void bindTexture(const textureReference* tex, const PtrStepSz& img) + { + cudaChannelFormatDesc desc = cudaCreateChannelDesc(); + cudaSafeCall( cudaBindTexture2D(0, tex, img.ptr(), &desc, img.cols, img.rows, img.step) ); + } + } +}} + +//! @endcond + +#endif // OPENCV_CUDA_COMMON_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/datamov_utils.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/datamov_utils.hpp new file mode 100644 index 0000000..6820d0f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/datamov_utils.hpp @@ -0,0 +1,113 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DATAMOV_UTILS_HPP +#define OPENCV_CUDA_DATAMOV_UTILS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 200 + + // for Fermi memory space is detected automatically + template struct ForceGlob + { + __device__ __forceinline__ static void Load(const T* ptr, int offset, T& val) { val = ptr[offset]; } + }; + + #else // __CUDA_ARCH__ >= 200 + + #if defined(_WIN64) || defined(__LP64__) + // 64-bit register modifier for inlined asm + #define OPENCV_CUDA_ASM_PTR "l" + #else + // 32-bit register modifier for inlined asm + #define OPENCV_CUDA_ASM_PTR "r" + #endif + + template struct ForceGlob; + + #define OPENCV_CUDA_DEFINE_FORCE_GLOB(base_type, ptx_type, reg_mod) \ + template <> struct ForceGlob \ + { \ + __device__ __forceinline__ static void Load(const base_type* ptr, int offset, base_type& val) \ + { \ + asm("ld.global."#ptx_type" %0, [%1];" : "="#reg_mod(val) : OPENCV_CUDA_ASM_PTR(ptr + offset)); \ + } \ + }; + + #define OPENCV_CUDA_DEFINE_FORCE_GLOB_B(base_type, ptx_type) \ + template <> struct ForceGlob \ + { \ + __device__ __forceinline__ static void Load(const base_type* ptr, int offset, base_type& val) \ + { \ + asm("ld.global."#ptx_type" %0, [%1];" : "=r"(*reinterpret_cast(&val)) : OPENCV_CUDA_ASM_PTR(ptr + offset)); \ + } \ + }; + + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(uchar, u8) + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(schar, s8) + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(char, b8) + OPENCV_CUDA_DEFINE_FORCE_GLOB (ushort, u16, h) + OPENCV_CUDA_DEFINE_FORCE_GLOB (short, s16, h) + OPENCV_CUDA_DEFINE_FORCE_GLOB (uint, u32, r) + OPENCV_CUDA_DEFINE_FORCE_GLOB (int, s32, r) + OPENCV_CUDA_DEFINE_FORCE_GLOB (float, f32, f) + OPENCV_CUDA_DEFINE_FORCE_GLOB (double, f64, d) + + #undef OPENCV_CUDA_DEFINE_FORCE_GLOB + #undef OPENCV_CUDA_DEFINE_FORCE_GLOB_B + #undef OPENCV_CUDA_ASM_PTR + + #endif // __CUDA_ARCH__ >= 200 +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_DATAMOV_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/color_detail.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/color_detail.hpp new file mode 100644 index 0000000..bfb4055 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/color_detail.hpp @@ -0,0 +1,1980 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COLOR_DETAIL_HPP +#define OPENCV_CUDA_COLOR_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" +#include "../saturate_cast.hpp" +#include "../limits.hpp" +#include "../functional.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + #ifndef CV_DESCALE + #define CV_DESCALE(x, n) (((x) + (1 << ((n)-1))) >> (n)) + #endif + + namespace color_detail + { + template struct ColorChannel + { + typedef float worktype_f; + static __device__ __forceinline__ T max() { return numeric_limits::max(); } + static __device__ __forceinline__ T half() { return (T)(max()/2 + 1); } + }; + + template<> struct ColorChannel + { + typedef float worktype_f; + static __device__ __forceinline__ float max() { return 1.f; } + static __device__ __forceinline__ float half() { return 0.5f; } + }; + + template static __device__ __forceinline__ void setAlpha(typename TypeVec::vec_type& vec, T val) + { + } + + template static __device__ __forceinline__ void setAlpha(typename TypeVec::vec_type& vec, T val) + { + vec.w = val; + } + + template static __device__ __forceinline__ T getAlpha(const typename TypeVec::vec_type& vec) + { + return ColorChannel::max(); + } + + template static __device__ __forceinline__ T getAlpha(const typename TypeVec::vec_type& vec) + { + return vec.w; + } + + enum + { + yuv_shift = 14, + xyz_shift = 12, + R2Y = 4899, + G2Y = 9617, + B2Y = 1868, + BLOCK_SIZE = 256 + }; + } + +////////////////// Various 3/4-channel to 3/4-channel RGB transformations ///////////////// + + namespace color_detail + { + template struct RGB2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + dst.x = (&src.x)[bidx]; + dst.y = src.y; + dst.z = (&src.x)[bidx^2]; + setAlpha(dst, getAlpha(src)); + + return dst; + } + + __host__ __device__ __forceinline__ RGB2RGB() {} + __host__ __device__ __forceinline__ RGB2RGB(const RGB2RGB&) {} + }; + + template <> struct RGB2RGB : unary_function + { + __device__ uint operator()(uint src) const + { + uint dst = 0; + + dst |= (0xffu & (src >> 16)); + dst |= (0xffu & (src >> 8)) << 8; + dst |= (0xffu & (src)) << 16; + dst |= (0xffu & (src >> 24)) << 24; + + return dst; + } + + __host__ __device__ __forceinline__ RGB2RGB() {} + __host__ __device__ __forceinline__ RGB2RGB(const RGB2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +/////////// Transforming 16-bit (565 or 555) RGB to/from 24/32-bit (888[8]) RGB ////////// + + namespace color_detail + { + template struct RGB2RGB5x5Converter; + template struct RGB2RGB5x5Converter<6, bidx> + { + static __device__ __forceinline__ ushort cvt(const uchar3& src) + { + return (ushort)(((&src.x)[bidx] >> 3) | ((src.y & ~3) << 3) | (((&src.x)[bidx^2] & ~7) << 8)); + } + + static __device__ __forceinline__ ushort cvt(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + return (ushort)((b >> 3) | ((g & ~3) << 3) | ((r & ~7) << 8)); + } + }; + + template struct RGB2RGB5x5Converter<5, bidx> + { + static __device__ __forceinline__ ushort cvt(const uchar3& src) + { + return (ushort)(((&src.x)[bidx] >> 3) | ((src.y & ~7) << 2) | (((&src.x)[bidx^2] & ~7) << 7)); + } + + static __device__ __forceinline__ ushort cvt(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + uint a = 0xffu & (src >> 24); + return (ushort)((b >> 3) | ((g & ~7) << 2) | ((r & ~7) << 7) | (a * 0x8000)); + } + }; + + template struct RGB2RGB5x5; + + template struct RGB2RGB5x5<3, bidx,green_bits> : unary_function + { + __device__ __forceinline__ ushort operator()(const uchar3& src) const + { + return RGB2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ RGB2RGB5x5() {} + __host__ __device__ __forceinline__ RGB2RGB5x5(const RGB2RGB5x5&) {} + }; + + template struct RGB2RGB5x5<4, bidx,green_bits> : unary_function + { + __device__ __forceinline__ ushort operator()(uint src) const + { + return RGB2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ RGB2RGB5x5() {} + __host__ __device__ __forceinline__ RGB2RGB5x5(const RGB2RGB5x5&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(name, scn, bidx, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2RGB5x5 functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template struct RGB5x52RGBConverter; + + template struct RGB5x52RGBConverter<5, bidx> + { + static __device__ __forceinline__ void cvt(uint src, uchar3& dst) + { + (&dst.x)[bidx] = src << 3; + dst.y = (src >> 2) & ~7; + (&dst.x)[bidx ^ 2] = (src >> 7) & ~7; + } + + static __device__ __forceinline__ void cvt(uint src, uint& dst) + { + dst = 0; + + dst |= (0xffu & (src << 3)) << (bidx * 8); + dst |= (0xffu & ((src >> 2) & ~7)) << 8; + dst |= (0xffu & ((src >> 7) & ~7)) << ((bidx ^ 2) * 8); + dst |= ((src & 0x8000) * 0xffu) << 24; + } + }; + + template struct RGB5x52RGBConverter<6, bidx> + { + static __device__ __forceinline__ void cvt(uint src, uchar3& dst) + { + (&dst.x)[bidx] = src << 3; + dst.y = (src >> 3) & ~3; + (&dst.x)[bidx ^ 2] = (src >> 8) & ~7; + } + + static __device__ __forceinline__ void cvt(uint src, uint& dst) + { + dst = 0xffu << 24; + + dst |= (0xffu & (src << 3)) << (bidx * 8); + dst |= (0xffu &((src >> 3) & ~3)) << 8; + dst |= (0xffu & ((src >> 8) & ~7)) << ((bidx ^ 2) * 8); + } + }; + + template struct RGB5x52RGB; + + template struct RGB5x52RGB<3, bidx, green_bits> : unary_function + { + __device__ __forceinline__ uchar3 operator()(ushort src) const + { + uchar3 dst; + RGB5x52RGBConverter::cvt(src, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB5x52RGB() {} + __host__ __device__ __forceinline__ RGB5x52RGB(const RGB5x52RGB&) {} + + }; + + template struct RGB5x52RGB<4, bidx, green_bits> : unary_function + { + __device__ __forceinline__ uint operator()(ushort src) const + { + uint dst; + RGB5x52RGBConverter::cvt(src, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB5x52RGB() {} + __host__ __device__ __forceinline__ RGB5x52RGB(const RGB5x52RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(name, dcn, bidx, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB5x52RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////// Grayscale to Color //////////////////////////////// + + namespace color_detail + { + template struct Gray2RGB : unary_function::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(T src) const + { + typename TypeVec::vec_type dst; + + dst.z = dst.y = dst.x = src; + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ Gray2RGB() {} + __host__ __device__ __forceinline__ Gray2RGB(const Gray2RGB&) {} + }; + + template <> struct Gray2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + uint dst = 0xffu << 24; + + dst |= src; + dst |= src << 8; + dst |= src << 16; + + return dst; + } + __host__ __device__ __forceinline__ Gray2RGB() {} + __host__ __device__ __forceinline__ Gray2RGB(const Gray2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(name, dcn) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Gray2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template struct Gray2RGB5x5Converter; + template<> struct Gray2RGB5x5Converter<6> + { + static __device__ __forceinline__ ushort cvt(uint t) + { + return (ushort)((t >> 3) | ((t & ~3) << 3) | ((t & ~7) << 8)); + } + }; + + template<> struct Gray2RGB5x5Converter<5> + { + static __device__ __forceinline__ ushort cvt(uint t) + { + t >>= 3; + return (ushort)(t | (t << 5) | (t << 10)); + } + }; + + template struct Gray2RGB5x5 : unary_function + { + __device__ __forceinline__ ushort operator()(uint src) const + { + return Gray2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ Gray2RGB5x5() {} + __host__ __device__ __forceinline__ Gray2RGB5x5(const Gray2RGB5x5&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(name, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Gray2RGB5x5 functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////// Color to Grayscale //////////////////////////////// + + namespace color_detail + { + template struct RGB5x52GrayConverter; + template <> struct RGB5x52GrayConverter<6> + { + static __device__ __forceinline__ uchar cvt(uint t) + { + return (uchar)CV_DESCALE(((t << 3) & 0xf8) * B2Y + ((t >> 3) & 0xfc) * G2Y + ((t >> 8) & 0xf8) * R2Y, yuv_shift); + } + }; + + template <> struct RGB5x52GrayConverter<5> + { + static __device__ __forceinline__ uchar cvt(uint t) + { + return (uchar)CV_DESCALE(((t << 3) & 0xf8) * B2Y + ((t >> 2) & 0xf8) * G2Y + ((t >> 7) & 0xf8) * R2Y, yuv_shift); + } + }; + + template struct RGB5x52Gray : unary_function + { + __device__ __forceinline__ uchar operator()(uint src) const + { + return RGB5x52GrayConverter::cvt(src); + } + __host__ __device__ __forceinline__ RGB5x52Gray() {} + __host__ __device__ __forceinline__ RGB5x52Gray(const RGB5x52Gray&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(name, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB5x52Gray functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template static __device__ __forceinline__ T RGB2GrayConvert(const T* src) + { + return (T)CV_DESCALE((unsigned)(src[bidx] * B2Y + src[1] * G2Y + src[bidx^2] * R2Y), yuv_shift); + } + + template static __device__ __forceinline__ uchar RGB2GrayConvert(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + return CV_DESCALE((uint)(b * B2Y + g * G2Y + r * R2Y), yuv_shift); + } + + template static __device__ __forceinline__ float RGB2GrayConvert(const float* src) + { + return src[bidx] * 0.114f + src[1] * 0.587f + src[bidx^2] * 0.299f; + } + + template struct RGB2Gray : unary_function::vec_type, T> + { + __device__ __forceinline__ T operator()(const typename TypeVec::vec_type& src) const + { + return RGB2GrayConvert(&src.x); + } + __host__ __device__ __forceinline__ RGB2Gray() {} + __host__ __device__ __forceinline__ RGB2Gray(const RGB2Gray&) {} + }; + + template struct RGB2Gray : unary_function + { + __device__ __forceinline__ uchar operator()(uint src) const + { + return RGB2GrayConvert(src); + } + __host__ __device__ __forceinline__ RGB2Gray() {} + __host__ __device__ __forceinline__ RGB2Gray(const RGB2Gray&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(name, scn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Gray functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> YUV ////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2YUVCoeffs_f[5] = { 0.114f, 0.587f, 0.299f, 0.492f, 0.877f }; + __constant__ int c_RGB2YUVCoeffs_i[5] = { B2Y, G2Y, R2Y, 8061, 14369 }; + + template static __device__ void RGB2YUVConvert(const T* src, D& dst) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE(src[0] * c_RGB2YUVCoeffs_i[bidx^2] + src[1] * c_RGB2YUVCoeffs_i[1] + src[2] * c_RGB2YUVCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE((src[bidx^2] - Y) * c_RGB2YUVCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE((src[bidx] - Y) * c_RGB2YUVCoeffs_i[4] + delta, yuv_shift); + + dst.x = saturate_cast(Y); + dst.y = saturate_cast(Cr); + dst.z = saturate_cast(Cb); + } + + template static __device__ __forceinline__ void RGB2YUVConvert(const float* src, D& dst) + { + dst.x = src[0] * c_RGB2YUVCoeffs_f[bidx^2] + src[1] * c_RGB2YUVCoeffs_f[1] + src[2] * c_RGB2YUVCoeffs_f[bidx]; + dst.y = (src[bidx^2] - dst.x) * c_RGB2YUVCoeffs_f[3] + ColorChannel::half(); + dst.z = (src[bidx] - dst.x) * c_RGB2YUVCoeffs_f[4] + ColorChannel::half(); + } + + template struct RGB2YUV + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + RGB2YUVConvert(&src.x, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB2YUV() {} + __host__ __device__ __forceinline__ RGB2YUV(const RGB2YUV&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2YUV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_YUV2RGBCoeffs_f[5] = { 2.032f, -0.395f, -0.581f, 1.140f }; + __constant__ int c_YUV2RGBCoeffs_i[5] = { 33292, -6472, -9519, 18678 }; + + template static __device__ void YUV2RGBConvert(const T& src, D* dst) + { + const int b = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[3], yuv_shift); + + const int g = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[2] + + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[1], yuv_shift); + + const int r = src.x + CV_DESCALE((src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[0], yuv_shift); + + dst[bidx] = saturate_cast(b); + dst[1] = saturate_cast(g); + dst[bidx^2] = saturate_cast(r); + } + + template static __device__ uint YUV2RGBConvert(uint src) + { + const int x = 0xff & (src); + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const int b = x + CV_DESCALE((z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[3], yuv_shift); + + const int g = x + CV_DESCALE((z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[2] + + (y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[1], yuv_shift); + + const int r = x + CV_DESCALE((y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[0], yuv_shift); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(b) << (bidx * 8); + dst |= saturate_cast(g) << 8; + dst |= saturate_cast(r) << ((bidx ^ 2) * 8); + + return dst; + } + + template static __device__ __forceinline__ void YUV2RGBConvert(const T& src, float* dst) + { + dst[bidx] = src.x + (src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_f[3]; + + dst[1] = src.x + (src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_f[2] + + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_f[1]; + + dst[bidx^2] = src.x + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_f[0]; + } + + template struct YUV2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + YUV2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ YUV2RGB() {} + __host__ __device__ __forceinline__ YUV2RGB(const YUV2RGB&) {} + }; + + template struct YUV2RGB : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return YUV2RGBConvert(src); + } + __host__ __device__ __forceinline__ YUV2RGB() {} + __host__ __device__ __forceinline__ YUV2RGB(const YUV2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::YUV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> YCrCb ////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2YCrCbCoeffs_f[5] = {0.299f, 0.587f, 0.114f, 0.713f, 0.564f}; + __constant__ int c_RGB2YCrCbCoeffs_i[5] = {R2Y, G2Y, B2Y, 11682, 9241}; + + template static __device__ void RGB2YCrCbConvert(const T* src, D& dst) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE(src[0] * c_RGB2YCrCbCoeffs_i[bidx^2] + src[1] * c_RGB2YCrCbCoeffs_i[1] + src[2] * c_RGB2YCrCbCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE((src[bidx^2] - Y) * c_RGB2YCrCbCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE((src[bidx] - Y) * c_RGB2YCrCbCoeffs_i[4] + delta, yuv_shift); + + dst.x = saturate_cast(Y); + dst.y = saturate_cast(Cr); + dst.z = saturate_cast(Cb); + } + + template static __device__ uint RGB2YCrCbConvert(uint src) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE((0xffu & src) * c_RGB2YCrCbCoeffs_i[bidx^2] + (0xffu & (src >> 8)) * c_RGB2YCrCbCoeffs_i[1] + (0xffu & (src >> 16)) * c_RGB2YCrCbCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE(((0xffu & (src >> ((bidx ^ 2) * 8))) - Y) * c_RGB2YCrCbCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE(((0xffu & (src >> (bidx * 8))) - Y) * c_RGB2YCrCbCoeffs_i[4] + delta, yuv_shift); + + uint dst = 0; + + dst |= saturate_cast(Y); + dst |= saturate_cast(Cr) << 8; + dst |= saturate_cast(Cb) << 16; + + return dst; + } + + template static __device__ __forceinline__ void RGB2YCrCbConvert(const float* src, D& dst) + { + dst.x = src[0] * c_RGB2YCrCbCoeffs_f[bidx^2] + src[1] * c_RGB2YCrCbCoeffs_f[1] + src[2] * c_RGB2YCrCbCoeffs_f[bidx]; + dst.y = (src[bidx^2] - dst.x) * c_RGB2YCrCbCoeffs_f[3] + ColorChannel::half(); + dst.z = (src[bidx] - dst.x) * c_RGB2YCrCbCoeffs_f[4] + ColorChannel::half(); + } + + template struct RGB2YCrCb + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + RGB2YCrCbConvert(&src.x, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB2YCrCb() {} + __host__ __device__ __forceinline__ RGB2YCrCb(const RGB2YCrCb&) {} + }; + + template struct RGB2YCrCb : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return RGB2YCrCbConvert(src); + } + + __host__ __device__ __forceinline__ RGB2YCrCb() {} + __host__ __device__ __forceinline__ RGB2YCrCb(const RGB2YCrCb&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2YCrCb functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_YCrCb2RGBCoeffs_f[5] = {1.403f, -0.714f, -0.344f, 1.773f}; + __constant__ int c_YCrCb2RGBCoeffs_i[5] = {22987, -11698, -5636, 29049}; + + template static __device__ void YCrCb2RGBConvert(const T& src, D* dst) + { + const int b = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[3], yuv_shift); + const int g = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[2] + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[1], yuv_shift); + const int r = src.x + CV_DESCALE((src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[0], yuv_shift); + + dst[bidx] = saturate_cast(b); + dst[1] = saturate_cast(g); + dst[bidx^2] = saturate_cast(r); + } + + template static __device__ uint YCrCb2RGBConvert(uint src) + { + const int x = 0xff & (src); + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const int b = x + CV_DESCALE((z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[3], yuv_shift); + const int g = x + CV_DESCALE((z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[2] + (y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[1], yuv_shift); + const int r = x + CV_DESCALE((y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[0], yuv_shift); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(b) << (bidx * 8); + dst |= saturate_cast(g) << 8; + dst |= saturate_cast(r) << ((bidx ^ 2) * 8); + + return dst; + } + + template __device__ __forceinline__ void YCrCb2RGBConvert(const T& src, float* dst) + { + dst[bidx] = src.x + (src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[3]; + dst[1] = src.x + (src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[2] + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[1]; + dst[bidx^2] = src.x + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[0]; + } + + template struct YCrCb2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + YCrCb2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ YCrCb2RGB() {} + __host__ __device__ __forceinline__ YCrCb2RGB(const YCrCb2RGB&) {} + }; + + template struct YCrCb2RGB : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return YCrCb2RGBConvert(src); + } + __host__ __device__ __forceinline__ YCrCb2RGB() {} + __host__ __device__ __forceinline__ YCrCb2RGB(const YCrCb2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::YCrCb2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +////////////////////////////////////// RGB <-> XYZ /////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2XYZ_D65f[9] = { 0.412453f, 0.357580f, 0.180423f, 0.212671f, 0.715160f, 0.072169f, 0.019334f, 0.119193f, 0.950227f }; + __constant__ int c_RGB2XYZ_D65i[9] = { 1689, 1465, 739, 871, 2929, 296, 79, 488, 3892 }; + + template static __device__ __forceinline__ void RGB2XYZConvert(const T* src, D& dst) + { + dst.z = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[6] + src[1] * c_RGB2XYZ_D65i[7] + src[bidx] * c_RGB2XYZ_D65i[8], xyz_shift)); + dst.x = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[0] + src[1] * c_RGB2XYZ_D65i[1] + src[bidx] * c_RGB2XYZ_D65i[2], xyz_shift)); + dst.y = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[3] + src[1] * c_RGB2XYZ_D65i[4] + src[bidx] * c_RGB2XYZ_D65i[5], xyz_shift)); + } + + template static __device__ __forceinline__ uint RGB2XYZConvert(uint src) + { + const uint b = 0xffu & (src >> (bidx * 8)); + const uint g = 0xffu & (src >> 8); + const uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + + const uint x = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[0] + g * c_RGB2XYZ_D65i[1] + b * c_RGB2XYZ_D65i[2], xyz_shift)); + const uint y = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[3] + g * c_RGB2XYZ_D65i[4] + b * c_RGB2XYZ_D65i[5], xyz_shift)); + const uint z = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[6] + g * c_RGB2XYZ_D65i[7] + b * c_RGB2XYZ_D65i[8], xyz_shift)); + + uint dst = 0; + + dst |= x; + dst |= y << 8; + dst |= z << 16; + + return dst; + } + + template static __device__ __forceinline__ void RGB2XYZConvert(const float* src, D& dst) + { + dst.x = src[bidx^2] * c_RGB2XYZ_D65f[0] + src[1] * c_RGB2XYZ_D65f[1] + src[bidx] * c_RGB2XYZ_D65f[2]; + dst.y = src[bidx^2] * c_RGB2XYZ_D65f[3] + src[1] * c_RGB2XYZ_D65f[4] + src[bidx] * c_RGB2XYZ_D65f[5]; + dst.z = src[bidx^2] * c_RGB2XYZ_D65f[6] + src[1] * c_RGB2XYZ_D65f[7] + src[bidx] * c_RGB2XYZ_D65f[8]; + } + + template struct RGB2XYZ + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2XYZConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2XYZ() {} + __host__ __device__ __forceinline__ RGB2XYZ(const RGB2XYZ&) {} + }; + + template struct RGB2XYZ : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2XYZConvert(src); + } + __host__ __device__ __forceinline__ RGB2XYZ() {} + __host__ __device__ __forceinline__ RGB2XYZ(const RGB2XYZ&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2XYZ functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_XYZ2sRGB_D65f[9] = { 3.240479f, -1.53715f, -0.498535f, -0.969256f, 1.875991f, 0.041556f, 0.055648f, -0.204043f, 1.057311f }; + __constant__ int c_XYZ2sRGB_D65i[9] = { 13273, -6296, -2042, -3970, 7684, 170, 228, -836, 4331 }; + + template static __device__ __forceinline__ void XYZ2RGBConvert(const T& src, D* dst) + { + dst[bidx^2] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[0] + src.y * c_XYZ2sRGB_D65i[1] + src.z * c_XYZ2sRGB_D65i[2], xyz_shift)); + dst[1] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[3] + src.y * c_XYZ2sRGB_D65i[4] + src.z * c_XYZ2sRGB_D65i[5], xyz_shift)); + dst[bidx] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[6] + src.y * c_XYZ2sRGB_D65i[7] + src.z * c_XYZ2sRGB_D65i[8], xyz_shift)); + } + + template static __device__ __forceinline__ uint XYZ2RGBConvert(uint src) + { + const int x = 0xff & src; + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const uint r = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[0] + y * c_XYZ2sRGB_D65i[1] + z * c_XYZ2sRGB_D65i[2], xyz_shift)); + const uint g = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[3] + y * c_XYZ2sRGB_D65i[4] + z * c_XYZ2sRGB_D65i[5], xyz_shift)); + const uint b = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[6] + y * c_XYZ2sRGB_D65i[7] + z * c_XYZ2sRGB_D65i[8], xyz_shift)); + + uint dst = 0xffu << 24; + + dst |= b << (bidx * 8); + dst |= g << 8; + dst |= r << ((bidx ^ 2) * 8); + + return dst; + } + + template static __device__ __forceinline__ void XYZ2RGBConvert(const T& src, float* dst) + { + dst[bidx^2] = src.x * c_XYZ2sRGB_D65f[0] + src.y * c_XYZ2sRGB_D65f[1] + src.z * c_XYZ2sRGB_D65f[2]; + dst[1] = src.x * c_XYZ2sRGB_D65f[3] + src.y * c_XYZ2sRGB_D65f[4] + src.z * c_XYZ2sRGB_D65f[5]; + dst[bidx] = src.x * c_XYZ2sRGB_D65f[6] + src.y * c_XYZ2sRGB_D65f[7] + src.z * c_XYZ2sRGB_D65f[8]; + } + + template struct XYZ2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + XYZ2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ XYZ2RGB() {} + __host__ __device__ __forceinline__ XYZ2RGB(const XYZ2RGB&) {} + }; + + template struct XYZ2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return XYZ2RGBConvert(src); + } + __host__ __device__ __forceinline__ XYZ2RGB() {} + __host__ __device__ __forceinline__ XYZ2RGB(const XYZ2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::XYZ2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +////////////////////////////////////// RGB <-> HSV /////////////////////////////////////// + + namespace color_detail + { + __constant__ int c_HsvDivTable [256] = {0, 1044480, 522240, 348160, 261120, 208896, 174080, 149211, 130560, 116053, 104448, 94953, 87040, 80345, 74606, 69632, 65280, 61440, 58027, 54973, 52224, 49737, 47476, 45412, 43520, 41779, 40172, 38684, 37303, 36017, 34816, 33693, 32640, 31651, 30720, 29842, 29013, 28229, 27486, 26782, 26112, 25475, 24869, 24290, 23738, 23211, 22706, 22223, 21760, 21316, 20890, 20480, 20086, 19707, 19342, 18991, 18651, 18324, 18008, 17703, 17408, 17123, 16846, 16579, 16320, 16069, 15825, 15589, 15360, 15137, 14921, 14711, 14507, 14308, 14115, 13926, 13743, 13565, 13391, 13221, 13056, 12895, 12738, 12584, 12434, 12288, 12145, 12006, 11869, 11736, 11605, 11478, 11353, 11231, 11111, 10995, 10880, 10768, 10658, 10550, 10445, 10341, 10240, 10141, 10043, 9947, 9854, 9761, 9671, 9582, 9495, 9410, 9326, 9243, 9162, 9082, 9004, 8927, 8852, 8777, 8704, 8632, 8561, 8492, 8423, 8356, 8290, 8224, 8160, 8097, 8034, 7973, 7913, 7853, 7795, 7737, 7680, 7624, 7569, 7514, 7461, 7408, 7355, 7304, 7253, 7203, 7154, 7105, 7057, 7010, 6963, 6917, 6872, 6827, 6782, 6739, 6695, 6653, 6611, 6569, 6528, 6487, 6447, 6408, 6369, 6330, 6292, 6254, 6217, 6180, 6144, 6108, 6073, 6037, 6003, 5968, 5935, 5901, 5868, 5835, 5803, 5771, 5739, 5708, 5677, 5646, 5615, 5585, 5556, 5526, 5497, 5468, 5440, 5412, 5384, 5356, 5329, 5302, 5275, 5249, 5222, 5196, 5171, 5145, 5120, 5095, 5070, 5046, 5022, 4998, 4974, 4950, 4927, 4904, 4881, 4858, 4836, 4813, 4791, 4769, 4748, 4726, 4705, 4684, 4663, 4642, 4622, 4601, 4581, 4561, 4541, 4522, 4502, 4483, 4464, 4445, 4426, 4407, 4389, 4370, 4352, 4334, 4316, 4298, 4281, 4263, 4246, 4229, 4212, 4195, 4178, 4161, 4145, 4128, 4112, 4096}; + __constant__ int c_HsvDivTable180[256] = {0, 122880, 61440, 40960, 30720, 24576, 20480, 17554, 15360, 13653, 12288, 11171, 10240, 9452, 8777, 8192, 7680, 7228, 6827, 6467, 6144, 5851, 5585, 5343, 5120, 4915, 4726, 4551, 4389, 4237, 4096, 3964, 3840, 3724, 3614, 3511, 3413, 3321, 3234, 3151, 3072, 2997, 2926, 2858, 2793, 2731, 2671, 2614, 2560, 2508, 2458, 2409, 2363, 2318, 2276, 2234, 2194, 2156, 2119, 2083, 2048, 2014, 1982, 1950, 1920, 1890, 1862, 1834, 1807, 1781, 1755, 1731, 1707, 1683, 1661, 1638, 1617, 1596, 1575, 1555, 1536, 1517, 1499, 1480, 1463, 1446, 1429, 1412, 1396, 1381, 1365, 1350, 1336, 1321, 1307, 1293, 1280, 1267, 1254, 1241, 1229, 1217, 1205, 1193, 1182, 1170, 1159, 1148, 1138, 1127, 1117, 1107, 1097, 1087, 1078, 1069, 1059, 1050, 1041, 1033, 1024, 1016, 1007, 999, 991, 983, 975, 968, 960, 953, 945, 938, 931, 924, 917, 910, 904, 897, 890, 884, 878, 871, 865, 859, 853, 847, 842, 836, 830, 825, 819, 814, 808, 803, 798, 793, 788, 783, 778, 773, 768, 763, 759, 754, 749, 745, 740, 736, 731, 727, 723, 719, 714, 710, 706, 702, 698, 694, 690, 686, 683, 679, 675, 671, 668, 664, 661, 657, 654, 650, 647, 643, 640, 637, 633, 630, 627, 624, 621, 617, 614, 611, 608, 605, 602, 599, 597, 594, 591, 588, 585, 582, 580, 577, 574, 572, 569, 566, 564, 561, 559, 556, 554, 551, 549, 546, 544, 541, 539, 537, 534, 532, 530, 527, 525, 523, 521, 518, 516, 514, 512, 510, 508, 506, 504, 502, 500, 497, 495, 493, 492, 490, 488, 486, 484, 482}; + __constant__ int c_HsvDivTable256[256] = {0, 174763, 87381, 58254, 43691, 34953, 29127, 24966, 21845, 19418, 17476, 15888, 14564, 13443, 12483, 11651, 10923, 10280, 9709, 9198, 8738, 8322, 7944, 7598, 7282, 6991, 6722, 6473, 6242, 6026, 5825, 5638, 5461, 5296, 5140, 4993, 4855, 4723, 4599, 4481, 4369, 4263, 4161, 4064, 3972, 3884, 3799, 3718, 3641, 3567, 3495, 3427, 3361, 3297, 3236, 3178, 3121, 3066, 3013, 2962, 2913, 2865, 2819, 2774, 2731, 2689, 2648, 2608, 2570, 2533, 2497, 2461, 2427, 2394, 2362, 2330, 2300, 2270, 2241, 2212, 2185, 2158, 2131, 2106, 2081, 2056, 2032, 2009, 1986, 1964, 1942, 1920, 1900, 1879, 1859, 1840, 1820, 1802, 1783, 1765, 1748, 1730, 1713, 1697, 1680, 1664, 1649, 1633, 1618, 1603, 1589, 1574, 1560, 1547, 1533, 1520, 1507, 1494, 1481, 1469, 1456, 1444, 1432, 1421, 1409, 1398, 1387, 1376, 1365, 1355, 1344, 1334, 1324, 1314, 1304, 1295, 1285, 1276, 1266, 1257, 1248, 1239, 1231, 1222, 1214, 1205, 1197, 1189, 1181, 1173, 1165, 1157, 1150, 1142, 1135, 1128, 1120, 1113, 1106, 1099, 1092, 1085, 1079, 1072, 1066, 1059, 1053, 1046, 1040, 1034, 1028, 1022, 1016, 1010, 1004, 999, 993, 987, 982, 976, 971, 966, 960, 955, 950, 945, 940, 935, 930, 925, 920, 915, 910, 906, 901, 896, 892, 887, 883, 878, 874, 869, 865, 861, 857, 853, 848, 844, 840, 836, 832, 828, 824, 820, 817, 813, 809, 805, 802, 798, 794, 791, 787, 784, 780, 777, 773, 770, 767, 763, 760, 757, 753, 750, 747, 744, 741, 737, 734, 731, 728, 725, 722, 719, 716, 713, 710, 708, 705, 702, 699, 696, 694, 691, 688, 685}; + + template static __device__ void RGB2HSVConvert(const uchar* src, D& dst) + { + const int hsv_shift = 12; + const int* hdiv_table = hr == 180 ? c_HsvDivTable180 : c_HsvDivTable256; + + int b = src[bidx], g = src[1], r = src[bidx^2]; + int h, s, v = b; + int vmin = b, diff; + int vr, vg; + + v = ::max(v, g); + v = ::max(v, r); + vmin = ::min(vmin, g); + vmin = ::min(vmin, r); + + diff = v - vmin; + vr = (v == r) * -1; + vg = (v == g) * -1; + + s = (diff * c_HsvDivTable[v] + (1 << (hsv_shift-1))) >> hsv_shift; + h = (vr & (g - b)) + (~vr & ((vg & (b - r + 2 * diff)) + ((~vg) & (r - g + 4 * diff)))); + h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + h += (h < 0) * hr; + + dst.x = saturate_cast(h); + dst.y = (uchar)s; + dst.z = (uchar)v; + } + + template static __device__ uint RGB2HSVConvert(uint src) + { + const int hsv_shift = 12; + const int* hdiv_table = hr == 180 ? c_HsvDivTable180 : c_HsvDivTable256; + + const int b = 0xff & (src >> (bidx * 8)); + const int g = 0xff & (src >> 8); + const int r = 0xff & (src >> ((bidx ^ 2) * 8)); + + int h, s, v = b; + int vmin = b, diff; + int vr, vg; + + v = ::max(v, g); + v = ::max(v, r); + vmin = ::min(vmin, g); + vmin = ::min(vmin, r); + + diff = v - vmin; + vr = (v == r) * -1; + vg = (v == g) * -1; + + s = (diff * c_HsvDivTable[v] + (1 << (hsv_shift-1))) >> hsv_shift; + h = (vr & (g - b)) + (~vr & ((vg & (b - r + 2 * diff)) + ((~vg) & (r - g + 4 * diff)))); + h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + h += (h < 0) * hr; + + uint dst = 0; + + dst |= saturate_cast(h); + dst |= (0xffu & s) << 8; + dst |= (0xffu & v) << 16; + + return dst; + } + + template static __device__ void RGB2HSVConvert(const float* src, D& dst) + { + const float hscale = hr * (1.f / 360.f); + + float b = src[bidx], g = src[1], r = src[bidx^2]; + float h, s, v; + + float vmin, diff; + + v = vmin = r; + v = fmax(v, g); + v = fmax(v, b); + vmin = fmin(vmin, g); + vmin = fmin(vmin, b); + + diff = v - vmin; + s = diff / (float)(::fabs(v) + numeric_limits::epsilon()); + diff = (float)(60. / (diff + numeric_limits::epsilon())); + + h = (v == r) * (g - b) * diff; + h += (v != r && v == g) * ((b - r) * diff + 120.f); + h += (v != r && v != g) * ((r - g) * diff + 240.f); + h += (h < 0) * 360.f; + + dst.x = h * hscale; + dst.y = s; + dst.z = v; + } + + template struct RGB2HSV + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2HSVConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2HSV() {} + __host__ __device__ __forceinline__ RGB2HSV(const RGB2HSV&) {} + }; + + template struct RGB2HSV : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2HSVConvert(src); + } + __host__ __device__ __forceinline__ RGB2HSV() {} + __host__ __device__ __forceinline__ RGB2HSV(const RGB2HSV&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ int c_HsvSectorData[6][3] = { {1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0} }; + + template static __device__ void HSV2RGBConvert(const T& src, float* dst) + { + const float hscale = 6.f / hr; + + float h = src.x, s = src.y, v = src.z; + float b = v, g = v, r = v; + + if (s != 0) + { + h *= hscale; + + if( h < 0 ) + do h += 6; while( h < 0 ); + else if( h >= 6 ) + do h -= 6; while( h >= 6 ); + + int sector = __float2int_rd(h); + h -= sector; + + if ( (unsigned)sector >= 6u ) + { + sector = 0; + h = 0.f; + } + + float tab[4]; + tab[0] = v; + tab[1] = v * (1.f - s); + tab[2] = v * (1.f - s * h); + tab[3] = v * (1.f - s * (1.f - h)); + + b = tab[c_HsvSectorData[sector][0]]; + g = tab[c_HsvSectorData[sector][1]]; + r = tab[c_HsvSectorData[sector][2]]; + } + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; + } + + template static __device__ void HSV2RGBConvert(const T& src, uchar* dst) + { + float3 buf; + + buf.x = src.x; + buf.y = src.y * (1.f / 255.f); + buf.z = src.z * (1.f / 255.f); + + HSV2RGBConvert(buf, &buf.x); + + dst[0] = saturate_cast(buf.x * 255.f); + dst[1] = saturate_cast(buf.y * 255.f); + dst[2] = saturate_cast(buf.z * 255.f); + } + + template static __device__ uint HSV2RGBConvert(uint src) + { + float3 buf; + + buf.x = src & 0xff; + buf.y = ((src >> 8) & 0xff) * (1.f/255.f); + buf.z = ((src >> 16) & 0xff) * (1.f/255.f); + + HSV2RGBConvert(buf, &buf.x); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x * 255.f); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct HSV2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + HSV2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ HSV2RGB() {} + __host__ __device__ __forceinline__ HSV2RGB(const HSV2RGB&) {} + }; + + template struct HSV2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return HSV2RGBConvert(src); + } + __host__ __device__ __forceinline__ HSV2RGB() {} + __host__ __device__ __forceinline__ HSV2RGB(const HSV2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +/////////////////////////////////////// RGB <-> HLS //////////////////////////////////////// + + namespace color_detail + { + template static __device__ void RGB2HLSConvert(const float* src, D& dst) + { + const float hscale = hr * (1.f / 360.f); + + float b = src[bidx], g = src[1], r = src[bidx^2]; + float h = 0.f, s = 0.f, l; + float vmin, vmax, diff; + + vmax = vmin = r; + vmax = fmax(vmax, g); + vmax = fmax(vmax, b); + vmin = fmin(vmin, g); + vmin = fmin(vmin, b); + + diff = vmax - vmin; + l = (vmax + vmin) * 0.5f; + + if (diff > numeric_limits::epsilon()) + { + s = (l < 0.5f) * diff / (vmax + vmin); + s += (l >= 0.5f) * diff / (2.0f - vmax - vmin); + + diff = 60.f / diff; + + h = (vmax == r) * (g - b) * diff; + h += (vmax != r && vmax == g) * ((b - r) * diff + 120.f); + h += (vmax != r && vmax != g) * ((r - g) * diff + 240.f); + h += (h < 0.f) * 360.f; + } + + dst.x = h * hscale; + dst.y = l; + dst.z = s; + } + + template static __device__ void RGB2HLSConvert(const uchar* src, D& dst) + { + float3 buf; + + buf.x = src[0] * (1.f / 255.f); + buf.y = src[1] * (1.f / 255.f); + buf.z = src[2] * (1.f / 255.f); + + RGB2HLSConvert(&buf.x, buf); + + dst.x = saturate_cast(buf.x); + dst.y = saturate_cast(buf.y*255.f); + dst.z = saturate_cast(buf.z*255.f); + } + + template static __device__ uint RGB2HLSConvert(uint src) + { + float3 buf; + + buf.x = (0xff & src) * (1.f / 255.f); + buf.y = (0xff & (src >> 8)) * (1.f / 255.f); + buf.z = (0xff & (src >> 16)) * (1.f / 255.f); + + RGB2HLSConvert(&buf.x, buf); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct RGB2HLS + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2HLSConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2HLS() {} + __host__ __device__ __forceinline__ RGB2HLS(const RGB2HLS&) {} + }; + + template struct RGB2HLS : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2HLSConvert(src); + } + __host__ __device__ __forceinline__ RGB2HLS() {} + __host__ __device__ __forceinline__ RGB2HLS(const RGB2HLS&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ int c_HlsSectorData[6][3] = { {1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0} }; + + template static __device__ void HLS2RGBConvert(const T& src, float* dst) + { + const float hscale = 6.0f / hr; + + float h = src.x, l = src.y, s = src.z; + float b = l, g = l, r = l; + + if (s != 0) + { + float p2 = (l <= 0.5f) * l * (1 + s); + p2 += (l > 0.5f) * (l + s - l * s); + float p1 = 2 * l - p2; + + h *= hscale; + + if( h < 0 ) + do h += 6; while( h < 0 ); + else if( h >= 6 ) + do h -= 6; while( h >= 6 ); + + int sector; + sector = __float2int_rd(h); + + h -= sector; + + float tab[4]; + tab[0] = p2; + tab[1] = p1; + tab[2] = p1 + (p2 - p1) * (1 - h); + tab[3] = p1 + (p2 - p1) * h; + + b = tab[c_HlsSectorData[sector][0]]; + g = tab[c_HlsSectorData[sector][1]]; + r = tab[c_HlsSectorData[sector][2]]; + } + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; + } + + template static __device__ void HLS2RGBConvert(const T& src, uchar* dst) + { + float3 buf; + + buf.x = src.x; + buf.y = src.y * (1.f / 255.f); + buf.z = src.z * (1.f / 255.f); + + HLS2RGBConvert(buf, &buf.x); + + dst[0] = saturate_cast(buf.x * 255.f); + dst[1] = saturate_cast(buf.y * 255.f); + dst[2] = saturate_cast(buf.z * 255.f); + } + + template static __device__ uint HLS2RGBConvert(uint src) + { + float3 buf; + + buf.x = 0xff & src; + buf.y = (0xff & (src >> 8)) * (1.f / 255.f); + buf.z = (0xff & (src >> 16)) * (1.f / 255.f); + + HLS2RGBConvert(buf, &buf.x); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x * 255.f); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct HLS2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + HLS2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ HLS2RGB() {} + __host__ __device__ __forceinline__ HLS2RGB(const HLS2RGB&) {} + }; + + template struct HLS2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return HLS2RGBConvert(src); + } + __host__ __device__ __forceinline__ HLS2RGB() {} + __host__ __device__ __forceinline__ HLS2RGB(const HLS2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> Lab ///////////////////////////////////// + + namespace color_detail + { + enum + { + LAB_CBRT_TAB_SIZE = 1024, + GAMMA_TAB_SIZE = 1024, + lab_shift = xyz_shift, + gamma_shift = 3, + lab_shift2 = (lab_shift + gamma_shift), + LAB_CBRT_TAB_SIZE_B = (256 * 3 / 2 * (1 << gamma_shift)) + }; + + __constant__ ushort c_sRGBGammaTab_b[] = {0,1,1,2,2,3,4,4,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,19,20,21,22,24,25,26,28,29,31,33,34,36,38,40,41,43,45,47,49,51,54,56,58,60,63,65,68,70,73,75,78,81,83,86,89,92,95,98,101,105,108,111,115,118,121,125,129,132,136,140,144,147,151,155,160,164,168,172,176,181,185,190,194,199,204,209,213,218,223,228,233,239,244,249,255,260,265,271,277,282,288,294,300,306,312,318,324,331,337,343,350,356,363,370,376,383,390,397,404,411,418,426,433,440,448,455,463,471,478,486,494,502,510,518,527,535,543,552,560,569,578,586,595,604,613,622,631,641,650,659,669,678,688,698,707,717,727,737,747,757,768,778,788,799,809,820,831,842,852,863,875,886,897,908,920,931,943,954,966,978,990,1002,1014,1026,1038,1050,1063,1075,1088,1101,1113,1126,1139,1152,1165,1178,1192,1205,1218,1232,1245,1259,1273,1287,1301,1315,1329,1343,1357,1372,1386,1401,1415,1430,1445,1460,1475,1490,1505,1521,1536,1551,1567,1583,1598,1614,1630,1646,1662,1678,1695,1711,1728,1744,1761,1778,1794,1811,1828,1846,1863,1880,1897,1915,1933,1950,1968,1986,2004,2022,2040}; + + __device__ __forceinline__ int LabCbrt_b(int i) + { + float x = i * (1.f / (255.f * (1 << gamma_shift))); + return (1 << lab_shift2) * (x < 0.008856f ? x * 7.787f + 0.13793103448275862f : ::cbrtf(x)); + } + + template + __device__ __forceinline__ void RGB2LabConvert_b(const T& src, D& dst) + { + const int Lscale = (116 * 255 + 50) / 100; + const int Lshift = -((16 * 255 * (1 << lab_shift2) + 50) / 100); + + int B = blueIdx == 0 ? src.x : src.z; + int G = src.y; + int R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = c_sRGBGammaTab_b[B]; + G = c_sRGBGammaTab_b[G]; + R = c_sRGBGammaTab_b[R]; + } + else + { + B <<= 3; + G <<= 3; + R <<= 3; + } + + int fX = LabCbrt_b(CV_DESCALE(B * 778 + G * 1541 + R * 1777, lab_shift)); + int fY = LabCbrt_b(CV_DESCALE(B * 296 + G * 2929 + R * 871, lab_shift)); + int fZ = LabCbrt_b(CV_DESCALE(B * 3575 + G * 448 + R * 73, lab_shift)); + + int L = CV_DESCALE(Lscale * fY + Lshift, lab_shift2); + int a = CV_DESCALE(500 * (fX - fY) + 128 * (1 << lab_shift2), lab_shift2); + int b = CV_DESCALE(200 * (fY - fZ) + 128 * (1 << lab_shift2), lab_shift2); + + dst.x = saturate_cast(L); + dst.y = saturate_cast(a); + dst.z = saturate_cast(b); + } + + __device__ __forceinline__ float splineInterpolate(float x, const float* tab, int n) + { + int ix = ::min(::max(int(x), 0), n-1); + x -= ix; + tab += ix * 4; + return ((tab[3] * x + tab[2]) * x + tab[1]) * x + tab[0]; + } + + __constant__ float c_sRGBGammaTab[] = {0,7.55853e-05,0.,-7.51331e-13,7.55853e-05,7.55853e-05,-2.25399e-12,3.75665e-12,0.000151171,7.55853e-05,9.01597e-12,-6.99932e-12,0.000226756,7.55853e-05,-1.1982e-11,2.41277e-12,0.000302341,7.55853e-05,-4.74369e-12,1.19001e-11,0.000377927,7.55853e-05,3.09568e-11,-2.09095e-11,0.000453512,7.55853e-05,-3.17718e-11,1.35303e-11,0.000529097,7.55853e-05,8.81905e-12,-4.10782e-12,0.000604683,7.55853e-05,-3.50439e-12,2.90097e-12,0.000680268,7.55853e-05,5.19852e-12,-7.49607e-12,0.000755853,7.55853e-05,-1.72897e-11,2.70833e-11,0.000831439,7.55854e-05,6.39602e-11,-4.26295e-11,0.000907024,7.55854e-05,-6.39282e-11,2.70193e-11,0.000982609,7.55853e-05,1.71298e-11,-7.24017e-12,0.00105819,7.55853e-05,-4.59077e-12,1.94137e-12,0.00113378,7.55853e-05,1.23333e-12,-5.25291e-13,0.00120937,7.55853e-05,-3.42545e-13,1.59799e-13,0.00128495,7.55853e-05,1.36852e-13,-1.13904e-13,0.00136054,7.55853e-05,-2.04861e-13,2.95818e-13,0.00143612,7.55853e-05,6.82594e-13,-1.06937e-12,0.00151171,7.55853e-05,-2.52551e-12,3.98166e-12,0.00158729,7.55853e-05,9.41946e-12,-1.48573e-11,0.00166288,7.55853e-05,-3.51523e-11,5.54474e-11,0.00173846,7.55854e-05,1.3119e-10,-9.0517e-11,0.00181405,7.55854e-05,-1.40361e-10,7.37899e-11,0.00188963,7.55853e-05,8.10085e-11,-8.82272e-11,0.00196522,7.55852e-05,-1.83673e-10,1.62704e-10,0.0020408,7.55853e-05,3.04438e-10,-2.13341e-10,0.00211639,7.55853e-05,-3.35586e-10,2.25e-10,0.00219197,7.55853e-05,3.39414e-10,-2.20997e-10,0.00226756,7.55853e-05,-3.23576e-10,1.93326e-10,0.00234315,7.55853e-05,2.564e-10,-8.66446e-11,0.00241873,7.55855e-05,-3.53328e-12,-7.9578e-11,0.00249432,7.55853e-05,-2.42267e-10,1.72126e-10,0.0025699,7.55853e-05,2.74111e-10,-1.43265e-10,0.00264549,7.55854e-05,-1.55683e-10,-6.47292e-11,0.00272107,7.55849e-05,-3.4987e-10,8.67842e-10,0.00279666,7.55868e-05,2.25366e-09,-3.8723e-09,0.00287224,7.55797e-05,-9.36325e-09,1.5087e-08,0.00294783,7.56063e-05,3.58978e-08,-5.69415e-08,0.00302341,7.55072e-05,-1.34927e-07,2.13144e-07,0.003099,7.58768e-05,5.04507e-07,1.38713e-07,0.00317552,7.7302e-05,9.20646e-07,-1.55186e-07,0.00325359,7.86777e-05,4.55087e-07,4.26813e-08,0.00333276,7.97159e-05,5.83131e-07,-1.06495e-08,0.00341305,8.08502e-05,5.51182e-07,3.87467e-09,0.00349446,8.19642e-05,5.62806e-07,-1.92586e-10,0.00357698,8.30892e-05,5.62228e-07,1.0866e-09,0.00366063,8.4217e-05,5.65488e-07,5.02818e-10,0.00374542,8.53494e-05,5.66997e-07,8.60211e-10,0.00383133,8.6486e-05,5.69577e-07,7.13044e-10,0.00391839,8.76273e-05,5.71716e-07,4.78527e-10,0.00400659,8.87722e-05,5.73152e-07,1.09818e-09,0.00409594,8.99218e-05,5.76447e-07,2.50964e-10,0.00418644,9.10754e-05,5.772e-07,1.15762e-09,0.00427809,9.22333e-05,5.80672e-07,2.40865e-10,0.0043709,9.33954e-05,5.81395e-07,1.13854e-09,0.00446488,9.45616e-05,5.84811e-07,3.27267e-10,0.00456003,9.57322e-05,5.85792e-07,8.1197e-10,0.00465635,9.69062e-05,5.88228e-07,6.15823e-10,0.00475384,9.80845e-05,5.90076e-07,9.15747e-10,0.00485252,9.92674e-05,5.92823e-07,3.778e-10,0.00495238,0.000100454,5.93956e-07,8.32623e-10,0.00505343,0.000101645,5.96454e-07,4.82695e-10,0.00515567,0.000102839,5.97902e-07,9.61904e-10,0.00525911,0.000104038,6.00788e-07,3.26281e-10,0.00536375,0.00010524,6.01767e-07,9.926e-10,0.00546959,0.000106447,6.04745e-07,3.59933e-10,0.00557664,0.000107657,6.05824e-07,8.2728e-10,0.0056849,0.000108871,6.08306e-07,5.21898e-10,0.00579438,0.00011009,6.09872e-07,8.10492e-10,0.00590508,0.000111312,6.12303e-07,4.27046e-10,0.00601701,0.000112538,6.13585e-07,7.40878e-10,0.00613016,0.000113767,6.15807e-07,8.00469e-10,0.00624454,0.000115001,6.18209e-07,2.48178e-10,0.00636016,0.000116238,6.18953e-07,1.00073e-09,0.00647702,0.000117479,6.21955e-07,4.05654e-10,0.00659512,0.000118724,6.23172e-07,6.36192e-10,0.00671447,0.000119973,6.25081e-07,7.74927e-10,0.00683507,0.000121225,6.27406e-07,4.54975e-10,0.00695692,0.000122481,6.28771e-07,6.64841e-10,0.00708003,0.000123741,6.30765e-07,6.10972e-10,0.00720441,0.000125004,6.32598e-07,6.16543e-10,0.00733004,0.000126271,6.34448e-07,6.48204e-10,0.00745695,0.000127542,6.36392e-07,5.15835e-10,0.00758513,0.000128816,6.3794e-07,5.48103e-10,0.00771458,0.000130094,6.39584e-07,1.01706e-09,0.00784532,0.000131376,6.42635e-07,4.0283e-11,0.00797734,0.000132661,6.42756e-07,6.84471e-10,0.00811064,0.000133949,6.4481e-07,9.47144e-10,0.00824524,0.000135241,6.47651e-07,1.83472e-10,0.00838112,0.000136537,6.48201e-07,1.11296e-09,0.00851831,0.000137837,6.5154e-07,2.13163e-11,0.0086568,0.00013914,6.51604e-07,6.64462e-10,0.00879659,0.000140445,6.53598e-07,1.04613e-09,0.00893769,0.000141756,6.56736e-07,-1.92377e-10,0.0090801,0.000143069,6.56159e-07,1.58601e-09,0.00922383,0.000144386,6.60917e-07,-5.63754e-10,0.00936888,0.000145706,6.59226e-07,1.60033e-09,0.00951524,0.000147029,6.64027e-07,-2.49543e-10,0.00966294,0.000148356,6.63278e-07,1.26043e-09,0.00981196,0.000149687,6.67059e-07,-1.35572e-10,0.00996231,0.00015102,6.66653e-07,1.14458e-09,0.010114,0.000152357,6.70086e-07,2.13864e-10,0.010267,0.000153698,6.70728e-07,7.93856e-10,0.0104214,0.000155042,6.73109e-07,3.36077e-10,0.0105771,0.000156389,6.74118e-07,6.55765e-10,0.0107342,0.000157739,6.76085e-07,7.66211e-10,0.0108926,0.000159094,6.78384e-07,4.66116e-12,0.0110524,0.000160451,6.78398e-07,1.07775e-09,0.0112135,0.000161811,6.81631e-07,3.41023e-10,0.011376,0.000163175,6.82654e-07,3.5205e-10,0.0115398,0.000164541,6.8371e-07,1.04473e-09,0.0117051,0.000165912,6.86844e-07,1.25757e-10,0.0118717,0.000167286,6.87222e-07,3.14818e-10,0.0120396,0.000168661,6.88166e-07,1.40886e-09,0.012209,0.000170042,6.92393e-07,-3.62244e-10,0.0123797,0.000171425,6.91306e-07,9.71397e-10,0.0125518,0.000172811,6.9422e-07,2.02003e-10,0.0127253,0.0001742,6.94826e-07,1.01448e-09,0.0129002,0.000175593,6.97869e-07,3.96653e-10,0.0130765,0.00017699,6.99059e-07,1.92927e-10,0.0132542,0.000178388,6.99638e-07,6.94305e-10,0.0134333,0.00017979,7.01721e-07,7.55108e-10,0.0136138,0.000181195,7.03986e-07,1.05918e-11,0.0137957,0.000182603,7.04018e-07,1.06513e-09,0.013979,0.000184015,7.07214e-07,3.85512e-10,0.0141637,0.00018543,7.0837e-07,1.86769e-10,0.0143499,0.000186848,7.0893e-07,7.30116e-10,0.0145374,0.000188268,7.11121e-07,6.17983e-10,0.0147264,0.000189692,7.12975e-07,5.23282e-10,0.0149168,0.000191119,7.14545e-07,8.28398e-11,0.0151087,0.000192549,7.14793e-07,1.0081e-09,0.0153019,0.000193981,7.17817e-07,5.41244e-10,0.0154966,0.000195418,7.19441e-07,-3.7907e-10,0.0156928,0.000196856,7.18304e-07,1.90641e-09,0.0158903,0.000198298,7.24023e-07,-7.27387e-10,0.0160893,0.000199744,7.21841e-07,1.00317e-09,0.0162898,0.000201191,7.24851e-07,4.39949e-10,0.0164917,0.000202642,7.2617e-07,9.6234e-10,0.0166951,0.000204097,7.29057e-07,-5.64019e-10,0.0168999,0.000205554,7.27365e-07,1.29374e-09,0.0171062,0.000207012,7.31247e-07,9.77025e-10,0.017314,0.000208478,7.34178e-07,-1.47651e-09,0.0175232,0.000209942,7.29748e-07,3.06636e-09,0.0177338,0.00021141,7.38947e-07,-1.47573e-09,0.017946,0.000212884,7.3452e-07,9.7386e-10,0.0181596,0.000214356,7.37442e-07,1.30562e-09,0.0183747,0.000215835,7.41358e-07,-6.08376e-10,0.0185913,0.000217315,7.39533e-07,1.12785e-09,0.0188093,0.000218798,7.42917e-07,-1.77711e-10,0.0190289,0.000220283,7.42384e-07,1.44562e-09,0.0192499,0.000221772,7.46721e-07,-1.68825e-11,0.0194724,0.000223266,7.4667e-07,4.84533e-10,0.0196964,0.000224761,7.48124e-07,-5.85298e-11,0.0199219,0.000226257,7.47948e-07,1.61217e-09,0.0201489,0.000227757,7.52785e-07,-8.02136e-10,0.0203775,0.00022926,7.50378e-07,1.59637e-09,0.0206075,0.000230766,7.55167e-07,4.47168e-12,0.020839,0.000232276,7.55181e-07,2.48387e-10,0.021072,0.000233787,7.55926e-07,8.6474e-10,0.0213066,0.000235302,7.5852e-07,1.78299e-11,0.0215426,0.000236819,7.58573e-07,9.26567e-10,0.0217802,0.000238339,7.61353e-07,1.34529e-12,0.0220193,0.000239862,7.61357e-07,9.30659e-10,0.0222599,0.000241387,7.64149e-07,1.34529e-12,0.0225021,0.000242915,7.64153e-07,9.26567e-10,0.0227458,0.000244447,7.66933e-07,1.76215e-11,0.022991,0.00024598,7.66986e-07,8.65536e-10,0.0232377,0.000247517,7.69582e-07,2.45677e-10,0.023486,0.000249057,7.70319e-07,1.44193e-11,0.0237358,0.000250598,7.70363e-07,1.55918e-09,0.0239872,0.000252143,7.7504e-07,-6.63173e-10,0.0242401,0.000253691,7.73051e-07,1.09357e-09,0.0244946,0.000255241,7.76331e-07,1.41919e-11,0.0247506,0.000256793,7.76374e-07,7.12248e-10,0.0250082,0.000258348,7.78511e-07,8.62049e-10,0.0252673,0.000259908,7.81097e-07,-4.35061e-10,0.025528,0.000261469,7.79792e-07,8.7825e-10,0.0257902,0.000263031,7.82426e-07,6.47181e-10,0.0260541,0.000264598,7.84368e-07,2.58448e-10,0.0263194,0.000266167,7.85143e-07,1.81558e-10,0.0265864,0.000267738,7.85688e-07,8.78041e-10,0.0268549,0.000269312,7.88322e-07,3.15102e-11,0.027125,0.000270889,7.88417e-07,8.58525e-10,0.0273967,0.000272468,7.90992e-07,2.59812e-10,0.02767,0.000274051,7.91772e-07,-3.5224e-11,0.0279448,0.000275634,7.91666e-07,1.74377e-09,0.0282212,0.000277223,7.96897e-07,-1.35196e-09,0.0284992,0.000278813,7.92841e-07,1.80141e-09,0.0287788,0.000280404,7.98246e-07,-2.65629e-10,0.0290601,0.000281999,7.97449e-07,1.12374e-09,0.0293428,0.000283598,8.0082e-07,-5.04106e-10,0.0296272,0.000285198,7.99308e-07,8.92764e-10,0.0299132,0.000286799,8.01986e-07,6.58379e-10,0.0302008,0.000288405,8.03961e-07,1.98971e-10,0.0304901,0.000290014,8.04558e-07,4.08382e-10,0.0307809,0.000291624,8.05783e-07,3.01839e-11,0.0310733,0.000293236,8.05874e-07,1.33343e-09,0.0313673,0.000294851,8.09874e-07,2.2419e-10,0.031663,0.000296472,8.10547e-07,-3.67606e-10,0.0319603,0.000298092,8.09444e-07,1.24624e-09,0.0322592,0.000299714,8.13182e-07,-8.92025e-10,0.0325597,0.000301338,8.10506e-07,2.32183e-09,0.0328619,0.000302966,8.17472e-07,-9.44719e-10,0.0331657,0.000304598,8.14638e-07,1.45703e-09,0.0334711,0.000306232,8.19009e-07,-1.15805e-09,0.0337781,0.000307866,8.15535e-07,3.17507e-09,0.0340868,0.000309507,8.2506e-07,-4.09161e-09,0.0343971,0.000311145,8.12785e-07,5.74079e-09,0.0347091,0.000312788,8.30007e-07,-3.97034e-09,0.0350227,0.000314436,8.18096e-07,2.68985e-09,0.035338,0.00031608,8.26166e-07,6.61676e-10,0.0356549,0.000317734,8.28151e-07,-1.61123e-09,0.0359734,0.000319386,8.23317e-07,2.05786e-09,0.0362936,0.000321038,8.29491e-07,8.30388e-10,0.0366155,0.0003227,8.31982e-07,-1.65424e-09,0.036939,0.000324359,8.27019e-07,2.06129e-09,0.0372642,0.000326019,8.33203e-07,8.59719e-10,0.0375911,0.000327688,8.35782e-07,-1.77488e-09,0.0379196,0.000329354,8.30458e-07,2.51464e-09,0.0382498,0.000331023,8.38002e-07,-8.33135e-10,0.0385817,0.000332696,8.35502e-07,8.17825e-10,0.0389152,0.00033437,8.37956e-07,1.28718e-09,0.0392504,0.00033605,8.41817e-07,-2.2413e-09,0.0395873,0.000337727,8.35093e-07,3.95265e-09,0.0399258,0.000339409,8.46951e-07,-2.39332e-09,0.0402661,0.000341095,8.39771e-07,1.89533e-09,0.040608,0.000342781,8.45457e-07,-1.46271e-09,0.0409517,0.000344467,8.41069e-07,3.95554e-09,0.041297,0.000346161,8.52936e-07,-3.18369e-09,0.041644,0.000347857,8.43385e-07,1.32873e-09,0.0419927,0.000349548,8.47371e-07,1.59402e-09,0.0423431,0.000351248,8.52153e-07,-2.54336e-10,0.0426952,0.000352951,8.5139e-07,-5.76676e-10,0.043049,0.000354652,8.4966e-07,2.56114e-09,0.0434045,0.000356359,8.57343e-07,-2.21744e-09,0.0437617,0.000358067,8.50691e-07,2.58344e-09,0.0441206,0.000359776,8.58441e-07,-6.65826e-10,0.0444813,0.000361491,8.56444e-07,7.99218e-11,0.0448436,0.000363204,8.56684e-07,3.46063e-10,0.0452077,0.000364919,8.57722e-07,2.26116e-09,0.0455734,0.000366641,8.64505e-07,-1.94005e-09,0.045941,0.000368364,8.58685e-07,1.77384e-09,0.0463102,0.000370087,8.64007e-07,-1.43005e-09,0.0466811,0.000371811,8.59717e-07,3.94634e-09,0.0470538,0.000373542,8.71556e-07,-3.17946e-09,0.0474282,0.000375276,8.62017e-07,1.32104e-09,0.0478043,0.000377003,8.6598e-07,1.62045e-09,0.0481822,0.00037874,8.70842e-07,-3.52297e-10,0.0485618,0.000380481,8.69785e-07,-2.11211e-10,0.0489432,0.00038222,8.69151e-07,1.19716e-09,0.0493263,0.000383962,8.72743e-07,-8.52026e-10,0.0497111,0.000385705,8.70187e-07,2.21092e-09,0.0500977,0.000387452,8.76819e-07,-5.41339e-10,0.050486,0.000389204,8.75195e-07,-4.5361e-11,0.0508761,0.000390954,8.75059e-07,7.22669e-10,0.0512679,0.000392706,8.77227e-07,8.79936e-10,0.0516615,0.000394463,8.79867e-07,-5.17048e-10,0.0520568,0.000396222,8.78316e-07,1.18833e-09,0.0524539,0.000397982,8.81881e-07,-5.11022e-10,0.0528528,0.000399744,8.80348e-07,8.55683e-10,0.0532534,0.000401507,8.82915e-07,8.13562e-10,0.0536558,0.000403276,8.85356e-07,-3.84603e-10,0.05406,0.000405045,8.84202e-07,7.24962e-10,0.0544659,0.000406816,8.86377e-07,1.20986e-09,0.0548736,0.000408592,8.90006e-07,-1.83896e-09,0.0552831,0.000410367,8.84489e-07,2.42071e-09,0.0556944,0.000412143,8.91751e-07,-3.93413e-10,0.0561074,0.000413925,8.90571e-07,-8.46967e-10,0.0565222,0.000415704,8.8803e-07,3.78122e-09,0.0569388,0.000417491,8.99374e-07,-3.1021e-09,0.0573572,0.000419281,8.90068e-07,1.17658e-09,0.0577774,0.000421064,8.93597e-07,2.12117e-09,0.0581993,0.000422858,8.99961e-07,-2.21068e-09,0.0586231,0.000424651,8.93329e-07,2.9961e-09,0.0590486,0.000426447,9.02317e-07,-2.32311e-09,0.059476,0.000428244,8.95348e-07,2.57122e-09,0.0599051,0.000430043,9.03062e-07,-5.11098e-10,0.0603361,0.000431847,9.01528e-07,-5.27166e-10,0.0607688,0.000433649,8.99947e-07,2.61984e-09,0.0612034,0.000435457,9.07806e-07,-2.50141e-09,0.0616397,0.000437265,9.00302e-07,3.66045e-09,0.0620779,0.000439076,9.11283e-07,-4.68977e-09,0.0625179,0.000440885,8.97214e-07,7.64783e-09,0.0629597,0.000442702,9.20158e-07,-7.27499e-09,0.0634033,0.000444521,8.98333e-07,6.55113e-09,0.0638487,0.000446337,9.17986e-07,-4.02844e-09,0.0642959,0.000448161,9.05901e-07,2.11196e-09,0.064745,0.000449979,9.12236e-07,3.03125e-09,0.0651959,0.000451813,9.2133e-07,-6.78648e-09,0.0656486,0.000453635,9.00971e-07,9.21375e-09,0.0661032,0.000455464,9.28612e-07,-7.71684e-09,0.0665596,0.000457299,9.05462e-07,6.7522e-09,0.0670178,0.00045913,9.25718e-07,-4.3907e-09,0.0674778,0.000460968,9.12546e-07,3.36e-09,0.0679397,0.000462803,9.22626e-07,-1.59876e-09,0.0684034,0.000464644,9.1783e-07,3.0351e-09,0.068869,0.000466488,9.26935e-07,-3.09101e-09,0.0693364,0.000468333,9.17662e-07,1.8785e-09,0.0698057,0.000470174,9.23298e-07,3.02733e-09,0.0702768,0.00047203,9.3238e-07,-6.53722e-09,0.0707497,0.000473875,9.12768e-07,8.22054e-09,0.0712245,0.000475725,9.37429e-07,-3.99325e-09,0.0717012,0.000477588,9.2545e-07,3.01839e-10,0.0721797,0.00047944,9.26355e-07,2.78597e-09,0.0726601,0.000481301,9.34713e-07,-3.99507e-09,0.0731423,0.000483158,9.22728e-07,5.7435e-09,0.0736264,0.000485021,9.39958e-07,-4.07776e-09,0.0741123,0.000486888,9.27725e-07,3.11695e-09,0.0746002,0.000488753,9.37076e-07,-9.39394e-10,0.0750898,0.000490625,9.34258e-07,6.4055e-10,0.0755814,0.000492495,9.3618e-07,-1.62265e-09,0.0760748,0.000494363,9.31312e-07,5.84995e-09,0.0765701,0.000496243,9.48861e-07,-6.87601e-09,0.0770673,0.00049812,9.28233e-07,6.75296e-09,0.0775664,0.000499997,9.48492e-07,-5.23467e-09,0.0780673,0.000501878,9.32788e-07,6.73523e-09,0.0785701,0.000503764,9.52994e-07,-6.80514e-09,0.0790748,0.000505649,9.32578e-07,5.5842e-09,0.0795814,0.000507531,9.49331e-07,-6.30583e-10,0.0800899,0.000509428,9.47439e-07,-3.0618e-09,0.0806003,0.000511314,9.38254e-07,5.4273e-09,0.0811125,0.000513206,9.54536e-07,-3.74627e-09,0.0816267,0.000515104,9.43297e-07,2.10713e-09,0.0821427,0.000516997,9.49618e-07,2.76839e-09,0.0826607,0.000518905,9.57924e-07,-5.73006e-09,0.0831805,0.000520803,9.40733e-07,5.25072e-09,0.0837023,0.0005227,9.56486e-07,-3.71718e-10,0.084226,0.000524612,9.5537e-07,-3.76404e-09,0.0847515,0.000526512,9.44078e-07,7.97735e-09,0.085279,0.000528424,9.6801e-07,-5.79367e-09,0.0858084,0.000530343,9.50629e-07,2.96268e-10,0.0863397,0.000532245,9.51518e-07,4.6086e-09,0.0868729,0.000534162,9.65344e-07,-3.82947e-09,0.087408,0.000536081,9.53856e-07,3.25861e-09,0.087945,0.000537998,9.63631e-07,-1.7543e-09,0.088484,0.00053992,9.58368e-07,3.75849e-09,0.0890249,0.000541848,9.69644e-07,-5.82891e-09,0.0895677,0.00054377,9.52157e-07,4.65593e-09,0.0901124,0.000545688,9.66125e-07,2.10643e-09,0.0906591,0.000547627,9.72444e-07,-5.63099e-09,0.0912077,0.000549555,9.55551e-07,5.51627e-09,0.0917582,0.000551483,9.721e-07,-1.53292e-09,0.0923106,0.000553422,9.67501e-07,6.15311e-10,0.092865,0.000555359,9.69347e-07,-9.28291e-10,0.0934213,0.000557295,9.66562e-07,3.09774e-09,0.0939796,0.000559237,9.75856e-07,-4.01186e-09,0.0945398,0.000561177,9.6382e-07,5.49892e-09,0.095102,0.000563121,9.80317e-07,-3.08258e-09,0.0956661,0.000565073,9.71069e-07,-6.19176e-10,0.0962321,0.000567013,9.69212e-07,5.55932e-09,0.0968001,0.000568968,9.8589e-07,-6.71704e-09,0.09737,0.00057092,9.65738e-07,6.40762e-09,0.0979419,0.00057287,9.84961e-07,-4.0122e-09,0.0985158,0.000574828,9.72925e-07,2.19059e-09,0.0990916,0.000576781,9.79496e-07,2.70048e-09,0.0996693,0.000578748,9.87598e-07,-5.54193e-09,0.100249,0.000580706,9.70972e-07,4.56597e-09,0.100831,0.000582662,9.8467e-07,2.17923e-09,0.101414,0.000584638,9.91208e-07,-5.83232e-09,0.102,0.000586603,9.73711e-07,6.24884e-09,0.102588,0.000588569,9.92457e-07,-4.26178e-09,0.103177,0.000590541,9.79672e-07,3.34781e-09,0.103769,0.00059251,9.89715e-07,-1.67904e-09,0.104362,0.000594485,9.84678e-07,3.36839e-09,0.104958,0.000596464,9.94783e-07,-4.34397e-09,0.105555,0.000598441,9.81751e-07,6.55696e-09,0.106155,0.000600424,1.00142e-06,-6.98272e-09,0.106756,0.000602406,9.80474e-07,6.4728e-09,0.107359,0.000604386,9.99893e-07,-4.00742e-09,0.107965,0.000606374,9.8787e-07,2.10654e-09,0.108572,0.000608356,9.9419e-07,3.0318e-09,0.109181,0.000610353,1.00329e-06,-6.7832e-09,0.109793,0.00061234,9.82936e-07,9.1998e-09,0.110406,0.000614333,1.01054e-06,-7.6642e-09,0.111021,0.000616331,9.87543e-07,6.55579e-09,0.111639,0.000618326,1.00721e-06,-3.65791e-09,0.112258,0.000620329,9.96236e-07,6.25467e-10,0.112879,0.000622324,9.98113e-07,1.15593e-09,0.113503,0.000624323,1.00158e-06,2.20158e-09,0.114128,0.000626333,1.00819e-06,-2.51191e-09,0.114755,0.000628342,1.00065e-06,3.95517e-10,0.115385,0.000630345,1.00184e-06,9.29807e-10,0.116016,0.000632351,1.00463e-06,3.33599e-09,0.116649,0.00063437,1.01463e-06,-6.82329e-09,0.117285,0.000636379,9.94163e-07,9.05595e-09,0.117922,0.000638395,1.02133e-06,-7.04862e-09,0.118562,0.000640416,1.00019e-06,4.23737e-09,0.119203,0.000642429,1.0129e-06,-2.45033e-09,0.119847,0.000644448,1.00555e-06,5.56395e-09,0.120492,0.000646475,1.02224e-06,-4.9043e-09,0.121139,0.000648505,1.00753e-06,-8.47952e-10,0.121789,0.000650518,1.00498e-06,8.29622e-09,0.122441,0.000652553,1.02987e-06,-9.98538e-09,0.123094,0.000654582,9.99914e-07,9.2936e-09,0.12375,0.00065661,1.02779e-06,-4.83707e-09,0.124407,0.000658651,1.01328e-06,2.60411e-09,0.125067,0.000660685,1.0211e-06,-5.57945e-09,0.125729,0.000662711,1.00436e-06,1.22631e-08,0.126392,0.000664756,1.04115e-06,-1.36704e-08,0.127058,0.000666798,1.00014e-06,1.26161e-08,0.127726,0.000668836,1.03798e-06,-6.99155e-09,0.128396,0.000670891,1.01701e-06,4.48836e-10,0.129068,0.000672926,1.01836e-06,5.19606e-09,0.129742,0.000674978,1.03394e-06,-6.3319e-09,0.130418,0.000677027,1.01495e-06,5.2305e-09,0.131096,0.000679073,1.03064e-06,3.11123e-10,0.131776,0.000681135,1.03157e-06,-6.47511e-09,0.132458,0.000683179,1.01215e-06,1.06882e-08,0.133142,0.000685235,1.04421e-06,-6.47519e-09,0.133829,0.000687304,1.02479e-06,3.11237e-10,0.134517,0.000689355,1.02572e-06,5.23035e-09,0.135207,0.000691422,1.04141e-06,-6.3316e-09,0.1359,0.000693486,1.02242e-06,5.19484e-09,0.136594,0.000695546,1.038e-06,4.53497e-10,0.137291,0.000697623,1.03936e-06,-7.00891e-09,0.137989,0.000699681,1.01834e-06,1.2681e-08,0.13869,0.000701756,1.05638e-06,-1.39128e-08,0.139393,0.000703827,1.01464e-06,1.31679e-08,0.140098,0.000705896,1.05414e-06,-8.95659e-09,0.140805,0.000707977,1.02727e-06,7.75742e-09,0.141514,0.000710055,1.05055e-06,-7.17182e-09,0.142225,0.000712135,1.02903e-06,6.02862e-09,0.142938,0.000714211,1.04712e-06,-2.04163e-09,0.143653,0.000716299,1.04099e-06,2.13792e-09,0.144371,0.000718387,1.04741e-06,-6.51009e-09,0.14509,0.000720462,1.02787e-06,9.00123e-09,0.145812,0.000722545,1.05488e-06,3.07523e-10,0.146535,0.000724656,1.0558e-06,-1.02312e-08,0.147261,0.000726737,1.02511e-06,1.0815e-08,0.147989,0.000728819,1.05755e-06,-3.22681e-09,0.148719,0.000730925,1.04787e-06,2.09244e-09,0.14945,0.000733027,1.05415e-06,-5.143e-09,0.150185,0.00073512,1.03872e-06,3.57844e-09,0.150921,0.000737208,1.04946e-06,5.73027e-09,0.151659,0.000739324,1.06665e-06,-1.15983e-08,0.152399,0.000741423,1.03185e-06,1.08605e-08,0.153142,0.000743519,1.06443e-06,-2.04106e-09,0.153886,0.000745642,1.05831e-06,-2.69642e-09,0.154633,0.00074775,1.05022e-06,-2.07425e-09,0.155382,0.000749844,1.044e-06,1.09934e-08,0.156133,0.000751965,1.07698e-06,-1.20972e-08,0.156886,0.000754083,1.04069e-06,7.59288e-09,0.157641,0.000756187,1.06347e-06,-3.37305e-09,0.158398,0.000758304,1.05335e-06,5.89921e-09,0.159158,0.000760428,1.07104e-06,-5.32248e-09,0.159919,0.000762554,1.05508e-06,4.8927e-10,0.160683,0.000764666,1.05654e-06,3.36547e-09,0.161448,0.000766789,1.06664e-06,9.50081e-10,0.162216,0.000768925,1.06949e-06,-7.16568e-09,0.162986,0.000771043,1.04799e-06,1.28114e-08,0.163758,0.000773177,1.08643e-06,-1.42774e-08,0.164533,0.000775307,1.0436e-06,1.44956e-08,0.165309,0.000777438,1.08708e-06,-1.39025e-08,0.166087,0.00077957,1.04538e-06,1.13118e-08,0.166868,0.000781695,1.07931e-06,-1.54224e-09,0.167651,0.000783849,1.07468e-06,-5.14312e-09,0.168436,0.000785983,1.05925e-06,7.21381e-09,0.169223,0.000788123,1.0809e-06,-8.81096e-09,0.170012,0.000790259,1.05446e-06,1.31289e-08,0.170803,0.000792407,1.09385e-06,-1.39022e-08,0.171597,0.000794553,1.05214e-06,1.26775e-08,0.172392,0.000796695,1.09018e-06,-7.00557e-09,0.17319,0.000798855,1.06916e-06,4.43796e-10,0.17399,0.000800994,1.07049e-06,5.23031e-09,0.174792,0.000803151,1.08618e-06,-6.46397e-09,0.175596,0.000805304,1.06679e-06,5.72444e-09,0.176403,0.000807455,1.08396e-06,-1.53254e-09,0.177211,0.000809618,1.07937e-06,4.05673e-10,0.178022,0.000811778,1.08058e-06,-9.01916e-11,0.178835,0.000813939,1.08031e-06,-4.49821e-11,0.17965,0.000816099,1.08018e-06,2.70234e-10,0.180467,0.00081826,1.08099e-06,-1.03603e-09,0.181286,0.000820419,1.07788e-06,3.87392e-09,0.182108,0.000822587,1.0895e-06,4.41522e-10,0.182932,0.000824767,1.09083e-06,-5.63997e-09,0.183758,0.000826932,1.07391e-06,7.21707e-09,0.184586,0.000829101,1.09556e-06,-8.32718e-09,0.185416,0.000831267,1.07058e-06,1.11907e-08,0.186248,0.000833442,1.10415e-06,-6.63336e-09,0.187083,0.00083563,1.08425e-06,4.41484e-10,0.187919,0.0008378,1.08557e-06,4.86754e-09,0.188758,0.000839986,1.10017e-06,-5.01041e-09,0.189599,0.000842171,1.08514e-06,2.72811e-10,0.190443,0.000844342,1.08596e-06,3.91916e-09,0.191288,0.000846526,1.09772e-06,-1.04819e-09,0.192136,0.000848718,1.09457e-06,2.73531e-10,0.192985,0.000850908,1.0954e-06,-4.58916e-11,0.193837,0.000853099,1.09526e-06,-9.01158e-11,0.194692,0.000855289,1.09499e-06,4.06506e-10,0.195548,0.00085748,1.09621e-06,-1.53595e-09,0.196407,0.000859668,1.0916e-06,5.73717e-09,0.197267,0.000861869,1.10881e-06,-6.51164e-09,0.19813,0.000864067,1.08928e-06,5.40831e-09,0.198995,0.000866261,1.1055e-06,-2.20401e-10,0.199863,0.000868472,1.10484e-06,-4.52652e-09,0.200732,0.000870668,1.09126e-06,3.42508e-09,0.201604,0.000872861,1.10153e-06,5.72762e-09,0.202478,0.000875081,1.11872e-06,-1.14344e-08,0.203354,0.000877284,1.08441e-06,1.02076e-08,0.204233,0.000879484,1.11504e-06,4.06355e-10,0.205113,0.000881715,1.11626e-06,-1.18329e-08,0.205996,0.000883912,1.08076e-06,1.71227e-08,0.206881,0.000886125,1.13213e-06,-1.19546e-08,0.207768,0.000888353,1.09626e-06,8.93465e-10,0.208658,0.000890548,1.09894e-06,8.38062e-09,0.209549,0.000892771,1.12408e-06,-4.61353e-09,0.210443,0.000895006,1.11024e-06,-4.82756e-09,0.211339,0.000897212,1.09576e-06,9.02245e-09,0.212238,0.00089943,1.12283e-06,-1.45997e-09,0.213138,0.000901672,1.11845e-06,-3.18255e-09,0.214041,0.000903899,1.1089e-06,-7.11073e-10,0.214946,0.000906115,1.10677e-06,6.02692e-09,0.215853,0.000908346,1.12485e-06,-8.49548e-09,0.216763,0.00091057,1.09936e-06,1.30537e-08,0.217675,0.000912808,1.13852e-06,-1.3917e-08,0.218588,0.000915044,1.09677e-06,1.28121e-08,0.219505,0.000917276,1.13521e-06,-7.5288e-09,0.220423,0.000919523,1.11262e-06,2.40205e-09,0.221344,0.000921756,1.11983e-06,-2.07941e-09,0.222267,0.000923989,1.11359e-06,5.91551e-09,0.223192,0.000926234,1.13134e-06,-6.68149e-09,0.224119,0.000928477,1.11129e-06,5.90929e-09,0.225049,0.000930717,1.12902e-06,-2.05436e-09,0.22598,0.000932969,1.12286e-06,2.30807e-09,0.226915,0.000935222,1.12978e-06,-7.17796e-09,0.227851,0.00093746,1.10825e-06,1.15028e-08,0.228789,0.000939711,1.14276e-06,-9.03083e-09,0.22973,0.000941969,1.11566e-06,9.71932e-09,0.230673,0.00094423,1.14482e-06,-1.49452e-08,0.231619,0.000946474,1.09998e-06,2.02591e-08,0.232566,0.000948735,1.16076e-06,-2.13879e-08,0.233516,0.000950993,1.0966e-06,2.05888e-08,0.234468,0.000953247,1.15837e-06,-1.62642e-08,0.235423,0.000955515,1.10957e-06,1.46658e-08,0.236379,0.000957779,1.15357e-06,-1.25966e-08,0.237338,0.000960048,1.11578e-06,5.91793e-09,0.238299,0.000962297,1.13353e-06,3.82602e-09,0.239263,0.000964576,1.14501e-06,-6.3208e-09,0.240229,0.000966847,1.12605e-06,6.55613e-09,0.241197,0.000969119,1.14572e-06,-5.00268e-09,0.242167,0.000971395,1.13071e-06,-1.44659e-09,0.243139,0.000973652,1.12637e-06,1.07891e-08,0.244114,0.000975937,1.15874e-06,-1.19073e-08,0.245091,0.000978219,1.12302e-06,7.03782e-09,0.246071,0.000980486,1.14413e-06,-1.34276e-09,0.247052,0.00098277,1.1401e-06,-1.66669e-09,0.248036,0.000985046,1.1351e-06,8.00935e-09,0.249022,0.00098734,1.15913e-06,-1.54694e-08,0.250011,0.000989612,1.11272e-06,2.4066e-08,0.251002,0.000991909,1.18492e-06,-2.11901e-08,0.251995,0.000994215,1.12135e-06,1.08973e-09,0.25299,0.000996461,1.12462e-06,1.68311e-08,0.253988,0.000998761,1.17511e-06,-8.8094e-09,0.254987,0.00100109,1.14868e-06,-1.13958e-08,0.25599,0.00100335,1.1145e-06,2.45902e-08,0.256994,0.00100565,1.18827e-06,-2.73603e-08,0.258001,0.00100795,1.10618e-06,2.52464e-08,0.25901,0.00101023,1.18192e-06,-1.40207e-08,0.260021,0.00101256,1.13986e-06,1.03387e-09,0.261035,0.00101484,1.14296e-06,9.8853e-09,0.262051,0.00101715,1.17262e-06,-1.07726e-08,0.263069,0.00101947,1.1403e-06,3.40272e-09,0.26409,0.00102176,1.15051e-06,-2.83827e-09,0.265113,0.00102405,1.142e-06,7.95039e-09,0.266138,0.00102636,1.16585e-06,8.39047e-10,0.267166,0.00102869,1.16836e-06,-1.13066e-08,0.268196,0.00103099,1.13444e-06,1.4585e-08,0.269228,0.00103331,1.1782e-06,-1.72314e-08,0.270262,0.00103561,1.1265e-06,2.45382e-08,0.271299,0.00103794,1.20012e-06,-2.13166e-08,0.272338,0.00104028,1.13617e-06,1.12364e-09,0.273379,0.00104255,1.13954e-06,1.68221e-08,0.274423,0.00104488,1.19001e-06,-8.80736e-09,0.275469,0.00104723,1.16358e-06,-1.13948e-08,0.276518,0.00104953,1.1294e-06,2.45839e-08,0.277568,0.00105186,1.20315e-06,-2.73361e-08,0.278621,0.00105418,1.12114e-06,2.51559e-08,0.279677,0.0010565,1.19661e-06,-1.36832e-08,0.280734,0.00105885,1.15556e-06,-2.25706e-10,0.281794,0.00106116,1.15488e-06,1.45862e-08,0.282857,0.00106352,1.19864e-06,-2.83167e-08,0.283921,0.00106583,1.11369e-06,3.90759e-08,0.284988,0.00106817,1.23092e-06,-3.85801e-08,0.286058,0.00107052,1.11518e-06,2.58375e-08,0.287129,0.00107283,1.19269e-06,-5.16498e-09,0.288203,0.0010752,1.1772e-06,-5.17768e-09,0.28928,0.00107754,1.16167e-06,-3.92671e-09,0.290358,0.00107985,1.14988e-06,2.08846e-08,0.29144,0.00108221,1.21254e-06,-2.00072e-08,0.292523,0.00108458,1.15252e-06,-4.60659e-10,0.293609,0.00108688,1.15114e-06,2.18499e-08,0.294697,0.00108925,1.21669e-06,-2.73343e-08,0.295787,0.0010916,1.13468e-06,2.78826e-08,0.29688,0.00109395,1.21833e-06,-2.45915e-08,0.297975,0.00109632,1.14456e-06,1.08787e-08,0.299073,0.00109864,1.17719e-06,1.08788e-08,0.300172,0.00110102,1.20983e-06,-2.45915e-08,0.301275,0.00110337,1.13605e-06,2.78828e-08,0.302379,0.00110573,1.2197e-06,-2.73348e-08,0.303486,0.00110808,1.1377e-06,2.18518e-08,0.304595,0.00111042,1.20325e-06,-4.67556e-10,0.305707,0.00111283,1.20185e-06,-1.99816e-08,0.306821,0.00111517,1.14191e-06,2.07891e-08,0.307937,0.00111752,1.20427e-06,-3.57026e-09,0.309056,0.00111992,1.19356e-06,-6.50797e-09,0.310177,0.00112228,1.17404e-06,-2.00165e-10,0.3113,0.00112463,1.17344e-06,7.30874e-09,0.312426,0.001127,1.19536e-06,7.67424e-10,0.313554,0.00112939,1.19767e-06,-1.03784e-08,0.314685,0.00113176,1.16653e-06,1.09437e-08,0.315818,0.00113412,1.19936e-06,-3.59406e-09,0.316953,0.00113651,1.18858e-06,3.43251e-09,0.318091,0.0011389,1.19888e-06,-1.0136e-08,0.319231,0.00114127,1.16847e-06,7.30915e-09,0.320374,0.00114363,1.1904e-06,1.07018e-08,0.321518,0.00114604,1.2225e-06,-2.03137e-08,0.322666,0.00114842,1.16156e-06,1.09484e-08,0.323815,0.00115078,1.19441e-06,6.32224e-09,0.324967,0.00115319,1.21337e-06,-6.43509e-09,0.326122,0.00115559,1.19407e-06,-1.03842e-08,0.327278,0.00115795,1.16291e-06,1.81697e-08,0.328438,0.00116033,1.21742e-06,-2.6901e-09,0.329599,0.00116276,1.20935e-06,-7.40939e-09,0.330763,0.00116515,1.18713e-06,2.52533e-09,0.331929,0.00116754,1.1947e-06,-2.69191e-09,0.333098,0.00116992,1.18663e-06,8.24218e-09,0.334269,0.00117232,1.21135e-06,-4.74377e-10,0.335443,0.00117474,1.20993e-06,-6.34471e-09,0.336619,0.00117714,1.1909e-06,-3.94922e-09,0.337797,0.00117951,1.17905e-06,2.21417e-08,0.338978,0.00118193,1.24547e-06,-2.50128e-08,0.340161,0.00118435,1.17043e-06,1.8305e-08,0.341346,0.00118674,1.22535e-06,-1.84048e-08,0.342534,0.00118914,1.17013e-06,2.55121e-08,0.343725,0.00119156,1.24667e-06,-2.40389e-08,0.344917,0.00119398,1.17455e-06,1.10389e-08,0.346113,0.00119636,1.20767e-06,9.68574e-09,0.34731,0.0011988,1.23673e-06,-1.99797e-08,0.34851,0.00120122,1.17679e-06,1.06284e-08,0.349713,0.0012036,1.20867e-06,7.26868e-09,0.350917,0.00120604,1.23048e-06,-9.90072e-09,0.352125,0.00120847,1.20078e-06,2.53177e-09,0.353334,0.00121088,1.20837e-06,-2.26199e-10,0.354546,0.0012133,1.20769e-06,-1.62705e-09,0.355761,0.00121571,1.20281e-06,6.73435e-09,0.356978,0.00121813,1.22302e-06,4.49207e-09,0.358197,0.00122059,1.23649e-06,-2.47027e-08,0.359419,0.00122299,1.16238e-06,3.47142e-08,0.360643,0.00122542,1.26653e-06,-2.47472e-08,0.36187,0.00122788,1.19229e-06,4.66965e-09,0.363099,0.00123028,1.20629e-06,6.06872e-09,0.36433,0.00123271,1.2245e-06,8.57729e-10,0.365564,0.00123516,1.22707e-06,-9.49952e-09,0.366801,0.00123759,1.19858e-06,7.33792e-09,0.36804,0.00124001,1.22059e-06,9.95025e-09,0.369281,0.00124248,1.25044e-06,-1.73366e-08,0.370525,0.00124493,1.19843e-06,-2.08464e-10,0.371771,0.00124732,1.1978e-06,1.81704e-08,0.373019,0.00124977,1.25232e-06,-1.28683e-08,0.37427,0.00125224,1.21371e-06,3.50042e-09,0.375524,0.00125468,1.22421e-06,-1.1335e-09,0.37678,0.00125712,1.22081e-06,1.03345e-09,0.378038,0.00125957,1.22391e-06,-3.00023e-09,0.379299,0.00126201,1.21491e-06,1.09676e-08,0.380562,0.00126447,1.24781e-06,-1.10676e-08,0.381828,0.00126693,1.21461e-06,3.50042e-09,0.383096,0.00126937,1.22511e-06,-2.93403e-09,0.384366,0.00127181,1.21631e-06,8.23574e-09,0.385639,0.00127427,1.24102e-06,-2.06607e-10,0.386915,0.00127675,1.2404e-06,-7.40935e-09,0.388193,0.00127921,1.21817e-06,4.1761e-11,0.389473,0.00128165,1.21829e-06,7.24223e-09,0.390756,0.0012841,1.24002e-06,7.91564e-10,0.392042,0.00128659,1.2424e-06,-1.04086e-08,0.393329,0.00128904,1.21117e-06,1.10405e-08,0.39462,0.0012915,1.24429e-06,-3.951e-09,0.395912,0.00129397,1.23244e-06,4.7634e-09,0.397208,0.00129645,1.24673e-06,-1.51025e-08,0.398505,0.0012989,1.20142e-06,2.58443e-08,0.399805,0.00130138,1.27895e-06,-2.86702e-08,0.401108,0.00130385,1.19294e-06,2.92318e-08,0.402413,0.00130632,1.28064e-06,-2.86524e-08,0.403721,0.0013088,1.19468e-06,2.57731e-08,0.405031,0.00131127,1.272e-06,-1.48355e-08,0.406343,0.00131377,1.2275e-06,3.76652e-09,0.407658,0.00131623,1.23879e-06,-2.30784e-10,0.408976,0.00131871,1.2381e-06,-2.84331e-09,0.410296,0.00132118,1.22957e-06,1.16041e-08,0.411618,0.00132367,1.26438e-06,-1.37708e-08,0.412943,0.00132616,1.22307e-06,1.36768e-08,0.41427,0.00132865,1.2641e-06,-1.1134e-08,0.4156,0.00133114,1.2307e-06,1.05714e-09,0.416933,0.00133361,1.23387e-06,6.90538e-09,0.418267,0.00133609,1.25459e-06,1.12372e-09,0.419605,0.00133861,1.25796e-06,-1.14002e-08,0.420945,0.00134109,1.22376e-06,1.46747e-08,0.422287,0.00134358,1.26778e-06,-1.7496e-08,0.423632,0.00134606,1.21529e-06,2.5507e-08,0.424979,0.00134857,1.29182e-06,-2.49272e-08,0.426329,0.00135108,1.21703e-06,1.45972e-08,0.427681,0.00135356,1.26083e-06,-3.65935e-09,0.429036,0.00135607,1.24985e-06,4.00178e-11,0.430393,0.00135857,1.24997e-06,3.49917e-09,0.431753,0.00136108,1.26047e-06,-1.40366e-08,0.433116,0.00136356,1.21836e-06,2.28448e-08,0.43448,0.00136606,1.28689e-06,-1.77378e-08,0.435848,0.00136858,1.23368e-06,1.83043e-08,0.437218,0.0013711,1.28859e-06,-2.56769e-08,0.43859,0.0013736,1.21156e-06,2.47987e-08,0.439965,0.0013761,1.28595e-06,-1.39133e-08,0.441342,0.00137863,1.24421e-06,1.05202e-09,0.442722,0.00138112,1.24737e-06,9.70507e-09,0.444104,0.00138365,1.27649e-06,-1.00698e-08,0.445489,0.00138617,1.24628e-06,7.72123e-10,0.446877,0.00138867,1.24859e-06,6.98132e-09,0.448267,0.00139118,1.26954e-06,1.10477e-09,0.449659,0.00139373,1.27285e-06,-1.14003e-08,0.451054,0.00139624,1.23865e-06,1.4694e-08,0.452452,0.00139876,1.28273e-06,-1.75734e-08,0.453852,0.00140127,1.23001e-06,2.5797e-08,0.455254,0.00140381,1.3074e-06,-2.60097e-08,0.456659,0.00140635,1.22937e-06,1.86371e-08,0.458067,0.00140886,1.28529e-06,-1.8736e-08,0.459477,0.00141137,1.22908e-06,2.65048e-08,0.46089,0.00141391,1.30859e-06,-2.76784e-08,0.462305,0.00141645,1.22556e-06,2.46043e-08,0.463722,0.00141897,1.29937e-06,-1.11341e-08,0.465143,0.00142154,1.26597e-06,-9.87033e-09,0.466565,0.00142404,1.23636e-06,2.08131e-08,0.467991,0.00142657,1.2988e-06,-1.37773e-08,0.469419,0.00142913,1.25746e-06,4.49378e-09,0.470849,0.00143166,1.27094e-06,-4.19781e-09,0.472282,0.00143419,1.25835e-06,1.22975e-08,0.473717,0.00143674,1.29524e-06,-1.51902e-08,0.475155,0.00143929,1.24967e-06,1.86608e-08,0.476596,0.00144184,1.30566e-06,-2.96506e-08,0.478039,0.00144436,1.2167e-06,4.03368e-08,0.479485,0.00144692,1.33771e-06,-4.22896e-08,0.480933,0.00144947,1.21085e-06,3.94148e-08,0.482384,0.00145201,1.32909e-06,-2.59626e-08,0.483837,0.00145459,1.2512e-06,4.83124e-09,0.485293,0.0014571,1.2657e-06,6.63757e-09,0.486751,0.00145966,1.28561e-06,-1.57911e-09,0.488212,0.00146222,1.28087e-06,-3.21468e-10,0.489676,0.00146478,1.27991e-06,2.86517e-09,0.491142,0.00146735,1.2885e-06,-1.11392e-08,0.49261,0.00146989,1.25508e-06,1.18893e-08,0.494081,0.00147244,1.29075e-06,-6.61574e-09,0.495555,0.001475,1.27091e-06,1.45736e-08,0.497031,0.00147759,1.31463e-06,-2.18759e-08,0.49851,0.00148015,1.249e-06,1.33252e-08,0.499992,0.00148269,1.28897e-06,-1.62277e-09,0.501476,0.00148526,1.28411e-06,-6.83421e-09,0.502962,0.00148781,1.2636e-06,2.89596e-08,0.504451,0.00149042,1.35048e-06,-4.93997e-08,0.505943,0.00149298,1.20228e-06,4.94299e-08,0.507437,0.00149553,1.35057e-06,-2.91107e-08,0.508934,0.00149814,1.26324e-06,7.40848e-09,0.510434,0.00150069,1.28547e-06,-5.23187e-10,0.511936,0.00150326,1.2839e-06,-5.31585e-09,0.51344,0.00150581,1.26795e-06,2.17866e-08,0.514947,0.00150841,1.33331e-06,-2.22257e-08,0.516457,0.00151101,1.26663e-06,7.51178e-09,0.517969,0.00151357,1.28917e-06,-7.82128e-09,0.519484,0.00151613,1.2657e-06,2.37733e-08,0.521002,0.00151873,1.33702e-06,-2.76674e-08,0.522522,0.00152132,1.25402e-06,2.72917e-08,0.524044,0.00152391,1.3359e-06,-2.18949e-08,0.525569,0.00152652,1.27021e-06,6.83372e-10,0.527097,0.00152906,1.27226e-06,1.91613e-08,0.528628,0.00153166,1.32974e-06,-1.77241e-08,0.53016,0.00153427,1.27657e-06,-7.86963e-09,0.531696,0.0015368,1.25296e-06,4.92027e-08,0.533234,0.00153945,1.40057e-06,-6.9732e-08,0.534775,0.00154204,1.19138e-06,5.09114e-08,0.536318,0.00154458,1.34411e-06,-1.4704e-08,0.537864,0.00154722,1.3e-06,7.9048e-09,0.539413,0.00154984,1.32371e-06,-1.69152e-08,0.540964,0.00155244,1.27297e-06,1.51355e-10,0.542517,0.00155499,1.27342e-06,1.63099e-08,0.544074,0.00155758,1.32235e-06,-5.78647e-09,0.545633,0.00156021,1.30499e-06,6.83599e-09,0.547194,0.00156284,1.3255e-06,-2.15575e-08,0.548758,0.00156543,1.26083e-06,1.97892e-08,0.550325,0.00156801,1.32019e-06,2.00525e-09,0.551894,0.00157065,1.32621e-06,-2.78103e-08,0.553466,0.00157322,1.24278e-06,4.96314e-08,0.555041,0.00157586,1.39167e-06,-5.1506e-08,0.556618,0.00157849,1.23716e-06,3.71835e-08,0.558198,0.00158107,1.34871e-06,-3.76233e-08,0.55978,0.00158366,1.23584e-06,5.37052e-08,0.561365,0.00158629,1.39695e-06,-5.79884e-08,0.562953,0.00158891,1.22299e-06,5.90392e-08,0.564543,0.00159153,1.4001e-06,-5.89592e-08,0.566136,0.00159416,1.22323e-06,5.7588e-08,0.567731,0.00159678,1.39599e-06,-5.21835e-08,0.569329,0.00159941,1.23944e-06,3.19369e-08,0.57093,0.00160199,1.33525e-06,-1.59594e-08,0.572533,0.00160461,1.28737e-06,3.19006e-08,0.574139,0.00160728,1.38307e-06,-5.20383e-08,0.575748,0.00160989,1.22696e-06,5.70431e-08,0.577359,0.00161251,1.39809e-06,-5.69247e-08,0.578973,0.00161514,1.22731e-06,5.14463e-08,0.580589,0.00161775,1.38165e-06,-2.9651e-08,0.582208,0.00162042,1.2927e-06,7.55339e-09,0.58383,0.00162303,1.31536e-06,-5.62636e-10,0.585455,0.00162566,1.31367e-06,-5.30281e-09,0.587081,0.00162827,1.29776e-06,2.17738e-08,0.588711,0.00163093,1.36309e-06,-2.21875e-08,0.590343,0.00163359,1.29652e-06,7.37164e-09,0.591978,0.00163621,1.31864e-06,-7.29907e-09,0.593616,0.00163882,1.29674e-06,2.18247e-08,0.595256,0.00164148,1.36221e-06,-2.03952e-08,0.596899,0.00164414,1.30103e-06,1.51241e-10,0.598544,0.00164675,1.30148e-06,1.97902e-08,0.600192,0.00164941,1.36085e-06,-1.97074e-08,0.601843,0.00165207,1.30173e-06,-5.65175e-10,0.603496,0.00165467,1.30004e-06,2.1968e-08,0.605152,0.00165734,1.36594e-06,-2.77024e-08,0.606811,0.00165999,1.28283e-06,2.92369e-08,0.608472,0.00166264,1.37054e-06,-2.96407e-08,0.610136,0.00166529,1.28162e-06,2.97215e-08,0.611803,0.00166795,1.37079e-06,-2.96408e-08,0.613472,0.0016706,1.28186e-06,2.92371e-08,0.615144,0.00167325,1.36957e-06,-2.77031e-08,0.616819,0.00167591,1.28647e-06,2.19708e-08,0.618496,0.00167855,1.35238e-06,-5.75407e-10,0.620176,0.00168125,1.35065e-06,-1.9669e-08,0.621858,0.00168389,1.29164e-06,1.96468e-08,0.623544,0.00168653,1.35058e-06,6.86403e-10,0.625232,0.00168924,1.35264e-06,-2.23924e-08,0.626922,0.00169187,1.28547e-06,2.92788e-08,0.628615,0.00169453,1.3733e-06,-3.51181e-08,0.630311,0.00169717,1.26795e-06,5.15889e-08,0.63201,0.00169987,1.42272e-06,-5.2028e-08,0.633711,0.00170255,1.26663e-06,3.73139e-08,0.635415,0.0017052,1.37857e-06,-3.76227e-08,0.637121,0.00170784,1.2657e-06,5.35722e-08,0.63883,0.00171054,1.42642e-06,-5.74567e-08,0.640542,0.00171322,1.25405e-06,5.70456e-08,0.642257,0.0017159,1.42519e-06,-5.15163e-08,0.643974,0.00171859,1.27064e-06,2.98103e-08,0.645694,0.00172122,1.36007e-06,-8.12016e-09,0.647417,0.00172392,1.33571e-06,2.67039e-09,0.649142,0.0017266,1.34372e-06,-2.56152e-09,0.65087,0.00172928,1.33604e-06,7.57571e-09,0.6526,0.00173197,1.35876e-06,-2.77413e-08,0.654334,0.00173461,1.27554e-06,4.3785e-08,0.65607,0.00173729,1.40689e-06,-2.81896e-08,0.657808,0.00174002,1.32233e-06,9.36893e-09,0.65955,0.00174269,1.35043e-06,-9.28617e-09,0.661294,0.00174536,1.32257e-06,2.77757e-08,0.66304,0.00174809,1.4059e-06,-4.2212e-08,0.66479,0.00175078,1.27926e-06,2.1863e-08,0.666542,0.0017534,1.34485e-06,1.43648e-08,0.668297,0.00175613,1.38795e-06,-1.97177e-08,0.670054,0.00175885,1.3288e-06,4.90115e-09,0.671814,0.00176152,1.3435e-06,1.13232e-10,0.673577,0.00176421,1.34384e-06,-5.3542e-09,0.675343,0.00176688,1.32778e-06,2.13035e-08,0.677111,0.0017696,1.39169e-06,-2.02553e-08,0.678882,0.00177232,1.33092e-06,1.13005e-10,0.680656,0.00177499,1.33126e-06,1.98031e-08,0.682432,0.00177771,1.39067e-06,-1.97211e-08,0.684211,0.00178043,1.33151e-06,-5.2349e-10,0.685993,0.00178309,1.32994e-06,2.18151e-08,0.687777,0.00178582,1.39538e-06,-2.71325e-08,0.689564,0.00178853,1.31398e-06,2.71101e-08,0.691354,0.00179124,1.39531e-06,-2.17035e-08,0.693147,0.00179396,1.3302e-06,9.92865e-11,0.694942,0.00179662,1.3305e-06,2.13063e-08,0.69674,0.00179935,1.39442e-06,-2.57198e-08,0.698541,0.00180206,1.31726e-06,2.19682e-08,0.700344,0.00180476,1.38317e-06,-2.54852e-09,0.70215,0.00180752,1.37552e-06,-1.17741e-08,0.703959,0.00181023,1.3402e-06,-9.95999e-09,0.705771,0.00181288,1.31032e-06,5.16141e-08,0.707585,0.00181566,1.46516e-06,-7.72869e-08,0.709402,0.00181836,1.2333e-06,7.87197e-08,0.711222,0.00182106,1.46946e-06,-5.87781e-08,0.713044,0.00182382,1.29312e-06,3.71834e-08,0.714869,0.00182652,1.40467e-06,-3.03511e-08,0.716697,0.00182924,1.31362e-06,2.46161e-08,0.718528,0.00183194,1.38747e-06,-8.5087e-09,0.720361,0.00183469,1.36194e-06,9.41892e-09,0.722197,0.00183744,1.3902e-06,-2.91671e-08,0.724036,0.00184014,1.3027e-06,4.76448e-08,0.725878,0.00184288,1.44563e-06,-4.22028e-08,0.727722,0.00184565,1.31902e-06,1.95682e-09,0.729569,0.00184829,1.3249e-06,3.43754e-08,0.731419,0.00185104,1.42802e-06,-2.0249e-08,0.733271,0.00185384,1.36727e-06,-1.29838e-08,0.735126,0.00185654,1.32832e-06,1.25794e-08,0.736984,0.00185923,1.36606e-06,2.22711e-08,0.738845,0.00186203,1.43287e-06,-4.20594e-08,0.740708,0.00186477,1.3067e-06,2.67571e-08,0.742574,0.00186746,1.38697e-06,-5.36424e-09,0.744443,0.00187022,1.37087e-06,-5.30023e-09,0.746315,0.00187295,1.35497e-06,2.65653e-08,0.748189,0.00187574,1.43467e-06,-4.13564e-08,0.750066,0.00187848,1.3106e-06,1.9651e-08,0.751946,0.00188116,1.36955e-06,2.23572e-08,0.753828,0.00188397,1.43663e-06,-4.9475e-08,0.755714,0.00188669,1.2882e-06,5.63335e-08,0.757602,0.00188944,1.4572e-06,-5.66499e-08,0.759493,0.00189218,1.28725e-06,5.10567e-08,0.761386,0.00189491,1.44042e-06,-2.83677e-08,0.763283,0.00189771,1.35532e-06,2.80962e-09,0.765182,0.00190042,1.36375e-06,1.71293e-08,0.767083,0.0019032,1.41513e-06,-1.17221e-08,0.768988,0.001906,1.37997e-06,-2.98453e-08,0.770895,0.00190867,1.29043e-06,7.14987e-08,0.772805,0.00191146,1.50493e-06,-7.73354e-08,0.774718,0.00191424,1.27292e-06,5.90292e-08,0.776634,0.00191697,1.45001e-06,-3.9572e-08,0.778552,0.00191975,1.33129e-06,3.9654e-08,0.780473,0.00192253,1.45026e-06,-5.94395e-08,0.782397,0.00192525,1.27194e-06,7.88945e-08,0.784324,0.00192803,1.50862e-06,-7.73249e-08,0.786253,0.00193082,1.27665e-06,5.15913e-08,0.788185,0.00193352,1.43142e-06,-9.83099e-09,0.79012,0.00193636,1.40193e-06,-1.22672e-08,0.792058,0.00193912,1.36513e-06,-7.05275e-10,0.793999,0.00194185,1.36301e-06,1.50883e-08,0.795942,0.00194462,1.40828e-06,-4.33147e-11,0.797888,0.00194744,1.40815e-06,-1.49151e-08,0.799837,0.00195021,1.3634e-06,9.93244e-11,0.801788,0.00195294,1.3637e-06,1.45179e-08,0.803743,0.00195571,1.40725e-06,1.43363e-09,0.8057,0.00195853,1.41155e-06,-2.02525e-08,0.80766,0.00196129,1.35079e-06,1.99718e-08,0.809622,0.00196405,1.41071e-06,-3.01649e-11,0.811588,0.00196687,1.41062e-06,-1.9851e-08,0.813556,0.00196964,1.35107e-06,1.98296e-08,0.815527,0.0019724,1.41056e-06,1.37485e-10,0.817501,0.00197522,1.41097e-06,-2.03796e-08,0.819477,0.00197798,1.34983e-06,2.17763e-08,0.821457,0.00198074,1.41516e-06,-7.12085e-09,0.823439,0.00198355,1.3938e-06,6.70707e-09,0.825424,0.00198636,1.41392e-06,-1.97074e-08,0.827412,0.00198913,1.35479e-06,1.25179e-08,0.829402,0.00199188,1.39235e-06,2.92405e-08,0.831396,0.00199475,1.48007e-06,-6.98755e-08,0.833392,0.0019975,1.27044e-06,7.14477e-08,0.835391,0.00200026,1.48479e-06,-3.71014e-08,0.837392,0.00200311,1.37348e-06,1.73533e-08,0.839397,0.00200591,1.42554e-06,-3.23118e-08,0.841404,0.00200867,1.32861e-06,5.2289e-08,0.843414,0.00201148,1.48547e-06,-5.76348e-08,0.845427,0.00201428,1.31257e-06,5.9041e-08,0.847443,0.00201708,1.48969e-06,-5.93197e-08,0.849461,0.00201988,1.31173e-06,5.90289e-08,0.851482,0.00202268,1.48882e-06,-5.75864e-08,0.853507,0.00202549,1.31606e-06,5.21075e-08,0.855533,0.00202828,1.47238e-06,-3.16344e-08,0.857563,0.00203113,1.37748e-06,1.48257e-08,0.859596,0.00203393,1.42196e-06,-2.76684e-08,0.861631,0.00203669,1.33895e-06,3.62433e-08,0.863669,0.00203947,1.44768e-06,1.90463e-09,0.86571,0.00204237,1.45339e-06,-4.38617e-08,0.867754,0.00204515,1.32181e-06,5.43328e-08,0.8698,0.00204796,1.48481e-06,-5.42603e-08,0.87185,0.00205076,1.32203e-06,4.34989e-08,0.873902,0.00205354,1.45252e-06,-5.26029e-10,0.875957,0.00205644,1.45095e-06,-4.13949e-08,0.878015,0.00205922,1.32676e-06,4.68962e-08,0.880075,0.00206201,1.46745e-06,-2.69807e-08,0.882139,0.00206487,1.38651e-06,1.42181e-09,0.884205,0.00206764,1.39077e-06,2.12935e-08,0.886274,0.00207049,1.45465e-06,-2.69912e-08,0.888346,0.00207332,1.37368e-06,2.70664e-08,0.890421,0.00207615,1.45488e-06,-2.16698e-08,0.892498,0.00207899,1.38987e-06,8.14756e-12,0.894579,0.00208177,1.38989e-06,2.16371e-08,0.896662,0.00208462,1.45481e-06,-2.6952e-08,0.898748,0.00208744,1.37395e-06,2.65663e-08,0.900837,0.00209027,1.45365e-06,-1.97084e-08,0.902928,0.00209312,1.39452e-06,-7.33731e-09,0.905023,0.00209589,1.37251e-06,4.90578e-08,0.90712,0.00209878,1.51968e-06,-6.96845e-08,0.90922,0.00210161,1.31063e-06,5.08664e-08,0.911323,0.00210438,1.46323e-06,-1.45717e-08,0.913429,0.00210727,1.41952e-06,7.42038e-09,0.915538,0.00211013,1.44178e-06,-1.51097e-08,0.917649,0.00211297,1.39645e-06,-6.58618e-09,0.919764,0.00211574,1.37669e-06,4.14545e-08,0.921881,0.00211862,1.50105e-06,-4.00222e-08,0.924001,0.0021215,1.38099e-06,-5.7518e-10,0.926124,0.00212426,1.37926e-06,4.23229e-08,0.92825,0.00212714,1.50623e-06,-4.9507e-08,0.930378,0.00213001,1.35771e-06,3.64958e-08,0.93251,0.00213283,1.4672e-06,-3.68713e-08,0.934644,0.00213566,1.35658e-06,5.13848e-08,0.936781,0.00213852,1.51074e-06,-4.94585e-08,0.938921,0.0021414,1.36236e-06,2.72399e-08,0.941064,0.0021442,1.44408e-06,1.0372e-10,0.943209,0.00214709,1.44439e-06,-2.76547e-08,0.945358,0.0021499,1.36143e-06,5.09106e-08,0.947509,0.00215277,1.51416e-06,-5.67784e-08,0.949663,0.00215563,1.34382e-06,5.69935e-08,0.95182,0.00215849,1.5148e-06,-5.19861e-08,0.95398,0.00216136,1.35885e-06,3.17417e-08,0.956143,0.00216418,1.45407e-06,-1.53758e-08,0.958309,0.00216704,1.40794e-06,2.97615e-08,0.960477,0.00216994,1.49723e-06,-4.40657e-08,0.962649,0.00217281,1.36503e-06,2.72919e-08,0.964823,0.00217562,1.44691e-06,-5.49729e-09,0.967,0.0021785,1.43041e-06,-5.30273e-09,0.96918,0.00218134,1.41451e-06,2.67084e-08,0.971363,0.00218425,1.49463e-06,-4.19265e-08,0.973548,0.00218711,1.36885e-06,2.17881e-08,0.975737,0.00218992,1.43422e-06,1.43789e-08,0.977928,0.00219283,1.47735e-06,-1.96989e-08,0.980122,0.00219572,1.41826e-06,4.81221e-09,0.98232,0.00219857,1.43269e-06,4.50048e-10,0.98452,0.00220144,1.43404e-06,-6.61237e-09,0.986722,0.00220429,1.41421e-06,2.59993e-08,0.988928,0.0022072,1.4922e-06,-3.77803e-08,0.991137,0.00221007,1.37886e-06,5.9127e-09,0.993348,0.00221284,1.3966e-06,1.33339e-07,0.995563,0.00221604,1.79662e-06,-5.98872e-07,0.99778,0.00222015,0.,0.}; + + template + __device__ __forceinline__ void RGB2LabConvert_f(const T& src, D& dst) + { + const float _1_3 = 1.0f / 3.0f; + const float _a = 16.0f / 116.0f; + + float B = blueIdx == 0 ? src.x : src.z; + float G = src.y; + float R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + } + + float X = B * 0.189828f + G * 0.376219f + R * 0.433953f; + float Y = B * 0.072169f + G * 0.715160f + R * 0.212671f; + float Z = B * 0.872766f + G * 0.109477f + R * 0.017758f; + + float FX = X > 0.008856f ? ::powf(X, _1_3) : (7.787f * X + _a); + float FY = Y > 0.008856f ? ::powf(Y, _1_3) : (7.787f * Y + _a); + float FZ = Z > 0.008856f ? ::powf(Z, _1_3) : (7.787f * Z + _a); + + float L = Y > 0.008856f ? (116.f * FY - 16.f) : (903.3f * Y); + float a = 500.f * (FX - FY); + float b = 200.f * (FY - FZ); + + dst.x = L; + dst.y = a; + dst.z = b; + } + + template struct RGB2Lab; + template + struct RGB2Lab + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LabConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Lab() {} + __host__ __device__ __forceinline__ RGB2Lab(const RGB2Lab&) {} + }; + template + struct RGB2Lab + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LabConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Lab() {} + __host__ __device__ __forceinline__ RGB2Lab(const RGB2Lab&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Lab functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_sRGBInvGammaTab[] = {0,0.0126255,0.,-8.33961e-06,0.0126172,0.0126005,-2.50188e-05,4.1698e-05,0.0252344,0.0126756,0.000100075,-0.000158451,0.0378516,0.0124004,-0.000375277,-0.000207393,0.0496693,0.0110276,-0.000997456,0.00016837,0.0598678,0.00953783,-0.000492346,2.07235e-05,0.068934,0.00861531,-0.000430176,3.62876e-05,0.0771554,0.00786382,-0.000321313,1.87625e-05,0.0847167,0.00727748,-0.000265025,1.53594e-05,0.0917445,0.00679351,-0.000218947,1.10545e-05,0.0983301,0.00638877,-0.000185784,8.66984e-06,0.104542,0.00604322,-0.000159774,6.82996e-06,0.110432,0.00574416,-0.000139284,5.51008e-06,0.116042,0.00548212,-0.000122754,4.52322e-06,0.121406,0.00525018,-0.000109184,3.75557e-06,0.126551,0.00504308,-9.79177e-05,3.17134e-06,0.131499,0.00485676,-8.84037e-05,2.68469e-06,0.13627,0.004688,-8.03496e-05,2.31725e-06,0.14088,0.00453426,-7.33978e-05,2.00868e-06,0.145343,0.00439349,-6.73718e-05,1.74775e-06,0.149671,0.00426399,-6.21286e-05,1.53547e-06,0.153875,0.00414434,-5.75222e-05,1.364e-06,0.157963,0.00403338,-5.34301e-05,1.20416e-06,0.161944,0.00393014,-4.98177e-05,1.09114e-06,0.165825,0.00383377,-4.65443e-05,9.57987e-07,0.169613,0.00374356,-4.36703e-05,8.88359e-07,0.173314,0.00365888,-4.10052e-05,7.7849e-07,0.176933,0.00357921,-3.86697e-05,7.36254e-07,0.180474,0.00350408,-3.6461e-05,6.42534e-07,0.183942,0.00343308,-3.45334e-05,6.12614e-07,0.187342,0.00336586,-3.26955e-05,5.42894e-07,0.190675,0.00330209,-3.10669e-05,5.08967e-07,0.193947,0.00324149,-2.954e-05,4.75977e-07,0.197159,0.00318383,-2.8112e-05,4.18343e-07,0.200315,0.00312887,-2.6857e-05,4.13651e-07,0.203418,0.00307639,-2.5616e-05,3.70847e-07,0.206469,0.00302627,-2.45035e-05,3.3813e-07,0.209471,0.00297828,-2.34891e-05,3.32999e-07,0.212426,0.0029323,-2.24901e-05,2.96826e-07,0.215336,0.00288821,-2.15996e-05,2.82736e-07,0.218203,0.00284586,-2.07514e-05,2.70961e-07,0.221029,0.00280517,-1.99385e-05,2.42744e-07,0.223814,0.00276602,-1.92103e-05,2.33277e-07,0.226561,0.0027283,-1.85105e-05,2.2486e-07,0.229271,0.00269195,-1.78359e-05,2.08383e-07,0.231945,0.00265691,-1.72108e-05,1.93305e-07,0.234585,0.00262307,-1.66308e-05,1.80687e-07,0.237192,0.00259035,-1.60888e-05,1.86632e-07,0.239766,0.00255873,-1.55289e-05,1.60569e-07,0.24231,0.00252815,-1.50472e-05,1.54566e-07,0.244823,0.00249852,-1.45835e-05,1.59939e-07,0.247307,0.00246983,-1.41037e-05,1.29549e-07,0.249763,0.00244202,-1.3715e-05,1.41429e-07,0.252191,0.00241501,-1.32907e-05,1.39198e-07,0.254593,0.00238885,-1.28731e-05,1.06444e-07,0.256969,0.00236342,-1.25538e-05,1.2048e-07,0.25932,0.00233867,-1.21924e-05,1.26892e-07,0.261647,0.00231467,-1.18117e-05,8.72084e-08,0.26395,0.00229131,-1.15501e-05,1.20323e-07,0.26623,0.00226857,-1.11891e-05,8.71514e-08,0.268487,0.00224645,-1.09276e-05,9.73165e-08,0.270723,0.00222489,-1.06357e-05,8.98259e-08,0.272937,0.00220389,-1.03662e-05,7.98218e-08,0.275131,0.00218339,-1.01267e-05,9.75254e-08,0.277304,0.00216343,-9.83416e-06,6.65195e-08,0.279458,0.00214396,-9.63461e-06,8.34313e-08,0.281592,0.00212494,-9.38431e-06,7.65919e-08,0.283708,0.00210641,-9.15454e-06,5.7236e-08,0.285805,0.00208827,-8.98283e-06,8.18939e-08,0.287885,0.00207055,-8.73715e-06,6.2224e-08,0.289946,0.00205326,-8.55047e-06,5.66388e-08,0.291991,0.00203633,-8.38056e-06,6.88491e-08,0.294019,0.00201978,-8.17401e-06,5.53955e-08,0.296031,0.00200359,-8.00782e-06,6.71971e-08,0.298027,0.00198778,-7.80623e-06,3.34439e-08,0.300007,0.00197227,-7.7059e-06,6.7248e-08,0.301971,0.00195706,-7.50416e-06,5.51915e-08,0.303921,0.00194221,-7.33858e-06,3.98124e-08,0.305856,0.00192766,-7.21915e-06,5.37795e-08,0.307776,0.00191338,-7.05781e-06,4.30919e-08,0.309683,0.00189939,-6.92853e-06,4.20744e-08,0.311575,0.00188566,-6.80231e-06,5.68321e-08,0.313454,0.00187223,-6.63181e-06,2.86195e-08,0.31532,0.00185905,-6.54595e-06,3.73075e-08,0.317172,0.00184607,-6.43403e-06,6.05684e-08,0.319012,0.00183338,-6.25233e-06,1.84426e-08,0.320839,0.00182094,-6.197e-06,4.44757e-08,0.322654,0.00180867,-6.06357e-06,4.20729e-08,0.324456,0.00179667,-5.93735e-06,2.56511e-08,0.326247,0.00178488,-5.8604e-06,3.41368e-08,0.328026,0.00177326,-5.75799e-06,4.64177e-08,0.329794,0.00176188,-5.61874e-06,1.86107e-08,0.33155,0.0017507,-5.5629e-06,2.81511e-08,0.333295,0.00173966,-5.47845e-06,4.75987e-08,0.335029,0.00172884,-5.33565e-06,1.98726e-08,0.336753,0.00171823,-5.27604e-06,2.19226e-08,0.338466,0.00170775,-5.21027e-06,4.14483e-08,0.340169,0.00169745,-5.08592e-06,2.09017e-08,0.341861,0.00168734,-5.02322e-06,2.39561e-08,0.343543,0.00167737,-4.95135e-06,3.22852e-08,0.345216,0.00166756,-4.85449e-06,2.57173e-08,0.346878,0.00165793,-4.77734e-06,1.38569e-08,0.348532,0.00164841,-4.73577e-06,3.80634e-08,0.350175,0.00163906,-4.62158e-06,1.27043e-08,0.35181,0.00162985,-4.58347e-06,3.03279e-08,0.353435,0.00162078,-4.49249e-06,1.49961e-08,0.355051,0.00161184,-4.4475e-06,2.88977e-08,0.356659,0.00160303,-4.3608e-06,1.84241e-08,0.358257,0.00159436,-4.30553e-06,1.6616e-08,0.359848,0.0015858,-4.25568e-06,3.43218e-08,0.361429,0.00157739,-4.15272e-06,-4.89172e-09,0.363002,0.00156907,-4.16739e-06,4.48498e-08,0.364567,0.00156087,-4.03284e-06,4.30676e-09,0.366124,0.00155282,-4.01992e-06,2.73303e-08,0.367673,0.00154486,-3.93793e-06,5.58036e-09,0.369214,0.001537,-3.92119e-06,3.97554e-08,0.370747,0.00152928,-3.80193e-06,-1.55904e-08,0.372272,0.00152163,-3.8487e-06,5.24081e-08,0.37379,0.00151409,-3.69147e-06,-1.52272e-08,0.375301,0.00150666,-3.73715e-06,3.83028e-08,0.376804,0.0014993,-3.62225e-06,1.10278e-08,0.378299,0.00149209,-3.58916e-06,6.99326e-09,0.379788,0.00148493,-3.56818e-06,2.06038e-08,0.381269,0.00147786,-3.50637e-06,2.98009e-08,0.382744,0.00147093,-3.41697e-06,-2.05978e-08,0.384211,0.00146404,-3.47876e-06,5.25899e-08,0.385672,0.00145724,-3.32099e-06,-1.09471e-08,0.387126,0.00145056,-3.35383e-06,2.10009e-08,0.388573,0.00144392,-3.29083e-06,1.63501e-08,0.390014,0.00143739,-3.24178e-06,3.00641e-09,0.391448,0.00143091,-3.23276e-06,3.12282e-08,0.392875,0.00142454,-3.13908e-06,-8.70932e-09,0.394297,0.00141824,-3.16521e-06,3.34114e-08,0.395712,0.00141201,-3.06497e-06,-5.72754e-09,0.397121,0.00140586,-3.08215e-06,1.9301e-08,0.398524,0.00139975,-3.02425e-06,1.7931e-08,0.39992,0.00139376,-2.97046e-06,-1.61822e-09,0.401311,0.00138781,-2.97531e-06,1.83442e-08,0.402696,0.00138192,-2.92028e-06,1.76485e-08,0.404075,0.00137613,-2.86733e-06,4.68617e-10,0.405448,0.00137039,-2.86593e-06,1.02794e-08,0.406816,0.00136469,-2.83509e-06,1.80179e-08,0.408178,0.00135908,-2.78104e-06,7.05594e-09,0.409534,0.00135354,-2.75987e-06,1.33633e-08,0.410885,0.00134806,-2.71978e-06,-9.04568e-10,0.41223,0.00134261,-2.72249e-06,2.0057e-08,0.41357,0.00133723,-2.66232e-06,1.00841e-08,0.414905,0.00133194,-2.63207e-06,-7.88835e-10,0.416234,0.00132667,-2.63444e-06,2.28734e-08,0.417558,0.00132147,-2.56582e-06,-1.29785e-09,0.418877,0.00131633,-2.56971e-06,1.21205e-08,0.420191,0.00131123,-2.53335e-06,1.24202e-08,0.421499,0.0013062,-2.49609e-06,-2.19681e-09,0.422803,0.0013012,-2.50268e-06,2.61696e-08,0.424102,0.00129628,-2.42417e-06,-1.30747e-08,0.425396,0.00129139,-2.46339e-06,2.6129e-08,0.426685,0.00128654,-2.38501e-06,-2.03454e-09,0.427969,0.00128176,-2.39111e-06,1.18115e-08,0.429248,0.00127702,-2.35567e-06,1.43932e-08,0.430523,0.00127235,-2.31249e-06,-9.77965e-09,0.431793,0.00126769,-2.34183e-06,2.47253e-08,0.433058,0.00126308,-2.26766e-06,2.85278e-10,0.434319,0.00125855,-2.2668e-06,3.93614e-09,0.435575,0.00125403,-2.25499e-06,1.37722e-08,0.436827,0.00124956,-2.21368e-06,5.79803e-10,0.438074,0.00124513,-2.21194e-06,1.37112e-08,0.439317,0.00124075,-2.1708e-06,4.17973e-09,0.440556,0.00123642,-2.15826e-06,-6.27703e-10,0.44179,0.0012321,-2.16015e-06,2.81332e-08,0.44302,0.00122787,-2.07575e-06,-2.24985e-08,0.444246,0.00122365,-2.14324e-06,3.20586e-08,0.445467,0.00121946,-2.04707e-06,-1.6329e-08,0.446685,0.00121532,-2.09605e-06,3.32573e-08,0.447898,0.00121122,-1.99628e-06,-2.72927e-08,0.449107,0.00120715,-2.07816e-06,4.6111e-08,0.450312,0.00120313,-1.93983e-06,-3.79416e-08,0.451514,0.00119914,-2.05365e-06,4.60507e-08,0.452711,0.00119517,-1.9155e-06,-2.7052e-08,0.453904,0.00119126,-1.99666e-06,3.23551e-08,0.455093,0.00118736,-1.89959e-06,-1.29613e-08,0.456279,0.00118352,-1.93848e-06,1.94905e-08,0.45746,0.0011797,-1.88e-06,-5.39588e-09,0.458638,0.00117593,-1.89619e-06,2.09282e-09,0.459812,0.00117214,-1.88991e-06,2.68267e-08,0.460982,0.00116844,-1.80943e-06,-1.99925e-08,0.462149,0.00116476,-1.86941e-06,2.3341e-08,0.463312,0.00116109,-1.79939e-06,-1.37674e-08,0.464471,0.00115745,-1.84069e-06,3.17287e-08,0.465627,0.00115387,-1.7455e-06,-2.37407e-08,0.466779,0.00115031,-1.81673e-06,3.34315e-08,0.467927,0.00114677,-1.71643e-06,-2.05786e-08,0.469073,0.00114328,-1.77817e-06,1.90802e-08,0.470214,0.00113978,-1.72093e-06,3.86247e-09,0.471352,0.00113635,-1.70934e-06,-4.72759e-09,0.472487,0.00113292,-1.72352e-06,1.50478e-08,0.473618,0.00112951,-1.67838e-06,4.14108e-09,0.474746,0.00112617,-1.66595e-06,-1.80986e-09,0.47587,0.00112283,-1.67138e-06,3.09816e-09,0.476991,0.0011195,-1.66209e-06,1.92198e-08,0.478109,0.00111623,-1.60443e-06,-2.03726e-08,0.479224,0.00111296,-1.66555e-06,3.2468e-08,0.480335,0.00110973,-1.56814e-06,-2.00922e-08,0.481443,0.00110653,-1.62842e-06,1.80983e-08,0.482548,0.00110333,-1.57413e-06,7.30362e-09,0.48365,0.0011002,-1.55221e-06,-1.75107e-08,0.484749,0.00109705,-1.60475e-06,3.29373e-08,0.485844,0.00109393,-1.50594e-06,-2.48315e-08,0.486937,0.00109085,-1.58043e-06,3.65865e-08,0.488026,0.0010878,-1.47067e-06,-3.21078e-08,0.489112,0.00108476,-1.56699e-06,3.22397e-08,0.490195,0.00108172,-1.47027e-06,-7.44391e-09,0.491276,0.00107876,-1.49261e-06,-2.46428e-09,0.492353,0.00107577,-1.5e-06,1.73011e-08,0.493427,0.00107282,-1.4481e-06,-7.13552e-09,0.494499,0.0010699,-1.4695e-06,1.1241e-08,0.495567,0.001067,-1.43578e-06,-8.02637e-09,0.496633,0.0010641,-1.45986e-06,2.08645e-08,0.497695,0.00106124,-1.39726e-06,-1.58271e-08,0.498755,0.0010584,-1.44475e-06,1.26415e-08,0.499812,0.00105555,-1.40682e-06,2.48655e-08,0.500866,0.00105281,-1.33222e-06,-5.24988e-08,0.501918,0.00104999,-1.48972e-06,6.59206e-08,0.502966,0.00104721,-1.29196e-06,-3.237e-08,0.504012,0.00104453,-1.38907e-06,3.95479e-09,0.505055,0.00104176,-1.3772e-06,1.65509e-08,0.506096,0.00103905,-1.32755e-06,-1.05539e-08,0.507133,0.00103637,-1.35921e-06,2.56648e-08,0.508168,0.00103373,-1.28222e-06,-3.25007e-08,0.509201,0.00103106,-1.37972e-06,4.47336e-08,0.51023,0.00102844,-1.24552e-06,-2.72245e-08,0.511258,0.00102587,-1.32719e-06,4.55952e-09,0.512282,0.00102323,-1.31352e-06,8.98645e-09,0.513304,0.00102063,-1.28656e-06,1.90992e-08,0.514323,0.00101811,-1.22926e-06,-2.57786e-08,0.51534,0.00101557,-1.30659e-06,2.44104e-08,0.516355,0.00101303,-1.23336e-06,-1.22581e-08,0.517366,0.00101053,-1.27014e-06,2.4622e-08,0.518376,0.00100806,-1.19627e-06,-2.66253e-08,0.519383,0.00100559,-1.27615e-06,2.22744e-08,0.520387,0.00100311,-1.20932e-06,-2.8679e-09,0.521389,0.00100068,-1.21793e-06,-1.08029e-08,0.522388,0.000998211,-1.25034e-06,4.60795e-08,0.523385,0.000995849,-1.1121e-06,-5.4306e-08,0.52438,0.000993462,-1.27502e-06,5.19354e-08,0.525372,0.000991067,-1.11921e-06,-3.42262e-08,0.526362,0.000988726,-1.22189e-06,2.53646e-08,0.52735,0.000986359,-1.14579e-06,-7.62782e-09,0.528335,0.000984044,-1.16868e-06,5.14668e-09,0.529318,0.000981722,-1.15324e-06,-1.29589e-08,0.530298,0.000979377,-1.19211e-06,4.66888e-08,0.531276,0.000977133,-1.05205e-06,-5.45868e-08,0.532252,0.000974865,-1.21581e-06,5.24495e-08,0.533226,0.000972591,-1.05846e-06,-3.60019e-08,0.534198,0.000970366,-1.16647e-06,3.19537e-08,0.535167,0.000968129,-1.07061e-06,-3.2208e-08,0.536134,0.000965891,-1.16723e-06,3.72738e-08,0.537099,0.000963668,-1.05541e-06,2.32205e-09,0.538061,0.000961564,-1.04844e-06,-4.65618e-08,0.539022,0.000959328,-1.18813e-06,6.47159e-08,0.53998,0.000957146,-9.93979e-07,-3.3488e-08,0.540936,0.000955057,-1.09444e-06,9.63166e-09,0.54189,0.000952897,-1.06555e-06,-5.03871e-09,0.542842,0.000950751,-1.08066e-06,1.05232e-08,0.543792,0.000948621,-1.04909e-06,2.25503e-08,0.544739,0.000946591,-9.81444e-07,-4.11195e-08,0.545685,0.000944504,-1.1048e-06,2.27182e-08,0.546628,0.000942363,-1.03665e-06,9.85146e-09,0.54757,0.000940319,-1.00709e-06,-2.51938e-09,0.548509,0.000938297,-1.01465e-06,2.25858e-10,0.549446,0.000936269,-1.01397e-06,1.61598e-09,0.550381,0.000934246,-1.00913e-06,-6.68983e-09,0.551315,0.000932207,-1.0292e-06,2.51434e-08,0.552246,0.000930224,-9.53765e-07,-3.42793e-08,0.553175,0.000928214,-1.0566e-06,5.23688e-08,0.554102,0.000926258,-8.99497e-07,-5.59865e-08,0.555028,0.000924291,-1.06746e-06,5.23679e-08,0.555951,0.000922313,-9.10352e-07,-3.42763e-08,0.556872,0.00092039,-1.01318e-06,2.51326e-08,0.557792,0.000918439,-9.37783e-07,-6.64954e-09,0.558709,0.000916543,-9.57732e-07,1.46554e-09,0.559625,0.000914632,-9.53335e-07,7.87281e-10,0.560538,0.000912728,-9.50973e-07,-4.61466e-09,0.56145,0.000910812,-9.64817e-07,1.76713e-08,0.56236,0.000908935,-9.11804e-07,-6.46564e-09,0.563268,0.000907092,-9.312e-07,8.19121e-09,0.564174,0.000905255,-9.06627e-07,-2.62992e-08,0.565078,0.000903362,-9.85524e-07,3.74007e-08,0.565981,0.000901504,-8.73322e-07,-4.0942e-09,0.566882,0.000899745,-8.85605e-07,-2.1024e-08,0.56778,0.00089791,-9.48677e-07,2.85854e-08,0.568677,0.000896099,-8.62921e-07,-3.3713e-08,0.569573,0.000894272,-9.64059e-07,4.6662e-08,0.570466,0.000892484,-8.24073e-07,-3.37258e-08,0.571358,0.000890734,-9.25251e-07,2.86365e-08,0.572247,0.00088897,-8.39341e-07,-2.12155e-08,0.573135,0.000887227,-9.02988e-07,-3.37913e-09,0.574022,0.000885411,-9.13125e-07,3.47319e-08,0.574906,0.000883689,-8.08929e-07,-1.63394e-08,0.575789,0.000882022,-8.57947e-07,-2.8979e-08,0.57667,0.00088022,-9.44885e-07,7.26509e-08,0.57755,0.000878548,-7.26932e-07,-8.28106e-08,0.578427,0.000876845,-9.75364e-07,7.97774e-08,0.579303,0.000875134,-7.36032e-07,-5.74849e-08,0.580178,0.00087349,-9.08486e-07,3.09529e-08,0.58105,0.000871765,-8.15628e-07,-6.72206e-09,0.581921,0.000870114,-8.35794e-07,-4.06451e-09,0.582791,0.00086843,-8.47987e-07,2.29799e-08,0.583658,0.000866803,-7.79048e-07,-2.82503e-08,0.584524,0.00086516,-8.63799e-07,3.04167e-08,0.585388,0.000863524,-7.72548e-07,-3.38119e-08,0.586251,0.000861877,-8.73984e-07,4.52264e-08,0.587112,0.000860265,-7.38305e-07,-2.78842e-08,0.587972,0.000858705,-8.21958e-07,6.70567e-09,0.58883,0.000857081,-8.01841e-07,1.06161e-09,0.589686,0.000855481,-7.98656e-07,-1.09521e-08,0.590541,0.00085385,-8.31512e-07,4.27468e-08,0.591394,0.000852316,-7.03272e-07,-4.08257e-08,0.592245,0.000850787,-8.25749e-07,1.34677e-09,0.593095,0.000849139,-8.21709e-07,3.54387e-08,0.593944,0.000847602,-7.15393e-07,-2.38924e-08,0.59479,0.0008461,-7.8707e-07,5.26143e-10,0.595636,0.000844527,-7.85491e-07,2.17879e-08,0.596479,0.000843021,-7.20127e-07,-2.80733e-08,0.597322,0.000841497,-8.04347e-07,3.09005e-08,0.598162,0.000839981,-7.11646e-07,-3.5924e-08,0.599002,0.00083845,-8.19418e-07,5.3191e-08,0.599839,0.000836971,-6.59845e-07,-5.76307e-08,0.600676,0.000835478,-8.32737e-07,5.81227e-08,0.60151,0.000833987,-6.58369e-07,-5.56507e-08,0.602344,0.000832503,-8.25321e-07,4.52706e-08,0.603175,0.000830988,-6.89509e-07,-6.22236e-09,0.604006,0.000829591,-7.08176e-07,-2.03811e-08,0.604834,0.000828113,-7.6932e-07,2.8142e-08,0.605662,0.000826659,-6.84894e-07,-3.25822e-08,0.606488,0.000825191,-7.8264e-07,4.25823e-08,0.607312,0.000823754,-6.54893e-07,-1.85376e-08,0.608135,0.000822389,-7.10506e-07,-2.80365e-08,0.608957,0.000820883,-7.94616e-07,7.1079e-08,0.609777,0.000819507,-5.81379e-07,-7.74655e-08,0.610596,0.000818112,-8.13775e-07,5.9969e-08,0.611413,0.000816665,-6.33868e-07,-4.32013e-08,0.612229,0.000815267,-7.63472e-07,5.32313e-08,0.613044,0.0008139,-6.03778e-07,-5.05148e-08,0.613857,0.000812541,-7.55323e-07,2.96187e-08,0.614669,0.000811119,-6.66466e-07,-8.35545e-09,0.615479,0.000809761,-6.91533e-07,3.80301e-09,0.616288,0.00080839,-6.80124e-07,-6.85666e-09,0.617096,0.000807009,-7.00694e-07,2.36237e-08,0.617903,0.000805678,-6.29822e-07,-2.80336e-08,0.618708,0.000804334,-7.13923e-07,2.8906e-08,0.619511,0.000802993,-6.27205e-07,-2.79859e-08,0.620314,0.000801655,-7.11163e-07,2.34329e-08,0.621114,0.000800303,-6.40864e-07,-6.14108e-09,0.621914,0.000799003,-6.59287e-07,1.13151e-09,0.622712,0.000797688,-6.55893e-07,1.61507e-09,0.62351,0.000796381,-6.51048e-07,-7.59186e-09,0.624305,0.000795056,-6.73823e-07,2.87524e-08,0.6251,0.000793794,-5.87566e-07,-4.7813e-08,0.625893,0.000792476,-7.31005e-07,4.32901e-08,0.626685,0.000791144,-6.01135e-07,-6.13814e-09,0.627475,0.000789923,-6.19549e-07,-1.87376e-08,0.628264,0.000788628,-6.75762e-07,2.14837e-08,0.629052,0.000787341,-6.11311e-07,-7.59265e-09,0.629839,0.000786095,-6.34089e-07,8.88692e-09,0.630625,0.000784854,-6.07428e-07,-2.7955e-08,0.631409,0.000783555,-6.91293e-07,4.33285e-08,0.632192,0.000782302,-5.61307e-07,-2.61497e-08,0.632973,0.000781101,-6.39757e-07,1.6658e-09,0.633754,0.000779827,-6.34759e-07,1.94866e-08,0.634533,0.000778616,-5.76299e-07,-2.00076e-08,0.635311,0.000777403,-6.36322e-07,9.39091e-10,0.636088,0.000776133,-6.33505e-07,1.62512e-08,0.636863,0.000774915,-5.84751e-07,-6.33937e-09,0.637638,0.000773726,-6.03769e-07,9.10609e-09,0.638411,0.000772546,-5.76451e-07,-3.00849e-08,0.639183,0.000771303,-6.66706e-07,5.1629e-08,0.639953,0.000770125,-5.11819e-07,-5.7222e-08,0.640723,0.000768929,-6.83485e-07,5.80497e-08,0.641491,0.000767736,-5.09336e-07,-5.57674e-08,0.642259,0.000766551,-6.76638e-07,4.58105e-08,0.643024,0.000765335,-5.39206e-07,-8.26541e-09,0.643789,0.000764231,-5.64002e-07,-1.27488e-08,0.644553,0.000763065,-6.02249e-07,-3.44168e-10,0.645315,0.00076186,-6.03281e-07,1.41254e-08,0.646077,0.000760695,-5.60905e-07,3.44727e-09,0.646837,0.000759584,-5.50563e-07,-2.79144e-08,0.647596,0.000758399,-6.34307e-07,4.86057e-08,0.648354,0.000757276,-4.88489e-07,-4.72989e-08,0.64911,0.000756158,-6.30386e-07,2.13807e-08,0.649866,0.000754961,-5.66244e-07,2.13808e-08,0.65062,0.000753893,-5.02102e-07,-4.7299e-08,0.651374,0.000752746,-6.43999e-07,4.86059e-08,0.652126,0.000751604,-4.98181e-07,-2.79154e-08,0.652877,0.000750524,-5.81927e-07,3.45089e-09,0.653627,0.000749371,-5.71575e-07,1.41119e-08,0.654376,0.00074827,-5.29239e-07,-2.93748e-10,0.655123,0.00074721,-5.3012e-07,-1.29368e-08,0.65587,0.000746111,-5.68931e-07,-7.56355e-09,0.656616,0.000744951,-5.91621e-07,4.3191e-08,0.65736,0.000743897,-4.62048e-07,-4.59911e-08,0.658103,0.000742835,-6.00022e-07,2.15642e-08,0.658846,0.0007417,-5.35329e-07,1.93389e-08,0.659587,0.000740687,-4.77312e-07,-3.93152e-08,0.660327,0.000739615,-5.95258e-07,1.87126e-08,0.661066,0.00073848,-5.3912e-07,2.40695e-08,0.661804,0.000737474,-4.66912e-07,-5.53859e-08,0.662541,0.000736374,-6.33069e-07,7.82648e-08,0.663277,0.000735343,-3.98275e-07,-7.88593e-08,0.664012,0.00073431,-6.34853e-07,5.83585e-08,0.664745,0.000733215,-4.59777e-07,-3.53656e-08,0.665478,0.000732189,-5.65874e-07,2.34994e-08,0.66621,0.000731128,-4.95376e-07,9.72743e-10,0.66694,0.00073014,-4.92458e-07,-2.73903e-08,0.66767,0.000729073,-5.74629e-07,4.89839e-08,0.668398,0.000728071,-4.27677e-07,-4.93359e-08,0.669126,0.000727068,-5.75685e-07,2.91504e-08,0.669853,0.000726004,-4.88234e-07,-7.66109e-09,0.670578,0.000725004,-5.11217e-07,1.49392e-09,0.671303,0.000723986,-5.06735e-07,1.68533e-09,0.672026,0.000722978,-5.01679e-07,-8.23525e-09,0.672749,0.00072195,-5.26385e-07,3.12556e-08,0.67347,0.000720991,-4.32618e-07,-5.71825e-08,0.674191,0.000719954,-6.04166e-07,7.8265e-08,0.67491,0.00071898,-3.69371e-07,-7.70634e-08,0.675628,0.00071801,-6.00561e-07,5.11747e-08,0.676346,0.000716963,-4.47037e-07,-8.42615e-09,0.677062,0.000716044,-4.72315e-07,-1.747e-08,0.677778,0.000715046,-5.24725e-07,1.87015e-08,0.678493,0.000714053,-4.68621e-07,2.26856e-09,0.679206,0.000713123,-4.61815e-07,-2.77758e-08,0.679919,0.000712116,-5.45142e-07,4.92298e-08,0.68063,0.000711173,-3.97453e-07,-4.99339e-08,0.681341,0.000710228,-5.47255e-07,3.12967e-08,0.682051,0.000709228,-4.53365e-07,-1.56481e-08,0.68276,0.000708274,-5.00309e-07,3.12958e-08,0.683467,0.000707367,-4.06422e-07,-4.99303e-08,0.684174,0.000706405,-5.56213e-07,4.9216e-08,0.68488,0.00070544,-4.08565e-07,-2.77245e-08,0.685585,0.00070454,-4.91738e-07,2.07748e-09,0.686289,0.000703562,-4.85506e-07,1.94146e-08,0.686992,0.00070265,-4.27262e-07,-2.01314e-08,0.687695,0.000701735,-4.87656e-07,1.50616e-09,0.688396,0.000700764,-4.83137e-07,1.41067e-08,0.689096,0.00069984,-4.40817e-07,1.67168e-09,0.689795,0.000698963,-4.35802e-07,-2.07934e-08,0.690494,0.000698029,-4.98182e-07,2.18972e-08,0.691192,0.000697099,-4.32491e-07,-7.19092e-09,0.691888,0.000696212,-4.54064e-07,6.86642e-09,0.692584,0.000695325,-4.33464e-07,-2.02747e-08,0.693279,0.000694397,-4.94288e-07,1.46279e-08,0.693973,0.000693452,-4.50405e-07,2.13678e-08,0.694666,0.000692616,-3.86301e-07,-4.04945e-08,0.695358,0.000691721,-5.07785e-07,2.14009e-08,0.696049,0.00069077,-4.43582e-07,1.44955e-08,0.69674,0.000689926,-4.00096e-07,-1.97783e-08,0.697429,0.000689067,-4.5943e-07,5.01296e-09,0.698118,0.000688163,-4.44392e-07,-2.73521e-10,0.698805,0.000687273,-4.45212e-07,-3.91893e-09,0.699492,0.000686371,-4.56969e-07,1.59493e-08,0.700178,0.000685505,-4.09121e-07,-2.73351e-10,0.700863,0.000684686,-4.09941e-07,-1.4856e-08,0.701548,0.000683822,-4.54509e-07,9.25979e-11,0.702231,0.000682913,-4.54231e-07,1.44855e-08,0.702913,0.000682048,-4.10775e-07,1.56992e-09,0.703595,0.000681231,-4.06065e-07,-2.07652e-08,0.704276,0.000680357,-4.68361e-07,2.18864e-08,0.704956,0.000679486,-4.02701e-07,-7.17595e-09,0.705635,0.000678659,-4.24229e-07,6.81748e-09,0.706313,0.000677831,-4.03777e-07,-2.0094e-08,0.70699,0.000676963,-4.64059e-07,1.39538e-08,0.707667,0.000676077,-4.22197e-07,2.38835e-08,0.708343,0.000675304,-3.50547e-07,-4.98831e-08,0.709018,0.000674453,-5.00196e-07,5.64395e-08,0.709692,0.000673622,-3.30878e-07,-5.66657e-08,0.710365,0.00067279,-5.00875e-07,5.1014e-08,0.711037,0.000671942,-3.47833e-07,-2.81809e-08,0.711709,0.000671161,-4.32376e-07,2.10513e-09,0.712379,0.000670303,-4.2606e-07,1.97604e-08,0.713049,0.00066951,-3.66779e-07,-2.15422e-08,0.713718,0.000668712,-4.31406e-07,6.8038e-09,0.714387,0.000667869,-4.10994e-07,-5.67295e-09,0.715054,0.00066703,-4.28013e-07,1.5888e-08,0.715721,0.000666222,-3.80349e-07,1.72576e-09,0.716387,0.000665467,-3.75172e-07,-2.27911e-08,0.717052,0.000664648,-4.43545e-07,2.9834e-08,0.717716,0.00066385,-3.54043e-07,-3.69401e-08,0.718379,0.000663031,-4.64864e-07,5.83219e-08,0.719042,0.000662277,-2.89898e-07,-7.71382e-08,0.719704,0.000661465,-5.21313e-07,7.14171e-08,0.720365,0.000660637,-3.07061e-07,-2.97161e-08,0.721025,0.000659934,-3.96209e-07,-1.21575e-08,0.721685,0.000659105,-4.32682e-07,1.87412e-08,0.722343,0.000658296,-3.76458e-07,-3.2029e-09,0.723001,0.000657533,-3.86067e-07,-5.9296e-09,0.723659,0.000656743,-4.03856e-07,2.69213e-08,0.724315,0.000656016,-3.23092e-07,-4.21511e-08,0.724971,0.000655244,-4.49545e-07,2.24737e-08,0.725625,0.000654412,-3.82124e-07,1.18611e-08,0.726279,0.000653683,-3.46541e-07,-1.03132e-08,0.726933,0.000652959,-3.7748e-07,-3.02128e-08,0.727585,0.000652114,-4.68119e-07,7.15597e-08,0.728237,0.000651392,-2.5344e-07,-7.72119e-08,0.728888,0.000650654,-4.85075e-07,5.8474e-08,0.729538,0.000649859,-3.09654e-07,-3.74746e-08,0.730188,0.000649127,-4.22077e-07,3.18197e-08,0.730837,0.000648379,-3.26618e-07,-3.01997e-08,0.731485,0.000647635,-4.17217e-07,2.93747e-08,0.732132,0.000646888,-3.29093e-07,-2.76943e-08,0.732778,0.000646147,-4.12176e-07,2.17979e-08,0.733424,0.000645388,-3.46783e-07,1.07292e-10,0.734069,0.000644695,-3.46461e-07,-2.22271e-08,0.734713,0.000643935,-4.13142e-07,2.91963e-08,0.735357,0.000643197,-3.25553e-07,-3.49536e-08,0.736,0.000642441,-4.30414e-07,5.10133e-08,0.736642,0.000641733,-2.77374e-07,-4.98904e-08,0.737283,0.000641028,-4.27045e-07,2.93392e-08,0.737924,0.000640262,-3.39028e-07,-7.86156e-09,0.738564,0.000639561,-3.62612e-07,2.10703e-09,0.739203,0.000638842,-3.56291e-07,-5.6653e-10,0.739842,0.000638128,-3.57991e-07,1.59086e-10,0.740479,0.000637412,-3.57513e-07,-6.98321e-11,0.741116,0.000636697,-3.57723e-07,1.20214e-10,0.741753,0.000635982,-3.57362e-07,-4.10987e-10,0.742388,0.000635266,-3.58595e-07,1.5237e-09,0.743023,0.000634553,-3.54024e-07,-5.68376e-09,0.743657,0.000633828,-3.71075e-07,2.12113e-08,0.744291,0.00063315,-3.07441e-07,-1.95569e-08,0.744924,0.000632476,-3.66112e-07,-2.58816e-09,0.745556,0.000631736,-3.73877e-07,2.99096e-08,0.746187,0.000631078,-2.84148e-07,-5.74454e-08,0.746818,0.000630337,-4.56484e-07,8.06629e-08,0.747448,0.000629666,-2.14496e-07,-8.63922e-08,0.748077,0.000628978,-4.73672e-07,8.60918e-08,0.748706,0.000628289,-2.15397e-07,-7.91613e-08,0.749334,0.000627621,-4.5288e-07,5.17393e-08,0.749961,0.00062687,-2.97663e-07,-8.58662e-09,0.750588,0.000626249,-3.23422e-07,-1.73928e-08,0.751214,0.00062555,-3.75601e-07,1.85532e-08,0.751839,0.000624855,-3.19941e-07,2.78479e-09,0.752463,0.000624223,-3.11587e-07,-2.96923e-08,0.753087,0.000623511,-4.00664e-07,5.63799e-08,0.75371,0.000622879,-2.31524e-07,-7.66179e-08,0.754333,0.000622186,-4.61378e-07,7.12778e-08,0.754955,0.000621477,-2.47545e-07,-2.96794e-08,0.755576,0.000620893,-3.36583e-07,-1.21648e-08,0.756196,0.000620183,-3.73077e-07,1.87339e-08,0.756816,0.000619493,-3.16875e-07,-3.16622e-09,0.757435,0.00061885,-3.26374e-07,-6.0691e-09,0.758054,0.000618179,-3.44581e-07,2.74426e-08,0.758672,0.000617572,-2.62254e-07,-4.40968e-08,0.759289,0.000616915,-3.94544e-07,2.97352e-08,0.759906,0.000616215,-3.05338e-07,-1.52393e-08,0.760522,0.000615559,-3.51056e-07,3.12221e-08,0.761137,0.000614951,-2.5739e-07,-5.00443e-08,0.761751,0.000614286,-4.07523e-07,4.9746e-08,0.762365,0.00061362,-2.58285e-07,-2.97303e-08,0.762979,0.000613014,-3.47476e-07,9.57079e-09,0.763591,0.000612348,-3.18764e-07,-8.55287e-09,0.764203,0.000611685,-3.44422e-07,2.46407e-08,0.764815,0.00061107,-2.705e-07,-3.04053e-08,0.765426,0.000610437,-3.61716e-07,3.73759e-08,0.766036,0.000609826,-2.49589e-07,-5.94935e-08,0.766645,0.000609149,-4.28069e-07,8.13889e-08,0.767254,0.000608537,-1.83902e-07,-8.72483e-08,0.767862,0.000607907,-4.45647e-07,8.87901e-08,0.76847,0.000607282,-1.79277e-07,-8.90983e-08,0.769077,0.000606656,-4.46572e-07,8.87892e-08,0.769683,0.000606029,-1.80204e-07,-8.72446e-08,0.770289,0.000605407,-4.41938e-07,8.13752e-08,0.770894,0.000604768,-1.97812e-07,-5.94423e-08,0.771498,0.000604194,-3.76139e-07,3.71848e-08,0.772102,0.000603553,-2.64585e-07,-2.96922e-08,0.772705,0.000602935,-3.53661e-07,2.19793e-08,0.773308,0.000602293,-2.87723e-07,1.37955e-09,0.77391,0.000601722,-2.83585e-07,-2.74976e-08,0.774512,0.000601072,-3.66077e-07,4.9006e-08,0.775112,0.000600487,-2.19059e-07,-4.93171e-08,0.775712,0.000599901,-3.67011e-07,2.90531e-08,0.776312,0.000599254,-2.79851e-07,-7.29081e-09,0.776911,0.000598673,-3.01724e-07,1.10077e-10,0.777509,0.00059807,-3.01393e-07,6.85053e-09,0.778107,0.000597487,-2.80842e-07,-2.75123e-08,0.778704,0.000596843,-3.63379e-07,4.35939e-08,0.779301,0.000596247,-2.32597e-07,-2.7654e-08,0.779897,0.000595699,-3.15559e-07,7.41741e-09,0.780492,0.00059509,-2.93307e-07,-2.01562e-09,0.781087,0.000594497,-2.99354e-07,6.45059e-10,0.781681,0.000593901,-2.97418e-07,-5.64635e-10,0.782275,0.000593304,-2.99112e-07,1.61347e-09,0.782868,0.000592711,-2.94272e-07,-5.88926e-09,0.78346,0.000592105,-3.1194e-07,2.19436e-08,0.784052,0.000591546,-2.46109e-07,-2.22805e-08,0.784643,0.000590987,-3.1295e-07,7.57368e-09,0.785234,0.000590384,-2.90229e-07,-8.01428e-09,0.785824,0.00058978,-3.14272e-07,2.44834e-08,0.786414,0.000589225,-2.40822e-07,-3.03148e-08,0.787003,0.000588652,-3.31766e-07,3.7171e-08,0.787591,0.0005881,-2.20253e-07,-5.87646e-08,0.788179,0.000587483,-3.96547e-07,7.86782e-08,0.788766,0.000586926,-1.60512e-07,-7.71342e-08,0.789353,0.000586374,-3.91915e-07,5.10444e-08,0.789939,0.000585743,-2.38782e-07,-7.83422e-09,0.790524,0.000585242,-2.62284e-07,-1.97076e-08,0.791109,0.000584658,-3.21407e-07,2.70598e-08,0.791693,0.000584097,-2.40228e-07,-2.89269e-08,0.792277,0.000583529,-3.27008e-07,2.90431e-08,0.792861,0.000582963,-2.39879e-07,-2.76409e-08,0.793443,0.0005824,-3.22802e-07,2.1916e-08,0.794025,0.00058182,-2.57054e-07,-4.18368e-10,0.794607,0.000581305,-2.58309e-07,-2.02425e-08,0.795188,0.000580727,-3.19036e-07,2.17838e-08,0.795768,0.000580155,-2.53685e-07,-7.28814e-09,0.796348,0.000579625,-2.75549e-07,7.36871e-09,0.796928,0.000579096,-2.53443e-07,-2.21867e-08,0.797506,0.000578523,-3.20003e-07,2.17736e-08,0.798085,0.000577948,-2.54683e-07,-5.30296e-09,0.798662,0.000577423,-2.70592e-07,-5.61698e-10,0.799239,0.00057688,-2.72277e-07,7.54977e-09,0.799816,0.000576358,-2.49627e-07,-2.96374e-08,0.800392,0.00057577,-3.38539e-07,5.1395e-08,0.800968,0.000575247,-1.84354e-07,-5.67335e-08,0.801543,0.000574708,-3.54555e-07,5.63297e-08,0.802117,0.000574168,-1.85566e-07,-4.93759e-08,0.802691,0.000573649,-3.33693e-07,2.19646e-08,0.803264,0.000573047,-2.678e-07,2.1122e-08,0.803837,0.000572575,-2.04433e-07,-4.68482e-08,0.804409,0.000572026,-3.44978e-07,4.70613e-08,0.804981,0.000571477,-2.03794e-07,-2.21877e-08,0.805552,0.000571003,-2.70357e-07,-1.79153e-08,0.806123,0.000570408,-3.24103e-07,3.42443e-08,0.806693,0.000569863,-2.2137e-07,1.47556e-10,0.807263,0.000569421,-2.20928e-07,-3.48345e-08,0.807832,0.000568874,-3.25431e-07,1.99812e-08,0.808401,0.000568283,-2.65487e-07,1.45143e-08,0.808969,0.000567796,-2.21945e-07,-1.84338e-08,0.809536,0.000567297,-2.77246e-07,-3.83608e-10,0.810103,0.000566741,-2.78397e-07,1.99683e-08,0.81067,0.000566244,-2.18492e-07,-1.98848e-08,0.811236,0.000565747,-2.78146e-07,-3.38976e-11,0.811801,0.000565191,-2.78248e-07,2.00204e-08,0.812366,0.000564695,-2.18187e-07,-2.04429e-08,0.812931,0.000564197,-2.79516e-07,2.1467e-09,0.813495,0.000563644,-2.73076e-07,1.18561e-08,0.814058,0.000563134,-2.37507e-07,1.00334e-08,0.814621,0.000562689,-2.07407e-07,-5.19898e-08,0.815183,0.000562118,-3.63376e-07,7.87163e-08,0.815745,0.000561627,-1.27227e-07,-8.40616e-08,0.816306,0.000561121,-3.79412e-07,7.87163e-08,0.816867,0.000560598,-1.43263e-07,-5.19898e-08,0.817428,0.000560156,-2.99233e-07,1.00335e-08,0.817988,0.000559587,-2.69132e-07,1.18559e-08,0.818547,0.000559085,-2.33564e-07,2.14764e-09,0.819106,0.000558624,-2.27122e-07,-2.04464e-08,0.819664,0.000558108,-2.88461e-07,2.00334e-08,0.820222,0.000557591,-2.28361e-07,-8.24277e-11,0.820779,0.000557135,-2.28608e-07,-1.97037e-08,0.821336,0.000556618,-2.87719e-07,1.92925e-08,0.821893,0.000556101,-2.29841e-07,2.13831e-09,0.822448,0.000555647,-2.23427e-07,-2.78458e-08,0.823004,0.000555117,-3.06964e-07,4.96402e-08,0.823559,0.000554652,-1.58043e-07,-5.15058e-08,0.824113,0.000554181,-3.12561e-07,3.71737e-08,0.824667,0.000553668,-2.0104e-07,-3.75844e-08,0.82522,0.000553153,-3.13793e-07,5.35592e-08,0.825773,0.000552686,-1.53115e-07,-5.74431e-08,0.826326,0.000552207,-3.25444e-07,5.7004e-08,0.826878,0.000551728,-1.54433e-07,-5.13635e-08,0.827429,0.000551265,-3.08523e-07,2.92406e-08,0.82798,0.000550735,-2.20801e-07,-5.99424e-09,0.828531,0.000550276,-2.38784e-07,-5.26363e-09,0.829081,0.000549782,-2.54575e-07,2.70488e-08,0.82963,0.000549354,-1.73429e-07,-4.33268e-08,0.83018,0.000548878,-3.03409e-07,2.7049e-08,0.830728,0.000548352,-2.22262e-07,-5.26461e-09,0.831276,0.000547892,-2.38056e-07,-5.99057e-09,0.831824,0.000547397,-2.56027e-07,2.92269e-08,0.832371,0.000546973,-1.68347e-07,-5.13125e-08,0.832918,0.000546482,-3.22284e-07,5.68139e-08,0.833464,0.000546008,-1.51843e-07,-5.67336e-08,0.83401,0.000545534,-3.22043e-07,5.09113e-08,0.834555,0.000545043,-1.6931e-07,-2.77022e-08,0.8351,0.000544621,-2.52416e-07,2.92924e-10,0.835644,0.000544117,-2.51537e-07,2.65305e-08,0.836188,0.000543694,-1.71946e-07,-4.68105e-08,0.836732,0.00054321,-3.12377e-07,4.15021e-08,0.837275,0.000542709,-1.87871e-07,1.13355e-11,0.837817,0.000542334,-1.87837e-07,-4.15474e-08,0.838359,0.000541833,-3.12479e-07,4.69691e-08,0.838901,0.000541349,-1.71572e-07,-2.71196e-08,0.839442,0.000540925,-2.52931e-07,1.90462e-09,0.839983,0.000540425,-2.47217e-07,1.95011e-08,0.840523,0.000539989,-1.88713e-07,-2.03045e-08,0.841063,0.00053955,-2.49627e-07,2.11216e-09,0.841602,0.000539057,-2.4329e-07,1.18558e-08,0.842141,0.000538606,-2.07723e-07,1.00691e-08,0.842679,0.000538221,-1.77516e-07,-5.21324e-08,0.843217,0.00053771,-3.33913e-07,7.92513e-08,0.843755,0.00053728,-9.6159e-08,-8.60587e-08,0.844292,0.000536829,-3.54335e-07,8.61696e-08,0.844828,0.000536379,-9.58263e-08,-7.98057e-08,0.845364,0.000535948,-3.35243e-07,5.42394e-08,0.8459,0.00053544,-1.72525e-07,-1.79426e-08,0.846435,0.000535041,-2.26353e-07,1.75308e-08,0.84697,0.000534641,-1.73761e-07,-5.21806e-08,0.847505,0.000534137,-3.30302e-07,7.19824e-08,0.848038,0.000533692,-1.14355e-07,-5.69349e-08,0.848572,0.000533293,-2.8516e-07,3.65479e-08,0.849105,0.000532832,-1.75516e-07,-2.96519e-08,0.849638,0.000532392,-2.64472e-07,2.2455e-08,0.85017,0.000531931,-1.97107e-07,-5.63451e-10,0.850702,0.000531535,-1.98797e-07,-2.02011e-08,0.851233,0.000531077,-2.59401e-07,2.17634e-08,0.851764,0.000530623,-1.94111e-07,-7.24794e-09,0.852294,0.000530213,-2.15854e-07,7.22832e-09,0.852824,0.000529803,-1.94169e-07,-2.16653e-08,0.853354,0.00052935,-2.59165e-07,1.98283e-08,0.853883,0.000528891,-1.9968e-07,1.95678e-09,0.854412,0.000528497,-1.9381e-07,-2.76554e-08,0.85494,0.000528027,-2.76776e-07,4.90603e-08,0.855468,0.00052762,-1.29596e-07,-4.93764e-08,0.855995,0.000527213,-2.77725e-07,2.92361e-08,0.856522,0.000526745,-1.90016e-07,-7.96341e-09,0.857049,0.000526341,-2.13907e-07,2.61752e-09,0.857575,0.000525922,-2.06054e-07,-2.50665e-09,0.8581,0.000525502,-2.13574e-07,7.40906e-09,0.858626,0.000525097,-1.91347e-07,-2.71296e-08,0.859151,0.000524633,-2.72736e-07,4.15048e-08,0.859675,0.000524212,-1.48221e-07,-1.96802e-08,0.860199,0.000523856,-2.07262e-07,-2.23886e-08,0.860723,0.000523375,-2.74428e-07,4.96299e-08,0.861246,0.000522975,-1.25538e-07,-5.69216e-08,0.861769,0.000522553,-2.96303e-07,5.88473e-08,0.862291,0.000522137,-1.19761e-07,-5.92584e-08,0.862813,0.00052172,-2.97536e-07,5.8977e-08,0.863334,0.000521301,-1.20605e-07,-5.74403e-08,0.863855,0.000520888,-2.92926e-07,5.15751e-08,0.864376,0.000520457,-1.38201e-07,-2.96506e-08,0.864896,0.000520091,-2.27153e-07,7.42277e-09,0.865416,0.000519659,-2.04885e-07,-4.05057e-11,0.865936,0.00051925,-2.05006e-07,-7.26074e-09,0.866455,0.000518818,-2.26788e-07,2.90835e-08,0.866973,0.000518451,-1.39538e-07,-4.94686e-08,0.867492,0.000518024,-2.87944e-07,4.95814e-08,0.868009,0.000517597,-1.39199e-07,-2.96479e-08,0.868527,0.000517229,-2.28143e-07,9.40539e-09,0.869044,0.000516801,-1.99927e-07,-7.9737e-09,0.86956,0.000516378,-2.23848e-07,2.24894e-08,0.870077,0.000515997,-1.5638e-07,-2.23793e-08,0.870592,0.000515617,-2.23517e-07,7.42302e-09,0.871108,0.000515193,-2.01248e-07,-7.31283e-09,0.871623,0.000514768,-2.23187e-07,2.18283e-08,0.872137,0.000514387,-1.57702e-07,-2.03959e-08,0.872652,0.000514011,-2.1889e-07,1.50711e-10,0.873165,0.000513573,-2.18437e-07,1.97931e-08,0.873679,0.000513196,-1.59058e-07,-1.97183e-08,0.874192,0.000512819,-2.18213e-07,-5.24324e-10,0.874704,0.000512381,-2.19786e-07,2.18156e-08,0.875217,0.000512007,-1.54339e-07,-2.71336e-08,0.875728,0.000511616,-2.3574e-07,2.71141e-08,0.87624,0.000511226,-1.54398e-07,-2.17182e-08,0.876751,0.000510852,-2.19552e-07,1.54131e-10,0.877262,0.000510414,-2.1909e-07,2.11017e-08,0.877772,0.000510039,-1.55785e-07,-2.49562e-08,0.878282,0.000509652,-2.30654e-07,1.91183e-08,0.878791,0.000509248,-1.73299e-07,8.08751e-09,0.8793,0.000508926,-1.49036e-07,-5.14684e-08,0.879809,0.000508474,-3.03441e-07,7.85766e-08,0.880317,0.000508103,-6.77112e-08,-8.40242e-08,0.880825,0.000507715,-3.19784e-07,7.87063e-08,0.881333,0.000507312,-8.36649e-08,-5.19871e-08,0.88184,0.000506988,-2.39626e-07,1.00327e-08,0.882346,0.000506539,-2.09528e-07,1.18562e-08,0.882853,0.000506156,-1.73959e-07,2.14703e-09,0.883359,0.000505814,-1.67518e-07,-2.04444e-08,0.883864,0.000505418,-2.28851e-07,2.00258e-08,0.88437,0.00050502,-1.68774e-07,-5.42855e-11,0.884874,0.000504682,-1.68937e-07,-1.98087e-08,0.885379,0.000504285,-2.28363e-07,1.96842e-08,0.885883,0.000503887,-1.6931e-07,6.76342e-10,0.886387,0.000503551,-1.67281e-07,-2.23896e-08,0.88689,0.000503149,-2.3445e-07,2.92774e-08,0.887393,0.000502768,-1.46618e-07,-3.51152e-08,0.887896,0.00050237,-2.51963e-07,5.15787e-08,0.888398,0.00050202,-9.72271e-08,-5.19903e-08,0.8889,0.00050167,-2.53198e-07,3.71732e-08,0.889401,0.000501275,-1.41678e-07,-3.70978e-08,0.889902,0.00050088,-2.52972e-07,5.16132e-08,0.890403,0.000500529,-9.81321e-08,-5.01459e-08,0.890903,0.000500183,-2.4857e-07,2.9761e-08,0.891403,0.000499775,-1.59287e-07,-9.29351e-09,0.891903,0.000499428,-1.87167e-07,7.41301e-09,0.892402,0.000499076,-1.64928e-07,-2.03585e-08,0.892901,0.000498685,-2.26004e-07,1.44165e-08,0.893399,0.000498276,-1.82754e-07,2.22974e-08,0.893898,0.000497978,-1.15862e-07,-4.40013e-08,0.894395,0.000497614,-2.47866e-07,3.44985e-08,0.894893,0.000497222,-1.44371e-07,-3.43882e-08,0.89539,0.00049683,-2.47535e-07,4.34497e-08,0.895886,0.000496465,-1.17186e-07,-2.02012e-08,0.896383,0.00049617,-1.7779e-07,-2.22497e-08,0.896879,0.000495748,-2.44539e-07,4.95952e-08,0.897374,0.000495408,-9.57532e-08,-5.69217e-08,0.89787,0.000495045,-2.66518e-07,5.88823e-08,0.898364,0.000494689,-8.98713e-08,-5.93983e-08,0.898859,0.000494331,-2.68066e-07,5.95017e-08,0.899353,0.000493973,-8.95613e-08,-5.9399e-08,0.899847,0.000493616,-2.67758e-07,5.8885e-08,0.90034,0.000493257,-9.11033e-08,-5.69317e-08,0.900833,0.000492904,-2.61898e-07,4.96326e-08,0.901326,0.000492529,-1.13001e-07,-2.23893e-08,0.901819,0.000492236,-1.80169e-07,-1.968e-08,0.902311,0.000491817,-2.39209e-07,4.15047e-08,0.902802,0.000491463,-1.14694e-07,-2.71296e-08,0.903293,0.000491152,-1.96083e-07,7.409e-09,0.903784,0.000490782,-1.73856e-07,-2.50645e-09,0.904275,0.000490427,-1.81376e-07,2.61679e-09,0.904765,0.000490072,-1.73525e-07,-7.96072e-09,0.905255,0.000489701,-1.97407e-07,2.92261e-08,0.905745,0.000489394,-1.09729e-07,-4.93389e-08,0.906234,0.000489027,-2.57746e-07,4.89204e-08,0.906723,0.000488658,-1.10985e-07,-2.71333e-08,0.907211,0.000488354,-1.92385e-07,8.30861e-12,0.907699,0.00048797,-1.9236e-07,2.71001e-08,0.908187,0.000487666,-1.1106e-07,-4.88041e-08,0.908675,0.000487298,-2.57472e-07,4.89069e-08,0.909162,0.000486929,-1.10751e-07,-2.76143e-08,0.909649,0.000486625,-1.93594e-07,1.9457e-09,0.910135,0.000486244,-1.87757e-07,1.98315e-08,0.910621,0.000485928,-1.28262e-07,-2.16671e-08,0.911107,0.000485606,-1.93264e-07,7.23216e-09,0.911592,0.000485241,-1.71567e-07,-7.26152e-09,0.912077,0.000484877,-1.93352e-07,2.18139e-08,0.912562,0.000484555,-1.2791e-07,-2.03895e-08,0.913047,0.000484238,-1.89078e-07,1.39494e-10,0.913531,0.000483861,-1.8866e-07,1.98315e-08,0.914014,0.000483543,-1.29165e-07,-1.98609e-08,0.914498,0.000483225,-1.88748e-07,7.39912e-12,0.914981,0.000482847,-1.88726e-07,1.98313e-08,0.915463,0.000482529,-1.29232e-07,-1.9728e-08,0.915946,0.000482212,-1.88416e-07,-5.24035e-10,0.916428,0.000481833,-1.89988e-07,2.18241e-08,0.916909,0.000481519,-1.24516e-07,-2.71679e-08,0.917391,0.000481188,-2.06019e-07,2.72427e-08,0.917872,0.000480858,-1.24291e-07,-2.21985e-08,0.918353,0.000480543,-1.90886e-07,1.94644e-09,0.918833,0.000480167,-1.85047e-07,1.44127e-08,0.919313,0.00047984,-1.41809e-07,7.39438e-12,0.919793,0.000479556,-1.41787e-07,-1.44423e-08,0.920272,0.000479229,-1.85114e-07,-1.84291e-09,0.920751,0.000478854,-1.90642e-07,2.18139e-08,0.92123,0.000478538,-1.25201e-07,-2.58081e-08,0.921708,0.00047821,-2.02625e-07,2.18139e-08,0.922186,0.00047787,-1.37183e-07,-1.84291e-09,0.922664,0.00047759,-1.42712e-07,-1.44423e-08,0.923141,0.000477262,-1.86039e-07,7.34701e-12,0.923618,0.00047689,-1.86017e-07,1.44129e-08,0.924095,0.000476561,-1.42778e-07,1.94572e-09,0.924572,0.000476281,-1.36941e-07,-2.21958e-08,0.925048,0.000475941,-2.03528e-07,2.72327e-08,0.925523,0.000475615,-1.2183e-07,-2.71304e-08,0.925999,0.00047529,-2.03221e-07,2.16843e-08,0.926474,0.000474949,-1.38168e-07,-2.16005e-12,0.926949,0.000474672,-1.38175e-07,-2.16756e-08,0.927423,0.000474331,-2.03202e-07,2.71001e-08,0.927897,0.000474006,-1.21902e-07,-2.71201e-08,0.928371,0.000473681,-2.03262e-07,2.17757e-08,0.928845,0.00047334,-1.37935e-07,-3.78028e-10,0.929318,0.000473063,-1.39069e-07,-2.02636e-08,0.929791,0.000472724,-1.9986e-07,2.18276e-08,0.930263,0.000472389,-1.34377e-07,-7.44231e-09,0.930736,0.000472098,-1.56704e-07,7.94165e-09,0.931208,0.000471809,-1.32879e-07,-2.43243e-08,0.931679,0.00047147,-2.05851e-07,2.97508e-08,0.932151,0.000471148,-1.16599e-07,-3.50742e-08,0.932622,0.000470809,-2.21822e-07,5.09414e-08,0.933092,0.000470518,-6.89976e-08,-4.94821e-08,0.933563,0.000470232,-2.17444e-07,2.77775e-08,0.934033,0.00046988,-1.34111e-07,-2.02351e-09,0.934502,0.000469606,-1.40182e-07,-1.96835e-08,0.934972,0.000469267,-1.99232e-07,2.11529e-08,0.935441,0.000468932,-1.35774e-07,-5.32332e-09,0.93591,0.000468644,-1.51743e-07,1.40413e-10,0.936378,0.000468341,-1.51322e-07,4.76166e-09,0.936846,0.000468053,-1.37037e-07,-1.9187e-08,0.937314,0.000467721,-1.94598e-07,1.23819e-08,0.937782,0.000467369,-1.57453e-07,2.92642e-08,0.938249,0.000467142,-6.96601e-08,-6.98342e-08,0.938716,0.000466793,-2.79163e-07,7.12586e-08,0.939183,0.000466449,-6.53869e-08,-3.63863e-08,0.939649,0.000466209,-1.74546e-07,1.46818e-08,0.940115,0.000465904,-1.305e-07,-2.2341e-08,0.940581,0.000465576,-1.97523e-07,1.50774e-08,0.941046,0.000465226,-1.52291e-07,2.16359e-08,0.941511,0.000464986,-8.73832e-08,-4.20162e-08,0.941976,0.000464685,-2.13432e-07,2.72198e-08,0.942441,0.00046434,-1.31773e-07,-7.2581e-09,0.942905,0.000464055,-1.53547e-07,1.81263e-09,0.943369,0.000463753,-1.48109e-07,7.58386e-12,0.943832,0.000463457,-1.48086e-07,-1.84298e-09,0.944296,0.000463155,-1.53615e-07,7.36433e-09,0.944759,0.00046287,-1.31522e-07,-2.76143e-08,0.945221,0.000462524,-2.14365e-07,4.34883e-08,0.945684,0.000462226,-8.39003e-08,-2.71297e-08,0.946146,0.000461977,-1.65289e-07,5.42595e-09,0.946608,0.000461662,-1.49012e-07,5.42593e-09,0.947069,0.000461381,-1.32734e-07,-2.71297e-08,0.94753,0.000461034,-2.14123e-07,4.34881e-08,0.947991,0.000460736,-8.36585e-08,-2.76134e-08,0.948452,0.000460486,-1.66499e-07,7.36083e-09,0.948912,0.000460175,-1.44416e-07,-1.82993e-09,0.949372,0.000459881,-1.49906e-07,-4.11073e-11,0.949832,0.000459581,-1.50029e-07,1.99434e-09,0.950291,0.000459287,-1.44046e-07,-7.93627e-09,0.950751,0.000458975,-1.67855e-07,2.97507e-08,0.951209,0.000458728,-7.86029e-08,-5.1462e-08,0.951668,0.000458417,-2.32989e-07,5.6888e-08,0.952126,0.000458121,-6.2325e-08,-5.68806e-08,0.952584,0.000457826,-2.32967e-07,5.14251e-08,0.953042,0.000457514,-7.86914e-08,-2.96107e-08,0.953499,0.000457268,-1.67523e-07,7.41296e-09,0.953956,0.000456955,-1.45285e-07,-4.11262e-11,0.954413,0.000456665,-1.45408e-07,-7.24847e-09,0.95487,0.000456352,-1.67153e-07,2.9035e-08,0.955326,0.000456105,-8.00484e-08,-4.92869e-08,0.955782,0.000455797,-2.27909e-07,4.89032e-08,0.956238,0.000455488,-8.11994e-08,-2.71166e-08,0.956693,0.000455244,-1.62549e-07,-4.13678e-11,0.957148,0.000454919,-1.62673e-07,2.72821e-08,0.957603,0.000454675,-8.0827e-08,-4.94824e-08,0.958057,0.000454365,-2.29274e-07,5.14382e-08,0.958512,0.000454061,-7.49597e-08,-3.7061e-08,0.958965,0.0004538,-1.86143e-07,3.72013e-08,0.959419,0.000453539,-7.45389e-08,-5.21396e-08,0.959873,0.000453234,-2.30958e-07,5.21476e-08,0.960326,0.000452928,-7.45146e-08,-3.72416e-08,0.960778,0.000452667,-1.8624e-07,3.72143e-08,0.961231,0.000452407,-7.45967e-08,-5.20109e-08,0.961683,0.000452101,-2.30629e-07,5.16199e-08,0.962135,0.000451795,-7.57696e-08,-3.52595e-08,0.962587,0.000451538,-1.81548e-07,2.98133e-08,0.963038,0.000451264,-9.2108e-08,-2.43892e-08,0.963489,0.000451007,-1.65276e-07,8.13892e-09,0.96394,0.000450701,-1.40859e-07,-8.16647e-09,0.964391,0.000450394,-1.65358e-07,2.45269e-08,0.964841,0.000450137,-9.17775e-08,-3.03367e-08,0.965291,0.000449863,-1.82787e-07,3.7215e-08,0.965741,0.000449609,-7.11424e-08,-5.89188e-08,0.96619,0.00044929,-2.47899e-07,7.92509e-08,0.966639,0.000449032,-1.01462e-08,-7.92707e-08,0.967088,0.000448773,-2.47958e-07,5.90181e-08,0.967537,0.000448455,-7.0904e-08,-3.75925e-08,0.967985,0.0004482,-1.83681e-07,3.17471e-08,0.968433,0.000447928,-8.84401e-08,-2.97913e-08,0.968881,0.000447662,-1.77814e-07,2.78133e-08,0.969329,0.000447389,-9.4374e-08,-2.18572e-08,0.969776,0.000447135,-1.59946e-07,1.10134e-11,0.970223,0.000446815,-1.59913e-07,2.18132e-08,0.97067,0.000446561,-9.44732e-08,-2.76591e-08,0.971116,0.000446289,-1.7745e-07,2.92185e-08,0.971562,0.000446022,-8.97948e-08,-2.96104e-08,0.972008,0.000445753,-1.78626e-07,2.96185e-08,0.972454,0.000445485,-8.97706e-08,-2.92588e-08,0.972899,0.000445218,-1.77547e-07,2.78123e-08,0.973344,0.000444946,-9.41103e-08,-2.23856e-08,0.973789,0.000444691,-1.61267e-07,2.12559e-09,0.974233,0.000444374,-1.5489e-07,1.38833e-08,0.974678,0.000444106,-1.13241e-07,1.94591e-09,0.975122,0.000443886,-1.07403e-07,-2.16669e-08,0.975565,0.000443606,-1.72404e-07,2.5117e-08,0.976009,0.000443336,-9.70526e-08,-1.91963e-08,0.976452,0.000443085,-1.54642e-07,-7.93627e-09,0.976895,0.000442752,-1.7845e-07,5.09414e-08,0.977338,0.000442548,-2.56262e-08,-7.66201e-08,0.97778,0.000442266,-2.55486e-07,7.67249e-08,0.978222,0.000441986,-2.53118e-08,-5.14655e-08,0.978664,0.000441781,-1.79708e-07,9.92773e-09,0.979106,0.000441451,-1.49925e-07,1.17546e-08,0.979547,0.000441186,-1.14661e-07,2.65868e-09,0.979988,0.000440965,-1.06685e-07,-2.23893e-08,0.980429,0.000440684,-1.73853e-07,2.72939e-08,0.980869,0.000440419,-9.19716e-08,-2.71816e-08,0.98131,0.000440153,-1.73516e-07,2.18278e-08,0.98175,0.000439872,-1.08033e-07,-5.24833e-10,0.982189,0.000439654,-1.09607e-07,-1.97284e-08,0.982629,0.000439376,-1.68793e-07,1.98339e-08,0.983068,0.000439097,-1.09291e-07,-2.62901e-12,0.983507,0.000438879,-1.09299e-07,-1.98234e-08,0.983946,0.000438601,-1.68769e-07,1.96916e-08,0.984384,0.000438322,-1.09694e-07,6.6157e-10,0.984823,0.000438105,-1.0771e-07,-2.23379e-08,0.985261,0.000437823,-1.74723e-07,2.90855e-08,0.985698,0.00043756,-8.74669e-08,-3.43992e-08,0.986136,0.000437282,-1.90665e-07,4.89068e-08,0.986573,0.000437048,-4.39442e-08,-4.20188e-08,0.98701,0.000436834,-1.7e-07,-4.11073e-11,0.987446,0.000436494,-1.70124e-07,4.21832e-08,0.987883,0.00043628,-4.35742e-08,-4.94824e-08,0.988319,0.000436044,-1.92021e-07,3.6537e-08,0.988755,0.00043577,-8.24102e-08,-3.70611e-08,0.989191,0.000435494,-1.93593e-07,5.21026e-08,0.989626,0.000435263,-3.72855e-08,-5.21402e-08,0.990061,0.000435032,-1.93706e-07,3.7249e-08,0.990496,0.000434756,-8.19592e-08,-3.72512e-08,0.990931,0.000434481,-1.93713e-07,5.21511e-08,0.991365,0.00043425,-3.72595e-08,-5.21439e-08,0.991799,0.000434019,-1.93691e-07,3.72152e-08,0.992233,0.000433743,-8.20456e-08,-3.71123e-08,0.992667,0.000433468,-1.93382e-07,5.16292e-08,0.9931,0.000433236,-3.84947e-08,-5.01953e-08,0.993533,0.000433008,-1.89081e-07,2.99427e-08,0.993966,0.00043272,-9.92525e-08,-9.9708e-09,0.994399,0.000432491,-1.29165e-07,9.94051e-09,0.994831,0.000432263,-9.93434e-08,-2.97912e-08,0.995263,0.000431975,-1.88717e-07,4.96198e-08,0.995695,0.000431746,-3.98578e-08,-4.94785e-08,0.996127,0.000431518,-1.88293e-07,2.9085e-08,0.996558,0.000431229,-1.01038e-07,-7.25675e-09,0.996989,0.000431005,-1.22809e-07,-5.79945e-11,0.99742,0.000430759,-1.22983e-07,7.48873e-09,0.997851,0.000430536,-1.00516e-07,-2.98969e-08,0.998281,0.000430245,-1.90207e-07,5.24942e-08,0.998711,0.000430022,-3.27246e-08,-6.08706e-08,0.999141,0.000429774,-2.15336e-07,7.17788e-08,0.999571,0.000429392,0.,0.}; + + template + __device__ __forceinline__ void Lab2RGBConvert_f(const T& src, D& dst) + { + const float lThresh = 0.008856f * 903.3f; + const float fThresh = 7.787f * 0.008856f + 16.0f / 116.0f; + + float Y, fy; + + if (src.x <= lThresh) + { + Y = src.x / 903.3f; + fy = 7.787f * Y + 16.0f / 116.0f; + } + else + { + fy = (src.x + 16.0f) / 116.0f; + Y = fy * fy * fy; + } + + float X = src.y / 500.0f + fy; + float Z = fy - src.z / 200.0f; + + if (X <= fThresh) + X = (X - 16.0f / 116.0f) / 7.787f; + else + X = X * X * X; + + if (Z <= fThresh) + Z = (Z - 16.0f / 116.0f) / 7.787f; + else + Z = Z * Z * Z; + + float B = 0.052891f * X - 0.204043f * Y + 1.151152f * Z; + float G = -0.921235f * X + 1.875991f * Y + 0.045244f * Z; + float R = 3.079933f * X - 1.537150f * Y - 0.542782f * Z; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + } + + dst.x = blueIdx == 0 ? B : R; + dst.y = G; + dst.z = blueIdx == 0 ? R : B; + setAlpha(dst, ColorChannel::max()); + } + + template + __device__ __forceinline__ void Lab2RGBConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (100.f / 255.f); + srcf.y = src.y - 128; + srcf.z = src.z - 128; + + Lab2RGBConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 255.f); + dst.y = saturate_cast(dstf.y * 255.f); + dst.z = saturate_cast(dstf.z * 255.f); + setAlpha(dst, ColorChannel::max()); + } + + template struct Lab2RGB; + template + struct Lab2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Lab2RGBConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Lab2RGB() {} + __host__ __device__ __forceinline__ Lab2RGB(const Lab2RGB&) {} + }; + template + struct Lab2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Lab2RGBConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Lab2RGB() {} + __host__ __device__ __forceinline__ Lab2RGB(const Lab2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Lab2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> Luv ///////////////////////////////////// + + namespace color_detail + { + __constant__ float c_LabCbrtTab[] = {0.137931,0.0114066,0.,1.18859e-07,0.149338,0.011407,3.56578e-07,-5.79396e-07,0.160745,0.0114059,-1.38161e-06,2.16892e-06,0.172151,0.0114097,5.12516e-06,-8.0814e-06,0.183558,0.0113957,-1.9119e-05,3.01567e-05,0.194965,0.0114479,7.13509e-05,-0.000112545,0.206371,0.011253,-0.000266285,-0.000106493,0.217252,0.0104009,-0.000585765,7.32149e-05,0.22714,0.00944906,-0.00036612,1.21917e-05,0.236235,0.0087534,-0.000329545,2.01753e-05,0.244679,0.00815483,-0.000269019,1.24435e-05,0.252577,0.00765412,-0.000231689,1.05618e-05,0.26001,0.00722243,-0.000200003,8.26662e-06,0.267041,0.00684723,-0.000175203,6.76746e-06,0.27372,0.00651712,-0.000154901,5.61192e-06,0.280088,0.00622416,-0.000138065,4.67009e-06,0.286179,0.00596204,-0.000124055,3.99012e-06,0.292021,0.0057259,-0.000112085,3.36032e-06,0.297638,0.00551181,-0.000102004,2.95338e-06,0.30305,0.00531666,-9.31435e-05,2.52875e-06,0.308277,0.00513796,-8.55572e-05,2.22022e-06,0.313331,0.00497351,-7.88966e-05,1.97163e-06,0.318228,0.00482163,-7.29817e-05,1.7248e-06,0.322978,0.00468084,-6.78073e-05,1.55998e-06,0.327593,0.0045499,-6.31274e-05,1.36343e-06,0.332081,0.00442774,-5.90371e-05,1.27136e-06,0.336451,0.00431348,-5.5223e-05,1.09111e-06,0.34071,0.00420631,-5.19496e-05,1.0399e-06,0.344866,0.00410553,-4.88299e-05,9.18347e-07,0.348923,0.00401062,-4.60749e-05,8.29942e-07,0.352889,0.00392096,-4.35851e-05,7.98478e-07,0.356767,0.00383619,-4.11896e-05,6.84917e-07,0.360562,0.00375586,-3.91349e-05,6.63976e-07,0.36428,0.00367959,-3.7143e-05,5.93086e-07,0.367923,0.00360708,-3.53637e-05,5.6976e-07,0.371495,0.00353806,-3.36544e-05,4.95533e-07,0.375,0.00347224,-3.21678e-05,4.87951e-07,0.378441,0.00340937,-3.0704e-05,4.4349e-07,0.38182,0.00334929,-2.93735e-05,4.20297e-07,0.38514,0.0032918,-2.81126e-05,3.7872e-07,0.388404,0.00323671,-2.69764e-05,3.596e-07,0.391614,0.00318384,-2.58976e-05,3.5845e-07,0.394772,0.00313312,-2.48223e-05,2.92765e-07,0.397881,0.00308435,-2.3944e-05,3.18232e-07,0.400942,0.00303742,-2.29893e-05,2.82046e-07,0.403957,0.00299229,-2.21432e-05,2.52315e-07,0.406927,0.00294876,-2.13862e-05,2.58416e-07,0.409855,0.00290676,-2.0611e-05,2.33939e-07,0.412741,0.00286624,-1.99092e-05,2.36342e-07,0.415587,0.00282713,-1.92001e-05,1.916e-07,0.418396,0.00278931,-1.86253e-05,2.1915e-07,0.421167,0.00275271,-1.79679e-05,1.83498e-07,0.423901,0.00271733,-1.74174e-05,1.79343e-07,0.426602,0.00268303,-1.68794e-05,1.72013e-07,0.429268,0.00264979,-1.63633e-05,1.75686e-07,0.431901,0.00261759,-1.58363e-05,1.3852e-07,0.434503,0.00258633,-1.54207e-05,1.64304e-07,0.437074,0.00255598,-1.49278e-05,1.28136e-07,0.439616,0.00252651,-1.45434e-05,1.57618e-07,0.442128,0.0024979,-1.40705e-05,1.0566e-07,0.444612,0.00247007,-1.37535e-05,1.34998e-07,0.447068,0.00244297,-1.33485e-05,1.29207e-07,0.449498,0.00241666,-1.29609e-05,9.32347e-08,0.451902,0.00239102,-1.26812e-05,1.23703e-07,0.45428,0.00236603,-1.23101e-05,9.74072e-08,0.456634,0.0023417,-1.20179e-05,1.12518e-07,0.458964,0.002318,-1.16803e-05,7.83681e-08,0.46127,0.00229488,-1.14452e-05,1.10452e-07,0.463554,0.00227232,-1.11139e-05,7.58719e-08,0.465815,0.00225032,-1.08863e-05,9.2699e-08,0.468055,0.00222882,-1.06082e-05,8.97738e-08,0.470273,0.00220788,-1.03388e-05,5.4845e-08,0.47247,0.00218736,-1.01743e-05,1.0808e-07,0.474648,0.00216734,-9.85007e-06,4.9277e-08,0.476805,0.00214779,-9.70224e-06,8.22408e-08,0.478943,0.00212863,-9.45551e-06,6.87942e-08,0.481063,0.00210993,-9.24913e-06,5.98144e-08,0.483163,0.00209161,-9.06969e-06,7.93789e-08,0.485246,0.00207371,-8.83155e-06,3.99032e-08,0.487311,0.00205616,-8.71184e-06,8.88325e-08,0.489358,0.002039,-8.44534e-06,2.20004e-08,0.491389,0.00202218,-8.37934e-06,9.13872e-08,0.493403,0.0020057,-8.10518e-06,2.96829e-08,0.495401,0.00198957,-8.01613e-06,5.81028e-08,0.497382,0.00197372,-7.84183e-06,6.5731e-08,0.499348,0.00195823,-7.64463e-06,3.66019e-08,0.501299,0.00194305,-7.53483e-06,2.62811e-08,0.503234,0.00192806,-7.45598e-06,9.66907e-08,0.505155,0.00191344,-7.16591e-06,4.18928e-09,0.507061,0.00189912,-7.15334e-06,6.53665e-08,0.508953,0.00188501,-6.95724e-06,3.23686e-08,0.510831,0.00187119,-6.86014e-06,4.35774e-08,0.512696,0.0018576,-6.72941e-06,3.17406e-08,0.514547,0.00184424,-6.63418e-06,6.78785e-08,0.516384,0.00183117,-6.43055e-06,-5.23126e-09,0.518209,0.0018183,-6.44624e-06,7.22562e-08,0.520021,0.00180562,-6.22947e-06,1.42292e-08,0.52182,0.0017932,-6.18679e-06,4.9641e-08,0.523607,0.00178098,-6.03786e-06,2.56259e-08,0.525382,0.00176898,-5.96099e-06,2.66696e-08,0.527145,0.00175714,-5.88098e-06,4.65094e-08,0.528897,0.00174552,-5.74145e-06,2.57114e-08,0.530637,0.00173411,-5.66431e-06,2.94588e-08,0.532365,0.00172287,-5.57594e-06,3.52667e-08,0.534082,0.00171182,-5.47014e-06,8.28868e-09,0.535789,0.00170091,-5.44527e-06,5.07871e-08,0.537484,0.00169017,-5.29291e-06,2.69817e-08,0.539169,0.00167967,-5.21197e-06,2.01009e-08,0.540844,0.0016693,-5.15166e-06,1.18237e-08,0.542508,0.00165903,-5.11619e-06,5.18135e-08,0.544162,0.00164896,-4.96075e-06,1.9341e-08,0.545806,0.00163909,-4.90273e-06,-9.96867e-09,0.54744,0.00162926,-4.93263e-06,8.01382e-08,0.549064,0.00161963,-4.69222e-06,-1.25601e-08,0.550679,0.00161021,-4.7299e-06,2.97067e-08,0.552285,0.00160084,-4.64078e-06,1.29426e-08,0.553881,0.0015916,-4.60195e-06,3.77327e-08,0.555468,0.00158251,-4.48875e-06,1.49412e-08,0.557046,0.00157357,-4.44393e-06,2.17118e-08,0.558615,0.00156475,-4.3788e-06,1.74206e-08,0.560176,0.00155605,-4.32653e-06,2.78152e-08,0.561727,0.00154748,-4.24309e-06,-9.47239e-09,0.563271,0.00153896,-4.27151e-06,6.9679e-08,0.564805,0.00153063,-4.06247e-06,-3.08246e-08,0.566332,0.00152241,-4.15494e-06,5.36188e-08,0.56785,0.00151426,-3.99409e-06,-4.83594e-09,0.56936,0.00150626,-4.00859e-06,2.53293e-08,0.570863,0.00149832,-3.93261e-06,2.27286e-08,0.572357,0.00149052,-3.86442e-06,2.96541e-09,0.573844,0.0014828,-3.85552e-06,2.50147e-08,0.575323,0.00147516,-3.78048e-06,1.61842e-08,0.576794,0.00146765,-3.73193e-06,2.94582e-08,0.578258,0.00146028,-3.64355e-06,-1.48076e-08,0.579715,0.00145295,-3.68798e-06,2.97724e-08,0.581164,0.00144566,-3.59866e-06,1.49272e-08,0.582606,0.00143851,-3.55388e-06,2.97285e-08,0.584041,0.00143149,-3.46469e-06,-1.46323e-08,0.585469,0.00142451,-3.50859e-06,2.88004e-08,0.58689,0.00141758,-3.42219e-06,1.864e-08,0.588304,0.00141079,-3.36627e-06,1.58482e-08,0.589712,0.00140411,-3.31872e-06,-2.24279e-08,0.591112,0.00139741,-3.38601e-06,7.38639e-08,0.592507,0.00139085,-3.16441e-06,-3.46088e-08,0.593894,0.00138442,-3.26824e-06,4.96675e-09,0.595275,0.0013779,-3.25334e-06,7.4346e-08,0.59665,0.00137162,-3.0303e-06,-6.39319e-08,0.598019,0.00136536,-3.2221e-06,6.21725e-08,0.599381,0.00135911,-3.03558e-06,-5.94423e-09,0.600737,0.00135302,-3.05341e-06,2.12091e-08,0.602087,0.00134697,-2.98979e-06,-1.92876e-08,0.603431,0.00134094,-3.04765e-06,5.5941e-08,0.604769,0.00133501,-2.87983e-06,-2.56622e-08,0.606101,0.00132917,-2.95681e-06,4.67078e-08,0.607427,0.0013234,-2.81669e-06,-4.19592e-08,0.608748,0.00131764,-2.94257e-06,6.15243e-08,0.610062,0.00131194,-2.75799e-06,-2.53244e-08,0.611372,0.00130635,-2.83397e-06,3.97739e-08,0.612675,0.0013008,-2.71465e-06,-1.45618e-08,0.613973,0.00129533,-2.75833e-06,1.84733e-08,0.615266,0.00128986,-2.70291e-06,2.73606e-10,0.616553,0.00128446,-2.70209e-06,4.00367e-08,0.617835,0.00127918,-2.58198e-06,-4.12113e-08,0.619111,0.00127389,-2.70561e-06,6.52039e-08,0.620383,0.00126867,-2.51e-06,-4.07901e-08,0.621649,0.00126353,-2.63237e-06,3.83516e-08,0.62291,0.00125838,-2.51732e-06,6.59315e-09,0.624166,0.00125337,-2.49754e-06,-5.11939e-09,0.625416,0.00124836,-2.5129e-06,1.38846e-08,0.626662,0.00124337,-2.47124e-06,9.18514e-09,0.627903,0.00123846,-2.44369e-06,8.97952e-09,0.629139,0.0012336,-2.41675e-06,1.45012e-08,0.63037,0.00122881,-2.37325e-06,-7.37949e-09,0.631597,0.00122404,-2.39538e-06,1.50169e-08,0.632818,0.00121929,-2.35033e-06,6.91648e-09,0.634035,0.00121461,-2.32958e-06,1.69219e-08,0.635248,0.00121,-2.27882e-06,-1.49997e-08,0.636455,0.0012054,-2.32382e-06,4.30769e-08,0.637659,0.00120088,-2.19459e-06,-3.80986e-08,0.638857,0.00119638,-2.30888e-06,4.97134e-08,0.640051,0.00119191,-2.15974e-06,-4.15463e-08,0.641241,0.00118747,-2.28438e-06,5.68667e-08,0.642426,0.00118307,-2.11378e-06,-7.10641e-09,0.643607,0.00117882,-2.1351e-06,-2.8441e-08,0.644784,0.00117446,-2.22042e-06,6.12658e-08,0.645956,0.00117021,-2.03663e-06,-3.78083e-08,0.647124,0.00116602,-2.15005e-06,3.03627e-08,0.648288,0.00116181,-2.05896e-06,-2.40379e-08,0.649448,0.00115762,-2.13108e-06,6.57887e-08,0.650603,0.00115356,-1.93371e-06,-6.03028e-08,0.651755,0.00114951,-2.11462e-06,5.62134e-08,0.652902,0.00114545,-1.94598e-06,-4.53417e-08,0.654046,0.00114142,-2.082e-06,6.55489e-08,0.655185,0.00113745,-1.88536e-06,-3.80396e-08,0.656321,0.00113357,-1.99948e-06,2.70049e-08,0.657452,0.00112965,-1.91846e-06,-1.03755e-08,0.65858,0.00112578,-1.94959e-06,1.44973e-08,0.659704,0.00112192,-1.9061e-06,1.1991e-08,0.660824,0.00111815,-1.87012e-06,-2.85634e-09,0.66194,0.0011144,-1.87869e-06,-5.65782e-10,0.663053,0.00111064,-1.88039e-06,5.11947e-09,0.664162,0.0011069,-1.86503e-06,3.96924e-08,0.665267,0.00110328,-1.74595e-06,-4.46795e-08,0.666368,0.00109966,-1.87999e-06,1.98161e-08,0.667466,0.00109596,-1.82054e-06,2.502e-08,0.66856,0.00109239,-1.74548e-06,-6.86593e-10,0.669651,0.0010889,-1.74754e-06,-2.22739e-08,0.670738,0.00108534,-1.81437e-06,3.01776e-08,0.671821,0.0010818,-1.72383e-06,2.07732e-08,0.672902,0.00107841,-1.66151e-06,-5.36658e-08,0.673978,0.00107493,-1.82251e-06,7.46802e-08,0.675051,0.00107151,-1.59847e-06,-6.62411e-08,0.676121,0.00106811,-1.79719e-06,7.10748e-08,0.677188,0.00106473,-1.58397e-06,-3.92441e-08,0.678251,0.00106145,-1.7017e-06,2.62973e-08,0.679311,0.00105812,-1.62281e-06,-6.34035e-09,0.680367,0.00105486,-1.64183e-06,-9.36249e-10,0.68142,0.00105157,-1.64464e-06,1.00854e-08,0.68247,0.00104831,-1.61438e-06,2.01995e-08,0.683517,0.00104514,-1.55378e-06,-3.1279e-08,0.68456,0.00104194,-1.64762e-06,4.53114e-08,0.685601,0.00103878,-1.51169e-06,-3.07573e-08,0.686638,0.00103567,-1.60396e-06,1.81133e-08,0.687672,0.00103251,-1.54962e-06,1.79085e-08,0.688703,0.00102947,-1.49589e-06,-3.01428e-08,0.689731,0.00102639,-1.58632e-06,4.30583e-08,0.690756,0.00102334,-1.45715e-06,-2.28814e-08,0.691778,0.00102036,-1.52579e-06,-1.11373e-08,0.692797,0.00101727,-1.5592e-06,6.74305e-08,0.693812,0.00101436,-1.35691e-06,-7.97709e-08,0.694825,0.0010114,-1.59622e-06,7.28391e-08,0.695835,0.00100843,-1.37771e-06,-3.27715e-08,0.696842,0.00100558,-1.47602e-06,-1.35807e-09,0.697846,0.00100262,-1.48009e-06,3.82037e-08,0.698847,0.000999775,-1.36548e-06,-3.22474e-08,0.699846,0.000996948,-1.46223e-06,3.11809e-08,0.700841,0.000994117,-1.36868e-06,-3.28714e-08,0.701834,0.000991281,-1.4673e-06,4.07001e-08,0.702824,0.000988468,-1.3452e-06,-1.07197e-08,0.703811,0.000985746,-1.37736e-06,2.17866e-09,0.704795,0.000982998,-1.37082e-06,2.00521e-09,0.705777,0.000980262,-1.3648e-06,-1.01996e-08,0.706756,0.000977502,-1.3954e-06,3.87931e-08,0.707732,0.000974827,-1.27902e-06,-2.57632e-08,0.708706,0.000972192,-1.35631e-06,4.65513e-09,0.709676,0.000969493,-1.34235e-06,7.14257e-09,0.710645,0.00096683,-1.32092e-06,2.63791e-08,0.71161,0.000964267,-1.24178e-06,-5.30543e-08,0.712573,0.000961625,-1.40095e-06,6.66289e-08,0.713533,0.000959023,-1.20106e-06,-3.46474e-08,0.714491,0.000956517,-1.305e-06,1.23559e-08,0.715446,0.000953944,-1.26793e-06,-1.47763e-08,0.716399,0.000951364,-1.31226e-06,4.67494e-08,0.717349,0.000948879,-1.17201e-06,-5.3012e-08,0.718297,0.000946376,-1.33105e-06,4.60894e-08,0.719242,0.000943852,-1.19278e-06,-1.21366e-08,0.720185,0.00094143,-1.22919e-06,2.45673e-09,0.721125,0.000938979,-1.22182e-06,2.30966e-09,0.722063,0.000936543,-1.21489e-06,-1.16954e-08,0.722998,0.000934078,-1.24998e-06,4.44718e-08,0.723931,0.000931711,-1.11656e-06,-4.69823e-08,0.724861,0.000929337,-1.25751e-06,2.4248e-08,0.725789,0.000926895,-1.18477e-06,9.5949e-09,0.726715,0.000924554,-1.15598e-06,-3.02286e-09,0.727638,0.000922233,-1.16505e-06,2.49649e-09,0.72856,0.00091991,-1.15756e-06,-6.96321e-09,0.729478,0.000917575,-1.17845e-06,2.53564e-08,0.730395,0.000915294,-1.10238e-06,-3.48578e-08,0.731309,0.000912984,-1.20695e-06,5.44704e-08,0.732221,0.000910734,-1.04354e-06,-6.38144e-08,0.73313,0.000908455,-1.23499e-06,8.15781e-08,0.734038,0.00090623,-9.90253e-07,-8.3684e-08,0.734943,0.000903999,-1.2413e-06,7.43441e-08,0.735846,0.000901739,-1.01827e-06,-3.48787e-08,0.736746,0.000899598,-1.12291e-06,5.56596e-09,0.737645,0.000897369,-1.10621e-06,1.26148e-08,0.738541,0.000895194,-1.06837e-06,3.57935e-09,0.739435,0.000893068,-1.05763e-06,-2.69322e-08,0.740327,0.000890872,-1.13842e-06,4.45448e-08,0.741217,0.000888729,-1.00479e-06,-3.20376e-08,0.742105,0.000886623,-1.1009e-06,2.40011e-08,0.74299,0.000884493,-1.0289e-06,-4.36209e-09,0.743874,0.000882422,-1.04199e-06,-6.55268e-09,0.744755,0.000880319,-1.06164e-06,3.05728e-08,0.745634,0.000878287,-9.69926e-07,-5.61338e-08,0.746512,0.000876179,-1.13833e-06,7.4753e-08,0.747387,0.000874127,-9.14068e-07,-6.40644e-08,0.74826,0.000872106,-1.10626e-06,6.22955e-08,0.749131,0.000870081,-9.19375e-07,-6.59083e-08,0.75,0.000868044,-1.1171e-06,8.21284e-08,0.750867,0.000866056,-8.70714e-07,-8.37915e-08,0.751732,0.000864064,-1.12209e-06,7.42237e-08,0.752595,0.000862042,-8.99418e-07,-3.42894e-08,0.753456,0.00086014,-1.00229e-06,3.32955e-09,0.754315,0.000858146,-9.92297e-07,2.09712e-08,0.755173,0.000856224,-9.29384e-07,-2.76096e-08,0.756028,0.000854282,-1.01221e-06,2.98627e-08,0.756881,0.000852348,-9.22625e-07,-3.22365e-08,0.757733,0.000850406,-1.01933e-06,3.94786e-08,0.758582,0.000848485,-9.00898e-07,-6.46833e-09,0.75943,0.000846664,-9.20303e-07,-1.36052e-08,0.760275,0.000844783,-9.61119e-07,1.28447e-09,0.761119,0.000842864,-9.57266e-07,8.4674e-09,0.761961,0.000840975,-9.31864e-07,2.44506e-08,0.762801,0.000839185,-8.58512e-07,-4.6665e-08,0.763639,0.000837328,-9.98507e-07,4.30001e-08,0.764476,0.00083546,-8.69507e-07,-6.12609e-09,0.76531,0.000833703,-8.87885e-07,-1.84959e-08,0.766143,0.000831871,-9.43372e-07,2.05052e-08,0.766974,0.000830046,-8.81857e-07,-3.92026e-09,0.767803,0.000828271,-8.93618e-07,-4.82426e-09,0.768631,0.000826469,-9.0809e-07,2.32172e-08,0.769456,0.000824722,-8.38439e-07,-2.84401e-08,0.77028,0.00082296,-9.23759e-07,3.09386e-08,0.771102,0.000821205,-8.30943e-07,-3.57099e-08,0.771922,0.000819436,-9.38073e-07,5.22963e-08,0.772741,0.000817717,-7.81184e-07,-5.42658e-08,0.773558,0.000815992,-9.43981e-07,4.55579e-08,0.774373,0.000814241,-8.07308e-07,-8.75656e-09,0.775186,0.0008126,-8.33578e-07,-1.05315e-08,0.775998,0.000810901,-8.65172e-07,-8.72188e-09,0.776808,0.000809145,-8.91338e-07,4.54191e-08,0.777616,0.000807498,-7.5508e-07,-5.37454e-08,0.778423,0.000805827,-9.16317e-07,5.03532e-08,0.779228,0.000804145,-7.65257e-07,-2.84584e-08,0.780031,0.000802529,-8.50632e-07,3.87579e-09,0.780833,0.00080084,-8.39005e-07,1.29552e-08,0.781633,0.0007992,-8.00139e-07,3.90804e-09,0.782432,0.000797612,-7.88415e-07,-2.85874e-08,0.783228,0.000795949,-8.74177e-07,5.0837e-08,0.784023,0.000794353,-7.21666e-07,-5.55513e-08,0.784817,0.000792743,-8.8832e-07,5.21587e-08,0.785609,0.000791123,-7.31844e-07,-3.38744e-08,0.786399,0.000789558,-8.33467e-07,2.37342e-08,0.787188,0.000787962,-7.62264e-07,-1.45775e-09,0.787975,0.000786433,-7.66638e-07,-1.79034e-08,0.788761,0.000784846,-8.20348e-07,1.34665e-08,0.789545,0.000783246,-7.79948e-07,2.3642e-08,0.790327,0.000781757,-7.09022e-07,-4.84297e-08,0.791108,0.000780194,-8.54311e-07,5.08674e-08,0.791888,0.000778638,-7.01709e-07,-3.58303e-08,0.792666,0.000777127,-8.092e-07,3.28493e-08,0.793442,0.000775607,-7.10652e-07,-3.59624e-08,0.794217,0.000774078,-8.1854e-07,5.13959e-08,0.79499,0.000772595,-6.64352e-07,-5.04121e-08,0.795762,0.000771115,-8.15588e-07,3.10431e-08,0.796532,0.000769577,-7.22459e-07,-1.41557e-08,0.797301,0.00076809,-7.64926e-07,2.55795e-08,0.798069,0.000766636,-6.88187e-07,-2.85578e-08,0.798835,0.000765174,-7.73861e-07,2.90472e-08,0.799599,0.000763714,-6.86719e-07,-2.80262e-08,0.800362,0.000762256,-7.70798e-07,2.34531e-08,0.801123,0.000760785,-7.00438e-07,-6.18144e-09,0.801884,0.000759366,-7.18983e-07,1.27263e-09,0.802642,0.000757931,-7.15165e-07,1.09101e-09,0.803399,0.000756504,-7.11892e-07,-5.63675e-09,0.804155,0.000755064,-7.28802e-07,2.14559e-08,0.80491,0.00075367,-6.64434e-07,-2.05821e-08,0.805663,0.00075228,-7.26181e-07,1.26812e-09,0.806414,0.000750831,-7.22377e-07,1.55097e-08,0.807164,0.000749433,-6.75848e-07,-3.70216e-09,0.807913,0.00074807,-6.86954e-07,-7.0105e-10,0.80866,0.000746694,-6.89057e-07,6.5063e-09,0.809406,0.000745336,-6.69538e-07,-2.53242e-08,0.810151,0.000743921,-7.45511e-07,3.51858e-08,0.810894,0.000742535,-6.39953e-07,3.79034e-09,0.811636,0.000741267,-6.28582e-07,-5.03471e-08,0.812377,0.000739858,-7.79624e-07,7.83886e-08,0.813116,0.000738534,-5.44458e-07,-8.43935e-08,0.813854,0.000737192,-7.97638e-07,8.03714e-08,0.81459,0.000735838,-5.56524e-07,-5.82784e-08,0.815325,0.00073455,-7.31359e-07,3.35329e-08,0.816059,0.000733188,-6.3076e-07,-1.62486e-08,0.816792,0.000731878,-6.79506e-07,3.14614e-08,0.817523,0.000730613,-5.85122e-07,-4.99925e-08,0.818253,0.000729293,-7.35099e-07,4.92994e-08,0.818982,0.000727971,-5.87201e-07,-2.79959e-08,0.819709,0.000726712,-6.71189e-07,3.07959e-09,0.820435,0.000725379,-6.6195e-07,1.56777e-08,0.82116,0.000724102,-6.14917e-07,-6.18564e-09,0.821883,0.000722854,-6.33474e-07,9.06488e-09,0.822606,0.000721614,-6.06279e-07,-3.00739e-08,0.823327,0.000720311,-6.96501e-07,5.16262e-08,0.824046,0.000719073,-5.41623e-07,-5.72214e-08,0.824765,0.000717818,-7.13287e-07,5.80503e-08,0.825482,0.000716566,-5.39136e-07,-5.57703e-08,0.826198,0.00071532,-7.06447e-07,4.58215e-08,0.826912,0.000714045,-5.68983e-07,-8.30636e-09,0.827626,0.000712882,-5.93902e-07,-1.25961e-08,0.828338,0.000711656,-6.3169e-07,-9.13985e-10,0.829049,0.00071039,-6.34432e-07,1.62519e-08,0.829759,0.00070917,-5.85676e-07,-4.48904e-09,0.830468,0.000707985,-5.99143e-07,1.70418e-09,0.831175,0.000706792,-5.9403e-07,-2.32768e-09,0.831881,0.000705597,-6.01014e-07,7.60648e-09,0.832586,0.000704418,-5.78194e-07,-2.80982e-08,0.83329,0.000703177,-6.62489e-07,4.51817e-08,0.833993,0.000701988,-5.26944e-07,-3.34192e-08,0.834694,0.000700834,-6.27201e-07,2.88904e-08,0.835394,0.000699666,-5.4053e-07,-2.25378e-08,0.836093,0.000698517,-6.08143e-07,1.65589e-09,0.836791,0.000697306,-6.03176e-07,1.59142e-08,0.837488,0.000696147,-5.55433e-07,-5.70801e-09,0.838184,0.000695019,-5.72557e-07,6.91792e-09,0.838878,0.000693895,-5.51803e-07,-2.19637e-08,0.839571,0.000692725,-6.17694e-07,2.13321e-08,0.840263,0.000691554,-5.53698e-07,-3.75996e-09,0.840954,0.000690435,-5.64978e-07,-6.29219e-09,0.841644,0.000689287,-5.83855e-07,2.89287e-08,0.842333,0.000688206,-4.97068e-07,-4.98181e-08,0.843021,0.000687062,-6.46523e-07,5.11344e-08,0.843707,0.000685922,-4.9312e-07,-3.55102e-08,0.844393,0.00068483,-5.9965e-07,3.13019e-08,0.845077,0.000683724,-5.05745e-07,-3.00925e-08,0.84576,0.000682622,-5.96022e-07,2.94636e-08,0.846442,0.000681519,-5.07631e-07,-2.81572e-08,0.847123,0.000680419,-5.92103e-07,2.35606e-08,0.847803,0.000679306,-5.21421e-07,-6.48045e-09,0.848482,0.000678243,-5.40863e-07,2.36124e-09,0.849159,0.000677169,-5.33779e-07,-2.96461e-09,0.849836,0.000676092,-5.42673e-07,9.49728e-09,0.850512,0.000675035,-5.14181e-07,-3.50245e-08,0.851186,0.000673902,-6.19254e-07,7.09959e-08,0.851859,0.000672876,-4.06267e-07,-7.01453e-08,0.852532,0.000671853,-6.16703e-07,3.07714e-08,0.853203,0.000670712,-5.24388e-07,6.66423e-09,0.853873,0.000669684,-5.04396e-07,2.17629e-09,0.854542,0.000668681,-4.97867e-07,-1.53693e-08,0.855211,0.000667639,-5.43975e-07,-3.03752e-10,0.855878,0.000666551,-5.44886e-07,1.65844e-08,0.856544,0.000665511,-4.95133e-07,-6.42907e-09,0.857209,0.000664501,-5.1442e-07,9.13195e-09,0.857873,0.0006635,-4.87024e-07,-3.00987e-08,0.858536,0.000662435,-5.7732e-07,5.16584e-08,0.859198,0.000661436,-4.22345e-07,-5.73255e-08,0.859859,0.000660419,-5.94322e-07,5.84343e-08,0.860518,0.000659406,-4.19019e-07,-5.72022e-08,0.861177,0.000658396,-5.90626e-07,5.11653e-08,0.861835,0.000657368,-4.3713e-07,-2.82495e-08,0.862492,0.000656409,-5.21878e-07,2.22788e-09,0.863148,0.000655372,-5.15195e-07,1.9338e-08,0.863803,0.0006544,-4.5718e-07,-1.99754e-08,0.864457,0.000653425,-5.17107e-07,9.59024e-10,0.86511,0.000652394,-5.1423e-07,1.61393e-08,0.865762,0.000651414,-4.65812e-07,-5.91149e-09,0.866413,0.000650465,-4.83546e-07,7.50665e-09,0.867063,0.00064952,-4.61026e-07,-2.4115e-08,0.867712,0.000648526,-5.33371e-07,2.93486e-08,0.86836,0.000647547,-4.45325e-07,-3.36748e-08,0.869007,0.000646555,-5.4635e-07,4.57461e-08,0.869653,0.0006456,-4.09112e-07,-3.01002e-08,0.870298,0.000644691,-4.99412e-07,1.50501e-08,0.870942,0.000643738,-4.54262e-07,-3.01002e-08,0.871585,0.000642739,-5.44563e-07,4.57461e-08,0.872228,0.000641787,-4.07324e-07,-3.36748e-08,0.872869,0.000640871,-5.08349e-07,2.93486e-08,0.873509,0.000639943,-4.20303e-07,-2.4115e-08,0.874149,0.00063903,-4.92648e-07,7.50655e-09,0.874787,0.000638067,-4.70128e-07,-5.91126e-09,0.875425,0.000637109,-4.87862e-07,1.61385e-08,0.876062,0.000636182,-4.39447e-07,9.61961e-10,0.876697,0.000635306,-4.36561e-07,-1.99863e-08,0.877332,0.000634373,-4.9652e-07,1.93785e-08,0.877966,0.000633438,-4.38384e-07,2.07697e-09,0.878599,0.000632567,-4.32153e-07,-2.76864e-08,0.879231,0.00063162,-5.15212e-07,4.90641e-08,0.879862,0.000630737,-3.6802e-07,-4.93606e-08,0.880493,0.000629852,-5.16102e-07,2.9169e-08,0.881122,0.000628908,-4.28595e-07,-7.71083e-09,0.881751,0.000628027,-4.51727e-07,1.6744e-09,0.882378,0.000627129,-4.46704e-07,1.01317e-09,0.883005,0.000626239,-4.43665e-07,-5.72703e-09,0.883631,0.000625334,-4.60846e-07,2.1895e-08,0.884255,0.000624478,-3.95161e-07,-2.22481e-08,0.88488,0.000623621,-4.61905e-07,7.4928e-09,0.885503,0.00062272,-4.39427e-07,-7.72306e-09,0.886125,0.000621818,-4.62596e-07,2.33995e-08,0.886746,0.000620963,-3.92398e-07,-2.62704e-08,0.887367,0.000620099,-4.71209e-07,2.20775e-08,0.887987,0.000619223,-4.04976e-07,-2.43496e-09,0.888605,0.000618406,-4.12281e-07,-1.23377e-08,0.889223,0.000617544,-4.49294e-07,-7.81876e-09,0.88984,0.000616622,-4.72751e-07,4.36128e-08,0.890457,0.000615807,-3.41912e-07,-4.7423e-08,0.891072,0.000614981,-4.84181e-07,2.68698e-08,0.891687,0.000614093,-4.03572e-07,-4.51384e-10,0.8923,0.000613285,-4.04926e-07,-2.50643e-08,0.892913,0.0006124,-4.80119e-07,4.11038e-08,0.893525,0.000611563,-3.56808e-07,-2.01414e-08,0.894136,0.000610789,-4.17232e-07,-2.01426e-08,0.894747,0.000609894,-4.7766e-07,4.11073e-08,0.895356,0.000609062,-3.54338e-07,-2.50773e-08,0.895965,0.000608278,-4.2957e-07,-4.02954e-10,0.896573,0.000607418,-4.30779e-07,2.66891e-08,0.89718,0.000606636,-3.50711e-07,-4.67489e-08,0.897786,0.000605795,-4.90958e-07,4.10972e-08,0.898391,0.000604936,-3.67666e-07,1.56948e-09,0.898996,0.000604205,-3.62958e-07,-4.73751e-08,0.8996,0.000603337,-5.05083e-07,6.87214e-08,0.900202,0.000602533,-2.98919e-07,-4.86966e-08,0.900805,0.000601789,-4.45009e-07,6.85589e-09,0.901406,0.00060092,-4.24441e-07,2.1273e-08,0.902007,0.000600135,-3.60622e-07,-3.23434e-08,0.902606,0.000599317,-4.57652e-07,4.84959e-08,0.903205,0.000598547,-3.12164e-07,-4.24309e-08,0.903803,0.000597795,-4.39457e-07,2.01844e-09,0.904401,0.000596922,-4.33402e-07,3.43571e-08,0.904997,0.000596159,-3.30331e-07,-2.02374e-08,0.905593,0.000595437,-3.91043e-07,-1.30123e-08,0.906188,0.000594616,-4.3008e-07,1.26819e-08,0.906782,0.000593794,-3.92034e-07,2.18894e-08,0.907376,0.000593076,-3.26366e-07,-4.06349e-08,0.907968,0.000592301,-4.4827e-07,2.1441e-08,0.90856,0.000591469,-3.83947e-07,1.44754e-08,0.909151,0.000590744,-3.40521e-07,-1.97379e-08,0.909742,0.000590004,-3.99735e-07,4.87161e-09,0.910331,0.000589219,-3.8512e-07,2.51532e-10,0.91092,0.00058845,-3.84366e-07,-5.87776e-09,0.911508,0.000587663,-4.01999e-07,2.32595e-08,0.912096,0.000586929,-3.3222e-07,-2.75554e-08,0.912682,0.000586182,-4.14887e-07,2.73573e-08,0.913268,0.000585434,-3.32815e-07,-2.22692e-08,0.913853,0.000584702,-3.99622e-07,2.11486e-09,0.914437,0.000583909,-3.93278e-07,1.38098e-08,0.915021,0.000583164,-3.51848e-07,2.25042e-09,0.915604,0.000582467,-3.45097e-07,-2.28115e-08,0.916186,0.000581708,-4.13531e-07,2.93911e-08,0.916767,0.000580969,-3.25358e-07,-3.51481e-08,0.917348,0.000580213,-4.30803e-07,5.15967e-08,0.917928,0.000579506,-2.76012e-07,-5.20296e-08,0.918507,0.000578798,-4.32101e-07,3.73124e-08,0.919085,0.000578046,-3.20164e-07,-3.76154e-08,0.919663,0.000577293,-4.3301e-07,5.35447e-08,0.92024,0.000576587,-2.72376e-07,-5.7354e-08,0.920816,0.000575871,-4.44438e-07,5.66621e-08,0.921391,0.000575152,-2.74452e-07,-5.00851e-08,0.921966,0.000574453,-4.24707e-07,2.4469e-08,0.92254,0.000573677,-3.513e-07,1.18138e-08,0.923114,0.000573009,-3.15859e-07,-1.21195e-08,0.923686,0.000572341,-3.52217e-07,-2.29403e-08,0.924258,0.000571568,-4.21038e-07,4.4276e-08,0.924829,0.000570859,-2.8821e-07,-3.49546e-08,0.9254,0.000570178,-3.93074e-07,3.59377e-08,0.92597,0.000569499,-2.85261e-07,-4.91915e-08,0.926539,0.000568781,-4.32835e-07,4.16189e-08,0.927107,0.00056804,-3.07979e-07,1.92523e-09,0.927675,0.00056743,-3.02203e-07,-4.93198e-08,0.928242,0.000566678,-4.50162e-07,7.61447e-08,0.928809,0.000566006,-2.21728e-07,-7.6445e-08,0.929374,0.000565333,-4.51063e-07,5.08216e-08,0.929939,0.000564583,-2.98599e-07,-7.63212e-09,0.930503,0.000563963,-3.21495e-07,-2.02931e-08,0.931067,0.000563259,-3.82374e-07,2.92001e-08,0.93163,0.000562582,-2.94774e-07,-3.69025e-08,0.932192,0.000561882,-4.05482e-07,5.88053e-08,0.932754,0.000561247,-2.29066e-07,-7.91094e-08,0.933315,0.000560552,-4.66394e-07,7.88184e-08,0.933875,0.000559856,-2.29939e-07,-5.73501e-08,0.934434,0.000559224,-4.01989e-07,3.13727e-08,0.934993,0.000558514,-3.07871e-07,-8.53611e-09,0.935551,0.000557873,-3.33479e-07,2.77175e-09,0.936109,0.000557214,-3.25164e-07,-2.55091e-09,0.936666,0.000556556,-3.32817e-07,7.43188e-09,0.937222,0.000555913,-3.10521e-07,-2.71766e-08,0.937778,0.00055521,-3.92051e-07,4.167e-08,0.938333,0.000554551,-2.67041e-07,-2.02941e-08,0.938887,0.000553956,-3.27923e-07,-2.00984e-08,0.93944,0.00055324,-3.88218e-07,4.10828e-08,0.939993,0.000552587,-2.6497e-07,-2.50237e-08,0.940546,0.000551982,-3.40041e-07,-5.92583e-10,0.941097,0.0005513,-3.41819e-07,2.7394e-08,0.941648,0.000550698,-2.59637e-07,-4.93788e-08,0.942199,0.000550031,-4.07773e-07,5.09119e-08,0.942748,0.000549368,-2.55038e-07,-3.50595e-08,0.943297,0.000548753,-3.60216e-07,2.97214e-08,0.943846,0.000548122,-2.71052e-07,-2.42215e-08,0.944394,0.000547507,-3.43716e-07,7.55985e-09,0.944941,0.000546842,-3.21037e-07,-6.01796e-09,0.945487,0.000546182,-3.3909e-07,1.65119e-08,0.946033,0.000545553,-2.89555e-07,-4.2498e-10,0.946578,0.000544973,-2.9083e-07,-1.4812e-08,0.947123,0.000544347,-3.35266e-07,6.83068e-11,0.947667,0.000543676,-3.35061e-07,1.45388e-08,0.94821,0.00054305,-2.91444e-07,1.38123e-09,0.948753,0.000542471,-2.87301e-07,-2.00637e-08,0.949295,0.000541836,-3.47492e-07,1.92688e-08,0.949837,0.000541199,-2.89685e-07,2.59298e-09,0.950378,0.000540628,-2.81906e-07,-2.96407e-08,0.950918,0.000539975,-3.70829e-07,5.63652e-08,0.951458,0.000539402,-2.01733e-07,-7.66107e-08,0.951997,0.000538769,-4.31565e-07,7.12638e-08,0.952535,0.00053812,-2.17774e-07,-2.96305e-08,0.953073,0.000537595,-3.06665e-07,-1.23464e-08,0.95361,0.000536945,-3.43704e-07,1.94114e-08,0.954147,0.000536316,-2.8547e-07,-5.69451e-09,0.954683,0.000535728,-3.02554e-07,3.36666e-09,0.955219,0.000535133,-2.92454e-07,-7.77208e-09,0.955753,0.000534525,-3.1577e-07,2.77216e-08,0.956288,0.000533976,-2.32605e-07,-4.35097e-08,0.956821,0.00053338,-3.63134e-07,2.7108e-08,0.957354,0.000532735,-2.8181e-07,-5.31772e-09,0.957887,0.000532156,-2.97764e-07,-5.83718e-09,0.958419,0.000531543,-3.15275e-07,2.86664e-08,0.95895,0.000530998,-2.29276e-07,-4.9224e-08,0.959481,0.000530392,-3.76948e-07,4.90201e-08,0.960011,0.000529785,-2.29887e-07,-2.76471e-08,0.96054,0.000529243,-3.12829e-07,1.96385e-09,0.961069,0.000528623,-3.06937e-07,1.97917e-08,0.961598,0.000528068,-2.47562e-07,-2.15261e-08,0.962125,0.000527508,-3.1214e-07,6.70795e-09,0.962653,0.000526904,-2.92016e-07,-5.30573e-09,0.963179,0.000526304,-3.07934e-07,1.4515e-08,0.963705,0.000525732,-2.64389e-07,6.85048e-09,0.964231,0.000525224,-2.43837e-07,-4.19169e-08,0.964756,0.00052461,-3.69588e-07,4.1608e-08,0.96528,0.000523996,-2.44764e-07,-5.30598e-09,0.965804,0.000523491,-2.60682e-07,-2.03841e-08,0.966327,0.000522908,-3.21834e-07,2.72378e-08,0.966849,0.000522346,-2.40121e-07,-2.89625e-08,0.967371,0.000521779,-3.27008e-07,2.90075e-08,0.967893,0.000521212,-2.39986e-07,-2.74629e-08,0.968414,0.00052065,-3.22374e-07,2.12396e-08,0.968934,0.000520069,-2.58656e-07,2.10922e-09,0.969454,0.000519558,-2.52328e-07,-2.96765e-08,0.969973,0.000518964,-3.41357e-07,5.6992e-08,0.970492,0.000518452,-1.70382e-07,-7.90821e-08,0.97101,0.000517874,-4.07628e-07,8.05224e-08,0.971528,0.000517301,-1.66061e-07,-6.41937e-08,0.972045,0.000516776,-3.58642e-07,5.70429e-08,0.972561,0.00051623,-1.87513e-07,-4.47686e-08,0.973077,0.00051572,-3.21819e-07,2.82237e-09,0.973593,0.000515085,-3.13352e-07,3.34792e-08,0.974108,0.000514559,-2.12914e-07,-1.75298e-08,0.974622,0.000514081,-2.65503e-07,-2.29648e-08,0.975136,0.000513481,-3.34398e-07,4.97843e-08,0.975649,0.000512961,-1.85045e-07,-5.6963e-08,0.976162,0.00051242,-3.55934e-07,5.88585e-08,0.976674,0.000511885,-1.79359e-07,-5.92616e-08,0.977185,0.000511348,-3.57143e-07,5.89785e-08,0.977696,0.000510811,-1.80208e-07,-5.74433e-08,0.978207,0.000510278,-3.52538e-07,5.15854e-08,0.978717,0.000509728,-1.97781e-07,-2.9689e-08,0.979226,0.000509243,-2.86848e-07,7.56591e-09,0.979735,0.000508692,-2.64151e-07,-5.74649e-10,0.980244,0.000508162,-2.65875e-07,-5.26732e-09,0.980752,0.000507615,-2.81677e-07,2.16439e-08,0.981259,0.000507116,-2.16745e-07,-2.17037e-08,0.981766,0.000506618,-2.81856e-07,5.56636e-09,0.982272,0.000506071,-2.65157e-07,-5.61689e-10,0.982778,0.000505539,-2.66842e-07,-3.31963e-09,0.983283,0.000504995,-2.76801e-07,1.38402e-08,0.983788,0.000504483,-2.3528e-07,7.56339e-09,0.984292,0.000504035,-2.1259e-07,-4.40938e-08,0.984796,0.000503478,-3.44871e-07,4.96026e-08,0.985299,0.000502937,-1.96064e-07,-3.51071e-08,0.985802,0.000502439,-3.01385e-07,3.12212e-08,0.986304,0.00050193,-2.07721e-07,-3.0173e-08,0.986806,0.000501424,-2.9824e-07,2.9866e-08,0.987307,0.000500917,-2.08642e-07,-2.96865e-08,0.987808,0.000500411,-2.97702e-07,2.92753e-08,0.988308,0.000499903,-2.09876e-07,-2.78101e-08,0.988807,0.0004994,-2.93306e-07,2.23604e-08,0.989307,0.000498881,-2.26225e-07,-2.02681e-09,0.989805,0.000498422,-2.32305e-07,-1.42531e-08,0.990303,0.000497915,-2.75065e-07,-5.65232e-10,0.990801,0.000497363,-2.76761e-07,1.65141e-08,0.991298,0.000496859,-2.27218e-07,-5.88639e-09,0.991795,0.000496387,-2.44878e-07,7.0315e-09,0.992291,0.000495918,-2.23783e-07,-2.22396e-08,0.992787,0.000495404,-2.90502e-07,2.23224e-08,0.993282,0.00049489,-2.23535e-07,-7.44543e-09,0.993776,0.000494421,-2.45871e-07,7.45924e-09,0.994271,0.000493951,-2.23493e-07,-2.23915e-08,0.994764,0.000493437,-2.90668e-07,2.25021e-08,0.995257,0.000492923,-2.23161e-07,-8.01218e-09,0.99575,0.000492453,-2.47198e-07,9.54669e-09,0.996242,0.000491987,-2.18558e-07,-3.01746e-08,0.996734,0.000491459,-3.09082e-07,5.1547e-08,0.997225,0.000490996,-1.54441e-07,-5.68039e-08,0.997716,0.000490517,-3.24853e-07,5.64594e-08,0.998206,0.000490036,-1.55474e-07,-4.98245e-08,0.998696,0.000489576,-3.04948e-07,2.36292e-08,0.999186,0.000489037,-2.3406e-07,1.49121e-08,0.999674,0.000488613,-1.89324e-07,-2.3673e-08,1.00016,0.000488164,-2.60343e-07,2.01754e-08,1.00065,0.000487704,-1.99816e-07,-5.70288e-08,1.00114,0.000487133,-3.70903e-07,8.87303e-08,1.00162,0.000486657,-1.04712e-07,-5.94737e-08,1.00211,0.000486269,-2.83133e-07,2.99553e-08,1.0026,0.000485793,-1.93267e-07,-6.03474e-08,1.00308,0.000485225,-3.74309e-07,9.2225e-08,1.00357,0.000484754,-9.76345e-08,-7.0134e-08,1.00405,0.000484348,-3.08036e-07,6.91016e-08,1.00454,0.000483939,-1.00731e-07,-8.70633e-08,1.00502,0.000483476,-3.61921e-07,4.07328e-08,1.0055,0.000482875,-2.39723e-07,4.33413e-08,1.00599,0.000482525,-1.09699e-07,-9.48886e-08,1.00647,0.000482021,-3.94365e-07,9.77947e-08,1.00695,0.000481526,-1.00981e-07,-5.78713e-08,1.00743,0.00048115,-2.74595e-07,1.44814e-08,1.00791,0.000480645,-2.31151e-07,-5.42665e-11,1.00839,0.000480182,-2.31314e-07,-1.42643e-08,1.00887,0.000479677,-2.74106e-07,5.71115e-08,1.00935,0.0004793,-1.02772e-07,-9.49724e-08,1.00983,0.000478809,-3.87689e-07,8.43596e-08,1.01031,0.000478287,-1.3461e-07,-4.04755e-09,1.01079,0.000478006,-1.46753e-07,-6.81694e-08,1.01127,0.000477508,-3.51261e-07,3.83067e-08,1.01174,0.00047692,-2.36341e-07,3.41521e-08,1.01222,0.00047655,-1.33885e-07,-5.57058e-08,1.0127,0.000476115,-3.01002e-07,6.94616e-08,1.01317,0.000475721,-9.26174e-08,-1.02931e-07,1.01365,0.000475227,-4.01412e-07,1.03846e-07,1.01412,0.000474736,-8.98751e-08,-7.40321e-08,1.0146,0.000474334,-3.11971e-07,7.30735e-08,1.01507,0.00047393,-9.27508e-08,-9.90527e-08,1.01554,0.000473447,-3.89909e-07,8.47188e-08,1.01602,0.000472921,-1.35753e-07,-1.40381e-09,1.01649,0.000472645,-1.39964e-07,-7.91035e-08,1.01696,0.000472128,-3.77275e-07,7.93993e-08,1.01744,0.000471612,-1.39077e-07,-7.52607e-11,1.01791,0.000471334,-1.39302e-07,-7.90983e-08,1.01838,0.000470818,-3.76597e-07,7.80499e-08,1.01885,0.000470299,-1.42448e-07,5.31733e-09,1.01932,0.00047003,-1.26496e-07,-9.93193e-08,1.01979,0.000469479,-4.24453e-07,1.53541e-07,1.02026,0.00046909,3.617e-08,-1.57217e-07,1.02073,0.000468691,-4.35482e-07,1.177e-07,1.02119,0.000468173,-8.23808e-08,-7.51659e-08,1.02166,0.000467783,-3.07878e-07,6.37538e-08,1.02213,0.000467358,-1.16617e-07,-6.064e-08,1.0226,0.000466943,-2.98537e-07,5.9597e-08,1.02306,0.000466525,-1.19746e-07,-5.85386e-08,1.02353,0.00046611,-2.95362e-07,5.53482e-08,1.024,0.000465685,-1.29317e-07,-4.36449e-08,1.02446,0.000465296,-2.60252e-07,2.20268e-11,1.02493,0.000464775,-2.60186e-07,4.35568e-08,1.02539,0.000464386,-1.29516e-07,-5.50398e-08,1.02586,0.000463961,-2.94635e-07,5.73932e-08,1.02632,0.000463544,-1.22456e-07,-5.53236e-08,1.02678,0.000463133,-2.88426e-07,4.46921e-08,1.02725,0.000462691,-1.5435e-07,-4.23534e-09,1.02771,0.000462369,-1.67056e-07,-2.77507e-08,1.02817,0.000461952,-2.50308e-07,-3.97101e-09,1.02863,0.000461439,-2.62221e-07,4.36348e-08,1.02909,0.000461046,-1.31317e-07,-5.13589e-08,1.02955,0.000460629,-2.85394e-07,4.25913e-08,1.03001,0.000460186,-1.5762e-07,2.0285e-10,1.03047,0.000459871,-1.57011e-07,-4.34027e-08,1.03093,0.000459427,-2.87219e-07,5.41987e-08,1.03139,0.000459015,-1.24623e-07,-5.4183e-08,1.03185,0.000458604,-2.87172e-07,4.33239e-08,1.03231,0.000458159,-1.572e-07,9.65817e-11,1.03277,0.000457845,-1.56911e-07,-4.37103e-08,1.03323,0.0004574,-2.88041e-07,5.55351e-08,1.03368,0.000456991,-1.21436e-07,-5.9221e-08,1.03414,0.00045657,-2.99099e-07,6.21394e-08,1.0346,0.000456158,-1.1268e-07,-7.01275e-08,1.03505,0.000455723,-3.23063e-07,9.91614e-08,1.03551,0.000455374,-2.55788e-08,-8.80996e-08,1.03596,0.000455058,-2.89878e-07,1.48184e-08,1.03642,0.000454523,-2.45422e-07,2.88258e-08,1.03687,0.000454119,-1.58945e-07,-1.09125e-08,1.03733,0.000453768,-1.91682e-07,1.48241e-08,1.03778,0.000453429,-1.4721e-07,-4.83838e-08,1.03823,0.00045299,-2.92361e-07,5.95019e-08,1.03869,0.000452584,-1.13856e-07,-7.04146e-08,1.03914,0.000452145,-3.25099e-07,1.02947e-07,1.03959,0.000451803,-1.62583e-08,-1.02955e-07,1.04004,0.000451462,-3.25123e-07,7.04544e-08,1.04049,0.000451023,-1.1376e-07,-5.96534e-08,1.04094,0.000450616,-2.9272e-07,4.89499e-08,1.04139,0.000450178,-1.45871e-07,-1.69369e-08,1.04184,0.000449835,-1.96681e-07,1.87977e-08,1.04229,0.000449498,-1.40288e-07,-5.82539e-08,1.04274,0.000449043,-3.1505e-07,9.50087e-08,1.04319,0.000448698,-3.00238e-08,-8.33623e-08,1.04364,0.000448388,-2.80111e-07,2.20363e-11,1.04409,0.000447828,-2.80045e-07,8.32742e-08,1.04454,0.000447517,-3.02221e-08,-9.47002e-08,1.04498,0.000447173,-3.14323e-07,5.7108e-08,1.04543,0.000446716,-1.42999e-07,-1.45225e-08,1.04588,0.000446386,-1.86566e-07,9.82022e-10,1.04632,0.000446016,-1.8362e-07,1.05944e-08,1.04677,0.00044568,-1.51837e-07,-4.33597e-08,1.04721,0.000445247,-2.81916e-07,4.36352e-08,1.04766,0.000444814,-1.51011e-07,-1.19717e-08,1.0481,0.000444476,-1.86926e-07,4.25158e-09,1.04855,0.000444115,-1.74171e-07,-5.03461e-09,1.04899,0.000443751,-1.89275e-07,1.58868e-08,1.04944,0.00044342,-1.41614e-07,-5.85127e-08,1.04988,0.000442961,-3.17152e-07,9.89548e-08,1.05032,0.000442624,-2.0288e-08,-9.88878e-08,1.05076,0.000442287,-3.16951e-07,5.81779e-08,1.05121,0.000441827,-1.42418e-07,-1.46144e-08,1.05165,0.000441499,-1.86261e-07,2.79892e-10,1.05209,0.000441127,-1.85421e-07,1.34949e-08,1.05253,0.000440797,-1.44937e-07,-5.42594e-08,1.05297,0.000440344,-3.07715e-07,8.43335e-08,1.05341,0.000439982,-5.47146e-08,-4.46558e-08,1.05385,0.000439738,-1.88682e-07,-2.49193e-08,1.05429,0.000439286,-2.6344e-07,2.5124e-08,1.05473,0.000438835,-1.88068e-07,4.36328e-08,1.05517,0.000438589,-5.71699e-08,-8.04459e-08,1.05561,0.000438234,-2.98508e-07,3.97324e-08,1.05605,0.000437756,-1.79311e-07,4.07258e-08,1.05648,0.000437519,-5.71332e-08,-8.34263e-08,1.05692,0.000437155,-3.07412e-07,5.45608e-08,1.05736,0.000436704,-1.4373e-07,-1.56078e-08,1.05779,0.000436369,-1.90553e-07,7.87043e-09,1.05823,0.000436012,-1.66942e-07,-1.58739e-08,1.05867,0.00043563,-2.14563e-07,5.56251e-08,1.0591,0.000435368,-4.76881e-08,-8.74172e-08,1.05954,0.000435011,-3.0994e-07,5.56251e-08,1.05997,0.000434558,-1.43064e-07,-1.58739e-08,1.06041,0.000434224,-1.90686e-07,7.87042e-09,1.06084,0.000433866,-1.67075e-07,-1.56078e-08,1.06127,0.000433485,-2.13898e-07,5.45609e-08,1.06171,0.000433221,-5.02157e-08,-8.34263e-08,1.06214,0.00043287,-3.00495e-07,4.07258e-08,1.06257,0.000432391,-1.78317e-07,3.97325e-08,1.063,0.000432154,-5.91198e-08,-8.04464e-08,1.06344,0.000431794,-3.00459e-07,4.36347e-08,1.06387,0.000431324,-1.69555e-07,2.5117e-08,1.0643,0.000431061,-9.42041e-08,-2.48934e-08,1.06473,0.000430798,-1.68884e-07,-4.47527e-08,1.06516,0.000430326,-3.03142e-07,8.46951e-08,1.06559,0.000429973,-4.90573e-08,-5.56089e-08,1.06602,0.000429708,-2.15884e-07,1.85314e-08,1.06645,0.000429332,-1.6029e-07,-1.85166e-08,1.06688,0.000428956,-2.1584e-07,5.5535e-08,1.06731,0.000428691,-4.92347e-08,-8.44142e-08,1.06774,0.000428339,-3.02477e-07,4.37032e-08,1.06816,0.000427865,-1.71368e-07,2.88107e-08,1.06859,0.000427609,-8.49356e-08,-3.97367e-08,1.06902,0.00042732,-2.04146e-07,1.09267e-08,1.06945,0.000426945,-1.71365e-07,-3.97023e-09,1.06987,0.00042659,-1.83276e-07,4.9542e-09,1.0703,0.000426238,-1.68414e-07,-1.58466e-08,1.07073,0.000425854,-2.15953e-07,5.84321e-08,1.07115,0.000425597,-4.0657e-08,-9.86725e-08,1.07158,0.00042522,-3.36674e-07,9.78392e-08,1.072,0.00042484,-4.31568e-08,-5.42658e-08,1.07243,0.000424591,-2.05954e-07,1.45377e-11,1.07285,0.000424179,-2.0591e-07,5.42076e-08,1.07328,0.00042393,-4.32877e-08,-9.76357e-08,1.0737,0.00042355,-3.36195e-07,9.79165e-08,1.07412,0.000423172,-4.24451e-08,-5.56118e-08,1.07455,0.00042292,-2.09281e-07,5.32143e-09,1.07497,0.000422518,-1.93316e-07,3.43261e-08,1.07539,0.000422234,-9.0338e-08,-2.34165e-08,1.07581,0.000421983,-1.60588e-07,-5.98692e-08,1.07623,0.000421482,-3.40195e-07,1.43684e-07,1.07666,0.000421233,9.08574e-08,-1.5724e-07,1.07708,0.000420943,-3.80862e-07,1.27647e-07,1.0775,0.000420564,2.0791e-09,-1.1493e-07,1.07792,0.000420223,-3.4271e-07,9.36534e-08,1.07834,0.000419819,-6.17499e-08,-2.12653e-08,1.07876,0.000419632,-1.25546e-07,-8.59219e-09,1.07918,0.000419355,-1.51322e-07,-6.35752e-08,1.0796,0.000418861,-3.42048e-07,1.43684e-07,1.08002,0.000418608,8.90034e-08,-1.53532e-07,1.08043,0.000418326,-3.71593e-07,1.12817e-07,1.08085,0.000417921,-3.31414e-08,-5.93184e-08,1.08127,0.000417677,-2.11097e-07,5.24697e-09,1.08169,0.00041727,-1.95356e-07,3.83305e-08,1.0821,0.000416995,-8.03642e-08,-3.93597e-08,1.08252,0.000416716,-1.98443e-07,-1.0094e-10,1.08294,0.000416319,-1.98746e-07,3.97635e-08,1.08335,0.00041604,-7.94557e-08,-3.97437e-08,1.08377,0.000415762,-1.98687e-07,1.94215e-12,1.08419,0.000415365,-1.98681e-07,3.97359e-08,1.0846,0.000415087,-7.94732e-08,-3.97362e-08,1.08502,0.000414809,-1.98682e-07,-4.31063e-13,1.08543,0.000414411,-1.98683e-07,3.97379e-08,1.08584,0.000414133,-7.94694e-08,-3.97418e-08,1.08626,0.000413855,-1.98695e-07,2.00563e-11,1.08667,0.000413458,-1.98635e-07,3.96616e-08,1.08709,0.000413179,-7.965e-08,-3.9457e-08,1.0875,0.000412902,-1.98021e-07,-1.04281e-09,1.08791,0.000412502,-2.01149e-07,4.36282e-08,1.08832,0.000412231,-7.02648e-08,-5.42608e-08,1.08874,0.000411928,-2.33047e-07,5.42057e-08,1.08915,0.000411624,-7.04301e-08,-4.33527e-08,1.08956,0.000411353,-2.00488e-07,-4.07378e-12,1.08997,0.000410952,-2.005e-07,4.3369e-08,1.09038,0.000410681,-7.03934e-08,-5.42627e-08,1.09079,0.000410378,-2.33182e-07,5.44726e-08,1.0912,0.000410075,-6.97637e-08,-4.44186e-08,1.09161,0.000409802,-2.03019e-07,3.99235e-09,1.09202,0.000409408,-1.91042e-07,2.84491e-08,1.09243,0.000409111,-1.05695e-07,1.42043e-09,1.09284,0.000408904,-1.01434e-07,-3.41308e-08,1.09325,0.000408599,-2.03826e-07,1.58937e-08,1.09366,0.000408239,-1.56145e-07,-2.94438e-08,1.09406,0.000407838,-2.44476e-07,1.01881e-07,1.09447,0.000407655,6.11676e-08,-1.39663e-07,1.09488,0.000407358,-3.57822e-07,9.91432e-08,1.09529,0.00040694,-6.03921e-08,-1.84912e-08,1.09569,0.000406764,-1.15866e-07,-2.51785e-08,1.0961,0.000406457,-1.91401e-07,-4.03115e-12,1.09651,0.000406074,-1.91413e-07,2.51947e-08,1.09691,0.000405767,-1.15829e-07,1.84346e-08,1.09732,0.00040559,-6.05254e-08,-9.89332e-08,1.09772,0.000405172,-3.57325e-07,1.3888e-07,1.09813,0.000404874,5.93136e-08,-9.8957e-08,1.09853,0.000404696,-2.37557e-07,1.853e-08,1.09894,0.000404277,-1.81968e-07,2.48372e-08,1.09934,0.000403987,-1.07456e-07,1.33047e-09,1.09975,0.000403776,-1.03465e-07,-3.01591e-08,1.10015,0.000403479,-1.93942e-07,9.66054e-11,1.10055,0.000403091,-1.93652e-07,2.97727e-08,1.10096,0.000402793,-1.04334e-07,2.19273e-11,1.10136,0.000402585,-1.04268e-07,-2.98604e-08,1.10176,0.000402287,-1.93849e-07,2.10325e-10,1.10216,0.0004019,-1.93218e-07,2.90191e-08,1.10256,0.0004016,-1.06161e-07,2.92264e-09,1.10297,0.000401397,-9.73931e-08,-4.07096e-08,1.10337,0.00040108,-2.19522e-07,4.07067e-08,1.10377,0.000400763,-9.7402e-08,-2.90783e-09,1.10417,0.000400559,-1.06126e-07,-2.90754e-08,1.10457,0.00040026,-1.93352e-07,9.00021e-14,1.10497,0.000399873,-1.93351e-07,2.9075e-08,1.10537,0.000399574,-1.06126e-07,2.90902e-09,1.10577,0.00039937,-9.73992e-08,-4.07111e-08,1.10617,0.000399053,-2.19533e-07,4.07262e-08,1.10657,0.000398736,-9.73541e-08,-2.98424e-09,1.10697,0.000398533,-1.06307e-07,-2.87892e-08,1.10736,0.000398234,-1.92674e-07,-1.06824e-09,1.10776,0.000397845,-1.95879e-07,3.30622e-08,1.10816,0.000397552,-9.66926e-08,-1.19712e-08,1.10856,0.000397323,-1.32606e-07,1.48225e-08,1.10895,0.000397102,-8.81387e-08,-4.73187e-08,1.10935,0.000396784,-2.30095e-07,5.52429e-08,1.10975,0.00039649,-6.4366e-08,-5.44437e-08,1.11014,0.000396198,-2.27697e-07,4.33226e-08,1.11054,0.000395872,-9.77293e-08,3.62656e-10,1.11094,0.000395678,-9.66414e-08,-4.47732e-08,1.11133,0.00039535,-2.30961e-07,5.95208e-08,1.11173,0.000395067,-5.23985e-08,-7.41008e-08,1.11212,0.00039474,-2.74701e-07,1.17673e-07,1.11252,0.000394543,7.83181e-08,-1.58172e-07,1.11291,0.000394225,-3.96199e-07,1.57389e-07,1.1133,0.000393905,7.59679e-08,-1.13756e-07,1.1137,0.000393716,-2.653e-07,5.92165e-08,1.11409,0.000393363,-8.76507e-08,-3.90074e-09,1.11449,0.000393176,-9.93529e-08,-4.36136e-08,1.11488,0.000392846,-2.30194e-07,5.91457e-08,1.11527,0.000392563,-5.27564e-08,-7.376e-08,1.11566,0.000392237,-2.74037e-07,1.16685e-07,1.11606,0.000392039,7.60189e-08,-1.54562e-07,1.11645,0.000391727,-3.87667e-07,1.43935e-07,1.11684,0.000391384,4.4137e-08,-6.35487e-08,1.11723,0.000391281,-1.46509e-07,-8.94896e-09,1.11762,0.000390961,-1.73356e-07,-1.98647e-08,1.11801,0.000390555,-2.3295e-07,8.8408e-08,1.1184,0.000390354,3.22736e-08,-9.53486e-08,1.11879,0.000390133,-2.53772e-07,5.45677e-08,1.11918,0.000389789,-9.0069e-08,-3.71296e-09,1.11957,0.000389598,-1.01208e-07,-3.97159e-08,1.11996,0.000389276,-2.20355e-07,4.33671e-08,1.12035,0.000388966,-9.02542e-08,-1.45431e-08,1.12074,0.000388741,-1.33883e-07,1.48052e-08,1.12113,0.000388518,-8.94678e-08,-4.46778e-08,1.12152,0.000388205,-2.23501e-07,4.46966e-08,1.12191,0.000387892,-8.94114e-08,-1.48992e-08,1.12229,0.000387669,-1.34109e-07,1.49003e-08,1.12268,0.000387445,-8.94082e-08,-4.47019e-08,1.12307,0.000387132,-2.23514e-07,4.4698e-08,1.12345,0.000386819,-8.942e-08,-1.48806e-08,1.12384,0.000386596,-1.34062e-07,1.48245e-08,1.12423,0.000386372,-8.95885e-08,-4.44172e-08,1.12461,0.00038606,-2.2284e-07,4.36351e-08,1.125,0.000385745,-9.19348e-08,-1.09139e-08,1.12539,0.000385528,-1.24677e-07,2.05584e-11,1.12577,0.000385279,-1.24615e-07,1.08317e-08,1.12616,0.000385062,-9.21198e-08,-4.33473e-08,1.12654,0.000384748,-2.22162e-07,4.33481e-08,1.12693,0.000384434,-9.21174e-08,-1.08356e-08,1.12731,0.000384217,-1.24624e-07,-5.50907e-12,1.12769,0.000383968,-1.24641e-07,1.08577e-08,1.12808,0.000383751,-9.20679e-08,-4.34252e-08,1.12846,0.000383437,-2.22343e-07,4.36337e-08,1.12884,0.000383123,-9.14422e-08,-1.19005e-08,1.12923,0.000382904,-1.27144e-07,3.96813e-09,1.12961,0.000382662,-1.15239e-07,-3.97207e-09,1.12999,0.000382419,-1.27155e-07,1.19201e-08,1.13038,0.000382201,-9.1395e-08,-4.37085e-08,1.13076,0.000381887,-2.2252e-07,4.37046e-08,1.13114,0.000381573,-9.14068e-08,-1.19005e-08,1.13152,0.000381355,-1.27108e-07,3.89734e-09,1.1319,0.000381112,-1.15416e-07,-3.68887e-09,1.13228,0.00038087,-1.26483e-07,1.08582e-08,1.13266,0.00038065,-9.39083e-08,-3.97438e-08,1.13304,0.000380343,-2.1314e-07,2.89076e-08,1.13342,0.000380003,-1.26417e-07,4.33225e-08,1.1338,0.00037988,3.55072e-09,-8.29883e-08,1.13418,0.000379638,-2.45414e-07,5.0212e-08,1.13456,0.000379298,-9.47781e-08,1.34964e-09,1.13494,0.000379113,-9.07292e-08,-5.56105e-08,1.13532,0.000378764,-2.57561e-07,1.01883e-07,1.1357,0.000378555,4.80889e-08,-1.13504e-07,1.13608,0.000378311,-2.92423e-07,1.13713e-07,1.13646,0.000378067,4.87176e-08,-1.02931e-07,1.13683,0.000377856,-2.60076e-07,5.95923e-08,1.13721,0.000377514,-8.12988e-08,-1.62288e-08,1.13759,0.000377303,-1.29985e-07,5.32278e-09,1.13797,0.000377059,-1.14017e-07,-5.06237e-09,1.13834,0.000376816,-1.29204e-07,1.49267e-08,1.13872,0.000376602,-8.44237e-08,-5.46444e-08,1.1391,0.000376269,-2.48357e-07,8.44417e-08,1.13947,0.000376026,4.96815e-09,-4.47039e-08,1.13985,0.000375902,-1.29143e-07,-2.48355e-08,1.14023,0.000375569,-2.0365e-07,2.48368e-08,1.1406,0.000375236,-1.2914e-07,4.46977e-08,1.14098,0.000375112,4.95341e-09,-8.44184e-08,1.14135,0.000374869,-2.48302e-07,5.45572e-08,1.14173,0.000374536,-8.463e-08,-1.46013e-08,1.1421,0.000374323,-1.28434e-07,3.8478e-09,1.14247,0.000374077,-1.1689e-07,-7.89941e-10,1.14285,0.000373841,-1.1926e-07,-6.88042e-10,1.14322,0.0003736,-1.21324e-07,3.54213e-09,1.1436,0.000373368,-1.10698e-07,-1.34805e-08,1.14397,0.000373107,-1.51139e-07,5.03798e-08,1.14434,0.000372767,0.,0.}; + + template + __device__ __forceinline__ void RGB2LuvConvert_f(const T& src, D& dst) + { + const float _d = 1.f / (0.950456f + 15 + 1.088754f * 3); + const float _un = 13 * (4 * 0.950456f * _d); + const float _vn = 13 * (9 * _d); + + float B = blueIdx == 0 ? src.x : src.z; + float G = src.y; + float R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + } + + float X = R * 0.412453f + G * 0.357580f + B * 0.180423f; + float Y = R * 0.212671f + G * 0.715160f + B * 0.072169f; + float Z = R * 0.019334f + G * 0.119193f + B * 0.950227f; + + float L = splineInterpolate(Y * (LAB_CBRT_TAB_SIZE / 1.5f), c_LabCbrtTab, LAB_CBRT_TAB_SIZE); + L = 116.f * L - 16.f; + + const float d = (4 * 13) / ::fmaxf(X + 15 * Y + 3 * Z, numeric_limits::epsilon()); + float u = L * (X * d - _un); + float v = L * ((9 * 0.25f) * Y * d - _vn); + + dst.x = L; + dst.y = u; + dst.z = v; + } + + template + __device__ __forceinline__ void RGB2LuvConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (1.f / 255.f); + srcf.y = src.y * (1.f / 255.f); + srcf.z = src.z * (1.f / 255.f); + + RGB2LuvConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 2.55f); + dst.y = saturate_cast(dstf.y * 0.72033898305084743f + 96.525423728813564f); + dst.z = saturate_cast(dstf.z * 0.9732824427480916f + 136.259541984732824f); + } + + template struct RGB2Luv; + template + struct RGB2Luv + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LuvConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Luv() {} + __host__ __device__ __forceinline__ RGB2Luv(const RGB2Luv&) {} + }; + template + struct RGB2Luv + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LuvConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Luv() {} + __host__ __device__ __forceinline__ RGB2Luv(const RGB2Luv&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Luv functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template + __device__ __forceinline__ void Luv2RGBConvert_f(const T& src, D& dst) + { + const float _d = 1.f / (0.950456f + 15 + 1.088754f * 3); + const float _un = 4 * 0.950456f * _d; + const float _vn = 9 * _d; + + float L = src.x; + float u = src.y; + float v = src.z; + + float Y = (L + 16.f) * (1.f / 116.f); + Y = Y * Y * Y; + + float d = (1.f / 13.f) / L; + u = u * d + _un; + v = v * d + _vn; + + float iv = 1.f / v; + float X = 2.25f * u * Y * iv; + float Z = (12 - 3 * u - 20 * v) * Y * 0.25f * iv; + + float B = 0.055648f * X - 0.204043f * Y + 1.057311f * Z; + float G = -0.969256f * X + 1.875991f * Y + 0.041556f * Z; + float R = 3.240479f * X - 1.537150f * Y - 0.498535f * Z; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + } + + dst.x = blueIdx == 0 ? B : R; + dst.y = G; + dst.z = blueIdx == 0 ? R : B; + setAlpha(dst, ColorChannel::max()); + } + + template + __device__ __forceinline__ void Luv2RGBConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (100.f / 255.f); + srcf.y = src.y * 1.388235294117647f - 134.f; + srcf.z = src.z * 1.027450980392157f - 140.f; + + Luv2RGBConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 255.f); + dst.y = saturate_cast(dstf.y * 255.f); + dst.z = saturate_cast(dstf.z * 255.f); + setAlpha(dst, ColorChannel::max()); + } + + template struct Luv2RGB; + template + struct Luv2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Luv2RGBConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Luv2RGB() {} + __host__ __device__ __forceinline__ Luv2RGB(const Luv2RGB&) {} + }; + template + struct Luv2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Luv2RGBConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Luv2RGB() {} + __host__ __device__ __forceinline__ Luv2RGB(const Luv2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Luv2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + #undef CV_DESCALE + +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_COLOR_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce.hpp new file mode 100644 index 0000000..8af20b0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce.hpp @@ -0,0 +1,365 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_REDUCE_DETAIL_HPP +#define OPENCV_CUDA_REDUCE_DETAIL_HPP + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace reduce_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(val); + + For::loadToSmem(smem, val, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(val) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, val, tid); + } + + template + static __device__ void merge(const PointerTuple& smem, const ValTuple& val, unsigned int tid, unsigned int delta, const OpTuple& op) + { + typename GetType::type>::type reg = thrust::get(smem)[tid + delta]; + thrust::get(smem)[tid] = thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::merge(smem, val, tid, delta, op); + } + template + static __device__ void mergeShfl(const ValTuple& val, unsigned int delta, unsigned int width, const OpTuple& op) + { + typename GetType::type>::type reg = shfl_down(thrust::get(val), delta, width); + thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::mergeShfl(val, delta, width, op); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + + template + static __device__ void merge(const PointerTuple&, const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + template + static __device__ void mergeShfl(const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + }; + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& val, unsigned int tid) + { + smem[tid] = val; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& val, unsigned int tid) + { + val = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, val, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, val, tid); + } + + template + __device__ __forceinline__ void merge(volatile T* smem, T& val, unsigned int tid, unsigned int delta, const Op& op) + { + T reg = smem[tid + delta]; + smem[tid] = val = op(val, reg); + } + template + __device__ __forceinline__ void mergeShfl(T& val, unsigned int delta, unsigned int width, const Op& op) + { + T reg = shfl_down(val, delta, width); + val = op(val, reg); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + unsigned int delta, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::merge(smem, val, tid, delta, op); + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& val, + unsigned int delta, + unsigned int width, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::mergeShfl(val, delta, width, op); + } + + template struct Generic + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + loadToSmem(smem, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(smem, val, tid, 1024, op); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(smem, val, tid, 512, op); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(smem, val, tid, 256, op); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(smem, val, tid, 128, op); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(smem, val, tid, 64, op); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(smem, val, tid, 32, op); + } + + if (tid < 16) + { + merge(smem, val, tid, 16, op); + merge(smem, val, tid, 8, op); + merge(smem, val, tid, 4, op); + merge(smem, val, tid, 2, op); + merge(smem, val, tid, 1, op); + } + } + }; + + template + struct Unroll + { + static __device__ void loopShfl(Reference val, Op op, unsigned int N) + { + mergeShfl(val, I, N, op); + Unroll::loopShfl(val, op, N); + } + static __device__ void loop(Pointer smem, Reference val, unsigned int tid, Op op) + { + merge(smem, val, tid, I, op); + Unroll::loop(smem, val, tid, op); + } + }; + template + struct Unroll<0, Pointer, Reference, Op> + { + static __device__ void loopShfl(Reference, Op, unsigned int) + { + } + static __device__ void loop(Pointer, Reference, unsigned int, Op) + { + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + CV_UNUSED(smem); + CV_UNUSED(tid); + + Unroll::loopShfl(val, op, N); + #else + loadToSmem(smem, val, tid); + + if (tid < N / 2) + Unroll::loop(smem, val, tid, op); + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + const unsigned int laneId = Warp::laneId(); + + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + Unroll<16, Pointer, Reference, Op>::loopShfl(val, op, warpSize); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #else + loadToSmem(smem, val, tid); + + if (laneId < 16) + Unroll<16, Pointer, Reference, Op>::loop(smem, val, tid, op); + + __syncthreads(); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #endif + + __syncthreads(); + + loadFromSmem(smem, val, tid); + + if (tid < 32) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + Unroll::loopShfl(val, op, M); + #else + Unroll::loop(smem, val, tid, op); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_REDUCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce_key_val.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce_key_val.hpp new file mode 100644 index 0000000..df37c17 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/reduce_key_val.hpp @@ -0,0 +1,502 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP +#define OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace reduce_key_val_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(data); + + For::loadToSmem(smem, data, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(data) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, data, tid); + } + + template + static __device__ void copyShfl(const ReferenceTuple& val, unsigned int delta, int width) + { + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + + For::copyShfl(val, delta, width); + } + template + static __device__ void copy(const PointerTuple& svals, const ReferenceTuple& val, unsigned int tid, unsigned int delta) + { + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + + For::copy(svals, val, tid, delta); + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple& key, const ValReferenceTuple& val, const CmpTuple& cmp, unsigned int delta, int width) + { + typename GetType::type>::type reg = shfl_down(thrust::get(key), delta, width); + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(key) = reg; + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + } + + For::mergeShfl(key, val, cmp, delta, width); + } + template + static __device__ void merge(const KeyPointerTuple& skeys, const KeyReferenceTuple& key, + const ValPointerTuple& svals, const ValReferenceTuple& val, + const CmpTuple& cmp, + unsigned int tid, unsigned int delta) + { + typename GetType::type>::type reg = thrust::get(skeys)[tid + delta]; + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(skeys)[tid] = thrust::get(key) = reg; + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + } + + For::merge(skeys, key, svals, val, cmp, tid, delta); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + + template + static __device__ void copyShfl(const ReferenceTuple&, unsigned int, int) + { + } + template + static __device__ void copy(const PointerTuple&, const ReferenceTuple&, unsigned int, unsigned int) + { + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple&, const ValReferenceTuple&, const CmpTuple&, unsigned int, int) + { + } + template + static __device__ void merge(const KeyPointerTuple&, const KeyReferenceTuple&, + const ValPointerTuple&, const ValReferenceTuple&, + const CmpTuple&, + unsigned int, unsigned int) + { + } + }; + + ////////////////////////////////////////////////////// + // loadToSmem + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& data, unsigned int tid) + { + smem[tid] = data; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& data, unsigned int tid) + { + data = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, data, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, data, tid); + } + + ////////////////////////////////////////////////////// + // copyVals + + template + __device__ __forceinline__ void copyValsShfl(V& val, unsigned int delta, int width) + { + val = shfl_down(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(volatile V* svals, V& val, unsigned int tid, unsigned int delta) + { + svals[tid] = val = svals[tid + delta]; + } + template + __device__ __forceinline__ void copyValsShfl(const thrust::tuple& val, + unsigned int delta, + int width) + { + For<0, thrust::tuple_size >::value>::copyShfl(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::copy(svals, val, tid, delta); + } + + ////////////////////////////////////////////////////// + // merge + + template + __device__ __forceinline__ void mergeShfl(K& key, V& val, const Cmp& cmp, unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, volatile V* svals, V& val, const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(K& key, + const thrust::tuple& val, + const Cmp& cmp, + unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& key, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int delta, int width) + { + For<0, thrust::tuple_size >::value>::mergeShfl(key, val, cmp, delta, width); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::merge(skeys, key, svals, val, cmp, tid, delta); + } + + ////////////////////////////////////////////////////// + // Generic + + template struct Generic + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + loadToSmem(skeys, key, tid); + loadValsToSmem(svals, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(skeys, key, svals, val, cmp, tid, 1024); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(skeys, key, svals, val, cmp, tid, 512); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(skeys, key, svals, val, cmp, tid, 256); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(skeys, key, svals, val, cmp, tid, 128); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(skeys, key, svals, val, cmp, tid, 64); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(skeys, key, svals, val, cmp, tid, 32); + } + + if (tid < 16) + { + merge(skeys, key, svals, val, cmp, tid, 16); + merge(skeys, key, svals, val, cmp, tid, 8); + merge(skeys, key, svals, val, cmp, tid, 4); + merge(skeys, key, svals, val, cmp, tid, 2); + merge(skeys, key, svals, val, cmp, tid, 1); + } + } + }; + + template + struct Unroll + { + static __device__ void loopShfl(KR key, VR val, Cmp cmp, unsigned int N) + { + mergeShfl(key, val, cmp, I, N); + Unroll::loopShfl(key, val, cmp, N); + } + static __device__ void loop(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + merge(skeys, key, svals, val, cmp, tid, I); + Unroll::loop(skeys, key, svals, val, tid, cmp); + } + }; + template + struct Unroll<0, KP, KR, VP, VR, Cmp> + { + static __device__ void loopShfl(KR, VR, Cmp, unsigned int) + { + } + static __device__ void loop(KP, KR, VP, VR, unsigned int, Cmp) + { + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + #if 0 // __CUDA_ARCH__ >= 300 + CV_UNUSED(skeys); + CV_UNUSED(svals); + CV_UNUSED(tid); + + Unroll::loopShfl(key, val, cmp, N); + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (tid < N / 2) + Unroll::loop(skeys, key, svals, val, tid, cmp); + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + const unsigned int laneId = Warp::laneId(); + + #if 0 // __CUDA_ARCH__ >= 300 + Unroll<16, KP, KR, VP, VR, Cmp>::loopShfl(key, val, cmp, warpSize); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (laneId < 16) + Unroll<16, KP, KR, VP, VR, Cmp>::loop(skeys, key, svals, val, tid, cmp); + + __syncthreads(); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #endif + + __syncthreads(); + + loadFromSmem(skeys, key, tid); + + if (tid < 32) + { + #if 0 // __CUDA_ARCH__ >= 300 + loadFromSmem(svals, val, tid); + + Unroll::loopShfl(key, val, cmp, M); + #else + Unroll::loop(skeys, key, svals, val, tid, cmp); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/transform_detail.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/transform_detail.hpp new file mode 100644 index 0000000..1919848 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/transform_detail.hpp @@ -0,0 +1,392 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TRANSFORM_DETAIL_HPP +#define OPENCV_CUDA_TRANSFORM_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" +#include "../functional.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace transform_detail + { + //! Read Write Traits + + template struct UnaryReadWriteTraits + { + typedef typename TypeVec::vec_type read_type; + typedef typename TypeVec::vec_type write_type; + }; + + template struct BinaryReadWriteTraits + { + typedef typename TypeVec::vec_type read_type1; + typedef typename TypeVec::vec_type read_type2; + typedef typename TypeVec::vec_type write_type; + }; + + //! Transform kernels + + template struct OpUnroller; + template <> struct OpUnroller<1> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + } + }; + template <> struct OpUnroller<2> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + } + }; + template <> struct OpUnroller<3> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src.z); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src1.z, src2.z); + } + }; + template <> struct OpUnroller<4> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src.z); + if (mask(y, x_shifted + 3)) + dst.w = op(src.w); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src1.z, src2.z); + if (mask(y, x_shifted + 3)) + dst.w = op(src1.w, src2.w); + } + }; + template <> struct OpUnroller<8> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.a0 = op(src.a0); + if (mask(y, x_shifted + 1)) + dst.a1 = op(src.a1); + if (mask(y, x_shifted + 2)) + dst.a2 = op(src.a2); + if (mask(y, x_shifted + 3)) + dst.a3 = op(src.a3); + if (mask(y, x_shifted + 4)) + dst.a4 = op(src.a4); + if (mask(y, x_shifted + 5)) + dst.a5 = op(src.a5); + if (mask(y, x_shifted + 6)) + dst.a6 = op(src.a6); + if (mask(y, x_shifted + 7)) + dst.a7 = op(src.a7); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.a0 = op(src1.a0, src2.a0); + if (mask(y, x_shifted + 1)) + dst.a1 = op(src1.a1, src2.a1); + if (mask(y, x_shifted + 2)) + dst.a2 = op(src1.a2, src2.a2); + if (mask(y, x_shifted + 3)) + dst.a3 = op(src1.a3, src2.a3); + if (mask(y, x_shifted + 4)) + dst.a4 = op(src1.a4, src2.a4); + if (mask(y, x_shifted + 5)) + dst.a5 = op(src1.a5, src2.a5); + if (mask(y, x_shifted + 6)) + dst.a6 = op(src1.a6, src2.a6); + if (mask(y, x_shifted + 7)) + dst.a7 = op(src1.a7, src2.a7); + } + }; + + template + static __global__ void transformSmart(const PtrStepSz src_, PtrStep dst_, const Mask mask, const UnOp op) + { + typedef TransformFunctorTraits ft; + typedef typename UnaryReadWriteTraits::read_type read_type; + typedef typename UnaryReadWriteTraits::write_type write_type; + + const int x = threadIdx.x + blockIdx.x * blockDim.x; + const int y = threadIdx.y + blockIdx.y * blockDim.y; + const int x_shifted = x * ft::smart_shift; + + if (y < src_.rows) + { + const T* src = src_.ptr(y); + D* dst = dst_.ptr(y); + + if (x_shifted + ft::smart_shift - 1 < src_.cols) + { + const read_type src_n_el = ((const read_type*)src)[x]; + OpUnroller::unroll(src_n_el, ((write_type*)dst)[x], mask, op, x_shifted, y); + } + else + { + for (int real_x = x_shifted; real_x < src_.cols; ++real_x) + { + if (mask(y, real_x)) + dst[real_x] = op(src[real_x]); + } + } + } + } + + template + __global__ static void transformSimple(const PtrStepSz src, PtrStep dst, const Mask mask, const UnOp op) + { + const int x = blockDim.x * blockIdx.x + threadIdx.x; + const int y = blockDim.y * blockIdx.y + threadIdx.y; + + if (x < src.cols && y < src.rows && mask(y, x)) + { + dst.ptr(y)[x] = op(src.ptr(y)[x]); + } + } + + template + static __global__ void transformSmart(const PtrStepSz src1_, const PtrStep src2_, PtrStep dst_, + const Mask mask, const BinOp op) + { + typedef TransformFunctorTraits ft; + typedef typename BinaryReadWriteTraits::read_type1 read_type1; + typedef typename BinaryReadWriteTraits::read_type2 read_type2; + typedef typename BinaryReadWriteTraits::write_type write_type; + + const int x = threadIdx.x + blockIdx.x * blockDim.x; + const int y = threadIdx.y + blockIdx.y * blockDim.y; + const int x_shifted = x * ft::smart_shift; + + if (y < src1_.rows) + { + const T1* src1 = src1_.ptr(y); + const T2* src2 = src2_.ptr(y); + D* dst = dst_.ptr(y); + + if (x_shifted + ft::smart_shift - 1 < src1_.cols) + { + const read_type1 src1_n_el = ((const read_type1*)src1)[x]; + const read_type2 src2_n_el = ((const read_type2*)src2)[x]; + + OpUnroller::unroll(src1_n_el, src2_n_el, ((write_type*)dst)[x], mask, op, x_shifted, y); + } + else + { + for (int real_x = x_shifted; real_x < src1_.cols; ++real_x) + { + if (mask(y, real_x)) + dst[real_x] = op(src1[real_x], src2[real_x]); + } + } + } + } + + template + static __global__ void transformSimple(const PtrStepSz src1, const PtrStep src2, PtrStep dst, + const Mask mask, const BinOp op) + { + const int x = blockDim.x * blockIdx.x + threadIdx.x; + const int y = blockDim.y * blockIdx.y + threadIdx.y; + + if (x < src1.cols && y < src1.rows && mask(y, x)) + { + const T1 src1_data = src1.ptr(y)[x]; + const T2 src2_data = src2.ptr(y)[x]; + dst.ptr(y)[x] = op(src1_data, src2_data); + } + } + + template struct TransformDispatcher; + template<> struct TransformDispatcher + { + template + static void call(PtrStepSz src, PtrStepSz dst, UnOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + const dim3 threads(ft::simple_block_dim_x, ft::simple_block_dim_y, 1); + const dim3 grid(divUp(src.cols, threads.x), divUp(src.rows, threads.y), 1); + + transformSimple<<>>(src, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + + template + static void call(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + const dim3 threads(ft::simple_block_dim_x, ft::simple_block_dim_y, 1); + const dim3 grid(divUp(src1.cols, threads.x), divUp(src1.rows, threads.y), 1); + + transformSimple<<>>(src1, src2, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; + template<> struct TransformDispatcher + { + template + static void call(PtrStepSz src, PtrStepSz dst, UnOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + CV_StaticAssert(ft::smart_shift != 1, ""); + + if (!isAligned(src.data, ft::smart_shift * sizeof(T)) || !isAligned(src.step, ft::smart_shift * sizeof(T)) || + !isAligned(dst.data, ft::smart_shift * sizeof(D)) || !isAligned(dst.step, ft::smart_shift * sizeof(D))) + { + TransformDispatcher::call(src, dst, op, mask, stream); + return; + } + + const dim3 threads(ft::smart_block_dim_x, ft::smart_block_dim_y, 1); + const dim3 grid(divUp(src.cols, threads.x * ft::smart_shift), divUp(src.rows, threads.y), 1); + + transformSmart<<>>(src, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + + template + static void call(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + CV_StaticAssert(ft::smart_shift != 1, ""); + + if (!isAligned(src1.data, ft::smart_shift * sizeof(T1)) || !isAligned(src1.step, ft::smart_shift * sizeof(T1)) || + !isAligned(src2.data, ft::smart_shift * sizeof(T2)) || !isAligned(src2.step, ft::smart_shift * sizeof(T2)) || + !isAligned(dst.data, ft::smart_shift * sizeof(D)) || !isAligned(dst.step, ft::smart_shift * sizeof(D))) + { + TransformDispatcher::call(src1, src2, dst, op, mask, stream); + return; + } + + const dim3 threads(ft::smart_block_dim_x, ft::smart_block_dim_y, 1); + const dim3 grid(divUp(src1.cols, threads.x * ft::smart_shift), divUp(src1.rows, threads.y), 1); + + transformSmart<<>>(src1, src2, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; + } // namespace transform_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_TRANSFORM_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/type_traits_detail.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/type_traits_detail.hpp new file mode 100644 index 0000000..a78bd2c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/type_traits_detail.hpp @@ -0,0 +1,191 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP +#define OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace type_traits_detail + { + template struct Select { typedef T1 type; }; + template struct Select { typedef T2 type; }; + + template struct IsSignedIntergral { enum {value = 0}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + + template struct IsUnsignedIntegral { enum {value = 0}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + + template struct IsIntegral { enum {value = IsSignedIntergral::value || IsUnsignedIntegral::value}; }; + template <> struct IsIntegral { enum {value = 1}; }; + template <> struct IsIntegral { enum {value = 1}; }; + + template struct IsFloat { enum {value = 0}; }; + template <> struct IsFloat { enum {value = 1}; }; + template <> struct IsFloat { enum {value = 1}; }; + + template struct IsVec { enum {value = 0}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + + template struct AddParameterType { typedef const U& type; }; + template struct AddParameterType { typedef U& type; }; + template <> struct AddParameterType { typedef void type; }; + + template struct ReferenceTraits + { + enum { value = false }; + typedef U type; + }; + template struct ReferenceTraits + { + enum { value = true }; + typedef U type; + }; + + template struct PointerTraits + { + enum { value = false }; + typedef void type; + }; + template struct PointerTraits + { + enum { value = true }; + typedef U type; + }; + template struct PointerTraits + { + enum { value = true }; + typedef U type; + }; + + template struct UnConst + { + typedef U type; + enum { value = 0 }; + }; + template struct UnConst + { + typedef U type; + enum { value = 1 }; + }; + template struct UnConst + { + typedef U& type; + enum { value = 1 }; + }; + + template struct UnVolatile + { + typedef U type; + enum { value = 0 }; + }; + template struct UnVolatile + { + typedef U type; + enum { value = 1 }; + }; + template struct UnVolatile + { + typedef U& type; + enum { value = 1 }; + }; + } // namespace type_traits_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/vec_distance_detail.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/vec_distance_detail.hpp new file mode 100644 index 0000000..8283a99 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/detail/vec_distance_detail.hpp @@ -0,0 +1,121 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP +#define OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP + +#include "../datamov_utils.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace vec_distance_detail + { + template struct UnrollVecDiffCached + { + template + static __device__ void calcCheck(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int ind) + { + if (ind < len) + { + T1 val1 = *vecCached++; + + T2 val2; + ForceGlob::Load(vecGlob, ind, val2); + + dist.reduceIter(val1, val2); + + UnrollVecDiffCached::calcCheck(vecCached, vecGlob, len, dist, ind + THREAD_DIM); + } + } + + template + static __device__ void calcWithoutCheck(const T1* vecCached, const T2* vecGlob, Dist& dist) + { + T1 val1 = *vecCached++; + + T2 val2; + ForceGlob::Load(vecGlob, 0, val2); + vecGlob += THREAD_DIM; + + dist.reduceIter(val1, val2); + + UnrollVecDiffCached::calcWithoutCheck(vecCached, vecGlob, dist); + } + }; + template struct UnrollVecDiffCached + { + template + static __device__ __forceinline__ void calcCheck(const T1*, const T2*, int, Dist&, int) + { + } + + template + static __device__ __forceinline__ void calcWithoutCheck(const T1*, const T2*, Dist&) + { + } + }; + + template struct VecDiffCachedCalculator; + template struct VecDiffCachedCalculator + { + template + static __device__ __forceinline__ void calc(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int tid) + { + UnrollVecDiffCached::calcCheck(vecCached, vecGlob, len, dist, tid); + } + }; + template struct VecDiffCachedCalculator + { + template + static __device__ __forceinline__ void calc(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int tid) + { + UnrollVecDiffCached::calcWithoutCheck(vecCached, vecGlob + tid, dist); + } + }; + } // namespace vec_distance_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/dynamic_smem.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/dynamic_smem.hpp new file mode 100644 index 0000000..42570c6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/dynamic_smem.hpp @@ -0,0 +1,88 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DYNAMIC_SMEM_HPP +#define OPENCV_CUDA_DYNAMIC_SMEM_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct DynamicSharedMem + { + __device__ __forceinline__ operator T*() + { + extern __shared__ int __smem[]; + return (T*)__smem; + } + + __device__ __forceinline__ operator const T*() const + { + extern __shared__ int __smem[]; + return (T*)__smem; + } + }; + + // specialize for double to avoid unaligned memory access compile errors + template<> struct DynamicSharedMem + { + __device__ __forceinline__ operator double*() + { + extern __shared__ double __smem_d[]; + return (double*)__smem_d; + } + + __device__ __forceinline__ operator const double*() const + { + extern __shared__ double __smem_d[]; + return (double*)__smem_d; + } + }; +}}} + +//! @endcond + +#endif // OPENCV_CUDA_DYNAMIC_SMEM_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/emulation.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/emulation.hpp new file mode 100644 index 0000000..17dc117 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/emulation.hpp @@ -0,0 +1,269 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_EMULATION_HPP_ +#define OPENCV_CUDA_EMULATION_HPP_ + +#include "common.hpp" +#include "warp_reduce.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Emulation + { + + static __device__ __forceinline__ int syncthreadsOr(int pred) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 200) + // just campilation stab + return 0; +#else + return __syncthreads_or(pred); +#endif + } + + template + static __forceinline__ __device__ int Ballot(int predicate) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 200) + return __ballot(predicate); +#else + __shared__ volatile int cta_buffer[CTA_SIZE]; + + int tid = threadIdx.x; + cta_buffer[tid] = predicate ? (1 << (tid & 31)) : 0; + return warp_reduce(cta_buffer); +#endif + } + + struct smem + { + enum { TAG_MASK = (1U << ( (sizeof(unsigned int) << 3) - 5U)) - 1U }; + + template + static __device__ __forceinline__ T atomicInc(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count; + unsigned int tag = threadIdx.x << ( (sizeof(unsigned int) << 3) - 5U); + do + { + count = *address & TAG_MASK; + count = tag | (count + 1); + *address = count; + } while (*address != count); + + return (count & TAG_MASK) - 1; +#else + return ::atomicInc(address, val); +#endif + } + + template + static __device__ __forceinline__ T atomicAdd(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count; + unsigned int tag = threadIdx.x << ( (sizeof(unsigned int) << 3) - 5U); + do + { + count = *address & TAG_MASK; + count = tag | (count + val); + *address = count; + } while (*address != count); + + return (count & TAG_MASK) - val; +#else + return ::atomicAdd(address, val); +#endif + } + + template + static __device__ __forceinline__ T atomicMin(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count = ::min(*address, val); + do + { + *address = count; + } while (*address > count); + + return count; +#else + return ::atomicMin(address, val); +#endif + } + }; // struct cmem + + struct glob + { + static __device__ __forceinline__ int atomicAdd(int* address, int val) + { + return ::atomicAdd(address, val); + } + static __device__ __forceinline__ unsigned int atomicAdd(unsigned int* address, unsigned int val) + { + return ::atomicAdd(address, val); + } + static __device__ __forceinline__ float atomicAdd(float* address, float val) + { + #if __CUDA_ARCH__ >= 200 + return ::atomicAdd(address, val); + #else + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(val + __int_as_float(assumed))); + } while (assumed != old); + return __int_as_float(old); + #endif + } + static __device__ __forceinline__ double atomicAdd(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + __longlong_as_double(assumed))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + + static __device__ __forceinline__ int atomicMin(int* address, int val) + { + return ::atomicMin(address, val); + } + static __device__ __forceinline__ float atomicMin(float* address, float val) + { + #if __CUDA_ARCH__ >= 120 + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(::fminf(val, __int_as_float(assumed)))); + } while (assumed != old); + return __int_as_float(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0f; + #endif + } + static __device__ __forceinline__ double atomicMin(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(::fmin(val, __longlong_as_double(assumed)))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + + static __device__ __forceinline__ int atomicMax(int* address, int val) + { + return ::atomicMax(address, val); + } + static __device__ __forceinline__ float atomicMax(float* address, float val) + { + #if __CUDA_ARCH__ >= 120 + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(::fmaxf(val, __int_as_float(assumed)))); + } while (assumed != old); + return __int_as_float(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0f; + #endif + } + static __device__ __forceinline__ double atomicMax(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(::fmax(val, __longlong_as_double(assumed)))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + }; + }; //struct Emulation +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_EMULATION_HPP_ */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/filters.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/filters.hpp new file mode 100644 index 0000000..bb94212 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/filters.hpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_FILTERS_HPP +#define OPENCV_CUDA_FILTERS_HPP + +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "vec_math.hpp" +#include "type_traits.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct PointFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ PointFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + return src(__float2int_rz(y), __float2int_rz(x)); + } + + Ptr2D src; + }; + + template struct LinearFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ LinearFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + typedef typename TypeVec::cn>::vec_type work_type; + + work_type out = VecTraits::all(0); + + const int x1 = __float2int_rd(x); + const int y1 = __float2int_rd(y); + const int x2 = x1 + 1; + const int y2 = y1 + 1; + + elem_type src_reg = src(y1, x1); + out = out + src_reg * ((x2 - x) * (y2 - y)); + + src_reg = src(y1, x2); + out = out + src_reg * ((x - x1) * (y2 - y)); + + src_reg = src(y2, x1); + out = out + src_reg * ((x2 - x) * (y - y1)); + + src_reg = src(y2, x2); + out = out + src_reg * ((x - x1) * (y - y1)); + + return saturate_cast(out); + } + + Ptr2D src; + }; + + template struct CubicFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + typedef typename TypeVec::cn>::vec_type work_type; + + explicit __host__ __device__ __forceinline__ CubicFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + + static __device__ __forceinline__ float bicubicCoeff(float x_) + { + float x = fabsf(x_); + if (x <= 1.0f) + { + return x * x * (1.5f * x - 2.5f) + 1.0f; + } + else if (x < 2.0f) + { + return x * (x * (-0.5f * x + 2.5f) - 4.0f) + 2.0f; + } + else + { + return 0.0f; + } + } + + __device__ elem_type operator ()(float y, float x) const + { + const float xmin = ::ceilf(x - 2.0f); + const float xmax = ::floorf(x + 2.0f); + + const float ymin = ::ceilf(y - 2.0f); + const float ymax = ::floorf(y + 2.0f); + + work_type sum = VecTraits::all(0); + float wsum = 0.0f; + + for (float cy = ymin; cy <= ymax; cy += 1.0f) + { + for (float cx = xmin; cx <= xmax; cx += 1.0f) + { + const float w = bicubicCoeff(x - cx) * bicubicCoeff(y - cy); + sum = sum + w * src(__float2int_rd(cy), __float2int_rd(cx)); + wsum += w; + } + } + + work_type res = (!wsum)? VecTraits::all(0) : sum / wsum; + + return saturate_cast(res); + } + + Ptr2D src; + }; + // for integer scaling + template struct IntegerAreaFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ IntegerAreaFilter(const Ptr2D& src_, float scale_x_, float scale_y_) + : src(src_), scale_x(scale_x_), scale_y(scale_y_), scale(1.f / (scale_x * scale_y)) {} + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + float fsx1 = x * scale_x; + float fsx2 = fsx1 + scale_x; + + int sx1 = __float2int_ru(fsx1); + int sx2 = __float2int_rd(fsx2); + + float fsy1 = y * scale_y; + float fsy2 = fsy1 + scale_y; + + int sy1 = __float2int_ru(fsy1); + int sy2 = __float2int_rd(fsy2); + + typedef typename TypeVec::cn>::vec_type work_type; + work_type out = VecTraits::all(0.f); + + for(int dy = sy1; dy < sy2; ++dy) + for(int dx = sx1; dx < sx2; ++dx) + { + out = out + src(dy, dx) * scale; + } + + return saturate_cast(out); + } + + Ptr2D src; + float scale_x, scale_y ,scale; + }; + + template struct AreaFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ AreaFilter(const Ptr2D& src_, float scale_x_, float scale_y_) + : src(src_), scale_x(scale_x_), scale_y(scale_y_){} + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + float fsx1 = x * scale_x; + float fsx2 = fsx1 + scale_x; + + int sx1 = __float2int_ru(fsx1); + int sx2 = __float2int_rd(fsx2); + + float fsy1 = y * scale_y; + float fsy2 = fsy1 + scale_y; + + int sy1 = __float2int_ru(fsy1); + int sy2 = __float2int_rd(fsy2); + + float scale = 1.f / (fminf(scale_x, src.width - fsx1) * fminf(scale_y, src.height - fsy1)); + + typedef typename TypeVec::cn>::vec_type work_type; + work_type out = VecTraits::all(0.f); + + for (int dy = sy1; dy < sy2; ++dy) + { + for (int dx = sx1; dx < sx2; ++dx) + out = out + src(dy, dx) * scale; + + if (sx1 > fsx1) + out = out + src(dy, (sx1 -1) ) * ((sx1 - fsx1) * scale); + + if (sx2 < fsx2) + out = out + src(dy, sx2) * ((fsx2 -sx2) * scale); + } + + if (sy1 > fsy1) + for (int dx = sx1; dx < sx2; ++dx) + out = out + src( (sy1 - 1) , dx) * ((sy1 -fsy1) * scale); + + if (sy2 < fsy2) + for (int dx = sx1; dx < sx2; ++dx) + out = out + src(sy2, dx) * ((fsy2 -sy2) * scale); + + if ((sy1 > fsy1) && (sx1 > fsx1)) + out = out + src( (sy1 - 1) , (sx1 - 1)) * ((sy1 -fsy1) * (sx1 -fsx1) * scale); + + if ((sy1 > fsy1) && (sx2 < fsx2)) + out = out + src( (sy1 - 1) , sx2) * ((sy1 -fsy1) * (fsx2 -sx2) * scale); + + if ((sy2 < fsy2) && (sx2 < fsx2)) + out = out + src(sy2, sx2) * ((fsy2 -sy2) * (fsx2 -sx2) * scale); + + if ((sy2 < fsy2) && (sx1 > fsx1)) + out = out + src(sy2, (sx1 - 1)) * ((fsy2 -sy2) * (sx1 -fsx1) * scale); + + return saturate_cast(out); + } + + Ptr2D src; + float scale_x, scale_y; + int width, haight; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_FILTERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/funcattrib.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/funcattrib.hpp new file mode 100644 index 0000000..f582080 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/funcattrib.hpp @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP +#define OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP + +#include + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + void printFuncAttrib(Func& func) + { + + cudaFuncAttributes attrs; + cudaFuncGetAttributes(&attrs, func); + + printf("=== Function stats ===\n"); + printf("Name: \n"); + printf("sharedSizeBytes = %d\n", attrs.sharedSizeBytes); + printf("constSizeBytes = %d\n", attrs.constSizeBytes); + printf("localSizeBytes = %d\n", attrs.localSizeBytes); + printf("maxThreadsPerBlock = %d\n", attrs.maxThreadsPerBlock); + printf("numRegs = %d\n", attrs.numRegs); + printf("ptxVersion = %d\n", attrs.ptxVersion); + printf("binaryVersion = %d\n", attrs.binaryVersion); + printf("\n"); + fflush(stdout); + } +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/functional.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/functional.hpp new file mode 100644 index 0000000..3b531a1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/functional.hpp @@ -0,0 +1,810 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_FUNCTIONAL_HPP +#define OPENCV_CUDA_FUNCTIONAL_HPP + +#include +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "type_traits.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // Function Objects +#ifdef CV_CXX11 + template struct unary_function + { + typedef Argument argument_type; + typedef Result result_type; + }; + template struct binary_function + { + typedef Argument1 first_argument_type; + typedef Argument2 second_argument_type; + typedef Result result_type; + }; +#else + template struct unary_function : public std::unary_function {}; + template struct binary_function : public std::binary_function {}; +#endif + + // Arithmetic Operations + template struct plus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a + b; + } + __host__ __device__ __forceinline__ plus() {} + __host__ __device__ __forceinline__ plus(const plus&) {} + }; + + template struct minus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a - b; + } + __host__ __device__ __forceinline__ minus() {} + __host__ __device__ __forceinline__ minus(const minus&) {} + }; + + template struct multiplies : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a * b; + } + __host__ __device__ __forceinline__ multiplies() {} + __host__ __device__ __forceinline__ multiplies(const multiplies&) {} + }; + + template struct divides : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a / b; + } + __host__ __device__ __forceinline__ divides() {} + __host__ __device__ __forceinline__ divides(const divides&) {} + }; + + template struct modulus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a % b; + } + __host__ __device__ __forceinline__ modulus() {} + __host__ __device__ __forceinline__ modulus(const modulus&) {} + }; + + template struct negate : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a) const + { + return -a; + } + __host__ __device__ __forceinline__ negate() {} + __host__ __device__ __forceinline__ negate(const negate&) {} + }; + + // Comparison Operations + template struct equal_to : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a == b; + } + __host__ __device__ __forceinline__ equal_to() {} + __host__ __device__ __forceinline__ equal_to(const equal_to&) {} + }; + + template struct not_equal_to : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a != b; + } + __host__ __device__ __forceinline__ not_equal_to() {} + __host__ __device__ __forceinline__ not_equal_to(const not_equal_to&) {} + }; + + template struct greater : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a > b; + } + __host__ __device__ __forceinline__ greater() {} + __host__ __device__ __forceinline__ greater(const greater&) {} + }; + + template struct less : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a < b; + } + __host__ __device__ __forceinline__ less() {} + __host__ __device__ __forceinline__ less(const less&) {} + }; + + template struct greater_equal : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a >= b; + } + __host__ __device__ __forceinline__ greater_equal() {} + __host__ __device__ __forceinline__ greater_equal(const greater_equal&) {} + }; + + template struct less_equal : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a <= b; + } + __host__ __device__ __forceinline__ less_equal() {} + __host__ __device__ __forceinline__ less_equal(const less_equal&) {} + }; + + // Logical Operations + template struct logical_and : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a && b; + } + __host__ __device__ __forceinline__ logical_and() {} + __host__ __device__ __forceinline__ logical_and(const logical_and&) {} + }; + + template struct logical_or : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a || b; + } + __host__ __device__ __forceinline__ logical_or() {} + __host__ __device__ __forceinline__ logical_or(const logical_or&) {} + }; + + template struct logical_not : unary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a) const + { + return !a; + } + __host__ __device__ __forceinline__ logical_not() {} + __host__ __device__ __forceinline__ logical_not(const logical_not&) {} + }; + + // Bitwise Operations + template struct bit_and : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a & b; + } + __host__ __device__ __forceinline__ bit_and() {} + __host__ __device__ __forceinline__ bit_and(const bit_and&) {} + }; + + template struct bit_or : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a | b; + } + __host__ __device__ __forceinline__ bit_or() {} + __host__ __device__ __forceinline__ bit_or(const bit_or&) {} + }; + + template struct bit_xor : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a ^ b; + } + __host__ __device__ __forceinline__ bit_xor() {} + __host__ __device__ __forceinline__ bit_xor(const bit_xor&) {} + }; + + template struct bit_not : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType v) const + { + return ~v; + } + __host__ __device__ __forceinline__ bit_not() {} + __host__ __device__ __forceinline__ bit_not(const bit_not&) {} + }; + + // Generalized Identity Operations + template struct identity : unary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType x) const + { + return x; + } + __host__ __device__ __forceinline__ identity() {} + __host__ __device__ __forceinline__ identity(const identity&) {} + }; + + template struct project1st : binary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return lhs; + } + __host__ __device__ __forceinline__ project1st() {} + __host__ __device__ __forceinline__ project1st(const project1st&) {} + }; + + template struct project2nd : binary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return rhs; + } + __host__ __device__ __forceinline__ project2nd() {} + __host__ __device__ __forceinline__ project2nd(const project2nd&) {} + }; + + // Min/Max Operations + +#define OPENCV_CUDA_IMPLEMENT_MINMAX(name, type, op) \ + template <> struct name : binary_function \ + { \ + __device__ __forceinline__ type operator()(type lhs, type rhs) const {return op(lhs, rhs);} \ + __host__ __device__ __forceinline__ name() {}\ + __host__ __device__ __forceinline__ name(const name&) {}\ + }; + + template struct maximum : binary_function + { + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return max(lhs, rhs); + } + __host__ __device__ __forceinline__ maximum() {} + __host__ __device__ __forceinline__ maximum(const maximum&) {} + }; + + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, uchar, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, schar, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, char, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, ushort, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, short, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, int, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, uint, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, float, ::fmax) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, double, ::fmax) + + template struct minimum : binary_function + { + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return min(lhs, rhs); + } + __host__ __device__ __forceinline__ minimum() {} + __host__ __device__ __forceinline__ minimum(const minimum&) {} + }; + + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, uchar, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, schar, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, char, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, ushort, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, short, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, int, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, uint, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, float, ::fmin) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, double, ::fmin) + +#undef OPENCV_CUDA_IMPLEMENT_MINMAX + + // Math functions + + template struct abs_func : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType x) const + { + return abs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned char operator ()(unsigned char x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ signed char operator ()(signed char x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ char operator ()(char x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned short operator ()(unsigned short x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ short operator ()(short x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned int operator ()(unsigned int x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ int operator ()(int x) const + { + return ::abs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ float operator ()(float x) const + { + return ::fabsf(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ double operator ()(double x) const + { + return ::fabs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + +#define OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(name, func) \ + template struct name ## _func : unary_function \ + { \ + __device__ __forceinline__ float operator ()(typename TypeTraits::ParameterType v) const \ + { \ + return func ## f(v); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; \ + template <> struct name ## _func : unary_function \ + { \ + __device__ __forceinline__ double operator ()(double v) const \ + { \ + return func(v); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; + +#define OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(name, func) \ + template struct name ## _func : binary_function \ + { \ + __device__ __forceinline__ float operator ()(typename TypeTraits::ParameterType v1, typename TypeTraits::ParameterType v2) const \ + { \ + return func ## f(v1, v2); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; \ + template <> struct name ## _func : binary_function \ + { \ + __device__ __forceinline__ double operator ()(double v1, double v2) const \ + { \ + return func(v1, v2); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; + + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sqrt, ::sqrt) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp, ::exp) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp2, ::exp2) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp10, ::exp10) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log, ::log) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log2, ::log2) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log10, ::log10) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sin, ::sin) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(cos, ::cos) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(tan, ::tan) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(asin, ::asin) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(acos, ::acos) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(atan, ::atan) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sinh, ::sinh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(cosh, ::cosh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(tanh, ::tanh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(asinh, ::asinh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(acosh, ::acosh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(atanh, ::atanh) + + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(hypot, ::hypot) + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(atan2, ::atan2) + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(pow, ::pow) + + #undef OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR + #undef OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR_NO_DOUBLE + #undef OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR + + template struct hypot_sqr_func : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType src1, typename TypeTraits::ParameterType src2) const + { + return src1 * src1 + src2 * src2; + } + __host__ __device__ __forceinline__ hypot_sqr_func() {} + __host__ __device__ __forceinline__ hypot_sqr_func(const hypot_sqr_func&) {} + }; + + // Saturate Cast Functor + template struct saturate_cast_func : unary_function + { + __device__ __forceinline__ D operator ()(typename TypeTraits::ParameterType v) const + { + return saturate_cast(v); + } + __host__ __device__ __forceinline__ saturate_cast_func() {} + __host__ __device__ __forceinline__ saturate_cast_func(const saturate_cast_func&) {} + }; + + // Threshold Functors + template struct thresh_binary_func : unary_function + { + __host__ __device__ __forceinline__ thresh_binary_func(T thresh_, T maxVal_) : thresh(thresh_), maxVal(maxVal_) {} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src > thresh) * maxVal; + } + + __host__ __device__ __forceinline__ thresh_binary_func() {} + __host__ __device__ __forceinline__ thresh_binary_func(const thresh_binary_func& other) + : thresh(other.thresh), maxVal(other.maxVal) {} + + T thresh; + T maxVal; + }; + + template struct thresh_binary_inv_func : unary_function + { + __host__ __device__ __forceinline__ thresh_binary_inv_func(T thresh_, T maxVal_) : thresh(thresh_), maxVal(maxVal_) {} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src <= thresh) * maxVal; + } + + __host__ __device__ __forceinline__ thresh_binary_inv_func() {} + __host__ __device__ __forceinline__ thresh_binary_inv_func(const thresh_binary_inv_func& other) + : thresh(other.thresh), maxVal(other.maxVal) {} + + T thresh; + T maxVal; + }; + + template struct thresh_trunc_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_trunc_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return minimum()(src, thresh); + } + + __host__ __device__ __forceinline__ thresh_trunc_func() {} + __host__ __device__ __forceinline__ thresh_trunc_func(const thresh_trunc_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + template struct thresh_to_zero_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_to_zero_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src > thresh) * src; + } + + __host__ __device__ __forceinline__ thresh_to_zero_func() {} + __host__ __device__ __forceinline__ thresh_to_zero_func(const thresh_to_zero_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + template struct thresh_to_zero_inv_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_to_zero_inv_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src <= thresh) * src; + } + + __host__ __device__ __forceinline__ thresh_to_zero_inv_func() {} + __host__ __device__ __forceinline__ thresh_to_zero_inv_func(const thresh_to_zero_inv_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + // Function Object Adaptors + template struct unary_negate : unary_function + { + explicit __host__ __device__ __forceinline__ unary_negate(const Predicate& p) : pred(p) {} + + __device__ __forceinline__ bool operator()(typename TypeTraits::ParameterType x) const + { + return !pred(x); + } + + __host__ __device__ __forceinline__ unary_negate() {} + __host__ __device__ __forceinline__ unary_negate(const unary_negate& other) : pred(other.pred) {} + + Predicate pred; + }; + + template __host__ __device__ __forceinline__ unary_negate not1(const Predicate& pred) + { + return unary_negate(pred); + } + + template struct binary_negate : binary_function + { + explicit __host__ __device__ __forceinline__ binary_negate(const Predicate& p) : pred(p) {} + + __device__ __forceinline__ bool operator()(typename TypeTraits::ParameterType x, + typename TypeTraits::ParameterType y) const + { + return !pred(x,y); + } + + __host__ __device__ __forceinline__ binary_negate() {} + __host__ __device__ __forceinline__ binary_negate(const binary_negate& other) : pred(other.pred) {} + + Predicate pred; + }; + + template __host__ __device__ __forceinline__ binary_negate not2(const BinaryPredicate& pred) + { + return binary_negate(pred); + } + + template struct binder1st : unary_function + { + __host__ __device__ __forceinline__ binder1st(const Op& op_, const typename Op::first_argument_type& arg1_) : op(op_), arg1(arg1_) {} + + __device__ __forceinline__ typename Op::result_type operator ()(typename TypeTraits::ParameterType a) const + { + return op(arg1, a); + } + + __host__ __device__ __forceinline__ binder1st() {} + __host__ __device__ __forceinline__ binder1st(const binder1st& other) : op(other.op), arg1(other.arg1) {} + + Op op; + typename Op::first_argument_type arg1; + }; + + template __host__ __device__ __forceinline__ binder1st bind1st(const Op& op, const T& x) + { + return binder1st(op, typename Op::first_argument_type(x)); + } + + template struct binder2nd : unary_function + { + __host__ __device__ __forceinline__ binder2nd(const Op& op_, const typename Op::second_argument_type& arg2_) : op(op_), arg2(arg2_) {} + + __forceinline__ __device__ typename Op::result_type operator ()(typename TypeTraits::ParameterType a) const + { + return op(a, arg2); + } + + __host__ __device__ __forceinline__ binder2nd() {} + __host__ __device__ __forceinline__ binder2nd(const binder2nd& other) : op(other.op), arg2(other.arg2) {} + + Op op; + typename Op::second_argument_type arg2; + }; + + template __host__ __device__ __forceinline__ binder2nd bind2nd(const Op& op, const T& x) + { + return binder2nd(op, typename Op::second_argument_type(x)); + } + + // Functor Traits + template struct IsUnaryFunction + { + typedef char Yes; + struct No {Yes a[2];}; + + template static Yes check(unary_function); + static No check(...); + + static F makeF(); + + enum { value = (sizeof(check(makeF())) == sizeof(Yes)) }; + }; + + template struct IsBinaryFunction + { + typedef char Yes; + struct No {Yes a[2];}; + + template static Yes check(binary_function); + static No check(...); + + static F makeF(); + + enum { value = (sizeof(check(makeF())) == sizeof(Yes)) }; + }; + + namespace functional_detail + { + template struct UnOpShift { enum { shift = 1 }; }; + template struct UnOpShift { enum { shift = 4 }; }; + template struct UnOpShift { enum { shift = 2 }; }; + + template struct DefaultUnaryShift + { + enum { shift = UnOpShift::shift }; + }; + + template struct BinOpShift { enum { shift = 1 }; }; + template struct BinOpShift { enum { shift = 4 }; }; + template struct BinOpShift { enum { shift = 2 }; }; + + template struct DefaultBinaryShift + { + enum { shift = BinOpShift::shift }; + }; + + template ::value> struct ShiftDispatcher; + template struct ShiftDispatcher + { + enum { shift = DefaultUnaryShift::shift }; + }; + template struct ShiftDispatcher + { + enum { shift = DefaultBinaryShift::shift }; + }; + } + + template struct DefaultTransformShift + { + enum { shift = functional_detail::ShiftDispatcher::shift }; + }; + + template struct DefaultTransformFunctorTraits + { + enum { simple_block_dim_x = 16 }; + enum { simple_block_dim_y = 16 }; + + enum { smart_block_dim_x = 16 }; + enum { smart_block_dim_y = 16 }; + enum { smart_shift = DefaultTransformShift::shift }; + }; + + template struct TransformFunctorTraits : DefaultTransformFunctorTraits {}; + +#define OPENCV_CUDA_TRANSFORM_FUNCTOR_TRAITS(type) \ + template <> struct TransformFunctorTraits< type > : DefaultTransformFunctorTraits< type > +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_FUNCTIONAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/limits.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/limits.hpp new file mode 100644 index 0000000..7e15ed6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/limits.hpp @@ -0,0 +1,128 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_LIMITS_HPP +#define OPENCV_CUDA_LIMITS_HPP + +#include +#include +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ +template struct numeric_limits; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static bool min() { return false; } + __device__ __forceinline__ static bool max() { return true; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static signed char min() { return SCHAR_MIN; } + __device__ __forceinline__ static signed char max() { return SCHAR_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned char min() { return 0; } + __device__ __forceinline__ static unsigned char max() { return UCHAR_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static short min() { return SHRT_MIN; } + __device__ __forceinline__ static short max() { return SHRT_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned short min() { return 0; } + __device__ __forceinline__ static unsigned short max() { return USHRT_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static int min() { return INT_MIN; } + __device__ __forceinline__ static int max() { return INT_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned int min() { return 0; } + __device__ __forceinline__ static unsigned int max() { return UINT_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static float min() { return FLT_MIN; } + __device__ __forceinline__ static float max() { return FLT_MAX; } + __device__ __forceinline__ static float epsilon() { return FLT_EPSILON; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static double min() { return DBL_MIN; } + __device__ __forceinline__ static double max() { return DBL_MAX; } + __device__ __forceinline__ static double epsilon() { return DBL_EPSILON; } + static const bool is_signed = true; +}; +}}} // namespace cv { namespace cuda { namespace cudev { + +//! @endcond + +#endif // OPENCV_CUDA_LIMITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/reduce.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/reduce.hpp new file mode 100644 index 0000000..5de3650 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/reduce.hpp @@ -0,0 +1,209 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_REDUCE_HPP +#define OPENCV_CUDA_REDUCE_HPP + +#ifndef THRUST_DEBUG // eliminate -Wundef warning +#define THRUST_DEBUG 0 +#endif + +#include +#include "detail/reduce.hpp" +#include "detail/reduce_key_val.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + __device__ __forceinline__ void reduce(volatile T* smem, T& val, unsigned int tid, const Op& op) + { + reduce_detail::Dispatcher::reductor::template reduce(smem, val, tid, op); + } + template + __device__ __forceinline__ void reduce(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& op) + { + reduce_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&>(smem, val, tid, op); + } + + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, volatile V* svals, V& val, unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce&, + const thrust::tuple&, + const Cmp&>(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple& + >(skeys, key, svals, val, tid, cmp); + } + + // smem_tuple + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0) + { + return thrust::make_tuple((volatile T0*) t0); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8, T9* t9) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8, (volatile T9*) t9); + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_REDUCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/saturate_cast.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/saturate_cast.hpp new file mode 100644 index 0000000..c3a3d1c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/saturate_cast.hpp @@ -0,0 +1,292 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_SATURATE_CAST_HPP +#define OPENCV_CUDA_SATURATE_CAST_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template __device__ __forceinline__ _Tp saturate_cast(uchar v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(schar v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(ushort v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(short v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(uint v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(int v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(float v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(double v) { return _Tp(v); } + + template<> __device__ __forceinline__ uchar saturate_cast(schar v) + { + uint res = 0; + int vi = v; + asm("cvt.sat.u8.s8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.u8.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(ushort v) + { + uint res = 0; + asm("cvt.sat.u8.u16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.u8.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(uint v) + { + uint res = 0; + asm("cvt.sat.u8.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(float v) + { + uint res = 0; + asm("cvt.rni.sat.u8.f32 %0, %1;" : "=r"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + uint res = 0; + asm("cvt.rni.sat.u8.f64 %0, %1;" : "=r"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ schar saturate_cast(uchar v) + { + uint res = 0; + uint vi = v; + asm("cvt.sat.s8.u8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.s8.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(ushort v) + { + uint res = 0; + asm("cvt.sat.s8.u16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.s8.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(uint v) + { + uint res = 0; + asm("cvt.sat.s8.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(float v) + { + uint res = 0; + asm("cvt.rni.sat.s8.f32 %0, %1;" : "=r"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + uint res = 0; + asm("cvt.rni.sat.s8.f64 %0, %1;" : "=r"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ ushort saturate_cast(schar v) + { + ushort res = 0; + int vi = v; + asm("cvt.sat.u16.s8 %0, %1;" : "=h"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(short v) + { + ushort res = 0; + asm("cvt.sat.u16.s16 %0, %1;" : "=h"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(int v) + { + ushort res = 0; + asm("cvt.sat.u16.s32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(uint v) + { + ushort res = 0; + asm("cvt.sat.u16.u32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(float v) + { + ushort res = 0; + asm("cvt.rni.sat.u16.f32 %0, %1;" : "=h"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + ushort res = 0; + asm("cvt.rni.sat.u16.f64 %0, %1;" : "=h"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ short saturate_cast(ushort v) + { + short res = 0; + asm("cvt.sat.s16.u16 %0, %1;" : "=h"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(int v) + { + short res = 0; + asm("cvt.sat.s16.s32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(uint v) + { + short res = 0; + asm("cvt.sat.s16.u32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(float v) + { + short res = 0; + asm("cvt.rni.sat.s16.f32 %0, %1;" : "=h"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + short res = 0; + asm("cvt.rni.sat.s16.f64 %0, %1;" : "=h"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ int saturate_cast(uint v) + { + int res = 0; + asm("cvt.sat.s32.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ int saturate_cast(float v) + { + return __float2int_rn(v); + } + template<> __device__ __forceinline__ int saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + return __double2int_rn(v); + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ uint saturate_cast(schar v) + { + uint res = 0; + int vi = v; + asm("cvt.sat.u32.s8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.u32.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.u32.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(float v) + { + return __float2uint_rn(v); + } + template<> __device__ __forceinline__ uint saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + return __double2uint_rn(v); + #else + return saturate_cast((float)v); + #endif + } +}}} + +//! @endcond + +#endif /* OPENCV_CUDA_SATURATE_CAST_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/scan.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/scan.hpp new file mode 100644 index 0000000..e128fb0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/scan.hpp @@ -0,0 +1,258 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_SCAN_HPP +#define OPENCV_CUDA_SCAN_HPP + +#include "opencv2/core/cuda/common.hpp" +#include "opencv2/core/cuda/utility.hpp" +#include "opencv2/core/cuda/warp.hpp" +#include "opencv2/core/cuda/warp_shuffle.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + enum ScanKind { EXCLUSIVE = 0, INCLUSIVE = 1 }; + + template struct WarpScan + { + __device__ __forceinline__ WarpScan() {} + __device__ __forceinline__ WarpScan(const WarpScan& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()( volatile T *ptr , const unsigned int idx) + { + const unsigned int lane = idx & 31; + F op; + + if ( lane >= 1) ptr [idx ] = op(ptr [idx - 1], ptr [idx]); + if ( lane >= 2) ptr [idx ] = op(ptr [idx - 2], ptr [idx]); + if ( lane >= 4) ptr [idx ] = op(ptr [idx - 4], ptr [idx]); + if ( lane >= 8) ptr [idx ] = op(ptr [idx - 8], ptr [idx]); + if ( lane >= 16) ptr [idx ] = op(ptr [idx - 16], ptr [idx]); + + if( Kind == INCLUSIVE ) + return ptr [idx]; + else + return (lane > 0) ? ptr [idx - 1] : 0; + } + + __device__ __forceinline__ unsigned int index(const unsigned int tid) + { + return tid; + } + + __device__ __forceinline__ void init(volatile T *ptr){} + + static const int warp_offset = 0; + + typedef WarpScan merge; + }; + + template struct WarpScanNoComp + { + __device__ __forceinline__ WarpScanNoComp() {} + __device__ __forceinline__ WarpScanNoComp(const WarpScanNoComp& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()( volatile T *ptr , const unsigned int idx) + { + const unsigned int lane = threadIdx.x & 31; + F op; + + ptr [idx ] = op(ptr [idx - 1], ptr [idx]); + ptr [idx ] = op(ptr [idx - 2], ptr [idx]); + ptr [idx ] = op(ptr [idx - 4], ptr [idx]); + ptr [idx ] = op(ptr [idx - 8], ptr [idx]); + ptr [idx ] = op(ptr [idx - 16], ptr [idx]); + + if( Kind == INCLUSIVE ) + return ptr [idx]; + else + return (lane > 0) ? ptr [idx - 1] : 0; + } + + __device__ __forceinline__ unsigned int index(const unsigned int tid) + { + return (tid >> warp_log) * warp_smem_stride + 16 + (tid & warp_mask); + } + + __device__ __forceinline__ void init(volatile T *ptr) + { + ptr[threadIdx.x] = 0; + } + + static const int warp_smem_stride = 32 + 16 + 1; + static const int warp_offset = 16; + static const int warp_log = 5; + static const int warp_mask = 31; + + typedef WarpScanNoComp merge; + }; + + template struct BlockScan + { + __device__ __forceinline__ BlockScan() {} + __device__ __forceinline__ BlockScan(const BlockScan& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()(volatile T *ptr) + { + const unsigned int tid = threadIdx.x; + const unsigned int lane = tid & warp_mask; + const unsigned int warp = tid >> warp_log; + + Sc scan; + typename Sc::merge merge_scan; + const unsigned int idx = scan.index(tid); + + T val = scan(ptr, idx); + __syncthreads (); + + if( warp == 0) + scan.init(ptr); + __syncthreads (); + + if( lane == 31 ) + ptr [scan.warp_offset + warp ] = (Kind == INCLUSIVE) ? val : ptr [idx]; + __syncthreads (); + + if( warp == 0 ) + merge_scan(ptr, idx); + __syncthreads(); + + if ( warp > 0) + val = ptr [scan.warp_offset + warp - 1] + val; + __syncthreads (); + + ptr[idx] = val; + __syncthreads (); + + return val ; + } + + static const int warp_log = 5; + static const int warp_mask = 31; + }; + + template + __device__ T warpScanInclusive(T idata, volatile T* s_Data, unsigned int tid) + { + #if __CUDA_ARCH__ >= 300 + const unsigned int laneId = cv::cuda::device::Warp::laneId(); + + // scan on shuffl functions + #pragma unroll + for (int i = 1; i <= (OPENCV_CUDA_WARP_SIZE / 2); i *= 2) + { + const T n = cv::cuda::device::shfl_up(idata, i); + if (laneId >= i) + idata += n; + } + + return idata; + #else + unsigned int pos = 2 * tid - (tid & (OPENCV_CUDA_WARP_SIZE - 1)); + s_Data[pos] = 0; + pos += OPENCV_CUDA_WARP_SIZE; + s_Data[pos] = idata; + + s_Data[pos] += s_Data[pos - 1]; + s_Data[pos] += s_Data[pos - 2]; + s_Data[pos] += s_Data[pos - 4]; + s_Data[pos] += s_Data[pos - 8]; + s_Data[pos] += s_Data[pos - 16]; + + return s_Data[pos]; + #endif + } + + template + __device__ __forceinline__ T warpScanExclusive(T idata, volatile T* s_Data, unsigned int tid) + { + return warpScanInclusive(idata, s_Data, tid) - idata; + } + + template + __device__ T blockScanInclusive(T idata, volatile T* s_Data, unsigned int tid) + { + if (tiNumScanThreads > OPENCV_CUDA_WARP_SIZE) + { + //Bottom-level inclusive warp scan + T warpResult = warpScanInclusive(idata, s_Data, tid); + + //Save top elements of each warp for exclusive warp scan + //sync to wait for warp scans to complete (because s_Data is being overwritten) + __syncthreads(); + if ((tid & (OPENCV_CUDA_WARP_SIZE - 1)) == (OPENCV_CUDA_WARP_SIZE - 1)) + { + s_Data[tid >> OPENCV_CUDA_LOG_WARP_SIZE] = warpResult; + } + + //wait for warp scans to complete + __syncthreads(); + + if (tid < (tiNumScanThreads / OPENCV_CUDA_WARP_SIZE) ) + { + //grab top warp elements + T val = s_Data[tid]; + //calculate exclusive scan and write back to shared memory + s_Data[tid] = warpScanExclusive(val, s_Data, tid); + } + + //return updated warp scans with exclusive scan results + __syncthreads(); + + return warpResult + s_Data[tid >> OPENCV_CUDA_LOG_WARP_SIZE]; + } + else + { + return warpScanInclusive(idata, s_Data, tid); + } + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_SCAN_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/simd_functions.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/simd_functions.hpp new file mode 100644 index 0000000..3d8c2e0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/simd_functions.hpp @@ -0,0 +1,869 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +/* + * Copyright (c) 2013 NVIDIA Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 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. + * + * Neither the name of NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 OPENCV_CUDA_SIMD_FUNCTIONS_HPP +#define OPENCV_CUDA_SIMD_FUNCTIONS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // 2 + + static __device__ __forceinline__ unsigned int vadd2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vadd2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vadd.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = a ^ b; // sum bits + r = a + b; // actual sum + s = s ^ r; // determine carry-ins for each bit position + s = s & 0x00010000; // carry-in to high word (= carry-out from low word) + r = r - s; // subtract out carry-out from low word + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsub2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vsub2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vsub.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = a ^ b; // sum bits + r = a - b; // actual sum + s = s ^ r; // determine carry-ins for each bit position + s = s & 0x00010000; // borrow to high word + r = r + s; // compensate for borrow from low word + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vabsdiff2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vabsdiff2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vabsdiff.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u, v; + s = a & 0x0000ffff; // extract low halfword + r = b & 0x0000ffff; // extract low halfword + u = ::max(r, s); // maximum of low halfwords + v = ::min(r, s); // minimum of low halfwords + s = a & 0xffff0000; // extract high halfword + r = b & 0xffff0000; // extract high halfword + t = ::max(r, s); // maximum of high halfwords + s = ::min(r, s); // minimum of high halfwords + r = u | t; // maximum of both halfwords + s = v | s; // minimum of both halfwords + r = r - s; // |a - b| = max(a,b) - min(a,b); + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vavg2(unsigned int a, unsigned int b) + { + unsigned int r, s; + + // HAKMEM #23: a + b = 2 * (a & b) + (a ^ b) ==> + // (a + b) / 2 = (a & b) + ((a ^ b) >> 1) + s = a ^ b; + r = a & b; + s = s & 0xfffefffe; // ensure shift doesn't cross halfword boundaries + s = s >> 1; + s = r + s; + + return s; + } + + static __device__ __forceinline__ unsigned int vavrg2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vavrg2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // HAKMEM #23: a + b = 2 * (a | b) - (a ^ b) ==> + // (a + b + 1) / 2 = (a | b) - ((a ^ b) >> 1) + unsigned int s; + s = a ^ b; + r = a | b; + s = s & 0xfffefffe; // ensure shift doesn't cross half-word boundaries + s = s >> 1; + r = r - s; + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vseteq2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.eq %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x8000 + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r & ~c; // msb = 1, if r was 0x0000 + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpeq2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vseteq2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x8000 + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r & ~c; // msb = 1, if r was 0x0000 + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetge2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.ge %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg2(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpge2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetge2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg2(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetgt2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.gt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg2(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80008000; // msbs = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpgt2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetgt2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg2(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80008000; // msbs = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetle2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.le %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg2(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmple2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetle2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg2(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetlt2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.lt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg2(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmplt2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetlt2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg2(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetne2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm ("vset2.u32.u32.ne %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r | c; // msb = 1, if r was not 0x0000 + c = c & 0x80008000; // extract msbs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpne2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetne2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r | c; // msb = 1, if r was not 0x0000 + c = c & 0x80008000; // extract msbs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmax2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmax2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmax.u32.u32.u32 %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u; + r = a & 0x0000ffff; // extract low halfword + s = b & 0x0000ffff; // extract low halfword + t = ::max(r, s); // maximum of low halfwords + r = a & 0xffff0000; // extract high halfword + s = b & 0xffff0000; // extract high halfword + u = ::max(r, s); // maximum of high halfwords + r = t | u; // combine halfword maximums + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmin2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmin2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmin.u32.u32.u32 %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u; + r = a & 0x0000ffff; // extract low halfword + s = b & 0x0000ffff; // extract low halfword + t = ::min(r, s); // minimum of low halfwords + r = a & 0xffff0000; // extract high halfword + s = b & 0xffff0000; // extract high halfword + u = ::min(r, s); // minimum of high halfwords + r = t | u; // combine halfword minimums + #endif + + return r; + } + + // 4 + + static __device__ __forceinline__ unsigned int vadd4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vadd4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vadd.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t; + s = a ^ b; // sum bits + r = a & 0x7f7f7f7f; // clear msbs + t = b & 0x7f7f7f7f; // clear msbs + s = s & 0x80808080; // msb sum bits + r = r + t; // add without msbs, record carry-out in msbs + r = r ^ s; // sum of msb sum and carry-in bits, w/o carry-out + #endif /* __CUDA_ARCH__ >= 300 */ + + return r; + } + + static __device__ __forceinline__ unsigned int vsub4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vsub4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vsub.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t; + s = a ^ ~b; // inverted sum bits + r = a | 0x80808080; // set msbs + t = b & 0x7f7f7f7f; // clear msbs + s = s & 0x80808080; // inverted msb sum bits + r = r - t; // subtract w/o msbs, record inverted borrows in msb + r = r ^ s; // combine inverted msb sum bits and borrows + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vavg4(unsigned int a, unsigned int b) + { + unsigned int r, s; + + // HAKMEM #23: a + b = 2 * (a & b) + (a ^ b) ==> + // (a + b) / 2 = (a & b) + ((a ^ b) >> 1) + s = a ^ b; + r = a & b; + s = s & 0xfefefefe; // ensure following shift doesn't cross byte boundaries + s = s >> 1; + s = r + s; + + return s; + } + + static __device__ __forceinline__ unsigned int vavrg4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vavrg4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // HAKMEM #23: a + b = 2 * (a | b) - (a ^ b) ==> + // (a + b + 1) / 2 = (a | b) - ((a ^ b) >> 1) + unsigned int c; + c = a ^ b; + r = a | b; + c = c & 0xfefefefe; // ensure following shift doesn't cross byte boundaries + c = c >> 1; + r = r - c; + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vseteq4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.eq %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x80 + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r & ~c; // msb = 1, if r was 0x00 + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpeq4(unsigned int a, unsigned int b) + { + unsigned int r, t; + + #if __CUDA_ARCH__ >= 300 + r = vseteq4(a, b); + t = r << 8; // convert bool + r = t - r; // to mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + t = a ^ b; // 0x00 if a == b + r = t | 0x80808080; // set msbs, to catch carry out + t = t ^ r; // extract msbs, msb = 1 if t < 0x80 + r = r - 0x01010101; // msb = 0, if t was 0x00 or 0x80 + r = t & ~r; // msb = 1, if t was 0x00 + t = r >> 7; // build mask + t = r - t; // from + r = t | r; // msbs + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetle4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.le %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg4(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmple4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetle4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg4(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80808080; // msbs = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetlt4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.lt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg4(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmplt4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetlt4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg4(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80808080; // msbs = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetge4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.ge %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg4(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpge4(unsigned int a, unsigned int b) + { + unsigned int r, s; + + #if __CUDA_ARCH__ >= 300 + r = vsetge4(a, b); + s = r << 8; // convert bool + r = s - r; // to mask + #else + asm ("not.b32 %0,%0;" : "+r"(b)); + r = vavrg4 (a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + r = r & 0x80808080; // msb = carry-outs + s = r >> 7; // build mask + s = r - s; // from + r = s | r; // msbs + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetgt4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.gt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg4(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpgt4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetgt4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg4(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetne4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.ne %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r | c; // msb = 1, if r was not 0x00 + c = c & 0x80808080; // extract msbs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpne4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetne4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r | c; // msb = 1, if r was not 0x00 + c = c & 0x80808080; // extract msbs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vabsdiff4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vabsdiff4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vabsdiff.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(a, b); // mask = 0xff if a >= b + r = a ^ b; // + s = (r & s) ^ b; // select a when a >= b, else select b => max(a,b) + r = s ^ r; // select a when b >= a, else select b => min(a,b) + r = s - r; // |a - b| = max(a,b) - min(a,b); + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmax4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmax4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmax.u32.u32.u32 %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(a, b); // mask = 0xff if a >= b + r = a & s; // select a when b >= a + s = b & ~s; // select b when b < a + r = r | s; // combine byte selections + #endif + + return r; // byte-wise unsigned maximum + } + + static __device__ __forceinline__ unsigned int vmin4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmin4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmin.u32.u32.u32 %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(b, a); // mask = 0xff if a >= b + r = a & s; // select a when b >= a + s = b & ~s; // select b when b < a + r = r | s; // combine byte selections + #endif + + return r; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_SIMD_FUNCTIONS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/transform.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/transform.hpp new file mode 100644 index 0000000..42aa6ea --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/transform.hpp @@ -0,0 +1,75 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TRANSFORM_HPP +#define OPENCV_CUDA_TRANSFORM_HPP + +#include "common.hpp" +#include "utility.hpp" +#include "detail/transform_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + static inline void transform(PtrStepSz src, PtrStepSz dst, UnOp op, const Mask& mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + transform_detail::TransformDispatcher::cn == 1 && VecTraits::cn == 1 && ft::smart_shift != 1>::call(src, dst, op, mask, stream); + } + + template + static inline void transform(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, const Mask& mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + transform_detail::TransformDispatcher::cn == 1 && VecTraits::cn == 1 && VecTraits::cn == 1 && ft::smart_shift != 1>::call(src1, src2, dst, op, mask, stream); + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_TRANSFORM_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/type_traits.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/type_traits.hpp new file mode 100644 index 0000000..8b7a3fd --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/type_traits.hpp @@ -0,0 +1,90 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TYPE_TRAITS_HPP +#define OPENCV_CUDA_TYPE_TRAITS_HPP + +#include "detail/type_traits_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct IsSimpleParameter + { + enum {value = type_traits_detail::IsIntegral::value || type_traits_detail::IsFloat::value || + type_traits_detail::PointerTraits::type>::value}; + }; + + template struct TypeTraits + { + typedef typename type_traits_detail::UnConst::type NonConstType; + typedef typename type_traits_detail::UnVolatile::type NonVolatileType; + typedef typename type_traits_detail::UnVolatile::type>::type UnqualifiedType; + typedef typename type_traits_detail::PointerTraits::type PointeeType; + typedef typename type_traits_detail::ReferenceTraits::type ReferredType; + + enum { isConst = type_traits_detail::UnConst::value }; + enum { isVolatile = type_traits_detail::UnVolatile::value }; + + enum { isReference = type_traits_detail::ReferenceTraits::value }; + enum { isPointer = type_traits_detail::PointerTraits::type>::value }; + + enum { isUnsignedInt = type_traits_detail::IsUnsignedIntegral::value }; + enum { isSignedInt = type_traits_detail::IsSignedIntergral::value }; + enum { isIntegral = type_traits_detail::IsIntegral::value }; + enum { isFloat = type_traits_detail::IsFloat::value }; + enum { isArith = isIntegral || isFloat }; + enum { isVec = type_traits_detail::IsVec::value }; + + typedef typename type_traits_detail::Select::value, + T, typename type_traits_detail::AddParameterType::type>::type ParameterType; + }; +}}} + +//! @endcond + +#endif // OPENCV_CUDA_TYPE_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/utility.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/utility.hpp new file mode 100644 index 0000000..7f5db48 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/utility.hpp @@ -0,0 +1,230 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_UTILITY_HPP +#define OPENCV_CUDA_UTILITY_HPP + +#include "saturate_cast.hpp" +#include "datamov_utils.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct CV_EXPORTS ThrustAllocator + { + typedef uchar value_type; + virtual ~ThrustAllocator(); + virtual __device__ __host__ uchar* allocate(size_t numBytes) = 0; + virtual __device__ __host__ void deallocate(uchar* ptr, size_t numBytes) = 0; + static ThrustAllocator& getAllocator(); + static void setAllocator(ThrustAllocator* allocator); + }; + #define OPENCV_CUDA_LOG_WARP_SIZE (5) + #define OPENCV_CUDA_WARP_SIZE (1 << OPENCV_CUDA_LOG_WARP_SIZE) + #define OPENCV_CUDA_LOG_MEM_BANKS ((__CUDA_ARCH__ >= 200) ? 5 : 4) // 32 banks on fermi, 16 on tesla + #define OPENCV_CUDA_MEM_BANKS (1 << OPENCV_CUDA_LOG_MEM_BANKS) + + /////////////////////////////////////////////////////////////////////////////// + // swap + + template void __device__ __host__ __forceinline__ swap(T& a, T& b) + { + const T temp = a; + a = b; + b = temp; + } + + /////////////////////////////////////////////////////////////////////////////// + // Mask Reader + + struct SingleMask + { + explicit __host__ __device__ __forceinline__ SingleMask(PtrStepb mask_) : mask(mask_) {} + __host__ __device__ __forceinline__ SingleMask(const SingleMask& mask_): mask(mask_.mask){} + + __device__ __forceinline__ bool operator()(int y, int x) const + { + return mask.ptr(y)[x] != 0; + } + + PtrStepb mask; + }; + + struct SingleMaskChannels + { + __host__ __device__ __forceinline__ SingleMaskChannels(PtrStepb mask_, int channels_) + : mask(mask_), channels(channels_) {} + __host__ __device__ __forceinline__ SingleMaskChannels(const SingleMaskChannels& mask_) + :mask(mask_.mask), channels(mask_.channels){} + + __device__ __forceinline__ bool operator()(int y, int x) const + { + return mask.ptr(y)[x / channels] != 0; + } + + PtrStepb mask; + int channels; + }; + + struct MaskCollection + { + explicit __host__ __device__ __forceinline__ MaskCollection(PtrStepb* maskCollection_) + : maskCollection(maskCollection_) {} + + __device__ __forceinline__ MaskCollection(const MaskCollection& masks_) + : maskCollection(masks_.maskCollection), curMask(masks_.curMask){} + + __device__ __forceinline__ void next() + { + curMask = *maskCollection++; + } + __device__ __forceinline__ void setMask(int z) + { + curMask = maskCollection[z]; + } + + __device__ __forceinline__ bool operator()(int y, int x) const + { + uchar val; + return curMask.data == 0 || (ForceGlob::Load(curMask.ptr(y), x, val), (val != 0)); + } + + const PtrStepb* maskCollection; + PtrStepb curMask; + }; + + struct WithOutMask + { + __host__ __device__ __forceinline__ WithOutMask(){} + __host__ __device__ __forceinline__ WithOutMask(const WithOutMask&){} + + __device__ __forceinline__ void next() const + { + } + __device__ __forceinline__ void setMask(int) const + { + } + + __device__ __forceinline__ bool operator()(int, int) const + { + return true; + } + + __device__ __forceinline__ bool operator()(int, int, int) const + { + return true; + } + + static __device__ __forceinline__ bool check(int, int) + { + return true; + } + + static __device__ __forceinline__ bool check(int, int, int) + { + return true; + } + }; + + /////////////////////////////////////////////////////////////////////////////// + // Solve linear system + + // solve 2x2 linear system Ax=b + template __device__ __forceinline__ bool solve2x2(const T A[2][2], const T b[2], T x[2]) + { + T det = A[0][0] * A[1][1] - A[1][0] * A[0][1]; + + if (det != 0) + { + double invdet = 1.0 / det; + + x[0] = saturate_cast(invdet * (b[0] * A[1][1] - b[1] * A[0][1])); + + x[1] = saturate_cast(invdet * (A[0][0] * b[1] - A[1][0] * b[0])); + + return true; + } + + return false; + } + + // solve 3x3 linear system Ax=b + template __device__ __forceinline__ bool solve3x3(const T A[3][3], const T b[3], T x[3]) + { + T det = A[0][0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) + - A[0][1] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) + + A[0][2] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]); + + if (det != 0) + { + double invdet = 1.0 / det; + + x[0] = saturate_cast(invdet * + (b[0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) - + A[0][1] * (b[1] * A[2][2] - A[1][2] * b[2] ) + + A[0][2] * (b[1] * A[2][1] - A[1][1] * b[2] ))); + + x[1] = saturate_cast(invdet * + (A[0][0] * (b[1] * A[2][2] - A[1][2] * b[2] ) - + b[0] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) + + A[0][2] * (A[1][0] * b[2] - b[1] * A[2][0]))); + + x[2] = saturate_cast(invdet * + (A[0][0] * (A[1][1] * b[2] - b[1] * A[2][1]) - + A[0][1] * (A[1][0] * b[2] - b[1] * A[2][0]) + + b[0] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]))); + + return true; + } + + return false; + } +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_UTILITY_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_distance.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_distance.hpp new file mode 100644 index 0000000..ef6e510 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_distance.hpp @@ -0,0 +1,232 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_DISTANCE_HPP +#define OPENCV_CUDA_VEC_DISTANCE_HPP + +#include "reduce.hpp" +#include "functional.hpp" +#include "detail/vec_distance_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct L1Dist + { + typedef int value_type; + typedef int result_type; + + __device__ __forceinline__ L1Dist() : mySum(0) {} + + __device__ __forceinline__ void reduceIter(int val1, int val2) + { + mySum = __sad(val1, val2, mySum); + } + + template __device__ __forceinline__ void reduceAll(int* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator int() const + { + return mySum; + } + + int mySum; + }; + template <> struct L1Dist + { + typedef float value_type; + typedef float result_type; + + __device__ __forceinline__ L1Dist() : mySum(0.0f) {} + + __device__ __forceinline__ void reduceIter(float val1, float val2) + { + mySum += ::fabs(val1 - val2); + } + + template __device__ __forceinline__ void reduceAll(float* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator float() const + { + return mySum; + } + + float mySum; + }; + + struct L2Dist + { + typedef float value_type; + typedef float result_type; + + __device__ __forceinline__ L2Dist() : mySum(0.0f) {} + + __device__ __forceinline__ void reduceIter(float val1, float val2) + { + float reg = val1 - val2; + mySum += reg * reg; + } + + template __device__ __forceinline__ void reduceAll(float* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator float() const + { + return sqrtf(mySum); + } + + float mySum; + }; + + struct HammingDist + { + typedef int value_type; + typedef int result_type; + + __device__ __forceinline__ HammingDist() : mySum(0) {} + + __device__ __forceinline__ void reduceIter(int val1, int val2) + { + mySum += __popc(val1 ^ val2); + } + + template __device__ __forceinline__ void reduceAll(int* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator int() const + { + return mySum; + } + + int mySum; + }; + + // calc distance between two vectors in global memory + template + __device__ void calcVecDiffGlobal(const T1* vec1, const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) + { + for (int i = tid; i < len; i += THREAD_DIM) + { + T1 val1; + ForceGlob::Load(vec1, i, val1); + + T2 val2; + ForceGlob::Load(vec2, i, val2); + + dist.reduceIter(val1, val2); + } + + dist.reduceAll(smem, tid); + } + + // calc distance between two vectors, first vector is cached in register or shared memory, second vector is in global memory + template + __device__ __forceinline__ void calcVecDiffCached(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, typename Dist::result_type* smem, int tid) + { + vec_distance_detail::VecDiffCachedCalculator::calc(vecCached, vecGlob, len, dist, tid); + + dist.reduceAll(smem, tid); + } + + // calc distance between two vectors in global memory + template struct VecDiffGlobal + { + explicit __device__ __forceinline__ VecDiffGlobal(const T1* vec1_, int = 0, void* = 0, int = 0, int = 0) + { + vec1 = vec1_; + } + + template + __device__ __forceinline__ void calc(const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) const + { + calcVecDiffGlobal(vec1, vec2, len, dist, smem, tid); + } + + const T1* vec1; + }; + + // calc distance between two vectors, first vector is cached in register memory, second vector is in global memory + template struct VecDiffCachedRegister + { + template __device__ __forceinline__ VecDiffCachedRegister(const T1* vec1, int len, U* smem, int glob_tid, int tid) + { + if (glob_tid < len) + smem[glob_tid] = vec1[glob_tid]; + __syncthreads(); + + U* vec1ValsPtr = vec1Vals; + + #pragma unroll + for (int i = tid; i < MAX_LEN; i += THREAD_DIM) + *vec1ValsPtr++ = smem[i]; + + __syncthreads(); + } + + template + __device__ __forceinline__ void calc(const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) const + { + calcVecDiffCached(vec1Vals, vec2, len, dist, smem, tid); + } + + U vec1Vals[MAX_LEN / THREAD_DIM]; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_DISTANCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_math.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_math.hpp new file mode 100644 index 0000000..9085b92 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_math.hpp @@ -0,0 +1,930 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VECMATH_HPP +#define OPENCV_CUDA_VECMATH_HPP + +#include "vec_traits.hpp" +#include "saturate_cast.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + +// saturate_cast + +namespace vec_math_detail +{ + template struct SatCastHelper; + template struct SatCastHelper<1, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x)); + } + }; + template struct SatCastHelper<2, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y)); + } + }; + template struct SatCastHelper<3, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y), saturate_cast(v.z)); + } + }; + template struct SatCastHelper<4, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y), saturate_cast(v.z), saturate_cast(v.w)); + } + }; + + template static __device__ __forceinline__ VecD saturate_cast_helper(const VecS& v) + { + return SatCastHelper::cn, VecD>::cast(v); + } +} + +template static __device__ __forceinline__ T saturate_cast(const uchar1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double1& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double2& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double3& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double4& v) {return vec_math_detail::saturate_cast_helper(v);} + +// unary operators + +#define CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(op, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a) \ + { \ + return VecTraits::make(op (a.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y), op (a.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y), op (a.z), op (a.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_VEC_UNARY_OP + +// unary functions + +#define CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(func_name, func, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a) \ + { \ + return VecTraits::make(func (a.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y), func (a.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y), func (a.z), func (a.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::fabsf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::fabs, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrt, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::exp, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::log, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cos, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tan, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acos, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atan, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::cosh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acosh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanh, double, double) + +#undef CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC + +// binary operators (vec & vec) + +#define CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(op, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a, const input_type ## 1 & b) \ + { \ + return VecTraits::make(a.x op b.x); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a, const input_type ## 2 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a, const input_type ## 3 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y, a.z op b.z); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a, const input_type ## 4 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y, a.z op b.z, a.w op b.w); \ + } + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, uint, uint) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, uint, uint) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_VEC_BINARY_OP + +// binary operators (vec & scalar) + +#define CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(op, input_type, scalar_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s); \ + } \ + __device__ __forceinline__ output_type ## 1 operator op(scalar_type s, const input_type ## 1 & b) \ + { \ + return VecTraits::make(s op b.x); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(scalar_type s, const input_type ## 2 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s, a.z op s); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(scalar_type s, const input_type ## 3 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y, s op b.z); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s, a.z op s, a.w op s); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(scalar_type s, const input_type ## 4 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y, s op b.z, s op b.w); \ + } + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, uint, uint, uint) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, uint, uint, uint) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, uint, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP + +// binary function (vec & vec) + +#define CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(func_name, func, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a, const input_type ## 1 & b) \ + { \ + return VecTraits::make(func (a.x, b.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a, const input_type ## 2 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a, const input_type ## 3 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y), func (a.z, b.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a, const input_type ## 4 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y), func (a.z, b.z), func (a.w, b.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::fmaxf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::fmax, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::fminf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::fmin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, char, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, short, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, int, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypot, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2, double, double) + +#undef CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC + +// binary function (vec & scalar) + +#define CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(func_name, func, input_type, scalar_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 1 func_name(scalar_type s, const input_type ## 1 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(scalar_type s, const input_type ## 2 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s), func ((output_type) a.z, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(scalar_type s, const input_type ## 3 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y), func ((output_type) s, (output_type) b.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s), func ((output_type) a.z, (output_type) s), func ((output_type) a.w, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(scalar_type s, const input_type ## 4 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y), func ((output_type) s, (output_type) b.z), func ((output_type) s, (output_type) b.w)); \ + } + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, double, double, double) + +#undef CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC + +}}} // namespace cv { namespace cuda { namespace device + +//! @endcond + +#endif // OPENCV_CUDA_VECMATH_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_traits.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_traits.hpp new file mode 100644 index 0000000..b5ff281 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/vec_traits.hpp @@ -0,0 +1,288 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_TRAITS_HPP +#define OPENCV_CUDA_VEC_TRAITS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct TypeVec; + + struct __align__(8) uchar8 + { + uchar a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ uchar8 make_uchar8(uchar a0, uchar a1, uchar a2, uchar a3, uchar a4, uchar a5, uchar a6, uchar a7) + { + uchar8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(8) char8 + { + schar a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ char8 make_char8(schar a0, schar a1, schar a2, schar a3, schar a4, schar a5, schar a6, schar a7) + { + char8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(16) ushort8 + { + ushort a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ ushort8 make_ushort8(ushort a0, ushort a1, ushort a2, ushort a3, ushort a4, ushort a5, ushort a6, ushort a7) + { + ushort8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(16) short8 + { + short a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ short8 make_short8(short a0, short a1, short a2, short a3, short a4, short a5, short a6, short a7) + { + short8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) uint8 + { + uint a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ uint8 make_uint8(uint a0, uint a1, uint a2, uint a3, uint a4, uint a5, uint a6, uint a7) + { + uint8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) int8 + { + int a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ int8 make_int8(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) + { + int8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) float8 + { + float a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ float8 make_float8(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7) + { + float8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct double8 + { + double a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ double8 make_double8(double a0, double a1, double a2, double a3, double a4, double a5, double a6, double a7) + { + double8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + +#define OPENCV_CUDA_IMPLEMENT_TYPE_VEC(type) \ + template<> struct TypeVec { typedef type vec_type; }; \ + template<> struct TypeVec { typedef type ## 1 vec_type; }; \ + template<> struct TypeVec { typedef type ## 2 vec_type; }; \ + template<> struct TypeVec { typedef type ## 2 vec_type; }; \ + template<> struct TypeVec { typedef type ## 3 vec_type; }; \ + template<> struct TypeVec { typedef type ## 3 vec_type; }; \ + template<> struct TypeVec { typedef type ## 4 vec_type; }; \ + template<> struct TypeVec { typedef type ## 4 vec_type; }; \ + template<> struct TypeVec { typedef type ## 8 vec_type; }; \ + template<> struct TypeVec { typedef type ## 8 vec_type; }; + + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(uchar) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(char) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(ushort) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(short) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(int) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(uint) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(float) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(double) + + #undef OPENCV_CUDA_IMPLEMENT_TYPE_VEC + + template<> struct TypeVec { typedef schar vec_type; }; + template<> struct TypeVec { typedef char2 vec_type; }; + template<> struct TypeVec { typedef char3 vec_type; }; + template<> struct TypeVec { typedef char4 vec_type; }; + template<> struct TypeVec { typedef char8 vec_type; }; + + template<> struct TypeVec { typedef uchar vec_type; }; + template<> struct TypeVec { typedef uchar2 vec_type; }; + template<> struct TypeVec { typedef uchar3 vec_type; }; + template<> struct TypeVec { typedef uchar4 vec_type; }; + template<> struct TypeVec { typedef uchar8 vec_type; }; + + template struct VecTraits; + +#define OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(type) \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=1}; \ + static __device__ __host__ __forceinline__ type all(type v) {return v;} \ + static __device__ __host__ __forceinline__ type make(type x) {return x;} \ + static __device__ __host__ __forceinline__ type make(const type* v) {return *v;} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=1}; \ + static __device__ __host__ __forceinline__ type ## 1 all(type v) {return make_ ## type ## 1(v);} \ + static __device__ __host__ __forceinline__ type ## 1 make(type x) {return make_ ## type ## 1(x);} \ + static __device__ __host__ __forceinline__ type ## 1 make(const type* v) {return make_ ## type ## 1(*v);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=2}; \ + static __device__ __host__ __forceinline__ type ## 2 all(type v) {return make_ ## type ## 2(v, v);} \ + static __device__ __host__ __forceinline__ type ## 2 make(type x, type y) {return make_ ## type ## 2(x, y);} \ + static __device__ __host__ __forceinline__ type ## 2 make(const type* v) {return make_ ## type ## 2(v[0], v[1]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=3}; \ + static __device__ __host__ __forceinline__ type ## 3 all(type v) {return make_ ## type ## 3(v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 3 make(type x, type y, type z) {return make_ ## type ## 3(x, y, z);} \ + static __device__ __host__ __forceinline__ type ## 3 make(const type* v) {return make_ ## type ## 3(v[0], v[1], v[2]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=4}; \ + static __device__ __host__ __forceinline__ type ## 4 all(type v) {return make_ ## type ## 4(v, v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 4 make(type x, type y, type z, type w) {return make_ ## type ## 4(x, y, z, w);} \ + static __device__ __host__ __forceinline__ type ## 4 make(const type* v) {return make_ ## type ## 4(v[0], v[1], v[2], v[3]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=8}; \ + static __device__ __host__ __forceinline__ type ## 8 all(type v) {return make_ ## type ## 8(v, v, v, v, v, v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 8 make(type a0, type a1, type a2, type a3, type a4, type a5, type a6, type a7) {return make_ ## type ## 8(a0, a1, a2, a3, a4, a5, a6, a7);} \ + static __device__ __host__ __forceinline__ type ## 8 make(const type* v) {return make_ ## type ## 8(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);} \ + }; + + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(uchar) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(ushort) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(short) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(int) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(uint) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(float) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(double) + + #undef OPENCV_CUDA_IMPLEMENT_VEC_TRAITS + + template<> struct VecTraits + { + typedef char elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ char all(char v) {return v;} + static __device__ __host__ __forceinline__ char make(char x) {return x;} + static __device__ __host__ __forceinline__ char make(const char* x) {return *x;} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ schar all(schar v) {return v;} + static __device__ __host__ __forceinline__ schar make(schar x) {return x;} + static __device__ __host__ __forceinline__ schar make(const schar* x) {return *x;} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ char1 all(schar v) {return make_char1(v);} + static __device__ __host__ __forceinline__ char1 make(schar x) {return make_char1(x);} + static __device__ __host__ __forceinline__ char1 make(const schar* v) {return make_char1(v[0]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=2}; + static __device__ __host__ __forceinline__ char2 all(schar v) {return make_char2(v, v);} + static __device__ __host__ __forceinline__ char2 make(schar x, schar y) {return make_char2(x, y);} + static __device__ __host__ __forceinline__ char2 make(const schar* v) {return make_char2(v[0], v[1]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=3}; + static __device__ __host__ __forceinline__ char3 all(schar v) {return make_char3(v, v, v);} + static __device__ __host__ __forceinline__ char3 make(schar x, schar y, schar z) {return make_char3(x, y, z);} + static __device__ __host__ __forceinline__ char3 make(const schar* v) {return make_char3(v[0], v[1], v[2]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=4}; + static __device__ __host__ __forceinline__ char4 all(schar v) {return make_char4(v, v, v, v);} + static __device__ __host__ __forceinline__ char4 make(schar x, schar y, schar z, schar w) {return make_char4(x, y, z, w);} + static __device__ __host__ __forceinline__ char4 make(const schar* v) {return make_char4(v[0], v[1], v[2], v[3]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=8}; + static __device__ __host__ __forceinline__ char8 all(schar v) {return make_char8(v, v, v, v, v, v, v, v);} + static __device__ __host__ __forceinline__ char8 make(schar a0, schar a1, schar a2, schar a3, schar a4, schar a5, schar a6, schar a7) {return make_char8(a0, a1, a2, a3, a4, a5, a6, a7);} + static __device__ __host__ __forceinline__ char8 make(const schar* v) {return make_char8(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);} + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp.hpp new file mode 100644 index 0000000..8af7e6a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp.hpp @@ -0,0 +1,139 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_WARP_HPP +#define OPENCV_CUDA_DEVICE_WARP_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Warp + { + enum + { + LOG_WARP_SIZE = 5, + WARP_SIZE = 1 << LOG_WARP_SIZE, + STRIDE = WARP_SIZE + }; + + /** \brief Returns the warp lane ID of the calling thread. */ + static __device__ __forceinline__ unsigned int laneId() + { + unsigned int ret; + asm("mov.u32 %0, %%laneid;" : "=r"(ret) ); + return ret; + } + + template + static __device__ __forceinline__ void fill(It beg, It end, const T& value) + { + for(It t = beg + laneId(); t < end; t += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ OutIt copy(InIt beg, InIt end, OutIt out) + { + for(InIt t = beg + laneId(); t < end; t += STRIDE, out += STRIDE) + *out = *t; + return out; + } + + template + static __device__ __forceinline__ OutIt transform(InIt beg, InIt end, OutIt out, UnOp op) + { + for(InIt t = beg + laneId(); t < end; t += STRIDE, out += STRIDE) + *out = op(*t); + return out; + } + + template + static __device__ __forceinline__ OutIt transform(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) + { + unsigned int lane = laneId(); + + InIt1 t1 = beg1 + lane; + InIt2 t2 = beg2 + lane; + for(; t1 < end1; t1 += STRIDE, t2 += STRIDE, out += STRIDE) + *out = op(*t1, *t2); + return out; + } + + template + static __device__ __forceinline__ T reduce(volatile T *ptr, BinOp op) + { + const unsigned int lane = laneId(); + + if (lane < 16) + { + T partial = ptr[lane]; + + ptr[lane] = partial = op(partial, ptr[lane + 16]); + ptr[lane] = partial = op(partial, ptr[lane + 8]); + ptr[lane] = partial = op(partial, ptr[lane + 4]); + ptr[lane] = partial = op(partial, ptr[lane + 2]); + ptr[lane] = partial = op(partial, ptr[lane + 1]); + } + + return *ptr; + } + + template + static __device__ __forceinline__ void yota(OutIt beg, OutIt end, T value) + { + unsigned int lane = laneId(); + value += lane; + + for(OutIt t = beg + lane; t < end; t += STRIDE, value += STRIDE) + *t = value; + } + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_WARP_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_reduce.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_reduce.hpp new file mode 100644 index 0000000..530303d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_reduce.hpp @@ -0,0 +1,76 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_WARP_REDUCE_HPP__ +#define OPENCV_CUDA_WARP_REDUCE_HPP__ + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + __device__ __forceinline__ T warp_reduce(volatile T *ptr , const unsigned int tid = threadIdx.x) + { + const unsigned int lane = tid & 31; // index of thread in warp (0..31) + + if (lane < 16) + { + T partial = ptr[tid]; + + ptr[tid] = partial = partial + ptr[tid + 16]; + ptr[tid] = partial = partial + ptr[tid + 8]; + ptr[tid] = partial = partial + ptr[tid + 4]; + ptr[tid] = partial = partial + ptr[tid + 2]; + ptr[tid] = partial = partial + ptr[tid + 1]; + } + + return ptr[tid - lane]; + } +}}} // namespace cv { namespace cuda { namespace cudev { + +//! @endcond + +#endif /* OPENCV_CUDA_WARP_REDUCE_HPP__ */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_shuffle.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_shuffle.hpp new file mode 100644 index 0000000..0da54ae --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda/warp_shuffle.hpp @@ -0,0 +1,162 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_WARP_SHUFFLE_HPP +#define OPENCV_CUDA_WARP_SHUFFLE_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ +#if __CUDACC_VER_MAJOR__ >= 9 +# define __shfl(x, y, z) __shfl_sync(0xFFFFFFFFU, x, y, z) +# define __shfl_up(x, y, z) __shfl_up_sync(0xFFFFFFFFU, x, y, z) +# define __shfl_down(x, y, z) __shfl_down_sync(0xFFFFFFFFU, x, y, z) +#endif + template + __device__ __forceinline__ T shfl(T val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl(val, srcLane, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl(unsigned int val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl((int) val, srcLane, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl(double val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl(lo, srcLane, width); + hi = __shfl(hi, srcLane, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } + + template + __device__ __forceinline__ T shfl_down(T val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl_down(val, delta, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl_down(unsigned int val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl_down((int) val, delta, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl_down(double val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl_down(lo, delta, width); + hi = __shfl_down(hi, delta, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } + + template + __device__ __forceinline__ T shfl_up(T val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl_up(val, delta, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl_up(unsigned int val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl_up((int) val, delta, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl_up(double val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl_up(lo, delta, width); + hi = __shfl_up(hi, delta, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } +}}} + +# undef __shfl +# undef __shfl_up +# undef __shfl_down + +//! @endcond + +#endif // OPENCV_CUDA_WARP_SHUFFLE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_stream_accessor.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_stream_accessor.hpp new file mode 100644 index 0000000..deaf356 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_stream_accessor.hpp @@ -0,0 +1,86 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP +#define OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP + +#ifndef __cplusplus +# error cuda_stream_accessor.hpp header must be compiled as C++ +#endif + +/** @file cuda_stream_accessor.hpp + * This is only header file that depends on CUDA Runtime API. All other headers are independent. + */ + +#include +#include "opencv2/core/cuda.hpp" + +namespace cv +{ + namespace cuda + { + +//! @addtogroup cudacore_struct +//! @{ + + /** @brief Class that enables getting cudaStream_t from cuda::Stream + */ + struct StreamAccessor + { + CV_EXPORTS static cudaStream_t getStream(const Stream& stream); + CV_EXPORTS static Stream wrapStream(cudaStream_t stream); + }; + + /** @brief Class that enables getting cudaEvent_t from cuda::Event + */ + struct EventAccessor + { + CV_EXPORTS static cudaEvent_t getEvent(const Event& event); + CV_EXPORTS static Event wrapEvent(cudaEvent_t event); + }; + +//! @} + + } +} + +#endif /* OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_types.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_types.hpp new file mode 100644 index 0000000..45dc2ca --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cuda_types.hpp @@ -0,0 +1,144 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_TYPES_HPP +#define OPENCV_CORE_CUDA_TYPES_HPP + +#ifndef __cplusplus +# error cuda_types.hpp header must be compiled as C++ +#endif + +#if defined(__OPENCV_BUILD) && defined(__clang__) +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif +#if defined(__OPENCV_BUILD) && defined(__GNUC__) && __GNUC__ >= 5 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +#ifdef __CUDACC__ + #define __CV_CUDA_HOST_DEVICE__ __host__ __device__ __forceinline__ +#else + #define __CV_CUDA_HOST_DEVICE__ +#endif + +namespace cv +{ + namespace cuda + { + + // Simple lightweight structures that encapsulates information about an image on device. + // It is intended to pass to nvcc-compiled code. GpuMat depends on headers that nvcc can't compile + + template struct DevPtr + { + typedef T elem_type; + typedef int index_type; + + enum { elem_size = sizeof(elem_type) }; + + T* data; + + __CV_CUDA_HOST_DEVICE__ DevPtr() : data(0) {} + __CV_CUDA_HOST_DEVICE__ DevPtr(T* data_) : data(data_) {} + + __CV_CUDA_HOST_DEVICE__ size_t elemSize() const { return elem_size; } + __CV_CUDA_HOST_DEVICE__ operator T*() { return data; } + __CV_CUDA_HOST_DEVICE__ operator const T*() const { return data; } + }; + + template struct PtrSz : public DevPtr + { + __CV_CUDA_HOST_DEVICE__ PtrSz() : size(0) {} + __CV_CUDA_HOST_DEVICE__ PtrSz(T* data_, size_t size_) : DevPtr(data_), size(size_) {} + + size_t size; + }; + + template struct PtrStep : public DevPtr + { + __CV_CUDA_HOST_DEVICE__ PtrStep() : step(0) {} + __CV_CUDA_HOST_DEVICE__ PtrStep(T* data_, size_t step_) : DevPtr(data_), step(step_) {} + + size_t step; + + __CV_CUDA_HOST_DEVICE__ T* ptr(int y = 0) { return ( T*)( ( char*)DevPtr::data + y * step); } + __CV_CUDA_HOST_DEVICE__ const T* ptr(int y = 0) const { return (const T*)( (const char*)DevPtr::data + y * step); } + + __CV_CUDA_HOST_DEVICE__ T& operator ()(int y, int x) { return ptr(y)[x]; } + __CV_CUDA_HOST_DEVICE__ const T& operator ()(int y, int x) const { return ptr(y)[x]; } + }; + + template struct PtrStepSz : public PtrStep + { + __CV_CUDA_HOST_DEVICE__ PtrStepSz() : cols(0), rows(0) {} + __CV_CUDA_HOST_DEVICE__ PtrStepSz(int rows_, int cols_, T* data_, size_t step_) + : PtrStep(data_, step_), cols(cols_), rows(rows_) {} + + template + explicit PtrStepSz(const PtrStepSz& d) : PtrStep((T*)d.data, d.step), cols(d.cols), rows(d.rows){} + + int cols; + int rows; + }; + + typedef PtrStepSz PtrStepSzb; + typedef PtrStepSz PtrStepSzus; + typedef PtrStepSz PtrStepSzf; + typedef PtrStepSz PtrStepSzi; + + typedef PtrStep PtrStepb; + typedef PtrStep PtrStepus; + typedef PtrStep PtrStepf; + typedef PtrStep PtrStepi; + + } +} + +//! @endcond + +#endif /* OPENCV_CORE_CUDA_TYPES_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_dispatch.h b/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_dispatch.h new file mode 100644 index 0000000..42651ae --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_dispatch.h @@ -0,0 +1,345 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#if defined __OPENCV_BUILD \ + +#include "cv_cpu_config.h" +#include "cv_cpu_helper.h" + +#ifdef CV_CPU_DISPATCH_MODE +#define CV_CPU_OPTIMIZATION_NAMESPACE __CV_CAT(opt_, CV_CPU_DISPATCH_MODE) +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace __CV_CAT(opt_, CV_CPU_DISPATCH_MODE) { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#else +#define CV_CPU_OPTIMIZATION_NAMESPACE cpu_baseline +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#define CV_CPU_BASELINE_MODE 1 +#endif + + +#define __CV_CPU_DISPATCH_CHAIN_END(fn, args, mode, ...) /* done */ +#define __CV_CPU_DISPATCH(fn, args, mode, ...) __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) +#define __CV_CPU_DISPATCH_EXPAND(fn, args, ...) __CV_EXPAND(__CV_CPU_DISPATCH(fn, args, __VA_ARGS__)) +#define CV_CPU_DISPATCH(fn, args, ...) __CV_CPU_DISPATCH_EXPAND(fn, args, __VA_ARGS__, END) // expand macros + + +#if defined CV_ENABLE_INTRINSICS \ + && !defined CV_DISABLE_OPTIMIZATION \ + && !defined __CUDACC__ /* do not include SSE/AVX/NEON headers for NVCC compiler */ \ + +#ifdef CV_CPU_COMPILE_SSE2 +# include +# define CV_MMX 1 +# define CV_SSE 1 +# define CV_SSE2 1 +#endif +#ifdef CV_CPU_COMPILE_SSE3 +# include +# define CV_SSE3 1 +#endif +#ifdef CV_CPU_COMPILE_SSSE3 +# include +# define CV_SSSE3 1 +#endif +#ifdef CV_CPU_COMPILE_SSE4_1 +# include +# define CV_SSE4_1 1 +#endif +#ifdef CV_CPU_COMPILE_SSE4_2 +# include +# define CV_SSE4_2 1 +#endif +#ifdef CV_CPU_COMPILE_POPCNT +# ifdef _MSC_VER +# include +# if defined(_M_X64) +# define CV_POPCNT_U64 _mm_popcnt_u64 +# endif +# define CV_POPCNT_U32 _mm_popcnt_u32 +# else +# include +# if defined(__x86_64__) +# define CV_POPCNT_U64 __builtin_popcountll +# endif +# define CV_POPCNT_U32 __builtin_popcount +# endif +# define CV_POPCNT 1 +#endif +#ifdef CV_CPU_COMPILE_AVX +# include +# define CV_AVX 1 +#endif +#ifdef CV_CPU_COMPILE_FP16 +# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) +# include +# else +# include +# endif +# define CV_FP16 1 +#endif +#ifdef CV_CPU_COMPILE_AVX2 +# include +# define CV_AVX2 1 +#endif +#ifdef CV_CPU_COMPILE_AVX_512F +# include +# define CV_AVX_512F 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_COMMON +# define CV_AVX512_COMMON 1 +# define CV_AVX_512CD 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_KNL +# define CV_AVX512_KNL 1 +# define CV_AVX_512ER 1 +# define CV_AVX_512PF 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_KNM +# define CV_AVX512_KNM 1 +# define CV_AVX_5124FMAPS 1 +# define CV_AVX_5124VNNIW 1 +# define CV_AVX_512VPOPCNTDQ 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_SKX +# define CV_AVX512_SKX 1 +# define CV_AVX_512VL 1 +# define CV_AVX_512BW 1 +# define CV_AVX_512DQ 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_CNL +# define CV_AVX512_CNL 1 +# define CV_AVX_512IFMA 1 +# define CV_AVX_512VBMI 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_CLX +# define CV_AVX512_CLX 1 +# define CV_AVX_512VNNI 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_ICL +# define CV_AVX512_ICL 1 +# undef CV_AVX_512IFMA +# define CV_AVX_512IFMA 1 +# undef CV_AVX_512VBMI +# define CV_AVX_512VBMI 1 +# undef CV_AVX_512VNNI +# define CV_AVX_512VNNI 1 +# define CV_AVX_512VBMI2 1 +# define CV_AVX_512BITALG 1 +# define CV_AVX_512VPOPCNTDQ 1 +#endif +#ifdef CV_CPU_COMPILE_FMA3 +# define CV_FMA3 1 +#endif + +#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER)) +# include +# include +# define CV_NEON 1 +#elif defined(__ARM_NEON__) || (defined (__ARM_NEON) && defined(__aarch64__)) +# include +# define CV_NEON 1 +#endif + +#if defined(__ARM_NEON__) || defined(__aarch64__) +# include +#endif + +#ifdef CV_CPU_COMPILE_VSX +# include +# undef vector +# undef pixel +# undef bool +# define CV_VSX 1 +#endif + +#ifdef CV_CPU_COMPILE_VSX3 +# define CV_VSX3 1 +#endif + +#ifdef CV_CPU_COMPILE_MSA +# include "hal/msa_macros.h" +# define CV_MSA 1 +#endif + +#ifdef __EMSCRIPTEN__ +# define CV_WASM_SIMD 1 +# include +#endif + +#endif // CV_ENABLE_INTRINSICS && !CV_DISABLE_OPTIMIZATION && !__CUDACC__ + +#if defined CV_CPU_COMPILE_AVX && !defined CV_CPU_BASELINE_COMPILE_AVX +struct VZeroUpperGuard { +#ifdef __GNUC__ + __attribute__((always_inline)) +#endif + inline VZeroUpperGuard() { _mm256_zeroupper(); } +#ifdef __GNUC__ + __attribute__((always_inline)) +#endif + inline ~VZeroUpperGuard() { _mm256_zeroupper(); } +}; +#define __CV_AVX_GUARD VZeroUpperGuard __vzeroupper_guard; CV_UNUSED(__vzeroupper_guard); +#endif + +#ifdef __CV_AVX_GUARD +#define CV_AVX_GUARD __CV_AVX_GUARD +#else +#define CV_AVX_GUARD +#endif + +#endif // __OPENCV_BUILD + + + +#if !defined __OPENCV_BUILD /* Compatibility code */ \ + && !defined __CUDACC__ /* do not include SSE/AVX/NEON headers for NVCC compiler */ +#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) +# include +# define CV_MMX 1 +# define CV_SSE 1 +# define CV_SSE2 1 +#elif defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER)) +# include +# include +# define CV_NEON 1 +#elif defined(__ARM_NEON__) || (defined (__ARM_NEON) && defined(__aarch64__)) +# include +# define CV_NEON 1 +#elif defined(__VSX__) && defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +# include +# undef vector +# undef pixel +# undef bool +# define CV_VSX 1 +#endif + +#endif // !__OPENCV_BUILD && !__CUDACC (Compatibility code) + + + +#ifndef CV_MMX +# define CV_MMX 0 +#endif +#ifndef CV_SSE +# define CV_SSE 0 +#endif +#ifndef CV_SSE2 +# define CV_SSE2 0 +#endif +#ifndef CV_SSE3 +# define CV_SSE3 0 +#endif +#ifndef CV_SSSE3 +# define CV_SSSE3 0 +#endif +#ifndef CV_SSE4_1 +# define CV_SSE4_1 0 +#endif +#ifndef CV_SSE4_2 +# define CV_SSE4_2 0 +#endif +#ifndef CV_POPCNT +# define CV_POPCNT 0 +#endif +#ifndef CV_AVX +# define CV_AVX 0 +#endif +#ifndef CV_FP16 +# define CV_FP16 0 +#endif +#ifndef CV_AVX2 +# define CV_AVX2 0 +#endif +#ifndef CV_FMA3 +# define CV_FMA3 0 +#endif +#ifndef CV_AVX_512F +# define CV_AVX_512F 0 +#endif +#ifndef CV_AVX_512BW +# define CV_AVX_512BW 0 +#endif +#ifndef CV_AVX_512CD +# define CV_AVX_512CD 0 +#endif +#ifndef CV_AVX_512DQ +# define CV_AVX_512DQ 0 +#endif +#ifndef CV_AVX_512ER +# define CV_AVX_512ER 0 +#endif +#ifndef CV_AVX_512IFMA +# define CV_AVX_512IFMA 0 +#endif +#define CV_AVX_512IFMA512 CV_AVX_512IFMA // deprecated +#ifndef CV_AVX_512PF +# define CV_AVX_512PF 0 +#endif +#ifndef CV_AVX_512VBMI +# define CV_AVX_512VBMI 0 +#endif +#ifndef CV_AVX_512VL +# define CV_AVX_512VL 0 +#endif +#ifndef CV_AVX_5124FMAPS +# define CV_AVX_5124FMAPS 0 +#endif +#ifndef CV_AVX_5124VNNIW +# define CV_AVX_5124VNNIW 0 +#endif +#ifndef CV_AVX_512VPOPCNTDQ +# define CV_AVX_512VPOPCNTDQ 0 +#endif +#ifndef CV_AVX_512VNNI +# define CV_AVX_512VNNI 0 +#endif +#ifndef CV_AVX_512VBMI2 +# define CV_AVX_512VBMI2 0 +#endif +#ifndef CV_AVX_512BITALG +# define CV_AVX_512BITALG 0 +#endif +#ifndef CV_AVX512_COMMON +# define CV_AVX512_COMMON 0 +#endif +#ifndef CV_AVX512_KNL +# define CV_AVX512_KNL 0 +#endif +#ifndef CV_AVX512_KNM +# define CV_AVX512_KNM 0 +#endif +#ifndef CV_AVX512_SKX +# define CV_AVX512_SKX 0 +#endif +#ifndef CV_AVX512_CNL +# define CV_AVX512_CNL 0 +#endif +#ifndef CV_AVX512_CLX +# define CV_AVX512_CLX 0 +#endif +#ifndef CV_AVX512_ICL +# define CV_AVX512_ICL 0 +#endif + +#ifndef CV_NEON +# define CV_NEON 0 +#endif + +#ifndef CV_VSX +# define CV_VSX 0 +#endif + +#ifndef CV_VSX3 +# define CV_VSX3 0 +#endif + +#ifndef CV_MSA +# define CV_MSA 0 +#endif + +#ifndef CV_WASM_SIMD +# define CV_WASM_SIMD 0 +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_helper.h b/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_helper.h new file mode 100644 index 0000000..aaa89ed --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cv_cpu_helper.h @@ -0,0 +1,487 @@ +// AUTOGENERATED, DO NOT EDIT + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE +# define CV_TRY_SSE 1 +# define CV_CPU_FORCE_SSE 1 +# define CV_CPU_HAS_SUPPORT_SSE 1 +# define CV_CPU_CALL_SSE(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE_(fn, args) return (opt_SSE::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE +# define CV_TRY_SSE 1 +# define CV_CPU_FORCE_SSE 0 +# define CV_CPU_HAS_SUPPORT_SSE (cv::checkHardwareSupport(CV_CPU_SSE)) +# define CV_CPU_CALL_SSE(fn, args) if (CV_CPU_HAS_SUPPORT_SSE) return (opt_SSE::fn args) +# define CV_CPU_CALL_SSE_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE) return (opt_SSE::fn args) +#else +# define CV_TRY_SSE 0 +# define CV_CPU_FORCE_SSE 0 +# define CV_CPU_HAS_SUPPORT_SSE 0 +# define CV_CPU_CALL_SSE(fn, args) +# define CV_CPU_CALL_SSE_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE(fn, args, mode, ...) CV_CPU_CALL_SSE(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE2 +# define CV_TRY_SSE2 1 +# define CV_CPU_FORCE_SSE2 1 +# define CV_CPU_HAS_SUPPORT_SSE2 1 +# define CV_CPU_CALL_SSE2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE2_(fn, args) return (opt_SSE2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE2 +# define CV_TRY_SSE2 1 +# define CV_CPU_FORCE_SSE2 0 +# define CV_CPU_HAS_SUPPORT_SSE2 (cv::checkHardwareSupport(CV_CPU_SSE2)) +# define CV_CPU_CALL_SSE2(fn, args) if (CV_CPU_HAS_SUPPORT_SSE2) return (opt_SSE2::fn args) +# define CV_CPU_CALL_SSE2_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE2) return (opt_SSE2::fn args) +#else +# define CV_TRY_SSE2 0 +# define CV_CPU_FORCE_SSE2 0 +# define CV_CPU_HAS_SUPPORT_SSE2 0 +# define CV_CPU_CALL_SSE2(fn, args) +# define CV_CPU_CALL_SSE2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE2(fn, args, mode, ...) CV_CPU_CALL_SSE2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE3 +# define CV_TRY_SSE3 1 +# define CV_CPU_FORCE_SSE3 1 +# define CV_CPU_HAS_SUPPORT_SSE3 1 +# define CV_CPU_CALL_SSE3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE3_(fn, args) return (opt_SSE3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE3 +# define CV_TRY_SSE3 1 +# define CV_CPU_FORCE_SSE3 0 +# define CV_CPU_HAS_SUPPORT_SSE3 (cv::checkHardwareSupport(CV_CPU_SSE3)) +# define CV_CPU_CALL_SSE3(fn, args) if (CV_CPU_HAS_SUPPORT_SSE3) return (opt_SSE3::fn args) +# define CV_CPU_CALL_SSE3_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE3) return (opt_SSE3::fn args) +#else +# define CV_TRY_SSE3 0 +# define CV_CPU_FORCE_SSE3 0 +# define CV_CPU_HAS_SUPPORT_SSE3 0 +# define CV_CPU_CALL_SSE3(fn, args) +# define CV_CPU_CALL_SSE3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE3(fn, args, mode, ...) CV_CPU_CALL_SSE3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSSE3 +# define CV_TRY_SSSE3 1 +# define CV_CPU_FORCE_SSSE3 1 +# define CV_CPU_HAS_SUPPORT_SSSE3 1 +# define CV_CPU_CALL_SSSE3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSSE3_(fn, args) return (opt_SSSE3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSSE3 +# define CV_TRY_SSSE3 1 +# define CV_CPU_FORCE_SSSE3 0 +# define CV_CPU_HAS_SUPPORT_SSSE3 (cv::checkHardwareSupport(CV_CPU_SSSE3)) +# define CV_CPU_CALL_SSSE3(fn, args) if (CV_CPU_HAS_SUPPORT_SSSE3) return (opt_SSSE3::fn args) +# define CV_CPU_CALL_SSSE3_(fn, args) if (CV_CPU_HAS_SUPPORT_SSSE3) return (opt_SSSE3::fn args) +#else +# define CV_TRY_SSSE3 0 +# define CV_CPU_FORCE_SSSE3 0 +# define CV_CPU_HAS_SUPPORT_SSSE3 0 +# define CV_CPU_CALL_SSSE3(fn, args) +# define CV_CPU_CALL_SSSE3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSSE3(fn, args, mode, ...) CV_CPU_CALL_SSSE3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE4_1 +# define CV_TRY_SSE4_1 1 +# define CV_CPU_FORCE_SSE4_1 1 +# define CV_CPU_HAS_SUPPORT_SSE4_1 1 +# define CV_CPU_CALL_SSE4_1(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE4_1_(fn, args) return (opt_SSE4_1::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE4_1 +# define CV_TRY_SSE4_1 1 +# define CV_CPU_FORCE_SSE4_1 0 +# define CV_CPU_HAS_SUPPORT_SSE4_1 (cv::checkHardwareSupport(CV_CPU_SSE4_1)) +# define CV_CPU_CALL_SSE4_1(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_1) return (opt_SSE4_1::fn args) +# define CV_CPU_CALL_SSE4_1_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_1) return (opt_SSE4_1::fn args) +#else +# define CV_TRY_SSE4_1 0 +# define CV_CPU_FORCE_SSE4_1 0 +# define CV_CPU_HAS_SUPPORT_SSE4_1 0 +# define CV_CPU_CALL_SSE4_1(fn, args) +# define CV_CPU_CALL_SSE4_1_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE4_1(fn, args, mode, ...) CV_CPU_CALL_SSE4_1(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE4_2 +# define CV_TRY_SSE4_2 1 +# define CV_CPU_FORCE_SSE4_2 1 +# define CV_CPU_HAS_SUPPORT_SSE4_2 1 +# define CV_CPU_CALL_SSE4_2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE4_2_(fn, args) return (opt_SSE4_2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE4_2 +# define CV_TRY_SSE4_2 1 +# define CV_CPU_FORCE_SSE4_2 0 +# define CV_CPU_HAS_SUPPORT_SSE4_2 (cv::checkHardwareSupport(CV_CPU_SSE4_2)) +# define CV_CPU_CALL_SSE4_2(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_2) return (opt_SSE4_2::fn args) +# define CV_CPU_CALL_SSE4_2_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_2) return (opt_SSE4_2::fn args) +#else +# define CV_TRY_SSE4_2 0 +# define CV_CPU_FORCE_SSE4_2 0 +# define CV_CPU_HAS_SUPPORT_SSE4_2 0 +# define CV_CPU_CALL_SSE4_2(fn, args) +# define CV_CPU_CALL_SSE4_2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE4_2(fn, args, mode, ...) CV_CPU_CALL_SSE4_2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_POPCNT +# define CV_TRY_POPCNT 1 +# define CV_CPU_FORCE_POPCNT 1 +# define CV_CPU_HAS_SUPPORT_POPCNT 1 +# define CV_CPU_CALL_POPCNT(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_POPCNT_(fn, args) return (opt_POPCNT::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_POPCNT +# define CV_TRY_POPCNT 1 +# define CV_CPU_FORCE_POPCNT 0 +# define CV_CPU_HAS_SUPPORT_POPCNT (cv::checkHardwareSupport(CV_CPU_POPCNT)) +# define CV_CPU_CALL_POPCNT(fn, args) if (CV_CPU_HAS_SUPPORT_POPCNT) return (opt_POPCNT::fn args) +# define CV_CPU_CALL_POPCNT_(fn, args) if (CV_CPU_HAS_SUPPORT_POPCNT) return (opt_POPCNT::fn args) +#else +# define CV_TRY_POPCNT 0 +# define CV_CPU_FORCE_POPCNT 0 +# define CV_CPU_HAS_SUPPORT_POPCNT 0 +# define CV_CPU_CALL_POPCNT(fn, args) +# define CV_CPU_CALL_POPCNT_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_POPCNT(fn, args, mode, ...) CV_CPU_CALL_POPCNT(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX +# define CV_TRY_AVX 1 +# define CV_CPU_FORCE_AVX 1 +# define CV_CPU_HAS_SUPPORT_AVX 1 +# define CV_CPU_CALL_AVX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX_(fn, args) return (opt_AVX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX +# define CV_TRY_AVX 1 +# define CV_CPU_FORCE_AVX 0 +# define CV_CPU_HAS_SUPPORT_AVX (cv::checkHardwareSupport(CV_CPU_AVX)) +# define CV_CPU_CALL_AVX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX) return (opt_AVX::fn args) +# define CV_CPU_CALL_AVX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX) return (opt_AVX::fn args) +#else +# define CV_TRY_AVX 0 +# define CV_CPU_FORCE_AVX 0 +# define CV_CPU_HAS_SUPPORT_AVX 0 +# define CV_CPU_CALL_AVX(fn, args) +# define CV_CPU_CALL_AVX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX(fn, args, mode, ...) CV_CPU_CALL_AVX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_FP16 +# define CV_TRY_FP16 1 +# define CV_CPU_FORCE_FP16 1 +# define CV_CPU_HAS_SUPPORT_FP16 1 +# define CV_CPU_CALL_FP16(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_FP16_(fn, args) return (opt_FP16::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_FP16 +# define CV_TRY_FP16 1 +# define CV_CPU_FORCE_FP16 0 +# define CV_CPU_HAS_SUPPORT_FP16 (cv::checkHardwareSupport(CV_CPU_FP16)) +# define CV_CPU_CALL_FP16(fn, args) if (CV_CPU_HAS_SUPPORT_FP16) return (opt_FP16::fn args) +# define CV_CPU_CALL_FP16_(fn, args) if (CV_CPU_HAS_SUPPORT_FP16) return (opt_FP16::fn args) +#else +# define CV_TRY_FP16 0 +# define CV_CPU_FORCE_FP16 0 +# define CV_CPU_HAS_SUPPORT_FP16 0 +# define CV_CPU_CALL_FP16(fn, args) +# define CV_CPU_CALL_FP16_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_FP16(fn, args, mode, ...) CV_CPU_CALL_FP16(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX2 +# define CV_TRY_AVX2 1 +# define CV_CPU_FORCE_AVX2 1 +# define CV_CPU_HAS_SUPPORT_AVX2 1 +# define CV_CPU_CALL_AVX2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX2_(fn, args) return (opt_AVX2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX2 +# define CV_TRY_AVX2 1 +# define CV_CPU_FORCE_AVX2 0 +# define CV_CPU_HAS_SUPPORT_AVX2 (cv::checkHardwareSupport(CV_CPU_AVX2)) +# define CV_CPU_CALL_AVX2(fn, args) if (CV_CPU_HAS_SUPPORT_AVX2) return (opt_AVX2::fn args) +# define CV_CPU_CALL_AVX2_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX2) return (opt_AVX2::fn args) +#else +# define CV_TRY_AVX2 0 +# define CV_CPU_FORCE_AVX2 0 +# define CV_CPU_HAS_SUPPORT_AVX2 0 +# define CV_CPU_CALL_AVX2(fn, args) +# define CV_CPU_CALL_AVX2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX2(fn, args, mode, ...) CV_CPU_CALL_AVX2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_FMA3 +# define CV_TRY_FMA3 1 +# define CV_CPU_FORCE_FMA3 1 +# define CV_CPU_HAS_SUPPORT_FMA3 1 +# define CV_CPU_CALL_FMA3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_FMA3_(fn, args) return (opt_FMA3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_FMA3 +# define CV_TRY_FMA3 1 +# define CV_CPU_FORCE_FMA3 0 +# define CV_CPU_HAS_SUPPORT_FMA3 (cv::checkHardwareSupport(CV_CPU_FMA3)) +# define CV_CPU_CALL_FMA3(fn, args) if (CV_CPU_HAS_SUPPORT_FMA3) return (opt_FMA3::fn args) +# define CV_CPU_CALL_FMA3_(fn, args) if (CV_CPU_HAS_SUPPORT_FMA3) return (opt_FMA3::fn args) +#else +# define CV_TRY_FMA3 0 +# define CV_CPU_FORCE_FMA3 0 +# define CV_CPU_HAS_SUPPORT_FMA3 0 +# define CV_CPU_CALL_FMA3(fn, args) +# define CV_CPU_CALL_FMA3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_FMA3(fn, args, mode, ...) CV_CPU_CALL_FMA3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX_512F +# define CV_TRY_AVX_512F 1 +# define CV_CPU_FORCE_AVX_512F 1 +# define CV_CPU_HAS_SUPPORT_AVX_512F 1 +# define CV_CPU_CALL_AVX_512F(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX_512F_(fn, args) return (opt_AVX_512F::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX_512F +# define CV_TRY_AVX_512F 1 +# define CV_CPU_FORCE_AVX_512F 0 +# define CV_CPU_HAS_SUPPORT_AVX_512F (cv::checkHardwareSupport(CV_CPU_AVX_512F)) +# define CV_CPU_CALL_AVX_512F(fn, args) if (CV_CPU_HAS_SUPPORT_AVX_512F) return (opt_AVX_512F::fn args) +# define CV_CPU_CALL_AVX_512F_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX_512F) return (opt_AVX_512F::fn args) +#else +# define CV_TRY_AVX_512F 0 +# define CV_CPU_FORCE_AVX_512F 0 +# define CV_CPU_HAS_SUPPORT_AVX_512F 0 +# define CV_CPU_CALL_AVX_512F(fn, args) +# define CV_CPU_CALL_AVX_512F_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX_512F(fn, args, mode, ...) CV_CPU_CALL_AVX_512F(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_COMMON +# define CV_TRY_AVX512_COMMON 1 +# define CV_CPU_FORCE_AVX512_COMMON 1 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON 1 +# define CV_CPU_CALL_AVX512_COMMON(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) return (opt_AVX512_COMMON::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_COMMON +# define CV_TRY_AVX512_COMMON 1 +# define CV_CPU_FORCE_AVX512_COMMON 0 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON (cv::checkHardwareSupport(CV_CPU_AVX512_COMMON)) +# define CV_CPU_CALL_AVX512_COMMON(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_COMMON) return (opt_AVX512_COMMON::fn args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_COMMON) return (opt_AVX512_COMMON::fn args) +#else +# define CV_TRY_AVX512_COMMON 0 +# define CV_CPU_FORCE_AVX512_COMMON 0 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON 0 +# define CV_CPU_CALL_AVX512_COMMON(fn, args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_COMMON(fn, args, mode, ...) CV_CPU_CALL_AVX512_COMMON(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_KNL +# define CV_TRY_AVX512_KNL 1 +# define CV_CPU_FORCE_AVX512_KNL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL 1 +# define CV_CPU_CALL_AVX512_KNL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) return (opt_AVX512_KNL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_KNL +# define CV_TRY_AVX512_KNL 1 +# define CV_CPU_FORCE_AVX512_KNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL (cv::checkHardwareSupport(CV_CPU_AVX512_KNL)) +# define CV_CPU_CALL_AVX512_KNL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNL) return (opt_AVX512_KNL::fn args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNL) return (opt_AVX512_KNL::fn args) +#else +# define CV_TRY_AVX512_KNL 0 +# define CV_CPU_FORCE_AVX512_KNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL 0 +# define CV_CPU_CALL_AVX512_KNL(fn, args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_KNL(fn, args, mode, ...) CV_CPU_CALL_AVX512_KNL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_KNM +# define CV_TRY_AVX512_KNM 1 +# define CV_CPU_FORCE_AVX512_KNM 1 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM 1 +# define CV_CPU_CALL_AVX512_KNM(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) return (opt_AVX512_KNM::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_KNM +# define CV_TRY_AVX512_KNM 1 +# define CV_CPU_FORCE_AVX512_KNM 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM (cv::checkHardwareSupport(CV_CPU_AVX512_KNM)) +# define CV_CPU_CALL_AVX512_KNM(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNM) return (opt_AVX512_KNM::fn args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNM) return (opt_AVX512_KNM::fn args) +#else +# define CV_TRY_AVX512_KNM 0 +# define CV_CPU_FORCE_AVX512_KNM 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM 0 +# define CV_CPU_CALL_AVX512_KNM(fn, args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_KNM(fn, args, mode, ...) CV_CPU_CALL_AVX512_KNM(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_SKX +# define CV_TRY_AVX512_SKX 1 +# define CV_CPU_FORCE_AVX512_SKX 1 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX 1 +# define CV_CPU_CALL_AVX512_SKX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) return (opt_AVX512_SKX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_SKX +# define CV_TRY_AVX512_SKX 1 +# define CV_CPU_FORCE_AVX512_SKX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX (cv::checkHardwareSupport(CV_CPU_AVX512_SKX)) +# define CV_CPU_CALL_AVX512_SKX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_SKX) return (opt_AVX512_SKX::fn args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_SKX) return (opt_AVX512_SKX::fn args) +#else +# define CV_TRY_AVX512_SKX 0 +# define CV_CPU_FORCE_AVX512_SKX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX 0 +# define CV_CPU_CALL_AVX512_SKX(fn, args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_SKX(fn, args, mode, ...) CV_CPU_CALL_AVX512_SKX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_CNL +# define CV_TRY_AVX512_CNL 1 +# define CV_CPU_FORCE_AVX512_CNL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL 1 +# define CV_CPU_CALL_AVX512_CNL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) return (opt_AVX512_CNL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_CNL +# define CV_TRY_AVX512_CNL 1 +# define CV_CPU_FORCE_AVX512_CNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL (cv::checkHardwareSupport(CV_CPU_AVX512_CNL)) +# define CV_CPU_CALL_AVX512_CNL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CNL) return (opt_AVX512_CNL::fn args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CNL) return (opt_AVX512_CNL::fn args) +#else +# define CV_TRY_AVX512_CNL 0 +# define CV_CPU_FORCE_AVX512_CNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL 0 +# define CV_CPU_CALL_AVX512_CNL(fn, args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_CNL(fn, args, mode, ...) CV_CPU_CALL_AVX512_CNL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_CLX +# define CV_TRY_AVX512_CLX 1 +# define CV_CPU_FORCE_AVX512_CLX 1 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX 1 +# define CV_CPU_CALL_AVX512_CLX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) return (opt_AVX512_CLX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_CLX +# define CV_TRY_AVX512_CLX 1 +# define CV_CPU_FORCE_AVX512_CLX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX (cv::checkHardwareSupport(CV_CPU_AVX512_CLX)) +# define CV_CPU_CALL_AVX512_CLX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CLX) return (opt_AVX512_CLX::fn args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CLX) return (opt_AVX512_CLX::fn args) +#else +# define CV_TRY_AVX512_CLX 0 +# define CV_CPU_FORCE_AVX512_CLX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX 0 +# define CV_CPU_CALL_AVX512_CLX(fn, args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_CLX(fn, args, mode, ...) CV_CPU_CALL_AVX512_CLX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_ICL +# define CV_TRY_AVX512_ICL 1 +# define CV_CPU_FORCE_AVX512_ICL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL 1 +# define CV_CPU_CALL_AVX512_ICL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) return (opt_AVX512_ICL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_ICL +# define CV_TRY_AVX512_ICL 1 +# define CV_CPU_FORCE_AVX512_ICL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL (cv::checkHardwareSupport(CV_CPU_AVX512_ICL)) +# define CV_CPU_CALL_AVX512_ICL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_ICL) return (opt_AVX512_ICL::fn args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_ICL) return (opt_AVX512_ICL::fn args) +#else +# define CV_TRY_AVX512_ICL 0 +# define CV_CPU_FORCE_AVX512_ICL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL 0 +# define CV_CPU_CALL_AVX512_ICL(fn, args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_ICL(fn, args, mode, ...) CV_CPU_CALL_AVX512_ICL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_NEON +# define CV_TRY_NEON 1 +# define CV_CPU_FORCE_NEON 1 +# define CV_CPU_HAS_SUPPORT_NEON 1 +# define CV_CPU_CALL_NEON(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_NEON_(fn, args) return (opt_NEON::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_NEON +# define CV_TRY_NEON 1 +# define CV_CPU_FORCE_NEON 0 +# define CV_CPU_HAS_SUPPORT_NEON (cv::checkHardwareSupport(CV_CPU_NEON)) +# define CV_CPU_CALL_NEON(fn, args) if (CV_CPU_HAS_SUPPORT_NEON) return (opt_NEON::fn args) +# define CV_CPU_CALL_NEON_(fn, args) if (CV_CPU_HAS_SUPPORT_NEON) return (opt_NEON::fn args) +#else +# define CV_TRY_NEON 0 +# define CV_CPU_FORCE_NEON 0 +# define CV_CPU_HAS_SUPPORT_NEON 0 +# define CV_CPU_CALL_NEON(fn, args) +# define CV_CPU_CALL_NEON_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_NEON(fn, args, mode, ...) CV_CPU_CALL_NEON(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_MSA +# define CV_TRY_MSA 1 +# define CV_CPU_FORCE_MSA 1 +# define CV_CPU_HAS_SUPPORT_MSA 1 +# define CV_CPU_CALL_MSA(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_MSA_(fn, args) return (opt_MSA::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_MSA +# define CV_TRY_MSA 1 +# define CV_CPU_FORCE_MSA 0 +# define CV_CPU_HAS_SUPPORT_MSA (cv::checkHardwareSupport(CV_CPU_MSA)) +# define CV_CPU_CALL_MSA(fn, args) if (CV_CPU_HAS_SUPPORT_MSA) return (opt_MSA::fn args) +# define CV_CPU_CALL_MSA_(fn, args) if (CV_CPU_HAS_SUPPORT_MSA) return (opt_MSA::fn args) +#else +# define CV_TRY_MSA 0 +# define CV_CPU_FORCE_MSA 0 +# define CV_CPU_HAS_SUPPORT_MSA 0 +# define CV_CPU_CALL_MSA(fn, args) +# define CV_CPU_CALL_MSA_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_MSA(fn, args, mode, ...) CV_CPU_CALL_MSA(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_VSX +# define CV_TRY_VSX 1 +# define CV_CPU_FORCE_VSX 1 +# define CV_CPU_HAS_SUPPORT_VSX 1 +# define CV_CPU_CALL_VSX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_VSX_(fn, args) return (opt_VSX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_VSX +# define CV_TRY_VSX 1 +# define CV_CPU_FORCE_VSX 0 +# define CV_CPU_HAS_SUPPORT_VSX (cv::checkHardwareSupport(CV_CPU_VSX)) +# define CV_CPU_CALL_VSX(fn, args) if (CV_CPU_HAS_SUPPORT_VSX) return (opt_VSX::fn args) +# define CV_CPU_CALL_VSX_(fn, args) if (CV_CPU_HAS_SUPPORT_VSX) return (opt_VSX::fn args) +#else +# define CV_TRY_VSX 0 +# define CV_CPU_FORCE_VSX 0 +# define CV_CPU_HAS_SUPPORT_VSX 0 +# define CV_CPU_CALL_VSX(fn, args) +# define CV_CPU_CALL_VSX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_VSX(fn, args, mode, ...) CV_CPU_CALL_VSX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_VSX3 +# define CV_TRY_VSX3 1 +# define CV_CPU_FORCE_VSX3 1 +# define CV_CPU_HAS_SUPPORT_VSX3 1 +# define CV_CPU_CALL_VSX3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_VSX3_(fn, args) return (opt_VSX3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_VSX3 +# define CV_TRY_VSX3 1 +# define CV_CPU_FORCE_VSX3 0 +# define CV_CPU_HAS_SUPPORT_VSX3 (cv::checkHardwareSupport(CV_CPU_VSX3)) +# define CV_CPU_CALL_VSX3(fn, args) if (CV_CPU_HAS_SUPPORT_VSX3) return (opt_VSX3::fn args) +# define CV_CPU_CALL_VSX3_(fn, args) if (CV_CPU_HAS_SUPPORT_VSX3) return (opt_VSX3::fn args) +#else +# define CV_TRY_VSX3 0 +# define CV_CPU_FORCE_VSX3 0 +# define CV_CPU_HAS_SUPPORT_VSX3 0 +# define CV_CPU_CALL_VSX3(fn, args) +# define CV_CPU_CALL_VSX3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_VSX3(fn, args, mode, ...) CV_CPU_CALL_VSX3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#define CV_CPU_CALL_BASELINE(fn, args) return (cpu_baseline::fn args) +#define __CV_CPU_DISPATCH_CHAIN_BASELINE(fn, args, mode, ...) CV_CPU_CALL_BASELINE(fn, args) /* last in sequence */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cvdef.h b/hgdriver/3rdparty/opencv/include/opencv2/core/cvdef.h new file mode 100644 index 0000000..c61f001 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cvdef.h @@ -0,0 +1,833 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVDEF_H +#define OPENCV_CORE_CVDEF_H + +//! @addtogroup core_utils +//! @{ + +#ifdef OPENCV_INCLUDE_PORT_FILE // User-provided header file with custom platform configuration +#include OPENCV_INCLUDE_PORT_FILE +#endif + +#if !defined CV_DOXYGEN && !defined CV_IGNORE_DEBUG_BUILD_GUARD +#if (defined(_MSC_VER) && (defined(DEBUG) || defined(_DEBUG))) || \ + (defined(_GLIBCXX_DEBUG) || defined(_GLIBCXX_DEBUG_PEDANTIC)) +// Guard to prevent using of binary incompatible binaries / runtimes +// https://github.com/opencv/opencv/pull/9161 +#define CV__DEBUG_NS_BEGIN namespace debug_build_guard { +#define CV__DEBUG_NS_END } +namespace cv { namespace debug_build_guard { } using namespace debug_build_guard; } +#endif +#endif + +#ifndef CV__DEBUG_NS_BEGIN +#define CV__DEBUG_NS_BEGIN +#define CV__DEBUG_NS_END +#endif + + +#ifdef __OPENCV_BUILD +#include "cvconfig.h" +#endif + +#ifndef __CV_EXPAND +#define __CV_EXPAND(x) x +#endif + +#ifndef __CV_CAT +#define __CV_CAT__(x, y) x ## y +#define __CV_CAT_(x, y) __CV_CAT__(x, y) +#define __CV_CAT(x, y) __CV_CAT_(x, y) +#endif + +#define __CV_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define __CV_VA_NUM_ARGS(...) __CV_VA_NUM_ARGS_HELPER(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#if defined __GNUC__ +#define CV_Func __func__ +#elif defined _MSC_VER +#define CV_Func __FUNCTION__ +#else +#define CV_Func "" +#endif + +//! @cond IGNORED + +//////////////// static assert ///////////////// +#define CVAUX_CONCAT_EXP(a, b) a##b +#define CVAUX_CONCAT(a, b) CVAUX_CONCAT_EXP(a,b) + +#if defined(__clang__) +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(cxx_static_assert) +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# elif __has_extension(c_static_assert) +# define CV_StaticAssert(condition, reason) _Static_assert((condition), reason " " #condition) +# endif +#elif defined(__GNUC__) +# if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# endif +#elif defined(_MSC_VER) +# if _MSC_VER >= 1600 /* MSVC 10 */ +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# endif +#endif +#ifndef CV_StaticAssert +# if !defined(__clang__) && defined(__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 302) +# define CV_StaticAssert(condition, reason) ({ extern int __attribute__((error("CV_StaticAssert: " reason " " #condition))) CV_StaticAssert(); ((condition) ? 0 : CV_StaticAssert()); }) +# else +namespace cv { + template struct CV_StaticAssert_failed; + template <> struct CV_StaticAssert_failed { enum { val = 1 }; }; + template struct CV_StaticAssert_test {}; +} +# define CV_StaticAssert(condition, reason)\ + typedef cv::CV_StaticAssert_test< sizeof(cv::CV_StaticAssert_failed< static_cast(condition) >) > CVAUX_CONCAT(CV_StaticAssert_failed_at_, __LINE__) +# endif +#endif + +// Suppress warning "-Wdeprecated-declarations" / C4996 +#if defined(_MSC_VER) + #define CV_DO_PRAGMA(x) __pragma(x) +#elif defined(__GNUC__) + #define CV_DO_PRAGMA(x) _Pragma (#x) +#else + #define CV_DO_PRAGMA(x) +#endif + +#ifdef _MSC_VER +#define CV_SUPPRESS_DEPRECATED_START \ + CV_DO_PRAGMA(warning(push)) \ + CV_DO_PRAGMA(warning(disable: 4996)) +#define CV_SUPPRESS_DEPRECATED_END CV_DO_PRAGMA(warning(pop)) +#elif defined (__clang__) || ((__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 405)) +#define CV_SUPPRESS_DEPRECATED_START \ + CV_DO_PRAGMA(GCC diagnostic push) \ + CV_DO_PRAGMA(GCC diagnostic ignored "-Wdeprecated-declarations") +#define CV_SUPPRESS_DEPRECATED_END CV_DO_PRAGMA(GCC diagnostic pop) +#else +#define CV_SUPPRESS_DEPRECATED_START +#define CV_SUPPRESS_DEPRECATED_END +#endif + +#define CV_UNUSED(name) (void)name + +#if defined __GNUC__ && !defined __EXCEPTIONS +#define CV_TRY +#define CV_CATCH(A, B) for (A B; false; ) +#define CV_CATCH_ALL if (false) +#define CV_THROW(A) abort() +#define CV_RETHROW() abort() +#else +#define CV_TRY try +#define CV_CATCH(A, B) catch(const A & B) +#define CV_CATCH_ALL catch(...) +#define CV_THROW(A) throw A +#define CV_RETHROW() throw +#endif + +//! @endcond + +// undef problematic defines sometimes defined by system headers (windows.h in particular) +#undef small +#undef min +#undef max +#undef abs +#undef Complex + +#if defined __cplusplus +#include +#else +#include +#endif + +#include "opencv2/core/hal/interface.h" + +#if defined __ICL +# define CV_ICC __ICL +#elif defined __ICC +# define CV_ICC __ICC +#elif defined __ECL +# define CV_ICC __ECL +#elif defined __ECC +# define CV_ICC __ECC +#elif defined __INTEL_COMPILER +# define CV_ICC __INTEL_COMPILER +#endif + +#ifndef CV_INLINE +# if defined __cplusplus +# define CV_INLINE static inline +# elif defined _MSC_VER +# define CV_INLINE __inline +# else +# define CV_INLINE static +# endif +#endif + +#ifndef CV_ALWAYS_INLINE +#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define CV_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define CV_ALWAYS_INLINE __forceinline +#else +#define CV_ALWAYS_INLINE inline +#endif +#endif + +#if defined CV_DISABLE_OPTIMIZATION || (defined CV_ICC && !defined CV_ENABLE_UNROLLED) +# define CV_ENABLE_UNROLLED 0 +#else +# define CV_ENABLE_UNROLLED 1 +#endif + +#ifdef __GNUC__ +# define CV_DECL_ALIGNED(x) __attribute__ ((aligned (x))) +#elif defined _MSC_VER +# define CV_DECL_ALIGNED(x) __declspec(align(x)) +#else +# define CV_DECL_ALIGNED(x) +#endif + +/* CPU features and intrinsics support */ +#define CV_CPU_NONE 0 +#define CV_CPU_MMX 1 +#define CV_CPU_SSE 2 +#define CV_CPU_SSE2 3 +#define CV_CPU_SSE3 4 +#define CV_CPU_SSSE3 5 +#define CV_CPU_SSE4_1 6 +#define CV_CPU_SSE4_2 7 +#define CV_CPU_POPCNT 8 +#define CV_CPU_FP16 9 +#define CV_CPU_AVX 10 +#define CV_CPU_AVX2 11 +#define CV_CPU_FMA3 12 + +#define CV_CPU_AVX_512F 13 +#define CV_CPU_AVX_512BW 14 +#define CV_CPU_AVX_512CD 15 +#define CV_CPU_AVX_512DQ 16 +#define CV_CPU_AVX_512ER 17 +#define CV_CPU_AVX_512IFMA512 18 // deprecated +#define CV_CPU_AVX_512IFMA 18 +#define CV_CPU_AVX_512PF 19 +#define CV_CPU_AVX_512VBMI 20 +#define CV_CPU_AVX_512VL 21 +#define CV_CPU_AVX_512VBMI2 22 +#define CV_CPU_AVX_512VNNI 23 +#define CV_CPU_AVX_512BITALG 24 +#define CV_CPU_AVX_512VPOPCNTDQ 25 +#define CV_CPU_AVX_5124VNNIW 26 +#define CV_CPU_AVX_5124FMAPS 27 + +#define CV_CPU_NEON 100 + +#define CV_CPU_MSA 150 + +#define CV_CPU_VSX 200 +#define CV_CPU_VSX3 201 + +// CPU features groups +#define CV_CPU_AVX512_SKX 256 +#define CV_CPU_AVX512_COMMON 257 +#define CV_CPU_AVX512_KNL 258 +#define CV_CPU_AVX512_KNM 259 +#define CV_CPU_AVX512_CNL 260 +#define CV_CPU_AVX512_CLX 261 +#define CV_CPU_AVX512_ICL 262 + +// when adding to this list remember to update the following enum +#define CV_HARDWARE_MAX_FEATURE 512 + +/** @brief Available CPU features. +*/ +enum CpuFeatures { + CPU_MMX = 1, + CPU_SSE = 2, + CPU_SSE2 = 3, + CPU_SSE3 = 4, + CPU_SSSE3 = 5, + CPU_SSE4_1 = 6, + CPU_SSE4_2 = 7, + CPU_POPCNT = 8, + CPU_FP16 = 9, + CPU_AVX = 10, + CPU_AVX2 = 11, + CPU_FMA3 = 12, + + CPU_AVX_512F = 13, + CPU_AVX_512BW = 14, + CPU_AVX_512CD = 15, + CPU_AVX_512DQ = 16, + CPU_AVX_512ER = 17, + CPU_AVX_512IFMA512 = 18, // deprecated + CPU_AVX_512IFMA = 18, + CPU_AVX_512PF = 19, + CPU_AVX_512VBMI = 20, + CPU_AVX_512VL = 21, + CPU_AVX_512VBMI2 = 22, + CPU_AVX_512VNNI = 23, + CPU_AVX_512BITALG = 24, + CPU_AVX_512VPOPCNTDQ= 25, + CPU_AVX_5124VNNIW = 26, + CPU_AVX_5124FMAPS = 27, + + CPU_NEON = 100, + + CPU_MSA = 150, + + CPU_VSX = 200, + CPU_VSX3 = 201, + + CPU_AVX512_SKX = 256, //!< Skylake-X with AVX-512F/CD/BW/DQ/VL + CPU_AVX512_COMMON = 257, //!< Common instructions AVX-512F/CD for all CPUs that support AVX-512 + CPU_AVX512_KNL = 258, //!< Knights Landing with AVX-512F/CD/ER/PF + CPU_AVX512_KNM = 259, //!< Knights Mill with AVX-512F/CD/ER/PF/4FMAPS/4VNNIW/VPOPCNTDQ + CPU_AVX512_CNL = 260, //!< Cannon Lake with AVX-512F/CD/BW/DQ/VL/IFMA/VBMI + CPU_AVX512_CLX = 261, //!< Cascade Lake with AVX-512F/CD/BW/DQ/VL/VNNI + CPU_AVX512_ICL = 262, //!< Ice Lake with AVX-512F/CD/BW/DQ/VL/IFMA/VBMI/VNNI/VBMI2/BITALG/VPOPCNTDQ + + CPU_MAX_FEATURE = 512 // see CV_HARDWARE_MAX_FEATURE +}; + + +#include "cv_cpu_dispatch.h" + +#if !defined(CV_STRONG_ALIGNMENT) && defined(__arm__) && !(defined(__aarch64__) || defined(_M_ARM64)) +// int*, int64* should be propertly aligned pointers on ARMv7 +#define CV_STRONG_ALIGNMENT 1 +#endif +#if !defined(CV_STRONG_ALIGNMENT) +#define CV_STRONG_ALIGNMENT 0 +#endif + +/* fundamental constants */ +#define CV_PI 3.1415926535897932384626433832795 +#define CV_2PI 6.283185307179586476925286766559 +#define CV_LOG2 0.69314718055994530941723212145818 + +#if defined __ARM_FP16_FORMAT_IEEE \ + && !defined __CUDACC__ +# define CV_FP16_TYPE 1 +#else +# define CV_FP16_TYPE 0 +#endif + +typedef union Cv16suf +{ + short i; + ushort u; +#if CV_FP16_TYPE + __fp16 h; +#endif +} +Cv16suf; + +typedef union Cv32suf +{ + int i; + unsigned u; + float f; +} +Cv32suf; + +typedef union Cv64suf +{ + int64 i; + uint64 u; + double f; +} +Cv64suf; + +#define OPENCV_ABI_COMPATIBILITY 300 + +#ifdef __OPENCV_BUILD +# define DISABLE_OPENCV_24_COMPATIBILITY +# define OPENCV_DISABLE_DEPRECATED_COMPATIBILITY +#endif + +#ifndef CV_EXPORTS +# if (defined _WIN32 || defined WINCE || defined __CYGWIN__) && defined(CVAPI_EXPORTS) +# define CV_EXPORTS __declspec(dllexport) +# elif defined __GNUC__ && __GNUC__ >= 4 && (defined(CVAPI_EXPORTS) || defined(__APPLE__)) +# define CV_EXPORTS __attribute__ ((visibility ("default"))) +# endif +#endif + +#ifndef CV_EXPORTS +# define CV_EXPORTS +#endif + +#ifdef _MSC_VER +# define CV_EXPORTS_TEMPLATE +#else +# define CV_EXPORTS_TEMPLATE CV_EXPORTS +#endif + +#ifndef CV_DEPRECATED +# if defined(__GNUC__) +# define CV_DEPRECATED __attribute__ ((deprecated)) +# elif defined(_MSC_VER) +# define CV_DEPRECATED __declspec(deprecated) +# else +# define CV_DEPRECATED +# endif +#endif + +#ifndef CV_DEPRECATED_EXTERNAL +# if defined(__OPENCV_BUILD) +# define CV_DEPRECATED_EXTERNAL /* nothing */ +# else +# define CV_DEPRECATED_EXTERNAL CV_DEPRECATED +# endif +#endif + + +#ifndef CV_EXTERN_C +# ifdef __cplusplus +# define CV_EXTERN_C extern "C" +# else +# define CV_EXTERN_C +# endif +#endif + +/* special informative macros for wrapper generators */ +#define CV_EXPORTS_W CV_EXPORTS +#define CV_EXPORTS_W_SIMPLE CV_EXPORTS +#define CV_EXPORTS_AS(synonym) CV_EXPORTS +#define CV_EXPORTS_W_MAP CV_EXPORTS +#define CV_IN_OUT +#define CV_OUT +#define CV_PROP +#define CV_PROP_RW +#define CV_WRAP +#define CV_WRAP_AS(synonym) + +/****************************************************************************************\ +* Matrix type (Mat) * +\****************************************************************************************/ + +#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT) +#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1) +#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1) +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) +#define CV_MAT_CONT_FLAG_SHIFT 14 +#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT) +#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG) +#define CV_IS_CONT_MAT CV_IS_MAT_CONT +#define CV_SUBMAT_FLAG_SHIFT 15 +#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT) +#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG) + +/** Size of each channel item, + 0x8442211 = 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */ +#define CV_ELEM_SIZE1(type) \ + ((((sizeof(size_t)<<28)|0x8442211) >> CV_MAT_DEPTH(type)*4) & 15) + +/** 0x3a50 = 11 10 10 01 01 00 00 ~ array of log2(sizeof(arr_type_elem)) */ +#define CV_ELEM_SIZE(type) \ + (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3)) + +#ifndef MIN +# define MIN(a,b) ((a) > (b) ? (b) : (a)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +/****************************************************************************************\ +* static analysys * +\****************************************************************************************/ + +// In practice, some macro are not processed correctly (noreturn is not detected). +// We need to use simplified definition for them. +#ifndef CV_STATIC_ANALYSIS +# if defined(__KLOCWORK__) || defined(__clang_analyzer__) || defined(__COVERITY__) +# define CV_STATIC_ANALYSIS 1 +# endif +#else +# if defined(CV_STATIC_ANALYSIS) && !(__CV_CAT(1, CV_STATIC_ANALYSIS) == 1) // defined and not empty +# if 0 == CV_STATIC_ANALYSIS +# undef CV_STATIC_ANALYSIS +# endif +# endif +#endif + +/****************************************************************************************\ +* Thread sanitizer * +\****************************************************************************************/ +#ifndef CV_THREAD_SANITIZER +# if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define CV_THREAD_SANITIZER +# endif +# endif +#endif + +/****************************************************************************************\ +* exchange-add operation for atomic operations on reference counters * +\****************************************************************************************/ + +#ifdef CV_XADD + // allow to use user-defined macro +#elif defined __GNUC__ || defined __clang__ +# if defined __clang__ && __clang_major__ >= 3 && !defined __ANDROID__ && !defined __EMSCRIPTEN__ && !defined(__CUDACC__) && !defined __INTEL_COMPILER +# ifdef __ATOMIC_ACQ_REL +# define CV_XADD(addr, delta) __c11_atomic_fetch_add((_Atomic(int)*)(addr), delta, __ATOMIC_ACQ_REL) +# else +# define CV_XADD(addr, delta) __atomic_fetch_add((_Atomic(int)*)(addr), delta, 4) +# endif +# else +# if defined __ATOMIC_ACQ_REL && !defined __clang__ + // version for gcc >= 4.7 +# define CV_XADD(addr, delta) (int)__atomic_fetch_add((unsigned*)(addr), (unsigned)(delta), __ATOMIC_ACQ_REL) +# else +# define CV_XADD(addr, delta) (int)__sync_fetch_and_add((unsigned*)(addr), (unsigned)(delta)) +# endif +# endif +#elif defined _MSC_VER && !defined RC_INVOKED +# include +# define CV_XADD(addr, delta) (int)_InterlockedExchangeAdd((long volatile*)addr, delta) +#else + #ifdef OPENCV_FORCE_UNSAFE_XADD + CV_INLINE CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; } + #else + #error "OpenCV: can't define safe CV_XADD macro for current platform (unsupported). Define CV_XADD macro through custom port header (see OPENCV_INCLUDE_PORT_FILE)" + #endif +#endif + + +/****************************************************************************************\ +* CV_NORETURN attribute * +\****************************************************************************************/ + +#ifndef CV_NORETURN +# if defined(__GNUC__) +# define CV_NORETURN __attribute__((__noreturn__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define CV_NORETURN __declspec(noreturn) +# else +# define CV_NORETURN /* nothing by default */ +# endif +#endif + + +/****************************************************************************************\ +* CV_NODISCARD attribute * +* encourages the compiler to issue a warning if the return value is discarded (C++17) * +\****************************************************************************************/ +#ifndef CV_NODISCARD +# if defined(__GNUC__) +# define CV_NODISCARD __attribute__((__warn_unused_result__)) // at least available with GCC 3.4 +# elif defined(__clang__) && defined(__has_attribute) +# if __has_attribute(__warn_unused_result__) +# define CV_NODISCARD __attribute__((__warn_unused_result__)) +# endif +# endif +#endif +#ifndef CV_NODISCARD +# define CV_NODISCARD /* nothing by default */ +#endif + + +/****************************************************************************************\ +* C++ 11 * +\****************************************************************************************/ +#ifndef CV_CXX11 +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +# define CV_CXX11 1 +# endif +#else +# if CV_CXX11 == 0 +# undef CV_CXX11 +# endif +#endif + + +/****************************************************************************************\ +* C++ Move semantics * +\****************************************************************************************/ + +#ifndef CV_CXX_MOVE_SEMANTICS +# if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || (defined(_MSC_VER) && _MSC_VER >= 1600) +# define CV_CXX_MOVE_SEMANTICS 1 +# elif defined(__clang) +# if __has_feature(cxx_rvalue_references) +# define CV_CXX_MOVE_SEMANTICS 1 +# endif +# endif +#else +# if CV_CXX_MOVE_SEMANTICS == 0 +# undef CV_CXX_MOVE_SEMANTICS +# endif +#endif + +#ifdef CV_CXX_MOVE_SEMANTICS +#define CV_CXX_MOVE(x) std::move(x) +#else +#define CV_CXX_MOVE(x) (x) +#endif + + +/****************************************************************************************\ +* C++11 std::array * +\****************************************************************************************/ + +#ifndef CV_CXX_STD_ARRAY +# if __cplusplus >= 201103L || (defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER >= 1900/*MSVS 2015*/) +# define CV_CXX_STD_ARRAY 1 +# include +# endif +#else +# if CV_CXX_STD_ARRAY == 0 +# undef CV_CXX_STD_ARRAY +# endif +#endif + + +/****************************************************************************************\ +* C++11 override / final * +\****************************************************************************************/ + +#ifndef CV_OVERRIDE +# ifdef CV_CXX11 +# define CV_OVERRIDE override +# endif +#endif +#ifndef CV_OVERRIDE +# define CV_OVERRIDE +#endif + +#ifndef CV_FINAL +# ifdef CV_CXX11 +# define CV_FINAL final +# endif +#endif +#ifndef CV_FINAL +# define CV_FINAL +#endif + +/****************************************************************************************\ +* C++11 noexcept * +\****************************************************************************************/ + +#ifndef CV_NOEXCEPT +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900/*MSVS 2015*/) +# define CV_NOEXCEPT noexcept +# endif +#endif +#ifndef CV_NOEXCEPT +# define CV_NOEXCEPT +#endif + + + +// Integer types portatibility +#ifdef OPENCV_STDINT_HEADER +#include OPENCV_STDINT_HEADER +#elif defined(__cplusplus) +#if defined(_MSC_VER) && _MSC_VER < 1600 /* MSVS 2010 */ +namespace cv { +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +} +#elif defined(_MSC_VER) || __cplusplus >= 201103L +#include +namespace cv { +using std::int8_t; +using std::uint8_t; +using std::int16_t; +using std::uint16_t; +using std::int32_t; +using std::uint32_t; +using std::int64_t; +using std::uint64_t; +} +#else +#include +namespace cv { +typedef ::int8_t int8_t; +typedef ::uint8_t uint8_t; +typedef ::int16_t int16_t; +typedef ::uint16_t uint16_t; +typedef ::int32_t int32_t; +typedef ::uint32_t uint32_t; +typedef ::int64_t int64_t; +typedef ::uint64_t uint64_t; +} +#endif +#else // pure C +#include +#endif + +#ifdef __cplusplus +namespace cv +{ + +class float16_t +{ +public: +#if CV_FP16_TYPE + + float16_t() {} + explicit float16_t(float x) { h = (__fp16)x; } + operator float() const { return (float)h; } + static float16_t fromBits(ushort w) + { + Cv16suf u; + u.u = w; + float16_t result; + result.h = u.h; + return result; + } + static float16_t zero() + { + float16_t result; + result.h = (__fp16)0; + return result; + } + ushort bits() const + { + Cv16suf u; + u.h = h; + return u.u; + } +protected: + __fp16 h; + +#else + float16_t() {} + explicit float16_t(float x) + { + #if CV_AVX2 + __m128 v = _mm_load_ss(&x); + w = (ushort)_mm_cvtsi128_si32(_mm_cvtps_ph(v, 0)); + #else + Cv32suf in; + in.f = x; + unsigned sign = in.u & 0x80000000; + in.u ^= sign; + + if( in.u >= 0x47800000 ) + w = (ushort)(in.u > 0x7f800000 ? 0x7e00 : 0x7c00); + else + { + if (in.u < 0x38800000) + { + in.f += 0.5f; + w = (ushort)(in.u - 0x3f000000); + } + else + { + unsigned t = in.u + 0xc8000fff; + w = (ushort)((t + ((in.u >> 13) & 1)) >> 13); + } + } + + w = (ushort)(w | (sign >> 16)); + #endif + } + + operator float() const + { + #if CV_AVX2 + float f; + _mm_store_ss(&f, _mm_cvtph_ps(_mm_cvtsi32_si128(w))); + return f; + #else + Cv32suf out; + + unsigned t = ((w & 0x7fff) << 13) + 0x38000000; + unsigned sign = (w & 0x8000) << 16; + unsigned e = w & 0x7c00; + + out.u = t + (1 << 23); + out.u = (e >= 0x7c00 ? t + 0x38000000 : + e == 0 ? (out.f -= 6.103515625e-05f, out.u) : t) | sign; + return out.f; + #endif + } + + static float16_t fromBits(ushort b) + { + float16_t result; + result.w = b; + return result; + } + static float16_t zero() + { + float16_t result; + result.w = (ushort)0; + return result; + } + ushort bits() const { return w; } +protected: + ushort w; + +#endif +}; + +} +#endif + +//! @} + +#ifndef __cplusplus +#include "opencv2/core/fast_math.hpp" // define cvRound(double) +#endif + +#endif // OPENCV_CORE_CVDEF_H diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.hpp new file mode 100644 index 0000000..fbf6d31 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.hpp @@ -0,0 +1,1074 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVSTD_HPP +#define OPENCV_CORE_CVSTD_HPP + +#ifndef __cplusplus +# error cvstd.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include +#include +#include + +#include + +// import useful primitives from stl +# include +# include +# include //for abs(int) +# include + +namespace cv +{ + static inline uchar abs(uchar a) { return a; } + static inline ushort abs(ushort a) { return a; } + static inline unsigned abs(unsigned a) { return a; } + static inline uint64 abs(uint64 a) { return a; } + + using std::min; + using std::max; + using std::abs; + using std::swap; + using std::sqrt; + using std::exp; + using std::pow; + using std::log; +} + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +//////////////////////////// memory management functions //////////////////////////// + +/** @brief Allocates an aligned memory buffer. + +The function allocates the buffer of the specified size and returns it. When the buffer size is 16 +bytes or more, the returned buffer is aligned to 16 bytes. +@param bufSize Allocated buffer size. + */ +CV_EXPORTS void* fastMalloc(size_t bufSize); + +/** @brief Deallocates a memory buffer. + +The function deallocates the buffer allocated with fastMalloc . If NULL pointer is passed, the +function does nothing. C version of the function clears the pointer *pptr* to avoid problems with +double memory deallocation. +@param ptr Pointer to the allocated buffer. + */ +CV_EXPORTS void fastFree(void* ptr); + +/*! + The STL-compliant memory Allocator based on cv::fastMalloc() and cv::fastFree() +*/ +template class Allocator +{ +public: + typedef _Tp value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + template class rebind { typedef Allocator other; }; + + explicit Allocator() {} + ~Allocator() {} + explicit Allocator(Allocator const&) {} + template + explicit Allocator(Allocator const&) {} + + // address + pointer address(reference r) { return &r; } + const_pointer address(const_reference r) { return &r; } + + pointer allocate(size_type count, const void* =0) { return reinterpret_cast(fastMalloc(count * sizeof (_Tp))); } + void deallocate(pointer p, size_type) { fastFree(p); } + + void construct(pointer p, const _Tp& v) { new(static_cast(p)) _Tp(v); } + void destroy(pointer p) { p->~_Tp(); } + + size_type max_size() const { return cv::max(static_cast<_Tp>(-1)/sizeof(_Tp), 1); } +}; + +//! @} core_utils + +//! @cond IGNORED + +namespace detail +{ + +// Metafunction to avoid taking a reference to void. +template +struct RefOrVoid { typedef T& type; }; + +template<> +struct RefOrVoid{ typedef void type; }; + +template<> +struct RefOrVoid{ typedef const void type; }; + +template<> +struct RefOrVoid{ typedef volatile void type; }; + +template<> +struct RefOrVoid{ typedef const volatile void type; }; + +// This class would be private to Ptr, if it didn't have to be a non-template. +struct PtrOwner; + +} + +template +struct DefaultDeleter +{ + void operator () (Y* p) const; +}; + +//! @endcond + +//! @addtogroup core_basic +//! @{ + +/** @brief Template class for smart pointers with shared ownership + +A Ptr\ pretends to be a pointer to an object of type T. Unlike an ordinary pointer, however, the +object will be automatically cleaned up once all Ptr instances pointing to it are destroyed. + +Ptr is similar to boost::shared_ptr that is part of the Boost library +() and std::shared_ptr from +the [C++11](http://en.wikipedia.org/wiki/C++11) standard. + +This class provides the following advantages: +- Default constructor, copy constructor, and assignment operator for an arbitrary C++ class or C + structure. For some objects, like files, windows, mutexes, sockets, and others, a copy + constructor or an assignment operator are difficult to define. For some other objects, like + complex classifiers in OpenCV, copy constructors are absent and not easy to implement. Finally, + some of complex OpenCV and your own data structures may be written in C. However, copy + constructors and default constructors can simplify programming a lot. Besides, they are often + required (for example, by STL containers). By using a Ptr to such an object instead of the + object itself, you automatically get all of the necessary constructors and the assignment + operator. +- *O(1)* complexity of the above-mentioned operations. While some structures, like std::vector, + provide a copy constructor and an assignment operator, the operations may take a considerable + amount of time if the data structures are large. But if the structures are put into a Ptr, the + overhead is small and independent of the data size. +- Automatic and customizable cleanup, even for C structures. See the example below with FILE\*. +- Heterogeneous collections of objects. The standard STL and most other C++ and OpenCV containers + can store only objects of the same type and the same size. The classical solution to store + objects of different types in the same container is to store pointers to the base class (Base\*) + instead but then you lose the automatic memory management. Again, by using Ptr\ instead + of raw pointers, you can solve the problem. + +A Ptr is said to *own* a pointer - that is, for each Ptr there is a pointer that will be deleted +once all Ptr instances that own it are destroyed. The owned pointer may be null, in which case +nothing is deleted. Each Ptr also *stores* a pointer. The stored pointer is the pointer the Ptr +pretends to be; that is, the one you get when you use Ptr::get or the conversion to T\*. It's +usually the same as the owned pointer, but if you use casts or the general shared-ownership +constructor, the two may diverge: the Ptr will still own the original pointer, but will itself point +to something else. + +The owned pointer is treated as a black box. The only thing Ptr needs to know about it is how to +delete it. This knowledge is encapsulated in the *deleter* - an auxiliary object that is associated +with the owned pointer and shared between all Ptr instances that own it. The default deleter is an +instance of DefaultDeleter, which uses the standard C++ delete operator; as such it will work with +any pointer allocated with the standard new operator. + +However, if the pointer must be deleted in a different way, you must specify a custom deleter upon +Ptr construction. A deleter is simply a callable object that accepts the pointer as its sole +argument. For example, if you want to wrap FILE, you may do so as follows: +@code + Ptr f(fopen("myfile.txt", "w"), fclose); + if(!f) throw ...; + fprintf(f, ....); + ... + // the file will be closed automatically by f's destructor. +@endcode +Alternatively, if you want all pointers of a particular type to be deleted the same way, you can +specialize DefaultDeleter::operator() for that type, like this: +@code + namespace cv { + template<> void DefaultDeleter::operator ()(FILE * obj) const + { + fclose(obj); + } + } +@endcode +For convenience, the following types from the OpenCV C API already have such a specialization that +calls the appropriate release function: +- CvCapture +- CvFileStorage +- CvHaarClassifierCascade +- CvMat +- CvMatND +- CvMemStorage +- CvSparseMat +- CvVideoWriter +- IplImage +@note The shared ownership mechanism is implemented with reference counting. As such, cyclic +ownership (e.g. when object a contains a Ptr to object b, which contains a Ptr to object a) will +lead to all involved objects never being cleaned up. Avoid such situations. +@note It is safe to concurrently read (but not write) a Ptr instance from multiple threads and +therefore it is normally safe to use it in multi-threaded applications. The same is true for Mat and +other C++ OpenCV classes that use internal reference counts. +*/ +template +struct Ptr +{ + /** Generic programming support. */ + typedef T element_type; + + /** The default constructor creates a null Ptr - one that owns and stores a null pointer. + */ + Ptr(); + + /** + If p is null, these are equivalent to the default constructor. + Otherwise, these constructors assume ownership of p - that is, the created Ptr owns and stores p + and assumes it is the sole owner of it. Don't use them if p is already owned by another Ptr, or + else p will get deleted twice. + With the first constructor, DefaultDeleter\() becomes the associated deleter (so p will + eventually be deleted with the standard delete operator). Y must be a complete type at the point + of invocation. + With the second constructor, d becomes the associated deleter. + Y\* must be convertible to T\*. + @param p Pointer to own. + @note It is often easier to use makePtr instead. + */ + template +#ifdef DISABLE_OPENCV_24_COMPATIBILITY + explicit +#endif + Ptr(Y* p); + + /** @overload + @param d Deleter to use for the owned pointer. + @param p Pointer to own. + */ + template + Ptr(Y* p, D d); + + /** + These constructors create a Ptr that shares ownership with another Ptr - that is, own the same + pointer as o. + With the first two, the same pointer is stored, as well; for the second, Y\* must be convertible + to T\*. + With the third, p is stored, and Y may be any type. This constructor allows to have completely + unrelated owned and stored pointers, and should be used with care to avoid confusion. A relatively + benign use is to create a non-owning Ptr, like this: + @code + ptr = Ptr(Ptr(), dont_delete_me); // owns nothing; will not delete the pointer. + @endcode + @param o Ptr to share ownership with. + */ + Ptr(const Ptr& o); + + /** @overload + @param o Ptr to share ownership with. + */ + template + Ptr(const Ptr& o); + + /** @overload + @param o Ptr to share ownership with. + @param p Pointer to store. + */ + template + Ptr(const Ptr& o, T* p); + + /** The destructor is equivalent to calling Ptr::release. */ + ~Ptr(); + + /** + Assignment replaces the current Ptr instance with one that owns and stores same pointers as o and + then destroys the old instance. + @param o Ptr to share ownership with. + */ + Ptr& operator = (const Ptr& o); + + /** @overload */ + template + Ptr& operator = (const Ptr& o); + + /** If no other Ptr instance owns the owned pointer, deletes it with the associated deleter. Then sets + both the owned and the stored pointers to NULL. + */ + void release(); + + /** + `ptr.reset(...)` is equivalent to `ptr = Ptr(...)`. + @param p Pointer to own. + */ + template + void reset(Y* p); + + /** @overload + @param d Deleter to use for the owned pointer. + @param p Pointer to own. + */ + template + void reset(Y* p, D d); + + /** + Swaps the owned and stored pointers (and deleters, if any) of this and o. + @param o Ptr to swap with. + */ + void swap(Ptr& o); + + /** Returns the stored pointer. */ + T* get() const; + + /** Ordinary pointer emulation. */ + typename detail::RefOrVoid::type operator * () const; + + /** Ordinary pointer emulation. */ + T* operator -> () const; + + /** Equivalent to get(). */ + operator T* () const; + + /** ptr.empty() is equivalent to `!ptr.get()`. */ + bool empty() const; + + /** Returns a Ptr that owns the same pointer as this, and stores the same + pointer as this, except converted via static_cast to Y*. + */ + template + Ptr staticCast() const; + + /** Ditto for const_cast. */ + template + Ptr constCast() const; + + /** Ditto for dynamic_cast. */ + template + Ptr dynamicCast() const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Ptr(Ptr&& o); + Ptr& operator = (Ptr&& o); +#endif + +private: + detail::PtrOwner* owner; + T* stored; + + template + friend struct Ptr; // have to do this for the cross-type copy constructor +}; + +/** Equivalent to ptr1.swap(ptr2). Provided to help write generic algorithms. */ +template +void swap(Ptr& ptr1, Ptr& ptr2); + +/** Return whether ptr1.get() and ptr2.get() are equal and not equal, respectively. */ +template +bool operator == (const Ptr& ptr1, const Ptr& ptr2); +template +bool operator != (const Ptr& ptr1, const Ptr& ptr2); + +/** `makePtr(...)` is equivalent to `Ptr(new T(...))`. It is shorter than the latter, and it's +marginally safer than using a constructor or Ptr::reset, since it ensures that the owned pointer +is new and thus not owned by any other Ptr instance. +Unfortunately, perfect forwarding is impossible to implement in C++03, and so makePtr is limited +to constructors of T that have up to 10 arguments, none of which are non-const references. + */ +template +Ptr makePtr(); +/** @overload */ +template +Ptr makePtr(const A1& a1); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10); + +//////////////////////////////// string class //////////////////////////////// + +class CV_EXPORTS FileNode; //for string constructor from FileNode + +class CV_EXPORTS String +{ +public: + typedef char value_type; + typedef char& reference; + typedef const char& const_reference; + typedef char* pointer; + typedef const char* const_pointer; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef char* iterator; + typedef const char* const_iterator; + + static const size_t npos = size_t(-1); + + String(); + String(const String& str); + String(const String& str, size_t pos, size_t len = npos); + String(const char* s); + String(const char* s, size_t n); + String(size_t n, char c); + String(const char* first, const char* last); + template String(Iterator first, Iterator last); + explicit String(const FileNode& fn); + ~String(); + + String& operator=(const String& str); + String& operator=(const char* s); + String& operator=(char c); + + String& operator+=(const String& str); + String& operator+=(const char* s); + String& operator+=(char c); + + size_t size() const; + size_t length() const; + + char operator[](size_t idx) const; + char operator[](int idx) const; + + const char* begin() const; + const char* end() const; + + const char* c_str() const; + + bool empty() const; + void clear(); + + int compare(const char* s) const; + int compare(const String& str) const; + + void swap(String& str); + String substr(size_t pos = 0, size_t len = npos) const; + + size_t find(const char* s, size_t pos, size_t n) const; + size_t find(char c, size_t pos = 0) const; + size_t find(const String& str, size_t pos = 0) const; + size_t find(const char* s, size_t pos = 0) const; + + size_t rfind(const char* s, size_t pos, size_t n) const; + size_t rfind(char c, size_t pos = npos) const; + size_t rfind(const String& str, size_t pos = npos) const; + size_t rfind(const char* s, size_t pos = npos) const; + + size_t find_first_of(const char* s, size_t pos, size_t n) const; + size_t find_first_of(char c, size_t pos = 0) const; + size_t find_first_of(const String& str, size_t pos = 0) const; + size_t find_first_of(const char* s, size_t pos = 0) const; + + size_t find_last_of(const char* s, size_t pos, size_t n) const; + size_t find_last_of(char c, size_t pos = npos) const; + size_t find_last_of(const String& str, size_t pos = npos) const; + size_t find_last_of(const char* s, size_t pos = npos) const; + + friend String operator+ (const String& lhs, const String& rhs); + friend String operator+ (const String& lhs, const char* rhs); + friend String operator+ (const char* lhs, const String& rhs); + friend String operator+ (const String& lhs, char rhs); + friend String operator+ (char lhs, const String& rhs); + + String toLowerCase() const; + + String(const std::string& str); + String(const std::string& str, size_t pos, size_t len = npos); + String& operator=(const std::string& str); + String& operator+=(const std::string& str); + operator std::string() const; + + friend String operator+ (const String& lhs, const std::string& rhs); + friend String operator+ (const std::string& lhs, const String& rhs); + +private: + char* cstr_; + size_t len_; + + char* allocate(size_t len); // len without trailing 0 + void deallocate(); + + String(int); // disabled and invalid. Catch invalid usages like, commandLineParser.has(0) problem +}; + +//! @} core_basic + +////////////////////////// cv::String implementation ///////////////////////// + +//! @cond IGNORED + +inline +String::String() + : cstr_(0), len_(0) +{} + +inline +String::String(const String& str) + : cstr_(str.cstr_), len_(str.len_) +{ + if (cstr_) + CV_XADD(((int*)cstr_)-1, 1); +} + +inline +String::String(const String& str, size_t pos, size_t len) + : cstr_(0), len_(0) +{ + pos = min(pos, str.len_); + len = min(str.len_ - pos, len); + if (!len) return; + if (len == str.len_) + { + CV_XADD(((int*)str.cstr_)-1, 1); + cstr_ = str.cstr_; + len_ = str.len_; + return; + } + memcpy(allocate(len), str.cstr_ + pos, len); +} + +inline +String::String(const char* s) + : cstr_(0), len_(0) +{ + if (!s) return; + size_t len = strlen(s); + if (!len) return; + memcpy(allocate(len), s, len); +} + +inline +String::String(const char* s, size_t n) + : cstr_(0), len_(0) +{ + if (!n) return; + if (!s) return; + memcpy(allocate(n), s, n); +} + +inline +String::String(size_t n, char c) + : cstr_(0), len_(0) +{ + if (!n) return; + memset(allocate(n), c, n); +} + +inline +String::String(const char* first, const char* last) + : cstr_(0), len_(0) +{ + size_t len = (size_t)(last - first); + if (!len) return; + memcpy(allocate(len), first, len); +} + +template inline +String::String(Iterator first, Iterator last) + : cstr_(0), len_(0) +{ + size_t len = (size_t)(last - first); + if (!len) return; + char* str = allocate(len); + while (first != last) + { + *str++ = *first; + ++first; + } +} + +inline +String::~String() +{ + deallocate(); +} + +inline +String& String::operator=(const String& str) +{ + if (&str == this) return *this; + + deallocate(); + if (str.cstr_) CV_XADD(((int*)str.cstr_)-1, 1); + cstr_ = str.cstr_; + len_ = str.len_; + return *this; +} + +inline +String& String::operator=(const char* s) +{ + deallocate(); + if (!s) return *this; + size_t len = strlen(s); + if (len) memcpy(allocate(len), s, len); + return *this; +} + +inline +String& String::operator=(char c) +{ + deallocate(); + allocate(1)[0] = c; + return *this; +} + +inline +String& String::operator+=(const String& str) +{ + *this = *this + str; + return *this; +} + +inline +String& String::operator+=(const char* s) +{ + *this = *this + s; + return *this; +} + +inline +String& String::operator+=(char c) +{ + *this = *this + c; + return *this; +} + +inline +size_t String::size() const +{ + return len_; +} + +inline +size_t String::length() const +{ + return len_; +} + +inline +char String::operator[](size_t idx) const +{ + return cstr_[idx]; +} + +inline +char String::operator[](int idx) const +{ + return cstr_[idx]; +} + +inline +const char* String::begin() const +{ + return cstr_; +} + +inline +const char* String::end() const +{ + return len_ ? cstr_ + len_ : NULL; +} + +inline +bool String::empty() const +{ + return len_ == 0; +} + +inline +const char* String::c_str() const +{ + return cstr_ ? cstr_ : ""; +} + +inline +void String::swap(String& str) +{ + cv::swap(cstr_, str.cstr_); + cv::swap(len_, str.len_); +} + +inline +void String::clear() +{ + deallocate(); +} + +inline +int String::compare(const char* s) const +{ + if (cstr_ == s) return 0; + return strcmp(c_str(), s); +} + +inline +int String::compare(const String& str) const +{ + if (cstr_ == str.cstr_) return 0; + return strcmp(c_str(), str.c_str()); +} + +inline +String String::substr(size_t pos, size_t len) const +{ + return String(*this, pos, len); +} + +inline +size_t String::find(const char* s, size_t pos, size_t n) const +{ + if (n == 0 || pos + n > len_) return npos; + const char* lmax = cstr_ + len_ - n; + for (const char* i = cstr_ + pos; i <= lmax; ++i) + { + size_t j = 0; + while (j < n && s[j] == i[j]) ++j; + if (j == n) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find(char c, size_t pos) const +{ + return find(&c, pos, 1); +} + +inline +size_t String::find(const String& str, size_t pos) const +{ + return find(str.c_str(), pos, str.len_); +} + +inline +size_t String::find(const char* s, size_t pos) const +{ + if (pos >= len_ || !s[0]) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + size_t j = 0; + while (s[j] && s[j] == i[j]) + { if(i + j >= lmax) return npos; + ++j; + } + if (!s[j]) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::rfind(const char* s, size_t pos, size_t n) const +{ + if (n > len_) return npos; + if (pos > len_ - n) pos = len_ - n; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + size_t j = 0; + while (j < n && s[j] == i[j]) ++j; + if (j == n) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::rfind(char c, size_t pos) const +{ + return rfind(&c, pos, 1); +} + +inline +size_t String::rfind(const String& str, size_t pos) const +{ + return rfind(str.c_str(), pos, str.len_); +} + +inline +size_t String::rfind(const char* s, size_t pos) const +{ + return rfind(s, pos, strlen(s)); +} + +inline +size_t String::find_first_of(const char* s, size_t pos, size_t n) const +{ + if (n == 0 || pos + n > len_) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + for (size_t j = 0; j < n; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_first_of(char c, size_t pos) const +{ + return find_first_of(&c, pos, 1); +} + +inline +size_t String::find_first_of(const String& str, size_t pos) const +{ + return find_first_of(str.c_str(), pos, str.len_); +} + +inline +size_t String::find_first_of(const char* s, size_t pos) const +{ + if (len_ == 0) return npos; + if (pos >= len_ || !s[0]) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + for (size_t j = 0; s[j]; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_last_of(const char* s, size_t pos, size_t n) const +{ + if (len_ == 0) return npos; + if (pos >= len_) pos = len_ - 1; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + for (size_t j = 0; j < n; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_last_of(char c, size_t pos) const +{ + return find_last_of(&c, pos, 1); +} + +inline +size_t String::find_last_of(const String& str, size_t pos) const +{ + return find_last_of(str.c_str(), pos, str.len_); +} + +inline +size_t String::find_last_of(const char* s, size_t pos) const +{ + if (len_ == 0) return npos; + if (pos >= len_) pos = len_ - 1; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + for (size_t j = 0; s[j]; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +String String::toLowerCase() const +{ + if (!cstr_) + return String(); + String res(cstr_, len_); + for (size_t i = 0; i < len_; ++i) + res.cstr_[i] = (char) ::tolower(cstr_[i]); + + return res; +} + +//! @endcond + +// ************************* cv::String non-member functions ************************* + +//! @relates cv::String +//! @{ + +inline +String operator + (const String& lhs, const String& rhs) +{ + String s; + s.allocate(lhs.len_ + rhs.len_); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhs.len_) memcpy(s.cstr_ + lhs.len_, rhs.cstr_, rhs.len_); + return s; +} + +inline +String operator + (const String& lhs, const char* rhs) +{ + String s; + size_t rhslen = strlen(rhs); + s.allocate(lhs.len_ + rhslen); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhslen) memcpy(s.cstr_ + lhs.len_, rhs, rhslen); + return s; +} + +inline +String operator + (const char* lhs, const String& rhs) +{ + String s; + size_t lhslen = strlen(lhs); + s.allocate(lhslen + rhs.len_); + if (lhslen) memcpy(s.cstr_, lhs, lhslen); + if (rhs.len_) memcpy(s.cstr_ + lhslen, rhs.cstr_, rhs.len_); + return s; +} + +inline +String operator + (const String& lhs, char rhs) +{ + String s; + s.allocate(lhs.len_ + 1); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + s.cstr_[lhs.len_] = rhs; + return s; +} + +inline +String operator + (char lhs, const String& rhs) +{ + String s; + s.allocate(rhs.len_ + 1); + s.cstr_[0] = lhs; + if (rhs.len_) memcpy(s.cstr_ + 1, rhs.cstr_, rhs.len_); + return s; +} + +static inline bool operator== (const String& lhs, const String& rhs) { return 0 == lhs.compare(rhs); } +static inline bool operator== (const char* lhs, const String& rhs) { return 0 == rhs.compare(lhs); } +static inline bool operator== (const String& lhs, const char* rhs) { return 0 == lhs.compare(rhs); } +static inline bool operator!= (const String& lhs, const String& rhs) { return 0 != lhs.compare(rhs); } +static inline bool operator!= (const char* lhs, const String& rhs) { return 0 != rhs.compare(lhs); } +static inline bool operator!= (const String& lhs, const char* rhs) { return 0 != lhs.compare(rhs); } +static inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } +static inline bool operator< (const char* lhs, const String& rhs) { return rhs.compare(lhs) > 0; } +static inline bool operator< (const String& lhs, const char* rhs) { return lhs.compare(rhs) < 0; } +static inline bool operator<= (const String& lhs, const String& rhs) { return lhs.compare(rhs) <= 0; } +static inline bool operator<= (const char* lhs, const String& rhs) { return rhs.compare(lhs) >= 0; } +static inline bool operator<= (const String& lhs, const char* rhs) { return lhs.compare(rhs) <= 0; } +static inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } +static inline bool operator> (const char* lhs, const String& rhs) { return rhs.compare(lhs) < 0; } +static inline bool operator> (const String& lhs, const char* rhs) { return lhs.compare(rhs) > 0; } +static inline bool operator>= (const String& lhs, const String& rhs) { return lhs.compare(rhs) >= 0; } +static inline bool operator>= (const char* lhs, const String& rhs) { return rhs.compare(lhs) <= 0; } +static inline bool operator>= (const String& lhs, const char* rhs) { return lhs.compare(rhs) >= 0; } + + +#ifndef OPENCV_DISABLE_STRING_LOWER_UPPER_CONVERSIONS + +//! @cond IGNORED +namespace details { +// std::tolower is int->int +static inline char char_tolower(char ch) +{ + return (char)std::tolower((int)ch); +} +// std::toupper is int->int +static inline char char_toupper(char ch) +{ + return (char)std::toupper((int)ch); +} +} // namespace details +//! @endcond + +static inline std::string toLowerCase(const std::string& str) +{ + std::string result(str); + std::transform(result.begin(), result.end(), result.begin(), details::char_tolower); + return result; +} + +static inline std::string toUpperCase(const std::string& str) +{ + std::string result(str); + std::transform(result.begin(), result.end(), result.begin(), details::char_toupper); + return result; +} + +#endif // OPENCV_DISABLE_STRING_LOWER_UPPER_CONVERSIONS + +//! @} relates cv::String + +} // cv + +namespace std +{ + static inline void swap(cv::String& a, cv::String& b) { a.swap(b); } +} + +#include "opencv2/core/ptr.inl.hpp" + +#endif //OPENCV_CORE_CVSTD_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.inl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.inl.hpp new file mode 100644 index 0000000..36c83e2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/cvstd.inl.hpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVSTDINL_HPP +#define OPENCV_CORE_CVSTDINL_HPP + +#include +#include +#include + +//! @cond IGNORED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable: 4127 ) +#endif + +namespace cv +{ + +template class DataType< std::complex<_Tp> > +{ +public: + typedef std::complex<_Tp> value_type; + typedef value_type work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) }; + + typedef Vec vec_type; +}; + +inline +String::String(const std::string& str) + : cstr_(0), len_(0) +{ + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); +} + +inline +String::String(const std::string& str, size_t pos, size_t len) + : cstr_(0), len_(0) +{ + size_t strlen = str.size(); + pos = min(pos, strlen); + len = min(strlen - pos, len); + if (!len) return; + memcpy(allocate(len), str.c_str() + pos, len); +} + +inline +String& String::operator = (const std::string& str) +{ + deallocate(); + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); + return *this; +} + +inline +String& String::operator += (const std::string& str) +{ + *this = *this + str; + return *this; +} + +inline +String::operator std::string() const +{ + return std::string(cstr_, len_); +} + +inline +String operator + (const String& lhs, const std::string& rhs) +{ + String s; + size_t rhslen = rhs.size(); + s.allocate(lhs.len_ + rhslen); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhslen) memcpy(s.cstr_ + lhs.len_, rhs.c_str(), rhslen); + return s; +} + +inline +String operator + (const std::string& lhs, const String& rhs) +{ + String s; + size_t lhslen = lhs.size(); + s.allocate(lhslen + rhs.len_); + if (lhslen) memcpy(s.cstr_, lhs.c_str(), lhslen); + if (rhs.len_) memcpy(s.cstr_ + lhslen, rhs.cstr_, rhs.len_); + return s; +} + +inline +FileNode::operator std::string() const +{ + String value; + read(*this, value, value); + return value; +} + +template<> inline +void operator >> (const FileNode& n, std::string& value) +{ + read(n, value, std::string()); +} + +template<> inline +FileStorage& operator << (FileStorage& fs, const std::string& value) +{ + return fs << cv::String(value); +} + +static inline +std::ostream& operator << (std::ostream& os, const String& str) +{ + return os << str.c_str(); +} + +static inline +std::ostream& operator << (std::ostream& out, Ptr fmtd) +{ + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + out << str; + return out; +} + +static inline +std::ostream& operator << (std::ostream& out, const Mat& mtx) +{ + return out << Formatter::get()->format(mtx); +} + +static inline +std::ostream& operator << (std::ostream& out, const UMat& m) +{ + return out << m.getMat(ACCESS_READ); +} + +template static inline +std::ostream& operator << (std::ostream& out, const Complex<_Tp>& c) +{ + return out << "(" << c.re << "," << c.im << ")"; +} + +template static inline +std::ostream& operator << (std::ostream& out, const std::vector >& vec) +{ + return out << Formatter::get()->format(Mat(vec)); +} + + +template static inline +std::ostream& operator << (std::ostream& out, const std::vector >& vec) +{ + return out << Formatter::get()->format(Mat(vec)); +} + + +template static inline +std::ostream& operator << (std::ostream& out, const Matx<_Tp, m, n>& matx) +{ + return out << Formatter::get()->format(Mat(matx)); +} + +template static inline +std::ostream& operator << (std::ostream& out, const Point_<_Tp>& p) +{ + out << "[" << p.x << ", " << p.y << "]"; + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Point3_<_Tp>& p) +{ + out << "[" << p.x << ", " << p.y << ", " << p.z << "]"; + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Vec<_Tp, n>& vec) +{ + out << "["; + if (cv::traits::Depth<_Tp>::value <= CV_32S) + { + for (int i = 0; i < n - 1; ++i) { + out << (int)vec[i] << ", "; + } + out << (int)vec[n-1] << "]"; + } + else + { + for (int i = 0; i < n - 1; ++i) { + out << vec[i] << ", "; + } + out << vec[n-1] << "]"; + } + + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Size_<_Tp>& size) +{ + return out << "[" << size.width << " x " << size.height << "]"; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Rect_<_Tp>& rect) +{ + return out << "[" << rect.width << " x " << rect.height << " from (" << rect.x << ", " << rect.y << ")]"; +} + +static inline std::ostream& operator << (std::ostream& out, const MatSize& msize) +{ + int i, dims = msize.dims(); + for( i = 0; i < dims; i++ ) + { + out << msize[i]; + if( i < dims-1 ) + out << " x "; + } + return out; +} + +static inline std::ostream &operator<< (std::ostream &s, cv::Range &r) +{ + return s << "[" << r.start << " : " << r.end << ")"; +} + +} // cv + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +//! @endcond + +#endif // OPENCV_CORE_CVSTDINL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/detail/async_promise.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/detail/async_promise.hpp new file mode 100644 index 0000000..6eb3fb5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/detail/async_promise.hpp @@ -0,0 +1,71 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ASYNC_PROMISE_HPP +#define OPENCV_CORE_ASYNC_PROMISE_HPP + +#include "../async.hpp" + +#include "exception_ptr.hpp" + +namespace cv { + +/** @addtogroup core_async +@{ +*/ + + +/** @brief Provides result of asynchronous operations + +*/ +class CV_EXPORTS AsyncPromise +{ +public: + ~AsyncPromise() CV_NOEXCEPT; + AsyncPromise() CV_NOEXCEPT; + explicit AsyncPromise(const AsyncPromise& o) CV_NOEXCEPT; + AsyncPromise& operator=(const AsyncPromise& o) CV_NOEXCEPT; + void release() CV_NOEXCEPT; + + /** Returns associated AsyncArray + @note Can be called once + */ + AsyncArray getArrayResult(); + + /** Stores asynchronous result. + @param[in] value result + */ + void setValue(InputArray value); + + // TODO "move" setters + +#if CV__EXCEPTION_PTR + /** Stores exception. + @param[in] exception exception to be raised in AsyncArray + */ + void setException(std::exception_ptr exception); +#endif + + /** Stores exception. + @param[in] exception exception to be raised in AsyncArray + */ + void setException(const cv::Exception& exception); + +#ifdef CV_CXX11 + explicit AsyncPromise(AsyncPromise&& o) { p = o.p; o.p = NULL; } + AsyncPromise& operator=(AsyncPromise&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; } +#endif + + + // PImpl + typedef struct AsyncArray::Impl Impl; friend struct AsyncArray::Impl; + inline void* _getImpl() const CV_NOEXCEPT { return p; } +protected: + Impl* p; +}; + + +//! @} +} // namespace +#endif // OPENCV_CORE_ASYNC_PROMISE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/detail/exception_ptr.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/detail/exception_ptr.hpp new file mode 100644 index 0000000..d98ffc4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/detail/exception_ptr.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_DETAILS_EXCEPTION_PTR_H +#define OPENCV_CORE_DETAILS_EXCEPTION_PTR_H + +#ifndef CV__EXCEPTION_PTR +# if defined(__ANDROID__) && defined(ATOMIC_INT_LOCK_FREE) && ATOMIC_INT_LOCK_FREE < 2 +# define CV__EXCEPTION_PTR 0 // Not supported, details: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58938 +# elif defined(CV_CXX11) +# define CV__EXCEPTION_PTR 1 +# elif defined(_MSC_VER) +# define CV__EXCEPTION_PTR (_MSC_VER >= 1600) +# elif defined(__clang__) +# define CV__EXCEPTION_PTR 0 // C++11 only (see above) +# elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CV__EXCEPTION_PTR (__GXX_EXPERIMENTAL_CXX0X__ > 0) +# endif +#endif +#ifndef CV__EXCEPTION_PTR +# define CV__EXCEPTION_PTR 0 +#elif CV__EXCEPTION_PTR +# include // std::exception_ptr +#endif + +#endif // OPENCV_CORE_DETAILS_EXCEPTION_PTR_H diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/directx.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/directx.hpp new file mode 100644 index 0000000..056a85a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/directx.hpp @@ -0,0 +1,184 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors 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 copyright holders or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_DIRECTX_HPP +#define OPENCV_CORE_DIRECTX_HPP + +#include "mat.hpp" +#include "ocl.hpp" + +#if !defined(__d3d11_h__) +struct ID3D11Device; +struct ID3D11Texture2D; +#endif + +#if !defined(__d3d10_h__) +struct ID3D10Device; +struct ID3D10Texture2D; +#endif + +#if !defined(_D3D9_H_) +struct IDirect3DDevice9; +struct IDirect3DDevice9Ex; +struct IDirect3DSurface9; +#endif + + +namespace cv { namespace directx { + +namespace ocl { +using namespace cv::ocl; + +//! @addtogroup core_directx +// This section describes OpenCL and DirectX interoperability. +// +// To enable DirectX support, configure OpenCV using CMake with WITH_DIRECTX=ON . Note, DirectX is +// supported only on Windows. +// +// To use OpenCL functionality you should first initialize OpenCL context from DirectX resource. +// +//! @{ + +// TODO static functions in the Context class +//! @brief Creates OpenCL context from D3D11 device +// +//! @param pD3D11Device - pointer to D3D11 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromD3D11Device(ID3D11Device* pD3D11Device); + +//! @brief Creates OpenCL context from D3D10 device +// +//! @param pD3D10Device - pointer to D3D10 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromD3D10Device(ID3D10Device* pD3D10Device); + +//! @brief Creates OpenCL context from Direct3DDevice9Ex device +// +//! @param pDirect3DDevice9Ex - pointer to Direct3DDevice9Ex device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromDirect3DDevice9Ex(IDirect3DDevice9Ex* pDirect3DDevice9Ex); + +//! @brief Creates OpenCL context from Direct3DDevice9 device +// +//! @param pDirect3DDevice9 - pointer to Direct3Device9 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromDirect3DDevice9(IDirect3DDevice9* pDirect3DDevice9); + +//! @} + +} // namespace cv::directx::ocl + +//! @addtogroup core_directx +//! @{ + +//! @brief Converts InputArray to ID3D11Texture2D. If destination texture format is DXGI_FORMAT_NV12 then +//! input UMat expected to be in BGR format and data will be downsampled and color-converted to NV12. +// +//! @note Note: Destination texture must be allocated by application. Function does memory copy from src to +//! pD3D11Texture2D +// +//! @param src - source InputArray +//! @param pD3D11Texture2D - destination D3D11 texture +CV_EXPORTS void convertToD3D11Texture2D(InputArray src, ID3D11Texture2D* pD3D11Texture2D); + +//! @brief Converts ID3D11Texture2D to OutputArray. If input texture format is DXGI_FORMAT_NV12 then +//! data will be upsampled and color-converted to BGR format. +// +//! @note Note: Destination matrix will be re-allocated if it has not enough memory to match texture size. +//! function does memory copy from pD3D11Texture2D to dst +// +//! @param pD3D11Texture2D - source D3D11 texture +//! @param dst - destination OutputArray +CV_EXPORTS void convertFromD3D11Texture2D(ID3D11Texture2D* pD3D11Texture2D, OutputArray dst); + +//! @brief Converts InputArray to ID3D10Texture2D +// +//! @note Note: function does memory copy from src to +//! pD3D10Texture2D +// +//! @param src - source InputArray +//! @param pD3D10Texture2D - destination D3D10 texture +CV_EXPORTS void convertToD3D10Texture2D(InputArray src, ID3D10Texture2D* pD3D10Texture2D); + +//! @brief Converts ID3D10Texture2D to OutputArray +// +//! @note Note: function does memory copy from pD3D10Texture2D +//! to dst +// +//! @param pD3D10Texture2D - source D3D10 texture +//! @param dst - destination OutputArray +CV_EXPORTS void convertFromD3D10Texture2D(ID3D10Texture2D* pD3D10Texture2D, OutputArray dst); + +//! @brief Converts InputArray to IDirect3DSurface9 +// +//! @note Note: function does memory copy from src to +//! pDirect3DSurface9 +// +//! @param src - source InputArray +//! @param pDirect3DSurface9 - destination D3D10 texture +//! @param surfaceSharedHandle - shared handle +CV_EXPORTS void convertToDirect3DSurface9(InputArray src, IDirect3DSurface9* pDirect3DSurface9, void* surfaceSharedHandle = NULL); + +//! @brief Converts IDirect3DSurface9 to OutputArray +// +//! @note Note: function does memory copy from pDirect3DSurface9 +//! to dst +// +//! @param pDirect3DSurface9 - source D3D10 texture +//! @param dst - destination OutputArray +//! @param surfaceSharedHandle - shared handle +CV_EXPORTS void convertFromDirect3DSurface9(IDirect3DSurface9* pDirect3DSurface9, OutputArray dst, void* surfaceSharedHandle = NULL); + +//! @brief Get OpenCV type from DirectX type +//! @param iDXGI_FORMAT - enum DXGI_FORMAT for D3D10/D3D11 +//! @return OpenCV type or -1 if there is no equivalent +CV_EXPORTS int getTypeFromDXGI_FORMAT(const int iDXGI_FORMAT); // enum DXGI_FORMAT for D3D10/D3D11 + +//! @brief Get OpenCV type from DirectX type +//! @param iD3DFORMAT - enum D3DTYPE for D3D9 +//! @return OpenCV type or -1 if there is no equivalent +CV_EXPORTS int getTypeFromD3DFORMAT(const int iD3DFORMAT); // enum D3DTYPE for D3D9 + +//! @} + +} } // namespace cv::directx + +#endif // OPENCV_CORE_DIRECTX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/eigen.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/eigen.hpp new file mode 100644 index 0000000..741648e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/eigen.hpp @@ -0,0 +1,280 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + + +#ifndef OPENCV_CORE_EIGEN_HPP +#define OPENCV_CORE_EIGEN_HPP + +#include "opencv2/core.hpp" + +#if defined _MSC_VER && _MSC_VER >= 1200 +#pragma warning( disable: 4714 ) //__forceinline is not inlined +#pragma warning( disable: 4127 ) //conditional expression is constant +#pragma warning( disable: 4244 ) //conversion from '__int64' to 'int', possible loss of data +#endif + +namespace cv +{ + +//! @addtogroup core_eigen +//! @{ + +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + Mat _src(src.cols(), src.rows(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + transpose(_src, dst); + } + else + { + Mat _src(src.rows(), src.cols(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + _src.copyTo(dst); + } +} + +// Matx case +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, + Matx<_Tp, _rows, _cols>& dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + dst = Matx<_Tp, _cols, _rows>(static_cast(src.data())).t(); + } + else + { + dst = Matx<_Tp, _rows, _cols>(static_cast(src.data())); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) +{ + CV_DbgAssert(src.rows == _rows && src.cols == _cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else if( src.cols == src.rows ) + { + src.convertTo(_dst, _dst.type()); + transpose(_dst, _dst); + } + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, _cols>& src, + Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) +{ + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst ) +{ + dst.resize(src.rows, src.cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else if( src.cols == src.rows ) + { + src.convertTo(_dst, _dst.type()); + transpose(_dst, _dst); + } + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, _cols>& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst ) +{ + dst.resize(_rows, _cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst ) +{ + CV_Assert(src.cols == 1); + dst.resize(src.rows); + + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, 1>& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst ) +{ + dst.resize(_rows); + + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(1, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, 1, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.copyTo(_dst); + } +} + + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst ) +{ + CV_Assert(src.rows == 1); + dst.resize(src.cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +//Matx +template static inline +void cv2eigen( const Matx<_Tp, 1, _cols>& src, + Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst ) +{ + dst.resize(_cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, 1, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(1, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +//! @} + +} // cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/fast_math.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/fast_math.hpp new file mode 100644 index 0000000..0f53cf5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/fast_math.hpp @@ -0,0 +1,408 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_FAST_MATH_HPP +#define OPENCV_CORE_FAST_MATH_HPP + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils +//! @{ + +/****************************************************************************************\ +* fast math * +\****************************************************************************************/ + +#ifdef __cplusplus +# include +#else +# ifdef __BORLANDC__ +# include +# else +# include +# endif +#endif + +#if defined(__CUDACC__) + // nothing, intrinsics/asm code is not supported +#else + #if ((defined _MSC_VER && defined _M_X64) \ + || (defined __GNUC__ && defined __x86_64__ && defined __SSE2__)) \ + && !defined(OPENCV_SKIP_INCLUDE_EMMINTRIN_H) + #include + #endif + + #if defined __PPC64__ && defined __GNUC__ && defined _ARCH_PWR8 \ + && !defined(OPENCV_SKIP_INCLUDE_ALTIVEC_H) + #include + #endif + + #if defined(CV_INLINE_ROUND_FLT) + // user-specified version + // CV_INLINE_ROUND_DBL should be defined too + #elif defined __GNUC__ && defined __arm__ && (defined __ARM_PCS_VFP || defined __ARM_VFPV3__ || defined __ARM_NEON__) && !defined __SOFTFP__ + // 1. general scheme + #define ARM_ROUND(_value, _asm_string) \ + int res; \ + float temp; \ + CV_UNUSED(temp); \ + __asm__(_asm_string : [res] "=r" (res), [temp] "=w" (temp) : [value] "w" (_value)); \ + return res + // 2. version for double + #ifdef __clang__ + #define CV_INLINE_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %[value] \n vmov %[res], %[temp]") + #else + #define CV_INLINE_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %P[value] \n vmov %[res], %[temp]") + #endif + // 3. version for float + #define CV_INLINE_ROUND_FLT(value) ARM_ROUND(value, "vcvtr.s32.f32 %[temp], %[value]\n vmov %[res], %[temp]") + #elif defined __PPC64__ && defined __GNUC__ && defined _ARCH_PWR8 + // P8 and newer machines can convert fp32/64 to int quickly. + #define CV_INLINE_ROUND_DBL(value) \ + int out; \ + double temp; \ + __asm__( "fctiw %[temp],%[in]\n\tmfvsrwz %[out],%[temp]\n\t" : [out] "=r" (out), [temp] "=d" (temp) : [in] "d" ((double)(value)) : ); \ + return out; + + // FP32 also works with FP64 routine above + #define CV_INLINE_ROUND_FLT(value) CV_INLINE_ROUND_DBL(value) + #endif + + #ifdef CV_INLINE_ISINF_FLT + // user-specified version + // CV_INLINE_ISINF_DBL should be defined too + #elif defined __PPC64__ && defined _ARCH_PWR9 && defined(scalar_test_data_class) + #define CV_INLINE_ISINF_DBL(value) return scalar_test_data_class(value, 0x30); + #define CV_INLINE_ISINF_FLT(value) CV_INLINE_ISINF_DBL(value) + #endif + + #ifdef CV_INLINE_ISNAN_FLT + // user-specified version + // CV_INLINE_ISNAN_DBL should be defined too + #elif defined __PPC64__ && defined _ARCH_PWR9 && defined(scalar_test_data_class) + #define CV_INLINE_ISNAN_DBL(value) return scalar_test_data_class(value, 0x40); + #define CV_INLINE_ISNAN_FLT(value) CV_INLINE_ISNAN_DBL(value) + #endif + + #if !defined(OPENCV_USE_FASTMATH_BUILTINS) \ + && ( \ + defined(__x86_64__) || defined(__i686__) \ + || defined(__arm__) \ + || defined(__PPC64__) \ + ) + /* Let builtin C math functions when available. Dedicated hardware is available to + round and convert FP values. */ + #define OPENCV_USE_FASTMATH_BUILTINS 1 + #endif + + /* Enable builtin math functions if possible, desired, and available. + Note, not all math functions inline equally. E.g lrint will not inline + without the -fno-math-errno option. */ + #if defined(CV_ICC) + // nothing + #elif defined(OPENCV_USE_FASTMATH_BUILTINS) && OPENCV_USE_FASTMATH_BUILTINS + #if defined(__clang__) + #define CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS + #if !defined(CV_INLINE_ISNAN_DBL) && __has_builtin(__builtin_isnan) + #define CV_INLINE_ISNAN_DBL(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) && __has_builtin(__builtin_isnan) + #define CV_INLINE_ISNAN_FLT(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) && __has_builtin(__builtin_isinf) + #define CV_INLINE_ISINF_DBL(value) return __builtin_isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) && __has_builtin(__builtin_isinf) + #define CV_INLINE_ISINF_FLT(value) return __builtin_isinf(value); + #endif + #elif defined(__GNUC__) + #define CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS + #if !defined(CV_INLINE_ISNAN_DBL) + #define CV_INLINE_ISNAN_DBL(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) + #define CV_INLINE_ISNAN_FLT(value) return __builtin_isnanf(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) + #define CV_INLINE_ISINF_DBL(value) return __builtin_isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) + #define CV_INLINE_ISINF_FLT(value) return __builtin_isinff(value); + #endif + #elif defined(_MSC_VER) + #if !defined(CV_INLINE_ISNAN_DBL) + #define CV_INLINE_ISNAN_DBL(value) return isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) + #define CV_INLINE_ISNAN_FLT(value) return isnan(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) + #define CV_INLINE_ISINF_DBL(value) return isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) + #define CV_INLINE_ISINF_FLT(value) return isinf(value); + #endif + #endif + #endif + +#endif // defined(__CUDACC__) + +/** @brief Rounds floating-point number to the nearest integer + + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int +cvRound( double value ) +{ +#if defined CV_INLINE_ROUND_DBL + CV_INLINE_ROUND_DBL(value); +#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \ + && defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \ + && !defined(__CUDACC__) + __m128d t = _mm_set_sd( value ); + return _mm_cvtsd_si32(t); +#elif defined _MSC_VER && defined _M_IX86 + int t; + __asm + { + fld value; + fistp t; + } + return t; +#elif defined CV_ICC || defined __GNUC__ + return (int)(lrint(value)); +#else + /* it's ok if round does not comply with IEEE754 standard; + the tests should allow +/-1 difference when the tested functions use round */ + return (int)(value + (value >= 0 ? 0.5 : -0.5)); +#endif +} + + +/** @brief Rounds floating-point number to the nearest integer not larger than the original. + + The function computes an integer i such that: + \f[i \le \texttt{value} < i+1\f] + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int cvFloor( double value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_floor(value); +#else + int i = (int)value; + return i - (i > value); +#endif +} + +/** @brief Rounds floating-point number to the nearest integer not smaller than the original. + + The function computes an integer i such that: + \f[i \le \texttt{value} < i+1\f] + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int cvCeil( double value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_ceil(value); +#else + int i = (int)value; + return i + (i < value); +#endif +} + +/** @brief Determines if the argument is Not A Number. + + @param value The input floating-point value + + The function returns 1 if the argument is Not A Number (as defined by IEEE754 standard), 0 + otherwise. */ +CV_INLINE int cvIsNaN( double value ) +{ +#if defined CV_INLINE_ISNAN_DBL + CV_INLINE_ISNAN_DBL(value); +#else + Cv64suf ieee754; + ieee754.f = value; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > 0x7ff00000; +#endif +} + +/** @brief Determines if the argument is Infinity. + + @param value The input floating-point value + + The function returns 1 if the argument is a plus or minus infinity (as defined by IEEE754 standard) + and 0 otherwise. */ +CV_INLINE int cvIsInf( double value ) +{ +#if defined CV_INLINE_ISINF_DBL + CV_INLINE_ISINF_DBL(value); +#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__PPC64__) + Cv64suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff00000000) == + 0x7ff0000000000000; +#else + Cv64suf ieee754; + ieee754.f = value; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && + (unsigned)ieee754.u == 0; +#endif +} + +#ifdef __cplusplus + +/** @overload */ +CV_INLINE int cvRound(float value) +{ +#if defined CV_INLINE_ROUND_FLT + CV_INLINE_ROUND_FLT(value); +#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \ + && defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \ + && !defined(__CUDACC__) + __m128 t = _mm_set_ss( value ); + return _mm_cvtss_si32(t); +#elif defined _MSC_VER && defined _M_IX86 + int t; + __asm + { + fld value; + fistp t; + } + return t; +#elif defined CV_ICC || defined __GNUC__ + return (int)(lrintf(value)); +#else + /* it's ok if round does not comply with IEEE754 standard; + the tests should allow +/-1 difference when the tested functions use round */ + return (int)(value + (value >= 0 ? 0.5f : -0.5f)); +#endif +} + +/** @overload */ +CV_INLINE int cvRound( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvFloor( float value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_floorf(value); +#else + int i = (int)value; + return i - (i > value); +#endif +} + +/** @overload */ +CV_INLINE int cvFloor( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvCeil( float value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_ceilf(value); +#else + int i = (int)value; + return i + (i < value); +#endif +} + +/** @overload */ +CV_INLINE int cvCeil( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvIsNaN( float value ) +{ +#if defined CV_INLINE_ISNAN_FLT + CV_INLINE_ISNAN_FLT(value); +#else + Cv32suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff) > 0x7f800000; +#endif +} + +/** @overload */ +CV_INLINE int cvIsInf( float value ) +{ +#if defined CV_INLINE_ISINF_FLT + CV_INLINE_ISINF_FLT(value); +#else + Cv32suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff) == 0x7f800000; +#endif +} + +#endif // __cplusplus + +//! @} core_utils + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/hal.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/hal.hpp new file mode 100644 index 0000000..68900ec --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/hal.hpp @@ -0,0 +1,250 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_HPP +#define OPENCV_HAL_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/hal/interface.h" + +namespace cv { namespace hal { + +//! @addtogroup core_hal_functions +//! @{ + +CV_EXPORTS int normHamming(const uchar* a, int n); +CV_EXPORTS int normHamming(const uchar* a, const uchar* b, int n); + +CV_EXPORTS int normHamming(const uchar* a, int n, int cellSize); +CV_EXPORTS int normHamming(const uchar* a, const uchar* b, int n, int cellSize); + +CV_EXPORTS int LU32f(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS int LU64f(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky32f(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky64f(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS void SVD32f(float* At, size_t astep, float* W, float* U, size_t ustep, float* Vt, size_t vstep, int m, int n, int flags); +CV_EXPORTS void SVD64f(double* At, size_t astep, double* W, double* U, size_t ustep, double* Vt, size_t vstep, int m, int n, int flags); +CV_EXPORTS int QR32f(float* A, size_t astep, int m, int n, int k, float* b, size_t bstep, float* hFactors); +CV_EXPORTS int QR64f(double* A, size_t astep, int m, int n, int k, double* b, size_t bstep, double* hFactors); + +CV_EXPORTS void gemm32f(const float* src1, size_t src1_step, const float* src2, size_t src2_step, + float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm64f(const double* src1, size_t src1_step, const double* src2, size_t src2_step, + double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm32fc(const float* src1, size_t src1_step, const float* src2, size_t src2_step, + float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm64fc(const double* src1, size_t src1_step, const double* src2, size_t src2_step, + double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); + +CV_EXPORTS int normL1_(const uchar* a, const uchar* b, int n); +CV_EXPORTS float normL1_(const float* a, const float* b, int n); +CV_EXPORTS float normL2Sqr_(const float* a, const float* b, int n); + +CV_EXPORTS void exp32f(const float* src, float* dst, int n); +CV_EXPORTS void exp64f(const double* src, double* dst, int n); +CV_EXPORTS void log32f(const float* src, float* dst, int n); +CV_EXPORTS void log64f(const double* src, double* dst, int n); + +CV_EXPORTS void fastAtan32f(const float* y, const float* x, float* dst, int n, bool angleInDegrees); +CV_EXPORTS void fastAtan64f(const double* y, const double* x, double* dst, int n, bool angleInDegrees); +CV_EXPORTS void magnitude32f(const float* x, const float* y, float* dst, int n); +CV_EXPORTS void magnitude64f(const double* x, const double* y, double* dst, int n); +CV_EXPORTS void sqrt32f(const float* src, float* dst, int len); +CV_EXPORTS void sqrt64f(const double* src, double* dst, int len); +CV_EXPORTS void invSqrt32f(const float* src, float* dst, int len); +CV_EXPORTS void invSqrt64f(const double* src, double* dst, int len); + +CV_EXPORTS void split8u(const uchar* src, uchar** dst, int len, int cn ); +CV_EXPORTS void split16u(const ushort* src, ushort** dst, int len, int cn ); +CV_EXPORTS void split32s(const int* src, int** dst, int len, int cn ); +CV_EXPORTS void split64s(const int64* src, int64** dst, int len, int cn ); + +CV_EXPORTS void merge8u(const uchar** src, uchar* dst, int len, int cn ); +CV_EXPORTS void merge16u(const ushort** src, ushort* dst, int len, int cn ); +CV_EXPORTS void merge32s(const int** src, int* dst, int len, int cn ); +CV_EXPORTS void merge64s(const int64** src, int64* dst, int len, int cn ); + +CV_EXPORTS void add8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void sub8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void max8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void min8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void absdiff8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void and8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void or8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void xor8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void not8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void cmp8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp8s(const schar* src1, size_t step1, const schar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp16s(const short* src1, size_t step1, const short* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp32s(const int* src1, size_t step1, const int* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp32f(const float* src1, size_t step1, const float* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp64f(const double* src1, size_t step1, const double* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); + +CV_EXPORTS void mul8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void div8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void recip8u( const uchar *, size_t, const uchar * src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip8s( const schar *, size_t, const schar * src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip16u( const ushort *, size_t, const ushort * src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip16s( const short *, size_t, const short * src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip32s( const int *, size_t, const int * src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip32f( const float *, size_t, const float * src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip64f( const double *, size_t, const double * src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void addWeighted8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _scalars ); +CV_EXPORTS void addWeighted8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scalars ); + +struct CV_EXPORTS DFT1D +{ + static Ptr create(int len, int count, int depth, int flags, bool * useBuffer = 0); + virtual void apply(const uchar *src, uchar *dst) = 0; + virtual ~DFT1D() {} +}; + +struct CV_EXPORTS DFT2D +{ + static Ptr create(int width, int height, int depth, + int src_channels, int dst_channels, + int flags, int nonzero_rows = 0); + virtual void apply(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step) = 0; + virtual ~DFT2D() {} +}; + +struct CV_EXPORTS DCT2D +{ + static Ptr create(int width, int height, int depth, int flags); + virtual void apply(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step) = 0; + virtual ~DCT2D() {} +}; + +//! @} core_hal + +//============================================================================= +// for binary compatibility with 3.0 + +//! @cond IGNORED + +CV_EXPORTS int LU(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS int LU(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky(double* A, size_t astep, int m, double* b, size_t bstep, int n); + +CV_EXPORTS void exp(const float* src, float* dst, int n); +CV_EXPORTS void exp(const double* src, double* dst, int n); +CV_EXPORTS void log(const float* src, float* dst, int n); +CV_EXPORTS void log(const double* src, double* dst, int n); + +CV_EXPORTS void fastAtan2(const float* y, const float* x, float* dst, int n, bool angleInDegrees); +CV_EXPORTS void magnitude(const float* x, const float* y, float* dst, int n); +CV_EXPORTS void magnitude(const double* x, const double* y, double* dst, int n); +CV_EXPORTS void sqrt(const float* src, float* dst, int len); +CV_EXPORTS void sqrt(const double* src, double* dst, int len); +CV_EXPORTS void invSqrt(const float* src, float* dst, int len); +CV_EXPORTS void invSqrt(const double* src, double* dst, int len); + +//! @endcond + +}} //cv::hal + +#endif //OPENCV_HAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/interface.h b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/interface.h new file mode 100644 index 0000000..8f64025 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/interface.h @@ -0,0 +1,182 @@ +#ifndef OPENCV_CORE_HAL_INTERFACE_H +#define OPENCV_CORE_HAL_INTERFACE_H + +//! @addtogroup core_hal_interface +//! @{ + +//! @name Return codes +//! @{ +#define CV_HAL_ERROR_OK 0 +#define CV_HAL_ERROR_NOT_IMPLEMENTED 1 +#define CV_HAL_ERROR_UNKNOWN -1 +//! @} + +#ifdef __cplusplus +#include +#else +#include +#include +#endif + +//! @name Data types +//! primitive types +//! - schar - signed 1 byte integer +//! - uchar - unsigned 1 byte integer +//! - short - signed 2 byte integer +//! - ushort - unsigned 2 byte integer +//! - int - signed 4 byte integer +//! - uint - unsigned 4 byte integer +//! - int64 - signed 8 byte integer +//! - uint64 - unsigned 8 byte integer +//! @{ +#if !defined _MSC_VER && !defined __BORLANDC__ +# if defined __cplusplus && __cplusplus >= 201103L && !defined __APPLE__ +# include +# ifdef __NEWLIB__ + typedef unsigned int uint; +# else + typedef std::uint32_t uint; +# endif +# else +# include + typedef uint32_t uint; +# endif +#else + typedef unsigned uint; +#endif + +typedef signed char schar; + +#ifndef __IPL_H__ + typedef unsigned char uchar; + typedef unsigned short ushort; +#endif + +#if defined _MSC_VER || defined __BORLANDC__ + typedef __int64 int64; + typedef unsigned __int64 uint64; +# define CV_BIG_INT(n) n##I64 +# define CV_BIG_UINT(n) n##UI64 +#else + typedef int64_t int64; + typedef uint64_t uint64; +# define CV_BIG_INT(n) n##LL +# define CV_BIG_UINT(n) n##ULL +#endif + +#define CV_CN_MAX 512 +#define CV_CN_SHIFT 3 +#define CV_DEPTH_MAX (1 << CV_CN_SHIFT) + +#define CV_8U 0 +#define CV_8S 1 +#define CV_16U 2 +#define CV_16S 3 +#define CV_32S 4 +#define CV_32F 5 +#define CV_64F 6 +#define CV_USRTYPE1 7 + +#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1) +#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK) + +#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT)) +#define CV_MAKE_TYPE CV_MAKETYPE + +#define CV_8UC1 CV_MAKETYPE(CV_8U,1) +#define CV_8UC2 CV_MAKETYPE(CV_8U,2) +#define CV_8UC3 CV_MAKETYPE(CV_8U,3) +#define CV_8UC4 CV_MAKETYPE(CV_8U,4) +#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n)) + +#define CV_8SC1 CV_MAKETYPE(CV_8S,1) +#define CV_8SC2 CV_MAKETYPE(CV_8S,2) +#define CV_8SC3 CV_MAKETYPE(CV_8S,3) +#define CV_8SC4 CV_MAKETYPE(CV_8S,4) +#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n)) + +#define CV_16UC1 CV_MAKETYPE(CV_16U,1) +#define CV_16UC2 CV_MAKETYPE(CV_16U,2) +#define CV_16UC3 CV_MAKETYPE(CV_16U,3) +#define CV_16UC4 CV_MAKETYPE(CV_16U,4) +#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n)) + +#define CV_16SC1 CV_MAKETYPE(CV_16S,1) +#define CV_16SC2 CV_MAKETYPE(CV_16S,2) +#define CV_16SC3 CV_MAKETYPE(CV_16S,3) +#define CV_16SC4 CV_MAKETYPE(CV_16S,4) +#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n)) + +#define CV_32SC1 CV_MAKETYPE(CV_32S,1) +#define CV_32SC2 CV_MAKETYPE(CV_32S,2) +#define CV_32SC3 CV_MAKETYPE(CV_32S,3) +#define CV_32SC4 CV_MAKETYPE(CV_32S,4) +#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n)) + +#define CV_32FC1 CV_MAKETYPE(CV_32F,1) +#define CV_32FC2 CV_MAKETYPE(CV_32F,2) +#define CV_32FC3 CV_MAKETYPE(CV_32F,3) +#define CV_32FC4 CV_MAKETYPE(CV_32F,4) +#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n)) + +#define CV_64FC1 CV_MAKETYPE(CV_64F,1) +#define CV_64FC2 CV_MAKETYPE(CV_64F,2) +#define CV_64FC3 CV_MAKETYPE(CV_64F,3) +#define CV_64FC4 CV_MAKETYPE(CV_64F,4) +#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n)) +//! @} + +//! @name Comparison operation +//! @sa cv::CmpTypes +//! @{ +#define CV_HAL_CMP_EQ 0 +#define CV_HAL_CMP_GT 1 +#define CV_HAL_CMP_GE 2 +#define CV_HAL_CMP_LT 3 +#define CV_HAL_CMP_LE 4 +#define CV_HAL_CMP_NE 5 +//! @} + +//! @name Border processing modes +//! @sa cv::BorderTypes +//! @{ +#define CV_HAL_BORDER_CONSTANT 0 +#define CV_HAL_BORDER_REPLICATE 1 +#define CV_HAL_BORDER_REFLECT 2 +#define CV_HAL_BORDER_WRAP 3 +#define CV_HAL_BORDER_REFLECT_101 4 +#define CV_HAL_BORDER_TRANSPARENT 5 +#define CV_HAL_BORDER_ISOLATED 16 +//! @} + +//! @name DFT flags +//! @{ +#define CV_HAL_DFT_INVERSE 1 +#define CV_HAL_DFT_SCALE 2 +#define CV_HAL_DFT_ROWS 4 +#define CV_HAL_DFT_COMPLEX_OUTPUT 16 +#define CV_HAL_DFT_REAL_OUTPUT 32 +#define CV_HAL_DFT_TWO_STAGE 64 +#define CV_HAL_DFT_STAGE_COLS 128 +#define CV_HAL_DFT_IS_CONTINUOUS 512 +#define CV_HAL_DFT_IS_INPLACE 1024 +//! @} + +//! @name SVD flags +//! @{ +#define CV_HAL_SVD_NO_UV 1 +#define CV_HAL_SVD_SHORT_UV 2 +#define CV_HAL_SVD_MODIFY_A 4 +#define CV_HAL_SVD_FULL_UV 8 +//! @} + +//! @name Gemm flags +//! @{ +#define CV_HAL_GEMM_1_T 1 +#define CV_HAL_GEMM_2_T 2 +#define CV_HAL_GEMM_3_T 4 +//! @} + +//! @} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin.hpp new file mode 100644 index 0000000..52f6b5d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin.hpp @@ -0,0 +1,520 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_HPP +#define OPENCV_HAL_INTRIN_HPP + +#include +#include +#include +#include "opencv2/core/cvdef.h" + +#define OPENCV_HAL_ADD(a, b) ((a) + (b)) +#define OPENCV_HAL_AND(a, b) ((a) & (b)) +#define OPENCV_HAL_NOP(a) (a) +#define OPENCV_HAL_1ST(a, b) (a) + +namespace { +inline unsigned int trailingZeros32(unsigned int value) { +#if defined(_MSC_VER) +#if (_MSC_VER < 1700) || defined(_M_ARM) || defined(_M_ARM64) + unsigned long index = 0; + _BitScanForward(&index, value); + return (unsigned int)index; +#elif defined(__clang__) + // clang-cl doesn't export _tzcnt_u32 for non BMI systems + return value ? __builtin_ctz(value) : 32; +#else + return _tzcnt_u32(value); +#endif +#elif defined(__GNUC__) || defined(__GNUG__) + return __builtin_ctz(value); +#elif defined(__ICC) || defined(__INTEL_COMPILER) + return _bit_scan_forward(value); +#elif defined(__clang__) + return llvm.cttz.i32(value, true); +#else + static const int MultiplyDeBruijnBitPosition[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; + return MultiplyDeBruijnBitPosition[((uint32_t)((value & -value) * 0x077CB531U)) >> 27]; +#endif +} +} + +// unlike HAL API, which is in cv::hal, +// we put intrinsics into cv namespace to make its +// access from within opencv code more accessible +namespace cv { + +namespace hal { + +enum StoreMode +{ + STORE_UNALIGNED = 0, + STORE_ALIGNED = 1, + STORE_ALIGNED_NOCACHE = 2 +}; + +} + +// TODO FIXIT: Don't use "God" traits. Split on separate cases. +template struct V_TypeTraits +{ +}; + +#define CV_INTRIN_DEF_TYPE_TRAITS(type, int_type_, uint_type_, abs_type_, w_type_, q_type_, sum_type_, nlanes128_) \ + template<> struct V_TypeTraits \ + { \ + typedef type value_type; \ + typedef int_type_ int_type; \ + typedef abs_type_ abs_type; \ + typedef uint_type_ uint_type; \ + typedef w_type_ w_type; \ + typedef q_type_ q_type; \ + typedef sum_type_ sum_type; \ + enum { nlanes128 = nlanes128_ }; \ + \ + static inline int_type reinterpret_int(type x) \ + { \ + union { type l; int_type i; } v; \ + v.l = x; \ + return v.i; \ + } \ + \ + static inline type reinterpret_from_int(int_type x) \ + { \ + union { type l; int_type i; } v; \ + v.i = x; \ + return v.l; \ + } \ + } + +#define CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(type, int_type_, uint_type_, abs_type_, w_type_, sum_type_, nlanes128_) \ + template<> struct V_TypeTraits \ + { \ + typedef type value_type; \ + typedef int_type_ int_type; \ + typedef abs_type_ abs_type; \ + typedef uint_type_ uint_type; \ + typedef w_type_ w_type; \ + typedef sum_type_ sum_type; \ + enum { nlanes128 = nlanes128_ }; \ + \ + static inline int_type reinterpret_int(type x) \ + { \ + union { type l; int_type i; } v; \ + v.l = x; \ + return v.i; \ + } \ + \ + static inline type reinterpret_from_int(int_type x) \ + { \ + union { type l; int_type i; } v; \ + v.i = x; \ + return v.l; \ + } \ + } + +CV_INTRIN_DEF_TYPE_TRAITS(uchar, schar, uchar, uchar, ushort, unsigned, unsigned, 16); +CV_INTRIN_DEF_TYPE_TRAITS(schar, schar, uchar, uchar, short, int, int, 16); +CV_INTRIN_DEF_TYPE_TRAITS(ushort, short, ushort, ushort, unsigned, uint64, unsigned, 8); +CV_INTRIN_DEF_TYPE_TRAITS(short, short, ushort, ushort, int, int64, int, 8); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(unsigned, int, unsigned, unsigned, uint64, unsigned, 4); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int, int, unsigned, unsigned, int64, int, 4); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(float, int, unsigned, float, double, float, 4); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(uint64, int64, uint64, uint64, void, uint64, 2); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int64, int64, uint64, uint64, void, int64, 2); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(double, int64, uint64, double, void, double, 2); + +#ifndef CV_DOXYGEN + +#ifndef CV_CPU_OPTIMIZATION_HAL_NAMESPACE +#ifdef CV_FORCE_SIMD128_CPP + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE hal_EMULATOR_CPP + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_EMULATOR_CPP { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#elif defined(CV_CPU_DISPATCH_MODE) + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#else + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE hal_baseline + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_baseline { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#endif +#endif // CV_CPU_OPTIMIZATION_HAL_NAMESPACE + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; +#endif +} + +#ifdef CV_DOXYGEN +# undef CV_AVX2 +# undef CV_SSE2 +# undef CV_NEON +# undef CV_VSX +# undef CV_FP16 +# undef CV_MSA +#endif + +#if (CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD) && !defined(CV_FORCE_SIMD128_CPP) +#define CV__SIMD_FORWARD 128 +#include "opencv2/core/hal/intrin_forward.hpp" +#endif + +#if CV_SSE2 && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_sse_em.hpp" +#include "opencv2/core/hal/intrin_sse.hpp" + +#elif CV_NEON && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_neon.hpp" + +#elif CV_VSX && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_vsx.hpp" + +#elif CV_MSA && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_msa.hpp" + +#elif CV_WASM_SIMD && !defined(CV_FORCE_SIMD128_CPP) +#include "opencv2/core/hal/intrin_wasm.hpp" + +#else + +#include "opencv2/core/hal/intrin_cpp.hpp" + +#endif + +// AVX2 can be used together with SSE2, so +// we define those two sets of intrinsics at once. +// Most of the intrinsics do not conflict (the proper overloaded variant is +// resolved by the argument types, e.g. v_float32x4 ~ SSE2, v_float32x8 ~ AVX2), +// but some of AVX2 intrinsics get v256_ prefix instead of v_, e.g. v256_load() vs v_load(). +// Correspondingly, the wide intrinsics (which are mapped to the "widest" +// available instruction set) will get vx_ prefix +// (and will be mapped to v256_ counterparts) (e.g. vx_load() => v256_load()) +#if CV_AVX2 + +#define CV__SIMD_FORWARD 256 +#include "opencv2/core/hal/intrin_forward.hpp" +#include "opencv2/core/hal/intrin_avx.hpp" + +#endif + +// AVX512 can be used together with SSE2 and AVX2, so +// we define those sets of intrinsics at once. +// For some of AVX512 intrinsics get v512_ prefix instead of v_, e.g. v512_load() vs v_load(). +// Wide intrinsics will be mapped to v512_ counterparts in this case(e.g. vx_load() => v512_load()) +#if CV_AVX512_SKX + +#define CV__SIMD_FORWARD 512 +#include "opencv2/core/hal/intrin_forward.hpp" +#include "opencv2/core/hal/intrin_avx512.hpp" + +#endif + +//! @cond IGNORED + +namespace cv { + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +#endif + +#ifndef CV_SIMD128 +#define CV_SIMD128 0 +#endif + +#ifndef CV_SIMD128_CPP +#define CV_SIMD128_CPP 0 +#endif + +#ifndef CV_SIMD128_64F +#define CV_SIMD128_64F 0 +#endif + +#ifndef CV_SIMD256 +#define CV_SIMD256 0 +#endif + +#ifndef CV_SIMD256_64F +#define CV_SIMD256_64F 0 +#endif + +#ifndef CV_SIMD512 +#define CV_SIMD512 0 +#endif + +#ifndef CV_SIMD512_64F +#define CV_SIMD512_64F 0 +#endif + +#ifndef CV_SIMD128_FP16 +#define CV_SIMD128_FP16 0 +#endif + +#ifndef CV_SIMD256_FP16 +#define CV_SIMD256_FP16 0 +#endif + +#ifndef CV_SIMD512_FP16 +#define CV_SIMD512_FP16 0 +#endif + +//================================================================================================== + +#define CV_INTRIN_DEFINE_WIDE_INTRIN(typ, vtyp, short_typ, prefix, loadsfx) \ + inline vtyp vx_setall_##short_typ(typ v) { return prefix##_setall_##short_typ(v); } \ + inline vtyp vx_setzero_##short_typ() { return prefix##_setzero_##short_typ(); } \ + inline vtyp vx_##loadsfx(const typ* ptr) { return prefix##_##loadsfx(ptr); } \ + inline vtyp vx_##loadsfx##_aligned(const typ* ptr) { return prefix##_##loadsfx##_aligned(ptr); } \ + inline vtyp vx_##loadsfx##_low(const typ* ptr) { return prefix##_##loadsfx##_low(ptr); } \ + inline vtyp vx_##loadsfx##_halves(const typ* ptr0, const typ* ptr1) { return prefix##_##loadsfx##_halves(ptr0, ptr1); } \ + inline void vx_store(typ* ptr, const vtyp& v) { return v_store(ptr, v); } \ + inline void vx_store_aligned(typ* ptr, const vtyp& v) { return v_store_aligned(ptr, v); } \ + inline vtyp vx_lut(const typ* ptr, const int* idx) { return prefix##_lut(ptr, idx); } \ + inline vtyp vx_lut_pairs(const typ* ptr, const int* idx) { return prefix##_lut_pairs(ptr, idx); } + +#define CV_INTRIN_DEFINE_WIDE_LUT_QUAD(typ, vtyp, prefix) \ + inline vtyp vx_lut_quads(const typ* ptr, const int* idx) { return prefix##_lut_quads(ptr, idx); } + +#define CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(typ, wtyp, prefix) \ + inline wtyp vx_load_expand(const typ* ptr) { return prefix##_load_expand(ptr); } + +#define CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND_Q(typ, qtyp, prefix) \ + inline qtyp vx_load_expand_q(const typ* ptr) { return prefix##_load_expand_q(ptr); } + +#define CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(typ, vtyp, short_typ, wtyp, qtyp, prefix, loadsfx) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(typ, vtyp, short_typ, prefix, loadsfx) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(typ, vtyp, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(typ, wtyp, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND_Q(typ, qtyp, prefix) + +#define CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(uchar, v_uint8, u8, v_uint16, v_uint32, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_INTRIN_WITH_EXPAND(schar, v_int8, s8, v_int16, v_int32, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(ushort, v_uint16, u16, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(ushort, v_uint16, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(ushort, v_uint32, prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(short, v_int16, s16, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(short, v_int16, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(short, v_int32, prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(int, v_int32, s32, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(int, v_int32, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(int, v_int64, prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(unsigned, v_uint32, u32, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(unsigned, v_uint32, prefix) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(unsigned, v_uint64, prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(float, v_float32, f32, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LUT_QUAD(float, v_float32, prefix) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(int64, v_int64, s64, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_INTRIN(uint64, v_uint64, u64, prefix, load) \ + CV_INTRIN_DEFINE_WIDE_LOAD_EXPAND(float16_t, v_float32, prefix) + +template struct V_RegTraits +{ +}; + +#define CV_DEF_REG_TRAITS(prefix, _reg, lane_type, suffix, _u_reg, _w_reg, _q_reg, _int_reg, _round_reg) \ + template<> struct V_RegTraits<_reg> \ + { \ + typedef _reg reg; \ + typedef _u_reg u_reg; \ + typedef _w_reg w_reg; \ + typedef _q_reg q_reg; \ + typedef _int_reg int_reg; \ + typedef _round_reg round_reg; \ + } + +#if CV_SIMD128 || CV_SIMD128_CPP + CV_DEF_REG_TRAITS(v, v_uint8x16, uchar, u8, v_uint8x16, v_uint16x8, v_uint32x4, v_int8x16, void); + CV_DEF_REG_TRAITS(v, v_int8x16, schar, s8, v_uint8x16, v_int16x8, v_int32x4, v_int8x16, void); + CV_DEF_REG_TRAITS(v, v_uint16x8, ushort, u16, v_uint16x8, v_uint32x4, v_uint64x2, v_int16x8, void); + CV_DEF_REG_TRAITS(v, v_int16x8, short, s16, v_uint16x8, v_int32x4, v_int64x2, v_int16x8, void); + CV_DEF_REG_TRAITS(v, v_uint32x4, unsigned, u32, v_uint32x4, v_uint64x2, void, v_int32x4, void); + CV_DEF_REG_TRAITS(v, v_int32x4, int, s32, v_uint32x4, v_int64x2, void, v_int32x4, void); +#if CV_SIMD128_64F || CV_SIMD128_CPP + CV_DEF_REG_TRAITS(v, v_float32x4, float, f32, v_float32x4, v_float64x2, void, v_int32x4, v_int32x4); +#else + CV_DEF_REG_TRAITS(v, v_float32x4, float, f32, v_float32x4, void, void, v_int32x4, v_int32x4); +#endif + CV_DEF_REG_TRAITS(v, v_uint64x2, uint64, u64, v_uint64x2, void, void, v_int64x2, void); + CV_DEF_REG_TRAITS(v, v_int64x2, int64, s64, v_uint64x2, void, void, v_int64x2, void); +#if CV_SIMD128_64F + CV_DEF_REG_TRAITS(v, v_float64x2, double, f64, v_float64x2, void, void, v_int64x2, v_int32x4); +#endif +#endif + +#if CV_SIMD256 + CV_DEF_REG_TRAITS(v256, v_uint8x32, uchar, u8, v_uint8x32, v_uint16x16, v_uint32x8, v_int8x32, void); + CV_DEF_REG_TRAITS(v256, v_int8x32, schar, s8, v_uint8x32, v_int16x16, v_int32x8, v_int8x32, void); + CV_DEF_REG_TRAITS(v256, v_uint16x16, ushort, u16, v_uint16x16, v_uint32x8, v_uint64x4, v_int16x16, void); + CV_DEF_REG_TRAITS(v256, v_int16x16, short, s16, v_uint16x16, v_int32x8, v_int64x4, v_int16x16, void); + CV_DEF_REG_TRAITS(v256, v_uint32x8, unsigned, u32, v_uint32x8, v_uint64x4, void, v_int32x8, void); + CV_DEF_REG_TRAITS(v256, v_int32x8, int, s32, v_uint32x8, v_int64x4, void, v_int32x8, void); + CV_DEF_REG_TRAITS(v256, v_float32x8, float, f32, v_float32x8, v_float64x4, void, v_int32x8, v_int32x8); + CV_DEF_REG_TRAITS(v256, v_uint64x4, uint64, u64, v_uint64x4, void, void, v_int64x4, void); + CV_DEF_REG_TRAITS(v256, v_int64x4, int64, s64, v_uint64x4, void, void, v_int64x4, void); + CV_DEF_REG_TRAITS(v256, v_float64x4, double, f64, v_float64x4, void, void, v_int64x4, v_int32x8); +#endif + +#if CV_SIMD512 + CV_DEF_REG_TRAITS(v512, v_uint8x64, uchar, u8, v_uint8x64, v_uint16x32, v_uint32x16, v_int8x64, void); + CV_DEF_REG_TRAITS(v512, v_int8x64, schar, s8, v_uint8x64, v_int16x32, v_int32x16, v_int8x64, void); + CV_DEF_REG_TRAITS(v512, v_uint16x32, ushort, u16, v_uint16x32, v_uint32x16, v_uint64x8, v_int16x32, void); + CV_DEF_REG_TRAITS(v512, v_int16x32, short, s16, v_uint16x32, v_int32x16, v_int64x8, v_int16x32, void); + CV_DEF_REG_TRAITS(v512, v_uint32x16, unsigned, u32, v_uint32x16, v_uint64x8, void, v_int32x16, void); + CV_DEF_REG_TRAITS(v512, v_int32x16, int, s32, v_uint32x16, v_int64x8, void, v_int32x16, void); + CV_DEF_REG_TRAITS(v512, v_float32x16, float, f32, v_float32x16, v_float64x8, void, v_int32x16, v_int32x16); + CV_DEF_REG_TRAITS(v512, v_uint64x8, uint64, u64, v_uint64x8, void, void, v_int64x8, void); + CV_DEF_REG_TRAITS(v512, v_int64x8, int64, s64, v_uint64x8, void, void, v_int64x8, void); + CV_DEF_REG_TRAITS(v512, v_float64x8, double, f64, v_float64x8, void, void, v_int64x8, v_int32x16); +#endif + +#if CV_SIMD512 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 512) +#define CV__SIMD_NAMESPACE simd512 +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD 1 + #define CV_SIMD_64F CV_SIMD512_64F + #define CV_SIMD_FP16 CV_SIMD512_FP16 + #define CV_SIMD_WIDTH 64 + typedef v_uint8x64 v_uint8; + typedef v_int8x64 v_int8; + typedef v_uint16x32 v_uint16; + typedef v_int16x32 v_int16; + typedef v_uint32x16 v_uint32; + typedef v_int32x16 v_int32; + typedef v_uint64x8 v_uint64; + typedef v_int64x8 v_int64; + typedef v_float32x16 v_float32; + CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v512) +#if CV_SIMD512_64F + typedef v_float64x8 v_float64; + CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v512, load) +#endif + inline void vx_cleanup() { v512_cleanup(); } +} // namespace +using namespace CV__SIMD_NAMESPACE; +#elif CV_SIMD256 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 256) +#define CV__SIMD_NAMESPACE simd256 +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD 1 + #define CV_SIMD_64F CV_SIMD256_64F + #define CV_SIMD_FP16 CV_SIMD256_FP16 + #define CV_SIMD_WIDTH 32 + typedef v_uint8x32 v_uint8; + typedef v_int8x32 v_int8; + typedef v_uint16x16 v_uint16; + typedef v_int16x16 v_int16; + typedef v_uint32x8 v_uint32; + typedef v_int32x8 v_int32; + typedef v_uint64x4 v_uint64; + typedef v_int64x4 v_int64; + typedef v_float32x8 v_float32; + CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v256) + #if CV_SIMD256_64F + typedef v_float64x4 v_float64; + CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v256, load) + #endif + inline void vx_cleanup() { v256_cleanup(); } +} // namespace +using namespace CV__SIMD_NAMESPACE; +#elif (CV_SIMD128 || CV_SIMD128_CPP) && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 128) +#if defined CV_SIMD128_CPP +#define CV__SIMD_NAMESPACE simd128_cpp +#else +#define CV__SIMD_NAMESPACE simd128 +#endif +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD CV_SIMD128 + #define CV_SIMD_64F CV_SIMD128_64F + #define CV_SIMD_WIDTH 16 + typedef v_uint8x16 v_uint8; + typedef v_int8x16 v_int8; + typedef v_uint16x8 v_uint16; + typedef v_int16x8 v_int16; + typedef v_uint32x4 v_uint32; + typedef v_int32x4 v_int32; + typedef v_uint64x2 v_uint64; + typedef v_int64x2 v_int64; + typedef v_float32x4 v_float32; + CV_INTRIN_DEFINE_WIDE_INTRIN_ALL_TYPES(v) + #if CV_SIMD128_64F + typedef v_float64x2 v_float64; + CV_INTRIN_DEFINE_WIDE_INTRIN(double, v_float64, f64, v, load) + #endif + inline void vx_cleanup() { v_cleanup(); } +} // namespace +using namespace CV__SIMD_NAMESPACE; +#endif + +#ifndef CV_SIMD_64F +#define CV_SIMD_64F 0 +#endif + +#ifndef CV_SIMD_FP16 +#define CV_SIMD_FP16 0 //!< Defined to 1 on native support of operations with float16x8_t / float16x16_t (SIMD256) types +#endif + +#ifndef CV_SIMD +#define CV_SIMD 0 +#endif + +#include "simd_utils.impl.hpp" + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +#endif + +} // cv:: + +//! @endcond + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx.hpp new file mode 100644 index 0000000..ca315ae --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx.hpp @@ -0,0 +1,3125 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_AVX_HPP +#define OPENCV_HAL_INTRIN_AVX_HPP + +#define CV_SIMD256 1 +#define CV_SIMD256_64F 1 +#define CV_SIMD256_FP16 0 // no native operations with FP16 type. Only load/store from float32x8 are available (if CV_FP16 == 1) + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Utils //////////// + +inline __m256i _v256_combine(const __m128i& lo, const __m128i& hi) +{ return _mm256_inserti128_si256(_mm256_castsi128_si256(lo), hi, 1); } + +inline __m256 _v256_combine(const __m128& lo, const __m128& hi) +{ return _mm256_insertf128_ps(_mm256_castps128_ps256(lo), hi, 1); } + +inline __m256d _v256_combine(const __m128d& lo, const __m128d& hi) +{ return _mm256_insertf128_pd(_mm256_castpd128_pd256(lo), hi, 1); } + +inline int _v_cvtsi256_si32(const __m256i& a) +{ return _mm_cvtsi128_si32(_mm256_castsi256_si128(a)); } + +inline __m256i _v256_shuffle_odd_64(const __m256i& v) +{ return _mm256_permute4x64_epi64(v, _MM_SHUFFLE(3, 1, 2, 0)); } + +inline __m256d _v256_shuffle_odd_64(const __m256d& v) +{ return _mm256_permute4x64_pd(v, _MM_SHUFFLE(3, 1, 2, 0)); } + +template +inline __m256i _v256_permute2x128(const __m256i& a, const __m256i& b) +{ return _mm256_permute2x128_si256(a, b, imm); } + +template +inline __m256 _v256_permute2x128(const __m256& a, const __m256& b) +{ return _mm256_permute2f128_ps(a, b, imm); } + +template +inline __m256d _v256_permute2x128(const __m256d& a, const __m256d& b) +{ return _mm256_permute2f128_pd(a, b, imm); } + +template +inline _Tpvec v256_permute2x128(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_v256_permute2x128(a.val, b.val)); } + +template +inline __m256i _v256_permute4x64(const __m256i& a) +{ return _mm256_permute4x64_epi64(a, imm); } + +template +inline __m256d _v256_permute4x64(const __m256d& a) +{ return _mm256_permute4x64_pd(a, imm); } + +template +inline _Tpvec v256_permute4x64(const _Tpvec& a) +{ return _Tpvec(_v256_permute4x64(a.val)); } + +inline __m128i _v256_extract_high(const __m256i& v) +{ return _mm256_extracti128_si256(v, 1); } + +inline __m128 _v256_extract_high(const __m256& v) +{ return _mm256_extractf128_ps(v, 1); } + +inline __m128d _v256_extract_high(const __m256d& v) +{ return _mm256_extractf128_pd(v, 1); } + +inline __m128i _v256_extract_low(const __m256i& v) +{ return _mm256_castsi256_si128(v); } + +inline __m128 _v256_extract_low(const __m256& v) +{ return _mm256_castps256_ps128(v); } + +inline __m128d _v256_extract_low(const __m256d& v) +{ return _mm256_castpd256_pd128(v); } + +inline __m256i _v256_packs_epu32(const __m256i& a, const __m256i& b) +{ + const __m256i m = _mm256_set1_epi32(65535); + __m256i am = _mm256_min_epu32(a, m); + __m256i bm = _mm256_min_epu32(b, m); + return _mm256_packus_epi32(am, bm); +} + +template +inline int _v256_extract_epi8(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi8(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 4)); + return _mm_extract_epi8(b, i & 15); // SSE4.1 +#endif +} + +template +inline int _v256_extract_epi16(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi16(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 3)); + return _mm_extract_epi16(b, i & 7); // SSE2 +#endif +} + +template +inline int _v256_extract_epi32(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi32(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 2)); + return _mm_extract_epi32(b, i & 3); // SSE4.1 +#endif +} + +template +inline int64 _v256_extract_epi64(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi64(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 1)); + return _mm_extract_epi64(b, i & 1); // SSE4.1 +#endif +} + +///////// Types //////////// + +struct v_uint8x32 +{ + typedef uchar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_uint8x32(__m256i v) : val(v) {} + v_uint8x32(uchar v0, uchar v1, uchar v2, uchar v3, + uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, + uchar v12, uchar v13, uchar v14, uchar v15, + uchar v16, uchar v17, uchar v18, uchar v19, + uchar v20, uchar v21, uchar v22, uchar v23, + uchar v24, uchar v25, uchar v26, uchar v27, + uchar v28, uchar v29, uchar v30, uchar v31) + { + val = _mm256_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6 , (char)v7, (char)v8, (char)v9, + (char)v10, (char)v11, (char)v12, (char)v13, (char)v14, (char)v15, + (char)v16, (char)v17, (char)v18, (char)v19, (char)v20, (char)v21, + (char)v22, (char)v23, (char)v24, (char)v25, (char)v26, (char)v27, + (char)v28, (char)v29, (char)v30, (char)v31); + } + v_uint8x32() : val(_mm256_setzero_si256()) {} + uchar get0() const { return (uchar)_v_cvtsi256_si32(val); } +}; + +struct v_int8x32 +{ + typedef schar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_int8x32(__m256i v) : val(v) {} + v_int8x32(schar v0, schar v1, schar v2, schar v3, + schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, + schar v12, schar v13, schar v14, schar v15, + schar v16, schar v17, schar v18, schar v19, + schar v20, schar v21, schar v22, schar v23, + schar v24, schar v25, schar v26, schar v27, + schar v28, schar v29, schar v30, schar v31) + { + val = _mm256_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, + v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31); + } + v_int8x32() : val(_mm256_setzero_si256()) {} + schar get0() const { return (schar)_v_cvtsi256_si32(val); } +}; + +struct v_uint16x16 +{ + typedef ushort lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_uint16x16(__m256i v) : val(v) {} + v_uint16x16(ushort v0, ushort v1, ushort v2, ushort v3, + ushort v4, ushort v5, ushort v6, ushort v7, + ushort v8, ushort v9, ushort v10, ushort v11, + ushort v12, ushort v13, ushort v14, ushort v15) + { + val = _mm256_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7, (short)v8, (short)v9, + (short)v10, (short)v11, (short)v12, (short)v13, (short)v14, (short)v15); + } + v_uint16x16() : val(_mm256_setzero_si256()) {} + ushort get0() const { return (ushort)_v_cvtsi256_si32(val); } +}; + +struct v_int16x16 +{ + typedef short lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_int16x16(__m256i v) : val(v) {} + v_int16x16(short v0, short v1, short v2, short v3, + short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, + short v12, short v13, short v14, short v15) + { + val = _mm256_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15); + } + v_int16x16() : val(_mm256_setzero_si256()) {} + short get0() const { return (short)_v_cvtsi256_si32(val); } +}; + +struct v_uint32x8 +{ + typedef unsigned lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_uint32x8(__m256i v) : val(v) {} + v_uint32x8(unsigned v0, unsigned v1, unsigned v2, unsigned v3, + unsigned v4, unsigned v5, unsigned v6, unsigned v7) + { + val = _mm256_setr_epi32((unsigned)v0, (unsigned)v1, (unsigned)v2, + (unsigned)v3, (unsigned)v4, (unsigned)v5, (unsigned)v6, (unsigned)v7); + } + v_uint32x8() : val(_mm256_setzero_si256()) {} + unsigned get0() const { return (unsigned)_v_cvtsi256_si32(val); } +}; + +struct v_int32x8 +{ + typedef int lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_int32x8(__m256i v) : val(v) {} + v_int32x8(int v0, int v1, int v2, int v3, + int v4, int v5, int v6, int v7) + { + val = _mm256_setr_epi32(v0, v1, v2, v3, v4, v5, v6, v7); + } + v_int32x8() : val(_mm256_setzero_si256()) {} + int get0() const { return _v_cvtsi256_si32(val); } +}; + +struct v_float32x8 +{ + typedef float lane_type; + enum { nlanes = 8 }; + __m256 val; + + explicit v_float32x8(__m256 v) : val(v) {} + v_float32x8(float v0, float v1, float v2, float v3, + float v4, float v5, float v6, float v7) + { + val = _mm256_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7); + } + v_float32x8() : val(_mm256_setzero_ps()) {} + float get0() const { return _mm_cvtss_f32(_mm256_castps256_ps128(val)); } +}; + +struct v_uint64x4 +{ + typedef uint64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_uint64x4(__m256i v) : val(v) {} + v_uint64x4(uint64 v0, uint64 v1, uint64 v2, uint64 v3) + { val = _mm256_setr_epi64x((int64)v0, (int64)v1, (int64)v2, (int64)v3); } + v_uint64x4() : val(_mm256_setzero_si256()) {} + uint64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (uint64)_mm_cvtsi128_si64(_mm256_castsi256_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm256_castsi256_si128(val)); + int b = _mm_cvtsi128_si32(_mm256_castsi256_si128(_mm256_srli_epi64(val, 32))); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #endif + } +}; + +struct v_int64x4 +{ + typedef int64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_int64x4(__m256i v) : val(v) {} + v_int64x4(int64 v0, int64 v1, int64 v2, int64 v3) + { val = _mm256_setr_epi64x(v0, v1, v2, v3); } + v_int64x4() : val(_mm256_setzero_si256()) {} + + int64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (int64)_mm_cvtsi128_si64(_mm256_castsi256_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm256_castsi256_si128(val)); + int b = _mm_cvtsi128_si32(_mm256_castsi256_si128(_mm256_srli_epi64(val, 32))); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #endif + } +}; + +struct v_float64x4 +{ + typedef double lane_type; + enum { nlanes = 4 }; + __m256d val; + + explicit v_float64x4(__m256d v) : val(v) {} + v_float64x4(double v0, double v1, double v2, double v3) + { val = _mm256_setr_pd(v0, v1, v2, v3); } + v_float64x4() : val(_mm256_setzero_pd()) {} + double get0() const { return _mm_cvtsd_f64(_mm256_castpd256_pd128(val)); } +}; + +//////////////// Load and store operations /////////////// + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE(_Tpvec, _Tp) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(_mm256_loadu_si256((const __m256i*)ptr)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm256_load_si256((const __m256i*)ptr)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + __m128i v128 = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpvec(_mm256_castsi128_si256(v128)); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + __m128i vlo = _mm_loadu_si128((const __m128i*)ptr0); \ + __m128i vhi = _mm_loadu_si128((const __m128i*)ptr1); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm256_store_si256((__m256i*)ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm256_stream_si256((__m256i*)ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm256_storeu_si256((__m256i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm256_stream_si256((__m256i*)ptr, a.val); \ + else \ + _mm256_store_si256((__m256i*)ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_si128((__m128i*)ptr, _v256_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_si128((__m128i*)ptr, _v256_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint8x32, uchar) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int8x32, schar) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint16x16, ushort) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int16x16, short) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint32x8, unsigned) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int32x8, int) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint64x4, uint64) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int64x4, int64) + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(_Tpvec, _Tp, suffix, halfreg) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(_mm256_loadu_##suffix(ptr)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm256_load_##suffix(ptr)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + return _Tpvec(_mm256_cast##suffix##128_##suffix##256 \ + (_mm_loadu_##suffix(ptr))); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + halfreg vlo = _mm_loadu_##suffix(ptr0); \ + halfreg vhi = _mm_loadu_##suffix(ptr1); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm256_store_##suffix(ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm256_stream_##suffix(ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm256_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm256_stream_##suffix(ptr, a.val); \ + else \ + _mm256_store_##suffix(ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_##suffix(ptr, _v256_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_##suffix(ptr, _v256_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(v_float32x8, float, ps, __m128) +OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(v_float64x4, double, pd, __m128d) + +#define OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, _Tpvecf, suffix, cast) \ + inline _Tpvec v_reinterpret_as_##suffix(const _Tpvecf& a) \ + { return _Tpvec(cast(a.val)); } + +#define OPENCV_HAL_IMPL_AVX_INIT(_Tpvec, _Tp, suffix, ssuffix, ctype_s) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(_mm256_setzero_si256()); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm256_set1_##ssuffix((ctype_s)v)); } \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_float32x8, suffix, _mm256_castps_si256) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_float64x4, suffix, _mm256_castpd_si256) + +OPENCV_HAL_IMPL_AVX_INIT(v_uint8x32, uchar, u8, epi8, char) +OPENCV_HAL_IMPL_AVX_INIT(v_int8x32, schar, s8, epi8, char) +OPENCV_HAL_IMPL_AVX_INIT(v_uint16x16, ushort, u16, epi16, short) +OPENCV_HAL_IMPL_AVX_INIT(v_int16x16, short, s16, epi16, short) +OPENCV_HAL_IMPL_AVX_INIT(v_uint32x8, unsigned, u32, epi32, int) +OPENCV_HAL_IMPL_AVX_INIT(v_int32x8, int, s32, epi32, int) +OPENCV_HAL_IMPL_AVX_INIT(v_uint64x4, uint64, u64, epi64x, int64) +OPENCV_HAL_IMPL_AVX_INIT(v_int64x4, int64, s64, epi64x, int64) + +#define OPENCV_HAL_IMPL_AVX_INIT_FLT(_Tpvec, _Tp, suffix, zsuffix, cast) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(_mm256_setzero_##zsuffix()); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm256_set1_##zsuffix(v)); } \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint8x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int8x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint16x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int16x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint32x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int32x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint64x4, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int64x4, suffix, cast) + +OPENCV_HAL_IMPL_AVX_INIT_FLT(v_float32x8, float, f32, ps, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_INIT_FLT(v_float64x4, double, f64, pd, _mm256_castsi256_pd) + +inline v_float32x8 v_reinterpret_as_f32(const v_float32x8& a) +{ return a; } +inline v_float32x8 v_reinterpret_as_f32(const v_float64x4& a) +{ return v_float32x8(_mm256_castpd_ps(a.val)); } + +inline v_float64x4 v_reinterpret_as_f64(const v_float64x4& a) +{ return a; } +inline v_float64x4 v_reinterpret_as_f64(const v_float32x8& a) +{ return v_float64x4(_mm256_castps_pd(a.val)); } + +/* Recombine */ +/*#define OPENCV_HAL_IMPL_AVX_COMBINE(_Tpvec, perm) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(perm(a.val, b.val, 0x20)); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(perm(a.val, b.val, 0x31)); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { c = v_combine_low(a, b); d = v_combine_high(a, b); } + +#define OPENCV_HAL_IMPL_AVX_UNPACKS(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_COMBINE(_Tpvec, _mm256_permute2x128_si256) \ + inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, \ + _Tpvec& b0, _Tpvec& b1) \ + { \ + __m256i v0 = _v256_shuffle_odd_64(a0.val); \ + __m256i v1 = _v256_shuffle_odd_64(a1.val); \ + b0.val = _mm256_unpacklo_##suffix(v0, v1); \ + b1.val = _mm256_unpackhi_##suffix(v0, v1); \ + } + +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int64x4, epi64) +OPENCV_HAL_IMPL_AVX_COMBINE(v_float32x8, _mm256_permute2f128_ps) +OPENCV_HAL_IMPL_AVX_COMBINE(v_float64x4, _mm256_permute2f128_pd) + +inline void v_zip(const v_float32x8& a0, const v_float32x8& a1, v_float32x8& b0, v_float32x8& b1) +{ + __m256 v0 = _mm256_unpacklo_ps(a0.val, a1.val); + __m256 v1 = _mm256_unpackhi_ps(a0.val, a1.val); + v_recombine(v_float32x8(v0), v_float32x8(v1), b0, b1); +} + +inline void v_zip(const v_float64x4& a0, const v_float64x4& a1, v_float64x4& b0, v_float64x4& b1) +{ + __m256d v0 = _v_shuffle_odd_64(a0.val); + __m256d v1 = _v_shuffle_odd_64(a1.val); + b0.val = _mm256_unpacklo_pd(v0, v1); + b1.val = _mm256_unpackhi_pd(v0, v1); +}*/ + +//////////////// Variant Value reordering /////////////// + +// unpacks +#define OPENCV_HAL_IMPL_AVX_UNPACK(_Tpvec, suffix) \ + inline _Tpvec v256_unpacklo(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_unpacklo_##suffix(a.val, b.val)); } \ + inline _Tpvec v256_unpackhi(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_unpackhi_##suffix(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACK(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_UNPACK(v_float64x4, pd) + +// blend +#define OPENCV_HAL_IMPL_AVX_BLEND(_Tpvec, suffix) \ + template \ + inline _Tpvec v256_blend(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_blend_##suffix(a.val, b.val, m)); } + +OPENCV_HAL_IMPL_AVX_BLEND(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_BLEND(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_BLEND(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_BLEND(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_BLEND(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_BLEND(v_float64x4, pd) + +template +inline v_uint64x4 v256_blend(const v_uint64x4& a, const v_uint64x4& b) +{ + enum {M0 = m}; + enum {M1 = (M0 | (M0 << 2)) & 0x33}; + enum {M2 = (M1 | (M1 << 1)) & 0x55}; + enum {MM = M2 | (M2 << 1)}; + return v_uint64x4(_mm256_blend_epi32(a.val, b.val, MM)); +} +template +inline v_int64x4 v256_blend(const v_int64x4& a, const v_int64x4& b) +{ return v_int64x4(v256_blend(v_uint64x4(a.val), v_uint64x4(b.val)).val); } + +// shuffle +// todo: emulate 64bit +#define OPENCV_HAL_IMPL_AVX_SHUFFLE(_Tpvec, intrin) \ + template \ + inline _Tpvec v256_shuffle(const _Tpvec& a) \ + { return _Tpvec(_mm256_##intrin(a.val, m)); } + +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_uint32x8, shuffle_epi32) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_int32x8, shuffle_epi32) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_float32x8, permute_ps) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_float64x4, permute_pd) + +template +inline void v256_zip(const _Tpvec& a, const _Tpvec& b, _Tpvec& ab0, _Tpvec& ab1) +{ + ab0 = v256_unpacklo(a, b); + ab1 = v256_unpackhi(a, b); +} + +template +inline _Tpvec v256_combine_diagonal(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_mm256_blend_epi32(a.val, b.val, 0xf0)); } + +inline v_float32x8 v256_combine_diagonal(const v_float32x8& a, const v_float32x8& b) +{ return v256_blend<0xf0>(a, b); } + +inline v_float64x4 v256_combine_diagonal(const v_float64x4& a, const v_float64x4& b) +{ return v256_blend<0xc>(a, b); } + +template +inline _Tpvec v256_alignr_128(const _Tpvec& a, const _Tpvec& b) +{ return v256_permute2x128<0x21>(a, b); } + +template +inline _Tpvec v256_alignr_64(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_mm256_alignr_epi8(a.val, b.val, 8)); } +inline v_float64x4 v256_alignr_64(const v_float64x4& a, const v_float64x4& b) +{ return v_float64x4(_mm256_shuffle_pd(b.val, a.val, _MM_SHUFFLE(0, 0, 1, 1))); } +// todo: emulate float32 + +template +inline _Tpvec v256_swap_halves(const _Tpvec& a) +{ return v256_permute2x128<1>(a, a); } + +template +inline _Tpvec v256_reverse_64(const _Tpvec& a) +{ return v256_permute4x64<_MM_SHUFFLE(0, 1, 2, 3)>(a); } + +// ZIP +#define OPENCV_HAL_IMPL_AVX_ZIP(_Tpvec) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x20>(a, b); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x31>(a, b); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { \ + _Tpvec a1b0 = v256_alignr_128(a, b); \ + c = v256_combine_diagonal(a, a1b0); \ + d = v256_combine_diagonal(a1b0, b); \ + } \ + inline void v_zip(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& ab0, _Tpvec& ab1) \ + { \ + _Tpvec ab0ab2, ab1ab3; \ + v256_zip(a, b, ab0ab2, ab1ab3); \ + v_recombine(ab0ab2, ab1ab3, ab0, ab1); \ + } + +OPENCV_HAL_IMPL_AVX_ZIP(v_uint8x32) +OPENCV_HAL_IMPL_AVX_ZIP(v_int8x32) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint16x16) +OPENCV_HAL_IMPL_AVX_ZIP(v_int16x16) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_int32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint64x4) +OPENCV_HAL_IMPL_AVX_ZIP(v_int64x4) +OPENCV_HAL_IMPL_AVX_ZIP(v_float32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_float64x4) + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ + +/** Arithmetics **/ +#define OPENCV_HAL_IMPL_AVX_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint8x32, _mm256_adds_epu8) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint8x32, _mm256_subs_epu8) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int8x32, _mm256_adds_epi8) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int8x32, _mm256_subs_epi8) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint16x16, _mm256_adds_epu16) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint16x16, _mm256_subs_epu16) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int16x16, _mm256_adds_epi16) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int16x16, _mm256_subs_epi16) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint32x8, _mm256_add_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint32x8, _mm256_sub_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_uint32x8, _mm256_mullo_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int32x8, _mm256_add_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int32x8, _mm256_sub_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_int32x8, _mm256_mullo_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint64x4, _mm256_add_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint64x4, _mm256_sub_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int64x4, _mm256_add_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int64x4, _mm256_sub_epi64) + +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_float32x8, _mm256_add_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_float32x8, _mm256_sub_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_float32x8, _mm256_mul_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(/, v_float32x8, _mm256_div_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_float64x4, _mm256_add_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_float64x4, _mm256_sub_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_float64x4, _mm256_mul_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(/, v_float64x4, _mm256_div_pd) + +// saturating multiply 8-bit, 16-bit +inline v_uint8x32 operator * (const v_uint8x32& a, const v_uint8x32& b) +{ + v_uint16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_int8x32 operator * (const v_int8x32& a, const v_int8x32& b) +{ + v_int16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_uint16x16 operator * (const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i pl = _mm256_mullo_epi16(a.val, b.val); + __m256i ph = _mm256_mulhi_epu16(a.val, b.val); + __m256i p0 = _mm256_unpacklo_epi16(pl, ph); + __m256i p1 = _mm256_unpackhi_epi16(pl, ph); + return v_uint16x16(_v256_packs_epu32(p0, p1)); +} +inline v_int16x16 operator * (const v_int16x16& a, const v_int16x16& b) +{ + __m256i pl = _mm256_mullo_epi16(a.val, b.val); + __m256i ph = _mm256_mulhi_epi16(a.val, b.val); + __m256i p0 = _mm256_unpacklo_epi16(pl, ph); + __m256i p1 = _mm256_unpackhi_epi16(pl, ph); + return v_int16x16(_mm256_packs_epi32(p0, p1)); +} +inline v_uint8x32& operator *= (v_uint8x32& a, const v_uint8x32& b) +{ a = a * b; return a; } +inline v_int8x32& operator *= (v_int8x32& a, const v_int8x32& b) +{ a = a * b; return a; } +inline v_uint16x16& operator *= (v_uint16x16& a, const v_uint16x16& b) +{ a = a * b; return a; } +inline v_int16x16& operator *= (v_int16x16& a, const v_int16x16& b) +{ a = a * b; return a; } + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_AVX_BIN_FUNC(func, _Tpvec, intrin) \ + inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_uint8x32, _mm256_add_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_int8x32, _mm256_add_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_uint16x16, _mm256_add_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_int16x16, _mm256_add_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_uint8x32, _mm256_sub_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_int8x32, _mm256_sub_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_uint16x16, _mm256_sub_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_int16x16, _mm256_sub_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_mul_wrap, v_uint16x16, _mm256_mullo_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_mul_wrap, v_int16x16, _mm256_mullo_epi16) + +inline v_uint8x32 v_mul_wrap(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i ad = _mm256_srai_epi16(a.val, 8); + __m256i bd = _mm256_srai_epi16(b.val, 8); + __m256i p0 = _mm256_mullo_epi16(a.val, b.val); // even + __m256i p1 = _mm256_slli_epi16(_mm256_mullo_epi16(ad, bd), 8); // odd + + const __m256i b01 = _mm256_set1_epi32(0xFF00FF00); + return v_uint8x32(_mm256_blendv_epi8(p0, p1, b01)); +} +inline v_int8x32 v_mul_wrap(const v_int8x32& a, const v_int8x32& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +// Multiply and expand +inline void v_mul_expand(const v_uint8x32& a, const v_uint8x32& b, + v_uint16x16& c, v_uint16x16& d) +{ + v_uint16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x32& a, const v_int8x32& b, + v_int16x16& c, v_int16x16& d) +{ + v_int16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x16& a, const v_int16x16& b, + v_int32x8& c, v_int32x8& d) +{ + v_int16x16 vhi = v_int16x16(_mm256_mulhi_epi16(a.val, b.val)); + + v_int16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_s32(v0); + d = v_reinterpret_as_s32(v1); +} + +inline void v_mul_expand(const v_uint16x16& a, const v_uint16x16& b, + v_uint32x8& c, v_uint32x8& d) +{ + v_uint16x16 vhi = v_uint16x16(_mm256_mulhi_epu16(a.val, b.val)); + + v_uint16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_u32(v0); + d = v_reinterpret_as_u32(v1); +} + +inline void v_mul_expand(const v_uint32x8& a, const v_uint32x8& b, + v_uint64x4& c, v_uint64x4& d) +{ + __m256i v0 = _mm256_mul_epu32(a.val, b.val); + __m256i v1 = _mm256_mul_epu32(_mm256_srli_epi64(a.val, 32), _mm256_srli_epi64(b.val, 32)); + v_zip(v_uint64x4(v0), v_uint64x4(v1), c, d); +} + +inline v_int16x16 v_mul_hi(const v_int16x16& a, const v_int16x16& b) { return v_int16x16(_mm256_mulhi_epi16(a.val, b.val)); } +inline v_uint16x16 v_mul_hi(const v_uint16x16& a, const v_uint16x16& b) { return v_uint16x16(_mm256_mulhi_epu16(a.val, b.val)); } + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_AVX_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, srai) \ + inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm256_slli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm256_slli_##suffix(a.val, imm)); } \ + inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm256_srli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ + { return _Tpsvec(srai(a.val, imm)); } \ + template \ + inline _Tpuvec v_shl(const _Tpuvec& a) \ + { return _Tpuvec(_mm256_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shl(const _Tpsvec& a) \ + { return _Tpsvec(_mm256_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shr(const _Tpuvec& a) \ + { return _Tpuvec(_mm256_srli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shr(const _Tpsvec& a) \ + { return _Tpsvec(srai(a.val, imm)); } + +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint16x16, v_int16x16, epi16, _mm256_srai_epi16) +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint32x8, v_int32x8, epi32, _mm256_srai_epi32) + +inline __m256i _mm256_srai_epi64xx(const __m256i a, int imm) +{ + __m256i d = _mm256_set1_epi64x((int64)1 << 63); + __m256i r = _mm256_srli_epi64(_mm256_add_epi64(a, d), imm); + return _mm256_sub_epi64(r, _mm256_srli_epi64(d, imm)); +} +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint64x4, v_int64x4, epi64, _mm256_srai_epi64xx) + + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_AVX_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(&, _Tpvec, _mm256_and_##suffix) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(|, _Tpvec, _mm256_or_##suffix) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(^, _Tpvec, _mm256_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(_mm256_xor_##suffix(a.val, not_const)); } + +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint8x32, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int8x32, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint16x16, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int16x16, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint32x8, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int32x8, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint64x4, si256, _mm256_set1_epi64x(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int64x4, si256, _mm256_set1_epi64x(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_float32x8, ps, _mm256_castsi256_ps(_mm256_set1_epi32(-1))) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_float64x4, pd, _mm256_castsi256_pd(_mm256_set1_epi32(-1))) + +/** Select **/ +#define OPENCV_HAL_IMPL_AVX_SELECT(_Tpvec, suffix) \ + inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_blendv_##suffix(b.val, a.val, mask.val)); } + +OPENCV_HAL_IMPL_AVX_SELECT(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_uint16x16, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int16x16, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_uint32x8, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int32x8, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_SELECT(v_float64x4, pd) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpvec) \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } \ + inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ + { return b > a; } \ + inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a < b); } \ + inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ + { return b >= a; } + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_INT(_Tpuvec, _Tpsvec, suffix, sbit) \ + inline _Tpuvec operator == (const _Tpuvec& a, const _Tpuvec& b) \ + { return _Tpuvec(_mm256_cmpeq_##suffix(a.val, b.val)); } \ + inline _Tpuvec operator > (const _Tpuvec& a, const _Tpuvec& b) \ + { \ + __m256i smask = _mm256_set1_##suffix(sbit); \ + return _Tpuvec(_mm256_cmpgt_##suffix( \ + _mm256_xor_si256(a.val, smask), \ + _mm256_xor_si256(b.val, smask))); \ + } \ + inline _Tpsvec operator == (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(_mm256_cmpeq_##suffix(a.val, b.val)); } \ + inline _Tpsvec operator > (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(_mm256_cmpgt_##suffix(a.val, b.val)); } \ + OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpuvec) \ + OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpsvec) + +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint8x32, v_int8x32, epi8, (char)-128) +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint16x16, v_int16x16, epi16, (short)-32768) +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint32x8, v_int32x8, epi32, (int)0x80000000) + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(_Tpvec) \ + inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_cmpeq_epi64(a.val, b.val)); } \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } + +OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(v_uint64x4) +OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(v_int64x4) + +#define OPENCV_HAL_IMPL_AVX_CMP_FLT(bin_op, imm8, _Tpvec, suffix) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_cmp_##suffix(a.val, b.val, imm8)); } + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(==, _CMP_EQ_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(!=, _CMP_NEQ_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(<, _CMP_LT_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(>, _CMP_GT_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(<=, _CMP_LE_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(>=, _CMP_GE_OQ, _Tpvec, suffix) + +OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(v_float64x4, pd) + +inline v_float32x8 v_not_nan(const v_float32x8& a) +{ return v_float32x8(_mm256_cmp_ps(a.val, a.val, _CMP_ORD_Q)); } +inline v_float64x4 v_not_nan(const v_float64x4& a) +{ return v_float64x4(_mm256_cmp_pd(a.val, a.val, _CMP_ORD_Q)); } + +/** min/max **/ +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint8x32, _mm256_min_epu8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint8x32, _mm256_max_epu8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int8x32, _mm256_min_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int8x32, _mm256_max_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint16x16, _mm256_min_epu16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint16x16, _mm256_max_epu16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int16x16, _mm256_min_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int16x16, _mm256_max_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint32x8, _mm256_min_epu32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint32x8, _mm256_max_epu32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int32x8, _mm256_min_epi32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int32x8, _mm256_max_epi32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_float32x8, _mm256_min_ps) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_float32x8, _mm256_max_ps) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_float64x4, _mm256_min_pd) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_float64x4, _mm256_max_pd) + +/** Rotate **/ +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_R = (16 - imm) & 0xFF}; + enum {IMM_R2 = (32 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _mm256_permute2x128_si256(a.val, b.val, 0x03); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(a.val, swap, IMM_R)); + return v_uint8x32(_mm256_alignr_epi8(swap, b.val, IMM_R2)); // imm < 32 +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _mm256_permute2x128_si256(a.val, b.val, 0x21); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(swap, a.val, imm)); + return v_uint8x32(_mm256_alignr_epi8(b.val, swap, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + enum {IMM_R = (16 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i swapz = _mm256_permute2x128_si256(a.val, a.val, _MM_SHUFFLE(0, 0, 2, 0)); + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(a.val, swapz, IMM_R)); + return v_uint8x32(_mm256_slli_si256(swapz, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i swapz = _mm256_permute2x128_si256(a.val, a.val, _MM_SHUFFLE(2, 0, 0, 1)); + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(swapz, a.val, imm)); + return v_uint8x32(_mm256_srli_si256(swapz, IMM_L)); +} + +#define OPENCV_HAL_IMPL_AVX_ROTATE_CAST(intrin, _Tpvec, cast) \ + template \ + inline _Tpvec intrin(const _Tpvec& a, const _Tpvec& b) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a), \ + v_reinterpret_as_u8(b)); \ + return _Tpvec(cast(ret.val)); \ + } \ + template \ + inline _Tpvec intrin(const _Tpvec& a) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a)); \ + return _Tpvec(cast(ret.val)); \ + } + +#define OPENCV_HAL_IMPL_AVX_ROTATE(_Tpvec) \ + OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, _Tpvec, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, _Tpvec, OPENCV_HAL_NOP) + +OPENCV_HAL_IMPL_AVX_ROTATE(v_int8x32) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint16x16) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int16x16) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint32x8) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int32x8) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint64x4) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int64x4) + +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, v_float32x8, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, v_float32x8, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, v_float64x4, _mm256_castsi256_pd) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, v_float64x4, _mm256_castsi256_pd) + +/** Reverse **/ +inline v_uint8x32 v_reverse(const v_uint8x32 &a) +{ + static const __m256i perm = _mm256_setr_epi8( + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + __m256i vec = _mm256_shuffle_epi8(a.val, perm); + return v_uint8x32(_mm256_permute2x128_si256(vec, vec, 1)); +} + +inline v_int8x32 v_reverse(const v_int8x32 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x16 v_reverse(const v_uint16x16 &a) +{ + static const __m256i perm = _mm256_setr_epi8( + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + __m256i vec = _mm256_shuffle_epi8(a.val, perm); + return v_uint16x16(_mm256_permute2x128_si256(vec, vec, 1)); +} + +inline v_int16x16 v_reverse(const v_int16x16 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x8 v_reverse(const v_uint32x8 &a) +{ + static const __m256i perm = _mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0); + return v_uint32x8(_mm256_permutevar8x32_epi32(a.val, perm)); +} + +inline v_int32x8 v_reverse(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x8 v_reverse(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x4 v_reverse(const v_uint64x4 &a) +{ + return v_uint64x4(_mm256_permute4x64_epi64(a.val, _MM_SHUFFLE(0, 1, 2, 3))); +} + +inline v_int64x4 v_reverse(const v_int64x4 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x4 v_reverse(const v_float64x4 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +////////// Reduce and mask ///////// + +/** Reduce **/ +inline unsigned v_reduce_sum(const v_uint8x32& a) +{ + __m256i half = _mm256_sad_epu8(a.val, _mm256_setzero_si256()); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline int v_reduce_sum(const v_int8x32& a) +{ + __m256i half = _mm256_sad_epu8(_mm256_xor_si256(a.val, _mm256_set1_epi8((schar)-128)), _mm256_setzero_si256()); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))) - 4096; +} +#define OPENCV_HAL_IMPL_AVX_REDUCE_32(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i val = intrin(_v256_extract_low(a.val), _v256_extract_high(a.val)); \ + val = intrin(val, _mm_srli_si128(val,8)); \ + val = intrin(val, _mm_srli_si128(val,4)); \ + val = intrin(val, _mm_srli_si128(val,2)); \ + val = intrin(val, _mm_srli_si128(val,1)); \ + return (sctype)_mm_cvtsi128_si32(val); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_uint8x32, uchar, min, _mm_min_epu8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_int8x32, schar, min, _mm_min_epi8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_uint8x32, uchar, max, _mm_max_epu8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_int8x32, schar, max, _mm_max_epi8) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_16(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_srli_si128(v0, 8)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 4)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 2)); \ + return (sctype) _mm_cvtsi128_si32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_uint16x16, ushort, min, _mm_min_epu16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_int16x16, short, min, _mm_min_epi16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_uint16x16, ushort, max, _mm_max_epu16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_int16x16, short, max, _mm_max_epi16) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_8(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_srli_si128(v0, 8)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 4)); \ + return (sctype) _mm_cvtsi128_si32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_uint32x8, unsigned, min, _mm_min_epu32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_int32x8, int, min, _mm_min_epi32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_uint32x8, unsigned, max, _mm_max_epu32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_int32x8, int, max, _mm_max_epi32) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_FLT(func, intrin) \ + inline float v_reduce_##func(const v_float32x8& a) \ + { \ + __m128 v0 = _v256_extract_low(a.val); \ + __m128 v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_permute_ps(v0, _MM_SHUFFLE(0, 0, 3, 2))); \ + v0 = intrin(v0, _mm_permute_ps(v0, _MM_SHUFFLE(0, 0, 0, 1))); \ + return _mm_cvtss_f32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_FLT(min, _mm_min_ps) +OPENCV_HAL_IMPL_AVX_REDUCE_FLT(max, _mm_max_ps) + +inline int v_reduce_sum(const v_int32x8& a) +{ + __m256i s0 = _mm256_hadd_epi32(a.val, a.val); + s0 = _mm256_hadd_epi32(s0, s0); + + __m128i s1 = _v256_extract_high(s0); + s1 = _mm_add_epi32(_v256_extract_low(s0), s1); + + return _mm_cvtsi128_si32(s1); +} + +inline unsigned v_reduce_sum(const v_uint32x8& a) +{ return v_reduce_sum(v_reinterpret_as_s32(a)); } + +inline int v_reduce_sum(const v_int16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline unsigned v_reduce_sum(const v_uint16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +inline float v_reduce_sum(const v_float32x8& a) +{ + __m256 s0 = _mm256_hadd_ps(a.val, a.val); + s0 = _mm256_hadd_ps(s0, s0); + + __m128 s1 = _v256_extract_high(s0); + s1 = _mm_add_ps(_v256_extract_low(s0), s1); + + return _mm_cvtss_f32(s1); +} + +inline uint64 v_reduce_sum(const v_uint64x4& a) +{ + uint64 CV_DECL_ALIGNED(32) idx[2]; + _mm_store_si128((__m128i*)idx, _mm_add_epi64(_v256_extract_low(a.val), _v256_extract_high(a.val))); + return idx[0] + idx[1]; +} +inline int64 v_reduce_sum(const v_int64x4& a) +{ + int64 CV_DECL_ALIGNED(32) idx[2]; + _mm_store_si128((__m128i*)idx, _mm_add_epi64(_v256_extract_low(a.val), _v256_extract_high(a.val))); + return idx[0] + idx[1]; +} +inline double v_reduce_sum(const v_float64x4& a) +{ + __m256d s0 = _mm256_hadd_pd(a.val, a.val); + return _mm_cvtsd_f64(_mm_add_pd(_v256_extract_low(s0), _v256_extract_high(s0))); +} + +inline v_float32x8 v_reduce_sum4(const v_float32x8& a, const v_float32x8& b, + const v_float32x8& c, const v_float32x8& d) +{ + __m256 ab = _mm256_hadd_ps(a.val, b.val); + __m256 cd = _mm256_hadd_ps(c.val, d.val); + return v_float32x8(_mm256_hadd_ps(ab, cd)); +} + +inline unsigned v_reduce_sad(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i half = _mm256_sad_epu8(a.val, b.val); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_int8x32& a, const v_int8x32& b) +{ + __m256i half = _mm256_set1_epi8(0x7f); + half = _mm256_sad_epu8(_mm256_add_epi8(a.val, half), _mm256_add_epi8(b.val, half)); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_uint16x16& a, const v_uint16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_add_wrap(a - b, b - a), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x16& a, const v_int16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x8& a, const v_uint32x8& b) +{ + return v_reduce_sum(v_max(a, b) - v_min(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 m = a < b; + return v_reduce_sum(v_reinterpret_as_u32(((a - b) ^ m) - m)); +} +inline float v_reduce_sad(const v_float32x8& a, const v_float32x8& b) +{ + return v_reduce_sum((a - b) & v_float32x8(_mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff)))); +} + +/** Popcount **/ +inline v_uint8x32 v_popcount(const v_uint8x32& a) +{ + __m256i _popcnt_table = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); + __m256i _popcnt_mask = _mm256_set1_epi8(0x0F); + return v_uint8x32(_mm256_add_epi8(_mm256_shuffle_epi8(_popcnt_table, _mm256_and_si256( a.val , _popcnt_mask)), + _mm256_shuffle_epi8(_popcnt_table, _mm256_and_si256(_mm256_srli_epi16(a.val, 4), _popcnt_mask)))); +} +inline v_uint16x16 v_popcount(const v_uint16x16& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v256_setall_u16(0x00ff); +} +inline v_uint32x8 v_popcount(const v_uint32x8& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v256_setall_u32(0x000000ff); +} +inline v_uint64x4 v_popcount(const v_uint64x4& a) +{ + return v_uint64x4(_mm256_sad_epu8(v_popcount(v_reinterpret_as_u8(a)).val, _mm256_setzero_si256())); +} +inline v_uint8x32 v_popcount(const v_int8x32& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x16 v_popcount(const v_int16x16& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x8 v_popcount(const v_int32x8& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x4 v_popcount(const v_int64x4& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +/** Mask **/ +inline int v_signmask(const v_int8x32& a) +{ return _mm256_movemask_epi8(a.val); } +inline int v_signmask(const v_uint8x32& a) +{ return v_signmask(v_reinterpret_as_s8(a)); } + +inline int v_signmask(const v_int16x16& a) +{ return v_signmask(v_pack(a, a)) & 0xFFFF; } +inline int v_signmask(const v_uint16x16& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } + +inline int v_signmask(const v_float32x8& a) +{ return _mm256_movemask_ps(a.val); } +inline int v_signmask(const v_float64x4& a) +{ return _mm256_movemask_pd(a.val); } + +inline int v_signmask(const v_int32x8& a) +{ return v_signmask(v_reinterpret_as_f32(a)); } +inline int v_signmask(const v_uint32x8& a) +{ return v_signmask(v_reinterpret_as_f32(a)); } + +inline int v_signmask(const v_int64x4& a) +{ return v_signmask(v_reinterpret_as_f64(a)); } +inline int v_signmask(const v_uint64x4& a) +{ return v_signmask(v_reinterpret_as_f64(a)); } + +inline int v_scan_forward(const v_int8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +/** Checks **/ +#define OPENCV_HAL_IMPL_AVX_CHECK(_Tpvec, allmask) \ + inline bool v_check_all(const _Tpvec& a) { return v_signmask(a) == allmask; } \ + inline bool v_check_any(const _Tpvec& a) { return v_signmask(a) != 0; } +OPENCV_HAL_IMPL_AVX_CHECK(v_uint8x32, -1) +OPENCV_HAL_IMPL_AVX_CHECK(v_int8x32, -1) +OPENCV_HAL_IMPL_AVX_CHECK(v_uint32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_int32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_uint64x4, 15) +OPENCV_HAL_IMPL_AVX_CHECK(v_int64x4, 15) +OPENCV_HAL_IMPL_AVX_CHECK(v_float32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_float64x4, 15) + +#define OPENCV_HAL_IMPL_AVX_CHECK_SHORT(_Tpvec) \ + inline bool v_check_all(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) == 0xaaaaaaaa; } \ + inline bool v_check_any(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) != 0; } +OPENCV_HAL_IMPL_AVX_CHECK_SHORT(v_uint16x16) +OPENCV_HAL_IMPL_AVX_CHECK_SHORT(v_int16x16) + +////////// Other math ///////// + +/** Some frequent operations **/ +#define OPENCV_HAL_IMPL_AVX_MULADD(_Tpvec, suffix) \ + inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm256_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm256_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_sqrt(const _Tpvec& x) \ + { return _Tpvec(_mm256_sqrt_##suffix(x.val)); } \ + inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_fma(a, a, b * b); } \ + inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_sqrt(v_fma(a, a, b*b)); } + +OPENCV_HAL_IMPL_AVX_MULADD(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_MULADD(v_float64x4, pd) + +inline v_int32x8 v_fma(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return a * b + c; +} + +inline v_int32x8 v_muladd(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x8 v_invsqrt(const v_float32x8& x) +{ + v_float32x8 half = x * v256_setall_f32(0.5); + v_float32x8 t = v_float32x8(_mm256_rsqrt_ps(x.val)); + // todo: _mm256_fnmsub_ps + t *= v256_setall_f32(1.5) - ((t * t) * half); + return t; +} + +inline v_float64x4 v_invsqrt(const v_float64x4& x) +{ + return v256_setall_f64(1.) / v_sqrt(x); +} + +/** Absolute values **/ +#define OPENCV_HAL_IMPL_AVX_ABS(_Tpvec, suffix) \ + inline v_u##_Tpvec v_abs(const v_##_Tpvec& x) \ + { return v_u##_Tpvec(_mm256_abs_##suffix(x.val)); } + +OPENCV_HAL_IMPL_AVX_ABS(int8x32, epi8) +OPENCV_HAL_IMPL_AVX_ABS(int16x16, epi16) +OPENCV_HAL_IMPL_AVX_ABS(int32x8, epi32) + +inline v_float32x8 v_abs(const v_float32x8& x) +{ return x & v_float32x8(_mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff))); } +inline v_float64x4 v_abs(const v_float64x4& x) +{ return x & v_float64x4(_mm256_castsi256_pd(_mm256_srli_epi64(_mm256_set1_epi64x(-1), 1))); } + +/** Absolute difference **/ +inline v_uint8x32 v_absdiff(const v_uint8x32& a, const v_uint8x32& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x16 v_absdiff(const v_uint16x16& a, const v_uint16x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x8 v_absdiff(const v_uint32x8& a, const v_uint32x8& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x32 v_absdiff(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = v_sub_wrap(a, b); + v_int8x32 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} + +inline v_uint16x16 v_absdiff(const v_int16x16& a, const v_int16x16& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } + +inline v_uint32x8 v_absdiff(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 d = a - b; + v_int32x8 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +inline v_float32x8 v_absdiff(const v_float32x8& a, const v_float32x8& b) +{ return v_abs(a - b); } + +inline v_float64x4 v_absdiff(const v_float64x4& a, const v_float64x4& b) +{ return v_abs(a - b); } + +/** Saturating absolute difference **/ +inline v_int8x32 v_absdiffs(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = a - b; + v_int8x32 m = a < b; + return (d ^ m) - m; +} +inline v_int16x16 v_absdiffs(const v_int16x16& a, const v_int16x16& b) +{ return v_max(a, b) - v_min(a, b); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x8 v_round(const v_float32x8& a) +{ return v_int32x8(_mm256_cvtps_epi32(a.val)); } + +inline v_int32x8 v_round(const v_float64x4& a) +{ return v_int32x8(_mm256_castsi128_si256(_mm256_cvtpd_epi32(a.val))); } + +inline v_int32x8 v_round(const v_float64x4& a, const v_float64x4& b) +{ + __m128i ai = _mm256_cvtpd_epi32(a.val), bi = _mm256_cvtpd_epi32(b.val); + return v_int32x8(_v256_combine(ai, bi)); +} + +inline v_int32x8 v_trunc(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(a.val)); } + +inline v_int32x8 v_trunc(const v_float64x4& a) +{ return v_int32x8(_mm256_castsi128_si256(_mm256_cvttpd_epi32(a.val))); } + +inline v_int32x8 v_floor(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(_mm256_floor_ps(a.val))); } + +inline v_int32x8 v_floor(const v_float64x4& a) +{ return v_trunc(v_float64x4(_mm256_floor_pd(a.val))); } + +inline v_int32x8 v_ceil(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(_mm256_ceil_ps(a.val))); } + +inline v_int32x8 v_ceil(const v_float64x4& a) +{ return v_trunc(v_float64x4(_mm256_ceil_pd(a.val))); } + +/** To float **/ +inline v_float32x8 v_cvt_f32(const v_int32x8& a) +{ return v_float32x8(_mm256_cvtepi32_ps(a.val)); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a) +{ return v_float32x8(_mm256_castps128_ps256(_mm256_cvtpd_ps(a.val))); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a, const v_float64x4& b) +{ + __m128 af = _mm256_cvtpd_ps(a.val), bf = _mm256_cvtpd_ps(b.val); + return v_float32x8(_v256_combine(af, bf)); +} + +inline v_float64x4 v_cvt_f64(const v_int32x8& a) +{ return v_float64x4(_mm256_cvtepi32_pd(_v256_extract_low(a.val))); } + +inline v_float64x4 v_cvt_f64_high(const v_int32x8& a) +{ return v_float64x4(_mm256_cvtepi32_pd(_v256_extract_high(a.val))); } + +inline v_float64x4 v_cvt_f64(const v_float32x8& a) +{ return v_float64x4(_mm256_cvtps_pd(_v256_extract_low(a.val))); } + +inline v_float64x4 v_cvt_f64_high(const v_float32x8& a) +{ return v_float64x4(_mm256_cvtps_pd(_v256_extract_high(a.val))); } + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x4 v_cvt_f64(const v_int64x4& v) +{ + // constants encoded as floating-point + __m256i magic_i_lo = _mm256_set1_epi64x(0x4330000000000000); // 2^52 + __m256i magic_i_hi32 = _mm256_set1_epi64x(0x4530000080000000); // 2^84 + 2^63 + __m256i magic_i_all = _mm256_set1_epi64x(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m256d magic_d_all = _mm256_castsi256_pd(magic_i_all); + + // Blend the 32 lowest significant bits of v with magic_int_lo + __m256i v_lo = _mm256_blend_epi32(magic_i_lo, v.val, 0x55); + // Extract the 32 most significant bits of v + __m256i v_hi = _mm256_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm256_xor_si256(v_hi, magic_i_hi32); + // Compute in double precision + __m256d v_hi_dbl = _mm256_sub_pd(_mm256_castsi256_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m256d result = _mm256_add_pd(v_hi_dbl, _mm256_castsi256_pd(v_lo)); + return v_float64x4(result); +} + +////////////// Lookup table access //////////////////// + +inline v_int8x32 v256_lut(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_setr_epi8(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[ 8]], tab[idx[ 9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]], + tab[idx[16]], tab[idx[17]], tab[idx[18]], tab[idx[19]], tab[idx[20]], tab[idx[21]], tab[idx[22]], tab[idx[23]], + tab[idx[24]], tab[idx[25]], tab[idx[26]], tab[idx[27]], tab[idx[28]], tab[idx[29]], tab[idx[30]], tab[idx[31]])); +} +inline v_int8x32 v256_lut_pairs(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_setr_epi16(*(const short*)(tab + idx[ 0]), *(const short*)(tab + idx[ 1]), *(const short*)(tab + idx[ 2]), *(const short*)(tab + idx[ 3]), + *(const short*)(tab + idx[ 4]), *(const short*)(tab + idx[ 5]), *(const short*)(tab + idx[ 6]), *(const short*)(tab + idx[ 7]), + *(const short*)(tab + idx[ 8]), *(const short*)(tab + idx[ 9]), *(const short*)(tab + idx[10]), *(const short*)(tab + idx[11]), + *(const short*)(tab + idx[12]), *(const short*)(tab + idx[13]), *(const short*)(tab + idx[14]), *(const short*)(tab + idx[15]))); +} +inline v_int8x32 v256_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_i32gather_epi32((const int*)tab, _mm256_loadu_si256((const __m256i*)idx), 1)); +} +inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); } + +inline v_int16x16 v256_lut(const short* tab, const int* idx) +{ + return v_int16x16(_mm256_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])); +} +inline v_int16x16 v256_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x16(_mm256_i32gather_epi32((const int*)tab, _mm256_loadu_si256((const __m256i*)idx), 2)); +} +inline v_int16x16 v256_lut_quads(const short* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int16x16(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 2));//Looks like intrinsic has wrong definition +#else + return v_int16x16(_mm256_i32gather_epi64((const int64*)tab, _mm_loadu_si128((const __m128i*)idx), 2)); +#endif +} +inline v_uint16x16 v256_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_pairs((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_quads((const short *)tab, idx)); } + +inline v_int32x8 v256_lut(const int* tab, const int* idx) +{ + return v_int32x8(_mm256_i32gather_epi32(tab, _mm256_loadu_si256((const __m256i*)idx), 4)); +} +inline v_int32x8 v256_lut_pairs(const int* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int32x8(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 4)); +#else + return v_int32x8(_mm256_i32gather_epi64((const int64*)tab, _mm_loadu_si128((const __m128i*)idx), 4)); +#endif +} +inline v_int32x8 v256_lut_quads(const int* tab, const int* idx) +{ + return v_int32x8(_v256_combine(_mm_loadu_si128((const __m128i*)(tab + idx[0])), _mm_loadu_si128((const __m128i*)(tab + idx[1])))); +} +inline v_uint32x8 v256_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_pairs((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_quads((const int *)tab, idx)); } + +inline v_int64x4 v256_lut(const int64* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int64x4(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 8)); +#else + return v_int64x4(_mm256_i32gather_epi64(tab, _mm_loadu_si128((const __m128i*)idx), 8)); +#endif +} +inline v_int64x4 v256_lut_pairs(const int64* tab, const int* idx) +{ + return v_int64x4(_v256_combine(_mm_loadu_si128((const __m128i*)(tab + idx[0])), _mm_loadu_si128((const __m128i*)(tab + idx[1])))); +} +inline v_uint64x4 v256_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut((const int64 *)tab, idx)); } +inline v_uint64x4 v256_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut_pairs((const int64 *)tab, idx)); } + +inline v_float32x8 v256_lut(const float* tab, const int* idx) +{ + return v_float32x8(_mm256_i32gather_ps(tab, _mm256_loadu_si256((const __m256i*)idx), 4)); +} +inline v_float32x8 v256_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_pairs((const int *)tab, idx)); } +inline v_float32x8 v256_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_quads((const int *)tab, idx)); } + +inline v_float64x4 v256_lut(const double* tab, const int* idx) +{ + return v_float64x4(_mm256_i32gather_pd(tab, _mm_loadu_si128((const __m128i*)idx), 8)); +} +inline v_float64x4 v256_lut_pairs(const double* tab, const int* idx) { return v_float64x4(_v256_combine(_mm_loadu_pd(tab + idx[0]), _mm_loadu_pd(tab + idx[1]))); } + +inline v_int32x8 v_lut(const int* tab, const v_int32x8& idxvec) +{ + return v_int32x8(_mm256_i32gather_epi32(tab, idxvec.val, 4)); +} + +inline v_uint32x8 v_lut(const unsigned* tab, const v_int32x8& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x8 v_lut(const float* tab, const v_int32x8& idxvec) +{ + return v_float32x8(_mm256_i32gather_ps(tab, idxvec.val, 4)); +} + +inline v_float64x4 v_lut(const double* tab, const v_int32x8& idxvec) +{ + return v_float64x4(_mm256_i32gather_pd(tab, _mm256_castsi256_si128(idxvec.val), 8)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x8& idxvec, v_float32x8& x, v_float32x8& y) +{ + int CV_DECL_ALIGNED(32) idx[8]; + v_store_aligned(idx, idxvec); + __m128 z = _mm_setzero_ps(); + __m128 xy01, xy45, xy23, xy67; + xy01 = _mm_loadl_pi(z, (const __m64*)(tab + idx[0])); + xy01 = _mm_loadh_pi(xy01, (const __m64*)(tab + idx[1])); + xy45 = _mm_loadl_pi(z, (const __m64*)(tab + idx[4])); + xy45 = _mm_loadh_pi(xy45, (const __m64*)(tab + idx[5])); + __m256 xy0145 = _v256_combine(xy01, xy45); + xy23 = _mm_loadl_pi(z, (const __m64*)(tab + idx[2])); + xy23 = _mm_loadh_pi(xy23, (const __m64*)(tab + idx[3])); + xy67 = _mm_loadl_pi(z, (const __m64*)(tab + idx[6])); + xy67 = _mm_loadh_pi(xy67, (const __m64*)(tab + idx[7])); + __m256 xy2367 = _v256_combine(xy23, xy67); + + __m256 xxyy0145 = _mm256_unpacklo_ps(xy0145, xy2367); + __m256 xxyy2367 = _mm256_unpackhi_ps(xy0145, xy2367); + + x = v_float32x8(_mm256_unpacklo_ps(xxyy0145, xxyy2367)); + y = v_float32x8(_mm256_unpackhi_ps(xxyy0145, xxyy2367)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x8& idxvec, v_float64x4& x, v_float64x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_low(idx, idxvec); + __m128d xy0 = _mm_loadu_pd(tab + idx[0]); + __m128d xy2 = _mm_loadu_pd(tab + idx[2]); + __m128d xy1 = _mm_loadu_pd(tab + idx[1]); + __m128d xy3 = _mm_loadu_pd(tab + idx[3]); + __m256d xy02 = _v256_combine(xy0, xy2); + __m256d xy13 = _v256_combine(xy1, xy3); + + x = v_float64x4(_mm256_unpacklo_pd(xy02, xy13)); + y = v_float64x4(_mm256_unpackhi_pd(xy02, xy13)); +} + +inline v_int8x32 v_interleave_pairs(const v_int8x32& vec) +{ + return v_int8x32(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0d0e0c0b090a08, 0x0705060403010200, 0x0f0d0e0c0b090a08, 0x0705060403010200))); +} +inline v_uint8x32 v_interleave_pairs(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x32 v_interleave_quads(const v_int8x32& vec) +{ + return v_int8x32(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0b0e0a0d090c08, 0x0703060205010400, 0x0f0b0e0a0d090c08, 0x0703060205010400))); +} +inline v_uint8x32 v_interleave_quads(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_interleave_pairs(const v_int16x16& vec) +{ + return v_int16x16(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0e0b0a0d0c0908, 0x0706030205040100, 0x0f0e0b0a0d0c0908, 0x0706030205040100))); +} +inline v_uint16x16 v_interleave_pairs(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x16 v_interleave_quads(const v_int16x16& vec) +{ + return v_int16x16(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0e07060d0c0504, 0x0b0a030209080100, 0x0f0e07060d0c0504, 0x0b0a030209080100))); +} +inline v_uint16x16 v_interleave_quads(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_interleave_pairs(const v_int32x8& vec) +{ + return v_int32x8(_mm256_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0))); +} +inline v_uint32x8 v_interleave_pairs(const v_uint32x8& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_interleave_pairs(const v_float32x8& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x32 v_pack_triplets(const v_int8x32& vec) +{ + return v_int8x32(_mm256_permutevar8x32_epi32(_mm256_shuffle_epi8(vec.val, _mm256_broadcastsi128_si256(_mm_set_epi64x(0xffffff0f0e0d0c0a, 0x0908060504020100))), + _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint8x32 v_pack_triplets(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_pack_triplets(const v_int16x16& vec) +{ + return v_int16x16(_mm256_permutevar8x32_epi32(_mm256_shuffle_epi8(vec.val, _mm256_broadcastsi128_si256(_mm_set_epi64x(0xffff0f0e0d0c0b0a, 0x0908050403020100))), + _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint16x16 v_pack_triplets(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_pack_triplets(const v_int32x8& vec) +{ + return v_int32x8(_mm256_permutevar8x32_epi32(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint32x8 v_pack_triplets(const v_uint32x8& vec) { return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_pack_triplets(const v_float32x8& vec) +{ + return v_float32x8(_mm256_permutevar8x32_ps(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} + +////////// Matrix operations ///////// + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b) +{ return v_int32x8(_mm256_madd_epi16(a.val, b.val)); } +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b) +{ + __m256i even = _mm256_mul_epi32(a.val, b.val); + __m256i odd = _mm256_mul_epi32(_mm256_srli_epi64(a.val, 32), _mm256_srli_epi64(b.val, 32)); + return v_int64x4(_mm256_add_epi64(even, odd)); +} +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i even_m = _mm256_set1_epi32(0xFF00FF00); + __m256i even_a = _mm256_blendv_epi8(a.val, _mm256_setzero_si256(), even_m); + __m256i odd_a = _mm256_srli_epi16(a.val, 8); + + __m256i even_b = _mm256_blendv_epi8(b.val, _mm256_setzero_si256(), even_m); + __m256i odd_b = _mm256_srli_epi16(b.val, 8); + + __m256i prod0 = _mm256_madd_epi16(even_a, even_b); + __m256i prod1 = _mm256_madd_epi16(odd_a, odd_b); + return v_uint32x8(_mm256_add_epi32(prod0, prod1)); +} +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b) +{ + __m256i even_a = _mm256_srai_epi16(_mm256_bslli_epi128(a.val, 1), 8); + __m256i odd_a = _mm256_srai_epi16(a.val, 8); + + __m256i even_b = _mm256_srai_epi16(_mm256_bslli_epi128(b.val, 1), 8); + __m256i odd_b = _mm256_srai_epi16(b.val, 8); + + __m256i prod0 = _mm256_madd_epi16(even_a, even_b); + __m256i prod1 = _mm256_madd_epi16(odd_a, odd_b); + return v_int32x8(_mm256_add_epi32(prod0, prod1)); +} +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = _mm256_mullo_epi16(a.val, b.val); + __m256i mulhi = _mm256_mulhi_epu16(a.val, b.val); + __m256i mul0 = _mm256_unpacklo_epi16(mullo, mulhi); + __m256i mul1 = _mm256_unpackhi_epi16(mullo, mulhi); + + __m256i p02 = _mm256_blend_epi32(mul0, _mm256_setzero_si256(), 0xAA); + __m256i p13 = _mm256_srli_epi64(mul0, 32); + __m256i p46 = _mm256_blend_epi32(mul1, _mm256_setzero_si256(), 0xAA); + __m256i p57 = _mm256_srli_epi64(mul1, 32); + + __m256i p15_ = _mm256_add_epi64(p02, p13); + __m256i p9d_ = _mm256_add_epi64(p46, p57); + + return v_uint64x4(_mm256_add_epi64( + _mm256_unpacklo_epi64(p15_, p9d_), + _mm256_unpackhi_epi64(p15_, p9d_) + )); +} +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = _mm256_madd_epi16(a.val, b.val); + __m256i sign = _mm256_srai_epi32(prod, 31); + + __m256i lo = _mm256_unpacklo_epi32(prod, sign); + __m256i hi = _mm256_unpackhi_epi32(prod, sign); + + return v_int64x4(_mm256_add_epi64( + _mm256_unpacklo_epi64(lo, hi), + _mm256_unpackhi_epi64(lo, hi) + )); +} +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b) +{ return v_dotprod(a, b); } +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod(a, b); } +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = _mm256_mullo_epi16(a.val, b.val); + __m256i mulhi = _mm256_mulhi_epu16(a.val, b.val); + __m256i mul0 = _mm256_unpacklo_epi16(mullo, mulhi); + __m256i mul1 = _mm256_unpackhi_epi16(mullo, mulhi); + + __m256i p02 = _mm256_blend_epi32(mul0, _mm256_setzero_si256(), 0xAA); + __m256i p13 = _mm256_srli_epi64(mul0, 32); + __m256i p46 = _mm256_blend_epi32(mul1, _mm256_setzero_si256(), 0xAA); + __m256i p57 = _mm256_srli_epi64(mul1, 32); + + __m256i p15_ = _mm256_add_epi64(p02, p13); + __m256i p9d_ = _mm256_add_epi64(p46, p57); + + return v_uint64x4(_mm256_add_epi64(p15_, p9d_)); +} +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = _mm256_madd_epi16(a.val, b.val); + __m256i sign = _mm256_srai_epi32(prod, 31); + __m256i lo = _mm256_unpacklo_epi32(prod, sign); + __m256i hi = _mm256_unpackhi_epi32(prod, sign); + return v_int64x4(_mm256_add_epi64(lo, hi)); +} +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_AVX_SPLAT2_PS(a, im) \ + v_float32x8(_mm256_permute_ps(a.val, _MM_SHUFFLE(im, im, im, im))) + +inline v_float32x8 v_matmul(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& m3) +{ + v_float32x8 v04 = OPENCV_HAL_AVX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_AVX_SPLAT2_PS(v, 1); + v_float32x8 v26 = OPENCV_HAL_AVX_SPLAT2_PS(v, 2); + v_float32x8 v37 = OPENCV_HAL_AVX_SPLAT2_PS(v, 3); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, v37 * m3))); +} + +inline v_float32x8 v_matmuladd(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& a) +{ + v_float32x8 v04 = OPENCV_HAL_AVX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_AVX_SPLAT2_PS(v, 1); + v_float32x8 v26 = OPENCV_HAL_AVX_SPLAT2_PS(v, 2); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, a))); +} + +#define OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ + inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ + { \ + __m256i t0 = cast_from(_mm256_unpacklo_##suffix(a0.val, a1.val)); \ + __m256i t1 = cast_from(_mm256_unpacklo_##suffix(a2.val, a3.val)); \ + __m256i t2 = cast_from(_mm256_unpackhi_##suffix(a0.val, a1.val)); \ + __m256i t3 = cast_from(_mm256_unpackhi_##suffix(a2.val, a3.val)); \ + b0.val = cast_to(_mm256_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm256_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm256_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm256_unpackhi_epi64(t2, t3)); \ + } + +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_uint32x8, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_int32x8, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_float32x8, ps, _mm256_castps_si256, _mm256_castsi256_ps) + +//////////////// Value reordering /////////////// + +/* Expand */ +#define OPENCV_HAL_IMPL_AVX_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(_v256_extract_low(a.val)); \ + b1.val = intrin(_v256_extract_high(a.val)); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v256_extract_low(a.val))); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v256_extract_high(a.val))); } \ + inline _Tpwvec v256_load_expand(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint8x32, v_uint16x16, uchar, _mm256_cvtepu8_epi16) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int8x32, v_int16x16, schar, _mm256_cvtepi8_epi16) +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint16x16, v_uint32x8, ushort, _mm256_cvtepu16_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int16x16, v_int32x8, short, _mm256_cvtepi16_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint32x8, v_uint64x4, unsigned, _mm256_cvtepu32_epi64) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int32x8, v_int64x4, int, _mm256_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_AVX_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v256_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadl_epi64((const __m128i*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX_EXPAND_Q(v_uint32x8, uchar, _mm256_cvtepu8_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND_Q(v_int32x8, schar, _mm256_cvtepi8_epi32) + +/* pack */ +// 16 +inline v_int8x32 v_pack(const v_int16x16& a, const v_int16x16& b) +{ return v_int8x32(_v256_shuffle_odd_64(_mm256_packs_epi16(a.val, b.val))); } + +inline v_uint8x32 v_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i t = _mm256_set1_epi16(255); + __m256i a1 = _mm256_min_epu16(a.val, t); + __m256i b1 = _mm256_min_epu16(b.val, t); + return v_uint8x32(_v256_shuffle_odd_64(_mm256_packus_epi16(a1, b1))); +} + +inline v_uint8x32 v_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + return v_uint8x32(_v256_shuffle_odd_64(_mm256_packus_epi16(a.val, b.val))); +} + +inline void v_pack_store(schar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(uchar* ptr, const v_uint16x16& a) +{ + const __m256i m = _mm256_set1_epi16(255); + __m256i am = _mm256_min_epu16(a.val, m); + am = _v256_shuffle_odd_64(_mm256_packus_epi16(am, am)); + v_store_low(ptr, v_uint8x32(am)); +} + +inline void v_pack_u_store(uchar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + +template inline +v_uint8x32 v_rshr_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + return v_pack_u(v_reinterpret_as_s16((a + delta) >> n), + v_reinterpret_as_s16((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x16& a) +{ + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + v_pack_u_store(ptr, v_reinterpret_as_s16((a + delta) >> n)); +} + +template inline +v_uint8x32 v_rshr_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int8x32 v_rshr_pack(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_store(ptr, (a + delta) >> n); +} + +// 32 +inline v_int16x16 v_pack(const v_int32x8& a, const v_int32x8& b) +{ return v_int16x16(_v256_shuffle_odd_64(_mm256_packs_epi32(a.val, b.val))); } + +inline v_uint16x16 v_pack(const v_uint32x8& a, const v_uint32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_v256_packs_epu32(a.val, b.val))); } + +inline v_uint16x16 v_pack_u(const v_int32x8& a, const v_int32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_mm256_packus_epi32(a.val, b.val))); } + +inline void v_pack_store(short* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x8& a) +{ + const __m256i m = _mm256_set1_epi32(65535); + __m256i am = _mm256_min_epu32(a.val, m); + am = _v256_shuffle_odd_64(_mm256_packus_epi32(am, am)); + v_store_low(ptr, v_uint16x16(am)); +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + + +template inline +v_uint16x16 v_rshr_pack(const v_uint32x8& a, const v_uint32x8& b) +{ + // we assume that n > 0, and so the shifted 32-bit values can be treated as signed numbers. + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + return v_pack_u(v_reinterpret_as_s32((a + delta) >> n), + v_reinterpret_as_s32((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x8& a) +{ + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + v_pack_u_store(ptr, v_reinterpret_as_s32((a + delta) >> n)); +} + +template inline +v_uint16x16 v_rshr_pack_u(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int16x16 v_rshr_pack(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// 64 +// Non-saturating pack +inline v_uint32x8 v_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + __m256i a0 = _mm256_shuffle_epi32(a.val, _MM_SHUFFLE(0, 0, 2, 0)); + __m256i b0 = _mm256_shuffle_epi32(b.val, _MM_SHUFFLE(0, 0, 2, 0)); + __m256i ab = _mm256_unpacklo_epi64(a0, b0); // a0, a1, b0, b1, a2, a3, b2, b3 + return v_uint32x8(_v256_shuffle_odd_64(ab)); +} + +inline v_int32x8 v_pack(const v_int64x4& a, const v_int64x4& b) +{ return v_reinterpret_as_s32(v_pack(v_reinterpret_as_u64(a), v_reinterpret_as_u64(b))); } + +inline void v_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + __m256i a0 = _mm256_shuffle_epi32(a.val, _MM_SHUFFLE(0, 0, 2, 0)); + v_store_low(ptr, v_uint32x8(_v256_shuffle_odd_64(a0))); +} + +inline void v_pack_store(int* ptr, const v_int64x4& b) +{ v_pack_store((unsigned*)ptr, v_reinterpret_as_u64(b)); } + +template inline +v_uint32x8 v_rshr_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +template inline +v_int32x8 v_rshr_pack(const v_int64x4& a, const v_int64x4& b) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x4& a) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// pack boolean +inline v_uint8x32 v_pack_b(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i ab = _mm256_packs_epi16(a.val, b.val); + return v_uint8x32(_v256_shuffle_odd_64(ab)); +} + +inline v_uint8x32 v_pack_b(const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d) +{ + __m256i ab = _mm256_packs_epi32(a.val, b.val); + __m256i cd = _mm256_packs_epi32(c.val, d.val); + + __m256i abcd = _v256_shuffle_odd_64(_mm256_packs_epi16(ab, cd)); + return v_uint8x32(_mm256_shuffle_epi32(abcd, _MM_SHUFFLE(3, 1, 2, 0))); +} + +inline v_uint8x32 v_pack_b(const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + const v_uint64x4& d, const v_uint64x4& e, const v_uint64x4& f, + const v_uint64x4& g, const v_uint64x4& h) +{ + __m256i ab = _mm256_packs_epi32(a.val, b.val); + __m256i cd = _mm256_packs_epi32(c.val, d.val); + __m256i ef = _mm256_packs_epi32(e.val, f.val); + __m256i gh = _mm256_packs_epi32(g.val, h.val); + + __m256i abcd = _mm256_packs_epi32(ab, cd); + __m256i efgh = _mm256_packs_epi32(ef, gh); + __m256i pkall = _v256_shuffle_odd_64(_mm256_packs_epi16(abcd, efgh)); + + __m256i rev = _mm256_alignr_epi8(pkall, pkall, 8); + return v_uint8x32(_mm256_unpacklo_epi16(pkall, rev)); +} + +/* Recombine */ +// its up there with load and store operations + +/* Extract */ +#define OPENCV_HAL_IMPL_AVX_EXTRACT(_Tpvec) \ + template \ + inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ + { return v_rotate_right(a, b); } + +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint8x32) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int8x32) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint16x16) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int16x16) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint64x4) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int64x4) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_float32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_float64x4) + +template +inline uchar v_extract_n(v_uint8x32 a) +{ + return (uchar)_v256_extract_epi8(a.val); +} + +template +inline schar v_extract_n(v_int8x32 a) +{ + return (schar)v_extract_n(v_reinterpret_as_u8(a)); +} + +template +inline ushort v_extract_n(v_uint16x16 a) +{ + return (ushort)_v256_extract_epi16(a.val); +} + +template +inline short v_extract_n(v_int16x16 a) +{ + return (short)v_extract_n(v_reinterpret_as_u16(a)); +} + +template +inline uint v_extract_n(v_uint32x8 a) +{ + return (uint)_v256_extract_epi32(a.val); +} + +template +inline int v_extract_n(v_int32x8 a) +{ + return (int)v_extract_n(v_reinterpret_as_u32(a)); +} + +template +inline uint64 v_extract_n(v_uint64x4 a) +{ + return (uint64)_v256_extract_epi64(a.val); +} + +template +inline int64 v_extract_n(v_int64x4 v) +{ + return (int64)v_extract_n(v_reinterpret_as_u64(v)); +} + +template +inline float v_extract_n(v_float32x8 v) +{ + union { uint iv; float fv; } d; + d.iv = v_extract_n(v_reinterpret_as_u32(v)); + return d.fv; +} + +template +inline double v_extract_n(v_float64x4 v) +{ + union { uint64 iv; double dv; } d; + d.iv = v_extract_n(v_reinterpret_as_u64(v)); + return d.dv; +} + +template +inline v_uint32x8 v_broadcast_element(v_uint32x8 a) +{ + static const __m256i perm = _mm256_set1_epi32((char)i); + return v_uint32x8(_mm256_permutevar8x32_epi32(a.val, perm)); +} + +template +inline v_int32x8 v_broadcast_element(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_broadcast_element(v_reinterpret_as_u32(a))); } + +template +inline v_float32x8 v_broadcast_element(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_broadcast_element(v_reinterpret_as_u32(a))); } + + +///////////////////// load deinterleave ///////////////////////////// + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + + const __m256i sh = _mm256_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15, + 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15); + __m256i p0 = _mm256_shuffle_epi8(ab0, sh); + __m256i p1 = _mm256_shuffle_epi8(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint8x32(a0); + b = v_uint8x32(b0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + + const __m256i sh = _mm256_setr_epi8(0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, + 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15); + __m256i p0 = _mm256_shuffle_epi8(ab0, sh); + __m256i p1 = _mm256_shuffle_epi8(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint16x16(a0); + b = v_uint16x16(b0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + + const int sh = 0+2*4+1*16+3*64; + __m256i p0 = _mm256_shuffle_epi32(ab0, sh); + __m256i p1 = _mm256_shuffle_epi32(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint32x8(a0); + b = v_uint32x8(b0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + + __m256i pl = _mm256_permute2x128_si256(ab0, ab1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(ab0, ab1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint64x4(a0); + b = v_uint64x4(b0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 64)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1); + + __m256i b0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_high, s02_low, m1), bgr1, m0); + __m256i r0 = _mm256_blendv_epi8(_mm256_blendv_epi8(bgr1, s02_low, m0), s02_high, m1); + + const __m256i + sh_b = _mm256_setr_epi8(0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, + 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13), + sh_g = _mm256_setr_epi8(1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, + 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14), + sh_r = _mm256_setr_epi8(2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, + 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15); + b0 = _mm256_shuffle_epi8(b0, sh_b); + g0 = _mm256_shuffle_epi8(g0, sh_g); + r0 = _mm256_shuffle_epi8(r0, sh_r); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + __m256i b0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = _mm256_blendv_epi8(_mm256_blendv_epi8(bgr1, s02_low, m0), s02_high, m1); + __m256i r0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_high, s02_low, m1), bgr1, m0); + const __m256i sh_b = _mm256_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _mm256_setr_epi8(2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, + 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13); + const __m256i sh_r = _mm256_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + b0 = _mm256_shuffle_epi8(b0, sh_b); + g0 = _mm256_shuffle_epi8(g0, sh_g); + r0 = _mm256_shuffle_epi8(r0, sh_r); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + __m256i b0 = _mm256_blend_epi32(_mm256_blend_epi32(s02_low, s02_high, 0x24), bgr1, 0x92); + __m256i g0 = _mm256_blend_epi32(_mm256_blend_epi32(s02_high, s02_low, 0x92), bgr1, 0x24); + __m256i r0 = _mm256_blend_epi32(_mm256_blend_epi32(bgr1, s02_low, 0x24), s02_high, 0x92); + + b0 = _mm256_shuffle_epi32(b0, 0x6c); + g0 = _mm256_shuffle_epi32(g0, 0xb1); + r0 = _mm256_shuffle_epi32(r0, 0xc6); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + + __m256i s01 = _mm256_blend_epi32(bgr0, bgr1, 0xf0); + __m256i s12 = _mm256_blend_epi32(bgr1, bgr2, 0xf0); + __m256i s20r = _mm256_permute4x64_epi64(_mm256_blend_epi32(bgr2, bgr0, 0xf0), 0x1b); + __m256i b0 = _mm256_unpacklo_epi64(s01, s20r); + __m256i g0 = _mm256_alignr_epi8(s12, s01, 8); + __m256i r0 = _mm256_unpackhi_epi64(s20r, s12); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c, v_uint8x32& d ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 64)); + __m256i bgr3 = _mm256_loadu_si256((const __m256i*)(ptr + 96)); + const __m256i sh = _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, + 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); + + __m256i p0 = _mm256_shuffle_epi8(bgr0, sh); + __m256i p1 = _mm256_shuffle_epi8(bgr1, sh); + __m256i p2 = _mm256_shuffle_epi8(bgr2, sh); + __m256i p3 = _mm256_shuffle_epi8(bgr3, sh); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); + d = v_uint8x32(a0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c, v_uint16x16& d ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr3 = _mm256_loadu_si256((const __m256i*)(ptr + 48)); + const __m256i sh = _mm256_setr_epi8(0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15, + 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15); + __m256i p0 = _mm256_shuffle_epi8(bgr0, sh); + __m256i p1 = _mm256_shuffle_epi8(bgr1, sh); + __m256i p2 = _mm256_shuffle_epi8(bgr2, sh); + __m256i p3 = _mm256_shuffle_epi8(bgr3, sh); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); + d = v_uint16x16(a0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c, v_uint32x8& d ) +{ + __m256i p0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i p1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i p2 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i p3 = _mm256_loadu_si256((const __m256i*)(ptr + 24)); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); + d = v_uint32x8(a0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c, v_uint64x4& d ) +{ + __m256i bgra0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgra1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + __m256i bgra2 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i bgra3 = _mm256_loadu_si256((const __m256i*)(ptr + 12)); + + __m256i l02 = _mm256_permute2x128_si256(bgra0, bgra2, 0 + 2*16); + __m256i h02 = _mm256_permute2x128_si256(bgra0, bgra2, 1 + 3*16); + __m256i l13 = _mm256_permute2x128_si256(bgra1, bgra3, 0 + 2*16); + __m256i h13 = _mm256_permute2x128_si256(bgra1, bgra3, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi64(l02, l13); + __m256i g0 = _mm256_unpackhi_epi64(l02, l13); + __m256i r0 = _mm256_unpacklo_epi64(h02, h13); + __m256i a0 = _mm256_unpackhi_epi64(h02, h13); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); + d = v_uint64x4(a0); +} + +///////////////////////////// store interleave ///////////////////////////////////// + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& x, const v_uint8x32& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi8(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi8(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 32), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 32), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 32), xy1); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& x, const v_uint16x16& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi16(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi16(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 16), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 16), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 16), xy1); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& x, const v_uint32x8& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi32(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi32(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 8), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 8), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 8), xy1); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& x, const v_uint64x4& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi64(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi64(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 4), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 4), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 4), xy1); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, const v_uint8x32& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _mm256_setr_epi8( + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5, + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5); + const __m256i sh_g = _mm256_setr_epi8( + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10); + const __m256i sh_r = _mm256_setr_epi8( + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15); + + __m256i b0 = _mm256_shuffle_epi8(a.val, sh_b); + __m256i g0 = _mm256_shuffle_epi8(b.val, sh_g); + __m256i r0 = _mm256_shuffle_epi8(c.val, sh_r); + + const __m256i m0 = _mm256_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + + __m256i p0 = _mm256_blendv_epi8(_mm256_blendv_epi8(b0, g0, m0), r0, m1); + __m256i p1 = _mm256_blendv_epi8(_mm256_blendv_epi8(g0, r0, m0), b0, m1); + __m256i p2 = _mm256_blendv_epi8(_mm256_blendv_epi8(r0, b0, m0), g0, m1); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i bgr1 = _mm256_permute2x128_si256(p2, p0, 0 + 3*16); + __m256i bgr2 = _mm256_permute2x128_si256(p1, p2, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 32), bgr1); + _mm256_stream_si256((__m256i*)(ptr + 64), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 32), bgr1); + _mm256_store_si256((__m256i*)(ptr + 64), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgr1); + _mm256_storeu_si256((__m256i*)(ptr + 64), bgr2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, const v_uint16x16& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _mm256_setr_epi8( + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _mm256_setr_epi8( + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5); + const __m256i sh_r = _mm256_setr_epi8( + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + + __m256i b0 = _mm256_shuffle_epi8(a.val, sh_b); + __m256i g0 = _mm256_shuffle_epi8(b.val, sh_g); + __m256i r0 = _mm256_shuffle_epi8(c.val, sh_r); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + + __m256i p0 = _mm256_blendv_epi8(_mm256_blendv_epi8(b0, g0, m0), r0, m1); + __m256i p1 = _mm256_blendv_epi8(_mm256_blendv_epi8(g0, r0, m0), b0, m1); + __m256i p2 = _mm256_blendv_epi8(_mm256_blendv_epi8(r0, b0, m0), g0, m1); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p2, 0 + 2*16); + //__m256i bgr1 = p1; + __m256i bgr2 = _mm256_permute2x128_si256(p0, p2, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 16), p1); + _mm256_stream_si256((__m256i*)(ptr + 32), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 16), p1); + _mm256_store_si256((__m256i*)(ptr + 32), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 16), p1); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgr2); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, const v_uint32x8& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i b0 = _mm256_shuffle_epi32(a.val, 0x6c); + __m256i g0 = _mm256_shuffle_epi32(b.val, 0xb1); + __m256i r0 = _mm256_shuffle_epi32(c.val, 0xc6); + + __m256i p0 = _mm256_blend_epi32(_mm256_blend_epi32(b0, g0, 0x92), r0, 0x24); + __m256i p1 = _mm256_blend_epi32(_mm256_blend_epi32(g0, r0, 0x92), b0, 0x24); + __m256i p2 = _mm256_blend_epi32(_mm256_blend_epi32(r0, b0, 0x92), g0, 0x24); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + //__m256i bgr1 = p2; + __m256i bgr2 = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 8), p2); + _mm256_stream_si256((__m256i*)(ptr + 16), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 8), p2); + _mm256_store_si256((__m256i*)(ptr + 16), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 8), p2); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgr2); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i s01 = _mm256_unpacklo_epi64(a.val, b.val); + __m256i s12 = _mm256_unpackhi_epi64(b.val, c.val); + __m256i s20 = _mm256_blend_epi32(c.val, a.val, 0xcc); + + __m256i bgr0 = _mm256_permute2x128_si256(s01, s20, 0 + 2*16); + __m256i bgr1 = _mm256_blend_epi32(s01, s12, 0x0f); + __m256i bgr2 = _mm256_permute2x128_si256(s20, s12, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 4), bgr1); + _mm256_stream_si256((__m256i*)(ptr + 8), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 4), bgr1); + _mm256_store_si256((__m256i*)(ptr + 8), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 4), bgr1); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgr2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, + const v_uint8x32& c, const v_uint8x32& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi8(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi8(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi8(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi8(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi16(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi16(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi16(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi16(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 32), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 64), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 96), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 32), bgra1); + _mm256_store_si256((__m256i*)(ptr + 64), bgra2); + _mm256_store_si256((__m256i*)(ptr + 96), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 64), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 96), bgra3); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, + const v_uint16x16& c, const v_uint16x16& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi16(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi16(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi16(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi16(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi32(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi32(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi32(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi32(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 16), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 32), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 48), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 16), bgra1); + _mm256_store_si256((__m256i*)(ptr + 32), bgra2); + _mm256_store_si256((__m256i*)(ptr + 48), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 48), bgra3); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi32(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi32(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi32(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi32(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi64(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi64(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi64(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi64(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 8), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 16), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 24), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 8), bgra1); + _mm256_store_si256((__m256i*)(ptr + 16), bgra2); + _mm256_store_si256((__m256i*)(ptr + 24), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 24), bgra3); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, + const v_uint64x4& c, const v_uint64x4& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi64(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi64(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi64(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi64(c.val, d.val); + + __m256i bgra0 = _mm256_permute2x128_si256(bg0, ra0, 0 + 2*16); + __m256i bgra1 = _mm256_permute2x128_si256(bg1, ra1, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bg0, ra0, 1 + 3*16); + __m256i bgra3 = _mm256_permute2x128_si256(bg1, ra1, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 4), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 8), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 12), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 4), bgra1); + _mm256_store_si256((__m256i*)(ptr + 8), bgra2); + _mm256_store_si256((__m256i*)(ptr + 12), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 4), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 12), bgra3); + } +} + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, const _Tpvec0& c0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int8x32, schar, s8, v_uint8x32, uchar, u8) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int16x16, short, s16, v_uint16x16, ushort, u16) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int32x8, int, s32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_float32x8, float, f32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int64x4, int64, s64, v_uint64x4, uint64, u64) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_float64x4, double, f64, v_uint64x4, uint64, u64) + +// FP16 +inline v_float32x8 v256_load_expand(const float16_t* ptr) +{ + return v_float32x8(_mm256_cvtph_ps(_mm_loadu_si128((const __m128i*)ptr))); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x8& a) +{ + __m128i ah = _mm256_cvtps_ph(a.val, 0); + _mm_storeu_si128((__m128i*)ptr, ah); +} + +inline void v256_cleanup() { _mm256_zeroall(); } + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_AVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx512.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx512.hpp new file mode 100644 index 0000000..e189582 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_avx512.hpp @@ -0,0 +1,3049 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_AVX512_HPP +#define OPENCV_HAL_INTRIN_AVX512_HPP + +#if defined(_MSC_VER) && (_MSC_VER < 1920/*MSVS2019*/) +# pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned +# pragma warning(disable:4309) // 'argument': truncation of constant value +# pragma warning(disable:4310) // cast truncates constant value +#endif + +#define CVT_ROUND_MODES_IMPLEMENTED 0 + +#define CV_SIMD512 1 +#define CV_SIMD512_64F 1 +#define CV_SIMD512_FP16 0 // no native operations with FP16 type. Only load/store from float32x8 are available (if CV_FP16 == 1) + +#define _v512_set_epu64(a7, a6, a5, a4, a3, a2, a1, a0) _mm512_set_epi64((int64)(a7),(int64)(a6),(int64)(a5),(int64)(a4),(int64)(a3),(int64)(a2),(int64)(a1),(int64)(a0)) +#define _v512_set_epu32(a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _mm512_set_epi64(((int64)(a15)<<32)|(int64)(a14), ((int64)(a13)<<32)|(int64)(a12), ((int64)(a11)<<32)|(int64)(a10), ((int64)( a9)<<32)|(int64)( a8), \ + ((int64)( a7)<<32)|(int64)( a6), ((int64)( a5)<<32)|(int64)( a4), ((int64)( a3)<<32)|(int64)( a2), ((int64)( a1)<<32)|(int64)( a0)) +#define _v512_set_epu16(a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu32(((unsigned)(a31)<<16)|(unsigned)(a30), ((unsigned)(a29)<<16)|(unsigned)(a28), ((unsigned)(a27)<<16)|(unsigned)(a26), ((unsigned)(a25)<<16)|(unsigned)(a24), \ + ((unsigned)(a23)<<16)|(unsigned)(a22), ((unsigned)(a21)<<16)|(unsigned)(a20), ((unsigned)(a19)<<16)|(unsigned)(a18), ((unsigned)(a17)<<16)|(unsigned)(a16), \ + ((unsigned)(a15)<<16)|(unsigned)(a14), ((unsigned)(a13)<<16)|(unsigned)(a12), ((unsigned)(a11)<<16)|(unsigned)(a10), ((unsigned)( a9)<<16)|(unsigned)( a8), \ + ((unsigned)( a7)<<16)|(unsigned)( a6), ((unsigned)( a5)<<16)|(unsigned)( a4), ((unsigned)( a3)<<16)|(unsigned)( a2), ((unsigned)( a1)<<16)|(unsigned)( a0)) +#define _v512_set_epu8(a63, a62, a61, a60, a59, a58, a57, a56, a55, a54, a53, a52, a51, a50, a49, a48, \ + a47, a46, a45, a44, a43, a42, a41, a40, a39, a38, a37, a36, a35, a34, a33, a32, \ + a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu32(((unsigned)(a63)<<24)|((unsigned)(a62)<<16)|((unsigned)(a61)<<8)|(unsigned)(a60),((unsigned)(a59)<<24)|((unsigned)(a58)<<16)|((unsigned)(a57)<<8)|(unsigned)(a56), \ + ((unsigned)(a55)<<24)|((unsigned)(a54)<<16)|((unsigned)(a53)<<8)|(unsigned)(a52),((unsigned)(a51)<<24)|((unsigned)(a50)<<16)|((unsigned)(a49)<<8)|(unsigned)(a48), \ + ((unsigned)(a47)<<24)|((unsigned)(a46)<<16)|((unsigned)(a45)<<8)|(unsigned)(a44),((unsigned)(a43)<<24)|((unsigned)(a42)<<16)|((unsigned)(a41)<<8)|(unsigned)(a40), \ + ((unsigned)(a39)<<24)|((unsigned)(a38)<<16)|((unsigned)(a37)<<8)|(unsigned)(a36),((unsigned)(a35)<<24)|((unsigned)(a34)<<16)|((unsigned)(a33)<<8)|(unsigned)(a32), \ + ((unsigned)(a31)<<24)|((unsigned)(a30)<<16)|((unsigned)(a29)<<8)|(unsigned)(a28),((unsigned)(a27)<<24)|((unsigned)(a26)<<16)|((unsigned)(a25)<<8)|(unsigned)(a24), \ + ((unsigned)(a23)<<24)|((unsigned)(a22)<<16)|((unsigned)(a21)<<8)|(unsigned)(a20),((unsigned)(a19)<<24)|((unsigned)(a18)<<16)|((unsigned)(a17)<<8)|(unsigned)(a16), \ + ((unsigned)(a15)<<24)|((unsigned)(a14)<<16)|((unsigned)(a13)<<8)|(unsigned)(a12),((unsigned)(a11)<<24)|((unsigned)(a10)<<16)|((unsigned)( a9)<<8)|(unsigned)( a8), \ + ((unsigned)( a7)<<24)|((unsigned)( a6)<<16)|((unsigned)( a5)<<8)|(unsigned)( a4),((unsigned)( a3)<<24)|((unsigned)( a2)<<16)|((unsigned)( a1)<<8)|(unsigned)( a0)) +#define _v512_set_epi8(a63, a62, a61, a60, a59, a58, a57, a56, a55, a54, a53, a52, a51, a50, a49, a48, \ + a47, a46, a45, a44, a43, a42, a41, a40, a39, a38, a37, a36, a35, a34, a33, a32, \ + a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu8((uchar)(a63), (uchar)(a62), (uchar)(a61), (uchar)(a60), (uchar)(a59), (uchar)(a58), (uchar)(a57), (uchar)(a56), \ + (uchar)(a55), (uchar)(a54), (uchar)(a53), (uchar)(a52), (uchar)(a51), (uchar)(a50), (uchar)(a49), (uchar)(a48), \ + (uchar)(a47), (uchar)(a46), (uchar)(a45), (uchar)(a44), (uchar)(a43), (uchar)(a42), (uchar)(a41), (uchar)(a40), \ + (uchar)(a39), (uchar)(a38), (uchar)(a37), (uchar)(a36), (uchar)(a35), (uchar)(a34), (uchar)(a33), (uchar)(a32), \ + (uchar)(a31), (uchar)(a30), (uchar)(a29), (uchar)(a28), (uchar)(a27), (uchar)(a26), (uchar)(a25), (uchar)(a24), \ + (uchar)(a23), (uchar)(a22), (uchar)(a21), (uchar)(a20), (uchar)(a19), (uchar)(a18), (uchar)(a17), (uchar)(a16), \ + (uchar)(a15), (uchar)(a14), (uchar)(a13), (uchar)(a12), (uchar)(a11), (uchar)(a10), (uchar)( a9), (uchar)( a8), \ + (uchar)( a7), (uchar)( a6), (uchar)( a5), (uchar)( a4), (uchar)( a3), (uchar)( a2), (uchar)( a1), (uchar)( a0)) + +#ifndef _mm512_cvtpd_pslo +#ifdef _mm512_zextsi256_si512 +#define _mm512_cvtpd_pslo(a) _mm512_zextps256_ps512(_mm512_cvtpd_ps(a)) +#else +//if preferred way to extend with zeros is unavailable +#define _mm512_cvtpd_pslo(a) _mm512_castps256_ps512(_mm512_cvtpd_ps(a)) +#endif +#endif +///////// Utils //////////// + +namespace +{ + +inline __m512i _v512_combine(const __m256i& lo, const __m256i& hi) +{ return _mm512_inserti32x8(_mm512_castsi256_si512(lo), hi, 1); } + +inline __m512 _v512_combine(const __m256& lo, const __m256& hi) +{ return _mm512_insertf32x8(_mm512_castps256_ps512(lo), hi, 1); } + +inline __m512d _v512_combine(const __m256d& lo, const __m256d& hi) +{ return _mm512_insertf64x4(_mm512_castpd256_pd512(lo), hi, 1); } + +inline int _v_cvtsi512_si32(const __m512i& a) +{ return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); } + +inline __m256i _v512_extract_high(const __m512i& v) +{ return _mm512_extracti32x8_epi32(v, 1); } + +inline __m256 _v512_extract_high(const __m512& v) +{ return _mm512_extractf32x8_ps(v, 1); } + +inline __m256d _v512_extract_high(const __m512d& v) +{ return _mm512_extractf64x4_pd(v, 1); } + +inline __m256i _v512_extract_low(const __m512i& v) +{ return _mm512_castsi512_si256(v); } + +inline __m256 _v512_extract_low(const __m512& v) +{ return _mm512_castps512_ps256(v); } + +inline __m256d _v512_extract_low(const __m512d& v) +{ return _mm512_castpd512_pd256(v); } + +inline __m512i _v512_insert(const __m512i& a, const __m256i& b) +{ return _mm512_inserti32x8(a, b, 0); } + +inline __m512 _v512_insert(const __m512& a, const __m256& b) +{ return _mm512_insertf32x8(a, b, 0); } + +inline __m512d _v512_insert(const __m512d& a, const __m256d& b) +{ return _mm512_insertf64x4(a, b, 0); } + +} + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x64 +{ + typedef uchar lane_type; + enum { nlanes = 64 }; + __m512i val; + + explicit v_uint8x64(__m512i v) : val(v) {} + v_uint8x64(uchar v0, uchar v1, uchar v2, uchar v3, + uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, + uchar v12, uchar v13, uchar v14, uchar v15, + uchar v16, uchar v17, uchar v18, uchar v19, + uchar v20, uchar v21, uchar v22, uchar v23, + uchar v24, uchar v25, uchar v26, uchar v27, + uchar v28, uchar v29, uchar v30, uchar v31, + uchar v32, uchar v33, uchar v34, uchar v35, + uchar v36, uchar v37, uchar v38, uchar v39, + uchar v40, uchar v41, uchar v42, uchar v43, + uchar v44, uchar v45, uchar v46, uchar v47, + uchar v48, uchar v49, uchar v50, uchar v51, + uchar v52, uchar v53, uchar v54, uchar v55, + uchar v56, uchar v57, uchar v58, uchar v59, + uchar v60, uchar v61, uchar v62, uchar v63) + { + val = _v512_set_epu8(v63, v62, v61, v60, v59, v58, v57, v56, v55, v54, v53, v52, v51, v50, v49, v48, + v47, v46, v45, v44, v43, v42, v41, v40, v39, v38, v37, v36, v35, v34, v33, v32, + v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_uint8x64() : val(_mm512_setzero_si512()) {} + uchar get0() const { return (uchar)_v_cvtsi512_si32(val); } +}; + +struct v_int8x64 +{ + typedef schar lane_type; + enum { nlanes = 64 }; + __m512i val; + + explicit v_int8x64(__m512i v) : val(v) {} + v_int8x64(schar v0, schar v1, schar v2, schar v3, + schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, + schar v12, schar v13, schar v14, schar v15, + schar v16, schar v17, schar v18, schar v19, + schar v20, schar v21, schar v22, schar v23, + schar v24, schar v25, schar v26, schar v27, + schar v28, schar v29, schar v30, schar v31, + schar v32, schar v33, schar v34, schar v35, + schar v36, schar v37, schar v38, schar v39, + schar v40, schar v41, schar v42, schar v43, + schar v44, schar v45, schar v46, schar v47, + schar v48, schar v49, schar v50, schar v51, + schar v52, schar v53, schar v54, schar v55, + schar v56, schar v57, schar v58, schar v59, + schar v60, schar v61, schar v62, schar v63) + { + val = _v512_set_epi8(v63, v62, v61, v60, v59, v58, v57, v56, v55, v54, v53, v52, v51, v50, v49, v48, + v47, v46, v45, v44, v43, v42, v41, v40, v39, v38, v37, v36, v35, v34, v33, v32, + v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_int8x64() : val(_mm512_setzero_si512()) {} + schar get0() const { return (schar)_v_cvtsi512_si32(val); } +}; + +struct v_uint16x32 +{ + typedef ushort lane_type; + enum { nlanes = 32 }; + __m512i val; + + explicit v_uint16x32(__m512i v) : val(v) {} + v_uint16x32(ushort v0, ushort v1, ushort v2, ushort v3, + ushort v4, ushort v5, ushort v6, ushort v7, + ushort v8, ushort v9, ushort v10, ushort v11, + ushort v12, ushort v13, ushort v14, ushort v15, + ushort v16, ushort v17, ushort v18, ushort v19, + ushort v20, ushort v21, ushort v22, ushort v23, + ushort v24, ushort v25, ushort v26, ushort v27, + ushort v28, ushort v29, ushort v30, ushort v31) + { + val = _v512_set_epu16(v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_uint16x32() : val(_mm512_setzero_si512()) {} + ushort get0() const { return (ushort)_v_cvtsi512_si32(val); } +}; + +struct v_int16x32 +{ + typedef short lane_type; + enum { nlanes = 32 }; + __m512i val; + + explicit v_int16x32(__m512i v) : val(v) {} + v_int16x32(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, short v12, short v13, short v14, short v15, + short v16, short v17, short v18, short v19, short v20, short v21, short v22, short v23, + short v24, short v25, short v26, short v27, short v28, short v29, short v30, short v31) + { + val = _v512_set_epu16((ushort)v31, (ushort)v30, (ushort)v29, (ushort)v28, (ushort)v27, (ushort)v26, (ushort)v25, (ushort)v24, + (ushort)v23, (ushort)v22, (ushort)v21, (ushort)v20, (ushort)v19, (ushort)v18, (ushort)v17, (ushort)v16, + (ushort)v15, (ushort)v14, (ushort)v13, (ushort)v12, (ushort)v11, (ushort)v10, (ushort)v9 , (ushort)v8, + (ushort)v7 , (ushort)v6 , (ushort)v5 , (ushort)v4 , (ushort)v3 , (ushort)v2 , (ushort)v1 , (ushort)v0); + } + v_int16x32() : val(_mm512_setzero_si512()) {} + short get0() const { return (short)_v_cvtsi512_si32(val); } +}; + +struct v_uint32x16 +{ + typedef unsigned lane_type; + enum { nlanes = 16 }; + __m512i val; + + explicit v_uint32x16(__m512i v) : val(v) {} + v_uint32x16(unsigned v0, unsigned v1, unsigned v2, unsigned v3, + unsigned v4, unsigned v5, unsigned v6, unsigned v7, + unsigned v8, unsigned v9, unsigned v10, unsigned v11, + unsigned v12, unsigned v13, unsigned v14, unsigned v15) + { + val = _mm512_setr_epi32((int)v0, (int)v1, (int)v2, (int)v3, (int)v4, (int)v5, (int)v6, (int)v7, + (int)v8, (int)v9, (int)v10, (int)v11, (int)v12, (int)v13, (int)v14, (int)v15); + } + v_uint32x16() : val(_mm512_setzero_si512()) {} + unsigned get0() const { return (unsigned)_v_cvtsi512_si32(val); } +}; + +struct v_int32x16 +{ + typedef int lane_type; + enum { nlanes = 16 }; + __m512i val; + + explicit v_int32x16(__m512i v) : val(v) {} + v_int32x16(int v0, int v1, int v2, int v3, int v4, int v5, int v6, int v7, + int v8, int v9, int v10, int v11, int v12, int v13, int v14, int v15) + { + val = _mm512_setr_epi32(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15); + } + v_int32x16() : val(_mm512_setzero_si512()) {} + int get0() const { return _v_cvtsi512_si32(val); } +}; + +struct v_float32x16 +{ + typedef float lane_type; + enum { nlanes = 16 }; + __m512 val; + + explicit v_float32x16(__m512 v) : val(v) {} + v_float32x16(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, + float v8, float v9, float v10, float v11, float v12, float v13, float v14, float v15) + { + val = _mm512_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15); + } + v_float32x16() : val(_mm512_setzero_ps()) {} + float get0() const { return _mm_cvtss_f32(_mm512_castps512_ps128(val)); } +}; + +struct v_uint64x8 +{ + typedef uint64 lane_type; + enum { nlanes = 8 }; + __m512i val; + + explicit v_uint64x8(__m512i v) : val(v) {} + v_uint64x8(uint64 v0, uint64 v1, uint64 v2, uint64 v3, uint64 v4, uint64 v5, uint64 v6, uint64 v7) + { val = _mm512_setr_epi64((int64)v0, (int64)v1, (int64)v2, (int64)v3, (int64)v4, (int64)v5, (int64)v6, (int64)v7); } + v_uint64x8() : val(_mm512_setzero_si512()) {} + uint64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (uint64)_mm_cvtsi128_si64(_mm512_castsi512_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm512_castsi512_si128(val)); + int b = _mm_cvtsi128_si32(_mm512_castsi512_si128(_mm512_srli_epi64(val, 32))); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #endif + } +}; + +struct v_int64x8 +{ + typedef int64 lane_type; + enum { nlanes = 8 }; + __m512i val; + + explicit v_int64x8(__m512i v) : val(v) {} + v_int64x8(int64 v0, int64 v1, int64 v2, int64 v3, int64 v4, int64 v5, int64 v6, int64 v7) + { val = _mm512_setr_epi64(v0, v1, v2, v3, v4, v5, v6, v7); } + v_int64x8() : val(_mm512_setzero_si512()) {} + + int64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (int64)_mm_cvtsi128_si64(_mm512_castsi512_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm512_castsi512_si128(val)); + int b = _mm_cvtsi128_si32(_mm512_castsi512_si128(_mm512_srli_epi64(val, 32))); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #endif + } +}; + +struct v_float64x8 +{ + typedef double lane_type; + enum { nlanes = 8 }; + __m512d val; + + explicit v_float64x8(__m512d v) : val(v) {} + v_float64x8(double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7) + { val = _mm512_setr_pd(v0, v1, v2, v3, v4, v5, v6, v7); } + v_float64x8() : val(_mm512_setzero_pd()) {} + double get0() const { return _mm_cvtsd_f64(_mm512_castpd512_pd128(val)); } +}; + +//////////////// Load and store operations /////////////// + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE(_Tpvec, _Tp) \ + inline _Tpvec v512_load(const _Tp* ptr) \ + { return _Tpvec(_mm512_loadu_si512((const __m512i*)ptr)); } \ + inline _Tpvec v512_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm512_load_si512((const __m512i*)ptr)); } \ + inline _Tpvec v512_load_low(const _Tp* ptr) \ + { \ + __m256i v256 = _mm256_loadu_si256((const __m256i*)ptr); \ + return _Tpvec(_mm512_castsi256_si512(v256)); \ + } \ + inline _Tpvec v512_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + __m256i vlo = _mm256_loadu_si256((const __m256i*)ptr0); \ + __m256i vhi = _mm256_loadu_si256((const __m256i*)ptr1); \ + return _Tpvec(_v512_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm512_storeu_si512((__m512i*)ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm512_store_si512((__m512i*)ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm512_stream_si512((__m512i*)ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm512_storeu_si512((__m512i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm512_stream_si512((__m512i*)ptr, a.val); \ + else \ + _mm512_store_si512((__m512i*)ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, _v512_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, _v512_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint8x64, uchar) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int8x64, schar) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint16x32, ushort) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int16x32, short) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint32x16, unsigned) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int32x16, int) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint64x8, uint64) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int64x8, int64) + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(_Tpvec, _Tp, suffix, halfreg) \ + inline _Tpvec v512_load(const _Tp* ptr) \ + { return _Tpvec(_mm512_loadu_##suffix(ptr)); } \ + inline _Tpvec v512_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm512_load_##suffix(ptr)); } \ + inline _Tpvec v512_load_low(const _Tp* ptr) \ + { \ + return _Tpvec(_mm512_cast##suffix##256_##suffix##512 \ + (_mm256_loadu_##suffix(ptr))); \ + } \ + inline _Tpvec v512_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + halfreg vlo = _mm256_loadu_##suffix(ptr0); \ + halfreg vhi = _mm256_loadu_##suffix(ptr1); \ + return _Tpvec(_v512_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm512_storeu_##suffix(ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm512_store_##suffix(ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm512_stream_##suffix(ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm512_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm512_stream_##suffix(ptr, a.val); \ + else \ + _mm512_store_##suffix(ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, _v512_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, _v512_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(v_float32x16, float, ps, __m256) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(v_float64x8, double, pd, __m256d) + +#define OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, _Tpvecf, suffix, cast) \ + inline _Tpvec v_reinterpret_as_##suffix(const _Tpvecf& a) \ + { return _Tpvec(cast(a.val)); } + +#define OPENCV_HAL_IMPL_AVX512_INIT(_Tpvec, _Tp, suffix, ssuffix, ctype_s) \ + inline _Tpvec v512_setzero_##suffix() \ + { return _Tpvec(_mm512_setzero_si512()); } \ + inline _Tpvec v512_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm512_set1_##ssuffix((ctype_s)v)); } \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint8x64, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int8x64, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint16x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int16x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint32x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int32x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint64x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int64x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_float32x16, suffix, _mm512_castps_si512) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_float64x8, suffix, _mm512_castpd_si512) + +OPENCV_HAL_IMPL_AVX512_INIT(v_uint8x64, uchar, u8, epi8, char) +OPENCV_HAL_IMPL_AVX512_INIT(v_int8x64, schar, s8, epi8, char) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint16x32, ushort, u16, epi16, short) +OPENCV_HAL_IMPL_AVX512_INIT(v_int16x32, short, s16, epi16, short) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint32x16, unsigned, u32, epi32, int) +OPENCV_HAL_IMPL_AVX512_INIT(v_int32x16, int, s32, epi32, int) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint64x8, uint64, u64, epi64, int64) +OPENCV_HAL_IMPL_AVX512_INIT(v_int64x8, int64, s64, epi64, int64) + +#define OPENCV_HAL_IMPL_AVX512_INIT_FLT(_Tpvec, _Tp, suffix, zsuffix, cast) \ + inline _Tpvec v512_setzero_##suffix() \ + { return _Tpvec(_mm512_setzero_##zsuffix()); } \ + inline _Tpvec v512_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm512_set1_##zsuffix(v)); } \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint8x64, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int8x64, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint16x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int16x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint32x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int32x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint64x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int64x8, suffix, cast) + +OPENCV_HAL_IMPL_AVX512_INIT_FLT(v_float32x16, float, f32, ps, _mm512_castsi512_ps) +OPENCV_HAL_IMPL_AVX512_INIT_FLT(v_float64x8, double, f64, pd, _mm512_castsi512_pd) + +inline v_float32x16 v_reinterpret_as_f32(const v_float32x16& a) +{ return a; } +inline v_float32x16 v_reinterpret_as_f32(const v_float64x8& a) +{ return v_float32x16(_mm512_castpd_ps(a.val)); } + +inline v_float64x8 v_reinterpret_as_f64(const v_float64x8& a) +{ return a; } +inline v_float64x8 v_reinterpret_as_f64(const v_float32x16& a) +{ return v_float64x8(_mm512_castps_pd(a.val)); } + +// FP16 +inline v_float32x16 v512_load_expand(const float16_t* ptr) +{ + return v_float32x16(_mm512_cvtph_ps(_mm256_loadu_si256((const __m256i*)ptr))); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x16& a) +{ + __m256i ah = _mm512_cvtps_ph(a.val, 0); + _mm256_storeu_si256((__m256i*)ptr, ah); +} + +/* Recombine & ZIP */ +inline void v_zip(const v_int8x64& a, const v_int8x64& b, v_int8x64& ab0, v_int8x64& ab1) +{ +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8( 95, 31, 94, 30, 93, 29, 92, 28, 91, 27, 90, 26, 89, 25, 88, 24, + 87, 23, 86, 22, 85, 21, 84, 20, 83, 19, 82, 18, 81, 17, 80, 16, + 79, 15, 78, 14, 77, 13, 76, 12, 75, 11, 74, 10, 73, 9, 72, 8, + 71, 7, 70, 6, 69, 5, 68, 4, 67, 3, 66, 2, 65, 1, 64, 0); + ab0 = v_int8x64(_mm512_permutex2var_epi8(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu8(127, 63, 126, 62, 125, 61, 124, 60, 123, 59, 122, 58, 121, 57, 120, 56, + 119, 55, 118, 54, 117, 53, 116, 52, 115, 51, 114, 50, 113, 49, 112, 48, + 111, 47, 110, 46, 109, 45, 108, 44, 107, 43, 106, 42, 105, 41, 104, 40, + 103, 39, 102, 38, 101, 37, 100, 36, 99, 35, 98, 34, 97, 33, 96, 32); + ab1 = v_int8x64(_mm512_permutex2var_epi8(a.val, mask1, b.val)); +#else + __m512i low = _mm512_unpacklo_epi8(a.val, b.val); + __m512i high = _mm512_unpackhi_epi8(a.val, b.val); + ab0 = v_int8x64(_mm512_permutex2var_epi64(low, _v512_set_epu64(11, 10, 3, 2, 9, 8, 1, 0), high)); + ab1 = v_int8x64(_mm512_permutex2var_epi64(low, _v512_set_epu64(15, 14, 7, 6, 13, 12, 5, 4), high)); +#endif +} +inline void v_zip(const v_int16x32& a, const v_int16x32& b, v_int16x32& ab0, v_int16x32& ab1) +{ + __m512i mask0 = _v512_set_epu16(47, 15, 46, 14, 45, 13, 44, 12, 43, 11, 42, 10, 41, 9, 40, 8, + 39, 7, 38, 6, 37, 5, 36, 4, 35, 3, 34, 2, 33, 1, 32, 0); + ab0 = v_int16x32(_mm512_permutex2var_epi16(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu16(63, 31, 62, 30, 61, 29, 60, 28, 59, 27, 58, 26, 57, 25, 56, 24, + 55, 23, 54, 22, 53, 21, 52, 20, 51, 19, 50, 18, 49, 17, 48, 16); + ab1 = v_int16x32(_mm512_permutex2var_epi16(a.val, mask1, b.val)); +} +inline void v_zip(const v_int32x16& a, const v_int32x16& b, v_int32x16& ab0, v_int32x16& ab1) +{ + __m512i mask0 = _v512_set_epu32(23, 7, 22, 6, 21, 5, 20, 4, 19, 3, 18, 2, 17, 1, 16, 0); + ab0 = v_int32x16(_mm512_permutex2var_epi32(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu32(31, 15, 30, 14, 29, 13, 28, 12, 27, 11, 26, 10, 25, 9, 24, 8); + ab1 = v_int32x16(_mm512_permutex2var_epi32(a.val, mask1, b.val)); +} +inline void v_zip(const v_int64x8& a, const v_int64x8& b, v_int64x8& ab0, v_int64x8& ab1) +{ + __m512i mask0 = _v512_set_epu64(11, 3, 10, 2, 9, 1, 8, 0); + ab0 = v_int64x8(_mm512_permutex2var_epi64(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu64(15, 7, 14, 6, 13, 5, 12, 4); + ab1 = v_int64x8(_mm512_permutex2var_epi64(a.val, mask1, b.val)); +} + +inline void v_zip(const v_uint8x64& a, const v_uint8x64& b, v_uint8x64& ab0, v_uint8x64& ab1) +{ + v_int8x64 i0, i1; + v_zip(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b), i0, i1); + ab0 = v_reinterpret_as_u8(i0); + ab1 = v_reinterpret_as_u8(i1); +} +inline void v_zip(const v_uint16x32& a, const v_uint16x32& b, v_uint16x32& ab0, v_uint16x32& ab1) +{ + v_int16x32 i0, i1; + v_zip(v_reinterpret_as_s16(a), v_reinterpret_as_s16(b), i0, i1); + ab0 = v_reinterpret_as_u16(i0); + ab1 = v_reinterpret_as_u16(i1); +} +inline void v_zip(const v_uint32x16& a, const v_uint32x16& b, v_uint32x16& ab0, v_uint32x16& ab1) +{ + v_int32x16 i0, i1; + v_zip(v_reinterpret_as_s32(a), v_reinterpret_as_s32(b), i0, i1); + ab0 = v_reinterpret_as_u32(i0); + ab1 = v_reinterpret_as_u32(i1); +} +inline void v_zip(const v_uint64x8& a, const v_uint64x8& b, v_uint64x8& ab0, v_uint64x8& ab1) +{ + v_int64x8 i0, i1; + v_zip(v_reinterpret_as_s64(a), v_reinterpret_as_s64(b), i0, i1); + ab0 = v_reinterpret_as_u64(i0); + ab1 = v_reinterpret_as_u64(i1); +} +inline void v_zip(const v_float32x16& a, const v_float32x16& b, v_float32x16& ab0, v_float32x16& ab1) +{ + v_int32x16 i0, i1; + v_zip(v_reinterpret_as_s32(a), v_reinterpret_as_s32(b), i0, i1); + ab0 = v_reinterpret_as_f32(i0); + ab1 = v_reinterpret_as_f32(i1); +} +inline void v_zip(const v_float64x8& a, const v_float64x8& b, v_float64x8& ab0, v_float64x8& ab1) +{ + v_int64x8 i0, i1; + v_zip(v_reinterpret_as_s64(a), v_reinterpret_as_s64(b), i0, i1); + ab0 = v_reinterpret_as_f64(i0); + ab1 = v_reinterpret_as_f64(i1); +} + +#define OPENCV_HAL_IMPL_AVX512_COMBINE(_Tpvec, suffix) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_v512_combine(_v512_extract_low(a.val), _v512_extract_low(b.val))); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_v512_insert(b.val, _v512_extract_high(a.val))); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { \ + c.val = _v512_combine(_v512_extract_low(a.val),_v512_extract_low(b.val)); \ + d.val = _v512_insert(b.val,_v512_extract_high(a.val)); \ + } + + +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint8x64, epi8) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int8x64, epi8) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint16x32, epi16) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int16x32, epi16) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint64x8, epi64) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int64x8, epi64) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_float64x8, pd) + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_AVX512_BIN_FUNC(func, _Tpvec, intrin) \ + inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_uint8x64, _mm512_add_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_int8x64, _mm512_add_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_uint16x32, _mm512_add_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_int16x32, _mm512_add_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_uint8x64, _mm512_sub_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_int8x64, _mm512_sub_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_uint16x32, _mm512_sub_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_int16x32, _mm512_sub_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_mul_wrap, v_uint16x32, _mm512_mullo_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_mul_wrap, v_int16x32, _mm512_mullo_epi16) + +inline v_uint8x64 v_mul_wrap(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i ad = _mm512_srai_epi16(a.val, 8); + __m512i bd = _mm512_srai_epi16(b.val, 8); + __m512i p0 = _mm512_mullo_epi16(a.val, b.val); // even + __m512i p1 = _mm512_slli_epi16(_mm512_mullo_epi16(ad, bd), 8); // odd + return v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, p0, p1)); +} +inline v_int8x64 v_mul_wrap(const v_int8x64& a, const v_int8x64& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +#define OPENCV_HAL_IMPL_AVX512_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint32x16, _mm512_add_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint32x16, _mm512_sub_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int32x16, _mm512_add_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int32x16, _mm512_sub_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint64x8, _mm512_add_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint64x8, _mm512_sub_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int64x8, _mm512_add_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int64x8, _mm512_sub_epi64) + +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_uint32x16, _mm512_mullo_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_int32x16, _mm512_mullo_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_uint64x8, _mm512_mullo_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_int64x8, _mm512_mullo_epi64) + +/** Saturating arithmetics **/ +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint8x64, _mm512_adds_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint8x64, _mm512_subs_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int8x64, _mm512_adds_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int8x64, _mm512_subs_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint16x32, _mm512_adds_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint16x32, _mm512_subs_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int16x32, _mm512_adds_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int16x32, _mm512_subs_epi16) + +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_float32x16, _mm512_add_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_float32x16, _mm512_sub_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_float32x16, _mm512_mul_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(/, v_float32x16, _mm512_div_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_float64x8, _mm512_add_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_float64x8, _mm512_sub_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_float64x8, _mm512_mul_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(/, v_float64x8, _mm512_div_pd) + +// saturating multiply +inline v_uint8x64 operator * (const v_uint8x64& a, const v_uint8x64& b) +{ + v_uint16x32 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_int8x64 operator * (const v_int8x64& a, const v_int8x64& b) +{ + v_int16x32 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_uint16x32 operator * (const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i pl = _mm512_mullo_epi16(a.val, b.val); + __m512i ph = _mm512_mulhi_epu16(a.val, b.val); + __m512i p0 = _mm512_unpacklo_epi16(pl, ph); + __m512i p1 = _mm512_unpackhi_epi16(pl, ph); + + const __m512i m = _mm512_set1_epi32(65535); + return v_uint16x32(_mm512_packus_epi32(_mm512_min_epu32(p0, m), _mm512_min_epu32(p1, m))); +} +inline v_int16x32 operator * (const v_int16x32& a, const v_int16x32& b) +{ + __m512i pl = _mm512_mullo_epi16(a.val, b.val); + __m512i ph = _mm512_mulhi_epi16(a.val, b.val); + __m512i p0 = _mm512_unpacklo_epi16(pl, ph); + __m512i p1 = _mm512_unpackhi_epi16(pl, ph); + return v_int16x32(_mm512_packs_epi32(p0, p1)); +} + +inline v_uint8x64& operator *= (v_uint8x64& a, const v_uint8x64& b) +{ a = a * b; return a; } +inline v_int8x64& operator *= (v_int8x64& a, const v_int8x64& b) +{ a = a * b; return a; } +inline v_uint16x32& operator *= (v_uint16x32& a, const v_uint16x32& b) +{ a = a * b; return a; } +inline v_int16x32& operator *= (v_int16x32& a, const v_int16x32& b) +{ a = a * b; return a; } + +inline v_int16x32 v_mul_hi(const v_int16x32& a, const v_int16x32& b) { return v_int16x32(_mm512_mulhi_epi16(a.val, b.val)); } +inline v_uint16x32 v_mul_hi(const v_uint16x32& a, const v_uint16x32& b) { return v_uint16x32(_mm512_mulhi_epu16(a.val, b.val)); } + +// Multiply and expand +inline void v_mul_expand(const v_uint8x64& a, const v_uint8x64& b, + v_uint16x32& c, v_uint16x32& d) +{ + v_uint16x32 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x64& a, const v_int8x64& b, + v_int16x32& c, v_int16x32& d) +{ + v_int16x32 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x32& a, const v_int16x32& b, + v_int32x16& c, v_int32x16& d) +{ + v_int16x32 v0, v1; + v_zip(v_mul_wrap(a, b), v_mul_hi(a, b), v0, v1); + + c = v_reinterpret_as_s32(v0); + d = v_reinterpret_as_s32(v1); +} + +inline void v_mul_expand(const v_uint16x32& a, const v_uint16x32& b, + v_uint32x16& c, v_uint32x16& d) +{ + v_uint16x32 v0, v1; + v_zip(v_mul_wrap(a, b), v_mul_hi(a, b), v0, v1); + + c = v_reinterpret_as_u32(v0); + d = v_reinterpret_as_u32(v1); +} + +inline void v_mul_expand(const v_uint32x16& a, const v_uint32x16& b, + v_uint64x8& c, v_uint64x8& d) +{ + v_zip(v_uint64x8(_mm512_mul_epu32(a.val, b.val)), + v_uint64x8(_mm512_mul_epu32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32))), c, d); +} + +inline void v_mul_expand(const v_int32x16& a, const v_int32x16& b, + v_int64x8& c, v_int64x8& d) +{ + v_zip(v_int64x8(_mm512_mul_epi32(a.val, b.val)), + v_int64x8(_mm512_mul_epi32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32))), c, d); +} + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_AVX512_SHIFT_OP(_Tpuvec, _Tpsvec, suffix) \ + inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm512_slli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm512_slli_##suffix(a.val, imm)); } \ + inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm512_srli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm512_srai_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shl(const _Tpuvec& a) \ + { return _Tpuvec(_mm512_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shl(const _Tpsvec& a) \ + { return _Tpsvec(_mm512_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shr(const _Tpuvec& a) \ + { return _Tpuvec(_mm512_srli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shr(const _Tpsvec& a) \ + { return _Tpsvec(_mm512_srai_##suffix(a.val, imm)); } + +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint16x32, v_int16x32, epi16) +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint32x16, v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint64x8, v_int64x8, epi64) + + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_AVX512_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(&, _Tpvec, _mm512_and_##suffix) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(|, _Tpvec, _mm512_or_##suffix) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(^, _Tpvec, _mm512_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(_mm512_xor_##suffix(a.val, not_const)); } + +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint8x64, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int8x64, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint16x32, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int16x32, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint32x16, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int32x16, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint64x8, si512, _mm512_set1_epi64(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int64x8, si512, _mm512_set1_epi64(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_float32x16, ps, _mm512_castsi512_ps(_mm512_set1_epi32(-1))) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_float64x8, pd, _mm512_castsi512_pd(_mm512_set1_epi32(-1))) + +/** Select **/ +#define OPENCV_HAL_IMPL_AVX512_SELECT(_Tpvec, suffix, zsuf) \ + inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_mask_blend_##suffix(_mm512_cmp_##suffix##_mask(mask.val, _mm512_setzero_##zsuf(), _MM_CMPINT_EQ), a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint8x64, epi8, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int8x64, epi8, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint16x32, epi16, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int16x32, epi16, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint32x16, epi32, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int32x16, epi32, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint64x8, epi64, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int64x8, epi64, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_float32x16, ps, ps) +OPENCV_HAL_IMPL_AVX512_SELECT(v_float64x8, pd, pd) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_AVX512_CMP_INT(bin_op, imm8, _Tpvec, sufcmp, sufset, tval) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_maskz_set1_##sufset(_mm512_cmp_##sufcmp##_mask(a.val, b.val, imm8), tval)); } + +#define OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(_Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(==, _MM_CMPINT_EQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(!=, _MM_CMPINT_NE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(<, _MM_CMPINT_LT, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(>, _MM_CMPINT_NLE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(<=, _MM_CMPINT_LE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(>=, _MM_CMPINT_NLT, _Tpvec, sufcmp, sufset, tval) + +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint8x64, epu8, epi8, (char)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int8x64, epi8, epi8, (char)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint16x32, epu16, epi16, (short)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int16x32, epi16, epi16, (short)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint32x16, epu32, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int32x16, epi32, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint64x8, epu64, epi64, (int64)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int64x8, epi64, epi64, (int64)-1) + +#define OPENCV_HAL_IMPL_AVX512_CMP_FLT(bin_op, imm8, _Tpvec, sufcmp, sufset, tval) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_castsi512_##sufcmp(_mm512_maskz_set1_##sufset(_mm512_cmp_##sufcmp##_mask(a.val, b.val, imm8), tval))); } + +#define OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(_Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(==, _CMP_EQ_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(!=, _CMP_NEQ_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(<, _CMP_LT_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(>, _CMP_GT_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(<=, _CMP_LE_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(>=, _CMP_GE_OQ, _Tpvec, sufcmp, sufset, tval) + +OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(v_float32x16, ps, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(v_float64x8, pd, epi64, (int64)-1) + +inline v_float32x16 v_not_nan(const v_float32x16& a) +{ return v_float32x16(_mm512_castsi512_ps(_mm512_maskz_set1_epi32(_mm512_cmp_ps_mask(a.val, a.val, _CMP_ORD_Q), (int)-1))); } +inline v_float64x8 v_not_nan(const v_float64x8& a) +{ return v_float64x8(_mm512_castsi512_pd(_mm512_maskz_set1_epi64(_mm512_cmp_pd_mask(a.val, a.val, _CMP_ORD_Q), (int64)-1))); } + +/** min/max **/ +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint8x64, _mm512_min_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint8x64, _mm512_max_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int8x64, _mm512_min_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int8x64, _mm512_max_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint16x32, _mm512_min_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint16x32, _mm512_max_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int16x32, _mm512_min_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int16x32, _mm512_max_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint32x16, _mm512_min_epu32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint32x16, _mm512_max_epu32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int32x16, _mm512_min_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int32x16, _mm512_max_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint64x8, _mm512_min_epu64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint64x8, _mm512_max_epu64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int64x8, _mm512_min_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int64x8, _mm512_max_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_float32x16, _mm512_min_ps) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_float32x16, _mm512_max_ps) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_float64x8, _mm512_min_pd) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_float64x8, _mm512_max_pd) + +/** Rotate **/ +namespace { + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64&) { return v_int8x64(); }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(b.val, a.val, imm32 ), imm4 *8), + _mm512_slli_epi32(_mm512_alignr_epi32(b.val, a.val, imm32 + 1), (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(b.val, a.val, 15), imm4 *8), + _mm512_slli_epi32( b.val, (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 16), imm4 *8), + _mm512_slli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 15), (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { return v_int8x64(_mm512_srli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, 15), imm4*8)); }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { return v_int8x64(_mm512_alignr_epi32(b.val, a.val, imm32)); }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64&) { return a; }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { return v_int8x64(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 16)); }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) { return b; }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64&) { return v_int8x64(); }}; +} +template inline v_int8x64 v_rotate_right(const v_int8x64& a, const v_int8x64& b) +{ + return imm >= 128 ? v_int8x64() : +#if CV_AVX_512VBMI + v_int8x64(_mm512_permutex2var_epi8(a.val, + _v512_set_epu8(0x3f + imm, 0x3e + imm, 0x3d + imm, 0x3c + imm, 0x3b + imm, 0x3a + imm, 0x39 + imm, 0x38 + imm, + 0x37 + imm, 0x36 + imm, 0x35 + imm, 0x34 + imm, 0x33 + imm, 0x32 + imm, 0x31 + imm, 0x30 + imm, + 0x2f + imm, 0x2e + imm, 0x2d + imm, 0x2c + imm, 0x2b + imm, 0x2a + imm, 0x29 + imm, 0x28 + imm, + 0x27 + imm, 0x26 + imm, 0x25 + imm, 0x24 + imm, 0x23 + imm, 0x22 + imm, 0x21 + imm, 0x20 + imm, + 0x1f + imm, 0x1e + imm, 0x1d + imm, 0x1c + imm, 0x1b + imm, 0x1a + imm, 0x19 + imm, 0x18 + imm, + 0x17 + imm, 0x16 + imm, 0x15 + imm, 0x14 + imm, 0x13 + imm, 0x12 + imm, 0x11 + imm, 0x10 + imm, + 0x0f + imm, 0x0e + imm, 0x0d + imm, 0x0c + imm, 0x0b + imm, 0x0a + imm, 0x09 + imm, 0x08 + imm, + 0x07 + imm, 0x06 + imm, 0x05 + imm, 0x04 + imm, 0x03 + imm, 0x02 + imm, 0x01 + imm, 0x00 + imm), b.val)); +#else + _v_rotate_right 15), imm/4>::eval(a, b); +#endif +} +template +inline v_int8x64 v_rotate_left(const v_int8x64& a, const v_int8x64& b) +{ + if (imm == 0) return a; + if (imm == 64) return b; + if (imm >= 128) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_permutex2var_epi8(b.val, + _v512_set_epi8(0x7f - imm,0x7e - imm,0x7d - imm,0x7c - imm,0x7b - imm,0x7a - imm,0x79 - imm,0x78 - imm, + 0x77 - imm,0x76 - imm,0x75 - imm,0x74 - imm,0x73 - imm,0x72 - imm,0x71 - imm,0x70 - imm, + 0x6f - imm,0x6e - imm,0x6d - imm,0x6c - imm,0x6b - imm,0x6a - imm,0x69 - imm,0x68 - imm, + 0x67 - imm,0x66 - imm,0x65 - imm,0x64 - imm,0x63 - imm,0x62 - imm,0x61 - imm,0x60 - imm, + 0x5f - imm,0x5e - imm,0x5d - imm,0x5c - imm,0x5b - imm,0x5a - imm,0x59 - imm,0x58 - imm, + 0x57 - imm,0x56 - imm,0x55 - imm,0x54 - imm,0x53 - imm,0x52 - imm,0x51 - imm,0x50 - imm, + 0x4f - imm,0x4e - imm,0x4d - imm,0x4c - imm,0x4b - imm,0x4a - imm,0x49 - imm,0x48 - imm, + 0x47 - imm,0x46 - imm,0x45 - imm,0x44 - imm,0x43 - imm,0x42 - imm,0x41 - imm,0x40 - imm), a.val)); +#else + return imm < 64 ? v_rotate_right<64 - imm>(b, a) : v_rotate_right<128 - imm>(v512_setzero_s8(), b); +#endif +} +template +inline v_int8x64 v_rotate_right(const v_int8x64& a) +{ + if (imm == 0) return a; + if (imm >= 64) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_maskz_permutexvar_epi8(0xFFFFFFFFFFFFFFFF >> imm, + _v512_set_epu8(0x3f + imm,0x3e + imm,0x3d + imm,0x3c + imm,0x3b + imm,0x3a + imm,0x39 + imm,0x38 + imm, + 0x37 + imm,0x36 + imm,0x35 + imm,0x34 + imm,0x33 + imm,0x32 + imm,0x31 + imm,0x30 + imm, + 0x2f + imm,0x2e + imm,0x2d + imm,0x2c + imm,0x2b + imm,0x2a + imm,0x29 + imm,0x28 + imm, + 0x27 + imm,0x26 + imm,0x25 + imm,0x24 + imm,0x23 + imm,0x22 + imm,0x21 + imm,0x20 + imm, + 0x1f + imm,0x1e + imm,0x1d + imm,0x1c + imm,0x1b + imm,0x1a + imm,0x19 + imm,0x18 + imm, + 0x17 + imm,0x16 + imm,0x15 + imm,0x14 + imm,0x13 + imm,0x12 + imm,0x11 + imm,0x10 + imm, + 0x0f + imm,0x0e + imm,0x0d + imm,0x0c + imm,0x0b + imm,0x0a + imm,0x09 + imm,0x08 + imm, + 0x07 + imm,0x06 + imm,0x05 + imm,0x04 + imm,0x03 + imm,0x02 + imm,0x01 + imm,0x00 + imm), a.val)); +#else + return v_rotate_right(a, v512_setzero_s8()); +#endif +} +template +inline v_int8x64 v_rotate_left(const v_int8x64& a) +{ + if (imm == 0) return a; + if (imm >= 64) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_maskz_permutexvar_epi8(0xFFFFFFFFFFFFFFFF << imm, + _v512_set_epi8(0x3f - imm,0x3e - imm,0x3d - imm,0x3c - imm,0x3b - imm,0x3a - imm,0x39 - imm,0x38 - imm, + 0x37 - imm,0x36 - imm,0x35 - imm,0x34 - imm,0x33 - imm,0x32 - imm,0x31 - imm,0x30 - imm, + 0x2f - imm,0x2e - imm,0x2d - imm,0x2c - imm,0x2b - imm,0x2a - imm,0x29 - imm,0x28 - imm, + 0x27 - imm,0x26 - imm,0x25 - imm,0x24 - imm,0x23 - imm,0x22 - imm,0x21 - imm,0x20 - imm, + 0x1f - imm,0x1e - imm,0x1d - imm,0x1c - imm,0x1b - imm,0x1a - imm,0x19 - imm,0x18 - imm, + 0x17 - imm,0x16 - imm,0x15 - imm,0x14 - imm,0x13 - imm,0x12 - imm,0x11 - imm,0x10 - imm, + 0x0f - imm,0x0e - imm,0x0d - imm,0x0c - imm,0x0b - imm,0x0a - imm,0x09 - imm,0x08 - imm, + 0x07 - imm,0x06 - imm,0x05 - imm,0x04 - imm,0x03 - imm,0x02 - imm,0x01 - imm,0x00 - imm), a.val)); +#else + return v_rotate_right<64 - imm>(v512_setzero_s8(), a); +#endif +} + +#define OPENCV_HAL_IMPL_AVX512_ROTATE_PM(_Tpvec, suffix) \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ return v_reinterpret_as_##suffix(v_rotate_left(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b))); } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ return v_reinterpret_as_##suffix(v_rotate_right(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b))); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ return v_reinterpret_as_##suffix(v_rotate_left(v_reinterpret_as_s8(a))); } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ return v_reinterpret_as_##suffix(v_rotate_right(v_reinterpret_as_s8(a))); } + +#define OPENCV_HAL_IMPL_AVX512_ROTATE_EC(_Tpvec, suffix) \ +template \ +inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + enum { SHIFT2 = (_Tpvec::nlanes - imm) }; \ + enum { MASK = ((1 << _Tpvec::nlanes) - 1) }; \ + if (imm == 0) return a; \ + if (imm == _Tpvec::nlanes) return b; \ + if (imm >= 2*_Tpvec::nlanes) return _Tpvec(); \ + return _Tpvec(_mm512_mask_expand_##suffix(_mm512_maskz_compress_##suffix((MASK << SHIFT2)&MASK, b.val), (MASK << (imm))&MASK, a.val)); \ +} \ +template \ +inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + enum { SHIFT2 = (_Tpvec::nlanes - imm) }; \ + enum { MASK = ((1 << _Tpvec::nlanes) - 1) }; \ + if (imm == 0) return a; \ + if (imm == _Tpvec::nlanes) return b; \ + if (imm >= 2*_Tpvec::nlanes) return _Tpvec(); \ + return _Tpvec(_mm512_mask_expand_##suffix(_mm512_maskz_compress_##suffix((MASK << (imm))&MASK, a.val), (MASK << SHIFT2)&MASK, b.val)); \ +} \ +template \ +inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + if (imm == 0) return a; \ + if (imm >= _Tpvec::nlanes) return _Tpvec(); \ + return _Tpvec(_mm512_maskz_expand_##suffix((1 << _Tpvec::nlanes) - (1 << (imm)), a.val)); \ +} \ +template \ +inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + if (imm == 0) return a; \ + if (imm >= _Tpvec::nlanes) return _Tpvec(); \ + return _Tpvec(_mm512_maskz_compress_##suffix((1 << _Tpvec::nlanes) - (1 << (imm)), a.val)); \ +} + +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_uint8x64, u8) +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_uint16x32, u16) +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_int16x32, s16) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_uint64x8, epi64) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_int64x8, epi64) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_float64x8, pd) + +/** Reverse **/ +inline v_uint8x64 v_reverse(const v_uint8x64 &a) +{ +#if CV_AVX_512VBMI + static const __m512i perm = _mm512_set_epi32( + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f, + 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f, + 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f); + return v_uint8x64(_mm512_permutexvar_epi8(perm, a.val)); +#else + static const __m512i shuf = _mm512_set_epi32( + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f); + static const __m512i perm = _mm512_set_epi64(1, 0, 3, 2, 5, 4, 7, 6); + __m512i vec = _mm512_shuffle_epi8(a.val, shuf); + return v_uint8x64(_mm512_permutexvar_epi64(perm, vec)); +#endif +} + +inline v_int8x64 v_reverse(const v_int8x64 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x32 v_reverse(const v_uint16x32 &a) +{ +#if CV_AVX_512VBMI + static const __m512i perm = _mm512_set_epi32( + 0x00000001, 0x00020003, 0x00040005, 0x00060007, + 0x00080009, 0x000a000b, 0x000c000d, 0x000e000f, + 0x00100011, 0x00120013, 0x00140015, 0x00160017, + 0x00180019, 0x001a001b, 0x001c001d, 0x001e001f); + return v_uint16x32(_mm512_permutexvar_epi16(perm, a.val)); +#else + static const __m512i shuf = _mm512_set_epi32( + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e); + static const __m512i perm = _mm512_set_epi64(1, 0, 3, 2, 5, 4, 7, 6); + __m512i vec = _mm512_shuffle_epi8(a.val, shuf); + return v_uint16x32(_mm512_permutexvar_epi64(perm, vec)); +#endif +} + +inline v_int16x32 v_reverse(const v_int16x32 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x16 v_reverse(const v_uint32x16 &a) +{ + static const __m512i perm = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15); + return v_uint32x16(_mm512_permutexvar_epi32(perm, a.val)); +} + +inline v_int32x16 v_reverse(const v_int32x16 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x16 v_reverse(const v_float32x16 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x8 v_reverse(const v_uint64x8 &a) +{ + static const __m512i perm = _mm512_set_epi64(0, 1, 2, 3, 4, 5, 6, 7); + return v_uint64x8(_mm512_permutexvar_epi64(perm, a.val)); +} + +inline v_int64x8 v_reverse(const v_int64x8 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x8 v_reverse(const v_float64x8 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +////////// Reduce ///////// + +/** Reduce **/ +#define OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64(a, b) a + b +#define OPENCV_HAL_IMPL_AVX512_REDUCE_8(sctype, func, _Tpvec, ifunc, scop) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + sctype CV_DECL_ALIGNED(64) idx[2]; \ + _mm_store_si128((__m128i*)idx, _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1))); \ + return scop(idx[0], idx[1]); } +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, min, v_uint64x8, min_epu64, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, max, v_uint64x8, max_epu64, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, sum, v_uint64x8, add_epi64, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, min, v_int64x8, min_epi64, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, max, v_int64x8, max_epi64, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, sum, v_int64x8, add_epi64, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_8F(func, ifunc, scop) \ + inline double v_reduce_##func(const v_float64x8& a) \ + { __m256d half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + double CV_DECL_ALIGNED(64) idx[2]; \ + _mm_store_pd(idx, _mm_##ifunc(_mm256_castpd256_pd128(half), _mm256_extractf128_pd(half, 1))); \ + return scop(idx[0], idx[1]); } +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(min, min_pd, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(max, max_pd, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(sum, add_pd, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_16(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_16(uint, min, v_uint32x16, min_epu32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(uint, max, v_uint32x16, max_epu32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(int, min, v_int32x16, min_epi32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(int, max, v_int32x16, max_epi32) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_16F(func, ifunc) \ + inline float v_reduce_##func(const v_float32x16& a) \ + { __m256 half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128 quarter = _mm_##ifunc(_mm256_castps256_ps128(half), _mm256_extractf128_ps(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_permute_ps(quarter, _MM_SHUFFLE(0, 0, 3, 2))); \ + quarter = _mm_##ifunc(quarter, _mm_permute_ps(quarter, _MM_SHUFFLE(0, 0, 0, 1))); \ + return _mm_cvtss_f32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_16F(min, min_ps) +OPENCV_HAL_IMPL_AVX512_REDUCE_16F(max, max_ps) + +inline float v_reduce_sum(const v_float32x16& a) +{ + __m256 half = _mm256_add_ps(_v512_extract_low(a.val), _v512_extract_high(a.val)); + __m128 quarter = _mm_add_ps(_mm256_castps256_ps128(half), _mm256_extractf128_ps(half, 1)); + quarter = _mm_hadd_ps(quarter, quarter); + return _mm_cvtss_f32(_mm_hadd_ps(quarter, quarter)); +} +inline int v_reduce_sum(const v_int32x16& a) +{ + __m256i half = _mm256_add_epi32(_v512_extract_low(a.val), _v512_extract_high(a.val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + quarter = _mm_hadd_epi32(quarter, quarter); + return _mm_cvtsi128_si32(_mm_hadd_epi32(quarter, quarter)); +} +inline uint v_reduce_sum(const v_uint32x16& a) +{ return (uint)v_reduce_sum(v_reinterpret_as_s32(a)); } + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_32(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 2)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_32(ushort, min, v_uint16x32, min_epu16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(ushort, max, v_uint16x32, max_epu16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(short, min, v_int16x32, min_epi16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(short, max, v_int16x32, max_epi16) + +inline int v_reduce_sum(const v_int16x32& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline uint v_reduce_sum(const v_uint16x32& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_64(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 2)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 1)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_64(uchar, min, v_uint8x64, min_epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(uchar, max, v_uint8x64, max_epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(schar, min, v_int8x64, min_epi8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(schar, max, v_int8x64, max_epi8) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(sctype, _Tpvec, suffix) \ + inline sctype v_reduce_sum(const _Tpvec& a) \ + { __m512i a16 = _mm512_add_epi16(_mm512_cvt##suffix##_epi16(_v512_extract_low(a.val)), \ + _mm512_cvt##suffix##_epi16(_v512_extract_high(a.val))); \ + a16 = _mm512_cvtepi16_epi32(_mm256_add_epi16(_v512_extract_low(a16), _v512_extract_high(a16))); \ + __m256i a8 = _mm256_add_epi32(_v512_extract_low(a16), _v512_extract_high(a16)); \ + __m128i a4 = _mm_add_epi32(_mm256_castsi256_si128(a8), _mm256_extracti128_si256(a8, 1)); \ + a4 = _mm_hadd_epi32(a4, a4); \ + return (sctype)_mm_cvtsi128_si32(_mm_hadd_epi32(a4, a4)); } +OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(uint, v_uint8x64, epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(int, v_int8x64, epi8) + +inline v_float32x16 v_reduce_sum4(const v_float32x16& a, const v_float32x16& b, + const v_float32x16& c, const v_float32x16& d) +{ + __m256 abl = _mm256_hadd_ps(_v512_extract_low(a.val), _v512_extract_low(b.val)); + __m256 abh = _mm256_hadd_ps(_v512_extract_high(a.val), _v512_extract_high(b.val)); + __m256 cdl = _mm256_hadd_ps(_v512_extract_low(c.val), _v512_extract_low(d.val)); + __m256 cdh = _mm256_hadd_ps(_v512_extract_high(c.val), _v512_extract_high(d.val)); + return v_float32x16(_v512_combine(_mm256_hadd_ps(abl, cdl), _mm256_hadd_ps(abh, cdh))); +} + +inline unsigned v_reduce_sad(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i val = _mm512_sad_epu8(a.val, b.val); + __m256i half = _mm256_add_epi32(_v512_extract_low(val), _v512_extract_high(val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_int8x64& a, const v_int8x64& b) +{ + __m512i val = _mm512_set1_epi8(-128); + val = _mm512_sad_epu8(_mm512_add_epi8(a.val, val), _mm512_add_epi8(b.val, val)); + __m256i half = _mm256_add_epi32(_v512_extract_low(val), _v512_extract_high(val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_uint16x32& a, const v_uint16x32& b) +{ return v_reduce_sum(v_add_wrap(a - b, b - a)); } +inline unsigned v_reduce_sad(const v_int16x32& a, const v_int16x32& b) +{ return v_reduce_sum(v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b)))); } +inline unsigned v_reduce_sad(const v_uint32x16& a, const v_uint32x16& b) +{ return v_reduce_sum(v_max(a, b) - v_min(a, b)); } +inline unsigned v_reduce_sad(const v_int32x16& a, const v_int32x16& b) +{ return v_reduce_sum(v_reinterpret_as_u32(v_max(a, b) - v_min(a, b))); } +inline float v_reduce_sad(const v_float32x16& a, const v_float32x16& b) +{ return v_reduce_sum((a - b) & v_float32x16(_mm512_castsi512_ps(_mm512_set1_epi32(0x7fffffff)))); } +inline double v_reduce_sad(const v_float64x8& a, const v_float64x8& b) +{ return v_reduce_sum((a - b) & v_float64x8(_mm512_castsi512_pd(_mm512_set1_epi64(0x7fffffffffffffff)))); } + +/** Popcount **/ +inline v_uint8x64 v_popcount(const v_int8x64& a) +{ +#if CV_AVX_512BITALG + return v_uint8x64(_mm512_popcnt_epi8(a.val)); +#elif CV_AVX_512VBMI + __m512i _popcnt_table0 = _v512_set_epu8(7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, + 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0); + __m512i _popcnt_table1 = _v512_set_epu8(7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, + 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, + 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1); + return v_uint8x64(_mm512_sub_epi8(_mm512_permutex2var_epi8(_popcnt_table0, a.val, _popcnt_table1), _mm512_movm_epi8(_mm512_movepi8_mask(a.val)))); +#else + __m512i _popcnt_table = _mm512_set4_epi32(0x04030302, 0x03020201, 0x03020201, 0x02010100); + __m512i _popcnt_mask = _mm512_set1_epi8(0x0F); + + return v_uint8x64(_mm512_add_epi8(_mm512_shuffle_epi8(_popcnt_table, _mm512_and_si512( a.val, _popcnt_mask)), + _mm512_shuffle_epi8(_popcnt_table, _mm512_and_si512(_mm512_srli_epi16(a.val, 4), _popcnt_mask)))); +#endif +} +inline v_uint16x32 v_popcount(const v_int16x32& a) +{ +#if CV_AVX_512BITALG + return v_uint16x32(_mm512_popcnt_epi16(a.val)); +#elif CV_AVX_512VPOPCNTDQ + __m512i zero = _mm512_setzero_si512(); + return v_uint16x32(_mm512_packs_epi32(_mm512_popcnt_epi32(_mm512_unpacklo_epi16(a.val, zero)), + _mm512_popcnt_epi32(_mm512_unpackhi_epi16(a.val, zero)))); +#else + v_uint8x64 p = v_popcount(v_reinterpret_as_s8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v512_setall_u16(0x00ff); +#endif +} +inline v_uint32x16 v_popcount(const v_int32x16& a) +{ +#if CV_AVX_512VPOPCNTDQ + return v_uint32x16(_mm512_popcnt_epi32(a.val)); +#else + v_uint8x64 p = v_popcount(v_reinterpret_as_s8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v512_setall_u32(0x000000ff); +#endif +} +inline v_uint64x8 v_popcount(const v_int64x8& a) +{ +#if CV_AVX_512VPOPCNTDQ + return v_uint64x8(_mm512_popcnt_epi64(a.val)); +#else + return v_uint64x8(_mm512_sad_epu8(v_popcount(v_reinterpret_as_s8(a)).val, _mm512_setzero_si512())); +#endif +} + + +inline v_uint8x64 v_popcount(const v_uint8x64& a) { return v_popcount(v_reinterpret_as_s8 (a)); } +inline v_uint16x32 v_popcount(const v_uint16x32& a) { return v_popcount(v_reinterpret_as_s16(a)); } +inline v_uint32x16 v_popcount(const v_uint32x16& a) { return v_popcount(v_reinterpret_as_s32(a)); } +inline v_uint64x8 v_popcount(const v_uint64x8& a) { return v_popcount(v_reinterpret_as_s64(a)); } + + +////////// Other math ///////// + +/** Some frequent operations **/ +#define OPENCV_HAL_IMPL_AVX512_MULADD(_Tpvec, suffix) \ + inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm512_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm512_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_sqrt(const _Tpvec& x) \ + { return _Tpvec(_mm512_sqrt_##suffix(x.val)); } \ + inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_fma(a, a, b * b); } \ + inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_sqrt(v_fma(a, a, b * b)); } + +OPENCV_HAL_IMPL_AVX512_MULADD(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_MULADD(v_float64x8, pd) + +inline v_int32x16 v_fma(const v_int32x16& a, const v_int32x16& b, const v_int32x16& c) +{ return a * b + c; } +inline v_int32x16 v_muladd(const v_int32x16& a, const v_int32x16& b, const v_int32x16& c) +{ return v_fma(a, b, c); } + +inline v_float32x16 v_invsqrt(const v_float32x16& x) +{ +#if CV_AVX_512ER + return v_float32x16(_mm512_rsqrt28_ps(x.val)); +#else + v_float32x16 half = x * v512_setall_f32(0.5); + v_float32x16 t = v_float32x16(_mm512_rsqrt14_ps(x.val)); + t *= v512_setall_f32(1.5) - ((t * t) * half); + return t; +#endif +} + +inline v_float64x8 v_invsqrt(const v_float64x8& x) +{ +#if CV_AVX_512ER + return v_float64x8(_mm512_rsqrt28_pd(x.val)); +#else + return v512_setall_f64(1.) / v_sqrt(x); +// v_float64x8 half = x * v512_setall_f64(0.5); +// v_float64x8 t = v_float64x8(_mm512_rsqrt14_pd(x.val)); +// t *= v512_setall_f64(1.5) - ((t * t) * half); +// t *= v512_setall_f64(1.5) - ((t * t) * half); +// return t; +#endif +} + +/** Absolute values **/ +#define OPENCV_HAL_IMPL_AVX512_ABS(_Tpvec, _Tpuvec, suffix) \ + inline _Tpuvec v_abs(const _Tpvec& x) \ + { return _Tpuvec(_mm512_abs_##suffix(x.val)); } + +OPENCV_HAL_IMPL_AVX512_ABS(v_int8x64, v_uint8x64, epi8) +OPENCV_HAL_IMPL_AVX512_ABS(v_int16x32, v_uint16x32, epi16) +OPENCV_HAL_IMPL_AVX512_ABS(v_int32x16, v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ABS(v_int64x8, v_uint64x8, epi64) + +inline v_float32x16 v_abs(const v_float32x16& x) +{ +#ifdef _mm512_abs_pd + return v_float32x16(_mm512_abs_ps(x.val)); +#else + return v_float32x16(_mm512_castsi512_ps(_mm512_and_si512(_mm512_castps_si512(x.val), + _v512_set_epu64(0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, + 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF)))); +#endif +} + +inline v_float64x8 v_abs(const v_float64x8& x) +{ +#ifdef _mm512_abs_pd + #if defined __GNUC__ && (__GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ <= 3) || (__GNUC__ == 8 && __GNUC_MINOR__ <= 2)) + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87476 + return v_float64x8(_mm512_abs_pd(_mm512_castpd_ps(x.val))); + #else + return v_float64x8(_mm512_abs_pd(x.val)); + #endif +#else + return v_float64x8(_mm512_castsi512_pd(_mm512_and_si512(_mm512_castpd_si512(x.val), + _v512_set_epu64(0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF)))); +#endif +} + +/** Absolute difference **/ +inline v_uint8x64 v_absdiff(const v_uint8x64& a, const v_uint8x64& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x32 v_absdiff(const v_uint16x32& a, const v_uint16x32& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x16 v_absdiff(const v_uint32x16& a, const v_uint32x16& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x64 v_absdiff(const v_int8x64& a, const v_int8x64& b) +{ + v_int8x64 d = v_sub_wrap(a, b); + v_int8x64 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} + +inline v_uint16x32 v_absdiff(const v_int16x32& a, const v_int16x32& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } + +inline v_uint32x16 v_absdiff(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 d = a - b; + v_int32x16 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +inline v_float32x16 v_absdiff(const v_float32x16& a, const v_float32x16& b) +{ return v_abs(a - b); } + +inline v_float64x8 v_absdiff(const v_float64x8& a, const v_float64x8& b) +{ return v_abs(a - b); } + +/** Saturating absolute difference **/ +inline v_int8x64 v_absdiffs(const v_int8x64& a, const v_int8x64& b) +{ + v_int8x64 d = a - b; + v_int8x64 m = a < b; + return (d ^ m) - m; +} +inline v_int16x32 v_absdiffs(const v_int16x32& a, const v_int16x32& b) +{ return v_max(a, b) - v_min(a, b); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x16 v_round(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(a.val)); } + +inline v_int32x16 v_round(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(a.val))); } + +inline v_int32x16 v_round(const v_float64x8& a, const v_float64x8& b) +{ return v_int32x16(_v512_combine(_mm512_cvtpd_epi32(a.val), _mm512_cvtpd_epi32(b.val))); } + +inline v_int32x16 v_trunc(const v_float32x16& a) +{ return v_int32x16(_mm512_cvttps_epi32(a.val)); } + +inline v_int32x16 v_trunc(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvttpd_epi32(a.val))); } + +#if CVT_ROUND_MODES_IMPLEMENTED +inline v_int32x16 v_floor(const v_float32x16& a) +{ return v_int32x16(_mm512_cvt_roundps_epi32(a.val, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC)); } + +inline v_int32x16 v_floor(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvt_roundpd_epi32(a.val, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC))); } + +inline v_int32x16 v_ceil(const v_float32x16& a) +{ return v_int32x16(_mm512_cvt_roundps_epi32(a.val, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC)); } + +inline v_int32x16 v_ceil(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvt_roundpd_epi32(a.val, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC))); } +#else +inline v_int32x16 v_floor(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(_mm512_roundscale_ps(a.val, 1))); } + +inline v_int32x16 v_floor(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(_mm512_roundscale_pd(a.val, 1)))); } + +inline v_int32x16 v_ceil(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(_mm512_roundscale_ps(a.val, 2))); } + +inline v_int32x16 v_ceil(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(_mm512_roundscale_pd(a.val, 2)))); } +#endif + +/** To float **/ +inline v_float32x16 v_cvt_f32(const v_int32x16& a) +{ return v_float32x16(_mm512_cvtepi32_ps(a.val)); } + +inline v_float32x16 v_cvt_f32(const v_float64x8& a) +{ return v_float32x16(_mm512_cvtpd_pslo(a.val)); } + +inline v_float32x16 v_cvt_f32(const v_float64x8& a, const v_float64x8& b) +{ return v_float32x16(_v512_combine(_mm512_cvtpd_ps(a.val), _mm512_cvtpd_ps(b.val))); } + +inline v_float64x8 v_cvt_f64(const v_int32x16& a) +{ return v_float64x8(_mm512_cvtepi32_pd(_v512_extract_low(a.val))); } + +inline v_float64x8 v_cvt_f64_high(const v_int32x16& a) +{ return v_float64x8(_mm512_cvtepi32_pd(_v512_extract_high(a.val))); } + +inline v_float64x8 v_cvt_f64(const v_float32x16& a) +{ return v_float64x8(_mm512_cvtps_pd(_v512_extract_low(a.val))); } + +inline v_float64x8 v_cvt_f64_high(const v_float32x16& a) +{ return v_float64x8(_mm512_cvtps_pd(_v512_extract_high(a.val))); } + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x8 v_cvt_f64(const v_int64x8& v) +{ +#if CV_AVX_512DQ + return v_float64x8(_mm512_cvtepi64_pd(v.val)); +#else + // constants encoded as floating-point + __m512i magic_i_lo = _mm512_set1_epi64(0x4330000000000000); // 2^52 + __m512i magic_i_hi32 = _mm512_set1_epi64(0x4530000080000000); // 2^84 + 2^63 + __m512i magic_i_all = _mm512_set1_epi64(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m512d magic_d_all = _mm512_castsi512_pd(magic_i_all); + + // Blend the 32 lowest significant bits of v with magic_int_lo + __m512i v_lo = _mm512_mask_blend_epi32(0x5555, magic_i_lo, v.val); + // Extract the 32 most significant bits of v + __m512i v_hi = _mm512_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm512_xor_si512(v_hi, magic_i_hi32); + // Compute in double precision + __m512d v_hi_dbl = _mm512_sub_pd(_mm512_castsi512_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m512d result = _mm512_add_pd(v_hi_dbl, _mm512_castsi512_pd(v_lo)); + return v_float64x8(result); +#endif +} + +////////////// Lookup table access //////////////////// + +inline v_int8x64 v512_lut(const schar* tab, const int* idx) +{ + __m128i p0 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 1)); + __m128i p1 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 1)); + __m128i p2 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 2), (const int *)tab, 1)); + __m128i p3 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 3), (const int *)tab, 1)); + return v_int8x64(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512(p0), p1, 1), p2, 2), p3, 3)); +} +inline v_int8x64 v512_lut_pairs(const schar* tab, const int* idx) +{ + __m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 1)); + __m256i p1 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 1)); + return v_int8x64(_v512_combine(p0, p1)); +} +inline v_int8x64 v512_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x64(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), (const int *)tab, 1)); +} +inline v_uint8x64 v512_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut((const schar *)tab, idx)); } +inline v_uint8x64 v512_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x64 v512_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_quads((const schar *)tab, idx)); } + +inline v_int16x32 v512_lut(const short* tab, const int* idx) +{ + __m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 2)); + __m256i p1 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 2)); + return v_int16x32(_v512_combine(p0, p1)); +} +inline v_int16x32 v512_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x32(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), (const int *)tab, 2)); +} +inline v_int16x32 v512_lut_quads(const short* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int16x32(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 2)); +#else + return v_int16x32(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const int64*)tab, 2)); +#endif +} +inline v_uint16x32 v512_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut((const short *)tab, idx)); } +inline v_uint16x32 v512_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut_pairs((const short *)tab, idx)); } +inline v_uint16x32 v512_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut_quads((const short *)tab, idx)); } + +inline v_int32x16 v512_lut(const int* tab, const int* idx) +{ + return v_int32x16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), tab, 4)); +} +inline v_int32x16 v512_lut_pairs(const int* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int32x16(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 4)); +#else + return v_int32x16(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const int64*)tab, 4)); +#endif +} +inline v_int32x16 v512_lut_quads(const int* tab, const int* idx) +{ + return v_int32x16(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512( + _mm_loadu_si128((const __m128i*)(tab + idx[0]))), + _mm_loadu_si128((const __m128i*)(tab + idx[1])), 1), + _mm_loadu_si128((const __m128i*)(tab + idx[2])), 2), + _mm_loadu_si128((const __m128i*)(tab + idx[3])), 3)); +} +inline v_uint32x16 v512_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut((const int *)tab, idx)); } +inline v_uint32x16 v512_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut_pairs((const int *)tab, idx)); } +inline v_uint32x16 v512_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut_quads((const int *)tab, idx)); } + +inline v_int64x8 v512_lut(const int64* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int64x8(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 8)); +#else + return v_int64x8(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), tab , 8)); +#endif +} +inline v_int64x8 v512_lut_pairs(const int64* tab, const int* idx) +{ + return v_int64x8(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512( + _mm_loadu_si128((const __m128i*)(tab + idx[0]))), + _mm_loadu_si128((const __m128i*)(tab + idx[1])), 1), + _mm_loadu_si128((const __m128i*)(tab + idx[2])), 2), + _mm_loadu_si128((const __m128i*)(tab + idx[3])), 3)); +} +inline v_uint64x8 v512_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v512_lut((const int64 *)tab, idx)); } +inline v_uint64x8 v512_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v512_lut_pairs((const int64 *)tab, idx)); } + +inline v_float32x16 v512_lut(const float* tab, const int* idx) +{ + return v_float32x16(_mm512_i32gather_ps(_mm512_loadu_si512((const __m512i*)idx), tab, 4)); +} +inline v_float32x16 v512_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v512_lut_pairs((const int *)tab, idx)); } +inline v_float32x16 v512_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v512_lut_quads((const int *)tab, idx)); } + +inline v_float64x8 v512_lut(const double* tab, const int* idx) +{ + return v_float64x8(_mm512_i32gather_pd(_mm256_loadu_si256((const __m256i*)idx), tab, 8)); +} +inline v_float64x8 v512_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x8(_mm512_insertf64x2(_mm512_insertf64x2(_mm512_insertf64x2(_mm512_castpd128_pd512( + _mm_loadu_pd(tab + idx[0])), + _mm_loadu_pd(tab + idx[1]), 1), + _mm_loadu_pd(tab + idx[2]), 2), + _mm_loadu_pd(tab + idx[3]), 3)); +} + +inline v_int32x16 v_lut(const int* tab, const v_int32x16& idxvec) +{ + return v_int32x16(_mm512_i32gather_epi32(idxvec.val, tab, 4)); +} + +inline v_uint32x16 v_lut(const unsigned* tab, const v_int32x16& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x16 v_lut(const float* tab, const v_int32x16& idxvec) +{ + return v_float32x16(_mm512_i32gather_ps(idxvec.val, tab, 4)); +} + +inline v_float64x8 v_lut(const double* tab, const v_int32x16& idxvec) +{ + return v_float64x8(_mm512_i32gather_pd(_v512_extract_low(idxvec.val), tab, 8)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x16& idxvec, v_float32x16& x, v_float32x16& y) +{ + x.val = _mm512_i32gather_ps(idxvec.val, tab, 4); + y.val = _mm512_i32gather_ps(idxvec.val, &tab[1], 4); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x16& idxvec, v_float64x8& x, v_float64x8& y) +{ + x.val = _mm512_i32gather_pd(_v512_extract_low(idxvec.val), tab, 8); + y.val = _mm512_i32gather_pd(_v512_extract_low(idxvec.val), &tab[1], 8); +} + +inline v_int8x64 v_interleave_pairs(const v_int8x64& vec) +{ + return v_int8x64(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0d0e0c, 0x0b090a08, 0x07050604, 0x03010200))); +} +inline v_uint8x64 v_interleave_pairs(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x64 v_interleave_quads(const v_int8x64& vec) +{ + return v_int8x64(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0b0e0a, 0x0d090c08, 0x07030602, 0x05010400))); +} +inline v_uint8x64 v_interleave_quads(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x32 v_interleave_pairs(const v_int16x32& vec) +{ + return v_int16x32(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0e0b0a, 0x0d0c0908, 0x07060302, 0x05040100))); +} +inline v_uint16x32 v_interleave_pairs(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x32 v_interleave_quads(const v_int16x32& vec) +{ + return v_int16x32(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0e0706, 0x0d0c0504, 0x0b0a0302, 0x09080100))); +} +inline v_uint16x32 v_interleave_quads(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x16 v_interleave_pairs(const v_int32x16& vec) +{ + return v_int32x16(_mm512_shuffle_epi32(vec.val, _MM_PERM_ACBD)); +} +inline v_uint32x16 v_interleave_pairs(const v_uint32x16& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x16 v_interleave_pairs(const v_float32x16& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x64 v_pack_triplets(const v_int8x64& vec) +{ + return v_int8x64(_mm512_permutexvar_epi32(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), + _mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0xffffff0f, 0x0e0d0c0a, 0x09080605, 0x04020100)))); +} +inline v_uint8x64 v_pack_triplets(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x32 v_pack_triplets(const v_int16x32& vec) +{ + return v_int16x32(_mm512_permutexvar_epi16(_v512_set_epu64(0x001f001f001f001f, 0x001f001f001f001f, 0x001e001d001c001a, 0x0019001800160015, + 0x0014001200110010, 0x000e000d000c000a, 0x0009000800060005, 0x0004000200010000), vec.val)); +} +inline v_uint16x32 v_pack_triplets(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x16 v_pack_triplets(const v_int32x16& vec) +{ + return v_int32x16(_mm512_permutexvar_epi32(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), vec.val)); +} +inline v_uint32x16 v_pack_triplets(const v_uint32x16& vec) { return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); } +inline v_float32x16 v_pack_triplets(const v_float32x16& vec) +{ + return v_float32x16(_mm512_permutexvar_ps(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), vec.val)); +} + +////////// Matrix operations ///////// + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x16 v_dotprod(const v_int16x32& a, const v_int16x32& b) +{ return v_int32x16(_mm512_madd_epi16(a.val, b.val)); } +inline v_int32x16 v_dotprod(const v_int16x32& a, const v_int16x32& b, const v_int32x16& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x8 v_dotprod(const v_int32x16& a, const v_int32x16& b) +{ + __m512i even = _mm512_mul_epi32(a.val, b.val); + __m512i odd = _mm512_mul_epi32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32)); + return v_int64x8(_mm512_add_epi64(even, odd)); +} +inline v_int64x8 v_dotprod(const v_int32x16& a, const v_int32x16& b, const v_int64x8& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x16 v_dotprod_expand(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i even_a = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, a.val, _mm512_setzero_si512()); + __m512i odd_a = _mm512_srli_epi16(a.val, 8); + + __m512i even_b = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, b.val, _mm512_setzero_si512()); + __m512i odd_b = _mm512_srli_epi16(b.val, 8); + + __m512i prod0 = _mm512_madd_epi16(even_a, even_b); + __m512i prod1 = _mm512_madd_epi16(odd_a, odd_b); + return v_uint32x16(_mm512_add_epi32(prod0, prod1)); +} +inline v_uint32x16 v_dotprod_expand(const v_uint8x64& a, const v_uint8x64& b, const v_uint32x16& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x16 v_dotprod_expand(const v_int8x64& a, const v_int8x64& b) +{ + __m512i even_a = _mm512_srai_epi16(_mm512_bslli_epi128(a.val, 1), 8); + __m512i odd_a = _mm512_srai_epi16(a.val, 8); + + __m512i even_b = _mm512_srai_epi16(_mm512_bslli_epi128(b.val, 1), 8); + __m512i odd_b = _mm512_srai_epi16(b.val, 8); + + __m512i prod0 = _mm512_madd_epi16(even_a, even_b); + __m512i prod1 = _mm512_madd_epi16(odd_a, odd_b); + return v_int32x16(_mm512_add_epi32(prod0, prod1)); +} +inline v_int32x16 v_dotprod_expand(const v_int8x64& a, const v_int8x64& b, const v_int32x16& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x8 v_dotprod_expand(const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i mullo = _mm512_mullo_epi16(a.val, b.val); + __m512i mulhi = _mm512_mulhi_epu16(a.val, b.val); + __m512i mul0 = _mm512_unpacklo_epi16(mullo, mulhi); + __m512i mul1 = _mm512_unpackhi_epi16(mullo, mulhi); + + __m512i p02 = _mm512_mask_blend_epi32(0xAAAA, mul0, _mm512_setzero_si512()); + __m512i p13 = _mm512_srli_epi64(mul0, 32); + __m512i p46 = _mm512_mask_blend_epi32(0xAAAA, mul1, _mm512_setzero_si512()); + __m512i p57 = _mm512_srli_epi64(mul1, 32); + + __m512i p15_ = _mm512_add_epi64(p02, p13); + __m512i p9d_ = _mm512_add_epi64(p46, p57); + + return v_uint64x8(_mm512_add_epi64( + _mm512_unpacklo_epi64(p15_, p9d_), + _mm512_unpackhi_epi64(p15_, p9d_) + )); +} +inline v_uint64x8 v_dotprod_expand(const v_uint16x32& a, const v_uint16x32& b, const v_uint64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x8 v_dotprod_expand(const v_int16x32& a, const v_int16x32& b) +{ + __m512i prod = _mm512_madd_epi16(a.val, b.val); + __m512i even = _mm512_srai_epi64(_mm512_bslli_epi128(prod, 4), 32); + __m512i odd = _mm512_srai_epi64(prod, 32); + return v_int64x8(_mm512_add_epi64(even, odd)); +} +inline v_int64x8 v_dotprod_expand(const v_int16x32& a, const v_int16x32& b, const v_int64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x8 v_dotprod_expand(const v_int32x16& a, const v_int32x16& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x8 v_dotprod_expand(const v_int32x16& a, const v_int32x16& b, const v_float64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x16 v_dotprod_fast(const v_int16x32& a, const v_int16x32& b) +{ return v_dotprod(a, b); } +inline v_int32x16 v_dotprod_fast(const v_int16x32& a, const v_int16x32& b, const v_int32x16& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x8 v_dotprod_fast(const v_int32x16& a, const v_int32x16& b) +{ return v_dotprod(a, b); } +inline v_int64x8 v_dotprod_fast(const v_int32x16& a, const v_int32x16& b, const v_int64x8& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x16 v_dotprod_expand_fast(const v_uint8x64& a, const v_uint8x64& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x16 v_dotprod_expand_fast(const v_uint8x64& a, const v_uint8x64& b, const v_uint32x16& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int32x16 v_dotprod_expand_fast(const v_int8x64& a, const v_int8x64& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x16 v_dotprod_expand_fast(const v_int8x64& a, const v_int8x64& b, const v_int32x16& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x8 v_dotprod_expand_fast(const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i mullo = _mm512_mullo_epi16(a.val, b.val); + __m512i mulhi = _mm512_mulhi_epu16(a.val, b.val); + __m512i mul0 = _mm512_unpacklo_epi16(mullo, mulhi); + __m512i mul1 = _mm512_unpackhi_epi16(mullo, mulhi); + + __m512i p02 = _mm512_mask_blend_epi32(0xAAAA, mul0, _mm512_setzero_si512()); + __m512i p13 = _mm512_srli_epi64(mul0, 32); + __m512i p46 = _mm512_mask_blend_epi32(0xAAAA, mul1, _mm512_setzero_si512()); + __m512i p57 = _mm512_srli_epi64(mul1, 32); + + __m512i p15_ = _mm512_add_epi64(p02, p13); + __m512i p9d_ = _mm512_add_epi64(p46, p57); + return v_uint64x8(_mm512_add_epi64(p15_, p9d_)); +} +inline v_uint64x8 v_dotprod_expand_fast(const v_uint16x32& a, const v_uint16x32& b, const v_uint64x8& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x8 v_dotprod_expand_fast(const v_int16x32& a, const v_int16x32& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x8 v_dotprod_expand_fast(const v_int16x32& a, const v_int16x32& b, const v_int64x8& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x8 v_dotprod_expand_fast(const v_int32x16& a, const v_int32x16& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x8 v_dotprod_expand_fast(const v_int32x16& a, const v_int32x16& b, const v_float64x8& c) +{ return v_dotprod_expand(a, b) + c; } + + +#define OPENCV_HAL_AVX512_SPLAT2_PS(a, im) \ + v_float32x16(_mm512_permute_ps(a.val, _MM_SHUFFLE(im, im, im, im))) + +inline v_float32x16 v_matmul(const v_float32x16& v, + const v_float32x16& m0, const v_float32x16& m1, + const v_float32x16& m2, const v_float32x16& m3) +{ + v_float32x16 v04 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 0); + v_float32x16 v15 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 1); + v_float32x16 v26 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 2); + v_float32x16 v37 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 3); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, v37 * m3))); +} + +inline v_float32x16 v_matmuladd(const v_float32x16& v, + const v_float32x16& m0, const v_float32x16& m1, + const v_float32x16& m2, const v_float32x16& a) +{ + v_float32x16 v04 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 0); + v_float32x16 v15 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 1); + v_float32x16 v26 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 2); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, a))); +} + +#define OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ + inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ + { \ + __m512i t0 = cast_from(_mm512_unpacklo_##suffix(a0.val, a1.val)); \ + __m512i t1 = cast_from(_mm512_unpacklo_##suffix(a2.val, a3.val)); \ + __m512i t2 = cast_from(_mm512_unpackhi_##suffix(a0.val, a1.val)); \ + __m512i t3 = cast_from(_mm512_unpackhi_##suffix(a2.val, a3.val)); \ + b0.val = cast_to(_mm512_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm512_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm512_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm512_unpackhi_epi64(t2, t3)); \ + } + +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_uint32x16, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_int32x16, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_float32x16, ps, _mm512_castps_si512, _mm512_castsi512_ps) + +//////////////// Value reordering /////////////// + +/* Expand */ +#define OPENCV_HAL_IMPL_AVX512_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(_v512_extract_low(a.val)); \ + b1.val = intrin(_v512_extract_high(a.val)); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v512_extract_low(a.val))); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v512_extract_high(a.val))); } \ + inline _Tpwvec v512_load_expand(const _Tp* ptr) \ + { \ + __m256i a = _mm256_loadu_si256((const __m256i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint8x64, v_uint16x32, uchar, _mm512_cvtepu8_epi16) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int8x64, v_int16x32, schar, _mm512_cvtepi8_epi16) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint16x32, v_uint32x16, ushort, _mm512_cvtepu16_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int16x32, v_int32x16, short, _mm512_cvtepi16_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint32x16, v_uint64x8, unsigned, _mm512_cvtepu32_epi64) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int32x16, v_int64x8, int, _mm512_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_AVX512_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v512_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX512_EXPAND_Q(v_uint32x16, uchar, _mm512_cvtepu8_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND_Q(v_int32x16, schar, _mm512_cvtepi8_epi32) + +/* pack */ +// 16 +inline v_int8x64 v_pack(const v_int16x32& a, const v_int16x32& b) +{ return v_int8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi16(a.val, b.val))); } + +inline v_uint8x64 v_pack(const v_uint16x32& a, const v_uint16x32& b) +{ + const __m512i t = _mm512_set1_epi16(255); + return v_uint8x64(_v512_combine(_mm512_cvtepi16_epi8(_mm512_min_epu16(a.val, t)), _mm512_cvtepi16_epi8(_mm512_min_epu16(b.val, t)))); +} + +inline v_uint8x64 v_pack_u(const v_int16x32& a, const v_int16x32& b) +{ + return v_uint8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packus_epi16(a.val, b.val))); +} + +inline void v_pack_store(schar* ptr, const v_int16x32& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(uchar* ptr, const v_uint16x32& a) +{ + const __m512i m = _mm512_set1_epi16(255); + _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi16_epi8(_mm512_min_epu16(a.val, m))); +} + +inline void v_pack_u_store(uchar* ptr, const v_int16x32& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + +template inline +v_uint8x64 v_rshr_pack(const v_uint16x32& a, const v_uint16x32& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + v_uint16x32 delta = v512_setall_u16((short)(1 << (n-1))); + return v_pack_u(v_reinterpret_as_s16((a + delta) >> n), + v_reinterpret_as_s16((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x32& a) +{ + v_uint16x32 delta = v512_setall_u16((short)(1 << (n-1))); + v_pack_u_store(ptr, v_reinterpret_as_s16((a + delta) >> n)); +} + +template inline +v_uint8x64 v_rshr_pack_u(const v_int16x32& a, const v_int16x32& b) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x32& a) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int8x64 v_rshr_pack(const v_int16x32& a, const v_int16x32& b) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x32& a) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + v_pack_store(ptr, (a + delta) >> n); +} + +// 32 +inline v_int16x32 v_pack(const v_int32x16& a, const v_int32x16& b) +{ return v_int16x32(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi32(a.val, b.val))); } + +inline v_uint16x32 v_pack(const v_uint32x16& a, const v_uint32x16& b) +{ + const __m512i m = _mm512_set1_epi32(65535); + return v_uint16x32(_v512_combine(_mm512_cvtepi32_epi16(_mm512_min_epu32(a.val, m)), _mm512_cvtepi32_epi16(_mm512_min_epu32(b.val, m)))); +} + +inline v_uint16x32 v_pack_u(const v_int32x16& a, const v_int32x16& b) +{ return v_uint16x32(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packus_epi32(a.val, b.val))); } + +inline void v_pack_store(short* ptr, const v_int32x16& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x16& a) +{ + const __m512i m = _mm512_set1_epi32(65535); + _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi32_epi16(_mm512_min_epu32(a.val, m))); +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x16& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + + +template inline +v_uint16x32 v_rshr_pack(const v_uint32x16& a, const v_uint32x16& b) +{ + v_uint32x16 delta = v512_setall_u32(1 << (n-1)); + return v_pack_u(v_reinterpret_as_s32((a + delta) >> n), + v_reinterpret_as_s32((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x16& a) +{ + v_uint32x16 delta = v512_setall_u32(1 << (n-1)); + v_pack_u_store(ptr, v_reinterpret_as_s32((a + delta) >> n)); +} + +template inline +v_uint16x32 v_rshr_pack_u(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x16& a) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int16x32 v_rshr_pack(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x16& a) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// 64 +// Non-saturating pack +inline v_uint32x16 v_pack(const v_uint64x8& a, const v_uint64x8& b) +{ return v_uint32x16(_v512_combine(_mm512_cvtepi64_epi32(a.val), _mm512_cvtepi64_epi32(b.val))); } + +inline v_int32x16 v_pack(const v_int64x8& a, const v_int64x8& b) +{ return v_reinterpret_as_s32(v_pack(v_reinterpret_as_u64(a), v_reinterpret_as_u64(b))); } + +inline void v_pack_store(unsigned* ptr, const v_uint64x8& a) +{ _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi64_epi32(a.val)); } + +inline void v_pack_store(int* ptr, const v_int64x8& b) +{ v_pack_store((unsigned*)ptr, v_reinterpret_as_u64(b)); } + +template inline +v_uint32x16 v_rshr_pack(const v_uint64x8& a, const v_uint64x8& b) +{ + v_uint64x8 delta = v512_setall_u64((uint64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x8& a) +{ + v_uint64x8 delta = v512_setall_u64((uint64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +template inline +v_int32x16 v_rshr_pack(const v_int64x8& a, const v_int64x8& b) +{ + v_int64x8 delta = v512_setall_s64((int64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x8& a) +{ + v_int64x8 delta = v512_setall_s64((int64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// pack boolean +inline v_uint8x64 v_pack_b(const v_uint16x32& a, const v_uint16x32& b) +{ return v_uint8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi16(a.val, b.val))); } + +inline v_uint8x64 v_pack_b(const v_uint32x16& a, const v_uint32x16& b, + const v_uint32x16& c, const v_uint32x16& d) +{ + __m512i ab = _mm512_packs_epi32(a.val, b.val); + __m512i cd = _mm512_packs_epi32(c.val, d.val); + + return v_uint8x64(_mm512_permutexvar_epi32(_v512_set_epu32(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0), _mm512_packs_epi16(ab, cd))); +} + +inline v_uint8x64 v_pack_b(const v_uint64x8& a, const v_uint64x8& b, const v_uint64x8& c, + const v_uint64x8& d, const v_uint64x8& e, const v_uint64x8& f, + const v_uint64x8& g, const v_uint64x8& h) +{ + __m512i ab = _mm512_packs_epi32(a.val, b.val); + __m512i cd = _mm512_packs_epi32(c.val, d.val); + __m512i ef = _mm512_packs_epi32(e.val, f.val); + __m512i gh = _mm512_packs_epi32(g.val, h.val); + + __m512i abcd = _mm512_packs_epi32(ab, cd); + __m512i efgh = _mm512_packs_epi32(ef, gh); + + return v_uint8x64(_mm512_permutexvar_epi16(_v512_set_epu16(31, 23, 15, 7, 30, 22, 14, 6, 29, 21, 13, 5, 28, 20, 12, 4, + 27, 19, 11, 3, 26, 18, 10, 2, 25, 17, 9, 1, 24, 16, 8, 0), _mm512_packs_epi16(abcd, efgh))); +} + +/* Recombine */ +// its up there with load and store operations + +/* Extract */ +#define OPENCV_HAL_IMPL_AVX512_EXTRACT(_Tpvec) \ + template \ + inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ + { return v_rotate_right(a, b); } + +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint8x64) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int8x64) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint16x32) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int16x32) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint64x8) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int64x8) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_float32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_float64x8) + +#define OPENCV_HAL_IMPL_AVX512_EXTRACT_N(_Tpvec, _Tp) \ +template inline _Tp v_extract_n(_Tpvec v) { return v_rotate_right(v).get0(); } + +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint8x64, uchar) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int8x64, schar) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint16x32, ushort) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int16x32, short) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint32x16, uint) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int32x16, int) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint64x8, uint64) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int64x8, int64) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_float32x16, float) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_float64x8, double) + +template +inline v_uint32x16 v_broadcast_element(v_uint32x16 a) +{ + static const __m512i perm = _mm512_set1_epi32((char)i); + return v_uint32x16(_mm512_permutexvar_epi32(perm, a.val)); +} + +template +inline v_int32x16 v_broadcast_element(const v_int32x16 &a) +{ return v_reinterpret_as_s32(v_broadcast_element(v_reinterpret_as_u32(a))); } + +template +inline v_float32x16 v_broadcast_element(const v_float32x16 &a) +{ return v_reinterpret_as_f32(v_broadcast_element(v_reinterpret_as_u32(a))); } + + +///////////////////// load deinterleave ///////////////////////////// + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(126, 124, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, + 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu8(127, 125, 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, 97, + 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, + 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint8x64(_mm512_permutex2var_epi8(ab0, mask0, ab1)); + b = v_uint8x64(_mm512_permutex2var_epi8(ab0, mask1, ab1)); +#else + __m512i mask0 = _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200); + __m512i a0b0 = _mm512_shuffle_epi8(ab0, mask0); + __m512i a1b1 = _mm512_shuffle_epi8(ab1, mask0); + __m512i mask1 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask2 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint8x64(_mm512_permutex2var_epi64(a0b0, mask1, a1b1)); + b = v_uint8x64(_mm512_permutex2var_epi64(a0b0, mask2, a1b1)); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i mask0 = _v512_set_epu16(62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu16(63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint16x32(_mm512_permutex2var_epi16(ab0, mask0, ab1)); + b = v_uint16x32(_mm512_permutex2var_epi16(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint32x16(_mm512_permutex2var_epi32(ab0, mask0, ab1)); + b = v_uint32x16(_mm512_permutex2var_epi32(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i mask0 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint64x8(_mm512_permutex2var_epi64(ab0, mask0, ab1)); + b = v_uint64x8(_mm512_permutex2var_epi64(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b, v_uint8x64& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 128)); + +#if CV_AVX_512VBMI2 + __m512i mask0 = _v512_set_epu8(126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, + 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, + 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 62, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2); + __m512i r0b01 = _mm512_permutex2var_epi8(bgr0, mask0, bgr1); + __m512i b1g12 = _mm512_permutex2var_epi8(bgr1, mask0, bgr2); + __m512i r12b2 = _mm512_permutex2var_epi8(bgr1, + _v512_set_epu8(125, 122, 119, 116, 113, 110, 107, 104, 101, 98, 95, 92, 89, 86, 83, 80, + 77, 74, 71, 68, 65, 127, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, + 94, 91, 88, 85, 82, 79, 76, 73, 70, 67, 64, 61, 58, 55, 52, 49, + 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1), bgr2); + a = v_uint8x64(_mm512_mask_compress_epi8(r12b2, 0xffffffffffe00000, r0b01)); + b = v_uint8x64(_mm512_mask_compress_epi8(b1g12, 0x2492492492492492, bgr0)); + c = v_uint8x64(_mm512_mask_expand_epi8(r0b01, 0xffffffffffe00000, r12b2)); +#elif CV_AVX_512VBMI + __m512i b0g0b1 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr1, bgr0); + __m512i g1r1g2 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr2, bgr1); + __m512i r2b2r0 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr0, bgr2); + a = v_uint8x64(_mm512_permutex2var_epi8(b0g0b1, _v512_set_epu8(125, 122, 119, 116, 113, 110, 107, 104, 101, 98, 95, 92, 89, 86, 83, 80, + 77, 74, 71, 68, 65, 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, + 46, 45, 43, 42, 40, 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, + 23, 21, 20, 18, 17, 15, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0), bgr2)); + b = v_uint8x64(_mm512_permutex2var_epi8(g1r1g2, _v512_set_epu8( 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, 46, 45, 43, 42, 40, + 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, 23, 21, 20, 18, 17, + 15, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0, 126, 123, 120, 117, 114, + 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, 78, 75, 72, 69, 66), bgr0)); + c = v_uint8x64(_mm512_permutex2var_epi8(r2b2r0, _v512_set_epu8( 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, + 15, 12, 9, 6, 3, 0, 125, 122, 119, 116, 113, 110, 107, 104, 101, 98, + 95, 92, 89, 86, 83, 80, 77, 74, 71, 68, 65, 62, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2), bgr1)); +#else + __m512i mask0 = _v512_set_epu16(61, 58, 55, 52, 49, 46, 43, 40, 37, 34, 63, 60, 57, 54, 51, 48, + 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi16(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi16(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi16(bgr2, mask0, bgr0); + + __m512i b0g0 = _mm512_mask_blend_epi32(0xf800, b01g1, r12b2); + __m512i r0b1 = _mm512_permutex2var_epi16(bgr1, _v512_set_epu16(42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 29, 26, 23, 20, 17, + 14, 11, 8, 5, 2, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43), g20r0); + __m512i g1r1 = _mm512_alignr_epi32(r12b2, g20r0, 11); + a = v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, b0g0, r0b1)); + c = v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, r0b1, g1r1)); + b = v_uint8x64(_mm512_shuffle_epi8(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, g1r1, b0g0), _mm512_set4_epi32(0x0e0f0c0d, 0x0a0b0809, 0x06070405, 0x02030001))); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b, v_uint16x32& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + + __m512i mask0 = _v512_set_epu16(61, 58, 55, 52, 49, 46, 43, 40, 37, 34, 63, 60, 57, 54, 51, 48, + 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi16(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi16(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi16(bgr2, mask0, bgr0); + + a = v_uint16x32(_mm512_mask_blend_epi32(0xf800, b01g1, r12b2)); + b = v_uint16x32(_mm512_permutex2var_epi16(bgr1, _v512_set_epu16(42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 29, 26, 23, 20, 17, + 14, 11, 8, 5, 2, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43), g20r0)); + c = v_uint16x32(_mm512_alignr_epi32(r12b2, g20r0, 11)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b, v_uint32x16& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + + __m512i mask0 = _v512_set_epu32(29, 26, 23, 20, 17, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01r1 = _mm512_permutex2var_epi32(bgr0, mask0, bgr1); + __m512i g12b2 = _mm512_permutex2var_epi32(bgr1, mask0, bgr2); + __m512i r20g0 = _mm512_permutex2var_epi32(bgr2, mask0, bgr0); + + a = v_uint32x16(_mm512_mask_blend_epi32(0xf800, b01r1, g12b2)); + b = v_uint32x16(_mm512_alignr_epi32(g12b2, r20g0, 11)); + c = v_uint32x16(_mm512_permutex2var_epi32(bgr1, _v512_set_epu32(21, 20, 19, 18, 17, 16, 13, 10, 7, 4, 1, 26, 25, 24, 23, 22), r20g0)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b, v_uint64x8& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + + __m512i mask0 = _v512_set_epu64(13, 10, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi64(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi64(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi64(bgr2, mask0, bgr0); + + a = v_uint64x8(_mm512_mask_blend_epi64(0xc0, b01g1, r12b2)); + c = v_uint64x8(_mm512_alignr_epi64(r12b2, g20r0, 6)); + b = v_uint64x8(_mm512_permutex2var_epi64(bgr1, _v512_set_epu64(10, 9, 8, 5, 2, 13, 12, 11), g20r0)); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b, v_uint8x64& c, v_uint8x64& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 128)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 192)); + +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(126, 124, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, + 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu8(127, 125, 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, 97, + 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, + 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi8(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi8(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi8(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi8(bgra2, mask1, bgra3); + + a = v_uint8x64(_mm512_permutex2var_epi8(br01, mask0, br23)); + c = v_uint8x64(_mm512_permutex2var_epi8(br01, mask1, br23)); + b = v_uint8x64(_mm512_permutex2var_epi8(ga01, mask0, ga23)); + d = v_uint8x64(_mm512_permutex2var_epi8(ga01, mask1, ga23)); +#else + __m512i mask = _mm512_set4_epi32(0x0f0b0703, 0x0e0a0602, 0x0d090501, 0x0c080400); + __m512i b0g0r0a0 = _mm512_shuffle_epi8(bgra0, mask); + __m512i b1g1r1a1 = _mm512_shuffle_epi8(bgra1, mask); + __m512i b2g2r2a2 = _mm512_shuffle_epi8(bgra2, mask); + __m512i b3g3r3a3 = _mm512_shuffle_epi8(bgra3, mask); + + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi32(b0g0r0a0, mask0, b1g1r1a1); + __m512i ga01 = _mm512_permutex2var_epi32(b0g0r0a0, mask1, b1g1r1a1); + __m512i br23 = _mm512_permutex2var_epi32(b2g2r2a2, mask0, b3g3r3a3); + __m512i ga23 = _mm512_permutex2var_epi32(b2g2r2a2, mask1, b3g3r3a3); + + a = v_uint8x64(_mm512_permutex2var_epi32(br01, mask0, br23)); + c = v_uint8x64(_mm512_permutex2var_epi32(br01, mask1, br23)); + b = v_uint8x64(_mm512_permutex2var_epi32(ga01, mask0, ga23)); + d = v_uint8x64(_mm512_permutex2var_epi32(ga01, mask1, ga23)); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b, v_uint16x32& c, v_uint16x32& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 96)); + + __m512i mask0 = _v512_set_epu16(62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu16(63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi16(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi16(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi16(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi16(bgra2, mask1, bgra3); + + a = v_uint16x32(_mm512_permutex2var_epi16(br01, mask0, br23)); + c = v_uint16x32(_mm512_permutex2var_epi16(br01, mask1, br23)); + b = v_uint16x32(_mm512_permutex2var_epi16(ga01, mask0, ga23)); + d = v_uint16x32(_mm512_permutex2var_epi16(ga01, mask1, ga23)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b, v_uint32x16& c, v_uint32x16& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 48)); + + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi32(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi32(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi32(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi32(bgra2, mask1, bgra3); + + a = v_uint32x16(_mm512_permutex2var_epi32(br01, mask0, br23)); + c = v_uint32x16(_mm512_permutex2var_epi32(br01, mask1, br23)); + b = v_uint32x16(_mm512_permutex2var_epi32(ga01, mask0, ga23)); + d = v_uint32x16(_mm512_permutex2var_epi32(ga01, mask1, ga23)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b, v_uint64x8& c, v_uint64x8& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 24)); + + __m512i mask0 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi64(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi64(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi64(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi64(bgra2, mask1, bgra3); + + a = v_uint64x8(_mm512_permutex2var_epi64(br01, mask0, br23)); + c = v_uint64x8(_mm512_permutex2var_epi64(br01, mask1, br23)); + b = v_uint64x8(_mm512_permutex2var_epi64(ga01, mask0, ga23)); + d = v_uint64x8(_mm512_permutex2var_epi64(ga01, mask1, ga23)); +} + +///////////////////////////// store interleave ///////////////////////////////////// + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& x, const v_uint8x64& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint8x64 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 64), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 64), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), high.val); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& x, const v_uint16x32& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint16x32 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 32), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 32), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), high.val); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& x, const v_uint32x16& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint32x16 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 16), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 16), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), high.val); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& x, const v_uint64x8& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint64x8 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 8), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 8), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 8), high.val); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& a, const v_uint8x64& b, const v_uint8x64& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(127, 84, 20, 126, 83, 19, 125, 82, 18, 124, 81, 17, 123, 80, 16, 122, + 79, 15, 121, 78, 14, 120, 77, 13, 119, 76, 12, 118, 75, 11, 117, 74, + 10, 116, 73, 9, 115, 72, 8, 114, 71, 7, 113, 70, 6, 112, 69, 5, + 111, 68, 4, 110, 67, 3, 109, 66, 2, 108, 65, 1, 107, 64, 0, 106); + __m512i mask1 = _v512_set_epu8( 21, 42, 105, 20, 41, 104, 19, 40, 103, 18, 39, 102, 17, 38, 101, 16, + 37, 100, 15, 36, 99, 14, 35, 98, 13, 34, 97, 12, 33, 96, 11, 32, + 95, 10, 31, 94, 9, 30, 93, 8, 29, 92, 7, 28, 91, 6, 27, 90, + 5, 26, 89, 4, 25, 88, 3, 24, 87, 2, 23, 86, 1, 22, 85, 0); + __m512i mask2 = _v512_set_epu8(106, 127, 63, 105, 126, 62, 104, 125, 61, 103, 124, 60, 102, 123, 59, 101, + 122, 58, 100, 121, 57, 99, 120, 56, 98, 119, 55, 97, 118, 54, 96, 117, + 53, 95, 116, 52, 94, 115, 51, 93, 114, 50, 92, 113, 49, 91, 112, 48, + 90, 111, 47, 89, 110, 46, 88, 109, 45, 87, 108, 44, 86, 107, 43, 85); + __m512i r2g0r0 = _mm512_permutex2var_epi8(b.val, mask0, c.val); + __m512i b0r1b1 = _mm512_permutex2var_epi8(a.val, mask1, c.val); + __m512i g1b2g2 = _mm512_permutex2var_epi8(a.val, mask2, b.val); + + __m512i bgr0 = _mm512_mask_blend_epi8(0x9249249249249249, r2g0r0, b0r1b1); + __m512i bgr1 = _mm512_mask_blend_epi8(0x9249249249249249, b0r1b1, g1b2g2); + __m512i bgr2 = _mm512_mask_blend_epi8(0x9249249249249249, g1b2g2, r2g0r0); +#else + __m512i g1g0 = _mm512_shuffle_epi8(b.val, _mm512_set4_epi32(0x0e0f0c0d, 0x0a0b0809, 0x06070405, 0x02030001)); + __m512i b0g0 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, a.val, g1g0); + __m512i r0b1 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, c.val, a.val); + __m512i g1r1 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, g1g0, c.val); + + __m512i mask0 = _v512_set_epu16(42, 10, 31, 41, 9, 30, 40, 8, 29, 39, 7, 28, 38, 6, 27, 37, + 5, 26, 36, 4, 25, 35, 3, 24, 34, 2, 23, 33, 1, 22, 32, 0); + __m512i mask1 = _v512_set_epu16(21, 52, 41, 20, 51, 40, 19, 50, 39, 18, 49, 38, 17, 48, 37, 16, + 47, 36, 15, 46, 35, 14, 45, 34, 13, 44, 33, 12, 43, 32, 11, 42); + __m512i mask2 = _v512_set_epu16(63, 31, 20, 62, 30, 19, 61, 29, 18, 60, 28, 17, 59, 27, 16, 58, + 26, 15, 57, 25, 14, 56, 24, 13, 55, 23, 12, 54, 22, 11, 53, 21); + __m512i b0g0b2 = _mm512_permutex2var_epi16(b0g0, mask0, r0b1); + __m512i r1b1r0 = _mm512_permutex2var_epi16(b0g0, mask1, g1r1); + __m512i g2r2g1 = _mm512_permutex2var_epi16(r0b1, mask2, g1r1); + + __m512i bgr0 = _mm512_mask_blend_epi16(0x24924924, b0g0b2, r1b1r0); + __m512i bgr1 = _mm512_mask_blend_epi16(0x24924924, r1b1r0, g2r2g1); + __m512i bgr2 = _mm512_mask_blend_epi16(0x24924924, g2r2g1, b0g0b2); +#endif + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 64), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 128), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 64), bgr1); + _mm512_store_si512((__m512i*)(ptr + 128), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 128), bgr2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& a, const v_uint16x32& b, const v_uint16x32& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu16(42, 10, 31, 41, 9, 30, 40, 8, 29, 39, 7, 28, 38, 6, 27, 37, + 5, 26, 36, 4, 25, 35, 3, 24, 34, 2, 23, 33, 1, 22, 32, 0); + __m512i mask1 = _v512_set_epu16(21, 52, 41, 20, 51, 40, 19, 50, 39, 18, 49, 38, 17, 48, 37, 16, + 47, 36, 15, 46, 35, 14, 45, 34, 13, 44, 33, 12, 43, 32, 11, 42); + __m512i mask2 = _v512_set_epu16(63, 31, 20, 62, 30, 19, 61, 29, 18, 60, 28, 17, 59, 27, 16, 58, + 26, 15, 57, 25, 14, 56, 24, 13, 55, 23, 12, 54, 22, 11, 53, 21); + __m512i b0g0b2 = _mm512_permutex2var_epi16(a.val, mask0, b.val); + __m512i r1b1r0 = _mm512_permutex2var_epi16(a.val, mask1, c.val); + __m512i g2r2g1 = _mm512_permutex2var_epi16(b.val, mask2, c.val); + + __m512i bgr0 = _mm512_mask_blend_epi16(0x24924924, b0g0b2, r1b1r0); + __m512i bgr1 = _mm512_mask_blend_epi16(0x24924924, r1b1r0, g2r2g1); + __m512i bgr2 = _mm512_mask_blend_epi16(0x24924924, g2r2g1, b0g0b2); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 32), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 64), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 32), bgr1); + _mm512_store_si512((__m512i*)(ptr + 64), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgr2); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& a, const v_uint32x16& b, const v_uint32x16& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu32(26, 31, 15, 25, 30, 14, 24, 29, 13, 23, 28, 12, 22, 27, 11, 21); + __m512i mask1 = _v512_set_epu32(31, 10, 25, 30, 9, 24, 29, 8, 23, 28, 7, 22, 27, 6, 21, 26); + __m512i g1b2g2 = _mm512_permutex2var_epi32(a.val, mask0, b.val); + __m512i r2r1b1 = _mm512_permutex2var_epi32(a.val, mask1, c.val); + + __m512i bgr0 = _mm512_mask_expand_epi32(_mm512_mask_expand_epi32(_mm512_maskz_expand_epi32(0x9249, a.val), 0x2492, b.val), 0x4924, c.val); + __m512i bgr1 = _mm512_mask_blend_epi32(0x9249, r2r1b1, g1b2g2); + __m512i bgr2 = _mm512_mask_blend_epi32(0x9249, g1b2g2, r2r1b1); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 16), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 32), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 16), bgr1); + _mm512_store_si512((__m512i*)(ptr + 32), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgr2); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& a, const v_uint64x8& b, const v_uint64x8& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu64( 5, 12, 7, 4, 11, 6, 3, 10); + __m512i mask1 = _v512_set_epu64(15, 7, 4, 14, 6, 3, 13, 5); + __m512i r1b1b2 = _mm512_permutex2var_epi64(a.val, mask0, c.val); + __m512i g2r2g1 = _mm512_permutex2var_epi64(b.val, mask1, c.val); + + __m512i bgr0 = _mm512_mask_expand_epi64(_mm512_mask_expand_epi64(_mm512_maskz_expand_epi64(0x49, a.val), 0x92, b.val), 0x24, c.val); + __m512i bgr1 = _mm512_mask_blend_epi64(0xdb, g2r2g1, r1b1b2); + __m512i bgr2 = _mm512_mask_blend_epi64(0xdb, r1b1b2, g2r2g1); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 8), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 16), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 8), bgr1); + _mm512_store_si512((__m512i*)(ptr + 16), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 8), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgr2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& a, const v_uint8x64& b, + const v_uint8x64& c, const v_uint8x64& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint8x64 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint8x64 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 192), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 192), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 192), bgra3.val); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& a, const v_uint16x32& b, + const v_uint16x32& c, const v_uint16x32& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint16x32 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint16x32 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 96), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 96), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 96), bgra3.val); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& a, const v_uint32x16& b, + const v_uint32x16& c, const v_uint32x16& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint32x16 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint32x16 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 48), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 48), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 48), bgra3.val); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& a, const v_uint64x8& b, + const v_uint64x8& c, const v_uint64x8& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint64x8 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint64x8 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 24), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 24), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 24), bgra3.val); + } +} + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, const _Tpvec0& c0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int8x64, schar, s8, v_uint8x64, uchar, u8) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int16x32, short, s16, v_uint16x32, ushort, u16) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int32x16, int, s32, v_uint32x16, unsigned, u32) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_float32x16, float, f32, v_uint32x16, unsigned, u32) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int64x8, int64, s64, v_uint64x8, uint64, u64) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_float64x8, double, f64, v_uint64x8, uint64, u64) + +////////// Mask and checks ///////// + +/** Mask **/ +inline int64 v_signmask(const v_int8x64& a) { return (int64)_mm512_movepi8_mask(a.val); } +inline int v_signmask(const v_int16x32& a) { return (int)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline int v_signmask(const v_int32x16& a) { return (int)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline int v_signmask(const v_int64x8& a) { return (int)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } + +inline int64 v_signmask(const v_uint8x64& a) { return v_signmask(v_reinterpret_as_s8(a)); } +inline int v_signmask(const v_uint16x32& a) { return v_signmask(v_reinterpret_as_s16(a)); } +inline int v_signmask(const v_uint32x16& a) { return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_uint64x8& a) { return v_signmask(v_reinterpret_as_s64(a)); } +inline int v_signmask(const v_float32x16& a) { return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_float64x8& a) { return v_signmask(v_reinterpret_as_s64(a)); } + +/** Checks **/ +inline bool v_check_all(const v_int8x64& a) { return !(bool)_mm512_cmp_epi8_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int8x64& a) { return (bool)_mm512_movepi8_mask(a.val); } +inline bool v_check_all(const v_int16x32& a) { return !(bool)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int16x32& a) { return (bool)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline bool v_check_all(const v_int32x16& a) { return !(bool)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int32x16& a) { return (bool)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline bool v_check_all(const v_int64x8& a) { return !(bool)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int64x8& a) { return (bool)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } + +inline bool v_check_all(const v_float32x16& a) { return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_float32x16& a) { return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_float64x8& a) { return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_float64x8& a) { return v_check_any(v_reinterpret_as_s64(a)); } +inline bool v_check_all(const v_uint8x64& a) { return v_check_all(v_reinterpret_as_s8(a)); } +inline bool v_check_all(const v_uint16x32& a) { return v_check_all(v_reinterpret_as_s16(a)); } +inline bool v_check_all(const v_uint32x16& a) { return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_uint64x8& a) { return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_uint8x64& a) { return v_check_any(v_reinterpret_as_s8(a)); } +inline bool v_check_any(const v_uint16x32& a) { return v_check_any(v_reinterpret_as_s16(a)); } +inline bool v_check_any(const v_uint32x16& a) { return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_uint64x8& a) { return v_check_any(v_reinterpret_as_s64(a)); } + +inline int v_scan_forward(const v_int8x64& a) +{ + int64 mask = _mm512_movepi8_mask(a.val); + int mask32 = (int)mask; + return mask != 0 ? mask32 != 0 ? trailingZeros32(mask32) : 32 + trailingZeros32((int)(mask >> 32)) : 0; +} +inline int v_scan_forward(const v_uint8x64& a) { return v_scan_forward(v_reinterpret_as_s8(a)); } +inline int v_scan_forward(const v_int16x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))); } +inline int v_scan_forward(const v_uint16x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))); } +inline int v_scan_forward(const v_int32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_uint32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_float32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_int64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } +inline int v_scan_forward(const v_uint64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } +inline int v_scan_forward(const v_float64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } + +inline void v512_cleanup() { _mm256_zeroall(); } + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_AVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_cpp.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_cpp.hpp new file mode 100644 index 0000000..859bfd7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_cpp.hpp @@ -0,0 +1,2781 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_CPP_HPP +#define OPENCV_HAL_INTRIN_CPP_HPP + +#include +#include +#include +#include "opencv2/core/saturate.hpp" + +//! @cond IGNORED +#define CV_SIMD128_CPP 1 +#if defined(CV_FORCE_SIMD128_CPP) || defined(CV_DOXYGEN) +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 +#endif +//! @endcond + +namespace cv +{ + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +#endif + +/** @addtogroup core_hal_intrin + +"Universal intrinsics" is a types and functions set intended to simplify vectorization of code on +different platforms. Currently there are two supported SIMD extensions: __SSE/SSE2__ on x86 +architectures and __NEON__ on ARM architectures, both allow working with 128 bit registers +containing packed values of different types. In case when there is no SIMD extension available +during compilation, fallback C++ implementation of intrinsics will be chosen and code will work as +expected although it could be slower. + +### Types + +There are several types representing 128-bit register as a vector of packed values, each type is +implemented as a structure based on a one SIMD register. + +- cv::v_uint8x16 and cv::v_int8x16: sixteen 8-bit integer values (unsigned/signed) - char +- cv::v_uint16x8 and cv::v_int16x8: eight 16-bit integer values (unsigned/signed) - short +- cv::v_uint32x4 and cv::v_int32x4: four 32-bit integer values (unsigned/signed) - int +- cv::v_uint64x2 and cv::v_int64x2: two 64-bit integer values (unsigned/signed) - int64 +- cv::v_float32x4: four 32-bit floating point values (signed) - float +- cv::v_float64x2: two 64-bit floating point values (signed) - double + +@note +cv::v_float64x2 is not implemented in NEON variant, if you want to use this type, don't forget to +check the CV_SIMD128_64F preprocessor definition: +@code +#if CV_SIMD128_64F +//... +#endif +@endcode + +### Load and store operations + +These operations allow to set contents of the register explicitly or by loading it from some memory +block and to save contents of the register to memory block. + +- Constructors: +@ref v_reg::v_reg(const _Tp *ptr) "from memory", +@ref v_reg::v_reg(_Tp s0, _Tp s1) "from two values", ... +- Other create methods: +@ref v_setall_s8, @ref v_setall_u8, ..., +@ref v_setzero_u8, @ref v_setzero_s8, ... +- Memory operations: +@ref v_load, @ref v_load_aligned, @ref v_load_low, @ref v_load_halves, +@ref v_store, @ref v_store_aligned, +@ref v_store_high, @ref v_store_low + +### Value reordering + +These operations allow to reorder or recombine elements in one or multiple vectors. + +- Interleave, deinterleave (2, 3 and 4 channels): @ref v_load_deinterleave, @ref v_store_interleave +- Expand: @ref v_load_expand, @ref v_load_expand_q, @ref v_expand, @ref v_expand_low, @ref v_expand_high +- Pack: @ref v_pack, @ref v_pack_u, @ref v_pack_b, @ref v_rshr_pack, @ref v_rshr_pack_u, +@ref v_pack_store, @ref v_pack_u_store, @ref v_rshr_pack_store, @ref v_rshr_pack_u_store +- Recombine: @ref v_zip, @ref v_recombine, @ref v_combine_low, @ref v_combine_high +- Reverse: @ref v_reverse +- Extract: @ref v_extract + + +### Arithmetic, bitwise and comparison operations + +Element-wise binary and unary operations. + +- Arithmetics: +@ref operator +(const v_reg &a, const v_reg &b) "+", +@ref operator -(const v_reg &a, const v_reg &b) "-", +@ref operator *(const v_reg &a, const v_reg &b) "*", +@ref operator /(const v_reg &a, const v_reg &b) "/", +@ref v_mul_expand + +- Non-saturating arithmetics: @ref v_add_wrap, @ref v_sub_wrap + +- Bitwise shifts: +@ref operator <<(const v_reg &a, int s) "<<", +@ref operator >>(const v_reg &a, int s) ">>", +@ref v_shl, @ref v_shr + +- Bitwise logic: +@ref operator &(const v_reg &a, const v_reg &b) "&", +@ref operator |(const v_reg &a, const v_reg &b) "|", +@ref operator ^(const v_reg &a, const v_reg &b) "^", +@ref operator ~(const v_reg &a) "~" + +- Comparison: +@ref operator >(const v_reg &a, const v_reg &b) ">", +@ref operator >=(const v_reg &a, const v_reg &b) ">=", +@ref operator <(const v_reg &a, const v_reg &b) "<", +@ref operator <=(const v_reg &a, const v_reg &b) "<=", +@ref operator==(const v_reg &a, const v_reg &b) "==", +@ref operator !=(const v_reg &a, const v_reg &b) "!=" + +- min/max: @ref v_min, @ref v_max + +### Reduce and mask + +Most of these operations return only one value. + +- Reduce: @ref v_reduce_min, @ref v_reduce_max, @ref v_reduce_sum, @ref v_popcount +- Mask: @ref v_signmask, @ref v_check_all, @ref v_check_any, @ref v_select + +### Other math + +- Some frequent operations: @ref v_sqrt, @ref v_invsqrt, @ref v_magnitude, @ref v_sqr_magnitude +- Absolute values: @ref v_abs, @ref v_absdiff, @ref v_absdiffs + +### Conversions + +Different type conversions and casts: + +- Rounding: @ref v_round, @ref v_floor, @ref v_ceil, @ref v_trunc, +- To float: @ref v_cvt_f32, @ref v_cvt_f64 +- Reinterpret: @ref v_reinterpret_as_u8, @ref v_reinterpret_as_s8, ... + +### Matrix operations + +In these operations vectors represent matrix rows/columns: @ref v_dotprod, @ref v_dotprod_fast, +@ref v_dotprod_expand, @ref v_dotprod_expand_fast, @ref v_matmul, @ref v_transpose4x4 + +### Usability + +Most operations are implemented only for some subset of the available types, following matrices +shows the applicability of different operations to the types. + +Regular integers: + +| Operations\\Types | uint 8x16 | int 8x16 | uint 16x8 | int 16x8 | uint 32x4 | int 32x4 | +|-------------------|:-:|:-:|:-:|:-:|:-:|:-:| +|load, store | x | x | x | x | x | x | +|interleave | x | x | x | x | x | x | +|expand | x | x | x | x | x | x | +|expand_low | x | x | x | x | x | x | +|expand_high | x | x | x | x | x | x | +|expand_q | x | x | | | | | +|add, sub | x | x | x | x | x | x | +|add_wrap, sub_wrap | x | x | x | x | | | +|mul_wrap | x | x | x | x | | | +|mul | x | x | x | x | x | x | +|mul_expand | x | x | x | x | x | | +|compare | x | x | x | x | x | x | +|shift | | | x | x | x | x | +|dotprod | | | | x | | x | +|dotprod_fast | | | | x | | x | +|dotprod_expand | x | x | x | x | | x | +|dotprod_expand_fast| x | x | x | x | | x | +|logical | x | x | x | x | x | x | +|min, max | x | x | x | x | x | x | +|absdiff | x | x | x | x | x | x | +|absdiffs | | x | | x | | | +|reduce | x | x | x | x | x | x | +|mask | x | x | x | x | x | x | +|pack | x | x | x | x | x | x | +|pack_u | x | | x | | | | +|pack_b | x | | | | | | +|unpack | x | x | x | x | x | x | +|extract | x | x | x | x | x | x | +|rotate (lanes) | x | x | x | x | x | x | +|cvt_flt32 | | | | | | x | +|cvt_flt64 | | | | | | x | +|transpose4x4 | | | | | x | x | +|reverse | x | x | x | x | x | x | +|extract_n | x | x | x | x | x | x | +|broadcast_element | | | | | x | x | + +Big integers: + +| Operations\\Types | uint 64x2 | int 64x2 | +|-------------------|:-:|:-:| +|load, store | x | x | +|add, sub | x | x | +|shift | x | x | +|logical | x | x | +|reverse | x | x | +|extract | x | x | +|rotate (lanes) | x | x | +|cvt_flt64 | | x | +|extract_n | x | x | + +Floating point: + +| Operations\\Types | float 32x4 | float 64x2 | +|-------------------|:-:|:-:| +|load, store | x | x | +|interleave | x | | +|add, sub | x | x | +|mul | x | x | +|div | x | x | +|compare | x | x | +|min, max | x | x | +|absdiff | x | x | +|reduce | x | | +|mask | x | x | +|unpack | x | x | +|cvt_flt32 | | x | +|cvt_flt64 | x | | +|sqrt, abs | x | x | +|float math | x | x | +|transpose4x4 | x | | +|extract | x | x | +|rotate (lanes) | x | x | +|reverse | x | x | +|extract_n | x | x | +|broadcast_element | x | | + + @{ */ + +template struct v_reg +{ +//! @cond IGNORED + typedef _Tp lane_type; + enum { nlanes = n }; +// !@endcond + + /** @brief Constructor + + Initializes register with data from memory + @param ptr pointer to memory block with data for register */ + explicit v_reg(const _Tp* ptr) { for( int i = 0; i < n; i++ ) s[i] = ptr[i]; } + + /** @brief Constructor + + Initializes register with two 64-bit values */ + v_reg(_Tp s0, _Tp s1) { s[0] = s0; s[1] = s1; } + + /** @brief Constructor + + Initializes register with four 32-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3) { s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; } + + /** @brief Constructor + + Initializes register with eight 16-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + } + + /** @brief Constructor + + Initializes register with sixteen 8-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7, + _Tp s8, _Tp s9, _Tp s10, _Tp s11, + _Tp s12, _Tp s13, _Tp s14, _Tp s15) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + s[8] = s8; s[9] = s9; s[10] = s10; s[11] = s11; + s[12] = s12; s[13] = s13; s[14] = s14; s[15] = s15; + } + + /** @brief Default constructor + + Does not initialize anything*/ + v_reg() {} + + /** @brief Copy constructor */ + v_reg(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + } + /** @brief Access first value + + Returns value of the first lane according to register type, for example: + @code{.cpp} + v_int32x4 r(1, 2, 3, 4); + int v = r.get0(); // returns 1 + v_uint64x2 r(1, 2); + uint64_t v = r.get0(); // returns 1 + @endcode + */ + _Tp get0() const { return s[0]; } + +//! @cond IGNORED + _Tp get(const int i) const { return s[i]; } + v_reg<_Tp, n> high() const + { + v_reg<_Tp, n> c; + int i; + for( i = 0; i < n/2; i++ ) + { + c.s[i] = s[i+(n/2)]; + c.s[i+(n/2)] = 0; + } + return c; + } + + static v_reg<_Tp, n> zero() + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = (_Tp)0; + return c; + } + + static v_reg<_Tp, n> all(_Tp s) + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = s; + return c; + } + + template v_reg<_Tp2, n2> reinterpret_as() const + { + size_t bytes = std::min(sizeof(_Tp2)*n2, sizeof(_Tp)*n); + v_reg<_Tp2, n2> c; + std::memcpy(&c.s[0], &s[0], bytes); + return c; + } + + v_reg& operator=(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + return *this; + } + + _Tp s[n]; +//! @endcond +}; + +/** @brief Sixteen 8-bit unsigned integer values */ +typedef v_reg v_uint8x16; +/** @brief Sixteen 8-bit signed integer values */ +typedef v_reg v_int8x16; +/** @brief Eight 16-bit unsigned integer values */ +typedef v_reg v_uint16x8; +/** @brief Eight 16-bit signed integer values */ +typedef v_reg v_int16x8; +/** @brief Four 32-bit unsigned integer values */ +typedef v_reg v_uint32x4; +/** @brief Four 32-bit signed integer values */ +typedef v_reg v_int32x4; +/** @brief Four 32-bit floating point values (single precision) */ +typedef v_reg v_float32x4; +/** @brief Two 64-bit floating point values (double precision) */ +typedef v_reg v_float64x2; +/** @brief Two 64-bit unsigned integer values */ +typedef v_reg v_uint64x2; +/** @brief Two 64-bit signed integer values */ +typedef v_reg v_int64x2; + +/** @brief Add values + +For all types. */ +template CV_INLINE v_reg<_Tp, n> operator+(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator+=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Subtract values + +For all types. */ +template CV_INLINE v_reg<_Tp, n> operator-(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator-=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Multiply values + +For 16- and 32-bit integer types and floating types. */ +template CV_INLINE v_reg<_Tp, n> operator*(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator*=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Divide values + +For floating types only. */ +template CV_INLINE v_reg<_Tp, n> operator/(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator/=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + + +/** @brief Bitwise AND + +Only for integer types. */ +template CV_INLINE v_reg<_Tp, n> operator&(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator&=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise OR + +Only for integer types. */ +template CV_INLINE v_reg<_Tp, n> operator|(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator|=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise XOR + +Only for integer types.*/ +template CV_INLINE v_reg<_Tp, n> operator^(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator^=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise NOT + +Only for integer types.*/ +template CV_INLINE v_reg<_Tp, n> operator~(const v_reg<_Tp, n>& a); + + +#ifndef CV_DOXYGEN + +#define CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(macro_name, ...) \ +__CV_EXPAND(macro_name(uchar, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(schar, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(ushort, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(short, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(unsigned, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(int, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(uint64, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(int64, __VA_ARGS__)) \ + +#define CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(macro_name, ...) \ +__CV_EXPAND(macro_name(float, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(double, __VA_ARGS__)) \ + +#define CV__HAL_INTRIN_EXPAND_WITH_ALL_TYPES(macro_name, ...) \ +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(macro_name, __VA_ARGS__) \ +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(macro_name, __VA_ARGS__) \ + +#define CV__HAL_INTRIN_IMPL_BIN_OP_(_Tp, bin_op) \ +template inline \ +v_reg<_Tp, n> operator bin_op (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return c; \ +} \ +template inline \ +v_reg<_Tp, n>& operator bin_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return a; \ +} + +#define CV__HAL_INTRIN_IMPL_BIN_OP(bin_op) CV__HAL_INTRIN_EXPAND_WITH_ALL_TYPES(CV__HAL_INTRIN_IMPL_BIN_OP_, bin_op) + +CV__HAL_INTRIN_IMPL_BIN_OP(+) +CV__HAL_INTRIN_IMPL_BIN_OP(-) +CV__HAL_INTRIN_IMPL_BIN_OP(*) +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(CV__HAL_INTRIN_IMPL_BIN_OP_, /) + +#define CV__HAL_INTRIN_IMPL_BIT_OP_(_Tp, bit_op) \ +template CV_INLINE \ +v_reg<_Tp, n> operator bit_op (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return c; \ +} \ +template CV_INLINE \ +v_reg<_Tp, n>& operator bit_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return a; \ +} + +#define CV__HAL_INTRIN_IMPL_BIT_OP(bit_op) \ +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(CV__HAL_INTRIN_IMPL_BIT_OP_, bit_op) \ +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(CV__HAL_INTRIN_IMPL_BIT_OP_, bit_op) /* TODO: FIXIT remove this after masks refactoring */ + + +CV__HAL_INTRIN_IMPL_BIT_OP(&) +CV__HAL_INTRIN_IMPL_BIT_OP(|) +CV__HAL_INTRIN_IMPL_BIT_OP(^) + +#define CV__HAL_INTRIN_IMPL_BITWISE_NOT_(_Tp, dummy) \ +template CV_INLINE \ +v_reg<_Tp, n> operator ~ (const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int(~V_TypeTraits<_Tp>::reinterpret_int(a.s[i])); \ + return c; \ +} \ + +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(CV__HAL_INTRIN_IMPL_BITWISE_NOT_, ~) + +#endif // !CV_DOXYGEN + + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_MATH_FUNC(func, cfunc, _Tp2) \ +template inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp2, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i]); \ + return c; \ +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_MATH_FUNC_FLOAT(func, cfunc) \ +inline v_reg func(const v_reg& a) \ +{ \ + v_reg c; \ + for( int i = 0; i < 4; i++ ) \ + c.s[i] = cfunc(a.s[i]); \ + return c; \ +} \ +inline v_reg func(const v_reg& a) \ +{ \ + v_reg c; \ + for( int i = 0; i < 2; i++ ) \ + { \ + c.s[i] = cfunc(a.s[i]); \ + c.s[i + 2] = 0; \ + } \ + return c; \ +} + +/** @brief Square root of elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC(v_sqrt, std::sqrt, _Tp) + +//! @cond IGNORED +OPENCV_HAL_IMPL_MATH_FUNC(v_sin, std::sin, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_cos, std::cos, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_exp, std::exp, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_log, std::log, _Tp) +//! @endcond + +/** @brief Absolute value of elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC(v_abs, (typename V_TypeTraits<_Tp>::abs_type)std::abs, + typename V_TypeTraits<_Tp>::abs_type) + +/** @brief Round elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC_FLOAT(v_round, cvRound) + +/** @brief Floor elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC_FLOAT(v_floor, cvFloor) + +/** @brief Ceil elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC_FLOAT(v_ceil, cvCeil) + +/** @brief Truncate elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC_FLOAT(v_trunc, int) + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_MINMAX_FUNC(func, cfunc) \ +template inline v_reg<_Tp, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i], b.s[i]); \ + return c; \ +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(func, cfunc) \ +template inline _Tp func(const v_reg<_Tp, n>& a) \ +{ \ + _Tp c = a.s[0]; \ + for( int i = 1; i < n; i++ ) \ + c = cfunc(c, a.s[i]); \ + return c; \ +} + +/** @brief Choose min values for each pair + +Scheme: +@code +{A1 A2 ...} +{B1 B2 ...} +-------------- +{min(A1,B1) min(A2,B2) ...} +@endcode +For all types except 64-bit integer. */ +OPENCV_HAL_IMPL_MINMAX_FUNC(v_min, std::min) + +/** @brief Choose max values for each pair + +Scheme: +@code +{A1 A2 ...} +{B1 B2 ...} +-------------- +{max(A1,B1) max(A2,B2) ...} +@endcode +For all types except 64-bit integer. */ +OPENCV_HAL_IMPL_MINMAX_FUNC(v_max, std::max) + +/** @brief Find one min value + +Scheme: +@code +{A1 A2 A3 ...} => min(A1,A2,A3,...) +@endcode +For all types except 64-bit integer and 64-bit floating point types. */ +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_min, std::min) + +/** @brief Find one max value + +Scheme: +@code +{A1 A2 A3 ...} => max(A1,A2,A3,...) +@endcode +For all types except 64-bit integer and 64-bit floating point types. */ +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_max, std::max) + +static const unsigned char popCountTable[] = +{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; +/** @brief Count the 1 bits in the vector lanes and return result as corresponding unsigned type + +Scheme: +@code +{A1 A2 A3 ...} => {popcount(A1), popcount(A2), popcount(A3), ...} +@endcode +For all integer types. */ +template +inline v_reg::abs_type, n> v_popcount(const v_reg<_Tp, n>& a) +{ + v_reg::abs_type, n> b = v_reg::abs_type, n>::zero(); + for (int i = 0; i < n*(int)sizeof(_Tp); i++) + b.s[i/sizeof(_Tp)] += popCountTable[v_reinterpret_as_u8(a).s[i]]; + return b; +} + + +//! @cond IGNORED +template +inline void v_minmax( const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& minval, v_reg<_Tp, n>& maxval ) +{ + for( int i = 0; i < n; i++ ) + { + minval.s[i] = std::min(a.s[i], b.s[i]); + maxval.s[i] = std::max(a.s[i], b.s[i]); + } +} +//! @endcond + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_CMP_OP(cmp_op) \ +template \ +inline v_reg<_Tp, n> operator cmp_op(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)-(int)(a.s[i] cmp_op b.s[i])); \ + return c; \ +} + +/** @brief Less-than comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(<) + +/** @brief Greater-than comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(>) + +/** @brief Less-than or equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(<=) + +/** @brief Greater-than or equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(>=) + +/** @brief Equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(==) + +/** @brief Not equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(!=) + +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_ARITHM_OP(func, bin_op, cast_op, _Tp2) \ +template \ +inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef _Tp2 rtype; \ + v_reg c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cast_op(a.s[i] bin_op b.s[i]); \ + return c; \ +} + +/** @brief Add values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_add_wrap, +, (_Tp), _Tp) + +/** @brief Subtract values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_sub_wrap, -, (_Tp), _Tp) + +/** @brief Multiply values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_mul_wrap, *, (_Tp), _Tp) + +//! @cond IGNORED +template inline T _absdiff(T a, T b) +{ + return a > b ? a - b : b - a; +} +//! @endcond + +/** @brief Absolute difference + +Returns \f$ |a - b| \f$ converted to corresponding unsigned type. +Example: +@code{.cpp} +v_int32x4 a, b; // {1, 2, 3, 4} and {4, 3, 2, 1} +v_uint32x4 c = v_absdiff(a, b); // result is {3, 1, 1, 3} +@endcode +For 8-, 16-, 32-bit integer source types. */ +template +inline v_reg::abs_type, n> v_absdiff(const v_reg<_Tp, n>& a, const v_reg<_Tp, n> & b) +{ + typedef typename V_TypeTraits<_Tp>::abs_type rtype; + v_reg c; + const rtype mask = (rtype)(std::numeric_limits<_Tp>::is_signed ? (1 << (sizeof(rtype)*8 - 1)) : 0); + for( int i = 0; i < n; i++ ) + { + rtype ua = a.s[i] ^ mask; + rtype ub = b.s[i] ^ mask; + c.s[i] = _absdiff(ua, ub); + } + return c; +} + +/** @overload + +For 32-bit floating point values */ +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @overload + +For 64-bit floating point values */ +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @brief Saturating absolute difference + +Returns \f$ saturate(|a - b|) \f$ . +For 8-, 16-bit signed integer source types. */ +template +inline v_reg<_Tp, n> v_absdiffs(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++) + c.s[i] = saturate_cast<_Tp>(std::abs(a.s[i] - b.s[i])); + return c; +} + +/** @brief Inversed square root + +Returns \f$ 1/sqrt(a) \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_invsqrt(const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = 1.f/std::sqrt(a.s[i]); + return c; +} + +/** @brief Magnitude + +Returns \f$ sqrt(a^2 + b^2) \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = std::sqrt(a.s[i]*a.s[i] + b.s[i]*b.s[i]); + return c; +} + +/** @brief Square of the magnitude + +Returns \f$ a^2 + b^2 \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_sqr_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = a.s[i]*a.s[i] + b.s[i]*b.s[i]; + return c; +} + +/** @brief Multiply and add + + Returns \f$ a*b + c \f$ + For floating point types and signed 32bit int only. */ +template +inline v_reg<_Tp, n> v_fma(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + v_reg<_Tp, n> d; + for( int i = 0; i < n; i++ ) + d.s[i] = a.s[i]*b.s[i] + c.s[i]; + return d; +} + +/** @brief A synonym for v_fma */ +template +inline v_reg<_Tp, n> v_muladd(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + return v_fma(a, b, c); +} + +/** @brief Dot product of elements + +Multiply values in two registers and sum adjacent result pairs. + +Scheme: +@code + {A1 A2 ...} // 16-bit +x {B1 B2 ...} // 16-bit +------------- +{A1B1+A2B2 ...} // 32-bit + +@endcode +*/ +template inline v_reg::w_type, n/2> +v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1]; + return c; +} + +/** @brief Dot product of elements + +Same as cv::v_dotprod, but add a third element to the sum of adjacent pairs. +Scheme: +@code + {A1 A2 ...} // 16-bit +x {B1 B2 ...} // 16-bit +------------- + {A1B1+A2B2+C1 ...} // 32-bit +@endcode +*/ +template inline v_reg::w_type, n/2> +v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::w_type, n / 2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg s; + for( int i = 0; i < (n/2); i++ ) + s.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1] + c.s[i]; + return s; +} + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod, but it may perform unorder sum between result pairs in some platforms, +this intrinsic can be used if the sum among all lanes is only matters +and also it should be yielding better performance on the affected platforms. + +*/ +template inline v_reg::w_type, n/2> +v_dotprod_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ return v_dotprod(a, b); } + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod_fast, but add a third element to the sum of adjacent pairs. +*/ +template inline v_reg::w_type, n/2> +v_dotprod_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::w_type, n / 2>& c) +{ return v_dotprod(a, b, c); } + +/** @brief Dot product of elements and expand + +Multiply values in two registers and expand the sum of adjacent result pairs. + +Scheme: +@code + {A1 A2 A3 A4 ...} // 8-bit +x {B1 B2 B3 B4 ...} // 8-bit +------------- + {A1B1+A2B2+A3B3+A4B4 ...} // 32-bit + +@endcode +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3]; + return s; +} + +/** @brief Dot product of elements + +Same as cv::v_dotprod_expand, but add a third element to the sum of adjacent pairs. +Scheme: +@code + {A1 A2 A3 A4 ...} // 8-bit +x {B1 B2 B3 B4 ...} // 8-bit +------------- + {A1B1+A2B2+A3B3+A4B4+C1 ...} // 32-bit +@endcode +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::q_type, n / 4>& c) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3] + c.s[i]; + return s; +} + +/** @brief Fast Dot product of elements and expand + +Multiply values in two registers and expand the sum of adjacent result pairs. + +Same as cv::v_dotprod_expand, but it may perform unorder sum between result pairs in some platforms, +this intrinsic can be used if the sum among all lanes is only matters +and also it should be yielding better performance on the affected platforms. + +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ return v_dotprod_expand(a, b); } + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod_expand_fast, but add a third element to the sum of adjacent pairs. +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::q_type, n / 4>& c) +{ return v_dotprod_expand(a, b, c); } + +/** @brief Multiply and expand + +Multiply values two registers and store results in two registers with wider pack type. +Scheme: +@code + {A B C D} // 32-bit +x {E F G H} // 32-bit +--------------- +{AE BF} // 64-bit + {CG DH} // 64-bit +@endcode +Example: +@code{.cpp} +v_uint32x4 a, b; // {1,2,3,4} and {2,2,2,2} +v_uint64x2 c, d; // results +v_mul_expand(a, b, c, d); // c, d = {2,4}, {6, 8} +@endcode +Implemented only for 16- and unsigned 32-bit source types (v_int16x8, v_uint16x8, v_uint32x4). +*/ +template inline void v_mul_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg::w_type, n/2>& c, + v_reg::w_type, n/2>& d) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i]*b.s[i]; + d.s[i] = (w_type)a.s[i+(n/2)]*b.s[i+(n/2)]; + } +} + +/** @brief Multiply and extract high part + +Multiply values two registers and store high part of the results. +Implemented only for 16-bit source types (v_int16x8, v_uint16x8). Returns \f$ a*b >> 16 \f$ +*/ +template inline v_reg<_Tp, n> v_mul_hi(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg<_Tp, n> c; + for (int i = 0; i < n; i++) + c.s[i] = (_Tp)(((w_type)a.s[i] * b.s[i]) >> sizeof(_Tp)*8); + return c; +} + +//! @cond IGNORED +template inline void v_hsum(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i*2] + a.s[i*2+1]; + } +} +//! @endcond + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_SHIFT_OP(shift_op) \ +template inline v_reg<_Tp, n> operator shift_op(const v_reg<_Tp, n>& a, int imm) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = (_Tp)(a.s[i] shift_op imm); \ + return c; \ +} + +/** @brief Bitwise shift left + +For 16-, 32- and 64-bit integer values. */ +OPENCV_HAL_IMPL_SHIFT_OP(<< ) + +/** @brief Bitwise shift right + +For 16-, 32- and 64-bit integer values. */ +OPENCV_HAL_IMPL_SHIFT_OP(>> ) + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(suffix,opA,opB) \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> b; \ + for (int i = 0; i < n; i++) \ + { \ + int sIndex = i opA imm; \ + if (0 <= sIndex && sIndex < n) \ + { \ + b.s[i] = a.s[sIndex]; \ + } \ + else \ + { \ + b.s[i] = 0; \ + } \ + } \ + return b; \ +} \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for (int i = 0; i < n; i++) \ + { \ + int aIndex = i opA imm; \ + int bIndex = i opA imm opB n; \ + if (0 <= bIndex && bIndex < n) \ + { \ + c.s[i] = b.s[bIndex]; \ + } \ + else if (0 <= aIndex && aIndex < n) \ + { \ + c.s[i] = a.s[aIndex]; \ + } \ + else \ + { \ + c.s[i] = 0; \ + } \ + } \ + return c; \ +} + +/** @brief Element shift left among vector + +For all type */ +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(left, -, +) + +/** @brief Element shift right among vector + +For all type */ +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(right, +, -) + +/** @brief Sum packed values + +Scheme: +@code +{A1 A2 A3 ...} => sum{A1,A2,A3,...} +@endcode +*/ +template inline typename V_TypeTraits<_Tp>::sum_type v_reduce_sum(const v_reg<_Tp, n>& a) +{ + typename V_TypeTraits<_Tp>::sum_type c = a.s[0]; + for( int i = 1; i < n; i++ ) + c += a.s[i]; + return c; +} + +/** @brief Sums all elements of each input vector, returns the vector of sums + + Scheme: + @code + result[0] = a[0] + a[1] + a[2] + a[3] + result[1] = b[0] + b[1] + b[2] + b[3] + result[2] = c[0] + c[1] + c[2] + c[3] + result[3] = d[0] + d[1] + d[2] + d[3] + @endcode +*/ +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v_float32x4 r; + r.s[0] = a.s[0] + a.s[1] + a.s[2] + a.s[3]; + r.s[1] = b.s[0] + b.s[1] + b.s[2] + b.s[3]; + r.s[2] = c.s[0] + c.s[1] + c.s[2] + c.s[3]; + r.s[3] = d.s[0] + d.s[1] + d.s[2] + d.s[3]; + return r; +} + +/** @brief Sum absolute differences of values + +Scheme: +@code +{A1 A2 A3 ...} {B1 B2 B3 ...} => sum{ABS(A1-B1),abs(A2-B2),abs(A3-B3),...} +@endcode +For all types except 64-bit types.*/ +template inline typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type v_reduce_sad(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type c = _absdiff(a.s[0], b.s[0]); + for (int i = 1; i < n; i++) + c += _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @brief Get negative values mask +@deprecated v_signmask depends on a lane count heavily and therefore isn't universal enough + +Returned value is a bit mask with bits set to 1 on places corresponding to negative packed values indexes. +Example: +@code{.cpp} +v_int32x4 r; // set to {-1, -1, 1, 1} +int mask = v_signmask(r); // mask = 3 <== 00000000 00000000 00000000 00000011 +@endcode +*/ +template inline int v_signmask(const v_reg<_Tp, n>& a) +{ + int mask = 0; + for( int i = 0; i < n; i++ ) + mask |= (V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0) << i; + return mask; +} + +/** @brief Get first negative lane index + +Returned value is an index of first negative lane (undefined for input of all positive values) +Example: +@code{.cpp} +v_int32x4 r; // set to {0, 0, -1, -1} +int idx = v_heading_zeros(r); // idx = 2 +@endcode +*/ +template inline int v_scan_forward(const v_reg<_Tp, n>& a) +{ + for (int i = 0; i < n; i++) + if(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0) + return i; + return 0; +} + +/** @brief Check if all packed values are less than zero + +Unsigned values will be casted to signed: `uchar 254 => char -2`. +*/ +template inline bool v_check_all(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) >= 0 ) + return false; + return true; +} + +/** @brief Check if any of packed values is less than zero + +Unsigned values will be casted to signed: `uchar 254 => char -2`. +*/ +template inline bool v_check_any(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0 ) + return true; + return false; +} + +/** @brief Per-element select (blend operation) + +Return value will be built by combining values _a_ and _b_ using the following scheme: + result[i] = mask[i] ? a[i] : b[i]; + +@note: _mask_ element values are restricted to these values: +- 0: select element from _b_ +- 0xff/0xffff/etc: select element from _a_ +(fully compatible with bitwise-based operator) +*/ +template inline v_reg<_Tp, n> v_select(const v_reg<_Tp, n>& mask, + const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef V_TypeTraits<_Tp> Traits; + typedef typename Traits::int_type int_type; + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + { + int_type m = Traits::reinterpret_int(mask.s[i]); + CV_DbgAssert(m == 0 || m == (~(int_type)0)); // restrict mask values: 0 or 0xff/0xffff/etc + c.s[i] = m ? a.s[i] : b.s[i]; + } + return c; +} + +/** @brief Expand values to the wider pack type + +Copy contents of register to two registers with 2x wider pack type. +Scheme: +@code + int32x4 int64x2 int64x2 +{A B C D} ==> {A B} , {C D} +@endcode */ +template inline void v_expand(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& b0, + v_reg::w_type, n/2>& b1) +{ + for( int i = 0; i < (n/2); i++ ) + { + b0.s[i] = a.s[i]; + b1.s[i] = a.s[i+(n/2)]; + } +} + +/** @brief Expand lower values to the wider pack type + +Same as cv::v_expand, but return lower half of the vector. + +Scheme: +@code + int32x4 int64x2 +{A B C D} ==> {A B} +@endcode */ +template +inline v_reg::w_type, n/2> +v_expand_low(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i]; + return b; +} + +/** @brief Expand higher values to the wider pack type + +Same as cv::v_expand_low, but expand higher half of the vector instead. + +Scheme: +@code + int32x4 int64x2 +{A B C D} ==> {C D} +@endcode */ +template +inline v_reg::w_type, n/2> +v_expand_high(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i+(n/2)]; + return b; +} + +//! @cond IGNORED +template inline v_reg::int_type, n> + v_reinterpret_as_int(const v_reg<_Tp, n>& a) +{ + v_reg::int_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_int(a.s[i]); + return c; +} + +template inline v_reg::uint_type, n> + v_reinterpret_as_uint(const v_reg<_Tp, n>& a) +{ + v_reg::uint_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_uint(a.s[i]); + return c; +} +//! @endcond + +/** @brief Interleave two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A1 B1 A2 B2} and {A3 B3 A4 B4} +@endcode +For all types except 64-bit. +*/ +template inline void v_zip( const v_reg<_Tp, n>& a0, const v_reg<_Tp, n>& a1, + v_reg<_Tp, n>& b0, v_reg<_Tp, n>& b1 ) +{ + int i; + for( i = 0; i < n/2; i++ ) + { + b0.s[i*2] = a0.s[i]; + b0.s[i*2+1] = a1.s[i]; + } + for( ; i < n; i++ ) + { + b1.s[i*2-n] = a0.s[i]; + b1.s[i*2-n+1] = a1.s[i]; + } +} + +/** @brief Load register contents from memory + +@param ptr pointer to memory block with data +@return register object + +@note Returned type will be detected from passed pointer type, for example uchar ==> cv::v_uint8x16, int ==> cv::v_int32x4, etc. + +@note Alignment requirement: +if CV_STRONG_ALIGNMENT=1 then passed pointer must be aligned (`sizeof(lane type)` should be enough). +Do not cast pointer types without runtime check for pointer alignment (like `uchar*` => `int*`). + */ +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + return v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128>(ptr); +} + +/** @brief Load register contents from memory (aligned) + +similar to cv::v_load, but source memory block should be aligned (to 16-byte boundary in case of SIMD128, 32-byte - SIMD256, etc) + */ +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_aligned(const _Tp* ptr) +{ + CV_Assert(isAligned::nlanes128>)>(ptr)); + return v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128>(ptr); +} + +/** @brief Load 64-bits of data to lower part (high part is undefined). + +@param ptr memory block containing data for first half (0..n/2) + +@code{.cpp} +int lo[2] = { 1, 2 }; +v_int32x4 r = v_load_low(lo); +@endcode + */ +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_low(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +/** @brief Load register contents from two memory blocks + +@param loptr memory block containing data for first half (0..n/2) +@param hiptr memory block containing data for second half (n/2..n) + +@code{.cpp} +int lo[2] = { 1, 2 }, hi[2] = { 3, 4 }; +v_int32x4 r = v_load_halves(lo, hi); +@endcode + */ +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_halves(const _Tp* loptr, const _Tp* hiptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(loptr)); + CV_Assert(isAligned(hiptr)); +#endif + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = loptr[i]; + c.s[i+c.nlanes/2] = hiptr[i]; + } + return c; +} + +/** @brief Load register contents from memory with double expand + +Same as cv::v_load, but result pack type will be 2x wider than memory type. + +@code{.cpp} +short buf[4] = {1, 2, 3, 4}; // type is int16 +v_int32x4 r = v_load_expand(buf); // r = {1, 2, 3, 4} - type is int32 +@endcode +For 8-, 16-, 32-bit integer source types. */ +template +inline v_reg::w_type, V_TypeTraits<_Tp>::nlanes128 / 2> +v_load_expand(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg::nlanes128> c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +/** @brief Load register contents from memory with quad expand + +Same as cv::v_load_expand, but result type is 4 times wider than source. +@code{.cpp} +char buf[4] = {1, 2, 3, 4}; // type is int8 +v_int32x4 r = v_load_q(buf); // r = {1, 2, 3, 4} - type is int32 +@endcode +For 8-bit integer source types. */ +template +inline v_reg::q_type, V_TypeTraits<_Tp>::nlanes128 / 4> +v_load_expand_q(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg::nlanes128> c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +/** @brief Load and deinterleave (2 channels) + +Load data from memory deinterleave and store to 2 registers. +Scheme: +@code +{A1 B1 A2 B2 ...} ==> {A1 A2 ...}, {B1 B2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + a.s[i] = ptr[i2]; + b.s[i] = ptr[i2+1]; + } +} + +/** @brief Load and deinterleave (3 channels) + +Load data from memory deinterleave and store to 3 registers. +Scheme: +@code +{A1 B1 C1 A2 B2 C2 ...} ==> {A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + a.s[i] = ptr[i3]; + b.s[i] = ptr[i3+1]; + c.s[i] = ptr[i3+2]; + } +} + +/** @brief Load and deinterleave (4 channels) + +Load data from memory deinterleave and store to 4 registers. +Scheme: +@code +{A1 B1 C1 D1 A2 B2 C2 D2 ...} ==> {A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...}, {D1 D2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c, + v_reg<_Tp, n>& d) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + a.s[i] = ptr[i4]; + b.s[i] = ptr[i4+1]; + c.s[i] = ptr[i4+2]; + d.s[i] = ptr[i4+3]; + } +} + +/** @brief Interleave and store (2 channels) + +Interleave and store data from 2 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...} ==> {A1 B1 A2 B2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + ptr[i2] = a.s[i]; + ptr[i2+1] = b.s[i]; + } +} + +/** @brief Interleave and store (3 channels) + +Interleave and store data from 3 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...} ==> {A1 B1 C1 A2 B2 C2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + ptr[i3] = a.s[i]; + ptr[i3+1] = b.s[i]; + ptr[i3+2] = c.s[i]; + } +} + +/** @brief Interleave and store (4 channels) + +Interleave and store data from 4 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...}, {D1 D2 ...} ==> {A1 B1 C1 D1 A2 B2 C2 D2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + const v_reg<_Tp, n>& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + ptr[i4] = a.s[i]; + ptr[i4+1] = b.s[i]; + ptr[i4+2] = c.s[i]; + ptr[i4+3] = d.s[i]; + } +} + +/** @brief Store data to memory + +Store register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B C D} +@endcode +Pointer can be unaligned. */ +template +inline void v_store(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_store(ptr, a); +} + +/** @brief Store data to memory (lower half) + +Store lower half of register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B} +@endcode */ +template +inline void v_store_low(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i]; +} + +/** @brief Store data to memory (higher half) + +Store higher half of register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {C D} +@endcode */ +template +inline void v_store_high(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i+(n/2)]; +} + +/** @brief Store data to memory (aligned) + +Store register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B C D} +@endcode +Pointer __should__ be aligned by 16-byte boundary. */ +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +template +inline void v_store_aligned_nocache(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +/** @brief Combine vector from first elements of two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A1 A2 B1 B2} +@endcode +For all types except 64-bit. */ +template +inline v_reg<_Tp, n> v_combine_low(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i]; + c.s[i+(n/2)] = b.s[i]; + } + return c; +} + +/** @brief Combine vector from last elements of two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A3 A4 B3 B4} +@endcode +For all types except 64-bit. */ +template +inline v_reg<_Tp, n> v_combine_high(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i+(n/2)]; + c.s[i+(n/2)] = b.s[i+(n/2)]; + } + return c; +} + +/** @brief Combine two vectors from lower and higher parts of two other vectors + +@code{.cpp} +low = cv::v_combine_low(a, b); +high = cv::v_combine_high(a, b); +@endcode */ +template +inline void v_recombine(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& low, v_reg<_Tp, n>& high) +{ + for( int i = 0; i < (n/2); i++ ) + { + low.s[i] = a.s[i]; + low.s[i+(n/2)] = b.s[i]; + high.s[i] = a.s[i+(n/2)]; + high.s[i+(n/2)] = b.s[i+(n/2)]; + } +} + +/** @brief Vector reverse order + +Reverse the order of the vector +Scheme: +@code + REG {A1 ... An} ==> REG {An ... A1} +@endcode +For all types. */ +template +inline v_reg<_Tp, n> v_reverse(const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = a.s[n-i-1]; + return c; +} + +/** @brief Vector extract + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +======================== +shift = 1 {A2 A3 A4 B1} +shift = 2 {A3 A4 B1 B2} +shift = 3 {A4 B1 B2 B3} +@endcode +Restriction: 0 <= shift < nlanes + +Usage: +@code +v_int32x4 a, b, c; +c = v_extract<2>(a, b); +@endcode +For all types. */ +template +inline v_reg<_Tp, n> v_extract(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> r; + const int shift = n - s; + int i = 0; + for (; i < shift; ++i) + r.s[i] = a.s[i+s]; + for (; i < n; ++i) + r.s[i] = b.s[i-shift]; + return r; +} + +/** @brief Vector extract + +Scheme: +Return the s-th element of v. +Restriction: 0 <= s < nlanes + +Usage: +@code +v_int32x4 a; +int r; +r = v_extract_n<2>(a); +@endcode +For all types. */ +template +inline _Tp v_extract_n(const v_reg<_Tp, n>& v) +{ + CV_DbgAssert(s >= 0 && s < n); + return v.s[s]; +} + +/** @brief Broadcast i-th element of vector + +Scheme: +@code +{ v[0] v[1] v[2] ... v[SZ] } => { v[i], v[i], v[i] ... v[i] } +@endcode +Restriction: 0 <= i < nlanes +Supported types: 32-bit integers and floats (s32/u32/f32) + */ +template +inline v_reg<_Tp, n> v_broadcast_element(const v_reg<_Tp, n>& a) +{ + CV_DbgAssert(i >= 0 && i < n); + return v_reg<_Tp, n>::all(a.s[i]); +} + +/** @brief Round + +Rounds each value. Input type is float vector ==> output type is int vector.*/ +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvRound(a.s[i]); + return c; +} + +/** @overload */ +template inline v_reg v_round(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = cvRound(b.s[i]); + } + return c; +} + +/** @brief Floor + +Floor each value. Input type is float vector ==> output type is int vector.*/ +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvFloor(a.s[i]); + return c; +} + +/** @brief Ceil + +Ceil each value. Input type is float vector ==> output type is int vector.*/ +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvCeil(a.s[i]); + return c; +} + +/** @brief Trunc + +Truncate each value. Input type is float vector ==> output type is int vector.*/ +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (int)(a.s[i]); + return c; +} + +/** @overload */ +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvFloor(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvCeil(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvCeil(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @brief Convert to float + +Supported input type is cv::v_int32x4. */ +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (float)a.s[i]; + return c; +} + +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_cvt_f32(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = (float)b.s[i]; + } + return c; +} + +/** @brief Convert to double + +Supported input type is cv::v_int32x4. */ +CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +/** @brief Convert to double high part of vector + +Supported input type is cv::v_int32x4. */ +CV_INLINE v_reg v_cvt_f64_high(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i + 2]; + return c; +} + +/** @brief Convert to double + +Supported input type is cv::v_float32x4. */ +CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +/** @brief Convert to double high part of vector + +Supported input type is cv::v_float32x4. */ +CV_INLINE v_reg v_cvt_f64_high(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i + 2]; + return c; +} + +/** @brief Convert to double + +Supported input type is cv::v_int64x2. */ +CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +/** @brief Convert to double high part of vector + +Supported input type is cv::v_int64x2. */ +CV_INLINE v_reg v_cvt_f64_high(const v_reg& a) +{ + enum { n = 2 }; + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + + +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i]]; + return c; +} +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut_pairs(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i / 2] + i % 2]; + return c; +} +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut_quads(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i / 4] + i % 4]; + return c; +} + +template inline v_reg v_lut(const int* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const unsigned* tab, const v_reg& idx) +{ + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const float* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const double* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + return v_lut(tab, idxvec.s); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + return v_lut(tab, idxvec.s); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + return v_lut(tab, idxvec.s); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + return v_lut(tab, idxvec.s); +} + + +template inline void v_lut_deinterleave(const float* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline void v_lut_deinterleave(const double* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline v_reg<_Tp, n> v_interleave_pairs(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[4*i ] = vec.s[4*i ]; + c.s[4*i+1] = vec.s[4*i+2]; + c.s[4*i+2] = vec.s[4*i+1]; + c.s[4*i+3] = vec.s[4*i+3]; + } + return c; +} + +template inline v_reg<_Tp, n> v_interleave_quads(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/8; i++) + { + c.s[8*i ] = vec.s[8*i ]; + c.s[8*i+1] = vec.s[8*i+4]; + c.s[8*i+2] = vec.s[8*i+1]; + c.s[8*i+3] = vec.s[8*i+5]; + c.s[8*i+4] = vec.s[8*i+2]; + c.s[8*i+5] = vec.s[8*i+6]; + c.s[8*i+6] = vec.s[8*i+3]; + c.s[8*i+7] = vec.s[8*i+7]; + } + return c; +} + +template inline v_reg<_Tp, n> v_pack_triplets(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[3*i ] = vec.s[4*i ]; + c.s[3*i+1] = vec.s[4*i+1]; + c.s[3*i+2] = vec.s[4*i+2]; + } + return c; +} + +/** @brief Transpose 4x4 matrix + +Scheme: +@code +a0 {A1 A2 A3 A4} +a1 {B1 B2 B3 B4} +a2 {C1 C2 C3 C4} +a3 {D1 D2 D3 D4} +=============== +b0 {A1 B1 C1 D1} +b1 {A2 B2 C2 D2} +b2 {A3 B3 C3 D3} +b3 {A4 B4 C4 D4} +@endcode +*/ +template +inline void v_transpose4x4( v_reg<_Tp, 4>& a0, const v_reg<_Tp, 4>& a1, + const v_reg<_Tp, 4>& a2, const v_reg<_Tp, 4>& a3, + v_reg<_Tp, 4>& b0, v_reg<_Tp, 4>& b1, + v_reg<_Tp, 4>& b2, v_reg<_Tp, 4>& b3 ) +{ + b0 = v_reg<_Tp, 4>(a0.s[0], a1.s[0], a2.s[0], a3.s[0]); + b1 = v_reg<_Tp, 4>(a0.s[1], a1.s[1], a2.s[1], a3.s[1]); + b2 = v_reg<_Tp, 4>(a0.s[2], a1.s[2], a2.s[2], a3.s[2]); + b3 = v_reg<_Tp, 4>(a0.s[3], a1.s[3], a2.s[3], a3.s[3]); +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_INIT_ZERO(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec::zero(); } + +//! @name Init with zero +//! @{ +//! @brief Create new vector with zero elements +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int64x2, int64, s64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_INIT_VAL(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_setall_##suffix(_Tp val) { return _Tpvec::all(val); } + +//! @name Init with value +//! @{ +//! @brief Create new vector with elements set to a specific value +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int64x2, int64, s64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_REINTERPRET(_Tpvec, _Tp, suffix) \ +template inline _Tpvec \ + v_reinterpret_as_##suffix(const v_reg<_Tp0, n0>& a) \ +{ return a.template reinterpret_as<_Tp, _Tpvec::nlanes>(); } + +//! @name Reinterpret +//! @{ +//! @brief Convert vector to different type without modifying underlying data. +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int64x2, int64, s64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_SHIFTL(_Tpvec, _Tp) \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return a << n; } + +//! @name Left shift +//! @{ +//! @brief Shift left +OPENCV_HAL_IMPL_C_SHIFTL(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_SHIFTL(v_int16x8, short) +OPENCV_HAL_IMPL_C_SHIFTL(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_SHIFTL(v_int32x4, int) +OPENCV_HAL_IMPL_C_SHIFTL(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_SHIFTL(v_int64x2, int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_SHIFTR(_Tpvec, _Tp) \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return a >> n; } + +//! @name Right shift +//! @{ +//! @brief Shift right +OPENCV_HAL_IMPL_C_SHIFTR(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_SHIFTR(v_int16x8, short) +OPENCV_HAL_IMPL_C_SHIFTR(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_SHIFTR(v_int32x4, int) +OPENCV_HAL_IMPL_C_SHIFTR(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_SHIFTR(v_int64x2, int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHIFTR(_Tpvec, _Tp) \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ \ + _Tpvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + c.s[i] = (_Tp)((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + return c; \ +} + +//! @name Rounding shift +//! @{ +//! @brief Rounding shift right +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int16x8, short) +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int32x4, int) +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int64x2, int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_PACK(_Tpvec, _Tpnvec, _Tpn, pack_suffix, cast) \ +inline _Tpnvec v_##pack_suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpnvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + { \ + c.s[i] = cast<_Tpn>(a.s[i]); \ + c.s[i+_Tpvec::nlanes] = cast<_Tpn>(b.s[i]); \ + } \ + return c; \ +} + +//! @name Pack +//! @{ +//! @brief Pack values from two vectors to one +//! +//! Return vector type have twice more elements than input vector types. Variant with _u_ suffix also +//! converts to corresponding unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_PACK(v_uint16x8, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int16x8, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_uint32x4, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int32x4, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_uint64x2, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(v_int64x2, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(v_int16x8, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int32x4, v_uint16x8, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHR_PACK(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +template inline _Tpnvec v_rshr_##pack_suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpnvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + { \ + c.s[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + c.s[i+_Tpvec::nlanes] = cast<_Tpn>((b.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + } \ + return c; \ +} + +//! @name Pack with rounding shift +//! @{ +//! @brief Pack values from two vectors to one with rounding shift +//! +//! Values from the input vectors will be shifted right by _n_ bits with rounding, converted to narrower +//! type and returned in the result vector. Variant with _u_ suffix converts to unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_PACK_STORE(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +inline void v_##pack_suffix##_store(_Tpn* ptr, const _Tpvec& a) \ +{ \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + ptr[i] = cast<_Tpn>(a.s[i]); \ +} + +//! @name Pack and store +//! @{ +//! @brief Store values from the input vector into memory with pack +//! +//! Values will be stored into memory with conversion to narrower type. +//! Variant with _u_ suffix converts to corresponding unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +template inline void v_rshr_##pack_suffix##_store(_Tpn* ptr, const _Tpvec& a) \ +{ \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + ptr[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ +} + +//! @name Pack and store with rounding shift +//! @{ +//! @brief Store values from the input vector into memory with pack +//! +//! Values will be shifted _n_ bits right with rounding, converted to narrower type and stored into +//! memory. Variant with _u_ suffix converts to unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) +//! @} + +//! @cond IGNORED +template +inline void _pack_b(_Tpm* mptr, const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + for (int i = 0; i < n; ++i) + { + mptr[i] = (_Tpm)a.s[i]; + mptr[i + n] = (_Tpm)b.s[i]; + } +} +//! @endcond + +//! @name Pack boolean values +//! @{ +//! @brief Pack boolean values from multiple vectors to one unsigned 8-bit integer vector +//! +//! @note Must provide valid boolean values to guarantee same result for all architectures. + +/** @brief +//! For 16-bit boolean values + +Scheme: +@code +a {0xFFFF 0 0 0xFFFF 0 0xFFFF 0xFFFF 0} +b {0xFFFF 0 0xFFFF 0 0 0xFFFF 0 0xFFFF} +=============== +{ + 0xFF 0 0 0xFF 0 0xFF 0xFF 0 + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ + +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + return mask; +} + +/** @overload +For 32-bit boolean values + +Scheme: +@code +a {0xFFFF.. 0 0 0xFFFF..} +b {0 0xFFFF.. 0xFFFF.. 0} +c {0xFFFF.. 0 0xFFFF.. 0} +d {0 0xFFFF.. 0 0xFFFF..} +=============== +{ + 0xFF 0 0 0xFF 0 0xFF 0xFF 0 + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 8, c, d); + return mask; +} + +/** @overload +For 64-bit boolean values + +Scheme: +@code +a {0xFFFF.. 0} +b {0 0xFFFF..} +c {0xFFFF.. 0} +d {0 0xFFFF..} + +e {0xFFFF.. 0} +f {0xFFFF.. 0} +g {0 0xFFFF..} +h {0 0xFFFF..} +=============== +{ + 0xFF 0 0 0xFF 0xFF 0 0 0xFF + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 4, c, d); + _pack_b(mask.s + 8, e, f); + _pack_b(mask.s + 12, g, h); + return mask; +} +//! @} + +/** @brief Matrix multiplication + +Scheme: +@code +{A0 A1 A2 A3} |V0| +{B0 B1 B2 B3} |V1| +{C0 C1 C2 C3} |V2| +{D0 D1 D2 D3} x |V3| +==================== +{R0 R1 R2 R3}, where: +R0 = A0V0 + A1V1 + A2V2 + A3V3, +R1 = B0V0 + B1V1 + B2V2 + B3V3 +... +@endcode +*/ +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + return v_float32x4(v.s[0]*m0.s[0] + v.s[1]*m1.s[0] + v.s[2]*m2.s[0] + v.s[3]*m3.s[0], + v.s[0]*m0.s[1] + v.s[1]*m1.s[1] + v.s[2]*m2.s[1] + v.s[3]*m3.s[1], + v.s[0]*m0.s[2] + v.s[1]*m1.s[2] + v.s[2]*m2.s[2] + v.s[3]*m3.s[2], + v.s[0]*m0.s[3] + v.s[1]*m1.s[3] + v.s[2]*m2.s[3] + v.s[3]*m3.s[3]); +} + +/** @brief Matrix multiplication and add + +Scheme: +@code +{A0 A1 A2 } |V0| |D0| +{B0 B1 B2 } |V1| |D1| +{C0 C1 C2 } x |V2| + |D2| +==================== +{R0 R1 R2 R3}, where: +R0 = A0V0 + A1V1 + A2V2 + D0, +R1 = B0V0 + B1V1 + B2V2 + D1 +... +@endcode +*/ +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + return v_float32x4(v.s[0]*m0.s[0] + v.s[1]*m1.s[0] + v.s[2]*m2.s[0] + m3.s[0], + v.s[0]*m0.s[1] + v.s[1]*m1.s[1] + v.s[2]*m2.s[1] + m3.s[1], + v.s[0]*m0.s[2] + v.s[1]*m1.s[2] + v.s[2]*m2.s[2] + m3.s[2], + v.s[0]*m0.s[3] + v.s[1]*m1.s[3] + v.s[2]*m2.s[3] + m3.s[3]); +} + + +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_cvt_f64_high(a) * v_cvt_f64_high(b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_fma(v_cvt_f64_high(a), v_cvt_f64_high(b), c)); } + +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +////// FP16 support /////// + +inline v_reg::nlanes128> +v_load_expand(const float16_t* ptr) +{ + v_reg::nlanes128> v; + for( int i = 0; i < v.nlanes; i++ ) + { + v.s[i] = ptr[i]; + } + return v; +} + +inline void +v_pack_store(float16_t* ptr, const v_reg::nlanes128>& v) +{ + for( int i = 0; i < v.nlanes; i++ ) + { + ptr[i] = float16_t(v.s[i]); + } +} + +inline void v_cleanup() {} + +//! @} + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +#endif +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_forward.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_forward.hpp new file mode 100644 index 0000000..979f15a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_forward.hpp @@ -0,0 +1,191 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef CV__SIMD_FORWARD +#error "Need to pre-define forward width" +#endif + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +/** Types **/ +#if CV__SIMD_FORWARD == 1024 +// [todo] 1024 +#error "1024-long ops not implemented yet" +#elif CV__SIMD_FORWARD == 512 +// 512 +#define __CV_VX(fun) v512_##fun +#define __CV_V_UINT8 v_uint8x64 +#define __CV_V_INT8 v_int8x64 +#define __CV_V_UINT16 v_uint16x32 +#define __CV_V_INT16 v_int16x32 +#define __CV_V_UINT32 v_uint32x16 +#define __CV_V_INT32 v_int32x16 +#define __CV_V_UINT64 v_uint64x8 +#define __CV_V_INT64 v_int64x8 +#define __CV_V_FLOAT32 v_float32x16 +#define __CV_V_FLOAT64 v_float64x8 +struct v_uint8x64; +struct v_int8x64; +struct v_uint16x32; +struct v_int16x32; +struct v_uint32x16; +struct v_int32x16; +struct v_uint64x8; +struct v_int64x8; +struct v_float32x16; +struct v_float64x8; +#elif CV__SIMD_FORWARD == 256 +// 256 +#define __CV_VX(fun) v256_##fun +#define __CV_V_UINT8 v_uint8x32 +#define __CV_V_INT8 v_int8x32 +#define __CV_V_UINT16 v_uint16x16 +#define __CV_V_INT16 v_int16x16 +#define __CV_V_UINT32 v_uint32x8 +#define __CV_V_INT32 v_int32x8 +#define __CV_V_UINT64 v_uint64x4 +#define __CV_V_INT64 v_int64x4 +#define __CV_V_FLOAT32 v_float32x8 +#define __CV_V_FLOAT64 v_float64x4 +struct v_uint8x32; +struct v_int8x32; +struct v_uint16x16; +struct v_int16x16; +struct v_uint32x8; +struct v_int32x8; +struct v_uint64x4; +struct v_int64x4; +struct v_float32x8; +struct v_float64x4; +#else +// 128 +#define __CV_VX(fun) v_##fun +#define __CV_V_UINT8 v_uint8x16 +#define __CV_V_INT8 v_int8x16 +#define __CV_V_UINT16 v_uint16x8 +#define __CV_V_INT16 v_int16x8 +#define __CV_V_UINT32 v_uint32x4 +#define __CV_V_INT32 v_int32x4 +#define __CV_V_UINT64 v_uint64x2 +#define __CV_V_INT64 v_int64x2 +#define __CV_V_FLOAT32 v_float32x4 +#define __CV_V_FLOAT64 v_float64x2 +struct v_uint8x16; +struct v_int8x16; +struct v_uint16x8; +struct v_int16x8; +struct v_uint32x4; +struct v_int32x4; +struct v_uint64x2; +struct v_int64x2; +struct v_float32x4; +struct v_float64x2; +#endif + +/** Value reordering **/ + +// Expansion +void v_expand(const __CV_V_UINT8&, __CV_V_UINT16&, __CV_V_UINT16&); +void v_expand(const __CV_V_INT8&, __CV_V_INT16&, __CV_V_INT16&); +void v_expand(const __CV_V_UINT16&, __CV_V_UINT32&, __CV_V_UINT32&); +void v_expand(const __CV_V_INT16&, __CV_V_INT32&, __CV_V_INT32&); +void v_expand(const __CV_V_UINT32&, __CV_V_UINT64&, __CV_V_UINT64&); +void v_expand(const __CV_V_INT32&, __CV_V_INT64&, __CV_V_INT64&); +// Low Expansion +__CV_V_UINT16 v_expand_low(const __CV_V_UINT8&); +__CV_V_INT16 v_expand_low(const __CV_V_INT8&); +__CV_V_UINT32 v_expand_low(const __CV_V_UINT16&); +__CV_V_INT32 v_expand_low(const __CV_V_INT16&); +__CV_V_UINT64 v_expand_low(const __CV_V_UINT32&); +__CV_V_INT64 v_expand_low(const __CV_V_INT32&); +// High Expansion +__CV_V_UINT16 v_expand_high(const __CV_V_UINT8&); +__CV_V_INT16 v_expand_high(const __CV_V_INT8&); +__CV_V_UINT32 v_expand_high(const __CV_V_UINT16&); +__CV_V_INT32 v_expand_high(const __CV_V_INT16&); +__CV_V_UINT64 v_expand_high(const __CV_V_UINT32&); +__CV_V_INT64 v_expand_high(const __CV_V_INT32&); +// Load & Low Expansion +__CV_V_UINT16 __CV_VX(load_expand)(const uchar*); +__CV_V_INT16 __CV_VX(load_expand)(const schar*); +__CV_V_UINT32 __CV_VX(load_expand)(const ushort*); +__CV_V_INT32 __CV_VX(load_expand)(const short*); +__CV_V_UINT64 __CV_VX(load_expand)(const uint*); +__CV_V_INT64 __CV_VX(load_expand)(const int*); +// Load lower 8-bit and expand into 32-bit +__CV_V_UINT32 __CV_VX(load_expand_q)(const uchar*); +__CV_V_INT32 __CV_VX(load_expand_q)(const schar*); + +// Saturating Pack +__CV_V_UINT8 v_pack(const __CV_V_UINT16&, const __CV_V_UINT16&); +__CV_V_INT8 v_pack(const __CV_V_INT16&, const __CV_V_INT16&); +__CV_V_UINT16 v_pack(const __CV_V_UINT32&, const __CV_V_UINT32&); +__CV_V_INT16 v_pack(const __CV_V_INT32&, const __CV_V_INT32&); +// Non-saturating Pack +__CV_V_UINT32 v_pack(const __CV_V_UINT64&, const __CV_V_UINT64&); +__CV_V_INT32 v_pack(const __CV_V_INT64&, const __CV_V_INT64&); +// Pack signed integers with unsigned saturation +__CV_V_UINT8 v_pack_u(const __CV_V_INT16&, const __CV_V_INT16&); +__CV_V_UINT16 v_pack_u(const __CV_V_INT32&, const __CV_V_INT32&); + +/** Arithmetic, bitwise and comparison operations **/ + +// Non-saturating multiply +#if CV_VSX +template +Tvec v_mul_wrap(const Tvec& a, const Tvec& b); +#else +__CV_V_UINT8 v_mul_wrap(const __CV_V_UINT8&, const __CV_V_UINT8&); +__CV_V_INT8 v_mul_wrap(const __CV_V_INT8&, const __CV_V_INT8&); +__CV_V_UINT16 v_mul_wrap(const __CV_V_UINT16&, const __CV_V_UINT16&); +__CV_V_INT16 v_mul_wrap(const __CV_V_INT16&, const __CV_V_INT16&); +#endif + +// Multiply and expand +#if CV_VSX +template +void v_mul_expand(const Tvec& a, const Tvec& b, Twvec& c, Twvec& d); +#else +void v_mul_expand(const __CV_V_UINT8&, const __CV_V_UINT8&, __CV_V_UINT16&, __CV_V_UINT16&); +void v_mul_expand(const __CV_V_INT8&, const __CV_V_INT8&, __CV_V_INT16&, __CV_V_INT16&); +void v_mul_expand(const __CV_V_UINT16&, const __CV_V_UINT16&, __CV_V_UINT32&, __CV_V_UINT32&); +void v_mul_expand(const __CV_V_INT16&, const __CV_V_INT16&, __CV_V_INT32&, __CV_V_INT32&); +void v_mul_expand(const __CV_V_UINT32&, const __CV_V_UINT32&, __CV_V_UINT64&, __CV_V_UINT64&); +void v_mul_expand(const __CV_V_INT32&, const __CV_V_INT32&, __CV_V_INT64&, __CV_V_INT64&); +#endif + +// Conversions +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_INT32& a); +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_FLOAT64& a); +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_FLOAT64& a, const __CV_V_FLOAT64& b); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_INT32& a); +__CV_V_FLOAT64 v_cvt_f64_high(const __CV_V_INT32& a); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_FLOAT32& a); +__CV_V_FLOAT64 v_cvt_f64_high(const __CV_V_FLOAT32& a); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_INT64& a); + +/** Cleanup **/ +#undef CV__SIMD_FORWARD +#undef __CV_VX +#undef __CV_V_UINT8 +#undef __CV_V_INT8 +#undef __CV_V_UINT16 +#undef __CV_V_INT16 +#undef __CV_V_UINT32 +#undef __CV_V_INT32 +#undef __CV_V_UINT64 +#undef __CV_V_INT64 +#undef __CV_V_FLOAT32 +#undef __CV_V_FLOAT64 + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: \ No newline at end of file diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_msa.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_msa.hpp new file mode 100644 index 0000000..260350c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_msa.hpp @@ -0,0 +1,1872 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HAL_INTRIN_MSA_HPP +#define OPENCV_HAL_INTRIN_MSA_HPP + +#include +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @cond IGNORED +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define CV_SIMD128 1 + +//MSA implements 128-bit wide vector registers shared with the 64-bit wide floating-point unit registers. +//MSA and FPU can not be both present, unless the FPU has 64-bit floating-point registers. +#define CV_SIMD128_64F 1 + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + + v_uint8x16() : val(msa_dupq_n_u8(0)) {} + explicit v_uint8x16(v16u8 v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = msa_ld1q_u8(v); + } + uchar get0() const + { + return msa_getq_lane_u8(val, 0); + } + + v16u8 val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + + v_int8x16() : val(msa_dupq_n_s8(0)) {} + explicit v_int8x16(v16i8 v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = msa_ld1q_s8(v); + } + schar get0() const + { + return msa_getq_lane_s8(val, 0); + } + + v16i8 val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + + v_uint16x8() : val(msa_dupq_n_u16(0)) {} + explicit v_uint16x8(v8u16 v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = msa_ld1q_u16(v); + } + ushort get0() const + { + return msa_getq_lane_u16(val, 0); + } + + v8u16 val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + + v_int16x8() : val(msa_dupq_n_s16(0)) {} + explicit v_int16x8(v8i16 v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = msa_ld1q_s16(v); + } + short get0() const + { + return msa_getq_lane_s16(val, 0); + } + + v8i16 val; +}; + +struct v_uint32x4 +{ + typedef unsigned int lane_type; + enum { nlanes = 4 }; + + v_uint32x4() : val(msa_dupq_n_u32(0)) {} + explicit v_uint32x4(v4u32 v) : val(v) {} + v_uint32x4(unsigned int v0, unsigned int v1, unsigned int v2, unsigned int v3) + { + unsigned int v[] = {v0, v1, v2, v3}; + val = msa_ld1q_u32(v); + } + unsigned int get0() const + { + return msa_getq_lane_u32(val, 0); + } + + v4u32 val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + + v_int32x4() : val(msa_dupq_n_s32(0)) {} + explicit v_int32x4(v4i32 v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = msa_ld1q_s32(v); + } + int get0() const + { + return msa_getq_lane_s32(val, 0); + } + v4i32 val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + + v_float32x4() : val(msa_dupq_n_f32(0.0f)) {} + explicit v_float32x4(v4f32 v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = msa_ld1q_f32(v); + } + float get0() const + { + return msa_getq_lane_f32(val, 0); + } + v4f32 val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + + v_uint64x2() : val(msa_dupq_n_u64(0)) {} + explicit v_uint64x2(v2u64 v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = msa_ld1q_u64(v); + } + uint64 get0() const + { + return msa_getq_lane_u64(val, 0); + } + v2u64 val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + + v_int64x2() : val(msa_dupq_n_s64(0)) {} + explicit v_int64x2(v2i64 v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = msa_ld1q_s64(v); + } + int64 get0() const + { + return msa_getq_lane_s64(val, 0); + } + v2i64 val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + + v_float64x2() : val(msa_dupq_n_f64(0.0f)) {} + explicit v_float64x2(v2f64 v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = msa_ld1q_f64(v); + } + double get0() const + { + return msa_getq_lane_f64(val, 0); + } + v2f64 val; +}; + +#define OPENCV_HAL_IMPL_MSA_INIT(_Tpv, _Tp, suffix) \ +inline v_##_Tpv v_setzero_##suffix() { return v_##_Tpv(msa_dupq_n_##suffix((_Tp)0)); } \ +inline v_##_Tpv v_setall_##suffix(_Tp v) { return v_##_Tpv(msa_dupq_n_##suffix(v)); } \ +inline v_uint8x16 v_reinterpret_as_u8(const v_##_Tpv& v) { return v_uint8x16(MSA_TPV_REINTERPRET(v16u8, v.val)); } \ +inline v_int8x16 v_reinterpret_as_s8(const v_##_Tpv& v) { return v_int8x16(MSA_TPV_REINTERPRET(v16i8, v.val)); } \ +inline v_uint16x8 v_reinterpret_as_u16(const v_##_Tpv& v) { return v_uint16x8(MSA_TPV_REINTERPRET(v8u16, v.val)); } \ +inline v_int16x8 v_reinterpret_as_s16(const v_##_Tpv& v) { return v_int16x8(MSA_TPV_REINTERPRET(v8i16, v.val)); } \ +inline v_uint32x4 v_reinterpret_as_u32(const v_##_Tpv& v) { return v_uint32x4(MSA_TPV_REINTERPRET(v4u32, v.val)); } \ +inline v_int32x4 v_reinterpret_as_s32(const v_##_Tpv& v) { return v_int32x4(MSA_TPV_REINTERPRET(v4i32, v.val)); } \ +inline v_uint64x2 v_reinterpret_as_u64(const v_##_Tpv& v) { return v_uint64x2(MSA_TPV_REINTERPRET(v2u64, v.val)); } \ +inline v_int64x2 v_reinterpret_as_s64(const v_##_Tpv& v) { return v_int64x2(MSA_TPV_REINTERPRET(v2i64, v.val)); } \ +inline v_float32x4 v_reinterpret_as_f32(const v_##_Tpv& v) { return v_float32x4(MSA_TPV_REINTERPRET(v4f32, v.val)); } \ +inline v_float64x2 v_reinterpret_as_f64(const v_##_Tpv& v) { return v_float64x2(MSA_TPV_REINTERPRET(v2f64, v.val)); } + +OPENCV_HAL_IMPL_MSA_INIT(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_INIT(int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_INIT(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_INIT(int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_INIT(uint32x4, unsigned int, u32) +OPENCV_HAL_IMPL_MSA_INIT(int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_INIT(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_INIT(int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_INIT(float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_INIT(float64x2, double, f64) + +#define OPENCV_HAL_IMPL_MSA_PACK(_Tpvec, _Tpwvec, pack, mov, rshr) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(mov(a.val, b.val)); \ +} \ +template inline \ +_Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(rshr(a.val, b.val, n)); \ +} + +OPENCV_HAL_IMPL_MSA_PACK(v_uint8x16, v_uint16x8, pack, msa_qpack_u16, msa_qrpackr_u16) +OPENCV_HAL_IMPL_MSA_PACK(v_int8x16, v_int16x8, pack, msa_qpack_s16, msa_qrpackr_s16) +OPENCV_HAL_IMPL_MSA_PACK(v_uint16x8, v_uint32x4, pack, msa_qpack_u32, msa_qrpackr_u32) +OPENCV_HAL_IMPL_MSA_PACK(v_int16x8, v_int32x4, pack, msa_qpack_s32, msa_qrpackr_s32) +OPENCV_HAL_IMPL_MSA_PACK(v_uint32x4, v_uint64x2, pack, msa_pack_u64, msa_rpackr_u64) +OPENCV_HAL_IMPL_MSA_PACK(v_int32x4, v_int64x2, pack, msa_pack_s64, msa_rpackr_s64) +OPENCV_HAL_IMPL_MSA_PACK(v_uint8x16, v_int16x8, pack_u, msa_qpacku_s16, msa_qrpackru_s16) +OPENCV_HAL_IMPL_MSA_PACK(v_uint16x8, v_int32x4, pack_u, msa_qpacku_s32, msa_qrpackru_s32) + +#define OPENCV_HAL_IMPL_MSA_PACK_STORE(_Tpvec, _Tp, hreg, suffix, _Tpwvec, pack, mov, rshr) \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = mov(a.val); \ + msa_st1_##suffix(ptr, a1); \ +} \ +template inline \ +void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = rshr(a.val, n); \ + msa_st1_##suffix(ptr, a1); \ +} + +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint8x16, uchar, v8u8, u8, v_uint16x8, pack, msa_qmovn_u16, msa_qrshrn_n_u16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int8x16, schar, v8i8, s8, v_int16x8, pack, msa_qmovn_s16, msa_qrshrn_n_s16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint16x8, ushort, v4u16, u16, v_uint32x4, pack, msa_qmovn_u32, msa_qrshrn_n_u32) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int16x8, short, v4i16, s16, v_int32x4, pack, msa_qmovn_s32, msa_qrshrn_n_s32) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint32x4, unsigned, v2u32, u32, v_uint64x2, pack, msa_movn_u64, msa_rshrn_n_u64) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int32x4, int, v2i32, s32, v_int64x2, pack, msa_movn_s64, msa_rshrn_n_s64) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint8x16, uchar, v8u8, u8, v_int16x8, pack_u, msa_qmovun_s16, msa_qrshrun_n_s16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint16x8, ushort, v4u16, u16, v_int32x4, pack_u, msa_qmovun_s32, msa_qrshrun_n_s32) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint8x16(msa_pack_u16(a.val, b.val)); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + return v_uint8x16(msa_pack_u16(msa_pack_u32(a.val, b.val), msa_pack_u32(c.val, d.val))); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + v8u16 abcd = msa_pack_u32(msa_pack_u64(a.val, b.val), msa_pack_u64(c.val, d.val)); + v8u16 efgh = msa_pack_u32(msa_pack_u64(e.val, f.val), msa_pack_u64(g.val, h.val)); + return v_uint8x16(msa_pack_u16(abcd, efgh)); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + v4f32 v0 = v.val; + v4f32 res = msa_mulq_lane_f32(m0.val, v0, 0); + res = msa_mlaq_lane_f32(res, m1.val, v0, 1); + res = msa_mlaq_lane_f32(res, m2.val, v0, 2); + res = msa_mlaq_lane_f32(res, m3.val, v0, 3); + return v_float32x4(res); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + v4f32 v0 = v.val; + v4f32 res = msa_mulq_lane_f32(m0.val, v0, 0); + res = msa_mlaq_lane_f32(res, m1.val, v0, 1); + res = msa_mlaq_lane_f32(res, m2.val, v0, 2); + res = msa_addq_f32(res, a.val); + return v_float32x4(res); +} + +#define OPENCV_HAL_IMPL_MSA_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint8x16, msa_qaddq_u8) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint8x16, msa_qsubq_u8) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int8x16, msa_qaddq_s8) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int8x16, msa_qsubq_s8) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint16x8, msa_qaddq_u16) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint16x8, msa_qsubq_u16) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int16x8, msa_qaddq_s16) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int16x8, msa_qsubq_s16) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int32x4, msa_addq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int32x4, msa_subq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_int32x4, msa_mulq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint32x4, msa_addq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint32x4, msa_subq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_uint32x4, msa_mulq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_float32x4, msa_addq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_float32x4, msa_subq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_float32x4, msa_mulq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int64x2, msa_addq_s64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int64x2, msa_subq_s64) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint64x2, msa_addq_u64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint64x2, msa_subq_u64) +OPENCV_HAL_IMPL_MSA_BIN_OP(/, v_float32x4, msa_divq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_float64x2, msa_addq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_float64x2, msa_subq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_float64x2, msa_mulq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(/, v_float64x2, msa_divq_f64) + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_MSA_MUL_SAT(_Tpvec, _Tpwvec) \ +inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ +} \ +inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ +{a = a * b; return a; } + +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_uint16x8, v_uint32x4) + +// Multiply and expand +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v16i8 a_lo, a_hi, b_lo, b_hi; + + ILVRL_B2_SB(a.val, msa_dupq_n_s8(0), a_lo, a_hi); + ILVRL_B2_SB(b.val, msa_dupq_n_s8(0), b_lo, b_hi); + c.val = msa_mulq_s16(msa_paddlq_s8(a_lo), msa_paddlq_s8(b_lo)); + d.val = msa_mulq_s16(msa_paddlq_s8(a_hi), msa_paddlq_s8(b_hi)); +} + +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v16u8 a_lo, a_hi, b_lo, b_hi; + + ILVRL_B2_UB(a.val, msa_dupq_n_u8(0), a_lo, a_hi); + ILVRL_B2_UB(b.val, msa_dupq_n_u8(0), b_lo, b_hi); + c.val = msa_mulq_u16(msa_paddlq_u8(a_lo), msa_paddlq_u8(b_lo)); + d.val = msa_mulq_u16(msa_paddlq_u8(a_hi), msa_paddlq_u8(b_hi)); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + v8i16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); + ILVRL_H2_SH(b.val, msa_dupq_n_s16(0), b_lo, b_hi); + c.val = msa_mulq_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(b_lo)); + d.val = msa_mulq_s32(msa_paddlq_s16(a_hi), msa_paddlq_s16(b_hi)); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + v8u16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); + ILVRL_H2_UH(b.val, msa_dupq_n_u16(0), b_lo, b_hi); + c.val = msa_mulq_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(b_lo)); + d.val = msa_mulq_u32(msa_paddlq_u16(a_hi), msa_paddlq_u16(b_hi)); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + v4u32 a_lo, a_hi, b_lo, b_hi; + + ILVRL_W2_UW(a.val, msa_dupq_n_u32(0), a_lo, a_hi); + ILVRL_W2_UW(b.val, msa_dupq_n_u32(0), b_lo, b_hi); + c.val = msa_mulq_u64(msa_paddlq_u32(a_lo), msa_paddlq_u32(b_lo)); + d.val = msa_mulq_u64(msa_paddlq_u32(a_hi), msa_paddlq_u32(b_hi)); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + v8i16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); + ILVRL_H2_SH(b.val, msa_dupq_n_s16(0), b_lo, b_hi); + + return v_int16x8(msa_packr_s32(msa_mulq_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(b_lo)), + msa_mulq_s32(msa_paddlq_s16(a_hi), msa_paddlq_s16(b_hi)), 16)); +} + +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + v8u16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); + ILVRL_H2_UH(b.val, msa_dupq_n_u16(0), b_lo, b_hi); + + return v_uint16x8(msa_packr_u32(msa_mulq_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(b_lo)), + msa_mulq_u32(msa_paddlq_u16(a_hi), msa_paddlq_u16(b_hi)), 16)); +} + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(msa_dotp_s_w(a.val, b.val)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(msa_dpadd_s_w(c.val , a.val, b.val)); } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ return v_int64x2(msa_dotp_s_d(a.val, b.val)); } +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_int64x2(msa_dpadd_s_d(c.val , a.val, b.val)); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + v8u16 even_a = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8), 8); + v8u16 odd_a = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8); + v8u16 even_b = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8), 8); + v8u16 odd_b = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8); + v4u32 prod = msa_dotp_u_w(even_a, even_b); + return v_uint32x4(msa_dpadd_u_w(prod, odd_a, odd_b)); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ + v8u16 even_a = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8), 8); + v8u16 odd_a = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8); + v8u16 even_b = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8), 8); + v8u16 odd_b = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8); + v4u32 prod = msa_dpadd_u_w(c.val, even_a, even_b); + return v_uint32x4(msa_dpadd_u_w(prod, odd_a, odd_b)); +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + v8i16 prod = msa_dotp_s_h(a.val, b.val); + return v_int32x4(msa_hadd_s32(prod, prod)); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, + const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + v4u32 even_a = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16), 16); + v4u32 odd_a = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16); + v4u32 even_b = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16), 16); + v4u32 odd_b = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16); + v2u64 prod = msa_dotp_u_d(even_a, even_b); + return v_uint64x2(msa_dpadd_u_d(prod, odd_a, odd_b)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, + const v_uint64x2& c) +{ + v4u32 even_a = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16), 16); + v4u32 odd_a = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16); + v4u32 even_b = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16), 16); + v4u32 odd_b = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16); + v2u64 prod = msa_dpadd_u_d(c.val, even_a, even_b); + return v_uint64x2(msa_dpadd_u_d(prod, odd_a, odd_b)); +} + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v4i32 prod = msa_dotp_s_w(a.val, b.val); + return v_int64x2(msa_hadd_s64(prod, prod)); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_IMPL_MSA_LOGIC_OP(_Tpvec, _Tpv, suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(&, _Tpvec, msa_andq_##suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(|, _Tpvec, msa_orrq_##suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(^, _Tpvec, msa_eorq_##suffix) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_mvnq_u8(MSA_TPV_REINTERPRET(v16u8, a.val)))); \ +} + +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint8x16, v16u8, u8) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int8x16, v16i8, s8) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint16x8, v8u16, u16) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int16x8, v8i16, s16) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint32x4, v4u32, u32) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int32x4, v4i32, s32) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint64x2, v2u64, u64) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int64x2, v2i64, s64) + +#define OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(bin_op, intrin) \ +inline v_float32x4 operator bin_op (const v_float32x4& a, const v_float32x4& b) \ +{ \ + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, intrin(MSA_TPV_REINTERPRET(v4i32, a.val), MSA_TPV_REINTERPRET(v4i32, b.val)))); \ +} \ +inline v_float32x4& operator bin_op##= (v_float32x4& a, const v_float32x4& b) \ +{ \ + a.val = MSA_TPV_REINTERPRET(v4f32, intrin(MSA_TPV_REINTERPRET(v4i32, a.val), MSA_TPV_REINTERPRET(v4i32, b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(&, msa_andq_s32) +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(|, msa_orrq_s32) +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(^, msa_eorq_s32) + +inline v_float32x4 operator ~ (const v_float32x4& a) +{ + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_mvnq_s32(MSA_TPV_REINTERPRET(v4i32, a.val)))); +} + +/* v_abs */ +#define OPENCV_HAL_IMPL_MSA_ABS(_Tpuvec, _Tpsvec, usuffix, ssuffix) \ +inline _Tpuvec v_abs(const _Tpsvec& a) \ +{ \ + return v_reinterpret_as_##usuffix(_Tpsvec(msa_absq_##ssuffix(a.val))); \ +} + +OPENCV_HAL_IMPL_MSA_ABS(v_uint8x16, v_int8x16, u8, s8) +OPENCV_HAL_IMPL_MSA_ABS(v_uint16x8, v_int16x8, u16, s16) +OPENCV_HAL_IMPL_MSA_ABS(v_uint32x4, v_int32x4, u32, s32) + +/* v_abs(float), v_sqrt, v_invsqrt */ +#define OPENCV_HAL_IMPL_MSA_BASIC_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a) \ +{ \ + return _Tpvec(intrin(a.val)); \ +} + +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_abs, msa_absq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_abs, msa_absq_f64) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_sqrt, msa_sqrtq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_invsqrt, msa_rsqrtq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_sqrt, msa_sqrtq_f64) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_invsqrt, msa_rsqrtq_f64) + +#define OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(bin_op, intrin) \ +inline v_float64x2 operator bin_op (const v_float64x2& a, const v_float64x2& b) \ +{ \ + return v_float64x2(MSA_TPV_REINTERPRET(v2f64, intrin(MSA_TPV_REINTERPRET(v2i64, a.val), MSA_TPV_REINTERPRET(v2i64, b.val)))); \ +} \ +inline v_float64x2& operator bin_op##= (v_float64x2& a, const v_float64x2& b) \ +{ \ + a.val = MSA_TPV_REINTERPRET(v2f64, intrin(MSA_TPV_REINTERPRET(v2i64, a.val), MSA_TPV_REINTERPRET(v2i64, b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(&, msa_andq_s64) +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(|, msa_orrq_s64) +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(^, msa_eorq_s64) + +inline v_float64x2 operator ~ (const v_float64x2& a) +{ + return v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_mvnq_s32(MSA_TPV_REINTERPRET(v4i32, a.val)))); +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_MSA_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_min, msa_minq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_max, msa_maxq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_min, msa_minq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_max, msa_maxq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_min, msa_minq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_max, msa_maxq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_min, msa_minq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_max, msa_maxq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_min, msa_minq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_max, msa_maxq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int32x4, v_min, msa_minq_s32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int32x4, v_max, msa_maxq_s32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_min, msa_minq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_max, msa_maxq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_min, msa_minq_f64) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_max, msa_maxq_f64) + +#define OPENCV_HAL_IMPL_MSA_INT_CMP_OP(_Tpvec, _Tpv, suffix, not_suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ceqq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_mvnq_##not_suffix(msa_ceqq_##suffix(a.val, b.val)))); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cltq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cgtq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cleq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cgeq_##suffix(a.val, b.val))); } + +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint8x16, v16u8, u8, u8) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int8x16, v16i8, s8, u8) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint16x8, v8u16, u16, u16) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int16x8, v8i16, s16, u16) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint32x4, v4u32, u32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int32x4, v4i32, s32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_float32x4, v4f32, f32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint64x2, v2u64, u64, u64) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int64x2, v2i64, s64, u64) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_float64x2, v2f64, f64, u64) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ceqq_f32(a.val, a.val))); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ceqq_f64(a.val, a.val))); } + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_add_wrap, msa_addq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_add_wrap, msa_addq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_add_wrap, msa_addq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_add_wrap, msa_addq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_sub_wrap, msa_subq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_sub_wrap, msa_subq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_sub_wrap, msa_subq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_sub_wrap, msa_subq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_mul_wrap, msa_mulq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_mul_wrap, msa_mulq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_mul_wrap, msa_mulq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_mul_wrap, msa_mulq_s16) + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_absdiff, msa_abdq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_absdiff, msa_abdq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_absdiff, msa_abdq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_absdiff, msa_abdq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_absdiff, msa_abdq_f64) + +/** Saturating absolute difference **/ +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_absdiffs, msa_qabdq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_absdiffs, msa_qabdq_s16) + +#define OPENCV_HAL_IMPL_MSA_BIN_FUNC2(_Tpvec, _Tpvec2, _Tpv, func, intrin) \ +inline _Tpvec2 func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec2(MSA_TPV_REINTERPRET(_Tpv, intrin(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int8x16, v_uint8x16, v16u8, v_absdiff, msa_abdq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int16x8, v_uint16x8, v8u16, v_absdiff, msa_abdq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int32x4, v_uint32x4, v4u32, v_absdiff, msa_abdq_s32) + +/* v_magnitude, v_sqr_magnitude, v_fma, v_muladd */ +inline v_float32x4 v_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 x(msa_mlaq_f32(msa_mulq_f32(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float32x4 v_sqr_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + return v_float32x4(msa_mlaq_f32(msa_mulq_f32(a.val, a.val), b.val, b.val)); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_float32x4(msa_mlaq_f32(c.val, a.val, b.val)); +} + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_int32x4(msa_mlaq_s32(c.val, a.val, b.val)); +} + +inline v_float32x4 v_muladd(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float64x2 v_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 x(msa_mlaq_f64(msa_mulq_f64(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float64x2 v_sqr_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + return v_float64x2(msa_mlaq_f64(msa_mulq_f64(a.val, a.val), b.val, b.val)); +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_float64x2(msa_mlaq_f64(c.val, a.val, b.val)); +} + +inline v_float64x2 v_muladd(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_fma(a, b, c); +} + +// trade efficiency for convenience +#define OPENCV_HAL_IMPL_MSA_SHIFT_OP(_Tpvec, suffix, _Tps, ssuffix) \ +inline _Tpvec operator << (const _Tpvec& a, int n) \ +{ return _Tpvec(msa_shlq_##suffix(a.val, msa_dupq_n_##ssuffix((_Tps)n))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int n) \ +{ return _Tpvec(msa_shrq_##suffix(a.val, msa_dupq_n_##ssuffix((_Tps)n))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(msa_shlq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(msa_shrq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ return _Tpvec(msa_rshrq_n_##suffix(a.val, n)); } + +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint8x16, u8, schar, s8) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int8x16, s8, schar, s8) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint16x8, u16, short, s16) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int16x8, s16, short, s16) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint32x4, u32, int, s32) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int32x4, s32, int, s32) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint64x2, u64, int64, s64) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int64x2, s64, int64, s64) + +/* v_rotate_right, v_rotate_left */ +#define OPENCV_HAL_IMPL_MSA_ROTATE_OP(_Tpvec, _Tpv, _Tpvs, suffix) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##suffix(0), n))); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(msa_dupq_n_##suffix(0), MSA_TPV_REINTERPRET(_Tpvs, a.val), _Tpvec::nlanes - n))); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ \ + return a; \ +} \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), MSA_TPV_REINTERPRET(_Tpvs, b.val), n))); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, b.val), MSA_TPV_REINTERPRET(_Tpvs, a.val), _Tpvec::nlanes - n))); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ \ + CV_UNUSED(b); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint64x2, v2u64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int64x2, v2i64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_float64x2, v2f64, v2i64, s64) + +#define OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(msa_ld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(msa_ld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(msa_combine_##suffix(msa_ld1_##suffix(ptr), msa_dup_n_##suffix((_Tp)0))); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(msa_combine_##suffix(msa_ld1_##suffix(ptr0), msa_ld1_##suffix(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ \ + int n = _Tpvec::nlanes; \ + for( int i = 0; i < (n/2); i++ ) \ + ptr[i] = a.val[i]; \ +} \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + int n = _Tpvec::nlanes; \ + for( int i = 0; i < (n/2); i++ ) \ + ptr[i] = a.val[i+(n/2)]; \ +} + +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_float64x2, double, f64) + + +/** Reverse **/ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + v_uint8x16 c = v_uint8x16((v16u8)__builtin_msa_vshf_b((v16i8)((v2i64){0x08090A0B0C0D0E0F, 0x0001020304050607}), msa_dupq_n_s8(0), (v16i8)a.val)); + return c; +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + v_uint16x8 c = v_uint16x8((v8u16)__builtin_msa_vshf_h((v8i16)((v2i64){0x0004000500060007, 0x0000000100020003}), msa_dupq_n_s16(0), (v8i16)a.val)); + return c; +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + v_uint32x4 c; + c.val[0] = a.val[3]; + c.val[1] = a.val[2]; + c.val[2] = a.val[1]; + c.val[3] = a.val[0]; + return c; +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + v_uint64x2 c; + c.val[0] = a.val[1]; + c.val[1] = a.val[0]; + return c; +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(func, cfunc) \ +inline unsigned short v_reduce_##func(const v_uint16x8& a) \ +{ \ + v8u16 a_lo, a_hi; \ + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); \ + v4u32 b = msa_##func##q_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(a_hi)); \ + v4u32 b_lo, b_hi; \ + ILVRL_W2_UW(b, msa_dupq_n_u32(0), b_lo, b_hi); \ + v2u64 c = msa_##func##q_u64(msa_paddlq_u32(b_lo), msa_paddlq_u32(b_hi)); \ + return (unsigned short)cfunc(c[0], c[1]); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(min, std::min) + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(func, cfunc) \ +inline short v_reduce_##func(const v_int16x8& a) \ +{ \ + v8i16 a_lo, a_hi; \ + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); \ + v4i32 b = msa_##func##q_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(a_hi)); \ + v4i32 b_lo, b_hi; \ + ILVRL_W2_SW(b, msa_dupq_n_s32(0), b_lo, b_hi); \ + v2i64 c = msa_##func##q_s64(msa_paddlq_s32(b_lo), msa_paddlq_s32(b_hi)); \ + return (short)cfunc(c[0], c[1]); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(min, std::min) + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(_Tpvec, scalartype, func, cfunc) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + return (scalartype)cfunc(cfunc(a.val[0], a.val[1]), cfunc(a.val[2], a.val[3])); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_float32x4, float, min, std::min) + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(_Tpvec, scalartype, _Tpvec2, func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpvec2 a1, a2; \ + v_expand(a, a1, a2); \ + return (scalartype)v_reduce_##func(v_##func(a1, a2)); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_uint8x16, uchar, v_uint16x8, min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_uint8x16, uchar, v_uint16x8, max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_int8x16, char, v_int16x8, min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_int8x16, char, v_int16x8, max) + + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_SUM(_Tpvec, scalartype, suffix) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + return (scalartype)msa_sum_##suffix(a.val); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint8x16, unsigned char, u8) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int8x16, char, s8) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint16x8, unsigned short, u16) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_float32x4, float, f32) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ return (uint64)(msa_getq_lane_u64(a.val, 0) + msa_getq_lane_u64(a.val, 1)); } +inline int64 v_reduce_sum(const v_int64x2& a) +{ return (int64)(msa_getq_lane_s64(a.val, 0) + msa_getq_lane_s64(a.val, 1)); } +inline double v_reduce_sum(const v_float64x2& a) +{ + return msa_getq_lane_f64(a.val, 0) + msa_getq_lane_f64(a.val, 1); +} + +/* v_reduce_sum4, v_reduce_sad */ +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v4f32 u0 = msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, b.val), MSA_TPV_REINTERPRET(v4i32, a.val))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, b.val), MSA_TPV_REINTERPRET(v4i32, a.val)))); // a0+a1 b0+b1 a2+a3 b2+b3 + v4f32 u1 = msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, d.val), MSA_TPV_REINTERPRET(v4i32, c.val))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, d.val), MSA_TPV_REINTERPRET(v4i32, c.val)))); // c0+c1 d0+d1 c2+c3 d2+d3 + + return v_float32x4(msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, u1), MSA_TPV_REINTERPRET(v2i64, u0))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, u1), MSA_TPV_REINTERPRET(v2i64, u0))))); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + v16u8 t0 = msa_abdq_u8(a.val, b.val); + v8u16 t1 = msa_paddlq_u8(t0); + v4u32 t2 = msa_paddlq_u16(t1); + return msa_sum_u32(t2); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + v16u8 t0 = MSA_TPV_REINTERPRET(v16u8, msa_abdq_s8(a.val, b.val)); + v8u16 t1 = msa_paddlq_u8(t0); + v4u32 t2 = msa_paddlq_u16(t1); + return msa_sum_u32(t2); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v8u16 t0 = msa_abdq_u16(a.val, b.val); + v4u32 t1 = msa_paddlq_u16(t0); + return msa_sum_u32(t1); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v8u16 t0 = MSA_TPV_REINTERPRET(v8u16, msa_abdq_s16(a.val, b.val)); + v4u32 t1 = msa_paddlq_u16(t0); + return msa_sum_u32(t1); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + v4u32 t0 = msa_abdq_u32(a.val, b.val); + return msa_sum_u32(t0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + v4u32 t0 = MSA_TPV_REINTERPRET(v4u32, msa_abdq_s32(a.val, b.val)); + return msa_sum_u32(t0); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + v4f32 t0 = msa_abdq_f32(a.val, b.val); + return msa_sum_f32(t0); +} + +/* v_popcount */ +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(_Tpvec) \ +inline v_uint8x16 v_popcount(const _Tpvec& a) \ +{ \ + v16u8 t = MSA_TPV_REINTERPRET(v16u8, msa_cntq_s8(MSA_TPV_REINTERPRET(v16i8, a.val))); \ + return v_uint8x16(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(v_uint8x16) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(v_int8x16) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(_Tpvec) \ +inline v_uint16x8 v_popcount(const _Tpvec& a) \ +{ \ + v8u16 t = MSA_TPV_REINTERPRET(v8u16, msa_cntq_s16(MSA_TPV_REINTERPRET(v8i16, a.val))); \ + return v_uint16x8(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(v_uint16x8) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(v_int16x8) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(_Tpvec) \ +inline v_uint32x4 v_popcount(const _Tpvec& a) \ +{ \ + v4u32 t = MSA_TPV_REINTERPRET(v4u32, msa_cntq_s32(MSA_TPV_REINTERPRET(v4i32, a.val))); \ + return v_uint32x4(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(v_uint32x4) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(v_int32x4) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(_Tpvec) \ +inline v_uint64x2 v_popcount(const _Tpvec& a) \ +{ \ + v2u64 t = MSA_TPV_REINTERPRET(v2u64, msa_cntq_s64(MSA_TPV_REINTERPRET(v2i64, a.val))); \ + return v_uint64x2(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(v_uint64x2) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(v_int64x2) + +inline int v_signmask(const v_uint8x16& a) +{ + v8i8 m0 = msa_create_s8(CV_BIG_UINT(0x0706050403020100)); + v16u8 v0 = msa_shlq_u8(msa_shrq_n_u8(a.val, 7), msa_combine_s8(m0, m0)); + v8u16 v1 = msa_paddlq_u8(v0); + v4u32 v2 = msa_paddlq_u16(v1); + v2u64 v3 = msa_paddlq_u32(v2); + return (int)msa_getq_lane_u64(v3, 0) + ((int)msa_getq_lane_u64(v3, 1) << 8); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_uint16x8& a) +{ + v4i16 m0 = msa_create_s16(CV_BIG_UINT(0x0003000200010000)); + v8u16 v0 = msa_shlq_u16(msa_shrq_n_u16(a.val, 15), msa_combine_s16(m0, m0)); + v4u32 v1 = msa_paddlq_u16(v0); + v2u64 v2 = msa_paddlq_u32(v1); + return (int)msa_getq_lane_u64(v2, 0) + ((int)msa_getq_lane_u64(v2, 1) << 4); +} +inline int v_signmask(const v_int16x8& a) +{ return v_signmask(v_reinterpret_as_u16(a)); } + +inline int v_signmask(const v_uint32x4& a) +{ + v2i32 m0 = msa_create_s32(CV_BIG_UINT(0x0000000100000000)); + v4u32 v0 = msa_shlq_u32(msa_shrq_n_u32(a.val, 31), msa_combine_s32(m0, m0)); + v2u64 v1 = msa_paddlq_u32(v0); + return (int)msa_getq_lane_u64(v1, 0) + ((int)msa_getq_lane_u64(v1, 1) << 2); +} +inline int v_signmask(const v_int32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } + +inline int v_signmask(const v_uint64x2& a) +{ + v2u64 v0 = msa_shrq_n_u64(a.val, 63); + return (int)msa_getq_lane_u64(v0, 0) + ((int)msa_getq_lane_u64(v0, 1) << 1); +} +inline int v_signmask(const v_int64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } + +#define OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(_Tpvec, _Tpvec2, suffix, shift) \ +inline bool v_check_all(const v_##_Tpvec& a) \ +{ \ + _Tpvec2 v0 = msa_shrq_n_##suffix(msa_mvnq_##suffix(a.val), shift); \ + v2u64 v1 = MSA_TPV_REINTERPRET(v2u64, v0); \ + return (msa_getq_lane_u64(v1, 0) | msa_getq_lane_u64(v1, 1)) == 0; \ +} \ +inline bool v_check_any(const v_##_Tpvec& a) \ +{ \ + _Tpvec2 v0 = msa_shrq_n_##suffix(a.val, shift); \ + v2u64 v1 = MSA_TPV_REINTERPRET(v2u64, v0); \ + return (msa_getq_lane_u64(v1, 0) | msa_getq_lane_u64(v1, 1)) != 0; \ +} + +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint8x16, v16u8, u8, 7) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint16x8, v8u16, u16, 15) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint32x4, v4u32, u32, 31) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint64x2, v2u64, u64, 63) + +inline bool v_check_all(const v_int8x16& a) +{ return v_check_all(v_reinterpret_as_u8(a)); } +inline bool v_check_all(const v_int16x8& a) +{ return v_check_all(v_reinterpret_as_u16(a)); } +inline bool v_check_all(const v_int32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } + +inline bool v_check_any(const v_int8x16& a) +{ return v_check_any(v_reinterpret_as_u8(a)); } +inline bool v_check_any(const v_int16x8& a) +{ return v_check_any(v_reinterpret_as_u16(a)); } +inline bool v_check_any(const v_int32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } + +inline bool v_check_all(const v_int64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_int64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } + +/* v_select */ +#define OPENCV_HAL_IMPL_MSA_SELECT(_Tpvec, _Tpv, _Tpvu) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_bslq_u8(MSA_TPV_REINTERPRET(_Tpvu, mask.val), \ + MSA_TPV_REINTERPRET(_Tpvu, b.val), MSA_TPV_REINTERPRET(_Tpvu, a.val)))); \ +} + +OPENCV_HAL_IMPL_MSA_SELECT(v_uint8x16, v16u8, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int8x16, v16i8, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_uint16x8, v8u16, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int16x8, v8i16, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_uint32x4, v4u32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int32x4, v4i32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_float32x4, v4f32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_float64x2, v2f64, v16u8) + +#define OPENCV_HAL_IMPL_MSA_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix, ssuffix, _Tpv, _Tpvs) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + _Tpv a_lo = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + _Tpv a_hi = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + b0.val = msa_paddlq_##suffix(a_lo); \ + b1.val = msa_paddlq_##suffix(a_hi); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ \ + _Tpv a_lo = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + return _Tpwvec(msa_paddlq_##suffix(a_lo)); \ +} \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ \ + _Tpv a_hi = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + return _Tpwvec(msa_paddlq_##suffix(a_hi)); \ +} \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + return _Tpwvec(msa_movl_##suffix(msa_ld1_##suffix(ptr))); \ +} + +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint8x16, v_uint16x8, uchar, u8, s8, v16u8, v16i8) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int8x16, v_int16x8, schar, s8, s8, v16i8, v16i8) +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint16x8, v_uint32x4, ushort, u16, s16, v8u16, v8i16) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int16x8, v_int32x4, short, s16, s16, v8i16, v8i16) +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint32x4, v_uint64x2, uint, u32, s32, v4u32, v4i32) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int32x4, v_int64x2, int, s32, s32, v4i32, v4i32) + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + return v_uint32x4((v4u32){ptr[0], ptr[1], ptr[2], ptr[3]}); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + return v_int32x4((v4i32){ptr[0], ptr[1], ptr[2], ptr[3]}); +} + +/* v_zip, v_combine_low, v_combine_high, v_recombine */ +#define OPENCV_HAL_IMPL_MSA_UNPACKS(_Tpvec, _Tpv, _Tpvs, ssuffix) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + b1.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val)))); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val)))); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + c.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val))); \ + d.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val))); \ +} + +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_float64x2, v2f64, v2i64, s64) + +/* v_extract */ +#define OPENCV_HAL_IMPL_MSA_EXTRACT(_Tpvec, _Tpv, _Tpvs, suffix) \ +template \ +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), MSA_TPV_REINTERPRET(_Tpvs, b.val), s))); \ +} + +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint64x2, v2u64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int64x2, v2i64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_float64x2, v2f64, v2i64, s64) + +/* v_round, v_floor, v_ceil, v_trunc */ +inline v_int32x4 v_round(const v_float32x4& a) +{ + return v_int32x4(msa_cvttintq_s32_f32(a.val)); +} + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + v4i32 a1 = msa_cvttintq_s32_f32(a.val); + return v_int32x4(msa_addq_s32(a1, MSA_TPV_REINTERPRET(v4i32, msa_cgtq_f32(msa_cvtfintq_f32_s32(a1), a.val)))); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + v4i32 a1 = msa_cvttintq_s32_f32(a.val); + return v_int32x4(msa_subq_s32(a1, MSA_TPV_REINTERPRET(v4i32, msa_cgtq_f32(a.val, msa_cvtfintq_f32_s32(a1))))); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ + return v_int32x4(msa_cvttruncq_s32_f32(a.val)); +} + +inline v_int32x4 v_round(const v_float64x2& a) +{ + return v_int32x4(msa_pack_s64(msa_cvttintq_s64_f64(a.val), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + return v_int32x4(msa_pack_s64(msa_cvttintq_s64_f64(a.val), msa_cvttintq_s64_f64(b.val))); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + v2f64 a1 = msa_cvtrintq_f64(a.val); + return v_int32x4(msa_pack_s64(msa_addq_s64(msa_cvttruncq_s64_f64(a1), MSA_TPV_REINTERPRET(v2i64, msa_cgtq_f64(a1, a.val))), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + v2f64 a1 = msa_cvtrintq_f64(a.val); + return v_int32x4(msa_pack_s64(msa_subq_s64(msa_cvttruncq_s64_f64(a1), MSA_TPV_REINTERPRET(v2i64, msa_cgtq_f64(a.val, a1))), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ + return v_int32x4(msa_pack_s64(msa_cvttruncq_s64_f64(a.val), msa_dupq_n_s64(0))); +} + +#define OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(_Tpvec, _Tpv, _Tpvs, ssuffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + _Tpv t00 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + _Tpv t01 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + _Tpv t10 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a3.val), MSA_TPV_REINTERPRET(_Tpvs, a2.val))); \ + _Tpv t11 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a3.val), MSA_TPV_REINTERPRET(_Tpvs, a2.val))); \ + b0.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, t10), MSA_TPV_REINTERPRET(v2i64, t00))); \ + b1.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, t10), MSA_TPV_REINTERPRET(v2i64, t00))); \ + b2.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, t11), MSA_TPV_REINTERPRET(v2i64, t01))); \ + b3.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, t11), MSA_TPV_REINTERPRET(v2i64, t01))); \ +} + +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_float32x4, v4f32, v4i32, s32) + +#define OPENCV_HAL_IMPL_MSA_INTERLEAVED(_Tpvec, _Tp, suffix) \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \ +{ \ + msa_ld2q_##suffix(ptr, &a.val, &b.val); \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \ +{ \ + msa_ld3q_##suffix(ptr, &a.val, &b.val, &c.val); \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \ + v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + msa_ld4q_##suffix(ptr, &a.val, &b.val, &c.val, &d.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + msa_st2q_##suffix(ptr, a.val, b.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + msa_st3q_##suffix(ptr, a.val, b.val, c.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, const v_##_Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + msa_st4q_##suffix(ptr, a.val, b.val, c.val, d.val); \ +} + +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(float64x2, double, f64) + +/* v_cvt_f32, v_cvt_f64, v_cvt_f64_high */ +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(msa_cvtfintq_f32_s32(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + return v_float32x4(msa_cvtfq_f32_f64(a.val, msa_dupq_n_f64(0.0f))); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(msa_cvtfq_f32_f64(a.val, b.val)); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(msa_cvtflq_f64_f32(msa_cvtfintq_f32_s32(a.val))); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(msa_cvtfhq_f64_f32(msa_cvtfintq_f32_s32(a.val))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(msa_cvtflq_f64_f32(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(msa_cvtfhq_f64_f32(a.val)); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ + return v_float64x2(msa_cvtfintq_f64_s64(a.val)); +} + +////////////// Lookup table access //////////////////// +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[ 0]], + tab[idx[ 1]], + tab[idx[ 2]], + tab[idx[ 3]], + tab[idx[ 4]], + tab[idx[ 5]], + tab[idx[ 6]], + tab[idx[ 7]], + tab[idx[ 8]], + tab[idx[ 9]], + tab[idx[10]], + tab[idx[11]], + tab[idx[12]], + tab[idx[13]], + tab[idx[14]], + tab[idx[15]] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[4]], + tab[idx[4] + 1], + tab[idx[5]], + tab[idx[5] + 1], + tab[idx[6]], + tab[idx[6] + 1], + tab[idx[7]], + tab[idx[7] + 1] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[0] + 2], + tab[idx[0] + 3], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[1] + 2], + tab[idx[1] + 3], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[2] + 2], + tab[idx[2] + 3], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[3] + 2], + tab[idx[3] + 3] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } + + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]], + tab[idx[4]], + tab[idx[5]], + tab[idx[6]], + tab[idx[7]] + }; + return v_int16x8(msa_ld1q_s16(elems)); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1] + }; + return v_int16x8(msa_ld1q_s16(elems)); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(msa_combine_s16(msa_ld1_s16(tab + idx[0]), msa_ld1_s16(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_int32x4(msa_ld1q_s32(elems)); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(msa_combine_s32(msa_ld1_s32(tab + idx[0]), msa_ld1_s32(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(msa_ld1q_s32(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(msa_combine_s64(msa_create_s64(tab[idx[0]]), msa_create_s64(tab[idx[1]]))); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(msa_ld1q_s64(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_float32x4(msa_ld1q_f32(elems)); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) +{ + uint64 CV_DECL_ALIGNED(32) elems[2] = + { + *(uint64*)(tab + idx[0]), + *(uint64*)(tab + idx[1]) + }; + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ld1q_u64(elems))); +} +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) +{ + return v_float32x4(msa_ld1q_f32(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + unsigned CV_DECL_ALIGNED(32) elems[4] = + { + tab[msa_getq_lane_s32(idxvec.val, 0)], + tab[msa_getq_lane_s32(idxvec.val, 1)], + tab[msa_getq_lane_s32(idxvec.val, 2)], + tab[msa_getq_lane_s32(idxvec.val, 3)] + }; + return v_uint32x4(msa_ld1q_u32(elems)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + v4f32 xy02 = msa_combine_f32(msa_ld1_f32(tab + idx[0]), msa_ld1_f32(tab + idx[2])); + v4f32 xy13 = msa_combine_f32(msa_ld1_f32(tab + idx[1]), msa_ld1_f32(tab + idx[3])); + x = v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, xy13), MSA_TPV_REINTERPRET(v4i32, xy02)))); + y = v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, xy13), MSA_TPV_REINTERPRET(v4i32, xy02)))); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0705060403010200, 0x0F0D0E0C0B090A08}), msa_dupq_n_s8(0), vec.val)); + return c; +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0703060205010400, 0x0F0B0E0A0D090C08}), msa_dupq_n_s8(0), vec.val)); + return c; +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0003000100020000, 0x0007000500060004}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } + +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0005000100040000, 0x0007000300060002}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + v_int32x4 c; + c.val[0] = vec.val[0]; + c.val[1] = vec.val[2]; + c.val[2] = vec.val[1]; + c.val[3] = vec.val[3]; + return c; +} + +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0908060504020100, 0x131211100E0D0C0A}), msa_dupq_n_s8(0), vec.val)); + return c; +} + +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0004000200010000, 0x0009000800060005}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[idx[0]], + tab[idx[1]] + }; + return v_float64x2(msa_ld1q_f64(elems)); +} + +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(msa_ld1q_f64(tab + idx[0])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + v2f64 xy0 = msa_ld1q_f64(tab + idx[0]); + v2f64 xy1 = msa_ld1q_f64(tab + idx[1]); + x = v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ilvevq_s64(MSA_TPV_REINTERPRET(v2i64, xy1), MSA_TPV_REINTERPRET(v2i64, xy0)))); + y = v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ilvodq_s64(MSA_TPV_REINTERPRET(v2i64, xy1), MSA_TPV_REINTERPRET(v2i64, xy0)))); +} + +template +inline typename _Tp::lane_type v_extract_n(const _Tp& a) +{ + return v_rotate_right(a).get0(); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& a) +{ + return v_setall_u32(v_extract_n(a)); +} +template +inline v_int32x4 v_broadcast_element(const v_int32x4& a) +{ + return v_setall_s32(v_extract_n(a)); +} +template +inline v_float32x4 v_broadcast_element(const v_float32x4& a) +{ + return v_setall_f32(v_extract_n(a)); +} + +////// FP16 support /////// +#if CV_FP16 +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ +#ifndef msa_ld1_f16 + v4f16 v = (v4f16)msa_ld1_s16((const short*)ptr); +#else + v4f16 v = msa_ld1_f16((const __fp16*)ptr); +#endif + return v_float32x4(msa_cvt_f32_f16(v)); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + v4f16 hv = msa_cvt_f16_f32(v.val); + +#ifndef msa_st1_f16 + msa_st1_s16((short*)ptr, (int16x4_t)hv); +#else + msa_st1_f16((__fp16*)ptr, hv); +#endif +} +#else +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + float buf[4]; + for( int i = 0; i < 4; i++ ) + buf[i] = (float)ptr[i]; + return v_load(buf); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + float buf[4]; + v_store(buf, v); + for( int i = 0; i < 4; i++ ) + ptr[i] = (float16_t)buf[i]; +} +#endif + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_neon.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_neon.hpp new file mode 100644 index 0000000..280691b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_neon.hpp @@ -0,0 +1,2346 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_NEON_HPP +#define OPENCV_HAL_INTRIN_NEON_HPP + +#include +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define CV_SIMD128 1 +#if defined(__aarch64__) || defined(_M_ARM64) +#define CV_SIMD128_64F 1 +#else +#define CV_SIMD128_64F 0 +#endif + +// TODO +#define CV_NEON_DOT 0 + +//////////// Utils //////////// + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { c = vuzp1q_##suffix(a, b); d = vuzp2q_##suffix(a, b); } +#define OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv&a, const _Tpv&b, _Tpv& c, _Tpv& d) \ + { c = vuzp1_##suffix(a, b); d = vuzp2_##suffix(a, b); } +#else +#define OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { _Tpvx2 ab = vuzpq_##suffix(a, b); c = ab.val[0]; d = ab.val[1]; } +#define OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { _Tpvx2 ab = vuzp_##suffix(a, b); c = ab.val[0]; d = ab.val[1]; } +#endif + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv, suffix) \ + template static inline \ + _Tpv vreinterpretq_##suffix##_f64(T a) { return (_Tpv) a; } \ + template static inline \ + float64x2_t vreinterpretq_f64_##suffix(T a) { return (float64x2_t) a; } +#else +#define OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv, suffix) +#endif + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv##_t, _Tpv##x2_t, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpvl##_t, _Tpvl##x2_t, suffix) \ + OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv##_t, suffix) + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv##_t, suffix) + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_F64(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv##_t, _Tpv##x2_t, suffix) + +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint8x16, uint8x8, u8) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int8x16, int8x8, s8) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint16x8, uint16x4, u16) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int16x8, int16x4, s16) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint32x4, uint32x2, u32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int32x4, int32x2, s32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(float32x4, float32x2, f32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(uint64x2, uint64x1, u64) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(int64x2, int64x1, s64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_F64(float64x2, float64x1,f64) +#endif + +//////////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + + v_uint8x16() {} + explicit v_uint8x16(uint8x16_t v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = vld1q_u8(v); + } + uchar get0() const + { + return vgetq_lane_u8(val, 0); + } + + uint8x16_t val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + + v_int8x16() {} + explicit v_int8x16(int8x16_t v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = vld1q_s8(v); + } + schar get0() const + { + return vgetq_lane_s8(val, 0); + } + + int8x16_t val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + + v_uint16x8() {} + explicit v_uint16x8(uint16x8_t v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = vld1q_u16(v); + } + ushort get0() const + { + return vgetq_lane_u16(val, 0); + } + + uint16x8_t val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + + v_int16x8() {} + explicit v_int16x8(int16x8_t v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = vld1q_s16(v); + } + short get0() const + { + return vgetq_lane_s16(val, 0); + } + + int16x8_t val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + enum { nlanes = 4 }; + + v_uint32x4() {} + explicit v_uint32x4(uint32x4_t v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + unsigned v[] = {v0, v1, v2, v3}; + val = vld1q_u32(v); + } + unsigned get0() const + { + return vgetq_lane_u32(val, 0); + } + + uint32x4_t val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + + v_int32x4() {} + explicit v_int32x4(int32x4_t v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = vld1q_s32(v); + } + int get0() const + { + return vgetq_lane_s32(val, 0); + } + int32x4_t val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + + v_float32x4() {} + explicit v_float32x4(float32x4_t v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = vld1q_f32(v); + } + float get0() const + { + return vgetq_lane_f32(val, 0); + } + float32x4_t val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + + v_uint64x2() {} + explicit v_uint64x2(uint64x2_t v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = vld1q_u64(v); + } + uint64 get0() const + { + return vgetq_lane_u64(val, 0); + } + uint64x2_t val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + + v_int64x2() {} + explicit v_int64x2(int64x2_t v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = vld1q_s64(v); + } + int64 get0() const + { + return vgetq_lane_s64(val, 0); + } + int64x2_t val; +}; + +#if CV_SIMD128_64F +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + + v_float64x2() {} + explicit v_float64x2(float64x2_t v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = vld1q_f64(v); + } + double get0() const + { + return vgetq_lane_f64(val, 0); + } + float64x2_t val; +}; +#endif + +#define OPENCV_HAL_IMPL_NEON_INIT(_Tpv, _Tp, suffix) \ +inline v_##_Tpv v_setzero_##suffix() { return v_##_Tpv(vdupq_n_##suffix((_Tp)0)); } \ +inline v_##_Tpv v_setall_##suffix(_Tp v) { return v_##_Tpv(vdupq_n_##suffix(v)); } \ +inline _Tpv##_t vreinterpretq_##suffix##_##suffix(_Tpv##_t v) { return v; } \ +inline v_uint8x16 v_reinterpret_as_u8(const v_##_Tpv& v) { return v_uint8x16(vreinterpretq_u8_##suffix(v.val)); } \ +inline v_int8x16 v_reinterpret_as_s8(const v_##_Tpv& v) { return v_int8x16(vreinterpretq_s8_##suffix(v.val)); } \ +inline v_uint16x8 v_reinterpret_as_u16(const v_##_Tpv& v) { return v_uint16x8(vreinterpretq_u16_##suffix(v.val)); } \ +inline v_int16x8 v_reinterpret_as_s16(const v_##_Tpv& v) { return v_int16x8(vreinterpretq_s16_##suffix(v.val)); } \ +inline v_uint32x4 v_reinterpret_as_u32(const v_##_Tpv& v) { return v_uint32x4(vreinterpretq_u32_##suffix(v.val)); } \ +inline v_int32x4 v_reinterpret_as_s32(const v_##_Tpv& v) { return v_int32x4(vreinterpretq_s32_##suffix(v.val)); } \ +inline v_uint64x2 v_reinterpret_as_u64(const v_##_Tpv& v) { return v_uint64x2(vreinterpretq_u64_##suffix(v.val)); } \ +inline v_int64x2 v_reinterpret_as_s64(const v_##_Tpv& v) { return v_int64x2(vreinterpretq_s64_##suffix(v.val)); } \ +inline v_float32x4 v_reinterpret_as_f32(const v_##_Tpv& v) { return v_float32x4(vreinterpretq_f32_##suffix(v.val)); } + +OPENCV_HAL_IMPL_NEON_INIT(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_INIT(int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_INIT(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_INIT(int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_INIT(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_INIT(int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_INIT(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_INIT(int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_INIT(float32x4, float, f32) +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_INIT_64(_Tpv, suffix) \ +inline v_float64x2 v_reinterpret_as_f64(const v_##_Tpv& v) { return v_float64x2(vreinterpretq_f64_##suffix(v.val)); } +OPENCV_HAL_IMPL_NEON_INIT(float64x2, double, f64) +OPENCV_HAL_IMPL_NEON_INIT_64(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_INIT_64(int8x16, s8) +OPENCV_HAL_IMPL_NEON_INIT_64(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_INIT_64(int16x8, s16) +OPENCV_HAL_IMPL_NEON_INIT_64(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_INIT_64(int32x4, s32) +OPENCV_HAL_IMPL_NEON_INIT_64(uint64x2, u64) +OPENCV_HAL_IMPL_NEON_INIT_64(int64x2, s64) +OPENCV_HAL_IMPL_NEON_INIT_64(float32x4, f32) +OPENCV_HAL_IMPL_NEON_INIT_64(float64x2, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_PACK(_Tpvec, _Tp, hreg, suffix, _Tpwvec, pack, mov, rshr) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + hreg a1 = mov(a.val), b1 = mov(b.val); \ + return _Tpvec(vcombine_##suffix(a1, b1)); \ +} \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = mov(a.val); \ + vst1_##suffix(ptr, a1); \ +} \ +template inline \ +_Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + hreg a1 = rshr(a.val, n); \ + hreg b1 = rshr(b.val, n); \ + return _Tpvec(vcombine_##suffix(a1, b1)); \ +} \ +template inline \ +void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = rshr(a.val, n); \ + vst1_##suffix(ptr, a1); \ +} + +OPENCV_HAL_IMPL_NEON_PACK(v_uint8x16, uchar, uint8x8_t, u8, v_uint16x8, pack, vqmovn_u16, vqrshrn_n_u16) +OPENCV_HAL_IMPL_NEON_PACK(v_int8x16, schar, int8x8_t, s8, v_int16x8, pack, vqmovn_s16, vqrshrn_n_s16) +OPENCV_HAL_IMPL_NEON_PACK(v_uint16x8, ushort, uint16x4_t, u16, v_uint32x4, pack, vqmovn_u32, vqrshrn_n_u32) +OPENCV_HAL_IMPL_NEON_PACK(v_int16x8, short, int16x4_t, s16, v_int32x4, pack, vqmovn_s32, vqrshrn_n_s32) +OPENCV_HAL_IMPL_NEON_PACK(v_uint32x4, unsigned, uint32x2_t, u32, v_uint64x2, pack, vmovn_u64, vrshrn_n_u64) +OPENCV_HAL_IMPL_NEON_PACK(v_int32x4, int, int32x2_t, s32, v_int64x2, pack, vmovn_s64, vrshrn_n_s64) + +OPENCV_HAL_IMPL_NEON_PACK(v_uint8x16, uchar, uint8x8_t, u8, v_int16x8, pack_u, vqmovun_s16, vqrshrun_n_s16) +OPENCV_HAL_IMPL_NEON_PACK(v_uint16x8, ushort, uint16x4_t, u16, v_int32x4, pack_u, vqmovun_s32, vqrshrun_n_s32) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + uint8x16_t ab = vcombine_u8(vmovn_u16(a.val), vmovn_u16(b.val)); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + uint16x8_t nab = vcombine_u16(vmovn_u32(a.val), vmovn_u32(b.val)); + uint16x8_t ncd = vcombine_u16(vmovn_u32(c.val), vmovn_u32(d.val)); + return v_uint8x16(vcombine_u8(vmovn_u16(nab), vmovn_u16(ncd))); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + uint32x4_t ab = vcombine_u32(vmovn_u64(a.val), vmovn_u64(b.val)); + uint32x4_t cd = vcombine_u32(vmovn_u64(c.val), vmovn_u64(d.val)); + uint32x4_t ef = vcombine_u32(vmovn_u64(e.val), vmovn_u64(f.val)); + uint32x4_t gh = vcombine_u32(vmovn_u64(g.val), vmovn_u64(h.val)); + + uint16x8_t abcd = vcombine_u16(vmovn_u32(ab), vmovn_u32(cd)); + uint16x8_t efgh = vcombine_u16(vmovn_u32(ef), vmovn_u32(gh)); + return v_uint8x16(vcombine_u8(vmovn_u16(abcd), vmovn_u16(efgh))); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + float32x2_t vl = vget_low_f32(v.val), vh = vget_high_f32(v.val); + float32x4_t res = vmulq_lane_f32(m0.val, vl, 0); + res = vmlaq_lane_f32(res, m1.val, vl, 1); + res = vmlaq_lane_f32(res, m2.val, vh, 0); + res = vmlaq_lane_f32(res, m3.val, vh, 1); + return v_float32x4(res); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + float32x2_t vl = vget_low_f32(v.val), vh = vget_high_f32(v.val); + float32x4_t res = vmulq_lane_f32(m0.val, vl, 0); + res = vmlaq_lane_f32(res, m1.val, vl, 1); + res = vmlaq_lane_f32(res, m2.val, vh, 0); + res = vaddq_f32(res, a.val); + return v_float32x4(res); +} + +#define OPENCV_HAL_IMPL_NEON_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint8x16, vqaddq_u8) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint8x16, vqsubq_u8) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int8x16, vqaddq_s8) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int8x16, vqsubq_s8) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint16x8, vqaddq_u16) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint16x8, vqsubq_u16) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int16x8, vqaddq_s16) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int16x8, vqsubq_s16) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int32x4, vaddq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int32x4, vsubq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_int32x4, vmulq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint32x4, vaddq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint32x4, vsubq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_uint32x4, vmulq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_float32x4, vaddq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_float32x4, vsubq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_float32x4, vmulq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int64x2, vaddq_s64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int64x2, vsubq_s64) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint64x2, vaddq_u64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint64x2, vsubq_u64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_OP(/, v_float32x4, vdivq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_float64x2, vaddq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_float64x2, vsubq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_float64x2, vmulq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(/, v_float64x2, vdivq_f64) +#else +inline v_float32x4 operator / (const v_float32x4& a, const v_float32x4& b) +{ + float32x4_t reciprocal = vrecpeq_f32(b.val); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + return v_float32x4(vmulq_f32(a.val, reciprocal)); +} +inline v_float32x4& operator /= (v_float32x4& a, const v_float32x4& b) +{ + float32x4_t reciprocal = vrecpeq_f32(b.val); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + a.val = vmulq_f32(a.val, reciprocal); + return a; +} +#endif + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_NEON_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_uint16x8, v_uint32x4) + +// Multiply and expand +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + c.val = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + d.val = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val)); +} + +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + c.val = vmull_u8(vget_low_u8(a.val), vget_low_u8(b.val)); + d.val = vmull_u8(vget_high_u8(a.val), vget_high_u8(b.val)); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + c.val = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + d.val = vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + c.val = vmull_u16(vget_low_u16(a.val), vget_low_u16(b.val)); + d.val = vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + c.val = vmull_u32(vget_low_u32(a.val), vget_low_u32(b.val)); + d.val = vmull_u32(vget_high_u32(a.val), vget_high_u32(b.val)); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + return v_int16x8(vcombine_s16( + vshrn_n_s32(vmull_s16( vget_low_s16(a.val), vget_low_s16(b.val)), 16), + vshrn_n_s32(vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)), 16) + )); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint16x8(vcombine_u16( + vshrn_n_u32(vmull_u16( vget_low_u16(a.val), vget_low_u16(b.val)), 16), + vshrn_n_u32(vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)), 16) + )); +} + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ + int16x8_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int16x4_t a0 = vget_low_s16(uzp1); + int16x4_t b0 = vget_high_s16(uzp1); + int16x4_t a1 = vget_low_s16(uzp2); + int16x4_t b1 = vget_high_s16(uzp2); + int32x4_t p = vmull_s16(a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ + int16x8_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int16x4_t a0 = vget_low_s16(uzp1); + int16x4_t b0 = vget_high_s16(uzp1); + int16x4_t a1 = vget_low_s16(uzp2); + int16x4_t b1 = vget_high_s16(uzp2); + int32x4_t p = vmlal_s16(c.val, a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ + int32x4_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int32x2_t a0 = vget_low_s32(uzp1); + int32x2_t b0 = vget_high_s32(uzp1); + int32x2_t a1 = vget_low_s32(uzp2); + int32x2_t b1 = vget_high_s32(uzp2); + int64x2_t p = vmull_s32(a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ + int32x4_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int32x2_t a0 = vget_low_s32(uzp1); + int32x2_t b0 = vget_high_s32(uzp1); + int32x2_t a1 = vget_low_s32(uzp2); + int32x2_t b1 = vget_high_s32(uzp2); + int64x2_t p = vmlal_s32(c.val, a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val)); +#else + const uint8x16_t zero = vreinterpretq_u8_u32(vdupq_n_u32(0)); + const uint8x16_t mask = vreinterpretq_u8_u32(vdupq_n_u32(0x00FF00FF)); + const uint16x8_t zero32 = vreinterpretq_u16_u32(vdupq_n_u32(0)); + const uint16x8_t mask32 = vreinterpretq_u16_u32(vdupq_n_u32(0x0000FFFF)); + + uint16x8_t even = vmulq_u16(vreinterpretq_u16_u8(vbslq_u8(mask, a.val, zero)), + vreinterpretq_u16_u8(vbslq_u8(mask, b.val, zero))); + uint16x8_t odd = vmulq_u16(vshrq_n_u16(vreinterpretq_u16_u8(a.val), 8), + vshrq_n_u16(vreinterpretq_u16_u8(b.val), 8)); + + uint32x4_t s0 = vaddq_u32(vreinterpretq_u32_u16(vbslq_u16(mask32, even, zero32)), + vreinterpretq_u32_u16(vbslq_u16(mask32, odd, zero32))); + uint32x4_t s1 = vaddq_u32(vshrq_n_u32(vreinterpretq_u32_u16(even), 16), + vshrq_n_u32(vreinterpretq_u32_u16(odd), 16)); + return v_uint32x4(vaddq_u32(s0, s1)); +#endif +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, + const v_uint32x4& c) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(c.val, a.val, b.val)); +#else + return v_dotprod_expand(a, b) + c; +#endif +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val)); +#else + int16x8_t p0 = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + int16x8_t p1 = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val)); + int16x8_t uzp1, uzp2; + _v128_unzip(p0, p1, uzp1, uzp2); + int16x8_t sum = vaddq_s16(uzp1, uzp2); + int16x4_t uzpl1, uzpl2; + _v128_unzip(vget_low_s16(sum), vget_high_s16(sum), uzpl1, uzpl2); + return v_int32x4(vaddl_s16(uzpl1, uzpl2)); +#endif +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, + const v_int32x4& c) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(c.val, a.val, b.val)); +#else + return v_dotprod_expand(a, b) + c; +#endif +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + const uint16x8_t zero = vreinterpretq_u16_u32(vdupq_n_u32(0)); + const uint16x8_t mask = vreinterpretq_u16_u32(vdupq_n_u32(0x0000FFFF)); + + uint32x4_t even = vmulq_u32(vreinterpretq_u32_u16(vbslq_u16(mask, a.val, zero)), + vreinterpretq_u32_u16(vbslq_u16(mask, b.val, zero))); + uint32x4_t odd = vmulq_u32(vshrq_n_u32(vreinterpretq_u32_u16(a.val), 16), + vshrq_n_u32(vreinterpretq_u32_u16(b.val), 16)); + uint32x4_t uzp1, uzp2; + _v128_unzip(even, odd, uzp1, uzp2); + uint64x2_t s0 = vaddl_u32(vget_low_u32(uzp1), vget_high_u32(uzp1)); + uint64x2_t s1 = vaddl_u32(vget_low_u32(uzp2), vget_high_u32(uzp2)); + return v_uint64x2(vaddq_u64(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + int32x4_t p0 = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + int32x4_t p1 = vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)); + + int32x4_t uzp1, uzp2; + _v128_unzip(p0, p1, uzp1, uzp2); + int32x4_t sum = vaddq_s32(uzp1, uzp2); + + int32x2_t uzpl1, uzpl2; + _v128_unzip(vget_low_s32(sum), vget_high_s32(sum), uzpl1, uzpl2); + return v_int64x2(vaddl_s32(uzpl1, uzpl2)); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, + const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +#if CV_SIMD128_64F +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, + const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } +#endif + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ + int16x4_t a0 = vget_low_s16(a.val); + int16x4_t a1 = vget_high_s16(a.val); + int16x4_t b0 = vget_low_s16(b.val); + int16x4_t b1 = vget_high_s16(b.val); + int32x4_t p = vmull_s16(a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ + int16x4_t a0 = vget_low_s16(a.val); + int16x4_t a1 = vget_high_s16(a.val); + int16x4_t b0 = vget_low_s16(b.val); + int16x4_t b1 = vget_high_s16(b.val); + int32x4_t p = vmlal_s16(c.val, a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ + int32x2_t a0 = vget_low_s32(a.val); + int32x2_t a1 = vget_high_s32(a.val); + int32x2_t b0 = vget_low_s32(b.val); + int32x2_t b1 = vget_high_s32(b.val); + int64x2_t p = vmull_s32(a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ + int32x2_t a0 = vget_low_s32(a.val); + int32x2_t a1 = vget_high_s32(a.val); + int32x2_t b0 = vget_low_s32(b.val); + int32x2_t b1 = vget_high_s32(b.val); + int64x2_t p = vmlal_s32(c.val, a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val)); +#else + uint16x8_t p0 = vmull_u8(vget_low_u8(a.val), vget_low_u8(b.val)); + uint16x8_t p1 = vmull_u8(vget_high_u8(a.val), vget_high_u8(b.val)); + uint32x4_t s0 = vaddl_u16(vget_low_u16(p0), vget_low_u16(p1)); + uint32x4_t s1 = vaddl_u16(vget_high_u16(p0), vget_high_u16(p1)); + return v_uint32x4(vaddq_u32(s0, s1)); +#endif +} +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(c.val, a.val, b.val)); +#else + return v_dotprod_expand_fast(a, b) + c; +#endif +} + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val)); +#else + int16x8_t prod = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + prod = vmlal_s8(prod, vget_high_s8(a.val), vget_high_s8(b.val)); + return v_int32x4(vaddl_s16(vget_low_s16(prod), vget_high_s16(prod))); +#endif +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(c.val, a.val, b.val)); +#else + return v_dotprod_expand_fast(a, b) + c; +#endif +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ + uint32x4_t p0 = vmull_u16(vget_low_u16(a.val), vget_low_u16(b.val)); + uint32x4_t p1 = vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)); + uint64x2_t s0 = vaddl_u32(vget_low_u32(p0), vget_high_u32(p0)); + uint64x2_t s1 = vaddl_u32(vget_low_u32(p1), vget_high_u32(p1)); + return v_uint64x2(vaddq_u64(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + int32x4_t prod = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + prod = vmlal_s16(prod, vget_high_s16(a.val), vget_high_s16(b.val)); + return v_int64x2(vaddl_s32(vget_low_s32(prod), vget_high_s32(prod))); +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +#if CV_SIMD128_64F +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod_fast(a, b)); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } +#endif + + +#define OPENCV_HAL_IMPL_NEON_LOGIC_OP(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(&, _Tpvec, vandq_##suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(|, _Tpvec, vorrq_##suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(^, _Tpvec, veorq_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { \ + return _Tpvec(vreinterpretq_##suffix##_u8(vmvnq_u8(vreinterpretq_u8_##suffix(a.val)))); \ + } + +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint8x16, u8) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int8x16, s8) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint16x8, u16) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int16x8, s16) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint32x4, u32) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int32x4, s32) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint64x2, u64) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int64x2, s64) + +#define OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(bin_op, intrin) \ +inline v_float32x4 operator bin_op (const v_float32x4& a, const v_float32x4& b) \ +{ \ + return v_float32x4(vreinterpretq_f32_s32(intrin(vreinterpretq_s32_f32(a.val), vreinterpretq_s32_f32(b.val)))); \ +} \ +inline v_float32x4& operator bin_op##= (v_float32x4& a, const v_float32x4& b) \ +{ \ + a.val = vreinterpretq_f32_s32(intrin(vreinterpretq_s32_f32(a.val), vreinterpretq_s32_f32(b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(&, vandq_s32) +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(|, vorrq_s32) +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(^, veorq_s32) + +inline v_float32x4 operator ~ (const v_float32x4& a) +{ + return v_float32x4(vreinterpretq_f32_s32(vmvnq_s32(vreinterpretq_s32_f32(a.val)))); +} + +#if CV_SIMD128_64F +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ + return v_float32x4(vsqrtq_f32(x.val)); +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + v_float32x4 one = v_setall_f32(1.0f); + return one / v_sqrt(x); +} +#else +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ + float32x4_t x1 = vmaxq_f32(x.val, vdupq_n_f32(FLT_MIN)); + float32x4_t e = vrsqrteq_f32(x1); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x1, e), e), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x1, e), e), e); + return v_float32x4(vmulq_f32(x.val, e)); +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + float32x4_t e = vrsqrteq_f32(x.val); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x.val, e), e), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x.val, e), e), e); + return v_float32x4(e); +} +#endif + +#define OPENCV_HAL_IMPL_NEON_ABS(_Tpuvec, _Tpsvec, usuffix, ssuffix) \ +inline _Tpuvec v_abs(const _Tpsvec& a) { return v_reinterpret_as_##usuffix(_Tpsvec(vabsq_##ssuffix(a.val))); } + +OPENCV_HAL_IMPL_NEON_ABS(v_uint8x16, v_int8x16, u8, s8) +OPENCV_HAL_IMPL_NEON_ABS(v_uint16x8, v_int16x8, u16, s16) +OPENCV_HAL_IMPL_NEON_ABS(v_uint32x4, v_int32x4, u32, s32) + +inline v_float32x4 v_abs(v_float32x4 x) +{ return v_float32x4(vabsq_f32(x.val)); } + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(bin_op, intrin) \ +inline v_float64x2 operator bin_op (const v_float64x2& a, const v_float64x2& b) \ +{ \ + return v_float64x2(vreinterpretq_f64_s64(intrin(vreinterpretq_s64_f64(a.val), vreinterpretq_s64_f64(b.val)))); \ +} \ +inline v_float64x2& operator bin_op##= (v_float64x2& a, const v_float64x2& b) \ +{ \ + a.val = vreinterpretq_f64_s64(intrin(vreinterpretq_s64_f64(a.val), vreinterpretq_s64_f64(b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(&, vandq_s64) +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(|, vorrq_s64) +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(^, veorq_s64) + +inline v_float64x2 operator ~ (const v_float64x2& a) +{ + return v_float64x2(vreinterpretq_f64_s32(vmvnq_s32(vreinterpretq_s32_f64(a.val)))); +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ + return v_float64x2(vsqrtq_f64(x.val)); +} + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ + v_float64x2 one = v_setall_f64(1.0f); + return one / v_sqrt(x); +} + +inline v_float64x2 v_abs(v_float64x2 x) +{ return v_float64x2(vabsq_f64(x.val)); } +#endif + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_NEON_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_min, vminq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_max, vmaxq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_min, vminq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_max, vmaxq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_min, vminq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_max, vmaxq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_min, vminq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_max, vmaxq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_min, vminq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_max, vmaxq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int32x4, v_min, vminq_s32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int32x4, v_max, vmaxq_s32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_min, vminq_f32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_max, vmaxq_f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_min, vminq_f64) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_max, vmaxq_f64) +#endif + +#if CV_SIMD128_64F +inline int64x2_t vmvnq_s64(int64x2_t a) +{ + int64x2_t vx = vreinterpretq_s64_u32(vdupq_n_u32(0xFFFFFFFF)); + return veorq_s64(a, vx); +} +inline uint64x2_t vmvnq_u64(uint64x2_t a) +{ + uint64x2_t vx = vreinterpretq_u64_u32(vdupq_n_u32(0xFFFFFFFF)); + return veorq_u64(a, vx); +} +#endif +#define OPENCV_HAL_IMPL_NEON_INT_CMP_OP(_Tpvec, cast, suffix, not_suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vceqq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vmvnq_##not_suffix(vceqq_##suffix(a.val, b.val)))); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcltq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcgtq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcleq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcgeq_##suffix(a.val, b.val))); } + +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint8x16, OPENCV_HAL_NOP, u8, u8) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int8x16, vreinterpretq_s8_u8, s8, u8) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint16x8, OPENCV_HAL_NOP, u16, u16) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int16x8, vreinterpretq_s16_u16, s16, u16) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint32x4, OPENCV_HAL_NOP, u32, u32) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int32x4, vreinterpretq_s32_u32, s32, u32) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_float32x4, vreinterpretq_f32_u32, f32, u32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint64x2, OPENCV_HAL_NOP, u64, u64) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int64x2, vreinterpretq_s64_u64, s64, u64) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_float64x2, vreinterpretq_f64_u64, f64, u64) +#endif + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(vreinterpretq_f32_u32(vceqq_f32(a.val, a.val))); } +#if CV_SIMD128_64F +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(vreinterpretq_f64_u64(vceqq_f64(a.val, a.val))); } +#endif + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_add_wrap, vaddq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_add_wrap, vaddq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_add_wrap, vaddq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_add_wrap, vaddq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_sub_wrap, vsubq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_sub_wrap, vsubq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_sub_wrap, vsubq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_sub_wrap, vsubq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_mul_wrap, vmulq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_mul_wrap, vmulq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_mul_wrap, vmulq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_mul_wrap, vmulq_s16) + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_absdiff, vabdq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_absdiff, vabdq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_absdiff, vabdq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_absdiff, vabdq_f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_absdiff, vabdq_f64) +#endif + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ return v_int8x16(vqabsq_s8(vqsubq_s8(a.val, b.val))); } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_int16x8(vqabsq_s16(vqsubq_s16(a.val, b.val))); } + +#define OPENCV_HAL_IMPL_NEON_BIN_FUNC2(_Tpvec, _Tpvec2, cast, func, intrin) \ +inline _Tpvec2 func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec2(cast(intrin(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int8x16, v_uint8x16, vreinterpretq_u8_s8, v_absdiff, vabdq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int16x8, v_uint16x8, vreinterpretq_u16_s16, v_absdiff, vabdq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int32x4, v_uint32x4, vreinterpretq_u32_s32, v_absdiff, vabdq_s32) + +inline v_float32x4 v_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 x(vmlaq_f32(vmulq_f32(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float32x4 v_sqr_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + return v_float32x4(vmlaq_f32(vmulq_f32(a.val, a.val), b.val, b.val)); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ +#if CV_SIMD128_64F + // ARMv8, which adds support for 64-bit floating-point (so CV_SIMD128_64F is defined), + // also adds FMA support both for single- and double-precision floating-point vectors + return v_float32x4(vfmaq_f32(c.val, a.val, b.val)); +#else + return v_float32x4(vmlaq_f32(c.val, a.val, b.val)); +#endif +} + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_int32x4(vmlaq_s32(c.val, a.val, b.val)); +} + +inline v_float32x4 v_muladd(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +#if CV_SIMD128_64F +inline v_float64x2 v_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 x(vaddq_f64(vmulq_f64(a.val, a.val), vmulq_f64(b.val, b.val))); + return v_sqrt(x); +} + +inline v_float64x2 v_sqr_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + return v_float64x2(vaddq_f64(vmulq_f64(a.val, a.val), vmulq_f64(b.val, b.val))); +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_float64x2(vfmaq_f64(c.val, a.val, b.val)); +} + +inline v_float64x2 v_muladd(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_fma(a, b, c); +} +#endif + +// trade efficiency for convenience +#define OPENCV_HAL_IMPL_NEON_SHIFT_OP(_Tpvec, suffix, _Tps, ssuffix) \ +inline _Tpvec operator << (const _Tpvec& a, int n) \ +{ return _Tpvec(vshlq_##suffix(a.val, vdupq_n_##ssuffix((_Tps)n))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int n) \ +{ return _Tpvec(vshlq_##suffix(a.val, vdupq_n_##ssuffix((_Tps)-n))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(vshlq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(vshrq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ return _Tpvec(vrshrq_n_##suffix(a.val, n)); } + +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint8x16, u8, schar, s8) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int8x16, s8, schar, s8) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint16x8, u16, short, s16) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int16x8, s16, short, s16) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint32x4, u32, int, s32) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int32x4, s32, int, s32) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint64x2, u64, int64, s64) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int64x2, s64, int64, s64) + +#define OPENCV_HAL_IMPL_NEON_ROTATE_OP(_Tpvec, suffix) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ return _Tpvec(vextq_##suffix(a.val, vdupq_n_##suffix(0), n)); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ return _Tpvec(vextq_##suffix(vdupq_n_##suffix(0), a.val, _Tpvec::nlanes - n)); } \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ return a; } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vextq_##suffix(a.val, b.val, n)); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vextq_##suffix(b.val, a.val, _Tpvec::nlanes - n)); } \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ CV_UNUSED(b); return a; } + +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint8x16, u8) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int8x16, s8) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint16x8, u16) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int16x8, s16) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint32x4, u32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int32x4, s32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_float32x4, f32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint64x2, u64) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int64x2, s64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_float64x2, f64) +#endif + +#if defined(__clang__) && defined(__aarch64__) +// avoid LD2 instruction. details: https://github.com/opencv/opencv/issues/14863 +#define OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ \ +typedef uint64 CV_DECL_ALIGNED(1) unaligned_uint64; \ +uint64 v = *(unaligned_uint64*)ptr; \ +return _Tpvec(v_reinterpret_as_##suffix(v_uint64x2(v, (uint64)123456))); \ +} +#else +#define OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(vcombine_##suffix(vld1_##suffix(ptr), vdup_n_##suffix((_Tp)0))); } +#endif + +#define OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(vld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(vld1q_##suffix(ptr)); } \ +OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(vcombine_##suffix(vld1_##suffix(ptr0), vld1_##suffix(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ vst1_##suffix(ptr, vget_low_##suffix(a.val)); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ vst1_##suffix(ptr, vget_high_##suffix(a.val)); } + +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_float64x2, double, f64) +#endif + +inline unsigned v_reduce_sum(const v_uint8x16& a) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(a.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + int32x4_t t0 = vpaddlq_s16(vpaddlq_s8(a.val)); + int32x2_t t1 = vpadd_s32(vget_low_s32(t0), vget_high_s32(t0)); + return vget_lane_s32(vpadd_s32(t1, t1), 0); +} +inline unsigned v_reduce_sum(const v_uint16x8& a) +{ + uint32x4_t t0 = vpaddlq_u16(a.val); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline int v_reduce_sum(const v_int16x8& a) +{ + int32x4_t t0 = vpaddlq_s16(a.val); + int32x2_t t1 = vpadd_s32(vget_low_s32(t0), vget_high_s32(t0)); + return vget_lane_s32(vpadd_s32(t1, t1), 0); +} + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, a0),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_uint8x16, uint8x8, uchar, max, max, u8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_uint8x16, uint8x8, uchar, min, min, u8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_int8x16, int8x8, schar, max, max, s8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_int8x16, int8x8, schar, min, min, s8) + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, a0),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_uint16x8, uint16x4, ushort, max, max, u16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_uint16x8, uint16x4, ushort, min, min, u16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_int16x8, int16x4, short, max, max, s16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_int16x8, int16x4, short, min, min, s16) + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, vget_high_##suffix(a.val)),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, sum, add, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, max, max, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, min, min, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, sum, add, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, max, max, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, min, min, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, sum, add, f32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, max, max, f32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, min, min, f32) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ return vget_lane_u64(vadd_u64(vget_low_u64(a.val), vget_high_u64(a.val)),0); } +inline int64 v_reduce_sum(const v_int64x2& a) +{ return vget_lane_s64(vadd_s64(vget_low_s64(a.val), vget_high_s64(a.val)),0); } +#if CV_SIMD128_64F +inline double v_reduce_sum(const v_float64x2& a) +{ + return vgetq_lane_f64(a.val, 0) + vgetq_lane_f64(a.val, 1); +} +#endif + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + float32x4x2_t ab = vtrnq_f32(a.val, b.val); + float32x4x2_t cd = vtrnq_f32(c.val, d.val); + + float32x4_t u0 = vaddq_f32(ab.val[0], ab.val[1]); // a0+a1 b0+b1 a2+a3 b2+b3 + float32x4_t u1 = vaddq_f32(cd.val[0], cd.val[1]); // c0+c1 d0+d1 c2+c3 d2+d3 + + float32x4_t v0 = vcombine_f32(vget_low_f32(u0), vget_low_f32(u1)); + float32x4_t v1 = vcombine_f32(vget_high_f32(u0), vget_high_f32(u1)); + + return v_float32x4(vaddq_f32(v0, v1)); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(vabdq_u8(a.val, b.val))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(vabdq_s8(a.val, b.val)))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + uint32x4_t t0 = vpaddlq_u16(vabdq_u16(a.val, b.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + uint32x4_t t0 = vpaddlq_u16(vreinterpretq_u16_s16(vabdq_s16(a.val, b.val))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + uint32x4_t t0 = vabdq_u32(a.val, b.val); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + uint32x4_t t0 = vreinterpretq_u32_s32(vabdq_s32(a.val, b.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + float32x4_t t0 = vabdq_f32(a.val, b.val); + float32x2_t t1 = vpadd_f32(vget_low_f32(t0), vget_high_f32(t0)); + return vget_lane_f32(vpadd_f32(t1, t1), 0); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ return v_uint8x16(vcntq_u8(a.val)); } +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_uint8x16(vcntq_u8(vreinterpretq_u8_s8(a.val))); } +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ return v_uint16x8(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u16(a.val)))); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_uint16x8(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s16(a.val)))); } +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ return v_uint32x4(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u32(a.val))))); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_uint32x4(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s32(a.val))))); } +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ return v_uint64x2(vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(a.val)))))); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_uint64x2(vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s64(a.val)))))); } + +inline int v_signmask(const v_uint8x16& a) +{ + int8x8_t m0 = vcreate_s8(CV_BIG_UINT(0x0706050403020100)); + uint8x16_t v0 = vshlq_u8(vshrq_n_u8(a.val, 7), vcombine_s8(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(v0))); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 8); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_uint16x8& a) +{ + int16x4_t m0 = vcreate_s16(CV_BIG_UINT(0x0003000200010000)); + uint16x8_t v0 = vshlq_u16(vshrq_n_u16(a.val, 15), vcombine_s16(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(vpaddlq_u16(v0)); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 4); +} +inline int v_signmask(const v_int16x8& a) +{ return v_signmask(v_reinterpret_as_u16(a)); } + +inline int v_signmask(const v_uint32x4& a) +{ + int32x2_t m0 = vcreate_s32(CV_BIG_UINT(0x0000000100000000)); + uint32x4_t v0 = vshlq_u32(vshrq_n_u32(a.val, 31), vcombine_s32(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(v0); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 2); +} +inline int v_signmask(const v_int32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_uint64x2& a) +{ + int64x1_t m0 = vdup_n_s64(0); + uint64x2_t v0 = vshlq_u64(vshrq_n_u64(a.val, 63), vcombine_s64(m0, m0)); + return (int)vgetq_lane_u64(v0, 0) + ((int)vgetq_lane_u64(v0, 1) << 1); +} +inline int v_signmask(const v_int64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +#if CV_SIMD128_64F +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +#endif + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +#if CV_SIMD128_64F +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } +#endif + +#define OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(_Tpvec, suffix, shift) \ +inline bool v_check_all(const v_##_Tpvec& a) \ +{ \ + _Tpvec##_t v0 = vshrq_n_##suffix(vmvnq_##suffix(a.val), shift); \ + uint64x2_t v1 = vreinterpretq_u64_##suffix(v0); \ + return (vgetq_lane_u64(v1, 0) | vgetq_lane_u64(v1, 1)) == 0; \ +} \ +inline bool v_check_any(const v_##_Tpvec& a) \ +{ \ + _Tpvec##_t v0 = vshrq_n_##suffix(a.val, shift); \ + uint64x2_t v1 = vreinterpretq_u64_##suffix(v0); \ + return (vgetq_lane_u64(v1, 0) | vgetq_lane_u64(v1, 1)) != 0; \ +} + +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint8x16, u8, 7) +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint16x8, u16, 15) +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint32x4, u32, 31) + +inline bool v_check_all(const v_uint64x2& a) +{ + uint64x2_t v0 = vshrq_n_u64(a.val, 63); + return (vgetq_lane_u64(v0, 0) & vgetq_lane_u64(v0, 1)) == 1; +} +inline bool v_check_any(const v_uint64x2& a) +{ + uint64x2_t v0 = vshrq_n_u64(a.val, 63); + return (vgetq_lane_u64(v0, 0) | vgetq_lane_u64(v0, 1)) != 0; +} + +inline bool v_check_all(const v_int8x16& a) +{ return v_check_all(v_reinterpret_as_u8(a)); } +inline bool v_check_all(const v_int16x8& a) +{ return v_check_all(v_reinterpret_as_u16(a)); } +inline bool v_check_all(const v_int32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } + +inline bool v_check_any(const v_int8x16& a) +{ return v_check_any(v_reinterpret_as_u8(a)); } +inline bool v_check_any(const v_int16x8& a) +{ return v_check_any(v_reinterpret_as_u16(a)); } +inline bool v_check_any(const v_int32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } + +inline bool v_check_all(const v_int64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_int64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +#if CV_SIMD128_64F +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +#endif + +#define OPENCV_HAL_IMPL_NEON_SELECT(_Tpvec, suffix, usuffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(vbslq_##suffix(vreinterpretq_##usuffix##_##suffix(mask.val), a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_NEON_SELECT(v_uint8x16, u8, u8) +OPENCV_HAL_IMPL_NEON_SELECT(v_int8x16, s8, u8) +OPENCV_HAL_IMPL_NEON_SELECT(v_uint16x8, u16, u16) +OPENCV_HAL_IMPL_NEON_SELECT(v_int16x8, s16, u16) +OPENCV_HAL_IMPL_NEON_SELECT(v_uint32x4, u32, u32) +OPENCV_HAL_IMPL_NEON_SELECT(v_int32x4, s32, u32) +OPENCV_HAL_IMPL_NEON_SELECT(v_float32x4, f32, u32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_SELECT(v_float64x2, f64, u64) +#endif + +#define OPENCV_HAL_IMPL_NEON_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = vmovl_##suffix(vget_low_##suffix(a.val)); \ + b1.val = vmovl_##suffix(vget_high_##suffix(a.val)); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_##suffix(vget_low_##suffix(a.val))); \ +} \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_##suffix(vget_high_##suffix(a.val))); \ +} \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + return _Tpwvec(vmovl_##suffix(vld1_##suffix(ptr))); \ +} + +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint8x16, v_uint16x8, uchar, u8) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int8x16, v_int16x8, schar, s8) +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint16x8, v_uint32x4, ushort, u16) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int16x8, v_int32x4, short, s16) +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint32x4, v_uint64x2, uint, u32) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int32x4, v_int64x2, int, s32) + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + typedef unsigned int CV_DECL_ALIGNED(1) unaligned_uint; + uint8x8_t v0 = vcreate_u8(*(unaligned_uint*)ptr); + uint16x4_t v1 = vget_low_u16(vmovl_u8(v0)); + return v_uint32x4(vmovl_u16(v1)); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + typedef unsigned int CV_DECL_ALIGNED(1) unaligned_uint; + int8x8_t v0 = vcreate_s8(*(unaligned_uint*)ptr); + int16x4_t v1 = vget_low_s16(vmovl_s8(v0)); + return v_int32x4(vmovl_s16(v1)); +} + +#if defined(__aarch64__) || defined(_M_ARM64) +#define OPENCV_HAL_IMPL_NEON_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const v_##_Tpvec& a0, const v_##_Tpvec& a1, v_##_Tpvec& b0, v_##_Tpvec& b1) \ +{ \ + b0.val = vzip1q_##suffix(a0.val, a1.val); \ + b1.val = vzip2q_##suffix(a0.val, a1.val); \ +} \ +inline v_##_Tpvec v_combine_low(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val))); \ +} \ +inline v_##_Tpvec v_combine_high(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val))); \ +} \ +inline void v_recombine(const v_##_Tpvec& a, const v_##_Tpvec& b, v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + c.val = vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val)); \ + d.val = vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val)); \ +} +#else +#define OPENCV_HAL_IMPL_NEON_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const v_##_Tpvec& a0, const v_##_Tpvec& a1, v_##_Tpvec& b0, v_##_Tpvec& b1) \ +{ \ + _Tpvec##x2_t p = vzipq_##suffix(a0.val, a1.val); \ + b0.val = p.val[0]; \ + b1.val = p.val[1]; \ +} \ +inline v_##_Tpvec v_combine_low(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val))); \ +} \ +inline v_##_Tpvec v_combine_high(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val))); \ +} \ +inline void v_recombine(const v_##_Tpvec& a, const v_##_Tpvec& b, v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + c.val = vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val)); \ + d.val = vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val)); \ +} +#endif + +OPENCV_HAL_IMPL_NEON_UNPACKS(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_UNPACKS(int8x16, s8) +OPENCV_HAL_IMPL_NEON_UNPACKS(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_UNPACKS(int16x8, s16) +OPENCV_HAL_IMPL_NEON_UNPACKS(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_UNPACKS(int32x4, s32) +OPENCV_HAL_IMPL_NEON_UNPACKS(float32x4, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_UNPACKS(float64x2, f64) +#endif + +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + uint8x16_t vec = vrev64q_u8(a.val); + return v_uint8x16(vextq_u8(vec, vec, 8)); +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + uint16x8_t vec = vrev64q_u16(a.val); + return v_uint16x8(vextq_u16(vec, vec, 4)); +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + uint32x4_t vec = vrev64q_u32(a.val); + return v_uint32x4(vextq_u32(vec, vec, 2)); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + uint64x2_t vec = a.val; + uint64x1_t vec_lo = vget_low_u64(vec); + uint64x1_t vec_hi = vget_high_u64(vec); + return v_uint64x2(vcombine_u64(vec_hi, vec_lo)); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +#if CV_SIMD128_64F +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } +#endif + +#define OPENCV_HAL_IMPL_NEON_EXTRACT(_Tpvec, suffix) \ +template \ +inline v_##_Tpvec v_extract(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vextq_##suffix(a.val, b.val, s)); \ +} + +OPENCV_HAL_IMPL_NEON_EXTRACT(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_EXTRACT(int8x16, s8) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_EXTRACT(int16x8, s16) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_EXTRACT(int32x4, s32) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint64x2, u64) +OPENCV_HAL_IMPL_NEON_EXTRACT(int64x2, s64) +OPENCV_HAL_IMPL_NEON_EXTRACT(float32x4, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_EXTRACT(float64x2, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_EXTRACT_N(_Tpvec, _Tp, suffix) \ +template inline _Tp v_extract_n(_Tpvec v) { return vgetq_lane_##suffix(v.val, i); } + +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint32x4, uint, u32) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_float64x2, double, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_BROADCAST(_Tpvec, _Tp, suffix) \ +template inline _Tpvec v_broadcast_element(_Tpvec v) { _Tp t = v_extract_n(v); return v_setall_##suffix(t); } + +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint32x4, uint, u32) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BROADCAST(v_float64x2, double, f64) +#endif + +#if CV_SIMD128_64F +inline v_int32x4 v_round(const v_float32x4& a) +{ + float32x4_t a_ = a.val; + int32x4_t result; + __asm__ ("fcvtns %0.4s, %1.4s" + : "=w"(result) + : "w"(a_) + : /* No clobbers */); + return v_int32x4(result); +} +#else +inline v_int32x4 v_round(const v_float32x4& a) +{ + static const int32x4_t v_sign = vdupq_n_s32(1 << 31), + v_05 = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); + + int32x4_t v_addition = vorrq_s32(v_05, vandq_s32(v_sign, vreinterpretq_s32_f32(a.val))); + return v_int32x4(vcvtq_s32_f32(vaddq_f32(a.val, vreinterpretq_f32_s32(v_addition)))); +} +#endif +inline v_int32x4 v_floor(const v_float32x4& a) +{ + int32x4_t a1 = vcvtq_s32_f32(a.val); + uint32x4_t mask = vcgtq_f32(vcvtq_f32_s32(a1), a.val); + return v_int32x4(vaddq_s32(a1, vreinterpretq_s32_u32(mask))); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + int32x4_t a1 = vcvtq_s32_f32(a.val); + uint32x4_t mask = vcgtq_f32(a.val, vcvtq_f32_s32(a1)); + return v_int32x4(vsubq_s32(a1, vreinterpretq_s32_u32(mask))); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(vcvtq_s32_f32(a.val)); } + +#if CV_SIMD128_64F +inline v_int32x4 v_round(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), zero)); +} + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), vmovn_s64(vcvtaq_s64_f64(b.val)))); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + int64x2_t a1 = vcvtq_s64_f64(a.val); + uint64x2_t mask = vcgtq_f64(vcvtq_f64_s64(a1), a.val); + a1 = vaddq_s64(a1, vreinterpretq_s64_u64(mask)); + return v_int32x4(vcombine_s32(vmovn_s64(a1), zero)); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + int64x2_t a1 = vcvtq_s64_f64(a.val); + uint64x2_t mask = vcgtq_f64(a.val, vcvtq_f64_s64(a1)); + a1 = vsubq_s64(a1, vreinterpretq_s64_u64(mask)); + return v_int32x4(vcombine_s32(vmovn_s64(a1), zero)); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), zero)); +} +#endif + +#define OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const v_##_Tpvec& a0, const v_##_Tpvec& a1, \ + const v_##_Tpvec& a2, const v_##_Tpvec& a3, \ + v_##_Tpvec& b0, v_##_Tpvec& b1, \ + v_##_Tpvec& b2, v_##_Tpvec& b3) \ +{ \ + /* m00 m01 m02 m03 */ \ + /* m10 m11 m12 m13 */ \ + /* m20 m21 m22 m23 */ \ + /* m30 m31 m32 m33 */ \ + _Tpvec##x2_t t0 = vtrnq_##suffix(a0.val, a1.val); \ + _Tpvec##x2_t t1 = vtrnq_##suffix(a2.val, a3.val); \ + /* m00 m10 m02 m12 */ \ + /* m01 m11 m03 m13 */ \ + /* m20 m30 m22 m32 */ \ + /* m21 m31 m23 m33 */ \ + b0.val = vcombine_##suffix(vget_low_##suffix(t0.val[0]), vget_low_##suffix(t1.val[0])); \ + b1.val = vcombine_##suffix(vget_low_##suffix(t0.val[1]), vget_low_##suffix(t1.val[1])); \ + b2.val = vcombine_##suffix(vget_high_##suffix(t0.val[0]), vget_high_##suffix(t1.val[0])); \ + b3.val = vcombine_##suffix(vget_high_##suffix(t0.val[1]), vget_high_##suffix(t1.val[1])); \ +} + +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(int32x4, s32) +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(float32x4, f32) + +#define OPENCV_HAL_IMPL_NEON_INTERLEAVED(_Tpvec, _Tp, suffix) \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \ +{ \ + _Tpvec##x2_t v = vld2q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \ +{ \ + _Tpvec##x3_t v = vld3q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ + c.val = v.val[2]; \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \ + v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + _Tpvec##x4_t v = vld4q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ + c.val = v.val[2]; \ + d.val = v.val[3]; \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + _Tpvec##x2_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + vst2q_##suffix(ptr, v); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + _Tpvec##x3_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + v.val[2] = c.val; \ + vst3q_##suffix(ptr, v); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, const v_##_Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec##x4_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + v.val[2] = c.val; \ + v.val[3] = d.val; \ + vst4q_##suffix(ptr, v); \ +} + +#define OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(tp, suffix) \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, v_##tp##x2& b ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t a1 = vld1_##suffix(ptr + 2); \ + tp##x1_t b1 = vld1_##suffix(ptr + 3); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ +} \ + \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, \ + v_##tp##x2& b, v_##tp##x2& c ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t c0 = vld1_##suffix(ptr + 2); \ + tp##x1_t a1 = vld1_##suffix(ptr + 3); \ + tp##x1_t b1 = vld1_##suffix(ptr + 4); \ + tp##x1_t c1 = vld1_##suffix(ptr + 5); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ + c = v_##tp##x2(vcombine_##suffix(c0, c1)); \ +} \ + \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, v_##tp##x2& b, \ + v_##tp##x2& c, v_##tp##x2& d ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t c0 = vld1_##suffix(ptr + 2); \ + tp##x1_t d0 = vld1_##suffix(ptr + 3); \ + tp##x1_t a1 = vld1_##suffix(ptr + 4); \ + tp##x1_t b1 = vld1_##suffix(ptr + 5); \ + tp##x1_t c1 = vld1_##suffix(ptr + 6); \ + tp##x1_t d1 = vld1_##suffix(ptr + 7); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ + c = v_##tp##x2(vcombine_##suffix(c0, c1)); \ + d = v_##tp##x2(vcombine_##suffix(d0, d1)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, const v_##tp##x2& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 3, vget_high_##suffix(b.val)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, \ + const v_##tp##x2& b, const v_##tp##x2& c, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_low_##suffix(c.val)); \ + vst1_##suffix(ptr + 3, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 4, vget_high_##suffix(b.val)); \ + vst1_##suffix(ptr + 5, vget_high_##suffix(c.val)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, const v_##tp##x2& b, \ + const v_##tp##x2& c, const v_##tp##x2& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_low_##suffix(c.val)); \ + vst1_##suffix(ptr + 3, vget_low_##suffix(d.val)); \ + vst1_##suffix(ptr + 4, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 5, vget_high_##suffix(b.val)); \ + vst1_##suffix(ptr + 6, vget_high_##suffix(c.val)); \ + vst1_##suffix(ptr + 7, vget_high_##suffix(d.val)); \ +} + +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_INTERLEAVED(float64x2, double, f64) +#endif + +OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(int64, s64) +OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(vcvtq_f32_s32(a.val)); +} + +#if CV_SIMD128_64F +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + float32x2_t zero = vdup_n_f32(0.0f); + return v_float32x4(vcombine_f32(vcvt_f32_f64(a.val), zero)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(vcombine_f32(vcvt_f32_f64(a.val), vcvt_f32_f64(b.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vcvt_f32_s32(vget_low_s32(a.val)))); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vcvt_f32_s32(vget_high_s32(a.val)))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vget_low_f32(a.val))); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vget_high_f32(a.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ return v_float64x2(vcvtq_f64_s64(a.val)); } + +#endif + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[ 0]], + tab[idx[ 1]], + tab[idx[ 2]], + tab[idx[ 3]], + tab[idx[ 4]], + tab[idx[ 5]], + tab[idx[ 6]], + tab[idx[ 7]], + tab[idx[ 8]], + tab[idx[ 9]], + tab[idx[10]], + tab[idx[11]], + tab[idx[12]], + tab[idx[13]], + tab[idx[14]], + tab[idx[15]] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[4]], + tab[idx[4] + 1], + tab[idx[5]], + tab[idx[5] + 1], + tab[idx[6]], + tab[idx[6] + 1], + tab[idx[7]], + tab[idx[7] + 1] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[0] + 2], + tab[idx[0] + 3], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[1] + 2], + tab[idx[1] + 3], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[2] + 2], + tab[idx[2] + 3], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[3] + 2], + tab[idx[3] + 3] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]], + tab[idx[4]], + tab[idx[5]], + tab[idx[6]], + tab[idx[7]] + }; + return v_int16x8(vld1q_s16(elems)); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1] + }; + return v_int16x8(vld1q_s16(elems)); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(vcombine_s16(vld1_s16(tab + idx[0]), vld1_s16(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_int32x4(vld1q_s32(elems)); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(vcombine_s32(vld1_s32(tab + idx[0]), vld1_s32(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(vld1q_s32(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(vcombine_s64(vcreate_s64(tab[idx[0]]), vcreate_s64(tab[idx[1]]))); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(vld1q_s64(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_float32x4(vld1q_f32(elems)); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) +{ + typedef uint64 CV_DECL_ALIGNED(1) unaligned_uint64; + + uint64 CV_DECL_ALIGNED(32) elems[2] = + { + *(unaligned_uint64*)(tab + idx[0]), + *(unaligned_uint64*)(tab + idx[1]) + }; + return v_float32x4(vreinterpretq_f32_u64(vld1q_u64(elems))); +} +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) +{ + return v_float32x4(vld1q_f32(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_int32x4(vld1q_s32(elems)); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + unsigned CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_uint32x4(vld1q_u32(elems)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_float32x4(vld1q_f32(elems)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + /*int CV_DECL_ALIGNED(32) idx[4]; + v_store(idx, idxvec); + + float32x4_t xy02 = vcombine_f32(vld1_f32(tab + idx[0]), vld1_f32(tab + idx[2])); + float32x4_t xy13 = vcombine_f32(vld1_f32(tab + idx[1]), vld1_f32(tab + idx[3])); + + float32x4x2_t xxyy = vuzpq_f32(xy02, xy13); + x = v_float32x4(xxyy.val[0]); + y = v_float32x4(xxyy.val[1]);*/ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + x = v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); + y = v_float32x4(tab[idx[0]+1], tab[idx[1]+1], tab[idx[2]+1], tab[idx[3]+1]); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + return v_int8x16(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0705060403010200)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0705060403010200)))); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + return v_int8x16(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0703060205010400)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0703060205010400)))); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + return v_int16x8(vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0706030205040100)), vtbl1_s8(vget_high_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0706030205040100))))); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + int16x4x2_t res = vzip_s16(vget_low_s16(vec.val), vget_high_s16(vec.val)); + return v_int16x8(vcombine_s16(res.val[0], res.val[1])); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + int32x2x2_t res = vzip_s32(vget_low_s32(vec.val), vget_high_s32(vec.val)); + return v_int32x4(vcombine_s32(res.val[0], res.val[1])); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + return v_int8x16(vextq_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0605040201000000)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0807060504020100))), vdupq_n_s8(0), 2)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + return v_int16x8(vreinterpretq_s16_s8(vextq_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0504030201000000)), vget_high_s8(vreinterpretq_s8_s16(vec.val))), vdupq_n_s8(0), 2))); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +#if CV_SIMD128_64F +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[idx[0]], + tab[idx[1]] + }; + return v_float64x2(vld1q_f64(elems)); +} + +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(vld1q_f64(tab + idx[0])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + }; + return v_float64x2(vld1q_f64(elems)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + x = v_float64x2(tab[idx[0]], tab[idx[1]]); + y = v_float64x2(tab[idx[0]+1], tab[idx[1]+1]); +} +#endif + +////// FP16 support /////// +#if CV_FP16 +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + float16x4_t v = + #ifndef vld1_f16 // APPLE compiler defines vld1_f16 as macro + (float16x4_t)vld1_s16((const short*)ptr); + #else + vld1_f16((const __fp16*)ptr); + #endif + return v_float32x4(vcvt_f32_f16(v)); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + float16x4_t hv = vcvt_f16_f32(v.val); + + #ifndef vst1_f16 // APPLE compiler defines vst1_f16 as macro + vst1_s16((short*)ptr, (int16x4_t)hv); + #else + vst1_f16((__fp16*)ptr, hv); + #endif +} +#else +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + const int N = 4; + float buf[N]; + for( int i = 0; i < N; i++ ) buf[i] = (float)ptr[i]; + return v_load(buf); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + const int N = 4; + float buf[N]; + v_store(buf, v); + for( int i = 0; i < N; i++ ) ptr[i] = float16_t(buf[i]); +} +#endif + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse.hpp new file mode 100644 index 0000000..867ff55 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse.hpp @@ -0,0 +1,3435 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_SSE_HPP +#define OPENCV_HAL_SSE_HPP + +#include +#include "opencv2/core/utility.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 +#define CV_SIMD128_FP16 0 // no native operations with FP16 type. + +namespace cv +{ + +//! @cond IGNORED + +// +// Compilation troubleshooting: +// - MSVC: error C2719: 'a': formal parameter with requested alignment of 16 won't be aligned +// Replace parameter declaration to const reference: +// -v_int32x4 a +// +const v_int32x4& a +// + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + typedef __m128i vector_type; + enum { nlanes = 16 }; + + v_uint8x16() : val(_mm_setzero_si128()) {} + explicit v_uint8x16(__m128i v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + val = _mm_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6, (char)v7, + (char)v8, (char)v9, (char)v10, (char)v11, + (char)v12, (char)v13, (char)v14, (char)v15); + } + uchar get0() const + { + return (uchar)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + typedef __m128i vector_type; + enum { nlanes = 16 }; + + v_int8x16() : val(_mm_setzero_si128()) {} + explicit v_int8x16(__m128i v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + val = _mm_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6, (char)v7, + (char)v8, (char)v9, (char)v10, (char)v11, + (char)v12, (char)v13, (char)v14, (char)v15); + } + schar get0() const + { + return (schar)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + typedef __m128i vector_type; + enum { nlanes = 8 }; + + v_uint16x8() : val(_mm_setzero_si128()) {} + explicit v_uint16x8(__m128i v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + val = _mm_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7); + } + ushort get0() const + { + return (ushort)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + typedef __m128i vector_type; + enum { nlanes = 8 }; + + v_int16x8() : val(_mm_setzero_si128()) {} + explicit v_int16x8(__m128i v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + val = _mm_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7); + } + short get0() const + { + return (short)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + typedef __m128i vector_type; + enum { nlanes = 4 }; + + v_uint32x4() : val(_mm_setzero_si128()) {} + explicit v_uint32x4(__m128i v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + val = _mm_setr_epi32((int)v0, (int)v1, (int)v2, (int)v3); + } + unsigned get0() const + { + return (unsigned)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + typedef __m128i vector_type; + enum { nlanes = 4 }; + + v_int32x4() : val(_mm_setzero_si128()) {} + explicit v_int32x4(__m128i v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + val = _mm_setr_epi32(v0, v1, v2, v3); + } + int get0() const + { + return _mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + typedef __m128 vector_type; + enum { nlanes = 4 }; + + v_float32x4() : val(_mm_setzero_ps()) {} + explicit v_float32x4(__m128 v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + val = _mm_setr_ps(v0, v1, v2, v3); + } + float get0() const + { + return _mm_cvtss_f32(val); + } + + __m128 val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + typedef __m128i vector_type; + enum { nlanes = 2 }; + + v_uint64x2() : val(_mm_setzero_si128()) {} + explicit v_uint64x2(__m128i v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + val = _mm_setr_epi32((int)v0, (int)(v0 >> 32), (int)v1, (int)(v1 >> 32)); + } + uint64 get0() const + { + #if !defined(__x86_64__) && !defined(_M_X64) + int a = _mm_cvtsi128_si32(val); + int b = _mm_cvtsi128_si32(_mm_srli_epi64(val, 32)); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #else + return (uint64)_mm_cvtsi128_si64(val); + #endif + } + + __m128i val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + typedef __m128i vector_type; + enum { nlanes = 2 }; + + v_int64x2() : val(_mm_setzero_si128()) {} + explicit v_int64x2(__m128i v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + val = _mm_setr_epi32((int)v0, (int)(v0 >> 32), (int)v1, (int)(v1 >> 32)); + } + int64 get0() const + { + #if !defined(__x86_64__) && !defined(_M_X64) + int a = _mm_cvtsi128_si32(val); + int b = _mm_cvtsi128_si32(_mm_srli_epi64(val, 32)); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #else + return _mm_cvtsi128_si64(val); + #endif + } + + __m128i val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + typedef __m128d vector_type; + enum { nlanes = 2 }; + + v_float64x2() : val(_mm_setzero_pd()) {} + explicit v_float64x2(__m128d v) : val(v) {} + v_float64x2(double v0, double v1) + { + val = _mm_setr_pd(v0, v1); + } + double get0() const + { + return _mm_cvtsd_f64(val); + } + + __m128d val; +}; + +namespace hal_sse_internal +{ + template + to_sse_type v_sse_reinterpret_as(const from_sse_type& val); + +#define OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(to_sse_type, from_sse_type, sse_cast_intrin) \ + template<> inline \ + to_sse_type v_sse_reinterpret_as(const from_sse_type& a) \ + { return sse_cast_intrin(a); } + + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128i, OPENCV_HAL_NOP) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128, _mm_castps_si128) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128d, _mm_castpd_si128) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128i, _mm_castsi128_ps) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128, OPENCV_HAL_NOP) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128d, _mm_castpd_ps) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128i, _mm_castsi128_pd) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128, _mm_castps_pd) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128d, OPENCV_HAL_NOP) +} + +#define OPENCV_HAL_IMPL_SSE_INITVEC(_Tpvec, _Tp, suffix, zsuffix, ssuffix, _Tps, cast) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(_mm_setzero_##zsuffix()); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(_mm_set1_##ssuffix((_Tps)v)); } \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0& a) \ +{ return _Tpvec(cast(a.val)); } + +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint8x16, uchar, u8, si128, epi8, schar, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int8x16, schar, s8, si128, epi8, schar, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint16x8, ushort, u16, si128, epi16, short, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int16x8, short, s16, si128, epi16, short, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint32x4, unsigned, u32, si128, epi32, int, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int32x4, int, s32, si128, epi32, int, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_float32x4, float, f32, ps, ps, float, _mm_castsi128_ps) +OPENCV_HAL_IMPL_SSE_INITVEC(v_float64x2, double, f64, pd, pd, double, _mm_castsi128_pd) + +inline v_uint64x2 v_setzero_u64() { return v_uint64x2(_mm_setzero_si128()); } +inline v_int64x2 v_setzero_s64() { return v_int64x2(_mm_setzero_si128()); } +inline v_uint64x2 v_setall_u64(uint64 val) { return v_uint64x2(val, val); } +inline v_int64x2 v_setall_s64(int64 val) { return v_int64x2(val, val); } + +template inline +v_uint64x2 v_reinterpret_as_u64(const _Tpvec& a) { return v_uint64x2(a.val); } +template inline +v_int64x2 v_reinterpret_as_s64(const _Tpvec& a) { return v_int64x2(a.val); } +inline v_float32x4 v_reinterpret_as_f32(const v_uint64x2& a) +{ return v_float32x4(_mm_castsi128_ps(a.val)); } +inline v_float32x4 v_reinterpret_as_f32(const v_int64x2& a) +{ return v_float32x4(_mm_castsi128_ps(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_uint64x2& a) +{ return v_float64x2(_mm_castsi128_pd(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_int64x2& a) +{ return v_float64x2(_mm_castsi128_pd(a.val)); } + +#define OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(_Tpvec, suffix) \ +inline _Tpvec v_reinterpret_as_##suffix(const v_float32x4& a) \ +{ return _Tpvec(_mm_castps_si128(a.val)); } \ +inline _Tpvec v_reinterpret_as_##suffix(const v_float64x2& a) \ +{ return _Tpvec(_mm_castpd_si128(a.val)); } + +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint8x16, u8) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int8x16, s8) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint16x8, u16) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int16x8, s16) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint32x4, u32) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int32x4, s32) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint64x2, u64) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int64x2, s64) + +inline v_float32x4 v_reinterpret_as_f32(const v_float32x4& a) {return a; } +inline v_float64x2 v_reinterpret_as_f64(const v_float64x2& a) {return a; } +inline v_float32x4 v_reinterpret_as_f32(const v_float64x2& a) {return v_float32x4(_mm_castpd_ps(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_float32x4& a) {return v_float64x2(_mm_castps_pd(a.val)); } + +//////////////// PACK /////////////// +inline v_uint8x16 v_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + __m128i delta = _mm_set1_epi16(255); + return v_uint8x16(_mm_packus_epi16(_mm_subs_epu16(a.val, _mm_subs_epu16(a.val, delta)), + _mm_subs_epu16(b.val, _mm_subs_epu16(b.val, delta)))); +} + +inline void v_pack_store(uchar* ptr, const v_uint16x8& a) +{ + __m128i delta = _mm_set1_epi16(255); + __m128i a1 = _mm_subs_epu16(a.val, _mm_subs_epu16(a.val, delta)); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +inline v_uint8x16 v_pack_u(const v_int16x8& a, const v_int16x8& b) +{ return v_uint8x16(_mm_packus_epi16(a.val, b.val)); } + +inline void v_pack_u_store(uchar* ptr, const v_int16x8& a) +{ _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a.val, a.val)); } + +template inline +v_uint8x16 v_rshr_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_uint8x16(_mm_packus_epi16(_mm_srli_epi16(_mm_adds_epu16(a.val, delta), n), + _mm_srli_epi16(_mm_adds_epu16(b.val, delta), n))); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x8& a) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srli_epi16(_mm_adds_epu16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +template inline +v_uint8x16 v_rshr_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_uint8x16(_mm_packus_epi16(_mm_srai_epi16(_mm_adds_epi16(a.val, delta), n), + _mm_srai_epi16(_mm_adds_epi16(b.val, delta), n))); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srai_epi16(_mm_adds_epi16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +inline v_int8x16 v_pack(const v_int16x8& a, const v_int16x8& b) +{ return v_int8x16(_mm_packs_epi16(a.val, b.val)); } + +inline void v_pack_store(schar* ptr, const v_int16x8& a) +{ _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi16(a.val, a.val)); } + +template inline +v_int8x16 v_rshr_pack(const v_int16x8& a, const v_int16x8& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_int8x16(_mm_packs_epi16(_mm_srai_epi16(_mm_adds_epi16(a.val, delta), n), + _mm_srai_epi16(_mm_adds_epi16(b.val, delta), n))); +} +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x8& a) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srai_epi16(_mm_adds_epi16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi16(a1, a1)); +} + + +// byte-wise "mask ? a : b" +inline __m128i v_select_si128(__m128i mask, __m128i a, __m128i b) +{ +#if CV_SSE4_1 + return _mm_blendv_epi8(b, a, mask); +#else + return _mm_xor_si128(b, _mm_and_si128(_mm_xor_si128(a, b), mask)); +#endif +} + +inline v_uint16x8 v_pack(const v_uint32x4& a, const v_uint32x4& b) +{ return v_uint16x8(_v128_packs_epu32(a.val, b.val)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x4& a) +{ + __m128i z = _mm_setzero_si128(), maxval32 = _mm_set1_epi32(65535), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(v_select_si128(_mm_cmpgt_epi32(z, a.val), maxval32, a.val), delta32); + __m128i r = _mm_packs_epi32(a1, a1); + _mm_storel_epi64((__m128i*)ptr, _mm_sub_epi16(r, _mm_set1_epi16(-32768))); +} + +template inline +v_uint16x8 v_rshr_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i b1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(b.val, delta), n), delta32); + return v_uint16x8(_mm_sub_epi16(_mm_packs_epi32(a1, b1), _mm_set1_epi16(-32768))); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x4& a) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +inline v_uint16x8 v_pack_u(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_packus_epi32(a.val, b.val)); +#else + __m128i delta32 = _mm_set1_epi32(32768); + + // preliminary saturate negative values to zero + __m128i a1 = _mm_and_si128(a.val, _mm_cmpgt_epi32(a.val, _mm_set1_epi32(0))); + __m128i b1 = _mm_and_si128(b.val, _mm_cmpgt_epi32(b.val, _mm_set1_epi32(0))); + + __m128i r = _mm_packs_epi32(_mm_sub_epi32(a1, delta32), _mm_sub_epi32(b1, delta32)); + return v_uint16x8(_mm_sub_epi16(r, _mm_set1_epi16(-32768))); +#endif +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x4& a) +{ +#if CV_SSE4_1 + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi32(a.val, a.val)); +#else + __m128i delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(a.val, delta32); + __m128i r = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, r); +#endif +} + +template inline +v_uint16x8 v_rshr_pack_u(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + __m128i delta = _mm_set1_epi32(1 << (n - 1)); + return v_uint16x8(_mm_packus_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), + _mm_srai_epi32(_mm_add_epi32(b.val, delta), n))); +#else + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + __m128i b1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(b.val, delta), n), delta32); + __m128i b2 = _mm_sub_epi16(_mm_packs_epi32(b1, b1), _mm_set1_epi16(-32768)); + return v_uint16x8(_mm_unpacklo_epi64(a2, b2)); +#endif +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x4& a) +{ +#if CV_SSE4_1 + __m128i delta = _mm_set1_epi32(1 << (n - 1)); + __m128i a1 = _mm_srai_epi32(_mm_add_epi32(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi32(a1, a1)); +#else + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, a2); +#endif +} + +inline v_int16x8 v_pack(const v_int32x4& a, const v_int32x4& b) +{ return v_int16x8(_mm_packs_epi32(a.val, b.val)); } + +inline void v_pack_store(short* ptr, const v_int32x4& a) +{ + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi32(a.val, a.val)); +} + +template inline +v_int16x8 v_rshr_pack(const v_int32x4& a, const v_int32x4& b) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)); + return v_int16x8(_mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), + _mm_srai_epi32(_mm_add_epi32(b.val, delta), n))); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x4& a) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)); + __m128i a1 = _mm_srai_epi32(_mm_add_epi32(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi32(a1, a1)); +} + + +// [a0 0 | b0 0] [a1 0 | b1 0] +inline v_uint32x4 v_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); // b0 b1 0 0 + return v_uint32x4(_mm_unpacklo_epi32(v0, v1)); +} + +inline void v_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + __m128i a1 = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a1); +} + +// [a0 0 | b0 0] [a1 0 | b1 0] +inline v_int32x4 v_pack(const v_int64x2& a, const v_int64x2& b) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); // b0 b1 0 0 + return v_int32x4(_mm_unpacklo_epi32(v0, v1)); +} + +inline void v_pack_store(int* ptr, const v_int64x2& a) +{ + __m128i a1 = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a1); +} + +template inline +v_uint32x4 v_rshr_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + uint64 delta = (uint64)1 << (n-1); + v_uint64x2 delta2(delta, delta); + __m128i a1 = _mm_srli_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i b1 = _mm_srli_epi64(_mm_add_epi64(b.val, delta2.val), n); + __m128i v0 = _mm_unpacklo_epi32(a1, b1); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a1, b1); // b0 b1 0 0 + return v_uint32x4(_mm_unpacklo_epi32(v0, v1)); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + uint64 delta = (uint64)1 << (n-1); + v_uint64x2 delta2(delta, delta); + __m128i a1 = _mm_srli_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i a2 = _mm_shuffle_epi32(a1, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +inline __m128i v_sign_epi64(__m128i a) +{ + return _mm_shuffle_epi32(_mm_srai_epi32(a, 31), _MM_SHUFFLE(3, 3, 1, 1)); // x m0 | x m1 +} + +inline __m128i v_srai_epi64(__m128i a, int imm) +{ + __m128i smask = v_sign_epi64(a); + return _mm_xor_si128(_mm_srli_epi64(_mm_xor_si128(a, smask), imm), smask); +} + +template inline +v_int32x4 v_rshr_pack(const v_int64x2& a, const v_int64x2& b) +{ + int64 delta = (int64)1 << (n-1); + v_int64x2 delta2(delta, delta); + __m128i a1 = v_srai_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i b1 = v_srai_epi64(_mm_add_epi64(b.val, delta2.val), n); + __m128i v0 = _mm_unpacklo_epi32(a1, b1); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a1, b1); // b0 b1 0 0 + return v_int32x4(_mm_unpacklo_epi32(v0, v1)); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x2& a) +{ + int64 delta = (int64)1 << (n-1); + v_int64x2 delta2(delta, delta); + __m128i a1 = v_srai_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i a2 = _mm_shuffle_epi32(a1, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + __m128i ab = _mm_packs_epi16(a.val, b.val); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + __m128i ab = _mm_packs_epi32(a.val, b.val); + __m128i cd = _mm_packs_epi32(c.val, d.val); + return v_uint8x16(_mm_packs_epi16(ab, cd)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + __m128i ab = _mm_packs_epi32(a.val, b.val); + __m128i cd = _mm_packs_epi32(c.val, d.val); + __m128i ef = _mm_packs_epi32(e.val, f.val); + __m128i gh = _mm_packs_epi32(g.val, h.val); + + __m128i abcd = _mm_packs_epi32(ab, cd); + __m128i efgh = _mm_packs_epi32(ef, gh); + return v_uint8x16(_mm_packs_epi16(abcd, efgh)); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + __m128 v0 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(0, 0, 0, 0)), m0.val); + __m128 v1 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(1, 1, 1, 1)), m1.val); + __m128 v2 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(2, 2, 2, 2)), m2.val); + __m128 v3 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(3, 3, 3, 3)), m3.val); + + return v_float32x4(_mm_add_ps(_mm_add_ps(v0, v1), _mm_add_ps(v2, v3))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + __m128 v0 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(0, 0, 0, 0)), m0.val); + __m128 v1 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(1, 1, 1, 1)), m1.val); + __m128 v2 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(2, 2, 2, 2)), m2.val); + + return v_float32x4(_mm_add_ps(_mm_add_ps(v0, v1), _mm_add_ps(v2, a.val))); +} + +#define OPENCV_HAL_IMPL_SSE_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { \ + return _Tpvec(intrin(a.val, b.val)); \ + } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { \ + a.val = intrin(a.val, b.val); \ + return a; \ + } + +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint8x16, _mm_adds_epu8) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint8x16, _mm_subs_epu8) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int8x16, _mm_adds_epi8) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int8x16, _mm_subs_epi8) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint16x8, _mm_adds_epu16) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint16x8, _mm_subs_epu16) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int16x8, _mm_adds_epi16) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int16x8, _mm_subs_epi16) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint32x4, _mm_add_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint32x4, _mm_sub_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_uint32x4, _v128_mullo_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int32x4, _mm_add_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int32x4, _mm_sub_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_int32x4, _v128_mullo_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_float32x4, _mm_add_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_float32x4, _mm_sub_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_float32x4, _mm_mul_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(/, v_float32x4, _mm_div_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_float64x2, _mm_add_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_float64x2, _mm_sub_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_float64x2, _mm_mul_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(/, v_float64x2, _mm_div_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint64x2, _mm_add_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint64x2, _mm_sub_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int64x2, _mm_add_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int64x2, _mm_sub_epi64) + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_SSE_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_uint16x8, v_uint32x4) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_int16x8, v_int32x4) + +// Multiply and expand +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v_uint16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v_int16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + __m128i v0 = _mm_mullo_epi16(a.val, b.val); + __m128i v1 = _mm_mulhi_epi16(a.val, b.val); + c.val = _mm_unpacklo_epi16(v0, v1); + d.val = _mm_unpackhi_epi16(v0, v1); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + __m128i v0 = _mm_mullo_epi16(a.val, b.val); + __m128i v1 = _mm_mulhi_epu16(a.val, b.val); + c.val = _mm_unpacklo_epi16(v0, v1); + d.val = _mm_unpackhi_epi16(v0, v1); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + __m128i c0 = _mm_mul_epu32(a.val, b.val); + __m128i c1 = _mm_mul_epu32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + c.val = _mm_unpacklo_epi64(c0, c1); + d.val = _mm_unpackhi_epi64(c0, c1); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) { return v_int16x8(_mm_mulhi_epi16(a.val, b.val)); } +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) { return v_uint16x8(_mm_mulhi_epu16(a.val, b.val)); } + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(_mm_madd_epi16(a.val, b.val)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + __m128i even = _mm_mul_epi32(a.val, b.val); + __m128i odd = _mm_mul_epi32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + return v_int64x2(_mm_add_epi64(even, odd)); +#else + __m128i even_u = _mm_mul_epu32(a.val, b.val); + __m128i odd_u = _mm_mul_epu32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + // convert unsigned to signed high multiplication (from: Agner Fog(veclib) and H S Warren: Hacker's delight, 2003, p. 132) + __m128i a_sign = _mm_srai_epi32(a.val, 31); + __m128i b_sign = _mm_srai_epi32(b.val, 31); + // |x * sign of x + __m128i axb = _mm_and_si128(a.val, b_sign); + __m128i bxa = _mm_and_si128(b.val, a_sign); + // sum of sign corrections + __m128i ssum = _mm_add_epi32(bxa, axb); + __m128i even_ssum = _mm_slli_epi64(ssum, 32); + __m128i odd_ssum = _mm_and_si128(ssum, _mm_set_epi32(-1, 0, -1, 0)); + // convert to signed and prod + return v_int64x2(_mm_add_epi64(_mm_sub_epi64(even_u, even_ssum), _mm_sub_epi64(odd_u, odd_ssum))); +#endif +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i a0 = _mm_srli_epi16(_mm_slli_si128(a.val, 1), 8); // even + __m128i a1 = _mm_srli_epi16(a.val, 8); // odd + __m128i b0 = _mm_srli_epi16(_mm_slli_si128(b.val, 1), 8); + __m128i b1 = _mm_srli_epi16(b.val, 8); + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_uint32x4(_mm_add_epi32(p0, p1)); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + __m128i a0 = _mm_srai_epi16(_mm_slli_si128(a.val, 1), 8); // even + __m128i a1 = _mm_srai_epi16(a.val, 8); // odd + __m128i b0 = _mm_srai_epi16(_mm_slli_si128(b.val, 1), 8); + __m128i b1 = _mm_srai_epi16(b.val, 8); + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_int32x4(_mm_add_epi32(p0, p1)); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 c, d; + v_mul_expand(a, b, c, d); + + v_uint64x2 c0, c1, d0, d1; + v_expand(c, c0, c1); + v_expand(d, d0, d1); + + c0 += c1; d0 += d1; + return v_uint64x2(_mm_add_epi64( + _mm_unpacklo_epi64(c0.val, d0.val), + _mm_unpackhi_epi64(c0.val, d0.val) + )); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return v_int64x2(_mm_add_epi64( + _mm_unpacklo_epi64(c.val, d.val), + _mm_unpackhi_epi64(c.val, d.val) + )); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_cvt_f64(v_dotprod(a, b)); +#else + v_float64x2 c = v_cvt_f64(a) * v_cvt_f64(b); + v_float64x2 d = v_cvt_f64_high(a) * v_cvt_f64_high(b); + + return v_float64x2(_mm_add_pd( + _mm_unpacklo_pd(c.val, d.val), + _mm_unpackhi_pd(c.val, d.val) + )); +#endif +} +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod_fast(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i a0 = v_expand_low(a).val; + __m128i a1 = v_expand_high(a).val; + __m128i b0 = v_expand_low(b).val; + __m128i b1 = v_expand_high(b).val; + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_uint32x4(_mm_add_epi32(p0, p1)); +} +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + __m128i a0 = _mm_cvtepi8_epi16(a.val); + __m128i a1 = v_expand_high(a).val; + __m128i b0 = _mm_cvtepi8_epi16(b.val); + __m128i b1 = v_expand_high(b).val; + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_int32x4(_mm_add_epi32(p0, p1)); +#else + return v_dotprod_expand(a, b); +#endif +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 c, d; + v_mul_expand(a, b, c, d); + + v_uint64x2 c0, c1, d0, d1; + v_expand(c, c0, c1); + v_expand(d, d0, d1); + + c0 += c1; d0 += d1; + return c0 + d0; +} +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return c + d; +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c); +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_cvt_f64_high(a) * v_cvt_f64_high(b)); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_fma(v_cvt_f64_high(a), v_cvt_f64_high(b), c)); } + +#define OPENCV_HAL_IMPL_SSE_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(&, _Tpvec, _mm_and_##suffix) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(|, _Tpvec, _mm_or_##suffix) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(^, _Tpvec, _mm_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { \ + return _Tpvec(_mm_xor_##suffix(a.val, not_const)); \ + } + +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint8x16, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int8x16, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint16x8, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int16x8, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint32x4, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int32x4, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint64x2, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int64x2, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_float32x4, ps, _mm_castsi128_ps(_mm_set1_epi32(-1))) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_float64x2, pd, _mm_castsi128_pd(_mm_set1_epi32(-1))) + +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ return v_float32x4(_mm_sqrt_ps(x.val)); } + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + const __m128 _0_5 = _mm_set1_ps(0.5f), _1_5 = _mm_set1_ps(1.5f); + __m128 t = x.val; + __m128 h = _mm_mul_ps(t, _0_5); + t = _mm_rsqrt_ps(t); + t = _mm_mul_ps(t, _mm_sub_ps(_1_5, _mm_mul_ps(_mm_mul_ps(t, t), h))); + return v_float32x4(t); +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ return v_float64x2(_mm_sqrt_pd(x.val)); } + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ + const __m128d v_1 = _mm_set1_pd(1.); + return v_float64x2(_mm_div_pd(v_1, _mm_sqrt_pd(x.val))); +} + +#define OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(_Tpuvec, _Tpsvec, func, suffix, subWidth) \ +inline _Tpuvec v_abs(const _Tpsvec& x) \ +{ return _Tpuvec(_mm_##func##_ep##suffix(x.val, _mm_sub_ep##subWidth(_mm_setzero_si128(), x.val))); } + +OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(v_uint8x16, v_int8x16, min, u8, i8) +OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(v_uint16x8, v_int16x8, max, i16, i16) +inline v_uint32x4 v_abs(const v_int32x4& x) +{ + __m128i s = _mm_srli_epi32(x.val, 31); + __m128i f = _mm_srai_epi32(x.val, 31); + return v_uint32x4(_mm_add_epi32(_mm_xor_si128(x.val, f), s)); +} +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(_mm_and_ps(x.val, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); } +inline v_float64x2 v_abs(const v_float64x2& x) +{ + return v_float64x2(_mm_and_pd(x.val, + _mm_castsi128_pd(_mm_srli_epi64(_mm_set1_epi32(-1), 1)))); +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_SSE_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_min, _mm_min_epu8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_max, _mm_max_epu8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_min, _mm_min_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_max, _mm_max_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float32x4, v_min, _mm_min_ps) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float32x4, v_max, _mm_max_ps) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float64x2, v_min, _mm_min_pd) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float64x2, v_max, _mm_max_pd) + +inline v_int8x16 v_min(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + return v_int8x16(_mm_min_epi8(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi8((char)-128); + return v_int8x16(_mm_xor_si128(delta, _mm_min_epu8(_mm_xor_si128(a.val, delta), + _mm_xor_si128(b.val, delta)))); +#endif +} +inline v_int8x16 v_max(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + return v_int8x16(_mm_max_epi8(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi8((char)-128); + return v_int8x16(_mm_xor_si128(delta, _mm_max_epu8(_mm_xor_si128(a.val, delta), + _mm_xor_si128(b.val, delta)))); +#endif +} +inline v_uint16x8 v_min(const v_uint16x8& a, const v_uint16x8& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_min_epu16(a.val, b.val)); +#else + return v_uint16x8(_mm_subs_epu16(a.val, _mm_subs_epu16(a.val, b.val))); +#endif +} +inline v_uint16x8 v_max(const v_uint16x8& a, const v_uint16x8& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_max_epu16(a.val, b.val)); +#else + return v_uint16x8(_mm_adds_epu16(_mm_subs_epu16(a.val, b.val), b.val)); +#endif +} +inline v_uint32x4 v_min(const v_uint32x4& a, const v_uint32x4& b) +{ +#if CV_SSE4_1 + return v_uint32x4(_mm_min_epu32(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi32((int)0x80000000); + __m128i mask = _mm_cmpgt_epi32(_mm_xor_si128(a.val, delta), _mm_xor_si128(b.val, delta)); + return v_uint32x4(v_select_si128(mask, b.val, a.val)); +#endif +} +inline v_uint32x4 v_max(const v_uint32x4& a, const v_uint32x4& b) +{ +#if CV_SSE4_1 + return v_uint32x4(_mm_max_epu32(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi32((int)0x80000000); + __m128i mask = _mm_cmpgt_epi32(_mm_xor_si128(a.val, delta), _mm_xor_si128(b.val, delta)); + return v_uint32x4(v_select_si128(mask, a.val, b.val)); +#endif +} +inline v_int32x4 v_min(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_int32x4(_mm_min_epi32(a.val, b.val)); +#else + return v_int32x4(v_select_si128(_mm_cmpgt_epi32(a.val, b.val), b.val, a.val)); +#endif +} +inline v_int32x4 v_max(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_int32x4(_mm_max_epi32(a.val, b.val)); +#else + return v_int32x4(v_select_si128(_mm_cmpgt_epi32(a.val, b.val), a.val, b.val)); +#endif +} + +#define OPENCV_HAL_IMPL_SSE_INT_CMP_OP(_Tpuvec, _Tpsvec, suffix, sbit) \ +inline _Tpuvec operator == (const _Tpuvec& a, const _Tpuvec& b) \ +{ return _Tpuvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpuvec operator != (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpuvec(_mm_xor_si128(_mm_cmpeq_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpsvec operator == (const _Tpsvec& a, const _Tpsvec& b) \ +{ return _Tpsvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpsvec operator != (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpeq_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpuvec operator < (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + return _Tpuvec(_mm_cmpgt_##suffix(_mm_xor_si128(b.val, smask), _mm_xor_si128(a.val, smask))); \ +} \ +inline _Tpuvec operator > (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + return _Tpuvec(_mm_cmpgt_##suffix(_mm_xor_si128(a.val, smask), _mm_xor_si128(b.val, smask))); \ +} \ +inline _Tpuvec operator <= (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + __m128i not_mask = _mm_set1_epi32(-1); \ + __m128i res = _mm_cmpgt_##suffix(_mm_xor_si128(a.val, smask), _mm_xor_si128(b.val, smask)); \ + return _Tpuvec(_mm_xor_si128(res, not_mask)); \ +} \ +inline _Tpuvec operator >= (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + __m128i not_mask = _mm_set1_epi32(-1); \ + __m128i res = _mm_cmpgt_##suffix(_mm_xor_si128(b.val, smask), _mm_xor_si128(a.val, smask)); \ + return _Tpuvec(_mm_xor_si128(res, not_mask)); \ +} \ +inline _Tpsvec operator < (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + return _Tpsvec(_mm_cmpgt_##suffix(b.val, a.val)); \ +} \ +inline _Tpsvec operator > (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + return _Tpsvec(_mm_cmpgt_##suffix(a.val, b.val)); \ +} \ +inline _Tpsvec operator <= (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpgt_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpsvec operator >= (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpgt_##suffix(b.val, a.val), not_mask)); \ +} + +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint8x16, v_int8x16, epi8, (char)-128) +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint16x8, v_int16x8, epi16, (short)-32768) +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint32x4, v_int32x4, epi32, (int)0x80000000) + +#define OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(_Tpvec, suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpneq_##suffix(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmplt_##suffix(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpgt_##suffix(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmple_##suffix(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpge_##suffix(a.val, b.val)); } + +OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(v_float32x4, ps) +OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(v_float64x2, pd) + +#if CV_SSE4_1 +#define OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpeq_epi64(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return ~(a == b); } +#else +#define OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ __m128i cmp = _mm_cmpeq_epi32(a.val, b.val); \ + return _Tpvec(_mm_and_si128(cmp, _mm_shuffle_epi32(cmp, _MM_SHUFFLE(2, 3, 0, 1)))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return ~(a == b); } +#endif + +OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(v_uint64x2) +OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(v_int64x2) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(_mm_cmpord_ps(a.val, a.val)); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(_mm_cmpord_pd(a.val, a.val)); } + +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_add_wrap, _mm_add_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int8x16, v_add_wrap, _mm_add_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_add_wrap, _mm_add_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_add_wrap, _mm_add_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_sub_wrap, _mm_sub_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int8x16, v_sub_wrap, _mm_sub_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_sub_wrap, _mm_sub_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_sub_wrap, _mm_sub_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_mul_wrap, _mm_mullo_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_mul_wrap, _mm_mullo_epi16) + +inline v_uint8x16 v_mul_wrap(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i ad = _mm_srai_epi16(a.val, 8); + __m128i bd = _mm_srai_epi16(b.val, 8); + __m128i p0 = _mm_mullo_epi16(a.val, b.val); // even + __m128i p1 = _mm_slli_epi16(_mm_mullo_epi16(ad, bd), 8); // odd + const __m128i b01 = _mm_set1_epi32(0xFF00FF00); + return v_uint8x16(_v128_blendv_epi8(p0, p1, b01)); +} +inline v_int8x16 v_mul_wrap(const v_int8x16& a, const v_int8x16& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +/** Absolute difference **/ + +inline v_uint8x16 v_absdiff(const v_uint8x16& a, const v_uint8x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x8 v_absdiff(const v_uint16x8& a, const v_uint16x8& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x4 v_absdiff(const v_uint32x4& a, const v_uint32x4& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = v_sub_wrap(a, b); + v_int8x16 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ + return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); +} +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ + v_int32x4 d = a - b; + v_int32x4 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = a - b; + v_int8x16 m = a < b; + return (d ^ m) - m; + } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_max(a, b) - v_min(a, b); } + + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return a * b + c; +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ +#if CV_FMA3 + return v_float32x4(_mm_fmadd_ps(a.val, b.val, c.val)); +#else + return v_float32x4(_mm_add_ps(_mm_mul_ps(a.val, b.val), c.val)); +#endif +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ +#if CV_FMA3 + return v_float64x2(_mm_fmadd_pd(a.val, b.val, c.val)); +#else + return v_float64x2(_mm_add_pd(_mm_mul_pd(a.val, b.val), c.val)); +#endif +} + +#define OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(_Tpvec, _Tp, _Tpreg, suffix, absmask_vec) \ +inline _Tpvec v_absdiff(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpreg absmask = _mm_castsi128_##suffix(absmask_vec); \ + return _Tpvec(_mm_and_##suffix(_mm_sub_##suffix(a.val, b.val), absmask)); \ +} \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpvec res = v_fma(a, a, b*b); \ + return _Tpvec(_mm_sqrt_##suffix(res.val)); \ +} \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return v_fma(a, a, b*b); \ +} \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ \ + return v_fma(a, b, c); \ +} + +OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(v_float32x4, float, __m128, ps, _mm_set1_epi32((int)0x7fffffff)) +OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(v_float64x2, double, __m128d, pd, _mm_srli_epi64(_mm_set1_epi32(-1), 1)) + +#define OPENCV_HAL_IMPL_SSE_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, srai) \ +inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(_mm_srli_##suffix(a.val, imm)); \ +} \ +inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(srai(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shl(const _Tpuvec& a) \ +{ \ + return _Tpuvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shl(const _Tpsvec& a) \ +{ \ + return _Tpsvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shr(const _Tpuvec& a) \ +{ \ + return _Tpuvec(_mm_srli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shr(const _Tpsvec& a) \ +{ \ + return _Tpsvec(srai(a.val, imm)); \ +} + +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint16x8, v_int16x8, epi16, _mm_srai_epi16) +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint32x4, v_int32x4, epi32, _mm_srai_epi32) +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint64x2, v_int64x2, epi64, v_srai_epi64) + +namespace hal_sse_internal +{ + template 16)), + bool is_first = (imm == 0), + bool is_half = (imm == 8), + bool is_second = (imm == 16), + bool is_other = (((imm > 0) && (imm < 8)) || ((imm > 8) && (imm < 16)))> + class v_sse_palignr_u8_class; + + template + class v_sse_palignr_u8_class; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i& a, const __m128i&) const + { + return a; + } + }; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + return _mm_unpacklo_epi64(_mm_unpackhi_epi64(a, a), b); + } + }; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i&, const __m128i& b) const + { + return b; + } + }; + + template + class v_sse_palignr_u8_class + { +#if CV_SSSE3 + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + return _mm_alignr_epi8(b, a, imm); + } +#else + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + enum { imm2 = (sizeof(__m128i) - imm) }; + return _mm_or_si128(_mm_srli_si128(a, imm), _mm_slli_si128(b, imm2)); + } +#endif + }; + + template + inline __m128i v_sse_palignr_u8(const __m128i& a, const __m128i& b) + { + CV_StaticAssert((imm >= 0) && (imm <= 16), "Invalid imm for v_sse_palignr_u8."); + return v_sse_palignr_u8_class()(a, b); + } +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + _mm_srli_si128( + v_sse_reinterpret_as<__m128i>(a.val), imm2))); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + _mm_slli_si128( + v_sse_reinterpret_as<__m128i>(a.val), imm2))); +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + v_sse_palignr_u8( + v_sse_reinterpret_as<__m128i>(a.val), + v_sse_reinterpret_as<__m128i>(b.val)))); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_sse_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + v_sse_palignr_u8( + v_sse_reinterpret_as<__m128i>(b.val), + v_sse_reinterpret_as<__m128i>(a.val)))); +} + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(_Tpvec, _Tp) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadu_si128((const __m128i*)ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(_mm_load_si128((const __m128i*)ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadl_epi64((const __m128i*)ptr)); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + return _Tpvec(_mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i*)ptr0), \ + _mm_loadl_epi64((const __m128i*)ptr1))); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storeu_si128((__m128i*)ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ _mm_store_si128((__m128i*)ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ _mm_stream_si128((__m128i*)ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm_storeu_si128((__m128i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm_stream_si128((__m128i*)ptr, a.val); \ + else \ + _mm_store_si128((__m128i*)ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, a.val); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, _mm_unpackhi_epi64(a.val, a.val)); } + +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint8x16, uchar) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int8x16, schar) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint16x8, ushort) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int16x8, short) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int32x4, int) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint64x2, uint64) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int64x2, int64) + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadu_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(_mm_load_##suffix(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(_mm_castsi128_##suffix(_mm_loadl_epi64((const __m128i*)ptr))); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + return _Tpvec(_mm_castsi128_##suffix( \ + _mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i*)ptr0), \ + _mm_loadl_epi64((const __m128i*)ptr1)))); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storeu_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ _mm_store_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ _mm_stream_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm_stream_##suffix(ptr, a.val); \ + else \ + _mm_store_##suffix(ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, _mm_cast##suffix##_si128(a.val)); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + __m128i a1 = _mm_cast##suffix##_si128(a.val); \ + _mm_storel_epi64((__m128i*)ptr, _mm_unpackhi_epi64(a1, a1)); \ +} + +OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(v_float32x4, float, ps) +OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(v_float64x2, double, pd) + +inline unsigned v_reduce_sum(const v_uint8x16& a) +{ + __m128i half = _mm_sad_epu8(a.val, _mm_setzero_si128()); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + __m128i half = _mm_set1_epi8((schar)-128); + half = _mm_sad_epu8(_mm_xor_si128(a.val, half), _mm_setzero_si128()); + return _mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))) - 2048; +} +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(func) \ +inline schar v_reduce_##func(const v_int8x16& a) \ +{ \ + __m128i val = a.val; \ + __m128i smask = _mm_set1_epi8((schar)-128); \ + val = _mm_xor_si128(val, smask); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,2)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,1)); \ + return (schar)_mm_cvtsi128_si32(val) ^ (schar)-128; \ +} \ +inline uchar v_reduce_##func(const v_uint8x16& a) \ +{ \ + __m128i val = a.val; \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,2)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,1)); \ + return (uchar)_mm_cvtsi128_si32(val); \ +} +OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(min) + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(_Tpvec, scalartype, func, suffix, sbit) \ +inline scalartype v_reduce_##func(const v_##_Tpvec& a) \ +{ \ + __m128i val = a.val; \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,2)); \ + return (scalartype)_mm_cvtsi128_si32(val); \ +} \ +inline unsigned scalartype v_reduce_##func(const v_u##_Tpvec& a) \ +{ \ + __m128i val = a.val; \ + __m128i smask = _mm_set1_epi16(sbit); \ + val = _mm_xor_si128(val, smask); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,2)); \ + return (unsigned scalartype)(_mm_cvtsi128_si32(val) ^ sbit); \ +} +OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(int16x8, short, max, epi16, (short)-32768) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(int16x8, short, min, epi16, (short)-32768) + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(_Tpvec, scalartype, regtype, suffix, cast_from, cast_to, extract) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + regtype val = a.val; \ + val = _mm_add_##suffix(val, cast_to(_mm_srli_si128(cast_from(val), 8))); \ + val = _mm_add_##suffix(val, cast_to(_mm_srli_si128(cast_from(val), 4))); \ + return (scalartype)_mm_cvt##extract(val); \ +} + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(_Tpvec, scalartype, func, scalar_func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + scalartype CV_DECL_ALIGNED(16) buf[4]; \ + v_store_aligned(buf, a); \ + scalartype s0 = scalar_func(buf[0], buf[1]); \ + scalartype s1 = scalar_func(buf[2], buf[3]); \ + return scalar_func(s0, s1); \ +} + +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_uint32x4, unsigned, __m128i, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP, si128_si32) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_int32x4, int, __m128i, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP, si128_si32) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_float32x4, float, __m128, ps, _mm_castps_si128, _mm_castsi128_ps, ss_f32) + +inline int v_reduce_sum(const v_int16x8& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline unsigned v_reduce_sum(const v_uint16x8& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ + uint64 CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} +inline int64 v_reduce_sum(const v_int64x2& a) +{ + int64 CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} +inline double v_reduce_sum(const v_float64x2& a) +{ + double CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ +#if CV_SSE3 + __m128 ab = _mm_hadd_ps(a.val, b.val); + __m128 cd = _mm_hadd_ps(c.val, d.val); + return v_float32x4(_mm_hadd_ps(ab, cd)); +#else + __m128 ac = _mm_add_ps(_mm_unpacklo_ps(a.val, c.val), _mm_unpackhi_ps(a.val, c.val)); + __m128 bd = _mm_add_ps(_mm_unpacklo_ps(b.val, d.val), _mm_unpackhi_ps(b.val, d.val)); + return v_float32x4(_mm_add_ps(_mm_unpacklo_ps(ac, bd), _mm_unpackhi_ps(ac, bd))); +#endif +} + +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_float32x4, float, min, std::min) + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i half = _mm_sad_epu8(a.val, b.val); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + __m128i half = _mm_set1_epi8(0x7f); + half = _mm_sad_epu8(_mm_add_epi8(a.val, half), _mm_add_epi8(b.val, half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ + __m128i m1 = _mm_set1_epi32(0x55555555); + __m128i m2 = _mm_set1_epi32(0x33333333); + __m128i m4 = _mm_set1_epi32(0x0f0f0f0f); + __m128i p = a.val; + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 1), m1), _mm_and_si128(p, m1)); + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 2), m2), _mm_and_si128(p, m2)); + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 4), m4), _mm_and_si128(p, m4)); + return v_uint8x16(p); +} +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v_setall_u16(0x00ff); +} +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v_setall_u32(0x000000ff); +} +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ + return v_uint64x2(_mm_sad_epu8(v_popcount(v_reinterpret_as_u8(a)).val, _mm_setzero_si128())); +} +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +#define OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(_Tpvec, suffix, cast_op, allmask) \ +inline int v_signmask(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)); } \ +inline bool v_check_all(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)) == allmask; } \ +inline bool v_check_any(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)) != 0; } +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint8x16, epi8, OPENCV_HAL_NOP, 65535) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int8x16, epi8, OPENCV_HAL_NOP, 65535) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint32x4, ps, _mm_castsi128_ps, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int32x4, ps, _mm_castsi128_ps, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint64x2, pd, _mm_castsi128_pd, 3) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int64x2, pd, _mm_castsi128_pd, 3) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float32x4, ps, OPENCV_HAL_NOP, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float64x2, pd, OPENCV_HAL_NOP, 3) + +#define OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(_Tpvec) \ +inline int v_signmask(const _Tpvec& a) { return _mm_movemask_epi8(_mm_packs_epi16(a.val, a.val)) & 255; } \ +inline bool v_check_all(const _Tpvec& a) { return (_mm_movemask_epi8(a.val) & 0xaaaa) == 0xaaaa; } \ +inline bool v_check_any(const _Tpvec& a) { return (_mm_movemask_epi8(a.val) & 0xaaaa) != 0; } +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(v_uint16x8) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(v_int16x8) + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +#if CV_SSE4_1 +#define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, cast_ret, cast, suffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(cast_ret(_mm_blendv_##suffix(cast(b.val), cast(a.val), cast(mask.val)))); \ +} + +OPENCV_HAL_IMPL_SSE_SELECT(v_uint8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +// OPENCV_HAL_IMPL_SSE_SELECT(v_uint64x2, TBD, TBD, pd) +// OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, TBD, TBD, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, OPENCV_HAL_NOP, OPENCV_HAL_NOP, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, OPENCV_HAL_NOP, OPENCV_HAL_NOP, pd) + +#else // CV_SSE4_1 + +#define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, suffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(_mm_xor_##suffix(b.val, _mm_and_##suffix(_mm_xor_##suffix(b.val, a.val), mask.val))); \ +} + +OPENCV_HAL_IMPL_SSE_SELECT(v_uint8x16, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int8x16, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint16x8, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int16x8, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint32x4, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, si128) +// OPENCV_HAL_IMPL_SSE_SELECT(v_uint64x2, si128) +// OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, pd) +#endif + +/* Expand */ +#define OPENCV_HAL_IMPL_SSE_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(a.val); \ + b1.val = __CV_CAT(intrin, _high)(a.val); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(a.val)); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(__CV_CAT(intrin, _high)(a.val)); } \ + inline _Tpwvec v_load_expand(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadl_epi64((const __m128i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint8x16, v_uint16x8, uchar, _v128_cvtepu8_epi16) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int8x16, v_int16x8, schar, _v128_cvtepi8_epi16) +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint16x8, v_uint32x4, ushort, _v128_cvtepu16_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int16x8, v_int32x4, short, _v128_cvtepi16_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint32x4, v_uint64x2, unsigned, _v128_cvtepu32_epi64) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int32x4, v_int64x2, int, _v128_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_SSE_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_cvtsi32_si128(*(const int*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_SSE_EXPAND_Q(v_uint32x4, uchar, _v128_cvtepu8_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND_Q(v_int32x4, schar, _v128_cvtepi8_epi32) + +#define OPENCV_HAL_IMPL_SSE_UNPACKS(_Tpvec, suffix, cast_from, cast_to) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = _mm_unpacklo_##suffix(a0.val, a1.val); \ + b1.val = _mm_unpackhi_##suffix(a0.val, a1.val); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + return _Tpvec(cast_to(_mm_unpacklo_epi64(a1, b1))); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + return _Tpvec(cast_to(_mm_unpackhi_epi64(a1, b1))); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + c.val = cast_to(_mm_unpacklo_epi64(a1, b1)); \ + d.val = cast_to(_mm_unpackhi_epi64(a1, b1)); \ +} + +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint8x16, epi8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int8x16, epi8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint16x8, epi16, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int16x8, epi16, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_float32x4, ps, _mm_castps_si128, _mm_castsi128_ps) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_float64x2, pd, _mm_castpd_si128, _mm_castsi128_pd) + +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ +#if CV_SSSE3 + static const __m128i perm = _mm_setr_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + return v_uint8x16(_mm_shuffle_epi8(a.val, perm)); +#else + uchar CV_DECL_ALIGNED(32) d[16]; + v_store_aligned(d, a); + return v_uint8x16(d[15], d[14], d[13], d[12], d[11], d[10], d[9], d[8], d[7], d[6], d[5], d[4], d[3], d[2], d[1], d[0]); +#endif +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ +#if CV_SSSE3 + static const __m128i perm = _mm_setr_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + return v_uint16x8(_mm_shuffle_epi8(a.val, perm)); +#else + __m128i r = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 1, 2, 3)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(2, 3, 0, 1)); + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(2, 3, 0, 1)); + return v_uint16x8(r); +#endif +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + return v_uint32x4(_mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 1, 2, 3))); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + return v_uint64x2(_mm_shuffle_epi32(a.val, _MM_SHUFFLE(1, 0, 3, 2))); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ + return v_rotate_right(a, b); +} + +inline v_int32x4 v_round(const v_float32x4& a) +{ return v_int32x4(_mm_cvtps_epi32(a.val)); } + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + __m128i a1 = _mm_cvtps_epi32(a.val); + __m128i mask = _mm_castps_si128(_mm_cmpgt_ps(_mm_cvtepi32_ps(a1), a.val)); + return v_int32x4(_mm_add_epi32(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + __m128i a1 = _mm_cvtps_epi32(a.val); + __m128i mask = _mm_castps_si128(_mm_cmpgt_ps(a.val, _mm_cvtepi32_ps(a1))); + return v_int32x4(_mm_sub_epi32(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(_mm_cvttps_epi32(a.val)); } + +inline v_int32x4 v_round(const v_float64x2& a) +{ return v_int32x4(_mm_cvtpd_epi32(a.val)); } + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + __m128i ai = _mm_cvtpd_epi32(a.val), bi = _mm_cvtpd_epi32(b.val); + return v_int32x4(_mm_unpacklo_epi64(ai, bi)); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + __m128i a1 = _mm_cvtpd_epi32(a.val); + __m128i mask = _mm_castpd_si128(_mm_cmpgt_pd(_mm_cvtepi32_pd(a1), a.val)); + mask = _mm_srli_si128(_mm_slli_si128(mask, 4), 8); // m0 m0 m1 m1 => m0 m1 0 0 + return v_int32x4(_mm_add_epi32(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + __m128i a1 = _mm_cvtpd_epi32(a.val); + __m128i mask = _mm_castpd_si128(_mm_cmpgt_pd(a.val, _mm_cvtepi32_pd(a1))); + mask = _mm_srli_si128(_mm_slli_si128(mask, 4), 8); // m0 m0 m1 m1 => m0 m1 0 0 + return v_int32x4(_mm_sub_epi32(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ return v_int32x4(_mm_cvttpd_epi32(a.val)); } + +#define OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + __m128i t0 = cast_from(_mm_unpacklo_##suffix(a0.val, a1.val)); \ + __m128i t1 = cast_from(_mm_unpacklo_##suffix(a2.val, a3.val)); \ + __m128i t2 = cast_from(_mm_unpackhi_##suffix(a0.val, a1.val)); \ + __m128i t3 = cast_from(_mm_unpackhi_##suffix(a2.val, a3.val)); \ +\ + b0.val = cast_to(_mm_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm_unpackhi_epi64(t2, t3)); \ +} + +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_uint32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_int32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_float32x4, ps, _mm_castps_si128, _mm_castsi128_ps) + +// load deinterleave +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b) +{ + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + + __m128i t10 = _mm_unpacklo_epi8(t00, t01); + __m128i t11 = _mm_unpackhi_epi8(t00, t01); + + __m128i t20 = _mm_unpacklo_epi8(t10, t11); + __m128i t21 = _mm_unpackhi_epi8(t10, t11); + + __m128i t30 = _mm_unpacklo_epi8(t20, t21); + __m128i t31 = _mm_unpackhi_epi8(t20, t21); + + a.val = _mm_unpacklo_epi8(t30, t31); + b.val = _mm_unpackhi_epi8(t30, t31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c) +{ +#if CV_SSE4_1 + const __m128i m0 = _mm_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + const __m128i m1 = _mm_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + __m128i s0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i s1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i s2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + __m128i a0 = _mm_blendv_epi8(_mm_blendv_epi8(s0, s1, m0), s2, m1); + __m128i b0 = _mm_blendv_epi8(_mm_blendv_epi8(s1, s2, m0), s0, m1); + __m128i c0 = _mm_blendv_epi8(_mm_blendv_epi8(s2, s0, m0), s1, m1); + const __m128i sh_b = _mm_setr_epi8(0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13); + const __m128i sh_g = _mm_setr_epi8(1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14); + const __m128i sh_r = _mm_setr_epi8(2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15); + a0 = _mm_shuffle_epi8(a0, sh_b); + b0 = _mm_shuffle_epi8(b0, sh_g); + c0 = _mm_shuffle_epi8(c0, sh_r); + a.val = a0; + b.val = b0; + c.val = c0; +#elif CV_SSSE3 + const __m128i m0 = _mm_setr_epi8(0, 3, 6, 9, 12, 15, 1, 4, 7, 10, 13, 2, 5, 8, 11, 14); + const __m128i m1 = _mm_alignr_epi8(m0, m0, 11); + const __m128i m2 = _mm_alignr_epi8(m0, m0, 6); + + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + + __m128i s0 = _mm_shuffle_epi8(t0, m0); + __m128i s1 = _mm_shuffle_epi8(t1, m1); + __m128i s2 = _mm_shuffle_epi8(t2, m2); + + t0 = _mm_alignr_epi8(s1, _mm_slli_si128(s0, 10), 5); + a.val = _mm_alignr_epi8(s2, t0, 5); + + t1 = _mm_alignr_epi8(_mm_srli_si128(s1, 5), _mm_slli_si128(s0, 5), 6); + b.val = _mm_alignr_epi8(_mm_srli_si128(s2, 5), t1, 5); + + t2 = _mm_alignr_epi8(_mm_srli_si128(s2, 10), s1, 11); + c.val = _mm_alignr_epi8(t2, s0, 11); +#else + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + + __m128i t10 = _mm_unpacklo_epi8(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi8(t01, _mm_unpackhi_epi64(t02, t02)); + + __m128i t20 = _mm_unpacklo_epi8(t10, _mm_unpackhi_epi64(t11, t11)); + __m128i t21 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t10, t10), t12); + __m128i t22 = _mm_unpacklo_epi8(t11, _mm_unpackhi_epi64(t12, t12)); + + __m128i t30 = _mm_unpacklo_epi8(t20, _mm_unpackhi_epi64(t21, t21)); + __m128i t31 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t20, t20), t22); + __m128i t32 = _mm_unpacklo_epi8(t21, _mm_unpackhi_epi64(t22, t22)); + + a.val = _mm_unpacklo_epi8(t30, _mm_unpackhi_epi64(t31, t31)); + b.val = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t30, t30), t32); + c.val = _mm_unpacklo_epi8(t31, _mm_unpackhi_epi64(t32, t32)); +#endif +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c, v_uint8x16& d) +{ + __m128i u0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 c0 d0 a1 b1 c1 d1 ... + __m128i u1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); // a4 b4 c4 d4 ... + __m128i u2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); // a8 b8 c8 d8 ... + __m128i u3 = _mm_loadu_si128((const __m128i*)(ptr + 48)); // a12 b12 c12 d12 ... + + __m128i v0 = _mm_unpacklo_epi8(u0, u2); // a0 a8 b0 b8 ... + __m128i v1 = _mm_unpackhi_epi8(u0, u2); // a2 a10 b2 b10 ... + __m128i v2 = _mm_unpacklo_epi8(u1, u3); // a4 a12 b4 b12 ... + __m128i v3 = _mm_unpackhi_epi8(u1, u3); // a6 a14 b6 b14 ... + + u0 = _mm_unpacklo_epi8(v0, v2); // a0 a4 a8 a12 ... + u1 = _mm_unpacklo_epi8(v1, v3); // a2 a6 a10 a14 ... + u2 = _mm_unpackhi_epi8(v0, v2); // a1 a5 a9 a13 ... + u3 = _mm_unpackhi_epi8(v1, v3); // a3 a7 a11 a15 ... + + v0 = _mm_unpacklo_epi8(u0, u1); // a0 a2 a4 a6 ... + v1 = _mm_unpacklo_epi8(u2, u3); // a1 a3 a5 a7 ... + v2 = _mm_unpackhi_epi8(u0, u1); // c0 c2 c4 c6 ... + v3 = _mm_unpackhi_epi8(u2, u3); // c1 c3 c5 c7 ... + + a.val = _mm_unpacklo_epi8(v0, v1); + b.val = _mm_unpackhi_epi8(v0, v1); + c.val = _mm_unpacklo_epi8(v2, v3); + d.val = _mm_unpackhi_epi8(v2, v3); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b) +{ + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); // a0 b0 a1 b1 a2 b2 a3 b3 + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 8)); // a4 b4 a5 b5 a6 b6 a7 b7 + + __m128i v2 = _mm_unpacklo_epi16(v0, v1); // a0 a4 b0 b4 a1 a5 b1 b5 + __m128i v3 = _mm_unpackhi_epi16(v0, v1); // a2 a6 b2 b6 a3 a7 b3 b7 + __m128i v4 = _mm_unpacklo_epi16(v2, v3); // a0 a2 a4 a6 b0 b2 b4 b6 + __m128i v5 = _mm_unpackhi_epi16(v2, v3); // a1 a3 a5 a7 b1 b3 b5 b7 + + a.val = _mm_unpacklo_epi16(v4, v5); // a0 a1 a2 a3 a4 a5 a6 a7 + b.val = _mm_unpackhi_epi16(v4, v5); // b0 b1 ab b3 b4 b5 b6 b7 +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c) +{ +#if CV_SSE4_1 + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 8)); + __m128i v2 = _mm_loadu_si128((__m128i*)(ptr + 16)); + __m128i a0 = _mm_blend_epi16(_mm_blend_epi16(v0, v1, 0x92), v2, 0x24); + __m128i b0 = _mm_blend_epi16(_mm_blend_epi16(v2, v0, 0x92), v1, 0x24); + __m128i c0 = _mm_blend_epi16(_mm_blend_epi16(v1, v2, 0x92), v0, 0x24); + + const __m128i sh_a = _mm_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m128i sh_b = _mm_setr_epi8(2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13); + const __m128i sh_c = _mm_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + a0 = _mm_shuffle_epi8(a0, sh_a); + b0 = _mm_shuffle_epi8(b0, sh_b); + c0 = _mm_shuffle_epi8(c0, sh_c); + + a.val = a0; + b.val = b0; + c.val = c0; +#else + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 8)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + + __m128i t10 = _mm_unpacklo_epi16(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi16(t01, _mm_unpackhi_epi64(t02, t02)); + + __m128i t20 = _mm_unpacklo_epi16(t10, _mm_unpackhi_epi64(t11, t11)); + __m128i t21 = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t10, t10), t12); + __m128i t22 = _mm_unpacklo_epi16(t11, _mm_unpackhi_epi64(t12, t12)); + + a.val = _mm_unpacklo_epi16(t20, _mm_unpackhi_epi64(t21, t21)); + b.val = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t20, t20), t22); + c.val = _mm_unpacklo_epi16(t21, _mm_unpackhi_epi64(t22, t22)); +#endif +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c, v_uint16x8& d) +{ + __m128i u0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 c0 d0 a1 b1 c1 d1 + __m128i u1 = _mm_loadu_si128((const __m128i*)(ptr + 8)); // a2 b2 c2 d2 ... + __m128i u2 = _mm_loadu_si128((const __m128i*)(ptr + 16)); // a4 b4 c4 d4 ... + __m128i u3 = _mm_loadu_si128((const __m128i*)(ptr + 24)); // a6 b6 c6 d6 ... + + __m128i v0 = _mm_unpacklo_epi16(u0, u2); // a0 a4 b0 b4 ... + __m128i v1 = _mm_unpackhi_epi16(u0, u2); // a1 a5 b1 b5 ... + __m128i v2 = _mm_unpacklo_epi16(u1, u3); // a2 a6 b2 b6 ... + __m128i v3 = _mm_unpackhi_epi16(u1, u3); // a3 a7 b3 b7 ... + + u0 = _mm_unpacklo_epi16(v0, v2); // a0 a2 a4 a6 ... + u1 = _mm_unpacklo_epi16(v1, v3); // a1 a3 a5 a7 ... + u2 = _mm_unpackhi_epi16(v0, v2); // c0 c2 c4 c6 ... + u3 = _mm_unpackhi_epi16(v1, v3); // c1 c3 c5 c7 ... + + a.val = _mm_unpacklo_epi16(u0, u1); + b.val = _mm_unpackhi_epi16(u0, u1); + c.val = _mm_unpacklo_epi16(u2, u3); + d.val = _mm_unpackhi_epi16(u2, u3); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b) +{ + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); // a0 b0 a1 b1 + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 4)); // a2 b2 a3 b3 + + __m128i v2 = _mm_unpacklo_epi32(v0, v1); // a0 a2 b0 b2 + __m128i v3 = _mm_unpackhi_epi32(v0, v1); // a1 a3 b1 b3 + + a.val = _mm_unpacklo_epi32(v2, v3); // a0 a1 a2 a3 + b.val = _mm_unpackhi_epi32(v2, v3); // b0 b1 ab b3 +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c) +{ + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 4)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 8)); + + __m128i t10 = _mm_unpacklo_epi32(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi32(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi32(t01, _mm_unpackhi_epi64(t02, t02)); + + a.val = _mm_unpacklo_epi32(t10, _mm_unpackhi_epi64(t11, t11)); + b.val = _mm_unpacklo_epi32(_mm_unpackhi_epi64(t10, t10), t12); + c.val = _mm_unpacklo_epi32(t11, _mm_unpackhi_epi64(t12, t12)); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 s0(_mm_loadu_si128((const __m128i*)ptr)); // a0 b0 c0 d0 + v_uint32x4 s1(_mm_loadu_si128((const __m128i*)(ptr + 4))); // a1 b1 c1 d1 + v_uint32x4 s2(_mm_loadu_si128((const __m128i*)(ptr + 8))); // a2 b2 c2 d2 + v_uint32x4 s3(_mm_loadu_si128((const __m128i*)(ptr + 12))); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b) +{ + __m128 u0 = _mm_loadu_ps(ptr); // a0 b0 a1 b1 + __m128 u1 = _mm_loadu_ps((ptr + 4)); // a2 b2 a3 b3 + + a.val = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(2, 0, 2, 0)); // a0 a1 a2 a3 + b.val = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(3, 1, 3, 1)); // b0 b1 ab b3 +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c) +{ + __m128 t0 = _mm_loadu_ps(ptr + 0); + __m128 t1 = _mm_loadu_ps(ptr + 4); + __m128 t2 = _mm_loadu_ps(ptr + 8); + + __m128 at12 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(0, 1, 0, 2)); + a.val = _mm_shuffle_ps(t0, at12, _MM_SHUFFLE(2, 0, 3, 0)); + + __m128 bt01 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(0, 0, 0, 1)); + __m128 bt12 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(0, 2, 0, 3)); + b.val = _mm_shuffle_ps(bt01, bt12, _MM_SHUFFLE(2, 0, 2, 0)); + + __m128 ct01 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(0, 1, 0, 2)); + c.val = _mm_shuffle_ps(ct01, t2, _MM_SHUFFLE(3, 0, 2, 0)); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c, v_float32x4& d) +{ + __m128 t0 = _mm_loadu_ps(ptr + 0); + __m128 t1 = _mm_loadu_ps(ptr + 4); + __m128 t2 = _mm_loadu_ps(ptr + 8); + __m128 t3 = _mm_loadu_ps(ptr + 12); + __m128 t02lo = _mm_unpacklo_ps(t0, t2); + __m128 t13lo = _mm_unpacklo_ps(t1, t3); + __m128 t02hi = _mm_unpackhi_ps(t0, t2); + __m128 t13hi = _mm_unpackhi_ps(t1, t3); + a.val = _mm_unpacklo_ps(t02lo, t13lo); + b.val = _mm_unpackhi_ps(t02lo, t13lo); + c.val = _mm_unpacklo_ps(t02hi, t13hi); + d.val = _mm_unpackhi_ps(t02hi, t13hi); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t1)); + b = v_uint64x2(_mm_unpackhi_epi64(t0, t1)); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b, v_uint64x2& c) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); // a0, b0 + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); // c0, a1 + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 4)); // b1, c1 + + t1 = _mm_shuffle_epi32(t1, 0x4e); // a1, c0 + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t1)); + b = v_uint64x2(_mm_unpacklo_epi64(_mm_unpackhi_epi64(t0, t0), t2)); + c = v_uint64x2(_mm_unpackhi_epi64(t1, t2)); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, + v_uint64x2& b, v_uint64x2& c, v_uint64x2& d) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); // c0 d0 + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 4)); // a1 b1 + __m128i t3 = _mm_loadu_si128((const __m128i*)(ptr + 6)); // c1 d1 + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t2)); + b = v_uint64x2(_mm_unpackhi_epi64(t0, t2)); + c = v_uint64x2(_mm_unpacklo_epi64(t1, t3)); + d = v_uint64x2(_mm_unpackhi_epi64(t1, t3)); +} + +// store interleave + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi8(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi8(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ +#if CV_SSE4_1 + const __m128i sh_a = _mm_setr_epi8(0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5); + const __m128i sh_b = _mm_setr_epi8(5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10); + const __m128i sh_c = _mm_setr_epi8(10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15); + __m128i a0 = _mm_shuffle_epi8(a.val, sh_a); + __m128i b0 = _mm_shuffle_epi8(b.val, sh_b); + __m128i c0 = _mm_shuffle_epi8(c.val, sh_c); + + const __m128i m0 = _mm_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + const __m128i m1 = _mm_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + __m128i v0 = _mm_blendv_epi8(_mm_blendv_epi8(a0, b0, m1), c0, m0); + __m128i v1 = _mm_blendv_epi8(_mm_blendv_epi8(b0, c0, m1), a0, m0); + __m128i v2 = _mm_blendv_epi8(_mm_blendv_epi8(c0, a0, m1), b0, m0); +#elif CV_SSSE3 + const __m128i m0 = _mm_setr_epi8(0, 6, 11, 1, 7, 12, 2, 8, 13, 3, 9, 14, 4, 10, 15, 5); + const __m128i m1 = _mm_setr_epi8(5, 11, 0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10); + const __m128i m2 = _mm_setr_epi8(10, 0, 5, 11, 1, 6, 12, 2, 7, 13, 3, 8, 14, 4, 9, 15); + + __m128i t0 = _mm_alignr_epi8(b.val, _mm_slli_si128(a.val, 10), 5); + t0 = _mm_alignr_epi8(c.val, t0, 5); + __m128i v0 = _mm_shuffle_epi8(t0, m0); + + __m128i t1 = _mm_alignr_epi8(_mm_srli_si128(b.val, 5), _mm_slli_si128(a.val, 5), 6); + t1 = _mm_alignr_epi8(_mm_srli_si128(c.val, 5), t1, 5); + __m128i v1 = _mm_shuffle_epi8(t1, m1); + + __m128i t2 = _mm_alignr_epi8(_mm_srli_si128(c.val, 10), b.val, 11); + t2 = _mm_alignr_epi8(t2, a.val, 11); + __m128i v2 = _mm_shuffle_epi8(t2, m2); +#else + __m128i z = _mm_setzero_si128(); + __m128i ab0 = _mm_unpacklo_epi8(a.val, b.val); + __m128i ab1 = _mm_unpackhi_epi8(a.val, b.val); + __m128i c0 = _mm_unpacklo_epi8(c.val, z); + __m128i c1 = _mm_unpackhi_epi8(c.val, z); + + __m128i p00 = _mm_unpacklo_epi16(ab0, c0); + __m128i p01 = _mm_unpackhi_epi16(ab0, c0); + __m128i p02 = _mm_unpacklo_epi16(ab1, c1); + __m128i p03 = _mm_unpackhi_epi16(ab1, c1); + + __m128i p10 = _mm_unpacklo_epi32(p00, p01); + __m128i p11 = _mm_unpackhi_epi32(p00, p01); + __m128i p12 = _mm_unpacklo_epi32(p02, p03); + __m128i p13 = _mm_unpackhi_epi32(p02, p03); + + __m128i p20 = _mm_unpacklo_epi64(p10, p11); + __m128i p21 = _mm_unpackhi_epi64(p10, p11); + __m128i p22 = _mm_unpacklo_epi64(p12, p13); + __m128i p23 = _mm_unpackhi_epi64(p12, p13); + + p20 = _mm_slli_si128(p20, 1); + p22 = _mm_slli_si128(p22, 1); + + __m128i p30 = _mm_slli_epi64(_mm_unpacklo_epi32(p20, p21), 8); + __m128i p31 = _mm_srli_epi64(_mm_unpackhi_epi32(p20, p21), 8); + __m128i p32 = _mm_slli_epi64(_mm_unpacklo_epi32(p22, p23), 8); + __m128i p33 = _mm_srli_epi64(_mm_unpackhi_epi32(p22, p23), 8); + + __m128i p40 = _mm_unpacklo_epi64(p30, p31); + __m128i p41 = _mm_unpackhi_epi64(p30, p31); + __m128i p42 = _mm_unpacklo_epi64(p32, p33); + __m128i p43 = _mm_unpackhi_epi64(p32, p33); + + __m128i v0 = _mm_or_si128(_mm_srli_si128(p40, 2), _mm_slli_si128(p41, 10)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(p41, 6), _mm_slli_si128(p42, 6)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(p42, 10), _mm_slli_si128(p43, 2)); +#endif + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + _mm_stream_si128((__m128i*)(ptr + 32), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + _mm_store_si128((__m128i*)(ptr + 32), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + _mm_storeu_si128((__m128i*)(ptr + 32), v2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, const v_uint8x16& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + __m128i u0 = _mm_unpacklo_epi8(a.val, c.val); // a0 c0 a1 c1 ... + __m128i u1 = _mm_unpackhi_epi8(a.val, c.val); // a8 c8 a9 c9 ... + __m128i u2 = _mm_unpacklo_epi8(b.val, d.val); // b0 d0 b1 d1 ... + __m128i u3 = _mm_unpackhi_epi8(b.val, d.val); // b8 d8 b9 d9 ... + + __m128i v0 = _mm_unpacklo_epi8(u0, u2); // a0 b0 c0 d0 ... + __m128i v1 = _mm_unpackhi_epi8(u0, u2); // a4 b4 c4 d4 ... + __m128i v2 = _mm_unpacklo_epi8(u1, u3); // a8 b8 c8 d8 ... + __m128i v3 = _mm_unpackhi_epi8(u1, u3); // a12 b12 c12 d12 ... + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + _mm_stream_si128((__m128i*)(ptr + 32), v2); + _mm_stream_si128((__m128i*)(ptr + 48), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + _mm_store_si128((__m128i*)(ptr + 32), v2); + _mm_store_si128((__m128i*)(ptr + 48), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + _mm_storeu_si128((__m128i*)(ptr + 32), v2); + _mm_storeu_si128((__m128i*)(ptr + 48), v3); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi16(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi16(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, + const v_uint16x8& b, const v_uint16x8& c, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ +#if CV_SSE4_1 + const __m128i sh_a = _mm_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m128i sh_b = _mm_setr_epi8(10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5); + const __m128i sh_c = _mm_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + __m128i a0 = _mm_shuffle_epi8(a.val, sh_a); + __m128i b0 = _mm_shuffle_epi8(b.val, sh_b); + __m128i c0 = _mm_shuffle_epi8(c.val, sh_c); + + __m128i v0 = _mm_blend_epi16(_mm_blend_epi16(a0, b0, 0x92), c0, 0x24); + __m128i v1 = _mm_blend_epi16(_mm_blend_epi16(c0, a0, 0x92), b0, 0x24); + __m128i v2 = _mm_blend_epi16(_mm_blend_epi16(b0, c0, 0x92), a0, 0x24); +#else + __m128i z = _mm_setzero_si128(); + __m128i ab0 = _mm_unpacklo_epi16(a.val, b.val); + __m128i ab1 = _mm_unpackhi_epi16(a.val, b.val); + __m128i c0 = _mm_unpacklo_epi16(c.val, z); + __m128i c1 = _mm_unpackhi_epi16(c.val, z); + + __m128i p10 = _mm_unpacklo_epi32(ab0, c0); + __m128i p11 = _mm_unpackhi_epi32(ab0, c0); + __m128i p12 = _mm_unpacklo_epi32(ab1, c1); + __m128i p13 = _mm_unpackhi_epi32(ab1, c1); + + __m128i p20 = _mm_unpacklo_epi64(p10, p11); + __m128i p21 = _mm_unpackhi_epi64(p10, p11); + __m128i p22 = _mm_unpacklo_epi64(p12, p13); + __m128i p23 = _mm_unpackhi_epi64(p12, p13); + + p20 = _mm_slli_si128(p20, 2); + p22 = _mm_slli_si128(p22, 2); + + __m128i p30 = _mm_unpacklo_epi64(p20, p21); + __m128i p31 = _mm_unpackhi_epi64(p20, p21); + __m128i p32 = _mm_unpacklo_epi64(p22, p23); + __m128i p33 = _mm_unpackhi_epi64(p22, p23); + + __m128i v0 = _mm_or_si128(_mm_srli_si128(p30, 2), _mm_slli_si128(p31, 10)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(p31, 6), _mm_slli_si128(p32, 6)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(p32, 10), _mm_slli_si128(p33, 2)); +#endif + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + _mm_stream_si128((__m128i*)(ptr + 16), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + _mm_store_si128((__m128i*)(ptr + 16), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + _mm_storeu_si128((__m128i*)(ptr + 16), v2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + const v_uint16x8& c, const v_uint16x8& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + __m128i u0 = _mm_unpacklo_epi16(a.val, c.val); // a0 c0 a1 c1 ... + __m128i u1 = _mm_unpackhi_epi16(a.val, c.val); // a4 c4 a5 c5 ... + __m128i u2 = _mm_unpacklo_epi16(b.val, d.val); // b0 d0 b1 d1 ... + __m128i u3 = _mm_unpackhi_epi16(b.val, d.val); // b4 d4 b5 d5 ... + + __m128i v0 = _mm_unpacklo_epi16(u0, u2); // a0 b0 c0 d0 ... + __m128i v1 = _mm_unpackhi_epi16(u0, u2); // a2 b2 c2 d2 ... + __m128i v2 = _mm_unpacklo_epi16(u1, u3); // a4 b4 c4 d4 ... + __m128i v3 = _mm_unpackhi_epi16(u1, u3); // a6 b6 c6 d6 ... + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + _mm_stream_si128((__m128i*)(ptr + 16), v2); + _mm_stream_si128((__m128i*)(ptr + 24), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + _mm_store_si128((__m128i*)(ptr + 16), v2); + _mm_store_si128((__m128i*)(ptr + 24), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + _mm_storeu_si128((__m128i*)(ptr + 16), v2); + _mm_storeu_si128((__m128i*)(ptr + 24), v3); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 4), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 4), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 4), v1); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + v_uint32x4 z = v_setzero_u32(), u0, u1, u2, u3; + v_transpose4x4(a, b, c, z, u0, u1, u2, u3); + + __m128i v0 = _mm_or_si128(u0.val, _mm_slli_si128(u1.val, 12)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(u1.val, 4), _mm_slli_si128(u2.val, 8)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(u2.val, 8), _mm_slli_si128(u3.val, 4)); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 4), v1); + _mm_stream_si128((__m128i*)(ptr + 8), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 4), v1); + _mm_store_si128((__m128i*)(ptr + 8), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 4), v1); + _mm_storeu_si128((__m128i*)(ptr + 8), v2); + } +} + +inline void v_store_interleave(unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + v_uint32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0.val); + _mm_stream_si128((__m128i*)(ptr + 4), v1.val); + _mm_stream_si128((__m128i*)(ptr + 8), v2.val); + _mm_stream_si128((__m128i*)(ptr + 12), v3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0.val); + _mm_store_si128((__m128i*)(ptr + 4), v1.val); + _mm_store_si128((__m128i*)(ptr + 8), v2.val); + _mm_store_si128((__m128i*)(ptr + 12), v3.val); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0.val); + _mm_storeu_si128((__m128i*)(ptr + 4), v1.val); + _mm_storeu_si128((__m128i*)(ptr + 8), v2.val); + _mm_storeu_si128((__m128i*)(ptr + 12), v3.val); + } +} + +// 2-channel, float only +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 v0 = _mm_unpacklo_ps(a.val, b.val); // a0 b0 a1 b1 + __m128 v1 = _mm_unpackhi_ps(a.val, b.val); // a2 b2 a3 b3 + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + } +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 u0 = _mm_shuffle_ps(a.val, b.val, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 u1 = _mm_shuffle_ps(c.val, a.val, _MM_SHUFFLE(1, 1, 0, 0)); + __m128 v0 = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(2, 0, 2, 0)); + __m128 u2 = _mm_shuffle_ps(b.val, c.val, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 u3 = _mm_shuffle_ps(a.val, b.val, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v1 = _mm_shuffle_ps(u2, u3, _MM_SHUFFLE(2, 0, 2, 0)); + __m128 u4 = _mm_shuffle_ps(c.val, a.val, _MM_SHUFFLE(3, 3, 2, 2)); + __m128 u5 = _mm_shuffle_ps(b.val, c.val, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 v2 = _mm_shuffle_ps(u4, u5, _MM_SHUFFLE(2, 0, 2, 0)); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + _mm_stream_ps(ptr + 8, v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + _mm_store_ps(ptr + 8, v2); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + _mm_storeu_ps(ptr + 8, v2); + } +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 u0 = _mm_unpacklo_ps(a.val, c.val); + __m128 u1 = _mm_unpacklo_ps(b.val, d.val); + __m128 u2 = _mm_unpackhi_ps(a.val, c.val); + __m128 u3 = _mm_unpackhi_ps(b.val, d.val); + __m128 v0 = _mm_unpacklo_ps(u0, u1); + __m128 v2 = _mm_unpacklo_ps(u2, u3); + __m128 v1 = _mm_unpackhi_ps(u0, u1); + __m128 v3 = _mm_unpackhi_ps(u2, u3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + _mm_stream_ps(ptr + 8, v2); + _mm_stream_ps(ptr + 12, v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + _mm_store_ps(ptr + 8, v2); + _mm_store_ps(ptr + 12, v3); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + _mm_storeu_ps(ptr + 8, v2); + _mm_storeu_ps(ptr + 12, v3); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi64(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpacklo_epi64(c.val, _mm_unpackhi_epi64(a.val, a.val)); + __m128i v2 = _mm_unpackhi_epi64(b.val, c.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + _mm_stream_si128((__m128i*)(ptr + 4), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + _mm_store_si128((__m128i*)(ptr + 4), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + _mm_storeu_si128((__m128i*)(ptr + 4), v2); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, const v_uint64x2& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpacklo_epi64(c.val, d.val); + __m128i v2 = _mm_unpackhi_epi64(a.val, b.val); + __m128i v3 = _mm_unpackhi_epi64(c.val, d.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + _mm_stream_si128((__m128i*)(ptr + 4), v2); + _mm_stream_si128((__m128i*)(ptr + 6), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + _mm_store_si128((__m128i*)(ptr + 4), v2); + _mm_store_si128((__m128i*)(ptr + 6), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + _mm_storeu_si128((__m128i*)(ptr + 4), v2); + _mm_storeu_si128((__m128i*)(ptr + 6), v3); + } +} + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int8x16, schar, s8, v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int16x8, short, s16, v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int32x4, int, s32, v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int64x2, int64, s64, v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_float64x2, double, f64, v_uint64x2, uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(_mm_cvtepi32_ps(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + return v_float32x4(_mm_cvtpd_ps(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(_mm_movelh_ps(_mm_cvtpd_ps(a.val), _mm_cvtpd_ps(b.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(_mm_cvtepi32_pd(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(_mm_cvtepi32_pd(_mm_srli_si128(a.val,8))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(_mm_cvtps_pd(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(_mm_cvtps_pd(_mm_movehl_ps(a.val, a.val))); +} + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x2 v_cvt_f64(const v_int64x2& v) +{ + // constants encoded as floating-point + __m128i magic_i_hi32 = _mm_set1_epi64x(0x4530000080000000); // 2^84 + 2^63 + __m128i magic_i_all = _mm_set1_epi64x(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m128d magic_d_all = _mm_castsi128_pd(magic_i_all); + // Blend the 32 lowest significant bits of v with magic_int_lo +#if CV_SSE4_1 + __m128i magic_i_lo = _mm_set1_epi64x(0x4330000000000000); // 2^52 + __m128i v_lo = _mm_blend_epi16(v.val, magic_i_lo, 0xcc); +#else + __m128i magic_i_lo = _mm_set1_epi32(0x43300000); // 2^52 + __m128i v_lo = _mm_unpacklo_epi32(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(0, 0, 2, 0)), magic_i_lo); +#endif + // Extract the 32 most significant bits of v + __m128i v_hi = _mm_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm_xor_si128(v_hi, magic_i_hi32); + // Compute in double precision + __m128d v_hi_dbl = _mm_sub_pd(_mm_castsi128_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m128d result = _mm_add_pd(v_hi_dbl, _mm_castsi128_pd(v_lo)); + return v_float64x2(result); +} + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]]), + _mm_setr_pi8(tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]) + )); +#endif +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3]), + *(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7]))); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3])), + _mm_setr_pi16(*(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7])) + )); +#endif +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])), + _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])) + )); +#endif +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int16x8(_mm_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], + tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]])); +#else + return v_int16x8(_mm_setr_epi64( + _mm_setr_pi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]), + _mm_setr_pi16(tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]) + )); +#endif +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int16x8(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +#else + return v_int16x8(_mm_setr_epi64( + _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])), + _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])) + )); +#endif +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short *)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]], + tab[idx[2]], tab[idx[3]])); +#else + return v_int32x4(_mm_setr_epi64( + _mm_setr_pi32(tab[idx[0]], tab[idx[1]]), + _mm_setr_pi32(tab[idx[2]], tab[idx[3]]) + )); +#endif +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(_mm_loadu_si128((const __m128i*)(tab + idx[0]))); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int *)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int *)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int *)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(_mm_set_epi64x(tab[idx[1]], tab[idx[0]])); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(_mm_loadu_si128((const __m128i*)(tab + idx[0]))); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(_mm_setr_ps(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int *)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_quads((const int *)tab, idx)); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(_mm_setr_pd(tab[idx[0]], tab[idx[1]])); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_float64x2(_mm_castsi128_pd(_mm_loadu_si128((const __m128i*)(tab + idx[0])))); } + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + return v_float32x4(_mm_setr_ps(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + int idx[2]; + v_store_low(idx, idxvec); + return v_float64x2(_mm_setr_pd(tab[idx[0]], tab[idx[1]])); +} + +// loads pairs from the table and deinterleaves them, e.g. returns: +// x = (tab[idxvec[0], tab[idxvec[1]], tab[idxvec[2]], tab[idxvec[3]]), +// y = (tab[idxvec[0]+1], tab[idxvec[1]+1], tab[idxvec[2]+1], tab[idxvec[3]+1]) +// note that the indices are float's indices, not the float-pair indices. +// in theory, this function can be used to implement bilinear interpolation, +// when idxvec are the offsets within the image. +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + __m128 z = _mm_setzero_ps(); + __m128 xy01 = _mm_loadl_pi(z, (__m64*)(tab + idx[0])); + __m128 xy23 = _mm_loadl_pi(z, (__m64*)(tab + idx[2])); + xy01 = _mm_loadh_pi(xy01, (__m64*)(tab + idx[1])); + xy23 = _mm_loadh_pi(xy23, (__m64*)(tab + idx[3])); + __m128 xxyy02 = _mm_unpacklo_ps(xy01, xy23); + __m128 xxyy13 = _mm_unpackhi_ps(xy01, xy23); + x = v_float32x4(_mm_unpacklo_ps(xxyy02, xxyy13)); + y = v_float32x4(_mm_unpackhi_ps(xxyy02, xxyy13)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int idx[2]; + v_store_low(idx, idxvec); + __m128d xy0 = _mm_loadu_pd(tab + idx[0]); + __m128d xy1 = _mm_loadu_pd(tab + idx[1]); + x = v_float64x2(_mm_unpacklo_pd(xy0, xy1)); + y = v_float64x2(_mm_unpackhi_pd(xy0, xy1)); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0d0e0c0b090a08, 0x0705060403010200))); +#else + __m128i a = _mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(3, 1, 2, 0)); + a = _mm_shuffle_epi32(a, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int8x16(_mm_unpacklo_epi8(a, _mm_unpackhi_epi64(a, a))); +#endif +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0b0e0a0d090c08, 0x0703060205010400))); +#else + __m128i a = _mm_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int8x16(_mm_unpacklo_epi8(a, _mm_unpackhi_epi64(a, a))); +#endif +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0e0b0a0d0c0908, 0x0706030205040100))); +#else + __m128i a = _mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int16x8(_mm_shufflehi_epi16(a, _MM_SHUFFLE(3, 1, 2, 0))); +#endif +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0e07060d0c0504, 0x0b0a030209080100))); +#else + return v_int16x8(_mm_unpacklo_epi16(vec.val, _mm_unpackhi_epi64(vec.val, vec.val))); +#endif +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + return v_int32x4(_mm_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0))); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0xffffff0f0e0d0c0a, 0x0908060504020100))); +#else + __m128i mask = _mm_set1_epi64x(0x00000000FFFFFFFF); + __m128i a = _mm_srli_si128(_mm_or_si128(_mm_andnot_si128(mask, vec.val), _mm_and_si128(mask, _mm_sll_epi32(vec.val, _mm_set_epi64x(0, 8)))), 1); + return v_int8x16(_mm_srli_si128(_mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 1, 0, 3)), 2)); +#endif +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0xffff0f0e0d0c0b0a, 0x0908050403020100))); +#else + return v_int16x8(_mm_srli_si128(_mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(2, 1, 0, 3)), 2)); +#endif +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +template +inline uchar v_extract_n(const v_uint8x16& v) +{ +#if CV_SSE4_1 + return (uchar)_mm_extract_epi8(v.val, i); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline schar v_extract_n(const v_int8x16& v) +{ + return (schar)v_extract_n(v_reinterpret_as_u8(v)); +} + +template +inline ushort v_extract_n(const v_uint16x8& v) +{ + return (ushort)_mm_extract_epi16(v.val, i); +} + +template +inline short v_extract_n(const v_int16x8& v) +{ + return (short)v_extract_n(v_reinterpret_as_u16(v)); +} + +template +inline uint v_extract_n(const v_uint32x4& v) +{ +#if CV_SSE4_1 + return (uint)_mm_extract_epi32(v.val, i); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline int v_extract_n(const v_int32x4& v) +{ + return (int)v_extract_n(v_reinterpret_as_u32(v)); +} + +template +inline uint64 v_extract_n(const v_uint64x2& v) +{ +#ifdef CV__SIMD_NATIVE_mm_extract_epi64 + return (uint64)_v128_extract_epi64(v.val); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline int64 v_extract_n(const v_int64x2& v) +{ + return (int64)v_extract_n(v_reinterpret_as_u64(v)); +} + +template +inline float v_extract_n(const v_float32x4& v) +{ + union { uint iv; float fv; } d; + d.iv = v_extract_n(v_reinterpret_as_u32(v)); + return d.fv; +} + +template +inline double v_extract_n(const v_float64x2& v) +{ + union { uint64 iv; double dv; } d; + d.iv = v_extract_n(v_reinterpret_as_u64(v)); + return d.dv; +} + +template +inline v_int32x4 v_broadcast_element(const v_int32x4& v) +{ + return v_int32x4(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(i,i,i,i))); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& v) +{ + return v_uint32x4(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(i,i,i,i))); +} + +template +inline v_float32x4 v_broadcast_element(const v_float32x4& v) +{ + return v_float32x4(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE((char)i,(char)i,(char)i,(char)i))); +} + +////////////// FP16 support /////////////////////////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ +#if CV_FP16 + return v_float32x4(_mm_cvtph_ps(_mm_loadu_si128((const __m128i*)ptr))); +#else + const __m128i z = _mm_setzero_si128(), delta = _mm_set1_epi32(0x38000000); + const __m128i signmask = _mm_set1_epi32(0x80000000), maxexp = _mm_set1_epi32(0x7c000000); + const __m128 deltaf = _mm_castsi128_ps(_mm_set1_epi32(0x38800000)); + __m128i bits = _mm_unpacklo_epi16(z, _mm_loadl_epi64((const __m128i*)ptr)); // h << 16 + __m128i e = _mm_and_si128(bits, maxexp), sign = _mm_and_si128(bits, signmask); + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_xor_si128(bits, sign), 3), delta); // ((h & 0x7fff) << 13) + delta + __m128i zt = _mm_castps_si128(_mm_sub_ps(_mm_castsi128_ps(_mm_add_epi32(t, _mm_set1_epi32(1 << 23))), deltaf)); + + t = _mm_add_epi32(t, _mm_and_si128(delta, _mm_cmpeq_epi32(maxexp, e))); + __m128i zmask = _mm_cmpeq_epi32(e, z); + __m128i ft = v_select_si128(zmask, zt, t); + return v_float32x4(_mm_castsi128_ps(_mm_or_si128(ft, sign))); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ +#if CV_FP16 + __m128i fp16_value = _mm_cvtps_ph(v.val, 0); + _mm_storel_epi64((__m128i*)ptr, fp16_value); +#else + const __m128i signmask = _mm_set1_epi32(0x80000000); + const __m128i rval = _mm_set1_epi32(0x3f000000); + + __m128i t = _mm_castps_si128(v.val); + __m128i sign = _mm_srai_epi32(_mm_and_si128(t, signmask), 16); + t = _mm_andnot_si128(signmask, t); + + __m128i finitemask = _mm_cmpgt_epi32(_mm_set1_epi32(0x47800000), t); + __m128i isnan = _mm_cmpgt_epi32(t, _mm_set1_epi32(0x7f800000)); + __m128i naninf = v_select_si128(isnan, _mm_set1_epi32(0x7e00), _mm_set1_epi32(0x7c00)); + __m128i tinymask = _mm_cmpgt_epi32(_mm_set1_epi32(0x38800000), t); + __m128i tt = _mm_castps_si128(_mm_add_ps(_mm_castsi128_ps(t), _mm_castsi128_ps(rval))); + tt = _mm_sub_epi32(tt, rval); + __m128i odd = _mm_and_si128(_mm_srli_epi32(t, 13), _mm_set1_epi32(1)); + __m128i nt = _mm_add_epi32(t, _mm_set1_epi32(0xc8000fff)); + nt = _mm_srli_epi32(_mm_add_epi32(nt, odd), 13); + t = v_select_si128(tinymask, tt, nt); + t = v_select_si128(finitemask, t, naninf); + t = _mm_or_si128(t, sign); + t = _mm_packs_epi32(t, t); + _mm_storel_epi64((__m128i*)ptr, t); +#endif +} + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse_em.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse_em.hpp new file mode 100644 index 0000000..6fb0881 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_sse_em.hpp @@ -0,0 +1,180 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_SSE_EM_HPP +#define OPENCV_HAL_INTRIN_SSE_EM_HPP + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define OPENCV_HAL_SSE_WRAP_1(fun, tp) \ + inline tp _v128_##fun(const tp& a) \ + { return _mm_##fun(a); } + +#define OPENCV_HAL_SSE_WRAP_2(fun, tp) \ + inline tp _v128_##fun(const tp& a, const tp& b) \ + { return _mm_##fun(a, b); } + +#define OPENCV_HAL_SSE_WRAP_3(fun, tp) \ + inline tp _v128_##fun(const tp& a, const tp& b, const tp& c) \ + { return _mm_##fun(a, b, c); } + +///////////////////////////// XOP ///////////////////////////// + +// [todo] define CV_XOP +#if 1 // CV_XOP +inline __m128i _v128_comgt_epu32(const __m128i& a, const __m128i& b) +{ + const __m128i delta = _mm_set1_epi32((int)0x80000000); + return _mm_cmpgt_epi32(_mm_xor_si128(a, delta), _mm_xor_si128(b, delta)); +} +// wrapping XOP +#else +OPENCV_HAL_SSE_WRAP_2(_v128_comgt_epu32, __m128i) +#endif // !CV_XOP + +///////////////////////////// SSE4.1 ///////////////////////////// + +#if !CV_SSE4_1 + +/** Swizzle **/ +inline __m128i _v128_blendv_epi8(const __m128i& a, const __m128i& b, const __m128i& mask) +{ return _mm_xor_si128(a, _mm_and_si128(_mm_xor_si128(b, a), mask)); } + +/** Convert **/ +// 8 >> 16 +inline __m128i _v128_cvtepu8_epi16(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi8(a, z); +} +inline __m128i _v128_cvtepi8_epi16(const __m128i& a) +{ return _mm_srai_epi16(_mm_unpacklo_epi8(a, a), 8); } +// 8 >> 32 +inline __m128i _v128_cvtepu8_epi32(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi16(_mm_unpacklo_epi8(a, z), z); +} +inline __m128i _v128_cvtepi8_epi32(const __m128i& a) +{ + __m128i r = _mm_unpacklo_epi8(a, a); + r = _mm_unpacklo_epi8(r, r); + return _mm_srai_epi32(r, 24); +} +// 16 >> 32 +inline __m128i _v128_cvtepu16_epi32(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi16(a, z); +} +inline __m128i _v128_cvtepi16_epi32(const __m128i& a) +{ return _mm_srai_epi32(_mm_unpacklo_epi16(a, a), 16); } +// 32 >> 64 +inline __m128i _v128_cvtepu32_epi64(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi32(a, z); +} +inline __m128i _v128_cvtepi32_epi64(const __m128i& a) +{ return _mm_unpacklo_epi32(a, _mm_srai_epi32(a, 31)); } + +/** Arithmetic **/ +inline __m128i _v128_mullo_epi32(const __m128i& a, const __m128i& b) +{ + __m128i c0 = _mm_mul_epu32(a, b); + __m128i c1 = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)); + __m128i d0 = _mm_unpacklo_epi32(c0, c1); + __m128i d1 = _mm_unpackhi_epi32(c0, c1); + return _mm_unpacklo_epi64(d0, d1); +} + +/** Math **/ +inline __m128i _v128_min_epu32(const __m128i& a, const __m128i& b) +{ return _v128_blendv_epi8(a, b, _v128_comgt_epu32(a, b)); } + +// wrapping SSE4.1 +#else +OPENCV_HAL_SSE_WRAP_1(cvtepu8_epi16, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi8_epi16, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu8_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi8_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu16_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi16_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu32_epi64, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi32_epi64, __m128i) +OPENCV_HAL_SSE_WRAP_2(min_epu32, __m128i) +OPENCV_HAL_SSE_WRAP_2(mullo_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_3(blendv_epi8, __m128i) +#endif // !CV_SSE4_1 + +///////////////////////////// Revolutionary ///////////////////////////// + +/** Convert **/ +// 16 << 8 +inline __m128i _v128_cvtepu8_epi16_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi8(a, z); +} +inline __m128i _v128_cvtepi8_epi16_high(const __m128i& a) +{ return _mm_srai_epi16(_mm_unpackhi_epi8(a, a), 8); } +// 32 << 16 +inline __m128i _v128_cvtepu16_epi32_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi16(a, z); +} +inline __m128i _v128_cvtepi16_epi32_high(const __m128i& a) +{ return _mm_srai_epi32(_mm_unpackhi_epi16(a, a), 16); } +// 64 << 32 +inline __m128i _v128_cvtepu32_epi64_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi32(a, z); +} +inline __m128i _v128_cvtepi32_epi64_high(const __m128i& a) +{ return _mm_unpackhi_epi32(a, _mm_srai_epi32(a, 31)); } + +/** Miscellaneous **/ +inline __m128i _v128_packs_epu32(const __m128i& a, const __m128i& b) +{ + const __m128i m = _mm_set1_epi32(65535); + __m128i am = _v128_min_epu32(a, m); + __m128i bm = _v128_min_epu32(b, m); +#if CV_SSE4_1 + return _mm_packus_epi32(am, bm); +#else + const __m128i d = _mm_set1_epi32(32768), nd = _mm_set1_epi16(-32768); + am = _mm_sub_epi32(am, d); + bm = _mm_sub_epi32(bm, d); + am = _mm_packs_epi32(am, bm); + return _mm_sub_epi16(am, nd); +#endif +} + +template +inline int64 _v128_extract_epi64(const __m128i& a) +{ +#if defined(CV__SIMD_HAVE_mm_extract_epi64) || (CV_SSE4_1 && (defined(__x86_64__)/*GCC*/ || defined(_M_X64)/*MSVC*/)) +#define CV__SIMD_NATIVE_mm_extract_epi64 1 + return _mm_extract_epi64(a, i); +#else + CV_DECL_ALIGNED(16) int64 tmp[2]; + _mm_store_si128((__m128i*)tmp, a); + return tmp[i]; +#endif +} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_SSE_EM_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_vsx.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_vsx.hpp new file mode 100644 index 0000000..e0f6cbf --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_vsx.hpp @@ -0,0 +1,1578 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_VSX_HPP +#define OPENCV_HAL_VSX_HPP + +#include +#include "opencv2/core/utility.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + vec_uchar16 val; + + explicit v_uint8x16(const vec_uchar16& v) : val(v) + {} + v_uint8x16() : val(vec_uchar16_z) + {} + v_uint8x16(vec_bchar16 v) : val(vec_uchar16_c(v)) + {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + : val(vec_uchar16_set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + {} + uchar get0() const + { return vec_extract(val, 0); } +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + vec_char16 val; + + explicit v_int8x16(const vec_char16& v) : val(v) + {} + v_int8x16() : val(vec_char16_z) + {} + v_int8x16(vec_bchar16 v) : val(vec_char16_c(v)) + {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + : val(vec_char16_set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + {} + schar get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + vec_ushort8 val; + + explicit v_uint16x8(const vec_ushort8& v) : val(v) + {} + v_uint16x8() : val(vec_ushort8_z) + {} + v_uint16x8(vec_bshort8 v) : val(vec_ushort8_c(v)) + {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + : val(vec_ushort8_set(v0, v1, v2, v3, v4, v5, v6, v7)) + {} + ushort get0() const + { return vec_extract(val, 0); } +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + vec_short8 val; + + explicit v_int16x8(const vec_short8& v) : val(v) + {} + v_int16x8() : val(vec_short8_z) + {} + v_int16x8(vec_bshort8 v) : val(vec_short8_c(v)) + {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + : val(vec_short8_set(v0, v1, v2, v3, v4, v5, v6, v7)) + {} + short get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + enum { nlanes = 4 }; + vec_uint4 val; + + explicit v_uint32x4(const vec_uint4& v) : val(v) + {} + v_uint32x4() : val(vec_uint4_z) + {} + v_uint32x4(vec_bint4 v) : val(vec_uint4_c(v)) + {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) : val(vec_uint4_set(v0, v1, v2, v3)) + {} + uint get0() const + { return vec_extract(val, 0); } +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + vec_int4 val; + + explicit v_int32x4(const vec_int4& v) : val(v) + {} + v_int32x4() : val(vec_int4_z) + {} + v_int32x4(vec_bint4 v) : val(vec_int4_c(v)) + {} + v_int32x4(int v0, int v1, int v2, int v3) : val(vec_int4_set(v0, v1, v2, v3)) + {} + int get0() const + { return vec_extract(val, 0); } +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + vec_float4 val; + + explicit v_float32x4(const vec_float4& v) : val(v) + {} + v_float32x4() : val(vec_float4_z) + {} + v_float32x4(vec_bint4 v) : val(vec_float4_c(v)) + {} + v_float32x4(float v0, float v1, float v2, float v3) : val(vec_float4_set(v0, v1, v2, v3)) + {} + float get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + vec_udword2 val; + + explicit v_uint64x2(const vec_udword2& v) : val(v) + {} + v_uint64x2() : val(vec_udword2_z) + {} + v_uint64x2(vec_bdword2 v) : val(vec_udword2_c(v)) + {} + v_uint64x2(uint64 v0, uint64 v1) : val(vec_udword2_set(v0, v1)) + {} + uint64 get0() const + { return vec_extract(val, 0); } +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + vec_dword2 val; + + explicit v_int64x2(const vec_dword2& v) : val(v) + {} + v_int64x2() : val(vec_dword2_z) + {} + v_int64x2(vec_bdword2 v) : val(vec_dword2_c(v)) + {} + v_int64x2(int64 v0, int64 v1) : val(vec_dword2_set(v0, v1)) + {} + int64 get0() const + { return vec_extract(val, 0); } +}; + +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + vec_double2 val; + + explicit v_float64x2(const vec_double2& v) : val(v) + {} + v_float64x2() : val(vec_double2_z) + {} + v_float64x2(vec_bdword2 v) : val(vec_double2_c(v)) + {} + v_float64x2(double v0, double v1) : val(vec_double2_set(v0, v1)) + {} + double get0() const + { return vec_extract(val, 0); } +}; + +#define OPENCV_HAL_IMPL_VSX_EXTRACT_N(_Tpvec, _Tp) \ +template inline _Tp v_extract_n(VSX_UNUSED(_Tpvec v)) { return vec_extract(v.val, i); } + +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint8x16, uchar) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int8x16, schar) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint16x8, ushort) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int16x8, short) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint32x4, uint) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int32x4, int) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint64x2, uint64) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int64x2, int64) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_float32x4, float) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_float64x2, double) + +//////////////// Load and store operations /////////////// + +/* + * clang-5 aborted during parse "vec_xxx_c" only if it's + * inside a function template which is defined by preprocessor macro. + * + * if vec_xxx_c defined as C++ cast, clang-5 will pass it +*/ +#define OPENCV_HAL_IMPL_VSX_INITVEC(_Tpvec, _Tp, suffix, cast) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(vec_splats((_Tp)v));} \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0 &a) \ +{ return _Tpvec((cast)a.val); } + +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint8x16, uchar, u8, vec_uchar16) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int8x16, schar, s8, vec_char16) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint16x8, ushort, u16, vec_ushort8) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int16x8, short, s16, vec_short8) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint32x4, uint, u32, vec_uint4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int32x4, int, s32, vec_int4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint64x2, uint64, u64, vec_udword2) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int64x2, int64, s64, vec_dword2) +OPENCV_HAL_IMPL_VSX_INITVEC(v_float32x4, float, f32, vec_float4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_float64x2, double, f64, vec_double2) + +#define OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, ld, ld_a, st, st_a) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(ld(0, ptr)); } \ +inline _Tpvec v_load_aligned(VSX_UNUSED(const _Tp* ptr)) \ +{ return _Tpvec(ld_a(0, ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(vec_ld_l8(ptr)); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(vec_mergesqh(vec_ld_l8(ptr0), vec_ld_l8(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ st(a.val, 0, ptr); } \ +inline void v_store_aligned(VSX_UNUSED(_Tp* ptr), const _Tpvec& a) \ +{ st_a(a.val, 0, ptr); } \ +inline void v_store_aligned_nocache(VSX_UNUSED(_Tp* ptr), const _Tpvec& a) \ +{ st_a(a.val, 0, ptr); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ if(mode == hal::STORE_UNALIGNED) st(a.val, 0, ptr); else st_a(a.val, 0, ptr); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ vec_st_l8(a.val, ptr); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ vec_st_h8(a.val, ptr); } + +// working around gcc bug for aligned ld/st +// if runtime check for vec_ld/st fail we failback to unaligned ld/st +// https://github.com/opencv/opencv/issues/13211 +#ifdef CV_COMPILER_VSX_BROKEN_ALIGNED + #define OPENCV_HAL_IMPL_VSX_LOADSTORE(_Tpvec, _Tp) \ + OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, vsx_ld, vsx_ld, vsx_st, vsx_st) +#else + #define OPENCV_HAL_IMPL_VSX_LOADSTORE(_Tpvec, _Tp) \ + OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, vsx_ld, vec_ld, vsx_st, vec_st) +#endif + +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint8x16, uchar) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int8x16, schar) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint16x8, ushort) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int16x8, short) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint32x4, uint) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int32x4, int) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_float32x4, float) + +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_float64x2, double, vsx_ld, vsx_ld, vsx_st, vsx_st) +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_uint64x2, uint64, vsx_ld2, vsx_ld2, vsx_st2, vsx_st2) +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_int64x2, int64, vsx_ld2, vsx_ld2, vsx_st2, vsx_st2) + +//////////////// Value reordering /////////////// + +/* de&interleave */ +#define OPENCV_HAL_IMPL_VSX_INTERLEAVE(_Tp, _Tpvec) \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, _Tpvec& b) \ +{ vec_ld_deinterleave(ptr, a.val, b.val);} \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, \ + _Tpvec& b, _Tpvec& c) \ +{ vec_ld_deinterleave(ptr, a.val, b.val, c.val); } \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ +{ vec_ld_deinterleave(ptr, a.val, b.val, c.val, d.val); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, const _Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, ptr); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, \ + const _Tpvec& b, const _Tpvec& c, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, c.val, ptr); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, const _Tpvec& b, \ + const _Tpvec& c, const _Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, c.val, d.val, ptr); } + +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uchar, v_uint8x16) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(schar, v_int8x16) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(ushort, v_uint16x8) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(short, v_int16x8) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uint, v_uint32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(int, v_int32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(float, v_float32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(double, v_float64x2) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(int64, v_int64x2) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uint64, v_uint64x2) + +/* Expand */ +#define OPENCV_HAL_IMPL_VSX_EXPAND(_Tpvec, _Tpwvec, _Tp, fl, fh) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = fh(a.val); \ + b1.val = fl(a.val); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ return _Tpwvec(fh(a.val)); } \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ return _Tpwvec(fl(a.val)); } \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ return _Tpwvec(fh(vec_ld_l8(ptr))); } + +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint8x16, v_uint16x8, uchar, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int8x16, v_int16x8, schar, vec_unpackl, vec_unpackh) +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint16x8, v_uint32x4, ushort, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int16x8, v_int32x4, short, vec_unpackl, vec_unpackh) +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint32x4, v_uint64x2, uint, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int32x4, v_int64x2, int, vec_unpackl, vec_unpackh) + +/* Load and zero expand a 4 byte value into the second dword, first is don't care. */ +#if !defined(CV_COMPILER_VSX_BROKEN_ASM) + #define _LXSIWZX(out, ptr, T) __asm__ ("lxsiwzx %x0, 0, %1\r\n" : "=wa"(out) : "r" (ptr) : "memory"); +#else + /* This is compiler-agnostic, but will introduce an unneeded splat on the critical path. */ + #define _LXSIWZX(out, ptr, T) out = (T)vec_udword2_sp(*(uint32_t*)(ptr)); +#endif + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + // Zero-extend the extra 24B instead of unpacking. Usually faster in small kernel + // Likewise note, value is zero extended and upper 4 bytes are zero'ed. + vec_uchar16 pmu = {8, 12, 12, 12, 9, 12, 12, 12, 10, 12, 12, 12, 11, 12, 12, 12}; + vec_uchar16 out; + + _LXSIWZX(out, ptr, vec_uchar16); + out = vec_perm(out, out, pmu); + return v_uint32x4((vec_uint4)out); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + vec_char16 out; + vec_short8 outs; + vec_int4 outw; + + _LXSIWZX(out, ptr, vec_char16); + outs = vec_unpackl(out); + outw = vec_unpackh(outs); + return v_int32x4(outw); +} + +/* pack */ +#define OPENCV_HAL_IMPL_VSX_PACK(_Tpvec, _Tp, _Tpwvec, _Tpvn, _Tpdel, sfnc, pkfnc, addfnc, pack) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(pkfnc(a.val, b.val)); \ +} \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + vec_st_l8(pkfnc(a.val, a.val), ptr); \ +} \ +template \ +inline _Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + const __vector _Tpvn vn = vec_splats((_Tpvn)n); \ + const __vector _Tpdel delta = vec_splats((_Tpdel)((_Tpdel)1 << (n-1))); \ + return _Tpvec(pkfnc(sfnc(addfnc(a.val, delta), vn), sfnc(addfnc(b.val, delta), vn))); \ +} \ +template \ +inline void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + const __vector _Tpvn vn = vec_splats((_Tpvn)n); \ + const __vector _Tpdel delta = vec_splats((_Tpdel)((_Tpdel)1 << (n-1))); \ + vec_st_l8(pkfnc(sfnc(addfnc(a.val, delta), vn), delta), ptr); \ +} + +OPENCV_HAL_IMPL_VSX_PACK(v_uint8x16, uchar, v_uint16x8, unsigned short, unsigned short, + vec_sr, vec_packs, vec_adds, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int8x16, schar, v_int16x8, unsigned short, short, + vec_sra, vec_packs, vec_adds, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint16x8, ushort, v_uint32x4, unsigned int, unsigned int, + vec_sr, vec_packs, vec_add, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int16x8, short, v_int32x4, unsigned int, int, + vec_sra, vec_packs, vec_add, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint32x4, uint, v_uint64x2, unsigned long long, unsigned long long, + vec_sr, vec_pack, vec_add, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int32x4, int, v_int64x2, unsigned long long, long long, + vec_sra, vec_pack, vec_add, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint8x16, uchar, v_int16x8, unsigned short, short, + vec_sra, vec_packsu, vec_adds, pack_u) +OPENCV_HAL_IMPL_VSX_PACK(v_uint16x8, ushort, v_int32x4, unsigned int, int, + vec_sra, vec_packsu, vec_add, pack_u) +// Following variant is not implemented on other platforms: +//OPENCV_HAL_IMPL_VSX_PACK(v_uint32x4, uint, v_int64x2, unsigned long long, long long, +// vec_sra, vec_packsu, vec_add, pack_u) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_uchar16 ab = vec_pack(a.val, b.val); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + vec_ushort8 ab = vec_pack(a.val, b.val); + vec_ushort8 cd = vec_pack(c.val, d.val); + return v_uint8x16(vec_pack(ab, cd)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + vec_uint4 ab = vec_pack(a.val, b.val); + vec_uint4 cd = vec_pack(c.val, d.val); + vec_uint4 ef = vec_pack(e.val, f.val); + vec_uint4 gh = vec_pack(g.val, h.val); + + vec_ushort8 abcd = vec_pack(ab, cd); + vec_ushort8 efgh = vec_pack(ef, gh); + return v_uint8x16(vec_pack(abcd, efgh)); +} + +/* Recombine */ +template +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) +{ + b0.val = vec_mergeh(a0.val, a1.val); + b1.val = vec_mergel(a0.val, a1.val); +} + +template +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(vec_mergesql(a.val, b.val)); } + +template +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(vec_mergesqh(a.val, b.val)); } + +template +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) +{ + c.val = vec_mergesqh(a.val, b.val); + d.val = vec_mergesql(a.val, b.val); +} + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ +/** Arithmetics **/ +#define OPENCV_HAL_IMPL_VSX_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(intrin(a.val, b.val)); } \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint8x16, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint8x16, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int8x16, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int8x16, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint16x8, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint16x8, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int16x8, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int16x8, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_uint32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_int32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_float32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_float32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_float32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(/, v_float32x4, vec_div) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_float64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_float64x2, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_float64x2, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(/, v_float64x2, vec_div) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint64x2, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int64x2, vec_sub) + +// saturating multiply +#define OPENCV_HAL_IMPL_VSX_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_uint16x8, v_uint32x4) + +template +inline void v_mul_expand(const Tvec& a, const Tvec& b, Twvec& c, Twvec& d) +{ + Twvec p0 = Twvec(vec_mule(a.val, b.val)); + Twvec p1 = Twvec(vec_mulo(a.val, b.val)); + v_zip(p0, p1, c, d); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + vec_int4 p0 = vec_mule(a.val, b.val); + vec_int4 p1 = vec_mulo(a.val, b.val); + static const vec_uchar16 perm = {2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}; + return v_int16x8(vec_perm(vec_short8_c(p0), vec_short8_c(p1), perm)); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_uint4 p0 = vec_mule(a.val, b.val); + vec_uint4 p1 = vec_mulo(a.val, b.val); + static const vec_uchar16 perm = {2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}; + return v_uint16x8(vec_perm(vec_ushort8_c(p0), vec_ushort8_c(p1), perm)); +} + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_VSX_BIN_FUNC(func, intrin) \ +template \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_add_wrap, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_sub_wrap, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_mul_wrap, vec_mul) + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_VSX_SHIFT_OP(_Tpvec, shr, splfunc) \ +inline _Tpvec operator << (const _Tpvec& a, int imm) \ +{ return _Tpvec(vec_sl(a.val, splfunc(imm))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int imm) \ +{ return _Tpvec(shr(a.val, splfunc(imm))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(vec_sl(a.val, splfunc(imm))); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(shr(a.val, splfunc(imm))); } + +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint8x16, vec_sr, vec_uchar16_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint16x8, vec_sr, vec_ushort8_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint32x4, vec_sr, vec_uint4_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint64x2, vec_sr, vec_udword2_sp) +// algebraic right shift +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int8x16, vec_sra, vec_uchar16_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int16x8, vec_sra, vec_ushort8_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int32x4, vec_sra, vec_uint4_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int64x2, vec_sra, vec_udword2_sp) + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_VSX_LOGIC_OP(_Tpvec) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(&, _Tpvec, vec_and) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(|, _Tpvec, vec_or) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(^, _Tpvec, vec_xor) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ return _Tpvec(vec_not(a.val)); } + +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint8x16) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int8x16) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint16x8) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int16x8) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint64x2) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int64x2) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_float32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_float64x2) + +/** Bitwise select **/ +#define OPENCV_HAL_IMPL_VSX_SELECT(_Tpvec, cast) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_sel(b.val, a.val, cast(mask.val))); } + +OPENCV_HAL_IMPL_VSX_SELECT(v_uint8x16, vec_bchar16_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int8x16, vec_bchar16_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_uint16x8, vec_bshort8_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int16x8, vec_bshort8_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_uint32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_float32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_float64x2, vec_bdword2_c) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_VSX_INT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpeq(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpne(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmplt(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpgt(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmple(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpge(a.val, b.val)); } + +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint8x16) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int8x16) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint16x8) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int16x8) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_float32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_float64x2) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint64x2) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int64x2) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(vec_cmpeq(a.val, a.val)); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(vec_cmpeq(a.val, a.val)); } + +/** min/max **/ +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_min, vec_min) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_max, vec_max) + +/** Rotate **/ +#define OPENCV_IMPL_VSX_ROTATE(_Tpvec, suffix, shf, cast) \ +template \ +inline _Tpvec v_rotate_##suffix(const _Tpvec& a) \ +{ \ + const int wd = imm * sizeof(typename _Tpvec::lane_type); \ + if (wd > 15) \ + return _Tpvec(); \ + return _Tpvec((cast)shf(vec_uchar16_c(a.val), vec_uchar16_sp(wd << 3))); \ +} + +#define OPENCV_IMPL_VSX_ROTATE_LR(_Tpvec, cast) \ +OPENCV_IMPL_VSX_ROTATE(_Tpvec, left, vec_slo, cast) \ +OPENCV_IMPL_VSX_ROTATE(_Tpvec, right, vec_sro, cast) + +OPENCV_IMPL_VSX_ROTATE_LR(v_uint8x16, vec_uchar16) +OPENCV_IMPL_VSX_ROTATE_LR(v_int8x16, vec_char16) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint16x8, vec_ushort8) +OPENCV_IMPL_VSX_ROTATE_LR(v_int16x8, vec_short8) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint32x4, vec_uint4) +OPENCV_IMPL_VSX_ROTATE_LR(v_int32x4, vec_int4) +OPENCV_IMPL_VSX_ROTATE_LR(v_float32x4, vec_float4) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint64x2, vec_udword2) +OPENCV_IMPL_VSX_ROTATE_LR(v_int64x2, vec_dword2) +OPENCV_IMPL_VSX_ROTATE_LR(v_float64x2, vec_double2) + +template +inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) +{ + enum { CV_SHIFT = 16 - imm * (sizeof(typename _Tpvec::lane_type)) }; + if (CV_SHIFT == 16) + return a; +#ifdef __IBMCPP__ + return _Tpvec(vec_sld(b.val, a.val, CV_SHIFT & 15)); +#else + return _Tpvec(vec_sld(b.val, a.val, CV_SHIFT)); +#endif +} + +template +inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) +{ + enum { CV_SHIFT = imm * (sizeof(typename _Tpvec::lane_type)) }; + if (CV_SHIFT == 16) + return b; + return _Tpvec(vec_sld(a.val, b.val, CV_SHIFT)); +} + +#define OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, suffix, rg1, rg2) \ +template \ +inline _Tpvec v_rotate_##suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + if (imm == 1) \ + return _Tpvec(vec_permi(rg1.val, rg2.val, 2)); \ + return imm ? b : a; \ +} + +#define OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(_Tpvec) \ +OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, left, b, a) \ +OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, right, a, b) + +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_float64x2) +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_uint64x2) +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_int64x2) + +/* Reverse */ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + static const vec_uchar16 perm = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_uint8x16(vec_perm(vec, vec, perm)); +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + static const vec_uchar16 perm = {14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u16(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + static const vec_uchar16 perm = {12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u32(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + static const vec_uchar16 perm = {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u64(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +/* Extract */ +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ return v_rotate_right(a, b); } + +////////// Reduce and mask ///////// + +/** Reduce **/ +inline uint v_reduce_sum(const v_uint8x16& a) +{ + const vec_uint4 zero4 = vec_uint4_z; + vec_uint4 sum4 = vec_sum4s(a.val, zero4); + return (uint)vec_extract(vec_sums(vec_int4_c(sum4), vec_int4_c(zero4)), 3); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + const vec_int4 zero4 = vec_int4_z; + vec_int4 sum4 = vec_sum4s(a.val, zero4); + return (int)vec_extract(vec_sums(sum4, zero4), 3); +} +inline int v_reduce_sum(const v_int16x8& a) +{ + const vec_int4 zero = vec_int4_z; + return saturate_cast(vec_extract(vec_sums(vec_sum4s(a.val, zero), zero), 3)); +} +inline uint v_reduce_sum(const v_uint16x8& a) +{ + const vec_int4 v4 = vec_int4_c(vec_unpackhu(vec_adds(a.val, vec_sld(a.val, a.val, 8)))); + return saturate_cast(vec_extract(vec_sums(v4, vec_int4_z), 3)); +} + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + const _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 4)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, min, vec_min) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} +inline int64 v_reduce_sum(const v_int64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} +inline double v_reduce_sum(const v_float64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + rs = func(rs, vec_sld(rs, rs, 4)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 2)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_uint16x8, vec_ushort8, ushort, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_uint16x8, vec_ushort8, ushort, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_int16x8, vec_short8, short, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_int16x8, vec_short8, short, min, vec_min) + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + rs = func(rs, vec_sld(rs, rs, 4)); \ + rs = func(rs, vec_sld(rs, rs, 2)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 1)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_uint8x16, vec_uchar16, uchar, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_uint8x16, vec_uchar16, uchar, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_int8x16, vec_char16, schar, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_int8x16, vec_char16, schar, min, vec_min) + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + vec_float4 ac = vec_add(vec_mergel(a.val, c.val), vec_mergeh(a.val, c.val)); + ac = vec_add(ac, vec_sld(ac, ac, 8)); + + vec_float4 bd = vec_add(vec_mergel(b.val, d.val), vec_mergeh(b.val, d.val)); + bd = vec_add(bd, vec_sld(bd, bd, 8)); + return v_float32x4(vec_mergeh(ac, bd)); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + const vec_uint4 zero4 = vec_uint4_z; + vec_uint4 sum4 = vec_sum4s(vec_absd(a.val, b.val), zero4); + return (unsigned)vec_extract(vec_sums(vec_int4_c(sum4), vec_int4_c(zero4)), 3); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + const vec_int4 zero4 = vec_int4_z; + vec_char16 ad = vec_abss(vec_subs(a.val, b.val)); + vec_int4 sum4 = vec_sum4s(ad, zero4); + return (unsigned)vec_extract(vec_sums(sum4, zero4), 3); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_ushort8 ad = vec_absd(a.val, b.val); + VSX_UNUSED(vec_int4) sum = vec_sums(vec_int4_c(vec_unpackhu(ad)) + vec_int4_c(vec_unpacklu(ad)), vec_int4_z); + return (unsigned)vec_extract(sum, 3); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + const vec_int4 zero4 = vec_int4_z; + vec_short8 ad = vec_abss(vec_subs(a.val, b.val)); + vec_int4 sum4 = vec_sum4s(ad, zero4); + return (unsigned)vec_extract(vec_sums(sum4, zero4), 3); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + const vec_uint4 ad = vec_absd(a.val, b.val); + const vec_uint4 rd = vec_add(ad, vec_sld(ad, ad, 8)); + return vec_extract(vec_add(rd, vec_sld(rd, rd, 4)), 0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + vec_int4 ad = vec_abss(vec_sub(a.val, b.val)); + return (unsigned)vec_extract(vec_sums(ad, vec_int4_z), 3); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + const vec_float4 ad = vec_abs(vec_sub(a.val, b.val)); + const vec_float4 rd = vec_add(ad, vec_sld(ad, ad, 8)); + return vec_extract(vec_add(rd, vec_sld(rd, rd, 4)), 0); +} + +/** Popcount **/ +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ return v_uint8x16(vec_popcntu(a.val)); } +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_uint8x16(vec_popcntu(a.val)); } +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ return v_uint16x8(vec_popcntu(a.val)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_uint16x8(vec_popcntu(a.val)); } +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ return v_uint32x4(vec_popcntu(a.val)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_uint32x4(vec_popcntu(a.val)); } +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ return v_uint64x2(vec_popcntu(a.val)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_uint64x2(vec_popcntu(a.val)); } + +/** Mask **/ +inline int v_signmask(const v_uint8x16& a) +{ + static const vec_uchar16 qperm = {120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_int16x8& a) +{ + static const vec_uchar16 qperm = {112, 96, 80, 64, 48, 32, 16, 0, 128, 128, 128, 128, 128, 128, 128, 128}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_uint16x8& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } + +inline int v_signmask(const v_int32x4& a) +{ + static const vec_uchar16 qperm = {96, 64, 32, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_uint32x4& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } + +inline int v_signmask(const v_int64x2& a) +{ + VSX_UNUSED(const vec_dword2) sv = vec_sr(a.val, vec_udword2_sp(63)); + return (int)vec_extract(sv, 0) | (int)vec_extract(sv, 1) << 1; +} +inline int v_signmask(const v_uint64x2& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } + +template +inline bool v_check_all(const _Tpvec& a) +{ return vec_all_lt(a.val, _Tpvec().val); } +inline bool v_check_all(const v_uint8x16& a) +{ return v_check_all(v_reinterpret_as_s8(a)); } +inline bool v_check_all(const v_uint16x8& a) +{ return v_check_all(v_reinterpret_as_s16(a)); } +inline bool v_check_all(const v_uint32x4& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_uint64x2& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } + +template +inline bool v_check_any(const _Tpvec& a) +{ return vec_any_lt(a.val, _Tpvec().val); } +inline bool v_check_any(const v_uint8x16& a) +{ return v_check_any(v_reinterpret_as_s8(a)); } +inline bool v_check_any(const v_uint16x8& a) +{ return v_check_any(v_reinterpret_as_s16(a)); } +inline bool v_check_any(const v_uint32x4& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_uint64x2& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } + +////////// Other math ///////// + +/** Some frequent operations **/ +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ return v_float32x4(vec_sqrt(x.val)); } +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ return v_float64x2(vec_sqrt(x.val)); } + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ return v_float32x4(vec_rsqrt(x.val)); } +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ return v_float64x2(vec_rsqrt(x.val)); } + +#define OPENCV_HAL_IMPL_VSX_MULADD(_Tpvec) \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_sqrt(vec_madd(a.val, a.val, vec_mul(b.val, b.val)))); } \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_madd(a.val, a.val, vec_mul(b.val, b.val))); } \ +inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ return _Tpvec(vec_madd(a.val, b.val, c.val)); } \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ return _Tpvec(vec_madd(a.val, b.val, c.val)); } + +OPENCV_HAL_IMPL_VSX_MULADD(v_float32x4) +OPENCV_HAL_IMPL_VSX_MULADD(v_float64x2) + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ return a * b + c; } + +// TODO: exp, log, sin, cos + +/** Absolute values **/ +inline v_uint8x16 v_abs(const v_int8x16& x) +{ return v_uint8x16(vec_uchar16_c(vec_abs(x.val))); } + +inline v_uint16x8 v_abs(const v_int16x8& x) +{ return v_uint16x8(vec_ushort8_c(vec_abs(x.val))); } + +inline v_uint32x4 v_abs(const v_int32x4& x) +{ return v_uint32x4(vec_uint4_c(vec_abs(x.val))); } + +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(vec_abs(x.val)); } + +inline v_float64x2 v_abs(const v_float64x2& x) +{ return v_float64x2(vec_abs(x.val)); } + +/** Absolute difference **/ +// unsigned +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_absdiff, vec_absd) + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ return v_reinterpret_as_u8(v_sub_wrap(v_max(a, b), v_min(a, b))); } +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ return v_reinterpret_as_u32(v_max(a, b) - v_min(a, b)); } + +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ return v_abs(a - b); } +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ return v_abs(a - b); } + +/** Absolute difference for signed integers **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ return v_int8x16(vec_abss(vec_subs(a.val, b.val))); } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_int16x8(vec_abss(vec_subs(a.val, b.val))); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x4 v_round(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_rint(a.val))); } + +inline v_int32x4 v_round(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_rint(a.val)), vec_int4_z)); } + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_rint(a.val)), vec_ctso(vec_rint(b.val)))); } + +inline v_int32x4 v_floor(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_floor(a.val))); } + +inline v_int32x4 v_floor(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_floor(a.val)), vec_int4_z)); } + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_ceil(a.val))); } + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_ceil(a.val)), vec_int4_z)); } + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(vec_cts(a.val)); } + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(a.val), vec_int4_z)); } + +/** To float **/ +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ return v_float32x4(vec_ctf(a.val)); } + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ return v_float32x4(vec_mergesqo(vec_cvfo(a.val), vec_float4_z)); } + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ return v_float32x4(vec_mergesqo(vec_cvfo(a.val), vec_cvfo(b.val))); } + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ return v_float64x2(vec_ctdo(vec_mergeh(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ return v_float64x2(vec_ctdo(vec_mergel(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ return v_float64x2(vec_cvfo(vec_mergeh(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ return v_float64x2(vec_cvfo(vec_mergel(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ return v_float64x2(vec_ctd(a.val)); } + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + return v_reinterpret_as_s8(v_int16x8(*(const short*)(tab+idx[0]), *(const short*)(tab+idx[1]), *(const short*)(tab+idx[2]), *(const short*)(tab+idx[3]), + *(const short*)(tab+idx[4]), *(const short*)(tab+idx[5]), *(const short*)(tab+idx[6]), *(const short*)(tab+idx[7]))); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + return v_reinterpret_as_s8(v_int32x4(*(const int*)(tab+idx[0]), *(const int*)(tab+idx[1]), *(const int*)(tab+idx[2]), *(const int*)(tab+idx[3]))); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + return v_reinterpret_as_s16(v_int32x4(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_reinterpret_as_s16(v_int64x2(*(const int64*)(tab + idx[0]), *(const int64*)(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_reinterpret_as_s32(v_int64x2(*(const int64*)(tab + idx[0]), *(const int64*)(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(vsx_ld(0, tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(vsx_ld2(0, tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int*)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_load(tab + *idx); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_load(tab + *idx); } + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_uint32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + const int idx[2] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1) + }; + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + vec_float4 xy0 = vec_ld_l8(tab + vec_extract(idxvec.val, 0)); + vec_float4 xy1 = vec_ld_l8(tab + vec_extract(idxvec.val, 1)); + vec_float4 xy2 = vec_ld_l8(tab + vec_extract(idxvec.val, 2)); + vec_float4 xy3 = vec_ld_l8(tab + vec_extract(idxvec.val, 3)); + vec_float4 xy02 = vec_mergeh(xy0, xy2); // x0, x2, y0, y2 + vec_float4 xy13 = vec_mergeh(xy1, xy3); // x1, x3, y1, y3 + x.val = vec_mergeh(xy02, xy13); + y.val = vec_mergel(xy02, xy13); +} +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + vec_double2 xy0 = vsx_ld(vec_extract(idxvec.val, 0), tab); + vec_double2 xy1 = vsx_ld(vec_extract(idxvec.val, 1), tab); + x.val = vec_mergeh(xy0, xy1); + y.val = vec_mergel(xy0, xy1); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11, 12, 14, 13, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } + +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 4, 1, 5, 2, 6, 3, 7, 8, 12, 9, 13, 10, 14, 11, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 4,5, 2,3, 6,7, 8,9, 12,13, 10,11, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } + +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 8,9, 2,3, 10,11, 4,5, 12,13, 6,7, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + static const vec_uchar16 perm = {0,1,2,3, 8,9,10,11, 4,5,6,7, 12,13,14,15}; + return v_int32x4(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) +{ return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) +{ return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 15, 15, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 2,3, 4,5, 8,9, 10,11, 12,13, 14,15, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) +{ return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) +{ return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) +{ return vec; } + +/////// FP16 support //////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + vec_ushort8 vf16 = vec_ld_l8((const ushort*)ptr); +#if CV_VSX3 && defined(vec_extract_fp_from_shorth) + return v_float32x4(vec_extract_fp_from_shorth(vf16)); +#elif CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM) + vec_float4 vf32; + __asm__ __volatile__ ("xvcvhpsp %x0,%x1" : "=wa" (vf32) : "wa" (vec_mergeh(vf16, vf16))); + return v_float32x4(vf32); +#else + const vec_int4 z = vec_int4_z, delta = vec_int4_sp(0x38000000); + const vec_int4 signmask = vec_int4_sp(0x80000000); + const vec_int4 maxexp = vec_int4_sp(0x7c000000); + const vec_float4 deltaf = vec_float4_c(vec_int4_sp(0x38800000)); + + vec_int4 bits = vec_int4_c(vec_mergeh(vec_short8_c(z), vec_short8_c(vf16))); + vec_int4 e = vec_and(bits, maxexp), sign = vec_and(bits, signmask); + vec_int4 t = vec_add(vec_sr(vec_xor(bits, sign), vec_uint4_sp(3)), delta); // ((h & 0x7fff) << 13) + delta + vec_int4 zt = vec_int4_c(vec_sub(vec_float4_c(vec_add(t, vec_int4_sp(1 << 23))), deltaf)); + + t = vec_add(t, vec_and(delta, vec_cmpeq(maxexp, e))); + vec_bint4 zmask = vec_cmpeq(e, z); + vec_int4 ft = vec_sel(t, zt, zmask); + return v_float32x4(vec_float4_c(vec_or(ft, sign))); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ +// fixme: Is there any builtin op or intrinsic that cover "xvcvsphp"? +#if CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM) + vec_ushort8 vf16; + __asm__ __volatile__ ("xvcvsphp %x0,%x1" : "=wa" (vf16) : "wa" (v.val)); + vec_st_l8(vec_mergesqe(vf16, vf16), ptr); +#else + const vec_int4 signmask = vec_int4_sp(0x80000000); + const vec_int4 rval = vec_int4_sp(0x3f000000); + + vec_int4 t = vec_int4_c(v.val); + vec_int4 sign = vec_sra(vec_and(t, signmask), vec_uint4_sp(16)); + t = vec_and(vec_nor(signmask, signmask), t); + + vec_bint4 finitemask = vec_cmpgt(vec_int4_sp(0x47800000), t); + vec_bint4 isnan = vec_cmpgt(t, vec_int4_sp(0x7f800000)); + vec_int4 naninf = vec_sel(vec_int4_sp(0x7c00), vec_int4_sp(0x7e00), isnan); + vec_bint4 tinymask = vec_cmpgt(vec_int4_sp(0x38800000), t); + vec_int4 tt = vec_int4_c(vec_add(vec_float4_c(t), vec_float4_c(rval))); + tt = vec_sub(tt, rval); + vec_int4 odd = vec_and(vec_sr(t, vec_uint4_sp(13)), vec_int4_sp(1)); + vec_int4 nt = vec_add(t, vec_int4_sp(0xc8000fff)); + nt = vec_sr(vec_add(nt, odd), vec_uint4_sp(13)); + t = vec_sel(nt, tt, tinymask); + t = vec_sel(naninf, t, finitemask); + t = vec_or(t, sign); + vec_st_l8(vec_packs(t, t), ptr); +#endif +} + +inline void v_cleanup() {} + + +/** Reinterpret **/ +/** its up there with load and store operations **/ + +////////// Matrix operations ///////// + +//////// Dot Product //////// +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(vec_msum(a.val, b.val, vec_int4_z)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(vec_msum(a.val, b.val, c.val)); } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ + vec_dword2 even = vec_mule(a.val, b.val); + vec_dword2 odd = vec_mulo(a.val, b.val); + return v_int64x2(vec_add(even, odd)); +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_uint32x4(vec_msum(a.val, b.val, c.val)); } +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ return v_uint32x4(vec_msum(a.val, b.val, vec_uint4_z)); } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + const vec_ushort8 eight = vec_ushort8_sp(8); + vec_short8 a0 = vec_sra((vec_short8)vec_sld(a.val, a.val, 1), eight); // even + vec_short8 a1 = vec_sra((vec_short8)a.val, eight); // odd + vec_short8 b0 = vec_sra((vec_short8)vec_sld(b.val, b.val, 1), eight); + vec_short8 b1 = vec_sra((vec_short8)b.val, eight); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, vec_int4_z))); +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ + const vec_ushort8 eight = vec_ushort8_sp(8); + vec_short8 a0 = vec_sra((vec_short8)vec_sld(a.val, a.val, 1), eight); // even + vec_short8 a1 = vec_sra((vec_short8)a.val, eight); // odd + vec_short8 b0 = vec_sra((vec_short8)vec_sld(b.val, b.val, 1), eight); + vec_short8 b1 = vec_sra((vec_short8)b.val, eight); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, c.val))); +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + const vec_uint4 zero = vec_uint4_z; + vec_uint4 even = vec_mule(a.val, b.val); + vec_uint4 odd = vec_mulo(a.val, b.val); + vec_udword2 e0 = (vec_udword2)vec_mergee(even, zero); + vec_udword2 e1 = (vec_udword2)vec_mergeo(even, zero); + vec_udword2 o0 = (vec_udword2)vec_mergee(odd, zero); + vec_udword2 o1 = (vec_udword2)vec_mergeo(odd, zero); + vec_udword2 s0 = vec_add(e0, o0); + vec_udword2 s1 = vec_add(e1, o1); + return v_uint64x2(vec_add(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return v_int64x2(vec_add(vec_mergeh(c.val, d.val), vec_mergel(c.val, d.val))); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(vec_msum(a.val, b.val, vec_int4_z)) + c; } +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_uint32x4(vec_msum(a.val, b.val, vec_uint4_z)) + c; } + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ + vec_short8 a0 = vec_unpackh(a.val); + vec_short8 a1 = vec_unpackl(a.val); + vec_short8 b0 = vec_unpackh(b.val); + vec_short8 b1 = vec_unpackl(b.val); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, vec_int4_z))); +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return c + d; +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + const vec_float4 v0 = vec_splat(v.val, 0); + const vec_float4 v1 = vec_splat(v.val, 1); + const vec_float4 v2 = vec_splat(v.val, 2); + VSX_UNUSED(const vec_float4) v3 = vec_splat(v.val, 3); + return v_float32x4(vec_madd(v0, m0.val, vec_madd(v1, m1.val, vec_madd(v2, m2.val, vec_mul(v3, m3.val))))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + const vec_float4 v0 = vec_splat(v.val, 0); + const vec_float4 v1 = vec_splat(v.val, 1); + const vec_float4 v2 = vec_splat(v.val, 2); + return v_float32x4(vec_madd(v0, m0.val, vec_madd(v1, m1.val, vec_madd(v2, m2.val, a.val)))); +} + +#define OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(_Tpvec, _Tpvec2) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ +{ \ + _Tpvec2 a02 = vec_mergeh(a0.val, a2.val); \ + _Tpvec2 a13 = vec_mergeh(a1.val, a3.val); \ + b0.val = vec_mergeh(a02, a13); \ + b1.val = vec_mergel(a02, a13); \ + a02 = vec_mergel(a0.val, a2.val); \ + a13 = vec_mergel(a1.val, a3.val); \ + b2.val = vec_mergeh(a02, a13); \ + b3.val = vec_mergel(a02, a13); \ +} +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_uint32x4, vec_uint4) +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_int32x4, vec_int4) +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_float32x4, vec_float4) + +template +inline Tvec v_broadcast_element(const Tvec& v) +{ return Tvec(vec_splat(v.val, i)); } + + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif // OPENCV_HAL_VSX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_wasm.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_wasm.hpp new file mode 100644 index 0000000..b8c250f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/intrin_wasm.hpp @@ -0,0 +1,4260 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HAL_INTRIN_WASM_HPP +#define OPENCV_HAL_INTRIN_WASM_HPP + +#include +#include +#include +#include "opencv2/core/saturate.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 0 // Now all implementation of f64 use fallback, so disable it. +#define CV_SIMD128_FP16 0 + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#if (__EMSCRIPTEN_major__ * 1000000 + __EMSCRIPTEN_minor__ * 1000 + __EMSCRIPTEN_tiny__) < (1038046) +// handle renames: https://github.com/emscripten-core/emscripten/pull/9440 (https://github.com/emscripten-core/emscripten/commit/755d5b46cb84d0aa120c10981b11d05646c29673) +#define wasm_i32x4_trunc_saturate_f32x4 wasm_trunc_saturate_i32x4_f32x4 +#define wasm_u32x4_trunc_saturate_f32x4 wasm_trunc_saturate_u32x4_f32x4 +#define wasm_i64x2_trunc_saturate_f64x2 wasm_trunc_saturate_i64x2_f64x2 +#define wasm_u64x2_trunc_saturate_f64x2 wasm_trunc_saturate_u64x2_f64x2 +#define wasm_f32x4_convert_i32x4 wasm_convert_f32x4_i32x4 +#define wasm_f32x4_convert_u32x4 wasm_convert_f32x4_u32x4 +#define wasm_f64x2_convert_i64x2 wasm_convert_f64x2_i64x2 +#define wasm_f64x2_convert_u64x2 wasm_convert_f64x2_u64x2 +#endif // COMPATIBILITY: <1.38.46 + +///////// Types /////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + typedef v128_t vector_type; + enum { nlanes = 16 }; + + v_uint8x16() : val(wasm_i8x16_splat(0)) {} + explicit v_uint8x16(v128_t v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = wasm_v128_load(v); + } + uchar get0() const + { + return (uchar)wasm_i8x16_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + typedef v128_t vector_type; + enum { nlanes = 16 }; + + v_int8x16() : val(wasm_i8x16_splat(0)) {} + explicit v_int8x16(v128_t v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = wasm_v128_load(v); + } + schar get0() const + { + return wasm_i8x16_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + typedef v128_t vector_type; + enum { nlanes = 8 }; + + v_uint16x8() : val(wasm_i16x8_splat(0)) {} + explicit v_uint16x8(v128_t v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = wasm_v128_load(v); + } + ushort get0() const + { + return (ushort)wasm_i16x8_extract_lane(val, 0); // wasm_u16x8_extract_lane() unimplemented yet + } + + v128_t val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + typedef v128_t vector_type; + enum { nlanes = 8 }; + + v_int16x8() : val(wasm_i16x8_splat(0)) {} + explicit v_int16x8(v128_t v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = wasm_v128_load(v); + } + short get0() const + { + return wasm_i16x8_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_uint32x4() : val(wasm_i32x4_splat(0)) {} + explicit v_uint32x4(v128_t v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + unsigned v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + unsigned get0() const + { + return (unsigned)wasm_i32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_int32x4() : val(wasm_i32x4_splat(0)) {} + explicit v_int32x4(v128_t v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + int get0() const + { + return wasm_i32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_float32x4() : val(wasm_f32x4_splat(0)) {} + explicit v_float32x4(v128_t v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + float get0() const + { + return wasm_f32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + +#ifdef __wasm_unimplemented_simd128__ + v_uint64x2() : val(wasm_i64x2_splat(0)) {} +#else + v_uint64x2() : val(wasm_i32x4_splat(0)) {} +#endif + explicit v_uint64x2(v128_t v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = wasm_v128_load(v); + } + uint64 get0() const + { +#ifdef __wasm_unimplemented_simd128__ + return (uint64)wasm_i64x2_extract_lane(val, 0); +#else + uint64 des[2]; + wasm_v128_store(des, val); + return des[0]; +#endif + } + + v128_t val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + +#ifdef __wasm_unimplemented_simd128__ + v_int64x2() : val(wasm_i64x2_splat(0)) {} +#else + v_int64x2() : val(wasm_i32x4_splat(0)) {} +#endif + explicit v_int64x2(v128_t v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = wasm_v128_load(v); + } + int64 get0() const + { +#ifdef __wasm_unimplemented_simd128__ + return wasm_i64x2_extract_lane(val, 0); +#else + int64 des[2]; + wasm_v128_store(des, val); + return des[0]; +#endif + } + + v128_t val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + +#ifdef __wasm_unimplemented_simd128__ + v_float64x2() : val(wasm_f64x2_splat(0)) {} +#else + v_float64x2() : val(wasm_f32x4_splat(0)) {} +#endif + explicit v_float64x2(v128_t v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = wasm_v128_load(v); + } + double get0() const + { +#ifdef __wasm_unimplemented_simd128__ + return wasm_f64x2_extract_lane(val, 0); +#else + double des[2]; + wasm_v128_store(des, val); + return des[0]; +#endif + } + + v128_t val; +}; + +namespace fallback +{ + +template struct v_reg +{ + typedef _Tp lane_type; + enum { nlanes = n }; + + explicit v_reg(const _Tp* ptr) { for( int i = 0; i < n; i++ ) s[i] = ptr[i]; } + + v_reg(_Tp s0, _Tp s1) { s[0] = s0; s[1] = s1; } + + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3) { s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; } + + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + } + + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7, + _Tp s8, _Tp s9, _Tp s10, _Tp s11, + _Tp s12, _Tp s13, _Tp s14, _Tp s15) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + s[8] = s8; s[9] = s9; s[10] = s10; s[11] = s11; + s[12] = s12; s[13] = s13; s[14] = s14; s[15] = s15; + } + + v_reg() {} + + v_reg(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + } + + _Tp get0() const { return s[0]; } + + _Tp get(const int i) const { return s[i]; } + v_reg<_Tp, n> high() const + { + v_reg<_Tp, n> c; + int i; + for( i = 0; i < n/2; i++ ) + { + c.s[i] = s[i+(n/2)]; + c.s[i+(n/2)] = 0; + } + return c; + } + + static v_reg<_Tp, n> zero() + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = (_Tp)0; + return c; + } + + static v_reg<_Tp, n> all(_Tp s) + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = s; + return c; + } + + template v_reg<_Tp2, n2> reinterpret_as() const + { + size_t bytes = std::min(sizeof(_Tp2)*n2, sizeof(_Tp)*n); + v_reg<_Tp2, n2> c; + std::memcpy(&c.s[0], &s[0], bytes); + return c; + } + + v_reg(const cv::v_uint8x16& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_int8x16& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_uint16x8& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_int16x8& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_uint32x4& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_int32x4& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_float32x4& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_float64x2& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_uint64x2& v) { wasm_v128_store(&s, v.val); } + v_reg(const cv::v_int64x2& v) { wasm_v128_store(&s, v.val); } + + operator cv::v_uint8x16() const { return cv::v_uint8x16(wasm_v128_load(&s)); } + operator cv::v_int8x16() const { return cv::v_int8x16(wasm_v128_load(&s)); } + operator cv::v_uint16x8() const { return cv::v_uint16x8(wasm_v128_load(&s)); } + operator cv::v_int16x8() const { return cv::v_int16x8(wasm_v128_load(&s)); } + operator cv::v_uint32x4() const { return cv::v_uint32x4(wasm_v128_load(&s)); } + operator cv::v_int32x4() const { return cv::v_int32x4(wasm_v128_load(&s)); } + operator cv::v_float32x4() const { return cv::v_float32x4(wasm_v128_load(&s)); } + operator cv::v_float64x2() const { return cv::v_float64x2(wasm_v128_load(&s)); } + operator cv::v_uint64x2() const { return cv::v_uint64x2(wasm_v128_load(&s)); } + operator cv::v_int64x2() const { return cv::v_int64x2(wasm_v128_load(&s)); } + + _Tp s[n]; +}; + +typedef v_reg v_uint8x16; +typedef v_reg v_int8x16; +typedef v_reg v_uint16x8; +typedef v_reg v_int16x8; +typedef v_reg v_uint32x4; +typedef v_reg v_int32x4; +typedef v_reg v_float32x4; +typedef v_reg v_float64x2; +typedef v_reg v_uint64x2; +typedef v_reg v_int64x2; + +#define OPENCV_HAL_IMPL_BIN_OP(bin_op) \ +template inline v_reg<_Tp, n> \ + operator bin_op (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return c; \ +} \ +template inline v_reg<_Tp, n>& \ + operator bin_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return a; \ +} + +OPENCV_HAL_IMPL_BIN_OP(+) +OPENCV_HAL_IMPL_BIN_OP(-) +OPENCV_HAL_IMPL_BIN_OP(*) +OPENCV_HAL_IMPL_BIN_OP(/) + +#define OPENCV_HAL_IMPL_BIT_OP(bit_op) \ +template inline v_reg<_Tp, n> operator bit_op \ + (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return c; \ +} \ +template inline v_reg<_Tp, n>& operator \ + bit_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return a; \ +} + +OPENCV_HAL_IMPL_BIT_OP(&) +OPENCV_HAL_IMPL_BIT_OP(|) +OPENCV_HAL_IMPL_BIT_OP(^) + +template inline v_reg<_Tp, n> operator ~ (const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int(~V_TypeTraits<_Tp>::reinterpret_int(a.s[i])); + } + return c; +} + +#define OPENCV_HAL_IMPL_MATH_FUNC(func, cfunc, _Tp2) \ +template inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp2, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i]); \ + return c; \ +} + +OPENCV_HAL_IMPL_MATH_FUNC(v_sqrt, std::sqrt, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_sin, std::sin, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_cos, std::cos, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_exp, std::exp, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_log, std::log, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_abs, (typename V_TypeTraits<_Tp>::abs_type)std::abs, + typename V_TypeTraits<_Tp>::abs_type) +OPENCV_HAL_IMPL_MATH_FUNC(v_round, cvRound, int) +OPENCV_HAL_IMPL_MATH_FUNC(v_floor, cvFloor, int) +OPENCV_HAL_IMPL_MATH_FUNC(v_ceil, cvCeil, int) +OPENCV_HAL_IMPL_MATH_FUNC(v_trunc, int, int) + +#define OPENCV_HAL_IMPL_MINMAX_FUNC(func, cfunc) \ +template inline v_reg<_Tp, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i], b.s[i]); \ + return c; \ +} + +#define OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(func, cfunc) \ +template inline _Tp func(const v_reg<_Tp, n>& a) \ +{ \ + _Tp c = a.s[0]; \ + for( int i = 1; i < n; i++ ) \ + c = cfunc(c, a.s[i]); \ + return c; \ +} + +OPENCV_HAL_IMPL_MINMAX_FUNC(v_min, std::min) +OPENCV_HAL_IMPL_MINMAX_FUNC(v_max, std::max) +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_min, std::min) +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_max, std::max) + +static const unsigned char popCountTable[] = +{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; + +template +inline v_reg::abs_type, n> v_popcount(const v_reg<_Tp, n>& a) +{ + v_reg::abs_type, n> b = v_reg::abs_type, n>::zero(); + for (int i = 0; i < (int)(n*sizeof(_Tp)); i++) + b.s[i/sizeof(_Tp)] += popCountTable[v_reinterpret_as_u8(a).s[i]]; + return b; +} + +template +inline void v_minmax( const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& minval, v_reg<_Tp, n>& maxval ) +{ + for( int i = 0; i < n; i++ ) + { + minval.s[i] = std::min(a.s[i], b.s[i]); + maxval.s[i] = std::max(a.s[i], b.s[i]); + } +} + +#define OPENCV_HAL_IMPL_CMP_OP(cmp_op) \ +template \ +inline v_reg<_Tp, n> operator cmp_op(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)-(int)(a.s[i] cmp_op b.s[i])); \ + return c; \ +} + +OPENCV_HAL_IMPL_CMP_OP(<) +OPENCV_HAL_IMPL_CMP_OP(>) +OPENCV_HAL_IMPL_CMP_OP(<=) +OPENCV_HAL_IMPL_CMP_OP(>=) +OPENCV_HAL_IMPL_CMP_OP(==) +OPENCV_HAL_IMPL_CMP_OP(!=) + +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} + +#define OPENCV_HAL_IMPL_ARITHM_OP(func, bin_op, cast_op, _Tp2) \ +template \ +inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef _Tp2 rtype; \ + v_reg c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cast_op(a.s[i] bin_op b.s[i]); \ + return c; \ +} + +OPENCV_HAL_IMPL_ARITHM_OP(v_add_wrap, +, (_Tp), _Tp) +OPENCV_HAL_IMPL_ARITHM_OP(v_sub_wrap, -, (_Tp), _Tp) +OPENCV_HAL_IMPL_ARITHM_OP(v_mul_wrap, *, (_Tp), _Tp) + +template inline T _absdiff(T a, T b) +{ + return a > b ? a - b : b - a; +} + +template +inline v_reg::abs_type, n> v_absdiff(const v_reg<_Tp, n>& a, const v_reg<_Tp, n> & b) +{ + typedef typename V_TypeTraits<_Tp>::abs_type rtype; + v_reg c; + const rtype mask = (rtype)(std::numeric_limits<_Tp>::is_signed ? (1 << (sizeof(rtype)*8 - 1)) : 0); + for( int i = 0; i < n; i++ ) + { + rtype ua = a.s[i] ^ mask; + rtype ub = b.s[i] ^ mask; + c.s[i] = _absdiff(ua, ub); + } + return c; +} + +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +template +inline v_reg<_Tp, n> v_absdiffs(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++) + c.s[i] = saturate_cast<_Tp>(std::abs(a.s[i] - b.s[i])); + return c; +} + +template +inline v_reg<_Tp, n> v_invsqrt(const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = 1.f/std::sqrt(a.s[i]); + return c; +} + +template +inline v_reg<_Tp, n> v_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = std::sqrt(a.s[i]*a.s[i] + b.s[i]*b.s[i]); + return c; +} + +template +inline v_reg<_Tp, n> v_sqr_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = a.s[i]*a.s[i] + b.s[i]*b.s[i]; + return c; +} + +template +inline v_reg<_Tp, n> v_fma(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + v_reg<_Tp, n> d; + for( int i = 0; i < n; i++ ) + d.s[i] = a.s[i]*b.s[i] + c.s[i]; + return d; +} + +template +inline v_reg<_Tp, n> v_muladd(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + return v_fma(a, b, c); +} + +template inline v_reg::w_type, n/2> + v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1]; + return c; +} + +template inline v_reg::w_type, n/2> + v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, const v_reg::w_type, n / 2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg s; + for( int i = 0; i < (n/2); i++ ) + s.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1] + c.s[i]; + return s; +} + +template inline v_reg::q_type, n/4> + v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3]; + return s; +} + +template inline v_reg::q_type, n/4> + v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::q_type, n / 4>& c) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3] + c.s[i]; + return s; +} + +template inline void v_mul_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg::w_type, n/2>& c, + v_reg::w_type, n/2>& d) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i]*b.s[i]; + d.s[i] = (w_type)a.s[i+(n/2)]*b.s[i+(n/2)]; + } +} + +template inline v_reg<_Tp, n> v_mul_hi(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg<_Tp, n> c; + for (int i = 0; i < n; i++) + c.s[i] = (_Tp)(((w_type)a.s[i] * b.s[i]) >> sizeof(_Tp)*8); + return c; +} + +template inline void v_hsum(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i*2] + a.s[i*2+1]; + } +} + +#define OPENCV_HAL_IMPL_SHIFT_OP(shift_op) \ +template inline v_reg<_Tp, n> operator shift_op(const v_reg<_Tp, n>& a, int imm) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = (_Tp)(a.s[i] shift_op imm); \ + return c; \ +} + +OPENCV_HAL_IMPL_SHIFT_OP(<< ) +OPENCV_HAL_IMPL_SHIFT_OP(>> ) + +#define OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(suffix,opA,opB) \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> b; \ + for (int i = 0; i < n; i++) \ + { \ + int sIndex = i opA imm; \ + if (0 <= sIndex && sIndex < n) \ + { \ + b.s[i] = a.s[sIndex]; \ + } \ + else \ + { \ + b.s[i] = 0; \ + } \ + } \ + return b; \ +} \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for (int i = 0; i < n; i++) \ + { \ + int aIndex = i opA imm; \ + int bIndex = i opA imm opB n; \ + if (0 <= bIndex && bIndex < n) \ + { \ + c.s[i] = b.s[bIndex]; \ + } \ + else if (0 <= aIndex && aIndex < n) \ + { \ + c.s[i] = a.s[aIndex]; \ + } \ + else \ + { \ + c.s[i] = 0; \ + } \ + } \ + return c; \ +} + +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(left, -, +) +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(right, +, -) + +template inline typename V_TypeTraits<_Tp>::sum_type v_reduce_sum(const v_reg<_Tp, n>& a) +{ + typename V_TypeTraits<_Tp>::sum_type c = a.s[0]; + for( int i = 1; i < n; i++ ) + c += a.s[i]; + return c; +} + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v_float32x4 r; + r.s[0] = a.s[0] + a.s[1] + a.s[2] + a.s[3]; + r.s[1] = b.s[0] + b.s[1] + b.s[2] + b.s[3]; + r.s[2] = c.s[0] + c.s[1] + c.s[2] + c.s[3]; + r.s[3] = d.s[0] + d.s[1] + d.s[2] + d.s[3]; + return r; +} + +template inline typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type v_reduce_sad(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type c = _absdiff(a.s[0], b.s[0]); + for (int i = 1; i < n; i++) + c += _absdiff(a.s[i], b.s[i]); + return c; +} + +template inline int v_signmask(const v_reg<_Tp, n>& a) +{ + int mask = 0; + for( int i = 0; i < n; i++ ) + mask |= (V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0) << i; + return mask; +} + +template inline bool v_check_all(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) >= 0 ) + return false; + return true; +} + +template inline bool v_check_any(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0 ) + return true; + return false; +} + +template inline v_reg<_Tp, n> v_select(const v_reg<_Tp, n>& mask, + const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef V_TypeTraits<_Tp> Traits; + typedef typename Traits::int_type int_type; + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + { + int_type m = Traits::reinterpret_int(mask.s[i]); + CV_DbgAssert(m == 0 || m == (~(int_type)0)); // restrict mask values: 0 or 0xff/0xffff/etc + c.s[i] = m ? a.s[i] : b.s[i]; + } + return c; +} + +template inline void v_expand(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& b0, + v_reg::w_type, n/2>& b1) +{ + for( int i = 0; i < (n/2); i++ ) + { + b0.s[i] = a.s[i]; + b1.s[i] = a.s[i+(n/2)]; + } +} + +template +inline v_reg::w_type, n/2> +v_expand_low(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i]; + return b; +} + +template +inline v_reg::w_type, n/2> +v_expand_high(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i+(n/2)]; + return b; +} + +template inline v_reg::int_type, n> + v_reinterpret_as_int(const v_reg<_Tp, n>& a) +{ + v_reg::int_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_int(a.s[i]); + return c; +} + +template inline v_reg::uint_type, n> + v_reinterpret_as_uint(const v_reg<_Tp, n>& a) +{ + v_reg::uint_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_uint(a.s[i]); + return c; +} + +template inline void v_zip( const v_reg<_Tp, n>& a0, const v_reg<_Tp, n>& a1, + v_reg<_Tp, n>& b0, v_reg<_Tp, n>& b1 ) +{ + int i; + for( i = 0; i < n/2; i++ ) + { + b0.s[i*2] = a0.s[i]; + b0.s[i*2+1] = a1.s[i]; + } + for( ; i < n; i++ ) + { + b1.s[i*2-n] = a0.s[i]; + b1.s[i*2-n+1] = a1.s[i]; + } +} + +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load(const _Tp* ptr) +{ + return v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128>(ptr); +} + +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_aligned(const _Tp* ptr) +{ + return v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128>(ptr); +} + +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_low(const _Tp* ptr) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +template +inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_load_halves(const _Tp* loptr, const _Tp* hiptr) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = loptr[i]; + c.s[i+c.nlanes/2] = hiptr[i]; + } + return c; +} + +template +inline v_reg::w_type, V_TypeTraits<_Tp>::nlanes128 / 2> +v_load_expand(const _Tp* ptr) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg::nlanes128> c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +template +inline v_reg::q_type, V_TypeTraits<_Tp>::nlanes128 / 4> +v_load_expand_q(const _Tp* ptr) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg::nlanes128> c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b) +{ + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + a.s[i] = ptr[i2]; + b.s[i] = ptr[i2+1]; + } +} + +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c) +{ + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + a.s[i] = ptr[i3]; + b.s[i] = ptr[i3+1]; + c.s[i] = ptr[i3+2]; + } +} + +template +inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c, + v_reg<_Tp, n>& d) +{ + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + a.s[i] = ptr[i4]; + b.s[i] = ptr[i4+1]; + c.s[i] = ptr[i4+2]; + d.s[i] = ptr[i4+3]; + } +} + +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + ptr[i2] = a.s[i]; + ptr[i2+1] = b.s[i]; + } +} + +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + ptr[i3] = a.s[i]; + ptr[i3+1] = b.s[i]; + ptr[i3+2] = c.s[i]; + } +} + +template inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + const v_reg<_Tp, n>& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + ptr[i4] = a.s[i]; + ptr[i4+1] = b.s[i]; + ptr[i4+2] = c.s[i]; + ptr[i4+3] = d.s[i]; + } +} + +template +inline void v_store(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store_low(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store_high(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i+(n/2)]; +} + +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store_aligned_nocache(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/) +{ + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline v_reg<_Tp, n> v_combine_low(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i]; + c.s[i+(n/2)] = b.s[i]; + } + return c; +} + +template +inline v_reg<_Tp, n> v_combine_high(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i+(n/2)]; + c.s[i+(n/2)] = b.s[i+(n/2)]; + } + return c; +} + +template +inline void v_recombine(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& low, v_reg<_Tp, n>& high) +{ + for( int i = 0; i < (n/2); i++ ) + { + low.s[i] = a.s[i]; + low.s[i+(n/2)] = b.s[i]; + high.s[i] = a.s[i+(n/2)]; + high.s[i+(n/2)] = b.s[i+(n/2)]; + } +} + +template +inline v_reg<_Tp, n> v_extract(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> r; + const int shift = n - s; + int i = 0; + for (; i < shift; ++i) + r.s[i] = a.s[i+s]; + for (; i < n; ++i) + r.s[i] = b.s[i-shift]; + return r; +} + +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvRound(a.s[i]); + return c; +} + +template inline v_reg v_round(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = cvRound(b.s[i]); + } + return c; +} + +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvFloor(a.s[i]); + return c; +} + +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvCeil(a.s[i]); + return c; +} + +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (int)(a.s[i]); + return c; +} + +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvFloor(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvCeil(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (int)(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (float)a.s[i]; + return c; +} + +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = 0; + } + return c; +} + +template inline v_reg v_cvt_f32(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = (float)b.s[i]; + } + return c; +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + v_float64x2 c; + for( int i = 0; i < 2; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + v_float64x2 c; + for( int i = 0; i < 2; i++ ) + c.s[i] = (double)a.s[i+2]; + return c; +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + v_float64x2 c; + for( int i = 0; i < 2; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + v_float64x2 c; + for( int i = 0; i < 2; i++ ) + c.s[i] = (double)a.s[i+2]; + return c; +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ + v_float64x2 c; + for( int i = 0; i < 2; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i]]; + return c; +} +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut_pairs(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i / 2] + i % 2]; + return c; +} +template inline v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> v_lut_quads(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, V_TypeTraits<_Tp>::nlanes128> c; + for (int i = 0; i < V_TypeTraits<_Tp>::nlanes128; i++) + c.s[i] = tab[idx[i / 4] + i % 4]; + return c; +} + +template inline v_reg v_lut(const int* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const unsigned* tab, const v_reg& idx) +{ + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const float* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const double* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline void v_lut_deinterleave(const float* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline void v_lut_deinterleave(const double* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline v_reg<_Tp, n> v_interleave_pairs(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[4*i ] = vec.s[4*i ]; + c.s[4*i+1] = vec.s[4*i+2]; + c.s[4*i+2] = vec.s[4*i+1]; + c.s[4*i+3] = vec.s[4*i+3]; + } + return c; +} + +template inline v_reg<_Tp, n> v_interleave_quads(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/8; i++) + { + c.s[8*i ] = vec.s[8*i ]; + c.s[8*i+1] = vec.s[8*i+4]; + c.s[8*i+2] = vec.s[8*i+1]; + c.s[8*i+3] = vec.s[8*i+5]; + c.s[8*i+4] = vec.s[8*i+2]; + c.s[8*i+5] = vec.s[8*i+6]; + c.s[8*i+6] = vec.s[8*i+3]; + c.s[8*i+7] = vec.s[8*i+7]; + } + return c; +} + +template inline v_reg<_Tp, n> v_pack_triplets(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[3*i ] = vec.s[4*i ]; + c.s[3*i+1] = vec.s[4*i+1]; + c.s[3*i+2] = vec.s[4*i+2]; + } + return c; +} + +template +inline void v_transpose4x4( v_reg<_Tp, 4>& a0, const v_reg<_Tp, 4>& a1, + const v_reg<_Tp, 4>& a2, const v_reg<_Tp, 4>& a3, + v_reg<_Tp, 4>& b0, v_reg<_Tp, 4>& b1, + v_reg<_Tp, 4>& b2, v_reg<_Tp, 4>& b3 ) +{ + b0 = v_reg<_Tp, 4>(a0.s[0], a1.s[0], a2.s[0], a3.s[0]); + b1 = v_reg<_Tp, 4>(a0.s[1], a1.s[1], a2.s[1], a3.s[1]); + b2 = v_reg<_Tp, 4>(a0.s[2], a1.s[2], a2.s[2], a3.s[2]); + b3 = v_reg<_Tp, 4>(a0.s[3], a1.s[3], a2.s[3], a3.s[3]); +} + +#define OPENCV_HAL_IMPL_C_INIT_ZERO(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec::zero(); } + +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int64x2, int64, s64) + +#define OPENCV_HAL_IMPL_C_INIT_VAL(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_setall_##suffix(_Tp val) { return _Tpvec::all(val); } + +OPENCV_HAL_IMPL_C_INIT_VAL(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int64x2, int64, s64) + +#define OPENCV_HAL_IMPL_C_REINTERPRET(_Tpvec, _Tp, suffix) \ +template inline _Tpvec \ + v_reinterpret_as_##suffix(const v_reg<_Tp0, n0>& a) \ +{ return a.template reinterpret_as<_Tp, _Tpvec::nlanes>(); } + +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int16x8, short, s16) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int32x4, int, s32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_float32x4, float, f32) +OPENCV_HAL_IMPL_C_REINTERPRET(v_float64x2, double, f64) +OPENCV_HAL_IMPL_C_REINTERPRET(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_C_REINTERPRET(v_int64x2, int64, s64) + +#define OPENCV_HAL_IMPL_C_SHIFTL(_Tpvec, _Tp) \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return a << n; } + +OPENCV_HAL_IMPL_C_SHIFTL(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_SHIFTL(v_int16x8, short) +OPENCV_HAL_IMPL_C_SHIFTL(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_SHIFTL(v_int32x4, int) +OPENCV_HAL_IMPL_C_SHIFTL(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_SHIFTL(v_int64x2, int64) + +#define OPENCV_HAL_IMPL_C_SHIFTR(_Tpvec, _Tp) \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return a >> n; } + +OPENCV_HAL_IMPL_C_SHIFTR(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_SHIFTR(v_int16x8, short) +OPENCV_HAL_IMPL_C_SHIFTR(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_SHIFTR(v_int32x4, int) +OPENCV_HAL_IMPL_C_SHIFTR(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_SHIFTR(v_int64x2, int64) + +#define OPENCV_HAL_IMPL_C_RSHIFTR(_Tpvec, _Tp) \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ \ + _Tpvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + c.s[i] = (_Tp)((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + return c; \ +} + +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint16x8, ushort) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int16x8, short) +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int32x4, int) +OPENCV_HAL_IMPL_C_RSHIFTR(v_uint64x2, uint64) +OPENCV_HAL_IMPL_C_RSHIFTR(v_int64x2, int64) + +#define OPENCV_HAL_IMPL_C_PACK(_Tpvec, _Tpnvec, _Tpn, pack_suffix, cast) \ +inline _Tpnvec v_##pack_suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpnvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + { \ + c.s[i] = cast<_Tpn>(a.s[i]); \ + c.s[i+_Tpvec::nlanes] = cast<_Tpn>(b.s[i]); \ + } \ + return c; \ +} + +OPENCV_HAL_IMPL_C_PACK(v_uint16x8, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int16x8, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_uint32x4, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int32x4, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_uint64x2, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(v_int64x2, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(v_int16x8, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(v_int32x4, v_uint16x8, ushort, pack_u, saturate_cast) + +#define OPENCV_HAL_IMPL_C_RSHR_PACK(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +template inline _Tpnvec v_rshr_##pack_suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpnvec c; \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + { \ + c.s[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + c.s[i+_Tpvec::nlanes] = cast<_Tpn>((b.s[i] + ((_Tp)1 << (n - 1))) >> n); \ + } \ + return c; \ +} + +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) + +#define OPENCV_HAL_IMPL_C_PACK_STORE(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +inline void v_##pack_suffix##_store(_Tpn* ptr, const _Tpvec& a) \ +{ \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + ptr[i] = cast<_Tpn>(a.s[i]); \ +} + +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) + +#define OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(_Tpvec, _Tp, _Tpnvec, _Tpn, pack_suffix, cast) \ +template inline void v_rshr_##pack_suffix##_store(_Tpn* ptr, const _Tpvec& a) \ +{ \ + for( int i = 0; i < _Tpvec::nlanes; i++ ) \ + ptr[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (n - 1))) >> n); \ +} + +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint16x8, ushort, v_uint8x16, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int16x8, short, v_int8x16, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint32x4, unsigned, v_uint16x8, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int32x4, int, v_int16x8, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_uint64x2, uint64, v_uint32x4, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int64x2, int64, v_int32x4, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int16x8, short, v_uint8x16, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(v_int32x4, int, v_uint16x8, ushort, pack_u, saturate_cast) + +template +inline void _pack_b(_Tpm* mptr, const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + for (int i = 0; i < n; ++i) + { + mptr[i] = (_Tpm)a.s[i]; + mptr[i + n] = (_Tpm)b.s[i]; + } +} + +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + return mask; +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 8, c, d); + return mask; +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + v_uint8x16 mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 4, c, d); + _pack_b(mask.s + 8, e, f); + _pack_b(mask.s + 12, g, h); + return mask; +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + return v_float32x4(v.s[0]*m0.s[0] + v.s[1]*m1.s[0] + v.s[2]*m2.s[0] + v.s[3]*m3.s[0], + v.s[0]*m0.s[1] + v.s[1]*m1.s[1] + v.s[2]*m2.s[1] + v.s[3]*m3.s[1], + v.s[0]*m0.s[2] + v.s[1]*m1.s[2] + v.s[2]*m2.s[2] + v.s[3]*m3.s[2], + v.s[0]*m0.s[3] + v.s[1]*m1.s[3] + v.s[2]*m2.s[3] + v.s[3]*m3.s[3]); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + return v_float32x4(v.s[0]*m0.s[0] + v.s[1]*m1.s[0] + v.s[2]*m2.s[0] + m3.s[0], + v.s[0]*m0.s[1] + v.s[1]*m1.s[1] + v.s[2]*m2.s[1] + m3.s[1], + v.s[0]*m0.s[2] + v.s[1]*m1.s[2] + v.s[2]*m2.s[2] + m3.s[2], + v.s[0]*m0.s[3] + v.s[1]*m1.s[3] + v.s[2]*m2.s[3] + m3.s[3]); +} + +inline v_reg::nlanes128> +v_load_expand(const float16_t* ptr) +{ + v_reg::nlanes128> v; + for( int i = 0; i < v.nlanes; i++ ) + { + v.s[i] = ptr[i]; + } + return v; +} + +inline void +v_pack_store(float16_t* ptr, const v_reg::nlanes128>& v) +{ + for( int i = 0; i < v.nlanes; i++ ) + { + ptr[i] = float16_t(v.s[i]); + } +} + +inline void v_cleanup() {} +} // namespace fallback + +static v128_t wasm_unpacklo_i8x16(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,16,1,17,2,18,3,19,4,20,5,21,6,22,7,23); +} + +static v128_t wasm_unpacklo_i16x8(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,16,17,2,3,18,19,4,5,20,21,6,7,22,23); +} + +static v128_t wasm_unpacklo_i32x4(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,2,3,16,17,18,19,4,5,6,7,20,21,22,23); +} + +static v128_t wasm_unpacklo_i64x2(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); +} + +static v128_t wasm_unpackhi_i8x16(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,24,9,25,10,26,11,27,12,28,13,29,14,30,15,31); +} + +static v128_t wasm_unpackhi_i16x8(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,24,25,10,11,26,27,12,13,28,29,14,15,30,31); +} + +static v128_t wasm_unpackhi_i32x4(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,10,11,24,25,26,27,12,13,14,15,28,29,30,31); +} + +static v128_t wasm_unpackhi_i64x2(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +/** Convert **/ +// 8 >> 16 +inline v128_t v128_cvtu8x16_i16x8(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i8x16(a, z); +} +inline v128_t v128_cvti8x16_i16x8(const v128_t& a) +{ return wasm_i16x8_shr(wasm_unpacklo_i8x16(a, a), 8); } +// 8 >> 32 +inline v128_t v128_cvtu8x16_i32x4(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i16x8(wasm_unpacklo_i8x16(a, z), z); +} +inline v128_t v128_cvti8x16_i32x4(const v128_t& a) +{ + v128_t r = wasm_unpacklo_i8x16(a, a); + r = wasm_unpacklo_i8x16(r, r); + return wasm_i32x4_shr(r, 24); +} +// 16 >> 32 +inline v128_t v128_cvtu16x8_i32x4(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i16x8(a, z); +} +inline v128_t v128_cvti16x8_i32x4(const v128_t& a) +{ return wasm_i32x4_shr(wasm_unpacklo_i16x8(a, a), 16); } +// 32 >> 64 +inline v128_t v128_cvtu32x4_i64x2(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i32x4(a, z); +} +inline v128_t v128_cvti32x4_i64x2(const v128_t& a) +{ return wasm_unpacklo_i32x4(a, wasm_i32x4_shr(a, 31)); } + +// 16 << 8 +inline v128_t v128_cvtu8x16_i16x8_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i8x16(a, z); +} +inline v128_t v128_cvti8x16_i16x8_high(const v128_t& a) +{ return wasm_i16x8_shr(wasm_unpackhi_i8x16(a, a), 8); } +// 32 << 16 +inline v128_t v128_cvtu16x8_i32x4_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i16x8(a, z); +} +inline v128_t v128_cvti16x8_i32x4_high(const v128_t& a) +{ return wasm_i32x4_shr(wasm_unpackhi_i16x8(a, a), 16); } +// 64 << 32 +inline v128_t v128_cvtu32x4_i64x2_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i32x4(a, z); +} +inline v128_t v128_cvti32x4_i64x2_high(const v128_t& a) +{ return wasm_unpackhi_i32x4(a, wasm_i32x4_shr(a, 31)); } + +#define OPENCV_HAL_IMPL_WASM_INITVEC(_Tpvec, _Tp, suffix, zsuffix, _Tps) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(wasm_##zsuffix##_splat((_Tps)0)); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(wasm_##zsuffix##_splat((_Tps)v)); } \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0& a) \ +{ return _Tpvec(a.val); } + +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint8x16, uchar, u8, i8x16, schar) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int8x16, schar, s8, i8x16, schar) +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint16x8, ushort, u16, i16x8, short) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int16x8, short, s16, i16x8, short) +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint32x4, unsigned, u32, i32x4, int) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int32x4, int, s32, i32x4, int) +OPENCV_HAL_IMPL_WASM_INITVEC(v_float32x4, float, f32, f32x4, float) + +#ifdef __wasm_unimplemented_simd128__ +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint64x2, uint64, u64, i64x2, int64) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int64x2, int64, s64, i64x2, int64) +OPENCV_HAL_IMPL_WASM_INITVEC(v_float64x2, double, f64, f64x2, double) +#else +#define OPENCV_HAL_IMPL_FALLBACK_INITVEC(_Tpvec, _Tp, suffix, _Tps) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec((_Tps)0, (_Tps)0); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec((_Tps)v, (_Tps)v); } \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0& a) \ +{ return _Tpvec(a.val); } + +OPENCV_HAL_IMPL_FALLBACK_INITVEC(v_uint64x2, uint64, u64, int64) +OPENCV_HAL_IMPL_FALLBACK_INITVEC(v_int64x2, int64, s64, int64) +OPENCV_HAL_IMPL_FALLBACK_INITVEC(v_float64x2, double, f64, double) +#endif + +//////////////// PACK /////////////// +inline v_uint8x16 v_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u16x8_gt(b.val, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a1, b1, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_int8x16 v_pack(const v_int16x8& a, const v_int16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i16x8_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i16x8_lt(b1, minval)); + return v_int8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_uint16x8 v_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u32x4_gt(b.val, maxval)); + return v_uint16x8(wasm_v8x16_shuffle(a1, b1, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +inline v_int16x8 v_pack(const v_int32x4& a, const v_int32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i32x4_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i32x4_lt(b1, minval)); + return v_int16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +inline v_uint32x4 v_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + return v_uint32x4(wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +inline v_int32x4 v_pack(const v_int64x2& a, const v_int64x2& b) +{ + return v_int32x4(wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +inline v_uint8x16 v_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i16x8_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i16x8_lt(b1, minval)); + return v_uint8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_uint16x8 v_pack_u(const v_int32x4& a, const v_int32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i32x4_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i32x4_lt(b1, minval)); + return v_uint16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} + +template +inline v_uint8x16 v_rshr_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_u16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_u16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_u16x8_gt(b1, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_int8x16 v_rshr_pack(const v_int16x8& a, const v_int16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_i16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i16x8_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i16x8_lt(b1, minval)); + return v_int8x16(wasm_v8x16_shuffle(a3, b3, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_uint16x8 v_rshr_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_u32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_u32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_u32x4_gt(b1, maxval)); + return v_uint16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +template +inline v_int16x8 v_rshr_pack(const v_int32x4& a, const v_int32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_i32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i16x8_splat(-32768); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i32x4_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i32x4_lt(b1, minval)); + return v_int16x8(wasm_v8x16_shuffle(a3, b3, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +template +inline v_uint32x4 v_rshr_pack(const v_uint64x2& a, const v_uint64x2& b) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_u64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t b1 = wasm_u64x2_shr(wasm_i64x2_add(b.val, delta), n); + return v_uint32x4(wasm_v8x16_shuffle(a1, b1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +#else + fallback::v_uint64x2 a_(a), b_(b); + return fallback::v_rshr_pack(a_, b_); +#endif +} +template +inline v_int32x4 v_rshr_pack(const v_int64x2& a, const v_int64x2& b) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_i64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t b1 = wasm_i64x2_shr(wasm_i64x2_add(b.val, delta), n); + return v_int32x4(wasm_v8x16_shuffle(a1, b1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +#else + fallback::v_int64x2 a_(a), b_(b); + return fallback::v_rshr_pack(a_, b_); +#endif +} +template +inline v_uint8x16 v_rshr_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_i16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i16x8_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i16x8_lt(b1, minval)); + return v_uint8x16(wasm_v8x16_shuffle(a3, b3, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_uint16x8 v_rshr_pack_u(const v_int32x4& a, const v_int32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_i32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i32x4_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i32x4_lt(b1, minval)); + return v_uint16x8(wasm_v8x16_shuffle(a3, b3, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} + +inline void v_pack_store(uchar* ptr, const v_uint16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(schar* ptr, const v_int16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + schar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(ushort* ptr, const v_uint32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(short* ptr, const v_int32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + short t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + v128_t r = wasm_v8x16_shuffle(a.val, a.val, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + unsigned t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(int* ptr, const v_int64x2& a) +{ + v128_t r = wasm_v8x16_shuffle(a.val, a.val, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + int t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_u_store(ushort* ptr, const v_int32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} + +template +inline void v_rshr_pack_store(uchar* ptr, const v_uint16x8& a) +{ + v128_t delta = wasm_i16x8_splat((short)(1 << (n-1))); + v128_t a1 = wasm_u16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u16x8_gt(a1, maxval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(schar* ptr, const v_int16x8& a) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + schar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(ushort* ptr, const v_uint32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_u32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u32x4_gt(a1, maxval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(short* ptr, const v_int32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + short t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(unsigned* ptr, const v_uint64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_u64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + unsigned t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +#else + fallback::v_uint64x2 _a(a); + fallback::v_rshr_pack_store(ptr, _a); +#endif +} +template +inline void v_rshr_pack_store(int* ptr, const v_int64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_i64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + int t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +#else + fallback::v_int64x2 _a(a); + fallback::v_rshr_pack_store(ptr, _a); +#endif +} +template +inline void v_rshr_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_u_store(ushort* ptr, const v_int32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} + +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u16x8_gt(b.val, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a1, b1, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + v128_t maxval = wasm_i32x4_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u32x4_gt(b.val, maxval)); + v128_t c1 = wasm_v128_bitselect(maxval, c.val, wasm_u32x4_gt(c.val, maxval)); + v128_t d1 = wasm_v128_bitselect(maxval, d.val, wasm_u32x4_gt(d.val, maxval)); + v128_t ab = wasm_v8x16_shuffle(a1, b1, 0,4,8,12,16,20,24,28,0,4,8,12,16,20,24,28); + v128_t cd = wasm_v8x16_shuffle(c1, d1, 0,4,8,12,16,20,24,28,0,4,8,12,16,20,24,28); + return v_uint8x16(wasm_v8x16_shuffle(ab, cd, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t maxval = wasm_i32x4_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, ((__u64x2)(a.val) > (__u64x2)maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, ((__u64x2)(b.val) > (__u64x2)maxval)); + v128_t c1 = wasm_v128_bitselect(maxval, c.val, ((__u64x2)(c.val) > (__u64x2)maxval)); + v128_t d1 = wasm_v128_bitselect(maxval, d.val, ((__u64x2)(d.val) > (__u64x2)maxval)); + v128_t e1 = wasm_v128_bitselect(maxval, e.val, ((__u64x2)(e.val) > (__u64x2)maxval)); + v128_t f1 = wasm_v128_bitselect(maxval, f.val, ((__u64x2)(f.val) > (__u64x2)maxval)); + v128_t g1 = wasm_v128_bitselect(maxval, g.val, ((__u64x2)(g.val) > (__u64x2)maxval)); + v128_t h1 = wasm_v128_bitselect(maxval, h.val, ((__u64x2)(h.val) > (__u64x2)maxval)); + v128_t ab = wasm_v8x16_shuffle(a1, b1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t cd = wasm_v8x16_shuffle(c1, d1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t ef = wasm_v8x16_shuffle(e1, f1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t gh = wasm_v8x16_shuffle(g1, h1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t abcd = wasm_v8x16_shuffle(ab, cd, 0,1,2,3,16,17,18,19,0,1,2,3,16,17,18,19); + v128_t efgh = wasm_v8x16_shuffle(ef, gh, 0,1,2,3,16,17,18,19,0,1,2,3,16,17,18,19); + return v_uint8x16(wasm_v8x16_shuffle(abcd, efgh, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23)); +#else + fallback::v_uint64x2 a_(a), b_(b), c_(c), d_(d), e_(e), f_(f), g_(g), h_(h); + return fallback::v_pack_b(a_, b_, c_, d_, e_, f_, g_, h_); +#endif +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + v128_t v0 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 0)); + v128_t v1 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 1)); + v128_t v2 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 2)); + v128_t v3 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 3)); + v0 = wasm_f32x4_mul(v0, m0.val); + v1 = wasm_f32x4_mul(v1, m1.val); + v2 = wasm_f32x4_mul(v2, m2.val); + v3 = wasm_f32x4_mul(v3, m3.val); + + return v_float32x4(wasm_f32x4_add(wasm_f32x4_add(v0, v1), wasm_f32x4_add(v2, v3))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + v128_t v0 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 0)); + v128_t v1 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 1)); + v128_t v2 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 2)); + v0 = wasm_f32x4_mul(v0, m0.val); + v1 = wasm_f32x4_mul(v1, m1.val); + v2 = wasm_f32x4_mul(v2, m2.val); + + return v_float32x4(wasm_f32x4_add(wasm_f32x4_add(v0, v1), wasm_f32x4_add(v2, a.val))); +} + +#define OPENCV_HAL_IMPL_WASM_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint8x16, wasm_u8x16_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint8x16, wasm_u8x16_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int8x16, wasm_i8x16_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int8x16, wasm_i8x16_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint16x8, wasm_u16x8_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint16x8, wasm_u16x8_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int16x8, wasm_i16x8_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int16x8, wasm_i16x8_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint32x4, wasm_i32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint32x4, wasm_i32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_uint32x4, wasm_i32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int32x4, wasm_i32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int32x4, wasm_i32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_int32x4, wasm_i32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_float32x4, wasm_f32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_float32x4, wasm_f32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_float32x4, wasm_f32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(/, v_float32x4, wasm_f32x4_div) + +#ifdef __wasm_unimplemented_simd128__ +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint64x2, wasm_i64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint64x2, wasm_i64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int64x2, wasm_i64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int64x2, wasm_i64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_float64x2, wasm_f64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_float64x2, wasm_f64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_float64x2, wasm_f64x2_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(/, v_float64x2, wasm_f64x2_div) +#else +#define OPENCV_HAL_IMPL_FALLBACK_BIN_OP(bin_op, _Tpvec) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + fallback::_Tpvec a_(a), b_(b); \ + return _Tpvec((a_) bin_op (b_)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + fallback::_Tpvec a_(a), b_(b); \ + a_ bin_op##= b_; \ + a = _Tpvec(a_); \ + return a; \ +} + +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(+, v_uint64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(-, v_uint64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(+, v_int64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(-, v_int64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(+, v_float64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(-, v_float64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(*, v_float64x2) +OPENCV_HAL_IMPL_FALLBACK_BIN_OP(/, v_float64x2) +#endif + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_WASM_MUL_SAT(_Tpvec, _Tpwvec) \ +inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ +} \ +inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ +{ a = a * b; return a; } + +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_uint16x8, v_uint32x4) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_int16x8, v_int32x4) + +// Multiply and expand +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v_uint16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v_int16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + v_int32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = wasm_i32x4_mul(a0.val, b0.val); + d.val = wasm_i32x4_mul(a1.val, b1.val); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = wasm_i32x4_mul(a0.val, b0.val); + d.val = wasm_i32x4_mul(a1.val, b1.val); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ +#ifdef __wasm_unimplemented_simd128__ + v_uint64x2 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = ((__u64x2)(a0.val) * (__u64x2)(b0.val)); + d.val = ((__u64x2)(a1.val) * (__u64x2)(b1.val)); +#else + fallback::v_uint32x4 a_(a), b_(b); + fallback::v_uint64x2 c_, d_; + fallback::v_mul_expand(a_, b_, c_, d_); + c = v_uint64x2(c_); + d = v_uint64x2(d_); +#endif +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + v128_t c = wasm_i32x4_mul(a0.val, b0.val); + v128_t d = wasm_i32x4_mul(a1.val, b1.val); + return v_int16x8(wasm_v8x16_shuffle(c, d, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31)); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + v128_t c = wasm_i32x4_mul(a0.val, b0.val); + v128_t d = wasm_i32x4_mul(a1.val, b1.val); + return v_uint16x8(wasm_v8x16_shuffle(c, d, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31)); +} + +//////// Dot Product //////// + +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ + v128_t a0 = wasm_i32x4_shr(wasm_i32x4_shl(a.val, 16), 16); + v128_t a1 = wasm_i32x4_shr(a.val, 16); + v128_t b0 = wasm_i32x4_shr(wasm_i32x4_shl(b.val, 16), 16); + v128_t b1 = wasm_i32x4_shr(b.val, 16); + v128_t c = wasm_i32x4_mul(a0, b0); + v128_t d = wasm_i32x4_mul(a1, b1); + return v_int32x4(wasm_i32x4_add(c, d)); +} + +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t a0 = wasm_i64x2_shr(wasm_i64x2_shl(a.val, 32), 32); + v128_t a1 = wasm_i64x2_shr(a.val, 32); + v128_t b0 = wasm_i64x2_shr(wasm_i64x2_shl(b.val, 32), 32); + v128_t b1 = wasm_i64x2_shr(b.val, 32); + v128_t c = (v128_t)((__i64x2)a0 * (__i64x2)b0); + v128_t d = (v128_t)((__i64x2)a1 * (__i64x2)b1); + return v_int64x2(wasm_i64x2_add(c, d)); +#else + fallback::v_int32x4 a_(a); + fallback::v_int32x4 b_(b); + return fallback::v_dotprod(a_, b_); +#endif +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_dotprod(a, b) + c; +#else + fallback::v_int32x4 a_(a); + fallback::v_int32x4 b_(b); + fallback::v_int64x2 c_(c); + return fallback::v_dotprod(a_, b_, c_); +#endif +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + v128_t a0 = wasm_u16x8_shr(wasm_i16x8_shl(a.val, 8), 8); + v128_t a1 = wasm_u16x8_shr(a.val, 8); + v128_t b0 = wasm_u16x8_shr(wasm_i16x8_shl(b.val, 8), 8); + v128_t b1 = wasm_u16x8_shr(b.val, 8); + return v_uint32x4(( + v_dotprod(v_int16x8(a0), v_int16x8(b0)) + + v_dotprod(v_int16x8(a1), v_int16x8(b1))).val + ); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + v128_t a0 = wasm_i16x8_shr(wasm_i16x8_shl(a.val, 8), 8); + v128_t a1 = wasm_i16x8_shr(a.val, 8); + v128_t b0 = wasm_i16x8_shr(wasm_i16x8_shl(b.val, 8), 8); + v128_t b1 = wasm_i16x8_shr(b.val, 8); + return v_int32x4( + v_dotprod(v_int16x8(a0), v_int16x8(b0)) + + v_dotprod(v_int16x8(a1), v_int16x8(b1)) + ); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + fallback::v_uint16x8 a_(a); + fallback::v_uint16x8 b_(b); + return fallback::v_dotprod_expand(a_, b_); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ + fallback::v_uint16x8 a_(a); + fallback::v_uint16x8 b_(b); + fallback::v_uint64x2 c_(c); + return fallback::v_dotprod_expand(a_, b_, c_); +} + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + fallback::v_int16x8 a_(a); + fallback::v_int16x8 b_(b); + return fallback::v_dotprod_expand(a_, b_); +} + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ + fallback::v_int16x8 a_(a); + fallback::v_int16x8 b_(b); + fallback::v_int64x2 c_(c); + return fallback::v_dotprod_expand(a_, b_, c_); +} + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_IMPL_WASM_LOGIC_OP(_Tpvec) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(&, _Tpvec, wasm_v128_and) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(|, _Tpvec, wasm_v128_or) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(^, _Tpvec, wasm_v128_xor) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ \ + return _Tpvec(wasm_v128_not(a.val)); \ +} + +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint8x16) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int8x16) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint16x8) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int16x8) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint64x2) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int64x2) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_float32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_float64x2) + +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_float32x4(wasm_f32x4_sqrt(x.val)); +#else + fallback::v_float32x4 x_(x); + return fallback::v_sqrt(x_); +#endif +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ +#ifdef __wasm_unimplemented_simd128__ + const v128_t _1_0 = wasm_f32x4_splat(1.0); + return v_float32x4(wasm_f32x4_div(_1_0, wasm_f32x4_sqrt(x.val))); +#else + fallback::v_float32x4 x_(x); + return fallback::v_invsqrt(x_); +#endif +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_float64x2(wasm_f64x2_sqrt(x.val)); +#else + fallback::v_float64x2 x_(x); + return fallback::v_sqrt(x_); +#endif +} + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ +#ifdef __wasm_unimplemented_simd128__ + const v128_t _1_0 = wasm_f64x2_splat(1.0); + return v_float64x2(wasm_f64x2_div(_1_0, wasm_f64x2_sqrt(x.val))); +#else + fallback::v_float64x2 x_(x); + return fallback::v_invsqrt(x_); +#endif +} + +#define OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(_Tpuvec, _Tpsvec, suffix, zsuffix, shiftWidth) \ +inline _Tpuvec v_abs(const _Tpsvec& x) \ +{ \ + v128_t s = wasm_##suffix##_shr(x.val, shiftWidth); \ + v128_t f = wasm_##zsuffix##_shr(x.val, shiftWidth); \ + return _Tpuvec(wasm_##zsuffix##_add(wasm_v128_xor(x.val, f), s)); \ +} + +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint8x16, v_int8x16, u8x16, i8x16, 7) +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint16x8, v_int16x8, u16x8, i16x8, 15) +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint32x4, v_int32x4, u32x4, i32x4, 31) + +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(wasm_f32x4_abs(x.val)); } +inline v_float64x2 v_abs(const v_float64x2& x) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_float64x2(wasm_f64x2_abs(x.val)); +#else + fallback::v_float64x2 x_(x); + return fallback::v_abs(x_); +#endif +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_WASM_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_min, wasm_f32x4_min) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_max, wasm_f32x4_max) + +#ifdef __wasm_unimplemented_simd128__ +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float64x2, v_min, wasm_f64x2_min) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float64x2, v_max, wasm_f64x2_max) +#else +#define OPENCV_HAL_IMPL_WASM_MINMAX_64f_FUNC(func) \ +inline v_float64x2 func(const v_float64x2& a, const v_float64x2& b) \ +{ \ + fallback::v_float64x2 a_(a), b_(b); \ + return fallback::func(a_, b_); \ +} + +OPENCV_HAL_IMPL_WASM_MINMAX_64f_FUNC(v_min) +OPENCV_HAL_IMPL_WASM_MINMAX_64f_FUNC(v_max) +#endif + +#define OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(_Tpvec, suffix) \ +inline _Tpvec v_min(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(b.val, a.val, wasm_##suffix##_gt(a.val, b.val))); \ +} \ +inline _Tpvec v_max(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, wasm_##suffix##_gt(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int8x16, i8x16) +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int16x8, i16x8) +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int32x4, i32x4) + +#define OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(_Tpvec, suffix, deltaNum) \ +inline _Tpvec v_min(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t delta = wasm_##suffix##_splat(deltaNum); \ + v128_t mask = wasm_##suffix##_gt(wasm_v128_xor(a.val, delta), wasm_v128_xor(b.val, delta)); \ + return _Tpvec(wasm_v128_bitselect(b.val, a.val, mask)); \ +} \ +inline _Tpvec v_max(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t delta = wasm_##suffix##_splat(deltaNum); \ + v128_t mask = wasm_##suffix##_gt(wasm_v128_xor(a.val, delta), wasm_v128_xor(b.val, delta)); \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, mask)); \ +} + +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint8x16, i8x16, (schar)0x80) +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint16x8, i16x8, (short)0x8000) +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint32x4, i32x4, (int)0x80000000) + +#define OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(_Tpvec, suffix, esuffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##esuffix##_eq(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##esuffix##_ne(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_lt(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_gt(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_le(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_ge(a.val, b.val)); } + +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint8x16, u8x16, i8x16) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int8x16, i8x16, i8x16) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint16x8, u16x8, i16x8) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int16x8, i16x8, i16x8) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint32x4, u32x4, i32x4) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int32x4, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_float32x4, f32x4, f32x4) + +#ifdef __wasm_unimplemented_simd128__ +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_float64x2, f64x2, f64x2) +#else +#define OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(_Tpvec, bin_op) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + fallback::_Tpvec a_(a), b_(b); \ + return _Tpvec((a_) bin_op (b_));\ +} \ + +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, ==) +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, !=) +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, <) +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, >) +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, <=) +OPENCV_HAL_IMPL_INIT_FALLBACK_CMP_OP(v_float64x2, >=) +#endif + +#define OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(_Tpvec, cast) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return cast(v_reinterpret_as_f64(a) == v_reinterpret_as_f64(b)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return cast(v_reinterpret_as_f64(a) != v_reinterpret_as_f64(b)); } + +OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(v_uint64x2, v_reinterpret_as_u64) +OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(v_int64x2, v_reinterpret_as_s64) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ + v128_t z = wasm_i32x4_splat(0x7fffffff); + v128_t t = wasm_i32x4_splat(0x7f800000); + return v_float32x4(wasm_u32x4_lt(wasm_v128_and(a.val, z), t)); +} +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t z = wasm_i64x2_splat(0x7fffffffffffffff); + v128_t t = wasm_i64x2_splat(0x7ff0000000000000); + return v_float64x2((__u64x2)(wasm_v128_and(a.val, z)) < (__u64x2)t); +#else + fallback::v_float64x2 a_(a); + return fallback::v_not_nan(a_); +#endif +} + +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_add_wrap, wasm_i8x16_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_add_wrap, wasm_i8x16_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_add_wrap, wasm_i16x8_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_add_wrap, wasm_i16x8_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_sub_wrap, wasm_i8x16_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_sub_wrap, wasm_i8x16_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_sub_wrap, wasm_i16x8_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_sub_wrap, wasm_i16x8_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_mul_wrap, wasm_i8x16_mul) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_mul_wrap, wasm_i8x16_mul) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_mul_wrap, wasm_i16x8_mul) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_mul_wrap, wasm_i16x8_mul) + + +/** Absolute difference **/ + +inline v_uint8x16 v_absdiff(const v_uint8x16& a, const v_uint8x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x8 v_absdiff(const v_uint16x8& a, const v_uint16x8& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x4 v_absdiff(const v_uint32x4& a, const v_uint32x4& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = v_sub_wrap(a, b); + v_int8x16 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ + return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); +} +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ + v_int32x4 d = a - b; + v_int32x4 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = a - b; + v_int8x16 m = a < b; + return (d ^ m) - m; + } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_max(a, b) - v_min(a, b); } + + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return a * b + c; +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return a * b + c; +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return a * b + c; +} + +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ + v128_t absmask_vec = wasm_i32x4_splat(0x7fffffff); + return v_float32x4(wasm_v128_and(wasm_f32x4_sub(a.val, b.val), absmask_vec)); +} +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t absmask_vec = wasm_u64x2_shr(wasm_i32x4_splat(-1), 1); + return v_float64x2(wasm_v128_and(wasm_f64x2_sub(a.val, b.val), absmask_vec)); +#else + fallback::v_float64x2 a_(a), b_(b); + return fallback::v_absdiff(a_, b_); +#endif +} + +#define OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(_Tpvec) \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + fallback::_Tpvec a_(a), b_(b); \ + return fallback::v_magnitude(a_, b_); \ +} \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return v_fma(a, a, b*b); \ +} \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ \ + return v_fma(a, b, c); \ +} + +OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(v_float32x4) +OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(v_float64x2) + +#define OPENCV_HAL_IMPL_WASM_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, ssuffix) \ +inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(wasm_##ssuffix##_shr(a.val, imm)); \ +} \ +inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(wasm_##suffix##_shr(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shl(const _Tpuvec& a) \ +{ \ + return _Tpuvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shl(const _Tpsvec& a) \ +{ \ + return _Tpsvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shr(const _Tpuvec& a) \ +{ \ + return _Tpuvec(wasm_##ssuffix##_shr(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shr(const _Tpsvec& a) \ +{ \ + return _Tpsvec(wasm_##suffix##_shr(a.val, imm)); \ +} + +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint8x16, v_int8x16, i8x16, u8x16) +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint16x8, v_int16x8, i16x8, u16x8) +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint32x4, v_int32x4, i32x4, u32x4) + +#ifdef __wasm_unimplemented_simd128__ +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint64x2, v_int64x2, i64x2, u64x2) +#else +#define OPENCV_HAL_IMPL_FALLBACK_SHIFT_OP(_Tpvec) \ +inline _Tpvec operator << (const _Tpvec& a, int imm) \ +{ \ + fallback::_Tpvec a_(a); \ + return a_ << imm; \ +} \ +inline _Tpvec operator >> (const _Tpvec& a, int imm) \ +{ \ + fallback::_Tpvec a_(a); \ + return a_ >> imm; \ +} \ +template \ +inline _Tpvec v_shl(const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + return fallback::v_shl(a_); \ +} \ +template \ +inline _Tpvec v_shr(const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + return fallback::v_shr(a_); \ +} \ + +OPENCV_HAL_IMPL_FALLBACK_SHIFT_OP(v_uint64x2) +OPENCV_HAL_IMPL_FALLBACK_SHIFT_OP(v_int64x2) +#endif + +namespace hal_wasm_internal +{ + template 16)), + bool is_first = (imm == 0), + bool is_second = (imm == 16), + bool is_other = (((imm > 0) && (imm < 16)))> + class v_wasm_palignr_u8_class; + + template + class v_wasm_palignr_u8_class; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t& a, const v128_t&) const + { + return a; + } + }; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t&, const v128_t& b) const + { + return b; + } + }; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t& a, const v128_t& b) const + { + enum { imm2 = (sizeof(v128_t) - imm) }; + return wasm_v8x16_shuffle(a, b, + imm, imm+1, imm+2, imm+3, + imm+4, imm+5, imm+6, imm+7, + imm+8, imm+9, imm+10, imm+11, + imm+12, imm+13, imm+14, imm+15); + } + }; + + template + inline v128_t v_wasm_palignr_u8(const v128_t& a, const v128_t& b) + { + CV_StaticAssert((imm >= 0) && (imm <= 16), "Invalid imm for v_wasm_palignr_u8."); + return v_wasm_palignr_u8_class()(a, b); + } +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a) +{ + using namespace hal_wasm_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + v128_t z = wasm_i8x16_splat(0); + return _Tpvec(v_wasm_palignr_u8(a.val, z)); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a) +{ + using namespace hal_wasm_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + v128_t z = wasm_i8x16_splat(0); + return _Tpvec(v_wasm_palignr_u8(z, a.val)); +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_wasm_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_wasm_palignr_u8(a.val, b.val)); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_wasm_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_wasm_palignr_u8(b.val, a.val)); +} + +#define OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(_Tpvec, _Tp) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(wasm_v128_load(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(wasm_v128_load(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ \ + _Tp tmp[_Tpvec::nlanes] = {0}; \ + for (int i=0; i<_Tpvec::nlanes/2; ++i) { \ + tmp[i] = ptr[i]; \ + } \ + return _Tpvec(wasm_v128_load(tmp)); \ +} \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + _Tp tmp[_Tpvec::nlanes]; \ + for (int i=0; i<_Tpvec::nlanes/2; ++i) { \ + tmp[i] = ptr0[i]; \ + tmp[i+_Tpvec::nlanes/2] = ptr1[i]; \ + } \ + return _Tpvec(wasm_v128_load(tmp)); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ \ + wasm_v128_store(ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + fallback::v_store_low(ptr, a_); \ +} \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + fallback::v_store_high(ptr, a_); \ +} + +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint8x16, uchar) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int8x16, schar) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint16x8, ushort) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int16x8, short) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int32x4, int) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint64x2, uint64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int64x2, int64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_float32x4, float) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_float64x2, double) + + +/** Reverse **/ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ return v_uint8x16(wasm_v8x16_shuffle(a.val, a.val, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); } + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ return v_uint16x8(wasm_v8x16_shuffle(a.val, a.val, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1)); } + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ return v_uint32x4(wasm_v8x16_shuffle(a.val, a.val, 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3)); } + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ return v_uint64x2(wasm_v8x16_shuffle(a.val, a.val, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)); } + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + + +#define OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(_Tpvec, scalartype, regtype, suffix, esuffix) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + regtype val = a.val; \ + val = wasm_##suffix##_add(val, wasm_v8x16_shuffle(val, val, 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); \ + val = wasm_##suffix##_add(val, wasm_v8x16_shuffle(val, val, 4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3)); \ + return (scalartype)wasm_##esuffix##_extract_lane(val, 0); \ +} + +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_uint32x4, unsigned, v128_t, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_int32x4, int, v128_t, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_float32x4, float, v128_t, f32x4, f32x4) + +// To do: Optimize v_reduce_sum with wasm intrin. +// Now use fallback implementation as there is no widening op in wasm intrin. + +#define OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(_Tpvec, scalartype) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + return fallback::v_reduce_sum(a_); \ +} + +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_uint8x16, unsigned) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_int8x16, int) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_uint16x8, unsigned) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_int16x8, int) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_uint64x2, uint64) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_int64x2, int64) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_float64x2, double) + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v128_t ac = wasm_f32x4_add(wasm_unpacklo_i32x4(a.val, c.val), wasm_unpackhi_i32x4(a.val, c.val)); + v128_t bd = wasm_f32x4_add(wasm_unpacklo_i32x4(b.val, d.val), wasm_unpackhi_i32x4(b.val, d.val)); + return v_float32x4(wasm_f32x4_add(wasm_unpacklo_i32x4(ac, bd), wasm_unpackhi_i32x4(ac, bd))); +} + +#define OPENCV_HAL_IMPL_WASM_REDUCE_OP(_Tpvec, scalartype, func, scalar_func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + scalartype buf[_Tpvec::nlanes]; \ + v_store(buf, a); \ + scalartype tmp = buf[0]; \ + for (int i=1; i<_Tpvec::nlanes; ++i) { \ + tmp = scalar_func(tmp, buf[i]); \ + } \ + return tmp; \ +} + +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint8x16, uchar, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint8x16, uchar, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int8x16, schar, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int8x16, schar, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint16x8, ushort, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint16x8, ushort, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int16x8, short, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int16x8, short, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_float32x4, float, min, std::min) + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + v_uint16x8 l16, h16; + v_uint32x4 l16_l32, l16_h32, h16_l32, h16_h32; + v_expand(v_absdiff(a, b), l16, h16); + v_expand(l16, l16_l32, l16_h32); + v_expand(h16, h16_l32, h16_h32); + return v_reduce_sum(l16_l32+l16_h32+h16_l32+h16_h32); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + v_uint16x8 l16, h16; + v_uint32x4 l16_l32, l16_h32, h16_l32, h16_h32; + v_expand(v_absdiff(a, b), l16, h16); + v_expand(l16, l16_l32, l16_h32); + v_expand(h16, h16_l32, h16_h32); + return v_reduce_sum(l16_l32+l16_h32+h16_l32+h16_h32); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ + v128_t m1 = wasm_i32x4_splat(0x55555555); + v128_t m2 = wasm_i32x4_splat(0x33333333); + v128_t m4 = wasm_i32x4_splat(0x0f0f0f0f); + v128_t p = a.val; + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 1), m1), wasm_v128_and(p, m1)); + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 2), m2), wasm_v128_and(p, m2)); + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 4), m4), wasm_v128_and(p, m4)); + return v_uint8x16(p); +} +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v_setall_u16(0x00ff); +} +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v_setall_u32(0x000000ff); +} +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ + fallback::v_uint64x2 a_(a); + return fallback::v_popcount(a_); +} +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +#define OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(_Tpvec, suffix, scalarType) \ +inline int v_signmask(const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + return fallback::v_signmask(a_); \ +} \ +inline bool v_check_all(const _Tpvec& a) \ +{ return wasm_i8x16_all_true(wasm_##suffix##_lt(a.val, wasm_##suffix##_splat(0))); } \ +inline bool v_check_any(const _Tpvec& a) \ +{ return wasm_i8x16_any_true(wasm_##suffix##_lt(a.val, wasm_##suffix##_splat(0)));; } + +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint8x16, i8x16, schar) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int8x16, i8x16, schar) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint16x8, i16x8, short) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int16x8, i16x8, short) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint32x4, i32x4, int) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int32x4, i32x4, int) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_float32x4, i32x4, float) + +inline int v_signmask(const v_float64x2& a) +{ + fallback::v_float64x2 a_(a); + return fallback::v_signmask(a_); +} +inline bool v_check_all(const v_float64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + return wasm_i8x16_all_true((__i64x2)(a.val) < (__i64x2)(wasm_i64x2_splat(0))); +#else + fallback::v_float64x2 a_(a); + return fallback::v_check_all(a_); +#endif +} +inline bool v_check_any(const v_float64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + return wasm_i8x16_any_true((__i64x2)(a.val) < (__i64x2)(wasm_i64x2_splat(0)));; +#else + fallback::v_float64x2 a_(a); + return fallback::v_check_any(a_); +#endif +} + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +#define OPENCV_HAL_IMPL_WASM_SELECT(_Tpvec) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, mask.val)); \ +} + +OPENCV_HAL_IMPL_WASM_SELECT(v_uint8x16) +OPENCV_HAL_IMPL_WASM_SELECT(v_int8x16) +OPENCV_HAL_IMPL_WASM_SELECT(v_uint16x8) +OPENCV_HAL_IMPL_WASM_SELECT(v_int16x8) +OPENCV_HAL_IMPL_WASM_SELECT(v_uint32x4) +OPENCV_HAL_IMPL_WASM_SELECT(v_int32x4) +// OPENCV_HAL_IMPL_WASM_SELECT(v_uint64x2) +// OPENCV_HAL_IMPL_WASM_SELECT(v_int64x2) +OPENCV_HAL_IMPL_WASM_SELECT(v_float32x4) +OPENCV_HAL_IMPL_WASM_SELECT(v_float64x2) + +#define OPENCV_HAL_IMPL_WASM_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = intrin(a.val); \ + b1.val = __CV_CAT(intrin, _high)(a.val); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ return _Tpwvec(intrin(a.val)); } \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ return _Tpwvec(__CV_CAT(intrin, _high)(a.val)); } \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + v128_t a = wasm_v128_load(ptr); \ + return _Tpwvec(intrin(a)); \ +} + +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint8x16, v_uint16x8, uchar, v128_cvtu8x16_i16x8) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int8x16, v_int16x8, schar, v128_cvti8x16_i16x8) +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint16x8, v_uint32x4, ushort, v128_cvtu16x8_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int16x8, v_int32x4, short, v128_cvti16x8_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint32x4, v_uint64x2, unsigned, v128_cvtu32x4_i64x2) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int32x4, v_int64x2, int, v128_cvti32x4_i64x2) + +#define OPENCV_HAL_IMPL_WASM_EXPAND_Q(_Tpvec, _Tp, intrin) \ +inline _Tpvec v_load_expand_q(const _Tp* ptr) \ +{ \ + v128_t a = wasm_v128_load(ptr); \ + return _Tpvec(intrin(a)); \ +} + +OPENCV_HAL_IMPL_WASM_EXPAND_Q(v_uint32x4, uchar, v128_cvtu8x16_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND_Q(v_int32x4, schar, v128_cvti8x16_i32x4) + +#define OPENCV_HAL_IMPL_WASM_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = wasm_unpacklo_##suffix(a0.val, a1.val); \ + b1.val = wasm_unpackhi_##suffix(a0.val, a1.val); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_unpacklo_i64x2(a.val, b.val)); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_unpackhi_i64x2(a.val, b.val)); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + c.val = wasm_unpacklo_i64x2(a.val, b.val); \ + d.val = wasm_unpackhi_i64x2(a.val, b.val); \ +} + +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint8x16, i8x16) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int8x16, i8x16) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint16x8, i16x8) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int16x8, i16x8) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_float32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_float64x2, i64x2) + +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ + return v_rotate_right(a, b); +} + +inline v_int32x4 v_round(const v_float32x4& a) +{ + v128_t h = wasm_f32x4_splat(0.5); + return v_int32x4(wasm_i32x4_trunc_saturate_f32x4(wasm_f32x4_add(a.val, h))); +} + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + v128_t a1 = wasm_i32x4_trunc_saturate_f32x4(a.val); + v128_t mask = wasm_f32x4_lt(a.val, wasm_f32x4_convert_i32x4(a1)); + return v_int32x4(wasm_i32x4_add(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + v128_t a1 = wasm_i32x4_trunc_saturate_f32x4(a.val); + v128_t mask = wasm_f32x4_gt(a.val, wasm_f32x4_convert_i32x4(a1)); + return v_int32x4(wasm_i32x4_sub(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(wasm_i32x4_trunc_saturate_f32x4(a.val)); } + +#define OPENCV_HAL_IMPL_WASM_MATH_FUNC(func, cfunc, _Tpvec, _Tpnvec, _Tp, _Tpn) \ +inline _Tpnvec func(const _Tpvec& a) \ +{ \ + fallback::_Tpvec a_(a); \ + return fallback::func(a_); \ +} + +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_round, cvRound, v_float64x2, v_int32x4, double, int) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_floor, cvFloor, v_float64x2, v_int32x4, double, int) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_ceil, cvCeil, v_float64x2, v_int32x4, double, int) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_trunc, int, v_float64x2, v_int32x4, double, int) + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + fallback::v_float64x2 a_(a), b_(b); + return fallback::v_round(a_, b_); +} + +#define OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + v128_t t0 = wasm_unpacklo_##suffix(a0.val, a1.val); \ + v128_t t1 = wasm_unpacklo_##suffix(a2.val, a3.val); \ + v128_t t2 = wasm_unpackhi_##suffix(a0.val, a1.val); \ + v128_t t3 = wasm_unpackhi_##suffix(a2.val, a3.val); \ +\ + b0.val = wasm_unpacklo_i64x2(t0, t1); \ + b1.val = wasm_unpackhi_i64x2(t0, t1); \ + b2.val = wasm_unpacklo_i64x2(t2, t3); \ + b3.val = wasm_unpackhi_i64x2(t2, t3); \ +} + +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_uint32x4, i32x4) +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_int32x4, i32x4) +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_float32x4, i32x4) + +// load deinterleave +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b) +{ + v128_t t00 = wasm_v128_load(ptr); + v128_t t01 = wasm_v128_load(ptr + 16); + + a.val = wasm_v8x16_shuffle(t00, t01, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30); + b.val = wasm_v8x16_shuffle(t00, t01, 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c) +{ + v128_t t00 = wasm_v128_load(ptr); + v128_t t01 = wasm_v128_load(ptr + 16); + v128_t t02 = wasm_v128_load(ptr + 32); + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,3,6,9,12,15,18,21,24,27,30,1,2,4,5,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 1,4,7,10,13,16,19,22,25,28,31,0,2,3,5,6); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 2,5,8,11,14,17,20,23,26,29,0,1,3,4,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,17,20,23,26,29); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,18,21,24,27,30); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,8,9,16,19,22,25,28,31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c, v_uint8x16& d) +{ + v128_t u0 = wasm_v128_load(ptr); // a0 b0 c0 d0 a1 b1 c1 d1 ... + v128_t u1 = wasm_v128_load(ptr + 16); // a4 b4 c4 d4 ... + v128_t u2 = wasm_v128_load(ptr + 32); // a8 b8 c8 d8 ... + v128_t u3 = wasm_v128_load(ptr + 48); // a12 b12 c12 d12 ... + + v128_t v0 = wasm_v8x16_shuffle(u0, u1, 0,4,8,12,16,20,24,28,1,5,9,13,17,21,25,29); + v128_t v1 = wasm_v8x16_shuffle(u2, u3, 0,4,8,12,16,20,24,28,1,5,9,13,17,21,25,29); + v128_t v2 = wasm_v8x16_shuffle(u0, u1, 2,6,10,14,18,22,26,30,3,7,11,15,19,23,27,31); + v128_t v3 = wasm_v8x16_shuffle(u2, u3, 2,6,10,14,18,22,26,30,3,7,11,15,19,23,27,31); + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + b.val = wasm_v8x16_shuffle(v0, v1, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + c.val = wasm_v8x16_shuffle(v2, v3, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + d.val = wasm_v8x16_shuffle(v2, v3, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 a2 b2 a3 b3 + v128_t v1 = wasm_v128_load(ptr + 8); // a4 b4 a5 b5 a6 b6 a7 b7 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29); // a0 a1 a2 a3 a4 a5 a6 a7 + b.val = wasm_v8x16_shuffle(v0, v1, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31); // b0 b1 ab b3 b4 b5 b6 b7 +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 b1 c1 a2 b2 + v128_t t01 = wasm_v128_load(ptr + 8); // c2 a3 b3 c3 a4 b4 c4 a5 + v128_t t02 = wasm_v128_load(ptr + 16); // b5 c5 a6 b6 c6 a7 b7 c7 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,6,7,12,13,18,19,24,25,30,31,2,3,4,5); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 2,3,8,9,14,15,20,21,26,27,0,1,4,5,6,7); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 4,5,10,11,16,17,22,23,28,29,0,1,2,3,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,26,27); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,16,17,22,23,28,29); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,8,9,18,19,24,25,30,31); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c, v_uint16x8& d) +{ + v128_t u0 = wasm_v128_load(ptr); // a0 b0 c0 d0 a1 b1 c1 d1 + v128_t u1 = wasm_v128_load(ptr + 8); // a2 b2 c2 d2 ... + v128_t u2 = wasm_v128_load(ptr + 16); // a4 b4 c4 d4 ... + v128_t u3 = wasm_v128_load(ptr + 24); // a6 b6 c6 d6 ... + + v128_t v0 = wasm_v8x16_shuffle(u0, u1, 0,1,8,9,16,17,24,25,2,3,10,11,18,19,26,27); // a0 a1 a2 a3 b0 b1 b2 b3 + v128_t v1 = wasm_v8x16_shuffle(u2, u3, 0,1,8,9,16,17,24,25,2,3,10,11,18,19,26,27); // a4 a5 a6 a7 b4 b5 b6 b7 + v128_t v2 = wasm_v8x16_shuffle(u0, u1, 4,5,12,13,20,21,28,29,6,7,14,15,22,23,30,31); // c0 c1 c2 c3 d0 d1 d2 d3 + v128_t v3 = wasm_v8x16_shuffle(u2, u3, 4,5,12,13,20,21,28,29,6,7,14,15,22,23,30,31); // c4 c5 c6 c7 d4 d5 d6 d7 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + b.val = wasm_v8x16_shuffle(v0, v1, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + c.val = wasm_v8x16_shuffle(v2, v3, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + d.val = wasm_v8x16_shuffle(v2, v3, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 + v128_t v1 = wasm_v128_load(ptr + 4); // a2 b2 a3 b3 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); // a0 a1 a2 a3 + b.val = wasm_v8x16_shuffle(v0, v1, 4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); // b0 b1 b2 b3 +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 + v128_t t01 = wasm_v128_load(ptr + 4); // b2 c2 a3 b3 + v128_t t02 = wasm_v128_load(ptr + 8); // c3 a4 b4 c4 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,2,3,12,13,14,15,24,25,26,27,4,5,6,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 4,5,6,7,16,17,18,19,28,29,30,31,0,1,2,3); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 8,9,10,11,20,21,22,23,0,1,2,3,4,5,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,22,23); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,11,24,25,26,27); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,16,17,18,19,28,29,30,31); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 s0(wasm_v128_load(ptr)); // a0 b0 c0 d0 + v_uint32x4 s1(wasm_v128_load(ptr + 4)); // a1 b1 c1 d1 + v_uint32x4 s2(wasm_v128_load(ptr + 8)); // a2 b2 c2 d2 + v_uint32x4 s3(wasm_v128_load(ptr + 12)); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 + v128_t v1 = wasm_v128_load((ptr + 4)); // a2 b2 a3 b3 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); // a0 a1 a2 a3 + b.val = wasm_v8x16_shuffle(v0, v1, 4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); // b0 b1 b2 b3 +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 + v128_t t01 = wasm_v128_load(ptr + 4); // b2 c2 a3 b3 + v128_t t02 = wasm_v128_load(ptr + 8); // c3 a4 b4 c4 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,2,3,12,13,14,15,24,25,26,27,4,5,6,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 4,5,6,7,16,17,18,19,28,29,30,31,0,1,2,3); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 8,9,10,11,20,21,22,23,0,1,2,3,4,5,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,22,23); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,11,24,25,26,27); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,16,17,18,19,28,29,30,31); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c, v_float32x4& d) +{ + v_float32x4 s0(wasm_v128_load(ptr)); // a0 b0 c0 d0 + v_float32x4 s1(wasm_v128_load(ptr + 4)); // a1 b1 c1 d1 + v_float32x4 s2(wasm_v128_load(ptr + 8)); // a2 b2 c2 d2 + v_float32x4 s3(wasm_v128_load(ptr + 12)); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b) +{ + v128_t t0 = wasm_v128_load(ptr); // a0 b0 + v128_t t1 = wasm_v128_load(ptr + 2); // a1 b1 + + a.val = wasm_unpacklo_i64x2(t0, t1); + b.val = wasm_unpackhi_i64x2(t0, t1); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b, v_uint64x2& c) +{ + v128_t t0 = wasm_v128_load(ptr); // a0, b0 + v128_t t1 = wasm_v128_load(ptr + 2); // c0, a1 + v128_t t2 = wasm_v128_load(ptr + 4); // b1, c1 + + a.val = wasm_v8x16_shuffle(t0, t1, 0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31); + b.val = wasm_v8x16_shuffle(t0, t2, 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23); + c.val = wasm_v8x16_shuffle(t1, t2, 0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, + v_uint64x2& b, v_uint64x2& c, v_uint64x2& d) +{ + v128_t t0 = wasm_v128_load(ptr); // a0 b0 + v128_t t1 = wasm_v128_load(ptr + 2); // c0 d0 + v128_t t2 = wasm_v128_load(ptr + 4); // a1 b1 + v128_t t3 = wasm_v128_load(ptr + 6); // c1 d1 + + a.val = wasm_unpacklo_i64x2(t0, t2); + b.val = wasm_unpackhi_i64x2(t0, t2); + c.val = wasm_unpacklo_i64x2(t1, t3); + d.val = wasm_unpackhi_i64x2(t1, t3); +} + +// store interleave + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i8x16(a.val, b.val); + v128_t v1 = wasm_unpackhi_i8x16(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 16, v1); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,16,0,1,17,0,2,18,0,3,19,0,4,20,0,5); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 21,0,6,22,0,7,23,0,8,24,0,9,25,0,10,26); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,11,27,0,12,28,0,13,29,0,14,30,0,15,31,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,16,3,4,17,6,7,18,9,10,19,12,13,20,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,21,2,3,22,5,6,23,8,9,24,11,12,25,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 26,1,2,27,4,5,28,7,8,29,10,11,30,13,14,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 16, t11); + wasm_v128_store(ptr + 32, t12); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, const v_uint8x16& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + v128_t u0 = wasm_unpacklo_i8x16(a.val, c.val); // a0 c0 a1 c1 ... + v128_t u1 = wasm_unpackhi_i8x16(a.val, c.val); // a8 c8 a9 c9 ... + v128_t u2 = wasm_unpacklo_i8x16(b.val, d.val); // b0 d0 b1 d1 ... + v128_t u3 = wasm_unpackhi_i8x16(b.val, d.val); // b8 d8 b9 d9 ... + + v128_t v0 = wasm_unpacklo_i8x16(u0, u2); // a0 b0 c0 d0 ... + v128_t v1 = wasm_unpackhi_i8x16(u0, u2); // a4 b4 c4 d4 ... + v128_t v2 = wasm_unpacklo_i8x16(u1, u3); // a8 b8 c8 d8 ... + v128_t v3 = wasm_unpackhi_i8x16(u1, u3); // a12 b12 c12 d12 ... + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 16, v1); + wasm_v128_store(ptr + 32, v2); + wasm_v128_store(ptr + 48, v3); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i16x8(a.val, b.val); + v128_t v1 = wasm_unpackhi_i16x8(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 8, v1); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, + const v_uint16x8& b, const v_uint16x8& c, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,16,17,0,0,2,3,18,19,0,0,4,5,20,21); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 0,0,6,7,22,23,0,0,8,9,24,25,0,0,10,11); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 26,27,0,0,12,13,28,29,0,0,14,15,30,31,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,16,17,6,7,8,9,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 20,21,2,3,4,5,22,23,8,9,10,11,24,25,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 0,1,26,27,4,5,6,7,28,29,10,11,12,13,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 8, t11); + wasm_v128_store(ptr + 16, t12); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + const v_uint16x8& c, const v_uint16x8& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + v128_t u0 = wasm_unpacklo_i16x8(a.val, c.val); // a0 c0 a1 c1 ... + v128_t u1 = wasm_unpackhi_i16x8(a.val, c.val); // a4 c4 a5 c5 ... + v128_t u2 = wasm_unpacklo_i16x8(b.val, d.val); // b0 d0 b1 d1 ... + v128_t u3 = wasm_unpackhi_i16x8(b.val, d.val); // b4 d4 b5 d5 ... + + v128_t v0 = wasm_unpacklo_i16x8(u0, u2); // a0 b0 c0 d0 ... + v128_t v1 = wasm_unpackhi_i16x8(u0, u2); // a2 b2 c2 d2 ... + v128_t v2 = wasm_unpacklo_i16x8(u1, u3); // a4 b4 c4 d4 ... + v128_t v3 = wasm_unpackhi_i16x8(u1, u3); // a6 b6 c6 d6 ... + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 8, v1); + wasm_v128_store(ptr + 16, v2); + wasm_v128_store(ptr + 24, v3); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i32x4(a.val, b.val); + v128_t v1 = wasm_unpackhi_i32x4(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 4, v1); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,16,17,18,19,0,0,0,0,4,5,6,7); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 20,21,22,23,0,0,0,0,8,9,10,11,24,25,26,27); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,0,0,0,12,13,14,15,28,29,30,31,0,0,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,4,5,6,7,16,17,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,1,2,3,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 24,25,26,27,4,5,6,7,8,9,10,11,28,29,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 4, t11); + wasm_v128_store(ptr + 8, t12); +} + +inline void v_store_interleave(unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v_uint32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + wasm_v128_store(ptr, v0.val); + wasm_v128_store(ptr + 4, v1.val); + wasm_v128_store(ptr + 8, v2.val); + wasm_v128_store(ptr + 12, v3.val); +} + +// 2-channel, float only +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i32x4(a.val, b.val); + v128_t v1 = wasm_unpackhi_i32x4(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 4, v1); +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,16,17,18,19,0,0,0,0,4,5,6,7); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 20,21,22,23,0,0,0,0,8,9,10,11,24,25,26,27); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,0,0,0,12,13,14,15,28,29,30,31,0,0,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,4,5,6,7,16,17,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,1,2,3,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 24,25,26,27,4,5,6,7,8,9,10,11,28,29,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 4, t11); + wasm_v128_store(ptr + 8, t12); +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v_float32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + wasm_v128_store(ptr, v0.val); + wasm_v128_store(ptr + 4, v1.val); + wasm_v128_store(ptr + 8, v2.val); + wasm_v128_store(ptr + 12, v3.val); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i64x2(a.val, b.val); + v128_t v1 = wasm_unpackhi_i64x2(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + v128_t v1 = wasm_v8x16_shuffle(a.val, c.val, 16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t v2 = wasm_v8x16_shuffle(b.val, c.val, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); + wasm_v128_store(ptr + 4, v2); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, const v_uint64x2& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i64x2(a.val, b.val); + v128_t v1 = wasm_unpacklo_i64x2(c.val, d.val); + v128_t v2 = wasm_unpackhi_i64x2(a.val, b.val); + v128_t v3 = wasm_unpackhi_i64x2(c.val, d.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); + wasm_v128_store(ptr + 4, v2); + wasm_v128_store(ptr + 6, v3); +} + +#define OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int8x16, schar, s8, v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int16x8, short, s16, v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int32x4, int, s32, v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int64x2, int64, s64, v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_float64x2, double, f64, v_uint64x2, uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(wasm_f32x4_convert_i32x4(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + fallback::v_float64x2 a_(a); + return fallback::v_cvt_f32(a_); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + fallback::v_float64x2 a_(a), b_(b); + return fallback::v_cvt_f32(a_, b_); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t p = v128_cvti32x4_i64x2(a.val); + return v_float64x2(wasm_f64x2_convert_i64x2(p)); +#else + fallback::v_int32x4 a_(a); + return fallback::v_cvt_f64(a_); +#endif +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t p = v128_cvti32x4_i64x2_high(a.val); + return v_float64x2(wasm_f64x2_convert_i64x2(p)); +#else + fallback::v_int32x4 a_(a); + return fallback::v_cvt_f64_high(a_); +#endif +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + fallback::v_float32x4 a_(a); + return fallback::v_cvt_f64(a_); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + fallback::v_float32x4 a_(a); + return fallback::v_cvt_f64_high(a_); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_float64x2(wasm_f64x2_convert_i64x2(a.val)); +#else + fallback::v_int64x2 a_(a); + return fallback::v_cvt_f64(a_); +#endif +} + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[0]+1], tab[idx[1]], tab[idx[1]+1], tab[idx[2]], tab[idx[2]+1], tab[idx[3]], tab[idx[3]+1], + tab[idx[4]], tab[idx[4]+1], tab[idx[5]], tab[idx[5]+1], tab[idx[6]], tab[idx[6]+1], tab[idx[7]], tab[idx[7]+1]); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[0]+1], tab[idx[0]+2], tab[idx[0]+3], tab[idx[1]], tab[idx[1]+1], tab[idx[1]+2], tab[idx[1]+3], + tab[idx[2]], tab[idx[2]+1], tab[idx[2]+2], tab[idx[2]+3], tab[idx[3]], tab[idx[3]+1], tab[idx[3]+2], tab[idx[3]+3]); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], + tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[0]+1], tab[idx[1]], tab[idx[1]+1], + tab[idx[2]], tab[idx[2]+1], tab[idx[3]], tab[idx[3]+1]); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[0]+1], tab[idx[0]+2], tab[idx[0]+3], + tab[idx[1]], tab[idx[1]+1], tab[idx[1]+2], tab[idx[1]+3]); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short *)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[1]], + tab[idx[2]], tab[idx[3]]); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[0]+1], + tab[idx[1]], tab[idx[1]+1]); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(wasm_v128_load(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int *)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int *)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int *)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(wasm_v128_load(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int *)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_quads((const int *)tab, idx)); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(wasm_v128_load(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + return v_int32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + return v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + return v_float64x2(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)]); +} + +// loads pairs from the table and deinterleaves them, e.g. returns: +// x = (tab[idxvec[0], tab[idxvec[1]], tab[idxvec[2]], tab[idxvec[3]]), +// y = (tab[idxvec[0]+1], tab[idxvec[1]+1], tab[idxvec[2]+1], tab[idxvec[3]+1]) +// note that the indices are float's indices, not the float-pair indices. +// in theory, this function can be used to implement bilinear interpolation, +// when idxvec are the offsets within the image. +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + x = v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); + y = v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)+1]); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + v128_t xy0 = wasm_v128_load(tab + wasm_i32x4_extract_lane(idxvec.val, 0)); + v128_t xy1 = wasm_v128_load(tab + wasm_i32x4_extract_lane(idxvec.val, 1)); + x.val = wasm_unpacklo_i64x2(xy0, xy1); + y.val = wasm_unpacklo_i64x2(xy0, xy1); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15)); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,4,1,5,2,6,3,7,8,12,9,13,10,14,11,15)); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15)); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,8,9,2,3,10,11,4,5,12,13,6,7,14,15)); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + return v_int32x4(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,8,9,10,11,4,5,6,7,12,13,14,15)); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) +{ + return v_float32x4(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,8,9,10,11,4,5,6,7,12,13,14,15)); +} + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,4,5,6,8,9,10,12,13,14,16,16,16,16)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,4,5,8,9,10,11,12,13,14,15,6,7)); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +template +inline typename _Tp::lane_type v_extract_n(const _Tp& a) +{ + return v_rotate_right(a).get0(); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& a) +{ + return v_setall_u32(v_extract_n(a)); +} +template +inline v_int32x4 v_broadcast_element(const v_int32x4& a) +{ + return v_setall_s32(v_extract_n(a)); +} +template +inline v_float32x4 v_broadcast_element(const v_float32x4& a) +{ + return v_setall_f32(v_extract_n(a)); +} + + +////////////// FP16 support /////////////////////////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + return fallback::v_load_expand(ptr); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + fallback::v_float32x4 v_(v); + fallback::v_pack_store(ptr, v_); +} + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/msa_macros.h b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/msa_macros.h new file mode 100644 index 0000000..bd6ddb1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/msa_macros.h @@ -0,0 +1,1558 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_HAL_MSA_MACROS_H +#define OPENCV_CORE_HAL_MSA_MACROS_H + +#ifdef __mips_msa +#include "msa.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define 64 bits vector types */ +typedef signed char v8i8 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned char v8u8 __attribute__ ((vector_size(8), aligned(8))); +typedef short v4i16 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned short v4u16 __attribute__ ((vector_size(8), aligned(8))); +typedef int v2i32 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned int v2u32 __attribute__ ((vector_size(8), aligned(8))); +typedef long long v1i64 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned long long v1u64 __attribute__ ((vector_size(8), aligned(8))); +typedef float v2f32 __attribute__ ((vector_size(8), aligned(8))); +typedef double v1f64 __attribute__ ((vector_size(8), aligned(8))); + + +/* Load values from the given memory a 64-bit vector. */ +#define msa_ld1_s8(__a) (*((v8i8*)(__a))) +#define msa_ld1_s16(__a) (*((v4i16*)(__a))) +#define msa_ld1_s32(__a) (*((v2i32*)(__a))) +#define msa_ld1_s64(__a) (*((v1i64*)(__a))) +#define msa_ld1_u8(__a) (*((v8u8*)(__a))) +#define msa_ld1_u16(__a) (*((v4u16*)(__a))) +#define msa_ld1_u32(__a) (*((v2u32*)(__a))) +#define msa_ld1_u64(__a) (*((v1u64*)(__a))) +#define msa_ld1_f32(__a) (*((v2f32*)(__a))) +#define msa_ld1_f64(__a) (*((v1f64*)(__a))) + +/* Load values from the given memory address to a 128-bit vector */ +#define msa_ld1q_s8(__a) ((v16i8)__builtin_msa_ld_b(__a, 0)) +#define msa_ld1q_s16(__a) ((v8i16)__builtin_msa_ld_h(__a, 0)) +#define msa_ld1q_s32(__a) ((v4i32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_s64(__a) ((v2i64)__builtin_msa_ld_d(__a, 0)) +#define msa_ld1q_u8(__a) ((v16u8)__builtin_msa_ld_b(__a, 0)) +#define msa_ld1q_u16(__a) ((v8u16)__builtin_msa_ld_h(__a, 0)) +#define msa_ld1q_u32(__a) ((v4u32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_u64(__a) ((v2u64)__builtin_msa_ld_d(__a, 0)) +#define msa_ld1q_f32(__a) ((v4f32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_f64(__a) ((v2f64)__builtin_msa_ld_d(__a, 0)) + +/* Store 64bits vector elements values to the given memory address. */ +#define msa_st1_s8(__a, __b) (*((v8i8*)(__a)) = __b) +#define msa_st1_s16(__a, __b) (*((v4i16*)(__a)) = __b) +#define msa_st1_s32(__a, __b) (*((v2i32*)(__a)) = __b) +#define msa_st1_s64(__a, __b) (*((v1i64*)(__a)) = __b) +#define msa_st1_u8(__a, __b) (*((v8u8*)(__a)) = __b) +#define msa_st1_u16(__a, __b) (*((v4u16*)(__a)) = __b) +#define msa_st1_u32(__a, __b) (*((v2u32*)(__a)) = __b) +#define msa_st1_u64(__a, __b) (*((v1u64*)(__a)) = __b) +#define msa_st1_f32(__a, __b) (*((v2f32*)(__a)) = __b) +#define msa_st1_f64(__a, __b) (*((v1f64*)(__a)) = __b) + +/* Store the values of elements in the 128 bits vector __a to the given memory address __a. */ +#define msa_st1q_s8(__a, __b) (__builtin_msa_st_b((v16i8)(__b), __a, 0)) +#define msa_st1q_s16(__a, __b) (__builtin_msa_st_h((v8i16)(__b), __a, 0)) +#define msa_st1q_s32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_s64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) +#define msa_st1q_u8(__a, __b) (__builtin_msa_st_b((v16i8)(__b), __a, 0)) +#define msa_st1q_u16(__a, __b) (__builtin_msa_st_h((v8i16)(__b), __a, 0)) +#define msa_st1q_u32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_u64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) +#define msa_st1q_f32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_f64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) + +/* Store the value of the element with the index __c in vector __a to the given memory address __a. */ +#define msa_st1_lane_s8(__a, __b, __c) (*((int8_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s16(__a, __b, __c) (*((int16_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s32(__a, __b, __c) (*((int32_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s64(__a, __b, __c) (*((int64_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u8(__a, __b, __c) (*((uint8_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u16(__a, __b, __c) (*((uint16_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u32(__a, __b, __c) (*((uint32_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u64(__a, __b, __c) (*((uint64_t*)(__a)) = __b[__c]) +#define msa_st1_lane_f32(__a, __b, __c) (*((float*)(__a)) = __b[__c]) +#define msa_st1_lane_f64(__a, __b, __c) (*((double*)(__a)) = __b[__c]) +#define msa_st1q_lane_s8(__a, __b, __c) (*((int8_t*)(__a)) = (int8_t)__builtin_msa_copy_s_b(__b, __c)) +#define msa_st1q_lane_s16(__a, __b, __c) (*((int16_t*)(__a)) = (int16_t)__builtin_msa_copy_s_h(__b, __c)) +#define msa_st1q_lane_s32(__a, __b, __c) (*((int32_t*)(__a)) = __builtin_msa_copy_s_w(__b, __c)) +#define msa_st1q_lane_s64(__a, __b, __c) (*((int64_t*)(__a)) = __builtin_msa_copy_s_d(__b, __c)) +#define msa_st1q_lane_u8(__a, __b, __c) (*((uint8_t*)(__a)) = (uint8_t)__builtin_msa_copy_u_b((v16i8)(__b), __c)) +#define msa_st1q_lane_u16(__a, __b, __c) (*((uint16_t*)(__a)) = (uint16_t)__builtin_msa_copy_u_h((v8i16)(__b), __c)) +#define msa_st1q_lane_u32(__a, __b, __c) (*((uint32_t*)(__a)) = __builtin_msa_copy_u_w((v4i32)(__b), __c)) +#define msa_st1q_lane_u64(__a, __b, __c) (*((uint64_t*)(__a)) = __builtin_msa_copy_u_d((v2i64)(__b), __c)) +#define msa_st1q_lane_f32(__a, __b, __c) (*((float*)(__a)) = __b[__c]) +#define msa_st1q_lane_f64(__a, __b, __c) (*((double*)(__a)) = __b[__c]) + +/* Duplicate elements for 64-bit doubleword vectors */ +#define msa_dup_n_s8(__a) ((v8i8)__builtin_msa_copy_s_d((v2i64)__builtin_msa_fill_b((int32_t)(__a)), 0)) +#define msa_dup_n_s16(__a) ((v4i16)__builtin_msa_copy_s_d((v2i64)__builtin_msa_fill_h((int32_t)(__a)), 0)) +#define msa_dup_n_s32(__a) ((v2i32){__a, __a}) +#define msa_dup_n_s64(__a) ((v1i64){__a}) +#define msa_dup_n_u8(__a) ((v8u8)__builtin_msa_copy_u_d((v2i64)__builtin_msa_fill_b((int32_t)(__a)), 0)) +#define msa_dup_n_u16(__a) ((v4u16)__builtin_msa_copy_u_d((v2i64)__builtin_msa_fill_h((int32_t)(__a)), 0)) +#define msa_dup_n_u32(__a) ((v2u32){__a, __a}) +#define msa_dup_n_u64(__a) ((v1u64){__a}) +#define msa_dup_n_f32(__a) ((v2f32){__a, __a}) +#define msa_dup_n_f64(__a) ((v1f64){__a}) + +/* Duplicate elements for 128-bit quadword vectors */ +#define msa_dupq_n_s8(__a) (__builtin_msa_fill_b((int32_t)(__a))) +#define msa_dupq_n_s16(__a) (__builtin_msa_fill_h((int32_t)(__a))) +#define msa_dupq_n_s32(__a) (__builtin_msa_fill_w((int32_t)(__a))) +#define msa_dupq_n_s64(__a) (__builtin_msa_fill_d((int64_t)(__a))) +#define msa_dupq_n_u8(__a) ((v16u8)__builtin_msa_fill_b((int32_t)(__a))) +#define msa_dupq_n_u16(__a) ((v8u16)__builtin_msa_fill_h((int32_t)(__a))) +#define msa_dupq_n_u32(__a) ((v4u32)__builtin_msa_fill_w((int32_t)(__a))) +#define msa_dupq_n_u64(__a) ((v2u64)__builtin_msa_fill_d((int64_t)(__a))) +#define msa_dupq_n_f32(__a) ((v4f32){__a, __a, __a, __a}) +#define msa_dupq_n_f64(__a) ((v2f64){__a, __a}) +#define msa_dupq_lane_s8(__a, __b) (__builtin_msa_splat_b(__a, __b)) +#define msa_dupq_lane_s16(__a, __b) (__builtin_msa_splat_h(__a, __b)) +#define msa_dupq_lane_s32(__a, __b) (__builtin_msa_splat_w(__a, __b)) +#define msa_dupq_lane_s64(__a, __b) (__builtin_msa_splat_d(__a, __b)) +#define msa_dupq_lane_u8(__a, __b) ((v16u8)__builtin_msa_splat_b((v16i8)(__a), __b)) +#define msa_dupq_lane_u16(__a, __b) ((v8u16)__builtin_msa_splat_h((v8i16)(__a), __b)) +#define msa_dupq_lane_u32(__a, __b) ((v4u32)__builtin_msa_splat_w((v4i32)(__a), __b)) +#define msa_dupq_lane_u64(__a, __b) ((v2u64)__builtin_msa_splat_d((v2i64)(__a), __b)) + +/* Create a 64 bits vector */ +#define msa_create_s8(__a) ((v8i8)((uint64_t)(__a))) +#define msa_create_s16(__a) ((v4i16)((uint64_t)(__a))) +#define msa_create_s32(__a) ((v2i32)((uint64_t)(__a))) +#define msa_create_s64(__a) ((v1i64)((uint64_t)(__a))) +#define msa_create_u8(__a) ((v8u8)((uint64_t)(__a))) +#define msa_create_u16(__a) ((v4u16)((uint64_t)(__a))) +#define msa_create_u32(__a) ((v2u32)((uint64_t)(__a))) +#define msa_create_u64(__a) ((v1u64)((uint64_t)(__a))) +#define msa_create_f32(__a) ((v2f32)((uint64_t)(__a))) +#define msa_create_f64(__a) ((v1f64)((uint64_t)(__a))) + +/* Sign extends or zero extends each element in a 64 bits vector to twice its original length, and places the results in a 128 bits vector. */ +/*Transform v8i8 to v8i16*/ +#define msa_movl_s8(__a) \ +((v8i16){(__a)[0], (__a)[1], (__a)[2], (__a)[3], \ + (__a)[4], (__a)[5], (__a)[6], (__a)[7]}) + +/*Transform v8u8 to v8u16*/ +#define msa_movl_u8(__a) \ +((v8u16){(__a)[0], (__a)[1], (__a)[2], (__a)[3], \ + (__a)[4], (__a)[5], (__a)[6], (__a)[7]}) + +/*Transform v4i16 to v8i16*/ +#define msa_movl_s16(__a) ((v4i32){(__a)[0], (__a)[1], (__a)[2], (__a)[3]}) + +/*Transform v2i32 to v4i32*/ +#define msa_movl_s32(__a) ((v2i64){(__a)[0], (__a)[1]}) + +/*Transform v4u16 to v8u16*/ +#define msa_movl_u16(__a) ((v4u32){(__a)[0], (__a)[1], (__a)[2], (__a)[3]}) + +/*Transform v2u32 to v4u32*/ +#define msa_movl_u32(__a) ((v2u64){(__a)[0], (__a)[1]}) + +/* Copies the least significant half of each element of a 128 bits vector into the corresponding elements of a 64 bits vector. */ +#define msa_movn_s16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)(__a)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_s32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_s64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)(__a)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* qmovn */ +#define msa_qmovn_s16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_s_h((v8i16)(__a), 7)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_s32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_s_w((v4i32)(__a), 15)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_s64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_s_d((v2i64)(__a), 31)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)(__a), 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)(__a), 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)(__a), 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* qmovun */ +#define msa_qmovun_s16(__a) \ +({ \ + v8i16 __d = __builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qmovun_s32(__a) \ +({ \ + v4i32 __d = __builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qmovun_s64(__a) \ +({ \ + v2i64 __d = __builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, and places the results in a 64 bits vector. */ +#define msa_shrn_n_s16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srai_h((v8i16)(__a), (int)(__b))); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_s32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srai_w((v4i32)(__a), (int)(__b))); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_s64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srai_d((v2i64)(__a), (int)(__b))); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srli_h((v8i16)(__a), (int)(__b))); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srli_w((v4i32)(__a), (int)(__b))); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srli_d((v2i64)(__a), (int)(__b))); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, and places the results in a 64 bits vector. */ +#define msa_rshrn_n_s16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srari_h((v8i16)(__a), (int)__b)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_s32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srari_w((v4i32)(__a), (int)__b)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_s64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srari_d((v2i64)(__a), (int)__b)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srlri_h((v8i16)(__a), (int)__b)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srlri_w((v4i32)(__a), (int)__b)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srlri_d((v2i64)(__a), (int)__b)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, saturate the results and them in a 64 bits vector. */ +#define msa_qrshrn_n_s16(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__a), (int)(__b)), 7); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__d); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_s32(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__a), (int)(__b)), 15); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__d); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_s64(__a, __b) \ +({ \ + v2i64 __d = __builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__a), (int)(__b)), 31); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__d); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u16(__a, __b) \ +({ \ + v8u16 __d = __builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__a), (int)(__b)), 7); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__d); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u32(__a, __b) \ +({ \ + v4u32 __d = __builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__a), (int)(__b)), 15); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__d); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u64(__a, __b) \ +({ \ + v2u64 __d = __builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__a), (int)(__b)), 31); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__d); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, saturate the results and them in a 64 bits vector. + Input is signed and output is unsigned. */ +#define msa_qrshrun_n_s16(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)), (int)(__b)); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrun_n_s32(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)), (int)(__b)); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrun_n_s64(__a, __b) \ +({ \ + v2i64 __d = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)), (int)(__b)); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* pack */ +#define msa_pack_s16(__a, __b) (__builtin_msa_pckev_b((v16i8)(__b), (v16i8)(__a))) +#define msa_pack_s32(__a, __b) (__builtin_msa_pckev_h((v8i16)(__b), (v8i16)(__a))) +#define msa_pack_s64(__a, __b) (__builtin_msa_pckev_w((v4i32)(__b), (v4i32)(__a))) +#define msa_pack_u16(__a, __b) ((v16u8)__builtin_msa_pckev_b((v16i8)(__b), (v16i8)(__a))) +#define msa_pack_u32(__a, __b) ((v8u16)__builtin_msa_pckev_h((v8i16)(__b), (v8i16)(__a))) +#define msa_pack_u64(__a, __b) ((v4u32)__builtin_msa_pckev_w((v4i32)(__b), (v4i32)(__a))) + +/* qpack */ +#define msa_qpack_s16(__a, __b) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_s_h((v8i16)(__b), 7), (v16i8)__builtin_msa_sat_s_h((v8i16)(__a), 7))) +#define msa_qpack_s32(__a, __b) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_s_w((v4i32)(__b), 15), (v8i16)__builtin_msa_sat_s_w((v4i32)(__a), 15))) +#define msa_qpack_s64(__a, __b) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_s_d((v2i64)(__b), 31), (v4i32)__builtin_msa_sat_s_d((v2i64)(__a), 31))) +#define msa_qpack_u16(__a, __b) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)(__b), 7), (v16i8)__builtin_msa_sat_u_h((v8u16)(__a), 7))) +#define msa_qpack_u32(__a, __b) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)(__b), 15), (v8i16)__builtin_msa_sat_u_w((v4u32)(__a), 15))) +#define msa_qpack_u64(__a, __b) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)(__b), 31), (v4i32)__builtin_msa_sat_u_d((v2u64)(__a), 31))) + +/* qpacku */ +#define msa_qpacku_s16(__a, __b) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__b))), 7), \ + (v16i8)__builtin_msa_sat_u_h((v8u16)(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a))), 7))) +#define msa_qpacku_s32(__a, __b) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__b))), 15), \ + (v8i16)__builtin_msa_sat_u_w((v4u32)(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a))), 15))) +#define msa_qpacku_s64(__a, __b) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__b))), 31), \ + (v4i32)__builtin_msa_sat_u_d((v2u64)(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a))), 31))) + +/* packr */ +#define msa_packr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_srai_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srai_h((v8i16)(__a), (int)(__c)))) +#define msa_packr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_srai_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srai_w((v4i32)(__a), (int)(__c)))) +#define msa_packr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_srai_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srai_d((v2i64)(__a), (int)(__c)))) +#define msa_packr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_srli_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srli_h((v8i16)(__a), (int)(__c)))) +#define msa_packr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_srli_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srli_w((v4i32)(__a), (int)(__c)))) +#define msa_packr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_srli_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srli_d((v2i64)(__a), (int)(__c)))) + +/* rpackr */ +#define msa_rpackr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_srari_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srari_h((v8i16)(__a), (int)(__c)))) +#define msa_rpackr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_srari_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srari_w((v4i32)(__a), (int)(__c)))) +#define msa_rpackr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_srari_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srari_d((v2i64)(__a), (int)(__c)))) +#define msa_rpackr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_srlri_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srlri_h((v8i16)(__a), (int)(__c)))) +#define msa_rpackr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_srlri_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srlri_w((v4i32)(__a), (int)(__c)))) +#define msa_rpackr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_srlri_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srlri_d((v2i64)(__a), (int)(__c)))) + +/* qrpackr */ +#define msa_qrpackr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__b), (int)(__c)), 7), \ + (v16i8)__builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__a), (int)(__c)), 7))) +#define msa_qrpackr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__b), (int)(__c)), 15), \ + (v8i16)__builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__a), (int)(__c)), 15))) +#define msa_qrpackr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__b), (int)(__c)), 31), \ + (v4i32)__builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__a), (int)(__c)), 31))) +#define msa_qrpackr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__b), (int)(__c)), 7), \ + (v16i8)__builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__a), (int)(__c)), 7))) +#define msa_qrpackr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__b), (int)(__c)), 15), \ + (v8i16)__builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__a), (int)(__c)), 15))) +#define msa_qrpackr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__b), (int)(__c)), 31), \ + (v4i32)__builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__a), (int)(__c)), 31))) + +/* qrpackru */ +#define msa_qrpackru_s16(__a, __b, __c) \ +({ \ + v8i16 __d = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)), (int)(__c)); \ + v8i16 __e = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__b)), (int)(__c)); \ + (v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)__e, 7), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ +}) + +#define msa_qrpackru_s32(__a, __b, __c) \ +({ \ + v4i32 __d = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)), (int)(__c)); \ + v4i32 __e = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__b)), (int)(__c)); \ + (v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)__e, 15), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ +}) + +#define msa_qrpackru_s64(__a, __b, __c) \ +({ \ + v2i64 __d = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)), (int)(__c)); \ + v2i64 __e = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__b)), (int)(__c)); \ + (v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)__e, 31), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ +}) + +/* Minimum values between corresponding elements in the two vectors are written to the returned vector. */ +#define msa_minq_s8(__a, __b) (__builtin_msa_min_s_b(__a, __b)) +#define msa_minq_s16(__a, __b) (__builtin_msa_min_s_h(__a, __b)) +#define msa_minq_s32(__a, __b) (__builtin_msa_min_s_w(__a, __b)) +#define msa_minq_s64(__a, __b) (__builtin_msa_min_s_d(__a, __b)) +#define msa_minq_u8(__a, __b) ((v16u8)__builtin_msa_min_u_b(__a, __b)) +#define msa_minq_u16(__a, __b) ((v8u16)__builtin_msa_min_u_h(__a, __b)) +#define msa_minq_u32(__a, __b) ((v4u32)__builtin_msa_min_u_w(__a, __b)) +#define msa_minq_u64(__a, __b) ((v2u64)__builtin_msa_min_u_d(__a, __b)) +#define msa_minq_f32(__a, __b) (__builtin_msa_fmin_w(__a, __b)) +#define msa_minq_f64(__a, __b) (__builtin_msa_fmin_d(__a, __b)) + +/* Maximum values between corresponding elements in the two vectors are written to the returned vector. */ +#define msa_maxq_s8(__a, __b) (__builtin_msa_max_s_b(__a, __b)) +#define msa_maxq_s16(__a, __b) (__builtin_msa_max_s_h(__a, __b)) +#define msa_maxq_s32(__a, __b) (__builtin_msa_max_s_w(__a, __b)) +#define msa_maxq_s64(__a, __b) (__builtin_msa_max_s_d(__a, __b)) +#define msa_maxq_u8(__a, __b) ((v16u8)__builtin_msa_max_u_b(__a, __b)) +#define msa_maxq_u16(__a, __b) ((v8u16)__builtin_msa_max_u_h(__a, __b)) +#define msa_maxq_u32(__a, __b) ((v4u32)__builtin_msa_max_u_w(__a, __b)) +#define msa_maxq_u64(__a, __b) ((v2u64)__builtin_msa_max_u_d(__a, __b)) +#define msa_maxq_f32(__a, __b) (__builtin_msa_fmax_w(__a, __b)) +#define msa_maxq_f64(__a, __b) (__builtin_msa_fmax_d(__a, __b)) + +/* Vector type reinterpretion */ +#define MSA_TPV_REINTERPRET(_Tpv, Vec) ((_Tpv)(Vec)) + +/* Add the odd elements in vector __a with the even elements in vector __b to double width elements in the returned vector. */ +/* v8i16 msa_hadd_s16 ((v16i8)__a, (v16i8)__b) */ +#define msa_hadd_s16(__a, __b) (__builtin_msa_hadd_s_h((v16i8)(__a), (v16i8)(__b))) +/* v4i32 msa_hadd_s32 ((v8i16)__a, (v8i16)__b) */ +#define msa_hadd_s32(__a, __b) (__builtin_msa_hadd_s_w((v8i16)(__a), (v8i16)(__b))) +/* v2i64 msa_hadd_s64 ((v4i32)__a, (v4i32)__b) */ +#define msa_hadd_s64(__a, __b) (__builtin_msa_hadd_s_d((v4i32)(__a), (v4i32)(__b))) + +/* Copy even elements in __a to the left half and even elements in __b to the right half and return the result vector. */ +#define msa_pckev_s8(__a, __b) (__builtin_msa_pckev_b((v16i8)(__a), (v16i8)(__b))) +#define msa_pckev_s16(__a, __b) (__builtin_msa_pckev_h((v8i16)(__a), (v8i16)(__b))) +#define msa_pckev_s32(__a, __b) (__builtin_msa_pckev_w((v4i32)(__a), (v4i32)(__b))) +#define msa_pckev_s64(__a, __b) (__builtin_msa_pckev_d((v2i64)(__a), (v2i64)(__b))) + +/* Copy even elements in __a to the left half and even elements in __b to the right half and return the result vector. */ +#define msa_pckod_s8(__a, __b) (__builtin_msa_pckod_b((v16i8)(__a), (v16i8)(__b))) +#define msa_pckod_s16(__a, __b) (__builtin_msa_pckod_h((v8i16)(__a), (v8i16)(__b))) +#define msa_pckod_s32(__a, __b) (__builtin_msa_pckod_w((v4i32)(__a), (v4i32)(__b))) +#define msa_pckod_s64(__a, __b) (__builtin_msa_pckod_d((v2i64)(__a), (v2i64)(__b))) + +#ifdef _MIPSEB +#define LANE_IMM0_1(x) (0b1 - ((x) & 0b1)) +#define LANE_IMM0_3(x) (0b11 - ((x) & 0b11)) +#define LANE_IMM0_7(x) (0b111 - ((x) & 0b111)) +#define LANE_IMM0_15(x) (0b1111 - ((x) & 0b1111)) +#else +#define LANE_IMM0_1(x) ((x) & 0b1) +#define LANE_IMM0_3(x) ((x) & 0b11) +#define LANE_IMM0_7(x) ((x) & 0b111) +#define LANE_IMM0_15(x) ((x) & 0b1111) +#endif + +#define msa_get_lane_u8(__a, __b) ((uint8_t)(__a)[LANE_IMM0_7(__b)]) +#define msa_get_lane_s8(__a, __b) ((int8_t)(__a)[LANE_IMM0_7(__b)]) +#define msa_get_lane_u16(__a, __b) ((uint16_t)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_s16(__a, __b) ((int16_t)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_u32(__a, __b) ((uint32_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_s32(__a, __b) ((int32_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_f32(__a, __b) ((float)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_s64(__a, __b) ((int64_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_u64(__a, __b) ((uint64_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_f64(__a, __b) ((double)(__a)[LANE_IMM0_1(__b)]) +#define msa_getq_lane_u8(__a, imm0_15) ((uint8_t)__builtin_msa_copy_u_b((v16i8)(__a), imm0_15)) +#define msa_getq_lane_s8(__a, imm0_15) ((int8_t)__builtin_msa_copy_s_b(__a, imm0_15)) +#define msa_getq_lane_u16(__a, imm0_7) ((uint16_t)__builtin_msa_copy_u_h((v8i16)(__a), imm0_7)) +#define msa_getq_lane_s16(__a, imm0_7) ((int16_t)__builtin_msa_copy_s_h(__a, imm0_7)) +#define msa_getq_lane_u32(__a, imm0_3) __builtin_msa_copy_u_w((v4i32)(__a), imm0_3) +#define msa_getq_lane_s32 __builtin_msa_copy_s_w +#define msa_getq_lane_f32(__a, __b) ((float)(__a)[LANE_IMM0_3(__b)]) +#define msa_getq_lane_f64(__a, __b) ((double)(__a)[LANE_IMM0_1(__b)]) +#if (__mips == 64) +#define msa_getq_lane_u64(__a, imm0_1) __builtin_msa_copy_u_d((v2i64)(__a), imm0_1) +#define msa_getq_lane_s64 __builtin_msa_copy_s_d +#else +#define msa_getq_lane_u64(__a, imm0_1) ((uint64_t)(__a)[LANE_IMM0_1(imm0_1)]) +#define msa_getq_lane_s64(__a, imm0_1) ((int64_t)(__a)[LANE_IMM0_1(imm0_1)]) +#endif + +/* combine */ +#if (__mips == 64) +#define __COMBINE_64_64(__TYPE, a, b) ((__TYPE)((v2u64){((v1u64)(a))[0], ((v1u64)(b))[0]})) +#else +#define __COMBINE_64_64(__TYPE, a, b) ((__TYPE)((v4u32){((v2u32)(a))[0], ((v2u32)(a))[1], \ + ((v2u32)(b))[0], ((v2u32)(b))[1]})) +#endif + +/* v16i8 msa_combine_s8 (v8i8 __a, v8i8 __b) */ +#define msa_combine_s8(__a, __b) __COMBINE_64_64(v16i8, __a, __b) + +/* v8i16 msa_combine_s16(v4i16 __a, v4i16 __b) */ +#define msa_combine_s16(__a, __b) __COMBINE_64_64(v8i16, __a, __b) + +/* v4i32 msa_combine_s32(v2i32 __a, v2i32 __b) */ +#define msa_combine_s32(__a, __b) __COMBINE_64_64(v4i32, __a, __b) + +/* v2i64 msa_combine_s64(v1i64 __a, v1i64 __b) */ +#define msa_combine_s64(__a, __b) __COMBINE_64_64(v2i64, __a, __b) + +/* v4f32 msa_combine_f32(v2f32 __a, v2f32 __b) */ +#define msa_combine_f32(__a, __b) __COMBINE_64_64(v4f32, __a, __b) + +/* v16u8 msa_combine_u8(v8u8 __a, v8u8 __b) */ +#define msa_combine_u8(__a, __b) __COMBINE_64_64(v16u8, __a, __b) + +/* v8u16 msa_combine_u16(v4u16 __a, v4u16 __b) */ +#define msa_combine_u16(__a, __b) __COMBINE_64_64(v8u16, __a, __b) + +/* v4u32 msa_combine_u32(v2u32 __a, v2u32 __b) */ +#define msa_combine_u32(__a, __b) __COMBINE_64_64(v4u32, __a, __b) + +/* v2u64 msa_combine_u64(v1u64 __a, v1u64 __b) */ +#define msa_combine_u64(__a, __b) __COMBINE_64_64(v2u64, __a, __b) + +/* v2f64 msa_combine_f64(v1f64 __a, v1f64 __b) */ +#define msa_combine_f64(__a, __b) __COMBINE_64_64(v2f64, __a, __b) + +/* get_low, get_high */ +#if (__mips == 64) +#define __GET_LOW(__TYPE, a) ((__TYPE)((v1u64)(__builtin_msa_copy_u_d((v2i64)(a), 0)))) +#define __GET_HIGH(__TYPE, a) ((__TYPE)((v1u64)(__builtin_msa_copy_u_d((v2i64)(a), 1)))) +#else +#define __GET_LOW(__TYPE, a) ((__TYPE)(((v2u64)(a))[0])) +#define __GET_HIGH(__TYPE, a) ((__TYPE)(((v2u64)(a))[1])) +#endif + +/* v8i8 msa_get_low_s8(v16i8 __a) */ +#define msa_get_low_s8(__a) __GET_LOW(v8i8, __a) + +/* v4i16 msa_get_low_s16(v8i16 __a) */ +#define msa_get_low_s16(__a) __GET_LOW(v4i16, __a) + +/* v2i32 msa_get_low_s32(v4i32 __a) */ +#define msa_get_low_s32(__a) __GET_LOW(v2i32, __a) + +/* v1i64 msa_get_low_s64(v2i64 __a) */ +#define msa_get_low_s64(__a) __GET_LOW(v1i64, __a) + +/* v8u8 msa_get_low_u8(v16u8 __a) */ +#define msa_get_low_u8(__a) __GET_LOW(v8u8, __a) + +/* v4u16 msa_get_low_u16(v8u16 __a) */ +#define msa_get_low_u16(__a) __GET_LOW(v4u16, __a) + +/* v2u32 msa_get_low_u32(v4u32 __a) */ +#define msa_get_low_u32(__a) __GET_LOW(v2u32, __a) + +/* v1u64 msa_get_low_u64(v2u64 __a) */ +#define msa_get_low_u64(__a) __GET_LOW(v1u64, __a) + +/* v2f32 msa_get_low_f32(v4f32 __a) */ +#define msa_get_low_f32(__a) __GET_LOW(v2f32, __a) + +/* v1f64 msa_get_low_f64(v2f64 __a) */ +#define msa_get_low_f64(__a) __GET_LOW(v1f64, __a) + +/* v8i8 msa_get_high_s8(v16i8 __a) */ +#define msa_get_high_s8(__a) __GET_HIGH(v8i8, __a) + +/* v4i16 msa_get_high_s16(v8i16 __a) */ +#define msa_get_high_s16(__a) __GET_HIGH(v4i16, __a) + +/* v2i32 msa_get_high_s32(v4i32 __a) */ +#define msa_get_high_s32(__a) __GET_HIGH(v2i32, __a) + +/* v1i64 msa_get_high_s64(v2i64 __a) */ +#define msa_get_high_s64(__a) __GET_HIGH(v1i64, __a) + +/* v8u8 msa_get_high_u8(v16u8 __a) */ +#define msa_get_high_u8(__a) __GET_HIGH(v8u8, __a) + +/* v4u16 msa_get_high_u16(v8u16 __a) */ +#define msa_get_high_u16(__a) __GET_HIGH(v4u16, __a) + +/* v2u32 msa_get_high_u32(v4u32 __a) */ +#define msa_get_high_u32(__a) __GET_HIGH(v2u32, __a) + +/* v1u64 msa_get_high_u64(v2u64 __a) */ +#define msa_get_high_u64(__a) __GET_HIGH(v1u64, __a) + +/* v2f32 msa_get_high_f32(v4f32 __a) */ +#define msa_get_high_f32(__a) __GET_HIGH(v2f32, __a) + +/* v1f64 msa_get_high_f64(v2f64 __a) */ +#define msa_get_high_f64(__a) __GET_HIGH(v1f64, __a) + +/* ri = ai * b[lane] */ +/* v4f32 msa_mulq_lane_f32(v4f32 __a, v4f32 __b, const int __lane) */ +#define msa_mulq_lane_f32(__a, __b, __lane) ((__a) * msa_getq_lane_f32(__b, __lane)) + +/* ri = ai + bi * c[lane] */ +/* v4f32 msa_mlaq_lane_f32(v4f32 __a, v4f32 __b, v4f32 __c, const int __lane) */ +#define msa_mlaq_lane_f32(__a, __b, __c, __lane) ((__a) + ((__b) * msa_getq_lane_f32(__c, __lane))) + +/* uint16_t msa_sum_u16(v8u16 __a)*/ +#define msa_sum_u16(__a) \ +({ \ + v4u32 _b; \ + v2u64 _c; \ + _b = __builtin_msa_hadd_u_w(__a, __a); \ + _c = __builtin_msa_hadd_u_d(_b, _b); \ + (uint16_t)(_c[0] + _c[1]); \ +}) + +/* int16_t msa_sum_s16(v8i16 __a) */ +#define msa_sum_s16(__a) \ +({ \ + v4i32 _b; \ + v2i64 _c; \ + _b = __builtin_msa_hadd_s_w(__a, __a); \ + _c = __builtin_msa_hadd_s_d(_b, _b); \ + (int16_t)(_c[0] + _c[1]); \ +}) + + +/* uint32_t msa_sum_u32(v4u32 __a)*/ +#define msa_sum_u32(__a) \ +({ \ + v2u64 _b; \ + _b = __builtin_msa_hadd_u_d(__a, __a); \ + (uint32_t)(_b[0] + _b[1]); \ +}) + +/* int32_t msa_sum_s32(v4i32 __a)*/ +#define msa_sum_s32(__a) \ +({ \ + v2i64 _b; \ + _b = __builtin_msa_hadd_s_d(__a, __a); \ + (int32_t)(_b[0] + _b[1]); \ +}) + +/* uint8_t msa_sum_u8(v16u8 __a)*/ +#define msa_sum_u8(__a) \ +({ \ + v8u16 _b16; \ + v4u32 _c32; \ + _b16 = __builtin_msa_hadd_u_h(__a, __a); \ + _c32 = __builtin_msa_hadd_u_w(_b16, _b16); \ + (uint8_t)msa_sum_u32(_c32); \ +}) + +/* int8_t msa_sum_s8(v16s8 __a)*/ +#define msa_sum_s8(__a) \ +({ \ + v8i16 _b16; \ + v4i32 _c32; \ + _b16 = __builtin_msa_hadd_s_h(__a, __a); \ + _c32 = __builtin_msa_hadd_s_w(_b16, _b16); \ + (int8_t)msa_sum_s32(_c32); \ +}) + +/* float msa_sum_f32(v4f32 __a)*/ +#define msa_sum_f32(__a) ((__a)[0] + (__a)[1] + (__a)[2] + (__a)[3]) + +/* v8u16 msa_paddlq_u8(v16u8 __a) */ +#define msa_paddlq_u8(__a) (__builtin_msa_hadd_u_h(__a, __a)) + +/* v8i16 msa_paddlq_s8(v16i8 __a) */ +#define msa_paddlq_s8(__a) (__builtin_msa_hadd_s_h(__a, __a)) + +/* v4u32 msa_paddlq_u16 (v8u16 __a)*/ +#define msa_paddlq_u16(__a) (__builtin_msa_hadd_u_w(__a, __a)) + +/* v4i32 msa_paddlq_s16 (v8i16 __a)*/ +#define msa_paddlq_s16(__a) (__builtin_msa_hadd_s_w(__a, __a)) + +/* v2u64 msa_paddlq_u32(v4u32 __a) */ +#define msa_paddlq_u32(__a) (__builtin_msa_hadd_u_d(__a, __a)) + +/* v2i64 msa_paddlq_s32(v4i32 __a) */ +#define msa_paddlq_s32(__a) (__builtin_msa_hadd_s_d(__a, __a)) + +#define V8U8_2_V8U16(x) {(uint16_t)x[0], (uint16_t)x[1], (uint16_t)x[2], (uint16_t)x[3], \ + (uint16_t)x[4], (uint16_t)x[5], (uint16_t)x[6], (uint16_t)x[7]} +#define V8U8_2_V8I16(x) {(int16_t)x[0], (int16_t)x[1], (int16_t)x[2], (int16_t)x[3], \ + (int16_t)x[4], (int16_t)x[5], (int16_t)x[6], (int16_t)x[7]} +#define V8I8_2_V8I16(x) {(int16_t)x[0], (int16_t)x[1], (int16_t)x[2], (int16_t)x[3], \ + (int16_t)x[4], (int16_t)x[5], (int16_t)x[6], (int16_t)x[7]} +#define V4U16_2_V4U32(x) {(uint32_t)x[0], (uint32_t)x[1], (uint32_t)x[2], (uint32_t)x[3]} +#define V4U16_2_V4I32(x) {(int32_t)x[0], (int32_t)x[1], (int32_t)x[2], (int32_t)x[3]} +#define V4I16_2_V4I32(x) {(int32_t)x[0], (int32_t)x[1], (int32_t)x[2], (int32_t)x[3]} +#define V2U32_2_V2U64(x) {(uint64_t)x[0], (uint64_t)x[1]} +#define V2U32_2_V2I64(x) {(int64_t)x[0], (int64_t)x[1]} + +/* v8u16 msa_mull_u8(v8u8 __a, v8u8 __b) */ +#define msa_mull_u8(__a, __b) ((v8u16)__builtin_msa_mulv_h((v8i16)V8U8_2_V8I16(__a), (v8i16)V8U8_2_V8I16(__b))) + +/* v8i16 msa_mull_s8(v8i8 __a, v8i8 __b)*/ +#define msa_mull_s8(__a, __b) (__builtin_msa_mulv_h((v8i16)V8I8_2_V8I16(__a), (v8i16)V8I8_2_V8I16(__b))) + +/* v4u32 msa_mull_u16(v4u16 __a, v4u16 __b) */ +#define msa_mull_u16(__a, __b) ((v4u32)__builtin_msa_mulv_w((v4i32)V4U16_2_V4I32(__a), (v4i32)V4U16_2_V4I32(__b))) + +/* v4i32 msa_mull_s16(v4i16 __a, v4i16 __b) */ +#define msa_mull_s16(__a, __b) (__builtin_msa_mulv_w((v4i32)V4I16_2_V4I32(__a), (v4i32)V4I16_2_V4I32(__b))) + +/* v2u64 msa_mull_u32(v2u32 __a, v2u32 __b) */ +#define msa_mull_u32(__a, __b) ((v2u64)__builtin_msa_mulv_d((v2i64)V2U32_2_V2I64(__a), (v2i64)V2U32_2_V2I64(__b))) + +/* bitwise and: __builtin_msa_and_v */ +#define msa_andq_u8(__a, __b) ((v16u8)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s8(__a, __b) ((v16i8)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u16(__a, __b) ((v8u16)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s16(__a, __b) ((v8i16)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u32(__a, __b) ((v4u32)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s32(__a, __b) ((v4i32)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u64(__a, __b) ((v2u64)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s64(__a, __b) ((v2i64)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise or: __builtin_msa_or_v */ +#define msa_orrq_u8(__a, __b) ((v16u8)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s8(__a, __b) ((v16i8)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u16(__a, __b) ((v8u16)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s16(__a, __b) ((v8i16)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u32(__a, __b) ((v4u32)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s32(__a, __b) ((v4i32)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u64(__a, __b) ((v2u64)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s64(__a, __b) ((v2i64)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise xor: __builtin_msa_xor_v */ +#define msa_eorq_u8(__a, __b) ((v16u8)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s8(__a, __b) ((v16i8)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u16(__a, __b) ((v8u16)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s16(__a, __b) ((v8i16)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u32(__a, __b) ((v4u32)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s32(__a, __b) ((v4i32)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u64(__a, __b) ((v2u64)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s64(__a, __b) ((v2i64)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise not: v16u8 __builtin_msa_xori_b (v16u8, 0xff) */ +#define msa_mvnq_u8(__a) ((v16u8)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s8(__a) ((v16i8)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u16(__a) ((v8u16)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s16(__a) ((v8i16)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u32(__a) ((v4u32)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s32(__a) ((v4i32)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u64(__a) ((v2u64)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s64(__a) ((v2i64)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) + +/* compare equal: ceq -> ri = ai == bi ? 1...1:0...0 */ +#define msa_ceqq_u8(__a, __b) ((v16u8)__builtin_msa_ceq_b((v16i8)(__a), (v16i8)(__b))) +#define msa_ceqq_s8(__a, __b) ((v16u8)__builtin_msa_ceq_b((v16i8)(__a), (v16i8)(__b))) +#define msa_ceqq_u16(__a, __b) ((v8u16)__builtin_msa_ceq_h((v8i16)(__a), (v8i16)(__b))) +#define msa_ceqq_s16(__a, __b) ((v8u16)__builtin_msa_ceq_h((v8i16)(__a), (v8i16)(__b))) +#define msa_ceqq_u32(__a, __b) ((v4u32)__builtin_msa_ceq_w((v4i32)(__a), (v4i32)(__b))) +#define msa_ceqq_s32(__a, __b) ((v4u32)__builtin_msa_ceq_w((v4i32)(__a), (v4i32)(__b))) +#define msa_ceqq_f32(__a, __b) ((v4u32)__builtin_msa_fceq_w((v4f32)(__a), (v4f32)(__b))) +#define msa_ceqq_u64(__a, __b) ((v2u64)__builtin_msa_ceq_d((v2i64)(__a), (v2i64)(__b))) +#define msa_ceqq_s64(__a, __b) ((v2u64)__builtin_msa_ceq_d((v2i64)(__a), (v2i64)(__b))) +#define msa_ceqq_f64(__a, __b) ((v2u64)__builtin_msa_fceq_d((v2f64)(__a), (v2f64)(__b))) + +/* Compare less-than: clt -> ri = ai < bi ? 1...1:0...0 */ +#define msa_cltq_u8(__a, __b) ((v16u8)__builtin_msa_clt_u_b((v16u8)(__a), (v16u8)(__b))) +#define msa_cltq_s8(__a, __b) ((v16u8)__builtin_msa_clt_s_b((v16i8)(__a), (v16i8)(__b))) +#define msa_cltq_u16(__a, __b) ((v8u16)__builtin_msa_clt_u_h((v8u16)(__a), (v8u16)(__b))) +#define msa_cltq_s16(__a, __b) ((v8u16)__builtin_msa_clt_s_h((v8i16)(__a), (v8i16)(__b))) +#define msa_cltq_u32(__a, __b) ((v4u32)__builtin_msa_clt_u_w((v4u32)(__a), (v4u32)(__b))) +#define msa_cltq_s32(__a, __b) ((v4u32)__builtin_msa_clt_s_w((v4i32)(__a), (v4i32)(__b))) +#define msa_cltq_f32(__a, __b) ((v4u32)__builtin_msa_fclt_w((v4f32)(__a), (v4f32)(__b))) +#define msa_cltq_u64(__a, __b) ((v2u64)__builtin_msa_clt_u_d((v2u64)(__a), (v2u64)(__b))) +#define msa_cltq_s64(__a, __b) ((v2u64)__builtin_msa_clt_s_d((v2i64)(__a), (v2i64)(__b))) +#define msa_cltq_f64(__a, __b) ((v2u64)__builtin_msa_fclt_d((v2f64)(__a), (v2f64)(__b))) + +/* compare greater-than: cgt -> ri = ai > bi ? 1...1:0...0 */ +#define msa_cgtq_u8(__a, __b) ((v16u8)__builtin_msa_clt_u_b((v16u8)(__b), (v16u8)(__a))) +#define msa_cgtq_s8(__a, __b) ((v16u8)__builtin_msa_clt_s_b((v16i8)(__b), (v16i8)(__a))) +#define msa_cgtq_u16(__a, __b) ((v8u16)__builtin_msa_clt_u_h((v8u16)(__b), (v8u16)(__a))) +#define msa_cgtq_s16(__a, __b) ((v8u16)__builtin_msa_clt_s_h((v8i16)(__b), (v8i16)(__a))) +#define msa_cgtq_u32(__a, __b) ((v4u32)__builtin_msa_clt_u_w((v4u32)(__b), (v4u32)(__a))) +#define msa_cgtq_s32(__a, __b) ((v4u32)__builtin_msa_clt_s_w((v4i32)(__b), (v4i32)(__a))) +#define msa_cgtq_f32(__a, __b) ((v4u32)__builtin_msa_fclt_w((v4f32)(__b), (v4f32)(__a))) +#define msa_cgtq_u64(__a, __b) ((v2u64)__builtin_msa_clt_u_d((v2u64)(__b), (v2u64)(__a))) +#define msa_cgtq_s64(__a, __b) ((v2u64)__builtin_msa_clt_s_d((v2i64)(__b), (v2i64)(__a))) +#define msa_cgtq_f64(__a, __b) ((v2u64)__builtin_msa_fclt_d((v2f64)(__b), (v2f64)(__a))) + +/* compare less-equal: cle -> ri = ai <= bi ? 1...1:0...0 */ +#define msa_cleq_u8(__a, __b) ((v16u8)__builtin_msa_cle_u_b((v16u8)(__a), (v16u8)(__b))) +#define msa_cleq_s8(__a, __b) ((v16u8)__builtin_msa_cle_s_b((v16i8)(__a), (v16i8)(__b))) +#define msa_cleq_u16(__a, __b) ((v8u16)__builtin_msa_cle_u_h((v8u16)(__a), (v8u16)(__b))) +#define msa_cleq_s16(__a, __b) ((v8u16)__builtin_msa_cle_s_h((v8i16)(__a), (v8i16)(__b))) +#define msa_cleq_u32(__a, __b) ((v4u32)__builtin_msa_cle_u_w((v4u32)(__a), (v4u32)(__b))) +#define msa_cleq_s32(__a, __b) ((v4u32)__builtin_msa_cle_s_w((v4i32)(__a), (v4i32)(__b))) +#define msa_cleq_f32(__a, __b) ((v4u32)__builtin_msa_fcle_w((v4f32)(__a), (v4f32)(__b))) +#define msa_cleq_u64(__a, __b) ((v2u64)__builtin_msa_cle_u_d((v2u64)(__a), (v2u64)(__b))) +#define msa_cleq_s64(__a, __b) ((v2u64)__builtin_msa_cle_s_d((v2i64)(__a), (v2i64)(__b))) +#define msa_cleq_f64(__a, __b) ((v2u64)__builtin_msa_fcle_d((v2f64)(__a), (v2f64)(__b))) + +/* compare greater-equal: cge -> ri = ai >= bi ? 1...1:0...0 */ +#define msa_cgeq_u8(__a, __b) ((v16u8)__builtin_msa_cle_u_b((v16u8)(__b), (v16u8)(__a))) +#define msa_cgeq_s8(__a, __b) ((v16u8)__builtin_msa_cle_s_b((v16i8)(__b), (v16i8)(__a))) +#define msa_cgeq_u16(__a, __b) ((v8u16)__builtin_msa_cle_u_h((v8u16)(__b), (v8u16)(__a))) +#define msa_cgeq_s16(__a, __b) ((v8u16)__builtin_msa_cle_s_h((v8i16)(__b), (v8i16)(__a))) +#define msa_cgeq_u32(__a, __b) ((v4u32)__builtin_msa_cle_u_w((v4u32)(__b), (v4u32)(__a))) +#define msa_cgeq_s32(__a, __b) ((v4u32)__builtin_msa_cle_s_w((v4i32)(__b), (v4i32)(__a))) +#define msa_cgeq_f32(__a, __b) ((v4u32)__builtin_msa_fcle_w((v4f32)(__b), (v4f32)(__a))) +#define msa_cgeq_u64(__a, __b) ((v2u64)__builtin_msa_cle_u_d((v2u64)(__b), (v2u64)(__a))) +#define msa_cgeq_s64(__a, __b) ((v2u64)__builtin_msa_cle_s_d((v2i64)(__b), (v2i64)(__a))) +#define msa_cgeq_f64(__a, __b) ((v2u64)__builtin_msa_fcle_d((v2f64)(__b), (v2f64)(__a))) + +/* Shift Left Logical: shl -> ri = ai << bi; */ +#define msa_shlq_u8(__a, __b) ((v16u8)__builtin_msa_sll_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shlq_s8(__a, __b) ((v16i8)__builtin_msa_sll_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shlq_u16(__a, __b) ((v8u16)__builtin_msa_sll_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shlq_s16(__a, __b) ((v8i16)__builtin_msa_sll_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shlq_u32(__a, __b) ((v4u32)__builtin_msa_sll_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shlq_s32(__a, __b) ((v4i32)__builtin_msa_sll_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shlq_u64(__a, __b) ((v2u64)__builtin_msa_sll_d((v2i64)(__a), (v2i64)(__b))) +#define msa_shlq_s64(__a, __b) ((v2i64)__builtin_msa_sll_d((v2i64)(__a), (v2i64)(__b))) + +/* Immediate Shift Left Logical: shl -> ri = ai << imm; */ +#define msa_shlq_n_u8(__a, __imm) ((v16u8)__builtin_msa_slli_b((v16i8)(__a), __imm)) +#define msa_shlq_n_s8(__a, __imm) ((v16i8)__builtin_msa_slli_b((v16i8)(__a), __imm)) +#define msa_shlq_n_u16(__a, __imm) ((v8u16)__builtin_msa_slli_h((v8i16)(__a), __imm)) +#define msa_shlq_n_s16(__a, __imm) ((v8i16)__builtin_msa_slli_h((v8i16)(__a), __imm)) +#define msa_shlq_n_u32(__a, __imm) ((v4u32)__builtin_msa_slli_w((v4i32)(__a), __imm)) +#define msa_shlq_n_s32(__a, __imm) ((v4i32)__builtin_msa_slli_w((v4i32)(__a), __imm)) +#define msa_shlq_n_u64(__a, __imm) ((v2u64)__builtin_msa_slli_d((v2i64)(__a), __imm)) +#define msa_shlq_n_s64(__a, __imm) ((v2i64)__builtin_msa_slli_d((v2i64)(__a), __imm)) + +/* shift right: shrq -> ri = ai >> bi; */ +#define msa_shrq_u8(__a, __b) ((v16u8)__builtin_msa_srl_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shrq_s8(__a, __b) ((v16i8)__builtin_msa_sra_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shrq_u16(__a, __b) ((v8u16)__builtin_msa_srl_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shrq_s16(__a, __b) ((v8i16)__builtin_msa_sra_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shrq_u32(__a, __b) ((v4u32)__builtin_msa_srl_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shrq_s32(__a, __b) ((v4i32)__builtin_msa_sra_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shrq_u64(__a, __b) ((v2u64)__builtin_msa_srl_d((v2i64)(__a), (v2i64)(__b))) +#define msa_shrq_s64(__a, __b) ((v2i64)__builtin_msa_sra_d((v2i64)(__a), (v2i64)(__b))) + +/* Immediate Shift Right: shr -> ri = ai >> imm; */ +#define msa_shrq_n_u8(__a, __imm) ((v16u8)__builtin_msa_srli_b((v16i8)(__a), __imm)) +#define msa_shrq_n_s8(__a, __imm) ((v16i8)__builtin_msa_srai_b((v16i8)(__a), __imm)) +#define msa_shrq_n_u16(__a, __imm) ((v8u16)__builtin_msa_srli_h((v8i16)(__a), __imm)) +#define msa_shrq_n_s16(__a, __imm) ((v8i16)__builtin_msa_srai_h((v8i16)(__a), __imm)) +#define msa_shrq_n_u32(__a, __imm) ((v4u32)__builtin_msa_srli_w((v4i32)(__a), __imm)) +#define msa_shrq_n_s32(__a, __imm) ((v4i32)__builtin_msa_srai_w((v4i32)(__a), __imm)) +#define msa_shrq_n_u64(__a, __imm) ((v2u64)__builtin_msa_srli_d((v2i64)(__a), __imm)) +#define msa_shrq_n_s64(__a, __imm) ((v2i64)__builtin_msa_srai_d((v2i64)(__a), __imm)) + +/* Immediate Shift Right Rounded: shr -> ri = ai >> (rounded)imm; */ +#define msa_rshrq_n_u8(__a, __imm) ((v16u8)__builtin_msa_srlri_b((v16i8)(__a), __imm)) +#define msa_rshrq_n_s8(__a, __imm) ((v16i8)__builtin_msa_srari_b((v16i8)(__a), __imm)) +#define msa_rshrq_n_u16(__a, __imm) ((v8u16)__builtin_msa_srlri_h((v8i16)(__a), __imm)) +#define msa_rshrq_n_s16(__a, __imm) ((v8i16)__builtin_msa_srari_h((v8i16)(__a), __imm)) +#define msa_rshrq_n_u32(__a, __imm) ((v4u32)__builtin_msa_srlri_w((v4i32)(__a), __imm)) +#define msa_rshrq_n_s32(__a, __imm) ((v4i32)__builtin_msa_srari_w((v4i32)(__a), __imm)) +#define msa_rshrq_n_u64(__a, __imm) ((v2u64)__builtin_msa_srlri_d((v2i64)(__a), __imm)) +#define msa_rshrq_n_s64(__a, __imm) ((v2i64)__builtin_msa_srari_d((v2i64)(__a), __imm)) + +/* Vector saturating rounding shift left, qrshl -> ri = ai << bi; */ +#define msa_qrshrq_s32(a, b) ((v4i32)__msa_srar_w((v4i32)(a), (v4i32)(b))) + +/* Rename the msa builtin func to unify the name style for intrin_msa.hpp */ +#define msa_qaddq_u8 __builtin_msa_adds_u_b +#define msa_qaddq_s8 __builtin_msa_adds_s_b +#define msa_qaddq_u16 __builtin_msa_adds_u_h +#define msa_qaddq_s16 __builtin_msa_adds_s_h +#define msa_qaddq_u32 __builtin_msa_adds_u_w +#define msa_qaddq_s32 __builtin_msa_adds_s_w +#define msa_qaddq_u64 __builtin_msa_adds_u_d +#define msa_qaddq_s64 __builtin_msa_adds_s_d +#define msa_addq_u8(a, b) ((v16u8)__builtin_msa_addv_b((v16i8)(a), (v16i8)(b))) +#define msa_addq_s8 __builtin_msa_addv_b +#define msa_addq_u16(a, b) ((v8u16)__builtin_msa_addv_h((v8i16)(a), (v8i16)(b))) +#define msa_addq_s16 __builtin_msa_addv_h +#define msa_addq_u32(a, b) ((v4u32)__builtin_msa_addv_w((v4i32)(a), (v4i32)(b))) +#define msa_addq_s32 __builtin_msa_addv_w +#define msa_addq_f32 __builtin_msa_fadd_w +#define msa_addq_u64(a, b) ((v2u64)__builtin_msa_addv_d((v2i64)(a), (v2i64)(b))) +#define msa_addq_s64 __builtin_msa_addv_d +#define msa_addq_f64 __builtin_msa_fadd_d +#define msa_qsubq_u8 __builtin_msa_subs_u_b +#define msa_qsubq_s8 __builtin_msa_subs_s_b +#define msa_qsubq_u16 __builtin_msa_subs_u_h +#define msa_qsubq_s16 __builtin_msa_subs_s_h +#define msa_subq_u8(a, b) ((v16u8)__builtin_msa_subv_b((v16i8)(a), (v16i8)(b))) +#define msa_subq_s8 __builtin_msa_subv_b +#define msa_subq_u16(a, b) ((v8u16)__builtin_msa_subv_h((v8i16)(a), (v8i16)(b))) +#define msa_subq_s16 __builtin_msa_subv_h +#define msa_subq_u32(a, b) ((v4u32)__builtin_msa_subv_w((v4i32)(a), (v4i32)(b))) +#define msa_subq_s32 __builtin_msa_subv_w +#define msa_subq_f32 __builtin_msa_fsub_w +#define msa_subq_u64(a, b) ((v2u64)__builtin_msa_subv_d((v2i64)(a), (v2i64)(b))) +#define msa_subq_s64 __builtin_msa_subv_d +#define msa_subq_f64 __builtin_msa_fsub_d +#define msa_mulq_u8(a, b) ((v16u8)__builtin_msa_mulv_b((v16i8)(a), (v16i8)(b))) +#define msa_mulq_s8(a, b) ((v16i8)__builtin_msa_mulv_b((v16i8)(a), (v16i8)(b))) +#define msa_mulq_u16(a, b) ((v8u16)__builtin_msa_mulv_h((v8i16)(a), (v8i16)(b))) +#define msa_mulq_s16(a, b) ((v8i16)__builtin_msa_mulv_h((v8i16)(a), (v8i16)(b))) +#define msa_mulq_u32(a, b) ((v4u32)__builtin_msa_mulv_w((v4i32)(a), (v4i32)(b))) +#define msa_mulq_s32(a, b) ((v4i32)__builtin_msa_mulv_w((v4i32)(a), (v4i32)(b))) +#define msa_mulq_u64(a, b) ((v2u64)__builtin_msa_mulv_d((v2i64)(a), (v2i64)(b))) +#define msa_mulq_s64(a, b) ((v2i64)__builtin_msa_mulv_d((v2i64)(a), (v2i64)(b))) +#define msa_mulq_f32 __builtin_msa_fmul_w +#define msa_mulq_f64 __builtin_msa_fmul_d +#define msa_divq_f32 __builtin_msa_fdiv_w +#define msa_divq_f64 __builtin_msa_fdiv_d +#define msa_dotp_s_h __builtin_msa_dotp_s_h +#define msa_dotp_s_w __builtin_msa_dotp_s_w +#define msa_dotp_s_d __builtin_msa_dotp_s_d +#define msa_dotp_u_h __builtin_msa_dotp_u_h +#define msa_dotp_u_w __builtin_msa_dotp_u_w +#define msa_dotp_u_d __builtin_msa_dotp_u_d +#define msa_dpadd_s_h __builtin_msa_dpadd_s_h +#define msa_dpadd_s_w __builtin_msa_dpadd_s_w +#define msa_dpadd_s_d __builtin_msa_dpadd_s_d +#define msa_dpadd_u_h __builtin_msa_dpadd_u_h +#define msa_dpadd_u_w __builtin_msa_dpadd_u_w +#define msa_dpadd_u_d __builtin_msa_dpadd_u_d + +#define ILVRL_B2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_b((v16i8)(in0), (v16i8)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_b((v16i8)(in0), (v16i8)(in1)); \ + } while (0) +#define ILVRL_B2_UB(...) ILVRL_B2(v16u8, __VA_ARGS__) +#define ILVRL_B2_SB(...) ILVRL_B2(v16i8, __VA_ARGS__) +#define ILVRL_B2_UH(...) ILVRL_B2(v8u16, __VA_ARGS__) +#define ILVRL_B2_SH(...) ILVRL_B2(v8i16, __VA_ARGS__) +#define ILVRL_B2_SW(...) ILVRL_B2(v4i32, __VA_ARGS__) + +#define ILVRL_H2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_h((v8i16)(in0), (v8i16)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_h((v8i16)(in0), (v8i16)(in1)); \ + } while (0) +#define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__) +#define ILVRL_H2_SB(...) ILVRL_H2(v16i8, __VA_ARGS__) +#define ILVRL_H2_UH(...) ILVRL_H2(v8u16, __VA_ARGS__) +#define ILVRL_H2_SH(...) ILVRL_H2(v8i16, __VA_ARGS__) +#define ILVRL_H2_SW(...) ILVRL_H2(v4i32, __VA_ARGS__) +#define ILVRL_H2_UW(...) ILVRL_H2(v4u32, __VA_ARGS__) + +#define ILVRL_W2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_w((v4i32)(in0), (v4i32)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_w((v4i32)(in0), (v4i32)(in1)); \ + } while (0) +#define ILVRL_W2_UB(...) ILVRL_W2(v16u8, __VA_ARGS__) +#define ILVRL_W2_SH(...) ILVRL_W2(v8i16, __VA_ARGS__) +#define ILVRL_W2_SW(...) ILVRL_W2(v4i32, __VA_ARGS__) +#define ILVRL_W2_UW(...) ILVRL_W2(v4u32, __VA_ARGS__) + +/* absq, qabsq (r = |a|;) */ +#define msa_absq_s8(a) __builtin_msa_add_a_b(a, __builtin_msa_fill_b(0)) +#define msa_absq_s16(a) __builtin_msa_add_a_h(a, __builtin_msa_fill_h(0)) +#define msa_absq_s32(a) __builtin_msa_add_a_w(a, __builtin_msa_fill_w(0)) +#define msa_absq_s64(a) __builtin_msa_add_a_d(a, __builtin_msa_fill_d(0)) +#define msa_absq_f32(a) ((v4f32)__builtin_msa_bclri_w((v4u32)(a), 31)) +#define msa_absq_f64(a) ((v2f64)__builtin_msa_bclri_d((v2u64)(a), 63)) +#define msa_qabsq_s8(a) __builtin_msa_adds_a_b(a, __builtin_msa_fill_b(0)) +#define msa_qabsq_s16(a) __builtin_msa_adds_a_h(a, __builtin_msa_fill_h(0)) +#define msa_qabsq_s32(a) __builtin_msa_adds_a_w(a, __builtin_msa_fill_w(0)) +#define msa_qabsq_s64(a) __builtin_msa_adds_a_d(a, __builtin_msa_fill_d(0)) + +/* abdq, qabdq (r = |a - b|;) */ +#define msa_abdq_u8 __builtin_msa_asub_u_b +#define msa_abdq_s8 __builtin_msa_asub_s_b +#define msa_abdq_u16 __builtin_msa_asub_u_h +#define msa_abdq_s16 __builtin_msa_asub_s_h +#define msa_abdq_u32 __builtin_msa_asub_u_w +#define msa_abdq_s32 __builtin_msa_asub_s_w +#define msa_abdq_u64 __builtin_msa_asub_u_d +#define msa_abdq_s64 __builtin_msa_asub_s_d +#define msa_abdq_f32(a, b) msa_absq_f32(__builtin_msa_fsub_w(a, b)) +#define msa_abdq_f64(a, b) msa_absq_f64(__builtin_msa_fsub_d(a, b)) +#define msa_qabdq_s8(a, b) msa_qabsq_s8(__builtin_msa_subs_s_b(a, b)) +#define msa_qabdq_s16(a, b) msa_qabsq_s16(__builtin_msa_subs_s_h(a, b)) +#define msa_qabdq_s32(a, b) msa_qabsq_s32(__builtin_msa_subs_s_w(a, b)) +#define msa_qabdq_s64(a, b) msa_qabsq_s64(__builtin_msa_subs_s_d(a, b)) + +/* sqrtq, rsqrtq */ +#define msa_sqrtq_f32 __builtin_msa_fsqrt_w +#define msa_sqrtq_f64 __builtin_msa_fsqrt_d +#define msa_rsqrtq_f32 __builtin_msa_frsqrt_w +#define msa_rsqrtq_f64 __builtin_msa_frsqrt_d + + +/* mlaq: r = a + b * c; */ +__extension__ extern __inline v4i32 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_s32(v4i32 __a, v4i32 __b, v4i32 __c) +{ + __asm__ volatile("maddv.w %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v2i64 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_s64(v2i64 __a, v2i64 __b, v2i64 __c) +{ + __asm__ volatile("maddv.d %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v4f32 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_f32(v4f32 __a, v4f32 __b, v4f32 __c) +{ + __asm__ volatile("fmadd.w %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v2f64 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_f64(v2f64 __a, v2f64 __b, v2f64 __c) +{ + __asm__ volatile("fmadd.d %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +/* cntq */ +#define msa_cntq_s8 __builtin_msa_pcnt_b +#define msa_cntq_s16 __builtin_msa_pcnt_h +#define msa_cntq_s32 __builtin_msa_pcnt_w +#define msa_cntq_s64 __builtin_msa_pcnt_d + +/* bslq (a: mask; r = b(if a == 0); r = c(if a == 1);) */ +#define msa_bslq_u8 __builtin_msa_bsel_v + +/* ilvrq, ilvlq (For EL only, ilvrq: b0, a0, b1, a1; ilvlq: b2, a2, b3, a3;) */ +#define msa_ilvrq_s8 __builtin_msa_ilvr_b +#define msa_ilvrq_s16 __builtin_msa_ilvr_h +#define msa_ilvrq_s32 __builtin_msa_ilvr_w +#define msa_ilvrq_s64 __builtin_msa_ilvr_d +#define msa_ilvlq_s8 __builtin_msa_ilvl_b +#define msa_ilvlq_s16 __builtin_msa_ilvl_h +#define msa_ilvlq_s32 __builtin_msa_ilvl_w +#define msa_ilvlq_s64 __builtin_msa_ilvl_d + +/* ilvevq, ilvodq (ilvevq: b0, a0, b2, a2; ilvodq: b1, a1, b3, a3; ) */ +#define msa_ilvevq_s8 __builtin_msa_ilvev_b +#define msa_ilvevq_s16 __builtin_msa_ilvev_h +#define msa_ilvevq_s32 __builtin_msa_ilvev_w +#define msa_ilvevq_s64 __builtin_msa_ilvev_d +#define msa_ilvodq_s8 __builtin_msa_ilvod_b +#define msa_ilvodq_s16 __builtin_msa_ilvod_h +#define msa_ilvodq_s32 __builtin_msa_ilvod_w +#define msa_ilvodq_s64 __builtin_msa_ilvod_d + +/* extq (r = (a || b); a concatenation b and get elements from index c) */ +#ifdef _MIPSEB +#define msa_extq_s8(a, b, c) \ +(__builtin_msa_vshf_b(__builtin_msa_subv_b((v16i8)((v2i64){0x1716151413121110, 0x1F1E1D1C1B1A1918}), __builtin_msa_fill_b(c)), a, b)) +#define msa_extq_s16(a, b, c) \ +(__builtin_msa_vshf_h(__builtin_msa_subv_h((v8i16)((v2i64){0x000B000A00090008, 0x000F000E000D000C}), __builtin_msa_fill_h(c)), a, b)) +#define msa_extq_s32(a, b, c) \ +(__builtin_msa_vshf_w(__builtin_msa_subv_w((v4i32)((v2i64){0x0000000500000004, 0x0000000700000006}), __builtin_msa_fill_w(c)), a, b)) +#define msa_extq_s64(a, b, c) \ +(__builtin_msa_vshf_d(__builtin_msa_subv_d((v2i64){0x0000000000000002, 0x0000000000000003}, __builtin_msa_fill_d(c)), a, b)) +#else +#define msa_extq_s8(a, b, c) \ +(__builtin_msa_vshf_b(__builtin_msa_addv_b((v16i8)((v2i64){0x0706050403020100, 0x0F0E0D0C0B0A0908}), __builtin_msa_fill_b(c)), b, a)) +#define msa_extq_s16(a, b, c) \ +(__builtin_msa_vshf_h(__builtin_msa_addv_h((v8i16)((v2i64){0x0003000200010000, 0x0007000600050004}), __builtin_msa_fill_h(c)), b, a)) +#define msa_extq_s32(a, b, c) \ +(__builtin_msa_vshf_w(__builtin_msa_addv_w((v4i32)((v2i64){0x0000000100000000, 0x0000000300000002}), __builtin_msa_fill_w(c)), b, a)) +#define msa_extq_s64(a, b, c) \ +(__builtin_msa_vshf_d(__builtin_msa_addv_d((v2i64){0x0000000000000000, 0x0000000000000001}, __builtin_msa_fill_d(c)), b, a)) +#endif /* _MIPSEB */ + +/* cvttruncq, cvttintq, cvtrintq */ +#define msa_cvttruncq_u32_f32 __builtin_msa_ftrunc_u_w +#define msa_cvttruncq_s32_f32 __builtin_msa_ftrunc_s_w +#define msa_cvttruncq_u64_f64 __builtin_msa_ftrunc_u_d +#define msa_cvttruncq_s64_f64 __builtin_msa_ftrunc_s_d +#define msa_cvttintq_u32_f32 __builtin_msa_ftint_u_w +#define msa_cvttintq_s32_f32 __builtin_msa_ftint_s_w +#define msa_cvttintq_u64_f64 __builtin_msa_ftint_u_d +#define msa_cvttintq_s64_f64 __builtin_msa_ftint_s_d +#define msa_cvtrintq_f32 __builtin_msa_frint_w +#define msa_cvtrintq_f64 __builtin_msa_frint_d + +/* cvtfintq, cvtfq */ +#define msa_cvtfintq_f32_u32 __builtin_msa_ffint_u_w +#define msa_cvtfintq_f32_s32 __builtin_msa_ffint_s_w +#define msa_cvtfintq_f64_u64 __builtin_msa_ffint_u_d +#define msa_cvtfintq_f64_s64 __builtin_msa_ffint_s_d +#define msa_cvtfq_f32_f64 __builtin_msa_fexdo_w +#define msa_cvtflq_f64_f32 __builtin_msa_fexupr_d +#define msa_cvtfhq_f64_f32 __builtin_msa_fexupl_d + +#define msa_addl_u8(a, b) ((v8u16)__builtin_msa_addv_h((v8i16)V8U8_2_V8I16(a), (v8i16)V8U8_2_V8I16(b))) +#define msa_addl_s8(a, b) (__builtin_msa_addv_h((v8i16)V8I8_2_V8I16(a), (v8i16)V8I8_2_V8I16(b))) +#define msa_addl_u16(a, b) ((v4u32)__builtin_msa_addv_w((v4i32)V4U16_2_V4I32(a), (v4i32)V4U16_2_V4I32(b))) +#define msa_addl_s16(a, b) (__builtin_msa_addv_w((v4i32)V4I16_2_V4I32(a), (v4i32)V4I16_2_V4I32(b))) +#define msa_subl_s16(a, b) (__builtin_msa_subv_w((v4i32)V4I16_2_V4I32(a), (v4i32)V4I16_2_V4I32(b))) +#define msa_recpeq_f32 __builtin_msa_frcp_w +#define msa_recpsq_f32(a, b) (__builtin_msa_fsub_w(msa_dupq_n_f32(2.0f), __builtin_msa_fmul_w(a, b))) + +#define MSA_INTERLEAVED_IMPL_LOAD2_STORE2(_Tp, _Tpv, _Tpvs, suffix, df, nlanes) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld2q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + nlanes); \ + *a = (_Tpv)__builtin_msa_pckev_##df((_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_pckod_##df((_Tpvs)v1, (_Tpvs)v0); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st2q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b) \ +{ \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_##df((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + nlanes, (_Tpv)__builtin_msa_ilvl_##df((_Tpvs)b, (_Tpvs)a)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint8_t, v16u8, v16i8, u8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int8_t, v16i8, v16i8, s8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint16_t, v8u16, v8i16, u16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int16_t, v8i16, v8i16, s16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint32_t, v4u32, v4i32, u32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int32_t, v4i32, v4i32, s32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(float, v4f32, v4i32, f32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint64_t, v2u64, v2i64, u64, d, 2) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int64_t, v2i64, v2i64, s64, d, 2) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(double, v2f64, v2i64, f64, d, 2) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_LOAD3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 16); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 32); \ + _Tpvs v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0704011F1F1F1F1F, 0x1F1C191613100D0A}), (_Tpvs)v0, (_Tpvs)v1); \ + *a = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x1716150E0B080502, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0603001F1F1F1F1F, 0x1E1B1815120F0C09}), (_Tpvs)v0, (_Tpvs)v1); \ + *b = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x1716150D0A070401, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x05021F1F1F1F1F1F, 0x1D1A1714110E0B08}), (_Tpvs)v0, (_Tpvs)v1); \ + *c = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x17160F0C09060300, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ +} +#else +#define MSA_INTERLEAVED_IMPL_LOAD3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 16); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 32); \ + _Tpvs v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x15120F0C09060300, 0x00000000001E1B18}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1D1A1714110A0908}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1613100D0A070401, 0x00000000001F1C19}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1E1B1815120A0908}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1714110E0B080502, 0x0000000000001D1A}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1F1C191613100908}), (_Tpvs)v2, v3); \ +} +#endif + +MSA_INTERLEAVED_IMPL_LOAD3_8(uint8_t, v16u8, v16i8, u8) +MSA_INTERLEAVED_IMPL_LOAD3_8(int8_t, v16i8, v16i8, s8) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_LOAD3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 8); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 16); \ + _Tpvs v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00030000000F000F, 0x000F000C00090006}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000A00050002, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0002000F000F000F, 0x000E000B00080005}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000700040001, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000F000F000F, 0x000D000A00070004}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000600030000, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ +} +#else +#define MSA_INTERLEAVED_IMPL_LOAD3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 8); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 16); \ + _Tpvs v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0009000600030000, 0x00000000000F000C}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000D000A00050004}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000A000700040001, 0x000000000000000D}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000E000B00080004}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000800050002, 0x000000000000000E}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000F000C00090004}), (_Tpvs)v2, v3); \ +} +#endif + +MSA_INTERLEAVED_IMPL_LOAD3_16(uint16_t, v8u16, v8i16, u16) +MSA_INTERLEAVED_IMPL_LOAD3_16(int16_t, v8i16, v8i16, s16) + +#define MSA_INTERLEAVED_IMPL_LOAD3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v00 = msa_ld1q_##suffix(ptr); \ + _Tpv v01 = msa_ld1q_##suffix(ptr + 4); \ + _Tpv v02 = msa_ld1q_##suffix(ptr + 8); \ + _Tpvs v10 = __builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v01, (v2i64)v01), (_Tpvs)v00); \ + _Tpvs v11 = __builtin_msa_ilvr_w((_Tpvs)v02, (_Tpvs)__builtin_msa_ilvl_d((v2i64)v00, (v2i64)v00)); \ + _Tpvs v12 = __builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v02, (v2i64)v02), (_Tpvs)v01); \ + *a = (_Tpv)__builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v11, (v2i64)v11), v10); \ + *b = (_Tpv)__builtin_msa_ilvr_w(v12, (_Tpvs)__builtin_msa_ilvl_d((v2i64)v10, (v2i64)v10)); \ + *c = (_Tpv)__builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v12, (v2i64)v12), v11); \ +} + +MSA_INTERLEAVED_IMPL_LOAD3_32(uint32_t, v4u32, v4i32, u32) +MSA_INTERLEAVED_IMPL_LOAD3_32(int32_t, v4i32, v4i32, s32) +MSA_INTERLEAVED_IMPL_LOAD3_32(float, v4f32, v4i32, f32) + +#define MSA_INTERLEAVED_IMPL_LOAD3_64(_Tp, _Tpv, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + *((_Tp*)a) = *ptr; *((_Tp*)b) = *(ptr + 1); *((_Tp*)c) = *(ptr + 2); \ + *((_Tp*)a + 1) = *(ptr + 3); *((_Tp*)b + 1) = *(ptr + 4); *((_Tp*)c + 1) = *(ptr + 5); \ +} + +MSA_INTERLEAVED_IMPL_LOAD3_64(uint64_t, v2u64, u64) +MSA_INTERLEAVED_IMPL_LOAD3_64(int64_t, v2i64, s64) +MSA_INTERLEAVED_IMPL_LOAD3_64(double, v2f64, f64) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0F0E0D0C0B1F1F1F, 0x1F1E1D1C1B1A1F1F}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0D1C140C1B130B1A, 0x1F170F1E160E1D15}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0A09080706051F1F, 0x19181716151F1F1F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1D14071C13061B12, 0x170A1F16091E1508}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x04030201001F1F1F, 0x14131211101F1F1F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x15021C14011B1300, 0x051F17041E16031D}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 32, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000050403020100, 0x0000001413121110}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0A02110901100800, 0x05140C04130B0312}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000000A09080706, 0x00001A1918171615}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x170A011609001508, 0x0D04190C03180B02}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000000F0E0D0C0B, 0x0000001F1E1D1C1B}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x021C09011B08001A, 0x1F0C041E0B031D0A}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 32, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_8(uint8_t, v16u8, v16i8, u8) +MSA_INTERLEAVED_IMPL_STORE3_8(int8_t, v16i8, v16i8, s8) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000700060005000F, 0x000F000E000D000F}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000A0006000D0009, 0x000F000B0007000E}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00040003000F000F, 0x000C000B000A000F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000E000A0003000D, 0x0005000F000B0004}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000200010000000F, 0x00090008000F000F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000E00090000, 0x000B0002000F000A}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000200010000, 0x0000000A00090008}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000800040000, 0x0006000200090005}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000500040003, 0x00000000000C000B}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B00040000000A, 0x0002000C00050001}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000000070006, 0x0000000F000E000D}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00050000000D0004, 0x000F00060001000E}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_16(uint16_t, v8u16, v8i16, u16) +MSA_INTERLEAVED_IMPL_STORE3_16(int16_t, v8i16, v8i16, s16) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000300000007, 0x0000000700000006}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000300000006, 0x0000000700000005}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000200000001, 0x0000000500000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000700000004, 0x0000000500000002}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000007, 0x0000000400000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000500000000, 0x0000000100000007}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000100000000, 0x0000000000000004}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000200000000, 0x0000000100000004}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000002, 0x0000000600000005}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000500000002, 0x0000000300000000}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000003, 0x0000000000000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000006, 0x0000000700000002}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_32(uint32_t, v4u32, v4i32, u32) +MSA_INTERLEAVED_IMPL_STORE3_32(int32_t, v4i32, v4i32, s32) +MSA_INTERLEAVED_IMPL_STORE3_32(float, v4f32, v4i32, f32) + +#define MSA_INTERLEAVED_IMPL_STORE3_64(_Tp, _Tpv, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + *ptr = a[0]; *(ptr + 1) = b[0]; *(ptr + 2) = c[0]; \ + *(ptr + 3) = a[1]; *(ptr + 4) = b[1]; *(ptr + 5) = c[1]; \ +} + +MSA_INTERLEAVED_IMPL_STORE3_64(uint64_t, v2u64, u64) +MSA_INTERLEAVED_IMPL_STORE3_64(int64_t, v2i64, s64) +MSA_INTERLEAVED_IMPL_STORE3_64(double, v2f64, f64) + +#define MSA_INTERLEAVED_IMPL_LOAD4_STORE4(_Tp, _Tpv, _Tpvs, suffix, df, nlanes) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld4q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c, _Tpv* d) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + nlanes); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + nlanes * 2); \ + _Tpv v3 = msa_ld1q_##suffix(ptr + nlanes * 3); \ + _Tpvs t0 = __builtin_msa_pckev_##df((_Tpvs)v1, (_Tpvs)v0); \ + _Tpvs t1 = __builtin_msa_pckev_##df((_Tpvs)v3, (_Tpvs)v2); \ + _Tpvs t2 = __builtin_msa_pckod_##df((_Tpvs)v1, (_Tpvs)v0); \ + _Tpvs t3 = __builtin_msa_pckod_##df((_Tpvs)v3, (_Tpvs)v2); \ + *a = (_Tpv)__builtin_msa_pckev_##df(t1, t0); \ + *b = (_Tpv)__builtin_msa_pckev_##df(t3, t2); \ + *c = (_Tpv)__builtin_msa_pckod_##df(t1, t0); \ + *d = (_Tpv)__builtin_msa_pckod_##df(t3, t2); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st4q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c, const _Tpv d) \ +{ \ + _Tpvs v0 = __builtin_msa_ilvr_##df((_Tpvs)c, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_ilvr_##df((_Tpvs)d, (_Tpvs)b); \ + _Tpvs v2 = __builtin_msa_ilvl_##df((_Tpvs)c, (_Tpvs)a); \ + _Tpvs v3 = __builtin_msa_ilvl_##df((_Tpvs)d, (_Tpvs)b); \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_##df(v1, v0)); \ + msa_st1q_##suffix(ptr + nlanes, (_Tpv)__builtin_msa_ilvl_##df(v1, v0)); \ + msa_st1q_##suffix(ptr + 2 * nlanes, (_Tpv)__builtin_msa_ilvr_##df(v3, v2)); \ + msa_st1q_##suffix(ptr + 3 * nlanes, (_Tpv)__builtin_msa_ilvl_##df(v3, v2)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint8_t, v16u8, v16i8, u8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int8_t, v16i8, v16i8, s8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint16_t, v8u16, v8i16, u16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int16_t, v8i16, v8i16, s16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint32_t, v4u32, v4i32, u32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int32_t, v4i32, v4i32, s32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(float, v4f32, v4i32, f32, w, 4) + +#define MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld4q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c, _Tpv* d) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 2); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 4); \ + _Tpv v3 = msa_ld1q_##suffix(ptr + 6); \ + *a = (_Tpv)__builtin_msa_ilvr_d((_Tpvs)v2, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_ilvl_d((_Tpvs)v2, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_ilvr_d((_Tpvs)v3, (_Tpvs)v1); \ + *d = (_Tpv)__builtin_msa_ilvl_d((_Tpvs)v3, (_Tpvs)v1); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st4q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c, const _Tpv d) \ +{ \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_d((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + 2, (_Tpv)__builtin_msa_ilvr_d((_Tpvs)d, (_Tpvs)c)); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)__builtin_msa_ilvl_d((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + 6, (_Tpv)__builtin_msa_ilvl_d((_Tpvs)d, (_Tpvs)c)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(uint64_t, v2u64, v2i64, u64) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(int64_t, v2i64, v2i64, s64) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(double, v2f64, v2i64, f64) + +__extension__ extern __inline v8i16 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_qdmulhq_n_s16(v8i16 a, int16_t b) +{ + v8i16 a_lo, a_hi; + ILVRL_H2_SH(a, msa_dupq_n_s16(0), a_lo, a_hi); + return msa_packr_s32(msa_shlq_n_s32(msa_mulq_s32(msa_paddlq_s16(a_lo), msa_dupq_n_s32(b)), 1), + msa_shlq_n_s32(msa_mulq_s32(msa_paddlq_s16(a_hi), msa_dupq_n_s32(b)), 1), 16); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /*__mips_msa*/ +#endif /* OPENCV_CORE_MSA_MACROS_H */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/hal/simd_utils.impl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/simd_utils.impl.hpp new file mode 100644 index 0000000..fff8f94 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/hal/simd_utils.impl.hpp @@ -0,0 +1,146 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +// This header is not standalone. Don't include directly, use "intrin.hpp" instead. +#ifdef OPENCV_HAL_INTRIN_HPP // defined in intrin.hpp + + +#if CV_SIMD128 || CV_SIMD128_CPP + +template struct Type2Vec128_Traits; +#define CV_INTRIN_DEF_TYPE2VEC128_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec128_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(uchar, v_uint8x16); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(schar, v_int8x16); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(ushort, v_uint16x8); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(short, v_int16x8); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(unsigned, v_uint32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(int, v_int32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(float, v_float32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(uint64, v_uint64x2); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(int64, v_int64x2); +#if CV_SIMD128_64F +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(double, v_float64x2); +#endif + +template static inline +typename Type2Vec128_Traits<_T>::vec_type v_setall(const _T& a); + +template<> inline Type2Vec128_Traits< uchar>::vec_type v_setall< uchar>(const uchar& a) { return v_setall_u8(a); } +template<> inline Type2Vec128_Traits< schar>::vec_type v_setall< schar>(const schar& a) { return v_setall_s8(a); } +template<> inline Type2Vec128_Traits::vec_type v_setall(const ushort& a) { return v_setall_u16(a); } +template<> inline Type2Vec128_Traits< short>::vec_type v_setall< short>(const short& a) { return v_setall_s16(a); } +template<> inline Type2Vec128_Traits< uint>::vec_type v_setall< uint>(const uint& a) { return v_setall_u32(a); } +template<> inline Type2Vec128_Traits< int>::vec_type v_setall< int>(const int& a) { return v_setall_s32(a); } +template<> inline Type2Vec128_Traits::vec_type v_setall(const uint64& a) { return v_setall_u64(a); } +template<> inline Type2Vec128_Traits< int64>::vec_type v_setall< int64>(const int64& a) { return v_setall_s64(a); } +template<> inline Type2Vec128_Traits< float>::vec_type v_setall< float>(const float& a) { return v_setall_f32(a); } +#if CV_SIMD128_64F +template<> inline Type2Vec128_Traits::vec_type v_setall(const double& a) { return v_setall_f64(a); } +#endif + +#endif // SIMD128 + + +#if CV_SIMD256 + +template struct Type2Vec256_Traits; +#define CV_INTRIN_DEF_TYPE2VEC256_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec256_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(uchar, v_uint8x32); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(schar, v_int8x32); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(ushort, v_uint16x16); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(short, v_int16x16); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(unsigned, v_uint32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(int, v_int32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(float, v_float32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(uint64, v_uint64x4); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(int64, v_int64x4); +#if CV_SIMD256_64F +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(double, v_float64x4); +#endif + +template static inline +typename Type2Vec256_Traits<_T>::vec_type v256_setall(const _T& a); + +template<> inline Type2Vec256_Traits< uchar>::vec_type v256_setall< uchar>(const uchar& a) { return v256_setall_u8(a); } +template<> inline Type2Vec256_Traits< schar>::vec_type v256_setall< schar>(const schar& a) { return v256_setall_s8(a); } +template<> inline Type2Vec256_Traits::vec_type v256_setall(const ushort& a) { return v256_setall_u16(a); } +template<> inline Type2Vec256_Traits< short>::vec_type v256_setall< short>(const short& a) { return v256_setall_s16(a); } +template<> inline Type2Vec256_Traits< uint>::vec_type v256_setall< uint>(const uint& a) { return v256_setall_u32(a); } +template<> inline Type2Vec256_Traits< int>::vec_type v256_setall< int>(const int& a) { return v256_setall_s32(a); } +template<> inline Type2Vec256_Traits::vec_type v256_setall(const uint64& a) { return v256_setall_u64(a); } +template<> inline Type2Vec256_Traits< int64>::vec_type v256_setall< int64>(const int64& a) { return v256_setall_s64(a); } +template<> inline Type2Vec256_Traits< float>::vec_type v256_setall< float>(const float& a) { return v256_setall_f32(a); } +#if CV_SIMD256_64F +template<> inline Type2Vec256_Traits::vec_type v256_setall(const double& a) { return v256_setall_f64(a); } +#endif + +#endif // SIMD256 + + +#if CV_SIMD512 + +template struct Type2Vec512_Traits; +#define CV_INTRIN_DEF_TYPE2VEC512_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec512_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(uchar, v_uint8x64); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(schar, v_int8x64); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(ushort, v_uint16x32); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(short, v_int16x32); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(unsigned, v_uint32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(int, v_int32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(float, v_float32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(uint64, v_uint64x8); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(int64, v_int64x8); +#if CV_SIMD512_64F +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(double, v_float64x8); +#endif + +template static inline +typename Type2Vec512_Traits<_T>::vec_type v512_setall(const _T& a); + +template<> inline Type2Vec512_Traits< uchar>::vec_type v512_setall< uchar>(const uchar& a) { return v512_setall_u8(a); } +template<> inline Type2Vec512_Traits< schar>::vec_type v512_setall< schar>(const schar& a) { return v512_setall_s8(a); } +template<> inline Type2Vec512_Traits::vec_type v512_setall(const ushort& a) { return v512_setall_u16(a); } +template<> inline Type2Vec512_Traits< short>::vec_type v512_setall< short>(const short& a) { return v512_setall_s16(a); } +template<> inline Type2Vec512_Traits< uint>::vec_type v512_setall< uint>(const uint& a) { return v512_setall_u32(a); } +template<> inline Type2Vec512_Traits< int>::vec_type v512_setall< int>(const int& a) { return v512_setall_s32(a); } +template<> inline Type2Vec512_Traits::vec_type v512_setall(const uint64& a) { return v512_setall_u64(a); } +template<> inline Type2Vec512_Traits< int64>::vec_type v512_setall< int64>(const int64& a) { return v512_setall_s64(a); } +template<> inline Type2Vec512_Traits< float>::vec_type v512_setall< float>(const float& a) { return v512_setall_f32(a); } +#if CV_SIMD512_64F +template<> inline Type2Vec512_Traits::vec_type v512_setall(const double& a) { return v512_setall_f64(a); } +#endif + +#endif // SIMD512 + + +#if CV_SIMD_WIDTH == 16 +template static inline +typename Type2Vec128_Traits<_T>::vec_type vx_setall(const _T& a) { return v_setall(a); } +#elif CV_SIMD_WIDTH == 32 +template static inline +typename Type2Vec256_Traits<_T>::vec_type vx_setall(const _T& a) { return v256_setall(a); } +#elif CV_SIMD_WIDTH == 64 +template static inline +typename Type2Vec512_Traits<_T>::vec_type vx_setall(const _T& a) { return v512_setall(a); } +#else +#error "Build configuration error, unsupported CV_SIMD_WIDTH" +#endif + + +#endif // OPENCV_HAL_INTRIN_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/ippasync.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/ippasync.hpp new file mode 100644 index 0000000..c35d8d8 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/ippasync.hpp @@ -0,0 +1,195 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_IPPASYNC_HPP +#define OPENCV_CORE_IPPASYNC_HPP + +#ifdef HAVE_IPP_A // this file will be removed in OpenCV 4.0 + +#include "opencv2/core.hpp" +#include +#include + +namespace cv +{ + +namespace hpp +{ + +/** @addtogroup core_ipp +This section describes conversion between OpenCV and [Intel® IPP Asynchronous +C/C++](http://software.intel.com/en-us/intel-ipp-preview) library. [Getting Started +Guide](http://registrationcenter.intel.com/irc_nas/3727/ipp_async_get_started.htm) help you to +install the library, configure header and library build paths. + */ +//! @{ + + //! convert OpenCV data type to hppDataType + inline int toHppType(const int cvType) + { + int depth = CV_MAT_DEPTH(cvType); + int hppType = depth == CV_8U ? HPP_DATA_TYPE_8U : + depth == CV_16U ? HPP_DATA_TYPE_16U : + depth == CV_16S ? HPP_DATA_TYPE_16S : + depth == CV_32S ? HPP_DATA_TYPE_32S : + depth == CV_32F ? HPP_DATA_TYPE_32F : + depth == CV_64F ? HPP_DATA_TYPE_64F : -1; + CV_Assert( hppType >= 0 ); + return hppType; + } + + //! convert hppDataType to OpenCV data type + inline int toCvType(const int hppType) + { + int cvType = hppType == HPP_DATA_TYPE_8U ? CV_8U : + hppType == HPP_DATA_TYPE_16U ? CV_16U : + hppType == HPP_DATA_TYPE_16S ? CV_16S : + hppType == HPP_DATA_TYPE_32S ? CV_32S : + hppType == HPP_DATA_TYPE_32F ? CV_32F : + hppType == HPP_DATA_TYPE_64F ? CV_64F : -1; + CV_Assert( cvType >= 0 ); + return cvType; + } + + /** @brief Convert hppiMatrix to Mat. + + This function allocates and initializes new matrix (if needed) that has the same size and type as + input matrix. Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + @param src input hppiMatrix. + @param dst output matrix. + @param accel accelerator instance (see hpp::getHpp for the list of acceleration framework types). + @param cn number of channels. + */ + inline void copyHppToMat(hppiMatrix* src, Mat& dst, hppAccel accel, int cn) + { + hppDataType type; + hpp32u width, height; + hppStatus sts; + + if (src == NULL) + return dst.release(); + + sts = hppiInquireMatrix(src, &type, &width, &height); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + + int matType = CV_MAKETYPE(toCvType(type), cn); + + CV_Assert(width%cn == 0); + + width /= cn; + + dst.create((int)height, (int)width, (int)matType); + + size_t newSize = (size_t)(height*(hpp32u)(dst.step)); + + sts = hppiGetMatrixData(accel,src,(hpp32u)(dst.step),dst.data,&newSize); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + } + + /** @brief Create Mat from hppiMatrix. + + This function allocates and initializes the Mat that has the same size and type as input matrix. + Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + @param src input hppiMatrix. + @param accel accelerator instance (see hpp::getHpp for the list of acceleration framework types). + @param cn number of channels. + @sa howToUseIPPAconversion, hpp::copyHppToMat, hpp::getHpp. + */ + inline Mat getMat(hppiMatrix* src, hppAccel accel, int cn) + { + Mat dst; + copyHppToMat(src, dst, accel, cn); + return dst; + } + + /** @brief Create hppiMatrix from Mat. + + This function allocates and initializes the hppiMatrix that has the same size and type as input + matrix, returns the hppiMatrix*. + + If you want to use zero-copy for GPU you should to have 4KB aligned matrix data. See details + [hppiCreateSharedMatrix](http://software.intel.com/ru-ru/node/501697). + + Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + + @note The hppiMatrix pointer to the image buffer in system memory refers to the src.data. Control + the lifetime of the matrix and don't change its data, if there is no special need. + @param src input matrix. + @param accel accelerator instance. Supports type: + - **HPP_ACCEL_TYPE_CPU** - accelerated by optimized CPU instructions. + - **HPP_ACCEL_TYPE_GPU** - accelerated by GPU programmable units or fixed-function + accelerators. + - **HPP_ACCEL_TYPE_ANY** - any acceleration or no acceleration available. + @sa howToUseIPPAconversion, hpp::getMat + */ + inline hppiMatrix* getHpp(const Mat& src, hppAccel accel) + { + int htype = toHppType(src.type()); + int cn = src.channels(); + + CV_Assert(src.data); + hppAccelType accelType = hppQueryAccelType(accel); + + if (accelType!=HPP_ACCEL_TYPE_CPU) + { + hpp32u pitch, size; + hppQueryMatrixAllocParams(accel, src.cols*cn, src.rows, htype, &pitch, &size); + if (pitch!=0 && size!=0) + if ((int)(src.data)%4096==0 && pitch==(hpp32u)(src.step)) + { + return hppiCreateSharedMatrix(htype, src.cols*cn, src.rows, src.data, pitch, size); + } + } + + return hppiCreateMatrix(htype, src.cols*cn, src.rows, src.data, (hpp32s)(src.step));; + } + +//! @} +}} + +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/mat.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/mat.hpp new file mode 100644 index 0000000..fe16ee7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/mat.hpp @@ -0,0 +1,3764 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MAT_HPP +#define OPENCV_CORE_MAT_HPP + +#ifndef __cplusplus +# error mat.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/matx.hpp" +#include "opencv2/core/types.hpp" + +#include "opencv2/core/bufferpool.hpp" + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +enum { ACCESS_READ=1<<24, ACCESS_WRITE=1<<25, + ACCESS_RW=3<<24, ACCESS_MASK=ACCESS_RW, ACCESS_FAST=1<<26 }; + +CV__DEBUG_NS_BEGIN + +class CV_EXPORTS _OutputArray; + +//////////////////////// Input/Output Array Arguments ///////////////////////////////// + +/** @brief This is the proxy class for passing read-only input arrays into OpenCV functions. + +It is defined as: +@code + typedef const _InputArray& InputArray; +@endcode +where _InputArray is a class that can be constructed from `Mat`, `Mat_`, `Matx`, +`std::vector`, `std::vector >`, `std::vector`, `std::vector >`, +`UMat`, `std::vector` or `double`. It can also be constructed from a matrix expression. + +Since this is mostly implementation-level class, and its interface may change in future versions, we +do not describe it in details. There are a few key things, though, that should be kept in mind: + +- When you see in the reference manual or in OpenCV source code a function that takes + InputArray, it means that you can actually pass `Mat`, `Matx`, `vector` etc. (see above the + complete list). +- Optional input arguments: If some of the input arrays may be empty, pass cv::noArray() (or + simply cv::Mat() as you probably did before). +- The class is designed solely for passing parameters. That is, normally you *should not* + declare class members, local and global variables of this type. +- If you want to design your own function or a class method that can operate of arrays of + multiple types, you can use InputArray (or OutputArray) for the respective parameters. Inside + a function you should use _InputArray::getMat() method to construct a matrix header for the + array (without copying data). _InputArray::kind() can be used to distinguish Mat from + `vector<>` etc., but normally it is not needed. + +Here is how you can use a function that takes InputArray : +@code + std::vector vec; + // points or a circle + for( int i = 0; i < 30; i++ ) + vec.push_back(Point2f((float)(100 + 30*cos(i*CV_PI*2/5)), + (float)(100 - 30*sin(i*CV_PI*2/5)))); + cv::transform(vec, vec, cv::Matx23f(0.707, -0.707, 10, 0.707, 0.707, 20)); +@endcode +That is, we form an STL vector containing points, and apply in-place affine transformation to the +vector using the 2x3 matrix created inline as `Matx` instance. + +Here is how such a function can be implemented (for simplicity, we implement a very specific case of +it, according to the assertion statement inside) : +@code + void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m) + { + // get Mat headers for input arrays. This is O(1) operation, + // unless _src and/or _m are matrix expressions. + Mat src = _src.getMat(), m = _m.getMat(); + CV_Assert( src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2) ); + + // [re]create the output array so that it has the proper size and type. + // In case of Mat it calls Mat::create, in case of STL vector it calls vector::resize. + _dst.create(src.size(), src.type()); + Mat dst = _dst.getMat(); + + for( int i = 0; i < src.rows; i++ ) + for( int j = 0; j < src.cols; j++ ) + { + Point2f pt = src.at(i, j); + dst.at(i, j) = Point2f(m.at(0, 0)*pt.x + + m.at(0, 1)*pt.y + + m.at(0, 2), + m.at(1, 0)*pt.x + + m.at(1, 1)*pt.y + + m.at(1, 2)); + } + } +@endcode +There is another related type, InputArrayOfArrays, which is currently defined as a synonym for +InputArray: +@code + typedef InputArray InputArrayOfArrays; +@endcode +It denotes function arguments that are either vectors of vectors or vectors of matrices. A separate +synonym is needed to generate Python/Java etc. wrappers properly. At the function implementation +level their use is similar, but _InputArray::getMat(idx) should be used to get header for the +idx-th component of the outer vector and _InputArray::size().area() should be used to find the +number of components (vectors/matrices) of the outer vector. + +In general, type support is limited to cv::Mat types. Other types are forbidden. +But in some cases we need to support passing of custom non-general Mat types, like arrays of cv::KeyPoint, cv::DMatch, etc. +This data is not intended to be interpreted as an image data, or processed somehow like regular cv::Mat. +To pass such custom type use rawIn() / rawOut() / rawInOut() wrappers. +Custom type is wrapped as Mat-compatible `CV_8UC` values (N = sizeof(T), N <= CV_CN_MAX). + */ +class CV_EXPORTS _InputArray +{ +public: + enum { + KIND_SHIFT = 16, + FIXED_TYPE = 0x8000 << KIND_SHIFT, + FIXED_SIZE = 0x4000 << KIND_SHIFT, + KIND_MASK = 31 << KIND_SHIFT, + + NONE = 0 << KIND_SHIFT, + MAT = 1 << KIND_SHIFT, + MATX = 2 << KIND_SHIFT, + STD_VECTOR = 3 << KIND_SHIFT, + STD_VECTOR_VECTOR = 4 << KIND_SHIFT, + STD_VECTOR_MAT = 5 << KIND_SHIFT, + EXPR = 6 << KIND_SHIFT, + OPENGL_BUFFER = 7 << KIND_SHIFT, + CUDA_HOST_MEM = 8 << KIND_SHIFT, + CUDA_GPU_MAT = 9 << KIND_SHIFT, + UMAT =10 << KIND_SHIFT, + STD_VECTOR_UMAT =11 << KIND_SHIFT, + STD_BOOL_VECTOR =12 << KIND_SHIFT, + STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT, + STD_ARRAY =14 << KIND_SHIFT, + STD_ARRAY_MAT =15 << KIND_SHIFT + }; + + _InputArray(); + _InputArray(int _flags, void* _obj); + _InputArray(const Mat& m); + _InputArray(const MatExpr& expr); + _InputArray(const std::vector& vec); + template _InputArray(const Mat_<_Tp>& m); + template _InputArray(const std::vector<_Tp>& vec); + _InputArray(const std::vector& vec); + template _InputArray(const std::vector >& vec); + _InputArray(const std::vector >&); + template _InputArray(const std::vector >& vec); + template _InputArray(const _Tp* vec, int n); + template _InputArray(const Matx<_Tp, m, n>& matx); + _InputArray(const double& val); + _InputArray(const cuda::GpuMat& d_mat); + _InputArray(const std::vector& d_mat_array); + _InputArray(const ogl::Buffer& buf); + _InputArray(const cuda::HostMem& cuda_mem); + template _InputArray(const cudev::GpuMat_<_Tp>& m); + _InputArray(const UMat& um); + _InputArray(const std::vector& umv); + +#ifdef CV_CXX_STD_ARRAY + template _InputArray(const std::array<_Tp, _Nm>& arr); + template _InputArray(const std::array& arr); +#endif + + template static _InputArray rawIn(const std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _InputArray rawIn(const std::array<_Tp, _Nm>& arr); +#endif + + Mat getMat(int idx=-1) const; + Mat getMat_(int idx=-1) const; + UMat getUMat(int idx=-1) const; + void getMatVector(std::vector& mv) const; + void getUMatVector(std::vector& umv) const; + void getGpuMatVector(std::vector& gpumv) const; + cuda::GpuMat getGpuMat() const; + ogl::Buffer getOGlBuffer() const; + + int getFlags() const; + void* getObj() const; + Size getSz() const; + + int kind() const; + int dims(int i=-1) const; + int cols(int i=-1) const; + int rows(int i=-1) const; + Size size(int i=-1) const; + int sizend(int* sz, int i=-1) const; + bool sameSize(const _InputArray& arr) const; + size_t total(int i=-1) const; + int type(int i=-1) const; + int depth(int i=-1) const; + int channels(int i=-1) const; + bool isContinuous(int i=-1) const; + bool isSubmatrix(int i=-1) const; + bool empty() const; + void copyTo(const _OutputArray& arr) const; + void copyTo(const _OutputArray& arr, const _InputArray & mask) const; + size_t offset(int i=-1) const; + size_t step(int i=-1) const; + bool isMat() const; + bool isUMat() const; + bool isMatVector() const; + bool isUMatVector() const; + bool isMatx() const; + bool isVector() const; + bool isGpuMat() const; + bool isGpuMatVector() const; + ~_InputArray(); + +protected: + int flags; + void* obj; + Size sz; + + void init(int _flags, const void* _obj); + void init(int _flags, const void* _obj, Size _sz); +}; + + +/** @brief This type is very similar to InputArray except that it is used for input/output and output function +parameters. + +Just like with InputArray, OpenCV users should not care about OutputArray, they just pass `Mat`, +`vector` etc. to the functions. The same limitation as for `InputArray`: *Do not explicitly +create OutputArray instances* applies here too. + +If you want to make your function polymorphic (i.e. accept different arrays as output parameters), +it is also not very difficult. Take the sample above as the reference. Note that +_OutputArray::create() needs to be called before _OutputArray::getMat(). This way you guarantee +that the output array is properly allocated. + +Optional output parameters. If you do not need certain output array to be computed and returned to +you, pass cv::noArray(), just like you would in the case of optional input array. At the +implementation level, use _OutputArray::needed() to check if certain output array needs to be +computed or not. + +There are several synonyms for OutputArray that are used to assist automatic Python/Java/... wrapper +generators: +@code + typedef OutputArray OutputArrayOfArrays; + typedef OutputArray InputOutputArray; + typedef OutputArray InputOutputArrayOfArrays; +@endcode + */ +class CV_EXPORTS _OutputArray : public _InputArray +{ +public: + enum + { + DEPTH_MASK_8U = 1 << CV_8U, + DEPTH_MASK_8S = 1 << CV_8S, + DEPTH_MASK_16U = 1 << CV_16U, + DEPTH_MASK_16S = 1 << CV_16S, + DEPTH_MASK_32S = 1 << CV_32S, + DEPTH_MASK_32F = 1 << CV_32F, + DEPTH_MASK_64F = 1 << CV_64F, + DEPTH_MASK_ALL = (DEPTH_MASK_64F<<1)-1, + DEPTH_MASK_ALL_BUT_8S = DEPTH_MASK_ALL & ~DEPTH_MASK_8S, + DEPTH_MASK_FLT = DEPTH_MASK_32F + DEPTH_MASK_64F + }; + + _OutputArray(); + _OutputArray(int _flags, void* _obj); + _OutputArray(Mat& m); + _OutputArray(std::vector& vec); + _OutputArray(cuda::GpuMat& d_mat); + _OutputArray(std::vector& d_mat); + _OutputArray(ogl::Buffer& buf); + _OutputArray(cuda::HostMem& cuda_mem); + template _OutputArray(cudev::GpuMat_<_Tp>& m); + template _OutputArray(std::vector<_Tp>& vec); + _OutputArray(std::vector& vec); + template _OutputArray(std::vector >& vec); + _OutputArray(std::vector >&); + template _OutputArray(std::vector >& vec); + template _OutputArray(Mat_<_Tp>& m); + template _OutputArray(_Tp* vec, int n); + template _OutputArray(Matx<_Tp, m, n>& matx); + _OutputArray(UMat& m); + _OutputArray(std::vector& vec); + + _OutputArray(const Mat& m); + _OutputArray(const std::vector& vec); + _OutputArray(const cuda::GpuMat& d_mat); + _OutputArray(const std::vector& d_mat); + _OutputArray(const ogl::Buffer& buf); + _OutputArray(const cuda::HostMem& cuda_mem); + template _OutputArray(const cudev::GpuMat_<_Tp>& m); + template _OutputArray(const std::vector<_Tp>& vec); + template _OutputArray(const std::vector >& vec); + template _OutputArray(const std::vector >& vec); + template _OutputArray(const Mat_<_Tp>& m); + template _OutputArray(const _Tp* vec, int n); + template _OutputArray(const Matx<_Tp, m, n>& matx); + _OutputArray(const UMat& m); + _OutputArray(const std::vector& vec); + +#ifdef CV_CXX_STD_ARRAY + template _OutputArray(std::array<_Tp, _Nm>& arr); + template _OutputArray(const std::array<_Tp, _Nm>& arr); + template _OutputArray(std::array& arr); + template _OutputArray(const std::array& arr); +#endif + + template static _OutputArray rawOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _OutputArray rawOut(std::array<_Tp, _Nm>& arr); +#endif + + bool fixedSize() const; + bool fixedType() const; + bool needed() const; + Mat& getMatRef(int i=-1) const; + UMat& getUMatRef(int i=-1) const; + cuda::GpuMat& getGpuMatRef() const; + std::vector& getGpuMatVecRef() const; + ogl::Buffer& getOGlBufferRef() const; + cuda::HostMem& getHostMemRef() const; + void create(Size sz, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void create(int rows, int cols, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void create(int dims, const int* size, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void createSameSize(const _InputArray& arr, int mtype) const; + void release() const; + void clear() const; + void setTo(const _InputArray& value, const _InputArray & mask = _InputArray()) const; + + void assign(const UMat& u) const; + void assign(const Mat& m) const; + + void assign(const std::vector& v) const; + void assign(const std::vector& v) const; + + void move(UMat& u) const; + void move(Mat& m) const; +}; + + +class CV_EXPORTS _InputOutputArray : public _OutputArray +{ +public: + _InputOutputArray(); + _InputOutputArray(int _flags, void* _obj); + _InputOutputArray(Mat& m); + _InputOutputArray(std::vector& vec); + _InputOutputArray(cuda::GpuMat& d_mat); + _InputOutputArray(ogl::Buffer& buf); + _InputOutputArray(cuda::HostMem& cuda_mem); + template _InputOutputArray(cudev::GpuMat_<_Tp>& m); + template _InputOutputArray(std::vector<_Tp>& vec); + _InputOutputArray(std::vector& vec); + template _InputOutputArray(std::vector >& vec); + template _InputOutputArray(std::vector >& vec); + template _InputOutputArray(Mat_<_Tp>& m); + template _InputOutputArray(_Tp* vec, int n); + template _InputOutputArray(Matx<_Tp, m, n>& matx); + _InputOutputArray(UMat& m); + _InputOutputArray(std::vector& vec); + + _InputOutputArray(const Mat& m); + _InputOutputArray(const std::vector& vec); + _InputOutputArray(const cuda::GpuMat& d_mat); + _InputOutputArray(const std::vector& d_mat); + _InputOutputArray(const ogl::Buffer& buf); + _InputOutputArray(const cuda::HostMem& cuda_mem); + template _InputOutputArray(const cudev::GpuMat_<_Tp>& m); + template _InputOutputArray(const std::vector<_Tp>& vec); + template _InputOutputArray(const std::vector >& vec); + template _InputOutputArray(const std::vector >& vec); + template _InputOutputArray(const Mat_<_Tp>& m); + template _InputOutputArray(const _Tp* vec, int n); + template _InputOutputArray(const Matx<_Tp, m, n>& matx); + _InputOutputArray(const UMat& m); + _InputOutputArray(const std::vector& vec); + +#ifdef CV_CXX_STD_ARRAY + template _InputOutputArray(std::array<_Tp, _Nm>& arr); + template _InputOutputArray(const std::array<_Tp, _Nm>& arr); + template _InputOutputArray(std::array& arr); + template _InputOutputArray(const std::array& arr); +#endif + + template static _InputOutputArray rawInOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template _InputOutputArray rawInOut(std::array<_Tp, _Nm>& arr); +#endif + +}; + +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputArray rawIn(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _OutputArray rawOut(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputOutputArray rawInOut(_Tp& v); + +CV__DEBUG_NS_END + +typedef const _InputArray& InputArray; +typedef InputArray InputArrayOfArrays; +typedef const _OutputArray& OutputArray; +typedef OutputArray OutputArrayOfArrays; +typedef const _InputOutputArray& InputOutputArray; +typedef InputOutputArray InputOutputArrayOfArrays; + +CV_EXPORTS InputOutputArray noArray(); + +/////////////////////////////////// MatAllocator ////////////////////////////////////// + +//! Usage flags for allocator +enum UMatUsageFlags +{ + USAGE_DEFAULT = 0, + + // buffer allocation policy is platform and usage specific + USAGE_ALLOCATE_HOST_MEMORY = 1 << 0, + USAGE_ALLOCATE_DEVICE_MEMORY = 1 << 1, + USAGE_ALLOCATE_SHARED_MEMORY = 1 << 2, // It is not equal to: USAGE_ALLOCATE_HOST_MEMORY | USAGE_ALLOCATE_DEVICE_MEMORY + + __UMAT_USAGE_FLAGS_32BIT = 0x7fffffff // Binary compatibility hint +}; + +struct CV_EXPORTS UMatData; + +/** @brief Custom array allocator +*/ +class CV_EXPORTS MatAllocator +{ +public: + MatAllocator() {} + virtual ~MatAllocator() {} + + // let's comment it off for now to detect and fix all the uses of allocator + //virtual void allocate(int dims, const int* sizes, int type, int*& refcount, + // uchar*& datastart, uchar*& data, size_t* step) = 0; + //virtual void deallocate(int* refcount, uchar* datastart, uchar* data) = 0; + virtual UMatData* allocate(int dims, const int* sizes, int type, + void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const = 0; + virtual bool allocate(UMatData* data, int accessflags, UMatUsageFlags usageFlags) const = 0; + virtual void deallocate(UMatData* data) const = 0; + virtual void map(UMatData* data, int accessflags) const; + virtual void unmap(UMatData* data) const; + virtual void download(UMatData* data, void* dst, int dims, const size_t sz[], + const size_t srcofs[], const size_t srcstep[], + const size_t dststep[]) const; + virtual void upload(UMatData* data, const void* src, int dims, const size_t sz[], + const size_t dstofs[], const size_t dststep[], + const size_t srcstep[]) const; + virtual void copy(UMatData* srcdata, UMatData* dstdata, int dims, const size_t sz[], + const size_t srcofs[], const size_t srcstep[], + const size_t dstofs[], const size_t dststep[], bool sync) const; + + // default implementation returns DummyBufferPoolController + virtual BufferPoolController* getBufferPoolController(const char* id = NULL) const; +}; + + +//////////////////////////////// MatCommaInitializer ////////////////////////////////// + +/** @brief Comma-separated Matrix Initializer + + The class instances are usually not created explicitly. + Instead, they are created on "matrix << firstValue" operator. + + The sample below initializes 2x2 rotation matrix: + + \code + double angle = 30, a = cos(angle*CV_PI/180), b = sin(angle*CV_PI/180); + Mat R = (Mat_(2,2) << a, -b, b, a); + \endcode +*/ +template class MatCommaInitializer_ +{ +public: + //! the constructor, created by "matrix << firstValue" operator, where matrix is cv::Mat + MatCommaInitializer_(Mat_<_Tp>* _m); + //! the operator that takes the next value and put it to the matrix + template MatCommaInitializer_<_Tp>& operator , (T2 v); + //! another form of conversion operator + operator Mat_<_Tp>() const; +protected: + MatIterator_<_Tp> it; +}; + + +/////////////////////////////////////// Mat /////////////////////////////////////////// + +// note that umatdata might be allocated together +// with the matrix data, not as a separate object. +// therefore, it does not have constructor or destructor; +// it should be explicitly initialized using init(). +struct CV_EXPORTS UMatData +{ + enum { COPY_ON_MAP=1, HOST_COPY_OBSOLETE=2, + DEVICE_COPY_OBSOLETE=4, TEMP_UMAT=8, TEMP_COPIED_UMAT=24, + USER_ALLOCATED=32, DEVICE_MEM_MAPPED=64, + ASYNC_CLEANUP=128 + }; + UMatData(const MatAllocator* allocator); + ~UMatData(); + + // provide atomic access to the structure + void lock(); + void unlock(); + + bool hostCopyObsolete() const; + bool deviceCopyObsolete() const; + bool deviceMemMapped() const; + bool copyOnMap() const; + bool tempUMat() const; + bool tempCopiedUMat() const; + void markHostCopyObsolete(bool flag); + void markDeviceCopyObsolete(bool flag); + void markDeviceMemMapped(bool flag); + + const MatAllocator* prevAllocator; + const MatAllocator* currAllocator; + int urefcount; + int refcount; + uchar* data; + uchar* origdata; + size_t size; + + int flags; + void* handle; + void* userdata; + int allocatorFlags_; + int mapcount; + UMatData* originalUMatData; +}; + + +struct CV_EXPORTS MatSize +{ + explicit MatSize(int* _p); + int dims() const; + Size operator()() const; + const int& operator[](int i) const; + int& operator[](int i); + operator const int*() const; // TODO OpenCV 4.0: drop this + bool operator == (const MatSize& sz) const; + bool operator != (const MatSize& sz) const; + + int* p; +}; + +struct CV_EXPORTS MatStep +{ + MatStep(); + explicit MatStep(size_t s); + const size_t& operator[](int i) const; + size_t& operator[](int i); + operator size_t() const; + MatStep& operator = (size_t s); + + size_t* p; + size_t buf[2]; +protected: + MatStep& operator = (const MatStep&); +}; + +/** @example samples/cpp/cout_mat.cpp +An example demonstrating the serial out capabilities of cv::Mat +*/ + + /** @brief n-dimensional dense array class \anchor CVMat_Details + +The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It +can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel +volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms +may be better stored in a SparseMat ). The data layout of the array `M` is defined by the array +`M.step[]`, so that the address of element \f$(i_0,...,i_{M.dims-1})\f$, where \f$0\leq i_k= M.step[i+1]` (in fact, `M.step[i] >= M.step[i+1]*M.size[i+1]` ). This means +that 2-dimensional matrices are stored row-by-row, 3-dimensional matrices are stored plane-by-plane, +and so on. M.step[M.dims-1] is minimal and always equal to the element size M.elemSize() . + +So, the data layout in Mat is fully compatible with CvMat, IplImage, and CvMatND types from OpenCV +1.x. It is also compatible with the majority of dense array types from the standard toolkits and +SDKs, such as Numpy (ndarray), Win32 (independent device bitmaps), and others, that is, with any +array that uses *steps* (or *strides*) to compute the position of a pixel. Due to this +compatibility, it is possible to make a Mat header for user-allocated data and process it in-place +using OpenCV functions. + +There are many different ways to create a Mat object. The most popular options are listed below: + +- Use the create(nrows, ncols, type) method or the similar Mat(nrows, ncols, type[, fillValue]) +constructor. A new array of the specified size and type is allocated. type has the same meaning as +in the cvCreateMat method. For example, CV_8UC1 means a 8-bit single-channel array, CV_32FC2 +means a 2-channel (complex) floating-point array, and so on. +@code + // make a 7x7 complex matrix filled with 1+3j. + Mat M(7,7,CV_32FC2,Scalar(1,3)); + // and now turn M to a 100x60 15-channel 8-bit matrix. + // The old content will be deallocated + M.create(100,60,CV_8UC(15)); +@endcode +As noted in the introduction to this chapter, create() allocates only a new array when the shape +or type of the current array are different from the specified ones. + +- Create a multi-dimensional array: +@code + // create a 100x100x100 8-bit array + int sz[] = {100, 100, 100}; + Mat bigCube(3, sz, CV_8U, Scalar::all(0)); +@endcode +It passes the number of dimensions =1 to the Mat constructor but the created array will be +2-dimensional with the number of columns set to 1. So, Mat::dims is always \>= 2 (can also be 0 +when the array is empty). + +- Use a copy constructor or assignment operator where there can be an array or expression on the +right side (see below). As noted in the introduction, the array assignment is an O(1) operation +because it only copies the header and increases the reference counter. The Mat::clone() method can +be used to get a full (deep) copy of the array when you need it. + +- Construct a header for a part of another array. It can be a single row, single column, several +rows, several columns, rectangular region in the array (called a *minor* in algebra) or a +diagonal. Such operations are also O(1) because the new header references the same data. You can +actually modify a part of the array using this feature, for example: +@code + // add the 5-th row, multiplied by 3 to the 3rd row + M.row(3) = M.row(3) + M.row(5)*3; + // now copy the 7-th column to the 1-st column + // M.col(1) = M.col(7); // this will not work + Mat M1 = M.col(1); + M.col(7).copyTo(M1); + // create a new 320x240 image + Mat img(Size(320,240),CV_8UC3); + // select a ROI + Mat roi(img, Rect(10,10,100,100)); + // fill the ROI with (0,255,0) (which is green in RGB space); + // the original 320x240 image will be modified + roi = Scalar(0,255,0); +@endcode +Due to the additional datastart and dataend members, it is possible to compute a relative +sub-array position in the main *container* array using locateROI(): +@code + Mat A = Mat::eye(10, 10, CV_32S); + // extracts A columns, 1 (inclusive) to 3 (exclusive). + Mat B = A(Range::all(), Range(1, 3)); + // extracts B rows, 5 (inclusive) to 9 (exclusive). + // that is, C \~ A(Range(5, 9), Range(1, 3)) + Mat C = B(Range(5, 9), Range::all()); + Size size; Point ofs; + C.locateROI(size, ofs); + // size will be (width=10,height=10) and the ofs will be (x=1, y=5) +@endcode +As in case of whole matrices, if you need a deep copy, use the `clone()` method of the extracted +sub-matrices. + +- Make a header for user-allocated data. It can be useful to do the following: + -# Process "foreign" data using OpenCV (for example, when you implement a DirectShow\* filter or + a processing module for gstreamer, and so on). For example: + @code + void process_video_frame(const unsigned char* pixels, + int width, int height, int step) + { + Mat img(height, width, CV_8UC3, pixels, step); + GaussianBlur(img, img, Size(7,7), 1.5, 1.5); + } + @endcode + -# Quickly initialize small matrices and/or get a super-fast element access. + @code + double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}}; + Mat M = Mat(3, 3, CV_64F, m).inv(); + @endcode + . + Partial yet very common cases of this *user-allocated data* case are conversions from CvMat and + IplImage to Mat. For this purpose, there is function cv::cvarrToMat taking pointers to CvMat or + IplImage and the optional flag indicating whether to copy the data or not. + @snippet samples/cpp/image.cpp iplimage + +- Use MATLAB-style array initializers, zeros(), ones(), eye(), for example: +@code + // create a double-precision identity matrix and add it to M. + M += Mat::eye(M.rows, M.cols, CV_64F); +@endcode + +- Use a comma-separated initializer: +@code + // create a 3x3 double-precision identity matrix + Mat M = (Mat_(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); +@endcode +With this approach, you first call a constructor of the Mat class with the proper parameters, and +then you just put `<< operator` followed by comma-separated values that can be constants, +variables, expressions, and so on. Also, note the extra parentheses required to avoid compilation +errors. + +Once the array is created, it is automatically managed via a reference-counting mechanism. If the +array header is built on top of user-allocated data, you should handle the data by yourself. The +array data is deallocated when no one points to it. If you want to release the data pointed by a +array header before the array destructor is called, use Mat::release(). + +The next important thing to learn about the array class is element access. This manual already +described how to compute an address of each array element. Normally, you are not required to use the +formula directly in the code. If you know the array element type (which can be retrieved using the +method Mat::type() ), you can access the element \f$M_{ij}\f$ of a 2-dimensional array as: +@code + M.at(i,j) += 1.f; +@endcode +assuming that `M` is a double-precision floating-point array. There are several variants of the method +at for a different number of dimensions. + +If you need to process a whole row of a 2D array, the most efficient way is to get the pointer to +the row first, and then just use the plain C operator [] : +@code + // compute sum of positive matrix elements + // (assuming that M is a double-precision matrix) + double sum=0; + for(int i = 0; i < M.rows; i++) + { + const double* Mi = M.ptr(i); + for(int j = 0; j < M.cols; j++) + sum += std::max(Mi[j], 0.); + } +@endcode +Some operations, like the one above, do not actually depend on the array shape. They just process +elements of an array one by one (or elements from multiple arrays that have the same coordinates, +for example, array addition). Such operations are called *element-wise*. It makes sense to check +whether all the input/output arrays are continuous, namely, have no gaps at the end of each row. If +yes, process them as a long single row: +@code + // compute the sum of positive matrix elements, optimized variant + double sum=0; + int cols = M.cols, rows = M.rows; + if(M.isContinuous()) + { + cols *= rows; + rows = 1; + } + for(int i = 0; i < rows; i++) + { + const double* Mi = M.ptr(i); + for(int j = 0; j < cols; j++) + sum += std::max(Mi[j], 0.); + } +@endcode +In case of the continuous matrix, the outer loop body is executed just once. So, the overhead is +smaller, which is especially noticeable in case of small matrices. + +Finally, there are STL-style iterators that are smart enough to skip gaps between successive rows: +@code + // compute sum of positive matrix elements, iterator-based variant + double sum=0; + MatConstIterator_ it = M.begin(), it_end = M.end(); + for(; it != it_end; ++it) + sum += std::max(*it, 0.); +@endcode +The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, +including std::sort(). + +@note Matrix Expressions and arithmetic see MatExpr +*/ +class CV_EXPORTS Mat +{ +public: + /** + These are various constructors that form a matrix. As noted in the AutomaticAllocation, often + the default constructor is enough, and the proper matrix will be allocated by an OpenCV function. + The constructed matrix can further be assigned to another matrix or matrix expression or can be + allocated with Mat::create . In the former case, the old content is de-referenced. + */ + Mat(); + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(int rows, int cols, int type); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(Size size, int type); + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(int rows, int cols, int type, const Scalar& s); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(Size size, int type, const Scalar& s); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(int ndims, const int* sizes, int type); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(const std::vector& sizes, int type); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(int ndims, const int* sizes, int type, const Scalar& s); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(const std::vector& sizes, int type, const Scalar& s); + + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + */ + Mat(const Mat& m); + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param step Number of bytes each matrix row occupies. The value should include the padding bytes at + the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed + and the actual step is calculated as cols*elemSize(). See Mat::elemSize. + */ + Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param step Number of bytes each matrix row occupies. The value should include the padding bytes at + the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed + and the actual step is calculated as cols*elemSize(). See Mat::elemSize. + */ + Mat(Size size, int type, void* data, size_t step=AUTO_STEP); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param steps Array of ndims-1 steps in case of a multi-dimensional array (the last step is always + set to the element size). If not specified, the matrix is assumed to be continuous. + */ + Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param steps Array of ndims-1 steps in case of a multi-dimensional array (the last step is always + set to the element size). If not specified, the matrix is assumed to be continuous. + */ + Mat(const std::vector& sizes, int type, void* data, const size_t* steps=0); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param rowRange Range of the m rows to take. As usual, the range start is inclusive and the range + end is exclusive. Use Range::all() to take all the rows. + @param colRange Range of the m columns to take. Use Range::all() to take all the columns. + */ + Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param roi Region of interest. + */ + Mat(const Mat& m, const Rect& roi); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param ranges Array of selected ranges of m along each dimensionality. + */ + Mat(const Mat& m, const Range* ranges); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param ranges Array of selected ranges of m along each dimensionality. + */ + Mat(const Mat& m, const std::vector& ranges); + + /** @overload + @param vec STL vector whose elements form the matrix. The matrix has a single column and the number + of rows equal to the number of vector elements. Type of the matrix matches the type of vector + elements. The constructor can handle arbitrary types, for which there is a properly declared + DataType . This means that the vector elements must be primitive numbers or uni-type numerical + tuples of numbers. Mixed-type structures are not supported. The corresponding constructor is + explicit. Since STL vectors are not automatically converted to Mat instances, you should write + Mat(vec) explicitly. Unless you copy the data into the matrix ( copyData=true ), no new elements + will be added to the vector because it can potentially yield vector data reallocation, and, thus, + the matrix data pointer will be invalid. + @param copyData Flag to specify whether the underlying data of the STL vector should be copied + to (true) or shared with (false) the newly constructed matrix. When the data is copied, the + allocated buffer is managed using Mat reference counting mechanism. While the data is shared, + the reference counter is NULL, and you should not deallocate the data until the matrix is not + destructed. + */ + template explicit Mat(const std::vector<_Tp>& vec, bool copyData=false); + +#ifdef CV_CXX11 + /** @overload + */ + template::value>::type> + explicit Mat(const std::initializer_list<_Tp> list); + + /** @overload + */ + template explicit Mat(const std::initializer_list sizes, const std::initializer_list<_Tp> list); +#endif + +#ifdef CV_CXX_STD_ARRAY + /** @overload + */ + template explicit Mat(const std::array<_Tp, _Nm>& arr, bool copyData=false); +#endif + + /** @overload + */ + template explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Point_<_Tp>& pt, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Point3_<_Tp>& pt, bool copyData=true); + + /** @overload + */ + template explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer); + + //! download data from GpuMat + explicit Mat(const cuda::GpuMat& m); + + //! destructor - calls release() + ~Mat(); + + /** @brief assignment operators + + These are available assignment operators. Since they all are very different, make sure to read the + operator parameters description. + @param m Assigned, right-hand-side matrix. Matrix assignment is an O(1) operation. This means that + no data is copied but the data is shared and the reference counter, if any, is incremented. Before + assigning new data, the old data is de-referenced via Mat::release . + */ + Mat& operator = (const Mat& m); + + /** @overload + @param expr Assigned matrix expression object. As opposite to the first form of the assignment + operation, the second form can reuse already allocated matrix if it has the right size and type to + fit the matrix expression result. It is automatically handled by the real function that the matrix + expressions is expanded to. For example, C=A+B is expanded to add(A, B, C), and add takes care of + automatic C reallocation. + */ + Mat& operator = (const MatExpr& expr); + + //! retrieve UMat from Mat + UMat getUMat(int accessFlags, UMatUsageFlags usageFlags = USAGE_DEFAULT) const; + + /** @brief Creates a matrix header for the specified matrix row. + + The method makes a new header for the specified matrix row and returns it. This is an O(1) + operation, regardless of the matrix size. The underlying data of the new matrix is shared with the + original matrix. Here is the example of one of the classical basic matrix processing operations, + axpy, used by LU and many other algorithms: + @code + inline void matrix_axpy(Mat& A, int i, int j, double alpha) + { + A.row(i) += A.row(j)*alpha; + } + @endcode + @note In the current implementation, the following code does not work as expected: + @code + Mat A; + ... + A.row(i) = A.row(j); // will not work + @endcode + This happens because A.row(i) forms a temporary header that is further assigned to another header. + Remember that each of these operations is O(1), that is, no data is copied. Thus, the above + assignment is not true if you may have expected the j-th row to be copied to the i-th row. To + achieve that, you should either turn this simple assignment into an expression or use the + Mat::copyTo method: + @code + Mat A; + ... + // works, but looks a bit obscure. + A.row(i) = A.row(j) + 0; + // this is a bit longer, but the recommended method. + A.row(j).copyTo(A.row(i)); + @endcode + @param y A 0-based row index. + */ + Mat row(int y) const; + + /** @brief Creates a matrix header for the specified matrix column. + + The method makes a new header for the specified matrix column and returns it. This is an O(1) + operation, regardless of the matrix size. The underlying data of the new matrix is shared with the + original matrix. See also the Mat::row description. + @param x A 0-based column index. + */ + Mat col(int x) const; + + /** @brief Creates a matrix header for the specified row span. + + The method makes a new header for the specified row span of the matrix. Similarly to Mat::row and + Mat::col , this is an O(1) operation. + @param startrow An inclusive 0-based start index of the row span. + @param endrow An exclusive 0-based ending index of the row span. + */ + Mat rowRange(int startrow, int endrow) const; + + /** @overload + @param r Range structure containing both the start and the end indices. + */ + Mat rowRange(const Range& r) const; + + /** @brief Creates a matrix header for the specified column span. + + The method makes a new header for the specified column span of the matrix. Similarly to Mat::row and + Mat::col , this is an O(1) operation. + @param startcol An inclusive 0-based start index of the column span. + @param endcol An exclusive 0-based ending index of the column span. + */ + Mat colRange(int startcol, int endcol) const; + + /** @overload + @param r Range structure containing both the start and the end indices. + */ + Mat colRange(const Range& r) const; + + /** @brief Extracts a diagonal from a matrix + + The method makes a new header for the specified matrix diagonal. The new matrix is represented as a + single-column matrix. Similarly to Mat::row and Mat::col, this is an O(1) operation. + @param d index of the diagonal, with the following values: + - `d=0` is the main diagonal. + - `d<0` is a diagonal from the lower half. For example, d=-1 means the diagonal is set + immediately below the main one. + - `d>0` is a diagonal from the upper half. For example, d=1 means the diagonal is set + immediately above the main one. + For example: + @code + Mat m = (Mat_(3,3) << + 1,2,3, + 4,5,6, + 7,8,9); + Mat d0 = m.diag(0); + Mat d1 = m.diag(1); + Mat d_1 = m.diag(-1); + @endcode + The resulting matrices are + @code + d0 = + [1; + 5; + 9] + d1 = + [2; + 6] + d_1 = + [4; + 8] + @endcode + */ + Mat diag(int d=0) const; + + /** @brief creates a diagonal matrix + + The method creates a square diagonal matrix from specified main diagonal. + @param d One-dimensional matrix that represents the main diagonal. + */ + static Mat diag(const Mat& d); + + /** @brief Creates a full copy of the array and the underlying data. + + The method creates a full copy of the array. The original step[] is not taken into account. So, the + array copy is a continuous array occupying total()*elemSize() bytes. + */ + Mat clone() const CV_NODISCARD; + + /** @brief Copies the matrix to another one. + + The method copies the matrix data to another matrix. Before copying the data, the method invokes : + @code + m.create(this->size(), this->type()); + @endcode + so that the destination matrix is reallocated if needed. While m.copyTo(m); works flawlessly, the + function does not handle the case of a partial overlap between the source and the destination + matrices. + + When the operation mask is specified, if the Mat::create call shown above reallocates the matrix, + the newly allocated matrix is initialized with all zeros before copying the data. + @param m Destination matrix. If it does not have a proper size or type before the operation, it is + reallocated. + */ + void copyTo( OutputArray m ) const; + + /** @overload + @param m Destination matrix. If it does not have a proper size or type before the operation, it is + reallocated. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. + */ + void copyTo( OutputArray m, InputArray mask ) const; + + /** @brief Converts an array to another data type with optional scaling. + + The method converts source pixel values to the target data type. saturate_cast\<\> is applied at + the end to avoid possible overflows: + + \f[m(x,y) = saturate \_ cast( \alpha (*this)(x,y) + \beta )\f] + @param m output matrix; if it does not have a proper size or type before the operation, it is + reallocated. + @param rtype desired output matrix type or, rather, the depth since the number of channels are the + same as the input has; if rtype is negative, the output matrix will have the same type as the input. + @param alpha optional scale factor. + @param beta optional delta added to the scaled values. + */ + void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; + + /** @brief Provides a functional form of convertTo. + + This is an internally used method called by the @ref MatrixExpressions engine. + @param m Destination array. + @param type Desired destination array depth (or -1 if it should be the same as the source type). + */ + void assignTo( Mat& m, int type=-1 ) const; + + /** @brief Sets all or some of the array elements to the specified value. + @param s Assigned scalar converted to the actual array type. + */ + Mat& operator = (const Scalar& s); + + /** @brief Sets all or some of the array elements to the specified value. + + This is an advanced variant of the Mat::operator=(const Scalar& s) operator. + @param value Assigned scalar converted to the actual array type. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels + */ + Mat& setTo(InputArray value, InputArray mask=noArray()); + + /** @brief Changes the shape and/or the number of channels of a 2D matrix without copying the data. + + The method makes a new matrix header for \*this elements. The new matrix may have a different size + and/or different number of channels. Any combination is possible if: + - No extra elements are included into the new matrix and no elements are excluded. Consequently, + the product rows\*cols\*channels() must stay the same after the transformation. + - No data is copied. That is, this is an O(1) operation. Consequently, if you change the number of + rows, or the operation changes the indices of elements row in some other way, the matrix must be + continuous. See Mat::isContinuous . + + For example, if there is a set of 3D points stored as an STL vector, and you want to represent the + points as a 3xN matrix, do the following: + @code + std::vector vec; + ... + Mat pointMat = Mat(vec). // convert vector to Mat, O(1) operation + reshape(1). // make Nx3 1-channel matrix out of Nx1 3-channel. + // Also, an O(1) operation + t(); // finally, transpose the Nx3 matrix. + // This involves copying all the elements + @endcode + @param cn New number of channels. If the parameter is 0, the number of channels remains the same. + @param rows New number of rows. If the parameter is 0, the number of rows remains the same. + */ + Mat reshape(int cn, int rows=0) const; + + /** @overload */ + Mat reshape(int cn, int newndims, const int* newsz) const; + + /** @overload */ + Mat reshape(int cn, const std::vector& newshape) const; + + /** @brief Transposes a matrix. + + The method performs matrix transposition by means of matrix expressions. It does not perform the + actual transposition but returns a temporary matrix transposition object that can be further used as + a part of more complex matrix expressions or can be assigned to a matrix: + @code + Mat A1 = A + Mat::eye(A.size(), A.type())*lambda; + Mat C = A1.t()*A1; // compute (A + lambda*I)^t * (A + lamda*I) + @endcode + */ + MatExpr t() const; + + /** @brief Inverses a matrix. + + The method performs a matrix inversion by means of matrix expressions. This means that a temporary + matrix inversion object is returned by the method and can be used further as a part of more complex + matrix expressions or can be assigned to a matrix. + @param method Matrix inversion method. One of cv::DecompTypes + */ + MatExpr inv(int method=DECOMP_LU) const; + + /** @brief Performs an element-wise multiplication or division of the two matrices. + + The method returns a temporary object encoding per-element array multiplication, with optional + scale. Note that this is not a matrix multiplication that corresponds to a simpler "\*" operator. + + Example: + @code + Mat C = A.mul(5/B); // equivalent to divide(A, B, C, 5) + @endcode + @param m Another array of the same type and the same size as \*this, or a matrix expression. + @param scale Optional scale factor. + */ + MatExpr mul(InputArray m, double scale=1) const; + + /** @brief Computes a cross-product of two 3-element vectors. + + The method computes a cross-product of two 3-element vectors. The vectors must be 3-element + floating-point vectors of the same shape and size. The result is another 3-element vector of the + same shape and type as operands. + @param m Another cross-product operand. + */ + Mat cross(InputArray m) const; + + /** @brief Computes a dot-product of two vectors. + + The method computes a dot-product of two matrices. If the matrices are not single-column or + single-row vectors, the top-to-bottom left-to-right scan ordering is used to treat them as 1D + vectors. The vectors must have the same size and type. If the matrices have more than one channel, + the dot products from all the channels are summed together. + @param m another dot-product operand. + */ + double dot(InputArray m) const; + + /** @brief Returns a zero array of the specified size and type. + + The method returns a Matlab-style zero array initializer. It can be used to quickly form a constant + array as a function parameter, part of a matrix expression, or as a matrix initializer: + @code + Mat A; + A = Mat::zeros(3, 3, CV_32F); + @endcode + In the example above, a new matrix is allocated only if A is not a 3x3 floating-point matrix. + Otherwise, the existing matrix A is filled with zeros. + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr zeros(int rows, int cols, int type); + + /** @overload + @param size Alternative to the matrix size specification Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr zeros(Size size, int type); + + /** @overload + @param ndims Array dimensionality. + @param sz Array of integers specifying the array shape. + @param type Created matrix type. + */ + static MatExpr zeros(int ndims, const int* sz, int type); + + /** @brief Returns an array of all 1's of the specified size and type. + + The method returns a Matlab-style 1's array initializer, similarly to Mat::zeros. Note that using + this method you can initialize an array with an arbitrary value, using the following Matlab idiom: + @code + Mat A = Mat::ones(100, 100, CV_8U)*3; // make 100x100 matrix filled with 3. + @endcode + The above operation does not form a 100x100 matrix of 1's and then multiply it by 3. Instead, it + just remembers the scale factor (3 in this case) and use it when actually invoking the matrix + initializer. + @note In case of multi-channels type, only the first channel will be initialized with 1's, the + others will be set to 0's. + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr ones(int rows, int cols, int type); + + /** @overload + @param size Alternative to the matrix size specification Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr ones(Size size, int type); + + /** @overload + @param ndims Array dimensionality. + @param sz Array of integers specifying the array shape. + @param type Created matrix type. + */ + static MatExpr ones(int ndims, const int* sz, int type); + + /** @brief Returns an identity matrix of the specified size and type. + + The method returns a Matlab-style identity matrix initializer, similarly to Mat::zeros. Similarly to + Mat::ones, you can use a scale operation to create a scaled identity matrix efficiently: + @code + // make a 4x4 diagonal matrix with 0.1's on the diagonal. + Mat A = Mat::eye(4, 4, CV_32F)*0.1; + @endcode + @note In case of multi-channels type, identity matrix will be initialized only for the first channel, + the others will be set to 0's + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr eye(int rows, int cols, int type); + + /** @overload + @param size Alternative matrix size specification as Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr eye(Size size, int type); + + /** @brief Allocates new array data if needed. + + This is one of the key Mat methods. Most new-style OpenCV functions and methods that produce arrays + call this method for each output array. The method uses the following algorithm: + + -# If the current array shape and the type match the new ones, return immediately. Otherwise, + de-reference the previous data by calling Mat::release. + -# Initialize the new header. + -# Allocate the new data of total()\*elemSize() bytes. + -# Allocate the new, associated with the data, reference counter and set it to 1. + + Such a scheme makes the memory management robust and efficient at the same time and helps avoid + extra typing for you. This means that usually there is no need to explicitly allocate output arrays. + That is, instead of writing: + @code + Mat color; + ... + Mat gray(color.rows, color.cols, color.depth()); + cvtColor(color, gray, COLOR_BGR2GRAY); + @endcode + you can simply write: + @code + Mat color; + ... + Mat gray; + cvtColor(color, gray, COLOR_BGR2GRAY); + @endcode + because cvtColor, as well as the most of OpenCV functions, calls Mat::create() for the output array + internally. + @param rows New number of rows. + @param cols New number of columns. + @param type New matrix type. + */ + void create(int rows, int cols, int type); + + /** @overload + @param size Alternative new matrix size specification: Size(cols, rows) + @param type New matrix type. + */ + void create(Size size, int type); + + /** @overload + @param ndims New array dimensionality. + @param sizes Array of integers specifying a new array shape. + @param type New matrix type. + */ + void create(int ndims, const int* sizes, int type); + + /** @overload + @param sizes Array of integers specifying a new array shape. + @param type New matrix type. + */ + void create(const std::vector& sizes, int type); + + /** @brief Increments the reference counter. + + The method increments the reference counter associated with the matrix data. If the matrix header + points to an external data set (see Mat::Mat ), the reference counter is NULL, and the method has no + effect in this case. Normally, to avoid memory leaks, the method should not be called explicitly. It + is called implicitly by the matrix assignment operator. The reference counter increment is an atomic + operation on the platforms that support it. Thus, it is safe to operate on the same matrices + asynchronously in different threads. + */ + void addref(); + + /** @brief Decrements the reference counter and deallocates the matrix if needed. + + The method decrements the reference counter associated with the matrix data. When the reference + counter reaches 0, the matrix data is deallocated and the data and the reference counter pointers + are set to NULL's. If the matrix header points to an external data set (see Mat::Mat ), the + reference counter is NULL, and the method has no effect in this case. + + This method can be called manually to force the matrix data deallocation. But since this method is + automatically called in the destructor, or by any other method that changes the data pointer, it is + usually not needed. The reference counter decrement and check for 0 is an atomic operation on the + platforms that support it. Thus, it is safe to operate on the same matrices asynchronously in + different threads. + */ + void release(); + + //! internal use function, consider to use 'release' method instead; deallocates the matrix data + void deallocate(); + //! internal use function; properly re-allocates _size, _step arrays + void copySize(const Mat& m); + + /** @brief Reserves space for the certain number of rows. + + The method reserves space for sz rows. If the matrix already has enough space to store sz rows, + nothing happens. If the matrix is reallocated, the first Mat::rows rows are preserved. The method + emulates the corresponding method of the STL vector class. + @param sz Number of rows. + */ + void reserve(size_t sz); + + /** @brief Reserves space for the certain number of bytes. + + The method reserves space for sz bytes. If the matrix already has enough space to store sz bytes, + nothing happens. If matrix has to be reallocated its previous content could be lost. + @param sz Number of bytes. + */ + void reserveBuffer(size_t sz); + + /** @brief Changes the number of matrix rows. + + The methods change the number of matrix rows. If the matrix is reallocated, the first + min(Mat::rows, sz) rows are preserved. The methods emulate the corresponding methods of the STL + vector class. + @param sz New number of rows. + */ + void resize(size_t sz); + + /** @overload + @param sz New number of rows. + @param s Value assigned to the newly added elements. + */ + void resize(size_t sz, const Scalar& s); + + //! internal function + void push_back_(const void* elem); + + /** @brief Adds elements to the bottom of the matrix. + + The methods add one or more elements to the bottom of the matrix. They emulate the corresponding + method of the STL vector class. When elem is Mat , its type and the number of columns must be the + same as in the container matrix. + @param elem Added element(s). + */ + template void push_back(const _Tp& elem); + + /** @overload + @param elem Added element(s). + */ + template void push_back(const Mat_<_Tp>& elem); + + /** @overload + @param elem Added element(s). + */ + template void push_back(const std::vector<_Tp>& elem); + + /** @overload + @param m Added line(s). + */ + void push_back(const Mat& m); + + /** @brief Removes elements from the bottom of the matrix. + + The method removes one or more rows from the bottom of the matrix. + @param nelems Number of removed rows. If it is greater than the total number of rows, an exception + is thrown. + */ + void pop_back(size_t nelems=1); + + /** @brief Locates the matrix header within a parent matrix. + + After you extracted a submatrix from a matrix using Mat::row, Mat::col, Mat::rowRange, + Mat::colRange, and others, the resultant submatrix points just to the part of the original big + matrix. However, each submatrix contains information (represented by datastart and dataend + fields) that helps reconstruct the original matrix size and the position of the extracted + submatrix within the original matrix. The method locateROI does exactly that. + @param wholeSize Output parameter that contains the size of the whole matrix containing *this* + as a part. + @param ofs Output parameter that contains an offset of *this* inside the whole matrix. + */ + void locateROI( Size& wholeSize, Point& ofs ) const; + + /** @brief Adjusts a submatrix size and position within the parent matrix. + + The method is complimentary to Mat::locateROI . The typical use of these functions is to determine + the submatrix position within the parent matrix and then shift the position somehow. Typically, it + can be required for filtering operations when pixels outside of the ROI should be taken into + account. When all the method parameters are positive, the ROI needs to grow in all directions by the + specified amount, for example: + @code + A.adjustROI(2, 2, 2, 2); + @endcode + In this example, the matrix size is increased by 4 elements in each direction. The matrix is shifted + by 2 elements to the left and 2 elements up, which brings in all the necessary pixels for the + filtering with the 5x5 kernel. + + adjustROI forces the adjusted ROI to be inside of the parent matrix that is boundaries of the + adjusted ROI are constrained by boundaries of the parent matrix. For example, if the submatrix A is + located in the first row of a parent matrix and you called A.adjustROI(2, 2, 2, 2) then A will not + be increased in the upward direction. + + The function is used internally by the OpenCV filtering functions, like filter2D , morphological + operations, and so on. + @param dtop Shift of the top submatrix boundary upwards. + @param dbottom Shift of the bottom submatrix boundary downwards. + @param dleft Shift of the left submatrix boundary to the left. + @param dright Shift of the right submatrix boundary to the right. + @sa copyMakeBorder + */ + Mat& adjustROI( int dtop, int dbottom, int dleft, int dright ); + + /** @brief Extracts a rectangular submatrix. + + The operators make a new header for the specified sub-array of \*this . They are the most + generalized forms of Mat::row, Mat::col, Mat::rowRange, and Mat::colRange . For example, + `A(Range(0, 10), Range::all())` is equivalent to `A.rowRange(0, 10)`. Similarly to all of the above, + the operators are O(1) operations, that is, no matrix data is copied. + @param rowRange Start and end row of the extracted submatrix. The upper boundary is not included. To + select all the rows, use Range::all(). + @param colRange Start and end column of the extracted submatrix. The upper boundary is not included. + To select all the columns, use Range::all(). + */ + Mat operator()( Range rowRange, Range colRange ) const; + + /** @overload + @param roi Extracted submatrix specified as a rectangle. + */ + Mat operator()( const Rect& roi ) const; + + /** @overload + @param ranges Array of selected ranges along each array dimension. + */ + Mat operator()( const Range* ranges ) const; + + /** @overload + @param ranges Array of selected ranges along each array dimension. + */ + Mat operator()(const std::vector& ranges) const; + + // //! converts header to CvMat; no data is copied + // operator CvMat() const; + // //! converts header to CvMatND; no data is copied + // operator CvMatND() const; + // //! converts header to IplImage; no data is copied + // operator IplImage() const; + + template operator std::vector<_Tp>() const; + template operator Vec<_Tp, n>() const; + template operator Matx<_Tp, m, n>() const; + +#ifdef CV_CXX_STD_ARRAY + template operator std::array<_Tp, _Nm>() const; +#endif + + /** @brief Reports whether the matrix is continuous or not. + + The method returns true if the matrix elements are stored continuously without gaps at the end of + each row. Otherwise, it returns false. Obviously, 1x1 or 1xN matrices are always continuous. + Matrices created with Mat::create are always continuous. But if you extract a part of the matrix + using Mat::col, Mat::diag, and so on, or constructed a matrix header for externally allocated data, + such matrices may no longer have this property. + + The continuity flag is stored as a bit in the Mat::flags field and is computed automatically when + you construct a matrix header. Thus, the continuity check is a very fast operation, though + theoretically it could be done as follows: + @code + // alternative implementation of Mat::isContinuous() + bool myCheckMatContinuity(const Mat& m) + { + //return (m.flags & Mat::CONTINUOUS_FLAG) != 0; + return m.rows == 1 || m.step == m.cols*m.elemSize(); + } + @endcode + The method is used in quite a few of OpenCV functions. The point is that element-wise operations + (such as arithmetic and logical operations, math functions, alpha blending, color space + transformations, and others) do not depend on the image geometry. Thus, if all the input and output + arrays are continuous, the functions can process them as very long single-row vectors. The example + below illustrates how an alpha-blending function can be implemented: + @code + template + void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) + { + const float alpha_scale = (float)std::numeric_limits::max(), + inv_scale = 1.f/alpha_scale; + + CV_Assert( src1.type() == src2.type() && + src1.type() == CV_MAKETYPE(traits::Depth::value, 4) && + src1.size() == src2.size()); + Size size = src1.size(); + dst.create(size, src1.type()); + + // here is the idiom: check the arrays for continuity and, + // if this is the case, + // treat the arrays as 1D vectors + if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() ) + { + size.width *= size.height; + size.height = 1; + } + size.width *= 4; + + for( int i = 0; i < size.height; i++ ) + { + // when the arrays are continuous, + // the outer loop is executed only once + const T* ptr1 = src1.ptr(i); + const T* ptr2 = src2.ptr(i); + T* dptr = dst.ptr(i); + + for( int j = 0; j < size.width; j += 4 ) + { + float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale; + dptr[j] = saturate_cast(ptr1[j]*alpha + ptr2[j]*beta); + dptr[j+1] = saturate_cast(ptr1[j+1]*alpha + ptr2[j+1]*beta); + dptr[j+2] = saturate_cast(ptr1[j+2]*alpha + ptr2[j+2]*beta); + dptr[j+3] = saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale); + } + } + } + @endcode + This approach, while being very simple, can boost the performance of a simple element-operation by + 10-20 percents, especially if the image is rather small and the operation is quite simple. + + Another OpenCV idiom in this function, a call of Mat::create for the destination array, that + allocates the destination array unless it already has the proper size and type. And while the newly + allocated arrays are always continuous, you still need to check the destination array because + Mat::create does not always allocate a new matrix. + */ + bool isContinuous() const; + + //! returns true if the matrix is a submatrix of another matrix + bool isSubmatrix() const; + + /** @brief Returns the matrix element size in bytes. + + The method returns the matrix element size in bytes. For example, if the matrix type is CV_16SC3 , + the method returns 3\*sizeof(short) or 6. + */ + size_t elemSize() const; + + /** @brief Returns the size of each matrix element channel in bytes. + + The method returns the matrix element channel size in bytes, that is, it ignores the number of + channels. For example, if the matrix type is CV_16SC3 , the method returns sizeof(short) or 2. + */ + size_t elemSize1() const; + + /** @brief Returns the type of a matrix element. + + The method returns a matrix element type. This is an identifier compatible with the CvMat type + system, like CV_16SC3 or 16-bit signed 3-channel array, and so on. + */ + int type() const; + + /** @brief Returns the depth of a matrix element. + + The method returns the identifier of the matrix element depth (the type of each individual channel). + For example, for a 16-bit signed element array, the method returns CV_16S . A complete list of + matrix types contains the following values: + - CV_8U - 8-bit unsigned integers ( 0..255 ) + - CV_8S - 8-bit signed integers ( -128..127 ) + - CV_16U - 16-bit unsigned integers ( 0..65535 ) + - CV_16S - 16-bit signed integers ( -32768..32767 ) + - CV_32S - 32-bit signed integers ( -2147483648..2147483647 ) + - CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN ) + - CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN ) + */ + int depth() const; + + /** @brief Returns the number of matrix channels. + + The method returns the number of matrix channels. + */ + int channels() const; + + /** @brief Returns a normalized step. + + The method returns a matrix step divided by Mat::elemSize1() . It can be useful to quickly access an + arbitrary matrix element. + */ + size_t step1(int i=0) const; + + /** @brief Returns true if the array has no elements. + + The method returns true if Mat::total() is 0 or if Mat::data is NULL. Because of pop_back() and + resize() methods `M.total() == 0` does not imply that `M.data == NULL`. + */ + bool empty() const; + + /** @brief Returns the total number of array elements. + + The method returns the number of array elements (a number of pixels if the array represents an + image). + */ + size_t total() const; + + /** @brief Returns the total number of array elements. + + The method returns the number of elements within a certain sub-array slice with startDim <= dim < endDim + */ + size_t total(int startDim, int endDim=INT_MAX) const; + + /** + * @param elemChannels Number of channels or number of columns the matrix should have. + * For a 2-D matrix, when the matrix has only 1 column, then it should have + * elemChannels channels; When the matrix has only 1 channel, + * then it should have elemChannels columns. + * For a 3-D matrix, it should have only one channel. Furthermore, + * if the number of planes is not one, then the number of rows + * within every plane has to be 1; if the number of rows within + * every plane is not 1, then the number of planes has to be 1. + * @param depth The depth the matrix should have. Set it to -1 when any depth is fine. + * @param requireContinuous Set it to true to require the matrix to be continuous + * @return -1 if the requirement is not satisfied. + * Otherwise, it returns the number of elements in the matrix. Note + * that an element may have multiple channels. + * + * The following code demonstrates its usage for a 2-d matrix: + * @snippet snippets/core_mat_checkVector.cpp example-2d + * + * The following code demonstrates its usage for a 3-d matrix: + * @snippet snippets/core_mat_checkVector.cpp example-3d + */ + int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const; + + /** @brief Returns a pointer to the specified matrix row. + + The methods return `uchar*` or typed pointer to the specified matrix row. See the sample in + Mat::isContinuous to know how to use these methods. + @param i0 A 0-based row index. + */ + uchar* ptr(int i0=0); + /** @overload */ + const uchar* ptr(int i0=0) const; + + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + uchar* ptr(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + const uchar* ptr(int row, int col) const; + + /** @overload */ + uchar* ptr(int i0, int i1, int i2); + /** @overload */ + const uchar* ptr(int i0, int i1, int i2) const; + + /** @overload */ + uchar* ptr(const int* idx); + /** @overload */ + const uchar* ptr(const int* idx) const; + /** @overload */ + template uchar* ptr(const Vec& idx); + /** @overload */ + template const uchar* ptr(const Vec& idx) const; + + /** @overload */ + template _Tp* ptr(int i0=0); + /** @overload */ + template const _Tp* ptr(int i0=0) const; + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template _Tp* ptr(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template const _Tp* ptr(int row, int col) const; + /** @overload */ + template _Tp* ptr(int i0, int i1, int i2); + /** @overload */ + template const _Tp* ptr(int i0, int i1, int i2) const; + /** @overload */ + template _Tp* ptr(const int* idx); + /** @overload */ + template const _Tp* ptr(const int* idx) const; + /** @overload */ + template _Tp* ptr(const Vec& idx); + /** @overload */ + template const _Tp* ptr(const Vec& idx) const; + + /** @brief Returns a reference to the specified array element. + + The template methods return a reference to the specified array element. For the sake of higher + performance, the index range checks are only performed in the Debug configuration. + + Note that the variants with a single index (i) can be used to access elements of single-row or + single-column 2-dimensional arrays. That is, if, for example, A is a 1 x N floating-point matrix and + B is an M x 1 integer matrix, you can simply write `A.at(k+4)` and `B.at(2*i+1)` + instead of `A.at(0,k+4)` and `B.at(2*i+1,0)`, respectively. + + The example below initializes a Hilbert matrix: + @code + Mat H(100, 100, CV_64F); + for(int i = 0; i < H.rows; i++) + for(int j = 0; j < H.cols; j++) + H.at(i,j)=1./(i+j+1); + @endcode + + Keep in mind that the size identifier used in the at operator cannot be chosen at random. It depends + on the image from which you are trying to retrieve the data. The table below gives a better insight in this: + - If matrix is of type `CV_8U` then use `Mat.at(y,x)`. + - If matrix is of type `CV_8S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_16U` then use `Mat.at(y,x)`. + - If matrix is of type `CV_16S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_32S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_32F` then use `Mat.at(y,x)`. + - If matrix is of type `CV_64F` then use `Mat.at(y,x)`. + + @param i0 Index along the dimension 0 + */ + template _Tp& at(int i0=0); + /** @overload + @param i0 Index along the dimension 0 + */ + template const _Tp& at(int i0=0) const; + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template _Tp& at(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template const _Tp& at(int row, int col) const; + + /** @overload + @param i0 Index along the dimension 0 + @param i1 Index along the dimension 1 + @param i2 Index along the dimension 2 + */ + template _Tp& at(int i0, int i1, int i2); + /** @overload + @param i0 Index along the dimension 0 + @param i1 Index along the dimension 1 + @param i2 Index along the dimension 2 + */ + template const _Tp& at(int i0, int i1, int i2) const; + + /** @overload + @param idx Array of Mat::dims indices. + */ + template _Tp& at(const int* idx); + /** @overload + @param idx Array of Mat::dims indices. + */ + template const _Tp& at(const int* idx) const; + + /** @overload */ + template _Tp& at(const Vec& idx); + /** @overload */ + template const _Tp& at(const Vec& idx) const; + + /** @overload + special versions for 2D arrays (especially convenient for referencing image pixels) + @param pt Element position specified as Point(j,i) . + */ + template _Tp& at(Point pt); + /** @overload + special versions for 2D arrays (especially convenient for referencing image pixels) + @param pt Element position specified as Point(j,i) . + */ + template const _Tp& at(Point pt) const; + + /** @brief Returns the matrix iterator and sets it to the first matrix element. + + The methods return the matrix read-only or read-write iterators. The use of matrix iterators is very + similar to the use of bi-directional STL iterators. In the example below, the alpha blending + function is rewritten using the matrix iterators: + @code + template + void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) + { + typedef Vec VT; + + const float alpha_scale = (float)std::numeric_limits::max(), + inv_scale = 1.f/alpha_scale; + + CV_Assert( src1.type() == src2.type() && + src1.type() == traits::Type::value && + src1.size() == src2.size()); + Size size = src1.size(); + dst.create(size, src1.type()); + + MatConstIterator_ it1 = src1.begin(), it1_end = src1.end(); + MatConstIterator_ it2 = src2.begin(); + MatIterator_ dst_it = dst.begin(); + + for( ; it1 != it1_end; ++it1, ++it2, ++dst_it ) + { + VT pix1 = *it1, pix2 = *it2; + float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale; + *dst_it = VT(saturate_cast(pix1[0]*alpha + pix2[0]*beta), + saturate_cast(pix1[1]*alpha + pix2[1]*beta), + saturate_cast(pix1[2]*alpha + pix2[2]*beta), + saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale)); + } + } + @endcode + */ + template MatIterator_<_Tp> begin(); + template MatConstIterator_<_Tp> begin() const; + + /** @brief Returns the matrix iterator and sets it to the after-last matrix element. + + The methods return the matrix read-only or read-write iterators, set to the point following the last + matrix element. + */ + template MatIterator_<_Tp> end(); + template MatConstIterator_<_Tp> end() const; + + /** @brief Runs the given functor over all matrix elements in parallel. + + The operation passed as argument has to be a function pointer, a function object or a lambda(C++11). + + Example 1. All of the operations below put 0xFF the first channel of all matrix elements: + @code + Mat image(1920, 1080, CV_8UC3); + typedef cv::Point3_ Pixel; + + // first. raw pointer access. + for (int r = 0; r < image.rows; ++r) { + Pixel* ptr = image.ptr(r, 0); + const Pixel* ptr_end = ptr + image.cols; + for (; ptr != ptr_end; ++ptr) { + ptr->x = 255; + } + } + + // Using MatIterator. (Simple but there are a Iterator's overhead) + for (Pixel &p : cv::Mat_(image)) { + p.x = 255; + } + + // Parallel execution with function object. + struct Operator { + void operator ()(Pixel &pixel, const int * position) { + pixel.x = 255; + } + }; + image.forEach(Operator()); + + // Parallel execution using C++11 lambda. + image.forEach([](Pixel &p, const int * position) -> void { + p.x = 255; + }); + @endcode + Example 2. Using the pixel's position: + @code + // Creating 3D matrix (255 x 255 x 255) typed uint8_t + // and initialize all elements by the value which equals elements position. + // i.e. pixels (x,y,z) = (1,2,3) is (b,g,r) = (1,2,3). + + int sizes[] = { 255, 255, 255 }; + typedef cv::Point3_ Pixel; + + Mat_ image = Mat::zeros(3, sizes, CV_8UC3); + + image.forEach([&](Pixel& pixel, const int position[]) -> void { + pixel.x = position[0]; + pixel.y = position[1]; + pixel.z = position[2]; + }); + @endcode + */ + template void forEach(const Functor& operation); + /** @overload */ + template void forEach(const Functor& operation) const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Mat(Mat&& m); + Mat& operator = (Mat&& m); +#endif + + enum { MAGIC_VAL = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG }; + enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 }; + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + //! the matrix dimensionality, >= 2 + int dims; + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows, cols; + //! pointer to the data + uchar* data; + + //! helper fields used in locateROI and adjustROI + const uchar* datastart; + const uchar* dataend; + const uchar* datalimit; + + //! custom allocator + MatAllocator* allocator; + //! and the standard allocator + static MatAllocator* getStdAllocator(); + static MatAllocator* getDefaultAllocator(); + static void setDefaultAllocator(MatAllocator* allocator); + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + //! interaction with UMat + UMatData* u; + + MatSize size; + MatStep step; + +protected: + template void forEach_impl(const Functor& operation); +}; + + +///////////////////////////////// Mat_<_Tp> //////////////////////////////////// + +/** @brief Template matrix class derived from Mat + +@code{.cpp} + template class Mat_ : public Mat + { + public: + // ... some specific methods + // and + // no new extra fields + }; +@endcode +The class `Mat_<_Tp>` is a *thin* template wrapper on top of the Mat class. It does not have any +extra data fields. Nor this class nor Mat has any virtual methods. Thus, references or pointers to +these two classes can be freely but carefully converted one to another. For example: +@code{.cpp} + // create a 100x100 8-bit matrix + Mat M(100,100,CV_8U); + // this will be compiled fine. no any data conversion will be done. + Mat_& M1 = (Mat_&)M; + // the program is likely to crash at the statement below + M1(99,99) = 1.f; +@endcode +While Mat is sufficient in most cases, Mat_ can be more convenient if you use a lot of element +access operations and if you know matrix type at the compilation time. Note that +`Mat::at(int y,int x)` and `Mat_::operator()(int y,int x)` do absolutely the same +and run at the same speed, but the latter is certainly shorter: +@code{.cpp} + Mat_ M(20,20); + for(int i = 0; i < M.rows; i++) + for(int j = 0; j < M.cols; j++) + M(i,j) = 1./(i+j+1); + Mat E, V; + eigen(M,E,V); + cout << E.at(0,0)/E.at(M.rows-1,0); +@endcode +To use Mat_ for multi-channel images/matrices, pass Vec as a Mat_ parameter: +@code{.cpp} + // allocate a 320x240 color image and fill it with green (in RGB space) + Mat_ img(240, 320, Vec3b(0,255,0)); + // now draw a diagonal white line + for(int i = 0; i < 100; i++) + img(i,i)=Vec3b(255,255,255); + // and now scramble the 2nd (red) channel of each pixel + for(int i = 0; i < img.rows; i++) + for(int j = 0; j < img.cols; j++) + img(i,j)[2] ^= (uchar)(i ^ j); +@endcode +Mat_ is fully compatible with C++11 range-based for loop. For example such loop +can be used to safely apply look-up table: +@code{.cpp} +void applyTable(Mat_& I, const uchar* const table) +{ + for(auto& pixel : I) + { + pixel = table[pixel]; + } +} +@endcode + */ +template class Mat_ : public Mat +{ +public: + typedef _Tp value_type; + typedef typename DataType<_Tp>::channel_type channel_type; + typedef MatIterator_<_Tp> iterator; + typedef MatConstIterator_<_Tp> const_iterator; + + //! default constructor + Mat_(); + //! equivalent to Mat(_rows, _cols, DataType<_Tp>::type) + Mat_(int _rows, int _cols); + //! constructor that sets each matrix element to specified value + Mat_(int _rows, int _cols, const _Tp& value); + //! equivalent to Mat(_size, DataType<_Tp>::type) + explicit Mat_(Size _size); + //! constructor that sets each matrix element to specified value + Mat_(Size _size, const _Tp& value); + //! n-dim array constructor + Mat_(int _ndims, const int* _sizes); + //! n-dim array constructor that sets each matrix element to specified value + Mat_(int _ndims, const int* _sizes, const _Tp& value); + //! copy/conversion constructor. If m is of different type, it's converted + Mat_(const Mat& m); + //! copy constructor + Mat_(const Mat_& m); + //! constructs a matrix on top of user-allocated data. step is in bytes(!!!), regardless of the type + Mat_(int _rows, int _cols, _Tp* _data, size_t _step=AUTO_STEP); + //! constructs n-dim matrix on top of user-allocated data. steps are in bytes(!!!), regardless of the type + Mat_(int _ndims, const int* _sizes, _Tp* _data, const size_t* _steps=0); + //! selects a submatrix + Mat_(const Mat_& m, const Range& rowRange, const Range& colRange=Range::all()); + //! selects a submatrix + Mat_(const Mat_& m, const Rect& roi); + //! selects a submatrix, n-dim version + Mat_(const Mat_& m, const Range* ranges); + //! selects a submatrix, n-dim version + Mat_(const Mat_& m, const std::vector& ranges); + //! from a matrix expression + explicit Mat_(const MatExpr& e); + //! makes a matrix out of Vec, std::vector, Point_ or Point3_. The matrix will have a single column + explicit Mat_(const std::vector<_Tp>& vec, bool copyData=false); + template explicit Mat_(const Vec::channel_type, n>& vec, bool copyData=true); + template explicit Mat_(const Matx::channel_type, m, n>& mtx, bool copyData=true); + explicit Mat_(const Point_::channel_type>& pt, bool copyData=true); + explicit Mat_(const Point3_::channel_type>& pt, bool copyData=true); + explicit Mat_(const MatCommaInitializer_<_Tp>& commaInitializer); + +#ifdef CV_CXX11 + Mat_(std::initializer_list<_Tp> values); + explicit Mat_(const std::initializer_list sizes, const std::initializer_list<_Tp> values); +#endif + +#ifdef CV_CXX_STD_ARRAY + template explicit Mat_(const std::array<_Tp, _Nm>& arr, bool copyData=false); +#endif + + Mat_& operator = (const Mat& m); + Mat_& operator = (const Mat_& m); + //! set all the elements to s. + Mat_& operator = (const _Tp& s); + //! assign a matrix expression + Mat_& operator = (const MatExpr& e); + + //! iterators; they are smart enough to skip gaps in the end of rows + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + //! template methods for for operation over all matrix elements. + // the operations take care of skipping gaps in the end of rows (if any) + template void forEach(const Functor& operation); + template void forEach(const Functor& operation) const; + + //! equivalent to Mat::create(_rows, _cols, DataType<_Tp>::type) + void create(int _rows, int _cols); + //! equivalent to Mat::create(_size, DataType<_Tp>::type) + void create(Size _size); + //! equivalent to Mat::create(_ndims, _sizes, DatType<_Tp>::type) + void create(int _ndims, const int* _sizes); + //! equivalent to Mat::release() + void release(); + //! cross-product + Mat_ cross(const Mat_& m) const; + //! data type conversion + template operator Mat_() const; + //! overridden forms of Mat::row() etc. + Mat_ row(int y) const; + Mat_ col(int x) const; + Mat_ diag(int d=0) const; + Mat_ clone() const CV_NODISCARD; + + //! overridden forms of Mat::elemSize() etc. + size_t elemSize() const; + size_t elemSize1() const; + int type() const; + int depth() const; + int channels() const; + size_t step1(int i=0) const; + //! returns step()/sizeof(_Tp) + size_t stepT(int i=0) const; + + //! overridden forms of Mat::zeros() etc. Data type is omitted, of course + static MatExpr zeros(int rows, int cols); + static MatExpr zeros(Size size); + static MatExpr zeros(int _ndims, const int* _sizes); + static MatExpr ones(int rows, int cols); + static MatExpr ones(Size size); + static MatExpr ones(int _ndims, const int* _sizes); + static MatExpr eye(int rows, int cols); + static MatExpr eye(Size size); + + //! some more overridden methods + Mat_& adjustROI( int dtop, int dbottom, int dleft, int dright ); + Mat_ operator()( const Range& rowRange, const Range& colRange ) const; + Mat_ operator()( const Rect& roi ) const; + Mat_ operator()( const Range* ranges ) const; + Mat_ operator()(const std::vector& ranges) const; + + //! more convenient forms of row and element access operators + _Tp* operator [](int y); + const _Tp* operator [](int y) const; + + //! returns reference to the specified element + _Tp& operator ()(const int* idx); + //! returns read-only reference to the specified element + const _Tp& operator ()(const int* idx) const; + + //! returns reference to the specified element + template _Tp& operator ()(const Vec& idx); + //! returns read-only reference to the specified element + template const _Tp& operator ()(const Vec& idx) const; + + //! returns reference to the specified element (1D case) + _Tp& operator ()(int idx0); + //! returns read-only reference to the specified element (1D case) + const _Tp& operator ()(int idx0) const; + //! returns reference to the specified element (2D case) + _Tp& operator ()(int row, int col); + //! returns read-only reference to the specified element (2D case) + const _Tp& operator ()(int row, int col) const; + //! returns reference to the specified element (3D case) + _Tp& operator ()(int idx0, int idx1, int idx2); + //! returns read-only reference to the specified element (3D case) + const _Tp& operator ()(int idx0, int idx1, int idx2) const; + + _Tp& operator ()(Point pt); + const _Tp& operator ()(Point pt) const; + + //! conversion to vector. + operator std::vector<_Tp>() const; + +#ifdef CV_CXX_STD_ARRAY + //! conversion to array. + template operator std::array<_Tp, _Nm>() const; +#endif + + //! conversion to Vec + template operator Vec::channel_type, n>() const; + //! conversion to Matx + template operator Matx::channel_type, m, n>() const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Mat_(Mat_&& m); + Mat_& operator = (Mat_&& m); + + Mat_(Mat&& m); + Mat_& operator = (Mat&& m); + + Mat_(MatExpr&& e); +#endif +}; + +typedef Mat_ Mat1b; +typedef Mat_ Mat2b; +typedef Mat_ Mat3b; +typedef Mat_ Mat4b; + +typedef Mat_ Mat1s; +typedef Mat_ Mat2s; +typedef Mat_ Mat3s; +typedef Mat_ Mat4s; + +typedef Mat_ Mat1w; +typedef Mat_ Mat2w; +typedef Mat_ Mat3w; +typedef Mat_ Mat4w; + +typedef Mat_ Mat1i; +typedef Mat_ Mat2i; +typedef Mat_ Mat3i; +typedef Mat_ Mat4i; + +typedef Mat_ Mat1f; +typedef Mat_ Mat2f; +typedef Mat_ Mat3f; +typedef Mat_ Mat4f; + +typedef Mat_ Mat1d; +typedef Mat_ Mat2d; +typedef Mat_ Mat3d; +typedef Mat_ Mat4d; + +/** @todo document */ +class CV_EXPORTS UMat +{ +public: + //! default constructor + UMat(UMatUsageFlags usageFlags = USAGE_DEFAULT); + //! constructs 2D matrix of the specified size and type + // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) + UMat(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(Size size, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + //! constructs 2D matrix and fills it with the specified value _s. + UMat(int rows, int cols, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(Size size, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! constructs n-dimensional matrix + UMat(int ndims, const int* sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(int ndims, const int* sizes, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! copy constructor + UMat(const UMat& m); + + //! creates a matrix header for a part of the bigger matrix + UMat(const UMat& m, const Range& rowRange, const Range& colRange=Range::all()); + UMat(const UMat& m, const Rect& roi); + UMat(const UMat& m, const Range* ranges); + UMat(const UMat& m, const std::vector& ranges); + //! builds matrix from std::vector with or without copying the data + template explicit UMat(const std::vector<_Tp>& vec, bool copyData=false); + + //! builds matrix from cv::Vec; the data is copied by default + template explicit UMat(const Vec<_Tp, n>& vec, bool copyData=true); + //! builds matrix from cv::Matx; the data is copied by default + template explicit UMat(const Matx<_Tp, m, n>& mtx, bool copyData=true); + //! builds matrix from a 2D point + template explicit UMat(const Point_<_Tp>& pt, bool copyData=true); + //! builds matrix from a 3D point + template explicit UMat(const Point3_<_Tp>& pt, bool copyData=true); + //! builds matrix from comma initializer + template explicit UMat(const MatCommaInitializer_<_Tp>& commaInitializer); + + //! destructor - calls release() + ~UMat(); + //! assignment operators + UMat& operator = (const UMat& m); + + Mat getMat(int flags) const; + + //! returns a new matrix header for the specified row + UMat row(int y) const; + //! returns a new matrix header for the specified column + UMat col(int x) const; + //! ... for the specified row span + UMat rowRange(int startrow, int endrow) const; + UMat rowRange(const Range& r) const; + //! ... for the specified column span + UMat colRange(int startcol, int endcol) const; + UMat colRange(const Range& r) const; + //! ... for the specified diagonal + //! (d=0 - the main diagonal, + //! >0 - a diagonal from the upper half, + //! <0 - a diagonal from the lower half) + UMat diag(int d=0) const; + //! constructs a square diagonal matrix which main diagonal is vector "d" + static UMat diag(const UMat& d); + + //! returns deep copy of the matrix, i.e. the data is copied + UMat clone() const CV_NODISCARD; + //! copies the matrix content to "m". + // It calls m.create(this->size(), this->type()). + void copyTo( OutputArray m ) const; + //! copies those matrix elements to "m" that are marked with non-zero mask elements. + void copyTo( OutputArray m, InputArray mask ) const; + //! converts matrix to another datatype with optional scaling. See cvConvertScale. + void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; + + void assignTo( UMat& m, int type=-1 ) const; + + //! sets every matrix element to s + UMat& operator = (const Scalar& s); + //! sets some of the matrix elements to s, according to the mask + UMat& setTo(InputArray value, InputArray mask=noArray()); + //! creates alternative matrix header for the same data, with different + // number of channels and/or different number of rows. see cvReshape. + UMat reshape(int cn, int rows=0) const; + UMat reshape(int cn, int newndims, const int* newsz) const; + + //! matrix transposition by means of matrix expressions + UMat t() const; + //! matrix inversion by means of matrix expressions + UMat inv(int method=DECOMP_LU) const; + //! per-element matrix multiplication by means of matrix expressions + UMat mul(InputArray m, double scale=1) const; + + //! computes dot-product + double dot(InputArray m) const; + + //! Matlab-style matrix initialization + static UMat zeros(int rows, int cols, int type); + static UMat zeros(Size size, int type); + static UMat zeros(int ndims, const int* sz, int type); + static UMat ones(int rows, int cols, int type); + static UMat ones(Size size, int type); + static UMat ones(int ndims, const int* sz, int type); + static UMat eye(int rows, int cols, int type); + static UMat eye(Size size, int type); + + //! allocates new matrix data unless the matrix already has specified size and type. + // previous data is unreferenced if needed. + void create(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(Size size, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(int ndims, const int* sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(const std::vector& sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! increases the reference counter; use with care to avoid memleaks + void addref(); + //! decreases reference counter; + // deallocates the data when reference counter reaches 0. + void release(); + + //! deallocates the matrix data + void deallocate(); + //! internal use function; properly re-allocates _size, _step arrays + void copySize(const UMat& m); + + //! locates matrix header within a parent matrix. See below + void locateROI( Size& wholeSize, Point& ofs ) const; + //! moves/resizes the current matrix ROI inside the parent matrix. + UMat& adjustROI( int dtop, int dbottom, int dleft, int dright ); + //! extracts a rectangular sub-matrix + // (this is a generalized form of row, rowRange etc.) + UMat operator()( Range rowRange, Range colRange ) const; + UMat operator()( const Rect& roi ) const; + UMat operator()( const Range* ranges ) const; + UMat operator()(const std::vector& ranges) const; + + //! returns true iff the matrix data is continuous + // (i.e. when there are no gaps between successive rows). + // similar to CV_IS_MAT_CONT(cvmat->type) + bool isContinuous() const; + + //! returns true if the matrix is a submatrix of another matrix + bool isSubmatrix() const; + + //! returns element size in bytes, + // similar to CV_ELEM_SIZE(cvmat->type) + size_t elemSize() const; + //! returns the size of element channel in bytes. + size_t elemSize1() const; + //! returns element type, similar to CV_MAT_TYPE(cvmat->type) + int type() const; + //! returns element type, similar to CV_MAT_DEPTH(cvmat->type) + int depth() const; + //! returns element type, similar to CV_MAT_CN(cvmat->type) + int channels() const; + //! returns step/elemSize1() + size_t step1(int i=0) const; + //! returns true if matrix data is NULL + bool empty() const; + //! returns the total number of matrix elements + size_t total() const; + + //! returns N if the matrix is 1-channel (N x ptdim) or ptdim-channel (1 x N) or (N x 1); negative number otherwise + int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const; + +#ifdef CV_CXX_MOVE_SEMANTICS + UMat(UMat&& m); + UMat& operator = (UMat&& m); +#endif + + /*! Returns the OpenCL buffer handle on which UMat operates on. + The UMat instance should be kept alive during the use of the handle to prevent the buffer to be + returned to the OpenCV buffer pool. + */ + void* handle(int accessFlags) const; + void ndoffset(size_t* ofs) const; + + enum { MAGIC_VAL = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG }; + enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 }; + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + //! the matrix dimensionality, >= 2 + int dims; + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows, cols; + + //! custom allocator + MatAllocator* allocator; + UMatUsageFlags usageFlags; // usage flags for allocator + //! and the standard allocator + static MatAllocator* getStdAllocator(); + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + // black-box container of UMat data + UMatData* u; + + // offset of the submatrix (or 0) + size_t offset; + + MatSize size; + MatStep step; + +protected: +}; + + +/////////////////////////// multi-dimensional sparse matrix ////////////////////////// + +/** @brief The class SparseMat represents multi-dimensional sparse numerical arrays. + +Such a sparse array can store elements of any type that Mat can store. *Sparse* means that only +non-zero elements are stored (though, as a result of operations on a sparse matrix, some of its +stored elements can actually become 0. It is up to you to detect such elements and delete them +using SparseMat::erase ). The non-zero elements are stored in a hash table that grows when it is +filled so that the search time is O(1) in average (regardless of whether element is there or not). +Elements can be accessed using the following methods: +- Query operations (SparseMat::ptr and the higher-level SparseMat::ref, SparseMat::value and + SparseMat::find), for example: + @code + const int dims = 5; + int size[5] = {10, 10, 10, 10, 10}; + SparseMat sparse_mat(dims, size, CV_32F); + for(int i = 0; i < 1000; i++) + { + int idx[dims]; + for(int k = 0; k < dims; k++) + idx[k] = rand() % size[k]; + sparse_mat.ref(idx) += 1.f; + } + cout << "nnz = " << sparse_mat.nzcount() << endl; + @endcode +- Sparse matrix iterators. They are similar to MatIterator but different from NAryMatIterator. + That is, the iteration loop is familiar to STL users: + @code + // prints elements of a sparse floating-point matrix + // and the sum of elements. + SparseMatConstIterator_ + it = sparse_mat.begin(), + it_end = sparse_mat.end(); + double s = 0; + int dims = sparse_mat.dims(); + for(; it != it_end; ++it) + { + // print element indices and the element value + const SparseMat::Node* n = it.node(); + printf("("); + for(int i = 0; i < dims; i++) + printf("%d%s", n->idx[i], i < dims-1 ? ", " : ")"); + printf(": %g\n", it.value()); + s += *it; + } + printf("Element sum is %g\n", s); + @endcode + If you run this loop, you will notice that elements are not enumerated in a logical order + (lexicographical, and so on). They come in the same order as they are stored in the hash table + (semi-randomly). You may collect pointers to the nodes and sort them to get the proper ordering. + Note, however, that pointers to the nodes may become invalid when you add more elements to the + matrix. This may happen due to possible buffer reallocation. +- Combination of the above 2 methods when you need to process 2 or more sparse matrices + simultaneously. For example, this is how you can compute unnormalized cross-correlation of the 2 + floating-point sparse matrices: + @code + double cross_corr(const SparseMat& a, const SparseMat& b) + { + const SparseMat *_a = &a, *_b = &b; + // if b contains less elements than a, + // it is faster to iterate through b + if(_a->nzcount() > _b->nzcount()) + std::swap(_a, _b); + SparseMatConstIterator_ it = _a->begin(), + it_end = _a->end(); + double ccorr = 0; + for(; it != it_end; ++it) + { + // take the next element from the first matrix + float avalue = *it; + const Node* anode = it.node(); + // and try to find an element with the same index in the second matrix. + // since the hash value depends only on the element index, + // reuse the hash value stored in the node + float bvalue = _b->value(anode->idx,&anode->hashval); + ccorr += avalue*bvalue; + } + return ccorr; + } + @endcode + */ +class CV_EXPORTS SparseMat +{ +public: + typedef SparseMatIterator iterator; + typedef SparseMatConstIterator const_iterator; + + enum { MAGIC_VAL=0x42FD0000, MAX_DIM=32, HASH_SCALE=0x5bd1e995, HASH_BIT=0x80000000 }; + + //! the sparse matrix header + struct CV_EXPORTS Hdr + { + Hdr(int _dims, const int* _sizes, int _type); + void clear(); + int refcount; + int dims; + int valueOffset; + size_t nodeSize; + size_t nodeCount; + size_t freeList; + std::vector pool; + std::vector hashtab; + int size[MAX_DIM]; + }; + + //! sparse matrix node - element of a hash table + struct CV_EXPORTS Node + { + //! hash value + size_t hashval; + //! index of the next node in the same hash table entry + size_t next; + //! index of the matrix element + int idx[MAX_DIM]; + }; + + /** @brief Various SparseMat constructors. + */ + SparseMat(); + + /** @overload + @param dims Array dimensionality. + @param _sizes Sparce matrix size on all dementions. + @param _type Sparse matrix data type. + */ + SparseMat(int dims, const int* _sizes, int _type); + + /** @overload + @param m Source matrix for copy constructor. If m is dense matrix (ocvMat) then it will be converted + to sparse representation. + */ + SparseMat(const SparseMat& m); + + /** @overload + @param m Source matrix for copy constructor. If m is dense matrix (ocvMat) then it will be converted + to sparse representation. + */ + explicit SparseMat(const Mat& m); + + //! the destructor + ~SparseMat(); + + //! assignment operator. This is O(1) operation, i.e. no data is copied + SparseMat& operator = (const SparseMat& m); + //! equivalent to the corresponding constructor + SparseMat& operator = (const Mat& m); + + //! creates full copy of the matrix + SparseMat clone() const CV_NODISCARD; + + //! copies all the data to the destination matrix. All the previous content of m is erased + void copyTo( SparseMat& m ) const; + //! converts sparse matrix to dense matrix. + void copyTo( Mat& m ) const; + //! multiplies all the matrix elements by the specified scale factor alpha and converts the results to the specified data type + void convertTo( SparseMat& m, int rtype, double alpha=1 ) const; + //! converts sparse matrix to dense n-dim matrix with optional type conversion and scaling. + /*! + @param [out] m - output matrix; if it does not have a proper size or type before the operation, + it is reallocated + @param [in] rtype - desired output matrix type or, rather, the depth since the number of channels + are the same as the input has; if rtype is negative, the output matrix will have the + same type as the input. + @param [in] alpha - optional scale factor + @param [in] beta - optional delta added to the scaled values + */ + void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 ) const; + + // not used now + void assignTo( SparseMat& m, int type=-1 ) const; + + //! reallocates sparse matrix. + /*! + If the matrix already had the proper size and type, + it is simply cleared with clear(), otherwise, + the old matrix is released (using release()) and the new one is allocated. + */ + void create(int dims, const int* _sizes, int _type); + //! sets all the sparse matrix elements to 0, which means clearing the hash table. + void clear(); + //! manually increments the reference counter to the header. + void addref(); + // decrements the header reference counter. When the counter reaches 0, the header and all the underlying data are deallocated. + void release(); + + //! converts sparse matrix to the old-style representation; all the elements are copied. + //operator CvSparseMat*() const; + //! returns the size of each element in bytes (not including the overhead - the space occupied by SparseMat::Node elements) + size_t elemSize() const; + //! returns elemSize()/channels() + size_t elemSize1() const; + + //! returns type of sparse matrix elements + int type() const; + //! returns the depth of sparse matrix elements + int depth() const; + //! returns the number of channels + int channels() const; + + //! returns the array of sizes, or NULL if the matrix is not allocated + const int* size() const; + //! returns the size of i-th matrix dimension (or 0) + int size(int i) const; + //! returns the matrix dimensionality + int dims() const; + //! returns the number of non-zero elements (=the number of hash table nodes) + size_t nzcount() const; + + //! computes the element hash value (1D case) + size_t hash(int i0) const; + //! computes the element hash value (2D case) + size_t hash(int i0, int i1) const; + //! computes the element hash value (3D case) + size_t hash(int i0, int i1, int i2) const; + //! computes the element hash value (nD case) + size_t hash(const int* idx) const; + + //!@{ + /*! + specialized variants for 1D, 2D, 3D cases and the generic_type one for n-D case. + return pointer to the matrix element. + - if the element is there (it's non-zero), the pointer to it is returned + - if it's not there and createMissing=false, NULL pointer is returned + - if it's not there and createMissing=true, then the new element + is created and initialized with 0. Pointer to it is returned + - if the optional hashval pointer is not NULL, the element hash value is + not computed, but *hashval is taken instead. + */ + //! returns pointer to the specified element (1D case) + uchar* ptr(int i0, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (2D case) + uchar* ptr(int i0, int i1, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (3D case) + uchar* ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (nD case) + uchar* ptr(const int* idx, bool createMissing, size_t* hashval=0); + //!@} + + //!@{ + /*! + return read-write reference to the specified sparse matrix element. + + `ref<_Tp>(i0,...[,hashval])` is equivalent to `*(_Tp*)ptr(i0,...,true[,hashval])`. + The methods always return a valid reference. + If the element did not exist, it is created and initialized with 0. + */ + //! returns reference to the specified element (1D case) + template _Tp& ref(int i0, size_t* hashval=0); + //! returns reference to the specified element (2D case) + template _Tp& ref(int i0, int i1, size_t* hashval=0); + //! returns reference to the specified element (3D case) + template _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); + //! returns reference to the specified element (nD case) + template _Tp& ref(const int* idx, size_t* hashval=0); + //!@} + + //!@{ + /*! + return value of the specified sparse matrix element. + + `value<_Tp>(i0,...[,hashval])` is equivalent to + @code + { const _Tp* p = find<_Tp>(i0,...[,hashval]); return p ? *p : _Tp(); } + @endcode + + That is, if the element did not exist, the methods return 0. + */ + //! returns value of the specified element (1D case) + template _Tp value(int i0, size_t* hashval=0) const; + //! returns value of the specified element (2D case) + template _Tp value(int i0, int i1, size_t* hashval=0) const; + //! returns value of the specified element (3D case) + template _Tp value(int i0, int i1, int i2, size_t* hashval=0) const; + //! returns value of the specified element (nD case) + template _Tp value(const int* idx, size_t* hashval=0) const; + //!@} + + //!@{ + /*! + Return pointer to the specified sparse matrix element if it exists + + `find<_Tp>(i0,...[,hashval])` is equivalent to `(_const Tp*)ptr(i0,...false[,hashval])`. + + If the specified element does not exist, the methods return NULL. + */ + //! returns pointer to the specified element (1D case) + template const _Tp* find(int i0, size_t* hashval=0) const; + //! returns pointer to the specified element (2D case) + template const _Tp* find(int i0, int i1, size_t* hashval=0) const; + //! returns pointer to the specified element (3D case) + template const _Tp* find(int i0, int i1, int i2, size_t* hashval=0) const; + //! returns pointer to the specified element (nD case) + template const _Tp* find(const int* idx, size_t* hashval=0) const; + //!@} + + //! erases the specified element (2D case) + void erase(int i0, int i1, size_t* hashval=0); + //! erases the specified element (3D case) + void erase(int i0, int i1, int i2, size_t* hashval=0); + //! erases the specified element (nD case) + void erase(const int* idx, size_t* hashval=0); + + //!@{ + /*! + return the sparse matrix iterator pointing to the first sparse matrix element + */ + //! returns the sparse matrix iterator at the matrix beginning + SparseMatIterator begin(); + //! returns the sparse matrix iterator at the matrix beginning + template SparseMatIterator_<_Tp> begin(); + //! returns the read-only sparse matrix iterator at the matrix beginning + SparseMatConstIterator begin() const; + //! returns the read-only sparse matrix iterator at the matrix beginning + template SparseMatConstIterator_<_Tp> begin() const; + //!@} + /*! + return the sparse matrix iterator pointing to the element following the last sparse matrix element + */ + //! returns the sparse matrix iterator at the matrix end + SparseMatIterator end(); + //! returns the read-only sparse matrix iterator at the matrix end + SparseMatConstIterator end() const; + //! returns the typed sparse matrix iterator at the matrix end + template SparseMatIterator_<_Tp> end(); + //! returns the typed read-only sparse matrix iterator at the matrix end + template SparseMatConstIterator_<_Tp> end() const; + + //! returns the value stored in the sparse martix node + template _Tp& value(Node* n); + //! returns the value stored in the sparse martix node + template const _Tp& value(const Node* n) const; + + ////////////// some internal-use methods /////////////// + Node* node(size_t nidx); + const Node* node(size_t nidx) const; + + uchar* newNode(const int* idx, size_t hashval); + void removeNode(size_t hidx, size_t nidx, size_t previdx); + void resizeHashTab(size_t newsize); + + int flags; + Hdr* hdr; +}; + + + +///////////////////////////////// SparseMat_<_Tp> //////////////////////////////////// + +/** @brief Template sparse n-dimensional array class derived from SparseMat + +SparseMat_ is a thin wrapper on top of SparseMat created in the same way as Mat_ . It simplifies +notation of some operations: +@code + int sz[] = {10, 20, 30}; + SparseMat_ M(3, sz); + ... + M.ref(1, 2, 3) = M(4, 5, 6) + M(7, 8, 9); +@endcode + */ +template class SparseMat_ : public SparseMat +{ +public: + typedef SparseMatIterator_<_Tp> iterator; + typedef SparseMatConstIterator_<_Tp> const_iterator; + + //! the default constructor + SparseMat_(); + //! the full constructor equivalent to SparseMat(dims, _sizes, DataType<_Tp>::type) + SparseMat_(int dims, const int* _sizes); + //! the copy constructor. If DataType<_Tp>.type != m.type(), the m elements are converted + SparseMat_(const SparseMat& m); + //! the copy constructor. This is O(1) operation - no data is copied + SparseMat_(const SparseMat_& m); + //! converts dense matrix to the sparse form + SparseMat_(const Mat& m); + //! converts the old-style sparse matrix to the C++ class. All the elements are copied + //SparseMat_(const CvSparseMat* m); + //! the assignment operator. If DataType<_Tp>.type != m.type(), the m elements are converted + SparseMat_& operator = (const SparseMat& m); + //! the assignment operator. This is O(1) operation - no data is copied + SparseMat_& operator = (const SparseMat_& m); + //! converts dense matrix to the sparse form + SparseMat_& operator = (const Mat& m); + + //! makes full copy of the matrix. All the elements are duplicated + SparseMat_ clone() const CV_NODISCARD; + //! equivalent to cv::SparseMat::create(dims, _sizes, DataType<_Tp>::type) + void create(int dims, const int* _sizes); + //! converts sparse matrix to the old-style CvSparseMat. All the elements are copied + //operator CvSparseMat*() const; + + //! returns type of the matrix elements + int type() const; + //! returns depth of the matrix elements + int depth() const; + //! returns the number of channels in each matrix element + int channels() const; + + //! equivalent to SparseMat::ref<_Tp>(i0, hashval) + _Tp& ref(int i0, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(i0, i1, hashval) + _Tp& ref(int i0, int i1, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(i0, i1, i2, hashval) + _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(idx, hashval) + _Tp& ref(const int* idx, size_t* hashval=0); + + //! equivalent to SparseMat::value<_Tp>(i0, hashval) + _Tp operator()(int i0, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(i0, i1, hashval) + _Tp operator()(int i0, int i1, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(i0, i1, i2, hashval) + _Tp operator()(int i0, int i1, int i2, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(idx, hashval) + _Tp operator()(const int* idx, size_t* hashval=0) const; + + //! returns sparse matrix iterator pointing to the first sparse matrix element + SparseMatIterator_<_Tp> begin(); + //! returns read-only sparse matrix iterator pointing to the first sparse matrix element + SparseMatConstIterator_<_Tp> begin() const; + //! returns sparse matrix iterator pointing to the element following the last sparse matrix element + SparseMatIterator_<_Tp> end(); + //! returns read-only sparse matrix iterator pointing to the element following the last sparse matrix element + SparseMatConstIterator_<_Tp> end() const; +}; + + + +////////////////////////////////// MatConstIterator ////////////////////////////////// + +class CV_EXPORTS MatConstIterator +{ +public: + typedef uchar* value_type; + typedef ptrdiff_t difference_type; + typedef const uchar** pointer; + typedef uchar* reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! default constructor + MatConstIterator(); + //! constructor that sets the iterator to the beginning of the matrix + MatConstIterator(const Mat* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, const int* _idx); + //! copy constructor + MatConstIterator(const MatConstIterator& it); + + //! copy operator + MatConstIterator& operator = (const MatConstIterator& it); + //! returns the current matrix element + const uchar* operator *() const; + //! returns the i-th matrix element, relative to the current + const uchar* operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatConstIterator& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatConstIterator& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatConstIterator& operator --(); + //! decrements the iterator + MatConstIterator operator --(int); + //! increments the iterator + MatConstIterator& operator ++(); + //! increments the iterator + MatConstIterator operator ++(int); + //! returns the current iterator position + Point pos() const; + //! returns the current iterator position + void pos(int* _idx) const; + + ptrdiff_t lpos() const; + void seek(ptrdiff_t ofs, bool relative = false); + void seek(const int* _idx, bool relative = false); + + const Mat* m; + size_t elemSize; + const uchar* ptr; + const uchar* sliceStart; + const uchar* sliceEnd; +}; + + + +////////////////////////////////// MatConstIterator_ ///////////////////////////////// + +/** @brief Matrix read-only iterator + */ +template +class MatConstIterator_ : public MatConstIterator +{ +public: + typedef _Tp value_type; + typedef ptrdiff_t difference_type; + typedef const _Tp* pointer; + typedef const _Tp& reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! default constructor + MatConstIterator_(); + //! constructor that sets the iterator to the beginning of the matrix + MatConstIterator_(const Mat_<_Tp>* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, const int* _idx); + //! copy constructor + MatConstIterator_(const MatConstIterator_& it); + + //! copy operator + MatConstIterator_& operator = (const MatConstIterator_& it); + //! returns the current matrix element + const _Tp& operator *() const; + //! returns the i-th matrix element, relative to the current + const _Tp& operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatConstIterator_& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatConstIterator_& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatConstIterator_& operator --(); + //! decrements the iterator + MatConstIterator_ operator --(int); + //! increments the iterator + MatConstIterator_& operator ++(); + //! increments the iterator + MatConstIterator_ operator ++(int); + //! returns the current iterator position + Point pos() const; +}; + + + +//////////////////////////////////// MatIterator_ //////////////////////////////////// + +/** @brief Matrix read-write iterator +*/ +template +class MatIterator_ : public MatConstIterator_<_Tp> +{ +public: + typedef _Tp* pointer; + typedef _Tp& reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! the default constructor + MatIterator_(); + //! constructor that sets the iterator to the beginning of the matrix + MatIterator_(Mat_<_Tp>* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, const int* _idx); + //! copy constructor + MatIterator_(const MatIterator_& it); + //! copy operator + MatIterator_& operator = (const MatIterator_<_Tp>& it ); + + //! returns the current matrix element + _Tp& operator *() const; + //! returns the i-th matrix element, relative to the current + _Tp& operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatIterator_& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatIterator_& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatIterator_& operator --(); + //! decrements the iterator + MatIterator_ operator --(int); + //! increments the iterator + MatIterator_& operator ++(); + //! increments the iterator + MatIterator_ operator ++(int); +}; + + + +/////////////////////////////// SparseMatConstIterator /////////////////////////////// + +/** @brief Read-Only Sparse Matrix Iterator. + + Here is how to use the iterator to compute the sum of floating-point sparse matrix elements: + + \code + SparseMatConstIterator it = m.begin(), it_end = m.end(); + double s = 0; + CV_Assert( m.type() == CV_32F ); + for( ; it != it_end; ++it ) + s += it.value(); + \endcode +*/ +class CV_EXPORTS SparseMatConstIterator +{ +public: + //! the default constructor + SparseMatConstIterator(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatConstIterator(const SparseMat* _m); + //! the copy constructor + SparseMatConstIterator(const SparseMatConstIterator& it); + + //! the assignment operator + SparseMatConstIterator& operator = (const SparseMatConstIterator& it); + + //! template method returning the current matrix element + template const _Tp& value() const; + //! returns the current node of the sparse matrix. it.node->idx is the current element index + const SparseMat::Node* node() const; + + //! moves iterator to the previous element + SparseMatConstIterator& operator --(); + //! moves iterator to the previous element + SparseMatConstIterator operator --(int); + //! moves iterator to the next element + SparseMatConstIterator& operator ++(); + //! moves iterator to the next element + SparseMatConstIterator operator ++(int); + + //! moves iterator to the element after the last element + void seekEnd(); + + const SparseMat* m; + size_t hashidx; + uchar* ptr; +}; + + + +////////////////////////////////// SparseMatIterator ///////////////////////////////// + +/** @brief Read-write Sparse Matrix Iterator + + The class is similar to cv::SparseMatConstIterator, + but can be used for in-place modification of the matrix elements. +*/ +class CV_EXPORTS SparseMatIterator : public SparseMatConstIterator +{ +public: + //! the default constructor + SparseMatIterator(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatIterator(SparseMat* _m); + //! the full constructor setting the iterator to the specified sparse matrix element + SparseMatIterator(SparseMat* _m, const int* idx); + //! the copy constructor + SparseMatIterator(const SparseMatIterator& it); + + //! the assignment operator + SparseMatIterator& operator = (const SparseMatIterator& it); + //! returns read-write reference to the current sparse matrix element + template _Tp& value() const; + //! returns pointer to the current sparse matrix node. it.node->idx is the index of the current element (do not modify it!) + SparseMat::Node* node() const; + + //! moves iterator to the next element + SparseMatIterator& operator ++(); + //! moves iterator to the next element + SparseMatIterator operator ++(int); +}; + + + +/////////////////////////////// SparseMatConstIterator_ ////////////////////////////// + +/** @brief Template Read-Only Sparse Matrix Iterator Class. + + This is the derived from SparseMatConstIterator class that + introduces more convenient operator *() for accessing the current element. +*/ +template class SparseMatConstIterator_ : public SparseMatConstIterator +{ +public: + + typedef std::forward_iterator_tag iterator_category; + + //! the default constructor + SparseMatConstIterator_(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatConstIterator_(const SparseMat_<_Tp>* _m); + SparseMatConstIterator_(const SparseMat* _m); + //! the copy constructor + SparseMatConstIterator_(const SparseMatConstIterator_& it); + + //! the assignment operator + SparseMatConstIterator_& operator = (const SparseMatConstIterator_& it); + //! the element access operator + const _Tp& operator *() const; + + //! moves iterator to the next element + SparseMatConstIterator_& operator ++(); + //! moves iterator to the next element + SparseMatConstIterator_ operator ++(int); +}; + + + +///////////////////////////////// SparseMatIterator_ ///////////////////////////////// + +/** @brief Template Read-Write Sparse Matrix Iterator Class. + + This is the derived from cv::SparseMatConstIterator_ class that + introduces more convenient operator *() for accessing the current element. +*/ +template class SparseMatIterator_ : public SparseMatConstIterator_<_Tp> +{ +public: + + typedef std::forward_iterator_tag iterator_category; + + //! the default constructor + SparseMatIterator_(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatIterator_(SparseMat_<_Tp>* _m); + SparseMatIterator_(SparseMat* _m); + //! the copy constructor + SparseMatIterator_(const SparseMatIterator_& it); + + //! the assignment operator + SparseMatIterator_& operator = (const SparseMatIterator_& it); + //! returns the reference to the current element + _Tp& operator *() const; + + //! moves the iterator to the next element + SparseMatIterator_& operator ++(); + //! moves the iterator to the next element + SparseMatIterator_ operator ++(int); +}; + + + +/////////////////////////////////// NAryMatIterator ////////////////////////////////// + +/** @brief n-ary multi-dimensional array iterator. + +Use the class to implement unary, binary, and, generally, n-ary element-wise operations on +multi-dimensional arrays. Some of the arguments of an n-ary function may be continuous arrays, some +may be not. It is possible to use conventional MatIterator 's for each array but incrementing all of +the iterators after each small operations may be a big overhead. In this case consider using +NAryMatIterator to iterate through several matrices simultaneously as long as they have the same +geometry (dimensionality and all the dimension sizes are the same). On each iteration `it.planes[0]`, +`it.planes[1]`,... will be the slices of the corresponding matrices. + +The example below illustrates how you can compute a normalized and threshold 3D color histogram: +@code + void computeNormalizedColorHist(const Mat& image, Mat& hist, int N, double minProb) + { + const int histSize[] = {N, N, N}; + + // make sure that the histogram has a proper size and type + hist.create(3, histSize, CV_32F); + + // and clear it + hist = Scalar(0); + + // the loop below assumes that the image + // is a 8-bit 3-channel. check it. + CV_Assert(image.type() == CV_8UC3); + MatConstIterator_ it = image.begin(), + it_end = image.end(); + for( ; it != it_end; ++it ) + { + const Vec3b& pix = *it; + hist.at(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f; + } + + minProb *= image.rows*image.cols; + + // initialize iterator (the style is different from STL). + // after initialization the iterator will contain + // the number of slices or planes the iterator will go through. + // it simultaneously increments iterators for several matrices + // supplied as a null terminated list of pointers + const Mat* arrays[] = {&hist, 0}; + Mat planes[1]; + NAryMatIterator itNAry(arrays, planes, 1); + double s = 0; + // iterate through the matrix. on each iteration + // itNAry.planes[i] (of type Mat) will be set to the current plane + // of the i-th n-dim matrix passed to the iterator constructor. + for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) + { + threshold(itNAry.planes[0], itNAry.planes[0], minProb, 0, THRESH_TOZERO); + s += sum(itNAry.planes[0])[0]; + } + + s = 1./s; + itNAry = NAryMatIterator(arrays, planes, 1); + for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) + itNAry.planes[0] *= s; + } +@endcode + */ +class CV_EXPORTS NAryMatIterator +{ +public: + //! the default constructor + NAryMatIterator(); + //! the full constructor taking arbitrary number of n-dim matrices + NAryMatIterator(const Mat** arrays, uchar** ptrs, int narrays=-1); + //! the full constructor taking arbitrary number of n-dim matrices + NAryMatIterator(const Mat** arrays, Mat* planes, int narrays=-1); + //! the separate iterator initialization method + void init(const Mat** arrays, Mat* planes, uchar** ptrs, int narrays=-1); + + //! proceeds to the next plane of every iterated matrix + NAryMatIterator& operator ++(); + //! proceeds to the next plane of every iterated matrix (postfix increment operator) + NAryMatIterator operator ++(int); + + //! the iterated arrays + const Mat** arrays; + //! the current planes + Mat* planes; + //! data pointers + uchar** ptrs; + //! the number of arrays + int narrays; + //! the number of hyper-planes that the iterator steps through + size_t nplanes; + //! the size of each segment (in elements) + size_t size; +protected: + int iterdepth; + size_t idx; +}; + + + +///////////////////////////////// Matrix Expressions ///////////////////////////////// + +class CV_EXPORTS MatOp +{ +public: + MatOp(); + virtual ~MatOp(); + + virtual bool elementWise(const MatExpr& expr) const; + virtual void assign(const MatExpr& expr, Mat& m, int type=-1) const = 0; + virtual void roi(const MatExpr& expr, const Range& rowRange, + const Range& colRange, MatExpr& res) const; + virtual void diag(const MatExpr& expr, int d, MatExpr& res) const; + virtual void augAssignAdd(const MatExpr& expr, Mat& m) const; + virtual void augAssignSubtract(const MatExpr& expr, Mat& m) const; + virtual void augAssignMultiply(const MatExpr& expr, Mat& m) const; + virtual void augAssignDivide(const MatExpr& expr, Mat& m) const; + virtual void augAssignAnd(const MatExpr& expr, Mat& m) const; + virtual void augAssignOr(const MatExpr& expr, Mat& m) const; + virtual void augAssignXor(const MatExpr& expr, Mat& m) const; + + virtual void add(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void add(const MatExpr& expr1, const Scalar& s, MatExpr& res) const; + + virtual void subtract(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void subtract(const Scalar& s, const MatExpr& expr, MatExpr& res) const; + + virtual void multiply(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res, double scale=1) const; + virtual void multiply(const MatExpr& expr1, double s, MatExpr& res) const; + + virtual void divide(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res, double scale=1) const; + virtual void divide(double s, const MatExpr& expr, MatExpr& res) const; + + virtual void abs(const MatExpr& expr, MatExpr& res) const; + + virtual void transpose(const MatExpr& expr, MatExpr& res) const; + virtual void matmul(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void invert(const MatExpr& expr, int method, MatExpr& res) const; + + virtual Size size(const MatExpr& expr) const; + virtual int type(const MatExpr& expr) const; +}; + +/** @brief Matrix expression representation +@anchor MatrixExpressions +This is a list of implemented matrix operations that can be combined in arbitrary complex +expressions (here A, B stand for matrices ( Mat ), s for a scalar ( Scalar ), alpha for a +real-valued scalar ( double )): +- Addition, subtraction, negation: `A+B`, `A-B`, `A+s`, `A-s`, `s+A`, `s-A`, `-A` +- Scaling: `A*alpha` +- Per-element multiplication and division: `A.mul(B)`, `A/B`, `alpha/A` +- Matrix multiplication: `A*B` +- Transposition: `A.t()` (means AT) +- Matrix inversion and pseudo-inversion, solving linear systems and least-squares problems: + `A.inv([method]) (~ A-1)`, `A.inv([method])*B (~ X: AX=B)` +- Comparison: `A cmpop B`, `A cmpop alpha`, `alpha cmpop A`, where *cmpop* is one of + `>`, `>=`, `==`, `!=`, `<=`, `<`. The result of comparison is an 8-bit single channel mask whose + elements are set to 255 (if the particular element or pair of elements satisfy the condition) or + 0. +- Bitwise logical operations: `A logicop B`, `A logicop s`, `s logicop A`, `~A`, where *logicop* is one of + `&`, `|`, `^`. +- Element-wise minimum and maximum: `min(A, B)`, `min(A, alpha)`, `max(A, B)`, `max(A, alpha)` +- Element-wise absolute value: `abs(A)` +- Cross-product, dot-product: `A.cross(B)`, `A.dot(B)` +- Any function of matrix or matrices and scalars that returns a matrix or a scalar, such as norm, + mean, sum, countNonZero, trace, determinant, repeat, and others. +- Matrix initializers ( Mat::eye(), Mat::zeros(), Mat::ones() ), matrix comma-separated + initializers, matrix constructors and operators that extract sub-matrices (see Mat description). +- Mat_() constructors to cast the result to the proper type. +@note Comma-separated initializers and probably some other operations may require additional +explicit Mat() or Mat_() constructor calls to resolve a possible ambiguity. + +Here are examples of matrix expressions: +@code + // compute pseudo-inverse of A, equivalent to A.inv(DECOMP_SVD) + SVD svd(A); + Mat pinvA = svd.vt.t()*Mat::diag(1./svd.w)*svd.u.t(); + + // compute the new vector of parameters in the Levenberg-Marquardt algorithm + x -= (A.t()*A + lambda*Mat::eye(A.cols,A.cols,A.type())).inv(DECOMP_CHOLESKY)*(A.t()*err); + + // sharpen image using "unsharp mask" algorithm + Mat blurred; double sigma = 1, threshold = 5, amount = 1; + GaussianBlur(img, blurred, Size(), sigma, sigma); + Mat lowContrastMask = abs(img - blurred) < threshold; + Mat sharpened = img*(1+amount) + blurred*(-amount); + img.copyTo(sharpened, lowContrastMask); +@endcode +*/ +class CV_EXPORTS MatExpr +{ +public: + MatExpr(); + explicit MatExpr(const Mat& m); + + MatExpr(const MatOp* _op, int _flags, const Mat& _a = Mat(), const Mat& _b = Mat(), + const Mat& _c = Mat(), double _alpha = 1, double _beta = 1, const Scalar& _s = Scalar()); + + operator Mat() const; + template operator Mat_<_Tp>() const; + + Size size() const; + int type() const; + + MatExpr row(int y) const; + MatExpr col(int x) const; + MatExpr diag(int d = 0) const; + MatExpr operator()( const Range& rowRange, const Range& colRange ) const; + MatExpr operator()( const Rect& roi ) const; + + MatExpr t() const; + MatExpr inv(int method = DECOMP_LU) const; + MatExpr mul(const MatExpr& e, double scale=1) const; + MatExpr mul(const Mat& m, double scale=1) const; + + Mat cross(const Mat& m) const; + double dot(const Mat& m) const; + + const MatOp* op; + int flags; + + Mat a, b, c; + double alpha, beta; + Scalar s; +}; + +//! @} core_basic + +//! @relates cv::MatExpr +//! @{ +CV_EXPORTS MatExpr operator + (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator + (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator + (const Scalar& s, const Mat& a); +CV_EXPORTS MatExpr operator + (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator + (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator + (const MatExpr& e, const Scalar& s); +CV_EXPORTS MatExpr operator + (const Scalar& s, const MatExpr& e); +CV_EXPORTS MatExpr operator + (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator + (const Mat& a, const Matx<_Tp, m, n>& b) { return a + Mat(b); } +template static inline +MatExpr operator + (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) + b; } + +CV_EXPORTS MatExpr operator - (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator - (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator - (const Scalar& s, const Mat& a); +CV_EXPORTS MatExpr operator - (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator - (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator - (const MatExpr& e, const Scalar& s); +CV_EXPORTS MatExpr operator - (const Scalar& s, const MatExpr& e); +CV_EXPORTS MatExpr operator - (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator - (const Mat& a, const Matx<_Tp, m, n>& b) { return a - Mat(b); } +template static inline +MatExpr operator - (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) - b; } + +CV_EXPORTS MatExpr operator - (const Mat& m); +CV_EXPORTS MatExpr operator - (const MatExpr& e); + +CV_EXPORTS MatExpr operator * (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator * (const Mat& a, double s); +CV_EXPORTS MatExpr operator * (double s, const Mat& a); +CV_EXPORTS MatExpr operator * (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator * (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator * (const MatExpr& e, double s); +CV_EXPORTS MatExpr operator * (double s, const MatExpr& e); +CV_EXPORTS MatExpr operator * (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator * (const Mat& a, const Matx<_Tp, m, n>& b) { return a * Mat(b); } +template static inline +MatExpr operator * (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) * b; } + +CV_EXPORTS MatExpr operator / (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator / (const Mat& a, double s); +CV_EXPORTS MatExpr operator / (double s, const Mat& a); +CV_EXPORTS MatExpr operator / (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator / (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator / (const MatExpr& e, double s); +CV_EXPORTS MatExpr operator / (double s, const MatExpr& e); +CV_EXPORTS MatExpr operator / (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator / (const Mat& a, const Matx<_Tp, m, n>& b) { return a / Mat(b); } +template static inline +MatExpr operator / (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) / b; } + +CV_EXPORTS MatExpr operator < (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator < (const Mat& a, double s); +CV_EXPORTS MatExpr operator < (double s, const Mat& a); +template static inline +MatExpr operator < (const Mat& a, const Matx<_Tp, m, n>& b) { return a < Mat(b); } +template static inline +MatExpr operator < (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) < b; } + +CV_EXPORTS MatExpr operator <= (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator <= (const Mat& a, double s); +CV_EXPORTS MatExpr operator <= (double s, const Mat& a); +template static inline +MatExpr operator <= (const Mat& a, const Matx<_Tp, m, n>& b) { return a <= Mat(b); } +template static inline +MatExpr operator <= (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) <= b; } + +CV_EXPORTS MatExpr operator == (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator == (const Mat& a, double s); +CV_EXPORTS MatExpr operator == (double s, const Mat& a); +template static inline +MatExpr operator == (const Mat& a, const Matx<_Tp, m, n>& b) { return a == Mat(b); } +template static inline +MatExpr operator == (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) == b; } + +CV_EXPORTS MatExpr operator != (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator != (const Mat& a, double s); +CV_EXPORTS MatExpr operator != (double s, const Mat& a); +template static inline +MatExpr operator != (const Mat& a, const Matx<_Tp, m, n>& b) { return a != Mat(b); } +template static inline +MatExpr operator != (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) != b; } + +CV_EXPORTS MatExpr operator >= (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator >= (const Mat& a, double s); +CV_EXPORTS MatExpr operator >= (double s, const Mat& a); +template static inline +MatExpr operator >= (const Mat& a, const Matx<_Tp, m, n>& b) { return a >= Mat(b); } +template static inline +MatExpr operator >= (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) >= b; } + +CV_EXPORTS MatExpr operator > (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator > (const Mat& a, double s); +CV_EXPORTS MatExpr operator > (double s, const Mat& a); +template static inline +MatExpr operator > (const Mat& a, const Matx<_Tp, m, n>& b) { return a > Mat(b); } +template static inline +MatExpr operator > (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) > b; } + +CV_EXPORTS MatExpr operator & (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator & (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator & (const Scalar& s, const Mat& a); +template static inline +MatExpr operator & (const Mat& a, const Matx<_Tp, m, n>& b) { return a & Mat(b); } +template static inline +MatExpr operator & (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) & b; } + +CV_EXPORTS MatExpr operator | (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator | (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator | (const Scalar& s, const Mat& a); +template static inline +MatExpr operator | (const Mat& a, const Matx<_Tp, m, n>& b) { return a | Mat(b); } +template static inline +MatExpr operator | (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) | b; } + +CV_EXPORTS MatExpr operator ^ (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator ^ (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator ^ (const Scalar& s, const Mat& a); +template static inline +MatExpr operator ^ (const Mat& a, const Matx<_Tp, m, n>& b) { return a ^ Mat(b); } +template static inline +MatExpr operator ^ (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) ^ b; } + +CV_EXPORTS MatExpr operator ~(const Mat& m); + +CV_EXPORTS MatExpr min(const Mat& a, const Mat& b); +CV_EXPORTS MatExpr min(const Mat& a, double s); +CV_EXPORTS MatExpr min(double s, const Mat& a); +template static inline +MatExpr min (const Mat& a, const Matx<_Tp, m, n>& b) { return min(a, Mat(b)); } +template static inline +MatExpr min (const Matx<_Tp, m, n>& a, const Mat& b) { return min(Mat(a), b); } + +CV_EXPORTS MatExpr max(const Mat& a, const Mat& b); +CV_EXPORTS MatExpr max(const Mat& a, double s); +CV_EXPORTS MatExpr max(double s, const Mat& a); +template static inline +MatExpr max (const Mat& a, const Matx<_Tp, m, n>& b) { return max(a, Mat(b)); } +template static inline +MatExpr max (const Matx<_Tp, m, n>& a, const Mat& b) { return max(Mat(a), b); } + +/** @brief Calculates an absolute value of each matrix element. + +abs is a meta-function that is expanded to one of absdiff or convertScaleAbs forms: +- C = abs(A-B) is equivalent to `absdiff(A, B, C)` +- C = abs(A) is equivalent to `absdiff(A, Scalar::all(0), C)` +- C = `Mat_ >(abs(A*alpha + beta))` is equivalent to `convertScaleAbs(A, C, alpha, +beta)` + +The output matrix has the same size and the same type as the input one except for the last case, +where C is depth=CV_8U . +@param m matrix. +@sa @ref MatrixExpressions, absdiff, convertScaleAbs + */ +CV_EXPORTS MatExpr abs(const Mat& m); +/** @overload +@param e matrix expression. +*/ +CV_EXPORTS MatExpr abs(const MatExpr& e); +//! @} relates cv::MatExpr + +} // cv + +#include "opencv2/core/mat.inl.hpp" + +#endif // OPENCV_CORE_MAT_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/mat.inl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/mat.inl.hpp new file mode 100644 index 0000000..55b4e62 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/mat.inl.hpp @@ -0,0 +1,4068 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MATRIX_OPERATIONS_HPP +#define OPENCV_CORE_MATRIX_OPERATIONS_HPP + +#ifndef __cplusplus +# error mat.inl.hpp header must be compiled as C++ +#endif + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable: 4127 ) +#endif + +#if defined(CV_SKIP_DISABLE_CLANG_ENUM_WARNINGS) + // nothing +#elif defined(CV_FORCE_DISABLE_CLANG_ENUM_WARNINGS) + #define CV_DISABLE_CLANG_ENUM_WARNINGS +#elif defined(__clang__) && defined(__has_warning) + #if __has_warning("-Wdeprecated-enum-enum-conversion") && __has_warning("-Wdeprecated-anon-enum-enum-conversion") + #define CV_DISABLE_CLANG_ENUM_WARNINGS + #endif +#endif +#ifdef CV_DISABLE_CLANG_ENUM_WARNINGS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" +#pragma clang diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion" +#endif + +namespace cv +{ +CV__DEBUG_NS_BEGIN + + +//! @cond IGNORED + +////////////////////////// Custom (raw) type wrapper ////////////////////////// + +template static inline +int rawType() +{ + CV_StaticAssert(sizeof(_Tp) <= CV_CN_MAX, "sizeof(_Tp) is too large"); + const int elemSize = sizeof(_Tp); + return (int)CV_MAKETYPE(CV_8U, elemSize); +} + +//////////////////////// Input/Output Arrays //////////////////////// + +inline void _InputArray::init(int _flags, const void* _obj) +{ flags = _flags; obj = (void*)_obj; } + +inline void _InputArray::init(int _flags, const void* _obj, Size _sz) +{ flags = _flags; obj = (void*)_obj; sz = _sz; } + +inline void* _InputArray::getObj() const { return obj; } +inline int _InputArray::getFlags() const { return flags; } +inline Size _InputArray::getSz() const { return sz; } + +inline _InputArray::_InputArray() { init(NONE, 0); } +inline _InputArray::_InputArray(int _flags, void* _obj) { init(_flags, _obj); } +inline _InputArray::_InputArray(const Mat& m) { init(MAT+ACCESS_READ, &m); } +inline _InputArray::_InputArray(const std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_READ, &vec); } +inline _InputArray::_InputArray(const UMat& m) { init(UMAT+ACCESS_READ, &m); } +inline _InputArray::_InputArray(const std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputArray::_InputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_READ, arr.data(), Size(1, _Nm)); } + +template inline +_InputArray::_InputArray(const std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_READ, arr.data(), Size(1, _Nm)); } +#endif + +inline +_InputArray::_InputArray(const std::vector& vec) +{ init(FIXED_TYPE + STD_BOOL_VECTOR + traits::Type::value + ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +inline +_InputArray::_InputArray(const std::vector >&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector > is not supported!\n"); } + +template inline +_InputArray::_InputArray(const std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, &mtx, Size(n, m)); } + +template inline +_InputArray::_InputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, vec, Size(n, 1)); } + +template inline +_InputArray::_InputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_READ, &m); } + +inline _InputArray::_InputArray(const double& val) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + CV_64F + ACCESS_READ, &val, Size(1,1)); } + +inline _InputArray::_InputArray(const MatExpr& expr) +{ init(FIXED_TYPE + FIXED_SIZE + EXPR + ACCESS_READ, &expr); } + +inline _InputArray::_InputArray(const cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_READ, &d_mat); } + +inline _InputArray::_InputArray(const std::vector& d_mat) +{ init(STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);} + +inline _InputArray::_InputArray(const ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_READ, &buf); } + +inline _InputArray::_InputArray(const cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_READ, &cuda_mem); } + +template inline +_InputArray _InputArray::rawIn(const std::vector<_Tp>& vec) +{ + _InputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_READ; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputArray _InputArray::rawIn(const std::array<_Tp, _Nm>& arr) +{ + _InputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_READ; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + +inline _InputArray::~_InputArray() {} + +inline Mat _InputArray::getMat(int i) const +{ + if( kind() == MAT && i < 0 ) + return *(const Mat*)obj; + return getMat_(i); +} + +inline bool _InputArray::isMat() const { return kind() == _InputArray::MAT; } +inline bool _InputArray::isUMat() const { return kind() == _InputArray::UMAT; } +inline bool _InputArray::isMatVector() const { return kind() == _InputArray::STD_VECTOR_MAT; } +inline bool _InputArray::isUMatVector() const { return kind() == _InputArray::STD_VECTOR_UMAT; } +inline bool _InputArray::isMatx() const { return kind() == _InputArray::MATX; } +inline bool _InputArray::isVector() const { return kind() == _InputArray::STD_VECTOR || + kind() == _InputArray::STD_BOOL_VECTOR || + kind() == _InputArray::STD_ARRAY; } +inline bool _InputArray::isGpuMat() const { return kind() == _InputArray::CUDA_GPU_MAT; } +inline bool _InputArray::isGpuMatVector() const { return kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT; } + +//////////////////////////////////////////////////////////////////////////////////////// + +inline _OutputArray::_OutputArray() { init(ACCESS_WRITE, 0); } +inline _OutputArray::_OutputArray(int _flags, void* _obj) { init(_flags|ACCESS_WRITE, _obj); } +inline _OutputArray::_OutputArray(Mat& m) { init(MAT+ACCESS_WRITE, &m); } +inline _OutputArray::_OutputArray(std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_WRITE, &vec); } +inline _OutputArray::_OutputArray(UMat& m) { init(UMAT+ACCESS_WRITE, &m); } +inline _OutputArray::_OutputArray(std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray::_OutputArray(std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } + +template inline +_OutputArray::_OutputArray(std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +#endif + +inline +_OutputArray::_OutputArray(std::vector&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector cannot be an output array\n"); } + +template inline +_OutputArray::_OutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +inline +_OutputArray::_OutputArray(std::vector >&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector > cannot be an output array\n"); } + +template inline +_OutputArray::_OutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &m); } + +template inline +_OutputArray::_OutputArray(Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, &mtx, Size(n, m)); } + +template inline +_OutputArray::_OutputArray(_Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, vec, Size(n, 1)); } + +template inline +_OutputArray::_OutputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray::_OutputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } + +template inline +_OutputArray::_OutputArray(const std::array& arr) +{ init(FIXED_SIZE + STD_ARRAY_MAT + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +#endif + +template inline +_OutputArray::_OutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &m); } + +template inline +_OutputArray::_OutputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, &mtx, Size(n, m)); } + +template inline +_OutputArray::_OutputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, vec, Size(n, 1)); } + +inline _OutputArray::_OutputArray(cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } + +inline _OutputArray::_OutputArray(std::vector& d_mat) +{ init(STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);} + +inline _OutputArray::_OutputArray(ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_WRITE, &buf); } + +inline _OutputArray::_OutputArray(cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_WRITE, &cuda_mem); } + +inline _OutputArray::_OutputArray(const Mat& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + ACCESS_WRITE, &m); } + +inline _OutputArray::_OutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_MAT + ACCESS_WRITE, &vec); } + +inline _OutputArray::_OutputArray(const UMat& m) +{ init(FIXED_TYPE + FIXED_SIZE + UMAT + ACCESS_WRITE, &m); } + +inline _OutputArray::_OutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_UMAT + ACCESS_WRITE, &vec); } + +inline _OutputArray::_OutputArray(const cuda::GpuMat& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } + + +inline _OutputArray::_OutputArray(const ogl::Buffer& buf) +{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_WRITE, &buf); } + +inline _OutputArray::_OutputArray(const cuda::HostMem& cuda_mem) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_WRITE, &cuda_mem); } + +template inline +_OutputArray _OutputArray::rawOut(std::vector<_Tp>& vec) +{ + _OutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_WRITE; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray _OutputArray::rawOut(std::array<_Tp, _Nm>& arr) +{ + _OutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////// + +inline _InputOutputArray::_InputOutputArray() { init(ACCESS_RW, 0); } +inline _InputOutputArray::_InputOutputArray(int _flags, void* _obj) { init(_flags|ACCESS_RW, _obj); } +inline _InputOutputArray::_InputOutputArray(Mat& m) { init(MAT+ACCESS_RW, &m); } +inline _InputOutputArray::_InputOutputArray(std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_RW, &vec); } +inline _InputOutputArray::_InputOutputArray(UMat& m) { init(UMAT+ACCESS_RW, &m); } +inline _InputOutputArray::_InputOutputArray(std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray::_InputOutputArray(std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } + +template inline +_InputOutputArray::_InputOutputArray(std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_RW, arr.data(), Size(1, _Nm)); } +#endif + +inline _InputOutputArray::_InputOutputArray(std::vector&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector cannot be an input/output array\n"); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_RW, &m); } + +template inline +_InputOutputArray::_InputOutputArray(Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, &mtx, Size(n, m)); } + +template inline +_InputOutputArray::_InputOutputArray(_Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, vec, Size(n, 1)); } + +template inline +_InputOutputArray::_InputOutputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray::_InputOutputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } + +template inline +_InputOutputArray::_InputOutputArray(const std::array& arr) +{ init(FIXED_SIZE + STD_ARRAY_MAT + ACCESS_RW, arr.data(), Size(1, _Nm)); } +#endif + +template inline +_InputOutputArray::_InputOutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + traits::Type<_Tp>::value + ACCESS_RW, &m); } + +template inline +_InputOutputArray::_InputOutputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, &mtx, Size(n, m)); } + +template inline +_InputOutputArray::_InputOutputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, vec, Size(n, 1)); } + +inline _InputOutputArray::_InputOutputArray(cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_RW, &d_mat); } + +inline _InputOutputArray::_InputOutputArray(ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_RW, &buf); } + +inline _InputOutputArray::_InputOutputArray(cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_RW, &cuda_mem); } + +inline _InputOutputArray::_InputOutputArray(const Mat& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + ACCESS_RW, &m); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_MAT + ACCESS_RW, &vec); } + +inline _InputOutputArray::_InputOutputArray(const UMat& m) +{ init(FIXED_TYPE + FIXED_SIZE + UMAT + ACCESS_RW, &m); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_UMAT + ACCESS_RW, &vec); } + +inline _InputOutputArray::_InputOutputArray(const cuda::GpuMat& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_RW, &d_mat); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);} + +template<> inline _InputOutputArray::_InputOutputArray(std::vector& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);} + +inline _InputOutputArray::_InputOutputArray(const ogl::Buffer& buf) +{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_RW, &buf); } + +inline _InputOutputArray::_InputOutputArray(const cuda::HostMem& cuda_mem) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_RW, &cuda_mem); } + +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::vector<_Tp>& vec) +{ + _InputOutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_RW; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::array<_Tp, _Nm>& arr) +{ + _InputOutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + + +template static inline _InputArray rawIn(_Tp& v) { return _InputArray::rawIn(v); } +template static inline _OutputArray rawOut(_Tp& v) { return _OutputArray::rawOut(v); } +template static inline _InputOutputArray rawInOut(_Tp& v) { return _InputOutputArray::rawInOut(v); } + +CV__DEBUG_NS_END + +//////////////////////////////////////////// Mat ////////////////////////////////////////// + +inline +Mat::Mat() + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{} + +inline +Mat::Mat(int _rows, int _cols, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_rows, _cols, _type); +} + +inline +Mat::Mat(int _rows, int _cols, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_rows, _cols, _type); + *this = _s; +} + +inline +Mat::Mat(Size _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create( _sz.height, _sz.width, _type ); +} + +inline +Mat::Mat(Size _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz.height, _sz.width, _type); + *this = _s; +} + +inline +Mat::Mat(int _dims, const int* _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_dims, _sz, _type); +} + +inline +Mat::Mat(int _dims, const int* _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_dims, _sz, _type); + *this = _s; +} + +inline +Mat::Mat(const std::vector& _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz, _type); +} + +inline +Mat::Mat(const std::vector& _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz, _type); + *this = _s; +} + +inline +Mat::Mat(const Mat& m) + : flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), data(m.data), + datastart(m.datastart), dataend(m.dataend), datalimit(m.datalimit), allocator(m.allocator), + u(m.u), size(&rows), step(0) +{ + if( u ) + CV_XADD(&u->refcount, 1); + if( m.dims <= 2 ) + { + step[0] = m.step[0]; step[1] = m.step[1]; + } + else + { + dims = 0; + copySize(m); + } +} + +inline +Mat::Mat(int _rows, int _cols, int _type, void* _data, size_t _step) + : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_rows), cols(_cols), + data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), + allocator(0), u(0), size(&rows) +{ + CV_Assert(total() == 0 || data != NULL); + + size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); + size_t minstep = cols * esz; + if( _step == AUTO_STEP ) + { + _step = minstep; + } + else + { + CV_DbgAssert( _step >= minstep ); + if (_step % esz1 != 0) + { + CV_Error(Error::BadStep, "Step must be a multiple of esz1"); + } + } + step[0] = _step; + step[1] = esz; + datalimit = datastart + _step * rows; + dataend = datalimit - _step + minstep; + updateContinuityFlag(); +} + +inline +Mat::Mat(Size _sz, int _type, void* _data, size_t _step) + : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_sz.height), cols(_sz.width), + data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), + allocator(0), u(0), size(&rows) +{ + CV_Assert(total() == 0 || data != NULL); + + size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); + size_t minstep = cols*esz; + if( _step == AUTO_STEP ) + { + _step = minstep; + } + else + { + CV_DbgAssert( _step >= minstep ); + + if (_step % esz1 != 0) + { + CV_Error(Error::BadStep, "Step must be a multiple of esz1"); + } + } + step[0] = _step; + step[1] = esz; + datalimit = datastart + _step*rows; + dataend = datalimit - _step + minstep; + updateContinuityFlag(); +} + +template inline +Mat::Mat(const std::vector<_Tp>& vec, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), + cols(1), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if(vec.empty()) + return; + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&vec[0]; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat((int)vec.size(), 1, traits::Type<_Tp>::value, (uchar*)&vec[0]).copyTo(*this); +} + +#ifdef CV_CXX11 +template inline +Mat::Mat(const std::initializer_list<_Tp> list) + : Mat() +{ + CV_Assert(list.size() != 0); + Mat((int)list.size(), 1, traits::Type<_Tp>::value, (uchar*)list.begin()).copyTo(*this); +} + +template inline +Mat::Mat(const std::initializer_list sizes, const std::initializer_list<_Tp> list) + : Mat() +{ + size_t size_total = 1; + for(auto s : sizes) + size_total *= s; + CV_Assert(list.size() != 0); + CV_Assert(size_total == list.size()); + Mat((int)sizes.size(), (int*)sizes.begin(), traits::Type<_Tp>::value, (uchar*)list.begin()).copyTo(*this); +} +#endif + +#ifdef CV_CXX_STD_ARRAY +template inline +Mat::Mat(const std::array<_Tp, _Nm>& arr, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)arr.size()), + cols(1), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if(arr.empty()) + return; + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)arr.data(); + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat((int)arr.size(), 1, traits::Type<_Tp>::value, (uchar*)arr.data()).copyTo(*this); +} +#endif + +template inline +Mat::Mat(const Vec<_Tp, n>& vec, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(n), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)vec.val; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat(n, 1, traits::Type<_Tp>::value, (void*)vec.val).copyTo(*this); +} + + +template inline +Mat::Mat(const Matx<_Tp,m,n>& M, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(m), cols(n), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = cols * sizeof(_Tp); + step[1] = sizeof(_Tp); + datastart = data = (uchar*)M.val; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat(m, n, traits::Type<_Tp>::value, (uchar*)M.val).copyTo(*this); +} + +template inline +Mat::Mat(const Point_<_Tp>& pt, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(2), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&pt.x; + datalimit = dataend = datastart + rows * step[0]; + } + else + { + create(2, 1, traits::Type<_Tp>::value); + ((_Tp*)data)[0] = pt.x; + ((_Tp*)data)[1] = pt.y; + } +} + +template inline +Mat::Mat(const Point3_<_Tp>& pt, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(3), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&pt.x; + datalimit = dataend = datastart + rows * step[0]; + } + else + { + create(3, 1, traits::Type<_Tp>::value); + ((_Tp*)data)[0] = pt.x; + ((_Tp*)data)[1] = pt.y; + ((_Tp*)data)[2] = pt.z; + } +} + +template inline +Mat::Mat(const MatCommaInitializer_<_Tp>& commaInitializer) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(0), rows(0), cols(0), data(0), + datastart(0), dataend(0), allocator(0), u(0), size(&rows) +{ + *this = commaInitializer.operator Mat_<_Tp>(); +} + +inline +Mat::~Mat() +{ + release(); + if( step.p != step.buf ) + fastFree(step.p); +} + +inline +Mat& Mat::operator = (const Mat& m) +{ + if( this != &m ) + { + if( m.u ) + CV_XADD(&m.u->refcount, 1); + release(); + flags = m.flags; + if( dims <= 2 && m.dims <= 2 ) + { + dims = m.dims; + rows = m.rows; + cols = m.cols; + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + copySize(m); + data = m.data; + datastart = m.datastart; + dataend = m.dataend; + datalimit = m.datalimit; + allocator = m.allocator; + u = m.u; + } + return *this; +} + +inline +Mat Mat::row(int y) const +{ + return Mat(*this, Range(y, y + 1), Range::all()); +} + +inline +Mat Mat::col(int x) const +{ + return Mat(*this, Range::all(), Range(x, x + 1)); +} + +inline +Mat Mat::rowRange(int startrow, int endrow) const +{ + return Mat(*this, Range(startrow, endrow), Range::all()); +} + +inline +Mat Mat::rowRange(const Range& r) const +{ + return Mat(*this, r, Range::all()); +} + +inline +Mat Mat::colRange(int startcol, int endcol) const +{ + return Mat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +Mat Mat::colRange(const Range& r) const +{ + return Mat(*this, Range::all(), r); +} + +inline +Mat Mat::clone() const +{ + Mat m; + copyTo(m); + return m; +} + +inline +void Mat::assignTo( Mat& m, int _type ) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +inline +void Mat::create(int _rows, int _cols, int _type) +{ + _type &= TYPE_MASK; + if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data ) + return; + int sz[] = {_rows, _cols}; + create(2, sz, _type); +} + +inline +void Mat::create(Size _sz, int _type) +{ + create(_sz.height, _sz.width, _type); +} + +inline +void Mat::addref() +{ + if( u ) + CV_XADD(&u->refcount, 1); +} + +inline +void Mat::release() +{ + if( u && CV_XADD(&u->refcount, -1) == 1 ) + deallocate(); + u = NULL; + datastart = dataend = datalimit = data = 0; + for(int i = 0; i < dims; i++) + size.p[i] = 0; +#ifdef _DEBUG + flags = MAGIC_VAL; + dims = rows = cols = 0; + if(step.p != step.buf) + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } +#endif +} + +inline +Mat Mat::operator()( Range _rowRange, Range _colRange ) const +{ + return Mat(*this, _rowRange, _colRange); +} + +inline +Mat Mat::operator()( const Rect& roi ) const +{ + return Mat(*this, roi); +} + +inline +Mat Mat::operator()(const Range* ranges) const +{ + return Mat(*this, ranges); +} + +inline +Mat Mat::operator()(const std::vector& ranges) const +{ + return Mat(*this, ranges); +} + +inline +bool Mat::isContinuous() const +{ + return (flags & CONTINUOUS_FLAG) != 0; +} + +inline +bool Mat::isSubmatrix() const +{ + return (flags & SUBMATRIX_FLAG) != 0; +} + +inline +size_t Mat::elemSize() const +{ + size_t res = dims > 0 ? step.p[dims - 1] : 0; + CV_DbgAssert(res != 0); + return res; +} + +inline +size_t Mat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int Mat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int Mat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int Mat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t Mat::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +inline +bool Mat::empty() const +{ + return data == 0 || total() == 0 || dims == 0; +} + +inline +size_t Mat::total() const +{ + if( dims <= 2 ) + return (size_t)rows * cols; + size_t p = 1; + for( int i = 0; i < dims; i++ ) + p *= size[i]; + return p; +} + +inline +size_t Mat::total(int startDim, int endDim) const +{ + CV_Assert( 0 <= startDim && startDim <= endDim); + size_t p = 1; + int endDim_ = endDim <= dims ? endDim : dims; + for( int i = startDim; i < endDim_; i++ ) + p *= size[i]; + return p; +} + +inline +uchar* Mat::ptr(int y) +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return data + step.p[0] * y; +} + +inline +const uchar* Mat::ptr(int y) const +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return data + step.p[0] * y; +} + +template inline +_Tp* Mat::ptr(int y) +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return (_Tp*)(data + step.p[0] * y); +} + +template inline +const _Tp* Mat::ptr(int y) const +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return (const _Tp*)(data + step.p[0] * y); +} + +inline +uchar* Mat::ptr(int i0, int i1) +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return data + i0 * step.p[0] + i1 * step.p[1]; +} + +inline +const uchar* Mat::ptr(int i0, int i1) const +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return data + i0 * step.p[0] + i1 * step.p[1]; +} + +template inline +_Tp* Mat::ptr(int i0, int i1) +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return (_Tp*)(data + i0 * step.p[0] + i1 * step.p[1]); +} + +template inline +const _Tp* Mat::ptr(int i0, int i1) const +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return (const _Tp*)(data + i0 * step.p[0] + i1 * step.p[1]); +} + +inline +uchar* Mat::ptr(int i0, int i1, int i2) +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]; +} + +inline +const uchar* Mat::ptr(int i0, int i1, int i2) const +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]; +} + +template inline +_Tp* Mat::ptr(int i0, int i1, int i2) +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return (_Tp*)(data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]); +} + +template inline +const _Tp* Mat::ptr(int i0, int i1, int i2) const +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return (const _Tp*)(data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]); +} + +inline +uchar* Mat::ptr(const int* idx) +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return p; +} + +inline +const uchar* Mat::ptr(const int* idx) const +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return p; +} + +template inline +_Tp* Mat::ptr(const int* idx) +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return (_Tp*)p; +} + +template inline +const _Tp* Mat::ptr(const int* idx) const +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return (const _Tp*)p; +} + +template inline +_Tp& Mat::at(int i0, int i1) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((_Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +const _Tp& Mat::at(int i0, int i1) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((const _Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +_Tp& Mat::at(Point pt) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(pt.x * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((_Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +const _Tp& Mat::at(Point pt) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(pt.x * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((const _Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +_Tp& Mat::at(int i0) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)(size.p[0] * size.p[1])); + CV_DbgAssert(elemSize() == sizeof(_Tp)); + if( isContinuous() || size.p[0] == 1 ) + return ((_Tp*)data)[i0]; + if( size.p[1] == 1 ) + return *(_Tp*)(data + step.p[0] * i0); + int i = i0 / cols, j = i0 - i * cols; + return ((_Tp*)(data + step.p[0] * i))[j]; +} + +template inline +const _Tp& Mat::at(int i0) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)(size.p[0] * size.p[1])); + CV_DbgAssert(elemSize() == sizeof(_Tp)); + if( isContinuous() || size.p[0] == 1 ) + return ((const _Tp*)data)[i0]; + if( size.p[1] == 1 ) + return *(const _Tp*)(data + step.p[0] * i0); + int i = i0 / cols, j = i0 - i * cols; + return ((const _Tp*)(data + step.p[0] * i))[j]; +} + +template inline +_Tp& Mat::at(int i0, int i1, int i2) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(i0, i1, i2); +} + +template inline +const _Tp& Mat::at(int i0, int i1, int i2) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(i0, i1, i2); +} + +template inline +_Tp& Mat::at(const int* idx) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(idx); +} + +template inline +const _Tp& Mat::at(const int* idx) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(idx); +} + +template inline +_Tp& Mat::at(const Vec& idx) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(idx.val); +} + +template inline +const _Tp& Mat::at(const Vec& idx) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(idx.val); +} + +template inline +MatConstIterator_<_Tp> Mat::begin() const +{ + if (empty()) + return MatConstIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return MatConstIterator_<_Tp>((const Mat_<_Tp>*)this); +} + +template inline +MatConstIterator_<_Tp> Mat::end() const +{ + if (empty()) + return MatConstIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatConstIterator_<_Tp> it((const Mat_<_Tp>*)this); + it += total(); + return it; +} + +template inline +MatIterator_<_Tp> Mat::begin() +{ + if (empty()) + return MatIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return MatIterator_<_Tp>((Mat_<_Tp>*)this); +} + +template inline +MatIterator_<_Tp> Mat::end() +{ + if (empty()) + return MatIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatIterator_<_Tp> it((Mat_<_Tp>*)this); + it += total(); + return it; +} + +template inline +void Mat::forEach(const Functor& operation) { + this->forEach_impl<_Tp>(operation); +} + +template inline +void Mat::forEach(const Functor& operation) const { + // call as not const + (const_cast(this))->forEach<_Tp>(operation); +} + +template inline +Mat::operator std::vector<_Tp>() const +{ + std::vector<_Tp> v; + copyTo(v); + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +Mat::operator std::array<_Tp, _Nm>() const +{ + std::array<_Tp, _Nm> v; + copyTo(v); + return v; +} +#endif + +template inline +Mat::operator Vec<_Tp, n>() const +{ + CV_Assert( data && dims <= 2 && (rows == 1 || cols == 1) && + rows + cols - 1 == n && channels() == 1 ); + + if( isContinuous() && type() == traits::Type<_Tp>::value ) + return Vec<_Tp, n>((_Tp*)data); + Vec<_Tp, n> v; + Mat tmp(rows, cols, traits::Type<_Tp>::value, v.val); + convertTo(tmp, tmp.type()); + return v; +} + +template inline +Mat::operator Matx<_Tp, m, n>() const +{ + CV_Assert( data && dims <= 2 && rows == m && cols == n && channels() == 1 ); + + if( isContinuous() && type() == traits::Type<_Tp>::value ) + return Matx<_Tp, m, n>((_Tp*)data); + Matx<_Tp, m, n> mtx; + Mat tmp(rows, cols, traits::Type<_Tp>::value, mtx.val); + convertTo(tmp, tmp.type()); + return mtx; +} + +template inline +void Mat::push_back(const _Tp& elem) +{ + if( !data ) + { + *this = Mat(1, 1, traits::Type<_Tp>::value, (void*)&elem).clone(); + return; + } + CV_Assert(traits::Type<_Tp>::value == type() && cols == 1 + /* && dims == 2 (cols == 1 implies dims == 2) */); + const uchar* tmp = dataend + step[0]; + if( !isSubmatrix() && isContinuous() && tmp <= datalimit ) + { + *(_Tp*)(data + (size.p[0]++) * step.p[0]) = elem; + dataend = tmp; + } + else + push_back_(&elem); +} + +template inline +void Mat::push_back(const Mat_<_Tp>& m) +{ + push_back((const Mat&)m); +} + +template<> inline +void Mat::push_back(const MatExpr& expr) +{ + push_back(static_cast(expr)); +} + + +template inline +void Mat::push_back(const std::vector<_Tp>& v) +{ + push_back(Mat(v)); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +inline +Mat::Mat(Mat&& m) + : flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), data(m.data), + datastart(m.datastart), dataend(m.dataend), datalimit(m.datalimit), allocator(m.allocator), + u(m.u), size(&rows) +{ + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.data = NULL; m.datastart = NULL; m.dataend = NULL; m.datalimit = NULL; + m.allocator = NULL; + m.u = NULL; +} + +inline +Mat& Mat::operator = (Mat&& m) +{ + if (this == &m) + return *this; + + release(); + flags = m.flags; dims = m.dims; rows = m.rows; cols = m.cols; data = m.data; + datastart = m.datastart; dataend = m.dataend; datalimit = m.datalimit; allocator = m.allocator; + u = m.u; + if (step.p != step.buf) // release self step/size + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.data = NULL; m.datastart = NULL; m.dataend = NULL; m.datalimit = NULL; + m.allocator = NULL; + m.u = NULL; + return *this; +} + +#endif + + +///////////////////////////// MatSize //////////////////////////// + +inline +MatSize::MatSize(int* _p) + : p(_p) {} + +inline +int MatSize::dims() const +{ + return (p - 1)[0]; +} + +inline +Size MatSize::operator()() const +{ + CV_DbgAssert(dims() <= 2); + return Size(p[1], p[0]); +} + +inline +const int& MatSize::operator[](int i) const +{ + CV_DbgAssert(i < dims()); +#ifdef __OPENCV_BUILD + CV_DbgAssert(i >= 0); +#endif + return p[i]; +} + +inline +int& MatSize::operator[](int i) +{ + CV_DbgAssert(i < dims()); +#ifdef __OPENCV_BUILD + CV_DbgAssert(i >= 0); +#endif + return p[i]; +} + +inline +MatSize::operator const int*() const +{ + return p; +} + +inline +bool MatSize::operator == (const MatSize& sz) const +{ + int d = dims(); + int dsz = sz.dims(); + if( d != dsz ) + return false; + if( d == 2 ) + return p[0] == sz.p[0] && p[1] == sz.p[1]; + + for( int i = 0; i < d; i++ ) + if( p[i] != sz.p[i] ) + return false; + return true; +} + +inline +bool MatSize::operator != (const MatSize& sz) const +{ + return !(*this == sz); +} + + + +///////////////////////////// MatStep //////////////////////////// + +inline +MatStep::MatStep() +{ + p = buf; p[0] = p[1] = 0; +} + +inline +MatStep::MatStep(size_t s) +{ + p = buf; p[0] = s; p[1] = 0; +} + +inline +const size_t& MatStep::operator[](int i) const +{ + return p[i]; +} + +inline +size_t& MatStep::operator[](int i) +{ + return p[i]; +} + +inline MatStep::operator size_t() const +{ + CV_DbgAssert( p == buf ); + return buf[0]; +} + +inline MatStep& MatStep::operator = (size_t s) +{ + CV_DbgAssert( p == buf ); + buf[0] = s; + return *this; +} + + + +////////////////////////////// Mat_<_Tp> //////////////////////////// + +template inline +Mat_<_Tp>::Mat_() + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; +} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols) + : Mat(_rows, _cols, traits::Type<_Tp>::value) +{ +} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols, const _Tp& value) + : Mat(_rows, _cols, traits::Type<_Tp>::value) +{ + *this = value; +} + +template inline +Mat_<_Tp>::Mat_(Size _sz) + : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) +{} + +template inline +Mat_<_Tp>::Mat_(Size _sz, const _Tp& value) + : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) +{ + *this = value; +} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz) + : Mat(_dims, _sz, traits::Type<_Tp>::value) +{} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz, const _Tp& _s) + : Mat(_dims, _sz, traits::Type<_Tp>::value, Scalar(_s)) +{} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz, _Tp* _data, const size_t* _steps) + : Mat(_dims, _sz, traits::Type<_Tp>::value, _data, _steps) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_<_Tp>& m, const Range* ranges) + : Mat(m, ranges) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_<_Tp>& m, const std::vector& ranges) + : Mat(m, ranges) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat& m) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = m; +} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m) + : Mat(m) +{} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols, _Tp* _data, size_t steps) + : Mat(_rows, _cols, traits::Type<_Tp>::value, _data, steps) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m, const Range& _rowRange, const Range& _colRange) + : Mat(m, _rowRange, _colRange) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m, const Rect& roi) + : Mat(m, roi) +{} + +template template inline +Mat_<_Tp>::Mat_(const Vec::channel_type, n>& vec, bool copyData) + : Mat(n / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&vec) +{ + CV_Assert(n%DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template template inline +Mat_<_Tp>::Mat_(const Matx::channel_type, m, n>& M, bool copyData) + : Mat(m, n / DataType<_Tp>::channels, traits::Type<_Tp>::value, (void*)&M) +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const Point_::channel_type>& pt, bool copyData) + : Mat(2 / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&pt) +{ + CV_Assert(2 % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const Point3_::channel_type>& pt, bool copyData) + : Mat(3 / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&pt) +{ + CV_Assert(3 % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const MatCommaInitializer_<_Tp>& commaInitializer) + : Mat(commaInitializer) +{} + +template inline +Mat_<_Tp>::Mat_(const std::vector<_Tp>& vec, bool copyData) + : Mat(vec, copyData) +{} + +#ifdef CV_CXX11 +template inline +Mat_<_Tp>::Mat_(std::initializer_list<_Tp> list) + : Mat(list) +{} + +template inline +Mat_<_Tp>::Mat_(const std::initializer_list sizes, std::initializer_list<_Tp> list) + : Mat(sizes, list) +{} +#endif + +#ifdef CV_CXX_STD_ARRAY +template template inline +Mat_<_Tp>::Mat_(const std::array<_Tp, _Nm>& arr, bool copyData) + : Mat(arr, copyData) +{} +#endif + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const Mat& m) +{ + if (m.empty()) + { + release(); + return *this; + } + if( traits::Type<_Tp>::value == m.type() ) + { + Mat::operator = (m); + return *this; + } + if( traits::Depth<_Tp>::value == m.depth() ) + { + return (*this = m.reshape(DataType<_Tp>::channels, m.dims, 0)); + } + CV_Assert(DataType<_Tp>::channels == m.channels() || m.empty()); + m.convertTo(*this, type()); + return *this; +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const Mat_& m) +{ + Mat::operator=(m); + return *this; +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const _Tp& s) +{ + typedef typename DataType<_Tp>::vec_type VT; + Mat::operator=(Scalar((const VT&)s)); + return *this; +} + +template inline +void Mat_<_Tp>::create(int _rows, int _cols) +{ + Mat::create(_rows, _cols, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::create(Size _sz) +{ + Mat::create(_sz, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::create(int _dims, const int* _sz) +{ + Mat::create(_dims, _sz, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::release() +{ + Mat::release(); +#ifdef _DEBUG + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; +#endif +} + +template inline +Mat_<_Tp> Mat_<_Tp>::cross(const Mat_& m) const +{ + return Mat_<_Tp>(Mat::cross(m)); +} + +template template inline +Mat_<_Tp>::operator Mat_() const +{ + return Mat_(static_cast(*this)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::row(int y) const +{ + return Mat_(*this, Range(y, y+1), Range::all()); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::col(int x) const +{ + return Mat_(*this, Range::all(), Range(x, x+1)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::diag(int d) const +{ + return Mat_(Mat::diag(d)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::clone() const +{ + return Mat_(Mat::clone()); +} + +template inline +size_t Mat_<_Tp>::elemSize() const +{ + CV_DbgAssert( Mat::elemSize() == sizeof(_Tp) ); + return sizeof(_Tp); +} + +template inline +size_t Mat_<_Tp>::elemSize1() const +{ + CV_DbgAssert( Mat::elemSize1() == sizeof(_Tp) / DataType<_Tp>::channels ); + return sizeof(_Tp) / DataType<_Tp>::channels; +} + +template inline +int Mat_<_Tp>::type() const +{ + CV_DbgAssert( Mat::type() == traits::Type<_Tp>::value ); + return traits::Type<_Tp>::value; +} + +template inline +int Mat_<_Tp>::depth() const +{ + CV_DbgAssert( Mat::depth() == traits::Depth<_Tp>::value ); + return traits::Depth<_Tp>::value; +} + +template inline +int Mat_<_Tp>::channels() const +{ + CV_DbgAssert( Mat::channels() == DataType<_Tp>::channels ); + return DataType<_Tp>::channels; +} + +template inline +size_t Mat_<_Tp>::stepT(int i) const +{ + return step.p[i] / elemSize(); +} + +template inline +size_t Mat_<_Tp>::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::adjustROI( int dtop, int dbottom, int dleft, int dright ) +{ + return (Mat_<_Tp>&)(Mat::adjustROI(dtop, dbottom, dleft, dright)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Range& _rowRange, const Range& _colRange ) const +{ + return Mat_<_Tp>(*this, _rowRange, _colRange); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Rect& roi ) const +{ + return Mat_<_Tp>(*this, roi); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Range* ranges ) const +{ + return Mat_<_Tp>(*this, ranges); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()(const std::vector& ranges) const +{ + return Mat_<_Tp>(*this, ranges); +} + +template inline +_Tp* Mat_<_Tp>::operator [](int y) +{ + CV_DbgAssert( 0 <= y && y < size.p[0] ); + return (_Tp*)(data + y*step.p[0]); +} + +template inline +const _Tp* Mat_<_Tp>::operator [](int y) const +{ + CV_DbgAssert( 0 <= y && y < size.p[0] ); + return (const _Tp*)(data + y*step.p[0]); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0, int i1) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((_Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0, int i1) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((const _Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +_Tp& Mat_<_Tp>::operator ()(Point pt) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)pt.x < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((_Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(Point pt) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)pt.x < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((const _Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +_Tp& Mat_<_Tp>::operator ()(const int* idx) +{ + return Mat::at<_Tp>(idx); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(const int* idx) const +{ + return Mat::at<_Tp>(idx); +} + +template template inline +_Tp& Mat_<_Tp>::operator ()(const Vec& idx) +{ + return Mat::at<_Tp>(idx); +} + +template template inline +const _Tp& Mat_<_Tp>::operator ()(const Vec& idx) const +{ + return Mat::at<_Tp>(idx); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0) +{ + return this->at<_Tp>(i0); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0) const +{ + return this->at<_Tp>(i0); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0, int i1, int i2) +{ + return this->at<_Tp>(i0, i1, i2); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0, int i1, int i2) const +{ + return this->at<_Tp>(i0, i1, i2); +} + +template inline +Mat_<_Tp>::operator std::vector<_Tp>() const +{ + std::vector<_Tp> v; + copyTo(v); + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template template inline +Mat_<_Tp>::operator std::array<_Tp, _Nm>() const +{ + std::array<_Tp, _Nm> a; + copyTo(a); + return a; +} +#endif + +template template inline +Mat_<_Tp>::operator Vec::channel_type, n>() const +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + return pMat->operator Vec::channel_type, n>(); +#else + return this->Mat::operator Vec::channel_type, n>(); +#endif +} + +template template inline +Mat_<_Tp>::operator Matx::channel_type, m, n>() const +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + Matx::channel_type, m, n> res = pMat->operator Matx::channel_type, m, n>(); + return res; +#else + Matx::channel_type, m, n> res = this->Mat::operator Matx::channel_type, m, n>(); + return res; +#endif +} + +template inline +MatConstIterator_<_Tp> Mat_<_Tp>::begin() const +{ + return Mat::begin<_Tp>(); +} + +template inline +MatConstIterator_<_Tp> Mat_<_Tp>::end() const +{ + return Mat::end<_Tp>(); +} + +template inline +MatIterator_<_Tp> Mat_<_Tp>::begin() +{ + return Mat::begin<_Tp>(); +} + +template inline +MatIterator_<_Tp> Mat_<_Tp>::end() +{ + return Mat::end<_Tp>(); +} + +template template inline +void Mat_<_Tp>::forEach(const Functor& operation) { + Mat::forEach<_Tp, Functor>(operation); +} + +template template inline +void Mat_<_Tp>::forEach(const Functor& operation) const { + Mat::forEach<_Tp, Functor>(operation); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +template inline +Mat_<_Tp>::Mat_(Mat_&& m) + : Mat(std::move(m)) +{ +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (Mat_&& m) +{ + Mat::operator = (std::move(m)); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(Mat&& m) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = std::move(m); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (Mat&& m) +{ + if (m.empty()) + { + release(); + return *this; + } + if( traits::Type<_Tp>::value == m.type() ) + { + Mat::operator = ((Mat&&)m); + return *this; + } + if( traits::Depth<_Tp>::value == m.depth() ) + { + Mat::operator = ((Mat&&)m.reshape(DataType<_Tp>::channels, m.dims, 0)); + return *this; + } + CV_DbgAssert(DataType<_Tp>::channels == m.channels()); + m.convertTo(*this, type()); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(MatExpr&& e) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = Mat(e); +} + +#endif + +///////////////////////////// SparseMat ///////////////////////////// + +inline +SparseMat::SparseMat() + : flags(MAGIC_VAL), hdr(0) +{} + +inline +SparseMat::SparseMat(int _dims, const int* _sizes, int _type) + : flags(MAGIC_VAL), hdr(0) +{ + create(_dims, _sizes, _type); +} + +inline +SparseMat::SparseMat(const SparseMat& m) + : flags(m.flags), hdr(m.hdr) +{ + addref(); +} + +inline +SparseMat::~SparseMat() +{ + release(); +} + +inline +SparseMat& SparseMat::operator = (const SparseMat& m) +{ + if( this != &m ) + { + if( m.hdr ) + CV_XADD(&m.hdr->refcount, 1); + release(); + flags = m.flags; + hdr = m.hdr; + } + return *this; +} + +inline +SparseMat& SparseMat::operator = (const Mat& m) +{ + return (*this = SparseMat(m)); +} + +inline +SparseMat SparseMat::clone() const +{ + SparseMat temp; + this->copyTo(temp); + return temp; +} + +inline +void SparseMat::assignTo( SparseMat& m, int _type ) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +inline +void SparseMat::addref() +{ + if( hdr ) + CV_XADD(&hdr->refcount, 1); +} + +inline +void SparseMat::release() +{ + if( hdr && CV_XADD(&hdr->refcount, -1) == 1 ) + delete hdr; + hdr = 0; +} + +inline +size_t SparseMat::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t SparseMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int SparseMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int SparseMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int SparseMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +const int* SparseMat::size() const +{ + return hdr ? hdr->size : 0; +} + +inline +int SparseMat::size(int i) const +{ + if( hdr ) + { + CV_DbgAssert((unsigned)i < (unsigned)hdr->dims); + return hdr->size[i]; + } + return 0; +} + +inline +int SparseMat::dims() const +{ + return hdr ? hdr->dims : 0; +} + +inline +size_t SparseMat::nzcount() const +{ + return hdr ? hdr->nodeCount : 0; +} + +inline +size_t SparseMat::hash(int i0) const +{ + return (size_t)i0; +} + +inline +size_t SparseMat::hash(int i0, int i1) const +{ + return (size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1; +} + +inline +size_t SparseMat::hash(int i0, int i1, int i2) const +{ + return ((size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1) * HASH_SCALE + (unsigned)i2; +} + +inline +size_t SparseMat::hash(const int* idx) const +{ + size_t h = (unsigned)idx[0]; + if( !hdr ) + return 0; + int d = hdr->dims; + for(int i = 1; i < d; i++ ) + h = h * HASH_SCALE + (unsigned)idx[i]; + return h; +} + +template inline +_Tp& SparseMat::ref(int i0, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, true, hashval); +} + +template inline +_Tp& SparseMat::ref(int i0, int i1, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, i1, true, hashval); +} + +template inline +_Tp& SparseMat::ref(int i0, int i1, int i2, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, i1, i2, true, hashval); +} + +template inline +_Tp& SparseMat::ref(const int* idx, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(idx, true, hashval); +} + +template inline +_Tp SparseMat::value(int i0, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(int i0, int i1, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, i1, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(int i0, int i1, int i2, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, i1, i2, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(const int* idx, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(idx, false, hashval); + return p ? *p : _Tp(); +} + +template inline +const _Tp* SparseMat::find(int i0, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, false, hashval); +} + +template inline +const _Tp* SparseMat::find(int i0, int i1, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, i1, false, hashval); +} + +template inline +const _Tp* SparseMat::find(int i0, int i1, int i2, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, i1, i2, false, hashval); +} + +template inline +const _Tp* SparseMat::find(const int* idx, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(idx, false, hashval); +} + +template inline +_Tp& SparseMat::value(Node* n) +{ + return *(_Tp*)((uchar*)n + hdr->valueOffset); +} + +template inline +const _Tp& SparseMat::value(const Node* n) const +{ + return *(const _Tp*)((const uchar*)n + hdr->valueOffset); +} + +inline +SparseMat::Node* SparseMat::node(size_t nidx) +{ + return (Node*)(void*)&hdr->pool[nidx]; +} + +inline +const SparseMat::Node* SparseMat::node(size_t nidx) const +{ + return (const Node*)(const void*)&hdr->pool[nidx]; +} + +inline +SparseMatIterator SparseMat::begin() +{ + return SparseMatIterator(this); +} + +inline +SparseMatConstIterator SparseMat::begin() const +{ + return SparseMatConstIterator(this); +} + +inline +SparseMatIterator SparseMat::end() +{ + SparseMatIterator it(this); + it.seekEnd(); + return it; +} + +inline +SparseMatConstIterator SparseMat::end() const +{ + SparseMatConstIterator it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatIterator_<_Tp> SparseMat::begin() +{ + return SparseMatIterator_<_Tp>(this); +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat::begin() const +{ + return SparseMatConstIterator_<_Tp>(this); +} + +template inline +SparseMatIterator_<_Tp> SparseMat::end() +{ + SparseMatIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat::end() const +{ + SparseMatConstIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + + + +///////////////////////////// SparseMat_ //////////////////////////// + +template inline +SparseMat_<_Tp>::SparseMat_() +{ + flags = MAGIC_VAL | traits::Type<_Tp>::value; +} + +template inline +SparseMat_<_Tp>::SparseMat_(int _dims, const int* _sizes) + : SparseMat(_dims, _sizes, traits::Type<_Tp>::value) +{} + +template inline +SparseMat_<_Tp>::SparseMat_(const SparseMat& m) +{ + if( m.type() == traits::Type<_Tp>::value ) + *this = (const SparseMat_<_Tp>&)m; + else + m.convertTo(*this, traits::Type<_Tp>::value); +} + +template inline +SparseMat_<_Tp>::SparseMat_(const SparseMat_<_Tp>& m) +{ + this->flags = m.flags; + this->hdr = m.hdr; + if( this->hdr ) + CV_XADD(&this->hdr->refcount, 1); +} + +template inline +SparseMat_<_Tp>::SparseMat_(const Mat& m) +{ + SparseMat sm(m); + *this = sm; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const SparseMat_<_Tp>& m) +{ + if( this != &m ) + { + if( m.hdr ) CV_XADD(&m.hdr->refcount, 1); + release(); + flags = m.flags; + hdr = m.hdr; + } + return *this; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const SparseMat& m) +{ + if( m.type() == traits::Type<_Tp>::value ) + return (*this = (const SparseMat_<_Tp>&)m); + m.convertTo(*this, traits::Type<_Tp>::value); + return *this; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const Mat& m) +{ + return (*this = SparseMat(m)); +} + +template inline +SparseMat_<_Tp> SparseMat_<_Tp>::clone() const +{ + SparseMat_<_Tp> m; + this->copyTo(m); + return m; +} + +template inline +void SparseMat_<_Tp>::create(int _dims, const int* _sizes) +{ + SparseMat::create(_dims, _sizes, traits::Type<_Tp>::value); +} + +template inline +int SparseMat_<_Tp>::type() const +{ + return traits::Type<_Tp>::value; +} + +template inline +int SparseMat_<_Tp>::depth() const +{ + return traits::Depth<_Tp>::value; +} + +template inline +int SparseMat_<_Tp>::channels() const +{ + return DataType<_Tp>::channels; +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, int i1, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, i1, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, int i1, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, i1, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, int i1, int i2, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, i1, i2, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, int i1, int i2, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, i1, i2, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(const int* idx, size_t* hashval) +{ + return SparseMat::ref<_Tp>(idx, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(const int* idx, size_t* hashval) const +{ + return SparseMat::value<_Tp>(idx, hashval); +} + +template inline +SparseMatIterator_<_Tp> SparseMat_<_Tp>::begin() +{ + return SparseMatIterator_<_Tp>(this); +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat_<_Tp>::begin() const +{ + return SparseMatConstIterator_<_Tp>(this); +} + +template inline +SparseMatIterator_<_Tp> SparseMat_<_Tp>::end() +{ + SparseMatIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat_<_Tp>::end() const +{ + SparseMatConstIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + + + +////////////////////////// MatConstIterator ///////////////////////// + +inline +MatConstIterator::MatConstIterator() + : m(0), elemSize(0), ptr(0), sliceStart(0), sliceEnd(0) +{} + +inline +MatConstIterator::MatConstIterator(const Mat* _m) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + if( m && m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + seek((const int*)0); +} + +inline +MatConstIterator::MatConstIterator(const Mat* _m, int _row, int _col) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + CV_Assert(m && m->dims <= 2); + if( m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + int idx[] = {_row, _col}; + seek(idx); +} + +inline +MatConstIterator::MatConstIterator(const Mat* _m, Point _pt) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + CV_Assert(m && m->dims <= 2); + if( m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + int idx[] = {_pt.y, _pt.x}; + seek(idx); +} + +inline +MatConstIterator::MatConstIterator(const MatConstIterator& it) + : m(it.m), elemSize(it.elemSize), ptr(it.ptr), sliceStart(it.sliceStart), sliceEnd(it.sliceEnd) +{} + +inline +MatConstIterator& MatConstIterator::operator = (const MatConstIterator& it ) +{ + m = it.m; elemSize = it.elemSize; ptr = it.ptr; + sliceStart = it.sliceStart; sliceEnd = it.sliceEnd; + return *this; +} + +inline +const uchar* MatConstIterator::operator *() const +{ + return ptr; +} + +inline MatConstIterator& MatConstIterator::operator += (ptrdiff_t ofs) +{ + if( !m || ofs == 0 ) + return *this; + ptrdiff_t ofsb = ofs*elemSize; + ptr += ofsb; + if( ptr < sliceStart || sliceEnd <= ptr ) + { + ptr -= ofsb; + seek(ofs, true); + } + return *this; +} + +inline +MatConstIterator& MatConstIterator::operator -= (ptrdiff_t ofs) +{ + return (*this += -ofs); +} + +inline +MatConstIterator& MatConstIterator::operator --() +{ + if( m && (ptr -= elemSize) < sliceStart ) + { + ptr += elemSize; + seek(-1, true); + } + return *this; +} + +inline +MatConstIterator MatConstIterator::operator --(int) +{ + MatConstIterator b = *this; + *this += -1; + return b; +} + +inline +MatConstIterator& MatConstIterator::operator ++() +{ + if( m && (ptr += elemSize) >= sliceEnd ) + { + ptr -= elemSize; + seek(1, true); + } + return *this; +} + +inline MatConstIterator MatConstIterator::operator ++(int) +{ + MatConstIterator b = *this; + *this += 1; + return b; +} + + +static inline +bool operator == (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +static inline +bool operator != (const MatConstIterator& a, const MatConstIterator& b) +{ + return !(a == b); +} + +static inline +bool operator < (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr < b.ptr; +} + +static inline +bool operator > (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr > b.ptr; +} + +static inline +bool operator <= (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr <= b.ptr; +} + +static inline +bool operator >= (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr >= b.ptr; +} + +static inline +ptrdiff_t operator - (const MatConstIterator& b, const MatConstIterator& a) +{ + if( a.m != b.m ) + return ((size_t)(-1) >> 1); + if( a.sliceEnd == b.sliceEnd ) + return (b.ptr - a.ptr)/static_cast(b.elemSize); + + return b.lpos() - a.lpos(); +} + +static inline +MatConstIterator operator + (const MatConstIterator& a, ptrdiff_t ofs) +{ + MatConstIterator b = a; + return b += ofs; +} + +static inline +MatConstIterator operator + (ptrdiff_t ofs, const MatConstIterator& a) +{ + MatConstIterator b = a; + return b += ofs; +} + +static inline +MatConstIterator operator - (const MatConstIterator& a, ptrdiff_t ofs) +{ + MatConstIterator b = a; + return b += -ofs; +} + + +inline +const uchar* MatConstIterator::operator [](ptrdiff_t i) const +{ + return *(*this + i); +} + + + +///////////////////////// MatConstIterator_ ///////////////////////// + +template inline +MatConstIterator_<_Tp>::MatConstIterator_() +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m) + : MatConstIterator(_m) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m, int _row, int _col) + : MatConstIterator(_m, _row, _col) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m, Point _pt) + : MatConstIterator(_m, _pt) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const MatConstIterator_& it) + : MatConstIterator(it) +{} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator = (const MatConstIterator_& it ) +{ + MatConstIterator::operator = (it); + return *this; +} + +template inline +const _Tp& MatConstIterator_<_Tp>::operator *() const +{ + return *(_Tp*)(this->ptr); +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator += (ptrdiff_t ofs) +{ + MatConstIterator::operator += (ofs); + return *this; +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator -= (ptrdiff_t ofs) +{ + return (*this += -ofs); +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator --() +{ + MatConstIterator::operator --(); + return *this; +} + +template inline +MatConstIterator_<_Tp> MatConstIterator_<_Tp>::operator --(int) +{ + MatConstIterator_ b = *this; + MatConstIterator::operator --(); + return b; +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator ++() +{ + MatConstIterator::operator ++(); + return *this; +} + +template inline +MatConstIterator_<_Tp> MatConstIterator_<_Tp>::operator ++(int) +{ + MatConstIterator_ b = *this; + MatConstIterator::operator ++(); + return b; +} + + +template inline +Point MatConstIterator_<_Tp>::pos() const +{ + if( !m ) + return Point(); + CV_DbgAssert( m->dims <= 2 ); + if( m->isContinuous() ) + { + ptrdiff_t ofs = (const _Tp*)ptr - (const _Tp*)m->data; + int y = (int)(ofs / m->cols); + int x = (int)(ofs - (ptrdiff_t)y * m->cols); + return Point(x, y); + } + else + { + ptrdiff_t ofs = (uchar*)ptr - m->data; + int y = (int)(ofs / m->step); + int x = (int)((ofs - y * m->step)/sizeof(_Tp)); + return Point(x, y); + } +} + + +template static inline +bool operator == (const MatConstIterator_<_Tp>& a, const MatConstIterator_<_Tp>& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +template static inline +bool operator != (const MatConstIterator_<_Tp>& a, const MatConstIterator_<_Tp>& b) +{ + return a.m != b.m || a.ptr != b.ptr; +} + +template static inline +MatConstIterator_<_Tp> operator + (const MatConstIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template static inline +MatConstIterator_<_Tp> operator + (ptrdiff_t ofs, const MatConstIterator_<_Tp>& a) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template static inline +MatConstIterator_<_Tp> operator - (const MatConstIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a - ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template inline +const _Tp& MatConstIterator_<_Tp>::operator [](ptrdiff_t i) const +{ + return *(_Tp*)MatConstIterator::operator [](i); +} + + + +//////////////////////////// MatIterator_ /////////////////////////// + +template inline +MatIterator_<_Tp>::MatIterator_() + : MatConstIterator_<_Tp>() +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m) + : MatConstIterator_<_Tp>(_m) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, int _row, int _col) + : MatConstIterator_<_Tp>(_m, _row, _col) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, Point _pt) + : MatConstIterator_<_Tp>(_m, _pt) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, const int* _idx) + : MatConstIterator_<_Tp>(_m, _idx) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(const MatIterator_& it) + : MatConstIterator_<_Tp>(it) +{} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator = (const MatIterator_<_Tp>& it ) +{ + MatConstIterator::operator = (it); + return *this; +} + +template inline +_Tp& MatIterator_<_Tp>::operator *() const +{ + return *(_Tp*)(this->ptr); +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator += (ptrdiff_t ofs) +{ + MatConstIterator::operator += (ofs); + return *this; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator -= (ptrdiff_t ofs) +{ + MatConstIterator::operator += (-ofs); + return *this; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator --() +{ + MatConstIterator::operator --(); + return *this; +} + +template inline +MatIterator_<_Tp> MatIterator_<_Tp>::operator --(int) +{ + MatIterator_ b = *this; + MatConstIterator::operator --(); + return b; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator ++() +{ + MatConstIterator::operator ++(); + return *this; +} + +template inline +MatIterator_<_Tp> MatIterator_<_Tp>::operator ++(int) +{ + MatIterator_ b = *this; + MatConstIterator::operator ++(); + return b; +} + +template inline +_Tp& MatIterator_<_Tp>::operator [](ptrdiff_t i) const +{ + return *(*this + i); +} + + +template static inline +bool operator == (const MatIterator_<_Tp>& a, const MatIterator_<_Tp>& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +template static inline +bool operator != (const MatIterator_<_Tp>& a, const MatIterator_<_Tp>& b) +{ + return a.m != b.m || a.ptr != b.ptr; +} + +template static inline +MatIterator_<_Tp> operator + (const MatIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatIterator_<_Tp>&)t; +} + +template static inline +MatIterator_<_Tp> operator + (ptrdiff_t ofs, const MatIterator_<_Tp>& a) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatIterator_<_Tp>&)t; +} + +template static inline +MatIterator_<_Tp> operator - (const MatIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a - ofs; + return (MatIterator_<_Tp>&)t; +} + + + +/////////////////////// SparseMatConstIterator ////////////////////// + +inline +SparseMatConstIterator::SparseMatConstIterator() + : m(0), hashidx(0), ptr(0) +{} + +inline +SparseMatConstIterator::SparseMatConstIterator(const SparseMatConstIterator& it) + : m(it.m), hashidx(it.hashidx), ptr(it.ptr) +{} + +inline SparseMatConstIterator& SparseMatConstIterator::operator = (const SparseMatConstIterator& it) +{ + if( this != &it ) + { + m = it.m; + hashidx = it.hashidx; + ptr = it.ptr; + } + return *this; +} + +template inline +const _Tp& SparseMatConstIterator::value() const +{ + return *(const _Tp*)ptr; +} + +inline +const SparseMat::Node* SparseMatConstIterator::node() const +{ + return (ptr && m && m->hdr) ? (const SparseMat::Node*)(const void*)(ptr - m->hdr->valueOffset) : 0; +} + +inline +SparseMatConstIterator SparseMatConstIterator::operator ++(int) +{ + SparseMatConstIterator it = *this; + ++*this; + return it; +} + +inline +void SparseMatConstIterator::seekEnd() +{ + if( m && m->hdr ) + { + hashidx = m->hdr->hashtab.size(); + ptr = 0; + } +} + + +static inline +bool operator == (const SparseMatConstIterator& it1, const SparseMatConstIterator& it2) +{ + return it1.m == it2.m && it1.ptr == it2.ptr; +} + +static inline +bool operator != (const SparseMatConstIterator& it1, const SparseMatConstIterator& it2) +{ + return !(it1 == it2); +} + + + +///////////////////////// SparseMatIterator ///////////////////////// + +inline +SparseMatIterator::SparseMatIterator() +{} + +inline +SparseMatIterator::SparseMatIterator(SparseMat* _m) + : SparseMatConstIterator(_m) +{} + +inline +SparseMatIterator::SparseMatIterator(const SparseMatIterator& it) + : SparseMatConstIterator(it) +{} + +inline +SparseMatIterator& SparseMatIterator::operator = (const SparseMatIterator& it) +{ + (SparseMatConstIterator&)*this = it; + return *this; +} + +template inline +_Tp& SparseMatIterator::value() const +{ + return *(_Tp*)ptr; +} + +inline +SparseMat::Node* SparseMatIterator::node() const +{ + return (SparseMat::Node*)SparseMatConstIterator::node(); +} + +inline +SparseMatIterator& SparseMatIterator::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +inline +SparseMatIterator SparseMatIterator::operator ++(int) +{ + SparseMatIterator it = *this; + ++*this; + return it; +} + + + +////////////////////// SparseMatConstIterator_ ////////////////////// + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_() +{} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMat_<_Tp>* _m) + : SparseMatConstIterator(_m) +{} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMat* _m) + : SparseMatConstIterator(_m) +{ + CV_Assert( _m->type() == traits::Type<_Tp>::value ); +} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMatConstIterator_<_Tp>& it) + : SparseMatConstIterator(it) +{} + +template inline +SparseMatConstIterator_<_Tp>& SparseMatConstIterator_<_Tp>::operator = (const SparseMatConstIterator_<_Tp>& it) +{ + return reinterpret_cast&> + (*reinterpret_cast(this) = + reinterpret_cast(it)); +} + +template inline +const _Tp& SparseMatConstIterator_<_Tp>::operator *() const +{ + return *(const _Tp*)this->ptr; +} + +template inline +SparseMatConstIterator_<_Tp>& SparseMatConstIterator_<_Tp>::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMatConstIterator_<_Tp>::operator ++(int) +{ + SparseMatConstIterator_<_Tp> it = *this; + SparseMatConstIterator::operator ++(); + return it; +} + + + +///////////////////////// SparseMatIterator_ //////////////////////// + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_() +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(SparseMat_<_Tp>* _m) + : SparseMatConstIterator_<_Tp>(_m) +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(SparseMat* _m) + : SparseMatConstIterator_<_Tp>(_m) +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(const SparseMatIterator_<_Tp>& it) + : SparseMatConstIterator_<_Tp>(it) +{} + +template inline +SparseMatIterator_<_Tp>& SparseMatIterator_<_Tp>::operator = (const SparseMatIterator_<_Tp>& it) +{ + return reinterpret_cast&> + (*reinterpret_cast(this) = + reinterpret_cast(it)); +} + +template inline +_Tp& SparseMatIterator_<_Tp>::operator *() const +{ + return *(_Tp*)this->ptr; +} + +template inline +SparseMatIterator_<_Tp>& SparseMatIterator_<_Tp>::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +template inline +SparseMatIterator_<_Tp> SparseMatIterator_<_Tp>::operator ++(int) +{ + SparseMatIterator_<_Tp> it = *this; + SparseMatConstIterator::operator ++(); + return it; +} + + + +//////////////////////// MatCommaInitializer_ /////////////////////// + +template inline +MatCommaInitializer_<_Tp>::MatCommaInitializer_(Mat_<_Tp>* _m) + : it(_m) +{} + +template template inline +MatCommaInitializer_<_Tp>& MatCommaInitializer_<_Tp>::operator , (T2 v) +{ + CV_DbgAssert( this->it < ((const Mat_<_Tp>*)this->it.m)->end() ); + *this->it = _Tp(v); + ++this->it; + return *this; +} + +template inline +MatCommaInitializer_<_Tp>::operator Mat_<_Tp>() const +{ + CV_DbgAssert( this->it == ((const Mat_<_Tp>*)this->it.m)->end() ); + return Mat_<_Tp>(*this->it.m); +} + + +template static inline +MatCommaInitializer_<_Tp> operator << (const Mat_<_Tp>& m, T2 val) +{ + MatCommaInitializer_<_Tp> commaInitializer((Mat_<_Tp>*)&m); + return (commaInitializer, val); +} + + + +///////////////////////// Matrix Expressions //////////////////////// + +inline +Mat& Mat::operator = (const MatExpr& e) +{ + e.op->assign(e, *this); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(const MatExpr& e) +{ + e.op->assign(e, *this, traits::Type<_Tp>::value); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const MatExpr& e) +{ + e.op->assign(e, *this, traits::Type<_Tp>::value); + return *this; +} + +template inline +MatExpr Mat_<_Tp>::zeros(int rows, int cols) +{ + return Mat::zeros(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::zeros(Size sz) +{ + return Mat::zeros(sz, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::ones(int rows, int cols) +{ + return Mat::ones(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::ones(Size sz) +{ + return Mat::ones(sz, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::eye(int rows, int cols) +{ + return Mat::eye(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::eye(Size sz) +{ + return Mat::eye(sz, traits::Type<_Tp>::value); +} + +inline +MatExpr::MatExpr() + : op(0), flags(0), a(Mat()), b(Mat()), c(Mat()), alpha(0), beta(0), s() +{} + +inline +MatExpr::MatExpr(const MatOp* _op, int _flags, const Mat& _a, const Mat& _b, + const Mat& _c, double _alpha, double _beta, const Scalar& _s) + : op(_op), flags(_flags), a(_a), b(_b), c(_c), alpha(_alpha), beta(_beta), s(_s) +{} + +inline +MatExpr::operator Mat() const +{ + Mat m; + op->assign(*this, m); + return m; +} + +template inline +MatExpr::operator Mat_<_Tp>() const +{ + Mat_<_Tp> m; + op->assign(*this, m, traits::Type<_Tp>::value); + return m; +} + + +template static inline +MatExpr min(const Mat_<_Tp>& a, const Mat_<_Tp>& b) +{ + return cv::min((const Mat&)a, (const Mat&)b); +} + +template static inline +MatExpr min(const Mat_<_Tp>& a, double s) +{ + return cv::min((const Mat&)a, s); +} + +template static inline +MatExpr min(double s, const Mat_<_Tp>& a) +{ + return cv::min((const Mat&)a, s); +} + +template static inline +MatExpr max(const Mat_<_Tp>& a, const Mat_<_Tp>& b) +{ + return cv::max((const Mat&)a, (const Mat&)b); +} + +template static inline +MatExpr max(const Mat_<_Tp>& a, double s) +{ + return cv::max((const Mat&)a, s); +} + +template static inline +MatExpr max(double s, const Mat_<_Tp>& a) +{ + return cv::max((const Mat&)a, s); +} + +template static inline +MatExpr abs(const Mat_<_Tp>& m) +{ + return cv::abs((const Mat&)m); +} + + +static inline +Mat& operator += (Mat& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, a); + return a; +} + +static inline +const Mat& operator += (const Mat& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator += (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator += (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, (Mat&)a); + return a; +} + +static inline +Mat& operator -= (Mat& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, a); + return a; +} + +static inline +const Mat& operator -= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator -= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator -= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, (Mat&)a); + return a; +} + +static inline +Mat& operator *= (Mat& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, a); + return a; +} + +static inline +const Mat& operator *= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator *= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator *= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, (Mat&)a); + return a; +} + +static inline +Mat& operator /= (Mat& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, a); + return a; +} + +static inline +const Mat& operator /= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator /= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator /= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, (Mat&)a); + return a; +} + + +//////////////////////////////// UMat //////////////////////////////// + +inline +UMat::UMat(UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{} + +inline +UMat::UMat(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_rows, _cols, _type); +} + +inline +UMat::UMat(int _rows, int _cols, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_rows, _cols, _type); + *this = _s; +} + +inline +UMat::UMat(Size _sz, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create( _sz.height, _sz.width, _type ); +} + +inline +UMat::UMat(Size _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_sz.height, _sz.width, _type); + *this = _s; +} + +inline +UMat::UMat(int _dims, const int* _sz, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_dims, _sz, _type); +} + +inline +UMat::UMat(int _dims, const int* _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_dims, _sz, _type); + *this = _s; +} + +inline +UMat::UMat(const UMat& m) +: flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), allocator(m.allocator), + usageFlags(m.usageFlags), u(m.u), offset(m.offset), size(&rows) +{ + addref(); + if( m.dims <= 2 ) + { + step[0] = m.step[0]; step[1] = m.step[1]; + } + else + { + dims = 0; + copySize(m); + } +} + + +template inline +UMat::UMat(const std::vector<_Tp>& vec, bool copyData) +: flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), +cols(1), allocator(0), usageFlags(USAGE_DEFAULT), u(0), offset(0), size(&rows) +{ + if(vec.empty()) + return; + if( !copyData ) + { + // !!!TODO!!! + CV_Error(Error::StsNotImplemented, ""); + } + else + Mat((int)vec.size(), 1, traits::Type<_Tp>::value, (uchar*)&vec[0]).copyTo(*this); +} + +inline +UMat& UMat::operator = (const UMat& m) +{ + if( this != &m ) + { + const_cast(m).addref(); + release(); + flags = m.flags; + if( dims <= 2 && m.dims <= 2 ) + { + dims = m.dims; + rows = m.rows; + cols = m.cols; + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + copySize(m); + allocator = m.allocator; + if (usageFlags == USAGE_DEFAULT) + usageFlags = m.usageFlags; + u = m.u; + offset = m.offset; + } + return *this; +} + +inline +UMat UMat::row(int y) const +{ + return UMat(*this, Range(y, y + 1), Range::all()); +} + +inline +UMat UMat::col(int x) const +{ + return UMat(*this, Range::all(), Range(x, x + 1)); +} + +inline +UMat UMat::rowRange(int startrow, int endrow) const +{ + return UMat(*this, Range(startrow, endrow), Range::all()); +} + +inline +UMat UMat::rowRange(const Range& r) const +{ + return UMat(*this, r, Range::all()); +} + +inline +UMat UMat::colRange(int startcol, int endcol) const +{ + return UMat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +UMat UMat::colRange(const Range& r) const +{ + return UMat(*this, Range::all(), r); +} + +inline +UMat UMat::clone() const +{ + UMat m; + copyTo(m); + return m; +} + +inline +void UMat::assignTo( UMat& m, int _type ) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +inline +void UMat::create(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) +{ + _type &= TYPE_MASK; + if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && u ) + return; + int sz[] = {_rows, _cols}; + create(2, sz, _type, _usageFlags); +} + +inline +void UMat::create(Size _sz, int _type, UMatUsageFlags _usageFlags) +{ + create(_sz.height, _sz.width, _type, _usageFlags); +} + +inline +void UMat::addref() +{ + if( u ) + CV_XADD(&(u->urefcount), 1); +} + +inline void UMat::release() +{ + if( u && CV_XADD(&(u->urefcount), -1) == 1 ) + deallocate(); + for(int i = 0; i < dims; i++) + size.p[i] = 0; + u = 0; +} + +inline +UMat UMat::operator()( Range _rowRange, Range _colRange ) const +{ + return UMat(*this, _rowRange, _colRange); +} + +inline +UMat UMat::operator()( const Rect& roi ) const +{ + return UMat(*this, roi); +} + +inline +UMat UMat::operator()(const Range* ranges) const +{ + return UMat(*this, ranges); +} + +inline +UMat UMat::operator()(const std::vector& ranges) const +{ + return UMat(*this, ranges); +} + +inline +bool UMat::isContinuous() const +{ + return (flags & CONTINUOUS_FLAG) != 0; +} + +inline +bool UMat::isSubmatrix() const +{ + return (flags & SUBMATRIX_FLAG) != 0; +} + +inline +size_t UMat::elemSize() const +{ + size_t res = dims > 0 ? step.p[dims - 1] : 0; + CV_DbgAssert(res != 0); + return res; +} + +inline +size_t UMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int UMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int UMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int UMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t UMat::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +inline +bool UMat::empty() const +{ + return u == 0 || total() == 0 || dims == 0; +} + +inline +size_t UMat::total() const +{ + if( dims <= 2 ) + return (size_t)rows * cols; + size_t p = 1; + for( int i = 0; i < dims; i++ ) + p *= size[i]; + return p; +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +inline +UMat::UMat(UMat&& m) +: flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), allocator(m.allocator), + usageFlags(m.usageFlags), u(m.u), offset(m.offset), size(&rows) +{ + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.allocator = NULL; + m.u = NULL; + m.offset = 0; +} + +inline +UMat& UMat::operator = (UMat&& m) +{ + if (this == &m) + return *this; + release(); + flags = m.flags; dims = m.dims; rows = m.rows; cols = m.cols; + allocator = m.allocator; usageFlags = m.usageFlags; + u = m.u; + offset = m.offset; + if (step.p != step.buf) // release self step/size + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.allocator = NULL; + m.u = NULL; + m.offset = 0; + return *this; +} + +#endif + + +inline bool UMatData::hostCopyObsolete() const { return (flags & HOST_COPY_OBSOLETE) != 0; } +inline bool UMatData::deviceCopyObsolete() const { return (flags & DEVICE_COPY_OBSOLETE) != 0; } +inline bool UMatData::deviceMemMapped() const { return (flags & DEVICE_MEM_MAPPED) != 0; } +inline bool UMatData::copyOnMap() const { return (flags & COPY_ON_MAP) != 0; } +inline bool UMatData::tempUMat() const { return (flags & TEMP_UMAT) != 0; } +inline bool UMatData::tempCopiedUMat() const { return (flags & TEMP_COPIED_UMAT) == TEMP_COPIED_UMAT; } + +inline void UMatData::markDeviceMemMapped(bool flag) +{ + if(flag) + flags |= DEVICE_MEM_MAPPED; + else + flags &= ~DEVICE_MEM_MAPPED; +} + +inline void UMatData::markHostCopyObsolete(bool flag) +{ + if(flag) + flags |= HOST_COPY_OBSOLETE; + else + flags &= ~HOST_COPY_OBSOLETE; +} +inline void UMatData::markDeviceCopyObsolete(bool flag) +{ + if(flag) + flags |= DEVICE_COPY_OBSOLETE; + else + flags &= ~DEVICE_COPY_OBSOLETE; +} + +//! @endcond + +} //cv + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#ifdef CV_DISABLE_CLANG_ENUM_WARNINGS +#undef CV_DISABLE_CLANG_ENUM_WARNINGS +#pragma clang diagnostic pop +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/matx.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/matx.hpp new file mode 100644 index 0000000..1b84f3a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/matx.hpp @@ -0,0 +1,1509 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MATX_HPP +#define OPENCV_CORE_MATX_HPP + +#ifndef __cplusplus +# error matx.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/base.hpp" +#include "opencv2/core/traits.hpp" +#include "opencv2/core/saturate.hpp" + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +////////////////////////////// Small Matrix /////////////////////////// + +//! @cond IGNORED +// FIXIT Remove this (especially CV_EXPORTS modifier) +struct CV_EXPORTS Matx_AddOp { Matx_AddOp() {} Matx_AddOp(const Matx_AddOp&) {} }; +struct CV_EXPORTS Matx_SubOp { Matx_SubOp() {} Matx_SubOp(const Matx_SubOp&) {} }; +struct CV_EXPORTS Matx_ScaleOp { Matx_ScaleOp() {} Matx_ScaleOp(const Matx_ScaleOp&) {} }; +struct CV_EXPORTS Matx_MulOp { Matx_MulOp() {} Matx_MulOp(const Matx_MulOp&) {} }; +struct CV_EXPORTS Matx_DivOp { Matx_DivOp() {} Matx_DivOp(const Matx_DivOp&) {} }; +struct CV_EXPORTS Matx_MatMulOp { Matx_MatMulOp() {} Matx_MatMulOp(const Matx_MatMulOp&) {} }; +struct CV_EXPORTS Matx_TOp { Matx_TOp() {} Matx_TOp(const Matx_TOp&) {} }; +//! @endcond + +/** @brief Template class for small matrices whose type and size are known at compilation time + +If you need a more flexible type, use Mat . The elements of the matrix M are accessible using the +M(i,j) notation. Most of the common matrix operations (see also @ref MatrixExpressions ) are +available. To do an operation on Matx that is not implemented, you can easily convert the matrix to +Mat and backwards: +@code{.cpp} + Matx33f m(1, 2, 3, + 4, 5, 6, + 7, 8, 9); + cout << sum(Mat(m*m.t())) << endl; +@endcode +Except of the plain constructor which takes a list of elements, Matx can be initialized from a C-array: +@code{.cpp} + float values[] = { 1, 2, 3}; + Matx31f m(values); +@endcode +In case if C++11 features are available, std::initializer_list can be also used to initialize Matx: +@code{.cpp} + Matx31f m = { 1, 2, 3}; +@endcode + */ +template class Matx +{ +public: + enum { + rows = m, + cols = n, + channels = rows*cols, +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = traits::Type<_Tp>::value, + type = CV_MAKETYPE(depth, channels), +#endif + shortdim = (m < n ? m : n) + }; + + typedef _Tp value_type; + typedef Matx<_Tp, m, n> mat_type; + typedef Matx<_Tp, shortdim, 1> diag_type; + + //! default constructor + Matx(); + + explicit Matx(_Tp v0); //!< 1x1 matrix + Matx(_Tp v0, _Tp v1); //!< 1x2 or 2x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2); //!< 1x3 or 3x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 1x4, 2x2 or 4x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 1x5 or 5x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 1x6, 2x3, 3x2 or 6x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 1x7 or 7x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 1x8, 2x4, 4x2 or 8x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 1x9, 3x3 or 9x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 1x10, 2x5 or 5x2 or 10x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11); //!< 1x12, 2x6, 3x4, 4x3, 6x2 or 12x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11, + _Tp v12, _Tp v13); //!< 1x14, 2x7, 7x2 or 14x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11, + _Tp v12, _Tp v13, _Tp v14, _Tp v15); //!< 1x16, 4x4 or 16x1 matrix + explicit Matx(const _Tp* vals); //!< initialize from a plain array + +#ifdef CV_CXX11 + Matx(std::initializer_list<_Tp>); //!< initialize from an initializer list +#endif + + static Matx all(_Tp alpha); + static Matx zeros(); + static Matx ones(); + static Matx eye(); + static Matx diag(const diag_type& d); + static Matx randu(_Tp a, _Tp b); + static Matx randn(_Tp a, _Tp b); + + //! dot product computed with the default precision + _Tp dot(const Matx<_Tp, m, n>& v) const; + + //! dot product computed in double-precision arithmetics + double ddot(const Matx<_Tp, m, n>& v) const; + + //! conversion to another data type + template operator Matx() const; + + //! change the matrix shape + template Matx<_Tp, m1, n1> reshape() const; + + //! extract part of the matrix + template Matx<_Tp, m1, n1> get_minor(int base_row, int base_col) const; + + //! extract the matrix row + Matx<_Tp, 1, n> row(int i) const; + + //! extract the matrix column + Matx<_Tp, m, 1> col(int i) const; + + //! extract the matrix diagonal + diag_type diag() const; + + //! transpose the matrix + Matx<_Tp, n, m> t() const; + + //! invert the matrix + Matx<_Tp, n, m> inv(int method=DECOMP_LU, bool *p_is_ok = NULL) const; + + //! solve linear system + template Matx<_Tp, n, l> solve(const Matx<_Tp, m, l>& rhs, int flags=DECOMP_LU) const; + Vec<_Tp, n> solve(const Vec<_Tp, m>& rhs, int method) const; + + //! multiply two matrices element-wise + Matx<_Tp, m, n> mul(const Matx<_Tp, m, n>& a) const; + + //! divide two matrices element-wise + Matx<_Tp, m, n> div(const Matx<_Tp, m, n>& a) const; + + //! element access + const _Tp& operator ()(int row, int col) const; + _Tp& operator ()(int row, int col); + + //! 1D element access + const _Tp& operator ()(int i) const; + _Tp& operator ()(int i); + + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_AddOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_SubOp); + template Matx(const Matx<_Tp, m, n>& a, _T2 alpha, Matx_ScaleOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_MulOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_DivOp); + template Matx(const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b, Matx_MatMulOp); + Matx(const Matx<_Tp, n, m>& a, Matx_TOp); + + _Tp val[m*n]; //< matrix elements +}; + +typedef Matx Matx12f; +typedef Matx Matx12d; +typedef Matx Matx13f; +typedef Matx Matx13d; +typedef Matx Matx14f; +typedef Matx Matx14d; +typedef Matx Matx16f; +typedef Matx Matx16d; + +typedef Matx Matx21f; +typedef Matx Matx21d; +typedef Matx Matx31f; +typedef Matx Matx31d; +typedef Matx Matx41f; +typedef Matx Matx41d; +typedef Matx Matx61f; +typedef Matx Matx61d; + +typedef Matx Matx22f; +typedef Matx Matx22d; +typedef Matx Matx23f; +typedef Matx Matx23d; +typedef Matx Matx32f; +typedef Matx Matx32d; + +typedef Matx Matx33f; +typedef Matx Matx33d; + +typedef Matx Matx34f; +typedef Matx Matx34d; +typedef Matx Matx43f; +typedef Matx Matx43d; + +typedef Matx Matx44f; +typedef Matx Matx44d; +typedef Matx Matx66f; +typedef Matx Matx66d; + +/*! + traits +*/ +template class DataType< Matx<_Tp, m, n> > +{ +public: + typedef Matx<_Tp, m, n> value_type; + typedef Matx::work_type, m, n> work_type; + typedef _Tp channel_type; + typedef value_type vec_type; + + enum { generic_type = 0, + channels = m * n, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; +}; + +namespace traits { +template +struct Depth< Matx<_Tp, m, n> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Matx<_Tp, m, n> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, n*m) }; }; +} // namespace + + +/** @brief Comma-separated Matrix Initializer +*/ +template class MatxCommaInitializer +{ +public: + MatxCommaInitializer(Matx<_Tp, m, n>* _mtx); + template MatxCommaInitializer<_Tp, m, n>& operator , (T2 val); + Matx<_Tp, m, n> operator *() const; + + Matx<_Tp, m, n>* dst; + int idx; +}; + +/* + Utility methods +*/ +template static double determinant(const Matx<_Tp, m, m>& a); +template static double trace(const Matx<_Tp, m, n>& a); +template static double norm(const Matx<_Tp, m, n>& M); +template static double norm(const Matx<_Tp, m, n>& M, int normType); + + + +/////////////////////// Vec (used as element of multi-channel images ///////////////////// + +/** @brief Template class for short numerical vectors, a partial case of Matx + +This template class represents short numerical vectors (of 1, 2, 3, 4 ... elements) on which you +can perform basic arithmetical operations, access individual elements using [] operator etc. The +vectors are allocated on stack, as opposite to std::valarray, std::vector, cv::Mat etc., which +elements are dynamically allocated in the heap. + +The template takes 2 parameters: +@tparam _Tp element type +@tparam cn the number of elements + +In addition to the universal notation like Vec, you can use shorter aliases +for the most popular specialized variants of Vec, e.g. Vec3f ~ Vec. + +It is possible to convert Vec\ to/from Point_, Vec\ to/from Point3_ , and Vec\ +to CvScalar or Scalar_. Use operator[] to access the elements of Vec. + +All the expected vector operations are also implemented: +- v1 = v2 + v3 +- v1 = v2 - v3 +- v1 = v2 \* scale +- v1 = scale \* v2 +- v1 = -v2 +- v1 += v2 and other augmenting operations +- v1 == v2, v1 != v2 +- norm(v1) (euclidean norm) +The Vec class is commonly used to describe pixel types of multi-channel arrays. See Mat for details. +*/ +template class Vec : public Matx<_Tp, cn, 1> +{ +public: + typedef _Tp value_type; + enum { + channels = cn, +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = Matx<_Tp, cn, 1>::depth, + type = CV_MAKETYPE(depth, channels), +#endif + _dummy_enum_finalizer = 0 + }; + + //! default constructor + Vec(); + + Vec(_Tp v0); //!< 1-element vector constructor + Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2); //!< 3-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 4-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 5-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 6-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 7-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 8-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 9-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 10-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13); //!< 14-element vector constructor + explicit Vec(const _Tp* values); + +#ifdef CV_CXX11 + Vec(std::initializer_list<_Tp>); +#endif + + Vec(const Vec<_Tp, cn>& v); + + static Vec all(_Tp alpha); + + //! per-element multiplication + Vec mul(const Vec<_Tp, cn>& v) const; + + //! conjugation (makes sense for complex numbers and quaternions) + Vec conj() const; + + /*! + cross product of the two 3D vectors. + + For other dimensionalities the exception is raised + */ + Vec cross(const Vec& v) const; + //! conversion to another data type + template operator Vec() const; + + /*! element access */ + const _Tp& operator [](int i) const; + _Tp& operator[](int i); + const _Tp& operator ()(int i) const; + _Tp& operator ()(int i); + +#ifdef CV_CXX11 + Vec<_Tp, cn>& operator=(const Vec<_Tp, cn>& rhs) = default; +#endif + + Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp); + Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp); + template Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp); +}; + +/** @name Shorter aliases for the most popular specializations of Vec + @{ +*/ +typedef Vec Vec2b; +typedef Vec Vec3b; +typedef Vec Vec4b; + +typedef Vec Vec2s; +typedef Vec Vec3s; +typedef Vec Vec4s; + +typedef Vec Vec2w; +typedef Vec Vec3w; +typedef Vec Vec4w; + +typedef Vec Vec2i; +typedef Vec Vec3i; +typedef Vec Vec4i; +typedef Vec Vec6i; +typedef Vec Vec8i; + +typedef Vec Vec2f; +typedef Vec Vec3f; +typedef Vec Vec4f; +typedef Vec Vec6f; + +typedef Vec Vec2d; +typedef Vec Vec3d; +typedef Vec Vec4d; +typedef Vec Vec6d; +/** @} */ + +/*! + traits +*/ +template class DataType< Vec<_Tp, cn> > +{ +public: + typedef Vec<_Tp, cn> value_type; + typedef Vec::work_type, cn> work_type; + typedef _Tp channel_type; + typedef value_type vec_type; + + enum { generic_type = 0, + channels = cn, + fmt = DataType::fmt + ((channels - 1) << 8), +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = DataType::depth, + type = CV_MAKETYPE(depth, channels), +#endif + _dummy_enum_finalizer = 0 + }; +}; + +namespace traits { +template +struct Depth< Vec<_Tp, cn> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Vec<_Tp, cn> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, cn) }; }; +} // namespace + + +/** @brief Comma-separated Vec Initializer +*/ +template class VecCommaInitializer : public MatxCommaInitializer<_Tp, m, 1> +{ +public: + VecCommaInitializer(Vec<_Tp, m>* _vec); + template VecCommaInitializer<_Tp, m>& operator , (T2 val); + Vec<_Tp, m> operator *() const; +}; + +template static Vec<_Tp, cn> normalize(const Vec<_Tp, cn>& v); + +//! @} core_basic + +//! @cond IGNORED + +///////////////////////////////////// helper classes ///////////////////////////////////// +namespace internal +{ + +template struct Matx_DetOp +{ + double operator ()(const Matx<_Tp, m, m>& a) const + { + Matx<_Tp, m, m> temp = a; + double p = LU(temp.val, m*sizeof(_Tp), m, 0, 0, 0); + if( p == 0 ) + return p; + for( int i = 0; i < m; i++ ) + p *= temp(i, i); + return p; + } +}; + +template struct Matx_DetOp<_Tp, 1> +{ + double operator ()(const Matx<_Tp, 1, 1>& a) const + { + return a(0,0); + } +}; + +template struct Matx_DetOp<_Tp, 2> +{ + double operator ()(const Matx<_Tp, 2, 2>& a) const + { + return a(0,0)*a(1,1) - a(0,1)*a(1,0); + } +}; + +template struct Matx_DetOp<_Tp, 3> +{ + double operator ()(const Matx<_Tp, 3, 3>& a) const + { + return a(0,0)*(a(1,1)*a(2,2) - a(2,1)*a(1,2)) - + a(0,1)*(a(1,0)*a(2,2) - a(2,0)*a(1,2)) + + a(0,2)*(a(1,0)*a(2,1) - a(2,0)*a(1,1)); + } +}; + +template Vec<_Tp, 2> inline conjugate(const Vec<_Tp, 2>& v) +{ + return Vec<_Tp, 2>(v[0], -v[1]); +} + +template Vec<_Tp, 4> inline conjugate(const Vec<_Tp, 4>& v) +{ + return Vec<_Tp, 4>(v[0], -v[1], -v[2], -v[3]); +} + +} // internal + + + +////////////////////////////////// Matx Implementation /////////////////////////////////// + +template inline +Matx<_Tp, m, n>::Matx() +{ + for(int i = 0; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0) +{ + val[0] = v0; + for(int i = 1; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1) +{ + CV_StaticAssert(channels >= 2, "Matx should have at least 2 elements."); + val[0] = v0; val[1] = v1; + for(int i = 2; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2) +{ + CV_StaticAssert(channels >= 3, "Matx should have at least 3 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; + for(int i = 3; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3) +{ + CV_StaticAssert(channels >= 4, "Matx should have at least 4 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + for(int i = 4; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4) +{ + CV_StaticAssert(channels >= 5, "Matx should have at least 5 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; val[4] = v4; + for(int i = 5; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5) +{ + CV_StaticAssert(channels >= 6, "Matx should have at least 6 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; + for(int i = 6; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6) +{ + CV_StaticAssert(channels >= 7, "Matx should have at least 7 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; + for(int i = 7; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7) +{ + CV_StaticAssert(channels >= 8, "Matx should have at least 8 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + for(int i = 8; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8) +{ + CV_StaticAssert(channels >= 9, "Matx should have at least 9 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; + for(int i = 9; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9) +{ + CV_StaticAssert(channels >= 10, "Matx should have at least 10 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; + for(int i = 10; i < channels; i++) val[i] = _Tp(0); +} + + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11) +{ + CV_StaticAssert(channels >= 12, "Matx should have at least 12 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + for(int i = 12; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13) +{ + CV_StaticAssert(channels >= 14, "Matx should have at least 14 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + val[12] = v12; val[13] = v13; + for (int i = 14; i < channels; i++) val[i] = _Tp(0); +} + + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13, _Tp v14, _Tp v15) +{ + CV_StaticAssert(channels >= 16, "Matx should have at least 16 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + val[12] = v12; val[13] = v13; val[14] = v14; val[15] = v15; + for(int i = 16; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(const _Tp* values) +{ + for( int i = 0; i < channels; i++ ) val[i] = values[i]; +} + +#ifdef CV_CXX11 +template inline +Matx<_Tp, m, n>::Matx(std::initializer_list<_Tp> list) +{ + CV_DbgAssert(list.size() == channels); + int i = 0; + for(const auto& elem : list) + { + val[i++] = elem; + } +} +#endif + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::all(_Tp alpha) +{ + Matx<_Tp, m, n> M; + for( int i = 0; i < m*n; i++ ) M.val[i] = alpha; + return M; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::zeros() +{ + return all(0); +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::ones() +{ + return all(1); +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::eye() +{ + Matx<_Tp,m,n> M; + for(int i = 0; i < shortdim; i++) + M(i,i) = 1; + return M; +} + +template inline +_Tp Matx<_Tp, m, n>::dot(const Matx<_Tp, m, n>& M) const +{ + _Tp s = 0; + for( int i = 0; i < channels; i++ ) s += val[i]*M.val[i]; + return s; +} + +template inline +double Matx<_Tp, m, n>::ddot(const Matx<_Tp, m, n>& M) const +{ + double s = 0; + for( int i = 0; i < channels; i++ ) s += (double)val[i]*M.val[i]; + return s; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::diag(const typename Matx<_Tp,m,n>::diag_type& d) +{ + Matx<_Tp,m,n> M; + for(int i = 0; i < shortdim; i++) + M(i,i) = d(i, 0); + return M; +} + +template template +inline Matx<_Tp, m, n>::operator Matx() const +{ + Matx M; + for( int i = 0; i < m*n; i++ ) M.val[i] = saturate_cast(val[i]); + return M; +} + +template template inline +Matx<_Tp, m1, n1> Matx<_Tp, m, n>::reshape() const +{ + CV_StaticAssert(m1*n1 == m*n, "Input and destnarion matrices must have the same number of elements"); + return (const Matx<_Tp, m1, n1>&)*this; +} + +template +template inline +Matx<_Tp, m1, n1> Matx<_Tp, m, n>::get_minor(int base_row, int base_col) const +{ + CV_DbgAssert(0 <= base_row && base_row+m1 <= m && 0 <= base_col && base_col+n1 <= n); + Matx<_Tp, m1, n1> s; + for( int di = 0; di < m1; di++ ) + for( int dj = 0; dj < n1; dj++ ) + s(di, dj) = (*this)(base_row+di, base_col+dj); + return s; +} + +template inline +Matx<_Tp, 1, n> Matx<_Tp, m, n>::row(int i) const +{ + CV_DbgAssert((unsigned)i < (unsigned)m); + return Matx<_Tp, 1, n>(&val[i*n]); +} + +template inline +Matx<_Tp, m, 1> Matx<_Tp, m, n>::col(int j) const +{ + CV_DbgAssert((unsigned)j < (unsigned)n); + Matx<_Tp, m, 1> v; + for( int i = 0; i < m; i++ ) + v.val[i] = val[i*n + j]; + return v; +} + +template inline +typename Matx<_Tp, m, n>::diag_type Matx<_Tp, m, n>::diag() const +{ + diag_type d; + for( int i = 0; i < shortdim; i++ ) + d.val[i] = val[i*n + i]; + return d; +} + +template inline +const _Tp& Matx<_Tp, m, n>::operator()(int row_idx, int col_idx) const +{ + CV_DbgAssert( (unsigned)row_idx < (unsigned)m && (unsigned)col_idx < (unsigned)n ); + return this->val[row_idx*n + col_idx]; +} + +template inline +_Tp& Matx<_Tp, m, n>::operator ()(int row_idx, int col_idx) +{ + CV_DbgAssert( (unsigned)row_idx < (unsigned)m && (unsigned)col_idx < (unsigned)n ); + return val[row_idx*n + col_idx]; +} + +template inline +const _Tp& Matx<_Tp, m, n>::operator ()(int i) const +{ + CV_StaticAssert(m == 1 || n == 1, "Single index indexation requires matrix to be a column or a row"); + CV_DbgAssert( (unsigned)i < (unsigned)(m+n-1) ); + return val[i]; +} + +template inline +_Tp& Matx<_Tp, m, n>::operator ()(int i) +{ + CV_StaticAssert(m == 1 || n == 1, "Single index indexation requires matrix to be a column or a row"); + CV_DbgAssert( (unsigned)i < (unsigned)(m+n-1) ); + return val[i]; +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_AddOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] + b.val[i]); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_SubOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] - b.val[i]); +} + +template template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, _T2 alpha, Matx_ScaleOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] * alpha); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_MulOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] * b.val[i]); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_DivOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] / b.val[i]); +} + +template template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b, Matx_MatMulOp) +{ + for( int i = 0; i < m; i++ ) + for( int j = 0; j < n; j++ ) + { + _Tp s = 0; + for( int k = 0; k < l; k++ ) + s += a(i, k) * b(k, j); + val[i*n + j] = s; + } +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, n, m>& a, Matx_TOp) +{ + for( int i = 0; i < m; i++ ) + for( int j = 0; j < n; j++ ) + val[i*n + j] = a(j, i); +} + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::mul(const Matx<_Tp, m, n>& a) const +{ + return Matx<_Tp, m, n>(*this, a, Matx_MulOp()); +} + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::div(const Matx<_Tp, m, n>& a) const +{ + return Matx<_Tp, m, n>(*this, a, Matx_DivOp()); +} + +template inline +Matx<_Tp, n, m> Matx<_Tp, m, n>::t() const +{ + return Matx<_Tp, n, m>(*this, Matx_TOp()); +} + +template inline +Vec<_Tp, n> Matx<_Tp, m, n>::solve(const Vec<_Tp, m>& rhs, int method) const +{ + Matx<_Tp, n, 1> x = solve((const Matx<_Tp, m, 1>&)(rhs), method); + return (Vec<_Tp, n>&)(x); +} + +template static inline +double determinant(const Matx<_Tp, m, m>& a) +{ + return cv::internal::Matx_DetOp<_Tp, m>()(a); +} + +template static inline +double trace(const Matx<_Tp, m, n>& a) +{ + _Tp s = 0; + for( int i = 0; i < std::min(m, n); i++ ) + s += a(i,i); + return s; +} + +template static inline +double norm(const Matx<_Tp, m, n>& M) +{ + return std::sqrt(normL2Sqr<_Tp, double>(M.val, m*n)); +} + +template static inline +double norm(const Matx<_Tp, m, n>& M, int normType) +{ + switch(normType) { + case NORM_INF: + return (double)normInf<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + case NORM_L1: + return (double)normL1<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + case NORM_L2SQR: + return (double)normL2Sqr<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + default: + case NORM_L2: + return std::sqrt((double)normL2Sqr<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n)); + } +} + + + +//////////////////////////////// matx comma initializer ////////////////////////////////// + +template static inline +MatxCommaInitializer<_Tp, m, n> operator << (const Matx<_Tp, m, n>& mtx, _T2 val) +{ + MatxCommaInitializer<_Tp, m, n> commaInitializer((Matx<_Tp, m, n>*)&mtx); + return (commaInitializer, val); +} + +template inline +MatxCommaInitializer<_Tp, m, n>::MatxCommaInitializer(Matx<_Tp, m, n>* _mtx) + : dst(_mtx), idx(0) +{} + +template template inline +MatxCommaInitializer<_Tp, m, n>& MatxCommaInitializer<_Tp, m, n>::operator , (_T2 value) +{ + CV_DbgAssert( idx < m*n ); + dst->val[idx++] = saturate_cast<_Tp>(value); + return *this; +} + +template inline +Matx<_Tp, m, n> MatxCommaInitializer<_Tp, m, n>::operator *() const +{ + CV_DbgAssert( idx == n*m ); + return *dst; +} + + + +/////////////////////////////////// Vec Implementation /////////////////////////////////// + +template inline +Vec<_Tp, cn>::Vec() {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0) + : Matx<_Tp, cn, 1>(v0) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1) + : Matx<_Tp, cn, 1>(v0, v1) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2) + : Matx<_Tp, cn, 1>(v0, v1, v2) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {} + +template inline +Vec<_Tp, cn>::Vec(const _Tp* values) + : Matx<_Tp, cn, 1>(values) {} + +#ifdef CV_CXX11 +template inline +Vec<_Tp, cn>::Vec(std::initializer_list<_Tp> list) + : Matx<_Tp, cn, 1>(list) {} +#endif + +template inline +Vec<_Tp, cn>::Vec(const Vec<_Tp, cn>& m) + : Matx<_Tp, cn, 1>(m.val) {} + +template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp op) + : Matx<_Tp, cn, 1>(a, b, op) {} + +template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp op) + : Matx<_Tp, cn, 1>(a, b, op) {} + +template template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp op) + : Matx<_Tp, cn, 1>(a, alpha, op) {} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::all(_Tp alpha) +{ + Vec v; + for( int i = 0; i < cn; i++ ) v.val[i] = alpha; + return v; +} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::mul(const Vec<_Tp, cn>& v) const +{ + Vec<_Tp, cn> w; + for( int i = 0; i < cn; i++ ) w.val[i] = saturate_cast<_Tp>(this->val[i]*v.val[i]); + return w; +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::cross(const Vec<_Tp, cn>&) const +{ + CV_StaticAssert(cn == 3, "for arbitrary-size vector there is no cross-product defined"); + return Vec<_Tp, cn>(); +} + +template<> inline +Vec Vec::cross(const Vec& v) const +{ + return Vec(this->val[1]*v.val[2] - this->val[2]*v.val[1], + this->val[2]*v.val[0] - this->val[0]*v.val[2], + this->val[0]*v.val[1] - this->val[1]*v.val[0]); +} + +template<> inline +Vec Vec::cross(const Vec& v) const +{ + return Vec(this->val[1]*v.val[2] - this->val[2]*v.val[1], + this->val[2]*v.val[0] - this->val[0]*v.val[2], + this->val[0]*v.val[1] - this->val[1]*v.val[0]); +} + +template template inline +Vec<_Tp, cn>::operator Vec() const +{ + Vec v; + for( int i = 0; i < cn; i++ ) v.val[i] = saturate_cast(this->val[i]); + return v; +} + +template inline +const _Tp& Vec<_Tp, cn>::operator [](int i) const +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +_Tp& Vec<_Tp, cn>::operator [](int i) +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +const _Tp& Vec<_Tp, cn>::operator ()(int i) const +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +_Tp& Vec<_Tp, cn>::operator ()(int i) +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +Vec<_Tp, cn> normalize(const Vec<_Tp, cn>& v) +{ + double nv = norm(v); + return v * (nv ? 1./nv : 0.); +} + + + +//////////////////////////////// vec comma initializer ////////////////////////////////// + + +template static inline +VecCommaInitializer<_Tp, cn> operator << (const Vec<_Tp, cn>& vec, _T2 val) +{ + VecCommaInitializer<_Tp, cn> commaInitializer((Vec<_Tp, cn>*)&vec); + return (commaInitializer, val); +} + +template inline +VecCommaInitializer<_Tp, cn>::VecCommaInitializer(Vec<_Tp, cn>* _vec) + : MatxCommaInitializer<_Tp, cn, 1>(_vec) +{} + +template template inline +VecCommaInitializer<_Tp, cn>& VecCommaInitializer<_Tp, cn>::operator , (_T2 value) +{ + CV_DbgAssert( this->idx < cn ); + this->dst->val[this->idx++] = saturate_cast<_Tp>(value); + return *this; +} + +template inline +Vec<_Tp, cn> VecCommaInitializer<_Tp, cn>::operator *() const +{ + CV_DbgAssert( this->idx == cn ); + return *this->dst; +} + +//! @endcond + +///////////////////////////// Matx out-of-class operators //////////////////////////////// + +//! @relates cv::Matx +//! @{ + +template static inline +Matx<_Tp1, m, n>& operator += (Matx<_Tp1, m, n>& a, const Matx<_Tp2, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] + b.val[i]); + return a; +} + +template static inline +Matx<_Tp1, m, n>& operator -= (Matx<_Tp1, m, n>& a, const Matx<_Tp2, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] - b.val[i]); + return a; +} + +template static inline +Matx<_Tp, m, n> operator + (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_AddOp()); +} + +template static inline +Matx<_Tp, m, n> operator - (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_SubOp()); +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, int alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, float alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, double alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, int alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, float alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, double alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (int alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (float alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (double alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n>& operator /= (Matx<_Tp, m, n>& a, float alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = a.val[i] / alpha; + return a; +} + +template static inline +Matx<_Tp, m, n>& operator /= (Matx<_Tp, m, n>& a, double alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = a.val[i] / alpha; + return a; +} + +template static inline +Matx<_Tp, m, n> operator / (const Matx<_Tp, m, n>& a, float alpha) +{ + return Matx<_Tp, m, n>(a, 1.f/alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator / (const Matx<_Tp, m, n>& a, double alpha) +{ + return Matx<_Tp, m, n>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator - (const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, -1, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_MatMulOp()); +} + +template static inline +Vec<_Tp, m> operator * (const Matx<_Tp, m, n>& a, const Vec<_Tp, n>& b) +{ + Matx<_Tp, m, 1> c(a, b, Matx_MatMulOp()); + return (const Vec<_Tp, m>&)(c); +} + +template static inline +bool operator == (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + if( a.val[i] != b.val[i] ) return false; + return true; +} + +template static inline +bool operator != (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return !(a == b); +} + +//! @} + +////////////////////////////// Vec out-of-class operators //////////////////////////////// + +//! @relates cv::Vec +//! @{ + +template static inline +Vec<_Tp1, cn>& operator += (Vec<_Tp1, cn>& a, const Vec<_Tp2, cn>& b) +{ + for( int i = 0; i < cn; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] + b.val[i]); + return a; +} + +template static inline +Vec<_Tp1, cn>& operator -= (Vec<_Tp1, cn>& a, const Vec<_Tp2, cn>& b) +{ + for( int i = 0; i < cn; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] - b.val[i]); + return a; +} + +template static inline +Vec<_Tp, cn> operator + (const Vec<_Tp, cn>& a, const Vec<_Tp, cn>& b) +{ + return Vec<_Tp, cn>(a, b, Matx_AddOp()); +} + +template static inline +Vec<_Tp, cn> operator - (const Vec<_Tp, cn>& a, const Vec<_Tp, cn>& b) +{ + return Vec<_Tp, cn>(a, b, Matx_SubOp()); +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, int alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, float alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, double alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, int alpha) +{ + double ialpha = 1./alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, float alpha) +{ + float ialpha = 1.f/alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, double alpha) +{ + double ialpha = 1./alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, int alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (int alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, float alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (float alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, double alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (double alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, int alpha) +{ + return Vec<_Tp, cn>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, float alpha) +{ + return Vec<_Tp, cn>(a, 1.f/alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, double alpha) +{ + return Vec<_Tp, cn>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator - (const Vec<_Tp, cn>& a) +{ + Vec<_Tp,cn> t; + for( int i = 0; i < cn; i++ ) t.val[i] = saturate_cast<_Tp>(-a.val[i]); + return t; +} + +template inline Vec<_Tp, 4> operator * (const Vec<_Tp, 4>& v1, const Vec<_Tp, 4>& v2) +{ + return Vec<_Tp, 4>(saturate_cast<_Tp>(v1[0]*v2[0] - v1[1]*v2[1] - v1[2]*v2[2] - v1[3]*v2[3]), + saturate_cast<_Tp>(v1[0]*v2[1] + v1[1]*v2[0] + v1[2]*v2[3] - v1[3]*v2[2]), + saturate_cast<_Tp>(v1[0]*v2[2] - v1[1]*v2[3] + v1[2]*v2[0] + v1[3]*v2[1]), + saturate_cast<_Tp>(v1[0]*v2[3] + v1[1]*v2[2] - v1[2]*v2[1] + v1[3]*v2[0])); +} + +template inline Vec<_Tp, 4>& operator *= (Vec<_Tp, 4>& v1, const Vec<_Tp, 4>& v2) +{ + v1 = v1 * v2; + return v1; +} + +//! @} + +} // cv + +#endif // OPENCV_CORE_MATX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/neon_utils.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/neon_utils.hpp new file mode 100644 index 0000000..573ba99 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/neon_utils.hpp @@ -0,0 +1,128 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_NEON_UTILS_HPP +#define OPENCV_HAL_NEON_UTILS_HPP + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils_neon +//! @{ + +#if CV_NEON + +inline int32x2_t cv_vrnd_s32_f32(float32x2_t v) +{ + static int32x2_t v_sign = vdup_n_s32(1 << 31), + v_05 = vreinterpret_s32_f32(vdup_n_f32(0.5f)); + + int32x2_t v_addition = vorr_s32(v_05, vand_s32(v_sign, vreinterpret_s32_f32(v))); + return vcvt_s32_f32(vadd_f32(v, vreinterpret_f32_s32(v_addition))); +} + +inline int32x4_t cv_vrndq_s32_f32(float32x4_t v) +{ + static int32x4_t v_sign = vdupq_n_s32(1 << 31), + v_05 = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); + + int32x4_t v_addition = vorrq_s32(v_05, vandq_s32(v_sign, vreinterpretq_s32_f32(v))); + return vcvtq_s32_f32(vaddq_f32(v, vreinterpretq_f32_s32(v_addition))); +} + +inline uint32x2_t cv_vrnd_u32_f32(float32x2_t v) +{ + static float32x2_t v_05 = vdup_n_f32(0.5f); + return vcvt_u32_f32(vadd_f32(v, v_05)); +} + +inline uint32x4_t cv_vrndq_u32_f32(float32x4_t v) +{ + static float32x4_t v_05 = vdupq_n_f32(0.5f); + return vcvtq_u32_f32(vaddq_f32(v, v_05)); +} + +inline float32x4_t cv_vrecpq_f32(float32x4_t val) +{ + float32x4_t reciprocal = vrecpeq_f32(val); + reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + return reciprocal; +} + +inline float32x2_t cv_vrecp_f32(float32x2_t val) +{ + float32x2_t reciprocal = vrecpe_f32(val); + reciprocal = vmul_f32(vrecps_f32(val, reciprocal), reciprocal); + reciprocal = vmul_f32(vrecps_f32(val, reciprocal), reciprocal); + return reciprocal; +} + +inline float32x4_t cv_vrsqrtq_f32(float32x4_t val) +{ + float32x4_t e = vrsqrteq_f32(val); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(e, e), val), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(e, e), val), e); + return e; +} + +inline float32x2_t cv_vrsqrt_f32(float32x2_t val) +{ + float32x2_t e = vrsqrte_f32(val); + e = vmul_f32(vrsqrts_f32(vmul_f32(e, e), val), e); + e = vmul_f32(vrsqrts_f32(vmul_f32(e, e), val), e); + return e; +} + +inline float32x4_t cv_vsqrtq_f32(float32x4_t val) +{ + return cv_vrecpq_f32(cv_vrsqrtq_f32(val)); +} + +inline float32x2_t cv_vsqrt_f32(float32x2_t val) +{ + return cv_vrecp_f32(cv_vrsqrt_f32(val)); +} + +#endif + +//! @} + +#endif // OPENCV_HAL_NEON_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/ocl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/ocl.hpp new file mode 100644 index 0000000..95f0fcd --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/ocl.hpp @@ -0,0 +1,843 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPENCL_HPP +#define OPENCV_OPENCL_HPP + +#include "opencv2/core.hpp" + +namespace cv { namespace ocl { + +//! @addtogroup core_opencl +//! @{ + +CV_EXPORTS_W bool haveOpenCL(); +CV_EXPORTS_W bool useOpenCL(); +CV_EXPORTS_W bool haveAmdBlas(); +CV_EXPORTS_W bool haveAmdFft(); +CV_EXPORTS_W void setUseOpenCL(bool flag); +CV_EXPORTS_W void finish(); + +CV_EXPORTS bool haveSVM(); + +class CV_EXPORTS Context; +class CV_EXPORTS_W_SIMPLE Device; +class CV_EXPORTS Kernel; +class CV_EXPORTS Program; +class CV_EXPORTS ProgramSource; +class CV_EXPORTS Queue; +class CV_EXPORTS PlatformInfo; +class CV_EXPORTS Image2D; + +class CV_EXPORTS_W_SIMPLE Device +{ +public: + CV_WRAP Device(); + explicit Device(void* d); + Device(const Device& d); + Device& operator = (const Device& d); + CV_WRAP ~Device(); + + void set(void* d); + + enum + { + TYPE_DEFAULT = (1 << 0), + TYPE_CPU = (1 << 1), + TYPE_GPU = (1 << 2), + TYPE_ACCELERATOR = (1 << 3), + TYPE_DGPU = TYPE_GPU + (1 << 16), + TYPE_IGPU = TYPE_GPU + (1 << 17), + TYPE_ALL = 0xFFFFFFFF + }; + + CV_WRAP String name() const; + CV_WRAP String extensions() const; + CV_WRAP bool isExtensionSupported(const String& extensionName) const; + CV_WRAP String version() const; + CV_WRAP String vendorName() const; + CV_WRAP String OpenCL_C_Version() const; + CV_WRAP String OpenCLVersion() const; + CV_WRAP int deviceVersionMajor() const; + CV_WRAP int deviceVersionMinor() const; + CV_WRAP String driverVersion() const; + void* ptr() const; + + CV_WRAP int type() const; + + CV_WRAP int addressBits() const; + CV_WRAP bool available() const; + CV_WRAP bool compilerAvailable() const; + CV_WRAP bool linkerAvailable() const; + + enum + { + FP_DENORM=(1 << 0), + FP_INF_NAN=(1 << 1), + FP_ROUND_TO_NEAREST=(1 << 2), + FP_ROUND_TO_ZERO=(1 << 3), + FP_ROUND_TO_INF=(1 << 4), + FP_FMA=(1 << 5), + FP_SOFT_FLOAT=(1 << 6), + FP_CORRECTLY_ROUNDED_DIVIDE_SQRT=(1 << 7) + }; + CV_WRAP int doubleFPConfig() const; + CV_WRAP int singleFPConfig() const; + CV_WRAP int halfFPConfig() const; + + CV_WRAP bool endianLittle() const; + CV_WRAP bool errorCorrectionSupport() const; + + enum + { + EXEC_KERNEL=(1 << 0), + EXEC_NATIVE_KERNEL=(1 << 1) + }; + CV_WRAP int executionCapabilities() const; + + CV_WRAP size_t globalMemCacheSize() const; + + enum + { + NO_CACHE=0, + READ_ONLY_CACHE=1, + READ_WRITE_CACHE=2 + }; + CV_WRAP int globalMemCacheType() const; + CV_WRAP int globalMemCacheLineSize() const; + CV_WRAP size_t globalMemSize() const; + + CV_WRAP size_t localMemSize() const; + enum + { + NO_LOCAL_MEM=0, + LOCAL_IS_LOCAL=1, + LOCAL_IS_GLOBAL=2 + }; + CV_WRAP int localMemType() const; + CV_WRAP bool hostUnifiedMemory() const; + + CV_WRAP bool imageSupport() const; + + CV_WRAP bool imageFromBufferSupport() const; + uint imagePitchAlignment() const; + uint imageBaseAddressAlignment() const; + + /// deprecated, use isExtensionSupported() method (probably with "cl_khr_subgroups" value) + CV_WRAP bool intelSubgroupsSupport() const; + + CV_WRAP size_t image2DMaxWidth() const; + CV_WRAP size_t image2DMaxHeight() const; + + CV_WRAP size_t image3DMaxWidth() const; + CV_WRAP size_t image3DMaxHeight() const; + CV_WRAP size_t image3DMaxDepth() const; + + CV_WRAP size_t imageMaxBufferSize() const; + CV_WRAP size_t imageMaxArraySize() const; + + enum + { + UNKNOWN_VENDOR=0, + VENDOR_AMD=1, + VENDOR_INTEL=2, + VENDOR_NVIDIA=3 + }; + CV_WRAP int vendorID() const; + // FIXIT + // dev.isAMD() doesn't work for OpenCL CPU devices from AMD OpenCL platform. + // This method should use platform name instead of vendor name. + // After fix restore code in arithm.cpp: ocl_compare() + CV_WRAP inline bool isAMD() const { return vendorID() == VENDOR_AMD; } + CV_WRAP inline bool isIntel() const { return vendorID() == VENDOR_INTEL; } + CV_WRAP inline bool isNVidia() const { return vendorID() == VENDOR_NVIDIA; } + + CV_WRAP int maxClockFrequency() const; + CV_WRAP int maxComputeUnits() const; + CV_WRAP int maxConstantArgs() const; + CV_WRAP size_t maxConstantBufferSize() const; + + CV_WRAP size_t maxMemAllocSize() const; + CV_WRAP size_t maxParameterSize() const; + + CV_WRAP int maxReadImageArgs() const; + CV_WRAP int maxWriteImageArgs() const; + CV_WRAP int maxSamplers() const; + + CV_WRAP size_t maxWorkGroupSize() const; + CV_WRAP int maxWorkItemDims() const; + void maxWorkItemSizes(size_t*) const; + + CV_WRAP int memBaseAddrAlign() const; + + CV_WRAP int nativeVectorWidthChar() const; + CV_WRAP int nativeVectorWidthShort() const; + CV_WRAP int nativeVectorWidthInt() const; + CV_WRAP int nativeVectorWidthLong() const; + CV_WRAP int nativeVectorWidthFloat() const; + CV_WRAP int nativeVectorWidthDouble() const; + CV_WRAP int nativeVectorWidthHalf() const; + + CV_WRAP int preferredVectorWidthChar() const; + CV_WRAP int preferredVectorWidthShort() const; + CV_WRAP int preferredVectorWidthInt() const; + CV_WRAP int preferredVectorWidthLong() const; + CV_WRAP int preferredVectorWidthFloat() const; + CV_WRAP int preferredVectorWidthDouble() const; + CV_WRAP int preferredVectorWidthHalf() const; + + CV_WRAP size_t printfBufferSize() const; + CV_WRAP size_t profilingTimerResolution() const; + + CV_WRAP static const Device& getDefault(); + +protected: + struct Impl; + Impl* p; +}; + + +class CV_EXPORTS Context +{ +public: + Context(); + explicit Context(int dtype); + ~Context(); + Context(const Context& c); + Context& operator = (const Context& c); + + bool create(); + bool create(int dtype); + size_t ndevices() const; + const Device& device(size_t idx) const; + Program getProg(const ProgramSource& prog, + const String& buildopt, String& errmsg); + void unloadProg(Program& prog); + + static Context& getDefault(bool initialize = true); + void* ptr() const; + + friend void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); + + bool useSVM() const; + void setUseSVM(bool enabled); + + struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +//protected: + Impl* p; +}; + +class CV_EXPORTS Platform +{ +public: + Platform(); + ~Platform(); + Platform(const Platform& p); + Platform& operator = (const Platform& p); + + void* ptr() const; + static Platform& getDefault(); + + friend void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); +protected: + struct Impl; + Impl* p; +}; + +/** @brief Attaches OpenCL context to OpenCV +@note + OpenCV will check if available OpenCL platform has platformName name, then assign context to + OpenCV and call `clRetainContext` function. The deviceID device will be used as target device and + new command queue will be created. +@param platformName name of OpenCL platform to attach, this string is used to check if platform is available to OpenCV at runtime +@param platformID ID of platform attached context was created for +@param context OpenCL context to be attached to OpenCV +@param deviceID ID of device, must be created from attached context +*/ +CV_EXPORTS void attachContext(const String& platformName, void* platformID, void* context, void* deviceID); + +/** @brief Convert OpenCL buffer to UMat +@note + OpenCL buffer (cl_mem_buffer) should contain 2D image data, compatible with OpenCV. Memory + content is not copied from `clBuffer` to UMat. Instead, buffer handle assigned to UMat and + `clRetainMemObject` is called. +@param cl_mem_buffer source clBuffer handle +@param step num of bytes in single row +@param rows number of rows +@param cols number of cols +@param type OpenCV type of image +@param dst destination UMat +*/ +CV_EXPORTS void convertFromBuffer(void* cl_mem_buffer, size_t step, int rows, int cols, int type, UMat& dst); + +/** @brief Convert OpenCL image2d_t to UMat +@note + OpenCL `image2d_t` (cl_mem_image), should be compatible with OpenCV UMat formats. Memory content + is copied from image to UMat with `clEnqueueCopyImageToBuffer` function. +@param cl_mem_image source image2d_t handle +@param dst destination UMat +*/ +CV_EXPORTS void convertFromImage(void* cl_mem_image, UMat& dst); + +// TODO Move to internal header +void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); + +class CV_EXPORTS Queue +{ +public: + Queue(); + explicit Queue(const Context& c, const Device& d=Device()); + ~Queue(); + Queue(const Queue& q); + Queue& operator = (const Queue& q); + + bool create(const Context& c=Context(), const Device& d=Device()); + void finish(); + void* ptr() const; + static Queue& getDefault(); + + /// @brief Returns OpenCL command queue with enable profiling mode support + const Queue& getProfilingQueue() const; + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return p; } +protected: + Impl* p; +}; + + +class CV_EXPORTS KernelArg +{ +public: + enum { LOCAL=1, READ_ONLY=2, WRITE_ONLY=4, READ_WRITE=6, CONSTANT=8, PTR_ONLY = 16, NO_SIZE=256 }; + KernelArg(int _flags, UMat* _m, int wscale=1, int iwscale=1, const void* _obj=0, size_t _sz=0); + KernelArg(); + + static KernelArg Local(size_t localMemSize) + { return KernelArg(LOCAL, 0, 1, 1, 0, localMemSize); } + static KernelArg PtrWriteOnly(const UMat& m) + { return KernelArg(PTR_ONLY+WRITE_ONLY, (UMat*)&m); } + static KernelArg PtrReadOnly(const UMat& m) + { return KernelArg(PTR_ONLY+READ_ONLY, (UMat*)&m); } + static KernelArg PtrReadWrite(const UMat& m) + { return KernelArg(PTR_ONLY+READ_WRITE, (UMat*)&m); } + static KernelArg ReadWrite(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_WRITE, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadWriteNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_WRITE+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadOnly(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_ONLY, (UMat*)&m, wscale, iwscale); } + static KernelArg WriteOnly(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(WRITE_ONLY, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadOnlyNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_ONLY+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg WriteOnlyNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(WRITE_ONLY+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg Constant(const Mat& m); + template static KernelArg Constant(const _Tp* arr, size_t n) + { return KernelArg(CONSTANT, 0, 1, 1, (void*)arr, n); } + + int flags; + UMat* m; + const void* obj; + size_t sz; + int wscale, iwscale; +}; + + +class CV_EXPORTS Kernel +{ +public: + Kernel(); + Kernel(const char* kname, const Program& prog); + Kernel(const char* kname, const ProgramSource& prog, + const String& buildopts = String(), String* errmsg=0); + ~Kernel(); + Kernel(const Kernel& k); + Kernel& operator = (const Kernel& k); + + bool empty() const; + bool create(const char* kname, const Program& prog); + bool create(const char* kname, const ProgramSource& prog, + const String& buildopts, String* errmsg=0); + + int set(int i, const void* value, size_t sz); + int set(int i, const Image2D& image2D); + int set(int i, const UMat& m); + int set(int i, const KernelArg& arg); + template int set(int i, const _Tp& value) + { return set(i, &value, sizeof(value)); } + + template + Kernel& args(const _Tp0& a0) + { + set(0, a0); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1) + { + int i = set(0, a0); set(i, a1); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2) + { + int i = set(0, a0); i = set(i, a1); set(i, a2); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, + const _Tp3& a3, const _Tp4& a4) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); + i = set(i, a3); set(i, a4); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, + const _Tp3& a3, const _Tp4& a4, const _Tp5& a5) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); + i = set(i, a3); i = set(i, a4); set(i, a5); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); + i = set(i, a4); i = set(i, a5); set(i, a6); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); + i = set(i, a4); i = set(i, a5); i = set(i, a6); set(i, a7); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); + i = set(i, a5); i = set(i, a6); i = set(i, a7); set(i, a8); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); set(i, a9); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); set(i, a10); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); set(i, a11); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + set(i, a12); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); set(i, a13); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13, const _Tp14& a14) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); i = set(i, a13); set(i, a14); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13, const _Tp14& a14, const _Tp15& a15) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); i = set(i, a13); i = set(i, a14); set(i, a15); return *this; + } + /** @brief Run the OpenCL kernel. + @param dims the work problem dimensions. It is the length of globalsize and localsize. It can be either 1, 2 or 3. + @param globalsize work items for each dimension. It is not the final globalsize passed to + OpenCL. Each dimension will be adjusted to the nearest integer divisible by the corresponding + value in localsize. If localsize is NULL, it will still be adjusted depending on dims. The + adjusted values are greater than or equal to the original values. + @param localsize work-group size for each dimension. + @param sync specify whether to wait for OpenCL computation to finish before return. + @param q command queue + */ + bool run(int dims, size_t globalsize[], + size_t localsize[], bool sync, const Queue& q=Queue()); + bool runTask(bool sync, const Queue& q=Queue()); + + /** @brief Similar to synchronized run() call with returning of kernel execution time + * Separate OpenCL command queue may be used (with CL_QUEUE_PROFILING_ENABLE) + * @return Execution time in nanoseconds or negative number on error + */ + int64 runProfiling(int dims, size_t globalsize[], size_t localsize[], const Queue& q=Queue()); + + size_t workGroupSize() const; + size_t preferedWorkGroupSizeMultiple() const; + bool compileWorkGroupSize(size_t wsz[]) const; + size_t localMemSize() const; + + void* ptr() const; + struct Impl; + +protected: + Impl* p; +}; + +class CV_EXPORTS Program +{ +public: + Program(); + Program(const ProgramSource& src, + const String& buildflags, String& errmsg); + Program(const Program& prog); + + Program& operator = (const Program& prog); + ~Program(); + + bool create(const ProgramSource& src, + const String& buildflags, String& errmsg); + + void* ptr() const; + + /** + * @brief Query device-specific program binary. + * + * Returns RAW OpenCL executable binary without additional attachments. + * + * @sa ProgramSource::fromBinary + * + * @param[out] binary output buffer + */ + void getBinary(std::vector& binary) const; + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +protected: + Impl* p; +public: +#ifndef OPENCV_REMOVE_DEPRECATED_API + // TODO Remove this + CV_DEPRECATED bool read(const String& buf, const String& buildflags); // removed, use ProgramSource instead + CV_DEPRECATED bool write(String& buf) const; // removed, use getBinary() method instead (RAW OpenCL binary) + CV_DEPRECATED const ProgramSource& source() const; // implementation removed + CV_DEPRECATED String getPrefix() const; // deprecated, implementation replaced + CV_DEPRECATED static String getPrefix(const String& buildflags); // deprecated, implementation replaced +#endif +}; + + +class CV_EXPORTS ProgramSource +{ +public: + typedef uint64 hash_t; // deprecated + + ProgramSource(); + explicit ProgramSource(const String& module, const String& name, const String& codeStr, const String& codeHash); + explicit ProgramSource(const String& prog); // deprecated + explicit ProgramSource(const char* prog); // deprecated + ~ProgramSource(); + ProgramSource(const ProgramSource& prog); + ProgramSource& operator = (const ProgramSource& prog); + + const String& source() const; // deprecated + hash_t hash() const; // deprecated + + + /** @brief Describe OpenCL program binary. + * Do not call clCreateProgramWithBinary() and/or clBuildProgram(). + * + * Caller should guarantee binary buffer lifetime greater than ProgramSource object (and any of its copies). + * + * This kind of binary is not portable between platforms in general - it is specific to OpenCL vendor / device / driver version. + * + * @param module name of program owner module + * @param name unique name of program (module+name is used as key for OpenCL program caching) + * @param binary buffer address. See buffer lifetime requirement in description. + * @param size buffer size + * @param buildOptions additional program-related build options passed to clBuildProgram() + * @return created ProgramSource object + */ + static ProgramSource fromBinary(const String& module, const String& name, + const unsigned char* binary, const size_t size, + const cv::String& buildOptions = cv::String()); + + /** @brief Describe OpenCL program in SPIR format. + * Do not call clCreateProgramWithBinary() and/or clBuildProgram(). + * + * Supports SPIR 1.2 by default (pass '-spir-std=X.Y' in buildOptions to override this behavior) + * + * Caller should guarantee binary buffer lifetime greater than ProgramSource object (and any of its copies). + * + * Programs in this format are portable between OpenCL implementations with 'khr_spir' extension: + * https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/cl_khr_spir.html + * (but they are not portable between different platforms: 32-bit / 64-bit) + * + * Note: these programs can't support vendor specific extensions, like 'cl_intel_subgroups'. + * + * @param module name of program owner module + * @param name unique name of program (module+name is used as key for OpenCL program caching) + * @param binary buffer address. See buffer lifetime requirement in description. + * @param size buffer size + * @param buildOptions additional program-related build options passed to clBuildProgram() + * (these options are added automatically: '-x spir' and '-spir-std=1.2') + * @return created ProgramSource object. + */ + static ProgramSource fromSPIR(const String& module, const String& name, + const unsigned char* binary, const size_t size, + const cv::String& buildOptions = cv::String()); + + //OpenCL 2.1+ only + //static Program fromSPIRV(const String& module, const String& name, + // const unsigned char* binary, const size_t size, + // const cv::String& buildOptions = cv::String()); + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +protected: + Impl* p; +}; + +class CV_EXPORTS PlatformInfo +{ +public: + PlatformInfo(); + explicit PlatformInfo(void* id); + ~PlatformInfo(); + + PlatformInfo(const PlatformInfo& i); + PlatformInfo& operator =(const PlatformInfo& i); + + String name() const; + String vendor() const; + String version() const; + int deviceNumber() const; + void getDevice(Device& device, int d) const; + +protected: + struct Impl; + Impl* p; +}; + +CV_EXPORTS const char* convertTypeStr(int sdepth, int ddepth, int cn, char* buf); +CV_EXPORTS const char* typeToStr(int t); +CV_EXPORTS const char* memopTypeToStr(int t); +CV_EXPORTS const char* vecopTypeToStr(int t); +CV_EXPORTS const char* getOpenCLErrorString(int errorCode); +CV_EXPORTS String kernelToStr(InputArray _kernel, int ddepth = -1, const char * name = NULL); +CV_EXPORTS void getPlatfomsInfo(std::vector& platform_info); + + +enum OclVectorStrategy +{ + // all matrices have its own vector width + OCL_VECTOR_OWN = 0, + // all matrices have maximal vector width among all matrices + // (useful for cases when matrices have different data types) + OCL_VECTOR_MAX = 1, + + // default strategy + OCL_VECTOR_DEFAULT = OCL_VECTOR_OWN +}; + +CV_EXPORTS int predictOptimalVectorWidth(InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray(), + OclVectorStrategy strat = OCL_VECTOR_DEFAULT); + +CV_EXPORTS int checkOptimalVectorWidth(const int *vectorWidths, + InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray(), + OclVectorStrategy strat = OCL_VECTOR_DEFAULT); + +// with OCL_VECTOR_MAX strategy +CV_EXPORTS int predictOptimalVectorWidthMax(InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray()); + +CV_EXPORTS void buildOptionsAddMatrixDescription(String& buildOptions, const String& name, InputArray _m); + +class CV_EXPORTS Image2D +{ +public: + Image2D(); + + /** + @param src UMat object from which to get image properties and data + @param norm flag to enable the use of normalized channel data types + @param alias flag indicating that the image should alias the src UMat. If true, changes to the + image or src will be reflected in both objects. + */ + explicit Image2D(const UMat &src, bool norm = false, bool alias = false); + Image2D(const Image2D & i); + ~Image2D(); + + Image2D & operator = (const Image2D & i); + + /** Indicates if creating an aliased image should succeed. + Depends on the underlying platform and the dimensions of the UMat. + */ + static bool canCreateAlias(const UMat &u); + + /** Indicates if the image format is supported. + */ + static bool isFormatSupported(int depth, int cn, bool norm); + + void* ptr() const; +protected: + struct Impl; + Impl* p; +}; + +class CV_EXPORTS Timer +{ +public: + Timer(const Queue& q); + ~Timer(); + void start(); + void stop(); + + uint64 durationNS() const; //< duration in nanoseconds + +protected: + struct Impl; + Impl* const p; + +private: + Timer(const Timer&); // disabled + Timer& operator=(const Timer&); // disabled +}; + +CV_EXPORTS MatAllocator* getOpenCLAllocator(); + + +#ifdef __OPENCV_BUILD +namespace internal { + +CV_EXPORTS bool isOpenCLForced(); +#define OCL_FORCE_CHECK(condition) (cv::ocl::internal::isOpenCLForced() || (condition)) + +CV_EXPORTS bool isPerformanceCheckBypassed(); +#define OCL_PERFORMANCE_CHECK(condition) (cv::ocl::internal::isPerformanceCheckBypassed() || (condition)) + +CV_EXPORTS bool isCLBuffer(UMat& u); + +} // namespace internal +#endif + +//! @} + +}} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/ocl_genbase.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/ocl_genbase.hpp new file mode 100644 index 0000000..5334cf1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/ocl_genbase.hpp @@ -0,0 +1,69 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPENCL_GENBASE_HPP +#define OPENCV_OPENCL_GENBASE_HPP + +//! @cond IGNORED + +namespace cv { +namespace ocl { + +class ProgramSource; + +namespace internal { + +struct CV_EXPORTS ProgramEntry +{ + const char* module; + const char* name; + const char* programCode; + const char* programHash; + ProgramSource* pProgramSource; + + operator ProgramSource& () const; +}; + +} } } // namespace + +//! @endcond + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/ocl_defs.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/ocl_defs.hpp new file mode 100644 index 0000000..605a65f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/ocl_defs.hpp @@ -0,0 +1,75 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_CORE_OPENCL_DEFS_HPP +#define OPENCV_CORE_OPENCL_DEFS_HPP + +#include "opencv2/core/utility.hpp" +#include "cvconfig.h" + +namespace cv { namespace ocl { +#ifdef HAVE_OPENCL +/// Call is similar to useOpenCL() but doesn't try to load OpenCL runtime or create OpenCL context +CV_EXPORTS bool isOpenCLActivated(); +#else +static inline bool isOpenCLActivated() { return false; } +#endif +}} // namespace + + +//#define CV_OPENCL_RUN_ASSERT + +#ifdef HAVE_OPENCL + +#ifdef CV_OPENCL_RUN_VERBOSE +#define CV_OCL_RUN_(condition, func, ...) \ + { \ + if (cv::ocl::isOpenCLActivated() && (condition) && func) \ + { \ + printf("%s: OpenCL implementation is running\n", CV_Func); \ + fflush(stdout); \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + return __VA_ARGS__; \ + } \ + else \ + { \ + printf("%s: Plain implementation is running\n", CV_Func); \ + fflush(stdout); \ + } \ + } +#elif defined CV_OPENCL_RUN_ASSERT +#define CV_OCL_RUN_(condition, func, ...) \ + { \ + if (cv::ocl::isOpenCLActivated() && (condition)) \ + { \ + if(func) \ + { \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + } \ + else \ + { \ + CV_Error(cv::Error::StsAssert, #func); \ + } \ + return __VA_ARGS__; \ + } \ + } +#else +#define CV_OCL_RUN_(condition, func, ...) \ + if (cv::ocl::isOpenCLActivated() && (condition) && func) \ + { \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + return __VA_ARGS__; \ + } +#endif + +#else +#define CV_OCL_RUN_(condition, func, ...) +#endif + +#define CV_OCL_RUN(condition, func) CV_OCL_RUN_(condition, func) + +#endif // OPENCV_CORE_OPENCL_DEFS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_info.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_info.hpp new file mode 100644 index 0000000..5e5c846 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_info.hpp @@ -0,0 +1,205 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include + +#include +#include + +#ifndef DUMP_CONFIG_PROPERTY +#define DUMP_CONFIG_PROPERTY(...) +#endif + +#ifndef DUMP_MESSAGE_STDOUT +#define DUMP_MESSAGE_STDOUT(...) do { std::cout << __VA_ARGS__ << std::endl; } while (false) +#endif + +namespace cv { + +namespace { +static std::string bytesToStringRepr(size_t value) +{ + size_t b = value % 1024; + value /= 1024; + + size_t kb = value % 1024; + value /= 1024; + + size_t mb = value % 1024; + value /= 1024; + + size_t gb = value; + + std::ostringstream stream; + + if (gb > 0) + stream << gb << " GB "; + if (mb > 0) + stream << mb << " MB "; + if (kb > 0) + stream << kb << " KB "; + if (b > 0) + stream << b << " B"; + + std::string s = stream.str(); + if (s[s.size() - 1] == ' ') + s = s.substr(0, s.size() - 1); + return s; +} + +static String getDeviceTypeString(const cv::ocl::Device& device) +{ + if (device.type() == cv::ocl::Device::TYPE_CPU) { + return "CPU"; + } + + if (device.type() == cv::ocl::Device::TYPE_GPU) { + if (device.hostUnifiedMemory()) { + return "iGPU"; + } else { + return "dGPU"; + } + } + + return "unknown"; +} +} // namespace + +static void dumpOpenCLInformation() +{ + using namespace cv::ocl; + + try + { + if (!haveOpenCL() || !useOpenCL()) + { + DUMP_MESSAGE_STDOUT("OpenCL is disabled"); + DUMP_CONFIG_PROPERTY("cv_ocl", "disabled"); + return; + } + + std::vector platforms; + cv::ocl::getPlatfomsInfo(platforms); + if (platforms.empty()) + { + DUMP_MESSAGE_STDOUT("OpenCL is not available"); + DUMP_CONFIG_PROPERTY("cv_ocl", "not available"); + return; + } + + DUMP_MESSAGE_STDOUT("OpenCL Platforms: "); + for (size_t i = 0; i < platforms.size(); i++) + { + const PlatformInfo* platform = &platforms[i]; + DUMP_MESSAGE_STDOUT(" " << platform->name()); + Device current_device; + for (int j = 0; j < platform->deviceNumber(); j++) + { + platform->getDevice(current_device, j); + String deviceTypeStr = getDeviceTypeString(current_device); + DUMP_MESSAGE_STDOUT( " " << deviceTypeStr << ": " << current_device.name() << " (" << current_device.version() << ")"); + DUMP_CONFIG_PROPERTY( cv::format("cv_ocl_platform_%d_device_%d", (int)i, j ), + cv::format("(Platform=%s)(Type=%s)(Name=%s)(Version=%s)", + platform->name().c_str(), deviceTypeStr.c_str(), current_device.name().c_str(), current_device.version().c_str()) ); + } + } + const Device& device = Device::getDefault(); + if (!device.available()) + CV_Error(Error::OpenCLInitError, "OpenCL device is not available"); + + DUMP_MESSAGE_STDOUT("Current OpenCL device: "); + + String deviceTypeStr = getDeviceTypeString(device); + DUMP_MESSAGE_STDOUT(" Type = " << deviceTypeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceType", deviceTypeStr); + + DUMP_MESSAGE_STDOUT(" Name = " << device.name()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceName", device.name()); + + DUMP_MESSAGE_STDOUT(" Version = " << device.version()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceVersion", device.version()); + + DUMP_MESSAGE_STDOUT(" Driver version = " << device.driverVersion()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_driverVersion", device.driverVersion()); + + DUMP_MESSAGE_STDOUT(" Address bits = " << device.addressBits()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_addressBits", device.addressBits()); + + DUMP_MESSAGE_STDOUT(" Compute units = " << device.maxComputeUnits()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxComputeUnits", device.maxComputeUnits()); + + DUMP_MESSAGE_STDOUT(" Max work group size = " << device.maxWorkGroupSize()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxWorkGroupSize", device.maxWorkGroupSize()); + + std::string localMemorySizeStr = bytesToStringRepr(device.localMemSize()); + DUMP_MESSAGE_STDOUT(" Local memory size = " << localMemorySizeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_localMemSize", device.localMemSize()); + + std::string maxMemAllocSizeStr = bytesToStringRepr(device.maxMemAllocSize()); + DUMP_MESSAGE_STDOUT(" Max memory allocation size = " << maxMemAllocSizeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxMemAllocSize", device.maxMemAllocSize()); + + const char* doubleSupportStr = device.doubleFPConfig() > 0 ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Double support = " << doubleSupportStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_haveDoubleSupport", device.doubleFPConfig() > 0); + + const char* isUnifiedMemoryStr = device.hostUnifiedMemory() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Host unified memory = " << isUnifiedMemoryStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_hostUnifiedMemory", device.hostUnifiedMemory()); + + DUMP_MESSAGE_STDOUT(" Device extensions:"); + String extensionsStr = device.extensions(); + size_t pos = 0; + while (pos < extensionsStr.size()) + { + size_t pos2 = extensionsStr.find(' ', pos); + if (pos2 == String::npos) + pos2 = extensionsStr.size(); + if (pos2 > pos) + { + String extensionName = extensionsStr.substr(pos, pos2 - pos); + DUMP_MESSAGE_STDOUT(" " << extensionName); + } + pos = pos2 + 1; + } + DUMP_CONFIG_PROPERTY("cv_ocl_current_extensions", extensionsStr); + + const char* haveAmdBlasStr = haveAmdBlas() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Has AMD Blas = " << haveAmdBlasStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_AmdBlas", haveAmdBlas()); + + const char* haveAmdFftStr = haveAmdFft() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Has AMD Fft = " << haveAmdFftStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_AmdFft", haveAmdFft()); + + + DUMP_MESSAGE_STDOUT(" Preferred vector width char = " << device.preferredVectorWidthChar()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthChar", device.preferredVectorWidthChar()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width short = " << device.preferredVectorWidthShort()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthShort", device.preferredVectorWidthShort()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width int = " << device.preferredVectorWidthInt()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthInt", device.preferredVectorWidthInt()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width long = " << device.preferredVectorWidthLong()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthLong", device.preferredVectorWidthLong()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width float = " << device.preferredVectorWidthFloat()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthFloat", device.preferredVectorWidthFloat()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width double = " << device.preferredVectorWidthDouble()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthDouble", device.preferredVectorWidthDouble()); + } + catch (...) + { + DUMP_MESSAGE_STDOUT("Exception. Can't dump OpenCL info"); + DUMP_MESSAGE_STDOUT("OpenCL device not available"); + DUMP_CONFIG_PROPERTY("cv_ocl", "not available"); + } +} +#undef DUMP_MESSAGE_STDOUT +#undef DUMP_CONFIG_PROPERTY + +} // namespace diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_svm.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_svm.hpp new file mode 100644 index 0000000..7453082 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/opencl_svm.hpp @@ -0,0 +1,81 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OPENCL_SVM_HPP +#define OPENCV_CORE_OPENCL_SVM_HPP + +// +// Internal usage only (binary compatibility is not guaranteed) +// +#ifndef __OPENCV_BUILD +#error Internal header file +#endif + +#if defined(HAVE_OPENCL) && defined(HAVE_OPENCL_SVM) +#include "runtime/opencl_core.hpp" +#include "runtime/opencl_svm_20.hpp" +#include "runtime/opencl_svm_hsa_extension.hpp" + +namespace cv { namespace ocl { namespace svm { + +struct SVMCapabilities +{ + enum Value + { + SVM_COARSE_GRAIN_BUFFER = (1 << 0), + SVM_FINE_GRAIN_BUFFER = (1 << 1), + SVM_FINE_GRAIN_SYSTEM = (1 << 2), + SVM_ATOMICS = (1 << 3), + }; + int value_; + + SVMCapabilities(int capabilities = 0) : value_(capabilities) { } + operator int() const { return value_; } + + inline bool isNoSVMSupport() const { return value_ == 0; } + inline bool isSupportCoarseGrainBuffer() const { return (value_ & SVM_COARSE_GRAIN_BUFFER) != 0; } + inline bool isSupportFineGrainBuffer() const { return (value_ & SVM_FINE_GRAIN_BUFFER) != 0; } + inline bool isSupportFineGrainSystem() const { return (value_ & SVM_FINE_GRAIN_SYSTEM) != 0; } + inline bool isSupportAtomics() const { return (value_ & SVM_ATOMICS) != 0; } +}; + +CV_EXPORTS const SVMCapabilities getSVMCapabilitites(const ocl::Context& context); + +struct SVMFunctions +{ + clSVMAllocAMD_fn fn_clSVMAlloc; + clSVMFreeAMD_fn fn_clSVMFree; + clSetKernelArgSVMPointerAMD_fn fn_clSetKernelArgSVMPointer; + //clSetKernelExecInfoAMD_fn fn_clSetKernelExecInfo; + //clEnqueueSVMFreeAMD_fn fn_clEnqueueSVMFree; + clEnqueueSVMMemcpyAMD_fn fn_clEnqueueSVMMemcpy; + clEnqueueSVMMemFillAMD_fn fn_clEnqueueSVMMemFill; + clEnqueueSVMMapAMD_fn fn_clEnqueueSVMMap; + clEnqueueSVMUnmapAMD_fn fn_clEnqueueSVMUnmap; + + inline SVMFunctions() + : fn_clSVMAlloc(NULL), fn_clSVMFree(NULL), + fn_clSetKernelArgSVMPointer(NULL), /*fn_clSetKernelExecInfo(NULL),*/ + /*fn_clEnqueueSVMFree(NULL),*/ fn_clEnqueueSVMMemcpy(NULL), fn_clEnqueueSVMMemFill(NULL), + fn_clEnqueueSVMMap(NULL), fn_clEnqueueSVMUnmap(NULL) + { + // nothing + } + + inline bool isValid() const + { + return fn_clSVMAlloc != NULL && fn_clSVMFree && fn_clSetKernelArgSVMPointer && + /*fn_clSetKernelExecInfo && fn_clEnqueueSVMFree &&*/ fn_clEnqueueSVMMemcpy && + fn_clEnqueueSVMMemFill && fn_clEnqueueSVMMap && fn_clEnqueueSVMUnmap; + } +}; + +// We should guarantee that SVMFunctions lifetime is not less than context's lifetime +CV_EXPORTS const SVMFunctions* getSVMFunctions(const ocl::Context& context); + +CV_EXPORTS bool useSVM(UMatUsageFlags usageFlags); + +}}} //namespace cv::ocl::svm +#endif + +#endif // OPENCV_CORE_OPENCL_SVM_HPP +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp new file mode 100644 index 0000000..65c8493 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp @@ -0,0 +1,714 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP +#error "Invalid usage" +#endif + +// generated by parser_clamdblas.py +#define clAmdBlasAddScratchImage clAmdBlasAddScratchImage_ +#define clAmdBlasCaxpy clAmdBlasCaxpy_ +#define clAmdBlasCcopy clAmdBlasCcopy_ +#define clAmdBlasCdotc clAmdBlasCdotc_ +#define clAmdBlasCdotu clAmdBlasCdotu_ +#define clAmdBlasCgbmv clAmdBlasCgbmv_ +#define clAmdBlasCgemm clAmdBlasCgemm_ +#define clAmdBlasCgemmEx clAmdBlasCgemmEx_ +#define clAmdBlasCgemv clAmdBlasCgemv_ +#define clAmdBlasCgemvEx clAmdBlasCgemvEx_ +#define clAmdBlasCgerc clAmdBlasCgerc_ +#define clAmdBlasCgeru clAmdBlasCgeru_ +#define clAmdBlasChbmv clAmdBlasChbmv_ +#define clAmdBlasChemm clAmdBlasChemm_ +#define clAmdBlasChemv clAmdBlasChemv_ +#define clAmdBlasCher clAmdBlasCher_ +#define clAmdBlasCher2 clAmdBlasCher2_ +#define clAmdBlasCher2k clAmdBlasCher2k_ +#define clAmdBlasCherk clAmdBlasCherk_ +#define clAmdBlasChpmv clAmdBlasChpmv_ +#define clAmdBlasChpr clAmdBlasChpr_ +#define clAmdBlasChpr2 clAmdBlasChpr2_ +#define clAmdBlasCrotg clAmdBlasCrotg_ +#define clAmdBlasCscal clAmdBlasCscal_ +#define clAmdBlasCsrot clAmdBlasCsrot_ +#define clAmdBlasCsscal clAmdBlasCsscal_ +#define clAmdBlasCswap clAmdBlasCswap_ +#define clAmdBlasCsymm clAmdBlasCsymm_ +#define clAmdBlasCsyr2k clAmdBlasCsyr2k_ +#define clAmdBlasCsyr2kEx clAmdBlasCsyr2kEx_ +#define clAmdBlasCsyrk clAmdBlasCsyrk_ +#define clAmdBlasCsyrkEx clAmdBlasCsyrkEx_ +#define clAmdBlasCtbmv clAmdBlasCtbmv_ +#define clAmdBlasCtbsv clAmdBlasCtbsv_ +#define clAmdBlasCtpmv clAmdBlasCtpmv_ +#define clAmdBlasCtpsv clAmdBlasCtpsv_ +#define clAmdBlasCtrmm clAmdBlasCtrmm_ +#define clAmdBlasCtrmmEx clAmdBlasCtrmmEx_ +#define clAmdBlasCtrmv clAmdBlasCtrmv_ +#define clAmdBlasCtrsm clAmdBlasCtrsm_ +#define clAmdBlasCtrsmEx clAmdBlasCtrsmEx_ +#define clAmdBlasCtrsv clAmdBlasCtrsv_ +#define clAmdBlasDasum clAmdBlasDasum_ +#define clAmdBlasDaxpy clAmdBlasDaxpy_ +#define clAmdBlasDcopy clAmdBlasDcopy_ +#define clAmdBlasDdot clAmdBlasDdot_ +#define clAmdBlasDgbmv clAmdBlasDgbmv_ +#define clAmdBlasDgemm clAmdBlasDgemm_ +#define clAmdBlasDgemmEx clAmdBlasDgemmEx_ +#define clAmdBlasDgemv clAmdBlasDgemv_ +#define clAmdBlasDgemvEx clAmdBlasDgemvEx_ +#define clAmdBlasDger clAmdBlasDger_ +#define clAmdBlasDnrm2 clAmdBlasDnrm2_ +#define clAmdBlasDrot clAmdBlasDrot_ +#define clAmdBlasDrotg clAmdBlasDrotg_ +#define clAmdBlasDrotm clAmdBlasDrotm_ +#define clAmdBlasDrotmg clAmdBlasDrotmg_ +#define clAmdBlasDsbmv clAmdBlasDsbmv_ +#define clAmdBlasDscal clAmdBlasDscal_ +#define clAmdBlasDspmv clAmdBlasDspmv_ +#define clAmdBlasDspr clAmdBlasDspr_ +#define clAmdBlasDspr2 clAmdBlasDspr2_ +#define clAmdBlasDswap clAmdBlasDswap_ +#define clAmdBlasDsymm clAmdBlasDsymm_ +#define clAmdBlasDsymv clAmdBlasDsymv_ +#define clAmdBlasDsymvEx clAmdBlasDsymvEx_ +#define clAmdBlasDsyr clAmdBlasDsyr_ +#define clAmdBlasDsyr2 clAmdBlasDsyr2_ +#define clAmdBlasDsyr2k clAmdBlasDsyr2k_ +#define clAmdBlasDsyr2kEx clAmdBlasDsyr2kEx_ +#define clAmdBlasDsyrk clAmdBlasDsyrk_ +#define clAmdBlasDsyrkEx clAmdBlasDsyrkEx_ +#define clAmdBlasDtbmv clAmdBlasDtbmv_ +#define clAmdBlasDtbsv clAmdBlasDtbsv_ +#define clAmdBlasDtpmv clAmdBlasDtpmv_ +#define clAmdBlasDtpsv clAmdBlasDtpsv_ +#define clAmdBlasDtrmm clAmdBlasDtrmm_ +#define clAmdBlasDtrmmEx clAmdBlasDtrmmEx_ +#define clAmdBlasDtrmv clAmdBlasDtrmv_ +#define clAmdBlasDtrsm clAmdBlasDtrsm_ +#define clAmdBlasDtrsmEx clAmdBlasDtrsmEx_ +#define clAmdBlasDtrsv clAmdBlasDtrsv_ +#define clAmdBlasDzasum clAmdBlasDzasum_ +#define clAmdBlasDznrm2 clAmdBlasDznrm2_ +#define clAmdBlasGetVersion clAmdBlasGetVersion_ +#define clAmdBlasRemoveScratchImage clAmdBlasRemoveScratchImage_ +#define clAmdBlasSasum clAmdBlasSasum_ +#define clAmdBlasSaxpy clAmdBlasSaxpy_ +#define clAmdBlasScasum clAmdBlasScasum_ +#define clAmdBlasScnrm2 clAmdBlasScnrm2_ +#define clAmdBlasScopy clAmdBlasScopy_ +#define clAmdBlasSdot clAmdBlasSdot_ +#define clAmdBlasSetup clAmdBlasSetup_ +#define clAmdBlasSgbmv clAmdBlasSgbmv_ +#define clAmdBlasSgemm clAmdBlasSgemm_ +#define clAmdBlasSgemmEx clAmdBlasSgemmEx_ +#define clAmdBlasSgemv clAmdBlasSgemv_ +#define clAmdBlasSgemvEx clAmdBlasSgemvEx_ +#define clAmdBlasSger clAmdBlasSger_ +#define clAmdBlasSnrm2 clAmdBlasSnrm2_ +#define clAmdBlasSrot clAmdBlasSrot_ +#define clAmdBlasSrotg clAmdBlasSrotg_ +#define clAmdBlasSrotm clAmdBlasSrotm_ +#define clAmdBlasSrotmg clAmdBlasSrotmg_ +#define clAmdBlasSsbmv clAmdBlasSsbmv_ +#define clAmdBlasSscal clAmdBlasSscal_ +#define clAmdBlasSspmv clAmdBlasSspmv_ +#define clAmdBlasSspr clAmdBlasSspr_ +#define clAmdBlasSspr2 clAmdBlasSspr2_ +#define clAmdBlasSswap clAmdBlasSswap_ +#define clAmdBlasSsymm clAmdBlasSsymm_ +#define clAmdBlasSsymv clAmdBlasSsymv_ +#define clAmdBlasSsymvEx clAmdBlasSsymvEx_ +#define clAmdBlasSsyr clAmdBlasSsyr_ +#define clAmdBlasSsyr2 clAmdBlasSsyr2_ +#define clAmdBlasSsyr2k clAmdBlasSsyr2k_ +#define clAmdBlasSsyr2kEx clAmdBlasSsyr2kEx_ +#define clAmdBlasSsyrk clAmdBlasSsyrk_ +#define clAmdBlasSsyrkEx clAmdBlasSsyrkEx_ +#define clAmdBlasStbmv clAmdBlasStbmv_ +#define clAmdBlasStbsv clAmdBlasStbsv_ +#define clAmdBlasStpmv clAmdBlasStpmv_ +#define clAmdBlasStpsv clAmdBlasStpsv_ +#define clAmdBlasStrmm clAmdBlasStrmm_ +#define clAmdBlasStrmmEx clAmdBlasStrmmEx_ +#define clAmdBlasStrmv clAmdBlasStrmv_ +#define clAmdBlasStrsm clAmdBlasStrsm_ +#define clAmdBlasStrsmEx clAmdBlasStrsmEx_ +#define clAmdBlasStrsv clAmdBlasStrsv_ +#define clAmdBlasTeardown clAmdBlasTeardown_ +#define clAmdBlasZaxpy clAmdBlasZaxpy_ +#define clAmdBlasZcopy clAmdBlasZcopy_ +#define clAmdBlasZdotc clAmdBlasZdotc_ +#define clAmdBlasZdotu clAmdBlasZdotu_ +#define clAmdBlasZdrot clAmdBlasZdrot_ +#define clAmdBlasZdscal clAmdBlasZdscal_ +#define clAmdBlasZgbmv clAmdBlasZgbmv_ +#define clAmdBlasZgemm clAmdBlasZgemm_ +#define clAmdBlasZgemmEx clAmdBlasZgemmEx_ +#define clAmdBlasZgemv clAmdBlasZgemv_ +#define clAmdBlasZgemvEx clAmdBlasZgemvEx_ +#define clAmdBlasZgerc clAmdBlasZgerc_ +#define clAmdBlasZgeru clAmdBlasZgeru_ +#define clAmdBlasZhbmv clAmdBlasZhbmv_ +#define clAmdBlasZhemm clAmdBlasZhemm_ +#define clAmdBlasZhemv clAmdBlasZhemv_ +#define clAmdBlasZher clAmdBlasZher_ +#define clAmdBlasZher2 clAmdBlasZher2_ +#define clAmdBlasZher2k clAmdBlasZher2k_ +#define clAmdBlasZherk clAmdBlasZherk_ +#define clAmdBlasZhpmv clAmdBlasZhpmv_ +#define clAmdBlasZhpr clAmdBlasZhpr_ +#define clAmdBlasZhpr2 clAmdBlasZhpr2_ +#define clAmdBlasZrotg clAmdBlasZrotg_ +#define clAmdBlasZscal clAmdBlasZscal_ +#define clAmdBlasZswap clAmdBlasZswap_ +#define clAmdBlasZsymm clAmdBlasZsymm_ +#define clAmdBlasZsyr2k clAmdBlasZsyr2k_ +#define clAmdBlasZsyr2kEx clAmdBlasZsyr2kEx_ +#define clAmdBlasZsyrk clAmdBlasZsyrk_ +#define clAmdBlasZsyrkEx clAmdBlasZsyrkEx_ +#define clAmdBlasZtbmv clAmdBlasZtbmv_ +#define clAmdBlasZtbsv clAmdBlasZtbsv_ +#define clAmdBlasZtpmv clAmdBlasZtpmv_ +#define clAmdBlasZtpsv clAmdBlasZtpsv_ +#define clAmdBlasZtrmm clAmdBlasZtrmm_ +#define clAmdBlasZtrmmEx clAmdBlasZtrmmEx_ +#define clAmdBlasZtrmv clAmdBlasZtrmv_ +#define clAmdBlasZtrsm clAmdBlasZtrsm_ +#define clAmdBlasZtrsmEx clAmdBlasZtrsmEx_ +#define clAmdBlasZtrsv clAmdBlasZtrsv_ +#define clAmdBlasiCamax clAmdBlasiCamax_ +#define clAmdBlasiDamax clAmdBlasiDamax_ +#define clAmdBlasiSamax clAmdBlasiSamax_ +#define clAmdBlasiZamax clAmdBlasiZamax_ + +#include + +// generated by parser_clamdblas.py +#undef clAmdBlasAddScratchImage +//#define clAmdBlasAddScratchImage clAmdBlasAddScratchImage_pfn +#undef clAmdBlasCaxpy +//#define clAmdBlasCaxpy clAmdBlasCaxpy_pfn +#undef clAmdBlasCcopy +//#define clAmdBlasCcopy clAmdBlasCcopy_pfn +#undef clAmdBlasCdotc +//#define clAmdBlasCdotc clAmdBlasCdotc_pfn +#undef clAmdBlasCdotu +//#define clAmdBlasCdotu clAmdBlasCdotu_pfn +#undef clAmdBlasCgbmv +//#define clAmdBlasCgbmv clAmdBlasCgbmv_pfn +#undef clAmdBlasCgemm +//#define clAmdBlasCgemm clAmdBlasCgemm_pfn +#undef clAmdBlasCgemmEx +#define clAmdBlasCgemmEx clAmdBlasCgemmEx_pfn +#undef clAmdBlasCgemv +//#define clAmdBlasCgemv clAmdBlasCgemv_pfn +#undef clAmdBlasCgemvEx +//#define clAmdBlasCgemvEx clAmdBlasCgemvEx_pfn +#undef clAmdBlasCgerc +//#define clAmdBlasCgerc clAmdBlasCgerc_pfn +#undef clAmdBlasCgeru +//#define clAmdBlasCgeru clAmdBlasCgeru_pfn +#undef clAmdBlasChbmv +//#define clAmdBlasChbmv clAmdBlasChbmv_pfn +#undef clAmdBlasChemm +//#define clAmdBlasChemm clAmdBlasChemm_pfn +#undef clAmdBlasChemv +//#define clAmdBlasChemv clAmdBlasChemv_pfn +#undef clAmdBlasCher +//#define clAmdBlasCher clAmdBlasCher_pfn +#undef clAmdBlasCher2 +//#define clAmdBlasCher2 clAmdBlasCher2_pfn +#undef clAmdBlasCher2k +//#define clAmdBlasCher2k clAmdBlasCher2k_pfn +#undef clAmdBlasCherk +//#define clAmdBlasCherk clAmdBlasCherk_pfn +#undef clAmdBlasChpmv +//#define clAmdBlasChpmv clAmdBlasChpmv_pfn +#undef clAmdBlasChpr +//#define clAmdBlasChpr clAmdBlasChpr_pfn +#undef clAmdBlasChpr2 +//#define clAmdBlasChpr2 clAmdBlasChpr2_pfn +#undef clAmdBlasCrotg +//#define clAmdBlasCrotg clAmdBlasCrotg_pfn +#undef clAmdBlasCscal +//#define clAmdBlasCscal clAmdBlasCscal_pfn +#undef clAmdBlasCsrot +//#define clAmdBlasCsrot clAmdBlasCsrot_pfn +#undef clAmdBlasCsscal +//#define clAmdBlasCsscal clAmdBlasCsscal_pfn +#undef clAmdBlasCswap +//#define clAmdBlasCswap clAmdBlasCswap_pfn +#undef clAmdBlasCsymm +//#define clAmdBlasCsymm clAmdBlasCsymm_pfn +#undef clAmdBlasCsyr2k +//#define clAmdBlasCsyr2k clAmdBlasCsyr2k_pfn +#undef clAmdBlasCsyr2kEx +//#define clAmdBlasCsyr2kEx clAmdBlasCsyr2kEx_pfn +#undef clAmdBlasCsyrk +//#define clAmdBlasCsyrk clAmdBlasCsyrk_pfn +#undef clAmdBlasCsyrkEx +//#define clAmdBlasCsyrkEx clAmdBlasCsyrkEx_pfn +#undef clAmdBlasCtbmv +//#define clAmdBlasCtbmv clAmdBlasCtbmv_pfn +#undef clAmdBlasCtbsv +//#define clAmdBlasCtbsv clAmdBlasCtbsv_pfn +#undef clAmdBlasCtpmv +//#define clAmdBlasCtpmv clAmdBlasCtpmv_pfn +#undef clAmdBlasCtpsv +//#define clAmdBlasCtpsv clAmdBlasCtpsv_pfn +#undef clAmdBlasCtrmm +//#define clAmdBlasCtrmm clAmdBlasCtrmm_pfn +#undef clAmdBlasCtrmmEx +//#define clAmdBlasCtrmmEx clAmdBlasCtrmmEx_pfn +#undef clAmdBlasCtrmv +//#define clAmdBlasCtrmv clAmdBlasCtrmv_pfn +#undef clAmdBlasCtrsm +//#define clAmdBlasCtrsm clAmdBlasCtrsm_pfn +#undef clAmdBlasCtrsmEx +//#define clAmdBlasCtrsmEx clAmdBlasCtrsmEx_pfn +#undef clAmdBlasCtrsv +//#define clAmdBlasCtrsv clAmdBlasCtrsv_pfn +#undef clAmdBlasDasum +//#define clAmdBlasDasum clAmdBlasDasum_pfn +#undef clAmdBlasDaxpy +//#define clAmdBlasDaxpy clAmdBlasDaxpy_pfn +#undef clAmdBlasDcopy +//#define clAmdBlasDcopy clAmdBlasDcopy_pfn +#undef clAmdBlasDdot +//#define clAmdBlasDdot clAmdBlasDdot_pfn +#undef clAmdBlasDgbmv +//#define clAmdBlasDgbmv clAmdBlasDgbmv_pfn +#undef clAmdBlasDgemm +//#define clAmdBlasDgemm clAmdBlasDgemm_pfn +#undef clAmdBlasDgemmEx +#define clAmdBlasDgemmEx clAmdBlasDgemmEx_pfn +#undef clAmdBlasDgemv +//#define clAmdBlasDgemv clAmdBlasDgemv_pfn +#undef clAmdBlasDgemvEx +//#define clAmdBlasDgemvEx clAmdBlasDgemvEx_pfn +#undef clAmdBlasDger +//#define clAmdBlasDger clAmdBlasDger_pfn +#undef clAmdBlasDnrm2 +//#define clAmdBlasDnrm2 clAmdBlasDnrm2_pfn +#undef clAmdBlasDrot +//#define clAmdBlasDrot clAmdBlasDrot_pfn +#undef clAmdBlasDrotg +//#define clAmdBlasDrotg clAmdBlasDrotg_pfn +#undef clAmdBlasDrotm +//#define clAmdBlasDrotm clAmdBlasDrotm_pfn +#undef clAmdBlasDrotmg +//#define clAmdBlasDrotmg clAmdBlasDrotmg_pfn +#undef clAmdBlasDsbmv +//#define clAmdBlasDsbmv clAmdBlasDsbmv_pfn +#undef clAmdBlasDscal +//#define clAmdBlasDscal clAmdBlasDscal_pfn +#undef clAmdBlasDspmv +//#define clAmdBlasDspmv clAmdBlasDspmv_pfn +#undef clAmdBlasDspr +//#define clAmdBlasDspr clAmdBlasDspr_pfn +#undef clAmdBlasDspr2 +//#define clAmdBlasDspr2 clAmdBlasDspr2_pfn +#undef clAmdBlasDswap +//#define clAmdBlasDswap clAmdBlasDswap_pfn +#undef clAmdBlasDsymm +//#define clAmdBlasDsymm clAmdBlasDsymm_pfn +#undef clAmdBlasDsymv +//#define clAmdBlasDsymv clAmdBlasDsymv_pfn +#undef clAmdBlasDsymvEx +//#define clAmdBlasDsymvEx clAmdBlasDsymvEx_pfn +#undef clAmdBlasDsyr +//#define clAmdBlasDsyr clAmdBlasDsyr_pfn +#undef clAmdBlasDsyr2 +//#define clAmdBlasDsyr2 clAmdBlasDsyr2_pfn +#undef clAmdBlasDsyr2k +//#define clAmdBlasDsyr2k clAmdBlasDsyr2k_pfn +#undef clAmdBlasDsyr2kEx +//#define clAmdBlasDsyr2kEx clAmdBlasDsyr2kEx_pfn +#undef clAmdBlasDsyrk +//#define clAmdBlasDsyrk clAmdBlasDsyrk_pfn +#undef clAmdBlasDsyrkEx +//#define clAmdBlasDsyrkEx clAmdBlasDsyrkEx_pfn +#undef clAmdBlasDtbmv +//#define clAmdBlasDtbmv clAmdBlasDtbmv_pfn +#undef clAmdBlasDtbsv +//#define clAmdBlasDtbsv clAmdBlasDtbsv_pfn +#undef clAmdBlasDtpmv +//#define clAmdBlasDtpmv clAmdBlasDtpmv_pfn +#undef clAmdBlasDtpsv +//#define clAmdBlasDtpsv clAmdBlasDtpsv_pfn +#undef clAmdBlasDtrmm +//#define clAmdBlasDtrmm clAmdBlasDtrmm_pfn +#undef clAmdBlasDtrmmEx +//#define clAmdBlasDtrmmEx clAmdBlasDtrmmEx_pfn +#undef clAmdBlasDtrmv +//#define clAmdBlasDtrmv clAmdBlasDtrmv_pfn +#undef clAmdBlasDtrsm +//#define clAmdBlasDtrsm clAmdBlasDtrsm_pfn +#undef clAmdBlasDtrsmEx +//#define clAmdBlasDtrsmEx clAmdBlasDtrsmEx_pfn +#undef clAmdBlasDtrsv +//#define clAmdBlasDtrsv clAmdBlasDtrsv_pfn +#undef clAmdBlasDzasum +//#define clAmdBlasDzasum clAmdBlasDzasum_pfn +#undef clAmdBlasDznrm2 +//#define clAmdBlasDznrm2 clAmdBlasDznrm2_pfn +#undef clAmdBlasGetVersion +//#define clAmdBlasGetVersion clAmdBlasGetVersion_pfn +#undef clAmdBlasRemoveScratchImage +//#define clAmdBlasRemoveScratchImage clAmdBlasRemoveScratchImage_pfn +#undef clAmdBlasSasum +//#define clAmdBlasSasum clAmdBlasSasum_pfn +#undef clAmdBlasSaxpy +//#define clAmdBlasSaxpy clAmdBlasSaxpy_pfn +#undef clAmdBlasScasum +//#define clAmdBlasScasum clAmdBlasScasum_pfn +#undef clAmdBlasScnrm2 +//#define clAmdBlasScnrm2 clAmdBlasScnrm2_pfn +#undef clAmdBlasScopy +//#define clAmdBlasScopy clAmdBlasScopy_pfn +#undef clAmdBlasSdot +//#define clAmdBlasSdot clAmdBlasSdot_pfn +#undef clAmdBlasSetup +#define clAmdBlasSetup clAmdBlasSetup_pfn +#undef clAmdBlasSgbmv +//#define clAmdBlasSgbmv clAmdBlasSgbmv_pfn +#undef clAmdBlasSgemm +//#define clAmdBlasSgemm clAmdBlasSgemm_pfn +#undef clAmdBlasSgemmEx +#define clAmdBlasSgemmEx clAmdBlasSgemmEx_pfn +#undef clAmdBlasSgemv +//#define clAmdBlasSgemv clAmdBlasSgemv_pfn +#undef clAmdBlasSgemvEx +//#define clAmdBlasSgemvEx clAmdBlasSgemvEx_pfn +#undef clAmdBlasSger +//#define clAmdBlasSger clAmdBlasSger_pfn +#undef clAmdBlasSnrm2 +//#define clAmdBlasSnrm2 clAmdBlasSnrm2_pfn +#undef clAmdBlasSrot +//#define clAmdBlasSrot clAmdBlasSrot_pfn +#undef clAmdBlasSrotg +//#define clAmdBlasSrotg clAmdBlasSrotg_pfn +#undef clAmdBlasSrotm +//#define clAmdBlasSrotm clAmdBlasSrotm_pfn +#undef clAmdBlasSrotmg +//#define clAmdBlasSrotmg clAmdBlasSrotmg_pfn +#undef clAmdBlasSsbmv +//#define clAmdBlasSsbmv clAmdBlasSsbmv_pfn +#undef clAmdBlasSscal +//#define clAmdBlasSscal clAmdBlasSscal_pfn +#undef clAmdBlasSspmv +//#define clAmdBlasSspmv clAmdBlasSspmv_pfn +#undef clAmdBlasSspr +//#define clAmdBlasSspr clAmdBlasSspr_pfn +#undef clAmdBlasSspr2 +//#define clAmdBlasSspr2 clAmdBlasSspr2_pfn +#undef clAmdBlasSswap +//#define clAmdBlasSswap clAmdBlasSswap_pfn +#undef clAmdBlasSsymm +//#define clAmdBlasSsymm clAmdBlasSsymm_pfn +#undef clAmdBlasSsymv +//#define clAmdBlasSsymv clAmdBlasSsymv_pfn +#undef clAmdBlasSsymvEx +//#define clAmdBlasSsymvEx clAmdBlasSsymvEx_pfn +#undef clAmdBlasSsyr +//#define clAmdBlasSsyr clAmdBlasSsyr_pfn +#undef clAmdBlasSsyr2 +//#define clAmdBlasSsyr2 clAmdBlasSsyr2_pfn +#undef clAmdBlasSsyr2k +//#define clAmdBlasSsyr2k clAmdBlasSsyr2k_pfn +#undef clAmdBlasSsyr2kEx +//#define clAmdBlasSsyr2kEx clAmdBlasSsyr2kEx_pfn +#undef clAmdBlasSsyrk +//#define clAmdBlasSsyrk clAmdBlasSsyrk_pfn +#undef clAmdBlasSsyrkEx +//#define clAmdBlasSsyrkEx clAmdBlasSsyrkEx_pfn +#undef clAmdBlasStbmv +//#define clAmdBlasStbmv clAmdBlasStbmv_pfn +#undef clAmdBlasStbsv +//#define clAmdBlasStbsv clAmdBlasStbsv_pfn +#undef clAmdBlasStpmv +//#define clAmdBlasStpmv clAmdBlasStpmv_pfn +#undef clAmdBlasStpsv +//#define clAmdBlasStpsv clAmdBlasStpsv_pfn +#undef clAmdBlasStrmm +//#define clAmdBlasStrmm clAmdBlasStrmm_pfn +#undef clAmdBlasStrmmEx +//#define clAmdBlasStrmmEx clAmdBlasStrmmEx_pfn +#undef clAmdBlasStrmv +//#define clAmdBlasStrmv clAmdBlasStrmv_pfn +#undef clAmdBlasStrsm +//#define clAmdBlasStrsm clAmdBlasStrsm_pfn +#undef clAmdBlasStrsmEx +//#define clAmdBlasStrsmEx clAmdBlasStrsmEx_pfn +#undef clAmdBlasStrsv +//#define clAmdBlasStrsv clAmdBlasStrsv_pfn +#undef clAmdBlasTeardown +#define clAmdBlasTeardown clAmdBlasTeardown_pfn +#undef clAmdBlasZaxpy +//#define clAmdBlasZaxpy clAmdBlasZaxpy_pfn +#undef clAmdBlasZcopy +//#define clAmdBlasZcopy clAmdBlasZcopy_pfn +#undef clAmdBlasZdotc +//#define clAmdBlasZdotc clAmdBlasZdotc_pfn +#undef clAmdBlasZdotu +//#define clAmdBlasZdotu clAmdBlasZdotu_pfn +#undef clAmdBlasZdrot +//#define clAmdBlasZdrot clAmdBlasZdrot_pfn +#undef clAmdBlasZdscal +//#define clAmdBlasZdscal clAmdBlasZdscal_pfn +#undef clAmdBlasZgbmv +//#define clAmdBlasZgbmv clAmdBlasZgbmv_pfn +#undef clAmdBlasZgemm +//#define clAmdBlasZgemm clAmdBlasZgemm_pfn +#undef clAmdBlasZgemmEx +#define clAmdBlasZgemmEx clAmdBlasZgemmEx_pfn +#undef clAmdBlasZgemv +//#define clAmdBlasZgemv clAmdBlasZgemv_pfn +#undef clAmdBlasZgemvEx +//#define clAmdBlasZgemvEx clAmdBlasZgemvEx_pfn +#undef clAmdBlasZgerc +//#define clAmdBlasZgerc clAmdBlasZgerc_pfn +#undef clAmdBlasZgeru +//#define clAmdBlasZgeru clAmdBlasZgeru_pfn +#undef clAmdBlasZhbmv +//#define clAmdBlasZhbmv clAmdBlasZhbmv_pfn +#undef clAmdBlasZhemm +//#define clAmdBlasZhemm clAmdBlasZhemm_pfn +#undef clAmdBlasZhemv +//#define clAmdBlasZhemv clAmdBlasZhemv_pfn +#undef clAmdBlasZher +//#define clAmdBlasZher clAmdBlasZher_pfn +#undef clAmdBlasZher2 +//#define clAmdBlasZher2 clAmdBlasZher2_pfn +#undef clAmdBlasZher2k +//#define clAmdBlasZher2k clAmdBlasZher2k_pfn +#undef clAmdBlasZherk +//#define clAmdBlasZherk clAmdBlasZherk_pfn +#undef clAmdBlasZhpmv +//#define clAmdBlasZhpmv clAmdBlasZhpmv_pfn +#undef clAmdBlasZhpr +//#define clAmdBlasZhpr clAmdBlasZhpr_pfn +#undef clAmdBlasZhpr2 +//#define clAmdBlasZhpr2 clAmdBlasZhpr2_pfn +#undef clAmdBlasZrotg +//#define clAmdBlasZrotg clAmdBlasZrotg_pfn +#undef clAmdBlasZscal +//#define clAmdBlasZscal clAmdBlasZscal_pfn +#undef clAmdBlasZswap +//#define clAmdBlasZswap clAmdBlasZswap_pfn +#undef clAmdBlasZsymm +//#define clAmdBlasZsymm clAmdBlasZsymm_pfn +#undef clAmdBlasZsyr2k +//#define clAmdBlasZsyr2k clAmdBlasZsyr2k_pfn +#undef clAmdBlasZsyr2kEx +//#define clAmdBlasZsyr2kEx clAmdBlasZsyr2kEx_pfn +#undef clAmdBlasZsyrk +//#define clAmdBlasZsyrk clAmdBlasZsyrk_pfn +#undef clAmdBlasZsyrkEx +//#define clAmdBlasZsyrkEx clAmdBlasZsyrkEx_pfn +#undef clAmdBlasZtbmv +//#define clAmdBlasZtbmv clAmdBlasZtbmv_pfn +#undef clAmdBlasZtbsv +//#define clAmdBlasZtbsv clAmdBlasZtbsv_pfn +#undef clAmdBlasZtpmv +//#define clAmdBlasZtpmv clAmdBlasZtpmv_pfn +#undef clAmdBlasZtpsv +//#define clAmdBlasZtpsv clAmdBlasZtpsv_pfn +#undef clAmdBlasZtrmm +//#define clAmdBlasZtrmm clAmdBlasZtrmm_pfn +#undef clAmdBlasZtrmmEx +//#define clAmdBlasZtrmmEx clAmdBlasZtrmmEx_pfn +#undef clAmdBlasZtrmv +//#define clAmdBlasZtrmv clAmdBlasZtrmv_pfn +#undef clAmdBlasZtrsm +//#define clAmdBlasZtrsm clAmdBlasZtrsm_pfn +#undef clAmdBlasZtrsmEx +//#define clAmdBlasZtrsmEx clAmdBlasZtrsmEx_pfn +#undef clAmdBlasZtrsv +//#define clAmdBlasZtrsv clAmdBlasZtrsv_pfn +#undef clAmdBlasiCamax +//#define clAmdBlasiCamax clAmdBlasiCamax_pfn +#undef clAmdBlasiDamax +//#define clAmdBlasiDamax clAmdBlasiDamax_pfn +#undef clAmdBlasiSamax +//#define clAmdBlasiSamax clAmdBlasiSamax_pfn +#undef clAmdBlasiZamax +//#define clAmdBlasiZamax clAmdBlasiZamax_pfn + +// generated by parser_clamdblas.py +//extern CL_RUNTIME_EXPORT cl_ulong (*clAmdBlasAddScratchImage)(cl_context context, size_t width, size_t height, clAmdBlasStatus* status); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCaxpy)(size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCdotc)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCdotu)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, FloatComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, FloatComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgerc)(clAmdBlasOrder order, size_t M, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgeru)(clAmdBlasOrder order, size_t M, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChemm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChemv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, FloatComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, FloatComplex beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCherk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, float alpha, const cl_mem A, size_t offa, size_t lda, float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCrotg)(cl_mem CA, size_t offCA, cl_mem CB, size_t offCB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCscal)(size_t N, cl_float2 alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_float C, cl_float S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsscal)(size_t N, cl_float alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDaxpy)(size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDdot)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDger)(clAmdBlasOrder order, size_t M, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_double C, cl_double S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotg)(cl_mem DA, size_t offDA, cl_mem DB, size_t offDB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotm)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, const cl_mem DPARAM, size_t offDparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotmg)(cl_mem DD1, size_t offDD1, cl_mem DD2, size_t offDD2, cl_mem DX1, size_t offDX1, const cl_mem DY1, size_t offDY1, cl_mem DPARAM, size_t offDparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDscal)(size_t N, cl_double alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymvEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDzasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDznrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasGetVersion)(cl_uint* major, cl_uint* minor, cl_uint* patch); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasRemoveScratchImage)(cl_ulong imageID); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSaxpy)(size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSdot)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSetup)(); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSger)(clAmdBlasOrder order, size_t M, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_float C, cl_float S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotg)(cl_mem SA, size_t offSA, cl_mem SB, size_t offSB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotm)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, const cl_mem SPARAM, size_t offSparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotmg)(cl_mem SD1, size_t offSD1, cl_mem SD2, size_t offSD2, cl_mem SX1, size_t offSX1, const cl_mem SY1, size_t offSY1, cl_mem SPARAM, size_t offSparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSscal)(size_t N, cl_float alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymvEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT void (*clAmdBlasTeardown)(); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZaxpy)(size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdotc)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdotu)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_double C, cl_double S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdscal)(size_t N, cl_double alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, DoubleComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, DoubleComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgerc)(clAmdBlasOrder order, size_t M, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgeru)(clAmdBlasOrder order, size_t M, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhemm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhemv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, DoubleComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, DoubleComplex beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZherk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, double alpha, const cl_mem A, size_t offa, size_t lda, double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZrotg)(cl_mem CA, size_t offCA, cl_mem CB, size_t offCB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZscal)(size_t N, cl_double2 alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiCamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiDamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiSamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiZamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp new file mode 100644 index 0000000..1457d7e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp @@ -0,0 +1,142 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP +#error "Invalid usage" +#endif + +// generated by parser_clamdfft.py +#define clAmdFftBakePlan clAmdFftBakePlan_ +#define clAmdFftCopyPlan clAmdFftCopyPlan_ +#define clAmdFftCreateDefaultPlan clAmdFftCreateDefaultPlan_ +#define clAmdFftDestroyPlan clAmdFftDestroyPlan_ +#define clAmdFftEnqueueTransform clAmdFftEnqueueTransform_ +#define clAmdFftGetLayout clAmdFftGetLayout_ +#define clAmdFftGetPlanBatchSize clAmdFftGetPlanBatchSize_ +#define clAmdFftGetPlanContext clAmdFftGetPlanContext_ +#define clAmdFftGetPlanDim clAmdFftGetPlanDim_ +#define clAmdFftGetPlanDistance clAmdFftGetPlanDistance_ +#define clAmdFftGetPlanInStride clAmdFftGetPlanInStride_ +#define clAmdFftGetPlanLength clAmdFftGetPlanLength_ +#define clAmdFftGetPlanOutStride clAmdFftGetPlanOutStride_ +#define clAmdFftGetPlanPrecision clAmdFftGetPlanPrecision_ +#define clAmdFftGetPlanScale clAmdFftGetPlanScale_ +#define clAmdFftGetPlanTransposeResult clAmdFftGetPlanTransposeResult_ +#define clAmdFftGetResultLocation clAmdFftGetResultLocation_ +#define clAmdFftGetTmpBufSize clAmdFftGetTmpBufSize_ +#define clAmdFftGetVersion clAmdFftGetVersion_ +#define clAmdFftSetLayout clAmdFftSetLayout_ +#define clAmdFftSetPlanBatchSize clAmdFftSetPlanBatchSize_ +#define clAmdFftSetPlanDim clAmdFftSetPlanDim_ +#define clAmdFftSetPlanDistance clAmdFftSetPlanDistance_ +#define clAmdFftSetPlanInStride clAmdFftSetPlanInStride_ +#define clAmdFftSetPlanLength clAmdFftSetPlanLength_ +#define clAmdFftSetPlanOutStride clAmdFftSetPlanOutStride_ +#define clAmdFftSetPlanPrecision clAmdFftSetPlanPrecision_ +#define clAmdFftSetPlanScale clAmdFftSetPlanScale_ +#define clAmdFftSetPlanTransposeResult clAmdFftSetPlanTransposeResult_ +#define clAmdFftSetResultLocation clAmdFftSetResultLocation_ +#define clAmdFftSetup clAmdFftSetup_ +#define clAmdFftTeardown clAmdFftTeardown_ + +#include + +// generated by parser_clamdfft.py +#undef clAmdFftBakePlan +#define clAmdFftBakePlan clAmdFftBakePlan_pfn +#undef clAmdFftCopyPlan +//#define clAmdFftCopyPlan clAmdFftCopyPlan_pfn +#undef clAmdFftCreateDefaultPlan +#define clAmdFftCreateDefaultPlan clAmdFftCreateDefaultPlan_pfn +#undef clAmdFftDestroyPlan +#define clAmdFftDestroyPlan clAmdFftDestroyPlan_pfn +#undef clAmdFftEnqueueTransform +#define clAmdFftEnqueueTransform clAmdFftEnqueueTransform_pfn +#undef clAmdFftGetLayout +//#define clAmdFftGetLayout clAmdFftGetLayout_pfn +#undef clAmdFftGetPlanBatchSize +//#define clAmdFftGetPlanBatchSize clAmdFftGetPlanBatchSize_pfn +#undef clAmdFftGetPlanContext +//#define clAmdFftGetPlanContext clAmdFftGetPlanContext_pfn +#undef clAmdFftGetPlanDim +//#define clAmdFftGetPlanDim clAmdFftGetPlanDim_pfn +#undef clAmdFftGetPlanDistance +//#define clAmdFftGetPlanDistance clAmdFftGetPlanDistance_pfn +#undef clAmdFftGetPlanInStride +//#define clAmdFftGetPlanInStride clAmdFftGetPlanInStride_pfn +#undef clAmdFftGetPlanLength +//#define clAmdFftGetPlanLength clAmdFftGetPlanLength_pfn +#undef clAmdFftGetPlanOutStride +//#define clAmdFftGetPlanOutStride clAmdFftGetPlanOutStride_pfn +#undef clAmdFftGetPlanPrecision +//#define clAmdFftGetPlanPrecision clAmdFftGetPlanPrecision_pfn +#undef clAmdFftGetPlanScale +//#define clAmdFftGetPlanScale clAmdFftGetPlanScale_pfn +#undef clAmdFftGetPlanTransposeResult +//#define clAmdFftGetPlanTransposeResult clAmdFftGetPlanTransposeResult_pfn +#undef clAmdFftGetResultLocation +//#define clAmdFftGetResultLocation clAmdFftGetResultLocation_pfn +#undef clAmdFftGetTmpBufSize +#define clAmdFftGetTmpBufSize clAmdFftGetTmpBufSize_pfn +#undef clAmdFftGetVersion +#define clAmdFftGetVersion clAmdFftGetVersion_pfn +#undef clAmdFftSetLayout +#define clAmdFftSetLayout clAmdFftSetLayout_pfn +#undef clAmdFftSetPlanBatchSize +#define clAmdFftSetPlanBatchSize clAmdFftSetPlanBatchSize_pfn +#undef clAmdFftSetPlanDim +//#define clAmdFftSetPlanDim clAmdFftSetPlanDim_pfn +#undef clAmdFftSetPlanDistance +#define clAmdFftSetPlanDistance clAmdFftSetPlanDistance_pfn +#undef clAmdFftSetPlanInStride +#define clAmdFftSetPlanInStride clAmdFftSetPlanInStride_pfn +#undef clAmdFftSetPlanLength +//#define clAmdFftSetPlanLength clAmdFftSetPlanLength_pfn +#undef clAmdFftSetPlanOutStride +#define clAmdFftSetPlanOutStride clAmdFftSetPlanOutStride_pfn +#undef clAmdFftSetPlanPrecision +#define clAmdFftSetPlanPrecision clAmdFftSetPlanPrecision_pfn +#undef clAmdFftSetPlanScale +#define clAmdFftSetPlanScale clAmdFftSetPlanScale_pfn +#undef clAmdFftSetPlanTransposeResult +//#define clAmdFftSetPlanTransposeResult clAmdFftSetPlanTransposeResult_pfn +#undef clAmdFftSetResultLocation +#define clAmdFftSetResultLocation clAmdFftSetResultLocation_pfn +#undef clAmdFftSetup +#define clAmdFftSetup clAmdFftSetup_pfn +#undef clAmdFftTeardown +#define clAmdFftTeardown clAmdFftTeardown_pfn + +// generated by parser_clamdfft.py +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftBakePlan)(clAmdFftPlanHandle plHandle, cl_uint numQueues, cl_command_queue* commQueueFFT, void (CL_CALLBACK* pfn_notify) (clAmdFftPlanHandle plHandle, void* user_data), void* user_data); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftCopyPlan)(clAmdFftPlanHandle* out_plHandle, cl_context new_context, clAmdFftPlanHandle in_plHandle); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftCreateDefaultPlan)(clAmdFftPlanHandle* plHandle, cl_context context, const clAmdFftDim dim, const size_t* clLengths); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftDestroyPlan)(clAmdFftPlanHandle* plHandle); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftEnqueueTransform)(clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_uint numQueuesAndEvents, cl_command_queue* commQueues, cl_uint numWaitEvents, const cl_event* waitEvents, cl_event* outEvents, cl_mem* inputBuffers, cl_mem* outputBuffers, cl_mem tmpBuffer); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetLayout)(const clAmdFftPlanHandle plHandle, clAmdFftLayout* iLayout, clAmdFftLayout* oLayout); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanBatchSize)(const clAmdFftPlanHandle plHandle, size_t* batchSize); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanContext)(const clAmdFftPlanHandle plHandle, cl_context* context); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanDim)(const clAmdFftPlanHandle plHandle, clAmdFftDim* dim, cl_uint* size); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanDistance)(const clAmdFftPlanHandle plHandle, size_t* iDist, size_t* oDist); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanInStride)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanLength)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clLengths); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanOutStride)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanPrecision)(const clAmdFftPlanHandle plHandle, clAmdFftPrecision* precision); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanScale)(const clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float* scale); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanTransposeResult)(const clAmdFftPlanHandle plHandle, clAmdFftResultTransposed* transposed); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetResultLocation)(const clAmdFftPlanHandle plHandle, clAmdFftResultLocation* placeness); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetTmpBufSize)(const clAmdFftPlanHandle plHandle, size_t* buffersize); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetVersion)(cl_uint* major, cl_uint* minor, cl_uint* patch); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetLayout)(clAmdFftPlanHandle plHandle, clAmdFftLayout iLayout, clAmdFftLayout oLayout); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanBatchSize)(clAmdFftPlanHandle plHandle, size_t batchSize); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanDim)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanDistance)(clAmdFftPlanHandle plHandle, size_t iDist, size_t oDist); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanInStride)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanLength)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, const size_t* clLengths); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanOutStride)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanPrecision)(clAmdFftPlanHandle plHandle, clAmdFftPrecision precision); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanScale)(clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float scale); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanTransposeResult)(clAmdFftPlanHandle plHandle, clAmdFftResultTransposed transposed); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetResultLocation)(clAmdFftPlanHandle plHandle, clAmdFftResultLocation placeness); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetup)(const clAmdFftSetupData* setupData); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftTeardown)(); diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp new file mode 100644 index 0000000..28618a1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp @@ -0,0 +1,371 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#define clBuildProgram clBuildProgram_ +#define clCompileProgram clCompileProgram_ +#define clCreateBuffer clCreateBuffer_ +#define clCreateCommandQueue clCreateCommandQueue_ +#define clCreateContext clCreateContext_ +#define clCreateContextFromType clCreateContextFromType_ +#define clCreateImage clCreateImage_ +#define clCreateImage2D clCreateImage2D_ +#define clCreateImage3D clCreateImage3D_ +#define clCreateKernel clCreateKernel_ +#define clCreateKernelsInProgram clCreateKernelsInProgram_ +#define clCreateProgramWithBinary clCreateProgramWithBinary_ +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_ +#define clCreateProgramWithSource clCreateProgramWithSource_ +#define clCreateSampler clCreateSampler_ +#define clCreateSubBuffer clCreateSubBuffer_ +#define clCreateSubDevices clCreateSubDevices_ +#define clCreateUserEvent clCreateUserEvent_ +#define clEnqueueBarrier clEnqueueBarrier_ +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_ +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_ +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_ +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_ +#define clEnqueueCopyImage clEnqueueCopyImage_ +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_ +#define clEnqueueFillBuffer clEnqueueFillBuffer_ +#define clEnqueueFillImage clEnqueueFillImage_ +#define clEnqueueMapBuffer clEnqueueMapBuffer_ +#define clEnqueueMapImage clEnqueueMapImage_ +#define clEnqueueMarker clEnqueueMarker_ +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_ +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_ +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_ +#define clEnqueueNativeKernel clEnqueueNativeKernel_ +#define clEnqueueReadBuffer clEnqueueReadBuffer_ +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_ +#define clEnqueueReadImage clEnqueueReadImage_ +#define clEnqueueTask clEnqueueTask_ +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_ +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_ +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_ +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_ +#define clEnqueueWriteImage clEnqueueWriteImage_ +#define clFinish clFinish_ +#define clFlush clFlush_ +#define clGetCommandQueueInfo clGetCommandQueueInfo_ +#define clGetContextInfo clGetContextInfo_ +#define clGetDeviceIDs clGetDeviceIDs_ +#define clGetDeviceInfo clGetDeviceInfo_ +#define clGetEventInfo clGetEventInfo_ +#define clGetEventProfilingInfo clGetEventProfilingInfo_ +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_ +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_ +#define clGetImageInfo clGetImageInfo_ +#define clGetKernelArgInfo clGetKernelArgInfo_ +#define clGetKernelInfo clGetKernelInfo_ +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_ +#define clGetMemObjectInfo clGetMemObjectInfo_ +#define clGetPlatformIDs clGetPlatformIDs_ +#define clGetPlatformInfo clGetPlatformInfo_ +#define clGetProgramBuildInfo clGetProgramBuildInfo_ +#define clGetProgramInfo clGetProgramInfo_ +#define clGetSamplerInfo clGetSamplerInfo_ +#define clGetSupportedImageFormats clGetSupportedImageFormats_ +#define clLinkProgram clLinkProgram_ +#define clReleaseCommandQueue clReleaseCommandQueue_ +#define clReleaseContext clReleaseContext_ +#define clReleaseDevice clReleaseDevice_ +#define clReleaseEvent clReleaseEvent_ +#define clReleaseKernel clReleaseKernel_ +#define clReleaseMemObject clReleaseMemObject_ +#define clReleaseProgram clReleaseProgram_ +#define clReleaseSampler clReleaseSampler_ +#define clRetainCommandQueue clRetainCommandQueue_ +#define clRetainContext clRetainContext_ +#define clRetainDevice clRetainDevice_ +#define clRetainEvent clRetainEvent_ +#define clRetainKernel clRetainKernel_ +#define clRetainMemObject clRetainMemObject_ +#define clRetainProgram clRetainProgram_ +#define clRetainSampler clRetainSampler_ +#define clSetEventCallback clSetEventCallback_ +#define clSetKernelArg clSetKernelArg_ +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_ +#define clSetUserEventStatus clSetUserEventStatus_ +#define clUnloadCompiler clUnloadCompiler_ +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_ +#define clWaitForEvents clWaitForEvents_ + +#if defined __APPLE__ +#define CL_SILENCE_DEPRECATION +#include +#else +#include +#endif + +// generated by parser_cl.py +#undef clBuildProgram +#define clBuildProgram clBuildProgram_pfn +#undef clCompileProgram +#define clCompileProgram clCompileProgram_pfn +#undef clCreateBuffer +#define clCreateBuffer clCreateBuffer_pfn +#undef clCreateCommandQueue +#define clCreateCommandQueue clCreateCommandQueue_pfn +#undef clCreateContext +#define clCreateContext clCreateContext_pfn +#undef clCreateContextFromType +#define clCreateContextFromType clCreateContextFromType_pfn +#undef clCreateImage +#define clCreateImage clCreateImage_pfn +#undef clCreateImage2D +#define clCreateImage2D clCreateImage2D_pfn +#undef clCreateImage3D +#define clCreateImage3D clCreateImage3D_pfn +#undef clCreateKernel +#define clCreateKernel clCreateKernel_pfn +#undef clCreateKernelsInProgram +#define clCreateKernelsInProgram clCreateKernelsInProgram_pfn +#undef clCreateProgramWithBinary +#define clCreateProgramWithBinary clCreateProgramWithBinary_pfn +#undef clCreateProgramWithBuiltInKernels +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_pfn +#undef clCreateProgramWithSource +#define clCreateProgramWithSource clCreateProgramWithSource_pfn +#undef clCreateSampler +#define clCreateSampler clCreateSampler_pfn +#undef clCreateSubBuffer +#define clCreateSubBuffer clCreateSubBuffer_pfn +#undef clCreateSubDevices +#define clCreateSubDevices clCreateSubDevices_pfn +#undef clCreateUserEvent +#define clCreateUserEvent clCreateUserEvent_pfn +#undef clEnqueueBarrier +#define clEnqueueBarrier clEnqueueBarrier_pfn +#undef clEnqueueBarrierWithWaitList +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_pfn +#undef clEnqueueCopyBuffer +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_pfn +#undef clEnqueueCopyBufferRect +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_pfn +#undef clEnqueueCopyBufferToImage +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_pfn +#undef clEnqueueCopyImage +#define clEnqueueCopyImage clEnqueueCopyImage_pfn +#undef clEnqueueCopyImageToBuffer +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_pfn +#undef clEnqueueFillBuffer +#define clEnqueueFillBuffer clEnqueueFillBuffer_pfn +#undef clEnqueueFillImage +#define clEnqueueFillImage clEnqueueFillImage_pfn +#undef clEnqueueMapBuffer +#define clEnqueueMapBuffer clEnqueueMapBuffer_pfn +#undef clEnqueueMapImage +#define clEnqueueMapImage clEnqueueMapImage_pfn +#undef clEnqueueMarker +#define clEnqueueMarker clEnqueueMarker_pfn +#undef clEnqueueMarkerWithWaitList +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_pfn +#undef clEnqueueMigrateMemObjects +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_pfn +#undef clEnqueueNDRangeKernel +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_pfn +#undef clEnqueueNativeKernel +#define clEnqueueNativeKernel clEnqueueNativeKernel_pfn +#undef clEnqueueReadBuffer +#define clEnqueueReadBuffer clEnqueueReadBuffer_pfn +#undef clEnqueueReadBufferRect +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_pfn +#undef clEnqueueReadImage +#define clEnqueueReadImage clEnqueueReadImage_pfn +#undef clEnqueueTask +#define clEnqueueTask clEnqueueTask_pfn +#undef clEnqueueUnmapMemObject +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_pfn +#undef clEnqueueWaitForEvents +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_pfn +#undef clEnqueueWriteBuffer +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_pfn +#undef clEnqueueWriteBufferRect +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_pfn +#undef clEnqueueWriteImage +#define clEnqueueWriteImage clEnqueueWriteImage_pfn +#undef clFinish +#define clFinish clFinish_pfn +#undef clFlush +#define clFlush clFlush_pfn +#undef clGetCommandQueueInfo +#define clGetCommandQueueInfo clGetCommandQueueInfo_pfn +#undef clGetContextInfo +#define clGetContextInfo clGetContextInfo_pfn +#undef clGetDeviceIDs +#define clGetDeviceIDs clGetDeviceIDs_pfn +#undef clGetDeviceInfo +#define clGetDeviceInfo clGetDeviceInfo_pfn +#undef clGetEventInfo +#define clGetEventInfo clGetEventInfo_pfn +#undef clGetEventProfilingInfo +#define clGetEventProfilingInfo clGetEventProfilingInfo_pfn +#undef clGetExtensionFunctionAddress +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_pfn +#undef clGetExtensionFunctionAddressForPlatform +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_pfn +#undef clGetImageInfo +#define clGetImageInfo clGetImageInfo_pfn +#undef clGetKernelArgInfo +#define clGetKernelArgInfo clGetKernelArgInfo_pfn +#undef clGetKernelInfo +#define clGetKernelInfo clGetKernelInfo_pfn +#undef clGetKernelWorkGroupInfo +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_pfn +#undef clGetMemObjectInfo +#define clGetMemObjectInfo clGetMemObjectInfo_pfn +#undef clGetPlatformIDs +#define clGetPlatformIDs clGetPlatformIDs_pfn +#undef clGetPlatformInfo +#define clGetPlatformInfo clGetPlatformInfo_pfn +#undef clGetProgramBuildInfo +#define clGetProgramBuildInfo clGetProgramBuildInfo_pfn +#undef clGetProgramInfo +#define clGetProgramInfo clGetProgramInfo_pfn +#undef clGetSamplerInfo +#define clGetSamplerInfo clGetSamplerInfo_pfn +#undef clGetSupportedImageFormats +#define clGetSupportedImageFormats clGetSupportedImageFormats_pfn +#undef clLinkProgram +#define clLinkProgram clLinkProgram_pfn +#undef clReleaseCommandQueue +#define clReleaseCommandQueue clReleaseCommandQueue_pfn +#undef clReleaseContext +#define clReleaseContext clReleaseContext_pfn +#undef clReleaseDevice +#define clReleaseDevice clReleaseDevice_pfn +#undef clReleaseEvent +#define clReleaseEvent clReleaseEvent_pfn +#undef clReleaseKernel +#define clReleaseKernel clReleaseKernel_pfn +#undef clReleaseMemObject +#define clReleaseMemObject clReleaseMemObject_pfn +#undef clReleaseProgram +#define clReleaseProgram clReleaseProgram_pfn +#undef clReleaseSampler +#define clReleaseSampler clReleaseSampler_pfn +#undef clRetainCommandQueue +#define clRetainCommandQueue clRetainCommandQueue_pfn +#undef clRetainContext +#define clRetainContext clRetainContext_pfn +#undef clRetainDevice +#define clRetainDevice clRetainDevice_pfn +#undef clRetainEvent +#define clRetainEvent clRetainEvent_pfn +#undef clRetainKernel +#define clRetainKernel clRetainKernel_pfn +#undef clRetainMemObject +#define clRetainMemObject clRetainMemObject_pfn +#undef clRetainProgram +#define clRetainProgram clRetainProgram_pfn +#undef clRetainSampler +#define clRetainSampler clRetainSampler_pfn +#undef clSetEventCallback +#define clSetEventCallback clSetEventCallback_pfn +#undef clSetKernelArg +#define clSetKernelArg clSetKernelArg_pfn +#undef clSetMemObjectDestructorCallback +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_pfn +#undef clSetUserEventStatus +#define clSetUserEventStatus clSetUserEventStatus_pfn +#undef clUnloadCompiler +#define clUnloadCompiler clUnloadCompiler_pfn +#undef clUnloadPlatformCompiler +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_pfn +#undef clWaitForEvents +#define clWaitForEvents clWaitForEvents_pfn + +// generated by parser_cl.py +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clBuildProgram)(cl_program, cl_uint, const cl_device_id*, const char*, void (CL_CALLBACK*) (cl_program, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCompileProgram)(cl_program, cl_uint, const cl_device_id*, const char*, cl_uint, const cl_program*, const char**, void (CL_CALLBACK*) (cl_program, void*), void*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateBuffer)(cl_context, cl_mem_flags, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_command_queue (CL_API_CALL*clCreateCommandQueue)(cl_context, cl_device_id, cl_command_queue_properties, cl_int*); +extern CL_RUNTIME_EXPORT cl_context (CL_API_CALL*clCreateContext)(const cl_context_properties*, cl_uint, const cl_device_id*, void (CL_CALLBACK*) (const char*, const void*, size_t, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_context (CL_API_CALL*clCreateContextFromType)(const cl_context_properties*, cl_device_type, void (CL_CALLBACK*) (const char*, const void*, size_t, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage)(cl_context, cl_mem_flags, const cl_image_format*, const cl_image_desc*, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage2D)(cl_context, cl_mem_flags, const cl_image_format*, size_t, size_t, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage3D)(cl_context, cl_mem_flags, const cl_image_format*, size_t, size_t, size_t, size_t, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_kernel (CL_API_CALL*clCreateKernel)(cl_program, const char*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCreateKernelsInProgram)(cl_program, cl_uint, cl_kernel*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithBinary)(cl_context, cl_uint, const cl_device_id*, const size_t*, const unsigned char**, cl_int*, cl_int*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithBuiltInKernels)(cl_context, cl_uint, const cl_device_id*, const char*, cl_int*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithSource)(cl_context, cl_uint, const char**, const size_t*, cl_int*); +extern CL_RUNTIME_EXPORT cl_sampler (CL_API_CALL*clCreateSampler)(cl_context, cl_bool, cl_addressing_mode, cl_filter_mode, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateSubBuffer)(cl_mem, cl_mem_flags, cl_buffer_create_type, const void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCreateSubDevices)(cl_device_id, const cl_device_partition_property*, cl_uint, cl_device_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_event (CL_API_CALL*clCreateUserEvent)(cl_context, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueBarrier)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueBarrierWithWaitList)(cl_command_queue, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBuffer)(cl_command_queue, cl_mem, cl_mem, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBufferRect)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBufferToImage)(cl_command_queue, cl_mem, cl_mem, size_t, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyImage)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyImageToBuffer)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueFillBuffer)(cl_command_queue, cl_mem, const void*, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueFillImage)(cl_command_queue, cl_mem, const void*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clEnqueueMapBuffer)(cl_command_queue, cl_mem, cl_bool, cl_map_flags, size_t, size_t, cl_uint, const cl_event*, cl_event*, cl_int*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clEnqueueMapImage)(cl_command_queue, cl_mem, cl_bool, cl_map_flags, const size_t*, const size_t*, size_t*, size_t*, cl_uint, const cl_event*, cl_event*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMarker)(cl_command_queue, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMarkerWithWaitList)(cl_command_queue, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMigrateMemObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_mem_migration_flags, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueNDRangeKernel)(cl_command_queue, cl_kernel, cl_uint, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueNativeKernel)(cl_command_queue, void (CL_CALLBACK*) (void*), void*, size_t, cl_uint, const cl_mem*, const void**, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadBufferRect)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadImage)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueTask)(cl_command_queue, cl_kernel, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueUnmapMemObject)(cl_command_queue, cl_mem, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWaitForEvents)(cl_command_queue, cl_uint, const cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteBufferRect)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteImage)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clFinish)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clFlush)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetCommandQueueInfo)(cl_command_queue, cl_command_queue_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetContextInfo)(cl_context, cl_context_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetDeviceIDs)(cl_platform_id, cl_device_type, cl_uint, cl_device_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetDeviceInfo)(cl_device_id, cl_device_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetEventInfo)(cl_event, cl_event_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetEventProfilingInfo)(cl_event, cl_profiling_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clGetExtensionFunctionAddress)(const char*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clGetExtensionFunctionAddressForPlatform)(cl_platform_id, const char*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetImageInfo)(cl_mem, cl_image_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelArgInfo)(cl_kernel, cl_uint, cl_kernel_arg_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelInfo)(cl_kernel, cl_kernel_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelWorkGroupInfo)(cl_kernel, cl_device_id, cl_kernel_work_group_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetMemObjectInfo)(cl_mem, cl_mem_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetPlatformInfo)(cl_platform_id, cl_platform_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetProgramBuildInfo)(cl_program, cl_device_id, cl_program_build_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetProgramInfo)(cl_program, cl_program_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetSamplerInfo)(cl_sampler, cl_sampler_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetSupportedImageFormats)(cl_context, cl_mem_flags, cl_mem_object_type, cl_uint, cl_image_format*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clLinkProgram)(cl_context, cl_uint, const cl_device_id*, const char*, cl_uint, const cl_program*, void (CL_CALLBACK*) (cl_program, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseCommandQueue)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseContext)(cl_context); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseDevice)(cl_device_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseEvent)(cl_event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseKernel)(cl_kernel); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseMemObject)(cl_mem); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseProgram)(cl_program); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseSampler)(cl_sampler); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainCommandQueue)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainContext)(cl_context); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainDevice)(cl_device_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainEvent)(cl_event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainKernel)(cl_kernel); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainMemObject)(cl_mem); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainProgram)(cl_program); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainSampler)(cl_sampler); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetEventCallback)(cl_event, cl_int, void (CL_CALLBACK*) (cl_event, cl_int, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetKernelArg)(cl_kernel, cl_uint, size_t, const void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetMemObjectDestructorCallback)(cl_mem, void (CL_CALLBACK*) (cl_mem, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetUserEventStatus)(cl_event, cl_int); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clUnloadCompiler)(); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clUnloadPlatformCompiler)(cl_platform_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clWaitForEvents)(cl_uint, const cl_event*); diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp new file mode 100644 index 0000000..216b22b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp @@ -0,0 +1,272 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#undef clBuildProgram +#define clBuildProgram clBuildProgram_fn +inline cl_int clBuildProgram(cl_program p0, cl_uint p1, const cl_device_id* p2, const char* p3, void (CL_CALLBACK*p4) (cl_program, void*), void* p5) { return clBuildProgram_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCompileProgram +#define clCompileProgram clCompileProgram_fn +inline cl_int clCompileProgram(cl_program p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_uint p4, const cl_program* p5, const char** p6, void (CL_CALLBACK*p7) (cl_program, void*), void* p8) { return clCompileProgram_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clCreateBuffer +#define clCreateBuffer clCreateBuffer_fn +inline cl_mem clCreateBuffer(cl_context p0, cl_mem_flags p1, size_t p2, void* p3, cl_int* p4) { return clCreateBuffer_pfn(p0, p1, p2, p3, p4); } +#undef clCreateCommandQueue +#define clCreateCommandQueue clCreateCommandQueue_fn +inline cl_command_queue clCreateCommandQueue(cl_context p0, cl_device_id p1, cl_command_queue_properties p2, cl_int* p3) { return clCreateCommandQueue_pfn(p0, p1, p2, p3); } +#undef clCreateContext +#define clCreateContext clCreateContext_fn +inline cl_context clCreateContext(const cl_context_properties* p0, cl_uint p1, const cl_device_id* p2, void (CL_CALLBACK*p3) (const char*, const void*, size_t, void*), void* p4, cl_int* p5) { return clCreateContext_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateContextFromType +#define clCreateContextFromType clCreateContextFromType_fn +inline cl_context clCreateContextFromType(const cl_context_properties* p0, cl_device_type p1, void (CL_CALLBACK*p2) (const char*, const void*, size_t, void*), void* p3, cl_int* p4) { return clCreateContextFromType_pfn(p0, p1, p2, p3, p4); } +#undef clCreateImage +#define clCreateImage clCreateImage_fn +inline cl_mem clCreateImage(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, const cl_image_desc* p3, void* p4, cl_int* p5) { return clCreateImage_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateImage2D +#define clCreateImage2D clCreateImage2D_fn +inline cl_mem clCreateImage2D(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, size_t p3, size_t p4, size_t p5, void* p6, cl_int* p7) { return clCreateImage2D_pfn(p0, p1, p2, p3, p4, p5, p6, p7); } +#undef clCreateImage3D +#define clCreateImage3D clCreateImage3D_fn +inline cl_mem clCreateImage3D(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, size_t p3, size_t p4, size_t p5, size_t p6, size_t p7, void* p8, cl_int* p9) { return clCreateImage3D_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clCreateKernel +#define clCreateKernel clCreateKernel_fn +inline cl_kernel clCreateKernel(cl_program p0, const char* p1, cl_int* p2) { return clCreateKernel_pfn(p0, p1, p2); } +#undef clCreateKernelsInProgram +#define clCreateKernelsInProgram clCreateKernelsInProgram_fn +inline cl_int clCreateKernelsInProgram(cl_program p0, cl_uint p1, cl_kernel* p2, cl_uint* p3) { return clCreateKernelsInProgram_pfn(p0, p1, p2, p3); } +#undef clCreateProgramWithBinary +#define clCreateProgramWithBinary clCreateProgramWithBinary_fn +inline cl_program clCreateProgramWithBinary(cl_context p0, cl_uint p1, const cl_device_id* p2, const size_t* p3, const unsigned char** p4, cl_int* p5, cl_int* p6) { return clCreateProgramWithBinary_pfn(p0, p1, p2, p3, p4, p5, p6); } +#undef clCreateProgramWithBuiltInKernels +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_fn +inline cl_program clCreateProgramWithBuiltInKernels(cl_context p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_int* p4) { return clCreateProgramWithBuiltInKernels_pfn(p0, p1, p2, p3, p4); } +#undef clCreateProgramWithSource +#define clCreateProgramWithSource clCreateProgramWithSource_fn +inline cl_program clCreateProgramWithSource(cl_context p0, cl_uint p1, const char** p2, const size_t* p3, cl_int* p4) { return clCreateProgramWithSource_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSampler +#define clCreateSampler clCreateSampler_fn +inline cl_sampler clCreateSampler(cl_context p0, cl_bool p1, cl_addressing_mode p2, cl_filter_mode p3, cl_int* p4) { return clCreateSampler_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSubBuffer +#define clCreateSubBuffer clCreateSubBuffer_fn +inline cl_mem clCreateSubBuffer(cl_mem p0, cl_mem_flags p1, cl_buffer_create_type p2, const void* p3, cl_int* p4) { return clCreateSubBuffer_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSubDevices +#define clCreateSubDevices clCreateSubDevices_fn +inline cl_int clCreateSubDevices(cl_device_id p0, const cl_device_partition_property* p1, cl_uint p2, cl_device_id* p3, cl_uint* p4) { return clCreateSubDevices_pfn(p0, p1, p2, p3, p4); } +#undef clCreateUserEvent +#define clCreateUserEvent clCreateUserEvent_fn +inline cl_event clCreateUserEvent(cl_context p0, cl_int* p1) { return clCreateUserEvent_pfn(p0, p1); } +#undef clEnqueueBarrier +#define clEnqueueBarrier clEnqueueBarrier_fn +inline cl_int clEnqueueBarrier(cl_command_queue p0) { return clEnqueueBarrier_pfn(p0); } +#undef clEnqueueBarrierWithWaitList +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_fn +inline cl_int clEnqueueBarrierWithWaitList(cl_command_queue p0, cl_uint p1, const cl_event* p2, cl_event* p3) { return clEnqueueBarrierWithWaitList_pfn(p0, p1, p2, p3); } +#undef clEnqueueCopyBuffer +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_fn +inline cl_int clEnqueueCopyBuffer(cl_command_queue p0, cl_mem p1, cl_mem p2, size_t p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyBufferRect +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_fn +inline cl_int clEnqueueCopyBufferRect(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, cl_uint p10, const cl_event* p11, cl_event* p12) { return clEnqueueCopyBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); } +#undef clEnqueueCopyBufferToImage +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_fn +inline cl_int clEnqueueCopyBufferToImage(cl_command_queue p0, cl_mem p1, cl_mem p2, size_t p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyBufferToImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyImage +#define clEnqueueCopyImage clEnqueueCopyImage_fn +inline cl_int clEnqueueCopyImage(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyImageToBuffer +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_fn +inline cl_int clEnqueueCopyImageToBuffer(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyImageToBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueFillBuffer +#define clEnqueueFillBuffer clEnqueueFillBuffer_fn +inline cl_int clEnqueueFillBuffer(cl_command_queue p0, cl_mem p1, const void* p2, size_t p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueFillBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueFillImage +#define clEnqueueFillImage clEnqueueFillImage_fn +inline cl_int clEnqueueFillImage(cl_command_queue p0, cl_mem p1, const void* p2, const size_t* p3, const size_t* p4, cl_uint p5, const cl_event* p6, cl_event* p7) { return clEnqueueFillImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7); } +#undef clEnqueueMapBuffer +#define clEnqueueMapBuffer clEnqueueMapBuffer_fn +inline void* clEnqueueMapBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, cl_map_flags p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8, cl_int* p9) { return clEnqueueMapBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clEnqueueMapImage +#define clEnqueueMapImage clEnqueueMapImage_fn +inline void* clEnqueueMapImage(cl_command_queue p0, cl_mem p1, cl_bool p2, cl_map_flags p3, const size_t* p4, const size_t* p5, size_t* p6, size_t* p7, cl_uint p8, const cl_event* p9, cl_event* p10, cl_int* p11) { return clEnqueueMapImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); } +#undef clEnqueueMarker +#define clEnqueueMarker clEnqueueMarker_fn +inline cl_int clEnqueueMarker(cl_command_queue p0, cl_event* p1) { return clEnqueueMarker_pfn(p0, p1); } +#undef clEnqueueMarkerWithWaitList +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_fn +inline cl_int clEnqueueMarkerWithWaitList(cl_command_queue p0, cl_uint p1, const cl_event* p2, cl_event* p3) { return clEnqueueMarkerWithWaitList_pfn(p0, p1, p2, p3); } +#undef clEnqueueMigrateMemObjects +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_fn +inline cl_int clEnqueueMigrateMemObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_mem_migration_flags p3, cl_uint p4, const cl_event* p5, cl_event* p6) { return clEnqueueMigrateMemObjects_pfn(p0, p1, p2, p3, p4, p5, p6); } +#undef clEnqueueNDRangeKernel +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_fn +inline cl_int clEnqueueNDRangeKernel(cl_command_queue p0, cl_kernel p1, cl_uint p2, const size_t* p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueNDRangeKernel_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueNativeKernel +#define clEnqueueNativeKernel clEnqueueNativeKernel_fn +inline cl_int clEnqueueNativeKernel(cl_command_queue p0, void (CL_CALLBACK*p1) (void*), void* p2, size_t p3, cl_uint p4, const cl_mem* p5, const void** p6, cl_uint p7, const cl_event* p8, cl_event* p9) { return clEnqueueNativeKernel_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clEnqueueReadBuffer +#define clEnqueueReadBuffer clEnqueueReadBuffer_fn +inline cl_int clEnqueueReadBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, size_t p3, size_t p4, void* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueReadBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueReadBufferRect +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_fn +inline cl_int clEnqueueReadBufferRect(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, void* p10, cl_uint p11, const cl_event* p12, cl_event* p13) { return clEnqueueReadBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); } +#undef clEnqueueReadImage +#define clEnqueueReadImage clEnqueueReadImage_fn +inline cl_int clEnqueueReadImage(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, size_t p5, size_t p6, void* p7, cl_uint p8, const cl_event* p9, cl_event* p10) { return clEnqueueReadImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } +#undef clEnqueueTask +#define clEnqueueTask clEnqueueTask_fn +inline cl_int clEnqueueTask(cl_command_queue p0, cl_kernel p1, cl_uint p2, const cl_event* p3, cl_event* p4) { return clEnqueueTask_pfn(p0, p1, p2, p3, p4); } +#undef clEnqueueUnmapMemObject +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_fn +inline cl_int clEnqueueUnmapMemObject(cl_command_queue p0, cl_mem p1, void* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueUnmapMemObject_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueWaitForEvents +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_fn +inline cl_int clEnqueueWaitForEvents(cl_command_queue p0, cl_uint p1, const cl_event* p2) { return clEnqueueWaitForEvents_pfn(p0, p1, p2); } +#undef clEnqueueWriteBuffer +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_fn +inline cl_int clEnqueueWriteBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, size_t p3, size_t p4, const void* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueWriteBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueWriteBufferRect +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_fn +inline cl_int clEnqueueWriteBufferRect(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, const void* p10, cl_uint p11, const cl_event* p12, cl_event* p13) { return clEnqueueWriteBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); } +#undef clEnqueueWriteImage +#define clEnqueueWriteImage clEnqueueWriteImage_fn +inline cl_int clEnqueueWriteImage(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, size_t p5, size_t p6, const void* p7, cl_uint p8, const cl_event* p9, cl_event* p10) { return clEnqueueWriteImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } +#undef clFinish +#define clFinish clFinish_fn +inline cl_int clFinish(cl_command_queue p0) { return clFinish_pfn(p0); } +#undef clFlush +#define clFlush clFlush_fn +inline cl_int clFlush(cl_command_queue p0) { return clFlush_pfn(p0); } +#undef clGetCommandQueueInfo +#define clGetCommandQueueInfo clGetCommandQueueInfo_fn +inline cl_int clGetCommandQueueInfo(cl_command_queue p0, cl_command_queue_info p1, size_t p2, void* p3, size_t* p4) { return clGetCommandQueueInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetContextInfo +#define clGetContextInfo clGetContextInfo_fn +inline cl_int clGetContextInfo(cl_context p0, cl_context_info p1, size_t p2, void* p3, size_t* p4) { return clGetContextInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetDeviceIDs +#define clGetDeviceIDs clGetDeviceIDs_fn +inline cl_int clGetDeviceIDs(cl_platform_id p0, cl_device_type p1, cl_uint p2, cl_device_id* p3, cl_uint* p4) { return clGetDeviceIDs_pfn(p0, p1, p2, p3, p4); } +#undef clGetDeviceInfo +#define clGetDeviceInfo clGetDeviceInfo_fn +inline cl_int clGetDeviceInfo(cl_device_id p0, cl_device_info p1, size_t p2, void* p3, size_t* p4) { return clGetDeviceInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetEventInfo +#define clGetEventInfo clGetEventInfo_fn +inline cl_int clGetEventInfo(cl_event p0, cl_event_info p1, size_t p2, void* p3, size_t* p4) { return clGetEventInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetEventProfilingInfo +#define clGetEventProfilingInfo clGetEventProfilingInfo_fn +inline cl_int clGetEventProfilingInfo(cl_event p0, cl_profiling_info p1, size_t p2, void* p3, size_t* p4) { return clGetEventProfilingInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetExtensionFunctionAddress +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_fn +inline void* clGetExtensionFunctionAddress(const char* p0) { return clGetExtensionFunctionAddress_pfn(p0); } +#undef clGetExtensionFunctionAddressForPlatform +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_fn +inline void* clGetExtensionFunctionAddressForPlatform(cl_platform_id p0, const char* p1) { return clGetExtensionFunctionAddressForPlatform_pfn(p0, p1); } +#undef clGetImageInfo +#define clGetImageInfo clGetImageInfo_fn +inline cl_int clGetImageInfo(cl_mem p0, cl_image_info p1, size_t p2, void* p3, size_t* p4) { return clGetImageInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetKernelArgInfo +#define clGetKernelArgInfo clGetKernelArgInfo_fn +inline cl_int clGetKernelArgInfo(cl_kernel p0, cl_uint p1, cl_kernel_arg_info p2, size_t p3, void* p4, size_t* p5) { return clGetKernelArgInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetKernelInfo +#define clGetKernelInfo clGetKernelInfo_fn +inline cl_int clGetKernelInfo(cl_kernel p0, cl_kernel_info p1, size_t p2, void* p3, size_t* p4) { return clGetKernelInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetKernelWorkGroupInfo +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_fn +inline cl_int clGetKernelWorkGroupInfo(cl_kernel p0, cl_device_id p1, cl_kernel_work_group_info p2, size_t p3, void* p4, size_t* p5) { return clGetKernelWorkGroupInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetMemObjectInfo +#define clGetMemObjectInfo clGetMemObjectInfo_fn +inline cl_int clGetMemObjectInfo(cl_mem p0, cl_mem_info p1, size_t p2, void* p3, size_t* p4) { return clGetMemObjectInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetPlatformIDs +#define clGetPlatformIDs clGetPlatformIDs_fn +inline cl_int clGetPlatformIDs(cl_uint p0, cl_platform_id* p1, cl_uint* p2) { return clGetPlatformIDs_pfn(p0, p1, p2); } +#undef clGetPlatformInfo +#define clGetPlatformInfo clGetPlatformInfo_fn +inline cl_int clGetPlatformInfo(cl_platform_id p0, cl_platform_info p1, size_t p2, void* p3, size_t* p4) { return clGetPlatformInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetProgramBuildInfo +#define clGetProgramBuildInfo clGetProgramBuildInfo_fn +inline cl_int clGetProgramBuildInfo(cl_program p0, cl_device_id p1, cl_program_build_info p2, size_t p3, void* p4, size_t* p5) { return clGetProgramBuildInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetProgramInfo +#define clGetProgramInfo clGetProgramInfo_fn +inline cl_int clGetProgramInfo(cl_program p0, cl_program_info p1, size_t p2, void* p3, size_t* p4) { return clGetProgramInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetSamplerInfo +#define clGetSamplerInfo clGetSamplerInfo_fn +inline cl_int clGetSamplerInfo(cl_sampler p0, cl_sampler_info p1, size_t p2, void* p3, size_t* p4) { return clGetSamplerInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetSupportedImageFormats +#define clGetSupportedImageFormats clGetSupportedImageFormats_fn +inline cl_int clGetSupportedImageFormats(cl_context p0, cl_mem_flags p1, cl_mem_object_type p2, cl_uint p3, cl_image_format* p4, cl_uint* p5) { return clGetSupportedImageFormats_pfn(p0, p1, p2, p3, p4, p5); } +#undef clLinkProgram +#define clLinkProgram clLinkProgram_fn +inline cl_program clLinkProgram(cl_context p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_uint p4, const cl_program* p5, void (CL_CALLBACK*p6) (cl_program, void*), void* p7, cl_int* p8) { return clLinkProgram_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clReleaseCommandQueue +#define clReleaseCommandQueue clReleaseCommandQueue_fn +inline cl_int clReleaseCommandQueue(cl_command_queue p0) { return clReleaseCommandQueue_pfn(p0); } +#undef clReleaseContext +#define clReleaseContext clReleaseContext_fn +inline cl_int clReleaseContext(cl_context p0) { return clReleaseContext_pfn(p0); } +#undef clReleaseDevice +#define clReleaseDevice clReleaseDevice_fn +inline cl_int clReleaseDevice(cl_device_id p0) { return clReleaseDevice_pfn(p0); } +#undef clReleaseEvent +#define clReleaseEvent clReleaseEvent_fn +inline cl_int clReleaseEvent(cl_event p0) { return clReleaseEvent_pfn(p0); } +#undef clReleaseKernel +#define clReleaseKernel clReleaseKernel_fn +inline cl_int clReleaseKernel(cl_kernel p0) { return clReleaseKernel_pfn(p0); } +#undef clReleaseMemObject +#define clReleaseMemObject clReleaseMemObject_fn +inline cl_int clReleaseMemObject(cl_mem p0) { return clReleaseMemObject_pfn(p0); } +#undef clReleaseProgram +#define clReleaseProgram clReleaseProgram_fn +inline cl_int clReleaseProgram(cl_program p0) { return clReleaseProgram_pfn(p0); } +#undef clReleaseSampler +#define clReleaseSampler clReleaseSampler_fn +inline cl_int clReleaseSampler(cl_sampler p0) { return clReleaseSampler_pfn(p0); } +#undef clRetainCommandQueue +#define clRetainCommandQueue clRetainCommandQueue_fn +inline cl_int clRetainCommandQueue(cl_command_queue p0) { return clRetainCommandQueue_pfn(p0); } +#undef clRetainContext +#define clRetainContext clRetainContext_fn +inline cl_int clRetainContext(cl_context p0) { return clRetainContext_pfn(p0); } +#undef clRetainDevice +#define clRetainDevice clRetainDevice_fn +inline cl_int clRetainDevice(cl_device_id p0) { return clRetainDevice_pfn(p0); } +#undef clRetainEvent +#define clRetainEvent clRetainEvent_fn +inline cl_int clRetainEvent(cl_event p0) { return clRetainEvent_pfn(p0); } +#undef clRetainKernel +#define clRetainKernel clRetainKernel_fn +inline cl_int clRetainKernel(cl_kernel p0) { return clRetainKernel_pfn(p0); } +#undef clRetainMemObject +#define clRetainMemObject clRetainMemObject_fn +inline cl_int clRetainMemObject(cl_mem p0) { return clRetainMemObject_pfn(p0); } +#undef clRetainProgram +#define clRetainProgram clRetainProgram_fn +inline cl_int clRetainProgram(cl_program p0) { return clRetainProgram_pfn(p0); } +#undef clRetainSampler +#define clRetainSampler clRetainSampler_fn +inline cl_int clRetainSampler(cl_sampler p0) { return clRetainSampler_pfn(p0); } +#undef clSetEventCallback +#define clSetEventCallback clSetEventCallback_fn +inline cl_int clSetEventCallback(cl_event p0, cl_int p1, void (CL_CALLBACK*p2) (cl_event, cl_int, void*), void* p3) { return clSetEventCallback_pfn(p0, p1, p2, p3); } +#undef clSetKernelArg +#define clSetKernelArg clSetKernelArg_fn +inline cl_int clSetKernelArg(cl_kernel p0, cl_uint p1, size_t p2, const void* p3) { return clSetKernelArg_pfn(p0, p1, p2, p3); } +#undef clSetMemObjectDestructorCallback +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_fn +inline cl_int clSetMemObjectDestructorCallback(cl_mem p0, void (CL_CALLBACK*p1) (cl_mem, void*), void* p2) { return clSetMemObjectDestructorCallback_pfn(p0, p1, p2); } +#undef clSetUserEventStatus +#define clSetUserEventStatus clSetUserEventStatus_fn +inline cl_int clSetUserEventStatus(cl_event p0, cl_int p1) { return clSetUserEventStatus_pfn(p0, p1); } +#undef clUnloadCompiler +#define clUnloadCompiler clUnloadCompiler_fn +inline cl_int clUnloadCompiler() { return clUnloadCompiler_pfn(); } +#undef clUnloadPlatformCompiler +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_fn +inline cl_int clUnloadPlatformCompiler(cl_platform_id p0) { return clUnloadPlatformCompiler_pfn(p0); } +#undef clWaitForEvents +#define clWaitForEvents clWaitForEvents_fn +inline cl_int clWaitForEvents(cl_uint p0, const cl_event* p1) { return clWaitForEvents_pfn(p0, p1); } diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp new file mode 100644 index 0000000..0b12aed --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp @@ -0,0 +1,62 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#define clCreateFromGLBuffer clCreateFromGLBuffer_ +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_ +#define clCreateFromGLTexture clCreateFromGLTexture_ +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_ +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_ +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_ +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_ +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_ +#define clGetGLObjectInfo clGetGLObjectInfo_ +#define clGetGLTextureInfo clGetGLTextureInfo_ + +#if defined __APPLE__ +#include +#else +#include +#endif + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_pfn +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_pfn +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_pfn +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_pfn +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_pfn +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_pfn +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_pfn +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_pfn +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_pfn +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_pfn + +#ifdef cl_khr_gl_sharing + +// generated by parser_cl.py +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLBuffer)(cl_context, cl_mem_flags, cl_GLuint, int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLRenderbuffer)(cl_context, cl_mem_flags, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture2D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture3D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueAcquireGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReleaseGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLContextInfoKHR)(const cl_context_properties*, cl_gl_context_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLObjectInfo)(cl_mem, cl_gl_object_type*, cl_GLuint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLTextureInfo)(cl_mem, cl_gl_texture_info, size_t, void*, size_t*); + +#endif // cl_khr_gl_sharing diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp new file mode 100644 index 0000000..12f342b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp @@ -0,0 +1,42 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP +#error "Invalid usage" +#endif + +#ifdef cl_khr_gl_sharing + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_fn +inline cl_mem clCreateFromGLBuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, int* p3) { return clCreateFromGLBuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_fn +inline cl_mem clCreateFromGLRenderbuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, cl_int* p3) { return clCreateFromGLRenderbuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_fn +inline cl_mem clCreateFromGLTexture(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_fn +inline cl_mem clCreateFromGLTexture2D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture2D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_fn +inline cl_mem clCreateFromGLTexture3D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture3D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_fn +inline cl_int clEnqueueAcquireGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueAcquireGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_fn +inline cl_int clEnqueueReleaseGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueReleaseGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_fn +inline cl_int clGetGLContextInfoKHR(const cl_context_properties* p0, cl_gl_context_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLContextInfoKHR_pfn(p0, p1, p2, p3, p4); } +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_fn +inline cl_int clGetGLObjectInfo(cl_mem p0, cl_gl_object_type* p1, cl_GLuint* p2) { return clGetGLObjectInfo_pfn(p0, p1, p2); } +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_fn +inline cl_int clGetGLTextureInfo(cl_mem p0, cl_gl_texture_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLTextureInfo_pfn(p0, p1, p2, p3, p4); } + +#endif // cl_khr_gl_sharing diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdblas.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdblas.hpp new file mode 100644 index 0000000..2ad8ac0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdblas.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP +#define OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP + +#ifdef HAVE_CLAMDBLAS + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_clamdblas.hpp" + +#endif // HAVE_CLAMDBLAS + +#endif // OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdfft.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdfft.hpp new file mode 100644 index 0000000..a328f72 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_clamdfft.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP +#define OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP + +#ifdef HAVE_CLAMDFFT + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_clamdfft.hpp" + +#endif // HAVE_CLAMDFFT + +#endif // OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core.hpp new file mode 100644 index 0000000..0404b31 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core.hpp @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP + +#ifdef HAVE_OPENCL + +#ifndef CL_RUNTIME_EXPORT +#if (defined(BUILD_SHARED_LIBS) || defined(OPENCV_CORE_SHARED)) && (defined _WIN32 || defined WINCE) && \ + !(defined(__OPENCV_BUILD) && defined(OPENCV_MODULE_IS_PART_OF_WORLD)) +#define CL_RUNTIME_EXPORT __declspec(dllimport) +#else +#define CL_RUNTIME_EXPORT +#endif +#endif + +#ifdef HAVE_OPENCL_SVM +#define clSVMAlloc clSVMAlloc_ +#define clSVMFree clSVMFree_ +#define clSetKernelArgSVMPointer clSetKernelArgSVMPointer_ +#define clSetKernelExecInfo clSetKernelExecInfo_ +#define clEnqueueSVMFree clEnqueueSVMFree_ +#define clEnqueueSVMMemcpy clEnqueueSVMMemcpy_ +#define clEnqueueSVMMemFill clEnqueueSVMMemFill_ +#define clEnqueueSVMMap clEnqueueSVMMap_ +#define clEnqueueSVMUnmap clEnqueueSVMUnmap_ +#endif + +#include "autogenerated/opencl_core.hpp" + +#ifndef CL_DEVICE_DOUBLE_FP_CONFIG +#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 +#endif + +#ifndef CL_DEVICE_HALF_FP_CONFIG +#define CL_DEVICE_HALF_FP_CONFIG 0x1033 +#endif + +#ifndef CL_VERSION_1_2 +#define CV_REQUIRE_OPENCL_1_2_ERROR CV_Error(cv::Error::OpenCLApiCallError, "OpenCV compiled without OpenCL v1.2 support, so we can't use functionality from OpenCL v1.2") +#endif + +#endif // HAVE_OPENCL + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp new file mode 100644 index 0000000..38fcae9 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP + +#include "autogenerated/opencl_core_wrappers.hpp" + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl.hpp new file mode 100644 index 0000000..659c7d8 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP + +#if defined HAVE_OPENCL && defined HAVE_OPENGL + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_gl.hpp" + +#endif // defined HAVE_OPENCL && defined HAVE_OPENGL + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp new file mode 100644 index 0000000..9700004 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP + +#include "autogenerated/opencl_gl_wrappers.hpp" + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_20.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_20.hpp new file mode 100644 index 0000000..9636b19 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_20.hpp @@ -0,0 +1,48 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP + +#if defined(HAVE_OPENCL_SVM) +#include "opencl_core.hpp" + +#include "opencl_svm_definitions.hpp" + +#undef clSVMAlloc +#define clSVMAlloc clSVMAlloc_pfn +#undef clSVMFree +#define clSVMFree clSVMFree_pfn +#undef clSetKernelArgSVMPointer +#define clSetKernelArgSVMPointer clSetKernelArgSVMPointer_pfn +#undef clSetKernelExecInfo +//#define clSetKernelExecInfo clSetKernelExecInfo_pfn +#undef clEnqueueSVMFree +//#define clEnqueueSVMFree clEnqueueSVMFree_pfn +#undef clEnqueueSVMMemcpy +#define clEnqueueSVMMemcpy clEnqueueSVMMemcpy_pfn +#undef clEnqueueSVMMemFill +#define clEnqueueSVMMemFill clEnqueueSVMMemFill_pfn +#undef clEnqueueSVMMap +#define clEnqueueSVMMap clEnqueueSVMMap_pfn +#undef clEnqueueSVMUnmap +#define clEnqueueSVMUnmap clEnqueueSVMUnmap_pfn + +extern CL_RUNTIME_EXPORT void* (CL_API_CALL *clSVMAlloc)(cl_context context, cl_svm_mem_flags flags, size_t size, unsigned int alignment); +extern CL_RUNTIME_EXPORT void (CL_API_CALL *clSVMFree)(cl_context context, void* svm_pointer); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clSetKernelArgSVMPointer)(cl_kernel kernel, cl_uint arg_index, const void* arg_value); +//extern CL_RUNTIME_EXPORT void* (CL_API_CALL *clSetKernelExecInfo)(cl_kernel kernel, cl_kernel_exec_info param_name, size_t param_value_size, const void* param_value); +//extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMFree)(cl_command_queue command_queue, cl_uint num_svm_pointers, void* svm_pointers[], +// void (CL_CALLBACK *pfn_free_func)(cl_command_queue queue, cl_uint num_svm_pointers, void* svm_pointers[], void* user_data), void* user_data, +// cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMemcpy)(cl_command_queue command_queue, cl_bool blocking_copy, void* dst_ptr, const void* src_ptr, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMemFill)(cl_command_queue command_queue, void* svm_ptr, const void* pattern, size_t pattern_size, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMap)(cl_command_queue command_queue, cl_bool blocking_map, cl_map_flags map_flags, void* svm_ptr, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMUnmap)(cl_command_queue command_queue, void* svm_ptr, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); + +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp new file mode 100644 index 0000000..97c927b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp @@ -0,0 +1,42 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP + +#if defined(HAVE_OPENCL_SVM) +#if defined(CL_VERSION_2_0) + +// OpenCL 2.0 contains SVM definitions + +#else + +typedef cl_bitfield cl_device_svm_capabilities; +typedef cl_bitfield cl_svm_mem_flags; +typedef cl_uint cl_kernel_exec_info; + +// +// TODO Add real values after OpenCL 2.0 release +// + +#ifndef CL_DEVICE_SVM_CAPABILITIES +#define CL_DEVICE_SVM_CAPABILITIES 0x1053 + +#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) +#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) +#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) +#define CL_DEVICE_SVM_ATOMICS (1 << 3) +#endif + +#ifndef CL_MEM_SVM_FINE_GRAIN_BUFFER +#define CL_MEM_SVM_FINE_GRAIN_BUFFER (1 << 10) +#endif + +#ifndef CL_MEM_SVM_ATOMICS +#define CL_MEM_SVM_ATOMICS (1 << 11) +#endif + + +#endif // CL_VERSION_2_0 +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp new file mode 100644 index 0000000..497bc3d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp @@ -0,0 +1,166 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP + +#if defined(HAVE_OPENCL_SVM) +#include "opencl_core.hpp" + +#ifndef CL_DEVICE_SVM_CAPABILITIES_AMD +// +// Part of the file is an extract from the cl_ext.h file from AMD APP SDK package. +// Below is the original copyright. +// +/******************************************************************************* + * Copyright (c) 2008-2013 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +/******************************************* + * Shared Virtual Memory (SVM) extension + *******************************************/ +typedef cl_bitfield cl_device_svm_capabilities_amd; +typedef cl_bitfield cl_svm_mem_flags_amd; +typedef cl_uint cl_kernel_exec_info_amd; + +/* cl_device_info */ +#define CL_DEVICE_SVM_CAPABILITIES_AMD 0x1053 +#define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT_AMD 0x1054 + +/* cl_device_svm_capabilities_amd */ +#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER_AMD (1 << 0) +#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER_AMD (1 << 1) +#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM_AMD (1 << 2) +#define CL_DEVICE_SVM_ATOMICS_AMD (1 << 3) + +/* cl_svm_mem_flags_amd */ +#define CL_MEM_SVM_FINE_GRAIN_BUFFER_AMD (1 << 10) +#define CL_MEM_SVM_ATOMICS_AMD (1 << 11) + +/* cl_mem_info */ +#define CL_MEM_USES_SVM_POINTER_AMD 0x1109 + +/* cl_kernel_exec_info_amd */ +#define CL_KERNEL_EXEC_INFO_SVM_PTRS_AMD 0x11B6 +#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM_AMD 0x11B7 + +/* cl_command_type */ +#define CL_COMMAND_SVM_FREE_AMD 0x1209 +#define CL_COMMAND_SVM_MEMCPY_AMD 0x120A +#define CL_COMMAND_SVM_MEMFILL_AMD 0x120B +#define CL_COMMAND_SVM_MAP_AMD 0x120C +#define CL_COMMAND_SVM_UNMAP_AMD 0x120D + +typedef CL_API_ENTRY void* +(CL_API_CALL * clSVMAllocAMD_fn)( + cl_context /* context */, + cl_svm_mem_flags_amd /* flags */, + size_t /* size */, + unsigned int /* alignment */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY void +(CL_API_CALL * clSVMFreeAMD_fn)( + cl_context /* context */, + void* /* svm_pointer */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMFreeAMD_fn)( + cl_command_queue /* command_queue */, + cl_uint /* num_svm_pointers */, + void** /* svm_pointers */, + void (CL_CALLBACK *)( /*pfn_free_func*/ + cl_command_queue /* queue */, + cl_uint /* num_svm_pointers */, + void** /* svm_pointers */, + void* /* user_data */), + void* /* user_data */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMemcpyAMD_fn)( + cl_command_queue /* command_queue */, + cl_bool /* blocking_copy */, + void* /* dst_ptr */, + const void* /* src_ptr */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMemFillAMD_fn)( + cl_command_queue /* command_queue */, + void* /* svm_ptr */, + const void* /* pattern */, + size_t /* pattern_size */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMapAMD_fn)( + cl_command_queue /* command_queue */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + void* /* svm_ptr */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMUnmapAMD_fn)( + cl_command_queue /* command_queue */, + void* /* svm_ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clSetKernelArgSVMPointerAMD_fn)( + cl_kernel /* kernel */, + cl_uint /* arg_index */, + const void * /* arg_value */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clSetKernelExecInfoAMD_fn)( + cl_kernel /* kernel */, + cl_kernel_exec_info_amd /* param_name */, + size_t /* param_value_size */, + const void * /* param_value */ +) CL_EXT_SUFFIX__VERSION_1_2; + +#endif + +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/opengl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/opengl.hpp new file mode 100644 index 0000000..a6288be --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/opengl.hpp @@ -0,0 +1,725 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OPENGL_HPP +#define OPENCV_CORE_OPENGL_HPP + +#ifndef __cplusplus +# error opengl.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "ocl.hpp" + +namespace cv { namespace ogl { + +/** @addtogroup core_opengl +This section describes OpenGL interoperability. + +To enable OpenGL support, configure OpenCV using CMake with WITH_OPENGL=ON . Currently OpenGL is +supported only with WIN32, GTK and Qt backends on Windows and Linux (MacOS and Android are not +supported). For GTK backend gtkglext-1.0 library is required. + +To use OpenGL functionality you should first create OpenGL context (window or frame buffer). You can +do this with namedWindow function or with other OpenGL toolkit (GLUT, for example). +*/ +//! @{ + +/////////////////// OpenGL Objects /////////////////// + +/** @brief Smart pointer for OpenGL buffer object with reference counting. + +Buffer Objects are OpenGL objects that store an array of unformatted memory allocated by the OpenGL +context. These can be used to store vertex data, pixel data retrieved from images or the +framebuffer, and a variety of other things. + +ogl::Buffer has interface similar with Mat interface and represents 2D array memory. + +ogl::Buffer supports memory transfers between host and device and also can be mapped to CUDA memory. + */ +class CV_EXPORTS Buffer +{ +public: + /** @brief The target defines how you intend to use the buffer object. + */ + enum Target + { + ARRAY_BUFFER = 0x8892, //!< The buffer will be used as a source for vertex data + ELEMENT_ARRAY_BUFFER = 0x8893, //!< The buffer will be used for indices (in glDrawElements, for example) + PIXEL_PACK_BUFFER = 0x88EB, //!< The buffer will be used for reading from OpenGL textures + PIXEL_UNPACK_BUFFER = 0x88EC //!< The buffer will be used for writing to OpenGL textures + }; + + enum Access + { + READ_ONLY = 0x88B8, + WRITE_ONLY = 0x88B9, + READ_WRITE = 0x88BA + }; + + /** @brief The constructors. + + Creates empty ogl::Buffer object, creates ogl::Buffer object from existed buffer ( abufId + parameter), allocates memory for ogl::Buffer object or copies from host/device memory. + */ + Buffer(); + + /** @overload + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param abufId Buffer object name. + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(int arows, int acols, int atype, unsigned int abufId, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param abufId Buffer object name. + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(Size asize, int atype, unsigned int abufId, bool autoRelease = false); + + /** @overload + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(int arows, int acols, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(Size asize, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or std::vector ). + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + explicit Buffer(InputArray arr, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Allocates memory for ogl::Buffer object. + + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(int arows, int acols, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(Size asize, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Decrements the reference counter and destroys the buffer object if needed. + + The function will call setAutoRelease(true) . + */ + void release(); + + /** @brief Sets auto release mode. + + The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was + bound to a window it could be released at any time (user can close a window). If object's destructor + is called after destruction of the context it will cause an error. Thus ogl::Buffer doesn't destroy + OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL context). + This function can force ogl::Buffer destructor to destroy OpenGL object. + @param flag Auto release mode (if true, release will be called in object's destructor). + */ + void setAutoRelease(bool flag); + + /** @brief Copies from host/device memory to OpenGL buffer. + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or std::vector ). + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void copyFrom(InputArray arr, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload */ + void copyFrom(InputArray arr, cuda::Stream& stream, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Copies from OpenGL buffer to host/device memory or another OpenGL buffer object. + + @param arr Destination array (host or device memory, can be Mat , cuda::GpuMat , std::vector or + ogl::Buffer ). + */ + void copyTo(OutputArray arr) const; + + /** @overload */ + void copyTo(OutputArray arr, cuda::Stream& stream) const; + + /** @brief Creates a full copy of the buffer object and the underlying data. + + @param target Buffer usage for destination buffer. + @param autoRelease Auto release mode for destination buffer. + */ + Buffer clone(Target target = ARRAY_BUFFER, bool autoRelease = false) const; + + /** @brief Binds OpenGL buffer to the specified buffer binding point. + + @param target Binding point. See cv::ogl::Buffer::Target . + */ + void bind(Target target) const; + + /** @brief Unbind any buffers from the specified binding point. + + @param target Binding point. See cv::ogl::Buffer::Target . + */ + static void unbind(Target target); + + /** @brief Maps OpenGL buffer to host memory. + + mapHost maps to the client's address space the entire data store of the buffer object. The data can + then be directly read and/or written relative to the returned pointer, depending on the specified + access policy. + + A mapped data store must be unmapped with ogl::Buffer::unmapHost before its buffer object is used. + + This operation can lead to memory transfers between host and device. + + Only one buffer object can be mapped at a time. + @param access Access policy, indicating whether it will be possible to read from, write to, or both + read from and write to the buffer object's mapped data store. The symbolic constant must be + ogl::Buffer::READ_ONLY , ogl::Buffer::WRITE_ONLY or ogl::Buffer::READ_WRITE . + */ + Mat mapHost(Access access); + + /** @brief Unmaps OpenGL buffer. + */ + void unmapHost(); + + //! map to device memory (blocking) + cuda::GpuMat mapDevice(); + void unmapDevice(); + + /** @brief Maps OpenGL buffer to CUDA device memory. + + This operation doesn't copy data. Several buffer objects can be mapped to CUDA memory at a time. + + A mapped data store must be unmapped with ogl::Buffer::unmapDevice before its buffer object is used. + */ + cuda::GpuMat mapDevice(cuda::Stream& stream); + + /** @brief Unmaps OpenGL buffer. + */ + void unmapDevice(cuda::Stream& stream); + + int rows() const; + int cols() const; + Size size() const; + bool empty() const; + + int type() const; + int depth() const; + int channels() const; + int elemSize() const; + int elemSize1() const; + + //! get OpenGL opject id + unsigned int bufId() const; + + class Impl; + +private: + Ptr impl_; + int rows_; + int cols_; + int type_; +}; + +/** @brief Smart pointer for OpenGL 2D texture memory with reference counting. + */ +class CV_EXPORTS Texture2D +{ +public: + /** @brief An Image Format describes the way that the images in Textures store their data. + */ + enum Format + { + NONE = 0, + DEPTH_COMPONENT = 0x1902, //!< Depth + RGB = 0x1907, //!< Red, Green, Blue + RGBA = 0x1908 //!< Red, Green, Blue, Alpha + }; + + /** @brief The constructors. + + Creates empty ogl::Texture2D object, allocates memory for ogl::Texture2D object or copies from + host/device memory. + */ + Texture2D(); + + /** @overload */ + Texture2D(int arows, int acols, Format aformat, unsigned int atexId, bool autoRelease = false); + + /** @overload */ + Texture2D(Size asize, Format aformat, unsigned int atexId, bool autoRelease = false); + + /** @overload + @param arows Number of rows. + @param acols Number of columns. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Texture2D(int arows, int acols, Format aformat, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Texture2D(Size asize, Format aformat, bool autoRelease = false); + + /** @overload + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or ogl::Buffer ). + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + explicit Texture2D(InputArray arr, bool autoRelease = false); + + /** @brief Allocates memory for ogl::Texture2D object. + + @param arows Number of rows. + @param acols Number of columns. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(int arows, int acols, Format aformat, bool autoRelease = false); + /** @overload + @param asize 2D array size. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(Size asize, Format aformat, bool autoRelease = false); + + /** @brief Decrements the reference counter and destroys the texture object if needed. + + The function will call setAutoRelease(true) . + */ + void release(); + + /** @brief Sets auto release mode. + + @param flag Auto release mode (if true, release will be called in object's destructor). + + The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was + bound to a window it could be released at any time (user can close a window). If object's destructor + is called after destruction of the context it will cause an error. Thus ogl::Texture2D doesn't + destroy OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL + context). This function can force ogl::Texture2D destructor to destroy OpenGL object. + */ + void setAutoRelease(bool flag); + + /** @brief Copies from host/device memory to OpenGL texture. + + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or ogl::Buffer ). + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void copyFrom(InputArray arr, bool autoRelease = false); + + /** @brief Copies from OpenGL texture to host/device memory or another OpenGL texture object. + + @param arr Destination array (host or device memory, can be Mat , cuda::GpuMat , ogl::Buffer or + ogl::Texture2D ). + @param ddepth Destination depth. + @param autoRelease Auto release mode for destination buffer (if arr is OpenGL buffer or texture). + */ + void copyTo(OutputArray arr, int ddepth = CV_32F, bool autoRelease = false) const; + + /** @brief Binds texture to current active texture unit for GL_TEXTURE_2D target. + */ + void bind() const; + + int rows() const; + int cols() const; + Size size() const; + bool empty() const; + + Format format() const; + + //! get OpenGL opject id + unsigned int texId() const; + + class Impl; + +private: + Ptr impl_; + int rows_; + int cols_; + Format format_; +}; + +/** @brief Wrapper for OpenGL Client-Side Vertex arrays. + +ogl::Arrays stores vertex data in ogl::Buffer objects. + */ +class CV_EXPORTS Arrays +{ +public: + /** @brief Default constructor + */ + Arrays(); + + /** @brief Sets an array of vertex coordinates. + @param vertex array with vertex coordinates, can be both host and device memory. + */ + void setVertexArray(InputArray vertex); + + /** @brief Resets vertex coordinates. + */ + void resetVertexArray(); + + /** @brief Sets an array of vertex colors. + @param color array with vertex colors, can be both host and device memory. + */ + void setColorArray(InputArray color); + + /** @brief Resets vertex colors. + */ + void resetColorArray(); + + /** @brief Sets an array of vertex normals. + @param normal array with vertex normals, can be both host and device memory. + */ + void setNormalArray(InputArray normal); + + /** @brief Resets vertex normals. + */ + void resetNormalArray(); + + /** @brief Sets an array of vertex texture coordinates. + @param texCoord array with vertex texture coordinates, can be both host and device memory. + */ + void setTexCoordArray(InputArray texCoord); + + /** @brief Resets vertex texture coordinates. + */ + void resetTexCoordArray(); + + /** @brief Releases all inner buffers. + */ + void release(); + + /** @brief Sets auto release mode all inner buffers. + @param flag Auto release mode. + */ + void setAutoRelease(bool flag); + + /** @brief Binds all vertex arrays. + */ + void bind() const; + + /** @brief Returns the vertex count. + */ + int size() const; + bool empty() const; + +private: + int size_; + Buffer vertex_; + Buffer color_; + Buffer normal_; + Buffer texCoord_; +}; + +/////////////////// Render Functions /////////////////// + +//! render mode +enum RenderModes { + POINTS = 0x0000, + LINES = 0x0001, + LINE_LOOP = 0x0002, + LINE_STRIP = 0x0003, + TRIANGLES = 0x0004, + TRIANGLE_STRIP = 0x0005, + TRIANGLE_FAN = 0x0006, + QUADS = 0x0007, + QUAD_STRIP = 0x0008, + POLYGON = 0x0009 +}; + +/** @brief Render OpenGL texture or primitives. +@param tex Texture to draw. +@param wndRect Region of window, where to draw a texture (normalized coordinates). +@param texRect Region of texture to draw (normalized coordinates). + */ +CV_EXPORTS void render(const Texture2D& tex, + Rect_ wndRect = Rect_(0.0, 0.0, 1.0, 1.0), + Rect_ texRect = Rect_(0.0, 0.0, 1.0, 1.0)); + +/** @overload +@param arr Array of privitives vertices. +@param mode Render mode. One of cv::ogl::RenderModes +@param color Color for all vertices. Will be used if arr doesn't contain color array. +*/ +CV_EXPORTS void render(const Arrays& arr, int mode = POINTS, Scalar color = Scalar::all(255)); + +/** @overload +@param arr Array of privitives vertices. +@param indices Array of vertices indices (host or device memory). +@param mode Render mode. One of cv::ogl::RenderModes +@param color Color for all vertices. Will be used if arr doesn't contain color array. +*/ +CV_EXPORTS void render(const Arrays& arr, InputArray indices, int mode = POINTS, Scalar color = Scalar::all(255)); + +/////////////////// CL-GL Interoperability Functions /////////////////// + +namespace ocl { +using namespace cv::ocl; + +// TODO static functions in the Context class +/** @brief Creates OpenCL context from GL. +@return Returns reference to OpenCL Context + */ +CV_EXPORTS Context& initializeContextFromGL(); + +} // namespace cv::ogl::ocl + +/** @brief Converts InputArray to Texture2D object. +@param src - source InputArray. +@param texture - destination Texture2D object. + */ +CV_EXPORTS void convertToGLTexture2D(InputArray src, Texture2D& texture); + +/** @brief Converts Texture2D object to OutputArray. +@param texture - source Texture2D object. +@param dst - destination OutputArray. + */ +CV_EXPORTS void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst); + +/** @brief Maps Buffer object to process on CL side (convert to UMat). + +Function creates CL buffer from GL one, and then constructs UMat that can be used +to process buffer data with OpenCV functions. Note that in current implementation +UMat constructed this way doesn't own corresponding GL buffer object, so it is +the user responsibility to close down CL/GL buffers relationships by explicitly +calling unmapGLBuffer() function. +@param buffer - source Buffer object. +@param accessFlags - data access flags (ACCESS_READ|ACCESS_WRITE). +@return Returns UMat object + */ +CV_EXPORTS UMat mapGLBuffer(const Buffer& buffer, int accessFlags = ACCESS_READ|ACCESS_WRITE); + +/** @brief Unmaps Buffer object (releases UMat, previously mapped from Buffer). + +Function must be called explicitly by the user for each UMat previously constructed +by the call to mapGLBuffer() function. +@param u - source UMat, created by mapGLBuffer(). + */ +CV_EXPORTS void unmapGLBuffer(UMat& u); + +//! @} +}} // namespace cv::ogl + +namespace cv { namespace cuda { + +/** @brief Sets a CUDA device and initializes it for the current thread with OpenGL interoperability. + +This function should be explicitly called after OpenGL context creation and before any CUDA calls. +@param device System index of a CUDA device starting with 0. +@ingroup core_opengl + */ +CV_EXPORTS void setGlDevice(int device = 0); + +}} + +//! @cond IGNORED + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +inline +cv::ogl::Buffer::Buffer(int arows, int acols, int atype, Target target, bool autoRelease) : rows_(0), cols_(0), type_(0) +{ + create(arows, acols, atype, target, autoRelease); +} + +inline +cv::ogl::Buffer::Buffer(Size asize, int atype, Target target, bool autoRelease) : rows_(0), cols_(0), type_(0) +{ + create(asize, atype, target, autoRelease); +} + +inline +void cv::ogl::Buffer::create(Size asize, int atype, Target target, bool autoRelease) +{ + create(asize.height, asize.width, atype, target, autoRelease); +} + +inline +int cv::ogl::Buffer::rows() const +{ + return rows_; +} + +inline +int cv::ogl::Buffer::cols() const +{ + return cols_; +} + +inline +cv::Size cv::ogl::Buffer::size() const +{ + return Size(cols_, rows_); +} + +inline +bool cv::ogl::Buffer::empty() const +{ + return rows_ == 0 || cols_ == 0; +} + +inline +int cv::ogl::Buffer::type() const +{ + return type_; +} + +inline +int cv::ogl::Buffer::depth() const +{ + return CV_MAT_DEPTH(type_); +} + +inline +int cv::ogl::Buffer::channels() const +{ + return CV_MAT_CN(type_); +} + +inline +int cv::ogl::Buffer::elemSize() const +{ + return CV_ELEM_SIZE(type_); +} + +inline +int cv::ogl::Buffer::elemSize1() const +{ + return CV_ELEM_SIZE1(type_); +} + +/////// + +inline +cv::ogl::Texture2D::Texture2D(int arows, int acols, Format aformat, bool autoRelease) : rows_(0), cols_(0), format_(NONE) +{ + create(arows, acols, aformat, autoRelease); +} + +inline +cv::ogl::Texture2D::Texture2D(Size asize, Format aformat, bool autoRelease) : rows_(0), cols_(0), format_(NONE) +{ + create(asize, aformat, autoRelease); +} + +inline +void cv::ogl::Texture2D::create(Size asize, Format aformat, bool autoRelease) +{ + create(asize.height, asize.width, aformat, autoRelease); +} + +inline +int cv::ogl::Texture2D::rows() const +{ + return rows_; +} + +inline +int cv::ogl::Texture2D::cols() const +{ + return cols_; +} + +inline +cv::Size cv::ogl::Texture2D::size() const +{ + return Size(cols_, rows_); +} + +inline +bool cv::ogl::Texture2D::empty() const +{ + return rows_ == 0 || cols_ == 0; +} + +inline +cv::ogl::Texture2D::Format cv::ogl::Texture2D::format() const +{ + return format_; +} + +/////// + +inline +cv::ogl::Arrays::Arrays() : size_(0) +{ +} + +inline +int cv::ogl::Arrays::size() const +{ + return size_; +} + +inline +bool cv::ogl::Arrays::empty() const +{ + return size_ == 0; +} + +//! @endcond + +#endif /* OPENCV_CORE_OPENGL_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/operations.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/operations.hpp new file mode 100644 index 0000000..ef1808a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/operations.hpp @@ -0,0 +1,573 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OPERATIONS_HPP +#define OPENCV_CORE_OPERATIONS_HPP + +#ifndef __cplusplus +# error operations.hpp header must be compiled as C++ +#endif + +#include + +//! @cond IGNORED + +namespace cv +{ + +////////////////////////////// Matx methods depending on core API ///////////////////////////// + +namespace internal +{ + +template struct Matx_FastInvOp +{ + bool operator()(const Matx<_Tp, m, n>& a, Matx<_Tp, n, m>& b, int method) const + { + return invert(a, b, method) != 0; + } +}; + +template struct Matx_FastInvOp<_Tp, m, m> +{ + bool operator()(const Matx<_Tp, m, m>& a, Matx<_Tp, m, m>& b, int method) const + { + if (method == DECOMP_LU || method == DECOMP_CHOLESKY) + { + Matx<_Tp, m, m> temp = a; + + // assume that b is all 0's on input => make it a unity matrix + for (int i = 0; i < m; i++) + b(i, i) = (_Tp)1; + + if (method == DECOMP_CHOLESKY) + return Cholesky(temp.val, m*sizeof(_Tp), m, b.val, m*sizeof(_Tp), m); + + return LU(temp.val, m*sizeof(_Tp), m, b.val, m*sizeof(_Tp), m) != 0; + } + else + { + return invert(a, b, method) != 0; + } + } +}; + +template struct Matx_FastInvOp<_Tp, 2, 2> +{ + bool operator()(const Matx<_Tp, 2, 2>& a, Matx<_Tp, 2, 2>& b, int /*method*/) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + b(1,1) = a(0,0)*d; + b(0,0) = a(1,1)*d; + b(0,1) = -a(0,1)*d; + b(1,0) = -a(1,0)*d; + return true; + } +}; + +template struct Matx_FastInvOp<_Tp, 3, 3> +{ + bool operator()(const Matx<_Tp, 3, 3>& a, Matx<_Tp, 3, 3>& b, int /*method*/) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + b(0,0) = (a(1,1) * a(2,2) - a(1,2) * a(2,1)) * d; + b(0,1) = (a(0,2) * a(2,1) - a(0,1) * a(2,2)) * d; + b(0,2) = (a(0,1) * a(1,2) - a(0,2) * a(1,1)) * d; + + b(1,0) = (a(1,2) * a(2,0) - a(1,0) * a(2,2)) * d; + b(1,1) = (a(0,0) * a(2,2) - a(0,2) * a(2,0)) * d; + b(1,2) = (a(0,2) * a(1,0) - a(0,0) * a(1,2)) * d; + + b(2,0) = (a(1,0) * a(2,1) - a(1,1) * a(2,0)) * d; + b(2,1) = (a(0,1) * a(2,0) - a(0,0) * a(2,1)) * d; + b(2,2) = (a(0,0) * a(1,1) - a(0,1) * a(1,0)) * d; + return true; + } +}; + + +template struct Matx_FastSolveOp +{ + bool operator()(const Matx<_Tp, m, l>& a, const Matx<_Tp, m, n>& b, + Matx<_Tp, l, n>& x, int method) const + { + return cv::solve(a, b, x, method); + } +}; + +template struct Matx_FastSolveOp<_Tp, m, m, n> +{ + bool operator()(const Matx<_Tp, m, m>& a, const Matx<_Tp, m, n>& b, + Matx<_Tp, m, n>& x, int method) const + { + if (method == DECOMP_LU || method == DECOMP_CHOLESKY) + { + Matx<_Tp, m, m> temp = a; + x = b; + if( method == DECOMP_CHOLESKY ) + return Cholesky(temp.val, m*sizeof(_Tp), m, x.val, n*sizeof(_Tp), n); + + return LU(temp.val, m*sizeof(_Tp), m, x.val, n*sizeof(_Tp), n) != 0; + } + else + { + return cv::solve(a, b, x, method); + } + } +}; + +template struct Matx_FastSolveOp<_Tp, 2, 2, 1> +{ + bool operator()(const Matx<_Tp, 2, 2>& a, const Matx<_Tp, 2, 1>& b, + Matx<_Tp, 2, 1>& x, int) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + x(0) = (b(0)*a(1,1) - b(1)*a(0,1))*d; + x(1) = (b(1)*a(0,0) - b(0)*a(1,0))*d; + return true; + } +}; + +template struct Matx_FastSolveOp<_Tp, 3, 3, 1> +{ + bool operator()(const Matx<_Tp, 3, 3>& a, const Matx<_Tp, 3, 1>& b, + Matx<_Tp, 3, 1>& x, int) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + x(0) = d*(b(0)*(a(1,1)*a(2,2) - a(1,2)*a(2,1)) - + a(0,1)*(b(1)*a(2,2) - a(1,2)*b(2)) + + a(0,2)*(b(1)*a(2,1) - a(1,1)*b(2))); + + x(1) = d*(a(0,0)*(b(1)*a(2,2) - a(1,2)*b(2)) - + b(0)*(a(1,0)*a(2,2) - a(1,2)*a(2,0)) + + a(0,2)*(a(1,0)*b(2) - b(1)*a(2,0))); + + x(2) = d*(a(0,0)*(a(1,1)*b(2) - b(1)*a(2,1)) - + a(0,1)*(a(1,0)*b(2) - b(1)*a(2,0)) + + b(0)*(a(1,0)*a(2,1) - a(1,1)*a(2,0))); + return true; + } +}; + +} // internal + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::randu(_Tp a, _Tp b) +{ + Matx<_Tp,m,n> M; + cv::randu(M, Scalar(a), Scalar(b)); + return M; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::randn(_Tp a, _Tp b) +{ + Matx<_Tp,m,n> M; + cv::randn(M, Scalar(a), Scalar(b)); + return M; +} + +template inline +Matx<_Tp, n, m> Matx<_Tp, m, n>::inv(int method, bool *p_is_ok /*= NULL*/) const +{ + Matx<_Tp, n, m> b; + bool ok = cv::internal::Matx_FastInvOp<_Tp, m, n>()(*this, b, method); + if (p_is_ok) *p_is_ok = ok; + return ok ? b : Matx<_Tp, n, m>::zeros(); +} + +template template inline +Matx<_Tp, n, l> Matx<_Tp, m, n>::solve(const Matx<_Tp, m, l>& rhs, int method) const +{ + Matx<_Tp, n, l> x; + bool ok = cv::internal::Matx_FastSolveOp<_Tp, m, n, l>()(*this, rhs, x, method); + return ok ? x : Matx<_Tp, n, l>::zeros(); +} + + + +////////////////////////// Augmenting algebraic & logical operations ////////////////////////// + +#define CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + static inline A& operator op (A& a, const B& b) { cvop; return a; } + +#define CV_MAT_AUG_OPERATOR(op, cvop, A, B) \ + CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + CV_MAT_AUG_OPERATOR1(op, cvop, const A, B) + +#define CV_MAT_AUG_OPERATOR_T(op, cvop, A, B) \ + template CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + template CV_MAT_AUG_OPERATOR1(op, cvop, const A, B) + +#define CV_MAT_AUG_OPERATOR_TN(op, cvop, A) \ + template static inline A& operator op (A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } \ + template static inline const A& operator op (const A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } + +CV_MAT_AUG_OPERATOR (+=, cv::add(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (+=, cv::add(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (-=, cv::subtract(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (-=, cv::subtract(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat, Mat) +CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR (*=, a.convertTo(a, -1, b), Mat, double) +CV_MAT_AUG_OPERATOR_T(*=, a.convertTo(a, -1, b), Mat_<_Tp>, double) +CV_MAT_AUG_OPERATOR_TN(*=, cv::gemm(a, Mat(b), 1, Mat(), 0, a, 0), Mat) +CV_MAT_AUG_OPERATOR_TN(*=, cv::gemm(a, Mat(b), 1, Mat(), 0, a, 0), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (/=, cv::divide(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR_T(/=, cv::divide(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(/=, cv::divide(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR (/=, a.convertTo((Mat&)a, -1, 1./b), Mat, double) +CV_MAT_AUG_OPERATOR_T(/=, a.convertTo((Mat&)a, -1, 1./b), Mat_<_Tp>, double) +CV_MAT_AUG_OPERATOR_TN(/=, cv::divide(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(/=, cv::divide(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (&=, cv::bitwise_and(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (&=, cv::bitwise_and(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +#undef CV_MAT_AUG_OPERATOR_TN +#undef CV_MAT_AUG_OPERATOR_T +#undef CV_MAT_AUG_OPERATOR +#undef CV_MAT_AUG_OPERATOR1 + + + +///////////////////////////////////////////// SVD ///////////////////////////////////////////// + +inline SVD::SVD() {} +inline SVD::SVD( InputArray m, int flags ) { operator ()(m, flags); } +inline void SVD::solveZ( InputArray m, OutputArray _dst ) +{ + Mat mtx = m.getMat(); + SVD svd(mtx, (mtx.rows >= mtx.cols ? 0 : SVD::FULL_UV)); + _dst.create(svd.vt.cols, 1, svd.vt.type()); + Mat dst = _dst.getMat(); + svd.vt.row(svd.vt.rows-1).reshape(1,svd.vt.cols).copyTo(dst); +} + +template inline void + SVD::compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w, Matx<_Tp, m, nm>& u, Matx<_Tp, n, nm>& vt ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _a(a, false), _u(u, false), _w(w, false), _vt(vt, false); + SVD::compute(_a, _w, _u, _vt); + CV_Assert(_w.data == (uchar*)&w.val[0] && _u.data == (uchar*)&u.val[0] && _vt.data == (uchar*)&vt.val[0]); +} + +template inline void +SVD::compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _a(a, false), _w(w, false); + SVD::compute(_a, _w); + CV_Assert(_w.data == (uchar*)&w.val[0]); +} + +template inline void +SVD::backSubst( const Matx<_Tp, nm, 1>& w, const Matx<_Tp, m, nm>& u, + const Matx<_Tp, n, nm>& vt, const Matx<_Tp, m, nb>& rhs, + Matx<_Tp, n, nb>& dst ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _u(u, false), _w(w, false), _vt(vt, false), _rhs(rhs, false), _dst(dst, false); + SVD::backSubst(_w, _u, _vt, _rhs, _dst); + CV_Assert(_dst.data == (uchar*)&dst.val[0]); +} + + + +/////////////////////////////////// Multiply-with-Carry RNG /////////////////////////////////// + +inline RNG::RNG() { state = 0xffffffff; } +inline RNG::RNG(uint64 _state) { state = _state ? _state : 0xffffffff; } + +inline RNG::operator uchar() { return (uchar)next(); } +inline RNG::operator schar() { return (schar)next(); } +inline RNG::operator ushort() { return (ushort)next(); } +inline RNG::operator short() { return (short)next(); } +inline RNG::operator int() { return (int)next(); } +inline RNG::operator unsigned() { return next(); } +inline RNG::operator float() { return next()*2.3283064365386962890625e-10f; } +inline RNG::operator double() { unsigned t = next(); return (((uint64)t << 32) | next()) * 5.4210108624275221700372640043497e-20; } + +inline unsigned RNG::operator ()(unsigned N) { return (unsigned)uniform(0,N); } +inline unsigned RNG::operator ()() { return next(); } + +inline int RNG::uniform(int a, int b) { return a == b ? a : (int)(next() % (b - a) + a); } +inline float RNG::uniform(float a, float b) { return ((float)*this)*(b - a) + a; } +inline double RNG::uniform(double a, double b) { return ((double)*this)*(b - a) + a; } + +inline bool RNG::operator ==(const RNG& other) const { return state == other.state; } + +inline unsigned RNG::next() +{ + state = (uint64)(unsigned)state* /*CV_RNG_COEFF*/ 4164903690U + (unsigned)(state >> 32); + return (unsigned)state; +} + +//! returns the next uniformly-distributed random number of the specified type +template static inline _Tp randu() +{ + return (_Tp)theRNG(); +} + +///////////////////////////////// Formatted string generation ///////////////////////////////// + +/** @brief Returns a text string formatted using the printf-like expression. + +The function acts like sprintf but forms and returns an STL string. It can be used to form an error +message in the Exception constructor. +@param fmt printf-compatible formatting specifiers. + */ +CV_EXPORTS String format( const char* fmt, ... ); + +///////////////////////////////// Formatted output of cv::Mat ///////////////////////////////// + +static inline +Ptr format(InputArray mtx, int fmt) +{ + return Formatter::get(fmt)->format(mtx.getMat()); +} + +static inline +int print(Ptr fmtd, FILE* stream = stdout) +{ + int written = 0; + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + written += fputs(str, stream); + + return written; +} + +static inline +int print(const Mat& mtx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(mtx), stream); +} + +static inline +int print(const UMat& mtx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(mtx.getMat(ACCESS_READ)), stream); +} + +template static inline +int print(const std::vector >& vec, FILE* stream = stdout) +{ + return print(Formatter::get()->format(Mat(vec)), stream); +} + +template static inline +int print(const std::vector >& vec, FILE* stream = stdout) +{ + return print(Formatter::get()->format(Mat(vec)), stream); +} + +template static inline +int print(const Matx<_Tp, m, n>& matx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(cv::Mat(matx)), stream); +} + +//! @endcond + +/****************************************************************************************\ +* Auxiliary algorithms * +\****************************************************************************************/ + +/** @brief Splits an element set into equivalency classes. + +The generic function partition implements an \f$O(N^2)\f$ algorithm for splitting a set of \f$N\f$ elements +into one or more equivalency classes, as described in + . The function returns the number of +equivalency classes. +@param _vec Set of elements stored as a vector. +@param labels Output vector of labels. It contains as many elements as vec. Each label labels[i] is +a 0-based cluster index of `vec[i]`. +@param predicate Equivalence predicate (pointer to a boolean function of two arguments or an +instance of the class that has the method bool operator()(const _Tp& a, const _Tp& b) ). The +predicate returns true when the elements are certainly in the same class, and returns false if they +may or may not be in the same class. +@ingroup core_cluster +*/ +template int +partition( const std::vector<_Tp>& _vec, std::vector& labels, + _EqPredicate predicate=_EqPredicate()) +{ + int i, j, N = (int)_vec.size(); + const _Tp* vec = &_vec[0]; + + const int PARENT=0; + const int RANK=1; + + std::vector _nodes(N*2); + int (*nodes)[2] = (int(*)[2])&_nodes[0]; + + // The first O(N) pass: create N single-vertex trees + for(i = 0; i < N; i++) + { + nodes[i][PARENT]=-1; + nodes[i][RANK] = 0; + } + + // The main O(N^2) pass: merge connected components + for( i = 0; i < N; i++ ) + { + int root = i; + + // find root + while( nodes[root][PARENT] >= 0 ) + root = nodes[root][PARENT]; + + for( j = 0; j < N; j++ ) + { + if( i == j || !predicate(vec[i], vec[j])) + continue; + int root2 = j; + + while( nodes[root2][PARENT] >= 0 ) + root2 = nodes[root2][PARENT]; + + if( root2 != root ) + { + // unite both trees + int rank = nodes[root][RANK], rank2 = nodes[root2][RANK]; + if( rank > rank2 ) + nodes[root2][PARENT] = root; + else + { + nodes[root][PARENT] = root2; + nodes[root2][RANK] += rank == rank2; + root = root2; + } + CV_Assert( nodes[root][PARENT] < 0 ); + + int k = j, parent; + + // compress the path from node2 to root + while( (parent = nodes[k][PARENT]) >= 0 ) + { + nodes[k][PARENT] = root; + k = parent; + } + + // compress the path from node to root + k = i; + while( (parent = nodes[k][PARENT]) >= 0 ) + { + nodes[k][PARENT] = root; + k = parent; + } + } + } + } + + // Final O(N) pass: enumerate classes + labels.resize(N); + int nclasses = 0; + + for( i = 0; i < N; i++ ) + { + int root = i; + while( nodes[root][PARENT] >= 0 ) + root = nodes[root][PARENT]; + // re-use the rank as the class label + if( nodes[root][RANK] >= 0 ) + nodes[root][RANK] = ~nclasses++; + labels[i] = ~nodes[root][RANK]; + } + + return nclasses; +} + +} // cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/optim.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/optim.hpp new file mode 100644 index 0000000..5a09400 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/optim.hpp @@ -0,0 +1,302 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPTIM_HPP +#define OPENCV_OPTIM_HPP + +#include "opencv2/core.hpp" + +namespace cv +{ + +/** @addtogroup core_optim +The algorithms in this section minimize or maximize function value within specified constraints or +without any constraints. +@{ +*/ + +/** @brief Basic interface for all solvers + */ +class CV_EXPORTS MinProblemSolver : public Algorithm +{ +public: + /** @brief Represents function being optimized + */ + class CV_EXPORTS Function + { + public: + virtual ~Function() {} + virtual int getDims() const = 0; + virtual double getGradientEps() const; + virtual double calc(const double* x) const = 0; + virtual void getGradient(const double* x,double* grad); + }; + + /** @brief Getter for the optimized function. + + The optimized function is represented by Function interface, which requires derivatives to + implement the calc(double*) and getDim() methods to evaluate the function. + + @return Smart-pointer to an object that implements Function interface - it represents the + function that is being optimized. It can be empty, if no function was given so far. + */ + virtual Ptr getFunction() const = 0; + + /** @brief Setter for the optimized function. + + *It should be called at least once before the call to* minimize(), as default value is not usable. + + @param f The new function to optimize. + */ + virtual void setFunction(const Ptr& f) = 0; + + /** @brief Getter for the previously set terminal criteria for this algorithm. + + @return Deep copy of the terminal criteria used at the moment. + */ + virtual TermCriteria getTermCriteria() const = 0; + + /** @brief Set terminal criteria for solver. + + This method *is not necessary* to be called before the first call to minimize(), as the default + value is sensible. + + Algorithm stops when the number of function evaluations done exceeds termcrit.maxCount, when + the function values at the vertices of simplex are within termcrit.epsilon range or simplex + becomes so small that it can enclosed in a box with termcrit.epsilon sides, whatever comes + first. + @param termcrit Terminal criteria to be used, represented as cv::TermCriteria structure. + */ + virtual void setTermCriteria(const TermCriteria& termcrit) = 0; + + /** @brief actually runs the algorithm and performs the minimization. + + The sole input parameter determines the centroid of the starting simplex (roughly, it tells + where to start), all the others (terminal criteria, initial step, function to be minimized) are + supposed to be set via the setters before the call to this method or the default values (not + always sensible) will be used. + + @param x The initial point, that will become a centroid of an initial simplex. After the algorithm + will terminate, it will be set to the point where the algorithm stops, the point of possible + minimum. + @return The value of a function at the point found. + */ + virtual double minimize(InputOutputArray x) = 0; +}; + +/** @brief This class is used to perform the non-linear non-constrained minimization of a function, + +defined on an `n`-dimensional Euclidean space, using the **Nelder-Mead method**, also known as +**downhill simplex method**. The basic idea about the method can be obtained from +. + +It should be noted, that this method, although deterministic, is rather a heuristic and therefore +may converge to a local minima, not necessary a global one. It is iterative optimization technique, +which at each step uses an information about the values of a function evaluated only at `n+1` +points, arranged as a *simplex* in `n`-dimensional space (hence the second name of the method). At +each step new point is chosen to evaluate function at, obtained value is compared with previous +ones and based on this information simplex changes it's shape , slowly moving to the local minimum. +Thus this method is using *only* function values to make decision, on contrary to, say, Nonlinear +Conjugate Gradient method (which is also implemented in optim). + +Algorithm stops when the number of function evaluations done exceeds termcrit.maxCount, when the +function values at the vertices of simplex are within termcrit.epsilon range or simplex becomes so +small that it can enclosed in a box with termcrit.epsilon sides, whatever comes first, for some +defined by user positive integer termcrit.maxCount and positive non-integer termcrit.epsilon. + +@note DownhillSolver is a derivative of the abstract interface +cv::MinProblemSolver, which in turn is derived from the Algorithm interface and is used to +encapsulate the functionality, common to all non-linear optimization algorithms in the optim +module. + +@note term criteria should meet following condition: +@code + termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) && termcrit.epsilon > 0 && termcrit.maxCount > 0 +@endcode + */ +class CV_EXPORTS DownhillSolver : public MinProblemSolver +{ +public: + /** @brief Returns the initial step that will be used in downhill simplex algorithm. + + @param step Initial step that will be used in algorithm. Note, that although corresponding setter + accepts column-vectors as well as row-vectors, this method will return a row-vector. + @see DownhillSolver::setInitStep + */ + virtual void getInitStep(OutputArray step) const=0; + + /** @brief Sets the initial step that will be used in downhill simplex algorithm. + + Step, together with initial point (given in DownhillSolver::minimize) are two `n`-dimensional + vectors that are used to determine the shape of initial simplex. Roughly said, initial point + determines the position of a simplex (it will become simplex's centroid), while step determines the + spread (size in each dimension) of a simplex. To be more precise, if \f$s,x_0\in\mathbb{R}^n\f$ are + the initial step and initial point respectively, the vertices of a simplex will be: + \f$v_0:=x_0-\frac{1}{2} s\f$ and \f$v_i:=x_0+s_i\f$ for \f$i=1,2,\dots,n\f$ where \f$s_i\f$ denotes + projections of the initial step of *n*-th coordinate (the result of projection is treated to be + vector given by \f$s_i:=e_i\cdot\left\f$, where \f$e_i\f$ form canonical basis) + + @param step Initial step that will be used in algorithm. Roughly said, it determines the spread + (size in each dimension) of an initial simplex. + */ + virtual void setInitStep(InputArray step)=0; + + /** @brief This function returns the reference to the ready-to-use DownhillSolver object. + + All the parameters are optional, so this procedure can be called even without parameters at + all. In this case, the default values will be used. As default value for terminal criteria are + the only sensible ones, MinProblemSolver::setFunction() and DownhillSolver::setInitStep() + should be called upon the obtained object, if the respective parameters were not given to + create(). Otherwise, the two ways (give parameters to createDownhillSolver() or miss them out + and call the MinProblemSolver::setFunction() and DownhillSolver::setInitStep()) are absolutely + equivalent (and will drop the same errors in the same way, should invalid input be detected). + @param f Pointer to the function that will be minimized, similarly to the one you submit via + MinProblemSolver::setFunction. + @param initStep Initial step, that will be used to construct the initial simplex, similarly to the one + you submit via MinProblemSolver::setInitStep. + @param termcrit Terminal criteria to the algorithm, similarly to the one you submit via + MinProblemSolver::setTermCriteria. + */ + static Ptr create(const Ptr& f=Ptr(), + InputArray initStep=Mat_(1,1,0.0), + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001)); +}; + +/** @brief This class is used to perform the non-linear non-constrained minimization of a function +with known gradient, + +defined on an *n*-dimensional Euclidean space, using the **Nonlinear Conjugate Gradient method**. +The implementation was done based on the beautifully clear explanatory article [An Introduction to +the Conjugate Gradient Method Without the Agonizing +Pain](http://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf) by Jonathan Richard +Shewchuk. The method can be seen as an adaptation of a standard Conjugate Gradient method (see, for +example ) for numerically solving the +systems of linear equations. + +It should be noted, that this method, although deterministic, is rather a heuristic method and +therefore may converge to a local minima, not necessary a global one. What is even more disastrous, +most of its behaviour is ruled by gradient, therefore it essentially cannot distinguish between +local minima and maxima. Therefore, if it starts sufficiently near to the local maximum, it may +converge to it. Another obvious restriction is that it should be possible to compute the gradient of +a function at any point, thus it is preferable to have analytic expression for gradient and +computational burden should be born by the user. + +The latter responsibility is accomplished via the getGradient method of a +MinProblemSolver::Function interface (which represents function being optimized). This method takes +point a point in *n*-dimensional space (first argument represents the array of coordinates of that +point) and compute its gradient (it should be stored in the second argument as an array). + +@note class ConjGradSolver thus does not add any new methods to the basic MinProblemSolver interface. + +@note term criteria should meet following condition: +@code + termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) && termcrit.epsilon > 0 && termcrit.maxCount > 0 + // or + termcrit.type == TermCriteria::MAX_ITER) && termcrit.maxCount > 0 +@endcode + */ +class CV_EXPORTS ConjGradSolver : public MinProblemSolver +{ +public: + /** @brief This function returns the reference to the ready-to-use ConjGradSolver object. + + All the parameters are optional, so this procedure can be called even without parameters at + all. In this case, the default values will be used. As default value for terminal criteria are + the only sensible ones, MinProblemSolver::setFunction() should be called upon the obtained + object, if the function was not given to create(). Otherwise, the two ways (submit it to + create() or miss it out and call the MinProblemSolver::setFunction()) are absolutely equivalent + (and will drop the same errors in the same way, should invalid input be detected). + @param f Pointer to the function that will be minimized, similarly to the one you submit via + MinProblemSolver::setFunction. + @param termcrit Terminal criteria to the algorithm, similarly to the one you submit via + MinProblemSolver::setTermCriteria. + */ + static Ptr create(const Ptr& f=Ptr(), + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001)); +}; + +//! return codes for cv::solveLP() function +enum SolveLPResult +{ + SOLVELP_UNBOUNDED = -2, //!< problem is unbounded (target function can achieve arbitrary high values) + SOLVELP_UNFEASIBLE = -1, //!< problem is unfeasible (there are no points that satisfy all the constraints imposed) + SOLVELP_SINGLE = 0, //!< there is only one maximum for target function + SOLVELP_MULTI = 1 //!< there are multiple maxima for target function - the arbitrary one is returned +}; + +/** @brief Solve given (non-integer) linear programming problem using the Simplex Algorithm (Simplex Method). + +What we mean here by "linear programming problem" (or LP problem, for short) can be formulated as: + +\f[\mbox{Maximize } c\cdot x\\ + \mbox{Subject to:}\\ + Ax\leq b\\ + x\geq 0\f] + +Where \f$c\f$ is fixed `1`-by-`n` row-vector, \f$A\f$ is fixed `m`-by-`n` matrix, \f$b\f$ is fixed `m`-by-`1` +column vector and \f$x\f$ is an arbitrary `n`-by-`1` column vector, which satisfies the constraints. + +Simplex algorithm is one of many algorithms that are designed to handle this sort of problems +efficiently. Although it is not optimal in theoretical sense (there exist algorithms that can solve +any problem written as above in polynomial time, while simplex method degenerates to exponential +time for some special cases), it is well-studied, easy to implement and is shown to work well for +real-life purposes. + +The particular implementation is taken almost verbatim from **Introduction to Algorithms, third +edition** by T. H. Cormen, C. E. Leiserson, R. L. Rivest and Clifford Stein. In particular, the +Bland's rule is used to prevent cycling. + +@param Func This row-vector corresponds to \f$c\f$ in the LP problem formulation (see above). It should +contain 32- or 64-bit floating point numbers. As a convenience, column-vector may be also submitted, +in the latter case it is understood to correspond to \f$c^T\f$. +@param Constr `m`-by-`n+1` matrix, whose rightmost column corresponds to \f$b\f$ in formulation above +and the remaining to \f$A\f$. It should contain 32- or 64-bit floating point numbers. +@param z The solution will be returned here as a column-vector - it corresponds to \f$c\f$ in the +formulation above. It will contain 64-bit floating point numbers. +@return One of cv::SolveLPResult + */ +CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z); + +//! @} + +}// cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/ovx.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/ovx.hpp new file mode 100644 index 0000000..8bb7d54 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/ovx.hpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2016, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. + +// OpenVX related definitions and declarations + +#pragma once +#ifndef OPENCV_OVX_HPP +#define OPENCV_OVX_HPP + +#include "cvdef.h" + +namespace cv +{ +/// Check if use of OpenVX is possible +CV_EXPORTS_W bool haveOpenVX(); + +/// Check if use of OpenVX is enabled +CV_EXPORTS_W bool useOpenVX(); + +/// Enable/disable use of OpenVX +CV_EXPORTS_W void setUseOpenVX(bool flag); +} // namespace cv + +#endif // OPENCV_OVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/persistence.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/persistence.hpp new file mode 100644 index 0000000..1194885 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/persistence.hpp @@ -0,0 +1,1372 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_PERSISTENCE_HPP +#define OPENCV_CORE_PERSISTENCE_HPP + +#ifndef CV_DOXYGEN +/// Define to support persistence legacy formats +#define CV__LEGACY_PERSISTENCE +#endif + +#ifndef __cplusplus +# error persistence.hpp header must be compiled as C++ +#endif + +//! @addtogroup core_c +//! @{ + +/** @brief "black box" representation of the file storage associated with a file on disk. + +Several functions that are described below take CvFileStorage\* as inputs and allow the user to +save or to load hierarchical collections that consist of scalar values, standard CXCore objects +(such as matrices, sequences, graphs), and user-defined objects. + +OpenCV can read and write data in XML (), YAML () or +JSON () formats. Below is an example of 3x3 floating-point identity matrix A, +stored in XML and YAML files +using CXCore functions: +XML: +@code{.xml} + + + + 3 + 3 +
f
+ 1. 0. 0. 0. 1. 0. 0. 0. 1. +
+
+@endcode +YAML: +@code{.yaml} + %YAML:1.0 + A: !!opencv-matrix + rows: 3 + cols: 3 + dt: f + data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1.] +@endcode +As it can be seen from the examples, XML uses nested tags to represent hierarchy, while YAML uses +indentation for that purpose (similar to the Python programming language). + +The same functions can read and write data in both formats; the particular format is determined by +the extension of the opened file, ".xml" for XML files, ".yml" or ".yaml" for YAML and ".json" for +JSON. + */ +typedef struct CvFileStorage CvFileStorage; +typedef struct CvFileNode CvFileNode; +typedef struct CvMat CvMat; +typedef struct CvMatND CvMatND; + +//! @} core_c + +#include "opencv2/core/types.hpp" +#include "opencv2/core/mat.hpp" + +namespace cv { + +/** @addtogroup core_xml + +XML/YAML/JSON file storages. {#xml_storage} +======================= +Writing to a file storage. +-------------------------- +You can store and then restore various OpenCV data structures to/from XML (), +YAML () or JSON () formats. Also, it is possible to store +and load arbitrarily complex data structures, which include OpenCV data structures, as well as +primitive data types (integer and floating-point numbers and text strings) as their elements. + +Use the following procedure to write something to XML, YAML or JSON: +-# Create new FileStorage and open it for writing. It can be done with a single call to +FileStorage::FileStorage constructor that takes a filename, or you can use the default constructor +and then call FileStorage::open. Format of the file (XML, YAML or JSON) is determined from the filename +extension (".xml", ".yml"/".yaml" and ".json", respectively) +-# Write all the data you want using the streaming operator `<<`, just like in the case of STL +streams. +-# Close the file using FileStorage::release. FileStorage destructor also closes the file. + +Here is an example: +@code + #include "opencv2/opencv.hpp" + #include + + using namespace cv; + + int main(int, char** argv) + { + FileStorage fs("test.yml", FileStorage::WRITE); + + fs << "frameCount" << 5; + time_t rawtime; time(&rawtime); + fs << "calibrationDate" << asctime(localtime(&rawtime)); + Mat cameraMatrix = (Mat_(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); + Mat distCoeffs = (Mat_(5,1) << 0.1, 0.01, -0.001, 0, 0); + fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs; + fs << "features" << "["; + for( int i = 0; i < 3; i++ ) + { + int x = rand() % 640; + int y = rand() % 480; + uchar lbp = rand() % 256; + + fs << "{:" << "x" << x << "y" << y << "lbp" << "[:"; + for( int j = 0; j < 8; j++ ) + fs << ((lbp >> j) & 1); + fs << "]" << "}"; + } + fs << "]"; + fs.release(); + return 0; + } +@endcode +The sample above stores to YML an integer, a text string (calibration date), 2 matrices, and a custom +structure "feature", which includes feature coordinates and LBP (local binary pattern) value. Here +is output of the sample: +@code{.yaml} +%YAML:1.0 +frameCount: 5 +calibrationDate: "Fri Jun 17 14:09:29 2011\n" +cameraMatrix: !!opencv-matrix + rows: 3 + cols: 3 + dt: d + data: [ 1000., 0., 320., 0., 1000., 240., 0., 0., 1. ] +distCoeffs: !!opencv-matrix + rows: 5 + cols: 1 + dt: d + data: [ 1.0000000000000001e-01, 1.0000000000000000e-02, + -1.0000000000000000e-03, 0., 0. ] +features: + - { x:167, y:49, lbp:[ 1, 0, 0, 1, 1, 0, 1, 1 ] } + - { x:298, y:130, lbp:[ 0, 0, 0, 1, 0, 0, 1, 1 ] } + - { x:344, y:158, lbp:[ 1, 1, 0, 0, 0, 0, 1, 0 ] } +@endcode + +As an exercise, you can replace ".yml" with ".xml" or ".json" in the sample above and see, how the +corresponding XML file will look like. + +Several things can be noted by looking at the sample code and the output: + +- The produced YAML (and XML/JSON) consists of heterogeneous collections that can be nested. There are + 2 types of collections: named collections (mappings) and unnamed collections (sequences). In mappings + each element has a name and is accessed by name. This is similar to structures and std::map in + C/C++ and dictionaries in Python. In sequences elements do not have names, they are accessed by + indices. This is similar to arrays and std::vector in C/C++ and lists, tuples in Python. + "Heterogeneous" means that elements of each single collection can have different types. + + Top-level collection in YAML/XML/JSON is a mapping. Each matrix is stored as a mapping, and the matrix + elements are stored as a sequence. Then, there is a sequence of features, where each feature is + represented a mapping, and lbp value in a nested sequence. + +- When you write to a mapping (a structure), you write element name followed by its value. When you + write to a sequence, you simply write the elements one by one. OpenCV data structures (such as + cv::Mat) are written in absolutely the same way as simple C data structures - using `<<` + operator. + +- To write a mapping, you first write the special string `{` to the storage, then write the + elements as pairs (`fs << << `) and then write the closing + `}`. + +- To write a sequence, you first write the special string `[`, then write the elements, then + write the closing `]`. + +- In YAML/JSON (but not XML), mappings and sequences can be written in a compact Python-like inline + form. In the sample above matrix elements, as well as each feature, including its lbp value, is + stored in such inline form. To store a mapping/sequence in a compact form, put `:` after the + opening character, e.g. use `{:` instead of `{` and `[:` instead of `[`. When the + data is written to XML, those extra `:` are ignored. + +Reading data from a file storage. +--------------------------------- +To read the previously written XML, YAML or JSON file, do the following: +-# Open the file storage using FileStorage::FileStorage constructor or FileStorage::open method. + In the current implementation the whole file is parsed and the whole representation of file + storage is built in memory as a hierarchy of file nodes (see FileNode) + +-# Read the data you are interested in. Use FileStorage::operator [], FileNode::operator [] + and/or FileNodeIterator. + +-# Close the storage using FileStorage::release. + +Here is how to read the file created by the code sample above: +@code + FileStorage fs2("test.yml", FileStorage::READ); + + // first method: use (type) operator on FileNode. + int frameCount = (int)fs2["frameCount"]; + + String date; + // second method: use FileNode::operator >> + fs2["calibrationDate"] >> date; + + Mat cameraMatrix2, distCoeffs2; + fs2["cameraMatrix"] >> cameraMatrix2; + fs2["distCoeffs"] >> distCoeffs2; + + cout << "frameCount: " << frameCount << endl + << "calibration date: " << date << endl + << "camera matrix: " << cameraMatrix2 << endl + << "distortion coeffs: " << distCoeffs2 << endl; + + FileNode features = fs2["features"]; + FileNodeIterator it = features.begin(), it_end = features.end(); + int idx = 0; + std::vector lbpval; + + // iterate through a sequence using FileNodeIterator + for( ; it != it_end; ++it, idx++ ) + { + cout << "feature #" << idx << ": "; + cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: ("; + // you can also easily read numerical arrays using FileNode >> std::vector operator. + (*it)["lbp"] >> lbpval; + for( int i = 0; i < (int)lbpval.size(); i++ ) + cout << " " << (int)lbpval[i]; + cout << ")" << endl; + } + fs2.release(); +@endcode + +Format specification {#format_spec} +-------------------- +`([count]{u|c|w|s|i|f|d})`... where the characters correspond to fundamental C++ types: +- `u` 8-bit unsigned number +- `c` 8-bit signed number +- `w` 16-bit unsigned number +- `s` 16-bit signed number +- `i` 32-bit signed number +- `f` single precision floating-point number +- `d` double precision floating-point number +- `r` pointer, 32 lower bits of which are written as a signed integer. The type can be used to + store structures with links between the elements. + +`count` is the optional counter of values of a given type. For example, `2if` means that each array +element is a structure of 2 integers, followed by a single-precision floating-point number. The +equivalent notations of the above specification are `iif`, `2i1f` and so forth. Other examples: `u` +means that the array consists of bytes, and `2d` means the array consists of pairs of doubles. + +@see @ref samples/cpp/filestorage.cpp +*/ + +//! @{ + +/** @example samples/cpp/filestorage.cpp +A complete example using the FileStorage interface +*/ + +////////////////////////// XML & YAML I/O ////////////////////////// + +class CV_EXPORTS FileNode; +class CV_EXPORTS FileNodeIterator; + +/** @brief XML/YAML/JSON file storage class that encapsulates all the information necessary for writing or +reading data to/from a file. + */ +class CV_EXPORTS_W FileStorage +{ +public: + //! file storage mode + enum Mode + { + READ = 0, //!< value, open the file for reading + WRITE = 1, //!< value, open the file for writing + APPEND = 2, //!< value, open the file for appending + MEMORY = 4, //!< flag, read data from source or write data to the internal buffer (which is + //!< returned by FileStorage::release) + FORMAT_MASK = (7<<3), //!< mask for format flags + FORMAT_AUTO = 0, //!< flag, auto format + FORMAT_XML = (1<<3), //!< flag, XML format + FORMAT_YAML = (2<<3), //!< flag, YAML format + FORMAT_JSON = (3<<3), //!< flag, JSON format + + BASE64 = 64, //!< flag, write rawdata in Base64 by default. (consider using WRITE_BASE64) + WRITE_BASE64 = BASE64 | WRITE, //!< flag, enable both WRITE and BASE64 + }; + enum + { + UNDEFINED = 0, + VALUE_EXPECTED = 1, + NAME_EXPECTED = 2, + INSIDE_MAP = 4 + }; + + /** @brief The constructors. + + The full constructor opens the file. Alternatively you can use the default constructor and then + call FileStorage::open. + */ + CV_WRAP FileStorage(); + + /** @overload + @copydoc open() + */ + CV_WRAP FileStorage(const String& filename, int flags, const String& encoding=String()); + + /** @overload */ + FileStorage(CvFileStorage* fs, bool owning=true); + + //! the destructor. calls release() + virtual ~FileStorage(); + + /** @brief Opens a file. + + See description of parameters in FileStorage::FileStorage. The method calls FileStorage::release + before opening the file. + @param filename Name of the file to open or the text string to read the data from. + Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON + respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz. If both + FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify + the output file format (e.g. mydata.xml, .yml etc.). A file name can also contain parameters. + You can use this format, "*?base64" (e.g. "file.json?base64" (case sensitive)), as an alternative to + FileStorage::BASE64 flag. + @param flags Mode of operation. One of FileStorage::Mode + @param encoding Encoding of the file. Note that UTF-16 XML encoding is not supported currently and + you should use 8-bit encoding instead of it. + */ + CV_WRAP virtual bool open(const String& filename, int flags, const String& encoding=String()); + + /** @brief Checks whether the file is opened. + + @returns true if the object is associated with the current file and false otherwise. It is a + good practice to call this method after you tried to open a file. + */ + CV_WRAP virtual bool isOpened() const; + + /** @brief Closes the file and releases all the memory buffers. + + Call this method after all I/O operations with the storage are finished. + */ + CV_WRAP virtual void release(); + + /** @brief Closes the file and releases all the memory buffers. + + Call this method after all I/O operations with the storage are finished. If the storage was + opened for writing data and FileStorage::WRITE was specified + */ + CV_WRAP virtual String releaseAndGetString(); + + /** @brief Returns the first element of the top-level mapping. + @returns The first element of the top-level mapping. + */ + CV_WRAP FileNode getFirstTopLevelNode() const; + + /** @brief Returns the top-level mapping + @param streamidx Zero-based index of the stream. In most cases there is only one stream in the file. + However, YAML supports multiple streams and so there can be several. + @returns The top-level mapping. + */ + CV_WRAP FileNode root(int streamidx=0) const; + + /** @brief Returns the specified element of the top-level mapping. + @param nodename Name of the file node. + @returns Node with the given name. + */ + FileNode operator[](const String& nodename) const; + + /** @overload */ + CV_WRAP_AS(getNode) FileNode operator[](const char* nodename) const; + + /** @brief Returns the obsolete C FileStorage structure. + @returns Pointer to the underlying C FileStorage structure + */ + CvFileStorage* operator *() { return fs.get(); } + + /** @overload */ + const CvFileStorage* operator *() const { return fs.get(); } + + /** @brief Writes multiple numbers. + + Writes one or more numbers of the specified format to the currently written structure. Usually it is + more convenient to use operator `<<` instead of this method. + @param fmt Specification of each array element, see @ref format_spec "format specification" + @param vec Pointer to the written array. + @param len Number of the uchar elements to write. + */ + void writeRaw( const String& fmt, const uchar* vec, size_t len ); + + /** @brief Writes the registered C structure (CvMat, CvMatND, CvSeq). + @param name Name of the written object. + @param obj Pointer to the object. + @see ocvWrite for details. + */ + void writeObj( const String& name, const void* obj ); + + /** + * @brief Simplified writing API to use with bindings. + * @param name Name of the written object + * @param val Value of the written object + */ + CV_WRAP void write(const String& name, int val); + /// @overload + CV_WRAP void write(const String& name, double val); + /// @overload + CV_WRAP void write(const String& name, const String& val); + /// @overload + CV_WRAP void write(const String& name, InputArray val); + + /** @brief Writes a comment. + + The function writes a comment into file storage. The comments are skipped when the storage is read. + @param comment The written comment, single-line or multi-line + @param append If true, the function tries to put the comment at the end of current line. + Else if the comment is multi-line, or if it does not fit at the end of the current + line, the comment starts a new line. + */ + CV_WRAP void writeComment(const String& comment, bool append = false); + + /** @brief Returns the normalized object name for the specified name of a file. + @param filename Name of a file + @returns The normalized object name. + */ + static String getDefaultObjectName(const String& filename); + + /** @brief Returns the current format. + * @returns The current format, see FileStorage::Mode + */ + CV_WRAP int getFormat() const; + + Ptr fs; //!< the underlying C FileStorage structure + String elname; //!< the currently written element + std::vector structs; //!< the stack of written structures + int state; //!< the writer state +}; + +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvFileStorage* obj) const; + +/** @brief File Storage Node class. + +The node is used to store each and every element of the file storage opened for reading. When +XML/YAML file is read, it is first parsed and stored in the memory as a hierarchical collection of +nodes. Each node can be a "leaf" that is contain a single number or a string, or be a collection of +other nodes. There can be named collections (mappings) where each element has a name and it is +accessed by a name, and ordered collections (sequences) where elements do not have names but rather +accessed by index. Type of the file node can be determined using FileNode::type method. + +Note that file nodes are only used for navigating file storages opened for reading. When a file +storage is opened for writing, no data is stored in memory after it is written. + */ +class CV_EXPORTS_W_SIMPLE FileNode +{ +public: + //! type of the file storage node + enum Type + { + NONE = 0, //!< empty node + INT = 1, //!< an integer + REAL = 2, //!< floating-point number + FLOAT = REAL, //!< synonym or REAL + STR = 3, //!< text string in UTF-8 encoding + STRING = STR, //!< synonym for STR + REF = 4, //!< integer of size size_t. Typically used for storing complex dynamic structures where some elements reference the others + SEQ = 5, //!< sequence + MAP = 6, //!< mapping + TYPE_MASK = 7, + FLOW = 8, //!< compact representation of a sequence or mapping. Used only by YAML writer + USER = 16, //!< a registered object (e.g. a matrix) + EMPTY = 32, //!< empty structure (sequence or mapping) + NAMED = 64 //!< the node has a name (i.e. it is element of a mapping) + }; + /** @brief The constructors. + + These constructors are used to create a default file node, construct it from obsolete structures or + from the another file node. + */ + CV_WRAP FileNode(); + + /** @overload + @param fs Pointer to the obsolete file storage structure. + @param node File node to be used as initialization for the created file node. + */ + FileNode(const CvFileStorage* fs, const CvFileNode* node); + + /** @overload + @param node File node to be used as initialization for the created file node. + */ + FileNode(const FileNode& node); + + FileNode& operator=(const FileNode& node); + + /** @brief Returns element of a mapping node or a sequence node. + @param nodename Name of an element in the mapping node. + @returns Returns the element with the given identifier. + */ + FileNode operator[](const String& nodename) const; + + /** @overload + @param nodename Name of an element in the mapping node. + */ + CV_WRAP_AS(getNode) FileNode operator[](const char* nodename) const; + + /** @overload + @param i Index of an element in the sequence node. + */ + CV_WRAP_AS(at) FileNode operator[](int i) const; + + /** @brief Returns keys of a mapping node. + @returns Keys of a mapping node. + */ + CV_WRAP std::vector keys() const; + + /** @brief Returns type of the node. + @returns Type of the node. See FileNode::Type + */ + CV_WRAP int type() const; + + //! returns true if the node is empty + CV_WRAP bool empty() const; + //! returns true if the node is a "none" object + CV_WRAP bool isNone() const; + //! returns true if the node is a sequence + CV_WRAP bool isSeq() const; + //! returns true if the node is a mapping + CV_WRAP bool isMap() const; + //! returns true if the node is an integer + CV_WRAP bool isInt() const; + //! returns true if the node is a floating-point number + CV_WRAP bool isReal() const; + //! returns true if the node is a text string + CV_WRAP bool isString() const; + //! returns true if the node has a name + CV_WRAP bool isNamed() const; + //! returns the node name or an empty string if the node is nameless + CV_WRAP String name() const; + //! returns the number of elements in the node, if it is a sequence or mapping, or 1 otherwise. + CV_WRAP size_t size() const; + //! returns the node content as an integer. If the node stores floating-point number, it is rounded. + operator int() const; + //! returns the node content as float + operator float() const; + //! returns the node content as double + operator double() const; + //! returns the node content as text string + operator String() const; + operator std::string() const; + + //! returns pointer to the underlying file node + CvFileNode* operator *(); + //! returns pointer to the underlying file node + const CvFileNode* operator* () const; + + //! returns iterator pointing to the first node element + FileNodeIterator begin() const; + //! returns iterator pointing to the element following the last node element + FileNodeIterator end() const; + + /** @brief Reads node elements to the buffer with the specified format. + + Usually it is more convenient to use operator `>>` instead of this method. + @param fmt Specification of each array element. See @ref format_spec "format specification" + @param vec Pointer to the destination array. + @param len Number of bytes to read (buffer size limit). If it is greater than number of + remaining elements then all of them will be read. + */ + void readRaw( const String& fmt, uchar* vec, size_t len ) const; + + //! reads the registered object and returns pointer to it + void* readObj() const; + + //! Simplified reading API to use with bindings. + CV_WRAP double real() const; + //! Simplified reading API to use with bindings. + CV_WRAP String string() const; + //! Simplified reading API to use with bindings. + CV_WRAP Mat mat() const; + + // do not use wrapper pointer classes for better efficiency + const CvFileStorage* fs; + const CvFileNode* node; +}; + + +/** @brief used to iterate through sequences and mappings. + +A standard STL notation, with node.begin(), node.end() denoting the beginning and the end of a +sequence, stored in node. See the data reading sample in the beginning of the section. + */ +class CV_EXPORTS FileNodeIterator +{ +public: + /** @brief The constructors. + + These constructors are used to create a default iterator, set it to specific element in a file node + or construct it from another iterator. + */ + FileNodeIterator(); + + /** @overload + @param fs File storage for the iterator. + @param node File node for the iterator. + @param ofs Index of the element in the node. The created iterator will point to this element. + */ + FileNodeIterator(const CvFileStorage* fs, const CvFileNode* node, size_t ofs=0); + + /** @overload + @param it Iterator to be used as initialization for the created iterator. + */ + FileNodeIterator(const FileNodeIterator& it); + + FileNodeIterator& operator=(const FileNodeIterator& it); + + //! returns the currently observed element + FileNode operator *() const; + //! accesses the currently observed element methods + FileNode operator ->() const; + + //! moves iterator to the next node + FileNodeIterator& operator ++ (); + //! moves iterator to the next node + FileNodeIterator operator ++ (int); + //! moves iterator to the previous node + FileNodeIterator& operator -- (); + //! moves iterator to the previous node + FileNodeIterator operator -- (int); + //! moves iterator forward by the specified offset (possibly negative) + FileNodeIterator& operator += (int ofs); + //! moves iterator backward by the specified offset (possibly negative) + FileNodeIterator& operator -= (int ofs); + + /** @brief Reads node elements to the buffer with the specified format. + + Usually it is more convenient to use operator `>>` instead of this method. + @param fmt Specification of each array element. See @ref format_spec "format specification" + @param vec Pointer to the destination array. + @param len Number of bytes to read (buffer size limit). If it is greater than number of + remaining elements then all of them will be read. + + */ + FileNodeIterator& readRaw( const String& fmt, uchar* vec, + size_t len=(size_t)INT_MAX ); + + struct SeqReader + { + int header_size; + void* seq; /* sequence, beign read; CvSeq */ + void* block; /* current block; CvSeqBlock */ + schar* ptr; /* pointer to element be read next */ + schar* block_min; /* pointer to the beginning of block */ + schar* block_max; /* pointer to the end of block */ + int delta_index;/* = seq->first->start_index */ + schar* prev_elem; /* pointer to previous element */ + }; + + const CvFileStorage* fs; + const CvFileNode* container; + SeqReader reader; + size_t remaining; +}; + +//! @} core_xml + +/////////////////// XML & YAML I/O implementation ////////////////// + +//! @relates cv::FileStorage +//! @{ + +CV_EXPORTS void write( FileStorage& fs, const String& name, int value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, float value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, double value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const String& value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const Mat& value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const SparseMat& value ); +#ifdef CV__LEGACY_PERSISTENCE +CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector& value); +CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector& value); +#endif + +CV_EXPORTS void writeScalar( FileStorage& fs, int value ); +CV_EXPORTS void writeScalar( FileStorage& fs, float value ); +CV_EXPORTS void writeScalar( FileStorage& fs, double value ); +CV_EXPORTS void writeScalar( FileStorage& fs, const String& value ); + +//! @} + +//! @relates cv::FileNode +//! @{ + +CV_EXPORTS void read(const FileNode& node, int& value, int default_value); +CV_EXPORTS void read(const FileNode& node, float& value, float default_value); +CV_EXPORTS void read(const FileNode& node, double& value, double default_value); +CV_EXPORTS void read(const FileNode& node, String& value, const String& default_value); +CV_EXPORTS void read(const FileNode& node, std::string& value, const std::string& default_value); +CV_EXPORTS void read(const FileNode& node, Mat& mat, const Mat& default_mat = Mat() ); +CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat = SparseMat() ); +#ifdef CV__LEGACY_PERSISTENCE +CV_EXPORTS void read(const FileNode& node, std::vector& keypoints); +CV_EXPORTS void read(const FileNode& node, std::vector& matches); +#endif +CV_EXPORTS void read(const FileNode& node, KeyPoint& value, const KeyPoint& default_value); +CV_EXPORTS void read(const FileNode& node, DMatch& value, const DMatch& default_value); + +template static inline void read(const FileNode& node, Point_<_Tp>& value, const Point_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Point_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Point3_<_Tp>& value, const Point3_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 3 ? default_value : Point3_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2])); +} + +template static inline void read(const FileNode& node, Size_<_Tp>& value, const Size_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Size_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Complex<_Tp>& value, const Complex<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Complex<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Rect_<_Tp>& value, const Rect_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 4 ? default_value : Rect_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2]), saturate_cast<_Tp>(temp[3])); +} + +template static inline void read(const FileNode& node, Vec<_Tp, cn>& value, const Vec<_Tp, cn>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != cn ? default_value : Vec<_Tp, cn>(&temp[0]); +} + +template static inline void read(const FileNode& node, Scalar_<_Tp>& value, const Scalar_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 4 ? default_value : Scalar_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2]), saturate_cast<_Tp>(temp[3])); +} + +static inline void read(const FileNode& node, Range& value, const Range& default_value) +{ + Point2i temp(value.start, value.end); const Point2i default_temp = Point2i(default_value.start, default_value.end); + read(node, temp, default_temp); + value.start = temp.x; value.end = temp.y; +} + +//! @} + +/** @brief Writes string to a file storage. +@relates cv::FileStorage + */ +CV_EXPORTS FileStorage& operator << (FileStorage& fs, const String& str); + +//! @cond IGNORED + +namespace internal +{ + class CV_EXPORTS WriteStructContext + { + public: + WriteStructContext(FileStorage& _fs, const String& name, int flags, const String& typeName = String()); + ~WriteStructContext(); + private: + FileStorage* fs; + }; + + template class VecWriterProxy + { + public: + VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} + void operator()(const std::vector<_Tp>& vec) const + { + size_t count = vec.size(); + for (size_t i = 0; i < count; i++) + write(*fs, vec[i]); + } + private: + FileStorage* fs; + }; + + template class VecWriterProxy<_Tp, 1> + { + public: + VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} + void operator()(const std::vector<_Tp>& vec) const + { + int _fmt = traits::SafeFmt<_Tp>::fmt; + char fmt[] = { (char)((_fmt >> 8) + '1'), (char)_fmt, '\0' }; + fs->writeRaw(fmt, !vec.empty() ? (uchar*)&vec[0] : 0, vec.size() * sizeof(_Tp)); + } + private: + FileStorage* fs; + }; + + template class VecReaderProxy + { + public: + VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} + void operator()(std::vector<_Tp>& vec, size_t count) const + { + count = std::min(count, it->remaining); + vec.resize(count); + for (size_t i = 0; i < count; i++, ++(*it)) + read(**it, vec[i], _Tp()); + } + private: + FileNodeIterator* it; + }; + + template class VecReaderProxy<_Tp, 1> + { + public: + VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} + void operator()(std::vector<_Tp>& vec, size_t count) const + { + size_t remaining = it->remaining; + size_t cn = DataType<_Tp>::channels; + int _fmt = traits::SafeFmt<_Tp>::fmt; + CV_Assert((_fmt >> 8) < 9); + char fmt[] = { (char)((_fmt >> 8)+'1'), (char)_fmt, '\0' }; + CV_Assert((remaining % cn) == 0); + size_t remaining1 = remaining / cn; + count = count < remaining1 ? count : remaining1; + vec.resize(count); + it->readRaw(fmt, !vec.empty() ? (uchar*)&vec[0] : 0, count*sizeof(_Tp)); + } + private: + FileNodeIterator* it; + }; + +} // internal + +//! @endcond + +//! @relates cv::FileStorage +//! @{ + +template static inline +void write(FileStorage& fs, const _Tp& value) +{ + write(fs, String(), value); +} + +template<> inline +void write( FileStorage& fs, const int& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const float& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const double& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const String& value ) +{ + writeScalar(fs, value); +} + +template static inline +void write(FileStorage& fs, const Point_<_Tp>& pt ) +{ + write(fs, pt.x); + write(fs, pt.y); +} + +template static inline +void write(FileStorage& fs, const Point3_<_Tp>& pt ) +{ + write(fs, pt.x); + write(fs, pt.y); + write(fs, pt.z); +} + +template static inline +void write(FileStorage& fs, const Size_<_Tp>& sz ) +{ + write(fs, sz.width); + write(fs, sz.height); +} + +template static inline +void write(FileStorage& fs, const Complex<_Tp>& c ) +{ + write(fs, c.re); + write(fs, c.im); +} + +template static inline +void write(FileStorage& fs, const Rect_<_Tp>& r ) +{ + write(fs, r.x); + write(fs, r.y); + write(fs, r.width); + write(fs, r.height); +} + +template static inline +void write(FileStorage& fs, const Vec<_Tp, cn>& v ) +{ + for(int i = 0; i < cn; i++) + write(fs, v.val[i]); +} + +template static inline +void write(FileStorage& fs, const Scalar_<_Tp>& s ) +{ + write(fs, s.val[0]); + write(fs, s.val[1]); + write(fs, s.val[2]); + write(fs, s.val[3]); +} + +static inline +void write(FileStorage& fs, const Range& r ) +{ + write(fs, r.start); + write(fs, r.end); +} + +template static inline +void write( FileStorage& fs, const std::vector<_Tp>& vec ) +{ + cv::internal::VecWriterProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> w(&fs); + w(vec); +} + +template static inline +void write(FileStorage& fs, const String& name, const Point_<_Tp>& pt ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, pt); +} + +template static inline +void write(FileStorage& fs, const String& name, const Point3_<_Tp>& pt ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, pt); +} + +template static inline +void write(FileStorage& fs, const String& name, const Size_<_Tp>& sz ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, sz); +} + +template static inline +void write(FileStorage& fs, const String& name, const Complex<_Tp>& c ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, c); +} + +template static inline +void write(FileStorage& fs, const String& name, const Rect_<_Tp>& r ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, r); +} + +template static inline +void write(FileStorage& fs, const String& name, const Vec<_Tp, cn>& v ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, v); +} + +template static inline +void write(FileStorage& fs, const String& name, const Scalar_<_Tp>& s ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, s); +} + +static inline +void write(FileStorage& fs, const String& name, const Range& r ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, r); +} + +static inline +void write(FileStorage& fs, const String& name, const KeyPoint& kpt) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, kpt.pt.x); + write(fs, kpt.pt.y); + write(fs, kpt.size); + write(fs, kpt.angle); + write(fs, kpt.response); + write(fs, kpt.octave); + write(fs, kpt.class_id); +} + +static inline +void write(FileStorage& fs, const String& name, const DMatch& m) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, m.queryIdx); + write(fs, m.trainIdx); + write(fs, m.imgIdx); + write(fs, m.distance); +} + +template static inline +void write( FileStorage& fs, const String& name, const std::vector<_Tp>& vec ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+(traits::SafeFmt<_Tp>::fmt != 0 ? FileNode::FLOW : 0)); + write(fs, vec); +} + +template static inline +void write( FileStorage& fs, const String& name, const std::vector< std::vector<_Tp> >& vec ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ); + for(size_t i = 0; i < vec.size(); i++) + { + cv::internal::WriteStructContext ws_(fs, name, FileNode::SEQ+(traits::SafeFmt<_Tp>::fmt != 0 ? FileNode::FLOW : 0)); + write(fs, vec[i]); + } +} + +#ifdef CV__LEGACY_PERSISTENCE +// This code is not needed anymore, but it is preserved here to keep source compatibility +// Implementation is similar to templates instantiations +static inline void write(FileStorage& fs, const KeyPoint& kpt) { write(fs, String(), kpt); } +static inline void write(FileStorage& fs, const DMatch& m) { write(fs, String(), m); } +static inline void write(FileStorage& fs, const std::vector& vec) +{ + cv::internal::VecWriterProxy w(&fs); + w(vec); +} +static inline void write(FileStorage& fs, const std::vector& vec) +{ + cv::internal::VecWriterProxy w(&fs); + w(vec); + +} +#endif + +//! @} FileStorage + +//! @relates cv::FileNode +//! @{ + +static inline +void read(const FileNode& node, bool& value, bool default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = temp != 0; +} + +static inline +void read(const FileNode& node, uchar& value, uchar default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, schar& value, schar default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, ushort& value, ushort default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, short& value, short default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +template static inline +void read( FileNodeIterator& it, std::vector<_Tp>& vec, size_t maxCount = (size_t)INT_MAX ) +{ + cv::internal::VecReaderProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> r(&it); + r(vec, maxCount); +} + +template static inline +void read( const FileNode& node, std::vector<_Tp>& vec, const std::vector<_Tp>& default_value = std::vector<_Tp>() ) +{ + if(!node.node) + vec = default_value; + else + { + FileNodeIterator it = node.begin(); + read( it, vec ); + } +} + +static inline +void read( const FileNode& node, std::vector& vec, const std::vector& default_value ) +{ + if(!node.node) + vec = default_value; + else + read(node, vec); +} + +static inline +void read( const FileNode& node, std::vector& vec, const std::vector& default_value ) +{ + if(!node.node) + vec = default_value; + else + read(node, vec); +} + +//! @} FileNode + +//! @relates cv::FileStorage +//! @{ + +/** @brief Writes data to a file storage. + */ +template static inline +FileStorage& operator << (FileStorage& fs, const _Tp& value) +{ + if( !fs.isOpened() ) + return fs; + if( fs.state == FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP ) + CV_Error( Error::StsError, "No element name has been given" ); + write( fs, fs.elname, value ); + if( fs.state & FileStorage::INSIDE_MAP ) + fs.state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; + return fs; +} + +/** @brief Writes data to a file storage. + */ +static inline +FileStorage& operator << (FileStorage& fs, const char* str) +{ + return (fs << String(str)); +} + +/** @brief Writes data to a file storage. + */ +static inline +FileStorage& operator << (FileStorage& fs, char* value) +{ + return (fs << String(value)); +} + +//! @} FileStorage + +//! @relates cv::FileNodeIterator +//! @{ + +/** @brief Reads data from a file storage. + */ +template static inline +FileNodeIterator& operator >> (FileNodeIterator& it, _Tp& value) +{ + read( *it, value, _Tp()); + return ++it; +} + +/** @brief Reads data from a file storage. + */ +template static inline +FileNodeIterator& operator >> (FileNodeIterator& it, std::vector<_Tp>& vec) +{ + cv::internal::VecReaderProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> r(&it); + r(vec, (size_t)INT_MAX); + return it; +} + +//! @} FileNodeIterator + +//! @relates cv::FileNode +//! @{ + +/** @brief Reads data from a file storage. + */ +template static inline +void operator >> (const FileNode& n, _Tp& value) +{ + read( n, value, _Tp()); +} + +/** @brief Reads data from a file storage. + */ +template static inline +void operator >> (const FileNode& n, std::vector<_Tp>& vec) +{ + FileNodeIterator it = n.begin(); + it >> vec; +} + +/** @brief Reads KeyPoint from a file storage. +*/ +//It needs special handling because it contains two types of fields, int & float. +static inline +void operator >> (const FileNode& n, KeyPoint& kpt) +{ + FileNodeIterator it = n.begin(); + it >> kpt.pt.x >> kpt.pt.y >> kpt.size >> kpt.angle >> kpt.response >> kpt.octave >> kpt.class_id; +} + +#ifdef CV__LEGACY_PERSISTENCE +static inline +void operator >> (const FileNode& n, std::vector& vec) +{ + read(n, vec); +} +static inline +void operator >> (const FileNode& n, std::vector& vec) +{ + read(n, vec); +} +#endif + +/** @brief Reads DMatch from a file storage. +*/ +//It needs special handling because it contains two types of fields, int & float. +static inline +void operator >> (const FileNode& n, DMatch& m) +{ + FileNodeIterator it = n.begin(); + it >> m.queryIdx >> m.trainIdx >> m.imgIdx >> m.distance; +} + +//! @} FileNode + +//! @relates cv::FileNodeIterator +//! @{ + +static inline +bool operator == (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it1.fs == it2.fs && it1.container == it2.container && + it1.reader.ptr == it2.reader.ptr && it1.remaining == it2.remaining; +} + +static inline +bool operator != (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return !(it1 == it2); +} + +static inline +ptrdiff_t operator - (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it2.remaining - it1.remaining; +} + +static inline +bool operator < (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it1.remaining > it2.remaining; +} + +//! @} FileNodeIterator + +//! @cond IGNORED + +inline FileNode FileStorage::getFirstTopLevelNode() const { FileNode r = root(); FileNodeIterator it = r.begin(); return it != r.end() ? *it : FileNode(); } +inline FileNode::FileNode() : fs(0), node(0) {} +inline FileNode::FileNode(const CvFileStorage* _fs, const CvFileNode* _node) : fs(_fs), node(_node) {} +inline FileNode::FileNode(const FileNode& _node) : fs(_node.fs), node(_node.node) {} +inline FileNode& FileNode::operator=(const FileNode& _node) { fs = _node.fs; node = _node.node; return *this; } +inline bool FileNode::empty() const { return node == 0; } +inline bool FileNode::isNone() const { return type() == NONE; } +inline bool FileNode::isSeq() const { return type() == SEQ; } +inline bool FileNode::isMap() const { return type() == MAP; } +inline bool FileNode::isInt() const { return type() == INT; } +inline bool FileNode::isReal() const { return type() == REAL; } +inline bool FileNode::isString() const { return type() == STR; } +inline CvFileNode* FileNode::operator *() { return (CvFileNode*)node; } +inline const CvFileNode* FileNode::operator* () const { return node; } +inline FileNode::operator int() const { int value; read(*this, value, 0); return value; } +inline FileNode::operator float() const { float value; read(*this, value, 0.f); return value; } +inline FileNode::operator double() const { double value; read(*this, value, 0.); return value; } +inline FileNode::operator String() const { String value; read(*this, value, value); return value; } +inline double FileNode::real() const { return double(*this); } +inline String FileNode::string() const { return String(*this); } +inline Mat FileNode::mat() const { Mat value; read(*this, value, value); return value; } +inline FileNodeIterator FileNode::begin() const { return FileNodeIterator(fs, node); } +inline FileNodeIterator FileNode::end() const { return FileNodeIterator(fs, node, size()); } +inline void FileNode::readRaw( const String& fmt, uchar* vec, size_t len ) const { begin().readRaw( fmt, vec, len ); } +inline FileNode FileNodeIterator::operator *() const { return FileNode(fs, (const CvFileNode*)(const void*)reader.ptr); } +inline FileNode FileNodeIterator::operator ->() const { return FileNode(fs, (const CvFileNode*)(const void*)reader.ptr); } +inline String::String(const FileNode& fn): cstr_(0), len_(0) { read(fn, *this, *this); } + +//! @endcond + + +CV_EXPORTS void cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt); + +CV_EXPORTS void cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len); + +CV_EXPORTS void cvEndWriteRawData_Base64(::CvFileStorage * fs); + +CV_EXPORTS void cvWriteMat_Base64(::CvFileStorage* fs, const char* name, const ::CvMat* mat); + +CV_EXPORTS void cvWriteMatND_Base64(::CvFileStorage* fs, const char* name, const ::CvMatND* mat); + +} // cv + +#endif // OPENCV_CORE_PERSISTENCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/ptr.inl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/ptr.inl.hpp new file mode 100644 index 0000000..466f634 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/ptr.inl.hpp @@ -0,0 +1,379 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, NVIDIA Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 copyright holders or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_PTR_INL_HPP +#define OPENCV_CORE_PTR_INL_HPP + +#include + +//! @cond IGNORED + +namespace cv { + +template +void DefaultDeleter::operator () (Y* p) const +{ + delete p; +} + +namespace detail +{ + +struct PtrOwner +{ + PtrOwner() : refCount(1) + {} + + void incRef() + { + CV_XADD(&refCount, 1); + } + + void decRef() + { + if (CV_XADD(&refCount, -1) == 1) deleteSelf(); + } + +protected: + /* This doesn't really need to be virtual, since PtrOwner is never deleted + directly, but it doesn't hurt and it helps avoid warnings. */ + virtual ~PtrOwner() + {} + + virtual void deleteSelf() = 0; + +private: + unsigned int refCount; + + // noncopyable + PtrOwner(const PtrOwner&); + PtrOwner& operator = (const PtrOwner&); +}; + +template +struct PtrOwnerImpl CV_FINAL : PtrOwner +{ + PtrOwnerImpl(Y* p, D d) : owned(p), deleter(d) + {} + + void deleteSelf() CV_OVERRIDE + { + deleter(owned); + delete this; + } + +private: + Y* owned; + D deleter; +}; + + +} + +template +Ptr::Ptr() : owner(NULL), stored(NULL) +{} + +template +template +Ptr::Ptr(Y* p) + : owner(p + ? new detail::PtrOwnerImpl >(p, DefaultDeleter()) + : NULL), + stored(p) +{} + +template +template +Ptr::Ptr(Y* p, D d) + : owner(p + ? new detail::PtrOwnerImpl(p, d) + : NULL), + stored(p) +{} + +template +Ptr::Ptr(const Ptr& o) : owner(o.owner), stored(o.stored) +{ + if (owner) owner->incRef(); +} + +template +template +Ptr::Ptr(const Ptr& o) : owner(o.owner), stored(o.stored) +{ + if (owner) owner->incRef(); +} + +template +template +Ptr::Ptr(const Ptr& o, T* p) : owner(o.owner), stored(p) +{ + if (owner) owner->incRef(); +} + +template +Ptr::~Ptr() +{ + release(); +} + +template +Ptr& Ptr::operator = (const Ptr& o) +{ + Ptr(o).swap(*this); + return *this; +} + +template +template +Ptr& Ptr::operator = (const Ptr& o) +{ + Ptr(o).swap(*this); + return *this; +} + +template +void Ptr::release() +{ + if (owner) owner->decRef(); + owner = NULL; + stored = NULL; +} + +template +template +void Ptr::reset(Y* p) +{ + Ptr(p).swap(*this); +} + +template +template +void Ptr::reset(Y* p, D d) +{ + Ptr(p, d).swap(*this); +} + +template +void Ptr::swap(Ptr& o) +{ + std::swap(owner, o.owner); + std::swap(stored, o.stored); +} + +template +T* Ptr::get() const +{ + return stored; +} + +template +typename detail::RefOrVoid::type Ptr::operator * () const +{ + return *stored; +} + +template +T* Ptr::operator -> () const +{ + return stored; +} + +template +Ptr::operator T* () const +{ + return stored; +} + + +template +bool Ptr::empty() const +{ + return !stored; +} + +template +template +Ptr Ptr::staticCast() const +{ + return Ptr(*this, static_cast(stored)); +} + +template +template +Ptr Ptr::constCast() const +{ + return Ptr(*this, const_cast(stored)); +} + +template +template +Ptr Ptr::dynamicCast() const +{ + return Ptr(*this, dynamic_cast(stored)); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +template +Ptr::Ptr(Ptr&& o) : owner(o.owner), stored(o.stored) +{ + o.owner = NULL; + o.stored = NULL; +} + +template +Ptr& Ptr::operator = (Ptr&& o) +{ + if (this == &o) + return *this; + + release(); + owner = o.owner; + stored = o.stored; + o.owner = NULL; + o.stored = NULL; + return *this; +} + +#endif + + +template +void swap(Ptr& ptr1, Ptr& ptr2){ + ptr1.swap(ptr2); +} + +template +bool operator == (const Ptr& ptr1, const Ptr& ptr2) +{ + return ptr1.get() == ptr2.get(); +} + +template +bool operator != (const Ptr& ptr1, const Ptr& ptr2) +{ + return ptr1.get() != ptr2.get(); +} + +template +Ptr makePtr() +{ + return Ptr(new T()); +} + +template +Ptr makePtr(const A1& a1) +{ + return Ptr(new T(a1)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2) +{ + return Ptr(new T(a1, a2)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3) +{ + return Ptr(new T(a1, a2, a3)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4) +{ + return Ptr(new T(a1, a2, a3, a4)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) +{ + return Ptr(new T(a1, a2, a3, a4, a5)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10, const A11& a11) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10, const A11& a11, const A12& a12) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); +} +} // namespace cv + +//! @endcond + +#endif // OPENCV_CORE_PTR_INL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/saturate.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/saturate.hpp new file mode 100644 index 0000000..36d3121 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/saturate.hpp @@ -0,0 +1,163 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2014, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_SATURATE_HPP +#define OPENCV_CORE_SATURATE_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/fast_math.hpp" + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +/////////////// saturate_cast (used in image & signal processing) /////////////////// + +/** @brief Template function for accurate conversion from one primitive type to another. + + The function saturate_cast resembles the standard C++ cast operations, such as static_cast\() + and others. It perform an efficient and accurate conversion from one primitive type to another + (see the introduction chapter). saturate in the name means that when the input value v is out of the + range of the target type, the result is not formed just by taking low bits of the input, but instead + the value is clipped. For example: + @code + uchar a = saturate_cast(-100); // a = 0 (UCHAR_MIN) + short b = saturate_cast(33333.33333); // b = 32767 (SHRT_MAX) + @endcode + Such clipping is done when the target type is unsigned char , signed char , unsigned short or + signed short . For 32-bit integers, no clipping is done. + + When the parameter is a floating-point value and the target type is an integer (8-, 16- or 32-bit), + the floating-point value is first rounded to the nearest integer and then clipped if needed (when + the target type is 8- or 16-bit). + + @param v Function parameter. + @sa add, subtract, multiply, divide, Mat::convertTo + */ +template static inline _Tp saturate_cast(uchar v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(schar v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(ushort v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(short v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(unsigned v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(int v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(float v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(double v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(int64 v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(uint64 v) { return _Tp(v); } + +template<> inline uchar saturate_cast(schar v) { return (uchar)std::max((int)v, 0); } +template<> inline uchar saturate_cast(ushort v) { return (uchar)std::min((unsigned)v, (unsigned)UCHAR_MAX); } +template<> inline uchar saturate_cast(int v) { return (uchar)((unsigned)v <= UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); } +template<> inline uchar saturate_cast(short v) { return saturate_cast((int)v); } +template<> inline uchar saturate_cast(unsigned v) { return (uchar)std::min(v, (unsigned)UCHAR_MAX); } +template<> inline uchar saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline uchar saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline uchar saturate_cast(int64 v) { return (uchar)((uint64)v <= (uint64)UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); } +template<> inline uchar saturate_cast(uint64 v) { return (uchar)std::min(v, (uint64)UCHAR_MAX); } + +template<> inline schar saturate_cast(uchar v) { return (schar)std::min((int)v, SCHAR_MAX); } +template<> inline schar saturate_cast(ushort v) { return (schar)std::min((unsigned)v, (unsigned)SCHAR_MAX); } +template<> inline schar saturate_cast(int v) { return (schar)((unsigned)(v-SCHAR_MIN) <= (unsigned)UCHAR_MAX ? v : v > 0 ? SCHAR_MAX : SCHAR_MIN); } +template<> inline schar saturate_cast(short v) { return saturate_cast((int)v); } +template<> inline schar saturate_cast(unsigned v) { return (schar)std::min(v, (unsigned)SCHAR_MAX); } +template<> inline schar saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline schar saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline schar saturate_cast(int64 v) { return (schar)((uint64)((int64)v-SCHAR_MIN) <= (uint64)UCHAR_MAX ? v : v > 0 ? SCHAR_MAX : SCHAR_MIN); } +template<> inline schar saturate_cast(uint64 v) { return (schar)std::min(v, (uint64)SCHAR_MAX); } + +template<> inline ushort saturate_cast(schar v) { return (ushort)std::max((int)v, 0); } +template<> inline ushort saturate_cast(short v) { return (ushort)std::max((int)v, 0); } +template<> inline ushort saturate_cast(int v) { return (ushort)((unsigned)v <= (unsigned)USHRT_MAX ? v : v > 0 ? USHRT_MAX : 0); } +template<> inline ushort saturate_cast(unsigned v) { return (ushort)std::min(v, (unsigned)USHRT_MAX); } +template<> inline ushort saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline ushort saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline ushort saturate_cast(int64 v) { return (ushort)((uint64)v <= (uint64)USHRT_MAX ? v : v > 0 ? USHRT_MAX : 0); } +template<> inline ushort saturate_cast(uint64 v) { return (ushort)std::min(v, (uint64)USHRT_MAX); } + +template<> inline short saturate_cast(ushort v) { return (short)std::min((int)v, SHRT_MAX); } +template<> inline short saturate_cast(int v) { return (short)((unsigned)(v - SHRT_MIN) <= (unsigned)USHRT_MAX ? v : v > 0 ? SHRT_MAX : SHRT_MIN); } +template<> inline short saturate_cast(unsigned v) { return (short)std::min(v, (unsigned)SHRT_MAX); } +template<> inline short saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline short saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline short saturate_cast(int64 v) { return (short)((uint64)((int64)v - SHRT_MIN) <= (uint64)USHRT_MAX ? v : v > 0 ? SHRT_MAX : SHRT_MIN); } +template<> inline short saturate_cast(uint64 v) { return (short)std::min(v, (uint64)SHRT_MAX); } + +template<> inline int saturate_cast(unsigned v) { return (int)std::min(v, (unsigned)INT_MAX); } +template<> inline int saturate_cast(int64 v) { return (int)((uint64)(v - INT_MIN) <= (uint64)UINT_MAX ? v : v > 0 ? INT_MAX : INT_MIN); } +template<> inline int saturate_cast(uint64 v) { return (int)std::min(v, (uint64)INT_MAX); } +template<> inline int saturate_cast(float v) { return cvRound(v); } +template<> inline int saturate_cast(double v) { return cvRound(v); } + +template<> inline unsigned saturate_cast(schar v) { return (unsigned)std::max(v, (schar)0); } +template<> inline unsigned saturate_cast(short v) { return (unsigned)std::max(v, (short)0); } +template<> inline unsigned saturate_cast(int v) { return (unsigned)std::max(v, (int)0); } +template<> inline unsigned saturate_cast(int64 v) { return (unsigned)((uint64)v <= (uint64)UINT_MAX ? v : v > 0 ? UINT_MAX : 0); } +template<> inline unsigned saturate_cast(uint64 v) { return (unsigned)std::min(v, (uint64)UINT_MAX); } +// we intentionally do not clip negative numbers, to make -1 become 0xffffffff etc. +template<> inline unsigned saturate_cast(float v) { return static_cast(cvRound(v)); } +template<> inline unsigned saturate_cast(double v) { return static_cast(cvRound(v)); } + +template<> inline uint64 saturate_cast(schar v) { return (uint64)std::max(v, (schar)0); } +template<> inline uint64 saturate_cast(short v) { return (uint64)std::max(v, (short)0); } +template<> inline uint64 saturate_cast(int v) { return (uint64)std::max(v, (int)0); } +template<> inline uint64 saturate_cast(int64 v) { return (uint64)std::max(v, (int64)0); } + +template<> inline int64 saturate_cast(uint64 v) { return (int64)std::min(v, (uint64)LLONG_MAX); } + +//! @} + +} // cv + +#endif // OPENCV_CORE_SATURATE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/simd_intrinsics.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/simd_intrinsics.hpp new file mode 100644 index 0000000..7151d36 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/simd_intrinsics.hpp @@ -0,0 +1,88 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_SIMD_INTRINSICS_HPP +#define OPENCV_CORE_SIMD_INTRINSICS_HPP + +/** +Helper header to support SIMD intrinsics (universal intrinsics) in user code. +Intrinsics documentation: https://docs.opencv.org/3.4/df/d91/group__core__hal__intrin.html + + +Checks of target CPU instruction set based on compiler definitions don't work well enough. +More reliable solutions require utilization of configuration systems (like CMake). + +So, probably you need to specify your own configuration. + +You can do that via CMake in this way: + add_definitions(/DOPENCV_SIMD_CONFIG_HEADER=opencv_simd_config_custom.hpp) +or + add_definitions(/DOPENCV_SIMD_CONFIG_INCLUDE_DIR=1) + +Additionally you may need to add include directory to your files: + include_directories("${CMAKE_CURRENT_LIST_DIR}/opencv_config_${MYTARGET}") + +These files can be pre-generated for target configurations of your application +or generated by CMake on the fly (use CMAKE_BINARY_DIR for that). + +Notes: +- H/W capability checks are still responsibility of your application +- runtime dispatching is not covered by this helper header +*/ + +#ifdef __OPENCV_BUILD +#error "Use core/hal/intrin.hpp during OpenCV build" +#endif + +#ifdef OPENCV_HAL_INTRIN_HPP +#error "core/simd_intrinsics.hpp must be included before core/hal/intrin.hpp" +#endif + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/version.hpp" + +#ifdef OPENCV_SIMD_CONFIG_HEADER +#include CVAUX_STR(OPENCV_SIMD_CONFIG_HEADER) +#elif defined(OPENCV_SIMD_CONFIG_INCLUDE_DIR) +#include "opencv_simd_config.hpp" // corresponding directory should be added via -I compiler parameter +#else // custom config headers + +#if (!defined(CV_AVX_512F) || !CV_AVX_512F) && (defined(__AVX512__) || defined(__AVX512F__)) +# include +# undef CV_AVX_512F +# define CV_AVX_512F 1 +# ifndef OPENCV_SIMD_DONT_ASSUME_SKX // Skylake-X with AVX-512F/CD/BW/DQ/VL +# undef CV_AVX512_SKX +# define CV_AVX512_SKX 1 +# undef CV_AVX_512CD +# define CV_AVX_512CD 1 +# undef CV_AVX_512BW +# define CV_AVX_512BW 1 +# undef CV_AVX_512DQ +# define CV_AVX_512DQ 1 +# undef CV_AVX_512VL +# define CV_AVX_512VL 1 +# endif +#endif // AVX512 + +// GCC/Clang: -mavx2 +// MSVC: /arch:AVX2 +#if defined __AVX2__ +# include +# undef CV_AVX2 +# define CV_AVX2 1 +# if defined __F16C__ +# undef CV_FP16 +# define CV_FP16 1 +# endif +#endif + +#endif + +// SSE / NEON / VSX is handled by cv_cpu_dispatch.h compatibility block +#include "cv_cpu_dispatch.h" + +#include "hal/intrin.hpp" + +#endif // OPENCV_CORE_SIMD_INTRINSICS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/softfloat.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/softfloat.hpp new file mode 100644 index 0000000..485e15c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/softfloat.hpp @@ -0,0 +1,514 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +// This file is based on files from package issued with the following license: + +/*============================================================================ + +This C header file is part of the SoftFloat IEEE Floating-Point Arithmetic +Package, Release 3c, by John R. Hauser. + +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 The Regents of the +University of California. All rights reserved. + +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. Neither the name of the University nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "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 REGENTS OR CONTRIBUTORS 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. + +=============================================================================*/ + +#pragma once +#ifndef softfloat_h +#define softfloat_h 1 + +#include "cvdef.h" + +namespace cv +{ + +/** @addtogroup core_utils_softfloat + + [SoftFloat](http://www.jhauser.us/arithmetic/SoftFloat.html) is a software implementation + of floating-point calculations according to IEEE 754 standard. + All calculations are done in integers, that's why they are machine-independent and bit-exact. + This library can be useful in accuracy-critical parts like look-up tables generation, tests, etc. + OpenCV contains a subset of SoftFloat partially rewritten to C++. + + ### Types + + There are two basic types: @ref softfloat and @ref softdouble. + These types are binary compatible with float and double types respectively + and support conversions to/from them. + Other types from original SoftFloat library like fp16 or fp128 were thrown away + as well as quiet/signaling NaN support, on-the-fly rounding mode switch + and exception flags (though exceptions can be implemented in the future). + + ### Operations + + Both types support the following: + - Construction from signed and unsigned 32-bit and 64 integers, + float/double or raw binary representation + - Conversions between each other, to float or double and to int + using @ref cvRound, @ref cvTrunc, @ref cvFloor, @ref cvCeil or a bunch of + saturate_cast functions + - Add, subtract, multiply, divide, remainder, square root, FMA with absolute precision + - Comparison operations + - Explicit sign, exponent and significand manipulation through get/set methods, + number state indicators (isInf, isNan, isSubnormal) + - Type-specific constants like eps, minimum/maximum value, best pi approximation, etc. + - min(), max(), abs(), exp(), log() and pow() functions + +*/ +//! @{ + +struct softfloat; +struct softdouble; + +struct CV_EXPORTS softfloat +{ +public: + /** @brief Default constructor */ + softfloat() { v = 0; } + /** @brief Copy constructor */ + softfloat( const softfloat& c) { v = c.v; } + /** @brief Assign constructor */ + softfloat& operator=( const softfloat& c ) + { + if(&c != this) v = c.v; + return *this; + } + /** @brief Construct from raw + + Builds new value from raw binary representation + */ + static const softfloat fromRaw( const uint32_t a ) { softfloat x; x.v = a; return x; } + + /** @brief Construct from integer */ + explicit softfloat( const uint32_t ); + explicit softfloat( const uint64_t ); + explicit softfloat( const int32_t ); + explicit softfloat( const int64_t ); + +#ifdef CV_INT32_T_IS_LONG_INT + // for platforms with int32_t = long int + explicit softfloat( const int a ) { *this = softfloat(static_cast(a)); } +#endif + + /** @brief Construct from float */ + explicit softfloat( const float a ) { Cv32suf s; s.f = a; v = s.u; } + + /** @brief Type casts */ + operator softdouble() const; + operator float() const { Cv32suf s; s.u = v; return s.f; } + + /** @brief Basic arithmetics */ + softfloat operator + (const softfloat&) const; + softfloat operator - (const softfloat&) const; + softfloat operator * (const softfloat&) const; + softfloat operator / (const softfloat&) const; + softfloat operator - () const { softfloat x; x.v = v ^ (1U << 31); return x; } + + /** @brief Remainder operator + + A quote from original SoftFloat manual: + + > The IEEE Standard remainder operation computes the value + > a - n * b, where n is the integer closest to a / b. + > If a / b is exactly halfway between two integers, n is the even integer + > closest to a / b. The IEEE Standard鈥檚 remainder operation is always exact and so requires no rounding. + > Depending on the relative magnitudes of the operands, the remainder functions + > can take considerably longer to execute than the other SoftFloat functions. + > This is an inherent characteristic of the remainder operation itself and is not a flaw + > in the SoftFloat implementation. + */ + softfloat operator % (const softfloat&) const; + + softfloat& operator += (const softfloat& a) { *this = *this + a; return *this; } + softfloat& operator -= (const softfloat& a) { *this = *this - a; return *this; } + softfloat& operator *= (const softfloat& a) { *this = *this * a; return *this; } + softfloat& operator /= (const softfloat& a) { *this = *this / a; return *this; } + softfloat& operator %= (const softfloat& a) { *this = *this % a; return *this; } + + /** @brief Comparison operations + + - Any operation with NaN produces false + + The only exception is when x is NaN: x != y for any y. + - Positive and negative zeros are equal + */ + bool operator == ( const softfloat& ) const; + bool operator != ( const softfloat& ) const; + bool operator > ( const softfloat& ) const; + bool operator >= ( const softfloat& ) const; + bool operator < ( const softfloat& ) const; + bool operator <= ( const softfloat& ) const; + + /** @brief NaN state indicator */ + inline bool isNaN() const { return (v & 0x7fffffff) > 0x7f800000; } + /** @brief Inf state indicator */ + inline bool isInf() const { return (v & 0x7fffffff) == 0x7f800000; } + /** @brief Subnormal number indicator */ + inline bool isSubnormal() const { return ((v >> 23) & 0xFF) == 0; } + + /** @brief Get sign bit */ + inline bool getSign() const { return (v >> 31) != 0; } + /** @brief Construct a copy with new sign bit */ + inline softfloat setSign(bool sign) const { softfloat x; x.v = (v & ((1U << 31) - 1)) | ((uint32_t)sign << 31); return x; } + /** @brief Get 0-based exponent */ + inline int getExp() const { return ((v >> 23) & 0xFF) - 127; } + /** @brief Construct a copy with new 0-based exponent */ + inline softfloat setExp(int e) const { softfloat x; x.v = (v & 0x807fffff) | (((e + 127) & 0xFF) << 23 ); return x; } + + /** @brief Get a fraction part + + Returns a number 1 <= x < 2 with the same significand + */ + inline softfloat getFrac() const + { + uint_fast32_t vv = (v & 0x007fffff) | (127 << 23); + return softfloat::fromRaw(vv); + } + /** @brief Construct a copy with provided significand + + Constructs a copy of a number with significand taken from parameter + */ + inline softfloat setFrac(const softfloat& s) const + { + softfloat x; + x.v = (v & 0xff800000) | (s.v & 0x007fffff); + return x; + } + + /** @brief Zero constant */ + static softfloat zero() { return softfloat::fromRaw( 0 ); } + /** @brief Positive infinity constant */ + static softfloat inf() { return softfloat::fromRaw( 0xFF << 23 ); } + /** @brief Default NaN constant */ + static softfloat nan() { return softfloat::fromRaw( 0x7fffffff ); } + /** @brief One constant */ + static softfloat one() { return softfloat::fromRaw( 127 << 23 ); } + /** @brief Smallest normalized value */ + static softfloat min() { return softfloat::fromRaw( 0x01 << 23 ); } + /** @brief Difference between 1 and next representable value */ + static softfloat eps() { return softfloat::fromRaw( (127 - 23) << 23 ); } + /** @brief Biggest finite value */ + static softfloat max() { return softfloat::fromRaw( (0xFF << 23) - 1 ); } + /** @brief Correct pi approximation */ + static softfloat pi() { return softfloat::fromRaw( 0x40490fdb ); } + + uint32_t v; +}; + +/*---------------------------------------------------------------------------- +*----------------------------------------------------------------------------*/ + +struct CV_EXPORTS softdouble +{ +public: + /** @brief Default constructor */ + softdouble() : v(0) { } + /** @brief Copy constructor */ + softdouble( const softdouble& c) { v = c.v; } + /** @brief Assign constructor */ + softdouble& operator=( const softdouble& c ) + { + if(&c != this) v = c.v; + return *this; + } + /** @brief Construct from raw + + Builds new value from raw binary representation + */ + static softdouble fromRaw( const uint64_t a ) { softdouble x; x.v = a; return x; } + + /** @brief Construct from integer */ + explicit softdouble( const uint32_t ); + explicit softdouble( const uint64_t ); + explicit softdouble( const int32_t ); + explicit softdouble( const int64_t ); + +#ifdef CV_INT32_T_IS_LONG_INT + // for platforms with int32_t = long int + explicit softdouble( const int a ) { *this = softdouble(static_cast(a)); } +#endif + + /** @brief Construct from double */ + explicit softdouble( const double a ) { Cv64suf s; s.f = a; v = s.u; } + + /** @brief Type casts */ + operator softfloat() const; + operator double() const { Cv64suf s; s.u = v; return s.f; } + + /** @brief Basic arithmetics */ + softdouble operator + (const softdouble&) const; + softdouble operator - (const softdouble&) const; + softdouble operator * (const softdouble&) const; + softdouble operator / (const softdouble&) const; + softdouble operator - () const { softdouble x; x.v = v ^ (1ULL << 63); return x; } + + /** @brief Remainder operator + + A quote from original SoftFloat manual: + + > The IEEE Standard remainder operation computes the value + > a - n * b, where n is the integer closest to a / b. + > If a / b is exactly halfway between two integers, n is the even integer + > closest to a / b. The IEEE Standard鈥檚 remainder operation is always exact and so requires no rounding. + > Depending on the relative magnitudes of the operands, the remainder functions + > can take considerably longer to execute than the other SoftFloat functions. + > This is an inherent characteristic of the remainder operation itself and is not a flaw + > in the SoftFloat implementation. + */ + softdouble operator % (const softdouble&) const; + + softdouble& operator += (const softdouble& a) { *this = *this + a; return *this; } + softdouble& operator -= (const softdouble& a) { *this = *this - a; return *this; } + softdouble& operator *= (const softdouble& a) { *this = *this * a; return *this; } + softdouble& operator /= (const softdouble& a) { *this = *this / a; return *this; } + softdouble& operator %= (const softdouble& a) { *this = *this % a; return *this; } + + /** @brief Comparison operations + + - Any operation with NaN produces false + + The only exception is when x is NaN: x != y for any y. + - Positive and negative zeros are equal + */ + bool operator == ( const softdouble& ) const; + bool operator != ( const softdouble& ) const; + bool operator > ( const softdouble& ) const; + bool operator >= ( const softdouble& ) const; + bool operator < ( const softdouble& ) const; + bool operator <= ( const softdouble& ) const; + + /** @brief NaN state indicator */ + inline bool isNaN() const { return (v & 0x7fffffffffffffff) > 0x7ff0000000000000; } + /** @brief Inf state indicator */ + inline bool isInf() const { return (v & 0x7fffffffffffffff) == 0x7ff0000000000000; } + /** @brief Subnormal number indicator */ + inline bool isSubnormal() const { return ((v >> 52) & 0x7FF) == 0; } + + /** @brief Get sign bit */ + inline bool getSign() const { return (v >> 63) != 0; } + /** @brief Construct a copy with new sign bit */ + softdouble setSign(bool sign) const { softdouble x; x.v = (v & ((1ULL << 63) - 1)) | ((uint_fast64_t)(sign) << 63); return x; } + /** @brief Get 0-based exponent */ + inline int getExp() const { return ((v >> 52) & 0x7FF) - 1023; } + /** @brief Construct a copy with new 0-based exponent */ + inline softdouble setExp(int e) const + { + softdouble x; + x.v = (v & 0x800FFFFFFFFFFFFF) | ((uint_fast64_t)((e + 1023) & 0x7FF) << 52); + return x; + } + + /** @brief Get a fraction part + + Returns a number 1 <= x < 2 with the same significand + */ + inline softdouble getFrac() const + { + uint_fast64_t vv = (v & 0x000FFFFFFFFFFFFF) | ((uint_fast64_t)(1023) << 52); + return softdouble::fromRaw(vv); + } + /** @brief Construct a copy with provided significand + + Constructs a copy of a number with significand taken from parameter + */ + inline softdouble setFrac(const softdouble& s) const + { + softdouble x; + x.v = (v & 0xFFF0000000000000) | (s.v & 0x000FFFFFFFFFFFFF); + return x; + } + + /** @brief Zero constant */ + static softdouble zero() { return softdouble::fromRaw( 0 ); } + /** @brief Positive infinity constant */ + static softdouble inf() { return softdouble::fromRaw( (uint_fast64_t)(0x7FF) << 52 ); } + /** @brief Default NaN constant */ + static softdouble nan() { return softdouble::fromRaw( CV_BIG_INT(0x7FFFFFFFFFFFFFFF) ); } + /** @brief One constant */ + static softdouble one() { return softdouble::fromRaw( (uint_fast64_t)( 1023) << 52 ); } + /** @brief Smallest normalized value */ + static softdouble min() { return softdouble::fromRaw( (uint_fast64_t)( 0x01) << 52 ); } + /** @brief Difference between 1 and next representable value */ + static softdouble eps() { return softdouble::fromRaw( (uint_fast64_t)( 1023 - 52 ) << 52 ); } + /** @brief Biggest finite value */ + static softdouble max() { return softdouble::fromRaw( ((uint_fast64_t)(0x7FF) << 52) - 1 ); } + /** @brief Correct pi approximation */ + static softdouble pi() { return softdouble::fromRaw( CV_BIG_INT(0x400921FB54442D18) ); } + + uint64_t v; +}; + +/*---------------------------------------------------------------------------- +*----------------------------------------------------------------------------*/ + +/** @brief Fused Multiplication and Addition + +Computes (a*b)+c with single rounding +*/ +CV_EXPORTS softfloat mulAdd( const softfloat& a, const softfloat& b, const softfloat & c); +CV_EXPORTS softdouble mulAdd( const softdouble& a, const softdouble& b, const softdouble& c); + +/** @brief Square root */ +CV_EXPORTS softfloat sqrt( const softfloat& a ); +CV_EXPORTS softdouble sqrt( const softdouble& a ); +} + +/*---------------------------------------------------------------------------- +| Ported from OpenCV and added for usability +*----------------------------------------------------------------------------*/ + +/** @brief Truncates number to integer with minimum magnitude */ +CV_EXPORTS int cvTrunc(const cv::softfloat& a); +CV_EXPORTS int cvTrunc(const cv::softdouble& a); + +/** @brief Rounds a number to nearest even integer */ +CV_EXPORTS int cvRound(const cv::softfloat& a); +CV_EXPORTS int cvRound(const cv::softdouble& a); + +/** @brief Rounds a number to nearest even long long integer */ +CV_EXPORTS int64_t cvRound64(const cv::softdouble& a); + +/** @brief Rounds a number down to integer */ +CV_EXPORTS int cvFloor(const cv::softfloat& a); +CV_EXPORTS int cvFloor(const cv::softdouble& a); + +/** @brief Rounds number up to integer */ +CV_EXPORTS int cvCeil(const cv::softfloat& a); +CV_EXPORTS int cvCeil(const cv::softdouble& a); + +namespace cv +{ +/** @brief Saturate casts */ +template static inline _Tp saturate_cast(softfloat a) { return _Tp(a); } +template static inline _Tp saturate_cast(softdouble a) { return _Tp(a); } + +template<> inline uchar saturate_cast(softfloat a) { return (uchar)std::max(std::min(cvRound(a), (int)UCHAR_MAX), 0); } +template<> inline uchar saturate_cast(softdouble a) { return (uchar)std::max(std::min(cvRound(a), (int)UCHAR_MAX), 0); } + +template<> inline schar saturate_cast(softfloat a) { return (schar)std::min(std::max(cvRound(a), (int)SCHAR_MIN), (int)SCHAR_MAX); } +template<> inline schar saturate_cast(softdouble a) { return (schar)std::min(std::max(cvRound(a), (int)SCHAR_MIN), (int)SCHAR_MAX); } + +template<> inline ushort saturate_cast(softfloat a) { return (ushort)std::max(std::min(cvRound(a), (int)USHRT_MAX), 0); } +template<> inline ushort saturate_cast(softdouble a) { return (ushort)std::max(std::min(cvRound(a), (int)USHRT_MAX), 0); } + +template<> inline short saturate_cast(softfloat a) { return (short)std::min(std::max(cvRound(a), (int)SHRT_MIN), (int)SHRT_MAX); } +template<> inline short saturate_cast(softdouble a) { return (short)std::min(std::max(cvRound(a), (int)SHRT_MIN), (int)SHRT_MAX); } + +template<> inline int saturate_cast(softfloat a) { return cvRound(a); } +template<> inline int saturate_cast(softdouble a) { return cvRound(a); } + +template<> inline int64_t saturate_cast(softfloat a) { return cvRound(a); } +template<> inline int64_t saturate_cast(softdouble a) { return cvRound64(a); } + +/** @brief Saturate cast to unsigned integer and unsigned long long integer +We intentionally do not clip negative numbers, to make -1 become 0xffffffff etc. +*/ +template<> inline unsigned saturate_cast(softfloat a) { return cvRound(a); } +template<> inline unsigned saturate_cast(softdouble a) { return cvRound(a); } + +template<> inline uint64_t saturate_cast(softfloat a) { return cvRound(a); } +template<> inline uint64_t saturate_cast(softdouble a) { return cvRound64(a); } + +/** @brief Min and Max functions */ +inline softfloat min(const softfloat& a, const softfloat& b) { return (a > b) ? b : a; } +inline softdouble min(const softdouble& a, const softdouble& b) { return (a > b) ? b : a; } + +inline softfloat max(const softfloat& a, const softfloat& b) { return (a > b) ? a : b; } +inline softdouble max(const softdouble& a, const softdouble& b) { return (a > b) ? a : b; } + +/** @brief Absolute value */ +inline softfloat abs( softfloat a) { softfloat x; x.v = a.v & ((1U << 31) - 1); return x; } +inline softdouble abs( softdouble a) { softdouble x; x.v = a.v & ((1ULL << 63) - 1); return x; } + +/** @brief Exponent + +Special cases: +- exp(NaN) is NaN +- exp(-Inf) == 0 +- exp(+Inf) == +Inf +*/ +CV_EXPORTS softfloat exp( const softfloat& a); +CV_EXPORTS softdouble exp( const softdouble& a); + +/** @brief Natural logarithm + +Special cases: +- log(NaN), log(x < 0) are NaN +- log(0) == -Inf +*/ +CV_EXPORTS softfloat log( const softfloat& a ); +CV_EXPORTS softdouble log( const softdouble& a ); + +/** @brief Raising to the power + +Special cases: +- x**NaN is NaN for any x +- ( |x| == 1 )**Inf is NaN +- ( |x| > 1 )**+Inf or ( |x| < 1 )**-Inf is +Inf +- ( |x| > 1 )**-Inf or ( |x| < 1 )**+Inf is 0 +- x ** 0 == 1 for any x +- x ** 1 == 1 for any x +- NaN ** y is NaN for any other y +- Inf**(y < 0) == 0 +- Inf ** y is +Inf for any other y +- (x < 0)**y is NaN for any other y if x can't be correctly rounded to integer +- 0 ** 0 == 1 +- 0 ** (y < 0) is +Inf +- 0 ** (y > 0) is 0 +*/ +CV_EXPORTS softfloat pow( const softfloat& a, const softfloat& b); +CV_EXPORTS softdouble pow( const softdouble& a, const softdouble& b); + +/** @brief Cube root + +Special cases: +- cbrt(NaN) is NaN +- cbrt(+/-Inf) is +/-Inf +*/ +CV_EXPORTS softfloat cbrt( const softfloat& a ); + +/** @brief Sine + +Special cases: +- sin(Inf) or sin(NaN) is NaN +- sin(x) == x when sin(x) is close to zero +*/ +CV_EXPORTS softdouble sin( const softdouble& a ); + +/** @brief Cosine + * +Special cases: +- cos(Inf) or cos(NaN) is NaN +- cos(x) == +/- 1 when cos(x) is close to +/- 1 +*/ +CV_EXPORTS softdouble cos( const softdouble& a ); + +//! @} core_utils_softfloat + +} // cv:: + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/sse_utils.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/sse_utils.hpp new file mode 100644 index 0000000..0906583 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/sse_utils.hpp @@ -0,0 +1,652 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_SSE_UTILS_HPP +#define OPENCV_CORE_SSE_UTILS_HPP + +#ifndef __cplusplus +# error sse_utils.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils_sse +//! @{ + +#if CV_SSE2 + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_g0); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_g0); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_g1); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_g1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk3); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk3); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk3); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk3); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk2); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk2); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk3); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk3); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk2); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk2); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk3); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk3); +} + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_g1); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_g1); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_b0); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_b0); + __m128i layer1_chunk4 = _mm_unpacklo_epi8(v_g0, v_b1); + __m128i layer1_chunk5 = _mm_unpackhi_epi8(v_g0, v_b1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk4 = _mm_unpacklo_epi8(layer1_chunk2, layer1_chunk5); + __m128i layer2_chunk5 = _mm_unpackhi_epi8(layer1_chunk2, layer1_chunk5); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk4 = _mm_unpacklo_epi8(layer2_chunk2, layer2_chunk5); + __m128i layer3_chunk5 = _mm_unpackhi_epi8(layer2_chunk2, layer2_chunk5); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk3); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk3); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk4); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk4); + __m128i layer4_chunk4 = _mm_unpacklo_epi8(layer3_chunk2, layer3_chunk5); + __m128i layer4_chunk5 = _mm_unpackhi_epi8(layer3_chunk2, layer3_chunk5); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk3); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk3); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk4); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk4); + v_b0 = _mm_unpacklo_epi8(layer4_chunk2, layer4_chunk5); + v_b1 = _mm_unpackhi_epi8(layer4_chunk2, layer4_chunk5); +} + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_b0); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_b0); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_b1); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_b1); + __m128i layer1_chunk4 = _mm_unpacklo_epi8(v_g0, v_a0); + __m128i layer1_chunk5 = _mm_unpackhi_epi8(v_g0, v_a0); + __m128i layer1_chunk6 = _mm_unpacklo_epi8(v_g1, v_a1); + __m128i layer1_chunk7 = _mm_unpackhi_epi8(v_g1, v_a1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk4 = _mm_unpacklo_epi8(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk5 = _mm_unpackhi_epi8(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk6 = _mm_unpacklo_epi8(layer1_chunk3, layer1_chunk7); + __m128i layer2_chunk7 = _mm_unpackhi_epi8(layer1_chunk3, layer1_chunk7); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk4 = _mm_unpacklo_epi8(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk5 = _mm_unpackhi_epi8(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk6 = _mm_unpacklo_epi8(layer2_chunk3, layer2_chunk7); + __m128i layer3_chunk7 = _mm_unpackhi_epi8(layer2_chunk3, layer2_chunk7); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk4); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk4); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk5); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk5); + __m128i layer4_chunk4 = _mm_unpacklo_epi8(layer3_chunk2, layer3_chunk6); + __m128i layer4_chunk5 = _mm_unpackhi_epi8(layer3_chunk2, layer3_chunk6); + __m128i layer4_chunk6 = _mm_unpacklo_epi8(layer3_chunk3, layer3_chunk7); + __m128i layer4_chunk7 = _mm_unpackhi_epi8(layer3_chunk3, layer3_chunk7); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk4); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk4); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk5); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk5); + v_b0 = _mm_unpacklo_epi8(layer4_chunk2, layer4_chunk6); + v_b1 = _mm_unpackhi_epi8(layer4_chunk2, layer4_chunk6); + v_a0 = _mm_unpacklo_epi8(layer4_chunk3, layer4_chunk7); + v_a1 = _mm_unpackhi_epi8(layer4_chunk3, layer4_chunk7); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_g1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk4 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer4_chunk5 = _mm_packus_epi16(_mm_srli_epi16(v_b0, 8), _mm_srli_epi16(v_b1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_and_si128(layer4_chunk4, v_mask), _mm_and_si128(layer4_chunk5, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk4, 8), _mm_srli_epi16(layer4_chunk5, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk4, 8), _mm_srli_epi16(layer3_chunk5, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk4, 8), _mm_srli_epi16(layer2_chunk5, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); + v_g0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_b1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk4, 8), _mm_srli_epi16(layer1_chunk5, 8)); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk4 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk5 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer4_chunk6 = _mm_packus_epi16(_mm_srli_epi16(v_b0, 8), _mm_srli_epi16(v_b1, 8)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_and_si128(v_a0, v_mask), _mm_and_si128(v_a1, v_mask)); + __m128i layer4_chunk7 = _mm_packus_epi16(_mm_srli_epi16(v_a0, 8), _mm_srli_epi16(v_a1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_and_si128(layer4_chunk4, v_mask), _mm_and_si128(layer4_chunk5, v_mask)); + __m128i layer3_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk4, 8), _mm_srli_epi16(layer4_chunk5, 8)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_and_si128(layer4_chunk6, v_mask), _mm_and_si128(layer4_chunk7, v_mask)); + __m128i layer3_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk6, 8), _mm_srli_epi16(layer4_chunk7, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk4, 8), _mm_srli_epi16(layer3_chunk5, 8)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_and_si128(layer3_chunk6, v_mask), _mm_and_si128(layer3_chunk7, v_mask)); + __m128i layer2_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk6, 8), _mm_srli_epi16(layer3_chunk7, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk4, 8), _mm_srli_epi16(layer2_chunk5, 8)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_and_si128(layer2_chunk6, v_mask), _mm_and_si128(layer2_chunk7, v_mask)); + __m128i layer1_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk6, 8), _mm_srli_epi16(layer2_chunk7, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_b0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); + v_g0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_a0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk4, 8), _mm_srli_epi16(layer1_chunk5, 8)); + v_g1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk6, v_mask), _mm_and_si128(layer1_chunk7, v_mask)); + v_a1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk6, 8), _mm_srli_epi16(layer1_chunk7, 8)); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_g0); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_g0); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_g1); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_g1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk3); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk3); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk3); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk3); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk2); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk2); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk3); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk3); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_g1); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_g1); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_b0); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_b0); + __m128i layer1_chunk4 = _mm_unpacklo_epi16(v_g0, v_b1); + __m128i layer1_chunk5 = _mm_unpackhi_epi16(v_g0, v_b1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk4 = _mm_unpacklo_epi16(layer1_chunk2, layer1_chunk5); + __m128i layer2_chunk5 = _mm_unpackhi_epi16(layer1_chunk2, layer1_chunk5); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk4 = _mm_unpacklo_epi16(layer2_chunk2, layer2_chunk5); + __m128i layer3_chunk5 = _mm_unpackhi_epi16(layer2_chunk2, layer2_chunk5); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk3); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk3); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk4); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk4); + v_b0 = _mm_unpacklo_epi16(layer3_chunk2, layer3_chunk5); + v_b1 = _mm_unpackhi_epi16(layer3_chunk2, layer3_chunk5); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_b0); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_b0); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_b1); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_b1); + __m128i layer1_chunk4 = _mm_unpacklo_epi16(v_g0, v_a0); + __m128i layer1_chunk5 = _mm_unpackhi_epi16(v_g0, v_a0); + __m128i layer1_chunk6 = _mm_unpacklo_epi16(v_g1, v_a1); + __m128i layer1_chunk7 = _mm_unpackhi_epi16(v_g1, v_a1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk4 = _mm_unpacklo_epi16(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk5 = _mm_unpackhi_epi16(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk6 = _mm_unpacklo_epi16(layer1_chunk3, layer1_chunk7); + __m128i layer2_chunk7 = _mm_unpackhi_epi16(layer1_chunk3, layer1_chunk7); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk4 = _mm_unpacklo_epi16(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk5 = _mm_unpackhi_epi16(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk6 = _mm_unpacklo_epi16(layer2_chunk3, layer2_chunk7); + __m128i layer3_chunk7 = _mm_unpackhi_epi16(layer2_chunk3, layer2_chunk7); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk4); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk4); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk5); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk5); + v_b0 = _mm_unpacklo_epi16(layer3_chunk2, layer3_chunk6); + v_b1 = _mm_unpackhi_epi16(layer3_chunk2, layer3_chunk6); + v_a0 = _mm_unpacklo_epi16(layer3_chunk3, layer3_chunk7); + v_a1 = _mm_unpackhi_epi16(layer3_chunk3, layer3_chunk7); +} + +#if CV_SSE4_1 + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_g1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); +} + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi32(_mm_srli_epi32(v_b0, 16), _mm_srli_epi32(v_b1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk4, 16), _mm_srli_epi32(layer3_chunk5, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk4, 16), _mm_srli_epi32(layer2_chunk5, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); + v_g0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_b1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk4, 16), _mm_srli_epi32(layer1_chunk5, 16)); +} + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer3_chunk6 = _mm_packus_epi32(_mm_srli_epi32(v_b0, 16), _mm_srli_epi32(v_b1, 16)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_and_si128(v_a0, v_mask), _mm_and_si128(v_a1, v_mask)); + __m128i layer3_chunk7 = _mm_packus_epi32(_mm_srli_epi32(v_a0, 16), _mm_srli_epi32(v_a1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk6 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk4, 16), _mm_srli_epi32(layer3_chunk5, 16)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_and_si128(layer3_chunk6, v_mask), _mm_and_si128(layer3_chunk7, v_mask)); + __m128i layer2_chunk7 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk6, 16), _mm_srli_epi32(layer3_chunk7, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk6 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk4, 16), _mm_srli_epi32(layer2_chunk5, 16)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_and_si128(layer2_chunk6, v_mask), _mm_and_si128(layer2_chunk7, v_mask)); + __m128i layer1_chunk7 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk6, 16), _mm_srli_epi32(layer2_chunk7, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_b0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); + v_g0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_a0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk4, 16), _mm_srli_epi32(layer1_chunk5, 16)); + v_g1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk6, v_mask), _mm_and_si128(layer1_chunk7, v_mask)); + v_a1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk6, 16), _mm_srli_epi32(layer1_chunk7, 16)); +} + +#endif // CV_SSE4_1 + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_g0); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_g0); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_g1); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_g1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk2); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk2); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk3); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk3); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk2); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk2); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk3); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk3); +} + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, + __m128 & v_g1, __m128 & v_b0, __m128 & v_b1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_g1); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_g1); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_b0); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_b0); + __m128 layer1_chunk4 = _mm_unpacklo_ps(v_g0, v_b1); + __m128 layer1_chunk5 = _mm_unpackhi_ps(v_g0, v_b1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk3); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk3); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk4); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk4); + __m128 layer2_chunk4 = _mm_unpacklo_ps(layer1_chunk2, layer1_chunk5); + __m128 layer2_chunk5 = _mm_unpackhi_ps(layer1_chunk2, layer1_chunk5); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk3); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk3); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk4); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk4); + v_b0 = _mm_unpacklo_ps(layer2_chunk2, layer2_chunk5); + v_b1 = _mm_unpackhi_ps(layer2_chunk2, layer2_chunk5); +} + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1, + __m128 & v_b0, __m128 & v_b1, __m128 & v_a0, __m128 & v_a1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_b0); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_b0); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_b1); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_b1); + __m128 layer1_chunk4 = _mm_unpacklo_ps(v_g0, v_a0); + __m128 layer1_chunk5 = _mm_unpackhi_ps(v_g0, v_a0); + __m128 layer1_chunk6 = _mm_unpacklo_ps(v_g1, v_a1); + __m128 layer1_chunk7 = _mm_unpackhi_ps(v_g1, v_a1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk4); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk4); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk5); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk5); + __m128 layer2_chunk4 = _mm_unpacklo_ps(layer1_chunk2, layer1_chunk6); + __m128 layer2_chunk5 = _mm_unpackhi_ps(layer1_chunk2, layer1_chunk6); + __m128 layer2_chunk6 = _mm_unpacklo_ps(layer1_chunk3, layer1_chunk7); + __m128 layer2_chunk7 = _mm_unpackhi_ps(layer1_chunk3, layer1_chunk7); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk4); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk4); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk5); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk5); + v_b0 = _mm_unpacklo_ps(layer2_chunk2, layer2_chunk6); + v_b1 = _mm_unpackhi_ps(layer2_chunk2, layer2_chunk6); + v_a0 = _mm_unpacklo_ps(layer2_chunk3, layer2_chunk7); + v_a1 = _mm_unpackhi_ps(layer2_chunk3, layer2_chunk7); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_g0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_g1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, + __m128 & v_g1, __m128 & v_b0, __m128 & v_b1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk4 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_b0, v_b1, mask_lo); + __m128 layer2_chunk5 = _mm_shuffle_ps(v_b0, v_b1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk4 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_lo); + __m128 layer1_chunk5 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_g1 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_b0 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); + v_g0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_lo); + v_b1 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_hi); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1, + __m128 & v_b0, __m128 & v_b1, __m128 & v_a0, __m128 & v_a1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk4 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk5 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_b0, v_b1, mask_lo); + __m128 layer2_chunk6 = _mm_shuffle_ps(v_b0, v_b1, mask_hi); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_a0, v_a1, mask_lo); + __m128 layer2_chunk7 = _mm_shuffle_ps(v_a0, v_a1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk4 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk5 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_lo); + __m128 layer1_chunk6 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_hi); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk6, layer2_chunk7, mask_lo); + __m128 layer1_chunk7 = _mm_shuffle_ps(layer2_chunk6, layer2_chunk7, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_b0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_b1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); + v_g0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_lo); + v_a0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_hi); + v_g1 = _mm_shuffle_ps(layer1_chunk6, layer1_chunk7, mask_lo); + v_a1 = _mm_shuffle_ps(layer1_chunk6, layer1_chunk7, mask_hi); +} + +#endif // CV_SSE2 + +//! @} + +#endif //OPENCV_CORE_SSE_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/traits.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/traits.hpp new file mode 100644 index 0000000..6cb10f4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/traits.hpp @@ -0,0 +1,397 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TRAITS_HPP +#define OPENCV_CORE_TRAITS_HPP + +#include "opencv2/core/cvdef.h" + +namespace cv +{ + +//#define OPENCV_TRAITS_ENABLE_DEPRECATED + +//! @addtogroup core_basic +//! @{ + +/** @brief Template "trait" class for OpenCV primitive data types. + +@note Deprecated. This is replaced by "single purpose" traits: traits::Type and traits::Depth + +A primitive OpenCV data type is one of unsigned char, bool, signed char, unsigned short, signed +short, int, float, double, or a tuple of values of one of these types, where all the values in the +tuple have the same type. Any primitive type from the list can be defined by an identifier in the +form CV_\{U|S|F}C(\), for example: uchar \~ CV_8UC1, 3-element +floating-point tuple \~ CV_32FC3, and so on. A universal OpenCV structure that is able to store a +single instance of such a primitive data type is Vec. Multiple instances of such a type can be +stored in a std::vector, Mat, Mat_, SparseMat, SparseMat_, or any other container that is able to +store Vec instances. + +The DataType class is basically used to provide a description of such primitive data types without +adding any fields or methods to the corresponding classes (and it is actually impossible to add +anything to primitive C/C++ data types). This technique is known in C++ as class traits. It is not +DataType itself that is used but its specialized versions, such as: +@code + template<> class DataType + { + typedef uchar value_type; + typedef int work_type; + typedef uchar channel_type; + enum { channel_type = CV_8U, channels = 1, fmt='u', type = CV_8U }; + }; + ... + template DataType > + { + typedef std::complex<_Tp> value_type; + typedef std::complex<_Tp> work_type; + typedef _Tp channel_type; + // DataDepth is another helper trait class + enum { depth = DataDepth<_Tp>::value, channels=2, + fmt=(channels-1)*256+DataDepth<_Tp>::fmt, + type=CV_MAKETYPE(depth, channels) }; + }; + ... +@endcode +The main purpose of this class is to convert compilation-time type information to an +OpenCV-compatible data type identifier, for example: +@code + // allocates a 30x40 floating-point matrix + Mat A(30, 40, DataType::type); + + Mat B = Mat_ >(3, 3); + // the statement below will print 6, 2 , that is depth == CV_64F, channels == 2 + cout << B.depth() << ", " << B.channels() << endl; +@endcode +So, such traits are used to tell OpenCV which data type you are working with, even if such a type is +not native to OpenCV. For example, the matrix B initialization above is compiled because OpenCV +defines the proper specialized template class DataType\ \> . This mechanism is also +useful (and used in OpenCV this way) for generic algorithms implementations. + +@note Default values were dropped to stop confusing developers about using of unsupported types (see #7599) +*/ +template class DataType +{ +public: +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + typedef _Tp value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 1, + depth = -1, + channels = 1, + fmt = 0, + type = CV_MAKETYPE(depth, channels) + }; +#endif +}; + +template<> class DataType +{ +public: + typedef bool value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8U, + channels = 1, + fmt = (int)'u', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef uchar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8U, + channels = 1, + fmt = (int)'u', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef schar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8S, + channels = 1, + fmt = (int)'c', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef schar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8S, + channels = 1, + fmt = (int)'c', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef ushort value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_16U, + channels = 1, + fmt = (int)'w', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef short value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_16S, + channels = 1, + fmt = (int)'s', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef int value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_32S, + channels = 1, + fmt = (int)'i', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef float value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_32F, + channels = 1, + fmt = (int)'f', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef double value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_64F, + channels = 1, + fmt = (int)'d', + type = CV_MAKETYPE(depth, channels) + }; +}; + + +/** @brief A helper class for cv::DataType + +The class is specialized for each fundamental numerical data type supported by OpenCV. It provides +DataDepth::value constant. +*/ +template class DataDepth +{ +public: + enum + { + value = DataType<_Tp>::depth, + fmt = DataType<_Tp>::fmt + }; +}; + + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + +template class TypeDepth +{ +#ifdef OPENCV_TRAITS_ENABLE_LEGACY_DEFAULTS + enum { depth = CV_USRTYPE1 }; + typedef void value_type; +#endif +}; + +template<> class TypeDepth +{ + enum { depth = CV_8U }; + typedef uchar value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_8S }; + typedef schar value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_16U }; + typedef ushort value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_16S }; + typedef short value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_32S }; + typedef int value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_32F }; + typedef float value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_64F }; + typedef double value_type; +}; + +#endif + +//! @} + +namespace traits { + +namespace internal { +#define CV_CREATE_MEMBER_CHECK(X) \ +template class CheckMember_##X { \ + struct Fallback { int X; }; \ + struct Derived : T, Fallback { }; \ + template struct Check; \ + typedef char CV_NO[1]; \ + typedef char CV_YES[2]; \ + template static CV_NO & func(Check *); \ + template static CV_YES & func(...); \ +public: \ + typedef CheckMember_##X type; \ + enum { value = sizeof(func(0)) == sizeof(CV_YES) }; \ +}; + +CV_CREATE_MEMBER_CHECK(fmt) +CV_CREATE_MEMBER_CHECK(type) + +} // namespace internal + + +template +struct Depth +{ enum { value = DataType::depth }; }; + +template +struct Type +{ enum { value = DataType::type }; }; + +/** Similar to traits::Type but has value = -1 in case of unknown type (instead of compiler error) */ +template >::value > +struct SafeType {}; + +template +struct SafeType +{ enum { value = -1 }; }; + +template +struct SafeType +{ enum { value = Type::value }; }; + + +template >::value > +struct SafeFmt {}; + +template +struct SafeFmt +{ enum { fmt = 0 }; }; + +template +struct SafeFmt +{ enum { fmt = DataType::fmt }; }; + + +} // namespace + +} // cv + +#endif // OPENCV_CORE_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/types.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/types.hpp new file mode 100644 index 0000000..4d04bef --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/types.hpp @@ -0,0 +1,2391 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TYPES_HPP +#define OPENCV_CORE_TYPES_HPP + +#ifndef __cplusplus +# error types.hpp header must be compiled as C++ +#endif + +#include +#include +#include +#include + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/matx.hpp" + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +//////////////////////////////// Complex ////////////////////////////// + +/** @brief A complex number class. + + The template class is similar and compatible with std::complex, however it provides slightly + more convenient access to the real and imaginary parts using through the simple field access, as opposite + to std::complex::real() and std::complex::imag(). +*/ +template class Complex +{ +public: + + //! default constructor + Complex(); + Complex( _Tp _re, _Tp _im = 0 ); + + //! conversion to another data type + template operator Complex() const; + //! conjugation + Complex conj() const; + + _Tp re, im; //< the real and the imaginary parts +}; + +typedef Complex Complexf; +typedef Complex Complexd; + +template class DataType< Complex<_Tp> > +{ +public: + typedef Complex<_Tp> value_type; + typedef value_type work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Complex<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Complex<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + + +//////////////////////////////// Point_ //////////////////////////////// + +/** @brief Template class for 2D points specified by its coordinates `x` and `y`. + +An instance of the class is interchangeable with C structures, CvPoint and CvPoint2D32f . There is +also a cast operator to convert point coordinates to the specified type. The conversion from +floating-point coordinates to integer coordinates is done by rounding. Commonly, the conversion +uses this operation for each of the coordinates. Besides the class members listed in the +declaration above, the following operations on points are implemented: +@code + pt1 = pt2 + pt3; + pt1 = pt2 - pt3; + pt1 = pt2 * a; + pt1 = a * pt2; + pt1 = pt2 / a; + pt1 += pt2; + pt1 -= pt2; + pt1 *= a; + pt1 /= a; + double value = norm(pt); // L2 norm + pt1 == pt2; + pt1 != pt2; +@endcode +For your convenience, the following type aliases are defined: +@code + typedef Point_ Point2i; + typedef Point2i Point; + typedef Point_ Point2f; + typedef Point_ Point2d; +@endcode +Example: +@code + Point2f a(0.3f, 0.f), b(0.f, 0.4f); + Point pt = (a + b)*10.f; + cout << pt.x << ", " << pt.y << endl; +@endcode +*/ +template class Point_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Point_(); + Point_(_Tp _x, _Tp _y); + Point_(const Point_& pt); + Point_(const Size_<_Tp>& sz); + Point_(const Vec<_Tp, 2>& v); + + Point_& operator = (const Point_& pt); + //! conversion to another data type + template operator Point_<_Tp2>() const; + + //! conversion to the old-style C structures + operator Vec<_Tp, 2>() const; + + //! dot product + _Tp dot(const Point_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point_& pt) const; + //! cross-product + double cross(const Point_& pt) const; + //! checks whether the point is inside the specified rectangle + bool inside(const Rect_<_Tp>& r) const; + _Tp x; //!< x coordinate of the point + _Tp y; //!< y coordinate of the point +}; + +typedef Point_ Point2i; +typedef Point_ Point2l; +typedef Point_ Point2f; +typedef Point_ Point2d; +typedef Point2i Point; + +template class DataType< Point_<_Tp> > +{ +public: + typedef Point_<_Tp> value_type; + typedef Point_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Point_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Point_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + + +//////////////////////////////// Point3_ //////////////////////////////// + +/** @brief Template class for 3D points specified by its coordinates `x`, `y` and `z`. + +An instance of the class is interchangeable with the C structure CvPoint2D32f . Similarly to +Point_ , the coordinates of 3D points can be converted to another type. The vector arithmetic and +comparison operations are also supported. + +The following Point3_\<\> aliases are available: +@code + typedef Point3_ Point3i; + typedef Point3_ Point3f; + typedef Point3_ Point3d; +@endcode +@see cv::Point3i, cv::Point3f and cv::Point3d +*/ +template class Point3_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Point3_(); + Point3_(_Tp _x, _Tp _y, _Tp _z); + Point3_(const Point3_& pt); + explicit Point3_(const Point_<_Tp>& pt); + Point3_(const Vec<_Tp, 3>& v); + + Point3_& operator = (const Point3_& pt); + //! conversion to another data type + template operator Point3_<_Tp2>() const; + //! conversion to cv::Vec<> +#if OPENCV_ABI_COMPATIBILITY > 300 + template operator Vec<_Tp2, 3>() const; +#else + operator Vec<_Tp, 3>() const; +#endif + + //! dot product + _Tp dot(const Point3_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point3_& pt) const; + //! cross product of the 2 3D points + Point3_ cross(const Point3_& pt) const; + _Tp x; //!< x coordinate of the 3D point + _Tp y; //!< y coordinate of the 3D point + _Tp z; //!< z coordinate of the 3D point +}; + +typedef Point3_ Point3i; +typedef Point3_ Point3f; +typedef Point3_ Point3d; + +template class DataType< Point3_<_Tp> > +{ +public: + typedef Point3_<_Tp> value_type; + typedef Point3_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 3, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Point3_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Point3_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 3) }; }; +} // namespace + +//////////////////////////////// Size_ //////////////////////////////// + +/** @brief Template class for specifying the size of an image or rectangle. + +The class includes two members called width and height. The structure can be converted to and from +the old OpenCV structures CvSize and CvSize2D32f . The same set of arithmetic and comparison +operations as for Point_ is available. + +OpenCV defines the following Size_\<\> aliases: +@code + typedef Size_ Size2i; + typedef Size2i Size; + typedef Size_ Size2f; +@endcode +*/ +template class Size_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Size_(); + Size_(_Tp _width, _Tp _height); + Size_(const Size_& sz); + Size_(const Point_<_Tp>& pt); + + Size_& operator = (const Size_& sz); + //! the area (width*height) + _Tp area() const; + //! true if empty + bool empty() const; + + //! conversion of another data type. + template operator Size_<_Tp2>() const; + + _Tp width; //!< the width + _Tp height; //!< the height +}; + +typedef Size_ Size2i; +typedef Size_ Size2l; +typedef Size_ Size2f; +typedef Size_ Size2d; +typedef Size2i Size; + +template class DataType< Size_<_Tp> > +{ +public: + typedef Size_<_Tp> value_type; + typedef Size_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Size_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Size_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + +//////////////////////////////// Rect_ //////////////////////////////// + +/** @brief Template class for 2D rectangles + +described by the following parameters: +- Coordinates of the top-left corner. This is a default interpretation of Rect_::x and Rect_::y + in OpenCV. Though, in your algorithms you may count x and y from the bottom-left corner. +- Rectangle width and height. + +OpenCV typically assumes that the top and left boundary of the rectangle are inclusive, while the +right and bottom boundaries are not. For example, the method Rect_::contains returns true if + +\f[x \leq pt.x < x+width, + y \leq pt.y < y+height\f] + +Virtually every loop over an image ROI in OpenCV (where ROI is specified by Rect_\ ) is +implemented as: +@code + for(int y = roi.y; y < roi.y + roi.height; y++) + for(int x = roi.x; x < roi.x + roi.width; x++) + { + // ... + } +@endcode +In addition to the class members, the following operations on rectangles are implemented: +- \f$\texttt{rect} = \texttt{rect} \pm \texttt{point}\f$ (shifting a rectangle by a certain offset) +- \f$\texttt{rect} = \texttt{rect} \pm \texttt{size}\f$ (expanding or shrinking a rectangle by a + certain amount) +- rect += point, rect -= point, rect += size, rect -= size (augmenting operations) +- rect = rect1 & rect2 (rectangle intersection) +- rect = rect1 | rect2 (minimum area rectangle containing rect1 and rect2 ) +- rect &= rect1, rect |= rect1 (and the corresponding augmenting operations) +- rect == rect1, rect != rect1 (rectangle comparison) + +This is an example how the partial ordering on rectangles can be established (rect1 \f$\subseteq\f$ +rect2): +@code + template inline bool + operator <= (const Rect_<_Tp>& r1, const Rect_<_Tp>& r2) + { + return (r1 & r2) == r1; + } +@endcode +For your convenience, the Rect_\<\> alias is available: cv::Rect +*/ +template class Rect_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Rect_(); + Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height); + Rect_(const Rect_& r); + Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz); + Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2); + + Rect_& operator = ( const Rect_& r ); + //! the top-left corner + Point_<_Tp> tl() const; + //! the bottom-right corner + Point_<_Tp> br() const; + + //! size (width, height) of the rectangle + Size_<_Tp> size() const; + //! area (width*height) of the rectangle + _Tp area() const; + //! true if empty + bool empty() const; + + //! conversion to another data type + template operator Rect_<_Tp2>() const; + + //! checks whether the rectangle contains the point + bool contains(const Point_<_Tp>& pt) const; + + _Tp x; //!< x coordinate of the top-left corner + _Tp y; //!< y coordinate of the top-left corner + _Tp width; //!< width of the rectangle + _Tp height; //!< height of the rectangle +}; + +typedef Rect_ Rect2i; +typedef Rect_ Rect2f; +typedef Rect_ Rect2d; +typedef Rect2i Rect; + +template class DataType< Rect_<_Tp> > +{ +public: + typedef Rect_<_Tp> value_type; + typedef Rect_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 4, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Rect_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Rect_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 4) }; }; +} // namespace + +///////////////////////////// RotatedRect ///////////////////////////// + +/** @brief The class represents rotated (i.e. not up-right) rectangles on a plane. + +Each rectangle is specified by the center point (mass center), length of each side (represented by +#Size2f structure) and the rotation angle in degrees. + +The sample below demonstrates how to use RotatedRect: +@snippet snippets/core_various.cpp RotatedRect_demo +![image](pics/rotatedrect.png) + +@sa CamShift, fitEllipse, minAreaRect, CvBox2D +*/ +class CV_EXPORTS RotatedRect +{ +public: + //! default constructor + RotatedRect(); + /** full constructor + @param center The rectangle mass center. + @param size Width and height of the rectangle. + @param angle The rotation angle in a clockwise direction. When the angle is 0, 90, 180, 270 etc., + the rectangle becomes an up-right rectangle. + */ + RotatedRect(const Point2f& center, const Size2f& size, float angle); + /** + Any 3 end points of the RotatedRect. They must be given in order (either clockwise or + anticlockwise). + */ + RotatedRect(const Point2f& point1, const Point2f& point2, const Point2f& point3); + + /** returns 4 vertices of the rectangle + @param pts The points array for storing rectangle vertices. The order is bottomLeft, topLeft, topRight, bottomRight. + */ + void points(Point2f pts[]) const; + //! returns the minimal up-right integer rectangle containing the rotated rectangle + Rect boundingRect() const; + //! returns the minimal (exact) floating point rectangle containing the rotated rectangle, not intended for use with images + Rect_ boundingRect2f() const; + //! returns the rectangle mass center + Point2f center; + //! returns width and height of the rectangle + Size2f size; + //! returns the rotation angle. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle. + float angle; +}; + +template<> class DataType< RotatedRect > +{ +public: + typedef RotatedRect value_type; + typedef value_type work_type; + typedef float channel_type; + + enum { generic_type = 0, + channels = (int)sizeof(value_type)/sizeof(channel_type), // 5 + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< RotatedRect > { enum { value = Depth::value }; }; +template<> +struct Type< RotatedRect > { enum { value = CV_MAKETYPE(Depth::value, (int)sizeof(RotatedRect)/sizeof(float)) }; }; +} // namespace + + +//////////////////////////////// Range ///////////////////////////////// + +/** @brief Template class specifying a continuous subsequence (slice) of a sequence. + +The class is used to specify a row or a column span in a matrix ( Mat ) and for many other purposes. +Range(a,b) is basically the same as a:b in Matlab or a..b in Python. As in Python, start is an +inclusive left boundary of the range and end is an exclusive right boundary of the range. Such a +half-opened interval is usually denoted as \f$[start,end)\f$ . + +The static method Range::all() returns a special variable that means "the whole sequence" or "the +whole range", just like " : " in Matlab or " ... " in Python. All the methods and functions in +OpenCV that take Range support this special Range::all() value. But, of course, in case of your own +custom processing, you will probably have to check and handle it explicitly: +@code + void my_function(..., const Range& r, ....) + { + if(r == Range::all()) { + // process all the data + } + else { + // process [r.start, r.end) + } + } +@endcode +*/ +class CV_EXPORTS Range +{ +public: + Range(); + Range(int _start, int _end); + int size() const; + bool empty() const; + static Range all(); + + int start, end; +}; + +template<> class DataType +{ +public: + typedef Range value_type; + typedef value_type work_type; + typedef int channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< Range > { enum { value = Depth::value }; }; +template<> +struct Type< Range > { enum { value = CV_MAKETYPE(Depth::value, 2) }; }; +} // namespace + + +//////////////////////////////// Scalar_ /////////////////////////////// + +/** @brief Template class for a 4-element vector derived from Vec. + +Being derived from Vec\<_Tp, 4\> , Scalar\_ and Scalar can be used just as typical 4-element +vectors. In addition, they can be converted to/from CvScalar . The type Scalar is widely used in +OpenCV to pass pixel values. +*/ +template class Scalar_ : public Vec<_Tp, 4> +{ +public: + //! default constructor + Scalar_(); + Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0); + Scalar_(_Tp v0); + + template + Scalar_(const Vec<_Tp2, cn>& v); + + //! returns a scalar with all elements set to v0 + static Scalar_<_Tp> all(_Tp v0); + + //! conversion to another data type + template operator Scalar_() const; + + //! per-element product + Scalar_<_Tp> mul(const Scalar_<_Tp>& a, double scale=1 ) const; + + //! returns (v0, -v1, -v2, -v3) + Scalar_<_Tp> conj() const; + + //! returns true iff v1 == v2 == v3 == 0 + bool isReal() const; +}; + +typedef Scalar_ Scalar; + +template class DataType< Scalar_<_Tp> > +{ +public: + typedef Scalar_<_Tp> value_type; + typedef Scalar_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 4, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Scalar_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Scalar_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 4) }; }; +} // namespace + + +/////////////////////////////// KeyPoint //////////////////////////////// + +/** @brief Data structure for salient point detectors. + +The class instance stores a keypoint, i.e. a point feature found by one of many available keypoint +detectors, such as Harris corner detector, #FAST, %StarDetector, %SURF, %SIFT etc. + +The keypoint is characterized by the 2D position, scale (proportional to the diameter of the +neighborhood that needs to be taken into account), orientation and some other parameters. The +keypoint neighborhood is then analyzed by another algorithm that builds a descriptor (usually +represented as a feature vector). The keypoints representing the same object in different images +can then be matched using %KDTree or another method. +*/ +class CV_EXPORTS_W_SIMPLE KeyPoint +{ +public: + //! the default constructor + CV_WRAP KeyPoint(); + /** + @param _pt x & y coordinates of the keypoint + @param _size keypoint diameter + @param _angle keypoint orientation + @param _response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param _octave pyramid octave in which the keypoint has been detected + @param _class_id object id + */ + KeyPoint(Point2f _pt, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1); + /** + @param x x-coordinate of the keypoint + @param y y-coordinate of the keypoint + @param _size keypoint diameter + @param _angle keypoint orientation + @param _response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param _octave pyramid octave in which the keypoint has been detected + @param _class_id object id + */ + CV_WRAP KeyPoint(float x, float y, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1); + + size_t hash() const; + + /** + This method converts vector of keypoints to vector of points or the reverse, where each keypoint is + assigned the same size and the same orientation. + + @param keypoints Keypoints obtained from any feature detection algorithm like SIFT/SURF/ORB + @param points2f Array of (x,y) coordinates of each keypoint + @param keypointIndexes Array of indexes of keypoints to be converted to points. (Acts like a mask to + convert only specified keypoints) + */ + CV_WRAP static void convert(const std::vector& keypoints, + CV_OUT std::vector& points2f, + const std::vector& keypointIndexes=std::vector()); + /** @overload + @param points2f Array of (x,y) coordinates of each keypoint + @param keypoints Keypoints obtained from any feature detection algorithm like SIFT/SURF/ORB + @param size keypoint diameter + @param response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param octave pyramid octave in which the keypoint has been detected + @param class_id object id + */ + CV_WRAP static void convert(const std::vector& points2f, + CV_OUT std::vector& keypoints, + float size=1, float response=1, int octave=0, int class_id=-1); + + /** + This method computes overlap for pair of keypoints. Overlap is the ratio between area of keypoint + regions' intersection and area of keypoint regions' union (considering keypoint region as circle). + If they don't overlap, we get zero. If they coincide at same location with same size, we get 1. + @param kp1 First keypoint + @param kp2 Second keypoint + */ + CV_WRAP static float overlap(const KeyPoint& kp1, const KeyPoint& kp2); + + CV_PROP_RW Point2f pt; //!< coordinates of the keypoints + CV_PROP_RW float size; //!< diameter of the meaningful keypoint neighborhood + CV_PROP_RW float angle; //!< computed orientation of the keypoint (-1 if not applicable); + //!< it's in [0,360) degrees and measured relative to + //!< image coordinate system, ie in clockwise. + CV_PROP_RW float response; //!< the response by which the most strong keypoints have been selected. Can be used for the further sorting or subsampling + CV_PROP_RW int octave; //!< octave (pyramid layer) from which the keypoint has been extracted + CV_PROP_RW int class_id; //!< object class (if the keypoints need to be clustered by an object they belong to) +}; + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED +template<> class DataType +{ +public: + typedef KeyPoint value_type; + typedef float work_type; + typedef float channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 7 + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) + }; + + typedef Vec vec_type; +}; +#endif + + +//////////////////////////////// DMatch ///////////////////////////////// + +/** @brief Class for matching keypoint descriptors + +query descriptor index, train descriptor index, train image index, and distance between +descriptors. +*/ +class CV_EXPORTS_W_SIMPLE DMatch +{ +public: + CV_WRAP DMatch(); + CV_WRAP DMatch(int _queryIdx, int _trainIdx, float _distance); + CV_WRAP DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance); + + CV_PROP_RW int queryIdx; //!< query descriptor index + CV_PROP_RW int trainIdx; //!< train descriptor index + CV_PROP_RW int imgIdx; //!< train image index + + CV_PROP_RW float distance; + + // less is better + bool operator<(const DMatch &m) const; +}; + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED +template<> class DataType +{ +public: + typedef DMatch value_type; + typedef int work_type; + typedef int channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 4 + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) + }; + + typedef Vec vec_type; +}; +#endif + + +///////////////////////////// TermCriteria ////////////////////////////// + +/** @brief The class defining termination criteria for iterative algorithms. + +You can initialize it by default constructor and then override any parameters, or the structure may +be fully initialized using the advanced variant of the constructor. +*/ +class CV_EXPORTS TermCriteria +{ +public: + /** + Criteria type, can be one of: COUNT, EPS or COUNT + EPS + */ + enum Type + { + COUNT=1, //!< the maximum number of iterations or elements to compute + MAX_ITER=COUNT, //!< ditto + EPS=2 //!< the desired accuracy or change in parameters at which the iterative algorithm stops + }; + + //! default constructor + TermCriteria(); + /** + @param type The type of termination criteria, one of TermCriteria::Type + @param maxCount The maximum number of iterations or elements to compute. + @param epsilon The desired accuracy or change in parameters at which the iterative algorithm stops. + */ + TermCriteria(int type, int maxCount, double epsilon); + + inline bool isValid() const + { + const bool isCount = (type & COUNT) && maxCount > 0; + const bool isEps = (type & EPS) && !cvIsNaN(epsilon); + return isCount || isEps; + } + + int type; //!< the type of termination criteria: COUNT, EPS or COUNT + EPS + int maxCount; //!< the maximum number of iterations/elements + double epsilon; //!< the desired accuracy +}; + + +//! @} core_basic + +///////////////////////// raster image moments ////////////////////////// + +//! @addtogroup imgproc_shape +//! @{ + +/** @brief struct returned by cv::moments + +The spatial moments \f$\texttt{Moments::m}_{ji}\f$ are computed as: + +\f[\texttt{m} _{ji}= \sum _{x,y} \left ( \texttt{array} (x,y) \cdot x^j \cdot y^i \right )\f] + +The central moments \f$\texttt{Moments::mu}_{ji}\f$ are computed as: + +\f[\texttt{mu} _{ji}= \sum _{x,y} \left ( \texttt{array} (x,y) \cdot (x - \bar{x} )^j \cdot (y - \bar{y} )^i \right )\f] + +where \f$(\bar{x}, \bar{y})\f$ is the mass center: + +\f[\bar{x} = \frac{\texttt{m}_{10}}{\texttt{m}_{00}} , \; \bar{y} = \frac{\texttt{m}_{01}}{\texttt{m}_{00}}\f] + +The normalized central moments \f$\texttt{Moments::nu}_{ij}\f$ are computed as: + +\f[\texttt{nu} _{ji}= \frac{\texttt{mu}_{ji}}{\texttt{m}_{00}^{(i+j)/2+1}} .\f] + +@note +\f$\texttt{mu}_{00}=\texttt{m}_{00}\f$, \f$\texttt{nu}_{00}=1\f$ +\f$\texttt{nu}_{10}=\texttt{mu}_{10}=\texttt{mu}_{01}=\texttt{mu}_{10}=0\f$ , hence the values are not +stored. + +The moments of a contour are defined in the same way but computed using the Green's formula (see +). So, due to a limited raster resolution, the moments +computed for a contour are slightly different from the moments computed for the same rasterized +contour. + +@note +Since the contour moments are computed using Green formula, you may get seemingly odd results for +contours with self-intersections, e.g. a zero area (m00) for butterfly-shaped contours. + */ +class CV_EXPORTS_W_MAP Moments +{ +public: + //! the default constructor + Moments(); + //! the full constructor + Moments(double m00, double m10, double m01, double m20, double m11, + double m02, double m30, double m21, double m12, double m03 ); + ////! the conversion from CvMoments + //Moments( const CvMoments& moments ); + ////! the conversion to CvMoments + //operator CvMoments() const; + + //! @name spatial moments + //! @{ + CV_PROP_RW double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; + //! @} + + //! @name central moments + //! @{ + CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03; + //! @} + + //! @name central normalized moments + //! @{ + CV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03; + //! @} +}; + +template<> class DataType +{ +public: + typedef Moments value_type; + typedef double work_type; + typedef double channel_type; + + enum { generic_type = 0, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 24 + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< Moments > { enum { value = Depth::value }; }; +template<> +struct Type< Moments > { enum { value = CV_MAKETYPE(Depth::value, (int)(sizeof(Moments)/sizeof(double))) }; }; +} // namespace + +//! @} imgproc_shape + +//! @cond IGNORED + +///////////////////////////////////////////////////////////////////////// +///////////////////////////// Implementation //////////////////////////// +///////////////////////////////////////////////////////////////////////// + +//////////////////////////////// Complex //////////////////////////////// + +template inline +Complex<_Tp>::Complex() + : re(0), im(0) {} + +template inline +Complex<_Tp>::Complex( _Tp _re, _Tp _im ) + : re(_re), im(_im) {} + +template template inline +Complex<_Tp>::operator Complex() const +{ + return Complex(saturate_cast(re), saturate_cast(im)); +} + +template inline +Complex<_Tp> Complex<_Tp>::conj() const +{ + return Complex<_Tp>(re, -im); +} + + +template static inline +bool operator == (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return a.re == b.re && a.im == b.im; +} + +template static inline +bool operator != (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return a.re != b.re || a.im != b.im; +} + +template static inline +Complex<_Tp> operator + (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re + b.re, a.im + b.im ); +} + +template static inline +Complex<_Tp>& operator += (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a.re += b.re; a.im += b.im; + return a; +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re - b.re, a.im - b.im ); +} + +template static inline +Complex<_Tp>& operator -= (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a.re -= b.re; a.im -= b.im; + return a; +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a) +{ + return Complex<_Tp>(-a.re, -a.im); +} + +template static inline +Complex<_Tp> operator * (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re*b.re - a.im*b.im, a.re*b.im + a.im*b.re ); +} + +template static inline +Complex<_Tp> operator * (const Complex<_Tp>& a, _Tp b) +{ + return Complex<_Tp>( a.re*b, a.im*b ); +} + +template static inline +Complex<_Tp> operator * (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( a.re*b, a.im*b ); +} + +template static inline +Complex<_Tp> operator + (const Complex<_Tp>& a, _Tp b) +{ + return Complex<_Tp>( a.re + b, a.im ); +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a, _Tp b) +{ return Complex<_Tp>( a.re - b, a.im ); } + +template static inline +Complex<_Tp> operator + (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( a.re + b, a.im ); +} + +template static inline +Complex<_Tp> operator - (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( b - a.re, -a.im ); +} + +template static inline +Complex<_Tp>& operator += (Complex<_Tp>& a, _Tp b) +{ + a.re += b; return a; +} + +template static inline +Complex<_Tp>& operator -= (Complex<_Tp>& a, _Tp b) +{ + a.re -= b; return a; +} + +template static inline +Complex<_Tp>& operator *= (Complex<_Tp>& a, _Tp b) +{ + a.re *= b; a.im *= b; return a; +} + +template static inline +double abs(const Complex<_Tp>& a) +{ + return std::sqrt( (double)a.re*a.re + (double)a.im*a.im); +} + +template static inline +Complex<_Tp> operator / (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + double t = 1./((double)b.re*b.re + (double)b.im*b.im); + return Complex<_Tp>( (_Tp)((a.re*b.re + a.im*b.im)*t), + (_Tp)((-a.re*b.im + a.im*b.re)*t) ); +} + +template static inline +Complex<_Tp>& operator /= (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a = a / b; + return a; +} + +template static inline +Complex<_Tp> operator / (const Complex<_Tp>& a, _Tp b) +{ + _Tp t = (_Tp)1/b; + return Complex<_Tp>( a.re*t, a.im*t ); +} + +template static inline +Complex<_Tp> operator / (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>(b)/a; +} + +template static inline +Complex<_Tp> operator /= (const Complex<_Tp>& a, _Tp b) +{ + _Tp t = (_Tp)1/b; + a.re *= t; a.im *= t; return a; +} + + + +//////////////////////////////// 2D Point /////////////////////////////// + +template inline +Point_<_Tp>::Point_() + : x(0), y(0) {} + +template inline +Point_<_Tp>::Point_(_Tp _x, _Tp _y) + : x(_x), y(_y) {} + +template inline +Point_<_Tp>::Point_(const Point_& pt) + : x(pt.x), y(pt.y) {} + +template inline +Point_<_Tp>::Point_(const Size_<_Tp>& sz) + : x(sz.width), y(sz.height) {} + +template inline +Point_<_Tp>::Point_(const Vec<_Tp,2>& v) + : x(v[0]), y(v[1]) {} + +template inline +Point_<_Tp>& Point_<_Tp>::operator = (const Point_& pt) +{ + x = pt.x; y = pt.y; + return *this; +} + +template template inline +Point_<_Tp>::operator Point_<_Tp2>() const +{ + return Point_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y)); +} + +template inline +Point_<_Tp>::operator Vec<_Tp, 2>() const +{ + return Vec<_Tp, 2>(x, y); +} + +template inline +_Tp Point_<_Tp>::dot(const Point_& pt) const +{ + return saturate_cast<_Tp>(x*pt.x + y*pt.y); +} + +template inline +double Point_<_Tp>::ddot(const Point_& pt) const +{ + return (double)x*(double)(pt.x) + (double)y*(double)(pt.y); +} + +template inline +double Point_<_Tp>::cross(const Point_& pt) const +{ + return (double)x*pt.y - (double)y*pt.x; +} + +template inline bool +Point_<_Tp>::inside( const Rect_<_Tp>& r ) const +{ + return r.contains(*this); +} + + +template static inline +Point_<_Tp>& operator += (Point_<_Tp>& a, const Point_<_Tp>& b) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +template static inline +Point_<_Tp>& operator -= (Point_<_Tp>& a, const Point_<_Tp>& b) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +double norm(const Point_<_Tp>& pt) +{ + return std::sqrt((double)pt.x*pt.x + (double)pt.y*pt.y); +} + +template static inline +bool operator == (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y; +} + +template static inline +bool operator != (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y; +} + +template static inline +Point_<_Tp> operator + (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x + b.x), saturate_cast<_Tp>(a.y + b.y) ); +} + +template static inline +Point_<_Tp> operator - (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x - b.x), saturate_cast<_Tp>(a.y - b.y) ); +} + +template static inline +Point_<_Tp> operator - (const Point_<_Tp>& a) +{ + return Point_<_Tp>( saturate_cast<_Tp>(-a.x), saturate_cast<_Tp>(-a.y) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, int b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (int a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, float b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (float a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, double b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (double a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Matx<_Tp, 2, 2>& a, const Point_<_Tp>& b) +{ + Matx<_Tp, 2, 1> tmp = a * Vec<_Tp,2>(b.x, b.y); + return Point_<_Tp>(tmp.val[0], tmp.val[1]); +} + +template static inline +Point3_<_Tp> operator * (const Matx<_Tp, 3, 3>& a, const Point_<_Tp>& b) +{ + Matx<_Tp, 3, 1> tmp = a * Vec<_Tp,3>(b.x, b.y, 1); + return Point3_<_Tp>(tmp.val[0], tmp.val[1], tmp.val[2]); +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, int b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, float b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, double b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + + +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); + +template<> inline int normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline int64 normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline float normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.dot(pt); } + +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } + + + +//////////////////////////////// 3D Point /////////////////////////////// + +template inline +Point3_<_Tp>::Point3_() + : x(0), y(0), z(0) {} + +template inline +Point3_<_Tp>::Point3_(_Tp _x, _Tp _y, _Tp _z) + : x(_x), y(_y), z(_z) {} + +template inline +Point3_<_Tp>::Point3_(const Point3_& pt) + : x(pt.x), y(pt.y), z(pt.z) {} + +template inline +Point3_<_Tp>::Point3_(const Point_<_Tp>& pt) + : x(pt.x), y(pt.y), z(_Tp()) {} + +template inline +Point3_<_Tp>::Point3_(const Vec<_Tp, 3>& v) + : x(v[0]), y(v[1]), z(v[2]) {} + +template template inline +Point3_<_Tp>::operator Point3_<_Tp2>() const +{ + return Point3_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y), saturate_cast<_Tp2>(z)); +} + +#if OPENCV_ABI_COMPATIBILITY > 300 +template template inline +Point3_<_Tp>::operator Vec<_Tp2, 3>() const +{ + return Vec<_Tp2, 3>(x, y, z); +} +#else +template inline +Point3_<_Tp>::operator Vec<_Tp, 3>() const +{ + return Vec<_Tp, 3>(x, y, z); +} +#endif + +template inline +Point3_<_Tp>& Point3_<_Tp>::operator = (const Point3_& pt) +{ + x = pt.x; y = pt.y; z = pt.z; + return *this; +} + +template inline +_Tp Point3_<_Tp>::dot(const Point3_& pt) const +{ + return saturate_cast<_Tp>(x*pt.x + y*pt.y + z*pt.z); +} + +template inline +double Point3_<_Tp>::ddot(const Point3_& pt) const +{ + return (double)x*pt.x + (double)y*pt.y + (double)z*pt.z; +} + +template inline +Point3_<_Tp> Point3_<_Tp>::cross(const Point3_<_Tp>& pt) const +{ + return Point3_<_Tp>(y*pt.z - z*pt.y, z*pt.x - x*pt.z, x*pt.y - y*pt.x); +} + + +template static inline +Point3_<_Tp>& operator += (Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + a.x += b.x; + a.y += b.y; + a.z += b.z; + return a; +} + +template static inline +Point3_<_Tp>& operator -= (Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +double norm(const Point3_<_Tp>& pt) +{ + return std::sqrt((double)pt.x*pt.x + (double)pt.y*pt.y + (double)pt.z*pt.z); +} + +template static inline +bool operator == (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +template static inline +bool operator != (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y || a.z != b.z; +} + +template static inline +Point3_<_Tp> operator + (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x + b.x), saturate_cast<_Tp>(a.y + b.y), saturate_cast<_Tp>(a.z + b.z)); +} + +template static inline +Point3_<_Tp> operator - (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x - b.x), saturate_cast<_Tp>(a.y - b.y), saturate_cast<_Tp>(a.z - b.z)); +} + +template static inline +Point3_<_Tp> operator - (const Point3_<_Tp>& a) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(-a.x), saturate_cast<_Tp>(-a.y), saturate_cast<_Tp>(-a.z) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, int b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b), saturate_cast<_Tp>(a.z*b) ); +} + +template static inline +Point3_<_Tp> operator * (int a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, float b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x * b), saturate_cast<_Tp>(a.y * b), saturate_cast<_Tp>(a.z * b) ); +} + +template static inline +Point3_<_Tp> operator * (float a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, double b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x * b), saturate_cast<_Tp>(a.y * b), saturate_cast<_Tp>(a.z * b) ); +} + +template static inline +Point3_<_Tp> operator * (double a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Matx<_Tp, 3, 3>& a, const Point3_<_Tp>& b) +{ + Matx<_Tp, 3, 1> tmp = a * Vec<_Tp,3>(b.x, b.y, b.z); + return Point3_<_Tp>(tmp.val[0], tmp.val[1], tmp.val[2]); +} + +template static inline +Matx<_Tp, 4, 1> operator * (const Matx<_Tp, 4, 4>& a, const Point3_<_Tp>& b) +{ + return a * Matx<_Tp, 4, 1>(b.x, b.y, b.z, 1); +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, int b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, float b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, double b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + + + +////////////////////////////////// Size ///////////////////////////////// + +template inline +Size_<_Tp>::Size_() + : width(0), height(0) {} + +template inline +Size_<_Tp>::Size_(_Tp _width, _Tp _height) + : width(_width), height(_height) {} + +template inline +Size_<_Tp>::Size_(const Size_& sz) + : width(sz.width), height(sz.height) {} + +template inline +Size_<_Tp>::Size_(const Point_<_Tp>& pt) + : width(pt.x), height(pt.y) {} + +template template inline +Size_<_Tp>::operator Size_<_Tp2>() const +{ + return Size_<_Tp2>(saturate_cast<_Tp2>(width), saturate_cast<_Tp2>(height)); +} + +template inline +Size_<_Tp>& Size_<_Tp>::operator = (const Size_<_Tp>& sz) +{ + width = sz.width; height = sz.height; + return *this; +} + +template inline +_Tp Size_<_Tp>::area() const +{ + const _Tp result = width * height; + CV_DbgAssert(!std::numeric_limits<_Tp>::is_integer + || width == 0 || result / width == height); // make sure the result fits in the return value + return result; +} + +template inline +bool Size_<_Tp>::empty() const +{ + return width <= 0 || height <= 0; +} + + +template static inline +Size_<_Tp>& operator *= (Size_<_Tp>& a, _Tp b) +{ + a.width *= b; + a.height *= b; + return a; +} + +template static inline +Size_<_Tp> operator * (const Size_<_Tp>& a, _Tp b) +{ + Size_<_Tp> tmp(a); + tmp *= b; + return tmp; +} + +template static inline +Size_<_Tp>& operator /= (Size_<_Tp>& a, _Tp b) +{ + a.width /= b; + a.height /= b; + return a; +} + +template static inline +Size_<_Tp> operator / (const Size_<_Tp>& a, _Tp b) +{ + Size_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Size_<_Tp>& operator += (Size_<_Tp>& a, const Size_<_Tp>& b) +{ + a.width += b.width; + a.height += b.height; + return a; +} + +template static inline +Size_<_Tp> operator + (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + Size_<_Tp> tmp(a); + tmp += b; + return tmp; +} + +template static inline +Size_<_Tp>& operator -= (Size_<_Tp>& a, const Size_<_Tp>& b) +{ + a.width -= b.width; + a.height -= b.height; + return a; +} + +template static inline +Size_<_Tp> operator - (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + Size_<_Tp> tmp(a); + tmp -= b; + return tmp; +} + +template static inline +bool operator == (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + return a.width == b.width && a.height == b.height; +} + +template static inline +bool operator != (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + return !(a == b); +} + + + +////////////////////////////////// Rect ///////////////////////////////// + +template inline +Rect_<_Tp>::Rect_() + : x(0), y(0), width(0), height(0) {} + +template inline +Rect_<_Tp>::Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height) + : x(_x), y(_y), width(_width), height(_height) {} + +template inline +Rect_<_Tp>::Rect_(const Rect_<_Tp>& r) + : x(r.x), y(r.y), width(r.width), height(r.height) {} + +template inline +Rect_<_Tp>::Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz) + : x(org.x), y(org.y), width(sz.width), height(sz.height) {} + +template inline +Rect_<_Tp>::Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2) +{ + x = std::min(pt1.x, pt2.x); + y = std::min(pt1.y, pt2.y); + width = std::max(pt1.x, pt2.x) - x; + height = std::max(pt1.y, pt2.y) - y; +} + +template inline +Rect_<_Tp>& Rect_<_Tp>::operator = ( const Rect_<_Tp>& r ) +{ + x = r.x; + y = r.y; + width = r.width; + height = r.height; + return *this; +} + +template inline +Point_<_Tp> Rect_<_Tp>::tl() const +{ + return Point_<_Tp>(x,y); +} + +template inline +Point_<_Tp> Rect_<_Tp>::br() const +{ + return Point_<_Tp>(x + width, y + height); +} + +template inline +Size_<_Tp> Rect_<_Tp>::size() const +{ + return Size_<_Tp>(width, height); +} + +template inline +_Tp Rect_<_Tp>::area() const +{ + const _Tp result = width * height; + CV_DbgAssert(!std::numeric_limits<_Tp>::is_integer + || width == 0 || result / width == height); // make sure the result fits in the return value + return result; +} + +template inline +bool Rect_<_Tp>::empty() const +{ + return width <= 0 || height <= 0; +} + +template template inline +Rect_<_Tp>::operator Rect_<_Tp2>() const +{ + return Rect_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y), saturate_cast<_Tp2>(width), saturate_cast<_Tp2>(height)); +} + +template inline +bool Rect_<_Tp>::contains(const Point_<_Tp>& pt) const +{ + return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height; +} + + +template static inline +Rect_<_Tp>& operator += ( Rect_<_Tp>& a, const Point_<_Tp>& b ) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +template static inline +Rect_<_Tp>& operator -= ( Rect_<_Tp>& a, const Point_<_Tp>& b ) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +template static inline +Rect_<_Tp>& operator += ( Rect_<_Tp>& a, const Size_<_Tp>& b ) +{ + a.width += b.width; + a.height += b.height; + return a; +} + +template static inline +Rect_<_Tp>& operator -= ( Rect_<_Tp>& a, const Size_<_Tp>& b ) +{ + const _Tp width = a.width - b.width; + const _Tp height = a.height - b.height; + CV_DbgAssert(width >= 0 && height >= 0); + a.width = width; + a.height = height; + return a; +} + +template static inline +Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b ) +{ + _Tp x1 = std::max(a.x, b.x); + _Tp y1 = std::max(a.y, b.y); + a.width = std::min(a.x + a.width, b.x + b.width) - x1; + a.height = std::min(a.y + a.height, b.y + b.height) - y1; + a.x = x1; + a.y = y1; + if( a.width <= 0 || a.height <= 0 ) + a = Rect(); + return a; +} + +template static inline +Rect_<_Tp>& operator |= ( Rect_<_Tp>& a, const Rect_<_Tp>& b ) +{ + if (a.empty()) { + a = b; + } + else if (!b.empty()) { + _Tp x1 = std::min(a.x, b.x); + _Tp y1 = std::min(a.y, b.y); + a.width = std::max(a.x + a.width, b.x + b.width) - x1; + a.height = std::max(a.y + a.height, b.y + b.height) - y1; + a.x = x1; + a.y = y1; + } + return a; +} + +template static inline +bool operator == (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; +} + +template static inline +bool operator != (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y || a.width != b.width || a.height != b.height; +} + +template static inline +Rect_<_Tp> operator + (const Rect_<_Tp>& a, const Point_<_Tp>& b) +{ + return Rect_<_Tp>( a.x + b.x, a.y + b.y, a.width, a.height ); +} + +template static inline +Rect_<_Tp> operator - (const Rect_<_Tp>& a, const Point_<_Tp>& b) +{ + return Rect_<_Tp>( a.x - b.x, a.y - b.y, a.width, a.height ); +} + +template static inline +Rect_<_Tp> operator + (const Rect_<_Tp>& a, const Size_<_Tp>& b) +{ + return Rect_<_Tp>( a.x, a.y, a.width + b.width, a.height + b.height ); +} + +template static inline +Rect_<_Tp> operator - (const Rect_<_Tp>& a, const Size_<_Tp>& b) +{ + const _Tp width = a.width - b.width; + const _Tp height = a.height - b.height; + CV_DbgAssert(width >= 0 && height >= 0); + return Rect_<_Tp>( a.x, a.y, width, height ); +} + +template static inline +Rect_<_Tp> operator & (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + Rect_<_Tp> c = a; + return c &= b; +} + +template static inline +Rect_<_Tp> operator | (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + Rect_<_Tp> c = a; + return c |= b; +} + +/** + * @brief measure dissimilarity between two sample sets + * + * computes the complement of the Jaccard Index as described in . + * For rectangles this reduces to computing the intersection over the union. + */ +template static inline +double jaccardDistance(const Rect_<_Tp>& a, const Rect_<_Tp>& b) { + _Tp Aa = a.area(); + _Tp Ab = b.area(); + + if ((Aa + Ab) <= std::numeric_limits<_Tp>::epsilon()) { + // jaccard_index = 1 -> distance = 0 + return 0.0; + } + + double Aab = (a & b).area(); + // distance = 1 - jaccard_index + return 1.0 - Aab / (Aa + Ab - Aab); +} + +////////////////////////////// RotatedRect ////////////////////////////// + +inline +RotatedRect::RotatedRect() + : center(), size(), angle(0) {} + +inline +RotatedRect::RotatedRect(const Point2f& _center, const Size2f& _size, float _angle) + : center(_center), size(_size), angle(_angle) {} + + + +///////////////////////////////// Range ///////////////////////////////// + +inline +Range::Range() + : start(0), end(0) {} + +inline +Range::Range(int _start, int _end) + : start(_start), end(_end) {} + +inline +int Range::size() const +{ + return end - start; +} + +inline +bool Range::empty() const +{ + return start == end; +} + +inline +Range Range::all() +{ + return Range(INT_MIN, INT_MAX); +} + + +static inline +bool operator == (const Range& r1, const Range& r2) +{ + return r1.start == r2.start && r1.end == r2.end; +} + +static inline +bool operator != (const Range& r1, const Range& r2) +{ + return !(r1 == r2); +} + +static inline +bool operator !(const Range& r) +{ + return r.start == r.end; +} + +static inline +Range operator & (const Range& r1, const Range& r2) +{ + Range r(std::max(r1.start, r2.start), std::min(r1.end, r2.end)); + r.end = std::max(r.end, r.start); + return r; +} + +static inline +Range& operator &= (Range& r1, const Range& r2) +{ + r1 = r1 & r2; + return r1; +} + +static inline +Range operator + (const Range& r1, int delta) +{ + return Range(r1.start + delta, r1.end + delta); +} + +static inline +Range operator + (int delta, const Range& r1) +{ + return Range(r1.start + delta, r1.end + delta); +} + +static inline +Range operator - (const Range& r1, int delta) +{ + return r1 + (-delta); +} + + + +///////////////////////////////// Scalar //////////////////////////////// + +template inline +Scalar_<_Tp>::Scalar_() +{ + this->val[0] = this->val[1] = this->val[2] = this->val[3] = 0; +} + +template inline +Scalar_<_Tp>::Scalar_(_Tp v0, _Tp v1, _Tp v2, _Tp v3) +{ + this->val[0] = v0; + this->val[1] = v1; + this->val[2] = v2; + this->val[3] = v3; +} + +template template inline +Scalar_<_Tp>::Scalar_(const Vec<_Tp2, cn>& v) +{ + int i; + for( i = 0; i < (cn < 4 ? cn : 4); i++ ) + this->val[i] = cv::saturate_cast<_Tp>(v.val[i]); + for( ; i < 4; i++ ) + this->val[i] = 0; +} + +template inline +Scalar_<_Tp>::Scalar_(_Tp v0) +{ + this->val[0] = v0; + this->val[1] = this->val[2] = this->val[3] = 0; +} + +template inline +Scalar_<_Tp> Scalar_<_Tp>::all(_Tp v0) +{ + return Scalar_<_Tp>(v0, v0, v0, v0); +} + + +template inline +Scalar_<_Tp> Scalar_<_Tp>::mul(const Scalar_<_Tp>& a, double scale ) const +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(this->val[0] * a.val[0] * scale), + saturate_cast<_Tp>(this->val[1] * a.val[1] * scale), + saturate_cast<_Tp>(this->val[2] * a.val[2] * scale), + saturate_cast<_Tp>(this->val[3] * a.val[3] * scale)); +} + +template inline +Scalar_<_Tp> Scalar_<_Tp>::conj() const +{ + return Scalar_<_Tp>(saturate_cast<_Tp>( this->val[0]), + saturate_cast<_Tp>(-this->val[1]), + saturate_cast<_Tp>(-this->val[2]), + saturate_cast<_Tp>(-this->val[3])); +} + +template inline +bool Scalar_<_Tp>::isReal() const +{ + return this->val[1] == 0 && this->val[2] == 0 && this->val[3] == 0; +} + + +template template inline +Scalar_<_Tp>::operator Scalar_() const +{ + return Scalar_(saturate_cast(this->val[0]), + saturate_cast(this->val[1]), + saturate_cast(this->val[2]), + saturate_cast(this->val[3])); +} + + +template static inline +Scalar_<_Tp>& operator += (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a.val[0] += b.val[0]; + a.val[1] += b.val[1]; + a.val[2] += b.val[2]; + a.val[3] += b.val[3]; + return a; +} + +template static inline +Scalar_<_Tp>& operator -= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a.val[0] -= b.val[0]; + a.val[1] -= b.val[1]; + a.val[2] -= b.val[2]; + a.val[3] -= b.val[3]; + return a; +} + +template static inline +Scalar_<_Tp>& operator *= ( Scalar_<_Tp>& a, _Tp v ) +{ + a.val[0] *= v; + a.val[1] *= v; + a.val[2] *= v; + a.val[3] *= v; + return a; +} + +template static inline +bool operator == ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b ) +{ + return a.val[0] == b.val[0] && a.val[1] == b.val[1] && + a.val[2] == b.val[2] && a.val[3] == b.val[3]; +} + +template static inline +bool operator != ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b ) +{ + return a.val[0] != b.val[0] || a.val[1] != b.val[1] || + a.val[2] != b.val[2] || a.val[3] != b.val[3]; +} + +template static inline +Scalar_<_Tp> operator + (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(a.val[0] + b.val[0], + a.val[1] + b.val[1], + a.val[2] + b.val[2], + a.val[3] + b.val[3]); +} + +template static inline +Scalar_<_Tp> operator - (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(a.val[0] - b.val[0]), + saturate_cast<_Tp>(a.val[1] - b.val[1]), + saturate_cast<_Tp>(a.val[2] - b.val[2]), + saturate_cast<_Tp>(a.val[3] - b.val[3])); +} + +template static inline +Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, _Tp alpha) +{ + return Scalar_<_Tp>(a.val[0] * alpha, + a.val[1] * alpha, + a.val[2] * alpha, + a.val[3] * alpha); +} + +template static inline +Scalar_<_Tp> operator * (_Tp alpha, const Scalar_<_Tp>& a) +{ + return a*alpha; +} + +template static inline +Scalar_<_Tp> operator - (const Scalar_<_Tp>& a) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(-a.val[0]), + saturate_cast<_Tp>(-a.val[1]), + saturate_cast<_Tp>(-a.val[2]), + saturate_cast<_Tp>(-a.val[3])); +} + + +template static inline +Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(a[0]*b[0] - a[1]*b[1] - a[2]*b[2] - a[3]*b[3]), + saturate_cast<_Tp>(a[0]*b[1] + a[1]*b[0] + a[2]*b[3] - a[3]*b[2]), + saturate_cast<_Tp>(a[0]*b[2] - a[1]*b[3] + a[2]*b[0] + a[3]*b[1]), + saturate_cast<_Tp>(a[0]*b[3] + a[1]*b[2] - a[2]*b[1] + a[3]*b[0])); +} + +template static inline +Scalar_<_Tp>& operator *= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a = a * b; + return a; +} + +template static inline +Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, _Tp alpha) +{ + return Scalar_<_Tp>(a.val[0] / alpha, + a.val[1] / alpha, + a.val[2] / alpha, + a.val[3] / alpha); +} + +template static inline +Scalar_ operator / (const Scalar_& a, float alpha) +{ + float s = 1 / alpha; + return Scalar_(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s); +} + +template static inline +Scalar_ operator / (const Scalar_& a, double alpha) +{ + double s = 1 / alpha; + return Scalar_(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s); +} + +template static inline +Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, _Tp alpha) +{ + a = a / alpha; + return a; +} + +template static inline +Scalar_<_Tp> operator / (_Tp a, const Scalar_<_Tp>& b) +{ + _Tp s = a / (b[0]*b[0] + b[1]*b[1] + b[2]*b[2] + b[3]*b[3]); + return b.conj() * s; +} + +template static inline +Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return a * ((_Tp)1 / b); +} + +template static inline +Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a = a / b; + return a; +} + +template static inline +Scalar operator * (const Matx<_Tp, 4, 4>& a, const Scalar& b) +{ + Matx c((Matx)a, b, Matx_MatMulOp()); + return reinterpret_cast(c); +} + +template<> inline +Scalar operator * (const Matx& a, const Scalar& b) +{ + Matx c(a, b, Matx_MatMulOp()); + return reinterpret_cast(c); +} + + + +//////////////////////////////// KeyPoint /////////////////////////////// + +inline +KeyPoint::KeyPoint() + : pt(0,0), size(0), angle(-1), response(0), octave(0), class_id(-1) {} + +inline +KeyPoint::KeyPoint(Point2f _pt, float _size, float _angle, float _response, int _octave, int _class_id) + : pt(_pt), size(_size), angle(_angle), response(_response), octave(_octave), class_id(_class_id) {} + +inline +KeyPoint::KeyPoint(float x, float y, float _size, float _angle, float _response, int _octave, int _class_id) + : pt(x, y), size(_size), angle(_angle), response(_response), octave(_octave), class_id(_class_id) {} + + + +///////////////////////////////// DMatch //////////////////////////////// + +inline +DMatch::DMatch() + : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} + +inline +DMatch::DMatch(int _queryIdx, int _trainIdx, float _distance) + : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} + +inline +DMatch::DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance) + : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} + +inline +bool DMatch::operator < (const DMatch &m) const +{ + return distance < m.distance; +} + + + +////////////////////////////// TermCriteria ///////////////////////////// + +inline +TermCriteria::TermCriteria() + : type(0), maxCount(0), epsilon(0) {} + +inline +TermCriteria::TermCriteria(int _type, int _maxCount, double _epsilon) + : type(_type), maxCount(_maxCount), epsilon(_epsilon) {} + +//! @endcond + +} // cv + +#endif //OPENCV_CORE_TYPES_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/types_c.h b/hgdriver/3rdparty/opencv/include/opencv2/core/types_c.h new file mode 100644 index 0000000..4330bc5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/types_c.h @@ -0,0 +1,2140 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TYPES_H +#define OPENCV_CORE_TYPES_H + +#ifdef CV__ENABLE_C_API_CTORS // invalid C API ctors (must be removed) +#if defined(_WIN32) && !defined(CV__SKIP_MESSAGE_MALFORMED_C_API_CTORS) +#error "C API ctors don't work on Win32: https://github.com/opencv/opencv/issues/15990" +#endif +#endif + +//#define CV__VALIDATE_UNUNITIALIZED_VARS 1 // C++11 & GCC only + +#ifdef __cplusplus + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#define CV_STRUCT_INITIALIZER {0,} +#else +#if defined(__GNUC__) && __GNUC__ == 4 // GCC 4.x warns on "= {}" initialization, fixed in GCC 5.0 +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif +#define CV_STRUCT_INITIALIZER {} +#endif + +#else +#define CV_STRUCT_INITIALIZER {0} +#endif + + +#ifdef HAVE_IPL +# ifndef __IPL_H__ +# if defined _WIN32 +# include +# else +# include +# endif +# endif +#elif defined __IPL_H__ +# define HAVE_IPL +#endif + +#include "opencv2/core/cvdef.h" + +#ifndef SKIP_INCLUDES +#include +#include +#include +#include +#endif // SKIP_INCLUDES + +#if defined _WIN32 +# define CV_CDECL __cdecl +# define CV_STDCALL __stdcall +#else +# define CV_CDECL +# define CV_STDCALL +#endif + +#ifndef CV_DEFAULT +# ifdef __cplusplus +# define CV_DEFAULT(val) = val +# else +# define CV_DEFAULT(val) +# endif +#endif + +#ifndef CV_EXTERN_C_FUNCPTR +# ifdef __cplusplus +# define CV_EXTERN_C_FUNCPTR(x) extern "C" { typedef x; } +# else +# define CV_EXTERN_C_FUNCPTR(x) typedef x +# endif +#endif + +#ifndef CVAPI +# define CVAPI(rettype) CV_EXTERN_C CV_EXPORTS rettype CV_CDECL +#endif + +#ifndef CV_IMPL +# define CV_IMPL CV_EXTERN_C +#endif + +#ifdef __cplusplus +# include "opencv2/core.hpp" +#endif + +/** @addtogroup core_c + @{ +*/ + +/** @brief This is the "metatype" used *only* as a function parameter. + +It denotes that the function accepts arrays of multiple types, such as IplImage*, CvMat* or even +CvSeq* sometimes. The particular array type is determined at runtime by analyzing the first 4 +bytes of the header. In C++ interface the role of CvArr is played by InputArray and OutputArray. + */ +typedef void CvArr; + +typedef int CVStatus; + +/** @see cv::Error::Code */ +enum { + CV_StsOk= 0, /**< everything is ok */ + CV_StsBackTrace= -1, /**< pseudo error for back trace */ + CV_StsError= -2, /**< unknown /unspecified error */ + CV_StsInternal= -3, /**< internal error (bad state) */ + CV_StsNoMem= -4, /**< insufficient memory */ + CV_StsBadArg= -5, /**< function arg/param is bad */ + CV_StsBadFunc= -6, /**< unsupported function */ + CV_StsNoConv= -7, /**< iter. didn't converge */ + CV_StsAutoTrace= -8, /**< tracing */ + CV_HeaderIsNull= -9, /**< image header is NULL */ + CV_BadImageSize= -10, /**< image size is invalid */ + CV_BadOffset= -11, /**< offset is invalid */ + CV_BadDataPtr= -12, /**/ + CV_BadStep= -13, /**< image step is wrong, this may happen for a non-continuous matrix */ + CV_BadModelOrChSeq= -14, /**/ + CV_BadNumChannels= -15, /**< bad number of channels, for example, some functions accept only single channel matrices */ + CV_BadNumChannel1U= -16, /**/ + CV_BadDepth= -17, /**< input image depth is not supported by the function */ + CV_BadAlphaChannel= -18, /**/ + CV_BadOrder= -19, /**< number of dimensions is out of range */ + CV_BadOrigin= -20, /**< incorrect input origin */ + CV_BadAlign= -21, /**< incorrect input align */ + CV_BadCallBack= -22, /**/ + CV_BadTileSize= -23, /**/ + CV_BadCOI= -24, /**< input COI is not supported */ + CV_BadROISize= -25, /**< incorrect input roi */ + CV_MaskIsTiled= -26, /**/ + CV_StsNullPtr= -27, /**< null pointer */ + CV_StsVecLengthErr= -28, /**< incorrect vector length */ + CV_StsFilterStructContentErr= -29, /**< incorrect filter structure content */ + CV_StsKernelStructContentErr= -30, /**< incorrect transform kernel content */ + CV_StsFilterOffsetErr= -31, /**< incorrect filter offset value */ + CV_StsBadSize= -201, /**< the input/output structure size is incorrect */ + CV_StsDivByZero= -202, /**< division by zero */ + CV_StsInplaceNotSupported= -203, /**< in-place operation is not supported */ + CV_StsObjectNotFound= -204, /**< request can't be completed */ + CV_StsUnmatchedFormats= -205, /**< formats of input/output arrays differ */ + CV_StsBadFlag= -206, /**< flag is wrong or not supported */ + CV_StsBadPoint= -207, /**< bad CvPoint */ + CV_StsBadMask= -208, /**< bad format of mask (neither 8uC1 nor 8sC1)*/ + CV_StsUnmatchedSizes= -209, /**< sizes of input/output structures do not match */ + CV_StsUnsupportedFormat= -210, /**< the data format/type is not supported by the function*/ + CV_StsOutOfRange= -211, /**< some of parameters are out of range */ + CV_StsParseError= -212, /**< invalid syntax/structure of the parsed file */ + CV_StsNotImplemented= -213, /**< the requested function/feature is not implemented */ + CV_StsBadMemBlock= -214, /**< an allocated block has been corrupted */ + CV_StsAssert= -215, /**< assertion failed */ + CV_GpuNotSupported= -216, /**< no CUDA support */ + CV_GpuApiCallError= -217, /**< GPU API call error */ + CV_OpenGlNotSupported= -218, /**< no OpenGL support */ + CV_OpenGlApiCallError= -219, /**< OpenGL API call error */ + CV_OpenCLApiCallError= -220, /**< OpenCL API call error */ + CV_OpenCLDoubleNotSupported= -221, + CV_OpenCLInitError= -222, /**< OpenCL initialization error */ + CV_OpenCLNoAMDBlasFft= -223 +}; + +/****************************************************************************************\ +* Common macros and inline functions * +\****************************************************************************************/ + +#define CV_SWAP(a,b,t) ((t) = (a), (a) = (b), (b) = (t)) + +/** min & max without jumps */ +#define CV_IMIN(a, b) ((a) ^ (((a)^(b)) & (((a) < (b)) - 1))) + +#define CV_IMAX(a, b) ((a) ^ (((a)^(b)) & (((a) > (b)) - 1))) + +/** absolute value without jumps */ +#ifndef __cplusplus +# define CV_IABS(a) (((a) ^ ((a) < 0 ? -1 : 0)) - ((a) < 0 ? -1 : 0)) +#else +# define CV_IABS(a) abs(a) +#endif +#define CV_CMP(a,b) (((a) > (b)) - ((a) < (b))) +#define CV_SIGN(a) CV_CMP((a),0) + +#define cvInvSqrt(value) ((float)(1./sqrt(value))) +#define cvSqrt(value) ((float)sqrt(value)) + + +/*************** Random number generation *******************/ + +typedef uint64 CvRNG; + +#define CV_RNG_COEFF 4164903690U + +/** @brief Initializes a random number generator state. + +The function initializes a random number generator and returns the state. The pointer to the state +can be then passed to the cvRandInt, cvRandReal and cvRandArr functions. In the current +implementation a multiply-with-carry generator is used. +@param seed 64-bit value used to initiate a random sequence +@sa the C++ class RNG replaced CvRNG. + */ +CV_INLINE CvRNG cvRNG( int64 seed CV_DEFAULT(-1)) +{ + CvRNG rng = seed ? (uint64)seed : (uint64)(int64)-1; + return rng; +} + +/** @brief Returns a 32-bit unsigned integer and updates RNG. + +The function returns a uniformly-distributed random 32-bit unsigned integer and updates the RNG +state. It is similar to the rand() function from the C runtime library, except that OpenCV functions +always generates a 32-bit random number, regardless of the platform. +@param rng CvRNG state initialized by cvRNG. + */ +CV_INLINE unsigned cvRandInt( CvRNG* rng ) +{ + uint64 temp = *rng; + temp = (uint64)(unsigned)temp*CV_RNG_COEFF + (temp >> 32); + *rng = temp; + return (unsigned)temp; +} + +/** @brief Returns a floating-point random number and updates RNG. + +The function returns a uniformly-distributed random floating-point number between 0 and 1 (1 is not +included). +@param rng RNG state initialized by cvRNG + */ +CV_INLINE double cvRandReal( CvRNG* rng ) +{ + return cvRandInt(rng)*2.3283064365386962890625e-10 /* 2^-32 */; +} + +/****************************************************************************************\ +* Image type (IplImage) * +\****************************************************************************************/ + +#ifndef HAVE_IPL + +/* + * The following definitions (until #endif) + * is an extract from IPL headers. + * Copyright (c) 1995 Intel Corporation. + */ +#define IPL_DEPTH_SIGN 0x80000000 + +#define IPL_DEPTH_1U 1 +#define IPL_DEPTH_8U 8 +#define IPL_DEPTH_16U 16 +#define IPL_DEPTH_32F 32 + +#define IPL_DEPTH_8S (IPL_DEPTH_SIGN| 8) +#define IPL_DEPTH_16S (IPL_DEPTH_SIGN|16) +#define IPL_DEPTH_32S (IPL_DEPTH_SIGN|32) + +#define IPL_DATA_ORDER_PIXEL 0 +#define IPL_DATA_ORDER_PLANE 1 + +#define IPL_ORIGIN_TL 0 +#define IPL_ORIGIN_BL 1 + +#define IPL_ALIGN_4BYTES 4 +#define IPL_ALIGN_8BYTES 8 +#define IPL_ALIGN_16BYTES 16 +#define IPL_ALIGN_32BYTES 32 + +#define IPL_ALIGN_DWORD IPL_ALIGN_4BYTES +#define IPL_ALIGN_QWORD IPL_ALIGN_8BYTES + +#define IPL_BORDER_CONSTANT 0 +#define IPL_BORDER_REPLICATE 1 +#define IPL_BORDER_REFLECT 2 +#define IPL_BORDER_WRAP 3 + +#ifdef __cplusplus +typedef struct _IplImage IplImage; +CV_EXPORTS _IplImage cvIplImage(const cv::Mat& m); +#endif + +/** The IplImage is taken from the Intel Image Processing Library, in which the format is native. OpenCV +only supports a subset of possible IplImage formats, as outlined in the parameter list above. + +In addition to the above restrictions, OpenCV handles ROIs differently. OpenCV functions require +that the image size or ROI size of all source and destination images match exactly. On the other +hand, the Intel Image Processing Library processes the area of intersection between the source and +destination images (or ROIs), allowing them to vary independently. +*/ +typedef struct +_IplImage +{ + int nSize; /**< sizeof(IplImage) */ + int ID; /**< version (=0)*/ + int nChannels; /**< Most of OpenCV functions support 1,2,3 or 4 channels */ + int alphaChannel; /**< Ignored by OpenCV */ + int depth; /**< Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, + IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */ + char colorModel[4]; /**< Ignored by OpenCV */ + char channelSeq[4]; /**< ditto */ + int dataOrder; /**< 0 - interleaved color channels, 1 - separate color channels. + cvCreateImage can only create interleaved images */ + int origin; /**< 0 - top-left origin, + 1 - bottom-left origin (Windows bitmaps style). */ + int align; /**< Alignment of image rows (4 or 8). + OpenCV ignores it and uses widthStep instead. */ + int width; /**< Image width in pixels. */ + int height; /**< Image height in pixels. */ + struct _IplROI *roi; /**< Image ROI. If NULL, the whole image is selected. */ + struct _IplImage *maskROI; /**< Must be NULL. */ + void *imageId; /**< " " */ + struct _IplTileInfo *tileInfo; /**< " " */ + int imageSize; /**< Image data size in bytes + (==image->height*image->widthStep + in case of interleaved data)*/ + char *imageData; /**< Pointer to aligned image data. */ + int widthStep; /**< Size of aligned image row in bytes. */ + int BorderMode[4]; /**< Ignored by OpenCV. */ + int BorderConst[4]; /**< Ditto. */ + char *imageDataOrigin; /**< Pointer to very origin of image data + (not necessarily aligned) - + needed for correct deallocation */ + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + _IplImage() {} + _IplImage(const cv::Mat& m) { *this = cvIplImage(m); } +#endif +} +IplImage; + +CV_INLINE IplImage cvIplImage() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + IplImage self = CV_STRUCT_INITIALIZER; self.nSize = sizeof(IplImage); return self; +#else + return _IplImage(); +#endif +} + +typedef struct _IplTileInfo IplTileInfo; + +typedef struct _IplROI +{ + int coi; /**< 0 - no COI (all channels are selected), 1 - 0th channel is selected ...*/ + int xOffset; + int yOffset; + int width; + int height; +} +IplROI; + +typedef struct _IplConvKernel +{ + int nCols; + int nRows; + int anchorX; + int anchorY; + int *values; + int nShiftR; +} +IplConvKernel; + +typedef struct _IplConvKernelFP +{ + int nCols; + int nRows; + int anchorX; + int anchorY; + float *values; +} +IplConvKernelFP; + +#define IPL_IMAGE_HEADER 1 +#define IPL_IMAGE_DATA 2 +#define IPL_IMAGE_ROI 4 + +#endif/*HAVE_IPL*/ + +/** extra border mode */ +#define IPL_BORDER_REFLECT_101 4 +#define IPL_BORDER_TRANSPARENT 5 + +#define IPL_IMAGE_MAGIC_VAL ((int)sizeof(IplImage)) +#define CV_TYPE_NAME_IMAGE "opencv-image" + +#define CV_IS_IMAGE_HDR(img) \ + ((img) != NULL && ((const IplImage*)(img))->nSize == sizeof(IplImage)) + +#define CV_IS_IMAGE(img) \ + (CV_IS_IMAGE_HDR(img) && ((IplImage*)img)->imageData != NULL) + +/** for storing double-precision + floating point data in IplImage's */ +#define IPL_DEPTH_64F 64 + +/** get reference to pixel at (col,row), + for multi-channel images (col) should be multiplied by number of channels */ +#define CV_IMAGE_ELEM( image, elemtype, row, col ) \ + (((elemtype*)((image)->imageData + (image)->widthStep*(row)))[(col)]) + +/****************************************************************************************\ +* Matrix type (CvMat) * +\****************************************************************************************/ + +#define CV_AUTO_STEP 0x7fffffff +#define CV_WHOLE_ARR cvSlice( 0, 0x3fffffff ) + +#define CV_MAGIC_MASK 0xFFFF0000 +#define CV_MAT_MAGIC_VAL 0x42420000 +#define CV_TYPE_NAME_MAT "opencv-matrix" + +#ifdef __cplusplus +typedef struct CvMat CvMat; +CV_INLINE CvMat cvMat(const cv::Mat& m); +#endif + +/** Matrix elements are stored row by row. Element (i, j) (i - 0-based row index, j - 0-based column +index) of a matrix can be retrieved or modified using CV_MAT_ELEM macro: + + uchar pixval = CV_MAT_ELEM(grayimg, uchar, i, j) + CV_MAT_ELEM(cameraMatrix, float, 0, 2) = image.width*0.5f; + +To access multiple-channel matrices, you can use +CV_MAT_ELEM(matrix, type, i, j\*nchannels + channel_idx). + +@deprecated CvMat is now obsolete; consider using Mat instead. + */ +typedef struct CvMat +{ + int type; + int step; + + /* for internal use only */ + int* refcount; + int hdr_refcount; + + union + { + uchar* ptr; + short* s; + int* i; + float* fl; + double* db; + } data; + +#ifdef __cplusplus + union + { + int rows; + int height; + }; + + union + { + int cols; + int width; + }; +#else + int rows; + int cols; +#endif + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvMat() {} + CvMat(const cv::Mat& m) { *this = cvMat(m); } +#endif +} +CvMat; + + +#define CV_IS_MAT_HDR(mat) \ + ((mat) != NULL && \ + (((const CvMat*)(mat))->type & CV_MAGIC_MASK) == CV_MAT_MAGIC_VAL && \ + ((const CvMat*)(mat))->cols > 0 && ((const CvMat*)(mat))->rows > 0) + +#define CV_IS_MAT_HDR_Z(mat) \ + ((mat) != NULL && \ + (((const CvMat*)(mat))->type & CV_MAGIC_MASK) == CV_MAT_MAGIC_VAL && \ + ((const CvMat*)(mat))->cols >= 0 && ((const CvMat*)(mat))->rows >= 0) + +#define CV_IS_MAT(mat) \ + (CV_IS_MAT_HDR(mat) && ((const CvMat*)(mat))->data.ptr != NULL) + +#define CV_IS_MASK_ARR(mat) \ + (((mat)->type & (CV_MAT_TYPE_MASK & ~CV_8SC1)) == 0) + +#define CV_ARE_TYPES_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_TYPE_MASK) == 0) + +#define CV_ARE_CNS_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_CN_MASK) == 0) + +#define CV_ARE_DEPTHS_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_DEPTH_MASK) == 0) + +#define CV_ARE_SIZES_EQ(mat1, mat2) \ + ((mat1)->rows == (mat2)->rows && (mat1)->cols == (mat2)->cols) + +#define CV_IS_MAT_CONST(mat) \ + (((mat)->rows|(mat)->cols) == 1) + +#define IPL2CV_DEPTH(depth) \ + ((((CV_8U)+(CV_16U<<4)+(CV_32F<<8)+(CV_64F<<16)+(CV_8S<<20)+ \ + (CV_16S<<24)+(CV_32S<<28)) >> ((((depth) & 0xF0) >> 2) + \ + (((depth) & IPL_DEPTH_SIGN) ? 20 : 0))) & 15) + +/** Inline constructor. No data is allocated internally!!! + * (Use together with cvCreateData, or use cvCreateMat instead to + * get a matrix with allocated data): + */ +CV_INLINE CvMat cvMat( int rows, int cols, int type, void* data CV_DEFAULT(NULL)) +{ + CvMat m; + + assert( (unsigned)CV_MAT_DEPTH(type) <= CV_64F ); + type = CV_MAT_TYPE(type); + m.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type; + m.cols = cols; + m.rows = rows; + m.step = m.cols*CV_ELEM_SIZE(type); + m.data.ptr = (uchar*)data; + m.refcount = NULL; + m.hdr_refcount = 0; + + return m; +} + +#ifdef __cplusplus + +CV_INLINE CvMat cvMat(const cv::Mat& m) +{ + CvMat self; + CV_DbgAssert(m.dims <= 2); + self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data); + self.step = (int)m.step[0]; + self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG); + return self; +} +CV_INLINE CvMat cvMat() +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMat self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMat(); +#endif +} +CV_INLINE CvMat cvMat(const CvMat& m) +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMat self = CV_STRUCT_INITIALIZER; memcpy(&self, &m, sizeof(self)); return self; +#else + return CvMat(m); +#endif +} + +#endif // __cplusplus + + +#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \ + (assert( (unsigned)(row) < (unsigned)(mat).rows && \ + (unsigned)(col) < (unsigned)(mat).cols ), \ + (mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col)) + +#define CV_MAT_ELEM_PTR( mat, row, col ) \ + CV_MAT_ELEM_PTR_FAST( mat, row, col, CV_ELEM_SIZE((mat).type) ) + +#define CV_MAT_ELEM( mat, elemtype, row, col ) \ + (*(elemtype*)CV_MAT_ELEM_PTR_FAST( mat, row, col, sizeof(elemtype))) + +/** @brief Returns the particular element of single-channel floating-point matrix. + +The function is a fast replacement for cvGetReal2D in the case of single-channel floating-point +matrices. It is faster because it is inline, it does fewer checks for array type and array element +type, and it checks for the row and column ranges only in debug mode. +@param mat Input matrix +@param row The zero-based index of row +@param col The zero-based index of column + */ +CV_INLINE double cvmGet( const CvMat* mat, int row, int col ) +{ + int type; + + type = CV_MAT_TYPE(mat->type); + assert( (unsigned)row < (unsigned)mat->rows && + (unsigned)col < (unsigned)mat->cols ); + + if( type == CV_32FC1 ) + return ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; + else + { + assert( type == CV_64FC1 ); + return ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; + } +} + +/** @brief Sets a specific element of a single-channel floating-point matrix. + +The function is a fast replacement for cvSetReal2D in the case of single-channel floating-point +matrices. It is faster because it is inline, it does fewer checks for array type and array element +type, and it checks for the row and column ranges only in debug mode. +@param mat The matrix +@param row The zero-based index of row +@param col The zero-based index of column +@param value The new value of the matrix element + */ +CV_INLINE void cvmSet( CvMat* mat, int row, int col, double value ) +{ + int type; + type = CV_MAT_TYPE(mat->type); + assert( (unsigned)row < (unsigned)mat->rows && + (unsigned)col < (unsigned)mat->cols ); + + if( type == CV_32FC1 ) + ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = (float)value; + else + { + assert( type == CV_64FC1 ); + ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = value; + } +} + + +CV_INLINE int cvIplDepth( int type ) +{ + int depth = CV_MAT_DEPTH(type); + return CV_ELEM_SIZE1(depth)*8 | (depth == CV_8S || depth == CV_16S || + depth == CV_32S ? IPL_DEPTH_SIGN : 0); +} + + +/****************************************************************************************\ +* Multi-dimensional dense array (CvMatND) * +\****************************************************************************************/ + +#define CV_MATND_MAGIC_VAL 0x42430000 +#define CV_TYPE_NAME_MATND "opencv-nd-matrix" + +#define CV_MAX_DIM 32 + +#ifdef __cplusplus +typedef struct CvMatND CvMatND; +CV_EXPORTS CvMatND cvMatND(const cv::Mat& m); +#endif + +/** + @deprecated consider using cv::Mat instead + */ +typedef struct +CvMatND +{ + int type; + int dims; + + int* refcount; + int hdr_refcount; + + union + { + uchar* ptr; + float* fl; + double* db; + int* i; + short* s; + } data; + + struct + { + int size; + int step; + } + dim[CV_MAX_DIM]; + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvMatND() {} + CvMatND(const cv::Mat& m) { *this = cvMatND(m); } +#endif +} +CvMatND; + + +CV_INLINE CvMatND cvMatND() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvMatND self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMatND(); +#endif +} + +#define CV_IS_MATND_HDR(mat) \ + ((mat) != NULL && (((const CvMatND*)(mat))->type & CV_MAGIC_MASK) == CV_MATND_MAGIC_VAL) + +#define CV_IS_MATND(mat) \ + (CV_IS_MATND_HDR(mat) && ((const CvMatND*)(mat))->data.ptr != NULL) + + +/****************************************************************************************\ +* Multi-dimensional sparse array (CvSparseMat) * +\****************************************************************************************/ + +#define CV_SPARSE_MAT_MAGIC_VAL 0x42440000 +#define CV_TYPE_NAME_SPARSE_MAT "opencv-sparse-matrix" + +struct CvSet; + +typedef struct CvSparseMat +{ + int type; + int dims; + int* refcount; + int hdr_refcount; + + struct CvSet* heap; + void** hashtable; + int hashsize; + int valoffset; + int idxoffset; + int size[CV_MAX_DIM]; + +#ifdef __cplusplus + CV_EXPORTS void copyToSparseMat(cv::SparseMat& m) const; +#endif +} +CvSparseMat; + +#ifdef __cplusplus +CV_EXPORTS CvSparseMat* cvCreateSparseMat(const cv::SparseMat& m); +#endif + +#define CV_IS_SPARSE_MAT_HDR(mat) \ + ((mat) != NULL && \ + (((const CvSparseMat*)(mat))->type & CV_MAGIC_MASK) == CV_SPARSE_MAT_MAGIC_VAL) + +#define CV_IS_SPARSE_MAT(mat) \ + CV_IS_SPARSE_MAT_HDR(mat) + +/**************** iteration through a sparse array *****************/ + +typedef struct CvSparseNode +{ + unsigned hashval; + struct CvSparseNode* next; +} +CvSparseNode; + +typedef struct CvSparseMatIterator +{ + CvSparseMat* mat; + CvSparseNode* node; + int curidx; +} +CvSparseMatIterator; + +#define CV_NODE_VAL(mat,node) ((void*)((uchar*)(node) + (mat)->valoffset)) +#define CV_NODE_IDX(mat,node) ((int*)((uchar*)(node) + (mat)->idxoffset)) + +/****************************************************************************************\ +* Histogram * +\****************************************************************************************/ + +typedef int CvHistType; + +#define CV_HIST_MAGIC_VAL 0x42450000 +#define CV_HIST_UNIFORM_FLAG (1 << 10) + +/** indicates whether bin ranges are set already or not */ +#define CV_HIST_RANGES_FLAG (1 << 11) + +#define CV_HIST_ARRAY 0 +#define CV_HIST_SPARSE 1 +#define CV_HIST_TREE CV_HIST_SPARSE + +/** should be used as a parameter only, + it turns to CV_HIST_UNIFORM_FLAG of hist->type */ +#define CV_HIST_UNIFORM 1 + +typedef struct CvHistogram +{ + int type; + CvArr* bins; + float thresh[CV_MAX_DIM][2]; /**< For uniform histograms. */ + float** thresh2; /**< For non-uniform histograms. */ + CvMatND mat; /**< Embedded matrix header for array histograms. */ +} +CvHistogram; + +#define CV_IS_HIST( hist ) \ + ((hist) != NULL && \ + (((CvHistogram*)(hist))->type & CV_MAGIC_MASK) == CV_HIST_MAGIC_VAL && \ + (hist)->bins != NULL) + +#define CV_IS_UNIFORM_HIST( hist ) \ + (((hist)->type & CV_HIST_UNIFORM_FLAG) != 0) + +#define CV_IS_SPARSE_HIST( hist ) \ + CV_IS_SPARSE_MAT((hist)->bins) + +#define CV_HIST_HAS_RANGES( hist ) \ + (((hist)->type & CV_HIST_RANGES_FLAG) != 0) + +/****************************************************************************************\ +* Other supplementary data type definitions * +\****************************************************************************************/ + +/*************************************** CvRect *****************************************/ +/** @sa Rect_ */ +typedef struct CvRect +{ + int x; + int y; + int width; + int height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvRect() __attribute__(( warning("Non-initialized variable") )) {}; + template CvRect(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 4); + x = y = width = height = 0; + if (list.size() == 4) + { + x = list.begin()[0]; y = list.begin()[1]; width = list.begin()[2]; height = list.begin()[3]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvRect(int _x = 0, int _y = 0, int w = 0, int h = 0): x(_x), y(_y), width(w), height(h) {} + template + CvRect(const cv::Rect_<_Tp>& r): x(cv::saturate_cast(r.x)), y(cv::saturate_cast(r.y)), width(cv::saturate_cast(r.width)), height(cv::saturate_cast(r.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Rect_<_Tp>() const { return cv::Rect_<_Tp>((_Tp)x, (_Tp)y, (_Tp)width, (_Tp)height); } +#endif +} +CvRect; + +/** constructs CvRect structure. */ +CV_INLINE CvRect cvRect( int x, int y, int width, int height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvRect r = {x, y, width, height}; +#else + CvRect r(x, y , width, height); +#endif + return r; +} +#ifdef __cplusplus +CV_INLINE CvRect cvRect(const cv::Rect& rc) { return cvRect(rc.x, rc.y, rc.width, rc.height); } +#endif + +CV_INLINE IplROI cvRectToROI( CvRect rect, int coi ) +{ + IplROI roi; + roi.xOffset = rect.x; + roi.yOffset = rect.y; + roi.width = rect.width; + roi.height = rect.height; + roi.coi = coi; + + return roi; +} + + +CV_INLINE CvRect cvROIToRect( IplROI roi ) +{ + return cvRect( roi.xOffset, roi.yOffset, roi.width, roi.height ); +} + +/*********************************** CvTermCriteria *************************************/ + +#define CV_TERMCRIT_ITER 1 +#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER +#define CV_TERMCRIT_EPS 2 + +/** @sa TermCriteria + */ +typedef struct CvTermCriteria +{ + int type; /**< may be combination of + CV_TERMCRIT_ITER + CV_TERMCRIT_EPS */ + int max_iter; + double epsilon; +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvTermCriteria(int _type = 0, int _iter = 0, double _eps = 0) : type(_type), max_iter(_iter), epsilon(_eps) {} + CvTermCriteria(const cv::TermCriteria& t) : type(t.type), max_iter(t.maxCount), epsilon(t.epsilon) {} +#endif +#ifdef __cplusplus + operator cv::TermCriteria() const { return cv::TermCriteria(type, max_iter, epsilon); } +#endif +} +CvTermCriteria; + +CV_INLINE CvTermCriteria cvTermCriteria( int type, int max_iter, double epsilon ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvTermCriteria t = { type, max_iter, (float)epsilon}; +#else + CvTermCriteria t(type, max_iter, epsilon); +#endif + return t; +} +#ifdef __cplusplus +CV_INLINE CvTermCriteria cvTermCriteria(const cv::TermCriteria& t) { return cvTermCriteria(t.type, t.maxCount, t.epsilon); } +#endif + + +/******************************* CvPoint and variants ***********************************/ + +typedef struct CvPoint +{ + int x; + int y; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint(int _x = 0, int _y = 0): x(_x), y(_y) {} + template + CvPoint(const cv::Point_<_Tp>& pt): x((int)pt.x), y((int)pt.y) {} +#endif +#ifdef __cplusplus + template + operator cv::Point_<_Tp>() const { return cv::Point_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y)); } +#endif +} +CvPoint; + +/** constructs CvPoint structure. */ +CV_INLINE CvPoint cvPoint( int x, int y ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint p = {x, y}; +#else + CvPoint p(x, y); +#endif + return p; +} +#ifdef __cplusplus +CV_INLINE CvPoint cvPoint(const cv::Point& pt) { return cvPoint(pt.x, pt.y); } +#endif + +typedef struct CvPoint2D32f +{ + float x; + float y; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint2D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint2D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint2D32f(float _x = 0, float _y = 0): x(_x), y(_y) {} + template + CvPoint2D32f(const cv::Point_<_Tp>& pt): x((float)pt.x), y((float)pt.y) {} +#endif +#ifdef __cplusplus + template + operator cv::Point_<_Tp>() const { return cv::Point_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y)); } +#endif +} +CvPoint2D32f; + +/** constructs CvPoint2D32f structure. */ +CV_INLINE CvPoint2D32f cvPoint2D32f( double x, double y ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint2D32f p = { (float)x, (float)y }; +#else + CvPoint2D32f p((float)x, (float)y); +#endif + return p; +} + +#ifdef __cplusplus +template +CvPoint2D32f cvPoint2D32f(const cv::Point_<_Tp>& pt) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint2D32f p = { (float)pt.x, (float)pt.y }; +#else + CvPoint2D32f p((float)pt.x, (float)pt.y); +#endif + return p; +} +#endif + +/** converts CvPoint to CvPoint2D32f. */ +CV_INLINE CvPoint2D32f cvPointTo32f( CvPoint point ) +{ + return cvPoint2D32f( (float)point.x, (float)point.y ); +} + +/** converts CvPoint2D32f to CvPoint. */ +CV_INLINE CvPoint cvPointFrom32f( CvPoint2D32f point ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint ipt = { cvRound(point.x), cvRound(point.y) }; +#else + CvPoint ipt(cvRound(point.x), cvRound(point.y)); +#endif + return ipt; +} + + +typedef struct CvPoint3D32f +{ + float x; + float y; + float z; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint3D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint3D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 3); + x = y = z = 0; + if (list.size() == 3) + { + x = list.begin()[0]; y = list.begin()[1]; z = list.begin()[2]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint3D32f(float _x = 0, float _y = 0, float _z = 0): x(_x), y(_y), z(_z) {} + template + CvPoint3D32f(const cv::Point3_<_Tp>& pt): x((float)pt.x), y((float)pt.y), z((float)pt.z) {} +#endif +#ifdef __cplusplus + template + operator cv::Point3_<_Tp>() const { return cv::Point3_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y), cv::saturate_cast<_Tp>(z)); } +#endif +} +CvPoint3D32f; + +/** constructs CvPoint3D32f structure. */ +CV_INLINE CvPoint3D32f cvPoint3D32f( double x, double y, double z ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint3D32f p = { (float)x, (float)y, (float)z }; +#else + CvPoint3D32f p((float)x, (float)y, (float)z); +#endif + return p; +} + +#ifdef __cplusplus +template +CvPoint3D32f cvPoint3D32f(const cv::Point3_<_Tp>& pt) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint3D32f p = { (float)pt.x, (float)pt.y, (float)pt.z }; +#else + CvPoint3D32f p((float)pt.x, (float)pt.y, (float)pt.z); +#endif + return p; +} +#endif + + +typedef struct CvPoint2D64f +{ + double x; + double y; +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint2D64f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint2D64f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#endif +} +CvPoint2D64f; + +/** constructs CvPoint2D64f structure.*/ +CV_INLINE CvPoint2D64f cvPoint2D64f( double x, double y ) +{ + CvPoint2D64f p = { x, y }; + return p; +} + + +typedef struct CvPoint3D64f +{ + double x; + double y; + double z; +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint3D64f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint3D64f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 3); + x = y = z = 0; + if (list.size() == 3) + { + x = list.begin()[0]; y = list.begin()[1]; z = list.begin()[2]; + } + }; +#endif +} +CvPoint3D64f; + +/** constructs CvPoint3D64f structure. */ +CV_INLINE CvPoint3D64f cvPoint3D64f( double x, double y, double z ) +{ + CvPoint3D64f p = { x, y, z }; + return p; +} + + +/******************************** CvSize's & CvBox **************************************/ + +typedef struct CvSize +{ + int width; + int height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSize() __attribute__(( warning("Non-initialized variable") )) {} + template CvSize(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + width = 0; height = 0; + if (list.size() == 2) + { + width = list.begin()[0]; height = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvSize(int w = 0, int h = 0): width(w), height(h) {} + template + CvSize(const cv::Size_<_Tp>& sz): width(cv::saturate_cast(sz.width)), height(cv::saturate_cast(sz.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Size_<_Tp>() const { return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(width), cv::saturate_cast<_Tp>(height)); } +#endif +} +CvSize; + +/** constructs CvSize structure. */ +CV_INLINE CvSize cvSize( int width, int height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize s = { width, height }; +#else + CvSize s(width, height); +#endif + return s; +} + +#ifdef __cplusplus +CV_INLINE CvSize cvSize(const cv::Size& sz) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize s = { sz.width, sz.height }; +#else + CvSize s(sz.width, sz.height); +#endif + return s; +} +#endif + +typedef struct CvSize2D32f +{ + float width; + float height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSize2D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvSize2D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + width = 0; height = 0; + if (list.size() == 2) + { + width = list.begin()[0]; height = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvSize2D32f(float w = 0, float h = 0): width(w), height(h) {} + template + CvSize2D32f(const cv::Size_<_Tp>& sz): width(cv::saturate_cast(sz.width)), height(cv::saturate_cast(sz.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Size_<_Tp>() const { return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(width), cv::saturate_cast<_Tp>(height)); } +#endif +} +CvSize2D32f; + +/** constructs CvSize2D32f structure. */ +CV_INLINE CvSize2D32f cvSize2D32f( double width, double height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize2D32f s = { (float)width, (float)height }; +#else + CvSize2D32f s((float)width, (float)height); +#endif + return s; +} +#ifdef __cplusplus +template +CvSize2D32f cvSize2D32f(const cv::Size_<_Tp>& sz) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize2D32f s = { (float)sz.width, (float)sz.height }; +#else + CvSize2D32f s((float)sz.width, (float)sz.height); +#endif + return s; +} +#endif + +/** @sa RotatedRect + */ +typedef struct CvBox2D +{ + CvPoint2D32f center; /**< Center of the box. */ + CvSize2D32f size; /**< Box width and length. */ + float angle; /**< Angle between the horizontal axis */ + /**< and the first side (i.e. length) in degrees */ + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvBox2D(CvPoint2D32f c = CvPoint2D32f(), CvSize2D32f s = CvSize2D32f(), float a = 0) : center(c), size(s), angle(a) {} + CvBox2D(const cv::RotatedRect& rr) : center(rr.center), size(rr.size), angle(rr.angle) {} +#endif +#ifdef __cplusplus + operator cv::RotatedRect() const { return cv::RotatedRect(center, size, angle); } +#endif +} +CvBox2D; + + +#ifdef __cplusplus +CV_INLINE CvBox2D cvBox2D(CvPoint2D32f c = CvPoint2D32f(), CvSize2D32f s = CvSize2D32f(), float a = 0) +{ + CvBox2D self; + self.center = c; + self.size = s; + self.angle = a; + return self; +} +CV_INLINE CvBox2D cvBox2D(const cv::RotatedRect& rr) +{ + CvBox2D self; + self.center = cvPoint2D32f(rr.center); + self.size = cvSize2D32f(rr.size); + self.angle = rr.angle; + return self; +} +#endif + + +/** Line iterator state: */ +typedef struct CvLineIterator +{ + /** Pointer to the current point: */ + uchar* ptr; + + /* Bresenham algorithm state: */ + int err; + int plus_delta; + int minus_delta; + int plus_step; + int minus_step; +} +CvLineIterator; + + + +/************************************* CvSlice ******************************************/ +#define CV_WHOLE_SEQ_END_INDEX 0x3fffffff +#define CV_WHOLE_SEQ cvSlice(0, CV_WHOLE_SEQ_END_INDEX) + +typedef struct CvSlice +{ + int start_index, end_index; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSlice() __attribute__(( warning("Non-initialized variable") )) {} + template CvSlice(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + start_index = end_index = 0; + if (list.size() == 2) + { + start_index = list.begin()[0]; end_index = list.begin()[1]; + } + }; +#endif +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) && !defined(__CUDACC__) + CvSlice(int start = 0, int end = 0) : start_index(start), end_index(end) {} + CvSlice(const cv::Range& r) { *this = (r.start != INT_MIN && r.end != INT_MAX) ? CvSlice(r.start, r.end) : CvSlice(0, CV_WHOLE_SEQ_END_INDEX); } + operator cv::Range() const { return (start_index == 0 && end_index == CV_WHOLE_SEQ_END_INDEX ) ? cv::Range::all() : cv::Range(start_index, end_index); } +#endif +} +CvSlice; + +CV_INLINE CvSlice cvSlice( int start, int end ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) && !defined(__CUDACC__)) + CvSlice slice = { start, end }; +#else + CvSlice slice(start, end); +#endif + return slice; +} + +#if defined(__cplusplus) +CV_INLINE CvSlice cvSlice(const cv::Range& r) +{ + CvSlice slice = (r.start != INT_MIN && r.end != INT_MAX) ? cvSlice(r.start, r.end) : cvSlice(0, CV_WHOLE_SEQ_END_INDEX); + return slice; +} +#endif + + +/************************************* CvScalar *****************************************/ +/** @sa Scalar_ + */ +typedef struct CvScalar +{ + double val[4]; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvScalar() __attribute__(( warning("Non-initialized variable") )) {} + CvScalar(const std::initializer_list list) + { + CV_Assert(list.size() == 0 || list.size() == 4); + val[0] = val[1] = val[2] = val[3] = 0; + if (list.size() == 4) + { + val[0] = list.begin()[0]; val[1] = list.begin()[1]; val[2] = list.begin()[2]; val[3] = list.begin()[3]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvScalar() {} + CvScalar(double d0, double d1 = 0, double d2 = 0, double d3 = 0) { val[0] = d0; val[1] = d1; val[2] = d2; val[3] = d3; } + template + CvScalar(const cv::Scalar_<_Tp>& s) { val[0] = s.val[0]; val[1] = s.val[1]; val[2] = s.val[2]; val[3] = s.val[3]; } + template + CvScalar(const cv::Vec<_Tp, cn>& v) + { + int i; + for( i = 0; i < (cn < 4 ? cn : 4); i++ ) val[i] = v.val[i]; + for( ; i < 4; i++ ) val[i] = 0; + } +#endif +#ifdef __cplusplus + template + operator cv::Scalar_<_Tp>() const { return cv::Scalar_<_Tp>(cv::saturate_cast<_Tp>(val[0]), cv::saturate_cast<_Tp>(val[1]), cv::saturate_cast<_Tp>(val[2]), cv::saturate_cast<_Tp>(val[3])); } +#endif +} +CvScalar; + +CV_INLINE CvScalar cvScalar( double val0, double val1 CV_DEFAULT(0), + double val2 CV_DEFAULT(0), double val3 CV_DEFAULT(0)) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0; scalar.val[1] = val1; + scalar.val[2] = val2; scalar.val[3] = val3; + return scalar; +} + +#ifdef __cplusplus +CV_INLINE CvScalar cvScalar() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = scalar.val[1] = scalar.val[2] = scalar.val[3] = 0; + return scalar; +} +CV_INLINE CvScalar cvScalar(const cv::Scalar& s) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = s.val[0]; + scalar.val[1] = s.val[1]; + scalar.val[2] = s.val[2]; + scalar.val[3] = s.val[3]; + return scalar; +} +#endif + +CV_INLINE CvScalar cvRealScalar( double val0 ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0; + scalar.val[1] = scalar.val[2] = scalar.val[3] = 0; + return scalar; +} + +CV_INLINE CvScalar cvScalarAll( double val0123 ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0123; + scalar.val[1] = val0123; + scalar.val[2] = val0123; + scalar.val[3] = val0123; + return scalar; +} + +/****************************************************************************************\ +* Dynamic Data structures * +\****************************************************************************************/ + +/******************************** Memory storage ****************************************/ + +typedef struct CvMemBlock +{ + struct CvMemBlock* prev; + struct CvMemBlock* next; +} +CvMemBlock; + +#define CV_STORAGE_MAGIC_VAL 0x42890000 + +typedef struct CvMemStorage +{ + int signature; + CvMemBlock* bottom; /**< First allocated block. */ + CvMemBlock* top; /**< Current memory block - top of the stack. */ + struct CvMemStorage* parent; /**< We get new blocks from parent as needed. */ + int block_size; /**< Block size. */ + int free_space; /**< Remaining free space in current block. */ +} +CvMemStorage; + +#define CV_IS_STORAGE(storage) \ + ((storage) != NULL && \ + (((CvMemStorage*)(storage))->signature & CV_MAGIC_MASK) == CV_STORAGE_MAGIC_VAL) + + +typedef struct CvMemStoragePos +{ + CvMemBlock* top; + int free_space; +} +CvMemStoragePos; + + +/*********************************** Sequence *******************************************/ + +typedef struct CvSeqBlock +{ + struct CvSeqBlock* prev; /**< Previous sequence block. */ + struct CvSeqBlock* next; /**< Next sequence block. */ + int start_index; /**< Index of the first element in the block + */ + /**< sequence->first->start_index. */ + int count; /**< Number of elements in the block. */ + schar* data; /**< Pointer to the first element of the block. */ +} +CvSeqBlock; + + +#define CV_TREE_NODE_FIELDS(node_type) \ + int flags; /**< Miscellaneous flags. */ \ + int header_size; /**< Size of sequence header. */ \ + struct node_type* h_prev; /**< Previous sequence. */ \ + struct node_type* h_next; /**< Next sequence. */ \ + struct node_type* v_prev; /**< 2nd previous sequence. */ \ + struct node_type* v_next /**< 2nd next sequence. */ + +/** + Read/Write sequence. + Elements can be dynamically inserted to or deleted from the sequence. +*/ +#define CV_SEQUENCE_FIELDS() \ + CV_TREE_NODE_FIELDS(CvSeq); \ + int total; /**< Total number of elements. */ \ + int elem_size; /**< Size of sequence element in bytes. */ \ + schar* block_max; /**< Maximal bound of the last block. */ \ + schar* ptr; /**< Current write pointer. */ \ + int delta_elems; /**< Grow seq this many at a time. */ \ + CvMemStorage* storage; /**< Where the seq is stored. */ \ + CvSeqBlock* free_blocks; /**< Free blocks list. */ \ + CvSeqBlock* first; /**< Pointer to the first sequence block. */ + +typedef struct CvSeq +{ + CV_SEQUENCE_FIELDS() +} +CvSeq; + +#define CV_TYPE_NAME_SEQ "opencv-sequence" +#define CV_TYPE_NAME_SEQ_TREE "opencv-sequence-tree" + +/*************************************** Set ********************************************/ +/** @brief Set + Order is not preserved. There can be gaps between sequence elements. + After the element has been inserted it stays in the same place all the time. + The MSB(most-significant or sign bit) of the first field (flags) is 0 iff the element exists. +*/ +#define CV_SET_ELEM_FIELDS(elem_type) \ + int flags; \ + struct elem_type* next_free; + +typedef struct CvSetElem +{ + CV_SET_ELEM_FIELDS(CvSetElem) +} +CvSetElem; + +#define CV_SET_FIELDS() \ + CV_SEQUENCE_FIELDS() \ + CvSetElem* free_elems; \ + int active_count; + +typedef struct CvSet +{ + CV_SET_FIELDS() +} +CvSet; + + +#define CV_SET_ELEM_IDX_MASK ((1 << 26) - 1) +#define CV_SET_ELEM_FREE_FLAG (1 << (sizeof(int)*8-1)) + +/** Checks whether the element pointed by ptr belongs to a set or not */ +#define CV_IS_SET_ELEM( ptr ) (((CvSetElem*)(ptr))->flags >= 0) + +/************************************* Graph ********************************************/ + +/** @name Graph + +We represent a graph as a set of vertices. Vertices contain their adjacency lists (more exactly, +pointers to first incoming or outcoming edge (or 0 if isolated vertex)). Edges are stored in +another set. There is a singly-linked list of incoming/outcoming edges for each vertex. + +Each edge consists of: + +- Two pointers to the starting and ending vertices (vtx[0] and vtx[1] respectively). + + A graph may be oriented or not. In the latter case, edges between vertex i to vertex j are not +distinguished during search operations. + +- Two pointers to next edges for the starting and ending vertices, where next[0] points to the +next edge in the vtx[0] adjacency list and next[1] points to the next edge in the vtx[1] +adjacency list. + +@see CvGraphEdge, CvGraphVtx, CvGraphVtx2D, CvGraph +@{ +*/ +#define CV_GRAPH_EDGE_FIELDS() \ + int flags; \ + float weight; \ + struct CvGraphEdge* next[2]; \ + struct CvGraphVtx* vtx[2]; + + +#define CV_GRAPH_VERTEX_FIELDS() \ + int flags; \ + struct CvGraphEdge* first; + + +typedef struct CvGraphEdge +{ + CV_GRAPH_EDGE_FIELDS() +} +CvGraphEdge; + +typedef struct CvGraphVtx +{ + CV_GRAPH_VERTEX_FIELDS() +} +CvGraphVtx; + +typedef struct CvGraphVtx2D +{ + CV_GRAPH_VERTEX_FIELDS() + CvPoint2D32f* ptr; +} +CvGraphVtx2D; + +/** + Graph is "derived" from the set (this is set a of vertices) + and includes another set (edges) +*/ +#define CV_GRAPH_FIELDS() \ + CV_SET_FIELDS() \ + CvSet* edges; + +typedef struct CvGraph +{ + CV_GRAPH_FIELDS() +} +CvGraph; + +#define CV_TYPE_NAME_GRAPH "opencv-graph" + +/** @} */ + +/*********************************** Chain/Contour *************************************/ + +typedef struct CvChain +{ + CV_SEQUENCE_FIELDS() + CvPoint origin; +} +CvChain; + +#define CV_CONTOUR_FIELDS() \ + CV_SEQUENCE_FIELDS() \ + CvRect rect; \ + int color; \ + int reserved[3]; + +typedef struct CvContour +{ + CV_CONTOUR_FIELDS() +} +CvContour; + +typedef CvContour CvPoint2DSeq; + +/****************************************************************************************\ +* Sequence types * +\****************************************************************************************/ + +#define CV_SEQ_MAGIC_VAL 0x42990000 + +#define CV_IS_SEQ(seq) \ + ((seq) != NULL && (((CvSeq*)(seq))->flags & CV_MAGIC_MASK) == CV_SEQ_MAGIC_VAL) + +#define CV_SET_MAGIC_VAL 0x42980000 +#define CV_IS_SET(set) \ + ((set) != NULL && (((CvSeq*)(set))->flags & CV_MAGIC_MASK) == CV_SET_MAGIC_VAL) + +#define CV_SEQ_ELTYPE_BITS 12 +#define CV_SEQ_ELTYPE_MASK ((1 << CV_SEQ_ELTYPE_BITS) - 1) + +#define CV_SEQ_ELTYPE_POINT CV_32SC2 /**< (x,y) */ +#define CV_SEQ_ELTYPE_CODE CV_8UC1 /**< freeman code: 0..7 */ +#define CV_SEQ_ELTYPE_GENERIC 0 +#define CV_SEQ_ELTYPE_PTR CV_USRTYPE1 +#define CV_SEQ_ELTYPE_PPOINT CV_SEQ_ELTYPE_PTR /**< &(x,y) */ +#define CV_SEQ_ELTYPE_INDEX CV_32SC1 /**< #(x,y) */ +#define CV_SEQ_ELTYPE_GRAPH_EDGE 0 /**< &next_o, &next_d, &vtx_o, &vtx_d */ +#define CV_SEQ_ELTYPE_GRAPH_VERTEX 0 /**< first_edge, &(x,y) */ +#define CV_SEQ_ELTYPE_TRIAN_ATR 0 /**< vertex of the binary tree */ +#define CV_SEQ_ELTYPE_CONNECTED_COMP 0 /**< connected component */ +#define CV_SEQ_ELTYPE_POINT3D CV_32FC3 /**< (x,y,z) */ + +#define CV_SEQ_KIND_BITS 2 +#define CV_SEQ_KIND_MASK (((1 << CV_SEQ_KIND_BITS) - 1)<flags & CV_SEQ_ELTYPE_MASK) +#define CV_SEQ_KIND( seq ) ((seq)->flags & CV_SEQ_KIND_MASK ) + +/** flag checking */ +#define CV_IS_SEQ_INDEX( seq ) ((CV_SEQ_ELTYPE(seq) == CV_SEQ_ELTYPE_INDEX) && \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_GENERIC)) + +#define CV_IS_SEQ_CURVE( seq ) (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE) +#define CV_IS_SEQ_CLOSED( seq ) (((seq)->flags & CV_SEQ_FLAG_CLOSED) != 0) +#define CV_IS_SEQ_CONVEX( seq ) 0 +#define CV_IS_SEQ_HOLE( seq ) (((seq)->flags & CV_SEQ_FLAG_HOLE) != 0) +#define CV_IS_SEQ_SIMPLE( seq ) 1 + +/** type checking macros */ +#define CV_IS_SEQ_POINT_SET( seq ) \ + ((CV_SEQ_ELTYPE(seq) == CV_32SC2 || CV_SEQ_ELTYPE(seq) == CV_32FC2)) + +#define CV_IS_SEQ_POINT_SUBSET( seq ) \ + (CV_IS_SEQ_INDEX( seq ) || CV_SEQ_ELTYPE(seq) == CV_SEQ_ELTYPE_PPOINT) + +#define CV_IS_SEQ_POLYLINE( seq ) \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE && CV_IS_SEQ_POINT_SET(seq)) + +#define CV_IS_SEQ_POLYGON( seq ) \ + (CV_IS_SEQ_POLYLINE(seq) && CV_IS_SEQ_CLOSED(seq)) + +#define CV_IS_SEQ_CHAIN( seq ) \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE && (seq)->elem_size == 1) + +#define CV_IS_SEQ_CONTOUR( seq ) \ + (CV_IS_SEQ_CLOSED(seq) && (CV_IS_SEQ_POLYLINE(seq) || CV_IS_SEQ_CHAIN(seq))) + +#define CV_IS_SEQ_CHAIN_CONTOUR( seq ) \ + (CV_IS_SEQ_CHAIN( seq ) && CV_IS_SEQ_CLOSED( seq )) + +#define CV_IS_SEQ_POLYGON_TREE( seq ) \ + (CV_SEQ_ELTYPE (seq) == CV_SEQ_ELTYPE_TRIAN_ATR && \ + CV_SEQ_KIND( seq ) == CV_SEQ_KIND_BIN_TREE ) + +#define CV_IS_GRAPH( seq ) \ + (CV_IS_SET(seq) && CV_SEQ_KIND((CvSet*)(seq)) == CV_SEQ_KIND_GRAPH) + +#define CV_IS_GRAPH_ORIENTED( seq ) \ + (((seq)->flags & CV_GRAPH_FLAG_ORIENTED) != 0) + +#define CV_IS_SUBDIV2D( seq ) \ + (CV_IS_SET(seq) && CV_SEQ_KIND((CvSet*)(seq)) == CV_SEQ_KIND_SUBDIV2D) + +/****************************************************************************************/ +/* Sequence writer & reader */ +/****************************************************************************************/ + +#define CV_SEQ_WRITER_FIELDS() \ + int header_size; \ + CvSeq* seq; /**< the sequence written */ \ + CvSeqBlock* block; /**< current block */ \ + schar* ptr; /**< pointer to free space */ \ + schar* block_min; /**< pointer to the beginning of block*/\ + schar* block_max; /**< pointer to the end of block */ + +typedef struct CvSeqWriter +{ + CV_SEQ_WRITER_FIELDS() +} +CvSeqWriter; + + +#define CV_SEQ_READER_FIELDS() \ + int header_size; \ + CvSeq* seq; /**< sequence, beign read */ \ + CvSeqBlock* block; /**< current block */ \ + schar* ptr; /**< pointer to element be read next */ \ + schar* block_min; /**< pointer to the beginning of block */\ + schar* block_max; /**< pointer to the end of block */ \ + int delta_index;/**< = seq->first->start_index */ \ + schar* prev_elem; /**< pointer to previous element */ + +typedef struct CvSeqReader +{ + CV_SEQ_READER_FIELDS() +} +CvSeqReader; + +/****************************************************************************************/ +/* Operations on sequences */ +/****************************************************************************************/ + +#define CV_SEQ_ELEM( seq, elem_type, index ) \ +/** assert gives some guarantee that parameter is valid */ \ +( assert(sizeof((seq)->first[0]) == sizeof(CvSeqBlock) && \ + (seq)->elem_size == sizeof(elem_type)), \ + (elem_type*)((seq)->first && (unsigned)index < \ + (unsigned)((seq)->first->count) ? \ + (seq)->first->data + (index) * sizeof(elem_type) : \ + cvGetSeqElem( (CvSeq*)(seq), (index) ))) +#define CV_GET_SEQ_ELEM( elem_type, seq, index ) CV_SEQ_ELEM( (seq), elem_type, (index) ) + +/** Add element to sequence: */ +#define CV_WRITE_SEQ_ELEM_VAR( elem_ptr, writer ) \ +{ \ + if( (writer).ptr >= (writer).block_max ) \ + { \ + cvCreateSeqBlock( &writer); \ + } \ + memcpy((writer).ptr, elem_ptr, (writer).seq->elem_size);\ + (writer).ptr += (writer).seq->elem_size; \ +} + +#define CV_WRITE_SEQ_ELEM( elem, writer ) \ +{ \ + assert( (writer).seq->elem_size == sizeof(elem)); \ + if( (writer).ptr >= (writer).block_max ) \ + { \ + cvCreateSeqBlock( &writer); \ + } \ + assert( (writer).ptr <= (writer).block_max - sizeof(elem));\ + memcpy((writer).ptr, &(elem), sizeof(elem)); \ + (writer).ptr += sizeof(elem); \ +} + + +/** Move reader position forward: */ +#define CV_NEXT_SEQ_ELEM( elem_size, reader ) \ +{ \ + if( ((reader).ptr += (elem_size)) >= (reader).block_max ) \ + { \ + cvChangeSeqBlock( &(reader), 1 ); \ + } \ +} + + +/** Move reader position backward: */ +#define CV_PREV_SEQ_ELEM( elem_size, reader ) \ +{ \ + if( ((reader).ptr -= (elem_size)) < (reader).block_min ) \ + { \ + cvChangeSeqBlock( &(reader), -1 ); \ + } \ +} + +/** Read element and move read position forward: */ +#define CV_READ_SEQ_ELEM( elem, reader ) \ +{ \ + assert( (reader).seq->elem_size == sizeof(elem)); \ + memcpy( &(elem), (reader).ptr, sizeof((elem))); \ + CV_NEXT_SEQ_ELEM( sizeof(elem), reader ) \ +} + +/** Read element and move read position backward: */ +#define CV_REV_READ_SEQ_ELEM( elem, reader ) \ +{ \ + assert( (reader).seq->elem_size == sizeof(elem)); \ + memcpy(&(elem), (reader).ptr, sizeof((elem))); \ + CV_PREV_SEQ_ELEM( sizeof(elem), reader ) \ +} + + +#define CV_READ_CHAIN_POINT( _pt, reader ) \ +{ \ + (_pt) = (reader).pt; \ + if( (reader).ptr ) \ + { \ + CV_READ_SEQ_ELEM( (reader).code, (reader)); \ + assert( ((reader).code & ~7) == 0 ); \ + (reader).pt.x += (reader).deltas[(int)(reader).code][0]; \ + (reader).pt.y += (reader).deltas[(int)(reader).code][1]; \ + } \ +} + +#define CV_CURRENT_POINT( reader ) (*((CvPoint*)((reader).ptr))) +#define CV_PREV_POINT( reader ) (*((CvPoint*)((reader).prev_elem))) + +#define CV_READ_EDGE( pt1, pt2, reader ) \ +{ \ + assert( sizeof(pt1) == sizeof(CvPoint) && \ + sizeof(pt2) == sizeof(CvPoint) && \ + reader.seq->elem_size == sizeof(CvPoint)); \ + (pt1) = CV_PREV_POINT( reader ); \ + (pt2) = CV_CURRENT_POINT( reader ); \ + (reader).prev_elem = (reader).ptr; \ + CV_NEXT_SEQ_ELEM( sizeof(CvPoint), (reader)); \ +} + +/************ Graph macros ************/ + +/** Return next graph edge for given vertex: */ +#define CV_NEXT_GRAPH_EDGE( edge, vertex ) \ + (assert((edge)->vtx[0] == (vertex) || (edge)->vtx[1] == (vertex)), \ + (edge)->next[(edge)->vtx[1] == (vertex)]) + + + +/****************************************************************************************\ +* Data structures for persistence (a.k.a serialization) functionality * +\****************************************************************************************/ + +/** "black box" file storage */ +typedef struct CvFileStorage CvFileStorage; + +/** Storage flags: */ +#define CV_STORAGE_READ 0 +#define CV_STORAGE_WRITE 1 +#define CV_STORAGE_WRITE_TEXT CV_STORAGE_WRITE +#define CV_STORAGE_WRITE_BINARY CV_STORAGE_WRITE +#define CV_STORAGE_APPEND 2 +#define CV_STORAGE_MEMORY 4 +#define CV_STORAGE_FORMAT_MASK (7<<3) +#define CV_STORAGE_FORMAT_AUTO 0 +#define CV_STORAGE_FORMAT_XML 8 +#define CV_STORAGE_FORMAT_YAML 16 +#define CV_STORAGE_FORMAT_JSON 24 +#define CV_STORAGE_BASE64 64 +#define CV_STORAGE_WRITE_BASE64 (CV_STORAGE_BASE64 | CV_STORAGE_WRITE) + +/** @brief List of attributes. : + +In the current implementation, attributes are used to pass extra parameters when writing user +objects (see cvWrite). XML attributes inside tags are not supported, aside from the object type +specification (type_id attribute). +@see cvAttrList, cvAttrValue + */ +typedef struct CvAttrList +{ + const char** attr; /**< NULL-terminated array of (attribute_name,attribute_value) pairs. */ + struct CvAttrList* next; /**< Pointer to next chunk of the attributes list. */ +} +CvAttrList; + +/** initializes CvAttrList structure */ +CV_INLINE CvAttrList cvAttrList( const char** attr CV_DEFAULT(NULL), + CvAttrList* next CV_DEFAULT(NULL) ) +{ + CvAttrList l; + l.attr = attr; + l.next = next; + + return l; +} + +struct CvTypeInfo; + +#define CV_NODE_NONE 0 +#define CV_NODE_INT 1 +#define CV_NODE_INTEGER CV_NODE_INT +#define CV_NODE_REAL 2 +#define CV_NODE_FLOAT CV_NODE_REAL +#define CV_NODE_STR 3 +#define CV_NODE_STRING CV_NODE_STR +#define CV_NODE_REF 4 /**< not used */ +#define CV_NODE_SEQ 5 +#define CV_NODE_MAP 6 +#define CV_NODE_TYPE_MASK 7 + +#define CV_NODE_TYPE(flags) ((flags) & CV_NODE_TYPE_MASK) + +/** file node flags */ +#define CV_NODE_FLOW 8 /**= CV_NODE_SEQ) +#define CV_NODE_IS_FLOW(flags) (((flags) & CV_NODE_FLOW) != 0) +#define CV_NODE_IS_EMPTY(flags) (((flags) & CV_NODE_EMPTY) != 0) +#define CV_NODE_IS_USER(flags) (((flags) & CV_NODE_USER) != 0) +#define CV_NODE_HAS_NAME(flags) (((flags) & CV_NODE_NAMED) != 0) + +#define CV_NODE_SEQ_SIMPLE 256 +#define CV_NODE_SEQ_IS_SIMPLE(seq) (((seq)->flags & CV_NODE_SEQ_SIMPLE) != 0) + +typedef struct CvString +{ + int len; + char* ptr; +} +CvString; + +/** All the keys (names) of elements in the read file storage + are stored in the hash to speed up the lookup operations: */ +typedef struct CvStringHashNode +{ + unsigned hashval; + CvString str; + struct CvStringHashNode* next; +} +CvStringHashNode; + +typedef struct CvGenericHash CvFileNodeHash; + +/** Basic element of the file storage - scalar or collection: */ +typedef struct CvFileNode +{ + int tag; + struct CvTypeInfo* info; /**< type information + (only for user-defined object, for others it is 0) */ + union + { + double f; /**< scalar floating-point number */ + int i; /**< scalar integer number */ + CvString str; /**< text string */ + CvSeq* seq; /**< sequence (ordered collection of file nodes) */ + CvFileNodeHash* map; /**< map (collection of named file nodes) */ + } data; +} +CvFileNode; + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (CV_CDECL *CvIsInstanceFunc)( const void* struct_ptr ); +typedef void (CV_CDECL *CvReleaseFunc)( void** struct_dblptr ); +typedef void* (CV_CDECL *CvReadFunc)( CvFileStorage* storage, CvFileNode* node ); +typedef void (CV_CDECL *CvWriteFunc)( CvFileStorage* storage, const char* name,const void* struct_ptr, CvAttrList attributes ); +typedef void* (CV_CDECL *CvCloneFunc)( const void* struct_ptr ); +#ifdef __cplusplus +} +#endif + +/** @brief Type information + +The structure contains information about one of the standard or user-defined types. Instances of the +type may or may not contain a pointer to the corresponding CvTypeInfo structure. In any case, there +is a way to find the type info structure for a given object using the cvTypeOf function. +Alternatively, type info can be found by type name using cvFindType, which is used when an object +is read from file storage. The user can register a new type with cvRegisterType that adds the type +information structure into the beginning of the type list. Thus, it is possible to create +specialized types from generic standard types and override the basic methods. + */ +typedef struct CvTypeInfo +{ + int flags; /**< not used */ + int header_size; /**< sizeof(CvTypeInfo) */ + struct CvTypeInfo* prev; /**< previous registered type in the list */ + struct CvTypeInfo* next; /**< next registered type in the list */ + const char* type_name; /**< type name, written to file storage */ + CvIsInstanceFunc is_instance; /**< checks if the passed object belongs to the type */ + CvReleaseFunc release; /**< releases object (memory etc.) */ + CvReadFunc read; /**< reads object from file storage */ + CvWriteFunc write; /**< writes object to file storage */ + CvCloneFunc clone; /**< creates a copy of the object */ +} +CvTypeInfo; + + +/**** System data types ******/ + +typedef struct CvPluginFuncInfo +{ + void** func_addr; + void* default_func_addr; + const char* func_names; + int search_modules; + int loaded_from; +} +CvPluginFuncInfo; + +typedef struct CvModuleInfo +{ + struct CvModuleInfo* next; + const char* name; + const char* version; + CvPluginFuncInfo* func_tab; +} +CvModuleInfo; + +/** @} */ + +#endif /*OPENCV_CORE_TYPES_H*/ + +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utility.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utility.hpp new file mode 100644 index 0000000..86cd2f1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utility.hpp @@ -0,0 +1,1245 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_UTILITY_H +#define OPENCV_CORE_UTILITY_H + +#ifndef __cplusplus +# error utility.hpp header must be compiled as C++ +#endif + +#if defined(check) +# warning Detected Apple 'check' macro definition, it can cause build conflicts. Please, include this header before any Apple headers. +#endif + +#include "opencv2/core.hpp" +#include + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +/** @brief Automatically Allocated Buffer Class + + The class is used for temporary buffers in functions and methods. + If a temporary buffer is usually small (a few K's of memory), + but its size depends on the parameters, it makes sense to create a small + fixed-size array on stack and use it if it's large enough. If the required buffer size + is larger than the fixed size, another buffer of sufficient size is allocated dynamically + and released after the processing. Therefore, in typical cases, when the buffer size is small, + there is no overhead associated with malloc()/free(). + At the same time, there is no limit on the size of processed data. + + This is what AutoBuffer does. The template takes 2 parameters - type of the buffer elements and + the number of stack-allocated elements. Here is how the class is used: + + \code + void my_func(const cv::Mat& m) + { + cv::AutoBuffer buf(1000); // create automatic buffer containing 1000 floats + + buf.allocate(m.rows); // if m.rows <= 1000, the pre-allocated buffer is used, + // otherwise the buffer of "m.rows" floats will be allocated + // dynamically and deallocated in cv::AutoBuffer destructor + ... + } + \endcode +*/ +#ifdef OPENCV_ENABLE_MEMORY_SANITIZER +template class AutoBuffer +#else +template class AutoBuffer +#endif +{ +public: + typedef _Tp value_type; + + //! the default constructor + AutoBuffer(); + //! constructor taking the real buffer size + explicit AutoBuffer(size_t _size); + + //! the copy constructor + AutoBuffer(const AutoBuffer<_Tp, fixed_size>& buf); + //! the assignment operator + AutoBuffer<_Tp, fixed_size>& operator = (const AutoBuffer<_Tp, fixed_size>& buf); + + //! destructor. calls deallocate() + ~AutoBuffer(); + + //! allocates the new buffer of size _size. if the _size is small enough, stack-allocated buffer is used + void allocate(size_t _size); + //! deallocates the buffer if it was dynamically allocated + void deallocate(); + //! resizes the buffer and preserves the content + void resize(size_t _size); + //! returns the current buffer size + size_t size() const; + //! returns pointer to the real buffer, stack-allocated or heap-allocated + inline _Tp* data() { return ptr; } + //! returns read-only pointer to the real buffer, stack-allocated or heap-allocated + inline const _Tp* data() const { return ptr; } + +#if !defined(OPENCV_DISABLE_DEPRECATED_COMPATIBILITY) // use to .data() calls instead + //! returns pointer to the real buffer, stack-allocated or heap-allocated + operator _Tp* () { return ptr; } + //! returns read-only pointer to the real buffer, stack-allocated or heap-allocated + operator const _Tp* () const { return ptr; } +#else + //! returns a reference to the element at specified location. No bounds checking is performed in Release builds. + inline _Tp& operator[] (size_t i) { CV_DbgCheckLT(i, sz, "out of range"); return ptr[i]; } + //! returns a reference to the element at specified location. No bounds checking is performed in Release builds. + inline const _Tp& operator[] (size_t i) const { CV_DbgCheckLT(i, sz, "out of range"); return ptr[i]; } +#endif + +protected: + //! pointer to the real buffer, can point to buf if the buffer is small enough + _Tp* ptr; + //! size of the real buffer + size_t sz; + //! pre-allocated buffer. At least 1 element to confirm C++ standard requirements + _Tp buf[(fixed_size > 0) ? fixed_size : 1]; +}; + +/** @brief Sets/resets the break-on-error mode. + +When the break-on-error mode is set, the default error handler issues a hardware exception, which +can make debugging more convenient. + +\return the previous state + */ +CV_EXPORTS bool setBreakOnError(bool flag); + +extern "C" typedef int (*ErrorCallback)( int status, const char* func_name, + const char* err_msg, const char* file_name, + int line, void* userdata ); + + +/** @brief Sets the new error handler and the optional user data. + + The function sets the new error handler, called from cv::error(). + + \param errCallback the new error handler. If NULL, the default error handler is used. + \param userdata the optional user data pointer, passed to the callback. + \param prevUserdata the optional output parameter where the previous user data pointer is stored + + \return the previous error handler +*/ +CV_EXPORTS ErrorCallback redirectError( ErrorCallback errCallback, void* userdata=0, void** prevUserdata=0); + +CV_EXPORTS String tempfile( const char* suffix = 0); +CV_EXPORTS void glob(String pattern, std::vector& result, bool recursive = false); + +/** @brief OpenCV will try to set the number of threads for the next parallel region. + +If threads == 0, OpenCV will disable threading optimizations and run all it's functions +sequentially. Passing threads \< 0 will reset threads number to system default. This function must +be called outside of parallel region. + +OpenCV will try to run its functions with specified threads number, but some behaviour differs from +framework: +- `TBB` - User-defined parallel constructions will run with the same threads number, if + another is not specified. If later on user creates his own scheduler, OpenCV will use it. +- `OpenMP` - No special defined behaviour. +- `Concurrency` - If threads == 1, OpenCV will disable threading optimizations and run its + functions sequentially. +- `GCD` - Supports only values \<= 0. +- `C=` - No special defined behaviour. +@param nthreads Number of threads used by OpenCV. +@sa getNumThreads, getThreadNum + */ +CV_EXPORTS_W void setNumThreads(int nthreads); + +/** @brief Returns the number of threads used by OpenCV for parallel regions. + +Always returns 1 if OpenCV is built without threading support. + +The exact meaning of return value depends on the threading framework used by OpenCV library: +- `TBB` - The number of threads, that OpenCV will try to use for parallel regions. If there is + any tbb::thread_scheduler_init in user code conflicting with OpenCV, then function returns + default number of threads used by TBB library. +- `OpenMP` - An upper bound on the number of threads that could be used to form a new team. +- `Concurrency` - The number of threads, that OpenCV will try to use for parallel regions. +- `GCD` - Unsupported; returns the GCD thread pool limit (512) for compatibility. +- `C=` - The number of threads, that OpenCV will try to use for parallel regions, if before + called setNumThreads with threads \> 0, otherwise returns the number of logical CPUs, + available for the process. +@sa setNumThreads, getThreadNum + */ +CV_EXPORTS_W int getNumThreads(); + +/** @brief Returns the index of the currently executed thread within the current parallel region. Always +returns 0 if called outside of parallel region. + +@deprecated Current implementation doesn't corresponding to this documentation. + +The exact meaning of the return value depends on the threading framework used by OpenCV library: +- `TBB` - Unsupported with current 4.1 TBB release. Maybe will be supported in future. +- `OpenMP` - The thread number, within the current team, of the calling thread. +- `Concurrency` - An ID for the virtual processor that the current context is executing on (0 + for master thread and unique number for others, but not necessary 1,2,3,...). +- `GCD` - System calling thread's ID. Never returns 0 inside parallel region. +- `C=` - The index of the current parallel task. +@sa setNumThreads, getNumThreads + */ +CV_EXPORTS_W int getThreadNum(); + +/** @brief Returns full configuration time cmake output. + +Returned value is raw cmake output including version control system revision, compiler version, +compiler flags, enabled modules and third party libraries, etc. Output format depends on target +architecture. + */ +CV_EXPORTS_W const String& getBuildInformation(); + +/** @brief Returns library version string + +For example "3.4.1-dev". + +@sa getMajorVersion, getMinorVersion, getRevisionVersion +*/ +CV_EXPORTS_W String getVersionString(); + +/** @brief Returns major library version */ +CV_EXPORTS_W int getVersionMajor(); + +/** @brief Returns minor library version */ +CV_EXPORTS_W int getVersionMinor(); + +/** @brief Returns revision field of the library version */ +CV_EXPORTS_W int getVersionRevision(); + +/** @brief Returns the number of ticks. + +The function returns the number of ticks after the certain event (for example, when the machine was +turned on). It can be used to initialize RNG or to measure a function execution time by reading the +tick count before and after the function call. +@sa getTickFrequency, TickMeter + */ +CV_EXPORTS_W int64 getTickCount(); + +/** @brief Returns the number of ticks per second. + +The function returns the number of ticks per second. That is, the following code computes the +execution time in seconds: +@code + double t = (double)getTickCount(); + // do something ... + t = ((double)getTickCount() - t)/getTickFrequency(); +@endcode +@sa getTickCount, TickMeter + */ +CV_EXPORTS_W double getTickFrequency(); + +/** @brief a Class to measure passing time. + +The class computes passing time by counting the number of ticks per second. That is, the following code computes the +execution time in seconds: +@code +TickMeter tm; +tm.start(); +// do something ... +tm.stop(); +std::cout << tm.getTimeSec(); +@endcode + +It is also possible to compute the average time over multiple runs: +@code +TickMeter tm; +for (int i = 0; i < 100; i++) +{ + tm.start(); + // do something ... + tm.stop(); +} +double average_time = tm.getTimeSec() / tm.getCounter(); +std::cout << "Average time in second per iteration is: " << average_time << std::endl; +@endcode +@sa getTickCount, getTickFrequency +*/ + +class CV_EXPORTS_W TickMeter +{ +public: + //! the default constructor + CV_WRAP TickMeter() + { + reset(); + } + + /** + starts counting ticks. + */ + CV_WRAP void start() + { + startTime = cv::getTickCount(); + } + + /** + stops counting ticks. + */ + CV_WRAP void stop() + { + int64 time = cv::getTickCount(); + if (startTime == 0) + return; + ++counter; + sumTime += (time - startTime); + startTime = 0; + } + + /** + returns counted ticks. + */ + CV_WRAP int64 getTimeTicks() const + { + return sumTime; + } + + /** + returns passed time in microseconds. + */ + CV_WRAP double getTimeMicro() const + { + return getTimeMilli()*1e3; + } + + /** + returns passed time in milliseconds. + */ + CV_WRAP double getTimeMilli() const + { + return getTimeSec()*1e3; + } + + /** + returns passed time in seconds. + */ + CV_WRAP double getTimeSec() const + { + return (double)getTimeTicks() / getTickFrequency(); + } + + /** + returns internal counter value. + */ + CV_WRAP int64 getCounter() const + { + return counter; + } + + /** + resets internal values. + */ + CV_WRAP void reset() + { + startTime = 0; + sumTime = 0; + counter = 0; + } + +private: + int64 counter; + int64 sumTime; + int64 startTime; +}; + +/** @brief output operator +@code +TickMeter tm; +tm.start(); +// do something ... +tm.stop(); +std::cout << tm; +@endcode +*/ + +static inline +std::ostream& operator << (std::ostream& out, const TickMeter& tm) +{ + return out << tm.getTimeSec() << "sec"; +} + +/** @brief Returns the number of CPU ticks. + +The function returns the current number of CPU ticks on some architectures (such as x86, x64, +PowerPC). On other platforms the function is equivalent to getTickCount. It can also be used for +very accurate time measurements, as well as for RNG initialization. Note that in case of multi-CPU +systems a thread, from which getCPUTickCount is called, can be suspended and resumed at another CPU +with its own counter. So, theoretically (and practically) the subsequent calls to the function do +not necessary return the monotonously increasing values. Also, since a modern CPU varies the CPU +frequency depending on the load, the number of CPU clocks spent in some code cannot be directly +converted to time units. Therefore, getTickCount is generally a preferable solution for measuring +execution time. + */ +CV_EXPORTS_W int64 getCPUTickCount(); + +/** @brief Returns true if the specified feature is supported by the host hardware. + +The function returns true if the host hardware supports the specified feature. When user calls +setUseOptimized(false), the subsequent calls to checkHardwareSupport() will return false until +setUseOptimized(true) is called. This way user can dynamically switch on and off the optimized code +in OpenCV. +@param feature The feature of interest, one of cv::CpuFeatures + */ +CV_EXPORTS_W bool checkHardwareSupport(int feature); + +/** @brief Returns feature name by ID + +Returns empty string if feature is not defined +*/ +CV_EXPORTS_W String getHardwareFeatureName(int feature); + +/** @brief Returns list of CPU features enabled during compilation. + +Returned value is a string containing space separated list of CPU features with following markers: + +- no markers - baseline features +- prefix `*` - features enabled in dispatcher +- suffix `?` - features enabled but not available in HW + +Example: `SSE SSE2 SSE3 *SSE4.1 *SSE4.2 *FP16 *AVX *AVX2 *AVX512-SKX?` +*/ +CV_EXPORTS_W std::string getCPUFeaturesLine(); + +/** @brief Returns the number of logical CPUs available for the process. + */ +CV_EXPORTS_W int getNumberOfCPUs(); + + +/** @brief Aligns a pointer to the specified number of bytes. + +The function returns the aligned pointer of the same type as the input pointer: +\f[\texttt{(_Tp*)(((size_t)ptr + n-1) & -n)}\f] +@param ptr Aligned pointer. +@param n Alignment size that must be a power of two. + */ +template static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_Tp)) +{ + CV_DbgAssert((n & (n - 1)) == 0); // n is a power of 2 + return (_Tp*)(((size_t)ptr + n-1) & -n); +} + +/** @brief Aligns a buffer size to the specified number of bytes. + +The function returns the minimum number that is greater than or equal to sz and is divisible by n : +\f[\texttt{(sz + n-1) & -n}\f] +@param sz Buffer size to align. +@param n Alignment size that must be a power of two. + */ +static inline size_t alignSize(size_t sz, int n) +{ + CV_DbgAssert((n & (n - 1)) == 0); // n is a power of 2 + return (sz + n-1) & -n; +} + +/** @brief Integer division with result round up. + +Use this function instead of `ceil((float)a / b)` expressions. + +@sa alignSize +*/ +static inline int divUp(int a, unsigned int b) +{ + CV_DbgAssert(a >= 0); + return (a + b - 1) / b; +} +/** @overload */ +static inline size_t divUp(size_t a, unsigned int b) +{ + return (a + b - 1) / b; +} + +/** @brief Round first value up to the nearest multiple of second value. + +Use this function instead of `ceil((float)a / b) * b` expressions. + +@sa divUp +*/ +static inline int roundUp(int a, unsigned int b) +{ + CV_DbgAssert(a >= 0); + return a + b - 1 - (a + b -1) % b; +} +/** @overload */ +static inline size_t roundUp(size_t a, unsigned int b) +{ + return a + b - 1 - (a + b - 1) % b; +} + +/** @brief Alignment check of passed values + +Usage: `isAligned(...)` + +@note Alignment(N) must be a power of 2 (2**k, 2^k) +*/ +template static inline +bool isAligned(const T& data) +{ + CV_StaticAssert((N & (N - 1)) == 0, ""); // power of 2 + return (((size_t)data) & (N - 1)) == 0; +} +/** @overload */ +template static inline +bool isAligned(const void* p1) +{ + return isAligned((size_t)p1); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2) +{ + return isAligned(((size_t)p1)|((size_t)p2)); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2, const void* p3) +{ + return isAligned(((size_t)p1)|((size_t)p2)|((size_t)p3)); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2, const void* p3, const void* p4) +{ + return isAligned(((size_t)p1)|((size_t)p2)|((size_t)p3)|((size_t)p4)); +} + +/** @brief Enables or disables the optimized code. + +The function can be used to dynamically turn on and off optimized dispatched code (code that uses SSE4.2, AVX/AVX2, +and other instructions on the platforms that support it). It sets a global flag that is further +checked by OpenCV functions. Since the flag is not checked in the inner OpenCV loops, it is only +safe to call the function on the very top level in your application where you can be sure that no +other OpenCV function is currently executed. + +By default, the optimized code is enabled unless you disable it in CMake. The current status can be +retrieved using useOptimized. +@param onoff The boolean flag specifying whether the optimized code should be used (onoff=true) +or not (onoff=false). + */ +CV_EXPORTS_W void setUseOptimized(bool onoff); + +/** @brief Returns the status of optimized code usage. + +The function returns true if the optimized code is enabled. Otherwise, it returns false. + */ +CV_EXPORTS_W bool useOptimized(); + +static inline size_t getElemSize(int type) { return (size_t)CV_ELEM_SIZE(type); } + +/////////////////////////////// Parallel Primitives ////////////////////////////////// + +/** @brief Base class for parallel data processors +*/ +class CV_EXPORTS ParallelLoopBody +{ +public: + virtual ~ParallelLoopBody(); + virtual void operator() (const Range& range) const = 0; +}; + +/** @brief Parallel data processor +*/ +CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body, double nstripes=-1.); + +#ifdef CV_CXX11 +class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody +{ +private: + std::function m_functor; +public: + ParallelLoopBodyLambdaWrapper(std::function functor) : + m_functor(functor) + { } + + virtual void operator() (const cv::Range& range) const CV_OVERRIDE + { + m_functor(range); + } +}; + +inline void parallel_for_(const Range& range, std::function functor, double nstripes=-1.) +{ + parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes); +} +#endif + +/////////////////////////////// forEach method of cv::Mat //////////////////////////// +template inline +void Mat::forEach_impl(const Functor& operation) { + if (false) { + operation(*reinterpret_cast<_Tp*>(0), reinterpret_cast(0)); + // If your compiler fails in this line. + // Please check that your functor signature is + // (_Tp&, const int*) <- multi-dimensional + // or (_Tp&, void*) <- in case you don't need current idx. + } + + CV_Assert(this->total() / this->size[this->dims - 1] <= INT_MAX); + const int LINES = static_cast(this->total() / this->size[this->dims - 1]); + + class PixelOperationWrapper :public ParallelLoopBody + { + public: + PixelOperationWrapper(Mat_<_Tp>* const frame, const Functor& _operation) + : mat(frame), op(_operation) {} + virtual ~PixelOperationWrapper(){} + // ! Overloaded virtual operator + // convert range call to row call. + virtual void operator()(const Range &range) const CV_OVERRIDE + { + const int DIMS = mat->dims; + const int COLS = mat->size[DIMS - 1]; + if (DIMS <= 2) { + for (int row = range.start; row < range.end; ++row) { + this->rowCall2(row, COLS); + } + } else { + std::vector idx(DIMS); /// idx is modified in this->rowCall + idx[DIMS - 2] = range.start - 1; + + for (int line_num = range.start; line_num < range.end; ++line_num) { + idx[DIMS - 2]++; + for (int i = DIMS - 2; i >= 0; --i) { + if (idx[i] >= mat->size[i]) { + idx[i - 1] += idx[i] / mat->size[i]; + idx[i] %= mat->size[i]; + continue; // carry-over; + } + else { + break; + } + } + this->rowCall(&idx[0], COLS, DIMS); + } + } + } + private: + Mat_<_Tp>* const mat; + const Functor op; + // ! Call operator for each elements in this row. + inline void rowCall(int* const idx, const int COLS, const int DIMS) const { + int &col = idx[DIMS - 1]; + col = 0; + _Tp* pixel = &(mat->template at<_Tp>(idx)); + + while (col < COLS) { + op(*pixel, const_cast(idx)); + pixel++; col++; + } + col = 0; + } + // ! Call operator for each elements in this row. 2d mat special version. + inline void rowCall2(const int row, const int COLS) const { + union Index{ + int body[2]; + operator const int*() const { + return reinterpret_cast(this); + } + int& operator[](const int i) { + return body[i]; + } + } idx = {{row, 0}}; + // Special union is needed to avoid + // "error: array subscript is above array bounds [-Werror=array-bounds]" + // when call the functor `op` such that access idx[3]. + + _Tp* pixel = &(mat->template at<_Tp>(idx)); + const _Tp* const pixel_end = pixel + COLS; + while(pixel < pixel_end) { + op(*pixel++, static_cast(idx)); + idx[1]++; + } + } + PixelOperationWrapper& operator=(const PixelOperationWrapper &) { + CV_Assert(false); + // We can not remove this implementation because Visual Studio warning C4822. + return *this; + } + }; + + parallel_for_(cv::Range(0, LINES), PixelOperationWrapper(reinterpret_cast*>(this), operation)); +} + +/////////////////////////// Synchronization Primitives /////////////////////////////// + +class CV_EXPORTS Mutex +{ +public: + Mutex(); + ~Mutex(); + Mutex(const Mutex& m); + Mutex& operator = (const Mutex& m); + + void lock(); + bool trylock(); + void unlock(); + + struct Impl; +protected: + Impl* impl; +}; + +class CV_EXPORTS AutoLock +{ +public: + AutoLock(Mutex& m) : mutex(&m) { mutex->lock(); } + ~AutoLock() { mutex->unlock(); } +protected: + Mutex* mutex; +private: + AutoLock(const AutoLock&); + AutoLock& operator = (const AutoLock&); +}; + + +/** @brief Designed for command line parsing + +The sample below demonstrates how to use CommandLineParser: +@code + CommandLineParser parser(argc, argv, keys); + parser.about("Application name v1.0.0"); + + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + int N = parser.get("N"); + double fps = parser.get("fps"); + String path = parser.get("path"); + + use_time_stamp = parser.has("timestamp"); + + String img1 = parser.get(0); + String img2 = parser.get(1); + + int repeat = parser.get(2); + + if (!parser.check()) + { + parser.printErrors(); + return 0; + } +@endcode + +### Keys syntax + +The keys parameter is a string containing several blocks, each one is enclosed in curly braces and +describes one argument. Each argument contains three parts separated by the `|` symbol: + +-# argument names is a space-separated list of option synonyms (to mark argument as positional, prefix it with the `@` symbol) +-# default value will be used if the argument was not provided (can be empty) +-# help message (can be empty) + +For example: + +@code{.cpp} + const String keys = + "{help h usage ? | | print this message }" + "{@image1 | | image1 for compare }" + "{@image2 || image2 for compare }" + "{@repeat |1 | number }" + "{path |. | path to file }" + "{fps | -1.0 | fps for output video }" + "{N count |100 | count of objects }" + "{ts timestamp | | use time stamp }" + ; +} +@endcode + +Note that there are no default values for `help` and `timestamp` so we can check their presence using the `has()` method. +Arguments with default values are considered to be always present. Use the `get()` method in these cases to check their +actual value instead. + +String keys like `get("@image1")` return the empty string `""` by default - even with an empty default value. +Use the special `` default value to enforce that the returned string must not be empty. (like in `get("@image2")`) + +### Usage + +For the described keys: + +@code{.sh} + # Good call (3 positional parameters: image1, image2 and repeat; N is 200, ts is true) + $ ./app -N=200 1.png 2.jpg 19 -ts + + # Bad call + $ ./app -fps=aaa + ERRORS: + Parameter 'fps': can not convert: [aaa] to [double] +@endcode + */ +class CV_EXPORTS CommandLineParser +{ +public: + + /** @brief Constructor + + Initializes command line parser object + + @param argc number of command line arguments (from main()) + @param argv array of command line arguments (from main()) + @param keys string describing acceptable command line parameters (see class description for syntax) + */ + CommandLineParser(int argc, const char* const argv[], const String& keys); + + /** @brief Copy constructor */ + CommandLineParser(const CommandLineParser& parser); + + /** @brief Assignment operator */ + CommandLineParser& operator = (const CommandLineParser& parser); + + /** @brief Destructor */ + ~CommandLineParser(); + + /** @brief Returns application path + + This method returns the path to the executable from the command line (`argv[0]`). + + For example, if the application has been started with such a command: + @code{.sh} + $ ./bin/my-executable + @endcode + this method will return `./bin`. + */ + String getPathToApplication() const; + + /** @brief Access arguments by name + + Returns argument converted to selected type. If the argument is not known or can not be + converted to selected type, the error flag is set (can be checked with @ref check). + + For example, define: + @code{.cpp} + String keys = "{N count||}"; + @endcode + + Call: + @code{.sh} + $ ./my-app -N=20 + # or + $ ./my-app --count=20 + @endcode + + Access: + @code{.cpp} + int N = parser.get("N"); + @endcode + + @param name name of the argument + @param space_delete remove spaces from the left and right of the string + @tparam T the argument will be converted to this type if possible + + @note You can access positional arguments by their `@`-prefixed name: + @code{.cpp} + parser.get("@image"); + @endcode + */ + template + T get(const String& name, bool space_delete = true) const + { + T val = T(); + getByName(name, space_delete, ParamType::type, (void*)&val); + return val; + } + + /** @brief Access positional arguments by index + + Returns argument converted to selected type. Indexes are counted from zero. + + For example, define: + @code{.cpp} + String keys = "{@arg1||}{@arg2||}" + @endcode + + Call: + @code{.sh} + ./my-app abc qwe + @endcode + + Access arguments: + @code{.cpp} + String val_1 = parser.get(0); // returns "abc", arg1 + String val_2 = parser.get(1); // returns "qwe", arg2 + @endcode + + @param index index of the argument + @param space_delete remove spaces from the left and right of the string + @tparam T the argument will be converted to this type if possible + */ + template + T get(int index, bool space_delete = true) const + { + T val = T(); + getByIndex(index, space_delete, ParamType::type, (void*)&val); + return val; + } + + /** @brief Check if field was provided in the command line + + @param name argument name to check + */ + bool has(const String& name) const; + + /** @brief Check for parsing errors + + Returns false if error occurred while accessing the parameters (bad conversion, missing arguments, + etc.). Call @ref printErrors to print error messages list. + */ + bool check() const; + + /** @brief Set the about message + + The about message will be shown when @ref printMessage is called, right before arguments table. + */ + void about(const String& message); + + /** @brief Print help message + + This method will print standard help message containing the about message and arguments description. + + @sa about + */ + void printMessage() const; + + /** @brief Print list of errors occurred + + @sa check + */ + void printErrors() const; + +protected: + void getByName(const String& name, bool space_delete, int type, void* dst) const; + void getByIndex(int index, bool space_delete, int type, void* dst) const; + + struct Impl; + Impl* impl; +}; + +//! @} core_utils + +//! @cond IGNORED + +/////////////////////////////// AutoBuffer implementation //////////////////////////////////////// + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer() +{ + ptr = buf; + sz = fixed_size; +} + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size) +{ + ptr = buf; + sz = fixed_size; + allocate(_size); +} + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer(const AutoBuffer<_Tp, fixed_size>& abuf ) +{ + ptr = buf; + sz = fixed_size; + allocate(abuf.size()); + for( size_t i = 0; i < sz; i++ ) + ptr[i] = abuf.ptr[i]; +} + +template inline AutoBuffer<_Tp, fixed_size>& +AutoBuffer<_Tp, fixed_size>::operator = (const AutoBuffer<_Tp, fixed_size>& abuf) +{ + if( this != &abuf ) + { + deallocate(); + allocate(abuf.size()); + for( size_t i = 0; i < sz; i++ ) + ptr[i] = abuf.ptr[i]; + } + return *this; +} + +template inline +AutoBuffer<_Tp, fixed_size>::~AutoBuffer() +{ deallocate(); } + +template inline void +AutoBuffer<_Tp, fixed_size>::allocate(size_t _size) +{ + if(_size <= sz) + { + sz = _size; + return; + } + deallocate(); + sz = _size; + if(_size > fixed_size) + { + ptr = new _Tp[_size]; + } +} + +template inline void +AutoBuffer<_Tp, fixed_size>::deallocate() +{ + if( ptr != buf ) + { + delete[] ptr; + ptr = buf; + sz = fixed_size; + } +} + +template inline void +AutoBuffer<_Tp, fixed_size>::resize(size_t _size) +{ + if(_size <= sz) + { + sz = _size; + return; + } + size_t i, prevsize = sz, minsize = MIN(prevsize, _size); + _Tp* prevptr = ptr; + + ptr = _size > fixed_size ? new _Tp[_size] : buf; + sz = _size; + + if( ptr != prevptr ) + for( i = 0; i < minsize; i++ ) + ptr[i] = prevptr[i]; + for( i = prevsize; i < _size; i++ ) + ptr[i] = _Tp(); + + if( prevptr != buf ) + delete[] prevptr; +} + +template inline size_t +AutoBuffer<_Tp, fixed_size>::size() const +{ return sz; } + +template<> inline std::string CommandLineParser::get(int index, bool space_delete) const +{ + return get(index, space_delete); +} +template<> inline std::string CommandLineParser::get(const String& name, bool space_delete) const +{ + return get(name, space_delete); +} + +//! @endcond + + +// Basic Node class for tree building +template +class CV_EXPORTS Node +{ +public: + Node() + { + m_pParent = 0; + } + Node(OBJECT& payload) : m_payload(payload) + { + m_pParent = 0; + } + ~Node() + { + removeChilds(); + if (m_pParent) + { + int idx = m_pParent->findChild(this); + if (idx >= 0) + m_pParent->m_childs.erase(m_pParent->m_childs.begin() + idx); + } + } + + Node* findChild(OBJECT& payload) const + { + for(size_t i = 0; i < this->m_childs.size(); i++) + { + if(this->m_childs[i]->m_payload == payload) + return this->m_childs[i]; + } + return NULL; + } + + int findChild(Node *pNode) const + { + for (size_t i = 0; i < this->m_childs.size(); i++) + { + if(this->m_childs[i] == pNode) + return (int)i; + } + return -1; + } + + void addChild(Node *pNode) + { + if(!pNode) + return; + + CV_Assert(pNode->m_pParent == 0); + pNode->m_pParent = this; + this->m_childs.push_back(pNode); + } + + void removeChilds() + { + for(size_t i = 0; i < m_childs.size(); i++) + { + m_childs[i]->m_pParent = 0; // avoid excessive parent vector trimming + delete m_childs[i]; + } + m_childs.clear(); + } + + int getDepth() + { + int count = 0; + Node *pParent = m_pParent; + while(pParent) count++, pParent = pParent->m_pParent; + return count; + } + +public: + OBJECT m_payload; + Node* m_pParent; + std::vector*> m_childs; +}; + + +namespace samples { + +//! @addtogroup core_utils_samples +// This section describes utility functions for OpenCV samples. +// +// @note Implementation of these utilities is not thread-safe. +// +//! @{ + +/** @brief Try to find requested data file + +Search directories: + +1. Directories passed via `addSamplesDataSearchPath()` +2. OPENCV_SAMPLES_DATA_PATH_HINT environment variable +3. OPENCV_SAMPLES_DATA_PATH environment variable + If parameter value is not empty and nothing is found then stop searching. +4. Detects build/install path based on: + a. current working directory (CWD) + b. and/or binary module location (opencv_core/opencv_world, doesn't work with static linkage) +5. Scan `/{,data,samples/data}` directories if build directory is detected or the current directory is in source tree. +6. Scan `/share/OpenCV` directory if install directory is detected. + +@see cv::utils::findDataFile + +@param relative_path Relative path to data file +@param required Specify "file not found" handling. + If true, function prints information message and raises cv::Exception. + If false, function returns empty result +@param silentMode Disables messages +@return Returns path (absolute or relative to the current directory) or empty string if file is not found +*/ +CV_EXPORTS_W cv::String findFile(const cv::String& relative_path, bool required = true, bool silentMode = false); + +CV_EXPORTS_W cv::String findFileOrKeep(const cv::String& relative_path, bool silentMode = false); + +inline cv::String findFileOrKeep(const cv::String& relative_path, bool silentMode) +{ + cv::String res = findFile(relative_path, false, silentMode); + if (res.empty()) + return relative_path; + return res; +} + +/** @brief Override search data path by adding new search location + +Use this only to override default behavior +Passed paths are used in LIFO order. + +@param path Path to used samples data +*/ +CV_EXPORTS_W void addSamplesDataSearchPath(const cv::String& path); + +/** @brief Append samples search data sub directory + +General usage is to add OpenCV modules name (`/modules//samples/data` -> `/samples/data` + `modules//samples/data`). +Passed subdirectories are used in LIFO order. + +@param subdir samples data sub directory +*/ +CV_EXPORTS_W void addSamplesDataSearchSubDirectory(const cv::String& subdir); + +//! @} +} // namespace samples + +namespace utils { + +CV_EXPORTS int getThreadID(); + +} // namespace + +} //namespace cv + +#ifdef CV_COLLECT_IMPL_DATA +#include "opencv2/core/utils/instrumentation.hpp" +#else +/// Collect implementation data on OpenCV function call. Requires ENABLE_IMPL_COLLECTION build option. +#define CV_IMPL_ADD(impl) +#endif + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/core/core_c.h" +#endif + +#endif //OPENCV_CORE_UTILITY_H diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.hpp new file mode 100644 index 0000000..79e9338 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.hpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ALLOCATOR_STATS_HPP +#define OPENCV_CORE_ALLOCATOR_STATS_HPP + +#include "../cvdef.h" + +namespace cv { namespace utils { + +class AllocatorStatisticsInterface +{ +protected: + AllocatorStatisticsInterface() {} + virtual ~AllocatorStatisticsInterface() {} +public: + virtual uint64_t getCurrentUsage() const = 0; + virtual uint64_t getTotalUsage() const = 0; + virtual uint64_t getNumberOfAllocations() const = 0; + virtual uint64_t getPeakUsage() const = 0; + + /** set peak usage = current usage */ + virtual void resetPeakUsage() = 0; +}; + +}} // namespace + +#endif // OPENCV_CORE_ALLOCATOR_STATS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.impl.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.impl.hpp new file mode 100644 index 0000000..61fcf15 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/allocator_stats.impl.hpp @@ -0,0 +1,150 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP +#define OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP + +#include "./allocator_stats.hpp" + +#ifdef CV_CXX11 +#include +#endif + +//#define OPENCV_DISABLE_ALLOCATOR_STATS + +namespace cv { namespace utils { + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#if defined(__GNUC__) && (\ + (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4) || \ + (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + ) +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE int +#endif +#endif + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE long long +#endif + +#ifdef CV__ALLOCATOR_STATS_LOG +namespace { +#endif + +class AllocatorStatistics : public AllocatorStatisticsInterface +{ +#ifdef OPENCV_DISABLE_ALLOCATOR_STATS + +public: + AllocatorStatistics() {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return 0; } + uint64_t getTotalUsage() const CV_OVERRIDE { return 0; } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return 0; } + uint64_t getPeakUsage() const CV_OVERRIDE { return 0; } + + /** set peak usage = current usage */ + void resetPeakUsage() CV_OVERRIDE {}; + + void onAllocate(size_t /*sz*/) {} + void onFree(size_t /*sz*/) {} + +#elif defined(CV_CXX11) + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + std::atomic curr, total, total_allocs, peak; +public: + AllocatorStatistics() {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr.load(); } + uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total.load(); } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs.load(); } + uint64_t getPeakUsage() const CV_OVERRIDE { return (uint64_t)peak.load(); } + + /** set peak usage = current usage */ + void resetPeakUsage() CV_OVERRIDE { peak.store(curr.load()); } + + // Controller interface + void onAllocate(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); +#endif + + counter_t new_curr = curr.fetch_add((counter_t)sz) + (counter_t)sz; + + // peak = std::max((uint64_t)peak, new_curr); + auto prev_peak = peak.load(); + while (prev_peak < new_curr) + { + if (peak.compare_exchange_weak(prev_peak, new_curr)) + break; + } + // end of peak = max(...) + + total += (counter_t)sz; + total_allocs++; + } + void onFree(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); +#endif + curr -= (counter_t)sz; + } + +#else // non C++11 + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + volatile counter_t curr, total, total_allocs, peak; // overflow is possible, CV_XADD operates with 'int' only +public: + AllocatorStatistics() + : curr(0), total(0), total_allocs(0), peak(0) + {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr; } + uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total; } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs; } + uint64_t getPeakUsage() const CV_OVERRIDE { return (uint64_t)peak; } + + void resetPeakUsage() CV_OVERRIDE { peak = curr; } + + // Controller interface + void onAllocate(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); +#endif + + counter_t new_curr = (counter_t)CV_XADD(&curr, (counter_t)sz) + (counter_t)sz; + + peak = std::max((counter_t)peak, new_curr); // non-thread safe + + //CV_XADD(&total, (uint64_t)sz); // overflow with int, non-reliable... + total += sz; + + CV_XADD(&total_allocs, (counter_t)1); + } + void onFree(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); +#endif + CV_XADD(&curr, (counter_t)-sz); + } +#endif +}; + +#ifdef CV__ALLOCATOR_STATS_LOG +} // namespace +#endif + +}} // namespace + +#endif // OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/filesystem.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/filesystem.hpp new file mode 100644 index 0000000..00b0dd1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/filesystem.hpp @@ -0,0 +1,78 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_FILESYSTEM_HPP +#define OPENCV_UTILS_FILESYSTEM_HPP + +namespace cv { namespace utils { namespace fs { + + +CV_EXPORTS bool exists(const cv::String& path); +CV_EXPORTS bool isDirectory(const cv::String& path); + +CV_EXPORTS void remove_all(const cv::String& path); + + +CV_EXPORTS cv::String getcwd(); + +/** @brief Converts path p to a canonical absolute path + * Symlinks are processed if there is support for them on running platform. + * + * @param path input path. Target file/directory should exist. + */ +CV_EXPORTS cv::String canonical(const cv::String& path); + +/** Join path components */ +CV_EXPORTS cv::String join(const cv::String& base, const cv::String& path); + +/** + * Generate a list of all files that match the globbing pattern. + * + * Result entries are prefixed by base directory path. + * + * @param directory base directory + * @param pattern filter pattern (based on '*'/'?' symbols). Use empty string to disable filtering and return all results + * @param[out] result result of globing. + * @param recursive scan nested directories too + * @param includeDirectories include directories into results list + */ +CV_EXPORTS void glob(const cv::String& directory, const cv::String& pattern, + CV_OUT std::vector& result, + bool recursive = false, bool includeDirectories = false); + +/** + * Generate a list of all files that match the globbing pattern. + * + * @param directory base directory + * @param pattern filter pattern (based on '*'/'?' symbols). Use empty string to disable filtering and return all results + * @param[out] result globbing result with relative paths from base directory + * @param recursive scan nested directories too + * @param includeDirectories include directories into results list + */ +CV_EXPORTS void glob_relative(const cv::String& directory, const cv::String& pattern, + CV_OUT std::vector& result, + bool recursive = false, bool includeDirectories = false); + + +CV_EXPORTS bool createDirectory(const cv::String& path); +CV_EXPORTS bool createDirectories(const cv::String& path); + +#ifdef __OPENCV_BUILD +// TODO +//CV_EXPORTS cv::String getTempDirectory(); + +/** + * @brief Returns directory to store OpenCV cache files + * Create sub-directory in common OpenCV cache directory if it doesn't exist. + * @param sub_directory_name name of sub-directory. NULL or "" value asks to return root cache directory. + * @param configuration_name optional name of configuration parameter name which overrides default behavior. + * @return Path to cache directory. Returns empty string if cache directories support is not available. Returns "disabled" if cache disabled by user. + */ +CV_EXPORTS cv::String getCacheDirectory(const char* sub_directory_name, const char* configuration_name = NULL); + +#endif + +}}} // namespace + +#endif // OPENCV_UTILS_FILESYSTEM_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/instrumentation.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/instrumentation.hpp new file mode 100644 index 0000000..3639867 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/instrumentation.hpp @@ -0,0 +1,125 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_INSTR_HPP +#define OPENCV_UTILS_INSTR_HPP + +#include +#include + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +#ifdef CV_COLLECT_IMPL_DATA +CV_EXPORTS void setImpl(int flags); // set implementation flags and reset storage arrays +CV_EXPORTS void addImpl(int flag, const char* func = 0); // add implementation and function name to storage arrays +// Get stored implementation flags and functions names arrays +// Each implementation entry correspond to function name entry, so you can find which implementation was executed in which function +CV_EXPORTS int getImpl(std::vector &impl, std::vector &funName); + +CV_EXPORTS bool useCollection(); // return implementation collection state +CV_EXPORTS void setUseCollection(bool flag); // set implementation collection state + +#define CV_IMPL_PLAIN 0x01 // native CPU OpenCV implementation +#define CV_IMPL_OCL 0x02 // OpenCL implementation +#define CV_IMPL_IPP 0x04 // IPP implementation +#define CV_IMPL_MT 0x10 // multithreaded implementation + +#undef CV_IMPL_ADD +#define CV_IMPL_ADD(impl) \ + if(cv::useCollection()) \ + { \ + cv::addImpl(impl, CV_Func); \ + } +#endif + +// Instrumentation external interface +namespace instr +{ + +#if !defined OPENCV_ABI_CHECK + +enum TYPE +{ + TYPE_GENERAL = 0, // OpenCV API function, e.g. exported function + TYPE_MARKER, // Information marker + TYPE_WRAPPER, // Wrapper function for implementation + TYPE_FUN, // Simple function call +}; + +enum IMPL +{ + IMPL_PLAIN = 0, + IMPL_IPP, + IMPL_OPENCL, +}; + +struct NodeDataTls +{ + NodeDataTls() + { + m_ticksTotal = 0; + } + uint64 m_ticksTotal; +}; + +class CV_EXPORTS NodeData +{ +public: + NodeData(const char* funName = 0, const char* fileName = NULL, int lineNum = 0, void* retAddress = NULL, bool alwaysExpand = false, cv::instr::TYPE instrType = TYPE_GENERAL, cv::instr::IMPL implType = IMPL_PLAIN); + NodeData(NodeData &ref); + ~NodeData(); + NodeData& operator=(const NodeData&); + + cv::String m_funName; + cv::instr::TYPE m_instrType; + cv::instr::IMPL m_implType; + const char* m_fileName; + int m_lineNum; + void* m_retAddress; + bool m_alwaysExpand; + bool m_funError; + + volatile int m_counter; + volatile uint64 m_ticksTotal; + TLSDataAccumulator m_tls; + int m_threads; + + // No synchronization + double getTotalMs() const { return ((double)m_ticksTotal / cv::getTickFrequency()) * 1000; } + double getMeanMs() const { return (((double)m_ticksTotal/m_counter) / cv::getTickFrequency()) * 1000; } +}; +bool operator==(const NodeData& lhs, const NodeData& rhs); + +typedef Node InstrNode; + +CV_EXPORTS InstrNode* getTrace(); + +#endif // !defined OPENCV_ABI_CHECK + + +CV_EXPORTS bool useInstrumentation(); +CV_EXPORTS void setUseInstrumentation(bool flag); +CV_EXPORTS void resetTrace(); + +enum FLAGS +{ + FLAGS_NONE = 0, + FLAGS_MAPPING = 0x01, + FLAGS_EXPAND_SAME_NAMES = 0x02, +}; + +CV_EXPORTS void setFlags(FLAGS modeFlags); +static inline void setFlags(int modeFlags) { setFlags((FLAGS)modeFlags); } +CV_EXPORTS FLAGS getFlags(); + +} // namespace instr + +//! @} + +} // namespace + +#endif // OPENCV_UTILS_TLS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.defines.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.defines.hpp new file mode 100644 index 0000000..b2dfc41 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.defines.hpp @@ -0,0 +1,22 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_LOGGER_DEFINES_HPP +#define OPENCV_LOGGER_DEFINES_HPP + +//! @addtogroup core_logging +//! @{ + +// Supported logging levels and their semantic +#define CV_LOG_LEVEL_SILENT 0 //!< for using in setLogLevel() call +#define CV_LOG_LEVEL_FATAL 1 //!< Fatal (critical) error (unrecoverable internal error) +#define CV_LOG_LEVEL_ERROR 2 //!< Error message +#define CV_LOG_LEVEL_WARN 3 //!< Warning message +#define CV_LOG_LEVEL_INFO 4 //!< Info message +#define CV_LOG_LEVEL_DEBUG 5 //!< Debug message. Disabled in the "Release" build. +#define CV_LOG_LEVEL_VERBOSE 6 //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build. + +//! @} + +#endif // OPENCV_LOGGER_DEFINES_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.hpp new file mode 100644 index 0000000..501d140 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/logger.hpp @@ -0,0 +1,159 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_LOGGER_HPP +#define OPENCV_LOGGER_HPP + +#include +#include +#include // INT_MAX + +#include "logger.defines.hpp" + +namespace cv { +namespace utils { +namespace logging { + +//! @addtogroup core_logging +//! @{ + +//! Supported logging levels and their semantic +enum LogLevel { + LOG_LEVEL_SILENT = 0, //!< for using in setLogVevel() call + LOG_LEVEL_FATAL = 1, //!< Fatal (critical) error (unrecoverable internal error) + LOG_LEVEL_ERROR = 2, //!< Error message + LOG_LEVEL_WARNING = 3, //!< Warning message + LOG_LEVEL_INFO = 4, //!< Info message + LOG_LEVEL_DEBUG = 5, //!< Debug message. Disabled in the "Release" build. + LOG_LEVEL_VERBOSE = 6, //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build. +#ifndef CV_DOXYGEN + ENUM_LOG_LEVEL_FORCE_INT = INT_MAX +#endif +}; + +/** Set global logging level +@return previous logging level +*/ +CV_EXPORTS LogLevel setLogLevel(LogLevel logLevel); +/** Get global logging level */ +CV_EXPORTS LogLevel getLogLevel(); + +namespace internal { +/** Write log message */ +CV_EXPORTS void writeLogMessage(LogLevel logLevel, const char* message); +} // namespace + +/** + * \def CV_LOG_STRIP_LEVEL + * + * Define CV_LOG_STRIP_LEVEL=CV_LOG_LEVEL_[DEBUG|INFO|WARN|ERROR|FATAL|DISABLED] to compile out anything at that and before that logging level + */ +#ifndef CV_LOG_STRIP_LEVEL +# if defined NDEBUG +# define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG +# else +# define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE +# endif +#endif + +#define CV_LOG_WITH_TAG(tag, msgLevel, extra_check0, extra_check1, msg_prefix, ...) \ + for(;;) { \ + extra_check0; \ + if (cv::utils::logging::getLogLevel() < msgLevel) break; \ + extra_check1; \ + std::stringstream ss; ss msg_prefix << __VA_ARGS__; \ + cv::utils::logging::internal::writeLogMessage(msgLevel, ss.str().c_str()); \ + break; \ + } + +#define CV_LOG_FATAL(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_FATAL, , , , __VA_ARGS__) +#define CV_LOG_ERROR(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, , , , __VA_ARGS__) +#define CV_LOG_WARNING(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, , , , __VA_ARGS__) +#define CV_LOG_INFO(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, , , , __VA_ARGS__) +#define CV_LOG_DEBUG(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, , , , __VA_ARGS__) +#define CV_LOG_VERBOSE(tag, v, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, , , << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_INFO +#define CV_LOG_INFO(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_DEBUG +#define CV_LOG_DEBUG(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_VERBOSE +#define CV_LOG_VERBOSE(tag, v, ...) +#endif + +//! @cond IGNORED +#define CV__LOG_ONCE_CHECK_PRE \ + static bool _cv_log_once_ ## __LINE__ = false; \ + if (_cv_log_once_ ## __LINE__) break; + +#define CV__LOG_ONCE_CHECK_POST \ + _cv_log_once_ ## __LINE__ = true; + +#define CV__LOG_IF_CHECK(logging_cond) \ + if (!(logging_cond)) break; + +//! @endcond + + +// CV_LOG_ONCE_XXX macros + +#define CV_LOG_ONCE_ERROR(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_WARNING(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_INFO(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_DEBUG(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_VERBOSE(tag, v, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_ONCE_INFO +#define CV_LOG_ONCE_INFO(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_ONCE_DEBUG +#define CV_LOG_ONCE_DEBUG(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_ONCE_VERBOSE +#define CV_LOG_ONCE_VERBOSE(tag, v, ...) +#endif + + +// CV_LOG_IF_XXX macros + +#define CV_LOG_IF_FATAL(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_FATAL, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_ERROR(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_WARNING(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_INFO(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_DEBUG(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_VERBOSE(tag, v, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, , CV__LOG_IF_CHECK(logging_cond), << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_IF_INFO +#define CV_LOG_IF_INFO(tag, logging_cond, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_IF_DEBUG +#define CV_LOG_IF_DEBUG(tag, logging_cond, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_IF_VERBOSE +#define CV_LOG_IF_VERBOSE(tag, v, logging_cond, ...) +#endif + + +//! @} + +}}} // namespace + +#endif // OPENCV_LOGGER_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/tls.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/tls.hpp new file mode 100644 index 0000000..b5f1138 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/tls.hpp @@ -0,0 +1,237 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_TLS_HPP +#define OPENCV_UTILS_TLS_HPP + +#include + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +namespace details { class TlsStorage; } + +/** TLS container base implementation + * + * Don't use directly. + * + * @sa TLSData, TLSDataAccumulator templates + */ +class CV_EXPORTS TLSDataContainer +{ +protected: + TLSDataContainer(); + virtual ~TLSDataContainer(); + + /// @deprecated use detachData() instead + void gatherData(std::vector &data) const; + /// get TLS data and detach all data from threads (similar to cleanup() call) + void detachData(std::vector& data); + + void* getData() const; + void release(); + +protected: + virtual void* createDataInstance() const = 0; + virtual void deleteDataInstance(void* pData) const = 0; + +#if OPENCV_ABI_COMPATIBILITY > 300 +private: +#else +public: +#endif + int key_; + + friend class cv::details::TlsStorage; // core/src/system.cpp + +public: + void cleanup(); //!< Release created TLS data container objects. It is similar to release() call, but it keeps TLS container valid. + +private: + // Disable copy/assign (noncopyable pattern) + TLSDataContainer(TLSDataContainer &); + TLSDataContainer& operator =(const TLSDataContainer &); +}; + + +/** @brief Simple TLS data class + * + * @sa TLSDataAccumulator + */ +template +class TLSData : protected TLSDataContainer +{ +public: + inline TLSData() {} + inline ~TLSData() { release(); } + + inline T* get() const { return (T*)getData(); } //!< Get data associated with key + inline T& getRef() const { T* ptr = (T*)getData(); CV_DbgAssert(ptr); return *ptr; } //!< Get data associated with key + + /// Release associated thread data + inline void cleanup() + { + TLSDataContainer::cleanup(); + } + +protected: + /// Wrapper to allocate data by template + virtual void* createDataInstance() const CV_OVERRIDE { return new T; } + /// Wrapper to release data by template + virtual void deleteDataInstance(void* pData) const CV_OVERRIDE { delete (T*)pData; } +}; + + +/// TLS data accumulator with gathering methods +template +class TLSDataAccumulator : public TLSData +{ + mutable cv::Mutex mutex; + mutable std::vector dataFromTerminatedThreads; + std::vector detachedData; + bool cleanupMode; +public: + TLSDataAccumulator() : cleanupMode(false) {} + ~TLSDataAccumulator() + { + release(); + } + + /** @brief Get data from all threads + * @deprecated replaced by detachData() + * + * Lifetime of vector data is valid until next detachData()/cleanup()/release() calls + * + * @param[out] data result buffer (should be empty) + */ + void gather(std::vector &data) const + { + CV_Assert(cleanupMode == false); // state is not valid + CV_Assert(data.empty()); + { + std::vector &dataVoid = reinterpret_cast&>(data); + TLSDataContainer::gatherData(dataVoid); + } + { + AutoLock lock(mutex); + data.reserve(data.size() + dataFromTerminatedThreads.size()); + for (typename std::vector::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + data.push_back((T*)*i); + } + } + } + + /** @brief Get and detach data from all threads + * + * Call cleanupDetachedData() when returned vector is not needed anymore. + * + * @return Vector with associated data. Content is preserved (including lifetime of attached data pointers) until next detachData()/cleanupDetachedData()/cleanup()/release() calls + */ + std::vector& detachData() + { + CV_Assert(cleanupMode == false); // state is not valid + std::vector dataVoid; + { + TLSDataContainer::detachData(dataVoid); + } + { + AutoLock lock(mutex); + detachedData.reserve(dataVoid.size() + dataFromTerminatedThreads.size()); + for (typename std::vector::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + detachedData.push_back((T*)*i); + } + dataFromTerminatedThreads.clear(); + for (typename std::vector::const_iterator i = dataVoid.begin(); i != dataVoid.end(); ++i) + { + detachedData.push_back((T*)(void*)*i); + } + } + dataVoid.clear(); + return detachedData; + } + + /// Release associated thread data returned by detachData() call + void cleanupDetachedData() + { + AutoLock lock(mutex); + cleanupMode = true; + _cleanupDetachedData(); + cleanupMode = false; + } + + /// Release associated thread data + void cleanup() + { + cleanupMode = true; + TLSDataContainer::cleanup(); + + AutoLock lock(mutex); + _cleanupDetachedData(); + _cleanupTerminatedData(); + cleanupMode = false; + } + + /// Release associated thread data and free TLS key + void release() + { + cleanupMode = true; + TLSDataContainer::release(); + { + AutoLock lock(mutex); + _cleanupDetachedData(); + _cleanupTerminatedData(); + } + } + +protected: + // synchronized + void _cleanupDetachedData() + { + for (typename std::vector::iterator i = detachedData.begin(); i != detachedData.end(); ++i) + { + deleteDataInstance((T*)*i); + } + detachedData.clear(); + } + + // synchronized + void _cleanupTerminatedData() + { + for (typename std::vector::iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + deleteDataInstance((T*)*i); + } + dataFromTerminatedThreads.clear(); + } + +protected: + virtual void* createDataInstance() const CV_OVERRIDE + { + // Note: we can collect all allocated data here, but this would require raced mutex locks + return new T; + } + virtual void deleteDataInstance(void* pData) const CV_OVERRIDE + { + if (cleanupMode) + { + delete (T*)pData; + } + else + { + AutoLock lock(mutex); + dataFromTerminatedThreads.push_back((T*)pData); + } + } +}; + + +//! @} + +} // namespace + +#endif // OPENCV_UTILS_TLS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/utils/trace.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/trace.hpp new file mode 100644 index 0000000..ef5d35b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/utils/trace.hpp @@ -0,0 +1,252 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_TRACE_HPP +#define OPENCV_TRACE_HPP + +#include + +namespace cv { +namespace utils { +namespace trace { + +//! @addtogroup core_logging +//! @{ + +//! Macro to trace function +#define CV_TRACE_FUNCTION() + +#define CV_TRACE_FUNCTION_SKIP_NESTED() + +//! Trace code scope. +//! @note Dynamic names are not supported in this macro (on stack or heap). Use string literals here only, like "initialize". +#define CV_TRACE_REGION(name_as_static_string_literal) +//! mark completed of the current opened region and create new one +//! @note Dynamic names are not supported in this macro (on stack or heap). Use string literals here only, like "step1". +#define CV_TRACE_REGION_NEXT(name_as_static_string_literal) + +//! Macro to trace argument value +#define CV_TRACE_ARG(arg_id) + +//! Macro to trace argument value (expanded version) +#define CV_TRACE_ARG_VALUE(arg_id, arg_name, value) + +//! @cond IGNORED +#define CV_TRACE_NS cv::utils::trace + +#if !defined(OPENCV_DISABLE_TRACE) && defined(__EMSCRIPTEN__) +#define OPENCV_DISABLE_TRACE 1 +#endif + +namespace details { + +#ifndef __OPENCV_TRACE +# if defined __OPENCV_BUILD && !defined __OPENCV_TESTS && !defined __OPENCV_APPS +# define __OPENCV_TRACE 1 +# else +# define __OPENCV_TRACE 0 +# endif +#endif + +#ifndef CV_TRACE_FILENAME +# define CV_TRACE_FILENAME __FILE__ +#endif + +#ifndef CV__TRACE_FUNCTION +# if defined _MSC_VER +# define CV__TRACE_FUNCTION __FUNCSIG__ +# elif defined __GNUC__ +# define CV__TRACE_FUNCTION __PRETTY_FUNCTION__ +# else +# define CV__TRACE_FUNCTION "" +# endif +#endif + +//! Thread-local instance (usually allocated on stack) +class CV_EXPORTS Region +{ +public: + struct LocationExtraData; + struct LocationStaticStorage + { + LocationExtraData** ppExtra; //< implementation specific data + const char* name; //< region name (function name or other custom name) + const char* filename; //< source code filename + int line; //< source code line + int flags; //< flags (implementation code path: Plain, IPP, OpenCL) + }; + + Region(const LocationStaticStorage& location); + inline ~Region() + { + if (implFlags != 0) + destroy(); + CV_DbgAssert(implFlags == 0); + CV_DbgAssert(pImpl == NULL); + } + + class Impl; + Impl* pImpl; // NULL if current region is not active + int implFlags; // see RegionFlag, 0 if region is ignored + + bool isActive() const { return pImpl != NULL; } + + void destroy(); +private: + Region(const Region&); // disabled + Region& operator= (const Region&); // disabled +}; + +//! Specify region flags +enum RegionLocationFlag { + REGION_FLAG_FUNCTION = (1 << 0), //< region is function (=1) / nested named region (=0) + REGION_FLAG_APP_CODE = (1 << 1), //< region is Application code (=1) / OpenCV library code (=0) + REGION_FLAG_SKIP_NESTED = (1 << 2), //< avoid processing of nested regions + + REGION_FLAG_IMPL_IPP = (1 << 16), //< region is part of IPP code path + REGION_FLAG_IMPL_OPENCL = (2 << 16), //< region is part of OpenCL code path + REGION_FLAG_IMPL_OPENVX = (3 << 16), //< region is part of OpenVX code path + + REGION_FLAG_IMPL_MASK = (15 << 16), + + REGION_FLAG_REGION_FORCE = (1 << 30), + REGION_FLAG_REGION_NEXT = (1 << 31), //< close previous region (see #CV_TRACE_REGION_NEXT macro) + + ENUM_REGION_FLAG_FORCE_INT = INT_MAX +}; + +struct CV_EXPORTS TraceArg { +public: + struct ExtraData; + ExtraData** ppExtra; + const char* name; + int flags; +}; +/** @brief Add meta information to current region (function) + * See CV_TRACE_ARG macro + * @param arg argument information structure (global static cache) + * @param value argument value (can by dynamic string literal in case of string, static allocation is not required) + */ +CV_EXPORTS void traceArg(const TraceArg& arg, const char* value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, int value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, int64 value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, double value); + +#define CV__TRACE_LOCATION_VARNAME(loc_id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_trace_location_, loc_id), __LINE__) +#define CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_trace_location_extra_, loc_id) , __LINE__) + +#define CV__TRACE_DEFINE_LOCATION_(loc_id, name, flags) \ + static CV_TRACE_NS::details::Region::LocationExtraData* CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id) = 0; \ + static const CV_TRACE_NS::details::Region::LocationStaticStorage \ + CV__TRACE_LOCATION_VARNAME(loc_id) = { &(CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id)), name, CV_TRACE_FILENAME, __LINE__, flags}; + +#define CV__TRACE_DEFINE_LOCATION_FN(name, flags) CV__TRACE_DEFINE_LOCATION_(fn, name, ((flags) | CV_TRACE_NS::details::REGION_FLAG_FUNCTION)) + + +#define CV__TRACE_OPENCV_FUNCTION() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, 0); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_OPENCV_FUNCTION_NAME(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, 0); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION_NAME(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + + +#define CV__TRACE_OPENCV_FUNCTION_SKIP_NESTED() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_OPENCV_FUNCTION_NAME_SKIP_NESTED(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION_SKIP_NESTED() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED | CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + + +#define CV__TRACE_REGION_(name_as_static_string_literal, flags) \ + CV__TRACE_DEFINE_LOCATION_(region, name_as_static_string_literal, flags); \ + CV_TRACE_NS::details::Region CVAUX_CONCAT(__region_, __LINE__)(CV__TRACE_LOCATION_VARNAME(region)); + +#define CV__TRACE_REGION(name_as_static_string_literal) CV__TRACE_REGION_(name_as_static_string_literal, 0) +#define CV__TRACE_REGION_NEXT(name_as_static_string_literal) CV__TRACE_REGION_(name_as_static_string_literal, CV_TRACE_NS::details::REGION_FLAG_REGION_NEXT) + +#define CV__TRACE_ARG_VARNAME(arg_id) CVAUX_CONCAT(__cv_trace_arg_ ## arg_id, __LINE__) +#define CV__TRACE_ARG_EXTRA_VARNAME(arg_id) CVAUX_CONCAT(__cv_trace_arg_extra_ ## arg_id, __LINE__) + +#define CV__TRACE_DEFINE_ARG_(arg_id, name, flags) \ + static CV_TRACE_NS::details::TraceArg::ExtraData* CV__TRACE_ARG_EXTRA_VARNAME(arg_id) = 0; \ + static const CV_TRACE_NS::details::TraceArg \ + CV__TRACE_ARG_VARNAME(arg_id) = { &(CV__TRACE_ARG_EXTRA_VARNAME(arg_id)), name, flags }; + +#define CV__TRACE_ARG_VALUE(arg_id, arg_name, value) \ + CV__TRACE_DEFINE_ARG_(arg_id, arg_name, 0); \ + CV_TRACE_NS::details::traceArg((CV__TRACE_ARG_VARNAME(arg_id)), value); + +#define CV__TRACE_ARG(arg_id) CV_TRACE_ARG_VALUE(arg_id, #arg_id, (arg_id)) + +} // namespace + +#ifndef OPENCV_DISABLE_TRACE +#undef CV_TRACE_FUNCTION +#undef CV_TRACE_FUNCTION_SKIP_NESTED +#if __OPENCV_TRACE +#define CV_TRACE_FUNCTION CV__TRACE_OPENCV_FUNCTION +#define CV_TRACE_FUNCTION_SKIP_NESTED CV__TRACE_OPENCV_FUNCTION_SKIP_NESTED +#else +#define CV_TRACE_FUNCTION CV__TRACE_APP_FUNCTION +#define CV_TRACE_FUNCTION_SKIP_NESTED CV__TRACE_APP_FUNCTION_SKIP_NESTED +#endif + +#undef CV_TRACE_REGION +#define CV_TRACE_REGION CV__TRACE_REGION + +#undef CV_TRACE_REGION_NEXT +#define CV_TRACE_REGION_NEXT CV__TRACE_REGION_NEXT + +#undef CV_TRACE_ARG_VALUE +#define CV_TRACE_ARG_VALUE(arg_id, arg_name, value) \ + if (__region_fn.isActive()) \ + { \ + CV__TRACE_ARG_VALUE(arg_id, arg_name, value); \ + } + +#undef CV_TRACE_ARG +#define CV_TRACE_ARG CV__TRACE_ARG + +#endif // OPENCV_DISABLE_TRACE + +#ifdef OPENCV_TRACE_VERBOSE +#define CV_TRACE_FUNCTION_VERBOSE CV_TRACE_FUNCTION +#define CV_TRACE_REGION_VERBOSE CV_TRACE_REGION +#define CV_TRACE_REGION_NEXT_VERBOSE CV_TRACE_REGION_NEXT +#define CV_TRACE_ARG_VALUE_VERBOSE CV_TRACE_ARG_VALUE +#define CV_TRACE_ARG_VERBOSE CV_TRACE_ARG +#else +#define CV_TRACE_FUNCTION_VERBOSE(...) +#define CV_TRACE_REGION_VERBOSE(...) +#define CV_TRACE_REGION_NEXT_VERBOSE(...) +#define CV_TRACE_ARG_VALUE_VERBOSE(...) +#define CV_TRACE_ARG_VERBOSE(...) +#endif + +//! @endcond + +//! @} + +}}} // namespace + +#endif // OPENCV_TRACE_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/va_intel.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/va_intel.hpp new file mode 100644 index 0000000..f665470 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/va_intel.hpp @@ -0,0 +1,78 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2015, Itseez, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_CORE_VA_INTEL_HPP +#define OPENCV_CORE_VA_INTEL_HPP + +#ifndef __cplusplus +# error va_intel.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "ocl.hpp" + +#if defined(HAVE_VA) +# include "va/va.h" +#else // HAVE_VA +# if !defined(_VA_H_) + typedef void* VADisplay; + typedef unsigned int VASurfaceID; +# endif // !_VA_H_ +#endif // HAVE_VA + +namespace cv { namespace va_intel { + +/** @addtogroup core_va_intel +This section describes Intel VA-API/OpenCL (CL-VA) interoperability. + +To enable CL-VA interoperability support, configure OpenCV using CMake with WITH_VA_INTEL=ON . Currently VA-API is +supported on Linux only. You should also install Intel Media Server Studio (MSS) to use this feature. You may +have to specify the path(s) to MSS components for cmake in environment variables: + +- VA_INTEL_IOCL_ROOT for Intel OpenCL (default is "/opt/intel/opencl"). + +To use CL-VA interoperability you should first create VADisplay (libva), and then call initializeContextFromVA() +function to create OpenCL context and set up interoperability. +*/ +//! @{ + +/////////////////// CL-VA Interoperability Functions /////////////////// + +namespace ocl { +using namespace cv::ocl; + +// TODO static functions in the Context class +/** @brief Creates OpenCL context from VA. +@param display - VADisplay for which CL interop should be established. +@param tryInterop - try to set up for interoperability, if true; set up for use slow copy if false. +@return Returns reference to OpenCL Context + */ +CV_EXPORTS Context& initializeContextFromVA(VADisplay display, bool tryInterop = true); + +} // namespace cv::va_intel::ocl + +/** @brief Converts InputArray to VASurfaceID object. +@param display - VADisplay object. +@param src - source InputArray. +@param surface - destination VASurfaceID object. +@param size - size of image represented by VASurfaceID object. + */ +CV_EXPORTS void convertToVASurface(VADisplay display, InputArray src, VASurfaceID surface, Size size); + +/** @brief Converts VASurfaceID object to OutputArray. +@param display - VADisplay object. +@param surface - source VASurfaceID object. +@param size - size of image represented by VASurfaceID object. +@param dst - destination OutputArray. + */ +CV_EXPORTS void convertFromVASurface(VADisplay display, VASurfaceID surface, Size size, OutputArray dst); + +//! @} + +}} // namespace cv::va_intel + +#endif /* OPENCV_CORE_VA_INTEL_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/version.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/version.hpp new file mode 100644 index 0000000..eb76dcd --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/version.hpp @@ -0,0 +1,26 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_VERSION_HPP +#define OPENCV_VERSION_HPP + +#define CV_VERSION_MAJOR 3 +#define CV_VERSION_MINOR 4 +#define CV_VERSION_REVISION 10 +#define CV_VERSION_STATUS "" + +#define CVAUX_STR_EXP(__A) #__A +#define CVAUX_STR(__A) CVAUX_STR_EXP(__A) + +#define CVAUX_STRW_EXP(__A) L ## #__A +#define CVAUX_STRW(__A) CVAUX_STRW_EXP(__A) + +#define CV_VERSION CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) "." CVAUX_STR(CV_VERSION_REVISION) CV_VERSION_STATUS + +/* old style version constants*/ +#define CV_MAJOR_VERSION CV_VERSION_MAJOR +#define CV_MINOR_VERSION CV_VERSION_MINOR +#define CV_SUBMINOR_VERSION CV_VERSION_REVISION + +#endif // OPENCV_VERSION_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/vsx_utils.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/vsx_utils.hpp new file mode 100644 index 0000000..08ae890 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/vsx_utils.hpp @@ -0,0 +1,1040 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_VSX_UTILS_HPP +#define OPENCV_HAL_VSX_UTILS_HPP + +#include "opencv2/core/cvdef.h" + +#ifndef SKIP_INCLUDES +# include +#endif + +//! @addtogroup core_utils_vsx +//! @{ +#if CV_VSX + +#define __VSX_S16__(c, v) (c){v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v} +#define __VSX_S8__(c, v) (c){v, v, v, v, v, v, v, v} +#define __VSX_S4__(c, v) (c){v, v, v, v} +#define __VSX_S2__(c, v) (c){v, v} + +typedef __vector unsigned char vec_uchar16; +#define vec_uchar16_set(...) (vec_uchar16){__VA_ARGS__} +#define vec_uchar16_sp(c) (__VSX_S16__(vec_uchar16, (unsigned char)c)) +#define vec_uchar16_c(v) ((vec_uchar16)(v)) +#define vec_uchar16_z vec_uchar16_sp(0) + +typedef __vector signed char vec_char16; +#define vec_char16_set(...) (vec_char16){__VA_ARGS__} +#define vec_char16_sp(c) (__VSX_S16__(vec_char16, (signed char)c)) +#define vec_char16_c(v) ((vec_char16)(v)) +#define vec_char16_z vec_char16_sp(0) + +typedef __vector unsigned short vec_ushort8; +#define vec_ushort8_set(...) (vec_ushort8){__VA_ARGS__} +#define vec_ushort8_sp(c) (__VSX_S8__(vec_ushort8, (unsigned short)c)) +#define vec_ushort8_c(v) ((vec_ushort8)(v)) +#define vec_ushort8_z vec_ushort8_sp(0) + +typedef __vector signed short vec_short8; +#define vec_short8_set(...) (vec_short8){__VA_ARGS__} +#define vec_short8_sp(c) (__VSX_S8__(vec_short8, (signed short)c)) +#define vec_short8_c(v) ((vec_short8)(v)) +#define vec_short8_z vec_short8_sp(0) + +typedef __vector unsigned int vec_uint4; +#define vec_uint4_set(...) (vec_uint4){__VA_ARGS__} +#define vec_uint4_sp(c) (__VSX_S4__(vec_uint4, (unsigned int)c)) +#define vec_uint4_c(v) ((vec_uint4)(v)) +#define vec_uint4_z vec_uint4_sp(0) + +typedef __vector signed int vec_int4; +#define vec_int4_set(...) (vec_int4){__VA_ARGS__} +#define vec_int4_sp(c) (__VSX_S4__(vec_int4, (signed int)c)) +#define vec_int4_c(v) ((vec_int4)(v)) +#define vec_int4_z vec_int4_sp(0) + +typedef __vector float vec_float4; +#define vec_float4_set(...) (vec_float4){__VA_ARGS__} +#define vec_float4_sp(c) (__VSX_S4__(vec_float4, c)) +#define vec_float4_c(v) ((vec_float4)(v)) +#define vec_float4_z vec_float4_sp(0) + +typedef __vector unsigned long long vec_udword2; +#define vec_udword2_set(...) (vec_udword2){__VA_ARGS__} +#define vec_udword2_sp(c) (__VSX_S2__(vec_udword2, (unsigned long long)c)) +#define vec_udword2_c(v) ((vec_udword2)(v)) +#define vec_udword2_z vec_udword2_sp(0) + +typedef __vector signed long long vec_dword2; +#define vec_dword2_set(...) (vec_dword2){__VA_ARGS__} +#define vec_dword2_sp(c) (__VSX_S2__(vec_dword2, (signed long long)c)) +#define vec_dword2_c(v) ((vec_dword2)(v)) +#define vec_dword2_z vec_dword2_sp(0) + +typedef __vector double vec_double2; +#define vec_double2_set(...) (vec_double2){__VA_ARGS__} +#define vec_double2_c(v) ((vec_double2)(v)) +#define vec_double2_sp(c) (__VSX_S2__(vec_double2, c)) +#define vec_double2_z vec_double2_sp(0) + +#define vec_bchar16 __vector __bool char +#define vec_bchar16_set(...) (vec_bchar16){__VA_ARGS__} +#define vec_bchar16_c(v) ((vec_bchar16)(v)) + +#define vec_bshort8 __vector __bool short +#define vec_bshort8_set(...) (vec_bshort8){__VA_ARGS__} +#define vec_bshort8_c(v) ((vec_bshort8)(v)) + +#define vec_bint4 __vector __bool int +#define vec_bint4_set(...) (vec_bint4){__VA_ARGS__} +#define vec_bint4_c(v) ((vec_bint4)(v)) + +#define vec_bdword2 __vector __bool long long +#define vec_bdword2_set(...) (vec_bdword2){__VA_ARGS__} +#define vec_bdword2_c(v) ((vec_bdword2)(v)) + +#define VSX_FINLINE(tp) extern inline tp __attribute__((always_inline)) + +#define VSX_REDIRECT_1RG(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fn2(a); } + +#define VSX_REDIRECT_2RG(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a, const rg& b) { return fn2(a, b); } + +/* + * GCC VSX compatibility +**/ +#if defined(__GNUG__) && !defined(__clang__) + +// inline asm helper +#define VSX_IMPL_1RG(rt, rg, opc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ rt rs; __asm__ __volatile__(#opc" %x0,%x1" : "=wa" (rs) : "wa" (a)); return rs; } + +#define VSX_IMPL_1VRG(rt, rg, opc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ rt rs; __asm__ __volatile__(#opc" %0,%1" : "=v" (rs) : "v" (a)); return rs; } + +#define VSX_IMPL_2VRG_F(rt, rg, fopc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a, const rg& b) \ +{ rt rs; __asm__ __volatile__(fopc : "=v" (rs) : "v" (a), "v" (b)); return rs; } + +#define VSX_IMPL_2VRG(rt, rg, opc, fnm) VSX_IMPL_2VRG_F(rt, rg, #opc" %0,%1,%2", fnm) + +#if __GNUG__ < 8 + + // Support for int4 -> dword2 expanding multiply was added in GCC 8. + #ifdef vec_mule + #undef vec_mule + #endif + #ifdef vec_mulo + #undef vec_mulo + #endif + + VSX_REDIRECT_2RG(vec_ushort8, vec_uchar16, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_short8, vec_char16, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_int4, vec_short8, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_uint4, vec_ushort8, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_ushort8, vec_uchar16, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_short8, vec_char16, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_int4, vec_short8, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_uint4, vec_ushort8, vec_mulo, __builtin_vec_mulo) + + // dword2 support arrived in ISA 2.07 and GCC 8+ + VSX_IMPL_2VRG(vec_dword2, vec_int4, vmulosw, vec_mule) + VSX_IMPL_2VRG(vec_udword2, vec_uint4, vmulouw, vec_mule) + VSX_IMPL_2VRG(vec_dword2, vec_int4, vmulesw, vec_mulo) + VSX_IMPL_2VRG(vec_udword2, vec_uint4, vmuleuw, vec_mulo) + +#endif + +#if __GNUG__ < 7 +// up to GCC 6 vec_mul only supports precisions and llong +# ifdef vec_mul +# undef vec_mul +# endif +/* + * there's no a direct instruction for supporting 8-bit, 16-bit multiplication in ISA 2.07, + * XLC Implement it by using instruction "multiply even", "multiply odd" and "permute" +**/ +# define VSX_IMPL_MULH(Tvec, cperm) \ + VSX_FINLINE(Tvec) vec_mul(const Tvec& a, const Tvec& b) \ + { \ + static const vec_uchar16 ev_od = {cperm}; \ + return vec_perm((Tvec)vec_mule(a, b), (Tvec)vec_mulo(a, b), ev_od); \ + } + #define VSX_IMPL_MULH_P16 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30 + VSX_IMPL_MULH(vec_char16, VSX_IMPL_MULH_P16) + VSX_IMPL_MULH(vec_uchar16, VSX_IMPL_MULH_P16) + #define VSX_IMPL_MULH_P8 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29 + VSX_IMPL_MULH(vec_short8, VSX_IMPL_MULH_P8) + VSX_IMPL_MULH(vec_ushort8, VSX_IMPL_MULH_P8) + // vmuluwm can be used for unsigned or signed integers, that's what they said + VSX_IMPL_2VRG(vec_int4, vec_int4, vmuluwm, vec_mul) + VSX_IMPL_2VRG(vec_uint4, vec_uint4, vmuluwm, vec_mul) + // redirect to GCC builtin vec_mul, since it already supports precisions and llong + VSX_REDIRECT_2RG(vec_float4, vec_float4, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mul, __builtin_vec_mul) +#endif // __GNUG__ < 7 + +#if __GNUG__ < 6 +/* + * Instruction "compare greater than or equal" in ISA 2.07 only supports single + * and double precision. + * In XLC and new versions of GCC implement integers by using instruction "greater than" and NOR. +**/ +# ifdef vec_cmpge +# undef vec_cmpge +# endif +# ifdef vec_cmple +# undef vec_cmple +# endif +# define vec_cmple(a, b) vec_cmpge(b, a) +# define VSX_IMPL_CMPGE(rt, rg, opc, fnm) \ + VSX_IMPL_2VRG_F(rt, rg, #opc" %0,%2,%1\n\t xxlnor %x0,%x0,%x0", fnm) + + VSX_IMPL_CMPGE(vec_bchar16, vec_char16, vcmpgtsb, vec_cmpge) + VSX_IMPL_CMPGE(vec_bchar16, vec_uchar16, vcmpgtub, vec_cmpge) + VSX_IMPL_CMPGE(vec_bshort8, vec_short8, vcmpgtsh, vec_cmpge) + VSX_IMPL_CMPGE(vec_bshort8, vec_ushort8, vcmpgtuh, vec_cmpge) + VSX_IMPL_CMPGE(vec_bint4, vec_int4, vcmpgtsw, vec_cmpge) + VSX_IMPL_CMPGE(vec_bint4, vec_uint4, vcmpgtuw, vec_cmpge) + VSX_IMPL_CMPGE(vec_bdword2, vec_dword2, vcmpgtsd, vec_cmpge) + VSX_IMPL_CMPGE(vec_bdword2, vec_udword2, vcmpgtud, vec_cmpge) + +// redirect to GCC builtin cmpge, since it already supports precisions + VSX_REDIRECT_2RG(vec_bint4, vec_float4, vec_cmpge, __builtin_vec_cmpge) + VSX_REDIRECT_2RG(vec_bdword2, vec_double2, vec_cmpge, __builtin_vec_cmpge) + +// up to gcc5 vec_nor doesn't support bool long long +# undef vec_nor + template + VSX_REDIRECT_2RG(T, T, vec_nor, __builtin_vec_nor) + + VSX_FINLINE(vec_bdword2) vec_nor(const vec_bdword2& a, const vec_bdword2& b) + { return vec_bdword2_c(__builtin_vec_nor(vec_dword2_c(a), vec_dword2_c(b))); } + +// vec_packs doesn't support double words in gcc4 and old versions of gcc5 +# undef vec_packs + VSX_REDIRECT_2RG(vec_char16, vec_short8, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_uchar16, vec_ushort8, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_short8, vec_int4, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_ushort8, vec_uint4, vec_packs, __builtin_vec_packs) + + VSX_IMPL_2VRG_F(vec_int4, vec_dword2, "vpksdss %0,%2,%1", vec_packs) + VSX_IMPL_2VRG_F(vec_uint4, vec_udword2, "vpkudus %0,%2,%1", vec_packs) +#endif // __GNUG__ < 6 + +#if __GNUG__ < 5 +// vec_xxpermdi in gcc4 missing little-endian supports just like clang +# define vec_permi(a, b, c) vec_xxpermdi(b, a, (3 ^ (((c) & 1) << 1 | (c) >> 1))) +// same as vec_xxpermdi +# undef vec_vbpermq + VSX_IMPL_2VRG(vec_udword2, vec_uchar16, vbpermq, vec_vbpermq) + VSX_IMPL_2VRG(vec_dword2, vec_char16, vbpermq, vec_vbpermq) +#else +# define vec_permi vec_xxpermdi +#endif // __GNUG__ < 5 + +// shift left double by word immediate +#ifndef vec_sldw +# define vec_sldw __builtin_vsx_xxsldwi +#endif + +// vector population count +VSX_IMPL_1VRG(vec_uchar16, vec_uchar16, vpopcntb, vec_popcntu) +VSX_IMPL_1VRG(vec_uchar16, vec_char16, vpopcntb, vec_popcntu) +VSX_IMPL_1VRG(vec_ushort8, vec_ushort8, vpopcnth, vec_popcntu) +VSX_IMPL_1VRG(vec_ushort8, vec_short8, vpopcnth, vec_popcntu) +VSX_IMPL_1VRG(vec_uint4, vec_uint4, vpopcntw, vec_popcntu) +VSX_IMPL_1VRG(vec_uint4, vec_int4, vpopcntw, vec_popcntu) +VSX_IMPL_1VRG(vec_udword2, vec_udword2, vpopcntd, vec_popcntu) +VSX_IMPL_1VRG(vec_udword2, vec_dword2, vpopcntd, vec_popcntu) + +// converts between single and double-precision +VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, __builtin_vsx_xvcvdpsp) +VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, __builtin_vsx_xvcvspdp) + +// converts word and doubleword to double-precision +#undef vec_ctd +VSX_IMPL_1RG(vec_double2, vec_int4, xvcvsxwdp, vec_ctdo) +VSX_IMPL_1RG(vec_double2, vec_uint4, xvcvuxwdp, vec_ctdo) +VSX_IMPL_1RG(vec_double2, vec_dword2, xvcvsxddp, vec_ctd) +VSX_IMPL_1RG(vec_double2, vec_udword2, xvcvuxddp, vec_ctd) + +// converts word and doubleword to single-precision +#undef vec_ctf +VSX_IMPL_1RG(vec_float4, vec_int4, xvcvsxwsp, vec_ctf) +VSX_IMPL_1RG(vec_float4, vec_uint4, xvcvuxwsp, vec_ctf) +VSX_IMPL_1RG(vec_float4, vec_dword2, xvcvsxdsp, vec_ctfo) +VSX_IMPL_1RG(vec_float4, vec_udword2, xvcvuxdsp, vec_ctfo) + +// converts single and double precision to signed word +#undef vec_cts +VSX_IMPL_1RG(vec_int4, vec_double2, xvcvdpsxws, vec_ctso) +VSX_IMPL_1RG(vec_int4, vec_float4, xvcvspsxws, vec_cts) + +// converts single and double precision to unsigned word +#undef vec_ctu +VSX_IMPL_1RG(vec_uint4, vec_double2, xvcvdpuxws, vec_ctuo) +VSX_IMPL_1RG(vec_uint4, vec_float4, xvcvspuxws, vec_ctu) + +// converts single and double precision to signed doubleword +#undef vec_ctsl +VSX_IMPL_1RG(vec_dword2, vec_double2, xvcvdpsxds, vec_ctsl) +VSX_IMPL_1RG(vec_dword2, vec_float4, xvcvspsxds, vec_ctslo) + +// converts single and double precision to unsigned doubleword +#undef vec_ctul +VSX_IMPL_1RG(vec_udword2, vec_double2, xvcvdpuxds, vec_ctul) +VSX_IMPL_1RG(vec_udword2, vec_float4, xvcvspuxds, vec_ctulo) + +// just in case if GCC doesn't define it +#ifndef vec_xl +# define vec_xl vec_vsx_ld +# define vec_xst vec_vsx_st +#endif + +#endif // GCC VSX compatibility + +/* + * CLANG VSX compatibility +**/ +#if defined(__clang__) && !defined(__IBMCPP__) + +/* + * CLANG doesn't support %x in the inline asm template which fixes register number + * when using any of the register constraints wa, wd, wf + * + * For more explanation checkout PowerPC and IBM RS6000 in https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html + * Also there's already an open bug https://bugs.llvm.org/show_bug.cgi?id=31837 + * + * So we're not able to use inline asm and only use built-in functions that CLANG supports + * and use __builtin_convertvector if clang missing any of vector conversions built-in functions + * + * todo: clang asm template bug is fixed, need to reconsider the current workarounds. +*/ + +// convert vector helper +#define VSX_IMPL_CONVERT(rt, rg, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) { return __builtin_convertvector(a, rt); } + +#if __clang_major__ < 5 +// implement vec_permi in a dirty way +# define VSX_IMPL_CLANG_4_PERMI(Tvec) \ + VSX_FINLINE(Tvec) vec_permi(const Tvec& a, const Tvec& b, unsigned const char c) \ + { \ + switch (c) \ + { \ + case 0: \ + return vec_mergeh(a, b); \ + case 1: \ + return vec_mergel(vec_mergeh(a, a), b); \ + case 2: \ + return vec_mergeh(vec_mergel(a, a), b); \ + default: \ + return vec_mergel(a, b); \ + } \ + } + VSX_IMPL_CLANG_4_PERMI(vec_udword2) + VSX_IMPL_CLANG_4_PERMI(vec_dword2) + VSX_IMPL_CLANG_4_PERMI(vec_double2) + +// vec_xxsldwi is missing in clang 4 +# define vec_xxsldwi(a, b, c) vec_sld(a, b, (c) * 4) +#else +// vec_xxpermdi is missing little-endian supports in clang 4 just like gcc4 +# define vec_permi(a, b, c) vec_xxpermdi(b, a, (3 ^ (((c) & 1) << 1 | (c) >> 1))) +#endif // __clang_major__ < 5 + +// shift left double by word immediate +#ifndef vec_sldw +# define vec_sldw vec_xxsldwi +#endif + +// Implement vec_rsqrt since clang only supports vec_rsqrte +#ifndef vec_rsqrt + VSX_FINLINE(vec_float4) vec_rsqrt(const vec_float4& a) + { return vec_div(vec_float4_sp(1), vec_sqrt(a)); } + + VSX_FINLINE(vec_double2) vec_rsqrt(const vec_double2& a) + { return vec_div(vec_double2_sp(1), vec_sqrt(a)); } +#endif + +// vec_promote missing support for doubleword +VSX_FINLINE(vec_dword2) vec_promote(long long a, int b) +{ + vec_dword2 ret = vec_dword2_z; + ret[b & 1] = a; + return ret; +} + +VSX_FINLINE(vec_udword2) vec_promote(unsigned long long a, int b) +{ + vec_udword2 ret = vec_udword2_z; + ret[b & 1] = a; + return ret; +} + +// vec_popcnt should return unsigned but clang has different thought just like gcc in vec_vpopcnt +#define VSX_IMPL_POPCNTU(Tvec, Tvec2, ucast) \ +VSX_FINLINE(Tvec) vec_popcntu(const Tvec2& a) \ +{ return ucast(vec_popcnt(a)); } +VSX_IMPL_POPCNTU(vec_uchar16, vec_char16, vec_uchar16_c); +VSX_IMPL_POPCNTU(vec_ushort8, vec_short8, vec_ushort8_c); +VSX_IMPL_POPCNTU(vec_uint4, vec_int4, vec_uint4_c); +VSX_IMPL_POPCNTU(vec_udword2, vec_dword2, vec_udword2_c); +// redirect unsigned types +VSX_REDIRECT_1RG(vec_uchar16, vec_uchar16, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_ushort8, vec_ushort8, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_uint4, vec_uint4, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_udword2, vec_udword2, vec_popcntu, vec_popcnt) + +// converts between single and double precision +VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, __builtin_vsx_xvcvdpsp) +VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, __builtin_vsx_xvcvspdp) + +// converts word and doubleword to double-precision +#ifdef vec_ctd +# undef vec_ctd +#endif +VSX_REDIRECT_1RG(vec_double2, vec_int4, vec_ctdo, __builtin_vsx_xvcvsxwdp) +VSX_REDIRECT_1RG(vec_double2, vec_uint4, vec_ctdo, __builtin_vsx_xvcvuxwdp) + +VSX_IMPL_CONVERT(vec_double2, vec_dword2, vec_ctd) +VSX_IMPL_CONVERT(vec_double2, vec_udword2, vec_ctd) + +// converts word and doubleword to single-precision +#if __clang_major__ > 4 +# undef vec_ctf +#endif +VSX_IMPL_CONVERT(vec_float4, vec_int4, vec_ctf) +VSX_IMPL_CONVERT(vec_float4, vec_uint4, vec_ctf) +VSX_REDIRECT_1RG(vec_float4, vec_dword2, vec_ctfo, __builtin_vsx_xvcvsxdsp) +VSX_REDIRECT_1RG(vec_float4, vec_udword2, vec_ctfo, __builtin_vsx_xvcvuxdsp) + +// converts single and double precision to signed word +#if __clang_major__ > 4 +# undef vec_cts +#endif +VSX_REDIRECT_1RG(vec_int4, vec_double2, vec_ctso, __builtin_vsx_xvcvdpsxws) +VSX_IMPL_CONVERT(vec_int4, vec_float4, vec_cts) + +// converts single and double precision to unsigned word +#if __clang_major__ > 4 +# undef vec_ctu +#endif +VSX_REDIRECT_1RG(vec_uint4, vec_double2, vec_ctuo, __builtin_vsx_xvcvdpuxws) +VSX_IMPL_CONVERT(vec_uint4, vec_float4, vec_ctu) + +// converts single and double precision to signed doubleword +#ifdef vec_ctsl +# undef vec_ctsl +#endif +VSX_IMPL_CONVERT(vec_dword2, vec_double2, vec_ctsl) +// __builtin_convertvector unable to convert, xvcvspsxds is missing on it +VSX_FINLINE(vec_dword2) vec_ctslo(const vec_float4& a) +{ return vec_ctsl(vec_cvfo(a)); } + +// converts single and double precision to unsigned doubleword +#ifdef vec_ctul +# undef vec_ctul +#endif +VSX_IMPL_CONVERT(vec_udword2, vec_double2, vec_ctul) +// __builtin_convertvector unable to convert, xvcvspuxds is missing on it +VSX_FINLINE(vec_udword2) vec_ctulo(const vec_float4& a) +{ return vec_ctul(vec_cvfo(a)); } + +#endif // CLANG VSX compatibility + +/* + * Common GCC, CLANG compatibility +**/ +#if defined(__GNUG__) && !defined(__IBMCPP__) + +#ifdef vec_cvf +# undef vec_cvf +#endif + +#define VSX_IMPL_CONV_EVEN_4_2(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ return fn2(vec_sldw(a, a, 1)); } + +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_float4, vec_cvf, vec_cvfo) +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_int4, vec_ctd, vec_ctdo) +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_uint4, vec_ctd, vec_ctdo) + +VSX_IMPL_CONV_EVEN_4_2(vec_dword2, vec_float4, vec_ctsl, vec_ctslo) +VSX_IMPL_CONV_EVEN_4_2(vec_udword2, vec_float4, vec_ctul, vec_ctulo) + +#define VSX_IMPL_CONV_EVEN_2_4(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ \ + rt v4 = fn2(a); \ + return vec_sldw(v4, v4, 3); \ +} + +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_double2, vec_cvf, vec_cvfo) +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_dword2, vec_ctf, vec_ctfo) +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_udword2, vec_ctf, vec_ctfo) + +VSX_IMPL_CONV_EVEN_2_4(vec_int4, vec_double2, vec_cts, vec_ctso) +VSX_IMPL_CONV_EVEN_2_4(vec_uint4, vec_double2, vec_ctu, vec_ctuo) + +// Only for Eigen! +/* + * changing behavior of conversion intrinsics for gcc has effect on Eigen + * so we redefine old behavior again only on gcc, clang +*/ +#if !defined(__clang__) || __clang_major__ > 4 + // ignoring second arg since Eigen only truncates toward zero +# define VSX_IMPL_CONV_2VARIANT(rt, rg, fnm, fn2) \ + VSX_FINLINE(rt) fnm(const rg& a, int only_truncate) \ + { \ + assert(only_truncate == 0); \ + CV_UNUSED(only_truncate); \ + return fn2(a); \ + } + VSX_IMPL_CONV_2VARIANT(vec_int4, vec_float4, vec_cts, vec_cts) + VSX_IMPL_CONV_2VARIANT(vec_float4, vec_int4, vec_ctf, vec_ctf) + // define vec_cts for converting double precision to signed doubleword + // which isn't combitable with xlc but its okay since Eigen only use it for gcc + VSX_IMPL_CONV_2VARIANT(vec_dword2, vec_double2, vec_cts, vec_ctsl) +#endif // Eigen + +#endif // Common GCC, CLANG compatibility + +/* + * XLC VSX compatibility +**/ +#if defined(__IBMCPP__) + +// vector population count +#define vec_popcntu vec_popcnt + +// overload and redirect with setting second arg to zero +// since we only support conversions without the second arg +#define VSX_IMPL_OVERLOAD_Z2(rt, rg, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fnm(a, 0); } + +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_int4, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_uint4, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_dword2, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_udword2, vec_ctd) + +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_int4, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_uint4, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_dword2, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_udword2, vec_ctf) + +VSX_IMPL_OVERLOAD_Z2(vec_int4, vec_double2, vec_cts) +VSX_IMPL_OVERLOAD_Z2(vec_int4, vec_float4, vec_cts) + +VSX_IMPL_OVERLOAD_Z2(vec_uint4, vec_double2, vec_ctu) +VSX_IMPL_OVERLOAD_Z2(vec_uint4, vec_float4, vec_ctu) + +VSX_IMPL_OVERLOAD_Z2(vec_dword2, vec_double2, vec_ctsl) +VSX_IMPL_OVERLOAD_Z2(vec_dword2, vec_float4, vec_ctsl) + +VSX_IMPL_OVERLOAD_Z2(vec_udword2, vec_double2, vec_ctul) +VSX_IMPL_OVERLOAD_Z2(vec_udword2, vec_float4, vec_ctul) + +// fixme: implement conversions of odd-numbered elements in a dirty way +// since xlc doesn't support VSX registers operand in inline asm. +#define VSX_IMPL_CONV_ODD_4_2(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fn2(vec_sldw(a, a, 3)); } + +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_float4, vec_cvfo, vec_cvf) +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_int4, vec_ctdo, vec_ctd) +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_uint4, vec_ctdo, vec_ctd) + +VSX_IMPL_CONV_ODD_4_2(vec_dword2, vec_float4, vec_ctslo, vec_ctsl) +VSX_IMPL_CONV_ODD_4_2(vec_udword2, vec_float4, vec_ctulo, vec_ctul) + +#define VSX_IMPL_CONV_ODD_2_4(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ \ + rt v4 = fn2(a); \ + return vec_sldw(v4, v4, 1); \ +} + +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_double2, vec_cvfo, vec_cvf) +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_dword2, vec_ctfo, vec_ctf) +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_udword2, vec_ctfo, vec_ctf) + +VSX_IMPL_CONV_ODD_2_4(vec_int4, vec_double2, vec_ctso, vec_cts) +VSX_IMPL_CONV_ODD_2_4(vec_uint4, vec_double2, vec_ctuo, vec_ctu) + +#endif // XLC VSX compatibility + +// ignore GCC warning that caused by -Wunused-but-set-variable in rare cases +#if defined(__GNUG__) && !defined(__clang__) +# define VSX_UNUSED(Tvec) Tvec __attribute__((__unused__)) +#else // CLANG, XLC +# define VSX_UNUSED(Tvec) Tvec +#endif + +// gcc can find his way in casting log int and XLC, CLANG ambiguous +#if defined(__clang__) || defined(__IBMCPP__) + VSX_FINLINE(vec_udword2) vec_splats(uint64 v) + { return vec_splats((unsigned long long) v); } + + VSX_FINLINE(vec_dword2) vec_splats(int64 v) + { return vec_splats((long long) v); } + + VSX_FINLINE(vec_udword2) vec_promote(uint64 a, int b) + { return vec_promote((unsigned long long) a, b); } + + VSX_FINLINE(vec_dword2) vec_promote(int64 a, int b) + { return vec_promote((long long) a, b); } +#endif + +/* + * implement vsx_ld(offset, pointer), vsx_st(vector, offset, pointer) + * load and set using offset depend on the pointer type + * + * implement vsx_ldf(offset, pointer), vsx_stf(vector, offset, pointer) + * load and set using offset depend on fixed bytes size + * + * Note: In clang vec_xl and vec_xst fails to load unaligned addresses + * so we are using vec_vsx_ld, vec_vsx_st instead +*/ + +#if defined(__clang__) && !defined(__IBMCPP__) +# define vsx_ldf vec_vsx_ld +# define vsx_stf vec_vsx_st +#else // GCC , XLC +# define vsx_ldf vec_xl +# define vsx_stf vec_xst +#endif + +#define VSX_OFFSET(o, p) ((o) * sizeof(*(p))) +#define vsx_ld(o, p) vsx_ldf(VSX_OFFSET(o, p), p) +#define vsx_st(v, o, p) vsx_stf(v, VSX_OFFSET(o, p), p) + +/* + * implement vsx_ld2(offset, pointer), vsx_st2(vector, offset, pointer) to load and store double words + * In GCC vec_xl and vec_xst it maps to vec_vsx_ld, vec_vsx_st which doesn't support long long + * and in CLANG we are using vec_vsx_ld, vec_vsx_st because vec_xl, vec_xst fails to load unaligned addresses + * + * In XLC vec_xl and vec_xst fail to cast int64(long int) to long long +*/ +#if (defined(__GNUG__) || defined(__clang__)) && !defined(__IBMCPP__) + VSX_FINLINE(vec_udword2) vsx_ld2(long o, const uint64* p) + { return vec_udword2_c(vsx_ldf(VSX_OFFSET(o, p), (unsigned int*)p)); } + + VSX_FINLINE(vec_dword2) vsx_ld2(long o, const int64* p) + { return vec_dword2_c(vsx_ldf(VSX_OFFSET(o, p), (int*)p)); } + + VSX_FINLINE(void) vsx_st2(const vec_udword2& vec, long o, uint64* p) + { vsx_stf(vec_uint4_c(vec), VSX_OFFSET(o, p), (unsigned int*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_dword2& vec, long o, int64* p) + { vsx_stf(vec_int4_c(vec), VSX_OFFSET(o, p), (int*)p); } +#else // XLC + VSX_FINLINE(vec_udword2) vsx_ld2(long o, const uint64* p) + { return vsx_ldf(VSX_OFFSET(o, p), (unsigned long long*)p); } + + VSX_FINLINE(vec_dword2) vsx_ld2(long o, const int64* p) + { return vsx_ldf(VSX_OFFSET(o, p), (long long*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_udword2& vec, long o, uint64* p) + { vsx_stf(vec, VSX_OFFSET(o, p), (unsigned long long*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_dword2& vec, long o, int64* p) + { vsx_stf(vec, VSX_OFFSET(o, p), (long long*)p); } +#endif + +// Store lower 8 byte +#define vec_st_l8(v, p) *((uint64*)(p)) = vec_extract(vec_udword2_c(v), 0) + +// Store higher 8 byte +#define vec_st_h8(v, p) *((uint64*)(p)) = vec_extract(vec_udword2_c(v), 1) + +// Load 64-bits of integer data to lower part +#define VSX_IMPL_LOAD_L8(Tvec, Tp) \ +VSX_FINLINE(Tvec) vec_ld_l8(const Tp *p) \ +{ return ((Tvec)vec_promote(*((uint64*)p), 0)); } + +VSX_IMPL_LOAD_L8(vec_uchar16, uchar) +VSX_IMPL_LOAD_L8(vec_char16, schar) +VSX_IMPL_LOAD_L8(vec_ushort8, ushort) +VSX_IMPL_LOAD_L8(vec_short8, short) +VSX_IMPL_LOAD_L8(vec_uint4, uint) +VSX_IMPL_LOAD_L8(vec_int4, int) +VSX_IMPL_LOAD_L8(vec_float4, float) +VSX_IMPL_LOAD_L8(vec_udword2, uint64) +VSX_IMPL_LOAD_L8(vec_dword2, int64) +VSX_IMPL_LOAD_L8(vec_double2, double) + +// logical not +#define vec_not(a) vec_nor(a, a) + +// power9 yaya +// not equal +#ifndef vec_cmpne +# define vec_cmpne(a, b) vec_not(vec_cmpeq(a, b)) +#endif + +// absolute difference +#ifndef vec_absd +# define vec_absd(a, b) vec_sub(vec_max(a, b), vec_min(a, b)) +#endif + +/* + * Implement vec_unpacklu and vec_unpackhu + * since vec_unpackl, vec_unpackh only support signed integers +**/ +#define VSX_IMPL_UNPACKU(rt, rg, zero) \ +VSX_FINLINE(rt) vec_unpacklu(const rg& a) \ +{ return (rt)(vec_mergel(a, zero)); } \ +VSX_FINLINE(rt) vec_unpackhu(const rg& a) \ +{ return (rt)(vec_mergeh(a, zero)); } + +VSX_IMPL_UNPACKU(vec_ushort8, vec_uchar16, vec_uchar16_z) +VSX_IMPL_UNPACKU(vec_uint4, vec_ushort8, vec_ushort8_z) +VSX_IMPL_UNPACKU(vec_udword2, vec_uint4, vec_uint4_z) + +/* + * Implement vec_mergesqe and vec_mergesqo + * Merges the sequence values of even and odd elements of two vectors +*/ +#define VSX_IMPL_PERM(rt, fnm, ...) \ +VSX_FINLINE(rt) fnm(const rt& a, const rt& b) \ +{ static const vec_uchar16 perm = {__VA_ARGS__}; return vec_perm(a, b, perm); } + +// 16 +#define perm16_mergesqe 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 +#define perm16_mergesqo 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 +VSX_IMPL_PERM(vec_uchar16, vec_mergesqe, perm16_mergesqe) +VSX_IMPL_PERM(vec_uchar16, vec_mergesqo, perm16_mergesqo) +VSX_IMPL_PERM(vec_char16, vec_mergesqe, perm16_mergesqe) +VSX_IMPL_PERM(vec_char16, vec_mergesqo, perm16_mergesqo) +// 8 +#define perm8_mergesqe 0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29 +#define perm8_mergesqo 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31 +VSX_IMPL_PERM(vec_ushort8, vec_mergesqe, perm8_mergesqe) +VSX_IMPL_PERM(vec_ushort8, vec_mergesqo, perm8_mergesqo) +VSX_IMPL_PERM(vec_short8, vec_mergesqe, perm8_mergesqe) +VSX_IMPL_PERM(vec_short8, vec_mergesqo, perm8_mergesqo) +// 4 +#define perm4_mergesqe 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 +#define perm4_mergesqo 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31 +VSX_IMPL_PERM(vec_uint4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_uint4, vec_mergesqo, perm4_mergesqo) +VSX_IMPL_PERM(vec_int4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_int4, vec_mergesqo, perm4_mergesqo) +VSX_IMPL_PERM(vec_float4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_float4, vec_mergesqo, perm4_mergesqo) +// 2 +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqo, vec_mergel) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqo, vec_mergel) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqo, vec_mergel) + +/* + * Implement vec_mergesqh and vec_mergesql + * Merges the sequence most and least significant halves of two vectors +*/ +#define VSX_IMPL_MERGESQHL(Tvec) \ +VSX_FINLINE(Tvec) vec_mergesqh(const Tvec& a, const Tvec& b) \ +{ return (Tvec)vec_mergeh(vec_udword2_c(a), vec_udword2_c(b)); } \ +VSX_FINLINE(Tvec) vec_mergesql(const Tvec& a, const Tvec& b) \ +{ return (Tvec)vec_mergel(vec_udword2_c(a), vec_udword2_c(b)); } +VSX_IMPL_MERGESQHL(vec_uchar16) +VSX_IMPL_MERGESQHL(vec_char16) +VSX_IMPL_MERGESQHL(vec_ushort8) +VSX_IMPL_MERGESQHL(vec_short8) +VSX_IMPL_MERGESQHL(vec_uint4) +VSX_IMPL_MERGESQHL(vec_int4) +VSX_IMPL_MERGESQHL(vec_float4) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesql, vec_mergel) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesql, vec_mergel) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesql, vec_mergel) + + +// 2 and 4 channels interleave for all types except 2 lanes +#define VSX_IMPL_ST_INTERLEAVE(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, Tp* ptr) \ +{ \ + vsx_stf(vec_mergeh(a, b), 0, ptr); \ + vsx_stf(vec_mergel(a, b), 16, ptr); \ +} \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, const Tvec& d, Tp* ptr) \ +{ \ + Tvec ac = vec_mergeh(a, c); \ + Tvec bd = vec_mergeh(b, d); \ + vsx_stf(vec_mergeh(ac, bd), 0, ptr); \ + vsx_stf(vec_mergel(ac, bd), 16, ptr); \ + ac = vec_mergel(a, c); \ + bd = vec_mergel(b, d); \ + vsx_stf(vec_mergeh(ac, bd), 32, ptr); \ + vsx_stf(vec_mergel(ac, bd), 48, ptr); \ +} +VSX_IMPL_ST_INTERLEAVE(uchar, vec_uchar16) +VSX_IMPL_ST_INTERLEAVE(schar, vec_char16) +VSX_IMPL_ST_INTERLEAVE(ushort, vec_ushort8) +VSX_IMPL_ST_INTERLEAVE(short, vec_short8) +VSX_IMPL_ST_INTERLEAVE(uint, vec_uint4) +VSX_IMPL_ST_INTERLEAVE(int, vec_int4) +VSX_IMPL_ST_INTERLEAVE(float, vec_float4) + +// 2 and 4 channels deinterleave for 16 lanes +#define VSX_IMPL_ST_DINTERLEAVE_8(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(16, ptr); \ + a = vec_mergesqe(v0, v1); \ + b = vec_mergesqo(v0, v1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(16, ptr); \ + Tvec v2 = vsx_ld(32, ptr); \ + Tvec v3 = vsx_ld(48, ptr); \ + Tvec m0 = vec_mergesqe(v0, v1); \ + Tvec m1 = vec_mergesqe(v2, v3); \ + a = vec_mergesqe(m0, m1); \ + c = vec_mergesqo(m0, m1); \ + m0 = vec_mergesqo(v0, v1); \ + m1 = vec_mergesqo(v2, v3); \ + b = vec_mergesqe(m0, m1); \ + d = vec_mergesqo(m0, m1); \ +} +VSX_IMPL_ST_DINTERLEAVE_8(uchar, vec_uchar16) +VSX_IMPL_ST_DINTERLEAVE_8(schar, vec_char16) + +// 2 and 4 channels deinterleave for 8 lanes +#define VSX_IMPL_ST_DINTERLEAVE_16(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(8, ptr); \ + a = vec_mergesqe(v0, v1); \ + b = vec_mergesqo(v0, v1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(8, ptr); \ + Tvec m0 = vec_mergeh(v0, v1); \ + Tvec m1 = vec_mergel(v0, v1); \ + Tvec ab0 = vec_mergeh(m0, m1); \ + Tvec cd0 = vec_mergel(m0, m1); \ + v0 = vsx_ld(16, ptr); \ + v1 = vsx_ld(24, ptr); \ + m0 = vec_mergeh(v0, v1); \ + m1 = vec_mergel(v0, v1); \ + Tvec ab1 = vec_mergeh(m0, m1); \ + Tvec cd1 = vec_mergel(m0, m1); \ + a = vec_mergesqh(ab0, ab1); \ + b = vec_mergesql(ab0, ab1); \ + c = vec_mergesqh(cd0, cd1); \ + d = vec_mergesql(cd0, cd1); \ +} +VSX_IMPL_ST_DINTERLEAVE_16(ushort, vec_ushort8) +VSX_IMPL_ST_DINTERLEAVE_16(short, vec_short8) + +// 2 and 4 channels deinterleave for 4 lanes +#define VSX_IMPL_ST_DINTERLEAVE_32(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + a = vsx_ld(0, ptr); \ + b = vsx_ld(4, ptr); \ + Tvec m0 = vec_mergeh(a, b); \ + Tvec m1 = vec_mergel(a, b); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(4, ptr); \ + Tvec v2 = vsx_ld(8, ptr); \ + Tvec v3 = vsx_ld(12, ptr); \ + Tvec m0 = vec_mergeh(v0, v2); \ + Tvec m1 = vec_mergeh(v1, v3); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ + m0 = vec_mergel(v0, v2); \ + m1 = vec_mergel(v1, v3); \ + c = vec_mergeh(m0, m1); \ + d = vec_mergel(m0, m1); \ +} +VSX_IMPL_ST_DINTERLEAVE_32(uint, vec_uint4) +VSX_IMPL_ST_DINTERLEAVE_32(int, vec_int4) +VSX_IMPL_ST_DINTERLEAVE_32(float, vec_float4) + +// 2 and 4 channels interleave and deinterleave for 2 lanes +#define VSX_IMPL_ST_D_INTERLEAVE_64(Tp, Tvec, ld_func, st_func) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_mergel(a, b), 2, ptr); \ +} \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, const Tvec& d, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_mergeh(c, d), 2, ptr); \ + st_func(vec_mergel(a, b), 4, ptr); \ + st_func(vec_mergel(c, d), 6, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec m0 = ld_func(0, ptr); \ + Tvec m1 = ld_func(2, ptr); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = ld_func(0, ptr); \ + Tvec v1 = ld_func(2, ptr); \ + Tvec v2 = ld_func(4, ptr); \ + Tvec v3 = ld_func(6, ptr); \ + a = vec_mergeh(v0, v2); \ + b = vec_mergel(v0, v2); \ + c = vec_mergeh(v1, v3); \ + d = vec_mergel(v1, v3); \ +} +VSX_IMPL_ST_D_INTERLEAVE_64(int64, vec_dword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_D_INTERLEAVE_64(uint64, vec_udword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_D_INTERLEAVE_64(double, vec_double2, vsx_ld, vsx_st) + +/* 3 channels */ +#define VSX_IMPL_ST_INTERLEAVE_3CH_16(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + static const vec_uchar16 a12 = {0, 16, 0, 1, 17, 0, 2, 18, 0, 3, 19, 0, 4, 20, 0, 5}; \ + static const vec_uchar16 a123 = {0, 1, 16, 3, 4, 17, 6, 7, 18, 9, 10, 19, 12, 13, 20, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, a12), c, a123), 0, ptr); \ + static const vec_uchar16 b12 = {21, 0, 6, 22, 0, 7, 23, 0, 8, 24, 0, 9, 25, 0, 10, 26}; \ + static const vec_uchar16 b123 = {0, 21, 2, 3, 22, 5, 6, 23, 8, 9, 24, 11, 12, 25, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, b12), c, b123), 16, ptr); \ + static const vec_uchar16 c12 = {0, 11, 27, 0, 12, 28, 0, 13, 29, 0, 14, 30, 0, 15, 31, 0}; \ + static const vec_uchar16 c123 = {26, 1, 2, 27, 4, 5, 28, 7, 8, 29, 10, 11, 30, 13, 14, 31}; \ + vsx_st(vec_perm(vec_perm(a, b, c12), c, c123), 32, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(16, ptr); \ + Tvec v3 = vsx_ld(32, ptr); \ + static const vec_uchar16 a12_perm = {0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 a123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 17, 20, 23, 26, 29}; \ + a = vec_perm(vec_perm(v1, v2, a12_perm), v3, a123_perm); \ + static const vec_uchar16 b12_perm = {1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 b123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 21, 24, 27, 30}; \ + b = vec_perm(vec_perm(v1, v2, b12_perm), v3, b123_perm); \ + static const vec_uchar16 c12_perm = {2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 c123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 19, 22, 25, 28, 31}; \ + c = vec_perm(vec_perm(v1, v2, c12_perm), v3, c123_perm); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_16(uchar, vec_uchar16) +VSX_IMPL_ST_INTERLEAVE_3CH_16(schar, vec_char16) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_8(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + static const vec_uchar16 a12 = {0, 1, 16, 17, 0, 0, 2, 3, 18, 19, 0, 0, 4, 5, 20, 21}; \ + static const vec_uchar16 a123 = {0, 1, 2, 3, 16, 17, 6, 7, 8, 9, 18, 19, 12, 13, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, a12), c, a123), 0, ptr); \ + static const vec_uchar16 b12 = {0, 0, 6, 7, 22, 23, 0, 0, 8, 9, 24, 25, 0, 0, 10, 11}; \ + static const vec_uchar16 b123 = {20, 21, 2, 3, 4, 5, 22, 23, 8, 9, 10, 11, 24, 25, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, b12), c, b123), 8, ptr); \ + static const vec_uchar16 c12 = {26, 27, 0, 0, 12, 13, 28, 29, 0, 0, 14, 15, 30, 31, 0, 0}; \ + static const vec_uchar16 c123 = {0, 1, 26, 27, 4, 5, 6, 7, 28, 29, 10, 11, 12, 13, 30, 31}; \ + vsx_st(vec_perm(vec_perm(a, b, c12), c, c123), 16, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(8, ptr); \ + Tvec v3 = vsx_ld(16, ptr); \ + static const vec_uchar16 a12_perm = {0, 1, 6, 7, 12, 13, 18, 19, 24, 25, 30, 31, 0, 0, 0, 0}; \ + static const vec_uchar16 a123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 21, 26, 27}; \ + a = vec_perm(vec_perm(v1, v2, a12_perm), v3, a123_perm); \ + static const vec_uchar16 b12_perm = {2, 3, 8, 9, 14, 15, 20, 21, 26, 27, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 b123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 22, 23, 28, 29}; \ + b = vec_perm(vec_perm(v1, v2, b12_perm), v3, b123_perm); \ + static const vec_uchar16 c12_perm = {4, 5, 10, 11, 16, 17, 22, 23, 28, 29, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 c123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 18, 19, 24, 25, 30, 31}; \ + c = vec_perm(vec_perm(v1, v2, c12_perm), v3, c123_perm); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_8(ushort, vec_ushort8) +VSX_IMPL_ST_INTERLEAVE_3CH_8(short, vec_short8) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_4(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + Tvec hbc = vec_mergeh(b, c); \ + static const vec_uchar16 ahbc = {0, 1, 2, 3, 16, 17, 18, 19, 20, 21, 22, 23, 4, 5, 6, 7}; \ + vsx_st(vec_perm(a, hbc, ahbc), 0, ptr); \ + Tvec lab = vec_mergel(a, b); \ + vsx_st(vec_sld(lab, hbc, 8), 4, ptr); \ + static const vec_uchar16 clab = {8, 9, 10, 11, 24, 25, 26, 27, 28, 29, 30, 31, 12, 13, 14, 15};\ + vsx_st(vec_perm(c, lab, clab), 8, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(4, ptr); \ + Tvec v3 = vsx_ld(8, ptr); \ + static const vec_uchar16 flp = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 28, 29, 30, 31}; \ + a = vec_perm(v1, vec_sld(v3, v2, 8), flp); \ + static const vec_uchar16 flp2 = {28, 29, 30, 31, 0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19}; \ + b = vec_perm(v2, vec_sld(v1, v3, 8), flp2); \ + c = vec_perm(vec_sld(v2, v1, 8), v3, flp); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_4(uint, vec_uint4) +VSX_IMPL_ST_INTERLEAVE_3CH_4(int, vec_int4) +VSX_IMPL_ST_INTERLEAVE_3CH_4(float, vec_float4) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_2(Tp, Tvec, ld_func, st_func) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_permi(c, a, 1), 2, ptr); \ + st_func(vec_mergel(b, c), 4, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, \ + Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = ld_func(0, ptr); \ + Tvec v2 = ld_func(2, ptr); \ + Tvec v3 = ld_func(4, ptr); \ + a = vec_permi(v1, v2, 1); \ + b = vec_permi(v1, v3, 2); \ + c = vec_permi(v2, v3, 1); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_2(int64, vec_dword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_INTERLEAVE_3CH_2(uint64, vec_udword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_INTERLEAVE_3CH_2(double, vec_double2, vsx_ld, vsx_st) + +#endif // CV_VSX + +//! @} + +#endif // OPENCV_HAL_VSX_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/core/wimage.hpp b/hgdriver/3rdparty/opencv/include/opencv2/core/wimage.hpp new file mode 100644 index 0000000..c7b6efa --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/core/wimage.hpp @@ -0,0 +1,603 @@ +/*M////////////////////////////////////////////////////////////////////////////// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to +// this license. If you do not agree to this license, do not download, +// install, copy or use the software. +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2008, Google, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation or contributors may not be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +///////////////////////////////////////////////////////////////////////////////// +//M*/ + +#ifndef OPENCV_CORE_WIMAGE_HPP +#define OPENCV_CORE_WIMAGE_HPP + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus + +namespace cv { + +//! @addtogroup core +//! @{ + +template class WImage; +template class WImageBuffer; +template class WImageView; + +template class WImageC; +template class WImageBufferC; +template class WImageViewC; + +// Commonly used typedefs. +typedef WImage WImage_b; +typedef WImageView WImageView_b; +typedef WImageBuffer WImageBuffer_b; + +typedef WImageC WImage1_b; +typedef WImageViewC WImageView1_b; +typedef WImageBufferC WImageBuffer1_b; + +typedef WImageC WImage3_b; +typedef WImageViewC WImageView3_b; +typedef WImageBufferC WImageBuffer3_b; + +typedef WImage WImage_f; +typedef WImageView WImageView_f; +typedef WImageBuffer WImageBuffer_f; + +typedef WImageC WImage1_f; +typedef WImageViewC WImageView1_f; +typedef WImageBufferC WImageBuffer1_f; + +typedef WImageC WImage3_f; +typedef WImageViewC WImageView3_f; +typedef WImageBufferC WImageBuffer3_f; + +// There isn't a standard for signed and unsigned short so be more +// explicit in the typename for these cases. +typedef WImage WImage_16s; +typedef WImageView WImageView_16s; +typedef WImageBuffer WImageBuffer_16s; + +typedef WImageC WImage1_16s; +typedef WImageViewC WImageView1_16s; +typedef WImageBufferC WImageBuffer1_16s; + +typedef WImageC WImage3_16s; +typedef WImageViewC WImageView3_16s; +typedef WImageBufferC WImageBuffer3_16s; + +typedef WImage WImage_16u; +typedef WImageView WImageView_16u; +typedef WImageBuffer WImageBuffer_16u; + +typedef WImageC WImage1_16u; +typedef WImageViewC WImageView1_16u; +typedef WImageBufferC WImageBuffer1_16u; + +typedef WImageC WImage3_16u; +typedef WImageViewC WImageView3_16u; +typedef WImageBufferC WImageBuffer3_16u; + +/** @brief Image class which provides a thin layer around an IplImage. + +The goals of the class design are: + + -# All the data has explicit ownership to avoid memory leaks + -# No hidden allocations or copies for performance. + -# Easy access to OpenCV methods (which will access IPP if available) + -# Can easily treat external data as an image + -# Easy to create images which are subsets of other images + -# Fast pixel access which can take advantage of number of channels if known at compile time. + +The WImage class is the image class which provides the data accessors. The 'W' comes from the fact +that it is also a wrapper around the popular but inconvenient IplImage class. A WImage can be +constructed either using a WImageBuffer class which allocates and frees the data, or using a +WImageView class which constructs a subimage or a view into external data. The view class does no +memory management. Each class actually has two versions, one when the number of channels is known +at compile time and one when it isn't. Using the one with the number of channels specified can +provide some compile time optimizations by using the fact that the number of channels is a +constant. + +We use the convention (c,r) to refer to column c and row r with (0,0) being the upper left corner. +This is similar to standard Euclidean coordinates with the first coordinate varying in the +horizontal direction and the second coordinate varying in the vertical direction. Thus (c,r) is +usually in the domain [0, width) X [0, height) + +Example usage: +@code +WImageBuffer3_b im(5,7); // Make a 5X7 3 channel image of type uchar +WImageView3_b sub_im(im, 2,2, 3,3); // 3X3 submatrix +vector vec(10, 3.0f); +WImageView1_f user_im(&vec[0], 2, 5); // 2X5 image w/ supplied data + +im.SetZero(); // same as cvSetZero(im.Ipl()) +*im(2, 3) = 15; // Modify the element at column 2, row 3 +MySetRand(&sub_im); + +// Copy the second row into the first. This can be done with no memory +// allocation and will use SSE if IPP is available. +int w = im.Width(); +im.View(0,0, w,1).CopyFrom(im.View(0,1, w,1)); + +// Doesn't care about source of data since using WImage +void MySetRand(WImage_b* im) { // Works with any number of channels +for (int r = 0; r < im->Height(); ++r) { + float* row = im->Row(r); + for (int c = 0; c < im->Width(); ++c) { + for (int ch = 0; ch < im->Channels(); ++ch, ++row) { + *row = uchar(rand() & 255); + } + } +} +} +@endcode + +Functions that are not part of the basic image allocation, viewing, and access should come from +OpenCV, except some useful functions that are not part of OpenCV can be found in wimage_util.h +*/ +template +class WImage +{ +public: + typedef T BaseType; + + // WImage is an abstract class with no other virtual methods so make the + // destructor virtual. + virtual ~WImage() = 0; + + // Accessors + IplImage* Ipl() {return image_; } + const IplImage* Ipl() const {return image_; } + T* ImageData() { return reinterpret_cast(image_->imageData); } + const T* ImageData() const { + return reinterpret_cast(image_->imageData); + } + + int Width() const {return image_->width; } + int Height() const {return image_->height; } + + // WidthStep is the number of bytes to go to the pixel with the next y coord + int WidthStep() const {return image_->widthStep; } + + int Channels() const {return image_->nChannels; } + int ChannelSize() const {return sizeof(T); } // number of bytes per channel + + // Number of bytes per pixel + int PixelSize() const {return Channels() * ChannelSize(); } + + // Return depth type (e.g. IPL_DEPTH_8U, IPL_DEPTH_32F) which is the number + // of bits per channel and with the signed bit set. + // This is known at compile time using specializations. + int Depth() const; + + inline const T* Row(int r) const { + return reinterpret_cast(image_->imageData + r*image_->widthStep); + } + + inline T* Row(int r) { + return reinterpret_cast(image_->imageData + r*image_->widthStep); + } + + // Pixel accessors which returns a pointer to the start of the channel + inline T* operator() (int c, int r) { + return reinterpret_cast(image_->imageData + r*image_->widthStep) + + c*Channels(); + } + + inline const T* operator() (int c, int r) const { + return reinterpret_cast(image_->imageData + r*image_->widthStep) + + c*Channels(); + } + + // Copy the contents from another image which is just a convenience to cvCopy + void CopyFrom(const WImage& src) { cvCopy(src.Ipl(), image_); } + + // Set contents to zero which is just a convenient to cvSetZero + void SetZero() { cvSetZero(image_); } + + // Construct a view into a region of this image + WImageView View(int c, int r, int width, int height); + +protected: + // Disallow copy and assignment + WImage(const WImage&); + void operator=(const WImage&); + + explicit WImage(IplImage* img) : image_(img) { + assert(!img || img->depth == Depth()); + } + + void SetIpl(IplImage* image) { + assert(!image || image->depth == Depth()); + image_ = image; + } + + IplImage* image_; +}; + + +/** Image class when both the pixel type and number of channels +are known at compile time. This wrapper will speed up some of the operations +like accessing individual pixels using the () operator. +*/ +template +class WImageC : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + explicit WImageC(IplImage* img) : WImage(img) { + assert(!img || img->nChannels == Channels()); + } + + // Construct a view into a region of this image + WImageViewC View(int c, int r, int width, int height); + + // Copy the contents from another image which is just a convenience to cvCopy + void CopyFrom(const WImageC& src) { + cvCopy(src.Ipl(), WImage::image_); + } + + // WImageC is an abstract class with no other virtual methods so make the + // destructor virtual. + virtual ~WImageC() = 0; + + int Channels() const {return C; } + +protected: + // Disallow copy and assignment + WImageC(const WImageC&); + void operator=(const WImageC&); + + void SetIpl(IplImage* image) { + assert(!image || image->depth == WImage::Depth()); + WImage::SetIpl(image); + } +}; + +/** Image class which owns the data, so it can be allocated and is always +freed. It cannot be copied but can be explicitly cloned. +*/ +template +class WImageBuffer : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + + // Default constructor which creates an object that can be + WImageBuffer() : WImage(0) {} + + WImageBuffer(int width, int height, int nchannels) : WImage(0) { + Allocate(width, height, nchannels); + } + + // Constructor which takes ownership of a given IplImage so releases + // the image on destruction. + explicit WImageBuffer(IplImage* img) : WImage(img) {} + + // Allocate an image. Does nothing if current size is the same as + // the new size. + void Allocate(int width, int height, int nchannels); + + // Set the data to point to an image, releasing the old data + void SetIpl(IplImage* img) { + ReleaseImage(); + WImage::SetIpl(img); + } + + // Clone an image which reallocates the image if of a different dimension. + void CloneFrom(const WImage& src) { + Allocate(src.Width(), src.Height(), src.Channels()); + CopyFrom(src); + } + + ~WImageBuffer() { + ReleaseImage(); + } + + // Release the image if it isn't null. + void ReleaseImage() { + if (WImage::image_) { + IplImage* image = WImage::image_; + cvReleaseImage(&image); + WImage::SetIpl(0); + } + } + + bool IsNull() const {return WImage::image_ == NULL; } + +private: + // Disallow copy and assignment + WImageBuffer(const WImageBuffer&); + void operator=(const WImageBuffer&); +}; + +/** Like a WImageBuffer class but when the number of channels is known at compile time. +*/ +template +class WImageBufferC : public WImageC +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + // Default constructor which creates an object that can be + WImageBufferC() : WImageC(0) {} + + WImageBufferC(int width, int height) : WImageC(0) { + Allocate(width, height); + } + + // Constructor which takes ownership of a given IplImage so releases + // the image on destruction. + explicit WImageBufferC(IplImage* img) : WImageC(img) {} + + // Allocate an image. Does nothing if current size is the same as + // the new size. + void Allocate(int width, int height); + + // Set the data to point to an image, releasing the old data + void SetIpl(IplImage* img) { + ReleaseImage(); + WImageC::SetIpl(img); + } + + // Clone an image which reallocates the image if of a different dimension. + void CloneFrom(const WImageC& src) { + Allocate(src.Width(), src.Height()); + CopyFrom(src); + } + + ~WImageBufferC() { + ReleaseImage(); + } + + // Release the image if it isn't null. + void ReleaseImage() { + if (WImage::image_) { + IplImage* image = WImage::image_; + cvReleaseImage(&image); + WImageC::SetIpl(0); + } + } + + bool IsNull() const {return WImage::image_ == NULL; } + +private: + // Disallow copy and assignment + WImageBufferC(const WImageBufferC&); + void operator=(const WImageBufferC&); +}; + +/** View into an image class which allows treating a subimage as an image or treating external data +as an image +*/ +template class WImageView : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + + // Construct a subimage. No checks are done that the subimage lies + // completely inside the original image. + WImageView(WImage* img, int c, int r, int width, int height); + + // Refer to external data. + // If not given width_step assumed to be same as width. + WImageView(T* data, int width, int height, int channels, int width_step = -1); + + // Refer to external data. This does NOT take ownership + // of the supplied IplImage. + WImageView(IplImage* img) : WImage(img) {} + + // Copy constructor + WImageView(const WImage& img) : WImage(0) { + header_ = *(img.Ipl()); + WImage::SetIpl(&header_); + } + + WImageView& operator=(const WImage& img) { + header_ = *(img.Ipl()); + WImage::SetIpl(&header_); + return *this; + } + +protected: + IplImage header_; +}; + + +template +class WImageViewC : public WImageC +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + // Default constructor needed for vectors of views. + WImageViewC(); + + virtual ~WImageViewC() {} + + // Construct a subimage. No checks are done that the subimage lies + // completely inside the original image. + WImageViewC(WImageC* img, + int c, int r, int width, int height); + + // Refer to external data + WImageViewC(T* data, int width, int height, int width_step = -1); + + // Refer to external data. This does NOT take ownership + // of the supplied IplImage. + WImageViewC(IplImage* img) : WImageC(img) {} + + // Copy constructor which does a shallow copy to allow multiple views + // of same data. gcc-4.1.1 gets confused if both versions of + // the constructor and assignment operator are not provided. + WImageViewC(const WImageC& img) : WImageC(0) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + } + WImageViewC(const WImageViewC& img) : WImageC(0) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + } + + WImageViewC& operator=(const WImageC& img) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + return *this; + } + WImageViewC& operator=(const WImageViewC& img) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + return *this; + } + +protected: + IplImage header_; +}; + + +// Specializations for depth +template<> +inline int WImage::Depth() const {return IPL_DEPTH_8U; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_8S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_16S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_16U; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_32S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_32F; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_64F; } + +template inline WImage::~WImage() {} +template inline WImageC::~WImageC() {} + +template +inline void WImageBuffer::Allocate(int width, int height, int nchannels) +{ + if (IsNull() || WImage::Width() != width || + WImage::Height() != height || WImage::Channels() != nchannels) { + ReleaseImage(); + WImage::image_ = cvCreateImage(cvSize(width, height), + WImage::Depth(), nchannels); + } +} + +template +inline void WImageBufferC::Allocate(int width, int height) +{ + if (IsNull() || WImage::Width() != width || WImage::Height() != height) { + ReleaseImage(); + WImageC::SetIpl(cvCreateImage(cvSize(width, height),WImage::Depth(), C)); + } +} + +template +WImageView::WImageView(WImage* img, int c, int r, int width, int height) + : WImage(0) +{ + header_ = *(img->Ipl()); + header_.imageData = reinterpret_cast((*img)(c, r)); + header_.width = width; + header_.height = height; + WImage::SetIpl(&header_); +} + +template +WImageView::WImageView(T* data, int width, int height, int nchannels, int width_step) + : WImage(0) +{ + cvInitImageHeader(&header_, cvSize(width, height), WImage::Depth(), nchannels); + header_.imageData = reinterpret_cast(data); + if (width_step > 0) { + header_.widthStep = width_step; + } + WImage::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC(WImageC* img, int c, int r, int width, int height) + : WImageC(0) +{ + header_ = *(img->Ipl()); + header_.imageData = reinterpret_cast((*img)(c, r)); + header_.width = width; + header_.height = height; + WImageC::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC() : WImageC(0) { + cvInitImageHeader(&header_, cvSize(0, 0), WImage::Depth(), C); + header_.imageData = reinterpret_cast(0); + WImageC::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC(T* data, int width, int height, int width_step) + : WImageC(0) +{ + cvInitImageHeader(&header_, cvSize(width, height), WImage::Depth(), C); + header_.imageData = reinterpret_cast(data); + if (width_step > 0) { + header_.widthStep = width_step; + } + WImageC::SetIpl(&header_); +} + +// Construct a view into a region of an image +template +WImageView WImage::View(int c, int r, int width, int height) { + return WImageView(this, c, r, width, height); +} + +template +WImageViewC WImageC::View(int c, int r, int width, int height) { + return WImageViewC(this, c, r, width, height); +} + +//! @} core + +} // end of namespace + +#endif // __cplusplus + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/cvconfig.h b/hgdriver/3rdparty/opencv/include/opencv2/cvconfig.h new file mode 100644 index 0000000..a6a1e6e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/cvconfig.h @@ -0,0 +1,251 @@ +#ifndef OPENCV_CVCONFIG_H_INCLUDED +#define OPENCV_CVCONFIG_H_INCLUDED + +/* OpenCV compiled as static or dynamic libs */ +/* #undef BUILD_SHARED_LIBS */ + +/* OpenCV intrinsics optimized code */ +#define CV_ENABLE_INTRINSICS + +/* OpenCV additional optimized code */ +/* #undef CV_DISABLE_OPTIMIZATION */ + +/* Compile for 'real' NVIDIA GPU architectures */ +#define CUDA_ARCH_BIN "" + +/* Create PTX or BIN for 1.0 compute capability */ +/* #undef CUDA_ARCH_BIN_OR_PTX_10 */ + +/* NVIDIA GPU features are used */ +#define CUDA_ARCH_FEATURES "" + +/* Compile for 'virtual' NVIDIA PTX architectures */ +#define CUDA_ARCH_PTX "" + +/* AVFoundation video libraries */ +/* #undef HAVE_AVFOUNDATION */ + +/* V4L capturing support */ +/* #undef HAVE_CAMV4L */ + +/* V4L2 capturing support */ +/* #undef HAVE_CAMV4L2 */ + +/* Carbon windowing environment */ +/* #undef HAVE_CARBON */ + +/* AMD's Basic Linear Algebra Subprograms Library*/ +/* #undef HAVE_CLAMDBLAS */ + +/* AMD's OpenCL Fast Fourier Transform Library*/ +/* #undef HAVE_CLAMDFFT */ + +/* Clp support */ +/* #undef HAVE_CLP */ + +/* Cocoa API */ +/* #undef HAVE_COCOA */ + +/* C= */ +/* #undef HAVE_CSTRIPES */ + +/* NVIDIA CUDA Basic Linear Algebra Subprograms (BLAS) API*/ +/* #undef HAVE_CUBLAS */ + +/* NVIDIA CUDA Runtime API*/ +/* #undef HAVE_CUDA */ + +/* NVIDIA CUDA Fast Fourier Transform (FFT) API*/ +/* #undef HAVE_CUFFT */ + +/* IEEE1394 capturing support */ +/* #undef HAVE_DC1394 */ + +/* IEEE1394 capturing support - libdc1394 v2.x */ +/* #undef HAVE_DC1394_2 */ + +/* DirectX */ +/* #undef HAVE_DIRECTX */ +/* #undef HAVE_DIRECTX_NV12 */ +/* #undef HAVE_D3D11 */ +/* #undef HAVE_D3D10 */ +/* #undef HAVE_D3D9 */ + +/* DirectShow Video Capture library */ +/* #undef HAVE_DSHOW */ + +/* Eigen Matrix & Linear Algebra Library */ +/* #undef HAVE_EIGEN */ + +/* FFMpeg video library */ +/* #undef HAVE_FFMPEG */ + +/* Geospatial Data Abstraction Library */ +/* #undef HAVE_GDAL */ + +/* GStreamer multimedia framework */ +/* #undef HAVE_GSTREAMER */ + +/* GTK+ 2.0 Thread support */ +#define HAVE_GTHREAD + +/* GTK+ 2.x toolkit */ +/* #undef HAVE_GTK */ + +/* Halide support */ +/* #undef HAVE_HALIDE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Intel Perceptual Computing SDK library */ +/* #undef HAVE_INTELPERC */ + +/* Intel Integrated Performance Primitives */ +/* #undef HAVE_IPP */ +/* #undef HAVE_IPP_ICV */ +/* #undef HAVE_IPP_IW */ +/* #undef HAVE_IPP_IW_LL */ + +/* JPEG-2000 codec */ +#define HAVE_JASPER + +/* IJG JPEG codec */ +#define HAVE_JPEG + +/* libpng/png.h needs to be included */ +/* #undef HAVE_LIBPNG_PNG_H */ + +/* GDCM DICOM codec */ +/* #undef HAVE_GDCM */ + +/* V4L/V4L2 capturing support via libv4l */ +/* #undef HAVE_LIBV4L */ + +/* Microsoft Media Foundation Capture library */ +/* #undef HAVE_MSMF */ + +/* NVIDIA Video Decoding API*/ +/* #undef HAVE_NVCUVID */ + +/* NVIDIA Video Encoding API*/ +/* #undef HAVE_NVCUVENC */ + +/* OpenCL Support */ +/* #undef HAVE_OPENCL */ +/* #undef HAVE_OPENCL_STATIC */ +/* #undef HAVE_OPENCL_SVM */ + +/* NVIDIA OpenCL D3D Extensions support */ +/* #undef HAVE_OPENCL_D3D11_NV */ + +/* OpenEXR codec */ +/* #undef HAVE_OPENEXR */ + +/* OpenGL support*/ +/* #undef HAVE_OPENGL */ + +/* OpenNI library */ +/* #undef HAVE_OPENNI */ + +/* OpenNI library */ +/* #undef HAVE_OPENNI2 */ + +/* PNG codec */ +#define HAVE_PNG + +/* Posix threads (pthreads) */ +#define HAVE_PTHREAD + +/* parallel_for with pthreads */ +#define HAVE_PTHREADS_PF + +/* Qt support */ +/* #undef HAVE_QT */ + +/* Qt OpenGL support */ +/* #undef HAVE_QT_OPENGL */ + +/* QuickTime video libraries */ +/* #undef HAVE_QUICKTIME */ + +/* QTKit video libraries */ +/* #undef HAVE_QTKIT */ + +/* Intel Threading Building Blocks */ +#define HAVE_TBB + +/* TIFF codec */ +#define HAVE_TIFF + +/* Unicap video capture library */ +/* #undef HAVE_UNICAP */ + +/* Video for Windows support */ +/* #undef HAVE_VFW */ + +/* V4L2 capturing support in videoio.h */ +/* #undef HAVE_VIDEOIO */ + +/* Win32 UI */ +/* #undef HAVE_WIN32UI */ + +/* XIMEA camera support */ +/* #undef HAVE_XIMEA */ + +/* Xine video library */ +/* #undef HAVE_XINE */ + +/* Define if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* gPhoto2 library */ +/* #undef HAVE_GPHOTO2 */ + +/* VA library (libva) */ +/* #undef HAVE_VA */ + +/* Intel VA-API/OpenCL */ +/* #undef HAVE_VA_INTEL */ + +/* Intel Media SDK */ +/* #undef HAVE_MFX */ + +/* Lapack */ +/* #undef HAVE_LAPACK */ + +/* Library was compiled with functions instrumentation */ +/* #undef ENABLE_INSTRUMENTATION */ + +/* OpenVX */ +/* #undef HAVE_OPENVX */ + +#if defined(HAVE_XINE) || \ + defined(HAVE_GSTREAMER) || \ + defined(HAVE_QUICKTIME) || \ + defined(HAVE_QTKIT) || \ + defined(HAVE_AVFOUNDATION) || \ + /*defined(HAVE_OPENNI) || too specialized */ \ + defined(HAVE_FFMPEG) || \ + defined(HAVE_MSMF) +#define HAVE_VIDEO_INPUT +#endif + +#if /*defined(HAVE_XINE) || */\ + defined(HAVE_GSTREAMER) || \ + defined(HAVE_QUICKTIME) || \ + defined(HAVE_QTKIT) || \ + defined(HAVE_AVFOUNDATION) || \ + defined(HAVE_FFMPEG) || \ + defined(HAVE_MSMF) +#define HAVE_VIDEO_OUTPUT +#endif + +/* OpenCV trace utilities */ +#define OPENCV_TRACE + +/* Library QR-code decoding */ +#define HAVE_QUIRC + +#endif // OPENCV_CVCONFIG_H_INCLUDED diff --git a/hgdriver/3rdparty/opencv/include/opencv2/highgui.hpp b/hgdriver/3rdparty/opencv/include/opencv2/highgui.hpp new file mode 100644 index 0000000..628b7fa --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/highgui.hpp @@ -0,0 +1,846 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HIGHGUI_HPP +#define OPENCV_HIGHGUI_HPP + +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio.hpp" +#endif + +/** +@defgroup highgui High-level GUI + +While OpenCV was designed for use in full-scale applications and can be used within functionally +rich UI frameworks (such as Qt\*, WinForms\*, or Cocoa\*) or without any UI at all, sometimes there +it is required to try functionality quickly and visualize the results. This is what the HighGUI +module has been designed for. + +It provides easy interface to: + +- Create and manipulate windows that can display images and "remember" their content (no need to + handle repaint events from OS). +- Add trackbars to the windows, handle simple mouse events as well as keyboard commands. + +@{ + @defgroup highgui_opengl OpenGL support + @defgroup highgui_qt Qt New Functions + + ![image](pics/qtgui.png) + + This figure explains new functionality implemented with Qt\* GUI. The new GUI provides a statusbar, + a toolbar, and a control panel. The control panel can have trackbars and buttonbars attached to it. + If you cannot see the control panel, press Ctrl+P or right-click any Qt window and select **Display + properties window**. + + - To attach a trackbar, the window name parameter must be NULL. + + - To attach a buttonbar, a button must be created. If the last bar attached to the control panel + is a buttonbar, the new button is added to the right of the last button. If the last bar + attached to the control panel is a trackbar, or the control panel is empty, a new buttonbar is + created. Then, a new button is attached to it. + + See below the example used to generate the figure: + @code + int main(int argc, char *argv[]) + { + + int value = 50; + int value2 = 0; + + + namedWindow("main1",WINDOW_NORMAL); + namedWindow("main2",WINDOW_AUTOSIZE | CV_GUI_NORMAL); + createTrackbar( "track1", "main1", &value, 255, NULL); + + String nameb1 = "button1"; + String nameb2 = "button2"; + + createButton(nameb1,callbackButton,&nameb1,QT_CHECKBOX,1); + createButton(nameb2,callbackButton,NULL,QT_CHECKBOX,0); + createTrackbar( "track2", NULL, &value2, 255, NULL); + createButton("button5",callbackButton1,NULL,QT_RADIOBOX,0); + createButton("button6",callbackButton2,NULL,QT_RADIOBOX,1); + + setMouseCallback( "main2",on_mouse,NULL ); + + Mat img1 = imread("files/flower.jpg"); + VideoCapture video; + video.open("files/hockey.avi"); + + Mat img2,img3; + + while( waitKey(33) != 27 ) + { + img1.convertTo(img2,-1,1,value); + video >> img3; + + imshow("main1",img2); + imshow("main2",img3); + } + + destroyAllWindows(); + + return 0; + } + @endcode + + + @defgroup highgui_winrt WinRT support + + This figure explains new functionality implemented with WinRT GUI. The new GUI provides an Image control, + and a slider panel. Slider panel holds trackbars attached to it. + + Sliders are attached below the image control. Every new slider is added below the previous one. + + See below the example used to generate the figure: + @code + void sample_app::MainPage::ShowWindow() + { + static cv::String windowName("sample"); + cv::winrt_initContainer(this->cvContainer); + cv::namedWindow(windowName); // not required + + cv::Mat image = cv::imread("Assets/sample.jpg"); + cv::Mat converted = cv::Mat(image.rows, image.cols, CV_8UC4); + cv::cvtColor(image, converted, COLOR_BGR2BGRA); + cv::imshow(windowName, converted); // this will create window if it hasn't been created before + + int state = 42; + cv::TrackbarCallback callback = [](int pos, void* userdata) + { + if (pos == 0) { + cv::destroyWindow(windowName); + } + }; + cv::TrackbarCallback callbackTwin = [](int pos, void* userdata) + { + if (pos >= 70) { + cv::destroyAllWindows(); + } + }; + cv::createTrackbar("Sample trackbar", windowName, &state, 100, callback); + cv::createTrackbar("Twin brother", windowName, &state, 100, callbackTwin); + } + @endcode + + @defgroup highgui_c C API +@} +*/ + +///////////////////////// graphical user interface ////////////////////////// +namespace cv +{ + +//! @addtogroup highgui +//! @{ + +//! Flags for cv::namedWindow +enum WindowFlags { + WINDOW_NORMAL = 0x00000000, //!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size. + WINDOW_AUTOSIZE = 0x00000001, //!< the user cannot resize the window, the size is constrainted by the image displayed. + WINDOW_OPENGL = 0x00001000, //!< window with opengl support. + + WINDOW_FULLSCREEN = 1, //!< change the window to fullscreen. + WINDOW_FREERATIO = 0x00000100, //!< the image expends as much as it can (no ratio constraint). + WINDOW_KEEPRATIO = 0x00000000, //!< the ratio of the image is respected. + WINDOW_GUI_EXPANDED=0x00000000, //!< status bar and tool bar + WINDOW_GUI_NORMAL = 0x00000010, //!< old fashious way + }; + +//! Flags for cv::setWindowProperty / cv::getWindowProperty +enum WindowPropertyFlags { + WND_PROP_FULLSCREEN = 0, //!< fullscreen property (can be WINDOW_NORMAL or WINDOW_FULLSCREEN). + WND_PROP_AUTOSIZE = 1, //!< autosize property (can be WINDOW_NORMAL or WINDOW_AUTOSIZE). + WND_PROP_ASPECT_RATIO = 2, //!< window's aspect ration (can be set to WINDOW_FREERATIO or WINDOW_KEEPRATIO). + WND_PROP_OPENGL = 3, //!< opengl support. + WND_PROP_VISIBLE = 4, //!< checks whether the window exists and is visible + WND_PROP_TOPMOST = 5 //!< property to toggle normal window being topmost or not + }; + +//! Mouse Events see cv::MouseCallback +enum MouseEventTypes { + EVENT_MOUSEMOVE = 0, //!< indicates that the mouse pointer has moved over the window. + EVENT_LBUTTONDOWN = 1, //!< indicates that the left mouse button is pressed. + EVENT_RBUTTONDOWN = 2, //!< indicates that the right mouse button is pressed. + EVENT_MBUTTONDOWN = 3, //!< indicates that the middle mouse button is pressed. + EVENT_LBUTTONUP = 4, //!< indicates that left mouse button is released. + EVENT_RBUTTONUP = 5, //!< indicates that right mouse button is released. + EVENT_MBUTTONUP = 6, //!< indicates that middle mouse button is released. + EVENT_LBUTTONDBLCLK = 7, //!< indicates that left mouse button is double clicked. + EVENT_RBUTTONDBLCLK = 8, //!< indicates that right mouse button is double clicked. + EVENT_MBUTTONDBLCLK = 9, //!< indicates that middle mouse button is double clicked. + EVENT_MOUSEWHEEL = 10,//!< positive and negative values mean forward and backward scrolling, respectively. + EVENT_MOUSEHWHEEL = 11 //!< positive and negative values mean right and left scrolling, respectively. + }; + +//! Mouse Event Flags see cv::MouseCallback +enum MouseEventFlags { + EVENT_FLAG_LBUTTON = 1, //!< indicates that the left mouse button is down. + EVENT_FLAG_RBUTTON = 2, //!< indicates that the right mouse button is down. + EVENT_FLAG_MBUTTON = 4, //!< indicates that the middle mouse button is down. + EVENT_FLAG_CTRLKEY = 8, //!< indicates that CTRL Key is pressed. + EVENT_FLAG_SHIFTKEY = 16,//!< indicates that SHIFT Key is pressed. + EVENT_FLAG_ALTKEY = 32 //!< indicates that ALT Key is pressed. + }; + +//! Qt font weight +enum QtFontWeights { + QT_FONT_LIGHT = 25, //!< Weight of 25 + QT_FONT_NORMAL = 50, //!< Weight of 50 + QT_FONT_DEMIBOLD = 63, //!< Weight of 63 + QT_FONT_BOLD = 75, //!< Weight of 75 + QT_FONT_BLACK = 87 //!< Weight of 87 + }; + +//! Qt font style +enum QtFontStyles { + QT_STYLE_NORMAL = 0, //!< Normal font. + QT_STYLE_ITALIC = 1, //!< Italic font. + QT_STYLE_OBLIQUE = 2 //!< Oblique font. + }; + +//! Qt "button" type +enum QtButtonTypes { + QT_PUSH_BUTTON = 0, //!< Push button. + QT_CHECKBOX = 1, //!< Checkbox button. + QT_RADIOBOX = 2, //!< Radiobox button. + QT_NEW_BUTTONBAR = 1024 //!< Button should create a new buttonbar + }; + +/** @brief Callback function for mouse events. see cv::setMouseCallback +@param event one of the cv::MouseEventTypes constants. +@param x The x-coordinate of the mouse event. +@param y The y-coordinate of the mouse event. +@param flags one of the cv::MouseEventFlags constants. +@param userdata The optional parameter. + */ +typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata); + +/** @brief Callback function for Trackbar see cv::createTrackbar +@param pos current position of the specified trackbar. +@param userdata The optional parameter. + */ +typedef void (*TrackbarCallback)(int pos, void* userdata); + +/** @brief Callback function defined to be called every frame. See cv::setOpenGlDrawCallback +@param userdata The optional parameter. + */ +typedef void (*OpenGlDrawCallback)(void* userdata); + +/** @brief Callback function for a button created by cv::createButton +@param state current state of the button. It could be -1 for a push button, 0 or 1 for a check/radio box button. +@param userdata The optional parameter. + */ +typedef void (*ButtonCallback)(int state, void* userdata); + +/** @brief Creates a window. + +The function namedWindow creates a window that can be used as a placeholder for images and +trackbars. Created windows are referred to by their names. + +If a window with the same name already exists, the function does nothing. + +You can call cv::destroyWindow or cv::destroyAllWindows to close the window and de-allocate any associated +memory usage. For a simple program, you do not really have to call these functions because all the +resources and windows of the application are closed automatically by the operating system upon exit. + +@note + +Qt backend supports additional flags: + - **WINDOW_NORMAL or WINDOW_AUTOSIZE:** WINDOW_NORMAL enables you to resize the + window, whereas WINDOW_AUTOSIZE adjusts automatically the window size to fit the + displayed image (see imshow ), and you cannot change the window size manually. + - **WINDOW_FREERATIO or WINDOW_KEEPRATIO:** WINDOW_FREERATIO adjusts the image + with no respect to its ratio, whereas WINDOW_KEEPRATIO keeps the image ratio. + - **WINDOW_GUI_NORMAL or WINDOW_GUI_EXPANDED:** WINDOW_GUI_NORMAL is the old way to draw the window + without statusbar and toolbar, whereas WINDOW_GUI_EXPANDED is a new enhanced GUI. +By default, flags == WINDOW_AUTOSIZE | WINDOW_KEEPRATIO | WINDOW_GUI_EXPANDED + +@param winname Name of the window in the window caption that may be used as a window identifier. +@param flags Flags of the window. The supported flags are: (cv::WindowFlags) + */ +CV_EXPORTS_W void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE); + +/** @brief Destroys the specified window. + +The function destroyWindow destroys the window with the given name. + +@param winname Name of the window to be destroyed. + */ +CV_EXPORTS_W void destroyWindow(const String& winname); + +/** @brief Destroys all of the HighGUI windows. + +The function destroyAllWindows destroys all of the opened HighGUI windows. + */ +CV_EXPORTS_W void destroyAllWindows(); + +CV_EXPORTS_W int startWindowThread(); + +/** @brief Similar to #waitKey, but returns full key code. + +@note + +Key code is implementation specific and depends on used backend: QT/GTK/Win32/etc + +*/ +CV_EXPORTS_W int waitKeyEx(int delay = 0); + +/** @brief Waits for a pressed key. + +The function waitKey waits for a key event infinitely (when \f$\texttt{delay}\leq 0\f$ ) or for delay +milliseconds, when it is positive. Since the OS has a minimum time between switching threads, the +function will not wait exactly delay ms, it will wait at least delay ms, depending on what else is +running on your computer at that time. It returns the code of the pressed key or -1 if no key was +pressed before the specified time had elapsed. + +@note + +This function is the only method in HighGUI that can fetch and handle events, so it needs to be +called periodically for normal event processing unless HighGUI is used within an environment that +takes care of event processing. + +@note + +The function only works if there is at least one HighGUI window created and the window is active. +If there are several HighGUI windows, any of them can be active. + +@param delay Delay in milliseconds. 0 is the special value that means "forever". + */ +CV_EXPORTS_W int waitKey(int delay = 0); + +/** @brief Displays an image in the specified window. + +The function imshow displays an image in the specified window. If the window was created with the +cv::WINDOW_AUTOSIZE flag, the image is shown with its original size, however it is still limited by the screen resolution. +Otherwise, the image is scaled to fit the window. The function may scale the image, depending on its depth: + +- If the image is 8-bit unsigned, it is displayed as is. +- If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the + value range [0,255\*256] is mapped to [0,255]. +- If the image is 32-bit or 64-bit floating-point, the pixel values are multiplied by 255. That is, the + value range [0,1] is mapped to [0,255]. + +If window was created with OpenGL support, cv::imshow also support ogl::Buffer , ogl::Texture2D and +cuda::GpuMat as input. + +If the window was not created before this function, it is assumed creating a window with cv::WINDOW_AUTOSIZE. + +If you need to show an image that is bigger than the screen resolution, you will need to call namedWindow("", WINDOW_NORMAL) before the imshow. + +@note This function should be followed by cv::waitKey function which displays the image for specified +milliseconds. Otherwise, it won't display the image. For example, **waitKey(0)** will display the window +infinitely until any keypress (it is suitable for image display). **waitKey(25)** will display a frame +for 25 ms, after which display will be automatically closed. (If you put it in a loop to read +videos, it will display the video frame-by-frame) + +@note + +[__Windows Backend Only__] Pressing Ctrl+C will copy the image to the clipboard. + +[__Windows Backend Only__] Pressing Ctrl+S will show a dialog to save the image. + +@param winname Name of the window. +@param mat Image to be shown. + */ +CV_EXPORTS_W void imshow(const String& winname, InputArray mat); + +/** @brief Resizes window to the specified size + +@note + +- The specified window size is for the image area. Toolbars are not counted. +- Only windows created without cv::WINDOW_AUTOSIZE flag can be resized. + +@param winname Window name. +@param width The new window width. +@param height The new window height. + */ +CV_EXPORTS_W void resizeWindow(const String& winname, int width, int height); + +/** @overload +@param winname Window name. +@param size The new window size. +*/ +CV_EXPORTS_W void resizeWindow(const String& winname, const cv::Size& size); + +/** @brief Moves window to the specified position + +@param winname Name of the window. +@param x The new x-coordinate of the window. +@param y The new y-coordinate of the window. + */ +CV_EXPORTS_W void moveWindow(const String& winname, int x, int y); + +/** @brief Changes parameters of a window dynamically. + +The function setWindowProperty enables changing properties of a window. + +@param winname Name of the window. +@param prop_id Window property to edit. The supported operation flags are: (cv::WindowPropertyFlags) +@param prop_value New value of the window property. The supported flags are: (cv::WindowFlags) + */ +CV_EXPORTS_W void setWindowProperty(const String& winname, int prop_id, double prop_value); + +/** @brief Updates window title +@param winname Name of the window. +@param title New title. +*/ +CV_EXPORTS_W void setWindowTitle(const String& winname, const String& title); + +/** @brief Provides parameters of a window. + +The function getWindowProperty returns properties of a window. + +@param winname Name of the window. +@param prop_id Window property to retrieve. The following operation flags are available: (cv::WindowPropertyFlags) + +@sa setWindowProperty + */ +CV_EXPORTS_W double getWindowProperty(const String& winname, int prop_id); + +/** @brief Provides rectangle of image in the window. + +The function getWindowImageRect returns the client screen coordinates, width and height of the image rendering area. + +@param winname Name of the window. + +@sa resizeWindow moveWindow + */ +CV_EXPORTS_W Rect getWindowImageRect(const String& winname); + +/** @example samples/cpp/create_mask.cpp +This program demonstrates using mouse events and how to make and use a mask image (black and white) . +*/ +/** @brief Sets mouse handler for the specified window + +@param winname Name of the window. +@param onMouse Callback function for mouse events. See OpenCV samples on how to specify and use the callback. +@param userdata The optional parameter passed to the callback. + */ +CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0); + +/** @brief Gets the mouse-wheel motion delta, when handling mouse-wheel events cv::EVENT_MOUSEWHEEL and +cv::EVENT_MOUSEHWHEEL. + +For regular mice with a scroll-wheel, delta will be a multiple of 120. The value 120 corresponds to +a one notch rotation of the wheel or the threshold for action to be taken and one such action should +occur for each delta. Some high-precision mice with higher-resolution freely-rotating wheels may +generate smaller values. + +For cv::EVENT_MOUSEWHEEL positive and negative values mean forward and backward scrolling, +respectively. For cv::EVENT_MOUSEHWHEEL, where available, positive and negative values mean right and +left scrolling, respectively. + +With the C API, the macro CV_GET_WHEEL_DELTA(flags) can be used alternatively. + +@note + +Mouse-wheel events are currently supported only on Windows. + +@param flags The mouse callback flags parameter. + */ +CV_EXPORTS int getMouseWheelDelta(int flags); + +/** @brief Selects ROI on the given image. +Function creates a window and allows user to select a ROI using mouse. +Controls: use `space` or `enter` to finish selection, use key `c` to cancel selection (function will return the zero cv::Rect). + +@param windowName name of the window where selection process will be shown. +@param img image to select a ROI. +@param showCrosshair if true crosshair of selection rectangle will be shown. +@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of +selection rectangle will correspont to the initial mouse position. +@return selected ROI or empty rect if selection canceled. + +@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...). +After finish of work an empty callback will be set for the used window. + */ +CV_EXPORTS_W Rect selectROI(const String& windowName, InputArray img, bool showCrosshair = true, bool fromCenter = false); + +/** @overload + */ +CV_EXPORTS_W Rect selectROI(InputArray img, bool showCrosshair = true, bool fromCenter = false); + +/** @brief Selects ROIs on the given image. +Function creates a window and allows user to select a ROIs using mouse. +Controls: use `space` or `enter` to finish current selection and start a new one, +use `esc` to terminate multiple ROI selection process. + +@param windowName name of the window where selection process will be shown. +@param img image to select a ROI. +@param boundingBoxes selected ROIs. +@param showCrosshair if true crosshair of selection rectangle will be shown. +@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of +selection rectangle will correspont to the initial mouse position. + +@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...). +After finish of work an empty callback will be set for the used window. + */ +CV_EXPORTS_W void selectROIs(const String& windowName, InputArray img, + CV_OUT std::vector& boundingBoxes, bool showCrosshair = true, bool fromCenter = false); + +/** @brief Creates a trackbar and attaches it to the specified window. + +The function createTrackbar creates a trackbar (a slider or range control) with the specified name +and range, assigns a variable value to be a position synchronized with the trackbar and specifies +the callback function onChange to be called on the trackbar position change. The created trackbar is +displayed in the specified window winname. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar should be attached to the +control panel. + +Clicking the label of each trackbar enables editing the trackbar values manually. + +@param trackbarname Name of the created trackbar. +@param winname Name of the window that will be used as a parent of the created trackbar. +@param value Optional pointer to an integer variable whose value reflects the position of the +slider. Upon creation, the slider position is defined by this variable. +@param count Maximal position of the slider. The minimal position is always 0. +@param onChange Pointer to the function to be called every time the slider changes position. This +function should be prototyped as void Foo(int,void\*); , where the first parameter is the trackbar +position and the second parameter is the user data (see the next parameter). If the callback is +the NULL pointer, no callbacks are called, but only value is updated. +@param userdata User data that is passed as is to the callback. It can be used to handle trackbar +events without using global variables. + */ +CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname, + int* value, int count, + TrackbarCallback onChange = 0, + void* userdata = 0); + +/** @brief Returns the trackbar position. + +The function returns the current position of the specified trackbar. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of the trackbar. + */ +CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname); + +/** @brief Sets the trackbar position. + +The function sets the position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param pos New position. + */ +CV_EXPORTS_W void setTrackbarPos(const String& trackbarname, const String& winname, int pos); + +/** @brief Sets the trackbar maximum position. + +The function sets the maximum position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param maxval New maximum position. + */ +CV_EXPORTS_W void setTrackbarMax(const String& trackbarname, const String& winname, int maxval); + +/** @brief Sets the trackbar minimum position. + +The function sets the minimum position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param minval New minimum position. + */ +CV_EXPORTS_W void setTrackbarMin(const String& trackbarname, const String& winname, int minval); + +//! @addtogroup highgui_opengl OpenGL support +//! @{ + +/** @brief Displays OpenGL 2D texture in the specified window. + +@param winname Name of the window. +@param tex OpenGL 2D texture data. + */ +CV_EXPORTS void imshow(const String& winname, const ogl::Texture2D& tex); + +/** @brief Sets a callback function to be called to draw on top of displayed image. + +The function setOpenGlDrawCallback can be used to draw 3D data on the window. See the example of +callback function below: +@code + void on_opengl(void* param) + { + glLoadIdentity(); + + glTranslated(0.0, 0.0, -1.0); + + glRotatef( 55, 1, 0, 0 ); + glRotatef( 45, 0, 1, 0 ); + glRotatef( 0, 0, 0, 1 ); + + static const int coords[6][4][3] = { + { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } }, + { { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } }, + { { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } }, + { { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } }, + { { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } }, + { { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } } + }; + + for (int i = 0; i < 6; ++i) { + glColor3ub( i*20, 100+i*10, i*42 ); + glBegin(GL_QUADS); + for (int j = 0; j < 4; ++j) { + glVertex3d(0.2 * coords[i][j][0], 0.2 * coords[i][j][1], 0.2 * coords[i][j][2]); + } + glEnd(); + } + } +@endcode + +@param winname Name of the window. +@param onOpenGlDraw Pointer to the function to be called every frame. This function should be +prototyped as void Foo(void\*) . +@param userdata Pointer passed to the callback function.(__Optional__) + */ +CV_EXPORTS void setOpenGlDrawCallback(const String& winname, OpenGlDrawCallback onOpenGlDraw, void* userdata = 0); + +/** @brief Sets the specified window as current OpenGL context. + +@param winname Name of the window. + */ +CV_EXPORTS void setOpenGlContext(const String& winname); + +/** @brief Force window to redraw its context and call draw callback ( See cv::setOpenGlDrawCallback ). + +@param winname Name of the window. + */ +CV_EXPORTS void updateWindow(const String& winname); + +//! @} highgui_opengl + +//! @addtogroup highgui_qt +//! @{ + +/** @brief QtFont available only for Qt. See cv::fontQt + */ +struct QtFont +{ + const char* nameFont; //!< Name of the font + Scalar color; //!< Color of the font. Scalar(blue_component, green_component, red_component[, alpha_component]) + int font_face; //!< See cv::QtFontStyles + const int* ascii; //!< font data and metrics + const int* greek; + const int* cyrillic; + float hscale, vscale; + float shear; //!< slope coefficient: 0 - normal, >0 - italic + int thickness; //!< See cv::QtFontWeights + float dx; //!< horizontal interval between letters + int line_type; //!< PointSize +}; + +/** @brief Creates the font to draw a text on an image. + +The function fontQt creates a cv::QtFont object. This cv::QtFont is not compatible with putText . + +A basic usage of this function is the following: : +@code + QtFont font = fontQt("Times"); + addText( img1, "Hello World !", Point(50,50), font); +@endcode + +@param nameFont Name of the font. The name should match the name of a system font (such as +*Times*). If the font is not found, a default one is used. +@param pointSize Size of the font. If not specified, equal zero or negative, the point size of the +font is set to a system-dependent default value. Generally, this is 12 points. +@param color Color of the font in BGRA where A = 255 is fully transparent. Use the macro CV_RGB +for simplicity. +@param weight Font weight. Available operation flags are : cv::QtFontWeights You can also specify a positive integer for better control. +@param style Font style. Available operation flags are : cv::QtFontStyles +@param spacing Spacing between characters. It can be negative or positive. + */ +CV_EXPORTS QtFont fontQt(const String& nameFont, int pointSize = -1, + Scalar color = Scalar::all(0), int weight = QT_FONT_NORMAL, + int style = QT_STYLE_NORMAL, int spacing = 0); + +/** @brief Draws a text on the image. + +The function addText draws *text* on the image *img* using a specific font *font* (see example cv::fontQt +) + +@param img 8-bit 3-channel image where the text should be drawn. +@param text Text to write on an image. +@param org Point(x,y) where the text should start on an image. +@param font Font to use to draw a text. + */ +CV_EXPORTS void addText( const Mat& img, const String& text, Point org, const QtFont& font); + +/** @brief Draws a text on the image. + +@param img 8-bit 3-channel image where the text should be drawn. +@param text Text to write on an image. +@param org Point(x,y) where the text should start on an image. +@param nameFont Name of the font. The name should match the name of a system font (such as +*Times*). If the font is not found, a default one is used. +@param pointSize Size of the font. If not specified, equal zero or negative, the point size of the +font is set to a system-dependent default value. Generally, this is 12 points. +@param color Color of the font in BGRA where A = 255 is fully transparent. +@param weight Font weight. Available operation flags are : cv::QtFontWeights You can also specify a positive integer for better control. +@param style Font style. Available operation flags are : cv::QtFontStyles +@param spacing Spacing between characters. It can be negative or positive. + */ +CV_EXPORTS_W void addText(const Mat& img, const String& text, Point org, const String& nameFont, int pointSize = -1, Scalar color = Scalar::all(0), + int weight = QT_FONT_NORMAL, int style = QT_STYLE_NORMAL, int spacing = 0); + +/** @brief Displays a text on a window image as an overlay for a specified duration. + +The function displayOverlay displays useful information/tips on top of the window for a certain +amount of time *delayms*. The function does not modify the image, displayed in the window, that is, +after the specified delay the original content of the window is restored. + +@param winname Name of the window. +@param text Overlay text to write on a window image. +@param delayms The period (in milliseconds), during which the overlay text is displayed. If this +function is called before the previous overlay text timed out, the timer is restarted and the text +is updated. If this value is zero, the text never disappears. + */ +CV_EXPORTS_W void displayOverlay(const String& winname, const String& text, int delayms = 0); + +/** @brief Displays a text on the window statusbar during the specified period of time. + +The function displayStatusBar displays useful information/tips on top of the window for a certain +amount of time *delayms* . This information is displayed on the window statusbar (the window must be +created with the CV_GUI_EXPANDED flags). + +@param winname Name of the window. +@param text Text to write on the window statusbar. +@param delayms Duration (in milliseconds) to display the text. If this function is called before +the previous text timed out, the timer is restarted and the text is updated. If this value is +zero, the text never disappears. + */ +CV_EXPORTS_W void displayStatusBar(const String& winname, const String& text, int delayms = 0); + +/** @brief Saves parameters of the specified window. + +The function saveWindowParameters saves size, location, flags, trackbars value, zoom and panning +location of the window windowName. + +@param windowName Name of the window. + */ +CV_EXPORTS void saveWindowParameters(const String& windowName); + +/** @brief Loads parameters of the specified window. + +The function loadWindowParameters loads size, location, flags, trackbars value, zoom and panning +location of the window windowName. + +@param windowName Name of the window. + */ +CV_EXPORTS void loadWindowParameters(const String& windowName); + +CV_EXPORTS int startLoop(int (*pt2Func)(int argc, char *argv[]), int argc, char* argv[]); + +CV_EXPORTS void stopLoop(); + +/** @brief Attaches a button to the control panel. + +The function createButton attaches a button to the control panel. Each button is added to a +buttonbar to the right of the last button. A new buttonbar is created if nothing was attached to the +control panel before, or if the last element attached to the control panel was a trackbar or if the +QT_NEW_BUTTONBAR flag is added to the type. + +See below various examples of the cv::createButton function call: : +@code + createButton(NULL,callbackButton);//create a push button "button 0", that will call callbackButton. + createButton("button2",callbackButton,NULL,QT_CHECKBOX,0); + createButton("button3",callbackButton,&value); + createButton("button5",callbackButton1,NULL,QT_RADIOBOX); + createButton("button6",callbackButton2,NULL,QT_PUSH_BUTTON,1); + createButton("button6",callbackButton2,NULL,QT_PUSH_BUTTON|QT_NEW_BUTTONBAR);// create a push button in a new row +@endcode + +@param bar_name Name of the button. +@param on_change Pointer to the function to be called every time the button changes its state. +This function should be prototyped as void Foo(int state,\*void); . *state* is the current state +of the button. It could be -1 for a push button, 0 or 1 for a check/radio box button. +@param userdata Pointer passed to the callback function. +@param type Optional type of the button. Available types are: (cv::QtButtonTypes) +@param initial_button_state Default state of the button. Use for checkbox and radiobox. Its +value could be 0 or 1. (__Optional__) +*/ +CV_EXPORTS int createButton( const String& bar_name, ButtonCallback on_change, + void* userdata = 0, int type = QT_PUSH_BUTTON, + bool initial_button_state = false); + +//! @} highgui_qt + +//! @} highgui + +} // cv + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/highgui/highgui_c.h" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui.hpp b/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui.hpp new file mode 100644 index 0000000..160c9cf --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/highgui.hpp" diff --git a/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui_c.h b/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui_c.h new file mode 100644 index 0000000..3541313 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/highgui/highgui_c.h @@ -0,0 +1,262 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HIGHGUI_H +#define OPENCV_HIGHGUI_H + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs/imgcodecs_c.h" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio/videoio_c.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup highgui_c + @{ + */ + +/****************************************************************************************\ +* Basic GUI functions * +\****************************************************************************************/ +//YV +//-----------New for Qt +/* For font */ +enum { CV_FONT_LIGHT = 25,//QFont::Light, + CV_FONT_NORMAL = 50,//QFont::Normal, + CV_FONT_DEMIBOLD = 63,//QFont::DemiBold, + CV_FONT_BOLD = 75,//QFont::Bold, + CV_FONT_BLACK = 87 //QFont::Black +}; + +enum { CV_STYLE_NORMAL = 0,//QFont::StyleNormal, + CV_STYLE_ITALIC = 1,//QFont::StyleItalic, + CV_STYLE_OBLIQUE = 2 //QFont::StyleOblique +}; +/* ---------*/ + +//for color cvScalar(blue_component, green_component, red_component[, alpha_component]) +//and alpha= 0 <-> 0xFF (not transparent <-> transparent) +CVAPI(CvFont) cvFontQt(const char* nameFont, int pointSize CV_DEFAULT(-1), CvScalar color CV_DEFAULT(cvScalarAll(0)), int weight CV_DEFAULT(CV_FONT_NORMAL), int style CV_DEFAULT(CV_STYLE_NORMAL), int spacing CV_DEFAULT(0)); + +CVAPI(void) cvAddText(const CvArr* img, const char* text, CvPoint org, CvFont *arg2); + +CVAPI(void) cvDisplayOverlay(const char* name, const char* text, int delayms CV_DEFAULT(0)); +CVAPI(void) cvDisplayStatusBar(const char* name, const char* text, int delayms CV_DEFAULT(0)); + +CVAPI(void) cvSaveWindowParameters(const char* name); +CVAPI(void) cvLoadWindowParameters(const char* name); +CVAPI(int) cvStartLoop(int (*pt2Func)(int argc, char *argv[]), int argc, char* argv[]); +CVAPI(void) cvStopLoop( void ); + +typedef void (CV_CDECL *CvButtonCallback)(int state, void* userdata); +enum {CV_PUSH_BUTTON = 0, CV_CHECKBOX = 1, CV_RADIOBOX = 2}; +CVAPI(int) cvCreateButton( const char* button_name CV_DEFAULT(NULL),CvButtonCallback on_change CV_DEFAULT(NULL), void* userdata CV_DEFAULT(NULL) , int button_type CV_DEFAULT(CV_PUSH_BUTTON), int initial_button_state CV_DEFAULT(0)); +//---------------------- + + +/* this function is used to set some external parameters in case of X Window */ +CVAPI(int) cvInitSystem( int argc, char** argv ); + +CVAPI(int) cvStartWindowThread( void ); + +// --------- YV --------- +enum +{ + //These 3 flags are used by cvSet/GetWindowProperty + CV_WND_PROP_FULLSCREEN = 0, //to change/get window's fullscreen property + CV_WND_PROP_AUTOSIZE = 1, //to change/get window's autosize property + CV_WND_PROP_ASPECTRATIO= 2, //to change/get window's aspectratio property + CV_WND_PROP_OPENGL = 3, //to change/get window's opengl support + CV_WND_PROP_VISIBLE = 4, + + //These 2 flags are used by cvNamedWindow and cvSet/GetWindowProperty + CV_WINDOW_NORMAL = 0x00000000, //the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size + CV_WINDOW_AUTOSIZE = 0x00000001, //the user cannot resize the window, the size is constrainted by the image displayed + CV_WINDOW_OPENGL = 0x00001000, //window with opengl support + + //Those flags are only for Qt + CV_GUI_EXPANDED = 0x00000000, //status bar and tool bar + CV_GUI_NORMAL = 0x00000010, //old fashious way + + //These 3 flags are used by cvNamedWindow and cvSet/GetWindowProperty + CV_WINDOW_FULLSCREEN = 1,//change the window to fullscreen + CV_WINDOW_FREERATIO = 0x00000100,//the image expends as much as it can (no ratio constraint) + CV_WINDOW_KEEPRATIO = 0x00000000//the ration image is respected. +}; + +/* create window */ +CVAPI(int) cvNamedWindow( const char* name, int flags CV_DEFAULT(CV_WINDOW_AUTOSIZE) ); + +/* Set and Get Property of the window */ +CVAPI(void) cvSetWindowProperty(const char* name, int prop_id, double prop_value); +CVAPI(double) cvGetWindowProperty(const char* name, int prop_id); + +#ifdef __cplusplus // FIXIT remove in OpenCV 4.0 +/* Get window image rectangle coordinates, width and height */ +CVAPI(cv::Rect)cvGetWindowImageRect(const char* name); +#endif + +/* display image within window (highgui windows remember their content) */ +CVAPI(void) cvShowImage( const char* name, const CvArr* image ); + +/* resize/move window */ +CVAPI(void) cvResizeWindow( const char* name, int width, int height ); +CVAPI(void) cvMoveWindow( const char* name, int x, int y ); + + +/* destroy window and all the trackers associated with it */ +CVAPI(void) cvDestroyWindow( const char* name ); + +CVAPI(void) cvDestroyAllWindows(void); + +/* get native window handle (HWND in case of Win32 and Widget in case of X Window) */ +CVAPI(void*) cvGetWindowHandle( const char* name ); + +/* get name of highgui window given its native handle */ +CVAPI(const char*) cvGetWindowName( void* window_handle ); + + +typedef void (CV_CDECL *CvTrackbarCallback)(int pos); + +/* create trackbar and display it on top of given window, set callback */ +CVAPI(int) cvCreateTrackbar( const char* trackbar_name, const char* window_name, + int* value, int count, CvTrackbarCallback on_change CV_DEFAULT(NULL)); + +typedef void (CV_CDECL *CvTrackbarCallback2)(int pos, void* userdata); + +CVAPI(int) cvCreateTrackbar2( const char* trackbar_name, const char* window_name, + int* value, int count, CvTrackbarCallback2 on_change, + void* userdata CV_DEFAULT(0)); + +/* retrieve or set trackbar position */ +CVAPI(int) cvGetTrackbarPos( const char* trackbar_name, const char* window_name ); +CVAPI(void) cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ); +CVAPI(void) cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval); +CVAPI(void) cvSetTrackbarMin(const char* trackbar_name, const char* window_name, int minval); + +enum +{ + CV_EVENT_MOUSEMOVE =0, + CV_EVENT_LBUTTONDOWN =1, + CV_EVENT_RBUTTONDOWN =2, + CV_EVENT_MBUTTONDOWN =3, + CV_EVENT_LBUTTONUP =4, + CV_EVENT_RBUTTONUP =5, + CV_EVENT_MBUTTONUP =6, + CV_EVENT_LBUTTONDBLCLK =7, + CV_EVENT_RBUTTONDBLCLK =8, + CV_EVENT_MBUTTONDBLCLK =9, + CV_EVENT_MOUSEWHEEL =10, + CV_EVENT_MOUSEHWHEEL =11 +}; + +enum +{ + CV_EVENT_FLAG_LBUTTON =1, + CV_EVENT_FLAG_RBUTTON =2, + CV_EVENT_FLAG_MBUTTON =4, + CV_EVENT_FLAG_CTRLKEY =8, + CV_EVENT_FLAG_SHIFTKEY =16, + CV_EVENT_FLAG_ALTKEY =32 +}; + + +#define CV_GET_WHEEL_DELTA(flags) ((short)((flags >> 16) & 0xffff)) // upper 16 bits + +typedef void (CV_CDECL *CvMouseCallback )(int event, int x, int y, int flags, void* param); + +/* assign callback for mouse events */ +CVAPI(void) cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, + void* param CV_DEFAULT(NULL)); + +/* wait for key event infinitely (delay<=0) or for "delay" milliseconds */ +CVAPI(int) cvWaitKey(int delay CV_DEFAULT(0)); + +// OpenGL support + +typedef void (CV_CDECL *CvOpenGlDrawCallback)(void* userdata); +CVAPI(void) cvSetOpenGlDrawCallback(const char* window_name, CvOpenGlDrawCallback callback, void* userdata CV_DEFAULT(NULL)); + +CVAPI(void) cvSetOpenGlContext(const char* window_name); +CVAPI(void) cvUpdateWindow(const char* window_name); + + +/****************************************************************************************\ + +* Obsolete functions/synonyms * +\****************************************************************************************/ + +#define cvAddSearchPath(path) +#define cvvInitSystem cvInitSystem +#define cvvNamedWindow cvNamedWindow +#define cvvShowImage cvShowImage +#define cvvResizeWindow cvResizeWindow +#define cvvDestroyWindow cvDestroyWindow +#define cvvCreateTrackbar cvCreateTrackbar +#define cvvAddSearchPath cvAddSearchPath +#define cvvWaitKey(name) cvWaitKey(0) +#define cvvWaitKeyEx(name,delay) cvWaitKey(delay) +#define HG_AUTOSIZE CV_WINDOW_AUTOSIZE +#define set_preprocess_func cvSetPreprocessFuncWin32 +#define set_postprocess_func cvSetPostprocessFuncWin32 + +#if defined _WIN32 + +CVAPI(void) cvSetPreprocessFuncWin32_(const void* callback); +CVAPI(void) cvSetPostprocessFuncWin32_(const void* callback); +#define cvSetPreprocessFuncWin32(callback) cvSetPreprocessFuncWin32_((const void*)(callback)) +#define cvSetPostprocessFuncWin32(callback) cvSetPostprocessFuncWin32_((const void*)(callback)) + +#endif + +/** @} highgui_c */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs.hpp new file mode 100644 index 0000000..319f6cb --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs.hpp @@ -0,0 +1,261 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGCODECS_HPP +#define OPENCV_IMGCODECS_HPP + +#include "opencv2/core.hpp" + +/** + @defgroup imgcodecs Image file reading and writing + @{ + @defgroup imgcodecs_c C API + @defgroup imgcodecs_ios iOS glue + @} +*/ + +//////////////////////////////// image codec //////////////////////////////// +namespace cv +{ + +//! @addtogroup imgcodecs +//! @{ + +//! Imread flags +enum ImreadModes { + IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation. + IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion). + IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image. + IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit. + IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format. + IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image. + IMREAD_REDUCED_GRAYSCALE_2 = 16, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2. + IMREAD_REDUCED_COLOR_2 = 17, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2. + IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4. + IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4. + IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8. + IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. + IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag. + }; + +//! Imwrite flags +enum ImwriteFlags { + IMWRITE_JPEG_QUALITY = 1, //!< For JPEG, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. + IMWRITE_JPEG_PROGRESSIVE = 2, //!< Enable JPEG features, 0 or 1, default is False. + IMWRITE_JPEG_OPTIMIZE = 3, //!< Enable JPEG features, 0 or 1, default is False. + IMWRITE_JPEG_RST_INTERVAL = 4, //!< JPEG restart interval, 0 - 65535, default is 0 - no restart. + IMWRITE_JPEG_LUMA_QUALITY = 5, //!< Separate luma quality level, 0 - 100, default is 0 - don't use. + IMWRITE_JPEG_CHROMA_QUALITY = 6, //!< Separate chroma quality level, 0 - 100, default is 0 - don't use. + IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting). + IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE. + IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0. + IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. + IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default) + IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. + IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format + IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values. + IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI. + IMWRITE_TIFF_YDPI = 258, //!< For TIFF, use to specify the Y direction DPI. + IMWRITE_TIFF_COMPRESSION = 259 //!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default. + }; + +enum ImwriteEXRTypeFlags { + /*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */ + IMWRITE_EXR_TYPE_HALF = 1, //!< store as HALF (FP16) + IMWRITE_EXR_TYPE_FLOAT = 2 //!< store as FP32 (default) + }; + +//! Imwrite PNG specific flags used to tune the compression algorithm. +/** These flags will be modify the way of PNG image compression and will be passed to the underlying zlib processing stage. + +- The effect of IMWRITE_PNG_STRATEGY_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between IMWRITE_PNG_STRATEGY_DEFAULT and IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY. +- IMWRITE_PNG_STRATEGY_RLE is designed to be almost as fast as IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY, but give better compression for PNG image data. +- The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. +- IMWRITE_PNG_STRATEGY_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. +*/ +enum ImwritePNGFlags { + IMWRITE_PNG_STRATEGY_DEFAULT = 0, //!< Use this value for normal data. + IMWRITE_PNG_STRATEGY_FILTERED = 1, //!< Use this value for data produced by a filter (or predictor).Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. + IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY = 2, //!< Use this value to force Huffman encoding only (no string match). + IMWRITE_PNG_STRATEGY_RLE = 3, //!< Use this value to limit match distances to one (run-length encoding). + IMWRITE_PNG_STRATEGY_FIXED = 4 //!< Using this value prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. + }; + +//! Imwrite PAM specific tupletype flags used to define the 'TUPETYPE' field of a PAM file. +enum ImwritePAMFlags { + IMWRITE_PAM_FORMAT_NULL = 0, + IMWRITE_PAM_FORMAT_BLACKANDWHITE = 1, + IMWRITE_PAM_FORMAT_GRAYSCALE = 2, + IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA = 3, + IMWRITE_PAM_FORMAT_RGB = 4, + IMWRITE_PAM_FORMAT_RGB_ALPHA = 5, + }; + +/** @brief Loads an image from a file. + +@anchor imread + +The function imread loads an image from the specified file and returns it. If the image cannot be +read (because of missing file, improper permissions, unsupported or invalid format), the function +returns an empty matrix ( Mat::data==NULL ). + +Currently, the following file formats are supported: + +- Windows bitmaps - \*.bmp, \*.dib (always supported) +- JPEG files - \*.jpeg, \*.jpg, \*.jpe (see the *Note* section) +- JPEG 2000 files - \*.jp2 (see the *Note* section) +- Portable Network Graphics - \*.png (see the *Note* section) +- WebP - \*.webp (see the *Note* section) +- Portable image format - \*.pbm, \*.pgm, \*.ppm \*.pxm, \*.pnm (always supported) +- Sun rasters - \*.sr, \*.ras (always supported) +- TIFF files - \*.tiff, \*.tif (see the *Note* section) +- OpenEXR Image files - \*.exr (see the *Note* section) +- Radiance HDR - \*.hdr, \*.pic (always supported) +- Raster and Vector geospatial data supported by GDAL (see the *Note* section) + +@note +- The function determines the type of an image by the content, not by the file extension. +- In the case of color images, the decoded images will have the channels stored in **B G R** order. +- When using IMREAD_GRAYSCALE, the codec's internal grayscale conversion will be used, if available. + Results may differ to the output of cvtColor() +- On Microsoft Windows\* OS and MacOSX\*, the codecs shipped with an OpenCV image (libjpeg, + libpng, libtiff, and libjasper) are used by default. So, OpenCV can always read JPEGs, PNGs, + and TIFFs. On MacOSX, there is also an option to use native MacOSX image readers. But beware + that currently these native image loaders give images with different pixel values because of + the color management embedded into MacOSX. +- On Linux\*, BSD flavors and other Unix-like open-source operating systems, OpenCV looks for + codecs supplied with an OS image. Install the relevant packages (do not forget the development + files, for example, "libjpeg-dev", in Debian\* and Ubuntu\*) to get the codec support or turn + on the OPENCV_BUILD_3RDPARTY_LIBS flag in CMake. +- In the case you set *WITH_GDAL* flag to true in CMake and @ref IMREAD_LOAD_GDAL to load the image, + then the [GDAL](http://www.gdal.org) driver will be used in order to decode the image, supporting + the following formats: [Raster](http://www.gdal.org/formats_list.html), + [Vector](http://www.gdal.org/ogr_formats.html). +- If EXIF information is embedded in the image file, the EXIF orientation will be taken into account + and thus the image will be rotated accordingly except if the flags @ref IMREAD_IGNORE_ORIENTATION + or @ref IMREAD_UNCHANGED are passed. +- By default number of pixels must be less than 2^30. Limit can be set using system + variable OPENCV_IO_MAX_IMAGE_PIXELS + +@param filename Name of file to be loaded. +@param flags Flag that can take values of cv::ImreadModes +*/ +CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); + +/** @brief Loads a multi-page image from a file. + +The function imreadmulti loads a multi-page image from the specified file into a vector of Mat objects. +@param filename Name of file to be loaded. +@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR. +@param mats A vector of Mat objects holding each page, if more than one. +@sa cv::imread +*/ +CV_EXPORTS_W bool imreadmulti(const String& filename, CV_OUT std::vector& mats, int flags = IMREAD_ANYCOLOR); + +/** @brief Saves an image to a specified file. + +The function imwrite saves the image to the specified file. The image format is chosen based on the +filename extension (see cv::imread for the list of extensions). In general, only 8-bit +single-channel or 3-channel (with 'BGR' channel order) images +can be saved using this function, with these exceptions: + +- 16-bit unsigned (CV_16U) images can be saved in the case of PNG, JPEG 2000, and TIFF formats +- 32-bit float (CV_32F) images can be saved in TIFF, OpenEXR, and Radiance HDR formats; 3-channel +(CV_32FC3) TIFF images will be saved using the LogLuv high dynamic range encoding (4 bytes per pixel) +- PNG images with an alpha channel can be saved using this function. To do this, create +8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels +should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535 (see the code sample below). + +If the format, depth or channel order is different, use +Mat::convertTo and cv::cvtColor to convert it before saving. Or, use the universal FileStorage I/O +functions to save the image to XML or YAML format. + +The sample below shows how to create a BGRA image and save it to a PNG file. It also demonstrates how to set custom +compression parameters: +@include snippets/imgcodecs_imwrite.cpp +@param filename Name of the file. +@param img Image to be saved. +@param params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags +*/ +CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, + const std::vector& params = std::vector()); + +/** @brief Reads an image from a buffer in memory. + +The function imdecode reads an image from the specified buffer in the memory. If the buffer is too short or +contains invalid data, the function returns an empty matrix ( Mat::data==NULL ). + +See cv::imread for the list of supported formats and flags description. + +@note In the case of color images, the decoded images will have the channels stored in **B G R** order. +@param buf Input array or vector of bytes. +@param flags The same flags as in cv::imread, see cv::ImreadModes. +*/ +CV_EXPORTS_W Mat imdecode( InputArray buf, int flags ); + +/** @overload +@param buf +@param flags +@param dst The optional output placeholder for the decoded matrix. It can save the image +reallocations when the function is called repeatedly for images of the same size. +*/ +CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst); + +/** @brief Encodes an image into a memory buffer. + +The function imencode compresses the image and stores it in the memory buffer that is resized to fit the +result. See cv::imwrite for the list of supported formats and flags description. + +@param ext File extension that defines the output format. +@param img Image to be written. +@param buf Output buffer resized to fit the compressed image. +@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags. +*/ +CV_EXPORTS_W bool imencode( const String& ext, InputArray img, + CV_OUT std::vector& buf, + const std::vector& params = std::vector()); + +//! @} imgcodecs + +} // cv + +#endif //OPENCV_IMGCODECS_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs.hpp new file mode 100644 index 0000000..a3cd232 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/imgcodecs.hpp" diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs_c.h b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs_c.h new file mode 100644 index 0000000..c36dac3 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/imgcodecs_c.h @@ -0,0 +1,149 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGCODECS_H +#define OPENCV_IMGCODECS_H + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup imgcodecs_c + @{ + */ + +enum +{ +/* 8bit, color or not */ + CV_LOAD_IMAGE_UNCHANGED =-1, +/* 8bit, gray */ + CV_LOAD_IMAGE_GRAYSCALE =0, +/* ?, color */ + CV_LOAD_IMAGE_COLOR =1, +/* any depth, ? */ + CV_LOAD_IMAGE_ANYDEPTH =2, +/* ?, any color */ + CV_LOAD_IMAGE_ANYCOLOR =4, +/* ?, no rotate */ + CV_LOAD_IMAGE_IGNORE_ORIENTATION =128 +}; + +/* load image from file + iscolor can be a combination of above flags where CV_LOAD_IMAGE_UNCHANGED + overrides the other flags + using CV_LOAD_IMAGE_ANYCOLOR alone is equivalent to CV_LOAD_IMAGE_UNCHANGED + unless CV_LOAD_IMAGE_ANYDEPTH is specified images are converted to 8bit +*/ +CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvLoadImageM( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +enum +{ + CV_IMWRITE_JPEG_QUALITY =1, + CV_IMWRITE_JPEG_PROGRESSIVE =2, + CV_IMWRITE_JPEG_OPTIMIZE =3, + CV_IMWRITE_JPEG_RST_INTERVAL =4, + CV_IMWRITE_JPEG_LUMA_QUALITY =5, + CV_IMWRITE_JPEG_CHROMA_QUALITY =6, + CV_IMWRITE_PNG_COMPRESSION =16, + CV_IMWRITE_PNG_STRATEGY =17, + CV_IMWRITE_PNG_BILEVEL =18, + CV_IMWRITE_PNG_STRATEGY_DEFAULT =0, + CV_IMWRITE_PNG_STRATEGY_FILTERED =1, + CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, + CV_IMWRITE_PNG_STRATEGY_RLE =3, + CV_IMWRITE_PNG_STRATEGY_FIXED =4, + CV_IMWRITE_PXM_BINARY =32, + CV_IMWRITE_EXR_TYPE = 48, + CV_IMWRITE_WEBP_QUALITY =64, + CV_IMWRITE_PAM_TUPLETYPE = 128, + CV_IMWRITE_PAM_FORMAT_NULL = 0, + CV_IMWRITE_PAM_FORMAT_BLACKANDWHITE = 1, + CV_IMWRITE_PAM_FORMAT_GRAYSCALE = 2, + CV_IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA = 3, + CV_IMWRITE_PAM_FORMAT_RGB = 4, + CV_IMWRITE_PAM_FORMAT_RGB_ALPHA = 5, +}; + + + +/* save image to file */ +CVAPI(int) cvSaveImage( const char* filename, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +/* decode image stored in the buffer */ +CVAPI(IplImage*) cvDecodeImage( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvDecodeImageM( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +/* encode image and store the result as a byte vector (single-row 8uC1 matrix) */ +CVAPI(CvMat*) cvEncodeImage( const char* ext, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +enum +{ + CV_CVTIMG_FLIP =1, + CV_CVTIMG_SWAP_RB =2 +}; + +/* utility function: convert one image to another with optional vertical flip */ +CVAPI(void) cvConvertImage( const CvArr* src, CvArr* dst, int flags CV_DEFAULT(0)); + +CVAPI(int) cvHaveImageReader(const char* filename); +CVAPI(int) cvHaveImageWriter(const char* filename); + + +/****************************************************************************************\ +* Obsolete functions/synonyms * +\****************************************************************************************/ + +#define cvvLoadImage(name) cvLoadImage((name),1) +#define cvvSaveImage cvSaveImage +#define cvvConvertImage cvConvertImage + +/** @} imgcodecs_c */ + +#ifdef __cplusplus +} +#endif + +#endif // OPENCV_IMGCODECS_H diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/ios.h b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/ios.h new file mode 100644 index 0000000..a90c6d3 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgcodecs/ios.h @@ -0,0 +1,57 @@ + +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#import +#import +#import +#import +#include "opencv2/core/core.hpp" + +//! @addtogroup imgcodecs_ios +//! @{ + +CV_EXPORTS UIImage* MatToUIImage(const cv::Mat& image); +CV_EXPORTS void UIImageToMat(const UIImage* image, + cv::Mat& m, bool alphaExist = false); + +//! @} diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgproc.hpp new file mode 100644 index 0000000..5c82dd6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc.hpp @@ -0,0 +1,4942 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_HPP +#define OPENCV_IMGPROC_HPP + +#include "opencv2/core.hpp" + +/** + @defgroup imgproc Image Processing + +This module includes image-processing functions. + + @{ + @defgroup imgproc_filter Image Filtering + +Functions and classes described in this section are used to perform various linear or non-linear +filtering operations on 2D images (represented as Mat's). It means that for each pixel location +\f$(x,y)\f$ in the source image (normally, rectangular), its neighborhood is considered and used to +compute the response. In case of a linear filter, it is a weighted sum of pixel values. In case of +morphological operations, it is the minimum or maximum values, and so on. The computed response is +stored in the destination image at the same location \f$(x,y)\f$. It means that the output image +will be of the same size as the input image. Normally, the functions support multi-channel arrays, +in which case every channel is processed independently. Therefore, the output image will also have +the same number of channels as the input one. + +Another common feature of the functions and classes described in this section is that, unlike +simple arithmetic functions, they need to extrapolate values of some non-existing pixels. For +example, if you want to smooth an image using a Gaussian \f$3 \times 3\f$ filter, then, when +processing the left-most pixels in each row, you need pixels to the left of them, that is, outside +of the image. You can let these pixels be the same as the left-most image pixels ("replicated +border" extrapolation method), or assume that all the non-existing pixels are zeros ("constant +border" extrapolation method), and so on. OpenCV enables you to specify the extrapolation method. +For details, see #BorderTypes + +@anchor filter_depths +### Depth combinations +Input depth (src.depth()) | Output depth (ddepth) +--------------------------|---------------------- +CV_8U | -1/CV_16S/CV_32F/CV_64F +CV_16U/CV_16S | -1/CV_32F/CV_64F +CV_32F | -1/CV_32F/CV_64F +CV_64F | -1/CV_64F + +@note when ddepth=-1, the output image will have the same depth as the source. + + @defgroup imgproc_transform Geometric Image Transformations + +The functions in this section perform various geometrical transformations of 2D images. They do not +change the image content but deform the pixel grid and map this deformed grid to the destination +image. In fact, to avoid sampling artifacts, the mapping is done in the reverse order, from +destination to the source. That is, for each pixel \f$(x, y)\f$ of the destination image, the +functions compute coordinates of the corresponding "donor" pixel in the source image and copy the +pixel value: + +\f[\texttt{dst} (x,y)= \texttt{src} (f_x(x,y), f_y(x,y))\f] + +In case when you specify the forward mapping \f$\left: \texttt{src} \rightarrow +\texttt{dst}\f$, the OpenCV functions first compute the corresponding inverse mapping +\f$\left: \texttt{dst} \rightarrow \texttt{src}\f$ and then use the above formula. + +The actual implementations of the geometrical transformations, from the most generic remap and to +the simplest and the fastest resize, need to solve two main problems with the above formula: + +- Extrapolation of non-existing pixels. Similarly to the filtering functions described in the +previous section, for some \f$(x,y)\f$, either one of \f$f_x(x,y)\f$, or \f$f_y(x,y)\f$, or both +of them may fall outside of the image. In this case, an extrapolation method needs to be used. +OpenCV provides the same selection of extrapolation methods as in the filtering functions. In +addition, it provides the method #BORDER_TRANSPARENT. This means that the corresponding pixels in +the destination image will not be modified at all. + +- Interpolation of pixel values. Usually \f$f_x(x,y)\f$ and \f$f_y(x,y)\f$ are floating-point +numbers. This means that \f$\left\f$ can be either an affine or perspective +transformation, or radial lens distortion correction, and so on. So, a pixel value at fractional +coordinates needs to be retrieved. In the simplest case, the coordinates can be just rounded to the +nearest integer coordinates and the corresponding pixel can be used. This is called a +nearest-neighbor interpolation. However, a better result can be achieved by using more +sophisticated [interpolation methods](http://en.wikipedia.org/wiki/Multivariate_interpolation) , +where a polynomial function is fit into some neighborhood of the computed pixel \f$(f_x(x,y), +f_y(x,y))\f$, and then the value of the polynomial at \f$(f_x(x,y), f_y(x,y))\f$ is taken as the +interpolated pixel value. In OpenCV, you can choose between several interpolation methods. See +resize for details. + +@note The geometrical transformations do not work with `CV_8S` or `CV_32S` images. + + @defgroup imgproc_misc Miscellaneous Image Transformations + @defgroup imgproc_draw Drawing Functions + +Drawing functions work with matrices/images of arbitrary depth. The boundaries of the shapes can be +rendered with antialiasing (implemented only for 8-bit images for now). All the functions include +the parameter color that uses an RGB value (that may be constructed with the Scalar constructor ) +for color images and brightness for grayscale images. For color images, the channel ordering is +normally *Blue, Green, Red*. This is what imshow, imread, and imwrite expect. So, if you form a +color using the Scalar constructor, it should look like: + +\f[\texttt{Scalar} (blue \_ component, green \_ component, red \_ component[, alpha \_ component])\f] + +If you are using your own image rendering and I/O functions, you can use any channel ordering. The +drawing functions process each channel independently and do not depend on the channel order or even +on the used color space. The whole image can be converted from BGR to RGB or to a different color +space using cvtColor . + +If a drawn figure is partially or completely outside the image, the drawing functions clip it. Also, +many drawing functions can handle pixel coordinates specified with sub-pixel accuracy. This means +that the coordinates can be passed as fixed-point numbers encoded as integers. The number of +fractional bits is specified by the shift parameter and the real point coordinates are calculated as +\f$\texttt{Point}(x,y)\rightarrow\texttt{Point2f}(x*2^{-shift},y*2^{-shift})\f$ . This feature is +especially effective when rendering antialiased shapes. + +@note The functions do not support alpha-transparency when the target image is 4-channel. In this +case, the color[3] is simply copied to the repainted pixels. Thus, if you want to paint +semi-transparent shapes, you can paint them in a separate buffer and then blend it with the main +image. + + @defgroup imgproc_color_conversions Color Space Conversions + @defgroup imgproc_colormap ColorMaps in OpenCV + +The human perception isn't built for observing fine changes in grayscale images. Human eyes are more +sensitive to observing changes between colors, so you often need to recolor your grayscale images to +get a clue about them. OpenCV now comes with various colormaps to enhance the visualization in your +computer vision application. + +In OpenCV you only need applyColorMap to apply a colormap on a given image. The following sample +code reads the path to an image from command line, applies a Jet colormap on it and shows the +result: + +@include snippets/imgproc_applyColorMap.cpp + +@see #ColormapTypes + + @defgroup imgproc_subdiv2d Planar Subdivision + +The Subdiv2D class described in this section is used to perform various planar subdivision on +a set of 2D points (represented as vector of Point2f). OpenCV subdivides a plane into triangles +using the Delaunay's algorithm, which corresponds to the dual graph of the Voronoi diagram. +In the figure below, the Delaunay's triangulation is marked with black lines and the Voronoi +diagram with red lines. + +![Delaunay triangulation (black) and Voronoi (red)](pics/delaunay_voronoi.png) + +The subdivisions can be used for the 3D piece-wise transformation of a plane, morphing, fast +location of points on the plane, building special graphs (such as NNG,RNG), and so forth. + + @defgroup imgproc_hist Histograms + @defgroup imgproc_shape Structural Analysis and Shape Descriptors + @defgroup imgproc_motion Motion Analysis and Object Tracking + @defgroup imgproc_feature Feature Detection + @defgroup imgproc_object Object Detection + @defgroup imgproc_c C API + @defgroup imgproc_hal Hardware Acceleration Layer + @{ + @defgroup imgproc_hal_functions Functions + @defgroup imgproc_hal_interface Interface + @} + @} +*/ + +namespace cv +{ + +/** @addtogroup imgproc +@{ +*/ + +//! @addtogroup imgproc_filter +//! @{ + +//! type of morphological operation +enum MorphTypes{ + MORPH_ERODE = 0, //!< see #erode + MORPH_DILATE = 1, //!< see #dilate + MORPH_OPEN = 2, //!< an opening operation + //!< \f[\texttt{dst} = \mathrm{open} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \mathrm{erode} ( \texttt{src} , \texttt{element} ))\f] + MORPH_CLOSE = 3, //!< a closing operation + //!< \f[\texttt{dst} = \mathrm{close} ( \texttt{src} , \texttt{element} )= \mathrm{erode} ( \mathrm{dilate} ( \texttt{src} , \texttt{element} ))\f] + MORPH_GRADIENT = 4, //!< a morphological gradient + //!< \f[\texttt{dst} = \mathrm{morph\_grad} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \texttt{src} , \texttt{element} )- \mathrm{erode} ( \texttt{src} , \texttt{element} )\f] + MORPH_TOPHAT = 5, //!< "top hat" + //!< \f[\texttt{dst} = \mathrm{tophat} ( \texttt{src} , \texttt{element} )= \texttt{src} - \mathrm{open} ( \texttt{src} , \texttt{element} )\f] + MORPH_BLACKHAT = 6, //!< "black hat" + //!< \f[\texttt{dst} = \mathrm{blackhat} ( \texttt{src} , \texttt{element} )= \mathrm{close} ( \texttt{src} , \texttt{element} )- \texttt{src}\f] + MORPH_HITMISS = 7 //!< "hit or miss" + //!< .- Only supported for CV_8UC1 binary images. A tutorial can be found in the documentation +}; + +//! shape of the structuring element +enum MorphShapes { + MORPH_RECT = 0, //!< a rectangular structuring element: \f[E_{ij}=1\f] + MORPH_CROSS = 1, //!< a cross-shaped structuring element: + //!< \f[E_{ij} = \fork{1}{if i=\texttt{anchor.y} or j=\texttt{anchor.x}}{0}{otherwise}\f] + MORPH_ELLIPSE = 2 //!< an elliptic structuring element, that is, a filled ellipse inscribed + //!< into the rectangle Rect(0, 0, esize.width, 0.esize.height) +}; + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +//! interpolation algorithm +enum InterpolationFlags{ + /** nearest neighbor interpolation */ + INTER_NEAREST = 0, + /** bilinear interpolation */ + INTER_LINEAR = 1, + /** bicubic interpolation */ + INTER_CUBIC = 2, + /** resampling using pixel area relation. It may be a preferred method for image decimation, as + it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST + method. */ + INTER_AREA = 3, + /** Lanczos interpolation over 8x8 neighborhood */ + INTER_LANCZOS4 = 4, + /** Bit exact bilinear interpolation */ + INTER_LINEAR_EXACT = 5, + /** mask for interpolation codes */ + INTER_MAX = 7, + /** flag, fills all of the destination image pixels. If some of them correspond to outliers in the + source image, they are set to zero */ + WARP_FILL_OUTLIERS = 8, + /** flag, inverse transformation + + For example, #linearPolar or #logPolar transforms: + - flag is __not__ set: \f$dst( \rho , \phi ) = src(x,y)\f$ + - flag is set: \f$dst(x,y) = src( \rho , \phi )\f$ + */ + WARP_INVERSE_MAP = 16 +}; + +/** \brief Specify the polar mapping mode +@sa warpPolar +*/ +enum WarpPolarMode +{ + WARP_POLAR_LINEAR = 0, ///< Remaps an image to/from polar space. + WARP_POLAR_LOG = 256 ///< Remaps an image to/from semilog-polar space. +}; + +enum InterpolationMasks { + INTER_BITS = 5, + INTER_BITS2 = INTER_BITS * 2, + INTER_TAB_SIZE = 1 << INTER_BITS, + INTER_TAB_SIZE2 = INTER_TAB_SIZE * INTER_TAB_SIZE + }; + +//! @} imgproc_transform + +//! @addtogroup imgproc_misc +//! @{ + +//! Distance types for Distance Transform and M-estimators +//! @see distanceTransform, fitLine +enum DistanceTypes { + DIST_USER = -1, //!< User defined distance + DIST_L1 = 1, //!< distance = |x1-x2| + |y1-y2| + DIST_L2 = 2, //!< the simple euclidean distance + DIST_C = 3, //!< distance = max(|x1-x2|,|y1-y2|) + DIST_L12 = 4, //!< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1)) + DIST_FAIR = 5, //!< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998 + DIST_WELSCH = 6, //!< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846 + DIST_HUBER = 7 //!< distance = |x| \texttt{thresh}\)}{0}{otherwise}\f] + THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f] + THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] + THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] + THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] + THRESH_MASK = 7, + THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value + THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value +}; + +//! adaptive threshold algorithm +//! @see adaptiveThreshold +enum AdaptiveThresholdTypes { + /** the threshold value \f$T(x,y)\f$ is a mean of the \f$\texttt{blockSize} \times + \texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$ minus C */ + ADAPTIVE_THRESH_MEAN_C = 0, + /** the threshold value \f$T(x, y)\f$ is a weighted sum (cross-correlation with a Gaussian + window) of the \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$ + minus C . The default sigma (standard deviation) is used for the specified blockSize . See + #getGaussianKernel*/ + ADAPTIVE_THRESH_GAUSSIAN_C = 1 +}; + +//! cv::undistort mode +enum UndistortTypes { + PROJ_SPHERICAL_ORTHO = 0, + PROJ_SPHERICAL_EQRECT = 1 + }; + +//! class of the pixel in GrabCut algorithm +enum GrabCutClasses { + GC_BGD = 0, //!< an obvious background pixels + GC_FGD = 1, //!< an obvious foreground (object) pixel + GC_PR_BGD = 2, //!< a possible background pixel + GC_PR_FGD = 3 //!< a possible foreground pixel +}; + +//! GrabCut algorithm flags +enum GrabCutModes { + /** The function initializes the state and the mask using the provided rectangle. After that it + runs iterCount iterations of the algorithm. */ + GC_INIT_WITH_RECT = 0, + /** The function initializes the state using the provided mask. Note that GC_INIT_WITH_RECT + and GC_INIT_WITH_MASK can be combined. Then, all the pixels outside of the ROI are + automatically initialized with GC_BGD .*/ + GC_INIT_WITH_MASK = 1, + /** The value means that the algorithm should just resume. */ + GC_EVAL = 2, + /** The value means that the algorithm should just run the grabCut algorithm (a single iteration) with the fixed model */ + GC_EVAL_FREEZE_MODEL = 3 +}; + +//! distanceTransform algorithm flags +enum DistanceTransformLabelTypes { + /** each connected component of zeros in src (as well as all the non-zero pixels closest to the + connected component) will be assigned the same label */ + DIST_LABEL_CCOMP = 0, + /** each zero pixel (and all the non-zero pixels closest to it) gets its own label. */ + DIST_LABEL_PIXEL = 1 +}; + +//! floodfill algorithm flags +enum FloodFillFlags { + /** If set, the difference between the current pixel and seed pixel is considered. Otherwise, + the difference between neighbor pixels is considered (that is, the range is floating). */ + FLOODFILL_FIXED_RANGE = 1 << 16, + /** If set, the function does not change the image ( newVal is ignored), and only fills the + mask with the value specified in bits 8-16 of flags as described above. This option only make + sense in function variants that have the mask parameter. */ + FLOODFILL_MASK_ONLY = 1 << 17 +}; + +//! @} imgproc_misc + +//! @addtogroup imgproc_shape +//! @{ + +//! connected components algorithm output formats +enum ConnectedComponentsTypes { + CC_STAT_LEFT = 0, //!< The leftmost (x) coordinate which is the inclusive start of the bounding + //!< box in the horizontal direction. + CC_STAT_TOP = 1, //!< The topmost (y) coordinate which is the inclusive start of the bounding + //!< box in the vertical direction. + CC_STAT_WIDTH = 2, //!< The horizontal size of the bounding box + CC_STAT_HEIGHT = 3, //!< The vertical size of the bounding box + CC_STAT_AREA = 4, //!< The total area (in pixels) of the connected component +#ifndef CV_DOXYGEN + CC_STAT_MAX = 5 //!< Max enumeration value. Used internally only for memory allocation +#endif +}; + +//! connected components algorithm +enum ConnectedComponentsAlgorithmsTypes { + CCL_WU = 0, //!< SAUF algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity + CCL_DEFAULT = -1, //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity + CCL_GRANA = 1 //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity +}; + +//! mode of the contour retrieval algorithm +enum RetrievalModes { + /** retrieves only the extreme outer contours. It sets `hierarchy[i][2]=hierarchy[i][3]=-1` for + all the contours. */ + RETR_EXTERNAL = 0, + /** retrieves all of the contours without establishing any hierarchical relationships. */ + RETR_LIST = 1, + /** retrieves all of the contours and organizes them into a two-level hierarchy. At the top + level, there are external boundaries of the components. At the second level, there are + boundaries of the holes. If there is another contour inside a hole of a connected component, it + is still put at the top level. */ + RETR_CCOMP = 2, + /** retrieves all of the contours and reconstructs a full hierarchy of nested contours.*/ + RETR_TREE = 3, + RETR_FLOODFILL = 4 //!< +}; + +//! the contour approximation algorithm +enum ContourApproximationModes { + /** stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and + (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is, + max(abs(x1-x2),abs(y2-y1))==1. */ + CHAIN_APPROX_NONE = 1, + /** compresses horizontal, vertical, and diagonal segments and leaves only their end points. + For example, an up-right rectangular contour is encoded with 4 points. */ + CHAIN_APPROX_SIMPLE = 2, + /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */ + CHAIN_APPROX_TC89_L1 = 3, + /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */ + CHAIN_APPROX_TC89_KCOS = 4 +}; + +/** @brief Shape matching methods + +\f$A\f$ denotes object1,\f$B\f$ denotes object2 + +\f$\begin{array}{l} m^A_i = \mathrm{sign} (h^A_i) \cdot \log{h^A_i} \\ m^B_i = \mathrm{sign} (h^B_i) \cdot \log{h^B_i} \end{array}\f$ + +and \f$h^A_i, h^B_i\f$ are the Hu moments of \f$A\f$ and \f$B\f$ , respectively. +*/ +enum ShapeMatchModes { + CONTOURS_MATCH_I1 =1, //!< \f[I_1(A,B) = \sum _{i=1...7} \left | \frac{1}{m^A_i} - \frac{1}{m^B_i} \right |\f] + CONTOURS_MATCH_I2 =2, //!< \f[I_2(A,B) = \sum _{i=1...7} \left | m^A_i - m^B_i \right |\f] + CONTOURS_MATCH_I3 =3 //!< \f[I_3(A,B) = \max _{i=1...7} \frac{ \left| m^A_i - m^B_i \right| }{ \left| m^A_i \right| }\f] +}; + +//! @} imgproc_shape + +//! @addtogroup imgproc_feature +//! @{ + +//! Variants of a Hough transform +enum HoughModes { + + /** classical or standard Hough transform. Every line is represented by two floating-point + numbers \f$(\rho, \theta)\f$ , where \f$\rho\f$ is a distance between (0,0) point and the line, + and \f$\theta\f$ is the angle between x-axis and the normal to the line. Thus, the matrix must + be (the created sequence will be) of CV_32FC2 type */ + HOUGH_STANDARD = 0, + /** probabilistic Hough transform (more efficient in case if the picture contains a few long + linear segments). It returns line segments rather than the whole line. Each segment is + represented by starting and ending points, and the matrix must be (the created sequence will + be) of the CV_32SC4 type. */ + HOUGH_PROBABILISTIC = 1, + /** multi-scale variant of the classical Hough transform. The lines are encoded the same way as + HOUGH_STANDARD. */ + HOUGH_MULTI_SCALE = 2, + HOUGH_GRADIENT = 3 //!< basically *21HT*, described in @cite Yuen90 +}; + +//! Variants of Line Segment %Detector +enum LineSegmentDetectorModes { + LSD_REFINE_NONE = 0, //!< No refinement applied + LSD_REFINE_STD = 1, //!< Standard refinement is applied. E.g. breaking arches into smaller straighter line approximations. + LSD_REFINE_ADV = 2 //!< Advanced refinement. Number of false alarms is calculated, lines are + //!< refined through increase of precision, decrement in size, etc. +}; + +//! @} imgproc_feature + +/** Histogram comparison methods + @ingroup imgproc_hist +*/ +enum HistCompMethods { + /** Correlation + \f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f] + where + \f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f] + and \f$N\f$ is a total number of histogram bins. */ + HISTCMP_CORREL = 0, + /** Chi-Square + \f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f] */ + HISTCMP_CHISQR = 1, + /** Intersection + \f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f] */ + HISTCMP_INTERSECT = 2, + /** Bhattacharyya distance + (In fact, OpenCV computes Hellinger distance, which is related to Bhattacharyya coefficient.) + \f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f] */ + HISTCMP_BHATTACHARYYA = 3, + HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA, //!< Synonym for HISTCMP_BHATTACHARYYA + /** Alternative Chi-Square + \f[d(H_1,H_2) = 2 * \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)+H_2(I)}\f] + This alternative formula is regularly used for texture comparison. See e.g. @cite Puzicha1997 */ + HISTCMP_CHISQR_ALT = 4, + /** Kullback-Leibler divergence + \f[d(H_1,H_2) = \sum _I H_1(I) \log \left(\frac{H_1(I)}{H_2(I)}\right)\f] */ + HISTCMP_KL_DIV = 5 +}; + +/** the color conversion codes +@see @ref imgproc_color_conversions +@ingroup imgproc_color_conversions + */ +enum ColorConversionCodes { + COLOR_BGR2BGRA = 0, //!< add alpha channel to RGB or BGR image + COLOR_RGB2RGBA = COLOR_BGR2BGRA, + + COLOR_BGRA2BGR = 1, //!< remove alpha channel from RGB or BGR image + COLOR_RGBA2RGB = COLOR_BGRA2BGR, + + COLOR_BGR2RGBA = 2, //!< convert between RGB and BGR color spaces (with or without alpha channel) + COLOR_RGB2BGRA = COLOR_BGR2RGBA, + + COLOR_RGBA2BGR = 3, + COLOR_BGRA2RGB = COLOR_RGBA2BGR, + + COLOR_BGR2RGB = 4, + COLOR_RGB2BGR = COLOR_BGR2RGB, + + COLOR_BGRA2RGBA = 5, + COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, + + COLOR_BGR2GRAY = 6, //!< convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions" + COLOR_RGB2GRAY = 7, + COLOR_GRAY2BGR = 8, + COLOR_GRAY2RGB = COLOR_GRAY2BGR, + COLOR_GRAY2BGRA = 9, + COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, + COLOR_BGRA2GRAY = 10, + COLOR_RGBA2GRAY = 11, + + COLOR_BGR2BGR565 = 12, //!< convert between RGB/BGR and BGR565 (16-bit images) + COLOR_RGB2BGR565 = 13, + COLOR_BGR5652BGR = 14, + COLOR_BGR5652RGB = 15, + COLOR_BGRA2BGR565 = 16, + COLOR_RGBA2BGR565 = 17, + COLOR_BGR5652BGRA = 18, + COLOR_BGR5652RGBA = 19, + + COLOR_GRAY2BGR565 = 20, //!< convert between grayscale to BGR565 (16-bit images) + COLOR_BGR5652GRAY = 21, + + COLOR_BGR2BGR555 = 22, //!< convert between RGB/BGR and BGR555 (16-bit images) + COLOR_RGB2BGR555 = 23, + COLOR_BGR5552BGR = 24, + COLOR_BGR5552RGB = 25, + COLOR_BGRA2BGR555 = 26, + COLOR_RGBA2BGR555 = 27, + COLOR_BGR5552BGRA = 28, + COLOR_BGR5552RGBA = 29, + + COLOR_GRAY2BGR555 = 30, //!< convert between grayscale and BGR555 (16-bit images) + COLOR_BGR5552GRAY = 31, + + COLOR_BGR2XYZ = 32, //!< convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions" + COLOR_RGB2XYZ = 33, + COLOR_XYZ2BGR = 34, + COLOR_XYZ2RGB = 35, + + COLOR_BGR2YCrCb = 36, //!< convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions" + COLOR_RGB2YCrCb = 37, + COLOR_YCrCb2BGR = 38, + COLOR_YCrCb2RGB = 39, + + COLOR_BGR2HSV = 40, //!< convert RGB/BGR to HSV (hue saturation value), @ref color_convert_rgb_hsv "color conversions" + COLOR_RGB2HSV = 41, + + COLOR_BGR2Lab = 44, //!< convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions" + COLOR_RGB2Lab = 45, + + COLOR_BGR2Luv = 50, //!< convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions" + COLOR_RGB2Luv = 51, + COLOR_BGR2HLS = 52, //!< convert RGB/BGR to HLS (hue lightness saturation), @ref color_convert_rgb_hls "color conversions" + COLOR_RGB2HLS = 53, + + COLOR_HSV2BGR = 54, //!< backward conversions to RGB/BGR + COLOR_HSV2RGB = 55, + + COLOR_Lab2BGR = 56, + COLOR_Lab2RGB = 57, + COLOR_Luv2BGR = 58, + COLOR_Luv2RGB = 59, + COLOR_HLS2BGR = 60, + COLOR_HLS2RGB = 61, + + COLOR_BGR2HSV_FULL = 66, + COLOR_RGB2HSV_FULL = 67, + COLOR_BGR2HLS_FULL = 68, + COLOR_RGB2HLS_FULL = 69, + + COLOR_HSV2BGR_FULL = 70, + COLOR_HSV2RGB_FULL = 71, + COLOR_HLS2BGR_FULL = 72, + COLOR_HLS2RGB_FULL = 73, + + COLOR_LBGR2Lab = 74, + COLOR_LRGB2Lab = 75, + COLOR_LBGR2Luv = 76, + COLOR_LRGB2Luv = 77, + + COLOR_Lab2LBGR = 78, + COLOR_Lab2LRGB = 79, + COLOR_Luv2LBGR = 80, + COLOR_Luv2LRGB = 81, + + COLOR_BGR2YUV = 82, //!< convert between RGB/BGR and YUV + COLOR_RGB2YUV = 83, + COLOR_YUV2BGR = 84, + COLOR_YUV2RGB = 85, + + //! YUV 4:2:0 family to RGB + COLOR_YUV2RGB_NV12 = 90, + COLOR_YUV2BGR_NV12 = 91, + COLOR_YUV2RGB_NV21 = 92, + COLOR_YUV2BGR_NV21 = 93, + COLOR_YUV420sp2RGB = COLOR_YUV2RGB_NV21, + COLOR_YUV420sp2BGR = COLOR_YUV2BGR_NV21, + + COLOR_YUV2RGBA_NV12 = 94, + COLOR_YUV2BGRA_NV12 = 95, + COLOR_YUV2RGBA_NV21 = 96, + COLOR_YUV2BGRA_NV21 = 97, + COLOR_YUV420sp2RGBA = COLOR_YUV2RGBA_NV21, + COLOR_YUV420sp2BGRA = COLOR_YUV2BGRA_NV21, + + COLOR_YUV2RGB_YV12 = 98, + COLOR_YUV2BGR_YV12 = 99, + COLOR_YUV2RGB_IYUV = 100, + COLOR_YUV2BGR_IYUV = 101, + COLOR_YUV2RGB_I420 = COLOR_YUV2RGB_IYUV, + COLOR_YUV2BGR_I420 = COLOR_YUV2BGR_IYUV, + COLOR_YUV420p2RGB = COLOR_YUV2RGB_YV12, + COLOR_YUV420p2BGR = COLOR_YUV2BGR_YV12, + + COLOR_YUV2RGBA_YV12 = 102, + COLOR_YUV2BGRA_YV12 = 103, + COLOR_YUV2RGBA_IYUV = 104, + COLOR_YUV2BGRA_IYUV = 105, + COLOR_YUV2RGBA_I420 = COLOR_YUV2RGBA_IYUV, + COLOR_YUV2BGRA_I420 = COLOR_YUV2BGRA_IYUV, + COLOR_YUV420p2RGBA = COLOR_YUV2RGBA_YV12, + COLOR_YUV420p2BGRA = COLOR_YUV2BGRA_YV12, + + COLOR_YUV2GRAY_420 = 106, + COLOR_YUV2GRAY_NV21 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_NV12 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_YV12 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_IYUV = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_I420 = COLOR_YUV2GRAY_420, + COLOR_YUV420sp2GRAY = COLOR_YUV2GRAY_420, + COLOR_YUV420p2GRAY = COLOR_YUV2GRAY_420, + + //! YUV 4:2:2 family to RGB + COLOR_YUV2RGB_UYVY = 107, + COLOR_YUV2BGR_UYVY = 108, + //COLOR_YUV2RGB_VYUY = 109, + //COLOR_YUV2BGR_VYUY = 110, + COLOR_YUV2RGB_Y422 = COLOR_YUV2RGB_UYVY, + COLOR_YUV2BGR_Y422 = COLOR_YUV2BGR_UYVY, + COLOR_YUV2RGB_UYNV = COLOR_YUV2RGB_UYVY, + COLOR_YUV2BGR_UYNV = COLOR_YUV2BGR_UYVY, + + COLOR_YUV2RGBA_UYVY = 111, + COLOR_YUV2BGRA_UYVY = 112, + //COLOR_YUV2RGBA_VYUY = 113, + //COLOR_YUV2BGRA_VYUY = 114, + COLOR_YUV2RGBA_Y422 = COLOR_YUV2RGBA_UYVY, + COLOR_YUV2BGRA_Y422 = COLOR_YUV2BGRA_UYVY, + COLOR_YUV2RGBA_UYNV = COLOR_YUV2RGBA_UYVY, + COLOR_YUV2BGRA_UYNV = COLOR_YUV2BGRA_UYVY, + + COLOR_YUV2RGB_YUY2 = 115, + COLOR_YUV2BGR_YUY2 = 116, + COLOR_YUV2RGB_YVYU = 117, + COLOR_YUV2BGR_YVYU = 118, + COLOR_YUV2RGB_YUYV = COLOR_YUV2RGB_YUY2, + COLOR_YUV2BGR_YUYV = COLOR_YUV2BGR_YUY2, + COLOR_YUV2RGB_YUNV = COLOR_YUV2RGB_YUY2, + COLOR_YUV2BGR_YUNV = COLOR_YUV2BGR_YUY2, + + COLOR_YUV2RGBA_YUY2 = 119, + COLOR_YUV2BGRA_YUY2 = 120, + COLOR_YUV2RGBA_YVYU = 121, + COLOR_YUV2BGRA_YVYU = 122, + COLOR_YUV2RGBA_YUYV = COLOR_YUV2RGBA_YUY2, + COLOR_YUV2BGRA_YUYV = COLOR_YUV2BGRA_YUY2, + COLOR_YUV2RGBA_YUNV = COLOR_YUV2RGBA_YUY2, + COLOR_YUV2BGRA_YUNV = COLOR_YUV2BGRA_YUY2, + + COLOR_YUV2GRAY_UYVY = 123, + COLOR_YUV2GRAY_YUY2 = 124, + //CV_YUV2GRAY_VYUY = CV_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_Y422 = COLOR_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_UYNV = COLOR_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_YVYU = COLOR_YUV2GRAY_YUY2, + COLOR_YUV2GRAY_YUYV = COLOR_YUV2GRAY_YUY2, + COLOR_YUV2GRAY_YUNV = COLOR_YUV2GRAY_YUY2, + + //! alpha premultiplication + COLOR_RGBA2mRGBA = 125, + COLOR_mRGBA2RGBA = 126, + + //! RGB to YUV 4:2:0 family + COLOR_RGB2YUV_I420 = 127, + COLOR_BGR2YUV_I420 = 128, + COLOR_RGB2YUV_IYUV = COLOR_RGB2YUV_I420, + COLOR_BGR2YUV_IYUV = COLOR_BGR2YUV_I420, + + COLOR_RGBA2YUV_I420 = 129, + COLOR_BGRA2YUV_I420 = 130, + COLOR_RGBA2YUV_IYUV = COLOR_RGBA2YUV_I420, + COLOR_BGRA2YUV_IYUV = COLOR_BGRA2YUV_I420, + COLOR_RGB2YUV_YV12 = 131, + COLOR_BGR2YUV_YV12 = 132, + COLOR_RGBA2YUV_YV12 = 133, + COLOR_BGRA2YUV_YV12 = 134, + + //! Demosaicing + COLOR_BayerBG2BGR = 46, + COLOR_BayerGB2BGR = 47, + COLOR_BayerRG2BGR = 48, + COLOR_BayerGR2BGR = 49, + + COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, + COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, + COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, + COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, + + COLOR_BayerBG2GRAY = 86, + COLOR_BayerGB2GRAY = 87, + COLOR_BayerRG2GRAY = 88, + COLOR_BayerGR2GRAY = 89, + + //! Demosaicing using Variable Number of Gradients + COLOR_BayerBG2BGR_VNG = 62, + COLOR_BayerGB2BGR_VNG = 63, + COLOR_BayerRG2BGR_VNG = 64, + COLOR_BayerGR2BGR_VNG = 65, + + COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, + COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, + COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, + COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, + + //! Edge-Aware Demosaicing + COLOR_BayerBG2BGR_EA = 135, + COLOR_BayerGB2BGR_EA = 136, + COLOR_BayerRG2BGR_EA = 137, + COLOR_BayerGR2BGR_EA = 138, + + COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, + COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, + COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, + COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, + + //! Demosaicing with alpha channel + COLOR_BayerBG2BGRA = 139, + COLOR_BayerGB2BGRA = 140, + COLOR_BayerRG2BGRA = 141, + COLOR_BayerGR2BGRA = 142, + + COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, + COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, + COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, + COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, + + COLOR_COLORCVT_MAX = 143 +}; + +//! @addtogroup imgproc_shape +//! @{ + +//! types of intersection between rectangles +enum RectanglesIntersectTypes { + INTERSECT_NONE = 0, //!< No intersection + INTERSECT_PARTIAL = 1, //!< There is a partial intersection + INTERSECT_FULL = 2 //!< One of the rectangle is fully enclosed in the other +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform +*/ +class CV_EXPORTS_W GeneralizedHough : public Algorithm +{ +public: + //! set template to search + CV_WRAP virtual void setTemplate(InputArray templ, Point templCenter = Point(-1, -1)) = 0; + CV_WRAP virtual void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) = 0; + + //! find template on image + CV_WRAP virtual void detect(InputArray image, OutputArray positions, OutputArray votes = noArray()) = 0; + CV_WRAP virtual void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes = noArray()) = 0; + + //! Canny low threshold. + CV_WRAP virtual void setCannyLowThresh(int cannyLowThresh) = 0; + CV_WRAP virtual int getCannyLowThresh() const = 0; + + //! Canny high threshold. + CV_WRAP virtual void setCannyHighThresh(int cannyHighThresh) = 0; + CV_WRAP virtual int getCannyHighThresh() const = 0; + + //! Minimum distance between the centers of the detected objects. + CV_WRAP virtual void setMinDist(double minDist) = 0; + CV_WRAP virtual double getMinDist() const = 0; + + //! Inverse ratio of the accumulator resolution to the image resolution. + CV_WRAP virtual void setDp(double dp) = 0; + CV_WRAP virtual double getDp() const = 0; + + //! Maximal size of inner buffers. + CV_WRAP virtual void setMaxBufferSize(int maxBufferSize) = 0; + CV_WRAP virtual int getMaxBufferSize() const = 0; +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform + +Detects position only without translation and rotation @cite Ballard1981 . +*/ +class CV_EXPORTS_W GeneralizedHoughBallard : public GeneralizedHough +{ +public: + //! R-Table levels. + CV_WRAP virtual void setLevels(int levels) = 0; + CV_WRAP virtual int getLevels() const = 0; + + //! The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected. + CV_WRAP virtual void setVotesThreshold(int votesThreshold) = 0; + CV_WRAP virtual int getVotesThreshold() const = 0; +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform + +Detects position, translation and rotation @cite Guil1999 . +*/ +class CV_EXPORTS_W GeneralizedHoughGuil : public GeneralizedHough +{ +public: + //! Angle difference in degrees between two points in feature. + CV_WRAP virtual void setXi(double xi) = 0; + CV_WRAP virtual double getXi() const = 0; + + //! Feature table levels. + CV_WRAP virtual void setLevels(int levels) = 0; + CV_WRAP virtual int getLevels() const = 0; + + //! Maximal difference between angles that treated as equal. + CV_WRAP virtual void setAngleEpsilon(double angleEpsilon) = 0; + CV_WRAP virtual double getAngleEpsilon() const = 0; + + //! Minimal rotation angle to detect in degrees. + CV_WRAP virtual void setMinAngle(double minAngle) = 0; + CV_WRAP virtual double getMinAngle() const = 0; + + //! Maximal rotation angle to detect in degrees. + CV_WRAP virtual void setMaxAngle(double maxAngle) = 0; + CV_WRAP virtual double getMaxAngle() const = 0; + + //! Angle step in degrees. + CV_WRAP virtual void setAngleStep(double angleStep) = 0; + CV_WRAP virtual double getAngleStep() const = 0; + + //! Angle votes threshold. + CV_WRAP virtual void setAngleThresh(int angleThresh) = 0; + CV_WRAP virtual int getAngleThresh() const = 0; + + //! Minimal scale to detect. + CV_WRAP virtual void setMinScale(double minScale) = 0; + CV_WRAP virtual double getMinScale() const = 0; + + //! Maximal scale to detect. + CV_WRAP virtual void setMaxScale(double maxScale) = 0; + CV_WRAP virtual double getMaxScale() const = 0; + + //! Scale step. + CV_WRAP virtual void setScaleStep(double scaleStep) = 0; + CV_WRAP virtual double getScaleStep() const = 0; + + //! Scale votes threshold. + CV_WRAP virtual void setScaleThresh(int scaleThresh) = 0; + CV_WRAP virtual int getScaleThresh() const = 0; + + //! Position votes threshold. + CV_WRAP virtual void setPosThresh(int posThresh) = 0; + CV_WRAP virtual int getPosThresh() const = 0; +}; + +//! @} imgproc_shape + +//! @addtogroup imgproc_hist +//! @{ + +/** @brief Base class for Contrast Limited Adaptive Histogram Equalization. +*/ +class CV_EXPORTS_W CLAHE : public Algorithm +{ +public: + /** @brief Equalizes the histogram of a grayscale image using Contrast Limited Adaptive Histogram Equalization. + + @param src Source image of type CV_8UC1 or CV_16UC1. + @param dst Destination image. + */ + CV_WRAP virtual void apply(InputArray src, OutputArray dst) = 0; + + /** @brief Sets threshold for contrast limiting. + + @param clipLimit threshold value. + */ + CV_WRAP virtual void setClipLimit(double clipLimit) = 0; + + //! Returns threshold value for contrast limiting. + CV_WRAP virtual double getClipLimit() const = 0; + + /** @brief Sets size of grid for histogram equalization. Input image will be divided into + equally sized rectangular tiles. + + @param tileGridSize defines the number of tiles in row and column. + */ + CV_WRAP virtual void setTilesGridSize(Size tileGridSize) = 0; + + //!@brief Returns Size defines the number of tiles in row and column. + CV_WRAP virtual Size getTilesGridSize() const = 0; + + CV_WRAP virtual void collectGarbage() = 0; +}; + +//! @} imgproc_hist + +//! @addtogroup imgproc_subdiv2d +//! @{ + +class CV_EXPORTS_W Subdiv2D +{ +public: + /** Subdiv2D point location cases */ + enum { PTLOC_ERROR = -2, //!< Point location error + PTLOC_OUTSIDE_RECT = -1, //!< Point outside the subdivision bounding rect + PTLOC_INSIDE = 0, //!< Point inside some facet + PTLOC_VERTEX = 1, //!< Point coincides with one of the subdivision vertices + PTLOC_ON_EDGE = 2 //!< Point on some edge + }; + + /** Subdiv2D edge type navigation (see: getEdge()) */ + enum { NEXT_AROUND_ORG = 0x00, + NEXT_AROUND_DST = 0x22, + PREV_AROUND_ORG = 0x11, + PREV_AROUND_DST = 0x33, + NEXT_AROUND_LEFT = 0x13, + NEXT_AROUND_RIGHT = 0x31, + PREV_AROUND_LEFT = 0x20, + PREV_AROUND_RIGHT = 0x02 + }; + + /** creates an empty Subdiv2D object. + To create a new empty Delaunay subdivision you need to use the #initDelaunay function. + */ + CV_WRAP Subdiv2D(); + + /** @overload + + @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision. + + The function creates an empty Delaunay subdivision where 2D points can be added using the function + insert() . All of the points to be added must be within the specified rectangle, otherwise a runtime + error is raised. + */ + CV_WRAP Subdiv2D(Rect rect); + + /** @brief Creates a new empty Delaunay subdivision + + @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision. + + */ + CV_WRAP void initDelaunay(Rect rect); + + /** @brief Insert a single point into a Delaunay triangulation. + + @param pt Point to insert. + + The function inserts a single point into a subdivision and modifies the subdivision topology + appropriately. If a point with the same coordinates exists already, no new point is added. + @returns the ID of the point. + + @note If the point is outside of the triangulation specified rect a runtime error is raised. + */ + CV_WRAP int insert(Point2f pt); + + /** @brief Insert multiple points into a Delaunay triangulation. + + @param ptvec Points to insert. + + The function inserts a vector of points into a subdivision and modifies the subdivision topology + appropriately. + */ + CV_WRAP void insert(const std::vector& ptvec); + + /** @brief Returns the location of a point within a Delaunay triangulation. + + @param pt Point to locate. + @param edge Output edge that the point belongs to or is located to the right of it. + @param vertex Optional output vertex the input point coincides with. + + The function locates the input point within the subdivision and gives one of the triangle edges + or vertices. + + @returns an integer which specify one of the following five cases for point location: + - The point falls into some facet. The function returns #PTLOC_INSIDE and edge will contain one of + edges of the facet. + - The point falls onto the edge. The function returns #PTLOC_ON_EDGE and edge will contain this edge. + - The point coincides with one of the subdivision vertices. The function returns #PTLOC_VERTEX and + vertex will contain a pointer to the vertex. + - The point is outside the subdivision reference rectangle. The function returns #PTLOC_OUTSIDE_RECT + and no pointers are filled. + - One of input arguments is invalid. A runtime error is raised or, if silent or "parent" error + processing mode is selected, #PTLOC_ERROR is returned. + */ + CV_WRAP int locate(Point2f pt, CV_OUT int& edge, CV_OUT int& vertex); + + /** @brief Finds the subdivision vertex closest to the given point. + + @param pt Input point. + @param nearestPt Output subdivision vertex point. + + The function is another function that locates the input point within the subdivision. It finds the + subdivision vertex that is the closest to the input point. It is not necessarily one of vertices + of the facet containing the input point, though the facet (located using locate() ) is used as a + starting point. + + @returns vertex ID. + */ + CV_WRAP int findNearest(Point2f pt, CV_OUT Point2f* nearestPt = 0); + + /** @brief Returns a list of all edges. + + @param edgeList Output vector. + + The function gives each edge as a 4 numbers vector, where each two are one of the edge + vertices. i.e. org_x = v[0], org_y = v[1], dst_x = v[2], dst_y = v[3]. + */ + CV_WRAP void getEdgeList(CV_OUT std::vector& edgeList) const; + + /** @brief Returns a list of the leading edge ID connected to each triangle. + + @param leadingEdgeList Output vector. + + The function gives one edge ID for each triangle. + */ + CV_WRAP void getLeadingEdgeList(CV_OUT std::vector& leadingEdgeList) const; + + /** @brief Returns a list of all triangles. + + @param triangleList Output vector. + + The function gives each triangle as a 6 numbers vector, where each two are one of the triangle + vertices. i.e. p1_x = v[0], p1_y = v[1], p2_x = v[2], p2_y = v[3], p3_x = v[4], p3_y = v[5]. + */ + CV_WRAP void getTriangleList(CV_OUT std::vector& triangleList) const; + + /** @brief Returns a list of all Voronoi facets. + + @param idx Vector of vertices IDs to consider. For all vertices you can pass empty vector. + @param facetList Output vector of the Voronoi facets. + @param facetCenters Output vector of the Voronoi facets center points. + + */ + CV_WRAP void getVoronoiFacetList(const std::vector& idx, CV_OUT std::vector >& facetList, + CV_OUT std::vector& facetCenters); + + /** @brief Returns vertex location from vertex ID. + + @param vertex vertex ID. + @param firstEdge Optional. The first edge ID which is connected to the vertex. + @returns vertex (x,y) + + */ + CV_WRAP Point2f getVertex(int vertex, CV_OUT int* firstEdge = 0) const; + + /** @brief Returns one of the edges related to the given edge. + + @param edge Subdivision edge ID. + @param nextEdgeType Parameter specifying which of the related edges to return. + The following values are possible: + - NEXT_AROUND_ORG next around the edge origin ( eOnext on the picture below if e is the input edge) + - NEXT_AROUND_DST next around the edge vertex ( eDnext ) + - PREV_AROUND_ORG previous around the edge origin (reversed eRnext ) + - PREV_AROUND_DST previous around the edge destination (reversed eLnext ) + - NEXT_AROUND_LEFT next around the left facet ( eLnext ) + - NEXT_AROUND_RIGHT next around the right facet ( eRnext ) + - PREV_AROUND_LEFT previous around the left facet (reversed eOnext ) + - PREV_AROUND_RIGHT previous around the right facet (reversed eDnext ) + + ![sample output](pics/quadedge.png) + + @returns edge ID related to the input edge. + */ + CV_WRAP int getEdge( int edge, int nextEdgeType ) const; + + /** @brief Returns next edge around the edge origin. + + @param edge Subdivision edge ID. + + @returns an integer which is next edge ID around the edge origin: eOnext on the + picture above if e is the input edge). + */ + CV_WRAP int nextEdge(int edge) const; + + /** @brief Returns another edge of the same quad-edge. + + @param edge Subdivision edge ID. + @param rotate Parameter specifying which of the edges of the same quad-edge as the input + one to return. The following values are possible: + - 0 - the input edge ( e on the picture below if e is the input edge) + - 1 - the rotated edge ( eRot ) + - 2 - the reversed edge (reversed e (in green)) + - 3 - the reversed rotated edge (reversed eRot (in green)) + + @returns one of the edges ID of the same quad-edge as the input edge. + */ + CV_WRAP int rotateEdge(int edge, int rotate) const; + CV_WRAP int symEdge(int edge) const; + + /** @brief Returns the edge origin. + + @param edge Subdivision edge ID. + @param orgpt Output vertex location. + + @returns vertex ID. + */ + CV_WRAP int edgeOrg(int edge, CV_OUT Point2f* orgpt = 0) const; + + /** @brief Returns the edge destination. + + @param edge Subdivision edge ID. + @param dstpt Output vertex location. + + @returns vertex ID. + */ + CV_WRAP int edgeDst(int edge, CV_OUT Point2f* dstpt = 0) const; + +protected: + int newEdge(); + void deleteEdge(int edge); + int newPoint(Point2f pt, bool isvirtual, int firstEdge = 0); + void deletePoint(int vtx); + void setEdgePoints( int edge, int orgPt, int dstPt ); + void splice( int edgeA, int edgeB ); + int connectEdges( int edgeA, int edgeB ); + void swapEdges( int edge ); + int isRightOf(Point2f pt, int edge) const; + void calcVoronoi(); + void clearVoronoi(); + void checkSubdiv() const; + + struct CV_EXPORTS Vertex + { + Vertex(); + Vertex(Point2f pt, bool _isvirtual, int _firstEdge=0); + bool isvirtual() const; + bool isfree() const; + + int firstEdge; + int type; + Point2f pt; + }; + + struct CV_EXPORTS QuadEdge + { + QuadEdge(); + QuadEdge(int edgeidx); + bool isfree() const; + + int next[4]; + int pt[4]; + }; + + //! All of the vertices + std::vector vtx; + //! All of the edges + std::vector qedges; + int freeQEdge; + int freePoint; + bool validGeometry; + + int recentEdge; + //! Top left corner of the bounding rect + Point2f topLeft; + //! Bottom right corner of the bounding rect + Point2f bottomRight; +}; + +//! @} imgproc_subdiv2d + +//! @addtogroup imgproc_feature +//! @{ + +/** @brief Line segment detector class + +following the algorithm described at @cite Rafael12 . + +@note Implementation has been removed due original code license conflict + +*/ +class CV_EXPORTS_W LineSegmentDetector : public Algorithm +{ +public: + + /** @brief Finds lines in the input image. + + This is the output of the default parameters of the algorithm on the above shown image. + + ![image](pics/building_lsd.png) + + @param _image A grayscale (CV_8UC1) input image. If only a roi needs to be selected, use: + `lsd_ptr-\>detect(image(roi), lines, ...); lines += Scalar(roi.x, roi.y, roi.x, roi.y);` + @param _lines A vector of Vec4i or Vec4f elements specifying the beginning and ending point of a line. Where + Vec4i/Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. Returned lines are strictly + oriented depending on the gradient. + @param width Vector of widths of the regions, where the lines are found. E.g. Width of line. + @param prec Vector of precisions with which the lines are found. + @param nfa Vector containing number of false alarms in the line region, with precision of 10%. The + bigger the value, logarithmically better the detection. + - -1 corresponds to 10 mean false alarms + - 0 corresponds to 1 mean false alarm + - 1 corresponds to 0.1 mean false alarms + This vector will be calculated only when the objects type is #LSD_REFINE_ADV. + */ + CV_WRAP virtual void detect(InputArray _image, OutputArray _lines, + OutputArray width = noArray(), OutputArray prec = noArray(), + OutputArray nfa = noArray()) = 0; + + /** @brief Draws the line segments on a given image. + @param _image The image, where the lines will be drawn. Should be bigger or equal to the image, + where the lines were found. + @param lines A vector of the lines that needed to be drawn. + */ + CV_WRAP virtual void drawSegments(InputOutputArray _image, InputArray lines) = 0; + + /** @brief Draws two groups of lines in blue and red, counting the non overlapping (mismatching) pixels. + + @param size The size of the image, where lines1 and lines2 were found. + @param lines1 The first group of lines that needs to be drawn. It is visualized in blue color. + @param lines2 The second group of lines. They visualized in red color. + @param _image Optional image, where the lines will be drawn. The image should be color(3-channel) + in order for lines1 and lines2 to be drawn in the above mentioned colors. + */ + CV_WRAP virtual int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) = 0; + + virtual ~LineSegmentDetector() { } +}; + +/** @brief Creates a smart pointer to a LineSegmentDetector object and initializes it. + +The LineSegmentDetector algorithm is defined using the standard values. Only advanced users may want +to edit those, as to tailor it for their own application. + +@param _refine The way found lines will be refined, see #LineSegmentDetectorModes +@param _scale The scale of the image that will be used to find the lines. Range (0..1]. +@param _sigma_scale Sigma for Gaussian filter. It is computed as sigma = _sigma_scale/_scale. +@param _quant Bound to the quantization error on the gradient norm. +@param _ang_th Gradient angle tolerance in degrees. +@param _log_eps Detection threshold: -log10(NFA) \> log_eps. Used only when advance refinement +is chosen. +@param _density_th Minimal density of aligned region points in the enclosing rectangle. +@param _n_bins Number of bins in pseudo-ordering of gradient modulus. + +@note Implementation has been removed due original code license conflict + */ +CV_EXPORTS_W Ptr createLineSegmentDetector( + int _refine = LSD_REFINE_STD, double _scale = 0.8, + double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5, + double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024); + +//! @} imgproc_feature + +//! @addtogroup imgproc_filter +//! @{ + +/** @brief Returns Gaussian filter coefficients. + +The function computes and returns the \f$\texttt{ksize} \times 1\f$ matrix of Gaussian filter +coefficients: + +\f[G_i= \alpha *e^{-(i-( \texttt{ksize} -1)/2)^2/(2* \texttt{sigma}^2)},\f] + +where \f$i=0..\texttt{ksize}-1\f$ and \f$\alpha\f$ is the scale factor chosen so that \f$\sum_i G_i=1\f$. + +Two of such generated kernels can be passed to sepFilter2D. Those functions automatically recognize +smoothing kernels (a symmetrical kernel with sum of weights equal to 1) and handle them accordingly. +You may also use the higher-level GaussianBlur. +@param ksize Aperture size. It should be odd ( \f$\texttt{ksize} \mod 2 = 1\f$ ) and positive. +@param sigma Gaussian standard deviation. If it is non-positive, it is computed from ksize as +`sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`. +@param ktype Type of filter coefficients. It can be CV_32F or CV_64F . +@sa sepFilter2D, getDerivKernels, getStructuringElement, GaussianBlur + */ +CV_EXPORTS_W Mat getGaussianKernel( int ksize, double sigma, int ktype = CV_64F ); + +/** @brief Returns filter coefficients for computing spatial image derivatives. + +The function computes and returns the filter coefficients for spatial image derivatives. When +`ksize=CV_SCHARR`, the Scharr \f$3 \times 3\f$ kernels are generated (see #Scharr). Otherwise, Sobel +kernels are generated (see #Sobel). The filters are normally passed to #sepFilter2D or to + +@param kx Output matrix of row filter coefficients. It has the type ktype . +@param ky Output matrix of column filter coefficients. It has the type ktype . +@param dx Derivative order in respect of x. +@param dy Derivative order in respect of y. +@param ksize Aperture size. It can be CV_SCHARR, 1, 3, 5, or 7. +@param normalize Flag indicating whether to normalize (scale down) the filter coefficients or not. +Theoretically, the coefficients should have the denominator \f$=2^{ksize*2-dx-dy-2}\f$. If you are +going to filter floating-point images, you are likely to use the normalized kernels. But if you +compute derivatives of an 8-bit image, store the results in a 16-bit image, and wish to preserve +all the fractional bits, you may want to set normalize=false . +@param ktype Type of filter coefficients. It can be CV_32f or CV_64F . + */ +CV_EXPORTS_W void getDerivKernels( OutputArray kx, OutputArray ky, + int dx, int dy, int ksize, + bool normalize = false, int ktype = CV_32F ); + +/** @brief Returns Gabor filter coefficients. + +For more details about gabor filter equations and parameters, see: [Gabor +Filter](http://en.wikipedia.org/wiki/Gabor_filter). + +@param ksize Size of the filter returned. +@param sigma Standard deviation of the gaussian envelope. +@param theta Orientation of the normal to the parallel stripes of a Gabor function. +@param lambd Wavelength of the sinusoidal factor. +@param gamma Spatial aspect ratio. +@param psi Phase offset. +@param ktype Type of filter coefficients. It can be CV_32F or CV_64F . + */ +CV_EXPORTS_W Mat getGaborKernel( Size ksize, double sigma, double theta, double lambd, + double gamma, double psi = CV_PI*0.5, int ktype = CV_64F ); + +//! returns "magic" border value for erosion and dilation. It is automatically transformed to Scalar::all(-DBL_MAX) for dilation. +static inline Scalar morphologyDefaultBorderValue() { return Scalar::all(DBL_MAX); } + +/** @brief Returns a structuring element of the specified size and shape for morphological operations. + +The function constructs and returns the structuring element that can be further passed to #erode, +#dilate or #morphologyEx. But you can also construct an arbitrary binary mask yourself and use it as +the structuring element. + +@param shape Element shape that could be one of #MorphShapes +@param ksize Size of the structuring element. +@param anchor Anchor position within the element. The default value \f$(-1, -1)\f$ means that the +anchor is at the center. Note that only the shape of a cross-shaped element depends on the anchor +position. In other cases the anchor just regulates how much the result of the morphological +operation is shifted. + */ +CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1)); + +/** @example samples/cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp +Sample code for simple filters +![Sample screenshot](Smoothing_Tutorial_Result_Median_Filter.jpg) +Check @ref tutorial_gausian_median_blur_bilateral_filter "the corresponding tutorial" for more details + */ + +/** @brief Blurs an image using the median filter. + +The function smoothes an image using the median filter with the \f$\texttt{ksize} \times +\texttt{ksize}\f$ aperture. Each channel of a multi-channel image is processed independently. +In-place operation is supported. + +@note The median filter uses #BORDER_REPLICATE internally to cope with border pixels, see #BorderTypes + +@param src input 1-, 3-, or 4-channel image; when ksize is 3 or 5, the image depth should be +CV_8U, CV_16U, or CV_32F, for larger aperture sizes, it can only be CV_8U. +@param dst destination array of the same size and type as src. +@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ... +@sa bilateralFilter, blur, boxFilter, GaussianBlur + */ +CV_EXPORTS_W void medianBlur( InputArray src, OutputArray dst, int ksize ); + +/** @brief Blurs an image using a Gaussian filter. + +The function convolves the source image with the specified Gaussian kernel. In-place filtering is +supported. + +@param src input image; the image can have any number of channels, which are processed +independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be +positive and odd. Or, they can be zero's and then they are computed from sigma. +@param sigmaX Gaussian kernel standard deviation in X direction. +@param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be +equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height, +respectively (see #getGaussianKernel for details); to fully control the result regardless of +possible future modifications of all this semantics, it is recommended to specify all of ksize, +sigmaX, and sigmaY. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. + +@sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur + */ +CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize, + double sigmaX, double sigmaY = 0, + int borderType = BORDER_DEFAULT ); + +/** @brief Applies the bilateral filter to an image. + +The function applies bilateral filtering to the input image, as described in +http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html +bilateralFilter can reduce unwanted noise very well while keeping edges fairly sharp. However, it is +very slow compared to most filters. + +_Sigma values_: For simplicity, you can set the 2 sigma values to be the same. If they are small (\< +10), the filter will not have much effect, whereas if they are large (\> 150), they will have a very +strong effect, making the image look "cartoonish". + +_Filter size_: Large filters (d \> 5) are very slow, so it is recommended to use d=5 for real-time +applications, and perhaps d=9 for offline applications that need heavy noise filtering. + +This filter does not work inplace. +@param src Source 8-bit or floating-point, 1-channel or 3-channel image. +@param dst Destination image of the same size and type as src . +@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, +it is computed from sigmaSpace. +@param sigmaColor Filter sigma in the color space. A larger value of the parameter means that +farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting +in larger areas of semi-equal color. +@param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that +farther pixels will influence each other as long as their colors are close enough (see sigmaColor +). When d\>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is +proportional to sigmaSpace. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes + */ +CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d, + double sigmaColor, double sigmaSpace, + int borderType = BORDER_DEFAULT ); + +/** @brief Blurs an image using the box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \alpha \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \end{bmatrix}\f] + +where + +\f[\alpha = \fork{\frac{1}{\texttt{ksize.width*ksize.height}}}{when \texttt{normalize=true}}{1}{otherwise}\f] + +Unnormalized box filter is useful for computing various integral characteristics over each pixel +neighborhood, such as covariance matrices of image derivatives (used in dense optical flow +algorithms, and so on). If you need to compute pixel sums over variable-size windows, use #integral. + +@param src input image. +@param dst output image of the same size and type as src. +@param ddepth the output image depth (-1 to use src.depth()). +@param ksize blurring kernel size. +@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel +center. +@param normalize flag, specifying whether the kernel is normalized by its area or not. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa blur, bilateralFilter, GaussianBlur, medianBlur, integral + */ +CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth, + Size ksize, Point anchor = Point(-1,-1), + bool normalize = true, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the normalized sum of squares of the pixel values overlapping the filter. + +For every pixel \f$ (x, y) \f$ in the source image, the function calculates the sum of squares of those neighboring +pixel values which overlap the filter placed over the pixel \f$ (x, y) \f$. + +The unnormalized square box filter can be useful in computing local image statistics such as the the local +variance and standard deviation around the neighborhood of a pixel. + +@param src input image +@param dst output image of the same size and type as _src +@param ddepth the output image depth (-1 to use src.depth()) +@param ksize kernel size +@param anchor kernel anchor point. The default value of Point(-1, -1) denotes that the anchor is at the kernel +center. +@param normalize flag, specifying whether the kernel is to be normalized by it's area or not. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa boxFilter +*/ +CV_EXPORTS_W void sqrBoxFilter( InputArray src, OutputArray dst, int ddepth, + Size ksize, Point anchor = Point(-1, -1), + bool normalize = true, + int borderType = BORDER_DEFAULT ); + +/** @brief Blurs an image using the normalized box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \frac{1}{\texttt{ksize.width*ksize.height}} \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \end{bmatrix}\f] + +The call `blur(src, dst, ksize, anchor, borderType)` is equivalent to `boxFilter(src, dst, src.type(), +anchor, true, borderType)`. + +@param src input image; it can have any number of channels, which are processed independently, but +the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param ksize blurring kernel size. +@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel +center. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa boxFilter, bilateralFilter, GaussianBlur, medianBlur + */ +CV_EXPORTS_W void blur( InputArray src, OutputArray dst, + Size ksize, Point anchor = Point(-1,-1), + int borderType = BORDER_DEFAULT ); + +/** @brief Convolves an image with the kernel. + +The function applies an arbitrary linear filter to an image. In-place operation is supported. When +the aperture is partially outside the image, the function interpolates outlier pixel values +according to the specified border mode. + +The function does actually compute correlation, not the convolution: + +\f[\texttt{dst} (x,y) = \sum _{ \stackrel{0\leq x' < \texttt{kernel.cols},}{0\leq y' < \texttt{kernel.rows}} } \texttt{kernel} (x',y')* \texttt{src} (x+x'- \texttt{anchor.x} ,y+y'- \texttt{anchor.y} )\f] + +That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip +the kernel using #flip and set the new anchor to `(kernel.cols - anchor.x - 1, kernel.rows - +anchor.y - 1)`. + +The function uses the DFT-based algorithm in case of sufficiently large kernels (~`11 x 11` or +larger) and the direct algorithm for small kernels. + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param ddepth desired depth of the destination image, see @ref filter_depths "combinations" +@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point +matrix; if you want to apply different kernels to different channels, split the image into +separate color planes using split and process them individually. +@param anchor anchor of the kernel that indicates the relative position of a filtered point within +the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor +is at the kernel center. +@param delta optional value added to the filtered pixels before storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa sepFilter2D, dft, matchTemplate + */ +CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth, + InputArray kernel, Point anchor = Point(-1,-1), + double delta = 0, int borderType = BORDER_DEFAULT ); + +/** @brief Applies a separable linear filter to an image. + +The function applies a separable linear filter to the image. That is, first, every row of src is +filtered with the 1D kernel kernelX. Then, every column of the result is filtered with the 1D +kernel kernelY. The final result shifted by delta is stored in dst . + +@param src Source image. +@param dst Destination image of the same size and the same number of channels as src . +@param ddepth Destination image depth, see @ref filter_depths "combinations" +@param kernelX Coefficients for filtering each row. +@param kernelY Coefficients for filtering each column. +@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor +is at the kernel center. +@param delta Value added to the filtered results before storing them. +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa filter2D, Sobel, GaussianBlur, boxFilter, blur + */ +CV_EXPORTS_W void sepFilter2D( InputArray src, OutputArray dst, int ddepth, + InputArray kernelX, InputArray kernelY, + Point anchor = Point(-1,-1), + double delta = 0, int borderType = BORDER_DEFAULT ); + +/** @example samples/cpp/tutorial_code/ImgTrans/Sobel_Demo.cpp +Sample code using Sobel and/or Scharr OpenCV functions to make a simple Edge Detector +![Sample screenshot](Sobel_Derivatives_Tutorial_Result.jpg) +Check @ref tutorial_sobel_derivatives "the corresponding tutorial" for more details +*/ + +/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. + +In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to +calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$ +kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first +or the second x- or y- derivatives. + +There is also the special value `ksize = #CV_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr +filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is + +\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f] + +for the x-derivative, or transposed for the y-derivative. + +The function calculates an image derivative by convolving the image with the appropriate kernel: + +\f[\texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f] + +The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less +resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3) +or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first +case corresponds to a kernel of: + +\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f] + +The second case corresponds to a kernel of: + +\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f] + +@param src input image. +@param dst output image of the same size and the same number of channels as src . +@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of + 8-bit input images it will result in truncated derivatives. +@param dx order of the derivative x. +@param dy order of the derivative y. +@param ksize size of the extended Sobel kernel; it must be 1, 3, 5, or 7. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see #getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa Scharr, Laplacian, sepFilter2D, filter2D, GaussianBlur, cartToPolar + */ +CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, + int dx, int dy, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the first order image derivative in both x and y using a Sobel operator + +Equivalent to calling: + +@code +Sobel( src, dx, CV_16SC1, 1, 0, 3 ); +Sobel( src, dy, CV_16SC1, 0, 1, 3 ); +@endcode + +@param src input image. +@param dx output image with first-order derivative in x. +@param dy output image with first-order derivative in y. +@param ksize size of Sobel kernel. It must be 3. +@param borderType pixel extrapolation method, see #BorderTypes. + Only #BORDER_DEFAULT=#BORDER_REFLECT_101 and #BORDER_REPLICATE are supported. + +@sa Sobel + */ + +CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, + OutputArray dy, int ksize = 3, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the first x- or y- image derivative using Scharr operator. + +The function computes the first x- or y- spatial image derivative using the Scharr operator. The +call + +\f[\texttt{Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)}\f] + +is equivalent to + +\f[\texttt{Sobel(src, dst, ddepth, dx, dy, CV_SCHARR, scale, delta, borderType)} .\f] + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param ddepth output image depth, see @ref filter_depths "combinations" +@param dx order of the derivative x. +@param dy order of the derivative y. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see #getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa cartToPolar + */ +CV_EXPORTS_W void Scharr( InputArray src, OutputArray dst, int ddepth, + int dx, int dy, double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +/** @example samples/cpp/laplace.cpp +An example using Laplace transformations for edge detection +*/ + +/** @brief Calculates the Laplacian of an image. + +The function calculates the Laplacian of the source image by adding up the second x and y +derivatives calculated using the Sobel operator: + +\f[\texttt{dst} = \Delta \texttt{src} = \frac{\partial^2 \texttt{src}}{\partial x^2} + \frac{\partial^2 \texttt{src}}{\partial y^2}\f] + +This is done when `ksize > 1`. When `ksize == 1`, the Laplacian is computed by filtering the image +with the following \f$3 \times 3\f$ aperture: + +\f[\vecthreethree {0}{1}{0}{1}{-4}{1}{0}{1}{0}\f] + +@param src Source image. +@param dst Destination image of the same size and the same number of channels as src . +@param ddepth Desired depth of the destination image. +@param ksize Aperture size used to compute the second-derivative filters. See #getDerivKernels for +details. The size must be positive and odd. +@param scale Optional scale factor for the computed Laplacian values. By default, no scaling is +applied. See #getDerivKernels for details. +@param delta Optional delta value that is added to the results prior to storing them in dst . +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa Sobel, Scharr + */ +CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth, + int ksize = 1, double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_feature +//! @{ + +/** @example samples/cpp/edge.cpp +This program demonstrates usage of the Canny edge detector + +Check @ref tutorial_canny_detector "the corresponding tutorial" for more details +*/ + +/** @brief Finds edges in an image using the Canny algorithm @cite Canny86 . + +The function finds edges in the input image and marks them in the output map edges using the +Canny algorithm. The smallest value between threshold1 and threshold2 is used for edge linking. The +largest value is used to find initial segments of strong edges. See + + +@param image 8-bit input image. +@param edges output edge map; single channels 8-bit image, which has the same size as image . +@param threshold1 first threshold for the hysteresis procedure. +@param threshold2 second threshold for the hysteresis procedure. +@param apertureSize aperture size for the Sobel operator. +@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm +\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude ( +L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( +L2gradient=false ). + */ +CV_EXPORTS_W void Canny( InputArray image, OutputArray edges, + double threshold1, double threshold2, + int apertureSize = 3, bool L2gradient = false ); + +/** \overload + +Finds edges in an image using the Canny algorithm with custom image gradient. + +@param dx 16-bit x derivative of input image (CV_16SC1 or CV_16SC3). +@param dy 16-bit y derivative of input image (same type as dx). +@param edges output edge map; single channels 8-bit image, which has the same size as image . +@param threshold1 first threshold for the hysteresis procedure. +@param threshold2 second threshold for the hysteresis procedure. +@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm +\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude ( +L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( +L2gradient=false ). + */ +CV_EXPORTS_W void Canny( InputArray dx, InputArray dy, + OutputArray edges, + double threshold1, double threshold2, + bool L2gradient = false ); + +/** @brief Calculates the minimal eigenvalue of gradient matrices for corner detection. + +The function is similar to cornerEigenValsAndVecs but it calculates and stores only the minimal +eigenvalue of the covariance matrix of derivatives, that is, \f$\min(\lambda_1, \lambda_2)\f$ in terms +of the formulae in the cornerEigenValsAndVecs description. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the minimal eigenvalues. It has the type CV_32FC1 and the same size as +src . +@param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). +@param ksize Aperture parameter for the Sobel operator. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void cornerMinEigenVal( InputArray src, OutputArray dst, + int blockSize, int ksize = 3, + int borderType = BORDER_DEFAULT ); + +/** @brief Harris corner detector. + +The function runs the Harris corner detector on the image. Similarly to cornerMinEigenVal and +cornerEigenValsAndVecs , for each pixel \f$(x, y)\f$ it calculates a \f$2\times2\f$ gradient covariance +matrix \f$M^{(x,y)}\f$ over a \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood. Then, it +computes the following characteristic: + +\f[\texttt{dst} (x,y) = \mathrm{det} M^{(x,y)} - k \cdot \left ( \mathrm{tr} M^{(x,y)} \right )^2\f] + +Corners in the image can be found as the local maxima of this response map. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the Harris detector responses. It has the type CV_32FC1 and the same +size as src . +@param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). +@param ksize Aperture parameter for the Sobel operator. +@param k Harris detector free parameter. See the formula above. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize, + int ksize, double k, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates eigenvalues and eigenvectors of image blocks for corner detection. + +For every pixel \f$p\f$ , the function cornerEigenValsAndVecs considers a blockSize \f$\times\f$ blockSize +neighborhood \f$S(p)\f$ . It calculates the covariation matrix of derivatives over the neighborhood as: + +\f[M = \begin{bmatrix} \sum _{S(p)}(dI/dx)^2 & \sum _{S(p)}dI/dx dI/dy \\ \sum _{S(p)}dI/dx dI/dy & \sum _{S(p)}(dI/dy)^2 \end{bmatrix}\f] + +where the derivatives are computed using the Sobel operator. + +After that, it finds eigenvectors and eigenvalues of \f$M\f$ and stores them in the destination image as +\f$(\lambda_1, \lambda_2, x_1, y_1, x_2, y_2)\f$ where + +- \f$\lambda_1, \lambda_2\f$ are the non-sorted eigenvalues of \f$M\f$ +- \f$x_1, y_1\f$ are the eigenvectors corresponding to \f$\lambda_1\f$ +- \f$x_2, y_2\f$ are the eigenvectors corresponding to \f$\lambda_2\f$ + +The output of the function can be used for robust edge or corner detection. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the results. It has the same size as src and the type CV_32FC(6) . +@param blockSize Neighborhood size (see details below). +@param ksize Aperture parameter for the Sobel operator. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + +@sa cornerMinEigenVal, cornerHarris, preCornerDetect + */ +CV_EXPORTS_W void cornerEigenValsAndVecs( InputArray src, OutputArray dst, + int blockSize, int ksize, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates a feature map for corner detection. + +The function calculates the complex spatial derivative-based function of the source image + +\f[\texttt{dst} = (D_x \texttt{src} )^2 \cdot D_{yy} \texttt{src} + (D_y \texttt{src} )^2 \cdot D_{xx} \texttt{src} - 2 D_x \texttt{src} \cdot D_y \texttt{src} \cdot D_{xy} \texttt{src}\f] + +where \f$D_x\f$,\f$D_y\f$ are the first image derivatives, \f$D_{xx}\f$,\f$D_{yy}\f$ are the second image +derivatives, and \f$D_{xy}\f$ is the mixed derivative. + +The corners can be found as local maximums of the functions, as shown below: +@code + Mat corners, dilated_corners; + preCornerDetect(image, corners, 3); + // dilation with 3x3 rectangular structuring element + dilate(corners, dilated_corners, Mat(), 1); + Mat corner_mask = corners == dilated_corners; +@endcode + +@param src Source single-channel 8-bit of floating-point image. +@param dst Output image that has the type CV_32F and the same size as src . +@param ksize %Aperture size of the Sobel . +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void preCornerDetect( InputArray src, OutputArray dst, int ksize, + int borderType = BORDER_DEFAULT ); + +/** @brief Refines the corner locations. + +The function iterates to find the sub-pixel accurate location of corners or radial saddle points, as +shown on the figure below. + +![image](pics/cornersubpix.png) + +Sub-pixel accurate corner locator is based on the observation that every vector from the center \f$q\f$ +to a point \f$p\f$ located within a neighborhood of \f$q\f$ is orthogonal to the image gradient at \f$p\f$ +subject to image and measurement noise. Consider the expression: + +\f[\epsilon _i = {DI_{p_i}}^T \cdot (q - p_i)\f] + +where \f${DI_{p_i}}\f$ is an image gradient at one of the points \f$p_i\f$ in a neighborhood of \f$q\f$ . The +value of \f$q\f$ is to be found so that \f$\epsilon_i\f$ is minimized. A system of equations may be set up +with \f$\epsilon_i\f$ set to zero: + +\f[\sum _i(DI_{p_i} \cdot {DI_{p_i}}^T) \cdot q - \sum _i(DI_{p_i} \cdot {DI_{p_i}}^T \cdot p_i)\f] + +where the gradients are summed within a neighborhood ("search window") of \f$q\f$ . Calling the first +gradient term \f$G\f$ and the second gradient term \f$b\f$ gives: + +\f[q = G^{-1} \cdot b\f] + +The algorithm sets the center of the neighborhood window at this new center \f$q\f$ and then iterates +until the center stays within a set threshold. + +@param image Input single-channel, 8-bit or float image. +@param corners Initial coordinates of the input corners and refined coordinates provided for +output. +@param winSize Half of the side length of the search window. For example, if winSize=Size(5,5) , +then a \f$(5*2+1) \times (5*2+1) = 11 \times 11\f$ search window is used. +@param zeroZone Half of the size of the dead region in the middle of the search zone over which +the summation in the formula below is not done. It is used sometimes to avoid possible +singularities of the autocorrelation matrix. The value of (-1,-1) indicates that there is no such +a size. +@param criteria Criteria for termination of the iterative process of corner refinement. That is, +the process of corner position refinement stops either after criteria.maxCount iterations or when +the corner position moves by less than criteria.epsilon on some iteration. + */ +CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners, + Size winSize, Size zeroZone, + TermCriteria criteria ); + +/** @brief Determines strong corners on an image. + +The function finds the most prominent corners in the image or in the specified image region, as +described in @cite Shi94 + +- Function calculates the corner quality measure at every source image pixel using the + #cornerMinEigenVal or #cornerHarris . +- Function performs a non-maximum suppression (the local maximums in *3 x 3* neighborhood are + retained). +- The corners with the minimal eigenvalue less than + \f$\texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y)\f$ are rejected. +- The remaining corners are sorted by the quality measure in the descending order. +- Function throws away each corner for which there is a stronger corner at a distance less than + maxDistance. + +The function can be used to initialize a point-based tracker of an object. + +@note If the function is called with different values A and B of the parameter qualityLevel , and +A \> B, the vector of returned corners with qualityLevel=A will be the prefix of the output vector +with qualityLevel=B . + +@param image Input 8-bit or floating-point 32-bit, single-channel image. +@param corners Output vector of detected corners. +@param maxCorners Maximum number of corners to return. If there are more corners than are found, +the strongest of them is returned. `maxCorners <= 0` implies that no limit on the maximum is set +and all detected corners are returned. +@param qualityLevel Parameter characterizing the minimal accepted quality of image corners. The +parameter value is multiplied by the best corner quality measure, which is the minimal eigenvalue +(see #cornerMinEigenVal ) or the Harris function response (see #cornerHarris ). The corners with the +quality measure less than the product are rejected. For example, if the best corner has the +quality measure = 1500, and the qualityLevel=0.01 , then all the corners with the quality measure +less than 15 are rejected. +@param minDistance Minimum possible Euclidean distance between the returned corners. +@param mask Optional region of interest. If the image is not empty (it needs to have the type +CV_8UC1 and the same size as image ), it specifies the region in which the corners are detected. +@param blockSize Size of an average block for computing a derivative covariation matrix over each +pixel neighborhood. See cornerEigenValsAndVecs . +@param useHarrisDetector Parameter indicating whether to use a Harris detector (see #cornerHarris) +or #cornerMinEigenVal. +@param k Free parameter of the Harris detector. + +@sa cornerMinEigenVal, cornerHarris, calcOpticalFlowPyrLK, estimateRigidTransform, + */ + +CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners, + int maxCorners, double qualityLevel, double minDistance, + InputArray mask = noArray(), int blockSize = 3, + bool useHarrisDetector = false, double k = 0.04 ); + +CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners, + int maxCorners, double qualityLevel, double minDistance, + InputArray mask, int blockSize, + int gradientSize, bool useHarrisDetector = false, + double k = 0.04 ); +/** @example samples/cpp/tutorial_code/ImgTrans/houghlines.cpp +An example using the Hough line detector +![Sample input image](Hough_Lines_Tutorial_Original_Image.jpg) ![Output image](Hough_Lines_Tutorial_Result.jpg) +*/ + +/** @brief Finds lines in a binary image using the standard Hough transform. + +The function implements the standard or standard multi-scale Hough transform algorithm for line +detection. See for a good explanation of Hough +transform. + +@param image 8-bit, single-channel binary source image. The image may be modified by the function. +@param lines Output vector of lines. Each line is represented by a 2 or 3 element vector +\f$(\rho, \theta)\f$ or \f$(\rho, \theta, \textrm{votes})\f$ . \f$\rho\f$ is the distance from the coordinate origin \f$(0,0)\f$ (top-left corner of +the image). \f$\theta\f$ is the line rotation angle in radians ( +\f$0 \sim \textrm{vertical line}, \pi/2 \sim \textrm{horizontal line}\f$ ). +\f$\textrm{votes}\f$ is the value of accumulator. +@param rho Distance resolution of the accumulator in pixels. +@param theta Angle resolution of the accumulator in radians. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ). +@param srn For the multi-scale Hough transform, it is a divisor for the distance resolution rho . +The coarse accumulator distance resolution is rho and the accurate accumulator resolution is +rho/srn . If both srn=0 and stn=0 , the classical Hough transform is used. Otherwise, both these +parameters should be positive. +@param stn For the multi-scale Hough transform, it is a divisor for the distance resolution theta. +@param min_theta For standard and multi-scale Hough transform, minimum angle to check for lines. +Must fall between 0 and max_theta. +@param max_theta For standard and multi-scale Hough transform, maximum angle to check for lines. +Must fall between min_theta and CV_PI. + */ +CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, + double rho, double theta, int threshold, + double srn = 0, double stn = 0, + double min_theta = 0, double max_theta = CV_PI ); + +/** @brief Finds line segments in a binary image using the probabilistic Hough transform. + +The function implements the probabilistic Hough transform algorithm for line detection, described +in @cite Matas00 + +See the line detection example below: +@include snippets/imgproc_HoughLinesP.cpp +This is a sample picture the function parameters have been tuned for: + +![image](pics/building.jpg) + +And this is the output of the above program in case of the probabilistic Hough transform: + +![image](pics/houghp.png) + +@param image 8-bit, single-channel binary source image. The image may be modified by the function. +@param lines Output vector of lines. Each line is represented by a 4-element vector +\f$(x_1, y_1, x_2, y_2)\f$ , where \f$(x_1,y_1)\f$ and \f$(x_2, y_2)\f$ are the ending points of each detected +line segment. +@param rho Distance resolution of the accumulator in pixels. +@param theta Angle resolution of the accumulator in radians. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ). +@param minLineLength Minimum line length. Line segments shorter than that are rejected. +@param maxLineGap Maximum allowed gap between points on the same line to link them. + +@sa LineSegmentDetector + */ +CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines, + double rho, double theta, int threshold, + double minLineLength = 0, double maxLineGap = 0 ); + +/** @brief Finds lines in a set of points using the standard Hough transform. + +The function finds lines in a set of points using a modification of the Hough transform. +@include snippets/imgproc_HoughLinesPointSet.cpp +@param _point Input vector of points. Each vector must be encoded as a Point vector \f$(x,y)\f$. Type must be CV_32FC2 or CV_32SC2. +@param _lines Output vector of found lines. Each vector is encoded as a vector \f$(votes, rho, theta)\f$. +The larger the value of 'votes', the higher the reliability of the Hough line. +@param lines_max Max count of hough lines. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ) +@param min_rho Minimum Distance value of the accumulator in pixels. +@param max_rho Maximum Distance value of the accumulator in pixels. +@param rho_step Distance resolution of the accumulator in pixels. +@param min_theta Minimum angle value of the accumulator in radians. +@param max_theta Maximum angle value of the accumulator in radians. +@param theta_step Angle resolution of the accumulator in radians. + */ +CV_EXPORTS_W void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, int threshold, + double min_rho, double max_rho, double rho_step, + double min_theta, double max_theta, double theta_step ); + +/** @example samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp +An example using the Hough circle detector +*/ + +/** @brief Finds circles in a grayscale image using the Hough transform. + +The function finds circles in a grayscale image using a modification of the Hough transform. + +Example: : +@include snippets/imgproc_HoughLinesCircles.cpp + +@note Usually the function detects the centers of circles well. However, it may fail to find correct +radii. You can assist to the function by specifying the radius range ( minRadius and maxRadius ) if +you know it. Or, you may set maxRadius to a negative number to return centers only without radius +search, and find the correct radius using an additional procedure. + +@param image 8-bit, single-channel, grayscale input image. +@param circles Output vector of found circles. Each vector is encoded as 3 or 4 element +floating-point vector \f$(x, y, radius)\f$ or \f$(x, y, radius, votes)\f$ . +@param method Detection method, see #HoughModes. Currently, the only implemented method is #HOUGH_GRADIENT +@param dp Inverse ratio of the accumulator resolution to the image resolution. For example, if +dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has +half as big width and height. +@param minDist Minimum distance between the centers of the detected circles. If the parameter is +too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is +too large, some circles may be missed. +@param param1 First method-specific parameter. In case of #HOUGH_GRADIENT , it is the higher +threshold of the two passed to the Canny edge detector (the lower one is twice smaller). +@param param2 Second method-specific parameter. In case of #HOUGH_GRADIENT , it is the +accumulator threshold for the circle centers at the detection stage. The smaller it is, the more +false circles may be detected. Circles, corresponding to the larger accumulator values, will be +returned first. +@param minRadius Minimum circle radius. +@param maxRadius Maximum circle radius. If <= 0, uses the maximum image dimension. If < 0, returns +centers without finding the radius. + +@sa fitEllipse, minEnclosingCircle + */ +CV_EXPORTS_W void HoughCircles( InputArray image, OutputArray circles, + int method, double dp, double minDist, + double param1 = 100, double param2 = 100, + int minRadius = 0, int maxRadius = 0 ); + +//! @} imgproc_feature + +//! @addtogroup imgproc_filter +//! @{ + +/** @example samples/cpp/tutorial_code/ImgProc/Morphology_2.cpp +Advanced morphology Transformations sample code +![Sample screenshot](Morphology_2_Tutorial_Result.jpg) +Check @ref tutorial_opening_closing_hats "the corresponding tutorial" for more details +*/ + +/** @brief Erodes an image by using a specific structuring element. + +The function erodes the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the minimum is taken: + +\f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +The function supports the in-place mode. Erosion can be applied several ( iterations ) times. In +case of multi-channel images, each channel is processed independently. + +@param src input image; the number of channels can be arbitrary, but the depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param kernel structuring element used for erosion; if `element=Mat()`, a `3 x 3` rectangular +structuring element is used. Kernel can be created using #getStructuringElement. +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times erosion is applied. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@param borderValue border value in case of a constant border +@sa dilate, morphologyEx, getStructuringElement + */ +CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +/** @example samples/cpp/tutorial_code/ImgProc/Morphology_1.cpp +Erosion and Dilation sample code +![Sample Screenshot-Erosion](Morphology_1_Tutorial_Erosion_Result.jpg)![Sample Screenshot-Dilation](Morphology_1_Tutorial_Dilation_Result.jpg) +Check @ref tutorial_erosion_dilatation "the corresponding tutorial" for more details +*/ + +/** @brief Dilates an image by using a specific structuring element. + +The function dilates the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the maximum is taken: +\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +The function supports the in-place mode. Dilation can be applied several ( iterations ) times. In +case of multi-channel images, each channel is processed independently. + +@param src input image; the number of channels can be arbitrary, but the depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param kernel structuring element used for dilation; if elemenat=Mat(), a 3 x 3 rectangular +structuring element is used. Kernel can be created using #getStructuringElement +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times dilation is applied. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not suported. +@param borderValue border value in case of a constant border +@sa erode, morphologyEx, getStructuringElement + */ +CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +/** @brief Performs advanced morphological transformations. + +The function cv::morphologyEx can perform advanced morphological transformations using an erosion and dilation as +basic operations. + +Any of the operations can be done in-place. In case of multi-channel images, each channel is +processed independently. + +@param src Source image. The number of channels can be arbitrary. The depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst Destination image of the same size and type as source image. +@param op Type of a morphological operation, see #MorphTypes +@param kernel Structuring element. It can be created using #getStructuringElement. +@param anchor Anchor position with the kernel. Negative values mean that the anchor is at the +kernel center. +@param iterations Number of times erosion and dilation are applied. +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@param borderValue Border value in case of a constant border. The default value has a special +meaning. +@sa dilate, erode, getStructuringElement +@note The number of iterations is the number of times erosion or dilatation operation will be applied. +For instance, an opening operation (#MORPH_OPEN) with two iterations is equivalent to apply +successively: erode -> erode -> dilate -> dilate (and not erode -> dilate -> erode -> dilate). + */ +CV_EXPORTS_W void morphologyEx( InputArray src, OutputArray dst, + int op, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +/** @brief Resizes an image. + +The function resize resizes the image src down to or up to the specified size. Note that the +initial dst type or size are not taken into account. Instead, the size and type are derived from +the `src`,`dsize`,`fx`, and `fy`. If you want to resize src so that it fits the pre-created dst, +you may call the function as follows: +@code + // explicitly specify dsize=dst.size(); fx and fy will be computed from that. + resize(src, dst, dst.size(), 0, 0, interpolation); +@endcode +If you want to decimate the image by factor of 2 in each direction, you can call the function this +way: +@code + // specify fx and fy and let the function compute the destination image size. + resize(src, dst, Size(), 0.5, 0.5, interpolation); +@endcode +To shrink an image, it will generally look best with #INTER_AREA interpolation, whereas to +enlarge an image, it will generally look best with c#INTER_CUBIC (slow) or #INTER_LINEAR +(faster but still looks OK). + +@param src input image. +@param dst output image; it has the size dsize (when it is non-zero) or the size computed from +src.size(), fx, and fy; the type of dst is the same as of src. +@param dsize output image size; if it equals zero, it is computed as: + \f[\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}\f] + Either dsize or both fx and fy must be non-zero. +@param fx scale factor along the horizontal axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.width/src.cols}\f] +@param fy scale factor along the vertical axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.height/src.rows}\f] +@param interpolation interpolation method, see #InterpolationFlags + +@sa warpAffine, warpPerspective, remap + */ +CV_EXPORTS_W void resize( InputArray src, OutputArray dst, + Size dsize, double fx = 0, double fy = 0, + int interpolation = INTER_LINEAR ); + +/** @brief Applies an affine transformation to an image. + +The function warpAffine transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23})\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted +with #invertAffineTransform and then put in the formula above instead of M. The function cannot +operate in-place. + +@param src input image. +@param dst output image that has the size dsize and the same type as src . +@param M \f$2\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (see #InterpolationFlags) and the optional +flag #WARP_INVERSE_MAP that means that M is the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (see #BorderTypes); when +borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image corresponding to +the "outliers" in the source image are not modified by the function. +@param borderValue value used in case of a constant border; by default, it is 0. + +@sa warpPerspective, resize, remap, getRectSubPix, transform + */ +CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst, + InputArray M, Size dsize, + int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @example samples/cpp/warpPerspective_demo.cpp +An example program shows using cv::findHomography and cv::warpPerspective for image warping +*/ + +/** @brief Applies a perspective transformation to an image. + +The function warpPerspective transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , + \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right )\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted with invert +and then put in the formula above instead of M. The function cannot operate in-place. + +@param src input image. +@param dst output image that has the size dsize and the same type as src . +@param M \f$3\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (#INTER_LINEAR or #INTER_NEAREST) and the +optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (#BORDER_CONSTANT or #BORDER_REPLICATE). +@param borderValue value used in case of a constant border; by default, it equals 0. + +@sa warpAffine, resize, remap, getRectSubPix, perspectiveTransform + */ +CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst, + InputArray M, Size dsize, + int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @brief Applies a generic geometrical transformation to an image. + +The function remap transforms the source image using the specified map: + +\f[\texttt{dst} (x,y) = \texttt{src} (map_x(x,y),map_y(x,y))\f] + +where values of pixels with non-integer coordinates are computed using one of available +interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps +in \f$map_1\f$ and \f$map_2\f$ respectively, or interleaved floating-point maps of \f$(x,y)\f$ in +\f$map_1\f$, or fixed-point maps created by using convertMaps. The reason you might want to +convert from floating to fixed-point representations of a map is that they can yield much faster +(\~2x) remapping operations. In the converted case, \f$map_1\f$ contains pairs (cvFloor(x), +cvFloor(y)) and \f$map_2\f$ contains indices in a table of interpolation coefficients. + +This function cannot operate in-place. + +@param src Source image. +@param dst Destination image. It has the same size as map1 and the same type as src . +@param map1 The first map of either (x,y) points or just x values having the type CV_16SC2 , +CV_32FC1, or CV_32FC2. See convertMaps for details on converting a floating point +representation to fixed-point for speed. +@param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map +if map1 is (x,y) points), respectively. +@param interpolation Interpolation method (see #InterpolationFlags). The method #INTER_AREA is +not supported by this function. +@param borderMode Pixel extrapolation method (see #BorderTypes). When +borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image that +corresponds to the "outliers" in the source image are not modified by the function. +@param borderValue Value used in case of a constant border. By default, it is 0. +@note +Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + */ +CV_EXPORTS_W void remap( InputArray src, OutputArray dst, + InputArray map1, InputArray map2, + int interpolation, int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @brief Converts image transformation maps from one representation to another. + +The function converts a pair of maps for remap from one representation to another. The following +options ( (map1.type(), map2.type()) \f$\rightarrow\f$ (dstmap1.type(), dstmap2.type()) ) are +supported: + +- \f$\texttt{(CV_32FC1, CV_32FC1)} \rightarrow \texttt{(CV_16SC2, CV_16UC1)}\f$. This is the +most frequently used conversion operation, in which the original floating-point maps (see remap ) +are converted to a more compact and much faster fixed-point representation. The first output array +contains the rounded coordinates and the second array (created only when nninterpolation=false ) +contains indices in the interpolation tables. + +- \f$\texttt{(CV_32FC2)} \rightarrow \texttt{(CV_16SC2, CV_16UC1)}\f$. The same as above but +the original maps are stored in one 2-channel matrix. + +- Reverse conversion. Obviously, the reconstructed floating-point maps will not be exactly the same +as the originals. + +@param map1 The first input map of type CV_16SC2, CV_32FC1, or CV_32FC2 . +@param map2 The second input map of type CV_16UC1, CV_32FC1, or none (empty matrix), +respectively. +@param dstmap1 The first output map that has the type dstmap1type and the same size as src . +@param dstmap2 The second output map. +@param dstmap1type Type of the first output map that should be CV_16SC2, CV_32FC1, or +CV_32FC2 . +@param nninterpolation Flag indicating whether the fixed-point maps are used for the +nearest-neighbor or for a more complex interpolation. + +@sa remap, undistort, initUndistortRectifyMap + */ +CV_EXPORTS_W void convertMaps( InputArray map1, InputArray map2, + OutputArray dstmap1, OutputArray dstmap2, + int dstmap1type, bool nninterpolation = false ); + +/** @brief Calculates an affine matrix of 2D rotation. + +The function calculates the following matrix: + +\f[\begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot \texttt{center.x} - \beta \cdot \texttt{center.y} \\ - \beta & \alpha & \beta \cdot \texttt{center.x} + (1- \alpha ) \cdot \texttt{center.y} \end{bmatrix}\f] + +where + +\f[\begin{array}{l} \alpha = \texttt{scale} \cdot \cos \texttt{angle} , \\ \beta = \texttt{scale} \cdot \sin \texttt{angle} \end{array}\f] + +The transformation maps the rotation center to itself. If this is not the target, adjust the shift. + +@param center Center of the rotation in the source image. +@param angle Rotation angle in degrees. Positive values mean counter-clockwise rotation (the +coordinate origin is assumed to be the top-left corner). +@param scale Isotropic scale factor. + +@sa getAffineTransform, warpAffine, transform + */ +CV_EXPORTS_W Mat getRotationMatrix2D( Point2f center, double angle, double scale ); + +//! returns 3x3 perspective transformation for the corresponding 4 point pairs. +CV_EXPORTS Mat getPerspectiveTransform( const Point2f src[], const Point2f dst[] ); + +/** @brief Calculates an affine transform from three pairs of the corresponding points. + +The function calculates the \f$2 \times 3\f$ matrix of an affine transform so that: + +\f[\begin{bmatrix} x'_i \\ y'_i \end{bmatrix} = \texttt{map_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\f] + +where + +\f[dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2\f] + +@param src Coordinates of triangle vertices in the source image. +@param dst Coordinates of the corresponding triangle vertices in the destination image. + +@sa warpAffine, transform + */ +CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] ); + +/** @brief Inverts an affine transformation. + +The function computes an inverse affine transformation represented by \f$2 \times 3\f$ matrix M: + +\f[\begin{bmatrix} a_{11} & a_{12} & b_1 \\ a_{21} & a_{22} & b_2 \end{bmatrix}\f] + +The result is also a \f$2 \times 3\f$ matrix of the same type as M. + +@param M Original affine transformation. +@param iM Output reverse affine transformation. + */ +CV_EXPORTS_W void invertAffineTransform( InputArray M, OutputArray iM ); + +/** @brief Calculates a perspective transform from four pairs of the corresponding points. + +The function calculates the \f$3 \times 3\f$ matrix of a perspective transform so that: + +\f[\begin{bmatrix} t_i x'_i \\ t_i y'_i \\ t_i \end{bmatrix} = \texttt{map_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\f] + +where + +\f[dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2,3\f] + +@param src Coordinates of quadrangle vertices in the source image. +@param dst Coordinates of the corresponding quadrangle vertices in the destination image. + +@sa findHomography, warpPerspective, perspectiveTransform + */ +CV_EXPORTS_W Mat getPerspectiveTransform( InputArray src, InputArray dst ); + +CV_EXPORTS_W Mat getAffineTransform( InputArray src, InputArray dst ); + +/** @brief Retrieves a pixel rectangle from an image with sub-pixel accuracy. + +The function getRectSubPix extracts pixels from src: + +\f[patch(x, y) = src(x + \texttt{center.x} - ( \texttt{dst.cols} -1)*0.5, y + \texttt{center.y} - ( \texttt{dst.rows} -1)*0.5)\f] + +where the values of the pixels at non-integer coordinates are retrieved using bilinear +interpolation. Every channel of multi-channel images is processed independently. Also +the image should be a single channel or three channel image. While the center of the +rectangle must be inside the image, parts of the rectangle may be outside. + +@param image Source image. +@param patchSize Size of the extracted patch. +@param center Floating point coordinates of the center of the extracted rectangle within the +source image. The center must be inside the image. +@param patch Extracted patch that has the size patchSize and the same number of channels as src . +@param patchType Depth of the extracted pixels. By default, they have the same depth as src . + +@sa warpAffine, warpPerspective + */ +CV_EXPORTS_W void getRectSubPix( InputArray image, Size patchSize, + Point2f center, OutputArray patch, int patchType = -1 ); + +/** @example samples/cpp/polar_transforms.cpp +An example using the cv::linearPolar and cv::logPolar operations +*/ + +/** @brief Remaps an image to semilog-polar coordinates space. + +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags+WARP_POLAR_LOG); + +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image d)"): +\f[\begin{array}{l} + dst( \rho , \phi ) = src(x,y) \\ + dst.size() \leftarrow src.size() +\end{array}\f] + +where +\f[\begin{array}{l} + I = (dx,dy) = (x - center.x,y - center.y) \\ + \rho = M \cdot log_e(\texttt{magnitude} (I)) ,\\ + \phi = Kangle \cdot \texttt{angle} (I) \\ +\end{array}\f] + +and +\f[\begin{array}{l} + M = src.cols / log_e(maxRadius) \\ + Kangle = src.rows / 2\Pi \\ +\end{array}\f] + +The function emulates the human "foveal" vision and can be used for fast scale and +rotation-invariant template matching, for object tracking and so forth. +@param src Source image +@param dst Destination image. It will have same size and type as src. +@param center The transformation center; where the output precision is maximal +@param M Magnitude scale parameter. It determines the radius of the bounding circle to transform too. +@param flags A combination of interpolation methods, see #InterpolationFlags + +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. + +@sa cv::linearPolar +@endinternal +*/ +CV_EXPORTS_W void logPolar( InputArray src, OutputArray dst, + Point2f center, double M, int flags ); + +/** @brief Remaps an image to polar coordinates space. + +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags) + +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image c)"): +\f[\begin{array}{l} + dst( \rho , \phi ) = src(x,y) \\ + dst.size() \leftarrow src.size() +\end{array}\f] + +where +\f[\begin{array}{l} + I = (dx,dy) = (x - center.x,y - center.y) \\ + \rho = Kmag \cdot \texttt{magnitude} (I) ,\\ + \phi = angle \cdot \texttt{angle} (I) +\end{array}\f] + +and +\f[\begin{array}{l} + Kx = src.cols / maxRadius \\ + Ky = src.rows / 2\Pi +\end{array}\f] + + +@param src Source image +@param dst Destination image. It will have same size and type as src. +@param center The transformation center; +@param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. +@param flags A combination of interpolation methods, see #InterpolationFlags + +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. + +@sa cv::logPolar +@endinternal +*/ +CV_EXPORTS_W void linearPolar( InputArray src, OutputArray dst, + Point2f center, double maxRadius, int flags ); + + +/** \brief Remaps an image to polar or semilog-polar coordinates space + +@anchor polar_remaps_reference_image +![Polar remaps reference](pics/polar_remap_doc.png) + +Transform the source image using the following transformation: +\f[ +dst(\rho , \phi ) = src(x,y) +\f] + +where +\f[ +\begin{array}{l} +\vec{I} = (x - center.x, \;y - center.y) \\ +\phi = Kangle \cdot \texttt{angle} (\vec{I}) \\ +\rho = \left\{\begin{matrix} +Klin \cdot \texttt{magnitude} (\vec{I}) & default \\ +Klog \cdot log_e(\texttt{magnitude} (\vec{I})) & if \; semilog \\ +\end{matrix}\right. +\end{array} +\f] + +and +\f[ +\begin{array}{l} +Kangle = dsize.height / 2\Pi \\ +Klin = dsize.width / maxRadius \\ +Klog = dsize.width / log_e(maxRadius) \\ +\end{array} +\f] + + +\par Linear vs semilog mapping + +Polar mapping can be linear or semi-log. Add one of #WarpPolarMode to `flags` to specify the polar mapping mode. + +Linear is the default mode. + +The semilog mapping emulates the human "foveal" vision that permit very high acuity on the line of sight (central vision) +in contrast to peripheral vision where acuity is minor. + +\par Option on `dsize`: + +- if both values in `dsize <=0 ` (default), +the destination image will have (almost) same area of source bounding circle: +\f[\begin{array}{l} +dsize.area \leftarrow (maxRadius^2 \cdot \Pi) \\ +dsize.width = \texttt{cvRound}(maxRadius) \\ +dsize.height = \texttt{cvRound}(maxRadius \cdot \Pi) \\ +\end{array}\f] + + +- if only `dsize.height <= 0`, +the destination image area will be proportional to the bounding circle area but scaled by `Kx * Kx`: +\f[\begin{array}{l} +dsize.height = \texttt{cvRound}(dsize.width \cdot \Pi) \\ +\end{array} +\f] + +- if both values in `dsize > 0 `, +the destination image will have the given size therefore the area of the bounding circle will be scaled to `dsize`. + + +\par Reverse mapping + +You can get reverse mapping adding #WARP_INVERSE_MAP to `flags` +\snippet polar_transforms.cpp InverseMap + +In addiction, to calculate the original coordinate from a polar mapped coordinate \f$(rho, phi)->(x, y)\f$: +\snippet polar_transforms.cpp InverseCoordinate + +@param src Source image. +@param dst Destination image. It will have same type as src. +@param dsize The destination image size (see description for valid options). +@param center The transformation center. +@param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. +@param flags A combination of interpolation methods, #InterpolationFlags + #WarpPolarMode. + - Add #WARP_POLAR_LINEAR to select linear polar mapping (default) + - Add #WARP_POLAR_LOG to select semilog polar mapping + - Add #WARP_INVERSE_MAP for reverse mapping. +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. +- This function uses #remap. Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + +@sa cv::remap +*/ +CV_EXPORTS_W void warpPolar(InputArray src, OutputArray dst, Size dsize, + Point2f center, double maxRadius, int flags); + + +//! @} imgproc_transform + +//! @addtogroup imgproc_misc +//! @{ + +/** @overload */ +CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = -1 ); + +/** @overload */ +CV_EXPORTS_AS(integral2) void integral( InputArray src, OutputArray sum, + OutputArray sqsum, int sdepth = -1, int sqdepth = -1 ); + +/** @brief Calculates the integral of an image. + +The function calculates one or more integral images for the source image as follows: + +\f[\texttt{sum} (X,Y) = \sum _{x + +Calculates the cross-power spectrum of two supplied source arrays. The arrays are padded if needed +with getOptimalDFTSize. + +The function performs the following equations: +- First it applies a Hanning window (see ) to each +image to remove possible edge effects. This window is cached until the array size changes to speed +up processing time. +- Next it computes the forward DFTs of each source array: +\f[\mathbf{G}_a = \mathcal{F}\{src_1\}, \; \mathbf{G}_b = \mathcal{F}\{src_2\}\f] +where \f$\mathcal{F}\f$ is the forward DFT. +- It then computes the cross-power spectrum of each frequency domain array: +\f[R = \frac{ \mathbf{G}_a \mathbf{G}_b^*}{|\mathbf{G}_a \mathbf{G}_b^*|}\f] +- Next the cross-correlation is converted back into the time domain via the inverse DFT: +\f[r = \mathcal{F}^{-1}\{R\}\f] +- Finally, it computes the peak location and computes a 5x5 weighted centroid around the peak to +achieve sub-pixel accuracy. +\f[(\Delta x, \Delta y) = \texttt{weightedCentroid} \{\arg \max_{(x, y)}\{r\}\}\f] +- If non-zero, the response parameter is computed as the sum of the elements of r within the 5x5 +centroid around the peak location. It is normalized to a maximum of 1 (meaning there is a single +peak) and will be smaller when there are multiple peaks. + +@param src1 Source floating point array (CV_32FC1 or CV_64FC1) +@param src2 Source floating point array (CV_32FC1 or CV_64FC1) +@param window Floating point array with windowing coefficients to reduce edge effects (optional). +@param response Signal power within the 5x5 centroid around the peak, between 0 and 1 (optional). +@returns detected phase shift (sub-pixel) between the two arrays. + +@sa dft, getOptimalDFTSize, idft, mulSpectrums createHanningWindow + */ +CV_EXPORTS_W Point2d phaseCorrelate(InputArray src1, InputArray src2, + InputArray window = noArray(), CV_OUT double* response = 0); + +/** @brief This function computes a Hanning window coefficients in two dimensions. + +See (http://en.wikipedia.org/wiki/Hann_function) and (http://en.wikipedia.org/wiki/Window_function) +for more information. + +An example is shown below: +@code + // create hanning window of size 100x100 and type CV_32F + Mat hann; + createHanningWindow(hann, Size(100, 100), CV_32F); +@endcode +@param dst Destination array to place Hann coefficients in +@param winSize The window size specifications (both width and height must be > 1) +@param type Created array type + */ +CV_EXPORTS_W void createHanningWindow(OutputArray dst, Size winSize, int type); + +//! @} imgproc_motion + +//! @addtogroup imgproc_misc +//! @{ + +/** @brief Applies a fixed-level threshold to each array element. + +The function applies fixed-level thresholding to a multiple-channel array. The function is typically +used to get a bi-level (binary) image out of a grayscale image ( #compare could be also used for +this purpose) or for removing a noise, that is, filtering out pixels with too small or too large +values. There are several types of thresholding supported by the function. They are determined by +type parameter. + +Also, the special values #THRESH_OTSU or #THRESH_TRIANGLE may be combined with one of the +above values. In these cases, the function determines the optimal threshold value using the Otsu's +or Triangle algorithm and uses it instead of the specified thresh. + +@note Currently, the Otsu's and Triangle methods are implemented only for 8-bit single-channel images. + +@param src input array (multiple-channel, 8-bit or 32-bit floating point). +@param dst output array of the same size and type and the same number of channels as src. +@param thresh threshold value. +@param maxval maximum value to use with the #THRESH_BINARY and #THRESH_BINARY_INV thresholding +types. +@param type thresholding type (see #ThresholdTypes). +@return the computed threshold value if Otsu's or Triangle methods used. + +@sa adaptiveThreshold, findContours, compare, min, max + */ +CV_EXPORTS_W double threshold( InputArray src, OutputArray dst, + double thresh, double maxval, int type ); + + +/** @brief Applies an adaptive threshold to an array. + +The function transforms a grayscale image to a binary image according to the formulae: +- **THRESH_BINARY** + \f[dst(x,y) = \fork{\texttt{maxValue}}{if \(src(x,y) > T(x,y)\)}{0}{otherwise}\f] +- **THRESH_BINARY_INV** + \f[dst(x,y) = \fork{0}{if \(src(x,y) > T(x,y)\)}{\texttt{maxValue}}{otherwise}\f] +where \f$T(x,y)\f$ is a threshold calculated individually for each pixel (see adaptiveMethod parameter). + +The function can process the image in-place. + +@param src Source 8-bit single-channel image. +@param dst Destination image of the same size and the same type as src. +@param maxValue Non-zero value assigned to the pixels for which the condition is satisfied +@param adaptiveMethod Adaptive thresholding algorithm to use, see #AdaptiveThresholdTypes. +The #BORDER_REPLICATE | #BORDER_ISOLATED is used to process boundaries. +@param thresholdType Thresholding type that must be either #THRESH_BINARY or #THRESH_BINARY_INV, +see #ThresholdTypes. +@param blockSize Size of a pixel neighborhood that is used to calculate a threshold value for the +pixel: 3, 5, 7, and so on. +@param C Constant subtracted from the mean or weighted mean (see the details below). Normally, it +is positive but may be zero or negative as well. + +@sa threshold, blur, GaussianBlur + */ +CV_EXPORTS_W void adaptiveThreshold( InputArray src, OutputArray dst, + double maxValue, int adaptiveMethod, + int thresholdType, int blockSize, double C ); + +//! @} imgproc_misc + +//! @addtogroup imgproc_filter +//! @{ + +/** @example samples/cpp/tutorial_code/ImgProc/Pyramids/Pyramids.cpp +An example using pyrDown and pyrUp functions +*/ + +/** @brief Blurs an image and downsamples it. + +By default, size of the output image is computed as `Size((src.cols+1)/2, (src.rows+1)/2)`, but in +any case, the following conditions should be satisfied: + +\f[\begin{array}{l} | \texttt{dstsize.width} *2-src.cols| \leq 2 \\ | \texttt{dstsize.height} *2-src.rows| \leq 2 \end{array}\f] + +The function performs the downsampling step of the Gaussian pyramid construction. First, it +convolves the source image with the kernel: + +\f[\frac{1}{256} \begin{bmatrix} 1 & 4 & 6 & 4 & 1 \\ 4 & 16 & 24 & 16 & 4 \\ 6 & 24 & 36 & 24 & 6 \\ 4 & 16 & 24 & 16 & 4 \\ 1 & 4 & 6 & 4 & 1 \end{bmatrix}\f] + +Then, it downsamples the image by rejecting even rows and columns. + +@param src input image. +@param dst output image; it has the specified size and the same type as src. +@param dstsize size of the output image. +@param borderType Pixel extrapolation method, see #BorderTypes (#BORDER_CONSTANT isn't supported) + */ +CV_EXPORTS_W void pyrDown( InputArray src, OutputArray dst, + const Size& dstsize = Size(), int borderType = BORDER_DEFAULT ); + +/** @brief Upsamples an image and then blurs it. + +By default, size of the output image is computed as `Size(src.cols\*2, (src.rows\*2)`, but in any +case, the following conditions should be satisfied: + +\f[\begin{array}{l} | \texttt{dstsize.width} -src.cols*2| \leq ( \texttt{dstsize.width} \mod 2) \\ | \texttt{dstsize.height} -src.rows*2| \leq ( \texttt{dstsize.height} \mod 2) \end{array}\f] + +The function performs the upsampling step of the Gaussian pyramid construction, though it can +actually be used to construct the Laplacian pyramid. First, it upsamples the source image by +injecting even zero rows and columns and then convolves the result with the same kernel as in +pyrDown multiplied by 4. + +@param src input image. +@param dst output image. It has the specified size and the same type as src . +@param dstsize size of the output image. +@param borderType Pixel extrapolation method, see #BorderTypes (only #BORDER_DEFAULT is supported) + */ +CV_EXPORTS_W void pyrUp( InputArray src, OutputArray dst, + const Size& dstsize = Size(), int borderType = BORDER_DEFAULT ); + +/** @brief Constructs the Gaussian pyramid for an image. + +The function constructs a vector of images and builds the Gaussian pyramid by recursively applying +pyrDown to the previously built pyramid layers, starting from `dst[0]==src`. + +@param src Source image. Check pyrDown for the list of supported types. +@param dst Destination vector of maxlevel+1 images of the same type as src. dst[0] will be the +same as src. dst[1] is the next pyramid layer, a smoothed and down-sized src, and so on. +@param maxlevel 0-based index of the last (the smallest) pyramid layer. It must be non-negative. +@param borderType Pixel extrapolation method, see #BorderTypes (#BORDER_CONSTANT isn't supported) + */ +CV_EXPORTS void buildPyramid( InputArray src, OutputArrayOfArrays dst, + int maxlevel, int borderType = BORDER_DEFAULT ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +/** @brief Transforms an image to compensate for lens distortion. + +The function transforms an image to compensate radial and tangential lens distortion. + +The function is simply a combination of #initUndistortRectifyMap (with unity R ) and #remap +(with bilinear interpolation). See the former function for details of the transformation being +performed. + +Those pixels in the destination image, for which there is no correspondent pixels in the source +image, are filled with zeros (black color). + +A particular subset of the source image that will be visible in the corrected image can be regulated +by newCameraMatrix. You can use #getOptimalNewCameraMatrix to compute the appropriate +newCameraMatrix depending on your requirements. + +The camera matrix and the distortion parameters can be determined using #calibrateCamera. If +the resolution of images is different from the resolution used at the calibration stage, \f$f_x, +f_y, c_x\f$ and \f$c_y\f$ need to be scaled accordingly, while the distortion coefficients remain +the same. + +@param src Input (distorted) image. +@param dst Output (corrected) image that has the same size and type as src . +@param cameraMatrix Input camera matrix \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param newCameraMatrix Camera matrix of the distorted image. By default, it is the same as +cameraMatrix but you may additionally scale and shift the result by using a different matrix. + */ +CV_EXPORTS_W void undistort( InputArray src, OutputArray dst, + InputArray cameraMatrix, + InputArray distCoeffs, + InputArray newCameraMatrix = noArray() ); + +/** @brief Computes the undistortion and rectification transformation map. + +The function computes the joint undistortion and rectification transformation and represents the +result in the form of maps for remap. The undistorted image looks like original, as if it is +captured with a camera using the camera matrix =newCameraMatrix and zero distortion. In case of a +monocular camera, newCameraMatrix is usually equal to cameraMatrix, or it can be computed by +#getOptimalNewCameraMatrix for a better control over scaling. In case of a stereo camera, +newCameraMatrix is normally set to P1 or P2 computed by #stereoRectify . + +Also, this new camera is oriented differently in the coordinate space, according to R. That, for +example, helps to align two heads of a stereo camera so that the epipolar lines on both images +become horizontal and have the same y- coordinate (in case of a horizontally aligned stereo camera). + +The function actually builds the maps for the inverse mapping algorithm that is used by remap. That +is, for each pixel \f$(u, v)\f$ in the destination (corrected and rectified) image, the function +computes the corresponding coordinates in the source image (that is, in the original image from +camera). The following process is applied: +\f[ +\begin{array}{l} +x \leftarrow (u - {c'}_x)/{f'}_x \\ +y \leftarrow (v - {c'}_y)/{f'}_y \\ +{[X\,Y\,W]} ^T \leftarrow R^{-1}*[x \, y \, 1]^T \\ +x' \leftarrow X/W \\ +y' \leftarrow Y/W \\ +r^2 \leftarrow x'^2 + y'^2 \\ +x'' \leftarrow x' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} ++ 2p_1 x' y' + p_2(r^2 + 2 x'^2) + s_1 r^2 + s_2 r^4\\ +y'' \leftarrow y' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} ++ p_1 (r^2 + 2 y'^2) + 2 p_2 x' y' + s_3 r^2 + s_4 r^4 \\ +s\vecthree{x'''}{y'''}{1} = +\vecthreethree{R_{33}(\tau_x, \tau_y)}{0}{-R_{13}((\tau_x, \tau_y)} +{0}{R_{33}(\tau_x, \tau_y)}{-R_{23}(\tau_x, \tau_y)} +{0}{0}{1} R(\tau_x, \tau_y) \vecthree{x''}{y''}{1}\\ +map_x(u,v) \leftarrow x''' f_x + c_x \\ +map_y(u,v) \leftarrow y''' f_y + c_y +\end{array} +\f] +where \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +are the distortion coefficients. + +In case of a stereo camera, this function is called twice: once for each camera head, after +stereoRectify, which in its turn is called after #stereoCalibrate. But if the stereo camera +was not calibrated, it is still possible to compute the rectification transformations directly from +the fundamental matrix using #stereoRectifyUncalibrated. For each camera, the function computes +homography H as the rectification transformation in a pixel domain, not a rotation matrix R in 3D +space. R can be computed from H as +\f[\texttt{R} = \texttt{cameraMatrix} ^{-1} \cdot \texttt{H} \cdot \texttt{cameraMatrix}\f] +where cameraMatrix can be chosen arbitrarily. + +@param cameraMatrix Input camera matrix \f$A=\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param R Optional rectification transformation in the object space (3x3 matrix). R1 or R2 , +computed by #stereoRectify can be passed here. If the matrix is empty, the identity transformation +is assumed. In cvInitUndistortMap R assumed to be an identity matrix. +@param newCameraMatrix New camera matrix \f$A'=\vecthreethree{f_x'}{0}{c_x'}{0}{f_y'}{c_y'}{0}{0}{1}\f$. +@param size Undistorted image size. +@param m1type Type of the first output map that can be CV_32FC1, CV_32FC2 or CV_16SC2, see #convertMaps +@param map1 The first output map. +@param map2 The second output map. + */ +CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs, + InputArray R, InputArray newCameraMatrix, + Size size, int m1type, OutputArray map1, OutputArray map2 ); + +//! initializes maps for #remap for wide-angle +CV_EXPORTS_W float initWideAngleProjMap( InputArray cameraMatrix, InputArray distCoeffs, + Size imageSize, int destImageWidth, + int m1type, OutputArray map1, OutputArray map2, + int projType = PROJ_SPHERICAL_EQRECT, double alpha = 0); + +/** @brief Returns the default new camera matrix. + +The function returns the camera matrix that is either an exact copy of the input cameraMatrix (when +centerPrinicipalPoint=false ), or the modified one (when centerPrincipalPoint=true). + +In the latter case, the new camera matrix will be: + +\f[\begin{bmatrix} f_x && 0 && ( \texttt{imgSize.width} -1)*0.5 \\ 0 && f_y && ( \texttt{imgSize.height} -1)*0.5 \\ 0 && 0 && 1 \end{bmatrix} ,\f] + +where \f$f_x\f$ and \f$f_y\f$ are \f$(0,0)\f$ and \f$(1,1)\f$ elements of cameraMatrix, respectively. + +By default, the undistortion functions in OpenCV (see #initUndistortRectifyMap, #undistort) do not +move the principal point. However, when you work with stereo, it is important to move the principal +points in both views to the same y-coordinate (which is required by most of stereo correspondence +algorithms), and may be to the same x-coordinate too. So, you can form the new camera matrix for +each view where the principal points are located at the center. + +@param cameraMatrix Input camera matrix. +@param imgsize Camera view image size in pixels. +@param centerPrincipalPoint Location of the principal point in the new camera matrix. The +parameter indicates whether this location should be at the image center or not. + */ +CV_EXPORTS_W Mat getDefaultNewCameraMatrix( InputArray cameraMatrix, Size imgsize = Size(), + bool centerPrincipalPoint = false ); + +/** @brief Computes the ideal point coordinates from the observed point coordinates. + +The function is similar to #undistort and #initUndistortRectifyMap but it operates on a +sparse set of points instead of a raster image. Also the function performs a reverse transformation +to projectPoints. In case of a 3D object, it does not reconstruct its 3D coordinates, but for a +planar object, it does, up to a translation vector, if the proper R is specified. + +For each observed point coordinate \f$(u, v)\f$ the function computes: +\f[ +\begin{array}{l} +x^{"} \leftarrow (u - c_x)/f_x \\ +y^{"} \leftarrow (v - c_y)/f_y \\ +(x',y') = undistort(x^{"},y^{"}, \texttt{distCoeffs}) \\ +{[X\,Y\,W]} ^T \leftarrow R*[x' \, y' \, 1]^T \\ +x \leftarrow X/W \\ +y \leftarrow Y/W \\ +\text{only performed if P is specified:} \\ +u' \leftarrow x {f'}_x + {c'}_x \\ +v' \leftarrow y {f'}_y + {c'}_y +\end{array} +\f] + +where *undistort* is an approximate iterative algorithm that estimates the normalized original +point coordinates out of the normalized distorted point coordinates ("normalized" means that the +coordinates do not depend on the camera matrix). + +The function can be used for both a stereo camera head or a monocular camera (when R is empty). +@param src Observed point coordinates, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel (CV_32FC2 or CV_64FC2) (or +vector\ ). +@param dst Output ideal point coordinates (1xN/Nx1 2-channel or vector\ ) after undistortion and reverse perspective +transformation. If matrix P is identity or omitted, dst will contain normalized point coordinates. +@param cameraMatrix Camera matrix \f$\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param R Rectification transformation in the object space (3x3 matrix). R1 or R2 computed by +#stereoRectify can be passed here. If the matrix is empty, the identity transformation is used. +@param P New camera matrix (3x3) or new projection matrix (3x4) \f$\begin{bmatrix} {f'}_x & 0 & {c'}_x & t_x \\ 0 & {f'}_y & {c'}_y & t_y \\ 0 & 0 & 1 & t_z \end{bmatrix}\f$. P1 or P2 computed by +#stereoRectify can be passed here. If the matrix is empty, the identity new camera matrix is used. + */ +CV_EXPORTS_W void undistortPoints( InputArray src, OutputArray dst, + InputArray cameraMatrix, InputArray distCoeffs, + InputArray R = noArray(), InputArray P = noArray() ); +/** @overload + @note Default version of #undistortPoints does 5 iterations to compute undistorted points. + + */ +CV_EXPORTS_AS(undistortPointsIter) void undistortPoints( InputArray src, OutputArray dst, + InputArray cameraMatrix, InputArray distCoeffs, + InputArray R, InputArray P, TermCriteria criteria ); + +//! @} imgproc_transform + +//! @addtogroup imgproc_hist +//! @{ + +/** @example samples/cpp/demhist.cpp +An example for creating histograms of an image +*/ + +/** @brief Calculates a histogram of a set of arrays. + +The function cv::calcHist calculates the histogram of one or more arrays. The elements of a tuple used +to increment a histogram bin are taken from the corresponding input arrays at the same location. The +sample below shows how to compute a 2D Hue-Saturation histogram for a color image. : +@include snippets/imgproc_calcHist.cpp + +@param images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same +size. Each of them can have an arbitrary number of channels. +@param nimages Number of source images. +@param channels List of the dims channels used to compute the histogram. The first array channels +are numerated from 0 to images[0].channels()-1 , the second array channels are counted from +images[0].channels() to images[0].channels() + images[1].channels()-1, and so on. +@param mask Optional mask. If the matrix is not empty, it must be an 8-bit array of the same size +as images[i] . The non-zero mask elements mark the array elements counted in the histogram. +@param hist Output histogram, which is a dense or sparse dims -dimensional array. +@param dims Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS +(equal to 32 in the current OpenCV version). +@param histSize Array of histogram sizes in each dimension. +@param ranges Array of the dims arrays of the histogram bin boundaries in each dimension. When the +histogram is uniform ( uniform =true), then for each dimension i it is enough to specify the lower +(inclusive) boundary \f$L_0\f$ of the 0-th histogram bin and the upper (exclusive) boundary +\f$U_{\texttt{histSize}[i]-1}\f$ for the last histogram bin histSize[i]-1 . That is, in case of a +uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not uniform ( +uniform=false ), then each of ranges[i] contains histSize[i]+1 elements: +\f$L_0, U_0=L_1, U_1=L_2, ..., U_{\texttt{histSize[i]}-2}=L_{\texttt{histSize[i]}-1}, U_{\texttt{histSize[i]}-1}\f$ +. The array elements, that are not between \f$L_0\f$ and \f$U_{\texttt{histSize[i]}-1}\f$ , are not +counted in the histogram. +@param uniform Flag indicating whether the histogram is uniform or not (see above). +@param accumulate Accumulation flag. If it is set, the histogram is not cleared in the beginning +when it is allocated. This feature enables you to compute a single histogram from several sets of +arrays, or to update the histogram in time. +*/ +CV_EXPORTS void calcHist( const Mat* images, int nimages, + const int* channels, InputArray mask, + OutputArray hist, int dims, const int* histSize, + const float** ranges, bool uniform = true, bool accumulate = false ); + +/** @overload + +this variant uses %SparseMat for output +*/ +CV_EXPORTS void calcHist( const Mat* images, int nimages, + const int* channels, InputArray mask, + SparseMat& hist, int dims, + const int* histSize, const float** ranges, + bool uniform = true, bool accumulate = false ); + +/** @overload */ +CV_EXPORTS_W void calcHist( InputArrayOfArrays images, + const std::vector& channels, + InputArray mask, OutputArray hist, + const std::vector& histSize, + const std::vector& ranges, + bool accumulate = false ); + +/** @brief Calculates the back projection of a histogram. + +The function cv::calcBackProject calculates the back project of the histogram. That is, similarly to +#calcHist , at each location (x, y) the function collects the values from the selected channels +in the input images and finds the corresponding histogram bin. But instead of incrementing it, the +function reads the bin value, scales it by scale , and stores in backProject(x,y) . In terms of +statistics, the function computes probability of each element value in respect with the empirical +probability distribution represented by the histogram. See how, for example, you can find and track +a bright-colored object in a scene: + +- Before tracking, show the object to the camera so that it covers almost the whole frame. +Calculate a hue histogram. The histogram may have strong maximums, corresponding to the dominant +colors in the object. + +- When tracking, calculate a back projection of a hue plane of each input video frame using that +pre-computed histogram. Threshold the back projection to suppress weak colors. It may also make +sense to suppress pixels with non-sufficient color saturation and too dark or too bright pixels. + +- Find connected components in the resulting picture and choose, for example, the largest +component. + +This is an approximate algorithm of the CamShift color object tracker. + +@param images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same +size. Each of them can have an arbitrary number of channels. +@param nimages Number of source images. +@param channels The list of channels used to compute the back projection. The number of channels +must match the histogram dimensionality. The first array channels are numerated from 0 to +images[0].channels()-1 , the second array channels are counted from images[0].channels() to +images[0].channels() + images[1].channels()-1, and so on. +@param hist Input histogram that can be dense or sparse. +@param backProject Destination back projection array that is a single-channel array of the same +size and depth as images[0] . +@param ranges Array of arrays of the histogram bin boundaries in each dimension. See #calcHist . +@param scale Optional scale factor for the output back projection. +@param uniform Flag indicating whether the histogram is uniform or not (see above). + +@sa calcHist, compareHist + */ +CV_EXPORTS void calcBackProject( const Mat* images, int nimages, + const int* channels, InputArray hist, + OutputArray backProject, const float** ranges, + double scale = 1, bool uniform = true ); + +/** @overload */ +CV_EXPORTS void calcBackProject( const Mat* images, int nimages, + const int* channels, const SparseMat& hist, + OutputArray backProject, const float** ranges, + double scale = 1, bool uniform = true ); + +/** @overload */ +CV_EXPORTS_W void calcBackProject( InputArrayOfArrays images, const std::vector& channels, + InputArray hist, OutputArray dst, + const std::vector& ranges, + double scale ); + +/** @brief Compares two histograms. + +The function cv::compareHist compares two dense or two sparse histograms using the specified method. + +The function returns \f$d(H_1, H_2)\f$ . + +While the function works well with 1-, 2-, 3-dimensional dense histograms, it may not be suitable +for high-dimensional sparse histograms. In such histograms, because of aliasing and sampling +problems, the coordinates of non-zero histogram bins can slightly shift. To compare such histograms +or more general sparse configurations of weighted points, consider using the #EMD function. + +@param H1 First compared histogram. +@param H2 Second compared histogram of the same size as H1 . +@param method Comparison method, see #HistCompMethods + */ +CV_EXPORTS_W double compareHist( InputArray H1, InputArray H2, int method ); + +/** @overload */ +CV_EXPORTS double compareHist( const SparseMat& H1, const SparseMat& H2, int method ); + +/** @brief Equalizes the histogram of a grayscale image. + +The function equalizes the histogram of the input image using the following algorithm: + +- Calculate the histogram \f$H\f$ for src . +- Normalize the histogram so that the sum of histogram bins is 255. +- Compute the integral of the histogram: +\f[H'_i = \sum _{0 \le j < i} H(j)\f] +- Transform the image using \f$H'\f$ as a look-up table: \f$\texttt{dst}(x,y) = H'(\texttt{src}(x,y))\f$ + +The algorithm normalizes the brightness and increases the contrast of the image. + +@param src Source 8-bit single channel image. +@param dst Destination image of the same size and type as src . + */ +CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst ); + +/** @brief Creates a smart pointer to a cv::CLAHE class and initializes it. + +@param clipLimit Threshold for contrast limiting. +@param tileGridSize Size of grid for histogram equalization. Input image will be divided into +equally sized rectangular tiles. tileGridSize defines the number of tiles in row and column. + */ +CV_EXPORTS_W Ptr createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8)); + +/** @brief Computes the "minimal work" distance between two weighted point configurations. + +The function computes the earth mover distance and/or a lower boundary of the distance between the +two weighted point configurations. One of the applications described in @cite RubnerSept98, +@cite Rubner2000 is multi-dimensional histogram comparison for image retrieval. EMD is a transportation +problem that is solved using some modification of a simplex algorithm, thus the complexity is +exponential in the worst case, though, on average it is much faster. In the case of a real metric +the lower boundary can be calculated even faster (using linear-time algorithm) and it can be used +to determine roughly whether the two signatures are far enough so that they cannot relate to the +same object. + +@param signature1 First signature, a \f$\texttt{size1}\times \texttt{dims}+1\f$ floating-point matrix. +Each row stores the point weight followed by the point coordinates. The matrix is allowed to have +a single column (weights only) if the user-defined cost matrix is used. The weights must be +non-negative and have at least one non-zero value. +@param signature2 Second signature of the same format as signature1 , though the number of rows +may be different. The total weights may be different. In this case an extra "dummy" point is added +to either signature1 or signature2. The weights must be non-negative and have at least one non-zero +value. +@param distType Used metric. See #DistanceTypes. +@param cost User-defined \f$\texttt{size1}\times \texttt{size2}\f$ cost matrix. Also, if a cost matrix +is used, lower boundary lowerBound cannot be calculated because it needs a metric function. +@param lowerBound Optional input/output parameter: lower boundary of a distance between the two +signatures that is a distance between mass centers. The lower boundary may not be calculated if +the user-defined cost matrix is used, the total weights of point configurations are not equal, or +if the signatures consist of weights only (the signature matrices have a single column). You +**must** initialize \*lowerBound . If the calculated distance between mass centers is greater or +equal to \*lowerBound (it means that the signatures are far enough), the function does not +calculate EMD. In any case \*lowerBound is set to the calculated distance between mass centers on +return. Thus, if you want to calculate both distance between mass centers and EMD, \*lowerBound +should be set to 0. +@param flow Resultant \f$\texttt{size1} \times \texttt{size2}\f$ flow matrix: \f$\texttt{flow}_{i,j}\f$ is +a flow from \f$i\f$ -th point of signature1 to \f$j\f$ -th point of signature2 . + */ +CV_EXPORTS float EMD( InputArray signature1, InputArray signature2, + int distType, InputArray cost=noArray(), + float* lowerBound = 0, OutputArray flow = noArray() ); + +CV_EXPORTS_AS(EMD) float wrapperEMD( InputArray signature1, InputArray signature2, + int distType, InputArray cost=noArray(), + CV_IN_OUT Ptr lowerBound = Ptr(), OutputArray flow = noArray() ); + +//! @} imgproc_hist + +/** @example samples/cpp/watershed.cpp +An example using the watershed algorithm +*/ + +/** @brief Performs a marker-based image segmentation using the watershed algorithm. + +The function implements one of the variants of watershed, non-parametric marker-based segmentation +algorithm, described in @cite Meyer92 . + +Before passing the image to the function, you have to roughly outline the desired regions in the +image markers with positive (\>0) indices. So, every region is represented as one or more connected +components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary +mask using #findContours and #drawContours (see the watershed.cpp demo). The markers are "seeds" of +the future image regions. All the other pixels in markers , whose relation to the outlined regions +is not known and should be defined by the algorithm, should be set to 0's. In the function output, +each pixel in markers is set to a value of the "seed" components or to -1 at boundaries between the +regions. + +@note Any two neighbor connected components are not necessarily separated by a watershed boundary +(-1's pixels); for example, they can touch each other in the initial marker image passed to the +function. + +@param image Input 8-bit 3-channel image. +@param markers Input/output 32-bit single-channel image (map) of markers. It should have the same +size as image . + +@sa findContours + +@ingroup imgproc_misc + */ +CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers ); + +//! @addtogroup imgproc_filter +//! @{ + +/** @brief Performs initial step of meanshift segmentation of an image. + +The function implements the filtering stage of meanshift segmentation, that is, the output of the +function is the filtered "posterized" image with color gradients and fine-grain texture flattened. +At every pixel (X,Y) of the input image (or down-sized input image, see below) the function executes +meanshift iterations, that is, the pixel (X,Y) neighborhood in the joint space-color hyperspace is +considered: + +\f[(x,y): X- \texttt{sp} \le x \le X+ \texttt{sp} , Y- \texttt{sp} \le y \le Y+ \texttt{sp} , ||(R,G,B)-(r,g,b)|| \le \texttt{sr}\f] + +where (R,G,B) and (r,g,b) are the vectors of color components at (X,Y) and (x,y), respectively +(though, the algorithm does not depend on the color space used, so any 3-component color space can +be used instead). Over the neighborhood the average spatial value (X',Y') and average color vector +(R',G',B') are found and they act as the neighborhood center on the next iteration: + +\f[(X,Y)~(X',Y'), (R,G,B)~(R',G',B').\f] + +After the iterations over, the color components of the initial pixel (that is, the pixel from where +the iterations started) are set to the final value (average color at the last iteration): + +\f[I(X,Y) <- (R*,G*,B*)\f] + +When maxLevel \> 0, the gaussian pyramid of maxLevel+1 levels is built, and the above procedure is +run on the smallest layer first. After that, the results are propagated to the larger layer and the +iterations are run again only on those pixels where the layer colors differ by more than sr from the +lower-resolution layer of the pyramid. That makes boundaries of color regions sharper. Note that the +results will be actually different from the ones obtained by running the meanshift procedure on the +whole original image (i.e. when maxLevel==0). + +@param src The source 8-bit, 3-channel image. +@param dst The destination image of the same format and the same size as the source. +@param sp The spatial window radius. +@param sr The color window radius. +@param maxLevel Maximum level of the pyramid for the segmentation. +@param termcrit Termination criteria: when to stop meanshift iterations. + */ +CV_EXPORTS_W void pyrMeanShiftFiltering( InputArray src, OutputArray dst, + double sp, double sr, int maxLevel = 1, + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) ); + +//! @} + +//! @addtogroup imgproc_misc +//! @{ + +/** @example samples/cpp/grabcut.cpp +An example using the GrabCut algorithm +![Sample Screenshot](grabcut_output1.jpg) +*/ + +/** @brief Runs the GrabCut algorithm. + +The function implements the [GrabCut image segmentation algorithm](http://en.wikipedia.org/wiki/GrabCut). + +@param img Input 8-bit 3-channel image. +@param mask Input/output 8-bit single-channel mask. The mask is initialized by the function when +mode is set to #GC_INIT_WITH_RECT. Its elements may have one of the #GrabCutClasses. +@param rect ROI containing a segmented object. The pixels outside of the ROI are marked as +"obvious background". The parameter is only used when mode==#GC_INIT_WITH_RECT . +@param bgdModel Temporary array for the background model. Do not modify it while you are +processing the same image. +@param fgdModel Temporary arrays for the foreground model. Do not modify it while you are +processing the same image. +@param iterCount Number of iterations the algorithm should make before returning the result. Note +that the result can be refined with further calls with mode==#GC_INIT_WITH_MASK or +mode==GC_EVAL . +@param mode Operation mode that could be one of the #GrabCutModes + */ +CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect, + InputOutputArray bgdModel, InputOutputArray fgdModel, + int iterCount, int mode = GC_EVAL ); + +/** @example samples/cpp/distrans.cpp +An example on using the distance transform +*/ + +/** @brief Calculates the distance to the closest zero pixel for each pixel of the source image. + +The function cv::distanceTransform calculates the approximate or precise distance from every binary +image pixel to the nearest zero pixel. For zero image pixels, the distance will obviously be zero. + +When maskSize == #DIST_MASK_PRECISE and distanceType == #DIST_L2 , the function runs the +algorithm described in @cite Felzenszwalb04 . This algorithm is parallelized with the TBB library. + +In other cases, the algorithm @cite Borgefors86 is used. This means that for a pixel the function +finds the shortest path to the nearest zero pixel consisting of basic shifts: horizontal, vertical, +diagonal, or knight's move (the latest is available for a \f$5\times 5\f$ mask). The overall +distance is calculated as a sum of these basic distances. Since the distance function should be +symmetric, all of the horizontal and vertical shifts must have the same cost (denoted as a ), all +the diagonal shifts must have the same cost (denoted as `b`), and all knight's moves must have the +same cost (denoted as `c`). For the #DIST_C and #DIST_L1 types, the distance is calculated +precisely, whereas for #DIST_L2 (Euclidean distance) the distance can be calculated only with a +relative error (a \f$5\times 5\f$ mask gives more accurate results). For `a`,`b`, and `c`, OpenCV +uses the values suggested in the original paper: +- DIST_L1: `a = 1, b = 2` +- DIST_L2: + - `3 x 3`: `a=0.955, b=1.3693` + - `5 x 5`: `a=1, b=1.4, c=2.1969` +- DIST_C: `a = 1, b = 1` + +Typically, for a fast, coarse distance estimation #DIST_L2, a \f$3\times 3\f$ mask is used. For a +more accurate distance estimation #DIST_L2, a \f$5\times 5\f$ mask or the precise algorithm is used. +Note that both the precise and the approximate algorithms are linear on the number of pixels. + +This variant of the function does not only compute the minimum distance for each pixel \f$(x, y)\f$ +but also identifies the nearest connected component consisting of zero pixels +(labelType==#DIST_LABEL_CCOMP) or the nearest zero pixel (labelType==#DIST_LABEL_PIXEL). Index of the +component/pixel is stored in `labels(x, y)`. When labelType==#DIST_LABEL_CCOMP, the function +automatically finds connected components of zero pixels in the input image and marks them with +distinct labels. When labelType==#DIST_LABEL_CCOMP, the function scans through the input image and +marks all the zero pixels with distinct labels. + +In this mode, the complexity is still linear. That is, the function provides a very fast way to +compute the Voronoi diagram for a binary image. Currently, the second variant can use only the +approximate distance transform algorithm, i.e. maskSize=#DIST_MASK_PRECISE is not supported +yet. + +@param src 8-bit, single-channel (binary) source image. +@param dst Output image with calculated distances. It is a 8-bit or 32-bit floating-point, +single-channel image of the same size as src. +@param labels Output 2D array of labels (the discrete Voronoi diagram). It has the type +CV_32SC1 and the same size as src. +@param distanceType Type of distance, see #DistanceTypes +@param maskSize Size of the distance transform mask, see #DistanceTransformMasks. +#DIST_MASK_PRECISE is not supported by this variant. In case of the #DIST_L1 or #DIST_C distance type, +the parameter is forced to 3 because a \f$3\times 3\f$ mask gives the same result as \f$5\times +5\f$ or any larger aperture. +@param labelType Type of the label array to build, see #DistanceTransformLabelTypes. + */ +CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray src, OutputArray dst, + OutputArray labels, int distanceType, int maskSize, + int labelType = DIST_LABEL_CCOMP ); + +/** @overload +@param src 8-bit, single-channel (binary) source image. +@param dst Output image with calculated distances. It is a 8-bit or 32-bit floating-point, +single-channel image of the same size as src . +@param distanceType Type of distance, see #DistanceTypes +@param maskSize Size of the distance transform mask, see #DistanceTransformMasks. In case of the +#DIST_L1 or #DIST_C distance type, the parameter is forced to 3 because a \f$3\times 3\f$ mask gives +the same result as \f$5\times 5\f$ or any larger aperture. +@param dstType Type of output image. It can be CV_8U or CV_32F. Type CV_8U can be used only for +the first variant of the function and distanceType == #DIST_L1. +*/ +CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst, + int distanceType, int maskSize, int dstType=CV_32F); + +/** @example samples/cpp/ffilldemo.cpp +An example using the FloodFill technique +*/ + +/** @overload + +variant without `mask` parameter +*/ +CV_EXPORTS int floodFill( InputOutputArray image, + Point seedPoint, Scalar newVal, CV_OUT Rect* rect = 0, + Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), + int flags = 4 ); + +/** @brief Fills a connected component with the given color. + +The function cv::floodFill fills a connected component starting from the seed point with the specified +color. The connectivity is determined by the color/brightness closeness of the neighbor pixels. The +pixel at \f$(x,y)\f$ is considered to belong to the repainted domain if: + +- in case of a grayscale image and floating range +\f[\texttt{src} (x',y')- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} (x',y')+ \texttt{upDiff}\f] + + +- in case of a grayscale image and fixed range +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)+ \texttt{upDiff}\f] + + +- in case of a color image and floating range +\f[\texttt{src} (x',y')_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} (x',y')_r+ \texttt{upDiff} _r,\f] +\f[\texttt{src} (x',y')_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} (x',y')_g+ \texttt{upDiff} _g\f] +and +\f[\texttt{src} (x',y')_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} (x',y')_b+ \texttt{upDiff} _b\f] + + +- in case of a color image and fixed range +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r+ \texttt{upDiff} _r,\f] +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g+ \texttt{upDiff} _g\f] +and +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b+ \texttt{upDiff} _b\f] + + +where \f$src(x',y')\f$ is the value of one of pixel neighbors that is already known to belong to the +component. That is, to be added to the connected component, a color/brightness of the pixel should +be close enough to: +- Color/brightness of one of its neighbors that already belong to the connected component in case +of a floating range. +- Color/brightness of the seed point in case of a fixed range. + +Use these functions to either mark a connected component with the specified color in-place, or build +a mask and then extract the contour, or copy the region to another image, and so on. + +@param image Input/output 1- or 3-channel, 8-bit, or floating-point image. It is modified by the +function unless the #FLOODFILL_MASK_ONLY flag is set in the second variant of the function. See +the details below. +@param mask Operation mask that should be a single-channel 8-bit image, 2 pixels wider and 2 pixels +taller than image. Since this is both an input and output parameter, you must take responsibility +of initializing it. Flood-filling cannot go across non-zero pixels in the input mask. For example, +an edge detector output can be used as a mask to stop filling at edges. On output, pixels in the +mask corresponding to filled pixels in the image are set to 1 or to the a value specified in flags +as described below. Additionally, the function fills the border of the mask with ones to simplify +internal processing. It is therefore possible to use the same mask in multiple calls to the function +to make sure the filled areas do not overlap. +@param seedPoint Starting point. +@param newVal New value of the repainted domain pixels. +@param loDiff Maximal lower brightness/color difference between the currently observed pixel and +one of its neighbors belonging to the component, or a seed pixel being added to the component. +@param upDiff Maximal upper brightness/color difference between the currently observed pixel and +one of its neighbors belonging to the component, or a seed pixel being added to the component. +@param rect Optional output parameter set by the function to the minimum bounding rectangle of the +repainted domain. +@param flags Operation flags. The first 8 bits contain a connectivity value. The default value of +4 means that only the four nearest neighbor pixels (those that share an edge) are considered. A +connectivity value of 8 means that the eight nearest neighbor pixels (those that share a corner) +will be considered. The next 8 bits (8-16) contain a value between 1 and 255 with which to fill +the mask (the default value is 1). For example, 4 | ( 255 \<\< 8 ) will consider 4 nearest +neighbours and fill the mask with a value of 255. The following additional options occupy higher +bits and therefore may be further combined with the connectivity and mask fill values using +bit-wise or (|), see #FloodFillFlags. + +@note Since the mask is larger than the filled image, a pixel \f$(x, y)\f$ in image corresponds to the +pixel \f$(x+1, y+1)\f$ in the mask . + +@sa findContours + */ +CV_EXPORTS_W int floodFill( InputOutputArray image, InputOutputArray mask, + Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0, + Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), + int flags = 4 ); + +//! Performs linear blending of two images: +//! \f[ \texttt{dst}(i,j) = \texttt{weights1}(i,j)*\texttt{src1}(i,j) + \texttt{weights2}(i,j)*\texttt{src2}(i,j) \f] +//! @param src1 It has a type of CV_8UC(n) or CV_32FC(n), where n is a positive integer. +//! @param src2 It has the same type and size as src1. +//! @param weights1 It has a type of CV_32FC1 and the same size with src1. +//! @param weights2 It has a type of CV_32FC1 and the same size with src1. +//! @param dst It is created if it does not have the same size and type with src1. +CV_EXPORTS void blendLinear(InputArray src1, InputArray src2, InputArray weights1, InputArray weights2, OutputArray dst); + +//! @} imgproc_misc + +//! @addtogroup imgproc_color_conversions +//! @{ + +/** @brief Converts an image from one color space to another. + +The function converts an input image from one color space to another. In case of a transformation +to-from RGB color space, the order of the channels should be specified explicitly (RGB or BGR). Note +that the default color format in OpenCV is often referred to as RGB but it is actually BGR (the +bytes are reversed). So the first byte in a standard (24-bit) color image will be an 8-bit Blue +component, the second byte will be Green, and the third byte will be Red. The fourth, fifth, and +sixth bytes would then be the second pixel (Blue, then Green, then Red), and so on. + +The conventional ranges for R, G, and B channel values are: +- 0 to 255 for CV_8U images +- 0 to 65535 for CV_16U images +- 0 to 1 for CV_32F images + +In case of linear transformations, the range does not matter. But in case of a non-linear +transformation, an input RGB image should be normalized to the proper value range to get the correct +results, for example, for RGB \f$\rightarrow\f$ L\*u\*v\* transformation. For example, if you have a +32-bit floating-point image directly converted from an 8-bit image without any scaling, then it will +have the 0..255 value range instead of 0..1 assumed by the function. So, before calling #cvtColor , +you need first to scale the image down: +@code + img *= 1./255; + cvtColor(img, img, COLOR_BGR2Luv); +@endcode +If you use #cvtColor with 8-bit images, the conversion will have some information lost. For many +applications, this will not be noticeable but it is recommended to use 32-bit images in applications +that need the full range of colors or that convert an image before an operation and then convert +back. + +If conversion adds the alpha channel, its value will set to the maximum of corresponding channel +range: 255 for CV_8U, 65535 for CV_16U, 1 for CV_32F. + +@param src input image: 8-bit unsigned, 16-bit unsigned ( CV_16UC... ), or single-precision +floating-point. +@param dst output image of the same size and depth as src. +@param code color space conversion code (see #ColorConversionCodes). +@param dstCn number of channels in the destination image; if the parameter is 0, the number of the +channels is derived automatically from src and code. + +@see @ref imgproc_color_conversions + */ +CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 ); + +/** @brief Converts an image from one color space to another where the source image is +stored in two planes. + +This function only supports YUV420 to RGB conversion as of now. + +@param src1: 8-bit image (#CV_8U) of the Y plane. +@param src2: image containing interleaved U/V plane. +@param dst: output image. +@param code: Specifies the type of conversion. It can take any of the following values: +- #COLOR_YUV2BGR_NV12 +- #COLOR_YUV2RGB_NV12 +- #COLOR_YUV2BGRA_NV12 +- #COLOR_YUV2RGBA_NV12 +- #COLOR_YUV2BGR_NV21 +- #COLOR_YUV2RGB_NV21 +- #COLOR_YUV2BGRA_NV21 +- #COLOR_YUV2RGBA_NV21 +*/ +CV_EXPORTS_W void cvtColorTwoPlane( InputArray src1, InputArray src2, OutputArray dst, int code ); + +/** @brief main function for all demosaicing processes + +@param src input image: 8-bit unsigned or 16-bit unsigned. +@param dst output image of the same size and depth as src. +@param code Color space conversion code (see the description below). +@param dstCn number of channels in the destination image; if the parameter is 0, the number of the +channels is derived automatically from src and code. + +The function can do the following transformations: + +- Demosaicing using bilinear interpolation + + #COLOR_BayerBG2BGR , #COLOR_BayerGB2BGR , #COLOR_BayerRG2BGR , #COLOR_BayerGR2BGR + + #COLOR_BayerBG2GRAY , #COLOR_BayerGB2GRAY , #COLOR_BayerRG2GRAY , #COLOR_BayerGR2GRAY + +- Demosaicing using Variable Number of Gradients. + + #COLOR_BayerBG2BGR_VNG , #COLOR_BayerGB2BGR_VNG , #COLOR_BayerRG2BGR_VNG , #COLOR_BayerGR2BGR_VNG + +- Edge-Aware Demosaicing. + + #COLOR_BayerBG2BGR_EA , #COLOR_BayerGB2BGR_EA , #COLOR_BayerRG2BGR_EA , #COLOR_BayerGR2BGR_EA + +- Demosaicing with alpha channel + + #COLOR_BayerBG2BGRA , #COLOR_BayerGB2BGRA , #COLOR_BayerRG2BGRA , #COLOR_BayerGR2BGRA + +@sa cvtColor +*/ +CV_EXPORTS_W void demosaicing(InputArray src, OutputArray dst, int code, int dstCn = 0); + +//! @} imgproc_color_conversions + +//! @addtogroup imgproc_shape +//! @{ + +/** @brief Calculates all of the moments up to the third order of a polygon or rasterized shape. + +The function computes moments, up to the 3rd order, of a vector shape or a rasterized shape. The +results are returned in the structure cv::Moments. + +@param array Raster image (single-channel, 8-bit or floating-point 2D array) or an array ( +\f$1 \times N\f$ or \f$N \times 1\f$ ) of 2D points (Point or Point2f ). +@param binaryImage If it is true, all non-zero image pixels are treated as 1's. The parameter is +used for images only. +@returns moments. + +@note Only applicable to contour moments calculations from Python bindings: Note that the numpy +type for the input array should be either np.int32 or np.float32. + +@sa contourArea, arcLength + */ +CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false ); + +/** @brief Calculates seven Hu invariants. + +The function calculates seven Hu invariants (introduced in @cite Hu62; see also +) defined as: + +\f[\begin{array}{l} hu[0]= \eta _{20}+ \eta _{02} \\ hu[1]=( \eta _{20}- \eta _{02})^{2}+4 \eta _{11}^{2} \\ hu[2]=( \eta _{30}-3 \eta _{12})^{2}+ (3 \eta _{21}- \eta _{03})^{2} \\ hu[3]=( \eta _{30}+ \eta _{12})^{2}+ ( \eta _{21}+ \eta _{03})^{2} \\ hu[4]=( \eta _{30}-3 \eta _{12})( \eta _{30}+ \eta _{12})[( \eta _{30}+ \eta _{12})^{2}-3( \eta _{21}+ \eta _{03})^{2}]+(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ hu[5]=( \eta _{20}- \eta _{02})[( \eta _{30}+ \eta _{12})^{2}- ( \eta _{21}+ \eta _{03})^{2}]+4 \eta _{11}( \eta _{30}+ \eta _{12})( \eta _{21}+ \eta _{03}) \\ hu[6]=(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}]-( \eta _{30}-3 \eta _{12})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ \end{array}\f] + +where \f$\eta_{ji}\f$ stands for \f$\texttt{Moments::nu}_{ji}\f$ . + +These values are proved to be invariants to the image scale, rotation, and reflection except the +seventh one, whose sign is changed by reflection. This invariance is proved with the assumption of +infinite image resolution. In case of raster images, the computed Hu invariants for the original and +transformed images are a bit different. + +@param moments Input moments computed with moments . +@param hu Output Hu invariants. + +@sa matchShapes + */ +CV_EXPORTS void HuMoments( const Moments& moments, double hu[7] ); + +/** @overload */ +CV_EXPORTS_W void HuMoments( const Moments& m, OutputArray hu ); + +//! @} imgproc_shape + +//! @addtogroup imgproc_object +//! @{ + +//! type of the template matching operation +enum TemplateMatchModes { + TM_SQDIFF = 0, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f] + TM_SQDIFF_NORMED = 1, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f] + TM_CCORR = 2, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f] + TM_CCORR_NORMED = 3, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f] + TM_CCOEFF = 4, //!< \f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\f] + //!< where + //!< \f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f] + TM_CCOEFF_NORMED = 5 //!< \f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f] +}; + +/** @example samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp +An example using Template Matching algorithm +*/ + +/** @brief Compares a template against overlapped image regions. + +The function slides through image , compares the overlapped patches of size \f$w \times h\f$ against +templ using the specified method and stores the comparison results in result . Here are the formulae +for the available comparison methods ( \f$I\f$ denotes image, \f$T\f$ template, \f$R\f$ result ). The summation +is done over template and/or the image patch: \f$x' = 0...w-1, y' = 0...h-1\f$ + +After the function finishes the comparison, the best matches can be found as global minimums (when +#TM_SQDIFF was used) or maximums (when #TM_CCORR or #TM_CCOEFF was used) using the +#minMaxLoc function. In case of a color image, template summation in the numerator and each sum in +the denominator is done over all of the channels and separate mean values are used for each channel. +That is, the function can take a color template and a color image. The result will still be a +single-channel image, which is easier to analyze. + +@param image Image where the search is running. It must be 8-bit or 32-bit floating-point. +@param templ Searched template. It must be not greater than the source image and have the same +data type. +@param result Map of comparison results. It must be single-channel 32-bit floating-point. If image +is \f$W \times H\f$ and templ is \f$w \times h\f$ , then result is \f$(W-w+1) \times (H-h+1)\f$ . +@param method Parameter specifying the comparison method, see #TemplateMatchModes +@param mask Mask of searched template. It must have the same datatype and size with templ. It is +not set by default. Currently, only the #TM_SQDIFF and #TM_CCORR_NORMED methods are supported. + */ +CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, + OutputArray result, int method, InputArray mask = noArray() ); + +//! @} + +//! @addtogroup imgproc_shape +//! @{ + +/** @example samples/cpp/connected_components.cpp +This program demonstrates connected components and use of the trackbar +*/ + +/** @brief computes the connected components labeled image of boolean image + +image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 +represents the background label. ltype specifies the output label image type, an important +consideration based on the total number of labels or alternatively the total number of pixels in +the source image. ccltype specifies the connected components labeling algorithm to use, currently +Grana (BBDT) and Wu's (SAUF) algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. +This function uses parallel version of both Grana and Wu's algorithms if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +@param ccltype connected components algorithm type (see the #ConnectedComponentsAlgorithmsTypes). +*/ +CV_EXPORTS_AS(connectedComponentsWithAlgorithm) int connectedComponents(InputArray image, OutputArray labels, + int connectivity, int ltype, int ccltype); + + +/** @overload + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +*/ +CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, + int connectivity = 8, int ltype = CV_32S); + + +/** @brief computes the connected components labeled image of boolean image and also produces a statistics output for each label + +image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 +represents the background label. ltype specifies the output label image type, an important +consideration based on the total number of labels or alternatively the total number of pixels in +the source image. ccltype specifies the connected components labeling algorithm to use, currently +Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. +This function uses parallel version of both Grana and Wu's algorithms (statistics included) if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param stats statistics output for each label, including the background label, see below for +available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of +#ConnectedComponentsTypes. The data type is CV_32S. +@param centroids centroid output for each label, including the background label. Centroids are +accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F. +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +@param ccltype connected components algorithm type (see #ConnectedComponentsAlgorithmsTypes). +*/ +CV_EXPORTS_AS(connectedComponentsWithStatsWithAlgorithm) int connectedComponentsWithStats(InputArray image, OutputArray labels, + OutputArray stats, OutputArray centroids, + int connectivity, int ltype, int ccltype); + +/** @overload +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param stats statistics output for each label, including the background label, see below for +available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of +#ConnectedComponentsTypes. The data type is CV_32S. +@param centroids centroid output for each label, including the background label. Centroids are +accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F. +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +*/ +CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, + OutputArray stats, OutputArray centroids, + int connectivity = 8, int ltype = CV_32S); + + +/** @brief Finds contours in a binary image. + +The function retrieves contours from the binary image using the algorithm @cite Suzuki85 . The contours +are a useful tool for shape analysis and object detection and recognition. See squares.cpp in the +OpenCV sample directory. +@note Since opencv 3.2 source image is not modified by this function. + +@param image Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero +pixels remain 0's, so the image is treated as binary . You can use #compare, #inRange, #threshold , +#adaptiveThreshold, #Canny, and others to create a binary image out of a grayscale or color one. +If mode equals to #RETR_CCOMP or #RETR_FLOODFILL, the input can also be a 32-bit integer image of labels (CV_32SC1). +@param contours Detected contours. Each contour is stored as a vector of points (e.g. +std::vector >). +@param hierarchy Optional output vector (e.g. std::vector), containing information about the image topology. It has +as many elements as the number of contours. For each i-th contour contours[i], the elements +hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , and hierarchy[i][3] are set to 0-based indices +in contours of the next and previous contours at the same hierarchical level, the first child +contour and the parent contour, respectively. If for the contour i there are no next, previous, +parent, or nested contours, the corresponding elements of hierarchy[i] will be negative. +@param mode Contour retrieval mode, see #RetrievalModes +@param method Contour approximation method, see #ContourApproximationModes +@param offset Optional offset by which every contour point is shifted. This is useful if the +contours are extracted from the image ROI and then they should be analyzed in the whole image +context. + */ +CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours, + OutputArray hierarchy, int mode, + int method, Point offset = Point()); + +/** @overload */ +CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours, + int mode, int method, Point offset = Point()); + +/** @example samples/cpp/squares.cpp +A program using pyramid scaling, Canny, contours and contour simplification to find +squares in a list of images (pic1-6.png). Returns sequence of squares detected on the image. +*/ + +/** @example samples/tapi/squares.cpp +A program using pyramid scaling, Canny, contours and contour simplification to find +squares in the input image. +*/ + +/** @brief Approximates a polygonal curve(s) with the specified precision. + +The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less +vertices so that the distance between them is less or equal to the specified precision. It uses the +Douglas-Peucker algorithm + +@param curve Input vector of a 2D point stored in std::vector or Mat +@param approxCurve Result of the approximation. The type should match the type of the input curve. +@param epsilon Parameter specifying the approximation accuracy. This is the maximum distance +between the original curve and its approximation. +@param closed If true, the approximated curve is closed (its first and last vertices are +connected). Otherwise, it is not closed. + */ +CV_EXPORTS_W void approxPolyDP( InputArray curve, + OutputArray approxCurve, + double epsilon, bool closed ); + +/** @brief Calculates a contour perimeter or a curve length. + +The function computes a curve length or a closed contour perimeter. + +@param curve Input vector of 2D points, stored in std::vector or Mat. +@param closed Flag indicating whether the curve is closed or not. + */ +CV_EXPORTS_W double arcLength( InputArray curve, bool closed ); + +/** @brief Calculates the up-right bounding rectangle of a point set or non-zero pixels of gray-scale image. + +The function calculates and returns the minimal up-right bounding rectangle for the specified point set or +non-zero pixels of gray-scale image. + +@param array Input gray-scale image or 2D point set, stored in std::vector or Mat. + */ +CV_EXPORTS_W Rect boundingRect( InputArray array ); + +/** @brief Calculates a contour area. + +The function computes a contour area. Similarly to moments , the area is computed using the Green +formula. Thus, the returned area and the number of non-zero pixels, if you draw the contour using +#drawContours or #fillPoly , can be different. Also, the function will most certainly give a wrong +results for contours with self-intersections. + +Example: +@code + vector contour; + contour.push_back(Point2f(0, 0)); + contour.push_back(Point2f(10, 0)); + contour.push_back(Point2f(10, 10)); + contour.push_back(Point2f(5, 4)); + + double area0 = contourArea(contour); + vector approx; + approxPolyDP(contour, approx, 5, true); + double area1 = contourArea(approx); + + cout << "area0 =" << area0 << endl << + "area1 =" << area1 << endl << + "approx poly vertices" << approx.size() << endl; +@endcode +@param contour Input vector of 2D points (contour vertices), stored in std::vector or Mat. +@param oriented Oriented area flag. If it is true, the function returns a signed area value, +depending on the contour orientation (clockwise or counter-clockwise). Using this feature you can +determine orientation of a contour by taking the sign of an area. By default, the parameter is +false, which means that the absolute value is returned. + */ +CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false ); + +/** @brief Finds a rotated rectangle of the minimum area enclosing the input 2D point set. + +The function calculates and returns the minimum-area bounding rectangle (possibly rotated) for a +specified point set. Developer should keep in mind that the returned RotatedRect can contain negative +indices when data is close to the containing Mat element boundary. + +@param points Input vector of 2D points, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect minAreaRect( InputArray points ); + +/** @brief Finds the four vertices of a rotated rect. Useful to draw the rotated rectangle. + +The function finds the four vertices of a rotated rectangle. This function is useful to draw the +rectangle. In C++, instead of using this function, you can directly use RotatedRect::points method. Please +visit the @ref tutorial_bounding_rotated_ellipses "tutorial on Creating Bounding rotated boxes and ellipses for contours" for more information. + +@param box The input rotated rectangle. It may be the output of +@param points The output array of four vertices of rectangles. + */ +CV_EXPORTS_W void boxPoints(RotatedRect box, OutputArray points); + +/** @brief Finds a circle of the minimum area enclosing a 2D point set. + +The function finds the minimal enclosing circle of a 2D point set using an iterative algorithm. + +@param points Input vector of 2D points, stored in std::vector\<\> or Mat +@param center Output center of the circle. +@param radius Output radius of the circle. + */ +CV_EXPORTS_W void minEnclosingCircle( InputArray points, + CV_OUT Point2f& center, CV_OUT float& radius ); + +/** @example samples/cpp/minarea.cpp +*/ + +/** @brief Finds a triangle of minimum area enclosing a 2D point set and returns its area. + +The function finds a triangle of minimum area enclosing the given set of 2D points and returns its +area. The output for a given 2D point set is shown in the image below. 2D points are depicted in +*red* and the enclosing triangle in *yellow*. + +![Sample output of the minimum enclosing triangle function](pics/minenclosingtriangle.png) + +The implementation of the algorithm is based on O'Rourke's @cite ORourke86 and Klee and Laskowski's +@cite KleeLaskowski85 papers. O'Rourke provides a \f$\theta(n)\f$ algorithm for finding the minimal +enclosing triangle of a 2D convex polygon with n vertices. Since the #minEnclosingTriangle function +takes a 2D point set as input an additional preprocessing step of computing the convex hull of the +2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which is higher +than \f$\theta(n)\f$. Thus the overall complexity of the function is \f$O(n log(n))\f$. + +@param points Input vector of 2D points with depth CV_32S or CV_32F, stored in std::vector\<\> or Mat +@param triangle Output vector of three 2D points defining the vertices of the triangle. The depth +of the OutputArray must be CV_32F. + */ +CV_EXPORTS_W double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle ); + +/** @brief Compares two shapes. + +The function compares two shapes. All three implemented methods use the Hu invariants (see #HuMoments) + +@param contour1 First contour or grayscale image. +@param contour2 Second contour or grayscale image. +@param method Comparison method, see #ShapeMatchModes +@param parameter Method-specific parameter (not supported now). + */ +CV_EXPORTS_W double matchShapes( InputArray contour1, InputArray contour2, + int method, double parameter ); + +/** @example samples/cpp/convexhull.cpp +An example using the convexHull functionality +*/ + +/** @brief Finds the convex hull of a point set. + +The function cv::convexHull finds the convex hull of a 2D point set using the Sklansky's algorithm @cite Sklansky82 +that has *O(N logN)* complexity in the current implementation. + +@param points Input 2D point set, stored in std::vector or Mat. +@param hull Output convex hull. It is either an integer vector of indices or vector of points. In +the first case, the hull elements are 0-based indices of the convex hull points in the original +array (since the set of convex hull points is a subset of the original point set). In the second +case, hull elements are the convex hull points themselves. +@param clockwise Orientation flag. If it is true, the output convex hull is oriented clockwise. +Otherwise, it is oriented counter-clockwise. The assumed coordinate system has its X axis pointing +to the right, and its Y axis pointing upwards. +@param returnPoints Operation flag. In case of a matrix, when the flag is true, the function +returns convex hull points. Otherwise, it returns indices of the convex hull points. When the +output array is std::vector, the flag is ignored, and the output depends on the type of the +vector: std::vector\ implies returnPoints=false, std::vector\ implies +returnPoints=true. + +@note `points` and `hull` should be different arrays, inplace processing isn't supported. + +Check @ref tutorial_hull "the corresponding tutorial" for more details. + +useful links: + +https://www.learnopencv.com/convex-hull-using-opencv-in-python-and-c/ + */ +CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull, + bool clockwise = false, bool returnPoints = true ); + +/** @brief Finds the convexity defects of a contour. + +The figure below displays convexity defects of a hand contour: + +![image](pics/defects.png) + +@param contour Input contour. +@param convexhull Convex hull obtained using convexHull that should contain indices of the contour +points that make the hull. +@param convexityDefects The output vector of convexity defects. In C++ and the new Python/Java +interface each convexity defect is represented as 4-element integer vector (a.k.a. #Vec4i): +(start_index, end_index, farthest_pt_index, fixpt_depth), where indices are 0-based indices +in the original contour of the convexity defect beginning, end and the farthest point, and +fixpt_depth is fixed-point approximation (with 8 fractional bits) of the distance between the +farthest contour point and the hull. That is, to get the floating-point value of the depth will be +fixpt_depth/256.0. + */ +CV_EXPORTS_W void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects ); + +/** @brief Tests a contour convexity. + +The function tests whether the input contour is convex or not. The contour must be simple, that is, +without self-intersections. Otherwise, the function output is undefined. + +@param contour Input vector of 2D points, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W bool isContourConvex( InputArray contour ); + +/** @example samples/cpp/intersectExample.cpp +Examples of how intersectConvexConvex works +*/ + +/** @brief Finds intersection of two convex polygons + +@param _p1 First polygon +@param _p2 Second polygon +@param _p12 Output polygon describing the intersecting area +@param handleNested When true, an intersection is found if one of the polygons is fully enclosed in the other. +When false, no intersection is found. If the polygons share a side or the vertex of one polygon lies on an edge +of the other, they are not considered nested and an intersection will be found regardless of the value of handleNested. + +@returns Absolute value of area of intersecting polygon + +@note intersectConvexConvex doesn't confirm that both polygons are convex and will return invalid results if they aren't. + */ +CV_EXPORTS_W float intersectConvexConvex( InputArray _p1, InputArray _p2, + OutputArray _p12, bool handleNested = true ); + +/** @example samples/cpp/fitellipse.cpp +An example using the fitEllipse technique +*/ + +/** @brief Fits an ellipse around a set of 2D points. + +The function calculates the ellipse that fits (in a least-squares sense) a set of 2D points best of +all. It returns the rotated rectangle in which the ellipse is inscribed. The first algorithm described by @cite Fitzgibbon95 +is used. Developer should keep in mind that it is possible that the returned +ellipse/rotatedRect data contains negative indices, due to the data points being close to the +border of the containing Mat element. + +@param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipse( InputArray points ); + +/** @brief Fits an ellipse around a set of 2D points. + + The function calculates the ellipse that fits a set of 2D points. + It returns the rotated rectangle in which the ellipse is inscribed. + The Approximate Mean Square (AMS) proposed by @cite Taubin1991 is used. + + For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$, + which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$. + However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$, + the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines, + quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits. + If the fit is found to be a parabolic or hyperbolic function then the standard #fitEllipse method is used. + The AMS method restricts the fit to parabolic, hyperbolic and elliptical curves + by imposing the condition that \f$ A^T ( D_x^T D_x + D_y^T D_y) A = 1 \f$ where + the matrices \f$ Dx \f$ and \f$ Dy \f$ are the partial derivatives of the design matrix \f$ D \f$ with + respect to x and y. The matrices are formed row by row applying the following to + each of the points in the set: + \f{align*}{ + D(i,:)&=\left\{x_i^2, x_i y_i, y_i^2, x_i, y_i, 1\right\} & + D_x(i,:)&=\left\{2 x_i,y_i,0,1,0,0\right\} & + D_y(i,:)&=\left\{0,x_i,2 y_i,0,1,0\right\} + \f} + The AMS method minimizes the cost function + \f{equation*}{ + \epsilon ^2=\frac{ A^T D^T D A }{ A^T (D_x^T D_x + D_y^T D_y) A^T } + \f} + + The minimum cost is found by solving the generalized eigenvalue problem. + + \f{equation*}{ + D^T D A = \lambda \left( D_x^T D_x + D_y^T D_y\right) A + \f} + + @param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipseAMS( InputArray points ); + + +/** @brief Fits an ellipse around a set of 2D points. + + The function calculates the ellipse that fits a set of 2D points. + It returns the rotated rectangle in which the ellipse is inscribed. + The Direct least square (Direct) method by @cite Fitzgibbon1999 is used. + + For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$, + which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$. + However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$, + the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines, + quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits. + The Direct method confines the fit to ellipses by ensuring that \f$ 4 A_{xx} A_{yy}- A_{xy}^2 > 0 \f$. + The condition imposed is that \f$ 4 A_{xx} A_{yy}- A_{xy}^2=1 \f$ which satisfies the inequality + and as the coefficients can be arbitrarily scaled is not overly restrictive. + + \f{equation*}{ + \epsilon ^2= A^T D^T D A \quad \text{with} \quad A^T C A =1 \quad \text{and} \quad C=\left(\begin{matrix} + 0 & 0 & 2 & 0 & 0 & 0 \\ + 0 & -1 & 0 & 0 & 0 & 0 \\ + 2 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 + \end{matrix} \right) + \f} + + The minimum cost is found by solving the generalized eigenvalue problem. + + \f{equation*}{ + D^T D A = \lambda \left( C\right) A + \f} + + The system produces only one positive eigenvalue \f$ \lambda\f$ which is chosen as the solution + with its eigenvector \f$\mathbf{u}\f$. These are used to find the coefficients + + \f{equation*}{ + A = \sqrt{\frac{1}{\mathbf{u}^T C \mathbf{u}}} \mathbf{u} + \f} + The scaling factor guarantees that \f$A^T C A =1\f$. + + @param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipseDirect( InputArray points ); + +/** @brief Fits a line to a 2D or 3D point set. + +The function fitLine fits a line to a 2D or 3D point set by minimizing \f$\sum_i \rho(r_i)\f$ where +\f$r_i\f$ is a distance between the \f$i^{th}\f$ point, the line and \f$\rho(r)\f$ is a distance function, one +of the following: +- DIST_L2 +\f[\rho (r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}\f] +- DIST_L1 +\f[\rho (r) = r\f] +- DIST_L12 +\f[\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)\f] +- DIST_FAIR +\f[\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998\f] +- DIST_WELSCH +\f[\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846\f] +- DIST_HUBER +\f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f] + +The algorithm is based on the M-estimator ( ) technique +that iteratively fits the line using the weighted least-squares algorithm. After each iteration the +weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . + +@param points Input vector of 2D or 3D points, stored in std::vector\<\> or Mat. +@param line Output line parameters. In case of 2D fitting, it should be a vector of 4 elements +(like Vec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized vector collinear to the line and +(x0, y0) is a point on the line. In case of 3D fitting, it should be a vector of 6 elements (like +Vec6f) - (vx, vy, vz, x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line +and (x0, y0, z0) is a point on the line. +@param distType Distance used by the M-estimator, see #DistanceTypes +@param param Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value +is chosen. +@param reps Sufficient accuracy for the radius (distance between the coordinate origin and the line). +@param aeps Sufficient accuracy for the angle. 0.01 would be a good default value for reps and aeps. + */ +CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType, + double param, double reps, double aeps ); + +/** @brief Performs a point-in-contour test. + +The function determines whether the point is inside a contour, outside, or lies on an edge (or +coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge) +value, correspondingly. When measureDist=false , the return value is +1, -1, and 0, respectively. +Otherwise, the return value is a signed distance between the point and the nearest contour edge. + +See below a sample output of the function where each image pixel is tested against the contour: + +![sample output](pics/pointpolygon.png) + +@param contour Input contour. +@param pt Point tested against the contour. +@param measureDist If true, the function estimates the signed distance from the point to the +nearest contour edge. Otherwise, the function only checks if the point is inside a contour or not. + */ +CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist ); + +/** @brief Finds out if there is any intersection between two rotated rectangles. + +If there is then the vertices of the intersecting region are returned as well. + +Below are some examples of intersection configurations. The hatched pattern indicates the +intersecting region and the red vertices are returned by the function. + +![intersection examples](pics/intersection.png) + +@param rect1 First rectangle +@param rect2 Second rectangle +@param intersectingRegion The output array of the vertices of the intersecting region. It returns +at most 8 vertices. Stored as std::vector\ or cv::Mat as Mx1 of type CV_32FC2. +@returns One of #RectanglesIntersectTypes + */ +CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); + +/** @brief Creates a smart pointer to a cv::GeneralizedHoughBallard class and initializes it. +*/ +CV_EXPORTS_W Ptr createGeneralizedHoughBallard(); + +/** @brief Creates a smart pointer to a cv::GeneralizedHoughGuil class and initializes it. +*/ +CV_EXPORTS_W Ptr createGeneralizedHoughGuil(); + +//! @} imgproc_shape + +//! @addtogroup imgproc_colormap +//! @{ + +//! GNU Octave/MATLAB equivalent colormaps +enum ColormapTypes +{ + COLORMAP_AUTUMN = 0, //!< ![autumn](pics/colormaps/colorscale_autumn.jpg) + COLORMAP_BONE = 1, //!< ![bone](pics/colormaps/colorscale_bone.jpg) + COLORMAP_JET = 2, //!< ![jet](pics/colormaps/colorscale_jet.jpg) + COLORMAP_WINTER = 3, //!< ![winter](pics/colormaps/colorscale_winter.jpg) + COLORMAP_RAINBOW = 4, //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg) + COLORMAP_OCEAN = 5, //!< ![ocean](pics/colormaps/colorscale_ocean.jpg) + COLORMAP_SUMMER = 6, //!< ![summer](pics/colormaps/colorscale_summer.jpg) + COLORMAP_SPRING = 7, //!< ![spring](pics/colormaps/colorscale_spring.jpg) + COLORMAP_COOL = 8, //!< ![cool](pics/colormaps/colorscale_cool.jpg) + COLORMAP_HSV = 9, //!< ![HSV](pics/colormaps/colorscale_hsv.jpg) + COLORMAP_PINK = 10, //!< ![pink](pics/colormaps/colorscale_pink.jpg) + COLORMAP_HOT = 11, //!< ![hot](pics/colormaps/colorscale_hot.jpg) + COLORMAP_PARULA = 12, //!< ![parula](pics/colormaps/colorscale_parula.jpg) + COLORMAP_MAGMA = 13, //!< ![magma](pics/colormaps/colorscale_magma.jpg) + COLORMAP_INFERNO = 14, //!< ![inferno](pics/colormaps/colorscale_inferno.jpg) + COLORMAP_PLASMA = 15, //!< ![plasma](pics/colormaps/colorscale_plasma.jpg) + COLORMAP_VIRIDIS = 16, //!< ![viridis](pics/colormaps/colorscale_viridis.jpg) + COLORMAP_CIVIDIS = 17, //!< ![cividis](pics/colormaps/colorscale_cividis.jpg) + COLORMAP_TWILIGHT = 18, //!< ![twilight](pics/colormaps/colorscale_twilight.jpg) + COLORMAP_TWILIGHT_SHIFTED = 19, //!< ![twilight shifted](pics/colormaps/colorscale_twilight_shifted.jpg) + COLORMAP_TURBO = 20 //!< ![turbo](pics/colormaps/colorscale_turbo.jpg) +}; + +/** @example samples/cpp/falsecolor.cpp +An example using applyColorMap function +*/ + +/** @brief Applies a GNU Octave/MATLAB equivalent colormap on a given image. + +@param src The source image, grayscale or colored of type CV_8UC1 or CV_8UC3. +@param dst The result is the colormapped source image. Note: Mat::create is called on dst. +@param colormap The colormap to apply, see #ColormapTypes +*/ +CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, int colormap); + +/** @brief Applies a user colormap on a given image. + +@param src The source image, grayscale or colored of type CV_8UC1 or CV_8UC3. +@param dst The result is the colormapped source image. Note: Mat::create is called on dst. +@param userColor The colormap to apply of type CV_8UC1 or CV_8UC3 and size 256 +*/ +CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, InputArray userColor); + +//! @} imgproc_colormap + +//! @addtogroup imgproc_draw +//! @{ + + +/** OpenCV color channel order is BGR[A] */ +#define CV_RGB(r, g, b) cv::Scalar((b), (g), (r), 0) + +/** @brief Draws a line segment connecting two points. + +The function line draws the line segment between pt1 and pt2 points in the image. The line is +clipped by the image boundaries. For non-antialiased lines with integer coordinates, the 8-connected +or 4-connected Bresenham algorithm is used. Thick lines are drawn with rounding endings. Antialiased +lines are drawn using Gaussian filtering. + +@param img Image. +@param pt1 First point of the line segment. +@param pt2 Second point of the line segment. +@param color Line color. +@param thickness Line thickness. +@param lineType Type of the line. See #LineTypes. +@param shift Number of fractional bits in the point coordinates. + */ +CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0); + +/** @brief Draws a arrow segment pointing from the first point to the second one. + +The function cv::arrowedLine draws an arrow between pt1 and pt2 points in the image. See also #line. + +@param img Image. +@param pt1 The point the arrow starts from. +@param pt2 The point the arrow points to. +@param color Line color. +@param thickness Line thickness. +@param line_type Type of the line. See #LineTypes +@param shift Number of fractional bits in the point coordinates. +@param tipLength The length of the arrow tip in relation to the arrow length + */ +CV_EXPORTS_W void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness=1, int line_type=8, int shift=0, double tipLength=0.1); + +/** @brief Draws a simple, thick, or filled up-right rectangle. + +The function cv::rectangle draws a rectangle outline or a filled rectangle whose two opposite corners +are pt1 and pt2. + +@param img Image. +@param pt1 Vertex of the rectangle. +@param pt2 Vertex of the rectangle opposite to pt1 . +@param color Rectangle color or brightness (grayscale image). +@param thickness Thickness of lines that make up the rectangle. Negative values, like #FILLED, +mean that the function has to draw a filled rectangle. +@param lineType Type of the line. See #LineTypes +@param shift Number of fractional bits in the point coordinates. + */ +CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @overload + +use `rec` parameter as alternative specification of the drawn rectangle: `r.tl() and +r.br()-Point(1,1)` are opposite corners +*/ +CV_EXPORTS void rectangle(CV_IN_OUT Mat& img, Rect rec, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @example samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_2.cpp +An example using drawing functions +*/ + +/** @brief Draws a circle. + +The function cv::circle draws a simple or filled circle with a given center and radius. +@param img Image where the circle is drawn. +@param center Center of the circle. +@param radius Radius of the circle. +@param color Circle color. +@param thickness Thickness of the circle outline, if positive. Negative values, like #FILLED, +mean that a filled circle is to be drawn. +@param lineType Type of the circle boundary. See #LineTypes +@param shift Number of fractional bits in the coordinates of the center and in the radius value. + */ +CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @brief Draws a simple or thick elliptic arc or fills an ellipse sector. + +The function cv::ellipse with more parameters draws an ellipse outline, a filled ellipse, an elliptic +arc, or a filled ellipse sector. The drawing code uses general parametric form. +A piecewise-linear curve is used to approximate the elliptic arc +boundary. If you need more control of the ellipse rendering, you can retrieve the curve using +#ellipse2Poly and then render it with #polylines or fill it with #fillPoly. If you use the first +variant of the function and want to draw the whole ellipse, not an arc, pass `startAngle=0` and +`endAngle=360`. If `startAngle` is greater than `endAngle`, they are swapped. The figure below explains +the meaning of the parameters to draw the blue arc. + +![Parameters of Elliptic Arc](pics/ellipse.svg) + +@param img Image. +@param center Center of the ellipse. +@param axes Half of the size of the ellipse main axes. +@param angle Ellipse rotation angle in degrees. +@param startAngle Starting angle of the elliptic arc in degrees. +@param endAngle Ending angle of the elliptic arc in degrees. +@param color Ellipse color. +@param thickness Thickness of the ellipse arc outline, if positive. Otherwise, this indicates that +a filled ellipse sector is to be drawn. +@param lineType Type of the ellipse boundary. See #LineTypes +@param shift Number of fractional bits in the coordinates of the center and values of axes. + */ +CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes, + double angle, double startAngle, double endAngle, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @overload +@param img Image. +@param box Alternative ellipse representation via RotatedRect. This means that the function draws +an ellipse inscribed in the rotated rectangle. +@param color Ellipse color. +@param thickness Thickness of the ellipse arc outline, if positive. Otherwise, this indicates that +a filled ellipse sector is to be drawn. +@param lineType Type of the ellipse boundary. See #LineTypes +*/ +CV_EXPORTS_W void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color, + int thickness = 1, int lineType = LINE_8); + +/* ----------------------------------------------------------------------------------------- */ +/* ADDING A SET OF PREDEFINED MARKERS WHICH COULD BE USED TO HIGHLIGHT POSITIONS IN AN IMAGE */ +/* ----------------------------------------------------------------------------------------- */ + +//! Possible set of marker types used for the cv::drawMarker function +enum MarkerTypes +{ + MARKER_CROSS = 0, //!< A crosshair marker shape + MARKER_TILTED_CROSS = 1, //!< A 45 degree tilted crosshair marker shape + MARKER_STAR = 2, //!< A star marker shape, combination of cross and tilted cross + MARKER_DIAMOND = 3, //!< A diamond marker shape + MARKER_SQUARE = 4, //!< A square marker shape + MARKER_TRIANGLE_UP = 5, //!< An upwards pointing triangle marker shape + MARKER_TRIANGLE_DOWN = 6 //!< A downwards pointing triangle marker shape +}; + +/** @brief Draws a marker on a predefined position in an image. + +The function cv::drawMarker draws a marker on a given position in the image. For the moment several +marker types are supported, see #MarkerTypes for more information. + +@param img Image. +@param position The point where the crosshair is positioned. +@param color Line color. +@param markerType The specific type of marker you want to use, see #MarkerTypes +@param thickness Line thickness. +@param line_type Type of the line, See #LineTypes +@param markerSize The length of the marker axis [default = 20 pixels] + */ +CV_EXPORTS_W void drawMarker(CV_IN_OUT Mat& img, Point position, const Scalar& color, + int markerType = MARKER_CROSS, int markerSize=20, int thickness=1, + int line_type=8); + +/* ----------------------------------------------------------------------------------------- */ +/* END OF MARKER SECTION */ +/* ----------------------------------------------------------------------------------------- */ + +/** @overload */ +CV_EXPORTS void fillConvexPoly(Mat& img, const Point* pts, int npts, + const Scalar& color, int lineType = LINE_8, + int shift = 0); + +/** @brief Fills a convex polygon. + +The function cv::fillConvexPoly draws a filled convex polygon. This function is much faster than the +function #fillPoly . It can fill not only convex polygons but any monotonic polygon without +self-intersections, that is, a polygon whose contour intersects every horizontal line (scan line) +twice at the most (though, its top-most and/or the bottom edge could be horizontal). + +@param img Image. +@param points Polygon vertices. +@param color Polygon color. +@param lineType Type of the polygon boundaries. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. + */ +CV_EXPORTS_W void fillConvexPoly(InputOutputArray img, InputArray points, + const Scalar& color, int lineType = LINE_8, + int shift = 0); + +/** @overload */ +CV_EXPORTS void fillPoly(Mat& img, const Point** pts, + const int* npts, int ncontours, + const Scalar& color, int lineType = LINE_8, int shift = 0, + Point offset = Point() ); + +/** @example samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp +An example using drawing functions +Check @ref tutorial_random_generator_and_text "the corresponding tutorial" for more details +*/ + +/** @brief Fills the area bounded by one or more polygons. + +The function cv::fillPoly fills an area bounded by several polygonal contours. The function can fill +complex areas, for example, areas with holes, contours with self-intersections (some of their +parts), and so forth. + +@param img Image. +@param pts Array of polygons where each polygon is represented as an array of points. +@param color Polygon color. +@param lineType Type of the polygon boundaries. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. +@param offset Optional offset of all points of the contours. + */ +CV_EXPORTS_W void fillPoly(InputOutputArray img, InputArrayOfArrays pts, + const Scalar& color, int lineType = LINE_8, int shift = 0, + Point offset = Point() ); + +/** @overload */ +CV_EXPORTS void polylines(Mat& img, const Point* const* pts, const int* npts, + int ncontours, bool isClosed, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0 ); + +/** @brief Draws several polygonal curves. + +@param img Image. +@param pts Array of polygonal curves. +@param isClosed Flag indicating whether the drawn polylines are closed or not. If they are closed, +the function draws a line from the last vertex of each curve to its first vertex. +@param color Polyline color. +@param thickness Thickness of the polyline edges. +@param lineType Type of the line segments. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. + +The function cv::polylines draws one or more polygonal curves. + */ +CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts, + bool isClosed, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0 ); + +/** @example samples/cpp/contours2.cpp +An example program illustrates the use of cv::findContours and cv::drawContours +\image html WindowsQtContoursOutput.png "Screenshot of the program" +*/ + +/** @example samples/cpp/segment_objects.cpp +An example using drawContours to clean up a background segmentation result +*/ + +/** @brief Draws contours outlines or filled contours. + +The function draws contour outlines in the image if \f$\texttt{thickness} \ge 0\f$ or fills the area +bounded by the contours if \f$\texttt{thickness}<0\f$ . The example below shows how to retrieve +connected components from the binary image and label them: : +@include snippets/imgproc_drawContours.cpp + +@param image Destination image. +@param contours All the input contours. Each contour is stored as a point vector. +@param contourIdx Parameter indicating a contour to draw. If it is negative, all the contours are drawn. +@param color Color of the contours. +@param thickness Thickness of lines the contours are drawn with. If it is negative (for example, +thickness=#FILLED ), the contour interiors are drawn. +@param lineType Line connectivity. See #LineTypes +@param hierarchy Optional information about hierarchy. It is only needed if you want to draw only +some of the contours (see maxLevel ). +@param maxLevel Maximal level for drawn contours. If it is 0, only the specified contour is drawn. +If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function +draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This +parameter is only taken into account when there is hierarchy available. +@param offset Optional contour shift parameter. Shift all the drawn contours by the specified +\f$\texttt{offset}=(dx,dy)\f$ . +@note When thickness=#FILLED, the function is designed to handle connected components with holes correctly +even when no hierarchy date is provided. This is done by analyzing all the outlines together +using even-odd rule. This may give incorrect results if you have a joint collection of separately retrieved +contours. In order to solve this problem, you need to call #drawContours separately for each sub-group +of contours, or iterate over the collection using contourIdx parameter. + */ +CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours, + int contourIdx, const Scalar& color, + int thickness = 1, int lineType = LINE_8, + InputArray hierarchy = noArray(), + int maxLevel = INT_MAX, Point offset = Point() ); + +/** @brief Clips the line against the image rectangle. + +The function cv::clipLine calculates a part of the line segment that is entirely within the specified +rectangle. it returns false if the line segment is completely outside the rectangle. Otherwise, +it returns true . +@param imgSize Image size. The image rectangle is Rect(0, 0, imgSize.width, imgSize.height) . +@param pt1 First line point. +@param pt2 Second line point. + */ +CV_EXPORTS bool clipLine(Size imgSize, CV_IN_OUT Point& pt1, CV_IN_OUT Point& pt2); + +/** @overload +@param imgSize Image size. The image rectangle is Rect(0, 0, imgSize.width, imgSize.height) . +@param pt1 First line point. +@param pt2 Second line point. +*/ +CV_EXPORTS bool clipLine(Size2l imgSize, CV_IN_OUT Point2l& pt1, CV_IN_OUT Point2l& pt2); + +/** @overload +@param imgRect Image rectangle. +@param pt1 First line point. +@param pt2 Second line point. +*/ +CV_EXPORTS_W bool clipLine(Rect imgRect, CV_OUT CV_IN_OUT Point& pt1, CV_OUT CV_IN_OUT Point& pt2); + +/** @brief Approximates an elliptic arc with a polyline. + +The function ellipse2Poly computes the vertices of a polyline that approximates the specified +elliptic arc. It is used by #ellipse. If `arcStart` is greater than `arcEnd`, they are swapped. + +@param center Center of the arc. +@param axes Half of the size of the ellipse main axes. See #ellipse for details. +@param angle Rotation angle of the ellipse in degrees. See #ellipse for details. +@param arcStart Starting angle of the elliptic arc in degrees. +@param arcEnd Ending angle of the elliptic arc in degrees. +@param delta Angle between the subsequent polyline vertices. It defines the approximation +accuracy. +@param pts Output vector of polyline vertices. + */ +CV_EXPORTS_W void ellipse2Poly( Point center, Size axes, int angle, + int arcStart, int arcEnd, int delta, + CV_OUT std::vector& pts ); + +/** @overload +@param center Center of the arc. +@param axes Half of the size of the ellipse main axes. See #ellipse for details. +@param angle Rotation angle of the ellipse in degrees. See #ellipse for details. +@param arcStart Starting angle of the elliptic arc in degrees. +@param arcEnd Ending angle of the elliptic arc in degrees. +@param delta Angle between the subsequent polyline vertices. It defines the approximation accuracy. +@param pts Output vector of polyline vertices. +*/ +CV_EXPORTS void ellipse2Poly(Point2d center, Size2d axes, int angle, + int arcStart, int arcEnd, int delta, + CV_OUT std::vector& pts); + +/** @brief Draws a text string. + +The function cv::putText renders the specified text string in the image. Symbols that cannot be rendered +using the specified font are replaced by question marks. See #getTextSize for a text rendering code +example. + +@param img Image. +@param text Text string to be drawn. +@param org Bottom-left corner of the text string in the image. +@param fontFace Font type, see #HersheyFonts. +@param fontScale Font scale factor that is multiplied by the font-specific base size. +@param color Text color. +@param thickness Thickness of the lines used to draw a text. +@param lineType Line type. See #LineTypes +@param bottomLeftOrigin When true, the image data origin is at the bottom-left corner. Otherwise, +it is at the top-left corner. + */ +CV_EXPORTS_W void putText( InputOutputArray img, const String& text, Point org, + int fontFace, double fontScale, Scalar color, + int thickness = 1, int lineType = LINE_8, + bool bottomLeftOrigin = false ); + +/** @brief Calculates the width and height of a text string. + +The function cv::getTextSize calculates and returns the size of a box that contains the specified text. +That is, the following code renders some text, the tight box surrounding it, and the baseline: : +@code + String text = "Funny text inside the box"; + int fontFace = FONT_HERSHEY_SCRIPT_SIMPLEX; + double fontScale = 2; + int thickness = 3; + + Mat img(600, 800, CV_8UC3, Scalar::all(0)); + + int baseline=0; + Size textSize = getTextSize(text, fontFace, + fontScale, thickness, &baseline); + baseline += thickness; + + // center the text + Point textOrg((img.cols - textSize.width)/2, + (img.rows + textSize.height)/2); + + // draw the box + rectangle(img, textOrg + Point(0, baseline), + textOrg + Point(textSize.width, -textSize.height), + Scalar(0,0,255)); + // ... and the baseline first + line(img, textOrg + Point(0, thickness), + textOrg + Point(textSize.width, thickness), + Scalar(0, 0, 255)); + + // then put the text itself + putText(img, text, textOrg, fontFace, fontScale, + Scalar::all(255), thickness, 8); +@endcode + +@param text Input text string. +@param fontFace Font to use, see #HersheyFonts. +@param fontScale Font scale factor that is multiplied by the font-specific base size. +@param thickness Thickness of lines used to render the text. See #putText for details. +@param[out] baseLine y-coordinate of the baseline relative to the bottom-most text +point. +@return The size of a box that contains the specified text. + +@see putText + */ +CV_EXPORTS_W Size getTextSize(const String& text, int fontFace, + double fontScale, int thickness, + CV_OUT int* baseLine); + + +/** @brief Calculates the font-specific size to use to achieve a given height in pixels. + +@param fontFace Font to use, see cv::HersheyFonts. +@param pixelHeight Pixel height to compute the fontScale for +@param thickness Thickness of lines used to render the text.See putText for details. +@return The fontSize to use for cv::putText + +@see cv::putText +*/ +CV_EXPORTS_W double getFontScaleFromHeight(const int fontFace, + const int pixelHeight, + const int thickness = 1); + +/** @brief Line iterator + +The class is used to iterate over all the pixels on the raster line +segment connecting two specified points. + +The class LineIterator is used to get each pixel of a raster line. It +can be treated as versatile implementation of the Bresenham algorithm +where you can stop at each pixel and do some extra processing, for +example, grab pixel values along the line or draw a line with an effect +(for example, with XOR operation). + +The number of pixels along the line is stored in LineIterator::count. +The method LineIterator::pos returns the current position in the image: + +@code{.cpp} +// grabs pixels along the line (pt1, pt2) +// from 8-bit 3-channel image to the buffer +LineIterator it(img, pt1, pt2, 8); +LineIterator it2 = it; +vector buf(it.count); + +for(int i = 0; i < it.count; i++, ++it) + buf[i] = *(const Vec3b*)*it; + +// alternative way of iterating through the line +for(int i = 0; i < it2.count; i++, ++it2) +{ + Vec3b val = img.at(it2.pos()); + CV_Assert(buf[i] == val); +} +@endcode +*/ +class CV_EXPORTS LineIterator +{ +public: + /** @brief initializes the iterator + + creates iterators for the line connecting pt1 and pt2 + the line will be clipped on the image boundaries + the line is 8-connected or 4-connected + If leftToRight=true, then the iteration is always done + from the left-most point to the right most, + not to depend on the ordering of pt1 and pt2 parameters + */ + LineIterator( const Mat& img, Point pt1, Point pt2, + int connectivity = 8, bool leftToRight = false ); + /** @brief returns pointer to the current pixel + */ + uchar* operator *(); + /** @brief prefix increment operator (++it). shifts iterator to the next pixel + */ + LineIterator& operator ++(); + /** @brief postfix increment operator (it++). shifts iterator to the next pixel + */ + LineIterator operator ++(int); + /** @brief returns coordinates of the current pixel + */ + Point pos() const; + + uchar* ptr; + const uchar* ptr0; + int step, elemSize; + int err, count; + int minusDelta, plusDelta; + int minusStep, plusStep; +}; + +//! @cond IGNORED + +// === LineIterator implementation === + +inline +uchar* LineIterator::operator *() +{ + return ptr; +} + +inline +LineIterator& LineIterator::operator ++() +{ + int mask = err < 0 ? -1 : 0; + err += minusDelta + (plusDelta & mask); + ptr += minusStep + (plusStep & mask); + return *this; +} + +inline +LineIterator LineIterator::operator ++(int) +{ + LineIterator it = *this; + ++(*this); + return it; +} + +inline +Point LineIterator::pos() const +{ + Point p; + p.y = (int)((ptr - ptr0)/step); + p.x = (int)(((ptr - ptr0) - p.y*step)/elemSize); + return p; +} + +//! @endcond + +//! @} imgproc_draw + +//! @} imgproc + +} // cv + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/imgproc/imgproc_c.h" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/detail/distortion_model.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/detail/distortion_model.hpp new file mode 100644 index 0000000..a9c3dde --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/detail/distortion_model.hpp @@ -0,0 +1,123 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP +#define OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP + +//! @cond IGNORED + +namespace cv { namespace detail { +/** +Computes the matrix for the projection onto a tilted image sensor +\param tauX angular parameter rotation around x-axis +\param tauY angular parameter rotation around y-axis +\param matTilt if not NULL returns the matrix +\f[ +\vecthreethree{R_{33}(\tau_x, \tau_y)}{0}{-R_{13}((\tau_x, \tau_y)} +{0}{R_{33}(\tau_x, \tau_y)}{-R_{23}(\tau_x, \tau_y)} +{0}{0}{1} R(\tau_x, \tau_y) +\f] +where +\f[ +R(\tau_x, \tau_y) = +\vecthreethree{\cos(\tau_y)}{0}{-\sin(\tau_y)}{0}{1}{0}{\sin(\tau_y)}{0}{\cos(\tau_y)} +\vecthreethree{1}{0}{0}{0}{\cos(\tau_x)}{\sin(\tau_x)}{0}{-\sin(\tau_x)}{\cos(\tau_x)} = +\vecthreethree{\cos(\tau_y)}{\sin(\tau_y)\sin(\tau_x)}{-\sin(\tau_y)\cos(\tau_x)} +{0}{\cos(\tau_x)}{\sin(\tau_x)} +{\sin(\tau_y)}{-\cos(\tau_y)\sin(\tau_x)}{\cos(\tau_y)\cos(\tau_x)}. +\f] +\param dMatTiltdTauX if not NULL it returns the derivative of matTilt with +respect to \f$\tau_x\f$. +\param dMatTiltdTauY if not NULL it returns the derivative of matTilt with +respect to \f$\tau_y\f$. +\param invMatTilt if not NULL it returns the inverse of matTilt +**/ +template +void computeTiltProjectionMatrix(FLOAT tauX, + FLOAT tauY, + Matx* matTilt = 0, + Matx* dMatTiltdTauX = 0, + Matx* dMatTiltdTauY = 0, + Matx* invMatTilt = 0) +{ + FLOAT cTauX = cos(tauX); + FLOAT sTauX = sin(tauX); + FLOAT cTauY = cos(tauY); + FLOAT sTauY = sin(tauY); + Matx matRotX = Matx(1,0,0,0,cTauX,sTauX,0,-sTauX,cTauX); + Matx matRotY = Matx(cTauY,0,-sTauY,0,1,0,sTauY,0,cTauY); + Matx matRotXY = matRotY * matRotX; + Matx matProjZ = Matx(matRotXY(2,2),0,-matRotXY(0,2),0,matRotXY(2,2),-matRotXY(1,2),0,0,1); + if (matTilt) + { + // Matrix for trapezoidal distortion of tilted image sensor + *matTilt = matProjZ * matRotXY; + } + if (dMatTiltdTauX) + { + // Derivative with respect to tauX + Matx dMatRotXYdTauX = matRotY * Matx(0,0,0,0,-sTauX,cTauX,0,-cTauX,-sTauX); + Matx dMatProjZdTauX = Matx(dMatRotXYdTauX(2,2),0,-dMatRotXYdTauX(0,2), + 0,dMatRotXYdTauX(2,2),-dMatRotXYdTauX(1,2),0,0,0); + *dMatTiltdTauX = (matProjZ * dMatRotXYdTauX) + (dMatProjZdTauX * matRotXY); + } + if (dMatTiltdTauY) + { + // Derivative with respect to tauY + Matx dMatRotXYdTauY = Matx(-sTauY,0,-cTauY,0,0,0,cTauY,0,-sTauY) * matRotX; + Matx dMatProjZdTauY = Matx(dMatRotXYdTauY(2,2),0,-dMatRotXYdTauY(0,2), + 0,dMatRotXYdTauY(2,2),-dMatRotXYdTauY(1,2),0,0,0); + *dMatTiltdTauY = (matProjZ * dMatRotXYdTauY) + (dMatProjZdTauY * matRotXY); + } + if (invMatTilt) + { + FLOAT inv = 1./matRotXY(2,2); + Matx invMatProjZ = Matx(inv,0,inv*matRotXY(0,2),0,inv,inv*matRotXY(1,2),0,0,1); + *invMatTilt = matRotXY.t()*invMatProjZ; + } +} +}} // namespace detail, cv + + +//! @endcond + +#endif // OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/hal.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/hal.hpp new file mode 100644 index 0000000..a435fd6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/hal.hpp @@ -0,0 +1,241 @@ +#ifndef CV_IMGPROC_HAL_HPP +#define CV_IMGPROC_HAL_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/hal/interface.h" + +namespace cv { namespace hal { + +//! @addtogroup imgproc_hal_functions +//! @{ + +//--------------------------- +//! @cond IGNORED + +struct CV_EXPORTS Filter2D +{ + CV_DEPRECATED static Ptr create(uchar * , size_t , int , + int , int , + int , int , + int , int , + int , double , + int , int , + bool , bool ); + virtual void apply(uchar * , size_t , + uchar * , size_t , + int , int , + int , int , + int , int ) = 0; + virtual ~Filter2D() {} +}; + +struct CV_EXPORTS SepFilter2D +{ + CV_DEPRECATED static Ptr create(int , int , int , + uchar * , int , + uchar * , int , + int , int , + double , int ); + virtual void apply(uchar * , size_t , + uchar * , size_t , + int , int , + int , int , + int , int ) = 0; + virtual ~SepFilter2D() {} +}; + + +struct CV_EXPORTS Morph +{ + CV_DEPRECATED static Ptr create(int , int , int , int , int , + int , uchar * , size_t , + int , int , + int , int , + int , const double *, + int , bool , bool ); + virtual void apply(uchar * , size_t , uchar * , size_t , int , int , + int , int , int , int , + int , int , int , int ) = 0; + virtual ~Morph() {} +}; + +//! @endcond +//--------------------------- + +CV_EXPORTS void filter2D(int stype, int dtype, int kernel_type, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int full_width, int full_height, + int offset_x, int offset_y, + uchar * kernel_data, size_t kernel_step, + int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + double delta, int borderType, + bool isSubmatrix); + +CV_EXPORTS void sepFilter2D(int stype, int dtype, int ktype, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int full_width, int full_height, + int offset_x, int offset_y, + uchar * kernelx_data, int kernelx_len, + uchar * kernely_data, int kernely_len, + int anchor_x, int anchor_y, + double delta, int borderType); + +CV_EXPORTS void morph(int op, int src_type, int dst_type, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2, + int kernel_type, uchar * kernel_data, size_t kernel_step, + int kernel_width, int kernel_height, int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix); + + +CV_EXPORTS void resize(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + double inv_scale_x, double inv_scale_y, int interpolation); + +CV_EXPORTS void warpAffine(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + const double M[6], int interpolation, int borderType, const double borderValue[4]); + +CV_EXPORTS void warpPerspectve(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + const double M[9], int interpolation, int borderType, const double borderValue[4]); + +CV_EXPORTS void cvtBGRtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, int dcn, bool swapBlue); + +CV_EXPORTS void cvtBGRtoBGR5x5(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int greenBits); + +CV_EXPORTS void cvtBGR5x5toBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int dcn, bool swapBlue, int greenBits); + +CV_EXPORTS void cvtBGRtoGray(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue); + +CV_EXPORTS void cvtGraytoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn); + +CV_EXPORTS void cvtBGR5x5toGray(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int greenBits); + +CV_EXPORTS void cvtGraytoBGR5x5(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int greenBits); +CV_EXPORTS void cvtBGRtoYUV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isCbCr); + +CV_EXPORTS void cvtYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isCbCr); + +CV_EXPORTS void cvtBGRtoXYZ(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue); + +CV_EXPORTS void cvtXYZtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue); + +CV_EXPORTS void cvtBGRtoHSV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isFullRange, bool isHSV); + +CV_EXPORTS void cvtHSVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isFullRange, bool isHSV); + +CV_EXPORTS void cvtBGRtoLab(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isLab, bool srgb); + +CV_EXPORTS void cvtLabtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isLab, bool srgb); + +CV_EXPORTS void cvtTwoPlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +//! Separate Y and UV planes +CV_EXPORTS void cvtTwoPlaneYUVtoBGR(const uchar * y_data, const uchar * uv_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtThreePlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtBGRtoThreePlaneYUV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int uIdx); + +//! Separate Y and UV planes +CV_EXPORTS void cvtBGRtoTwoPlaneYUV(const uchar * src_data, size_t src_step, + uchar * y_data, uchar * uv_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtOnePlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int dcn, bool swapBlue, int uIdx, int ycn); + +CV_EXPORTS void cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height); + +CV_EXPORTS void cvtMultipliedRGBAtoRGBA(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height); + +CV_EXPORTS void integral(int depth, int sdepth, int sqdepth, + const uchar* src, size_t srcstep, + uchar* sum, size_t sumstep, + uchar* sqsum, size_t sqsumstep, + uchar* tilted, size_t tstep, + int width, int height, int cn); + +//! @} + +}} + +#endif // CV_IMGPROC_HAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/interface.h b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/interface.h new file mode 100644 index 0000000..f8dbcfe --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/hal/interface.h @@ -0,0 +1,46 @@ +#ifndef OPENCV_IMGPROC_HAL_INTERFACE_H +#define OPENCV_IMGPROC_HAL_INTERFACE_H + +//! @addtogroup imgproc_hal_interface +//! @{ + +//! @name Interpolation modes +//! @sa cv::InterpolationFlags +//! @{ +#define CV_HAL_INTER_NEAREST 0 +#define CV_HAL_INTER_LINEAR 1 +#define CV_HAL_INTER_CUBIC 2 +#define CV_HAL_INTER_AREA 3 +#define CV_HAL_INTER_LANCZOS4 4 +//! @} + +//! @name Morphology operations +//! @sa cv::MorphTypes +//! @{ +#define CV_HAL_MORPH_ERODE 0 +#define CV_HAL_MORPH_DILATE 1 +//! @} + +//! @name Threshold types +//! @sa cv::ThresholdTypes +//! @{ +#define CV_HAL_THRESH_BINARY 0 +#define CV_HAL_THRESH_BINARY_INV 1 +#define CV_HAL_THRESH_TRUNC 2 +#define CV_HAL_THRESH_TOZERO 3 +#define CV_HAL_THRESH_TOZERO_INV 4 +#define CV_HAL_THRESH_MASK 7 +#define CV_HAL_THRESH_OTSU 8 +#define CV_HAL_THRESH_TRIANGLE 16 +//! @} + +//! @name Adaptive threshold algorithm +//! @sa cv::AdaptiveThresholdTypes +//! @{ +#define CV_HAL_ADAPTIVE_THRESH_MEAN_C 0 +#define CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C 1 +//! @} + +//! @} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc.hpp b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc.hpp new file mode 100644 index 0000000..4175bd0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/imgproc.hpp" diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc_c.h b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc_c.h new file mode 100644 index 0000000..2fe85b6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/imgproc_c.h @@ -0,0 +1,1210 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_IMGPROC_C_H +#define OPENCV_IMGPROC_IMGPROC_C_H + +#include "opencv2/imgproc/types_c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup imgproc_c +@{ +*/ + +/*********************** Background statistics accumulation *****************************/ + +/** @brief Adds image to accumulator +@see cv::accumulate +*/ +CVAPI(void) cvAcc( const CvArr* image, CvArr* sum, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds squared image to accumulator +@see cv::accumulateSquare +*/ +CVAPI(void) cvSquareAcc( const CvArr* image, CvArr* sqsum, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds a product of two images to accumulator +@see cv::accumulateProduct +*/ +CVAPI(void) cvMultiplyAcc( const CvArr* image1, const CvArr* image2, CvArr* acc, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds image to accumulator with weights: acc = acc*(1-alpha) + image*alpha +@see cv::accumulateWeighted +*/ +CVAPI(void) cvRunningAvg( const CvArr* image, CvArr* acc, double alpha, + const CvArr* mask CV_DEFAULT(NULL) ); + +/****************************************************************************************\ +* Image Processing * +\****************************************************************************************/ + +/** Copies source 2D array inside of the larger destination array and + makes a border of the specified type (IPL_BORDER_*) around the copied area. */ +CVAPI(void) cvCopyMakeBorder( const CvArr* src, CvArr* dst, CvPoint offset, + int bordertype, CvScalar value CV_DEFAULT(cvScalarAll(0))); + +/** @brief Smooths the image in one of several ways. + +@param src The source image +@param dst The destination image +@param smoothtype Type of the smoothing, see SmoothMethod_c +@param size1 The first parameter of the smoothing operation, the aperture width. Must be a +positive odd number (1, 3, 5, ...) +@param size2 The second parameter of the smoothing operation, the aperture height. Ignored by +CV_MEDIAN and CV_BILATERAL methods. In the case of simple scaled/non-scaled and Gaussian blur if +size2 is zero, it is set to size1. Otherwise it must be a positive odd number. +@param sigma1 In the case of a Gaussian parameter this parameter may specify Gaussian \f$\sigma\f$ +(standard deviation). If it is zero, it is calculated from the kernel size: +\f[\sigma = 0.3 (n/2 - 1) + 0.8 \quad \text{where} \quad n= \begin{array}{l l} \mbox{\texttt{size1} for horizontal kernel} \\ \mbox{\texttt{size2} for vertical kernel} \end{array}\f] +Using standard sigma for small kernels ( \f$3\times 3\f$ to \f$7\times 7\f$ ) gives better speed. If +sigma1 is not zero, while size1 and size2 are zeros, the kernel size is calculated from the +sigma (to provide accurate enough operation). +@param sigma2 additional parameter for bilateral filtering + +@see cv::GaussianBlur, cv::blur, cv::medianBlur, cv::bilateralFilter. + */ +CVAPI(void) cvSmooth( const CvArr* src, CvArr* dst, + int smoothtype CV_DEFAULT(CV_GAUSSIAN), + int size1 CV_DEFAULT(3), + int size2 CV_DEFAULT(0), + double sigma1 CV_DEFAULT(0), + double sigma2 CV_DEFAULT(0)); + +/** @brief Convolves an image with the kernel. + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point +matrix; if you want to apply different kernels to different channels, split the image into +separate color planes using split and process them individually. +@param anchor anchor of the kernel that indicates the relative position of a filtered point within +the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor +is at the kernel center. + +@see cv::filter2D + */ +CVAPI(void) cvFilter2D( const CvArr* src, CvArr* dst, const CvMat* kernel, + CvPoint anchor CV_DEFAULT(cvPoint(-1,-1))); + +/** @brief Finds integral image: SUM(X,Y) = sum(x \texttt{hist1}(I)\)}{\frac{\texttt{hist2}(I) \cdot \texttt{scale}}{\texttt{hist1}(I)}}{if \(\texttt{hist1}(I) \ne 0\) and \(\texttt{hist2}(I) \le \texttt{hist1}(I)\)}\f] + +@param hist1 First histogram (the divisor). +@param hist2 Second histogram. +@param dst_hist Destination histogram. +@param scale Scale factor for the destination histogram. + */ +CVAPI(void) cvCalcProbDensity( const CvHistogram* hist1, const CvHistogram* hist2, + CvHistogram* dst_hist, double scale CV_DEFAULT(255) ); + +/** @brief equalizes histogram of 8-bit single-channel image +@see cv::equalizeHist +*/ +CVAPI(void) cvEqualizeHist( const CvArr* src, CvArr* dst ); + + +/** @brief Applies distance transform to binary image +@see cv::distanceTransform +*/ +CVAPI(void) cvDistTransform( const CvArr* src, CvArr* dst, + int distance_type CV_DEFAULT(CV_DIST_L2), + int mask_size CV_DEFAULT(3), + const float* mask CV_DEFAULT(NULL), + CvArr* labels CV_DEFAULT(NULL), + int labelType CV_DEFAULT(CV_DIST_LABEL_CCOMP)); + + +/** @brief Applies fixed-level threshold to grayscale image. + + This is a basic operation applied before retrieving contours +@see cv::threshold +*/ +CVAPI(double) cvThreshold( const CvArr* src, CvArr* dst, + double threshold, double max_value, + int threshold_type ); + +/** @brief Applies adaptive threshold to grayscale image. + + The two parameters for methods CV_ADAPTIVE_THRESH_MEAN_C and + CV_ADAPTIVE_THRESH_GAUSSIAN_C are: + neighborhood size (3, 5, 7 etc.), + and a constant subtracted from mean (...,-3,-2,-1,0,1,2,3,...) +@see cv::adaptiveThreshold +*/ +CVAPI(void) cvAdaptiveThreshold( const CvArr* src, CvArr* dst, double max_value, + int adaptive_method CV_DEFAULT(CV_ADAPTIVE_THRESH_MEAN_C), + int threshold_type CV_DEFAULT(CV_THRESH_BINARY), + int block_size CV_DEFAULT(3), + double param1 CV_DEFAULT(5)); + +/** @brief Fills the connected component until the color difference gets large enough +@see cv::floodFill +*/ +CVAPI(void) cvFloodFill( CvArr* image, CvPoint seed_point, + CvScalar new_val, CvScalar lo_diff CV_DEFAULT(cvScalarAll(0)), + CvScalar up_diff CV_DEFAULT(cvScalarAll(0)), + CvConnectedComp* comp CV_DEFAULT(NULL), + int flags CV_DEFAULT(4), + CvArr* mask CV_DEFAULT(NULL)); + +/****************************************************************************************\ +* Feature detection * +\****************************************************************************************/ + +/** @brief Runs canny edge detector +@see cv::Canny +*/ +CVAPI(void) cvCanny( const CvArr* image, CvArr* edges, double threshold1, + double threshold2, int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates constraint image for corner detection + + Dx^2 * Dyy + Dxx * Dy^2 - 2 * Dx * Dy * Dxy. + Applying threshold to the result gives coordinates of corners +@see cv::preCornerDetect +*/ +CVAPI(void) cvPreCornerDetect( const CvArr* image, CvArr* corners, + int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates eigen values and vectors of 2x2 + gradient covariation matrix at every image pixel +@see cv::cornerEigenValsAndVecs +*/ +CVAPI(void) cvCornerEigenValsAndVecs( const CvArr* image, CvArr* eigenvv, + int block_size, int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates minimal eigenvalue for 2x2 gradient covariation matrix at + every image pixel +@see cv::cornerMinEigenVal +*/ +CVAPI(void) cvCornerMinEigenVal( const CvArr* image, CvArr* eigenval, + int block_size, int aperture_size CV_DEFAULT(3) ); + +/** @brief Harris corner detector: + + Calculates det(M) - k*(trace(M)^2), where M is 2x2 gradient covariation matrix for each pixel +@see cv::cornerHarris +*/ +CVAPI(void) cvCornerHarris( const CvArr* image, CvArr* harris_response, + int block_size, int aperture_size CV_DEFAULT(3), + double k CV_DEFAULT(0.04) ); + +/** @brief Adjust corner position using some sort of gradient search +@see cv::cornerSubPix +*/ +CVAPI(void) cvFindCornerSubPix( const CvArr* image, CvPoint2D32f* corners, + int count, CvSize win, CvSize zero_zone, + CvTermCriteria criteria ); + +/** @brief Finds a sparse set of points within the selected region + that seem to be easy to track +@see cv::goodFeaturesToTrack +*/ +CVAPI(void) cvGoodFeaturesToTrack( const CvArr* image, CvArr* eig_image, + CvArr* temp_image, CvPoint2D32f* corners, + int* corner_count, double quality_level, + double min_distance, + const CvArr* mask CV_DEFAULT(NULL), + int block_size CV_DEFAULT(3), + int use_harris CV_DEFAULT(0), + double k CV_DEFAULT(0.04) ); + +/** @brief Finds lines on binary image using one of several methods. + + line_storage is either memory storage or 1 x _max number of lines_ CvMat, its + number of columns is changed by the function. + method is one of CV_HOUGH_*; + rho, theta and threshold are used for each of those methods; + param1 ~ line length, param2 ~ line gap - for probabilistic, + param1 ~ srn, param2 ~ stn - for multi-scale +@see cv::HoughLines +*/ +CVAPI(CvSeq*) cvHoughLines2( CvArr* image, void* line_storage, int method, + double rho, double theta, int threshold, + double param1 CV_DEFAULT(0), double param2 CV_DEFAULT(0), + double min_theta CV_DEFAULT(0), double max_theta CV_DEFAULT(CV_PI)); + +/** @brief Finds circles in the image +@see cv::HoughCircles +*/ +CVAPI(CvSeq*) cvHoughCircles( CvArr* image, void* circle_storage, + int method, double dp, double min_dist, + double param1 CV_DEFAULT(100), + double param2 CV_DEFAULT(100), + int min_radius CV_DEFAULT(0), + int max_radius CV_DEFAULT(0)); + +/** @brief Fits a line into set of 2d or 3d points in a robust way (M-estimator technique) +@see cv::fitLine +*/ +CVAPI(void) cvFitLine( const CvArr* points, int dist_type, double param, + double reps, double aeps, float* line ); + +/****************************************************************************************\ +* Drawing * +\****************************************************************************************/ + +/****************************************************************************************\ +* Drawing functions work with images/matrices of arbitrary type. * +* For color images the channel order is BGR[A] * +* Antialiasing is supported only for 8-bit image now. * +* All the functions include parameter color that means rgb value (that may be * +* constructed with CV_RGB macro) for color images and brightness * +* for grayscale images. * +* If a drawn figure is partially or completely outside of the image, it is clipped.* +\****************************************************************************************/ + +#define CV_FILLED -1 + +#define CV_AA 16 + +/** @brief Draws 4-connected, 8-connected or antialiased line segment connecting two points +@see cv::line +*/ +CVAPI(void) cvLine( CvArr* img, CvPoint pt1, CvPoint pt2, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +/** @brief Draws a rectangle given two opposite corners of the rectangle (pt1 & pt2) + + if thickness<0 (e.g. thickness == CV_FILLED), the filled box is drawn +@see cv::rectangle +*/ +CVAPI(void) cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + int shift CV_DEFAULT(0)); + +/** @brief Draws a rectangle specified by a CvRect structure +@see cv::rectangle +*/ +CVAPI(void) cvRectangleR( CvArr* img, CvRect r, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + int shift CV_DEFAULT(0)); + + +/** @brief Draws a circle with specified center and radius. + + Thickness works in the same way as with cvRectangle +@see cv::circle +*/ +CVAPI(void) cvCircle( CvArr* img, CvPoint center, int radius, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +/** @brief Draws ellipse outline, filled ellipse, elliptic arc or filled elliptic sector + + depending on _thickness_, _start_angle_ and _end_angle_ parameters. The resultant figure + is rotated by _angle_. All the angles are in degrees +@see cv::ellipse +*/ +CVAPI(void) cvEllipse( CvArr* img, CvPoint center, CvSize axes, + double angle, double start_angle, double end_angle, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +CV_INLINE void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color, + int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ) +{ + CvSize axes = cvSize( + cvRound(box.size.width*0.5), + cvRound(box.size.height*0.5) + ); + + cvEllipse( img, cvPointFrom32f( box.center ), axes, box.angle, + 0, 360, color, thickness, line_type, shift ); +} + +/** @brief Fills convex or monotonous polygon. +@see cv::fillConvexPoly +*/ +CVAPI(void) cvFillConvexPoly( CvArr* img, const CvPoint* pts, int npts, CvScalar color, + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +/** @brief Fills an area bounded by one or more arbitrary polygons +@see cv::fillPoly +*/ +CVAPI(void) cvFillPoly( CvArr* img, CvPoint** pts, const int* npts, + int contours, CvScalar color, + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +/** @brief Draws one or more polygonal curves +@see cv::polylines +*/ +CVAPI(void) cvPolyLine( CvArr* img, CvPoint** pts, const int* npts, int contours, + int is_closed, CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +#define cvDrawRect cvRectangle +#define cvDrawLine cvLine +#define cvDrawCircle cvCircle +#define cvDrawEllipse cvEllipse +#define cvDrawPolyLine cvPolyLine + +/** @brief Clips the line segment connecting *pt1 and *pt2 + by the rectangular window + + (0<=xptr will point to pt1 (or pt2, see left_to_right description) location in +the image. Returns the number of pixels on the line between the ending points. +@see cv::LineIterator +*/ +CVAPI(int) cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, + CvLineIterator* line_iterator, + int connectivity CV_DEFAULT(8), + int left_to_right CV_DEFAULT(0)); + +#define CV_NEXT_LINE_POINT( line_iterator ) \ +{ \ + int _line_iterator_mask = (line_iterator).err < 0 ? -1 : 0; \ + (line_iterator).err += (line_iterator).minus_delta + \ + ((line_iterator).plus_delta & _line_iterator_mask); \ + (line_iterator).ptr += (line_iterator).minus_step + \ + ((line_iterator).plus_step & _line_iterator_mask); \ +} + + +#define CV_FONT_HERSHEY_SIMPLEX 0 +#define CV_FONT_HERSHEY_PLAIN 1 +#define CV_FONT_HERSHEY_DUPLEX 2 +#define CV_FONT_HERSHEY_COMPLEX 3 +#define CV_FONT_HERSHEY_TRIPLEX 4 +#define CV_FONT_HERSHEY_COMPLEX_SMALL 5 +#define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6 +#define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7 + +#define CV_FONT_ITALIC 16 + +#define CV_FONT_VECTOR0 CV_FONT_HERSHEY_SIMPLEX + + +/** Font structure */ +typedef struct CvFont +{ + const char* nameFont; //Qt:nameFont + CvScalar color; //Qt:ColorFont -> cvScalar(blue_component, green_component, red_component[, alpha_component]) + int font_face; //Qt: bool italic /** =CV_FONT_* */ + const int* ascii; //!< font data and metrics + const int* greek; + const int* cyrillic; + float hscale, vscale; + float shear; //!< slope coefficient: 0 - normal, >0 - italic + int thickness; //!< Qt: weight /** letters thickness */ + float dx; //!< horizontal interval between letters + int line_type; //!< Qt: PointSize +} +CvFont; + +/** @brief Initializes font structure (OpenCV 1.x API). + +The function initializes the font structure that can be passed to text rendering functions. + +@param font Pointer to the font structure initialized by the function +@param font_face Font name identifier. See cv::HersheyFonts and corresponding old CV_* identifiers. +@param hscale Horizontal scale. If equal to 1.0f , the characters have the original width +depending on the font type. If equal to 0.5f , the characters are of half the original width. +@param vscale Vertical scale. If equal to 1.0f , the characters have the original height depending +on the font type. If equal to 0.5f , the characters are of half the original height. +@param shear Approximate tangent of the character slope relative to the vertical line. A zero +value means a non-italic font, 1.0f means about a 45 degree slope, etc. +@param thickness Thickness of the text strokes +@param line_type Type of the strokes, see line description + +@sa cvPutText + */ +CVAPI(void) cvInitFont( CvFont* font, int font_face, + double hscale, double vscale, + double shear CV_DEFAULT(0), + int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8)); + +CV_INLINE CvFont cvFont( double scale, int thickness CV_DEFAULT(1) ) +{ + CvFont font; + cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, scale, scale, 0, thickness, CV_AA ); + return font; +} + +/** @brief Renders text stroke with specified font and color at specified location. + CvFont should be initialized with cvInitFont +@see cvInitFont, cvGetTextSize, cvFont, cv::putText +*/ +CVAPI(void) cvPutText( CvArr* img, const char* text, CvPoint org, + const CvFont* font, CvScalar color ); + +/** @brief Calculates bounding box of text stroke (useful for alignment) +@see cv::getTextSize +*/ +CVAPI(void) cvGetTextSize( const char* text_string, const CvFont* font, + CvSize* text_size, int* baseline ); + +/** @brief Unpacks color value + +if arrtype is CV_8UC?, _color_ is treated as packed color value, otherwise the first channels +(depending on arrtype) of destination scalar are set to the same value = _color_ +*/ +CVAPI(CvScalar) cvColorToScalar( double packed_color, int arrtype ); + +/** @brief Returns the polygon points which make up the given ellipse. + +The ellipse is define by the box of size 'axes' rotated 'angle' around the 'center'. A partial +sweep of the ellipse arc can be done by specifying arc_start and arc_end to be something other than +0 and 360, respectively. The input array 'pts' must be large enough to hold the result. The total +number of points stored into 'pts' is returned by this function. +@see cv::ellipse2Poly +*/ +CVAPI(int) cvEllipse2Poly( CvPoint center, CvSize axes, + int angle, int arc_start, int arc_end, CvPoint * pts, int delta ); + +/** @brief Draws contour outlines or filled interiors on the image +@see cv::drawContours +*/ +CVAPI(void) cvDrawContours( CvArr *img, CvSeq* contour, + CvScalar external_color, CvScalar hole_color, + int max_level, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + CvPoint offset CV_DEFAULT(cvPoint(0,0))); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/imgproc/types_c.h b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/types_c.h new file mode 100644 index 0000000..d3e55f5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/imgproc/types_c.h @@ -0,0 +1,659 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_TYPES_C_H +#define OPENCV_IMGPROC_TYPES_C_H + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup imgproc_c + @{ +*/ + +/** Connected component structure */ +typedef struct CvConnectedComp +{ + double area; /** DBL_EPSILON ? 1./std::sqrt(am00) : 0; + } + operator cv::Moments() const + { + return cv::Moments(m00, m10, m01, m20, m11, m02, m30, m21, m12, m03); + } +#endif +} +CvMoments; + +#ifdef __cplusplus +} // extern "C" + +CV_INLINE CvMoments cvMoments() +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMoments self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMoments(); +#endif +} + +CV_INLINE CvMoments cvMoments(const cv::Moments& m) +{ +#if !defined(CV__ENABLE_C_API_CTORS) + double am00 = std::abs(m.m00); + CvMoments self = { + m.m00, m.m10, m.m01, m.m20, m.m11, m.m02, m.m30, m.m21, m.m12, m.m03, + m.mu20, m.mu11, m.mu02, m.mu30, m.mu21, m.mu12, m.mu03, + am00 > DBL_EPSILON ? 1./std::sqrt(am00) : 0 + }; + return self; +#else + return CvMoments(m); +#endif +} + +extern "C" { +#endif // __cplusplus + +/** Hu invariants */ +typedef struct CvHuMoments +{ + double hu1, hu2, hu3, hu4, hu5, hu6, hu7; /**< Hu invariants */ +} +CvHuMoments; + +/** Template matching methods */ +enum +{ + CV_TM_SQDIFF =0, + CV_TM_SQDIFF_NORMED =1, + CV_TM_CCORR =2, + CV_TM_CCORR_NORMED =3, + CV_TM_CCOEFF =4, + CV_TM_CCOEFF_NORMED =5 +}; + +typedef float (CV_CDECL * CvDistanceFunction)( const float* a, const float* b, void* user_param ); + +/** Contour retrieval modes */ +enum +{ + CV_RETR_EXTERNAL=0, + CV_RETR_LIST=1, + CV_RETR_CCOMP=2, + CV_RETR_TREE=3, + CV_RETR_FLOODFILL=4 +}; + +/** Contour approximation methods */ +enum +{ + CV_CHAIN_CODE=0, + CV_CHAIN_APPROX_NONE=1, + CV_CHAIN_APPROX_SIMPLE=2, + CV_CHAIN_APPROX_TC89_L1=3, + CV_CHAIN_APPROX_TC89_KCOS=4, + CV_LINK_RUNS=5 +}; + +/* +Internal structure that is used for sequential retrieving contours from the image. +It supports both hierarchical and plane variants of Suzuki algorithm. +*/ +typedef struct _CvContourScanner* CvContourScanner; + +/** Freeman chain reader state */ +typedef struct CvChainPtReader +{ + CV_SEQ_READER_FIELDS() + char code; + CvPoint pt; + schar deltas[8][2]; +} +CvChainPtReader; + +/** initializes 8-element array for fast access to 3x3 neighborhood of a pixel */ +#define CV_INIT_3X3_DELTAS( deltas, step, nch ) \ + ((deltas)[0] = (nch), (deltas)[1] = -(step) + (nch), \ + (deltas)[2] = -(step), (deltas)[3] = -(step) - (nch), \ + (deltas)[4] = -(nch), (deltas)[5] = (step) - (nch), \ + (deltas)[6] = (step), (deltas)[7] = (step) + (nch)) + + +/** Contour approximation algorithms */ +enum +{ + CV_POLY_APPROX_DP = 0 +}; + +/** Shape matching methods */ +enum +{ + CV_CONTOURS_MATCH_I1 =1, //!< \f[I_1(A,B) = \sum _{i=1...7} \left | \frac{1}{m^A_i} - \frac{1}{m^B_i} \right |\f] + CV_CONTOURS_MATCH_I2 =2, //!< \f[I_2(A,B) = \sum _{i=1...7} \left | m^A_i - m^B_i \right |\f] + CV_CONTOURS_MATCH_I3 =3 //!< \f[I_3(A,B) = \max _{i=1...7} \frac{ \left| m^A_i - m^B_i \right| }{ \left| m^A_i \right| }\f] +}; + +/** Shape orientation */ +enum +{ + CV_CLOCKWISE =1, + CV_COUNTER_CLOCKWISE =2 +}; + + +/** Convexity defect */ +typedef struct CvConvexityDefect +{ + CvPoint* start; /**< point of the contour where the defect begins */ + CvPoint* end; /**< point of the contour where the defect ends */ + CvPoint* depth_point; /**< the farthest from the convex hull point within the defect */ + float depth; /**< distance between the farthest point and the convex hull */ +} CvConvexityDefect; + + +/** Histogram comparison methods */ +enum +{ + CV_COMP_CORREL =0, + CV_COMP_CHISQR =1, + CV_COMP_INTERSECT =2, + CV_COMP_BHATTACHARYYA =3, + CV_COMP_HELLINGER =CV_COMP_BHATTACHARYYA, + CV_COMP_CHISQR_ALT =4, + CV_COMP_KL_DIV =5 +}; + +/** Mask size for distance transform */ +enum +{ + CV_DIST_MASK_3 =3, + CV_DIST_MASK_5 =5, + CV_DIST_MASK_PRECISE =0 +}; + +/** Content of output label array: connected components or pixels */ +enum +{ + CV_DIST_LABEL_CCOMP = 0, + CV_DIST_LABEL_PIXEL = 1 +}; + +/** Distance types for Distance Transform and M-estimators */ +enum +{ + CV_DIST_USER =-1, /**< User defined distance */ + CV_DIST_L1 =1, /**< distance = |x1-x2| + |y1-y2| */ + CV_DIST_L2 =2, /**< the simple euclidean distance */ + CV_DIST_C =3, /**< distance = max(|x1-x2|,|y1-y2|) */ + CV_DIST_L12 =4, /**< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1)) */ + CV_DIST_FAIR =5, /**< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998 */ + CV_DIST_WELSCH =6, /**< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846 */ + CV_DIST_HUBER =7 /**< distance = |x| threshold ? max_value : 0 */ + CV_THRESH_BINARY_INV =1, /**< value = value > threshold ? 0 : max_value */ + CV_THRESH_TRUNC =2, /**< value = value > threshold ? threshold : value */ + CV_THRESH_TOZERO =3, /**< value = value > threshold ? value : 0 */ + CV_THRESH_TOZERO_INV =4, /**< value = value > threshold ? 0 : value */ + CV_THRESH_MASK =7, + CV_THRESH_OTSU =8, /**< use Otsu algorithm to choose the optimal threshold value; + combine the flag with one of the above CV_THRESH_* values */ + CV_THRESH_TRIANGLE =16 /**< use Triangle algorithm to choose the optimal threshold value; + combine the flag with one of the above CV_THRESH_* values, but not + with CV_THRESH_OTSU */ +}; + +/** Adaptive threshold methods */ +enum +{ + CV_ADAPTIVE_THRESH_MEAN_C =0, + CV_ADAPTIVE_THRESH_GAUSSIAN_C =1 +}; + +/** FloodFill flags */ +enum +{ + CV_FLOODFILL_FIXED_RANGE =(1 << 16), + CV_FLOODFILL_MASK_ONLY =(1 << 17) +}; + + +/** Canny edge detector flags */ +enum +{ + CV_CANNY_L2_GRADIENT =(1 << 31) +}; + +/** Variants of a Hough transform */ +enum +{ + CV_HOUGH_STANDARD =0, + CV_HOUGH_PROBABILISTIC =1, + CV_HOUGH_MULTI_SCALE =2, + CV_HOUGH_GRADIENT =3 +}; + + +/* Fast search data structures */ +struct CvFeatureTree; +struct CvLSH; +struct CvLSHOperations; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/opencv.hpp b/hgdriver/3rdparty/opencv/include/opencv2/opencv.hpp new file mode 100644 index 0000000..4048158 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/opencv.hpp @@ -0,0 +1,139 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2010, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_ALL_HPP +#define OPENCV_ALL_HPP + +// File that defines what modules where included during the build of OpenCV +// These are purely the defines of the correct HAVE_OPENCV_modulename values +#include "opencv2/opencv_modules.hpp" + +// Then the list of defines is checked to include the correct headers +// Core library is always included --> without no OpenCV functionality available +#include "opencv2/core.hpp" + +// Then the optional modules are checked +#ifdef HAVE_OPENCV_CALIB3D +#include "opencv2/calib3d.hpp" +#endif +#ifdef HAVE_OPENCV_FEATURES2D +#include "opencv2/features2d.hpp" +#endif +#ifdef HAVE_OPENCV_DNN +#include "opencv2/dnn.hpp" +#endif +#ifdef HAVE_OPENCV_FLANN +#include "opencv2/flann.hpp" +#endif +#ifdef HAVE_OPENCV_HIGHGUI +#include "opencv2/highgui.hpp" +#endif +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs.hpp" +#endif +#ifdef HAVE_OPENCV_IMGPROC +#include "opencv2/imgproc.hpp" +#endif +#ifdef HAVE_OPENCV_ML +#include "opencv2/ml.hpp" +#endif +#ifdef HAVE_OPENCV_OBJDETECT +#include "opencv2/objdetect.hpp" +#endif +#ifdef HAVE_OPENCV_PHOTO +#include "opencv2/photo.hpp" +#endif +#ifdef HAVE_OPENCV_SHAPE +#include "opencv2/shape.hpp" +#endif +#ifdef HAVE_OPENCV_STITCHING +#include "opencv2/stitching.hpp" +#endif +#ifdef HAVE_OPENCV_SUPERRES +#include "opencv2/superres.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEO +#include "opencv2/video.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOSTAB +#include "opencv2/videostab.hpp" +#endif +#ifdef HAVE_OPENCV_VIZ +#include "opencv2/viz.hpp" +#endif + +// Finally CUDA specific entries are checked and added +#ifdef HAVE_OPENCV_CUDAARITHM +#include "opencv2/cudaarithm.hpp" +#endif +#ifdef HAVE_OPENCV_CUDABGSEGM +#include "opencv2/cudabgsegm.hpp" +#endif +#ifdef HAVE_OPENCV_CUDACODEC +#include "opencv2/cudacodec.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAFEATURES2D +#include "opencv2/cudafeatures2d.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAFILTERS +#include "opencv2/cudafilters.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAIMGPROC +#include "opencv2/cudaimgproc.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAOBJDETECT +#include "opencv2/cudaobjdetect.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAOPTFLOW +#include "opencv2/cudaoptflow.hpp" +#endif +#ifdef HAVE_OPENCV_CUDASTEREO +#include "opencv2/cudastereo.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAWARPING +#include "opencv2/cudawarping.hpp" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/opencv2/opencv_modules.hpp b/hgdriver/3rdparty/opencv/include/opencv2/opencv_modules.hpp new file mode 100644 index 0000000..506f595 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/opencv_modules.hpp @@ -0,0 +1,19 @@ +/* + * ** File generated automatically, do not modify ** + * + * This file defines the list of modules available in current build configuration + * + * +*/ + +// This definition means that OpenCV is built with enabled non-free code. +// For example, patented algorithms for non-profit/non-commercial use only. +/* #undef OPENCV_ENABLE_NONFREE */ + +#define HAVE_OPENCV_CORE +#define HAVE_OPENCV_HIGHGUI +#define HAVE_OPENCV_IMGCODECS +#define HAVE_OPENCV_IMGPROC +#define HAVE_OPENCV_WORLD + + diff --git a/hgdriver/3rdparty/opencv/include/opencv2/world.hpp b/hgdriver/3rdparty/opencv/include/opencv2/world.hpp new file mode 100644 index 0000000..4902c2f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/opencv2/world.hpp @@ -0,0 +1,58 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2010, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_WORLD_HPP +#define OPENCV_WORLD_HPP + +#include "opencv2/core.hpp" + +#ifdef __cplusplus +namespace cv +{ + +CV_EXPORTS_W bool initAll(); + +} + +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cv.h b/hgdriver/3rdparty/opencv/include/win/opencv/cv.h new file mode 100644 index 0000000..19a74e2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cv.h @@ -0,0 +1,73 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CV_H +#define OPENCV_OLD_CV_H + +#if defined(_MSC_VER) + #define CV_DO_PRAGMA(x) __pragma(x) + #define __CVSTR2__(x) #x + #define __CVSTR1__(x) __CVSTR2__(x) + #define __CVMSVCLOC__ __FILE__ "("__CVSTR1__(__LINE__)") : " + #define CV_MSG_PRAGMA(_msg) CV_DO_PRAGMA(message (__CVMSVCLOC__ _msg)) +#elif defined(__GNUC__) + #define CV_DO_PRAGMA(x) _Pragma (#x) + #define CV_MSG_PRAGMA(_msg) CV_DO_PRAGMA(message (_msg)) +#else + #define CV_DO_PRAGMA(x) + #define CV_MSG_PRAGMA(_msg) +#endif +#define CV_WARNING(x) CV_MSG_PRAGMA("Warning: " #x) + +//CV_WARNING("This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module") + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/photo/photo_c.h" +#include "opencv2/video/tracking_c.h" +#include "opencv2/objdetect/objdetect_c.h" + +#if !defined(CV_IMPL) +#define CV_IMPL extern "C" +#endif //CV_IMPL + +#endif // __OPENCV_OLD_CV_H_ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cv.hpp b/hgdriver/3rdparty/opencv/include/win/opencv/cv.hpp new file mode 100644 index 0000000..8673956 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cv.hpp @@ -0,0 +1,60 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CV_HPP +#define OPENCV_OLD_CV_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cv.h" +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/photo.hpp" +#include "opencv2/video.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/calib3d.hpp" +#include "opencv2/objdetect.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.h b/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.h new file mode 100644 index 0000000..c0367cc --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.h @@ -0,0 +1,57 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_AUX_H +#define OPENCV_OLD_AUX_H + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/photo/photo_c.h" +#include "opencv2/video/tracking_c.h" +#include "opencv2/objdetect/objdetect_c.h" + +#endif + +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.hpp b/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.hpp new file mode 100644 index 0000000..4888eef --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cvaux.hpp @@ -0,0 +1,52 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_AUX_HPP +#define OPENCV_OLD_AUX_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cvaux.h" +#include "opencv2/core/utility.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cvwimage.h b/hgdriver/3rdparty/opencv/include/win/opencv/cvwimage.h new file mode 100644 index 0000000..ec0ab14 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cvwimage.h @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to +// this license. If you do not agree to this license, do not download, +// install, copy or use the software. +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2008, Google, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation or contributors may not be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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 OPENCV_OLD_WIMAGE_HPP +#define OPENCV_OLD_WIMAGE_HPP + +#include "opencv2/core/wimage.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.h b/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.h new file mode 100644 index 0000000..dc070c7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.h @@ -0,0 +1,52 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CXCORE_H +#define OPENCV_OLD_CXCORE_H + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "opencv2/core/core_c.h" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.hpp b/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.hpp new file mode 100644 index 0000000..c371677 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cxcore.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_CXCORE_HPP +#define OPENCV_OLD_CXCORE_HPP + +//#if defined(__GNUC__) +//#warning "This is a deprecated opencv header provided for compatibility. Please include a header from a corresponding opencv module" +//#endif + +#include "cxcore.h" +#include "opencv2/core.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cxeigen.hpp b/hgdriver/3rdparty/opencv/include/win/opencv/cxeigen.hpp new file mode 100644 index 0000000..1d3df91 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cxeigen.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_EIGEN_HPP +#define OPENCV_OLD_EIGEN_HPP + +#include "opencv2/core/eigen.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/cxmisc.h b/hgdriver/3rdparty/opencv/include/win/opencv/cxmisc.h new file mode 100644 index 0000000..9b9bc82 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/cxmisc.h @@ -0,0 +1,8 @@ +#ifndef OPENCV_OLD_CXMISC_H +#define OPENCV_OLD_CXMISC_H + +#ifdef __cplusplus +# include "opencv2/core/utility.hpp" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/highgui.h b/hgdriver/3rdparty/opencv/include/win/opencv/highgui.h new file mode 100644 index 0000000..69b394e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/highgui.h @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_HIGHGUI_H +#define OPENCV_OLD_HIGHGUI_H + +#include "opencv2/core/core_c.h" +#include "opencv2/highgui/highgui_c.h" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv/ml.h b/hgdriver/3rdparty/opencv/include/win/opencv/ml.h new file mode 100644 index 0000000..0c376ba --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv/ml.h @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OLD_ML_H +#define OPENCV_OLD_ML_H + +#include "opencv2/core/core_c.h" +#include "opencv2/ml.hpp" + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core.hpp new file mode 100644 index 0000000..be0a3a0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core.hpp @@ -0,0 +1,3298 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +// Copyright (C) 2015, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_HPP +#define OPENCV_CORE_HPP + +#ifndef __cplusplus +# error core.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/base.hpp" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/traits.hpp" +#include "opencv2/core/matx.hpp" +#include "opencv2/core/types.hpp" +#include "opencv2/core/mat.hpp" +#include "opencv2/core/persistence.hpp" + +/** +@defgroup core Core functionality +@{ + @defgroup core_basic Basic structures + @defgroup core_c C structures and operations + @{ + @defgroup core_c_glue Connections with C++ + @} + @defgroup core_array Operations on arrays + @defgroup core_async Asynchronous API + @defgroup core_xml XML/YAML Persistence + @defgroup core_cluster Clustering + @defgroup core_utils Utility and system functions and macros + @{ + @defgroup core_logging Logging facilities + @defgroup core_utils_sse SSE utilities + @defgroup core_utils_neon NEON utilities + @defgroup core_utils_vsx VSX utilities + @defgroup core_utils_softfloat Softfloat support + @defgroup core_utils_samples Utility functions for OpenCV samples + @} + @defgroup core_opengl OpenGL interoperability + @defgroup core_ipp Intel IPP Asynchronous C/C++ Converters + @defgroup core_optim Optimization Algorithms + @defgroup core_directx DirectX interoperability + @defgroup core_eigen Eigen support + @defgroup core_opencl OpenCL support + @defgroup core_va_intel Intel VA-API/OpenCL (CL-VA) interoperability + @defgroup core_hal Hardware Acceleration Layer + @{ + @defgroup core_hal_functions Functions + @defgroup core_hal_interface Interface + @defgroup core_hal_intrin Universal intrinsics + @{ + @defgroup core_hal_intrin_impl Private implementation helpers + @} + @} +@} + */ + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +/*! @brief Class passed to an error. + +This class encapsulates all or almost all necessary +information about the error happened in the program. The exception is +usually constructed and thrown implicitly via CV_Error and CV_Error_ macros. +@see error + */ +class CV_EXPORTS Exception : public std::exception +{ +public: + /*! + Default constructor + */ + Exception(); + /*! + Full constructor. Normally the constructor is not called explicitly. + Instead, the macros CV_Error(), CV_Error_() and CV_Assert() are used. + */ + Exception(int _code, const String& _err, const String& _func, const String& _file, int _line); + virtual ~Exception() throw(); + + /*! + \return the error description and the context as a text string. + */ + virtual const char *what() const throw() CV_OVERRIDE; + void formatMessage(); + + String msg; ///< the formatted error message + + int code; ///< error code @see CVStatus + String err; ///< error description + String func; ///< function name. Available only when the compiler supports getting it + String file; ///< source file name where the error has occurred + int line; ///< line number in the source file where the error has occurred +}; + +/*! @brief Signals an error and raises the exception. + +By default the function prints information about the error to stderr, +then it either stops if cv::setBreakOnError() had been called before or raises the exception. +It is possible to alternate error processing by using #redirectError(). +@param exc the exception raisen. +@deprecated drop this version + */ +CV_EXPORTS void error( const Exception& exc ); + +enum SortFlags { SORT_EVERY_ROW = 0, //!< each matrix row is sorted independently + SORT_EVERY_COLUMN = 1, //!< each matrix column is sorted + //!< independently; this flag and the previous one are + //!< mutually exclusive. + SORT_ASCENDING = 0, //!< each matrix row is sorted in the ascending + //!< order. + SORT_DESCENDING = 16 //!< each matrix row is sorted in the + //!< descending order; this flag and the previous one are also + //!< mutually exclusive. + }; + +//! @} core_utils + +//! @addtogroup core +//! @{ + +//! Covariation flags +enum CovarFlags { + /** The output covariance matrix is calculated as: + \f[\texttt{scale} \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...]^T \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...],\f] + The covariance matrix will be nsamples x nsamples. Such an unusual covariance matrix is used + for fast PCA of a set of very large vectors (see, for example, the EigenFaces technique for + face recognition). Eigenvalues of this "scrambled" matrix match the eigenvalues of the true + covariance matrix. The "true" eigenvectors can be easily calculated from the eigenvectors of + the "scrambled" covariance matrix. */ + COVAR_SCRAMBLED = 0, + /**The output covariance matrix is calculated as: + \f[\texttt{scale} \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...] \cdot [ \texttt{vects} [0]- \texttt{mean} , \texttt{vects} [1]- \texttt{mean} ,...]^T,\f] + covar will be a square matrix of the same size as the total number of elements in each input + vector. One and only one of #COVAR_SCRAMBLED and #COVAR_NORMAL must be specified.*/ + COVAR_NORMAL = 1, + /** If the flag is specified, the function does not calculate mean from + the input vectors but, instead, uses the passed mean vector. This is useful if mean has been + pre-calculated or known in advance, or if the covariance matrix is calculated by parts. In + this case, mean is not a mean vector of the input sub-set of vectors but rather the mean + vector of the whole set.*/ + COVAR_USE_AVG = 2, + /** If the flag is specified, the covariance matrix is scaled. In the + "normal" mode, scale is 1./nsamples . In the "scrambled" mode, scale is the reciprocal of the + total number of elements in each input vector. By default (if the flag is not specified), the + covariance matrix is not scaled ( scale=1 ).*/ + COVAR_SCALE = 4, + /** If the flag is + specified, all the input vectors are stored as rows of the samples matrix. mean should be a + single-row vector in this case.*/ + COVAR_ROWS = 8, + /** If the flag is + specified, all the input vectors are stored as columns of the samples matrix. mean should be a + single-column vector in this case.*/ + COVAR_COLS = 16 +}; + +//! @addtogroup core_cluster +//! @{ + +//! k-Means flags +enum KmeansFlags { + /** Select random initial centers in each attempt.*/ + KMEANS_RANDOM_CENTERS = 0, + /** Use kmeans++ center initialization by Arthur and Vassilvitskii [Arthur2007].*/ + KMEANS_PP_CENTERS = 2, + /** During the first (and possibly the only) attempt, use the + user-supplied labels instead of computing them from the initial centers. For the second and + further attempts, use the random or semi-random centers. Use one of KMEANS_\*_CENTERS flag + to specify the exact method.*/ + KMEANS_USE_INITIAL_LABELS = 1 +}; + +//! @} core_cluster + +//! type of line +enum LineTypes { + FILLED = -1, + LINE_4 = 4, //!< 4-connected line + LINE_8 = 8, //!< 8-connected line + LINE_AA = 16 //!< antialiased line +}; + +//! Only a subset of Hershey fonts are supported +enum HersheyFonts { + FONT_HERSHEY_SIMPLEX = 0, //!< normal size sans-serif font + FONT_HERSHEY_PLAIN = 1, //!< small size sans-serif font + FONT_HERSHEY_DUPLEX = 2, //!< normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX) + FONT_HERSHEY_COMPLEX = 3, //!< normal size serif font + FONT_HERSHEY_TRIPLEX = 4, //!< normal size serif font (more complex than FONT_HERSHEY_COMPLEX) + FONT_HERSHEY_COMPLEX_SMALL = 5, //!< smaller version of FONT_HERSHEY_COMPLEX + FONT_HERSHEY_SCRIPT_SIMPLEX = 6, //!< hand-writing style font + FONT_HERSHEY_SCRIPT_COMPLEX = 7, //!< more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX + FONT_ITALIC = 16 //!< flag for italic font +}; + +//! @addtogroup core_array +//! @{ + +enum ReduceTypes { REDUCE_SUM = 0, //!< the output is the sum of all rows/columns of the matrix. + REDUCE_AVG = 1, //!< the output is the mean vector of all rows/columns of the matrix. + REDUCE_MAX = 2, //!< the output is the maximum (column/row-wise) of all rows/columns of the matrix. + REDUCE_MIN = 3 //!< the output is the minimum (column/row-wise) of all rows/columns of the matrix. + }; + +//! @} core_array + +/** @brief Swaps two matrices +*/ +CV_EXPORTS void swap(Mat& a, Mat& b); +/** @overload */ +CV_EXPORTS void swap( UMat& a, UMat& b ); + +//! @} core + +//! @addtogroup core_array +//! @{ + +/** @brief Computes the source location of an extrapolated pixel. + +The function computes and returns the coordinate of a donor pixel corresponding to the specified +extrapolated pixel when using the specified extrapolation border mode. For example, if you use +cv::BORDER_WRAP mode in the horizontal direction, cv::BORDER_REFLECT_101 in the vertical direction and +want to compute value of the "virtual" pixel Point(-5, 100) in a floating-point image img , it +looks like: +@code{.cpp} + float val = img.at(borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101), + borderInterpolate(-5, img.cols, cv::BORDER_WRAP)); +@endcode +Normally, the function is not called directly. It is used inside filtering functions and also in +copyMakeBorder. +@param p 0-based coordinate of the extrapolated pixel along one of the axes, likely \<0 or \>= len +@param len Length of the array along the corresponding axis. +@param borderType Border type, one of the #BorderTypes, except for #BORDER_TRANSPARENT and +#BORDER_ISOLATED . When borderType==#BORDER_CONSTANT , the function always returns -1, regardless +of p and len. + +@sa copyMakeBorder +*/ +CV_EXPORTS_W int borderInterpolate(int p, int len, int borderType); + +/** @example samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp +An example using copyMakeBorder function. +Check @ref tutorial_copyMakeBorder "the corresponding tutorial" for more details +*/ + +/** @brief Forms a border around an image. + +The function copies the source image into the middle of the destination image. The areas to the +left, to the right, above and below the copied source image will be filled with extrapolated +pixels. This is not what filtering functions based on it do (they extrapolate pixels on-fly), but +what other more complex functions, including your own, may do to simplify image boundary handling. + +The function supports the mode when src is already in the middle of dst . In this case, the +function does not copy src itself but simply constructs the border, for example: + +@code{.cpp} + // let border be the same in all directions + int border=2; + // constructs a larger image to fit both the image and the border + Mat gray_buf(rgb.rows + border*2, rgb.cols + border*2, rgb.depth()); + // select the middle part of it w/o copying data + Mat gray(gray_canvas, Rect(border, border, rgb.cols, rgb.rows)); + // convert image from RGB to grayscale + cvtColor(rgb, gray, COLOR_RGB2GRAY); + // form a border in-place + copyMakeBorder(gray, gray_buf, border, border, + border, border, BORDER_REPLICATE); + // now do some custom filtering ... + ... +@endcode +@note When the source image is a part (ROI) of a bigger image, the function will try to use the +pixels outside of the ROI to form a border. To disable this feature and always do extrapolation, as +if src was not a ROI, use borderType | #BORDER_ISOLATED. + +@param src Source image. +@param dst Destination image of the same type as src and the size Size(src.cols+left+right, +src.rows+top+bottom) . +@param top the top pixels +@param bottom the bottom pixels +@param left the left pixels +@param right Parameter specifying how many pixels in each direction from the source image rectangle +to extrapolate. For example, top=1, bottom=1, left=1, right=1 mean that 1 pixel-wide border needs +to be built. +@param borderType Border type. See borderInterpolate for details. +@param value Border value if borderType==BORDER_CONSTANT . + +@sa borderInterpolate +*/ +CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst, + int top, int bottom, int left, int right, + int borderType, const Scalar& value = Scalar() ); + +/** @brief Calculates the per-element sum of two arrays or an array and a scalar. + +The function add calculates: +- Sum of two arrays when both input arrays have the same size and the same number of channels: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) + \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f] +- Sum of an array and a scalar when src2 is constructed from Scalar or has the same number of +elements as `src1.channels()`: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) + \texttt{src2} ) \quad \texttt{if mask}(I) \ne0\f] +- Sum of a scalar and an array when src1 is constructed from Scalar or has the same number of +elements as `src2.channels()`: +\f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1} + \texttt{src2}(I) ) \quad \texttt{if mask}(I) \ne0\f] +where `I` is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +The first function in the list above can be replaced with matrix expressions: +@code{.cpp} + dst = src1 + src2; + dst += src1; // equivalent to add(dst, src1, dst); +@endcode +The input arrays and the output array can all have the same or different depths. For example, you +can add a 16-bit unsigned array to a 8-bit signed array and store the sum as a 32-bit +floating-point array. Depth of the output array is determined by the dtype parameter. In the second +and third cases above, as well as in the first case, when src1.depth() == src2.depth(), dtype can +be set to the default -1. In this case, the output array will have the same depth as the input +array, be it src1, src2 or both. +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and number of channels as the input array(s); the +depth is defined by dtype or src1/src2. +@param mask optional operation mask - 8-bit single channel array, that specifies elements of the +output array to be changed. +@param dtype optional depth of the output array (see the discussion below). +@sa subtract, addWeighted, scaleAdd, Mat::convertTo +*/ +CV_EXPORTS_W void add(InputArray src1, InputArray src2, OutputArray dst, + InputArray mask = noArray(), int dtype = -1); + +/** @brief Calculates the per-element difference between two arrays or array and a scalar. + +The function subtract calculates: +- Difference between two arrays, when both input arrays have the same size and the same number of +channels: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) - \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f] +- Difference between an array and a scalar, when src2 is constructed from Scalar or has the same +number of elements as `src1.channels()`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1}(I) - \texttt{src2} ) \quad \texttt{if mask}(I) \ne0\f] +- Difference between a scalar and an array, when src1 is constructed from Scalar or has the same +number of elements as `src2.channels()`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src1} - \texttt{src2}(I) ) \quad \texttt{if mask}(I) \ne0\f] +- The reverse difference between a scalar and an array in the case of `SubRS`: + \f[\texttt{dst}(I) = \texttt{saturate} ( \texttt{src2} - \texttt{src1}(I) ) \quad \texttt{if mask}(I) \ne0\f] +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +The first function in the list above can be replaced with matrix expressions: +@code{.cpp} + dst = src1 - src2; + dst -= src1; // equivalent to subtract(dst, src1, dst); +@endcode +The input arrays and the output array can all have the same or different depths. For example, you +can subtract to 8-bit unsigned arrays and store the difference in a 16-bit signed array. Depth of +the output array is determined by dtype parameter. In the second and third cases above, as well as +in the first case, when src1.depth() == src2.depth(), dtype can be set to the default -1. In this +case the output array will have the same depth as the input array, be it src1, src2 or both. +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array of the same size and the same number of channels as the input array. +@param mask optional operation mask; this is an 8-bit single channel array that specifies elements +of the output array to be changed. +@param dtype optional depth of the output array +@sa add, addWeighted, scaleAdd, Mat::convertTo + */ +CV_EXPORTS_W void subtract(InputArray src1, InputArray src2, OutputArray dst, + InputArray mask = noArray(), int dtype = -1); + + +/** @brief Calculates the per-element scaled product of two arrays. + +The function multiply calculates the per-element product of two arrays: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{scale} \cdot \texttt{src1} (I) \cdot \texttt{src2} (I))\f] + +There is also a @ref MatrixExpressions -friendly variant of the first function. See Mat::mul . + +For a not-per-element matrix product, see gemm . + +@note Saturation is not applied when the output array has the depth +CV_32S. You may even get result of an incorrect sign in the case of +overflow. +@param src1 first input array. +@param src2 second input array of the same size and the same type as src1. +@param dst output array of the same size and type as src1. +@param scale optional scale factor. +@param dtype optional depth of the output array +@sa add, subtract, divide, scaleAdd, addWeighted, accumulate, accumulateProduct, accumulateSquare, +Mat::convertTo +*/ +CV_EXPORTS_W void multiply(InputArray src1, InputArray src2, + OutputArray dst, double scale = 1, int dtype = -1); + +/** @brief Performs per-element division of two arrays or a scalar by an array. + +The function cv::divide divides one array by another: +\f[\texttt{dst(I) = saturate(src1(I)*scale/src2(I))}\f] +or a scalar by an array when there is no src1 : +\f[\texttt{dst(I) = saturate(scale/src2(I))}\f] + +When src2(I) is zero, dst(I) will also be zero. Different channels of +multi-channel arrays are processed independently. + +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array. +@param src2 second input array of the same size and type as src1. +@param scale scalar factor. +@param dst output array of the same size and type as src2. +@param dtype optional depth of the output array; if -1, dst will have depth src2.depth(), but in +case of an array-by-array division, you can only pass -1 when src1.depth()==src2.depth(). +@sa multiply, add, subtract +*/ +CV_EXPORTS_W void divide(InputArray src1, InputArray src2, OutputArray dst, + double scale = 1, int dtype = -1); + +/** @overload */ +CV_EXPORTS_W void divide(double scale, InputArray src2, + OutputArray dst, int dtype = -1); + +/** @brief Calculates the sum of a scaled array and another array. + +The function scaleAdd is one of the classical primitive linear algebra operations, known as DAXPY +or SAXPY in [BLAS](http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms). It calculates +the sum of a scaled array and another array: +\f[\texttt{dst} (I)= \texttt{scale} \cdot \texttt{src1} (I) + \texttt{src2} (I)\f] +The function can also be emulated with a matrix expression, for example: +@code{.cpp} + Mat A(3, 3, CV_64F); + ... + A.row(0) = A.row(1)*2 + A.row(2); +@endcode +@param src1 first input array. +@param alpha scale factor for the first array. +@param src2 second input array of the same size and type as src1. +@param dst output array of the same size and type as src1. +@sa add, addWeighted, subtract, Mat::dot, Mat::convertTo +*/ +CV_EXPORTS_W void scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst); + +/** @example samples/cpp/tutorial_code/HighGUI/AddingImagesTrackbar.cpp +Check @ref tutorial_trackbar "the corresponding tutorial" for more details +*/ + +/** @brief Calculates the weighted sum of two arrays. + +The function addWeighted calculates the weighted sum of two arrays as follows: +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} + \texttt{src2} (I)* \texttt{beta} + \texttt{gamma} )\f] +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. +The function can be replaced with a matrix expression: +@code{.cpp} + dst = src1*alpha + src2*beta + gamma; +@endcode +@note Saturation is not applied when the output array has the depth CV_32S. You may even get +result of an incorrect sign in the case of overflow. +@param src1 first input array. +@param alpha weight of the first array elements. +@param src2 second input array of the same size and channel number as src1. +@param beta weight of the second array elements. +@param gamma scalar added to each sum. +@param dst output array that has the same size and number of channels as the input arrays. +@param dtype optional depth of the output array; when both input arrays have the same depth, dtype +can be set to -1, which will be equivalent to src1.depth(). +@sa add, subtract, scaleAdd, Mat::convertTo +*/ +CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2, + double beta, double gamma, OutputArray dst, int dtype = -1); + +/** @brief Scales, calculates absolute values, and converts the result to 8-bit. + +On each element of the input array, the function convertScaleAbs +performs three operations sequentially: scaling, taking an absolute +value, conversion to an unsigned 8-bit type: +\f[\texttt{dst} (I)= \texttt{saturate\_cast} (| \texttt{src} (I)* \texttt{alpha} + \texttt{beta} |)\f] +In case of multi-channel arrays, the function processes each channel +independently. When the output is not 8-bit, the operation can be +emulated by calling the Mat::convertTo method (or by using matrix +expressions) and then by calculating an absolute value of the result. +For example: +@code{.cpp} + Mat_ A(30,30); + randu(A, Scalar(-100), Scalar(100)); + Mat_ B = A*5 + 3; + B = abs(B); + // Mat_ B = abs(A*5+3) will also do the job, + // but it will allocate a temporary matrix +@endcode +@param src input array. +@param dst output array. +@param alpha optional scale factor. +@param beta optional delta added to the scaled values. +@sa Mat::convertTo, cv::abs(const Mat&) +*/ +CV_EXPORTS_W void convertScaleAbs(InputArray src, OutputArray dst, + double alpha = 1, double beta = 0); + +/** @brief Converts an array to half precision floating number. + +This function converts FP32 (single precision floating point) from/to FP16 (half precision floating point). CV_16S format is used to represent FP16 data. +There are two use modes (src -> dst): CV_32F -> CV_16S and CV_16S -> CV_32F. The input array has to have type of CV_32F or +CV_16S to represent the bit depth. If the input array is neither of them, the function will raise an error. +The format of half precision floating point is defined in IEEE 754-2008. + +@param src input array. +@param dst output array. +*/ +CV_EXPORTS_W void convertFp16(InputArray src, OutputArray dst); + +/** @brief Performs a look-up table transform of an array. + +The function LUT fills the output array with values from the look-up table. Indices of the entries +are taken from the input array. That is, the function processes each element of src as follows: +\f[\texttt{dst} (I) \leftarrow \texttt{lut(src(I) + d)}\f] +where +\f[d = \fork{0}{if \(\texttt{src}\) has depth \(\texttt{CV_8U}\)}{128}{if \(\texttt{src}\) has depth \(\texttt{CV_8S}\)}\f] +@param src input array of 8-bit elements. +@param lut look-up table of 256 elements; in case of multi-channel input array, the table should +either have a single channel (in this case the same table is used for all channels) or the same +number of channels as in the input array. +@param dst output array of the same size and number of channels as src, and the same depth as lut. +@sa convertScaleAbs, Mat::convertTo +*/ +CV_EXPORTS_W void LUT(InputArray src, InputArray lut, OutputArray dst); + +/** @brief Calculates the sum of array elements. + +The function cv::sum calculates and returns the sum of array elements, +independently for each channel. +@param src input array that must have from 1 to 4 channels. +@sa countNonZero, mean, meanStdDev, norm, minMaxLoc, reduce +*/ +CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src); + +/** @brief Counts non-zero array elements. + +The function returns the number of non-zero elements in src : +\f[\sum _{I: \; \texttt{src} (I) \ne0 } 1\f] +@param src single-channel array. +@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix +*/ +CV_EXPORTS_W int countNonZero( InputArray src ); + +/** @brief Returns the list of locations of non-zero pixels + +Given a binary matrix (likely returned from an operation such +as threshold(), compare(), >, ==, etc, return all of +the non-zero indices as a cv::Mat or std::vector (x,y) +For example: +@code{.cpp} + cv::Mat binaryImage; // input, binary image + cv::Mat locations; // output, locations of non-zero pixels + cv::findNonZero(binaryImage, locations); + + // access pixel coordinates + Point pnt = locations.at(i); +@endcode +or +@code{.cpp} + cv::Mat binaryImage; // input, binary image + vector locations; // output, locations of non-zero pixels + cv::findNonZero(binaryImage, locations); + + // access pixel coordinates + Point pnt = locations[i]; +@endcode +@param src single-channel array (type CV_8UC1) +@param idx the output array, type of cv::Mat or std::vector, corresponding to non-zero indices in the input +*/ +CV_EXPORTS_W void findNonZero( InputArray src, OutputArray idx ); + +/** @brief Calculates an average (mean) of array elements. + +The function cv::mean calculates the mean value M of array elements, +independently for each channel, and return it: +\f[\begin{array}{l} N = \sum _{I: \; \texttt{mask} (I) \ne 0} 1 \\ M_c = \left ( \sum _{I: \; \texttt{mask} (I) \ne 0}{ \texttt{mtx} (I)_c} \right )/N \end{array}\f] +When all the mask elements are 0's, the function returns Scalar::all(0) +@param src input array that should have from 1 to 4 channels so that the result can be stored in +Scalar_ . +@param mask optional operation mask. +@sa countNonZero, meanStdDev, norm, minMaxLoc +*/ +CV_EXPORTS_W Scalar mean(InputArray src, InputArray mask = noArray()); + +/** Calculates a mean and standard deviation of array elements. + +The function cv::meanStdDev calculates the mean and the standard deviation M +of array elements independently for each channel and returns it via the +output parameters: +\f[\begin{array}{l} N = \sum _{I, \texttt{mask} (I) \ne 0} 1 \\ \texttt{mean} _c = \frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \texttt{src} (I)_c}{N} \\ \texttt{stddev} _c = \sqrt{\frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \left ( \texttt{src} (I)_c - \texttt{mean} _c \right )^2}{N}} \end{array}\f] +When all the mask elements are 0's, the function returns +mean=stddev=Scalar::all(0). +@note The calculated standard deviation is only the diagonal of the +complete normalized covariance matrix. If the full matrix is needed, you +can reshape the multi-channel array M x N to the single-channel array +M\*N x mtx.channels() (only possible when the matrix is continuous) and +then pass the matrix to calcCovarMatrix . +@param src input array that should have from 1 to 4 channels so that the results can be stored in +Scalar_ 's. +@param mean output parameter: calculated mean value. +@param stddev output parameter: calculated standard deviation. +@param mask optional operation mask. +@sa countNonZero, mean, norm, minMaxLoc, calcCovarMatrix +*/ +CV_EXPORTS_W void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, + InputArray mask=noArray()); + +/** @brief Calculates the absolute norm of an array. + +This version of #norm calculates the absolute norm of src1. The type of norm to calculate is specified using #NormTypes. + +As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$. +The \f$ L_{1}, L_{2} \f$ and \f$ L_{\infty} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$ +is calculated as follows +\f{align*} + \| r(-1) \|_{L_1} &= |-1| + |2| = 3 \\ + \| r(-1) \|_{L_2} &= \sqrt{(-1)^{2} + (2)^{2}} = \sqrt{5} \\ + \| r(-1) \|_{L_\infty} &= \max(|-1|,|2|) = 2 +\f} +and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is +\f{align*} + \| r(0.5) \|_{L_1} &= |0.5| + |0.5| = 1 \\ + \| r(0.5) \|_{L_2} &= \sqrt{(0.5)^{2} + (0.5)^{2}} = \sqrt{0.5} \\ + \| r(0.5) \|_{L_\infty} &= \max(|0.5|,|0.5|) = 0.5. +\f} +The following graphic shows all values for the three norm functions \f$\| r(x) \|_{L_1}, \| r(x) \|_{L_2}\f$ and \f$\| r(x) \|_{L_\infty}\f$. +It is notable that the \f$ L_{1} \f$ norm forms the upper and the \f$ L_{\infty} \f$ norm forms the lower border for the example function \f$ r(x) \f$. +![Graphs for the different norm functions from the above example](pics/NormTypes_OneArray_1-2-INF.png) + +When the mask parameter is specified and it is not empty, the norm is + +If normType is not specified, #NORM_L2 is used. +calculated only over the region specified by the mask. + +Multi-channel input arrays are treated as single-channel arrays, that is, +the results for all channels are combined. + +Hamming norms can only be calculated with CV_8U depth arrays. + +@param src1 first input array. +@param normType type of the norm (see #NormTypes). +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +*/ +CV_EXPORTS_W double norm(InputArray src1, int normType = NORM_L2, InputArray mask = noArray()); + +/** @brief Calculates an absolute difference norm or a relative difference norm. + +This version of cv::norm calculates the absolute difference norm +or the relative difference norm of arrays src1 and src2. +The type of norm to calculate is specified using #NormTypes. + +@param src1 first input array. +@param src2 second input array of the same size and the same type as src1. +@param normType type of the norm (see #NormTypes). +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +*/ +CV_EXPORTS_W double norm(InputArray src1, InputArray src2, + int normType = NORM_L2, InputArray mask = noArray()); +/** @overload +@param src first input array. +@param normType type of the norm (see #NormTypes). +*/ +CV_EXPORTS double norm( const SparseMat& src, int normType ); + +/** @brief Computes the Peak Signal-to-Noise Ratio (PSNR) image quality metric. + +This function calculates the Peak Signal-to-Noise Ratio (PSNR) image quality metric in decibels (dB), between two input arrays src1 and src2. Arrays must have depth CV_8U. + +The PSNR is calculated as follows: + +\f[ +\texttt{PSNR} = 10 \cdot \log_{10}{\left( \frac{R^2}{MSE} \right) } +\f] + +where R is the maximum integer value of depth CV_8U (255) and MSE is the mean squared error between the two arrays. + +@param src1 first input array. +@param src2 second input array of the same size as src1. + + */ +CV_EXPORTS_W double PSNR(InputArray src1, InputArray src2); + +/** @brief naive nearest neighbor finder + +see http://en.wikipedia.org/wiki/Nearest_neighbor_search +@todo document + */ +CV_EXPORTS_W void batchDistance(InputArray src1, InputArray src2, + OutputArray dist, int dtype, OutputArray nidx, + int normType = NORM_L2, int K = 0, + InputArray mask = noArray(), int update = 0, + bool crosscheck = false); + +/** @brief Normalizes the norm or value range of an array. + +The function cv::normalize normalizes scale and shift the input array elements so that +\f[\| \texttt{dst} \| _{L_p}= \texttt{alpha}\f] +(where p=Inf, 1 or 2) when normType=NORM_INF, NORM_L1, or NORM_L2, respectively; or so that +\f[\min _I \texttt{dst} (I)= \texttt{alpha} , \, \, \max _I \texttt{dst} (I)= \texttt{beta}\f] + +when normType=NORM_MINMAX (for dense arrays only). The optional mask specifies a sub-array to be +normalized. This means that the norm or min-n-max are calculated over the sub-array, and then this +sub-array is modified to be normalized. If you want to only use the mask to calculate the norm or +min-max but modify the whole array, you can use norm and Mat::convertTo. + +In case of sparse matrices, only the non-zero values are analyzed and transformed. Because of this, +the range transformation for sparse matrices is not allowed since it can shift the zero level. + +Possible usage with some positive example data: +@code{.cpp} + vector positiveData = { 2.0, 8.0, 10.0 }; + vector normalizedData_l1, normalizedData_l2, normalizedData_inf, normalizedData_minmax; + + // Norm to probability (total count) + // sum(numbers) = 20.0 + // 2.0 0.1 (2.0/20.0) + // 8.0 0.4 (8.0/20.0) + // 10.0 0.5 (10.0/20.0) + normalize(positiveData, normalizedData_l1, 1.0, 0.0, NORM_L1); + + // Norm to unit vector: ||positiveData|| = 1.0 + // 2.0 0.15 + // 8.0 0.62 + // 10.0 0.77 + normalize(positiveData, normalizedData_l2, 1.0, 0.0, NORM_L2); + + // Norm to max element + // 2.0 0.2 (2.0/10.0) + // 8.0 0.8 (8.0/10.0) + // 10.0 1.0 (10.0/10.0) + normalize(positiveData, normalizedData_inf, 1.0, 0.0, NORM_INF); + + // Norm to range [0.0;1.0] + // 2.0 0.0 (shift to left border) + // 8.0 0.75 (6.0/8.0) + // 10.0 1.0 (shift to right border) + normalize(positiveData, normalizedData_minmax, 1.0, 0.0, NORM_MINMAX); +@endcode + +@param src input array. +@param dst output array of the same size as src . +@param alpha norm value to normalize to or the lower range boundary in case of the range +normalization. +@param beta upper range boundary in case of the range normalization; it is not used for the norm +normalization. +@param norm_type normalization type (see cv::NormTypes). +@param dtype when negative, the output array has the same type as src; otherwise, it has the same +number of channels as src and the depth =CV_MAT_DEPTH(dtype). +@param mask optional operation mask. +@sa norm, Mat::convertTo, SparseMat::convertTo +*/ +CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, + int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray()); + +/** @overload +@param src input array. +@param dst output array of the same size as src . +@param alpha norm value to normalize to or the lower range boundary in case of the range +normalization. +@param normType normalization type (see cv::NormTypes). +*/ +CV_EXPORTS void normalize( const SparseMat& src, SparseMat& dst, double alpha, int normType ); + +/** @brief Finds the global minimum and maximum in an array. + +The function cv::minMaxLoc finds the minimum and maximum element values and their positions. The +extremums are searched across the whole array or, if mask is not an empty array, in the specified +array region. + +The function do not work with multi-channel arrays. If you need to find minimum or maximum +elements across all the channels, use Mat::reshape first to reinterpret the array as +single-channel. Or you may extract the particular channel using either extractImageCOI , or +mixChannels , or split . +@param src input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required. +@param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required. +@param mask optional mask used to select a sub-array. +@sa max, min, compare, inRange, extractImageCOI, mixChannels, split, Mat::reshape +*/ +CV_EXPORTS_W void minMaxLoc(InputArray src, CV_OUT double* minVal, + CV_OUT double* maxVal = 0, CV_OUT Point* minLoc = 0, + CV_OUT Point* maxLoc = 0, InputArray mask = noArray()); + + +/** @brief Finds the global minimum and maximum in an array + +The function cv::minMaxIdx finds the minimum and maximum element values and their positions. The +extremums are searched across the whole array or, if mask is not an empty array, in the specified +array region. The function does not work with multi-channel arrays. If you need to find minimum or +maximum elements across all the channels, use Mat::reshape first to reinterpret the array as +single-channel. Or you may extract the particular channel using either extractImageCOI , or +mixChannels , or split . In case of a sparse matrix, the minimum is found among non-zero elements +only. +@note When minIdx is not NULL, it must have at least 2 elements (as well as maxIdx), even if src is +a single-row or single-column matrix. In OpenCV (following MATLAB) each array has at least 2 +dimensions, i.e. single-column matrix is Mx1 matrix (and therefore minIdx/maxIdx will be +(i1,0)/(i2,0)) and single-row matrix is 1xN matrix (and therefore minIdx/maxIdx will be +(0,j1)/(0,j2)). +@param src input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minIdx pointer to the returned minimum location (in nD case); NULL is used if not required; +Otherwise, it must point to an array of src.dims elements, the coordinates of the minimum element +in each dimension are stored there sequentially. +@param maxIdx pointer to the returned maximum location (in nD case). NULL is used if not required. +@param mask specified array region +*/ +CV_EXPORTS void minMaxIdx(InputArray src, double* minVal, double* maxVal = 0, + int* minIdx = 0, int* maxIdx = 0, InputArray mask = noArray()); + +/** @overload +@param a input single-channel array. +@param minVal pointer to the returned minimum value; NULL is used if not required. +@param maxVal pointer to the returned maximum value; NULL is used if not required. +@param minIdx pointer to the returned minimum location (in nD case); NULL is used if not required; +Otherwise, it must point to an array of src.dims elements, the coordinates of the minimum element +in each dimension are stored there sequentially. +@param maxIdx pointer to the returned maximum location (in nD case). NULL is used if not required. +*/ +CV_EXPORTS void minMaxLoc(const SparseMat& a, double* minVal, + double* maxVal, int* minIdx = 0, int* maxIdx = 0); + +/** @brief Reduces a matrix to a vector. + +The function #reduce reduces the matrix to a vector by treating the matrix rows/columns as a set of +1D vectors and performing the specified operation on the vectors until a single row/column is +obtained. For example, the function can be used to compute horizontal and vertical projections of a +raster image. In case of #REDUCE_MAX and #REDUCE_MIN , the output image should have the same type as the source one. +In case of #REDUCE_SUM and #REDUCE_AVG , the output may have a larger element bit-depth to preserve accuracy. +And multi-channel arrays are also supported in these two reduction modes. + +The following code demonstrates its usage for a single channel matrix. +@snippet snippets/core_reduce.cpp example + +And the following code demonstrates its usage for a two-channel matrix. +@snippet snippets/core_reduce.cpp example2 + +@param src input 2D matrix. +@param dst output vector. Its size and type is defined by dim and dtype parameters. +@param dim dimension index along which the matrix is reduced. 0 means that the matrix is reduced to +a single row. 1 means that the matrix is reduced to a single column. +@param rtype reduction operation that could be one of #ReduceTypes +@param dtype when negative, the output vector will have the same type as the input matrix, +otherwise, its type will be CV_MAKE_TYPE(CV_MAT_DEPTH(dtype), src.channels()). +@sa repeat +*/ +CV_EXPORTS_W void reduce(InputArray src, OutputArray dst, int dim, int rtype, int dtype = -1); + +/** @brief Creates one multi-channel array out of several single-channel ones. + +The function cv::merge merges several arrays to make a single multi-channel array. That is, each +element of the output array will be a concatenation of the elements of the input arrays, where +elements of i-th input array are treated as mv[i].channels()-element vectors. + +The function cv::split does the reverse operation. If you need to shuffle channels in some other +advanced way, use cv::mixChannels. + +The following example shows how to merge 3 single channel matrices into a single 3-channel matrix. +@snippet snippets/core_merge.cpp example + +@param mv input array of matrices to be merged; all the matrices in mv must have the same +size and the same depth. +@param count number of input matrices when mv is a plain C array; it must be greater than zero. +@param dst output array of the same size and the same depth as mv[0]; The number of channels will +be equal to the parameter count. +@sa mixChannels, split, Mat::reshape +*/ +CV_EXPORTS void merge(const Mat* mv, size_t count, OutputArray dst); + +/** @overload +@param mv input vector of matrices to be merged; all the matrices in mv must have the same +size and the same depth. +@param dst output array of the same size and the same depth as mv[0]; The number of channels will +be the total number of channels in the matrix array. + */ +CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst); + +/** @brief Divides a multi-channel array into several single-channel arrays. + +The function cv::split splits a multi-channel array into separate single-channel arrays: +\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f] +If you need to extract a single channel or do some other sophisticated channel permutation, use +mixChannels . + +The following example demonstrates how to split a 3-channel matrix into 3 single channel matrices. +@snippet snippets/core_split.cpp example + +@param src input multi-channel array. +@param mvbegin output array; the number of arrays must match src.channels(); the arrays themselves are +reallocated, if needed. +@sa merge, mixChannels, cvtColor +*/ +CV_EXPORTS void split(const Mat& src, Mat* mvbegin); + +/** @overload +@param m input multi-channel array. +@param mv output vector of arrays; the arrays themselves are reallocated, if needed. +*/ +CV_EXPORTS_W void split(InputArray m, OutputArrayOfArrays mv); + +/** @brief Copies specified channels from input arrays to the specified channels of +output arrays. + +The function cv::mixChannels provides an advanced mechanism for shuffling image channels. + +cv::split,cv::merge,cv::extractChannel,cv::insertChannel and some forms of cv::cvtColor are partial cases of cv::mixChannels. + +In the example below, the code splits a 4-channel BGRA image into a 3-channel BGR (with B and R +channels swapped) and a separate alpha-channel image: +@code{.cpp} + Mat bgra( 100, 100, CV_8UC4, Scalar(255,0,0,255) ); + Mat bgr( bgra.rows, bgra.cols, CV_8UC3 ); + Mat alpha( bgra.rows, bgra.cols, CV_8UC1 ); + + // forming an array of matrices is a quite efficient operation, + // because the matrix data is not copied, only the headers + Mat out[] = { bgr, alpha }; + // bgra[0] -> bgr[2], bgra[1] -> bgr[1], + // bgra[2] -> bgr[0], bgra[3] -> alpha[0] + int from_to[] = { 0,2, 1,1, 2,0, 3,3 }; + mixChannels( &bgra, 1, out, 2, from_to, 4 ); +@endcode +@note Unlike many other new-style C++ functions in OpenCV (see the introduction section and +Mat::create ), cv::mixChannels requires the output arrays to be pre-allocated before calling the +function. +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param nsrcs number of matrices in `src`. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in `src[0]`. +@param ndsts number of matrices in `dst`. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +@param npairs number of index pairs in `fromTo`. +@sa split, merge, extractChannel, insertChannel, cvtColor +*/ +CV_EXPORTS void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, + const int* fromTo, size_t npairs); + +/** @overload +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in src[0]. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +@param npairs number of index pairs in fromTo. +*/ +CV_EXPORTS void mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst, + const int* fromTo, size_t npairs); + +/** @overload +@param src input array or vector of matrices; all of the matrices must have the same size and the +same depth. +@param dst output array or vector of matrices; all the matrices **must be allocated**; their size and +depth must be the same as in src[0]. +@param fromTo array of index pairs specifying which channels are copied and where; fromTo[k\*2] is +a 0-based index of the input channel in src, fromTo[k\*2+1] is an index of the output channel in +dst; the continuous channel numbering is used: the first input image channels are indexed from 0 to +src[0].channels()-1, the second input image channels are indexed from src[0].channels() to +src[0].channels() + src[1].channels()-1, and so on, the same scheme is used for the output image +channels; as a special case, when fromTo[k\*2] is negative, the corresponding output channel is +filled with zero . +*/ +CV_EXPORTS_W void mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst, + const std::vector& fromTo); + +/** @brief Extracts a single channel from src (coi is 0-based index) +@param src input array +@param dst output array +@param coi index of channel to extract +@sa mixChannels, split +*/ +CV_EXPORTS_W void extractChannel(InputArray src, OutputArray dst, int coi); + +/** @brief Inserts a single channel to dst (coi is 0-based index) +@param src input array +@param dst output array +@param coi index of channel for insertion +@sa mixChannels, merge +*/ +CV_EXPORTS_W void insertChannel(InputArray src, InputOutputArray dst, int coi); + +/** @brief Flips a 2D array around vertical, horizontal, or both axes. + +The function cv::flip flips the array in one of three different ways (row +and column indices are 0-based): +\f[\texttt{dst} _{ij} = +\left\{ +\begin{array}{l l} +\texttt{src} _{\texttt{src.rows}-i-1,j} & if\; \texttt{flipCode} = 0 \\ +\texttt{src} _{i, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} > 0 \\ +\texttt{src} _{ \texttt{src.rows} -i-1, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} < 0 \\ +\end{array} +\right.\f] +The example scenarios of using the function are the following: +* Vertical flipping of the image (flipCode == 0) to switch between + top-left and bottom-left image origin. This is a typical operation + in video processing on Microsoft Windows\* OS. +* Horizontal flipping of the image with the subsequent horizontal + shift and absolute difference calculation to check for a + vertical-axis symmetry (flipCode \> 0). +* Simultaneous horizontal and vertical flipping of the image with + the subsequent shift and absolute difference calculation to check + for a central symmetry (flipCode \< 0). +* Reversing the order of point arrays (flipCode \> 0 or + flipCode == 0). +@param src input array. +@param dst output array of the same size and type as src. +@param flipCode a flag to specify how to flip the array; 0 means +flipping around the x-axis and positive value (for example, 1) means +flipping around y-axis. Negative value (for example, -1) means flipping +around both axes. +@sa transpose , repeat , completeSymm +*/ +CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode); + +enum RotateFlags { + ROTATE_90_CLOCKWISE = 0, //! A = (cv::Mat_(3, 2) << 1, 4, + 2, 5, + 3, 6); + cv::Mat_ B = (cv::Mat_(3, 2) << 7, 10, + 8, 11, + 9, 12); + + cv::Mat C; + cv::hconcat(A, B, C); + //C: + //[1, 4, 7, 10; + // 2, 5, 8, 11; + // 3, 6, 9, 12] + @endcode + @param src1 first input array to be considered for horizontal concatenation. + @param src2 second input array to be considered for horizontal concatenation. + @param dst output array. It has the same number of rows and depth as the src1 and src2, and the sum of cols of the src1 and src2. + */ +CV_EXPORTS void hconcat(InputArray src1, InputArray src2, OutputArray dst); +/** @overload + @code{.cpp} + std::vector matrices = { cv::Mat(4, 1, CV_8UC1, cv::Scalar(1)), + cv::Mat(4, 1, CV_8UC1, cv::Scalar(2)), + cv::Mat(4, 1, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::hconcat( matrices, out ); + //out: + //[1, 2, 3; + // 1, 2, 3; + // 1, 2, 3; + // 1, 2, 3] + @endcode + @param src input array or vector of matrices. all of the matrices must have the same number of rows and the same depth. + @param dst output array. It has the same number of rows and depth as the src, and the sum of cols of the src. +same depth. + */ +CV_EXPORTS_W void hconcat(InputArrayOfArrays src, OutputArray dst); + +/** @brief Applies vertical concatenation to given matrices. + +The function vertically concatenates two or more cv::Mat matrices (with the same number of cols). +@code{.cpp} + cv::Mat matArray[] = { cv::Mat(1, 4, CV_8UC1, cv::Scalar(1)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(2)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::vconcat( matArray, 3, out ); + //out: + //[1, 1, 1, 1; + // 2, 2, 2, 2; + // 3, 3, 3, 3] +@endcode +@param src input array or vector of matrices. all of the matrices must have the same number of cols and the same depth. +@param nsrc number of matrices in src. +@param dst output array. It has the same number of cols and depth as the src, and the sum of rows of the src. +@sa cv::hconcat(const Mat*, size_t, OutputArray), @sa cv::hconcat(InputArrayOfArrays, OutputArray) and @sa cv::hconcat(InputArray, InputArray, OutputArray) +*/ +CV_EXPORTS void vconcat(const Mat* src, size_t nsrc, OutputArray dst); +/** @overload + @code{.cpp} + cv::Mat_ A = (cv::Mat_(3, 2) << 1, 7, + 2, 8, + 3, 9); + cv::Mat_ B = (cv::Mat_(3, 2) << 4, 10, + 5, 11, + 6, 12); + + cv::Mat C; + cv::vconcat(A, B, C); + //C: + //[1, 7; + // 2, 8; + // 3, 9; + // 4, 10; + // 5, 11; + // 6, 12] + @endcode + @param src1 first input array to be considered for vertical concatenation. + @param src2 second input array to be considered for vertical concatenation. + @param dst output array. It has the same number of cols and depth as the src1 and src2, and the sum of rows of the src1 and src2. + */ +CV_EXPORTS void vconcat(InputArray src1, InputArray src2, OutputArray dst); +/** @overload + @code{.cpp} + std::vector matrices = { cv::Mat(1, 4, CV_8UC1, cv::Scalar(1)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(2)), + cv::Mat(1, 4, CV_8UC1, cv::Scalar(3)),}; + + cv::Mat out; + cv::vconcat( matrices, out ); + //out: + //[1, 1, 1, 1; + // 2, 2, 2, 2; + // 3, 3, 3, 3] + @endcode + @param src input array or vector of matrices. all of the matrices must have the same number of cols and the same depth + @param dst output array. It has the same number of cols and depth as the src, and the sum of rows of the src. +same depth. + */ +CV_EXPORTS_W void vconcat(InputArrayOfArrays src, OutputArray dst); + +/** @brief computes bitwise conjunction of the two arrays (dst = src1 & src2) +Calculates the per-element bit-wise conjunction of two arrays or an +array and a scalar. + +The function cv::bitwise_and calculates the per-element bit-wise logical conjunction for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the second and third cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_and(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Calculates the per-element bit-wise disjunction of two arrays or an +array and a scalar. + +The function cv::bitwise_or calculates the per-element bit-wise logical disjunction for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \vee \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \vee \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \vee \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the second and third cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_or(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Calculates the per-element bit-wise "exclusive or" operation on two +arrays or an array and a scalar. + +The function cv::bitwise_xor calculates the per-element bit-wise logical "exclusive-or" +operation for: +* Two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \oplus \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +* An array and a scalar when src2 is constructed from Scalar or has + the same number of elements as `src1.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} (I) \oplus \texttt{src2} \quad \texttt{if mask} (I) \ne0\f] +* A scalar and an array when src1 is constructed from Scalar or has + the same number of elements as `src2.channels()`: + \f[\texttt{dst} (I) = \texttt{src1} \oplus \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0\f] +In case of floating-point arrays, their machine-specific bit +representations (usually IEEE754-compliant) are used for the operation. +In case of multi-channel arrays, each channel is processed +independently. In the 2nd and 3rd cases above, the scalar is first +converted to the array type. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as the input +arrays. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_xor(InputArray src1, InputArray src2, + OutputArray dst, InputArray mask = noArray()); + +/** @brief Inverts every bit of an array. + +The function cv::bitwise_not calculates per-element bit-wise inversion of the input +array: +\f[\texttt{dst} (I) = \neg \texttt{src} (I)\f] +In case of a floating-point input array, its machine-specific bit +representation (usually IEEE754-compliant) is used for the operation. In +case of multi-channel arrays, each channel is processed independently. +@param src input array. +@param dst output array that has the same size and type as the input +array. +@param mask optional operation mask, 8-bit single channel array, that +specifies elements of the output array to be changed. +*/ +CV_EXPORTS_W void bitwise_not(InputArray src, OutputArray dst, + InputArray mask = noArray()); + +/** @brief Calculates the per-element absolute difference between two arrays or between an array and a scalar. + +The function cv::absdiff calculates: +* Absolute difference between two arrays when they have the same + size and type: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{src2}(I)|)\f] +* Absolute difference between an array and a scalar when the second + array is constructed from Scalar or has as many elements as the + number of channels in `src1`: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1}(I) - \texttt{src2} |)\f] +* Absolute difference between a scalar and an array when the first + array is constructed from Scalar or has as many elements as the + number of channels in `src2`: + \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1} - \texttt{src2}(I) |)\f] + where I is a multi-dimensional index of array elements. In case of + multi-channel arrays, each channel is processed independently. +@note Saturation is not applied when the arrays have the depth CV_32S. +You may even get a negative value in the case of overflow. +@param src1 first input array or a scalar. +@param src2 second input array or a scalar. +@param dst output array that has the same size and type as input arrays. +@sa cv::abs(const Mat&) +*/ +CV_EXPORTS_W void absdiff(InputArray src1, InputArray src2, OutputArray dst); + +/** @brief Checks if array elements lie between the elements of two other arrays. + +The function checks the range as follows: +- For every element of a single-channel input array: + \f[\texttt{dst} (I)= \texttt{lowerb} (I)_0 \leq \texttt{src} (I)_0 \leq \texttt{upperb} (I)_0\f] +- For two-channel arrays: + \f[\texttt{dst} (I)= \texttt{lowerb} (I)_0 \leq \texttt{src} (I)_0 \leq \texttt{upperb} (I)_0 \land \texttt{lowerb} (I)_1 \leq \texttt{src} (I)_1 \leq \texttt{upperb} (I)_1\f] +- and so forth. + +That is, dst (I) is set to 255 (all 1 -bits) if src (I) is within the +specified 1D, 2D, 3D, ... box and 0 otherwise. + +When the lower and/or upper boundary parameters are scalars, the indexes +(I) at lowerb and upperb in the above formulas should be omitted. +@param src first input array. +@param lowerb inclusive lower boundary array or a scalar. +@param upperb inclusive upper boundary array or a scalar. +@param dst output array of the same size as src and CV_8U type. +*/ +CV_EXPORTS_W void inRange(InputArray src, InputArray lowerb, + InputArray upperb, OutputArray dst); + +/** @brief Performs the per-element comparison of two arrays or an array and scalar value. + +The function compares: +* Elements of two arrays when src1 and src2 have the same size: + \f[\texttt{dst} (I) = \texttt{src1} (I) \,\texttt{cmpop}\, \texttt{src2} (I)\f] +* Elements of src1 with a scalar src2 when src2 is constructed from + Scalar or has a single element: + \f[\texttt{dst} (I) = \texttt{src1}(I) \,\texttt{cmpop}\, \texttt{src2}\f] +* src1 with elements of src2 when src1 is constructed from Scalar or + has a single element: + \f[\texttt{dst} (I) = \texttt{src1} \,\texttt{cmpop}\, \texttt{src2} (I)\f] +When the comparison result is true, the corresponding element of output +array is set to 255. The comparison operations can be replaced with the +equivalent matrix expressions: +@code{.cpp} + Mat dst1 = src1 >= src2; + Mat dst2 = src1 < 8; + ... +@endcode +@param src1 first input array or a scalar; when it is an array, it must have a single channel. +@param src2 second input array or a scalar; when it is an array, it must have a single channel. +@param dst output array of type ref CV_8U that has the same size and the same number of channels as + the input arrays. +@param cmpop a flag, that specifies correspondence between the arrays (cv::CmpTypes) +@sa checkRange, min, max, threshold +*/ +CV_EXPORTS_W void compare(InputArray src1, InputArray src2, OutputArray dst, int cmpop); + +/** @brief Calculates per-element minimum of two arrays or an array and a scalar. + +The function cv::min calculates the per-element minimum of two arrays: +\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{src2} (I))\f] +or array and a scalar: +\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{value} )\f] +@param src1 first input array. +@param src2 second input array of the same size and type as src1. +@param dst output array of the same size and type as src1. +@sa max, compare, inRange, minMaxLoc +*/ +CV_EXPORTS_W void min(InputArray src1, InputArray src2, OutputArray dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void min(const Mat& src1, const Mat& src2, Mat& dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void min(const UMat& src1, const UMat& src2, UMat& dst); + +/** @brief Calculates per-element maximum of two arrays or an array and a scalar. + +The function cv::max calculates the per-element maximum of two arrays: +\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{src2} (I))\f] +or array and a scalar: +\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{value} )\f] +@param src1 first input array. +@param src2 second input array of the same size and type as src1 . +@param dst output array of the same size and type as src1. +@sa min, compare, inRange, minMaxLoc, @ref MatrixExpressions +*/ +CV_EXPORTS_W void max(InputArray src1, InputArray src2, OutputArray dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void max(const Mat& src1, const Mat& src2, Mat& dst); +/** @overload +needed to avoid conflicts with const _Tp& std::min(const _Tp&, const _Tp&, _Compare) +*/ +CV_EXPORTS void max(const UMat& src1, const UMat& src2, UMat& dst); + +/** @brief Calculates a square root of array elements. + +The function cv::sqrt calculates a square root of each input array element. +In case of multi-channel arrays, each channel is processed +independently. The accuracy is approximately the same as of the built-in +std::sqrt . +@param src input floating-point array. +@param dst output array of the same size and type as src. +*/ +CV_EXPORTS_W void sqrt(InputArray src, OutputArray dst); + +/** @brief Raises every array element to a power. + +The function cv::pow raises every element of the input array to power : +\f[\texttt{dst} (I) = \fork{\texttt{src}(I)^{power}}{if \(\texttt{power}\) is integer}{|\texttt{src}(I)|^{power}}{otherwise}\f] + +So, for a non-integer power exponent, the absolute values of input array +elements are used. However, it is possible to get true values for +negative values using some extra operations. In the example below, +computing the 5th root of array src shows: +@code{.cpp} + Mat mask = src < 0; + pow(src, 1./5, dst); + subtract(Scalar::all(0), dst, dst, mask); +@endcode +For some values of power, such as integer values, 0.5 and -0.5, +specialized faster algorithms are used. + +Special values (NaN, Inf) are not handled. +@param src input array. +@param power exponent of power. +@param dst output array of the same size and type as src. +@sa sqrt, exp, log, cartToPolar, polarToCart +*/ +CV_EXPORTS_W void pow(InputArray src, double power, OutputArray dst); + +/** @brief Calculates the exponent of every array element. + +The function cv::exp calculates the exponent of every element of the input +array: +\f[\texttt{dst} [I] = e^{ src(I) }\f] + +The maximum relative error is about 7e-6 for single-precision input and +less than 1e-10 for double-precision input. Currently, the function +converts denormalized values to zeros on output. Special values (NaN, +Inf) are not handled. +@param src input array. +@param dst output array of the same size and type as src. +@sa log , cartToPolar , polarToCart , phase , pow , sqrt , magnitude +*/ +CV_EXPORTS_W void exp(InputArray src, OutputArray dst); + +/** @brief Calculates the natural logarithm of every array element. + +The function cv::log calculates the natural logarithm of every element of the input array: +\f[\texttt{dst} (I) = \log (\texttt{src}(I)) \f] + +Output on zero, negative and special (NaN, Inf) values is undefined. + +@param src input array. +@param dst output array of the same size and type as src . +@sa exp, cartToPolar, polarToCart, phase, pow, sqrt, magnitude +*/ +CV_EXPORTS_W void log(InputArray src, OutputArray dst); + +/** @brief Calculates x and y coordinates of 2D vectors from their magnitude and angle. + +The function cv::polarToCart calculates the Cartesian coordinates of each 2D +vector represented by the corresponding elements of magnitude and angle: +\f[\begin{array}{l} \texttt{x} (I) = \texttt{magnitude} (I) \cos ( \texttt{angle} (I)) \\ \texttt{y} (I) = \texttt{magnitude} (I) \sin ( \texttt{angle} (I)) \\ \end{array}\f] + +The relative accuracy of the estimated coordinates is about 1e-6. +@param magnitude input floating-point array of magnitudes of 2D vectors; +it can be an empty matrix (=Mat()), in this case, the function assumes +that all the magnitudes are =1; if it is not empty, it must have the +same size and type as angle. +@param angle input floating-point array of angles of 2D vectors. +@param x output array of x-coordinates of 2D vectors; it has the same +size and type as angle. +@param y output array of y-coordinates of 2D vectors; it has the same +size and type as angle. +@param angleInDegrees when true, the input angles are measured in +degrees, otherwise, they are measured in radians. +@sa cartToPolar, magnitude, phase, exp, log, pow, sqrt +*/ +CV_EXPORTS_W void polarToCart(InputArray magnitude, InputArray angle, + OutputArray x, OutputArray y, bool angleInDegrees = false); + +/** @brief Calculates the magnitude and angle of 2D vectors. + +The function cv::cartToPolar calculates either the magnitude, angle, or both +for every 2D vector (x(I),y(I)): +\f[\begin{array}{l} \texttt{magnitude} (I)= \sqrt{\texttt{x}(I)^2+\texttt{y}(I)^2} , \\ \texttt{angle} (I)= \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))[ \cdot180 / \pi ] \end{array}\f] + +The angles are calculated with accuracy about 0.3 degrees. For the point +(0,0), the angle is set to 0. +@param x array of x-coordinates; this must be a single-precision or +double-precision floating-point array. +@param y array of y-coordinates, that must have the same size and same type as x. +@param magnitude output array of magnitudes of the same size and type as x. +@param angle output array of angles that has the same size and type as +x; the angles are measured in radians (from 0 to 2\*Pi) or in degrees (0 to 360 degrees). +@param angleInDegrees a flag, indicating whether the angles are measured +in radians (which is by default), or in degrees. +@sa Sobel, Scharr +*/ +CV_EXPORTS_W void cartToPolar(InputArray x, InputArray y, + OutputArray magnitude, OutputArray angle, + bool angleInDegrees = false); + +/** @brief Calculates the rotation angle of 2D vectors. + +The function cv::phase calculates the rotation angle of each 2D vector that +is formed from the corresponding elements of x and y : +\f[\texttt{angle} (I) = \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))\f] + +The angle estimation accuracy is about 0.3 degrees. When x(I)=y(I)=0 , +the corresponding angle(I) is set to 0. +@param x input floating-point array of x-coordinates of 2D vectors. +@param y input array of y-coordinates of 2D vectors; it must have the +same size and the same type as x. +@param angle output array of vector angles; it has the same size and +same type as x . +@param angleInDegrees when true, the function calculates the angle in +degrees, otherwise, they are measured in radians. +*/ +CV_EXPORTS_W void phase(InputArray x, InputArray y, OutputArray angle, + bool angleInDegrees = false); + +/** @brief Calculates the magnitude of 2D vectors. + +The function cv::magnitude calculates the magnitude of 2D vectors formed +from the corresponding elements of x and y arrays: +\f[\texttt{dst} (I) = \sqrt{\texttt{x}(I)^2 + \texttt{y}(I)^2}\f] +@param x floating-point array of x-coordinates of the vectors. +@param y floating-point array of y-coordinates of the vectors; it must +have the same size as x. +@param magnitude output array of the same size and type as x. +@sa cartToPolar, polarToCart, phase, sqrt +*/ +CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude); + +/** @brief Checks every element of an input array for invalid values. + +The function cv::checkRange checks that every array element is neither NaN nor infinite. When minVal \> +-DBL_MAX and maxVal \< DBL_MAX, the function also checks that each value is between minVal and +maxVal. In case of multi-channel arrays, each channel is processed independently. If some values +are out of range, position of the first outlier is stored in pos (when pos != NULL). Then, the +function either returns false (when quiet=true) or throws an exception. +@param a input array. +@param quiet a flag, indicating whether the functions quietly return false when the array elements +are out of range or they throw an exception. +@param pos optional output parameter, when not NULL, must be a pointer to array of src.dims +elements. +@param minVal inclusive lower boundary of valid values range. +@param maxVal exclusive upper boundary of valid values range. +*/ +CV_EXPORTS_W bool checkRange(InputArray a, bool quiet = true, CV_OUT Point* pos = 0, + double minVal = -DBL_MAX, double maxVal = DBL_MAX); + +/** @brief converts NaNs to the given number +@param a input/output matrix (CV_32F type). +@param val value to convert the NaNs +*/ +CV_EXPORTS_W void patchNaNs(InputOutputArray a, double val = 0); + +/** @brief Performs generalized matrix multiplication. + +The function cv::gemm performs generalized matrix multiplication similar to the +gemm functions in BLAS level 3. For example, +`gemm(src1, src2, alpha, src3, beta, dst, GEMM_1_T + GEMM_3_T)` +corresponds to +\f[\texttt{dst} = \texttt{alpha} \cdot \texttt{src1} ^T \cdot \texttt{src2} + \texttt{beta} \cdot \texttt{src3} ^T\f] + +In case of complex (two-channel) data, performed a complex matrix +multiplication. + +The function can be replaced with a matrix expression. For example, the +above call can be replaced with: +@code{.cpp} + dst = alpha*src1.t()*src2 + beta*src3.t(); +@endcode +@param src1 first multiplied input matrix that could be real(CV_32FC1, +CV_64FC1) or complex(CV_32FC2, CV_64FC2). +@param src2 second multiplied input matrix of the same type as src1. +@param alpha weight of the matrix product. +@param src3 third optional delta matrix added to the matrix product; it +should have the same type as src1 and src2. +@param beta weight of src3. +@param dst output matrix; it has the proper size and the same type as +input matrices. +@param flags operation flags (cv::GemmFlags) +@sa mulTransposed , transform +*/ +CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha, + InputArray src3, double beta, OutputArray dst, int flags = 0); + +/** @brief Calculates the product of a matrix and its transposition. + +The function cv::mulTransposed calculates the product of src and its +transposition: +\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} )^T ( \texttt{src} - \texttt{delta} )\f] +if aTa=true , and +\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} ) ( \texttt{src} - \texttt{delta} )^T\f] +otherwise. The function is used to calculate the covariance matrix. With +zero delta, it can be used as a faster substitute for general matrix +product A\*B when B=A' +@param src input single-channel matrix. Note that unlike gemm, the +function can multiply not only floating-point matrices. +@param dst output square matrix. +@param aTa Flag specifying the multiplication ordering. See the +description below. +@param delta Optional delta matrix subtracted from src before the +multiplication. When the matrix is empty ( delta=noArray() ), it is +assumed to be zero, that is, nothing is subtracted. If it has the same +size as src , it is simply subtracted. Otherwise, it is "repeated" (see +repeat ) to cover the full src and then subtracted. Type of the delta +matrix, when it is not empty, must be the same as the type of created +output matrix. See the dtype parameter description below. +@param scale Optional scale factor for the matrix product. +@param dtype Optional type of the output matrix. When it is negative, +the output matrix will have the same type as src . Otherwise, it will be +type=CV_MAT_DEPTH(dtype) that should be either CV_32F or CV_64F . +@sa calcCovarMatrix, gemm, repeat, reduce +*/ +CV_EXPORTS_W void mulTransposed( InputArray src, OutputArray dst, bool aTa, + InputArray delta = noArray(), + double scale = 1, int dtype = -1 ); + +/** @brief Transposes a matrix. + +The function cv::transpose transposes the matrix src : +\f[\texttt{dst} (i,j) = \texttt{src} (j,i)\f] +@note No complex conjugation is done in case of a complex matrix. It +should be done separately if needed. +@param src input array. +@param dst output array of the same type as src. +*/ +CV_EXPORTS_W void transpose(InputArray src, OutputArray dst); + +/** @brief Performs the matrix transformation of every array element. + +The function cv::transform performs the matrix transformation of every +element of the array src and stores the results in dst : +\f[\texttt{dst} (I) = \texttt{m} \cdot \texttt{src} (I)\f] +(when m.cols=src.channels() ), or +\f[\texttt{dst} (I) = \texttt{m} \cdot [ \texttt{src} (I); 1]\f] +(when m.cols=src.channels()+1 ) + +Every element of the N -channel array src is interpreted as N -element +vector that is transformed using the M x N or M x (N+1) matrix m to +M-element vector - the corresponding element of the output array dst . + +The function may be used for geometrical transformation of +N -dimensional points, arbitrary linear color space transformation (such +as various kinds of RGB to YUV transforms), shuffling the image +channels, and so forth. +@param src input array that must have as many channels (1 to 4) as +m.cols or m.cols-1. +@param dst output array of the same size and depth as src; it has as +many channels as m.rows. +@param m transformation 2x2 or 2x3 floating-point matrix. +@sa perspectiveTransform, getAffineTransform, estimateAffine2D, warpAffine, warpPerspective +*/ +CV_EXPORTS_W void transform(InputArray src, OutputArray dst, InputArray m ); + +/** @brief Performs the perspective matrix transformation of vectors. + +The function cv::perspectiveTransform transforms every element of src by +treating it as a 2D or 3D vector, in the following way: +\f[(x, y, z) \rightarrow (x'/w, y'/w, z'/w)\f] +where +\f[(x', y', z', w') = \texttt{mat} \cdot \begin{bmatrix} x & y & z & 1 \end{bmatrix}\f] +and +\f[w = \fork{w'}{if \(w' \ne 0\)}{\infty}{otherwise}\f] + +Here a 3D vector transformation is shown. In case of a 2D vector +transformation, the z component is omitted. + +@note The function transforms a sparse set of 2D or 3D vectors. If you +want to transform an image using perspective transformation, use +warpPerspective . If you have an inverse problem, that is, you want to +compute the most probable perspective transformation out of several +pairs of corresponding points, you can use getPerspectiveTransform or +findHomography . +@param src input two-channel or three-channel floating-point array; each +element is a 2D/3D vector to be transformed. +@param dst output array of the same size and type as src. +@param m 3x3 or 4x4 floating-point transformation matrix. +@sa transform, warpPerspective, getPerspectiveTransform, findHomography +*/ +CV_EXPORTS_W void perspectiveTransform(InputArray src, OutputArray dst, InputArray m ); + +/** @brief Copies the lower or the upper half of a square matrix to its another half. + +The function cv::completeSymm copies the lower or the upper half of a square matrix to +its another half. The matrix diagonal remains unchanged: + - \f$\texttt{m}_{ij}=\texttt{m}_{ji}\f$ for \f$i > j\f$ if + lowerToUpper=false + - \f$\texttt{m}_{ij}=\texttt{m}_{ji}\f$ for \f$i < j\f$ if + lowerToUpper=true + +@param m input-output floating-point square matrix. +@param lowerToUpper operation flag; if true, the lower half is copied to +the upper half. Otherwise, the upper half is copied to the lower half. +@sa flip, transpose +*/ +CV_EXPORTS_W void completeSymm(InputOutputArray m, bool lowerToUpper = false); + +/** @brief Initializes a scaled identity matrix. + +The function cv::setIdentity initializes a scaled identity matrix: +\f[\texttt{mtx} (i,j)= \fork{\texttt{value}}{ if \(i=j\)}{0}{otherwise}\f] + +The function can also be emulated using the matrix initializers and the +matrix expressions: +@code + Mat A = Mat::eye(4, 3, CV_32F)*5; + // A will be set to [[5, 0, 0], [0, 5, 0], [0, 0, 5], [0, 0, 0]] +@endcode +@param mtx matrix to initialize (not necessarily square). +@param s value to assign to diagonal elements. +@sa Mat::zeros, Mat::ones, Mat::setTo, Mat::operator= +*/ +CV_EXPORTS_W void setIdentity(InputOutputArray mtx, const Scalar& s = Scalar(1)); + +/** @brief Returns the determinant of a square floating-point matrix. + +The function cv::determinant calculates and returns the determinant of the +specified matrix. For small matrices ( mtx.cols=mtx.rows\<=3 ), the +direct method is used. For larger matrices, the function uses LU +factorization with partial pivoting. + +For symmetric positively-determined matrices, it is also possible to use +eigen decomposition to calculate the determinant. +@param mtx input matrix that must have CV_32FC1 or CV_64FC1 type and +square size. +@sa trace, invert, solve, eigen, @ref MatrixExpressions +*/ +CV_EXPORTS_W double determinant(InputArray mtx); + +/** @brief Returns the trace of a matrix. + +The function cv::trace returns the sum of the diagonal elements of the +matrix mtx . +\f[\mathrm{tr} ( \texttt{mtx} ) = \sum _i \texttt{mtx} (i,i)\f] +@param mtx input matrix. +*/ +CV_EXPORTS_W Scalar trace(InputArray mtx); + +/** @brief Finds the inverse or pseudo-inverse of a matrix. + +The function cv::invert inverts the matrix src and stores the result in dst +. When the matrix src is singular or non-square, the function calculates +the pseudo-inverse matrix (the dst matrix) so that norm(src\*dst - I) is +minimal, where I is an identity matrix. + +In case of the #DECOMP_LU method, the function returns non-zero value if +the inverse has been successfully calculated and 0 if src is singular. + +In case of the #DECOMP_SVD method, the function returns the inverse +condition number of src (the ratio of the smallest singular value to the +largest singular value) and 0 if src is singular. The SVD method +calculates a pseudo-inverse matrix if src is singular. + +Similarly to #DECOMP_LU, the method #DECOMP_CHOLESKY works only with +non-singular square matrices that should also be symmetrical and +positively defined. In this case, the function stores the inverted +matrix in dst and returns non-zero. Otherwise, it returns 0. + +@param src input floating-point M x N matrix. +@param dst output matrix of N x M size and the same type as src. +@param flags inversion method (cv::DecompTypes) +@sa solve, SVD +*/ +CV_EXPORTS_W double invert(InputArray src, OutputArray dst, int flags = DECOMP_LU); + +/** @brief Solves one or more linear systems or least-squares problems. + +The function cv::solve solves a linear system or least-squares problem (the +latter is possible with SVD or QR methods, or by specifying the flag +#DECOMP_NORMAL ): +\f[\texttt{dst} = \arg \min _X \| \texttt{src1} \cdot \texttt{X} - \texttt{src2} \|\f] + +If #DECOMP_LU or #DECOMP_CHOLESKY method is used, the function returns 1 +if src1 (or \f$\texttt{src1}^T\texttt{src1}\f$ ) is non-singular. Otherwise, +it returns 0. In the latter case, dst is not valid. Other methods find a +pseudo-solution in case of a singular left-hand side part. + +@note If you want to find a unity-norm solution of an under-defined +singular system \f$\texttt{src1}\cdot\texttt{dst}=0\f$ , the function solve +will not do the work. Use SVD::solveZ instead. + +@param src1 input matrix on the left-hand side of the system. +@param src2 input matrix on the right-hand side of the system. +@param dst output solution. +@param flags solution (matrix inversion) method (#DecompTypes) +@sa invert, SVD, eigen +*/ +CV_EXPORTS_W bool solve(InputArray src1, InputArray src2, + OutputArray dst, int flags = DECOMP_LU); + +/** @brief Sorts each row or each column of a matrix. + +The function cv::sort sorts each matrix row or each matrix column in +ascending or descending order. So you should pass two operation flags to +get desired behaviour. If you want to sort matrix rows or columns +lexicographically, you can use STL std::sort generic function with the +proper comparison predicate. + +@param src input single-channel array. +@param dst output array of the same size and type as src. +@param flags operation flags, a combination of #SortFlags +@sa sortIdx, randShuffle +*/ +CV_EXPORTS_W void sort(InputArray src, OutputArray dst, int flags); + +/** @brief Sorts each row or each column of a matrix. + +The function cv::sortIdx sorts each matrix row or each matrix column in the +ascending or descending order. So you should pass two operation flags to +get desired behaviour. Instead of reordering the elements themselves, it +stores the indices of sorted elements in the output array. For example: +@code + Mat A = Mat::eye(3,3,CV_32F), B; + sortIdx(A, B, SORT_EVERY_ROW + SORT_ASCENDING); + // B will probably contain + // (because of equal elements in A some permutations are possible): + // [[1, 2, 0], [0, 2, 1], [0, 1, 2]] +@endcode +@param src input single-channel array. +@param dst output integer array of the same size as src. +@param flags operation flags that could be a combination of cv::SortFlags +@sa sort, randShuffle +*/ +CV_EXPORTS_W void sortIdx(InputArray src, OutputArray dst, int flags); + +/** @brief Finds the real roots of a cubic equation. + +The function solveCubic finds the real roots of a cubic equation: +- if coeffs is a 4-element vector: +\f[\texttt{coeffs} [0] x^3 + \texttt{coeffs} [1] x^2 + \texttt{coeffs} [2] x + \texttt{coeffs} [3] = 0\f] +- if coeffs is a 3-element vector: +\f[x^3 + \texttt{coeffs} [0] x^2 + \texttt{coeffs} [1] x + \texttt{coeffs} [2] = 0\f] + +The roots are stored in the roots array. +@param coeffs equation coefficients, an array of 3 or 4 elements. +@param roots output array of real roots that has 1 or 3 elements. +@return number of real roots. It can be 0, 1 or 2. +*/ +CV_EXPORTS_W int solveCubic(InputArray coeffs, OutputArray roots); + +/** @brief Finds the real or complex roots of a polynomial equation. + +The function cv::solvePoly finds real and complex roots of a polynomial equation: +\f[\texttt{coeffs} [n] x^{n} + \texttt{coeffs} [n-1] x^{n-1} + ... + \texttt{coeffs} [1] x + \texttt{coeffs} [0] = 0\f] +@param coeffs array of polynomial coefficients. +@param roots output (complex) array of roots. +@param maxIters maximum number of iterations the algorithm does. +*/ +CV_EXPORTS_W double solvePoly(InputArray coeffs, OutputArray roots, int maxIters = 300); + +/** @brief Calculates eigenvalues and eigenvectors of a symmetric matrix. + +The function cv::eigen calculates just eigenvalues, or eigenvalues and eigenvectors of the symmetric +matrix src: +@code + src*eigenvectors.row(i).t() = eigenvalues.at(i)*eigenvectors.row(i).t() +@endcode + +@note Use cv::eigenNonSymmetric for calculation of real eigenvalues and eigenvectors of non-symmetric matrix. + +@param src input matrix that must have CV_32FC1 or CV_64FC1 type, square size and be symmetrical +(src ^T^ == src). +@param eigenvalues output vector of eigenvalues of the same type as src; the eigenvalues are stored +in the descending order. +@param eigenvectors output matrix of eigenvectors; it has the same size and type as src; the +eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding +eigenvalues. +@sa eigenNonSymmetric, completeSymm , PCA +*/ +CV_EXPORTS_W bool eigen(InputArray src, OutputArray eigenvalues, + OutputArray eigenvectors = noArray()); + +/** @brief Calculates eigenvalues and eigenvectors of a non-symmetric matrix (real eigenvalues only). + +@note Assumes real eigenvalues. + +The function calculates eigenvalues and eigenvectors (optional) of the square matrix src: +@code + src*eigenvectors.row(i).t() = eigenvalues.at(i)*eigenvectors.row(i).t() +@endcode + +@param src input matrix (CV_32FC1 or CV_64FC1 type). +@param eigenvalues output vector of eigenvalues (type is the same type as src). +@param eigenvectors output matrix of eigenvectors (type is the same type as src). The eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding eigenvalues. +@sa eigen +*/ +CV_EXPORTS_W void eigenNonSymmetric(InputArray src, OutputArray eigenvalues, + OutputArray eigenvectors); + +/** @brief Calculates the covariance matrix of a set of vectors. + +The function cv::calcCovarMatrix calculates the covariance matrix and, optionally, the mean vector of +the set of input vectors. +@param samples samples stored as separate matrices +@param nsamples number of samples +@param covar output covariance matrix of the type ctype and square size. +@param mean input or output (depending on the flags) array as the average value of the input vectors. +@param flags operation flags as a combination of #CovarFlags +@param ctype type of the matrixl; it equals 'CV_64F' by default. +@sa PCA, mulTransposed, Mahalanobis +@todo InputArrayOfArrays +*/ +CV_EXPORTS void calcCovarMatrix( const Mat* samples, int nsamples, Mat& covar, Mat& mean, + int flags, int ctype = CV_64F); + +/** @overload +@note use #COVAR_ROWS or #COVAR_COLS flag +@param samples samples stored as rows/columns of a single matrix. +@param covar output covariance matrix of the type ctype and square size. +@param mean input or output (depending on the flags) array as the average value of the input vectors. +@param flags operation flags as a combination of #CovarFlags +@param ctype type of the matrixl; it equals 'CV_64F' by default. +*/ +CV_EXPORTS_W void calcCovarMatrix( InputArray samples, OutputArray covar, + InputOutputArray mean, int flags, int ctype = CV_64F); + +/** wrap PCA::operator() */ +CV_EXPORTS_W void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, int maxComponents = 0); + +/** wrap PCA::operator() and add eigenvalues output parameter */ +CV_EXPORTS_AS(PCACompute2) void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, OutputArray eigenvalues, + int maxComponents = 0); + +/** wrap PCA::operator() */ +CV_EXPORTS_W void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, double retainedVariance); + +/** wrap PCA::operator() and add eigenvalues output parameter */ +CV_EXPORTS_AS(PCACompute2) void PCACompute(InputArray data, InputOutputArray mean, + OutputArray eigenvectors, OutputArray eigenvalues, + double retainedVariance); + +/** wrap PCA::project */ +CV_EXPORTS_W void PCAProject(InputArray data, InputArray mean, + InputArray eigenvectors, OutputArray result); + +/** wrap PCA::backProject */ +CV_EXPORTS_W void PCABackProject(InputArray data, InputArray mean, + InputArray eigenvectors, OutputArray result); + +/** wrap SVD::compute */ +CV_EXPORTS_W void SVDecomp( InputArray src, OutputArray w, OutputArray u, OutputArray vt, int flags = 0 ); + +/** wrap SVD::backSubst */ +CV_EXPORTS_W void SVBackSubst( InputArray w, InputArray u, InputArray vt, + InputArray rhs, OutputArray dst ); + +/** @brief Calculates the Mahalanobis distance between two vectors. + +The function cv::Mahalanobis calculates and returns the weighted distance between two vectors: +\f[d( \texttt{vec1} , \texttt{vec2} )= \sqrt{\sum_{i,j}{\texttt{icovar(i,j)}\cdot(\texttt{vec1}(I)-\texttt{vec2}(I))\cdot(\texttt{vec1(j)}-\texttt{vec2(j)})} }\f] +The covariance matrix may be calculated using the #calcCovarMatrix function and then inverted using +the invert function (preferably using the #DECOMP_SVD method, as the most accurate). +@param v1 first 1D input vector. +@param v2 second 1D input vector. +@param icovar inverse covariance matrix. +*/ +CV_EXPORTS_W double Mahalanobis(InputArray v1, InputArray v2, InputArray icovar); + +/** @brief Performs a forward or inverse Discrete Fourier transform of a 1D or 2D floating-point array. + +The function cv::dft performs one of the following: +- Forward the Fourier transform of a 1D vector of N elements: + \f[Y = F^{(N)} \cdot X,\f] + where \f$F^{(N)}_{jk}=\exp(-2\pi i j k/N)\f$ and \f$i=\sqrt{-1}\f$ +- Inverse the Fourier transform of a 1D vector of N elements: + \f[\begin{array}{l} X'= \left (F^{(N)} \right )^{-1} \cdot Y = \left (F^{(N)} \right )^* \cdot y \\ X = (1/N) \cdot X, \end{array}\f] + where \f$F^*=\left(\textrm{Re}(F^{(N)})-\textrm{Im}(F^{(N)})\right)^T\f$ +- Forward the 2D Fourier transform of a M x N matrix: + \f[Y = F^{(M)} \cdot X \cdot F^{(N)}\f] +- Inverse the 2D Fourier transform of a M x N matrix: + \f[\begin{array}{l} X'= \left (F^{(M)} \right )^* \cdot Y \cdot \left (F^{(N)} \right )^* \\ X = \frac{1}{M \cdot N} \cdot X' \end{array}\f] + +In case of real (single-channel) data, the output spectrum of the forward Fourier transform or input +spectrum of the inverse Fourier transform can be represented in a packed format called *CCS* +(complex-conjugate-symmetrical). It was borrowed from IPL (Intel\* Image Processing Library). Here +is how 2D *CCS* spectrum looks: +\f[\begin{bmatrix} Re Y_{0,0} & Re Y_{0,1} & Im Y_{0,1} & Re Y_{0,2} & Im Y_{0,2} & \cdots & Re Y_{0,N/2-1} & Im Y_{0,N/2-1} & Re Y_{0,N/2} \\ Re Y_{1,0} & Re Y_{1,1} & Im Y_{1,1} & Re Y_{1,2} & Im Y_{1,2} & \cdots & Re Y_{1,N/2-1} & Im Y_{1,N/2-1} & Re Y_{1,N/2} \\ Im Y_{1,0} & Re Y_{2,1} & Im Y_{2,1} & Re Y_{2,2} & Im Y_{2,2} & \cdots & Re Y_{2,N/2-1} & Im Y_{2,N/2-1} & Im Y_{1,N/2} \\ \hdotsfor{9} \\ Re Y_{M/2-1,0} & Re Y_{M-3,1} & Im Y_{M-3,1} & \hdotsfor{3} & Re Y_{M-3,N/2-1} & Im Y_{M-3,N/2-1}& Re Y_{M/2-1,N/2} \\ Im Y_{M/2-1,0} & Re Y_{M-2,1} & Im Y_{M-2,1} & \hdotsfor{3} & Re Y_{M-2,N/2-1} & Im Y_{M-2,N/2-1}& Im Y_{M/2-1,N/2} \\ Re Y_{M/2,0} & Re Y_{M-1,1} & Im Y_{M-1,1} & \hdotsfor{3} & Re Y_{M-1,N/2-1} & Im Y_{M-1,N/2-1}& Re Y_{M/2,N/2} \end{bmatrix}\f] + +In case of 1D transform of a real vector, the output looks like the first row of the matrix above. + +So, the function chooses an operation mode depending on the flags and size of the input array: +- If #DFT_ROWS is set or the input array has a single row or single column, the function + performs a 1D forward or inverse transform of each row of a matrix when #DFT_ROWS is set. + Otherwise, it performs a 2D transform. +- If the input array is real and #DFT_INVERSE is not set, the function performs a forward 1D or + 2D transform: + - When #DFT_COMPLEX_OUTPUT is set, the output is a complex matrix of the same size as + input. + - When #DFT_COMPLEX_OUTPUT is not set, the output is a real matrix of the same size as + input. In case of 2D transform, it uses the packed format as shown above. In case of a + single 1D transform, it looks like the first row of the matrix above. In case of + multiple 1D transforms (when using the #DFT_ROWS flag), each row of the output matrix + looks like the first row of the matrix above. +- If the input array is complex and either #DFT_INVERSE or #DFT_REAL_OUTPUT are not set, the + output is a complex array of the same size as input. The function performs a forward or + inverse 1D or 2D transform of the whole input array or each row of the input array + independently, depending on the flags DFT_INVERSE and DFT_ROWS. +- When #DFT_INVERSE is set and the input array is real, or it is complex but #DFT_REAL_OUTPUT + is set, the output is a real array of the same size as input. The function performs a 1D or 2D + inverse transformation of the whole input array or each individual row, depending on the flags + #DFT_INVERSE and #DFT_ROWS. + +If #DFT_SCALE is set, the scaling is done after the transformation. + +Unlike dct , the function supports arrays of arbitrary size. But only those arrays are processed +efficiently, whose sizes can be factorized in a product of small prime numbers (2, 3, and 5 in the +current implementation). Such an efficient DFT size can be calculated using the getOptimalDFTSize +method. + +The sample below illustrates how to calculate a DFT-based convolution of two 2D real arrays: +@code + void convolveDFT(InputArray A, InputArray B, OutputArray C) + { + // reallocate the output array if needed + C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type()); + Size dftSize; + // calculate the size of DFT transform + dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1); + dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1); + + // allocate temporary buffers and initialize them with 0's + Mat tempA(dftSize, A.type(), Scalar::all(0)); + Mat tempB(dftSize, B.type(), Scalar::all(0)); + + // copy A and B to the top-left corners of tempA and tempB, respectively + Mat roiA(tempA, Rect(0,0,A.cols,A.rows)); + A.copyTo(roiA); + Mat roiB(tempB, Rect(0,0,B.cols,B.rows)); + B.copyTo(roiB); + + // now transform the padded A & B in-place; + // use "nonzeroRows" hint for faster processing + dft(tempA, tempA, 0, A.rows); + dft(tempB, tempB, 0, B.rows); + + // multiply the spectrums; + // the function handles packed spectrum representations well + mulSpectrums(tempA, tempB, tempA); + + // transform the product back from the frequency domain. + // Even though all the result rows will be non-zero, + // you need only the first C.rows of them, and thus you + // pass nonzeroRows == C.rows + dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows); + + // now copy the result back to C. + tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C); + + // all the temporary buffers will be deallocated automatically + } +@endcode +To optimize this sample, consider the following approaches: +- Since nonzeroRows != 0 is passed to the forward transform calls and since A and B are copied to + the top-left corners of tempA and tempB, respectively, it is not necessary to clear the whole + tempA and tempB. It is only necessary to clear the tempA.cols - A.cols ( tempB.cols - B.cols) + rightmost columns of the matrices. +- This DFT-based convolution does not have to be applied to the whole big arrays, especially if B + is significantly smaller than A or vice versa. Instead, you can calculate convolution by parts. + To do this, you need to split the output array C into multiple tiles. For each tile, estimate + which parts of A and B are required to calculate convolution in this tile. If the tiles in C are + too small, the speed will decrease a lot because of repeated work. In the ultimate case, when + each tile in C is a single pixel, the algorithm becomes equivalent to the naive convolution + algorithm. If the tiles are too big, the temporary arrays tempA and tempB become too big and + there is also a slowdown because of bad cache locality. So, there is an optimal tile size + somewhere in the middle. +- If different tiles in C can be calculated in parallel and, thus, the convolution is done by + parts, the loop can be threaded. + +All of the above improvements have been implemented in #matchTemplate and #filter2D . Therefore, by +using them, you can get the performance even better than with the above theoretically optimal +implementation. Though, those two functions actually calculate cross-correlation, not convolution, +so you need to "flip" the second convolution operand B vertically and horizontally using flip . +@note +- An example using the discrete fourier transform can be found at + opencv_source_code/samples/cpp/dft.cpp +- (Python) An example using the dft functionality to perform Wiener deconvolution can be found + at opencv_source/samples/python/deconvolution.py +- (Python) An example rearranging the quadrants of a Fourier image can be found at + opencv_source/samples/python/dft.py +@param src input array that could be real or complex. +@param dst output array whose size and type depends on the flags . +@param flags transformation flags, representing a combination of the #DftFlags +@param nonzeroRows when the parameter is not zero, the function assumes that only the first +nonzeroRows rows of the input array (#DFT_INVERSE is not set) or only the first nonzeroRows of the +output array (#DFT_INVERSE is set) contain non-zeros, thus, the function can handle the rest of the +rows more efficiently and save some time; this technique is very useful for calculating array +cross-correlation or convolution using DFT. +@sa dct , getOptimalDFTSize , mulSpectrums, filter2D , matchTemplate , flip , cartToPolar , +magnitude , phase +*/ +CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0); + +/** @brief Calculates the inverse Discrete Fourier Transform of a 1D or 2D array. + +idft(src, dst, flags) is equivalent to dft(src, dst, flags | #DFT_INVERSE) . +@note None of dft and idft scales the result by default. So, you should pass #DFT_SCALE to one of +dft or idft explicitly to make these transforms mutually inverse. +@sa dft, dct, idct, mulSpectrums, getOptimalDFTSize +@param src input floating-point real or complex array. +@param dst output array whose size and type depend on the flags. +@param flags operation flags (see dft and #DftFlags). +@param nonzeroRows number of dst rows to process; the rest of the rows have undefined content (see +the convolution sample in dft description. +*/ +CV_EXPORTS_W void idft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0); + +/** @brief Performs a forward or inverse discrete Cosine transform of 1D or 2D array. + +The function cv::dct performs a forward or inverse discrete Cosine transform (DCT) of a 1D or 2D +floating-point array: +- Forward Cosine transform of a 1D vector of N elements: + \f[Y = C^{(N)} \cdot X\f] + where + \f[C^{(N)}_{jk}= \sqrt{\alpha_j/N} \cos \left ( \frac{\pi(2k+1)j}{2N} \right )\f] + and + \f$\alpha_0=1\f$, \f$\alpha_j=2\f$ for *j \> 0*. +- Inverse Cosine transform of a 1D vector of N elements: + \f[X = \left (C^{(N)} \right )^{-1} \cdot Y = \left (C^{(N)} \right )^T \cdot Y\f] + (since \f$C^{(N)}\f$ is an orthogonal matrix, \f$C^{(N)} \cdot \left(C^{(N)}\right)^T = I\f$ ) +- Forward 2D Cosine transform of M x N matrix: + \f[Y = C^{(N)} \cdot X \cdot \left (C^{(N)} \right )^T\f] +- Inverse 2D Cosine transform of M x N matrix: + \f[X = \left (C^{(N)} \right )^T \cdot X \cdot C^{(N)}\f] + +The function chooses the mode of operation by looking at the flags and size of the input array: +- If (flags & #DCT_INVERSE) == 0 , the function does a forward 1D or 2D transform. Otherwise, it + is an inverse 1D or 2D transform. +- If (flags & #DCT_ROWS) != 0 , the function performs a 1D transform of each row. +- If the array is a single column or a single row, the function performs a 1D transform. +- If none of the above is true, the function performs a 2D transform. + +@note Currently dct supports even-size arrays (2, 4, 6 ...). For data analysis and approximation, you +can pad the array when necessary. +Also, the function performance depends very much, and not monotonically, on the array size (see +getOptimalDFTSize ). In the current implementation DCT of a vector of size N is calculated via DFT +of a vector of size N/2 . Thus, the optimal DCT size N1 \>= N can be calculated as: +@code + size_t getOptimalDCTSize(size_t N) { return 2*getOptimalDFTSize((N+1)/2); } + N1 = getOptimalDCTSize(N); +@endcode +@param src input floating-point array. +@param dst output array of the same size and type as src . +@param flags transformation flags as a combination of cv::DftFlags (DCT_*) +@sa dft , getOptimalDFTSize , idct +*/ +CV_EXPORTS_W void dct(InputArray src, OutputArray dst, int flags = 0); + +/** @brief Calculates the inverse Discrete Cosine Transform of a 1D or 2D array. + +idct(src, dst, flags) is equivalent to dct(src, dst, flags | DCT_INVERSE). +@param src input floating-point single-channel array. +@param dst output array of the same size and type as src. +@param flags operation flags. +@sa dct, dft, idft, getOptimalDFTSize +*/ +CV_EXPORTS_W void idct(InputArray src, OutputArray dst, int flags = 0); + +/** @brief Performs the per-element multiplication of two Fourier spectrums. + +The function cv::mulSpectrums performs the per-element multiplication of the two CCS-packed or complex +matrices that are results of a real or complex Fourier transform. + +The function, together with dft and idft , may be used to calculate convolution (pass conjB=false ) +or correlation (pass conjB=true ) of two arrays rapidly. When the arrays are complex, they are +simply multiplied (per element) with an optional conjugation of the second-array elements. When the +arrays are real, they are assumed to be CCS-packed (see dft for details). +@param a first input array. +@param b second input array of the same size and type as src1 . +@param c output array of the same size and type as src1 . +@param flags operation flags; currently, the only supported flag is cv::DFT_ROWS, which indicates that +each row of src1 and src2 is an independent 1D Fourier spectrum. If you do not want to use this flag, then simply add a `0` as value. +@param conjB optional flag that conjugates the second input array before the multiplication (true) +or not (false). +*/ +CV_EXPORTS_W void mulSpectrums(InputArray a, InputArray b, OutputArray c, + int flags, bool conjB = false); + +/** @brief Returns the optimal DFT size for a given vector size. + +DFT performance is not a monotonic function of a vector size. Therefore, when you calculate +convolution of two arrays or perform the spectral analysis of an array, it usually makes sense to +pad the input data with zeros to get a bit larger array that can be transformed much faster than the +original one. Arrays whose size is a power-of-two (2, 4, 8, 16, 32, ...) are the fastest to process. +Though, the arrays whose size is a product of 2's, 3's, and 5's (for example, 300 = 5\*5\*3\*2\*2) +are also processed quite efficiently. + +The function cv::getOptimalDFTSize returns the minimum number N that is greater than or equal to vecsize +so that the DFT of a vector of size N can be processed efficiently. In the current implementation N += 2 ^p^ \* 3 ^q^ \* 5 ^r^ for some integer p, q, r. + +The function returns a negative number if vecsize is too large (very close to INT_MAX ). + +While the function cannot be used directly to estimate the optimal vector size for DCT transform +(since the current DCT implementation supports only even-size vectors), it can be easily processed +as getOptimalDFTSize((vecsize+1)/2)\*2. +@param vecsize vector size. +@sa dft , dct , idft , idct , mulSpectrums +*/ +CV_EXPORTS_W int getOptimalDFTSize(int vecsize); + +/** @brief Returns the default random number generator. + +The function cv::theRNG returns the default random number generator. For each thread, there is a +separate random number generator, so you can use the function safely in multi-thread environments. +If you just need to get a single random number using this generator or initialize an array, you can +use randu or randn instead. But if you are going to generate many random numbers inside a loop, it +is much faster to use this function to retrieve the generator and then use RNG::operator _Tp() . +@sa RNG, randu, randn +*/ +CV_EXPORTS RNG& theRNG(); + +/** @brief Sets state of default random number generator. + +The function cv::setRNGSeed sets state of default random number generator to custom value. +@param seed new state for default random number generator +@sa RNG, randu, randn +*/ +CV_EXPORTS_W void setRNGSeed(int seed); + +/** @brief Generates a single uniformly-distributed random number or an array of random numbers. + +Non-template variant of the function fills the matrix dst with uniformly-distributed +random numbers from the specified range: +\f[\texttt{low} _c \leq \texttt{dst} (I)_c < \texttt{high} _c\f] +@param dst output array of random numbers; the array must be pre-allocated. +@param low inclusive lower boundary of the generated random numbers. +@param high exclusive upper boundary of the generated random numbers. +@sa RNG, randn, theRNG +*/ +CV_EXPORTS_W void randu(InputOutputArray dst, InputArray low, InputArray high); + +/** @brief Fills the array with normally distributed random numbers. + +The function cv::randn fills the matrix dst with normally distributed random numbers with the specified +mean vector and the standard deviation matrix. The generated random numbers are clipped to fit the +value range of the output array data type. +@param dst output array of random numbers; the array must be pre-allocated and have 1 to 4 channels. +@param mean mean value (expectation) of the generated random numbers. +@param stddev standard deviation of the generated random numbers; it can be either a vector (in +which case a diagonal standard deviation matrix is assumed) or a square matrix. +@sa RNG, randu +*/ +CV_EXPORTS_W void randn(InputOutputArray dst, InputArray mean, InputArray stddev); + +/** @brief Shuffles the array elements randomly. + +The function cv::randShuffle shuffles the specified 1D array by randomly choosing pairs of elements and +swapping them. The number of such swap operations will be dst.rows\*dst.cols\*iterFactor . +@param dst input/output numerical 1D array. +@param iterFactor scale factor that determines the number of random swap operations (see the details +below). +@param rng optional random number generator used for shuffling; if it is zero, theRNG () is used +instead. +@sa RNG, sort +*/ +CV_EXPORTS_W void randShuffle(InputOutputArray dst, double iterFactor = 1., RNG* rng = 0); + +/** @brief Principal Component Analysis + +The class is used to calculate a special basis for a set of vectors. The +basis will consist of eigenvectors of the covariance matrix calculated +from the input set of vectors. The class %PCA can also transform +vectors to/from the new coordinate space defined by the basis. Usually, +in this new coordinate system, each vector from the original set (and +any linear combination of such vectors) can be quite accurately +approximated by taking its first few components, corresponding to the +eigenvectors of the largest eigenvalues of the covariance matrix. +Geometrically it means that you calculate a projection of the vector to +a subspace formed by a few eigenvectors corresponding to the dominant +eigenvalues of the covariance matrix. And usually such a projection is +very close to the original vector. So, you can represent the original +vector from a high-dimensional space with a much shorter vector +consisting of the projected vector's coordinates in the subspace. Such a +transformation is also known as Karhunen-Loeve Transform, or KLT. +See http://en.wikipedia.org/wiki/Principal_component_analysis + +The sample below is the function that takes two matrices. The first +function stores a set of vectors (a row per vector) that is used to +calculate PCA. The second function stores another "test" set of vectors +(a row per vector). First, these vectors are compressed with PCA, then +reconstructed back, and then the reconstruction error norm is computed +and printed for each vector. : + +@code{.cpp} +using namespace cv; + +PCA compressPCA(const Mat& pcaset, int maxComponents, + const Mat& testset, Mat& compressed) +{ + PCA pca(pcaset, // pass the data + Mat(), // we do not have a pre-computed mean vector, + // so let the PCA engine to compute it + PCA::DATA_AS_ROW, // indicate that the vectors + // are stored as matrix rows + // (use PCA::DATA_AS_COL if the vectors are + // the matrix columns) + maxComponents // specify, how many principal components to retain + ); + // if there is no test data, just return the computed basis, ready-to-use + if( !testset.data ) + return pca; + CV_Assert( testset.cols == pcaset.cols ); + + compressed.create(testset.rows, maxComponents, testset.type()); + + Mat reconstructed; + for( int i = 0; i < testset.rows; i++ ) + { + Mat vec = testset.row(i), coeffs = compressed.row(i), reconstructed; + // compress the vector, the result will be stored + // in the i-th row of the output matrix + pca.project(vec, coeffs); + // and then reconstruct it + pca.backProject(coeffs, reconstructed); + // and measure the error + printf("%d. diff = %g\n", i, norm(vec, reconstructed, NORM_L2)); + } + return pca; +} +@endcode +@sa calcCovarMatrix, mulTransposed, SVD, dft, dct +*/ +class CV_EXPORTS PCA +{ +public: + enum Flags { DATA_AS_ROW = 0, //!< indicates that the input samples are stored as matrix rows + DATA_AS_COL = 1, //!< indicates that the input samples are stored as matrix columns + USE_AVG = 2 //! + }; + + /** @brief default constructor + + The default constructor initializes an empty %PCA structure. The other + constructors initialize the structure and call PCA::operator()(). + */ + PCA(); + + /** @overload + @param data input samples stored as matrix rows or matrix columns. + @param mean optional mean value; if the matrix is empty (@c noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout (PCA::Flags) + @param maxComponents maximum number of components that %PCA should + retain; by default, all the components are retained. + */ + PCA(InputArray data, InputArray mean, int flags, int maxComponents = 0); + + /** @overload + @param data input samples stored as matrix rows or matrix columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout (PCA::Flags) + @param retainedVariance Percentage of variance that PCA should retain. + Using this parameter will let the PCA decided how many components to + retain but it will always keep at least 2. + */ + PCA(InputArray data, InputArray mean, int flags, double retainedVariance); + + /** @brief performs %PCA + + The operator performs %PCA of the supplied dataset. It is safe to reuse + the same PCA structure for multiple datasets. That is, if the structure + has been previously used with another dataset, the existing internal + data is reclaimed and the new @ref eigenvalues, @ref eigenvectors and @ref + mean are allocated and computed. + + The computed @ref eigenvalues are sorted from the largest to the smallest and + the corresponding @ref eigenvectors are stored as eigenvectors rows. + + @param data input samples stored as the matrix rows or as the matrix + columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout. (Flags) + @param maxComponents maximum number of components that PCA should + retain; by default, all the components are retained. + */ + PCA& operator()(InputArray data, InputArray mean, int flags, int maxComponents = 0); + + /** @overload + @param data input samples stored as the matrix rows or as the matrix + columns. + @param mean optional mean value; if the matrix is empty (noArray()), + the mean is computed from the data. + @param flags operation flags; currently the parameter is only used to + specify the data layout. (PCA::Flags) + @param retainedVariance Percentage of variance that %PCA should retain. + Using this parameter will let the %PCA decided how many components to + retain but it will always keep at least 2. + */ + PCA& operator()(InputArray data, InputArray mean, int flags, double retainedVariance); + + /** @brief Projects vector(s) to the principal component subspace. + + The methods project one or more vectors to the principal component + subspace, where each vector projection is represented by coefficients in + the principal component basis. The first form of the method returns the + matrix that the second form writes to the result. So the first form can + be used as a part of expression while the second form can be more + efficient in a processing loop. + @param vec input vector(s); must have the same dimensionality and the + same layout as the input data used at %PCA phase, that is, if + DATA_AS_ROW are specified, then `vec.cols==data.cols` + (vector dimensionality) and `vec.rows` is the number of vectors to + project, and the same is true for the PCA::DATA_AS_COL case. + */ + Mat project(InputArray vec) const; + + /** @overload + @param vec input vector(s); must have the same dimensionality and the + same layout as the input data used at PCA phase, that is, if + DATA_AS_ROW are specified, then `vec.cols==data.cols` + (vector dimensionality) and `vec.rows` is the number of vectors to + project, and the same is true for the PCA::DATA_AS_COL case. + @param result output vectors; in case of PCA::DATA_AS_COL, the + output matrix has as many columns as the number of input vectors, this + means that `result.cols==vec.cols` and the number of rows match the + number of principal components (for example, `maxComponents` parameter + passed to the constructor). + */ + void project(InputArray vec, OutputArray result) const; + + /** @brief Reconstructs vectors from their PC projections. + + The methods are inverse operations to PCA::project. They take PC + coordinates of projected vectors and reconstruct the original vectors. + Unless all the principal components have been retained, the + reconstructed vectors are different from the originals. But typically, + the difference is small if the number of components is large enough (but + still much smaller than the original vector dimensionality). As a + result, PCA is used. + @param vec coordinates of the vectors in the principal component + subspace, the layout and size are the same as of PCA::project output + vectors. + */ + Mat backProject(InputArray vec) const; + + /** @overload + @param vec coordinates of the vectors in the principal component + subspace, the layout and size are the same as of PCA::project output + vectors. + @param result reconstructed vectors; the layout and size are the same as + of PCA::project input vectors. + */ + void backProject(InputArray vec, OutputArray result) const; + + /** @brief write PCA objects + + Writes @ref eigenvalues @ref eigenvectors and @ref mean to specified FileStorage + */ + void write(FileStorage& fs) const; + + /** @brief load PCA objects + + Loads @ref eigenvalues @ref eigenvectors and @ref mean from specified FileNode + */ + void read(const FileNode& fn); + + Mat eigenvectors; //!< eigenvectors of the covariation matrix + Mat eigenvalues; //!< eigenvalues of the covariation matrix + Mat mean; //!< mean value subtracted before the projection and added after the back projection +}; + +/** @example samples/cpp/pca.cpp +An example using %PCA for dimensionality reduction while maintaining an amount of variance +*/ + +/** @example samples/cpp/tutorial_code/ml/introduction_to_pca/introduction_to_pca.cpp +Check @ref tutorial_introduction_to_pca "the corresponding tutorial" for more details +*/ + +/** +@brief Linear Discriminant Analysis +@todo document this class +*/ +class CV_EXPORTS LDA +{ +public: + /** @brief constructor + Initializes a LDA with num_components (default 0). + */ + explicit LDA(int num_components = 0); + + /** Initializes and performs a Discriminant Analysis with Fisher's + Optimization Criterion on given data in src and corresponding labels + in labels. If 0 (or less) number of components are given, they are + automatically determined for given data in computation. + */ + LDA(InputArrayOfArrays src, InputArray labels, int num_components = 0); + + /** Serializes this object to a given filename. + */ + void save(const String& filename) const; + + /** Deserializes this object from a given filename. + */ + void load(const String& filename); + + /** Serializes this object to a given cv::FileStorage. + */ + void save(FileStorage& fs) const; + + /** Deserializes this object from a given cv::FileStorage. + */ + void load(const FileStorage& node); + + /** destructor + */ + ~LDA(); + + /** Compute the discriminants for data in src (row aligned) and labels. + */ + void compute(InputArrayOfArrays src, InputArray labels); + + /** Projects samples into the LDA subspace. + src may be one or more row aligned samples. + */ + Mat project(InputArray src); + + /** Reconstructs projections from the LDA subspace. + src may be one or more row aligned projections. + */ + Mat reconstruct(InputArray src); + + /** Returns the eigenvectors of this LDA. + */ + Mat eigenvectors() const { return _eigenvectors; } + + /** Returns the eigenvalues of this LDA. + */ + Mat eigenvalues() const { return _eigenvalues; } + + static Mat subspaceProject(InputArray W, InputArray mean, InputArray src); + static Mat subspaceReconstruct(InputArray W, InputArray mean, InputArray src); + +protected: + bool _dataAsRow; // unused, but needed for 3.0 ABI compatibility. + int _num_components; + Mat _eigenvectors; + Mat _eigenvalues; + void lda(InputArrayOfArrays src, InputArray labels); +}; + +/** @brief Singular Value Decomposition + +Class for computing Singular Value Decomposition of a floating-point +matrix. The Singular Value Decomposition is used to solve least-square +problems, under-determined linear systems, invert matrices, compute +condition numbers, and so on. + +If you want to compute a condition number of a matrix or an absolute value of +its determinant, you do not need `u` and `vt`. You can pass +flags=SVD::NO_UV|... . Another flag SVD::FULL_UV indicates that full-size u +and vt must be computed, which is not necessary most of the time. + +@sa invert, solve, eigen, determinant +*/ +class CV_EXPORTS SVD +{ +public: + enum Flags { + /** allow the algorithm to modify the decomposed matrix; it can save space and speed up + processing. currently ignored. */ + MODIFY_A = 1, + /** indicates that only a vector of singular values `w` is to be processed, while u and vt + will be set to empty matrices */ + NO_UV = 2, + /** when the matrix is not square, by default the algorithm produces u and vt matrices of + sufficiently large size for the further A reconstruction; if, however, FULL_UV flag is + specified, u and vt will be full-size square orthogonal matrices.*/ + FULL_UV = 4 + }; + + /** @brief the default constructor + + initializes an empty SVD structure + */ + SVD(); + + /** @overload + initializes an empty SVD structure and then calls SVD::operator() + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param flags operation flags (SVD::Flags) + */ + SVD( InputArray src, int flags = 0 ); + + /** @brief the operator that performs SVD. The previously allocated u, w and vt are released. + + The operator performs the singular value decomposition of the supplied + matrix. The u,`vt` , and the vector of singular values w are stored in + the structure. The same SVD structure can be reused many times with + different matrices. Each time, if needed, the previous u,`vt` , and w + are reclaimed and the new matrices are created, which is all handled by + Mat::create. + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param flags operation flags (SVD::Flags) + */ + SVD& operator ()( InputArray src, int flags = 0 ); + + /** @brief decomposes matrix and stores the results to user-provided matrices + + The methods/functions perform SVD of matrix. Unlike SVD::SVD constructor + and SVD::operator(), they store the results to the user-provided + matrices: + + @code{.cpp} + Mat A, w, u, vt; + SVD::compute(A, w, u, vt); + @endcode + + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param w calculated singular values + @param u calculated left singular vectors + @param vt transposed matrix of right singular vectors + @param flags operation flags - see SVD::Flags. + */ + static void compute( InputArray src, OutputArray w, + OutputArray u, OutputArray vt, int flags = 0 ); + + /** @overload + computes singular values of a matrix + @param src decomposed matrix. The depth has to be CV_32F or CV_64F. + @param w calculated singular values + @param flags operation flags - see SVD::Flags. + */ + static void compute( InputArray src, OutputArray w, int flags = 0 ); + + /** @brief performs back substitution + */ + static void backSubst( InputArray w, InputArray u, + InputArray vt, InputArray rhs, + OutputArray dst ); + + /** @brief solves an under-determined singular linear system + + The method finds a unit-length solution x of a singular linear system + A\*x = 0. Depending on the rank of A, there can be no solutions, a + single solution or an infinite number of solutions. In general, the + algorithm solves the following problem: + \f[dst = \arg \min _{x: \| x \| =1} \| src \cdot x \|\f] + @param src left-hand-side matrix. + @param dst found solution. + */ + static void solveZ( InputArray src, OutputArray dst ); + + /** @brief performs a singular value back substitution. + + The method calculates a back substitution for the specified right-hand + side: + + \f[\texttt{x} = \texttt{vt} ^T \cdot diag( \texttt{w} )^{-1} \cdot \texttt{u} ^T \cdot \texttt{rhs} \sim \texttt{A} ^{-1} \cdot \texttt{rhs}\f] + + Using this technique you can either get a very accurate solution of the + convenient linear system, or the best (in the least-squares terms) + pseudo-solution of an overdetermined linear system. + + @param rhs right-hand side of a linear system (u\*w\*v')\*dst = rhs to + be solved, where A has been previously decomposed. + + @param dst found solution of the system. + + @note Explicit SVD with the further back substitution only makes sense + if you need to solve many linear systems with the same left-hand side + (for example, src ). If all you need is to solve a single system + (possibly with multiple rhs immediately available), simply call solve + add pass #DECOMP_SVD there. It does absolutely the same thing. + */ + void backSubst( InputArray rhs, OutputArray dst ) const; + + /** @todo document */ + template static + void compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w, Matx<_Tp, m, nm>& u, Matx<_Tp, n, nm>& vt ); + + /** @todo document */ + template static + void compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w ); + + /** @todo document */ + template static + void backSubst( const Matx<_Tp, nm, 1>& w, const Matx<_Tp, m, nm>& u, const Matx<_Tp, n, nm>& vt, const Matx<_Tp, m, nb>& rhs, Matx<_Tp, n, nb>& dst ); + + Mat u, w, vt; +}; + +/** @brief Random Number Generator + +Random number generator. It encapsulates the state (currently, a 64-bit +integer) and has methods to return scalar random values and to fill +arrays with random values. Currently it supports uniform and Gaussian +(normal) distributions. The generator uses Multiply-With-Carry +algorithm, introduced by G. Marsaglia ( + ). +Gaussian-distribution random numbers are generated using the Ziggurat +algorithm ( ), +introduced by G. Marsaglia and W. W. Tsang. +*/ +class CV_EXPORTS RNG +{ +public: + enum { UNIFORM = 0, + NORMAL = 1 + }; + + /** @brief constructor + + These are the RNG constructors. The first form sets the state to some + pre-defined value, equal to 2\*\*32-1 in the current implementation. The + second form sets the state to the specified value. If you passed state=0 + , the constructor uses the above default value instead to avoid the + singular random number sequence, consisting of all zeros. + */ + RNG(); + /** @overload + @param state 64-bit value used to initialize the RNG. + */ + RNG(uint64 state); + /**The method updates the state using the MWC algorithm and returns the + next 32-bit random number.*/ + unsigned next(); + + /**Each of the methods updates the state using the MWC algorithm and + returns the next random number of the specified type. In case of integer + types, the returned number is from the available value range for the + specified type. In case of floating-point types, the returned value is + from [0,1) range. + */ + operator uchar(); + /** @overload */ + operator schar(); + /** @overload */ + operator ushort(); + /** @overload */ + operator short(); + /** @overload */ + operator unsigned(); + /** @overload */ + operator int(); + /** @overload */ + operator float(); + /** @overload */ + operator double(); + + /** @brief returns a random integer sampled uniformly from [0, N). + + The methods transform the state using the MWC algorithm and return the + next random number. The first form is equivalent to RNG::next . The + second form returns the random number modulo N , which means that the + result is in the range [0, N) . + */ + unsigned operator ()(); + /** @overload + @param N upper non-inclusive boundary of the returned random number. + */ + unsigned operator ()(unsigned N); + + /** @brief returns uniformly distributed integer random number from [a,b) range + + The methods transform the state using the MWC algorithm and return the + next uniformly-distributed random number of the specified type, deduced + from the input parameter type, from the range [a, b) . There is a nuance + illustrated by the following sample: + + @code{.cpp} + RNG rng; + + // always produces 0 + double a = rng.uniform(0, 1); + + // produces double from [0, 1) + double a1 = rng.uniform((double)0, (double)1); + + // produces float from [0, 1) + float b = rng.uniform(0.f, 1.f); + + // produces double from [0, 1) + double c = rng.uniform(0., 1.); + + // may cause compiler error because of ambiguity: + // RNG::uniform(0, (int)0.999999)? or RNG::uniform((double)0, 0.99999)? + double d = rng.uniform(0, 0.999999); + @endcode + + The compiler does not take into account the type of the variable to + which you assign the result of RNG::uniform . The only thing that + matters to the compiler is the type of a and b parameters. So, if you + want a floating-point random number, but the range boundaries are + integer numbers, either put dots in the end, if they are constants, or + use explicit type cast operators, as in the a1 initialization above. + @param a lower inclusive boundary of the returned random number. + @param b upper non-inclusive boundary of the returned random number. + */ + int uniform(int a, int b); + /** @overload */ + float uniform(float a, float b); + /** @overload */ + double uniform(double a, double b); + + /** @brief Fills arrays with random numbers. + + @param mat 2D or N-dimensional matrix; currently matrices with more than + 4 channels are not supported by the methods, use Mat::reshape as a + possible workaround. + @param distType distribution type, RNG::UNIFORM or RNG::NORMAL. + @param a first distribution parameter; in case of the uniform + distribution, this is an inclusive lower boundary, in case of the normal + distribution, this is a mean value. + @param b second distribution parameter; in case of the uniform + distribution, this is a non-inclusive upper boundary, in case of the + normal distribution, this is a standard deviation (diagonal of the + standard deviation matrix or the full standard deviation matrix). + @param saturateRange pre-saturation flag; for uniform distribution only; + if true, the method will first convert a and b to the acceptable value + range (according to the mat datatype) and then will generate uniformly + distributed random numbers within the range [saturate(a), saturate(b)), + if saturateRange=false, the method will generate uniformly distributed + random numbers in the original range [a, b) and then will saturate them, + it means, for example, that + theRNG().fill(mat_8u, RNG::UNIFORM, -DBL_MAX, DBL_MAX) will likely + produce array mostly filled with 0's and 255's, since the range (0, 255) + is significantly smaller than [-DBL_MAX, DBL_MAX). + + Each of the methods fills the matrix with the random values from the + specified distribution. As the new numbers are generated, the RNG state + is updated accordingly. In case of multiple-channel images, every + channel is filled independently, which means that RNG cannot generate + samples from the multi-dimensional Gaussian distribution with + non-diagonal covariance matrix directly. To do that, the method + generates samples from multi-dimensional standard Gaussian distribution + with zero mean and identity covariation matrix, and then transforms them + using transform to get samples from the specified Gaussian distribution. + */ + void fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange = false ); + + /** @brief Returns the next random number sampled from the Gaussian distribution + @param sigma standard deviation of the distribution. + + The method transforms the state using the MWC algorithm and returns the + next random number from the Gaussian distribution N(0,sigma) . That is, + the mean value of the returned random numbers is zero and the standard + deviation is the specified sigma . + */ + double gaussian(double sigma); + + uint64 state; + + bool operator ==(const RNG& other) const; +}; + +/** @brief Mersenne Twister random number generator + +Inspired by http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c +@todo document +*/ +class CV_EXPORTS RNG_MT19937 +{ +public: + RNG_MT19937(); + RNG_MT19937(unsigned s); + void seed(unsigned s); + + unsigned next(); + + operator int(); + operator unsigned(); + operator float(); + operator double(); + + unsigned operator ()(unsigned N); + unsigned operator ()(); + + /** @brief returns uniformly distributed integer random number from [a,b) range*/ + int uniform(int a, int b); + /** @brief returns uniformly distributed floating-point random number from [a,b) range*/ + float uniform(float a, float b); + /** @brief returns uniformly distributed double-precision floating-point random number from [a,b) range*/ + double uniform(double a, double b); + +private: + enum PeriodParameters {N = 624, M = 397}; + unsigned state[N]; + int mti; +}; + +//! @} core_array + +//! @addtogroup core_cluster +//! @{ + +/** @example samples/cpp/kmeans.cpp +An example on K-means clustering +*/ + +/** @brief Finds centers of clusters and groups input samples around the clusters. + +The function kmeans implements a k-means algorithm that finds the centers of cluster_count clusters +and groups the input samples around the clusters. As an output, \f$\texttt{bestLabels}_i\f$ contains a +0-based cluster index for the sample stored in the \f$i^{th}\f$ row of the samples matrix. + +@note +- (Python) An example on K-means clustering can be found at + opencv_source_code/samples/python/kmeans.py +@param data Data for clustering. An array of N-Dimensional points with float coordinates is needed. +Examples of this array can be: +- Mat points(count, 2, CV_32F); +- Mat points(count, 1, CV_32FC2); +- Mat points(1, count, CV_32FC2); +- std::vector\ points(sampleCount); +@param K Number of clusters to split the set by. +@param bestLabels Input/output integer array that stores the cluster indices for every sample. +@param criteria The algorithm termination criteria, that is, the maximum number of iterations and/or +the desired accuracy. The accuracy is specified as criteria.epsilon. As soon as each of the cluster +centers moves by less than criteria.epsilon on some iteration, the algorithm stops. +@param attempts Flag to specify the number of times the algorithm is executed using different +initial labellings. The algorithm returns the labels that yield the best compactness (see the last +function parameter). +@param flags Flag that can take values of cv::KmeansFlags +@param centers Output matrix of the cluster centers, one row per each cluster center. +@return The function returns the compactness measure that is computed as +\f[\sum _i \| \texttt{samples} _i - \texttt{centers} _{ \texttt{labels} _i} \| ^2\f] +after every attempt. The best (minimum) value is chosen and the corresponding labels and the +compactness value are returned by the function. Basically, you can use only the core of the +function, set the number of attempts to 1, initialize labels each time using a custom algorithm, +pass them with the ( flags = #KMEANS_USE_INITIAL_LABELS ) flag, and then choose the best +(most-compact) clustering. +*/ +CV_EXPORTS_W double kmeans( InputArray data, int K, InputOutputArray bestLabels, + TermCriteria criteria, int attempts, + int flags, OutputArray centers = noArray() ); + +//! @} core_cluster + +//! @addtogroup core_basic +//! @{ + +/////////////////////////////// Formatted output of cv::Mat /////////////////////////// + +/** @todo document */ +class CV_EXPORTS Formatted +{ +public: + virtual const char* next() = 0; + virtual void reset() = 0; + virtual ~Formatted(); +}; + +/** @todo document */ +class CV_EXPORTS Formatter +{ +public: + enum { FMT_DEFAULT = 0, + FMT_MATLAB = 1, + FMT_CSV = 2, + FMT_PYTHON = 3, + FMT_NUMPY = 4, + FMT_C = 5 + }; + + virtual ~Formatter(); + + virtual Ptr format(const Mat& mtx) const = 0; + + virtual void set32fPrecision(int p = 8) = 0; + virtual void set64fPrecision(int p = 16) = 0; + virtual void setMultiline(bool ml = true) = 0; + + static Ptr get(int fmt = FMT_DEFAULT); + +}; + +static inline +String& operator << (String& out, Ptr fmtd) +{ + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + out += cv::String(str); + return out; +} + +static inline +String& operator << (String& out, const Mat& mtx) +{ + return out << Formatter::get()->format(mtx); +} + +//////////////////////////////////////// Algorithm //////////////////////////////////// + +class CV_EXPORTS Algorithm; + +template struct ParamType {}; + + +/** @brief This is a base class for all more or less complex algorithms in OpenCV + +especially for classes of algorithms, for which there can be multiple implementations. The examples +are stereo correspondence (for which there are algorithms like block matching, semi-global block +matching, graph-cut etc.), background subtraction (which can be done using mixture-of-gaussians +models, codebook-based algorithm etc.), optical flow (block matching, Lucas-Kanade, Horn-Schunck +etc.). + +Here is example of SimpleBlobDetector use in your application via Algorithm interface: +@snippet snippets/core_various.cpp Algorithm +*/ +class CV_EXPORTS_W Algorithm +{ +public: + Algorithm(); + virtual ~Algorithm(); + + /** @brief Clears the algorithm state + */ + CV_WRAP virtual void clear() {} + + /** @brief Stores algorithm parameters in a file storage + */ + virtual void write(FileStorage& fs) const { CV_UNUSED(fs); } + + /** @brief simplified API for language bindings + * @overload + */ + CV_WRAP void write(const Ptr& fs, const String& name = String()) const; + + /** @brief Reads algorithm parameters from a file storage + */ + CV_WRAP virtual void read(const FileNode& fn) { CV_UNUSED(fn); } + + /** @brief Returns true if the Algorithm is empty (e.g. in the very beginning or after unsuccessful read + */ + CV_WRAP virtual bool empty() const { return false; } + + /** @brief Reads algorithm from the file node + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + cv::FileStorage fsRead("example.xml", FileStorage::READ); + Ptr svm = Algorithm::read(fsRead.root()); + @endcode + In order to make this method work, the derived class must overwrite Algorithm::read(const + FileNode& fn) and also have static create() method without parameters + (or with all the optional parameters) + */ + template static Ptr<_Tp> read(const FileNode& fn) + { + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** @brief Loads algorithm from the file + + @param filename Name of the file to read. + @param objname The optional name of the node to read (if empty, the first top-level node will be used) + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + Ptr svm = Algorithm::load("my_svm_model.xml"); + @endcode + In order to make this method work, the derived class must overwrite Algorithm::read(const + FileNode& fn). + */ + template static Ptr<_Tp> load(const String& filename, const String& objname=String()) + { + FileStorage fs(filename, FileStorage::READ); + CV_Assert(fs.isOpened()); + FileNode fn = objname.empty() ? fs.getFirstTopLevelNode() : fs[objname]; + if (fn.empty()) return Ptr<_Tp>(); + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** @brief Loads algorithm from a String + + @param strModel The string variable containing the model you want to load. + @param objname The optional name of the node to read (if empty, the first top-level node will be used) + + This is static template method of Algorithm. It's usage is following (in the case of SVM): + @code + Ptr svm = Algorithm::loadFromString(myStringModel); + @endcode + */ + template static Ptr<_Tp> loadFromString(const String& strModel, const String& objname=String()) + { + FileStorage fs(strModel, FileStorage::READ + FileStorage::MEMORY); + FileNode fn = objname.empty() ? fs.getFirstTopLevelNode() : fs[objname]; + Ptr<_Tp> obj = _Tp::create(); + obj->read(fn); + return !obj->empty() ? obj : Ptr<_Tp>(); + } + + /** Saves the algorithm to a file. + In order to make this method work, the derived class must implement Algorithm::write(FileStorage& fs). */ + CV_WRAP virtual void save(const String& filename) const; + + /** Returns the algorithm string identifier. + This string is used as top level xml/yml node tag when the object is saved to a file or string. */ + CV_WRAP virtual String getDefaultName() const; + +protected: + void writeFormat(FileStorage& fs) const; +}; + +struct Param { + enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6, FLOAT=7, + UNSIGNED_INT=8, UINT64=9, UCHAR=11, SCALAR=12 }; +}; + + + +template<> struct ParamType +{ + typedef bool const_param_type; + typedef bool member_type; + + enum { type = Param::BOOLEAN }; +}; + +template<> struct ParamType +{ + typedef int const_param_type; + typedef int member_type; + + enum { type = Param::INT }; +}; + +template<> struct ParamType +{ + typedef double const_param_type; + typedef double member_type; + + enum { type = Param::REAL }; +}; + +template<> struct ParamType +{ + typedef const String& const_param_type; + typedef String member_type; + + enum { type = Param::STRING }; +}; + +template<> struct ParamType +{ + typedef const Mat& const_param_type; + typedef Mat member_type; + + enum { type = Param::MAT }; +}; + +template<> struct ParamType > +{ + typedef const std::vector& const_param_type; + typedef std::vector member_type; + + enum { type = Param::MAT_VECTOR }; +}; + +template<> struct ParamType +{ + typedef const Ptr& const_param_type; + typedef Ptr member_type; + + enum { type = Param::ALGORITHM }; +}; + +template<> struct ParamType +{ + typedef float const_param_type; + typedef float member_type; + + enum { type = Param::FLOAT }; +}; + +template<> struct ParamType +{ + typedef unsigned const_param_type; + typedef unsigned member_type; + + enum { type = Param::UNSIGNED_INT }; +}; + +template<> struct ParamType +{ + typedef uint64 const_param_type; + typedef uint64 member_type; + + enum { type = Param::UINT64 }; +}; + +template<> struct ParamType +{ + typedef uchar const_param_type; + typedef uchar member_type; + + enum { type = Param::UCHAR }; +}; + +template<> struct ParamType +{ + typedef const Scalar& const_param_type; + typedef Scalar member_type; + + enum { type = Param::SCALAR }; +}; + +//! @} core_basic + +} //namespace cv + +#include "opencv2/core/operations.hpp" +#include "opencv2/core/cvstd.inl.hpp" +#include "opencv2/core/utility.hpp" +#include "opencv2/core/optim.hpp" +#include "opencv2/core/ovx.hpp" + +#endif /*OPENCV_CORE_HPP*/ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/affine.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/affine.hpp new file mode 100644 index 0000000..7e2ed30 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/affine.hpp @@ -0,0 +1,678 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_AFFINE3_HPP +#define OPENCV_CORE_AFFINE3_HPP + +#ifdef __cplusplus + +#include + +namespace cv +{ + +//! @addtogroup core +//! @{ + + /** @brief Affine transform + * + * It represents a 4x4 homogeneous transformation matrix \f$T\f$ + * + * \f[T = + * \begin{bmatrix} + * R & t\\ + * 0 & 1\\ + * \end{bmatrix} + * \f] + * + * where \f$R\f$ is a 3x3 rotation matrix and \f$t\f$ is a 3x1 translation vector. + * + * You can specify \f$R\f$ either by a 3x3 rotation matrix or by a 3x1 rotation vector, + * which is converted to a 3x3 rotation matrix by the Rodrigues formula. + * + * To construct a matrix \f$T\f$ representing first rotation around the axis \f$r\f$ with rotation + * angle \f$|r|\f$ in radian (right hand rule) and then translation by the vector \f$t\f$, you can use + * + * @code + * cv::Vec3f r, t; + * cv::Affine3f T(r, t); + * @endcode + * + * If you already have the rotation matrix \f$R\f$, then you can use + * + * @code + * cv::Matx33f R; + * cv::Affine3f T(R, t); + * @endcode + * + * To extract the rotation matrix \f$R\f$ from \f$T\f$, use + * + * @code + * cv::Matx33f R = T.rotation(); + * @endcode + * + * To extract the translation vector \f$t\f$ from \f$T\f$, use + * + * @code + * cv::Vec3f t = T.translation(); + * @endcode + * + * To extract the rotation vector \f$r\f$ from \f$T\f$, use + * + * @code + * cv::Vec3f r = T.rvec(); + * @endcode + * + * Note that since the mapping from rotation vectors to rotation matrices + * is many to one. The returned rotation vector is not necessarily the one + * you used before to set the matrix. + * + * If you have two transformations \f$T = T_1 * T_2\f$, use + * + * @code + * cv::Affine3f T, T1, T2; + * T = T2.concatenate(T1); + * @endcode + * + * To get the inverse transform of \f$T\f$, use + * + * @code + * cv::Affine3f T, T_inv; + * T_inv = T.inv(); + * @endcode + * + */ + template + class Affine3 + { + public: + typedef T float_type; + typedef Matx Mat3; + typedef Matx Mat4; + typedef Vec Vec3; + + //! Default constructor. It represents a 4x4 identity matrix. + Affine3(); + + //! Augmented affine matrix + Affine3(const Mat4& affine); + + /** + * The resulting 4x4 matrix is + * + * \f[ + * \begin{bmatrix} + * R & t\\ + * 0 & 1\\ + * \end{bmatrix} + * \f] + * + * @param R 3x3 rotation matrix. + * @param t 3x1 translation vector. + */ + Affine3(const Mat3& R, const Vec3& t = Vec3::all(0)); + + /** + * Rodrigues vector. + * + * The last row of the current matrix is set to [0,0,0,1]. + * + * @param rvec 3x1 rotation vector. Its direction indicates the rotation axis and its length + * indicates the rotation angle in radian (using right hand rule). + * @param t 3x1 translation vector. + */ + Affine3(const Vec3& rvec, const Vec3& t = Vec3::all(0)); + + /** + * Combines all constructors above. Supports 4x4, 3x4, 3x3, 1x3, 3x1 sizes of data matrix. + * + * The last row of the current matrix is set to [0,0,0,1] when data is not 4x4. + * + * @param data 1-channel matrix. + * when it is 4x4, it is copied to the current matrix and t is not used. + * When it is 3x4, it is copied to the upper part 3x4 of the current matrix and t is not used. + * When it is 3x3, it is copied to the upper left 3x3 part of the current matrix. + * When it is 3x1 or 1x3, it is treated as a rotation vector and the Rodrigues formula is used + * to compute a 3x3 rotation matrix. + * @param t 3x1 translation vector. It is used only when data is neither 4x4 nor 3x4. + */ + explicit Affine3(const Mat& data, const Vec3& t = Vec3::all(0)); + + //! From 16-element array + explicit Affine3(const float_type* vals); + + //! Create an 4x4 identity transform + static Affine3 Identity(); + + /** + * Rotation matrix. + * + * Copy the rotation matrix to the upper left 3x3 part of the current matrix. + * The remaining elements of the current matrix are not changed. + * + * @param R 3x3 rotation matrix. + * + */ + void rotation(const Mat3& R); + + /** + * Rodrigues vector. + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param rvec 3x1 rotation vector. The direction indicates the rotation axis and + * its length indicates the rotation angle in radian (using the right thumb convention). + */ + void rotation(const Vec3& rvec); + + /** + * Combines rotation methods above. Supports 3x3, 1x3, 3x1 sizes of data matrix. + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param data 1-channel matrix. + * When it is a 3x3 matrix, it sets the upper left 3x3 part of the current matrix. + * When it is a 1x3 or 3x1 matrix, it is used as a rotation vector. The Rodrigues formula + * is used to compute the rotation matrix and sets the upper left 3x3 part of the current matrix. + */ + void rotation(const Mat& data); + + /** + * Copy the 3x3 matrix L to the upper left part of the current matrix + * + * It sets the upper left 3x3 part of the matrix. The remaining part is unaffected. + * + * @param L 3x3 matrix. + */ + void linear(const Mat3& L); + + /** + * Copy t to the first three elements of the last column of the current matrix + * + * It sets the upper right 3x1 part of the matrix. The remaining part is unaffected. + * + * @param t 3x1 translation vector. + */ + void translation(const Vec3& t); + + //! @return the upper left 3x3 part + Mat3 rotation() const; + + //! @return the upper left 3x3 part + Mat3 linear() const; + + //! @return the upper right 3x1 part + Vec3 translation() const; + + //! Rodrigues vector. + //! @return a vector representing the upper left 3x3 rotation matrix of the current matrix. + //! @warning Since the mapping between rotation vectors and rotation matrices is many to one, + //! this function returns only one rotation vector that represents the current rotation matrix, + //! which is not necessarily the same one set by `rotation(const Vec3& rvec)`. + Vec3 rvec() const; + + //! @return the inverse of the current matrix. + Affine3 inv(int method = cv::DECOMP_SVD) const; + + //! a.rotate(R) is equivalent to Affine(R, 0) * a; + Affine3 rotate(const Mat3& R) const; + + //! a.rotate(rvec) is equivalent to Affine(rvec, 0) * a; + Affine3 rotate(const Vec3& rvec) const; + + //! a.translate(t) is equivalent to Affine(E, t) * a, where E is an identity matrix + Affine3 translate(const Vec3& t) const; + + //! a.concatenate(affine) is equivalent to affine * a; + Affine3 concatenate(const Affine3& affine) const; + + template operator Affine3() const; + + template Affine3 cast() const; + + Mat4 matrix; + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + Affine3(const Eigen::Transform& affine); + Affine3(const Eigen::Transform& affine); + operator Eigen::Transform() const; + operator Eigen::Transform() const; +#endif + }; + + template static + Affine3 operator*(const Affine3& affine1, const Affine3& affine2); + + //! V is a 3-element vector with member fields x, y and z + template static + V operator*(const Affine3& affine, const V& vector); + + typedef Affine3 Affine3f; + typedef Affine3 Affine3d; + + static Vec3f operator*(const Affine3f& affine, const Vec3f& vector); + static Vec3d operator*(const Affine3d& affine, const Vec3d& vector); + + template class DataType< Affine3<_Tp> > + { + public: + typedef Affine3<_Tp> value_type; + typedef Affine3::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 16, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; + }; + + namespace traits { + template + struct Depth< Affine3<_Tp> > { enum { value = Depth<_Tp>::value }; }; + template + struct Type< Affine3<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 16) }; }; + } // namespace + +//! @} core + +} + +//! @cond IGNORED + +/////////////////////////////////////////////////////////////////////////////////// +// Implementation + +template inline +cv::Affine3::Affine3() + : matrix(Mat4::eye()) +{} + +template inline +cv::Affine3::Affine3(const Mat4& affine) + : matrix(affine) +{} + +template inline +cv::Affine3::Affine3(const Mat3& R, const Vec3& t) +{ + rotation(R); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const Vec3& _rvec, const Vec3& t) +{ + rotation(_rvec); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const cv::Mat& data, const Vec3& t) +{ + CV_Assert(data.type() == cv::traits::Type::value); + CV_Assert(data.channels() == 1); + + if (data.cols == 4 && data.rows == 4) + { + data.copyTo(matrix); + return; + } + else if (data.cols == 4 && data.rows == 3) + { + rotation(data(Rect(0, 0, 3, 3))); + translation(data(Rect(3, 0, 1, 3))); + } + else + { + rotation(data); + translation(t); + } + + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const float_type* vals) : matrix(vals) +{} + +template inline +cv::Affine3 cv::Affine3::Identity() +{ + return Affine3(cv::Affine3::Mat4::eye()); +} + +template inline +void cv::Affine3::rotation(const Mat3& R) +{ + linear(R); +} + +template inline +void cv::Affine3::rotation(const Vec3& _rvec) +{ + double theta = norm(_rvec); + + if (theta < DBL_EPSILON) + rotation(Mat3::eye()); + else + { + double c = std::cos(theta); + double s = std::sin(theta); + double c1 = 1. - c; + double itheta = (theta != 0) ? 1./theta : 0.; + + Point3_ r = _rvec*itheta; + + Mat3 rrt( r.x*r.x, r.x*r.y, r.x*r.z, r.x*r.y, r.y*r.y, r.y*r.z, r.x*r.z, r.y*r.z, r.z*r.z ); + Mat3 r_x( 0, -r.z, r.y, r.z, 0, -r.x, -r.y, r.x, 0 ); + + // R = cos(theta)*I + (1 - cos(theta))*r*rT + sin(theta)*[r_x] + // where [r_x] is [0 -rz ry; rz 0 -rx; -ry rx 0] + Mat3 R = c*Mat3::eye() + c1*rrt + s*r_x; + + rotation(R); + } +} + +//Combines rotation methods above. Supports 3x3, 1x3, 3x1 sizes of data matrix; +template inline +void cv::Affine3::rotation(const cv::Mat& data) +{ + CV_Assert(data.type() == cv::traits::Type::value); + CV_Assert(data.channels() == 1); + + if (data.cols == 3 && data.rows == 3) + { + Mat3 R; + data.copyTo(R); + rotation(R); + } + else if ((data.cols == 3 && data.rows == 1) || (data.cols == 1 && data.rows == 3)) + { + Vec3 _rvec; + data.reshape(1, 3).copyTo(_rvec); + rotation(_rvec); + } + else + CV_Error(Error::StsError, "Input matrix can only be 3x3, 1x3 or 3x1"); +} + +template inline +void cv::Affine3::linear(const Mat3& L) +{ + matrix.val[0] = L.val[0]; matrix.val[1] = L.val[1]; matrix.val[ 2] = L.val[2]; + matrix.val[4] = L.val[3]; matrix.val[5] = L.val[4]; matrix.val[ 6] = L.val[5]; + matrix.val[8] = L.val[6]; matrix.val[9] = L.val[7]; matrix.val[10] = L.val[8]; +} + +template inline +void cv::Affine3::translation(const Vec3& t) +{ + matrix.val[3] = t[0]; matrix.val[7] = t[1]; matrix.val[11] = t[2]; +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::rotation() const +{ + return linear(); +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::linear() const +{ + typename cv::Affine3::Mat3 R; + R.val[0] = matrix.val[0]; R.val[1] = matrix.val[1]; R.val[2] = matrix.val[ 2]; + R.val[3] = matrix.val[4]; R.val[4] = matrix.val[5]; R.val[5] = matrix.val[ 6]; + R.val[6] = matrix.val[8]; R.val[7] = matrix.val[9]; R.val[8] = matrix.val[10]; + return R; +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::translation() const +{ + return Vec3(matrix.val[3], matrix.val[7], matrix.val[11]); +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::rvec() const +{ + cv::Vec3d w; + cv::Matx33d u, vt, R = rotation(); + cv::SVD::compute(R, w, u, vt, cv::SVD::FULL_UV + cv::SVD::MODIFY_A); + R = u * vt; + + double rx = R.val[7] - R.val[5]; + double ry = R.val[2] - R.val[6]; + double rz = R.val[3] - R.val[1]; + + double s = std::sqrt((rx*rx + ry*ry + rz*rz)*0.25); + double c = (R.val[0] + R.val[4] + R.val[8] - 1) * 0.5; + c = c > 1.0 ? 1.0 : c < -1.0 ? -1.0 : c; + double theta = acos(c); + + if( s < 1e-5 ) + { + if( c > 0 ) + rx = ry = rz = 0; + else + { + double t; + t = (R.val[0] + 1) * 0.5; + rx = std::sqrt(std::max(t, 0.0)); + t = (R.val[4] + 1) * 0.5; + ry = std::sqrt(std::max(t, 0.0)) * (R.val[1] < 0 ? -1.0 : 1.0); + t = (R.val[8] + 1) * 0.5; + rz = std::sqrt(std::max(t, 0.0)) * (R.val[2] < 0 ? -1.0 : 1.0); + + if( fabs(rx) < fabs(ry) && fabs(rx) < fabs(rz) && (R.val[5] > 0) != (ry*rz > 0) ) + rz = -rz; + theta /= std::sqrt(rx*rx + ry*ry + rz*rz); + rx *= theta; + ry *= theta; + rz *= theta; + } + } + else + { + double vth = 1/(2*s); + vth *= theta; + rx *= vth; ry *= vth; rz *= vth; + } + + return cv::Vec3d(rx, ry, rz); +} + +template inline +cv::Affine3 cv::Affine3::inv(int method) const +{ + return matrix.inv(method); +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Mat3& R) const +{ + Mat3 Lc = linear(); + Vec3 tc = translation(); + Mat4 result; + result.val[12] = result.val[13] = result.val[14] = 0; + result.val[15] = 1; + + for(int j = 0; j < 3; ++j) + { + for(int i = 0; i < 3; ++i) + { + float_type value = 0; + for(int k = 0; k < 3; ++k) + value += R(j, k) * Lc(k, i); + result(j, i) = value; + } + + result(j, 3) = R.row(j).dot(tc.t()); + } + return result; +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Vec3& _rvec) const +{ + return rotate(Affine3f(_rvec).rotation()); +} + +template inline +cv::Affine3 cv::Affine3::translate(const Vec3& t) const +{ + Mat4 m = matrix; + m.val[ 3] += t[0]; + m.val[ 7] += t[1]; + m.val[11] += t[2]; + return m; +} + +template inline +cv::Affine3 cv::Affine3::concatenate(const Affine3& affine) const +{ + return (*this).rotate(affine.rotation()).translate(affine.translation()); +} + +template template inline +cv::Affine3::operator Affine3() const +{ + return Affine3(matrix); +} + +template template inline +cv::Affine3 cv::Affine3::cast() const +{ + return Affine3(matrix); +} + +template inline +cv::Affine3 cv::operator*(const cv::Affine3& affine1, const cv::Affine3& affine2) +{ + return affine2.concatenate(affine1); +} + +template inline +V cv::operator*(const cv::Affine3& affine, const V& v) +{ + const typename Affine3::Mat4& m = affine.matrix; + + V r; + r.x = m.val[0] * v.x + m.val[1] * v.y + m.val[ 2] * v.z + m.val[ 3]; + r.y = m.val[4] * v.x + m.val[5] * v.y + m.val[ 6] * v.z + m.val[ 7]; + r.z = m.val[8] * v.x + m.val[9] * v.y + m.val[10] * v.z + m.val[11]; + return r; +} + +static inline +cv::Vec3f cv::operator*(const cv::Affine3f& affine, const cv::Vec3f& v) +{ + const cv::Matx44f& m = affine.matrix; + cv::Vec3f r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + +static inline +cv::Vec3d cv::operator*(const cv::Affine3d& affine, const cv::Vec3d& v) +{ + const cv::Matx44d& m = affine.matrix; + cv::Vec3d r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + + + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + cv::Mat(4, 4, cv::traits::Type::value, affine.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + Eigen::Transform a = affine; + cv::Mat(4, 4, cv::traits::Type::value, a.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + Eigen::Transform r; + cv::Mat hdr(4, 4, cv::traits::Type::value, r.matrix().data()); + cv::Mat(matrix, false).copyTo(hdr); + return r; +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + return this->operator Eigen::Transform(); +} + +#endif /* defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H */ + +//! @endcond + +#endif /* __cplusplus */ + +#endif /* OPENCV_CORE_AFFINE3_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/async.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/async.hpp new file mode 100644 index 0000000..54560c7 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/async.hpp @@ -0,0 +1,105 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ASYNC_HPP +#define OPENCV_CORE_ASYNC_HPP + +#include + +#ifdef CV_CXX11 +//#include +#include +#endif + +namespace cv { + +/** @addtogroup core_async + +@{ +*/ + + +/** @brief Returns result of asynchronous operations + +Object has attached asynchronous state. +Assignment operator doesn't clone asynchronous state (it is shared between all instances). + +Result can be fetched via get() method only once. + +*/ +class CV_EXPORTS_W AsyncArray +{ +public: + ~AsyncArray() CV_NOEXCEPT; + CV_WRAP AsyncArray() CV_NOEXCEPT; + AsyncArray(const AsyncArray& o) CV_NOEXCEPT; + AsyncArray& operator=(const AsyncArray& o) CV_NOEXCEPT; + CV_WRAP void release() CV_NOEXCEPT; + + /** Fetch the result. + @param[out] dst destination array + + Waits for result until container has valid result. + Throws exception if exception was stored as a result. + + Throws exception on invalid container state. + + @note Result or stored exception can be fetched only once. + */ + CV_WRAP void get(OutputArray dst) const; + + /** Retrieving the result with timeout + @param[out] dst destination array + @param[in] timeoutNs timeout in nanoseconds, -1 for infinite wait + + @returns true if result is ready, false if the timeout has expired + + @note Result or stored exception can be fetched only once. + */ + bool get(OutputArray dst, int64 timeoutNs) const; + + CV_WRAP inline + bool get(OutputArray dst, double timeoutNs) const { return get(dst, (int64)timeoutNs); } + + bool wait_for(int64 timeoutNs) const; + + CV_WRAP inline + bool wait_for(double timeoutNs) const { return wait_for((int64)timeoutNs); } + + CV_WRAP bool valid() const CV_NOEXCEPT; + +#ifdef CV_CXX11 + inline AsyncArray(AsyncArray&& o) { p = o.p; o.p = NULL; } + inline AsyncArray& operator=(AsyncArray&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; } + + template + inline bool get(OutputArray dst, const std::chrono::duration<_Rep, _Period>& timeout) + { + return get(dst, (int64)(std::chrono::nanoseconds(timeout).count())); + } + + template + inline bool wait_for(const std::chrono::duration<_Rep, _Period>& timeout) + { + return wait_for((int64)(std::chrono::nanoseconds(timeout).count())); + } + +#if 0 + std::future getFutureMat() const; + std::future getFutureUMat() const; +#endif +#endif + + + // PImpl + struct Impl; friend struct Impl; + inline void* _getImpl() const CV_NOEXCEPT { return p; } +protected: + Impl* p; +}; + + +//! @} +} // namespace +#endif // OPENCV_CORE_ASYNC_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/base.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/base.hpp new file mode 100644 index 0000000..1250497 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/base.hpp @@ -0,0 +1,722 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2014, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_BASE_HPP +#define OPENCV_CORE_BASE_HPP + +#ifndef __cplusplus +# error base.hpp header must be compiled as C++ +#endif + +#include "opencv2/opencv_modules.hpp" + +#include +#include + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +namespace Error { +//! error codes +enum Code { + StsOk= 0, //!< everything is ok + StsBackTrace= -1, //!< pseudo error for back trace + StsError= -2, //!< unknown /unspecified error + StsInternal= -3, //!< internal error (bad state) + StsNoMem= -4, //!< insufficient memory + StsBadArg= -5, //!< function arg/param is bad + StsBadFunc= -6, //!< unsupported function + StsNoConv= -7, //!< iteration didn't converge + StsAutoTrace= -8, //!< tracing + HeaderIsNull= -9, //!< image header is NULL + BadImageSize= -10, //!< image size is invalid + BadOffset= -11, //!< offset is invalid + BadDataPtr= -12, //!< + BadStep= -13, //!< image step is wrong, this may happen for a non-continuous matrix. + BadModelOrChSeq= -14, //!< + BadNumChannels= -15, //!< bad number of channels, for example, some functions accept only single channel matrices. + BadNumChannel1U= -16, //!< + BadDepth= -17, //!< input image depth is not supported by the function + BadAlphaChannel= -18, //!< + BadOrder= -19, //!< number of dimensions is out of range + BadOrigin= -20, //!< incorrect input origin + BadAlign= -21, //!< incorrect input align + BadCallBack= -22, //!< + BadTileSize= -23, //!< + BadCOI= -24, //!< input COI is not supported + BadROISize= -25, //!< incorrect input roi + MaskIsTiled= -26, //!< + StsNullPtr= -27, //!< null pointer + StsVecLengthErr= -28, //!< incorrect vector length + StsFilterStructContentErr= -29, //!< incorrect filter structure content + StsKernelStructContentErr= -30, //!< incorrect transform kernel content + StsFilterOffsetErr= -31, //!< incorrect filter offset value + StsBadSize= -201, //!< the input/output structure size is incorrect + StsDivByZero= -202, //!< division by zero + StsInplaceNotSupported= -203, //!< in-place operation is not supported + StsObjectNotFound= -204, //!< request can't be completed + StsUnmatchedFormats= -205, //!< formats of input/output arrays differ + StsBadFlag= -206, //!< flag is wrong or not supported + StsBadPoint= -207, //!< bad CvPoint + StsBadMask= -208, //!< bad format of mask (neither 8uC1 nor 8sC1) + StsUnmatchedSizes= -209, //!< sizes of input/output structures do not match + StsUnsupportedFormat= -210, //!< the data format/type is not supported by the function + StsOutOfRange= -211, //!< some of parameters are out of range + StsParseError= -212, //!< invalid syntax/structure of the parsed file + StsNotImplemented= -213, //!< the requested function/feature is not implemented + StsBadMemBlock= -214, //!< an allocated block has been corrupted + StsAssert= -215, //!< assertion failed + GpuNotSupported= -216, //!< no CUDA support + GpuApiCallError= -217, //!< GPU API call error + OpenGlNotSupported= -218, //!< no OpenGL support + OpenGlApiCallError= -219, //!< OpenGL API call error + OpenCLApiCallError= -220, //!< OpenCL API call error + OpenCLDoubleNotSupported= -221, + OpenCLInitError= -222, //!< OpenCL initialization error + OpenCLNoAMDBlasFft= -223 +}; +} //Error + +//! @} core_utils + +//! @addtogroup core_array +//! @{ + +//! matrix decomposition types +enum DecompTypes { + /** Gaussian elimination with the optimal pivot element chosen. */ + DECOMP_LU = 0, + /** singular value decomposition (SVD) method; the system can be over-defined and/or the matrix + src1 can be singular */ + DECOMP_SVD = 1, + /** eigenvalue decomposition; the matrix src1 must be symmetrical */ + DECOMP_EIG = 2, + /** Cholesky \f$LL^T\f$ factorization; the matrix src1 must be symmetrical and positively + defined */ + DECOMP_CHOLESKY = 3, + /** QR factorization; the system can be over-defined and/or the matrix src1 can be singular */ + DECOMP_QR = 4, + /** while all the previous flags are mutually exclusive, this flag can be used together with + any of the previous; it means that the normal equations + \f$\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}\f$ are + solved instead of the original system + \f$\texttt{src1}\cdot\texttt{dst}=\texttt{src2}\f$ */ + DECOMP_NORMAL = 16 +}; + +/** norm types + +src1 and src2 denote input arrays. +*/ + +enum NormTypes { + /** + \f[ + norm = \forkthree + {\|\texttt{src1}\|_{L_{\infty}} = \max _I | \texttt{src1} (I)|}{if \(\texttt{normType} = \texttt{NORM_INF}\) } + {\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}} = \max _I | \texttt{src1} (I) - \texttt{src2} (I)|}{if \(\texttt{normType} = \texttt{NORM_INF}\) } + {\frac{\|\texttt{src1}-\texttt{src2}\|_{L_{\infty}} }{\|\texttt{src2}\|_{L_{\infty}} }}{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_INF}\) } + \f] + */ + NORM_INF = 1, + /** + \f[ + norm = \forkthree + {\| \texttt{src1} \| _{L_1} = \sum _I | \texttt{src1} (I)|}{if \(\texttt{normType} = \texttt{NORM_L1}\)} + { \| \texttt{src1} - \texttt{src2} \| _{L_1} = \sum _I | \texttt{src1} (I) - \texttt{src2} (I)|}{if \(\texttt{normType} = \texttt{NORM_L1}\) } + { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_1} }{\|\texttt{src2}\|_{L_1}} }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L1}\) } + \f]*/ + NORM_L1 = 2, + /** + \f[ + norm = \forkthree + { \| \texttt{src1} \| _{L_2} = \sqrt{\sum_I \texttt{src1}(I)^2} }{if \(\texttt{normType} = \texttt{NORM_L2}\) } + { \| \texttt{src1} - \texttt{src2} \| _{L_2} = \sqrt{\sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2} }{if \(\texttt{normType} = \texttt{NORM_L2}\) } + { \frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}} }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2}\) } + \f] + */ + NORM_L2 = 4, + /** + \f[ + norm = \forkthree + { \| \texttt{src1} \| _{L_2} ^{2} = \sum_I \texttt{src1}(I)^2} {if \(\texttt{normType} = \texttt{NORM_L2SQR}\)} + { \| \texttt{src1} - \texttt{src2} \| _{L_2} ^{2} = \sum_I (\texttt{src1}(I) - \texttt{src2}(I))^2 }{if \(\texttt{normType} = \texttt{NORM_L2SQR}\) } + { \left(\frac{\|\texttt{src1}-\texttt{src2}\|_{L_2} }{\|\texttt{src2}\|_{L_2}}\right)^2 }{if \(\texttt{normType} = \texttt{NORM_RELATIVE | NORM_L2SQR}\) } + \f] + */ + NORM_L2SQR = 5, + /** + In the case of one input array, calculates the Hamming distance of the array from zero, + In the case of two input arrays, calculates the Hamming distance between the arrays. + */ + NORM_HAMMING = 6, + /** + Similar to NORM_HAMMING, but in the calculation, each two bits of the input sequence will + be added and treated as a single bit to be used in the same calculation as NORM_HAMMING. + */ + NORM_HAMMING2 = 7, + NORM_TYPE_MASK = 7, //!< bit-mask which can be used to separate norm type from norm flags + NORM_RELATIVE = 8, //!< flag + NORM_MINMAX = 32 //!< flag + }; + +//! comparison types +enum CmpTypes { CMP_EQ = 0, //!< src1 is equal to src2. + CMP_GT = 1, //!< src1 is greater than src2. + CMP_GE = 2, //!< src1 is greater than or equal to src2. + CMP_LT = 3, //!< src1 is less than src2. + CMP_LE = 4, //!< src1 is less than or equal to src2. + CMP_NE = 5 //!< src1 is unequal to src2. + }; + +//! generalized matrix multiplication flags +enum GemmFlags { GEMM_1_T = 1, //!< transposes src1 + GEMM_2_T = 2, //!< transposes src2 + GEMM_3_T = 4 //!< transposes src3 + }; + +enum DftFlags { + /** performs an inverse 1D or 2D transform instead of the default forward + transform. */ + DFT_INVERSE = 1, + /** scales the result: divide it by the number of array elements. Normally, it is + combined with DFT_INVERSE. */ + DFT_SCALE = 2, + /** performs a forward or inverse transform of every individual row of the input + matrix; this flag enables you to transform multiple vectors simultaneously and can be used to + decrease the overhead (which is sometimes several times larger than the processing itself) to + perform 3D and higher-dimensional transformations and so forth.*/ + DFT_ROWS = 4, + /** performs a forward transformation of 1D or 2D real array; the result, + though being a complex array, has complex-conjugate symmetry (*CCS*, see the function + description below for details), and such an array can be packed into a real array of the same + size as input, which is the fastest option and which is what the function does by default; + however, you may wish to get a full complex array (for simpler spectrum analysis, and so on) - + pass the flag to enable the function to produce a full-size complex output array. */ + DFT_COMPLEX_OUTPUT = 16, + /** performs an inverse transformation of a 1D or 2D complex array; the + result is normally a complex array of the same size, however, if the input array has + conjugate-complex symmetry (for example, it is a result of forward transformation with + DFT_COMPLEX_OUTPUT flag), the output is a real array; while the function itself does not + check whether the input is symmetrical or not, you can pass the flag and then the function + will assume the symmetry and produce the real output array (note that when the input is packed + into a real array and inverse transformation is executed, the function treats the input as a + packed complex-conjugate symmetrical array, and the output will also be a real array). */ + DFT_REAL_OUTPUT = 32, + /** specifies that input is complex input. If this flag is set, the input must have 2 channels. + On the other hand, for backwards compatibility reason, if input has 2 channels, input is + already considered complex. */ + DFT_COMPLEX_INPUT = 64, + /** performs an inverse 1D or 2D transform instead of the default forward transform. */ + DCT_INVERSE = DFT_INVERSE, + /** performs a forward or inverse transform of every individual row of the input + matrix. This flag enables you to transform multiple vectors simultaneously and can be used to + decrease the overhead (which is sometimes several times larger than the processing itself) to + perform 3D and higher-dimensional transforms and so forth.*/ + DCT_ROWS = DFT_ROWS +}; + +//! Various border types, image boundaries are denoted with `|` +//! @see borderInterpolate, copyMakeBorder +enum BorderTypes { + BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i` + BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh` + BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb` + BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg` + BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba` + BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno` + + BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_ISOLATED = 16 //!< do not look outside of ROI +}; + +//! @} core_array + +//! @addtogroup core_utils +//! @{ + +/*! @brief Signals an error and raises the exception. + +By default the function prints information about the error to stderr, +then it either stops if setBreakOnError() had been called before or raises the exception. +It is possible to alternate error processing by using redirectError(). +@param _code - error code (Error::Code) +@param _err - error description +@param _func - function name. Available only when the compiler supports getting it +@param _file - source file name where the error has occurred +@param _line - line number in the source file where the error has occurred +@see CV_Error, CV_Error_, CV_Assert, CV_DbgAssert + */ +CV_EXPORTS void error(int _code, const String& _err, const char* _func, const char* _file, int _line); + +#ifdef __GNUC__ +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Winvalid-noreturn" +# endif +#endif + +/** same as cv::error, but does not return */ +CV_INLINE CV_NORETURN void errorNoReturn(int _code, const String& _err, const char* _func, const char* _file, int _line) +{ + error(_code, _err, _func, _file, _line); +#ifdef __GNUC__ +# if !defined __clang__ && !defined __APPLE__ + // this suppresses this warning: "noreturn" function does return [enabled by default] + __builtin_trap(); + // or use infinite loop: for (;;) {} +# endif +#endif +} +#ifdef __GNUC__ +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic pop +# endif +#endif + +#ifdef CV_STATIC_ANALYSIS + +// In practice, some macro are not processed correctly (noreturn is not detected). +// We need to use simplified definition for them. +#define CV_Error(code, msg) do { (void)(code); (void)(msg); abort(); } while (0) +#define CV_Error_(code, args) do { (void)(code); (void)(cv::format args); abort(); } while (0) +#define CV_Assert( expr ) do { if (!(expr)) abort(); } while (0) +#define CV_ErrorNoReturn CV_Error +#define CV_ErrorNoReturn_ CV_Error_ + +#else // CV_STATIC_ANALYSIS + +/** @brief Call the error handler. + +Currently, the error handler prints the error code and the error message to the standard +error stream `stderr`. In the Debug configuration, it then provokes memory access violation, so that +the execution stack and all the parameters can be analyzed by the debugger. In the Release +configuration, the exception is thrown. + +@param code one of Error::Code +@param msg error message +*/ +#define CV_Error( code, msg ) cv::error( code, msg, CV_Func, __FILE__, __LINE__ ) + +/** @brief Call the error handler. + +This macro can be used to construct an error message on-fly to include some dynamic information, +for example: +@code + // note the extra parentheses around the formatted text message + CV_Error_(Error::StsOutOfRange, + ("the value at (%d, %d)=%g is out of range", badPt.x, badPt.y, badValue)); +@endcode +@param code one of Error::Code +@param args printf-like formatted error message in parentheses +*/ +#define CV_Error_( code, args ) cv::error( code, cv::format args, CV_Func, __FILE__, __LINE__ ) + +/** @brief Checks a condition at runtime and throws exception if it fails + +The macros CV_Assert (and CV_DbgAssert(expr)) evaluate the specified expression. If it is 0, the macros +raise an error (see cv::error). The macro CV_Assert checks the condition in both Debug and Release +configurations while CV_DbgAssert is only retained in the Debug configuration. +*/ +#define CV_Assert( expr ) do { if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) + +//! @cond IGNORED +#define CV__ErrorNoReturn( code, msg ) cv::errorNoReturn( code, msg, CV_Func, __FILE__, __LINE__ ) +#define CV__ErrorNoReturn_( code, args ) cv::errorNoReturn( code, cv::format args, CV_Func, __FILE__, __LINE__ ) +#ifdef __OPENCV_BUILD +#undef CV_Error +#define CV_Error CV__ErrorNoReturn +#undef CV_Error_ +#define CV_Error_ CV__ErrorNoReturn_ +#undef CV_Assert +#define CV_Assert( expr ) do { if(!!(expr)) ; else cv::errorNoReturn( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) +#else +// backward compatibility +#define CV_ErrorNoReturn CV__ErrorNoReturn +#define CV_ErrorNoReturn_ CV__ErrorNoReturn_ +#endif +//! @endcond + +#endif // CV_STATIC_ANALYSIS + +//! @cond IGNORED + +#if defined OPENCV_FORCE_MULTIARG_ASSERT_CHECK && defined CV_STATIC_ANALYSIS +#warning "OPENCV_FORCE_MULTIARG_ASSERT_CHECK can't be used with CV_STATIC_ANALYSIS" +#undef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#endif + +#ifdef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#define CV_Assert_1( expr ) do { if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ); } while(0) +#else +#define CV_Assert_1 CV_Assert +#endif +#define CV_Assert_2( expr1, expr2 ) CV_Assert_1(expr1); CV_Assert_1(expr2) +#define CV_Assert_3( expr1, expr2, expr3 ) CV_Assert_2(expr1, expr2); CV_Assert_1(expr3) +#define CV_Assert_4( expr1, expr2, expr3, expr4 ) CV_Assert_3(expr1, expr2, expr3); CV_Assert_1(expr4) +#define CV_Assert_5( expr1, expr2, expr3, expr4, expr5 ) CV_Assert_4(expr1, expr2, expr3, expr4); CV_Assert_1(expr5) +#define CV_Assert_6( expr1, expr2, expr3, expr4, expr5, expr6 ) CV_Assert_5(expr1, expr2, expr3, expr4, expr5); CV_Assert_1(expr6) +#define CV_Assert_7( expr1, expr2, expr3, expr4, expr5, expr6, expr7 ) CV_Assert_6(expr1, expr2, expr3, expr4, expr5, expr6 ); CV_Assert_1(expr7) +#define CV_Assert_8( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8 ) CV_Assert_7(expr1, expr2, expr3, expr4, expr5, expr6, expr7 ); CV_Assert_1(expr8) +#define CV_Assert_9( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9 ) CV_Assert_8(expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8 ); CV_Assert_1(expr9) +#define CV_Assert_10( expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9, expr10 ) CV_Assert_9(expr1, expr2, expr3, expr4, expr5, expr6, expr7, expr8, expr9 ); CV_Assert_1(expr10) + +#define CV_Assert_N(...) do { __CV_CAT(CV_Assert_, __CV_VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__); } while(0) + +#ifdef OPENCV_FORCE_MULTIARG_ASSERT_CHECK +#undef CV_Assert +#define CV_Assert CV_Assert_N +#endif +//! @endcond + +#if defined _DEBUG || defined CV_STATIC_ANALYSIS +# define CV_DbgAssert(expr) CV_Assert(expr) +#else +/** replaced with CV_Assert(expr) in Debug configuration */ +# define CV_DbgAssert(expr) +#endif + +/* + * Hamming distance functor - counts the bit differences between two strings - useful for the Brief descriptor + * bit count of A exclusive XOR'ed with B + */ +struct CV_EXPORTS Hamming +{ + enum { normType = NORM_HAMMING }; + typedef unsigned char ValueType; + typedef int ResultType; + + /** this will count the bits in a ^ b + */ + ResultType operator()( const unsigned char* a, const unsigned char* b, int size ) const; +}; + +typedef Hamming HammingLUT; + +/////////////////////////////////// inline norms //////////////////////////////////// + +template inline _Tp cv_abs(_Tp x) { return std::abs(x); } +inline int cv_abs(uchar x) { return x; } +inline int cv_abs(schar x) { return std::abs(x); } +inline int cv_abs(ushort x) { return x; } +inline int cv_abs(short x) { return std::abs(x); } + +template static inline +_AccTp normL2Sqr(const _Tp* a, int n) +{ + _AccTp s = 0; + int i=0; +#if CV_ENABLE_UNROLLED + for( ; i <= n - 4; i += 4 ) + { + _AccTp v0 = a[i], v1 = a[i+1], v2 = a[i+2], v3 = a[i+3]; + s += v0*v0 + v1*v1 + v2*v2 + v3*v3; + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = a[i]; + s += v*v; + } + return s; +} + +template static inline +_AccTp normL1(const _Tp* a, int n) +{ + _AccTp s = 0; + int i = 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + s += (_AccTp)cv_abs(a[i]) + (_AccTp)cv_abs(a[i+1]) + + (_AccTp)cv_abs(a[i+2]) + (_AccTp)cv_abs(a[i+3]); + } +#endif + for( ; i < n; i++ ) + s += cv_abs(a[i]); + return s; +} + +template static inline +_AccTp normInf(const _Tp* a, int n) +{ + _AccTp s = 0; + for( int i = 0; i < n; i++ ) + s = std::max(s, (_AccTp)cv_abs(a[i])); + return s; +} + +template static inline +_AccTp normL2Sqr(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + int i= 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + _AccTp v0 = _AccTp(a[i] - b[i]), v1 = _AccTp(a[i+1] - b[i+1]), v2 = _AccTp(a[i+2] - b[i+2]), v3 = _AccTp(a[i+3] - b[i+3]); + s += v0*v0 + v1*v1 + v2*v2 + v3*v3; + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = _AccTp(a[i] - b[i]); + s += v*v; + } + return s; +} + +static inline float normL2Sqr(const float* a, const float* b, int n) +{ + float s = 0.f; + for( int i = 0; i < n; i++ ) + { + float v = a[i] - b[i]; + s += v*v; + } + return s; +} + +template static inline +_AccTp normL1(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + int i= 0; +#if CV_ENABLE_UNROLLED + for(; i <= n - 4; i += 4 ) + { + _AccTp v0 = _AccTp(a[i] - b[i]), v1 = _AccTp(a[i+1] - b[i+1]), v2 = _AccTp(a[i+2] - b[i+2]), v3 = _AccTp(a[i+3] - b[i+3]); + s += std::abs(v0) + std::abs(v1) + std::abs(v2) + std::abs(v3); + } +#endif + for( ; i < n; i++ ) + { + _AccTp v = _AccTp(a[i] - b[i]); + s += std::abs(v); + } + return s; +} + +inline float normL1(const float* a, const float* b, int n) +{ + float s = 0.f; + for( int i = 0; i < n; i++ ) + { + s += std::abs(a[i] - b[i]); + } + return s; +} + +inline int normL1(const uchar* a, const uchar* b, int n) +{ + int s = 0; + for( int i = 0; i < n; i++ ) + { + s += std::abs(a[i] - b[i]); + } + return s; +} + +template static inline +_AccTp normInf(const _Tp* a, const _Tp* b, int n) +{ + _AccTp s = 0; + for( int i = 0; i < n; i++ ) + { + _AccTp v0 = a[i] - b[i]; + s = std::max(s, std::abs(v0)); + } + return s; +} + +/** @brief Computes the cube root of an argument. + + The function cubeRoot computes \f$\sqrt[3]{\texttt{val}}\f$. Negative arguments are handled correctly. + NaN and Inf are not handled. The accuracy approaches the maximum possible accuracy for + single-precision data. + @param val A function argument. + */ +CV_EXPORTS_W float cubeRoot(float val); + +/** @overload + +cubeRoot with argument of `double` type calls `std::cbrt(double)` (C++11) or falls back on `pow()` for C++98 compilation mode. +*/ +static inline +double cubeRoot(double val) +{ +#ifdef CV_CXX11 + return std::cbrt(val); +#else + double v = pow(abs(val), 1/3.); // pow doesn't support negative inputs with fractional exponents + return val >= 0 ? v : -v; +#endif +} + +/** @brief Calculates the angle of a 2D vector in degrees. + + The function fastAtan2 calculates the full-range angle of an input 2D vector. The angle is measured + in degrees and varies from 0 to 360 degrees. The accuracy is about 0.3 degrees. + @param x x-coordinate of the vector. + @param y y-coordinate of the vector. + */ +CV_EXPORTS_W float fastAtan2(float y, float x); + +/** proxy for hal::LU */ +CV_EXPORTS int LU(float* A, size_t astep, int m, float* b, size_t bstep, int n); +/** proxy for hal::LU */ +CV_EXPORTS int LU(double* A, size_t astep, int m, double* b, size_t bstep, int n); +/** proxy for hal::Cholesky */ +CV_EXPORTS bool Cholesky(float* A, size_t astep, int m, float* b, size_t bstep, int n); +/** proxy for hal::Cholesky */ +CV_EXPORTS bool Cholesky(double* A, size_t astep, int m, double* b, size_t bstep, int n); + +////////////////// forward declarations for important OpenCV types ////////////////// + +//! @cond IGNORED + +template class Vec; +template class Matx; + +template class Complex; +template class Point_; +template class Point3_; +template class Size_; +template class Rect_; +template class Scalar_; + +class CV_EXPORTS RotatedRect; +class CV_EXPORTS Range; +class CV_EXPORTS TermCriteria; +class CV_EXPORTS KeyPoint; +class CV_EXPORTS DMatch; +class CV_EXPORTS RNG; + +class CV_EXPORTS Mat; +class CV_EXPORTS MatExpr; + +class CV_EXPORTS UMat; + +class CV_EXPORTS SparseMat; +typedef Mat MatND; + +template class Mat_; +template class SparseMat_; + +class CV_EXPORTS MatConstIterator; +class CV_EXPORTS SparseMatIterator; +class CV_EXPORTS SparseMatConstIterator; +template class MatIterator_; +template class MatConstIterator_; +template class SparseMatIterator_; +template class SparseMatConstIterator_; + +namespace ogl +{ + class CV_EXPORTS Buffer; + class CV_EXPORTS Texture2D; + class CV_EXPORTS Arrays; +} + +namespace cuda +{ + class CV_EXPORTS GpuMat; + class CV_EXPORTS HostMem; + class CV_EXPORTS Stream; + class CV_EXPORTS Event; +} + +namespace cudev +{ + template class GpuMat_; +} + +namespace ipp +{ +#if OPENCV_ABI_COMPATIBILITY > 300 +CV_EXPORTS unsigned long long getIppFeatures(); +#else +CV_EXPORTS int getIppFeatures(); +#endif +CV_EXPORTS void setIppStatus(int status, const char * const funcname = NULL, const char * const filename = NULL, + int line = 0); +CV_EXPORTS int getIppStatus(); +CV_EXPORTS String getIppErrorLocation(); +CV_EXPORTS_W bool useIPP(); +CV_EXPORTS_W void setUseIPP(bool flag); +CV_EXPORTS_W String getIppVersion(); + +// IPP Not-Exact mode. This function may force use of IPP then both IPP and OpenCV provide proper results +// but have internal accuracy differences which have too much direct or indirect impact on accuracy tests. +CV_EXPORTS_W bool useIPP_NotExact(); +CV_EXPORTS_W void setUseIPP_NotExact(bool flag); +#if OPENCV_ABI_COMPATIBILITY < 400 +CV_EXPORTS_W bool useIPP_NE(); +CV_EXPORTS_W void setUseIPP_NE(bool flag); +#endif + +} // ipp + +//! @endcond + +//! @} core_utils + + + + +} // cv + +#include "opencv2/core/neon_utils.hpp" +#include "opencv2/core/vsx_utils.hpp" +#include "opencv2/core/check.hpp" + +#endif //OPENCV_CORE_BASE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/bindings_utils.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/bindings_utils.hpp new file mode 100644 index 0000000..98a4a2b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/bindings_utils.hpp @@ -0,0 +1,170 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_BINDINGS_UTILS_HPP +#define OPENCV_CORE_BINDINGS_UTILS_HPP + +#include +#include +#include + +#include + +namespace cv { namespace utils { +//! @addtogroup core_utils +//! @{ + +CV_EXPORTS_W String dumpInputArray(InputArray argument); + +CV_EXPORTS_W String dumpInputArrayOfArrays(InputArrayOfArrays argument); + +CV_EXPORTS_W String dumpInputOutputArray(InputOutputArray argument); + +CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argument); + +CV_WRAP static inline +String dumpBool(bool argument) +{ + return (argument) ? String("Bool: True") : String("Bool: False"); +} + +CV_WRAP static inline +String dumpInt(int argument) +{ + return cv::format("Int: %d", argument); +} + +CV_WRAP static inline +String dumpSizeT(size_t argument) +{ + std::ostringstream oss("size_t: ", std::ios::ate); + oss << argument; + return oss.str(); +} + +CV_WRAP static inline +String dumpFloat(float argument) +{ + return cv::format("Float: %.2f", argument); +} + +CV_WRAP static inline +String dumpDouble(double argument) +{ + return cv::format("Double: %.2f", argument); +} + +CV_WRAP static inline +String dumpCString(const char* argument) +{ + return cv::format("String: %s", argument); +} + +CV_WRAP static inline +String dumpString(const String& argument) +{ + return cv::format("String: %s", argument.c_str()); +} + +CV_WRAP static inline +String testOverloadResolution(int value, const Point& point = Point(42, 24)) +{ + return format("overload (int=%d, point=(x=%d, y=%d))", value, point.x, + point.y); +} + +CV_WRAP static inline +String testOverloadResolution(const Rect& rect) +{ + return format("overload (rect=(x=%d, y=%d, w=%d, h=%d))", rect.x, rect.y, + rect.width, rect.height); +} + +CV_WRAP static inline +String dumpRect(const Rect& argument) +{ + return format("rect: (x=%d, y=%d, w=%d, h=%d)", argument.x, argument.y, + argument.width, argument.height); +} + +CV_WRAP static inline +String dumpTermCriteria(const TermCriteria& argument) +{ + return format("term_criteria: (type=%d, max_count=%d, epsilon=%lf", + argument.type, argument.maxCount, argument.epsilon); +} + +CV_WRAP static inline +String dumpRotatedRect(const RotatedRect& argument) +{ + return format("rotated_rect: (c_x=%f, c_y=%f, w=%f, h=%f, a=%f)", + argument.center.x, argument.center.y, argument.size.width, + argument.size.height, argument.angle); +} + +CV_WRAP static inline +String dumpRange(const Range& argument) +{ + if (argument == Range::all()) + { + return "range: all"; + } + else + { + return format("range: (s=%d, e=%d)", argument.start, argument.end); + } +} + +CV_WRAP static inline +void testRaiseGeneralException() +{ + throw std::runtime_error("exception text"); +} + +CV_WRAP static inline +AsyncArray testAsyncArray(InputArray argument) +{ + AsyncPromise p; + p.setValue(argument); + return p.getArrayResult(); +} + +CV_WRAP static inline +AsyncArray testAsyncException() +{ + AsyncPromise p; + try + { + CV_Error(Error::StsOk, "Test: Generated async error"); + } + catch (const cv::Exception& e) + { + p.setException(e); + } + return p.getArrayResult(); +} + +//! @} // core_utils +} // namespace cv::utils + +//! @cond IGNORED + +CV_WRAP static inline +int setLogLevel(int level) +{ + // NB: Binding generators doesn't work with enums properly yet, so we define separate overload here + return cv::utils::logging::setLogLevel((cv::utils::logging::LogLevel)level); +} + +CV_WRAP static inline +int getLogLevel() +{ + return cv::utils::logging::getLogLevel(); +} + +//! @endcond IGNORED + +} // namespaces cv / utils + +#endif // OPENCV_CORE_BINDINGS_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/bufferpool.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/bufferpool.hpp new file mode 100644 index 0000000..4698e5d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/bufferpool.hpp @@ -0,0 +1,40 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved. + +#ifndef OPENCV_CORE_BUFFER_POOL_HPP +#define OPENCV_CORE_BUFFER_POOL_HPP + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4265) +#endif + +namespace cv +{ + +//! @addtogroup core +//! @{ + +class BufferPoolController +{ +protected: + ~BufferPoolController() { } +public: + virtual size_t getReservedSize() const = 0; + virtual size_t getMaxReservedSize() const = 0; + virtual void setMaxReservedSize(size_t size) = 0; + virtual void freeAllReservedBuffers() = 0; +}; + +//! @} + +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // OPENCV_CORE_BUFFER_POOL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/check.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/check.hpp new file mode 100644 index 0000000..d975223 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/check.hpp @@ -0,0 +1,160 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_CHECK_HPP +#define OPENCV_CORE_CHECK_HPP + +#include + +namespace cv { + +/** Returns string of cv::Mat depth value: CV_8U -> "CV_8U" or "" */ +CV_EXPORTS const char* depthToString(int depth); + +/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or "" */ +CV_EXPORTS const String typeToString(int type); + + +//! @cond IGNORED +namespace detail { + +/** Returns string of cv::Mat depth value: CV_8U -> "CV_8U" or NULL */ +CV_EXPORTS const char* depthToString_(int depth); + +/** Returns string of cv::Mat depth value: CV_8UC3 -> "CV_8UC3" or cv::String() */ +CV_EXPORTS const cv::String typeToString_(int type); + +enum TestOp { + TEST_CUSTOM = 0, + TEST_EQ = 1, + TEST_NE = 2, + TEST_LE = 3, + TEST_LT = 4, + TEST_GE = 5, + TEST_GT = 6, + CV__LAST_TEST_OP +}; + +struct CheckContext { + const char* func; + const char* file; + int line; + enum TestOp testOp; + const char* message; + const char* p1_str; + const char* p2_str; +}; + +#ifndef CV__CHECK_FILENAME +# define CV__CHECK_FILENAME __FILE__ +#endif + +#ifndef CV__CHECK_FUNCTION +# if defined _MSC_VER +# define CV__CHECK_FUNCTION __FUNCSIG__ +# elif defined __GNUC__ +# define CV__CHECK_FUNCTION __PRETTY_FUNCTION__ +# else +# define CV__CHECK_FUNCTION "" +# endif +#endif + +#define CV__CHECK_LOCATION_VARNAME(id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_check_, id), __LINE__) +#define CV__DEFINE_CHECK_CONTEXT(id, message, testOp, p1_str, p2_str) \ + static const cv::detail::CheckContext CV__CHECK_LOCATION_VARNAME(id) = \ + { CV__CHECK_FUNCTION, CV__CHECK_FILENAME, __LINE__, testOp, "" message, "" p1_str, "" p2_str } + +CV_EXPORTS void CV_NORETURN check_failed_auto(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v1, const size_t v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const float v1, const float v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const double v1, const double v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v1, const Size_ v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v1, const int v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v1, const int v2, const CheckContext& ctx); + +CV_EXPORTS void CV_NORETURN check_failed_auto(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const float v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const double v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const std::string& v1, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v, const CheckContext& ctx); + + +#define CV__TEST_EQ(v1, v2) ((v1) == (v2)) +#define CV__TEST_NE(v1, v2) ((v1) != (v2)) +#define CV__TEST_LE(v1, v2) ((v1) <= (v2)) +#define CV__TEST_LT(v1, v2) ((v1) < (v2)) +#define CV__TEST_GE(v1, v2) ((v1) >= (v2)) +#define CV__TEST_GT(v1, v2) ((v1) > (v2)) + +#define CV__CHECK(id, op, type, v1, v2, v1_str, v2_str, msg_str) do { \ + if(CV__TEST_##op((v1), (v2))) ; else { \ + CV__DEFINE_CHECK_CONTEXT(id, msg_str, cv::detail::TEST_ ## op, v1_str, v2_str); \ + cv::detail::check_failed_ ## type((v1), (v2), CV__CHECK_LOCATION_VARNAME(id)); \ + } \ +} while (0) + +#define CV__CHECK_CUSTOM_TEST(id, type, v, test_expr, v_str, test_expr_str, msg_str) do { \ + if(!!(test_expr)) ; else { \ + CV__DEFINE_CHECK_CONTEXT(id, msg_str, cv::detail::TEST_CUSTOM, v_str, test_expr_str); \ + cv::detail::check_failed_ ## type((v), CV__CHECK_LOCATION_VARNAME(id)); \ + } \ +} while (0) + +} // namespace +//! @endcond + + +/// Supported values of these types: int, float, double +#define CV_CheckEQ(v1, v2, msg) CV__CHECK(_, EQ, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckNE(v1, v2, msg) CV__CHECK(_, NE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckLE(v1, v2, msg) CV__CHECK(_, LE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckLT(v1, v2, msg) CV__CHECK(_, LT, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckGE(v1, v2, msg) CV__CHECK(_, GE, auto, v1, v2, #v1, #v2, msg) +#define CV_CheckGT(v1, v2, msg) CV__CHECK(_, GT, auto, v1, v2, #v1, #v2, msg) + +/// Check with additional "decoding" of type values in error message +#define CV_CheckTypeEQ(t1, t2, msg) CV__CHECK(_, EQ, MatType, t1, t2, #t1, #t2, msg) +/// Check with additional "decoding" of depth values in error message +#define CV_CheckDepthEQ(d1, d2, msg) CV__CHECK(_, EQ, MatDepth, d1, d2, #d1, #d2, msg) + +#define CV_CheckChannelsEQ(c1, c2, msg) CV__CHECK(_, EQ, MatChannels, c1, c2, #c1, #c2, msg) + +/// Example: type == CV_8UC1 || type == CV_8UC3 +#define CV_CheckType(t, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, MatType, t, (test_expr), #t, #test_expr, msg) + +/// Example: depth == CV_32F || depth == CV_64F +#define CV_CheckDepth(t, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, MatDepth, t, (test_expr), #t, #test_expr, msg) + +/// Example: v == A || v == B +#define CV_Check(v, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, auto, v, (test_expr), #v, #test_expr, msg) + +/// Some complex conditions: CV_Check(src2, src2.empty() || (src2.type() == src1.type() && src2.size() == src1.size()), "src2 should have same size/type as src1") +// TODO define pretty-printers + +#ifndef NDEBUG +#define CV_DbgCheck(v, test_expr, msg) CV__CHECK_CUSTOM_TEST(_, auto, v, (test_expr), #v, #test_expr, msg) +#define CV_DbgCheckEQ(v1, v2, msg) CV__CHECK(_, EQ, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckNE(v1, v2, msg) CV__CHECK(_, NE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckLE(v1, v2, msg) CV__CHECK(_, LE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckLT(v1, v2, msg) CV__CHECK(_, LT, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckGE(v1, v2, msg) CV__CHECK(_, GE, auto, v1, v2, #v1, #v2, msg) +#define CV_DbgCheckGT(v1, v2, msg) CV__CHECK(_, GT, auto, v1, v2, #v1, #v2, msg) +#else +#define CV_DbgCheck(v, test_expr, msg) do { } while (0) +#define CV_DbgCheckEQ(v1, v2, msg) do { } while (0) +#define CV_DbgCheckNE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckLE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckLT(v1, v2, msg) do { } while (0) +#define CV_DbgCheckGE(v1, v2, msg) do { } while (0) +#define CV_DbgCheckGT(v1, v2, msg) do { } while (0) +#endif + +} // namespace + +#endif // OPENCV_CORE_CHECK_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/core.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/core.hpp new file mode 100644 index 0000000..4389183 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/core.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/core.hpp" diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/core_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/core_c.h new file mode 100644 index 0000000..95a98cf --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/core_c.h @@ -0,0 +1,3175 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + + +#ifndef OPENCV_CORE_C_H +#define OPENCV_CORE_C_H + +#include "opencv2/core/types_c.h" + +#ifdef __cplusplus +# ifdef _MSC_VER +/* disable warning C4190: 'function' has C-linkage specified, but returns UDT 'typename' + which is incompatible with C + + It is OK to disable it because we only extend few plain structures with + C++ constructors for simpler interoperability with C++ API of the library +*/ +# pragma warning(disable:4190) +# elif defined __clang__ && __clang_major__ >= 3 +# pragma GCC diagnostic ignored "-Wreturn-type-c-linkage" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup core_c + @{ +*/ + +/****************************************************************************************\ +* Array allocation, deallocation, initialization and access to elements * +\****************************************************************************************/ + +/** `malloc` wrapper. + If there is no enough memory, the function + (as well as other OpenCV functions that call cvAlloc) + raises an error. */ +CVAPI(void*) cvAlloc( size_t size ); + +/** `free` wrapper. + Here and further all the memory releasing functions + (that all call cvFree) take double pointer in order to + to clear pointer to the data after releasing it. + Passing pointer to NULL pointer is Ok: nothing happens in this case +*/ +CVAPI(void) cvFree_( void* ptr ); +#define cvFree(ptr) (cvFree_(*(ptr)), *(ptr)=0) + +/** @brief Creates an image header but does not allocate the image data. + +@param size Image width and height +@param depth Image depth (see cvCreateImage ) +@param channels Number of channels (see cvCreateImage ) + */ +CVAPI(IplImage*) cvCreateImageHeader( CvSize size, int depth, int channels ); + +/** @brief Initializes an image header that was previously allocated. + +The returned IplImage\* points to the initialized header. +@param image Image header to initialize +@param size Image width and height +@param depth Image depth (see cvCreateImage ) +@param channels Number of channels (see cvCreateImage ) +@param origin Top-left IPL_ORIGIN_TL or bottom-left IPL_ORIGIN_BL +@param align Alignment for image rows, typically 4 or 8 bytes + */ +CVAPI(IplImage*) cvInitImageHeader( IplImage* image, CvSize size, int depth, + int channels, int origin CV_DEFAULT(0), + int align CV_DEFAULT(4)); + +/** @brief Creates an image header and allocates the image data. + +This function call is equivalent to the following code: +@code + header = cvCreateImageHeader(size, depth, channels); + cvCreateData(header); +@endcode +@param size Image width and height +@param depth Bit depth of image elements. See IplImage for valid depths. +@param channels Number of channels per pixel. See IplImage for details. This function only creates +images with interleaved channels. + */ +CVAPI(IplImage*) cvCreateImage( CvSize size, int depth, int channels ); + +/** @brief Deallocates an image header. + +This call is an analogue of : +@code + if(image ) + { + iplDeallocate(*image, IPL_IMAGE_HEADER | IPL_IMAGE_ROI); + *image = 0; + } +@endcode +but it does not use IPL functions by default (see the CV_TURN_ON_IPL_COMPATIBILITY macro). +@param image Double pointer to the image header + */ +CVAPI(void) cvReleaseImageHeader( IplImage** image ); + +/** @brief Deallocates the image header and the image data. + +This call is a shortened form of : +@code + if(*image ) + { + cvReleaseData(*image); + cvReleaseImageHeader(image); + } +@endcode +@param image Double pointer to the image header +*/ +CVAPI(void) cvReleaseImage( IplImage** image ); + +/** Creates a copy of IPL image (widthStep may differ) */ +CVAPI(IplImage*) cvCloneImage( const IplImage* image ); + +/** @brief Sets the channel of interest in an IplImage. + +If the ROI is set to NULL and the coi is *not* 0, the ROI is allocated. Most OpenCV functions do +*not* support the COI setting, so to process an individual image/matrix channel one may copy (via +cvCopy or cvSplit) the channel to a separate image/matrix, process it and then copy the result +back (via cvCopy or cvMerge) if needed. +@param image A pointer to the image header +@param coi The channel of interest. 0 - all channels are selected, 1 - first channel is selected, +etc. Note that the channel indices become 1-based. + */ +CVAPI(void) cvSetImageCOI( IplImage* image, int coi ); + +/** @brief Returns the index of the channel of interest. + +Returns the channel of interest of in an IplImage. Returned values correspond to the coi in +cvSetImageCOI. +@param image A pointer to the image header + */ +CVAPI(int) cvGetImageCOI( const IplImage* image ); + +/** @brief Sets an image Region Of Interest (ROI) for a given rectangle. + +If the original image ROI was NULL and the rect is not the whole image, the ROI structure is +allocated. + +Most OpenCV functions support the use of ROI and treat the image rectangle as a separate image. For +example, all of the pixel coordinates are counted from the top-left (or bottom-left) corner of the +ROI, not the original image. +@param image A pointer to the image header +@param rect The ROI rectangle + */ +CVAPI(void) cvSetImageROI( IplImage* image, CvRect rect ); + +/** @brief Resets the image ROI to include the entire image and releases the ROI structure. + +This produces a similar result to the following, but in addition it releases the ROI structure. : +@code + cvSetImageROI(image, cvRect(0, 0, image->width, image->height )); + cvSetImageCOI(image, 0); +@endcode +@param image A pointer to the image header + */ +CVAPI(void) cvResetImageROI( IplImage* image ); + +/** @brief Returns the image ROI. + +If there is no ROI set, cvRect(0,0,image-\>width,image-\>height) is returned. +@param image A pointer to the image header + */ +CVAPI(CvRect) cvGetImageROI( const IplImage* image ); + +/** @brief Creates a matrix header but does not allocate the matrix data. + +The function allocates a new matrix header and returns a pointer to it. The matrix data can then be +allocated using cvCreateData or set explicitly to user-allocated data via cvSetData. +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type Type of the matrix elements, see cvCreateMat + */ +CVAPI(CvMat*) cvCreateMatHeader( int rows, int cols, int type ); + +#define CV_AUTOSTEP 0x7fffffff + +/** @brief Initializes a pre-allocated matrix header. + +This function is often used to process raw data with OpenCV matrix functions. For example, the +following code computes the matrix product of two matrices, stored as ordinary arrays: +@code + double a[] = { 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 }; + + double b[] = { 1, 5, 9, + 2, 6, 10, + 3, 7, 11, + 4, 8, 12 }; + + double c[9]; + CvMat Ma, Mb, Mc ; + + cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a); + cvInitMatHeader(&Mb, 4, 3, CV_64FC1, b); + cvInitMatHeader(&Mc, 3, 3, CV_64FC1, c); + + cvMatMulAdd(&Ma, &Mb, 0, &Mc); + // the c array now contains the product of a (3x4) and b (4x3) +@endcode +@param mat A pointer to the matrix header to be initialized +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type Type of the matrix elements, see cvCreateMat . +@param data Optional: data pointer assigned to the matrix header +@param step Optional: full row width in bytes of the assigned data. By default, the minimal +possible step is used which assumes there are no gaps between subsequent rows of the matrix. + */ +CVAPI(CvMat*) cvInitMatHeader( CvMat* mat, int rows, int cols, + int type, void* data CV_DEFAULT(NULL), + int step CV_DEFAULT(CV_AUTOSTEP) ); + +/** @brief Creates a matrix header and allocates the matrix data. + +The function call is equivalent to the following code: +@code + CvMat* mat = cvCreateMatHeader(rows, cols, type); + cvCreateData(mat); +@endcode +@param rows Number of rows in the matrix +@param cols Number of columns in the matrix +@param type The type of the matrix elements in the form +CV_\\C\ , where S=signed, U=unsigned, F=float. For +example, CV _ 8UC1 means the elements are 8-bit unsigned and the there is 1 channel, and CV _ +32SC2 means the elements are 32-bit signed and there are 2 channels. + */ +CVAPI(CvMat*) cvCreateMat( int rows, int cols, int type ); + +/** @brief Deallocates a matrix. + +The function decrements the matrix data reference counter and deallocates matrix header. If the data +reference counter is 0, it also deallocates the data. : +@code + if(*mat ) + cvDecRefData(*mat); + cvFree((void**)mat); +@endcode +@param mat Double pointer to the matrix + */ +CVAPI(void) cvReleaseMat( CvMat** mat ); + +/** @brief Decrements an array data reference counter. + +The function decrements the data reference counter in a CvMat or CvMatND if the reference counter + +pointer is not NULL. If the counter reaches zero, the data is deallocated. In the current +implementation the reference counter is not NULL only if the data was allocated using the +cvCreateData function. The counter will be NULL in other cases such as: external data was assigned +to the header using cvSetData, header is part of a larger matrix or image, or the header was +converted from an image or n-dimensional matrix header. +@param arr Pointer to an array header + */ +CV_INLINE void cvDecRefData( CvArr* arr ) +{ + if( CV_IS_MAT( arr )) + { + CvMat* mat = (CvMat*)arr; + mat->data.ptr = NULL; + if( mat->refcount != NULL && --*mat->refcount == 0 ) + cvFree( &mat->refcount ); + mat->refcount = NULL; + } + else if( CV_IS_MATND( arr )) + { + CvMatND* mat = (CvMatND*)arr; + mat->data.ptr = NULL; + if( mat->refcount != NULL && --*mat->refcount == 0 ) + cvFree( &mat->refcount ); + mat->refcount = NULL; + } +} + +/** @brief Increments array data reference counter. + +The function increments CvMat or CvMatND data reference counter and returns the new counter value if +the reference counter pointer is not NULL, otherwise it returns zero. +@param arr Array header + */ +CV_INLINE int cvIncRefData( CvArr* arr ) +{ + int refcount = 0; + if( CV_IS_MAT( arr )) + { + CvMat* mat = (CvMat*)arr; + if( mat->refcount != NULL ) + refcount = ++*mat->refcount; + } + else if( CV_IS_MATND( arr )) + { + CvMatND* mat = (CvMatND*)arr; + if( mat->refcount != NULL ) + refcount = ++*mat->refcount; + } + return refcount; +} + + +/** Creates an exact copy of the input matrix (except, may be, step value) */ +CVAPI(CvMat*) cvCloneMat( const CvMat* mat ); + + +/** @brief Returns matrix header corresponding to the rectangular sub-array of input image or matrix. + +The function returns header, corresponding to a specified rectangle of the input array. In other + +words, it allows the user to treat a rectangular part of input array as a stand-alone array. ROI is +taken into account by the function so the sub-array of ROI is actually extracted. +@param arr Input array +@param submat Pointer to the resultant sub-array header +@param rect Zero-based coordinates of the rectangle of interest + */ +CVAPI(CvMat*) cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect ); +#define cvGetSubArr cvGetSubRect + +/** @brief Returns array row or row span. + +The function returns the header, corresponding to a specified row/row span of the input array. +cvGetRow(arr, submat, row) is a shortcut for cvGetRows(arr, submat, row, row+1). +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param start_row Zero-based index of the starting row (inclusive) of the span +@param end_row Zero-based index of the ending row (exclusive) of the span +@param delta_row Index step in the row span. That is, the function extracts every delta_row -th +row from start_row and up to (but not including) end_row . + */ +CVAPI(CvMat*) cvGetRows( const CvArr* arr, CvMat* submat, + int start_row, int end_row, + int delta_row CV_DEFAULT(1)); + +/** @overload +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param row Zero-based index of the selected row +*/ +CV_INLINE CvMat* cvGetRow( const CvArr* arr, CvMat* submat, int row ) +{ + return cvGetRows( arr, submat, row, row + 1, 1 ); +} + + +/** @brief Returns one of more array columns. + +The function returns the header, corresponding to a specified column span of the input array. That + +is, no data is copied. Therefore, any modifications of the submatrix will affect the original array. +If you need to copy the columns, use cvCloneMat. cvGetCol(arr, submat, col) is a shortcut for +cvGetCols(arr, submat, col, col+1). +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param start_col Zero-based index of the starting column (inclusive) of the span +@param end_col Zero-based index of the ending column (exclusive) of the span + */ +CVAPI(CvMat*) cvGetCols( const CvArr* arr, CvMat* submat, + int start_col, int end_col ); + +/** @overload +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param col Zero-based index of the selected column +*/ +CV_INLINE CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col ) +{ + return cvGetCols( arr, submat, col, col + 1 ); +} + +/** @brief Returns one of array diagonals. + +The function returns the header, corresponding to a specified diagonal of the input array. +@param arr Input array +@param submat Pointer to the resulting sub-array header +@param diag Index of the array diagonal. Zero value corresponds to the main diagonal, -1 +corresponds to the diagonal above the main, 1 corresponds to the diagonal below the main, and so +forth. + */ +CVAPI(CvMat*) cvGetDiag( const CvArr* arr, CvMat* submat, + int diag CV_DEFAULT(0)); + +/** low-level scalar <-> raw data conversion functions */ +CVAPI(void) cvScalarToRawData( const CvScalar* scalar, void* data, int type, + int extend_to_12 CV_DEFAULT(0) ); + +CVAPI(void) cvRawDataToScalar( const void* data, int type, CvScalar* scalar ); + +/** @brief Creates a new matrix header but does not allocate the matrix data. + +The function allocates a header for a multi-dimensional dense array. The array data can further be +allocated using cvCreateData or set explicitly to user-allocated data via cvSetData. +@param dims Number of array dimensions +@param sizes Array of dimension sizes +@param type Type of array elements, see cvCreateMat + */ +CVAPI(CvMatND*) cvCreateMatNDHeader( int dims, const int* sizes, int type ); + +/** @brief Creates the header and allocates the data for a multi-dimensional dense array. + +This function call is equivalent to the following code: +@code + CvMatND* mat = cvCreateMatNDHeader(dims, sizes, type); + cvCreateData(mat); +@endcode +@param dims Number of array dimensions. This must not exceed CV_MAX_DIM (32 by default, but can be +changed at build time). +@param sizes Array of dimension sizes. +@param type Type of array elements, see cvCreateMat . + */ +CVAPI(CvMatND*) cvCreateMatND( int dims, const int* sizes, int type ); + +/** @brief Initializes a pre-allocated multi-dimensional array header. + +@param mat A pointer to the array header to be initialized +@param dims The number of array dimensions +@param sizes An array of dimension sizes +@param type Type of array elements, see cvCreateMat +@param data Optional data pointer assigned to the matrix header + */ +CVAPI(CvMatND*) cvInitMatNDHeader( CvMatND* mat, int dims, const int* sizes, + int type, void* data CV_DEFAULT(NULL) ); + +/** @brief Deallocates a multi-dimensional array. + +The function decrements the array data reference counter and releases the array header. If the +reference counter reaches 0, it also deallocates the data. : +@code + if(*mat ) + cvDecRefData(*mat); + cvFree((void**)mat); +@endcode +@param mat Double pointer to the array + */ +CV_INLINE void cvReleaseMatND( CvMatND** mat ) +{ + cvReleaseMat( (CvMat**)mat ); +} + +/** Creates a copy of CvMatND (except, may be, steps) */ +CVAPI(CvMatND*) cvCloneMatND( const CvMatND* mat ); + +/** @brief Creates sparse array. + +The function allocates a multi-dimensional sparse array. Initially the array contain no elements, +that is PtrND and other related functions will return 0 for every index. +@param dims Number of array dimensions. In contrast to the dense matrix, the number of dimensions is +practically unlimited (up to \f$2^{16}\f$ ). +@param sizes Array of dimension sizes +@param type Type of array elements. The same as for CvMat + */ +CVAPI(CvSparseMat*) cvCreateSparseMat( int dims, const int* sizes, int type ); + +/** @brief Deallocates sparse array. + +The function releases the sparse array and clears the array pointer upon exit. +@param mat Double pointer to the array + */ +CVAPI(void) cvReleaseSparseMat( CvSparseMat** mat ); + +/** Creates a copy of CvSparseMat (except, may be, zero items) */ +CVAPI(CvSparseMat*) cvCloneSparseMat( const CvSparseMat* mat ); + +/** @brief Initializes sparse array elements iterator. + +The function initializes iterator of sparse array elements and returns pointer to the first element, +or NULL if the array is empty. +@param mat Input array +@param mat_iterator Initialized iterator + */ +CVAPI(CvSparseNode*) cvInitSparseMatIterator( const CvSparseMat* mat, + CvSparseMatIterator* mat_iterator ); + +/** @brief Returns the next sparse matrix element + +The function moves iterator to the next sparse matrix element and returns pointer to it. In the +current version there is no any particular order of the elements, because they are stored in the +hash table. The sample below demonstrates how to iterate through the sparse matrix: +@code + // print all the non-zero sparse matrix elements and compute their sum + double sum = 0; + int i, dims = cvGetDims(sparsemat); + CvSparseMatIterator it; + CvSparseNode* node = cvInitSparseMatIterator(sparsemat, &it); + + for(; node != 0; node = cvGetNextSparseNode(&it)) + { + int* idx = CV_NODE_IDX(array, node); + float val = *(float*)CV_NODE_VAL(array, node); + printf("M"); + for(i = 0; i < dims; i++ ) + printf("[%d]", idx[i]); + printf("=%g\n", val); + + sum += val; + } + + printf("nTotal sum = %g\n", sum); +@endcode +@param mat_iterator Sparse array iterator + */ +CV_INLINE CvSparseNode* cvGetNextSparseNode( CvSparseMatIterator* mat_iterator ) +{ + if( mat_iterator->node->next ) + return mat_iterator->node = mat_iterator->node->next; + else + { + int idx; + for( idx = ++mat_iterator->curidx; idx < mat_iterator->mat->hashsize; idx++ ) + { + CvSparseNode* node = (CvSparseNode*)mat_iterator->mat->hashtable[idx]; + if( node ) + { + mat_iterator->curidx = idx; + return mat_iterator->node = node; + } + } + return NULL; + } +} + + +#define CV_MAX_ARR 10 + +/** matrix iterator: used for n-ary operations on dense arrays */ +typedef struct CvNArrayIterator +{ + int count; /**< number of arrays */ + int dims; /**< number of dimensions to iterate */ + CvSize size; /**< maximal common linear size: { width = size, height = 1 } */ + uchar* ptr[CV_MAX_ARR]; /**< pointers to the array slices */ + int stack[CV_MAX_DIM]; /**< for internal use */ + CvMatND* hdr[CV_MAX_ARR]; /**< pointers to the headers of the + matrices that are processed */ +} +CvNArrayIterator; + +#define CV_NO_DEPTH_CHECK 1 +#define CV_NO_CN_CHECK 2 +#define CV_NO_SIZE_CHECK 4 + +/** initializes iterator that traverses through several arrays simultaneously + (the function together with cvNextArraySlice is used for + N-ari element-wise operations) */ +CVAPI(int) cvInitNArrayIterator( int count, CvArr** arrs, + const CvArr* mask, CvMatND* stubs, + CvNArrayIterator* array_iterator, + int flags CV_DEFAULT(0) ); + +/** returns zero value if iteration is finished, non-zero (slice length) otherwise */ +CVAPI(int) cvNextNArraySlice( CvNArrayIterator* array_iterator ); + + +/** @brief Returns type of array elements. + +The function returns type of the array elements. In the case of IplImage the type is converted to +CvMat-like representation. For example, if the image has been created as: +@code + IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3); +@endcode +The code cvGetElemType(img) will return CV_8UC3. +@param arr Input array + */ +CVAPI(int) cvGetElemType( const CvArr* arr ); + +/** @brief Return number of array dimensions + +The function returns the array dimensionality and the array of dimension sizes. In the case of +IplImage or CvMat it always returns 2 regardless of number of image/matrix rows. For example, the +following code calculates total number of array elements: +@code + int sizes[CV_MAX_DIM]; + int i, total = 1; + int dims = cvGetDims(arr, size); + for(i = 0; i < dims; i++ ) + total *= sizes[i]; +@endcode +@param arr Input array +@param sizes Optional output vector of the array dimension sizes. For 2d arrays the number of rows +(height) goes first, number of columns (width) next. + */ +CVAPI(int) cvGetDims( const CvArr* arr, int* sizes CV_DEFAULT(NULL) ); + + +/** @brief Returns array size along the specified dimension. + +@param arr Input array +@param index Zero-based dimension index (for matrices 0 means number of rows, 1 means number of +columns; for images 0 means height, 1 means width) + */ +CVAPI(int) cvGetDimSize( const CvArr* arr, int index ); + + +/** @brief Return pointer to a particular array element. + +The functions return a pointer to a specific array element. Number of array dimension should match +to the number of indices passed to the function except for cvPtr1D function that can be used for +sequential access to 1D, 2D or nD dense arrays. + +The functions can be used for sparse arrays as well - if the requested node does not exist they +create it and set it to zero. + +All these as well as other functions accessing array elements ( cvGetND , cvGetRealND , cvSet +, cvSetND , cvSetRealND ) raise an error in case if the element index is out of range. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param type Optional output parameter: type of matrix elements + */ +CVAPI(uchar*) cvPtr1D( const CvArr* arr, int idx0, int* type CV_DEFAULT(NULL)); +/** @overload */ +CVAPI(uchar*) cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type CV_DEFAULT(NULL) ); +/** @overload */ +CVAPI(uchar*) cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, + int* type CV_DEFAULT(NULL)); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param type Optional output parameter: type of matrix elements +@param create_node Optional input parameter for sparse matrices. Non-zero value of the parameter +means that the requested element is created if it does not exist already. +@param precalc_hashval Optional input parameter for sparse matrices. If the pointer is not NULL, +the function does not recalculate the node hash value, but takes it from the specified location. +It is useful for speeding up pair-wise operations (TODO: provide an example) +*/ +CVAPI(uchar*) cvPtrND( const CvArr* arr, const int* idx, int* type CV_DEFAULT(NULL), + int create_node CV_DEFAULT(1), + unsigned* precalc_hashval CV_DEFAULT(NULL)); + +/** @brief Return a specific array element. + +The functions return a specific array element. In the case of a sparse array the functions return 0 +if the requested node does not exist (no new node is created by the functions). +@param arr Input array +@param idx0 The first zero-based component of the element index + */ +CVAPI(CvScalar) cvGet1D( const CvArr* arr, int idx0 ); +/** @overload */ +CVAPI(CvScalar) cvGet2D( const CvArr* arr, int idx0, int idx1 ); +/** @overload */ +CVAPI(CvScalar) cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); +/** @overload +@param arr Input array +@param idx Array of the element indices +*/ +CVAPI(CvScalar) cvGetND( const CvArr* arr, const int* idx ); + +/** @brief Return a specific element of single-channel 1D, 2D, 3D or nD array. + +Returns a specific element of a single-channel array. If the array has multiple channels, a runtime +error is raised. Note that Get?D functions can be used safely for both single-channel and +multiple-channel arrays though they are a bit slower. + +In the case of a sparse array the functions return 0 if the requested node does not exist (no new +node is created by the functions). +@param arr Input array. Must have a single channel. +@param idx0 The first zero-based component of the element index + */ +CVAPI(double) cvGetReal1D( const CvArr* arr, int idx0 ); +/** @overload */ +CVAPI(double) cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); +/** @overload */ +CVAPI(double) cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); +/** @overload +@param arr Input array. Must have a single channel. +@param idx Array of the element indices +*/ +CVAPI(double) cvGetRealND( const CvArr* arr, const int* idx ); + +/** @brief Change the particular array element. + +The functions assign the new value to a particular array element. In the case of a sparse array the +functions create the node if it does not exist yet. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param value The assigned value + */ +CVAPI(void) cvSet1D( CvArr* arr, int idx0, CvScalar value ); +/** @overload */ +CVAPI(void) cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); +/** @overload */ +CVAPI(void) cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value ); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param value The assigned value +*/ +CVAPI(void) cvSetND( CvArr* arr, const int* idx, CvScalar value ); + +/** @brief Change a specific array element. + +The functions assign a new value to a specific element of a single-channel array. If the array has +multiple channels, a runtime error is raised. Note that the Set\*D function can be used safely for +both single-channel and multiple-channel arrays, though they are a bit slower. + +In the case of a sparse array the functions create the node if it does not yet exist. +@param arr Input array +@param idx0 The first zero-based component of the element index +@param value The assigned value + */ +CVAPI(void) cvSetReal1D( CvArr* arr, int idx0, double value ); +/** @overload */ +CVAPI(void) cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); +/** @overload */ +CVAPI(void) cvSetReal3D( CvArr* arr, int idx0, + int idx1, int idx2, double value ); +/** @overload +@param arr Input array +@param idx Array of the element indices +@param value The assigned value +*/ +CVAPI(void) cvSetRealND( CvArr* arr, const int* idx, double value ); + +/** clears element of ND dense array, + in case of sparse arrays it deletes the specified node */ +CVAPI(void) cvClearND( CvArr* arr, const int* idx ); + +/** @brief Returns matrix header for arbitrary array. + +The function returns a matrix header for the input array that can be a matrix - CvMat, an image - +IplImage, or a multi-dimensional dense array - CvMatND (the third option is allowed only if +allowND != 0) . In the case of matrix the function simply returns the input pointer. In the case of +IplImage\* or CvMatND it initializes the header structure with parameters of the current image ROI +and returns &header. Because COI is not supported by CvMat, it is returned separately. + +The function provides an easy way to handle both types of arrays - IplImage and CvMat using the same +code. Input array must have non-zero data pointer, otherwise the function will report an error. + +@note If the input array is IplImage with planar data layout and COI set, the function returns the +pointer to the selected plane and COI == 0. This feature allows user to process IplImage structures +with planar data layout, even though OpenCV does not support such images. +@param arr Input array +@param header Pointer to CvMat structure used as a temporary buffer +@param coi Optional output parameter for storing COI +@param allowND If non-zero, the function accepts multi-dimensional dense arrays (CvMatND\*) and +returns 2D matrix (if CvMatND has two dimensions) or 1D matrix (when CvMatND has 1 dimension or +more than 2 dimensions). The CvMatND array must be continuous. +@sa cvGetImage, cvarrToMat. + */ +CVAPI(CvMat*) cvGetMat( const CvArr* arr, CvMat* header, + int* coi CV_DEFAULT(NULL), + int allowND CV_DEFAULT(0)); + +/** @brief Returns image header for arbitrary array. + +The function returns the image header for the input array that can be a matrix (CvMat) or image +(IplImage). In the case of an image the function simply returns the input pointer. In the case of +CvMat it initializes an image_header structure with the parameters of the input matrix. Note that +if we transform IplImage to CvMat using cvGetMat and then transform CvMat back to IplImage using +this function, we will get different headers if the ROI is set in the original image. +@param arr Input array +@param image_header Pointer to IplImage structure used as a temporary buffer + */ +CVAPI(IplImage*) cvGetImage( const CvArr* arr, IplImage* image_header ); + + +/** @brief Changes the shape of a multi-dimensional array without copying the data. + +The function is an advanced version of cvReshape that can work with multi-dimensional arrays as +well (though it can work with ordinary images and matrices) and change the number of dimensions. + +Below are the two samples from the cvReshape description rewritten using cvReshapeMatND: +@code + IplImage* color_img = cvCreateImage(cvSize(320,240), IPL_DEPTH_8U, 3); + IplImage gray_img_hdr, *gray_img; + gray_img = (IplImage*)cvReshapeMatND(color_img, sizeof(gray_img_hdr), &gray_img_hdr, 1, 0, 0); + ... + int size[] = { 2, 2, 2 }; + CvMatND* mat = cvCreateMatND(3, size, CV_32F); + CvMat row_header, *row; + row = (CvMat*)cvReshapeMatND(mat, sizeof(row_header), &row_header, 0, 1, 0); +@endcode +In C, the header file for this function includes a convenient macro cvReshapeND that does away with +the sizeof_header parameter. So, the lines containing the call to cvReshapeMatND in the examples +may be replaced as follow: +@code + gray_img = (IplImage*)cvReshapeND(color_img, &gray_img_hdr, 1, 0, 0); + ... + row = (CvMat*)cvReshapeND(mat, &row_header, 0, 1, 0); +@endcode +@param arr Input array +@param sizeof_header Size of output header to distinguish between IplImage, CvMat and CvMatND +output headers +@param header Output header to be filled +@param new_cn New number of channels. new_cn = 0 means that the number of channels remains +unchanged. +@param new_dims New number of dimensions. new_dims = 0 means that the number of dimensions +remains the same. +@param new_sizes Array of new dimension sizes. Only new_dims-1 values are used, because the +total number of elements must remain the same. Thus, if new_dims = 1, new_sizes array is not +used. + */ +CVAPI(CvArr*) cvReshapeMatND( const CvArr* arr, + int sizeof_header, CvArr* header, + int new_cn, int new_dims, int* new_sizes ); + +#define cvReshapeND( arr, header, new_cn, new_dims, new_sizes ) \ + cvReshapeMatND( (arr), sizeof(*(header)), (header), \ + (new_cn), (new_dims), (new_sizes)) + +/** @brief Changes shape of matrix/image without copying data. + +The function initializes the CvMat header so that it points to the same data as the original array +but has a different shape - different number of channels, different number of rows, or both. + +The following example code creates one image buffer and two image headers, the first is for a +320x240x3 image and the second is for a 960x240x1 image: +@code + IplImage* color_img = cvCreateImage(cvSize(320,240), IPL_DEPTH_8U, 3); + CvMat gray_mat_hdr; + IplImage gray_img_hdr, *gray_img; + cvReshape(color_img, &gray_mat_hdr, 1); + gray_img = cvGetImage(&gray_mat_hdr, &gray_img_hdr); +@endcode +And the next example converts a 3x3 matrix to a single 1x9 vector: +@code + CvMat* mat = cvCreateMat(3, 3, CV_32F); + CvMat row_header, *row; + row = cvReshape(mat, &row_header, 0, 1); +@endcode +@param arr Input array +@param header Output header to be filled +@param new_cn New number of channels. 'new_cn = 0' means that the number of channels remains +unchanged. +@param new_rows New number of rows. 'new_rows = 0' means that the number of rows remains +unchanged unless it needs to be changed according to new_cn value. +*/ +CVAPI(CvMat*) cvReshape( const CvArr* arr, CvMat* header, + int new_cn, int new_rows CV_DEFAULT(0) ); + +/** Repeats source 2d array several times in both horizontal and + vertical direction to fill destination array */ +CVAPI(void) cvRepeat( const CvArr* src, CvArr* dst ); + +/** @brief Allocates array data + +The function allocates image, matrix or multi-dimensional dense array data. Note that in the case of +matrix types OpenCV allocation functions are used. In the case of IplImage they are used unless +CV_TURN_ON_IPL_COMPATIBILITY() has been called before. In the latter case IPL functions are used +to allocate the data. +@param arr Array header + */ +CVAPI(void) cvCreateData( CvArr* arr ); + +/** @brief Releases array data. + +The function releases the array data. In the case of CvMat or CvMatND it simply calls +cvDecRefData(), that is the function can not deallocate external data. See also the note to +cvCreateData . +@param arr Array header + */ +CVAPI(void) cvReleaseData( CvArr* arr ); + +/** @brief Assigns user data to the array header. + +The function assigns user data to the array header. Header should be initialized before using +cvCreateMatHeader, cvCreateImageHeader, cvCreateMatNDHeader, cvInitMatHeader, +cvInitImageHeader or cvInitMatNDHeader. +@param arr Array header +@param data User data +@param step Full row length in bytes + */ +CVAPI(void) cvSetData( CvArr* arr, void* data, int step ); + +/** @brief Retrieves low-level information about the array. + +The function fills output variables with low-level information about the array data. All output + +parameters are optional, so some of the pointers may be set to NULL. If the array is IplImage with +ROI set, the parameters of ROI are returned. + +The following example shows how to get access to array elements. It computes absolute values of the +array elements : +@code + float* data; + int step; + CvSize size; + + cvGetRawData(array, (uchar**)&data, &step, &size); + step /= sizeof(data[0]); + + for(int y = 0; y < size.height; y++, data += step ) + for(int x = 0; x < size.width; x++ ) + data[x] = (float)fabs(data[x]); +@endcode +@param arr Array header +@param data Output pointer to the whole image origin or ROI origin if ROI is set +@param step Output full row length in bytes +@param roi_size Output ROI size + */ +CVAPI(void) cvGetRawData( const CvArr* arr, uchar** data, + int* step CV_DEFAULT(NULL), + CvSize* roi_size CV_DEFAULT(NULL)); + +/** @brief Returns size of matrix or image ROI. + +The function returns number of rows (CvSize::height) and number of columns (CvSize::width) of the +input matrix or image. In the case of image the size of ROI is returned. +@param arr array header + */ +CVAPI(CvSize) cvGetSize( const CvArr* arr ); + +/** @brief Copies one array to another. + +The function copies selected elements from an input array to an output array: + +\f[\texttt{dst} (I)= \texttt{src} (I) \quad \text{if} \quad \texttt{mask} (I) \ne 0.\f] + +If any of the passed arrays is of IplImage type, then its ROI and COI fields are used. Both arrays +must have the same type, the same number of dimensions, and the same size. The function can also +copy sparse arrays (mask is not supported in this case). +@param src The source array +@param dst The destination array +@param mask Operation mask, 8-bit single channel array; specifies elements of the destination array +to be changed + */ +CVAPI(void) cvCopy( const CvArr* src, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Sets every element of an array to a given value. + +The function copies the scalar value to every selected element of the destination array: +\f[\texttt{arr} (I)= \texttt{value} \quad \text{if} \quad \texttt{mask} (I) \ne 0\f] +If array arr is of IplImage type, then is ROI used, but COI must not be set. +@param arr The destination array +@param value Fill value +@param mask Operation mask, 8-bit single channel array; specifies elements of the destination +array to be changed + */ +CVAPI(void) cvSet( CvArr* arr, CvScalar value, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Clears the array. + +The function clears the array. In the case of dense arrays (CvMat, CvMatND or IplImage), +cvZero(array) is equivalent to cvSet(array,cvScalarAll(0),0). In the case of sparse arrays all the +elements are removed. +@param arr Array to be cleared + */ +CVAPI(void) cvSetZero( CvArr* arr ); +#define cvZero cvSetZero + + +/** Splits a multi-channel array into the set of single-channel arrays or + extracts particular [color] plane */ +CVAPI(void) cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1, + CvArr* dst2, CvArr* dst3 ); + +/** Merges a set of single-channel arrays into the single multi-channel array + or inserts one particular [color] plane to the array */ +CVAPI(void) cvMerge( const CvArr* src0, const CvArr* src1, + const CvArr* src2, const CvArr* src3, + CvArr* dst ); + +/** Copies several channels from input arrays to + certain channels of output arrays */ +CVAPI(void) cvMixChannels( const CvArr** src, int src_count, + CvArr** dst, int dst_count, + const int* from_to, int pair_count ); + +/** @brief Converts one array to another with optional linear transformation. + +The function has several different purposes, and thus has several different names. It copies one +array to another with optional scaling, which is performed first, and/or optional type conversion, +performed after: + +\f[\texttt{dst} (I) = \texttt{scale} \texttt{src} (I) + ( \texttt{shift} _0, \texttt{shift} _1,...)\f] + +All the channels of multi-channel arrays are processed independently. + +The type of conversion is done with rounding and saturation, that is if the result of scaling + +conversion can not be represented exactly by a value of the destination array element type, it is +set to the nearest representable value on the real axis. +@param src Source array +@param dst Destination array +@param scale Scale factor +@param shift Value added to the scaled source array elements + */ +CVAPI(void) cvConvertScale( const CvArr* src, CvArr* dst, + double scale CV_DEFAULT(1), + double shift CV_DEFAULT(0) ); +#define cvCvtScale cvConvertScale +#define cvScale cvConvertScale +#define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 ) + + +/** Performs linear transformation on every source array element, + stores absolute value of the result: + dst(x,y,c) = abs(scale*src(x,y,c)+shift). + destination array must have 8u type. + In other cases one may use cvConvertScale + cvAbsDiffS */ +CVAPI(void) cvConvertScaleAbs( const CvArr* src, CvArr* dst, + double scale CV_DEFAULT(1), + double shift CV_DEFAULT(0) ); +#define cvCvtScaleAbs cvConvertScaleAbs + + +/** checks termination criteria validity and + sets eps to default_eps (if it is not set), + max_iter to default_max_iters (if it is not set) +*/ +CVAPI(CvTermCriteria) cvCheckTermCriteria( CvTermCriteria criteria, + double default_eps, + int default_max_iters ); + +/****************************************************************************************\ +* Arithmetic, logic and comparison operations * +\****************************************************************************************/ + +/** dst(mask) = src1(mask) + src2(mask) */ +CVAPI(void) cvAdd( const CvArr* src1, const CvArr* src2, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src(mask) + value */ +CVAPI(void) cvAddS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src1(mask) - src2(mask) */ +CVAPI(void) cvSub( const CvArr* src1, const CvArr* src2, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(mask) = src(mask) - value = src(mask) + (-value) */ +CV_INLINE void cvSubS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)) +{ + cvAddS( src, cvScalar( -value.val[0], -value.val[1], -value.val[2], -value.val[3]), + dst, mask ); +} + +/** dst(mask) = value - src(mask) */ +CVAPI(void) cvSubRS( const CvArr* src, CvScalar value, CvArr* dst, + const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) * src2(idx) * scale + (scaled element-wise multiplication of 2 arrays) */ +CVAPI(void) cvMul( const CvArr* src1, const CvArr* src2, + CvArr* dst, double scale CV_DEFAULT(1) ); + +/** element-wise division/inversion with scaling: + dst(idx) = src1(idx) * scale / src2(idx) + or dst(idx) = scale / src2(idx) if src1 == 0 */ +CVAPI(void) cvDiv( const CvArr* src1, const CvArr* src2, + CvArr* dst, double scale CV_DEFAULT(1)); + +/** dst = src1 * scale + src2 */ +CVAPI(void) cvScaleAdd( const CvArr* src1, CvScalar scale, + const CvArr* src2, CvArr* dst ); +#define cvAXPY( A, real_scalar, B, C ) cvScaleAdd(A, cvRealScalar(real_scalar), B, C) + +/** dst = src1 * alpha + src2 * beta + gamma */ +CVAPI(void) cvAddWeighted( const CvArr* src1, double alpha, + const CvArr* src2, double beta, + double gamma, CvArr* dst ); + +/** @brief Calculates the dot product of two arrays in Euclidean metrics. + +The function calculates and returns the Euclidean dot product of two arrays. + +\f[src1 \bullet src2 = \sum _I ( \texttt{src1} (I) \texttt{src2} (I))\f] + +In the case of multiple channel arrays, the results for all channels are accumulated. In particular, +cvDotProduct(a,a) where a is a complex vector, will return \f$||\texttt{a}||^2\f$. The function can +process multi-dimensional arrays, row by row, layer by layer, and so on. +@param src1 The first source array +@param src2 The second source array + */ +CVAPI(double) cvDotProduct( const CvArr* src1, const CvArr* src2 ); + +/** dst(idx) = src1(idx) & src2(idx) */ +CVAPI(void) cvAnd( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) & value */ +CVAPI(void) cvAndS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) | src2(idx) */ +CVAPI(void) cvOr( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) | value */ +CVAPI(void) cvOrS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src1(idx) ^ src2(idx) */ +CVAPI(void) cvXor( const CvArr* src1, const CvArr* src2, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = src(idx) ^ value */ +CVAPI(void) cvXorS( const CvArr* src, CvScalar value, + CvArr* dst, const CvArr* mask CV_DEFAULT(NULL)); + +/** dst(idx) = ~src(idx) */ +CVAPI(void) cvNot( const CvArr* src, CvArr* dst ); + +/** dst(idx) = lower(idx) <= src(idx) < upper(idx) */ +CVAPI(void) cvInRange( const CvArr* src, const CvArr* lower, + const CvArr* upper, CvArr* dst ); + +/** dst(idx) = lower <= src(idx) < upper */ +CVAPI(void) cvInRangeS( const CvArr* src, CvScalar lower, + CvScalar upper, CvArr* dst ); + +#define CV_CMP_EQ 0 +#define CV_CMP_GT 1 +#define CV_CMP_GE 2 +#define CV_CMP_LT 3 +#define CV_CMP_LE 4 +#define CV_CMP_NE 5 + +/** The comparison operation support single-channel arrays only. + Destination image should be 8uC1 or 8sC1 */ + +/** dst(idx) = src1(idx) _cmp_op_ src2(idx) */ +CVAPI(void) cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op ); + +/** dst(idx) = src1(idx) _cmp_op_ value */ +CVAPI(void) cvCmpS( const CvArr* src, double value, CvArr* dst, int cmp_op ); + +/** dst(idx) = min(src1(idx),src2(idx)) */ +CVAPI(void) cvMin( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(idx) = max(src1(idx),src2(idx)) */ +CVAPI(void) cvMax( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(idx) = min(src(idx),value) */ +CVAPI(void) cvMinS( const CvArr* src, double value, CvArr* dst ); + +/** dst(idx) = max(src(idx),value) */ +CVAPI(void) cvMaxS( const CvArr* src, double value, CvArr* dst ); + +/** dst(x,y,c) = abs(src1(x,y,c) - src2(x,y,c)) */ +CVAPI(void) cvAbsDiff( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** dst(x,y,c) = abs(src(x,y,c) - value(c)) */ +CVAPI(void) cvAbsDiffS( const CvArr* src, CvArr* dst, CvScalar value ); +#define cvAbs( src, dst ) cvAbsDiffS( (src), (dst), cvScalarAll(0)) + +/****************************************************************************************\ +* Math operations * +\****************************************************************************************/ + +/** Does cartesian->polar coordinates conversion. + Either of output components (magnitude or angle) is optional */ +CVAPI(void) cvCartToPolar( const CvArr* x, const CvArr* y, + CvArr* magnitude, CvArr* angle CV_DEFAULT(NULL), + int angle_in_degrees CV_DEFAULT(0)); + +/** Does polar->cartesian coordinates conversion. + Either of output components (magnitude or angle) is optional. + If magnitude is missing it is assumed to be all 1's */ +CVAPI(void) cvPolarToCart( const CvArr* magnitude, const CvArr* angle, + CvArr* x, CvArr* y, + int angle_in_degrees CV_DEFAULT(0)); + +/** Does powering: dst(idx) = src(idx)^power */ +CVAPI(void) cvPow( const CvArr* src, CvArr* dst, double power ); + +/** Does exponention: dst(idx) = exp(src(idx)). + Overflow is not handled yet. Underflow is handled. + Maximal relative error is ~7e-6 for single-precision input */ +CVAPI(void) cvExp( const CvArr* src, CvArr* dst ); + +/** Calculates natural logarithms: dst(idx) = log(abs(src(idx))). + Logarithm of 0 gives large negative number(~-700) + Maximal relative error is ~3e-7 for single-precision output +*/ +CVAPI(void) cvLog( const CvArr* src, CvArr* dst ); + +/** Fast arctangent calculation */ +CVAPI(float) cvFastArctan( float y, float x ); + +/** Fast cubic root calculation */ +CVAPI(float) cvCbrt( float value ); + +#define CV_CHECK_RANGE 1 +#define CV_CHECK_QUIET 2 +/** Checks array values for NaNs, Infs or simply for too large numbers + (if CV_CHECK_RANGE is set). If CV_CHECK_QUIET is set, + no runtime errors is raised (function returns zero value in case of "bad" values). + Otherwise cvError is called */ +CVAPI(int) cvCheckArr( const CvArr* arr, int flags CV_DEFAULT(0), + double min_val CV_DEFAULT(0), double max_val CV_DEFAULT(0)); +#define cvCheckArray cvCheckArr + +#define CV_RAND_UNI 0 +#define CV_RAND_NORMAL 1 + +/** @brief Fills an array with random numbers and updates the RNG state. + +The function fills the destination array with uniformly or normally distributed random numbers. +@param rng CvRNG state initialized by cvRNG +@param arr The destination array +@param dist_type Distribution type +> - **CV_RAND_UNI** uniform distribution +> - **CV_RAND_NORMAL** normal or Gaussian distribution +@param param1 The first parameter of the distribution. In the case of a uniform distribution it is +the inclusive lower boundary of the random numbers range. In the case of a normal distribution it +is the mean value of the random numbers. +@param param2 The second parameter of the distribution. In the case of a uniform distribution it +is the exclusive upper boundary of the random numbers range. In the case of a normal distribution +it is the standard deviation of the random numbers. +@sa randu, randn, RNG::fill. + */ +CVAPI(void) cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, + CvScalar param1, CvScalar param2 ); + +CVAPI(void) cvRandShuffle( CvArr* mat, CvRNG* rng, + double iter_factor CV_DEFAULT(1.)); + +#define CV_SORT_EVERY_ROW 0 +#define CV_SORT_EVERY_COLUMN 1 +#define CV_SORT_ASCENDING 0 +#define CV_SORT_DESCENDING 16 + +CVAPI(void) cvSort( const CvArr* src, CvArr* dst CV_DEFAULT(NULL), + CvArr* idxmat CV_DEFAULT(NULL), + int flags CV_DEFAULT(0)); + +/** Finds real roots of a cubic equation */ +CVAPI(int) cvSolveCubic( const CvMat* coeffs, CvMat* roots ); + +/** Finds all real and complex roots of a polynomial equation */ +CVAPI(void) cvSolvePoly(const CvMat* coeffs, CvMat *roots2, + int maxiter CV_DEFAULT(20), int fig CV_DEFAULT(100)); + +/****************************************************************************************\ +* Matrix operations * +\****************************************************************************************/ + +/** @brief Calculates the cross product of two 3D vectors. + +The function calculates the cross product of two 3D vectors: +\f[\texttt{dst} = \texttt{src1} \times \texttt{src2}\f] +or: +\f[\begin{array}{l} \texttt{dst} _1 = \texttt{src1} _2 \texttt{src2} _3 - \texttt{src1} _3 \texttt{src2} _2 \\ \texttt{dst} _2 = \texttt{src1} _3 \texttt{src2} _1 - \texttt{src1} _1 \texttt{src2} _3 \\ \texttt{dst} _3 = \texttt{src1} _1 \texttt{src2} _2 - \texttt{src1} _2 \texttt{src2} _1 \end{array}\f] +@param src1 The first source vector +@param src2 The second source vector +@param dst The destination vector + */ +CVAPI(void) cvCrossProduct( const CvArr* src1, const CvArr* src2, CvArr* dst ); + +/** Matrix transform: dst = A*B + C, C is optional */ +#define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( (src1), (src2), 1., (src3), 1., (dst), 0 ) +#define cvMatMul( src1, src2, dst ) cvMatMulAdd( (src1), (src2), NULL, (dst)) + +#define CV_GEMM_A_T 1 +#define CV_GEMM_B_T 2 +#define CV_GEMM_C_T 4 +/** Extended matrix transform: + dst = alpha*op(A)*op(B) + beta*op(C), where op(X) is X or X^T */ +CVAPI(void) cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, + const CvArr* src3, double beta, CvArr* dst, + int tABC CV_DEFAULT(0)); +#define cvMatMulAddEx cvGEMM + +/** Transforms each element of source array and stores + resultant vectors in destination array */ +CVAPI(void) cvTransform( const CvArr* src, CvArr* dst, + const CvMat* transmat, + const CvMat* shiftvec CV_DEFAULT(NULL)); +#define cvMatMulAddS cvTransform + +/** Does perspective transform on every element of input array */ +CVAPI(void) cvPerspectiveTransform( const CvArr* src, CvArr* dst, + const CvMat* mat ); + +/** Calculates (A-delta)*(A-delta)^T (order=0) or (A-delta)^T*(A-delta) (order=1) */ +CVAPI(void) cvMulTransposed( const CvArr* src, CvArr* dst, int order, + const CvArr* delta CV_DEFAULT(NULL), + double scale CV_DEFAULT(1.) ); + +/** Transposes matrix. Square matrices can be transposed in-place */ +CVAPI(void) cvTranspose( const CvArr* src, CvArr* dst ); +#define cvT cvTranspose + +/** Completes the symmetric matrix from the lower (LtoR=0) or from the upper (LtoR!=0) part */ +CVAPI(void) cvCompleteSymm( CvMat* matrix, int LtoR CV_DEFAULT(0) ); + +/** Mirror array data around horizontal (flip=0), + vertical (flip=1) or both(flip=-1) axises: + cvFlip(src) flips images vertically and sequences horizontally (inplace) */ +CVAPI(void) cvFlip( const CvArr* src, CvArr* dst CV_DEFAULT(NULL), + int flip_mode CV_DEFAULT(0)); +#define cvMirror cvFlip + + +#define CV_SVD_MODIFY_A 1 +#define CV_SVD_U_T 2 +#define CV_SVD_V_T 4 + +/** Performs Singular Value Decomposition of a matrix */ +CVAPI(void) cvSVD( CvArr* A, CvArr* W, CvArr* U CV_DEFAULT(NULL), + CvArr* V CV_DEFAULT(NULL), int flags CV_DEFAULT(0)); + +/** Performs Singular Value Back Substitution (solves A*X = B): + flags must be the same as in cvSVD */ +CVAPI(void) cvSVBkSb( const CvArr* W, const CvArr* U, + const CvArr* V, const CvArr* B, + CvArr* X, int flags ); + +#define CV_LU 0 +#define CV_SVD 1 +#define CV_SVD_SYM 2 +#define CV_CHOLESKY 3 +#define CV_QR 4 +#define CV_NORMAL 16 + +/** Inverts matrix */ +CVAPI(double) cvInvert( const CvArr* src, CvArr* dst, + int method CV_DEFAULT(CV_LU)); +#define cvInv cvInvert + +/** Solves linear system (src1)*(dst) = (src2) + (returns 0 if src1 is a singular and CV_LU method is used) */ +CVAPI(int) cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, + int method CV_DEFAULT(CV_LU)); + +/** Calculates determinant of input matrix */ +CVAPI(double) cvDet( const CvArr* mat ); + +/** Calculates trace of the matrix (sum of elements on the main diagonal) */ +CVAPI(CvScalar) cvTrace( const CvArr* mat ); + +/** Finds eigen values and vectors of a symmetric matrix */ +CVAPI(void) cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, + double eps CV_DEFAULT(0), + int lowindex CV_DEFAULT(-1), + int highindex CV_DEFAULT(-1)); + +///* Finds selected eigen values and vectors of a symmetric matrix */ +//CVAPI(void) cvSelectedEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, +// int lowindex, int highindex ); + +/** Makes an identity matrix (mat_ij = i == j) */ +CVAPI(void) cvSetIdentity( CvArr* mat, CvScalar value CV_DEFAULT(cvRealScalar(1)) ); + +/** Fills matrix with given range of numbers */ +CVAPI(CvArr*) cvRange( CvArr* mat, double start, double end ); + +/** @anchor core_c_CovarFlags +@name Flags for cvCalcCovarMatrix +@see cvCalcCovarMatrix + @{ +*/ + +/** flag for cvCalcCovarMatrix, transpose([v1-avg, v2-avg,...]) * [v1-avg,v2-avg,...] */ +#define CV_COVAR_SCRAMBLED 0 + +/** flag for cvCalcCovarMatrix, [v1-avg, v2-avg,...] * transpose([v1-avg,v2-avg,...]) */ +#define CV_COVAR_NORMAL 1 + +/** flag for cvCalcCovarMatrix, do not calc average (i.e. mean vector) - use the input vector instead + (useful for calculating covariance matrix by parts) */ +#define CV_COVAR_USE_AVG 2 + +/** flag for cvCalcCovarMatrix, scale the covariance matrix coefficients by number of the vectors */ +#define CV_COVAR_SCALE 4 + +/** flag for cvCalcCovarMatrix, all the input vectors are stored in a single matrix, as its rows */ +#define CV_COVAR_ROWS 8 + +/** flag for cvCalcCovarMatrix, all the input vectors are stored in a single matrix, as its columns */ +#define CV_COVAR_COLS 16 + +/** @} */ + +/** Calculates covariation matrix for a set of vectors +@see @ref core_c_CovarFlags "flags" +*/ +CVAPI(void) cvCalcCovarMatrix( const CvArr** vects, int count, + CvArr* cov_mat, CvArr* avg, int flags ); + +#define CV_PCA_DATA_AS_ROW 0 +#define CV_PCA_DATA_AS_COL 1 +#define CV_PCA_USE_AVG 2 +CVAPI(void) cvCalcPCA( const CvArr* data, CvArr* mean, + CvArr* eigenvals, CvArr* eigenvects, int flags ); + +CVAPI(void) cvProjectPCA( const CvArr* data, const CvArr* mean, + const CvArr* eigenvects, CvArr* result ); + +CVAPI(void) cvBackProjectPCA( const CvArr* proj, const CvArr* mean, + const CvArr* eigenvects, CvArr* result ); + +/** Calculates Mahalanobis(weighted) distance */ +CVAPI(double) cvMahalanobis( const CvArr* vec1, const CvArr* vec2, const CvArr* mat ); +#define cvMahalonobis cvMahalanobis + +/****************************************************************************************\ +* Array Statistics * +\****************************************************************************************/ + +/** Finds sum of array elements */ +CVAPI(CvScalar) cvSum( const CvArr* arr ); + +/** Calculates number of non-zero pixels */ +CVAPI(int) cvCountNonZero( const CvArr* arr ); + +/** Calculates mean value of array elements */ +CVAPI(CvScalar) cvAvg( const CvArr* arr, const CvArr* mask CV_DEFAULT(NULL) ); + +/** Calculates mean and standard deviation of pixel values */ +CVAPI(void) cvAvgSdv( const CvArr* arr, CvScalar* mean, CvScalar* std_dev, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** Finds global minimum, maximum and their positions */ +CVAPI(void) cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val, + CvPoint* min_loc CV_DEFAULT(NULL), + CvPoint* max_loc CV_DEFAULT(NULL), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @anchor core_c_NormFlags + @name Flags for cvNorm and cvNormalize + @{ +*/ +#define CV_C 1 +#define CV_L1 2 +#define CV_L2 4 +#define CV_NORM_MASK 7 +#define CV_RELATIVE 8 +#define CV_DIFF 16 +#define CV_MINMAX 32 + +#define CV_DIFF_C (CV_DIFF | CV_C) +#define CV_DIFF_L1 (CV_DIFF | CV_L1) +#define CV_DIFF_L2 (CV_DIFF | CV_L2) +#define CV_RELATIVE_C (CV_RELATIVE | CV_C) +#define CV_RELATIVE_L1 (CV_RELATIVE | CV_L1) +#define CV_RELATIVE_L2 (CV_RELATIVE | CV_L2) +/** @} */ + +/** Finds norm, difference norm or relative difference norm for an array (or two arrays) +@see ref core_c_NormFlags "flags" +*/ +CVAPI(double) cvNorm( const CvArr* arr1, const CvArr* arr2 CV_DEFAULT(NULL), + int norm_type CV_DEFAULT(CV_L2), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @see ref core_c_NormFlags "flags" */ +CVAPI(void) cvNormalize( const CvArr* src, CvArr* dst, + double a CV_DEFAULT(1.), double b CV_DEFAULT(0.), + int norm_type CV_DEFAULT(CV_L2), + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @anchor core_c_ReduceFlags + @name Flags for cvReduce + @{ +*/ +#define CV_REDUCE_SUM 0 +#define CV_REDUCE_AVG 1 +#define CV_REDUCE_MAX 2 +#define CV_REDUCE_MIN 3 +/** @} */ + +/** @see @ref core_c_ReduceFlags "flags" */ +CVAPI(void) cvReduce( const CvArr* src, CvArr* dst, int dim CV_DEFAULT(-1), + int op CV_DEFAULT(CV_REDUCE_SUM) ); + +/****************************************************************************************\ +* Discrete Linear Transforms and Related Functions * +\****************************************************************************************/ + +/** @anchor core_c_DftFlags + @name Flags for cvDFT, cvDCT and cvMulSpectrums + @{ + */ +#define CV_DXT_FORWARD 0 +#define CV_DXT_INVERSE 1 +#define CV_DXT_SCALE 2 /**< divide result by size of array */ +#define CV_DXT_INV_SCALE (CV_DXT_INVERSE + CV_DXT_SCALE) +#define CV_DXT_INVERSE_SCALE CV_DXT_INV_SCALE +#define CV_DXT_ROWS 4 /**< transform each row individually */ +#define CV_DXT_MUL_CONJ 8 /**< conjugate the second argument of cvMulSpectrums */ +/** @} */ + +/** Discrete Fourier Transform: + complex->complex, + real->ccs (forward), + ccs->real (inverse) +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvDFT( const CvArr* src, CvArr* dst, int flags, + int nonzero_rows CV_DEFAULT(0) ); +#define cvFFT cvDFT + +/** Multiply results of DFTs: DFT(X)*DFT(Y) or DFT(X)*conj(DFT(Y)) +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvMulSpectrums( const CvArr* src1, const CvArr* src2, + CvArr* dst, int flags ); + +/** Finds optimal DFT vector size >= size0 */ +CVAPI(int) cvGetOptimalDFTSize( int size0 ); + +/** Discrete Cosine Transform +@see core_c_DftFlags "flags" +*/ +CVAPI(void) cvDCT( const CvArr* src, CvArr* dst, int flags ); + +/****************************************************************************************\ +* Dynamic data structures * +\****************************************************************************************/ + +/** Calculates length of sequence slice (with support of negative indices). */ +CVAPI(int) cvSliceLength( CvSlice slice, const CvSeq* seq ); + + +/** Creates new memory storage. + block_size == 0 means that default, + somewhat optimal size, is used (currently, it is 64K) */ +CVAPI(CvMemStorage*) cvCreateMemStorage( int block_size CV_DEFAULT(0)); + + +/** Creates a memory storage that will borrow memory blocks from parent storage */ +CVAPI(CvMemStorage*) cvCreateChildMemStorage( CvMemStorage* parent ); + + +/** Releases memory storage. All the children of a parent must be released before + the parent. A child storage returns all the blocks to parent when it is released */ +CVAPI(void) cvReleaseMemStorage( CvMemStorage** storage ); + + +/** Clears memory storage. This is the only way(!!!) (besides cvRestoreMemStoragePos) + to reuse memory allocated for the storage - cvClearSeq,cvClearSet ... + do not free any memory. + A child storage returns all the blocks to the parent when it is cleared */ +CVAPI(void) cvClearMemStorage( CvMemStorage* storage ); + +/** Remember a storage "free memory" position */ +CVAPI(void) cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos ); + +/** Restore a storage "free memory" position */ +CVAPI(void) cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos ); + +/** Allocates continuous buffer of the specified size in the storage */ +CVAPI(void*) cvMemStorageAlloc( CvMemStorage* storage, size_t size ); + +/** Allocates string in memory storage */ +CVAPI(CvString) cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, + int len CV_DEFAULT(-1) ); + +/** Creates new empty sequence that will reside in the specified storage */ +CVAPI(CvSeq*) cvCreateSeq( int seq_flags, size_t header_size, + size_t elem_size, CvMemStorage* storage ); + +/** Changes default size (granularity) of sequence blocks. + The default size is ~1Kbyte */ +CVAPI(void) cvSetSeqBlockSize( CvSeq* seq, int delta_elems ); + + +/** Adds new element to the end of sequence. Returns pointer to the element */ +CVAPI(schar*) cvSeqPush( CvSeq* seq, const void* element CV_DEFAULT(NULL)); + + +/** Adds new element to the beginning of sequence. Returns pointer to it */ +CVAPI(schar*) cvSeqPushFront( CvSeq* seq, const void* element CV_DEFAULT(NULL)); + + +/** Removes the last element from sequence and optionally saves it */ +CVAPI(void) cvSeqPop( CvSeq* seq, void* element CV_DEFAULT(NULL)); + + +/** Removes the first element from sequence and optioanally saves it */ +CVAPI(void) cvSeqPopFront( CvSeq* seq, void* element CV_DEFAULT(NULL)); + + +#define CV_FRONT 1 +#define CV_BACK 0 +/** Adds several new elements to the end of sequence */ +CVAPI(void) cvSeqPushMulti( CvSeq* seq, const void* elements, + int count, int in_front CV_DEFAULT(0) ); + +/** Removes several elements from the end of sequence and optionally saves them */ +CVAPI(void) cvSeqPopMulti( CvSeq* seq, void* elements, + int count, int in_front CV_DEFAULT(0) ); + +/** Inserts a new element in the middle of sequence. + cvSeqInsert(seq,0,elem) == cvSeqPushFront(seq,elem) */ +CVAPI(schar*) cvSeqInsert( CvSeq* seq, int before_index, + const void* element CV_DEFAULT(NULL)); + +/** Removes specified sequence element */ +CVAPI(void) cvSeqRemove( CvSeq* seq, int index ); + + +/** Removes all the elements from the sequence. The freed memory + can be reused later only by the same sequence unless cvClearMemStorage + or cvRestoreMemStoragePos is called */ +CVAPI(void) cvClearSeq( CvSeq* seq ); + + +/** Retrieves pointer to specified sequence element. + Negative indices are supported and mean counting from the end + (e.g -1 means the last sequence element) */ +CVAPI(schar*) cvGetSeqElem( const CvSeq* seq, int index ); + +/** Calculates index of the specified sequence element. + Returns -1 if element does not belong to the sequence */ +CVAPI(int) cvSeqElemIdx( const CvSeq* seq, const void* element, + CvSeqBlock** block CV_DEFAULT(NULL) ); + +/** Initializes sequence writer. The new elements will be added to the end of sequence */ +CVAPI(void) cvStartAppendToSeq( CvSeq* seq, CvSeqWriter* writer ); + + +/** Combination of cvCreateSeq and cvStartAppendToSeq */ +CVAPI(void) cvStartWriteSeq( int seq_flags, int header_size, + int elem_size, CvMemStorage* storage, + CvSeqWriter* writer ); + +/** Closes sequence writer, updates sequence header and returns pointer + to the resultant sequence + (which may be useful if the sequence was created using cvStartWriteSeq)) +*/ +CVAPI(CvSeq*) cvEndWriteSeq( CvSeqWriter* writer ); + + +/** Updates sequence header. May be useful to get access to some of previously + written elements via cvGetSeqElem or sequence reader */ +CVAPI(void) cvFlushSeqWriter( CvSeqWriter* writer ); + + +/** Initializes sequence reader. + The sequence can be read in forward or backward direction */ +CVAPI(void) cvStartReadSeq( const CvSeq* seq, CvSeqReader* reader, + int reverse CV_DEFAULT(0) ); + + +/** Returns current sequence reader position (currently observed sequence element) */ +CVAPI(int) cvGetSeqReaderPos( CvSeqReader* reader ); + + +/** Changes sequence reader position. It may seek to an absolute or + to relative to the current position */ +CVAPI(void) cvSetSeqReaderPos( CvSeqReader* reader, int index, + int is_relative CV_DEFAULT(0)); + +/** Copies sequence content to a continuous piece of memory */ +CVAPI(void*) cvCvtSeqToArray( const CvSeq* seq, void* elements, + CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ) ); + +/** Creates sequence header for array. + After that all the operations on sequences that do not alter the content + can be applied to the resultant sequence */ +CVAPI(CvSeq*) cvMakeSeqHeaderForArray( int seq_type, int header_size, + int elem_size, void* elements, int total, + CvSeq* seq, CvSeqBlock* block ); + +/** Extracts sequence slice (with or without copying sequence elements) */ +CVAPI(CvSeq*) cvSeqSlice( const CvSeq* seq, CvSlice slice, + CvMemStorage* storage CV_DEFAULT(NULL), + int copy_data CV_DEFAULT(0)); + +CV_INLINE CvSeq* cvCloneSeq( const CvSeq* seq, CvMemStorage* storage CV_DEFAULT(NULL)) +{ + return cvSeqSlice( seq, CV_WHOLE_SEQ, storage, 1 ); +} + +/** Removes sequence slice */ +CVAPI(void) cvSeqRemoveSlice( CvSeq* seq, CvSlice slice ); + +/** Inserts a sequence or array into another sequence */ +CVAPI(void) cvSeqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr ); + +/** a < b ? -1 : a > b ? 1 : 0 */ +typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void* b, void* userdata ); + +/** Sorts sequence in-place given element comparison function */ +CVAPI(void) cvSeqSort( CvSeq* seq, CvCmpFunc func, void* userdata CV_DEFAULT(NULL) ); + +/** Finds element in a [sorted] sequence */ +CVAPI(schar*) cvSeqSearch( CvSeq* seq, const void* elem, CvCmpFunc func, + int is_sorted, int* elem_idx, + void* userdata CV_DEFAULT(NULL) ); + +/** Reverses order of sequence elements in-place */ +CVAPI(void) cvSeqInvert( CvSeq* seq ); + +/** Splits sequence into one or more equivalence classes using the specified criteria */ +CVAPI(int) cvSeqPartition( const CvSeq* seq, CvMemStorage* storage, + CvSeq** labels, CvCmpFunc is_equal, void* userdata ); + +/************ Internal sequence functions ************/ +CVAPI(void) cvChangeSeqBlock( void* reader, int direction ); +CVAPI(void) cvCreateSeqBlock( CvSeqWriter* writer ); + + +/** Creates a new set */ +CVAPI(CvSet*) cvCreateSet( int set_flags, int header_size, + int elem_size, CvMemStorage* storage ); + +/** Adds new element to the set and returns pointer to it */ +CVAPI(int) cvSetAdd( CvSet* set_header, CvSetElem* elem CV_DEFAULT(NULL), + CvSetElem** inserted_elem CV_DEFAULT(NULL) ); + +/** Fast variant of cvSetAdd */ +CV_INLINE CvSetElem* cvSetNew( CvSet* set_header ) +{ + CvSetElem* elem = set_header->free_elems; + if( elem ) + { + set_header->free_elems = elem->next_free; + elem->flags = elem->flags & CV_SET_ELEM_IDX_MASK; + set_header->active_count++; + } + else + cvSetAdd( set_header, NULL, &elem ); + return elem; +} + +/** Removes set element given its pointer */ +CV_INLINE void cvSetRemoveByPtr( CvSet* set_header, void* elem ) +{ + CvSetElem* _elem = (CvSetElem*)elem; + assert( _elem->flags >= 0 /*&& (elem->flags & CV_SET_ELEM_IDX_MASK) < set_header->total*/ ); + _elem->next_free = set_header->free_elems; + _elem->flags = (_elem->flags & CV_SET_ELEM_IDX_MASK) | CV_SET_ELEM_FREE_FLAG; + set_header->free_elems = _elem; + set_header->active_count--; +} + +/** Removes element from the set by its index */ +CVAPI(void) cvSetRemove( CvSet* set_header, int index ); + +/** Returns a set element by index. If the element doesn't belong to the set, + NULL is returned */ +CV_INLINE CvSetElem* cvGetSetElem( const CvSet* set_header, int idx ) +{ + CvSetElem* elem = (CvSetElem*)(void *)cvGetSeqElem( (CvSeq*)set_header, idx ); + return elem && CV_IS_SET_ELEM( elem ) ? elem : 0; +} + +/** Removes all the elements from the set */ +CVAPI(void) cvClearSet( CvSet* set_header ); + +/** Creates new graph */ +CVAPI(CvGraph*) cvCreateGraph( int graph_flags, int header_size, + int vtx_size, int edge_size, + CvMemStorage* storage ); + +/** Adds new vertex to the graph */ +CVAPI(int) cvGraphAddVtx( CvGraph* graph, const CvGraphVtx* vtx CV_DEFAULT(NULL), + CvGraphVtx** inserted_vtx CV_DEFAULT(NULL) ); + + +/** Removes vertex from the graph together with all incident edges */ +CVAPI(int) cvGraphRemoveVtx( CvGraph* graph, int index ); +CVAPI(int) cvGraphRemoveVtxByPtr( CvGraph* graph, CvGraphVtx* vtx ); + + +/** Link two vertices specified by indices or pointers if they + are not connected or return pointer to already existing edge + connecting the vertices. + Functions return 1 if a new edge was created, 0 otherwise */ +CVAPI(int) cvGraphAddEdge( CvGraph* graph, + int start_idx, int end_idx, + const CvGraphEdge* edge CV_DEFAULT(NULL), + CvGraphEdge** inserted_edge CV_DEFAULT(NULL) ); + +CVAPI(int) cvGraphAddEdgeByPtr( CvGraph* graph, + CvGraphVtx* start_vtx, CvGraphVtx* end_vtx, + const CvGraphEdge* edge CV_DEFAULT(NULL), + CvGraphEdge** inserted_edge CV_DEFAULT(NULL) ); + +/** Remove edge connecting two vertices */ +CVAPI(void) cvGraphRemoveEdge( CvGraph* graph, int start_idx, int end_idx ); +CVAPI(void) cvGraphRemoveEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, + CvGraphVtx* end_vtx ); + +/** Find edge connecting two vertices */ +CVAPI(CvGraphEdge*) cvFindGraphEdge( const CvGraph* graph, int start_idx, int end_idx ); +CVAPI(CvGraphEdge*) cvFindGraphEdgeByPtr( const CvGraph* graph, + const CvGraphVtx* start_vtx, + const CvGraphVtx* end_vtx ); +#define cvGraphFindEdge cvFindGraphEdge +#define cvGraphFindEdgeByPtr cvFindGraphEdgeByPtr + +/** Remove all vertices and edges from the graph */ +CVAPI(void) cvClearGraph( CvGraph* graph ); + + +/** Count number of edges incident to the vertex */ +CVAPI(int) cvGraphVtxDegree( const CvGraph* graph, int vtx_idx ); +CVAPI(int) cvGraphVtxDegreeByPtr( const CvGraph* graph, const CvGraphVtx* vtx ); + + +/** Retrieves graph vertex by given index */ +#define cvGetGraphVtx( graph, idx ) (CvGraphVtx*)cvGetSetElem((CvSet*)(graph), (idx)) + +/** Retrieves index of a graph vertex given its pointer */ +#define cvGraphVtxIdx( graph, vtx ) ((vtx)->flags & CV_SET_ELEM_IDX_MASK) + +/** Retrieves index of a graph edge given its pointer */ +#define cvGraphEdgeIdx( graph, edge ) ((edge)->flags & CV_SET_ELEM_IDX_MASK) + +#define cvGraphGetVtxCount( graph ) ((graph)->active_count) +#define cvGraphGetEdgeCount( graph ) ((graph)->edges->active_count) + +#define CV_GRAPH_VERTEX 1 +#define CV_GRAPH_TREE_EDGE 2 +#define CV_GRAPH_BACK_EDGE 4 +#define CV_GRAPH_FORWARD_EDGE 8 +#define CV_GRAPH_CROSS_EDGE 16 +#define CV_GRAPH_ANY_EDGE 30 +#define CV_GRAPH_NEW_TREE 32 +#define CV_GRAPH_BACKTRACKING 64 +#define CV_GRAPH_OVER -1 + +#define CV_GRAPH_ALL_ITEMS -1 + +/** flags for graph vertices and edges */ +#define CV_GRAPH_ITEM_VISITED_FLAG (1 << 30) +#define CV_IS_GRAPH_VERTEX_VISITED(vtx) \ + (((CvGraphVtx*)(vtx))->flags & CV_GRAPH_ITEM_VISITED_FLAG) +#define CV_IS_GRAPH_EDGE_VISITED(edge) \ + (((CvGraphEdge*)(edge))->flags & CV_GRAPH_ITEM_VISITED_FLAG) +#define CV_GRAPH_SEARCH_TREE_NODE_FLAG (1 << 29) +#define CV_GRAPH_FORWARD_EDGE_FLAG (1 << 28) + +typedef struct CvGraphScanner +{ + CvGraphVtx* vtx; /* current graph vertex (or current edge origin) */ + CvGraphVtx* dst; /* current graph edge destination vertex */ + CvGraphEdge* edge; /* current edge */ + + CvGraph* graph; /* the graph */ + CvSeq* stack; /* the graph vertex stack */ + int index; /* the lower bound of certainly visited vertices */ + int mask; /* event mask */ +} +CvGraphScanner; + +/** Creates new graph scanner. */ +CVAPI(CvGraphScanner*) cvCreateGraphScanner( CvGraph* graph, + CvGraphVtx* vtx CV_DEFAULT(NULL), + int mask CV_DEFAULT(CV_GRAPH_ALL_ITEMS)); + +/** Releases graph scanner. */ +CVAPI(void) cvReleaseGraphScanner( CvGraphScanner** scanner ); + +/** Get next graph element */ +CVAPI(int) cvNextGraphItem( CvGraphScanner* scanner ); + +/** Creates a copy of graph */ +CVAPI(CvGraph*) cvCloneGraph( const CvGraph* graph, CvMemStorage* storage ); + + +/** Does look-up transformation. Elements of the source array + (that should be 8uC1 or 8sC1) are used as indexes in lutarr 256-element table */ +CVAPI(void) cvLUT( const CvArr* src, CvArr* dst, const CvArr* lut ); + + +/******************* Iteration through the sequence tree *****************/ +typedef struct CvTreeNodeIterator +{ + const void* node; + int level; + int max_level; +} +CvTreeNodeIterator; + +CVAPI(void) cvInitTreeNodeIterator( CvTreeNodeIterator* tree_iterator, + const void* first, int max_level ); +CVAPI(void*) cvNextTreeNode( CvTreeNodeIterator* tree_iterator ); +CVAPI(void*) cvPrevTreeNode( CvTreeNodeIterator* tree_iterator ); + +/** Inserts sequence into tree with specified "parent" sequence. + If parent is equal to frame (e.g. the most external contour), + then added contour will have null pointer to parent. */ +CVAPI(void) cvInsertNodeIntoTree( void* node, void* parent, void* frame ); + +/** Removes contour from tree (together with the contour children). */ +CVAPI(void) cvRemoveNodeFromTree( void* node, void* frame ); + +/** Gathers pointers to all the sequences, + accessible from the `first`, to the single sequence */ +CVAPI(CvSeq*) cvTreeToNodeSeq( const void* first, int header_size, + CvMemStorage* storage ); + +/** The function implements the K-means algorithm for clustering an array of sample + vectors in a specified number of classes */ +#define CV_KMEANS_USE_INITIAL_LABELS 1 +CVAPI(int) cvKMeans2( const CvArr* samples, int cluster_count, CvArr* labels, + CvTermCriteria termcrit, int attempts CV_DEFAULT(1), + CvRNG* rng CV_DEFAULT(0), int flags CV_DEFAULT(0), + CvArr* _centers CV_DEFAULT(0), double* compactness CV_DEFAULT(0) ); + +/****************************************************************************************\ +* System functions * +\****************************************************************************************/ + +/** Loads optimized functions from IPP, MKL etc. or switches back to pure C code */ +CVAPI(int) cvUseOptimized( int on_off ); + +typedef IplImage* (CV_STDCALL* Cv_iplCreateImageHeader) + (int,int,int,char*,char*,int,int,int,int,int, + IplROI*,IplImage*,void*,IplTileInfo*); +typedef void (CV_STDCALL* Cv_iplAllocateImageData)(IplImage*,int,int); +typedef void (CV_STDCALL* Cv_iplDeallocate)(IplImage*,int); +typedef IplROI* (CV_STDCALL* Cv_iplCreateROI)(int,int,int,int,int); +typedef IplImage* (CV_STDCALL* Cv_iplCloneImage)(const IplImage*); + +/** @brief Makes OpenCV use IPL functions for allocating IplImage and IplROI structures. + +Normally, the function is not called directly. Instead, a simple macro +CV_TURN_ON_IPL_COMPATIBILITY() is used that calls cvSetIPLAllocators and passes there pointers +to IPL allocation functions. : +@code + ... + CV_TURN_ON_IPL_COMPATIBILITY() + ... +@endcode +@param create_header pointer to a function, creating IPL image header. +@param allocate_data pointer to a function, allocating IPL image data. +@param deallocate pointer to a function, deallocating IPL image. +@param create_roi pointer to a function, creating IPL image ROI (i.e. Region of Interest). +@param clone_image pointer to a function, cloning an IPL image. + */ +CVAPI(void) cvSetIPLAllocators( Cv_iplCreateImageHeader create_header, + Cv_iplAllocateImageData allocate_data, + Cv_iplDeallocate deallocate, + Cv_iplCreateROI create_roi, + Cv_iplCloneImage clone_image ); + +#define CV_TURN_ON_IPL_COMPATIBILITY() \ + cvSetIPLAllocators( iplCreateImageHeader, iplAllocateImage, \ + iplDeallocate, iplCreateROI, iplCloneImage ) + +/****************************************************************************************\ +* Data Persistence * +\****************************************************************************************/ + +/********************************** High-level functions ********************************/ + +/** @brief Opens file storage for reading or writing data. + +The function opens file storage for reading or writing data. In the latter case, a new file is +created or an existing file is rewritten. The type of the read or written file is determined by the +filename extension: .xml for XML, .yml or .yaml for YAML and .json for JSON. + +At the same time, it also supports adding parameters like "example.xml?base64". + +The function returns a pointer to the CvFileStorage structure. +If the file cannot be opened then the function returns NULL. +@param filename Name of the file associated with the storage +@param memstorage Memory storage used for temporary data and for +: storing dynamic structures, such as CvSeq or CvGraph . If it is NULL, a temporary memory + storage is created and used. +@param flags Can be one of the following: +> - **CV_STORAGE_READ** the storage is open for reading +> - **CV_STORAGE_WRITE** the storage is open for writing + (use **CV_STORAGE_WRITE | CV_STORAGE_WRITE_BASE64** to write rawdata in Base64) +@param encoding + */ +CVAPI(CvFileStorage*) cvOpenFileStorage( const char* filename, CvMemStorage* memstorage, + int flags, const char* encoding CV_DEFAULT(NULL) ); + +/** @brief Releases file storage. + +The function closes the file associated with the storage and releases all the temporary structures. +It must be called after all I/O operations with the storage are finished. +@param fs Double pointer to the released file storage + */ +CVAPI(void) cvReleaseFileStorage( CvFileStorage** fs ); + +/** returns attribute value or 0 (NULL) if there is no such attribute */ +CVAPI(const char*) cvAttrValue( const CvAttrList* attr, const char* attr_name ); + +/** @brief Starts writing a new structure. + +The function starts writing a compound structure (collection) that can be a sequence or a map. After +all the structure fields, which can be scalars or structures, are written, cvEndWriteStruct should +be called. The function can be used to group some objects or to implement the write function for a +some user object (see CvTypeInfo). +@param fs File storage +@param name Name of the written structure. The structure can be accessed by this name when the +storage is read. +@param struct_flags A combination one of the following values: +- **CV_NODE_SEQ** the written structure is a sequence (see discussion of CvFileStorage ), + that is, its elements do not have a name. +- **CV_NODE_MAP** the written structure is a map (see discussion of CvFileStorage ), that + is, all its elements have names. +One and only one of the two above flags must be specified +- **CV_NODE_FLOW** the optional flag that makes sense only for YAML streams. It means that + the structure is written as a flow (not as a block), which is more compact. It is + recommended to use this flag for structures or arrays whose elements are all scalars. +@param type_name Optional parameter - the object type name. In + case of XML it is written as a type_id attribute of the structure opening tag. In the case of + YAML it is written after a colon following the structure name (see the example in + CvFileStorage description). In case of JSON it is written as a name/value pair. + Mainly it is used with user objects. When the storage is read, the + encoded type name is used to determine the object type (see CvTypeInfo and cvFindType ). +@param attributes This parameter is not used in the current implementation + */ +CVAPI(void) cvStartWriteStruct( CvFileStorage* fs, const char* name, + int struct_flags, const char* type_name CV_DEFAULT(NULL), + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Finishes writing to a file node collection. +@param fs File storage +@sa cvStartWriteStruct. + */ +CVAPI(void) cvEndWriteStruct( CvFileStorage* fs ); + +/** @brief Writes an integer value. + +The function writes a single integer value (with or without a name) to the file storage. +@param fs File storage +@param name Name of the written value. Should be NULL if and only if the parent structure is a +sequence. +@param value The written value + */ +CVAPI(void) cvWriteInt( CvFileStorage* fs, const char* name, int value ); + +/** @brief Writes a floating-point value. + +The function writes a single floating-point value (with or without a name) to file storage. Special +values are encoded as follows: NaN (Not A Number) as .NaN, infinity as +.Inf or -.Inf. + +The following example shows how to use the low-level writing functions to store custom structures, +such as termination criteria, without registering a new type. : +@code + void write_termcriteria( CvFileStorage* fs, const char* struct_name, + CvTermCriteria* termcrit ) + { + cvStartWriteStruct( fs, struct_name, CV_NODE_MAP, NULL, cvAttrList(0,0)); + cvWriteComment( fs, "termination criteria", 1 ); // just a description + if( termcrit->type & CV_TERMCRIT_ITER ) + cvWriteInteger( fs, "max_iterations", termcrit->max_iter ); + if( termcrit->type & CV_TERMCRIT_EPS ) + cvWriteReal( fs, "accuracy", termcrit->epsilon ); + cvEndWriteStruct( fs ); + } +@endcode +@param fs File storage +@param name Name of the written value. Should be NULL if and only if the parent structure is a +sequence. +@param value The written value +*/ +CVAPI(void) cvWriteReal( CvFileStorage* fs, const char* name, double value ); + +/** @brief Writes a text string. + +The function writes a text string to file storage. +@param fs File storage +@param name Name of the written string . Should be NULL if and only if the parent structure is a +sequence. +@param str The written text string +@param quote If non-zero, the written string is put in quotes, regardless of whether they are +required. Otherwise, if the flag is zero, quotes are used only when they are required (e.g. when +the string starts with a digit or contains spaces). + */ +CVAPI(void) cvWriteString( CvFileStorage* fs, const char* name, + const char* str, int quote CV_DEFAULT(0) ); + +/** @brief Writes a comment. + +The function writes a comment into file storage. The comments are skipped when the storage is read. +@param fs File storage +@param comment The written comment, single-line or multi-line +@param eol_comment If non-zero, the function tries to put the comment at the end of current line. +If the flag is zero, if the comment is multi-line, or if it does not fit at the end of the current +line, the comment starts a new line. + */ +CVAPI(void) cvWriteComment( CvFileStorage* fs, const char* comment, + int eol_comment ); + +/** @brief Writes an object to file storage. + +The function writes an object to file storage. First, the appropriate type info is found using +cvTypeOf. Then, the write method associated with the type info is called. + +Attributes are used to customize the writing procedure. The standard types support the following +attributes (all the dt attributes have the same format as in cvWriteRawData): + +-# CvSeq + - **header_dt** description of user fields of the sequence header that follow CvSeq, or + CvChain (if the sequence is a Freeman chain) or CvContour (if the sequence is a contour or + point sequence) + - **dt** description of the sequence elements. + - **recursive** if the attribute is present and is not equal to "0" or "false", the whole + tree of sequences (contours) is stored. +-# CvGraph + - **header_dt** description of user fields of the graph header that follows CvGraph; + - **vertex_dt** description of user fields of graph vertices + - **edge_dt** description of user fields of graph edges (note that the edge weight is + always written, so there is no need to specify it explicitly) + +Below is the code that creates the YAML file shown in the CvFileStorage description: +@code + #include "cxcore.h" + + int main( int argc, char** argv ) + { + CvMat* mat = cvCreateMat( 3, 3, CV_32F ); + CvFileStorage* fs = cvOpenFileStorage( "example.yml", 0, CV_STORAGE_WRITE ); + + cvSetIdentity( mat ); + cvWrite( fs, "A", mat, cvAttrList(0,0) ); + + cvReleaseFileStorage( &fs ); + cvReleaseMat( &mat ); + return 0; + } +@endcode +@param fs File storage +@param name Name of the written object. Should be NULL if and only if the parent structure is a +sequence. +@param ptr Pointer to the object +@param attributes The attributes of the object. They are specific for each particular type (see +the discussion below). + */ +CVAPI(void) cvWrite( CvFileStorage* fs, const char* name, const void* ptr, + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Starts the next stream. + +The function finishes the currently written stream and starts the next stream. In the case of XML +the file with multiple streams looks like this: +@code{.xml} + + + + + + + ... +@endcode +The YAML file will look like this: +@code{.yaml} + %YAML 1.0 + # stream #1 data + ... + --- + # stream #2 data +@endcode +This is useful for concatenating files or for resuming the writing process. +@param fs File storage + */ +CVAPI(void) cvStartNextStream( CvFileStorage* fs ); + +/** @brief Writes multiple numbers. + +The function writes an array, whose elements consist of single or multiple numbers. The function +call can be replaced with a loop containing a few cvWriteInt and cvWriteReal calls, but a single +call is more efficient. Note that because none of the elements have a name, they should be written +to a sequence rather than a map. +@param fs File storage +@param src Pointer to the written array +@param len Number of the array elements to write +@param dt Specification of each array element, see @ref format_spec "format specification" + */ +CVAPI(void) cvWriteRawData( CvFileStorage* fs, const void* src, + int len, const char* dt ); + +/** @brief Writes multiple numbers in Base64. + +If either CV_STORAGE_WRITE_BASE64 or cv::FileStorage::WRITE_BASE64 is used, +this function will be the same as cvWriteRawData. If neither, the main +difference is that it outputs a sequence in Base64 encoding rather than +in plain text. + +This function can only be used to write a sequence with a type "binary". + +@param fs File storage +@param src Pointer to the written array +@param len Number of the array elements to write +@param dt Specification of each array element, see @ref format_spec "format specification" +*/ +CVAPI(void) cvWriteRawDataBase64( CvFileStorage* fs, const void* src, + int len, const char* dt ); + +/** @brief Returns a unique pointer for a given name. + +The function returns a unique pointer for each particular file node name. This pointer can be then +passed to the cvGetFileNode function that is faster than cvGetFileNodeByName because it compares +text strings by comparing pointers rather than the strings' content. + +Consider the following example where an array of points is encoded as a sequence of 2-entry maps: +@code + points: + - { x: 10, y: 10 } + - { x: 20, y: 20 } + - { x: 30, y: 30 } + # ... +@endcode +Then, it is possible to get hashed "x" and "y" pointers to speed up decoding of the points. : +@code + #include "cxcore.h" + + int main( int argc, char** argv ) + { + CvFileStorage* fs = cvOpenFileStorage( "points.yml", 0, CV_STORAGE_READ ); + CvStringHashNode* x_key = cvGetHashedNode( fs, "x", -1, 1 ); + CvStringHashNode* y_key = cvGetHashedNode( fs, "y", -1, 1 ); + CvFileNode* points = cvGetFileNodeByName( fs, 0, "points" ); + + if( CV_NODE_IS_SEQ(points->tag) ) + { + CvSeq* seq = points->data.seq; + int i, total = seq->total; + CvSeqReader reader; + cvStartReadSeq( seq, &reader, 0 ); + for( i = 0; i < total; i++ ) + { + CvFileNode* pt = (CvFileNode*)reader.ptr; + #if 1 // faster variant + CvFileNode* xnode = cvGetFileNode( fs, pt, x_key, 0 ); + CvFileNode* ynode = cvGetFileNode( fs, pt, y_key, 0 ); + assert( xnode && CV_NODE_IS_INT(xnode->tag) && + ynode && CV_NODE_IS_INT(ynode->tag)); + int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); + int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); + #elif 1 // slower variant; does not use x_key & y_key + CvFileNode* xnode = cvGetFileNodeByName( fs, pt, "x" ); + CvFileNode* ynode = cvGetFileNodeByName( fs, pt, "y" ); + assert( xnode && CV_NODE_IS_INT(xnode->tag) && + ynode && CV_NODE_IS_INT(ynode->tag)); + int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); + int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); + #else // the slowest yet the easiest to use variant + int x = cvReadIntByName( fs, pt, "x", 0 ); + int y = cvReadIntByName( fs, pt, "y", 0 ); + #endif + CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); + printf(" + } + } + cvReleaseFileStorage( &fs ); + return 0; + } +@endcode +Please note that whatever method of accessing a map you are using, it is still much slower than +using plain sequences; for example, in the above example, it is more efficient to encode the points +as pairs of integers in a single numeric sequence. +@param fs File storage +@param name Literal node name +@param len Length of the name (if it is known apriori), or -1 if it needs to be calculated +@param create_missing Flag that specifies, whether an absent key should be added into the hash table +*/ +CVAPI(CvStringHashNode*) cvGetHashedKey( CvFileStorage* fs, const char* name, + int len CV_DEFAULT(-1), + int create_missing CV_DEFAULT(0)); + +/** @brief Retrieves one of the top-level nodes of the file storage. + +The function returns one of the top-level file nodes. The top-level nodes do not have a name, they +correspond to the streams that are stored one after another in the file storage. If the index is out +of range, the function returns a NULL pointer, so all the top-level nodes can be iterated by +subsequent calls to the function with stream_index=0,1,..., until the NULL pointer is returned. +This function can be used as a base for recursive traversal of the file storage. +@param fs File storage +@param stream_index Zero-based index of the stream. See cvStartNextStream . In most cases, +there is only one stream in the file; however, there can be several. + */ +CVAPI(CvFileNode*) cvGetRootFileNode( const CvFileStorage* fs, + int stream_index CV_DEFAULT(0) ); + +/** @brief Finds a node in a map or file storage. + +The function finds a file node. It is a faster version of cvGetFileNodeByName (see +cvGetHashedKey discussion). Also, the function can insert a new node, if it is not in the map yet. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. If both map and +key are NULLs, the function returns the root file node - a map that contains top-level nodes. +@param key Unique pointer to the node name, retrieved with cvGetHashedKey +@param create_missing Flag that specifies whether an absent node should be added to the map + */ +CVAPI(CvFileNode*) cvGetFileNode( CvFileStorage* fs, CvFileNode* map, + const CvStringHashNode* key, + int create_missing CV_DEFAULT(0) ); + +/** @brief Finds a node in a map or file storage. + +The function finds a file node by name. The node is searched either in map or, if the pointer is +NULL, among the top-level file storage nodes. Using this function for maps and cvGetSeqElem (or +sequence reader) for sequences, it is possible to navigate through the file storage. To speed up +multiple queries for a certain key (e.g., in the case of an array of structures) one may use a +combination of cvGetHashedKey and cvGetFileNode. +@param fs File storage +@param map The parent map. If it is NULL, the function searches in all the top-level nodes +(streams), starting with the first one. +@param name The file node name + */ +CVAPI(CvFileNode*) cvGetFileNodeByName( const CvFileStorage* fs, + const CvFileNode* map, + const char* name ); + +/** @brief Retrieves an integer value from a file node. + +The function returns an integer that is represented by the file node. If the file node is NULL, the +default_value is returned (thus, it is convenient to call the function right after cvGetFileNode +without checking for a NULL pointer). If the file node has type CV_NODE_INT, then node-\>data.i is +returned. If the file node has type CV_NODE_REAL, then node-\>data.f is converted to an integer +and returned. Otherwise the error is reported. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE int cvReadInt( const CvFileNode* node, int default_value CV_DEFAULT(0) ) +{ + return !node ? default_value : + CV_NODE_IS_INT(node->tag) ? node->data.i : + CV_NODE_IS_REAL(node->tag) ? cvRound(node->data.f) : 0x7fffffff; +} + +/** @brief Finds a file node and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadInt. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE int cvReadIntByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, int default_value CV_DEFAULT(0) ) +{ + return cvReadInt( cvGetFileNodeByName( fs, map, name ), default_value ); +} + +/** @brief Retrieves a floating-point value from a file node. + +The function returns a floating-point value that is represented by the file node. If the file node +is NULL, the default_value is returned (thus, it is convenient to call the function right after +cvGetFileNode without checking for a NULL pointer). If the file node has type CV_NODE_REAL , +then node-\>data.f is returned. If the file node has type CV_NODE_INT , then node-:math:\>data.f +is converted to floating-point and returned. Otherwise the result is not determined. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE double cvReadReal( const CvFileNode* node, double default_value CV_DEFAULT(0.) ) +{ + return !node ? default_value : + CV_NODE_IS_INT(node->tag) ? (double)node->data.i : + CV_NODE_IS_REAL(node->tag) ? node->data.f : 1e300; +} + +/** @brief Finds a file node and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadReal . +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE double cvReadRealByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, double default_value CV_DEFAULT(0.) ) +{ + return cvReadReal( cvGetFileNodeByName( fs, map, name ), default_value ); +} + +/** @brief Retrieves a text string from a file node. + +The function returns a text string that is represented by the file node. If the file node is NULL, +the default_value is returned (thus, it is convenient to call the function right after +cvGetFileNode without checking for a NULL pointer). If the file node has type CV_NODE_STR , then +node-:math:\>data.str.ptr is returned. Otherwise the result is not determined. +@param node File node +@param default_value The value that is returned if node is NULL + */ +CV_INLINE const char* cvReadString( const CvFileNode* node, + const char* default_value CV_DEFAULT(NULL) ) +{ + return !node ? default_value : CV_NODE_IS_STRING(node->tag) ? node->data.str.ptr : 0; +} + +/** @brief Finds a file node by its name and returns its value. + +The function is a simple superposition of cvGetFileNodeByName and cvReadString . +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param default_value The value that is returned if the file node is not found + */ +CV_INLINE const char* cvReadStringByName( const CvFileStorage* fs, const CvFileNode* map, + const char* name, const char* default_value CV_DEFAULT(NULL) ) +{ + return cvReadString( cvGetFileNodeByName( fs, map, name ), default_value ); +} + + +/** @brief Decodes an object and returns a pointer to it. + +The function decodes a user object (creates an object in a native representation from the file +storage subtree) and returns it. The object to be decoded must be an instance of a registered type +that supports the read method (see CvTypeInfo). The type of the object is determined by the type +name that is encoded in the file. If the object is a dynamic structure, it is created either in +memory storage and passed to cvOpenFileStorage or, if a NULL pointer was passed, in temporary +memory storage, which is released when cvReleaseFileStorage is called. Otherwise, if the object is +not a dynamic structure, it is created in a heap and should be released with a specialized function +or by using the generic cvRelease. +@param fs File storage +@param node The root object node +@param attributes Unused parameter + */ +CVAPI(void*) cvRead( CvFileStorage* fs, CvFileNode* node, + CvAttrList* attributes CV_DEFAULT(NULL)); + +/** @brief Finds an object by name and decodes it. + +The function is a simple superposition of cvGetFileNodeByName and cvRead. +@param fs File storage +@param map The parent map. If it is NULL, the function searches a top-level node. +@param name The node name +@param attributes Unused parameter + */ +CV_INLINE void* cvReadByName( CvFileStorage* fs, const CvFileNode* map, + const char* name, CvAttrList* attributes CV_DEFAULT(NULL) ) +{ + return cvRead( fs, cvGetFileNodeByName( fs, map, name ), attributes ); +} + + +/** @brief Initializes the file node sequence reader. + +The function initializes the sequence reader to read data from a file node. The initialized reader +can be then passed to cvReadRawDataSlice. +@param fs File storage +@param src The file node (a sequence) to read numbers from +@param reader Pointer to the sequence reader + */ +CVAPI(void) cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src, + CvSeqReader* reader ); + +/** @brief Initializes file node sequence reader. + +The function reads one or more elements from the file node, representing a sequence, to a +user-specified array. The total number of read sequence elements is a product of total and the +number of components in each array element. For example, if dt=2if, the function will read total\*3 +sequence elements. As with any sequence, some parts of the file node sequence can be skipped or read +repeatedly by repositioning the reader using cvSetSeqReaderPos. +@param fs File storage +@param reader The sequence reader. Initialize it with cvStartReadRawData . +@param count The number of elements to read +@param dst Pointer to the destination array +@param dt Specification of each array element. It has the same format as in cvWriteRawData . + */ +CVAPI(void) cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader, + int count, void* dst, const char* dt ); + +/** @brief Reads multiple numbers. + +The function reads elements from a file node that represents a sequence of scalars. +@param fs File storage +@param src The file node (a sequence) to read numbers from +@param dst Pointer to the destination array +@param dt Specification of each array element. It has the same format as in cvWriteRawData . + */ +CVAPI(void) cvReadRawData( const CvFileStorage* fs, const CvFileNode* src, + void* dst, const char* dt ); + +/** @brief Writes a file node to another file storage. + +The function writes a copy of a file node to file storage. Possible applications of the function are +merging several file storages into one and conversion between XML, YAML and JSON formats. +@param fs Destination file storage +@param new_node_name New name of the file node in the destination file storage. To keep the +existing name, use cvcvGetFileNodeName +@param node The written node +@param embed If the written node is a collection and this parameter is not zero, no extra level of +hierarchy is created. Instead, all the elements of node are written into the currently written +structure. Of course, map elements can only be embedded into another map, and sequence elements +can only be embedded into another sequence. + */ +CVAPI(void) cvWriteFileNode( CvFileStorage* fs, const char* new_node_name, + const CvFileNode* node, int embed ); + +/** @brief Returns the name of a file node. + +The function returns the name of a file node or NULL, if the file node does not have a name or if +node is NULL. +@param node File node + */ +CVAPI(const char*) cvGetFileNodeName( const CvFileNode* node ); + +/*********************************** Adding own types ***********************************/ + +/** @brief Registers a new type. + +The function registers a new type, which is described by info . The function creates a copy of the +structure, so the user should delete it after calling the function. +@param info Type info structure + */ +CVAPI(void) cvRegisterType( const CvTypeInfo* info ); + +/** @brief Unregisters the type. + +The function unregisters a type with a specified name. If the name is unknown, it is possible to +locate the type info by an instance of the type using cvTypeOf or by iterating the type list, +starting from cvFirstType, and then calling cvUnregisterType(info-\>typeName). +@param type_name Name of an unregistered type + */ +CVAPI(void) cvUnregisterType( const char* type_name ); + +/** @brief Returns the beginning of a type list. + +The function returns the first type in the list of registered types. Navigation through the list can +be done via the prev and next fields of the CvTypeInfo structure. + */ +CVAPI(CvTypeInfo*) cvFirstType(void); + +/** @brief Finds a type by its name. + +The function finds a registered type by its name. It returns NULL if there is no type with the +specified name. +@param type_name Type name + */ +CVAPI(CvTypeInfo*) cvFindType( const char* type_name ); + +/** @brief Returns the type of an object. + +The function finds the type of a given object. It iterates through the list of registered types and +calls the is_instance function/method for every type info structure with that object until one of +them returns non-zero or until the whole list has been traversed. In the latter case, the function +returns NULL. +@param struct_ptr The object pointer + */ +CVAPI(CvTypeInfo*) cvTypeOf( const void* struct_ptr ); + +/** @brief Releases an object. + +The function finds the type of a given object and calls release with the double pointer. +@param struct_ptr Double pointer to the object + */ +CVAPI(void) cvRelease( void** struct_ptr ); + +/** @brief Makes a clone of an object. + +The function finds the type of a given object and calls clone with the passed object. Of course, if +you know the object type, for example, struct_ptr is CvMat\*, it is faster to call the specific +function, like cvCloneMat. +@param struct_ptr The object to clone + */ +CVAPI(void*) cvClone( const void* struct_ptr ); + +/** @brief Saves an object to a file. + +The function saves an object to a file. It provides a simple interface to cvWrite . +@param filename File name +@param struct_ptr Object to save +@param name Optional object name. If it is NULL, the name will be formed from filename . +@param comment Optional comment to put in the beginning of the file +@param attributes Optional attributes passed to cvWrite + */ +CVAPI(void) cvSave( const char* filename, const void* struct_ptr, + const char* name CV_DEFAULT(NULL), + const char* comment CV_DEFAULT(NULL), + CvAttrList attributes CV_DEFAULT(cvAttrList())); + +/** @brief Loads an object from a file. + +The function loads an object from a file. It basically reads the specified file, find the first +top-level node and calls cvRead for that node. If the file node does not have type information or +the type information can not be found by the type name, the function returns NULL. After the object +is loaded, the file storage is closed and all the temporary buffers are deleted. Thus, to load a +dynamic structure, such as a sequence, contour, or graph, one should pass a valid memory storage +destination to the function. +@param filename File name +@param memstorage Memory storage for dynamic structures, such as CvSeq or CvGraph . It is not used +for matrices or images. +@param name Optional object name. If it is NULL, the first top-level object in the storage will be +loaded. +@param real_name Optional output parameter that will contain the name of the loaded object +(useful if name=NULL ) + */ +CVAPI(void*) cvLoad( const char* filename, + CvMemStorage* memstorage CV_DEFAULT(NULL), + const char* name CV_DEFAULT(NULL), + const char** real_name CV_DEFAULT(NULL) ); + +/*********************************** Measuring Execution Time ***************************/ + +/** helper functions for RNG initialization and accurate time measurement: + uses internal clock counter on x86 */ +CVAPI(int64) cvGetTickCount( void ); +CVAPI(double) cvGetTickFrequency( void ); + +/*********************************** CPU capabilities ***********************************/ + +CVAPI(int) cvCheckHardwareSupport(int feature); + +/*********************************** Multi-Threading ************************************/ + +/** retrieve/set the number of threads used in OpenMP implementations */ +CVAPI(int) cvGetNumThreads( void ); +CVAPI(void) cvSetNumThreads( int threads CV_DEFAULT(0) ); +/** get index of the thread being executed */ +CVAPI(int) cvGetThreadNum( void ); + + +/********************************** Error Handling **************************************/ + +/** Get current OpenCV error status */ +CVAPI(int) cvGetErrStatus( void ); + +/** Sets error status silently */ +CVAPI(void) cvSetErrStatus( int status ); + +#define CV_ErrModeLeaf 0 /* Print error and exit program */ +#define CV_ErrModeParent 1 /* Print error and continue */ +#define CV_ErrModeSilent 2 /* Don't print and continue */ + +/** Retrieves current error processing mode */ +CVAPI(int) cvGetErrMode( void ); + +/** Sets error processing mode, returns previously used mode */ +CVAPI(int) cvSetErrMode( int mode ); + +/** Sets error status and performs some additional actions (displaying message box, + writing message to stderr, terminating application etc.) + depending on the current error mode */ +CVAPI(void) cvError( int status, const char* func_name, + const char* err_msg, const char* file_name, int line ); + +/** Retrieves textual description of the error given its code */ +CVAPI(const char*) cvErrorStr( int status ); + +/** Retrieves detailed information about the last error occurred */ +CVAPI(int) cvGetErrInfo( const char** errcode_desc, const char** description, + const char** filename, int* line ); + +/** Maps IPP error codes to the counterparts from OpenCV */ +CVAPI(int) cvErrorFromIppStatus( int ipp_status ); + +typedef int (CV_CDECL *CvErrorCallback)( int status, const char* func_name, + const char* err_msg, const char* file_name, int line, void* userdata ); + +/** Assigns a new error-handling function */ +CVAPI(CvErrorCallback) cvRedirectError( CvErrorCallback error_handler, + void* userdata CV_DEFAULT(NULL), + void** prev_userdata CV_DEFAULT(NULL) ); + +/** Output nothing */ +CVAPI(int) cvNulDevReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +/** Output to console(fprintf(stderr,...)) */ +CVAPI(int) cvStdErrReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +/** Output to MessageBox(WIN32) */ +CVAPI(int) cvGuiBoxReport( int status, const char* func_name, const char* err_msg, + const char* file_name, int line, void* userdata ); + +#define OPENCV_ERROR(status,func,context) \ +cvError((status),(func),(context),__FILE__,__LINE__) + +#define OPENCV_ASSERT(expr,func,context) \ +{if (! (expr)) \ +{OPENCV_ERROR(CV_StsInternal,(func),(context));}} + +#define OPENCV_CALL( Func ) \ +{ \ +Func; \ +} + + +/** CV_FUNCNAME macro defines icvFuncName constant which is used by CV_ERROR macro */ +#ifdef CV_NO_FUNC_NAMES +#define CV_FUNCNAME( Name ) +#define cvFuncName "" +#else +#define CV_FUNCNAME( Name ) \ +static char cvFuncName[] = Name +#endif + + +/** + CV_ERROR macro unconditionally raises error with passed code and message. + After raising error, control will be transferred to the exit label. + */ +#define CV_ERROR( Code, Msg ) \ +{ \ + cvError( (Code), cvFuncName, Msg, __FILE__, __LINE__ ); \ + __CV_EXIT__; \ +} + +/** + CV_CHECK macro checks error status after CV (or IPL) + function call. If error detected, control will be transferred to the exit + label. + */ +#define CV_CHECK() \ +{ \ + if( cvGetErrStatus() < 0 ) \ + CV_ERROR( CV_StsBackTrace, "Inner function failed." ); \ +} + + +/** + CV_CALL macro calls CV (or IPL) function, checks error status and + signals a error if the function failed. Useful in "parent node" + error processing mode + */ +#define CV_CALL( Func ) \ +{ \ + Func; \ + CV_CHECK(); \ +} + + +/** Runtime assertion macro */ +#define CV_ASSERT( Condition ) \ +{ \ + if( !(Condition) ) \ + CV_ERROR( CV_StsInternal, "Assertion: " #Condition " failed" ); \ +} + +#define __CV_BEGIN__ { +#define __CV_END__ goto exit; exit: ; } +#define __CV_EXIT__ goto exit + +/** @} core_c */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef __cplusplus + +//! @addtogroup core_c_glue +//! @{ + +//! class for automatic module/RTTI data registration/unregistration +struct CV_EXPORTS CvType +{ + CvType( const char* type_name, + CvIsInstanceFunc is_instance, CvReleaseFunc release=0, + CvReadFunc read=0, CvWriteFunc write=0, CvCloneFunc clone=0 ); + ~CvType(); + CvTypeInfo* info; + + static CvTypeInfo* first; + static CvTypeInfo* last; +}; + +//! @} + +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @addtogroup core_c_glue +//! @{ + +/////////////////////////////////////////// glue /////////////////////////////////////////// + +//! converts array (CvMat or IplImage) to cv::Mat +CV_EXPORTS Mat cvarrToMat(const CvArr* arr, bool copyData=false, + bool allowND=true, int coiMode=0, + AutoBuffer* buf=0); + +static inline Mat cvarrToMatND(const CvArr* arr, bool copyData=false, int coiMode=0) +{ + return cvarrToMat(arr, copyData, true, coiMode); +} + + +//! extracts Channel of Interest from CvMat or IplImage and makes cv::Mat out of it. +CV_EXPORTS void extractImageCOI(const CvArr* arr, OutputArray coiimg, int coi=-1); +//! inserts single-channel cv::Mat into a multi-channel CvMat or IplImage +CV_EXPORTS void insertImageCOI(InputArray coiimg, CvArr* arr, int coi=-1); + + + +////// specialized implementations of DefaultDeleter::operator() for classic OpenCV types ////// + +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMat* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(IplImage* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMatND* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvSparseMat* obj) const; +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvMemStorage* obj) const; + +////////////// convenient wrappers for operating old-style dynamic structures ////////////// + +template class SeqIterator; + +typedef Ptr MemStorage; + +/*! + Template Sequence Class derived from CvSeq + + The class provides more convenient access to sequence elements, + STL-style operations and iterators. + + \note The class is targeted for simple data types, + i.e. no constructors or destructors + are called for the sequence elements. +*/ +template class Seq +{ +public: + typedef SeqIterator<_Tp> iterator; + typedef SeqIterator<_Tp> const_iterator; + + //! the default constructor + Seq(); + //! the constructor for wrapping CvSeq structure. The real element type in CvSeq should match _Tp. + Seq(const CvSeq* seq); + //! creates the empty sequence that resides in the specified storage + Seq(MemStorage& storage, int headerSize = sizeof(CvSeq)); + //! returns read-write reference to the specified element + _Tp& operator [](int idx); + //! returns read-only reference to the specified element + const _Tp& operator[](int idx) const; + //! returns iterator pointing to the beginning of the sequence + SeqIterator<_Tp> begin() const; + //! returns iterator pointing to the element following the last sequence element + SeqIterator<_Tp> end() const; + //! returns the number of elements in the sequence + size_t size() const; + //! returns the type of sequence elements (CV_8UC1 ... CV_64FC(CV_CN_MAX) ...) + int type() const; + //! returns the depth of sequence elements (CV_8U ... CV_64F) + int depth() const; + //! returns the number of channels in each sequence element + int channels() const; + //! returns the size of each sequence element + size_t elemSize() const; + //! returns index of the specified sequence element + size_t index(const _Tp& elem) const; + //! appends the specified element to the end of the sequence + void push_back(const _Tp& elem); + //! appends the specified element to the front of the sequence + void push_front(const _Tp& elem); + //! appends zero or more elements to the end of the sequence + void push_back(const _Tp* elems, size_t count); + //! appends zero or more elements to the front of the sequence + void push_front(const _Tp* elems, size_t count); + //! inserts the specified element to the specified position + void insert(int idx, const _Tp& elem); + //! inserts zero or more elements to the specified position + void insert(int idx, const _Tp* elems, size_t count); + //! removes element at the specified position + void remove(int idx); + //! removes the specified subsequence + void remove(const Range& r); + + //! returns reference to the first sequence element + _Tp& front(); + //! returns read-only reference to the first sequence element + const _Tp& front() const; + //! returns reference to the last sequence element + _Tp& back(); + //! returns read-only reference to the last sequence element + const _Tp& back() const; + //! returns true iff the sequence contains no elements + bool empty() const; + + //! removes all the elements from the sequence + void clear(); + //! removes the first element from the sequence + void pop_front(); + //! removes the last element from the sequence + void pop_back(); + //! removes zero or more elements from the beginning of the sequence + void pop_front(_Tp* elems, size_t count); + //! removes zero or more elements from the end of the sequence + void pop_back(_Tp* elems, size_t count); + + //! copies the whole sequence or the sequence slice to the specified vector + void copyTo(std::vector<_Tp>& vec, const Range& range=Range::all()) const; + //! returns the vector containing all the sequence elements + operator std::vector<_Tp>() const; + + CvSeq* seq; +}; + + +/*! + STL-style Sequence Iterator inherited from the CvSeqReader structure +*/ +template class SeqIterator : public CvSeqReader +{ +public: + //! the default constructor + SeqIterator(); + //! the constructor setting the iterator to the beginning or to the end of the sequence + SeqIterator(const Seq<_Tp>& seq, bool seekEnd=false); + //! positions the iterator within the sequence + void seek(size_t pos); + //! reports the current iterator position + size_t tell() const; + //! returns reference to the current sequence element + _Tp& operator *(); + //! returns read-only reference to the current sequence element + const _Tp& operator *() const; + //! moves iterator to the next sequence element + SeqIterator& operator ++(); + //! moves iterator to the next sequence element + SeqIterator operator ++(int) const; + //! moves iterator to the previous sequence element + SeqIterator& operator --(); + //! moves iterator to the previous sequence element + SeqIterator operator --(int) const; + + //! moves iterator forward by the specified offset (possibly negative) + SeqIterator& operator +=(int); + //! moves iterator backward by the specified offset (possibly negative) + SeqIterator& operator -=(int); + + // this is index of the current element module seq->total*2 + // (to distinguish between 0 and seq->total) + int index; +}; + + + +// bridge C++ => C Seq API +CV_EXPORTS schar* seqPush( CvSeq* seq, const void* element=0); +CV_EXPORTS schar* seqPushFront( CvSeq* seq, const void* element=0); +CV_EXPORTS void seqPop( CvSeq* seq, void* element=0); +CV_EXPORTS void seqPopFront( CvSeq* seq, void* element=0); +CV_EXPORTS void seqPopMulti( CvSeq* seq, void* elements, + int count, int in_front=0 ); +CV_EXPORTS void seqRemove( CvSeq* seq, int index ); +CV_EXPORTS void clearSeq( CvSeq* seq ); +CV_EXPORTS schar* getSeqElem( const CvSeq* seq, int index ); +CV_EXPORTS void seqRemoveSlice( CvSeq* seq, CvSlice slice ); +CV_EXPORTS void seqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr ); + +template inline Seq<_Tp>::Seq() : seq(0) {} +template inline Seq<_Tp>::Seq( const CvSeq* _seq ) : seq((CvSeq*)_seq) +{ + CV_Assert(!_seq || _seq->elem_size == sizeof(_Tp)); +} + +template inline Seq<_Tp>::Seq( MemStorage& storage, + int headerSize ) +{ + CV_Assert(headerSize >= (int)sizeof(CvSeq)); + seq = cvCreateSeq(DataType<_Tp>::type, headerSize, sizeof(_Tp), storage); +} + +template inline _Tp& Seq<_Tp>::operator [](int idx) +{ return *(_Tp*)getSeqElem(seq, idx); } + +template inline const _Tp& Seq<_Tp>::operator [](int idx) const +{ return *(_Tp*)getSeqElem(seq, idx); } + +template inline SeqIterator<_Tp> Seq<_Tp>::begin() const +{ return SeqIterator<_Tp>(*this); } + +template inline SeqIterator<_Tp> Seq<_Tp>::end() const +{ return SeqIterator<_Tp>(*this, true); } + +template inline size_t Seq<_Tp>::size() const +{ return seq ? seq->total : 0; } + +template inline int Seq<_Tp>::type() const +{ return seq ? CV_MAT_TYPE(seq->flags) : 0; } + +template inline int Seq<_Tp>::depth() const +{ return seq ? CV_MAT_DEPTH(seq->flags) : 0; } + +template inline int Seq<_Tp>::channels() const +{ return seq ? CV_MAT_CN(seq->flags) : 0; } + +template inline size_t Seq<_Tp>::elemSize() const +{ return seq ? seq->elem_size : 0; } + +template inline size_t Seq<_Tp>::index(const _Tp& elem) const +{ return cvSeqElemIdx(seq, &elem); } + +template inline void Seq<_Tp>::push_back(const _Tp& elem) +{ cvSeqPush(seq, &elem); } + +template inline void Seq<_Tp>::push_front(const _Tp& elem) +{ cvSeqPushFront(seq, &elem); } + +template inline void Seq<_Tp>::push_back(const _Tp* elem, size_t count) +{ cvSeqPushMulti(seq, elem, (int)count, 0); } + +template inline void Seq<_Tp>::push_front(const _Tp* elem, size_t count) +{ cvSeqPushMulti(seq, elem, (int)count, 1); } + +template inline _Tp& Seq<_Tp>::back() +{ return *(_Tp*)getSeqElem(seq, -1); } + +template inline const _Tp& Seq<_Tp>::back() const +{ return *(const _Tp*)getSeqElem(seq, -1); } + +template inline _Tp& Seq<_Tp>::front() +{ return *(_Tp*)getSeqElem(seq, 0); } + +template inline const _Tp& Seq<_Tp>::front() const +{ return *(const _Tp*)getSeqElem(seq, 0); } + +template inline bool Seq<_Tp>::empty() const +{ return !seq || seq->total == 0; } + +template inline void Seq<_Tp>::clear() +{ if(seq) clearSeq(seq); } + +template inline void Seq<_Tp>::pop_back() +{ seqPop(seq); } + +template inline void Seq<_Tp>::pop_front() +{ seqPopFront(seq); } + +template inline void Seq<_Tp>::pop_back(_Tp* elem, size_t count) +{ seqPopMulti(seq, elem, (int)count, 0); } + +template inline void Seq<_Tp>::pop_front(_Tp* elem, size_t count) +{ seqPopMulti(seq, elem, (int)count, 1); } + +template inline void Seq<_Tp>::insert(int idx, const _Tp& elem) +{ seqInsert(seq, idx, &elem); } + +template inline void Seq<_Tp>::insert(int idx, const _Tp* elems, size_t count) +{ + CvMat m = cvMat(1, count, DataType<_Tp>::type, elems); + seqInsertSlice(seq, idx, &m); +} + +template inline void Seq<_Tp>::remove(int idx) +{ seqRemove(seq, idx); } + +template inline void Seq<_Tp>::remove(const Range& r) +{ seqRemoveSlice(seq, cvSlice(r.start, r.end)); } + +template inline void Seq<_Tp>::copyTo(std::vector<_Tp>& vec, const Range& range) const +{ + size_t len = !seq ? 0 : range == Range::all() ? seq->total : range.end - range.start; + vec.resize(len); + if( seq && len ) + cvCvtSeqToArray(seq, &vec[0], cvSlice(range)); +} + +template inline Seq<_Tp>::operator std::vector<_Tp>() const +{ + std::vector<_Tp> vec; + copyTo(vec); + return vec; +} + +template inline SeqIterator<_Tp>::SeqIterator() +{ memset(this, 0, sizeof(*this)); } + +template inline SeqIterator<_Tp>::SeqIterator(const Seq<_Tp>& _seq, bool seekEnd) +{ + cvStartReadSeq(_seq.seq, this); + index = seekEnd ? _seq.seq->total : 0; +} + +template inline void SeqIterator<_Tp>::seek(size_t pos) +{ + cvSetSeqReaderPos(this, (int)pos, false); + index = pos; +} + +template inline size_t SeqIterator<_Tp>::tell() const +{ return index; } + +template inline _Tp& SeqIterator<_Tp>::operator *() +{ return *(_Tp*)ptr; } + +template inline const _Tp& SeqIterator<_Tp>::operator *() const +{ return *(const _Tp*)ptr; } + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator ++() +{ + CV_NEXT_SEQ_ELEM(sizeof(_Tp), *this); + if( ++index >= seq->total*2 ) + index = 0; + return *this; +} + +template inline SeqIterator<_Tp> SeqIterator<_Tp>::operator ++(int) const +{ + SeqIterator<_Tp> it = *this; + ++*this; + return it; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator --() +{ + CV_PREV_SEQ_ELEM(sizeof(_Tp), *this); + if( --index < 0 ) + index = seq->total*2-1; + return *this; +} + +template inline SeqIterator<_Tp> SeqIterator<_Tp>::operator --(int) const +{ + SeqIterator<_Tp> it = *this; + --*this; + return it; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator +=(int delta) +{ + cvSetSeqReaderPos(this, delta, 1); + index += delta; + int n = seq->total*2; + if( index < 0 ) + index += n; + if( index >= n ) + index -= n; + return *this; +} + +template inline SeqIterator<_Tp>& SeqIterator<_Tp>::operator -=(int delta) +{ + return (*this += -delta); +} + +template inline ptrdiff_t operator - (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + ptrdiff_t delta = a.index - b.index, n = a.seq->total; + if( delta > n || delta < -n ) + delta += delta < 0 ? n : -n; + return delta; +} + +template inline bool operator == (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + return a.seq == b.seq && a.index == b.index; +} + +template inline bool operator != (const SeqIterator<_Tp>& a, + const SeqIterator<_Tp>& b) +{ + return !(a == b); +} + +//! @} + +} // cv + +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.hpp new file mode 100644 index 0000000..7d7bb62 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.hpp @@ -0,0 +1,1049 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_HPP +#define OPENCV_CORE_CUDA_HPP + +#ifndef __cplusplus +# error cuda.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "opencv2/core/cuda_types.hpp" + +/** + @defgroup cuda CUDA-accelerated Computer Vision + @{ + @defgroup cudacore Core part + @{ + @defgroup cudacore_init Initialization and Information + @defgroup cudacore_struct Data Structures + @} + @} + */ + +namespace cv { namespace cuda { + +//! @addtogroup cudacore_struct +//! @{ + +//=================================================================================== +// GpuMat +//=================================================================================== + +/** @brief Base storage class for GPU memory with reference counting. + +Its interface matches the Mat interface with the following limitations: + +- no arbitrary dimensions support (only 2D) +- no functions that return references to their data (because references on GPU are not valid for + CPU) +- no expression templates technique support + +Beware that the latter limitation may lead to overloaded matrix operators that cause memory +allocations. The GpuMat class is convertible to cuda::PtrStepSz and cuda::PtrStep so it can be +passed directly to the kernel. + +@note In contrast with Mat, in most cases GpuMat::isContinuous() == false . This means that rows are +aligned to a size depending on the hardware. Single-row GpuMat is always a continuous matrix. + +@note You are not recommended to leave static or global GpuMat variables allocated, that is, to rely +on its destructor. The destruction order of such variables and CUDA context is undefined. GPU memory +release function returns error if the CUDA context has been destroyed before. + +Some member functions are described as a "Blocking Call" while some are described as a +"Non-Blocking Call". Blocking functions are synchronous to host. It is guaranteed that the GPU +operation is finished when the function returns. However, non-blocking functions are asynchronous to +host. Those functions may return even if the GPU operation is not finished. + +Compared to their blocking counterpart, non-blocking functions accept Stream as an additional +argument. If a non-default stream is passed, the GPU operation may overlap with operations in other +streams. + +@sa Mat + */ +class CV_EXPORTS GpuMat +{ +public: + class CV_EXPORTS Allocator + { + public: + virtual ~Allocator() {} + + // allocator must fill data, step and refcount fields + virtual bool allocate(GpuMat* mat, int rows, int cols, size_t elemSize) = 0; + virtual void free(GpuMat* mat) = 0; + }; + + //! default allocator + static Allocator* defaultAllocator(); + static void setDefaultAllocator(Allocator* allocator); + + //! default constructor + explicit GpuMat(Allocator* allocator = defaultAllocator()); + + //! constructs GpuMat of the specified size and type + GpuMat(int rows, int cols, int type, Allocator* allocator = defaultAllocator()); + GpuMat(Size size, int type, Allocator* allocator = defaultAllocator()); + + //! constructs GpuMat and fills it with the specified value _s + GpuMat(int rows, int cols, int type, Scalar s, Allocator* allocator = defaultAllocator()); + GpuMat(Size size, int type, Scalar s, Allocator* allocator = defaultAllocator()); + + //! copy constructor + GpuMat(const GpuMat& m); + + //! constructor for GpuMat headers pointing to user-allocated data + GpuMat(int rows, int cols, int type, void* data, size_t step = Mat::AUTO_STEP); + GpuMat(Size size, int type, void* data, size_t step = Mat::AUTO_STEP); + + //! creates a GpuMat header for a part of the bigger matrix + GpuMat(const GpuMat& m, Range rowRange, Range colRange); + GpuMat(const GpuMat& m, Rect roi); + + //! builds GpuMat from host memory (Blocking call) + explicit GpuMat(InputArray arr, Allocator* allocator = defaultAllocator()); + + //! destructor - calls release() + ~GpuMat(); + + //! assignment operators + GpuMat& operator =(const GpuMat& m); + + //! allocates new GpuMat data unless the GpuMat already has specified size and type + void create(int rows, int cols, int type); + void create(Size size, int type); + + //! decreases reference counter, deallocate the data when reference counter reaches 0 + void release(); + + //! swaps with other smart pointer + void swap(GpuMat& mat); + + /** @brief Performs data upload to GpuMat (Blocking call) + + This function copies data from host memory to device memory. As being a blocking call, it is + guaranteed that the copy operation is finished when this function returns. + */ + void upload(InputArray arr); + + /** @brief Performs data upload to GpuMat (Non-Blocking call) + + This function copies data from host memory to device memory. As being a non-blocking call, this + function may return even if the copy operation is not finished. + + The copy operation may be overlapped with operations in other non-default streams if \p stream is + not the default stream and \p dst is HostMem allocated with HostMem::PAGE_LOCKED option. + */ + void upload(InputArray arr, Stream& stream); + + /** @brief Performs data download from GpuMat (Blocking call) + + This function copies data from device memory to host memory. As being a blocking call, it is + guaranteed that the copy operation is finished when this function returns. + */ + void download(OutputArray dst) const; + + /** @brief Performs data download from GpuMat (Non-Blocking call) + + This function copies data from device memory to host memory. As being a non-blocking call, this + function may return even if the copy operation is not finished. + + The copy operation may be overlapped with operations in other non-default streams if \p stream is + not the default stream and \p dst is HostMem allocated with HostMem::PAGE_LOCKED option. + */ + void download(OutputArray dst, Stream& stream) const; + + //! returns deep copy of the GpuMat, i.e. the data is copied + GpuMat clone() const; + + //! copies the GpuMat content to device memory (Blocking call) + void copyTo(OutputArray dst) const; + + //! copies the GpuMat content to device memory (Non-Blocking call) + void copyTo(OutputArray dst, Stream& stream) const; + + //! copies those GpuMat elements to "m" that are marked with non-zero mask elements (Blocking call) + void copyTo(OutputArray dst, InputArray mask) const; + + //! copies those GpuMat elements to "m" that are marked with non-zero mask elements (Non-Blocking call) + void copyTo(OutputArray dst, InputArray mask, Stream& stream) const; + + //! sets some of the GpuMat elements to s (Blocking call) + GpuMat& setTo(Scalar s); + + //! sets some of the GpuMat elements to s (Non-Blocking call) + GpuMat& setTo(Scalar s, Stream& stream); + + //! sets some of the GpuMat elements to s, according to the mask (Blocking call) + GpuMat& setTo(Scalar s, InputArray mask); + + //! sets some of the GpuMat elements to s, according to the mask (Non-Blocking call) + GpuMat& setTo(Scalar s, InputArray mask, Stream& stream); + + //! converts GpuMat to another datatype (Blocking call) + void convertTo(OutputArray dst, int rtype) const; + + //! converts GpuMat to another datatype (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, Stream& stream) const; + + //! converts GpuMat to another datatype with scaling (Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, double beta = 0.0) const; + + //! converts GpuMat to another datatype with scaling (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const; + + //! converts GpuMat to another datatype with scaling (Non-Blocking call) + void convertTo(OutputArray dst, int rtype, double alpha, double beta, Stream& stream) const; + + void assignTo(GpuMat& m, int type=-1) const; + + //! returns pointer to y-th row + uchar* ptr(int y = 0); + const uchar* ptr(int y = 0) const; + + //! template version of the above method + template _Tp* ptr(int y = 0); + template const _Tp* ptr(int y = 0) const; + + template operator PtrStepSz<_Tp>() const; + template operator PtrStep<_Tp>() const; + + //! returns a new GpuMat header for the specified row + GpuMat row(int y) const; + + //! returns a new GpuMat header for the specified column + GpuMat col(int x) const; + + //! ... for the specified row span + GpuMat rowRange(int startrow, int endrow) const; + GpuMat rowRange(Range r) const; + + //! ... for the specified column span + GpuMat colRange(int startcol, int endcol) const; + GpuMat colRange(Range r) const; + + //! extracts a rectangular sub-GpuMat (this is a generalized form of row, rowRange etc.) + GpuMat operator ()(Range rowRange, Range colRange) const; + GpuMat operator ()(Rect roi) const; + + //! creates alternative GpuMat header for the same data, with different + //! number of channels and/or different number of rows + GpuMat reshape(int cn, int rows = 0) const; + + //! locates GpuMat header within a parent GpuMat + void locateROI(Size& wholeSize, Point& ofs) const; + + //! moves/resizes the current GpuMat ROI inside the parent GpuMat + GpuMat& adjustROI(int dtop, int dbottom, int dleft, int dright); + + //! returns true iff the GpuMat data is continuous + //! (i.e. when there are no gaps between successive rows) + bool isContinuous() const; + + //! returns element size in bytes + size_t elemSize() const; + + //! returns the size of element channel in bytes + size_t elemSize1() const; + + //! returns element type + int type() const; + + //! returns element type + int depth() const; + + //! returns number of channels + int channels() const; + + //! returns step/elemSize1() + size_t step1() const; + + //! returns GpuMat size : width == number of columns, height == number of rows + Size size() const; + + //! returns true if GpuMat data is NULL + bool empty() const; + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + + //! the number of rows and columns + int rows, cols; + + //! a distance between successive rows in bytes; includes the gap if any + size_t step; + + //! pointer to the data + uchar* data; + + //! pointer to the reference counter; + //! when GpuMat points to user-allocated data, the pointer is NULL + int* refcount; + + //! helper fields used in locateROI and adjustROI + uchar* datastart; + const uchar* dataend; + + //! allocator + Allocator* allocator; +}; + +/** @brief Creates a continuous matrix. + +@param rows Row count. +@param cols Column count. +@param type Type of the matrix. +@param arr Destination matrix. This parameter changes only if it has a proper type and area ( +\f$\texttt{rows} \times \texttt{cols}\f$ ). + +Matrix is called continuous if its elements are stored continuously, that is, without gaps at the +end of each row. + */ +CV_EXPORTS void createContinuous(int rows, int cols, int type, OutputArray arr); + +/** @brief Ensures that the size of a matrix is big enough and the matrix has a proper type. + +@param rows Minimum desired number of rows. +@param cols Minimum desired number of columns. +@param type Desired matrix type. +@param arr Destination matrix. + +The function does not reallocate memory if the matrix has proper attributes already. + */ +CV_EXPORTS void ensureSizeIsEnough(int rows, int cols, int type, OutputArray arr); + +/** @brief BufferPool for use with CUDA streams + +BufferPool utilizes Stream's allocator to create new buffers for GpuMat's. It is +only useful when enabled with #setBufferPoolUsage. + +@code + setBufferPoolUsage(true); +@endcode + +@note #setBufferPoolUsage must be called \em before any Stream declaration. + +Users may specify custom allocator for Stream and may implement their own stream based +functions utilizing the same underlying GPU memory management. + +If custom allocator is not specified, BufferPool utilizes StackAllocator by +default. StackAllocator allocates a chunk of GPU device memory beforehand, +and when GpuMat is declared later on, it is given the pre-allocated memory. +This kind of strategy reduces the number of calls for memory allocating APIs +such as cudaMalloc or cudaMallocPitch. + +Below is an example that utilizes BufferPool with StackAllocator: + +@code + #include + + using namespace cv; + using namespace cv::cuda + + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + setBufferPoolConfig(getDevice(), 1024 * 1024 * 64, 2); // Allocate 64 MB, 2 stacks (default is 10 MB, 5 stacks) + + Stream stream1, stream2; // Each stream uses 1 stack + BufferPool pool1(stream1), pool2(stream2); + + GpuMat d_src1 = pool1.getBuffer(4096, 4096, CV_8UC1); // 16MB + GpuMat d_dst1 = pool1.getBuffer(4096, 4096, CV_8UC3); // 48MB, pool1 is now full + + GpuMat d_src2 = pool2.getBuffer(1024, 1024, CV_8UC1); // 1MB + GpuMat d_dst2 = pool2.getBuffer(1024, 1024, CV_8UC3); // 3MB + + cvtColor(d_src1, d_dst1, CV_GRAY2BGR, 0, stream1); + cvtColor(d_src2, d_dst2, CV_GRAY2BGR, 0, stream2); + } +@endcode + +If we allocate another GpuMat on pool1 in the above example, it will be carried out by +the DefaultAllocator since the stack for pool1 is full. + +@code + GpuMat d_add1 = pool1.getBuffer(1024, 1024, CV_8UC1); // Stack for pool1 is full, memory is allocated with DefaultAllocator +@endcode + +If a third stream is declared in the above example, allocating with #getBuffer +within that stream will also be carried out by the DefaultAllocator because we've run out of +stacks. + +@code + Stream stream3; // Only 2 stacks were allocated, we've run out of stacks + BufferPool pool3(stream3); + GpuMat d_src3 = pool3.getBuffer(1024, 1024, CV_8UC1); // Memory is allocated with DefaultAllocator +@endcode + +@warning When utilizing StackAllocator, deallocation order is important. + +Just like a stack, deallocation must be done in LIFO order. Below is an example of +erroneous usage that violates LIFO rule. If OpenCV is compiled in Debug mode, this +sample code will emit CV_Assert error. + +@code + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + Stream stream; // A default size (10 MB) stack is allocated to this stream + BufferPool pool(stream); + + GpuMat mat1 = pool.getBuffer(1024, 1024, CV_8UC1); // Allocate mat1 (1MB) + GpuMat mat2 = pool.getBuffer(1024, 1024, CV_8UC1); // Allocate mat2 (1MB) + + mat1.release(); // erroneous usage : mat2 must be deallocated before mat1 + } +@endcode + +Since C++ local variables are destroyed in the reverse order of construction, +the code sample below satisfies the LIFO rule. Local GpuMat's are deallocated +and the corresponding memory is automatically returned to the pool for later usage. + +@code + int main() + { + setBufferPoolUsage(true); // Tell OpenCV that we are going to utilize BufferPool + setBufferPoolConfig(getDevice(), 1024 * 1024 * 64, 2); // Allocate 64 MB, 2 stacks (default is 10 MB, 5 stacks) + + Stream stream1, stream2; // Each stream uses 1 stack + BufferPool pool1(stream1), pool2(stream2); + + for (int i = 0; i < 10; i++) + { + GpuMat d_src1 = pool1.getBuffer(4096, 4096, CV_8UC1); // 16MB + GpuMat d_dst1 = pool1.getBuffer(4096, 4096, CV_8UC3); // 48MB, pool1 is now full + + GpuMat d_src2 = pool2.getBuffer(1024, 1024, CV_8UC1); // 1MB + GpuMat d_dst2 = pool2.getBuffer(1024, 1024, CV_8UC3); // 3MB + + d_src1.setTo(Scalar(i), stream1); + d_src2.setTo(Scalar(i), stream2); + + cvtColor(d_src1, d_dst1, CV_GRAY2BGR, 0, stream1); + cvtColor(d_src2, d_dst2, CV_GRAY2BGR, 0, stream2); + // The order of destruction of the local variables is: + // d_dst2 => d_src2 => d_dst1 => d_src1 + // LIFO rule is satisfied, this code runs without error + } + } +@endcode + */ +class CV_EXPORTS BufferPool +{ +public: + + //! Gets the BufferPool for the given stream. + explicit BufferPool(Stream& stream); + + //! Allocates a new GpuMat of given size and type. + GpuMat getBuffer(int rows, int cols, int type); + + //! Allocates a new GpuMat of given size and type. + GpuMat getBuffer(Size size, int type) { return getBuffer(size.height, size.width, type); } + + //! Returns the allocator associated with the stream. + Ptr getAllocator() const { return allocator_; } + +private: + Ptr allocator_; +}; + +//! BufferPool management (must be called before Stream creation) +CV_EXPORTS void setBufferPoolUsage(bool on); +CV_EXPORTS void setBufferPoolConfig(int deviceId, size_t stackSize, int stackCount); + +//=================================================================================== +// HostMem +//=================================================================================== + +/** @brief Class with reference counting wrapping special memory type allocation functions from CUDA. + +Its interface is also Mat-like but with additional memory type parameters. + +- **PAGE_LOCKED** sets a page locked memory type used commonly for fast and asynchronous + uploading/downloading data from/to GPU. +- **SHARED** specifies a zero copy memory allocation that enables mapping the host memory to GPU + address space, if supported. +- **WRITE_COMBINED** sets the write combined buffer that is not cached by CPU. Such buffers are + used to supply GPU with data when GPU only reads it. The advantage is a better CPU cache + utilization. + +@note Allocation size of such memory types is usually limited. For more details, see *CUDA 2.2 +Pinned Memory APIs* document or *CUDA C Programming Guide*. + */ +class CV_EXPORTS HostMem +{ +public: + enum AllocType { PAGE_LOCKED = 1, SHARED = 2, WRITE_COMBINED = 4 }; + + static MatAllocator* getAllocator(AllocType alloc_type = PAGE_LOCKED); + + explicit HostMem(AllocType alloc_type = PAGE_LOCKED); + + HostMem(const HostMem& m); + + HostMem(int rows, int cols, int type, AllocType alloc_type = PAGE_LOCKED); + HostMem(Size size, int type, AllocType alloc_type = PAGE_LOCKED); + + //! creates from host memory with coping data + explicit HostMem(InputArray arr, AllocType alloc_type = PAGE_LOCKED); + + ~HostMem(); + + HostMem& operator =(const HostMem& m); + + //! swaps with other smart pointer + void swap(HostMem& b); + + //! returns deep copy of the matrix, i.e. the data is copied + HostMem clone() const; + + //! allocates new matrix data unless the matrix already has specified size and type. + void create(int rows, int cols, int type); + void create(Size size, int type); + + //! creates alternative HostMem header for the same data, with different + //! number of channels and/or different number of rows + HostMem reshape(int cn, int rows = 0) const; + + //! decrements reference counter and released memory if needed. + void release(); + + //! returns matrix header with disabled reference counting for HostMem data. + Mat createMatHeader() const; + + /** @brief Maps CPU memory to GPU address space and creates the cuda::GpuMat header without reference counting + for it. + + This can be done only if memory was allocated with the SHARED flag and if it is supported by the + hardware. Laptops often share video and CPU memory, so address spaces can be mapped, which + eliminates an extra copy. + */ + GpuMat createGpuMatHeader() const; + + // Please see cv::Mat for descriptions + bool isContinuous() const; + size_t elemSize() const; + size_t elemSize1() const; + int type() const; + int depth() const; + int channels() const; + size_t step1() const; + Size size() const; + bool empty() const; + + // Please see cv::Mat for descriptions + int flags; + int rows, cols; + size_t step; + + uchar* data; + int* refcount; + + uchar* datastart; + const uchar* dataend; + + AllocType alloc_type; +}; + +/** @brief Page-locks the memory of matrix and maps it for the device(s). + +@param m Input matrix. + */ +CV_EXPORTS void registerPageLocked(Mat& m); + +/** @brief Unmaps the memory of matrix and makes it pageable again. + +@param m Input matrix. + */ +CV_EXPORTS void unregisterPageLocked(Mat& m); + +//=================================================================================== +// Stream +//=================================================================================== + +/** @brief This class encapsulates a queue of asynchronous calls. + +@note Currently, you may face problems if an operation is enqueued twice with different data. Some +functions use the constant GPU memory, and next call may update the memory before the previous one +has been finished. But calling different operations asynchronously is safe because each operation +has its own constant buffer. Memory copy/upload/download/set operations to the buffers you hold are +also safe. + +@note The Stream class is not thread-safe. Please use different Stream objects for different CPU threads. + +@code +void thread1() +{ + cv::cuda::Stream stream1; + cv::cuda::func1(..., stream1); +} + +void thread2() +{ + cv::cuda::Stream stream2; + cv::cuda::func2(..., stream2); +} +@endcode + +@note By default all CUDA routines are launched in Stream::Null() object, if the stream is not specified by user. +In multi-threading environment the stream objects must be passed explicitly (see previous note). + */ +class CV_EXPORTS Stream +{ + typedef void (Stream::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + +public: + typedef void (*StreamCallback)(int status, void* userData); + + //! creates a new asynchronous stream + Stream(); + + //! creates a new asynchronous stream with custom allocator + Stream(const Ptr& allocator); + + /** @brief Returns true if the current stream queue is finished. Otherwise, it returns false. + */ + bool queryIfComplete() const; + + /** @brief Blocks the current CPU thread until all operations in the stream are complete. + */ + void waitForCompletion(); + + /** @brief Makes a compute stream wait on an event. + */ + void waitEvent(const Event& event); + + /** @brief Adds a callback to be called on the host after all currently enqueued items in the stream have + completed. + + @note Callbacks must not make any CUDA API calls. Callbacks must not perform any synchronization + that may depend on outstanding device work or other callbacks that are not mandated to run earlier. + Callbacks without a mandated order (in independent streams) execute in undefined order and may be + serialized. + */ + void enqueueHostCallback(StreamCallback callback, void* userData); + + //! return Stream object for default CUDA stream + static Stream& Null(); + + //! returns true if stream object is not default (!= 0) + operator bool_type() const; + + class Impl; + +private: + Ptr impl_; + Stream(const Ptr& impl); + + friend struct StreamAccessor; + friend class BufferPool; + friend class DefaultDeviceInitializer; +}; + +class CV_EXPORTS Event +{ +public: + enum CreateFlags + { + DEFAULT = 0x00, /**< Default event flag */ + BLOCKING_SYNC = 0x01, /**< Event uses blocking synchronization */ + DISABLE_TIMING = 0x02, /**< Event will not record timing data */ + INTERPROCESS = 0x04 /**< Event is suitable for interprocess use. DisableTiming must be set */ + }; + + explicit Event(CreateFlags flags = DEFAULT); + + //! records an event + void record(Stream& stream = Stream::Null()); + + //! queries an event's status + bool queryIfComplete() const; + + //! waits for an event to complete + void waitForCompletion(); + + //! computes the elapsed time between events + static float elapsedTime(const Event& start, const Event& end); + + class Impl; + +private: + Ptr impl_; + Event(const Ptr& impl); + + friend struct EventAccessor; +}; + +//! @} cudacore_struct + +//=================================================================================== +// Initialization & Info +//=================================================================================== + +//! @addtogroup cudacore_init +//! @{ + +/** @brief Returns the number of installed CUDA-enabled devices. + +Use this function before any other CUDA functions calls. If OpenCV is compiled without CUDA support, +this function returns 0. If the CUDA driver is not installed, or is incompatible, this function +returns -1. + */ +CV_EXPORTS int getCudaEnabledDeviceCount(); + +/** @brief Sets a device and initializes it for the current thread. + +@param device System index of a CUDA device starting with 0. + +If the call of this function is omitted, a default device is initialized at the fist CUDA usage. + */ +CV_EXPORTS void setDevice(int device); + +/** @brief Returns the current device index set by cuda::setDevice or initialized by default. + */ +CV_EXPORTS int getDevice(); + +/** @brief Explicitly destroys and cleans up all resources associated with the current device in the current +process. + +Any subsequent API call to this device will reinitialize the device. + */ +CV_EXPORTS void resetDevice(); + +/** @brief Enumeration providing CUDA computing features. + */ +enum FeatureSet +{ + FEATURE_SET_COMPUTE_10 = 10, + FEATURE_SET_COMPUTE_11 = 11, + FEATURE_SET_COMPUTE_12 = 12, + FEATURE_SET_COMPUTE_13 = 13, + FEATURE_SET_COMPUTE_20 = 20, + FEATURE_SET_COMPUTE_21 = 21, + FEATURE_SET_COMPUTE_30 = 30, + FEATURE_SET_COMPUTE_32 = 32, + FEATURE_SET_COMPUTE_35 = 35, + FEATURE_SET_COMPUTE_50 = 50, + + GLOBAL_ATOMICS = FEATURE_SET_COMPUTE_11, + SHARED_ATOMICS = FEATURE_SET_COMPUTE_12, + NATIVE_DOUBLE = FEATURE_SET_COMPUTE_13, + WARP_SHUFFLE_FUNCTIONS = FEATURE_SET_COMPUTE_30, + DYNAMIC_PARALLELISM = FEATURE_SET_COMPUTE_35 +}; + +//! checks whether current device supports the given feature +CV_EXPORTS bool deviceSupports(FeatureSet feature_set); + +/** @brief Class providing a set of static methods to check what NVIDIA\* card architecture the CUDA module was +built for. + +According to the CUDA C Programming Guide Version 3.2: "PTX code produced for some specific compute +capability can always be compiled to binary code of greater or equal compute capability". + */ +class CV_EXPORTS TargetArchs +{ +public: + /** @brief The following method checks whether the module was built with the support of the given feature: + + @param feature_set Features to be checked. See :ocvcuda::FeatureSet. + */ + static bool builtWith(FeatureSet feature_set); + + /** @brief There is a set of methods to check whether the module contains intermediate (PTX) or binary CUDA + code for the given architecture(s): + + @param major Major compute capability version. + @param minor Minor compute capability version. + */ + static bool has(int major, int minor); + static bool hasPtx(int major, int minor); + static bool hasBin(int major, int minor); + + static bool hasEqualOrLessPtx(int major, int minor); + static bool hasEqualOrGreater(int major, int minor); + static bool hasEqualOrGreaterPtx(int major, int minor); + static bool hasEqualOrGreaterBin(int major, int minor); +}; + +/** @brief Class providing functionality for querying the specified GPU properties. + */ +class CV_EXPORTS DeviceInfo +{ +public: + //! creates DeviceInfo object for the current GPU + DeviceInfo(); + + /** @brief The constructors. + + @param device_id System index of the CUDA device starting with 0. + + Constructs the DeviceInfo object for the specified device. If device_id parameter is missed, it + constructs an object for the current device. + */ + DeviceInfo(int device_id); + + /** @brief Returns system index of the CUDA device starting with 0. + */ + int deviceID() const; + + //! ASCII string identifying device + const char* name() const; + + //! global memory available on device in bytes + size_t totalGlobalMem() const; + + //! shared memory available per block in bytes + size_t sharedMemPerBlock() const; + + //! 32-bit registers available per block + int regsPerBlock() const; + + //! warp size in threads + int warpSize() const; + + //! maximum pitch in bytes allowed by memory copies + size_t memPitch() const; + + //! maximum number of threads per block + int maxThreadsPerBlock() const; + + //! maximum size of each dimension of a block + Vec3i maxThreadsDim() const; + + //! maximum size of each dimension of a grid + Vec3i maxGridSize() const; + + //! clock frequency in kilohertz + int clockRate() const; + + //! constant memory available on device in bytes + size_t totalConstMem() const; + + //! major compute capability + int majorVersion() const; + + //! minor compute capability + int minorVersion() const; + + //! alignment requirement for textures + size_t textureAlignment() const; + + //! pitch alignment requirement for texture references bound to pitched memory + size_t texturePitchAlignment() const; + + //! number of multiprocessors on device + int multiProcessorCount() const; + + //! specified whether there is a run time limit on kernels + bool kernelExecTimeoutEnabled() const; + + //! device is integrated as opposed to discrete + bool integrated() const; + + //! device can map host memory with cudaHostAlloc/cudaHostGetDevicePointer + bool canMapHostMemory() const; + + enum ComputeMode + { + ComputeModeDefault, /**< default compute mode (Multiple threads can use cudaSetDevice with this device) */ + ComputeModeExclusive, /**< compute-exclusive-thread mode (Only one thread in one process will be able to use cudaSetDevice with this device) */ + ComputeModeProhibited, /**< compute-prohibited mode (No threads can use cudaSetDevice with this device) */ + ComputeModeExclusiveProcess /**< compute-exclusive-process mode (Many threads in one process will be able to use cudaSetDevice with this device) */ + }; + + //! compute mode + ComputeMode computeMode() const; + + //! maximum 1D texture size + int maxTexture1D() const; + + //! maximum 1D mipmapped texture size + int maxTexture1DMipmap() const; + + //! maximum size for 1D textures bound to linear memory + int maxTexture1DLinear() const; + + //! maximum 2D texture dimensions + Vec2i maxTexture2D() const; + + //! maximum 2D mipmapped texture dimensions + Vec2i maxTexture2DMipmap() const; + + //! maximum dimensions (width, height, pitch) for 2D textures bound to pitched memory + Vec3i maxTexture2DLinear() const; + + //! maximum 2D texture dimensions if texture gather operations have to be performed + Vec2i maxTexture2DGather() const; + + //! maximum 3D texture dimensions + Vec3i maxTexture3D() const; + + //! maximum Cubemap texture dimensions + int maxTextureCubemap() const; + + //! maximum 1D layered texture dimensions + Vec2i maxTexture1DLayered() const; + + //! maximum 2D layered texture dimensions + Vec3i maxTexture2DLayered() const; + + //! maximum Cubemap layered texture dimensions + Vec2i maxTextureCubemapLayered() const; + + //! maximum 1D surface size + int maxSurface1D() const; + + //! maximum 2D surface dimensions + Vec2i maxSurface2D() const; + + //! maximum 3D surface dimensions + Vec3i maxSurface3D() const; + + //! maximum 1D layered surface dimensions + Vec2i maxSurface1DLayered() const; + + //! maximum 2D layered surface dimensions + Vec3i maxSurface2DLayered() const; + + //! maximum Cubemap surface dimensions + int maxSurfaceCubemap() const; + + //! maximum Cubemap layered surface dimensions + Vec2i maxSurfaceCubemapLayered() const; + + //! alignment requirements for surfaces + size_t surfaceAlignment() const; + + //! device can possibly execute multiple kernels concurrently + bool concurrentKernels() const; + + //! device has ECC support enabled + bool ECCEnabled() const; + + //! PCI bus ID of the device + int pciBusID() const; + + //! PCI device ID of the device + int pciDeviceID() const; + + //! PCI domain ID of the device + int pciDomainID() const; + + //! true if device is a Tesla device using TCC driver, false otherwise + bool tccDriver() const; + + //! number of asynchronous engines + int asyncEngineCount() const; + + //! device shares a unified address space with the host + bool unifiedAddressing() const; + + //! peak memory clock frequency in kilohertz + int memoryClockRate() const; + + //! global memory bus width in bits + int memoryBusWidth() const; + + //! size of L2 cache in bytes + int l2CacheSize() const; + + //! maximum resident threads per multiprocessor + int maxThreadsPerMultiProcessor() const; + + //! gets free and total device memory + void queryMemory(size_t& totalMemory, size_t& freeMemory) const; + size_t freeMemory() const; + size_t totalMemory() const; + + /** @brief Provides information on CUDA feature support. + + @param feature_set Features to be checked. See cuda::FeatureSet. + + This function returns true if the device has the specified CUDA feature. Otherwise, it returns false + */ + bool supports(FeatureSet feature_set) const; + + /** @brief Checks the CUDA module and device compatibility. + + This function returns true if the CUDA module can be run on the specified device. Otherwise, it + returns false . + */ + bool isCompatible() const; + +private: + int device_id_; +}; + +CV_EXPORTS void printCudaDeviceInfo(int device); +CV_EXPORTS void printShortCudaDeviceInfo(int device); + +/** @brief Converts an array to half precision floating number. + +@param _src input array. +@param _dst output array. +@param stream Stream for the asynchronous version. +@sa convertFp16 +*/ +CV_EXPORTS void convertFp16(InputArray _src, OutputArray _dst, Stream& stream = Stream::Null()); + +//! @} cudacore_init + +}} // namespace cv { namespace cuda { + + +#include "opencv2/core/cuda.inl.hpp" + +#endif /* OPENCV_CORE_CUDA_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.inl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.inl.hpp new file mode 100644 index 0000000..35ae2e4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda.inl.hpp @@ -0,0 +1,631 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDAINL_HPP +#define OPENCV_CORE_CUDAINL_HPP + +#include "opencv2/core/cuda.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { + +//=================================================================================== +// GpuMat +//=================================================================================== + +inline +GpuMat::GpuMat(Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{} + +inline +GpuMat::GpuMat(int rows_, int cols_, int type_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (rows_ > 0 && cols_ > 0) + create(rows_, cols_, type_); +} + +inline +GpuMat::GpuMat(Size size_, int type_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (size_.height > 0 && size_.width > 0) + create(size_.height, size_.width, type_); +} + +inline +GpuMat::GpuMat(int rows_, int cols_, int type_, Scalar s_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (rows_ > 0 && cols_ > 0) + { + create(rows_, cols_, type_); + setTo(s_); + } +} + +inline +GpuMat::GpuMat(Size size_, int type_, Scalar s_, Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + if (size_.height > 0 && size_.width > 0) + { + create(size_.height, size_.width, type_); + setTo(s_); + } +} + +inline +GpuMat::GpuMat(const GpuMat& m) + : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), data(m.data), refcount(m.refcount), datastart(m.datastart), dataend(m.dataend), allocator(m.allocator) +{ + if (refcount) + CV_XADD(refcount, 1); +} + +inline +GpuMat::GpuMat(InputArray arr, Allocator* allocator_) : + flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), allocator(allocator_) +{ + upload(arr); +} + +inline +GpuMat::~GpuMat() +{ + release(); +} + +inline +GpuMat& GpuMat::operator =(const GpuMat& m) +{ + if (this != &m) + { + GpuMat temp(m); + swap(temp); + } + + return *this; +} + +inline +void GpuMat::create(Size size_, int type_) +{ + create(size_.height, size_.width, type_); +} + +inline +void GpuMat::swap(GpuMat& b) +{ + std::swap(flags, b.flags); + std::swap(rows, b.rows); + std::swap(cols, b.cols); + std::swap(step, b.step); + std::swap(data, b.data); + std::swap(datastart, b.datastart); + std::swap(dataend, b.dataend); + std::swap(refcount, b.refcount); + std::swap(allocator, b.allocator); +} + +inline +GpuMat GpuMat::clone() const +{ + GpuMat m; + copyTo(m); + return m; +} + +inline +void GpuMat::copyTo(OutputArray dst, InputArray mask) const +{ + copyTo(dst, mask, Stream::Null()); +} + +inline +GpuMat& GpuMat::setTo(Scalar s) +{ + return setTo(s, Stream::Null()); +} + +inline +GpuMat& GpuMat::setTo(Scalar s, InputArray mask) +{ + return setTo(s, mask, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype) const +{ + convertTo(dst, rtype, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, double beta) const +{ + convertTo(dst, rtype, alpha, beta, Stream::Null()); +} + +inline +void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const +{ + convertTo(dst, rtype, alpha, 0.0, stream); +} + +inline +void GpuMat::assignTo(GpuMat& m, int _type) const +{ + if (_type < 0) + m = *this; + else + convertTo(m, _type); +} + +inline +uchar* GpuMat::ptr(int y) +{ + CV_DbgAssert( (unsigned)y < (unsigned)rows ); + return data + step * y; +} + +inline +const uchar* GpuMat::ptr(int y) const +{ + CV_DbgAssert( (unsigned)y < (unsigned)rows ); + return data + step * y; +} + +template inline +_Tp* GpuMat::ptr(int y) +{ + return (_Tp*)ptr(y); +} + +template inline +const _Tp* GpuMat::ptr(int y) const +{ + return (const _Tp*)ptr(y); +} + +template inline +GpuMat::operator PtrStepSz() const +{ + return PtrStepSz(rows, cols, (T*)data, step); +} + +template inline +GpuMat::operator PtrStep() const +{ + return PtrStep((T*)data, step); +} + +inline +GpuMat GpuMat::row(int y) const +{ + return GpuMat(*this, Range(y, y+1), Range::all()); +} + +inline +GpuMat GpuMat::col(int x) const +{ + return GpuMat(*this, Range::all(), Range(x, x+1)); +} + +inline +GpuMat GpuMat::rowRange(int startrow, int endrow) const +{ + return GpuMat(*this, Range(startrow, endrow), Range::all()); +} + +inline +GpuMat GpuMat::rowRange(Range r) const +{ + return GpuMat(*this, r, Range::all()); +} + +inline +GpuMat GpuMat::colRange(int startcol, int endcol) const +{ + return GpuMat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +GpuMat GpuMat::colRange(Range r) const +{ + return GpuMat(*this, Range::all(), r); +} + +inline +GpuMat GpuMat::operator ()(Range rowRange_, Range colRange_) const +{ + return GpuMat(*this, rowRange_, colRange_); +} + +inline +GpuMat GpuMat::operator ()(Rect roi) const +{ + return GpuMat(*this, roi); +} + +inline +bool GpuMat::isContinuous() const +{ + return (flags & Mat::CONTINUOUS_FLAG) != 0; +} + +inline +size_t GpuMat::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t GpuMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int GpuMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int GpuMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int GpuMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t GpuMat::step1() const +{ + return step / elemSize1(); +} + +inline +Size GpuMat::size() const +{ + return Size(cols, rows); +} + +inline +bool GpuMat::empty() const +{ + return data == 0; +} + +static inline +GpuMat createContinuous(int rows, int cols, int type) +{ + GpuMat m; + createContinuous(rows, cols, type, m); + return m; +} + +static inline +void createContinuous(Size size, int type, OutputArray arr) +{ + createContinuous(size.height, size.width, type, arr); +} + +static inline +GpuMat createContinuous(Size size, int type) +{ + GpuMat m; + createContinuous(size, type, m); + return m; +} + +static inline +void ensureSizeIsEnough(Size size, int type, OutputArray arr) +{ + ensureSizeIsEnough(size.height, size.width, type, arr); +} + +static inline +void swap(GpuMat& a, GpuMat& b) +{ + a.swap(b); +} + +//=================================================================================== +// HostMem +//=================================================================================== + +inline +HostMem::HostMem(AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ +} + +inline +HostMem::HostMem(const HostMem& m) + : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), data(m.data), refcount(m.refcount), datastart(m.datastart), dataend(m.dataend), alloc_type(m.alloc_type) +{ + if( refcount ) + CV_XADD(refcount, 1); +} + +inline +HostMem::HostMem(int rows_, int cols_, int type_, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + if (rows_ > 0 && cols_ > 0) + create(rows_, cols_, type_); +} + +inline +HostMem::HostMem(Size size_, int type_, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + if (size_.height > 0 && size_.width > 0) + create(size_.height, size_.width, type_); +} + +inline +HostMem::HostMem(InputArray arr, AllocType alloc_type_) + : flags(0), rows(0), cols(0), step(0), data(0), refcount(0), datastart(0), dataend(0), alloc_type(alloc_type_) +{ + arr.getMat().copyTo(*this); +} + +inline +HostMem::~HostMem() +{ + release(); +} + +inline +HostMem& HostMem::operator =(const HostMem& m) +{ + if (this != &m) + { + HostMem temp(m); + swap(temp); + } + + return *this; +} + +inline +void HostMem::swap(HostMem& b) +{ + std::swap(flags, b.flags); + std::swap(rows, b.rows); + std::swap(cols, b.cols); + std::swap(step, b.step); + std::swap(data, b.data); + std::swap(datastart, b.datastart); + std::swap(dataend, b.dataend); + std::swap(refcount, b.refcount); + std::swap(alloc_type, b.alloc_type); +} + +inline +HostMem HostMem::clone() const +{ + HostMem m(size(), type(), alloc_type); + createMatHeader().copyTo(m); + return m; +} + +inline +void HostMem::create(Size size_, int type_) +{ + create(size_.height, size_.width, type_); +} + +inline +Mat HostMem::createMatHeader() const +{ + return Mat(size(), type(), data, step); +} + +inline +bool HostMem::isContinuous() const +{ + return (flags & Mat::CONTINUOUS_FLAG) != 0; +} + +inline +size_t HostMem::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t HostMem::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int HostMem::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int HostMem::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int HostMem::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t HostMem::step1() const +{ + return step / elemSize1(); +} + +inline +Size HostMem::size() const +{ + return Size(cols, rows); +} + +inline +bool HostMem::empty() const +{ + return data == 0; +} + +static inline +void swap(HostMem& a, HostMem& b) +{ + a.swap(b); +} + +//=================================================================================== +// Stream +//=================================================================================== + +inline +Stream::Stream(const Ptr& impl) + : impl_(impl) +{ +} + +//=================================================================================== +// Event +//=================================================================================== + +inline +Event::Event(const Ptr& impl) + : impl_(impl) +{ +} + +//=================================================================================== +// Initialization & Info +//=================================================================================== + +inline +bool TargetArchs::has(int major, int minor) +{ + return hasPtx(major, minor) || hasBin(major, minor); +} + +inline +bool TargetArchs::hasEqualOrGreater(int major, int minor) +{ + return hasEqualOrGreaterPtx(major, minor) || hasEqualOrGreaterBin(major, minor); +} + +inline +DeviceInfo::DeviceInfo() +{ + device_id_ = getDevice(); +} + +inline +DeviceInfo::DeviceInfo(int device_id) +{ + CV_Assert( device_id >= 0 && device_id < getCudaEnabledDeviceCount() ); + device_id_ = device_id; +} + +inline +int DeviceInfo::deviceID() const +{ + return device_id_; +} + +inline +size_t DeviceInfo::freeMemory() const +{ + size_t _totalMemory = 0, _freeMemory = 0; + queryMemory(_totalMemory, _freeMemory); + return _freeMemory; +} + +inline +size_t DeviceInfo::totalMemory() const +{ + size_t _totalMemory = 0, _freeMemory = 0; + queryMemory(_totalMemory, _freeMemory); + return _totalMemory; +} + +inline +bool DeviceInfo::supports(FeatureSet feature_set) const +{ + int version = majorVersion() * 10 + minorVersion(); + return version >= feature_set; +} + + +}} // namespace cv { namespace cuda { + +//=================================================================================== +// Mat +//=================================================================================== + +namespace cv { + +inline +Mat::Mat(const cuda::GpuMat& m) + : flags(0), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows) +{ + m.download(*this); +} + +} + +//! @endcond + +#endif // OPENCV_CORE_CUDAINL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/block.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/block.hpp new file mode 100644 index 0000000..c277f0e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/block.hpp @@ -0,0 +1,211 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_BLOCK_HPP +#define OPENCV_CUDA_DEVICE_BLOCK_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Block + { + static __device__ __forceinline__ unsigned int id() + { + return blockIdx.x; + } + + static __device__ __forceinline__ unsigned int stride() + { + return blockDim.x * blockDim.y * blockDim.z; + } + + static __device__ __forceinline__ void sync() + { + __syncthreads(); + } + + static __device__ __forceinline__ int flattenedThreadId() + { + return threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x; + } + + template + static __device__ __forceinline__ void fill(It beg, It end, const T& value) + { + int STRIDE = stride(); + It t = beg + flattenedThreadId(); + + for(; t < end; t += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ void yota(OutIt beg, OutIt end, T value) + { + int STRIDE = stride(); + int tid = flattenedThreadId(); + value += tid; + + for(OutIt t = beg + tid; t < end; t += STRIDE, value += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ void copy(InIt beg, InIt end, OutIt out) + { + int STRIDE = stride(); + InIt t = beg + flattenedThreadId(); + OutIt o = out + (t - beg); + + for(; t < end; t += STRIDE, o += STRIDE) + *o = *t; + } + + template + static __device__ __forceinline__ void transform(InIt beg, InIt end, OutIt out, UnOp op) + { + int STRIDE = stride(); + InIt t = beg + flattenedThreadId(); + OutIt o = out + (t - beg); + + for(; t < end; t += STRIDE, o += STRIDE) + *o = op(*t); + } + + template + static __device__ __forceinline__ void transform(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) + { + int STRIDE = stride(); + InIt1 t1 = beg1 + flattenedThreadId(); + InIt2 t2 = beg2 + flattenedThreadId(); + OutIt o = out + (t1 - beg1); + + for(; t1 < end1; t1 += STRIDE, t2 += STRIDE, o += STRIDE) + *o = op(*t1, *t2); + } + + template + static __device__ __forceinline__ void reduce(volatile T* buffer, BinOp op) + { + int tid = flattenedThreadId(); + T val = buffer[tid]; + + if (CTA_SIZE >= 1024) { if (tid < 512) buffer[tid] = val = op(val, buffer[tid + 512]); __syncthreads(); } + if (CTA_SIZE >= 512) { if (tid < 256) buffer[tid] = val = op(val, buffer[tid + 256]); __syncthreads(); } + if (CTA_SIZE >= 256) { if (tid < 128) buffer[tid] = val = op(val, buffer[tid + 128]); __syncthreads(); } + if (CTA_SIZE >= 128) { if (tid < 64) buffer[tid] = val = op(val, buffer[tid + 64]); __syncthreads(); } + + if (tid < 32) + { + if (CTA_SIZE >= 64) { buffer[tid] = val = op(val, buffer[tid + 32]); } + if (CTA_SIZE >= 32) { buffer[tid] = val = op(val, buffer[tid + 16]); } + if (CTA_SIZE >= 16) { buffer[tid] = val = op(val, buffer[tid + 8]); } + if (CTA_SIZE >= 8) { buffer[tid] = val = op(val, buffer[tid + 4]); } + if (CTA_SIZE >= 4) { buffer[tid] = val = op(val, buffer[tid + 2]); } + if (CTA_SIZE >= 2) { buffer[tid] = val = op(val, buffer[tid + 1]); } + } + } + + template + static __device__ __forceinline__ T reduce(volatile T* buffer, T init, BinOp op) + { + int tid = flattenedThreadId(); + T val = buffer[tid] = init; + __syncthreads(); + + if (CTA_SIZE >= 1024) { if (tid < 512) buffer[tid] = val = op(val, buffer[tid + 512]); __syncthreads(); } + if (CTA_SIZE >= 512) { if (tid < 256) buffer[tid] = val = op(val, buffer[tid + 256]); __syncthreads(); } + if (CTA_SIZE >= 256) { if (tid < 128) buffer[tid] = val = op(val, buffer[tid + 128]); __syncthreads(); } + if (CTA_SIZE >= 128) { if (tid < 64) buffer[tid] = val = op(val, buffer[tid + 64]); __syncthreads(); } + + if (tid < 32) + { + if (CTA_SIZE >= 64) { buffer[tid] = val = op(val, buffer[tid + 32]); } + if (CTA_SIZE >= 32) { buffer[tid] = val = op(val, buffer[tid + 16]); } + if (CTA_SIZE >= 16) { buffer[tid] = val = op(val, buffer[tid + 8]); } + if (CTA_SIZE >= 8) { buffer[tid] = val = op(val, buffer[tid + 4]); } + if (CTA_SIZE >= 4) { buffer[tid] = val = op(val, buffer[tid + 2]); } + if (CTA_SIZE >= 2) { buffer[tid] = val = op(val, buffer[tid + 1]); } + } + __syncthreads(); + return buffer[0]; + } + + template + static __device__ __forceinline__ void reduce_n(T* data, unsigned int n, BinOp op) + { + int ftid = flattenedThreadId(); + int sft = stride(); + + if (sft < n) + { + for (unsigned int i = sft + ftid; i < n; i += sft) + data[ftid] = op(data[ftid], data[i]); + + __syncthreads(); + + n = sft; + } + + while (n > 1) + { + unsigned int half = n/2; + + if (ftid < half) + data[ftid] = op(data[ftid], data[n - ftid - 1]); + + __syncthreads(); + + n = n - half; + } + } + }; +}}} + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_BLOCK_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/border_interpolate.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/border_interpolate.hpp new file mode 100644 index 0000000..874f705 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/border_interpolate.hpp @@ -0,0 +1,722 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_BORDER_INTERPOLATE_HPP +#define OPENCV_CUDA_BORDER_INTERPOLATE_HPP + +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "vec_math.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + ////////////////////////////////////////////////////////////// + // BrdConstant + + template struct BrdRowConstant + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowConstant(int width_, const D& val_ = VecTraits::all(0)) : width(width_), val(val_) {} + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return x >= 0 ? saturate_cast(data[x]) : val; + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return x < width ? saturate_cast(data[x]) : val; + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return (x >= 0 && x < width) ? saturate_cast(data[x]) : val; + } + + int width; + D val; + }; + + template struct BrdColConstant + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColConstant(int height_, const D& val_ = VecTraits::all(0)) : height(height_), val(val_) {} + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return y >= 0 ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return y < height ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return (y >= 0 && y < height) ? saturate_cast(*(const T*)((const char*)data + y * step)) : val; + } + + int height; + D val; + }; + + template struct BrdConstant + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdConstant(int height_, int width_, const D& val_ = VecTraits::all(0)) : height(height_), width(width_), val(val_) + { + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(((const T*)((const uchar*)data + y * step))[x]) : val; + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(src(y, x)) : val; + } + + int height; + int width; + D val; + }; + + ////////////////////////////////////////////////////////////// + // BrdReplicate + + template struct BrdRowReplicate + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReplicate(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReplicate(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::max(x, 0); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::min(x, last_col); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReplicate + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReplicate(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReplicate(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::max(y, 0); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::min(y, last_row); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const T*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReplicate + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReplicate(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReplicate(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::max(y, 0); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::min(y, last_row); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::max(x, 0); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::min(x, last_col); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdReflect101 + + template struct BrdRowReflect101 + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReflect101(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReflect101(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::abs(x) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReflect101 + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReflect101(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReflect101(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::abs(y) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReflect101 + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReflect101(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReflect101(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return ::abs(y) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return ::abs(x) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdReflect + + template struct BrdRowReflect + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowReflect(int width) : last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdRowReflect(int width, U) : last_col(width - 1) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (::abs(x) - (x < 0)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return ::abs(last_col - ::abs(last_col - x) + (x > last_col)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(::abs(x) - (x < 0)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int last_col; + }; + + template struct BrdColReflect + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColReflect(int height) : last_row(height - 1) {} + template __host__ __device__ __forceinline__ BrdColReflect(int height, U) : last_row(height - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (::abs(y) - (y < 0)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return ::abs(last_row - ::abs(last_row - y) + (y > last_row)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(::abs(y) - (y < 0)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int last_row; + }; + + template struct BrdReflect + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdReflect(int height, int width) : last_row(height - 1), last_col(width - 1) {} + template __host__ __device__ __forceinline__ BrdReflect(int height, int width, U) : last_row(height - 1), last_col(width - 1) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (::abs(y) - (y < 0)) % (last_row + 1); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return /*::abs*/(last_row - ::abs(last_row - y) + (y > last_row)) /*% (last_row + 1)*/; + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_low(idx_row_high(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (::abs(x) - (x < 0)) % (last_col + 1); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (last_col - ::abs(last_col - x) + (x > last_col)); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_low(idx_col_high(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int last_row; + int last_col; + }; + + ////////////////////////////////////////////////////////////// + // BrdWrap + + template struct BrdRowWrap + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdRowWrap(int width_) : width(width_) {} + template __host__ __device__ __forceinline__ BrdRowWrap(int width_, U) : width(width_) {} + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (x >= 0) * x + (x < 0) * (x - ((x - width + 1) / width) * width); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (x < width) * x + (x >= width) * (x % width); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(idx_col_low(x)); + } + + template __device__ __forceinline__ D at_low(int x, const T* data) const + { + return saturate_cast(data[idx_col_low(x)]); + } + + template __device__ __forceinline__ D at_high(int x, const T* data) const + { + return saturate_cast(data[idx_col_high(x)]); + } + + template __device__ __forceinline__ D at(int x, const T* data) const + { + return saturate_cast(data[idx_col(x)]); + } + + int width; + }; + + template struct BrdColWrap + { + typedef D result_type; + + explicit __host__ __device__ __forceinline__ BrdColWrap(int height_) : height(height_) {} + template __host__ __device__ __forceinline__ BrdColWrap(int height_, U) : height(height_) {} + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (y >= 0) * y + (y < 0) * (y - ((y - height + 1) / height) * height); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return (y < height) * y + (y >= height) * (y % height); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(idx_row_low(y)); + } + + template __device__ __forceinline__ D at_low(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_low(y) * step)); + } + + template __device__ __forceinline__ D at_high(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row_high(y) * step)); + } + + template __device__ __forceinline__ D at(int y, const T* data, size_t step) const + { + return saturate_cast(*(const D*)((const char*)data + idx_row(y) * step)); + } + + int height; + }; + + template struct BrdWrap + { + typedef D result_type; + + __host__ __device__ __forceinline__ BrdWrap(int height_, int width_) : + height(height_), width(width_) + { + } + template + __host__ __device__ __forceinline__ BrdWrap(int height_, int width_, U) : + height(height_), width(width_) + { + } + + __device__ __forceinline__ int idx_row_low(int y) const + { + return (y >= 0) ? y : (y - ((y - height + 1) / height) * height); + } + + __device__ __forceinline__ int idx_row_high(int y) const + { + return (y < height) ? y : (y % height); + } + + __device__ __forceinline__ int idx_row(int y) const + { + return idx_row_high(idx_row_low(y)); + } + + __device__ __forceinline__ int idx_col_low(int x) const + { + return (x >= 0) ? x : (x - ((x - width + 1) / width) * width); + } + + __device__ __forceinline__ int idx_col_high(int x) const + { + return (x < width) ? x : (x % width); + } + + __device__ __forceinline__ int idx_col(int x) const + { + return idx_col_high(idx_col_low(x)); + } + + template __device__ __forceinline__ D at(int y, int x, const T* data, size_t step) const + { + return saturate_cast(((const T*)((const char*)data + idx_row(y) * step))[idx_col(x)]); + } + + template __device__ __forceinline__ D at(typename Ptr2D::index_type y, typename Ptr2D::index_type x, const Ptr2D& src) const + { + return saturate_cast(src(idx_row(y), idx_col(x))); + } + + int height; + int width; + }; + + ////////////////////////////////////////////////////////////// + // BorderReader + + template struct BorderReader + { + typedef typename B::result_type elem_type; + typedef typename Ptr2D::index_type index_type; + + __host__ __device__ __forceinline__ BorderReader(const Ptr2D& ptr_, const B& b_) : ptr(ptr_), b(b_) {} + + __device__ __forceinline__ elem_type operator ()(index_type y, index_type x) const + { + return b.at(y, x, ptr); + } + + Ptr2D ptr; + B b; + }; + + // under win32 there is some bug with templated types that passed as kernel parameters + // with this specialization all works fine + template struct BorderReader< Ptr2D, BrdConstant > + { + typedef typename BrdConstant::result_type elem_type; + typedef typename Ptr2D::index_type index_type; + + __host__ __device__ __forceinline__ BorderReader(const Ptr2D& src_, const BrdConstant& b) : + src(src_), height(b.height), width(b.width), val(b.val) + { + } + + __device__ __forceinline__ D operator ()(index_type y, index_type x) const + { + return (x >= 0 && x < width && y >= 0 && y < height) ? saturate_cast(src(y, x)) : val; + } + + Ptr2D src; + int height; + int width; + D val; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_BORDER_INTERPOLATE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/color.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/color.hpp new file mode 100644 index 0000000..dcce280 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/color.hpp @@ -0,0 +1,309 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COLOR_HPP +#define OPENCV_CUDA_COLOR_HPP + +#include "detail/color_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // All OPENCV_CUDA_IMPLEMENT_*_TRAITS(ColorSpace1_to_ColorSpace2, ...) macros implements + // template class ColorSpace1_to_ColorSpace2_traits + // { + // typedef ... functor_type; + // static __host__ __device__ functor_type create_functor(); + // }; + + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgr_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(bgra_to_rgba, 4, 4, 2) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgr_to_bgr555, 3, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgr_to_bgr565, 3, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgb_to_bgr555, 3, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgb_to_bgr565, 3, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgra_to_bgr555, 4, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(bgra_to_bgr565, 4, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgba_to_bgr555, 4, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(rgba_to_bgr565, 4, 2, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_rgb, 3, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_rgb, 3, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_bgr, 3, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_bgr, 3, 0, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_rgba, 4, 2, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_rgba, 4, 2, 6) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr555_to_bgra, 4, 0, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(bgr565_to_bgra, 4, 0, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(gray_to_bgr, 3) + OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(gray_to_bgra, 4) + + #undef OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(gray_to_bgr555, 5) + OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(gray_to_bgr565, 6) + + #undef OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(bgr555_to_gray, 5) + OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(bgr565_to_gray, 6) + + #undef OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(rgb_to_gray, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(bgr_to_gray, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(rgba_to_gray, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(bgra_to_gray, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgb_to_yuv, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgba_to_yuv, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgb_to_yuv4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(rgba_to_yuv4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgr_to_yuv, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgra_to_yuv, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgr_to_yuv4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(bgra_to_yuv4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS + + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(yuv4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgb_to_YCrCb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgba_to_YCrCb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgb_to_YCrCb4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(rgba_to_YCrCb4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgr_to_YCrCb, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgra_to_YCrCb, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgr_to_YCrCb4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(bgra_to_YCrCb4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS + + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(YCrCb4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgb_to_xyz, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgba_to_xyz, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgb_to_xyz4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(rgba_to_xyz4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgr_to_xyz, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgra_to_xyz, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgr_to_xyz4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(bgra_to_xyz4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS + + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(xyz4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgb_to_hsv, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgba_to_hsv, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgb_to_hsv4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(rgba_to_hsv4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgr_to_hsv, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgra_to_hsv, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgr_to_hsv4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(bgra_to_hsv4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS + + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(hsv4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgb_to_hls, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgba_to_hls, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgb_to_hls4, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(rgba_to_hls4, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgr_to_hls, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgra_to_hls, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgr_to_hls4, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(bgra_to_hls4, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS + + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_rgb, 3, 3, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_rgba, 3, 4, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_rgb, 4, 3, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_rgba, 4, 4, 2) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_bgr, 3, 3, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls_to_bgra, 3, 4, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_bgr, 4, 3, 0) + OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(hls4_to_bgra, 4, 4, 0) + + #undef OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgb_to_lab, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgba_to_lab, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgb_to_lab4, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(rgba_to_lab4, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgr_to_lab, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgra_to_lab, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgr_to_lab4, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(bgra_to_lab4, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgb_to_lab, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgba_to_lab, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgb_to_lab4, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lrgba_to_lab4, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgr_to_lab, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgra_to_lab, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgr_to_lab4, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(lbgra_to_lab4, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS + + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_rgb, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_rgb, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_rgba, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_rgba, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_bgr, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_bgr, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_bgra, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_bgra, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lrgb, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lrgb, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lrgba, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lrgba, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lbgr, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lbgr, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab_to_lbgra, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(lab4_to_lbgra, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS + + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgb_to_luv, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgba_to_luv, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgb_to_luv4, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(rgba_to_luv4, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgr_to_luv, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgra_to_luv, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgr_to_luv4, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(bgra_to_luv4, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgb_to_luv, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgba_to_luv, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgb_to_luv4, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lrgba_to_luv4, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgr_to_luv, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgra_to_luv, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgr_to_luv4, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(lbgra_to_luv4, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS + + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_rgb, 3, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_rgb, 4, 3, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_rgba, 3, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_rgba, 4, 4, true, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_bgr, 3, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_bgr, 4, 3, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_bgra, 3, 4, true, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_bgra, 4, 4, true, 0) + + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lrgb, 3, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lrgb, 4, 3, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lrgba, 3, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lrgba, 4, 4, false, 2) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lbgr, 3, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lbgr, 4, 3, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv_to_lbgra, 3, 4, false, 0) + OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(luv4_to_lbgra, 4, 4, false, 0) + + #undef OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_COLOR_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/common.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/common.hpp new file mode 100644 index 0000000..7772159 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/common.hpp @@ -0,0 +1,125 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COMMON_HPP +#define OPENCV_CUDA_COMMON_HPP + +#include +#include "opencv2/core/cuda_types.hpp" +#include "opencv2/core/cvdef.h" +#include "opencv2/core/base.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +#ifndef CV_PI_F + #ifndef CV_PI + #define CV_PI_F 3.14159265f + #else + #define CV_PI_F ((float)CV_PI) + #endif +#endif + +namespace cv { namespace cuda { + static inline void checkCudaError(cudaError_t err, const char* file, const int line, const char* func) + { + if (cudaSuccess != err) + cv::error(cv::Error::GpuApiCallError, cudaGetErrorString(err), func, file, line); + } +}} + +#ifndef cudaSafeCall + #define cudaSafeCall(expr) cv::cuda::checkCudaError(expr, __FILE__, __LINE__, CV_Func) +#endif + +namespace cv { namespace cuda +{ + template static inline bool isAligned(const T* ptr, size_t size) + { + return reinterpret_cast(ptr) % size == 0; + } + + static inline bool isAligned(size_t step, size_t size) + { + return step % size == 0; + } +}} + +namespace cv { namespace cuda +{ + namespace device + { + __host__ __device__ __forceinline__ int divUp(int total, int grain) + { + return (total + grain - 1) / grain; + } + + template inline void bindTexture(const textureReference* tex, const PtrStepSz& img) + { + cudaChannelFormatDesc desc = cudaCreateChannelDesc(); + cudaSafeCall( cudaBindTexture2D(0, tex, img.ptr(), &desc, img.cols, img.rows, img.step) ); + } + +#ifdef WIN32 + template inline void createTextureObjectPitch2D(cudaTextureObject_t* tex, PtrStepSz& img, const cudaTextureDesc& texDesc) + { + cudaResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = cudaResourceTypePitch2D; + resDesc.res.pitch2D.devPtr = static_cast(img.ptr()); + resDesc.res.pitch2D.height = img.rows; + resDesc.res.pitch2D.width = img.cols; + resDesc.res.pitch2D.pitchInBytes = img.step; + resDesc.res.pitch2D.desc = cudaCreateChannelDesc(); + + cudaSafeCall( cudaCreateTextureObject(tex, &resDesc, &texDesc, NULL) ); + } +#endif + } +}} + +//! @endcond + +#endif // OPENCV_CUDA_COMMON_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/datamov_utils.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/datamov_utils.hpp new file mode 100644 index 0000000..6820d0f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/datamov_utils.hpp @@ -0,0 +1,113 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DATAMOV_UTILS_HPP +#define OPENCV_CUDA_DATAMOV_UTILS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 200 + + // for Fermi memory space is detected automatically + template struct ForceGlob + { + __device__ __forceinline__ static void Load(const T* ptr, int offset, T& val) { val = ptr[offset]; } + }; + + #else // __CUDA_ARCH__ >= 200 + + #if defined(_WIN64) || defined(__LP64__) + // 64-bit register modifier for inlined asm + #define OPENCV_CUDA_ASM_PTR "l" + #else + // 32-bit register modifier for inlined asm + #define OPENCV_CUDA_ASM_PTR "r" + #endif + + template struct ForceGlob; + + #define OPENCV_CUDA_DEFINE_FORCE_GLOB(base_type, ptx_type, reg_mod) \ + template <> struct ForceGlob \ + { \ + __device__ __forceinline__ static void Load(const base_type* ptr, int offset, base_type& val) \ + { \ + asm("ld.global."#ptx_type" %0, [%1];" : "="#reg_mod(val) : OPENCV_CUDA_ASM_PTR(ptr + offset)); \ + } \ + }; + + #define OPENCV_CUDA_DEFINE_FORCE_GLOB_B(base_type, ptx_type) \ + template <> struct ForceGlob \ + { \ + __device__ __forceinline__ static void Load(const base_type* ptr, int offset, base_type& val) \ + { \ + asm("ld.global."#ptx_type" %0, [%1];" : "=r"(*reinterpret_cast(&val)) : OPENCV_CUDA_ASM_PTR(ptr + offset)); \ + } \ + }; + + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(uchar, u8) + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(schar, s8) + OPENCV_CUDA_DEFINE_FORCE_GLOB_B(char, b8) + OPENCV_CUDA_DEFINE_FORCE_GLOB (ushort, u16, h) + OPENCV_CUDA_DEFINE_FORCE_GLOB (short, s16, h) + OPENCV_CUDA_DEFINE_FORCE_GLOB (uint, u32, r) + OPENCV_CUDA_DEFINE_FORCE_GLOB (int, s32, r) + OPENCV_CUDA_DEFINE_FORCE_GLOB (float, f32, f) + OPENCV_CUDA_DEFINE_FORCE_GLOB (double, f64, d) + + #undef OPENCV_CUDA_DEFINE_FORCE_GLOB + #undef OPENCV_CUDA_DEFINE_FORCE_GLOB_B + #undef OPENCV_CUDA_ASM_PTR + + #endif // __CUDA_ARCH__ >= 200 +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_DATAMOV_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/color_detail.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/color_detail.hpp new file mode 100644 index 0000000..bfb4055 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/color_detail.hpp @@ -0,0 +1,1980 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_COLOR_DETAIL_HPP +#define OPENCV_CUDA_COLOR_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" +#include "../saturate_cast.hpp" +#include "../limits.hpp" +#include "../functional.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + #ifndef CV_DESCALE + #define CV_DESCALE(x, n) (((x) + (1 << ((n)-1))) >> (n)) + #endif + + namespace color_detail + { + template struct ColorChannel + { + typedef float worktype_f; + static __device__ __forceinline__ T max() { return numeric_limits::max(); } + static __device__ __forceinline__ T half() { return (T)(max()/2 + 1); } + }; + + template<> struct ColorChannel + { + typedef float worktype_f; + static __device__ __forceinline__ float max() { return 1.f; } + static __device__ __forceinline__ float half() { return 0.5f; } + }; + + template static __device__ __forceinline__ void setAlpha(typename TypeVec::vec_type& vec, T val) + { + } + + template static __device__ __forceinline__ void setAlpha(typename TypeVec::vec_type& vec, T val) + { + vec.w = val; + } + + template static __device__ __forceinline__ T getAlpha(const typename TypeVec::vec_type& vec) + { + return ColorChannel::max(); + } + + template static __device__ __forceinline__ T getAlpha(const typename TypeVec::vec_type& vec) + { + return vec.w; + } + + enum + { + yuv_shift = 14, + xyz_shift = 12, + R2Y = 4899, + G2Y = 9617, + B2Y = 1868, + BLOCK_SIZE = 256 + }; + } + +////////////////// Various 3/4-channel to 3/4-channel RGB transformations ///////////////// + + namespace color_detail + { + template struct RGB2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + dst.x = (&src.x)[bidx]; + dst.y = src.y; + dst.z = (&src.x)[bidx^2]; + setAlpha(dst, getAlpha(src)); + + return dst; + } + + __host__ __device__ __forceinline__ RGB2RGB() {} + __host__ __device__ __forceinline__ RGB2RGB(const RGB2RGB&) {} + }; + + template <> struct RGB2RGB : unary_function + { + __device__ uint operator()(uint src) const + { + uint dst = 0; + + dst |= (0xffu & (src >> 16)); + dst |= (0xffu & (src >> 8)) << 8; + dst |= (0xffu & (src)) << 16; + dst |= (0xffu & (src >> 24)) << 24; + + return dst; + } + + __host__ __device__ __forceinline__ RGB2RGB() {} + __host__ __device__ __forceinline__ RGB2RGB(const RGB2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +/////////// Transforming 16-bit (565 or 555) RGB to/from 24/32-bit (888[8]) RGB ////////// + + namespace color_detail + { + template struct RGB2RGB5x5Converter; + template struct RGB2RGB5x5Converter<6, bidx> + { + static __device__ __forceinline__ ushort cvt(const uchar3& src) + { + return (ushort)(((&src.x)[bidx] >> 3) | ((src.y & ~3) << 3) | (((&src.x)[bidx^2] & ~7) << 8)); + } + + static __device__ __forceinline__ ushort cvt(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + return (ushort)((b >> 3) | ((g & ~3) << 3) | ((r & ~7) << 8)); + } + }; + + template struct RGB2RGB5x5Converter<5, bidx> + { + static __device__ __forceinline__ ushort cvt(const uchar3& src) + { + return (ushort)(((&src.x)[bidx] >> 3) | ((src.y & ~7) << 2) | (((&src.x)[bidx^2] & ~7) << 7)); + } + + static __device__ __forceinline__ ushort cvt(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + uint a = 0xffu & (src >> 24); + return (ushort)((b >> 3) | ((g & ~7) << 2) | ((r & ~7) << 7) | (a * 0x8000)); + } + }; + + template struct RGB2RGB5x5; + + template struct RGB2RGB5x5<3, bidx,green_bits> : unary_function + { + __device__ __forceinline__ ushort operator()(const uchar3& src) const + { + return RGB2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ RGB2RGB5x5() {} + __host__ __device__ __forceinline__ RGB2RGB5x5(const RGB2RGB5x5&) {} + }; + + template struct RGB2RGB5x5<4, bidx,green_bits> : unary_function + { + __device__ __forceinline__ ushort operator()(uint src) const + { + return RGB2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ RGB2RGB5x5() {} + __host__ __device__ __forceinline__ RGB2RGB5x5(const RGB2RGB5x5&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2RGB5x5_TRAITS(name, scn, bidx, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2RGB5x5 functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template struct RGB5x52RGBConverter; + + template struct RGB5x52RGBConverter<5, bidx> + { + static __device__ __forceinline__ void cvt(uint src, uchar3& dst) + { + (&dst.x)[bidx] = src << 3; + dst.y = (src >> 2) & ~7; + (&dst.x)[bidx ^ 2] = (src >> 7) & ~7; + } + + static __device__ __forceinline__ void cvt(uint src, uint& dst) + { + dst = 0; + + dst |= (0xffu & (src << 3)) << (bidx * 8); + dst |= (0xffu & ((src >> 2) & ~7)) << 8; + dst |= (0xffu & ((src >> 7) & ~7)) << ((bidx ^ 2) * 8); + dst |= ((src & 0x8000) * 0xffu) << 24; + } + }; + + template struct RGB5x52RGBConverter<6, bidx> + { + static __device__ __forceinline__ void cvt(uint src, uchar3& dst) + { + (&dst.x)[bidx] = src << 3; + dst.y = (src >> 3) & ~3; + (&dst.x)[bidx ^ 2] = (src >> 8) & ~7; + } + + static __device__ __forceinline__ void cvt(uint src, uint& dst) + { + dst = 0xffu << 24; + + dst |= (0xffu & (src << 3)) << (bidx * 8); + dst |= (0xffu &((src >> 3) & ~3)) << 8; + dst |= (0xffu & ((src >> 8) & ~7)) << ((bidx ^ 2) * 8); + } + }; + + template struct RGB5x52RGB; + + template struct RGB5x52RGB<3, bidx, green_bits> : unary_function + { + __device__ __forceinline__ uchar3 operator()(ushort src) const + { + uchar3 dst; + RGB5x52RGBConverter::cvt(src, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB5x52RGB() {} + __host__ __device__ __forceinline__ RGB5x52RGB(const RGB5x52RGB&) {} + + }; + + template struct RGB5x52RGB<4, bidx, green_bits> : unary_function + { + __device__ __forceinline__ uint operator()(ushort src) const + { + uint dst; + RGB5x52RGBConverter::cvt(src, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB5x52RGB() {} + __host__ __device__ __forceinline__ RGB5x52RGB(const RGB5x52RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB5x52RGB_TRAITS(name, dcn, bidx, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB5x52RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////// Grayscale to Color //////////////////////////////// + + namespace color_detail + { + template struct Gray2RGB : unary_function::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(T src) const + { + typename TypeVec::vec_type dst; + + dst.z = dst.y = dst.x = src; + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ Gray2RGB() {} + __host__ __device__ __forceinline__ Gray2RGB(const Gray2RGB&) {} + }; + + template <> struct Gray2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + uint dst = 0xffu << 24; + + dst |= src; + dst |= src << 8; + dst |= src << 16; + + return dst; + } + __host__ __device__ __forceinline__ Gray2RGB() {} + __host__ __device__ __forceinline__ Gray2RGB(const Gray2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_GRAY2RGB_TRAITS(name, dcn) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Gray2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template struct Gray2RGB5x5Converter; + template<> struct Gray2RGB5x5Converter<6> + { + static __device__ __forceinline__ ushort cvt(uint t) + { + return (ushort)((t >> 3) | ((t & ~3) << 3) | ((t & ~7) << 8)); + } + }; + + template<> struct Gray2RGB5x5Converter<5> + { + static __device__ __forceinline__ ushort cvt(uint t) + { + t >>= 3; + return (ushort)(t | (t << 5) | (t << 10)); + } + }; + + template struct Gray2RGB5x5 : unary_function + { + __device__ __forceinline__ ushort operator()(uint src) const + { + return Gray2RGB5x5Converter::cvt(src); + } + + __host__ __device__ __forceinline__ Gray2RGB5x5() {} + __host__ __device__ __forceinline__ Gray2RGB5x5(const Gray2RGB5x5&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_GRAY2RGB5x5_TRAITS(name, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Gray2RGB5x5 functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////// Color to Grayscale //////////////////////////////// + + namespace color_detail + { + template struct RGB5x52GrayConverter; + template <> struct RGB5x52GrayConverter<6> + { + static __device__ __forceinline__ uchar cvt(uint t) + { + return (uchar)CV_DESCALE(((t << 3) & 0xf8) * B2Y + ((t >> 3) & 0xfc) * G2Y + ((t >> 8) & 0xf8) * R2Y, yuv_shift); + } + }; + + template <> struct RGB5x52GrayConverter<5> + { + static __device__ __forceinline__ uchar cvt(uint t) + { + return (uchar)CV_DESCALE(((t << 3) & 0xf8) * B2Y + ((t >> 2) & 0xf8) * G2Y + ((t >> 7) & 0xf8) * R2Y, yuv_shift); + } + }; + + template struct RGB5x52Gray : unary_function + { + __device__ __forceinline__ uchar operator()(uint src) const + { + return RGB5x52GrayConverter::cvt(src); + } + __host__ __device__ __forceinline__ RGB5x52Gray() {} + __host__ __device__ __forceinline__ RGB5x52Gray(const RGB5x52Gray&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB5x52GRAY_TRAITS(name, green_bits) \ + struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB5x52Gray functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template static __device__ __forceinline__ T RGB2GrayConvert(const T* src) + { + return (T)CV_DESCALE((unsigned)(src[bidx] * B2Y + src[1] * G2Y + src[bidx^2] * R2Y), yuv_shift); + } + + template static __device__ __forceinline__ uchar RGB2GrayConvert(uint src) + { + uint b = 0xffu & (src >> (bidx * 8)); + uint g = 0xffu & (src >> 8); + uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + return CV_DESCALE((uint)(b * B2Y + g * G2Y + r * R2Y), yuv_shift); + } + + template static __device__ __forceinline__ float RGB2GrayConvert(const float* src) + { + return src[bidx] * 0.114f + src[1] * 0.587f + src[bidx^2] * 0.299f; + } + + template struct RGB2Gray : unary_function::vec_type, T> + { + __device__ __forceinline__ T operator()(const typename TypeVec::vec_type& src) const + { + return RGB2GrayConvert(&src.x); + } + __host__ __device__ __forceinline__ RGB2Gray() {} + __host__ __device__ __forceinline__ RGB2Gray(const RGB2Gray&) {} + }; + + template struct RGB2Gray : unary_function + { + __device__ __forceinline__ uchar operator()(uint src) const + { + return RGB2GrayConvert(src); + } + __host__ __device__ __forceinline__ RGB2Gray() {} + __host__ __device__ __forceinline__ RGB2Gray(const RGB2Gray&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2GRAY_TRAITS(name, scn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Gray functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> YUV ////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2YUVCoeffs_f[5] = { 0.114f, 0.587f, 0.299f, 0.492f, 0.877f }; + __constant__ int c_RGB2YUVCoeffs_i[5] = { B2Y, G2Y, R2Y, 8061, 14369 }; + + template static __device__ void RGB2YUVConvert(const T* src, D& dst) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE(src[0] * c_RGB2YUVCoeffs_i[bidx^2] + src[1] * c_RGB2YUVCoeffs_i[1] + src[2] * c_RGB2YUVCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE((src[bidx^2] - Y) * c_RGB2YUVCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE((src[bidx] - Y) * c_RGB2YUVCoeffs_i[4] + delta, yuv_shift); + + dst.x = saturate_cast(Y); + dst.y = saturate_cast(Cr); + dst.z = saturate_cast(Cb); + } + + template static __device__ __forceinline__ void RGB2YUVConvert(const float* src, D& dst) + { + dst.x = src[0] * c_RGB2YUVCoeffs_f[bidx^2] + src[1] * c_RGB2YUVCoeffs_f[1] + src[2] * c_RGB2YUVCoeffs_f[bidx]; + dst.y = (src[bidx^2] - dst.x) * c_RGB2YUVCoeffs_f[3] + ColorChannel::half(); + dst.z = (src[bidx] - dst.x) * c_RGB2YUVCoeffs_f[4] + ColorChannel::half(); + } + + template struct RGB2YUV + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + RGB2YUVConvert(&src.x, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB2YUV() {} + __host__ __device__ __forceinline__ RGB2YUV(const RGB2YUV&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2YUV_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2YUV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_YUV2RGBCoeffs_f[5] = { 2.032f, -0.395f, -0.581f, 1.140f }; + __constant__ int c_YUV2RGBCoeffs_i[5] = { 33292, -6472, -9519, 18678 }; + + template static __device__ void YUV2RGBConvert(const T& src, D* dst) + { + const int b = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[3], yuv_shift); + + const int g = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[2] + + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[1], yuv_shift); + + const int r = src.x + CV_DESCALE((src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[0], yuv_shift); + + dst[bidx] = saturate_cast(b); + dst[1] = saturate_cast(g); + dst[bidx^2] = saturate_cast(r); + } + + template static __device__ uint YUV2RGBConvert(uint src) + { + const int x = 0xff & (src); + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const int b = x + CV_DESCALE((z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[3], yuv_shift); + + const int g = x + CV_DESCALE((z - ColorChannel::half()) * c_YUV2RGBCoeffs_i[2] + + (y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[1], yuv_shift); + + const int r = x + CV_DESCALE((y - ColorChannel::half()) * c_YUV2RGBCoeffs_i[0], yuv_shift); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(b) << (bidx * 8); + dst |= saturate_cast(g) << 8; + dst |= saturate_cast(r) << ((bidx ^ 2) * 8); + + return dst; + } + + template static __device__ __forceinline__ void YUV2RGBConvert(const T& src, float* dst) + { + dst[bidx] = src.x + (src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_f[3]; + + dst[1] = src.x + (src.z - ColorChannel::half()) * c_YUV2RGBCoeffs_f[2] + + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_f[1]; + + dst[bidx^2] = src.x + (src.y - ColorChannel::half()) * c_YUV2RGBCoeffs_f[0]; + } + + template struct YUV2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + YUV2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ YUV2RGB() {} + __host__ __device__ __forceinline__ YUV2RGB(const YUV2RGB&) {} + }; + + template struct YUV2RGB : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return YUV2RGBConvert(src); + } + __host__ __device__ __forceinline__ YUV2RGB() {} + __host__ __device__ __forceinline__ YUV2RGB(const YUV2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_YUV2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::YUV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> YCrCb ////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2YCrCbCoeffs_f[5] = {0.299f, 0.587f, 0.114f, 0.713f, 0.564f}; + __constant__ int c_RGB2YCrCbCoeffs_i[5] = {R2Y, G2Y, B2Y, 11682, 9241}; + + template static __device__ void RGB2YCrCbConvert(const T* src, D& dst) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE(src[0] * c_RGB2YCrCbCoeffs_i[bidx^2] + src[1] * c_RGB2YCrCbCoeffs_i[1] + src[2] * c_RGB2YCrCbCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE((src[bidx^2] - Y) * c_RGB2YCrCbCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE((src[bidx] - Y) * c_RGB2YCrCbCoeffs_i[4] + delta, yuv_shift); + + dst.x = saturate_cast(Y); + dst.y = saturate_cast(Cr); + dst.z = saturate_cast(Cb); + } + + template static __device__ uint RGB2YCrCbConvert(uint src) + { + const int delta = ColorChannel::half() * (1 << yuv_shift); + + const int Y = CV_DESCALE((0xffu & src) * c_RGB2YCrCbCoeffs_i[bidx^2] + (0xffu & (src >> 8)) * c_RGB2YCrCbCoeffs_i[1] + (0xffu & (src >> 16)) * c_RGB2YCrCbCoeffs_i[bidx], yuv_shift); + const int Cr = CV_DESCALE(((0xffu & (src >> ((bidx ^ 2) * 8))) - Y) * c_RGB2YCrCbCoeffs_i[3] + delta, yuv_shift); + const int Cb = CV_DESCALE(((0xffu & (src >> (bidx * 8))) - Y) * c_RGB2YCrCbCoeffs_i[4] + delta, yuv_shift); + + uint dst = 0; + + dst |= saturate_cast(Y); + dst |= saturate_cast(Cr) << 8; + dst |= saturate_cast(Cb) << 16; + + return dst; + } + + template static __device__ __forceinline__ void RGB2YCrCbConvert(const float* src, D& dst) + { + dst.x = src[0] * c_RGB2YCrCbCoeffs_f[bidx^2] + src[1] * c_RGB2YCrCbCoeffs_f[1] + src[2] * c_RGB2YCrCbCoeffs_f[bidx]; + dst.y = (src[bidx^2] - dst.x) * c_RGB2YCrCbCoeffs_f[3] + ColorChannel::half(); + dst.z = (src[bidx] - dst.x) * c_RGB2YCrCbCoeffs_f[4] + ColorChannel::half(); + } + + template struct RGB2YCrCb + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + RGB2YCrCbConvert(&src.x, dst); + return dst; + } + __host__ __device__ __forceinline__ RGB2YCrCb() {} + __host__ __device__ __forceinline__ RGB2YCrCb(const RGB2YCrCb&) {} + }; + + template struct RGB2YCrCb : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return RGB2YCrCbConvert(src); + } + + __host__ __device__ __forceinline__ RGB2YCrCb() {} + __host__ __device__ __forceinline__ RGB2YCrCb(const RGB2YCrCb&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2YCrCb_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2YCrCb functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_YCrCb2RGBCoeffs_f[5] = {1.403f, -0.714f, -0.344f, 1.773f}; + __constant__ int c_YCrCb2RGBCoeffs_i[5] = {22987, -11698, -5636, 29049}; + + template static __device__ void YCrCb2RGBConvert(const T& src, D* dst) + { + const int b = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[3], yuv_shift); + const int g = src.x + CV_DESCALE((src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[2] + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[1], yuv_shift); + const int r = src.x + CV_DESCALE((src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[0], yuv_shift); + + dst[bidx] = saturate_cast(b); + dst[1] = saturate_cast(g); + dst[bidx^2] = saturate_cast(r); + } + + template static __device__ uint YCrCb2RGBConvert(uint src) + { + const int x = 0xff & (src); + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const int b = x + CV_DESCALE((z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[3], yuv_shift); + const int g = x + CV_DESCALE((z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[2] + (y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[1], yuv_shift); + const int r = x + CV_DESCALE((y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_i[0], yuv_shift); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(b) << (bidx * 8); + dst |= saturate_cast(g) << 8; + dst |= saturate_cast(r) << ((bidx ^ 2) * 8); + + return dst; + } + + template __device__ __forceinline__ void YCrCb2RGBConvert(const T& src, float* dst) + { + dst[bidx] = src.x + (src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[3]; + dst[1] = src.x + (src.z - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[2] + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[1]; + dst[bidx^2] = src.x + (src.y - ColorChannel::half()) * c_YCrCb2RGBCoeffs_f[0]; + } + + template struct YCrCb2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + YCrCb2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ YCrCb2RGB() {} + __host__ __device__ __forceinline__ YCrCb2RGB(const YCrCb2RGB&) {} + }; + + template struct YCrCb2RGB : unary_function + { + __device__ __forceinline__ uint operator ()(uint src) const + { + return YCrCb2RGBConvert(src); + } + __host__ __device__ __forceinline__ YCrCb2RGB() {} + __host__ __device__ __forceinline__ YCrCb2RGB(const YCrCb2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_YCrCb2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::YCrCb2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +////////////////////////////////////// RGB <-> XYZ /////////////////////////////////////// + + namespace color_detail + { + __constant__ float c_RGB2XYZ_D65f[9] = { 0.412453f, 0.357580f, 0.180423f, 0.212671f, 0.715160f, 0.072169f, 0.019334f, 0.119193f, 0.950227f }; + __constant__ int c_RGB2XYZ_D65i[9] = { 1689, 1465, 739, 871, 2929, 296, 79, 488, 3892 }; + + template static __device__ __forceinline__ void RGB2XYZConvert(const T* src, D& dst) + { + dst.z = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[6] + src[1] * c_RGB2XYZ_D65i[7] + src[bidx] * c_RGB2XYZ_D65i[8], xyz_shift)); + dst.x = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[0] + src[1] * c_RGB2XYZ_D65i[1] + src[bidx] * c_RGB2XYZ_D65i[2], xyz_shift)); + dst.y = saturate_cast(CV_DESCALE(src[bidx^2] * c_RGB2XYZ_D65i[3] + src[1] * c_RGB2XYZ_D65i[4] + src[bidx] * c_RGB2XYZ_D65i[5], xyz_shift)); + } + + template static __device__ __forceinline__ uint RGB2XYZConvert(uint src) + { + const uint b = 0xffu & (src >> (bidx * 8)); + const uint g = 0xffu & (src >> 8); + const uint r = 0xffu & (src >> ((bidx ^ 2) * 8)); + + const uint x = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[0] + g * c_RGB2XYZ_D65i[1] + b * c_RGB2XYZ_D65i[2], xyz_shift)); + const uint y = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[3] + g * c_RGB2XYZ_D65i[4] + b * c_RGB2XYZ_D65i[5], xyz_shift)); + const uint z = saturate_cast(CV_DESCALE(r * c_RGB2XYZ_D65i[6] + g * c_RGB2XYZ_D65i[7] + b * c_RGB2XYZ_D65i[8], xyz_shift)); + + uint dst = 0; + + dst |= x; + dst |= y << 8; + dst |= z << 16; + + return dst; + } + + template static __device__ __forceinline__ void RGB2XYZConvert(const float* src, D& dst) + { + dst.x = src[bidx^2] * c_RGB2XYZ_D65f[0] + src[1] * c_RGB2XYZ_D65f[1] + src[bidx] * c_RGB2XYZ_D65f[2]; + dst.y = src[bidx^2] * c_RGB2XYZ_D65f[3] + src[1] * c_RGB2XYZ_D65f[4] + src[bidx] * c_RGB2XYZ_D65f[5]; + dst.z = src[bidx^2] * c_RGB2XYZ_D65f[6] + src[1] * c_RGB2XYZ_D65f[7] + src[bidx] * c_RGB2XYZ_D65f[8]; + } + + template struct RGB2XYZ + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2XYZConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2XYZ() {} + __host__ __device__ __forceinline__ RGB2XYZ(const RGB2XYZ&) {} + }; + + template struct RGB2XYZ : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2XYZConvert(src); + } + __host__ __device__ __forceinline__ RGB2XYZ() {} + __host__ __device__ __forceinline__ RGB2XYZ(const RGB2XYZ&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2XYZ_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2XYZ functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_XYZ2sRGB_D65f[9] = { 3.240479f, -1.53715f, -0.498535f, -0.969256f, 1.875991f, 0.041556f, 0.055648f, -0.204043f, 1.057311f }; + __constant__ int c_XYZ2sRGB_D65i[9] = { 13273, -6296, -2042, -3970, 7684, 170, 228, -836, 4331 }; + + template static __device__ __forceinline__ void XYZ2RGBConvert(const T& src, D* dst) + { + dst[bidx^2] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[0] + src.y * c_XYZ2sRGB_D65i[1] + src.z * c_XYZ2sRGB_D65i[2], xyz_shift)); + dst[1] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[3] + src.y * c_XYZ2sRGB_D65i[4] + src.z * c_XYZ2sRGB_D65i[5], xyz_shift)); + dst[bidx] = saturate_cast(CV_DESCALE(src.x * c_XYZ2sRGB_D65i[6] + src.y * c_XYZ2sRGB_D65i[7] + src.z * c_XYZ2sRGB_D65i[8], xyz_shift)); + } + + template static __device__ __forceinline__ uint XYZ2RGBConvert(uint src) + { + const int x = 0xff & src; + const int y = 0xff & (src >> 8); + const int z = 0xff & (src >> 16); + + const uint r = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[0] + y * c_XYZ2sRGB_D65i[1] + z * c_XYZ2sRGB_D65i[2], xyz_shift)); + const uint g = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[3] + y * c_XYZ2sRGB_D65i[4] + z * c_XYZ2sRGB_D65i[5], xyz_shift)); + const uint b = saturate_cast(CV_DESCALE(x * c_XYZ2sRGB_D65i[6] + y * c_XYZ2sRGB_D65i[7] + z * c_XYZ2sRGB_D65i[8], xyz_shift)); + + uint dst = 0xffu << 24; + + dst |= b << (bidx * 8); + dst |= g << 8; + dst |= r << ((bidx ^ 2) * 8); + + return dst; + } + + template static __device__ __forceinline__ void XYZ2RGBConvert(const T& src, float* dst) + { + dst[bidx^2] = src.x * c_XYZ2sRGB_D65f[0] + src.y * c_XYZ2sRGB_D65f[1] + src.z * c_XYZ2sRGB_D65f[2]; + dst[1] = src.x * c_XYZ2sRGB_D65f[3] + src.y * c_XYZ2sRGB_D65f[4] + src.z * c_XYZ2sRGB_D65f[5]; + dst[bidx] = src.x * c_XYZ2sRGB_D65f[6] + src.y * c_XYZ2sRGB_D65f[7] + src.z * c_XYZ2sRGB_D65f[8]; + } + + template struct XYZ2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + XYZ2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ XYZ2RGB() {} + __host__ __device__ __forceinline__ XYZ2RGB(const XYZ2RGB&) {} + }; + + template struct XYZ2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return XYZ2RGBConvert(src); + } + __host__ __device__ __forceinline__ XYZ2RGB() {} + __host__ __device__ __forceinline__ XYZ2RGB(const XYZ2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_XYZ2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::XYZ2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +////////////////////////////////////// RGB <-> HSV /////////////////////////////////////// + + namespace color_detail + { + __constant__ int c_HsvDivTable [256] = {0, 1044480, 522240, 348160, 261120, 208896, 174080, 149211, 130560, 116053, 104448, 94953, 87040, 80345, 74606, 69632, 65280, 61440, 58027, 54973, 52224, 49737, 47476, 45412, 43520, 41779, 40172, 38684, 37303, 36017, 34816, 33693, 32640, 31651, 30720, 29842, 29013, 28229, 27486, 26782, 26112, 25475, 24869, 24290, 23738, 23211, 22706, 22223, 21760, 21316, 20890, 20480, 20086, 19707, 19342, 18991, 18651, 18324, 18008, 17703, 17408, 17123, 16846, 16579, 16320, 16069, 15825, 15589, 15360, 15137, 14921, 14711, 14507, 14308, 14115, 13926, 13743, 13565, 13391, 13221, 13056, 12895, 12738, 12584, 12434, 12288, 12145, 12006, 11869, 11736, 11605, 11478, 11353, 11231, 11111, 10995, 10880, 10768, 10658, 10550, 10445, 10341, 10240, 10141, 10043, 9947, 9854, 9761, 9671, 9582, 9495, 9410, 9326, 9243, 9162, 9082, 9004, 8927, 8852, 8777, 8704, 8632, 8561, 8492, 8423, 8356, 8290, 8224, 8160, 8097, 8034, 7973, 7913, 7853, 7795, 7737, 7680, 7624, 7569, 7514, 7461, 7408, 7355, 7304, 7253, 7203, 7154, 7105, 7057, 7010, 6963, 6917, 6872, 6827, 6782, 6739, 6695, 6653, 6611, 6569, 6528, 6487, 6447, 6408, 6369, 6330, 6292, 6254, 6217, 6180, 6144, 6108, 6073, 6037, 6003, 5968, 5935, 5901, 5868, 5835, 5803, 5771, 5739, 5708, 5677, 5646, 5615, 5585, 5556, 5526, 5497, 5468, 5440, 5412, 5384, 5356, 5329, 5302, 5275, 5249, 5222, 5196, 5171, 5145, 5120, 5095, 5070, 5046, 5022, 4998, 4974, 4950, 4927, 4904, 4881, 4858, 4836, 4813, 4791, 4769, 4748, 4726, 4705, 4684, 4663, 4642, 4622, 4601, 4581, 4561, 4541, 4522, 4502, 4483, 4464, 4445, 4426, 4407, 4389, 4370, 4352, 4334, 4316, 4298, 4281, 4263, 4246, 4229, 4212, 4195, 4178, 4161, 4145, 4128, 4112, 4096}; + __constant__ int c_HsvDivTable180[256] = {0, 122880, 61440, 40960, 30720, 24576, 20480, 17554, 15360, 13653, 12288, 11171, 10240, 9452, 8777, 8192, 7680, 7228, 6827, 6467, 6144, 5851, 5585, 5343, 5120, 4915, 4726, 4551, 4389, 4237, 4096, 3964, 3840, 3724, 3614, 3511, 3413, 3321, 3234, 3151, 3072, 2997, 2926, 2858, 2793, 2731, 2671, 2614, 2560, 2508, 2458, 2409, 2363, 2318, 2276, 2234, 2194, 2156, 2119, 2083, 2048, 2014, 1982, 1950, 1920, 1890, 1862, 1834, 1807, 1781, 1755, 1731, 1707, 1683, 1661, 1638, 1617, 1596, 1575, 1555, 1536, 1517, 1499, 1480, 1463, 1446, 1429, 1412, 1396, 1381, 1365, 1350, 1336, 1321, 1307, 1293, 1280, 1267, 1254, 1241, 1229, 1217, 1205, 1193, 1182, 1170, 1159, 1148, 1138, 1127, 1117, 1107, 1097, 1087, 1078, 1069, 1059, 1050, 1041, 1033, 1024, 1016, 1007, 999, 991, 983, 975, 968, 960, 953, 945, 938, 931, 924, 917, 910, 904, 897, 890, 884, 878, 871, 865, 859, 853, 847, 842, 836, 830, 825, 819, 814, 808, 803, 798, 793, 788, 783, 778, 773, 768, 763, 759, 754, 749, 745, 740, 736, 731, 727, 723, 719, 714, 710, 706, 702, 698, 694, 690, 686, 683, 679, 675, 671, 668, 664, 661, 657, 654, 650, 647, 643, 640, 637, 633, 630, 627, 624, 621, 617, 614, 611, 608, 605, 602, 599, 597, 594, 591, 588, 585, 582, 580, 577, 574, 572, 569, 566, 564, 561, 559, 556, 554, 551, 549, 546, 544, 541, 539, 537, 534, 532, 530, 527, 525, 523, 521, 518, 516, 514, 512, 510, 508, 506, 504, 502, 500, 497, 495, 493, 492, 490, 488, 486, 484, 482}; + __constant__ int c_HsvDivTable256[256] = {0, 174763, 87381, 58254, 43691, 34953, 29127, 24966, 21845, 19418, 17476, 15888, 14564, 13443, 12483, 11651, 10923, 10280, 9709, 9198, 8738, 8322, 7944, 7598, 7282, 6991, 6722, 6473, 6242, 6026, 5825, 5638, 5461, 5296, 5140, 4993, 4855, 4723, 4599, 4481, 4369, 4263, 4161, 4064, 3972, 3884, 3799, 3718, 3641, 3567, 3495, 3427, 3361, 3297, 3236, 3178, 3121, 3066, 3013, 2962, 2913, 2865, 2819, 2774, 2731, 2689, 2648, 2608, 2570, 2533, 2497, 2461, 2427, 2394, 2362, 2330, 2300, 2270, 2241, 2212, 2185, 2158, 2131, 2106, 2081, 2056, 2032, 2009, 1986, 1964, 1942, 1920, 1900, 1879, 1859, 1840, 1820, 1802, 1783, 1765, 1748, 1730, 1713, 1697, 1680, 1664, 1649, 1633, 1618, 1603, 1589, 1574, 1560, 1547, 1533, 1520, 1507, 1494, 1481, 1469, 1456, 1444, 1432, 1421, 1409, 1398, 1387, 1376, 1365, 1355, 1344, 1334, 1324, 1314, 1304, 1295, 1285, 1276, 1266, 1257, 1248, 1239, 1231, 1222, 1214, 1205, 1197, 1189, 1181, 1173, 1165, 1157, 1150, 1142, 1135, 1128, 1120, 1113, 1106, 1099, 1092, 1085, 1079, 1072, 1066, 1059, 1053, 1046, 1040, 1034, 1028, 1022, 1016, 1010, 1004, 999, 993, 987, 982, 976, 971, 966, 960, 955, 950, 945, 940, 935, 930, 925, 920, 915, 910, 906, 901, 896, 892, 887, 883, 878, 874, 869, 865, 861, 857, 853, 848, 844, 840, 836, 832, 828, 824, 820, 817, 813, 809, 805, 802, 798, 794, 791, 787, 784, 780, 777, 773, 770, 767, 763, 760, 757, 753, 750, 747, 744, 741, 737, 734, 731, 728, 725, 722, 719, 716, 713, 710, 708, 705, 702, 699, 696, 694, 691, 688, 685}; + + template static __device__ void RGB2HSVConvert(const uchar* src, D& dst) + { + const int hsv_shift = 12; + const int* hdiv_table = hr == 180 ? c_HsvDivTable180 : c_HsvDivTable256; + + int b = src[bidx], g = src[1], r = src[bidx^2]; + int h, s, v = b; + int vmin = b, diff; + int vr, vg; + + v = ::max(v, g); + v = ::max(v, r); + vmin = ::min(vmin, g); + vmin = ::min(vmin, r); + + diff = v - vmin; + vr = (v == r) * -1; + vg = (v == g) * -1; + + s = (diff * c_HsvDivTable[v] + (1 << (hsv_shift-1))) >> hsv_shift; + h = (vr & (g - b)) + (~vr & ((vg & (b - r + 2 * diff)) + ((~vg) & (r - g + 4 * diff)))); + h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + h += (h < 0) * hr; + + dst.x = saturate_cast(h); + dst.y = (uchar)s; + dst.z = (uchar)v; + } + + template static __device__ uint RGB2HSVConvert(uint src) + { + const int hsv_shift = 12; + const int* hdiv_table = hr == 180 ? c_HsvDivTable180 : c_HsvDivTable256; + + const int b = 0xff & (src >> (bidx * 8)); + const int g = 0xff & (src >> 8); + const int r = 0xff & (src >> ((bidx ^ 2) * 8)); + + int h, s, v = b; + int vmin = b, diff; + int vr, vg; + + v = ::max(v, g); + v = ::max(v, r); + vmin = ::min(vmin, g); + vmin = ::min(vmin, r); + + diff = v - vmin; + vr = (v == r) * -1; + vg = (v == g) * -1; + + s = (diff * c_HsvDivTable[v] + (1 << (hsv_shift-1))) >> hsv_shift; + h = (vr & (g - b)) + (~vr & ((vg & (b - r + 2 * diff)) + ((~vg) & (r - g + 4 * diff)))); + h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + h += (h < 0) * hr; + + uint dst = 0; + + dst |= saturate_cast(h); + dst |= (0xffu & s) << 8; + dst |= (0xffu & v) << 16; + + return dst; + } + + template static __device__ void RGB2HSVConvert(const float* src, D& dst) + { + const float hscale = hr * (1.f / 360.f); + + float b = src[bidx], g = src[1], r = src[bidx^2]; + float h, s, v; + + float vmin, diff; + + v = vmin = r; + v = fmax(v, g); + v = fmax(v, b); + vmin = fmin(vmin, g); + vmin = fmin(vmin, b); + + diff = v - vmin; + s = diff / (float)(::fabs(v) + numeric_limits::epsilon()); + diff = (float)(60. / (diff + numeric_limits::epsilon())); + + h = (v == r) * (g - b) * diff; + h += (v != r && v == g) * ((b - r) * diff + 120.f); + h += (v != r && v != g) * ((r - g) * diff + 240.f); + h += (h < 0) * 360.f; + + dst.x = h * hscale; + dst.y = s; + dst.z = v; + } + + template struct RGB2HSV + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2HSVConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2HSV() {} + __host__ __device__ __forceinline__ RGB2HSV(const RGB2HSV&) {} + }; + + template struct RGB2HSV : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2HSVConvert(src); + } + __host__ __device__ __forceinline__ RGB2HSV() {} + __host__ __device__ __forceinline__ RGB2HSV(const RGB2HSV&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2HSV_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HSV functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ int c_HsvSectorData[6][3] = { {1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0} }; + + template static __device__ void HSV2RGBConvert(const T& src, float* dst) + { + const float hscale = 6.f / hr; + + float h = src.x, s = src.y, v = src.z; + float b = v, g = v, r = v; + + if (s != 0) + { + h *= hscale; + + if( h < 0 ) + do h += 6; while( h < 0 ); + else if( h >= 6 ) + do h -= 6; while( h >= 6 ); + + int sector = __float2int_rd(h); + h -= sector; + + if ( (unsigned)sector >= 6u ) + { + sector = 0; + h = 0.f; + } + + float tab[4]; + tab[0] = v; + tab[1] = v * (1.f - s); + tab[2] = v * (1.f - s * h); + tab[3] = v * (1.f - s * (1.f - h)); + + b = tab[c_HsvSectorData[sector][0]]; + g = tab[c_HsvSectorData[sector][1]]; + r = tab[c_HsvSectorData[sector][2]]; + } + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; + } + + template static __device__ void HSV2RGBConvert(const T& src, uchar* dst) + { + float3 buf; + + buf.x = src.x; + buf.y = src.y * (1.f / 255.f); + buf.z = src.z * (1.f / 255.f); + + HSV2RGBConvert(buf, &buf.x); + + dst[0] = saturate_cast(buf.x * 255.f); + dst[1] = saturate_cast(buf.y * 255.f); + dst[2] = saturate_cast(buf.z * 255.f); + } + + template static __device__ uint HSV2RGBConvert(uint src) + { + float3 buf; + + buf.x = src & 0xff; + buf.y = ((src >> 8) & 0xff) * (1.f/255.f); + buf.z = ((src >> 16) & 0xff) * (1.f/255.f); + + HSV2RGBConvert(buf, &buf.x); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x * 255.f); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct HSV2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + HSV2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ HSV2RGB() {} + __host__ __device__ __forceinline__ HSV2RGB(const HSV2RGB&) {} + }; + + template struct HSV2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return HSV2RGBConvert(src); + } + __host__ __device__ __forceinline__ HSV2RGB() {} + __host__ __device__ __forceinline__ HSV2RGB(const HSV2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_HSV2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HSV2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +/////////////////////////////////////// RGB <-> HLS //////////////////////////////////////// + + namespace color_detail + { + template static __device__ void RGB2HLSConvert(const float* src, D& dst) + { + const float hscale = hr * (1.f / 360.f); + + float b = src[bidx], g = src[1], r = src[bidx^2]; + float h = 0.f, s = 0.f, l; + float vmin, vmax, diff; + + vmax = vmin = r; + vmax = fmax(vmax, g); + vmax = fmax(vmax, b); + vmin = fmin(vmin, g); + vmin = fmin(vmin, b); + + diff = vmax - vmin; + l = (vmax + vmin) * 0.5f; + + if (diff > numeric_limits::epsilon()) + { + s = (l < 0.5f) * diff / (vmax + vmin); + s += (l >= 0.5f) * diff / (2.0f - vmax - vmin); + + diff = 60.f / diff; + + h = (vmax == r) * (g - b) * diff; + h += (vmax != r && vmax == g) * ((b - r) * diff + 120.f); + h += (vmax != r && vmax != g) * ((r - g) * diff + 240.f); + h += (h < 0.f) * 360.f; + } + + dst.x = h * hscale; + dst.y = l; + dst.z = s; + } + + template static __device__ void RGB2HLSConvert(const uchar* src, D& dst) + { + float3 buf; + + buf.x = src[0] * (1.f / 255.f); + buf.y = src[1] * (1.f / 255.f); + buf.z = src[2] * (1.f / 255.f); + + RGB2HLSConvert(&buf.x, buf); + + dst.x = saturate_cast(buf.x); + dst.y = saturate_cast(buf.y*255.f); + dst.z = saturate_cast(buf.z*255.f); + } + + template static __device__ uint RGB2HLSConvert(uint src) + { + float3 buf; + + buf.x = (0xff & src) * (1.f / 255.f); + buf.y = (0xff & (src >> 8)) * (1.f / 255.f); + buf.z = (0xff & (src >> 16)) * (1.f / 255.f); + + RGB2HLSConvert(&buf.x, buf); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct RGB2HLS + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2HLSConvert(&src.x, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2HLS() {} + __host__ __device__ __forceinline__ RGB2HLS(const RGB2HLS&) {} + }; + + template struct RGB2HLS : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return RGB2HLSConvert(src); + } + __host__ __device__ __forceinline__ RGB2HLS() {} + __host__ __device__ __forceinline__ RGB2HLS(const RGB2HLS&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2HLS_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2HLS functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ int c_HlsSectorData[6][3] = { {1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0} }; + + template static __device__ void HLS2RGBConvert(const T& src, float* dst) + { + const float hscale = 6.0f / hr; + + float h = src.x, l = src.y, s = src.z; + float b = l, g = l, r = l; + + if (s != 0) + { + float p2 = (l <= 0.5f) * l * (1 + s); + p2 += (l > 0.5f) * (l + s - l * s); + float p1 = 2 * l - p2; + + h *= hscale; + + if( h < 0 ) + do h += 6; while( h < 0 ); + else if( h >= 6 ) + do h -= 6; while( h >= 6 ); + + int sector; + sector = __float2int_rd(h); + + h -= sector; + + float tab[4]; + tab[0] = p2; + tab[1] = p1; + tab[2] = p1 + (p2 - p1) * (1 - h); + tab[3] = p1 + (p2 - p1) * h; + + b = tab[c_HlsSectorData[sector][0]]; + g = tab[c_HlsSectorData[sector][1]]; + r = tab[c_HlsSectorData[sector][2]]; + } + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; + } + + template static __device__ void HLS2RGBConvert(const T& src, uchar* dst) + { + float3 buf; + + buf.x = src.x; + buf.y = src.y * (1.f / 255.f); + buf.z = src.z * (1.f / 255.f); + + HLS2RGBConvert(buf, &buf.x); + + dst[0] = saturate_cast(buf.x * 255.f); + dst[1] = saturate_cast(buf.y * 255.f); + dst[2] = saturate_cast(buf.z * 255.f); + } + + template static __device__ uint HLS2RGBConvert(uint src) + { + float3 buf; + + buf.x = 0xff & src; + buf.y = (0xff & (src >> 8)) * (1.f / 255.f); + buf.z = (0xff & (src >> 16)) * (1.f / 255.f); + + HLS2RGBConvert(buf, &buf.x); + + uint dst = 0xffu << 24; + + dst |= saturate_cast(buf.x * 255.f); + dst |= saturate_cast(buf.y * 255.f) << 8; + dst |= saturate_cast(buf.z * 255.f) << 16; + + return dst; + } + + template struct HLS2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + HLS2RGBConvert(src, &dst.x); + setAlpha(dst, ColorChannel::max()); + + return dst; + } + __host__ __device__ __forceinline__ HLS2RGB() {} + __host__ __device__ __forceinline__ HLS2RGB(const HLS2RGB&) {} + }; + + template struct HLS2RGB : unary_function + { + __device__ __forceinline__ uint operator()(uint src) const + { + return HLS2RGBConvert(src); + } + __host__ __device__ __forceinline__ HLS2RGB() {} + __host__ __device__ __forceinline__ HLS2RGB(const HLS2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_HLS2RGB_TRAITS(name, scn, dcn, bidx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; \ + template <> struct name ## _full_traits \ + { \ + typedef ::cv::cuda::device::color_detail::HLS2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> Lab ///////////////////////////////////// + + namespace color_detail + { + enum + { + LAB_CBRT_TAB_SIZE = 1024, + GAMMA_TAB_SIZE = 1024, + lab_shift = xyz_shift, + gamma_shift = 3, + lab_shift2 = (lab_shift + gamma_shift), + LAB_CBRT_TAB_SIZE_B = (256 * 3 / 2 * (1 << gamma_shift)) + }; + + __constant__ ushort c_sRGBGammaTab_b[] = {0,1,1,2,2,3,4,4,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,19,20,21,22,24,25,26,28,29,31,33,34,36,38,40,41,43,45,47,49,51,54,56,58,60,63,65,68,70,73,75,78,81,83,86,89,92,95,98,101,105,108,111,115,118,121,125,129,132,136,140,144,147,151,155,160,164,168,172,176,181,185,190,194,199,204,209,213,218,223,228,233,239,244,249,255,260,265,271,277,282,288,294,300,306,312,318,324,331,337,343,350,356,363,370,376,383,390,397,404,411,418,426,433,440,448,455,463,471,478,486,494,502,510,518,527,535,543,552,560,569,578,586,595,604,613,622,631,641,650,659,669,678,688,698,707,717,727,737,747,757,768,778,788,799,809,820,831,842,852,863,875,886,897,908,920,931,943,954,966,978,990,1002,1014,1026,1038,1050,1063,1075,1088,1101,1113,1126,1139,1152,1165,1178,1192,1205,1218,1232,1245,1259,1273,1287,1301,1315,1329,1343,1357,1372,1386,1401,1415,1430,1445,1460,1475,1490,1505,1521,1536,1551,1567,1583,1598,1614,1630,1646,1662,1678,1695,1711,1728,1744,1761,1778,1794,1811,1828,1846,1863,1880,1897,1915,1933,1950,1968,1986,2004,2022,2040}; + + __device__ __forceinline__ int LabCbrt_b(int i) + { + float x = i * (1.f / (255.f * (1 << gamma_shift))); + return (1 << lab_shift2) * (x < 0.008856f ? x * 7.787f + 0.13793103448275862f : ::cbrtf(x)); + } + + template + __device__ __forceinline__ void RGB2LabConvert_b(const T& src, D& dst) + { + const int Lscale = (116 * 255 + 50) / 100; + const int Lshift = -((16 * 255 * (1 << lab_shift2) + 50) / 100); + + int B = blueIdx == 0 ? src.x : src.z; + int G = src.y; + int R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = c_sRGBGammaTab_b[B]; + G = c_sRGBGammaTab_b[G]; + R = c_sRGBGammaTab_b[R]; + } + else + { + B <<= 3; + G <<= 3; + R <<= 3; + } + + int fX = LabCbrt_b(CV_DESCALE(B * 778 + G * 1541 + R * 1777, lab_shift)); + int fY = LabCbrt_b(CV_DESCALE(B * 296 + G * 2929 + R * 871, lab_shift)); + int fZ = LabCbrt_b(CV_DESCALE(B * 3575 + G * 448 + R * 73, lab_shift)); + + int L = CV_DESCALE(Lscale * fY + Lshift, lab_shift2); + int a = CV_DESCALE(500 * (fX - fY) + 128 * (1 << lab_shift2), lab_shift2); + int b = CV_DESCALE(200 * (fY - fZ) + 128 * (1 << lab_shift2), lab_shift2); + + dst.x = saturate_cast(L); + dst.y = saturate_cast(a); + dst.z = saturate_cast(b); + } + + __device__ __forceinline__ float splineInterpolate(float x, const float* tab, int n) + { + int ix = ::min(::max(int(x), 0), n-1); + x -= ix; + tab += ix * 4; + return ((tab[3] * x + tab[2]) * x + tab[1]) * x + tab[0]; + } + + __constant__ float c_sRGBGammaTab[] = {0,7.55853e-05,0.,-7.51331e-13,7.55853e-05,7.55853e-05,-2.25399e-12,3.75665e-12,0.000151171,7.55853e-05,9.01597e-12,-6.99932e-12,0.000226756,7.55853e-05,-1.1982e-11,2.41277e-12,0.000302341,7.55853e-05,-4.74369e-12,1.19001e-11,0.000377927,7.55853e-05,3.09568e-11,-2.09095e-11,0.000453512,7.55853e-05,-3.17718e-11,1.35303e-11,0.000529097,7.55853e-05,8.81905e-12,-4.10782e-12,0.000604683,7.55853e-05,-3.50439e-12,2.90097e-12,0.000680268,7.55853e-05,5.19852e-12,-7.49607e-12,0.000755853,7.55853e-05,-1.72897e-11,2.70833e-11,0.000831439,7.55854e-05,6.39602e-11,-4.26295e-11,0.000907024,7.55854e-05,-6.39282e-11,2.70193e-11,0.000982609,7.55853e-05,1.71298e-11,-7.24017e-12,0.00105819,7.55853e-05,-4.59077e-12,1.94137e-12,0.00113378,7.55853e-05,1.23333e-12,-5.25291e-13,0.00120937,7.55853e-05,-3.42545e-13,1.59799e-13,0.00128495,7.55853e-05,1.36852e-13,-1.13904e-13,0.00136054,7.55853e-05,-2.04861e-13,2.95818e-13,0.00143612,7.55853e-05,6.82594e-13,-1.06937e-12,0.00151171,7.55853e-05,-2.52551e-12,3.98166e-12,0.00158729,7.55853e-05,9.41946e-12,-1.48573e-11,0.00166288,7.55853e-05,-3.51523e-11,5.54474e-11,0.00173846,7.55854e-05,1.3119e-10,-9.0517e-11,0.00181405,7.55854e-05,-1.40361e-10,7.37899e-11,0.00188963,7.55853e-05,8.10085e-11,-8.82272e-11,0.00196522,7.55852e-05,-1.83673e-10,1.62704e-10,0.0020408,7.55853e-05,3.04438e-10,-2.13341e-10,0.00211639,7.55853e-05,-3.35586e-10,2.25e-10,0.00219197,7.55853e-05,3.39414e-10,-2.20997e-10,0.00226756,7.55853e-05,-3.23576e-10,1.93326e-10,0.00234315,7.55853e-05,2.564e-10,-8.66446e-11,0.00241873,7.55855e-05,-3.53328e-12,-7.9578e-11,0.00249432,7.55853e-05,-2.42267e-10,1.72126e-10,0.0025699,7.55853e-05,2.74111e-10,-1.43265e-10,0.00264549,7.55854e-05,-1.55683e-10,-6.47292e-11,0.00272107,7.55849e-05,-3.4987e-10,8.67842e-10,0.00279666,7.55868e-05,2.25366e-09,-3.8723e-09,0.00287224,7.55797e-05,-9.36325e-09,1.5087e-08,0.00294783,7.56063e-05,3.58978e-08,-5.69415e-08,0.00302341,7.55072e-05,-1.34927e-07,2.13144e-07,0.003099,7.58768e-05,5.04507e-07,1.38713e-07,0.00317552,7.7302e-05,9.20646e-07,-1.55186e-07,0.00325359,7.86777e-05,4.55087e-07,4.26813e-08,0.00333276,7.97159e-05,5.83131e-07,-1.06495e-08,0.00341305,8.08502e-05,5.51182e-07,3.87467e-09,0.00349446,8.19642e-05,5.62806e-07,-1.92586e-10,0.00357698,8.30892e-05,5.62228e-07,1.0866e-09,0.00366063,8.4217e-05,5.65488e-07,5.02818e-10,0.00374542,8.53494e-05,5.66997e-07,8.60211e-10,0.00383133,8.6486e-05,5.69577e-07,7.13044e-10,0.00391839,8.76273e-05,5.71716e-07,4.78527e-10,0.00400659,8.87722e-05,5.73152e-07,1.09818e-09,0.00409594,8.99218e-05,5.76447e-07,2.50964e-10,0.00418644,9.10754e-05,5.772e-07,1.15762e-09,0.00427809,9.22333e-05,5.80672e-07,2.40865e-10,0.0043709,9.33954e-05,5.81395e-07,1.13854e-09,0.00446488,9.45616e-05,5.84811e-07,3.27267e-10,0.00456003,9.57322e-05,5.85792e-07,8.1197e-10,0.00465635,9.69062e-05,5.88228e-07,6.15823e-10,0.00475384,9.80845e-05,5.90076e-07,9.15747e-10,0.00485252,9.92674e-05,5.92823e-07,3.778e-10,0.00495238,0.000100454,5.93956e-07,8.32623e-10,0.00505343,0.000101645,5.96454e-07,4.82695e-10,0.00515567,0.000102839,5.97902e-07,9.61904e-10,0.00525911,0.000104038,6.00788e-07,3.26281e-10,0.00536375,0.00010524,6.01767e-07,9.926e-10,0.00546959,0.000106447,6.04745e-07,3.59933e-10,0.00557664,0.000107657,6.05824e-07,8.2728e-10,0.0056849,0.000108871,6.08306e-07,5.21898e-10,0.00579438,0.00011009,6.09872e-07,8.10492e-10,0.00590508,0.000111312,6.12303e-07,4.27046e-10,0.00601701,0.000112538,6.13585e-07,7.40878e-10,0.00613016,0.000113767,6.15807e-07,8.00469e-10,0.00624454,0.000115001,6.18209e-07,2.48178e-10,0.00636016,0.000116238,6.18953e-07,1.00073e-09,0.00647702,0.000117479,6.21955e-07,4.05654e-10,0.00659512,0.000118724,6.23172e-07,6.36192e-10,0.00671447,0.000119973,6.25081e-07,7.74927e-10,0.00683507,0.000121225,6.27406e-07,4.54975e-10,0.00695692,0.000122481,6.28771e-07,6.64841e-10,0.00708003,0.000123741,6.30765e-07,6.10972e-10,0.00720441,0.000125004,6.32598e-07,6.16543e-10,0.00733004,0.000126271,6.34448e-07,6.48204e-10,0.00745695,0.000127542,6.36392e-07,5.15835e-10,0.00758513,0.000128816,6.3794e-07,5.48103e-10,0.00771458,0.000130094,6.39584e-07,1.01706e-09,0.00784532,0.000131376,6.42635e-07,4.0283e-11,0.00797734,0.000132661,6.42756e-07,6.84471e-10,0.00811064,0.000133949,6.4481e-07,9.47144e-10,0.00824524,0.000135241,6.47651e-07,1.83472e-10,0.00838112,0.000136537,6.48201e-07,1.11296e-09,0.00851831,0.000137837,6.5154e-07,2.13163e-11,0.0086568,0.00013914,6.51604e-07,6.64462e-10,0.00879659,0.000140445,6.53598e-07,1.04613e-09,0.00893769,0.000141756,6.56736e-07,-1.92377e-10,0.0090801,0.000143069,6.56159e-07,1.58601e-09,0.00922383,0.000144386,6.60917e-07,-5.63754e-10,0.00936888,0.000145706,6.59226e-07,1.60033e-09,0.00951524,0.000147029,6.64027e-07,-2.49543e-10,0.00966294,0.000148356,6.63278e-07,1.26043e-09,0.00981196,0.000149687,6.67059e-07,-1.35572e-10,0.00996231,0.00015102,6.66653e-07,1.14458e-09,0.010114,0.000152357,6.70086e-07,2.13864e-10,0.010267,0.000153698,6.70728e-07,7.93856e-10,0.0104214,0.000155042,6.73109e-07,3.36077e-10,0.0105771,0.000156389,6.74118e-07,6.55765e-10,0.0107342,0.000157739,6.76085e-07,7.66211e-10,0.0108926,0.000159094,6.78384e-07,4.66116e-12,0.0110524,0.000160451,6.78398e-07,1.07775e-09,0.0112135,0.000161811,6.81631e-07,3.41023e-10,0.011376,0.000163175,6.82654e-07,3.5205e-10,0.0115398,0.000164541,6.8371e-07,1.04473e-09,0.0117051,0.000165912,6.86844e-07,1.25757e-10,0.0118717,0.000167286,6.87222e-07,3.14818e-10,0.0120396,0.000168661,6.88166e-07,1.40886e-09,0.012209,0.000170042,6.92393e-07,-3.62244e-10,0.0123797,0.000171425,6.91306e-07,9.71397e-10,0.0125518,0.000172811,6.9422e-07,2.02003e-10,0.0127253,0.0001742,6.94826e-07,1.01448e-09,0.0129002,0.000175593,6.97869e-07,3.96653e-10,0.0130765,0.00017699,6.99059e-07,1.92927e-10,0.0132542,0.000178388,6.99638e-07,6.94305e-10,0.0134333,0.00017979,7.01721e-07,7.55108e-10,0.0136138,0.000181195,7.03986e-07,1.05918e-11,0.0137957,0.000182603,7.04018e-07,1.06513e-09,0.013979,0.000184015,7.07214e-07,3.85512e-10,0.0141637,0.00018543,7.0837e-07,1.86769e-10,0.0143499,0.000186848,7.0893e-07,7.30116e-10,0.0145374,0.000188268,7.11121e-07,6.17983e-10,0.0147264,0.000189692,7.12975e-07,5.23282e-10,0.0149168,0.000191119,7.14545e-07,8.28398e-11,0.0151087,0.000192549,7.14793e-07,1.0081e-09,0.0153019,0.000193981,7.17817e-07,5.41244e-10,0.0154966,0.000195418,7.19441e-07,-3.7907e-10,0.0156928,0.000196856,7.18304e-07,1.90641e-09,0.0158903,0.000198298,7.24023e-07,-7.27387e-10,0.0160893,0.000199744,7.21841e-07,1.00317e-09,0.0162898,0.000201191,7.24851e-07,4.39949e-10,0.0164917,0.000202642,7.2617e-07,9.6234e-10,0.0166951,0.000204097,7.29057e-07,-5.64019e-10,0.0168999,0.000205554,7.27365e-07,1.29374e-09,0.0171062,0.000207012,7.31247e-07,9.77025e-10,0.017314,0.000208478,7.34178e-07,-1.47651e-09,0.0175232,0.000209942,7.29748e-07,3.06636e-09,0.0177338,0.00021141,7.38947e-07,-1.47573e-09,0.017946,0.000212884,7.3452e-07,9.7386e-10,0.0181596,0.000214356,7.37442e-07,1.30562e-09,0.0183747,0.000215835,7.41358e-07,-6.08376e-10,0.0185913,0.000217315,7.39533e-07,1.12785e-09,0.0188093,0.000218798,7.42917e-07,-1.77711e-10,0.0190289,0.000220283,7.42384e-07,1.44562e-09,0.0192499,0.000221772,7.46721e-07,-1.68825e-11,0.0194724,0.000223266,7.4667e-07,4.84533e-10,0.0196964,0.000224761,7.48124e-07,-5.85298e-11,0.0199219,0.000226257,7.47948e-07,1.61217e-09,0.0201489,0.000227757,7.52785e-07,-8.02136e-10,0.0203775,0.00022926,7.50378e-07,1.59637e-09,0.0206075,0.000230766,7.55167e-07,4.47168e-12,0.020839,0.000232276,7.55181e-07,2.48387e-10,0.021072,0.000233787,7.55926e-07,8.6474e-10,0.0213066,0.000235302,7.5852e-07,1.78299e-11,0.0215426,0.000236819,7.58573e-07,9.26567e-10,0.0217802,0.000238339,7.61353e-07,1.34529e-12,0.0220193,0.000239862,7.61357e-07,9.30659e-10,0.0222599,0.000241387,7.64149e-07,1.34529e-12,0.0225021,0.000242915,7.64153e-07,9.26567e-10,0.0227458,0.000244447,7.66933e-07,1.76215e-11,0.022991,0.00024598,7.66986e-07,8.65536e-10,0.0232377,0.000247517,7.69582e-07,2.45677e-10,0.023486,0.000249057,7.70319e-07,1.44193e-11,0.0237358,0.000250598,7.70363e-07,1.55918e-09,0.0239872,0.000252143,7.7504e-07,-6.63173e-10,0.0242401,0.000253691,7.73051e-07,1.09357e-09,0.0244946,0.000255241,7.76331e-07,1.41919e-11,0.0247506,0.000256793,7.76374e-07,7.12248e-10,0.0250082,0.000258348,7.78511e-07,8.62049e-10,0.0252673,0.000259908,7.81097e-07,-4.35061e-10,0.025528,0.000261469,7.79792e-07,8.7825e-10,0.0257902,0.000263031,7.82426e-07,6.47181e-10,0.0260541,0.000264598,7.84368e-07,2.58448e-10,0.0263194,0.000266167,7.85143e-07,1.81558e-10,0.0265864,0.000267738,7.85688e-07,8.78041e-10,0.0268549,0.000269312,7.88322e-07,3.15102e-11,0.027125,0.000270889,7.88417e-07,8.58525e-10,0.0273967,0.000272468,7.90992e-07,2.59812e-10,0.02767,0.000274051,7.91772e-07,-3.5224e-11,0.0279448,0.000275634,7.91666e-07,1.74377e-09,0.0282212,0.000277223,7.96897e-07,-1.35196e-09,0.0284992,0.000278813,7.92841e-07,1.80141e-09,0.0287788,0.000280404,7.98246e-07,-2.65629e-10,0.0290601,0.000281999,7.97449e-07,1.12374e-09,0.0293428,0.000283598,8.0082e-07,-5.04106e-10,0.0296272,0.000285198,7.99308e-07,8.92764e-10,0.0299132,0.000286799,8.01986e-07,6.58379e-10,0.0302008,0.000288405,8.03961e-07,1.98971e-10,0.0304901,0.000290014,8.04558e-07,4.08382e-10,0.0307809,0.000291624,8.05783e-07,3.01839e-11,0.0310733,0.000293236,8.05874e-07,1.33343e-09,0.0313673,0.000294851,8.09874e-07,2.2419e-10,0.031663,0.000296472,8.10547e-07,-3.67606e-10,0.0319603,0.000298092,8.09444e-07,1.24624e-09,0.0322592,0.000299714,8.13182e-07,-8.92025e-10,0.0325597,0.000301338,8.10506e-07,2.32183e-09,0.0328619,0.000302966,8.17472e-07,-9.44719e-10,0.0331657,0.000304598,8.14638e-07,1.45703e-09,0.0334711,0.000306232,8.19009e-07,-1.15805e-09,0.0337781,0.000307866,8.15535e-07,3.17507e-09,0.0340868,0.000309507,8.2506e-07,-4.09161e-09,0.0343971,0.000311145,8.12785e-07,5.74079e-09,0.0347091,0.000312788,8.30007e-07,-3.97034e-09,0.0350227,0.000314436,8.18096e-07,2.68985e-09,0.035338,0.00031608,8.26166e-07,6.61676e-10,0.0356549,0.000317734,8.28151e-07,-1.61123e-09,0.0359734,0.000319386,8.23317e-07,2.05786e-09,0.0362936,0.000321038,8.29491e-07,8.30388e-10,0.0366155,0.0003227,8.31982e-07,-1.65424e-09,0.036939,0.000324359,8.27019e-07,2.06129e-09,0.0372642,0.000326019,8.33203e-07,8.59719e-10,0.0375911,0.000327688,8.35782e-07,-1.77488e-09,0.0379196,0.000329354,8.30458e-07,2.51464e-09,0.0382498,0.000331023,8.38002e-07,-8.33135e-10,0.0385817,0.000332696,8.35502e-07,8.17825e-10,0.0389152,0.00033437,8.37956e-07,1.28718e-09,0.0392504,0.00033605,8.41817e-07,-2.2413e-09,0.0395873,0.000337727,8.35093e-07,3.95265e-09,0.0399258,0.000339409,8.46951e-07,-2.39332e-09,0.0402661,0.000341095,8.39771e-07,1.89533e-09,0.040608,0.000342781,8.45457e-07,-1.46271e-09,0.0409517,0.000344467,8.41069e-07,3.95554e-09,0.041297,0.000346161,8.52936e-07,-3.18369e-09,0.041644,0.000347857,8.43385e-07,1.32873e-09,0.0419927,0.000349548,8.47371e-07,1.59402e-09,0.0423431,0.000351248,8.52153e-07,-2.54336e-10,0.0426952,0.000352951,8.5139e-07,-5.76676e-10,0.043049,0.000354652,8.4966e-07,2.56114e-09,0.0434045,0.000356359,8.57343e-07,-2.21744e-09,0.0437617,0.000358067,8.50691e-07,2.58344e-09,0.0441206,0.000359776,8.58441e-07,-6.65826e-10,0.0444813,0.000361491,8.56444e-07,7.99218e-11,0.0448436,0.000363204,8.56684e-07,3.46063e-10,0.0452077,0.000364919,8.57722e-07,2.26116e-09,0.0455734,0.000366641,8.64505e-07,-1.94005e-09,0.045941,0.000368364,8.58685e-07,1.77384e-09,0.0463102,0.000370087,8.64007e-07,-1.43005e-09,0.0466811,0.000371811,8.59717e-07,3.94634e-09,0.0470538,0.000373542,8.71556e-07,-3.17946e-09,0.0474282,0.000375276,8.62017e-07,1.32104e-09,0.0478043,0.000377003,8.6598e-07,1.62045e-09,0.0481822,0.00037874,8.70842e-07,-3.52297e-10,0.0485618,0.000380481,8.69785e-07,-2.11211e-10,0.0489432,0.00038222,8.69151e-07,1.19716e-09,0.0493263,0.000383962,8.72743e-07,-8.52026e-10,0.0497111,0.000385705,8.70187e-07,2.21092e-09,0.0500977,0.000387452,8.76819e-07,-5.41339e-10,0.050486,0.000389204,8.75195e-07,-4.5361e-11,0.0508761,0.000390954,8.75059e-07,7.22669e-10,0.0512679,0.000392706,8.77227e-07,8.79936e-10,0.0516615,0.000394463,8.79867e-07,-5.17048e-10,0.0520568,0.000396222,8.78316e-07,1.18833e-09,0.0524539,0.000397982,8.81881e-07,-5.11022e-10,0.0528528,0.000399744,8.80348e-07,8.55683e-10,0.0532534,0.000401507,8.82915e-07,8.13562e-10,0.0536558,0.000403276,8.85356e-07,-3.84603e-10,0.05406,0.000405045,8.84202e-07,7.24962e-10,0.0544659,0.000406816,8.86377e-07,1.20986e-09,0.0548736,0.000408592,8.90006e-07,-1.83896e-09,0.0552831,0.000410367,8.84489e-07,2.42071e-09,0.0556944,0.000412143,8.91751e-07,-3.93413e-10,0.0561074,0.000413925,8.90571e-07,-8.46967e-10,0.0565222,0.000415704,8.8803e-07,3.78122e-09,0.0569388,0.000417491,8.99374e-07,-3.1021e-09,0.0573572,0.000419281,8.90068e-07,1.17658e-09,0.0577774,0.000421064,8.93597e-07,2.12117e-09,0.0581993,0.000422858,8.99961e-07,-2.21068e-09,0.0586231,0.000424651,8.93329e-07,2.9961e-09,0.0590486,0.000426447,9.02317e-07,-2.32311e-09,0.059476,0.000428244,8.95348e-07,2.57122e-09,0.0599051,0.000430043,9.03062e-07,-5.11098e-10,0.0603361,0.000431847,9.01528e-07,-5.27166e-10,0.0607688,0.000433649,8.99947e-07,2.61984e-09,0.0612034,0.000435457,9.07806e-07,-2.50141e-09,0.0616397,0.000437265,9.00302e-07,3.66045e-09,0.0620779,0.000439076,9.11283e-07,-4.68977e-09,0.0625179,0.000440885,8.97214e-07,7.64783e-09,0.0629597,0.000442702,9.20158e-07,-7.27499e-09,0.0634033,0.000444521,8.98333e-07,6.55113e-09,0.0638487,0.000446337,9.17986e-07,-4.02844e-09,0.0642959,0.000448161,9.05901e-07,2.11196e-09,0.064745,0.000449979,9.12236e-07,3.03125e-09,0.0651959,0.000451813,9.2133e-07,-6.78648e-09,0.0656486,0.000453635,9.00971e-07,9.21375e-09,0.0661032,0.000455464,9.28612e-07,-7.71684e-09,0.0665596,0.000457299,9.05462e-07,6.7522e-09,0.0670178,0.00045913,9.25718e-07,-4.3907e-09,0.0674778,0.000460968,9.12546e-07,3.36e-09,0.0679397,0.000462803,9.22626e-07,-1.59876e-09,0.0684034,0.000464644,9.1783e-07,3.0351e-09,0.068869,0.000466488,9.26935e-07,-3.09101e-09,0.0693364,0.000468333,9.17662e-07,1.8785e-09,0.0698057,0.000470174,9.23298e-07,3.02733e-09,0.0702768,0.00047203,9.3238e-07,-6.53722e-09,0.0707497,0.000473875,9.12768e-07,8.22054e-09,0.0712245,0.000475725,9.37429e-07,-3.99325e-09,0.0717012,0.000477588,9.2545e-07,3.01839e-10,0.0721797,0.00047944,9.26355e-07,2.78597e-09,0.0726601,0.000481301,9.34713e-07,-3.99507e-09,0.0731423,0.000483158,9.22728e-07,5.7435e-09,0.0736264,0.000485021,9.39958e-07,-4.07776e-09,0.0741123,0.000486888,9.27725e-07,3.11695e-09,0.0746002,0.000488753,9.37076e-07,-9.39394e-10,0.0750898,0.000490625,9.34258e-07,6.4055e-10,0.0755814,0.000492495,9.3618e-07,-1.62265e-09,0.0760748,0.000494363,9.31312e-07,5.84995e-09,0.0765701,0.000496243,9.48861e-07,-6.87601e-09,0.0770673,0.00049812,9.28233e-07,6.75296e-09,0.0775664,0.000499997,9.48492e-07,-5.23467e-09,0.0780673,0.000501878,9.32788e-07,6.73523e-09,0.0785701,0.000503764,9.52994e-07,-6.80514e-09,0.0790748,0.000505649,9.32578e-07,5.5842e-09,0.0795814,0.000507531,9.49331e-07,-6.30583e-10,0.0800899,0.000509428,9.47439e-07,-3.0618e-09,0.0806003,0.000511314,9.38254e-07,5.4273e-09,0.0811125,0.000513206,9.54536e-07,-3.74627e-09,0.0816267,0.000515104,9.43297e-07,2.10713e-09,0.0821427,0.000516997,9.49618e-07,2.76839e-09,0.0826607,0.000518905,9.57924e-07,-5.73006e-09,0.0831805,0.000520803,9.40733e-07,5.25072e-09,0.0837023,0.0005227,9.56486e-07,-3.71718e-10,0.084226,0.000524612,9.5537e-07,-3.76404e-09,0.0847515,0.000526512,9.44078e-07,7.97735e-09,0.085279,0.000528424,9.6801e-07,-5.79367e-09,0.0858084,0.000530343,9.50629e-07,2.96268e-10,0.0863397,0.000532245,9.51518e-07,4.6086e-09,0.0868729,0.000534162,9.65344e-07,-3.82947e-09,0.087408,0.000536081,9.53856e-07,3.25861e-09,0.087945,0.000537998,9.63631e-07,-1.7543e-09,0.088484,0.00053992,9.58368e-07,3.75849e-09,0.0890249,0.000541848,9.69644e-07,-5.82891e-09,0.0895677,0.00054377,9.52157e-07,4.65593e-09,0.0901124,0.000545688,9.66125e-07,2.10643e-09,0.0906591,0.000547627,9.72444e-07,-5.63099e-09,0.0912077,0.000549555,9.55551e-07,5.51627e-09,0.0917582,0.000551483,9.721e-07,-1.53292e-09,0.0923106,0.000553422,9.67501e-07,6.15311e-10,0.092865,0.000555359,9.69347e-07,-9.28291e-10,0.0934213,0.000557295,9.66562e-07,3.09774e-09,0.0939796,0.000559237,9.75856e-07,-4.01186e-09,0.0945398,0.000561177,9.6382e-07,5.49892e-09,0.095102,0.000563121,9.80317e-07,-3.08258e-09,0.0956661,0.000565073,9.71069e-07,-6.19176e-10,0.0962321,0.000567013,9.69212e-07,5.55932e-09,0.0968001,0.000568968,9.8589e-07,-6.71704e-09,0.09737,0.00057092,9.65738e-07,6.40762e-09,0.0979419,0.00057287,9.84961e-07,-4.0122e-09,0.0985158,0.000574828,9.72925e-07,2.19059e-09,0.0990916,0.000576781,9.79496e-07,2.70048e-09,0.0996693,0.000578748,9.87598e-07,-5.54193e-09,0.100249,0.000580706,9.70972e-07,4.56597e-09,0.100831,0.000582662,9.8467e-07,2.17923e-09,0.101414,0.000584638,9.91208e-07,-5.83232e-09,0.102,0.000586603,9.73711e-07,6.24884e-09,0.102588,0.000588569,9.92457e-07,-4.26178e-09,0.103177,0.000590541,9.79672e-07,3.34781e-09,0.103769,0.00059251,9.89715e-07,-1.67904e-09,0.104362,0.000594485,9.84678e-07,3.36839e-09,0.104958,0.000596464,9.94783e-07,-4.34397e-09,0.105555,0.000598441,9.81751e-07,6.55696e-09,0.106155,0.000600424,1.00142e-06,-6.98272e-09,0.106756,0.000602406,9.80474e-07,6.4728e-09,0.107359,0.000604386,9.99893e-07,-4.00742e-09,0.107965,0.000606374,9.8787e-07,2.10654e-09,0.108572,0.000608356,9.9419e-07,3.0318e-09,0.109181,0.000610353,1.00329e-06,-6.7832e-09,0.109793,0.00061234,9.82936e-07,9.1998e-09,0.110406,0.000614333,1.01054e-06,-7.6642e-09,0.111021,0.000616331,9.87543e-07,6.55579e-09,0.111639,0.000618326,1.00721e-06,-3.65791e-09,0.112258,0.000620329,9.96236e-07,6.25467e-10,0.112879,0.000622324,9.98113e-07,1.15593e-09,0.113503,0.000624323,1.00158e-06,2.20158e-09,0.114128,0.000626333,1.00819e-06,-2.51191e-09,0.114755,0.000628342,1.00065e-06,3.95517e-10,0.115385,0.000630345,1.00184e-06,9.29807e-10,0.116016,0.000632351,1.00463e-06,3.33599e-09,0.116649,0.00063437,1.01463e-06,-6.82329e-09,0.117285,0.000636379,9.94163e-07,9.05595e-09,0.117922,0.000638395,1.02133e-06,-7.04862e-09,0.118562,0.000640416,1.00019e-06,4.23737e-09,0.119203,0.000642429,1.0129e-06,-2.45033e-09,0.119847,0.000644448,1.00555e-06,5.56395e-09,0.120492,0.000646475,1.02224e-06,-4.9043e-09,0.121139,0.000648505,1.00753e-06,-8.47952e-10,0.121789,0.000650518,1.00498e-06,8.29622e-09,0.122441,0.000652553,1.02987e-06,-9.98538e-09,0.123094,0.000654582,9.99914e-07,9.2936e-09,0.12375,0.00065661,1.02779e-06,-4.83707e-09,0.124407,0.000658651,1.01328e-06,2.60411e-09,0.125067,0.000660685,1.0211e-06,-5.57945e-09,0.125729,0.000662711,1.00436e-06,1.22631e-08,0.126392,0.000664756,1.04115e-06,-1.36704e-08,0.127058,0.000666798,1.00014e-06,1.26161e-08,0.127726,0.000668836,1.03798e-06,-6.99155e-09,0.128396,0.000670891,1.01701e-06,4.48836e-10,0.129068,0.000672926,1.01836e-06,5.19606e-09,0.129742,0.000674978,1.03394e-06,-6.3319e-09,0.130418,0.000677027,1.01495e-06,5.2305e-09,0.131096,0.000679073,1.03064e-06,3.11123e-10,0.131776,0.000681135,1.03157e-06,-6.47511e-09,0.132458,0.000683179,1.01215e-06,1.06882e-08,0.133142,0.000685235,1.04421e-06,-6.47519e-09,0.133829,0.000687304,1.02479e-06,3.11237e-10,0.134517,0.000689355,1.02572e-06,5.23035e-09,0.135207,0.000691422,1.04141e-06,-6.3316e-09,0.1359,0.000693486,1.02242e-06,5.19484e-09,0.136594,0.000695546,1.038e-06,4.53497e-10,0.137291,0.000697623,1.03936e-06,-7.00891e-09,0.137989,0.000699681,1.01834e-06,1.2681e-08,0.13869,0.000701756,1.05638e-06,-1.39128e-08,0.139393,0.000703827,1.01464e-06,1.31679e-08,0.140098,0.000705896,1.05414e-06,-8.95659e-09,0.140805,0.000707977,1.02727e-06,7.75742e-09,0.141514,0.000710055,1.05055e-06,-7.17182e-09,0.142225,0.000712135,1.02903e-06,6.02862e-09,0.142938,0.000714211,1.04712e-06,-2.04163e-09,0.143653,0.000716299,1.04099e-06,2.13792e-09,0.144371,0.000718387,1.04741e-06,-6.51009e-09,0.14509,0.000720462,1.02787e-06,9.00123e-09,0.145812,0.000722545,1.05488e-06,3.07523e-10,0.146535,0.000724656,1.0558e-06,-1.02312e-08,0.147261,0.000726737,1.02511e-06,1.0815e-08,0.147989,0.000728819,1.05755e-06,-3.22681e-09,0.148719,0.000730925,1.04787e-06,2.09244e-09,0.14945,0.000733027,1.05415e-06,-5.143e-09,0.150185,0.00073512,1.03872e-06,3.57844e-09,0.150921,0.000737208,1.04946e-06,5.73027e-09,0.151659,0.000739324,1.06665e-06,-1.15983e-08,0.152399,0.000741423,1.03185e-06,1.08605e-08,0.153142,0.000743519,1.06443e-06,-2.04106e-09,0.153886,0.000745642,1.05831e-06,-2.69642e-09,0.154633,0.00074775,1.05022e-06,-2.07425e-09,0.155382,0.000749844,1.044e-06,1.09934e-08,0.156133,0.000751965,1.07698e-06,-1.20972e-08,0.156886,0.000754083,1.04069e-06,7.59288e-09,0.157641,0.000756187,1.06347e-06,-3.37305e-09,0.158398,0.000758304,1.05335e-06,5.89921e-09,0.159158,0.000760428,1.07104e-06,-5.32248e-09,0.159919,0.000762554,1.05508e-06,4.8927e-10,0.160683,0.000764666,1.05654e-06,3.36547e-09,0.161448,0.000766789,1.06664e-06,9.50081e-10,0.162216,0.000768925,1.06949e-06,-7.16568e-09,0.162986,0.000771043,1.04799e-06,1.28114e-08,0.163758,0.000773177,1.08643e-06,-1.42774e-08,0.164533,0.000775307,1.0436e-06,1.44956e-08,0.165309,0.000777438,1.08708e-06,-1.39025e-08,0.166087,0.00077957,1.04538e-06,1.13118e-08,0.166868,0.000781695,1.07931e-06,-1.54224e-09,0.167651,0.000783849,1.07468e-06,-5.14312e-09,0.168436,0.000785983,1.05925e-06,7.21381e-09,0.169223,0.000788123,1.0809e-06,-8.81096e-09,0.170012,0.000790259,1.05446e-06,1.31289e-08,0.170803,0.000792407,1.09385e-06,-1.39022e-08,0.171597,0.000794553,1.05214e-06,1.26775e-08,0.172392,0.000796695,1.09018e-06,-7.00557e-09,0.17319,0.000798855,1.06916e-06,4.43796e-10,0.17399,0.000800994,1.07049e-06,5.23031e-09,0.174792,0.000803151,1.08618e-06,-6.46397e-09,0.175596,0.000805304,1.06679e-06,5.72444e-09,0.176403,0.000807455,1.08396e-06,-1.53254e-09,0.177211,0.000809618,1.07937e-06,4.05673e-10,0.178022,0.000811778,1.08058e-06,-9.01916e-11,0.178835,0.000813939,1.08031e-06,-4.49821e-11,0.17965,0.000816099,1.08018e-06,2.70234e-10,0.180467,0.00081826,1.08099e-06,-1.03603e-09,0.181286,0.000820419,1.07788e-06,3.87392e-09,0.182108,0.000822587,1.0895e-06,4.41522e-10,0.182932,0.000824767,1.09083e-06,-5.63997e-09,0.183758,0.000826932,1.07391e-06,7.21707e-09,0.184586,0.000829101,1.09556e-06,-8.32718e-09,0.185416,0.000831267,1.07058e-06,1.11907e-08,0.186248,0.000833442,1.10415e-06,-6.63336e-09,0.187083,0.00083563,1.08425e-06,4.41484e-10,0.187919,0.0008378,1.08557e-06,4.86754e-09,0.188758,0.000839986,1.10017e-06,-5.01041e-09,0.189599,0.000842171,1.08514e-06,2.72811e-10,0.190443,0.000844342,1.08596e-06,3.91916e-09,0.191288,0.000846526,1.09772e-06,-1.04819e-09,0.192136,0.000848718,1.09457e-06,2.73531e-10,0.192985,0.000850908,1.0954e-06,-4.58916e-11,0.193837,0.000853099,1.09526e-06,-9.01158e-11,0.194692,0.000855289,1.09499e-06,4.06506e-10,0.195548,0.00085748,1.09621e-06,-1.53595e-09,0.196407,0.000859668,1.0916e-06,5.73717e-09,0.197267,0.000861869,1.10881e-06,-6.51164e-09,0.19813,0.000864067,1.08928e-06,5.40831e-09,0.198995,0.000866261,1.1055e-06,-2.20401e-10,0.199863,0.000868472,1.10484e-06,-4.52652e-09,0.200732,0.000870668,1.09126e-06,3.42508e-09,0.201604,0.000872861,1.10153e-06,5.72762e-09,0.202478,0.000875081,1.11872e-06,-1.14344e-08,0.203354,0.000877284,1.08441e-06,1.02076e-08,0.204233,0.000879484,1.11504e-06,4.06355e-10,0.205113,0.000881715,1.11626e-06,-1.18329e-08,0.205996,0.000883912,1.08076e-06,1.71227e-08,0.206881,0.000886125,1.13213e-06,-1.19546e-08,0.207768,0.000888353,1.09626e-06,8.93465e-10,0.208658,0.000890548,1.09894e-06,8.38062e-09,0.209549,0.000892771,1.12408e-06,-4.61353e-09,0.210443,0.000895006,1.11024e-06,-4.82756e-09,0.211339,0.000897212,1.09576e-06,9.02245e-09,0.212238,0.00089943,1.12283e-06,-1.45997e-09,0.213138,0.000901672,1.11845e-06,-3.18255e-09,0.214041,0.000903899,1.1089e-06,-7.11073e-10,0.214946,0.000906115,1.10677e-06,6.02692e-09,0.215853,0.000908346,1.12485e-06,-8.49548e-09,0.216763,0.00091057,1.09936e-06,1.30537e-08,0.217675,0.000912808,1.13852e-06,-1.3917e-08,0.218588,0.000915044,1.09677e-06,1.28121e-08,0.219505,0.000917276,1.13521e-06,-7.5288e-09,0.220423,0.000919523,1.11262e-06,2.40205e-09,0.221344,0.000921756,1.11983e-06,-2.07941e-09,0.222267,0.000923989,1.11359e-06,5.91551e-09,0.223192,0.000926234,1.13134e-06,-6.68149e-09,0.224119,0.000928477,1.11129e-06,5.90929e-09,0.225049,0.000930717,1.12902e-06,-2.05436e-09,0.22598,0.000932969,1.12286e-06,2.30807e-09,0.226915,0.000935222,1.12978e-06,-7.17796e-09,0.227851,0.00093746,1.10825e-06,1.15028e-08,0.228789,0.000939711,1.14276e-06,-9.03083e-09,0.22973,0.000941969,1.11566e-06,9.71932e-09,0.230673,0.00094423,1.14482e-06,-1.49452e-08,0.231619,0.000946474,1.09998e-06,2.02591e-08,0.232566,0.000948735,1.16076e-06,-2.13879e-08,0.233516,0.000950993,1.0966e-06,2.05888e-08,0.234468,0.000953247,1.15837e-06,-1.62642e-08,0.235423,0.000955515,1.10957e-06,1.46658e-08,0.236379,0.000957779,1.15357e-06,-1.25966e-08,0.237338,0.000960048,1.11578e-06,5.91793e-09,0.238299,0.000962297,1.13353e-06,3.82602e-09,0.239263,0.000964576,1.14501e-06,-6.3208e-09,0.240229,0.000966847,1.12605e-06,6.55613e-09,0.241197,0.000969119,1.14572e-06,-5.00268e-09,0.242167,0.000971395,1.13071e-06,-1.44659e-09,0.243139,0.000973652,1.12637e-06,1.07891e-08,0.244114,0.000975937,1.15874e-06,-1.19073e-08,0.245091,0.000978219,1.12302e-06,7.03782e-09,0.246071,0.000980486,1.14413e-06,-1.34276e-09,0.247052,0.00098277,1.1401e-06,-1.66669e-09,0.248036,0.000985046,1.1351e-06,8.00935e-09,0.249022,0.00098734,1.15913e-06,-1.54694e-08,0.250011,0.000989612,1.11272e-06,2.4066e-08,0.251002,0.000991909,1.18492e-06,-2.11901e-08,0.251995,0.000994215,1.12135e-06,1.08973e-09,0.25299,0.000996461,1.12462e-06,1.68311e-08,0.253988,0.000998761,1.17511e-06,-8.8094e-09,0.254987,0.00100109,1.14868e-06,-1.13958e-08,0.25599,0.00100335,1.1145e-06,2.45902e-08,0.256994,0.00100565,1.18827e-06,-2.73603e-08,0.258001,0.00100795,1.10618e-06,2.52464e-08,0.25901,0.00101023,1.18192e-06,-1.40207e-08,0.260021,0.00101256,1.13986e-06,1.03387e-09,0.261035,0.00101484,1.14296e-06,9.8853e-09,0.262051,0.00101715,1.17262e-06,-1.07726e-08,0.263069,0.00101947,1.1403e-06,3.40272e-09,0.26409,0.00102176,1.15051e-06,-2.83827e-09,0.265113,0.00102405,1.142e-06,7.95039e-09,0.266138,0.00102636,1.16585e-06,8.39047e-10,0.267166,0.00102869,1.16836e-06,-1.13066e-08,0.268196,0.00103099,1.13444e-06,1.4585e-08,0.269228,0.00103331,1.1782e-06,-1.72314e-08,0.270262,0.00103561,1.1265e-06,2.45382e-08,0.271299,0.00103794,1.20012e-06,-2.13166e-08,0.272338,0.00104028,1.13617e-06,1.12364e-09,0.273379,0.00104255,1.13954e-06,1.68221e-08,0.274423,0.00104488,1.19001e-06,-8.80736e-09,0.275469,0.00104723,1.16358e-06,-1.13948e-08,0.276518,0.00104953,1.1294e-06,2.45839e-08,0.277568,0.00105186,1.20315e-06,-2.73361e-08,0.278621,0.00105418,1.12114e-06,2.51559e-08,0.279677,0.0010565,1.19661e-06,-1.36832e-08,0.280734,0.00105885,1.15556e-06,-2.25706e-10,0.281794,0.00106116,1.15488e-06,1.45862e-08,0.282857,0.00106352,1.19864e-06,-2.83167e-08,0.283921,0.00106583,1.11369e-06,3.90759e-08,0.284988,0.00106817,1.23092e-06,-3.85801e-08,0.286058,0.00107052,1.11518e-06,2.58375e-08,0.287129,0.00107283,1.19269e-06,-5.16498e-09,0.288203,0.0010752,1.1772e-06,-5.17768e-09,0.28928,0.00107754,1.16167e-06,-3.92671e-09,0.290358,0.00107985,1.14988e-06,2.08846e-08,0.29144,0.00108221,1.21254e-06,-2.00072e-08,0.292523,0.00108458,1.15252e-06,-4.60659e-10,0.293609,0.00108688,1.15114e-06,2.18499e-08,0.294697,0.00108925,1.21669e-06,-2.73343e-08,0.295787,0.0010916,1.13468e-06,2.78826e-08,0.29688,0.00109395,1.21833e-06,-2.45915e-08,0.297975,0.00109632,1.14456e-06,1.08787e-08,0.299073,0.00109864,1.17719e-06,1.08788e-08,0.300172,0.00110102,1.20983e-06,-2.45915e-08,0.301275,0.00110337,1.13605e-06,2.78828e-08,0.302379,0.00110573,1.2197e-06,-2.73348e-08,0.303486,0.00110808,1.1377e-06,2.18518e-08,0.304595,0.00111042,1.20325e-06,-4.67556e-10,0.305707,0.00111283,1.20185e-06,-1.99816e-08,0.306821,0.00111517,1.14191e-06,2.07891e-08,0.307937,0.00111752,1.20427e-06,-3.57026e-09,0.309056,0.00111992,1.19356e-06,-6.50797e-09,0.310177,0.00112228,1.17404e-06,-2.00165e-10,0.3113,0.00112463,1.17344e-06,7.30874e-09,0.312426,0.001127,1.19536e-06,7.67424e-10,0.313554,0.00112939,1.19767e-06,-1.03784e-08,0.314685,0.00113176,1.16653e-06,1.09437e-08,0.315818,0.00113412,1.19936e-06,-3.59406e-09,0.316953,0.00113651,1.18858e-06,3.43251e-09,0.318091,0.0011389,1.19888e-06,-1.0136e-08,0.319231,0.00114127,1.16847e-06,7.30915e-09,0.320374,0.00114363,1.1904e-06,1.07018e-08,0.321518,0.00114604,1.2225e-06,-2.03137e-08,0.322666,0.00114842,1.16156e-06,1.09484e-08,0.323815,0.00115078,1.19441e-06,6.32224e-09,0.324967,0.00115319,1.21337e-06,-6.43509e-09,0.326122,0.00115559,1.19407e-06,-1.03842e-08,0.327278,0.00115795,1.16291e-06,1.81697e-08,0.328438,0.00116033,1.21742e-06,-2.6901e-09,0.329599,0.00116276,1.20935e-06,-7.40939e-09,0.330763,0.00116515,1.18713e-06,2.52533e-09,0.331929,0.00116754,1.1947e-06,-2.69191e-09,0.333098,0.00116992,1.18663e-06,8.24218e-09,0.334269,0.00117232,1.21135e-06,-4.74377e-10,0.335443,0.00117474,1.20993e-06,-6.34471e-09,0.336619,0.00117714,1.1909e-06,-3.94922e-09,0.337797,0.00117951,1.17905e-06,2.21417e-08,0.338978,0.00118193,1.24547e-06,-2.50128e-08,0.340161,0.00118435,1.17043e-06,1.8305e-08,0.341346,0.00118674,1.22535e-06,-1.84048e-08,0.342534,0.00118914,1.17013e-06,2.55121e-08,0.343725,0.00119156,1.24667e-06,-2.40389e-08,0.344917,0.00119398,1.17455e-06,1.10389e-08,0.346113,0.00119636,1.20767e-06,9.68574e-09,0.34731,0.0011988,1.23673e-06,-1.99797e-08,0.34851,0.00120122,1.17679e-06,1.06284e-08,0.349713,0.0012036,1.20867e-06,7.26868e-09,0.350917,0.00120604,1.23048e-06,-9.90072e-09,0.352125,0.00120847,1.20078e-06,2.53177e-09,0.353334,0.00121088,1.20837e-06,-2.26199e-10,0.354546,0.0012133,1.20769e-06,-1.62705e-09,0.355761,0.00121571,1.20281e-06,6.73435e-09,0.356978,0.00121813,1.22302e-06,4.49207e-09,0.358197,0.00122059,1.23649e-06,-2.47027e-08,0.359419,0.00122299,1.16238e-06,3.47142e-08,0.360643,0.00122542,1.26653e-06,-2.47472e-08,0.36187,0.00122788,1.19229e-06,4.66965e-09,0.363099,0.00123028,1.20629e-06,6.06872e-09,0.36433,0.00123271,1.2245e-06,8.57729e-10,0.365564,0.00123516,1.22707e-06,-9.49952e-09,0.366801,0.00123759,1.19858e-06,7.33792e-09,0.36804,0.00124001,1.22059e-06,9.95025e-09,0.369281,0.00124248,1.25044e-06,-1.73366e-08,0.370525,0.00124493,1.19843e-06,-2.08464e-10,0.371771,0.00124732,1.1978e-06,1.81704e-08,0.373019,0.00124977,1.25232e-06,-1.28683e-08,0.37427,0.00125224,1.21371e-06,3.50042e-09,0.375524,0.00125468,1.22421e-06,-1.1335e-09,0.37678,0.00125712,1.22081e-06,1.03345e-09,0.378038,0.00125957,1.22391e-06,-3.00023e-09,0.379299,0.00126201,1.21491e-06,1.09676e-08,0.380562,0.00126447,1.24781e-06,-1.10676e-08,0.381828,0.00126693,1.21461e-06,3.50042e-09,0.383096,0.00126937,1.22511e-06,-2.93403e-09,0.384366,0.00127181,1.21631e-06,8.23574e-09,0.385639,0.00127427,1.24102e-06,-2.06607e-10,0.386915,0.00127675,1.2404e-06,-7.40935e-09,0.388193,0.00127921,1.21817e-06,4.1761e-11,0.389473,0.00128165,1.21829e-06,7.24223e-09,0.390756,0.0012841,1.24002e-06,7.91564e-10,0.392042,0.00128659,1.2424e-06,-1.04086e-08,0.393329,0.00128904,1.21117e-06,1.10405e-08,0.39462,0.0012915,1.24429e-06,-3.951e-09,0.395912,0.00129397,1.23244e-06,4.7634e-09,0.397208,0.00129645,1.24673e-06,-1.51025e-08,0.398505,0.0012989,1.20142e-06,2.58443e-08,0.399805,0.00130138,1.27895e-06,-2.86702e-08,0.401108,0.00130385,1.19294e-06,2.92318e-08,0.402413,0.00130632,1.28064e-06,-2.86524e-08,0.403721,0.0013088,1.19468e-06,2.57731e-08,0.405031,0.00131127,1.272e-06,-1.48355e-08,0.406343,0.00131377,1.2275e-06,3.76652e-09,0.407658,0.00131623,1.23879e-06,-2.30784e-10,0.408976,0.00131871,1.2381e-06,-2.84331e-09,0.410296,0.00132118,1.22957e-06,1.16041e-08,0.411618,0.00132367,1.26438e-06,-1.37708e-08,0.412943,0.00132616,1.22307e-06,1.36768e-08,0.41427,0.00132865,1.2641e-06,-1.1134e-08,0.4156,0.00133114,1.2307e-06,1.05714e-09,0.416933,0.00133361,1.23387e-06,6.90538e-09,0.418267,0.00133609,1.25459e-06,1.12372e-09,0.419605,0.00133861,1.25796e-06,-1.14002e-08,0.420945,0.00134109,1.22376e-06,1.46747e-08,0.422287,0.00134358,1.26778e-06,-1.7496e-08,0.423632,0.00134606,1.21529e-06,2.5507e-08,0.424979,0.00134857,1.29182e-06,-2.49272e-08,0.426329,0.00135108,1.21703e-06,1.45972e-08,0.427681,0.00135356,1.26083e-06,-3.65935e-09,0.429036,0.00135607,1.24985e-06,4.00178e-11,0.430393,0.00135857,1.24997e-06,3.49917e-09,0.431753,0.00136108,1.26047e-06,-1.40366e-08,0.433116,0.00136356,1.21836e-06,2.28448e-08,0.43448,0.00136606,1.28689e-06,-1.77378e-08,0.435848,0.00136858,1.23368e-06,1.83043e-08,0.437218,0.0013711,1.28859e-06,-2.56769e-08,0.43859,0.0013736,1.21156e-06,2.47987e-08,0.439965,0.0013761,1.28595e-06,-1.39133e-08,0.441342,0.00137863,1.24421e-06,1.05202e-09,0.442722,0.00138112,1.24737e-06,9.70507e-09,0.444104,0.00138365,1.27649e-06,-1.00698e-08,0.445489,0.00138617,1.24628e-06,7.72123e-10,0.446877,0.00138867,1.24859e-06,6.98132e-09,0.448267,0.00139118,1.26954e-06,1.10477e-09,0.449659,0.00139373,1.27285e-06,-1.14003e-08,0.451054,0.00139624,1.23865e-06,1.4694e-08,0.452452,0.00139876,1.28273e-06,-1.75734e-08,0.453852,0.00140127,1.23001e-06,2.5797e-08,0.455254,0.00140381,1.3074e-06,-2.60097e-08,0.456659,0.00140635,1.22937e-06,1.86371e-08,0.458067,0.00140886,1.28529e-06,-1.8736e-08,0.459477,0.00141137,1.22908e-06,2.65048e-08,0.46089,0.00141391,1.30859e-06,-2.76784e-08,0.462305,0.00141645,1.22556e-06,2.46043e-08,0.463722,0.00141897,1.29937e-06,-1.11341e-08,0.465143,0.00142154,1.26597e-06,-9.87033e-09,0.466565,0.00142404,1.23636e-06,2.08131e-08,0.467991,0.00142657,1.2988e-06,-1.37773e-08,0.469419,0.00142913,1.25746e-06,4.49378e-09,0.470849,0.00143166,1.27094e-06,-4.19781e-09,0.472282,0.00143419,1.25835e-06,1.22975e-08,0.473717,0.00143674,1.29524e-06,-1.51902e-08,0.475155,0.00143929,1.24967e-06,1.86608e-08,0.476596,0.00144184,1.30566e-06,-2.96506e-08,0.478039,0.00144436,1.2167e-06,4.03368e-08,0.479485,0.00144692,1.33771e-06,-4.22896e-08,0.480933,0.00144947,1.21085e-06,3.94148e-08,0.482384,0.00145201,1.32909e-06,-2.59626e-08,0.483837,0.00145459,1.2512e-06,4.83124e-09,0.485293,0.0014571,1.2657e-06,6.63757e-09,0.486751,0.00145966,1.28561e-06,-1.57911e-09,0.488212,0.00146222,1.28087e-06,-3.21468e-10,0.489676,0.00146478,1.27991e-06,2.86517e-09,0.491142,0.00146735,1.2885e-06,-1.11392e-08,0.49261,0.00146989,1.25508e-06,1.18893e-08,0.494081,0.00147244,1.29075e-06,-6.61574e-09,0.495555,0.001475,1.27091e-06,1.45736e-08,0.497031,0.00147759,1.31463e-06,-2.18759e-08,0.49851,0.00148015,1.249e-06,1.33252e-08,0.499992,0.00148269,1.28897e-06,-1.62277e-09,0.501476,0.00148526,1.28411e-06,-6.83421e-09,0.502962,0.00148781,1.2636e-06,2.89596e-08,0.504451,0.00149042,1.35048e-06,-4.93997e-08,0.505943,0.00149298,1.20228e-06,4.94299e-08,0.507437,0.00149553,1.35057e-06,-2.91107e-08,0.508934,0.00149814,1.26324e-06,7.40848e-09,0.510434,0.00150069,1.28547e-06,-5.23187e-10,0.511936,0.00150326,1.2839e-06,-5.31585e-09,0.51344,0.00150581,1.26795e-06,2.17866e-08,0.514947,0.00150841,1.33331e-06,-2.22257e-08,0.516457,0.00151101,1.26663e-06,7.51178e-09,0.517969,0.00151357,1.28917e-06,-7.82128e-09,0.519484,0.00151613,1.2657e-06,2.37733e-08,0.521002,0.00151873,1.33702e-06,-2.76674e-08,0.522522,0.00152132,1.25402e-06,2.72917e-08,0.524044,0.00152391,1.3359e-06,-2.18949e-08,0.525569,0.00152652,1.27021e-06,6.83372e-10,0.527097,0.00152906,1.27226e-06,1.91613e-08,0.528628,0.00153166,1.32974e-06,-1.77241e-08,0.53016,0.00153427,1.27657e-06,-7.86963e-09,0.531696,0.0015368,1.25296e-06,4.92027e-08,0.533234,0.00153945,1.40057e-06,-6.9732e-08,0.534775,0.00154204,1.19138e-06,5.09114e-08,0.536318,0.00154458,1.34411e-06,-1.4704e-08,0.537864,0.00154722,1.3e-06,7.9048e-09,0.539413,0.00154984,1.32371e-06,-1.69152e-08,0.540964,0.00155244,1.27297e-06,1.51355e-10,0.542517,0.00155499,1.27342e-06,1.63099e-08,0.544074,0.00155758,1.32235e-06,-5.78647e-09,0.545633,0.00156021,1.30499e-06,6.83599e-09,0.547194,0.00156284,1.3255e-06,-2.15575e-08,0.548758,0.00156543,1.26083e-06,1.97892e-08,0.550325,0.00156801,1.32019e-06,2.00525e-09,0.551894,0.00157065,1.32621e-06,-2.78103e-08,0.553466,0.00157322,1.24278e-06,4.96314e-08,0.555041,0.00157586,1.39167e-06,-5.1506e-08,0.556618,0.00157849,1.23716e-06,3.71835e-08,0.558198,0.00158107,1.34871e-06,-3.76233e-08,0.55978,0.00158366,1.23584e-06,5.37052e-08,0.561365,0.00158629,1.39695e-06,-5.79884e-08,0.562953,0.00158891,1.22299e-06,5.90392e-08,0.564543,0.00159153,1.4001e-06,-5.89592e-08,0.566136,0.00159416,1.22323e-06,5.7588e-08,0.567731,0.00159678,1.39599e-06,-5.21835e-08,0.569329,0.00159941,1.23944e-06,3.19369e-08,0.57093,0.00160199,1.33525e-06,-1.59594e-08,0.572533,0.00160461,1.28737e-06,3.19006e-08,0.574139,0.00160728,1.38307e-06,-5.20383e-08,0.575748,0.00160989,1.22696e-06,5.70431e-08,0.577359,0.00161251,1.39809e-06,-5.69247e-08,0.578973,0.00161514,1.22731e-06,5.14463e-08,0.580589,0.00161775,1.38165e-06,-2.9651e-08,0.582208,0.00162042,1.2927e-06,7.55339e-09,0.58383,0.00162303,1.31536e-06,-5.62636e-10,0.585455,0.00162566,1.31367e-06,-5.30281e-09,0.587081,0.00162827,1.29776e-06,2.17738e-08,0.588711,0.00163093,1.36309e-06,-2.21875e-08,0.590343,0.00163359,1.29652e-06,7.37164e-09,0.591978,0.00163621,1.31864e-06,-7.29907e-09,0.593616,0.00163882,1.29674e-06,2.18247e-08,0.595256,0.00164148,1.36221e-06,-2.03952e-08,0.596899,0.00164414,1.30103e-06,1.51241e-10,0.598544,0.00164675,1.30148e-06,1.97902e-08,0.600192,0.00164941,1.36085e-06,-1.97074e-08,0.601843,0.00165207,1.30173e-06,-5.65175e-10,0.603496,0.00165467,1.30004e-06,2.1968e-08,0.605152,0.00165734,1.36594e-06,-2.77024e-08,0.606811,0.00165999,1.28283e-06,2.92369e-08,0.608472,0.00166264,1.37054e-06,-2.96407e-08,0.610136,0.00166529,1.28162e-06,2.97215e-08,0.611803,0.00166795,1.37079e-06,-2.96408e-08,0.613472,0.0016706,1.28186e-06,2.92371e-08,0.615144,0.00167325,1.36957e-06,-2.77031e-08,0.616819,0.00167591,1.28647e-06,2.19708e-08,0.618496,0.00167855,1.35238e-06,-5.75407e-10,0.620176,0.00168125,1.35065e-06,-1.9669e-08,0.621858,0.00168389,1.29164e-06,1.96468e-08,0.623544,0.00168653,1.35058e-06,6.86403e-10,0.625232,0.00168924,1.35264e-06,-2.23924e-08,0.626922,0.00169187,1.28547e-06,2.92788e-08,0.628615,0.00169453,1.3733e-06,-3.51181e-08,0.630311,0.00169717,1.26795e-06,5.15889e-08,0.63201,0.00169987,1.42272e-06,-5.2028e-08,0.633711,0.00170255,1.26663e-06,3.73139e-08,0.635415,0.0017052,1.37857e-06,-3.76227e-08,0.637121,0.00170784,1.2657e-06,5.35722e-08,0.63883,0.00171054,1.42642e-06,-5.74567e-08,0.640542,0.00171322,1.25405e-06,5.70456e-08,0.642257,0.0017159,1.42519e-06,-5.15163e-08,0.643974,0.00171859,1.27064e-06,2.98103e-08,0.645694,0.00172122,1.36007e-06,-8.12016e-09,0.647417,0.00172392,1.33571e-06,2.67039e-09,0.649142,0.0017266,1.34372e-06,-2.56152e-09,0.65087,0.00172928,1.33604e-06,7.57571e-09,0.6526,0.00173197,1.35876e-06,-2.77413e-08,0.654334,0.00173461,1.27554e-06,4.3785e-08,0.65607,0.00173729,1.40689e-06,-2.81896e-08,0.657808,0.00174002,1.32233e-06,9.36893e-09,0.65955,0.00174269,1.35043e-06,-9.28617e-09,0.661294,0.00174536,1.32257e-06,2.77757e-08,0.66304,0.00174809,1.4059e-06,-4.2212e-08,0.66479,0.00175078,1.27926e-06,2.1863e-08,0.666542,0.0017534,1.34485e-06,1.43648e-08,0.668297,0.00175613,1.38795e-06,-1.97177e-08,0.670054,0.00175885,1.3288e-06,4.90115e-09,0.671814,0.00176152,1.3435e-06,1.13232e-10,0.673577,0.00176421,1.34384e-06,-5.3542e-09,0.675343,0.00176688,1.32778e-06,2.13035e-08,0.677111,0.0017696,1.39169e-06,-2.02553e-08,0.678882,0.00177232,1.33092e-06,1.13005e-10,0.680656,0.00177499,1.33126e-06,1.98031e-08,0.682432,0.00177771,1.39067e-06,-1.97211e-08,0.684211,0.00178043,1.33151e-06,-5.2349e-10,0.685993,0.00178309,1.32994e-06,2.18151e-08,0.687777,0.00178582,1.39538e-06,-2.71325e-08,0.689564,0.00178853,1.31398e-06,2.71101e-08,0.691354,0.00179124,1.39531e-06,-2.17035e-08,0.693147,0.00179396,1.3302e-06,9.92865e-11,0.694942,0.00179662,1.3305e-06,2.13063e-08,0.69674,0.00179935,1.39442e-06,-2.57198e-08,0.698541,0.00180206,1.31726e-06,2.19682e-08,0.700344,0.00180476,1.38317e-06,-2.54852e-09,0.70215,0.00180752,1.37552e-06,-1.17741e-08,0.703959,0.00181023,1.3402e-06,-9.95999e-09,0.705771,0.00181288,1.31032e-06,5.16141e-08,0.707585,0.00181566,1.46516e-06,-7.72869e-08,0.709402,0.00181836,1.2333e-06,7.87197e-08,0.711222,0.00182106,1.46946e-06,-5.87781e-08,0.713044,0.00182382,1.29312e-06,3.71834e-08,0.714869,0.00182652,1.40467e-06,-3.03511e-08,0.716697,0.00182924,1.31362e-06,2.46161e-08,0.718528,0.00183194,1.38747e-06,-8.5087e-09,0.720361,0.00183469,1.36194e-06,9.41892e-09,0.722197,0.00183744,1.3902e-06,-2.91671e-08,0.724036,0.00184014,1.3027e-06,4.76448e-08,0.725878,0.00184288,1.44563e-06,-4.22028e-08,0.727722,0.00184565,1.31902e-06,1.95682e-09,0.729569,0.00184829,1.3249e-06,3.43754e-08,0.731419,0.00185104,1.42802e-06,-2.0249e-08,0.733271,0.00185384,1.36727e-06,-1.29838e-08,0.735126,0.00185654,1.32832e-06,1.25794e-08,0.736984,0.00185923,1.36606e-06,2.22711e-08,0.738845,0.00186203,1.43287e-06,-4.20594e-08,0.740708,0.00186477,1.3067e-06,2.67571e-08,0.742574,0.00186746,1.38697e-06,-5.36424e-09,0.744443,0.00187022,1.37087e-06,-5.30023e-09,0.746315,0.00187295,1.35497e-06,2.65653e-08,0.748189,0.00187574,1.43467e-06,-4.13564e-08,0.750066,0.00187848,1.3106e-06,1.9651e-08,0.751946,0.00188116,1.36955e-06,2.23572e-08,0.753828,0.00188397,1.43663e-06,-4.9475e-08,0.755714,0.00188669,1.2882e-06,5.63335e-08,0.757602,0.00188944,1.4572e-06,-5.66499e-08,0.759493,0.00189218,1.28725e-06,5.10567e-08,0.761386,0.00189491,1.44042e-06,-2.83677e-08,0.763283,0.00189771,1.35532e-06,2.80962e-09,0.765182,0.00190042,1.36375e-06,1.71293e-08,0.767083,0.0019032,1.41513e-06,-1.17221e-08,0.768988,0.001906,1.37997e-06,-2.98453e-08,0.770895,0.00190867,1.29043e-06,7.14987e-08,0.772805,0.00191146,1.50493e-06,-7.73354e-08,0.774718,0.00191424,1.27292e-06,5.90292e-08,0.776634,0.00191697,1.45001e-06,-3.9572e-08,0.778552,0.00191975,1.33129e-06,3.9654e-08,0.780473,0.00192253,1.45026e-06,-5.94395e-08,0.782397,0.00192525,1.27194e-06,7.88945e-08,0.784324,0.00192803,1.50862e-06,-7.73249e-08,0.786253,0.00193082,1.27665e-06,5.15913e-08,0.788185,0.00193352,1.43142e-06,-9.83099e-09,0.79012,0.00193636,1.40193e-06,-1.22672e-08,0.792058,0.00193912,1.36513e-06,-7.05275e-10,0.793999,0.00194185,1.36301e-06,1.50883e-08,0.795942,0.00194462,1.40828e-06,-4.33147e-11,0.797888,0.00194744,1.40815e-06,-1.49151e-08,0.799837,0.00195021,1.3634e-06,9.93244e-11,0.801788,0.00195294,1.3637e-06,1.45179e-08,0.803743,0.00195571,1.40725e-06,1.43363e-09,0.8057,0.00195853,1.41155e-06,-2.02525e-08,0.80766,0.00196129,1.35079e-06,1.99718e-08,0.809622,0.00196405,1.41071e-06,-3.01649e-11,0.811588,0.00196687,1.41062e-06,-1.9851e-08,0.813556,0.00196964,1.35107e-06,1.98296e-08,0.815527,0.0019724,1.41056e-06,1.37485e-10,0.817501,0.00197522,1.41097e-06,-2.03796e-08,0.819477,0.00197798,1.34983e-06,2.17763e-08,0.821457,0.00198074,1.41516e-06,-7.12085e-09,0.823439,0.00198355,1.3938e-06,6.70707e-09,0.825424,0.00198636,1.41392e-06,-1.97074e-08,0.827412,0.00198913,1.35479e-06,1.25179e-08,0.829402,0.00199188,1.39235e-06,2.92405e-08,0.831396,0.00199475,1.48007e-06,-6.98755e-08,0.833392,0.0019975,1.27044e-06,7.14477e-08,0.835391,0.00200026,1.48479e-06,-3.71014e-08,0.837392,0.00200311,1.37348e-06,1.73533e-08,0.839397,0.00200591,1.42554e-06,-3.23118e-08,0.841404,0.00200867,1.32861e-06,5.2289e-08,0.843414,0.00201148,1.48547e-06,-5.76348e-08,0.845427,0.00201428,1.31257e-06,5.9041e-08,0.847443,0.00201708,1.48969e-06,-5.93197e-08,0.849461,0.00201988,1.31173e-06,5.90289e-08,0.851482,0.00202268,1.48882e-06,-5.75864e-08,0.853507,0.00202549,1.31606e-06,5.21075e-08,0.855533,0.00202828,1.47238e-06,-3.16344e-08,0.857563,0.00203113,1.37748e-06,1.48257e-08,0.859596,0.00203393,1.42196e-06,-2.76684e-08,0.861631,0.00203669,1.33895e-06,3.62433e-08,0.863669,0.00203947,1.44768e-06,1.90463e-09,0.86571,0.00204237,1.45339e-06,-4.38617e-08,0.867754,0.00204515,1.32181e-06,5.43328e-08,0.8698,0.00204796,1.48481e-06,-5.42603e-08,0.87185,0.00205076,1.32203e-06,4.34989e-08,0.873902,0.00205354,1.45252e-06,-5.26029e-10,0.875957,0.00205644,1.45095e-06,-4.13949e-08,0.878015,0.00205922,1.32676e-06,4.68962e-08,0.880075,0.00206201,1.46745e-06,-2.69807e-08,0.882139,0.00206487,1.38651e-06,1.42181e-09,0.884205,0.00206764,1.39077e-06,2.12935e-08,0.886274,0.00207049,1.45465e-06,-2.69912e-08,0.888346,0.00207332,1.37368e-06,2.70664e-08,0.890421,0.00207615,1.45488e-06,-2.16698e-08,0.892498,0.00207899,1.38987e-06,8.14756e-12,0.894579,0.00208177,1.38989e-06,2.16371e-08,0.896662,0.00208462,1.45481e-06,-2.6952e-08,0.898748,0.00208744,1.37395e-06,2.65663e-08,0.900837,0.00209027,1.45365e-06,-1.97084e-08,0.902928,0.00209312,1.39452e-06,-7.33731e-09,0.905023,0.00209589,1.37251e-06,4.90578e-08,0.90712,0.00209878,1.51968e-06,-6.96845e-08,0.90922,0.00210161,1.31063e-06,5.08664e-08,0.911323,0.00210438,1.46323e-06,-1.45717e-08,0.913429,0.00210727,1.41952e-06,7.42038e-09,0.915538,0.00211013,1.44178e-06,-1.51097e-08,0.917649,0.00211297,1.39645e-06,-6.58618e-09,0.919764,0.00211574,1.37669e-06,4.14545e-08,0.921881,0.00211862,1.50105e-06,-4.00222e-08,0.924001,0.0021215,1.38099e-06,-5.7518e-10,0.926124,0.00212426,1.37926e-06,4.23229e-08,0.92825,0.00212714,1.50623e-06,-4.9507e-08,0.930378,0.00213001,1.35771e-06,3.64958e-08,0.93251,0.00213283,1.4672e-06,-3.68713e-08,0.934644,0.00213566,1.35658e-06,5.13848e-08,0.936781,0.00213852,1.51074e-06,-4.94585e-08,0.938921,0.0021414,1.36236e-06,2.72399e-08,0.941064,0.0021442,1.44408e-06,1.0372e-10,0.943209,0.00214709,1.44439e-06,-2.76547e-08,0.945358,0.0021499,1.36143e-06,5.09106e-08,0.947509,0.00215277,1.51416e-06,-5.67784e-08,0.949663,0.00215563,1.34382e-06,5.69935e-08,0.95182,0.00215849,1.5148e-06,-5.19861e-08,0.95398,0.00216136,1.35885e-06,3.17417e-08,0.956143,0.00216418,1.45407e-06,-1.53758e-08,0.958309,0.00216704,1.40794e-06,2.97615e-08,0.960477,0.00216994,1.49723e-06,-4.40657e-08,0.962649,0.00217281,1.36503e-06,2.72919e-08,0.964823,0.00217562,1.44691e-06,-5.49729e-09,0.967,0.0021785,1.43041e-06,-5.30273e-09,0.96918,0.00218134,1.41451e-06,2.67084e-08,0.971363,0.00218425,1.49463e-06,-4.19265e-08,0.973548,0.00218711,1.36885e-06,2.17881e-08,0.975737,0.00218992,1.43422e-06,1.43789e-08,0.977928,0.00219283,1.47735e-06,-1.96989e-08,0.980122,0.00219572,1.41826e-06,4.81221e-09,0.98232,0.00219857,1.43269e-06,4.50048e-10,0.98452,0.00220144,1.43404e-06,-6.61237e-09,0.986722,0.00220429,1.41421e-06,2.59993e-08,0.988928,0.0022072,1.4922e-06,-3.77803e-08,0.991137,0.00221007,1.37886e-06,5.9127e-09,0.993348,0.00221284,1.3966e-06,1.33339e-07,0.995563,0.00221604,1.79662e-06,-5.98872e-07,0.99778,0.00222015,0.,0.}; + + template + __device__ __forceinline__ void RGB2LabConvert_f(const T& src, D& dst) + { + const float _1_3 = 1.0f / 3.0f; + const float _a = 16.0f / 116.0f; + + float B = blueIdx == 0 ? src.x : src.z; + float G = src.y; + float R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + } + + float X = B * 0.189828f + G * 0.376219f + R * 0.433953f; + float Y = B * 0.072169f + G * 0.715160f + R * 0.212671f; + float Z = B * 0.872766f + G * 0.109477f + R * 0.017758f; + + float FX = X > 0.008856f ? ::powf(X, _1_3) : (7.787f * X + _a); + float FY = Y > 0.008856f ? ::powf(Y, _1_3) : (7.787f * Y + _a); + float FZ = Z > 0.008856f ? ::powf(Z, _1_3) : (7.787f * Z + _a); + + float L = Y > 0.008856f ? (116.f * FY - 16.f) : (903.3f * Y); + float a = 500.f * (FX - FY); + float b = 200.f * (FY - FZ); + + dst.x = L; + dst.y = a; + dst.z = b; + } + + template struct RGB2Lab; + template + struct RGB2Lab + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LabConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Lab() {} + __host__ __device__ __forceinline__ RGB2Lab(const RGB2Lab&) {} + }; + template + struct RGB2Lab + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LabConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Lab() {} + __host__ __device__ __forceinline__ RGB2Lab(const RGB2Lab&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2Lab_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Lab functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + __constant__ float c_sRGBInvGammaTab[] = {0,0.0126255,0.,-8.33961e-06,0.0126172,0.0126005,-2.50188e-05,4.1698e-05,0.0252344,0.0126756,0.000100075,-0.000158451,0.0378516,0.0124004,-0.000375277,-0.000207393,0.0496693,0.0110276,-0.000997456,0.00016837,0.0598678,0.00953783,-0.000492346,2.07235e-05,0.068934,0.00861531,-0.000430176,3.62876e-05,0.0771554,0.00786382,-0.000321313,1.87625e-05,0.0847167,0.00727748,-0.000265025,1.53594e-05,0.0917445,0.00679351,-0.000218947,1.10545e-05,0.0983301,0.00638877,-0.000185784,8.66984e-06,0.104542,0.00604322,-0.000159774,6.82996e-06,0.110432,0.00574416,-0.000139284,5.51008e-06,0.116042,0.00548212,-0.000122754,4.52322e-06,0.121406,0.00525018,-0.000109184,3.75557e-06,0.126551,0.00504308,-9.79177e-05,3.17134e-06,0.131499,0.00485676,-8.84037e-05,2.68469e-06,0.13627,0.004688,-8.03496e-05,2.31725e-06,0.14088,0.00453426,-7.33978e-05,2.00868e-06,0.145343,0.00439349,-6.73718e-05,1.74775e-06,0.149671,0.00426399,-6.21286e-05,1.53547e-06,0.153875,0.00414434,-5.75222e-05,1.364e-06,0.157963,0.00403338,-5.34301e-05,1.20416e-06,0.161944,0.00393014,-4.98177e-05,1.09114e-06,0.165825,0.00383377,-4.65443e-05,9.57987e-07,0.169613,0.00374356,-4.36703e-05,8.88359e-07,0.173314,0.00365888,-4.10052e-05,7.7849e-07,0.176933,0.00357921,-3.86697e-05,7.36254e-07,0.180474,0.00350408,-3.6461e-05,6.42534e-07,0.183942,0.00343308,-3.45334e-05,6.12614e-07,0.187342,0.00336586,-3.26955e-05,5.42894e-07,0.190675,0.00330209,-3.10669e-05,5.08967e-07,0.193947,0.00324149,-2.954e-05,4.75977e-07,0.197159,0.00318383,-2.8112e-05,4.18343e-07,0.200315,0.00312887,-2.6857e-05,4.13651e-07,0.203418,0.00307639,-2.5616e-05,3.70847e-07,0.206469,0.00302627,-2.45035e-05,3.3813e-07,0.209471,0.00297828,-2.34891e-05,3.32999e-07,0.212426,0.0029323,-2.24901e-05,2.96826e-07,0.215336,0.00288821,-2.15996e-05,2.82736e-07,0.218203,0.00284586,-2.07514e-05,2.70961e-07,0.221029,0.00280517,-1.99385e-05,2.42744e-07,0.223814,0.00276602,-1.92103e-05,2.33277e-07,0.226561,0.0027283,-1.85105e-05,2.2486e-07,0.229271,0.00269195,-1.78359e-05,2.08383e-07,0.231945,0.00265691,-1.72108e-05,1.93305e-07,0.234585,0.00262307,-1.66308e-05,1.80687e-07,0.237192,0.00259035,-1.60888e-05,1.86632e-07,0.239766,0.00255873,-1.55289e-05,1.60569e-07,0.24231,0.00252815,-1.50472e-05,1.54566e-07,0.244823,0.00249852,-1.45835e-05,1.59939e-07,0.247307,0.00246983,-1.41037e-05,1.29549e-07,0.249763,0.00244202,-1.3715e-05,1.41429e-07,0.252191,0.00241501,-1.32907e-05,1.39198e-07,0.254593,0.00238885,-1.28731e-05,1.06444e-07,0.256969,0.00236342,-1.25538e-05,1.2048e-07,0.25932,0.00233867,-1.21924e-05,1.26892e-07,0.261647,0.00231467,-1.18117e-05,8.72084e-08,0.26395,0.00229131,-1.15501e-05,1.20323e-07,0.26623,0.00226857,-1.11891e-05,8.71514e-08,0.268487,0.00224645,-1.09276e-05,9.73165e-08,0.270723,0.00222489,-1.06357e-05,8.98259e-08,0.272937,0.00220389,-1.03662e-05,7.98218e-08,0.275131,0.00218339,-1.01267e-05,9.75254e-08,0.277304,0.00216343,-9.83416e-06,6.65195e-08,0.279458,0.00214396,-9.63461e-06,8.34313e-08,0.281592,0.00212494,-9.38431e-06,7.65919e-08,0.283708,0.00210641,-9.15454e-06,5.7236e-08,0.285805,0.00208827,-8.98283e-06,8.18939e-08,0.287885,0.00207055,-8.73715e-06,6.2224e-08,0.289946,0.00205326,-8.55047e-06,5.66388e-08,0.291991,0.00203633,-8.38056e-06,6.88491e-08,0.294019,0.00201978,-8.17401e-06,5.53955e-08,0.296031,0.00200359,-8.00782e-06,6.71971e-08,0.298027,0.00198778,-7.80623e-06,3.34439e-08,0.300007,0.00197227,-7.7059e-06,6.7248e-08,0.301971,0.00195706,-7.50416e-06,5.51915e-08,0.303921,0.00194221,-7.33858e-06,3.98124e-08,0.305856,0.00192766,-7.21915e-06,5.37795e-08,0.307776,0.00191338,-7.05781e-06,4.30919e-08,0.309683,0.00189939,-6.92853e-06,4.20744e-08,0.311575,0.00188566,-6.80231e-06,5.68321e-08,0.313454,0.00187223,-6.63181e-06,2.86195e-08,0.31532,0.00185905,-6.54595e-06,3.73075e-08,0.317172,0.00184607,-6.43403e-06,6.05684e-08,0.319012,0.00183338,-6.25233e-06,1.84426e-08,0.320839,0.00182094,-6.197e-06,4.44757e-08,0.322654,0.00180867,-6.06357e-06,4.20729e-08,0.324456,0.00179667,-5.93735e-06,2.56511e-08,0.326247,0.00178488,-5.8604e-06,3.41368e-08,0.328026,0.00177326,-5.75799e-06,4.64177e-08,0.329794,0.00176188,-5.61874e-06,1.86107e-08,0.33155,0.0017507,-5.5629e-06,2.81511e-08,0.333295,0.00173966,-5.47845e-06,4.75987e-08,0.335029,0.00172884,-5.33565e-06,1.98726e-08,0.336753,0.00171823,-5.27604e-06,2.19226e-08,0.338466,0.00170775,-5.21027e-06,4.14483e-08,0.340169,0.00169745,-5.08592e-06,2.09017e-08,0.341861,0.00168734,-5.02322e-06,2.39561e-08,0.343543,0.00167737,-4.95135e-06,3.22852e-08,0.345216,0.00166756,-4.85449e-06,2.57173e-08,0.346878,0.00165793,-4.77734e-06,1.38569e-08,0.348532,0.00164841,-4.73577e-06,3.80634e-08,0.350175,0.00163906,-4.62158e-06,1.27043e-08,0.35181,0.00162985,-4.58347e-06,3.03279e-08,0.353435,0.00162078,-4.49249e-06,1.49961e-08,0.355051,0.00161184,-4.4475e-06,2.88977e-08,0.356659,0.00160303,-4.3608e-06,1.84241e-08,0.358257,0.00159436,-4.30553e-06,1.6616e-08,0.359848,0.0015858,-4.25568e-06,3.43218e-08,0.361429,0.00157739,-4.15272e-06,-4.89172e-09,0.363002,0.00156907,-4.16739e-06,4.48498e-08,0.364567,0.00156087,-4.03284e-06,4.30676e-09,0.366124,0.00155282,-4.01992e-06,2.73303e-08,0.367673,0.00154486,-3.93793e-06,5.58036e-09,0.369214,0.001537,-3.92119e-06,3.97554e-08,0.370747,0.00152928,-3.80193e-06,-1.55904e-08,0.372272,0.00152163,-3.8487e-06,5.24081e-08,0.37379,0.00151409,-3.69147e-06,-1.52272e-08,0.375301,0.00150666,-3.73715e-06,3.83028e-08,0.376804,0.0014993,-3.62225e-06,1.10278e-08,0.378299,0.00149209,-3.58916e-06,6.99326e-09,0.379788,0.00148493,-3.56818e-06,2.06038e-08,0.381269,0.00147786,-3.50637e-06,2.98009e-08,0.382744,0.00147093,-3.41697e-06,-2.05978e-08,0.384211,0.00146404,-3.47876e-06,5.25899e-08,0.385672,0.00145724,-3.32099e-06,-1.09471e-08,0.387126,0.00145056,-3.35383e-06,2.10009e-08,0.388573,0.00144392,-3.29083e-06,1.63501e-08,0.390014,0.00143739,-3.24178e-06,3.00641e-09,0.391448,0.00143091,-3.23276e-06,3.12282e-08,0.392875,0.00142454,-3.13908e-06,-8.70932e-09,0.394297,0.00141824,-3.16521e-06,3.34114e-08,0.395712,0.00141201,-3.06497e-06,-5.72754e-09,0.397121,0.00140586,-3.08215e-06,1.9301e-08,0.398524,0.00139975,-3.02425e-06,1.7931e-08,0.39992,0.00139376,-2.97046e-06,-1.61822e-09,0.401311,0.00138781,-2.97531e-06,1.83442e-08,0.402696,0.00138192,-2.92028e-06,1.76485e-08,0.404075,0.00137613,-2.86733e-06,4.68617e-10,0.405448,0.00137039,-2.86593e-06,1.02794e-08,0.406816,0.00136469,-2.83509e-06,1.80179e-08,0.408178,0.00135908,-2.78104e-06,7.05594e-09,0.409534,0.00135354,-2.75987e-06,1.33633e-08,0.410885,0.00134806,-2.71978e-06,-9.04568e-10,0.41223,0.00134261,-2.72249e-06,2.0057e-08,0.41357,0.00133723,-2.66232e-06,1.00841e-08,0.414905,0.00133194,-2.63207e-06,-7.88835e-10,0.416234,0.00132667,-2.63444e-06,2.28734e-08,0.417558,0.00132147,-2.56582e-06,-1.29785e-09,0.418877,0.00131633,-2.56971e-06,1.21205e-08,0.420191,0.00131123,-2.53335e-06,1.24202e-08,0.421499,0.0013062,-2.49609e-06,-2.19681e-09,0.422803,0.0013012,-2.50268e-06,2.61696e-08,0.424102,0.00129628,-2.42417e-06,-1.30747e-08,0.425396,0.00129139,-2.46339e-06,2.6129e-08,0.426685,0.00128654,-2.38501e-06,-2.03454e-09,0.427969,0.00128176,-2.39111e-06,1.18115e-08,0.429248,0.00127702,-2.35567e-06,1.43932e-08,0.430523,0.00127235,-2.31249e-06,-9.77965e-09,0.431793,0.00126769,-2.34183e-06,2.47253e-08,0.433058,0.00126308,-2.26766e-06,2.85278e-10,0.434319,0.00125855,-2.2668e-06,3.93614e-09,0.435575,0.00125403,-2.25499e-06,1.37722e-08,0.436827,0.00124956,-2.21368e-06,5.79803e-10,0.438074,0.00124513,-2.21194e-06,1.37112e-08,0.439317,0.00124075,-2.1708e-06,4.17973e-09,0.440556,0.00123642,-2.15826e-06,-6.27703e-10,0.44179,0.0012321,-2.16015e-06,2.81332e-08,0.44302,0.00122787,-2.07575e-06,-2.24985e-08,0.444246,0.00122365,-2.14324e-06,3.20586e-08,0.445467,0.00121946,-2.04707e-06,-1.6329e-08,0.446685,0.00121532,-2.09605e-06,3.32573e-08,0.447898,0.00121122,-1.99628e-06,-2.72927e-08,0.449107,0.00120715,-2.07816e-06,4.6111e-08,0.450312,0.00120313,-1.93983e-06,-3.79416e-08,0.451514,0.00119914,-2.05365e-06,4.60507e-08,0.452711,0.00119517,-1.9155e-06,-2.7052e-08,0.453904,0.00119126,-1.99666e-06,3.23551e-08,0.455093,0.00118736,-1.89959e-06,-1.29613e-08,0.456279,0.00118352,-1.93848e-06,1.94905e-08,0.45746,0.0011797,-1.88e-06,-5.39588e-09,0.458638,0.00117593,-1.89619e-06,2.09282e-09,0.459812,0.00117214,-1.88991e-06,2.68267e-08,0.460982,0.00116844,-1.80943e-06,-1.99925e-08,0.462149,0.00116476,-1.86941e-06,2.3341e-08,0.463312,0.00116109,-1.79939e-06,-1.37674e-08,0.464471,0.00115745,-1.84069e-06,3.17287e-08,0.465627,0.00115387,-1.7455e-06,-2.37407e-08,0.466779,0.00115031,-1.81673e-06,3.34315e-08,0.467927,0.00114677,-1.71643e-06,-2.05786e-08,0.469073,0.00114328,-1.77817e-06,1.90802e-08,0.470214,0.00113978,-1.72093e-06,3.86247e-09,0.471352,0.00113635,-1.70934e-06,-4.72759e-09,0.472487,0.00113292,-1.72352e-06,1.50478e-08,0.473618,0.00112951,-1.67838e-06,4.14108e-09,0.474746,0.00112617,-1.66595e-06,-1.80986e-09,0.47587,0.00112283,-1.67138e-06,3.09816e-09,0.476991,0.0011195,-1.66209e-06,1.92198e-08,0.478109,0.00111623,-1.60443e-06,-2.03726e-08,0.479224,0.00111296,-1.66555e-06,3.2468e-08,0.480335,0.00110973,-1.56814e-06,-2.00922e-08,0.481443,0.00110653,-1.62842e-06,1.80983e-08,0.482548,0.00110333,-1.57413e-06,7.30362e-09,0.48365,0.0011002,-1.55221e-06,-1.75107e-08,0.484749,0.00109705,-1.60475e-06,3.29373e-08,0.485844,0.00109393,-1.50594e-06,-2.48315e-08,0.486937,0.00109085,-1.58043e-06,3.65865e-08,0.488026,0.0010878,-1.47067e-06,-3.21078e-08,0.489112,0.00108476,-1.56699e-06,3.22397e-08,0.490195,0.00108172,-1.47027e-06,-7.44391e-09,0.491276,0.00107876,-1.49261e-06,-2.46428e-09,0.492353,0.00107577,-1.5e-06,1.73011e-08,0.493427,0.00107282,-1.4481e-06,-7.13552e-09,0.494499,0.0010699,-1.4695e-06,1.1241e-08,0.495567,0.001067,-1.43578e-06,-8.02637e-09,0.496633,0.0010641,-1.45986e-06,2.08645e-08,0.497695,0.00106124,-1.39726e-06,-1.58271e-08,0.498755,0.0010584,-1.44475e-06,1.26415e-08,0.499812,0.00105555,-1.40682e-06,2.48655e-08,0.500866,0.00105281,-1.33222e-06,-5.24988e-08,0.501918,0.00104999,-1.48972e-06,6.59206e-08,0.502966,0.00104721,-1.29196e-06,-3.237e-08,0.504012,0.00104453,-1.38907e-06,3.95479e-09,0.505055,0.00104176,-1.3772e-06,1.65509e-08,0.506096,0.00103905,-1.32755e-06,-1.05539e-08,0.507133,0.00103637,-1.35921e-06,2.56648e-08,0.508168,0.00103373,-1.28222e-06,-3.25007e-08,0.509201,0.00103106,-1.37972e-06,4.47336e-08,0.51023,0.00102844,-1.24552e-06,-2.72245e-08,0.511258,0.00102587,-1.32719e-06,4.55952e-09,0.512282,0.00102323,-1.31352e-06,8.98645e-09,0.513304,0.00102063,-1.28656e-06,1.90992e-08,0.514323,0.00101811,-1.22926e-06,-2.57786e-08,0.51534,0.00101557,-1.30659e-06,2.44104e-08,0.516355,0.00101303,-1.23336e-06,-1.22581e-08,0.517366,0.00101053,-1.27014e-06,2.4622e-08,0.518376,0.00100806,-1.19627e-06,-2.66253e-08,0.519383,0.00100559,-1.27615e-06,2.22744e-08,0.520387,0.00100311,-1.20932e-06,-2.8679e-09,0.521389,0.00100068,-1.21793e-06,-1.08029e-08,0.522388,0.000998211,-1.25034e-06,4.60795e-08,0.523385,0.000995849,-1.1121e-06,-5.4306e-08,0.52438,0.000993462,-1.27502e-06,5.19354e-08,0.525372,0.000991067,-1.11921e-06,-3.42262e-08,0.526362,0.000988726,-1.22189e-06,2.53646e-08,0.52735,0.000986359,-1.14579e-06,-7.62782e-09,0.528335,0.000984044,-1.16868e-06,5.14668e-09,0.529318,0.000981722,-1.15324e-06,-1.29589e-08,0.530298,0.000979377,-1.19211e-06,4.66888e-08,0.531276,0.000977133,-1.05205e-06,-5.45868e-08,0.532252,0.000974865,-1.21581e-06,5.24495e-08,0.533226,0.000972591,-1.05846e-06,-3.60019e-08,0.534198,0.000970366,-1.16647e-06,3.19537e-08,0.535167,0.000968129,-1.07061e-06,-3.2208e-08,0.536134,0.000965891,-1.16723e-06,3.72738e-08,0.537099,0.000963668,-1.05541e-06,2.32205e-09,0.538061,0.000961564,-1.04844e-06,-4.65618e-08,0.539022,0.000959328,-1.18813e-06,6.47159e-08,0.53998,0.000957146,-9.93979e-07,-3.3488e-08,0.540936,0.000955057,-1.09444e-06,9.63166e-09,0.54189,0.000952897,-1.06555e-06,-5.03871e-09,0.542842,0.000950751,-1.08066e-06,1.05232e-08,0.543792,0.000948621,-1.04909e-06,2.25503e-08,0.544739,0.000946591,-9.81444e-07,-4.11195e-08,0.545685,0.000944504,-1.1048e-06,2.27182e-08,0.546628,0.000942363,-1.03665e-06,9.85146e-09,0.54757,0.000940319,-1.00709e-06,-2.51938e-09,0.548509,0.000938297,-1.01465e-06,2.25858e-10,0.549446,0.000936269,-1.01397e-06,1.61598e-09,0.550381,0.000934246,-1.00913e-06,-6.68983e-09,0.551315,0.000932207,-1.0292e-06,2.51434e-08,0.552246,0.000930224,-9.53765e-07,-3.42793e-08,0.553175,0.000928214,-1.0566e-06,5.23688e-08,0.554102,0.000926258,-8.99497e-07,-5.59865e-08,0.555028,0.000924291,-1.06746e-06,5.23679e-08,0.555951,0.000922313,-9.10352e-07,-3.42763e-08,0.556872,0.00092039,-1.01318e-06,2.51326e-08,0.557792,0.000918439,-9.37783e-07,-6.64954e-09,0.558709,0.000916543,-9.57732e-07,1.46554e-09,0.559625,0.000914632,-9.53335e-07,7.87281e-10,0.560538,0.000912728,-9.50973e-07,-4.61466e-09,0.56145,0.000910812,-9.64817e-07,1.76713e-08,0.56236,0.000908935,-9.11804e-07,-6.46564e-09,0.563268,0.000907092,-9.312e-07,8.19121e-09,0.564174,0.000905255,-9.06627e-07,-2.62992e-08,0.565078,0.000903362,-9.85524e-07,3.74007e-08,0.565981,0.000901504,-8.73322e-07,-4.0942e-09,0.566882,0.000899745,-8.85605e-07,-2.1024e-08,0.56778,0.00089791,-9.48677e-07,2.85854e-08,0.568677,0.000896099,-8.62921e-07,-3.3713e-08,0.569573,0.000894272,-9.64059e-07,4.6662e-08,0.570466,0.000892484,-8.24073e-07,-3.37258e-08,0.571358,0.000890734,-9.25251e-07,2.86365e-08,0.572247,0.00088897,-8.39341e-07,-2.12155e-08,0.573135,0.000887227,-9.02988e-07,-3.37913e-09,0.574022,0.000885411,-9.13125e-07,3.47319e-08,0.574906,0.000883689,-8.08929e-07,-1.63394e-08,0.575789,0.000882022,-8.57947e-07,-2.8979e-08,0.57667,0.00088022,-9.44885e-07,7.26509e-08,0.57755,0.000878548,-7.26932e-07,-8.28106e-08,0.578427,0.000876845,-9.75364e-07,7.97774e-08,0.579303,0.000875134,-7.36032e-07,-5.74849e-08,0.580178,0.00087349,-9.08486e-07,3.09529e-08,0.58105,0.000871765,-8.15628e-07,-6.72206e-09,0.581921,0.000870114,-8.35794e-07,-4.06451e-09,0.582791,0.00086843,-8.47987e-07,2.29799e-08,0.583658,0.000866803,-7.79048e-07,-2.82503e-08,0.584524,0.00086516,-8.63799e-07,3.04167e-08,0.585388,0.000863524,-7.72548e-07,-3.38119e-08,0.586251,0.000861877,-8.73984e-07,4.52264e-08,0.587112,0.000860265,-7.38305e-07,-2.78842e-08,0.587972,0.000858705,-8.21958e-07,6.70567e-09,0.58883,0.000857081,-8.01841e-07,1.06161e-09,0.589686,0.000855481,-7.98656e-07,-1.09521e-08,0.590541,0.00085385,-8.31512e-07,4.27468e-08,0.591394,0.000852316,-7.03272e-07,-4.08257e-08,0.592245,0.000850787,-8.25749e-07,1.34677e-09,0.593095,0.000849139,-8.21709e-07,3.54387e-08,0.593944,0.000847602,-7.15393e-07,-2.38924e-08,0.59479,0.0008461,-7.8707e-07,5.26143e-10,0.595636,0.000844527,-7.85491e-07,2.17879e-08,0.596479,0.000843021,-7.20127e-07,-2.80733e-08,0.597322,0.000841497,-8.04347e-07,3.09005e-08,0.598162,0.000839981,-7.11646e-07,-3.5924e-08,0.599002,0.00083845,-8.19418e-07,5.3191e-08,0.599839,0.000836971,-6.59845e-07,-5.76307e-08,0.600676,0.000835478,-8.32737e-07,5.81227e-08,0.60151,0.000833987,-6.58369e-07,-5.56507e-08,0.602344,0.000832503,-8.25321e-07,4.52706e-08,0.603175,0.000830988,-6.89509e-07,-6.22236e-09,0.604006,0.000829591,-7.08176e-07,-2.03811e-08,0.604834,0.000828113,-7.6932e-07,2.8142e-08,0.605662,0.000826659,-6.84894e-07,-3.25822e-08,0.606488,0.000825191,-7.8264e-07,4.25823e-08,0.607312,0.000823754,-6.54893e-07,-1.85376e-08,0.608135,0.000822389,-7.10506e-07,-2.80365e-08,0.608957,0.000820883,-7.94616e-07,7.1079e-08,0.609777,0.000819507,-5.81379e-07,-7.74655e-08,0.610596,0.000818112,-8.13775e-07,5.9969e-08,0.611413,0.000816665,-6.33868e-07,-4.32013e-08,0.612229,0.000815267,-7.63472e-07,5.32313e-08,0.613044,0.0008139,-6.03778e-07,-5.05148e-08,0.613857,0.000812541,-7.55323e-07,2.96187e-08,0.614669,0.000811119,-6.66466e-07,-8.35545e-09,0.615479,0.000809761,-6.91533e-07,3.80301e-09,0.616288,0.00080839,-6.80124e-07,-6.85666e-09,0.617096,0.000807009,-7.00694e-07,2.36237e-08,0.617903,0.000805678,-6.29822e-07,-2.80336e-08,0.618708,0.000804334,-7.13923e-07,2.8906e-08,0.619511,0.000802993,-6.27205e-07,-2.79859e-08,0.620314,0.000801655,-7.11163e-07,2.34329e-08,0.621114,0.000800303,-6.40864e-07,-6.14108e-09,0.621914,0.000799003,-6.59287e-07,1.13151e-09,0.622712,0.000797688,-6.55893e-07,1.61507e-09,0.62351,0.000796381,-6.51048e-07,-7.59186e-09,0.624305,0.000795056,-6.73823e-07,2.87524e-08,0.6251,0.000793794,-5.87566e-07,-4.7813e-08,0.625893,0.000792476,-7.31005e-07,4.32901e-08,0.626685,0.000791144,-6.01135e-07,-6.13814e-09,0.627475,0.000789923,-6.19549e-07,-1.87376e-08,0.628264,0.000788628,-6.75762e-07,2.14837e-08,0.629052,0.000787341,-6.11311e-07,-7.59265e-09,0.629839,0.000786095,-6.34089e-07,8.88692e-09,0.630625,0.000784854,-6.07428e-07,-2.7955e-08,0.631409,0.000783555,-6.91293e-07,4.33285e-08,0.632192,0.000782302,-5.61307e-07,-2.61497e-08,0.632973,0.000781101,-6.39757e-07,1.6658e-09,0.633754,0.000779827,-6.34759e-07,1.94866e-08,0.634533,0.000778616,-5.76299e-07,-2.00076e-08,0.635311,0.000777403,-6.36322e-07,9.39091e-10,0.636088,0.000776133,-6.33505e-07,1.62512e-08,0.636863,0.000774915,-5.84751e-07,-6.33937e-09,0.637638,0.000773726,-6.03769e-07,9.10609e-09,0.638411,0.000772546,-5.76451e-07,-3.00849e-08,0.639183,0.000771303,-6.66706e-07,5.1629e-08,0.639953,0.000770125,-5.11819e-07,-5.7222e-08,0.640723,0.000768929,-6.83485e-07,5.80497e-08,0.641491,0.000767736,-5.09336e-07,-5.57674e-08,0.642259,0.000766551,-6.76638e-07,4.58105e-08,0.643024,0.000765335,-5.39206e-07,-8.26541e-09,0.643789,0.000764231,-5.64002e-07,-1.27488e-08,0.644553,0.000763065,-6.02249e-07,-3.44168e-10,0.645315,0.00076186,-6.03281e-07,1.41254e-08,0.646077,0.000760695,-5.60905e-07,3.44727e-09,0.646837,0.000759584,-5.50563e-07,-2.79144e-08,0.647596,0.000758399,-6.34307e-07,4.86057e-08,0.648354,0.000757276,-4.88489e-07,-4.72989e-08,0.64911,0.000756158,-6.30386e-07,2.13807e-08,0.649866,0.000754961,-5.66244e-07,2.13808e-08,0.65062,0.000753893,-5.02102e-07,-4.7299e-08,0.651374,0.000752746,-6.43999e-07,4.86059e-08,0.652126,0.000751604,-4.98181e-07,-2.79154e-08,0.652877,0.000750524,-5.81927e-07,3.45089e-09,0.653627,0.000749371,-5.71575e-07,1.41119e-08,0.654376,0.00074827,-5.29239e-07,-2.93748e-10,0.655123,0.00074721,-5.3012e-07,-1.29368e-08,0.65587,0.000746111,-5.68931e-07,-7.56355e-09,0.656616,0.000744951,-5.91621e-07,4.3191e-08,0.65736,0.000743897,-4.62048e-07,-4.59911e-08,0.658103,0.000742835,-6.00022e-07,2.15642e-08,0.658846,0.0007417,-5.35329e-07,1.93389e-08,0.659587,0.000740687,-4.77312e-07,-3.93152e-08,0.660327,0.000739615,-5.95258e-07,1.87126e-08,0.661066,0.00073848,-5.3912e-07,2.40695e-08,0.661804,0.000737474,-4.66912e-07,-5.53859e-08,0.662541,0.000736374,-6.33069e-07,7.82648e-08,0.663277,0.000735343,-3.98275e-07,-7.88593e-08,0.664012,0.00073431,-6.34853e-07,5.83585e-08,0.664745,0.000733215,-4.59777e-07,-3.53656e-08,0.665478,0.000732189,-5.65874e-07,2.34994e-08,0.66621,0.000731128,-4.95376e-07,9.72743e-10,0.66694,0.00073014,-4.92458e-07,-2.73903e-08,0.66767,0.000729073,-5.74629e-07,4.89839e-08,0.668398,0.000728071,-4.27677e-07,-4.93359e-08,0.669126,0.000727068,-5.75685e-07,2.91504e-08,0.669853,0.000726004,-4.88234e-07,-7.66109e-09,0.670578,0.000725004,-5.11217e-07,1.49392e-09,0.671303,0.000723986,-5.06735e-07,1.68533e-09,0.672026,0.000722978,-5.01679e-07,-8.23525e-09,0.672749,0.00072195,-5.26385e-07,3.12556e-08,0.67347,0.000720991,-4.32618e-07,-5.71825e-08,0.674191,0.000719954,-6.04166e-07,7.8265e-08,0.67491,0.00071898,-3.69371e-07,-7.70634e-08,0.675628,0.00071801,-6.00561e-07,5.11747e-08,0.676346,0.000716963,-4.47037e-07,-8.42615e-09,0.677062,0.000716044,-4.72315e-07,-1.747e-08,0.677778,0.000715046,-5.24725e-07,1.87015e-08,0.678493,0.000714053,-4.68621e-07,2.26856e-09,0.679206,0.000713123,-4.61815e-07,-2.77758e-08,0.679919,0.000712116,-5.45142e-07,4.92298e-08,0.68063,0.000711173,-3.97453e-07,-4.99339e-08,0.681341,0.000710228,-5.47255e-07,3.12967e-08,0.682051,0.000709228,-4.53365e-07,-1.56481e-08,0.68276,0.000708274,-5.00309e-07,3.12958e-08,0.683467,0.000707367,-4.06422e-07,-4.99303e-08,0.684174,0.000706405,-5.56213e-07,4.9216e-08,0.68488,0.00070544,-4.08565e-07,-2.77245e-08,0.685585,0.00070454,-4.91738e-07,2.07748e-09,0.686289,0.000703562,-4.85506e-07,1.94146e-08,0.686992,0.00070265,-4.27262e-07,-2.01314e-08,0.687695,0.000701735,-4.87656e-07,1.50616e-09,0.688396,0.000700764,-4.83137e-07,1.41067e-08,0.689096,0.00069984,-4.40817e-07,1.67168e-09,0.689795,0.000698963,-4.35802e-07,-2.07934e-08,0.690494,0.000698029,-4.98182e-07,2.18972e-08,0.691192,0.000697099,-4.32491e-07,-7.19092e-09,0.691888,0.000696212,-4.54064e-07,6.86642e-09,0.692584,0.000695325,-4.33464e-07,-2.02747e-08,0.693279,0.000694397,-4.94288e-07,1.46279e-08,0.693973,0.000693452,-4.50405e-07,2.13678e-08,0.694666,0.000692616,-3.86301e-07,-4.04945e-08,0.695358,0.000691721,-5.07785e-07,2.14009e-08,0.696049,0.00069077,-4.43582e-07,1.44955e-08,0.69674,0.000689926,-4.00096e-07,-1.97783e-08,0.697429,0.000689067,-4.5943e-07,5.01296e-09,0.698118,0.000688163,-4.44392e-07,-2.73521e-10,0.698805,0.000687273,-4.45212e-07,-3.91893e-09,0.699492,0.000686371,-4.56969e-07,1.59493e-08,0.700178,0.000685505,-4.09121e-07,-2.73351e-10,0.700863,0.000684686,-4.09941e-07,-1.4856e-08,0.701548,0.000683822,-4.54509e-07,9.25979e-11,0.702231,0.000682913,-4.54231e-07,1.44855e-08,0.702913,0.000682048,-4.10775e-07,1.56992e-09,0.703595,0.000681231,-4.06065e-07,-2.07652e-08,0.704276,0.000680357,-4.68361e-07,2.18864e-08,0.704956,0.000679486,-4.02701e-07,-7.17595e-09,0.705635,0.000678659,-4.24229e-07,6.81748e-09,0.706313,0.000677831,-4.03777e-07,-2.0094e-08,0.70699,0.000676963,-4.64059e-07,1.39538e-08,0.707667,0.000676077,-4.22197e-07,2.38835e-08,0.708343,0.000675304,-3.50547e-07,-4.98831e-08,0.709018,0.000674453,-5.00196e-07,5.64395e-08,0.709692,0.000673622,-3.30878e-07,-5.66657e-08,0.710365,0.00067279,-5.00875e-07,5.1014e-08,0.711037,0.000671942,-3.47833e-07,-2.81809e-08,0.711709,0.000671161,-4.32376e-07,2.10513e-09,0.712379,0.000670303,-4.2606e-07,1.97604e-08,0.713049,0.00066951,-3.66779e-07,-2.15422e-08,0.713718,0.000668712,-4.31406e-07,6.8038e-09,0.714387,0.000667869,-4.10994e-07,-5.67295e-09,0.715054,0.00066703,-4.28013e-07,1.5888e-08,0.715721,0.000666222,-3.80349e-07,1.72576e-09,0.716387,0.000665467,-3.75172e-07,-2.27911e-08,0.717052,0.000664648,-4.43545e-07,2.9834e-08,0.717716,0.00066385,-3.54043e-07,-3.69401e-08,0.718379,0.000663031,-4.64864e-07,5.83219e-08,0.719042,0.000662277,-2.89898e-07,-7.71382e-08,0.719704,0.000661465,-5.21313e-07,7.14171e-08,0.720365,0.000660637,-3.07061e-07,-2.97161e-08,0.721025,0.000659934,-3.96209e-07,-1.21575e-08,0.721685,0.000659105,-4.32682e-07,1.87412e-08,0.722343,0.000658296,-3.76458e-07,-3.2029e-09,0.723001,0.000657533,-3.86067e-07,-5.9296e-09,0.723659,0.000656743,-4.03856e-07,2.69213e-08,0.724315,0.000656016,-3.23092e-07,-4.21511e-08,0.724971,0.000655244,-4.49545e-07,2.24737e-08,0.725625,0.000654412,-3.82124e-07,1.18611e-08,0.726279,0.000653683,-3.46541e-07,-1.03132e-08,0.726933,0.000652959,-3.7748e-07,-3.02128e-08,0.727585,0.000652114,-4.68119e-07,7.15597e-08,0.728237,0.000651392,-2.5344e-07,-7.72119e-08,0.728888,0.000650654,-4.85075e-07,5.8474e-08,0.729538,0.000649859,-3.09654e-07,-3.74746e-08,0.730188,0.000649127,-4.22077e-07,3.18197e-08,0.730837,0.000648379,-3.26618e-07,-3.01997e-08,0.731485,0.000647635,-4.17217e-07,2.93747e-08,0.732132,0.000646888,-3.29093e-07,-2.76943e-08,0.732778,0.000646147,-4.12176e-07,2.17979e-08,0.733424,0.000645388,-3.46783e-07,1.07292e-10,0.734069,0.000644695,-3.46461e-07,-2.22271e-08,0.734713,0.000643935,-4.13142e-07,2.91963e-08,0.735357,0.000643197,-3.25553e-07,-3.49536e-08,0.736,0.000642441,-4.30414e-07,5.10133e-08,0.736642,0.000641733,-2.77374e-07,-4.98904e-08,0.737283,0.000641028,-4.27045e-07,2.93392e-08,0.737924,0.000640262,-3.39028e-07,-7.86156e-09,0.738564,0.000639561,-3.62612e-07,2.10703e-09,0.739203,0.000638842,-3.56291e-07,-5.6653e-10,0.739842,0.000638128,-3.57991e-07,1.59086e-10,0.740479,0.000637412,-3.57513e-07,-6.98321e-11,0.741116,0.000636697,-3.57723e-07,1.20214e-10,0.741753,0.000635982,-3.57362e-07,-4.10987e-10,0.742388,0.000635266,-3.58595e-07,1.5237e-09,0.743023,0.000634553,-3.54024e-07,-5.68376e-09,0.743657,0.000633828,-3.71075e-07,2.12113e-08,0.744291,0.00063315,-3.07441e-07,-1.95569e-08,0.744924,0.000632476,-3.66112e-07,-2.58816e-09,0.745556,0.000631736,-3.73877e-07,2.99096e-08,0.746187,0.000631078,-2.84148e-07,-5.74454e-08,0.746818,0.000630337,-4.56484e-07,8.06629e-08,0.747448,0.000629666,-2.14496e-07,-8.63922e-08,0.748077,0.000628978,-4.73672e-07,8.60918e-08,0.748706,0.000628289,-2.15397e-07,-7.91613e-08,0.749334,0.000627621,-4.5288e-07,5.17393e-08,0.749961,0.00062687,-2.97663e-07,-8.58662e-09,0.750588,0.000626249,-3.23422e-07,-1.73928e-08,0.751214,0.00062555,-3.75601e-07,1.85532e-08,0.751839,0.000624855,-3.19941e-07,2.78479e-09,0.752463,0.000624223,-3.11587e-07,-2.96923e-08,0.753087,0.000623511,-4.00664e-07,5.63799e-08,0.75371,0.000622879,-2.31524e-07,-7.66179e-08,0.754333,0.000622186,-4.61378e-07,7.12778e-08,0.754955,0.000621477,-2.47545e-07,-2.96794e-08,0.755576,0.000620893,-3.36583e-07,-1.21648e-08,0.756196,0.000620183,-3.73077e-07,1.87339e-08,0.756816,0.000619493,-3.16875e-07,-3.16622e-09,0.757435,0.00061885,-3.26374e-07,-6.0691e-09,0.758054,0.000618179,-3.44581e-07,2.74426e-08,0.758672,0.000617572,-2.62254e-07,-4.40968e-08,0.759289,0.000616915,-3.94544e-07,2.97352e-08,0.759906,0.000616215,-3.05338e-07,-1.52393e-08,0.760522,0.000615559,-3.51056e-07,3.12221e-08,0.761137,0.000614951,-2.5739e-07,-5.00443e-08,0.761751,0.000614286,-4.07523e-07,4.9746e-08,0.762365,0.00061362,-2.58285e-07,-2.97303e-08,0.762979,0.000613014,-3.47476e-07,9.57079e-09,0.763591,0.000612348,-3.18764e-07,-8.55287e-09,0.764203,0.000611685,-3.44422e-07,2.46407e-08,0.764815,0.00061107,-2.705e-07,-3.04053e-08,0.765426,0.000610437,-3.61716e-07,3.73759e-08,0.766036,0.000609826,-2.49589e-07,-5.94935e-08,0.766645,0.000609149,-4.28069e-07,8.13889e-08,0.767254,0.000608537,-1.83902e-07,-8.72483e-08,0.767862,0.000607907,-4.45647e-07,8.87901e-08,0.76847,0.000607282,-1.79277e-07,-8.90983e-08,0.769077,0.000606656,-4.46572e-07,8.87892e-08,0.769683,0.000606029,-1.80204e-07,-8.72446e-08,0.770289,0.000605407,-4.41938e-07,8.13752e-08,0.770894,0.000604768,-1.97812e-07,-5.94423e-08,0.771498,0.000604194,-3.76139e-07,3.71848e-08,0.772102,0.000603553,-2.64585e-07,-2.96922e-08,0.772705,0.000602935,-3.53661e-07,2.19793e-08,0.773308,0.000602293,-2.87723e-07,1.37955e-09,0.77391,0.000601722,-2.83585e-07,-2.74976e-08,0.774512,0.000601072,-3.66077e-07,4.9006e-08,0.775112,0.000600487,-2.19059e-07,-4.93171e-08,0.775712,0.000599901,-3.67011e-07,2.90531e-08,0.776312,0.000599254,-2.79851e-07,-7.29081e-09,0.776911,0.000598673,-3.01724e-07,1.10077e-10,0.777509,0.00059807,-3.01393e-07,6.85053e-09,0.778107,0.000597487,-2.80842e-07,-2.75123e-08,0.778704,0.000596843,-3.63379e-07,4.35939e-08,0.779301,0.000596247,-2.32597e-07,-2.7654e-08,0.779897,0.000595699,-3.15559e-07,7.41741e-09,0.780492,0.00059509,-2.93307e-07,-2.01562e-09,0.781087,0.000594497,-2.99354e-07,6.45059e-10,0.781681,0.000593901,-2.97418e-07,-5.64635e-10,0.782275,0.000593304,-2.99112e-07,1.61347e-09,0.782868,0.000592711,-2.94272e-07,-5.88926e-09,0.78346,0.000592105,-3.1194e-07,2.19436e-08,0.784052,0.000591546,-2.46109e-07,-2.22805e-08,0.784643,0.000590987,-3.1295e-07,7.57368e-09,0.785234,0.000590384,-2.90229e-07,-8.01428e-09,0.785824,0.00058978,-3.14272e-07,2.44834e-08,0.786414,0.000589225,-2.40822e-07,-3.03148e-08,0.787003,0.000588652,-3.31766e-07,3.7171e-08,0.787591,0.0005881,-2.20253e-07,-5.87646e-08,0.788179,0.000587483,-3.96547e-07,7.86782e-08,0.788766,0.000586926,-1.60512e-07,-7.71342e-08,0.789353,0.000586374,-3.91915e-07,5.10444e-08,0.789939,0.000585743,-2.38782e-07,-7.83422e-09,0.790524,0.000585242,-2.62284e-07,-1.97076e-08,0.791109,0.000584658,-3.21407e-07,2.70598e-08,0.791693,0.000584097,-2.40228e-07,-2.89269e-08,0.792277,0.000583529,-3.27008e-07,2.90431e-08,0.792861,0.000582963,-2.39879e-07,-2.76409e-08,0.793443,0.0005824,-3.22802e-07,2.1916e-08,0.794025,0.00058182,-2.57054e-07,-4.18368e-10,0.794607,0.000581305,-2.58309e-07,-2.02425e-08,0.795188,0.000580727,-3.19036e-07,2.17838e-08,0.795768,0.000580155,-2.53685e-07,-7.28814e-09,0.796348,0.000579625,-2.75549e-07,7.36871e-09,0.796928,0.000579096,-2.53443e-07,-2.21867e-08,0.797506,0.000578523,-3.20003e-07,2.17736e-08,0.798085,0.000577948,-2.54683e-07,-5.30296e-09,0.798662,0.000577423,-2.70592e-07,-5.61698e-10,0.799239,0.00057688,-2.72277e-07,7.54977e-09,0.799816,0.000576358,-2.49627e-07,-2.96374e-08,0.800392,0.00057577,-3.38539e-07,5.1395e-08,0.800968,0.000575247,-1.84354e-07,-5.67335e-08,0.801543,0.000574708,-3.54555e-07,5.63297e-08,0.802117,0.000574168,-1.85566e-07,-4.93759e-08,0.802691,0.000573649,-3.33693e-07,2.19646e-08,0.803264,0.000573047,-2.678e-07,2.1122e-08,0.803837,0.000572575,-2.04433e-07,-4.68482e-08,0.804409,0.000572026,-3.44978e-07,4.70613e-08,0.804981,0.000571477,-2.03794e-07,-2.21877e-08,0.805552,0.000571003,-2.70357e-07,-1.79153e-08,0.806123,0.000570408,-3.24103e-07,3.42443e-08,0.806693,0.000569863,-2.2137e-07,1.47556e-10,0.807263,0.000569421,-2.20928e-07,-3.48345e-08,0.807832,0.000568874,-3.25431e-07,1.99812e-08,0.808401,0.000568283,-2.65487e-07,1.45143e-08,0.808969,0.000567796,-2.21945e-07,-1.84338e-08,0.809536,0.000567297,-2.77246e-07,-3.83608e-10,0.810103,0.000566741,-2.78397e-07,1.99683e-08,0.81067,0.000566244,-2.18492e-07,-1.98848e-08,0.811236,0.000565747,-2.78146e-07,-3.38976e-11,0.811801,0.000565191,-2.78248e-07,2.00204e-08,0.812366,0.000564695,-2.18187e-07,-2.04429e-08,0.812931,0.000564197,-2.79516e-07,2.1467e-09,0.813495,0.000563644,-2.73076e-07,1.18561e-08,0.814058,0.000563134,-2.37507e-07,1.00334e-08,0.814621,0.000562689,-2.07407e-07,-5.19898e-08,0.815183,0.000562118,-3.63376e-07,7.87163e-08,0.815745,0.000561627,-1.27227e-07,-8.40616e-08,0.816306,0.000561121,-3.79412e-07,7.87163e-08,0.816867,0.000560598,-1.43263e-07,-5.19898e-08,0.817428,0.000560156,-2.99233e-07,1.00335e-08,0.817988,0.000559587,-2.69132e-07,1.18559e-08,0.818547,0.000559085,-2.33564e-07,2.14764e-09,0.819106,0.000558624,-2.27122e-07,-2.04464e-08,0.819664,0.000558108,-2.88461e-07,2.00334e-08,0.820222,0.000557591,-2.28361e-07,-8.24277e-11,0.820779,0.000557135,-2.28608e-07,-1.97037e-08,0.821336,0.000556618,-2.87719e-07,1.92925e-08,0.821893,0.000556101,-2.29841e-07,2.13831e-09,0.822448,0.000555647,-2.23427e-07,-2.78458e-08,0.823004,0.000555117,-3.06964e-07,4.96402e-08,0.823559,0.000554652,-1.58043e-07,-5.15058e-08,0.824113,0.000554181,-3.12561e-07,3.71737e-08,0.824667,0.000553668,-2.0104e-07,-3.75844e-08,0.82522,0.000553153,-3.13793e-07,5.35592e-08,0.825773,0.000552686,-1.53115e-07,-5.74431e-08,0.826326,0.000552207,-3.25444e-07,5.7004e-08,0.826878,0.000551728,-1.54433e-07,-5.13635e-08,0.827429,0.000551265,-3.08523e-07,2.92406e-08,0.82798,0.000550735,-2.20801e-07,-5.99424e-09,0.828531,0.000550276,-2.38784e-07,-5.26363e-09,0.829081,0.000549782,-2.54575e-07,2.70488e-08,0.82963,0.000549354,-1.73429e-07,-4.33268e-08,0.83018,0.000548878,-3.03409e-07,2.7049e-08,0.830728,0.000548352,-2.22262e-07,-5.26461e-09,0.831276,0.000547892,-2.38056e-07,-5.99057e-09,0.831824,0.000547397,-2.56027e-07,2.92269e-08,0.832371,0.000546973,-1.68347e-07,-5.13125e-08,0.832918,0.000546482,-3.22284e-07,5.68139e-08,0.833464,0.000546008,-1.51843e-07,-5.67336e-08,0.83401,0.000545534,-3.22043e-07,5.09113e-08,0.834555,0.000545043,-1.6931e-07,-2.77022e-08,0.8351,0.000544621,-2.52416e-07,2.92924e-10,0.835644,0.000544117,-2.51537e-07,2.65305e-08,0.836188,0.000543694,-1.71946e-07,-4.68105e-08,0.836732,0.00054321,-3.12377e-07,4.15021e-08,0.837275,0.000542709,-1.87871e-07,1.13355e-11,0.837817,0.000542334,-1.87837e-07,-4.15474e-08,0.838359,0.000541833,-3.12479e-07,4.69691e-08,0.838901,0.000541349,-1.71572e-07,-2.71196e-08,0.839442,0.000540925,-2.52931e-07,1.90462e-09,0.839983,0.000540425,-2.47217e-07,1.95011e-08,0.840523,0.000539989,-1.88713e-07,-2.03045e-08,0.841063,0.00053955,-2.49627e-07,2.11216e-09,0.841602,0.000539057,-2.4329e-07,1.18558e-08,0.842141,0.000538606,-2.07723e-07,1.00691e-08,0.842679,0.000538221,-1.77516e-07,-5.21324e-08,0.843217,0.00053771,-3.33913e-07,7.92513e-08,0.843755,0.00053728,-9.6159e-08,-8.60587e-08,0.844292,0.000536829,-3.54335e-07,8.61696e-08,0.844828,0.000536379,-9.58263e-08,-7.98057e-08,0.845364,0.000535948,-3.35243e-07,5.42394e-08,0.8459,0.00053544,-1.72525e-07,-1.79426e-08,0.846435,0.000535041,-2.26353e-07,1.75308e-08,0.84697,0.000534641,-1.73761e-07,-5.21806e-08,0.847505,0.000534137,-3.30302e-07,7.19824e-08,0.848038,0.000533692,-1.14355e-07,-5.69349e-08,0.848572,0.000533293,-2.8516e-07,3.65479e-08,0.849105,0.000532832,-1.75516e-07,-2.96519e-08,0.849638,0.000532392,-2.64472e-07,2.2455e-08,0.85017,0.000531931,-1.97107e-07,-5.63451e-10,0.850702,0.000531535,-1.98797e-07,-2.02011e-08,0.851233,0.000531077,-2.59401e-07,2.17634e-08,0.851764,0.000530623,-1.94111e-07,-7.24794e-09,0.852294,0.000530213,-2.15854e-07,7.22832e-09,0.852824,0.000529803,-1.94169e-07,-2.16653e-08,0.853354,0.00052935,-2.59165e-07,1.98283e-08,0.853883,0.000528891,-1.9968e-07,1.95678e-09,0.854412,0.000528497,-1.9381e-07,-2.76554e-08,0.85494,0.000528027,-2.76776e-07,4.90603e-08,0.855468,0.00052762,-1.29596e-07,-4.93764e-08,0.855995,0.000527213,-2.77725e-07,2.92361e-08,0.856522,0.000526745,-1.90016e-07,-7.96341e-09,0.857049,0.000526341,-2.13907e-07,2.61752e-09,0.857575,0.000525922,-2.06054e-07,-2.50665e-09,0.8581,0.000525502,-2.13574e-07,7.40906e-09,0.858626,0.000525097,-1.91347e-07,-2.71296e-08,0.859151,0.000524633,-2.72736e-07,4.15048e-08,0.859675,0.000524212,-1.48221e-07,-1.96802e-08,0.860199,0.000523856,-2.07262e-07,-2.23886e-08,0.860723,0.000523375,-2.74428e-07,4.96299e-08,0.861246,0.000522975,-1.25538e-07,-5.69216e-08,0.861769,0.000522553,-2.96303e-07,5.88473e-08,0.862291,0.000522137,-1.19761e-07,-5.92584e-08,0.862813,0.00052172,-2.97536e-07,5.8977e-08,0.863334,0.000521301,-1.20605e-07,-5.74403e-08,0.863855,0.000520888,-2.92926e-07,5.15751e-08,0.864376,0.000520457,-1.38201e-07,-2.96506e-08,0.864896,0.000520091,-2.27153e-07,7.42277e-09,0.865416,0.000519659,-2.04885e-07,-4.05057e-11,0.865936,0.00051925,-2.05006e-07,-7.26074e-09,0.866455,0.000518818,-2.26788e-07,2.90835e-08,0.866973,0.000518451,-1.39538e-07,-4.94686e-08,0.867492,0.000518024,-2.87944e-07,4.95814e-08,0.868009,0.000517597,-1.39199e-07,-2.96479e-08,0.868527,0.000517229,-2.28143e-07,9.40539e-09,0.869044,0.000516801,-1.99927e-07,-7.9737e-09,0.86956,0.000516378,-2.23848e-07,2.24894e-08,0.870077,0.000515997,-1.5638e-07,-2.23793e-08,0.870592,0.000515617,-2.23517e-07,7.42302e-09,0.871108,0.000515193,-2.01248e-07,-7.31283e-09,0.871623,0.000514768,-2.23187e-07,2.18283e-08,0.872137,0.000514387,-1.57702e-07,-2.03959e-08,0.872652,0.000514011,-2.1889e-07,1.50711e-10,0.873165,0.000513573,-2.18437e-07,1.97931e-08,0.873679,0.000513196,-1.59058e-07,-1.97183e-08,0.874192,0.000512819,-2.18213e-07,-5.24324e-10,0.874704,0.000512381,-2.19786e-07,2.18156e-08,0.875217,0.000512007,-1.54339e-07,-2.71336e-08,0.875728,0.000511616,-2.3574e-07,2.71141e-08,0.87624,0.000511226,-1.54398e-07,-2.17182e-08,0.876751,0.000510852,-2.19552e-07,1.54131e-10,0.877262,0.000510414,-2.1909e-07,2.11017e-08,0.877772,0.000510039,-1.55785e-07,-2.49562e-08,0.878282,0.000509652,-2.30654e-07,1.91183e-08,0.878791,0.000509248,-1.73299e-07,8.08751e-09,0.8793,0.000508926,-1.49036e-07,-5.14684e-08,0.879809,0.000508474,-3.03441e-07,7.85766e-08,0.880317,0.000508103,-6.77112e-08,-8.40242e-08,0.880825,0.000507715,-3.19784e-07,7.87063e-08,0.881333,0.000507312,-8.36649e-08,-5.19871e-08,0.88184,0.000506988,-2.39626e-07,1.00327e-08,0.882346,0.000506539,-2.09528e-07,1.18562e-08,0.882853,0.000506156,-1.73959e-07,2.14703e-09,0.883359,0.000505814,-1.67518e-07,-2.04444e-08,0.883864,0.000505418,-2.28851e-07,2.00258e-08,0.88437,0.00050502,-1.68774e-07,-5.42855e-11,0.884874,0.000504682,-1.68937e-07,-1.98087e-08,0.885379,0.000504285,-2.28363e-07,1.96842e-08,0.885883,0.000503887,-1.6931e-07,6.76342e-10,0.886387,0.000503551,-1.67281e-07,-2.23896e-08,0.88689,0.000503149,-2.3445e-07,2.92774e-08,0.887393,0.000502768,-1.46618e-07,-3.51152e-08,0.887896,0.00050237,-2.51963e-07,5.15787e-08,0.888398,0.00050202,-9.72271e-08,-5.19903e-08,0.8889,0.00050167,-2.53198e-07,3.71732e-08,0.889401,0.000501275,-1.41678e-07,-3.70978e-08,0.889902,0.00050088,-2.52972e-07,5.16132e-08,0.890403,0.000500529,-9.81321e-08,-5.01459e-08,0.890903,0.000500183,-2.4857e-07,2.9761e-08,0.891403,0.000499775,-1.59287e-07,-9.29351e-09,0.891903,0.000499428,-1.87167e-07,7.41301e-09,0.892402,0.000499076,-1.64928e-07,-2.03585e-08,0.892901,0.000498685,-2.26004e-07,1.44165e-08,0.893399,0.000498276,-1.82754e-07,2.22974e-08,0.893898,0.000497978,-1.15862e-07,-4.40013e-08,0.894395,0.000497614,-2.47866e-07,3.44985e-08,0.894893,0.000497222,-1.44371e-07,-3.43882e-08,0.89539,0.00049683,-2.47535e-07,4.34497e-08,0.895886,0.000496465,-1.17186e-07,-2.02012e-08,0.896383,0.00049617,-1.7779e-07,-2.22497e-08,0.896879,0.000495748,-2.44539e-07,4.95952e-08,0.897374,0.000495408,-9.57532e-08,-5.69217e-08,0.89787,0.000495045,-2.66518e-07,5.88823e-08,0.898364,0.000494689,-8.98713e-08,-5.93983e-08,0.898859,0.000494331,-2.68066e-07,5.95017e-08,0.899353,0.000493973,-8.95613e-08,-5.9399e-08,0.899847,0.000493616,-2.67758e-07,5.8885e-08,0.90034,0.000493257,-9.11033e-08,-5.69317e-08,0.900833,0.000492904,-2.61898e-07,4.96326e-08,0.901326,0.000492529,-1.13001e-07,-2.23893e-08,0.901819,0.000492236,-1.80169e-07,-1.968e-08,0.902311,0.000491817,-2.39209e-07,4.15047e-08,0.902802,0.000491463,-1.14694e-07,-2.71296e-08,0.903293,0.000491152,-1.96083e-07,7.409e-09,0.903784,0.000490782,-1.73856e-07,-2.50645e-09,0.904275,0.000490427,-1.81376e-07,2.61679e-09,0.904765,0.000490072,-1.73525e-07,-7.96072e-09,0.905255,0.000489701,-1.97407e-07,2.92261e-08,0.905745,0.000489394,-1.09729e-07,-4.93389e-08,0.906234,0.000489027,-2.57746e-07,4.89204e-08,0.906723,0.000488658,-1.10985e-07,-2.71333e-08,0.907211,0.000488354,-1.92385e-07,8.30861e-12,0.907699,0.00048797,-1.9236e-07,2.71001e-08,0.908187,0.000487666,-1.1106e-07,-4.88041e-08,0.908675,0.000487298,-2.57472e-07,4.89069e-08,0.909162,0.000486929,-1.10751e-07,-2.76143e-08,0.909649,0.000486625,-1.93594e-07,1.9457e-09,0.910135,0.000486244,-1.87757e-07,1.98315e-08,0.910621,0.000485928,-1.28262e-07,-2.16671e-08,0.911107,0.000485606,-1.93264e-07,7.23216e-09,0.911592,0.000485241,-1.71567e-07,-7.26152e-09,0.912077,0.000484877,-1.93352e-07,2.18139e-08,0.912562,0.000484555,-1.2791e-07,-2.03895e-08,0.913047,0.000484238,-1.89078e-07,1.39494e-10,0.913531,0.000483861,-1.8866e-07,1.98315e-08,0.914014,0.000483543,-1.29165e-07,-1.98609e-08,0.914498,0.000483225,-1.88748e-07,7.39912e-12,0.914981,0.000482847,-1.88726e-07,1.98313e-08,0.915463,0.000482529,-1.29232e-07,-1.9728e-08,0.915946,0.000482212,-1.88416e-07,-5.24035e-10,0.916428,0.000481833,-1.89988e-07,2.18241e-08,0.916909,0.000481519,-1.24516e-07,-2.71679e-08,0.917391,0.000481188,-2.06019e-07,2.72427e-08,0.917872,0.000480858,-1.24291e-07,-2.21985e-08,0.918353,0.000480543,-1.90886e-07,1.94644e-09,0.918833,0.000480167,-1.85047e-07,1.44127e-08,0.919313,0.00047984,-1.41809e-07,7.39438e-12,0.919793,0.000479556,-1.41787e-07,-1.44423e-08,0.920272,0.000479229,-1.85114e-07,-1.84291e-09,0.920751,0.000478854,-1.90642e-07,2.18139e-08,0.92123,0.000478538,-1.25201e-07,-2.58081e-08,0.921708,0.00047821,-2.02625e-07,2.18139e-08,0.922186,0.00047787,-1.37183e-07,-1.84291e-09,0.922664,0.00047759,-1.42712e-07,-1.44423e-08,0.923141,0.000477262,-1.86039e-07,7.34701e-12,0.923618,0.00047689,-1.86017e-07,1.44129e-08,0.924095,0.000476561,-1.42778e-07,1.94572e-09,0.924572,0.000476281,-1.36941e-07,-2.21958e-08,0.925048,0.000475941,-2.03528e-07,2.72327e-08,0.925523,0.000475615,-1.2183e-07,-2.71304e-08,0.925999,0.00047529,-2.03221e-07,2.16843e-08,0.926474,0.000474949,-1.38168e-07,-2.16005e-12,0.926949,0.000474672,-1.38175e-07,-2.16756e-08,0.927423,0.000474331,-2.03202e-07,2.71001e-08,0.927897,0.000474006,-1.21902e-07,-2.71201e-08,0.928371,0.000473681,-2.03262e-07,2.17757e-08,0.928845,0.00047334,-1.37935e-07,-3.78028e-10,0.929318,0.000473063,-1.39069e-07,-2.02636e-08,0.929791,0.000472724,-1.9986e-07,2.18276e-08,0.930263,0.000472389,-1.34377e-07,-7.44231e-09,0.930736,0.000472098,-1.56704e-07,7.94165e-09,0.931208,0.000471809,-1.32879e-07,-2.43243e-08,0.931679,0.00047147,-2.05851e-07,2.97508e-08,0.932151,0.000471148,-1.16599e-07,-3.50742e-08,0.932622,0.000470809,-2.21822e-07,5.09414e-08,0.933092,0.000470518,-6.89976e-08,-4.94821e-08,0.933563,0.000470232,-2.17444e-07,2.77775e-08,0.934033,0.00046988,-1.34111e-07,-2.02351e-09,0.934502,0.000469606,-1.40182e-07,-1.96835e-08,0.934972,0.000469267,-1.99232e-07,2.11529e-08,0.935441,0.000468932,-1.35774e-07,-5.32332e-09,0.93591,0.000468644,-1.51743e-07,1.40413e-10,0.936378,0.000468341,-1.51322e-07,4.76166e-09,0.936846,0.000468053,-1.37037e-07,-1.9187e-08,0.937314,0.000467721,-1.94598e-07,1.23819e-08,0.937782,0.000467369,-1.57453e-07,2.92642e-08,0.938249,0.000467142,-6.96601e-08,-6.98342e-08,0.938716,0.000466793,-2.79163e-07,7.12586e-08,0.939183,0.000466449,-6.53869e-08,-3.63863e-08,0.939649,0.000466209,-1.74546e-07,1.46818e-08,0.940115,0.000465904,-1.305e-07,-2.2341e-08,0.940581,0.000465576,-1.97523e-07,1.50774e-08,0.941046,0.000465226,-1.52291e-07,2.16359e-08,0.941511,0.000464986,-8.73832e-08,-4.20162e-08,0.941976,0.000464685,-2.13432e-07,2.72198e-08,0.942441,0.00046434,-1.31773e-07,-7.2581e-09,0.942905,0.000464055,-1.53547e-07,1.81263e-09,0.943369,0.000463753,-1.48109e-07,7.58386e-12,0.943832,0.000463457,-1.48086e-07,-1.84298e-09,0.944296,0.000463155,-1.53615e-07,7.36433e-09,0.944759,0.00046287,-1.31522e-07,-2.76143e-08,0.945221,0.000462524,-2.14365e-07,4.34883e-08,0.945684,0.000462226,-8.39003e-08,-2.71297e-08,0.946146,0.000461977,-1.65289e-07,5.42595e-09,0.946608,0.000461662,-1.49012e-07,5.42593e-09,0.947069,0.000461381,-1.32734e-07,-2.71297e-08,0.94753,0.000461034,-2.14123e-07,4.34881e-08,0.947991,0.000460736,-8.36585e-08,-2.76134e-08,0.948452,0.000460486,-1.66499e-07,7.36083e-09,0.948912,0.000460175,-1.44416e-07,-1.82993e-09,0.949372,0.000459881,-1.49906e-07,-4.11073e-11,0.949832,0.000459581,-1.50029e-07,1.99434e-09,0.950291,0.000459287,-1.44046e-07,-7.93627e-09,0.950751,0.000458975,-1.67855e-07,2.97507e-08,0.951209,0.000458728,-7.86029e-08,-5.1462e-08,0.951668,0.000458417,-2.32989e-07,5.6888e-08,0.952126,0.000458121,-6.2325e-08,-5.68806e-08,0.952584,0.000457826,-2.32967e-07,5.14251e-08,0.953042,0.000457514,-7.86914e-08,-2.96107e-08,0.953499,0.000457268,-1.67523e-07,7.41296e-09,0.953956,0.000456955,-1.45285e-07,-4.11262e-11,0.954413,0.000456665,-1.45408e-07,-7.24847e-09,0.95487,0.000456352,-1.67153e-07,2.9035e-08,0.955326,0.000456105,-8.00484e-08,-4.92869e-08,0.955782,0.000455797,-2.27909e-07,4.89032e-08,0.956238,0.000455488,-8.11994e-08,-2.71166e-08,0.956693,0.000455244,-1.62549e-07,-4.13678e-11,0.957148,0.000454919,-1.62673e-07,2.72821e-08,0.957603,0.000454675,-8.0827e-08,-4.94824e-08,0.958057,0.000454365,-2.29274e-07,5.14382e-08,0.958512,0.000454061,-7.49597e-08,-3.7061e-08,0.958965,0.0004538,-1.86143e-07,3.72013e-08,0.959419,0.000453539,-7.45389e-08,-5.21396e-08,0.959873,0.000453234,-2.30958e-07,5.21476e-08,0.960326,0.000452928,-7.45146e-08,-3.72416e-08,0.960778,0.000452667,-1.8624e-07,3.72143e-08,0.961231,0.000452407,-7.45967e-08,-5.20109e-08,0.961683,0.000452101,-2.30629e-07,5.16199e-08,0.962135,0.000451795,-7.57696e-08,-3.52595e-08,0.962587,0.000451538,-1.81548e-07,2.98133e-08,0.963038,0.000451264,-9.2108e-08,-2.43892e-08,0.963489,0.000451007,-1.65276e-07,8.13892e-09,0.96394,0.000450701,-1.40859e-07,-8.16647e-09,0.964391,0.000450394,-1.65358e-07,2.45269e-08,0.964841,0.000450137,-9.17775e-08,-3.03367e-08,0.965291,0.000449863,-1.82787e-07,3.7215e-08,0.965741,0.000449609,-7.11424e-08,-5.89188e-08,0.96619,0.00044929,-2.47899e-07,7.92509e-08,0.966639,0.000449032,-1.01462e-08,-7.92707e-08,0.967088,0.000448773,-2.47958e-07,5.90181e-08,0.967537,0.000448455,-7.0904e-08,-3.75925e-08,0.967985,0.0004482,-1.83681e-07,3.17471e-08,0.968433,0.000447928,-8.84401e-08,-2.97913e-08,0.968881,0.000447662,-1.77814e-07,2.78133e-08,0.969329,0.000447389,-9.4374e-08,-2.18572e-08,0.969776,0.000447135,-1.59946e-07,1.10134e-11,0.970223,0.000446815,-1.59913e-07,2.18132e-08,0.97067,0.000446561,-9.44732e-08,-2.76591e-08,0.971116,0.000446289,-1.7745e-07,2.92185e-08,0.971562,0.000446022,-8.97948e-08,-2.96104e-08,0.972008,0.000445753,-1.78626e-07,2.96185e-08,0.972454,0.000445485,-8.97706e-08,-2.92588e-08,0.972899,0.000445218,-1.77547e-07,2.78123e-08,0.973344,0.000444946,-9.41103e-08,-2.23856e-08,0.973789,0.000444691,-1.61267e-07,2.12559e-09,0.974233,0.000444374,-1.5489e-07,1.38833e-08,0.974678,0.000444106,-1.13241e-07,1.94591e-09,0.975122,0.000443886,-1.07403e-07,-2.16669e-08,0.975565,0.000443606,-1.72404e-07,2.5117e-08,0.976009,0.000443336,-9.70526e-08,-1.91963e-08,0.976452,0.000443085,-1.54642e-07,-7.93627e-09,0.976895,0.000442752,-1.7845e-07,5.09414e-08,0.977338,0.000442548,-2.56262e-08,-7.66201e-08,0.97778,0.000442266,-2.55486e-07,7.67249e-08,0.978222,0.000441986,-2.53118e-08,-5.14655e-08,0.978664,0.000441781,-1.79708e-07,9.92773e-09,0.979106,0.000441451,-1.49925e-07,1.17546e-08,0.979547,0.000441186,-1.14661e-07,2.65868e-09,0.979988,0.000440965,-1.06685e-07,-2.23893e-08,0.980429,0.000440684,-1.73853e-07,2.72939e-08,0.980869,0.000440419,-9.19716e-08,-2.71816e-08,0.98131,0.000440153,-1.73516e-07,2.18278e-08,0.98175,0.000439872,-1.08033e-07,-5.24833e-10,0.982189,0.000439654,-1.09607e-07,-1.97284e-08,0.982629,0.000439376,-1.68793e-07,1.98339e-08,0.983068,0.000439097,-1.09291e-07,-2.62901e-12,0.983507,0.000438879,-1.09299e-07,-1.98234e-08,0.983946,0.000438601,-1.68769e-07,1.96916e-08,0.984384,0.000438322,-1.09694e-07,6.6157e-10,0.984823,0.000438105,-1.0771e-07,-2.23379e-08,0.985261,0.000437823,-1.74723e-07,2.90855e-08,0.985698,0.00043756,-8.74669e-08,-3.43992e-08,0.986136,0.000437282,-1.90665e-07,4.89068e-08,0.986573,0.000437048,-4.39442e-08,-4.20188e-08,0.98701,0.000436834,-1.7e-07,-4.11073e-11,0.987446,0.000436494,-1.70124e-07,4.21832e-08,0.987883,0.00043628,-4.35742e-08,-4.94824e-08,0.988319,0.000436044,-1.92021e-07,3.6537e-08,0.988755,0.00043577,-8.24102e-08,-3.70611e-08,0.989191,0.000435494,-1.93593e-07,5.21026e-08,0.989626,0.000435263,-3.72855e-08,-5.21402e-08,0.990061,0.000435032,-1.93706e-07,3.7249e-08,0.990496,0.000434756,-8.19592e-08,-3.72512e-08,0.990931,0.000434481,-1.93713e-07,5.21511e-08,0.991365,0.00043425,-3.72595e-08,-5.21439e-08,0.991799,0.000434019,-1.93691e-07,3.72152e-08,0.992233,0.000433743,-8.20456e-08,-3.71123e-08,0.992667,0.000433468,-1.93382e-07,5.16292e-08,0.9931,0.000433236,-3.84947e-08,-5.01953e-08,0.993533,0.000433008,-1.89081e-07,2.99427e-08,0.993966,0.00043272,-9.92525e-08,-9.9708e-09,0.994399,0.000432491,-1.29165e-07,9.94051e-09,0.994831,0.000432263,-9.93434e-08,-2.97912e-08,0.995263,0.000431975,-1.88717e-07,4.96198e-08,0.995695,0.000431746,-3.98578e-08,-4.94785e-08,0.996127,0.000431518,-1.88293e-07,2.9085e-08,0.996558,0.000431229,-1.01038e-07,-7.25675e-09,0.996989,0.000431005,-1.22809e-07,-5.79945e-11,0.99742,0.000430759,-1.22983e-07,7.48873e-09,0.997851,0.000430536,-1.00516e-07,-2.98969e-08,0.998281,0.000430245,-1.90207e-07,5.24942e-08,0.998711,0.000430022,-3.27246e-08,-6.08706e-08,0.999141,0.000429774,-2.15336e-07,7.17788e-08,0.999571,0.000429392,0.,0.}; + + template + __device__ __forceinline__ void Lab2RGBConvert_f(const T& src, D& dst) + { + const float lThresh = 0.008856f * 903.3f; + const float fThresh = 7.787f * 0.008856f + 16.0f / 116.0f; + + float Y, fy; + + if (src.x <= lThresh) + { + Y = src.x / 903.3f; + fy = 7.787f * Y + 16.0f / 116.0f; + } + else + { + fy = (src.x + 16.0f) / 116.0f; + Y = fy * fy * fy; + } + + float X = src.y / 500.0f + fy; + float Z = fy - src.z / 200.0f; + + if (X <= fThresh) + X = (X - 16.0f / 116.0f) / 7.787f; + else + X = X * X * X; + + if (Z <= fThresh) + Z = (Z - 16.0f / 116.0f) / 7.787f; + else + Z = Z * Z * Z; + + float B = 0.052891f * X - 0.204043f * Y + 1.151152f * Z; + float G = -0.921235f * X + 1.875991f * Y + 0.045244f * Z; + float R = 3.079933f * X - 1.537150f * Y - 0.542782f * Z; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + } + + dst.x = blueIdx == 0 ? B : R; + dst.y = G; + dst.z = blueIdx == 0 ? R : B; + setAlpha(dst, ColorChannel::max()); + } + + template + __device__ __forceinline__ void Lab2RGBConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (100.f / 255.f); + srcf.y = src.y - 128; + srcf.z = src.z - 128; + + Lab2RGBConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 255.f); + dst.y = saturate_cast(dstf.y * 255.f); + dst.z = saturate_cast(dstf.z * 255.f); + setAlpha(dst, ColorChannel::max()); + } + + template struct Lab2RGB; + template + struct Lab2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Lab2RGBConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Lab2RGB() {} + __host__ __device__ __forceinline__ Lab2RGB(const Lab2RGB&) {} + }; + template + struct Lab2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Lab2RGBConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Lab2RGB() {} + __host__ __device__ __forceinline__ Lab2RGB(const Lab2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_Lab2RGB_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Lab2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + +///////////////////////////////////// RGB <-> Luv ///////////////////////////////////// + + namespace color_detail + { + __constant__ float c_LabCbrtTab[] = {0.137931,0.0114066,0.,1.18859e-07,0.149338,0.011407,3.56578e-07,-5.79396e-07,0.160745,0.0114059,-1.38161e-06,2.16892e-06,0.172151,0.0114097,5.12516e-06,-8.0814e-06,0.183558,0.0113957,-1.9119e-05,3.01567e-05,0.194965,0.0114479,7.13509e-05,-0.000112545,0.206371,0.011253,-0.000266285,-0.000106493,0.217252,0.0104009,-0.000585765,7.32149e-05,0.22714,0.00944906,-0.00036612,1.21917e-05,0.236235,0.0087534,-0.000329545,2.01753e-05,0.244679,0.00815483,-0.000269019,1.24435e-05,0.252577,0.00765412,-0.000231689,1.05618e-05,0.26001,0.00722243,-0.000200003,8.26662e-06,0.267041,0.00684723,-0.000175203,6.76746e-06,0.27372,0.00651712,-0.000154901,5.61192e-06,0.280088,0.00622416,-0.000138065,4.67009e-06,0.286179,0.00596204,-0.000124055,3.99012e-06,0.292021,0.0057259,-0.000112085,3.36032e-06,0.297638,0.00551181,-0.000102004,2.95338e-06,0.30305,0.00531666,-9.31435e-05,2.52875e-06,0.308277,0.00513796,-8.55572e-05,2.22022e-06,0.313331,0.00497351,-7.88966e-05,1.97163e-06,0.318228,0.00482163,-7.29817e-05,1.7248e-06,0.322978,0.00468084,-6.78073e-05,1.55998e-06,0.327593,0.0045499,-6.31274e-05,1.36343e-06,0.332081,0.00442774,-5.90371e-05,1.27136e-06,0.336451,0.00431348,-5.5223e-05,1.09111e-06,0.34071,0.00420631,-5.19496e-05,1.0399e-06,0.344866,0.00410553,-4.88299e-05,9.18347e-07,0.348923,0.00401062,-4.60749e-05,8.29942e-07,0.352889,0.00392096,-4.35851e-05,7.98478e-07,0.356767,0.00383619,-4.11896e-05,6.84917e-07,0.360562,0.00375586,-3.91349e-05,6.63976e-07,0.36428,0.00367959,-3.7143e-05,5.93086e-07,0.367923,0.00360708,-3.53637e-05,5.6976e-07,0.371495,0.00353806,-3.36544e-05,4.95533e-07,0.375,0.00347224,-3.21678e-05,4.87951e-07,0.378441,0.00340937,-3.0704e-05,4.4349e-07,0.38182,0.00334929,-2.93735e-05,4.20297e-07,0.38514,0.0032918,-2.81126e-05,3.7872e-07,0.388404,0.00323671,-2.69764e-05,3.596e-07,0.391614,0.00318384,-2.58976e-05,3.5845e-07,0.394772,0.00313312,-2.48223e-05,2.92765e-07,0.397881,0.00308435,-2.3944e-05,3.18232e-07,0.400942,0.00303742,-2.29893e-05,2.82046e-07,0.403957,0.00299229,-2.21432e-05,2.52315e-07,0.406927,0.00294876,-2.13862e-05,2.58416e-07,0.409855,0.00290676,-2.0611e-05,2.33939e-07,0.412741,0.00286624,-1.99092e-05,2.36342e-07,0.415587,0.00282713,-1.92001e-05,1.916e-07,0.418396,0.00278931,-1.86253e-05,2.1915e-07,0.421167,0.00275271,-1.79679e-05,1.83498e-07,0.423901,0.00271733,-1.74174e-05,1.79343e-07,0.426602,0.00268303,-1.68794e-05,1.72013e-07,0.429268,0.00264979,-1.63633e-05,1.75686e-07,0.431901,0.00261759,-1.58363e-05,1.3852e-07,0.434503,0.00258633,-1.54207e-05,1.64304e-07,0.437074,0.00255598,-1.49278e-05,1.28136e-07,0.439616,0.00252651,-1.45434e-05,1.57618e-07,0.442128,0.0024979,-1.40705e-05,1.0566e-07,0.444612,0.00247007,-1.37535e-05,1.34998e-07,0.447068,0.00244297,-1.33485e-05,1.29207e-07,0.449498,0.00241666,-1.29609e-05,9.32347e-08,0.451902,0.00239102,-1.26812e-05,1.23703e-07,0.45428,0.00236603,-1.23101e-05,9.74072e-08,0.456634,0.0023417,-1.20179e-05,1.12518e-07,0.458964,0.002318,-1.16803e-05,7.83681e-08,0.46127,0.00229488,-1.14452e-05,1.10452e-07,0.463554,0.00227232,-1.11139e-05,7.58719e-08,0.465815,0.00225032,-1.08863e-05,9.2699e-08,0.468055,0.00222882,-1.06082e-05,8.97738e-08,0.470273,0.00220788,-1.03388e-05,5.4845e-08,0.47247,0.00218736,-1.01743e-05,1.0808e-07,0.474648,0.00216734,-9.85007e-06,4.9277e-08,0.476805,0.00214779,-9.70224e-06,8.22408e-08,0.478943,0.00212863,-9.45551e-06,6.87942e-08,0.481063,0.00210993,-9.24913e-06,5.98144e-08,0.483163,0.00209161,-9.06969e-06,7.93789e-08,0.485246,0.00207371,-8.83155e-06,3.99032e-08,0.487311,0.00205616,-8.71184e-06,8.88325e-08,0.489358,0.002039,-8.44534e-06,2.20004e-08,0.491389,0.00202218,-8.37934e-06,9.13872e-08,0.493403,0.0020057,-8.10518e-06,2.96829e-08,0.495401,0.00198957,-8.01613e-06,5.81028e-08,0.497382,0.00197372,-7.84183e-06,6.5731e-08,0.499348,0.00195823,-7.64463e-06,3.66019e-08,0.501299,0.00194305,-7.53483e-06,2.62811e-08,0.503234,0.00192806,-7.45598e-06,9.66907e-08,0.505155,0.00191344,-7.16591e-06,4.18928e-09,0.507061,0.00189912,-7.15334e-06,6.53665e-08,0.508953,0.00188501,-6.95724e-06,3.23686e-08,0.510831,0.00187119,-6.86014e-06,4.35774e-08,0.512696,0.0018576,-6.72941e-06,3.17406e-08,0.514547,0.00184424,-6.63418e-06,6.78785e-08,0.516384,0.00183117,-6.43055e-06,-5.23126e-09,0.518209,0.0018183,-6.44624e-06,7.22562e-08,0.520021,0.00180562,-6.22947e-06,1.42292e-08,0.52182,0.0017932,-6.18679e-06,4.9641e-08,0.523607,0.00178098,-6.03786e-06,2.56259e-08,0.525382,0.00176898,-5.96099e-06,2.66696e-08,0.527145,0.00175714,-5.88098e-06,4.65094e-08,0.528897,0.00174552,-5.74145e-06,2.57114e-08,0.530637,0.00173411,-5.66431e-06,2.94588e-08,0.532365,0.00172287,-5.57594e-06,3.52667e-08,0.534082,0.00171182,-5.47014e-06,8.28868e-09,0.535789,0.00170091,-5.44527e-06,5.07871e-08,0.537484,0.00169017,-5.29291e-06,2.69817e-08,0.539169,0.00167967,-5.21197e-06,2.01009e-08,0.540844,0.0016693,-5.15166e-06,1.18237e-08,0.542508,0.00165903,-5.11619e-06,5.18135e-08,0.544162,0.00164896,-4.96075e-06,1.9341e-08,0.545806,0.00163909,-4.90273e-06,-9.96867e-09,0.54744,0.00162926,-4.93263e-06,8.01382e-08,0.549064,0.00161963,-4.69222e-06,-1.25601e-08,0.550679,0.00161021,-4.7299e-06,2.97067e-08,0.552285,0.00160084,-4.64078e-06,1.29426e-08,0.553881,0.0015916,-4.60195e-06,3.77327e-08,0.555468,0.00158251,-4.48875e-06,1.49412e-08,0.557046,0.00157357,-4.44393e-06,2.17118e-08,0.558615,0.00156475,-4.3788e-06,1.74206e-08,0.560176,0.00155605,-4.32653e-06,2.78152e-08,0.561727,0.00154748,-4.24309e-06,-9.47239e-09,0.563271,0.00153896,-4.27151e-06,6.9679e-08,0.564805,0.00153063,-4.06247e-06,-3.08246e-08,0.566332,0.00152241,-4.15494e-06,5.36188e-08,0.56785,0.00151426,-3.99409e-06,-4.83594e-09,0.56936,0.00150626,-4.00859e-06,2.53293e-08,0.570863,0.00149832,-3.93261e-06,2.27286e-08,0.572357,0.00149052,-3.86442e-06,2.96541e-09,0.573844,0.0014828,-3.85552e-06,2.50147e-08,0.575323,0.00147516,-3.78048e-06,1.61842e-08,0.576794,0.00146765,-3.73193e-06,2.94582e-08,0.578258,0.00146028,-3.64355e-06,-1.48076e-08,0.579715,0.00145295,-3.68798e-06,2.97724e-08,0.581164,0.00144566,-3.59866e-06,1.49272e-08,0.582606,0.00143851,-3.55388e-06,2.97285e-08,0.584041,0.00143149,-3.46469e-06,-1.46323e-08,0.585469,0.00142451,-3.50859e-06,2.88004e-08,0.58689,0.00141758,-3.42219e-06,1.864e-08,0.588304,0.00141079,-3.36627e-06,1.58482e-08,0.589712,0.00140411,-3.31872e-06,-2.24279e-08,0.591112,0.00139741,-3.38601e-06,7.38639e-08,0.592507,0.00139085,-3.16441e-06,-3.46088e-08,0.593894,0.00138442,-3.26824e-06,4.96675e-09,0.595275,0.0013779,-3.25334e-06,7.4346e-08,0.59665,0.00137162,-3.0303e-06,-6.39319e-08,0.598019,0.00136536,-3.2221e-06,6.21725e-08,0.599381,0.00135911,-3.03558e-06,-5.94423e-09,0.600737,0.00135302,-3.05341e-06,2.12091e-08,0.602087,0.00134697,-2.98979e-06,-1.92876e-08,0.603431,0.00134094,-3.04765e-06,5.5941e-08,0.604769,0.00133501,-2.87983e-06,-2.56622e-08,0.606101,0.00132917,-2.95681e-06,4.67078e-08,0.607427,0.0013234,-2.81669e-06,-4.19592e-08,0.608748,0.00131764,-2.94257e-06,6.15243e-08,0.610062,0.00131194,-2.75799e-06,-2.53244e-08,0.611372,0.00130635,-2.83397e-06,3.97739e-08,0.612675,0.0013008,-2.71465e-06,-1.45618e-08,0.613973,0.00129533,-2.75833e-06,1.84733e-08,0.615266,0.00128986,-2.70291e-06,2.73606e-10,0.616553,0.00128446,-2.70209e-06,4.00367e-08,0.617835,0.00127918,-2.58198e-06,-4.12113e-08,0.619111,0.00127389,-2.70561e-06,6.52039e-08,0.620383,0.00126867,-2.51e-06,-4.07901e-08,0.621649,0.00126353,-2.63237e-06,3.83516e-08,0.62291,0.00125838,-2.51732e-06,6.59315e-09,0.624166,0.00125337,-2.49754e-06,-5.11939e-09,0.625416,0.00124836,-2.5129e-06,1.38846e-08,0.626662,0.00124337,-2.47124e-06,9.18514e-09,0.627903,0.00123846,-2.44369e-06,8.97952e-09,0.629139,0.0012336,-2.41675e-06,1.45012e-08,0.63037,0.00122881,-2.37325e-06,-7.37949e-09,0.631597,0.00122404,-2.39538e-06,1.50169e-08,0.632818,0.00121929,-2.35033e-06,6.91648e-09,0.634035,0.00121461,-2.32958e-06,1.69219e-08,0.635248,0.00121,-2.27882e-06,-1.49997e-08,0.636455,0.0012054,-2.32382e-06,4.30769e-08,0.637659,0.00120088,-2.19459e-06,-3.80986e-08,0.638857,0.00119638,-2.30888e-06,4.97134e-08,0.640051,0.00119191,-2.15974e-06,-4.15463e-08,0.641241,0.00118747,-2.28438e-06,5.68667e-08,0.642426,0.00118307,-2.11378e-06,-7.10641e-09,0.643607,0.00117882,-2.1351e-06,-2.8441e-08,0.644784,0.00117446,-2.22042e-06,6.12658e-08,0.645956,0.00117021,-2.03663e-06,-3.78083e-08,0.647124,0.00116602,-2.15005e-06,3.03627e-08,0.648288,0.00116181,-2.05896e-06,-2.40379e-08,0.649448,0.00115762,-2.13108e-06,6.57887e-08,0.650603,0.00115356,-1.93371e-06,-6.03028e-08,0.651755,0.00114951,-2.11462e-06,5.62134e-08,0.652902,0.00114545,-1.94598e-06,-4.53417e-08,0.654046,0.00114142,-2.082e-06,6.55489e-08,0.655185,0.00113745,-1.88536e-06,-3.80396e-08,0.656321,0.00113357,-1.99948e-06,2.70049e-08,0.657452,0.00112965,-1.91846e-06,-1.03755e-08,0.65858,0.00112578,-1.94959e-06,1.44973e-08,0.659704,0.00112192,-1.9061e-06,1.1991e-08,0.660824,0.00111815,-1.87012e-06,-2.85634e-09,0.66194,0.0011144,-1.87869e-06,-5.65782e-10,0.663053,0.00111064,-1.88039e-06,5.11947e-09,0.664162,0.0011069,-1.86503e-06,3.96924e-08,0.665267,0.00110328,-1.74595e-06,-4.46795e-08,0.666368,0.00109966,-1.87999e-06,1.98161e-08,0.667466,0.00109596,-1.82054e-06,2.502e-08,0.66856,0.00109239,-1.74548e-06,-6.86593e-10,0.669651,0.0010889,-1.74754e-06,-2.22739e-08,0.670738,0.00108534,-1.81437e-06,3.01776e-08,0.671821,0.0010818,-1.72383e-06,2.07732e-08,0.672902,0.00107841,-1.66151e-06,-5.36658e-08,0.673978,0.00107493,-1.82251e-06,7.46802e-08,0.675051,0.00107151,-1.59847e-06,-6.62411e-08,0.676121,0.00106811,-1.79719e-06,7.10748e-08,0.677188,0.00106473,-1.58397e-06,-3.92441e-08,0.678251,0.00106145,-1.7017e-06,2.62973e-08,0.679311,0.00105812,-1.62281e-06,-6.34035e-09,0.680367,0.00105486,-1.64183e-06,-9.36249e-10,0.68142,0.00105157,-1.64464e-06,1.00854e-08,0.68247,0.00104831,-1.61438e-06,2.01995e-08,0.683517,0.00104514,-1.55378e-06,-3.1279e-08,0.68456,0.00104194,-1.64762e-06,4.53114e-08,0.685601,0.00103878,-1.51169e-06,-3.07573e-08,0.686638,0.00103567,-1.60396e-06,1.81133e-08,0.687672,0.00103251,-1.54962e-06,1.79085e-08,0.688703,0.00102947,-1.49589e-06,-3.01428e-08,0.689731,0.00102639,-1.58632e-06,4.30583e-08,0.690756,0.00102334,-1.45715e-06,-2.28814e-08,0.691778,0.00102036,-1.52579e-06,-1.11373e-08,0.692797,0.00101727,-1.5592e-06,6.74305e-08,0.693812,0.00101436,-1.35691e-06,-7.97709e-08,0.694825,0.0010114,-1.59622e-06,7.28391e-08,0.695835,0.00100843,-1.37771e-06,-3.27715e-08,0.696842,0.00100558,-1.47602e-06,-1.35807e-09,0.697846,0.00100262,-1.48009e-06,3.82037e-08,0.698847,0.000999775,-1.36548e-06,-3.22474e-08,0.699846,0.000996948,-1.46223e-06,3.11809e-08,0.700841,0.000994117,-1.36868e-06,-3.28714e-08,0.701834,0.000991281,-1.4673e-06,4.07001e-08,0.702824,0.000988468,-1.3452e-06,-1.07197e-08,0.703811,0.000985746,-1.37736e-06,2.17866e-09,0.704795,0.000982998,-1.37082e-06,2.00521e-09,0.705777,0.000980262,-1.3648e-06,-1.01996e-08,0.706756,0.000977502,-1.3954e-06,3.87931e-08,0.707732,0.000974827,-1.27902e-06,-2.57632e-08,0.708706,0.000972192,-1.35631e-06,4.65513e-09,0.709676,0.000969493,-1.34235e-06,7.14257e-09,0.710645,0.00096683,-1.32092e-06,2.63791e-08,0.71161,0.000964267,-1.24178e-06,-5.30543e-08,0.712573,0.000961625,-1.40095e-06,6.66289e-08,0.713533,0.000959023,-1.20106e-06,-3.46474e-08,0.714491,0.000956517,-1.305e-06,1.23559e-08,0.715446,0.000953944,-1.26793e-06,-1.47763e-08,0.716399,0.000951364,-1.31226e-06,4.67494e-08,0.717349,0.000948879,-1.17201e-06,-5.3012e-08,0.718297,0.000946376,-1.33105e-06,4.60894e-08,0.719242,0.000943852,-1.19278e-06,-1.21366e-08,0.720185,0.00094143,-1.22919e-06,2.45673e-09,0.721125,0.000938979,-1.22182e-06,2.30966e-09,0.722063,0.000936543,-1.21489e-06,-1.16954e-08,0.722998,0.000934078,-1.24998e-06,4.44718e-08,0.723931,0.000931711,-1.11656e-06,-4.69823e-08,0.724861,0.000929337,-1.25751e-06,2.4248e-08,0.725789,0.000926895,-1.18477e-06,9.5949e-09,0.726715,0.000924554,-1.15598e-06,-3.02286e-09,0.727638,0.000922233,-1.16505e-06,2.49649e-09,0.72856,0.00091991,-1.15756e-06,-6.96321e-09,0.729478,0.000917575,-1.17845e-06,2.53564e-08,0.730395,0.000915294,-1.10238e-06,-3.48578e-08,0.731309,0.000912984,-1.20695e-06,5.44704e-08,0.732221,0.000910734,-1.04354e-06,-6.38144e-08,0.73313,0.000908455,-1.23499e-06,8.15781e-08,0.734038,0.00090623,-9.90253e-07,-8.3684e-08,0.734943,0.000903999,-1.2413e-06,7.43441e-08,0.735846,0.000901739,-1.01827e-06,-3.48787e-08,0.736746,0.000899598,-1.12291e-06,5.56596e-09,0.737645,0.000897369,-1.10621e-06,1.26148e-08,0.738541,0.000895194,-1.06837e-06,3.57935e-09,0.739435,0.000893068,-1.05763e-06,-2.69322e-08,0.740327,0.000890872,-1.13842e-06,4.45448e-08,0.741217,0.000888729,-1.00479e-06,-3.20376e-08,0.742105,0.000886623,-1.1009e-06,2.40011e-08,0.74299,0.000884493,-1.0289e-06,-4.36209e-09,0.743874,0.000882422,-1.04199e-06,-6.55268e-09,0.744755,0.000880319,-1.06164e-06,3.05728e-08,0.745634,0.000878287,-9.69926e-07,-5.61338e-08,0.746512,0.000876179,-1.13833e-06,7.4753e-08,0.747387,0.000874127,-9.14068e-07,-6.40644e-08,0.74826,0.000872106,-1.10626e-06,6.22955e-08,0.749131,0.000870081,-9.19375e-07,-6.59083e-08,0.75,0.000868044,-1.1171e-06,8.21284e-08,0.750867,0.000866056,-8.70714e-07,-8.37915e-08,0.751732,0.000864064,-1.12209e-06,7.42237e-08,0.752595,0.000862042,-8.99418e-07,-3.42894e-08,0.753456,0.00086014,-1.00229e-06,3.32955e-09,0.754315,0.000858146,-9.92297e-07,2.09712e-08,0.755173,0.000856224,-9.29384e-07,-2.76096e-08,0.756028,0.000854282,-1.01221e-06,2.98627e-08,0.756881,0.000852348,-9.22625e-07,-3.22365e-08,0.757733,0.000850406,-1.01933e-06,3.94786e-08,0.758582,0.000848485,-9.00898e-07,-6.46833e-09,0.75943,0.000846664,-9.20303e-07,-1.36052e-08,0.760275,0.000844783,-9.61119e-07,1.28447e-09,0.761119,0.000842864,-9.57266e-07,8.4674e-09,0.761961,0.000840975,-9.31864e-07,2.44506e-08,0.762801,0.000839185,-8.58512e-07,-4.6665e-08,0.763639,0.000837328,-9.98507e-07,4.30001e-08,0.764476,0.00083546,-8.69507e-07,-6.12609e-09,0.76531,0.000833703,-8.87885e-07,-1.84959e-08,0.766143,0.000831871,-9.43372e-07,2.05052e-08,0.766974,0.000830046,-8.81857e-07,-3.92026e-09,0.767803,0.000828271,-8.93618e-07,-4.82426e-09,0.768631,0.000826469,-9.0809e-07,2.32172e-08,0.769456,0.000824722,-8.38439e-07,-2.84401e-08,0.77028,0.00082296,-9.23759e-07,3.09386e-08,0.771102,0.000821205,-8.30943e-07,-3.57099e-08,0.771922,0.000819436,-9.38073e-07,5.22963e-08,0.772741,0.000817717,-7.81184e-07,-5.42658e-08,0.773558,0.000815992,-9.43981e-07,4.55579e-08,0.774373,0.000814241,-8.07308e-07,-8.75656e-09,0.775186,0.0008126,-8.33578e-07,-1.05315e-08,0.775998,0.000810901,-8.65172e-07,-8.72188e-09,0.776808,0.000809145,-8.91338e-07,4.54191e-08,0.777616,0.000807498,-7.5508e-07,-5.37454e-08,0.778423,0.000805827,-9.16317e-07,5.03532e-08,0.779228,0.000804145,-7.65257e-07,-2.84584e-08,0.780031,0.000802529,-8.50632e-07,3.87579e-09,0.780833,0.00080084,-8.39005e-07,1.29552e-08,0.781633,0.0007992,-8.00139e-07,3.90804e-09,0.782432,0.000797612,-7.88415e-07,-2.85874e-08,0.783228,0.000795949,-8.74177e-07,5.0837e-08,0.784023,0.000794353,-7.21666e-07,-5.55513e-08,0.784817,0.000792743,-8.8832e-07,5.21587e-08,0.785609,0.000791123,-7.31844e-07,-3.38744e-08,0.786399,0.000789558,-8.33467e-07,2.37342e-08,0.787188,0.000787962,-7.62264e-07,-1.45775e-09,0.787975,0.000786433,-7.66638e-07,-1.79034e-08,0.788761,0.000784846,-8.20348e-07,1.34665e-08,0.789545,0.000783246,-7.79948e-07,2.3642e-08,0.790327,0.000781757,-7.09022e-07,-4.84297e-08,0.791108,0.000780194,-8.54311e-07,5.08674e-08,0.791888,0.000778638,-7.01709e-07,-3.58303e-08,0.792666,0.000777127,-8.092e-07,3.28493e-08,0.793442,0.000775607,-7.10652e-07,-3.59624e-08,0.794217,0.000774078,-8.1854e-07,5.13959e-08,0.79499,0.000772595,-6.64352e-07,-5.04121e-08,0.795762,0.000771115,-8.15588e-07,3.10431e-08,0.796532,0.000769577,-7.22459e-07,-1.41557e-08,0.797301,0.00076809,-7.64926e-07,2.55795e-08,0.798069,0.000766636,-6.88187e-07,-2.85578e-08,0.798835,0.000765174,-7.73861e-07,2.90472e-08,0.799599,0.000763714,-6.86719e-07,-2.80262e-08,0.800362,0.000762256,-7.70798e-07,2.34531e-08,0.801123,0.000760785,-7.00438e-07,-6.18144e-09,0.801884,0.000759366,-7.18983e-07,1.27263e-09,0.802642,0.000757931,-7.15165e-07,1.09101e-09,0.803399,0.000756504,-7.11892e-07,-5.63675e-09,0.804155,0.000755064,-7.28802e-07,2.14559e-08,0.80491,0.00075367,-6.64434e-07,-2.05821e-08,0.805663,0.00075228,-7.26181e-07,1.26812e-09,0.806414,0.000750831,-7.22377e-07,1.55097e-08,0.807164,0.000749433,-6.75848e-07,-3.70216e-09,0.807913,0.00074807,-6.86954e-07,-7.0105e-10,0.80866,0.000746694,-6.89057e-07,6.5063e-09,0.809406,0.000745336,-6.69538e-07,-2.53242e-08,0.810151,0.000743921,-7.45511e-07,3.51858e-08,0.810894,0.000742535,-6.39953e-07,3.79034e-09,0.811636,0.000741267,-6.28582e-07,-5.03471e-08,0.812377,0.000739858,-7.79624e-07,7.83886e-08,0.813116,0.000738534,-5.44458e-07,-8.43935e-08,0.813854,0.000737192,-7.97638e-07,8.03714e-08,0.81459,0.000735838,-5.56524e-07,-5.82784e-08,0.815325,0.00073455,-7.31359e-07,3.35329e-08,0.816059,0.000733188,-6.3076e-07,-1.62486e-08,0.816792,0.000731878,-6.79506e-07,3.14614e-08,0.817523,0.000730613,-5.85122e-07,-4.99925e-08,0.818253,0.000729293,-7.35099e-07,4.92994e-08,0.818982,0.000727971,-5.87201e-07,-2.79959e-08,0.819709,0.000726712,-6.71189e-07,3.07959e-09,0.820435,0.000725379,-6.6195e-07,1.56777e-08,0.82116,0.000724102,-6.14917e-07,-6.18564e-09,0.821883,0.000722854,-6.33474e-07,9.06488e-09,0.822606,0.000721614,-6.06279e-07,-3.00739e-08,0.823327,0.000720311,-6.96501e-07,5.16262e-08,0.824046,0.000719073,-5.41623e-07,-5.72214e-08,0.824765,0.000717818,-7.13287e-07,5.80503e-08,0.825482,0.000716566,-5.39136e-07,-5.57703e-08,0.826198,0.00071532,-7.06447e-07,4.58215e-08,0.826912,0.000714045,-5.68983e-07,-8.30636e-09,0.827626,0.000712882,-5.93902e-07,-1.25961e-08,0.828338,0.000711656,-6.3169e-07,-9.13985e-10,0.829049,0.00071039,-6.34432e-07,1.62519e-08,0.829759,0.00070917,-5.85676e-07,-4.48904e-09,0.830468,0.000707985,-5.99143e-07,1.70418e-09,0.831175,0.000706792,-5.9403e-07,-2.32768e-09,0.831881,0.000705597,-6.01014e-07,7.60648e-09,0.832586,0.000704418,-5.78194e-07,-2.80982e-08,0.83329,0.000703177,-6.62489e-07,4.51817e-08,0.833993,0.000701988,-5.26944e-07,-3.34192e-08,0.834694,0.000700834,-6.27201e-07,2.88904e-08,0.835394,0.000699666,-5.4053e-07,-2.25378e-08,0.836093,0.000698517,-6.08143e-07,1.65589e-09,0.836791,0.000697306,-6.03176e-07,1.59142e-08,0.837488,0.000696147,-5.55433e-07,-5.70801e-09,0.838184,0.000695019,-5.72557e-07,6.91792e-09,0.838878,0.000693895,-5.51803e-07,-2.19637e-08,0.839571,0.000692725,-6.17694e-07,2.13321e-08,0.840263,0.000691554,-5.53698e-07,-3.75996e-09,0.840954,0.000690435,-5.64978e-07,-6.29219e-09,0.841644,0.000689287,-5.83855e-07,2.89287e-08,0.842333,0.000688206,-4.97068e-07,-4.98181e-08,0.843021,0.000687062,-6.46523e-07,5.11344e-08,0.843707,0.000685922,-4.9312e-07,-3.55102e-08,0.844393,0.00068483,-5.9965e-07,3.13019e-08,0.845077,0.000683724,-5.05745e-07,-3.00925e-08,0.84576,0.000682622,-5.96022e-07,2.94636e-08,0.846442,0.000681519,-5.07631e-07,-2.81572e-08,0.847123,0.000680419,-5.92103e-07,2.35606e-08,0.847803,0.000679306,-5.21421e-07,-6.48045e-09,0.848482,0.000678243,-5.40863e-07,2.36124e-09,0.849159,0.000677169,-5.33779e-07,-2.96461e-09,0.849836,0.000676092,-5.42673e-07,9.49728e-09,0.850512,0.000675035,-5.14181e-07,-3.50245e-08,0.851186,0.000673902,-6.19254e-07,7.09959e-08,0.851859,0.000672876,-4.06267e-07,-7.01453e-08,0.852532,0.000671853,-6.16703e-07,3.07714e-08,0.853203,0.000670712,-5.24388e-07,6.66423e-09,0.853873,0.000669684,-5.04396e-07,2.17629e-09,0.854542,0.000668681,-4.97867e-07,-1.53693e-08,0.855211,0.000667639,-5.43975e-07,-3.03752e-10,0.855878,0.000666551,-5.44886e-07,1.65844e-08,0.856544,0.000665511,-4.95133e-07,-6.42907e-09,0.857209,0.000664501,-5.1442e-07,9.13195e-09,0.857873,0.0006635,-4.87024e-07,-3.00987e-08,0.858536,0.000662435,-5.7732e-07,5.16584e-08,0.859198,0.000661436,-4.22345e-07,-5.73255e-08,0.859859,0.000660419,-5.94322e-07,5.84343e-08,0.860518,0.000659406,-4.19019e-07,-5.72022e-08,0.861177,0.000658396,-5.90626e-07,5.11653e-08,0.861835,0.000657368,-4.3713e-07,-2.82495e-08,0.862492,0.000656409,-5.21878e-07,2.22788e-09,0.863148,0.000655372,-5.15195e-07,1.9338e-08,0.863803,0.0006544,-4.5718e-07,-1.99754e-08,0.864457,0.000653425,-5.17107e-07,9.59024e-10,0.86511,0.000652394,-5.1423e-07,1.61393e-08,0.865762,0.000651414,-4.65812e-07,-5.91149e-09,0.866413,0.000650465,-4.83546e-07,7.50665e-09,0.867063,0.00064952,-4.61026e-07,-2.4115e-08,0.867712,0.000648526,-5.33371e-07,2.93486e-08,0.86836,0.000647547,-4.45325e-07,-3.36748e-08,0.869007,0.000646555,-5.4635e-07,4.57461e-08,0.869653,0.0006456,-4.09112e-07,-3.01002e-08,0.870298,0.000644691,-4.99412e-07,1.50501e-08,0.870942,0.000643738,-4.54262e-07,-3.01002e-08,0.871585,0.000642739,-5.44563e-07,4.57461e-08,0.872228,0.000641787,-4.07324e-07,-3.36748e-08,0.872869,0.000640871,-5.08349e-07,2.93486e-08,0.873509,0.000639943,-4.20303e-07,-2.4115e-08,0.874149,0.00063903,-4.92648e-07,7.50655e-09,0.874787,0.000638067,-4.70128e-07,-5.91126e-09,0.875425,0.000637109,-4.87862e-07,1.61385e-08,0.876062,0.000636182,-4.39447e-07,9.61961e-10,0.876697,0.000635306,-4.36561e-07,-1.99863e-08,0.877332,0.000634373,-4.9652e-07,1.93785e-08,0.877966,0.000633438,-4.38384e-07,2.07697e-09,0.878599,0.000632567,-4.32153e-07,-2.76864e-08,0.879231,0.00063162,-5.15212e-07,4.90641e-08,0.879862,0.000630737,-3.6802e-07,-4.93606e-08,0.880493,0.000629852,-5.16102e-07,2.9169e-08,0.881122,0.000628908,-4.28595e-07,-7.71083e-09,0.881751,0.000628027,-4.51727e-07,1.6744e-09,0.882378,0.000627129,-4.46704e-07,1.01317e-09,0.883005,0.000626239,-4.43665e-07,-5.72703e-09,0.883631,0.000625334,-4.60846e-07,2.1895e-08,0.884255,0.000624478,-3.95161e-07,-2.22481e-08,0.88488,0.000623621,-4.61905e-07,7.4928e-09,0.885503,0.00062272,-4.39427e-07,-7.72306e-09,0.886125,0.000621818,-4.62596e-07,2.33995e-08,0.886746,0.000620963,-3.92398e-07,-2.62704e-08,0.887367,0.000620099,-4.71209e-07,2.20775e-08,0.887987,0.000619223,-4.04976e-07,-2.43496e-09,0.888605,0.000618406,-4.12281e-07,-1.23377e-08,0.889223,0.000617544,-4.49294e-07,-7.81876e-09,0.88984,0.000616622,-4.72751e-07,4.36128e-08,0.890457,0.000615807,-3.41912e-07,-4.7423e-08,0.891072,0.000614981,-4.84181e-07,2.68698e-08,0.891687,0.000614093,-4.03572e-07,-4.51384e-10,0.8923,0.000613285,-4.04926e-07,-2.50643e-08,0.892913,0.0006124,-4.80119e-07,4.11038e-08,0.893525,0.000611563,-3.56808e-07,-2.01414e-08,0.894136,0.000610789,-4.17232e-07,-2.01426e-08,0.894747,0.000609894,-4.7766e-07,4.11073e-08,0.895356,0.000609062,-3.54338e-07,-2.50773e-08,0.895965,0.000608278,-4.2957e-07,-4.02954e-10,0.896573,0.000607418,-4.30779e-07,2.66891e-08,0.89718,0.000606636,-3.50711e-07,-4.67489e-08,0.897786,0.000605795,-4.90958e-07,4.10972e-08,0.898391,0.000604936,-3.67666e-07,1.56948e-09,0.898996,0.000604205,-3.62958e-07,-4.73751e-08,0.8996,0.000603337,-5.05083e-07,6.87214e-08,0.900202,0.000602533,-2.98919e-07,-4.86966e-08,0.900805,0.000601789,-4.45009e-07,6.85589e-09,0.901406,0.00060092,-4.24441e-07,2.1273e-08,0.902007,0.000600135,-3.60622e-07,-3.23434e-08,0.902606,0.000599317,-4.57652e-07,4.84959e-08,0.903205,0.000598547,-3.12164e-07,-4.24309e-08,0.903803,0.000597795,-4.39457e-07,2.01844e-09,0.904401,0.000596922,-4.33402e-07,3.43571e-08,0.904997,0.000596159,-3.30331e-07,-2.02374e-08,0.905593,0.000595437,-3.91043e-07,-1.30123e-08,0.906188,0.000594616,-4.3008e-07,1.26819e-08,0.906782,0.000593794,-3.92034e-07,2.18894e-08,0.907376,0.000593076,-3.26366e-07,-4.06349e-08,0.907968,0.000592301,-4.4827e-07,2.1441e-08,0.90856,0.000591469,-3.83947e-07,1.44754e-08,0.909151,0.000590744,-3.40521e-07,-1.97379e-08,0.909742,0.000590004,-3.99735e-07,4.87161e-09,0.910331,0.000589219,-3.8512e-07,2.51532e-10,0.91092,0.00058845,-3.84366e-07,-5.87776e-09,0.911508,0.000587663,-4.01999e-07,2.32595e-08,0.912096,0.000586929,-3.3222e-07,-2.75554e-08,0.912682,0.000586182,-4.14887e-07,2.73573e-08,0.913268,0.000585434,-3.32815e-07,-2.22692e-08,0.913853,0.000584702,-3.99622e-07,2.11486e-09,0.914437,0.000583909,-3.93278e-07,1.38098e-08,0.915021,0.000583164,-3.51848e-07,2.25042e-09,0.915604,0.000582467,-3.45097e-07,-2.28115e-08,0.916186,0.000581708,-4.13531e-07,2.93911e-08,0.916767,0.000580969,-3.25358e-07,-3.51481e-08,0.917348,0.000580213,-4.30803e-07,5.15967e-08,0.917928,0.000579506,-2.76012e-07,-5.20296e-08,0.918507,0.000578798,-4.32101e-07,3.73124e-08,0.919085,0.000578046,-3.20164e-07,-3.76154e-08,0.919663,0.000577293,-4.3301e-07,5.35447e-08,0.92024,0.000576587,-2.72376e-07,-5.7354e-08,0.920816,0.000575871,-4.44438e-07,5.66621e-08,0.921391,0.000575152,-2.74452e-07,-5.00851e-08,0.921966,0.000574453,-4.24707e-07,2.4469e-08,0.92254,0.000573677,-3.513e-07,1.18138e-08,0.923114,0.000573009,-3.15859e-07,-1.21195e-08,0.923686,0.000572341,-3.52217e-07,-2.29403e-08,0.924258,0.000571568,-4.21038e-07,4.4276e-08,0.924829,0.000570859,-2.8821e-07,-3.49546e-08,0.9254,0.000570178,-3.93074e-07,3.59377e-08,0.92597,0.000569499,-2.85261e-07,-4.91915e-08,0.926539,0.000568781,-4.32835e-07,4.16189e-08,0.927107,0.00056804,-3.07979e-07,1.92523e-09,0.927675,0.00056743,-3.02203e-07,-4.93198e-08,0.928242,0.000566678,-4.50162e-07,7.61447e-08,0.928809,0.000566006,-2.21728e-07,-7.6445e-08,0.929374,0.000565333,-4.51063e-07,5.08216e-08,0.929939,0.000564583,-2.98599e-07,-7.63212e-09,0.930503,0.000563963,-3.21495e-07,-2.02931e-08,0.931067,0.000563259,-3.82374e-07,2.92001e-08,0.93163,0.000562582,-2.94774e-07,-3.69025e-08,0.932192,0.000561882,-4.05482e-07,5.88053e-08,0.932754,0.000561247,-2.29066e-07,-7.91094e-08,0.933315,0.000560552,-4.66394e-07,7.88184e-08,0.933875,0.000559856,-2.29939e-07,-5.73501e-08,0.934434,0.000559224,-4.01989e-07,3.13727e-08,0.934993,0.000558514,-3.07871e-07,-8.53611e-09,0.935551,0.000557873,-3.33479e-07,2.77175e-09,0.936109,0.000557214,-3.25164e-07,-2.55091e-09,0.936666,0.000556556,-3.32817e-07,7.43188e-09,0.937222,0.000555913,-3.10521e-07,-2.71766e-08,0.937778,0.00055521,-3.92051e-07,4.167e-08,0.938333,0.000554551,-2.67041e-07,-2.02941e-08,0.938887,0.000553956,-3.27923e-07,-2.00984e-08,0.93944,0.00055324,-3.88218e-07,4.10828e-08,0.939993,0.000552587,-2.6497e-07,-2.50237e-08,0.940546,0.000551982,-3.40041e-07,-5.92583e-10,0.941097,0.0005513,-3.41819e-07,2.7394e-08,0.941648,0.000550698,-2.59637e-07,-4.93788e-08,0.942199,0.000550031,-4.07773e-07,5.09119e-08,0.942748,0.000549368,-2.55038e-07,-3.50595e-08,0.943297,0.000548753,-3.60216e-07,2.97214e-08,0.943846,0.000548122,-2.71052e-07,-2.42215e-08,0.944394,0.000547507,-3.43716e-07,7.55985e-09,0.944941,0.000546842,-3.21037e-07,-6.01796e-09,0.945487,0.000546182,-3.3909e-07,1.65119e-08,0.946033,0.000545553,-2.89555e-07,-4.2498e-10,0.946578,0.000544973,-2.9083e-07,-1.4812e-08,0.947123,0.000544347,-3.35266e-07,6.83068e-11,0.947667,0.000543676,-3.35061e-07,1.45388e-08,0.94821,0.00054305,-2.91444e-07,1.38123e-09,0.948753,0.000542471,-2.87301e-07,-2.00637e-08,0.949295,0.000541836,-3.47492e-07,1.92688e-08,0.949837,0.000541199,-2.89685e-07,2.59298e-09,0.950378,0.000540628,-2.81906e-07,-2.96407e-08,0.950918,0.000539975,-3.70829e-07,5.63652e-08,0.951458,0.000539402,-2.01733e-07,-7.66107e-08,0.951997,0.000538769,-4.31565e-07,7.12638e-08,0.952535,0.00053812,-2.17774e-07,-2.96305e-08,0.953073,0.000537595,-3.06665e-07,-1.23464e-08,0.95361,0.000536945,-3.43704e-07,1.94114e-08,0.954147,0.000536316,-2.8547e-07,-5.69451e-09,0.954683,0.000535728,-3.02554e-07,3.36666e-09,0.955219,0.000535133,-2.92454e-07,-7.77208e-09,0.955753,0.000534525,-3.1577e-07,2.77216e-08,0.956288,0.000533976,-2.32605e-07,-4.35097e-08,0.956821,0.00053338,-3.63134e-07,2.7108e-08,0.957354,0.000532735,-2.8181e-07,-5.31772e-09,0.957887,0.000532156,-2.97764e-07,-5.83718e-09,0.958419,0.000531543,-3.15275e-07,2.86664e-08,0.95895,0.000530998,-2.29276e-07,-4.9224e-08,0.959481,0.000530392,-3.76948e-07,4.90201e-08,0.960011,0.000529785,-2.29887e-07,-2.76471e-08,0.96054,0.000529243,-3.12829e-07,1.96385e-09,0.961069,0.000528623,-3.06937e-07,1.97917e-08,0.961598,0.000528068,-2.47562e-07,-2.15261e-08,0.962125,0.000527508,-3.1214e-07,6.70795e-09,0.962653,0.000526904,-2.92016e-07,-5.30573e-09,0.963179,0.000526304,-3.07934e-07,1.4515e-08,0.963705,0.000525732,-2.64389e-07,6.85048e-09,0.964231,0.000525224,-2.43837e-07,-4.19169e-08,0.964756,0.00052461,-3.69588e-07,4.1608e-08,0.96528,0.000523996,-2.44764e-07,-5.30598e-09,0.965804,0.000523491,-2.60682e-07,-2.03841e-08,0.966327,0.000522908,-3.21834e-07,2.72378e-08,0.966849,0.000522346,-2.40121e-07,-2.89625e-08,0.967371,0.000521779,-3.27008e-07,2.90075e-08,0.967893,0.000521212,-2.39986e-07,-2.74629e-08,0.968414,0.00052065,-3.22374e-07,2.12396e-08,0.968934,0.000520069,-2.58656e-07,2.10922e-09,0.969454,0.000519558,-2.52328e-07,-2.96765e-08,0.969973,0.000518964,-3.41357e-07,5.6992e-08,0.970492,0.000518452,-1.70382e-07,-7.90821e-08,0.97101,0.000517874,-4.07628e-07,8.05224e-08,0.971528,0.000517301,-1.66061e-07,-6.41937e-08,0.972045,0.000516776,-3.58642e-07,5.70429e-08,0.972561,0.00051623,-1.87513e-07,-4.47686e-08,0.973077,0.00051572,-3.21819e-07,2.82237e-09,0.973593,0.000515085,-3.13352e-07,3.34792e-08,0.974108,0.000514559,-2.12914e-07,-1.75298e-08,0.974622,0.000514081,-2.65503e-07,-2.29648e-08,0.975136,0.000513481,-3.34398e-07,4.97843e-08,0.975649,0.000512961,-1.85045e-07,-5.6963e-08,0.976162,0.00051242,-3.55934e-07,5.88585e-08,0.976674,0.000511885,-1.79359e-07,-5.92616e-08,0.977185,0.000511348,-3.57143e-07,5.89785e-08,0.977696,0.000510811,-1.80208e-07,-5.74433e-08,0.978207,0.000510278,-3.52538e-07,5.15854e-08,0.978717,0.000509728,-1.97781e-07,-2.9689e-08,0.979226,0.000509243,-2.86848e-07,7.56591e-09,0.979735,0.000508692,-2.64151e-07,-5.74649e-10,0.980244,0.000508162,-2.65875e-07,-5.26732e-09,0.980752,0.000507615,-2.81677e-07,2.16439e-08,0.981259,0.000507116,-2.16745e-07,-2.17037e-08,0.981766,0.000506618,-2.81856e-07,5.56636e-09,0.982272,0.000506071,-2.65157e-07,-5.61689e-10,0.982778,0.000505539,-2.66842e-07,-3.31963e-09,0.983283,0.000504995,-2.76801e-07,1.38402e-08,0.983788,0.000504483,-2.3528e-07,7.56339e-09,0.984292,0.000504035,-2.1259e-07,-4.40938e-08,0.984796,0.000503478,-3.44871e-07,4.96026e-08,0.985299,0.000502937,-1.96064e-07,-3.51071e-08,0.985802,0.000502439,-3.01385e-07,3.12212e-08,0.986304,0.00050193,-2.07721e-07,-3.0173e-08,0.986806,0.000501424,-2.9824e-07,2.9866e-08,0.987307,0.000500917,-2.08642e-07,-2.96865e-08,0.987808,0.000500411,-2.97702e-07,2.92753e-08,0.988308,0.000499903,-2.09876e-07,-2.78101e-08,0.988807,0.0004994,-2.93306e-07,2.23604e-08,0.989307,0.000498881,-2.26225e-07,-2.02681e-09,0.989805,0.000498422,-2.32305e-07,-1.42531e-08,0.990303,0.000497915,-2.75065e-07,-5.65232e-10,0.990801,0.000497363,-2.76761e-07,1.65141e-08,0.991298,0.000496859,-2.27218e-07,-5.88639e-09,0.991795,0.000496387,-2.44878e-07,7.0315e-09,0.992291,0.000495918,-2.23783e-07,-2.22396e-08,0.992787,0.000495404,-2.90502e-07,2.23224e-08,0.993282,0.00049489,-2.23535e-07,-7.44543e-09,0.993776,0.000494421,-2.45871e-07,7.45924e-09,0.994271,0.000493951,-2.23493e-07,-2.23915e-08,0.994764,0.000493437,-2.90668e-07,2.25021e-08,0.995257,0.000492923,-2.23161e-07,-8.01218e-09,0.99575,0.000492453,-2.47198e-07,9.54669e-09,0.996242,0.000491987,-2.18558e-07,-3.01746e-08,0.996734,0.000491459,-3.09082e-07,5.1547e-08,0.997225,0.000490996,-1.54441e-07,-5.68039e-08,0.997716,0.000490517,-3.24853e-07,5.64594e-08,0.998206,0.000490036,-1.55474e-07,-4.98245e-08,0.998696,0.000489576,-3.04948e-07,2.36292e-08,0.999186,0.000489037,-2.3406e-07,1.49121e-08,0.999674,0.000488613,-1.89324e-07,-2.3673e-08,1.00016,0.000488164,-2.60343e-07,2.01754e-08,1.00065,0.000487704,-1.99816e-07,-5.70288e-08,1.00114,0.000487133,-3.70903e-07,8.87303e-08,1.00162,0.000486657,-1.04712e-07,-5.94737e-08,1.00211,0.000486269,-2.83133e-07,2.99553e-08,1.0026,0.000485793,-1.93267e-07,-6.03474e-08,1.00308,0.000485225,-3.74309e-07,9.2225e-08,1.00357,0.000484754,-9.76345e-08,-7.0134e-08,1.00405,0.000484348,-3.08036e-07,6.91016e-08,1.00454,0.000483939,-1.00731e-07,-8.70633e-08,1.00502,0.000483476,-3.61921e-07,4.07328e-08,1.0055,0.000482875,-2.39723e-07,4.33413e-08,1.00599,0.000482525,-1.09699e-07,-9.48886e-08,1.00647,0.000482021,-3.94365e-07,9.77947e-08,1.00695,0.000481526,-1.00981e-07,-5.78713e-08,1.00743,0.00048115,-2.74595e-07,1.44814e-08,1.00791,0.000480645,-2.31151e-07,-5.42665e-11,1.00839,0.000480182,-2.31314e-07,-1.42643e-08,1.00887,0.000479677,-2.74106e-07,5.71115e-08,1.00935,0.0004793,-1.02772e-07,-9.49724e-08,1.00983,0.000478809,-3.87689e-07,8.43596e-08,1.01031,0.000478287,-1.3461e-07,-4.04755e-09,1.01079,0.000478006,-1.46753e-07,-6.81694e-08,1.01127,0.000477508,-3.51261e-07,3.83067e-08,1.01174,0.00047692,-2.36341e-07,3.41521e-08,1.01222,0.00047655,-1.33885e-07,-5.57058e-08,1.0127,0.000476115,-3.01002e-07,6.94616e-08,1.01317,0.000475721,-9.26174e-08,-1.02931e-07,1.01365,0.000475227,-4.01412e-07,1.03846e-07,1.01412,0.000474736,-8.98751e-08,-7.40321e-08,1.0146,0.000474334,-3.11971e-07,7.30735e-08,1.01507,0.00047393,-9.27508e-08,-9.90527e-08,1.01554,0.000473447,-3.89909e-07,8.47188e-08,1.01602,0.000472921,-1.35753e-07,-1.40381e-09,1.01649,0.000472645,-1.39964e-07,-7.91035e-08,1.01696,0.000472128,-3.77275e-07,7.93993e-08,1.01744,0.000471612,-1.39077e-07,-7.52607e-11,1.01791,0.000471334,-1.39302e-07,-7.90983e-08,1.01838,0.000470818,-3.76597e-07,7.80499e-08,1.01885,0.000470299,-1.42448e-07,5.31733e-09,1.01932,0.00047003,-1.26496e-07,-9.93193e-08,1.01979,0.000469479,-4.24453e-07,1.53541e-07,1.02026,0.00046909,3.617e-08,-1.57217e-07,1.02073,0.000468691,-4.35482e-07,1.177e-07,1.02119,0.000468173,-8.23808e-08,-7.51659e-08,1.02166,0.000467783,-3.07878e-07,6.37538e-08,1.02213,0.000467358,-1.16617e-07,-6.064e-08,1.0226,0.000466943,-2.98537e-07,5.9597e-08,1.02306,0.000466525,-1.19746e-07,-5.85386e-08,1.02353,0.00046611,-2.95362e-07,5.53482e-08,1.024,0.000465685,-1.29317e-07,-4.36449e-08,1.02446,0.000465296,-2.60252e-07,2.20268e-11,1.02493,0.000464775,-2.60186e-07,4.35568e-08,1.02539,0.000464386,-1.29516e-07,-5.50398e-08,1.02586,0.000463961,-2.94635e-07,5.73932e-08,1.02632,0.000463544,-1.22456e-07,-5.53236e-08,1.02678,0.000463133,-2.88426e-07,4.46921e-08,1.02725,0.000462691,-1.5435e-07,-4.23534e-09,1.02771,0.000462369,-1.67056e-07,-2.77507e-08,1.02817,0.000461952,-2.50308e-07,-3.97101e-09,1.02863,0.000461439,-2.62221e-07,4.36348e-08,1.02909,0.000461046,-1.31317e-07,-5.13589e-08,1.02955,0.000460629,-2.85394e-07,4.25913e-08,1.03001,0.000460186,-1.5762e-07,2.0285e-10,1.03047,0.000459871,-1.57011e-07,-4.34027e-08,1.03093,0.000459427,-2.87219e-07,5.41987e-08,1.03139,0.000459015,-1.24623e-07,-5.4183e-08,1.03185,0.000458604,-2.87172e-07,4.33239e-08,1.03231,0.000458159,-1.572e-07,9.65817e-11,1.03277,0.000457845,-1.56911e-07,-4.37103e-08,1.03323,0.0004574,-2.88041e-07,5.55351e-08,1.03368,0.000456991,-1.21436e-07,-5.9221e-08,1.03414,0.00045657,-2.99099e-07,6.21394e-08,1.0346,0.000456158,-1.1268e-07,-7.01275e-08,1.03505,0.000455723,-3.23063e-07,9.91614e-08,1.03551,0.000455374,-2.55788e-08,-8.80996e-08,1.03596,0.000455058,-2.89878e-07,1.48184e-08,1.03642,0.000454523,-2.45422e-07,2.88258e-08,1.03687,0.000454119,-1.58945e-07,-1.09125e-08,1.03733,0.000453768,-1.91682e-07,1.48241e-08,1.03778,0.000453429,-1.4721e-07,-4.83838e-08,1.03823,0.00045299,-2.92361e-07,5.95019e-08,1.03869,0.000452584,-1.13856e-07,-7.04146e-08,1.03914,0.000452145,-3.25099e-07,1.02947e-07,1.03959,0.000451803,-1.62583e-08,-1.02955e-07,1.04004,0.000451462,-3.25123e-07,7.04544e-08,1.04049,0.000451023,-1.1376e-07,-5.96534e-08,1.04094,0.000450616,-2.9272e-07,4.89499e-08,1.04139,0.000450178,-1.45871e-07,-1.69369e-08,1.04184,0.000449835,-1.96681e-07,1.87977e-08,1.04229,0.000449498,-1.40288e-07,-5.82539e-08,1.04274,0.000449043,-3.1505e-07,9.50087e-08,1.04319,0.000448698,-3.00238e-08,-8.33623e-08,1.04364,0.000448388,-2.80111e-07,2.20363e-11,1.04409,0.000447828,-2.80045e-07,8.32742e-08,1.04454,0.000447517,-3.02221e-08,-9.47002e-08,1.04498,0.000447173,-3.14323e-07,5.7108e-08,1.04543,0.000446716,-1.42999e-07,-1.45225e-08,1.04588,0.000446386,-1.86566e-07,9.82022e-10,1.04632,0.000446016,-1.8362e-07,1.05944e-08,1.04677,0.00044568,-1.51837e-07,-4.33597e-08,1.04721,0.000445247,-2.81916e-07,4.36352e-08,1.04766,0.000444814,-1.51011e-07,-1.19717e-08,1.0481,0.000444476,-1.86926e-07,4.25158e-09,1.04855,0.000444115,-1.74171e-07,-5.03461e-09,1.04899,0.000443751,-1.89275e-07,1.58868e-08,1.04944,0.00044342,-1.41614e-07,-5.85127e-08,1.04988,0.000442961,-3.17152e-07,9.89548e-08,1.05032,0.000442624,-2.0288e-08,-9.88878e-08,1.05076,0.000442287,-3.16951e-07,5.81779e-08,1.05121,0.000441827,-1.42418e-07,-1.46144e-08,1.05165,0.000441499,-1.86261e-07,2.79892e-10,1.05209,0.000441127,-1.85421e-07,1.34949e-08,1.05253,0.000440797,-1.44937e-07,-5.42594e-08,1.05297,0.000440344,-3.07715e-07,8.43335e-08,1.05341,0.000439982,-5.47146e-08,-4.46558e-08,1.05385,0.000439738,-1.88682e-07,-2.49193e-08,1.05429,0.000439286,-2.6344e-07,2.5124e-08,1.05473,0.000438835,-1.88068e-07,4.36328e-08,1.05517,0.000438589,-5.71699e-08,-8.04459e-08,1.05561,0.000438234,-2.98508e-07,3.97324e-08,1.05605,0.000437756,-1.79311e-07,4.07258e-08,1.05648,0.000437519,-5.71332e-08,-8.34263e-08,1.05692,0.000437155,-3.07412e-07,5.45608e-08,1.05736,0.000436704,-1.4373e-07,-1.56078e-08,1.05779,0.000436369,-1.90553e-07,7.87043e-09,1.05823,0.000436012,-1.66942e-07,-1.58739e-08,1.05867,0.00043563,-2.14563e-07,5.56251e-08,1.0591,0.000435368,-4.76881e-08,-8.74172e-08,1.05954,0.000435011,-3.0994e-07,5.56251e-08,1.05997,0.000434558,-1.43064e-07,-1.58739e-08,1.06041,0.000434224,-1.90686e-07,7.87042e-09,1.06084,0.000433866,-1.67075e-07,-1.56078e-08,1.06127,0.000433485,-2.13898e-07,5.45609e-08,1.06171,0.000433221,-5.02157e-08,-8.34263e-08,1.06214,0.00043287,-3.00495e-07,4.07258e-08,1.06257,0.000432391,-1.78317e-07,3.97325e-08,1.063,0.000432154,-5.91198e-08,-8.04464e-08,1.06344,0.000431794,-3.00459e-07,4.36347e-08,1.06387,0.000431324,-1.69555e-07,2.5117e-08,1.0643,0.000431061,-9.42041e-08,-2.48934e-08,1.06473,0.000430798,-1.68884e-07,-4.47527e-08,1.06516,0.000430326,-3.03142e-07,8.46951e-08,1.06559,0.000429973,-4.90573e-08,-5.56089e-08,1.06602,0.000429708,-2.15884e-07,1.85314e-08,1.06645,0.000429332,-1.6029e-07,-1.85166e-08,1.06688,0.000428956,-2.1584e-07,5.5535e-08,1.06731,0.000428691,-4.92347e-08,-8.44142e-08,1.06774,0.000428339,-3.02477e-07,4.37032e-08,1.06816,0.000427865,-1.71368e-07,2.88107e-08,1.06859,0.000427609,-8.49356e-08,-3.97367e-08,1.06902,0.00042732,-2.04146e-07,1.09267e-08,1.06945,0.000426945,-1.71365e-07,-3.97023e-09,1.06987,0.00042659,-1.83276e-07,4.9542e-09,1.0703,0.000426238,-1.68414e-07,-1.58466e-08,1.07073,0.000425854,-2.15953e-07,5.84321e-08,1.07115,0.000425597,-4.0657e-08,-9.86725e-08,1.07158,0.00042522,-3.36674e-07,9.78392e-08,1.072,0.00042484,-4.31568e-08,-5.42658e-08,1.07243,0.000424591,-2.05954e-07,1.45377e-11,1.07285,0.000424179,-2.0591e-07,5.42076e-08,1.07328,0.00042393,-4.32877e-08,-9.76357e-08,1.0737,0.00042355,-3.36195e-07,9.79165e-08,1.07412,0.000423172,-4.24451e-08,-5.56118e-08,1.07455,0.00042292,-2.09281e-07,5.32143e-09,1.07497,0.000422518,-1.93316e-07,3.43261e-08,1.07539,0.000422234,-9.0338e-08,-2.34165e-08,1.07581,0.000421983,-1.60588e-07,-5.98692e-08,1.07623,0.000421482,-3.40195e-07,1.43684e-07,1.07666,0.000421233,9.08574e-08,-1.5724e-07,1.07708,0.000420943,-3.80862e-07,1.27647e-07,1.0775,0.000420564,2.0791e-09,-1.1493e-07,1.07792,0.000420223,-3.4271e-07,9.36534e-08,1.07834,0.000419819,-6.17499e-08,-2.12653e-08,1.07876,0.000419632,-1.25546e-07,-8.59219e-09,1.07918,0.000419355,-1.51322e-07,-6.35752e-08,1.0796,0.000418861,-3.42048e-07,1.43684e-07,1.08002,0.000418608,8.90034e-08,-1.53532e-07,1.08043,0.000418326,-3.71593e-07,1.12817e-07,1.08085,0.000417921,-3.31414e-08,-5.93184e-08,1.08127,0.000417677,-2.11097e-07,5.24697e-09,1.08169,0.00041727,-1.95356e-07,3.83305e-08,1.0821,0.000416995,-8.03642e-08,-3.93597e-08,1.08252,0.000416716,-1.98443e-07,-1.0094e-10,1.08294,0.000416319,-1.98746e-07,3.97635e-08,1.08335,0.00041604,-7.94557e-08,-3.97437e-08,1.08377,0.000415762,-1.98687e-07,1.94215e-12,1.08419,0.000415365,-1.98681e-07,3.97359e-08,1.0846,0.000415087,-7.94732e-08,-3.97362e-08,1.08502,0.000414809,-1.98682e-07,-4.31063e-13,1.08543,0.000414411,-1.98683e-07,3.97379e-08,1.08584,0.000414133,-7.94694e-08,-3.97418e-08,1.08626,0.000413855,-1.98695e-07,2.00563e-11,1.08667,0.000413458,-1.98635e-07,3.96616e-08,1.08709,0.000413179,-7.965e-08,-3.9457e-08,1.0875,0.000412902,-1.98021e-07,-1.04281e-09,1.08791,0.000412502,-2.01149e-07,4.36282e-08,1.08832,0.000412231,-7.02648e-08,-5.42608e-08,1.08874,0.000411928,-2.33047e-07,5.42057e-08,1.08915,0.000411624,-7.04301e-08,-4.33527e-08,1.08956,0.000411353,-2.00488e-07,-4.07378e-12,1.08997,0.000410952,-2.005e-07,4.3369e-08,1.09038,0.000410681,-7.03934e-08,-5.42627e-08,1.09079,0.000410378,-2.33182e-07,5.44726e-08,1.0912,0.000410075,-6.97637e-08,-4.44186e-08,1.09161,0.000409802,-2.03019e-07,3.99235e-09,1.09202,0.000409408,-1.91042e-07,2.84491e-08,1.09243,0.000409111,-1.05695e-07,1.42043e-09,1.09284,0.000408904,-1.01434e-07,-3.41308e-08,1.09325,0.000408599,-2.03826e-07,1.58937e-08,1.09366,0.000408239,-1.56145e-07,-2.94438e-08,1.09406,0.000407838,-2.44476e-07,1.01881e-07,1.09447,0.000407655,6.11676e-08,-1.39663e-07,1.09488,0.000407358,-3.57822e-07,9.91432e-08,1.09529,0.00040694,-6.03921e-08,-1.84912e-08,1.09569,0.000406764,-1.15866e-07,-2.51785e-08,1.0961,0.000406457,-1.91401e-07,-4.03115e-12,1.09651,0.000406074,-1.91413e-07,2.51947e-08,1.09691,0.000405767,-1.15829e-07,1.84346e-08,1.09732,0.00040559,-6.05254e-08,-9.89332e-08,1.09772,0.000405172,-3.57325e-07,1.3888e-07,1.09813,0.000404874,5.93136e-08,-9.8957e-08,1.09853,0.000404696,-2.37557e-07,1.853e-08,1.09894,0.000404277,-1.81968e-07,2.48372e-08,1.09934,0.000403987,-1.07456e-07,1.33047e-09,1.09975,0.000403776,-1.03465e-07,-3.01591e-08,1.10015,0.000403479,-1.93942e-07,9.66054e-11,1.10055,0.000403091,-1.93652e-07,2.97727e-08,1.10096,0.000402793,-1.04334e-07,2.19273e-11,1.10136,0.000402585,-1.04268e-07,-2.98604e-08,1.10176,0.000402287,-1.93849e-07,2.10325e-10,1.10216,0.0004019,-1.93218e-07,2.90191e-08,1.10256,0.0004016,-1.06161e-07,2.92264e-09,1.10297,0.000401397,-9.73931e-08,-4.07096e-08,1.10337,0.00040108,-2.19522e-07,4.07067e-08,1.10377,0.000400763,-9.7402e-08,-2.90783e-09,1.10417,0.000400559,-1.06126e-07,-2.90754e-08,1.10457,0.00040026,-1.93352e-07,9.00021e-14,1.10497,0.000399873,-1.93351e-07,2.9075e-08,1.10537,0.000399574,-1.06126e-07,2.90902e-09,1.10577,0.00039937,-9.73992e-08,-4.07111e-08,1.10617,0.000399053,-2.19533e-07,4.07262e-08,1.10657,0.000398736,-9.73541e-08,-2.98424e-09,1.10697,0.000398533,-1.06307e-07,-2.87892e-08,1.10736,0.000398234,-1.92674e-07,-1.06824e-09,1.10776,0.000397845,-1.95879e-07,3.30622e-08,1.10816,0.000397552,-9.66926e-08,-1.19712e-08,1.10856,0.000397323,-1.32606e-07,1.48225e-08,1.10895,0.000397102,-8.81387e-08,-4.73187e-08,1.10935,0.000396784,-2.30095e-07,5.52429e-08,1.10975,0.00039649,-6.4366e-08,-5.44437e-08,1.11014,0.000396198,-2.27697e-07,4.33226e-08,1.11054,0.000395872,-9.77293e-08,3.62656e-10,1.11094,0.000395678,-9.66414e-08,-4.47732e-08,1.11133,0.00039535,-2.30961e-07,5.95208e-08,1.11173,0.000395067,-5.23985e-08,-7.41008e-08,1.11212,0.00039474,-2.74701e-07,1.17673e-07,1.11252,0.000394543,7.83181e-08,-1.58172e-07,1.11291,0.000394225,-3.96199e-07,1.57389e-07,1.1133,0.000393905,7.59679e-08,-1.13756e-07,1.1137,0.000393716,-2.653e-07,5.92165e-08,1.11409,0.000393363,-8.76507e-08,-3.90074e-09,1.11449,0.000393176,-9.93529e-08,-4.36136e-08,1.11488,0.000392846,-2.30194e-07,5.91457e-08,1.11527,0.000392563,-5.27564e-08,-7.376e-08,1.11566,0.000392237,-2.74037e-07,1.16685e-07,1.11606,0.000392039,7.60189e-08,-1.54562e-07,1.11645,0.000391727,-3.87667e-07,1.43935e-07,1.11684,0.000391384,4.4137e-08,-6.35487e-08,1.11723,0.000391281,-1.46509e-07,-8.94896e-09,1.11762,0.000390961,-1.73356e-07,-1.98647e-08,1.11801,0.000390555,-2.3295e-07,8.8408e-08,1.1184,0.000390354,3.22736e-08,-9.53486e-08,1.11879,0.000390133,-2.53772e-07,5.45677e-08,1.11918,0.000389789,-9.0069e-08,-3.71296e-09,1.11957,0.000389598,-1.01208e-07,-3.97159e-08,1.11996,0.000389276,-2.20355e-07,4.33671e-08,1.12035,0.000388966,-9.02542e-08,-1.45431e-08,1.12074,0.000388741,-1.33883e-07,1.48052e-08,1.12113,0.000388518,-8.94678e-08,-4.46778e-08,1.12152,0.000388205,-2.23501e-07,4.46966e-08,1.12191,0.000387892,-8.94114e-08,-1.48992e-08,1.12229,0.000387669,-1.34109e-07,1.49003e-08,1.12268,0.000387445,-8.94082e-08,-4.47019e-08,1.12307,0.000387132,-2.23514e-07,4.4698e-08,1.12345,0.000386819,-8.942e-08,-1.48806e-08,1.12384,0.000386596,-1.34062e-07,1.48245e-08,1.12423,0.000386372,-8.95885e-08,-4.44172e-08,1.12461,0.00038606,-2.2284e-07,4.36351e-08,1.125,0.000385745,-9.19348e-08,-1.09139e-08,1.12539,0.000385528,-1.24677e-07,2.05584e-11,1.12577,0.000385279,-1.24615e-07,1.08317e-08,1.12616,0.000385062,-9.21198e-08,-4.33473e-08,1.12654,0.000384748,-2.22162e-07,4.33481e-08,1.12693,0.000384434,-9.21174e-08,-1.08356e-08,1.12731,0.000384217,-1.24624e-07,-5.50907e-12,1.12769,0.000383968,-1.24641e-07,1.08577e-08,1.12808,0.000383751,-9.20679e-08,-4.34252e-08,1.12846,0.000383437,-2.22343e-07,4.36337e-08,1.12884,0.000383123,-9.14422e-08,-1.19005e-08,1.12923,0.000382904,-1.27144e-07,3.96813e-09,1.12961,0.000382662,-1.15239e-07,-3.97207e-09,1.12999,0.000382419,-1.27155e-07,1.19201e-08,1.13038,0.000382201,-9.1395e-08,-4.37085e-08,1.13076,0.000381887,-2.2252e-07,4.37046e-08,1.13114,0.000381573,-9.14068e-08,-1.19005e-08,1.13152,0.000381355,-1.27108e-07,3.89734e-09,1.1319,0.000381112,-1.15416e-07,-3.68887e-09,1.13228,0.00038087,-1.26483e-07,1.08582e-08,1.13266,0.00038065,-9.39083e-08,-3.97438e-08,1.13304,0.000380343,-2.1314e-07,2.89076e-08,1.13342,0.000380003,-1.26417e-07,4.33225e-08,1.1338,0.00037988,3.55072e-09,-8.29883e-08,1.13418,0.000379638,-2.45414e-07,5.0212e-08,1.13456,0.000379298,-9.47781e-08,1.34964e-09,1.13494,0.000379113,-9.07292e-08,-5.56105e-08,1.13532,0.000378764,-2.57561e-07,1.01883e-07,1.1357,0.000378555,4.80889e-08,-1.13504e-07,1.13608,0.000378311,-2.92423e-07,1.13713e-07,1.13646,0.000378067,4.87176e-08,-1.02931e-07,1.13683,0.000377856,-2.60076e-07,5.95923e-08,1.13721,0.000377514,-8.12988e-08,-1.62288e-08,1.13759,0.000377303,-1.29985e-07,5.32278e-09,1.13797,0.000377059,-1.14017e-07,-5.06237e-09,1.13834,0.000376816,-1.29204e-07,1.49267e-08,1.13872,0.000376602,-8.44237e-08,-5.46444e-08,1.1391,0.000376269,-2.48357e-07,8.44417e-08,1.13947,0.000376026,4.96815e-09,-4.47039e-08,1.13985,0.000375902,-1.29143e-07,-2.48355e-08,1.14023,0.000375569,-2.0365e-07,2.48368e-08,1.1406,0.000375236,-1.2914e-07,4.46977e-08,1.14098,0.000375112,4.95341e-09,-8.44184e-08,1.14135,0.000374869,-2.48302e-07,5.45572e-08,1.14173,0.000374536,-8.463e-08,-1.46013e-08,1.1421,0.000374323,-1.28434e-07,3.8478e-09,1.14247,0.000374077,-1.1689e-07,-7.89941e-10,1.14285,0.000373841,-1.1926e-07,-6.88042e-10,1.14322,0.0003736,-1.21324e-07,3.54213e-09,1.1436,0.000373368,-1.10698e-07,-1.34805e-08,1.14397,0.000373107,-1.51139e-07,5.03798e-08,1.14434,0.000372767,0.,0.}; + + template + __device__ __forceinline__ void RGB2LuvConvert_f(const T& src, D& dst) + { + const float _d = 1.f / (0.950456f + 15 + 1.088754f * 3); + const float _un = 13 * (4 * 0.950456f * _d); + const float _vn = 13 * (9 * _d); + + float B = blueIdx == 0 ? src.x : src.z; + float G = src.y; + float R = blueIdx == 0 ? src.z : src.x; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBGammaTab, GAMMA_TAB_SIZE); + } + + float X = R * 0.412453f + G * 0.357580f + B * 0.180423f; + float Y = R * 0.212671f + G * 0.715160f + B * 0.072169f; + float Z = R * 0.019334f + G * 0.119193f + B * 0.950227f; + + float L = splineInterpolate(Y * (LAB_CBRT_TAB_SIZE / 1.5f), c_LabCbrtTab, LAB_CBRT_TAB_SIZE); + L = 116.f * L - 16.f; + + const float d = (4 * 13) / ::fmaxf(X + 15 * Y + 3 * Z, numeric_limits::epsilon()); + float u = L * (X * d - _un); + float v = L * ((9 * 0.25f) * Y * d - _vn); + + dst.x = L; + dst.y = u; + dst.z = v; + } + + template + __device__ __forceinline__ void RGB2LuvConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (1.f / 255.f); + srcf.y = src.y * (1.f / 255.f); + srcf.z = src.z * (1.f / 255.f); + + RGB2LuvConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 2.55f); + dst.y = saturate_cast(dstf.y * 0.72033898305084743f + 96.525423728813564f); + dst.z = saturate_cast(dstf.z * 0.9732824427480916f + 136.259541984732824f); + } + + template struct RGB2Luv; + template + struct RGB2Luv + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LuvConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Luv() {} + __host__ __device__ __forceinline__ RGB2Luv(const RGB2Luv&) {} + }; + template + struct RGB2Luv + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + RGB2LuvConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ RGB2Luv() {} + __host__ __device__ __forceinline__ RGB2Luv(const RGB2Luv&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_RGB2Luv_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::RGB2Luv functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + namespace color_detail + { + template + __device__ __forceinline__ void Luv2RGBConvert_f(const T& src, D& dst) + { + const float _d = 1.f / (0.950456f + 15 + 1.088754f * 3); + const float _un = 4 * 0.950456f * _d; + const float _vn = 9 * _d; + + float L = src.x; + float u = src.y; + float v = src.z; + + float Y = (L + 16.f) * (1.f / 116.f); + Y = Y * Y * Y; + + float d = (1.f / 13.f) / L; + u = u * d + _un; + v = v * d + _vn; + + float iv = 1.f / v; + float X = 2.25f * u * Y * iv; + float Z = (12 - 3 * u - 20 * v) * Y * 0.25f * iv; + + float B = 0.055648f * X - 0.204043f * Y + 1.057311f * Z; + float G = -0.969256f * X + 1.875991f * Y + 0.041556f * Z; + float R = 3.240479f * X - 1.537150f * Y - 0.498535f * Z; + + if (srgb) + { + B = splineInterpolate(B * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R * GAMMA_TAB_SIZE, c_sRGBInvGammaTab, GAMMA_TAB_SIZE); + } + + dst.x = blueIdx == 0 ? B : R; + dst.y = G; + dst.z = blueIdx == 0 ? R : B; + setAlpha(dst, ColorChannel::max()); + } + + template + __device__ __forceinline__ void Luv2RGBConvert_b(const T& src, D& dst) + { + float3 srcf, dstf; + + srcf.x = src.x * (100.f / 255.f); + srcf.y = src.y * 1.388235294117647f - 134.f; + srcf.z = src.z * 1.027450980392157f - 140.f; + + Luv2RGBConvert_f(srcf, dstf); + + dst.x = saturate_cast(dstf.x * 255.f); + dst.y = saturate_cast(dstf.y * 255.f); + dst.z = saturate_cast(dstf.z * 255.f); + setAlpha(dst, ColorChannel::max()); + } + + template struct Luv2RGB; + template + struct Luv2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Luv2RGBConvert_b(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Luv2RGB() {} + __host__ __device__ __forceinline__ Luv2RGB(const Luv2RGB&) {} + }; + template + struct Luv2RGB + : unary_function::vec_type, typename TypeVec::vec_type> + { + __device__ __forceinline__ typename TypeVec::vec_type operator ()(const typename TypeVec::vec_type& src) const + { + typename TypeVec::vec_type dst; + + Luv2RGBConvert_f(src, dst); + + return dst; + } + __host__ __device__ __forceinline__ Luv2RGB() {} + __host__ __device__ __forceinline__ Luv2RGB(const Luv2RGB&) {} + }; + } + +#define OPENCV_CUDA_IMPLEMENT_Luv2RGB_TRAITS(name, scn, dcn, srgb, blueIdx) \ + template struct name ## _traits \ + { \ + typedef ::cv::cuda::device::color_detail::Luv2RGB functor_type; \ + static __host__ __device__ __forceinline__ functor_type create_functor() \ + { \ + return functor_type(); \ + } \ + }; + + #undef CV_DESCALE + +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_COLOR_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce.hpp new file mode 100644 index 0000000..8af20b0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce.hpp @@ -0,0 +1,365 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_REDUCE_DETAIL_HPP +#define OPENCV_CUDA_REDUCE_DETAIL_HPP + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace reduce_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(val); + + For::loadToSmem(smem, val, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ValTuple& val, unsigned int tid) + { + thrust::get(val) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, val, tid); + } + + template + static __device__ void merge(const PointerTuple& smem, const ValTuple& val, unsigned int tid, unsigned int delta, const OpTuple& op) + { + typename GetType::type>::type reg = thrust::get(smem)[tid + delta]; + thrust::get(smem)[tid] = thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::merge(smem, val, tid, delta, op); + } + template + static __device__ void mergeShfl(const ValTuple& val, unsigned int delta, unsigned int width, const OpTuple& op) + { + typename GetType::type>::type reg = shfl_down(thrust::get(val), delta, width); + thrust::get(val) = thrust::get(op)(thrust::get(val), reg); + + For::mergeShfl(val, delta, width, op); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ValTuple&, unsigned int) + { + } + + template + static __device__ void merge(const PointerTuple&, const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + template + static __device__ void mergeShfl(const ValTuple&, unsigned int, unsigned int, const OpTuple&) + { + } + }; + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& val, unsigned int tid) + { + smem[tid] = val; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& val, unsigned int tid) + { + val = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, val, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, val, tid); + } + + template + __device__ __forceinline__ void merge(volatile T* smem, T& val, unsigned int tid, unsigned int delta, const Op& op) + { + T reg = smem[tid + delta]; + smem[tid] = val = op(val, reg); + } + template + __device__ __forceinline__ void mergeShfl(T& val, unsigned int delta, unsigned int width, const Op& op) + { + T reg = shfl_down(val, delta, width); + val = op(val, reg); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + unsigned int delta, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::merge(smem, val, tid, delta, op); + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& val, + unsigned int delta, + unsigned int width, + const thrust::tuple& op) + { + For<0, thrust::tuple_size >::value>::mergeShfl(val, delta, width, op); + } + + template struct Generic + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + loadToSmem(smem, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(smem, val, tid, 1024, op); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(smem, val, tid, 512, op); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(smem, val, tid, 256, op); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(smem, val, tid, 128, op); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(smem, val, tid, 64, op); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(smem, val, tid, 32, op); + } + + if (tid < 16) + { + merge(smem, val, tid, 16, op); + merge(smem, val, tid, 8, op); + merge(smem, val, tid, 4, op); + merge(smem, val, tid, 2, op); + merge(smem, val, tid, 1, op); + } + } + }; + + template + struct Unroll + { + static __device__ void loopShfl(Reference val, Op op, unsigned int N) + { + mergeShfl(val, I, N, op); + Unroll::loopShfl(val, op, N); + } + static __device__ void loop(Pointer smem, Reference val, unsigned int tid, Op op) + { + merge(smem, val, tid, I, op); + Unroll::loop(smem, val, tid, op); + } + }; + template + struct Unroll<0, Pointer, Reference, Op> + { + static __device__ void loopShfl(Reference, Op, unsigned int) + { + } + static __device__ void loop(Pointer, Reference, unsigned int, Op) + { + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + CV_UNUSED(smem); + CV_UNUSED(tid); + + Unroll::loopShfl(val, op, N); + #else + loadToSmem(smem, val, tid); + + if (tid < N / 2) + Unroll::loop(smem, val, tid, op); + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(Pointer smem, Reference val, unsigned int tid, Op op) + { + const unsigned int laneId = Warp::laneId(); + + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + Unroll<16, Pointer, Reference, Op>::loopShfl(val, op, warpSize); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #else + loadToSmem(smem, val, tid); + + if (laneId < 16) + Unroll<16, Pointer, Reference, Op>::loop(smem, val, tid, op); + + __syncthreads(); + + if (laneId == 0) + loadToSmem(smem, val, tid / 32); + #endif + + __syncthreads(); + + loadFromSmem(smem, val, tid); + + if (tid < 32) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + Unroll::loopShfl(val, op, M); + #else + Unroll::loop(smem, val, tid, op); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_REDUCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce_key_val.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce_key_val.hpp new file mode 100644 index 0000000..df37c17 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/reduce_key_val.hpp @@ -0,0 +1,502 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP +#define OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP + +#include +#include "../warp.hpp" +#include "../warp_shuffle.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace reduce_key_val_detail + { + template struct GetType; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + template struct GetType + { + typedef T type; + }; + + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(smem)[tid] = thrust::get(data); + + For::loadToSmem(smem, data, tid); + } + template + static __device__ void loadFromSmem(const PointerTuple& smem, const ReferenceTuple& data, unsigned int tid) + { + thrust::get(data) = thrust::get(smem)[tid]; + + For::loadFromSmem(smem, data, tid); + } + + template + static __device__ void copyShfl(const ReferenceTuple& val, unsigned int delta, int width) + { + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + + For::copyShfl(val, delta, width); + } + template + static __device__ void copy(const PointerTuple& svals, const ReferenceTuple& val, unsigned int tid, unsigned int delta) + { + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + + For::copy(svals, val, tid, delta); + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple& key, const ValReferenceTuple& val, const CmpTuple& cmp, unsigned int delta, int width) + { + typename GetType::type>::type reg = shfl_down(thrust::get(key), delta, width); + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(key) = reg; + thrust::get(val) = shfl_down(thrust::get(val), delta, width); + } + + For::mergeShfl(key, val, cmp, delta, width); + } + template + static __device__ void merge(const KeyPointerTuple& skeys, const KeyReferenceTuple& key, + const ValPointerTuple& svals, const ValReferenceTuple& val, + const CmpTuple& cmp, + unsigned int tid, unsigned int delta) + { + typename GetType::type>::type reg = thrust::get(skeys)[tid + delta]; + + if (thrust::get(cmp)(reg, thrust::get(key))) + { + thrust::get(skeys)[tid] = thrust::get(key) = reg; + thrust::get(svals)[tid] = thrust::get(val) = thrust::get(svals)[tid + delta]; + } + + For::merge(skeys, key, svals, val, cmp, tid, delta); + } + }; + template + struct For + { + template + static __device__ void loadToSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + template + static __device__ void loadFromSmem(const PointerTuple&, const ReferenceTuple&, unsigned int) + { + } + + template + static __device__ void copyShfl(const ReferenceTuple&, unsigned int, int) + { + } + template + static __device__ void copy(const PointerTuple&, const ReferenceTuple&, unsigned int, unsigned int) + { + } + + template + static __device__ void mergeShfl(const KeyReferenceTuple&, const ValReferenceTuple&, const CmpTuple&, unsigned int, int) + { + } + template + static __device__ void merge(const KeyPointerTuple&, const KeyReferenceTuple&, + const ValPointerTuple&, const ValReferenceTuple&, + const CmpTuple&, + unsigned int, unsigned int) + { + } + }; + + ////////////////////////////////////////////////////// + // loadToSmem + + template + __device__ __forceinline__ void loadToSmem(volatile T* smem, T& data, unsigned int tid) + { + smem[tid] = data; + } + template + __device__ __forceinline__ void loadFromSmem(volatile T* smem, T& data, unsigned int tid) + { + data = smem[tid]; + } + template + __device__ __forceinline__ void loadToSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadToSmem(smem, data, tid); + } + template + __device__ __forceinline__ void loadFromSmem(const thrust::tuple& smem, + const thrust::tuple& data, + unsigned int tid) + { + For<0, thrust::tuple_size >::value>::loadFromSmem(smem, data, tid); + } + + ////////////////////////////////////////////////////// + // copyVals + + template + __device__ __forceinline__ void copyValsShfl(V& val, unsigned int delta, int width) + { + val = shfl_down(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(volatile V* svals, V& val, unsigned int tid, unsigned int delta) + { + svals[tid] = val = svals[tid + delta]; + } + template + __device__ __forceinline__ void copyValsShfl(const thrust::tuple& val, + unsigned int delta, + int width) + { + For<0, thrust::tuple_size >::value>::copyShfl(val, delta, width); + } + template + __device__ __forceinline__ void copyVals(const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::copy(svals, val, tid, delta); + } + + ////////////////////////////////////////////////////// + // merge + + template + __device__ __forceinline__ void mergeShfl(K& key, V& val, const Cmp& cmp, unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, volatile V* svals, V& val, const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(K& key, + const thrust::tuple& val, + const Cmp& cmp, + unsigned int delta, int width) + { + K reg = shfl_down(key, delta, width); + + if (cmp(reg, key)) + { + key = reg; + copyValsShfl(val, delta, width); + } + } + template + __device__ __forceinline__ void merge(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const Cmp& cmp, unsigned int tid, unsigned int delta) + { + K reg = skeys[tid + delta]; + + if (cmp(reg, key)) + { + skeys[tid] = key = reg; + copyVals(svals, val, tid, delta); + } + } + template + __device__ __forceinline__ void mergeShfl(const thrust::tuple& key, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int delta, int width) + { + For<0, thrust::tuple_size >::value>::mergeShfl(key, val, cmp, delta, width); + } + template + __device__ __forceinline__ void merge(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + const thrust::tuple& cmp, + unsigned int tid, unsigned int delta) + { + For<0, thrust::tuple_size >::value>::merge(skeys, key, svals, val, cmp, tid, delta); + } + + ////////////////////////////////////////////////////// + // Generic + + template struct Generic + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + loadToSmem(skeys, key, tid); + loadValsToSmem(svals, val, tid); + if (N >= 32) + __syncthreads(); + + if (N >= 2048) + { + if (tid < 1024) + merge(skeys, key, svals, val, cmp, tid, 1024); + + __syncthreads(); + } + if (N >= 1024) + { + if (tid < 512) + merge(skeys, key, svals, val, cmp, tid, 512); + + __syncthreads(); + } + if (N >= 512) + { + if (tid < 256) + merge(skeys, key, svals, val, cmp, tid, 256); + + __syncthreads(); + } + if (N >= 256) + { + if (tid < 128) + merge(skeys, key, svals, val, cmp, tid, 128); + + __syncthreads(); + } + if (N >= 128) + { + if (tid < 64) + merge(skeys, key, svals, val, cmp, tid, 64); + + __syncthreads(); + } + if (N >= 64) + { + if (tid < 32) + merge(skeys, key, svals, val, cmp, tid, 32); + } + + if (tid < 16) + { + merge(skeys, key, svals, val, cmp, tid, 16); + merge(skeys, key, svals, val, cmp, tid, 8); + merge(skeys, key, svals, val, cmp, tid, 4); + merge(skeys, key, svals, val, cmp, tid, 2); + merge(skeys, key, svals, val, cmp, tid, 1); + } + } + }; + + template + struct Unroll + { + static __device__ void loopShfl(KR key, VR val, Cmp cmp, unsigned int N) + { + mergeShfl(key, val, cmp, I, N); + Unroll::loopShfl(key, val, cmp, N); + } + static __device__ void loop(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + merge(skeys, key, svals, val, cmp, tid, I); + Unroll::loop(skeys, key, svals, val, tid, cmp); + } + }; + template + struct Unroll<0, KP, KR, VP, VR, Cmp> + { + static __device__ void loopShfl(KR, VR, Cmp, unsigned int) + { + } + static __device__ void loop(KP, KR, VP, VR, unsigned int, Cmp) + { + } + }; + + template struct WarpOptimized + { + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + #if 0 // __CUDA_ARCH__ >= 300 + CV_UNUSED(skeys); + CV_UNUSED(svals); + CV_UNUSED(tid); + + Unroll::loopShfl(key, val, cmp, N); + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (tid < N / 2) + Unroll::loop(skeys, key, svals, val, tid, cmp); + #endif + } + }; + + template struct GenericOptimized32 + { + enum { M = N / 32 }; + + template + static __device__ void reduce(KP skeys, KR key, VP svals, VR val, unsigned int tid, Cmp cmp) + { + const unsigned int laneId = Warp::laneId(); + + #if 0 // __CUDA_ARCH__ >= 300 + Unroll<16, KP, KR, VP, VR, Cmp>::loopShfl(key, val, cmp, warpSize); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #else + loadToSmem(skeys, key, tid); + loadToSmem(svals, val, tid); + + if (laneId < 16) + Unroll<16, KP, KR, VP, VR, Cmp>::loop(skeys, key, svals, val, tid, cmp); + + __syncthreads(); + + if (laneId == 0) + { + loadToSmem(skeys, key, tid / 32); + loadToSmem(svals, val, tid / 32); + } + #endif + + __syncthreads(); + + loadFromSmem(skeys, key, tid); + + if (tid < 32) + { + #if 0 // __CUDA_ARCH__ >= 300 + loadFromSmem(svals, val, tid); + + Unroll::loopShfl(key, val, cmp, M); + #else + Unroll::loop(skeys, key, svals, val, tid, cmp); + #endif + } + } + }; + + template struct StaticIf; + template struct StaticIf + { + typedef T1 type; + }; + template struct StaticIf + { + typedef T2 type; + }; + + template struct IsPowerOf2 + { + enum { value = ((N != 0) && !(N & (N - 1))) }; + }; + + template struct Dispatcher + { + typedef typename StaticIf< + (N <= 32) && IsPowerOf2::value, + WarpOptimized, + typename StaticIf< + (N <= 1024) && IsPowerOf2::value, + GenericOptimized32, + Generic + >::type + >::type reductor; + }; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_PRED_VAL_REDUCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/transform_detail.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/transform_detail.hpp new file mode 100644 index 0000000..1919848 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/transform_detail.hpp @@ -0,0 +1,392 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TRANSFORM_DETAIL_HPP +#define OPENCV_CUDA_TRANSFORM_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" +#include "../functional.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace transform_detail + { + //! Read Write Traits + + template struct UnaryReadWriteTraits + { + typedef typename TypeVec::vec_type read_type; + typedef typename TypeVec::vec_type write_type; + }; + + template struct BinaryReadWriteTraits + { + typedef typename TypeVec::vec_type read_type1; + typedef typename TypeVec::vec_type read_type2; + typedef typename TypeVec::vec_type write_type; + }; + + //! Transform kernels + + template struct OpUnroller; + template <> struct OpUnroller<1> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + } + }; + template <> struct OpUnroller<2> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + } + }; + template <> struct OpUnroller<3> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src.z); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src1.z, src2.z); + } + }; + template <> struct OpUnroller<4> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src.z); + if (mask(y, x_shifted + 3)) + dst.w = op(src.w); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.x = op(src1.x, src2.x); + if (mask(y, x_shifted + 1)) + dst.y = op(src1.y, src2.y); + if (mask(y, x_shifted + 2)) + dst.z = op(src1.z, src2.z); + if (mask(y, x_shifted + 3)) + dst.w = op(src1.w, src2.w); + } + }; + template <> struct OpUnroller<8> + { + template + static __device__ __forceinline__ void unroll(const T& src, D& dst, const Mask& mask, const UnOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.a0 = op(src.a0); + if (mask(y, x_shifted + 1)) + dst.a1 = op(src.a1); + if (mask(y, x_shifted + 2)) + dst.a2 = op(src.a2); + if (mask(y, x_shifted + 3)) + dst.a3 = op(src.a3); + if (mask(y, x_shifted + 4)) + dst.a4 = op(src.a4); + if (mask(y, x_shifted + 5)) + dst.a5 = op(src.a5); + if (mask(y, x_shifted + 6)) + dst.a6 = op(src.a6); + if (mask(y, x_shifted + 7)) + dst.a7 = op(src.a7); + } + + template + static __device__ __forceinline__ void unroll(const T1& src1, const T2& src2, D& dst, const Mask& mask, const BinOp& op, int x_shifted, int y) + { + if (mask(y, x_shifted)) + dst.a0 = op(src1.a0, src2.a0); + if (mask(y, x_shifted + 1)) + dst.a1 = op(src1.a1, src2.a1); + if (mask(y, x_shifted + 2)) + dst.a2 = op(src1.a2, src2.a2); + if (mask(y, x_shifted + 3)) + dst.a3 = op(src1.a3, src2.a3); + if (mask(y, x_shifted + 4)) + dst.a4 = op(src1.a4, src2.a4); + if (mask(y, x_shifted + 5)) + dst.a5 = op(src1.a5, src2.a5); + if (mask(y, x_shifted + 6)) + dst.a6 = op(src1.a6, src2.a6); + if (mask(y, x_shifted + 7)) + dst.a7 = op(src1.a7, src2.a7); + } + }; + + template + static __global__ void transformSmart(const PtrStepSz src_, PtrStep dst_, const Mask mask, const UnOp op) + { + typedef TransformFunctorTraits ft; + typedef typename UnaryReadWriteTraits::read_type read_type; + typedef typename UnaryReadWriteTraits::write_type write_type; + + const int x = threadIdx.x + blockIdx.x * blockDim.x; + const int y = threadIdx.y + blockIdx.y * blockDim.y; + const int x_shifted = x * ft::smart_shift; + + if (y < src_.rows) + { + const T* src = src_.ptr(y); + D* dst = dst_.ptr(y); + + if (x_shifted + ft::smart_shift - 1 < src_.cols) + { + const read_type src_n_el = ((const read_type*)src)[x]; + OpUnroller::unroll(src_n_el, ((write_type*)dst)[x], mask, op, x_shifted, y); + } + else + { + for (int real_x = x_shifted; real_x < src_.cols; ++real_x) + { + if (mask(y, real_x)) + dst[real_x] = op(src[real_x]); + } + } + } + } + + template + __global__ static void transformSimple(const PtrStepSz src, PtrStep dst, const Mask mask, const UnOp op) + { + const int x = blockDim.x * blockIdx.x + threadIdx.x; + const int y = blockDim.y * blockIdx.y + threadIdx.y; + + if (x < src.cols && y < src.rows && mask(y, x)) + { + dst.ptr(y)[x] = op(src.ptr(y)[x]); + } + } + + template + static __global__ void transformSmart(const PtrStepSz src1_, const PtrStep src2_, PtrStep dst_, + const Mask mask, const BinOp op) + { + typedef TransformFunctorTraits ft; + typedef typename BinaryReadWriteTraits::read_type1 read_type1; + typedef typename BinaryReadWriteTraits::read_type2 read_type2; + typedef typename BinaryReadWriteTraits::write_type write_type; + + const int x = threadIdx.x + blockIdx.x * blockDim.x; + const int y = threadIdx.y + blockIdx.y * blockDim.y; + const int x_shifted = x * ft::smart_shift; + + if (y < src1_.rows) + { + const T1* src1 = src1_.ptr(y); + const T2* src2 = src2_.ptr(y); + D* dst = dst_.ptr(y); + + if (x_shifted + ft::smart_shift - 1 < src1_.cols) + { + const read_type1 src1_n_el = ((const read_type1*)src1)[x]; + const read_type2 src2_n_el = ((const read_type2*)src2)[x]; + + OpUnroller::unroll(src1_n_el, src2_n_el, ((write_type*)dst)[x], mask, op, x_shifted, y); + } + else + { + for (int real_x = x_shifted; real_x < src1_.cols; ++real_x) + { + if (mask(y, real_x)) + dst[real_x] = op(src1[real_x], src2[real_x]); + } + } + } + } + + template + static __global__ void transformSimple(const PtrStepSz src1, const PtrStep src2, PtrStep dst, + const Mask mask, const BinOp op) + { + const int x = blockDim.x * blockIdx.x + threadIdx.x; + const int y = blockDim.y * blockIdx.y + threadIdx.y; + + if (x < src1.cols && y < src1.rows && mask(y, x)) + { + const T1 src1_data = src1.ptr(y)[x]; + const T2 src2_data = src2.ptr(y)[x]; + dst.ptr(y)[x] = op(src1_data, src2_data); + } + } + + template struct TransformDispatcher; + template<> struct TransformDispatcher + { + template + static void call(PtrStepSz src, PtrStepSz dst, UnOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + const dim3 threads(ft::simple_block_dim_x, ft::simple_block_dim_y, 1); + const dim3 grid(divUp(src.cols, threads.x), divUp(src.rows, threads.y), 1); + + transformSimple<<>>(src, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + + template + static void call(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + const dim3 threads(ft::simple_block_dim_x, ft::simple_block_dim_y, 1); + const dim3 grid(divUp(src1.cols, threads.x), divUp(src1.rows, threads.y), 1); + + transformSimple<<>>(src1, src2, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; + template<> struct TransformDispatcher + { + template + static void call(PtrStepSz src, PtrStepSz dst, UnOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + CV_StaticAssert(ft::smart_shift != 1, ""); + + if (!isAligned(src.data, ft::smart_shift * sizeof(T)) || !isAligned(src.step, ft::smart_shift * sizeof(T)) || + !isAligned(dst.data, ft::smart_shift * sizeof(D)) || !isAligned(dst.step, ft::smart_shift * sizeof(D))) + { + TransformDispatcher::call(src, dst, op, mask, stream); + return; + } + + const dim3 threads(ft::smart_block_dim_x, ft::smart_block_dim_y, 1); + const dim3 grid(divUp(src.cols, threads.x * ft::smart_shift), divUp(src.rows, threads.y), 1); + + transformSmart<<>>(src, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + + template + static void call(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, Mask mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + + CV_StaticAssert(ft::smart_shift != 1, ""); + + if (!isAligned(src1.data, ft::smart_shift * sizeof(T1)) || !isAligned(src1.step, ft::smart_shift * sizeof(T1)) || + !isAligned(src2.data, ft::smart_shift * sizeof(T2)) || !isAligned(src2.step, ft::smart_shift * sizeof(T2)) || + !isAligned(dst.data, ft::smart_shift * sizeof(D)) || !isAligned(dst.step, ft::smart_shift * sizeof(D))) + { + TransformDispatcher::call(src1, src2, dst, op, mask, stream); + return; + } + + const dim3 threads(ft::smart_block_dim_x, ft::smart_block_dim_y, 1); + const dim3 grid(divUp(src1.cols, threads.x * ft::smart_shift), divUp(src1.rows, threads.y), 1); + + transformSmart<<>>(src1, src2, dst, mask, op); + cudaSafeCall( cudaGetLastError() ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; + } // namespace transform_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_TRANSFORM_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/type_traits_detail.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/type_traits_detail.hpp new file mode 100644 index 0000000..a78bd2c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/type_traits_detail.hpp @@ -0,0 +1,191 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP +#define OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP + +#include "../common.hpp" +#include "../vec_traits.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace type_traits_detail + { + template struct Select { typedef T1 type; }; + template struct Select { typedef T2 type; }; + + template struct IsSignedIntergral { enum {value = 0}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + template <> struct IsSignedIntergral { enum {value = 1}; }; + + template struct IsUnsignedIntegral { enum {value = 0}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + template <> struct IsUnsignedIntegral { enum {value = 1}; }; + + template struct IsIntegral { enum {value = IsSignedIntergral::value || IsUnsignedIntegral::value}; }; + template <> struct IsIntegral { enum {value = 1}; }; + template <> struct IsIntegral { enum {value = 1}; }; + + template struct IsFloat { enum {value = 0}; }; + template <> struct IsFloat { enum {value = 1}; }; + template <> struct IsFloat { enum {value = 1}; }; + + template struct IsVec { enum {value = 0}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + template <> struct IsVec { enum {value = 1}; }; + + template struct AddParameterType { typedef const U& type; }; + template struct AddParameterType { typedef U& type; }; + template <> struct AddParameterType { typedef void type; }; + + template struct ReferenceTraits + { + enum { value = false }; + typedef U type; + }; + template struct ReferenceTraits + { + enum { value = true }; + typedef U type; + }; + + template struct PointerTraits + { + enum { value = false }; + typedef void type; + }; + template struct PointerTraits + { + enum { value = true }; + typedef U type; + }; + template struct PointerTraits + { + enum { value = true }; + typedef U type; + }; + + template struct UnConst + { + typedef U type; + enum { value = 0 }; + }; + template struct UnConst + { + typedef U type; + enum { value = 1 }; + }; + template struct UnConst + { + typedef U& type; + enum { value = 1 }; + }; + + template struct UnVolatile + { + typedef U type; + enum { value = 0 }; + }; + template struct UnVolatile + { + typedef U type; + enum { value = 1 }; + }; + template struct UnVolatile + { + typedef U& type; + enum { value = 1 }; + }; + } // namespace type_traits_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_TYPE_TRAITS_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/vec_distance_detail.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/vec_distance_detail.hpp new file mode 100644 index 0000000..8283a99 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/detail/vec_distance_detail.hpp @@ -0,0 +1,121 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP +#define OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP + +#include "../datamov_utils.hpp" + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + namespace vec_distance_detail + { + template struct UnrollVecDiffCached + { + template + static __device__ void calcCheck(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int ind) + { + if (ind < len) + { + T1 val1 = *vecCached++; + + T2 val2; + ForceGlob::Load(vecGlob, ind, val2); + + dist.reduceIter(val1, val2); + + UnrollVecDiffCached::calcCheck(vecCached, vecGlob, len, dist, ind + THREAD_DIM); + } + } + + template + static __device__ void calcWithoutCheck(const T1* vecCached, const T2* vecGlob, Dist& dist) + { + T1 val1 = *vecCached++; + + T2 val2; + ForceGlob::Load(vecGlob, 0, val2); + vecGlob += THREAD_DIM; + + dist.reduceIter(val1, val2); + + UnrollVecDiffCached::calcWithoutCheck(vecCached, vecGlob, dist); + } + }; + template struct UnrollVecDiffCached + { + template + static __device__ __forceinline__ void calcCheck(const T1*, const T2*, int, Dist&, int) + { + } + + template + static __device__ __forceinline__ void calcWithoutCheck(const T1*, const T2*, Dist&) + { + } + }; + + template struct VecDiffCachedCalculator; + template struct VecDiffCachedCalculator + { + template + static __device__ __forceinline__ void calc(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int tid) + { + UnrollVecDiffCached::calcCheck(vecCached, vecGlob, len, dist, tid); + } + }; + template struct VecDiffCachedCalculator + { + template + static __device__ __forceinline__ void calc(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, int tid) + { + UnrollVecDiffCached::calcWithoutCheck(vecCached, vecGlob + tid, dist); + } + }; + } // namespace vec_distance_detail +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_DISTANCE_DETAIL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/dynamic_smem.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/dynamic_smem.hpp new file mode 100644 index 0000000..42570c6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/dynamic_smem.hpp @@ -0,0 +1,88 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DYNAMIC_SMEM_HPP +#define OPENCV_CUDA_DYNAMIC_SMEM_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct DynamicSharedMem + { + __device__ __forceinline__ operator T*() + { + extern __shared__ int __smem[]; + return (T*)__smem; + } + + __device__ __forceinline__ operator const T*() const + { + extern __shared__ int __smem[]; + return (T*)__smem; + } + }; + + // specialize for double to avoid unaligned memory access compile errors + template<> struct DynamicSharedMem + { + __device__ __forceinline__ operator double*() + { + extern __shared__ double __smem_d[]; + return (double*)__smem_d; + } + + __device__ __forceinline__ operator const double*() const + { + extern __shared__ double __smem_d[]; + return (double*)__smem_d; + } + }; +}}} + +//! @endcond + +#endif // OPENCV_CUDA_DYNAMIC_SMEM_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/emulation.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/emulation.hpp new file mode 100644 index 0000000..17dc117 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/emulation.hpp @@ -0,0 +1,269 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_EMULATION_HPP_ +#define OPENCV_CUDA_EMULATION_HPP_ + +#include "common.hpp" +#include "warp_reduce.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Emulation + { + + static __device__ __forceinline__ int syncthreadsOr(int pred) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 200) + // just campilation stab + return 0; +#else + return __syncthreads_or(pred); +#endif + } + + template + static __forceinline__ __device__ int Ballot(int predicate) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 200) + return __ballot(predicate); +#else + __shared__ volatile int cta_buffer[CTA_SIZE]; + + int tid = threadIdx.x; + cta_buffer[tid] = predicate ? (1 << (tid & 31)) : 0; + return warp_reduce(cta_buffer); +#endif + } + + struct smem + { + enum { TAG_MASK = (1U << ( (sizeof(unsigned int) << 3) - 5U)) - 1U }; + + template + static __device__ __forceinline__ T atomicInc(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count; + unsigned int tag = threadIdx.x << ( (sizeof(unsigned int) << 3) - 5U); + do + { + count = *address & TAG_MASK; + count = tag | (count + 1); + *address = count; + } while (*address != count); + + return (count & TAG_MASK) - 1; +#else + return ::atomicInc(address, val); +#endif + } + + template + static __device__ __forceinline__ T atomicAdd(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count; + unsigned int tag = threadIdx.x << ( (sizeof(unsigned int) << 3) - 5U); + do + { + count = *address & TAG_MASK; + count = tag | (count + val); + *address = count; + } while (*address != count); + + return (count & TAG_MASK) - val; +#else + return ::atomicAdd(address, val); +#endif + } + + template + static __device__ __forceinline__ T atomicMin(T* address, T val) + { +#if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 120) + T count = ::min(*address, val); + do + { + *address = count; + } while (*address > count); + + return count; +#else + return ::atomicMin(address, val); +#endif + } + }; // struct cmem + + struct glob + { + static __device__ __forceinline__ int atomicAdd(int* address, int val) + { + return ::atomicAdd(address, val); + } + static __device__ __forceinline__ unsigned int atomicAdd(unsigned int* address, unsigned int val) + { + return ::atomicAdd(address, val); + } + static __device__ __forceinline__ float atomicAdd(float* address, float val) + { + #if __CUDA_ARCH__ >= 200 + return ::atomicAdd(address, val); + #else + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(val + __int_as_float(assumed))); + } while (assumed != old); + return __int_as_float(old); + #endif + } + static __device__ __forceinline__ double atomicAdd(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + __longlong_as_double(assumed))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + + static __device__ __forceinline__ int atomicMin(int* address, int val) + { + return ::atomicMin(address, val); + } + static __device__ __forceinline__ float atomicMin(float* address, float val) + { + #if __CUDA_ARCH__ >= 120 + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(::fminf(val, __int_as_float(assumed)))); + } while (assumed != old); + return __int_as_float(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0f; + #endif + } + static __device__ __forceinline__ double atomicMin(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(::fmin(val, __longlong_as_double(assumed)))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + + static __device__ __forceinline__ int atomicMax(int* address, int val) + { + return ::atomicMax(address, val); + } + static __device__ __forceinline__ float atomicMax(float* address, float val) + { + #if __CUDA_ARCH__ >= 120 + int* address_as_i = (int*) address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(::fmaxf(val, __int_as_float(assumed)))); + } while (assumed != old); + return __int_as_float(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0f; + #endif + } + static __device__ __forceinline__ double atomicMax(double* address, double val) + { + #if __CUDA_ARCH__ >= 130 + unsigned long long int* address_as_ull = (unsigned long long int*) address; + unsigned long long int old = *address_as_ull, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_ull, assumed, + __double_as_longlong(::fmax(val, __longlong_as_double(assumed)))); + } while (assumed != old); + return __longlong_as_double(old); + #else + CV_UNUSED(address); + CV_UNUSED(val); + return 0.0; + #endif + } + }; + }; //struct Emulation +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_EMULATION_HPP_ */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/filters.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/filters.hpp new file mode 100644 index 0000000..bb94212 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/filters.hpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_FILTERS_HPP +#define OPENCV_CUDA_FILTERS_HPP + +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "vec_math.hpp" +#include "type_traits.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct PointFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ PointFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + return src(__float2int_rz(y), __float2int_rz(x)); + } + + Ptr2D src; + }; + + template struct LinearFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ LinearFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + typedef typename TypeVec::cn>::vec_type work_type; + + work_type out = VecTraits::all(0); + + const int x1 = __float2int_rd(x); + const int y1 = __float2int_rd(y); + const int x2 = x1 + 1; + const int y2 = y1 + 1; + + elem_type src_reg = src(y1, x1); + out = out + src_reg * ((x2 - x) * (y2 - y)); + + src_reg = src(y1, x2); + out = out + src_reg * ((x - x1) * (y2 - y)); + + src_reg = src(y2, x1); + out = out + src_reg * ((x2 - x) * (y - y1)); + + src_reg = src(y2, x2); + out = out + src_reg * ((x - x1) * (y - y1)); + + return saturate_cast(out); + } + + Ptr2D src; + }; + + template struct CubicFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + typedef typename TypeVec::cn>::vec_type work_type; + + explicit __host__ __device__ __forceinline__ CubicFilter(const Ptr2D& src_, float fx = 0.f, float fy = 0.f) + : src(src_) + { + CV_UNUSED(fx); + CV_UNUSED(fy); + } + + static __device__ __forceinline__ float bicubicCoeff(float x_) + { + float x = fabsf(x_); + if (x <= 1.0f) + { + return x * x * (1.5f * x - 2.5f) + 1.0f; + } + else if (x < 2.0f) + { + return x * (x * (-0.5f * x + 2.5f) - 4.0f) + 2.0f; + } + else + { + return 0.0f; + } + } + + __device__ elem_type operator ()(float y, float x) const + { + const float xmin = ::ceilf(x - 2.0f); + const float xmax = ::floorf(x + 2.0f); + + const float ymin = ::ceilf(y - 2.0f); + const float ymax = ::floorf(y + 2.0f); + + work_type sum = VecTraits::all(0); + float wsum = 0.0f; + + for (float cy = ymin; cy <= ymax; cy += 1.0f) + { + for (float cx = xmin; cx <= xmax; cx += 1.0f) + { + const float w = bicubicCoeff(x - cx) * bicubicCoeff(y - cy); + sum = sum + w * src(__float2int_rd(cy), __float2int_rd(cx)); + wsum += w; + } + } + + work_type res = (!wsum)? VecTraits::all(0) : sum / wsum; + + return saturate_cast(res); + } + + Ptr2D src; + }; + // for integer scaling + template struct IntegerAreaFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ IntegerAreaFilter(const Ptr2D& src_, float scale_x_, float scale_y_) + : src(src_), scale_x(scale_x_), scale_y(scale_y_), scale(1.f / (scale_x * scale_y)) {} + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + float fsx1 = x * scale_x; + float fsx2 = fsx1 + scale_x; + + int sx1 = __float2int_ru(fsx1); + int sx2 = __float2int_rd(fsx2); + + float fsy1 = y * scale_y; + float fsy2 = fsy1 + scale_y; + + int sy1 = __float2int_ru(fsy1); + int sy2 = __float2int_rd(fsy2); + + typedef typename TypeVec::cn>::vec_type work_type; + work_type out = VecTraits::all(0.f); + + for(int dy = sy1; dy < sy2; ++dy) + for(int dx = sx1; dx < sx2; ++dx) + { + out = out + src(dy, dx) * scale; + } + + return saturate_cast(out); + } + + Ptr2D src; + float scale_x, scale_y ,scale; + }; + + template struct AreaFilter + { + typedef typename Ptr2D::elem_type elem_type; + typedef float index_type; + + explicit __host__ __device__ __forceinline__ AreaFilter(const Ptr2D& src_, float scale_x_, float scale_y_) + : src(src_), scale_x(scale_x_), scale_y(scale_y_){} + + __device__ __forceinline__ elem_type operator ()(float y, float x) const + { + float fsx1 = x * scale_x; + float fsx2 = fsx1 + scale_x; + + int sx1 = __float2int_ru(fsx1); + int sx2 = __float2int_rd(fsx2); + + float fsy1 = y * scale_y; + float fsy2 = fsy1 + scale_y; + + int sy1 = __float2int_ru(fsy1); + int sy2 = __float2int_rd(fsy2); + + float scale = 1.f / (fminf(scale_x, src.width - fsx1) * fminf(scale_y, src.height - fsy1)); + + typedef typename TypeVec::cn>::vec_type work_type; + work_type out = VecTraits::all(0.f); + + for (int dy = sy1; dy < sy2; ++dy) + { + for (int dx = sx1; dx < sx2; ++dx) + out = out + src(dy, dx) * scale; + + if (sx1 > fsx1) + out = out + src(dy, (sx1 -1) ) * ((sx1 - fsx1) * scale); + + if (sx2 < fsx2) + out = out + src(dy, sx2) * ((fsx2 -sx2) * scale); + } + + if (sy1 > fsy1) + for (int dx = sx1; dx < sx2; ++dx) + out = out + src( (sy1 - 1) , dx) * ((sy1 -fsy1) * scale); + + if (sy2 < fsy2) + for (int dx = sx1; dx < sx2; ++dx) + out = out + src(sy2, dx) * ((fsy2 -sy2) * scale); + + if ((sy1 > fsy1) && (sx1 > fsx1)) + out = out + src( (sy1 - 1) , (sx1 - 1)) * ((sy1 -fsy1) * (sx1 -fsx1) * scale); + + if ((sy1 > fsy1) && (sx2 < fsx2)) + out = out + src( (sy1 - 1) , sx2) * ((sy1 -fsy1) * (fsx2 -sx2) * scale); + + if ((sy2 < fsy2) && (sx2 < fsx2)) + out = out + src(sy2, sx2) * ((fsy2 -sy2) * (fsx2 -sx2) * scale); + + if ((sy2 < fsy2) && (sx1 > fsx1)) + out = out + src(sy2, (sx1 - 1)) * ((fsy2 -sy2) * (sx1 -fsx1) * scale); + + return saturate_cast(out); + } + + Ptr2D src; + float scale_x, scale_y; + int width, haight; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_FILTERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/funcattrib.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/funcattrib.hpp new file mode 100644 index 0000000..f582080 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/funcattrib.hpp @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP +#define OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP + +#include + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + void printFuncAttrib(Func& func) + { + + cudaFuncAttributes attrs; + cudaFuncGetAttributes(&attrs, func); + + printf("=== Function stats ===\n"); + printf("Name: \n"); + printf("sharedSizeBytes = %d\n", attrs.sharedSizeBytes); + printf("constSizeBytes = %d\n", attrs.constSizeBytes); + printf("localSizeBytes = %d\n", attrs.localSizeBytes); + printf("maxThreadsPerBlock = %d\n", attrs.maxThreadsPerBlock); + printf("numRegs = %d\n", attrs.numRegs); + printf("ptxVersion = %d\n", attrs.ptxVersion); + printf("binaryVersion = %d\n", attrs.binaryVersion); + printf("\n"); + fflush(stdout); + } +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_FUNCATTRIB_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/functional.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/functional.hpp new file mode 100644 index 0000000..3b531a1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/functional.hpp @@ -0,0 +1,810 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_FUNCTIONAL_HPP +#define OPENCV_CUDA_FUNCTIONAL_HPP + +#include +#include "saturate_cast.hpp" +#include "vec_traits.hpp" +#include "type_traits.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // Function Objects +#ifdef CV_CXX11 + template struct unary_function + { + typedef Argument argument_type; + typedef Result result_type; + }; + template struct binary_function + { + typedef Argument1 first_argument_type; + typedef Argument2 second_argument_type; + typedef Result result_type; + }; +#else + template struct unary_function : public std::unary_function {}; + template struct binary_function : public std::binary_function {}; +#endif + + // Arithmetic Operations + template struct plus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a + b; + } + __host__ __device__ __forceinline__ plus() {} + __host__ __device__ __forceinline__ plus(const plus&) {} + }; + + template struct minus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a - b; + } + __host__ __device__ __forceinline__ minus() {} + __host__ __device__ __forceinline__ minus(const minus&) {} + }; + + template struct multiplies : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a * b; + } + __host__ __device__ __forceinline__ multiplies() {} + __host__ __device__ __forceinline__ multiplies(const multiplies&) {} + }; + + template struct divides : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a / b; + } + __host__ __device__ __forceinline__ divides() {} + __host__ __device__ __forceinline__ divides(const divides&) {} + }; + + template struct modulus : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a % b; + } + __host__ __device__ __forceinline__ modulus() {} + __host__ __device__ __forceinline__ modulus(const modulus&) {} + }; + + template struct negate : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a) const + { + return -a; + } + __host__ __device__ __forceinline__ negate() {} + __host__ __device__ __forceinline__ negate(const negate&) {} + }; + + // Comparison Operations + template struct equal_to : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a == b; + } + __host__ __device__ __forceinline__ equal_to() {} + __host__ __device__ __forceinline__ equal_to(const equal_to&) {} + }; + + template struct not_equal_to : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a != b; + } + __host__ __device__ __forceinline__ not_equal_to() {} + __host__ __device__ __forceinline__ not_equal_to(const not_equal_to&) {} + }; + + template struct greater : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a > b; + } + __host__ __device__ __forceinline__ greater() {} + __host__ __device__ __forceinline__ greater(const greater&) {} + }; + + template struct less : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a < b; + } + __host__ __device__ __forceinline__ less() {} + __host__ __device__ __forceinline__ less(const less&) {} + }; + + template struct greater_equal : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a >= b; + } + __host__ __device__ __forceinline__ greater_equal() {} + __host__ __device__ __forceinline__ greater_equal(const greater_equal&) {} + }; + + template struct less_equal : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a <= b; + } + __host__ __device__ __forceinline__ less_equal() {} + __host__ __device__ __forceinline__ less_equal(const less_equal&) {} + }; + + // Logical Operations + template struct logical_and : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a && b; + } + __host__ __device__ __forceinline__ logical_and() {} + __host__ __device__ __forceinline__ logical_and(const logical_and&) {} + }; + + template struct logical_or : binary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a || b; + } + __host__ __device__ __forceinline__ logical_or() {} + __host__ __device__ __forceinline__ logical_or(const logical_or&) {} + }; + + template struct logical_not : unary_function + { + __device__ __forceinline__ bool operator ()(typename TypeTraits::ParameterType a) const + { + return !a; + } + __host__ __device__ __forceinline__ logical_not() {} + __host__ __device__ __forceinline__ logical_not(const logical_not&) {} + }; + + // Bitwise Operations + template struct bit_and : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a & b; + } + __host__ __device__ __forceinline__ bit_and() {} + __host__ __device__ __forceinline__ bit_and(const bit_and&) {} + }; + + template struct bit_or : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a | b; + } + __host__ __device__ __forceinline__ bit_or() {} + __host__ __device__ __forceinline__ bit_or(const bit_or&) {} + }; + + template struct bit_xor : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType a, + typename TypeTraits::ParameterType b) const + { + return a ^ b; + } + __host__ __device__ __forceinline__ bit_xor() {} + __host__ __device__ __forceinline__ bit_xor(const bit_xor&) {} + }; + + template struct bit_not : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType v) const + { + return ~v; + } + __host__ __device__ __forceinline__ bit_not() {} + __host__ __device__ __forceinline__ bit_not(const bit_not&) {} + }; + + // Generalized Identity Operations + template struct identity : unary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType x) const + { + return x; + } + __host__ __device__ __forceinline__ identity() {} + __host__ __device__ __forceinline__ identity(const identity&) {} + }; + + template struct project1st : binary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return lhs; + } + __host__ __device__ __forceinline__ project1st() {} + __host__ __device__ __forceinline__ project1st(const project1st&) {} + }; + + template struct project2nd : binary_function + { + __device__ __forceinline__ typename TypeTraits::ParameterType operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return rhs; + } + __host__ __device__ __forceinline__ project2nd() {} + __host__ __device__ __forceinline__ project2nd(const project2nd&) {} + }; + + // Min/Max Operations + +#define OPENCV_CUDA_IMPLEMENT_MINMAX(name, type, op) \ + template <> struct name : binary_function \ + { \ + __device__ __forceinline__ type operator()(type lhs, type rhs) const {return op(lhs, rhs);} \ + __host__ __device__ __forceinline__ name() {}\ + __host__ __device__ __forceinline__ name(const name&) {}\ + }; + + template struct maximum : binary_function + { + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return max(lhs, rhs); + } + __host__ __device__ __forceinline__ maximum() {} + __host__ __device__ __forceinline__ maximum(const maximum&) {} + }; + + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, uchar, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, schar, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, char, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, ushort, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, short, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, int, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, uint, ::max) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, float, ::fmax) + OPENCV_CUDA_IMPLEMENT_MINMAX(maximum, double, ::fmax) + + template struct minimum : binary_function + { + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType lhs, typename TypeTraits::ParameterType rhs) const + { + return min(lhs, rhs); + } + __host__ __device__ __forceinline__ minimum() {} + __host__ __device__ __forceinline__ minimum(const minimum&) {} + }; + + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, uchar, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, schar, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, char, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, ushort, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, short, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, int, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, uint, ::min) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, float, ::fmin) + OPENCV_CUDA_IMPLEMENT_MINMAX(minimum, double, ::fmin) + +#undef OPENCV_CUDA_IMPLEMENT_MINMAX + + // Math functions + + template struct abs_func : unary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType x) const + { + return abs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned char operator ()(unsigned char x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ signed char operator ()(signed char x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ char operator ()(char x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned short operator ()(unsigned short x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ short operator ()(short x) const + { + return ::abs((int)x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ unsigned int operator ()(unsigned int x) const + { + return x; + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ int operator ()(int x) const + { + return ::abs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ float operator ()(float x) const + { + return ::fabsf(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + template <> struct abs_func : unary_function + { + __device__ __forceinline__ double operator ()(double x) const + { + return ::fabs(x); + } + + __host__ __device__ __forceinline__ abs_func() {} + __host__ __device__ __forceinline__ abs_func(const abs_func&) {} + }; + +#define OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(name, func) \ + template struct name ## _func : unary_function \ + { \ + __device__ __forceinline__ float operator ()(typename TypeTraits::ParameterType v) const \ + { \ + return func ## f(v); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; \ + template <> struct name ## _func : unary_function \ + { \ + __device__ __forceinline__ double operator ()(double v) const \ + { \ + return func(v); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; + +#define OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(name, func) \ + template struct name ## _func : binary_function \ + { \ + __device__ __forceinline__ float operator ()(typename TypeTraits::ParameterType v1, typename TypeTraits::ParameterType v2) const \ + { \ + return func ## f(v1, v2); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; \ + template <> struct name ## _func : binary_function \ + { \ + __device__ __forceinline__ double operator ()(double v1, double v2) const \ + { \ + return func(v1, v2); \ + } \ + __host__ __device__ __forceinline__ name ## _func() {} \ + __host__ __device__ __forceinline__ name ## _func(const name ## _func&) {} \ + }; + + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sqrt, ::sqrt) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp, ::exp) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp2, ::exp2) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(exp10, ::exp10) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log, ::log) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log2, ::log2) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(log10, ::log10) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sin, ::sin) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(cos, ::cos) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(tan, ::tan) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(asin, ::asin) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(acos, ::acos) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(atan, ::atan) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(sinh, ::sinh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(cosh, ::cosh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(tanh, ::tanh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(asinh, ::asinh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(acosh, ::acosh) + OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR(atanh, ::atanh) + + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(hypot, ::hypot) + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(atan2, ::atan2) + OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR(pow, ::pow) + + #undef OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR + #undef OPENCV_CUDA_IMPLEMENT_UN_FUNCTOR_NO_DOUBLE + #undef OPENCV_CUDA_IMPLEMENT_BIN_FUNCTOR + + template struct hypot_sqr_func : binary_function + { + __device__ __forceinline__ T operator ()(typename TypeTraits::ParameterType src1, typename TypeTraits::ParameterType src2) const + { + return src1 * src1 + src2 * src2; + } + __host__ __device__ __forceinline__ hypot_sqr_func() {} + __host__ __device__ __forceinline__ hypot_sqr_func(const hypot_sqr_func&) {} + }; + + // Saturate Cast Functor + template struct saturate_cast_func : unary_function + { + __device__ __forceinline__ D operator ()(typename TypeTraits::ParameterType v) const + { + return saturate_cast(v); + } + __host__ __device__ __forceinline__ saturate_cast_func() {} + __host__ __device__ __forceinline__ saturate_cast_func(const saturate_cast_func&) {} + }; + + // Threshold Functors + template struct thresh_binary_func : unary_function + { + __host__ __device__ __forceinline__ thresh_binary_func(T thresh_, T maxVal_) : thresh(thresh_), maxVal(maxVal_) {} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src > thresh) * maxVal; + } + + __host__ __device__ __forceinline__ thresh_binary_func() {} + __host__ __device__ __forceinline__ thresh_binary_func(const thresh_binary_func& other) + : thresh(other.thresh), maxVal(other.maxVal) {} + + T thresh; + T maxVal; + }; + + template struct thresh_binary_inv_func : unary_function + { + __host__ __device__ __forceinline__ thresh_binary_inv_func(T thresh_, T maxVal_) : thresh(thresh_), maxVal(maxVal_) {} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src <= thresh) * maxVal; + } + + __host__ __device__ __forceinline__ thresh_binary_inv_func() {} + __host__ __device__ __forceinline__ thresh_binary_inv_func(const thresh_binary_inv_func& other) + : thresh(other.thresh), maxVal(other.maxVal) {} + + T thresh; + T maxVal; + }; + + template struct thresh_trunc_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_trunc_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return minimum()(src, thresh); + } + + __host__ __device__ __forceinline__ thresh_trunc_func() {} + __host__ __device__ __forceinline__ thresh_trunc_func(const thresh_trunc_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + template struct thresh_to_zero_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_to_zero_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src > thresh) * src; + } + + __host__ __device__ __forceinline__ thresh_to_zero_func() {} + __host__ __device__ __forceinline__ thresh_to_zero_func(const thresh_to_zero_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + template struct thresh_to_zero_inv_func : unary_function + { + explicit __host__ __device__ __forceinline__ thresh_to_zero_inv_func(T thresh_, T maxVal_ = 0) : thresh(thresh_) {CV_UNUSED(maxVal_);} + + __device__ __forceinline__ T operator()(typename TypeTraits::ParameterType src) const + { + return (src <= thresh) * src; + } + + __host__ __device__ __forceinline__ thresh_to_zero_inv_func() {} + __host__ __device__ __forceinline__ thresh_to_zero_inv_func(const thresh_to_zero_inv_func& other) + : thresh(other.thresh) {} + + T thresh; + }; + + // Function Object Adaptors + template struct unary_negate : unary_function + { + explicit __host__ __device__ __forceinline__ unary_negate(const Predicate& p) : pred(p) {} + + __device__ __forceinline__ bool operator()(typename TypeTraits::ParameterType x) const + { + return !pred(x); + } + + __host__ __device__ __forceinline__ unary_negate() {} + __host__ __device__ __forceinline__ unary_negate(const unary_negate& other) : pred(other.pred) {} + + Predicate pred; + }; + + template __host__ __device__ __forceinline__ unary_negate not1(const Predicate& pred) + { + return unary_negate(pred); + } + + template struct binary_negate : binary_function + { + explicit __host__ __device__ __forceinline__ binary_negate(const Predicate& p) : pred(p) {} + + __device__ __forceinline__ bool operator()(typename TypeTraits::ParameterType x, + typename TypeTraits::ParameterType y) const + { + return !pred(x,y); + } + + __host__ __device__ __forceinline__ binary_negate() {} + __host__ __device__ __forceinline__ binary_negate(const binary_negate& other) : pred(other.pred) {} + + Predicate pred; + }; + + template __host__ __device__ __forceinline__ binary_negate not2(const BinaryPredicate& pred) + { + return binary_negate(pred); + } + + template struct binder1st : unary_function + { + __host__ __device__ __forceinline__ binder1st(const Op& op_, const typename Op::first_argument_type& arg1_) : op(op_), arg1(arg1_) {} + + __device__ __forceinline__ typename Op::result_type operator ()(typename TypeTraits::ParameterType a) const + { + return op(arg1, a); + } + + __host__ __device__ __forceinline__ binder1st() {} + __host__ __device__ __forceinline__ binder1st(const binder1st& other) : op(other.op), arg1(other.arg1) {} + + Op op; + typename Op::first_argument_type arg1; + }; + + template __host__ __device__ __forceinline__ binder1st bind1st(const Op& op, const T& x) + { + return binder1st(op, typename Op::first_argument_type(x)); + } + + template struct binder2nd : unary_function + { + __host__ __device__ __forceinline__ binder2nd(const Op& op_, const typename Op::second_argument_type& arg2_) : op(op_), arg2(arg2_) {} + + __forceinline__ __device__ typename Op::result_type operator ()(typename TypeTraits::ParameterType a) const + { + return op(a, arg2); + } + + __host__ __device__ __forceinline__ binder2nd() {} + __host__ __device__ __forceinline__ binder2nd(const binder2nd& other) : op(other.op), arg2(other.arg2) {} + + Op op; + typename Op::second_argument_type arg2; + }; + + template __host__ __device__ __forceinline__ binder2nd bind2nd(const Op& op, const T& x) + { + return binder2nd(op, typename Op::second_argument_type(x)); + } + + // Functor Traits + template struct IsUnaryFunction + { + typedef char Yes; + struct No {Yes a[2];}; + + template static Yes check(unary_function); + static No check(...); + + static F makeF(); + + enum { value = (sizeof(check(makeF())) == sizeof(Yes)) }; + }; + + template struct IsBinaryFunction + { + typedef char Yes; + struct No {Yes a[2];}; + + template static Yes check(binary_function); + static No check(...); + + static F makeF(); + + enum { value = (sizeof(check(makeF())) == sizeof(Yes)) }; + }; + + namespace functional_detail + { + template struct UnOpShift { enum { shift = 1 }; }; + template struct UnOpShift { enum { shift = 4 }; }; + template struct UnOpShift { enum { shift = 2 }; }; + + template struct DefaultUnaryShift + { + enum { shift = UnOpShift::shift }; + }; + + template struct BinOpShift { enum { shift = 1 }; }; + template struct BinOpShift { enum { shift = 4 }; }; + template struct BinOpShift { enum { shift = 2 }; }; + + template struct DefaultBinaryShift + { + enum { shift = BinOpShift::shift }; + }; + + template ::value> struct ShiftDispatcher; + template struct ShiftDispatcher + { + enum { shift = DefaultUnaryShift::shift }; + }; + template struct ShiftDispatcher + { + enum { shift = DefaultBinaryShift::shift }; + }; + } + + template struct DefaultTransformShift + { + enum { shift = functional_detail::ShiftDispatcher::shift }; + }; + + template struct DefaultTransformFunctorTraits + { + enum { simple_block_dim_x = 16 }; + enum { simple_block_dim_y = 16 }; + + enum { smart_block_dim_x = 16 }; + enum { smart_block_dim_y = 16 }; + enum { smart_shift = DefaultTransformShift::shift }; + }; + + template struct TransformFunctorTraits : DefaultTransformFunctorTraits {}; + +#define OPENCV_CUDA_TRANSFORM_FUNCTOR_TRAITS(type) \ + template <> struct TransformFunctorTraits< type > : DefaultTransformFunctorTraits< type > +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_FUNCTIONAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/limits.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/limits.hpp new file mode 100644 index 0000000..7e15ed6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/limits.hpp @@ -0,0 +1,128 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_LIMITS_HPP +#define OPENCV_CUDA_LIMITS_HPP + +#include +#include +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ +template struct numeric_limits; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static bool min() { return false; } + __device__ __forceinline__ static bool max() { return true; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static signed char min() { return SCHAR_MIN; } + __device__ __forceinline__ static signed char max() { return SCHAR_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned char min() { return 0; } + __device__ __forceinline__ static unsigned char max() { return UCHAR_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static short min() { return SHRT_MIN; } + __device__ __forceinline__ static short max() { return SHRT_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned short min() { return 0; } + __device__ __forceinline__ static unsigned short max() { return USHRT_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static int min() { return INT_MIN; } + __device__ __forceinline__ static int max() { return INT_MAX; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static unsigned int min() { return 0; } + __device__ __forceinline__ static unsigned int max() { return UINT_MAX; } + static const bool is_signed = false; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static float min() { return FLT_MIN; } + __device__ __forceinline__ static float max() { return FLT_MAX; } + __device__ __forceinline__ static float epsilon() { return FLT_EPSILON; } + static const bool is_signed = true; +}; + +template <> struct numeric_limits +{ + __device__ __forceinline__ static double min() { return DBL_MIN; } + __device__ __forceinline__ static double max() { return DBL_MAX; } + __device__ __forceinline__ static double epsilon() { return DBL_EPSILON; } + static const bool is_signed = true; +}; +}}} // namespace cv { namespace cuda { namespace cudev { + +//! @endcond + +#endif // OPENCV_CUDA_LIMITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/reduce.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/reduce.hpp new file mode 100644 index 0000000..5de3650 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/reduce.hpp @@ -0,0 +1,209 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_REDUCE_HPP +#define OPENCV_CUDA_REDUCE_HPP + +#ifndef THRUST_DEBUG // eliminate -Wundef warning +#define THRUST_DEBUG 0 +#endif + +#include +#include "detail/reduce.hpp" +#include "detail/reduce_key_val.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + __device__ __forceinline__ void reduce(volatile T* smem, T& val, unsigned int tid, const Op& op) + { + reduce_detail::Dispatcher::reductor::template reduce(smem, val, tid, op); + } + template + __device__ __forceinline__ void reduce(const thrust::tuple& smem, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& op) + { + reduce_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&>(smem, val, tid, op); + } + + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, volatile V* svals, V& val, unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(volatile K* skeys, K& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, const Cmp& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce&, + const thrust::tuple&, + const Cmp&>(skeys, key, svals, val, tid, cmp); + } + template + __device__ __forceinline__ void reduceKeyVal(const thrust::tuple& skeys, + const thrust::tuple& key, + const thrust::tuple& svals, + const thrust::tuple& val, + unsigned int tid, + const thrust::tuple& cmp) + { + reduce_key_val_detail::Dispatcher::reductor::template reduce< + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple&, + const thrust::tuple& + >(skeys, key, svals, val, tid, cmp); + } + + // smem_tuple + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0) + { + return thrust::make_tuple((volatile T0*) t0); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8); + } + + template + __device__ __forceinline__ + thrust::tuple + smem_tuple(T0* t0, T1* t1, T2* t2, T3* t3, T4* t4, T5* t5, T6* t6, T7* t7, T8* t8, T9* t9) + { + return thrust::make_tuple((volatile T0*) t0, (volatile T1*) t1, (volatile T2*) t2, (volatile T3*) t3, (volatile T4*) t4, (volatile T5*) t5, (volatile T6*) t6, (volatile T7*) t7, (volatile T8*) t8, (volatile T9*) t9); + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_REDUCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/saturate_cast.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/saturate_cast.hpp new file mode 100644 index 0000000..c3a3d1c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/saturate_cast.hpp @@ -0,0 +1,292 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_SATURATE_CAST_HPP +#define OPENCV_CUDA_SATURATE_CAST_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template __device__ __forceinline__ _Tp saturate_cast(uchar v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(schar v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(ushort v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(short v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(uint v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(int v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(float v) { return _Tp(v); } + template __device__ __forceinline__ _Tp saturate_cast(double v) { return _Tp(v); } + + template<> __device__ __forceinline__ uchar saturate_cast(schar v) + { + uint res = 0; + int vi = v; + asm("cvt.sat.u8.s8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.u8.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(ushort v) + { + uint res = 0; + asm("cvt.sat.u8.u16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.u8.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(uint v) + { + uint res = 0; + asm("cvt.sat.u8.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(float v) + { + uint res = 0; + asm("cvt.rni.sat.u8.f32 %0, %1;" : "=r"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ uchar saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + uint res = 0; + asm("cvt.rni.sat.u8.f64 %0, %1;" : "=r"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ schar saturate_cast(uchar v) + { + uint res = 0; + uint vi = v; + asm("cvt.sat.s8.u8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.s8.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(ushort v) + { + uint res = 0; + asm("cvt.sat.s8.u16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.s8.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(uint v) + { + uint res = 0; + asm("cvt.sat.s8.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(float v) + { + uint res = 0; + asm("cvt.rni.sat.s8.f32 %0, %1;" : "=r"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ schar saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + uint res = 0; + asm("cvt.rni.sat.s8.f64 %0, %1;" : "=r"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ ushort saturate_cast(schar v) + { + ushort res = 0; + int vi = v; + asm("cvt.sat.u16.s8 %0, %1;" : "=h"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(short v) + { + ushort res = 0; + asm("cvt.sat.u16.s16 %0, %1;" : "=h"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(int v) + { + ushort res = 0; + asm("cvt.sat.u16.s32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(uint v) + { + ushort res = 0; + asm("cvt.sat.u16.u32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(float v) + { + ushort res = 0; + asm("cvt.rni.sat.u16.f32 %0, %1;" : "=h"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ ushort saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + ushort res = 0; + asm("cvt.rni.sat.u16.f64 %0, %1;" : "=h"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ short saturate_cast(ushort v) + { + short res = 0; + asm("cvt.sat.s16.u16 %0, %1;" : "=h"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(int v) + { + short res = 0; + asm("cvt.sat.s16.s32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(uint v) + { + short res = 0; + asm("cvt.sat.s16.u32 %0, %1;" : "=h"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(float v) + { + short res = 0; + asm("cvt.rni.sat.s16.f32 %0, %1;" : "=h"(res) : "f"(v)); + return res; + } + template<> __device__ __forceinline__ short saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + short res = 0; + asm("cvt.rni.sat.s16.f64 %0, %1;" : "=h"(res) : "d"(v)); + return res; + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ int saturate_cast(uint v) + { + int res = 0; + asm("cvt.sat.s32.u32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ int saturate_cast(float v) + { + return __float2int_rn(v); + } + template<> __device__ __forceinline__ int saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + return __double2int_rn(v); + #else + return saturate_cast((float)v); + #endif + } + + template<> __device__ __forceinline__ uint saturate_cast(schar v) + { + uint res = 0; + int vi = v; + asm("cvt.sat.u32.s8 %0, %1;" : "=r"(res) : "r"(vi)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(short v) + { + uint res = 0; + asm("cvt.sat.u32.s16 %0, %1;" : "=r"(res) : "h"(v)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(int v) + { + uint res = 0; + asm("cvt.sat.u32.s32 %0, %1;" : "=r"(res) : "r"(v)); + return res; + } + template<> __device__ __forceinline__ uint saturate_cast(float v) + { + return __float2uint_rn(v); + } + template<> __device__ __forceinline__ uint saturate_cast(double v) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 130 + return __double2uint_rn(v); + #else + return saturate_cast((float)v); + #endif + } +}}} + +//! @endcond + +#endif /* OPENCV_CUDA_SATURATE_CAST_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/scan.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/scan.hpp new file mode 100644 index 0000000..e128fb0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/scan.hpp @@ -0,0 +1,258 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_SCAN_HPP +#define OPENCV_CUDA_SCAN_HPP + +#include "opencv2/core/cuda/common.hpp" +#include "opencv2/core/cuda/utility.hpp" +#include "opencv2/core/cuda/warp.hpp" +#include "opencv2/core/cuda/warp_shuffle.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + enum ScanKind { EXCLUSIVE = 0, INCLUSIVE = 1 }; + + template struct WarpScan + { + __device__ __forceinline__ WarpScan() {} + __device__ __forceinline__ WarpScan(const WarpScan& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()( volatile T *ptr , const unsigned int idx) + { + const unsigned int lane = idx & 31; + F op; + + if ( lane >= 1) ptr [idx ] = op(ptr [idx - 1], ptr [idx]); + if ( lane >= 2) ptr [idx ] = op(ptr [idx - 2], ptr [idx]); + if ( lane >= 4) ptr [idx ] = op(ptr [idx - 4], ptr [idx]); + if ( lane >= 8) ptr [idx ] = op(ptr [idx - 8], ptr [idx]); + if ( lane >= 16) ptr [idx ] = op(ptr [idx - 16], ptr [idx]); + + if( Kind == INCLUSIVE ) + return ptr [idx]; + else + return (lane > 0) ? ptr [idx - 1] : 0; + } + + __device__ __forceinline__ unsigned int index(const unsigned int tid) + { + return tid; + } + + __device__ __forceinline__ void init(volatile T *ptr){} + + static const int warp_offset = 0; + + typedef WarpScan merge; + }; + + template struct WarpScanNoComp + { + __device__ __forceinline__ WarpScanNoComp() {} + __device__ __forceinline__ WarpScanNoComp(const WarpScanNoComp& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()( volatile T *ptr , const unsigned int idx) + { + const unsigned int lane = threadIdx.x & 31; + F op; + + ptr [idx ] = op(ptr [idx - 1], ptr [idx]); + ptr [idx ] = op(ptr [idx - 2], ptr [idx]); + ptr [idx ] = op(ptr [idx - 4], ptr [idx]); + ptr [idx ] = op(ptr [idx - 8], ptr [idx]); + ptr [idx ] = op(ptr [idx - 16], ptr [idx]); + + if( Kind == INCLUSIVE ) + return ptr [idx]; + else + return (lane > 0) ? ptr [idx - 1] : 0; + } + + __device__ __forceinline__ unsigned int index(const unsigned int tid) + { + return (tid >> warp_log) * warp_smem_stride + 16 + (tid & warp_mask); + } + + __device__ __forceinline__ void init(volatile T *ptr) + { + ptr[threadIdx.x] = 0; + } + + static const int warp_smem_stride = 32 + 16 + 1; + static const int warp_offset = 16; + static const int warp_log = 5; + static const int warp_mask = 31; + + typedef WarpScanNoComp merge; + }; + + template struct BlockScan + { + __device__ __forceinline__ BlockScan() {} + __device__ __forceinline__ BlockScan(const BlockScan& other) { CV_UNUSED(other); } + + __device__ __forceinline__ T operator()(volatile T *ptr) + { + const unsigned int tid = threadIdx.x; + const unsigned int lane = tid & warp_mask; + const unsigned int warp = tid >> warp_log; + + Sc scan; + typename Sc::merge merge_scan; + const unsigned int idx = scan.index(tid); + + T val = scan(ptr, idx); + __syncthreads (); + + if( warp == 0) + scan.init(ptr); + __syncthreads (); + + if( lane == 31 ) + ptr [scan.warp_offset + warp ] = (Kind == INCLUSIVE) ? val : ptr [idx]; + __syncthreads (); + + if( warp == 0 ) + merge_scan(ptr, idx); + __syncthreads(); + + if ( warp > 0) + val = ptr [scan.warp_offset + warp - 1] + val; + __syncthreads (); + + ptr[idx] = val; + __syncthreads (); + + return val ; + } + + static const int warp_log = 5; + static const int warp_mask = 31; + }; + + template + __device__ T warpScanInclusive(T idata, volatile T* s_Data, unsigned int tid) + { + #if __CUDA_ARCH__ >= 300 + const unsigned int laneId = cv::cuda::device::Warp::laneId(); + + // scan on shuffl functions + #pragma unroll + for (int i = 1; i <= (OPENCV_CUDA_WARP_SIZE / 2); i *= 2) + { + const T n = cv::cuda::device::shfl_up(idata, i); + if (laneId >= i) + idata += n; + } + + return idata; + #else + unsigned int pos = 2 * tid - (tid & (OPENCV_CUDA_WARP_SIZE - 1)); + s_Data[pos] = 0; + pos += OPENCV_CUDA_WARP_SIZE; + s_Data[pos] = idata; + + s_Data[pos] += s_Data[pos - 1]; + s_Data[pos] += s_Data[pos - 2]; + s_Data[pos] += s_Data[pos - 4]; + s_Data[pos] += s_Data[pos - 8]; + s_Data[pos] += s_Data[pos - 16]; + + return s_Data[pos]; + #endif + } + + template + __device__ __forceinline__ T warpScanExclusive(T idata, volatile T* s_Data, unsigned int tid) + { + return warpScanInclusive(idata, s_Data, tid) - idata; + } + + template + __device__ T blockScanInclusive(T idata, volatile T* s_Data, unsigned int tid) + { + if (tiNumScanThreads > OPENCV_CUDA_WARP_SIZE) + { + //Bottom-level inclusive warp scan + T warpResult = warpScanInclusive(idata, s_Data, tid); + + //Save top elements of each warp for exclusive warp scan + //sync to wait for warp scans to complete (because s_Data is being overwritten) + __syncthreads(); + if ((tid & (OPENCV_CUDA_WARP_SIZE - 1)) == (OPENCV_CUDA_WARP_SIZE - 1)) + { + s_Data[tid >> OPENCV_CUDA_LOG_WARP_SIZE] = warpResult; + } + + //wait for warp scans to complete + __syncthreads(); + + if (tid < (tiNumScanThreads / OPENCV_CUDA_WARP_SIZE) ) + { + //grab top warp elements + T val = s_Data[tid]; + //calculate exclusive scan and write back to shared memory + s_Data[tid] = warpScanExclusive(val, s_Data, tid); + } + + //return updated warp scans with exclusive scan results + __syncthreads(); + + return warpResult + s_Data[tid >> OPENCV_CUDA_LOG_WARP_SIZE]; + } + else + { + return warpScanInclusive(idata, s_Data, tid); + } + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_SCAN_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/simd_functions.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/simd_functions.hpp new file mode 100644 index 0000000..3d8c2e0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/simd_functions.hpp @@ -0,0 +1,869 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +/* + * Copyright (c) 2013 NVIDIA Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 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. + * + * Neither the name of NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 OPENCV_CUDA_SIMD_FUNCTIONS_HPP +#define OPENCV_CUDA_SIMD_FUNCTIONS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + // 2 + + static __device__ __forceinline__ unsigned int vadd2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vadd2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vadd.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = a ^ b; // sum bits + r = a + b; // actual sum + s = s ^ r; // determine carry-ins for each bit position + s = s & 0x00010000; // carry-in to high word (= carry-out from low word) + r = r - s; // subtract out carry-out from low word + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsub2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vsub2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vsub.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = a ^ b; // sum bits + r = a - b; // actual sum + s = s ^ r; // determine carry-ins for each bit position + s = s & 0x00010000; // borrow to high word + r = r + s; // compensate for borrow from low word + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vabsdiff2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vabsdiff2.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vabsdiff.u32.u32.u32.sat %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u, v; + s = a & 0x0000ffff; // extract low halfword + r = b & 0x0000ffff; // extract low halfword + u = ::max(r, s); // maximum of low halfwords + v = ::min(r, s); // minimum of low halfwords + s = a & 0xffff0000; // extract high halfword + r = b & 0xffff0000; // extract high halfword + t = ::max(r, s); // maximum of high halfwords + s = ::min(r, s); // minimum of high halfwords + r = u | t; // maximum of both halfwords + s = v | s; // minimum of both halfwords + r = r - s; // |a - b| = max(a,b) - min(a,b); + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vavg2(unsigned int a, unsigned int b) + { + unsigned int r, s; + + // HAKMEM #23: a + b = 2 * (a & b) + (a ^ b) ==> + // (a + b) / 2 = (a & b) + ((a ^ b) >> 1) + s = a ^ b; + r = a & b; + s = s & 0xfffefffe; // ensure shift doesn't cross halfword boundaries + s = s >> 1; + s = r + s; + + return s; + } + + static __device__ __forceinline__ unsigned int vavrg2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vavrg2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // HAKMEM #23: a + b = 2 * (a | b) - (a ^ b) ==> + // (a + b + 1) / 2 = (a | b) - ((a ^ b) >> 1) + unsigned int s; + s = a ^ b; + r = a | b; + s = s & 0xfffefffe; // ensure shift doesn't cross half-word boundaries + s = s >> 1; + r = r - s; + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vseteq2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.eq %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x8000 + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r & ~c; // msb = 1, if r was 0x0000 + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpeq2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vseteq2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x8000 + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r & ~c; // msb = 1, if r was 0x0000 + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetge2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.ge %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg2(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpge2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetge2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg2(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetgt2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.gt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg2(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80008000; // msbs = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpgt2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetgt2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg2(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80008000; // msbs = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetle2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.le %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg2(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmple2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetle2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg2(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetlt2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset2.u32.u32.lt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg2(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmplt2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetlt2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg2(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80008000; // msb = carry-outs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetne2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm ("vset2.u32.u32.ne %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r | c; // msb = 1, if r was not 0x0000 + c = c & 0x80008000; // extract msbs + r = c >> 15; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpne2(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetne2(a, b); + c = r << 16; // convert bool + r = c - r; // into mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x0000 if a == b + c = r | 0x80008000; // set msbs, to catch carry out + c = c - 0x00010001; // msb = 0, if r was 0x0000 or 0x8000 + c = r | c; // msb = 1, if r was not 0x0000 + c = c & 0x80008000; // extract msbs + r = c >> 15; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmax2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmax2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmax.u32.u32.u32 %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u; + r = a & 0x0000ffff; // extract low halfword + s = b & 0x0000ffff; // extract low halfword + t = ::max(r, s); // maximum of low halfwords + r = a & 0xffff0000; // extract high halfword + s = b & 0xffff0000; // extract high halfword + u = ::max(r, s); // maximum of high halfwords + r = t | u; // combine halfword maximums + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmin2(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmin2.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmin.u32.u32.u32 %0.h0, %1.h0, %2.h0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.h1, %1.h1, %2.h1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t, u; + r = a & 0x0000ffff; // extract low halfword + s = b & 0x0000ffff; // extract low halfword + t = ::min(r, s); // minimum of low halfwords + r = a & 0xffff0000; // extract high halfword + s = b & 0xffff0000; // extract high halfword + u = ::min(r, s); // minimum of high halfwords + r = t | u; // combine halfword minimums + #endif + + return r; + } + + // 4 + + static __device__ __forceinline__ unsigned int vadd4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vadd4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vadd.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vadd.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t; + s = a ^ b; // sum bits + r = a & 0x7f7f7f7f; // clear msbs + t = b & 0x7f7f7f7f; // clear msbs + s = s & 0x80808080; // msb sum bits + r = r + t; // add without msbs, record carry-out in msbs + r = r ^ s; // sum of msb sum and carry-in bits, w/o carry-out + #endif /* __CUDA_ARCH__ >= 300 */ + + return r; + } + + static __device__ __forceinline__ unsigned int vsub4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vsub4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vsub.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vsub.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s, t; + s = a ^ ~b; // inverted sum bits + r = a | 0x80808080; // set msbs + t = b & 0x7f7f7f7f; // clear msbs + s = s & 0x80808080; // inverted msb sum bits + r = r - t; // subtract w/o msbs, record inverted borrows in msb + r = r ^ s; // combine inverted msb sum bits and borrows + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vavg4(unsigned int a, unsigned int b) + { + unsigned int r, s; + + // HAKMEM #23: a + b = 2 * (a & b) + (a ^ b) ==> + // (a + b) / 2 = (a & b) + ((a ^ b) >> 1) + s = a ^ b; + r = a & b; + s = s & 0xfefefefe; // ensure following shift doesn't cross byte boundaries + s = s >> 1; + s = r + s; + + return s; + } + + static __device__ __forceinline__ unsigned int vavrg4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vavrg4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // HAKMEM #23: a + b = 2 * (a | b) - (a ^ b) ==> + // (a + b + 1) / 2 = (a | b) - ((a ^ b) >> 1) + unsigned int c; + c = a ^ b; + r = a | b; + c = c & 0xfefefefe; // ensure following shift doesn't cross byte boundaries + c = c >> 1; + r = r - c; + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vseteq4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.eq %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + r = r ^ c; // extract msbs, msb = 1 if r < 0x80 + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r & ~c; // msb = 1, if r was 0x00 + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpeq4(unsigned int a, unsigned int b) + { + unsigned int r, t; + + #if __CUDA_ARCH__ >= 300 + r = vseteq4(a, b); + t = r << 8; // convert bool + r = t - r; // to mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + t = a ^ b; // 0x00 if a == b + r = t | 0x80808080; // set msbs, to catch carry out + t = t ^ r; // extract msbs, msb = 1 if t < 0x80 + r = r - 0x01010101; // msb = 0, if t was 0x00 or 0x80 + r = t & ~r; // msb = 1, if t was 0x00 + t = r >> 7; // build mask + t = r - t; // from + r = t | r; // msbs + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetle4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.le %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg4(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmple4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetle4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavrg4(a, b); // (b + ~a + 1) / 2 = (b - a) / 2 + c = c & 0x80808080; // msbs = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetlt4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.lt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg4(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmplt4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetlt4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(a)); + c = vavg4(a, b); // (b + ~a) / 2 = (b - a) / 2 [rounded down] + c = c & 0x80808080; // msbs = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetge4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.ge %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavrg4(a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpge4(unsigned int a, unsigned int b) + { + unsigned int r, s; + + #if __CUDA_ARCH__ >= 300 + r = vsetge4(a, b); + s = r << 8; // convert bool + r = s - r; // to mask + #else + asm ("not.b32 %0,%0;" : "+r"(b)); + r = vavrg4 (a, b); // (a + ~b + 1) / 2 = (a - b) / 2 + r = r & 0x80808080; // msb = carry-outs + s = r >> 7; // build mask + s = r - s; // from + r = s | r; // msbs + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetgt4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.gt %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int c; + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg4(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpgt4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetgt4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + asm("not.b32 %0, %0;" : "+r"(b)); + c = vavg4(a, b); // (a + ~b) / 2 = (a - b) / 2 [rounded down] + c = c & 0x80808080; // msb = carry-outs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vsetne4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vset4.u32.u32.ne %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + unsigned int c; + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r | c; // msb = 1, if r was not 0x00 + c = c & 0x80808080; // extract msbs + r = c >> 7; // convert to bool + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vcmpne4(unsigned int a, unsigned int b) + { + unsigned int r, c; + + #if __CUDA_ARCH__ >= 300 + r = vsetne4(a, b); + c = r << 8; // convert bool + r = c - r; // to mask + #else + // inspired by Alan Mycroft's null-byte detection algorithm: + // null_byte(x) = ((x - 0x01010101) & (~x & 0x80808080)) + r = a ^ b; // 0x00 if a == b + c = r | 0x80808080; // set msbs, to catch carry out + c = c - 0x01010101; // msb = 0, if r was 0x00 or 0x80 + c = r | c; // msb = 1, if r was not 0x00 + c = c & 0x80808080; // extract msbs + r = c >> 7; // convert + r = c - r; // msbs to + r = c | r; // mask + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vabsdiff4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vabsdiff4.u32.u32.u32.sat %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vabsdiff.u32.u32.u32.sat %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vabsdiff.u32.u32.u32.sat %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(a, b); // mask = 0xff if a >= b + r = a ^ b; // + s = (r & s) ^ b; // select a when a >= b, else select b => max(a,b) + r = s ^ r; // select a when b >= a, else select b => min(a,b) + r = s - r; // |a - b| = max(a,b) - min(a,b); + #endif + + return r; + } + + static __device__ __forceinline__ unsigned int vmax4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmax4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmax.u32.u32.u32 %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmax.u32.u32.u32 %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(a, b); // mask = 0xff if a >= b + r = a & s; // select a when b >= a + s = b & ~s; // select b when b < a + r = r | s; // combine byte selections + #endif + + return r; // byte-wise unsigned maximum + } + + static __device__ __forceinline__ unsigned int vmin4(unsigned int a, unsigned int b) + { + unsigned int r = 0; + + #if __CUDA_ARCH__ >= 300 + asm("vmin4.u32.u32.u32 %0, %1, %2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #elif __CUDA_ARCH__ >= 200 + asm("vmin.u32.u32.u32 %0.b0, %1.b0, %2.b0, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b1, %1.b1, %2.b1, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b2, %1.b2, %2.b2, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + asm("vmin.u32.u32.u32 %0.b3, %1.b3, %2.b3, %3;" : "=r"(r) : "r"(a), "r"(b), "r"(r)); + #else + unsigned int s; + s = vcmpge4(b, a); // mask = 0xff if a >= b + r = a & s; // select a when b >= a + s = b & ~s; // select b when b < a + r = r | s; // combine byte selections + #endif + + return r; + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_SIMD_FUNCTIONS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/transform.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/transform.hpp new file mode 100644 index 0000000..42aa6ea --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/transform.hpp @@ -0,0 +1,75 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TRANSFORM_HPP +#define OPENCV_CUDA_TRANSFORM_HPP + +#include "common.hpp" +#include "utility.hpp" +#include "detail/transform_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + static inline void transform(PtrStepSz src, PtrStepSz dst, UnOp op, const Mask& mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + transform_detail::TransformDispatcher::cn == 1 && VecTraits::cn == 1 && ft::smart_shift != 1>::call(src, dst, op, mask, stream); + } + + template + static inline void transform(PtrStepSz src1, PtrStepSz src2, PtrStepSz dst, BinOp op, const Mask& mask, cudaStream_t stream) + { + typedef TransformFunctorTraits ft; + transform_detail::TransformDispatcher::cn == 1 && VecTraits::cn == 1 && VecTraits::cn == 1 && ft::smart_shift != 1>::call(src1, src2, dst, op, mask, stream); + } +}}} + +//! @endcond + +#endif // OPENCV_CUDA_TRANSFORM_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/type_traits.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/type_traits.hpp new file mode 100644 index 0000000..8b7a3fd --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/type_traits.hpp @@ -0,0 +1,90 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_TYPE_TRAITS_HPP +#define OPENCV_CUDA_TYPE_TRAITS_HPP + +#include "detail/type_traits_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct IsSimpleParameter + { + enum {value = type_traits_detail::IsIntegral::value || type_traits_detail::IsFloat::value || + type_traits_detail::PointerTraits::type>::value}; + }; + + template struct TypeTraits + { + typedef typename type_traits_detail::UnConst::type NonConstType; + typedef typename type_traits_detail::UnVolatile::type NonVolatileType; + typedef typename type_traits_detail::UnVolatile::type>::type UnqualifiedType; + typedef typename type_traits_detail::PointerTraits::type PointeeType; + typedef typename type_traits_detail::ReferenceTraits::type ReferredType; + + enum { isConst = type_traits_detail::UnConst::value }; + enum { isVolatile = type_traits_detail::UnVolatile::value }; + + enum { isReference = type_traits_detail::ReferenceTraits::value }; + enum { isPointer = type_traits_detail::PointerTraits::type>::value }; + + enum { isUnsignedInt = type_traits_detail::IsUnsignedIntegral::value }; + enum { isSignedInt = type_traits_detail::IsSignedIntergral::value }; + enum { isIntegral = type_traits_detail::IsIntegral::value }; + enum { isFloat = type_traits_detail::IsFloat::value }; + enum { isArith = isIntegral || isFloat }; + enum { isVec = type_traits_detail::IsVec::value }; + + typedef typename type_traits_detail::Select::value, + T, typename type_traits_detail::AddParameterType::type>::type ParameterType; + }; +}}} + +//! @endcond + +#endif // OPENCV_CUDA_TYPE_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/utility.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/utility.hpp new file mode 100644 index 0000000..7f5db48 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/utility.hpp @@ -0,0 +1,230 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_UTILITY_HPP +#define OPENCV_CUDA_UTILITY_HPP + +#include "saturate_cast.hpp" +#include "datamov_utils.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct CV_EXPORTS ThrustAllocator + { + typedef uchar value_type; + virtual ~ThrustAllocator(); + virtual __device__ __host__ uchar* allocate(size_t numBytes) = 0; + virtual __device__ __host__ void deallocate(uchar* ptr, size_t numBytes) = 0; + static ThrustAllocator& getAllocator(); + static void setAllocator(ThrustAllocator* allocator); + }; + #define OPENCV_CUDA_LOG_WARP_SIZE (5) + #define OPENCV_CUDA_WARP_SIZE (1 << OPENCV_CUDA_LOG_WARP_SIZE) + #define OPENCV_CUDA_LOG_MEM_BANKS ((__CUDA_ARCH__ >= 200) ? 5 : 4) // 32 banks on fermi, 16 on tesla + #define OPENCV_CUDA_MEM_BANKS (1 << OPENCV_CUDA_LOG_MEM_BANKS) + + /////////////////////////////////////////////////////////////////////////////// + // swap + + template void __device__ __host__ __forceinline__ swap(T& a, T& b) + { + const T temp = a; + a = b; + b = temp; + } + + /////////////////////////////////////////////////////////////////////////////// + // Mask Reader + + struct SingleMask + { + explicit __host__ __device__ __forceinline__ SingleMask(PtrStepb mask_) : mask(mask_) {} + __host__ __device__ __forceinline__ SingleMask(const SingleMask& mask_): mask(mask_.mask){} + + __device__ __forceinline__ bool operator()(int y, int x) const + { + return mask.ptr(y)[x] != 0; + } + + PtrStepb mask; + }; + + struct SingleMaskChannels + { + __host__ __device__ __forceinline__ SingleMaskChannels(PtrStepb mask_, int channels_) + : mask(mask_), channels(channels_) {} + __host__ __device__ __forceinline__ SingleMaskChannels(const SingleMaskChannels& mask_) + :mask(mask_.mask), channels(mask_.channels){} + + __device__ __forceinline__ bool operator()(int y, int x) const + { + return mask.ptr(y)[x / channels] != 0; + } + + PtrStepb mask; + int channels; + }; + + struct MaskCollection + { + explicit __host__ __device__ __forceinline__ MaskCollection(PtrStepb* maskCollection_) + : maskCollection(maskCollection_) {} + + __device__ __forceinline__ MaskCollection(const MaskCollection& masks_) + : maskCollection(masks_.maskCollection), curMask(masks_.curMask){} + + __device__ __forceinline__ void next() + { + curMask = *maskCollection++; + } + __device__ __forceinline__ void setMask(int z) + { + curMask = maskCollection[z]; + } + + __device__ __forceinline__ bool operator()(int y, int x) const + { + uchar val; + return curMask.data == 0 || (ForceGlob::Load(curMask.ptr(y), x, val), (val != 0)); + } + + const PtrStepb* maskCollection; + PtrStepb curMask; + }; + + struct WithOutMask + { + __host__ __device__ __forceinline__ WithOutMask(){} + __host__ __device__ __forceinline__ WithOutMask(const WithOutMask&){} + + __device__ __forceinline__ void next() const + { + } + __device__ __forceinline__ void setMask(int) const + { + } + + __device__ __forceinline__ bool operator()(int, int) const + { + return true; + } + + __device__ __forceinline__ bool operator()(int, int, int) const + { + return true; + } + + static __device__ __forceinline__ bool check(int, int) + { + return true; + } + + static __device__ __forceinline__ bool check(int, int, int) + { + return true; + } + }; + + /////////////////////////////////////////////////////////////////////////////// + // Solve linear system + + // solve 2x2 linear system Ax=b + template __device__ __forceinline__ bool solve2x2(const T A[2][2], const T b[2], T x[2]) + { + T det = A[0][0] * A[1][1] - A[1][0] * A[0][1]; + + if (det != 0) + { + double invdet = 1.0 / det; + + x[0] = saturate_cast(invdet * (b[0] * A[1][1] - b[1] * A[0][1])); + + x[1] = saturate_cast(invdet * (A[0][0] * b[1] - A[1][0] * b[0])); + + return true; + } + + return false; + } + + // solve 3x3 linear system Ax=b + template __device__ __forceinline__ bool solve3x3(const T A[3][3], const T b[3], T x[3]) + { + T det = A[0][0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) + - A[0][1] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) + + A[0][2] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]); + + if (det != 0) + { + double invdet = 1.0 / det; + + x[0] = saturate_cast(invdet * + (b[0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) - + A[0][1] * (b[1] * A[2][2] - A[1][2] * b[2] ) + + A[0][2] * (b[1] * A[2][1] - A[1][1] * b[2] ))); + + x[1] = saturate_cast(invdet * + (A[0][0] * (b[1] * A[2][2] - A[1][2] * b[2] ) - + b[0] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) + + A[0][2] * (A[1][0] * b[2] - b[1] * A[2][0]))); + + x[2] = saturate_cast(invdet * + (A[0][0] * (A[1][1] * b[2] - b[1] * A[2][1]) - + A[0][1] * (A[1][0] * b[2] - b[1] * A[2][0]) + + b[0] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]))); + + return true; + } + + return false; + } +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_UTILITY_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_distance.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_distance.hpp new file mode 100644 index 0000000..ef6e510 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_distance.hpp @@ -0,0 +1,232 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_DISTANCE_HPP +#define OPENCV_CUDA_VEC_DISTANCE_HPP + +#include "reduce.hpp" +#include "functional.hpp" +#include "detail/vec_distance_detail.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct L1Dist + { + typedef int value_type; + typedef int result_type; + + __device__ __forceinline__ L1Dist() : mySum(0) {} + + __device__ __forceinline__ void reduceIter(int val1, int val2) + { + mySum = __sad(val1, val2, mySum); + } + + template __device__ __forceinline__ void reduceAll(int* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator int() const + { + return mySum; + } + + int mySum; + }; + template <> struct L1Dist + { + typedef float value_type; + typedef float result_type; + + __device__ __forceinline__ L1Dist() : mySum(0.0f) {} + + __device__ __forceinline__ void reduceIter(float val1, float val2) + { + mySum += ::fabs(val1 - val2); + } + + template __device__ __forceinline__ void reduceAll(float* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator float() const + { + return mySum; + } + + float mySum; + }; + + struct L2Dist + { + typedef float value_type; + typedef float result_type; + + __device__ __forceinline__ L2Dist() : mySum(0.0f) {} + + __device__ __forceinline__ void reduceIter(float val1, float val2) + { + float reg = val1 - val2; + mySum += reg * reg; + } + + template __device__ __forceinline__ void reduceAll(float* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator float() const + { + return sqrtf(mySum); + } + + float mySum; + }; + + struct HammingDist + { + typedef int value_type; + typedef int result_type; + + __device__ __forceinline__ HammingDist() : mySum(0) {} + + __device__ __forceinline__ void reduceIter(int val1, int val2) + { + mySum += __popc(val1 ^ val2); + } + + template __device__ __forceinline__ void reduceAll(int* smem, int tid) + { + reduce(smem, mySum, tid, plus()); + } + + __device__ __forceinline__ operator int() const + { + return mySum; + } + + int mySum; + }; + + // calc distance between two vectors in global memory + template + __device__ void calcVecDiffGlobal(const T1* vec1, const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) + { + for (int i = tid; i < len; i += THREAD_DIM) + { + T1 val1; + ForceGlob::Load(vec1, i, val1); + + T2 val2; + ForceGlob::Load(vec2, i, val2); + + dist.reduceIter(val1, val2); + } + + dist.reduceAll(smem, tid); + } + + // calc distance between two vectors, first vector is cached in register or shared memory, second vector is in global memory + template + __device__ __forceinline__ void calcVecDiffCached(const T1* vecCached, const T2* vecGlob, int len, Dist& dist, typename Dist::result_type* smem, int tid) + { + vec_distance_detail::VecDiffCachedCalculator::calc(vecCached, vecGlob, len, dist, tid); + + dist.reduceAll(smem, tid); + } + + // calc distance between two vectors in global memory + template struct VecDiffGlobal + { + explicit __device__ __forceinline__ VecDiffGlobal(const T1* vec1_, int = 0, void* = 0, int = 0, int = 0) + { + vec1 = vec1_; + } + + template + __device__ __forceinline__ void calc(const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) const + { + calcVecDiffGlobal(vec1, vec2, len, dist, smem, tid); + } + + const T1* vec1; + }; + + // calc distance between two vectors, first vector is cached in register memory, second vector is in global memory + template struct VecDiffCachedRegister + { + template __device__ __forceinline__ VecDiffCachedRegister(const T1* vec1, int len, U* smem, int glob_tid, int tid) + { + if (glob_tid < len) + smem[glob_tid] = vec1[glob_tid]; + __syncthreads(); + + U* vec1ValsPtr = vec1Vals; + + #pragma unroll + for (int i = tid; i < MAX_LEN; i += THREAD_DIM) + *vec1ValsPtr++ = smem[i]; + + __syncthreads(); + } + + template + __device__ __forceinline__ void calc(const T2* vec2, int len, Dist& dist, typename Dist::result_type* smem, int tid) const + { + calcVecDiffCached(vec1Vals, vec2, len, dist, smem, tid); + } + + U vec1Vals[MAX_LEN / THREAD_DIM]; + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_DISTANCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_math.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_math.hpp new file mode 100644 index 0000000..9085b92 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_math.hpp @@ -0,0 +1,930 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VECMATH_HPP +#define OPENCV_CUDA_VECMATH_HPP + +#include "vec_traits.hpp" +#include "saturate_cast.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + +// saturate_cast + +namespace vec_math_detail +{ + template struct SatCastHelper; + template struct SatCastHelper<1, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x)); + } + }; + template struct SatCastHelper<2, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y)); + } + }; + template struct SatCastHelper<3, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y), saturate_cast(v.z)); + } + }; + template struct SatCastHelper<4, VecD> + { + template static __device__ __forceinline__ VecD cast(const VecS& v) + { + typedef typename VecTraits::elem_type D; + return VecTraits::make(saturate_cast(v.x), saturate_cast(v.y), saturate_cast(v.z), saturate_cast(v.w)); + } + }; + + template static __device__ __forceinline__ VecD saturate_cast_helper(const VecS& v) + { + return SatCastHelper::cn, VecD>::cast(v); + } +} + +template static __device__ __forceinline__ T saturate_cast(const uchar1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float1& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double1& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float2& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double2& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float3& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double3& v) {return vec_math_detail::saturate_cast_helper(v);} + +template static __device__ __forceinline__ T saturate_cast(const uchar4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const char4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const ushort4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const short4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const uint4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const int4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const float4& v) {return vec_math_detail::saturate_cast_helper(v);} +template static __device__ __forceinline__ T saturate_cast(const double4& v) {return vec_math_detail::saturate_cast_helper(v);} + +// unary operators + +#define CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(op, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a) \ + { \ + return VecTraits::make(op (a.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y), op (a.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a) \ + { \ + return VecTraits::make(op (a.x), op (a.y), op (a.z), op (a.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(-, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(!, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_OP(~, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_VEC_UNARY_OP + +// unary functions + +#define CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(func_name, func, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a) \ + { \ + return VecTraits::make(func (a.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y), func (a.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a) \ + { \ + return VecTraits::make(func (a.x), func (a.y), func (a.z), func (a.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, char, char) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, short, short) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::abs, int, int) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, /*::abs*/, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::fabsf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(abs, ::fabs, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrtf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sqrt, ::sqrt, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::expf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp, ::exp, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp2, ::exp2, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(exp10, ::exp10, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::logf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log, ::log, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log2, ::log2, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10f, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(log10, ::log10, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sinf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sin, ::sin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cosf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cos, ::cos, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tanf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tan, ::tan, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asinf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asin, ::asin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acosf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acos, ::acos, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atanf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atan, ::atan, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(sinh, ::sinh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::coshf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(cosh, ::cosh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(tanh, ::tanh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(asinh, ::asinh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acoshf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(acosh, ::acosh, double, double) + +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, char, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, short, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, int, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanhf, float, float) +CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC(atanh, ::atanh, double, double) + +#undef CV_CUDEV_IMPLEMENT_VEC_UNARY_FUNC + +// binary operators (vec & vec) + +#define CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(op, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a, const input_type ## 1 & b) \ + { \ + return VecTraits::make(a.x op b.x); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a, const input_type ## 2 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a, const input_type ## 3 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y, a.z op b.z); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a, const input_type ## 4 & b) \ + { \ + return VecTraits::make(a.x op b.x, a.y op b.y, a.z op b.z, a.w op b.w); \ + } + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(+, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(-, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(*, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, uchar, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, char, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, ushort, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, short, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(/, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(==, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(!=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(>=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(<=, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&&, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, char, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, ushort, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, short, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, int, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, uint, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, float, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(||, double, uchar) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(&, uint, uint) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(|, uint, uint) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_OP(^, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_VEC_BINARY_OP + +// binary operators (vec & scalar) + +#define CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(op, input_type, scalar_type, output_type) \ + __device__ __forceinline__ output_type ## 1 operator op(const input_type ## 1 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s); \ + } \ + __device__ __forceinline__ output_type ## 1 operator op(scalar_type s, const input_type ## 1 & b) \ + { \ + return VecTraits::make(s op b.x); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(const input_type ## 2 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s); \ + } \ + __device__ __forceinline__ output_type ## 2 operator op(scalar_type s, const input_type ## 2 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(const input_type ## 3 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s, a.z op s); \ + } \ + __device__ __forceinline__ output_type ## 3 operator op(scalar_type s, const input_type ## 3 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y, s op b.z); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(const input_type ## 4 & a, scalar_type s) \ + { \ + return VecTraits::make(a.x op s, a.y op s, a.z op s, a.w op s); \ + } \ + __device__ __forceinline__ output_type ## 4 operator op(scalar_type s, const input_type ## 4 & b) \ + { \ + return VecTraits::make(s op b.x, s op b.y, s op b.z, s op b.w); \ + } + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(+, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(-, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(*, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(/, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(==, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(!=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(>=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(<=, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&&, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, char, char, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, ushort, ushort, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, short, short, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, int, int, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, uint, uint, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, float, float, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(||, double, double, uchar) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(&, uint, uint, uint) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(|, uint, uint, uint) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP(^, uint, uint, uint) + +#undef CV_CUDEV_IMPLEMENT_SCALAR_BINARY_OP + +// binary function (vec & vec) + +#define CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(func_name, func, input_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a, const input_type ## 1 & b) \ + { \ + return VecTraits::make(func (a.x, b.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a, const input_type ## 2 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a, const input_type ## 3 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y), func (a.z, b.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a, const input_type ## 4 & b) \ + { \ + return VecTraits::make(func (a.x, b.x), func (a.y, b.y), func (a.z, b.z), func (a.w, b.w)); \ + } + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::max, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::fmaxf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(max, ::fmax, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, uchar, uchar) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, char, char) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, ushort, ushort) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, short, short) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, uint, uint) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::min, int, int) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::fminf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(min, ::fmin, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, char, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, short, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, uint, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, int, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypotf, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(hypot, ::hypot, double, double) + +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, uchar, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, char, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, ushort, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, short, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, uint, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, int, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2f, float, float) +CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC(atan2, ::atan2, double, double) + +#undef CV_CUDEV_IMPLEMENT_VEC_BINARY_FUNC + +// binary function (vec & scalar) + +#define CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(func_name, func, input_type, scalar_type, output_type) \ + __device__ __forceinline__ output_type ## 1 func_name(const input_type ## 1 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 1 func_name(scalar_type s, const input_type ## 1 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(const input_type ## 2 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 2 func_name(scalar_type s, const input_type ## 2 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(const input_type ## 3 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s), func ((output_type) a.z, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 3 func_name(scalar_type s, const input_type ## 3 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y), func ((output_type) s, (output_type) b.z)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(const input_type ## 4 & a, scalar_type s) \ + { \ + return VecTraits::make(func ((output_type) a.x, (output_type) s), func ((output_type) a.y, (output_type) s), func ((output_type) a.z, (output_type) s), func ((output_type) a.w, (output_type) s)); \ + } \ + __device__ __forceinline__ output_type ## 4 func_name(scalar_type s, const input_type ## 4 & b) \ + { \ + return VecTraits::make(func ((output_type) s, (output_type) b.x), func ((output_type) s, (output_type) b.y), func ((output_type) s, (output_type) b.z), func ((output_type) s, (output_type) b.w)); \ + } + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::max, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmaxf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(max, ::fmax, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, uchar, uchar, uchar) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, char, char, char) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, ushort, ushort, ushort) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, short, short, short) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, uint, uint, uint) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::min, int, int, int) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fminf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(min, ::fmin, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypotf, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(hypot, ::hypot, double, double, double) + +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, uchar, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, uchar, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, char, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, char, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, ushort, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, ushort, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, short, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, short, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, uint, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, uint, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, int, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, int, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2f, float, float, float) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, float, double, double) +CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC(atan2, ::atan2, double, double, double) + +#undef CV_CUDEV_IMPLEMENT_SCALAR_BINARY_FUNC + +}}} // namespace cv { namespace cuda { namespace device + +//! @endcond + +#endif // OPENCV_CUDA_VECMATH_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_traits.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_traits.hpp new file mode 100644 index 0000000..b5ff281 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/vec_traits.hpp @@ -0,0 +1,288 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_VEC_TRAITS_HPP +#define OPENCV_CUDA_VEC_TRAITS_HPP + +#include "common.hpp" + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template struct TypeVec; + + struct __align__(8) uchar8 + { + uchar a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ uchar8 make_uchar8(uchar a0, uchar a1, uchar a2, uchar a3, uchar a4, uchar a5, uchar a6, uchar a7) + { + uchar8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(8) char8 + { + schar a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ char8 make_char8(schar a0, schar a1, schar a2, schar a3, schar a4, schar a5, schar a6, schar a7) + { + char8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(16) ushort8 + { + ushort a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ ushort8 make_ushort8(ushort a0, ushort a1, ushort a2, ushort a3, ushort a4, ushort a5, ushort a6, ushort a7) + { + ushort8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(16) short8 + { + short a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ short8 make_short8(short a0, short a1, short a2, short a3, short a4, short a5, short a6, short a7) + { + short8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) uint8 + { + uint a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ uint8 make_uint8(uint a0, uint a1, uint a2, uint a3, uint a4, uint a5, uint a6, uint a7) + { + uint8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) int8 + { + int a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ int8 make_int8(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7) + { + int8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct __align__(32) float8 + { + float a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ float8 make_float8(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7) + { + float8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + struct double8 + { + double a0, a1, a2, a3, a4, a5, a6, a7; + }; + static __host__ __device__ __forceinline__ double8 make_double8(double a0, double a1, double a2, double a3, double a4, double a5, double a6, double a7) + { + double8 val = {a0, a1, a2, a3, a4, a5, a6, a7}; + return val; + } + +#define OPENCV_CUDA_IMPLEMENT_TYPE_VEC(type) \ + template<> struct TypeVec { typedef type vec_type; }; \ + template<> struct TypeVec { typedef type ## 1 vec_type; }; \ + template<> struct TypeVec { typedef type ## 2 vec_type; }; \ + template<> struct TypeVec { typedef type ## 2 vec_type; }; \ + template<> struct TypeVec { typedef type ## 3 vec_type; }; \ + template<> struct TypeVec { typedef type ## 3 vec_type; }; \ + template<> struct TypeVec { typedef type ## 4 vec_type; }; \ + template<> struct TypeVec { typedef type ## 4 vec_type; }; \ + template<> struct TypeVec { typedef type ## 8 vec_type; }; \ + template<> struct TypeVec { typedef type ## 8 vec_type; }; + + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(uchar) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(char) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(ushort) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(short) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(int) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(uint) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(float) + OPENCV_CUDA_IMPLEMENT_TYPE_VEC(double) + + #undef OPENCV_CUDA_IMPLEMENT_TYPE_VEC + + template<> struct TypeVec { typedef schar vec_type; }; + template<> struct TypeVec { typedef char2 vec_type; }; + template<> struct TypeVec { typedef char3 vec_type; }; + template<> struct TypeVec { typedef char4 vec_type; }; + template<> struct TypeVec { typedef char8 vec_type; }; + + template<> struct TypeVec { typedef uchar vec_type; }; + template<> struct TypeVec { typedef uchar2 vec_type; }; + template<> struct TypeVec { typedef uchar3 vec_type; }; + template<> struct TypeVec { typedef uchar4 vec_type; }; + template<> struct TypeVec { typedef uchar8 vec_type; }; + + template struct VecTraits; + +#define OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(type) \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=1}; \ + static __device__ __host__ __forceinline__ type all(type v) {return v;} \ + static __device__ __host__ __forceinline__ type make(type x) {return x;} \ + static __device__ __host__ __forceinline__ type make(const type* v) {return *v;} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=1}; \ + static __device__ __host__ __forceinline__ type ## 1 all(type v) {return make_ ## type ## 1(v);} \ + static __device__ __host__ __forceinline__ type ## 1 make(type x) {return make_ ## type ## 1(x);} \ + static __device__ __host__ __forceinline__ type ## 1 make(const type* v) {return make_ ## type ## 1(*v);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=2}; \ + static __device__ __host__ __forceinline__ type ## 2 all(type v) {return make_ ## type ## 2(v, v);} \ + static __device__ __host__ __forceinline__ type ## 2 make(type x, type y) {return make_ ## type ## 2(x, y);} \ + static __device__ __host__ __forceinline__ type ## 2 make(const type* v) {return make_ ## type ## 2(v[0], v[1]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=3}; \ + static __device__ __host__ __forceinline__ type ## 3 all(type v) {return make_ ## type ## 3(v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 3 make(type x, type y, type z) {return make_ ## type ## 3(x, y, z);} \ + static __device__ __host__ __forceinline__ type ## 3 make(const type* v) {return make_ ## type ## 3(v[0], v[1], v[2]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=4}; \ + static __device__ __host__ __forceinline__ type ## 4 all(type v) {return make_ ## type ## 4(v, v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 4 make(type x, type y, type z, type w) {return make_ ## type ## 4(x, y, z, w);} \ + static __device__ __host__ __forceinline__ type ## 4 make(const type* v) {return make_ ## type ## 4(v[0], v[1], v[2], v[3]);} \ + }; \ + template<> struct VecTraits \ + { \ + typedef type elem_type; \ + enum {cn=8}; \ + static __device__ __host__ __forceinline__ type ## 8 all(type v) {return make_ ## type ## 8(v, v, v, v, v, v, v, v);} \ + static __device__ __host__ __forceinline__ type ## 8 make(type a0, type a1, type a2, type a3, type a4, type a5, type a6, type a7) {return make_ ## type ## 8(a0, a1, a2, a3, a4, a5, a6, a7);} \ + static __device__ __host__ __forceinline__ type ## 8 make(const type* v) {return make_ ## type ## 8(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);} \ + }; + + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(uchar) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(ushort) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(short) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(int) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(uint) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(float) + OPENCV_CUDA_IMPLEMENT_VEC_TRAITS(double) + + #undef OPENCV_CUDA_IMPLEMENT_VEC_TRAITS + + template<> struct VecTraits + { + typedef char elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ char all(char v) {return v;} + static __device__ __host__ __forceinline__ char make(char x) {return x;} + static __device__ __host__ __forceinline__ char make(const char* x) {return *x;} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ schar all(schar v) {return v;} + static __device__ __host__ __forceinline__ schar make(schar x) {return x;} + static __device__ __host__ __forceinline__ schar make(const schar* x) {return *x;} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=1}; + static __device__ __host__ __forceinline__ char1 all(schar v) {return make_char1(v);} + static __device__ __host__ __forceinline__ char1 make(schar x) {return make_char1(x);} + static __device__ __host__ __forceinline__ char1 make(const schar* v) {return make_char1(v[0]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=2}; + static __device__ __host__ __forceinline__ char2 all(schar v) {return make_char2(v, v);} + static __device__ __host__ __forceinline__ char2 make(schar x, schar y) {return make_char2(x, y);} + static __device__ __host__ __forceinline__ char2 make(const schar* v) {return make_char2(v[0], v[1]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=3}; + static __device__ __host__ __forceinline__ char3 all(schar v) {return make_char3(v, v, v);} + static __device__ __host__ __forceinline__ char3 make(schar x, schar y, schar z) {return make_char3(x, y, z);} + static __device__ __host__ __forceinline__ char3 make(const schar* v) {return make_char3(v[0], v[1], v[2]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=4}; + static __device__ __host__ __forceinline__ char4 all(schar v) {return make_char4(v, v, v, v);} + static __device__ __host__ __forceinline__ char4 make(schar x, schar y, schar z, schar w) {return make_char4(x, y, z, w);} + static __device__ __host__ __forceinline__ char4 make(const schar* v) {return make_char4(v[0], v[1], v[2], v[3]);} + }; + template<> struct VecTraits + { + typedef schar elem_type; + enum {cn=8}; + static __device__ __host__ __forceinline__ char8 all(schar v) {return make_char8(v, v, v, v, v, v, v, v);} + static __device__ __host__ __forceinline__ char8 make(schar a0, schar a1, schar a2, schar a3, schar a4, schar a5, schar a6, schar a7) {return make_char8(a0, a1, a2, a3, a4, a5, a6, a7);} + static __device__ __host__ __forceinline__ char8 make(const schar* v) {return make_char8(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);} + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif // OPENCV_CUDA_VEC_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp.hpp new file mode 100644 index 0000000..8af7e6a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp.hpp @@ -0,0 +1,139 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_DEVICE_WARP_HPP +#define OPENCV_CUDA_DEVICE_WARP_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + struct Warp + { + enum + { + LOG_WARP_SIZE = 5, + WARP_SIZE = 1 << LOG_WARP_SIZE, + STRIDE = WARP_SIZE + }; + + /** \brief Returns the warp lane ID of the calling thread. */ + static __device__ __forceinline__ unsigned int laneId() + { + unsigned int ret; + asm("mov.u32 %0, %%laneid;" : "=r"(ret) ); + return ret; + } + + template + static __device__ __forceinline__ void fill(It beg, It end, const T& value) + { + for(It t = beg + laneId(); t < end; t += STRIDE) + *t = value; + } + + template + static __device__ __forceinline__ OutIt copy(InIt beg, InIt end, OutIt out) + { + for(InIt t = beg + laneId(); t < end; t += STRIDE, out += STRIDE) + *out = *t; + return out; + } + + template + static __device__ __forceinline__ OutIt transform(InIt beg, InIt end, OutIt out, UnOp op) + { + for(InIt t = beg + laneId(); t < end; t += STRIDE, out += STRIDE) + *out = op(*t); + return out; + } + + template + static __device__ __forceinline__ OutIt transform(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) + { + unsigned int lane = laneId(); + + InIt1 t1 = beg1 + lane; + InIt2 t2 = beg2 + lane; + for(; t1 < end1; t1 += STRIDE, t2 += STRIDE, out += STRIDE) + *out = op(*t1, *t2); + return out; + } + + template + static __device__ __forceinline__ T reduce(volatile T *ptr, BinOp op) + { + const unsigned int lane = laneId(); + + if (lane < 16) + { + T partial = ptr[lane]; + + ptr[lane] = partial = op(partial, ptr[lane + 16]); + ptr[lane] = partial = op(partial, ptr[lane + 8]); + ptr[lane] = partial = op(partial, ptr[lane + 4]); + ptr[lane] = partial = op(partial, ptr[lane + 2]); + ptr[lane] = partial = op(partial, ptr[lane + 1]); + } + + return *ptr; + } + + template + static __device__ __forceinline__ void yota(OutIt beg, OutIt end, T value) + { + unsigned int lane = laneId(); + value += lane; + + for(OutIt t = beg + lane; t < end; t += STRIDE, value += STRIDE) + *t = value; + } + }; +}}} // namespace cv { namespace cuda { namespace cudev + +//! @endcond + +#endif /* OPENCV_CUDA_DEVICE_WARP_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_reduce.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_reduce.hpp new file mode 100644 index 0000000..530303d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_reduce.hpp @@ -0,0 +1,76 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_WARP_REDUCE_HPP__ +#define OPENCV_CUDA_WARP_REDUCE_HPP__ + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ + template + __device__ __forceinline__ T warp_reduce(volatile T *ptr , const unsigned int tid = threadIdx.x) + { + const unsigned int lane = tid & 31; // index of thread in warp (0..31) + + if (lane < 16) + { + T partial = ptr[tid]; + + ptr[tid] = partial = partial + ptr[tid + 16]; + ptr[tid] = partial = partial + ptr[tid + 8]; + ptr[tid] = partial = partial + ptr[tid + 4]; + ptr[tid] = partial = partial + ptr[tid + 2]; + ptr[tid] = partial = partial + ptr[tid + 1]; + } + + return ptr[tid - lane]; + } +}}} // namespace cv { namespace cuda { namespace cudev { + +//! @endcond + +#endif /* OPENCV_CUDA_WARP_REDUCE_HPP__ */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_shuffle.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_shuffle.hpp new file mode 100644 index 0000000..0da54ae --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda/warp_shuffle.hpp @@ -0,0 +1,162 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CUDA_WARP_SHUFFLE_HPP +#define OPENCV_CUDA_WARP_SHUFFLE_HPP + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +namespace cv { namespace cuda { namespace device +{ +#if __CUDACC_VER_MAJOR__ >= 9 +# define __shfl(x, y, z) __shfl_sync(0xFFFFFFFFU, x, y, z) +# define __shfl_up(x, y, z) __shfl_up_sync(0xFFFFFFFFU, x, y, z) +# define __shfl_down(x, y, z) __shfl_down_sync(0xFFFFFFFFU, x, y, z) +#endif + template + __device__ __forceinline__ T shfl(T val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl(val, srcLane, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl(unsigned int val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl((int) val, srcLane, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl(double val, int srcLane, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl(lo, srcLane, width); + hi = __shfl(hi, srcLane, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } + + template + __device__ __forceinline__ T shfl_down(T val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl_down(val, delta, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl_down(unsigned int val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl_down((int) val, delta, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl_down(double val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl_down(lo, delta, width); + hi = __shfl_down(hi, delta, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } + + template + __device__ __forceinline__ T shfl_up(T val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return __shfl_up(val, delta, width); + #else + return T(); + #endif + } + __device__ __forceinline__ unsigned int shfl_up(unsigned int val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + return (unsigned int) __shfl_up((int) val, delta, width); + #else + return 0; + #endif + } + __device__ __forceinline__ double shfl_up(double val, unsigned int delta, int width = warpSize) + { + #if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 300 + int lo = __double2loint(val); + int hi = __double2hiint(val); + + lo = __shfl_up(lo, delta, width); + hi = __shfl_up(hi, delta, width); + + return __hiloint2double(hi, lo); + #else + return 0.0; + #endif + } +}}} + +# undef __shfl +# undef __shfl_up +# undef __shfl_down + +//! @endcond + +#endif // OPENCV_CUDA_WARP_SHUFFLE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_stream_accessor.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_stream_accessor.hpp new file mode 100644 index 0000000..deaf356 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_stream_accessor.hpp @@ -0,0 +1,86 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP +#define OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP + +#ifndef __cplusplus +# error cuda_stream_accessor.hpp header must be compiled as C++ +#endif + +/** @file cuda_stream_accessor.hpp + * This is only header file that depends on CUDA Runtime API. All other headers are independent. + */ + +#include +#include "opencv2/core/cuda.hpp" + +namespace cv +{ + namespace cuda + { + +//! @addtogroup cudacore_struct +//! @{ + + /** @brief Class that enables getting cudaStream_t from cuda::Stream + */ + struct StreamAccessor + { + CV_EXPORTS static cudaStream_t getStream(const Stream& stream); + CV_EXPORTS static Stream wrapStream(cudaStream_t stream); + }; + + /** @brief Class that enables getting cudaEvent_t from cuda::Event + */ + struct EventAccessor + { + CV_EXPORTS static cudaEvent_t getEvent(const Event& event); + CV_EXPORTS static Event wrapEvent(cudaEvent_t event); + }; + +//! @} + + } +} + +#endif /* OPENCV_CORE_CUDA_STREAM_ACCESSOR_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_types.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_types.hpp new file mode 100644 index 0000000..b33f061 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cuda_types.hpp @@ -0,0 +1,144 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CUDA_TYPES_HPP +#define OPENCV_CORE_CUDA_TYPES_HPP + +#ifndef __cplusplus +# error cuda_types.hpp header must be compiled as C++ +#endif + +#if defined(__OPENCV_BUILD) && defined(__clang__) +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif +#if defined(__OPENCV_BUILD) && defined(__GNUC__) && __GNUC__ >= 5 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + +/** @file + * @deprecated Use @ref cudev instead. + */ + +//! @cond IGNORED + +#ifdef __CUDACC__ + #define __CV_CUDA_HOST_DEVICE__ __host__ __device__ __forceinline__ +#else + #define __CV_CUDA_HOST_DEVICE__ +#endif + +namespace cv +{ + namespace cuda + { + + // Simple lightweight structures that encapsulates information about an image on device. + // It is intended to pass to nvcc-compiled code. GpuMat depends on headers that nvcc can't compile + + template struct DevPtr + { + typedef T elem_type; + typedef int index_type; + + enum { elem_size = sizeof(elem_type) }; + + T* data; + + __CV_CUDA_HOST_DEVICE__ DevPtr() : data(0) {} + __CV_CUDA_HOST_DEVICE__ DevPtr(T* data_) : data(data_) {} + + __CV_CUDA_HOST_DEVICE__ size_t elemSize() const { return elem_size; } + __CV_CUDA_HOST_DEVICE__ operator T*() { return data; } + __CV_CUDA_HOST_DEVICE__ operator const T*() const { return data; } + }; + + template struct PtrSz : public DevPtr + { + __CV_CUDA_HOST_DEVICE__ PtrSz() : size(0) {} + __CV_CUDA_HOST_DEVICE__ PtrSz(T* data_, size_t size_) : DevPtr(data_), size(size_) {} + + size_t size; + }; + + template struct PtrStep : public DevPtr + { + __CV_CUDA_HOST_DEVICE__ PtrStep() : step(0) {} + __CV_CUDA_HOST_DEVICE__ PtrStep(T* data_, size_t step_) : DevPtr(data_), step(step_) {} + + size_t step; + + __CV_CUDA_HOST_DEVICE__ T* ptr(int y = 0) { return ( T*)( ( char*)(((DevPtr*)this)->data) + y * step); } + __CV_CUDA_HOST_DEVICE__ const T* ptr(int y = 0) const { return (const T*)( (const char*)(((DevPtr*)this)->data) + y * step); } + + __CV_CUDA_HOST_DEVICE__ T& operator ()(int y, int x) { return ptr(y)[x]; } + __CV_CUDA_HOST_DEVICE__ const T& operator ()(int y, int x) const { return ptr(y)[x]; } + }; + + template struct PtrStepSz : public PtrStep + { + __CV_CUDA_HOST_DEVICE__ PtrStepSz() : cols(0), rows(0) {} + __CV_CUDA_HOST_DEVICE__ PtrStepSz(int rows_, int cols_, T* data_, size_t step_) + : PtrStep(data_, step_), cols(cols_), rows(rows_) {} + + template + explicit PtrStepSz(const PtrStepSz& d) : PtrStep((T*)d.data, d.step), cols(d.cols), rows(d.rows){} + + int cols; + int rows; + }; + + typedef PtrStepSz PtrStepSzb; + typedef PtrStepSz PtrStepSzus; + typedef PtrStepSz PtrStepSzf; + typedef PtrStepSz PtrStepSzi; + + typedef PtrStep PtrStepb; + typedef PtrStep PtrStepus; + typedef PtrStep PtrStepf; + typedef PtrStep PtrStepi; + + } +} + +//! @endcond + +#endif /* OPENCV_CORE_CUDA_TYPES_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_dispatch.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_dispatch.h new file mode 100644 index 0000000..540fbb6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_dispatch.h @@ -0,0 +1,350 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#if defined __OPENCV_BUILD \ + +#include "cv_cpu_config.h" +#include "cv_cpu_helper.h" + +#ifdef CV_CPU_DISPATCH_MODE +#define CV_CPU_OPTIMIZATION_NAMESPACE __CV_CAT(opt_, CV_CPU_DISPATCH_MODE) +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace __CV_CAT(opt_, CV_CPU_DISPATCH_MODE) { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#else +#define CV_CPU_OPTIMIZATION_NAMESPACE cpu_baseline +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#define CV_CPU_BASELINE_MODE 1 +#endif + + +#define __CV_CPU_DISPATCH_CHAIN_END(fn, args, mode, ...) /* done */ +#define __CV_CPU_DISPATCH(fn, args, mode, ...) __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) +#define __CV_CPU_DISPATCH_EXPAND(fn, args, ...) __CV_EXPAND(__CV_CPU_DISPATCH(fn, args, __VA_ARGS__)) +#define CV_CPU_DISPATCH(fn, args, ...) __CV_CPU_DISPATCH_EXPAND(fn, args, __VA_ARGS__, END) // expand macros + + +#if defined CV_ENABLE_INTRINSICS \ + && !defined CV_DISABLE_OPTIMIZATION \ + && !defined __CUDACC__ /* do not include SSE/AVX/NEON headers for NVCC compiler */ \ + +#ifdef CV_CPU_COMPILE_SSE2 +# include +# define CV_MMX 1 +# define CV_SSE 1 +# define CV_SSE2 1 +#endif +#ifdef CV_CPU_COMPILE_SSE3 +# include +# define CV_SSE3 1 +#endif +#ifdef CV_CPU_COMPILE_SSSE3 +# include +# define CV_SSSE3 1 +#endif +#ifdef CV_CPU_COMPILE_SSE4_1 +# include +# define CV_SSE4_1 1 +#endif +#ifdef CV_CPU_COMPILE_SSE4_2 +# include +# define CV_SSE4_2 1 +#endif +#ifdef CV_CPU_COMPILE_POPCNT +# ifdef _MSC_VER +# include +# if defined(_M_X64) +# define CV_POPCNT_U64 _mm_popcnt_u64 +# endif +# define CV_POPCNT_U32 _mm_popcnt_u32 +# else +# include +# if defined(__x86_64__) +# define CV_POPCNT_U64 __builtin_popcountll +# endif +# define CV_POPCNT_U32 __builtin_popcount +# endif +# define CV_POPCNT 1 +#endif +#ifdef CV_CPU_COMPILE_AVX +# include +# define CV_AVX 1 +#endif +#ifdef CV_CPU_COMPILE_FP16 +# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) +# include +# else +# include +# endif +# define CV_FP16 1 +#endif +#ifdef CV_CPU_COMPILE_AVX2 +# include +# define CV_AVX2 1 +#endif +#ifdef CV_CPU_COMPILE_AVX_512F +# include +# define CV_AVX_512F 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_COMMON +# define CV_AVX512_COMMON 1 +# define CV_AVX_512CD 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_KNL +# define CV_AVX512_KNL 1 +# define CV_AVX_512ER 1 +# define CV_AVX_512PF 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_KNM +# define CV_AVX512_KNM 1 +# define CV_AVX_5124FMAPS 1 +# define CV_AVX_5124VNNIW 1 +# define CV_AVX_512VPOPCNTDQ 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_SKX +# define CV_AVX512_SKX 1 +# define CV_AVX_512VL 1 +# define CV_AVX_512BW 1 +# define CV_AVX_512DQ 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_CNL +# define CV_AVX512_CNL 1 +# define CV_AVX_512IFMA 1 +# define CV_AVX_512VBMI 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_CLX +# define CV_AVX512_CLX 1 +# define CV_AVX_512VNNI 1 +#endif +#ifdef CV_CPU_COMPILE_AVX512_ICL +# define CV_AVX512_ICL 1 +# undef CV_AVX_512IFMA +# define CV_AVX_512IFMA 1 +# undef CV_AVX_512VBMI +# define CV_AVX_512VBMI 1 +# undef CV_AVX_512VNNI +# define CV_AVX_512VNNI 1 +# define CV_AVX_512VBMI2 1 +# define CV_AVX_512BITALG 1 +# define CV_AVX_512VPOPCNTDQ 1 +#endif +#ifdef CV_CPU_COMPILE_FMA3 +# define CV_FMA3 1 +#endif + +#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER)) +# include +# include +# define CV_NEON 1 +#elif defined(__ARM_NEON__) || (defined (__ARM_NEON) && defined(__aarch64__)) +# include +# define CV_NEON 1 +#endif + +#if defined(__ARM_NEON__) || defined(__aarch64__) +# include +#endif + +#ifdef CV_CPU_COMPILE_VSX +# include +# undef vector +# undef pixel +# undef bool +# define CV_VSX 1 +#endif + +#ifdef CV_CPU_COMPILE_VSX3 +# define CV_VSX3 1 +#endif + +#ifdef CV_CPU_COMPILE_MSA +# include "hal/msa_macros.h" +# define CV_MSA 1 +#endif + +#ifdef __EMSCRIPTEN__ +# define CV_WASM_SIMD 1 +# include +#endif + +#endif // CV_ENABLE_INTRINSICS && !CV_DISABLE_OPTIMIZATION && !__CUDACC__ + +#if defined CV_CPU_COMPILE_AVX && !defined CV_CPU_BASELINE_COMPILE_AVX +struct VZeroUpperGuard { +#ifdef __GNUC__ + __attribute__((always_inline)) +#endif + inline VZeroUpperGuard() { _mm256_zeroupper(); } +#ifdef __GNUC__ + __attribute__((always_inline)) +#endif + inline ~VZeroUpperGuard() { _mm256_zeroupper(); } +}; +#define __CV_AVX_GUARD VZeroUpperGuard __vzeroupper_guard; CV_UNUSED(__vzeroupper_guard); +#endif + +#ifdef __CV_AVX_GUARD +#define CV_AVX_GUARD __CV_AVX_GUARD +#else +#define CV_AVX_GUARD +#endif + +#endif // __OPENCV_BUILD + + + +#if !defined __OPENCV_BUILD /* Compatibility code */ \ + && !defined __CUDACC__ /* do not include SSE/AVX/NEON headers for NVCC compiler */ +#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) +# include +# define CV_MMX 1 +# define CV_SSE 1 +# define CV_SSE2 1 +#elif defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER)) +# include +# include +# define CV_NEON 1 +#elif defined(__ARM_NEON__) || (defined (__ARM_NEON) && defined(__aarch64__)) +# include +# define CV_NEON 1 +#elif defined(__VSX__) && defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +# include +# undef vector +# undef pixel +# undef bool +# define CV_VSX 1 +#endif + +#ifdef __F16C__ +# include +# define CV_FP16 1 +#endif + +#endif // !__OPENCV_BUILD && !__CUDACC (Compatibility code) + + + +#ifndef CV_MMX +# define CV_MMX 0 +#endif +#ifndef CV_SSE +# define CV_SSE 0 +#endif +#ifndef CV_SSE2 +# define CV_SSE2 0 +#endif +#ifndef CV_SSE3 +# define CV_SSE3 0 +#endif +#ifndef CV_SSSE3 +# define CV_SSSE3 0 +#endif +#ifndef CV_SSE4_1 +# define CV_SSE4_1 0 +#endif +#ifndef CV_SSE4_2 +# define CV_SSE4_2 0 +#endif +#ifndef CV_POPCNT +# define CV_POPCNT 0 +#endif +#ifndef CV_AVX +# define CV_AVX 0 +#endif +#ifndef CV_FP16 +# define CV_FP16 0 +#endif +#ifndef CV_AVX2 +# define CV_AVX2 0 +#endif +#ifndef CV_FMA3 +# define CV_FMA3 0 +#endif +#ifndef CV_AVX_512F +# define CV_AVX_512F 0 +#endif +#ifndef CV_AVX_512BW +# define CV_AVX_512BW 0 +#endif +#ifndef CV_AVX_512CD +# define CV_AVX_512CD 0 +#endif +#ifndef CV_AVX_512DQ +# define CV_AVX_512DQ 0 +#endif +#ifndef CV_AVX_512ER +# define CV_AVX_512ER 0 +#endif +#ifndef CV_AVX_512IFMA +# define CV_AVX_512IFMA 0 +#endif +#define CV_AVX_512IFMA512 CV_AVX_512IFMA // deprecated +#ifndef CV_AVX_512PF +# define CV_AVX_512PF 0 +#endif +#ifndef CV_AVX_512VBMI +# define CV_AVX_512VBMI 0 +#endif +#ifndef CV_AVX_512VL +# define CV_AVX_512VL 0 +#endif +#ifndef CV_AVX_5124FMAPS +# define CV_AVX_5124FMAPS 0 +#endif +#ifndef CV_AVX_5124VNNIW +# define CV_AVX_5124VNNIW 0 +#endif +#ifndef CV_AVX_512VPOPCNTDQ +# define CV_AVX_512VPOPCNTDQ 0 +#endif +#ifndef CV_AVX_512VNNI +# define CV_AVX_512VNNI 0 +#endif +#ifndef CV_AVX_512VBMI2 +# define CV_AVX_512VBMI2 0 +#endif +#ifndef CV_AVX_512BITALG +# define CV_AVX_512BITALG 0 +#endif +#ifndef CV_AVX512_COMMON +# define CV_AVX512_COMMON 0 +#endif +#ifndef CV_AVX512_KNL +# define CV_AVX512_KNL 0 +#endif +#ifndef CV_AVX512_KNM +# define CV_AVX512_KNM 0 +#endif +#ifndef CV_AVX512_SKX +# define CV_AVX512_SKX 0 +#endif +#ifndef CV_AVX512_CNL +# define CV_AVX512_CNL 0 +#endif +#ifndef CV_AVX512_CLX +# define CV_AVX512_CLX 0 +#endif +#ifndef CV_AVX512_ICL +# define CV_AVX512_ICL 0 +#endif + +#ifndef CV_NEON +# define CV_NEON 0 +#endif + +#ifndef CV_VSX +# define CV_VSX 0 +#endif + +#ifndef CV_VSX3 +# define CV_VSX3 0 +#endif + +#ifndef CV_MSA +# define CV_MSA 0 +#endif + +#ifndef CV_WASM_SIMD +# define CV_WASM_SIMD 0 +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_helper.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_helper.h new file mode 100644 index 0000000..aaa89ed --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cv_cpu_helper.h @@ -0,0 +1,487 @@ +// AUTOGENERATED, DO NOT EDIT + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE +# define CV_TRY_SSE 1 +# define CV_CPU_FORCE_SSE 1 +# define CV_CPU_HAS_SUPPORT_SSE 1 +# define CV_CPU_CALL_SSE(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE_(fn, args) return (opt_SSE::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE +# define CV_TRY_SSE 1 +# define CV_CPU_FORCE_SSE 0 +# define CV_CPU_HAS_SUPPORT_SSE (cv::checkHardwareSupport(CV_CPU_SSE)) +# define CV_CPU_CALL_SSE(fn, args) if (CV_CPU_HAS_SUPPORT_SSE) return (opt_SSE::fn args) +# define CV_CPU_CALL_SSE_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE) return (opt_SSE::fn args) +#else +# define CV_TRY_SSE 0 +# define CV_CPU_FORCE_SSE 0 +# define CV_CPU_HAS_SUPPORT_SSE 0 +# define CV_CPU_CALL_SSE(fn, args) +# define CV_CPU_CALL_SSE_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE(fn, args, mode, ...) CV_CPU_CALL_SSE(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE2 +# define CV_TRY_SSE2 1 +# define CV_CPU_FORCE_SSE2 1 +# define CV_CPU_HAS_SUPPORT_SSE2 1 +# define CV_CPU_CALL_SSE2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE2_(fn, args) return (opt_SSE2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE2 +# define CV_TRY_SSE2 1 +# define CV_CPU_FORCE_SSE2 0 +# define CV_CPU_HAS_SUPPORT_SSE2 (cv::checkHardwareSupport(CV_CPU_SSE2)) +# define CV_CPU_CALL_SSE2(fn, args) if (CV_CPU_HAS_SUPPORT_SSE2) return (opt_SSE2::fn args) +# define CV_CPU_CALL_SSE2_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE2) return (opt_SSE2::fn args) +#else +# define CV_TRY_SSE2 0 +# define CV_CPU_FORCE_SSE2 0 +# define CV_CPU_HAS_SUPPORT_SSE2 0 +# define CV_CPU_CALL_SSE2(fn, args) +# define CV_CPU_CALL_SSE2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE2(fn, args, mode, ...) CV_CPU_CALL_SSE2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE3 +# define CV_TRY_SSE3 1 +# define CV_CPU_FORCE_SSE3 1 +# define CV_CPU_HAS_SUPPORT_SSE3 1 +# define CV_CPU_CALL_SSE3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE3_(fn, args) return (opt_SSE3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE3 +# define CV_TRY_SSE3 1 +# define CV_CPU_FORCE_SSE3 0 +# define CV_CPU_HAS_SUPPORT_SSE3 (cv::checkHardwareSupport(CV_CPU_SSE3)) +# define CV_CPU_CALL_SSE3(fn, args) if (CV_CPU_HAS_SUPPORT_SSE3) return (opt_SSE3::fn args) +# define CV_CPU_CALL_SSE3_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE3) return (opt_SSE3::fn args) +#else +# define CV_TRY_SSE3 0 +# define CV_CPU_FORCE_SSE3 0 +# define CV_CPU_HAS_SUPPORT_SSE3 0 +# define CV_CPU_CALL_SSE3(fn, args) +# define CV_CPU_CALL_SSE3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE3(fn, args, mode, ...) CV_CPU_CALL_SSE3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSSE3 +# define CV_TRY_SSSE3 1 +# define CV_CPU_FORCE_SSSE3 1 +# define CV_CPU_HAS_SUPPORT_SSSE3 1 +# define CV_CPU_CALL_SSSE3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSSE3_(fn, args) return (opt_SSSE3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSSE3 +# define CV_TRY_SSSE3 1 +# define CV_CPU_FORCE_SSSE3 0 +# define CV_CPU_HAS_SUPPORT_SSSE3 (cv::checkHardwareSupport(CV_CPU_SSSE3)) +# define CV_CPU_CALL_SSSE3(fn, args) if (CV_CPU_HAS_SUPPORT_SSSE3) return (opt_SSSE3::fn args) +# define CV_CPU_CALL_SSSE3_(fn, args) if (CV_CPU_HAS_SUPPORT_SSSE3) return (opt_SSSE3::fn args) +#else +# define CV_TRY_SSSE3 0 +# define CV_CPU_FORCE_SSSE3 0 +# define CV_CPU_HAS_SUPPORT_SSSE3 0 +# define CV_CPU_CALL_SSSE3(fn, args) +# define CV_CPU_CALL_SSSE3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSSE3(fn, args, mode, ...) CV_CPU_CALL_SSSE3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE4_1 +# define CV_TRY_SSE4_1 1 +# define CV_CPU_FORCE_SSE4_1 1 +# define CV_CPU_HAS_SUPPORT_SSE4_1 1 +# define CV_CPU_CALL_SSE4_1(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE4_1_(fn, args) return (opt_SSE4_1::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE4_1 +# define CV_TRY_SSE4_1 1 +# define CV_CPU_FORCE_SSE4_1 0 +# define CV_CPU_HAS_SUPPORT_SSE4_1 (cv::checkHardwareSupport(CV_CPU_SSE4_1)) +# define CV_CPU_CALL_SSE4_1(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_1) return (opt_SSE4_1::fn args) +# define CV_CPU_CALL_SSE4_1_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_1) return (opt_SSE4_1::fn args) +#else +# define CV_TRY_SSE4_1 0 +# define CV_CPU_FORCE_SSE4_1 0 +# define CV_CPU_HAS_SUPPORT_SSE4_1 0 +# define CV_CPU_CALL_SSE4_1(fn, args) +# define CV_CPU_CALL_SSE4_1_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE4_1(fn, args, mode, ...) CV_CPU_CALL_SSE4_1(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SSE4_2 +# define CV_TRY_SSE4_2 1 +# define CV_CPU_FORCE_SSE4_2 1 +# define CV_CPU_HAS_SUPPORT_SSE4_2 1 +# define CV_CPU_CALL_SSE4_2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SSE4_2_(fn, args) return (opt_SSE4_2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SSE4_2 +# define CV_TRY_SSE4_2 1 +# define CV_CPU_FORCE_SSE4_2 0 +# define CV_CPU_HAS_SUPPORT_SSE4_2 (cv::checkHardwareSupport(CV_CPU_SSE4_2)) +# define CV_CPU_CALL_SSE4_2(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_2) return (opt_SSE4_2::fn args) +# define CV_CPU_CALL_SSE4_2_(fn, args) if (CV_CPU_HAS_SUPPORT_SSE4_2) return (opt_SSE4_2::fn args) +#else +# define CV_TRY_SSE4_2 0 +# define CV_CPU_FORCE_SSE4_2 0 +# define CV_CPU_HAS_SUPPORT_SSE4_2 0 +# define CV_CPU_CALL_SSE4_2(fn, args) +# define CV_CPU_CALL_SSE4_2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SSE4_2(fn, args, mode, ...) CV_CPU_CALL_SSE4_2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_POPCNT +# define CV_TRY_POPCNT 1 +# define CV_CPU_FORCE_POPCNT 1 +# define CV_CPU_HAS_SUPPORT_POPCNT 1 +# define CV_CPU_CALL_POPCNT(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_POPCNT_(fn, args) return (opt_POPCNT::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_POPCNT +# define CV_TRY_POPCNT 1 +# define CV_CPU_FORCE_POPCNT 0 +# define CV_CPU_HAS_SUPPORT_POPCNT (cv::checkHardwareSupport(CV_CPU_POPCNT)) +# define CV_CPU_CALL_POPCNT(fn, args) if (CV_CPU_HAS_SUPPORT_POPCNT) return (opt_POPCNT::fn args) +# define CV_CPU_CALL_POPCNT_(fn, args) if (CV_CPU_HAS_SUPPORT_POPCNT) return (opt_POPCNT::fn args) +#else +# define CV_TRY_POPCNT 0 +# define CV_CPU_FORCE_POPCNT 0 +# define CV_CPU_HAS_SUPPORT_POPCNT 0 +# define CV_CPU_CALL_POPCNT(fn, args) +# define CV_CPU_CALL_POPCNT_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_POPCNT(fn, args, mode, ...) CV_CPU_CALL_POPCNT(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX +# define CV_TRY_AVX 1 +# define CV_CPU_FORCE_AVX 1 +# define CV_CPU_HAS_SUPPORT_AVX 1 +# define CV_CPU_CALL_AVX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX_(fn, args) return (opt_AVX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX +# define CV_TRY_AVX 1 +# define CV_CPU_FORCE_AVX 0 +# define CV_CPU_HAS_SUPPORT_AVX (cv::checkHardwareSupport(CV_CPU_AVX)) +# define CV_CPU_CALL_AVX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX) return (opt_AVX::fn args) +# define CV_CPU_CALL_AVX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX) return (opt_AVX::fn args) +#else +# define CV_TRY_AVX 0 +# define CV_CPU_FORCE_AVX 0 +# define CV_CPU_HAS_SUPPORT_AVX 0 +# define CV_CPU_CALL_AVX(fn, args) +# define CV_CPU_CALL_AVX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX(fn, args, mode, ...) CV_CPU_CALL_AVX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_FP16 +# define CV_TRY_FP16 1 +# define CV_CPU_FORCE_FP16 1 +# define CV_CPU_HAS_SUPPORT_FP16 1 +# define CV_CPU_CALL_FP16(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_FP16_(fn, args) return (opt_FP16::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_FP16 +# define CV_TRY_FP16 1 +# define CV_CPU_FORCE_FP16 0 +# define CV_CPU_HAS_SUPPORT_FP16 (cv::checkHardwareSupport(CV_CPU_FP16)) +# define CV_CPU_CALL_FP16(fn, args) if (CV_CPU_HAS_SUPPORT_FP16) return (opt_FP16::fn args) +# define CV_CPU_CALL_FP16_(fn, args) if (CV_CPU_HAS_SUPPORT_FP16) return (opt_FP16::fn args) +#else +# define CV_TRY_FP16 0 +# define CV_CPU_FORCE_FP16 0 +# define CV_CPU_HAS_SUPPORT_FP16 0 +# define CV_CPU_CALL_FP16(fn, args) +# define CV_CPU_CALL_FP16_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_FP16(fn, args, mode, ...) CV_CPU_CALL_FP16(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX2 +# define CV_TRY_AVX2 1 +# define CV_CPU_FORCE_AVX2 1 +# define CV_CPU_HAS_SUPPORT_AVX2 1 +# define CV_CPU_CALL_AVX2(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX2_(fn, args) return (opt_AVX2::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX2 +# define CV_TRY_AVX2 1 +# define CV_CPU_FORCE_AVX2 0 +# define CV_CPU_HAS_SUPPORT_AVX2 (cv::checkHardwareSupport(CV_CPU_AVX2)) +# define CV_CPU_CALL_AVX2(fn, args) if (CV_CPU_HAS_SUPPORT_AVX2) return (opt_AVX2::fn args) +# define CV_CPU_CALL_AVX2_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX2) return (opt_AVX2::fn args) +#else +# define CV_TRY_AVX2 0 +# define CV_CPU_FORCE_AVX2 0 +# define CV_CPU_HAS_SUPPORT_AVX2 0 +# define CV_CPU_CALL_AVX2(fn, args) +# define CV_CPU_CALL_AVX2_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX2(fn, args, mode, ...) CV_CPU_CALL_AVX2(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_FMA3 +# define CV_TRY_FMA3 1 +# define CV_CPU_FORCE_FMA3 1 +# define CV_CPU_HAS_SUPPORT_FMA3 1 +# define CV_CPU_CALL_FMA3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_FMA3_(fn, args) return (opt_FMA3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_FMA3 +# define CV_TRY_FMA3 1 +# define CV_CPU_FORCE_FMA3 0 +# define CV_CPU_HAS_SUPPORT_FMA3 (cv::checkHardwareSupport(CV_CPU_FMA3)) +# define CV_CPU_CALL_FMA3(fn, args) if (CV_CPU_HAS_SUPPORT_FMA3) return (opt_FMA3::fn args) +# define CV_CPU_CALL_FMA3_(fn, args) if (CV_CPU_HAS_SUPPORT_FMA3) return (opt_FMA3::fn args) +#else +# define CV_TRY_FMA3 0 +# define CV_CPU_FORCE_FMA3 0 +# define CV_CPU_HAS_SUPPORT_FMA3 0 +# define CV_CPU_CALL_FMA3(fn, args) +# define CV_CPU_CALL_FMA3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_FMA3(fn, args, mode, ...) CV_CPU_CALL_FMA3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX_512F +# define CV_TRY_AVX_512F 1 +# define CV_CPU_FORCE_AVX_512F 1 +# define CV_CPU_HAS_SUPPORT_AVX_512F 1 +# define CV_CPU_CALL_AVX_512F(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX_512F_(fn, args) return (opt_AVX_512F::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX_512F +# define CV_TRY_AVX_512F 1 +# define CV_CPU_FORCE_AVX_512F 0 +# define CV_CPU_HAS_SUPPORT_AVX_512F (cv::checkHardwareSupport(CV_CPU_AVX_512F)) +# define CV_CPU_CALL_AVX_512F(fn, args) if (CV_CPU_HAS_SUPPORT_AVX_512F) return (opt_AVX_512F::fn args) +# define CV_CPU_CALL_AVX_512F_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX_512F) return (opt_AVX_512F::fn args) +#else +# define CV_TRY_AVX_512F 0 +# define CV_CPU_FORCE_AVX_512F 0 +# define CV_CPU_HAS_SUPPORT_AVX_512F 0 +# define CV_CPU_CALL_AVX_512F(fn, args) +# define CV_CPU_CALL_AVX_512F_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX_512F(fn, args, mode, ...) CV_CPU_CALL_AVX_512F(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_COMMON +# define CV_TRY_AVX512_COMMON 1 +# define CV_CPU_FORCE_AVX512_COMMON 1 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON 1 +# define CV_CPU_CALL_AVX512_COMMON(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) return (opt_AVX512_COMMON::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_COMMON +# define CV_TRY_AVX512_COMMON 1 +# define CV_CPU_FORCE_AVX512_COMMON 0 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON (cv::checkHardwareSupport(CV_CPU_AVX512_COMMON)) +# define CV_CPU_CALL_AVX512_COMMON(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_COMMON) return (opt_AVX512_COMMON::fn args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_COMMON) return (opt_AVX512_COMMON::fn args) +#else +# define CV_TRY_AVX512_COMMON 0 +# define CV_CPU_FORCE_AVX512_COMMON 0 +# define CV_CPU_HAS_SUPPORT_AVX512_COMMON 0 +# define CV_CPU_CALL_AVX512_COMMON(fn, args) +# define CV_CPU_CALL_AVX512_COMMON_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_COMMON(fn, args, mode, ...) CV_CPU_CALL_AVX512_COMMON(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_KNL +# define CV_TRY_AVX512_KNL 1 +# define CV_CPU_FORCE_AVX512_KNL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL 1 +# define CV_CPU_CALL_AVX512_KNL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) return (opt_AVX512_KNL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_KNL +# define CV_TRY_AVX512_KNL 1 +# define CV_CPU_FORCE_AVX512_KNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL (cv::checkHardwareSupport(CV_CPU_AVX512_KNL)) +# define CV_CPU_CALL_AVX512_KNL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNL) return (opt_AVX512_KNL::fn args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNL) return (opt_AVX512_KNL::fn args) +#else +# define CV_TRY_AVX512_KNL 0 +# define CV_CPU_FORCE_AVX512_KNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNL 0 +# define CV_CPU_CALL_AVX512_KNL(fn, args) +# define CV_CPU_CALL_AVX512_KNL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_KNL(fn, args, mode, ...) CV_CPU_CALL_AVX512_KNL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_KNM +# define CV_TRY_AVX512_KNM 1 +# define CV_CPU_FORCE_AVX512_KNM 1 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM 1 +# define CV_CPU_CALL_AVX512_KNM(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) return (opt_AVX512_KNM::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_KNM +# define CV_TRY_AVX512_KNM 1 +# define CV_CPU_FORCE_AVX512_KNM 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM (cv::checkHardwareSupport(CV_CPU_AVX512_KNM)) +# define CV_CPU_CALL_AVX512_KNM(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNM) return (opt_AVX512_KNM::fn args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_KNM) return (opt_AVX512_KNM::fn args) +#else +# define CV_TRY_AVX512_KNM 0 +# define CV_CPU_FORCE_AVX512_KNM 0 +# define CV_CPU_HAS_SUPPORT_AVX512_KNM 0 +# define CV_CPU_CALL_AVX512_KNM(fn, args) +# define CV_CPU_CALL_AVX512_KNM_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_KNM(fn, args, mode, ...) CV_CPU_CALL_AVX512_KNM(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_SKX +# define CV_TRY_AVX512_SKX 1 +# define CV_CPU_FORCE_AVX512_SKX 1 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX 1 +# define CV_CPU_CALL_AVX512_SKX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) return (opt_AVX512_SKX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_SKX +# define CV_TRY_AVX512_SKX 1 +# define CV_CPU_FORCE_AVX512_SKX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX (cv::checkHardwareSupport(CV_CPU_AVX512_SKX)) +# define CV_CPU_CALL_AVX512_SKX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_SKX) return (opt_AVX512_SKX::fn args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_SKX) return (opt_AVX512_SKX::fn args) +#else +# define CV_TRY_AVX512_SKX 0 +# define CV_CPU_FORCE_AVX512_SKX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_SKX 0 +# define CV_CPU_CALL_AVX512_SKX(fn, args) +# define CV_CPU_CALL_AVX512_SKX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_SKX(fn, args, mode, ...) CV_CPU_CALL_AVX512_SKX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_CNL +# define CV_TRY_AVX512_CNL 1 +# define CV_CPU_FORCE_AVX512_CNL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL 1 +# define CV_CPU_CALL_AVX512_CNL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) return (opt_AVX512_CNL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_CNL +# define CV_TRY_AVX512_CNL 1 +# define CV_CPU_FORCE_AVX512_CNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL (cv::checkHardwareSupport(CV_CPU_AVX512_CNL)) +# define CV_CPU_CALL_AVX512_CNL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CNL) return (opt_AVX512_CNL::fn args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CNL) return (opt_AVX512_CNL::fn args) +#else +# define CV_TRY_AVX512_CNL 0 +# define CV_CPU_FORCE_AVX512_CNL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CNL 0 +# define CV_CPU_CALL_AVX512_CNL(fn, args) +# define CV_CPU_CALL_AVX512_CNL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_CNL(fn, args, mode, ...) CV_CPU_CALL_AVX512_CNL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_CLX +# define CV_TRY_AVX512_CLX 1 +# define CV_CPU_FORCE_AVX512_CLX 1 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX 1 +# define CV_CPU_CALL_AVX512_CLX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) return (opt_AVX512_CLX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_CLX +# define CV_TRY_AVX512_CLX 1 +# define CV_CPU_FORCE_AVX512_CLX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX (cv::checkHardwareSupport(CV_CPU_AVX512_CLX)) +# define CV_CPU_CALL_AVX512_CLX(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CLX) return (opt_AVX512_CLX::fn args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_CLX) return (opt_AVX512_CLX::fn args) +#else +# define CV_TRY_AVX512_CLX 0 +# define CV_CPU_FORCE_AVX512_CLX 0 +# define CV_CPU_HAS_SUPPORT_AVX512_CLX 0 +# define CV_CPU_CALL_AVX512_CLX(fn, args) +# define CV_CPU_CALL_AVX512_CLX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_CLX(fn, args, mode, ...) CV_CPU_CALL_AVX512_CLX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_AVX512_ICL +# define CV_TRY_AVX512_ICL 1 +# define CV_CPU_FORCE_AVX512_ICL 1 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL 1 +# define CV_CPU_CALL_AVX512_ICL(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) return (opt_AVX512_ICL::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_AVX512_ICL +# define CV_TRY_AVX512_ICL 1 +# define CV_CPU_FORCE_AVX512_ICL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL (cv::checkHardwareSupport(CV_CPU_AVX512_ICL)) +# define CV_CPU_CALL_AVX512_ICL(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_ICL) return (opt_AVX512_ICL::fn args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) if (CV_CPU_HAS_SUPPORT_AVX512_ICL) return (opt_AVX512_ICL::fn args) +#else +# define CV_TRY_AVX512_ICL 0 +# define CV_CPU_FORCE_AVX512_ICL 0 +# define CV_CPU_HAS_SUPPORT_AVX512_ICL 0 +# define CV_CPU_CALL_AVX512_ICL(fn, args) +# define CV_CPU_CALL_AVX512_ICL_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_AVX512_ICL(fn, args, mode, ...) CV_CPU_CALL_AVX512_ICL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_NEON +# define CV_TRY_NEON 1 +# define CV_CPU_FORCE_NEON 1 +# define CV_CPU_HAS_SUPPORT_NEON 1 +# define CV_CPU_CALL_NEON(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_NEON_(fn, args) return (opt_NEON::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_NEON +# define CV_TRY_NEON 1 +# define CV_CPU_FORCE_NEON 0 +# define CV_CPU_HAS_SUPPORT_NEON (cv::checkHardwareSupport(CV_CPU_NEON)) +# define CV_CPU_CALL_NEON(fn, args) if (CV_CPU_HAS_SUPPORT_NEON) return (opt_NEON::fn args) +# define CV_CPU_CALL_NEON_(fn, args) if (CV_CPU_HAS_SUPPORT_NEON) return (opt_NEON::fn args) +#else +# define CV_TRY_NEON 0 +# define CV_CPU_FORCE_NEON 0 +# define CV_CPU_HAS_SUPPORT_NEON 0 +# define CV_CPU_CALL_NEON(fn, args) +# define CV_CPU_CALL_NEON_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_NEON(fn, args, mode, ...) CV_CPU_CALL_NEON(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_MSA +# define CV_TRY_MSA 1 +# define CV_CPU_FORCE_MSA 1 +# define CV_CPU_HAS_SUPPORT_MSA 1 +# define CV_CPU_CALL_MSA(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_MSA_(fn, args) return (opt_MSA::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_MSA +# define CV_TRY_MSA 1 +# define CV_CPU_FORCE_MSA 0 +# define CV_CPU_HAS_SUPPORT_MSA (cv::checkHardwareSupport(CV_CPU_MSA)) +# define CV_CPU_CALL_MSA(fn, args) if (CV_CPU_HAS_SUPPORT_MSA) return (opt_MSA::fn args) +# define CV_CPU_CALL_MSA_(fn, args) if (CV_CPU_HAS_SUPPORT_MSA) return (opt_MSA::fn args) +#else +# define CV_TRY_MSA 0 +# define CV_CPU_FORCE_MSA 0 +# define CV_CPU_HAS_SUPPORT_MSA 0 +# define CV_CPU_CALL_MSA(fn, args) +# define CV_CPU_CALL_MSA_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_MSA(fn, args, mode, ...) CV_CPU_CALL_MSA(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_VSX +# define CV_TRY_VSX 1 +# define CV_CPU_FORCE_VSX 1 +# define CV_CPU_HAS_SUPPORT_VSX 1 +# define CV_CPU_CALL_VSX(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_VSX_(fn, args) return (opt_VSX::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_VSX +# define CV_TRY_VSX 1 +# define CV_CPU_FORCE_VSX 0 +# define CV_CPU_HAS_SUPPORT_VSX (cv::checkHardwareSupport(CV_CPU_VSX)) +# define CV_CPU_CALL_VSX(fn, args) if (CV_CPU_HAS_SUPPORT_VSX) return (opt_VSX::fn args) +# define CV_CPU_CALL_VSX_(fn, args) if (CV_CPU_HAS_SUPPORT_VSX) return (opt_VSX::fn args) +#else +# define CV_TRY_VSX 0 +# define CV_CPU_FORCE_VSX 0 +# define CV_CPU_HAS_SUPPORT_VSX 0 +# define CV_CPU_CALL_VSX(fn, args) +# define CV_CPU_CALL_VSX_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_VSX(fn, args, mode, ...) CV_CPU_CALL_VSX(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_VSX3 +# define CV_TRY_VSX3 1 +# define CV_CPU_FORCE_VSX3 1 +# define CV_CPU_HAS_SUPPORT_VSX3 1 +# define CV_CPU_CALL_VSX3(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_VSX3_(fn, args) return (opt_VSX3::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_VSX3 +# define CV_TRY_VSX3 1 +# define CV_CPU_FORCE_VSX3 0 +# define CV_CPU_HAS_SUPPORT_VSX3 (cv::checkHardwareSupport(CV_CPU_VSX3)) +# define CV_CPU_CALL_VSX3(fn, args) if (CV_CPU_HAS_SUPPORT_VSX3) return (opt_VSX3::fn args) +# define CV_CPU_CALL_VSX3_(fn, args) if (CV_CPU_HAS_SUPPORT_VSX3) return (opt_VSX3::fn args) +#else +# define CV_TRY_VSX3 0 +# define CV_CPU_FORCE_VSX3 0 +# define CV_CPU_HAS_SUPPORT_VSX3 0 +# define CV_CPU_CALL_VSX3(fn, args) +# define CV_CPU_CALL_VSX3_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_VSX3(fn, args, mode, ...) CV_CPU_CALL_VSX3(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + +#define CV_CPU_CALL_BASELINE(fn, args) return (cpu_baseline::fn args) +#define __CV_CPU_DISPATCH_CHAIN_BASELINE(fn, args, mode, ...) CV_CPU_CALL_BASELINE(fn, args) /* last in sequence */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvdef.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvdef.h new file mode 100644 index 0000000..7d61e87 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvdef.h @@ -0,0 +1,849 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVDEF_H +#define OPENCV_CORE_CVDEF_H + +#include "opencv2/core/version.hpp" + +//! @addtogroup core_utils +//! @{ + +#ifdef OPENCV_INCLUDE_PORT_FILE // User-provided header file with custom platform configuration +#include OPENCV_INCLUDE_PORT_FILE +#endif + +#if !defined CV_DOXYGEN && !defined CV_IGNORE_DEBUG_BUILD_GUARD +#if (defined(_MSC_VER) && (defined(DEBUG) || defined(_DEBUG))) || \ + (defined(_GLIBCXX_DEBUG) || defined(_GLIBCXX_DEBUG_PEDANTIC)) +// Guard to prevent using of binary incompatible binaries / runtimes +// https://github.com/opencv/opencv/pull/9161 +#define CV__DEBUG_NS_BEGIN namespace debug_build_guard { +#define CV__DEBUG_NS_END } +namespace cv { namespace debug_build_guard { } using namespace debug_build_guard; } +#endif +#endif + +#ifndef CV__DEBUG_NS_BEGIN +#define CV__DEBUG_NS_BEGIN +#define CV__DEBUG_NS_END +#endif + + +#ifdef __OPENCV_BUILD +#include "cvconfig.h" +#endif + +#ifndef __CV_EXPAND +#define __CV_EXPAND(x) x +#endif + +#ifndef __CV_CAT +#define __CV_CAT__(x, y) x ## y +#define __CV_CAT_(x, y) __CV_CAT__(x, y) +#define __CV_CAT(x, y) __CV_CAT_(x, y) +#endif + +#define __CV_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define __CV_VA_NUM_ARGS(...) __CV_VA_NUM_ARGS_HELPER(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#ifdef CV_Func +// keep current value (through OpenCV port file) +#elif defined __GNUC__ || (defined (__cpluscplus) && (__cpluscplus >= 201103)) +#define CV_Func __func__ +#elif defined __clang__ && (__clang_minor__ * 100 + __clang_major__ >= 305) +#define CV_Func __func__ +#elif defined(__STDC_VERSION__) && (__STDC_VERSION >= 199901) +#define CV_Func __func__ +#elif defined _MSC_VER +#define CV_Func __FUNCTION__ +#elif defined(__INTEL_COMPILER) && (_INTEL_COMPILER >= 600) +#define CV_Func __FUNCTION__ +#elif defined __IBMCPP__ && __IBMCPP__ >=500 +#define CV_Func __FUNCTION__ +#elif defined __BORLAND__ && (__BORLANDC__ >= 0x550) +#define CV_Func __FUNC__ +#else +#define CV_Func "" +#endif + +//! @cond IGNORED + +//////////////// static assert ///////////////// +#define CVAUX_CONCAT_EXP(a, b) a##b +#define CVAUX_CONCAT(a, b) CVAUX_CONCAT_EXP(a,b) + +#if defined(__clang__) +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(cxx_static_assert) +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# elif __has_extension(c_static_assert) +# define CV_StaticAssert(condition, reason) _Static_assert((condition), reason " " #condition) +# endif +#elif defined(__GNUC__) +# if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# endif +#elif defined(_MSC_VER) +# if _MSC_VER >= 1600 /* MSVC 10 */ +# define CV_StaticAssert(condition, reason) static_assert((condition), reason " " #condition) +# endif +#endif +#ifndef CV_StaticAssert +# if !defined(__clang__) && defined(__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 302) +# define CV_StaticAssert(condition, reason) ({ extern int __attribute__((error("CV_StaticAssert: " reason " " #condition))) CV_StaticAssert(); ((condition) ? 0 : CV_StaticAssert()); }) +# else +namespace cv { + template struct CV_StaticAssert_failed; + template <> struct CV_StaticAssert_failed { enum { val = 1 }; }; + template struct CV_StaticAssert_test {}; +} +# define CV_StaticAssert(condition, reason)\ + typedef cv::CV_StaticAssert_test< sizeof(cv::CV_StaticAssert_failed< static_cast(condition) >) > CVAUX_CONCAT(CV_StaticAssert_failed_at_, __LINE__) +# endif +#endif + +// Suppress warning "-Wdeprecated-declarations" / C4996 +#if defined(_MSC_VER) + #define CV_DO_PRAGMA(x) __pragma(x) +#elif defined(__GNUC__) + #define CV_DO_PRAGMA(x) _Pragma (#x) +#else + #define CV_DO_PRAGMA(x) +#endif + +#ifdef _MSC_VER +#define CV_SUPPRESS_DEPRECATED_START \ + CV_DO_PRAGMA(warning(push)) \ + CV_DO_PRAGMA(warning(disable: 4996)) +#define CV_SUPPRESS_DEPRECATED_END CV_DO_PRAGMA(warning(pop)) +#elif defined (__clang__) || ((__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 405)) +#define CV_SUPPRESS_DEPRECATED_START \ + CV_DO_PRAGMA(GCC diagnostic push) \ + CV_DO_PRAGMA(GCC diagnostic ignored "-Wdeprecated-declarations") +#define CV_SUPPRESS_DEPRECATED_END CV_DO_PRAGMA(GCC diagnostic pop) +#else +#define CV_SUPPRESS_DEPRECATED_START +#define CV_SUPPRESS_DEPRECATED_END +#endif + +#define CV_UNUSED(name) (void)name + +#if defined __GNUC__ && !defined __EXCEPTIONS +#define CV_TRY +#define CV_CATCH(A, B) for (A B; false; ) +#define CV_CATCH_ALL if (false) +#define CV_THROW(A) abort() +#define CV_RETHROW() abort() +#else +#define CV_TRY try +#define CV_CATCH(A, B) catch(const A & B) +#define CV_CATCH_ALL catch(...) +#define CV_THROW(A) throw A +#define CV_RETHROW() throw +#endif + +//! @endcond + +// undef problematic defines sometimes defined by system headers (windows.h in particular) +#undef small +#undef min +#undef max +#undef abs +#undef Complex + +#if defined __cplusplus +#include +#else +#include +#endif + +#include "opencv2/core/hal/interface.h" + +#if defined __ICL +# define CV_ICC __ICL +#elif defined __ICC +# define CV_ICC __ICC +#elif defined __ECL +# define CV_ICC __ECL +#elif defined __ECC +# define CV_ICC __ECC +#elif defined __INTEL_COMPILER +# define CV_ICC __INTEL_COMPILER +#endif + +#ifndef CV_INLINE +# if defined __cplusplus +# define CV_INLINE static inline +# elif defined _MSC_VER +# define CV_INLINE __inline +# else +# define CV_INLINE static +# endif +#endif + +#ifndef CV_ALWAYS_INLINE +#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define CV_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define CV_ALWAYS_INLINE __forceinline +#else +#define CV_ALWAYS_INLINE inline +#endif +#endif + +#if defined CV_DISABLE_OPTIMIZATION || (defined CV_ICC && !defined CV_ENABLE_UNROLLED) +# define CV_ENABLE_UNROLLED 0 +#else +# define CV_ENABLE_UNROLLED 1 +#endif + +#ifdef __GNUC__ +# define CV_DECL_ALIGNED(x) __attribute__ ((aligned (x))) +#elif defined _MSC_VER +# define CV_DECL_ALIGNED(x) __declspec(align(x)) +#else +# define CV_DECL_ALIGNED(x) +#endif + +/* CPU features and intrinsics support */ +#define CV_CPU_NONE 0 +#define CV_CPU_MMX 1 +#define CV_CPU_SSE 2 +#define CV_CPU_SSE2 3 +#define CV_CPU_SSE3 4 +#define CV_CPU_SSSE3 5 +#define CV_CPU_SSE4_1 6 +#define CV_CPU_SSE4_2 7 +#define CV_CPU_POPCNT 8 +#define CV_CPU_FP16 9 +#define CV_CPU_AVX 10 +#define CV_CPU_AVX2 11 +#define CV_CPU_FMA3 12 + +#define CV_CPU_AVX_512F 13 +#define CV_CPU_AVX_512BW 14 +#define CV_CPU_AVX_512CD 15 +#define CV_CPU_AVX_512DQ 16 +#define CV_CPU_AVX_512ER 17 +#define CV_CPU_AVX_512IFMA512 18 // deprecated +#define CV_CPU_AVX_512IFMA 18 +#define CV_CPU_AVX_512PF 19 +#define CV_CPU_AVX_512VBMI 20 +#define CV_CPU_AVX_512VL 21 +#define CV_CPU_AVX_512VBMI2 22 +#define CV_CPU_AVX_512VNNI 23 +#define CV_CPU_AVX_512BITALG 24 +#define CV_CPU_AVX_512VPOPCNTDQ 25 +#define CV_CPU_AVX_5124VNNIW 26 +#define CV_CPU_AVX_5124FMAPS 27 + +#define CV_CPU_NEON 100 + +#define CV_CPU_MSA 150 + +#define CV_CPU_VSX 200 +#define CV_CPU_VSX3 201 + +// CPU features groups +#define CV_CPU_AVX512_SKX 256 +#define CV_CPU_AVX512_COMMON 257 +#define CV_CPU_AVX512_KNL 258 +#define CV_CPU_AVX512_KNM 259 +#define CV_CPU_AVX512_CNL 260 +#define CV_CPU_AVX512_CLX 261 +#define CV_CPU_AVX512_ICL 262 + +// when adding to this list remember to update the following enum +#define CV_HARDWARE_MAX_FEATURE 512 + +/** @brief Available CPU features. +*/ +enum CpuFeatures { + CPU_MMX = 1, + CPU_SSE = 2, + CPU_SSE2 = 3, + CPU_SSE3 = 4, + CPU_SSSE3 = 5, + CPU_SSE4_1 = 6, + CPU_SSE4_2 = 7, + CPU_POPCNT = 8, + CPU_FP16 = 9, + CPU_AVX = 10, + CPU_AVX2 = 11, + CPU_FMA3 = 12, + + CPU_AVX_512F = 13, + CPU_AVX_512BW = 14, + CPU_AVX_512CD = 15, + CPU_AVX_512DQ = 16, + CPU_AVX_512ER = 17, + CPU_AVX_512IFMA512 = 18, // deprecated + CPU_AVX_512IFMA = 18, + CPU_AVX_512PF = 19, + CPU_AVX_512VBMI = 20, + CPU_AVX_512VL = 21, + CPU_AVX_512VBMI2 = 22, + CPU_AVX_512VNNI = 23, + CPU_AVX_512BITALG = 24, + CPU_AVX_512VPOPCNTDQ= 25, + CPU_AVX_5124VNNIW = 26, + CPU_AVX_5124FMAPS = 27, + + CPU_NEON = 100, + + CPU_MSA = 150, + + CPU_VSX = 200, + CPU_VSX3 = 201, + + CPU_AVX512_SKX = 256, //!< Skylake-X with AVX-512F/CD/BW/DQ/VL + CPU_AVX512_COMMON = 257, //!< Common instructions AVX-512F/CD for all CPUs that support AVX-512 + CPU_AVX512_KNL = 258, //!< Knights Landing with AVX-512F/CD/ER/PF + CPU_AVX512_KNM = 259, //!< Knights Mill with AVX-512F/CD/ER/PF/4FMAPS/4VNNIW/VPOPCNTDQ + CPU_AVX512_CNL = 260, //!< Cannon Lake with AVX-512F/CD/BW/DQ/VL/IFMA/VBMI + CPU_AVX512_CLX = 261, //!< Cascade Lake with AVX-512F/CD/BW/DQ/VL/VNNI + CPU_AVX512_ICL = 262, //!< Ice Lake with AVX-512F/CD/BW/DQ/VL/IFMA/VBMI/VNNI/VBMI2/BITALG/VPOPCNTDQ + + CPU_MAX_FEATURE = 512 // see CV_HARDWARE_MAX_FEATURE +}; + + +#include "cv_cpu_dispatch.h" + +#if !defined(CV_STRONG_ALIGNMENT) && defined(__arm__) && !(defined(__aarch64__) || defined(_M_ARM64)) +// int*, int64* should be propertly aligned pointers on ARMv7 +#define CV_STRONG_ALIGNMENT 1 +#endif +#if !defined(CV_STRONG_ALIGNMENT) +#define CV_STRONG_ALIGNMENT 0 +#endif + +/* fundamental constants */ +#define CV_PI 3.1415926535897932384626433832795 +#define CV_2PI 6.283185307179586476925286766559 +#define CV_LOG2 0.69314718055994530941723212145818 + +#if defined __ARM_FP16_FORMAT_IEEE \ + && !defined __CUDACC__ +# define CV_FP16_TYPE 1 +#else +# define CV_FP16_TYPE 0 +#endif + +typedef union Cv16suf +{ + short i; + ushort u; +#if CV_FP16_TYPE + __fp16 h; +#endif +} +Cv16suf; + +typedef union Cv32suf +{ + int i; + unsigned u; + float f; +} +Cv32suf; + +typedef union Cv64suf +{ + int64 i; + uint64 u; + double f; +} +Cv64suf; + +#ifndef OPENCV_ABI_COMPATIBILITY +#define OPENCV_ABI_COMPATIBILITY 300 +#endif + +#ifdef __OPENCV_BUILD +# define DISABLE_OPENCV_24_COMPATIBILITY +# define OPENCV_DISABLE_DEPRECATED_COMPATIBILITY +#endif + +#ifndef CV_EXPORTS +# if (defined _WIN32 || defined WINCE || defined __CYGWIN__) && defined(CVAPI_EXPORTS) +# define CV_EXPORTS __declspec(dllexport) +# elif defined __GNUC__ && __GNUC__ >= 4 && (defined(CVAPI_EXPORTS) || defined(__APPLE__)) +# define CV_EXPORTS __attribute__ ((visibility ("default"))) +# endif +#endif + +#ifndef CV_EXPORTS +# define CV_EXPORTS +#endif + +#ifdef _MSC_VER +# define CV_EXPORTS_TEMPLATE +#else +# define CV_EXPORTS_TEMPLATE CV_EXPORTS +#endif + +#ifndef CV_DEPRECATED +# if defined(__GNUC__) +# define CV_DEPRECATED __attribute__ ((deprecated)) +# elif defined(_MSC_VER) +# define CV_DEPRECATED __declspec(deprecated) +# else +# define CV_DEPRECATED +# endif +#endif + +#ifndef CV_DEPRECATED_EXTERNAL +# if defined(__OPENCV_BUILD) +# define CV_DEPRECATED_EXTERNAL /* nothing */ +# else +# define CV_DEPRECATED_EXTERNAL CV_DEPRECATED +# endif +#endif + + +#ifndef CV_EXTERN_C +# ifdef __cplusplus +# define CV_EXTERN_C extern "C" +# else +# define CV_EXTERN_C +# endif +#endif + +/* special informative macros for wrapper generators */ +#define CV_EXPORTS_W CV_EXPORTS +#define CV_EXPORTS_W_SIMPLE CV_EXPORTS +#define CV_EXPORTS_AS(synonym) CV_EXPORTS +#define CV_EXPORTS_W_MAP CV_EXPORTS +#define CV_IN_OUT +#define CV_OUT +#define CV_PROP +#define CV_PROP_RW +#define CV_WRAP +#define CV_WRAP_AS(synonym) + +/****************************************************************************************\ +* Matrix type (Mat) * +\****************************************************************************************/ + +#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT) +#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1) +#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1) +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) +#define CV_MAT_CONT_FLAG_SHIFT 14 +#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT) +#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG) +#define CV_IS_CONT_MAT CV_IS_MAT_CONT +#define CV_SUBMAT_FLAG_SHIFT 15 +#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT) +#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG) + +/** Size of each channel item, + 0x8442211 = 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */ +#define CV_ELEM_SIZE1(type) \ + ((((sizeof(size_t)<<28)|0x8442211) >> CV_MAT_DEPTH(type)*4) & 15) + +/** 0x3a50 = 11 10 10 01 01 00 00 ~ array of log2(sizeof(arr_type_elem)) */ +#define CV_ELEM_SIZE(type) \ + (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3)) + +#ifndef MIN +# define MIN(a,b) ((a) > (b) ? (b) : (a)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +/****************************************************************************************\ +* static analysys * +\****************************************************************************************/ + +// In practice, some macro are not processed correctly (noreturn is not detected). +// We need to use simplified definition for them. +#ifndef CV_STATIC_ANALYSIS +# if defined(__KLOCWORK__) || defined(__clang_analyzer__) || defined(__COVERITY__) +# define CV_STATIC_ANALYSIS 1 +# endif +#else +# if defined(CV_STATIC_ANALYSIS) && !(__CV_CAT(1, CV_STATIC_ANALYSIS) == 1) // defined and not empty +# if 0 == CV_STATIC_ANALYSIS +# undef CV_STATIC_ANALYSIS +# endif +# endif +#endif + +/****************************************************************************************\ +* Thread sanitizer * +\****************************************************************************************/ +#ifndef CV_THREAD_SANITIZER +# if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define CV_THREAD_SANITIZER +# endif +# endif +#endif + +/****************************************************************************************\ +* exchange-add operation for atomic operations on reference counters * +\****************************************************************************************/ + +#ifdef CV_XADD + // allow to use user-defined macro +#elif defined __GNUC__ || defined __clang__ +# if defined __clang__ && __clang_major__ >= 3 && !defined __ANDROID__ && !defined __EMSCRIPTEN__ && !defined(__CUDACC__) && !defined __INTEL_COMPILER +# ifdef __ATOMIC_ACQ_REL +# define CV_XADD(addr, delta) __c11_atomic_fetch_add((_Atomic(int)*)(addr), delta, __ATOMIC_ACQ_REL) +# else +# define CV_XADD(addr, delta) __atomic_fetch_add((_Atomic(int)*)(addr), delta, 4) +# endif +# else +# if defined __ATOMIC_ACQ_REL && !defined __clang__ + // version for gcc >= 4.7 +# define CV_XADD(addr, delta) (int)__atomic_fetch_add((unsigned*)(addr), (unsigned)(delta), __ATOMIC_ACQ_REL) +# else +# define CV_XADD(addr, delta) (int)__sync_fetch_and_add((unsigned*)(addr), (unsigned)(delta)) +# endif +# endif +#elif defined _MSC_VER && !defined RC_INVOKED +# include +# define CV_XADD(addr, delta) (int)_InterlockedExchangeAdd((long volatile*)addr, delta) +#else + #ifdef OPENCV_FORCE_UNSAFE_XADD + CV_INLINE CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; } + #else + #error "OpenCV: can't define safe CV_XADD macro for current platform (unsupported). Define CV_XADD macro through custom port header (see OPENCV_INCLUDE_PORT_FILE)" + #endif +#endif + + +/****************************************************************************************\ +* CV_NORETURN attribute * +\****************************************************************************************/ + +#ifndef CV_NORETURN +# if defined(__GNUC__) +# define CV_NORETURN __attribute__((__noreturn__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define CV_NORETURN __declspec(noreturn) +# else +# define CV_NORETURN /* nothing by default */ +# endif +#endif + + +/****************************************************************************************\ +* CV_NODISCARD attribute * +* encourages the compiler to issue a warning if the return value is discarded (C++17) * +\****************************************************************************************/ +#ifndef CV_NODISCARD +# if defined(__GNUC__) +# define CV_NODISCARD __attribute__((__warn_unused_result__)) // at least available with GCC 3.4 +# elif defined(__clang__) && defined(__has_attribute) +# if __has_attribute(__warn_unused_result__) +# define CV_NODISCARD __attribute__((__warn_unused_result__)) +# endif +# endif +#endif +#ifndef CV_NODISCARD +# define CV_NODISCARD /* nothing by default */ +#endif + + +/****************************************************************************************\ +* C++ 11 * +\****************************************************************************************/ +#ifndef CV_CXX11 +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +# define CV_CXX11 1 +# endif +#else +# if CV_CXX11 == 0 +# undef CV_CXX11 +# endif +#endif + + +/****************************************************************************************\ +* C++ Move semantics * +\****************************************************************************************/ + +#ifndef CV_CXX_MOVE_SEMANTICS +# if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || (defined(_MSC_VER) && _MSC_VER >= 1600) +# define CV_CXX_MOVE_SEMANTICS 1 +# elif defined(__clang) +# if __has_feature(cxx_rvalue_references) +# define CV_CXX_MOVE_SEMANTICS 1 +# endif +# endif +#else +# if CV_CXX_MOVE_SEMANTICS == 0 +# undef CV_CXX_MOVE_SEMANTICS +# endif +#endif + +#ifdef CV_CXX_MOVE_SEMANTICS +#define CV_CXX_MOVE(x) std::move(x) +#else +#define CV_CXX_MOVE(x) (x) +#endif + + +/****************************************************************************************\ +* C++11 std::array * +\****************************************************************************************/ + +#ifndef CV_CXX_STD_ARRAY +# if __cplusplus >= 201103L || (defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER >= 1900/*MSVS 2015*/) +# define CV_CXX_STD_ARRAY 1 +# include +# endif +#else +# if CV_CXX_STD_ARRAY == 0 +# undef CV_CXX_STD_ARRAY +# endif +#endif + + +/****************************************************************************************\ +* C++11 override / final * +\****************************************************************************************/ + +#ifndef CV_OVERRIDE +# ifdef CV_CXX11 +# define CV_OVERRIDE override +# endif +#endif +#ifndef CV_OVERRIDE +# define CV_OVERRIDE +#endif + +#ifndef CV_FINAL +# ifdef CV_CXX11 +# define CV_FINAL final +# endif +#endif +#ifndef CV_FINAL +# define CV_FINAL +#endif + +/****************************************************************************************\ +* C++11 noexcept * +\****************************************************************************************/ + +#ifndef CV_NOEXCEPT +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900/*MSVS 2015*/) +# define CV_NOEXCEPT noexcept +# endif +#endif +#ifndef CV_NOEXCEPT +# define CV_NOEXCEPT +#endif + + + +// Integer types portatibility +#ifdef OPENCV_STDINT_HEADER +#include OPENCV_STDINT_HEADER +#elif defined(__cplusplus) +#if defined(_MSC_VER) && _MSC_VER < 1600 /* MSVS 2010 */ +namespace cv { +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +} +#elif defined(_MSC_VER) || __cplusplus >= 201103L +#include +namespace cv { +using std::int8_t; +using std::uint8_t; +using std::int16_t; +using std::uint16_t; +using std::int32_t; +using std::uint32_t; +using std::int64_t; +using std::uint64_t; +} +#else +#include +namespace cv { +typedef ::int8_t int8_t; +typedef ::uint8_t uint8_t; +typedef ::int16_t int16_t; +typedef ::uint16_t uint16_t; +typedef ::int32_t int32_t; +typedef ::uint32_t uint32_t; +typedef ::int64_t int64_t; +typedef ::uint64_t uint64_t; +} +#endif +#else // pure C +#include +#endif + +#ifdef __cplusplus +namespace cv +{ + +class float16_t +{ +public: +#if CV_FP16_TYPE + + float16_t() {} + explicit float16_t(float x) { h = (__fp16)x; } + operator float() const { return (float)h; } + static float16_t fromBits(ushort w) + { + Cv16suf u; + u.u = w; + float16_t result; + result.h = u.h; + return result; + } + static float16_t zero() + { + float16_t result; + result.h = (__fp16)0; + return result; + } + ushort bits() const + { + Cv16suf u; + u.h = h; + return u.u; + } +protected: + __fp16 h; + +#else + float16_t() {} + explicit float16_t(float x) + { + #if CV_FP16 + __m128 v = _mm_load_ss(&x); + w = (ushort)_mm_cvtsi128_si32(_mm_cvtps_ph(v, 0)); + #else + Cv32suf in; + in.f = x; + unsigned sign = in.u & 0x80000000; + in.u ^= sign; + + if( in.u >= 0x47800000 ) + w = (ushort)(in.u > 0x7f800000 ? 0x7e00 : 0x7c00); + else + { + if (in.u < 0x38800000) + { + in.f += 0.5f; + w = (ushort)(in.u - 0x3f000000); + } + else + { + unsigned t = in.u + 0xc8000fff; + w = (ushort)((t + ((in.u >> 13) & 1)) >> 13); + } + } + + w = (ushort)(w | (sign >> 16)); + #endif + } + + operator float() const + { + #if CV_FP16 + float f; + _mm_store_ss(&f, _mm_cvtph_ps(_mm_cvtsi32_si128(w))); + return f; + #else + Cv32suf out; + + unsigned t = ((w & 0x7fff) << 13) + 0x38000000; + unsigned sign = (w & 0x8000) << 16; + unsigned e = w & 0x7c00; + + out.u = t + (1 << 23); + out.u = (e >= 0x7c00 ? t + 0x38000000 : + e == 0 ? (out.f -= 6.103515625e-05f, out.u) : t) | sign; + return out.f; + #endif + } + + static float16_t fromBits(ushort b) + { + float16_t result; + result.w = b; + return result; + } + static float16_t zero() + { + float16_t result; + result.w = (ushort)0; + return result; + } + ushort bits() const { return w; } +protected: + ushort w; + +#endif +}; + +} +#endif + +//! @} + +#ifndef __cplusplus +#include "opencv2/core/fast_math.hpp" // define cvRound(double) +#endif + +#endif // OPENCV_CORE_CVDEF_H diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.hpp new file mode 100644 index 0000000..fbf6d31 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.hpp @@ -0,0 +1,1074 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVSTD_HPP +#define OPENCV_CORE_CVSTD_HPP + +#ifndef __cplusplus +# error cvstd.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include +#include +#include + +#include + +// import useful primitives from stl +# include +# include +# include //for abs(int) +# include + +namespace cv +{ + static inline uchar abs(uchar a) { return a; } + static inline ushort abs(ushort a) { return a; } + static inline unsigned abs(unsigned a) { return a; } + static inline uint64 abs(uint64 a) { return a; } + + using std::min; + using std::max; + using std::abs; + using std::swap; + using std::sqrt; + using std::exp; + using std::pow; + using std::log; +} + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +//////////////////////////// memory management functions //////////////////////////// + +/** @brief Allocates an aligned memory buffer. + +The function allocates the buffer of the specified size and returns it. When the buffer size is 16 +bytes or more, the returned buffer is aligned to 16 bytes. +@param bufSize Allocated buffer size. + */ +CV_EXPORTS void* fastMalloc(size_t bufSize); + +/** @brief Deallocates a memory buffer. + +The function deallocates the buffer allocated with fastMalloc . If NULL pointer is passed, the +function does nothing. C version of the function clears the pointer *pptr* to avoid problems with +double memory deallocation. +@param ptr Pointer to the allocated buffer. + */ +CV_EXPORTS void fastFree(void* ptr); + +/*! + The STL-compliant memory Allocator based on cv::fastMalloc() and cv::fastFree() +*/ +template class Allocator +{ +public: + typedef _Tp value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + template class rebind { typedef Allocator other; }; + + explicit Allocator() {} + ~Allocator() {} + explicit Allocator(Allocator const&) {} + template + explicit Allocator(Allocator const&) {} + + // address + pointer address(reference r) { return &r; } + const_pointer address(const_reference r) { return &r; } + + pointer allocate(size_type count, const void* =0) { return reinterpret_cast(fastMalloc(count * sizeof (_Tp))); } + void deallocate(pointer p, size_type) { fastFree(p); } + + void construct(pointer p, const _Tp& v) { new(static_cast(p)) _Tp(v); } + void destroy(pointer p) { p->~_Tp(); } + + size_type max_size() const { return cv::max(static_cast<_Tp>(-1)/sizeof(_Tp), 1); } +}; + +//! @} core_utils + +//! @cond IGNORED + +namespace detail +{ + +// Metafunction to avoid taking a reference to void. +template +struct RefOrVoid { typedef T& type; }; + +template<> +struct RefOrVoid{ typedef void type; }; + +template<> +struct RefOrVoid{ typedef const void type; }; + +template<> +struct RefOrVoid{ typedef volatile void type; }; + +template<> +struct RefOrVoid{ typedef const volatile void type; }; + +// This class would be private to Ptr, if it didn't have to be a non-template. +struct PtrOwner; + +} + +template +struct DefaultDeleter +{ + void operator () (Y* p) const; +}; + +//! @endcond + +//! @addtogroup core_basic +//! @{ + +/** @brief Template class for smart pointers with shared ownership + +A Ptr\ pretends to be a pointer to an object of type T. Unlike an ordinary pointer, however, the +object will be automatically cleaned up once all Ptr instances pointing to it are destroyed. + +Ptr is similar to boost::shared_ptr that is part of the Boost library +() and std::shared_ptr from +the [C++11](http://en.wikipedia.org/wiki/C++11) standard. + +This class provides the following advantages: +- Default constructor, copy constructor, and assignment operator for an arbitrary C++ class or C + structure. For some objects, like files, windows, mutexes, sockets, and others, a copy + constructor or an assignment operator are difficult to define. For some other objects, like + complex classifiers in OpenCV, copy constructors are absent and not easy to implement. Finally, + some of complex OpenCV and your own data structures may be written in C. However, copy + constructors and default constructors can simplify programming a lot. Besides, they are often + required (for example, by STL containers). By using a Ptr to such an object instead of the + object itself, you automatically get all of the necessary constructors and the assignment + operator. +- *O(1)* complexity of the above-mentioned operations. While some structures, like std::vector, + provide a copy constructor and an assignment operator, the operations may take a considerable + amount of time if the data structures are large. But if the structures are put into a Ptr, the + overhead is small and independent of the data size. +- Automatic and customizable cleanup, even for C structures. See the example below with FILE\*. +- Heterogeneous collections of objects. The standard STL and most other C++ and OpenCV containers + can store only objects of the same type and the same size. The classical solution to store + objects of different types in the same container is to store pointers to the base class (Base\*) + instead but then you lose the automatic memory management. Again, by using Ptr\ instead + of raw pointers, you can solve the problem. + +A Ptr is said to *own* a pointer - that is, for each Ptr there is a pointer that will be deleted +once all Ptr instances that own it are destroyed. The owned pointer may be null, in which case +nothing is deleted. Each Ptr also *stores* a pointer. The stored pointer is the pointer the Ptr +pretends to be; that is, the one you get when you use Ptr::get or the conversion to T\*. It's +usually the same as the owned pointer, but if you use casts or the general shared-ownership +constructor, the two may diverge: the Ptr will still own the original pointer, but will itself point +to something else. + +The owned pointer is treated as a black box. The only thing Ptr needs to know about it is how to +delete it. This knowledge is encapsulated in the *deleter* - an auxiliary object that is associated +with the owned pointer and shared between all Ptr instances that own it. The default deleter is an +instance of DefaultDeleter, which uses the standard C++ delete operator; as such it will work with +any pointer allocated with the standard new operator. + +However, if the pointer must be deleted in a different way, you must specify a custom deleter upon +Ptr construction. A deleter is simply a callable object that accepts the pointer as its sole +argument. For example, if you want to wrap FILE, you may do so as follows: +@code + Ptr f(fopen("myfile.txt", "w"), fclose); + if(!f) throw ...; + fprintf(f, ....); + ... + // the file will be closed automatically by f's destructor. +@endcode +Alternatively, if you want all pointers of a particular type to be deleted the same way, you can +specialize DefaultDeleter::operator() for that type, like this: +@code + namespace cv { + template<> void DefaultDeleter::operator ()(FILE * obj) const + { + fclose(obj); + } + } +@endcode +For convenience, the following types from the OpenCV C API already have such a specialization that +calls the appropriate release function: +- CvCapture +- CvFileStorage +- CvHaarClassifierCascade +- CvMat +- CvMatND +- CvMemStorage +- CvSparseMat +- CvVideoWriter +- IplImage +@note The shared ownership mechanism is implemented with reference counting. As such, cyclic +ownership (e.g. when object a contains a Ptr to object b, which contains a Ptr to object a) will +lead to all involved objects never being cleaned up. Avoid such situations. +@note It is safe to concurrently read (but not write) a Ptr instance from multiple threads and +therefore it is normally safe to use it in multi-threaded applications. The same is true for Mat and +other C++ OpenCV classes that use internal reference counts. +*/ +template +struct Ptr +{ + /** Generic programming support. */ + typedef T element_type; + + /** The default constructor creates a null Ptr - one that owns and stores a null pointer. + */ + Ptr(); + + /** + If p is null, these are equivalent to the default constructor. + Otherwise, these constructors assume ownership of p - that is, the created Ptr owns and stores p + and assumes it is the sole owner of it. Don't use them if p is already owned by another Ptr, or + else p will get deleted twice. + With the first constructor, DefaultDeleter\() becomes the associated deleter (so p will + eventually be deleted with the standard delete operator). Y must be a complete type at the point + of invocation. + With the second constructor, d becomes the associated deleter. + Y\* must be convertible to T\*. + @param p Pointer to own. + @note It is often easier to use makePtr instead. + */ + template +#ifdef DISABLE_OPENCV_24_COMPATIBILITY + explicit +#endif + Ptr(Y* p); + + /** @overload + @param d Deleter to use for the owned pointer. + @param p Pointer to own. + */ + template + Ptr(Y* p, D d); + + /** + These constructors create a Ptr that shares ownership with another Ptr - that is, own the same + pointer as o. + With the first two, the same pointer is stored, as well; for the second, Y\* must be convertible + to T\*. + With the third, p is stored, and Y may be any type. This constructor allows to have completely + unrelated owned and stored pointers, and should be used with care to avoid confusion. A relatively + benign use is to create a non-owning Ptr, like this: + @code + ptr = Ptr(Ptr(), dont_delete_me); // owns nothing; will not delete the pointer. + @endcode + @param o Ptr to share ownership with. + */ + Ptr(const Ptr& o); + + /** @overload + @param o Ptr to share ownership with. + */ + template + Ptr(const Ptr& o); + + /** @overload + @param o Ptr to share ownership with. + @param p Pointer to store. + */ + template + Ptr(const Ptr& o, T* p); + + /** The destructor is equivalent to calling Ptr::release. */ + ~Ptr(); + + /** + Assignment replaces the current Ptr instance with one that owns and stores same pointers as o and + then destroys the old instance. + @param o Ptr to share ownership with. + */ + Ptr& operator = (const Ptr& o); + + /** @overload */ + template + Ptr& operator = (const Ptr& o); + + /** If no other Ptr instance owns the owned pointer, deletes it with the associated deleter. Then sets + both the owned and the stored pointers to NULL. + */ + void release(); + + /** + `ptr.reset(...)` is equivalent to `ptr = Ptr(...)`. + @param p Pointer to own. + */ + template + void reset(Y* p); + + /** @overload + @param d Deleter to use for the owned pointer. + @param p Pointer to own. + */ + template + void reset(Y* p, D d); + + /** + Swaps the owned and stored pointers (and deleters, if any) of this and o. + @param o Ptr to swap with. + */ + void swap(Ptr& o); + + /** Returns the stored pointer. */ + T* get() const; + + /** Ordinary pointer emulation. */ + typename detail::RefOrVoid::type operator * () const; + + /** Ordinary pointer emulation. */ + T* operator -> () const; + + /** Equivalent to get(). */ + operator T* () const; + + /** ptr.empty() is equivalent to `!ptr.get()`. */ + bool empty() const; + + /** Returns a Ptr that owns the same pointer as this, and stores the same + pointer as this, except converted via static_cast to Y*. + */ + template + Ptr staticCast() const; + + /** Ditto for const_cast. */ + template + Ptr constCast() const; + + /** Ditto for dynamic_cast. */ + template + Ptr dynamicCast() const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Ptr(Ptr&& o); + Ptr& operator = (Ptr&& o); +#endif + +private: + detail::PtrOwner* owner; + T* stored; + + template + friend struct Ptr; // have to do this for the cross-type copy constructor +}; + +/** Equivalent to ptr1.swap(ptr2). Provided to help write generic algorithms. */ +template +void swap(Ptr& ptr1, Ptr& ptr2); + +/** Return whether ptr1.get() and ptr2.get() are equal and not equal, respectively. */ +template +bool operator == (const Ptr& ptr1, const Ptr& ptr2); +template +bool operator != (const Ptr& ptr1, const Ptr& ptr2); + +/** `makePtr(...)` is equivalent to `Ptr(new T(...))`. It is shorter than the latter, and it's +marginally safer than using a constructor or Ptr::reset, since it ensures that the owned pointer +is new and thus not owned by any other Ptr instance. +Unfortunately, perfect forwarding is impossible to implement in C++03, and so makePtr is limited +to constructors of T that have up to 10 arguments, none of which are non-const references. + */ +template +Ptr makePtr(); +/** @overload */ +template +Ptr makePtr(const A1& a1); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9); +/** @overload */ +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10); + +//////////////////////////////// string class //////////////////////////////// + +class CV_EXPORTS FileNode; //for string constructor from FileNode + +class CV_EXPORTS String +{ +public: + typedef char value_type; + typedef char& reference; + typedef const char& const_reference; + typedef char* pointer; + typedef const char* const_pointer; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef char* iterator; + typedef const char* const_iterator; + + static const size_t npos = size_t(-1); + + String(); + String(const String& str); + String(const String& str, size_t pos, size_t len = npos); + String(const char* s); + String(const char* s, size_t n); + String(size_t n, char c); + String(const char* first, const char* last); + template String(Iterator first, Iterator last); + explicit String(const FileNode& fn); + ~String(); + + String& operator=(const String& str); + String& operator=(const char* s); + String& operator=(char c); + + String& operator+=(const String& str); + String& operator+=(const char* s); + String& operator+=(char c); + + size_t size() const; + size_t length() const; + + char operator[](size_t idx) const; + char operator[](int idx) const; + + const char* begin() const; + const char* end() const; + + const char* c_str() const; + + bool empty() const; + void clear(); + + int compare(const char* s) const; + int compare(const String& str) const; + + void swap(String& str); + String substr(size_t pos = 0, size_t len = npos) const; + + size_t find(const char* s, size_t pos, size_t n) const; + size_t find(char c, size_t pos = 0) const; + size_t find(const String& str, size_t pos = 0) const; + size_t find(const char* s, size_t pos = 0) const; + + size_t rfind(const char* s, size_t pos, size_t n) const; + size_t rfind(char c, size_t pos = npos) const; + size_t rfind(const String& str, size_t pos = npos) const; + size_t rfind(const char* s, size_t pos = npos) const; + + size_t find_first_of(const char* s, size_t pos, size_t n) const; + size_t find_first_of(char c, size_t pos = 0) const; + size_t find_first_of(const String& str, size_t pos = 0) const; + size_t find_first_of(const char* s, size_t pos = 0) const; + + size_t find_last_of(const char* s, size_t pos, size_t n) const; + size_t find_last_of(char c, size_t pos = npos) const; + size_t find_last_of(const String& str, size_t pos = npos) const; + size_t find_last_of(const char* s, size_t pos = npos) const; + + friend String operator+ (const String& lhs, const String& rhs); + friend String operator+ (const String& lhs, const char* rhs); + friend String operator+ (const char* lhs, const String& rhs); + friend String operator+ (const String& lhs, char rhs); + friend String operator+ (char lhs, const String& rhs); + + String toLowerCase() const; + + String(const std::string& str); + String(const std::string& str, size_t pos, size_t len = npos); + String& operator=(const std::string& str); + String& operator+=(const std::string& str); + operator std::string() const; + + friend String operator+ (const String& lhs, const std::string& rhs); + friend String operator+ (const std::string& lhs, const String& rhs); + +private: + char* cstr_; + size_t len_; + + char* allocate(size_t len); // len without trailing 0 + void deallocate(); + + String(int); // disabled and invalid. Catch invalid usages like, commandLineParser.has(0) problem +}; + +//! @} core_basic + +////////////////////////// cv::String implementation ///////////////////////// + +//! @cond IGNORED + +inline +String::String() + : cstr_(0), len_(0) +{} + +inline +String::String(const String& str) + : cstr_(str.cstr_), len_(str.len_) +{ + if (cstr_) + CV_XADD(((int*)cstr_)-1, 1); +} + +inline +String::String(const String& str, size_t pos, size_t len) + : cstr_(0), len_(0) +{ + pos = min(pos, str.len_); + len = min(str.len_ - pos, len); + if (!len) return; + if (len == str.len_) + { + CV_XADD(((int*)str.cstr_)-1, 1); + cstr_ = str.cstr_; + len_ = str.len_; + return; + } + memcpy(allocate(len), str.cstr_ + pos, len); +} + +inline +String::String(const char* s) + : cstr_(0), len_(0) +{ + if (!s) return; + size_t len = strlen(s); + if (!len) return; + memcpy(allocate(len), s, len); +} + +inline +String::String(const char* s, size_t n) + : cstr_(0), len_(0) +{ + if (!n) return; + if (!s) return; + memcpy(allocate(n), s, n); +} + +inline +String::String(size_t n, char c) + : cstr_(0), len_(0) +{ + if (!n) return; + memset(allocate(n), c, n); +} + +inline +String::String(const char* first, const char* last) + : cstr_(0), len_(0) +{ + size_t len = (size_t)(last - first); + if (!len) return; + memcpy(allocate(len), first, len); +} + +template inline +String::String(Iterator first, Iterator last) + : cstr_(0), len_(0) +{ + size_t len = (size_t)(last - first); + if (!len) return; + char* str = allocate(len); + while (first != last) + { + *str++ = *first; + ++first; + } +} + +inline +String::~String() +{ + deallocate(); +} + +inline +String& String::operator=(const String& str) +{ + if (&str == this) return *this; + + deallocate(); + if (str.cstr_) CV_XADD(((int*)str.cstr_)-1, 1); + cstr_ = str.cstr_; + len_ = str.len_; + return *this; +} + +inline +String& String::operator=(const char* s) +{ + deallocate(); + if (!s) return *this; + size_t len = strlen(s); + if (len) memcpy(allocate(len), s, len); + return *this; +} + +inline +String& String::operator=(char c) +{ + deallocate(); + allocate(1)[0] = c; + return *this; +} + +inline +String& String::operator+=(const String& str) +{ + *this = *this + str; + return *this; +} + +inline +String& String::operator+=(const char* s) +{ + *this = *this + s; + return *this; +} + +inline +String& String::operator+=(char c) +{ + *this = *this + c; + return *this; +} + +inline +size_t String::size() const +{ + return len_; +} + +inline +size_t String::length() const +{ + return len_; +} + +inline +char String::operator[](size_t idx) const +{ + return cstr_[idx]; +} + +inline +char String::operator[](int idx) const +{ + return cstr_[idx]; +} + +inline +const char* String::begin() const +{ + return cstr_; +} + +inline +const char* String::end() const +{ + return len_ ? cstr_ + len_ : NULL; +} + +inline +bool String::empty() const +{ + return len_ == 0; +} + +inline +const char* String::c_str() const +{ + return cstr_ ? cstr_ : ""; +} + +inline +void String::swap(String& str) +{ + cv::swap(cstr_, str.cstr_); + cv::swap(len_, str.len_); +} + +inline +void String::clear() +{ + deallocate(); +} + +inline +int String::compare(const char* s) const +{ + if (cstr_ == s) return 0; + return strcmp(c_str(), s); +} + +inline +int String::compare(const String& str) const +{ + if (cstr_ == str.cstr_) return 0; + return strcmp(c_str(), str.c_str()); +} + +inline +String String::substr(size_t pos, size_t len) const +{ + return String(*this, pos, len); +} + +inline +size_t String::find(const char* s, size_t pos, size_t n) const +{ + if (n == 0 || pos + n > len_) return npos; + const char* lmax = cstr_ + len_ - n; + for (const char* i = cstr_ + pos; i <= lmax; ++i) + { + size_t j = 0; + while (j < n && s[j] == i[j]) ++j; + if (j == n) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find(char c, size_t pos) const +{ + return find(&c, pos, 1); +} + +inline +size_t String::find(const String& str, size_t pos) const +{ + return find(str.c_str(), pos, str.len_); +} + +inline +size_t String::find(const char* s, size_t pos) const +{ + if (pos >= len_ || !s[0]) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + size_t j = 0; + while (s[j] && s[j] == i[j]) + { if(i + j >= lmax) return npos; + ++j; + } + if (!s[j]) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::rfind(const char* s, size_t pos, size_t n) const +{ + if (n > len_) return npos; + if (pos > len_ - n) pos = len_ - n; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + size_t j = 0; + while (j < n && s[j] == i[j]) ++j; + if (j == n) return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::rfind(char c, size_t pos) const +{ + return rfind(&c, pos, 1); +} + +inline +size_t String::rfind(const String& str, size_t pos) const +{ + return rfind(str.c_str(), pos, str.len_); +} + +inline +size_t String::rfind(const char* s, size_t pos) const +{ + return rfind(s, pos, strlen(s)); +} + +inline +size_t String::find_first_of(const char* s, size_t pos, size_t n) const +{ + if (n == 0 || pos + n > len_) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + for (size_t j = 0; j < n; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_first_of(char c, size_t pos) const +{ + return find_first_of(&c, pos, 1); +} + +inline +size_t String::find_first_of(const String& str, size_t pos) const +{ + return find_first_of(str.c_str(), pos, str.len_); +} + +inline +size_t String::find_first_of(const char* s, size_t pos) const +{ + if (len_ == 0) return npos; + if (pos >= len_ || !s[0]) return npos; + const char* lmax = cstr_ + len_; + for (const char* i = cstr_ + pos; i < lmax; ++i) + { + for (size_t j = 0; s[j]; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_last_of(const char* s, size_t pos, size_t n) const +{ + if (len_ == 0) return npos; + if (pos >= len_) pos = len_ - 1; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + for (size_t j = 0; j < n; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +size_t String::find_last_of(char c, size_t pos) const +{ + return find_last_of(&c, pos, 1); +} + +inline +size_t String::find_last_of(const String& str, size_t pos) const +{ + return find_last_of(str.c_str(), pos, str.len_); +} + +inline +size_t String::find_last_of(const char* s, size_t pos) const +{ + if (len_ == 0) return npos; + if (pos >= len_) pos = len_ - 1; + for (const char* i = cstr_ + pos; i >= cstr_; --i) + { + for (size_t j = 0; s[j]; ++j) + if (s[j] == *i) + return (size_t)(i - cstr_); + } + return npos; +} + +inline +String String::toLowerCase() const +{ + if (!cstr_) + return String(); + String res(cstr_, len_); + for (size_t i = 0; i < len_; ++i) + res.cstr_[i] = (char) ::tolower(cstr_[i]); + + return res; +} + +//! @endcond + +// ************************* cv::String non-member functions ************************* + +//! @relates cv::String +//! @{ + +inline +String operator + (const String& lhs, const String& rhs) +{ + String s; + s.allocate(lhs.len_ + rhs.len_); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhs.len_) memcpy(s.cstr_ + lhs.len_, rhs.cstr_, rhs.len_); + return s; +} + +inline +String operator + (const String& lhs, const char* rhs) +{ + String s; + size_t rhslen = strlen(rhs); + s.allocate(lhs.len_ + rhslen); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhslen) memcpy(s.cstr_ + lhs.len_, rhs, rhslen); + return s; +} + +inline +String operator + (const char* lhs, const String& rhs) +{ + String s; + size_t lhslen = strlen(lhs); + s.allocate(lhslen + rhs.len_); + if (lhslen) memcpy(s.cstr_, lhs, lhslen); + if (rhs.len_) memcpy(s.cstr_ + lhslen, rhs.cstr_, rhs.len_); + return s; +} + +inline +String operator + (const String& lhs, char rhs) +{ + String s; + s.allocate(lhs.len_ + 1); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + s.cstr_[lhs.len_] = rhs; + return s; +} + +inline +String operator + (char lhs, const String& rhs) +{ + String s; + s.allocate(rhs.len_ + 1); + s.cstr_[0] = lhs; + if (rhs.len_) memcpy(s.cstr_ + 1, rhs.cstr_, rhs.len_); + return s; +} + +static inline bool operator== (const String& lhs, const String& rhs) { return 0 == lhs.compare(rhs); } +static inline bool operator== (const char* lhs, const String& rhs) { return 0 == rhs.compare(lhs); } +static inline bool operator== (const String& lhs, const char* rhs) { return 0 == lhs.compare(rhs); } +static inline bool operator!= (const String& lhs, const String& rhs) { return 0 != lhs.compare(rhs); } +static inline bool operator!= (const char* lhs, const String& rhs) { return 0 != rhs.compare(lhs); } +static inline bool operator!= (const String& lhs, const char* rhs) { return 0 != lhs.compare(rhs); } +static inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } +static inline bool operator< (const char* lhs, const String& rhs) { return rhs.compare(lhs) > 0; } +static inline bool operator< (const String& lhs, const char* rhs) { return lhs.compare(rhs) < 0; } +static inline bool operator<= (const String& lhs, const String& rhs) { return lhs.compare(rhs) <= 0; } +static inline bool operator<= (const char* lhs, const String& rhs) { return rhs.compare(lhs) >= 0; } +static inline bool operator<= (const String& lhs, const char* rhs) { return lhs.compare(rhs) <= 0; } +static inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } +static inline bool operator> (const char* lhs, const String& rhs) { return rhs.compare(lhs) < 0; } +static inline bool operator> (const String& lhs, const char* rhs) { return lhs.compare(rhs) > 0; } +static inline bool operator>= (const String& lhs, const String& rhs) { return lhs.compare(rhs) >= 0; } +static inline bool operator>= (const char* lhs, const String& rhs) { return rhs.compare(lhs) <= 0; } +static inline bool operator>= (const String& lhs, const char* rhs) { return lhs.compare(rhs) >= 0; } + + +#ifndef OPENCV_DISABLE_STRING_LOWER_UPPER_CONVERSIONS + +//! @cond IGNORED +namespace details { +// std::tolower is int->int +static inline char char_tolower(char ch) +{ + return (char)std::tolower((int)ch); +} +// std::toupper is int->int +static inline char char_toupper(char ch) +{ + return (char)std::toupper((int)ch); +} +} // namespace details +//! @endcond + +static inline std::string toLowerCase(const std::string& str) +{ + std::string result(str); + std::transform(result.begin(), result.end(), result.begin(), details::char_tolower); + return result; +} + +static inline std::string toUpperCase(const std::string& str) +{ + std::string result(str); + std::transform(result.begin(), result.end(), result.begin(), details::char_toupper); + return result; +} + +#endif // OPENCV_DISABLE_STRING_LOWER_UPPER_CONVERSIONS + +//! @} relates cv::String + +} // cv + +namespace std +{ + static inline void swap(cv::String& a, cv::String& b) { a.swap(b); } +} + +#include "opencv2/core/ptr.inl.hpp" + +#endif //OPENCV_CORE_CVSTD_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.inl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.inl.hpp new file mode 100644 index 0000000..36c83e2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/cvstd.inl.hpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_CVSTDINL_HPP +#define OPENCV_CORE_CVSTDINL_HPP + +#include +#include +#include + +//! @cond IGNORED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable: 4127 ) +#endif + +namespace cv +{ + +template class DataType< std::complex<_Tp> > +{ +public: + typedef std::complex<_Tp> value_type; + typedef value_type work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) }; + + typedef Vec vec_type; +}; + +inline +String::String(const std::string& str) + : cstr_(0), len_(0) +{ + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); +} + +inline +String::String(const std::string& str, size_t pos, size_t len) + : cstr_(0), len_(0) +{ + size_t strlen = str.size(); + pos = min(pos, strlen); + len = min(strlen - pos, len); + if (!len) return; + memcpy(allocate(len), str.c_str() + pos, len); +} + +inline +String& String::operator = (const std::string& str) +{ + deallocate(); + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); + return *this; +} + +inline +String& String::operator += (const std::string& str) +{ + *this = *this + str; + return *this; +} + +inline +String::operator std::string() const +{ + return std::string(cstr_, len_); +} + +inline +String operator + (const String& lhs, const std::string& rhs) +{ + String s; + size_t rhslen = rhs.size(); + s.allocate(lhs.len_ + rhslen); + if (lhs.len_) memcpy(s.cstr_, lhs.cstr_, lhs.len_); + if (rhslen) memcpy(s.cstr_ + lhs.len_, rhs.c_str(), rhslen); + return s; +} + +inline +String operator + (const std::string& lhs, const String& rhs) +{ + String s; + size_t lhslen = lhs.size(); + s.allocate(lhslen + rhs.len_); + if (lhslen) memcpy(s.cstr_, lhs.c_str(), lhslen); + if (rhs.len_) memcpy(s.cstr_ + lhslen, rhs.cstr_, rhs.len_); + return s; +} + +inline +FileNode::operator std::string() const +{ + String value; + read(*this, value, value); + return value; +} + +template<> inline +void operator >> (const FileNode& n, std::string& value) +{ + read(n, value, std::string()); +} + +template<> inline +FileStorage& operator << (FileStorage& fs, const std::string& value) +{ + return fs << cv::String(value); +} + +static inline +std::ostream& operator << (std::ostream& os, const String& str) +{ + return os << str.c_str(); +} + +static inline +std::ostream& operator << (std::ostream& out, Ptr fmtd) +{ + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + out << str; + return out; +} + +static inline +std::ostream& operator << (std::ostream& out, const Mat& mtx) +{ + return out << Formatter::get()->format(mtx); +} + +static inline +std::ostream& operator << (std::ostream& out, const UMat& m) +{ + return out << m.getMat(ACCESS_READ); +} + +template static inline +std::ostream& operator << (std::ostream& out, const Complex<_Tp>& c) +{ + return out << "(" << c.re << "," << c.im << ")"; +} + +template static inline +std::ostream& operator << (std::ostream& out, const std::vector >& vec) +{ + return out << Formatter::get()->format(Mat(vec)); +} + + +template static inline +std::ostream& operator << (std::ostream& out, const std::vector >& vec) +{ + return out << Formatter::get()->format(Mat(vec)); +} + + +template static inline +std::ostream& operator << (std::ostream& out, const Matx<_Tp, m, n>& matx) +{ + return out << Formatter::get()->format(Mat(matx)); +} + +template static inline +std::ostream& operator << (std::ostream& out, const Point_<_Tp>& p) +{ + out << "[" << p.x << ", " << p.y << "]"; + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Point3_<_Tp>& p) +{ + out << "[" << p.x << ", " << p.y << ", " << p.z << "]"; + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Vec<_Tp, n>& vec) +{ + out << "["; + if (cv::traits::Depth<_Tp>::value <= CV_32S) + { + for (int i = 0; i < n - 1; ++i) { + out << (int)vec[i] << ", "; + } + out << (int)vec[n-1] << "]"; + } + else + { + for (int i = 0; i < n - 1; ++i) { + out << vec[i] << ", "; + } + out << vec[n-1] << "]"; + } + + return out; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Size_<_Tp>& size) +{ + return out << "[" << size.width << " x " << size.height << "]"; +} + +template static inline +std::ostream& operator << (std::ostream& out, const Rect_<_Tp>& rect) +{ + return out << "[" << rect.width << " x " << rect.height << " from (" << rect.x << ", " << rect.y << ")]"; +} + +static inline std::ostream& operator << (std::ostream& out, const MatSize& msize) +{ + int i, dims = msize.dims(); + for( i = 0; i < dims; i++ ) + { + out << msize[i]; + if( i < dims-1 ) + out << " x "; + } + return out; +} + +static inline std::ostream &operator<< (std::ostream &s, cv::Range &r) +{ + return s << "[" << r.start << " : " << r.end << ")"; +} + +} // cv + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +//! @endcond + +#endif // OPENCV_CORE_CVSTDINL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/async_promise.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/async_promise.hpp new file mode 100644 index 0000000..6eb3fb5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/async_promise.hpp @@ -0,0 +1,71 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ASYNC_PROMISE_HPP +#define OPENCV_CORE_ASYNC_PROMISE_HPP + +#include "../async.hpp" + +#include "exception_ptr.hpp" + +namespace cv { + +/** @addtogroup core_async +@{ +*/ + + +/** @brief Provides result of asynchronous operations + +*/ +class CV_EXPORTS AsyncPromise +{ +public: + ~AsyncPromise() CV_NOEXCEPT; + AsyncPromise() CV_NOEXCEPT; + explicit AsyncPromise(const AsyncPromise& o) CV_NOEXCEPT; + AsyncPromise& operator=(const AsyncPromise& o) CV_NOEXCEPT; + void release() CV_NOEXCEPT; + + /** Returns associated AsyncArray + @note Can be called once + */ + AsyncArray getArrayResult(); + + /** Stores asynchronous result. + @param[in] value result + */ + void setValue(InputArray value); + + // TODO "move" setters + +#if CV__EXCEPTION_PTR + /** Stores exception. + @param[in] exception exception to be raised in AsyncArray + */ + void setException(std::exception_ptr exception); +#endif + + /** Stores exception. + @param[in] exception exception to be raised in AsyncArray + */ + void setException(const cv::Exception& exception); + +#ifdef CV_CXX11 + explicit AsyncPromise(AsyncPromise&& o) { p = o.p; o.p = NULL; } + AsyncPromise& operator=(AsyncPromise&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; } +#endif + + + // PImpl + typedef struct AsyncArray::Impl Impl; friend struct AsyncArray::Impl; + inline void* _getImpl() const CV_NOEXCEPT { return p; } +protected: + Impl* p; +}; + + +//! @} +} // namespace +#endif // OPENCV_CORE_ASYNC_PROMISE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/exception_ptr.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/exception_ptr.hpp new file mode 100644 index 0000000..d98ffc4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/detail/exception_ptr.hpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_DETAILS_EXCEPTION_PTR_H +#define OPENCV_CORE_DETAILS_EXCEPTION_PTR_H + +#ifndef CV__EXCEPTION_PTR +# if defined(__ANDROID__) && defined(ATOMIC_INT_LOCK_FREE) && ATOMIC_INT_LOCK_FREE < 2 +# define CV__EXCEPTION_PTR 0 // Not supported, details: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58938 +# elif defined(CV_CXX11) +# define CV__EXCEPTION_PTR 1 +# elif defined(_MSC_VER) +# define CV__EXCEPTION_PTR (_MSC_VER >= 1600) +# elif defined(__clang__) +# define CV__EXCEPTION_PTR 0 // C++11 only (see above) +# elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CV__EXCEPTION_PTR (__GXX_EXPERIMENTAL_CXX0X__ > 0) +# endif +#endif +#ifndef CV__EXCEPTION_PTR +# define CV__EXCEPTION_PTR 0 +#elif CV__EXCEPTION_PTR +# include // std::exception_ptr +#endif + +#endif // OPENCV_CORE_DETAILS_EXCEPTION_PTR_H diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/directx.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/directx.hpp new file mode 100644 index 0000000..056a85a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/directx.hpp @@ -0,0 +1,184 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors 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 copyright holders or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_DIRECTX_HPP +#define OPENCV_CORE_DIRECTX_HPP + +#include "mat.hpp" +#include "ocl.hpp" + +#if !defined(__d3d11_h__) +struct ID3D11Device; +struct ID3D11Texture2D; +#endif + +#if !defined(__d3d10_h__) +struct ID3D10Device; +struct ID3D10Texture2D; +#endif + +#if !defined(_D3D9_H_) +struct IDirect3DDevice9; +struct IDirect3DDevice9Ex; +struct IDirect3DSurface9; +#endif + + +namespace cv { namespace directx { + +namespace ocl { +using namespace cv::ocl; + +//! @addtogroup core_directx +// This section describes OpenCL and DirectX interoperability. +// +// To enable DirectX support, configure OpenCV using CMake with WITH_DIRECTX=ON . Note, DirectX is +// supported only on Windows. +// +// To use OpenCL functionality you should first initialize OpenCL context from DirectX resource. +// +//! @{ + +// TODO static functions in the Context class +//! @brief Creates OpenCL context from D3D11 device +// +//! @param pD3D11Device - pointer to D3D11 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromD3D11Device(ID3D11Device* pD3D11Device); + +//! @brief Creates OpenCL context from D3D10 device +// +//! @param pD3D10Device - pointer to D3D10 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromD3D10Device(ID3D10Device* pD3D10Device); + +//! @brief Creates OpenCL context from Direct3DDevice9Ex device +// +//! @param pDirect3DDevice9Ex - pointer to Direct3DDevice9Ex device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromDirect3DDevice9Ex(IDirect3DDevice9Ex* pDirect3DDevice9Ex); + +//! @brief Creates OpenCL context from Direct3DDevice9 device +// +//! @param pDirect3DDevice9 - pointer to Direct3Device9 device +//! @return Returns reference to OpenCL Context +CV_EXPORTS Context& initializeContextFromDirect3DDevice9(IDirect3DDevice9* pDirect3DDevice9); + +//! @} + +} // namespace cv::directx::ocl + +//! @addtogroup core_directx +//! @{ + +//! @brief Converts InputArray to ID3D11Texture2D. If destination texture format is DXGI_FORMAT_NV12 then +//! input UMat expected to be in BGR format and data will be downsampled and color-converted to NV12. +// +//! @note Note: Destination texture must be allocated by application. Function does memory copy from src to +//! pD3D11Texture2D +// +//! @param src - source InputArray +//! @param pD3D11Texture2D - destination D3D11 texture +CV_EXPORTS void convertToD3D11Texture2D(InputArray src, ID3D11Texture2D* pD3D11Texture2D); + +//! @brief Converts ID3D11Texture2D to OutputArray. If input texture format is DXGI_FORMAT_NV12 then +//! data will be upsampled and color-converted to BGR format. +// +//! @note Note: Destination matrix will be re-allocated if it has not enough memory to match texture size. +//! function does memory copy from pD3D11Texture2D to dst +// +//! @param pD3D11Texture2D - source D3D11 texture +//! @param dst - destination OutputArray +CV_EXPORTS void convertFromD3D11Texture2D(ID3D11Texture2D* pD3D11Texture2D, OutputArray dst); + +//! @brief Converts InputArray to ID3D10Texture2D +// +//! @note Note: function does memory copy from src to +//! pD3D10Texture2D +// +//! @param src - source InputArray +//! @param pD3D10Texture2D - destination D3D10 texture +CV_EXPORTS void convertToD3D10Texture2D(InputArray src, ID3D10Texture2D* pD3D10Texture2D); + +//! @brief Converts ID3D10Texture2D to OutputArray +// +//! @note Note: function does memory copy from pD3D10Texture2D +//! to dst +// +//! @param pD3D10Texture2D - source D3D10 texture +//! @param dst - destination OutputArray +CV_EXPORTS void convertFromD3D10Texture2D(ID3D10Texture2D* pD3D10Texture2D, OutputArray dst); + +//! @brief Converts InputArray to IDirect3DSurface9 +// +//! @note Note: function does memory copy from src to +//! pDirect3DSurface9 +// +//! @param src - source InputArray +//! @param pDirect3DSurface9 - destination D3D10 texture +//! @param surfaceSharedHandle - shared handle +CV_EXPORTS void convertToDirect3DSurface9(InputArray src, IDirect3DSurface9* pDirect3DSurface9, void* surfaceSharedHandle = NULL); + +//! @brief Converts IDirect3DSurface9 to OutputArray +// +//! @note Note: function does memory copy from pDirect3DSurface9 +//! to dst +// +//! @param pDirect3DSurface9 - source D3D10 texture +//! @param dst - destination OutputArray +//! @param surfaceSharedHandle - shared handle +CV_EXPORTS void convertFromDirect3DSurface9(IDirect3DSurface9* pDirect3DSurface9, OutputArray dst, void* surfaceSharedHandle = NULL); + +//! @brief Get OpenCV type from DirectX type +//! @param iDXGI_FORMAT - enum DXGI_FORMAT for D3D10/D3D11 +//! @return OpenCV type or -1 if there is no equivalent +CV_EXPORTS int getTypeFromDXGI_FORMAT(const int iDXGI_FORMAT); // enum DXGI_FORMAT for D3D10/D3D11 + +//! @brief Get OpenCV type from DirectX type +//! @param iD3DFORMAT - enum D3DTYPE for D3D9 +//! @return OpenCV type or -1 if there is no equivalent +CV_EXPORTS int getTypeFromD3DFORMAT(const int iD3DFORMAT); // enum D3DTYPE for D3D9 + +//! @} + +} } // namespace cv::directx + +#endif // OPENCV_CORE_DIRECTX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/eigen.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/eigen.hpp new file mode 100644 index 0000000..51f4147 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/eigen.hpp @@ -0,0 +1,402 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + + +#ifndef OPENCV_CORE_EIGEN_HPP +#define OPENCV_CORE_EIGEN_HPP + +#ifndef EIGEN_WORLD_VERSION +#error "Wrong usage of OpenCV's Eigen utility header. Include Eigen's headers first. See https://github.com/opencv/opencv/issues/17366" +#endif + +#include "opencv2/core.hpp" + +#if defined _MSC_VER && _MSC_VER >= 1200 +#define NOMINMAX // fix https://github.com/opencv/opencv/issues/17548 +#pragma warning( disable: 4714 ) //__forceinline is not inlined +#pragma warning( disable: 4127 ) //conditional expression is constant +#pragma warning( disable: 4244 ) //conversion from '__int64' to 'int', possible loss of data +#endif + +#if !defined(OPENCV_DISABLE_EIGEN_TENSOR_SUPPORT) +#if EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 \ + && defined(CV_CXX11) && defined(CV_CXX_STD_ARRAY) +#include +#define OPENCV_EIGEN_TENSOR_SUPPORT 1 +#endif // EIGEN_WORLD_VERSION == 3 && EIGEN_MAJOR_VERSION >= 3 +#endif // !defined(OPENCV_DISABLE_EIGEN_TENSOR_SUPPORT) + +namespace cv +{ + +/** @addtogroup core_eigen +These functions are provided for OpenCV-Eigen interoperability. They convert `Mat` +objects to corresponding `Eigen::Matrix` objects and vice-versa. Consult the [Eigen +documentation](https://eigen.tuxfamily.org/dox/group__TutorialMatrixClass.html) for +information about the `Matrix` template type. + +@note Using these functions requires the `Eigen/Dense` or similar header to be +included before this header. +*/ +//! @{ + +#if defined(OPENCV_EIGEN_TENSOR_SUPPORT) || defined(CV_DOXYGEN) +/** @brief Converts an Eigen::Tensor to a cv::Mat. + +The method converts an Eigen::Tensor with shape (H x W x C) to a cv::Mat where: + H = number of rows + W = number of columns + C = number of channels + +Usage: +\code +Eigen::Tensor a_tensor(...); +// populate tensor with values +Mat a_mat; +eigen2cv(a_tensor, a_mat); +\endcode +*/ +template static inline +void eigen2cv( const Eigen::Tensor<_Tp, 3, _layout> &src, OutputArray dst ) +{ + if( !(_layout & Eigen::RowMajorBit) ) + { + const std::array shuffle{2, 1, 0}; + Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor = src.swap_layout().shuffle(shuffle); + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), row_major_tensor.data()); + _src.copyTo(dst); + } + else + { + Mat _src(src.dimension(0), src.dimension(1), CV_MAKETYPE(DataType<_Tp>::type, src.dimension(2)), (void *)src.data()); + _src.copyTo(dst); + } +} + +/** @brief Converts a cv::Mat to an Eigen::Tensor. + +The method converts a cv::Mat to an Eigen Tensor with shape (H x W x C) where: + H = number of rows + W = number of columns + C = number of channels + +Usage: +\code +Mat a_mat(...); +// populate Mat with values +Eigen::Tensor a_tensor(...); +cv2eigen(a_mat, a_tensor); +\endcode +*/ +template static inline +void cv2eigen( const Mat &src, Eigen::Tensor<_Tp, 3, _layout> &dst ) +{ + if( !(_layout & Eigen::RowMajorBit) ) + { + Eigen::Tensor<_Tp, 3, !_layout> row_major_tensor(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), row_major_tensor.data()); + if (src.type() == _dst.type()) + src.copyTo(_dst); + else + src.convertTo(_dst, _dst.type()); + const std::array shuffle{2, 1, 0}; + dst = row_major_tensor.swap_layout().shuffle(shuffle); + } + else + { + dst.resize(src.rows, src.cols, src.channels()); + Mat _dst(src.rows, src.cols, CV_MAKETYPE(DataType<_Tp>::type, src.channels()), dst.data()); + if (src.type() == _dst.type()) + src.copyTo(_dst); + else + src.convertTo(_dst, _dst.type()); + } +} + +/** @brief Maps cv::Mat data to an Eigen::TensorMap. + +The method wraps an existing Mat data array with an Eigen TensorMap of shape (H x W x C) where: + H = number of rows + W = number of columns + C = number of channels + +Explicit instantiation of the return type is required. + +@note Caller should be aware of the lifetime of the cv::Mat instance and take appropriate safety measures. +The cv::Mat instance will retain ownership of the data and the Eigen::TensorMap will lose access when the cv::Mat data is deallocated. + +The example below initializes a cv::Mat and produces an Eigen::TensorMap: +\code +float arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +Mat a_mat(2, 2, CV_32FC3, arr); +Eigen::TensorMap> a_tensormap = cv2eigen_tensormap(a_mat); +\endcode +*/ +template static inline +Eigen::TensorMap> cv2eigen_tensormap(InputArray src) +{ + Mat mat = src.getMat(); + CV_CheckTypeEQ(mat.type(), CV_MAKETYPE(traits::Type<_Tp>::value, mat.channels()), ""); + return Eigen::TensorMap>((_Tp *)mat.data, mat.rows, mat.cols, mat.channels()); +} +#endif // OPENCV_EIGEN_TENSOR_SUPPORT + +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, OutputArray dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + Mat _src(src.cols(), src.rows(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + transpose(_src, dst); + } + else + { + Mat _src(src.rows(), src.cols(), traits::Type<_Tp>::value, + (void*)src.data(), src.outerStride()*sizeof(_Tp)); + _src.copyTo(dst); + } +} + +// Matx case +template static inline +void eigen2cv( const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& src, + Matx<_Tp, _rows, _cols>& dst ) +{ + if( !(src.Flags & Eigen::RowMajorBit) ) + { + dst = Matx<_Tp, _cols, _rows>(static_cast(src.data())).t(); + } + else + { + dst = Matx<_Tp, _rows, _cols>(static_cast(src.data())); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) +{ + CV_DbgAssert(src.rows == _rows && src.cols == _cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else if( src.cols == src.rows ) + { + src.convertTo(_dst, _dst.type()); + transpose(_dst, _dst); + } + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, _cols>& src, + Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst ) +{ + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst ) +{ + dst.resize(src.rows, src.cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else if( src.cols == src.rows ) + { + src.convertTo(_dst, _dst.type()); + transpose(_dst, _dst); + } + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, _cols>& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, Eigen::Dynamic>& dst ) +{ + dst.resize(_rows, _cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst ) +{ + CV_Assert(src.cols == 1); + dst.resize(src.rows); + + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +// Matx case +template static inline +void cv2eigen( const Matx<_Tp, _rows, 1>& src, + Eigen::Matrix<_Tp, Eigen::Dynamic, 1>& dst ) +{ + dst.resize(_rows); + + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(1, _rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(_rows, 1, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.copyTo(_dst); + } +} + + +template static inline +void cv2eigen( const Mat& src, + Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst ) +{ + CV_Assert(src.rows == 1); + dst.resize(src.cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(src.cols, src.rows, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + if( src.type() == _dst.type() ) + transpose(src, _dst); + else + Mat(src.t()).convertTo(_dst, _dst.type()); + } + else + { + const Mat _dst(src.rows, src.cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + src.convertTo(_dst, _dst.type()); + } +} + +//Matx +template static inline +void cv2eigen( const Matx<_Tp, 1, _cols>& src, + Eigen::Matrix<_Tp, 1, Eigen::Dynamic>& dst ) +{ + dst.resize(_cols); + if( !(dst.Flags & Eigen::RowMajorBit) ) + { + const Mat _dst(_cols, 1, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + transpose(src, _dst); + } + else + { + const Mat _dst(1, _cols, traits::Type<_Tp>::value, + dst.data(), (size_t)(dst.outerStride()*sizeof(_Tp))); + Mat(src).copyTo(_dst); + } +} + +//! @} + +} // cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/fast_math.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/fast_math.hpp new file mode 100644 index 0000000..eb4fbe2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/fast_math.hpp @@ -0,0 +1,411 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_FAST_MATH_HPP +#define OPENCV_CORE_FAST_MATH_HPP + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils +//! @{ + +/****************************************************************************************\ +* fast math * +\****************************************************************************************/ + +#ifdef __cplusplus +# include +#else +# ifdef __BORLANDC__ +# include +# else +# include +# endif +#endif + +#if defined(__CUDACC__) + // nothing, intrinsics/asm code is not supported +#else + #if ((defined _MSC_VER && defined _M_X64) \ + || (defined __GNUC__ && defined __x86_64__ && defined __SSE2__)) \ + && !defined(OPENCV_SKIP_INCLUDE_EMMINTRIN_H) + #include + #endif + + #if defined __PPC64__ && defined __GNUC__ && defined _ARCH_PWR8 \ + && !defined(OPENCV_SKIP_INCLUDE_ALTIVEC_H) + #include + #undef vector + #undef bool + #undef pixel + #endif + + #if defined(CV_INLINE_ROUND_FLT) + // user-specified version + // CV_INLINE_ROUND_DBL should be defined too + #elif defined __GNUC__ && defined __arm__ && (defined __ARM_PCS_VFP || defined __ARM_VFPV3__ || defined __ARM_NEON__) && !defined __SOFTFP__ + // 1. general scheme + #define ARM_ROUND(_value, _asm_string) \ + int res; \ + float temp; \ + CV_UNUSED(temp); \ + __asm__(_asm_string : [res] "=r" (res), [temp] "=w" (temp) : [value] "w" (_value)); \ + return res + // 2. version for double + #ifdef __clang__ + #define CV_INLINE_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %[value] \n vmov %[res], %[temp]") + #else + #define CV_INLINE_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %P[value] \n vmov %[res], %[temp]") + #endif + // 3. version for float + #define CV_INLINE_ROUND_FLT(value) ARM_ROUND(value, "vcvtr.s32.f32 %[temp], %[value]\n vmov %[res], %[temp]") + #elif defined __PPC64__ && defined __GNUC__ && defined _ARCH_PWR8 + // P8 and newer machines can convert fp32/64 to int quickly. + #define CV_INLINE_ROUND_DBL(value) \ + int out; \ + double temp; \ + __asm__( "fctiw %[temp],%[in]\n\tmfvsrwz %[out],%[temp]\n\t" : [out] "=r" (out), [temp] "=d" (temp) : [in] "d" ((double)(value)) : ); \ + return out; + + // FP32 also works with FP64 routine above + #define CV_INLINE_ROUND_FLT(value) CV_INLINE_ROUND_DBL(value) + #endif + + #ifdef CV_INLINE_ISINF_FLT + // user-specified version + // CV_INLINE_ISINF_DBL should be defined too + #elif defined __PPC64__ && defined _ARCH_PWR9 && defined(scalar_test_data_class) + #define CV_INLINE_ISINF_DBL(value) return scalar_test_data_class(value, 0x30); + #define CV_INLINE_ISINF_FLT(value) CV_INLINE_ISINF_DBL(value) + #endif + + #ifdef CV_INLINE_ISNAN_FLT + // user-specified version + // CV_INLINE_ISNAN_DBL should be defined too + #elif defined __PPC64__ && defined _ARCH_PWR9 && defined(scalar_test_data_class) + #define CV_INLINE_ISNAN_DBL(value) return scalar_test_data_class(value, 0x40); + #define CV_INLINE_ISNAN_FLT(value) CV_INLINE_ISNAN_DBL(value) + #endif + + #if !defined(OPENCV_USE_FASTMATH_BUILTINS) \ + && ( \ + defined(__x86_64__) || defined(__i686__) \ + || defined(__arm__) \ + || defined(__PPC64__) \ + ) + /* Let builtin C math functions when available. Dedicated hardware is available to + round and convert FP values. */ + #define OPENCV_USE_FASTMATH_BUILTINS 1 + #endif + + /* Enable builtin math functions if possible, desired, and available. + Note, not all math functions inline equally. E.g lrint will not inline + without the -fno-math-errno option. */ + #if defined(CV_ICC) + // nothing + #elif defined(OPENCV_USE_FASTMATH_BUILTINS) && OPENCV_USE_FASTMATH_BUILTINS + #if defined(__clang__) + #define CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS + #if !defined(CV_INLINE_ISNAN_DBL) && __has_builtin(__builtin_isnan) + #define CV_INLINE_ISNAN_DBL(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) && __has_builtin(__builtin_isnan) + #define CV_INLINE_ISNAN_FLT(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) && __has_builtin(__builtin_isinf) + #define CV_INLINE_ISINF_DBL(value) return __builtin_isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) && __has_builtin(__builtin_isinf) + #define CV_INLINE_ISINF_FLT(value) return __builtin_isinf(value); + #endif + #elif defined(__GNUC__) + #define CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS + #if !defined(CV_INLINE_ISNAN_DBL) + #define CV_INLINE_ISNAN_DBL(value) return __builtin_isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) + #define CV_INLINE_ISNAN_FLT(value) return __builtin_isnanf(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) + #define CV_INLINE_ISINF_DBL(value) return __builtin_isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) + #define CV_INLINE_ISINF_FLT(value) return __builtin_isinff(value); + #endif + #elif defined(_MSC_VER) + #if !defined(CV_INLINE_ISNAN_DBL) + #define CV_INLINE_ISNAN_DBL(value) return isnan(value); + #endif + #if !defined(CV_INLINE_ISNAN_FLT) + #define CV_INLINE_ISNAN_FLT(value) return isnan(value); + #endif + #if !defined(CV_INLINE_ISINF_DBL) + #define CV_INLINE_ISINF_DBL(value) return isinf(value); + #endif + #if !defined(CV_INLINE_ISINF_FLT) + #define CV_INLINE_ISINF_FLT(value) return isinf(value); + #endif + #endif + #endif + +#endif // defined(__CUDACC__) + +/** @brief Rounds floating-point number to the nearest integer + + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int +cvRound( double value ) +{ +#if defined CV_INLINE_ROUND_DBL + CV_INLINE_ROUND_DBL(value); +#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \ + && defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \ + && !defined(__CUDACC__) + __m128d t = _mm_set_sd( value ); + return _mm_cvtsd_si32(t); +#elif defined _MSC_VER && defined _M_IX86 + int t; + __asm + { + fld value; + fistp t; + } + return t; +#elif defined CV_ICC || defined __GNUC__ + return (int)(lrint(value)); +#else + /* it's ok if round does not comply with IEEE754 standard; + the tests should allow +/-1 difference when the tested functions use round */ + return (int)(value + (value >= 0 ? 0.5 : -0.5)); +#endif +} + + +/** @brief Rounds floating-point number to the nearest integer not larger than the original. + + The function computes an integer i such that: + \f[i \le \texttt{value} < i+1\f] + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int cvFloor( double value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_floor(value); +#else + int i = (int)value; + return i - (i > value); +#endif +} + +/** @brief Rounds floating-point number to the nearest integer not smaller than the original. + + The function computes an integer i such that: + \f[i \le \texttt{value} < i+1\f] + @param value floating-point number. If the value is outside of INT_MIN ... INT_MAX range, the + result is not defined. + */ +CV_INLINE int cvCeil( double value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_ceil(value); +#else + int i = (int)value; + return i + (i < value); +#endif +} + +/** @brief Determines if the argument is Not A Number. + + @param value The input floating-point value + + The function returns 1 if the argument is Not A Number (as defined by IEEE754 standard), 0 + otherwise. */ +CV_INLINE int cvIsNaN( double value ) +{ +#if defined CV_INLINE_ISNAN_DBL + CV_INLINE_ISNAN_DBL(value); +#else + Cv64suf ieee754; + ieee754.f = value; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > 0x7ff00000; +#endif +} + +/** @brief Determines if the argument is Infinity. + + @param value The input floating-point value + + The function returns 1 if the argument is a plus or minus infinity (as defined by IEEE754 standard) + and 0 otherwise. */ +CV_INLINE int cvIsInf( double value ) +{ +#if defined CV_INLINE_ISINF_DBL + CV_INLINE_ISINF_DBL(value); +#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__PPC64__) + Cv64suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff00000000) == + 0x7ff0000000000000; +#else + Cv64suf ieee754; + ieee754.f = value; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && + (unsigned)ieee754.u == 0; +#endif +} + +#ifdef __cplusplus + +/** @overload */ +CV_INLINE int cvRound(float value) +{ +#if defined CV_INLINE_ROUND_FLT + CV_INLINE_ROUND_FLT(value); +#elif ((defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ \ + && defined __SSE2__ && !defined __APPLE__) || CV_SSE2) \ + && !defined(__CUDACC__) + __m128 t = _mm_set_ss( value ); + return _mm_cvtss_si32(t); +#elif defined _MSC_VER && defined _M_IX86 + int t; + __asm + { + fld value; + fistp t; + } + return t; +#elif defined CV_ICC || defined __GNUC__ + return (int)(lrintf(value)); +#else + /* it's ok if round does not comply with IEEE754 standard; + the tests should allow +/-1 difference when the tested functions use round */ + return (int)(value + (value >= 0 ? 0.5f : -0.5f)); +#endif +} + +/** @overload */ +CV_INLINE int cvRound( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvFloor( float value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_floorf(value); +#else + int i = (int)value; + return i - (i > value); +#endif +} + +/** @overload */ +CV_INLINE int cvFloor( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvCeil( float value ) +{ +#if (defined CV__FASTMATH_ENABLE_GCC_MATH_BUILTINS || defined CV__FASTMATH_ENABLE_CLANG_MATH_BUILTINS) \ + && ( \ + defined(__PPC64__) \ + ) + return __builtin_ceilf(value); +#else + int i = (int)value; + return i + (i < value); +#endif +} + +/** @overload */ +CV_INLINE int cvCeil( int value ) +{ + return value; +} + +/** @overload */ +CV_INLINE int cvIsNaN( float value ) +{ +#if defined CV_INLINE_ISNAN_FLT + CV_INLINE_ISNAN_FLT(value); +#else + Cv32suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff) > 0x7f800000; +#endif +} + +/** @overload */ +CV_INLINE int cvIsInf( float value ) +{ +#if defined CV_INLINE_ISINF_FLT + CV_INLINE_ISINF_FLT(value); +#else + Cv32suf ieee754; + ieee754.f = value; + return (ieee754.u & 0x7fffffff) == 0x7f800000; +#endif +} + +#endif // __cplusplus + +//! @} core_utils + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/hal.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/hal.hpp new file mode 100644 index 0000000..68900ec --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/hal.hpp @@ -0,0 +1,250 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_HPP +#define OPENCV_HAL_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/hal/interface.h" + +namespace cv { namespace hal { + +//! @addtogroup core_hal_functions +//! @{ + +CV_EXPORTS int normHamming(const uchar* a, int n); +CV_EXPORTS int normHamming(const uchar* a, const uchar* b, int n); + +CV_EXPORTS int normHamming(const uchar* a, int n, int cellSize); +CV_EXPORTS int normHamming(const uchar* a, const uchar* b, int n, int cellSize); + +CV_EXPORTS int LU32f(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS int LU64f(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky32f(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky64f(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS void SVD32f(float* At, size_t astep, float* W, float* U, size_t ustep, float* Vt, size_t vstep, int m, int n, int flags); +CV_EXPORTS void SVD64f(double* At, size_t astep, double* W, double* U, size_t ustep, double* Vt, size_t vstep, int m, int n, int flags); +CV_EXPORTS int QR32f(float* A, size_t astep, int m, int n, int k, float* b, size_t bstep, float* hFactors); +CV_EXPORTS int QR64f(double* A, size_t astep, int m, int n, int k, double* b, size_t bstep, double* hFactors); + +CV_EXPORTS void gemm32f(const float* src1, size_t src1_step, const float* src2, size_t src2_step, + float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm64f(const double* src1, size_t src1_step, const double* src2, size_t src2_step, + double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm32fc(const float* src1, size_t src1_step, const float* src2, size_t src2_step, + float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); +CV_EXPORTS void gemm64fc(const double* src1, size_t src1_step, const double* src2, size_t src2_step, + double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step, + int m_a, int n_a, int n_d, int flags); + +CV_EXPORTS int normL1_(const uchar* a, const uchar* b, int n); +CV_EXPORTS float normL1_(const float* a, const float* b, int n); +CV_EXPORTS float normL2Sqr_(const float* a, const float* b, int n); + +CV_EXPORTS void exp32f(const float* src, float* dst, int n); +CV_EXPORTS void exp64f(const double* src, double* dst, int n); +CV_EXPORTS void log32f(const float* src, float* dst, int n); +CV_EXPORTS void log64f(const double* src, double* dst, int n); + +CV_EXPORTS void fastAtan32f(const float* y, const float* x, float* dst, int n, bool angleInDegrees); +CV_EXPORTS void fastAtan64f(const double* y, const double* x, double* dst, int n, bool angleInDegrees); +CV_EXPORTS void magnitude32f(const float* x, const float* y, float* dst, int n); +CV_EXPORTS void magnitude64f(const double* x, const double* y, double* dst, int n); +CV_EXPORTS void sqrt32f(const float* src, float* dst, int len); +CV_EXPORTS void sqrt64f(const double* src, double* dst, int len); +CV_EXPORTS void invSqrt32f(const float* src, float* dst, int len); +CV_EXPORTS void invSqrt64f(const double* src, double* dst, int len); + +CV_EXPORTS void split8u(const uchar* src, uchar** dst, int len, int cn ); +CV_EXPORTS void split16u(const ushort* src, ushort** dst, int len, int cn ); +CV_EXPORTS void split32s(const int* src, int** dst, int len, int cn ); +CV_EXPORTS void split64s(const int64* src, int64** dst, int len, int cn ); + +CV_EXPORTS void merge8u(const uchar** src, uchar* dst, int len, int cn ); +CV_EXPORTS void merge16u(const ushort** src, ushort* dst, int len, int cn ); +CV_EXPORTS void merge32s(const int** src, int* dst, int len, int cn ); +CV_EXPORTS void merge64s(const int64** src, int64* dst, int len, int cn ); + +CV_EXPORTS void add8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void add64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void sub8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void sub64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void max8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void max64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void min8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void min64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void absdiff8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void absdiff64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void and8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void or8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void xor8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); +CV_EXPORTS void not8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* ); + +CV_EXPORTS void cmp8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp8s(const schar* src1, size_t step1, const schar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp16s(const short* src1, size_t step1, const short* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp32s(const int* src1, size_t step1, const int* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp32f(const float* src1, size_t step1, const float* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); +CV_EXPORTS void cmp64f(const double* src1, size_t step1, const double* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _cmpop); + +CV_EXPORTS void mul8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void mul64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void div8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void div64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void recip8u( const uchar *, size_t, const uchar * src2, size_t step2, uchar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip8s( const schar *, size_t, const schar * src2, size_t step2, schar* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip16u( const ushort *, size_t, const ushort * src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip16s( const short *, size_t, const short * src2, size_t step2, short* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip32s( const int *, size_t, const int * src2, size_t step2, int* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip32f( const float *, size_t, const float * src2, size_t step2, float* dst, size_t step, int width, int height, void* scale); +CV_EXPORTS void recip64f( const double *, size_t, const double * src2, size_t step2, double* dst, size_t step, int width, int height, void* scale); + +CV_EXPORTS void addWeighted8u( const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, void* _scalars ); +CV_EXPORTS void addWeighted8s( const schar* src1, size_t step1, const schar* src2, size_t step2, schar* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted16u( const ushort* src1, size_t step1, const ushort* src2, size_t step2, ushort* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted16s( const short* src1, size_t step1, const short* src2, size_t step2, short* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted32s( const int* src1, size_t step1, const int* src2, size_t step2, int* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted32f( const float* src1, size_t step1, const float* src2, size_t step2, float* dst, size_t step, int width, int height, void* scalars ); +CV_EXPORTS void addWeighted64f( const double* src1, size_t step1, const double* src2, size_t step2, double* dst, size_t step, int width, int height, void* scalars ); + +struct CV_EXPORTS DFT1D +{ + static Ptr create(int len, int count, int depth, int flags, bool * useBuffer = 0); + virtual void apply(const uchar *src, uchar *dst) = 0; + virtual ~DFT1D() {} +}; + +struct CV_EXPORTS DFT2D +{ + static Ptr create(int width, int height, int depth, + int src_channels, int dst_channels, + int flags, int nonzero_rows = 0); + virtual void apply(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step) = 0; + virtual ~DFT2D() {} +}; + +struct CV_EXPORTS DCT2D +{ + static Ptr create(int width, int height, int depth, int flags); + virtual void apply(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step) = 0; + virtual ~DCT2D() {} +}; + +//! @} core_hal + +//============================================================================= +// for binary compatibility with 3.0 + +//! @cond IGNORED + +CV_EXPORTS int LU(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS int LU(double* A, size_t astep, int m, double* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky(float* A, size_t astep, int m, float* b, size_t bstep, int n); +CV_EXPORTS bool Cholesky(double* A, size_t astep, int m, double* b, size_t bstep, int n); + +CV_EXPORTS void exp(const float* src, float* dst, int n); +CV_EXPORTS void exp(const double* src, double* dst, int n); +CV_EXPORTS void log(const float* src, float* dst, int n); +CV_EXPORTS void log(const double* src, double* dst, int n); + +CV_EXPORTS void fastAtan2(const float* y, const float* x, float* dst, int n, bool angleInDegrees); +CV_EXPORTS void magnitude(const float* x, const float* y, float* dst, int n); +CV_EXPORTS void magnitude(const double* x, const double* y, double* dst, int n); +CV_EXPORTS void sqrt(const float* src, float* dst, int len); +CV_EXPORTS void sqrt(const double* src, double* dst, int len); +CV_EXPORTS void invSqrt(const float* src, float* dst, int len); +CV_EXPORTS void invSqrt(const double* src, double* dst, int len); + +//! @endcond + +}} //cv::hal + +#endif //OPENCV_HAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/interface.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/interface.h new file mode 100644 index 0000000..8f64025 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/interface.h @@ -0,0 +1,182 @@ +#ifndef OPENCV_CORE_HAL_INTERFACE_H +#define OPENCV_CORE_HAL_INTERFACE_H + +//! @addtogroup core_hal_interface +//! @{ + +//! @name Return codes +//! @{ +#define CV_HAL_ERROR_OK 0 +#define CV_HAL_ERROR_NOT_IMPLEMENTED 1 +#define CV_HAL_ERROR_UNKNOWN -1 +//! @} + +#ifdef __cplusplus +#include +#else +#include +#include +#endif + +//! @name Data types +//! primitive types +//! - schar - signed 1 byte integer +//! - uchar - unsigned 1 byte integer +//! - short - signed 2 byte integer +//! - ushort - unsigned 2 byte integer +//! - int - signed 4 byte integer +//! - uint - unsigned 4 byte integer +//! - int64 - signed 8 byte integer +//! - uint64 - unsigned 8 byte integer +//! @{ +#if !defined _MSC_VER && !defined __BORLANDC__ +# if defined __cplusplus && __cplusplus >= 201103L && !defined __APPLE__ +# include +# ifdef __NEWLIB__ + typedef unsigned int uint; +# else + typedef std::uint32_t uint; +# endif +# else +# include + typedef uint32_t uint; +# endif +#else + typedef unsigned uint; +#endif + +typedef signed char schar; + +#ifndef __IPL_H__ + typedef unsigned char uchar; + typedef unsigned short ushort; +#endif + +#if defined _MSC_VER || defined __BORLANDC__ + typedef __int64 int64; + typedef unsigned __int64 uint64; +# define CV_BIG_INT(n) n##I64 +# define CV_BIG_UINT(n) n##UI64 +#else + typedef int64_t int64; + typedef uint64_t uint64; +# define CV_BIG_INT(n) n##LL +# define CV_BIG_UINT(n) n##ULL +#endif + +#define CV_CN_MAX 512 +#define CV_CN_SHIFT 3 +#define CV_DEPTH_MAX (1 << CV_CN_SHIFT) + +#define CV_8U 0 +#define CV_8S 1 +#define CV_16U 2 +#define CV_16S 3 +#define CV_32S 4 +#define CV_32F 5 +#define CV_64F 6 +#define CV_USRTYPE1 7 + +#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1) +#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK) + +#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT)) +#define CV_MAKE_TYPE CV_MAKETYPE + +#define CV_8UC1 CV_MAKETYPE(CV_8U,1) +#define CV_8UC2 CV_MAKETYPE(CV_8U,2) +#define CV_8UC3 CV_MAKETYPE(CV_8U,3) +#define CV_8UC4 CV_MAKETYPE(CV_8U,4) +#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n)) + +#define CV_8SC1 CV_MAKETYPE(CV_8S,1) +#define CV_8SC2 CV_MAKETYPE(CV_8S,2) +#define CV_8SC3 CV_MAKETYPE(CV_8S,3) +#define CV_8SC4 CV_MAKETYPE(CV_8S,4) +#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n)) + +#define CV_16UC1 CV_MAKETYPE(CV_16U,1) +#define CV_16UC2 CV_MAKETYPE(CV_16U,2) +#define CV_16UC3 CV_MAKETYPE(CV_16U,3) +#define CV_16UC4 CV_MAKETYPE(CV_16U,4) +#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n)) + +#define CV_16SC1 CV_MAKETYPE(CV_16S,1) +#define CV_16SC2 CV_MAKETYPE(CV_16S,2) +#define CV_16SC3 CV_MAKETYPE(CV_16S,3) +#define CV_16SC4 CV_MAKETYPE(CV_16S,4) +#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n)) + +#define CV_32SC1 CV_MAKETYPE(CV_32S,1) +#define CV_32SC2 CV_MAKETYPE(CV_32S,2) +#define CV_32SC3 CV_MAKETYPE(CV_32S,3) +#define CV_32SC4 CV_MAKETYPE(CV_32S,4) +#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n)) + +#define CV_32FC1 CV_MAKETYPE(CV_32F,1) +#define CV_32FC2 CV_MAKETYPE(CV_32F,2) +#define CV_32FC3 CV_MAKETYPE(CV_32F,3) +#define CV_32FC4 CV_MAKETYPE(CV_32F,4) +#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n)) + +#define CV_64FC1 CV_MAKETYPE(CV_64F,1) +#define CV_64FC2 CV_MAKETYPE(CV_64F,2) +#define CV_64FC3 CV_MAKETYPE(CV_64F,3) +#define CV_64FC4 CV_MAKETYPE(CV_64F,4) +#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n)) +//! @} + +//! @name Comparison operation +//! @sa cv::CmpTypes +//! @{ +#define CV_HAL_CMP_EQ 0 +#define CV_HAL_CMP_GT 1 +#define CV_HAL_CMP_GE 2 +#define CV_HAL_CMP_LT 3 +#define CV_HAL_CMP_LE 4 +#define CV_HAL_CMP_NE 5 +//! @} + +//! @name Border processing modes +//! @sa cv::BorderTypes +//! @{ +#define CV_HAL_BORDER_CONSTANT 0 +#define CV_HAL_BORDER_REPLICATE 1 +#define CV_HAL_BORDER_REFLECT 2 +#define CV_HAL_BORDER_WRAP 3 +#define CV_HAL_BORDER_REFLECT_101 4 +#define CV_HAL_BORDER_TRANSPARENT 5 +#define CV_HAL_BORDER_ISOLATED 16 +//! @} + +//! @name DFT flags +//! @{ +#define CV_HAL_DFT_INVERSE 1 +#define CV_HAL_DFT_SCALE 2 +#define CV_HAL_DFT_ROWS 4 +#define CV_HAL_DFT_COMPLEX_OUTPUT 16 +#define CV_HAL_DFT_REAL_OUTPUT 32 +#define CV_HAL_DFT_TWO_STAGE 64 +#define CV_HAL_DFT_STAGE_COLS 128 +#define CV_HAL_DFT_IS_CONTINUOUS 512 +#define CV_HAL_DFT_IS_INPLACE 1024 +//! @} + +//! @name SVD flags +//! @{ +#define CV_HAL_SVD_NO_UV 1 +#define CV_HAL_SVD_SHORT_UV 2 +#define CV_HAL_SVD_MODIFY_A 4 +#define CV_HAL_SVD_FULL_UV 8 +//! @} + +//! @name Gemm flags +//! @{ +#define CV_HAL_GEMM_1_T 1 +#define CV_HAL_GEMM_2_T 2 +#define CV_HAL_GEMM_3_T 4 +//! @} + +//! @} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin.hpp new file mode 100644 index 0000000..16d5284 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin.hpp @@ -0,0 +1,698 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_HPP +#define OPENCV_HAL_INTRIN_HPP + +#include +#include +#include +#include "opencv2/core/cvdef.h" + +#define OPENCV_HAL_ADD(a, b) ((a) + (b)) +#define OPENCV_HAL_AND(a, b) ((a) & (b)) +#define OPENCV_HAL_NOP(a) (a) +#define OPENCV_HAL_1ST(a, b) (a) + +namespace { +inline unsigned int trailingZeros32(unsigned int value) { +#if defined(_MSC_VER) +#if (_MSC_VER < 1700) || defined(_M_ARM) || defined(_M_ARM64) + unsigned long index = 0; + _BitScanForward(&index, value); + return (unsigned int)index; +#elif defined(__clang__) + // clang-cl doesn't export _tzcnt_u32 for non BMI systems + return value ? __builtin_ctz(value) : 32; +#else + return _tzcnt_u32(value); +#endif +#elif defined(__GNUC__) || defined(__GNUG__) + return __builtin_ctz(value); +#elif defined(__ICC) || defined(__INTEL_COMPILER) + return _bit_scan_forward(value); +#elif defined(__clang__) + return llvm.cttz.i32(value, true); +#else + static const int MultiplyDeBruijnBitPosition[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; + return MultiplyDeBruijnBitPosition[((uint32_t)((value & -value) * 0x077CB531U)) >> 27]; +#endif +} +} + +// unlike HAL API, which is in cv::hal, +// we put intrinsics into cv namespace to make its +// access from within opencv code more accessible +namespace cv { + +namespace hal { + +enum StoreMode +{ + STORE_UNALIGNED = 0, + STORE_ALIGNED = 1, + STORE_ALIGNED_NOCACHE = 2 +}; + +} + +// TODO FIXIT: Don't use "God" traits. Split on separate cases. +template struct V_TypeTraits +{ +}; + +#define CV_INTRIN_DEF_TYPE_TRAITS(type, int_type_, uint_type_, abs_type_, w_type_, q_type_, sum_type_) \ + template<> struct V_TypeTraits \ + { \ + typedef type value_type; \ + typedef int_type_ int_type; \ + typedef abs_type_ abs_type; \ + typedef uint_type_ uint_type; \ + typedef w_type_ w_type; \ + typedef q_type_ q_type; \ + typedef sum_type_ sum_type; \ + \ + static inline int_type reinterpret_int(type x) \ + { \ + union { type l; int_type i; } v; \ + v.l = x; \ + return v.i; \ + } \ + \ + static inline type reinterpret_from_int(int_type x) \ + { \ + union { type l; int_type i; } v; \ + v.i = x; \ + return v.l; \ + } \ + } + +#define CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(type, int_type_, uint_type_, abs_type_, w_type_, sum_type_) \ + template<> struct V_TypeTraits \ + { \ + typedef type value_type; \ + typedef int_type_ int_type; \ + typedef abs_type_ abs_type; \ + typedef uint_type_ uint_type; \ + typedef w_type_ w_type; \ + typedef sum_type_ sum_type; \ + \ + static inline int_type reinterpret_int(type x) \ + { \ + union { type l; int_type i; } v; \ + v.l = x; \ + return v.i; \ + } \ + \ + static inline type reinterpret_from_int(int_type x) \ + { \ + union { type l; int_type i; } v; \ + v.i = x; \ + return v.l; \ + } \ + } + +CV_INTRIN_DEF_TYPE_TRAITS(uchar, schar, uchar, uchar, ushort, unsigned, unsigned); +CV_INTRIN_DEF_TYPE_TRAITS(schar, schar, uchar, uchar, short, int, int); +CV_INTRIN_DEF_TYPE_TRAITS(ushort, short, ushort, ushort, unsigned, uint64, unsigned); +CV_INTRIN_DEF_TYPE_TRAITS(short, short, ushort, ushort, int, int64, int); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(unsigned, int, unsigned, unsigned, uint64, unsigned); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int, int, unsigned, unsigned, int64, int); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(float, int, unsigned, float, double, float); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(uint64, int64, uint64, uint64, void, uint64); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(int64, int64, uint64, uint64, void, int64); +CV_INTRIN_DEF_TYPE_TRAITS_NO_Q_TYPE(double, int64, uint64, double, void, double); + +#ifndef CV_DOXYGEN + +#ifndef CV_CPU_OPTIMIZATION_HAL_NAMESPACE +#ifdef CV_FORCE_SIMD128_CPP + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE hal_EMULATOR_CPP + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_EMULATOR_CPP { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#elif defined(CV_CPU_DISPATCH_MODE) + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#else + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE hal_baseline + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_baseline { + #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } +#endif +#endif // CV_CPU_OPTIMIZATION_HAL_NAMESPACE + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; +#endif +} + +#ifdef CV_DOXYGEN +# undef CV_AVX2 +# undef CV_SSE2 +# undef CV_NEON +# undef CV_VSX +# undef CV_FP16 +# undef CV_MSA +#endif + +#if (CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD) && !defined(CV_FORCE_SIMD128_CPP) +#define CV__SIMD_FORWARD 128 +#include "opencv2/core/hal/intrin_forward.hpp" +#endif + +#if CV_SSE2 && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_sse_em.hpp" +#include "opencv2/core/hal/intrin_sse.hpp" + +#elif CV_NEON && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_neon.hpp" + +#elif CV_VSX && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_vsx.hpp" + +#elif CV_MSA && !defined(CV_FORCE_SIMD128_CPP) + +#include "opencv2/core/hal/intrin_msa.hpp" + +#elif CV_WASM_SIMD && !defined(CV_FORCE_SIMD128_CPP) +#include "opencv2/core/hal/intrin_wasm.hpp" + +#else + +#include "opencv2/core/hal/intrin_cpp.hpp" + +#endif + +// AVX2 can be used together with SSE2, so +// we define those two sets of intrinsics at once. +// Most of the intrinsics do not conflict (the proper overloaded variant is +// resolved by the argument types, e.g. v_float32x4 ~ SSE2, v_float32x8 ~ AVX2), +// but some of AVX2 intrinsics get v256_ prefix instead of v_, e.g. v256_load() vs v_load(). +// Correspondingly, the wide intrinsics (which are mapped to the "widest" +// available instruction set) will get vx_ prefix +// (and will be mapped to v256_ counterparts) (e.g. vx_load() => v256_load()) +#if CV_AVX2 + +#define CV__SIMD_FORWARD 256 +#include "opencv2/core/hal/intrin_forward.hpp" +#include "opencv2/core/hal/intrin_avx.hpp" + +#endif + +// AVX512 can be used together with SSE2 and AVX2, so +// we define those sets of intrinsics at once. +// For some of AVX512 intrinsics get v512_ prefix instead of v_, e.g. v512_load() vs v_load(). +// Wide intrinsics will be mapped to v512_ counterparts in this case(e.g. vx_load() => v512_load()) +#if CV_AVX512_SKX + +#define CV__SIMD_FORWARD 512 +#include "opencv2/core/hal/intrin_forward.hpp" +#include "opencv2/core/hal/intrin_avx512.hpp" + +#endif + +//! @cond IGNORED + +namespace cv { + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +#endif + +#ifndef CV_SIMD128 +#define CV_SIMD128 0 +#endif + +#ifndef CV_SIMD128_CPP +#define CV_SIMD128_CPP 0 +#endif + +#ifndef CV_SIMD128_64F +#define CV_SIMD128_64F 0 +#endif + +#ifndef CV_SIMD256 +#define CV_SIMD256 0 +#endif + +#ifndef CV_SIMD256_64F +#define CV_SIMD256_64F 0 +#endif + +#ifndef CV_SIMD512 +#define CV_SIMD512 0 +#endif + +#ifndef CV_SIMD512_64F +#define CV_SIMD512_64F 0 +#endif + +#ifndef CV_SIMD128_FP16 +#define CV_SIMD128_FP16 0 +#endif + +#ifndef CV_SIMD256_FP16 +#define CV_SIMD256_FP16 0 +#endif + +#ifndef CV_SIMD512_FP16 +#define CV_SIMD512_FP16 0 +#endif + +//================================================================================================== + +template struct V_RegTraits +{ +}; + +#define CV_DEF_REG_TRAITS(prefix, _reg, lane_type, suffix, _u_reg, _w_reg, _q_reg, _int_reg, _round_reg) \ + template<> struct V_RegTraits<_reg> \ + { \ + typedef _reg reg; \ + typedef _u_reg u_reg; \ + typedef _w_reg w_reg; \ + typedef _q_reg q_reg; \ + typedef _int_reg int_reg; \ + typedef _round_reg round_reg; \ + } + +#if CV_SIMD128 || CV_SIMD128_CPP + CV_DEF_REG_TRAITS(v, v_uint8x16, uchar, u8, v_uint8x16, v_uint16x8, v_uint32x4, v_int8x16, void); + CV_DEF_REG_TRAITS(v, v_int8x16, schar, s8, v_uint8x16, v_int16x8, v_int32x4, v_int8x16, void); + CV_DEF_REG_TRAITS(v, v_uint16x8, ushort, u16, v_uint16x8, v_uint32x4, v_uint64x2, v_int16x8, void); + CV_DEF_REG_TRAITS(v, v_int16x8, short, s16, v_uint16x8, v_int32x4, v_int64x2, v_int16x8, void); + CV_DEF_REG_TRAITS(v, v_uint32x4, unsigned, u32, v_uint32x4, v_uint64x2, void, v_int32x4, void); + CV_DEF_REG_TRAITS(v, v_int32x4, int, s32, v_uint32x4, v_int64x2, void, v_int32x4, void); +#if CV_SIMD128_64F || CV_SIMD128_CPP + CV_DEF_REG_TRAITS(v, v_float32x4, float, f32, v_float32x4, v_float64x2, void, v_int32x4, v_int32x4); +#else + CV_DEF_REG_TRAITS(v, v_float32x4, float, f32, v_float32x4, void, void, v_int32x4, v_int32x4); +#endif + CV_DEF_REG_TRAITS(v, v_uint64x2, uint64, u64, v_uint64x2, void, void, v_int64x2, void); + CV_DEF_REG_TRAITS(v, v_int64x2, int64, s64, v_uint64x2, void, void, v_int64x2, void); +#if CV_SIMD128_64F + CV_DEF_REG_TRAITS(v, v_float64x2, double, f64, v_float64x2, void, void, v_int64x2, v_int32x4); +#endif +#endif + +#if CV_SIMD256 + CV_DEF_REG_TRAITS(v256, v_uint8x32, uchar, u8, v_uint8x32, v_uint16x16, v_uint32x8, v_int8x32, void); + CV_DEF_REG_TRAITS(v256, v_int8x32, schar, s8, v_uint8x32, v_int16x16, v_int32x8, v_int8x32, void); + CV_DEF_REG_TRAITS(v256, v_uint16x16, ushort, u16, v_uint16x16, v_uint32x8, v_uint64x4, v_int16x16, void); + CV_DEF_REG_TRAITS(v256, v_int16x16, short, s16, v_uint16x16, v_int32x8, v_int64x4, v_int16x16, void); + CV_DEF_REG_TRAITS(v256, v_uint32x8, unsigned, u32, v_uint32x8, v_uint64x4, void, v_int32x8, void); + CV_DEF_REG_TRAITS(v256, v_int32x8, int, s32, v_uint32x8, v_int64x4, void, v_int32x8, void); + CV_DEF_REG_TRAITS(v256, v_float32x8, float, f32, v_float32x8, v_float64x4, void, v_int32x8, v_int32x8); + CV_DEF_REG_TRAITS(v256, v_uint64x4, uint64, u64, v_uint64x4, void, void, v_int64x4, void); + CV_DEF_REG_TRAITS(v256, v_int64x4, int64, s64, v_uint64x4, void, void, v_int64x4, void); + CV_DEF_REG_TRAITS(v256, v_float64x4, double, f64, v_float64x4, void, void, v_int64x4, v_int32x8); +#endif + +#if CV_SIMD512 + CV_DEF_REG_TRAITS(v512, v_uint8x64, uchar, u8, v_uint8x64, v_uint16x32, v_uint32x16, v_int8x64, void); + CV_DEF_REG_TRAITS(v512, v_int8x64, schar, s8, v_uint8x64, v_int16x32, v_int32x16, v_int8x64, void); + CV_DEF_REG_TRAITS(v512, v_uint16x32, ushort, u16, v_uint16x32, v_uint32x16, v_uint64x8, v_int16x32, void); + CV_DEF_REG_TRAITS(v512, v_int16x32, short, s16, v_uint16x32, v_int32x16, v_int64x8, v_int16x32, void); + CV_DEF_REG_TRAITS(v512, v_uint32x16, unsigned, u32, v_uint32x16, v_uint64x8, void, v_int32x16, void); + CV_DEF_REG_TRAITS(v512, v_int32x16, int, s32, v_uint32x16, v_int64x8, void, v_int32x16, void); + CV_DEF_REG_TRAITS(v512, v_float32x16, float, f32, v_float32x16, v_float64x8, void, v_int32x16, v_int32x16); + CV_DEF_REG_TRAITS(v512, v_uint64x8, uint64, u64, v_uint64x8, void, void, v_int64x8, void); + CV_DEF_REG_TRAITS(v512, v_int64x8, int64, s64, v_uint64x8, void, void, v_int64x8, void); + CV_DEF_REG_TRAITS(v512, v_float64x8, double, f64, v_float64x8, void, void, v_int64x8, v_int32x16); +#endif +//! @endcond + +#if CV_SIMD512 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 512) +#define CV__SIMD_NAMESPACE simd512 +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD 1 + #define CV_SIMD_64F CV_SIMD512_64F + #define CV_SIMD_FP16 CV_SIMD512_FP16 + #define CV_SIMD_WIDTH 64 +//! @addtogroup core_hal_intrin +//! @{ + //! @brief Maximum available vector register capacity 8-bit unsigned integer values + typedef v_uint8x64 v_uint8; + //! @brief Maximum available vector register capacity 8-bit signed integer values + typedef v_int8x64 v_int8; + //! @brief Maximum available vector register capacity 16-bit unsigned integer values + typedef v_uint16x32 v_uint16; + //! @brief Maximum available vector register capacity 16-bit signed integer values + typedef v_int16x32 v_int16; + //! @brief Maximum available vector register capacity 32-bit unsigned integer values + typedef v_uint32x16 v_uint32; + //! @brief Maximum available vector register capacity 32-bit signed integer values + typedef v_int32x16 v_int32; + //! @brief Maximum available vector register capacity 64-bit unsigned integer values + typedef v_uint64x8 v_uint64; + //! @brief Maximum available vector register capacity 64-bit signed integer values + typedef v_int64x8 v_int64; + //! @brief Maximum available vector register capacity 32-bit floating point values (single precision) + typedef v_float32x16 v_float32; + #if CV_SIMD512_64F + //! @brief Maximum available vector register capacity 64-bit floating point values (double precision) + typedef v_float64x8 v_float64; + #endif +//! @} + + #define VXPREFIX(func) v512##func +} // namespace +using namespace CV__SIMD_NAMESPACE; +#elif CV_SIMD256 && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 256) +#define CV__SIMD_NAMESPACE simd256 +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD 1 + #define CV_SIMD_64F CV_SIMD256_64F + #define CV_SIMD_FP16 CV_SIMD256_FP16 + #define CV_SIMD_WIDTH 32 +//! @addtogroup core_hal_intrin +//! @{ + //! @brief Maximum available vector register capacity 8-bit unsigned integer values + typedef v_uint8x32 v_uint8; + //! @brief Maximum available vector register capacity 8-bit signed integer values + typedef v_int8x32 v_int8; + //! @brief Maximum available vector register capacity 16-bit unsigned integer values + typedef v_uint16x16 v_uint16; + //! @brief Maximum available vector register capacity 16-bit signed integer values + typedef v_int16x16 v_int16; + //! @brief Maximum available vector register capacity 32-bit unsigned integer values + typedef v_uint32x8 v_uint32; + //! @brief Maximum available vector register capacity 32-bit signed integer values + typedef v_int32x8 v_int32; + //! @brief Maximum available vector register capacity 64-bit unsigned integer values + typedef v_uint64x4 v_uint64; + //! @brief Maximum available vector register capacity 64-bit signed integer values + typedef v_int64x4 v_int64; + //! @brief Maximum available vector register capacity 32-bit floating point values (single precision) + typedef v_float32x8 v_float32; + #if CV_SIMD256_64F + //! @brief Maximum available vector register capacity 64-bit floating point values (double precision) + typedef v_float64x4 v_float64; + #endif +//! @} + + #define VXPREFIX(func) v256##func +} // namespace +using namespace CV__SIMD_NAMESPACE; +#elif (CV_SIMD128 || CV_SIMD128_CPP) && (!defined(CV__SIMD_FORCE_WIDTH) || CV__SIMD_FORCE_WIDTH == 128) +#if defined CV_SIMD128_CPP +#define CV__SIMD_NAMESPACE simd128_cpp +#else +#define CV__SIMD_NAMESPACE simd128 +#endif +namespace CV__SIMD_NAMESPACE { + #define CV_SIMD CV_SIMD128 + #define CV_SIMD_64F CV_SIMD128_64F + #define CV_SIMD_WIDTH 16 +//! @addtogroup core_hal_intrin +//! @{ + //! @brief Maximum available vector register capacity 8-bit unsigned integer values + typedef v_uint8x16 v_uint8; + //! @brief Maximum available vector register capacity 8-bit signed integer values + typedef v_int8x16 v_int8; + //! @brief Maximum available vector register capacity 16-bit unsigned integer values + typedef v_uint16x8 v_uint16; + //! @brief Maximum available vector register capacity 16-bit signed integer values + typedef v_int16x8 v_int16; + //! @brief Maximum available vector register capacity 32-bit unsigned integer values + typedef v_uint32x4 v_uint32; + //! @brief Maximum available vector register capacity 32-bit signed integer values + typedef v_int32x4 v_int32; + //! @brief Maximum available vector register capacity 64-bit unsigned integer values + typedef v_uint64x2 v_uint64; + //! @brief Maximum available vector register capacity 64-bit signed integer values + typedef v_int64x2 v_int64; + //! @brief Maximum available vector register capacity 32-bit floating point values (single precision) + typedef v_float32x4 v_float32; + #if CV_SIMD128_64F + //! @brief Maximum available vector register capacity 64-bit floating point values (double precision) + typedef v_float64x2 v_float64; + #endif +//! @} + + #define VXPREFIX(func) v##func +} // namespace +using namespace CV__SIMD_NAMESPACE; +#endif + +namespace CV__SIMD_NAMESPACE { +//! @addtogroup core_hal_intrin +//! @{ + //! @name Wide init with value + //! @{ + //! @brief Create maximum available capacity vector with elements set to a specific value + inline v_uint8 vx_setall_u8(uchar v) { return VXPREFIX(_setall_u8)(v); } + inline v_int8 vx_setall_s8(schar v) { return VXPREFIX(_setall_s8)(v); } + inline v_uint16 vx_setall_u16(ushort v) { return VXPREFIX(_setall_u16)(v); } + inline v_int16 vx_setall_s16(short v) { return VXPREFIX(_setall_s16)(v); } + inline v_int32 vx_setall_s32(int v) { return VXPREFIX(_setall_s32)(v); } + inline v_uint32 vx_setall_u32(unsigned v) { return VXPREFIX(_setall_u32)(v); } + inline v_float32 vx_setall_f32(float v) { return VXPREFIX(_setall_f32)(v); } + inline v_int64 vx_setall_s64(int64 v) { return VXPREFIX(_setall_s64)(v); } + inline v_uint64 vx_setall_u64(uint64 v) { return VXPREFIX(_setall_u64)(v); } +#if CV_SIMD_64F + inline v_float64 vx_setall_f64(double v) { return VXPREFIX(_setall_f64)(v); } +#endif + //! @} + + //! @name Wide init with zero + //! @{ + //! @brief Create maximum available capacity vector with elements set to zero + inline v_uint8 vx_setzero_u8() { return VXPREFIX(_setzero_u8)(); } + inline v_int8 vx_setzero_s8() { return VXPREFIX(_setzero_s8)(); } + inline v_uint16 vx_setzero_u16() { return VXPREFIX(_setzero_u16)(); } + inline v_int16 vx_setzero_s16() { return VXPREFIX(_setzero_s16)(); } + inline v_int32 vx_setzero_s32() { return VXPREFIX(_setzero_s32)(); } + inline v_uint32 vx_setzero_u32() { return VXPREFIX(_setzero_u32)(); } + inline v_float32 vx_setzero_f32() { return VXPREFIX(_setzero_f32)(); } + inline v_int64 vx_setzero_s64() { return VXPREFIX(_setzero_s64)(); } + inline v_uint64 vx_setzero_u64() { return VXPREFIX(_setzero_u64)(); } +#if CV_SIMD_64F + inline v_float64 vx_setzero_f64() { return VXPREFIX(_setzero_f64)(); } +#endif + //! @} + + //! @name Wide load from memory + //! @{ + //! @brief Load maximum available capacity register contents from memory + inline v_uint8 vx_load(const uchar * ptr) { return VXPREFIX(_load)(ptr); } + inline v_int8 vx_load(const schar * ptr) { return VXPREFIX(_load)(ptr); } + inline v_uint16 vx_load(const ushort * ptr) { return VXPREFIX(_load)(ptr); } + inline v_int16 vx_load(const short * ptr) { return VXPREFIX(_load)(ptr); } + inline v_int32 vx_load(const int * ptr) { return VXPREFIX(_load)(ptr); } + inline v_uint32 vx_load(const unsigned * ptr) { return VXPREFIX(_load)(ptr); } + inline v_float32 vx_load(const float * ptr) { return VXPREFIX(_load)(ptr); } + inline v_int64 vx_load(const int64 * ptr) { return VXPREFIX(_load)(ptr); } + inline v_uint64 vx_load(const uint64 * ptr) { return VXPREFIX(_load)(ptr); } +#if CV_SIMD_64F + inline v_float64 vx_load(const double * ptr) { return VXPREFIX(_load)(ptr); } +#endif + //! @} + + //! @name Wide load from memory(aligned) + //! @{ + //! @brief Load maximum available capacity register contents from memory(aligned) + inline v_uint8 vx_load_aligned(const uchar * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_int8 vx_load_aligned(const schar * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_uint16 vx_load_aligned(const ushort * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_int16 vx_load_aligned(const short * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_int32 vx_load_aligned(const int * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_uint32 vx_load_aligned(const unsigned * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_float32 vx_load_aligned(const float * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_int64 vx_load_aligned(const int64 * ptr) { return VXPREFIX(_load_aligned)(ptr); } + inline v_uint64 vx_load_aligned(const uint64 * ptr) { return VXPREFIX(_load_aligned)(ptr); } +#if CV_SIMD_64F + inline v_float64 vx_load_aligned(const double * ptr) { return VXPREFIX(_load_aligned)(ptr); } +#endif + //! @} + + //! @name Wide load lower half from memory + //! @{ + //! @brief Load lower half of maximum available capacity register from memory + inline v_uint8 vx_load_low(const uchar * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_int8 vx_load_low(const schar * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_uint16 vx_load_low(const ushort * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_int16 vx_load_low(const short * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_int32 vx_load_low(const int * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_uint32 vx_load_low(const unsigned * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_float32 vx_load_low(const float * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_int64 vx_load_low(const int64 * ptr) { return VXPREFIX(_load_low)(ptr); } + inline v_uint64 vx_load_low(const uint64 * ptr) { return VXPREFIX(_load_low)(ptr); } +#if CV_SIMD_64F + inline v_float64 vx_load_low(const double * ptr) { return VXPREFIX(_load_low)(ptr); } +#endif + //! @} + + //! @name Wide load halfs from memory + //! @{ + //! @brief Load maximum available capacity register contents from two memory blocks + inline v_uint8 vx_load_halves(const uchar * ptr0, const uchar * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_int8 vx_load_halves(const schar * ptr0, const schar * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_uint16 vx_load_halves(const ushort * ptr0, const ushort * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_int16 vx_load_halves(const short * ptr0, const short * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_int32 vx_load_halves(const int * ptr0, const int * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_uint32 vx_load_halves(const unsigned * ptr0, const unsigned * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_float32 vx_load_halves(const float * ptr0, const float * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_int64 vx_load_halves(const int64 * ptr0, const int64 * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } + inline v_uint64 vx_load_halves(const uint64 * ptr0, const uint64 * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } +#if CV_SIMD_64F + inline v_float64 vx_load_halves(const double * ptr0, const double * ptr1) { return VXPREFIX(_load_halves)(ptr0, ptr1); } +#endif + //! @} + + //! @name Wide LUT of elements + //! @{ + //! @brief Load maximum available capacity register contents with array elements by provided indexes + inline v_uint8 vx_lut(const uchar * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_int8 vx_lut(const schar * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_uint16 vx_lut(const ushort * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_int16 vx_lut(const short* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_int32 vx_lut(const int* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_uint32 vx_lut(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_float32 vx_lut(const float* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_int64 vx_lut(const int64 * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } + inline v_uint64 vx_lut(const uint64 * ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } +#if CV_SIMD_64F + inline v_float64 vx_lut(const double* ptr, const int* idx) { return VXPREFIX(_lut)(ptr, idx); } +#endif + //! @} + + //! @name Wide LUT of element pairs + //! @{ + //! @brief Load maximum available capacity register contents with array element pairs by provided indexes + inline v_uint8 vx_lut_pairs(const uchar * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_int8 vx_lut_pairs(const schar * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_uint16 vx_lut_pairs(const ushort * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_int16 vx_lut_pairs(const short* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_int32 vx_lut_pairs(const int* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_uint32 vx_lut_pairs(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_float32 vx_lut_pairs(const float* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_int64 vx_lut_pairs(const int64 * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } + inline v_uint64 vx_lut_pairs(const uint64 * ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } +#if CV_SIMD_64F + inline v_float64 vx_lut_pairs(const double* ptr, const int* idx) { return VXPREFIX(_lut_pairs)(ptr, idx); } +#endif + //! @} + + //! @name Wide LUT of element quads + //! @{ + //! @brief Load maximum available capacity register contents with array element quads by provided indexes + inline v_uint8 vx_lut_quads(const uchar* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_int8 vx_lut_quads(const schar* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_uint16 vx_lut_quads(const ushort* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_int16 vx_lut_quads(const short* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_int32 vx_lut_quads(const int* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_uint32 vx_lut_quads(const unsigned* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + inline v_float32 vx_lut_quads(const float* ptr, const int* idx) { return VXPREFIX(_lut_quads)(ptr, idx); } + //! @} + + //! @name Wide load with double expansion + //! @{ + //! @brief Load maximum available capacity register contents from memory with double expand + inline v_uint16 vx_load_expand(const uchar * ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_int16 vx_load_expand(const schar * ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_uint32 vx_load_expand(const ushort * ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_int32 vx_load_expand(const short* ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_int64 vx_load_expand(const int* ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_uint64 vx_load_expand(const unsigned* ptr) { return VXPREFIX(_load_expand)(ptr); } + inline v_float32 vx_load_expand(const float16_t * ptr) { return VXPREFIX(_load_expand)(ptr); } + //! @} + + //! @name Wide load with quad expansion + //! @{ + //! @brief Load maximum available capacity register contents from memory with quad expand + inline v_uint32 vx_load_expand_q(const uchar * ptr) { return VXPREFIX(_load_expand_q)(ptr); } + inline v_int32 vx_load_expand_q(const schar * ptr) { return VXPREFIX(_load_expand_q)(ptr); } + //! @} + + /** @brief SIMD processing state cleanup call */ + inline void vx_cleanup() { VXPREFIX(_cleanup)(); } + + +//! @cond IGNORED + + // backward compatibility + template static inline + void vx_store(_Tp* dst, const _Tvec& v) { return v_store(dst, v); } + // backward compatibility + template static inline + void vx_store_aligned(_Tp* dst, const _Tvec& v) { return v_store_aligned(dst, v); } + +//! @endcond + + +//! @} + #undef VXPREFIX +} // namespace + +//! @cond IGNORED +#ifndef CV_SIMD_64F +#define CV_SIMD_64F 0 +#endif + +#ifndef CV_SIMD_FP16 +#define CV_SIMD_FP16 0 //!< Defined to 1 on native support of operations with float16x8_t / float16x16_t (SIMD256) types +#endif + +#ifndef CV_SIMD +#define CV_SIMD 0 +#endif + +#include "simd_utils.impl.hpp" + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +#endif + +} // cv:: + +//! @endcond + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx.hpp new file mode 100644 index 0000000..54e8927 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx.hpp @@ -0,0 +1,3165 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_AVX_HPP +#define OPENCV_HAL_INTRIN_AVX_HPP + +#define CV_SIMD256 1 +#define CV_SIMD256_64F 1 +#define CV_SIMD256_FP16 0 // no native operations with FP16 type. Only load/store from float32x8 are available (if CV_FP16 == 1) + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Utils //////////// + +inline __m256i _v256_combine(const __m128i& lo, const __m128i& hi) +{ return _mm256_inserti128_si256(_mm256_castsi128_si256(lo), hi, 1); } + +inline __m256 _v256_combine(const __m128& lo, const __m128& hi) +{ return _mm256_insertf128_ps(_mm256_castps128_ps256(lo), hi, 1); } + +inline __m256d _v256_combine(const __m128d& lo, const __m128d& hi) +{ return _mm256_insertf128_pd(_mm256_castpd128_pd256(lo), hi, 1); } + +inline int _v_cvtsi256_si32(const __m256i& a) +{ return _mm_cvtsi128_si32(_mm256_castsi256_si128(a)); } + +inline __m256i _v256_shuffle_odd_64(const __m256i& v) +{ return _mm256_permute4x64_epi64(v, _MM_SHUFFLE(3, 1, 2, 0)); } + +inline __m256d _v256_shuffle_odd_64(const __m256d& v) +{ return _mm256_permute4x64_pd(v, _MM_SHUFFLE(3, 1, 2, 0)); } + +template +inline __m256i _v256_permute2x128(const __m256i& a, const __m256i& b) +{ return _mm256_permute2x128_si256(a, b, imm); } + +template +inline __m256 _v256_permute2x128(const __m256& a, const __m256& b) +{ return _mm256_permute2f128_ps(a, b, imm); } + +template +inline __m256d _v256_permute2x128(const __m256d& a, const __m256d& b) +{ return _mm256_permute2f128_pd(a, b, imm); } + +template +inline _Tpvec v256_permute2x128(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_v256_permute2x128(a.val, b.val)); } + +template +inline __m256i _v256_permute4x64(const __m256i& a) +{ return _mm256_permute4x64_epi64(a, imm); } + +template +inline __m256d _v256_permute4x64(const __m256d& a) +{ return _mm256_permute4x64_pd(a, imm); } + +template +inline _Tpvec v256_permute4x64(const _Tpvec& a) +{ return _Tpvec(_v256_permute4x64(a.val)); } + +inline __m128i _v256_extract_high(const __m256i& v) +{ return _mm256_extracti128_si256(v, 1); } + +inline __m128 _v256_extract_high(const __m256& v) +{ return _mm256_extractf128_ps(v, 1); } + +inline __m128d _v256_extract_high(const __m256d& v) +{ return _mm256_extractf128_pd(v, 1); } + +inline __m128i _v256_extract_low(const __m256i& v) +{ return _mm256_castsi256_si128(v); } + +inline __m128 _v256_extract_low(const __m256& v) +{ return _mm256_castps256_ps128(v); } + +inline __m128d _v256_extract_low(const __m256d& v) +{ return _mm256_castpd256_pd128(v); } + +inline __m256i _v256_packs_epu32(const __m256i& a, const __m256i& b) +{ + const __m256i m = _mm256_set1_epi32(65535); + __m256i am = _mm256_min_epu32(a, m); + __m256i bm = _mm256_min_epu32(b, m); + return _mm256_packus_epi32(am, bm); +} + +template +inline int _v256_extract_epi8(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi8(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 4)); + return _mm_extract_epi8(b, i & 15); // SSE4.1 +#endif +} + +template +inline int _v256_extract_epi16(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi16(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 3)); + return _mm_extract_epi16(b, i & 7); // SSE2 +#endif +} + +template +inline int _v256_extract_epi32(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi32(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 2)); + return _mm_extract_epi32(b, i & 3); // SSE4.1 +#endif +} + +template +inline int64 _v256_extract_epi64(const __m256i& a) +{ +#if defined(CV__SIMD_HAVE_mm256_extract_epi8) || (CV_AVX2 && (!defined(_MSC_VER) || _MSC_VER >= 1910/*MSVS 2017*/)) + return _mm256_extract_epi64(a, i); +#else + __m128i b = _mm256_extractf128_si256(a, ((i) >> 1)); + return _mm_extract_epi64(b, i & 1); // SSE4.1 +#endif +} + +///////// Types //////////// + +struct v_uint8x32 +{ + typedef uchar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_uint8x32(__m256i v) : val(v) {} + v_uint8x32(uchar v0, uchar v1, uchar v2, uchar v3, + uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, + uchar v12, uchar v13, uchar v14, uchar v15, + uchar v16, uchar v17, uchar v18, uchar v19, + uchar v20, uchar v21, uchar v22, uchar v23, + uchar v24, uchar v25, uchar v26, uchar v27, + uchar v28, uchar v29, uchar v30, uchar v31) + { + val = _mm256_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6 , (char)v7, (char)v8, (char)v9, + (char)v10, (char)v11, (char)v12, (char)v13, (char)v14, (char)v15, + (char)v16, (char)v17, (char)v18, (char)v19, (char)v20, (char)v21, + (char)v22, (char)v23, (char)v24, (char)v25, (char)v26, (char)v27, + (char)v28, (char)v29, (char)v30, (char)v31); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint8x32() {} + + uchar get0() const { return (uchar)_v_cvtsi256_si32(val); } +}; + +struct v_int8x32 +{ + typedef schar lane_type; + enum { nlanes = 32 }; + __m256i val; + + explicit v_int8x32(__m256i v) : val(v) {} + v_int8x32(schar v0, schar v1, schar v2, schar v3, + schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, + schar v12, schar v13, schar v14, schar v15, + schar v16, schar v17, schar v18, schar v19, + schar v20, schar v21, schar v22, schar v23, + schar v24, schar v25, schar v26, schar v27, + schar v28, schar v29, schar v30, schar v31) + { + val = _mm256_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, + v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int8x32() {} + + schar get0() const { return (schar)_v_cvtsi256_si32(val); } +}; + +struct v_uint16x16 +{ + typedef ushort lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_uint16x16(__m256i v) : val(v) {} + v_uint16x16(ushort v0, ushort v1, ushort v2, ushort v3, + ushort v4, ushort v5, ushort v6, ushort v7, + ushort v8, ushort v9, ushort v10, ushort v11, + ushort v12, ushort v13, ushort v14, ushort v15) + { + val = _mm256_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7, (short)v8, (short)v9, + (short)v10, (short)v11, (short)v12, (short)v13, (short)v14, (short)v15); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint16x16() {} + + ushort get0() const { return (ushort)_v_cvtsi256_si32(val); } +}; + +struct v_int16x16 +{ + typedef short lane_type; + enum { nlanes = 16 }; + __m256i val; + + explicit v_int16x16(__m256i v) : val(v) {} + v_int16x16(short v0, short v1, short v2, short v3, + short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, + short v12, short v13, short v14, short v15) + { + val = _mm256_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int16x16() {} + + short get0() const { return (short)_v_cvtsi256_si32(val); } +}; + +struct v_uint32x8 +{ + typedef unsigned lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_uint32x8(__m256i v) : val(v) {} + v_uint32x8(unsigned v0, unsigned v1, unsigned v2, unsigned v3, + unsigned v4, unsigned v5, unsigned v6, unsigned v7) + { + val = _mm256_setr_epi32((unsigned)v0, (unsigned)v1, (unsigned)v2, + (unsigned)v3, (unsigned)v4, (unsigned)v5, (unsigned)v6, (unsigned)v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_uint32x8() {} + + unsigned get0() const { return (unsigned)_v_cvtsi256_si32(val); } +}; + +struct v_int32x8 +{ + typedef int lane_type; + enum { nlanes = 8 }; + __m256i val; + + explicit v_int32x8(__m256i v) : val(v) {} + v_int32x8(int v0, int v1, int v2, int v3, + int v4, int v5, int v6, int v7) + { + val = _mm256_setr_epi32(v0, v1, v2, v3, v4, v5, v6, v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_int32x8() {} + + int get0() const { return _v_cvtsi256_si32(val); } +}; + +struct v_float32x8 +{ + typedef float lane_type; + enum { nlanes = 8 }; + __m256 val; + + explicit v_float32x8(__m256 v) : val(v) {} + v_float32x8(float v0, float v1, float v2, float v3, + float v4, float v5, float v6, float v7) + { + val = _mm256_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7); + } + /* coverity[uninit_ctor]: suppress warning */ + v_float32x8() {} + + float get0() const { return _mm_cvtss_f32(_mm256_castps256_ps128(val)); } +}; + +struct v_uint64x4 +{ + typedef uint64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_uint64x4(__m256i v) : val(v) {} + v_uint64x4(uint64 v0, uint64 v1, uint64 v2, uint64 v3) + { val = _mm256_setr_epi64x((int64)v0, (int64)v1, (int64)v2, (int64)v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_uint64x4() {} + + uint64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (uint64)_mm_cvtsi128_si64(_mm256_castsi256_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm256_castsi256_si128(val)); + int b = _mm_cvtsi128_si32(_mm256_castsi256_si128(_mm256_srli_epi64(val, 32))); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #endif + } +}; + +struct v_int64x4 +{ + typedef int64 lane_type; + enum { nlanes = 4 }; + __m256i val; + + explicit v_int64x4(__m256i v) : val(v) {} + v_int64x4(int64 v0, int64 v1, int64 v2, int64 v3) + { val = _mm256_setr_epi64x(v0, v1, v2, v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_int64x4() {} + + int64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (int64)_mm_cvtsi128_si64(_mm256_castsi256_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm256_castsi256_si128(val)); + int b = _mm_cvtsi128_si32(_mm256_castsi256_si128(_mm256_srli_epi64(val, 32))); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #endif + } +}; + +struct v_float64x4 +{ + typedef double lane_type; + enum { nlanes = 4 }; + __m256d val; + + explicit v_float64x4(__m256d v) : val(v) {} + v_float64x4(double v0, double v1, double v2, double v3) + { val = _mm256_setr_pd(v0, v1, v2, v3); } + /* coverity[uninit_ctor]: suppress warning */ + v_float64x4() {} + + double get0() const { return _mm_cvtsd_f64(_mm256_castpd256_pd128(val)); } +}; + +//////////////// Load and store operations /////////////// + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE(_Tpvec, _Tp) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(_mm256_loadu_si256((const __m256i*)ptr)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm256_load_si256((const __m256i*)ptr)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + __m128i v128 = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpvec(_mm256_castsi128_si256(v128)); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + __m128i vlo = _mm_loadu_si128((const __m128i*)ptr0); \ + __m128i vhi = _mm_loadu_si128((const __m128i*)ptr1); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm256_store_si256((__m256i*)ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm256_stream_si256((__m256i*)ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm256_storeu_si256((__m256i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm256_stream_si256((__m256i*)ptr, a.val); \ + else \ + _mm256_store_si256((__m256i*)ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_si128((__m128i*)ptr, _v256_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_si128((__m128i*)ptr, _v256_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint8x32, uchar) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int8x32, schar) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint16x16, ushort) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int16x16, short) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint32x8, unsigned) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int32x8, int) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_uint64x4, uint64) +OPENCV_HAL_IMPL_AVX_LOADSTORE(v_int64x4, int64) + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(_Tpvec, _Tp, suffix, halfreg) \ + inline _Tpvec v256_load(const _Tp* ptr) \ + { return _Tpvec(_mm256_loadu_##suffix(ptr)); } \ + inline _Tpvec v256_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm256_load_##suffix(ptr)); } \ + inline _Tpvec v256_load_low(const _Tp* ptr) \ + { \ + return _Tpvec(_mm256_cast##suffix##128_##suffix##256 \ + (_mm_loadu_##suffix(ptr))); \ + } \ + inline _Tpvec v256_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + halfreg vlo = _mm_loadu_##suffix(ptr0); \ + halfreg vhi = _mm_loadu_##suffix(ptr1); \ + return _Tpvec(_v256_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm256_store_##suffix(ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm256_stream_##suffix(ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm256_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm256_stream_##suffix(ptr, a.val); \ + else \ + _mm256_store_##suffix(ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_##suffix(ptr, _v256_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm_storeu_##suffix(ptr, _v256_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(v_float32x8, float, ps, __m128) +OPENCV_HAL_IMPL_AVX_LOADSTORE_FLT(v_float64x4, double, pd, __m128d) + +#define OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, _Tpvecf, suffix, cast) \ + inline _Tpvec v_reinterpret_as_##suffix(const _Tpvecf& a) \ + { return _Tpvec(cast(a.val)); } + +#define OPENCV_HAL_IMPL_AVX_INIT(_Tpvec, _Tp, suffix, ssuffix, ctype_s) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(_mm256_setzero_si256()); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm256_set1_##ssuffix((ctype_s)v)); } \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int8x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int16x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int32x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int64x4, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_float32x8, suffix, _mm256_castps_si256) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_float64x4, suffix, _mm256_castpd_si256) + +OPENCV_HAL_IMPL_AVX_INIT(v_uint8x32, uchar, u8, epi8, char) +OPENCV_HAL_IMPL_AVX_INIT(v_int8x32, schar, s8, epi8, char) +OPENCV_HAL_IMPL_AVX_INIT(v_uint16x16, ushort, u16, epi16, short) +OPENCV_HAL_IMPL_AVX_INIT(v_int16x16, short, s16, epi16, short) +OPENCV_HAL_IMPL_AVX_INIT(v_uint32x8, unsigned, u32, epi32, int) +OPENCV_HAL_IMPL_AVX_INIT(v_int32x8, int, s32, epi32, int) +OPENCV_HAL_IMPL_AVX_INIT(v_uint64x4, uint64, u64, epi64x, int64) +OPENCV_HAL_IMPL_AVX_INIT(v_int64x4, int64, s64, epi64x, int64) + +#define OPENCV_HAL_IMPL_AVX_INIT_FLT(_Tpvec, _Tp, suffix, zsuffix, cast) \ + inline _Tpvec v256_setzero_##suffix() \ + { return _Tpvec(_mm256_setzero_##zsuffix()); } \ + inline _Tpvec v256_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm256_set1_##zsuffix(v)); } \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint8x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int8x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint16x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int16x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint32x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int32x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_uint64x4, suffix, cast) \ + OPENCV_HAL_IMPL_AVX_CAST(_Tpvec, v_int64x4, suffix, cast) + +OPENCV_HAL_IMPL_AVX_INIT_FLT(v_float32x8, float, f32, ps, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_INIT_FLT(v_float64x4, double, f64, pd, _mm256_castsi256_pd) + +inline v_float32x8 v_reinterpret_as_f32(const v_float32x8& a) +{ return a; } +inline v_float32x8 v_reinterpret_as_f32(const v_float64x4& a) +{ return v_float32x8(_mm256_castpd_ps(a.val)); } + +inline v_float64x4 v_reinterpret_as_f64(const v_float64x4& a) +{ return a; } +inline v_float64x4 v_reinterpret_as_f64(const v_float32x8& a) +{ return v_float64x4(_mm256_castps_pd(a.val)); } + +/* Recombine */ +/*#define OPENCV_HAL_IMPL_AVX_COMBINE(_Tpvec, perm) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(perm(a.val, b.val, 0x20)); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(perm(a.val, b.val, 0x31)); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { c = v_combine_low(a, b); d = v_combine_high(a, b); } + +#define OPENCV_HAL_IMPL_AVX_UNPACKS(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_COMBINE(_Tpvec, _mm256_permute2x128_si256) \ + inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, \ + _Tpvec& b0, _Tpvec& b1) \ + { \ + __m256i v0 = _v256_shuffle_odd_64(a0.val); \ + __m256i v1 = _v256_shuffle_odd_64(a1.val); \ + b0.val = _mm256_unpacklo_##suffix(v0, v1); \ + b1.val = _mm256_unpackhi_##suffix(v0, v1); \ + } + +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_uint64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACKS(v_int64x4, epi64) +OPENCV_HAL_IMPL_AVX_COMBINE(v_float32x8, _mm256_permute2f128_ps) +OPENCV_HAL_IMPL_AVX_COMBINE(v_float64x4, _mm256_permute2f128_pd) + +inline void v_zip(const v_float32x8& a0, const v_float32x8& a1, v_float32x8& b0, v_float32x8& b1) +{ + __m256 v0 = _mm256_unpacklo_ps(a0.val, a1.val); + __m256 v1 = _mm256_unpackhi_ps(a0.val, a1.val); + v_recombine(v_float32x8(v0), v_float32x8(v1), b0, b1); +} + +inline void v_zip(const v_float64x4& a0, const v_float64x4& a1, v_float64x4& b0, v_float64x4& b1) +{ + __m256d v0 = _v_shuffle_odd_64(a0.val); + __m256d v1 = _v_shuffle_odd_64(a1.val); + b0.val = _mm256_unpacklo_pd(v0, v1); + b1.val = _mm256_unpackhi_pd(v0, v1); +}*/ + +//////////////// Variant Value reordering /////////////// + +// unpacks +#define OPENCV_HAL_IMPL_AVX_UNPACK(_Tpvec, suffix) \ + inline _Tpvec v256_unpacklo(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_unpacklo_##suffix(a.val, b.val)); } \ + inline _Tpvec v256_unpackhi(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_unpackhi_##suffix(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_UNPACK(v_uint64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACK(v_int64x4, epi64) +OPENCV_HAL_IMPL_AVX_UNPACK(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_UNPACK(v_float64x4, pd) + +// blend +#define OPENCV_HAL_IMPL_AVX_BLEND(_Tpvec, suffix) \ + template \ + inline _Tpvec v256_blend(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_blend_##suffix(a.val, b.val, m)); } + +OPENCV_HAL_IMPL_AVX_BLEND(v_uint16x16, epi16) +OPENCV_HAL_IMPL_AVX_BLEND(v_int16x16, epi16) +OPENCV_HAL_IMPL_AVX_BLEND(v_uint32x8, epi32) +OPENCV_HAL_IMPL_AVX_BLEND(v_int32x8, epi32) +OPENCV_HAL_IMPL_AVX_BLEND(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_BLEND(v_float64x4, pd) + +template +inline v_uint64x4 v256_blend(const v_uint64x4& a, const v_uint64x4& b) +{ + enum {M0 = m}; + enum {M1 = (M0 | (M0 << 2)) & 0x33}; + enum {M2 = (M1 | (M1 << 1)) & 0x55}; + enum {MM = M2 | (M2 << 1)}; + return v_uint64x4(_mm256_blend_epi32(a.val, b.val, MM)); +} +template +inline v_int64x4 v256_blend(const v_int64x4& a, const v_int64x4& b) +{ return v_int64x4(v256_blend(v_uint64x4(a.val), v_uint64x4(b.val)).val); } + +// shuffle +// todo: emulate 64bit +#define OPENCV_HAL_IMPL_AVX_SHUFFLE(_Tpvec, intrin) \ + template \ + inline _Tpvec v256_shuffle(const _Tpvec& a) \ + { return _Tpvec(_mm256_##intrin(a.val, m)); } + +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_uint32x8, shuffle_epi32) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_int32x8, shuffle_epi32) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_float32x8, permute_ps) +OPENCV_HAL_IMPL_AVX_SHUFFLE(v_float64x4, permute_pd) + +template +inline void v256_zip(const _Tpvec& a, const _Tpvec& b, _Tpvec& ab0, _Tpvec& ab1) +{ + ab0 = v256_unpacklo(a, b); + ab1 = v256_unpackhi(a, b); +} + +template +inline _Tpvec v256_combine_diagonal(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_mm256_blend_epi32(a.val, b.val, 0xf0)); } + +inline v_float32x8 v256_combine_diagonal(const v_float32x8& a, const v_float32x8& b) +{ return v256_blend<0xf0>(a, b); } + +inline v_float64x4 v256_combine_diagonal(const v_float64x4& a, const v_float64x4& b) +{ return v256_blend<0xc>(a, b); } + +template +inline _Tpvec v256_alignr_128(const _Tpvec& a, const _Tpvec& b) +{ return v256_permute2x128<0x21>(a, b); } + +template +inline _Tpvec v256_alignr_64(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(_mm256_alignr_epi8(a.val, b.val, 8)); } +inline v_float64x4 v256_alignr_64(const v_float64x4& a, const v_float64x4& b) +{ return v_float64x4(_mm256_shuffle_pd(b.val, a.val, _MM_SHUFFLE(0, 0, 1, 1))); } +// todo: emulate float32 + +template +inline _Tpvec v256_swap_halves(const _Tpvec& a) +{ return v256_permute2x128<1>(a, a); } + +template +inline _Tpvec v256_reverse_64(const _Tpvec& a) +{ return v256_permute4x64<_MM_SHUFFLE(0, 1, 2, 3)>(a); } + +// ZIP +#define OPENCV_HAL_IMPL_AVX_ZIP(_Tpvec) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x20>(a, b); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return v256_permute2x128<0x31>(a, b); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { \ + _Tpvec a1b0 = v256_alignr_128(a, b); \ + c = v256_combine_diagonal(a, a1b0); \ + d = v256_combine_diagonal(a1b0, b); \ + } \ + inline void v_zip(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& ab0, _Tpvec& ab1) \ + { \ + _Tpvec ab0ab2, ab1ab3; \ + v256_zip(a, b, ab0ab2, ab1ab3); \ + v_recombine(ab0ab2, ab1ab3, ab0, ab1); \ + } + +OPENCV_HAL_IMPL_AVX_ZIP(v_uint8x32) +OPENCV_HAL_IMPL_AVX_ZIP(v_int8x32) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint16x16) +OPENCV_HAL_IMPL_AVX_ZIP(v_int16x16) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_int32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_uint64x4) +OPENCV_HAL_IMPL_AVX_ZIP(v_int64x4) +OPENCV_HAL_IMPL_AVX_ZIP(v_float32x8) +OPENCV_HAL_IMPL_AVX_ZIP(v_float64x4) + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ + +/** Arithmetics **/ +#define OPENCV_HAL_IMPL_AVX_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint8x32, _mm256_adds_epu8) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint8x32, _mm256_subs_epu8) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int8x32, _mm256_adds_epi8) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int8x32, _mm256_subs_epi8) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint16x16, _mm256_adds_epu16) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint16x16, _mm256_subs_epu16) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int16x16, _mm256_adds_epi16) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int16x16, _mm256_subs_epi16) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint32x8, _mm256_add_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint32x8, _mm256_sub_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_uint32x8, _mm256_mullo_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int32x8, _mm256_add_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int32x8, _mm256_sub_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_int32x8, _mm256_mullo_epi32) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_uint64x4, _mm256_add_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_uint64x4, _mm256_sub_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_int64x4, _mm256_add_epi64) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_int64x4, _mm256_sub_epi64) + +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_float32x8, _mm256_add_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_float32x8, _mm256_sub_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_float32x8, _mm256_mul_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(/, v_float32x8, _mm256_div_ps) +OPENCV_HAL_IMPL_AVX_BIN_OP(+, v_float64x4, _mm256_add_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(-, v_float64x4, _mm256_sub_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(*, v_float64x4, _mm256_mul_pd) +OPENCV_HAL_IMPL_AVX_BIN_OP(/, v_float64x4, _mm256_div_pd) + +// saturating multiply 8-bit, 16-bit +inline v_uint8x32 operator * (const v_uint8x32& a, const v_uint8x32& b) +{ + v_uint16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_int8x32 operator * (const v_int8x32& a, const v_int8x32& b) +{ + v_int16x16 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_uint16x16 operator * (const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i pl = _mm256_mullo_epi16(a.val, b.val); + __m256i ph = _mm256_mulhi_epu16(a.val, b.val); + __m256i p0 = _mm256_unpacklo_epi16(pl, ph); + __m256i p1 = _mm256_unpackhi_epi16(pl, ph); + return v_uint16x16(_v256_packs_epu32(p0, p1)); +} +inline v_int16x16 operator * (const v_int16x16& a, const v_int16x16& b) +{ + __m256i pl = _mm256_mullo_epi16(a.val, b.val); + __m256i ph = _mm256_mulhi_epi16(a.val, b.val); + __m256i p0 = _mm256_unpacklo_epi16(pl, ph); + __m256i p1 = _mm256_unpackhi_epi16(pl, ph); + return v_int16x16(_mm256_packs_epi32(p0, p1)); +} +inline v_uint8x32& operator *= (v_uint8x32& a, const v_uint8x32& b) +{ a = a * b; return a; } +inline v_int8x32& operator *= (v_int8x32& a, const v_int8x32& b) +{ a = a * b; return a; } +inline v_uint16x16& operator *= (v_uint16x16& a, const v_uint16x16& b) +{ a = a * b; return a; } +inline v_int16x16& operator *= (v_int16x16& a, const v_int16x16& b) +{ a = a * b; return a; } + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_AVX_BIN_FUNC(func, _Tpvec, intrin) \ + inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_uint8x32, _mm256_add_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_int8x32, _mm256_add_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_uint16x16, _mm256_add_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_add_wrap, v_int16x16, _mm256_add_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_uint8x32, _mm256_sub_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_int8x32, _mm256_sub_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_uint16x16, _mm256_sub_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_sub_wrap, v_int16x16, _mm256_sub_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_mul_wrap, v_uint16x16, _mm256_mullo_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_mul_wrap, v_int16x16, _mm256_mullo_epi16) + +inline v_uint8x32 v_mul_wrap(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i ad = _mm256_srai_epi16(a.val, 8); + __m256i bd = _mm256_srai_epi16(b.val, 8); + __m256i p0 = _mm256_mullo_epi16(a.val, b.val); // even + __m256i p1 = _mm256_slli_epi16(_mm256_mullo_epi16(ad, bd), 8); // odd + + const __m256i b01 = _mm256_set1_epi32(0xFF00FF00); + return v_uint8x32(_mm256_blendv_epi8(p0, p1, b01)); +} +inline v_int8x32 v_mul_wrap(const v_int8x32& a, const v_int8x32& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +// Multiply and expand +inline void v_mul_expand(const v_uint8x32& a, const v_uint8x32& b, + v_uint16x16& c, v_uint16x16& d) +{ + v_uint16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x32& a, const v_int8x32& b, + v_int16x16& c, v_int16x16& d) +{ + v_int16x16 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x16& a, const v_int16x16& b, + v_int32x8& c, v_int32x8& d) +{ + v_int16x16 vhi = v_int16x16(_mm256_mulhi_epi16(a.val, b.val)); + + v_int16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_s32(v0); + d = v_reinterpret_as_s32(v1); +} + +inline void v_mul_expand(const v_uint16x16& a, const v_uint16x16& b, + v_uint32x8& c, v_uint32x8& d) +{ + v_uint16x16 vhi = v_uint16x16(_mm256_mulhi_epu16(a.val, b.val)); + + v_uint16x16 v0, v1; + v_zip(v_mul_wrap(a, b), vhi, v0, v1); + + c = v_reinterpret_as_u32(v0); + d = v_reinterpret_as_u32(v1); +} + +inline void v_mul_expand(const v_uint32x8& a, const v_uint32x8& b, + v_uint64x4& c, v_uint64x4& d) +{ + __m256i v0 = _mm256_mul_epu32(a.val, b.val); + __m256i v1 = _mm256_mul_epu32(_mm256_srli_epi64(a.val, 32), _mm256_srli_epi64(b.val, 32)); + v_zip(v_uint64x4(v0), v_uint64x4(v1), c, d); +} + +inline v_int16x16 v_mul_hi(const v_int16x16& a, const v_int16x16& b) { return v_int16x16(_mm256_mulhi_epi16(a.val, b.val)); } +inline v_uint16x16 v_mul_hi(const v_uint16x16& a, const v_uint16x16& b) { return v_uint16x16(_mm256_mulhi_epu16(a.val, b.val)); } + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_AVX_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, srai) \ + inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm256_slli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm256_slli_##suffix(a.val, imm)); } \ + inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm256_srli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ + { return _Tpsvec(srai(a.val, imm)); } \ + template \ + inline _Tpuvec v_shl(const _Tpuvec& a) \ + { return _Tpuvec(_mm256_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shl(const _Tpsvec& a) \ + { return _Tpsvec(_mm256_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shr(const _Tpuvec& a) \ + { return _Tpuvec(_mm256_srli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shr(const _Tpsvec& a) \ + { return _Tpsvec(srai(a.val, imm)); } + +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint16x16, v_int16x16, epi16, _mm256_srai_epi16) +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint32x8, v_int32x8, epi32, _mm256_srai_epi32) + +inline __m256i _mm256_srai_epi64xx(const __m256i a, int imm) +{ + __m256i d = _mm256_set1_epi64x((int64)1 << 63); + __m256i r = _mm256_srli_epi64(_mm256_add_epi64(a, d), imm); + return _mm256_sub_epi64(r, _mm256_srli_epi64(d, imm)); +} +OPENCV_HAL_IMPL_AVX_SHIFT_OP(v_uint64x4, v_int64x4, epi64, _mm256_srai_epi64xx) + + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_AVX_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(&, _Tpvec, _mm256_and_##suffix) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(|, _Tpvec, _mm256_or_##suffix) \ + OPENCV_HAL_IMPL_AVX_BIN_OP(^, _Tpvec, _mm256_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(_mm256_xor_##suffix(a.val, not_const)); } + +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint8x32, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int8x32, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint16x16, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int16x16, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint32x8, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int32x8, si256, _mm256_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_uint64x4, si256, _mm256_set1_epi64x(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_int64x4, si256, _mm256_set1_epi64x(-1)) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_float32x8, ps, _mm256_castsi256_ps(_mm256_set1_epi32(-1))) +OPENCV_HAL_IMPL_AVX_LOGIC_OP(v_float64x4, pd, _mm256_castsi256_pd(_mm256_set1_epi32(-1))) + +/** Select **/ +#define OPENCV_HAL_IMPL_AVX_SELECT(_Tpvec, suffix) \ + inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_blendv_##suffix(b.val, a.val, mask.val)); } + +OPENCV_HAL_IMPL_AVX_SELECT(v_uint8x32, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int8x32, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_uint16x16, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int16x16, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_uint32x8, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_int32x8, epi8) +OPENCV_HAL_IMPL_AVX_SELECT(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_SELECT(v_float64x4, pd) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpvec) \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } \ + inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ + { return b > a; } \ + inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a < b); } \ + inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ + { return b >= a; } + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_INT(_Tpuvec, _Tpsvec, suffix, sbit) \ + inline _Tpuvec operator == (const _Tpuvec& a, const _Tpuvec& b) \ + { return _Tpuvec(_mm256_cmpeq_##suffix(a.val, b.val)); } \ + inline _Tpuvec operator > (const _Tpuvec& a, const _Tpuvec& b) \ + { \ + __m256i smask = _mm256_set1_##suffix(sbit); \ + return _Tpuvec(_mm256_cmpgt_##suffix( \ + _mm256_xor_si256(a.val, smask), \ + _mm256_xor_si256(b.val, smask))); \ + } \ + inline _Tpsvec operator == (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(_mm256_cmpeq_##suffix(a.val, b.val)); } \ + inline _Tpsvec operator > (const _Tpsvec& a, const _Tpsvec& b) \ + { return _Tpsvec(_mm256_cmpgt_##suffix(a.val, b.val)); } \ + OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpuvec) \ + OPENCV_HAL_IMPL_AVX_CMP_OP_OV(_Tpsvec) + +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint8x32, v_int8x32, epi8, (char)-128) +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint16x16, v_int16x16, epi16, (short)-32768) +OPENCV_HAL_IMPL_AVX_CMP_OP_INT(v_uint32x8, v_int32x8, epi32, (int)0x80000000) + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(_Tpvec) \ + inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_cmpeq_epi64(a.val, b.val)); } \ + inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ + { return ~(a == b); } + +OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(v_uint64x4) +OPENCV_HAL_IMPL_AVX_CMP_OP_64BIT(v_int64x4) + +#define OPENCV_HAL_IMPL_AVX_CMP_FLT(bin_op, imm8, _Tpvec, suffix) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm256_cmp_##suffix(a.val, b.val, imm8)); } + +#define OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(==, _CMP_EQ_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(!=, _CMP_NEQ_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(<, _CMP_LT_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(>, _CMP_GT_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(<=, _CMP_LE_OQ, _Tpvec, suffix) \ + OPENCV_HAL_IMPL_AVX_CMP_FLT(>=, _CMP_GE_OQ, _Tpvec, suffix) + +OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_CMP_OP_FLT(v_float64x4, pd) + +inline v_float32x8 v_not_nan(const v_float32x8& a) +{ return v_float32x8(_mm256_cmp_ps(a.val, a.val, _CMP_ORD_Q)); } +inline v_float64x4 v_not_nan(const v_float64x4& a) +{ return v_float64x4(_mm256_cmp_pd(a.val, a.val, _CMP_ORD_Q)); } + +/** min/max **/ +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint8x32, _mm256_min_epu8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint8x32, _mm256_max_epu8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int8x32, _mm256_min_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int8x32, _mm256_max_epi8) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint16x16, _mm256_min_epu16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint16x16, _mm256_max_epu16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int16x16, _mm256_min_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int16x16, _mm256_max_epi16) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_uint32x8, _mm256_min_epu32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_uint32x8, _mm256_max_epu32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_int32x8, _mm256_min_epi32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_int32x8, _mm256_max_epi32) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_float32x8, _mm256_min_ps) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_float32x8, _mm256_max_ps) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_min, v_float64x4, _mm256_min_pd) +OPENCV_HAL_IMPL_AVX_BIN_FUNC(v_max, v_float64x4, _mm256_max_pd) + +/** Rotate **/ +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_R = (16 - imm) & 0xFF}; + enum {IMM_R2 = (32 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _mm256_permute2x128_si256(a.val, b.val, 0x03); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(a.val, swap, IMM_R)); + return v_uint8x32(_mm256_alignr_epi8(swap, b.val, IMM_R2)); // imm < 32 +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a, const v_uint8x32& b) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm == 32) return b; + if (imm > 32) return v_uint8x32(); + + __m256i swap = _mm256_permute2x128_si256(a.val, b.val, 0x21); + if (imm == 16) return v_uint8x32(swap); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(swap, a.val, imm)); + return v_uint8x32(_mm256_alignr_epi8(b.val, swap, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_left(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + enum {IMM_R = (16 - imm) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i swapz = _mm256_permute2x128_si256(a.val, a.val, _MM_SHUFFLE(0, 0, 2, 0)); + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(a.val, swapz, IMM_R)); + return v_uint8x32(_mm256_slli_si256(swapz, IMM_L)); +} + +template +inline v_uint8x32 v_rotate_right(const v_uint8x32& a) +{ + enum {IMM_L = (imm - 16) & 0xFF}; + + if (imm == 0) return a; + if (imm > 32) return v_uint8x32(); + + // ESAC control[3] ? [127:0] = 0 + __m256i swapz = _mm256_permute2x128_si256(a.val, a.val, _MM_SHUFFLE(2, 0, 0, 1)); + if (imm == 16) return v_uint8x32(swapz); + if (imm < 16) return v_uint8x32(_mm256_alignr_epi8(swapz, a.val, imm)); + return v_uint8x32(_mm256_srli_si256(swapz, IMM_L)); +} + +#define OPENCV_HAL_IMPL_AVX_ROTATE_CAST(intrin, _Tpvec, cast) \ + template \ + inline _Tpvec intrin(const _Tpvec& a, const _Tpvec& b) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a), \ + v_reinterpret_as_u8(b)); \ + return _Tpvec(cast(ret.val)); \ + } \ + template \ + inline _Tpvec intrin(const _Tpvec& a) \ + { \ + enum {IMMxW = imm * sizeof(typename _Tpvec::lane_type)}; \ + v_uint8x32 ret = intrin(v_reinterpret_as_u8(a)); \ + return _Tpvec(cast(ret.val)); \ + } + +#define OPENCV_HAL_IMPL_AVX_ROTATE(_Tpvec) \ + OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, _Tpvec, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, _Tpvec, OPENCV_HAL_NOP) + +OPENCV_HAL_IMPL_AVX_ROTATE(v_int8x32) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint16x16) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int16x16) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint32x8) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int32x8) +OPENCV_HAL_IMPL_AVX_ROTATE(v_uint64x4) +OPENCV_HAL_IMPL_AVX_ROTATE(v_int64x4) + +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, v_float32x8, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, v_float32x8, _mm256_castsi256_ps) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_left, v_float64x4, _mm256_castsi256_pd) +OPENCV_HAL_IMPL_AVX_ROTATE_CAST(v_rotate_right, v_float64x4, _mm256_castsi256_pd) + +/** Reverse **/ +inline v_uint8x32 v_reverse(const v_uint8x32 &a) +{ + static const __m256i perm = _mm256_setr_epi8( + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + __m256i vec = _mm256_shuffle_epi8(a.val, perm); + return v_uint8x32(_mm256_permute2x128_si256(vec, vec, 1)); +} + +inline v_int8x32 v_reverse(const v_int8x32 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x16 v_reverse(const v_uint16x16 &a) +{ + static const __m256i perm = _mm256_setr_epi8( + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + __m256i vec = _mm256_shuffle_epi8(a.val, perm); + return v_uint16x16(_mm256_permute2x128_si256(vec, vec, 1)); +} + +inline v_int16x16 v_reverse(const v_int16x16 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x8 v_reverse(const v_uint32x8 &a) +{ + static const __m256i perm = _mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0); + return v_uint32x8(_mm256_permutevar8x32_epi32(a.val, perm)); +} + +inline v_int32x8 v_reverse(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x8 v_reverse(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x4 v_reverse(const v_uint64x4 &a) +{ + return v_uint64x4(_mm256_permute4x64_epi64(a.val, _MM_SHUFFLE(0, 1, 2, 3))); +} + +inline v_int64x4 v_reverse(const v_int64x4 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x4 v_reverse(const v_float64x4 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +////////// Reduce and mask ///////// + +/** Reduce **/ +inline unsigned v_reduce_sum(const v_uint8x32& a) +{ + __m256i half = _mm256_sad_epu8(a.val, _mm256_setzero_si256()); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline int v_reduce_sum(const v_int8x32& a) +{ + __m256i half = _mm256_sad_epu8(_mm256_xor_si256(a.val, _mm256_set1_epi8((schar)-128)), _mm256_setzero_si256()); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))) - 4096; +} +#define OPENCV_HAL_IMPL_AVX_REDUCE_32(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i val = intrin(_v256_extract_low(a.val), _v256_extract_high(a.val)); \ + val = intrin(val, _mm_srli_si128(val,8)); \ + val = intrin(val, _mm_srli_si128(val,4)); \ + val = intrin(val, _mm_srli_si128(val,2)); \ + val = intrin(val, _mm_srli_si128(val,1)); \ + return (sctype)_mm_cvtsi128_si32(val); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_uint8x32, uchar, min, _mm_min_epu8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_int8x32, schar, min, _mm_min_epi8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_uint8x32, uchar, max, _mm_max_epu8) +OPENCV_HAL_IMPL_AVX_REDUCE_32(v_int8x32, schar, max, _mm_max_epi8) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_16(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_srli_si128(v0, 8)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 4)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 2)); \ + return (sctype) _mm_cvtsi128_si32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_uint16x16, ushort, min, _mm_min_epu16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_int16x16, short, min, _mm_min_epi16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_uint16x16, ushort, max, _mm_max_epu16) +OPENCV_HAL_IMPL_AVX_REDUCE_16(v_int16x16, short, max, _mm_max_epi16) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_8(_Tpvec, sctype, func, intrin) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { \ + __m128i v0 = _v256_extract_low(a.val); \ + __m128i v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_srli_si128(v0, 8)); \ + v0 = intrin(v0, _mm_srli_si128(v0, 4)); \ + return (sctype) _mm_cvtsi128_si32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_uint32x8, unsigned, min, _mm_min_epu32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_int32x8, int, min, _mm_min_epi32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_uint32x8, unsigned, max, _mm_max_epu32) +OPENCV_HAL_IMPL_AVX_REDUCE_8(v_int32x8, int, max, _mm_max_epi32) + +#define OPENCV_HAL_IMPL_AVX_REDUCE_FLT(func, intrin) \ + inline float v_reduce_##func(const v_float32x8& a) \ + { \ + __m128 v0 = _v256_extract_low(a.val); \ + __m128 v1 = _v256_extract_high(a.val); \ + v0 = intrin(v0, v1); \ + v0 = intrin(v0, _mm_permute_ps(v0, _MM_SHUFFLE(0, 0, 3, 2))); \ + v0 = intrin(v0, _mm_permute_ps(v0, _MM_SHUFFLE(0, 0, 0, 1))); \ + return _mm_cvtss_f32(v0); \ + } + +OPENCV_HAL_IMPL_AVX_REDUCE_FLT(min, _mm_min_ps) +OPENCV_HAL_IMPL_AVX_REDUCE_FLT(max, _mm_max_ps) + +inline int v_reduce_sum(const v_int32x8& a) +{ + __m256i s0 = _mm256_hadd_epi32(a.val, a.val); + s0 = _mm256_hadd_epi32(s0, s0); + + __m128i s1 = _v256_extract_high(s0); + s1 = _mm_add_epi32(_v256_extract_low(s0), s1); + + return _mm_cvtsi128_si32(s1); +} + +inline unsigned v_reduce_sum(const v_uint32x8& a) +{ return v_reduce_sum(v_reinterpret_as_s32(a)); } + +inline int v_reduce_sum(const v_int16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline unsigned v_reduce_sum(const v_uint16x16& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +inline float v_reduce_sum(const v_float32x8& a) +{ + __m256 s0 = _mm256_hadd_ps(a.val, a.val); + s0 = _mm256_hadd_ps(s0, s0); + + __m128 s1 = _v256_extract_high(s0); + s1 = _mm_add_ps(_v256_extract_low(s0), s1); + + return _mm_cvtss_f32(s1); +} + +inline uint64 v_reduce_sum(const v_uint64x4& a) +{ + uint64 CV_DECL_ALIGNED(32) idx[2]; + _mm_store_si128((__m128i*)idx, _mm_add_epi64(_v256_extract_low(a.val), _v256_extract_high(a.val))); + return idx[0] + idx[1]; +} +inline int64 v_reduce_sum(const v_int64x4& a) +{ + int64 CV_DECL_ALIGNED(32) idx[2]; + _mm_store_si128((__m128i*)idx, _mm_add_epi64(_v256_extract_low(a.val), _v256_extract_high(a.val))); + return idx[0] + idx[1]; +} +inline double v_reduce_sum(const v_float64x4& a) +{ + __m256d s0 = _mm256_hadd_pd(a.val, a.val); + return _mm_cvtsd_f64(_mm_add_pd(_v256_extract_low(s0), _v256_extract_high(s0))); +} + +inline v_float32x8 v_reduce_sum4(const v_float32x8& a, const v_float32x8& b, + const v_float32x8& c, const v_float32x8& d) +{ + __m256 ab = _mm256_hadd_ps(a.val, b.val); + __m256 cd = _mm256_hadd_ps(c.val, d.val); + return v_float32x8(_mm256_hadd_ps(ab, cd)); +} + +inline unsigned v_reduce_sad(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i half = _mm256_sad_epu8(a.val, b.val); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_int8x32& a, const v_int8x32& b) +{ + __m256i half = _mm256_set1_epi8(0x7f); + half = _mm256_sad_epu8(_mm256_add_epi8(a.val, half), _mm256_add_epi8(b.val, half)); + __m128i quarter = _mm_add_epi32(_v256_extract_low(half), _v256_extract_high(half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_uint16x16& a, const v_uint16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_add_wrap(a - b, b - a), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x16& a, const v_int16x16& b) +{ + v_uint32x8 l, h; + v_expand(v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x8& a, const v_uint32x8& b) +{ + return v_reduce_sum(v_max(a, b) - v_min(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 m = a < b; + return v_reduce_sum(v_reinterpret_as_u32(((a - b) ^ m) - m)); +} +inline float v_reduce_sad(const v_float32x8& a, const v_float32x8& b) +{ + return v_reduce_sum((a - b) & v_float32x8(_mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff)))); +} + +/** Popcount **/ +inline v_uint8x32 v_popcount(const v_uint8x32& a) +{ + __m256i _popcnt_table = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); + __m256i _popcnt_mask = _mm256_set1_epi8(0x0F); + return v_uint8x32(_mm256_add_epi8(_mm256_shuffle_epi8(_popcnt_table, _mm256_and_si256( a.val , _popcnt_mask)), + _mm256_shuffle_epi8(_popcnt_table, _mm256_and_si256(_mm256_srli_epi16(a.val, 4), _popcnt_mask)))); +} +inline v_uint16x16 v_popcount(const v_uint16x16& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v256_setall_u16(0x00ff); +} +inline v_uint32x8 v_popcount(const v_uint32x8& a) +{ + v_uint8x32 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v256_setall_u32(0x000000ff); +} +inline v_uint64x4 v_popcount(const v_uint64x4& a) +{ + return v_uint64x4(_mm256_sad_epu8(v_popcount(v_reinterpret_as_u8(a)).val, _mm256_setzero_si256())); +} +inline v_uint8x32 v_popcount(const v_int8x32& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x16 v_popcount(const v_int16x16& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x8 v_popcount(const v_int32x8& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x4 v_popcount(const v_int64x4& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +/** Mask **/ +inline int v_signmask(const v_int8x32& a) +{ return _mm256_movemask_epi8(a.val); } +inline int v_signmask(const v_uint8x32& a) +{ return v_signmask(v_reinterpret_as_s8(a)); } + +inline int v_signmask(const v_int16x16& a) +{ return v_signmask(v_pack(a, a)) & 0xFFFF; } +inline int v_signmask(const v_uint16x16& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } + +inline int v_signmask(const v_float32x8& a) +{ return _mm256_movemask_ps(a.val); } +inline int v_signmask(const v_float64x4& a) +{ return _mm256_movemask_pd(a.val); } + +inline int v_signmask(const v_int32x8& a) +{ return v_signmask(v_reinterpret_as_f32(a)); } +inline int v_signmask(const v_uint32x8& a) +{ return v_signmask(v_reinterpret_as_f32(a)); } + +inline int v_signmask(const v_int64x4& a) +{ return v_signmask(v_reinterpret_as_f64(a)); } +inline int v_signmask(const v_uint64x4& a) +{ return v_signmask(v_reinterpret_as_f64(a)); } + +inline int v_scan_forward(const v_int8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +/** Checks **/ +#define OPENCV_HAL_IMPL_AVX_CHECK(_Tpvec, allmask) \ + inline bool v_check_all(const _Tpvec& a) { return v_signmask(a) == allmask; } \ + inline bool v_check_any(const _Tpvec& a) { return v_signmask(a) != 0; } +OPENCV_HAL_IMPL_AVX_CHECK(v_uint8x32, -1) +OPENCV_HAL_IMPL_AVX_CHECK(v_int8x32, -1) +OPENCV_HAL_IMPL_AVX_CHECK(v_uint32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_int32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_uint64x4, 15) +OPENCV_HAL_IMPL_AVX_CHECK(v_int64x4, 15) +OPENCV_HAL_IMPL_AVX_CHECK(v_float32x8, 255) +OPENCV_HAL_IMPL_AVX_CHECK(v_float64x4, 15) + +#define OPENCV_HAL_IMPL_AVX_CHECK_SHORT(_Tpvec) \ + inline bool v_check_all(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) == 0xaaaaaaaa; } \ + inline bool v_check_any(const _Tpvec& a) { return (v_signmask(v_reinterpret_as_s8(a)) & 0xaaaaaaaa) != 0; } +OPENCV_HAL_IMPL_AVX_CHECK_SHORT(v_uint16x16) +OPENCV_HAL_IMPL_AVX_CHECK_SHORT(v_int16x16) + +////////// Other math ///////// + +/** Some frequent operations **/ +#define OPENCV_HAL_IMPL_AVX_MULADD(_Tpvec, suffix) \ + inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm256_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm256_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_sqrt(const _Tpvec& x) \ + { return _Tpvec(_mm256_sqrt_##suffix(x.val)); } \ + inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_fma(a, a, b * b); } \ + inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_sqrt(v_fma(a, a, b*b)); } + +OPENCV_HAL_IMPL_AVX_MULADD(v_float32x8, ps) +OPENCV_HAL_IMPL_AVX_MULADD(v_float64x4, pd) + +inline v_int32x8 v_fma(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return a * b + c; +} + +inline v_int32x8 v_muladd(const v_int32x8& a, const v_int32x8& b, const v_int32x8& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x8 v_invsqrt(const v_float32x8& x) +{ + v_float32x8 half = x * v256_setall_f32(0.5); + v_float32x8 t = v_float32x8(_mm256_rsqrt_ps(x.val)); + // todo: _mm256_fnmsub_ps + t *= v256_setall_f32(1.5) - ((t * t) * half); + return t; +} + +inline v_float64x4 v_invsqrt(const v_float64x4& x) +{ + return v256_setall_f64(1.) / v_sqrt(x); +} + +/** Absolute values **/ +#define OPENCV_HAL_IMPL_AVX_ABS(_Tpvec, suffix) \ + inline v_u##_Tpvec v_abs(const v_##_Tpvec& x) \ + { return v_u##_Tpvec(_mm256_abs_##suffix(x.val)); } + +OPENCV_HAL_IMPL_AVX_ABS(int8x32, epi8) +OPENCV_HAL_IMPL_AVX_ABS(int16x16, epi16) +OPENCV_HAL_IMPL_AVX_ABS(int32x8, epi32) + +inline v_float32x8 v_abs(const v_float32x8& x) +{ return x & v_float32x8(_mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff))); } +inline v_float64x4 v_abs(const v_float64x4& x) +{ return x & v_float64x4(_mm256_castsi256_pd(_mm256_srli_epi64(_mm256_set1_epi64x(-1), 1))); } + +/** Absolute difference **/ +inline v_uint8x32 v_absdiff(const v_uint8x32& a, const v_uint8x32& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x16 v_absdiff(const v_uint16x16& a, const v_uint16x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x8 v_absdiff(const v_uint32x8& a, const v_uint32x8& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x32 v_absdiff(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = v_sub_wrap(a, b); + v_int8x32 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} + +inline v_uint16x16 v_absdiff(const v_int16x16& a, const v_int16x16& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } + +inline v_uint32x8 v_absdiff(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 d = a - b; + v_int32x8 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +inline v_float32x8 v_absdiff(const v_float32x8& a, const v_float32x8& b) +{ return v_abs(a - b); } + +inline v_float64x4 v_absdiff(const v_float64x4& a, const v_float64x4& b) +{ return v_abs(a - b); } + +/** Saturating absolute difference **/ +inline v_int8x32 v_absdiffs(const v_int8x32& a, const v_int8x32& b) +{ + v_int8x32 d = a - b; + v_int8x32 m = a < b; + return (d ^ m) - m; +} +inline v_int16x16 v_absdiffs(const v_int16x16& a, const v_int16x16& b) +{ return v_max(a, b) - v_min(a, b); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x8 v_round(const v_float32x8& a) +{ return v_int32x8(_mm256_cvtps_epi32(a.val)); } + +inline v_int32x8 v_round(const v_float64x4& a) +{ return v_int32x8(_mm256_castsi128_si256(_mm256_cvtpd_epi32(a.val))); } + +inline v_int32x8 v_round(const v_float64x4& a, const v_float64x4& b) +{ + __m128i ai = _mm256_cvtpd_epi32(a.val), bi = _mm256_cvtpd_epi32(b.val); + return v_int32x8(_v256_combine(ai, bi)); +} + +inline v_int32x8 v_trunc(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(a.val)); } + +inline v_int32x8 v_trunc(const v_float64x4& a) +{ return v_int32x8(_mm256_castsi128_si256(_mm256_cvttpd_epi32(a.val))); } + +inline v_int32x8 v_floor(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(_mm256_floor_ps(a.val))); } + +inline v_int32x8 v_floor(const v_float64x4& a) +{ return v_trunc(v_float64x4(_mm256_floor_pd(a.val))); } + +inline v_int32x8 v_ceil(const v_float32x8& a) +{ return v_int32x8(_mm256_cvttps_epi32(_mm256_ceil_ps(a.val))); } + +inline v_int32x8 v_ceil(const v_float64x4& a) +{ return v_trunc(v_float64x4(_mm256_ceil_pd(a.val))); } + +/** To float **/ +inline v_float32x8 v_cvt_f32(const v_int32x8& a) +{ return v_float32x8(_mm256_cvtepi32_ps(a.val)); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a) +{ return v_float32x8(_mm256_castps128_ps256(_mm256_cvtpd_ps(a.val))); } + +inline v_float32x8 v_cvt_f32(const v_float64x4& a, const v_float64x4& b) +{ + __m128 af = _mm256_cvtpd_ps(a.val), bf = _mm256_cvtpd_ps(b.val); + return v_float32x8(_v256_combine(af, bf)); +} + +inline v_float64x4 v_cvt_f64(const v_int32x8& a) +{ return v_float64x4(_mm256_cvtepi32_pd(_v256_extract_low(a.val))); } + +inline v_float64x4 v_cvt_f64_high(const v_int32x8& a) +{ return v_float64x4(_mm256_cvtepi32_pd(_v256_extract_high(a.val))); } + +inline v_float64x4 v_cvt_f64(const v_float32x8& a) +{ return v_float64x4(_mm256_cvtps_pd(_v256_extract_low(a.val))); } + +inline v_float64x4 v_cvt_f64_high(const v_float32x8& a) +{ return v_float64x4(_mm256_cvtps_pd(_v256_extract_high(a.val))); } + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x4 v_cvt_f64(const v_int64x4& v) +{ + // constants encoded as floating-point + __m256i magic_i_lo = _mm256_set1_epi64x(0x4330000000000000); // 2^52 + __m256i magic_i_hi32 = _mm256_set1_epi64x(0x4530000080000000); // 2^84 + 2^63 + __m256i magic_i_all = _mm256_set1_epi64x(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m256d magic_d_all = _mm256_castsi256_pd(magic_i_all); + + // Blend the 32 lowest significant bits of v with magic_int_lo + __m256i v_lo = _mm256_blend_epi32(magic_i_lo, v.val, 0x55); + // Extract the 32 most significant bits of v + __m256i v_hi = _mm256_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm256_xor_si256(v_hi, magic_i_hi32); + // Compute in double precision + __m256d v_hi_dbl = _mm256_sub_pd(_mm256_castsi256_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m256d result = _mm256_add_pd(v_hi_dbl, _mm256_castsi256_pd(v_lo)); + return v_float64x4(result); +} + +////////////// Lookup table access //////////////////// + +inline v_int8x32 v256_lut(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_setr_epi8(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[ 8]], tab[idx[ 9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]], + tab[idx[16]], tab[idx[17]], tab[idx[18]], tab[idx[19]], tab[idx[20]], tab[idx[21]], tab[idx[22]], tab[idx[23]], + tab[idx[24]], tab[idx[25]], tab[idx[26]], tab[idx[27]], tab[idx[28]], tab[idx[29]], tab[idx[30]], tab[idx[31]])); +} +inline v_int8x32 v256_lut_pairs(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_setr_epi16(*(const short*)(tab + idx[ 0]), *(const short*)(tab + idx[ 1]), *(const short*)(tab + idx[ 2]), *(const short*)(tab + idx[ 3]), + *(const short*)(tab + idx[ 4]), *(const short*)(tab + idx[ 5]), *(const short*)(tab + idx[ 6]), *(const short*)(tab + idx[ 7]), + *(const short*)(tab + idx[ 8]), *(const short*)(tab + idx[ 9]), *(const short*)(tab + idx[10]), *(const short*)(tab + idx[11]), + *(const short*)(tab + idx[12]), *(const short*)(tab + idx[13]), *(const short*)(tab + idx[14]), *(const short*)(tab + idx[15]))); +} +inline v_int8x32 v256_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x32(_mm256_i32gather_epi32((const int*)tab, _mm256_loadu_si256((const __m256i*)idx), 1)); +} +inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); } + +inline v_int16x16 v256_lut(const short* tab, const int* idx) +{ + return v_int16x16(_mm256_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])); +} +inline v_int16x16 v256_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x16(_mm256_i32gather_epi32((const int*)tab, _mm256_loadu_si256((const __m256i*)idx), 2)); +} +inline v_int16x16 v256_lut_quads(const short* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int16x16(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 2));//Looks like intrinsic has wrong definition +#else + return v_int16x16(_mm256_i32gather_epi64((const int64*)tab, _mm_loadu_si128((const __m128i*)idx), 2)); +#endif +} +inline v_uint16x16 v256_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_pairs((const short *)tab, idx)); } +inline v_uint16x16 v256_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v256_lut_quads((const short *)tab, idx)); } + +inline v_int32x8 v256_lut(const int* tab, const int* idx) +{ + return v_int32x8(_mm256_i32gather_epi32(tab, _mm256_loadu_si256((const __m256i*)idx), 4)); +} +inline v_int32x8 v256_lut_pairs(const int* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int32x8(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 4)); +#else + return v_int32x8(_mm256_i32gather_epi64((const int64*)tab, _mm_loadu_si128((const __m128i*)idx), 4)); +#endif +} +inline v_int32x8 v256_lut_quads(const int* tab, const int* idx) +{ + return v_int32x8(_v256_combine(_mm_loadu_si128((const __m128i*)(tab + idx[0])), _mm_loadu_si128((const __m128i*)(tab + idx[1])))); +} +inline v_uint32x8 v256_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_pairs((const int *)tab, idx)); } +inline v_uint32x8 v256_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v256_lut_quads((const int *)tab, idx)); } + +inline v_int64x4 v256_lut(const int64* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int64x4(_mm256_i32gather_epi64((const long long int*)tab, _mm_loadu_si128((const __m128i*)idx), 8)); +#else + return v_int64x4(_mm256_i32gather_epi64(tab, _mm_loadu_si128((const __m128i*)idx), 8)); +#endif +} +inline v_int64x4 v256_lut_pairs(const int64* tab, const int* idx) +{ + return v_int64x4(_v256_combine(_mm_loadu_si128((const __m128i*)(tab + idx[0])), _mm_loadu_si128((const __m128i*)(tab + idx[1])))); +} +inline v_uint64x4 v256_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut((const int64 *)tab, idx)); } +inline v_uint64x4 v256_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v256_lut_pairs((const int64 *)tab, idx)); } + +inline v_float32x8 v256_lut(const float* tab, const int* idx) +{ + return v_float32x8(_mm256_i32gather_ps(tab, _mm256_loadu_si256((const __m256i*)idx), 4)); +} +inline v_float32x8 v256_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_pairs((const int *)tab, idx)); } +inline v_float32x8 v256_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v256_lut_quads((const int *)tab, idx)); } + +inline v_float64x4 v256_lut(const double* tab, const int* idx) +{ + return v_float64x4(_mm256_i32gather_pd(tab, _mm_loadu_si128((const __m128i*)idx), 8)); +} +inline v_float64x4 v256_lut_pairs(const double* tab, const int* idx) { return v_float64x4(_v256_combine(_mm_loadu_pd(tab + idx[0]), _mm_loadu_pd(tab + idx[1]))); } + +inline v_int32x8 v_lut(const int* tab, const v_int32x8& idxvec) +{ + return v_int32x8(_mm256_i32gather_epi32(tab, idxvec.val, 4)); +} + +inline v_uint32x8 v_lut(const unsigned* tab, const v_int32x8& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x8 v_lut(const float* tab, const v_int32x8& idxvec) +{ + return v_float32x8(_mm256_i32gather_ps(tab, idxvec.val, 4)); +} + +inline v_float64x4 v_lut(const double* tab, const v_int32x8& idxvec) +{ + return v_float64x4(_mm256_i32gather_pd(tab, _mm256_castsi256_si128(idxvec.val), 8)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x8& idxvec, v_float32x8& x, v_float32x8& y) +{ + int CV_DECL_ALIGNED(32) idx[8]; + v_store_aligned(idx, idxvec); + __m128 z = _mm_setzero_ps(); + __m128 xy01, xy45, xy23, xy67; + xy01 = _mm_loadl_pi(z, (const __m64*)(tab + idx[0])); + xy01 = _mm_loadh_pi(xy01, (const __m64*)(tab + idx[1])); + xy45 = _mm_loadl_pi(z, (const __m64*)(tab + idx[4])); + xy45 = _mm_loadh_pi(xy45, (const __m64*)(tab + idx[5])); + __m256 xy0145 = _v256_combine(xy01, xy45); + xy23 = _mm_loadl_pi(z, (const __m64*)(tab + idx[2])); + xy23 = _mm_loadh_pi(xy23, (const __m64*)(tab + idx[3])); + xy67 = _mm_loadl_pi(z, (const __m64*)(tab + idx[6])); + xy67 = _mm_loadh_pi(xy67, (const __m64*)(tab + idx[7])); + __m256 xy2367 = _v256_combine(xy23, xy67); + + __m256 xxyy0145 = _mm256_unpacklo_ps(xy0145, xy2367); + __m256 xxyy2367 = _mm256_unpackhi_ps(xy0145, xy2367); + + x = v_float32x8(_mm256_unpacklo_ps(xxyy0145, xxyy2367)); + y = v_float32x8(_mm256_unpackhi_ps(xxyy0145, xxyy2367)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x8& idxvec, v_float64x4& x, v_float64x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_low(idx, idxvec); + __m128d xy0 = _mm_loadu_pd(tab + idx[0]); + __m128d xy2 = _mm_loadu_pd(tab + idx[2]); + __m128d xy1 = _mm_loadu_pd(tab + idx[1]); + __m128d xy3 = _mm_loadu_pd(tab + idx[3]); + __m256d xy02 = _v256_combine(xy0, xy2); + __m256d xy13 = _v256_combine(xy1, xy3); + + x = v_float64x4(_mm256_unpacklo_pd(xy02, xy13)); + y = v_float64x4(_mm256_unpackhi_pd(xy02, xy13)); +} + +inline v_int8x32 v_interleave_pairs(const v_int8x32& vec) +{ + return v_int8x32(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0d0e0c0b090a08, 0x0705060403010200, 0x0f0d0e0c0b090a08, 0x0705060403010200))); +} +inline v_uint8x32 v_interleave_pairs(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x32 v_interleave_quads(const v_int8x32& vec) +{ + return v_int8x32(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0b0e0a0d090c08, 0x0703060205010400, 0x0f0b0e0a0d090c08, 0x0703060205010400))); +} +inline v_uint8x32 v_interleave_quads(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_interleave_pairs(const v_int16x16& vec) +{ + return v_int16x16(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0e0b0a0d0c0908, 0x0706030205040100, 0x0f0e0b0a0d0c0908, 0x0706030205040100))); +} +inline v_uint16x16 v_interleave_pairs(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x16 v_interleave_quads(const v_int16x16& vec) +{ + return v_int16x16(_mm256_shuffle_epi8(vec.val, _mm256_set_epi64x(0x0f0e07060d0c0504, 0x0b0a030209080100, 0x0f0e07060d0c0504, 0x0b0a030209080100))); +} +inline v_uint16x16 v_interleave_quads(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_interleave_pairs(const v_int32x8& vec) +{ + return v_int32x8(_mm256_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0))); +} +inline v_uint32x8 v_interleave_pairs(const v_uint32x8& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_interleave_pairs(const v_float32x8& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x32 v_pack_triplets(const v_int8x32& vec) +{ + return v_int8x32(_mm256_permutevar8x32_epi32(_mm256_shuffle_epi8(vec.val, _mm256_broadcastsi128_si256(_mm_set_epi64x(0xffffff0f0e0d0c0a, 0x0908060504020100))), + _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint8x32 v_pack_triplets(const v_uint8x32& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x16 v_pack_triplets(const v_int16x16& vec) +{ + return v_int16x16(_mm256_permutevar8x32_epi32(_mm256_shuffle_epi8(vec.val, _mm256_broadcastsi128_si256(_mm_set_epi64x(0xffff0f0e0d0c0b0a, 0x0908050403020100))), + _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint16x16 v_pack_triplets(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x8 v_pack_triplets(const v_int32x8& vec) +{ + return v_int32x8(_mm256_permutevar8x32_epi32(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} +inline v_uint32x8 v_pack_triplets(const v_uint32x8& vec) { return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); } +inline v_float32x8 v_pack_triplets(const v_float32x8& vec) +{ + return v_float32x8(_mm256_permutevar8x32_ps(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000))); +} + +////////// Matrix operations ///////// + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b) +{ return v_int32x8(_mm256_madd_epi16(a.val, b.val)); } +inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b) +{ + __m256i even = _mm256_mul_epi32(a.val, b.val); + __m256i odd = _mm256_mul_epi32(_mm256_srli_epi64(a.val, 32), _mm256_srli_epi64(b.val, 32)); + return v_int64x4(_mm256_add_epi64(even, odd)); +} +inline v_int64x4 v_dotprod(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b) +{ + __m256i even_m = _mm256_set1_epi32(0xFF00FF00); + __m256i even_a = _mm256_blendv_epi8(a.val, _mm256_setzero_si256(), even_m); + __m256i odd_a = _mm256_srli_epi16(a.val, 8); + + __m256i even_b = _mm256_blendv_epi8(b.val, _mm256_setzero_si256(), even_m); + __m256i odd_b = _mm256_srli_epi16(b.val, 8); + + __m256i prod0 = _mm256_madd_epi16(even_a, even_b); + __m256i prod1 = _mm256_madd_epi16(odd_a, odd_b); + return v_uint32x8(_mm256_add_epi32(prod0, prod1)); +} +inline v_uint32x8 v_dotprod_expand(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b) +{ + __m256i even_a = _mm256_srai_epi16(_mm256_bslli_epi128(a.val, 1), 8); + __m256i odd_a = _mm256_srai_epi16(a.val, 8); + + __m256i even_b = _mm256_srai_epi16(_mm256_bslli_epi128(b.val, 1), 8); + __m256i odd_b = _mm256_srai_epi16(b.val, 8); + + __m256i prod0 = _mm256_madd_epi16(even_a, even_b); + __m256i prod1 = _mm256_madd_epi16(odd_a, odd_b); + return v_int32x8(_mm256_add_epi32(prod0, prod1)); +} +inline v_int32x8 v_dotprod_expand(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = _mm256_mullo_epi16(a.val, b.val); + __m256i mulhi = _mm256_mulhi_epu16(a.val, b.val); + __m256i mul0 = _mm256_unpacklo_epi16(mullo, mulhi); + __m256i mul1 = _mm256_unpackhi_epi16(mullo, mulhi); + + __m256i p02 = _mm256_blend_epi32(mul0, _mm256_setzero_si256(), 0xAA); + __m256i p13 = _mm256_srli_epi64(mul0, 32); + __m256i p46 = _mm256_blend_epi32(mul1, _mm256_setzero_si256(), 0xAA); + __m256i p57 = _mm256_srli_epi64(mul1, 32); + + __m256i p15_ = _mm256_add_epi64(p02, p13); + __m256i p9d_ = _mm256_add_epi64(p46, p57); + + return v_uint64x4(_mm256_add_epi64( + _mm256_unpacklo_epi64(p15_, p9d_), + _mm256_unpackhi_epi64(p15_, p9d_) + )); +} +inline v_uint64x4 v_dotprod_expand(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = _mm256_madd_epi16(a.val, b.val); + __m256i sign = _mm256_srai_epi32(prod, 31); + + __m256i lo = _mm256_unpacklo_epi32(prod, sign); + __m256i hi = _mm256_unpackhi_epi32(prod, sign); + + return v_int64x4(_mm256_add_epi64( + _mm256_unpacklo_epi64(lo, hi), + _mm256_unpackhi_epi64(lo, hi) + )); +} +inline v_int64x4 v_dotprod_expand(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x4 v_dotprod_expand(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b) +{ return v_dotprod(a, b); } +inline v_int32x8 v_dotprod_fast(const v_int16x16& a, const v_int16x16& b, const v_int32x8& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod(a, b); } +inline v_int64x4 v_dotprod_fast(const v_int32x8& a, const v_int32x8& b, const v_int64x4& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x8 v_dotprod_expand_fast(const v_uint8x32& a, const v_uint8x32& b, const v_uint32x8& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x8 v_dotprod_expand_fast(const v_int8x32& a, const v_int8x32& b, const v_int32x8& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i mullo = _mm256_mullo_epi16(a.val, b.val); + __m256i mulhi = _mm256_mulhi_epu16(a.val, b.val); + __m256i mul0 = _mm256_unpacklo_epi16(mullo, mulhi); + __m256i mul1 = _mm256_unpackhi_epi16(mullo, mulhi); + + __m256i p02 = _mm256_blend_epi32(mul0, _mm256_setzero_si256(), 0xAA); + __m256i p13 = _mm256_srli_epi64(mul0, 32); + __m256i p46 = _mm256_blend_epi32(mul1, _mm256_setzero_si256(), 0xAA); + __m256i p57 = _mm256_srli_epi64(mul1, 32); + + __m256i p15_ = _mm256_add_epi64(p02, p13); + __m256i p9d_ = _mm256_add_epi64(p46, p57); + + return v_uint64x4(_mm256_add_epi64(p15_, p9d_)); +} +inline v_uint64x4 v_dotprod_expand_fast(const v_uint16x16& a, const v_uint16x16& b, const v_uint64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b) +{ + __m256i prod = _mm256_madd_epi16(a.val, b.val); + __m256i sign = _mm256_srai_epi32(prod, 31); + __m256i lo = _mm256_unpacklo_epi32(prod, sign); + __m256i hi = _mm256_unpackhi_epi32(prod, sign); + return v_int64x4(_mm256_add_epi64(lo, hi)); +} +inline v_int64x4 v_dotprod_expand_fast(const v_int16x16& a, const v_int16x16& b, const v_int64x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x4 v_dotprod_expand_fast(const v_int32x8& a, const v_int32x8& b, const v_float64x4& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_AVX_SPLAT2_PS(a, im) \ + v_float32x8(_mm256_permute_ps(a.val, _MM_SHUFFLE(im, im, im, im))) + +inline v_float32x8 v_matmul(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& m3) +{ + v_float32x8 v04 = OPENCV_HAL_AVX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_AVX_SPLAT2_PS(v, 1); + v_float32x8 v26 = OPENCV_HAL_AVX_SPLAT2_PS(v, 2); + v_float32x8 v37 = OPENCV_HAL_AVX_SPLAT2_PS(v, 3); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, v37 * m3))); +} + +inline v_float32x8 v_matmuladd(const v_float32x8& v, const v_float32x8& m0, + const v_float32x8& m1, const v_float32x8& m2, + const v_float32x8& a) +{ + v_float32x8 v04 = OPENCV_HAL_AVX_SPLAT2_PS(v, 0); + v_float32x8 v15 = OPENCV_HAL_AVX_SPLAT2_PS(v, 1); + v_float32x8 v26 = OPENCV_HAL_AVX_SPLAT2_PS(v, 2); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, a))); +} + +#define OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ + inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ + { \ + __m256i t0 = cast_from(_mm256_unpacklo_##suffix(a0.val, a1.val)); \ + __m256i t1 = cast_from(_mm256_unpacklo_##suffix(a2.val, a3.val)); \ + __m256i t2 = cast_from(_mm256_unpackhi_##suffix(a0.val, a1.val)); \ + __m256i t3 = cast_from(_mm256_unpackhi_##suffix(a2.val, a3.val)); \ + b0.val = cast_to(_mm256_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm256_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm256_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm256_unpackhi_epi64(t2, t3)); \ + } + +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_uint32x8, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_int32x8, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX_TRANSPOSE4x4(v_float32x8, ps, _mm256_castps_si256, _mm256_castsi256_ps) + +//////////////// Value reordering /////////////// + +/* Expand */ +#define OPENCV_HAL_IMPL_AVX_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(_v256_extract_low(a.val)); \ + b1.val = intrin(_v256_extract_high(a.val)); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v256_extract_low(a.val))); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v256_extract_high(a.val))); } \ + inline _Tpwvec v256_load_expand(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint8x32, v_uint16x16, uchar, _mm256_cvtepu8_epi16) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int8x32, v_int16x16, schar, _mm256_cvtepi8_epi16) +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint16x16, v_uint32x8, ushort, _mm256_cvtepu16_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int16x16, v_int32x8, short, _mm256_cvtepi16_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND(v_uint32x8, v_uint64x4, unsigned, _mm256_cvtepu32_epi64) +OPENCV_HAL_IMPL_AVX_EXPAND(v_int32x8, v_int64x4, int, _mm256_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_AVX_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v256_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadl_epi64((const __m128i*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX_EXPAND_Q(v_uint32x8, uchar, _mm256_cvtepu8_epi32) +OPENCV_HAL_IMPL_AVX_EXPAND_Q(v_int32x8, schar, _mm256_cvtepi8_epi32) + +/* pack */ +// 16 +inline v_int8x32 v_pack(const v_int16x16& a, const v_int16x16& b) +{ return v_int8x32(_v256_shuffle_odd_64(_mm256_packs_epi16(a.val, b.val))); } + +inline v_uint8x32 v_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i t = _mm256_set1_epi16(255); + __m256i a1 = _mm256_min_epu16(a.val, t); + __m256i b1 = _mm256_min_epu16(b.val, t); + return v_uint8x32(_v256_shuffle_odd_64(_mm256_packus_epi16(a1, b1))); +} + +inline v_uint8x32 v_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + return v_uint8x32(_v256_shuffle_odd_64(_mm256_packus_epi16(a.val, b.val))); +} + +inline void v_pack_store(schar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(uchar* ptr, const v_uint16x16& a) +{ + const __m256i m = _mm256_set1_epi16(255); + __m256i am = _mm256_min_epu16(a.val, m); + am = _v256_shuffle_odd_64(_mm256_packus_epi16(am, am)); + v_store_low(ptr, v_uint8x32(am)); +} + +inline void v_pack_u_store(uchar* ptr, const v_int16x16& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + +template inline +v_uint8x32 v_rshr_pack(const v_uint16x16& a, const v_uint16x16& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + return v_pack_u(v_reinterpret_as_s16((a + delta) >> n), + v_reinterpret_as_s16((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x16& a) +{ + v_uint16x16 delta = v256_setall_u16((short)(1 << (n-1))); + v_pack_u_store(ptr, v_reinterpret_as_s16((a + delta) >> n)); +} + +template inline +v_uint8x32 v_rshr_pack_u(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int8x32 v_rshr_pack(const v_int16x16& a, const v_int16x16& b) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x16& a) +{ + v_int16x16 delta = v256_setall_s16((short)(1 << (n-1))); + v_pack_store(ptr, (a + delta) >> n); +} + +// 32 +inline v_int16x16 v_pack(const v_int32x8& a, const v_int32x8& b) +{ return v_int16x16(_v256_shuffle_odd_64(_mm256_packs_epi32(a.val, b.val))); } + +inline v_uint16x16 v_pack(const v_uint32x8& a, const v_uint32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_v256_packs_epu32(a.val, b.val))); } + +inline v_uint16x16 v_pack_u(const v_int32x8& a, const v_int32x8& b) +{ return v_uint16x16(_v256_shuffle_odd_64(_mm256_packus_epi32(a.val, b.val))); } + +inline void v_pack_store(short* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x8& a) +{ + const __m256i m = _mm256_set1_epi32(65535); + __m256i am = _mm256_min_epu32(a.val, m); + am = _v256_shuffle_odd_64(_mm256_packus_epi32(am, am)); + v_store_low(ptr, v_uint16x16(am)); +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x8& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + + +template inline +v_uint16x16 v_rshr_pack(const v_uint32x8& a, const v_uint32x8& b) +{ + // we assume that n > 0, and so the shifted 32-bit values can be treated as signed numbers. + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + return v_pack_u(v_reinterpret_as_s32((a + delta) >> n), + v_reinterpret_as_s32((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x8& a) +{ + v_uint32x8 delta = v256_setall_u32(1 << (n-1)); + v_pack_u_store(ptr, v_reinterpret_as_s32((a + delta) >> n)); +} + +template inline +v_uint16x16 v_rshr_pack_u(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int16x16 v_rshr_pack(const v_int32x8& a, const v_int32x8& b) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x8& a) +{ + v_int32x8 delta = v256_setall_s32(1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// 64 +// Non-saturating pack +inline v_uint32x8 v_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + __m256i a0 = _mm256_shuffle_epi32(a.val, _MM_SHUFFLE(0, 0, 2, 0)); + __m256i b0 = _mm256_shuffle_epi32(b.val, _MM_SHUFFLE(0, 0, 2, 0)); + __m256i ab = _mm256_unpacklo_epi64(a0, b0); // a0, a1, b0, b1, a2, a3, b2, b3 + return v_uint32x8(_v256_shuffle_odd_64(ab)); +} + +inline v_int32x8 v_pack(const v_int64x4& a, const v_int64x4& b) +{ return v_reinterpret_as_s32(v_pack(v_reinterpret_as_u64(a), v_reinterpret_as_u64(b))); } + +inline void v_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + __m256i a0 = _mm256_shuffle_epi32(a.val, _MM_SHUFFLE(0, 0, 2, 0)); + v_store_low(ptr, v_uint32x8(_v256_shuffle_odd_64(a0))); +} + +inline void v_pack_store(int* ptr, const v_int64x4& b) +{ v_pack_store((unsigned*)ptr, v_reinterpret_as_u64(b)); } + +template inline +v_uint32x8 v_rshr_pack(const v_uint64x4& a, const v_uint64x4& b) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x4& a) +{ + v_uint64x4 delta = v256_setall_u64((uint64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +template inline +v_int32x8 v_rshr_pack(const v_int64x4& a, const v_int64x4& b) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x4& a) +{ + v_int64x4 delta = v256_setall_s64((int64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// pack boolean +inline v_uint8x32 v_pack_b(const v_uint16x16& a, const v_uint16x16& b) +{ + __m256i ab = _mm256_packs_epi16(a.val, b.val); + return v_uint8x32(_v256_shuffle_odd_64(ab)); +} + +inline v_uint8x32 v_pack_b(const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d) +{ + __m256i ab = _mm256_packs_epi32(a.val, b.val); + __m256i cd = _mm256_packs_epi32(c.val, d.val); + + __m256i abcd = _v256_shuffle_odd_64(_mm256_packs_epi16(ab, cd)); + return v_uint8x32(_mm256_shuffle_epi32(abcd, _MM_SHUFFLE(3, 1, 2, 0))); +} + +inline v_uint8x32 v_pack_b(const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + const v_uint64x4& d, const v_uint64x4& e, const v_uint64x4& f, + const v_uint64x4& g, const v_uint64x4& h) +{ + __m256i ab = _mm256_packs_epi32(a.val, b.val); + __m256i cd = _mm256_packs_epi32(c.val, d.val); + __m256i ef = _mm256_packs_epi32(e.val, f.val); + __m256i gh = _mm256_packs_epi32(g.val, h.val); + + __m256i abcd = _mm256_packs_epi32(ab, cd); + __m256i efgh = _mm256_packs_epi32(ef, gh); + __m256i pkall = _v256_shuffle_odd_64(_mm256_packs_epi16(abcd, efgh)); + + __m256i rev = _mm256_alignr_epi8(pkall, pkall, 8); + return v_uint8x32(_mm256_unpacklo_epi16(pkall, rev)); +} + +/* Recombine */ +// its up there with load and store operations + +/* Extract */ +#define OPENCV_HAL_IMPL_AVX_EXTRACT(_Tpvec) \ + template \ + inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ + { return v_rotate_right(a, b); } + +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint8x32) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int8x32) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint16x16) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int16x16) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_uint64x4) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_int64x4) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_float32x8) +OPENCV_HAL_IMPL_AVX_EXTRACT(v_float64x4) + +template +inline uchar v_extract_n(v_uint8x32 a) +{ + return (uchar)_v256_extract_epi8(a.val); +} + +template +inline schar v_extract_n(v_int8x32 a) +{ + return (schar)v_extract_n(v_reinterpret_as_u8(a)); +} + +template +inline ushort v_extract_n(v_uint16x16 a) +{ + return (ushort)_v256_extract_epi16(a.val); +} + +template +inline short v_extract_n(v_int16x16 a) +{ + return (short)v_extract_n(v_reinterpret_as_u16(a)); +} + +template +inline uint v_extract_n(v_uint32x8 a) +{ + return (uint)_v256_extract_epi32(a.val); +} + +template +inline int v_extract_n(v_int32x8 a) +{ + return (int)v_extract_n(v_reinterpret_as_u32(a)); +} + +template +inline uint64 v_extract_n(v_uint64x4 a) +{ + return (uint64)_v256_extract_epi64(a.val); +} + +template +inline int64 v_extract_n(v_int64x4 v) +{ + return (int64)v_extract_n(v_reinterpret_as_u64(v)); +} + +template +inline float v_extract_n(v_float32x8 v) +{ + union { uint iv; float fv; } d; + d.iv = v_extract_n(v_reinterpret_as_u32(v)); + return d.fv; +} + +template +inline double v_extract_n(v_float64x4 v) +{ + union { uint64 iv; double dv; } d; + d.iv = v_extract_n(v_reinterpret_as_u64(v)); + return d.dv; +} + +template +inline v_uint32x8 v_broadcast_element(v_uint32x8 a) +{ + static const __m256i perm = _mm256_set1_epi32((char)i); + return v_uint32x8(_mm256_permutevar8x32_epi32(a.val, perm)); +} + +template +inline v_int32x8 v_broadcast_element(const v_int32x8 &a) +{ return v_reinterpret_as_s32(v_broadcast_element(v_reinterpret_as_u32(a))); } + +template +inline v_float32x8 v_broadcast_element(const v_float32x8 &a) +{ return v_reinterpret_as_f32(v_broadcast_element(v_reinterpret_as_u32(a))); } + + +///////////////////// load deinterleave ///////////////////////////// + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + + const __m256i sh = _mm256_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15, + 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15); + __m256i p0 = _mm256_shuffle_epi8(ab0, sh); + __m256i p1 = _mm256_shuffle_epi8(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint8x32(a0); + b = v_uint8x32(b0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + + const __m256i sh = _mm256_setr_epi8(0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, + 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15); + __m256i p0 = _mm256_shuffle_epi8(ab0, sh); + __m256i p1 = _mm256_shuffle_epi8(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint16x16(a0); + b = v_uint16x16(b0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + + const int sh = 0+2*4+1*16+3*64; + __m256i p0 = _mm256_shuffle_epi32(ab0, sh); + __m256i p1 = _mm256_shuffle_epi32(ab1, sh); + __m256i pl = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint32x8(a0); + b = v_uint32x8(b0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b ) +{ + __m256i ab0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i ab1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + + __m256i pl = _mm256_permute2x128_si256(ab0, ab1, 0 + 2*16); + __m256i ph = _mm256_permute2x128_si256(ab0, ab1, 1 + 3*16); + __m256i a0 = _mm256_unpacklo_epi64(pl, ph); + __m256i b0 = _mm256_unpackhi_epi64(pl, ph); + a = v_uint64x4(a0); + b = v_uint64x4(b0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 64)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1); + + __m256i b0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_high, s02_low, m1), bgr1, m0); + __m256i r0 = _mm256_blendv_epi8(_mm256_blendv_epi8(bgr1, s02_low, m0), s02_high, m1); + + const __m256i + sh_b = _mm256_setr_epi8(0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, + 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13), + sh_g = _mm256_setr_epi8(1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, + 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14), + sh_r = _mm256_setr_epi8(2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, + 2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15); + b0 = _mm256_shuffle_epi8(b0, sh_b); + g0 = _mm256_shuffle_epi8(g0, sh_g); + r0 = _mm256_shuffle_epi8(r0, sh_r); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + __m256i b0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_low, s02_high, m0), bgr1, m1); + __m256i g0 = _mm256_blendv_epi8(_mm256_blendv_epi8(bgr1, s02_low, m0), s02_high, m1); + __m256i r0 = _mm256_blendv_epi8(_mm256_blendv_epi8(s02_high, s02_low, m1), bgr1, m0); + const __m256i sh_b = _mm256_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _mm256_setr_epi8(2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, + 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13); + const __m256i sh_r = _mm256_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + b0 = _mm256_shuffle_epi8(b0, sh_b); + g0 = _mm256_shuffle_epi8(g0, sh_g); + r0 = _mm256_shuffle_epi8(r0, sh_r); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + + __m256i s02_low = _mm256_permute2x128_si256(bgr0, bgr2, 0 + 2*16); + __m256i s02_high = _mm256_permute2x128_si256(bgr0, bgr2, 1 + 3*16); + + __m256i b0 = _mm256_blend_epi32(_mm256_blend_epi32(s02_low, s02_high, 0x24), bgr1, 0x92); + __m256i g0 = _mm256_blend_epi32(_mm256_blend_epi32(s02_high, s02_low, 0x92), bgr1, 0x24); + __m256i r0 = _mm256_blend_epi32(_mm256_blend_epi32(bgr1, s02_low, 0x24), s02_high, 0x92); + + b0 = _mm256_shuffle_epi32(b0, 0x6c); + g0 = _mm256_shuffle_epi32(g0, 0xb1); + r0 = _mm256_shuffle_epi32(r0, 0xc6); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + + __m256i s01 = _mm256_blend_epi32(bgr0, bgr1, 0xf0); + __m256i s12 = _mm256_blend_epi32(bgr1, bgr2, 0xf0); + __m256i s20r = _mm256_permute4x64_epi64(_mm256_blend_epi32(bgr2, bgr0, 0xf0), 0x1b); + __m256i b0 = _mm256_unpacklo_epi64(s01, s20r); + __m256i g0 = _mm256_alignr_epi8(s12, s01, 8); + __m256i r0 = _mm256_unpackhi_epi64(s20r, s12); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x32& a, v_uint8x32& b, v_uint8x32& c, v_uint8x32& d ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 64)); + __m256i bgr3 = _mm256_loadu_si256((const __m256i*)(ptr + 96)); + const __m256i sh = _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, + 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); + + __m256i p0 = _mm256_shuffle_epi8(bgr0, sh); + __m256i p1 = _mm256_shuffle_epi8(bgr1, sh); + __m256i p2 = _mm256_shuffle_epi8(bgr2, sh); + __m256i p3 = _mm256_shuffle_epi8(bgr3, sh); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint8x32(b0); + b = v_uint8x32(g0); + c = v_uint8x32(r0); + d = v_uint8x32(a0); +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x16& a, v_uint16x16& b, v_uint16x16& c, v_uint16x16& d ) +{ + __m256i bgr0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgr1 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i bgr2 = _mm256_loadu_si256((const __m256i*)(ptr + 32)); + __m256i bgr3 = _mm256_loadu_si256((const __m256i*)(ptr + 48)); + const __m256i sh = _mm256_setr_epi8(0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15, + 0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15); + __m256i p0 = _mm256_shuffle_epi8(bgr0, sh); + __m256i p1 = _mm256_shuffle_epi8(bgr1, sh); + __m256i p2 = _mm256_shuffle_epi8(bgr2, sh); + __m256i p3 = _mm256_shuffle_epi8(bgr3, sh); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint16x16(b0); + b = v_uint16x16(g0); + c = v_uint16x16(r0); + d = v_uint16x16(a0); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x8& a, v_uint32x8& b, v_uint32x8& c, v_uint32x8& d ) +{ + __m256i p0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i p1 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i p2 = _mm256_loadu_si256((const __m256i*)(ptr + 16)); + __m256i p3 = _mm256_loadu_si256((const __m256i*)(ptr + 24)); + + __m256i p01l = _mm256_unpacklo_epi32(p0, p1); + __m256i p01h = _mm256_unpackhi_epi32(p0, p1); + __m256i p23l = _mm256_unpacklo_epi32(p2, p3); + __m256i p23h = _mm256_unpackhi_epi32(p2, p3); + + __m256i pll = _mm256_permute2x128_si256(p01l, p23l, 0 + 2*16); + __m256i plh = _mm256_permute2x128_si256(p01l, p23l, 1 + 3*16); + __m256i phl = _mm256_permute2x128_si256(p01h, p23h, 0 + 2*16); + __m256i phh = _mm256_permute2x128_si256(p01h, p23h, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi32(pll, plh); + __m256i g0 = _mm256_unpackhi_epi32(pll, plh); + __m256i r0 = _mm256_unpacklo_epi32(phl, phh); + __m256i a0 = _mm256_unpackhi_epi32(phl, phh); + + a = v_uint32x8(b0); + b = v_uint32x8(g0); + c = v_uint32x8(r0); + d = v_uint32x8(a0); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x4& a, v_uint64x4& b, v_uint64x4& c, v_uint64x4& d ) +{ + __m256i bgra0 = _mm256_loadu_si256((const __m256i*)ptr); + __m256i bgra1 = _mm256_loadu_si256((const __m256i*)(ptr + 4)); + __m256i bgra2 = _mm256_loadu_si256((const __m256i*)(ptr + 8)); + __m256i bgra3 = _mm256_loadu_si256((const __m256i*)(ptr + 12)); + + __m256i l02 = _mm256_permute2x128_si256(bgra0, bgra2, 0 + 2*16); + __m256i h02 = _mm256_permute2x128_si256(bgra0, bgra2, 1 + 3*16); + __m256i l13 = _mm256_permute2x128_si256(bgra1, bgra3, 0 + 2*16); + __m256i h13 = _mm256_permute2x128_si256(bgra1, bgra3, 1 + 3*16); + + __m256i b0 = _mm256_unpacklo_epi64(l02, l13); + __m256i g0 = _mm256_unpackhi_epi64(l02, l13); + __m256i r0 = _mm256_unpacklo_epi64(h02, h13); + __m256i a0 = _mm256_unpackhi_epi64(h02, h13); + + a = v_uint64x4(b0); + b = v_uint64x4(g0); + c = v_uint64x4(r0); + d = v_uint64x4(a0); +} + +///////////////////////////// store interleave ///////////////////////////////////// + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& x, const v_uint8x32& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi8(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi8(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 32), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 32), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 32), xy1); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& x, const v_uint16x16& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi16(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi16(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 16), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 16), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 16), xy1); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& x, const v_uint32x8& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi32(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi32(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 8), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 8), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 8), xy1); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& x, const v_uint64x4& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i xy_l = _mm256_unpacklo_epi64(x.val, y.val); + __m256i xy_h = _mm256_unpackhi_epi64(x.val, y.val); + + __m256i xy0 = _mm256_permute2x128_si256(xy_l, xy_h, 0 + 2*16); + __m256i xy1 = _mm256_permute2x128_si256(xy_l, xy_h, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, xy0); + _mm256_stream_si256((__m256i*)(ptr + 4), xy1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, xy0); + _mm256_store_si256((__m256i*)(ptr + 4), xy1); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, xy0); + _mm256_storeu_si256((__m256i*)(ptr + 4), xy1); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, const v_uint8x32& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _mm256_setr_epi8( + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5, + 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5); + const __m256i sh_g = _mm256_setr_epi8( + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, + 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10); + const __m256i sh_r = _mm256_setr_epi8( + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, + 10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15); + + __m256i b0 = _mm256_shuffle_epi8(a.val, sh_b); + __m256i g0 = _mm256_shuffle_epi8(b.val, sh_g); + __m256i r0 = _mm256_shuffle_epi8(c.val, sh_r); + + const __m256i m0 = _mm256_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + + __m256i p0 = _mm256_blendv_epi8(_mm256_blendv_epi8(b0, g0, m0), r0, m1); + __m256i p1 = _mm256_blendv_epi8(_mm256_blendv_epi8(g0, r0, m0), b0, m1); + __m256i p2 = _mm256_blendv_epi8(_mm256_blendv_epi8(r0, b0, m0), g0, m1); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + __m256i bgr1 = _mm256_permute2x128_si256(p2, p0, 0 + 3*16); + __m256i bgr2 = _mm256_permute2x128_si256(p1, p2, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 32), bgr1); + _mm256_stream_si256((__m256i*)(ptr + 64), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 32), bgr1); + _mm256_store_si256((__m256i*)(ptr + 64), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgr1); + _mm256_storeu_si256((__m256i*)(ptr + 64), bgr2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, const v_uint16x16& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + const __m256i sh_b = _mm256_setr_epi8( + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11, + 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m256i sh_g = _mm256_setr_epi8( + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, + 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5); + const __m256i sh_r = _mm256_setr_epi8( + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, + 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + + __m256i b0 = _mm256_shuffle_epi8(a.val, sh_b); + __m256i g0 = _mm256_shuffle_epi8(b.val, sh_g); + __m256i r0 = _mm256_shuffle_epi8(c.val, sh_r); + + const __m256i m0 = _mm256_setr_epi8(0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); + const __m256i m1 = _mm256_setr_epi8(0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, + -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0); + + __m256i p0 = _mm256_blendv_epi8(_mm256_blendv_epi8(b0, g0, m0), r0, m1); + __m256i p1 = _mm256_blendv_epi8(_mm256_blendv_epi8(g0, r0, m0), b0, m1); + __m256i p2 = _mm256_blendv_epi8(_mm256_blendv_epi8(r0, b0, m0), g0, m1); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p2, 0 + 2*16); + //__m256i bgr1 = p1; + __m256i bgr2 = _mm256_permute2x128_si256(p0, p2, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 16), p1); + _mm256_stream_si256((__m256i*)(ptr + 32), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 16), p1); + _mm256_store_si256((__m256i*)(ptr + 32), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 16), p1); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgr2); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, const v_uint32x8& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i b0 = _mm256_shuffle_epi32(a.val, 0x6c); + __m256i g0 = _mm256_shuffle_epi32(b.val, 0xb1); + __m256i r0 = _mm256_shuffle_epi32(c.val, 0xc6); + + __m256i p0 = _mm256_blend_epi32(_mm256_blend_epi32(b0, g0, 0x92), r0, 0x24); + __m256i p1 = _mm256_blend_epi32(_mm256_blend_epi32(g0, r0, 0x92), b0, 0x24); + __m256i p2 = _mm256_blend_epi32(_mm256_blend_epi32(r0, b0, 0x92), g0, 0x24); + + __m256i bgr0 = _mm256_permute2x128_si256(p0, p1, 0 + 2*16); + //__m256i bgr1 = p2; + __m256i bgr2 = _mm256_permute2x128_si256(p0, p1, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 8), p2); + _mm256_stream_si256((__m256i*)(ptr + 16), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 8), p2); + _mm256_store_si256((__m256i*)(ptr + 16), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 8), p2); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgr2); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, const v_uint64x4& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i s01 = _mm256_unpacklo_epi64(a.val, b.val); + __m256i s12 = _mm256_unpackhi_epi64(b.val, c.val); + __m256i s20 = _mm256_blend_epi32(c.val, a.val, 0xcc); + + __m256i bgr0 = _mm256_permute2x128_si256(s01, s20, 0 + 2*16); + __m256i bgr1 = _mm256_blend_epi32(s01, s12, 0x0f); + __m256i bgr2 = _mm256_permute2x128_si256(s20, s12, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgr0); + _mm256_stream_si256((__m256i*)(ptr + 4), bgr1); + _mm256_stream_si256((__m256i*)(ptr + 8), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgr0); + _mm256_store_si256((__m256i*)(ptr + 4), bgr1); + _mm256_store_si256((__m256i*)(ptr + 8), bgr2); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgr0); + _mm256_storeu_si256((__m256i*)(ptr + 4), bgr1); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgr2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x32& a, const v_uint8x32& b, + const v_uint8x32& c, const v_uint8x32& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi8(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi8(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi8(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi8(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi16(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi16(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi16(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi16(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 32), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 64), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 96), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 32), bgra1); + _mm256_store_si256((__m256i*)(ptr + 64), bgra2); + _mm256_store_si256((__m256i*)(ptr + 96), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 64), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 96), bgra3); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x16& a, const v_uint16x16& b, + const v_uint16x16& c, const v_uint16x16& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi16(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi16(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi16(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi16(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi32(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi32(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi32(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi32(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 16), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 32), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 48), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 16), bgra1); + _mm256_store_si256((__m256i*)(ptr + 32), bgra2); + _mm256_store_si256((__m256i*)(ptr + 48), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 32), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 48), bgra3); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x8& a, const v_uint32x8& b, + const v_uint32x8& c, const v_uint32x8& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi32(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi32(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi32(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi32(c.val, d.val); + + __m256i bgra0_ = _mm256_unpacklo_epi64(bg0, ra0); + __m256i bgra1_ = _mm256_unpackhi_epi64(bg0, ra0); + __m256i bgra2_ = _mm256_unpacklo_epi64(bg1, ra1); + __m256i bgra3_ = _mm256_unpackhi_epi64(bg1, ra1); + + __m256i bgra0 = _mm256_permute2x128_si256(bgra0_, bgra1_, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bgra0_, bgra1_, 1 + 3*16); + __m256i bgra1 = _mm256_permute2x128_si256(bgra2_, bgra3_, 0 + 2*16); + __m256i bgra3 = _mm256_permute2x128_si256(bgra2_, bgra3_, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 8), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 16), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 24), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 8), bgra1); + _mm256_store_si256((__m256i*)(ptr + 16), bgra2); + _mm256_store_si256((__m256i*)(ptr + 24), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 16), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 24), bgra3); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x4& a, const v_uint64x4& b, + const v_uint64x4& c, const v_uint64x4& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m256i bg0 = _mm256_unpacklo_epi64(a.val, b.val); + __m256i bg1 = _mm256_unpackhi_epi64(a.val, b.val); + __m256i ra0 = _mm256_unpacklo_epi64(c.val, d.val); + __m256i ra1 = _mm256_unpackhi_epi64(c.val, d.val); + + __m256i bgra0 = _mm256_permute2x128_si256(bg0, ra0, 0 + 2*16); + __m256i bgra1 = _mm256_permute2x128_si256(bg1, ra1, 0 + 2*16); + __m256i bgra2 = _mm256_permute2x128_si256(bg0, ra0, 1 + 3*16); + __m256i bgra3 = _mm256_permute2x128_si256(bg1, ra1, 1 + 3*16); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm256_stream_si256((__m256i*)ptr, bgra0); + _mm256_stream_si256((__m256i*)(ptr + 4), bgra1); + _mm256_stream_si256((__m256i*)(ptr + 8), bgra2); + _mm256_stream_si256((__m256i*)(ptr + 12), bgra3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm256_store_si256((__m256i*)ptr, bgra0); + _mm256_store_si256((__m256i*)(ptr + 4), bgra1); + _mm256_store_si256((__m256i*)(ptr + 8), bgra2); + _mm256_store_si256((__m256i*)(ptr + 12), bgra3); + } + else + { + _mm256_storeu_si256((__m256i*)ptr, bgra0); + _mm256_storeu_si256((__m256i*)(ptr + 4), bgra1); + _mm256_storeu_si256((__m256i*)(ptr + 8), bgra2); + _mm256_storeu_si256((__m256i*)(ptr + 12), bgra3); + } +} + +#define OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, const _Tpvec0& c0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int8x32, schar, s8, v_uint8x32, uchar, u8) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int16x16, short, s16, v_uint16x16, ushort, u16) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int32x8, int, s32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_float32x8, float, f32, v_uint32x8, unsigned, u32) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_int64x4, int64, s64, v_uint64x4, uint64, u64) +OPENCV_HAL_IMPL_AVX_LOADSTORE_INTERLEAVE(v_float64x4, double, f64, v_uint64x4, uint64, u64) + +// +// FP16 +// + +inline v_float32x8 v256_load_expand(const float16_t* ptr) +{ +#if CV_FP16 + return v_float32x8(_mm256_cvtph_ps(_mm_loadu_si128((const __m128i*)ptr))); +#else + float CV_DECL_ALIGNED(32) buf[8]; + for (int i = 0; i < 8; i++) + buf[i] = (float)ptr[i]; + return v256_load_aligned(buf); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x8& a) +{ +#if CV_FP16 + __m128i ah = _mm256_cvtps_ph(a.val, 0); + _mm_storeu_si128((__m128i*)ptr, ah); +#else + float CV_DECL_ALIGNED(32) buf[8]; + v_store_aligned(buf, a); + for (int i = 0; i < 8; i++) + ptr[i] = float16_t(buf[i]); +#endif +} + +// +// end of FP16 +// + +inline void v256_cleanup() { _mm256_zeroall(); } + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_AVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx512.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx512.hpp new file mode 100644 index 0000000..75a3bd4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_avx512.hpp @@ -0,0 +1,3078 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_AVX512_HPP +#define OPENCV_HAL_INTRIN_AVX512_HPP + +#if defined(_MSC_VER) && (_MSC_VER < 1920/*MSVS2019*/) +# pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned +# pragma warning(disable:4309) // 'argument': truncation of constant value +# pragma warning(disable:4310) // cast truncates constant value +#endif + +#define CVT_ROUND_MODES_IMPLEMENTED 0 + +#define CV_SIMD512 1 +#define CV_SIMD512_64F 1 +#define CV_SIMD512_FP16 0 // no native operations with FP16 type. Only load/store from float32x8 are available (if CV_FP16 == 1) + +#define _v512_set_epu64(a7, a6, a5, a4, a3, a2, a1, a0) _mm512_set_epi64((int64)(a7),(int64)(a6),(int64)(a5),(int64)(a4),(int64)(a3),(int64)(a2),(int64)(a1),(int64)(a0)) +#define _v512_set_epu32(a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _mm512_set_epi64(((int64)(a15)<<32)|(int64)(a14), ((int64)(a13)<<32)|(int64)(a12), ((int64)(a11)<<32)|(int64)(a10), ((int64)( a9)<<32)|(int64)( a8), \ + ((int64)( a7)<<32)|(int64)( a6), ((int64)( a5)<<32)|(int64)( a4), ((int64)( a3)<<32)|(int64)( a2), ((int64)( a1)<<32)|(int64)( a0)) +#define _v512_set_epu16(a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu32(((unsigned)(a31)<<16)|(unsigned)(a30), ((unsigned)(a29)<<16)|(unsigned)(a28), ((unsigned)(a27)<<16)|(unsigned)(a26), ((unsigned)(a25)<<16)|(unsigned)(a24), \ + ((unsigned)(a23)<<16)|(unsigned)(a22), ((unsigned)(a21)<<16)|(unsigned)(a20), ((unsigned)(a19)<<16)|(unsigned)(a18), ((unsigned)(a17)<<16)|(unsigned)(a16), \ + ((unsigned)(a15)<<16)|(unsigned)(a14), ((unsigned)(a13)<<16)|(unsigned)(a12), ((unsigned)(a11)<<16)|(unsigned)(a10), ((unsigned)( a9)<<16)|(unsigned)( a8), \ + ((unsigned)( a7)<<16)|(unsigned)( a6), ((unsigned)( a5)<<16)|(unsigned)( a4), ((unsigned)( a3)<<16)|(unsigned)( a2), ((unsigned)( a1)<<16)|(unsigned)( a0)) +#define _v512_set_epu8(a63, a62, a61, a60, a59, a58, a57, a56, a55, a54, a53, a52, a51, a50, a49, a48, \ + a47, a46, a45, a44, a43, a42, a41, a40, a39, a38, a37, a36, a35, a34, a33, a32, \ + a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu32(((unsigned)(a63)<<24)|((unsigned)(a62)<<16)|((unsigned)(a61)<<8)|(unsigned)(a60),((unsigned)(a59)<<24)|((unsigned)(a58)<<16)|((unsigned)(a57)<<8)|(unsigned)(a56), \ + ((unsigned)(a55)<<24)|((unsigned)(a54)<<16)|((unsigned)(a53)<<8)|(unsigned)(a52),((unsigned)(a51)<<24)|((unsigned)(a50)<<16)|((unsigned)(a49)<<8)|(unsigned)(a48), \ + ((unsigned)(a47)<<24)|((unsigned)(a46)<<16)|((unsigned)(a45)<<8)|(unsigned)(a44),((unsigned)(a43)<<24)|((unsigned)(a42)<<16)|((unsigned)(a41)<<8)|(unsigned)(a40), \ + ((unsigned)(a39)<<24)|((unsigned)(a38)<<16)|((unsigned)(a37)<<8)|(unsigned)(a36),((unsigned)(a35)<<24)|((unsigned)(a34)<<16)|((unsigned)(a33)<<8)|(unsigned)(a32), \ + ((unsigned)(a31)<<24)|((unsigned)(a30)<<16)|((unsigned)(a29)<<8)|(unsigned)(a28),((unsigned)(a27)<<24)|((unsigned)(a26)<<16)|((unsigned)(a25)<<8)|(unsigned)(a24), \ + ((unsigned)(a23)<<24)|((unsigned)(a22)<<16)|((unsigned)(a21)<<8)|(unsigned)(a20),((unsigned)(a19)<<24)|((unsigned)(a18)<<16)|((unsigned)(a17)<<8)|(unsigned)(a16), \ + ((unsigned)(a15)<<24)|((unsigned)(a14)<<16)|((unsigned)(a13)<<8)|(unsigned)(a12),((unsigned)(a11)<<24)|((unsigned)(a10)<<16)|((unsigned)( a9)<<8)|(unsigned)( a8), \ + ((unsigned)( a7)<<24)|((unsigned)( a6)<<16)|((unsigned)( a5)<<8)|(unsigned)( a4),((unsigned)( a3)<<24)|((unsigned)( a2)<<16)|((unsigned)( a1)<<8)|(unsigned)( a0)) +#define _v512_set_epi8(a63, a62, a61, a60, a59, a58, a57, a56, a55, a54, a53, a52, a51, a50, a49, a48, \ + a47, a46, a45, a44, a43, a42, a41, a40, a39, a38, a37, a36, a35, a34, a33, a32, \ + a31, a30, a29, a28, a27, a26, a25, a24, a23, a22, a21, a20, a19, a18, a17, a16, \ + a15, a14, a13, a12, a11, a10, a9, a8, a7, a6, a5, a4, a3, a2, a1, a0) \ + _v512_set_epu8((uchar)(a63), (uchar)(a62), (uchar)(a61), (uchar)(a60), (uchar)(a59), (uchar)(a58), (uchar)(a57), (uchar)(a56), \ + (uchar)(a55), (uchar)(a54), (uchar)(a53), (uchar)(a52), (uchar)(a51), (uchar)(a50), (uchar)(a49), (uchar)(a48), \ + (uchar)(a47), (uchar)(a46), (uchar)(a45), (uchar)(a44), (uchar)(a43), (uchar)(a42), (uchar)(a41), (uchar)(a40), \ + (uchar)(a39), (uchar)(a38), (uchar)(a37), (uchar)(a36), (uchar)(a35), (uchar)(a34), (uchar)(a33), (uchar)(a32), \ + (uchar)(a31), (uchar)(a30), (uchar)(a29), (uchar)(a28), (uchar)(a27), (uchar)(a26), (uchar)(a25), (uchar)(a24), \ + (uchar)(a23), (uchar)(a22), (uchar)(a21), (uchar)(a20), (uchar)(a19), (uchar)(a18), (uchar)(a17), (uchar)(a16), \ + (uchar)(a15), (uchar)(a14), (uchar)(a13), (uchar)(a12), (uchar)(a11), (uchar)(a10), (uchar)( a9), (uchar)( a8), \ + (uchar)( a7), (uchar)( a6), (uchar)( a5), (uchar)( a4), (uchar)( a3), (uchar)( a2), (uchar)( a1), (uchar)( a0)) + +#ifndef _mm512_cvtpd_pslo +#ifdef _mm512_zextsi256_si512 +#define _mm512_cvtpd_pslo(a) _mm512_zextps256_ps512(_mm512_cvtpd_ps(a)) +#else +//if preferred way to extend with zeros is unavailable +#define _mm512_cvtpd_pslo(a) _mm512_castps256_ps512(_mm512_cvtpd_ps(a)) +#endif +#endif +///////// Utils //////////// + +namespace +{ + +inline __m512i _v512_combine(const __m256i& lo, const __m256i& hi) +{ return _mm512_inserti32x8(_mm512_castsi256_si512(lo), hi, 1); } + +inline __m512 _v512_combine(const __m256& lo, const __m256& hi) +{ return _mm512_insertf32x8(_mm512_castps256_ps512(lo), hi, 1); } + +inline __m512d _v512_combine(const __m256d& lo, const __m256d& hi) +{ return _mm512_insertf64x4(_mm512_castpd256_pd512(lo), hi, 1); } + +inline int _v_cvtsi512_si32(const __m512i& a) +{ return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); } + +inline __m256i _v512_extract_high(const __m512i& v) +{ return _mm512_extracti32x8_epi32(v, 1); } + +inline __m256 _v512_extract_high(const __m512& v) +{ return _mm512_extractf32x8_ps(v, 1); } + +inline __m256d _v512_extract_high(const __m512d& v) +{ return _mm512_extractf64x4_pd(v, 1); } + +inline __m256i _v512_extract_low(const __m512i& v) +{ return _mm512_castsi512_si256(v); } + +inline __m256 _v512_extract_low(const __m512& v) +{ return _mm512_castps512_ps256(v); } + +inline __m256d _v512_extract_low(const __m512d& v) +{ return _mm512_castpd512_pd256(v); } + +inline __m512i _v512_insert(const __m512i& a, const __m256i& b) +{ return _mm512_inserti32x8(a, b, 0); } + +inline __m512 _v512_insert(const __m512& a, const __m256& b) +{ return _mm512_insertf32x8(a, b, 0); } + +inline __m512d _v512_insert(const __m512d& a, const __m256d& b) +{ return _mm512_insertf64x4(a, b, 0); } + +} + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x64 +{ + typedef uchar lane_type; + enum { nlanes = 64 }; + __m512i val; + + explicit v_uint8x64(__m512i v) : val(v) {} + v_uint8x64(uchar v0, uchar v1, uchar v2, uchar v3, + uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, + uchar v12, uchar v13, uchar v14, uchar v15, + uchar v16, uchar v17, uchar v18, uchar v19, + uchar v20, uchar v21, uchar v22, uchar v23, + uchar v24, uchar v25, uchar v26, uchar v27, + uchar v28, uchar v29, uchar v30, uchar v31, + uchar v32, uchar v33, uchar v34, uchar v35, + uchar v36, uchar v37, uchar v38, uchar v39, + uchar v40, uchar v41, uchar v42, uchar v43, + uchar v44, uchar v45, uchar v46, uchar v47, + uchar v48, uchar v49, uchar v50, uchar v51, + uchar v52, uchar v53, uchar v54, uchar v55, + uchar v56, uchar v57, uchar v58, uchar v59, + uchar v60, uchar v61, uchar v62, uchar v63) + { + val = _v512_set_epu8(v63, v62, v61, v60, v59, v58, v57, v56, v55, v54, v53, v52, v51, v50, v49, v48, + v47, v46, v45, v44, v43, v42, v41, v40, v39, v38, v37, v36, v35, v34, v33, v32, + v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_uint8x64() {} + + static inline v_uint8x64 zero() { return v_uint8x64(_mm512_setzero_si512()); } + + uchar get0() const { return (uchar)_v_cvtsi512_si32(val); } +}; + +struct v_int8x64 +{ + typedef schar lane_type; + enum { nlanes = 64 }; + __m512i val; + + explicit v_int8x64(__m512i v) : val(v) {} + v_int8x64(schar v0, schar v1, schar v2, schar v3, + schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, + schar v12, schar v13, schar v14, schar v15, + schar v16, schar v17, schar v18, schar v19, + schar v20, schar v21, schar v22, schar v23, + schar v24, schar v25, schar v26, schar v27, + schar v28, schar v29, schar v30, schar v31, + schar v32, schar v33, schar v34, schar v35, + schar v36, schar v37, schar v38, schar v39, + schar v40, schar v41, schar v42, schar v43, + schar v44, schar v45, schar v46, schar v47, + schar v48, schar v49, schar v50, schar v51, + schar v52, schar v53, schar v54, schar v55, + schar v56, schar v57, schar v58, schar v59, + schar v60, schar v61, schar v62, schar v63) + { + val = _v512_set_epi8(v63, v62, v61, v60, v59, v58, v57, v56, v55, v54, v53, v52, v51, v50, v49, v48, + v47, v46, v45, v44, v43, v42, v41, v40, v39, v38, v37, v36, v35, v34, v33, v32, + v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_int8x64() {} + + static inline v_int8x64 zero() { return v_int8x64(_mm512_setzero_si512()); } + + schar get0() const { return (schar)_v_cvtsi512_si32(val); } +}; + +struct v_uint16x32 +{ + typedef ushort lane_type; + enum { nlanes = 32 }; + __m512i val; + + explicit v_uint16x32(__m512i v) : val(v) {} + v_uint16x32(ushort v0, ushort v1, ushort v2, ushort v3, + ushort v4, ushort v5, ushort v6, ushort v7, + ushort v8, ushort v9, ushort v10, ushort v11, + ushort v12, ushort v13, ushort v14, ushort v15, + ushort v16, ushort v17, ushort v18, ushort v19, + ushort v20, ushort v21, ushort v22, ushort v23, + ushort v24, ushort v25, ushort v26, ushort v27, + ushort v28, ushort v29, ushort v30, ushort v31) + { + val = _v512_set_epu16(v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, + v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0); + } + v_uint16x32() {} + + static inline v_uint16x32 zero() { return v_uint16x32(_mm512_setzero_si512()); } + + ushort get0() const { return (ushort)_v_cvtsi512_si32(val); } +}; + +struct v_int16x32 +{ + typedef short lane_type; + enum { nlanes = 32 }; + __m512i val; + + explicit v_int16x32(__m512i v) : val(v) {} + v_int16x32(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7, + short v8, short v9, short v10, short v11, short v12, short v13, short v14, short v15, + short v16, short v17, short v18, short v19, short v20, short v21, short v22, short v23, + short v24, short v25, short v26, short v27, short v28, short v29, short v30, short v31) + { + val = _v512_set_epu16((ushort)v31, (ushort)v30, (ushort)v29, (ushort)v28, (ushort)v27, (ushort)v26, (ushort)v25, (ushort)v24, + (ushort)v23, (ushort)v22, (ushort)v21, (ushort)v20, (ushort)v19, (ushort)v18, (ushort)v17, (ushort)v16, + (ushort)v15, (ushort)v14, (ushort)v13, (ushort)v12, (ushort)v11, (ushort)v10, (ushort)v9 , (ushort)v8, + (ushort)v7 , (ushort)v6 , (ushort)v5 , (ushort)v4 , (ushort)v3 , (ushort)v2 , (ushort)v1 , (ushort)v0); + } + v_int16x32() {} + + static inline v_int16x32 zero() { return v_int16x32(_mm512_setzero_si512()); } + + short get0() const { return (short)_v_cvtsi512_si32(val); } +}; + +struct v_uint32x16 +{ + typedef unsigned lane_type; + enum { nlanes = 16 }; + __m512i val; + + explicit v_uint32x16(__m512i v) : val(v) {} + v_uint32x16(unsigned v0, unsigned v1, unsigned v2, unsigned v3, + unsigned v4, unsigned v5, unsigned v6, unsigned v7, + unsigned v8, unsigned v9, unsigned v10, unsigned v11, + unsigned v12, unsigned v13, unsigned v14, unsigned v15) + { + val = _mm512_setr_epi32((int)v0, (int)v1, (int)v2, (int)v3, (int)v4, (int)v5, (int)v6, (int)v7, + (int)v8, (int)v9, (int)v10, (int)v11, (int)v12, (int)v13, (int)v14, (int)v15); + } + v_uint32x16() {} + + static inline v_uint32x16 zero() { return v_uint32x16(_mm512_setzero_si512()); } + + unsigned get0() const { return (unsigned)_v_cvtsi512_si32(val); } +}; + +struct v_int32x16 +{ + typedef int lane_type; + enum { nlanes = 16 }; + __m512i val; + + explicit v_int32x16(__m512i v) : val(v) {} + v_int32x16(int v0, int v1, int v2, int v3, int v4, int v5, int v6, int v7, + int v8, int v9, int v10, int v11, int v12, int v13, int v14, int v15) + { + val = _mm512_setr_epi32(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15); + } + v_int32x16() {} + + static inline v_int32x16 zero() { return v_int32x16(_mm512_setzero_si512()); } + + int get0() const { return _v_cvtsi512_si32(val); } +}; + +struct v_float32x16 +{ + typedef float lane_type; + enum { nlanes = 16 }; + __m512 val; + + explicit v_float32x16(__m512 v) : val(v) {} + v_float32x16(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, + float v8, float v9, float v10, float v11, float v12, float v13, float v14, float v15) + { + val = _mm512_setr_ps(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15); + } + v_float32x16() {} + + static inline v_float32x16 zero() { return v_float32x16(_mm512_setzero_ps()); } + + float get0() const { return _mm_cvtss_f32(_mm512_castps512_ps128(val)); } +}; + +struct v_uint64x8 +{ + typedef uint64 lane_type; + enum { nlanes = 8 }; + __m512i val; + + explicit v_uint64x8(__m512i v) : val(v) {} + v_uint64x8(uint64 v0, uint64 v1, uint64 v2, uint64 v3, uint64 v4, uint64 v5, uint64 v6, uint64 v7) + { val = _mm512_setr_epi64((int64)v0, (int64)v1, (int64)v2, (int64)v3, (int64)v4, (int64)v5, (int64)v6, (int64)v7); } + v_uint64x8() {} + + static inline v_uint64x8 zero() { return v_uint64x8(_mm512_setzero_si512()); } + + uint64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (uint64)_mm_cvtsi128_si64(_mm512_castsi512_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm512_castsi512_si128(val)); + int b = _mm_cvtsi128_si32(_mm512_castsi512_si128(_mm512_srli_epi64(val, 32))); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #endif + } +}; + +struct v_int64x8 +{ + typedef int64 lane_type; + enum { nlanes = 8 }; + __m512i val; + + explicit v_int64x8(__m512i v) : val(v) {} + v_int64x8(int64 v0, int64 v1, int64 v2, int64 v3, int64 v4, int64 v5, int64 v6, int64 v7) + { val = _mm512_setr_epi64(v0, v1, v2, v3, v4, v5, v6, v7); } + v_int64x8() {} + + static inline v_int64x8 zero() { return v_int64x8(_mm512_setzero_si512()); } + + int64 get0() const + { + #if defined __x86_64__ || defined _M_X64 + return (int64)_mm_cvtsi128_si64(_mm512_castsi512_si128(val)); + #else + int a = _mm_cvtsi128_si32(_mm512_castsi512_si128(val)); + int b = _mm_cvtsi128_si32(_mm512_castsi512_si128(_mm512_srli_epi64(val, 32))); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #endif + } +}; + +struct v_float64x8 +{ + typedef double lane_type; + enum { nlanes = 8 }; + __m512d val; + + explicit v_float64x8(__m512d v) : val(v) {} + v_float64x8(double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7) + { val = _mm512_setr_pd(v0, v1, v2, v3, v4, v5, v6, v7); } + v_float64x8() {} + + static inline v_float64x8 zero() { return v_float64x8(_mm512_setzero_pd()); } + + double get0() const { return _mm_cvtsd_f64(_mm512_castpd512_pd128(val)); } +}; + +//////////////// Load and store operations /////////////// + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE(_Tpvec, _Tp) \ + inline _Tpvec v512_load(const _Tp* ptr) \ + { return _Tpvec(_mm512_loadu_si512((const __m512i*)ptr)); } \ + inline _Tpvec v512_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm512_load_si512((const __m512i*)ptr)); } \ + inline _Tpvec v512_load_low(const _Tp* ptr) \ + { \ + __m256i v256 = _mm256_loadu_si256((const __m256i*)ptr); \ + return _Tpvec(_mm512_castsi256_si512(v256)); \ + } \ + inline _Tpvec v512_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + __m256i vlo = _mm256_loadu_si256((const __m256i*)ptr0); \ + __m256i vhi = _mm256_loadu_si256((const __m256i*)ptr1); \ + return _Tpvec(_v512_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm512_storeu_si512((__m512i*)ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm512_store_si512((__m512i*)ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm512_stream_si512((__m512i*)ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm512_storeu_si512((__m512i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm512_stream_si512((__m512i*)ptr, a.val); \ + else \ + _mm512_store_si512((__m512i*)ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, _v512_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_si256((__m256i*)ptr, _v512_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint8x64, uchar) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int8x64, schar) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint16x32, ushort) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int16x32, short) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint32x16, unsigned) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int32x16, int) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_uint64x8, uint64) +OPENCV_HAL_IMPL_AVX512_LOADSTORE(v_int64x8, int64) + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(_Tpvec, _Tp, suffix, halfreg) \ + inline _Tpvec v512_load(const _Tp* ptr) \ + { return _Tpvec(_mm512_loadu_##suffix(ptr)); } \ + inline _Tpvec v512_load_aligned(const _Tp* ptr) \ + { return _Tpvec(_mm512_load_##suffix(ptr)); } \ + inline _Tpvec v512_load_low(const _Tp* ptr) \ + { \ + return _Tpvec(_mm512_cast##suffix##256_##suffix##512 \ + (_mm256_loadu_##suffix(ptr))); \ + } \ + inline _Tpvec v512_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ + { \ + halfreg vlo = _mm256_loadu_##suffix(ptr0); \ + halfreg vhi = _mm256_loadu_##suffix(ptr1); \ + return _Tpvec(_v512_combine(vlo, vhi)); \ + } \ + inline void v_store(_Tp* ptr, const _Tpvec& a) \ + { _mm512_storeu_##suffix(ptr, a.val); } \ + inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ + { _mm512_store_##suffix(ptr, a.val); } \ + inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ + { _mm512_stream_##suffix(ptr, a.val); } \ + inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ + { \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm512_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm512_stream_##suffix(ptr, a.val); \ + else \ + _mm512_store_##suffix(ptr, a.val); \ + } \ + inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, _v512_extract_low(a.val)); } \ + inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ + { _mm256_storeu_##suffix(ptr, _v512_extract_high(a.val)); } + +OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(v_float32x16, float, ps, __m256) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_FLT(v_float64x8, double, pd, __m256d) + +#define OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, _Tpvecf, suffix, cast) \ + inline _Tpvec v_reinterpret_as_##suffix(const _Tpvecf& a) \ + { return _Tpvec(cast(a.val)); } + +#define OPENCV_HAL_IMPL_AVX512_INIT(_Tpvec, _Tp, suffix, ssuffix, ctype_s) \ + inline _Tpvec v512_setzero_##suffix() \ + { return _Tpvec(_mm512_setzero_si512()); } \ + inline _Tpvec v512_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm512_set1_##ssuffix((ctype_s)v)); } \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint8x64, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int8x64, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint16x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int16x32, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint32x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int32x16, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint64x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int64x8, suffix, OPENCV_HAL_NOP) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_float32x16, suffix, _mm512_castps_si512) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_float64x8, suffix, _mm512_castpd_si512) + +OPENCV_HAL_IMPL_AVX512_INIT(v_uint8x64, uchar, u8, epi8, char) +OPENCV_HAL_IMPL_AVX512_INIT(v_int8x64, schar, s8, epi8, char) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint16x32, ushort, u16, epi16, short) +OPENCV_HAL_IMPL_AVX512_INIT(v_int16x32, short, s16, epi16, short) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint32x16, unsigned, u32, epi32, int) +OPENCV_HAL_IMPL_AVX512_INIT(v_int32x16, int, s32, epi32, int) +OPENCV_HAL_IMPL_AVX512_INIT(v_uint64x8, uint64, u64, epi64, int64) +OPENCV_HAL_IMPL_AVX512_INIT(v_int64x8, int64, s64, epi64, int64) + +#define OPENCV_HAL_IMPL_AVX512_INIT_FLT(_Tpvec, _Tp, suffix, zsuffix, cast) \ + inline _Tpvec v512_setzero_##suffix() \ + { return _Tpvec(_mm512_setzero_##zsuffix()); } \ + inline _Tpvec v512_setall_##suffix(_Tp v) \ + { return _Tpvec(_mm512_set1_##zsuffix(v)); } \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint8x64, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int8x64, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint16x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int16x32, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint32x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int32x16, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_uint64x8, suffix, cast) \ + OPENCV_HAL_IMPL_AVX512_CAST(_Tpvec, v_int64x8, suffix, cast) + +OPENCV_HAL_IMPL_AVX512_INIT_FLT(v_float32x16, float, f32, ps, _mm512_castsi512_ps) +OPENCV_HAL_IMPL_AVX512_INIT_FLT(v_float64x8, double, f64, pd, _mm512_castsi512_pd) + +inline v_float32x16 v_reinterpret_as_f32(const v_float32x16& a) +{ return a; } +inline v_float32x16 v_reinterpret_as_f32(const v_float64x8& a) +{ return v_float32x16(_mm512_castpd_ps(a.val)); } + +inline v_float64x8 v_reinterpret_as_f64(const v_float64x8& a) +{ return a; } +inline v_float64x8 v_reinterpret_as_f64(const v_float32x16& a) +{ return v_float64x8(_mm512_castps_pd(a.val)); } + +// FP16 +inline v_float32x16 v512_load_expand(const float16_t* ptr) +{ + return v_float32x16(_mm512_cvtph_ps(_mm256_loadu_si256((const __m256i*)ptr))); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x16& a) +{ + __m256i ah = _mm512_cvtps_ph(a.val, 0); + _mm256_storeu_si256((__m256i*)ptr, ah); +} + +/* Recombine & ZIP */ +inline void v_zip(const v_int8x64& a, const v_int8x64& b, v_int8x64& ab0, v_int8x64& ab1) +{ +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8( 95, 31, 94, 30, 93, 29, 92, 28, 91, 27, 90, 26, 89, 25, 88, 24, + 87, 23, 86, 22, 85, 21, 84, 20, 83, 19, 82, 18, 81, 17, 80, 16, + 79, 15, 78, 14, 77, 13, 76, 12, 75, 11, 74, 10, 73, 9, 72, 8, + 71, 7, 70, 6, 69, 5, 68, 4, 67, 3, 66, 2, 65, 1, 64, 0); + ab0 = v_int8x64(_mm512_permutex2var_epi8(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu8(127, 63, 126, 62, 125, 61, 124, 60, 123, 59, 122, 58, 121, 57, 120, 56, + 119, 55, 118, 54, 117, 53, 116, 52, 115, 51, 114, 50, 113, 49, 112, 48, + 111, 47, 110, 46, 109, 45, 108, 44, 107, 43, 106, 42, 105, 41, 104, 40, + 103, 39, 102, 38, 101, 37, 100, 36, 99, 35, 98, 34, 97, 33, 96, 32); + ab1 = v_int8x64(_mm512_permutex2var_epi8(a.val, mask1, b.val)); +#else + __m512i low = _mm512_unpacklo_epi8(a.val, b.val); + __m512i high = _mm512_unpackhi_epi8(a.val, b.val); + ab0 = v_int8x64(_mm512_permutex2var_epi64(low, _v512_set_epu64(11, 10, 3, 2, 9, 8, 1, 0), high)); + ab1 = v_int8x64(_mm512_permutex2var_epi64(low, _v512_set_epu64(15, 14, 7, 6, 13, 12, 5, 4), high)); +#endif +} +inline void v_zip(const v_int16x32& a, const v_int16x32& b, v_int16x32& ab0, v_int16x32& ab1) +{ + __m512i mask0 = _v512_set_epu16(47, 15, 46, 14, 45, 13, 44, 12, 43, 11, 42, 10, 41, 9, 40, 8, + 39, 7, 38, 6, 37, 5, 36, 4, 35, 3, 34, 2, 33, 1, 32, 0); + ab0 = v_int16x32(_mm512_permutex2var_epi16(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu16(63, 31, 62, 30, 61, 29, 60, 28, 59, 27, 58, 26, 57, 25, 56, 24, + 55, 23, 54, 22, 53, 21, 52, 20, 51, 19, 50, 18, 49, 17, 48, 16); + ab1 = v_int16x32(_mm512_permutex2var_epi16(a.val, mask1, b.val)); +} +inline void v_zip(const v_int32x16& a, const v_int32x16& b, v_int32x16& ab0, v_int32x16& ab1) +{ + __m512i mask0 = _v512_set_epu32(23, 7, 22, 6, 21, 5, 20, 4, 19, 3, 18, 2, 17, 1, 16, 0); + ab0 = v_int32x16(_mm512_permutex2var_epi32(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu32(31, 15, 30, 14, 29, 13, 28, 12, 27, 11, 26, 10, 25, 9, 24, 8); + ab1 = v_int32x16(_mm512_permutex2var_epi32(a.val, mask1, b.val)); +} +inline void v_zip(const v_int64x8& a, const v_int64x8& b, v_int64x8& ab0, v_int64x8& ab1) +{ + __m512i mask0 = _v512_set_epu64(11, 3, 10, 2, 9, 1, 8, 0); + ab0 = v_int64x8(_mm512_permutex2var_epi64(a.val, mask0, b.val)); + __m512i mask1 = _v512_set_epu64(15, 7, 14, 6, 13, 5, 12, 4); + ab1 = v_int64x8(_mm512_permutex2var_epi64(a.val, mask1, b.val)); +} + +inline void v_zip(const v_uint8x64& a, const v_uint8x64& b, v_uint8x64& ab0, v_uint8x64& ab1) +{ + v_int8x64 i0, i1; + v_zip(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b), i0, i1); + ab0 = v_reinterpret_as_u8(i0); + ab1 = v_reinterpret_as_u8(i1); +} +inline void v_zip(const v_uint16x32& a, const v_uint16x32& b, v_uint16x32& ab0, v_uint16x32& ab1) +{ + v_int16x32 i0, i1; + v_zip(v_reinterpret_as_s16(a), v_reinterpret_as_s16(b), i0, i1); + ab0 = v_reinterpret_as_u16(i0); + ab1 = v_reinterpret_as_u16(i1); +} +inline void v_zip(const v_uint32x16& a, const v_uint32x16& b, v_uint32x16& ab0, v_uint32x16& ab1) +{ + v_int32x16 i0, i1; + v_zip(v_reinterpret_as_s32(a), v_reinterpret_as_s32(b), i0, i1); + ab0 = v_reinterpret_as_u32(i0); + ab1 = v_reinterpret_as_u32(i1); +} +inline void v_zip(const v_uint64x8& a, const v_uint64x8& b, v_uint64x8& ab0, v_uint64x8& ab1) +{ + v_int64x8 i0, i1; + v_zip(v_reinterpret_as_s64(a), v_reinterpret_as_s64(b), i0, i1); + ab0 = v_reinterpret_as_u64(i0); + ab1 = v_reinterpret_as_u64(i1); +} +inline void v_zip(const v_float32x16& a, const v_float32x16& b, v_float32x16& ab0, v_float32x16& ab1) +{ + v_int32x16 i0, i1; + v_zip(v_reinterpret_as_s32(a), v_reinterpret_as_s32(b), i0, i1); + ab0 = v_reinterpret_as_f32(i0); + ab1 = v_reinterpret_as_f32(i1); +} +inline void v_zip(const v_float64x8& a, const v_float64x8& b, v_float64x8& ab0, v_float64x8& ab1) +{ + v_int64x8 i0, i1; + v_zip(v_reinterpret_as_s64(a), v_reinterpret_as_s64(b), i0, i1); + ab0 = v_reinterpret_as_f64(i0); + ab1 = v_reinterpret_as_f64(i1); +} + +#define OPENCV_HAL_IMPL_AVX512_COMBINE(_Tpvec, suffix) \ + inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_v512_combine(_v512_extract_low(a.val), _v512_extract_low(b.val))); } \ + inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_v512_insert(b.val, _v512_extract_high(a.val))); } \ + inline void v_recombine(const _Tpvec& a, const _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ + { \ + c.val = _v512_combine(_v512_extract_low(a.val),_v512_extract_low(b.val)); \ + d.val = _v512_insert(b.val,_v512_extract_high(a.val)); \ + } + + +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint8x64, epi8) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int8x64, epi8) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint16x32, epi16) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int16x32, epi16) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_uint64x8, epi64) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_int64x8, epi64) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_COMBINE(v_float64x8, pd) + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_AVX512_BIN_FUNC(func, _Tpvec, intrin) \ + inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_uint8x64, _mm512_add_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_int8x64, _mm512_add_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_uint16x32, _mm512_add_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_add_wrap, v_int16x32, _mm512_add_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_uint8x64, _mm512_sub_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_int8x64, _mm512_sub_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_uint16x32, _mm512_sub_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_sub_wrap, v_int16x32, _mm512_sub_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_mul_wrap, v_uint16x32, _mm512_mullo_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_mul_wrap, v_int16x32, _mm512_mullo_epi16) + +inline v_uint8x64 v_mul_wrap(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i ad = _mm512_srai_epi16(a.val, 8); + __m512i bd = _mm512_srai_epi16(b.val, 8); + __m512i p0 = _mm512_mullo_epi16(a.val, b.val); // even + __m512i p1 = _mm512_slli_epi16(_mm512_mullo_epi16(ad, bd), 8); // odd + return v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, p0, p1)); +} +inline v_int8x64 v_mul_wrap(const v_int8x64& a, const v_int8x64& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +#define OPENCV_HAL_IMPL_AVX512_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(intrin(a.val, b.val)); } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint32x16, _mm512_add_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint32x16, _mm512_sub_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int32x16, _mm512_add_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int32x16, _mm512_sub_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint64x8, _mm512_add_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint64x8, _mm512_sub_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int64x8, _mm512_add_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int64x8, _mm512_sub_epi64) + +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_uint32x16, _mm512_mullo_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_int32x16, _mm512_mullo_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_uint64x8, _mm512_mullo_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_int64x8, _mm512_mullo_epi64) + +/** Saturating arithmetics **/ +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint8x64, _mm512_adds_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint8x64, _mm512_subs_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int8x64, _mm512_adds_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int8x64, _mm512_subs_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_uint16x32, _mm512_adds_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_uint16x32, _mm512_subs_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_int16x32, _mm512_adds_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_int16x32, _mm512_subs_epi16) + +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_float32x16, _mm512_add_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_float32x16, _mm512_sub_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_float32x16, _mm512_mul_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(/, v_float32x16, _mm512_div_ps) +OPENCV_HAL_IMPL_AVX512_BIN_OP(+, v_float64x8, _mm512_add_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(-, v_float64x8, _mm512_sub_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(*, v_float64x8, _mm512_mul_pd) +OPENCV_HAL_IMPL_AVX512_BIN_OP(/, v_float64x8, _mm512_div_pd) + +// saturating multiply +inline v_uint8x64 operator * (const v_uint8x64& a, const v_uint8x64& b) +{ + v_uint16x32 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_int8x64 operator * (const v_int8x64& a, const v_int8x64& b) +{ + v_int16x32 c, d; + v_mul_expand(a, b, c, d); + return v_pack(c, d); +} +inline v_uint16x32 operator * (const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i pl = _mm512_mullo_epi16(a.val, b.val); + __m512i ph = _mm512_mulhi_epu16(a.val, b.val); + __m512i p0 = _mm512_unpacklo_epi16(pl, ph); + __m512i p1 = _mm512_unpackhi_epi16(pl, ph); + + const __m512i m = _mm512_set1_epi32(65535); + return v_uint16x32(_mm512_packus_epi32(_mm512_min_epu32(p0, m), _mm512_min_epu32(p1, m))); +} +inline v_int16x32 operator * (const v_int16x32& a, const v_int16x32& b) +{ + __m512i pl = _mm512_mullo_epi16(a.val, b.val); + __m512i ph = _mm512_mulhi_epi16(a.val, b.val); + __m512i p0 = _mm512_unpacklo_epi16(pl, ph); + __m512i p1 = _mm512_unpackhi_epi16(pl, ph); + return v_int16x32(_mm512_packs_epi32(p0, p1)); +} + +inline v_uint8x64& operator *= (v_uint8x64& a, const v_uint8x64& b) +{ a = a * b; return a; } +inline v_int8x64& operator *= (v_int8x64& a, const v_int8x64& b) +{ a = a * b; return a; } +inline v_uint16x32& operator *= (v_uint16x32& a, const v_uint16x32& b) +{ a = a * b; return a; } +inline v_int16x32& operator *= (v_int16x32& a, const v_int16x32& b) +{ a = a * b; return a; } + +inline v_int16x32 v_mul_hi(const v_int16x32& a, const v_int16x32& b) { return v_int16x32(_mm512_mulhi_epi16(a.val, b.val)); } +inline v_uint16x32 v_mul_hi(const v_uint16x32& a, const v_uint16x32& b) { return v_uint16x32(_mm512_mulhi_epu16(a.val, b.val)); } + +// Multiply and expand +inline void v_mul_expand(const v_uint8x64& a, const v_uint8x64& b, + v_uint16x32& c, v_uint16x32& d) +{ + v_uint16x32 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x64& a, const v_int8x64& b, + v_int16x32& c, v_int16x32& d) +{ + v_int16x32 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x32& a, const v_int16x32& b, + v_int32x16& c, v_int32x16& d) +{ + v_int16x32 v0, v1; + v_zip(v_mul_wrap(a, b), v_mul_hi(a, b), v0, v1); + + c = v_reinterpret_as_s32(v0); + d = v_reinterpret_as_s32(v1); +} + +inline void v_mul_expand(const v_uint16x32& a, const v_uint16x32& b, + v_uint32x16& c, v_uint32x16& d) +{ + v_uint16x32 v0, v1; + v_zip(v_mul_wrap(a, b), v_mul_hi(a, b), v0, v1); + + c = v_reinterpret_as_u32(v0); + d = v_reinterpret_as_u32(v1); +} + +inline void v_mul_expand(const v_uint32x16& a, const v_uint32x16& b, + v_uint64x8& c, v_uint64x8& d) +{ + v_zip(v_uint64x8(_mm512_mul_epu32(a.val, b.val)), + v_uint64x8(_mm512_mul_epu32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32))), c, d); +} + +inline void v_mul_expand(const v_int32x16& a, const v_int32x16& b, + v_int64x8& c, v_int64x8& d) +{ + v_zip(v_int64x8(_mm512_mul_epi32(a.val, b.val)), + v_int64x8(_mm512_mul_epi32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32))), c, d); +} + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_AVX512_SHIFT_OP(_Tpuvec, _Tpsvec, suffix) \ + inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm512_slli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm512_slli_##suffix(a.val, imm)); } \ + inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ + { return _Tpuvec(_mm512_srli_##suffix(a.val, imm)); } \ + inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ + { return _Tpsvec(_mm512_srai_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shl(const _Tpuvec& a) \ + { return _Tpuvec(_mm512_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shl(const _Tpsvec& a) \ + { return _Tpsvec(_mm512_slli_##suffix(a.val, imm)); } \ + template \ + inline _Tpuvec v_shr(const _Tpuvec& a) \ + { return _Tpuvec(_mm512_srli_##suffix(a.val, imm)); } \ + template \ + inline _Tpsvec v_shr(const _Tpsvec& a) \ + { return _Tpsvec(_mm512_srai_##suffix(a.val, imm)); } + +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint16x32, v_int16x32, epi16) +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint32x16, v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_SHIFT_OP(v_uint64x8, v_int64x8, epi64) + + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_AVX512_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(&, _Tpvec, _mm512_and_##suffix) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(|, _Tpvec, _mm512_or_##suffix) \ + OPENCV_HAL_IMPL_AVX512_BIN_OP(^, _Tpvec, _mm512_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { return _Tpvec(_mm512_xor_##suffix(a.val, not_const)); } + +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint8x64, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int8x64, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint16x32, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int16x32, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint32x16, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int32x16, si512, _mm512_set1_epi32(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_uint64x8, si512, _mm512_set1_epi64(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_int64x8, si512, _mm512_set1_epi64(-1)) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_float32x16, ps, _mm512_castsi512_ps(_mm512_set1_epi32(-1))) +OPENCV_HAL_IMPL_AVX512_LOGIC_OP(v_float64x8, pd, _mm512_castsi512_pd(_mm512_set1_epi32(-1))) + +/** Select **/ +#define OPENCV_HAL_IMPL_AVX512_SELECT(_Tpvec, suffix, zsuf) \ + inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_mask_blend_##suffix(_mm512_cmp_##suffix##_mask(mask.val, _mm512_setzero_##zsuf(), _MM_CMPINT_EQ), a.val, b.val)); } + +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint8x64, epi8, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int8x64, epi8, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint16x32, epi16, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int16x32, epi16, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint32x16, epi32, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int32x16, epi32, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_uint64x8, epi64, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_int64x8, epi64, si512) +OPENCV_HAL_IMPL_AVX512_SELECT(v_float32x16, ps, ps) +OPENCV_HAL_IMPL_AVX512_SELECT(v_float64x8, pd, pd) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_AVX512_CMP_INT(bin_op, imm8, _Tpvec, sufcmp, sufset, tval) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_maskz_set1_##sufset(_mm512_cmp_##sufcmp##_mask(a.val, b.val, imm8), tval)); } + +#define OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(_Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(==, _MM_CMPINT_EQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(!=, _MM_CMPINT_NE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(<, _MM_CMPINT_LT, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(>, _MM_CMPINT_NLE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(<=, _MM_CMPINT_LE, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_INT(>=, _MM_CMPINT_NLT, _Tpvec, sufcmp, sufset, tval) + +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint8x64, epu8, epi8, (char)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int8x64, epi8, epi8, (char)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint16x32, epu16, epi16, (short)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int16x32, epi16, epi16, (short)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint32x16, epu32, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int32x16, epi32, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_uint64x8, epu64, epi64, (int64)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_INT(v_int64x8, epi64, epi64, (int64)-1) + +#define OPENCV_HAL_IMPL_AVX512_CMP_FLT(bin_op, imm8, _Tpvec, sufcmp, sufset, tval) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { return _Tpvec(_mm512_castsi512_##sufcmp(_mm512_maskz_set1_##sufset(_mm512_cmp_##sufcmp##_mask(a.val, b.val, imm8), tval))); } + +#define OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(_Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(==, _CMP_EQ_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(!=, _CMP_NEQ_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(<, _CMP_LT_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(>, _CMP_GT_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(<=, _CMP_LE_OQ, _Tpvec, sufcmp, sufset, tval) \ + OPENCV_HAL_IMPL_AVX512_CMP_FLT(>=, _CMP_GE_OQ, _Tpvec, sufcmp, sufset, tval) + +OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(v_float32x16, ps, epi32, (int)-1) +OPENCV_HAL_IMPL_AVX512_CMP_OP_FLT(v_float64x8, pd, epi64, (int64)-1) + +inline v_float32x16 v_not_nan(const v_float32x16& a) +{ return v_float32x16(_mm512_castsi512_ps(_mm512_maskz_set1_epi32(_mm512_cmp_ps_mask(a.val, a.val, _CMP_ORD_Q), (int)-1))); } +inline v_float64x8 v_not_nan(const v_float64x8& a) +{ return v_float64x8(_mm512_castsi512_pd(_mm512_maskz_set1_epi64(_mm512_cmp_pd_mask(a.val, a.val, _CMP_ORD_Q), (int64)-1))); } + +/** min/max **/ +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint8x64, _mm512_min_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint8x64, _mm512_max_epu8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int8x64, _mm512_min_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int8x64, _mm512_max_epi8) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint16x32, _mm512_min_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint16x32, _mm512_max_epu16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int16x32, _mm512_min_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int16x32, _mm512_max_epi16) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint32x16, _mm512_min_epu32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint32x16, _mm512_max_epu32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int32x16, _mm512_min_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int32x16, _mm512_max_epi32) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_uint64x8, _mm512_min_epu64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_uint64x8, _mm512_max_epu64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_int64x8, _mm512_min_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_int64x8, _mm512_max_epi64) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_float32x16, _mm512_min_ps) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_float32x16, _mm512_max_ps) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_min, v_float64x8, _mm512_min_pd) +OPENCV_HAL_IMPL_AVX512_BIN_FUNC(v_max, v_float64x8, _mm512_max_pd) + +/** Rotate **/ +namespace { + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64&) { return v_int8x64(); }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(b.val, a.val, imm32 ), imm4 *8), + _mm512_slli_epi32(_mm512_alignr_epi32(b.val, a.val, imm32 + 1), (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(b.val, a.val, 15), imm4 *8), + _mm512_slli_epi32( b.val, (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { + return v_int8x64(_mm512_or_si512(_mm512_srli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 16), imm4 *8), + _mm512_slli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 15), (4-imm4)*8))); + }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { return v_int8x64(_mm512_srli_epi32(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, 15), imm4*8)); }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64& b) + { return v_int8x64(_mm512_alignr_epi32(b.val, a.val, imm32)); }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64& a, const v_int8x64&) { return a; }}; + template + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) + { return v_int8x64(_mm512_alignr_epi32(_mm512_setzero_si512(), b.val, imm32 - 16)); }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64& b) { return b; }}; + template<> + struct _v_rotate_right { static inline v_int8x64 eval(const v_int8x64&, const v_int8x64&) { return v_int8x64(); }}; +} +template inline v_int8x64 v_rotate_right(const v_int8x64& a, const v_int8x64& b) +{ + return imm >= 128 ? v_int8x64() : +#if CV_AVX_512VBMI + v_int8x64(_mm512_permutex2var_epi8(a.val, + _v512_set_epu8(0x3f + imm, 0x3e + imm, 0x3d + imm, 0x3c + imm, 0x3b + imm, 0x3a + imm, 0x39 + imm, 0x38 + imm, + 0x37 + imm, 0x36 + imm, 0x35 + imm, 0x34 + imm, 0x33 + imm, 0x32 + imm, 0x31 + imm, 0x30 + imm, + 0x2f + imm, 0x2e + imm, 0x2d + imm, 0x2c + imm, 0x2b + imm, 0x2a + imm, 0x29 + imm, 0x28 + imm, + 0x27 + imm, 0x26 + imm, 0x25 + imm, 0x24 + imm, 0x23 + imm, 0x22 + imm, 0x21 + imm, 0x20 + imm, + 0x1f + imm, 0x1e + imm, 0x1d + imm, 0x1c + imm, 0x1b + imm, 0x1a + imm, 0x19 + imm, 0x18 + imm, + 0x17 + imm, 0x16 + imm, 0x15 + imm, 0x14 + imm, 0x13 + imm, 0x12 + imm, 0x11 + imm, 0x10 + imm, + 0x0f + imm, 0x0e + imm, 0x0d + imm, 0x0c + imm, 0x0b + imm, 0x0a + imm, 0x09 + imm, 0x08 + imm, + 0x07 + imm, 0x06 + imm, 0x05 + imm, 0x04 + imm, 0x03 + imm, 0x02 + imm, 0x01 + imm, 0x00 + imm), b.val)); +#else + _v_rotate_right 15), imm/4>::eval(a, b); +#endif +} +template +inline v_int8x64 v_rotate_left(const v_int8x64& a, const v_int8x64& b) +{ + if (imm == 0) return a; + if (imm == 64) return b; + if (imm >= 128) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_permutex2var_epi8(b.val, + _v512_set_epi8(0x7f - imm,0x7e - imm,0x7d - imm,0x7c - imm,0x7b - imm,0x7a - imm,0x79 - imm,0x78 - imm, + 0x77 - imm,0x76 - imm,0x75 - imm,0x74 - imm,0x73 - imm,0x72 - imm,0x71 - imm,0x70 - imm, + 0x6f - imm,0x6e - imm,0x6d - imm,0x6c - imm,0x6b - imm,0x6a - imm,0x69 - imm,0x68 - imm, + 0x67 - imm,0x66 - imm,0x65 - imm,0x64 - imm,0x63 - imm,0x62 - imm,0x61 - imm,0x60 - imm, + 0x5f - imm,0x5e - imm,0x5d - imm,0x5c - imm,0x5b - imm,0x5a - imm,0x59 - imm,0x58 - imm, + 0x57 - imm,0x56 - imm,0x55 - imm,0x54 - imm,0x53 - imm,0x52 - imm,0x51 - imm,0x50 - imm, + 0x4f - imm,0x4e - imm,0x4d - imm,0x4c - imm,0x4b - imm,0x4a - imm,0x49 - imm,0x48 - imm, + 0x47 - imm,0x46 - imm,0x45 - imm,0x44 - imm,0x43 - imm,0x42 - imm,0x41 - imm,0x40 - imm), a.val)); +#else + return imm < 64 ? v_rotate_right<64 - imm>(b, a) : v_rotate_right<128 - imm>(v512_setzero_s8(), b); +#endif +} +template +inline v_int8x64 v_rotate_right(const v_int8x64& a) +{ + if (imm == 0) return a; + if (imm >= 64) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_maskz_permutexvar_epi8(0xFFFFFFFFFFFFFFFF >> imm, + _v512_set_epu8(0x3f + imm,0x3e + imm,0x3d + imm,0x3c + imm,0x3b + imm,0x3a + imm,0x39 + imm,0x38 + imm, + 0x37 + imm,0x36 + imm,0x35 + imm,0x34 + imm,0x33 + imm,0x32 + imm,0x31 + imm,0x30 + imm, + 0x2f + imm,0x2e + imm,0x2d + imm,0x2c + imm,0x2b + imm,0x2a + imm,0x29 + imm,0x28 + imm, + 0x27 + imm,0x26 + imm,0x25 + imm,0x24 + imm,0x23 + imm,0x22 + imm,0x21 + imm,0x20 + imm, + 0x1f + imm,0x1e + imm,0x1d + imm,0x1c + imm,0x1b + imm,0x1a + imm,0x19 + imm,0x18 + imm, + 0x17 + imm,0x16 + imm,0x15 + imm,0x14 + imm,0x13 + imm,0x12 + imm,0x11 + imm,0x10 + imm, + 0x0f + imm,0x0e + imm,0x0d + imm,0x0c + imm,0x0b + imm,0x0a + imm,0x09 + imm,0x08 + imm, + 0x07 + imm,0x06 + imm,0x05 + imm,0x04 + imm,0x03 + imm,0x02 + imm,0x01 + imm,0x00 + imm), a.val)); +#else + return v_rotate_right(a, v512_setzero_s8()); +#endif +} +template +inline v_int8x64 v_rotate_left(const v_int8x64& a) +{ + if (imm == 0) return a; + if (imm >= 64) return v_int8x64(); +#if CV_AVX_512VBMI + return v_int8x64(_mm512_maskz_permutexvar_epi8(0xFFFFFFFFFFFFFFFF << imm, + _v512_set_epi8(0x3f - imm,0x3e - imm,0x3d - imm,0x3c - imm,0x3b - imm,0x3a - imm,0x39 - imm,0x38 - imm, + 0x37 - imm,0x36 - imm,0x35 - imm,0x34 - imm,0x33 - imm,0x32 - imm,0x31 - imm,0x30 - imm, + 0x2f - imm,0x2e - imm,0x2d - imm,0x2c - imm,0x2b - imm,0x2a - imm,0x29 - imm,0x28 - imm, + 0x27 - imm,0x26 - imm,0x25 - imm,0x24 - imm,0x23 - imm,0x22 - imm,0x21 - imm,0x20 - imm, + 0x1f - imm,0x1e - imm,0x1d - imm,0x1c - imm,0x1b - imm,0x1a - imm,0x19 - imm,0x18 - imm, + 0x17 - imm,0x16 - imm,0x15 - imm,0x14 - imm,0x13 - imm,0x12 - imm,0x11 - imm,0x10 - imm, + 0x0f - imm,0x0e - imm,0x0d - imm,0x0c - imm,0x0b - imm,0x0a - imm,0x09 - imm,0x08 - imm, + 0x07 - imm,0x06 - imm,0x05 - imm,0x04 - imm,0x03 - imm,0x02 - imm,0x01 - imm,0x00 - imm), a.val)); +#else + return v_rotate_right<64 - imm>(v512_setzero_s8(), a); +#endif +} + +#define OPENCV_HAL_IMPL_AVX512_ROTATE_PM(_Tpvec, suffix) \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ return v_reinterpret_as_##suffix(v_rotate_left(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b))); } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ return v_reinterpret_as_##suffix(v_rotate_right(v_reinterpret_as_s8(a), v_reinterpret_as_s8(b))); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ return v_reinterpret_as_##suffix(v_rotate_left(v_reinterpret_as_s8(a))); } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ return v_reinterpret_as_##suffix(v_rotate_right(v_reinterpret_as_s8(a))); } + +#define OPENCV_HAL_IMPL_AVX512_ROTATE_EC(_Tpvec, suffix) \ +template \ +inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + enum { SHIFT2 = (_Tpvec::nlanes - imm) }; \ + enum { MASK = ((1 << _Tpvec::nlanes) - 1) }; \ + if (imm == 0) return a; \ + if (imm == _Tpvec::nlanes) return b; \ + if (imm >= 2*_Tpvec::nlanes) return _Tpvec::zero(); \ + return _Tpvec(_mm512_mask_expand_##suffix(_mm512_maskz_compress_##suffix((MASK << SHIFT2)&MASK, b.val), (MASK << (imm))&MASK, a.val)); \ +} \ +template \ +inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + enum { SHIFT2 = (_Tpvec::nlanes - imm) }; \ + enum { MASK = ((1 << _Tpvec::nlanes) - 1) }; \ + if (imm == 0) return a; \ + if (imm == _Tpvec::nlanes) return b; \ + if (imm >= 2*_Tpvec::nlanes) return _Tpvec::zero(); \ + return _Tpvec(_mm512_mask_expand_##suffix(_mm512_maskz_compress_##suffix((MASK << (imm))&MASK, a.val), (MASK << SHIFT2)&MASK, b.val)); \ +} \ +template \ +inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + if (imm == 0) return a; \ + if (imm >= _Tpvec::nlanes) return _Tpvec::zero(); \ + return _Tpvec(_mm512_maskz_expand_##suffix((1 << _Tpvec::nlanes) - (1 << (imm)), a.val)); \ +} \ +template \ +inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + if (imm == 0) return a; \ + if (imm >= _Tpvec::nlanes) return _Tpvec::zero(); \ + return _Tpvec(_mm512_maskz_compress_##suffix((1 << _Tpvec::nlanes) - (1 << (imm)), a.val)); \ +} + +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_uint8x64, u8) +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_uint16x32, u16) +OPENCV_HAL_IMPL_AVX512_ROTATE_PM(v_int16x32, s16) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_int32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_uint64x8, epi64) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_int64x8, epi64) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_ROTATE_EC(v_float64x8, pd) + +/** Reverse **/ +inline v_uint8x64 v_reverse(const v_uint8x64 &a) +{ +#if CV_AVX_512VBMI + static const __m512i perm = _mm512_set_epi32( + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f, + 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f, + 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f); + return v_uint8x64(_mm512_permutexvar_epi8(perm, a.val)); +#else + static const __m512i shuf = _mm512_set_epi32( + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f); + static const __m512i perm = _mm512_set_epi64(1, 0, 3, 2, 5, 4, 7, 6); + __m512i vec = _mm512_shuffle_epi8(a.val, shuf); + return v_uint8x64(_mm512_permutexvar_epi64(perm, vec)); +#endif +} + +inline v_int8x64 v_reverse(const v_int8x64 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x32 v_reverse(const v_uint16x32 &a) +{ +#if CV_AVX_512VBMI + static const __m512i perm = _mm512_set_epi32( + 0x00000001, 0x00020003, 0x00040005, 0x00060007, + 0x00080009, 0x000a000b, 0x000c000d, 0x000e000f, + 0x00100011, 0x00120013, 0x00140015, 0x00160017, + 0x00180019, 0x001a001b, 0x001c001d, 0x001e001f); + return v_uint16x32(_mm512_permutexvar_epi16(perm, a.val)); +#else + static const __m512i shuf = _mm512_set_epi32( + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e, + 0x01000302, 0x05040706, 0x09080b0a, 0x0d0c0f0e); + static const __m512i perm = _mm512_set_epi64(1, 0, 3, 2, 5, 4, 7, 6); + __m512i vec = _mm512_shuffle_epi8(a.val, shuf); + return v_uint16x32(_mm512_permutexvar_epi64(perm, vec)); +#endif +} + +inline v_int16x32 v_reverse(const v_int16x32 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x16 v_reverse(const v_uint32x16 &a) +{ + static const __m512i perm = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15); + return v_uint32x16(_mm512_permutexvar_epi32(perm, a.val)); +} + +inline v_int32x16 v_reverse(const v_int32x16 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x16 v_reverse(const v_float32x16 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x8 v_reverse(const v_uint64x8 &a) +{ + static const __m512i perm = _mm512_set_epi64(0, 1, 2, 3, 4, 5, 6, 7); + return v_uint64x8(_mm512_permutexvar_epi64(perm, a.val)); +} + +inline v_int64x8 v_reverse(const v_int64x8 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x8 v_reverse(const v_float64x8 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +////////// Reduce ///////// + +/** Reduce **/ +#define OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64(a, b) a + b +#define OPENCV_HAL_IMPL_AVX512_REDUCE_8(sctype, func, _Tpvec, ifunc, scop) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + sctype CV_DECL_ALIGNED(64) idx[2]; \ + _mm_store_si128((__m128i*)idx, _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1))); \ + return scop(idx[0], idx[1]); } +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, min, v_uint64x8, min_epu64, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, max, v_uint64x8, max_epu64, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(uint64, sum, v_uint64x8, add_epi64, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, min, v_int64x8, min_epi64, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, max, v_int64x8, max_epi64, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8(int64, sum, v_int64x8, add_epi64, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_8F(func, ifunc, scop) \ + inline double v_reduce_##func(const v_float64x8& a) \ + { __m256d half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + double CV_DECL_ALIGNED(64) idx[2]; \ + _mm_store_pd(idx, _mm_##ifunc(_mm256_castpd256_pd128(half), _mm256_extractf128_pd(half, 1))); \ + return scop(idx[0], idx[1]); } +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(min, min_pd, min) +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(max, max_pd, max) +OPENCV_HAL_IMPL_AVX512_REDUCE_8F(sum, add_pd, OPENCV_HAL_IMPL_AVX512_REDUCE_ADD64) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_16(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_16(uint, min, v_uint32x16, min_epu32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(uint, max, v_uint32x16, max_epu32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(int, min, v_int32x16, min_epi32) +OPENCV_HAL_IMPL_AVX512_REDUCE_16(int, max, v_int32x16, max_epi32) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_16F(func, ifunc) \ + inline float v_reduce_##func(const v_float32x16& a) \ + { __m256 half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128 quarter = _mm_##ifunc(_mm256_castps256_ps128(half), _mm256_extractf128_ps(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_permute_ps(quarter, _MM_SHUFFLE(0, 0, 3, 2))); \ + quarter = _mm_##ifunc(quarter, _mm_permute_ps(quarter, _MM_SHUFFLE(0, 0, 0, 1))); \ + return _mm_cvtss_f32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_16F(min, min_ps) +OPENCV_HAL_IMPL_AVX512_REDUCE_16F(max, max_ps) + +inline float v_reduce_sum(const v_float32x16& a) +{ + __m256 half = _mm256_add_ps(_v512_extract_low(a.val), _v512_extract_high(a.val)); + __m128 quarter = _mm_add_ps(_mm256_castps256_ps128(half), _mm256_extractf128_ps(half, 1)); + quarter = _mm_hadd_ps(quarter, quarter); + return _mm_cvtss_f32(_mm_hadd_ps(quarter, quarter)); +} +inline int v_reduce_sum(const v_int32x16& a) +{ + __m256i half = _mm256_add_epi32(_v512_extract_low(a.val), _v512_extract_high(a.val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + quarter = _mm_hadd_epi32(quarter, quarter); + return _mm_cvtsi128_si32(_mm_hadd_epi32(quarter, quarter)); +} +inline uint v_reduce_sum(const v_uint32x16& a) +{ return (uint)v_reduce_sum(v_reinterpret_as_s32(a)); } + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_32(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 2)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_32(ushort, min, v_uint16x32, min_epu16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(ushort, max, v_uint16x32, max_epu16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(short, min, v_int16x32, min_epi16) +OPENCV_HAL_IMPL_AVX512_REDUCE_32(short, max, v_int16x32, max_epi16) + +inline int v_reduce_sum(const v_int16x32& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline uint v_reduce_sum(const v_uint16x32& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_64(sctype, func, _Tpvec, ifunc) \ + inline sctype v_reduce_##func(const _Tpvec& a) \ + { __m256i half = _mm256_##ifunc(_v512_extract_low(a.val), _v512_extract_high(a.val)); \ + __m128i quarter = _mm_##ifunc(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 8)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 4)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 2)); \ + quarter = _mm_##ifunc(quarter, _mm_srli_si128(quarter, 1)); \ + return (sctype)_mm_cvtsi128_si32(quarter); } +OPENCV_HAL_IMPL_AVX512_REDUCE_64(uchar, min, v_uint8x64, min_epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(uchar, max, v_uint8x64, max_epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(schar, min, v_int8x64, min_epi8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64(schar, max, v_int8x64, max_epi8) + +#define OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(sctype, _Tpvec, suffix) \ + inline sctype v_reduce_sum(const _Tpvec& a) \ + { __m512i a16 = _mm512_add_epi16(_mm512_cvt##suffix##_epi16(_v512_extract_low(a.val)), \ + _mm512_cvt##suffix##_epi16(_v512_extract_high(a.val))); \ + a16 = _mm512_cvtepi16_epi32(_mm256_add_epi16(_v512_extract_low(a16), _v512_extract_high(a16))); \ + __m256i a8 = _mm256_add_epi32(_v512_extract_low(a16), _v512_extract_high(a16)); \ + __m128i a4 = _mm_add_epi32(_mm256_castsi256_si128(a8), _mm256_extracti128_si256(a8, 1)); \ + a4 = _mm_hadd_epi32(a4, a4); \ + return (sctype)_mm_cvtsi128_si32(_mm_hadd_epi32(a4, a4)); } +OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(uint, v_uint8x64, epu8) +OPENCV_HAL_IMPL_AVX512_REDUCE_64_SUM(int, v_int8x64, epi8) + +inline v_float32x16 v_reduce_sum4(const v_float32x16& a, const v_float32x16& b, + const v_float32x16& c, const v_float32x16& d) +{ + __m256 abl = _mm256_hadd_ps(_v512_extract_low(a.val), _v512_extract_low(b.val)); + __m256 abh = _mm256_hadd_ps(_v512_extract_high(a.val), _v512_extract_high(b.val)); + __m256 cdl = _mm256_hadd_ps(_v512_extract_low(c.val), _v512_extract_low(d.val)); + __m256 cdh = _mm256_hadd_ps(_v512_extract_high(c.val), _v512_extract_high(d.val)); + return v_float32x16(_v512_combine(_mm256_hadd_ps(abl, cdl), _mm256_hadd_ps(abh, cdh))); +} + +inline unsigned v_reduce_sad(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i val = _mm512_sad_epu8(a.val, b.val); + __m256i half = _mm256_add_epi32(_v512_extract_low(val), _v512_extract_high(val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_int8x64& a, const v_int8x64& b) +{ + __m512i val = _mm512_set1_epi8(-128); + val = _mm512_sad_epu8(_mm512_add_epi8(a.val, val), _mm512_add_epi8(b.val, val)); + __m256i half = _mm256_add_epi32(_v512_extract_low(val), _v512_extract_high(val)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(quarter, _mm_unpackhi_epi64(quarter, quarter))); +} +inline unsigned v_reduce_sad(const v_uint16x32& a, const v_uint16x32& b) +{ return v_reduce_sum(v_add_wrap(a - b, b - a)); } +inline unsigned v_reduce_sad(const v_int16x32& a, const v_int16x32& b) +{ return v_reduce_sum(v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b)))); } +inline unsigned v_reduce_sad(const v_uint32x16& a, const v_uint32x16& b) +{ return v_reduce_sum(v_max(a, b) - v_min(a, b)); } +inline unsigned v_reduce_sad(const v_int32x16& a, const v_int32x16& b) +{ return v_reduce_sum(v_reinterpret_as_u32(v_max(a, b) - v_min(a, b))); } +inline float v_reduce_sad(const v_float32x16& a, const v_float32x16& b) +{ return v_reduce_sum((a - b) & v_float32x16(_mm512_castsi512_ps(_mm512_set1_epi32(0x7fffffff)))); } +inline double v_reduce_sad(const v_float64x8& a, const v_float64x8& b) +{ return v_reduce_sum((a - b) & v_float64x8(_mm512_castsi512_pd(_mm512_set1_epi64(0x7fffffffffffffff)))); } + +/** Popcount **/ +inline v_uint8x64 v_popcount(const v_int8x64& a) +{ +#if CV_AVX_512BITALG + return v_uint8x64(_mm512_popcnt_epi8(a.val)); +#elif CV_AVX_512VBMI + __m512i _popcnt_table0 = _v512_set_epu8(7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, + 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0); + __m512i _popcnt_table1 = _v512_set_epu8(7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, + 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, + 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, + 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1); + return v_uint8x64(_mm512_sub_epi8(_mm512_permutex2var_epi8(_popcnt_table0, a.val, _popcnt_table1), _mm512_movm_epi8(_mm512_movepi8_mask(a.val)))); +#else + __m512i _popcnt_table = _mm512_set4_epi32(0x04030302, 0x03020201, 0x03020201, 0x02010100); + __m512i _popcnt_mask = _mm512_set1_epi8(0x0F); + + return v_uint8x64(_mm512_add_epi8(_mm512_shuffle_epi8(_popcnt_table, _mm512_and_si512( a.val, _popcnt_mask)), + _mm512_shuffle_epi8(_popcnt_table, _mm512_and_si512(_mm512_srli_epi16(a.val, 4), _popcnt_mask)))); +#endif +} +inline v_uint16x32 v_popcount(const v_int16x32& a) +{ +#if CV_AVX_512BITALG + return v_uint16x32(_mm512_popcnt_epi16(a.val)); +#elif CV_AVX_512VPOPCNTDQ + __m512i zero = _mm512_setzero_si512(); + return v_uint16x32(_mm512_packs_epi32(_mm512_popcnt_epi32(_mm512_unpacklo_epi16(a.val, zero)), + _mm512_popcnt_epi32(_mm512_unpackhi_epi16(a.val, zero)))); +#else + v_uint8x64 p = v_popcount(v_reinterpret_as_s8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v512_setall_u16(0x00ff); +#endif +} +inline v_uint32x16 v_popcount(const v_int32x16& a) +{ +#if CV_AVX_512VPOPCNTDQ + return v_uint32x16(_mm512_popcnt_epi32(a.val)); +#else + v_uint8x64 p = v_popcount(v_reinterpret_as_s8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v512_setall_u32(0x000000ff); +#endif +} +inline v_uint64x8 v_popcount(const v_int64x8& a) +{ +#if CV_AVX_512VPOPCNTDQ + return v_uint64x8(_mm512_popcnt_epi64(a.val)); +#else + return v_uint64x8(_mm512_sad_epu8(v_popcount(v_reinterpret_as_s8(a)).val, _mm512_setzero_si512())); +#endif +} + + +inline v_uint8x64 v_popcount(const v_uint8x64& a) { return v_popcount(v_reinterpret_as_s8 (a)); } +inline v_uint16x32 v_popcount(const v_uint16x32& a) { return v_popcount(v_reinterpret_as_s16(a)); } +inline v_uint32x16 v_popcount(const v_uint32x16& a) { return v_popcount(v_reinterpret_as_s32(a)); } +inline v_uint64x8 v_popcount(const v_uint64x8& a) { return v_popcount(v_reinterpret_as_s64(a)); } + + +////////// Other math ///////// + +/** Some frequent operations **/ +#define OPENCV_HAL_IMPL_AVX512_MULADD(_Tpvec, suffix) \ + inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm512_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ + { return _Tpvec(_mm512_fmadd_##suffix(a.val, b.val, c.val)); } \ + inline _Tpvec v_sqrt(const _Tpvec& x) \ + { return _Tpvec(_mm512_sqrt_##suffix(x.val)); } \ + inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_fma(a, a, b * b); } \ + inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ + { return v_sqrt(v_fma(a, a, b * b)); } + +OPENCV_HAL_IMPL_AVX512_MULADD(v_float32x16, ps) +OPENCV_HAL_IMPL_AVX512_MULADD(v_float64x8, pd) + +inline v_int32x16 v_fma(const v_int32x16& a, const v_int32x16& b, const v_int32x16& c) +{ return a * b + c; } +inline v_int32x16 v_muladd(const v_int32x16& a, const v_int32x16& b, const v_int32x16& c) +{ return v_fma(a, b, c); } + +inline v_float32x16 v_invsqrt(const v_float32x16& x) +{ +#if CV_AVX_512ER + return v_float32x16(_mm512_rsqrt28_ps(x.val)); +#else + v_float32x16 half = x * v512_setall_f32(0.5); + v_float32x16 t = v_float32x16(_mm512_rsqrt14_ps(x.val)); + t *= v512_setall_f32(1.5) - ((t * t) * half); + return t; +#endif +} + +inline v_float64x8 v_invsqrt(const v_float64x8& x) +{ +#if CV_AVX_512ER + return v_float64x8(_mm512_rsqrt28_pd(x.val)); +#else + return v512_setall_f64(1.) / v_sqrt(x); +// v_float64x8 half = x * v512_setall_f64(0.5); +// v_float64x8 t = v_float64x8(_mm512_rsqrt14_pd(x.val)); +// t *= v512_setall_f64(1.5) - ((t * t) * half); +// t *= v512_setall_f64(1.5) - ((t * t) * half); +// return t; +#endif +} + +/** Absolute values **/ +#define OPENCV_HAL_IMPL_AVX512_ABS(_Tpvec, _Tpuvec, suffix) \ + inline _Tpuvec v_abs(const _Tpvec& x) \ + { return _Tpuvec(_mm512_abs_##suffix(x.val)); } + +OPENCV_HAL_IMPL_AVX512_ABS(v_int8x64, v_uint8x64, epi8) +OPENCV_HAL_IMPL_AVX512_ABS(v_int16x32, v_uint16x32, epi16) +OPENCV_HAL_IMPL_AVX512_ABS(v_int32x16, v_uint32x16, epi32) +OPENCV_HAL_IMPL_AVX512_ABS(v_int64x8, v_uint64x8, epi64) + +inline v_float32x16 v_abs(const v_float32x16& x) +{ +#ifdef _mm512_abs_pd + return v_float32x16(_mm512_abs_ps(x.val)); +#else + return v_float32x16(_mm512_castsi512_ps(_mm512_and_si512(_mm512_castps_si512(x.val), + _v512_set_epu64(0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, + 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF, 0x7FFFFFFF7FFFFFFF)))); +#endif +} + +inline v_float64x8 v_abs(const v_float64x8& x) +{ +#ifdef _mm512_abs_pd + #if defined __GNUC__ && (__GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ <= 3) || (__GNUC__ == 8 && __GNUC_MINOR__ <= 2)) + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87476 + return v_float64x8(_mm512_abs_pd(_mm512_castpd_ps(x.val))); + #else + return v_float64x8(_mm512_abs_pd(x.val)); + #endif +#else + return v_float64x8(_mm512_castsi512_pd(_mm512_and_si512(_mm512_castpd_si512(x.val), + _v512_set_epu64(0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF)))); +#endif +} + +/** Absolute difference **/ +inline v_uint8x64 v_absdiff(const v_uint8x64& a, const v_uint8x64& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x32 v_absdiff(const v_uint16x32& a, const v_uint16x32& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x16 v_absdiff(const v_uint32x16& a, const v_uint32x16& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x64 v_absdiff(const v_int8x64& a, const v_int8x64& b) +{ + v_int8x64 d = v_sub_wrap(a, b); + v_int8x64 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} + +inline v_uint16x32 v_absdiff(const v_int16x32& a, const v_int16x32& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } + +inline v_uint32x16 v_absdiff(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 d = a - b; + v_int32x16 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +inline v_float32x16 v_absdiff(const v_float32x16& a, const v_float32x16& b) +{ return v_abs(a - b); } + +inline v_float64x8 v_absdiff(const v_float64x8& a, const v_float64x8& b) +{ return v_abs(a - b); } + +/** Saturating absolute difference **/ +inline v_int8x64 v_absdiffs(const v_int8x64& a, const v_int8x64& b) +{ + v_int8x64 d = a - b; + v_int8x64 m = a < b; + return (d ^ m) - m; +} +inline v_int16x32 v_absdiffs(const v_int16x32& a, const v_int16x32& b) +{ return v_max(a, b) - v_min(a, b); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x16 v_round(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(a.val)); } + +inline v_int32x16 v_round(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(a.val))); } + +inline v_int32x16 v_round(const v_float64x8& a, const v_float64x8& b) +{ return v_int32x16(_v512_combine(_mm512_cvtpd_epi32(a.val), _mm512_cvtpd_epi32(b.val))); } + +inline v_int32x16 v_trunc(const v_float32x16& a) +{ return v_int32x16(_mm512_cvttps_epi32(a.val)); } + +inline v_int32x16 v_trunc(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvttpd_epi32(a.val))); } + +#if CVT_ROUND_MODES_IMPLEMENTED +inline v_int32x16 v_floor(const v_float32x16& a) +{ return v_int32x16(_mm512_cvt_roundps_epi32(a.val, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC)); } + +inline v_int32x16 v_floor(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvt_roundpd_epi32(a.val, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC))); } + +inline v_int32x16 v_ceil(const v_float32x16& a) +{ return v_int32x16(_mm512_cvt_roundps_epi32(a.val, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC)); } + +inline v_int32x16 v_ceil(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvt_roundpd_epi32(a.val, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC))); } +#else +inline v_int32x16 v_floor(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(_mm512_roundscale_ps(a.val, 1))); } + +inline v_int32x16 v_floor(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(_mm512_roundscale_pd(a.val, 1)))); } + +inline v_int32x16 v_ceil(const v_float32x16& a) +{ return v_int32x16(_mm512_cvtps_epi32(_mm512_roundscale_ps(a.val, 2))); } + +inline v_int32x16 v_ceil(const v_float64x8& a) +{ return v_int32x16(_mm512_castsi256_si512(_mm512_cvtpd_epi32(_mm512_roundscale_pd(a.val, 2)))); } +#endif + +/** To float **/ +inline v_float32x16 v_cvt_f32(const v_int32x16& a) +{ return v_float32x16(_mm512_cvtepi32_ps(a.val)); } + +inline v_float32x16 v_cvt_f32(const v_float64x8& a) +{ return v_float32x16(_mm512_cvtpd_pslo(a.val)); } + +inline v_float32x16 v_cvt_f32(const v_float64x8& a, const v_float64x8& b) +{ return v_float32x16(_v512_combine(_mm512_cvtpd_ps(a.val), _mm512_cvtpd_ps(b.val))); } + +inline v_float64x8 v_cvt_f64(const v_int32x16& a) +{ return v_float64x8(_mm512_cvtepi32_pd(_v512_extract_low(a.val))); } + +inline v_float64x8 v_cvt_f64_high(const v_int32x16& a) +{ return v_float64x8(_mm512_cvtepi32_pd(_v512_extract_high(a.val))); } + +inline v_float64x8 v_cvt_f64(const v_float32x16& a) +{ return v_float64x8(_mm512_cvtps_pd(_v512_extract_low(a.val))); } + +inline v_float64x8 v_cvt_f64_high(const v_float32x16& a) +{ return v_float64x8(_mm512_cvtps_pd(_v512_extract_high(a.val))); } + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x8 v_cvt_f64(const v_int64x8& v) +{ +#if CV_AVX_512DQ + return v_float64x8(_mm512_cvtepi64_pd(v.val)); +#else + // constants encoded as floating-point + __m512i magic_i_lo = _mm512_set1_epi64(0x4330000000000000); // 2^52 + __m512i magic_i_hi32 = _mm512_set1_epi64(0x4530000080000000); // 2^84 + 2^63 + __m512i magic_i_all = _mm512_set1_epi64(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m512d magic_d_all = _mm512_castsi512_pd(magic_i_all); + + // Blend the 32 lowest significant bits of v with magic_int_lo + __m512i v_lo = _mm512_mask_blend_epi32(0x5555, magic_i_lo, v.val); + // Extract the 32 most significant bits of v + __m512i v_hi = _mm512_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm512_xor_si512(v_hi, magic_i_hi32); + // Compute in double precision + __m512d v_hi_dbl = _mm512_sub_pd(_mm512_castsi512_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m512d result = _mm512_add_pd(v_hi_dbl, _mm512_castsi512_pd(v_lo)); + return v_float64x8(result); +#endif +} + +////////////// Lookup table access //////////////////// + +inline v_int8x64 v512_lut(const schar* tab, const int* idx) +{ + __m128i p0 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 1)); + __m128i p1 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 1)); + __m128i p2 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 2), (const int *)tab, 1)); + __m128i p3 = _mm512_cvtepi32_epi8(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 3), (const int *)tab, 1)); + return v_int8x64(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512(p0), p1, 1), p2, 2), p3, 3)); +} +inline v_int8x64 v512_lut_pairs(const schar* tab, const int* idx) +{ + __m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 1)); + __m256i p1 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 1)); + return v_int8x64(_v512_combine(p0, p1)); +} +inline v_int8x64 v512_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x64(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), (const int *)tab, 1)); +} +inline v_uint8x64 v512_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut((const schar *)tab, idx)); } +inline v_uint8x64 v512_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x64 v512_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_quads((const schar *)tab, idx)); } + +inline v_int16x32 v512_lut(const short* tab, const int* idx) +{ + __m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 2)); + __m256i p1 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx + 1), (const int *)tab, 2)); + return v_int16x32(_v512_combine(p0, p1)); +} +inline v_int16x32 v512_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x32(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), (const int *)tab, 2)); +} +inline v_int16x32 v512_lut_quads(const short* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int16x32(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 2)); +#else + return v_int16x32(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const int64*)tab, 2)); +#endif +} +inline v_uint16x32 v512_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut((const short *)tab, idx)); } +inline v_uint16x32 v512_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut_pairs((const short *)tab, idx)); } +inline v_uint16x32 v512_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v512_lut_quads((const short *)tab, idx)); } + +inline v_int32x16 v512_lut(const int* tab, const int* idx) +{ + return v_int32x16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx), tab, 4)); +} +inline v_int32x16 v512_lut_pairs(const int* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int32x16(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 4)); +#else + return v_int32x16(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const int64*)tab, 4)); +#endif +} +inline v_int32x16 v512_lut_quads(const int* tab, const int* idx) +{ + return v_int32x16(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512( + _mm_loadu_si128((const __m128i*)(tab + idx[0]))), + _mm_loadu_si128((const __m128i*)(tab + idx[1])), 1), + _mm_loadu_si128((const __m128i*)(tab + idx[2])), 2), + _mm_loadu_si128((const __m128i*)(tab + idx[3])), 3)); +} +inline v_uint32x16 v512_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut((const int *)tab, idx)); } +inline v_uint32x16 v512_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut_pairs((const int *)tab, idx)); } +inline v_uint32x16 v512_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v512_lut_quads((const int *)tab, idx)); } + +inline v_int64x8 v512_lut(const int64* tab, const int* idx) +{ +#if defined(__GNUC__) + return v_int64x8(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), (const long long int*)tab, 8)); +#else + return v_int64x8(_mm512_i32gather_epi64(_mm256_loadu_si256((const __m256i*)idx), tab , 8)); +#endif +} +inline v_int64x8 v512_lut_pairs(const int64* tab, const int* idx) +{ + return v_int64x8(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_inserti32x4(_mm512_castsi128_si512( + _mm_loadu_si128((const __m128i*)(tab + idx[0]))), + _mm_loadu_si128((const __m128i*)(tab + idx[1])), 1), + _mm_loadu_si128((const __m128i*)(tab + idx[2])), 2), + _mm_loadu_si128((const __m128i*)(tab + idx[3])), 3)); +} +inline v_uint64x8 v512_lut(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v512_lut((const int64 *)tab, idx)); } +inline v_uint64x8 v512_lut_pairs(const uint64* tab, const int* idx) { return v_reinterpret_as_u64(v512_lut_pairs((const int64 *)tab, idx)); } + +inline v_float32x16 v512_lut(const float* tab, const int* idx) +{ + return v_float32x16(_mm512_i32gather_ps(_mm512_loadu_si512((const __m512i*)idx), tab, 4)); +} +inline v_float32x16 v512_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v512_lut_pairs((const int *)tab, idx)); } +inline v_float32x16 v512_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v512_lut_quads((const int *)tab, idx)); } + +inline v_float64x8 v512_lut(const double* tab, const int* idx) +{ + return v_float64x8(_mm512_i32gather_pd(_mm256_loadu_si256((const __m256i*)idx), tab, 8)); +} +inline v_float64x8 v512_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x8(_mm512_insertf64x2(_mm512_insertf64x2(_mm512_insertf64x2(_mm512_castpd128_pd512( + _mm_loadu_pd(tab + idx[0])), + _mm_loadu_pd(tab + idx[1]), 1), + _mm_loadu_pd(tab + idx[2]), 2), + _mm_loadu_pd(tab + idx[3]), 3)); +} + +inline v_int32x16 v_lut(const int* tab, const v_int32x16& idxvec) +{ + return v_int32x16(_mm512_i32gather_epi32(idxvec.val, tab, 4)); +} + +inline v_uint32x16 v_lut(const unsigned* tab, const v_int32x16& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x16 v_lut(const float* tab, const v_int32x16& idxvec) +{ + return v_float32x16(_mm512_i32gather_ps(idxvec.val, tab, 4)); +} + +inline v_float64x8 v_lut(const double* tab, const v_int32x16& idxvec) +{ + return v_float64x8(_mm512_i32gather_pd(_v512_extract_low(idxvec.val), tab, 8)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x16& idxvec, v_float32x16& x, v_float32x16& y) +{ + x.val = _mm512_i32gather_ps(idxvec.val, tab, 4); + y.val = _mm512_i32gather_ps(idxvec.val, &tab[1], 4); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x16& idxvec, v_float64x8& x, v_float64x8& y) +{ + x.val = _mm512_i32gather_pd(_v512_extract_low(idxvec.val), tab, 8); + y.val = _mm512_i32gather_pd(_v512_extract_low(idxvec.val), &tab[1], 8); +} + +inline v_int8x64 v_interleave_pairs(const v_int8x64& vec) +{ + return v_int8x64(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0d0e0c, 0x0b090a08, 0x07050604, 0x03010200))); +} +inline v_uint8x64 v_interleave_pairs(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x64 v_interleave_quads(const v_int8x64& vec) +{ + return v_int8x64(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0b0e0a, 0x0d090c08, 0x07030602, 0x05010400))); +} +inline v_uint8x64 v_interleave_quads(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x32 v_interleave_pairs(const v_int16x32& vec) +{ + return v_int16x32(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0e0b0a, 0x0d0c0908, 0x07060302, 0x05040100))); +} +inline v_uint16x32 v_interleave_pairs(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x32 v_interleave_quads(const v_int16x32& vec) +{ + return v_int16x32(_mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0x0f0e0706, 0x0d0c0504, 0x0b0a0302, 0x09080100))); +} +inline v_uint16x32 v_interleave_quads(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x16 v_interleave_pairs(const v_int32x16& vec) +{ + return v_int32x16(_mm512_shuffle_epi32(vec.val, _MM_PERM_ACBD)); +} +inline v_uint32x16 v_interleave_pairs(const v_uint32x16& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x16 v_interleave_pairs(const v_float32x16& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x64 v_pack_triplets(const v_int8x64& vec) +{ + return v_int8x64(_mm512_permutexvar_epi32(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), + _mm512_shuffle_epi8(vec.val, _mm512_set4_epi32(0xffffff0f, 0x0e0d0c0a, 0x09080605, 0x04020100)))); +} +inline v_uint8x64 v_pack_triplets(const v_uint8x64& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x32 v_pack_triplets(const v_int16x32& vec) +{ + return v_int16x32(_mm512_permutexvar_epi16(_v512_set_epu64(0x001f001f001f001f, 0x001f001f001f001f, 0x001e001d001c001a, 0x0019001800160015, + 0x0014001200110010, 0x000e000d000c000a, 0x0009000800060005, 0x0004000200010000), vec.val)); +} +inline v_uint16x32 v_pack_triplets(const v_uint16x32& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x16 v_pack_triplets(const v_int32x16& vec) +{ + return v_int32x16(_mm512_permutexvar_epi32(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), vec.val)); +} +inline v_uint32x16 v_pack_triplets(const v_uint32x16& vec) { return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); } +inline v_float32x16 v_pack_triplets(const v_float32x16& vec) +{ + return v_float32x16(_mm512_permutexvar_ps(_v512_set_epu64(0x0000000f0000000f, 0x0000000f0000000f, 0x0000000e0000000d, 0x0000000c0000000a, + 0x0000000900000008, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000), vec.val)); +} + +////////// Matrix operations ///////// + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x16 v_dotprod(const v_int16x32& a, const v_int16x32& b) +{ return v_int32x16(_mm512_madd_epi16(a.val, b.val)); } +inline v_int32x16 v_dotprod(const v_int16x32& a, const v_int16x32& b, const v_int32x16& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x8 v_dotprod(const v_int32x16& a, const v_int32x16& b) +{ + __m512i even = _mm512_mul_epi32(a.val, b.val); + __m512i odd = _mm512_mul_epi32(_mm512_srli_epi64(a.val, 32), _mm512_srli_epi64(b.val, 32)); + return v_int64x8(_mm512_add_epi64(even, odd)); +} +inline v_int64x8 v_dotprod(const v_int32x16& a, const v_int32x16& b, const v_int64x8& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x16 v_dotprod_expand(const v_uint8x64& a, const v_uint8x64& b) +{ + __m512i even_a = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, a.val, _mm512_setzero_si512()); + __m512i odd_a = _mm512_srli_epi16(a.val, 8); + + __m512i even_b = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, b.val, _mm512_setzero_si512()); + __m512i odd_b = _mm512_srli_epi16(b.val, 8); + + __m512i prod0 = _mm512_madd_epi16(even_a, even_b); + __m512i prod1 = _mm512_madd_epi16(odd_a, odd_b); + return v_uint32x16(_mm512_add_epi32(prod0, prod1)); +} +inline v_uint32x16 v_dotprod_expand(const v_uint8x64& a, const v_uint8x64& b, const v_uint32x16& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x16 v_dotprod_expand(const v_int8x64& a, const v_int8x64& b) +{ + __m512i even_a = _mm512_srai_epi16(_mm512_bslli_epi128(a.val, 1), 8); + __m512i odd_a = _mm512_srai_epi16(a.val, 8); + + __m512i even_b = _mm512_srai_epi16(_mm512_bslli_epi128(b.val, 1), 8); + __m512i odd_b = _mm512_srai_epi16(b.val, 8); + + __m512i prod0 = _mm512_madd_epi16(even_a, even_b); + __m512i prod1 = _mm512_madd_epi16(odd_a, odd_b); + return v_int32x16(_mm512_add_epi32(prod0, prod1)); +} +inline v_int32x16 v_dotprod_expand(const v_int8x64& a, const v_int8x64& b, const v_int32x16& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x8 v_dotprod_expand(const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i mullo = _mm512_mullo_epi16(a.val, b.val); + __m512i mulhi = _mm512_mulhi_epu16(a.val, b.val); + __m512i mul0 = _mm512_unpacklo_epi16(mullo, mulhi); + __m512i mul1 = _mm512_unpackhi_epi16(mullo, mulhi); + + __m512i p02 = _mm512_mask_blend_epi32(0xAAAA, mul0, _mm512_setzero_si512()); + __m512i p13 = _mm512_srli_epi64(mul0, 32); + __m512i p46 = _mm512_mask_blend_epi32(0xAAAA, mul1, _mm512_setzero_si512()); + __m512i p57 = _mm512_srli_epi64(mul1, 32); + + __m512i p15_ = _mm512_add_epi64(p02, p13); + __m512i p9d_ = _mm512_add_epi64(p46, p57); + + return v_uint64x8(_mm512_add_epi64( + _mm512_unpacklo_epi64(p15_, p9d_), + _mm512_unpackhi_epi64(p15_, p9d_) + )); +} +inline v_uint64x8 v_dotprod_expand(const v_uint16x32& a, const v_uint16x32& b, const v_uint64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x8 v_dotprod_expand(const v_int16x32& a, const v_int16x32& b) +{ + __m512i prod = _mm512_madd_epi16(a.val, b.val); + __m512i even = _mm512_srai_epi64(_mm512_bslli_epi128(prod, 4), 32); + __m512i odd = _mm512_srai_epi64(prod, 32); + return v_int64x8(_mm512_add_epi64(even, odd)); +} +inline v_int64x8 v_dotprod_expand(const v_int16x32& a, const v_int16x32& b, const v_int64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x8 v_dotprod_expand(const v_int32x16& a, const v_int32x16& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x8 v_dotprod_expand(const v_int32x16& a, const v_int32x16& b, const v_float64x8& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x16 v_dotprod_fast(const v_int16x32& a, const v_int16x32& b) +{ return v_dotprod(a, b); } +inline v_int32x16 v_dotprod_fast(const v_int16x32& a, const v_int16x32& b, const v_int32x16& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x8 v_dotprod_fast(const v_int32x16& a, const v_int32x16& b) +{ return v_dotprod(a, b); } +inline v_int64x8 v_dotprod_fast(const v_int32x16& a, const v_int32x16& b, const v_int64x8& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x16 v_dotprod_expand_fast(const v_uint8x64& a, const v_uint8x64& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x16 v_dotprod_expand_fast(const v_uint8x64& a, const v_uint8x64& b, const v_uint32x16& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int32x16 v_dotprod_expand_fast(const v_int8x64& a, const v_int8x64& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x16 v_dotprod_expand_fast(const v_int8x64& a, const v_int8x64& b, const v_int32x16& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x8 v_dotprod_expand_fast(const v_uint16x32& a, const v_uint16x32& b) +{ + __m512i mullo = _mm512_mullo_epi16(a.val, b.val); + __m512i mulhi = _mm512_mulhi_epu16(a.val, b.val); + __m512i mul0 = _mm512_unpacklo_epi16(mullo, mulhi); + __m512i mul1 = _mm512_unpackhi_epi16(mullo, mulhi); + + __m512i p02 = _mm512_mask_blend_epi32(0xAAAA, mul0, _mm512_setzero_si512()); + __m512i p13 = _mm512_srli_epi64(mul0, 32); + __m512i p46 = _mm512_mask_blend_epi32(0xAAAA, mul1, _mm512_setzero_si512()); + __m512i p57 = _mm512_srli_epi64(mul1, 32); + + __m512i p15_ = _mm512_add_epi64(p02, p13); + __m512i p9d_ = _mm512_add_epi64(p46, p57); + return v_uint64x8(_mm512_add_epi64(p15_, p9d_)); +} +inline v_uint64x8 v_dotprod_expand_fast(const v_uint16x32& a, const v_uint16x32& b, const v_uint64x8& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x8 v_dotprod_expand_fast(const v_int16x32& a, const v_int16x32& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x8 v_dotprod_expand_fast(const v_int16x32& a, const v_int16x32& b, const v_int64x8& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x8 v_dotprod_expand_fast(const v_int32x16& a, const v_int32x16& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x8 v_dotprod_expand_fast(const v_int32x16& a, const v_int32x16& b, const v_float64x8& c) +{ return v_dotprod_expand(a, b) + c; } + + +#define OPENCV_HAL_AVX512_SPLAT2_PS(a, im) \ + v_float32x16(_mm512_permute_ps(a.val, _MM_SHUFFLE(im, im, im, im))) + +inline v_float32x16 v_matmul(const v_float32x16& v, + const v_float32x16& m0, const v_float32x16& m1, + const v_float32x16& m2, const v_float32x16& m3) +{ + v_float32x16 v04 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 0); + v_float32x16 v15 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 1); + v_float32x16 v26 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 2); + v_float32x16 v37 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 3); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, v37 * m3))); +} + +inline v_float32x16 v_matmuladd(const v_float32x16& v, + const v_float32x16& m0, const v_float32x16& m1, + const v_float32x16& m2, const v_float32x16& a) +{ + v_float32x16 v04 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 0); + v_float32x16 v15 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 1); + v_float32x16 v26 = OPENCV_HAL_AVX512_SPLAT2_PS(v, 2); + return v_fma(v04, m0, v_fma(v15, m1, v_fma(v26, m2, a))); +} + +#define OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ + inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ + { \ + __m512i t0 = cast_from(_mm512_unpacklo_##suffix(a0.val, a1.val)); \ + __m512i t1 = cast_from(_mm512_unpacklo_##suffix(a2.val, a3.val)); \ + __m512i t2 = cast_from(_mm512_unpackhi_##suffix(a0.val, a1.val)); \ + __m512i t3 = cast_from(_mm512_unpackhi_##suffix(a2.val, a3.val)); \ + b0.val = cast_to(_mm512_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm512_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm512_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm512_unpackhi_epi64(t2, t3)); \ + } + +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_uint32x16, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_int32x16, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_AVX512_TRANSPOSE4x4(v_float32x16, ps, _mm512_castps_si512, _mm512_castsi512_ps) + +//////////////// Value reordering /////////////// + +/* Expand */ +#define OPENCV_HAL_IMPL_AVX512_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(_v512_extract_low(a.val)); \ + b1.val = intrin(_v512_extract_high(a.val)); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v512_extract_low(a.val))); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(intrin(_v512_extract_high(a.val))); } \ + inline _Tpwvec v512_load_expand(const _Tp* ptr) \ + { \ + __m256i a = _mm256_loadu_si256((const __m256i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint8x64, v_uint16x32, uchar, _mm512_cvtepu8_epi16) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int8x64, v_int16x32, schar, _mm512_cvtepi8_epi16) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint16x32, v_uint32x16, ushort, _mm512_cvtepu16_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int16x32, v_int32x16, short, _mm512_cvtepi16_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_uint32x16, v_uint64x8, unsigned, _mm512_cvtepu32_epi64) +OPENCV_HAL_IMPL_AVX512_EXPAND(v_int32x16, v_int64x8, int, _mm512_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_AVX512_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v512_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadu_si128((const __m128i*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_AVX512_EXPAND_Q(v_uint32x16, uchar, _mm512_cvtepu8_epi32) +OPENCV_HAL_IMPL_AVX512_EXPAND_Q(v_int32x16, schar, _mm512_cvtepi8_epi32) + +/* pack */ +// 16 +inline v_int8x64 v_pack(const v_int16x32& a, const v_int16x32& b) +{ return v_int8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi16(a.val, b.val))); } + +inline v_uint8x64 v_pack(const v_uint16x32& a, const v_uint16x32& b) +{ + const __m512i t = _mm512_set1_epi16(255); + return v_uint8x64(_v512_combine(_mm512_cvtepi16_epi8(_mm512_min_epu16(a.val, t)), _mm512_cvtepi16_epi8(_mm512_min_epu16(b.val, t)))); +} + +inline v_uint8x64 v_pack_u(const v_int16x32& a, const v_int16x32& b) +{ + return v_uint8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packus_epi16(a.val, b.val))); +} + +inline void v_pack_store(schar* ptr, const v_int16x32& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(uchar* ptr, const v_uint16x32& a) +{ + const __m512i m = _mm512_set1_epi16(255); + _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi16_epi8(_mm512_min_epu16(a.val, m))); +} + +inline void v_pack_u_store(uchar* ptr, const v_int16x32& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + +template inline +v_uint8x64 v_rshr_pack(const v_uint16x32& a, const v_uint16x32& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + v_uint16x32 delta = v512_setall_u16((short)(1 << (n-1))); + return v_pack_u(v_reinterpret_as_s16((a + delta) >> n), + v_reinterpret_as_s16((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x32& a) +{ + v_uint16x32 delta = v512_setall_u16((short)(1 << (n-1))); + v_pack_u_store(ptr, v_reinterpret_as_s16((a + delta) >> n)); +} + +template inline +v_uint8x64 v_rshr_pack_u(const v_int16x32& a, const v_int16x32& b) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x32& a) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int8x64 v_rshr_pack(const v_int16x32& a, const v_int16x32& b) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x32& a) +{ + v_int16x32 delta = v512_setall_s16((short)(1 << (n-1))); + v_pack_store(ptr, (a + delta) >> n); +} + +// 32 +inline v_int16x32 v_pack(const v_int32x16& a, const v_int32x16& b) +{ return v_int16x32(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi32(a.val, b.val))); } + +inline v_uint16x32 v_pack(const v_uint32x16& a, const v_uint32x16& b) +{ + const __m512i m = _mm512_set1_epi32(65535); + return v_uint16x32(_v512_combine(_mm512_cvtepi32_epi16(_mm512_min_epu32(a.val, m)), _mm512_cvtepi32_epi16(_mm512_min_epu32(b.val, m)))); +} + +inline v_uint16x32 v_pack_u(const v_int32x16& a, const v_int32x16& b) +{ return v_uint16x32(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packus_epi32(a.val, b.val))); } + +inline void v_pack_store(short* ptr, const v_int32x16& a) +{ v_store_low(ptr, v_pack(a, a)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x16& a) +{ + const __m512i m = _mm512_set1_epi32(65535); + _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi32_epi16(_mm512_min_epu32(a.val, m))); +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x16& a) +{ v_store_low(ptr, v_pack_u(a, a)); } + + +template inline +v_uint16x32 v_rshr_pack(const v_uint32x16& a, const v_uint32x16& b) +{ + v_uint32x16 delta = v512_setall_u32(1 << (n-1)); + return v_pack_u(v_reinterpret_as_s32((a + delta) >> n), + v_reinterpret_as_s32((b + delta) >> n)); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x16& a) +{ + v_uint32x16 delta = v512_setall_u32(1 << (n-1)); + v_pack_u_store(ptr, v_reinterpret_as_s32((a + delta) >> n)); +} + +template inline +v_uint16x32 v_rshr_pack_u(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + return v_pack_u((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x16& a) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + v_pack_u_store(ptr, (a + delta) >> n); +} + +template inline +v_int16x32 v_rshr_pack(const v_int32x16& a, const v_int32x16& b) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x16& a) +{ + v_int32x16 delta = v512_setall_s32(1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// 64 +// Non-saturating pack +inline v_uint32x16 v_pack(const v_uint64x8& a, const v_uint64x8& b) +{ return v_uint32x16(_v512_combine(_mm512_cvtepi64_epi32(a.val), _mm512_cvtepi64_epi32(b.val))); } + +inline v_int32x16 v_pack(const v_int64x8& a, const v_int64x8& b) +{ return v_reinterpret_as_s32(v_pack(v_reinterpret_as_u64(a), v_reinterpret_as_u64(b))); } + +inline void v_pack_store(unsigned* ptr, const v_uint64x8& a) +{ _mm256_storeu_si256((__m256i*)ptr, _mm512_cvtepi64_epi32(a.val)); } + +inline void v_pack_store(int* ptr, const v_int64x8& b) +{ v_pack_store((unsigned*)ptr, v_reinterpret_as_u64(b)); } + +template inline +v_uint32x16 v_rshr_pack(const v_uint64x8& a, const v_uint64x8& b) +{ + v_uint64x8 delta = v512_setall_u64((uint64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x8& a) +{ + v_uint64x8 delta = v512_setall_u64((uint64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +template inline +v_int32x16 v_rshr_pack(const v_int64x8& a, const v_int64x8& b) +{ + v_int64x8 delta = v512_setall_s64((int64)1 << (n-1)); + return v_pack((a + delta) >> n, (b + delta) >> n); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x8& a) +{ + v_int64x8 delta = v512_setall_s64((int64)1 << (n-1)); + v_pack_store(ptr, (a + delta) >> n); +} + +// pack boolean +inline v_uint8x64 v_pack_b(const v_uint16x32& a, const v_uint16x32& b) +{ return v_uint8x64(_mm512_permutexvar_epi64(_v512_set_epu64(7, 5, 3, 1, 6, 4, 2, 0), _mm512_packs_epi16(a.val, b.val))); } + +inline v_uint8x64 v_pack_b(const v_uint32x16& a, const v_uint32x16& b, + const v_uint32x16& c, const v_uint32x16& d) +{ + __m512i ab = _mm512_packs_epi32(a.val, b.val); + __m512i cd = _mm512_packs_epi32(c.val, d.val); + + return v_uint8x64(_mm512_permutexvar_epi32(_v512_set_epu32(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0), _mm512_packs_epi16(ab, cd))); +} + +inline v_uint8x64 v_pack_b(const v_uint64x8& a, const v_uint64x8& b, const v_uint64x8& c, + const v_uint64x8& d, const v_uint64x8& e, const v_uint64x8& f, + const v_uint64x8& g, const v_uint64x8& h) +{ + __m512i ab = _mm512_packs_epi32(a.val, b.val); + __m512i cd = _mm512_packs_epi32(c.val, d.val); + __m512i ef = _mm512_packs_epi32(e.val, f.val); + __m512i gh = _mm512_packs_epi32(g.val, h.val); + + __m512i abcd = _mm512_packs_epi32(ab, cd); + __m512i efgh = _mm512_packs_epi32(ef, gh); + + return v_uint8x64(_mm512_permutexvar_epi16(_v512_set_epu16(31, 23, 15, 7, 30, 22, 14, 6, 29, 21, 13, 5, 28, 20, 12, 4, + 27, 19, 11, 3, 26, 18, 10, 2, 25, 17, 9, 1, 24, 16, 8, 0), _mm512_packs_epi16(abcd, efgh))); +} + +/* Recombine */ +// its up there with load and store operations + +/* Extract */ +#define OPENCV_HAL_IMPL_AVX512_EXTRACT(_Tpvec) \ + template \ + inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ + { return v_rotate_right(a, b); } + +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint8x64) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int8x64) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint16x32) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int16x32) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_uint64x8) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_int64x8) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_float32x16) +OPENCV_HAL_IMPL_AVX512_EXTRACT(v_float64x8) + +#define OPENCV_HAL_IMPL_AVX512_EXTRACT_N(_Tpvec, _Tp) \ +template inline _Tp v_extract_n(_Tpvec v) { return v_rotate_right(v).get0(); } + +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint8x64, uchar) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int8x64, schar) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint16x32, ushort) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int16x32, short) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint32x16, uint) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int32x16, int) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_uint64x8, uint64) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_int64x8, int64) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_float32x16, float) +OPENCV_HAL_IMPL_AVX512_EXTRACT_N(v_float64x8, double) + +template +inline v_uint32x16 v_broadcast_element(v_uint32x16 a) +{ + static const __m512i perm = _mm512_set1_epi32((char)i); + return v_uint32x16(_mm512_permutexvar_epi32(perm, a.val)); +} + +template +inline v_int32x16 v_broadcast_element(const v_int32x16 &a) +{ return v_reinterpret_as_s32(v_broadcast_element(v_reinterpret_as_u32(a))); } + +template +inline v_float32x16 v_broadcast_element(const v_float32x16 &a) +{ return v_reinterpret_as_f32(v_broadcast_element(v_reinterpret_as_u32(a))); } + + +///////////////////// load deinterleave ///////////////////////////// + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(126, 124, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, + 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu8(127, 125, 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, 97, + 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, + 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint8x64(_mm512_permutex2var_epi8(ab0, mask0, ab1)); + b = v_uint8x64(_mm512_permutex2var_epi8(ab0, mask1, ab1)); +#else + __m512i mask0 = _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200); + __m512i a0b0 = _mm512_shuffle_epi8(ab0, mask0); + __m512i a1b1 = _mm512_shuffle_epi8(ab1, mask0); + __m512i mask1 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask2 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint8x64(_mm512_permutex2var_epi64(a0b0, mask1, a1b1)); + b = v_uint8x64(_mm512_permutex2var_epi64(a0b0, mask2, a1b1)); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i mask0 = _v512_set_epu16(62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu16(63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint16x32(_mm512_permutex2var_epi16(ab0, mask0, ab1)); + b = v_uint16x32(_mm512_permutex2var_epi16(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint32x16(_mm512_permutex2var_epi32(ab0, mask0, ab1)); + b = v_uint32x16(_mm512_permutex2var_epi32(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b ) +{ + __m512i ab0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i ab1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i mask0 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + a = v_uint64x8(_mm512_permutex2var_epi64(ab0, mask0, ab1)); + b = v_uint64x8(_mm512_permutex2var_epi64(ab0, mask1, ab1)); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b, v_uint8x64& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 128)); + +#if CV_AVX_512VBMI2 + __m512i mask0 = _v512_set_epu8(126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, + 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, + 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 62, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2); + __m512i r0b01 = _mm512_permutex2var_epi8(bgr0, mask0, bgr1); + __m512i b1g12 = _mm512_permutex2var_epi8(bgr1, mask0, bgr2); + __m512i r12b2 = _mm512_permutex2var_epi8(bgr1, + _v512_set_epu8(125, 122, 119, 116, 113, 110, 107, 104, 101, 98, 95, 92, 89, 86, 83, 80, + 77, 74, 71, 68, 65, 127, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, + 94, 91, 88, 85, 82, 79, 76, 73, 70, 67, 64, 61, 58, 55, 52, 49, + 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1), bgr2); + a = v_uint8x64(_mm512_mask_compress_epi8(r12b2, 0xffffffffffe00000, r0b01)); + b = v_uint8x64(_mm512_mask_compress_epi8(b1g12, 0x2492492492492492, bgr0)); + c = v_uint8x64(_mm512_mask_expand_epi8(r0b01, 0xffffffffffe00000, r12b2)); +#elif CV_AVX_512VBMI + __m512i b0g0b1 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr1, bgr0); + __m512i g1r1g2 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr2, bgr1); + __m512i r2b2r0 = _mm512_mask_blend_epi8(0xb6db6db6db6db6db, bgr0, bgr2); + a = v_uint8x64(_mm512_permutex2var_epi8(b0g0b1, _v512_set_epu8(125, 122, 119, 116, 113, 110, 107, 104, 101, 98, 95, 92, 89, 86, 83, 80, + 77, 74, 71, 68, 65, 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, + 46, 45, 43, 42, 40, 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, + 23, 21, 20, 18, 17, 15, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0), bgr2)); + b = v_uint8x64(_mm512_permutex2var_epi8(g1r1g2, _v512_set_epu8( 63, 61, 60, 58, 57, 55, 54, 52, 51, 49, 48, 46, 45, 43, 42, 40, + 39, 37, 36, 34, 33, 31, 30, 28, 27, 25, 24, 23, 21, 20, 18, 17, + 15, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0, 126, 123, 120, 117, 114, + 111, 108, 105, 102, 99, 96, 93, 90, 87, 84, 81, 78, 75, 72, 69, 66), bgr0)); + c = v_uint8x64(_mm512_permutex2var_epi8(r2b2r0, _v512_set_epu8( 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, + 15, 12, 9, 6, 3, 0, 125, 122, 119, 116, 113, 110, 107, 104, 101, 98, + 95, 92, 89, 86, 83, 80, 77, 74, 71, 68, 65, 62, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2), bgr1)); +#else + __m512i mask0 = _v512_set_epu16(61, 58, 55, 52, 49, 46, 43, 40, 37, 34, 63, 60, 57, 54, 51, 48, + 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi16(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi16(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi16(bgr2, mask0, bgr0); + + __m512i b0g0 = _mm512_mask_blend_epi32(0xf800, b01g1, r12b2); + __m512i r0b1 = _mm512_permutex2var_epi16(bgr1, _v512_set_epu16(42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 29, 26, 23, 20, 17, + 14, 11, 8, 5, 2, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43), g20r0); + __m512i g1r1 = _mm512_alignr_epi32(r12b2, g20r0, 11); + a = v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, b0g0, r0b1)); + c = v_uint8x64(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, r0b1, g1r1)); + b = v_uint8x64(_mm512_shuffle_epi8(_mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, g1r1, b0g0), _mm512_set4_epi32(0x0e0f0c0d, 0x0a0b0809, 0x06070405, 0x02030001))); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b, v_uint16x32& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + + __m512i mask0 = _v512_set_epu16(61, 58, 55, 52, 49, 46, 43, 40, 37, 34, 63, 60, 57, 54, 51, 48, + 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi16(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi16(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi16(bgr2, mask0, bgr0); + + a = v_uint16x32(_mm512_mask_blend_epi32(0xf800, b01g1, r12b2)); + b = v_uint16x32(_mm512_permutex2var_epi16(bgr1, _v512_set_epu16(42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 29, 26, 23, 20, 17, + 14, 11, 8, 5, 2, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43), g20r0)); + c = v_uint16x32(_mm512_alignr_epi32(r12b2, g20r0, 11)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b, v_uint32x16& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + + __m512i mask0 = _v512_set_epu32(29, 26, 23, 20, 17, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0); + __m512i b01r1 = _mm512_permutex2var_epi32(bgr0, mask0, bgr1); + __m512i g12b2 = _mm512_permutex2var_epi32(bgr1, mask0, bgr2); + __m512i r20g0 = _mm512_permutex2var_epi32(bgr2, mask0, bgr0); + + a = v_uint32x16(_mm512_mask_blend_epi32(0xf800, b01r1, g12b2)); + b = v_uint32x16(_mm512_alignr_epi32(g12b2, r20g0, 11)); + c = v_uint32x16(_mm512_permutex2var_epi32(bgr1, _v512_set_epu32(21, 20, 19, 18, 17, 16, 13, 10, 7, 4, 1, 26, 25, 24, 23, 22), r20g0)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b, v_uint64x8& c ) +{ + __m512i bgr0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgr1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i bgr2 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + + __m512i mask0 = _v512_set_epu64(13, 10, 15, 12, 9, 6, 3, 0); + __m512i b01g1 = _mm512_permutex2var_epi64(bgr0, mask0, bgr1); + __m512i r12b2 = _mm512_permutex2var_epi64(bgr1, mask0, bgr2); + __m512i g20r0 = _mm512_permutex2var_epi64(bgr2, mask0, bgr0); + + a = v_uint64x8(_mm512_mask_blend_epi64(0xc0, b01g1, r12b2)); + c = v_uint64x8(_mm512_alignr_epi64(r12b2, g20r0, 6)); + b = v_uint64x8(_mm512_permutex2var_epi64(bgr1, _v512_set_epu64(10, 9, 8, 5, 2, 13, 12, 11), g20r0)); +} + +inline void v_load_deinterleave( const uchar* ptr, v_uint8x64& a, v_uint8x64& b, v_uint8x64& c, v_uint8x64& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 128)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 192)); + +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(126, 124, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, + 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu8(127, 125, 123, 121, 119, 117, 115, 113, 111, 109, 107, 105, 103, 101, 99, 97, + 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, + 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi8(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi8(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi8(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi8(bgra2, mask1, bgra3); + + a = v_uint8x64(_mm512_permutex2var_epi8(br01, mask0, br23)); + c = v_uint8x64(_mm512_permutex2var_epi8(br01, mask1, br23)); + b = v_uint8x64(_mm512_permutex2var_epi8(ga01, mask0, ga23)); + d = v_uint8x64(_mm512_permutex2var_epi8(ga01, mask1, ga23)); +#else + __m512i mask = _mm512_set4_epi32(0x0f0b0703, 0x0e0a0602, 0x0d090501, 0x0c080400); + __m512i b0g0r0a0 = _mm512_shuffle_epi8(bgra0, mask); + __m512i b1g1r1a1 = _mm512_shuffle_epi8(bgra1, mask); + __m512i b2g2r2a2 = _mm512_shuffle_epi8(bgra2, mask); + __m512i b3g3r3a3 = _mm512_shuffle_epi8(bgra3, mask); + + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi32(b0g0r0a0, mask0, b1g1r1a1); + __m512i ga01 = _mm512_permutex2var_epi32(b0g0r0a0, mask1, b1g1r1a1); + __m512i br23 = _mm512_permutex2var_epi32(b2g2r2a2, mask0, b3g3r3a3); + __m512i ga23 = _mm512_permutex2var_epi32(b2g2r2a2, mask1, b3g3r3a3); + + a = v_uint8x64(_mm512_permutex2var_epi32(br01, mask0, br23)); + c = v_uint8x64(_mm512_permutex2var_epi32(br01, mask1, br23)); + b = v_uint8x64(_mm512_permutex2var_epi32(ga01, mask0, ga23)); + d = v_uint8x64(_mm512_permutex2var_epi32(ga01, mask1, ga23)); +#endif +} + +inline void v_load_deinterleave( const ushort* ptr, v_uint16x32& a, v_uint16x32& b, v_uint16x32& c, v_uint16x32& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 64)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 96)); + + __m512i mask0 = _v512_set_epu16(62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu16(63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi16(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi16(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi16(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi16(bgra2, mask1, bgra3); + + a = v_uint16x32(_mm512_permutex2var_epi16(br01, mask0, br23)); + c = v_uint16x32(_mm512_permutex2var_epi16(br01, mask1, br23)); + b = v_uint16x32(_mm512_permutex2var_epi16(ga01, mask0, ga23)); + d = v_uint16x32(_mm512_permutex2var_epi16(ga01, mask1, ga23)); +} + +inline void v_load_deinterleave( const unsigned* ptr, v_uint32x16& a, v_uint32x16& b, v_uint32x16& c, v_uint32x16& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 32)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 48)); + + __m512i mask0 = _v512_set_epu32(30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu32(31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi32(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi32(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi32(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi32(bgra2, mask1, bgra3); + + a = v_uint32x16(_mm512_permutex2var_epi32(br01, mask0, br23)); + c = v_uint32x16(_mm512_permutex2var_epi32(br01, mask1, br23)); + b = v_uint32x16(_mm512_permutex2var_epi32(ga01, mask0, ga23)); + d = v_uint32x16(_mm512_permutex2var_epi32(ga01, mask1, ga23)); +} + +inline void v_load_deinterleave( const uint64* ptr, v_uint64x8& a, v_uint64x8& b, v_uint64x8& c, v_uint64x8& d ) +{ + __m512i bgra0 = _mm512_loadu_si512((const __m512i*)ptr); + __m512i bgra1 = _mm512_loadu_si512((const __m512i*)(ptr + 8)); + __m512i bgra2 = _mm512_loadu_si512((const __m512i*)(ptr + 16)); + __m512i bgra3 = _mm512_loadu_si512((const __m512i*)(ptr + 24)); + + __m512i mask0 = _v512_set_epu64(14, 12, 10, 8, 6, 4, 2, 0); + __m512i mask1 = _v512_set_epu64(15, 13, 11, 9, 7, 5, 3, 1); + + __m512i br01 = _mm512_permutex2var_epi64(bgra0, mask0, bgra1); + __m512i ga01 = _mm512_permutex2var_epi64(bgra0, mask1, bgra1); + __m512i br23 = _mm512_permutex2var_epi64(bgra2, mask0, bgra3); + __m512i ga23 = _mm512_permutex2var_epi64(bgra2, mask1, bgra3); + + a = v_uint64x8(_mm512_permutex2var_epi64(br01, mask0, br23)); + c = v_uint64x8(_mm512_permutex2var_epi64(br01, mask1, br23)); + b = v_uint64x8(_mm512_permutex2var_epi64(ga01, mask0, ga23)); + d = v_uint64x8(_mm512_permutex2var_epi64(ga01, mask1, ga23)); +} + +///////////////////////////// store interleave ///////////////////////////////////// + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& x, const v_uint8x64& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint8x64 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 64), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 64), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), high.val); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& x, const v_uint16x32& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint16x32 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 32), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 32), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), high.val); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& x, const v_uint32x16& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint32x16 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 16), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 16), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), high.val); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& x, const v_uint64x8& y, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint64x8 low, high; + v_zip(x, y, low, high); + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, low.val); + _mm512_stream_si512((__m512i*)(ptr + 8), high.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, low.val); + _mm512_store_si512((__m512i*)(ptr + 8), high.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, low.val); + _mm512_storeu_si512((__m512i*)(ptr + 8), high.val); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& a, const v_uint8x64& b, const v_uint8x64& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ +#if CV_AVX_512VBMI + __m512i mask0 = _v512_set_epu8(127, 84, 20, 126, 83, 19, 125, 82, 18, 124, 81, 17, 123, 80, 16, 122, + 79, 15, 121, 78, 14, 120, 77, 13, 119, 76, 12, 118, 75, 11, 117, 74, + 10, 116, 73, 9, 115, 72, 8, 114, 71, 7, 113, 70, 6, 112, 69, 5, + 111, 68, 4, 110, 67, 3, 109, 66, 2, 108, 65, 1, 107, 64, 0, 106); + __m512i mask1 = _v512_set_epu8( 21, 42, 105, 20, 41, 104, 19, 40, 103, 18, 39, 102, 17, 38, 101, 16, + 37, 100, 15, 36, 99, 14, 35, 98, 13, 34, 97, 12, 33, 96, 11, 32, + 95, 10, 31, 94, 9, 30, 93, 8, 29, 92, 7, 28, 91, 6, 27, 90, + 5, 26, 89, 4, 25, 88, 3, 24, 87, 2, 23, 86, 1, 22, 85, 0); + __m512i mask2 = _v512_set_epu8(106, 127, 63, 105, 126, 62, 104, 125, 61, 103, 124, 60, 102, 123, 59, 101, + 122, 58, 100, 121, 57, 99, 120, 56, 98, 119, 55, 97, 118, 54, 96, 117, + 53, 95, 116, 52, 94, 115, 51, 93, 114, 50, 92, 113, 49, 91, 112, 48, + 90, 111, 47, 89, 110, 46, 88, 109, 45, 87, 108, 44, 86, 107, 43, 85); + __m512i r2g0r0 = _mm512_permutex2var_epi8(b.val, mask0, c.val); + __m512i b0r1b1 = _mm512_permutex2var_epi8(a.val, mask1, c.val); + __m512i g1b2g2 = _mm512_permutex2var_epi8(a.val, mask2, b.val); + + __m512i bgr0 = _mm512_mask_blend_epi8(0x9249249249249249, r2g0r0, b0r1b1); + __m512i bgr1 = _mm512_mask_blend_epi8(0x9249249249249249, b0r1b1, g1b2g2); + __m512i bgr2 = _mm512_mask_blend_epi8(0x9249249249249249, g1b2g2, r2g0r0); +#else + __m512i g1g0 = _mm512_shuffle_epi8(b.val, _mm512_set4_epi32(0x0e0f0c0d, 0x0a0b0809, 0x06070405, 0x02030001)); + __m512i b0g0 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, a.val, g1g0); + __m512i r0b1 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, c.val, a.val); + __m512i g1r1 = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, g1g0, c.val); + + __m512i mask0 = _v512_set_epu16(42, 10, 31, 41, 9, 30, 40, 8, 29, 39, 7, 28, 38, 6, 27, 37, + 5, 26, 36, 4, 25, 35, 3, 24, 34, 2, 23, 33, 1, 22, 32, 0); + __m512i mask1 = _v512_set_epu16(21, 52, 41, 20, 51, 40, 19, 50, 39, 18, 49, 38, 17, 48, 37, 16, + 47, 36, 15, 46, 35, 14, 45, 34, 13, 44, 33, 12, 43, 32, 11, 42); + __m512i mask2 = _v512_set_epu16(63, 31, 20, 62, 30, 19, 61, 29, 18, 60, 28, 17, 59, 27, 16, 58, + 26, 15, 57, 25, 14, 56, 24, 13, 55, 23, 12, 54, 22, 11, 53, 21); + __m512i b0g0b2 = _mm512_permutex2var_epi16(b0g0, mask0, r0b1); + __m512i r1b1r0 = _mm512_permutex2var_epi16(b0g0, mask1, g1r1); + __m512i g2r2g1 = _mm512_permutex2var_epi16(r0b1, mask2, g1r1); + + __m512i bgr0 = _mm512_mask_blend_epi16(0x24924924, b0g0b2, r1b1r0); + __m512i bgr1 = _mm512_mask_blend_epi16(0x24924924, r1b1r0, g2r2g1); + __m512i bgr2 = _mm512_mask_blend_epi16(0x24924924, g2r2g1, b0g0b2); +#endif + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 64), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 128), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 64), bgr1); + _mm512_store_si512((__m512i*)(ptr + 128), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 128), bgr2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& a, const v_uint16x32& b, const v_uint16x32& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu16(42, 10, 31, 41, 9, 30, 40, 8, 29, 39, 7, 28, 38, 6, 27, 37, + 5, 26, 36, 4, 25, 35, 3, 24, 34, 2, 23, 33, 1, 22, 32, 0); + __m512i mask1 = _v512_set_epu16(21, 52, 41, 20, 51, 40, 19, 50, 39, 18, 49, 38, 17, 48, 37, 16, + 47, 36, 15, 46, 35, 14, 45, 34, 13, 44, 33, 12, 43, 32, 11, 42); + __m512i mask2 = _v512_set_epu16(63, 31, 20, 62, 30, 19, 61, 29, 18, 60, 28, 17, 59, 27, 16, 58, + 26, 15, 57, 25, 14, 56, 24, 13, 55, 23, 12, 54, 22, 11, 53, 21); + __m512i b0g0b2 = _mm512_permutex2var_epi16(a.val, mask0, b.val); + __m512i r1b1r0 = _mm512_permutex2var_epi16(a.val, mask1, c.val); + __m512i g2r2g1 = _mm512_permutex2var_epi16(b.val, mask2, c.val); + + __m512i bgr0 = _mm512_mask_blend_epi16(0x24924924, b0g0b2, r1b1r0); + __m512i bgr1 = _mm512_mask_blend_epi16(0x24924924, r1b1r0, g2r2g1); + __m512i bgr2 = _mm512_mask_blend_epi16(0x24924924, g2r2g1, b0g0b2); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 32), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 64), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 32), bgr1); + _mm512_store_si512((__m512i*)(ptr + 64), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgr2); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& a, const v_uint32x16& b, const v_uint32x16& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu32(26, 31, 15, 25, 30, 14, 24, 29, 13, 23, 28, 12, 22, 27, 11, 21); + __m512i mask1 = _v512_set_epu32(31, 10, 25, 30, 9, 24, 29, 8, 23, 28, 7, 22, 27, 6, 21, 26); + __m512i g1b2g2 = _mm512_permutex2var_epi32(a.val, mask0, b.val); + __m512i r2r1b1 = _mm512_permutex2var_epi32(a.val, mask1, c.val); + + __m512i bgr0 = _mm512_mask_expand_epi32(_mm512_mask_expand_epi32(_mm512_maskz_expand_epi32(0x9249, a.val), 0x2492, b.val), 0x4924, c.val); + __m512i bgr1 = _mm512_mask_blend_epi32(0x9249, r2r1b1, g1b2g2); + __m512i bgr2 = _mm512_mask_blend_epi32(0x9249, g1b2g2, r2r1b1); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 16), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 32), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 16), bgr1); + _mm512_store_si512((__m512i*)(ptr + 32), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgr2); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& a, const v_uint64x8& b, const v_uint64x8& c, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + __m512i mask0 = _v512_set_epu64( 5, 12, 7, 4, 11, 6, 3, 10); + __m512i mask1 = _v512_set_epu64(15, 7, 4, 14, 6, 3, 13, 5); + __m512i r1b1b2 = _mm512_permutex2var_epi64(a.val, mask0, c.val); + __m512i g2r2g1 = _mm512_permutex2var_epi64(b.val, mask1, c.val); + + __m512i bgr0 = _mm512_mask_expand_epi64(_mm512_mask_expand_epi64(_mm512_maskz_expand_epi64(0x49, a.val), 0x92, b.val), 0x24, c.val); + __m512i bgr1 = _mm512_mask_blend_epi64(0xdb, g2r2g1, r1b1b2); + __m512i bgr2 = _mm512_mask_blend_epi64(0xdb, r1b1b2, g2r2g1); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgr0); + _mm512_stream_si512((__m512i*)(ptr + 8), bgr1); + _mm512_stream_si512((__m512i*)(ptr + 16), bgr2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgr0); + _mm512_store_si512((__m512i*)(ptr + 8), bgr1); + _mm512_store_si512((__m512i*)(ptr + 16), bgr2); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgr0); + _mm512_storeu_si512((__m512i*)(ptr + 8), bgr1); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgr2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x64& a, const v_uint8x64& b, + const v_uint8x64& c, const v_uint8x64& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint8x64 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint8x64 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 192), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 192), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 128), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 192), bgra3.val); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x32& a, const v_uint16x32& b, + const v_uint16x32& c, const v_uint16x32& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint16x32 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint16x32 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 96), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 96), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 64), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 96), bgra3.val); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x16& a, const v_uint32x16& b, + const v_uint32x16& c, const v_uint32x16& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint32x16 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint32x16 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 48), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 48), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 32), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 48), bgra3.val); + } +} + +inline void v_store_interleave( uint64* ptr, const v_uint64x8& a, const v_uint64x8& b, + const v_uint64x8& c, const v_uint64x8& d, + hal::StoreMode mode=hal::STORE_UNALIGNED ) +{ + v_uint64x8 br01, br23, ga01, ga23; + v_zip(a, c, br01, br23); + v_zip(b, d, ga01, ga23); + v_uint64x8 bgra0, bgra1, bgra2, bgra3; + v_zip(br01, ga01, bgra0, bgra1); + v_zip(br23, ga23, bgra2, bgra3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm512_stream_si512((__m512i*)ptr, bgra0.val); + _mm512_stream_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_stream_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_stream_si512((__m512i*)(ptr + 24), bgra3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm512_store_si512((__m512i*)ptr, bgra0.val); + _mm512_store_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_store_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_store_si512((__m512i*)(ptr + 24), bgra3.val); + } + else + { + _mm512_storeu_si512((__m512i*)ptr, bgra0.val); + _mm512_storeu_si512((__m512i*)(ptr + 8), bgra1.val); + _mm512_storeu_si512((__m512i*)(ptr + 16), bgra2.val); + _mm512_storeu_si512((__m512i*)(ptr + 24), bgra3.val); + } +} + +#define OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, const _Tpvec0& c0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int8x64, schar, s8, v_uint8x64, uchar, u8) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int16x32, short, s16, v_uint16x32, ushort, u16) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int32x16, int, s32, v_uint32x16, unsigned, u32) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_float32x16, float, f32, v_uint32x16, unsigned, u32) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_int64x8, int64, s64, v_uint64x8, uint64, u64) +OPENCV_HAL_IMPL_AVX512_LOADSTORE_INTERLEAVE(v_float64x8, double, f64, v_uint64x8, uint64, u64) + +////////// Mask and checks ///////// + +/** Mask **/ +inline int64 v_signmask(const v_int8x64& a) { return (int64)_mm512_movepi8_mask(a.val); } +inline int v_signmask(const v_int16x32& a) { return (int)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline int v_signmask(const v_int32x16& a) { return (int)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline int v_signmask(const v_int64x8& a) { return (int)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } + +inline int64 v_signmask(const v_uint8x64& a) { return v_signmask(v_reinterpret_as_s8(a)); } +inline int v_signmask(const v_uint16x32& a) { return v_signmask(v_reinterpret_as_s16(a)); } +inline int v_signmask(const v_uint32x16& a) { return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_uint64x8& a) { return v_signmask(v_reinterpret_as_s64(a)); } +inline int v_signmask(const v_float32x16& a) { return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_float64x8& a) { return v_signmask(v_reinterpret_as_s64(a)); } + +/** Checks **/ +inline bool v_check_all(const v_int8x64& a) { return !(bool)_mm512_cmp_epi8_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int8x64& a) { return (bool)_mm512_movepi8_mask(a.val); } +inline bool v_check_all(const v_int16x32& a) { return !(bool)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int16x32& a) { return (bool)_mm512_cmp_epi16_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline bool v_check_all(const v_int32x16& a) { return !(bool)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int32x16& a) { return (bool)_mm512_cmp_epi32_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } +inline bool v_check_all(const v_int64x8& a) { return !(bool)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_NLT); } +inline bool v_check_any(const v_int64x8& a) { return (bool)_mm512_cmp_epi64_mask(a.val, _mm512_setzero_si512(), _MM_CMPINT_LT); } + +inline bool v_check_all(const v_float32x16& a) { return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_float32x16& a) { return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_float64x8& a) { return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_float64x8& a) { return v_check_any(v_reinterpret_as_s64(a)); } +inline bool v_check_all(const v_uint8x64& a) { return v_check_all(v_reinterpret_as_s8(a)); } +inline bool v_check_all(const v_uint16x32& a) { return v_check_all(v_reinterpret_as_s16(a)); } +inline bool v_check_all(const v_uint32x16& a) { return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_uint64x8& a) { return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_uint8x64& a) { return v_check_any(v_reinterpret_as_s8(a)); } +inline bool v_check_any(const v_uint16x32& a) { return v_check_any(v_reinterpret_as_s16(a)); } +inline bool v_check_any(const v_uint32x16& a) { return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_uint64x8& a) { return v_check_any(v_reinterpret_as_s64(a)); } + +inline int v_scan_forward(const v_int8x64& a) +{ + int64 mask = _mm512_movepi8_mask(a.val); + int mask32 = (int)mask; + return mask != 0 ? mask32 != 0 ? trailingZeros32(mask32) : 32 + trailingZeros32((int)(mask >> 32)) : 0; +} +inline int v_scan_forward(const v_uint8x64& a) { return v_scan_forward(v_reinterpret_as_s8(a)); } +inline int v_scan_forward(const v_int16x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))); } +inline int v_scan_forward(const v_uint16x32& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))); } +inline int v_scan_forward(const v_int32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_uint32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_float32x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 2; } +inline int v_scan_forward(const v_int64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } +inline int v_scan_forward(const v_uint64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } +inline int v_scan_forward(const v_float64x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s16(a))) / 4; } + +inline void v512_cleanup() { _mm256_zeroall(); } + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_AVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_cpp.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_cpp.hpp new file mode 100644 index 0000000..4622214 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_cpp.hpp @@ -0,0 +1,3320 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_CPP_HPP +#define OPENCV_HAL_INTRIN_CPP_HPP + +#include +#include +#include +#include "opencv2/core/saturate.hpp" + +//! @cond IGNORED +#define CV_SIMD128_CPP 1 +#if defined(CV_FORCE_SIMD128_CPP) +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 +#endif +#if defined(CV_DOXYGEN) +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 +#define CV_SIMD256 1 +#define CV_SIMD256_64F 1 +#define CV_SIMD512 1 +#define CV_SIMD512_64F 1 +#else +#define CV_SIMD256 0 // Explicitly disable SIMD256 and SIMD512 support for scalar intrinsic implementation +#define CV_SIMD512 0 // to avoid warnings during compilation +#endif +//! @endcond + +namespace cv +{ + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN +#endif + +/** @addtogroup core_hal_intrin + +"Universal intrinsics" is a types and functions set intended to simplify vectorization of code on +different platforms. Currently a few different SIMD extensions on different architectures are supported. +128 bit registers of various types support is implemented for a wide range of architectures +including x86(__SSE/SSE2/SSE4.2__), ARM(__NEON__), PowerPC(__VSX__), MIPS(__MSA__). +256 bit long registers are supported on x86(__AVX2__) and 512 bit long registers are supported on x86(__AVX512__). +In case when there is no SIMD extension available during compilation, fallback C++ implementation of intrinsics +will be chosen and code will work as expected although it could be slower. + +### Types + +There are several types representing packed values vector registers, each type is +implemented as a structure based on a one SIMD register. + +- cv::v_uint8 and cv::v_int8: 8-bit integer values (unsigned/signed) - char +- cv::v_uint16 and cv::v_int16: 16-bit integer values (unsigned/signed) - short +- cv::v_uint32 and cv::v_int32: 32-bit integer values (unsigned/signed) - int +- cv::v_uint64 and cv::v_int64: 64-bit integer values (unsigned/signed) - int64 +- cv::v_float32: 32-bit floating point values (signed) - float +- cv::v_float64: 64-bit floating point values (signed) - double + +Exact bit length(and value quantity) of listed types is compile time deduced and depends on architecture SIMD +capabilities chosen as available during compilation of the library. All the types contains __nlanes__ enumeration +to check for exact value quantity of the type. + +In case the exact bit length of the type is important it is possible to use specific fixed length register types. + +There are several types representing 128-bit registers. + +- cv::v_uint8x16 and cv::v_int8x16: sixteen 8-bit integer values (unsigned/signed) - char +- cv::v_uint16x8 and cv::v_int16x8: eight 16-bit integer values (unsigned/signed) - short +- cv::v_uint32x4 and cv::v_int32x4: four 32-bit integer values (unsigned/signed) - int +- cv::v_uint64x2 and cv::v_int64x2: two 64-bit integer values (unsigned/signed) - int64 +- cv::v_float32x4: four 32-bit floating point values (signed) - float +- cv::v_float64x2: two 64-bit floating point values (signed) - double + +There are several types representing 256-bit registers. + +- cv::v_uint8x32 and cv::v_int8x32: thirty two 8-bit integer values (unsigned/signed) - char +- cv::v_uint16x16 and cv::v_int16x16: sixteen 16-bit integer values (unsigned/signed) - short +- cv::v_uint32x8 and cv::v_int32x8: eight 32-bit integer values (unsigned/signed) - int +- cv::v_uint64x4 and cv::v_int64x4: four 64-bit integer values (unsigned/signed) - int64 +- cv::v_float32x8: eight 32-bit floating point values (signed) - float +- cv::v_float64x4: four 64-bit floating point values (signed) - double + +@note +256 bit registers at the moment implemented for AVX2 SIMD extension only, if you want to use this type directly, +don't forget to check the CV_SIMD256 preprocessor definition: +@code +#if CV_SIMD256 +//... +#endif +@endcode + +There are several types representing 512-bit registers. + +- cv::v_uint8x64 and cv::v_int8x64: sixty four 8-bit integer values (unsigned/signed) - char +- cv::v_uint16x32 and cv::v_int16x32: thirty two 16-bit integer values (unsigned/signed) - short +- cv::v_uint32x16 and cv::v_int32x16: sixteen 32-bit integer values (unsigned/signed) - int +- cv::v_uint64x8 and cv::v_int64x8: eight 64-bit integer values (unsigned/signed) - int64 +- cv::v_float32x16: sixteen 32-bit floating point values (signed) - float +- cv::v_float64x8: eight 64-bit floating point values (signed) - double +@note +512 bit registers at the moment implemented for AVX512 SIMD extension only, if you want to use this type directly, +don't forget to check the CV_SIMD512 preprocessor definition. + +@note +cv::v_float64x2 is not implemented in NEON variant, if you want to use this type, don't forget to +check the CV_SIMD128_64F preprocessor definition. + +### Load and store operations + +These operations allow to set contents of the register explicitly or by loading it from some memory +block and to save contents of the register to memory block. + +There are variable size register load operations that provide result of maximum available size +depending on chosen platform capabilities. +- Constructors: +@ref v_reg::v_reg(const _Tp *ptr) "from memory", +- Other create methods: +vx_setall_s8, vx_setall_u8, ..., +vx_setzero_u8, vx_setzero_s8, ... +- Memory load operations: +vx_load, vx_load_aligned, vx_load_low, vx_load_halves, +- Memory operations with expansion of values: +vx_load_expand, vx_load_expand_q + +Also there are fixed size register load/store operations. + +For 128 bit registers +- Constructors: +@ref v_reg::v_reg(const _Tp *ptr) "from memory", +@ref v_reg::v_reg(_Tp s0, _Tp s1) "from two values", ... +- Other create methods: +@ref v_setall_s8, @ref v_setall_u8, ..., +@ref v_setzero_u8, @ref v_setzero_s8, ... +- Memory load operations: +@ref v_load, @ref v_load_aligned, @ref v_load_low, @ref v_load_halves, +- Memory operations with expansion of values: +@ref v_load_expand, @ref v_load_expand_q + +For 256 bit registers(check CV_SIMD256 preprocessor definition) +- Constructors: +@ref v_reg::v_reg(const _Tp *ptr) "from memory", +@ref v_reg::v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3) "from four values", ... +- Other create methods: +@ref v256_setall_s8, @ref v256_setall_u8, ..., +@ref v256_setzero_u8, @ref v256_setzero_s8, ... +- Memory load operations: +@ref v256_load, @ref v256_load_aligned, @ref v256_load_low, @ref v256_load_halves, +- Memory operations with expansion of values: +@ref v256_load_expand, @ref v256_load_expand_q + +For 512 bit registers(check CV_SIMD512 preprocessor definition) +- Constructors: +@ref v_reg::v_reg(const _Tp *ptr) "from memory", +@ref v_reg::v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, _Tp s4, _Tp s5, _Tp s6, _Tp s7) "from eight values", ... +- Other create methods: +@ref v512_setall_s8, @ref v512_setall_u8, ..., +@ref v512_setzero_u8, @ref v512_setzero_s8, ... +- Memory load operations: +@ref v512_load, @ref v512_load_aligned, @ref v512_load_low, @ref v512_load_halves, +- Memory operations with expansion of values: +@ref v512_load_expand, @ref v512_load_expand_q + +Store to memory operations are similar across different platform capabilities: +@ref v_store, @ref v_store_aligned, +@ref v_store_high, @ref v_store_low + +### Value reordering + +These operations allow to reorder or recombine elements in one or multiple vectors. + +- Interleave, deinterleave (2, 3 and 4 channels): @ref v_load_deinterleave, @ref v_store_interleave +- Expand: @ref v_expand, @ref v_expand_low, @ref v_expand_high +- Pack: @ref v_pack, @ref v_pack_u, @ref v_pack_b, @ref v_rshr_pack, @ref v_rshr_pack_u, +@ref v_pack_store, @ref v_pack_u_store, @ref v_rshr_pack_store, @ref v_rshr_pack_u_store +- Recombine: @ref v_zip, @ref v_recombine, @ref v_combine_low, @ref v_combine_high +- Reverse: @ref v_reverse +- Extract: @ref v_extract + + +### Arithmetic, bitwise and comparison operations + +Element-wise binary and unary operations. + +- Arithmetics: +@ref operator +(const v_reg &a, const v_reg &b) "+", +@ref operator -(const v_reg &a, const v_reg &b) "-", +@ref operator *(const v_reg &a, const v_reg &b) "*", +@ref operator /(const v_reg &a, const v_reg &b) "/", +@ref v_mul_expand + +- Non-saturating arithmetics: @ref v_add_wrap, @ref v_sub_wrap + +- Bitwise shifts: +@ref operator <<(const v_reg &a, int s) "<<", +@ref operator >>(const v_reg &a, int s) ">>", +@ref v_shl, @ref v_shr + +- Bitwise logic: +@ref operator &(const v_reg &a, const v_reg &b) "&", +@ref operator |(const v_reg &a, const v_reg &b) "|", +@ref operator ^(const v_reg &a, const v_reg &b) "^", +@ref operator ~(const v_reg &a) "~" + +- Comparison: +@ref operator >(const v_reg &a, const v_reg &b) ">", +@ref operator >=(const v_reg &a, const v_reg &b) ">=", +@ref operator <(const v_reg &a, const v_reg &b) "<", +@ref operator <=(const v_reg &a, const v_reg &b) "<=", +@ref operator ==(const v_reg &a, const v_reg &b) "==", +@ref operator !=(const v_reg &a, const v_reg &b) "!=" + +- min/max: @ref v_min, @ref v_max + +### Reduce and mask + +Most of these operations return only one value. + +- Reduce: @ref v_reduce_min, @ref v_reduce_max, @ref v_reduce_sum, @ref v_popcount +- Mask: @ref v_signmask, @ref v_check_all, @ref v_check_any, @ref v_select + +### Other math + +- Some frequent operations: @ref v_sqrt, @ref v_invsqrt, @ref v_magnitude, @ref v_sqr_magnitude +- Absolute values: @ref v_abs, @ref v_absdiff, @ref v_absdiffs + +### Conversions + +Different type conversions and casts: + +- Rounding: @ref v_round, @ref v_floor, @ref v_ceil, @ref v_trunc, +- To float: @ref v_cvt_f32, @ref v_cvt_f64 +- Reinterpret: @ref v_reinterpret_as_u8, @ref v_reinterpret_as_s8, ... + +### Matrix operations + +In these operations vectors represent matrix rows/columns: @ref v_dotprod, @ref v_dotprod_fast, +@ref v_dotprod_expand, @ref v_dotprod_expand_fast, @ref v_matmul, @ref v_transpose4x4 + +### Usability + +Most operations are implemented only for some subset of the available types, following matrices +shows the applicability of different operations to the types. + +Regular integers: + +| Operations\\Types | uint 8 | int 8 | uint 16 | int 16 | uint 32 | int 32 | +|-------------------|:-:|:-:|:-:|:-:|:-:|:-:| +|load, store | x | x | x | x | x | x | +|interleave | x | x | x | x | x | x | +|expand | x | x | x | x | x | x | +|expand_low | x | x | x | x | x | x | +|expand_high | x | x | x | x | x | x | +|expand_q | x | x | | | | | +|add, sub | x | x | x | x | x | x | +|add_wrap, sub_wrap | x | x | x | x | | | +|mul_wrap | x | x | x | x | | | +|mul | x | x | x | x | x | x | +|mul_expand | x | x | x | x | x | | +|compare | x | x | x | x | x | x | +|shift | | | x | x | x | x | +|dotprod | | | | x | | x | +|dotprod_fast | | | | x | | x | +|dotprod_expand | x | x | x | x | | x | +|dotprod_expand_fast| x | x | x | x | | x | +|logical | x | x | x | x | x | x | +|min, max | x | x | x | x | x | x | +|absdiff | x | x | x | x | x | x | +|absdiffs | | x | | x | | | +|reduce | x | x | x | x | x | x | +|mask | x | x | x | x | x | x | +|pack | x | x | x | x | x | x | +|pack_u | x | | x | | | | +|pack_b | x | | | | | | +|unpack | x | x | x | x | x | x | +|extract | x | x | x | x | x | x | +|rotate (lanes) | x | x | x | x | x | x | +|cvt_flt32 | | | | | | x | +|cvt_flt64 | | | | | | x | +|transpose4x4 | | | | | x | x | +|reverse | x | x | x | x | x | x | +|extract_n | x | x | x | x | x | x | +|broadcast_element | | | | | x | x | + +Big integers: + +| Operations\\Types | uint 64 | int 64 | +|-------------------|:-:|:-:| +|load, store | x | x | +|add, sub | x | x | +|shift | x | x | +|logical | x | x | +|reverse | x | x | +|extract | x | x | +|rotate (lanes) | x | x | +|cvt_flt64 | | x | +|extract_n | x | x | + +Floating point: + +| Operations\\Types | float 32 | float 64 | +|-------------------|:-:|:-:| +|load, store | x | x | +|interleave | x | | +|add, sub | x | x | +|mul | x | x | +|div | x | x | +|compare | x | x | +|min, max | x | x | +|absdiff | x | x | +|reduce | x | | +|mask | x | x | +|unpack | x | x | +|cvt_flt32 | | x | +|cvt_flt64 | x | | +|sqrt, abs | x | x | +|float math | x | x | +|transpose4x4 | x | | +|extract | x | x | +|rotate (lanes) | x | x | +|reverse | x | x | +|extract_n | x | x | +|broadcast_element | x | | + + @{ */ + +template struct v_reg +{ +//! @cond IGNORED + typedef _Tp lane_type; + enum { nlanes = n }; +// !@endcond + + /** @brief Constructor + + Initializes register with data from memory + @param ptr pointer to memory block with data for register */ + explicit v_reg(const _Tp* ptr) { for( int i = 0; i < n; i++ ) s[i] = ptr[i]; } + + /** @brief Constructor + + Initializes register with two 64-bit values */ + v_reg(_Tp s0, _Tp s1) { s[0] = s0; s[1] = s1; } + + /** @brief Constructor + + Initializes register with four 32-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3) { s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; } + + /** @brief Constructor + + Initializes register with eight 16-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + } + + /** @brief Constructor + + Initializes register with sixteen 8-bit values */ + v_reg(_Tp s0, _Tp s1, _Tp s2, _Tp s3, + _Tp s4, _Tp s5, _Tp s6, _Tp s7, + _Tp s8, _Tp s9, _Tp s10, _Tp s11, + _Tp s12, _Tp s13, _Tp s14, _Tp s15) + { + s[0] = s0; s[1] = s1; s[2] = s2; s[3] = s3; + s[4] = s4; s[5] = s5; s[6] = s6; s[7] = s7; + s[8] = s8; s[9] = s9; s[10] = s10; s[11] = s11; + s[12] = s12; s[13] = s13; s[14] = s14; s[15] = s15; + } + + /** @brief Default constructor + + Does not initialize anything*/ + v_reg() {} + + /** @brief Copy constructor */ + v_reg(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + } + /** @brief Access first value + + Returns value of the first lane according to register type, for example: + @code{.cpp} + v_int32x4 r(1, 2, 3, 4); + int v = r.get0(); // returns 1 + v_uint64x2 r(1, 2); + uint64_t v = r.get0(); // returns 1 + @endcode + */ + _Tp get0() const { return s[0]; } + +//! @cond IGNORED + _Tp get(const int i) const { return s[i]; } + v_reg<_Tp, n> high() const + { + v_reg<_Tp, n> c; + int i; + for( i = 0; i < n/2; i++ ) + { + c.s[i] = s[i+(n/2)]; + c.s[i+(n/2)] = 0; + } + return c; + } + + static v_reg<_Tp, n> zero() + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = (_Tp)0; + return c; + } + + static v_reg<_Tp, n> all(_Tp s) + { + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = s; + return c; + } + + template v_reg<_Tp2, n2> reinterpret_as() const + { + size_t bytes = std::min(sizeof(_Tp2)*n2, sizeof(_Tp)*n); + v_reg<_Tp2, n2> c; + std::memcpy(&c.s[0], &s[0], bytes); + return c; + } + + v_reg& operator=(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + return *this; + } + + _Tp s[n]; +//! @endcond +}; + +/** @brief Sixteen 8-bit unsigned integer values */ +typedef v_reg v_uint8x16; +/** @brief Sixteen 8-bit signed integer values */ +typedef v_reg v_int8x16; +/** @brief Eight 16-bit unsigned integer values */ +typedef v_reg v_uint16x8; +/** @brief Eight 16-bit signed integer values */ +typedef v_reg v_int16x8; +/** @brief Four 32-bit unsigned integer values */ +typedef v_reg v_uint32x4; +/** @brief Four 32-bit signed integer values */ +typedef v_reg v_int32x4; +/** @brief Four 32-bit floating point values (single precision) */ +typedef v_reg v_float32x4; +/** @brief Two 64-bit floating point values (double precision) */ +typedef v_reg v_float64x2; +/** @brief Two 64-bit unsigned integer values */ +typedef v_reg v_uint64x2; +/** @brief Two 64-bit signed integer values */ +typedef v_reg v_int64x2; + +#if CV_SIMD256 +/** @brief Thirty two 8-bit unsigned integer values */ +typedef v_reg v_uint8x32; +/** @brief Thirty two 8-bit signed integer values */ +typedef v_reg v_int8x32; +/** @brief Sixteen 16-bit unsigned integer values */ +typedef v_reg v_uint16x16; +/** @brief Sixteen 16-bit signed integer values */ +typedef v_reg v_int16x16; +/** @brief Eight 32-bit unsigned integer values */ +typedef v_reg v_uint32x8; +/** @brief Eight 32-bit signed integer values */ +typedef v_reg v_int32x8; +/** @brief Eight 32-bit floating point values (single precision) */ +typedef v_reg v_float32x8; +/** @brief Four 64-bit floating point values (double precision) */ +typedef v_reg v_float64x4; +/** @brief Four 64-bit unsigned integer values */ +typedef v_reg v_uint64x4; +/** @brief Four 64-bit signed integer values */ +typedef v_reg v_int64x4; +#endif + +#if CV_SIMD512 +/** @brief Sixty four 8-bit unsigned integer values */ +typedef v_reg v_uint8x64; +/** @brief Sixty four 8-bit signed integer values */ +typedef v_reg v_int8x64; +/** @brief Thirty two 16-bit unsigned integer values */ +typedef v_reg v_uint16x32; +/** @brief Thirty two 16-bit signed integer values */ +typedef v_reg v_int16x32; +/** @brief Sixteen 32-bit unsigned integer values */ +typedef v_reg v_uint32x16; +/** @brief Sixteen 32-bit signed integer values */ +typedef v_reg v_int32x16; +/** @brief Sixteen 32-bit floating point values (single precision) */ +typedef v_reg v_float32x16; +/** @brief Eight 64-bit floating point values (double precision) */ +typedef v_reg v_float64x8; +/** @brief Eight 64-bit unsigned integer values */ +typedef v_reg v_uint64x8; +/** @brief Eight 64-bit signed integer values */ +typedef v_reg v_int64x8; +#endif + +enum { + simd128_width = 16, +#if CV_SIMD256 + simd256_width = 32, +#endif +#if CV_SIMD512 + simd512_width = 64, + simdmax_width = simd512_width +#elif CV_SIMD256 + simdmax_width = simd256_width +#else + simdmax_width = simd128_width +#endif +}; + +/** @brief Add values + +For all types. */ +template CV_INLINE v_reg<_Tp, n> operator+(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator+=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Subtract values + +For all types. */ +template CV_INLINE v_reg<_Tp, n> operator-(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator-=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Multiply values + +For 16- and 32-bit integer types and floating types. */ +template CV_INLINE v_reg<_Tp, n> operator*(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator*=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Divide values + +For floating types only. */ +template CV_INLINE v_reg<_Tp, n> operator/(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator/=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + + +/** @brief Bitwise AND + +Only for integer types. */ +template CV_INLINE v_reg<_Tp, n> operator&(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator&=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise OR + +Only for integer types. */ +template CV_INLINE v_reg<_Tp, n> operator|(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator|=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise XOR + +Only for integer types.*/ +template CV_INLINE v_reg<_Tp, n> operator^(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); +template CV_INLINE v_reg<_Tp, n>& operator^=(v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b); + +/** @brief Bitwise NOT + +Only for integer types.*/ +template CV_INLINE v_reg<_Tp, n> operator~(const v_reg<_Tp, n>& a); + + +#ifndef CV_DOXYGEN + +#define CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(macro_name, ...) \ +__CV_EXPAND(macro_name(uchar, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(schar, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(ushort, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(short, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(unsigned, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(int, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(uint64, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(int64, __VA_ARGS__)) \ + +#define CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(macro_name, ...) \ +__CV_EXPAND(macro_name(float, __VA_ARGS__)) \ +__CV_EXPAND(macro_name(double, __VA_ARGS__)) \ + +#define CV__HAL_INTRIN_EXPAND_WITH_ALL_TYPES(macro_name, ...) \ +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(macro_name, __VA_ARGS__) \ +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(macro_name, __VA_ARGS__) \ + +#define CV__HAL_INTRIN_IMPL_BIN_OP_(_Tp, bin_op) \ +template inline \ +v_reg<_Tp, n> operator bin_op (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return c; \ +} \ +template inline \ +v_reg<_Tp, n>& operator bin_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = saturate_cast<_Tp>(a.s[i] bin_op b.s[i]); \ + return a; \ +} + +#define CV__HAL_INTRIN_IMPL_BIN_OP(bin_op) CV__HAL_INTRIN_EXPAND_WITH_ALL_TYPES(CV__HAL_INTRIN_IMPL_BIN_OP_, bin_op) + +CV__HAL_INTRIN_IMPL_BIN_OP(+) +CV__HAL_INTRIN_IMPL_BIN_OP(-) +CV__HAL_INTRIN_IMPL_BIN_OP(*) +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(CV__HAL_INTRIN_IMPL_BIN_OP_, /) + +#define CV__HAL_INTRIN_IMPL_BIT_OP_(_Tp, bit_op) \ +template CV_INLINE \ +v_reg<_Tp, n> operator bit_op (const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return c; \ +} \ +template CV_INLINE \ +v_reg<_Tp, n>& operator bit_op##= (v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + for( int i = 0; i < n; i++ ) \ + a.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) bit_op \ + V_TypeTraits<_Tp>::reinterpret_int(b.s[i]))); \ + return a; \ +} + +#define CV__HAL_INTRIN_IMPL_BIT_OP(bit_op) \ +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(CV__HAL_INTRIN_IMPL_BIT_OP_, bit_op) \ +CV__HAL_INTRIN_EXPAND_WITH_FP_TYPES(CV__HAL_INTRIN_IMPL_BIT_OP_, bit_op) /* TODO: FIXIT remove this after masks refactoring */ + + +CV__HAL_INTRIN_IMPL_BIT_OP(&) +CV__HAL_INTRIN_IMPL_BIT_OP(|) +CV__HAL_INTRIN_IMPL_BIT_OP(^) + +#define CV__HAL_INTRIN_IMPL_BITWISE_NOT_(_Tp, dummy) \ +template CV_INLINE \ +v_reg<_Tp, n> operator ~ (const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int(~V_TypeTraits<_Tp>::reinterpret_int(a.s[i])); \ + return c; \ +} \ + +CV__HAL_INTRIN_EXPAND_WITH_INTEGER_TYPES(CV__HAL_INTRIN_IMPL_BITWISE_NOT_, ~) + +#endif // !CV_DOXYGEN + + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_MATH_FUNC(func, cfunc, _Tp2) \ +template inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp2, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i]); \ + return c; \ +} + +/** @brief Square root of elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC(v_sqrt, std::sqrt, _Tp) + +//! @cond IGNORED +OPENCV_HAL_IMPL_MATH_FUNC(v_sin, std::sin, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_cos, std::cos, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_exp, std::exp, _Tp) +OPENCV_HAL_IMPL_MATH_FUNC(v_log, std::log, _Tp) +//! @endcond + +/** @brief Absolute value of elements + +Only for floating point types.*/ +OPENCV_HAL_IMPL_MATH_FUNC(v_abs, (typename V_TypeTraits<_Tp>::abs_type)std::abs, + typename V_TypeTraits<_Tp>::abs_type) + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_MINMAX_FUNC(func, cfunc) \ +template inline v_reg<_Tp, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cfunc(a.s[i], b.s[i]); \ + return c; \ +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(func, cfunc) \ +template inline _Tp func(const v_reg<_Tp, n>& a) \ +{ \ + _Tp c = a.s[0]; \ + for( int i = 1; i < n; i++ ) \ + c = cfunc(c, a.s[i]); \ + return c; \ +} + +/** @brief Choose min values for each pair + +Scheme: +@code +{A1 A2 ...} +{B1 B2 ...} +-------------- +{min(A1,B1) min(A2,B2) ...} +@endcode +For all types except 64-bit integer. */ +OPENCV_HAL_IMPL_MINMAX_FUNC(v_min, std::min) + +/** @brief Choose max values for each pair + +Scheme: +@code +{A1 A2 ...} +{B1 B2 ...} +-------------- +{max(A1,B1) max(A2,B2) ...} +@endcode +For all types except 64-bit integer. */ +OPENCV_HAL_IMPL_MINMAX_FUNC(v_max, std::max) + +/** @brief Find one min value + +Scheme: +@code +{A1 A2 A3 ...} => min(A1,A2,A3,...) +@endcode +For all types except 64-bit integer and 64-bit floating point types. */ +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_min, std::min) + +/** @brief Find one max value + +Scheme: +@code +{A1 A2 A3 ...} => max(A1,A2,A3,...) +@endcode +For all types except 64-bit integer and 64-bit floating point types. */ +OPENCV_HAL_IMPL_REDUCE_MINMAX_FUNC(v_reduce_max, std::max) + +static const unsigned char popCountTable[] = +{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; +/** @brief Count the 1 bits in the vector lanes and return result as corresponding unsigned type + +Scheme: +@code +{A1 A2 A3 ...} => {popcount(A1), popcount(A2), popcount(A3), ...} +@endcode +For all integer types. */ +template +inline v_reg::abs_type, n> v_popcount(const v_reg<_Tp, n>& a) +{ + v_reg::abs_type, n> b = v_reg::abs_type, n>::zero(); + for (int i = 0; i < n*(int)sizeof(_Tp); i++) + b.s[i/sizeof(_Tp)] += popCountTable[v_reinterpret_as_u8(a).s[i]]; + return b; +} + + +//! @cond IGNORED +template +inline void v_minmax( const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& minval, v_reg<_Tp, n>& maxval ) +{ + for( int i = 0; i < n; i++ ) + { + minval.s[i] = std::min(a.s[i], b.s[i]); + maxval.s[i] = std::max(a.s[i], b.s[i]); + } +} +//! @endcond + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_CMP_OP(cmp_op) \ +template \ +inline v_reg<_Tp, n> operator cmp_op(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef typename V_TypeTraits<_Tp>::int_type itype; \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = V_TypeTraits<_Tp>::reinterpret_from_int((itype)-(int)(a.s[i] cmp_op b.s[i])); \ + return c; \ +} + +/** @brief Less-than comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(<) + +/** @brief Greater-than comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(>) + +/** @brief Less-than or equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(<=) + +/** @brief Greater-than or equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(>=) + +/** @brief Equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(==) + +/** @brief Not equal comparison + +For all types except 64-bit integer values. */ +OPENCV_HAL_IMPL_CMP_OP(!=) + +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} +template +inline v_reg v_not_nan(const v_reg& a) +{ + typedef typename V_TypeTraits::int_type itype; + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = V_TypeTraits::reinterpret_from_int((itype)-(int)(a.s[i] == a.s[i])); + return c; +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_ARITHM_OP(func, bin_op, cast_op, _Tp2) \ +template \ +inline v_reg<_Tp2, n> func(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + typedef _Tp2 rtype; \ + v_reg c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = cast_op(a.s[i] bin_op b.s[i]); \ + return c; \ +} + +/** @brief Add values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_add_wrap, +, (_Tp), _Tp) + +/** @brief Subtract values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_sub_wrap, -, (_Tp), _Tp) + +/** @brief Multiply values without saturation + +For 8- and 16-bit integer values. */ +OPENCV_HAL_IMPL_ARITHM_OP(v_mul_wrap, *, (_Tp), _Tp) + +//! @cond IGNORED +template inline T _absdiff(T a, T b) +{ + return a > b ? a - b : b - a; +} +//! @endcond + +/** @brief Absolute difference + +Returns \f$ |a - b| \f$ converted to corresponding unsigned type. +Example: +@code{.cpp} +v_int32x4 a, b; // {1, 2, 3, 4} and {4, 3, 2, 1} +v_uint32x4 c = v_absdiff(a, b); // result is {3, 1, 1, 3} +@endcode +For 8-, 16-, 32-bit integer source types. */ +template +inline v_reg::abs_type, n> v_absdiff(const v_reg<_Tp, n>& a, const v_reg<_Tp, n> & b) +{ + typedef typename V_TypeTraits<_Tp>::abs_type rtype; + v_reg c; + const rtype mask = (rtype)(std::numeric_limits<_Tp>::is_signed ? (1 << (sizeof(rtype)*8 - 1)) : 0); + for( int i = 0; i < n; i++ ) + { + rtype ua = a.s[i] ^ mask; + rtype ub = b.s[i] ^ mask; + c.s[i] = _absdiff(ua, ub); + } + return c; +} + +/** @overload + +For 32-bit floating point values */ +template inline v_reg v_absdiff(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @overload + +For 64-bit floating point values */ +template inline v_reg v_absdiff(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < c.nlanes; i++ ) + c.s[i] = _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @brief Saturating absolute difference + +Returns \f$ saturate(|a - b|) \f$ . +For 8-, 16-bit signed integer source types. */ +template +inline v_reg<_Tp, n> v_absdiffs(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++) + c.s[i] = saturate_cast<_Tp>(std::abs(a.s[i] - b.s[i])); + return c; +} + +/** @brief Inversed square root + +Returns \f$ 1/sqrt(a) \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_invsqrt(const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = 1.f/std::sqrt(a.s[i]); + return c; +} + +/** @brief Magnitude + +Returns \f$ sqrt(a^2 + b^2) \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = std::sqrt(a.s[i]*a.s[i] + b.s[i]*b.s[i]); + return c; +} + +/** @brief Square of the magnitude + +Returns \f$ a^2 + b^2 \f$ +For floating point types only. */ +template +inline v_reg<_Tp, n> v_sqr_magnitude(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = a.s[i]*a.s[i] + b.s[i]*b.s[i]; + return c; +} + +/** @brief Multiply and add + + Returns \f$ a*b + c \f$ + For floating point types and signed 32bit int only. */ +template +inline v_reg<_Tp, n> v_fma(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + v_reg<_Tp, n> d; + for( int i = 0; i < n; i++ ) + d.s[i] = a.s[i]*b.s[i] + c.s[i]; + return d; +} + +/** @brief A synonym for v_fma */ +template +inline v_reg<_Tp, n> v_muladd(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg<_Tp, n>& c) +{ + return v_fma(a, b, c); +} + +/** @brief Dot product of elements + +Multiply values in two registers and sum adjacent result pairs. + +Scheme: +@code + {A1 A2 ...} // 16-bit +x {B1 B2 ...} // 16-bit +------------- +{A1B1+A2B2 ...} // 32-bit + +@endcode +*/ +template inline v_reg::w_type, n/2> +v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1]; + return c; +} + +/** @brief Dot product of elements + +Same as cv::v_dotprod, but add a third element to the sum of adjacent pairs. +Scheme: +@code + {A1 A2 ...} // 16-bit +x {B1 B2 ...} // 16-bit +------------- + {A1B1+A2B2+C1 ...} // 32-bit +@endcode +*/ +template inline v_reg::w_type, n/2> +v_dotprod(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::w_type, n / 2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg s; + for( int i = 0; i < (n/2); i++ ) + s.s[i] = (w_type)a.s[i*2]*b.s[i*2] + (w_type)a.s[i*2+1]*b.s[i*2+1] + c.s[i]; + return s; +} + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod, but it may perform unorder sum between result pairs in some platforms, +this intrinsic can be used if the sum among all lanes is only matters +and also it should be yielding better performance on the affected platforms. + +*/ +template inline v_reg::w_type, n/2> +v_dotprod_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ return v_dotprod(a, b); } + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod_fast, but add a third element to the sum of adjacent pairs. +*/ +template inline v_reg::w_type, n/2> +v_dotprod_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::w_type, n / 2>& c) +{ return v_dotprod(a, b, c); } + +/** @brief Dot product of elements and expand + +Multiply values in two registers and expand the sum of adjacent result pairs. + +Scheme: +@code + {A1 A2 A3 A4 ...} // 8-bit +x {B1 B2 B3 B4 ...} // 8-bit +------------- + {A1B1+A2B2+A3B3+A4B4 ...} // 32-bit + +@endcode +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3]; + return s; +} + +/** @brief Dot product of elements + +Same as cv::v_dotprod_expand, but add a third element to the sum of adjacent pairs. +Scheme: +@code + {A1 A2 A3 A4 ...} // 8-bit +x {B1 B2 B3 B4 ...} // 8-bit +------------- + {A1B1+A2B2+A3B3+A4B4+C1 ...} // 32-bit +@endcode +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::q_type, n / 4>& c) +{ + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg s; + for( int i = 0; i < (n/4); i++ ) + s.s[i] = (q_type)a.s[i*4 ]*b.s[i*4 ] + (q_type)a.s[i*4 + 1]*b.s[i*4 + 1] + + (q_type)a.s[i*4 + 2]*b.s[i*4 + 2] + (q_type)a.s[i*4 + 3]*b.s[i*4 + 3] + c.s[i]; + return s; +} + +/** @brief Fast Dot product of elements and expand + +Multiply values in two registers and expand the sum of adjacent result pairs. + +Same as cv::v_dotprod_expand, but it may perform unorder sum between result pairs in some platforms, +this intrinsic can be used if the sum among all lanes is only matters +and also it should be yielding better performance on the affected platforms. + +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ return v_dotprod_expand(a, b); } + +/** @brief Fast Dot product of elements + +Same as cv::v_dotprod_expand_fast, but add a third element to the sum of adjacent pairs. +*/ +template inline v_reg::q_type, n/4> +v_dotprod_expand_fast(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + const v_reg::q_type, n / 4>& c) +{ return v_dotprod_expand(a, b, c); } + +/** @brief Multiply and expand + +Multiply values two registers and store results in two registers with wider pack type. +Scheme: +@code + {A B C D} // 32-bit +x {E F G H} // 32-bit +--------------- +{AE BF} // 64-bit + {CG DH} // 64-bit +@endcode +Example: +@code{.cpp} +v_uint32x4 a, b; // {1,2,3,4} and {2,2,2,2} +v_uint64x2 c, d; // results +v_mul_expand(a, b, c, d); // c, d = {2,4}, {6, 8} +@endcode +Implemented only for 16- and unsigned 32-bit source types (v_int16x8, v_uint16x8, v_uint32x4). +*/ +template inline void v_mul_expand(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg::w_type, n/2>& c, + v_reg::w_type, n/2>& d) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i]*b.s[i]; + d.s[i] = (w_type)a.s[i+(n/2)]*b.s[i+(n/2)]; + } +} + +/** @brief Multiply and extract high part + +Multiply values two registers and store high part of the results. +Implemented only for 16-bit source types (v_int16x8, v_uint16x8). Returns \f$ a*b >> 16 \f$ +*/ +template inline v_reg<_Tp, n> v_mul_hi(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg<_Tp, n> c; + for (int i = 0; i < n; i++) + c.s[i] = (_Tp)(((w_type)a.s[i] * b.s[i]) >> sizeof(_Tp)*8); + return c; +} + +//! @cond IGNORED +template inline void v_hsum(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& c) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = (w_type)a.s[i*2] + a.s[i*2+1]; + } +} +//! @endcond + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_SHIFT_OP(shift_op) \ +template inline v_reg<_Tp, n> operator shift_op(const v_reg<_Tp, n>& a, int imm) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = (_Tp)(a.s[i] shift_op imm); \ + return c; \ +} + +/** @brief Bitwise shift left + +For 16-, 32- and 64-bit integer values. */ +OPENCV_HAL_IMPL_SHIFT_OP(<< ) + +/** @brief Bitwise shift right + +For 16-, 32- and 64-bit integer values. */ +OPENCV_HAL_IMPL_SHIFT_OP(>> ) + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(suffix,opA,opB) \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> b; \ + for (int i = 0; i < n; i++) \ + { \ + int sIndex = i opA imm; \ + if (0 <= sIndex && sIndex < n) \ + { \ + b.s[i] = a.s[sIndex]; \ + } \ + else \ + { \ + b.s[i] = 0; \ + } \ + } \ + return b; \ +} \ +template inline v_reg<_Tp, n> v_rotate_##suffix(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tp, n> c; \ + for (int i = 0; i < n; i++) \ + { \ + int aIndex = i opA imm; \ + int bIndex = i opA imm opB n; \ + if (0 <= bIndex && bIndex < n) \ + { \ + c.s[i] = b.s[bIndex]; \ + } \ + else if (0 <= aIndex && aIndex < n) \ + { \ + c.s[i] = a.s[aIndex]; \ + } \ + else \ + { \ + c.s[i] = 0; \ + } \ + } \ + return c; \ +} + +/** @brief Element shift left among vector + +For all type */ +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(left, -, +) + +/** @brief Element shift right among vector + +For all type */ +OPENCV_HAL_IMPL_ROTATE_SHIFT_OP(right, +, -) + +/** @brief Sum packed values + +Scheme: +@code +{A1 A2 A3 ...} => sum{A1,A2,A3,...} +@endcode +*/ +template inline typename V_TypeTraits<_Tp>::sum_type v_reduce_sum(const v_reg<_Tp, n>& a) +{ + typename V_TypeTraits<_Tp>::sum_type c = a.s[0]; + for( int i = 1; i < n; i++ ) + c += a.s[i]; + return c; +} + +/** @brief Sums all elements of each input vector, returns the vector of sums + + Scheme: + @code + result[0] = a[0] + a[1] + a[2] + a[3] + result[1] = b[0] + b[1] + b[2] + b[3] + result[2] = c[0] + c[1] + c[2] + c[3] + result[3] = d[0] + d[1] + d[2] + d[3] + @endcode +*/ +template inline v_reg v_reduce_sum4(const v_reg& a, const v_reg& b, + const v_reg& c, const v_reg& d) +{ + v_reg r; + for(int i = 0; i < (n/4); i++) + { + r.s[i*4 + 0] = a.s[i*4 + 0] + a.s[i*4 + 1] + a.s[i*4 + 2] + a.s[i*4 + 3]; + r.s[i*4 + 1] = b.s[i*4 + 0] + b.s[i*4 + 1] + b.s[i*4 + 2] + b.s[i*4 + 3]; + r.s[i*4 + 2] = c.s[i*4 + 0] + c.s[i*4 + 1] + c.s[i*4 + 2] + c.s[i*4 + 3]; + r.s[i*4 + 3] = d.s[i*4 + 0] + d.s[i*4 + 1] + d.s[i*4 + 2] + d.s[i*4 + 3]; + } + return r; +} + +/** @brief Sum absolute differences of values + +Scheme: +@code +{A1 A2 A3 ...} {B1 B2 B3 ...} => sum{ABS(A1-B1),abs(A2-B2),abs(A3-B3),...} +@endcode +For all types except 64-bit types.*/ +template inline typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type v_reduce_sad(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typename V_TypeTraits< typename V_TypeTraits<_Tp>::abs_type >::sum_type c = _absdiff(a.s[0], b.s[0]); + for (int i = 1; i < n; i++) + c += _absdiff(a.s[i], b.s[i]); + return c; +} + +/** @brief Get negative values mask +@deprecated v_signmask depends on a lane count heavily and therefore isn't universal enough + +Returned value is a bit mask with bits set to 1 on places corresponding to negative packed values indexes. +Example: +@code{.cpp} +v_int32x4 r; // set to {-1, -1, 1, 1} +int mask = v_signmask(r); // mask = 3 <== 00000000 00000000 00000000 00000011 +@endcode +*/ +template inline int v_signmask(const v_reg<_Tp, n>& a) +{ + int mask = 0; + for( int i = 0; i < n; i++ ) + mask |= (V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0) << i; + return mask; +} + +/** @brief Get first negative lane index + +Returned value is an index of first negative lane (undefined for input of all positive values) +Example: +@code{.cpp} +v_int32x4 r; // set to {0, 0, -1, -1} +int idx = v_heading_zeros(r); // idx = 2 +@endcode +*/ +template inline int v_scan_forward(const v_reg<_Tp, n>& a) +{ + for (int i = 0; i < n; i++) + if(V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0) + return i; + return 0; +} + +/** @brief Check if all packed values are less than zero + +Unsigned values will be casted to signed: `uchar 254 => char -2`. +*/ +template inline bool v_check_all(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) >= 0 ) + return false; + return true; +} + +/** @brief Check if any of packed values is less than zero + +Unsigned values will be casted to signed: `uchar 254 => char -2`. +*/ +template inline bool v_check_any(const v_reg<_Tp, n>& a) +{ + for( int i = 0; i < n; i++ ) + if( V_TypeTraits<_Tp>::reinterpret_int(a.s[i]) < 0 ) + return true; + return false; +} + +/** @brief Per-element select (blend operation) + +Return value will be built by combining values _a_ and _b_ using the following scheme: + result[i] = mask[i] ? a[i] : b[i]; + +@note: _mask_ element values are restricted to these values: +- 0: select element from _b_ +- 0xff/0xffff/etc: select element from _a_ +(fully compatible with bitwise-based operator) +*/ +template inline v_reg<_Tp, n> v_select(const v_reg<_Tp, n>& mask, + const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef V_TypeTraits<_Tp> Traits; + typedef typename Traits::int_type int_type; + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + { + int_type m = Traits::reinterpret_int(mask.s[i]); + CV_DbgAssert(m == 0 || m == (~(int_type)0)); // restrict mask values: 0 or 0xff/0xffff/etc + c.s[i] = m ? a.s[i] : b.s[i]; + } + return c; +} + +/** @brief Expand values to the wider pack type + +Copy contents of register to two registers with 2x wider pack type. +Scheme: +@code + int32x4 int64x2 int64x2 +{A B C D} ==> {A B} , {C D} +@endcode */ +template inline void v_expand(const v_reg<_Tp, n>& a, + v_reg::w_type, n/2>& b0, + v_reg::w_type, n/2>& b1) +{ + for( int i = 0; i < (n/2); i++ ) + { + b0.s[i] = a.s[i]; + b1.s[i] = a.s[i+(n/2)]; + } +} + +/** @brief Expand lower values to the wider pack type + +Same as cv::v_expand, but return lower half of the vector. + +Scheme: +@code + int32x4 int64x2 +{A B C D} ==> {A B} +@endcode */ +template +inline v_reg::w_type, n/2> +v_expand_low(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i]; + return b; +} + +/** @brief Expand higher values to the wider pack type + +Same as cv::v_expand_low, but expand higher half of the vector instead. + +Scheme: +@code + int32x4 int64x2 +{A B C D} ==> {C D} +@endcode */ +template +inline v_reg::w_type, n/2> +v_expand_high(const v_reg<_Tp, n>& a) +{ + v_reg::w_type, n/2> b; + for( int i = 0; i < (n/2); i++ ) + b.s[i] = a.s[i+(n/2)]; + return b; +} + +//! @cond IGNORED +template inline v_reg::int_type, n> + v_reinterpret_as_int(const v_reg<_Tp, n>& a) +{ + v_reg::int_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_int(a.s[i]); + return c; +} + +template inline v_reg::uint_type, n> + v_reinterpret_as_uint(const v_reg<_Tp, n>& a) +{ + v_reg::uint_type, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = V_TypeTraits<_Tp>::reinterpret_uint(a.s[i]); + return c; +} +//! @endcond + +/** @brief Interleave two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A1 B1 A2 B2} and {A3 B3 A4 B4} +@endcode +For all types except 64-bit. +*/ +template inline void v_zip( const v_reg<_Tp, n>& a0, const v_reg<_Tp, n>& a1, + v_reg<_Tp, n>& b0, v_reg<_Tp, n>& b1 ) +{ + int i; + for( i = 0; i < n/2; i++ ) + { + b0.s[i*2] = a0.s[i]; + b0.s[i*2+1] = a1.s[i]; + } + for( ; i < n; i++ ) + { + b1.s[i*2-n] = a0.s[i]; + b1.s[i*2-n+1] = a1.s[i]; + } +} + +/** @brief Load register contents from memory + +@param ptr pointer to memory block with data +@return register object + +@note Returned type will be detected from passed pointer type, for example uchar ==> cv::v_uint8x16, int ==> cv::v_int32x4, etc. + +@note Use vx_load version to get maximum available register length result + +@note Alignment requirement: +if CV_STRONG_ALIGNMENT=1 then passed pointer must be aligned (`sizeof(lane type)` should be enough). +Do not cast pointer types without runtime check for pointer alignment (like `uchar*` => `int*`). + */ +template +inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_load(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + return v_reg<_Tp, simd128_width / sizeof(_Tp)>(ptr); +} + +#if CV_SIMD256 +/** @brief Load 256-bit length register contents from memory + +@param ptr pointer to memory block with data +@return register object + +@note Returned type will be detected from passed pointer type, for example uchar ==> cv::v_uint8x32, int ==> cv::v_int32x8, etc. + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load version to get maximum available register length result + +@note Alignment requirement: +if CV_STRONG_ALIGNMENT=1 then passed pointer must be aligned (`sizeof(lane type)` should be enough). +Do not cast pointer types without runtime check for pointer alignment (like `uchar*` => `int*`). + */ +template +inline v_reg<_Tp, simd256_width / sizeof(_Tp)> v256_load(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + return v_reg<_Tp, simd256_width / sizeof(_Tp)>(ptr); +} +#endif + +#if CV_SIMD512 +/** @brief Load 512-bit length register contents from memory + +@param ptr pointer to memory block with data +@return register object + +@note Returned type will be detected from passed pointer type, for example uchar ==> cv::v_uint8x64, int ==> cv::v_int32x16, etc. + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load version to get maximum available register length result + +@note Alignment requirement: +if CV_STRONG_ALIGNMENT=1 then passed pointer must be aligned (`sizeof(lane type)` should be enough). +Do not cast pointer types without runtime check for pointer alignment (like `uchar*` => `int*`). + */ +template +inline v_reg<_Tp, simd512_width / sizeof(_Tp)> v512_load(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + return v_reg<_Tp, simd512_width / sizeof(_Tp)>(ptr); +} +#endif + +/** @brief Load register contents from memory (aligned) + +similar to cv::v_load, but source memory block should be aligned (to 16-byte boundary in case of SIMD128, 32-byte - SIMD256, etc) + +@note Use vx_load_aligned version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_load_aligned(const _Tp* ptr) +{ + CV_Assert(isAligned)>(ptr)); + return v_reg<_Tp, simd128_width / sizeof(_Tp)>(ptr); +} + +#if CV_SIMD256 +/** @brief Load register contents from memory (aligned) + +similar to cv::v256_load, but source memory block should be aligned (to 32-byte boundary in case of SIMD256, 64-byte - SIMD512, etc) + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load_aligned version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd256_width / sizeof(_Tp)> v256_load_aligned(const _Tp* ptr) +{ + CV_Assert(isAligned)>(ptr)); + return v_reg<_Tp, simd256_width / sizeof(_Tp)>(ptr); +} +#endif + +#if CV_SIMD512 +/** @brief Load register contents from memory (aligned) + +similar to cv::v512_load, but source memory block should be aligned (to 64-byte boundary in case of SIMD512, etc) + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load_aligned version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd512_width / sizeof(_Tp)> v512_load_aligned(const _Tp* ptr) +{ + CV_Assert(isAligned)>(ptr)); + return v_reg<_Tp, simd512_width / sizeof(_Tp)>(ptr); +} +#endif + +/** @brief Load 64-bits of data to lower part (high part is undefined). + +@param ptr memory block containing data for first half (0..n/2) + +@code{.cpp} +int lo[2] = { 1, 2 }; +v_int32x4 r = v_load_low(lo); +@endcode + +@note Use vx_load_low version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_load_low(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_reg<_Tp, simd128_width / sizeof(_Tp)> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +#if CV_SIMD256 +/** @brief Load 128-bits of data to lower part (high part is undefined). + +@param ptr memory block containing data for first half (0..n/2) + +@code{.cpp} +int lo[4] = { 1, 2, 3, 4 }; +v_int32x8 r = v256_load_low(lo); +@endcode + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load_low version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd256_width / sizeof(_Tp)> v256_load_low(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_reg<_Tp, simd256_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes / 2; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +#if CV_SIMD512 +/** @brief Load 256-bits of data to lower part (high part is undefined). + +@param ptr memory block containing data for first half (0..n/2) + +@code{.cpp} +int lo[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +v_int32x16 r = v512_load_low(lo); +@endcode + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load_low version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd512_width / sizeof(_Tp)> v512_load_low(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_reg<_Tp, simd512_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes / 2; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +/** @brief Load register contents from two memory blocks + +@param loptr memory block containing data for first half (0..n/2) +@param hiptr memory block containing data for second half (n/2..n) + +@code{.cpp} +int lo[2] = { 1, 2 }, hi[2] = { 3, 4 }; +v_int32x4 r = v_load_halves(lo, hi); +@endcode + +@note Use vx_load_halves version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_load_halves(const _Tp* loptr, const _Tp* hiptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(loptr)); + CV_Assert(isAligned(hiptr)); +#endif + v_reg<_Tp, simd128_width / sizeof(_Tp)> c; + for( int i = 0; i < c.nlanes/2; i++ ) + { + c.s[i] = loptr[i]; + c.s[i+c.nlanes/2] = hiptr[i]; + } + return c; +} + +#if CV_SIMD256 +/** @brief Load register contents from two memory blocks + +@param loptr memory block containing data for first half (0..n/2) +@param hiptr memory block containing data for second half (n/2..n) + +@code{.cpp} +int lo[4] = { 1, 2, 3, 4 }, hi[4] = { 5, 6, 7, 8 }; +v_int32x8 r = v256_load_halves(lo, hi); +@endcode + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load_halves version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd256_width / sizeof(_Tp)> v256_load_halves(const _Tp* loptr, const _Tp* hiptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(loptr)); + CV_Assert(isAligned(hiptr)); +#endif + v_reg<_Tp, simd256_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes / 2; i++) + { + c.s[i] = loptr[i]; + c.s[i + c.nlanes / 2] = hiptr[i]; + } + return c; +} +#endif + +#if CV_SIMD512 +/** @brief Load register contents from two memory blocks + +@param loptr memory block containing data for first half (0..n/2) +@param hiptr memory block containing data for second half (n/2..n) + +@code{.cpp} +int lo[4] = { 1, 2, 3, 4, 5, 6, 7, 8 }, hi[4] = { 9, 10, 11, 12, 13, 14, 15, 16 }; +v_int32x16 r = v512_load_halves(lo, hi); +@endcode + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load_halves version to get maximum available register length result +*/ +template +inline v_reg<_Tp, simd512_width / sizeof(_Tp)> v512_load_halves(const _Tp* loptr, const _Tp* hiptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(loptr)); + CV_Assert(isAligned(hiptr)); +#endif + v_reg<_Tp, simd512_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes / 2; i++) + { + c.s[i] = loptr[i]; + c.s[i + c.nlanes / 2] = hiptr[i]; + } + return c; +} +#endif + +/** @brief Load register contents from memory with double expand + +Same as cv::v_load, but result pack type will be 2x wider than memory type. + +@code{.cpp} +short buf[4] = {1, 2, 3, 4}; // type is int16 +v_int32x4 r = v_load_expand(buf); // r = {1, 2, 3, 4} - type is int32 +@endcode +For 8-, 16-, 32-bit integer source types. + +@note Use vx_load_expand version to get maximum available register length result +*/ +template +inline v_reg::w_type, simd128_width / sizeof(typename V_TypeTraits<_Tp>::w_type)> +v_load_expand(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +#if CV_SIMD256 +/** @brief Load register contents from memory with double expand + +Same as cv::v256_load, but result pack type will be 2x wider than memory type. + +@code{.cpp} +short buf[8] = {1, 2, 3, 4, 5, 6, 7, 8}; // type is int16 +v_int32x8 r = v256_load_expand(buf); // r = {1, 2, 3, 4, 5, 6, 7, 8} - type is int32 +@endcode +For 8-, 16-, 32-bit integer source types. + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load_expand version to get maximum available register length result +*/ +template +inline v_reg::w_type, simd256_width / sizeof(typename V_TypeTraits<_Tp>::w_type)> +v256_load_expand(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for (int i = 0; i < c.nlanes; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +#if CV_SIMD512 +/** @brief Load register contents from memory with double expand + +Same as cv::v512_load, but result pack type will be 2x wider than memory type. + +@code{.cpp} +short buf[8] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // type is int16 +v_int32x16 r = v512_load_expand(buf); // r = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - type is int32 +@endcode +For 8-, 16-, 32-bit integer source types. + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load_expand version to get maximum available register length result +*/ +template +inline v_reg::w_type, simd512_width / sizeof(typename V_TypeTraits<_Tp>::w_type)> +v512_load_expand(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg c; + for (int i = 0; i < c.nlanes; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +/** @brief Load register contents from memory with quad expand + +Same as cv::v_load_expand, but result type is 4 times wider than source. +@code{.cpp} +char buf[4] = {1, 2, 3, 4}; // type is int8 +v_int32x4 r = v_load_expand_q(buf); // r = {1, 2, 3, 4} - type is int32 +@endcode +For 8-bit integer source types. + +@note Use vx_load_expand_q version to get maximum available register length result +*/ +template +inline v_reg::q_type, simd128_width / sizeof(typename V_TypeTraits<_Tp>::q_type)> +v_load_expand_q(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg c; + for( int i = 0; i < c.nlanes; i++ ) + { + c.s[i] = ptr[i]; + } + return c; +} + +#if CV_SIMD256 +/** @brief Load register contents from memory with quad expand + +Same as cv::v256_load_expand, but result type is 4 times wider than source. +@code{.cpp} +char buf[8] = {1, 2, 3, 4, 5, 6, 7, 8}; // type is int8 +v_int32x8 r = v256_load_expand_q(buf); // r = {1, 2, 3, 4, 5, 6, 7, 8} - type is int32 +@endcode +For 8-bit integer source types. + +@note Check CV_SIMD256 preprocessor definition prior to use. +Use vx_load_expand_q version to get maximum available register length result +*/ +template +inline v_reg::q_type, simd256_width / sizeof(typename V_TypeTraits<_Tp>::q_type)> +v256_load_expand_q(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg c; + for (int i = 0; i < c.nlanes; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +#if CV_SIMD512 +/** @brief Load register contents from memory with quad expand + +Same as cv::v512_load_expand, but result type is 4 times wider than source. +@code{.cpp} +char buf[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // type is int8 +v_int32x16 r = v512_load_expand_q(buf); // r = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - type is int32 +@endcode +For 8-bit integer source types. + +@note Check CV_SIMD512 preprocessor definition prior to use. +Use vx_load_expand_q version to get maximum available register length result +*/ +template +inline v_reg::q_type, simd512_width / sizeof(typename V_TypeTraits<_Tp>::q_type)> +v512_load_expand_q(const _Tp* ptr) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + typedef typename V_TypeTraits<_Tp>::q_type q_type; + v_reg c; + for (int i = 0; i < c.nlanes; i++) + { + c.s[i] = ptr[i]; + } + return c; +} +#endif + +/** @brief Load and deinterleave (2 channels) + +Load data from memory deinterleave and store to 2 registers. +Scheme: +@code +{A1 B1 A2 B2 ...} ==> {A1 A2 ...}, {B1 B2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + a.s[i] = ptr[i2]; + b.s[i] = ptr[i2+1]; + } +} + +/** @brief Load and deinterleave (3 channels) + +Load data from memory deinterleave and store to 3 registers. +Scheme: +@code +{A1 B1 C1 A2 B2 C2 ...} ==> {A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + a.s[i] = ptr[i3]; + b.s[i] = ptr[i3+1]; + c.s[i] = ptr[i3+2]; + } +} + +/** @brief Load and deinterleave (4 channels) + +Load data from memory deinterleave and store to 4 registers. +Scheme: +@code +{A1 B1 C1 D1 A2 B2 C2 D2 ...} ==> {A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...}, {D1 D2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_load_deinterleave(const _Tp* ptr, v_reg<_Tp, n>& a, + v_reg<_Tp, n>& b, v_reg<_Tp, n>& c, + v_reg<_Tp, n>& d) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + a.s[i] = ptr[i4]; + b.s[i] = ptr[i4+1]; + c.s[i] = ptr[i4+2]; + d.s[i] = ptr[i4+3]; + } +} + +/** @brief Interleave and store (2 channels) + +Interleave and store data from 2 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...} ==> {A1 B1 A2 B2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i2; + for( i = i2 = 0; i < n; i++, i2 += 2 ) + { + ptr[i2] = a.s[i]; + ptr[i2+1] = b.s[i]; + } +} + +/** @brief Interleave and store (3 channels) + +Interleave and store data from 3 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...} ==> {A1 B1 C1 A2 B2 C2 ...} +@endcode +For all types except 64-bit. */ +template +inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i3; + for( i = i3 = 0; i < n; i++, i3 += 3 ) + { + ptr[i3] = a.s[i]; + ptr[i3+1] = b.s[i]; + ptr[i3+2] = c.s[i]; + } +} + +/** @brief Interleave and store (4 channels) + +Interleave and store data from 4 registers to memory. +Scheme: +@code +{A1 A2 ...}, {B1 B2 ...}, {C1 C2 ...}, {D1 D2 ...} ==> {A1 B1 C1 D1 A2 B2 C2 D2 ...} +@endcode +For all types except 64-bit. */ +template inline void v_store_interleave( _Tp* ptr, const v_reg<_Tp, n>& a, + const v_reg<_Tp, n>& b, const v_reg<_Tp, n>& c, + const v_reg<_Tp, n>& d, + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + int i, i4; + for( i = i4 = 0; i < n; i++, i4 += 4 ) + { + ptr[i4] = a.s[i]; + ptr[i4+1] = b.s[i]; + ptr[i4+2] = c.s[i]; + ptr[i4+3] = d.s[i]; + } +} + +/** @brief Store data to memory + +Store register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B C D} +@endcode +Pointer can be unaligned. */ +template +inline void v_store(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < n; i++ ) + ptr[i] = a.s[i]; +} + +template +inline void v_store(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + v_store(ptr, a); +} + +/** @brief Store data to memory (lower half) + +Store lower half of register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B} +@endcode */ +template +inline void v_store_low(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i]; +} + +/** @brief Store data to memory (higher half) + +Store higher half of register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {C D} +@endcode */ +template +inline void v_store_high(_Tp* ptr, const v_reg<_Tp, n>& a) +{ +#if CV_STRONG_ALIGNMENT + CV_Assert(isAligned(ptr)); +#endif + for( int i = 0; i < (n/2); i++ ) + ptr[i] = a.s[i+(n/2)]; +} + +/** @brief Store data to memory (aligned) + +Store register contents to memory. +Scheme: +@code + REG {A B C D} ==> MEM {A B C D} +@endcode +Pointer __should__ be aligned by 16-byte boundary. */ +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +template +inline void v_store_aligned_nocache(_Tp* ptr, const v_reg<_Tp, n>& a) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +template +inline void v_store_aligned(_Tp* ptr, const v_reg<_Tp, n>& a, hal::StoreMode /*mode*/) +{ + CV_Assert(isAligned)>(ptr)); + v_store(ptr, a); +} + +/** @brief Combine vector from first elements of two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A1 A2 B1 B2} +@endcode +For all types except 64-bit. */ +template +inline v_reg<_Tp, n> v_combine_low(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i]; + c.s[i+(n/2)] = b.s[i]; + } + return c; +} + +/** @brief Combine vector from last elements of two vectors + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +--------------- + {A3 A4 B3 B4} +@endcode +For all types except 64-bit. */ +template +inline v_reg<_Tp, n> v_combine_high(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < (n/2); i++ ) + { + c.s[i] = a.s[i+(n/2)]; + c.s[i+(n/2)] = b.s[i+(n/2)]; + } + return c; +} + +/** @brief Combine two vectors from lower and higher parts of two other vectors + +@code{.cpp} +low = cv::v_combine_low(a, b); +high = cv::v_combine_high(a, b); +@endcode */ +template +inline void v_recombine(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b, + v_reg<_Tp, n>& low, v_reg<_Tp, n>& high) +{ + for( int i = 0; i < (n/2); i++ ) + { + low.s[i] = a.s[i]; + low.s[i+(n/2)] = b.s[i]; + high.s[i] = a.s[i+(n/2)]; + high.s[i+(n/2)] = b.s[i+(n/2)]; + } +} + +/** @brief Vector reverse order + +Reverse the order of the vector +Scheme: +@code + REG {A1 ... An} ==> REG {An ... A1} +@endcode +For all types. */ +template +inline v_reg<_Tp, n> v_reverse(const v_reg<_Tp, n>& a) +{ + v_reg<_Tp, n> c; + for( int i = 0; i < n; i++ ) + c.s[i] = a.s[n-i-1]; + return c; +} + +/** @brief Vector extract + +Scheme: +@code + {A1 A2 A3 A4} + {B1 B2 B3 B4} +======================== +shift = 1 {A2 A3 A4 B1} +shift = 2 {A3 A4 B1 B2} +shift = 3 {A4 B1 B2 B3} +@endcode +Restriction: 0 <= shift < nlanes + +Usage: +@code +v_int32x4 a, b, c; +c = v_extract<2>(a, b); +@endcode +For all types. */ +template +inline v_reg<_Tp, n> v_extract(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + v_reg<_Tp, n> r; + const int shift = n - s; + int i = 0; + for (; i < shift; ++i) + r.s[i] = a.s[i+s]; + for (; i < n; ++i) + r.s[i] = b.s[i-shift]; + return r; +} + +/** @brief Vector extract + +Scheme: +Return the s-th element of v. +Restriction: 0 <= s < nlanes + +Usage: +@code +v_int32x4 a; +int r; +r = v_extract_n<2>(a); +@endcode +For all types. */ +template +inline _Tp v_extract_n(const v_reg<_Tp, n>& v) +{ + CV_DbgAssert(s >= 0 && s < n); + return v.s[s]; +} + +/** @brief Broadcast i-th element of vector + +Scheme: +@code +{ v[0] v[1] v[2] ... v[SZ] } => { v[i], v[i], v[i] ... v[i] } +@endcode +Restriction: 0 <= i < nlanes +Supported types: 32-bit integers and floats (s32/u32/f32) + */ +template +inline v_reg<_Tp, n> v_broadcast_element(const v_reg<_Tp, n>& a) +{ + CV_DbgAssert(i >= 0 && i < n); + return v_reg<_Tp, n>::all(a.s[i]); +} + +/** @brief Round elements + +Rounds each value. Input type is float vector ==> output type is int vector. +@note Only for floating point types. +*/ +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvRound(a.s[i]); + return c; +} + +/** @overload */ +template inline v_reg v_round(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = cvRound(b.s[i]); + } + return c; +} + +/** @brief Floor elements + +Floor each value. Input type is float vector ==> output type is int vector. +@note Only for floating point types. +*/ +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvFloor(a.s[i]); + return c; +} + +/** @brief Ceil elements + +Ceil each value. Input type is float vector ==> output type is int vector. +@note Only for floating point types. +*/ +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = cvCeil(a.s[i]); + return c; +} + +/** @brief Truncate elements + +Truncate each value. Input type is float vector ==> output type is int vector. +@note Only for floating point types. +*/ +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (int)(a.s[i]); + return c; +} + +/** @overload */ +template inline v_reg v_round(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvRound(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_floor(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvFloor(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_ceil(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = cvCeil(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @overload */ +template inline v_reg v_trunc(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (int)(a.s[i]); + c.s[i+n] = 0; + } + return c; +} + +/** @brief Convert to float + +Supported input type is cv::v_int32. */ +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (float)a.s[i]; + return c; +} + +/** @brief Convert lower half to float + +Supported input type is cv::v_float64. */ +template inline v_reg v_cvt_f32(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = 0; + } + return c; +} + +/** @brief Convert to float + +Supported input type is cv::v_float64. */ +template inline v_reg v_cvt_f32(const v_reg& a, const v_reg& b) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + { + c.s[i] = (float)a.s[i]; + c.s[i+n] = (float)b.s[i]; + } + return c; +} + +/** @brief Convert lower half to double + +Supported input type is cv::v_int32. */ +template CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +/** @brief Convert to double high part of vector + +Supported input type is cv::v_int32. */ +template CV_INLINE v_reg v_cvt_f64_high(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (double)a.s[i + (n/2)]; + return c; +} + +/** @brief Convert lower half to double + +Supported input type is cv::v_float32. */ +template CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + +/** @brief Convert to double high part of vector + +Supported input type is cv::v_float32. */ +template CV_INLINE v_reg v_cvt_f64_high(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < (n/2); i++ ) + c.s[i] = (double)a.s[i + (n/2)]; + return c; +} + +/** @brief Convert to double + +Supported input type is cv::v_int64. */ +template CV_INLINE v_reg v_cvt_f64(const v_reg& a) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = (double)a.s[i]; + return c; +} + + +template inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, simd128_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes; i++) + c.s[i] = tab[idx[i]]; + return c; +} +template inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_pairs(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, simd128_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes; i++) + c.s[i] = tab[idx[i / 2] + i % 2]; + return c; +} +template inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_quads(const _Tp* tab, const int* idx) +{ + v_reg<_Tp, simd128_width / sizeof(_Tp)> c; + for (int i = 0; i < c.nlanes; i++) + c.s[i] = tab[idx[i / 4] + i % 4]; + return c; +} + +template inline v_reg v_lut(const int* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const unsigned* tab, const v_reg& idx) +{ + v_reg c; + for (int i = 0; i < n; i++) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const float* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + +template inline v_reg v_lut(const double* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n/2; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} + + +template inline void v_lut_deinterleave(const float* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline void v_lut_deinterleave(const double* tab, const v_reg& idx, + v_reg& x, v_reg& y) +{ + for( int i = 0; i < n; i++ ) + { + int j = idx.s[i]; + x.s[i] = tab[j]; + y.s[i] = tab[j+1]; + } +} + +template inline v_reg<_Tp, n> v_interleave_pairs(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[4*i ] = vec.s[4*i ]; + c.s[4*i+1] = vec.s[4*i+2]; + c.s[4*i+2] = vec.s[4*i+1]; + c.s[4*i+3] = vec.s[4*i+3]; + } + return c; +} + +template inline v_reg<_Tp, n> v_interleave_quads(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/8; i++) + { + c.s[8*i ] = vec.s[8*i ]; + c.s[8*i+1] = vec.s[8*i+4]; + c.s[8*i+2] = vec.s[8*i+1]; + c.s[8*i+3] = vec.s[8*i+5]; + c.s[8*i+4] = vec.s[8*i+2]; + c.s[8*i+5] = vec.s[8*i+6]; + c.s[8*i+6] = vec.s[8*i+3]; + c.s[8*i+7] = vec.s[8*i+7]; + } + return c; +} + +template inline v_reg<_Tp, n> v_pack_triplets(const v_reg<_Tp, n>& vec) +{ + v_reg<_Tp, n> c; + for (int i = 0; i < n/4; i++) + { + c.s[3*i ] = vec.s[4*i ]; + c.s[3*i+1] = vec.s[4*i+1]; + c.s[3*i+2] = vec.s[4*i+2]; + } + return c; +} + +/** @brief Transpose 4x4 matrix + +Scheme: +@code +a0 {A1 A2 A3 A4} +a1 {B1 B2 B3 B4} +a2 {C1 C2 C3 C4} +a3 {D1 D2 D3 D4} +=============== +b0 {A1 B1 C1 D1} +b1 {A2 B2 C2 D2} +b2 {A3 B3 C3 D3} +b3 {A4 B4 C4 D4} +@endcode +*/ +template +inline void v_transpose4x4( v_reg<_Tp, n>& a0, const v_reg<_Tp, n>& a1, + const v_reg<_Tp, n>& a2, const v_reg<_Tp, n>& a3, + v_reg<_Tp, n>& b0, v_reg<_Tp, n>& b1, + v_reg<_Tp, n>& b2, v_reg<_Tp, n>& b3 ) +{ + for (int i = 0; i < n / 4; i++) + { + b0.s[0 + i*4] = a0.s[0 + i*4]; b0.s[1 + i*4] = a1.s[0 + i*4]; + b0.s[2 + i*4] = a2.s[0 + i*4]; b0.s[3 + i*4] = a3.s[0 + i*4]; + b1.s[0 + i*4] = a0.s[1 + i*4]; b1.s[1 + i*4] = a1.s[1 + i*4]; + b1.s[2 + i*4] = a2.s[1 + i*4]; b1.s[3 + i*4] = a3.s[1 + i*4]; + b2.s[0 + i*4] = a0.s[2 + i*4]; b2.s[1 + i*4] = a1.s[2 + i*4]; + b2.s[2 + i*4] = a2.s[2 + i*4]; b2.s[3 + i*4] = a3.s[2 + i*4]; + b3.s[0 + i*4] = a0.s[3 + i*4]; b3.s[1 + i*4] = a1.s[3 + i*4]; + b3.s[2 + i*4] = a2.s[3 + i*4]; b3.s[3 + i*4] = a3.s[3 + i*4]; + } +} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_INIT_ZERO(_Tpvec, prefix, suffix) \ +inline _Tpvec prefix##_setzero_##suffix() { return _Tpvec::zero(); } + +//! @name Init with zero +//! @{ +//! @brief Create new vector with zero elements +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint8x16, v, u8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int8x16, v, s8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint16x8, v, u16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int16x8, v, s16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint32x4, v, u32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int32x4, v, s32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float32x4, v, f32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float64x2, v, f64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint64x2, v, u64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int64x2, v, s64) + +#if CV_SIMD256 +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint8x32, v256, u8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int8x32, v256, s8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint16x16, v256, u16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int16x16, v256, s16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint32x8, v256, u32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int32x8, v256, s32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float32x8, v256, f32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float64x4, v256, f64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint64x4, v256, u64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int64x4, v256, s64) +#endif + +#if CV_SIMD512 +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint8x64, v512, u8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int8x64, v512, s8) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint16x32, v512, u16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int16x32, v512, s16) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint32x16, v512, u32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int32x16, v512, s32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float32x16, v512, f32) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_float64x8, v512, f64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_uint64x8, v512, u64) +OPENCV_HAL_IMPL_C_INIT_ZERO(v_int64x8, v512, s64) +#endif +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_INIT_VAL(_Tpvec, _Tp, prefix, suffix) \ +inline _Tpvec prefix##_setall_##suffix(_Tp val) { return _Tpvec::all(val); } + +//! @name Init with value +//! @{ +//! @brief Create new vector with elements set to a specific value +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint8x16, uchar, v, u8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int8x16, schar, v, s8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint16x8, ushort, v, u16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int16x8, short, v, s16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint32x4, unsigned, v, u32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int32x4, int, v, s32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float32x4, float, v, f32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float64x2, double, v, f64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint64x2, uint64, v, u64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int64x2, int64, v, s64) + +#if CV_SIMD256 +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint8x32, uchar, v256, u8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int8x32, schar, v256, s8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint16x16, ushort, v256, u16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int16x16, short, v256, s16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint32x8, unsigned, v256, u32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int32x8, int, v256, s32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float32x8, float, v256, f32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float64x4, double, v256, f64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint64x4, uint64, v256, u64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int64x4, int64, v256, s64) +#endif + +#if CV_SIMD512 +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint8x64, uchar, v512, u8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int8x64, schar, v512, s8) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint16x32, ushort, v512, u16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int16x32, short, v512, s16) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint32x16, unsigned, v512, u32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int32x16, int, v512, s32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float32x16, float, v512, f32) +OPENCV_HAL_IMPL_C_INIT_VAL(v_float64x8, double, v512, f64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_uint64x8, uint64, v512, u64) +OPENCV_HAL_IMPL_C_INIT_VAL(v_int64x8, int64, v512, s64) +#endif +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_REINTERPRET(_Tp, suffix) \ +template inline v_reg<_Tp, n0*sizeof(_Tp0)/sizeof(_Tp)> \ + v_reinterpret_as_##suffix(const v_reg<_Tp0, n0>& a) \ +{ return a.template reinterpret_as<_Tp, n0*sizeof(_Tp0)/sizeof(_Tp)>(); } + +//! @name Reinterpret +//! @{ +//! @brief Convert vector to different type without modifying underlying data. +OPENCV_HAL_IMPL_C_REINTERPRET(uchar, u8) +OPENCV_HAL_IMPL_C_REINTERPRET(schar, s8) +OPENCV_HAL_IMPL_C_REINTERPRET(ushort, u16) +OPENCV_HAL_IMPL_C_REINTERPRET(short, s16) +OPENCV_HAL_IMPL_C_REINTERPRET(unsigned, u32) +OPENCV_HAL_IMPL_C_REINTERPRET(int, s32) +OPENCV_HAL_IMPL_C_REINTERPRET(float, f32) +OPENCV_HAL_IMPL_C_REINTERPRET(double, f64) +OPENCV_HAL_IMPL_C_REINTERPRET(uint64, u64) +OPENCV_HAL_IMPL_C_REINTERPRET(int64, s64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_SHIFTL(_Tp) \ +template inline v_reg<_Tp, n> v_shl(const v_reg<_Tp, n>& a) \ +{ return a << shift; } + +//! @name Left shift +//! @{ +//! @brief Shift left +OPENCV_HAL_IMPL_C_SHIFTL(ushort) +OPENCV_HAL_IMPL_C_SHIFTL(short) +OPENCV_HAL_IMPL_C_SHIFTL(unsigned) +OPENCV_HAL_IMPL_C_SHIFTL(int) +OPENCV_HAL_IMPL_C_SHIFTL(uint64) +OPENCV_HAL_IMPL_C_SHIFTL(int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_SHIFTR(_Tp) \ +template inline v_reg<_Tp, n> v_shr(const v_reg<_Tp, n>& a) \ +{ return a >> shift; } + +//! @name Right shift +//! @{ +//! @brief Shift right +OPENCV_HAL_IMPL_C_SHIFTR(ushort) +OPENCV_HAL_IMPL_C_SHIFTR(short) +OPENCV_HAL_IMPL_C_SHIFTR(unsigned) +OPENCV_HAL_IMPL_C_SHIFTR(int) +OPENCV_HAL_IMPL_C_SHIFTR(uint64) +OPENCV_HAL_IMPL_C_SHIFTR(int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHIFTR(_Tp) \ +template inline v_reg<_Tp, n> v_rshr(const v_reg<_Tp, n>& a) \ +{ \ + v_reg<_Tp, n> c; \ + for( int i = 0; i < n; i++ ) \ + c.s[i] = (_Tp)((a.s[i] + ((_Tp)1 << (shift - 1))) >> shift); \ + return c; \ +} + +//! @name Rounding shift +//! @{ +//! @brief Rounding shift right +OPENCV_HAL_IMPL_C_RSHIFTR(ushort) +OPENCV_HAL_IMPL_C_RSHIFTR(short) +OPENCV_HAL_IMPL_C_RSHIFTR(unsigned) +OPENCV_HAL_IMPL_C_RSHIFTR(int) +OPENCV_HAL_IMPL_C_RSHIFTR(uint64) +OPENCV_HAL_IMPL_C_RSHIFTR(int64) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_PACK(_Tp, _Tpn, pack_suffix, cast) \ +template inline v_reg<_Tpn, 2*n> v_##pack_suffix(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tpn, 2*n> c; \ + for( int i = 0; i < n; i++ ) \ + { \ + c.s[i] = cast<_Tpn>(a.s[i]); \ + c.s[i+n] = cast<_Tpn>(b.s[i]); \ + } \ + return c; \ +} + +//! @name Pack +//! @{ +//! @brief Pack values from two vectors to one +//! +//! Return vector type have twice more elements than input vector types. Variant with _u_ suffix also +//! converts to corresponding unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_PACK(ushort, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(short, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(unsigned, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(int, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(uint64, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(int64, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK(short, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK(int, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHR_PACK(_Tp, _Tpn, pack_suffix, cast) \ +template inline v_reg<_Tpn, 2*n> v_rshr_##pack_suffix(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) \ +{ \ + v_reg<_Tpn, 2*n> c; \ + for( int i = 0; i < n; i++ ) \ + { \ + c.s[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (shift - 1))) >> shift); \ + c.s[i+n] = cast<_Tpn>((b.s[i] + ((_Tp)1 << (shift - 1))) >> shift); \ + } \ + return c; \ +} + +//! @name Pack with rounding shift +//! @{ +//! @brief Pack values from two vectors to one with rounding shift +//! +//! Values from the input vectors will be shifted right by _n_ bits with rounding, converted to narrower +//! type and returned in the result vector. Variant with _u_ suffix converts to unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_RSHR_PACK(ushort, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(short, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(unsigned, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(int, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(uint64, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(int64, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(short, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK(int, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_PACK_STORE(_Tp, _Tpn, pack_suffix, cast) \ +template inline void v_##pack_suffix##_store(_Tpn* ptr, const v_reg<_Tp, n>& a) \ +{ \ + for( int i = 0; i < n; i++ ) \ + ptr[i] = cast<_Tpn>(a.s[i]); \ +} + +//! @name Pack and store +//! @{ +//! @brief Store values from the input vector into memory with pack +//! +//! Values will be stored into memory with conversion to narrower type. +//! Variant with _u_ suffix converts to corresponding unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_PACK_STORE(ushort, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(short, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(unsigned, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(int, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(uint64, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(int64, int, pack, static_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(short, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_PACK_STORE(int, ushort, pack_u, saturate_cast) +//! @} + +//! @brief Helper macro +//! @ingroup core_hal_intrin_impl +#define OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(_Tp, _Tpn, pack_suffix, cast) \ +template inline void v_rshr_##pack_suffix##_store(_Tpn* ptr, const v_reg<_Tp, n>& a) \ +{ \ + for( int i = 0; i < n; i++ ) \ + ptr[i] = cast<_Tpn>((a.s[i] + ((_Tp)1 << (shift - 1))) >> shift); \ +} + +//! @name Pack and store with rounding shift +//! @{ +//! @brief Store values from the input vector into memory with pack +//! +//! Values will be shifted _n_ bits right with rounding, converted to narrower type and stored into +//! memory. Variant with _u_ suffix converts to unsigned type. +//! +//! - pack: for 16-, 32- and 64-bit integer input types +//! - pack_u: for 16- and 32-bit signed integer input types +//! +//! @note All variants except 64-bit use saturation. +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(ushort, uchar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(short, schar, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(unsigned, ushort, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(int, short, pack, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(uint64, unsigned, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(int64, int, pack, static_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(short, uchar, pack_u, saturate_cast) +OPENCV_HAL_IMPL_C_RSHR_PACK_STORE(int, ushort, pack_u, saturate_cast) +//! @} + +//! @cond IGNORED +template +inline void _pack_b(_Tpm* mptr, const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + for (int i = 0; i < n; ++i) + { + mptr[i] = (_Tpm)a.s[i]; + mptr[i + n] = (_Tpm)b.s[i]; + } +} +//! @endcond + +//! @name Pack boolean values +//! @{ +//! @brief Pack boolean values from multiple vectors to one unsigned 8-bit integer vector +//! +//! @note Must provide valid boolean values to guarantee same result for all architectures. + +/** @brief +//! For 16-bit boolean values + +Scheme: +@code +a {0xFFFF 0 0 0xFFFF 0 0xFFFF 0xFFFF 0} +b {0xFFFF 0 0xFFFF 0 0 0xFFFF 0 0xFFFF} +=============== +{ + 0xFF 0 0 0xFF 0 0xFF 0xFF 0 + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ + +template inline v_reg v_pack_b(const v_reg& a, const v_reg& b) +{ + v_reg mask; + _pack_b(mask.s, a, b); + return mask; +} + +/** @overload +For 32-bit boolean values + +Scheme: +@code +a {0xFFFF.. 0 0 0xFFFF..} +b {0 0xFFFF.. 0xFFFF.. 0} +c {0xFFFF.. 0 0xFFFF.. 0} +d {0 0xFFFF.. 0 0xFFFF..} +=============== +{ + 0xFF 0 0 0xFF 0 0xFF 0xFF 0 + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ + +template inline v_reg v_pack_b(const v_reg& a, const v_reg& b, + const v_reg& c, const v_reg& d) +{ + v_reg mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 2*n, c, d); + return mask; +} + +/** @overload +For 64-bit boolean values + +Scheme: +@code +a {0xFFFF.. 0} +b {0 0xFFFF..} +c {0xFFFF.. 0} +d {0 0xFFFF..} + +e {0xFFFF.. 0} +f {0xFFFF.. 0} +g {0 0xFFFF..} +h {0 0xFFFF..} +=============== +{ + 0xFF 0 0 0xFF 0xFF 0 0 0xFF + 0xFF 0 0xFF 0 0 0xFF 0 0xFF +} +@endcode */ +template inline v_reg v_pack_b(const v_reg& a, const v_reg& b, + const v_reg& c, const v_reg& d, + const v_reg& e, const v_reg& f, + const v_reg& g, const v_reg& h) +{ + v_reg mask; + _pack_b(mask.s, a, b); + _pack_b(mask.s + 2*n, c, d); + _pack_b(mask.s + 4*n, e, f); + _pack_b(mask.s + 6*n, g, h); + return mask; +} +//! @} + +/** @brief Matrix multiplication + +Scheme: +@code +{A0 A1 A2 A3} |V0| +{B0 B1 B2 B3} |V1| +{C0 C1 C2 C3} |V2| +{D0 D1 D2 D3} x |V3| +==================== +{R0 R1 R2 R3}, where: +R0 = A0V0 + B0V1 + C0V2 + D0V3, +R1 = A1V0 + B1V1 + C1V2 + D1V3 +... +@endcode +*/ +template +inline v_reg v_matmul(const v_reg& v, + const v_reg& a, const v_reg& b, + const v_reg& c, const v_reg& d) +{ + v_reg res; + for (int i = 0; i < n / 4; i++) + { + res.s[0 + i*4] = v.s[0 + i*4] * a.s[0 + i*4] + v.s[1 + i*4] * b.s[0 + i*4] + v.s[2 + i*4] * c.s[0 + i*4] + v.s[3 + i*4] * d.s[0 + i*4]; + res.s[1 + i*4] = v.s[0 + i*4] * a.s[1 + i*4] + v.s[1 + i*4] * b.s[1 + i*4] + v.s[2 + i*4] * c.s[1 + i*4] + v.s[3 + i*4] * d.s[1 + i*4]; + res.s[2 + i*4] = v.s[0 + i*4] * a.s[2 + i*4] + v.s[1 + i*4] * b.s[2 + i*4] + v.s[2 + i*4] * c.s[2 + i*4] + v.s[3 + i*4] * d.s[2 + i*4]; + res.s[3 + i*4] = v.s[0 + i*4] * a.s[3 + i*4] + v.s[1 + i*4] * b.s[3 + i*4] + v.s[2 + i*4] * c.s[3 + i*4] + v.s[3 + i*4] * d.s[3 + i*4]; + } + return res; +} + +/** @brief Matrix multiplication and add + +Scheme: +@code +{A0 A1 A2 A3} |V0| |D0| +{B0 B1 B2 B3} |V1| |D1| +{C0 C1 C2 C3} x |V2| + |D2| +==================== |D3| +{R0 R1 R2 R3}, where: +R0 = A0V0 + B0V1 + C0V2 + D0, +R1 = A1V0 + B1V1 + C1V2 + D1 +... +@endcode +*/ +template +inline v_reg v_matmuladd(const v_reg& v, + const v_reg& a, const v_reg& b, + const v_reg& c, const v_reg& d) +{ + v_reg res; + for (int i = 0; i < n / 4; i++) + { + res.s[0 + i * 4] = v.s[0 + i * 4] * a.s[0 + i * 4] + v.s[1 + i * 4] * b.s[0 + i * 4] + v.s[2 + i * 4] * c.s[0 + i * 4] + d.s[0 + i * 4]; + res.s[1 + i * 4] = v.s[0 + i * 4] * a.s[1 + i * 4] + v.s[1 + i * 4] * b.s[1 + i * 4] + v.s[2 + i * 4] * c.s[1 + i * 4] + d.s[1 + i * 4]; + res.s[2 + i * 4] = v.s[0 + i * 4] * a.s[2 + i * 4] + v.s[1 + i * 4] * b.s[2 + i * 4] + v.s[2 + i * 4] * c.s[2 + i * 4] + d.s[2 + i * 4]; + res.s[3 + i * 4] = v.s[0 + i * 4] * a.s[3 + i * 4] + v.s[1 + i * 4] * b.s[3 + i * 4] + v.s[2 + i * 4] * c.s[3 + i * 4] + d.s[3 + i * 4]; + } + return res; +} + + +template inline v_reg v_dotprod_expand(const v_reg& a, const v_reg& b) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_cvt_f64_high(a) * v_cvt_f64_high(b)); } +template inline v_reg v_dotprod_expand(const v_reg& a, const v_reg& b, + const v_reg& c) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_fma(v_cvt_f64_high(a), v_cvt_f64_high(b), c)); } + +template inline v_reg v_dotprod_expand_fast(const v_reg& a, const v_reg& b) +{ return v_dotprod_expand(a, b); } +template inline v_reg v_dotprod_expand_fast(const v_reg& a, const v_reg& b, + const v_reg& c) +{ return v_dotprod_expand(a, b, c); } + +////// FP16 support /////// + +inline v_reg +v_load_expand(const float16_t* ptr) +{ + v_reg v; + for( int i = 0; i < v.nlanes; i++ ) + { + v.s[i] = ptr[i]; + } + return v; +} +#if CV_SIMD256 +inline v_reg +v256_load_expand(const float16_t* ptr) +{ + v_reg v; + for (int i = 0; i < v.nlanes; i++) + { + v.s[i] = ptr[i]; + } + return v; +} +#endif +#if CV_SIMD512 +inline v_reg +v512_load_expand(const float16_t* ptr) +{ + v_reg v; + for (int i = 0; i < v.nlanes; i++) + { + v.s[i] = ptr[i]; + } + return v; +} +#endif + +template inline void +v_pack_store(float16_t* ptr, const v_reg& v) +{ + for( int i = 0; i < v.nlanes; i++ ) + { + ptr[i] = float16_t(v.s[i]); + } +} + +inline void v_cleanup() {} +#if CV_SIMD256 +inline void v256_cleanup() {} +#endif +#if CV_SIMD512 +inline void v512_cleanup() {} +#endif + +//! @} + +#ifndef CV_DOXYGEN +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END +#endif +} + +#if !defined(CV_DOXYGEN) +#undef CV_SIMD256 +#undef CV_SIMD512 +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_forward.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_forward.hpp new file mode 100644 index 0000000..979f15a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_forward.hpp @@ -0,0 +1,191 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef CV__SIMD_FORWARD +#error "Need to pre-define forward width" +#endif + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +/** Types **/ +#if CV__SIMD_FORWARD == 1024 +// [todo] 1024 +#error "1024-long ops not implemented yet" +#elif CV__SIMD_FORWARD == 512 +// 512 +#define __CV_VX(fun) v512_##fun +#define __CV_V_UINT8 v_uint8x64 +#define __CV_V_INT8 v_int8x64 +#define __CV_V_UINT16 v_uint16x32 +#define __CV_V_INT16 v_int16x32 +#define __CV_V_UINT32 v_uint32x16 +#define __CV_V_INT32 v_int32x16 +#define __CV_V_UINT64 v_uint64x8 +#define __CV_V_INT64 v_int64x8 +#define __CV_V_FLOAT32 v_float32x16 +#define __CV_V_FLOAT64 v_float64x8 +struct v_uint8x64; +struct v_int8x64; +struct v_uint16x32; +struct v_int16x32; +struct v_uint32x16; +struct v_int32x16; +struct v_uint64x8; +struct v_int64x8; +struct v_float32x16; +struct v_float64x8; +#elif CV__SIMD_FORWARD == 256 +// 256 +#define __CV_VX(fun) v256_##fun +#define __CV_V_UINT8 v_uint8x32 +#define __CV_V_INT8 v_int8x32 +#define __CV_V_UINT16 v_uint16x16 +#define __CV_V_INT16 v_int16x16 +#define __CV_V_UINT32 v_uint32x8 +#define __CV_V_INT32 v_int32x8 +#define __CV_V_UINT64 v_uint64x4 +#define __CV_V_INT64 v_int64x4 +#define __CV_V_FLOAT32 v_float32x8 +#define __CV_V_FLOAT64 v_float64x4 +struct v_uint8x32; +struct v_int8x32; +struct v_uint16x16; +struct v_int16x16; +struct v_uint32x8; +struct v_int32x8; +struct v_uint64x4; +struct v_int64x4; +struct v_float32x8; +struct v_float64x4; +#else +// 128 +#define __CV_VX(fun) v_##fun +#define __CV_V_UINT8 v_uint8x16 +#define __CV_V_INT8 v_int8x16 +#define __CV_V_UINT16 v_uint16x8 +#define __CV_V_INT16 v_int16x8 +#define __CV_V_UINT32 v_uint32x4 +#define __CV_V_INT32 v_int32x4 +#define __CV_V_UINT64 v_uint64x2 +#define __CV_V_INT64 v_int64x2 +#define __CV_V_FLOAT32 v_float32x4 +#define __CV_V_FLOAT64 v_float64x2 +struct v_uint8x16; +struct v_int8x16; +struct v_uint16x8; +struct v_int16x8; +struct v_uint32x4; +struct v_int32x4; +struct v_uint64x2; +struct v_int64x2; +struct v_float32x4; +struct v_float64x2; +#endif + +/** Value reordering **/ + +// Expansion +void v_expand(const __CV_V_UINT8&, __CV_V_UINT16&, __CV_V_UINT16&); +void v_expand(const __CV_V_INT8&, __CV_V_INT16&, __CV_V_INT16&); +void v_expand(const __CV_V_UINT16&, __CV_V_UINT32&, __CV_V_UINT32&); +void v_expand(const __CV_V_INT16&, __CV_V_INT32&, __CV_V_INT32&); +void v_expand(const __CV_V_UINT32&, __CV_V_UINT64&, __CV_V_UINT64&); +void v_expand(const __CV_V_INT32&, __CV_V_INT64&, __CV_V_INT64&); +// Low Expansion +__CV_V_UINT16 v_expand_low(const __CV_V_UINT8&); +__CV_V_INT16 v_expand_low(const __CV_V_INT8&); +__CV_V_UINT32 v_expand_low(const __CV_V_UINT16&); +__CV_V_INT32 v_expand_low(const __CV_V_INT16&); +__CV_V_UINT64 v_expand_low(const __CV_V_UINT32&); +__CV_V_INT64 v_expand_low(const __CV_V_INT32&); +// High Expansion +__CV_V_UINT16 v_expand_high(const __CV_V_UINT8&); +__CV_V_INT16 v_expand_high(const __CV_V_INT8&); +__CV_V_UINT32 v_expand_high(const __CV_V_UINT16&); +__CV_V_INT32 v_expand_high(const __CV_V_INT16&); +__CV_V_UINT64 v_expand_high(const __CV_V_UINT32&); +__CV_V_INT64 v_expand_high(const __CV_V_INT32&); +// Load & Low Expansion +__CV_V_UINT16 __CV_VX(load_expand)(const uchar*); +__CV_V_INT16 __CV_VX(load_expand)(const schar*); +__CV_V_UINT32 __CV_VX(load_expand)(const ushort*); +__CV_V_INT32 __CV_VX(load_expand)(const short*); +__CV_V_UINT64 __CV_VX(load_expand)(const uint*); +__CV_V_INT64 __CV_VX(load_expand)(const int*); +// Load lower 8-bit and expand into 32-bit +__CV_V_UINT32 __CV_VX(load_expand_q)(const uchar*); +__CV_V_INT32 __CV_VX(load_expand_q)(const schar*); + +// Saturating Pack +__CV_V_UINT8 v_pack(const __CV_V_UINT16&, const __CV_V_UINT16&); +__CV_V_INT8 v_pack(const __CV_V_INT16&, const __CV_V_INT16&); +__CV_V_UINT16 v_pack(const __CV_V_UINT32&, const __CV_V_UINT32&); +__CV_V_INT16 v_pack(const __CV_V_INT32&, const __CV_V_INT32&); +// Non-saturating Pack +__CV_V_UINT32 v_pack(const __CV_V_UINT64&, const __CV_V_UINT64&); +__CV_V_INT32 v_pack(const __CV_V_INT64&, const __CV_V_INT64&); +// Pack signed integers with unsigned saturation +__CV_V_UINT8 v_pack_u(const __CV_V_INT16&, const __CV_V_INT16&); +__CV_V_UINT16 v_pack_u(const __CV_V_INT32&, const __CV_V_INT32&); + +/** Arithmetic, bitwise and comparison operations **/ + +// Non-saturating multiply +#if CV_VSX +template +Tvec v_mul_wrap(const Tvec& a, const Tvec& b); +#else +__CV_V_UINT8 v_mul_wrap(const __CV_V_UINT8&, const __CV_V_UINT8&); +__CV_V_INT8 v_mul_wrap(const __CV_V_INT8&, const __CV_V_INT8&); +__CV_V_UINT16 v_mul_wrap(const __CV_V_UINT16&, const __CV_V_UINT16&); +__CV_V_INT16 v_mul_wrap(const __CV_V_INT16&, const __CV_V_INT16&); +#endif + +// Multiply and expand +#if CV_VSX +template +void v_mul_expand(const Tvec& a, const Tvec& b, Twvec& c, Twvec& d); +#else +void v_mul_expand(const __CV_V_UINT8&, const __CV_V_UINT8&, __CV_V_UINT16&, __CV_V_UINT16&); +void v_mul_expand(const __CV_V_INT8&, const __CV_V_INT8&, __CV_V_INT16&, __CV_V_INT16&); +void v_mul_expand(const __CV_V_UINT16&, const __CV_V_UINT16&, __CV_V_UINT32&, __CV_V_UINT32&); +void v_mul_expand(const __CV_V_INT16&, const __CV_V_INT16&, __CV_V_INT32&, __CV_V_INT32&); +void v_mul_expand(const __CV_V_UINT32&, const __CV_V_UINT32&, __CV_V_UINT64&, __CV_V_UINT64&); +void v_mul_expand(const __CV_V_INT32&, const __CV_V_INT32&, __CV_V_INT64&, __CV_V_INT64&); +#endif + +// Conversions +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_INT32& a); +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_FLOAT64& a); +__CV_V_FLOAT32 v_cvt_f32(const __CV_V_FLOAT64& a, const __CV_V_FLOAT64& b); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_INT32& a); +__CV_V_FLOAT64 v_cvt_f64_high(const __CV_V_INT32& a); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_FLOAT32& a); +__CV_V_FLOAT64 v_cvt_f64_high(const __CV_V_FLOAT32& a); +__CV_V_FLOAT64 v_cvt_f64(const __CV_V_INT64& a); + +/** Cleanup **/ +#undef CV__SIMD_FORWARD +#undef __CV_VX +#undef __CV_V_UINT8 +#undef __CV_V_INT8 +#undef __CV_V_UINT16 +#undef __CV_V_INT16 +#undef __CV_V_UINT32 +#undef __CV_V_INT32 +#undef __CV_V_UINT64 +#undef __CV_V_INT64 +#undef __CV_V_FLOAT32 +#undef __CV_V_FLOAT64 + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: \ No newline at end of file diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_msa.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_msa.hpp new file mode 100644 index 0000000..a1fbb09 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_msa.hpp @@ -0,0 +1,1887 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HAL_INTRIN_MSA_HPP +#define OPENCV_HAL_INTRIN_MSA_HPP + +#include +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @cond IGNORED +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define CV_SIMD128 1 + +//MSA implements 128-bit wide vector registers shared with the 64-bit wide floating-point unit registers. +//MSA and FPU can not be both present, unless the FPU has 64-bit floating-point registers. +#define CV_SIMD128_64F 1 + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + + v_uint8x16() {} + explicit v_uint8x16(v16u8 v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = msa_ld1q_u8(v); + } + + uchar get0() const + { + return msa_getq_lane_u8(val, 0); + } + + v16u8 val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + + v_int8x16() {} + explicit v_int8x16(v16i8 v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = msa_ld1q_s8(v); + } + + schar get0() const + { + return msa_getq_lane_s8(val, 0); + } + + v16i8 val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + + v_uint16x8() {} + explicit v_uint16x8(v8u16 v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = msa_ld1q_u16(v); + } + + ushort get0() const + { + return msa_getq_lane_u16(val, 0); + } + + v8u16 val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + + v_int16x8() {} + explicit v_int16x8(v8i16 v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = msa_ld1q_s16(v); + } + + short get0() const + { + return msa_getq_lane_s16(val, 0); + } + + v8i16 val; +}; + +struct v_uint32x4 +{ + typedef unsigned int lane_type; + enum { nlanes = 4 }; + + v_uint32x4() {} + explicit v_uint32x4(v4u32 v) : val(v) {} + v_uint32x4(unsigned int v0, unsigned int v1, unsigned int v2, unsigned int v3) + { + unsigned int v[] = {v0, v1, v2, v3}; + val = msa_ld1q_u32(v); + } + + unsigned int get0() const + { + return msa_getq_lane_u32(val, 0); + } + + v4u32 val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + + v_int32x4() {} + explicit v_int32x4(v4i32 v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = msa_ld1q_s32(v); + } + + int get0() const + { + return msa_getq_lane_s32(val, 0); + } + + v4i32 val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + + v_float32x4() {} + explicit v_float32x4(v4f32 v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = msa_ld1q_f32(v); + } + + float get0() const + { + return msa_getq_lane_f32(val, 0); + } + + v4f32 val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + + v_uint64x2() {} + explicit v_uint64x2(v2u64 v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = msa_ld1q_u64(v); + } + + uint64 get0() const + { + return msa_getq_lane_u64(val, 0); + } + + v2u64 val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + + v_int64x2() {} + explicit v_int64x2(v2i64 v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = msa_ld1q_s64(v); + } + + int64 get0() const + { + return msa_getq_lane_s64(val, 0); + } + + v2i64 val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + + v_float64x2() {} + explicit v_float64x2(v2f64 v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = msa_ld1q_f64(v); + } + + double get0() const + { + return msa_getq_lane_f64(val, 0); + } + + v2f64 val; +}; + +#define OPENCV_HAL_IMPL_MSA_INIT(_Tpv, _Tp, suffix) \ +inline v_##_Tpv v_setzero_##suffix() { return v_##_Tpv(msa_dupq_n_##suffix((_Tp)0)); } \ +inline v_##_Tpv v_setall_##suffix(_Tp v) { return v_##_Tpv(msa_dupq_n_##suffix(v)); } \ +inline v_uint8x16 v_reinterpret_as_u8(const v_##_Tpv& v) { return v_uint8x16(MSA_TPV_REINTERPRET(v16u8, v.val)); } \ +inline v_int8x16 v_reinterpret_as_s8(const v_##_Tpv& v) { return v_int8x16(MSA_TPV_REINTERPRET(v16i8, v.val)); } \ +inline v_uint16x8 v_reinterpret_as_u16(const v_##_Tpv& v) { return v_uint16x8(MSA_TPV_REINTERPRET(v8u16, v.val)); } \ +inline v_int16x8 v_reinterpret_as_s16(const v_##_Tpv& v) { return v_int16x8(MSA_TPV_REINTERPRET(v8i16, v.val)); } \ +inline v_uint32x4 v_reinterpret_as_u32(const v_##_Tpv& v) { return v_uint32x4(MSA_TPV_REINTERPRET(v4u32, v.val)); } \ +inline v_int32x4 v_reinterpret_as_s32(const v_##_Tpv& v) { return v_int32x4(MSA_TPV_REINTERPRET(v4i32, v.val)); } \ +inline v_uint64x2 v_reinterpret_as_u64(const v_##_Tpv& v) { return v_uint64x2(MSA_TPV_REINTERPRET(v2u64, v.val)); } \ +inline v_int64x2 v_reinterpret_as_s64(const v_##_Tpv& v) { return v_int64x2(MSA_TPV_REINTERPRET(v2i64, v.val)); } \ +inline v_float32x4 v_reinterpret_as_f32(const v_##_Tpv& v) { return v_float32x4(MSA_TPV_REINTERPRET(v4f32, v.val)); } \ +inline v_float64x2 v_reinterpret_as_f64(const v_##_Tpv& v) { return v_float64x2(MSA_TPV_REINTERPRET(v2f64, v.val)); } + +OPENCV_HAL_IMPL_MSA_INIT(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_INIT(int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_INIT(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_INIT(int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_INIT(uint32x4, unsigned int, u32) +OPENCV_HAL_IMPL_MSA_INIT(int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_INIT(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_INIT(int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_INIT(float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_INIT(float64x2, double, f64) + +#define OPENCV_HAL_IMPL_MSA_PACK(_Tpvec, _Tpwvec, pack, mov, rshr) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(mov(a.val, b.val)); \ +} \ +template inline \ +_Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(rshr(a.val, b.val, n)); \ +} + +OPENCV_HAL_IMPL_MSA_PACK(v_uint8x16, v_uint16x8, pack, msa_qpack_u16, msa_qrpackr_u16) +OPENCV_HAL_IMPL_MSA_PACK(v_int8x16, v_int16x8, pack, msa_qpack_s16, msa_qrpackr_s16) +OPENCV_HAL_IMPL_MSA_PACK(v_uint16x8, v_uint32x4, pack, msa_qpack_u32, msa_qrpackr_u32) +OPENCV_HAL_IMPL_MSA_PACK(v_int16x8, v_int32x4, pack, msa_qpack_s32, msa_qrpackr_s32) +OPENCV_HAL_IMPL_MSA_PACK(v_uint32x4, v_uint64x2, pack, msa_pack_u64, msa_rpackr_u64) +OPENCV_HAL_IMPL_MSA_PACK(v_int32x4, v_int64x2, pack, msa_pack_s64, msa_rpackr_s64) +OPENCV_HAL_IMPL_MSA_PACK(v_uint8x16, v_int16x8, pack_u, msa_qpacku_s16, msa_qrpackru_s16) +OPENCV_HAL_IMPL_MSA_PACK(v_uint16x8, v_int32x4, pack_u, msa_qpacku_s32, msa_qrpackru_s32) + +#define OPENCV_HAL_IMPL_MSA_PACK_STORE(_Tpvec, _Tp, hreg, suffix, _Tpwvec, pack, mov, rshr) \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = mov(a.val); \ + msa_st1_##suffix(ptr, a1); \ +} \ +template inline \ +void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = rshr(a.val, n); \ + msa_st1_##suffix(ptr, a1); \ +} + +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint8x16, uchar, v8u8, u8, v_uint16x8, pack, msa_qmovn_u16, msa_qrshrn_n_u16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int8x16, schar, v8i8, s8, v_int16x8, pack, msa_qmovn_s16, msa_qrshrn_n_s16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint16x8, ushort, v4u16, u16, v_uint32x4, pack, msa_qmovn_u32, msa_qrshrn_n_u32) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int16x8, short, v4i16, s16, v_int32x4, pack, msa_qmovn_s32, msa_qrshrn_n_s32) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint32x4, unsigned, v2u32, u32, v_uint64x2, pack, msa_movn_u64, msa_rshrn_n_u64) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_int32x4, int, v2i32, s32, v_int64x2, pack, msa_movn_s64, msa_rshrn_n_s64) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint8x16, uchar, v8u8, u8, v_int16x8, pack_u, msa_qmovun_s16, msa_qrshrun_n_s16) +OPENCV_HAL_IMPL_MSA_PACK_STORE(v_uint16x8, ushort, v4u16, u16, v_int32x4, pack_u, msa_qmovun_s32, msa_qrshrun_n_s32) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint8x16(msa_pack_u16(a.val, b.val)); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + return v_uint8x16(msa_pack_u16(msa_pack_u32(a.val, b.val), msa_pack_u32(c.val, d.val))); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + v8u16 abcd = msa_pack_u32(msa_pack_u64(a.val, b.val), msa_pack_u64(c.val, d.val)); + v8u16 efgh = msa_pack_u32(msa_pack_u64(e.val, f.val), msa_pack_u64(g.val, h.val)); + return v_uint8x16(msa_pack_u16(abcd, efgh)); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + v4f32 v0 = v.val; + v4f32 res = msa_mulq_lane_f32(m0.val, v0, 0); + res = msa_mlaq_lane_f32(res, m1.val, v0, 1); + res = msa_mlaq_lane_f32(res, m2.val, v0, 2); + res = msa_mlaq_lane_f32(res, m3.val, v0, 3); + return v_float32x4(res); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + v4f32 v0 = v.val; + v4f32 res = msa_mulq_lane_f32(m0.val, v0, 0); + res = msa_mlaq_lane_f32(res, m1.val, v0, 1); + res = msa_mlaq_lane_f32(res, m2.val, v0, 2); + res = msa_addq_f32(res, a.val); + return v_float32x4(res); +} + +#define OPENCV_HAL_IMPL_MSA_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint8x16, msa_qaddq_u8) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint8x16, msa_qsubq_u8) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int8x16, msa_qaddq_s8) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int8x16, msa_qsubq_s8) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint16x8, msa_qaddq_u16) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint16x8, msa_qsubq_u16) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int16x8, msa_qaddq_s16) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int16x8, msa_qsubq_s16) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int32x4, msa_addq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int32x4, msa_subq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_int32x4, msa_mulq_s32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint32x4, msa_addq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint32x4, msa_subq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_uint32x4, msa_mulq_u32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_float32x4, msa_addq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_float32x4, msa_subq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_float32x4, msa_mulq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_int64x2, msa_addq_s64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_int64x2, msa_subq_s64) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_uint64x2, msa_addq_u64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_uint64x2, msa_subq_u64) +OPENCV_HAL_IMPL_MSA_BIN_OP(/, v_float32x4, msa_divq_f32) +OPENCV_HAL_IMPL_MSA_BIN_OP(+, v_float64x2, msa_addq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(-, v_float64x2, msa_subq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(*, v_float64x2, msa_mulq_f64) +OPENCV_HAL_IMPL_MSA_BIN_OP(/, v_float64x2, msa_divq_f64) + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_MSA_MUL_SAT(_Tpvec, _Tpwvec) \ +inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ +} \ +inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ +{a = a * b; return a; } + +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_MSA_MUL_SAT(v_uint16x8, v_uint32x4) + +// Multiply and expand +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v16i8 a_lo, a_hi, b_lo, b_hi; + + ILVRL_B2_SB(a.val, msa_dupq_n_s8(0), a_lo, a_hi); + ILVRL_B2_SB(b.val, msa_dupq_n_s8(0), b_lo, b_hi); + c.val = msa_mulq_s16(msa_paddlq_s8(a_lo), msa_paddlq_s8(b_lo)); + d.val = msa_mulq_s16(msa_paddlq_s8(a_hi), msa_paddlq_s8(b_hi)); +} + +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v16u8 a_lo, a_hi, b_lo, b_hi; + + ILVRL_B2_UB(a.val, msa_dupq_n_u8(0), a_lo, a_hi); + ILVRL_B2_UB(b.val, msa_dupq_n_u8(0), b_lo, b_hi); + c.val = msa_mulq_u16(msa_paddlq_u8(a_lo), msa_paddlq_u8(b_lo)); + d.val = msa_mulq_u16(msa_paddlq_u8(a_hi), msa_paddlq_u8(b_hi)); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + v8i16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); + ILVRL_H2_SH(b.val, msa_dupq_n_s16(0), b_lo, b_hi); + c.val = msa_mulq_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(b_lo)); + d.val = msa_mulq_s32(msa_paddlq_s16(a_hi), msa_paddlq_s16(b_hi)); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + v8u16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); + ILVRL_H2_UH(b.val, msa_dupq_n_u16(0), b_lo, b_hi); + c.val = msa_mulq_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(b_lo)); + d.val = msa_mulq_u32(msa_paddlq_u16(a_hi), msa_paddlq_u16(b_hi)); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + v4u32 a_lo, a_hi, b_lo, b_hi; + + ILVRL_W2_UW(a.val, msa_dupq_n_u32(0), a_lo, a_hi); + ILVRL_W2_UW(b.val, msa_dupq_n_u32(0), b_lo, b_hi); + c.val = msa_mulq_u64(msa_paddlq_u32(a_lo), msa_paddlq_u32(b_lo)); + d.val = msa_mulq_u64(msa_paddlq_u32(a_hi), msa_paddlq_u32(b_hi)); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + v8i16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); + ILVRL_H2_SH(b.val, msa_dupq_n_s16(0), b_lo, b_hi); + + return v_int16x8(msa_packr_s32(msa_mulq_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(b_lo)), + msa_mulq_s32(msa_paddlq_s16(a_hi), msa_paddlq_s16(b_hi)), 16)); +} + +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + v8u16 a_lo, a_hi, b_lo, b_hi; + + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); + ILVRL_H2_UH(b.val, msa_dupq_n_u16(0), b_lo, b_hi); + + return v_uint16x8(msa_packr_u32(msa_mulq_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(b_lo)), + msa_mulq_u32(msa_paddlq_u16(a_hi), msa_paddlq_u16(b_hi)), 16)); +} + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(msa_dotp_s_w(a.val, b.val)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(msa_dpadd_s_w(c.val , a.val, b.val)); } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ return v_int64x2(msa_dotp_s_d(a.val, b.val)); } +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_int64x2(msa_dpadd_s_d(c.val , a.val, b.val)); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + v8u16 even_a = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8), 8); + v8u16 odd_a = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8); + v8u16 even_b = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8), 8); + v8u16 odd_b = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8); + v4u32 prod = msa_dotp_u_w(even_a, even_b); + return v_uint32x4(msa_dpadd_u_w(prod, odd_a, odd_b)); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ + v8u16 even_a = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8), 8); + v8u16 odd_a = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, a.val), 8); + v8u16 even_b = msa_shrq_n_u16(msa_shlq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8), 8); + v8u16 odd_b = msa_shrq_n_u16(MSA_TPV_REINTERPRET(v8u16, b.val), 8); + v4u32 prod = msa_dpadd_u_w(c.val, even_a, even_b); + return v_uint32x4(msa_dpadd_u_w(prod, odd_a, odd_b)); +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + v8i16 prod = msa_dotp_s_h(a.val, b.val); + return v_int32x4(msa_hadd_s32(prod, prod)); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, + const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + v4u32 even_a = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16), 16); + v4u32 odd_a = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16); + v4u32 even_b = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16), 16); + v4u32 odd_b = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16); + v2u64 prod = msa_dotp_u_d(even_a, even_b); + return v_uint64x2(msa_dpadd_u_d(prod, odd_a, odd_b)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, + const v_uint64x2& c) +{ + v4u32 even_a = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16), 16); + v4u32 odd_a = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, a.val), 16); + v4u32 even_b = msa_shrq_n_u32(msa_shlq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16), 16); + v4u32 odd_b = msa_shrq_n_u32(MSA_TPV_REINTERPRET(v4u32, b.val), 16); + v2u64 prod = msa_dpadd_u_d(c.val, even_a, even_b); + return v_uint64x2(msa_dpadd_u_d(prod, odd_a, odd_b)); +} + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v4i32 prod = msa_dotp_s_w(a.val, b.val); + return v_int64x2(msa_hadd_s64(prod, prod)); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_IMPL_MSA_LOGIC_OP(_Tpvec, _Tpv, suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(&, _Tpvec, msa_andq_##suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(|, _Tpvec, msa_orrq_##suffix) \ +OPENCV_HAL_IMPL_MSA_BIN_OP(^, _Tpvec, msa_eorq_##suffix) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_mvnq_u8(MSA_TPV_REINTERPRET(v16u8, a.val)))); \ +} + +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint8x16, v16u8, u8) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int8x16, v16i8, s8) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint16x8, v8u16, u16) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int16x8, v8i16, s16) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint32x4, v4u32, u32) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int32x4, v4i32, s32) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_uint64x2, v2u64, u64) +OPENCV_HAL_IMPL_MSA_LOGIC_OP(v_int64x2, v2i64, s64) + +#define OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(bin_op, intrin) \ +inline v_float32x4 operator bin_op (const v_float32x4& a, const v_float32x4& b) \ +{ \ + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, intrin(MSA_TPV_REINTERPRET(v4i32, a.val), MSA_TPV_REINTERPRET(v4i32, b.val)))); \ +} \ +inline v_float32x4& operator bin_op##= (v_float32x4& a, const v_float32x4& b) \ +{ \ + a.val = MSA_TPV_REINTERPRET(v4f32, intrin(MSA_TPV_REINTERPRET(v4i32, a.val), MSA_TPV_REINTERPRET(v4i32, b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(&, msa_andq_s32) +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(|, msa_orrq_s32) +OPENCV_HAL_IMPL_MSA_FLT_BIT_OP(^, msa_eorq_s32) + +inline v_float32x4 operator ~ (const v_float32x4& a) +{ + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_mvnq_s32(MSA_TPV_REINTERPRET(v4i32, a.val)))); +} + +/* v_abs */ +#define OPENCV_HAL_IMPL_MSA_ABS(_Tpuvec, _Tpsvec, usuffix, ssuffix) \ +inline _Tpuvec v_abs(const _Tpsvec& a) \ +{ \ + return v_reinterpret_as_##usuffix(_Tpsvec(msa_absq_##ssuffix(a.val))); \ +} + +OPENCV_HAL_IMPL_MSA_ABS(v_uint8x16, v_int8x16, u8, s8) +OPENCV_HAL_IMPL_MSA_ABS(v_uint16x8, v_int16x8, u16, s16) +OPENCV_HAL_IMPL_MSA_ABS(v_uint32x4, v_int32x4, u32, s32) + +/* v_abs(float), v_sqrt, v_invsqrt */ +#define OPENCV_HAL_IMPL_MSA_BASIC_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a) \ +{ \ + return _Tpvec(intrin(a.val)); \ +} + +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_abs, msa_absq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_abs, msa_absq_f64) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_sqrt, msa_sqrtq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float32x4, v_invsqrt, msa_rsqrtq_f32) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_sqrt, msa_sqrtq_f64) +OPENCV_HAL_IMPL_MSA_BASIC_FUNC(v_float64x2, v_invsqrt, msa_rsqrtq_f64) + +#define OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(bin_op, intrin) \ +inline v_float64x2 operator bin_op (const v_float64x2& a, const v_float64x2& b) \ +{ \ + return v_float64x2(MSA_TPV_REINTERPRET(v2f64, intrin(MSA_TPV_REINTERPRET(v2i64, a.val), MSA_TPV_REINTERPRET(v2i64, b.val)))); \ +} \ +inline v_float64x2& operator bin_op##= (v_float64x2& a, const v_float64x2& b) \ +{ \ + a.val = MSA_TPV_REINTERPRET(v2f64, intrin(MSA_TPV_REINTERPRET(v2i64, a.val), MSA_TPV_REINTERPRET(v2i64, b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(&, msa_andq_s64) +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(|, msa_orrq_s64) +OPENCV_HAL_IMPL_MSA_DBL_BIT_OP(^, msa_eorq_s64) + +inline v_float64x2 operator ~ (const v_float64x2& a) +{ + return v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_mvnq_s32(MSA_TPV_REINTERPRET(v4i32, a.val)))); +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_MSA_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_min, msa_minq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_max, msa_maxq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_min, msa_minq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_max, msa_maxq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_min, msa_minq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_max, msa_maxq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_min, msa_minq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_max, msa_maxq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_min, msa_minq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_max, msa_maxq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int32x4, v_min, msa_minq_s32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int32x4, v_max, msa_maxq_s32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_min, msa_minq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_max, msa_maxq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_min, msa_minq_f64) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_max, msa_maxq_f64) + +#define OPENCV_HAL_IMPL_MSA_INT_CMP_OP(_Tpvec, _Tpv, suffix, not_suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ceqq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_mvnq_##not_suffix(msa_ceqq_##suffix(a.val, b.val)))); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cltq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cgtq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cleq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_cgeq_##suffix(a.val, b.val))); } + +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint8x16, v16u8, u8, u8) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int8x16, v16i8, s8, u8) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint16x8, v8u16, u16, u16) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int16x8, v8i16, s16, u16) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint32x4, v4u32, u32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int32x4, v4i32, s32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_float32x4, v4f32, f32, u32) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_uint64x2, v2u64, u64, u64) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_int64x2, v2i64, s64, u64) +OPENCV_HAL_IMPL_MSA_INT_CMP_OP(v_float64x2, v2f64, f64, u64) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ceqq_f32(a.val, a.val))); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ceqq_f64(a.val, a.val))); } + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_add_wrap, msa_addq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_add_wrap, msa_addq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_add_wrap, msa_addq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_add_wrap, msa_addq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_sub_wrap, msa_subq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_sub_wrap, msa_subq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_sub_wrap, msa_subq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_sub_wrap, msa_subq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_mul_wrap, msa_mulq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_mul_wrap, msa_mulq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_mul_wrap, msa_mulq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_mul_wrap, msa_mulq_s16) + +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint8x16, v_absdiff, msa_abdq_u8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint16x8, v_absdiff, msa_abdq_u16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_uint32x4, v_absdiff, msa_abdq_u32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float32x4, v_absdiff, msa_abdq_f32) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_float64x2, v_absdiff, msa_abdq_f64) + +/** Saturating absolute difference **/ +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int8x16, v_absdiffs, msa_qabdq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC(v_int16x8, v_absdiffs, msa_qabdq_s16) + +#define OPENCV_HAL_IMPL_MSA_BIN_FUNC2(_Tpvec, _Tpvec2, _Tpv, func, intrin) \ +inline _Tpvec2 func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec2(MSA_TPV_REINTERPRET(_Tpv, intrin(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int8x16, v_uint8x16, v16u8, v_absdiff, msa_abdq_s8) +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int16x8, v_uint16x8, v8u16, v_absdiff, msa_abdq_s16) +OPENCV_HAL_IMPL_MSA_BIN_FUNC2(v_int32x4, v_uint32x4, v4u32, v_absdiff, msa_abdq_s32) + +/* v_magnitude, v_sqr_magnitude, v_fma, v_muladd */ +inline v_float32x4 v_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 x(msa_mlaq_f32(msa_mulq_f32(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float32x4 v_sqr_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + return v_float32x4(msa_mlaq_f32(msa_mulq_f32(a.val, a.val), b.val, b.val)); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_float32x4(msa_mlaq_f32(c.val, a.val, b.val)); +} + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_int32x4(msa_mlaq_s32(c.val, a.val, b.val)); +} + +inline v_float32x4 v_muladd(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float64x2 v_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 x(msa_mlaq_f64(msa_mulq_f64(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float64x2 v_sqr_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + return v_float64x2(msa_mlaq_f64(msa_mulq_f64(a.val, a.val), b.val, b.val)); +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_float64x2(msa_mlaq_f64(c.val, a.val, b.val)); +} + +inline v_float64x2 v_muladd(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_fma(a, b, c); +} + +// trade efficiency for convenience +#define OPENCV_HAL_IMPL_MSA_SHIFT_OP(_Tpvec, suffix, _Tps, ssuffix) \ +inline _Tpvec operator << (const _Tpvec& a, int n) \ +{ return _Tpvec(msa_shlq_##suffix(a.val, msa_dupq_n_##ssuffix((_Tps)n))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int n) \ +{ return _Tpvec(msa_shrq_##suffix(a.val, msa_dupq_n_##ssuffix((_Tps)n))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(msa_shlq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(msa_shrq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ return _Tpvec(msa_rshrq_n_##suffix(a.val, n)); } + +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint8x16, u8, schar, s8) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int8x16, s8, schar, s8) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint16x8, u16, short, s16) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int16x8, s16, short, s16) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint32x4, u32, int, s32) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int32x4, s32, int, s32) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_uint64x2, u64, int64, s64) +OPENCV_HAL_IMPL_MSA_SHIFT_OP(v_int64x2, s64, int64, s64) + +/* v_rotate_right, v_rotate_left */ +#define OPENCV_HAL_IMPL_MSA_ROTATE_OP(_Tpvec, _Tpv, _Tpvs, suffix) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##suffix(0), n))); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(msa_dupq_n_##suffix(0), MSA_TPV_REINTERPRET(_Tpvs, a.val), _Tpvec::nlanes - n))); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ \ + return a; \ +} \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), MSA_TPV_REINTERPRET(_Tpvs, b.val), n))); \ +} \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, b.val), MSA_TPV_REINTERPRET(_Tpvs, a.val), _Tpvec::nlanes - n))); \ +} \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ \ + CV_UNUSED(b); \ + return a; \ +} + +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_uint64x2, v2u64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_int64x2, v2i64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_ROTATE_OP(v_float64x2, v2f64, v2i64, s64) + +#define OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(msa_ld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(msa_ld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(msa_combine_##suffix(msa_ld1_##suffix(ptr), msa_dup_n_##suffix((_Tp)0))); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(msa_combine_##suffix(msa_ld1_##suffix(ptr0), msa_ld1_##suffix(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ msa_st1q_##suffix(ptr, a.val); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ \ + int n = _Tpvec::nlanes; \ + for( int i = 0; i < (n/2); i++ ) \ + ptr[i] = a.val[i]; \ +} \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + int n = _Tpvec::nlanes; \ + for( int i = 0; i < (n/2); i++ ) \ + ptr[i] = a.val[i+(n/2)]; \ +} + +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_LOADSTORE_OP(v_float64x2, double, f64) + + +/** Reverse **/ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + v_uint8x16 c = v_uint8x16((v16u8)__builtin_msa_vshf_b((v16i8)((v2i64){0x08090A0B0C0D0E0F, 0x0001020304050607}), msa_dupq_n_s8(0), (v16i8)a.val)); + return c; +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + v_uint16x8 c = v_uint16x8((v8u16)__builtin_msa_vshf_h((v8i16)((v2i64){0x0004000500060007, 0x0000000100020003}), msa_dupq_n_s16(0), (v8i16)a.val)); + return c; +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + v_uint32x4 c; + c.val[0] = a.val[3]; + c.val[1] = a.val[2]; + c.val[2] = a.val[1]; + c.val[3] = a.val[0]; + return c; +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + v_uint64x2 c; + c.val[0] = a.val[1]; + c.val[1] = a.val[0]; + return c; +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(func, cfunc) \ +inline unsigned short v_reduce_##func(const v_uint16x8& a) \ +{ \ + v8u16 a_lo, a_hi; \ + ILVRL_H2_UH(a.val, msa_dupq_n_u16(0), a_lo, a_hi); \ + v4u32 b = msa_##func##q_u32(msa_paddlq_u16(a_lo), msa_paddlq_u16(a_hi)); \ + v4u32 b_lo, b_hi; \ + ILVRL_W2_UW(b, msa_dupq_n_u32(0), b_lo, b_hi); \ + v2u64 c = msa_##func##q_u64(msa_paddlq_u32(b_lo), msa_paddlq_u32(b_hi)); \ + return (unsigned short)cfunc(c[0], c[1]); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8U(min, std::min) + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(func, cfunc) \ +inline short v_reduce_##func(const v_int16x8& a) \ +{ \ + v8i16 a_lo, a_hi; \ + ILVRL_H2_SH(a.val, msa_dupq_n_s16(0), a_lo, a_hi); \ + v4i32 b = msa_##func##q_s32(msa_paddlq_s16(a_lo), msa_paddlq_s16(a_hi)); \ + v4i32 b_lo, b_hi; \ + ILVRL_W2_SW(b, msa_dupq_n_s32(0), b_lo, b_hi); \ + v2i64 c = msa_##func##q_s64(msa_paddlq_s32(b_lo), msa_paddlq_s32(b_hi)); \ + return (short)cfunc(c[0], c[1]); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_8S(min, std::min) + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(_Tpvec, scalartype, func, cfunc) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + return (scalartype)cfunc(cfunc(a.val[0], a.val[1]), cfunc(a.val[2], a.val[3])); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_4(v_float32x4, float, min, std::min) + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(_Tpvec, scalartype, _Tpvec2, func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpvec2 a1, a2; \ + v_expand(a, a1, a2); \ + return (scalartype)v_reduce_##func(v_##func(a1, a2)); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_uint8x16, uchar, v_uint16x8, min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_uint8x16, uchar, v_uint16x8, max) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_int8x16, char, v_int16x8, min) +OPENCV_HAL_IMPL_MSA_REDUCE_OP_16(v_int8x16, char, v_int16x8, max) + + + +#define OPENCV_HAL_IMPL_MSA_REDUCE_SUM(_Tpvec, scalartype, suffix) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + return (scalartype)msa_sum_##suffix(a.val); \ +} + +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint8x16, unsigned char, u8) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int8x16, char, s8) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint16x8, unsigned short, u16) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_REDUCE_SUM(v_float32x4, float, f32) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ return (uint64)(msa_getq_lane_u64(a.val, 0) + msa_getq_lane_u64(a.val, 1)); } +inline int64 v_reduce_sum(const v_int64x2& a) +{ return (int64)(msa_getq_lane_s64(a.val, 0) + msa_getq_lane_s64(a.val, 1)); } +inline double v_reduce_sum(const v_float64x2& a) +{ + return msa_getq_lane_f64(a.val, 0) + msa_getq_lane_f64(a.val, 1); +} + +/* v_reduce_sum4, v_reduce_sad */ +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v4f32 u0 = msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, b.val), MSA_TPV_REINTERPRET(v4i32, a.val))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, b.val), MSA_TPV_REINTERPRET(v4i32, a.val)))); // a0+a1 b0+b1 a2+a3 b2+b3 + v4f32 u1 = msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, d.val), MSA_TPV_REINTERPRET(v4i32, c.val))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, d.val), MSA_TPV_REINTERPRET(v4i32, c.val)))); // c0+c1 d0+d1 c2+c3 d2+d3 + + return v_float32x4(msa_addq_f32(MSA_TPV_REINTERPRET(v4f32, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, u1), MSA_TPV_REINTERPRET(v2i64, u0))), + MSA_TPV_REINTERPRET(v4f32, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, u1), MSA_TPV_REINTERPRET(v2i64, u0))))); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + v16u8 t0 = msa_abdq_u8(a.val, b.val); + v8u16 t1 = msa_paddlq_u8(t0); + v4u32 t2 = msa_paddlq_u16(t1); + return msa_sum_u32(t2); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + v16u8 t0 = MSA_TPV_REINTERPRET(v16u8, msa_abdq_s8(a.val, b.val)); + v8u16 t1 = msa_paddlq_u8(t0); + v4u32 t2 = msa_paddlq_u16(t1); + return msa_sum_u32(t2); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v8u16 t0 = msa_abdq_u16(a.val, b.val); + v4u32 t1 = msa_paddlq_u16(t0); + return msa_sum_u32(t1); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v8u16 t0 = MSA_TPV_REINTERPRET(v8u16, msa_abdq_s16(a.val, b.val)); + v4u32 t1 = msa_paddlq_u16(t0); + return msa_sum_u32(t1); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + v4u32 t0 = msa_abdq_u32(a.val, b.val); + return msa_sum_u32(t0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + v4u32 t0 = MSA_TPV_REINTERPRET(v4u32, msa_abdq_s32(a.val, b.val)); + return msa_sum_u32(t0); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + v4f32 t0 = msa_abdq_f32(a.val, b.val); + return msa_sum_f32(t0); +} + +/* v_popcount */ +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(_Tpvec) \ +inline v_uint8x16 v_popcount(const _Tpvec& a) \ +{ \ + v16u8 t = MSA_TPV_REINTERPRET(v16u8, msa_cntq_s8(MSA_TPV_REINTERPRET(v16i8, a.val))); \ + return v_uint8x16(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(v_uint8x16) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE8(v_int8x16) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(_Tpvec) \ +inline v_uint16x8 v_popcount(const _Tpvec& a) \ +{ \ + v8u16 t = MSA_TPV_REINTERPRET(v8u16, msa_cntq_s16(MSA_TPV_REINTERPRET(v8i16, a.val))); \ + return v_uint16x8(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(v_uint16x8) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE16(v_int16x8) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(_Tpvec) \ +inline v_uint32x4 v_popcount(const _Tpvec& a) \ +{ \ + v4u32 t = MSA_TPV_REINTERPRET(v4u32, msa_cntq_s32(MSA_TPV_REINTERPRET(v4i32, a.val))); \ + return v_uint32x4(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(v_uint32x4) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE32(v_int32x4) + +#define OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(_Tpvec) \ +inline v_uint64x2 v_popcount(const _Tpvec& a) \ +{ \ + v2u64 t = MSA_TPV_REINTERPRET(v2u64, msa_cntq_s64(MSA_TPV_REINTERPRET(v2i64, a.val))); \ + return v_uint64x2(t); \ +} +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(v_uint64x2) +OPENCV_HAL_IMPL_MSA_POPCOUNT_SIZE64(v_int64x2) + +inline int v_signmask(const v_uint8x16& a) +{ + v8i8 m0 = msa_create_s8(CV_BIG_UINT(0x0706050403020100)); + v16u8 v0 = msa_shlq_u8(msa_shrq_n_u8(a.val, 7), msa_combine_s8(m0, m0)); + v8u16 v1 = msa_paddlq_u8(v0); + v4u32 v2 = msa_paddlq_u16(v1); + v2u64 v3 = msa_paddlq_u32(v2); + return (int)msa_getq_lane_u64(v3, 0) + ((int)msa_getq_lane_u64(v3, 1) << 8); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_uint16x8& a) +{ + v4i16 m0 = msa_create_s16(CV_BIG_UINT(0x0003000200010000)); + v8u16 v0 = msa_shlq_u16(msa_shrq_n_u16(a.val, 15), msa_combine_s16(m0, m0)); + v4u32 v1 = msa_paddlq_u16(v0); + v2u64 v2 = msa_paddlq_u32(v1); + return (int)msa_getq_lane_u64(v2, 0) + ((int)msa_getq_lane_u64(v2, 1) << 4); +} +inline int v_signmask(const v_int16x8& a) +{ return v_signmask(v_reinterpret_as_u16(a)); } + +inline int v_signmask(const v_uint32x4& a) +{ + v2i32 m0 = msa_create_s32(CV_BIG_UINT(0x0000000100000000)); + v4u32 v0 = msa_shlq_u32(msa_shrq_n_u32(a.val, 31), msa_combine_s32(m0, m0)); + v2u64 v1 = msa_paddlq_u32(v0); + return (int)msa_getq_lane_u64(v1, 0) + ((int)msa_getq_lane_u64(v1, 1) << 2); +} +inline int v_signmask(const v_int32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } + +inline int v_signmask(const v_uint64x2& a) +{ + v2u64 v0 = msa_shrq_n_u64(a.val, 63); + return (int)msa_getq_lane_u64(v0, 0) + ((int)msa_getq_lane_u64(v0, 1) << 1); +} +inline int v_signmask(const v_int64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } + +#define OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(_Tpvec, _Tpvec2, suffix, shift) \ +inline bool v_check_all(const v_##_Tpvec& a) \ +{ \ + _Tpvec2 v0 = msa_shrq_n_##suffix(msa_mvnq_##suffix(a.val), shift); \ + v2u64 v1 = MSA_TPV_REINTERPRET(v2u64, v0); \ + return (msa_getq_lane_u64(v1, 0) | msa_getq_lane_u64(v1, 1)) == 0; \ +} \ +inline bool v_check_any(const v_##_Tpvec& a) \ +{ \ + _Tpvec2 v0 = msa_shrq_n_##suffix(a.val, shift); \ + v2u64 v1 = MSA_TPV_REINTERPRET(v2u64, v0); \ + return (msa_getq_lane_u64(v1, 0) | msa_getq_lane_u64(v1, 1)) != 0; \ +} + +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint8x16, v16u8, u8, 7) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint16x8, v8u16, u16, 15) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint32x4, v4u32, u32, 31) +OPENCV_HAL_IMPL_MSA_CHECK_ALLANY(uint64x2, v2u64, u64, 63) + +inline bool v_check_all(const v_int8x16& a) +{ return v_check_all(v_reinterpret_as_u8(a)); } +inline bool v_check_all(const v_int16x8& a) +{ return v_check_all(v_reinterpret_as_u16(a)); } +inline bool v_check_all(const v_int32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } + +inline bool v_check_any(const v_int8x16& a) +{ return v_check_any(v_reinterpret_as_u8(a)); } +inline bool v_check_any(const v_int16x8& a) +{ return v_check_any(v_reinterpret_as_u16(a)); } +inline bool v_check_any(const v_int32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } + +inline bool v_check_all(const v_int64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_int64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } + +/* v_select */ +#define OPENCV_HAL_IMPL_MSA_SELECT(_Tpvec, _Tpv, _Tpvu) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_bslq_u8(MSA_TPV_REINTERPRET(_Tpvu, mask.val), \ + MSA_TPV_REINTERPRET(_Tpvu, b.val), MSA_TPV_REINTERPRET(_Tpvu, a.val)))); \ +} + +OPENCV_HAL_IMPL_MSA_SELECT(v_uint8x16, v16u8, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int8x16, v16i8, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_uint16x8, v8u16, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int16x8, v8i16, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_uint32x4, v4u32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_int32x4, v4i32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_float32x4, v4f32, v16u8) +OPENCV_HAL_IMPL_MSA_SELECT(v_float64x2, v2f64, v16u8) + +#define OPENCV_HAL_IMPL_MSA_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix, ssuffix, _Tpv, _Tpvs) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + _Tpv a_lo = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + _Tpv a_hi = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + b0.val = msa_paddlq_##suffix(a_lo); \ + b1.val = msa_paddlq_##suffix(a_hi); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ \ + _Tpv a_lo = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + return _Tpwvec(msa_paddlq_##suffix(a_lo)); \ +} \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ \ + _Tpv a_hi = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), msa_dupq_n_##ssuffix(0))); \ + return _Tpwvec(msa_paddlq_##suffix(a_hi)); \ +} \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + return _Tpwvec(msa_movl_##suffix(msa_ld1_##suffix(ptr))); \ +} + +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint8x16, v_uint16x8, uchar, u8, s8, v16u8, v16i8) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int8x16, v_int16x8, schar, s8, s8, v16i8, v16i8) +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint16x8, v_uint32x4, ushort, u16, s16, v8u16, v8i16) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int16x8, v_int32x4, short, s16, s16, v8i16, v8i16) +OPENCV_HAL_IMPL_MSA_EXPAND(v_uint32x4, v_uint64x2, uint, u32, s32, v4u32, v4i32) +OPENCV_HAL_IMPL_MSA_EXPAND(v_int32x4, v_int64x2, int, s32, s32, v4i32, v4i32) + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + return v_uint32x4((v4u32){ptr[0], ptr[1], ptr[2], ptr[3]}); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + return v_int32x4((v4i32){ptr[0], ptr[1], ptr[2], ptr[3]}); +} + +/* v_zip, v_combine_low, v_combine_high, v_recombine */ +#define OPENCV_HAL_IMPL_MSA_UNPACKS(_Tpvec, _Tpv, _Tpvs, ssuffix) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + b1.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val)))); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val)))); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + c.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val))); \ + d.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, b.val), MSA_TPV_REINTERPRET(v2i64, a.val))); \ +} + +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_UNPACKS(v_float64x2, v2f64, v2i64, s64) + +/* v_extract */ +#define OPENCV_HAL_IMPL_MSA_EXTRACT(_Tpvec, _Tpv, _Tpvs, suffix) \ +template \ +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(MSA_TPV_REINTERPRET(_Tpv, msa_extq_##suffix(MSA_TPV_REINTERPRET(_Tpvs, a.val), MSA_TPV_REINTERPRET(_Tpvs, b.val), s))); \ +} + +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint8x16, v16u8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int8x16, v16i8, v16i8, s8) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint16x8, v8u16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int16x8, v8i16, v8i16, s16) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_uint64x2, v2u64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_int64x2, v2i64, v2i64, s64) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_float32x4, v4f32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_EXTRACT(v_float64x2, v2f64, v2i64, s64) + +/* v_round, v_floor, v_ceil, v_trunc */ +inline v_int32x4 v_round(const v_float32x4& a) +{ + return v_int32x4(msa_cvttintq_s32_f32(a.val)); +} + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + v4i32 a1 = msa_cvttintq_s32_f32(a.val); + return v_int32x4(msa_addq_s32(a1, MSA_TPV_REINTERPRET(v4i32, msa_cgtq_f32(msa_cvtfintq_f32_s32(a1), a.val)))); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + v4i32 a1 = msa_cvttintq_s32_f32(a.val); + return v_int32x4(msa_subq_s32(a1, MSA_TPV_REINTERPRET(v4i32, msa_cgtq_f32(a.val, msa_cvtfintq_f32_s32(a1))))); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ + return v_int32x4(msa_cvttruncq_s32_f32(a.val)); +} + +inline v_int32x4 v_round(const v_float64x2& a) +{ + return v_int32x4(msa_pack_s64(msa_cvttintq_s64_f64(a.val), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + return v_int32x4(msa_pack_s64(msa_cvttintq_s64_f64(a.val), msa_cvttintq_s64_f64(b.val))); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + v2f64 a1 = msa_cvtrintq_f64(a.val); + return v_int32x4(msa_pack_s64(msa_addq_s64(msa_cvttruncq_s64_f64(a1), MSA_TPV_REINTERPRET(v2i64, msa_cgtq_f64(a1, a.val))), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + v2f64 a1 = msa_cvtrintq_f64(a.val); + return v_int32x4(msa_pack_s64(msa_subq_s64(msa_cvttruncq_s64_f64(a1), MSA_TPV_REINTERPRET(v2i64, msa_cgtq_f64(a.val, a1))), msa_dupq_n_s64(0))); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ + return v_int32x4(msa_pack_s64(msa_cvttruncq_s64_f64(a.val), msa_dupq_n_s64(0))); +} + +#define OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(_Tpvec, _Tpv, _Tpvs, ssuffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + _Tpv t00 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + _Tpv t01 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a1.val), MSA_TPV_REINTERPRET(_Tpvs, a0.val))); \ + _Tpv t10 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a3.val), MSA_TPV_REINTERPRET(_Tpvs, a2.val))); \ + _Tpv t11 = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_##ssuffix(MSA_TPV_REINTERPRET(_Tpvs, a3.val), MSA_TPV_REINTERPRET(_Tpvs, a2.val))); \ + b0.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, t10), MSA_TPV_REINTERPRET(v2i64, t00))); \ + b1.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, t10), MSA_TPV_REINTERPRET(v2i64, t00))); \ + b2.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvrq_s64(MSA_TPV_REINTERPRET(v2i64, t11), MSA_TPV_REINTERPRET(v2i64, t01))); \ + b3.val = MSA_TPV_REINTERPRET(_Tpv, msa_ilvlq_s64(MSA_TPV_REINTERPRET(v2i64, t11), MSA_TPV_REINTERPRET(v2i64, t01))); \ +} + +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_uint32x4, v4u32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_int32x4, v4i32, v4i32, s32) +OPENCV_HAL_IMPL_MSA_TRANSPOSE4x4(v_float32x4, v4f32, v4i32, s32) + +#define OPENCV_HAL_IMPL_MSA_INTERLEAVED(_Tpvec, _Tp, suffix) \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \ +{ \ + msa_ld2q_##suffix(ptr, &a.val, &b.val); \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \ +{ \ + msa_ld3q_##suffix(ptr, &a.val, &b.val, &c.val); \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \ + v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + msa_ld4q_##suffix(ptr, &a.val, &b.val, &c.val, &d.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + msa_st2q_##suffix(ptr, a.val, b.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + msa_st3q_##suffix(ptr, a.val, b.val, c.val); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, const v_##_Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + msa_st4q_##suffix(ptr, a.val, b.val, c.val, d.val); \ +} + +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int8x16, schar, s8) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int16x8, short, s16) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int32x4, int, s32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(float32x4, float, f32) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(int64x2, int64, s64) +OPENCV_HAL_IMPL_MSA_INTERLEAVED(float64x2, double, f64) + +/* v_cvt_f32, v_cvt_f64, v_cvt_f64_high */ +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(msa_cvtfintq_f32_s32(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + return v_float32x4(msa_cvtfq_f32_f64(a.val, msa_dupq_n_f64(0.0f))); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(msa_cvtfq_f32_f64(a.val, b.val)); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(msa_cvtflq_f64_f32(msa_cvtfintq_f32_s32(a.val))); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(msa_cvtfhq_f64_f32(msa_cvtfintq_f32_s32(a.val))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(msa_cvtflq_f64_f32(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(msa_cvtfhq_f64_f32(a.val)); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ + return v_float64x2(msa_cvtfintq_f64_s64(a.val)); +} + +////////////// Lookup table access //////////////////// +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[ 0]], + tab[idx[ 1]], + tab[idx[ 2]], + tab[idx[ 3]], + tab[idx[ 4]], + tab[idx[ 5]], + tab[idx[ 6]], + tab[idx[ 7]], + tab[idx[ 8]], + tab[idx[ 9]], + tab[idx[10]], + tab[idx[11]], + tab[idx[12]], + tab[idx[13]], + tab[idx[14]], + tab[idx[15]] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[4]], + tab[idx[4] + 1], + tab[idx[5]], + tab[idx[5] + 1], + tab[idx[6]], + tab[idx[6] + 1], + tab[idx[7]], + tab[idx[7] + 1] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[0] + 2], + tab[idx[0] + 3], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[1] + 2], + tab[idx[1] + 3], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[2] + 2], + tab[idx[2] + 3], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[3] + 2], + tab[idx[3] + 3] + }; + return v_int8x16(msa_ld1q_s8(elems)); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } + + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]], + tab[idx[4]], + tab[idx[5]], + tab[idx[6]], + tab[idx[7]] + }; + return v_int16x8(msa_ld1q_s16(elems)); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1] + }; + return v_int16x8(msa_ld1q_s16(elems)); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(msa_combine_s16(msa_ld1_s16(tab + idx[0]), msa_ld1_s16(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_int32x4(msa_ld1q_s32(elems)); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(msa_combine_s32(msa_ld1_s32(tab + idx[0]), msa_ld1_s32(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(msa_ld1q_s32(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(msa_combine_s64(msa_create_s64(tab[idx[0]]), msa_create_s64(tab[idx[1]]))); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(msa_ld1q_s64(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_float32x4(msa_ld1q_f32(elems)); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) +{ + uint64 CV_DECL_ALIGNED(32) elems[2] = + { + *(uint64*)(tab + idx[0]), + *(uint64*)(tab + idx[1]) + }; + return v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ld1q_u64(elems))); +} +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) +{ + return v_float32x4(msa_ld1q_f32(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + unsigned CV_DECL_ALIGNED(32) elems[4] = + { + tab[msa_getq_lane_s32(idxvec.val, 0)], + tab[msa_getq_lane_s32(idxvec.val, 1)], + tab[msa_getq_lane_s32(idxvec.val, 2)], + tab[msa_getq_lane_s32(idxvec.val, 3)] + }; + return v_uint32x4(msa_ld1q_u32(elems)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + v4f32 xy02 = msa_combine_f32(msa_ld1_f32(tab + idx[0]), msa_ld1_f32(tab + idx[2])); + v4f32 xy13 = msa_combine_f32(msa_ld1_f32(tab + idx[1]), msa_ld1_f32(tab + idx[3])); + x = v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ilvevq_s32(MSA_TPV_REINTERPRET(v4i32, xy13), MSA_TPV_REINTERPRET(v4i32, xy02)))); + y = v_float32x4(MSA_TPV_REINTERPRET(v4f32, msa_ilvodq_s32(MSA_TPV_REINTERPRET(v4i32, xy13), MSA_TPV_REINTERPRET(v4i32, xy02)))); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0705060403010200, 0x0F0D0E0C0B090A08}), msa_dupq_n_s8(0), vec.val)); + return c; +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0703060205010400, 0x0F0B0E0A0D090C08}), msa_dupq_n_s8(0), vec.val)); + return c; +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0003000100020000, 0x0007000500060004}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } + +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0005000100040000, 0x0007000300060002}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + v_int32x4 c; + c.val[0] = vec.val[0]; + c.val[1] = vec.val[2]; + c.val[2] = vec.val[1]; + c.val[3] = vec.val[3]; + return c; +} + +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + v_int8x16 c = v_int8x16(__builtin_msa_vshf_b((v16i8)((v2i64){0x0908060504020100, 0x131211100E0D0C0A}), msa_dupq_n_s8(0), vec.val)); + return c; +} + +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + v_int16x8 c = v_int16x8(__builtin_msa_vshf_h((v8i16)((v2i64){0x0004000200010000, 0x0009000800060005}), msa_dupq_n_s16(0), vec.val)); + return c; +} + +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[idx[0]], + tab[idx[1]] + }; + return v_float64x2(msa_ld1q_f64(elems)); +} + +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(msa_ld1q_f64(tab + idx[0])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + v2f64 xy0 = msa_ld1q_f64(tab + idx[0]); + v2f64 xy1 = msa_ld1q_f64(tab + idx[1]); + x = v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ilvevq_s64(MSA_TPV_REINTERPRET(v2i64, xy1), MSA_TPV_REINTERPRET(v2i64, xy0)))); + y = v_float64x2(MSA_TPV_REINTERPRET(v2f64, msa_ilvodq_s64(MSA_TPV_REINTERPRET(v2i64, xy1), MSA_TPV_REINTERPRET(v2i64, xy0)))); +} + +template +inline typename _Tp::lane_type v_extract_n(const _Tp& a) +{ + return v_rotate_right(a).get0(); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& a) +{ + return v_setall_u32(v_extract_n(a)); +} +template +inline v_int32x4 v_broadcast_element(const v_int32x4& a) +{ + return v_setall_s32(v_extract_n(a)); +} +template +inline v_float32x4 v_broadcast_element(const v_float32x4& a) +{ + return v_setall_f32(v_extract_n(a)); +} + +////// FP16 support /////// +#if CV_FP16 +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ +#ifndef msa_ld1_f16 + v4f16 v = (v4f16)msa_ld1_s16((const short*)ptr); +#else + v4f16 v = msa_ld1_f16((const __fp16*)ptr); +#endif + return v_float32x4(msa_cvt_f32_f16(v)); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + v4f16 hv = msa_cvt_f16_f32(v.val); + +#ifndef msa_st1_f16 + msa_st1_s16((short*)ptr, (int16x4_t)hv); +#else + msa_st1_f16((__fp16*)ptr, hv); +#endif +} +#else +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + float buf[4]; + for( int i = 0; i < 4; i++ ) + buf[i] = (float)ptr[i]; + return v_load(buf); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + float buf[4]; + v_store(buf, v); + for( int i = 0; i < 4; i++ ) + ptr[i] = (float16_t)buf[i]; +} +#endif + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_neon.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_neon.hpp new file mode 100644 index 0000000..7856485 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_neon.hpp @@ -0,0 +1,2403 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_INTRIN_NEON_HPP +#define OPENCV_HAL_INTRIN_NEON_HPP + +#include +#include "opencv2/core/utility.hpp" + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define CV_SIMD128 1 +#if defined(__aarch64__) || defined(_M_ARM64) +#define CV_SIMD128_64F 1 +#else +#define CV_SIMD128_64F 0 +#endif + +// The following macro checks if the code is being compiled for the +// AArch64 execution state of Armv8, to enable the 128-bit +// intrinsics. The macro `__ARM_64BIT_STATE` is the one recommended by +// the Arm C Language Extension (ACLE) specifications [1] to check the +// availability of 128-bit intrinsics, and it is supporrted by clang +// and gcc. The macro `_M_ARM64` is the equivalent one for Microsoft +// Visual Studio [2] . +// +// [1] https://developer.arm.com/documentation/101028/0012/13--Advanced-SIMD--Neon--intrinsics +// [2] https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64) +#define CV_NEON_AARCH64 1 +#else +#define CV_NEON_AARCH64 0 +#endif + +// TODO +#define CV_NEON_DOT 0 + +//////////// Utils //////////// + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { c = vuzp1q_##suffix(a, b); d = vuzp2q_##suffix(a, b); } +#define OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv&a, const _Tpv&b, _Tpv& c, _Tpv& d) \ + { c = vuzp1_##suffix(a, b); d = vuzp2_##suffix(a, b); } +#else +#define OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { _Tpvx2 ab = vuzpq_##suffix(a, b); c = ab.val[0]; d = ab.val[1]; } +#define OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpv, _Tpvx2, suffix) \ + inline void _v128_unzip(const _Tpv& a, const _Tpv& b, _Tpv& c, _Tpv& d) \ + { _Tpvx2 ab = vuzp_##suffix(a, b); c = ab.val[0]; d = ab.val[1]; } +#endif + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv, suffix) \ + template static inline \ + _Tpv vreinterpretq_##suffix##_f64(T a) { return (_Tpv) a; } \ + template static inline \ + float64x2_t vreinterpretq_f64_##suffix(T a) { return (float64x2_t) a; } +#else +#define OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv, suffix) +#endif + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv##_t, _Tpv##x2_t, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP_L(_Tpvl##_t, _Tpvl##x2_t, suffix) \ + OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv##_t, suffix) + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_REINTERPRET(_Tpv##_t, suffix) + +#define OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_F64(_Tpv, _Tpvl, suffix) \ + OPENCV_HAL_IMPL_NEON_UNZIP(_Tpv##_t, _Tpv##x2_t, suffix) + +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint8x16, uint8x8, u8) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int8x16, int8x8, s8) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint16x8, uint16x4, u16) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int16x8, int16x4, s16) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint32x4, uint32x2, u32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int32x4, int32x2, s32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(float32x4, float32x2, f32) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(uint64x2, uint64x1, u64) +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(int64x2, int64x1, s64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_F64(float64x2, float64x1,f64) +#endif + +//////////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + + v_uint8x16() {} + explicit v_uint8x16(uint8x16_t v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = vld1q_u8(v); + } + uchar get0() const + { + return vgetq_lane_u8(val, 0); + } + + uint8x16_t val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + + v_int8x16() {} + explicit v_int8x16(int8x16_t v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = vld1q_s8(v); + } + schar get0() const + { + return vgetq_lane_s8(val, 0); + } + + int8x16_t val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + + v_uint16x8() {} + explicit v_uint16x8(uint16x8_t v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = vld1q_u16(v); + } + ushort get0() const + { + return vgetq_lane_u16(val, 0); + } + + uint16x8_t val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + + v_int16x8() {} + explicit v_int16x8(int16x8_t v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = vld1q_s16(v); + } + short get0() const + { + return vgetq_lane_s16(val, 0); + } + + int16x8_t val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + enum { nlanes = 4 }; + + v_uint32x4() {} + explicit v_uint32x4(uint32x4_t v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + unsigned v[] = {v0, v1, v2, v3}; + val = vld1q_u32(v); + } + unsigned get0() const + { + return vgetq_lane_u32(val, 0); + } + + uint32x4_t val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + + v_int32x4() {} + explicit v_int32x4(int32x4_t v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = vld1q_s32(v); + } + int get0() const + { + return vgetq_lane_s32(val, 0); + } + int32x4_t val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + + v_float32x4() {} + explicit v_float32x4(float32x4_t v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = vld1q_f32(v); + } + float get0() const + { + return vgetq_lane_f32(val, 0); + } + float32x4_t val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + + v_uint64x2() {} + explicit v_uint64x2(uint64x2_t v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = vld1q_u64(v); + } + uint64 get0() const + { + return vgetq_lane_u64(val, 0); + } + uint64x2_t val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + + v_int64x2() {} + explicit v_int64x2(int64x2_t v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = vld1q_s64(v); + } + int64 get0() const + { + return vgetq_lane_s64(val, 0); + } + int64x2_t val; +}; + +#if CV_SIMD128_64F +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + + v_float64x2() {} + explicit v_float64x2(float64x2_t v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = vld1q_f64(v); + } + double get0() const + { + return vgetq_lane_f64(val, 0); + } + float64x2_t val; +}; +#endif + +#define OPENCV_HAL_IMPL_NEON_INIT(_Tpv, _Tp, suffix) \ +inline v_##_Tpv v_setzero_##suffix() { return v_##_Tpv(vdupq_n_##suffix((_Tp)0)); } \ +inline v_##_Tpv v_setall_##suffix(_Tp v) { return v_##_Tpv(vdupq_n_##suffix(v)); } \ +inline _Tpv##_t vreinterpretq_##suffix##_##suffix(_Tpv##_t v) { return v; } \ +inline v_uint8x16 v_reinterpret_as_u8(const v_##_Tpv& v) { return v_uint8x16(vreinterpretq_u8_##suffix(v.val)); } \ +inline v_int8x16 v_reinterpret_as_s8(const v_##_Tpv& v) { return v_int8x16(vreinterpretq_s8_##suffix(v.val)); } \ +inline v_uint16x8 v_reinterpret_as_u16(const v_##_Tpv& v) { return v_uint16x8(vreinterpretq_u16_##suffix(v.val)); } \ +inline v_int16x8 v_reinterpret_as_s16(const v_##_Tpv& v) { return v_int16x8(vreinterpretq_s16_##suffix(v.val)); } \ +inline v_uint32x4 v_reinterpret_as_u32(const v_##_Tpv& v) { return v_uint32x4(vreinterpretq_u32_##suffix(v.val)); } \ +inline v_int32x4 v_reinterpret_as_s32(const v_##_Tpv& v) { return v_int32x4(vreinterpretq_s32_##suffix(v.val)); } \ +inline v_uint64x2 v_reinterpret_as_u64(const v_##_Tpv& v) { return v_uint64x2(vreinterpretq_u64_##suffix(v.val)); } \ +inline v_int64x2 v_reinterpret_as_s64(const v_##_Tpv& v) { return v_int64x2(vreinterpretq_s64_##suffix(v.val)); } \ +inline v_float32x4 v_reinterpret_as_f32(const v_##_Tpv& v) { return v_float32x4(vreinterpretq_f32_##suffix(v.val)); } + +OPENCV_HAL_IMPL_NEON_INIT(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_INIT(int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_INIT(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_INIT(int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_INIT(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_INIT(int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_INIT(uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_INIT(int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_INIT(float32x4, float, f32) +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_INIT_64(_Tpv, suffix) \ +inline v_float64x2 v_reinterpret_as_f64(const v_##_Tpv& v) { return v_float64x2(vreinterpretq_f64_##suffix(v.val)); } +OPENCV_HAL_IMPL_NEON_INIT(float64x2, double, f64) +OPENCV_HAL_IMPL_NEON_INIT_64(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_INIT_64(int8x16, s8) +OPENCV_HAL_IMPL_NEON_INIT_64(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_INIT_64(int16x8, s16) +OPENCV_HAL_IMPL_NEON_INIT_64(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_INIT_64(int32x4, s32) +OPENCV_HAL_IMPL_NEON_INIT_64(uint64x2, u64) +OPENCV_HAL_IMPL_NEON_INIT_64(int64x2, s64) +OPENCV_HAL_IMPL_NEON_INIT_64(float32x4, f32) +OPENCV_HAL_IMPL_NEON_INIT_64(float64x2, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_PACK(_Tpvec, _Tp, hreg, suffix, _Tpwvec, pack, mov, rshr) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + hreg a1 = mov(a.val), b1 = mov(b.val); \ + return _Tpvec(vcombine_##suffix(a1, b1)); \ +} \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = mov(a.val); \ + vst1_##suffix(ptr, a1); \ +} \ +template inline \ +_Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + hreg a1 = rshr(a.val, n); \ + hreg b1 = rshr(b.val, n); \ + return _Tpvec(vcombine_##suffix(a1, b1)); \ +} \ +template inline \ +void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + hreg a1 = rshr(a.val, n); \ + vst1_##suffix(ptr, a1); \ +} + +OPENCV_HAL_IMPL_NEON_PACK(v_uint8x16, uchar, uint8x8_t, u8, v_uint16x8, pack, vqmovn_u16, vqrshrn_n_u16) +OPENCV_HAL_IMPL_NEON_PACK(v_int8x16, schar, int8x8_t, s8, v_int16x8, pack, vqmovn_s16, vqrshrn_n_s16) +OPENCV_HAL_IMPL_NEON_PACK(v_uint16x8, ushort, uint16x4_t, u16, v_uint32x4, pack, vqmovn_u32, vqrshrn_n_u32) +OPENCV_HAL_IMPL_NEON_PACK(v_int16x8, short, int16x4_t, s16, v_int32x4, pack, vqmovn_s32, vqrshrn_n_s32) +OPENCV_HAL_IMPL_NEON_PACK(v_uint32x4, unsigned, uint32x2_t, u32, v_uint64x2, pack, vmovn_u64, vrshrn_n_u64) +OPENCV_HAL_IMPL_NEON_PACK(v_int32x4, int, int32x2_t, s32, v_int64x2, pack, vmovn_s64, vrshrn_n_s64) + +OPENCV_HAL_IMPL_NEON_PACK(v_uint8x16, uchar, uint8x8_t, u8, v_int16x8, pack_u, vqmovun_s16, vqrshrun_n_s16) +OPENCV_HAL_IMPL_NEON_PACK(v_uint16x8, ushort, uint16x4_t, u16, v_int32x4, pack_u, vqmovun_s32, vqrshrun_n_s32) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + uint8x16_t ab = vcombine_u8(vmovn_u16(a.val), vmovn_u16(b.val)); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + uint16x8_t nab = vcombine_u16(vmovn_u32(a.val), vmovn_u32(b.val)); + uint16x8_t ncd = vcombine_u16(vmovn_u32(c.val), vmovn_u32(d.val)); + return v_uint8x16(vcombine_u8(vmovn_u16(nab), vmovn_u16(ncd))); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + uint32x4_t ab = vcombine_u32(vmovn_u64(a.val), vmovn_u64(b.val)); + uint32x4_t cd = vcombine_u32(vmovn_u64(c.val), vmovn_u64(d.val)); + uint32x4_t ef = vcombine_u32(vmovn_u64(e.val), vmovn_u64(f.val)); + uint32x4_t gh = vcombine_u32(vmovn_u64(g.val), vmovn_u64(h.val)); + + uint16x8_t abcd = vcombine_u16(vmovn_u32(ab), vmovn_u32(cd)); + uint16x8_t efgh = vcombine_u16(vmovn_u32(ef), vmovn_u32(gh)); + return v_uint8x16(vcombine_u8(vmovn_u16(abcd), vmovn_u16(efgh))); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + float32x2_t vl = vget_low_f32(v.val), vh = vget_high_f32(v.val); + float32x4_t res = vmulq_lane_f32(m0.val, vl, 0); + res = vmlaq_lane_f32(res, m1.val, vl, 1); + res = vmlaq_lane_f32(res, m2.val, vh, 0); + res = vmlaq_lane_f32(res, m3.val, vh, 1); + return v_float32x4(res); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + float32x2_t vl = vget_low_f32(v.val), vh = vget_high_f32(v.val); + float32x4_t res = vmulq_lane_f32(m0.val, vl, 0); + res = vmlaq_lane_f32(res, m1.val, vl, 1); + res = vmlaq_lane_f32(res, m2.val, vh, 0); + res = vaddq_f32(res, a.val); + return v_float32x4(res); +} + +#define OPENCV_HAL_IMPL_NEON_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint8x16, vqaddq_u8) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint8x16, vqsubq_u8) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int8x16, vqaddq_s8) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int8x16, vqsubq_s8) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint16x8, vqaddq_u16) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint16x8, vqsubq_u16) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int16x8, vqaddq_s16) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int16x8, vqsubq_s16) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int32x4, vaddq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int32x4, vsubq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_int32x4, vmulq_s32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint32x4, vaddq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint32x4, vsubq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_uint32x4, vmulq_u32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_float32x4, vaddq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_float32x4, vsubq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_float32x4, vmulq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_int64x2, vaddq_s64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_int64x2, vsubq_s64) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_uint64x2, vaddq_u64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_uint64x2, vsubq_u64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_OP(/, v_float32x4, vdivq_f32) +OPENCV_HAL_IMPL_NEON_BIN_OP(+, v_float64x2, vaddq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(-, v_float64x2, vsubq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(*, v_float64x2, vmulq_f64) +OPENCV_HAL_IMPL_NEON_BIN_OP(/, v_float64x2, vdivq_f64) +#else +inline v_float32x4 operator / (const v_float32x4& a, const v_float32x4& b) +{ + float32x4_t reciprocal = vrecpeq_f32(b.val); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + return v_float32x4(vmulq_f32(a.val, reciprocal)); +} +inline v_float32x4& operator /= (v_float32x4& a, const v_float32x4& b) +{ + float32x4_t reciprocal = vrecpeq_f32(b.val); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(b.val, reciprocal), reciprocal); + a.val = vmulq_f32(a.val, reciprocal); + return a; +} +#endif + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_NEON_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_NEON_MUL_SAT(v_uint16x8, v_uint32x4) + +// Multiply and expand +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + c.val = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + d.val = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val)); +} + +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + c.val = vmull_u8(vget_low_u8(a.val), vget_low_u8(b.val)); + d.val = vmull_u8(vget_high_u8(a.val), vget_high_u8(b.val)); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + c.val = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + d.val = vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + c.val = vmull_u16(vget_low_u16(a.val), vget_low_u16(b.val)); + d.val = vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + c.val = vmull_u32(vget_low_u32(a.val), vget_low_u32(b.val)); + d.val = vmull_u32(vget_high_u32(a.val), vget_high_u32(b.val)); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + return v_int16x8(vcombine_s16( + vshrn_n_s32(vmull_s16( vget_low_s16(a.val), vget_low_s16(b.val)), 16), + vshrn_n_s32(vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)), 16) + )); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint16x8(vcombine_u16( + vshrn_n_u32(vmull_u16( vget_low_u16(a.val), vget_low_u16(b.val)), 16), + vshrn_n_u32(vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)), 16) + )); +} + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ + int16x8_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int16x4_t a0 = vget_low_s16(uzp1); + int16x4_t b0 = vget_high_s16(uzp1); + int16x4_t a1 = vget_low_s16(uzp2); + int16x4_t b1 = vget_high_s16(uzp2); + int32x4_t p = vmull_s16(a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ + int16x8_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int16x4_t a0 = vget_low_s16(uzp1); + int16x4_t b0 = vget_high_s16(uzp1); + int16x4_t a1 = vget_low_s16(uzp2); + int16x4_t b1 = vget_high_s16(uzp2); + int32x4_t p = vmlal_s16(c.val, a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +} + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ + int32x4_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int32x2_t a0 = vget_low_s32(uzp1); + int32x2_t b0 = vget_high_s32(uzp1); + int32x2_t a1 = vget_low_s32(uzp2); + int32x2_t b1 = vget_high_s32(uzp2); + int64x2_t p = vmull_s32(a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ + int32x4_t uzp1, uzp2; + _v128_unzip(a.val, b.val, uzp1, uzp2); + int32x2_t a0 = vget_low_s32(uzp1); + int32x2_t b0 = vget_high_s32(uzp1); + int32x2_t a1 = vget_low_s32(uzp2); + int32x2_t b1 = vget_high_s32(uzp2); + int64x2_t p = vmlal_s32(c.val, a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val)); +#else + const uint8x16_t zero = vreinterpretq_u8_u32(vdupq_n_u32(0)); + const uint8x16_t mask = vreinterpretq_u8_u32(vdupq_n_u32(0x00FF00FF)); + const uint16x8_t zero32 = vreinterpretq_u16_u32(vdupq_n_u32(0)); + const uint16x8_t mask32 = vreinterpretq_u16_u32(vdupq_n_u32(0x0000FFFF)); + + uint16x8_t even = vmulq_u16(vreinterpretq_u16_u8(vbslq_u8(mask, a.val, zero)), + vreinterpretq_u16_u8(vbslq_u8(mask, b.val, zero))); + uint16x8_t odd = vmulq_u16(vshrq_n_u16(vreinterpretq_u16_u8(a.val), 8), + vshrq_n_u16(vreinterpretq_u16_u8(b.val), 8)); + + uint32x4_t s0 = vaddq_u32(vreinterpretq_u32_u16(vbslq_u16(mask32, even, zero32)), + vreinterpretq_u32_u16(vbslq_u16(mask32, odd, zero32))); + uint32x4_t s1 = vaddq_u32(vshrq_n_u32(vreinterpretq_u32_u16(even), 16), + vshrq_n_u32(vreinterpretq_u32_u16(odd), 16)); + return v_uint32x4(vaddq_u32(s0, s1)); +#endif +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, + const v_uint32x4& c) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(c.val, a.val, b.val)); +#else + return v_dotprod_expand(a, b) + c; +#endif +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val)); +#else + int16x8_t p0 = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + int16x8_t p1 = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val)); + int16x8_t uzp1, uzp2; + _v128_unzip(p0, p1, uzp1, uzp2); + int16x8_t sum = vaddq_s16(uzp1, uzp2); + int16x4_t uzpl1, uzpl2; + _v128_unzip(vget_low_s16(sum), vget_high_s16(sum), uzpl1, uzpl2); + return v_int32x4(vaddl_s16(uzpl1, uzpl2)); +#endif +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, + const v_int32x4& c) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(c.val, a.val, b.val)); +#else + return v_dotprod_expand(a, b) + c; +#endif +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + const uint16x8_t zero = vreinterpretq_u16_u32(vdupq_n_u32(0)); + const uint16x8_t mask = vreinterpretq_u16_u32(vdupq_n_u32(0x0000FFFF)); + + uint32x4_t even = vmulq_u32(vreinterpretq_u32_u16(vbslq_u16(mask, a.val, zero)), + vreinterpretq_u32_u16(vbslq_u16(mask, b.val, zero))); + uint32x4_t odd = vmulq_u32(vshrq_n_u32(vreinterpretq_u32_u16(a.val), 16), + vshrq_n_u32(vreinterpretq_u32_u16(b.val), 16)); + uint32x4_t uzp1, uzp2; + _v128_unzip(even, odd, uzp1, uzp2); + uint64x2_t s0 = vaddl_u32(vget_low_u32(uzp1), vget_high_u32(uzp1)); + uint64x2_t s1 = vaddl_u32(vget_low_u32(uzp2), vget_high_u32(uzp2)); + return v_uint64x2(vaddq_u64(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + int32x4_t p0 = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + int32x4_t p1 = vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)); + + int32x4_t uzp1, uzp2; + _v128_unzip(p0, p1, uzp1, uzp2); + int32x4_t sum = vaddq_s32(uzp1, uzp2); + + int32x2_t uzpl1, uzpl2; + _v128_unzip(vget_low_s32(sum), vget_high_s32(sum), uzpl1, uzpl2); + return v_int64x2(vaddl_s32(uzpl1, uzpl2)); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, + const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +#if CV_SIMD128_64F +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, + const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } +#endif + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ +#if CV_NEON_AARCH64 + int32x4_t p = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + return v_int32x4(vmlal_high_s16(p, a.val, b.val)); +#else + int16x4_t a0 = vget_low_s16(a.val); + int16x4_t a1 = vget_high_s16(a.val); + int16x4_t b0 = vget_low_s16(b.val); + int16x4_t b1 = vget_high_s16(b.val); + int32x4_t p = vmull_s16(a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +#endif +} +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ +#if CV_NEON_AARCH64 + int32x4_t p = vmlal_s16(c.val, vget_low_s16(a.val), vget_low_s16(b.val)); + return v_int32x4(vmlal_high_s16(p, a.val, b.val)); +#else + int16x4_t a0 = vget_low_s16(a.val); + int16x4_t a1 = vget_high_s16(a.val); + int16x4_t b0 = vget_low_s16(b.val); + int16x4_t b1 = vget_high_s16(b.val); + int32x4_t p = vmlal_s16(c.val, a0, b0); + return v_int32x4(vmlal_s16(p, a1, b1)); +#endif +} + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_NEON_AARCH64 + int64x2_t p = vmull_s32(vget_low_s32(a.val), vget_low_s32(b.val)); + return v_int64x2(vmlal_high_s32(p, a.val, b.val)); +#else + int32x2_t a0 = vget_low_s32(a.val); + int32x2_t a1 = vget_high_s32(a.val); + int32x2_t b0 = vget_low_s32(b.val); + int32x2_t b1 = vget_high_s32(b.val); + int64x2_t p = vmull_s32(a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +#endif +} +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ +#if CV_NEON_AARCH64 + int64x2_t p = vmlal_s32(c.val, vget_low_s32(a.val), vget_low_s32(b.val)); + return v_int64x2(vmlal_high_s32(p, a.val, b.val)); +#else + int32x2_t a0 = vget_low_s32(a.val); + int32x2_t a1 = vget_high_s32(a.val); + int32x2_t b0 = vget_low_s32(b.val); + int32x2_t b1 = vget_high_s32(b.val); + int64x2_t p = vmlal_s32(c.val, a0, b0); + return v_int64x2(vmlal_s32(p, a1, b1)); +#endif +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(vdupq_n_u32(0), a.val, b.val)); +#else + uint16x8_t p0 = vmull_u8(vget_low_u8(a.val), vget_low_u8(b.val)); + uint16x8_t p1 = vmull_u8(vget_high_u8(a.val), vget_high_u8(b.val)); + uint32x4_t s0 = vaddl_u16(vget_low_u16(p0), vget_low_u16(p1)); + uint32x4_t s1 = vaddl_u16(vget_high_u16(p0), vget_high_u16(p1)); + return v_uint32x4(vaddq_u32(s0, s1)); +#endif +} +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ +#if CV_NEON_DOT + return v_uint32x4(vdotq_u32(c.val, a.val, b.val)); +#else + return v_dotprod_expand_fast(a, b) + c; +#endif +} + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(vdupq_n_s32(0), a.val, b.val)); +#else + int16x8_t prod = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + prod = vmlal_s8(prod, vget_high_s8(a.val), vget_high_s8(b.val)); + return v_int32x4(vaddl_s16(vget_low_s16(prod), vget_high_s16(prod))); +#endif +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ +#if CV_NEON_DOT + return v_int32x4(vdotq_s32(c.val, a.val, b.val)); +#else + return v_dotprod_expand_fast(a, b) + c; +#endif +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ + uint32x4_t p0 = vmull_u16(vget_low_u16(a.val), vget_low_u16(b.val)); + uint32x4_t p1 = vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)); + uint64x2_t s0 = vaddl_u32(vget_low_u32(p0), vget_high_u32(p0)); + uint64x2_t s1 = vaddl_u32(vget_low_u32(p1), vget_high_u32(p1)); + return v_uint64x2(vaddq_u64(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + int32x4_t prod = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + prod = vmlal_s16(prod, vget_high_s16(a.val), vget_high_s16(b.val)); + return v_int64x2(vaddl_s32(vget_low_s32(prod), vget_high_s32(prod))); +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +#if CV_SIMD128_64F +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod_fast(a, b)); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } +#endif + + +#define OPENCV_HAL_IMPL_NEON_LOGIC_OP(_Tpvec, suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(&, _Tpvec, vandq_##suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(|, _Tpvec, vorrq_##suffix) \ + OPENCV_HAL_IMPL_NEON_BIN_OP(^, _Tpvec, veorq_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { \ + return _Tpvec(vreinterpretq_##suffix##_u8(vmvnq_u8(vreinterpretq_u8_##suffix(a.val)))); \ + } + +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint8x16, u8) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int8x16, s8) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint16x8, u16) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int16x8, s16) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint32x4, u32) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int32x4, s32) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_uint64x2, u64) +OPENCV_HAL_IMPL_NEON_LOGIC_OP(v_int64x2, s64) + +#define OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(bin_op, intrin) \ +inline v_float32x4 operator bin_op (const v_float32x4& a, const v_float32x4& b) \ +{ \ + return v_float32x4(vreinterpretq_f32_s32(intrin(vreinterpretq_s32_f32(a.val), vreinterpretq_s32_f32(b.val)))); \ +} \ +inline v_float32x4& operator bin_op##= (v_float32x4& a, const v_float32x4& b) \ +{ \ + a.val = vreinterpretq_f32_s32(intrin(vreinterpretq_s32_f32(a.val), vreinterpretq_s32_f32(b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(&, vandq_s32) +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(|, vorrq_s32) +OPENCV_HAL_IMPL_NEON_FLT_BIT_OP(^, veorq_s32) + +inline v_float32x4 operator ~ (const v_float32x4& a) +{ + return v_float32x4(vreinterpretq_f32_s32(vmvnq_s32(vreinterpretq_s32_f32(a.val)))); +} + +#if CV_SIMD128_64F +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ + return v_float32x4(vsqrtq_f32(x.val)); +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + v_float32x4 one = v_setall_f32(1.0f); + return one / v_sqrt(x); +} +#else +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ + float32x4_t x1 = vmaxq_f32(x.val, vdupq_n_f32(FLT_MIN)); + float32x4_t e = vrsqrteq_f32(x1); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x1, e), e), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x1, e), e), e); + return v_float32x4(vmulq_f32(x.val, e)); +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + float32x4_t e = vrsqrteq_f32(x.val); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x.val, e), e), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x.val, e), e), e); + return v_float32x4(e); +} +#endif + +#define OPENCV_HAL_IMPL_NEON_ABS(_Tpuvec, _Tpsvec, usuffix, ssuffix) \ +inline _Tpuvec v_abs(const _Tpsvec& a) { return v_reinterpret_as_##usuffix(_Tpsvec(vabsq_##ssuffix(a.val))); } + +OPENCV_HAL_IMPL_NEON_ABS(v_uint8x16, v_int8x16, u8, s8) +OPENCV_HAL_IMPL_NEON_ABS(v_uint16x8, v_int16x8, u16, s16) +OPENCV_HAL_IMPL_NEON_ABS(v_uint32x4, v_int32x4, u32, s32) + +inline v_float32x4 v_abs(v_float32x4 x) +{ return v_float32x4(vabsq_f32(x.val)); } + +#if CV_SIMD128_64F +#define OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(bin_op, intrin) \ +inline v_float64x2 operator bin_op (const v_float64x2& a, const v_float64x2& b) \ +{ \ + return v_float64x2(vreinterpretq_f64_s64(intrin(vreinterpretq_s64_f64(a.val), vreinterpretq_s64_f64(b.val)))); \ +} \ +inline v_float64x2& operator bin_op##= (v_float64x2& a, const v_float64x2& b) \ +{ \ + a.val = vreinterpretq_f64_s64(intrin(vreinterpretq_s64_f64(a.val), vreinterpretq_s64_f64(b.val))); \ + return a; \ +} + +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(&, vandq_s64) +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(|, vorrq_s64) +OPENCV_HAL_IMPL_NEON_DBL_BIT_OP(^, veorq_s64) + +inline v_float64x2 operator ~ (const v_float64x2& a) +{ + return v_float64x2(vreinterpretq_f64_s32(vmvnq_s32(vreinterpretq_s32_f64(a.val)))); +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ + return v_float64x2(vsqrtq_f64(x.val)); +} + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ + v_float64x2 one = v_setall_f64(1.0f); + return one / v_sqrt(x); +} + +inline v_float64x2 v_abs(v_float64x2 x) +{ return v_float64x2(vabsq_f64(x.val)); } +#endif + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_NEON_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_min, vminq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_max, vmaxq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_min, vminq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_max, vmaxq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_min, vminq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_max, vmaxq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_min, vminq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_max, vmaxq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_min, vminq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_max, vmaxq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int32x4, v_min, vminq_s32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int32x4, v_max, vmaxq_s32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_min, vminq_f32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_max, vmaxq_f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_min, vminq_f64) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_max, vmaxq_f64) +#endif + +#if CV_SIMD128_64F +inline int64x2_t vmvnq_s64(int64x2_t a) +{ + int64x2_t vx = vreinterpretq_s64_u32(vdupq_n_u32(0xFFFFFFFF)); + return veorq_s64(a, vx); +} +inline uint64x2_t vmvnq_u64(uint64x2_t a) +{ + uint64x2_t vx = vreinterpretq_u64_u32(vdupq_n_u32(0xFFFFFFFF)); + return veorq_u64(a, vx); +} +#endif +#define OPENCV_HAL_IMPL_NEON_INT_CMP_OP(_Tpvec, cast, suffix, not_suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vceqq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vmvnq_##not_suffix(vceqq_##suffix(a.val, b.val)))); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcltq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcgtq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcleq_##suffix(a.val, b.val))); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(cast(vcgeq_##suffix(a.val, b.val))); } + +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint8x16, OPENCV_HAL_NOP, u8, u8) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int8x16, vreinterpretq_s8_u8, s8, u8) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint16x8, OPENCV_HAL_NOP, u16, u16) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int16x8, vreinterpretq_s16_u16, s16, u16) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint32x4, OPENCV_HAL_NOP, u32, u32) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int32x4, vreinterpretq_s32_u32, s32, u32) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_float32x4, vreinterpretq_f32_u32, f32, u32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint64x2, OPENCV_HAL_NOP, u64, u64) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int64x2, vreinterpretq_s64_u64, s64, u64) +OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_float64x2, vreinterpretq_f64_u64, f64, u64) +#endif + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(vreinterpretq_f32_u32(vceqq_f32(a.val, a.val))); } +#if CV_SIMD128_64F +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(vreinterpretq_f64_u64(vceqq_f64(a.val, a.val))); } +#endif + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_add_wrap, vaddq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_add_wrap, vaddq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_add_wrap, vaddq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_add_wrap, vaddq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_sub_wrap, vsubq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_sub_wrap, vsubq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_sub_wrap, vsubq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_sub_wrap, vsubq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_mul_wrap, vmulq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int8x16, v_mul_wrap, vmulq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_mul_wrap, vmulq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_int16x8, v_mul_wrap, vmulq_s16) + +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint8x16, v_absdiff, vabdq_u8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint16x8, v_absdiff, vabdq_u16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_uint32x4, v_absdiff, vabdq_u32) +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float32x4, v_absdiff, vabdq_f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BIN_FUNC(v_float64x2, v_absdiff, vabdq_f64) +#endif + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ return v_int8x16(vqabsq_s8(vqsubq_s8(a.val, b.val))); } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_int16x8(vqabsq_s16(vqsubq_s16(a.val, b.val))); } + +#define OPENCV_HAL_IMPL_NEON_BIN_FUNC2(_Tpvec, _Tpvec2, cast, func, intrin) \ +inline _Tpvec2 func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec2(cast(intrin(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int8x16, v_uint8x16, vreinterpretq_u8_s8, v_absdiff, vabdq_s8) +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int16x8, v_uint16x8, vreinterpretq_u16_s16, v_absdiff, vabdq_s16) +OPENCV_HAL_IMPL_NEON_BIN_FUNC2(v_int32x4, v_uint32x4, vreinterpretq_u32_s32, v_absdiff, vabdq_s32) + +inline v_float32x4 v_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + v_float32x4 x(vmlaq_f32(vmulq_f32(a.val, a.val), b.val, b.val)); + return v_sqrt(x); +} + +inline v_float32x4 v_sqr_magnitude(const v_float32x4& a, const v_float32x4& b) +{ + return v_float32x4(vmlaq_f32(vmulq_f32(a.val, a.val), b.val, b.val)); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ +#if CV_SIMD128_64F + // ARMv8, which adds support for 64-bit floating-point (so CV_SIMD128_64F is defined), + // also adds FMA support both for single- and double-precision floating-point vectors + return v_float32x4(vfmaq_f32(c.val, a.val, b.val)); +#else + return v_float32x4(vmlaq_f32(c.val, a.val, b.val)); +#endif +} + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_int32x4(vmlaq_s32(c.val, a.val, b.val)); +} + +inline v_float32x4 v_muladd(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +#if CV_SIMD128_64F +inline v_float64x2 v_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + v_float64x2 x(vaddq_f64(vmulq_f64(a.val, a.val), vmulq_f64(b.val, b.val))); + return v_sqrt(x); +} + +inline v_float64x2 v_sqr_magnitude(const v_float64x2& a, const v_float64x2& b) +{ + return v_float64x2(vaddq_f64(vmulq_f64(a.val, a.val), vmulq_f64(b.val, b.val))); +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_float64x2(vfmaq_f64(c.val, a.val, b.val)); +} + +inline v_float64x2 v_muladd(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return v_fma(a, b, c); +} +#endif + +// trade efficiency for convenience +#define OPENCV_HAL_IMPL_NEON_SHIFT_OP(_Tpvec, suffix, _Tps, ssuffix) \ +inline _Tpvec operator << (const _Tpvec& a, int n) \ +{ return _Tpvec(vshlq_##suffix(a.val, vdupq_n_##ssuffix((_Tps)n))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int n) \ +{ return _Tpvec(vshlq_##suffix(a.val, vdupq_n_##ssuffix((_Tps)-n))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(vshlq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(vshrq_n_##suffix(a.val, n)); } \ +template inline _Tpvec v_rshr(const _Tpvec& a) \ +{ return _Tpvec(vrshrq_n_##suffix(a.val, n)); } + +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint8x16, u8, schar, s8) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int8x16, s8, schar, s8) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint16x8, u16, short, s16) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int16x8, s16, short, s16) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint32x4, u32, int, s32) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int32x4, s32, int, s32) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_uint64x2, u64, int64, s64) +OPENCV_HAL_IMPL_NEON_SHIFT_OP(v_int64x2, s64, int64, s64) + +#define OPENCV_HAL_IMPL_NEON_ROTATE_OP(_Tpvec, suffix) \ +template inline _Tpvec v_rotate_right(const _Tpvec& a) \ +{ return _Tpvec(vextq_##suffix(a.val, vdupq_n_##suffix(0), n)); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a) \ +{ return _Tpvec(vextq_##suffix(vdupq_n_##suffix(0), a.val, _Tpvec::nlanes - n)); } \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a) \ +{ return a; } \ +template inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vextq_##suffix(a.val, b.val, n)); } \ +template inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vextq_##suffix(b.val, a.val, _Tpvec::nlanes - n)); } \ +template<> inline _Tpvec v_rotate_left<0>(const _Tpvec& a, const _Tpvec& b) \ +{ CV_UNUSED(b); return a; } + +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint8x16, u8) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int8x16, s8) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint16x8, u16) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int16x8, s16) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint32x4, u32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int32x4, s32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_float32x4, f32) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_uint64x2, u64) +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_int64x2, s64) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_ROTATE_OP(v_float64x2, f64) +#endif + +#if defined(__clang__) && defined(__aarch64__) +// avoid LD2 instruction. details: https://github.com/opencv/opencv/issues/14863 +#define OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ \ +typedef uint64 CV_DECL_ALIGNED(1) unaligned_uint64; \ +uint64 v = *(unaligned_uint64*)ptr; \ +return _Tpvec(v_reinterpret_as_##suffix(v_uint64x2(v, (uint64)123456))); \ +} +#else +#define OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(vcombine_##suffix(vld1_##suffix(ptr), vdup_n_##suffix((_Tp)0))); } +#endif + +#define OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(vld1q_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(vld1q_##suffix(ptr)); } \ +OPENCV_HAL_IMPL_NEON_LOAD_LOW_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(vcombine_##suffix(vld1_##suffix(ptr0), vld1_##suffix(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ vst1q_##suffix(ptr, a.val); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ vst1_##suffix(ptr, vget_low_##suffix(a.val)); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ vst1_##suffix(ptr, vget_high_##suffix(a.val)); } + +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_LOADSTORE_OP(v_float64x2, double, f64) +#endif + +inline unsigned v_reduce_sum(const v_uint8x16& a) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(a.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + int32x4_t t0 = vpaddlq_s16(vpaddlq_s8(a.val)); + int32x2_t t1 = vpadd_s32(vget_low_s32(t0), vget_high_s32(t0)); + return vget_lane_s32(vpadd_s32(t1, t1), 0); +} +inline unsigned v_reduce_sum(const v_uint16x8& a) +{ + uint32x4_t t0 = vpaddlq_u16(a.val); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline int v_reduce_sum(const v_int16x8& a) +{ + int32x4_t t0 = vpaddlq_s16(a.val); + int32x2_t t1 = vpadd_s32(vget_low_s32(t0), vget_high_s32(t0)); + return vget_lane_s32(vpadd_s32(t1, t1), 0); +} + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, a0),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_uint8x16, uint8x8, uchar, max, max, u8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_uint8x16, uint8x8, uchar, min, min, u8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_int8x16, int8x8, schar, max, max, s8) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_16(v_int8x16, int8x8, schar, min, min, s8) + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + a0 = vp##vectorfunc##_##suffix(a0, a0); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, a0),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_uint16x8, uint16x4, ushort, max, max, u16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_uint16x8, uint16x4, ushort, min, min, u16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_int16x8, int16x4, short, max, max, s16) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_8(v_int16x8, int16x4, short, min, min, s16) + +#define OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(_Tpvec, _Tpnvec, scalartype, func, vectorfunc, suffix) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + _Tpnvec##_t a0 = vp##vectorfunc##_##suffix(vget_low_##suffix(a.val), vget_high_##suffix(a.val)); \ + return (scalartype)vget_lane_##suffix(vp##vectorfunc##_##suffix(a0, vget_high_##suffix(a.val)),0); \ +} + +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, sum, add, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, max, max, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_uint32x4, uint32x2, unsigned, min, min, u32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, sum, add, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, max, max, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_int32x4, int32x2, int, min, min, s32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, sum, add, f32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, max, max, f32) +OPENCV_HAL_IMPL_NEON_REDUCE_OP_4(v_float32x4, float32x2, float, min, min, f32) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ return vget_lane_u64(vadd_u64(vget_low_u64(a.val), vget_high_u64(a.val)),0); } +inline int64 v_reduce_sum(const v_int64x2& a) +{ return vget_lane_s64(vadd_s64(vget_low_s64(a.val), vget_high_s64(a.val)),0); } +#if CV_SIMD128_64F +inline double v_reduce_sum(const v_float64x2& a) +{ + return vaddvq_f64(a.val); +} +#endif + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + float32x4x2_t ab = vtrnq_f32(a.val, b.val); + float32x4x2_t cd = vtrnq_f32(c.val, d.val); + + float32x4_t u0 = vaddq_f32(ab.val[0], ab.val[1]); // a0+a1 b0+b1 a2+a3 b2+b3 + float32x4_t u1 = vaddq_f32(cd.val[0], cd.val[1]); // c0+c1 d0+d1 c2+c3 d2+d3 + + float32x4_t v0 = vcombine_f32(vget_low_f32(u0), vget_low_f32(u1)); + float32x4_t v1 = vcombine_f32(vget_high_f32(u0), vget_high_f32(u1)); + + return v_float32x4(vaddq_f32(v0, v1)); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(vabdq_u8(a.val, b.val))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(vabdq_s8(a.val, b.val)))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + uint32x4_t t0 = vpaddlq_u16(vabdq_u16(a.val, b.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + uint32x4_t t0 = vpaddlq_u16(vreinterpretq_u16_s16(vabdq_s16(a.val, b.val))); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + uint32x4_t t0 = vabdq_u32(a.val, b.val); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + uint32x4_t t0 = vreinterpretq_u32_s32(vabdq_s32(a.val, b.val)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + float32x4_t t0 = vabdq_f32(a.val, b.val); + float32x2_t t1 = vpadd_f32(vget_low_f32(t0), vget_high_f32(t0)); + return vget_lane_f32(vpadd_f32(t1, t1), 0); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ return v_uint8x16(vcntq_u8(a.val)); } +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_uint8x16(vcntq_u8(vreinterpretq_u8_s8(a.val))); } +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ return v_uint16x8(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u16(a.val)))); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_uint16x8(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s16(a.val)))); } +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ return v_uint32x4(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u32(a.val))))); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_uint32x4(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s32(a.val))))); } +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ return v_uint64x2(vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(a.val)))))); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_uint64x2(vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vcntq_u8(vreinterpretq_u8_s64(a.val)))))); } + +inline int v_signmask(const v_uint8x16& a) +{ + int8x8_t m0 = vcreate_s8(CV_BIG_UINT(0x0706050403020100)); + uint8x16_t v0 = vshlq_u8(vshrq_n_u8(a.val, 7), vcombine_s8(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(v0))); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 8); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_uint16x8& a) +{ + int16x4_t m0 = vcreate_s16(CV_BIG_UINT(0x0003000200010000)); + uint16x8_t v0 = vshlq_u16(vshrq_n_u16(a.val, 15), vcombine_s16(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(vpaddlq_u16(v0)); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 4); +} +inline int v_signmask(const v_int16x8& a) +{ return v_signmask(v_reinterpret_as_u16(a)); } + +inline int v_signmask(const v_uint32x4& a) +{ + int32x2_t m0 = vcreate_s32(CV_BIG_UINT(0x0000000100000000)); + uint32x4_t v0 = vshlq_u32(vshrq_n_u32(a.val, 31), vcombine_s32(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(v0); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 2); +} +inline int v_signmask(const v_int32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_u32(a)); } +inline int v_signmask(const v_uint64x2& a) +{ + int64x1_t m0 = vdup_n_s64(0); + uint64x2_t v0 = vshlq_u64(vshrq_n_u64(a.val, 63), vcombine_s64(m0, m0)); + return (int)vgetq_lane_u64(v0, 0) + ((int)vgetq_lane_u64(v0, 1) << 1); +} +inline int v_signmask(const v_int64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +#if CV_SIMD128_64F +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_u64(a)); } +#endif + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +#if CV_SIMD128_64F +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } +#endif + +#define OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(_Tpvec, suffix, shift) \ +inline bool v_check_all(const v_##_Tpvec& a) \ +{ \ + _Tpvec##_t v0 = vshrq_n_##suffix(vmvnq_##suffix(a.val), shift); \ + uint64x2_t v1 = vreinterpretq_u64_##suffix(v0); \ + return (vgetq_lane_u64(v1, 0) | vgetq_lane_u64(v1, 1)) == 0; \ +} \ +inline bool v_check_any(const v_##_Tpvec& a) \ +{ \ + _Tpvec##_t v0 = vshrq_n_##suffix(a.val, shift); \ + uint64x2_t v1 = vreinterpretq_u64_##suffix(v0); \ + return (vgetq_lane_u64(v1, 0) | vgetq_lane_u64(v1, 1)) != 0; \ +} + +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint8x16, u8, 7) +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint16x8, u16, 15) +OPENCV_HAL_IMPL_NEON_CHECK_ALLANY(uint32x4, u32, 31) + +inline bool v_check_all(const v_uint64x2& a) +{ + uint64x2_t v0 = vshrq_n_u64(a.val, 63); + return (vgetq_lane_u64(v0, 0) & vgetq_lane_u64(v0, 1)) == 1; +} +inline bool v_check_any(const v_uint64x2& a) +{ + uint64x2_t v0 = vshrq_n_u64(a.val, 63); + return (vgetq_lane_u64(v0, 0) | vgetq_lane_u64(v0, 1)) != 0; +} + +inline bool v_check_all(const v_int8x16& a) +{ return v_check_all(v_reinterpret_as_u8(a)); } +inline bool v_check_all(const v_int16x8& a) +{ return v_check_all(v_reinterpret_as_u16(a)); } +inline bool v_check_all(const v_int32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_u32(a)); } + +inline bool v_check_any(const v_int8x16& a) +{ return v_check_any(v_reinterpret_as_u8(a)); } +inline bool v_check_any(const v_int16x8& a) +{ return v_check_any(v_reinterpret_as_u16(a)); } +inline bool v_check_any(const v_int32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_u32(a)); } + +inline bool v_check_all(const v_int64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_int64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +#if CV_SIMD128_64F +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_u64(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_u64(a)); } +#endif + +#define OPENCV_HAL_IMPL_NEON_SELECT(_Tpvec, suffix, usuffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(vbslq_##suffix(vreinterpretq_##usuffix##_##suffix(mask.val), a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_NEON_SELECT(v_uint8x16, u8, u8) +OPENCV_HAL_IMPL_NEON_SELECT(v_int8x16, s8, u8) +OPENCV_HAL_IMPL_NEON_SELECT(v_uint16x8, u16, u16) +OPENCV_HAL_IMPL_NEON_SELECT(v_int16x8, s16, u16) +OPENCV_HAL_IMPL_NEON_SELECT(v_uint32x4, u32, u32) +OPENCV_HAL_IMPL_NEON_SELECT(v_int32x4, s32, u32) +OPENCV_HAL_IMPL_NEON_SELECT(v_float32x4, f32, u32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_SELECT(v_float64x2, f64, u64) +#endif + +#if CV_NEON_AARCH64 +#define OPENCV_HAL_IMPL_NEON_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = vmovl_##suffix(vget_low_##suffix(a.val)); \ + b1.val = vmovl_high_##suffix(a.val); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_##suffix(vget_low_##suffix(a.val))); \ +} \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_high_##suffix(a.val)); \ +} \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + return _Tpwvec(vmovl_##suffix(vld1_##suffix(ptr))); \ +} +#else +#define OPENCV_HAL_IMPL_NEON_EXPAND(_Tpvec, _Tpwvec, _Tp, suffix) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = vmovl_##suffix(vget_low_##suffix(a.val)); \ + b1.val = vmovl_##suffix(vget_high_##suffix(a.val)); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_##suffix(vget_low_##suffix(a.val))); \ +} \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ \ + return _Tpwvec(vmovl_##suffix(vget_high_##suffix(a.val))); \ +} \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + return _Tpwvec(vmovl_##suffix(vld1_##suffix(ptr))); \ +} +#endif + +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint8x16, v_uint16x8, uchar, u8) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int8x16, v_int16x8, schar, s8) +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint16x8, v_uint32x4, ushort, u16) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int16x8, v_int32x4, short, s16) +OPENCV_HAL_IMPL_NEON_EXPAND(v_uint32x4, v_uint64x2, uint, u32) +OPENCV_HAL_IMPL_NEON_EXPAND(v_int32x4, v_int64x2, int, s32) + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + typedef unsigned int CV_DECL_ALIGNED(1) unaligned_uint; + uint8x8_t v0 = vcreate_u8(*(unaligned_uint*)ptr); + uint16x4_t v1 = vget_low_u16(vmovl_u8(v0)); + return v_uint32x4(vmovl_u16(v1)); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + typedef unsigned int CV_DECL_ALIGNED(1) unaligned_uint; + int8x8_t v0 = vcreate_s8(*(unaligned_uint*)ptr); + int16x4_t v1 = vget_low_s16(vmovl_s8(v0)); + return v_int32x4(vmovl_s16(v1)); +} + +#if defined(__aarch64__) || defined(_M_ARM64) +#define OPENCV_HAL_IMPL_NEON_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const v_##_Tpvec& a0, const v_##_Tpvec& a1, v_##_Tpvec& b0, v_##_Tpvec& b1) \ +{ \ + b0.val = vzip1q_##suffix(a0.val, a1.val); \ + b1.val = vzip2q_##suffix(a0.val, a1.val); \ +} \ +inline v_##_Tpvec v_combine_low(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val))); \ +} \ +inline v_##_Tpvec v_combine_high(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val))); \ +} \ +inline void v_recombine(const v_##_Tpvec& a, const v_##_Tpvec& b, v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + c.val = vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val)); \ + d.val = vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val)); \ +} +#else +#define OPENCV_HAL_IMPL_NEON_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const v_##_Tpvec& a0, const v_##_Tpvec& a1, v_##_Tpvec& b0, v_##_Tpvec& b1) \ +{ \ + _Tpvec##x2_t p = vzipq_##suffix(a0.val, a1.val); \ + b0.val = p.val[0]; \ + b1.val = p.val[1]; \ +} \ +inline v_##_Tpvec v_combine_low(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val))); \ +} \ +inline v_##_Tpvec v_combine_high(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val))); \ +} \ +inline void v_recombine(const v_##_Tpvec& a, const v_##_Tpvec& b, v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + c.val = vcombine_##suffix(vget_low_##suffix(a.val), vget_low_##suffix(b.val)); \ + d.val = vcombine_##suffix(vget_high_##suffix(a.val), vget_high_##suffix(b.val)); \ +} +#endif + +OPENCV_HAL_IMPL_NEON_UNPACKS(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_UNPACKS(int8x16, s8) +OPENCV_HAL_IMPL_NEON_UNPACKS(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_UNPACKS(int16x8, s16) +OPENCV_HAL_IMPL_NEON_UNPACKS(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_UNPACKS(int32x4, s32) +OPENCV_HAL_IMPL_NEON_UNPACKS(float32x4, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_UNPACKS(float64x2, f64) +#endif + +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + uint8x16_t vec = vrev64q_u8(a.val); + return v_uint8x16(vextq_u8(vec, vec, 8)); +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + uint16x8_t vec = vrev64q_u16(a.val); + return v_uint16x8(vextq_u16(vec, vec, 4)); +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + uint32x4_t vec = vrev64q_u32(a.val); + return v_uint32x4(vextq_u32(vec, vec, 2)); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + uint64x2_t vec = a.val; + uint64x1_t vec_lo = vget_low_u64(vec); + uint64x1_t vec_hi = vget_high_u64(vec); + return v_uint64x2(vcombine_u64(vec_hi, vec_lo)); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +#if CV_SIMD128_64F +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } +#endif + +#define OPENCV_HAL_IMPL_NEON_EXTRACT(_Tpvec, suffix) \ +template \ +inline v_##_Tpvec v_extract(const v_##_Tpvec& a, const v_##_Tpvec& b) \ +{ \ + return v_##_Tpvec(vextq_##suffix(a.val, b.val, s)); \ +} + +OPENCV_HAL_IMPL_NEON_EXTRACT(uint8x16, u8) +OPENCV_HAL_IMPL_NEON_EXTRACT(int8x16, s8) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint16x8, u16) +OPENCV_HAL_IMPL_NEON_EXTRACT(int16x8, s16) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_EXTRACT(int32x4, s32) +OPENCV_HAL_IMPL_NEON_EXTRACT(uint64x2, u64) +OPENCV_HAL_IMPL_NEON_EXTRACT(int64x2, s64) +OPENCV_HAL_IMPL_NEON_EXTRACT(float32x4, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_EXTRACT(float64x2, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_EXTRACT_N(_Tpvec, _Tp, suffix) \ +template inline _Tp v_extract_n(_Tpvec v) { return vgetq_lane_##suffix(v.val, i); } + +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint32x4, uint, u32) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_EXTRACT_N(v_float64x2, double, f64) +#endif + +#define OPENCV_HAL_IMPL_NEON_BROADCAST(_Tpvec, _Tp, suffix) \ +template inline _Tpvec v_broadcast_element(_Tpvec v) { _Tp t = v_extract_n(v); return v_setall_##suffix(t); } + +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint32x4, uint, u32) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_int64x2, int64, s64) +OPENCV_HAL_IMPL_NEON_BROADCAST(v_float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_BROADCAST(v_float64x2, double, f64) +#endif + +#if CV_SIMD128_64F +inline v_int32x4 v_round(const v_float32x4& a) +{ + float32x4_t a_ = a.val; + int32x4_t result; + __asm__ ("fcvtns %0.4s, %1.4s" + : "=w"(result) + : "w"(a_) + : /* No clobbers */); + return v_int32x4(result); +} +#else +inline v_int32x4 v_round(const v_float32x4& a) +{ + static const int32x4_t v_sign = vdupq_n_s32(1 << 31), + v_05 = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); + + int32x4_t v_addition = vorrq_s32(v_05, vandq_s32(v_sign, vreinterpretq_s32_f32(a.val))); + return v_int32x4(vcvtq_s32_f32(vaddq_f32(a.val, vreinterpretq_f32_s32(v_addition)))); +} +#endif +inline v_int32x4 v_floor(const v_float32x4& a) +{ + int32x4_t a1 = vcvtq_s32_f32(a.val); + uint32x4_t mask = vcgtq_f32(vcvtq_f32_s32(a1), a.val); + return v_int32x4(vaddq_s32(a1, vreinterpretq_s32_u32(mask))); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + int32x4_t a1 = vcvtq_s32_f32(a.val); + uint32x4_t mask = vcgtq_f32(a.val, vcvtq_f32_s32(a1)); + return v_int32x4(vsubq_s32(a1, vreinterpretq_s32_u32(mask))); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(vcvtq_s32_f32(a.val)); } + +#if CV_SIMD128_64F +inline v_int32x4 v_round(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), zero)); +} + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), vmovn_s64(vcvtaq_s64_f64(b.val)))); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + int64x2_t a1 = vcvtq_s64_f64(a.val); + uint64x2_t mask = vcgtq_f64(vcvtq_f64_s64(a1), a.val); + a1 = vaddq_s64(a1, vreinterpretq_s64_u64(mask)); + return v_int32x4(vcombine_s32(vmovn_s64(a1), zero)); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + int64x2_t a1 = vcvtq_s64_f64(a.val); + uint64x2_t mask = vcgtq_f64(a.val, vcvtq_f64_s64(a1)); + a1 = vsubq_s64(a1, vreinterpretq_s64_u64(mask)); + return v_int32x4(vcombine_s32(vmovn_s64(a1), zero)); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ + static const int32x2_t zero = vdup_n_s32(0); + return v_int32x4(vcombine_s32(vmovn_s64(vcvtaq_s64_f64(a.val)), zero)); +} +#endif + +#define OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const v_##_Tpvec& a0, const v_##_Tpvec& a1, \ + const v_##_Tpvec& a2, const v_##_Tpvec& a3, \ + v_##_Tpvec& b0, v_##_Tpvec& b1, \ + v_##_Tpvec& b2, v_##_Tpvec& b3) \ +{ \ + /* m00 m01 m02 m03 */ \ + /* m10 m11 m12 m13 */ \ + /* m20 m21 m22 m23 */ \ + /* m30 m31 m32 m33 */ \ + _Tpvec##x2_t t0 = vtrnq_##suffix(a0.val, a1.val); \ + _Tpvec##x2_t t1 = vtrnq_##suffix(a2.val, a3.val); \ + /* m00 m10 m02 m12 */ \ + /* m01 m11 m03 m13 */ \ + /* m20 m30 m22 m32 */ \ + /* m21 m31 m23 m33 */ \ + b0.val = vcombine_##suffix(vget_low_##suffix(t0.val[0]), vget_low_##suffix(t1.val[0])); \ + b1.val = vcombine_##suffix(vget_low_##suffix(t0.val[1]), vget_low_##suffix(t1.val[1])); \ + b2.val = vcombine_##suffix(vget_high_##suffix(t0.val[0]), vget_high_##suffix(t1.val[0])); \ + b3.val = vcombine_##suffix(vget_high_##suffix(t0.val[1]), vget_high_##suffix(t1.val[1])); \ +} + +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(uint32x4, u32) +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(int32x4, s32) +OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(float32x4, f32) + +#define OPENCV_HAL_IMPL_NEON_INTERLEAVED(_Tpvec, _Tp, suffix) \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \ +{ \ + _Tpvec##x2_t v = vld2q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \ +{ \ + _Tpvec##x3_t v = vld3q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ + c.val = v.val[2]; \ +} \ +inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \ + v_##_Tpvec& c, v_##_Tpvec& d) \ +{ \ + _Tpvec##x4_t v = vld4q_##suffix(ptr); \ + a.val = v.val[0]; \ + b.val = v.val[1]; \ + c.val = v.val[2]; \ + d.val = v.val[3]; \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + _Tpvec##x2_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + vst2q_##suffix(ptr, v); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + _Tpvec##x3_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + v.val[2] = c.val; \ + vst3q_##suffix(ptr, v); \ +} \ +inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \ + const v_##_Tpvec& c, const v_##_Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec##x4_t v; \ + v.val[0] = a.val; \ + v.val[1] = b.val; \ + v.val[2] = c.val; \ + v.val[3] = d.val; \ + vst4q_##suffix(ptr, v); \ +} + +#define OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(tp, suffix) \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, v_##tp##x2& b ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t a1 = vld1_##suffix(ptr + 2); \ + tp##x1_t b1 = vld1_##suffix(ptr + 3); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ +} \ + \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, \ + v_##tp##x2& b, v_##tp##x2& c ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t c0 = vld1_##suffix(ptr + 2); \ + tp##x1_t a1 = vld1_##suffix(ptr + 3); \ + tp##x1_t b1 = vld1_##suffix(ptr + 4); \ + tp##x1_t c1 = vld1_##suffix(ptr + 5); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ + c = v_##tp##x2(vcombine_##suffix(c0, c1)); \ +} \ + \ +inline void v_load_deinterleave( const tp* ptr, v_##tp##x2& a, v_##tp##x2& b, \ + v_##tp##x2& c, v_##tp##x2& d ) \ +{ \ + tp##x1_t a0 = vld1_##suffix(ptr); \ + tp##x1_t b0 = vld1_##suffix(ptr + 1); \ + tp##x1_t c0 = vld1_##suffix(ptr + 2); \ + tp##x1_t d0 = vld1_##suffix(ptr + 3); \ + tp##x1_t a1 = vld1_##suffix(ptr + 4); \ + tp##x1_t b1 = vld1_##suffix(ptr + 5); \ + tp##x1_t c1 = vld1_##suffix(ptr + 6); \ + tp##x1_t d1 = vld1_##suffix(ptr + 7); \ + a = v_##tp##x2(vcombine_##suffix(a0, a1)); \ + b = v_##tp##x2(vcombine_##suffix(b0, b1)); \ + c = v_##tp##x2(vcombine_##suffix(c0, c1)); \ + d = v_##tp##x2(vcombine_##suffix(d0, d1)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, const v_##tp##x2& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 3, vget_high_##suffix(b.val)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, \ + const v_##tp##x2& b, const v_##tp##x2& c, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_low_##suffix(c.val)); \ + vst1_##suffix(ptr + 3, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 4, vget_high_##suffix(b.val)); \ + vst1_##suffix(ptr + 5, vget_high_##suffix(c.val)); \ +} \ + \ +inline void v_store_interleave( tp* ptr, const v_##tp##x2& a, const v_##tp##x2& b, \ + const v_##tp##x2& c, const v_##tp##x2& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ \ + vst1_##suffix(ptr, vget_low_##suffix(a.val)); \ + vst1_##suffix(ptr + 1, vget_low_##suffix(b.val)); \ + vst1_##suffix(ptr + 2, vget_low_##suffix(c.val)); \ + vst1_##suffix(ptr + 3, vget_low_##suffix(d.val)); \ + vst1_##suffix(ptr + 4, vget_high_##suffix(a.val)); \ + vst1_##suffix(ptr + 5, vget_high_##suffix(b.val)); \ + vst1_##suffix(ptr + 6, vget_high_##suffix(c.val)); \ + vst1_##suffix(ptr + 7, vget_high_##suffix(d.val)); \ +} + +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint8x16, uchar, u8) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int8x16, schar, s8) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint16x8, ushort, u16) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int16x8, short, s16) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(int32x4, int, s32) +OPENCV_HAL_IMPL_NEON_INTERLEAVED(float32x4, float, f32) +#if CV_SIMD128_64F +OPENCV_HAL_IMPL_NEON_INTERLEAVED(float64x2, double, f64) +#endif + +OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(int64, s64) +OPENCV_HAL_IMPL_NEON_INTERLEAVED_INT64(uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(vcvtq_f32_s32(a.val)); +} + +#if CV_SIMD128_64F +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + float32x2_t zero = vdup_n_f32(0.0f); + return v_float32x4(vcombine_f32(vcvt_f32_f64(a.val), zero)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(vcombine_f32(vcvt_f32_f64(a.val), vcvt_f32_f64(b.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vcvt_f32_s32(vget_low_s32(a.val)))); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vcvt_f32_s32(vget_high_s32(a.val)))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vget_low_f32(a.val))); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(vcvt_f64_f32(vget_high_f32(a.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ return v_float64x2(vcvtq_f64_s64(a.val)); } + +#endif + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[ 0]], + tab[idx[ 1]], + tab[idx[ 2]], + tab[idx[ 3]], + tab[idx[ 4]], + tab[idx[ 5]], + tab[idx[ 6]], + tab[idx[ 7]], + tab[idx[ 8]], + tab[idx[ 9]], + tab[idx[10]], + tab[idx[11]], + tab[idx[12]], + tab[idx[13]], + tab[idx[14]], + tab[idx[15]] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[4]], + tab[idx[4] + 1], + tab[idx[5]], + tab[idx[5] + 1], + tab[idx[6]], + tab[idx[6] + 1], + tab[idx[7]], + tab[idx[7] + 1] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + schar CV_DECL_ALIGNED(32) elems[16] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[0] + 2], + tab[idx[0] + 3], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[1] + 2], + tab[idx[1] + 3], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[2] + 2], + tab[idx[2] + 3], + tab[idx[3]], + tab[idx[3] + 1], + tab[idx[3] + 2], + tab[idx[3] + 3] + }; + return v_int8x16(vld1q_s8(elems)); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]], + tab[idx[4]], + tab[idx[5]], + tab[idx[6]], + tab[idx[7]] + }; + return v_int16x8(vld1q_s16(elems)); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + short CV_DECL_ALIGNED(32) elems[8] = + { + tab[idx[0]], + tab[idx[0] + 1], + tab[idx[1]], + tab[idx[1] + 1], + tab[idx[2]], + tab[idx[2] + 1], + tab[idx[3]], + tab[idx[3] + 1] + }; + return v_int16x8(vld1q_s16(elems)); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(vcombine_s16(vld1_s16(tab + idx[0]), vld1_s16(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_int32x4(vld1q_s32(elems)); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(vcombine_s32(vld1_s32(tab + idx[0]), vld1_s32(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(vld1q_s32(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(vcombine_s64(vcreate_s64(tab[idx[0]]), vcreate_s64(tab[idx[1]]))); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(vld1q_s64(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[idx[0]], + tab[idx[1]], + tab[idx[2]], + tab[idx[3]] + }; + return v_float32x4(vld1q_f32(elems)); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) +{ + typedef uint64 CV_DECL_ALIGNED(1) unaligned_uint64; + + uint64 CV_DECL_ALIGNED(32) elems[2] = + { + *(unaligned_uint64*)(tab + idx[0]), + *(unaligned_uint64*)(tab + idx[1]) + }; + return v_float32x4(vreinterpretq_f32_u64(vld1q_u64(elems))); +} +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) +{ + return v_float32x4(vld1q_f32(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_int32x4(vld1q_s32(elems)); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + unsigned CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_uint32x4(vld1q_u32(elems)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + float CV_DECL_ALIGNED(32) elems[4] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + tab[vgetq_lane_s32(idxvec.val, 2)], + tab[vgetq_lane_s32(idxvec.val, 3)] + }; + return v_float32x4(vld1q_f32(elems)); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + /*int CV_DECL_ALIGNED(32) idx[4]; + v_store(idx, idxvec); + + float32x4_t xy02 = vcombine_f32(vld1_f32(tab + idx[0]), vld1_f32(tab + idx[2])); + float32x4_t xy13 = vcombine_f32(vld1_f32(tab + idx[1]), vld1_f32(tab + idx[3])); + + float32x4x2_t xxyy = vuzpq_f32(xy02, xy13); + x = v_float32x4(xxyy.val[0]); + y = v_float32x4(xxyy.val[1]);*/ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + x = v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); + y = v_float32x4(tab[idx[0]+1], tab[idx[1]+1], tab[idx[2]+1], tab[idx[3]+1]); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + return v_int8x16(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0705060403010200)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0705060403010200)))); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + return v_int8x16(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0703060205010400)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0703060205010400)))); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + return v_int16x8(vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0706030205040100)), vtbl1_s8(vget_high_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0706030205040100))))); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + int16x4x2_t res = vzip_s16(vget_low_s16(vec.val), vget_high_s16(vec.val)); + return v_int16x8(vcombine_s16(res.val[0], res.val[1])); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + int32x2x2_t res = vzip_s32(vget_low_s32(vec.val), vget_high_s32(vec.val)); + return v_int32x4(vcombine_s32(res.val[0], res.val[1])); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + return v_int8x16(vextq_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vec.val), vcreate_s8(0x0605040201000000)), vtbl1_s8(vget_high_s8(vec.val), vcreate_s8(0x0807060504020100))), vdupq_n_s8(0), 2)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + return v_int16x8(vreinterpretq_s16_s8(vextq_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0504030201000000)), vget_high_s8(vreinterpretq_s8_s16(vec.val))), vdupq_n_s8(0), 2))); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +#if CV_SIMD128_64F +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[idx[0]], + tab[idx[1]] + }; + return v_float64x2(vld1q_f64(elems)); +} + +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(vld1q_f64(tab + idx[0])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + double CV_DECL_ALIGNED(32) elems[2] = + { + tab[vgetq_lane_s32(idxvec.val, 0)], + tab[vgetq_lane_s32(idxvec.val, 1)], + }; + return v_float64x2(vld1q_f64(elems)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + + x = v_float64x2(tab[idx[0]], tab[idx[1]]); + y = v_float64x2(tab[idx[0]+1], tab[idx[1]+1]); +} +#endif + +////// FP16 support /////// +#if CV_FP16 +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + float16x4_t v = + #ifndef vld1_f16 // APPLE compiler defines vld1_f16 as macro + (float16x4_t)vld1_s16((const short*)ptr); + #else + vld1_f16((const __fp16*)ptr); + #endif + return v_float32x4(vcvt_f32_f16(v)); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + float16x4_t hv = vcvt_f16_f32(v.val); + + #ifndef vst1_f16 // APPLE compiler defines vst1_f16 as macro + vst1_s16((short*)ptr, (int16x4_t)hv); + #else + vst1_f16((__fp16*)ptr, hv); + #endif +} +#else +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + const int N = 4; + float buf[N]; + for( int i = 0; i < N; i++ ) buf[i] = (float)ptr[i]; + return v_load(buf); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + const int N = 4; + float buf[N]; + v_store(buf, v); + for( int i = 0; i < N; i++ ) ptr[i] = float16_t(buf[i]); +} +#endif + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse.hpp new file mode 100644 index 0000000..f4b43a2 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse.hpp @@ -0,0 +1,3455 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_SSE_HPP +#define OPENCV_HAL_SSE_HPP + +#include +#include "opencv2/core/utility.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 +#define CV_SIMD128_FP16 0 // no native operations with FP16 type. + +namespace cv +{ + +//! @cond IGNORED + +// +// Compilation troubleshooting: +// - MSVC: error C2719: 'a': formal parameter with requested alignment of 16 won't be aligned +// Replace parameter declaration to const reference: +// -v_int32x4 a +// +const v_int32x4& a +// + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + typedef __m128i vector_type; + enum { nlanes = 16 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_uint8x16() {} + explicit v_uint8x16(__m128i v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + val = _mm_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6, (char)v7, + (char)v8, (char)v9, (char)v10, (char)v11, + (char)v12, (char)v13, (char)v14, (char)v15); + } + + uchar get0() const + { + return (uchar)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + typedef __m128i vector_type; + enum { nlanes = 16 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_int8x16() {} + explicit v_int8x16(__m128i v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + val = _mm_setr_epi8((char)v0, (char)v1, (char)v2, (char)v3, + (char)v4, (char)v5, (char)v6, (char)v7, + (char)v8, (char)v9, (char)v10, (char)v11, + (char)v12, (char)v13, (char)v14, (char)v15); + } + + schar get0() const + { + return (schar)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + typedef __m128i vector_type; + enum { nlanes = 8 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_uint16x8() {} + explicit v_uint16x8(__m128i v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + val = _mm_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7); + } + + ushort get0() const + { + return (ushort)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + typedef __m128i vector_type; + enum { nlanes = 8 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_int16x8() {} + explicit v_int16x8(__m128i v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + val = _mm_setr_epi16((short)v0, (short)v1, (short)v2, (short)v3, + (short)v4, (short)v5, (short)v6, (short)v7); + } + + short get0() const + { + return (short)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + typedef __m128i vector_type; + enum { nlanes = 4 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_uint32x4() {} + explicit v_uint32x4(__m128i v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + val = _mm_setr_epi32((int)v0, (int)v1, (int)v2, (int)v3); + } + + unsigned get0() const + { + return (unsigned)_mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + typedef __m128i vector_type; + enum { nlanes = 4 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_int32x4() {} + explicit v_int32x4(__m128i v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + val = _mm_setr_epi32(v0, v1, v2, v3); + } + + int get0() const + { + return _mm_cvtsi128_si32(val); + } + + __m128i val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + typedef __m128 vector_type; + enum { nlanes = 4 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_float32x4() {} + explicit v_float32x4(__m128 v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + val = _mm_setr_ps(v0, v1, v2, v3); + } + + float get0() const + { + return _mm_cvtss_f32(val); + } + + __m128 val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + typedef __m128i vector_type; + enum { nlanes = 2 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_uint64x2() {} + explicit v_uint64x2(__m128i v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + val = _mm_setr_epi32((int)v0, (int)(v0 >> 32), (int)v1, (int)(v1 >> 32)); + } + + uint64 get0() const + { + #if !defined(__x86_64__) && !defined(_M_X64) + int a = _mm_cvtsi128_si32(val); + int b = _mm_cvtsi128_si32(_mm_srli_epi64(val, 32)); + return (unsigned)a | ((uint64)(unsigned)b << 32); + #else + return (uint64)_mm_cvtsi128_si64(val); + #endif + } + + __m128i val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + typedef __m128i vector_type; + enum { nlanes = 2 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_int64x2() {} + explicit v_int64x2(__m128i v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + val = _mm_setr_epi32((int)v0, (int)(v0 >> 32), (int)v1, (int)(v1 >> 32)); + } + + int64 get0() const + { + #if !defined(__x86_64__) && !defined(_M_X64) + int a = _mm_cvtsi128_si32(val); + int b = _mm_cvtsi128_si32(_mm_srli_epi64(val, 32)); + return (int64)((unsigned)a | ((uint64)(unsigned)b << 32)); + #else + return _mm_cvtsi128_si64(val); + #endif + } + + __m128i val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + typedef __m128d vector_type; + enum { nlanes = 2 }; + + /* coverity[uninit_ctor]: suppress warning */ + v_float64x2() {} + explicit v_float64x2(__m128d v) : val(v) {} + v_float64x2(double v0, double v1) + { + val = _mm_setr_pd(v0, v1); + } + + double get0() const + { + return _mm_cvtsd_f64(val); + } + + __m128d val; +}; + +namespace hal_sse_internal +{ + template + to_sse_type v_sse_reinterpret_as(const from_sse_type& val); + +#define OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(to_sse_type, from_sse_type, sse_cast_intrin) \ + template<> inline \ + to_sse_type v_sse_reinterpret_as(const from_sse_type& a) \ + { return sse_cast_intrin(a); } + + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128i, OPENCV_HAL_NOP) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128, _mm_castps_si128) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128i, __m128d, _mm_castpd_si128) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128i, _mm_castsi128_ps) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128, OPENCV_HAL_NOP) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128, __m128d, _mm_castpd_ps) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128i, _mm_castsi128_pd) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128, _mm_castps_pd) + OPENCV_HAL_IMPL_SSE_REINTERPRET_RAW(__m128d, __m128d, OPENCV_HAL_NOP) +} + +#define OPENCV_HAL_IMPL_SSE_INITVEC(_Tpvec, _Tp, suffix, zsuffix, ssuffix, _Tps, cast) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(_mm_setzero_##zsuffix()); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(_mm_set1_##ssuffix((_Tps)v)); } \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0& a) \ +{ return _Tpvec(cast(a.val)); } + +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint8x16, uchar, u8, si128, epi8, schar, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int8x16, schar, s8, si128, epi8, schar, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint16x8, ushort, u16, si128, epi16, short, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int16x8, short, s16, si128, epi16, short, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_uint32x4, unsigned, u32, si128, epi32, int, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_int32x4, int, s32, si128, epi32, int, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_INITVEC(v_float32x4, float, f32, ps, ps, float, _mm_castsi128_ps) +OPENCV_HAL_IMPL_SSE_INITVEC(v_float64x2, double, f64, pd, pd, double, _mm_castsi128_pd) + +inline v_uint64x2 v_setzero_u64() { return v_uint64x2(_mm_setzero_si128()); } +inline v_int64x2 v_setzero_s64() { return v_int64x2(_mm_setzero_si128()); } +inline v_uint64x2 v_setall_u64(uint64 val) { return v_uint64x2(val, val); } +inline v_int64x2 v_setall_s64(int64 val) { return v_int64x2(val, val); } + +template inline +v_uint64x2 v_reinterpret_as_u64(const _Tpvec& a) { return v_uint64x2(a.val); } +template inline +v_int64x2 v_reinterpret_as_s64(const _Tpvec& a) { return v_int64x2(a.val); } +inline v_float32x4 v_reinterpret_as_f32(const v_uint64x2& a) +{ return v_float32x4(_mm_castsi128_ps(a.val)); } +inline v_float32x4 v_reinterpret_as_f32(const v_int64x2& a) +{ return v_float32x4(_mm_castsi128_ps(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_uint64x2& a) +{ return v_float64x2(_mm_castsi128_pd(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_int64x2& a) +{ return v_float64x2(_mm_castsi128_pd(a.val)); } + +#define OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(_Tpvec, suffix) \ +inline _Tpvec v_reinterpret_as_##suffix(const v_float32x4& a) \ +{ return _Tpvec(_mm_castps_si128(a.val)); } \ +inline _Tpvec v_reinterpret_as_##suffix(const v_float64x2& a) \ +{ return _Tpvec(_mm_castpd_si128(a.val)); } + +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint8x16, u8) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int8x16, s8) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint16x8, u16) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int16x8, s16) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint32x4, u32) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int32x4, s32) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_uint64x2, u64) +OPENCV_HAL_IMPL_SSE_INIT_FROM_FLT(v_int64x2, s64) + +inline v_float32x4 v_reinterpret_as_f32(const v_float32x4& a) {return a; } +inline v_float64x2 v_reinterpret_as_f64(const v_float64x2& a) {return a; } +inline v_float32x4 v_reinterpret_as_f32(const v_float64x2& a) {return v_float32x4(_mm_castpd_ps(a.val)); } +inline v_float64x2 v_reinterpret_as_f64(const v_float32x4& a) {return v_float64x2(_mm_castps_pd(a.val)); } + +//////////////// PACK /////////////// +inline v_uint8x16 v_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + __m128i delta = _mm_set1_epi16(255); + return v_uint8x16(_mm_packus_epi16(_mm_subs_epu16(a.val, _mm_subs_epu16(a.val, delta)), + _mm_subs_epu16(b.val, _mm_subs_epu16(b.val, delta)))); +} + +inline void v_pack_store(uchar* ptr, const v_uint16x8& a) +{ + __m128i delta = _mm_set1_epi16(255); + __m128i a1 = _mm_subs_epu16(a.val, _mm_subs_epu16(a.val, delta)); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +inline v_uint8x16 v_pack_u(const v_int16x8& a, const v_int16x8& b) +{ return v_uint8x16(_mm_packus_epi16(a.val, b.val)); } + +inline void v_pack_u_store(uchar* ptr, const v_int16x8& a) +{ _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a.val, a.val)); } + +template inline +v_uint8x16 v_rshr_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_uint8x16(_mm_packus_epi16(_mm_srli_epi16(_mm_adds_epu16(a.val, delta), n), + _mm_srli_epi16(_mm_adds_epu16(b.val, delta), n))); +} + +template inline +void v_rshr_pack_store(uchar* ptr, const v_uint16x8& a) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srli_epi16(_mm_adds_epu16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +template inline +v_uint8x16 v_rshr_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_uint8x16(_mm_packus_epi16(_mm_srai_epi16(_mm_adds_epi16(a.val, delta), n), + _mm_srai_epi16(_mm_adds_epi16(b.val, delta), n))); +} + +template inline +void v_rshr_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srai_epi16(_mm_adds_epi16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi16(a1, a1)); +} + +inline v_int8x16 v_pack(const v_int16x8& a, const v_int16x8& b) +{ return v_int8x16(_mm_packs_epi16(a.val, b.val)); } + +inline void v_pack_store(schar* ptr, const v_int16x8& a) +{ _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi16(a.val, a.val)); } + +template inline +v_int8x16 v_rshr_pack(const v_int16x8& a, const v_int16x8& b) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + return v_int8x16(_mm_packs_epi16(_mm_srai_epi16(_mm_adds_epi16(a.val, delta), n), + _mm_srai_epi16(_mm_adds_epi16(b.val, delta), n))); +} +template inline +void v_rshr_pack_store(schar* ptr, const v_int16x8& a) +{ + // we assume that n > 0, and so the shifted 16-bit values can be treated as signed numbers. + __m128i delta = _mm_set1_epi16((short)(1 << (n-1))); + __m128i a1 = _mm_srai_epi16(_mm_adds_epi16(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi16(a1, a1)); +} + + +// byte-wise "mask ? a : b" +inline __m128i v_select_si128(__m128i mask, __m128i a, __m128i b) +{ +#if CV_SSE4_1 + return _mm_blendv_epi8(b, a, mask); +#else + return _mm_xor_si128(b, _mm_and_si128(_mm_xor_si128(a, b), mask)); +#endif +} + +inline v_uint16x8 v_pack(const v_uint32x4& a, const v_uint32x4& b) +{ return v_uint16x8(_v128_packs_epu32(a.val, b.val)); } + +inline void v_pack_store(ushort* ptr, const v_uint32x4& a) +{ + __m128i z = _mm_setzero_si128(), maxval32 = _mm_set1_epi32(65535), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(v_select_si128(_mm_cmpgt_epi32(z, a.val), maxval32, a.val), delta32); + __m128i r = _mm_packs_epi32(a1, a1); + _mm_storel_epi64((__m128i*)ptr, _mm_sub_epi16(r, _mm_set1_epi16(-32768))); +} + +template inline +v_uint16x8 v_rshr_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i b1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(b.val, delta), n), delta32); + return v_uint16x8(_mm_sub_epi16(_mm_packs_epi32(a1, b1), _mm_set1_epi16(-32768))); +} + +template inline +void v_rshr_pack_store(ushort* ptr, const v_uint32x4& a) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srli_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +inline v_uint16x8 v_pack_u(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_packus_epi32(a.val, b.val)); +#else + __m128i delta32 = _mm_set1_epi32(32768); + + // preliminary saturate negative values to zero + __m128i a1 = _mm_and_si128(a.val, _mm_cmpgt_epi32(a.val, _mm_set1_epi32(0))); + __m128i b1 = _mm_and_si128(b.val, _mm_cmpgt_epi32(b.val, _mm_set1_epi32(0))); + + __m128i r = _mm_packs_epi32(_mm_sub_epi32(a1, delta32), _mm_sub_epi32(b1, delta32)); + return v_uint16x8(_mm_sub_epi16(r, _mm_set1_epi16(-32768))); +#endif +} + +inline void v_pack_u_store(ushort* ptr, const v_int32x4& a) +{ +#if CV_SSE4_1 + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi32(a.val, a.val)); +#else + __m128i delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(a.val, delta32); + __m128i r = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, r); +#endif +} + +template inline +v_uint16x8 v_rshr_pack_u(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + __m128i delta = _mm_set1_epi32(1 << (n - 1)); + return v_uint16x8(_mm_packus_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), + _mm_srai_epi32(_mm_add_epi32(b.val, delta), n))); +#else + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + __m128i b1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(b.val, delta), n), delta32); + __m128i b2 = _mm_sub_epi16(_mm_packs_epi32(b1, b1), _mm_set1_epi16(-32768)); + return v_uint16x8(_mm_unpacklo_epi64(a2, b2)); +#endif +} + +template inline +void v_rshr_pack_u_store(ushort* ptr, const v_int32x4& a) +{ +#if CV_SSE4_1 + __m128i delta = _mm_set1_epi32(1 << (n - 1)); + __m128i a1 = _mm_srai_epi32(_mm_add_epi32(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packus_epi32(a1, a1)); +#else + __m128i delta = _mm_set1_epi32(1 << (n-1)), delta32 = _mm_set1_epi32(32768); + __m128i a1 = _mm_sub_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), delta32); + __m128i a2 = _mm_sub_epi16(_mm_packs_epi32(a1, a1), _mm_set1_epi16(-32768)); + _mm_storel_epi64((__m128i*)ptr, a2); +#endif +} + +inline v_int16x8 v_pack(const v_int32x4& a, const v_int32x4& b) +{ return v_int16x8(_mm_packs_epi32(a.val, b.val)); } + +inline void v_pack_store(short* ptr, const v_int32x4& a) +{ + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi32(a.val, a.val)); +} + +template inline +v_int16x8 v_rshr_pack(const v_int32x4& a, const v_int32x4& b) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)); + return v_int16x8(_mm_packs_epi32(_mm_srai_epi32(_mm_add_epi32(a.val, delta), n), + _mm_srai_epi32(_mm_add_epi32(b.val, delta), n))); +} + +template inline +void v_rshr_pack_store(short* ptr, const v_int32x4& a) +{ + __m128i delta = _mm_set1_epi32(1 << (n-1)); + __m128i a1 = _mm_srai_epi32(_mm_add_epi32(a.val, delta), n); + _mm_storel_epi64((__m128i*)ptr, _mm_packs_epi32(a1, a1)); +} + + +// [a0 0 | b0 0] [a1 0 | b1 0] +inline v_uint32x4 v_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); // b0 b1 0 0 + return v_uint32x4(_mm_unpacklo_epi32(v0, v1)); +} + +inline void v_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + __m128i a1 = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a1); +} + +// [a0 0 | b0 0] [a1 0 | b1 0] +inline v_int32x4 v_pack(const v_int64x2& a, const v_int64x2& b) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); // b0 b1 0 0 + return v_int32x4(_mm_unpacklo_epi32(v0, v1)); +} + +inline void v_pack_store(int* ptr, const v_int64x2& a) +{ + __m128i a1 = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a1); +} + +template inline +v_uint32x4 v_rshr_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + uint64 delta = (uint64)1 << (n-1); + v_uint64x2 delta2(delta, delta); + __m128i a1 = _mm_srli_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i b1 = _mm_srli_epi64(_mm_add_epi64(b.val, delta2.val), n); + __m128i v0 = _mm_unpacklo_epi32(a1, b1); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a1, b1); // b0 b1 0 0 + return v_uint32x4(_mm_unpacklo_epi32(v0, v1)); +} + +template inline +void v_rshr_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + uint64 delta = (uint64)1 << (n-1); + v_uint64x2 delta2(delta, delta); + __m128i a1 = _mm_srli_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i a2 = _mm_shuffle_epi32(a1, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +inline __m128i v_sign_epi64(__m128i a) +{ + return _mm_shuffle_epi32(_mm_srai_epi32(a, 31), _MM_SHUFFLE(3, 3, 1, 1)); // x m0 | x m1 +} + +inline __m128i v_srai_epi64(__m128i a, int imm) +{ + __m128i smask = v_sign_epi64(a); + return _mm_xor_si128(_mm_srli_epi64(_mm_xor_si128(a, smask), imm), smask); +} + +template inline +v_int32x4 v_rshr_pack(const v_int64x2& a, const v_int64x2& b) +{ + int64 delta = (int64)1 << (n-1); + v_int64x2 delta2(delta, delta); + __m128i a1 = v_srai_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i b1 = v_srai_epi64(_mm_add_epi64(b.val, delta2.val), n); + __m128i v0 = _mm_unpacklo_epi32(a1, b1); // a0 a1 0 0 + __m128i v1 = _mm_unpackhi_epi32(a1, b1); // b0 b1 0 0 + return v_int32x4(_mm_unpacklo_epi32(v0, v1)); +} + +template inline +void v_rshr_pack_store(int* ptr, const v_int64x2& a) +{ + int64 delta = (int64)1 << (n-1); + v_int64x2 delta2(delta, delta); + __m128i a1 = v_srai_epi64(_mm_add_epi64(a.val, delta2.val), n); + __m128i a2 = _mm_shuffle_epi32(a1, _MM_SHUFFLE(0, 2, 2, 0)); + _mm_storel_epi64((__m128i*)ptr, a2); +} + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + __m128i ab = _mm_packs_epi16(a.val, b.val); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + __m128i ab = _mm_packs_epi32(a.val, b.val); + __m128i cd = _mm_packs_epi32(c.val, d.val); + return v_uint8x16(_mm_packs_epi16(ab, cd)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + __m128i ab = _mm_packs_epi32(a.val, b.val); + __m128i cd = _mm_packs_epi32(c.val, d.val); + __m128i ef = _mm_packs_epi32(e.val, f.val); + __m128i gh = _mm_packs_epi32(g.val, h.val); + + __m128i abcd = _mm_packs_epi32(ab, cd); + __m128i efgh = _mm_packs_epi32(ef, gh); + return v_uint8x16(_mm_packs_epi16(abcd, efgh)); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + __m128 v0 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(0, 0, 0, 0)), m0.val); + __m128 v1 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(1, 1, 1, 1)), m1.val); + __m128 v2 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(2, 2, 2, 2)), m2.val); + __m128 v3 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(3, 3, 3, 3)), m3.val); + + return v_float32x4(_mm_add_ps(_mm_add_ps(v0, v1), _mm_add_ps(v2, v3))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + __m128 v0 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(0, 0, 0, 0)), m0.val); + __m128 v1 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(1, 1, 1, 1)), m1.val); + __m128 v2 = _mm_mul_ps(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE(2, 2, 2, 2)), m2.val); + + return v_float32x4(_mm_add_ps(_mm_add_ps(v0, v1), _mm_add_ps(v2, a.val))); +} + +#define OPENCV_HAL_IMPL_SSE_BIN_OP(bin_op, _Tpvec, intrin) \ + inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ + { \ + return _Tpvec(intrin(a.val, b.val)); \ + } \ + inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ + { \ + a.val = intrin(a.val, b.val); \ + return a; \ + } + +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint8x16, _mm_adds_epu8) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint8x16, _mm_subs_epu8) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int8x16, _mm_adds_epi8) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int8x16, _mm_subs_epi8) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint16x8, _mm_adds_epu16) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint16x8, _mm_subs_epu16) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int16x8, _mm_adds_epi16) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int16x8, _mm_subs_epi16) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint32x4, _mm_add_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint32x4, _mm_sub_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_uint32x4, _v128_mullo_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int32x4, _mm_add_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int32x4, _mm_sub_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_int32x4, _v128_mullo_epi32) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_float32x4, _mm_add_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_float32x4, _mm_sub_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_float32x4, _mm_mul_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(/, v_float32x4, _mm_div_ps) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_float64x2, _mm_add_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_float64x2, _mm_sub_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(*, v_float64x2, _mm_mul_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(/, v_float64x2, _mm_div_pd) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_uint64x2, _mm_add_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_uint64x2, _mm_sub_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(+, v_int64x2, _mm_add_epi64) +OPENCV_HAL_IMPL_SSE_BIN_OP(-, v_int64x2, _mm_sub_epi64) + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_SSE_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_uint16x8, v_uint32x4) +OPENCV_HAL_IMPL_SSE_MUL_SAT(v_int16x8, v_int32x4) + +// Multiply and expand +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v_uint16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v_int16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + __m128i v0 = _mm_mullo_epi16(a.val, b.val); + __m128i v1 = _mm_mulhi_epi16(a.val, b.val); + c.val = _mm_unpacklo_epi16(v0, v1); + d.val = _mm_unpackhi_epi16(v0, v1); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + __m128i v0 = _mm_mullo_epi16(a.val, b.val); + __m128i v1 = _mm_mulhi_epu16(a.val, b.val); + c.val = _mm_unpacklo_epi16(v0, v1); + d.val = _mm_unpackhi_epi16(v0, v1); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + __m128i c0 = _mm_mul_epu32(a.val, b.val); + __m128i c1 = _mm_mul_epu32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + c.val = _mm_unpacklo_epi64(c0, c1); + d.val = _mm_unpackhi_epi64(c0, c1); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) { return v_int16x8(_mm_mulhi_epi16(a.val, b.val)); } +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) { return v_uint16x8(_mm_mulhi_epu16(a.val, b.val)); } + +//////// Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(_mm_madd_epi16(a.val, b.val)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + __m128i even = _mm_mul_epi32(a.val, b.val); + __m128i odd = _mm_mul_epi32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + return v_int64x2(_mm_add_epi64(even, odd)); +#else + __m128i even_u = _mm_mul_epu32(a.val, b.val); + __m128i odd_u = _mm_mul_epu32(_mm_srli_epi64(a.val, 32), _mm_srli_epi64(b.val, 32)); + // convert unsigned to signed high multiplication (from: Agner Fog(veclib) and H S Warren: Hacker's delight, 2003, p. 132) + __m128i a_sign = _mm_srai_epi32(a.val, 31); + __m128i b_sign = _mm_srai_epi32(b.val, 31); + // |x * sign of x + __m128i axb = _mm_and_si128(a.val, b_sign); + __m128i bxa = _mm_and_si128(b.val, a_sign); + // sum of sign corrections + __m128i ssum = _mm_add_epi32(bxa, axb); + __m128i even_ssum = _mm_slli_epi64(ssum, 32); + __m128i odd_ssum = _mm_and_si128(ssum, _mm_set_epi32(-1, 0, -1, 0)); + // convert to signed and prod + return v_int64x2(_mm_add_epi64(_mm_sub_epi64(even_u, even_ssum), _mm_sub_epi64(odd_u, odd_ssum))); +#endif +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i a0 = _mm_srli_epi16(_mm_slli_si128(a.val, 1), 8); // even + __m128i a1 = _mm_srli_epi16(a.val, 8); // odd + __m128i b0 = _mm_srli_epi16(_mm_slli_si128(b.val, 1), 8); + __m128i b1 = _mm_srli_epi16(b.val, 8); + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_uint32x4(_mm_add_epi32(p0, p1)); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + __m128i a0 = _mm_srai_epi16(_mm_slli_si128(a.val, 1), 8); // even + __m128i a1 = _mm_srai_epi16(a.val, 8); // odd + __m128i b0 = _mm_srai_epi16(_mm_slli_si128(b.val, 1), 8); + __m128i b1 = _mm_srai_epi16(b.val, 8); + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_int32x4(_mm_add_epi32(p0, p1)); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 c, d; + v_mul_expand(a, b, c, d); + + v_uint64x2 c0, c1, d0, d1; + v_expand(c, c0, c1); + v_expand(d, d0, d1); + + c0 += c1; d0 += d1; + return v_uint64x2(_mm_add_epi64( + _mm_unpacklo_epi64(c0.val, d0.val), + _mm_unpackhi_epi64(c0.val, d0.val) + )); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return v_int64x2(_mm_add_epi64( + _mm_unpacklo_epi64(c.val, d.val), + _mm_unpackhi_epi64(c.val, d.val) + )); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_cvt_f64(v_dotprod(a, b)); +#else + v_float64x2 c = v_cvt_f64(a) * v_cvt_f64(b); + v_float64x2 d = v_cvt_f64_high(a) * v_cvt_f64_high(b); + + return v_float64x2(_mm_add_pd( + _mm_unpacklo_pd(c.val, d.val), + _mm_unpackhi_pd(c.val, d.val) + )); +#endif +} +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod_fast(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i a0 = v_expand_low(a).val; + __m128i a1 = v_expand_high(a).val; + __m128i b0 = v_expand_low(b).val; + __m128i b1 = v_expand_high(b).val; + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_uint32x4(_mm_add_epi32(p0, p1)); +} +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + __m128i a0 = _mm_cvtepi8_epi16(a.val); + __m128i a1 = v_expand_high(a).val; + __m128i b0 = _mm_cvtepi8_epi16(b.val); + __m128i b1 = v_expand_high(b).val; + __m128i p0 = _mm_madd_epi16(a0, b0); + __m128i p1 = _mm_madd_epi16(a1, b1); + return v_int32x4(_mm_add_epi32(p0, p1)); +#else + return v_dotprod_expand(a, b); +#endif +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 c, d; + v_mul_expand(a, b, c, d); + + v_uint64x2 c0, c1, d0, d1; + v_expand(c, c0, c1); + v_expand(d, d0, d1); + + c0 += c1; d0 += d1; + return c0 + d0; +} +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return c + d; +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c); +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_cvt_f64_high(a) * v_cvt_f64_high(b)); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_fma(v_cvt_f64(a), v_cvt_f64(b), v_fma(v_cvt_f64_high(a), v_cvt_f64_high(b), c)); } + +#define OPENCV_HAL_IMPL_SSE_LOGIC_OP(_Tpvec, suffix, not_const) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(&, _Tpvec, _mm_and_##suffix) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(|, _Tpvec, _mm_or_##suffix) \ + OPENCV_HAL_IMPL_SSE_BIN_OP(^, _Tpvec, _mm_xor_##suffix) \ + inline _Tpvec operator ~ (const _Tpvec& a) \ + { \ + return _Tpvec(_mm_xor_##suffix(a.val, not_const)); \ + } + +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint8x16, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int8x16, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint16x8, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int16x8, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint32x4, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int32x4, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_uint64x2, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_int64x2, si128, _mm_set1_epi32(-1)) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_float32x4, ps, _mm_castsi128_ps(_mm_set1_epi32(-1))) +OPENCV_HAL_IMPL_SSE_LOGIC_OP(v_float64x2, pd, _mm_castsi128_pd(_mm_set1_epi32(-1))) + +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ return v_float32x4(_mm_sqrt_ps(x.val)); } + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + const __m128 _0_5 = _mm_set1_ps(0.5f), _1_5 = _mm_set1_ps(1.5f); + __m128 t = x.val; + __m128 h = _mm_mul_ps(t, _0_5); + t = _mm_rsqrt_ps(t); + t = _mm_mul_ps(t, _mm_sub_ps(_1_5, _mm_mul_ps(_mm_mul_ps(t, t), h))); + return v_float32x4(t); +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ return v_float64x2(_mm_sqrt_pd(x.val)); } + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ + const __m128d v_1 = _mm_set1_pd(1.); + return v_float64x2(_mm_div_pd(v_1, _mm_sqrt_pd(x.val))); +} + +#define OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(_Tpuvec, _Tpsvec, func, suffix, subWidth) \ +inline _Tpuvec v_abs(const _Tpsvec& x) \ +{ return _Tpuvec(_mm_##func##_ep##suffix(x.val, _mm_sub_ep##subWidth(_mm_setzero_si128(), x.val))); } + +OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(v_uint8x16, v_int8x16, min, u8, i8) +OPENCV_HAL_IMPL_SSE_ABS_INT_FUNC(v_uint16x8, v_int16x8, max, i16, i16) +inline v_uint32x4 v_abs(const v_int32x4& x) +{ + __m128i s = _mm_srli_epi32(x.val, 31); + __m128i f = _mm_srai_epi32(x.val, 31); + return v_uint32x4(_mm_add_epi32(_mm_xor_si128(x.val, f), s)); +} +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(_mm_and_ps(x.val, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); } +inline v_float64x2 v_abs(const v_float64x2& x) +{ + return v_float64x2(_mm_and_pd(x.val, + _mm_castsi128_pd(_mm_srli_epi64(_mm_set1_epi32(-1), 1)))); +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_SSE_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_min, _mm_min_epu8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_max, _mm_max_epu8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_min, _mm_min_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_max, _mm_max_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float32x4, v_min, _mm_min_ps) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float32x4, v_max, _mm_max_ps) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float64x2, v_min, _mm_min_pd) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_float64x2, v_max, _mm_max_pd) + +inline v_int8x16 v_min(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + return v_int8x16(_mm_min_epi8(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi8((char)-128); + return v_int8x16(_mm_xor_si128(delta, _mm_min_epu8(_mm_xor_si128(a.val, delta), + _mm_xor_si128(b.val, delta)))); +#endif +} +inline v_int8x16 v_max(const v_int8x16& a, const v_int8x16& b) +{ +#if CV_SSE4_1 + return v_int8x16(_mm_max_epi8(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi8((char)-128); + return v_int8x16(_mm_xor_si128(delta, _mm_max_epu8(_mm_xor_si128(a.val, delta), + _mm_xor_si128(b.val, delta)))); +#endif +} +inline v_uint16x8 v_min(const v_uint16x8& a, const v_uint16x8& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_min_epu16(a.val, b.val)); +#else + return v_uint16x8(_mm_subs_epu16(a.val, _mm_subs_epu16(a.val, b.val))); +#endif +} +inline v_uint16x8 v_max(const v_uint16x8& a, const v_uint16x8& b) +{ +#if CV_SSE4_1 + return v_uint16x8(_mm_max_epu16(a.val, b.val)); +#else + return v_uint16x8(_mm_adds_epu16(_mm_subs_epu16(a.val, b.val), b.val)); +#endif +} +inline v_uint32x4 v_min(const v_uint32x4& a, const v_uint32x4& b) +{ +#if CV_SSE4_1 + return v_uint32x4(_mm_min_epu32(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi32((int)0x80000000); + __m128i mask = _mm_cmpgt_epi32(_mm_xor_si128(a.val, delta), _mm_xor_si128(b.val, delta)); + return v_uint32x4(v_select_si128(mask, b.val, a.val)); +#endif +} +inline v_uint32x4 v_max(const v_uint32x4& a, const v_uint32x4& b) +{ +#if CV_SSE4_1 + return v_uint32x4(_mm_max_epu32(a.val, b.val)); +#else + __m128i delta = _mm_set1_epi32((int)0x80000000); + __m128i mask = _mm_cmpgt_epi32(_mm_xor_si128(a.val, delta), _mm_xor_si128(b.val, delta)); + return v_uint32x4(v_select_si128(mask, a.val, b.val)); +#endif +} +inline v_int32x4 v_min(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_int32x4(_mm_min_epi32(a.val, b.val)); +#else + return v_int32x4(v_select_si128(_mm_cmpgt_epi32(a.val, b.val), b.val, a.val)); +#endif +} +inline v_int32x4 v_max(const v_int32x4& a, const v_int32x4& b) +{ +#if CV_SSE4_1 + return v_int32x4(_mm_max_epi32(a.val, b.val)); +#else + return v_int32x4(v_select_si128(_mm_cmpgt_epi32(a.val, b.val), a.val, b.val)); +#endif +} + +#define OPENCV_HAL_IMPL_SSE_INT_CMP_OP(_Tpuvec, _Tpsvec, suffix, sbit) \ +inline _Tpuvec operator == (const _Tpuvec& a, const _Tpuvec& b) \ +{ return _Tpuvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpuvec operator != (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpuvec(_mm_xor_si128(_mm_cmpeq_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpsvec operator == (const _Tpsvec& a, const _Tpsvec& b) \ +{ return _Tpsvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpsvec operator != (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpeq_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpuvec operator < (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + return _Tpuvec(_mm_cmpgt_##suffix(_mm_xor_si128(b.val, smask), _mm_xor_si128(a.val, smask))); \ +} \ +inline _Tpuvec operator > (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + return _Tpuvec(_mm_cmpgt_##suffix(_mm_xor_si128(a.val, smask), _mm_xor_si128(b.val, smask))); \ +} \ +inline _Tpuvec operator <= (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + __m128i not_mask = _mm_set1_epi32(-1); \ + __m128i res = _mm_cmpgt_##suffix(_mm_xor_si128(a.val, smask), _mm_xor_si128(b.val, smask)); \ + return _Tpuvec(_mm_xor_si128(res, not_mask)); \ +} \ +inline _Tpuvec operator >= (const _Tpuvec& a, const _Tpuvec& b) \ +{ \ + __m128i smask = _mm_set1_##suffix(sbit); \ + __m128i not_mask = _mm_set1_epi32(-1); \ + __m128i res = _mm_cmpgt_##suffix(_mm_xor_si128(b.val, smask), _mm_xor_si128(a.val, smask)); \ + return _Tpuvec(_mm_xor_si128(res, not_mask)); \ +} \ +inline _Tpsvec operator < (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + return _Tpsvec(_mm_cmpgt_##suffix(b.val, a.val)); \ +} \ +inline _Tpsvec operator > (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + return _Tpsvec(_mm_cmpgt_##suffix(a.val, b.val)); \ +} \ +inline _Tpsvec operator <= (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpgt_##suffix(a.val, b.val), not_mask)); \ +} \ +inline _Tpsvec operator >= (const _Tpsvec& a, const _Tpsvec& b) \ +{ \ + __m128i not_mask = _mm_set1_epi32(-1); \ + return _Tpsvec(_mm_xor_si128(_mm_cmpgt_##suffix(b.val, a.val), not_mask)); \ +} + +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint8x16, v_int8x16, epi8, (char)-128) +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint16x8, v_int16x8, epi16, (short)-32768) +OPENCV_HAL_IMPL_SSE_INT_CMP_OP(v_uint32x4, v_int32x4, epi32, (int)0x80000000) + +#define OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(_Tpvec, suffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpeq_##suffix(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpneq_##suffix(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmplt_##suffix(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpgt_##suffix(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmple_##suffix(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpge_##suffix(a.val, b.val)); } + +OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(v_float32x4, ps) +OPENCV_HAL_IMPL_SSE_FLT_CMP_OP(v_float64x2, pd) + +#if CV_SSE4_1 +#define OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(_mm_cmpeq_epi64(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return ~(a == b); } +#else +#define OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ __m128i cmp = _mm_cmpeq_epi32(a.val, b.val); \ + return _Tpvec(_mm_and_si128(cmp, _mm_shuffle_epi32(cmp, _MM_SHUFFLE(2, 3, 0, 1)))); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return ~(a == b); } +#endif + +OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(v_uint64x2) +OPENCV_HAL_IMPL_SSE_64BIT_CMP_OP(v_int64x2) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(_mm_cmpord_ps(a.val, a.val)); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(_mm_cmpord_pd(a.val, a.val)); } + +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_add_wrap, _mm_add_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int8x16, v_add_wrap, _mm_add_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_add_wrap, _mm_add_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_add_wrap, _mm_add_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint8x16, v_sub_wrap, _mm_sub_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int8x16, v_sub_wrap, _mm_sub_epi8) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_sub_wrap, _mm_sub_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_sub_wrap, _mm_sub_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_uint16x8, v_mul_wrap, _mm_mullo_epi16) +OPENCV_HAL_IMPL_SSE_BIN_FUNC(v_int16x8, v_mul_wrap, _mm_mullo_epi16) + +inline v_uint8x16 v_mul_wrap(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i ad = _mm_srai_epi16(a.val, 8); + __m128i bd = _mm_srai_epi16(b.val, 8); + __m128i p0 = _mm_mullo_epi16(a.val, b.val); // even + __m128i p1 = _mm_slli_epi16(_mm_mullo_epi16(ad, bd), 8); // odd + const __m128i b01 = _mm_set1_epi32(0xFF00FF00); + return v_uint8x16(_v128_blendv_epi8(p0, p1, b01)); +} +inline v_int8x16 v_mul_wrap(const v_int8x16& a, const v_int8x16& b) +{ + return v_reinterpret_as_s8(v_mul_wrap(v_reinterpret_as_u8(a), v_reinterpret_as_u8(b))); +} + +/** Absolute difference **/ + +inline v_uint8x16 v_absdiff(const v_uint8x16& a, const v_uint8x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x8 v_absdiff(const v_uint16x8& a, const v_uint16x8& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x4 v_absdiff(const v_uint32x4& a, const v_uint32x4& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = v_sub_wrap(a, b); + v_int8x16 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ + return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); +} +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ + v_int32x4 d = a - b; + v_int32x4 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = a - b; + v_int8x16 m = a < b; + return (d ^ m) - m; + } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_max(a, b) - v_min(a, b); } + + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return a * b + c; +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ +#if CV_FMA3 + return v_float32x4(_mm_fmadd_ps(a.val, b.val, c.val)); +#else + return v_float32x4(_mm_add_ps(_mm_mul_ps(a.val, b.val), c.val)); +#endif +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ +#if CV_FMA3 + return v_float64x2(_mm_fmadd_pd(a.val, b.val, c.val)); +#else + return v_float64x2(_mm_add_pd(_mm_mul_pd(a.val, b.val), c.val)); +#endif +} + +#define OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(_Tpvec, _Tp, _Tpreg, suffix, absmask_vec) \ +inline _Tpvec v_absdiff(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpreg absmask = _mm_castsi128_##suffix(absmask_vec); \ + return _Tpvec(_mm_and_##suffix(_mm_sub_##suffix(a.val, b.val), absmask)); \ +} \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpvec res = v_fma(a, a, b*b); \ + return _Tpvec(_mm_sqrt_##suffix(res.val)); \ +} \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return v_fma(a, a, b*b); \ +} \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ \ + return v_fma(a, b, c); \ +} + +OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(v_float32x4, float, __m128, ps, _mm_set1_epi32((int)0x7fffffff)) +OPENCV_HAL_IMPL_SSE_MISC_FLT_OP(v_float64x2, double, __m128d, pd, _mm_srli_epi64(_mm_set1_epi32(-1), 1)) + +#define OPENCV_HAL_IMPL_SSE_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, srai) \ +inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(_mm_srli_##suffix(a.val, imm)); \ +} \ +inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(srai(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shl(const _Tpuvec& a) \ +{ \ + return _Tpuvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shl(const _Tpsvec& a) \ +{ \ + return _Tpsvec(_mm_slli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shr(const _Tpuvec& a) \ +{ \ + return _Tpuvec(_mm_srli_##suffix(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shr(const _Tpsvec& a) \ +{ \ + return _Tpsvec(srai(a.val, imm)); \ +} + +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint16x8, v_int16x8, epi16, _mm_srai_epi16) +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint32x4, v_int32x4, epi32, _mm_srai_epi32) +OPENCV_HAL_IMPL_SSE_SHIFT_OP(v_uint64x2, v_int64x2, epi64, v_srai_epi64) + +namespace hal_sse_internal +{ + template 16)), + bool is_first = (imm == 0), + bool is_half = (imm == 8), + bool is_second = (imm == 16), + bool is_other = (((imm > 0) && (imm < 8)) || ((imm > 8) && (imm < 16)))> + class v_sse_palignr_u8_class; + + template + class v_sse_palignr_u8_class; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i& a, const __m128i&) const + { + return a; + } + }; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + return _mm_unpacklo_epi64(_mm_unpackhi_epi64(a, a), b); + } + }; + + template + class v_sse_palignr_u8_class + { + public: + inline __m128i operator()(const __m128i&, const __m128i& b) const + { + return b; + } + }; + + template + class v_sse_palignr_u8_class + { +#if CV_SSSE3 + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + return _mm_alignr_epi8(b, a, imm); + } +#else + public: + inline __m128i operator()(const __m128i& a, const __m128i& b) const + { + enum { imm2 = (sizeof(__m128i) - imm) }; + return _mm_or_si128(_mm_srli_si128(a, imm), _mm_slli_si128(b, imm2)); + } +#endif + }; + + template + inline __m128i v_sse_palignr_u8(const __m128i& a, const __m128i& b) + { + CV_StaticAssert((imm >= 0) && (imm <= 16), "Invalid imm for v_sse_palignr_u8."); + return v_sse_palignr_u8_class()(a, b); + } +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + _mm_srli_si128( + v_sse_reinterpret_as<__m128i>(a.val), imm2))); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + _mm_slli_si128( + v_sse_reinterpret_as<__m128i>(a.val), imm2))); +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_sse_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + v_sse_palignr_u8( + v_sse_reinterpret_as<__m128i>(a.val), + v_sse_reinterpret_as<__m128i>(b.val)))); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_sse_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_sse_reinterpret_as( + v_sse_palignr_u8( + v_sse_reinterpret_as<__m128i>(b.val), + v_sse_reinterpret_as<__m128i>(a.val)))); +} + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(_Tpvec, _Tp) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadu_si128((const __m128i*)ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(_mm_load_si128((const __m128i*)ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadl_epi64((const __m128i*)ptr)); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + return _Tpvec(_mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i*)ptr0), \ + _mm_loadl_epi64((const __m128i*)ptr1))); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storeu_si128((__m128i*)ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ _mm_store_si128((__m128i*)ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ _mm_stream_si128((__m128i*)ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm_storeu_si128((__m128i*)ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm_stream_si128((__m128i*)ptr, a.val); \ + else \ + _mm_store_si128((__m128i*)ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, a.val); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, _mm_unpackhi_epi64(a.val, a.val)); } + +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint8x16, uchar) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int8x16, schar) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint16x8, ushort) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int16x8, short) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int32x4, int) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_uint64x2, uint64) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INT_OP(v_int64x2, int64) + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(_Tpvec, _Tp, suffix) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(_mm_loadu_##suffix(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(_mm_load_##suffix(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(_mm_castsi128_##suffix(_mm_loadl_epi64((const __m128i*)ptr))); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + return _Tpvec(_mm_castsi128_##suffix( \ + _mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i*)ptr0), \ + _mm_loadl_epi64((const __m128i*)ptr1)))); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storeu_##suffix(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ _mm_store_##suffix(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ _mm_stream_##suffix(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ \ + if( mode == hal::STORE_UNALIGNED ) \ + _mm_storeu_##suffix(ptr, a.val); \ + else if( mode == hal::STORE_ALIGNED_NOCACHE ) \ + _mm_stream_##suffix(ptr, a.val); \ + else \ + _mm_store_##suffix(ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ _mm_storel_epi64((__m128i*)ptr, _mm_cast##suffix##_si128(a.val)); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + __m128i a1 = _mm_cast##suffix##_si128(a.val); \ + _mm_storel_epi64((__m128i*)ptr, _mm_unpackhi_epi64(a1, a1)); \ +} + +OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(v_float32x4, float, ps) +OPENCV_HAL_IMPL_SSE_LOADSTORE_FLT_OP(v_float64x2, double, pd) + +inline unsigned v_reduce_sum(const v_uint8x16& a) +{ + __m128i half = _mm_sad_epu8(a.val, _mm_setzero_si128()); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + __m128i half = _mm_set1_epi8((schar)-128); + half = _mm_sad_epu8(_mm_xor_si128(a.val, half), _mm_setzero_si128()); + return _mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))) - 2048; +} +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(func) \ +inline schar v_reduce_##func(const v_int8x16& a) \ +{ \ + __m128i val = a.val; \ + __m128i smask = _mm_set1_epi8((schar)-128); \ + val = _mm_xor_si128(val, smask); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,2)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,1)); \ + return (schar)_mm_cvtsi128_si32(val) ^ (schar)-128; \ +} \ +inline uchar v_reduce_##func(const v_uint8x16& a) \ +{ \ + __m128i val = a.val; \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,2)); \ + val = _mm_##func##_epu8(val, _mm_srli_si128(val,1)); \ + return (uchar)_mm_cvtsi128_si32(val); \ +} +OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_16(min) + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(_Tpvec, scalartype, func, suffix, sbit) \ +inline scalartype v_reduce_##func(const v_##_Tpvec& a) \ +{ \ + __m128i val = a.val; \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,2)); \ + return (scalartype)_mm_cvtsi128_si32(val); \ +} \ +inline unsigned scalartype v_reduce_##func(const v_u##_Tpvec& a) \ +{ \ + __m128i val = a.val; \ + __m128i smask = _mm_set1_epi16(sbit); \ + val = _mm_xor_si128(val, smask); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,8)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,4)); \ + val = _mm_##func##_##suffix(val, _mm_srli_si128(val,2)); \ + return (unsigned scalartype)(_mm_cvtsi128_si32(val) ^ sbit); \ +} +OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(int16x8, short, max, epi16, (short)-32768) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_8(int16x8, short, min, epi16, (short)-32768) + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(_Tpvec, scalartype, regtype, suffix, cast_from, cast_to, extract) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + regtype val = a.val; \ + val = _mm_add_##suffix(val, cast_to(_mm_srli_si128(cast_from(val), 8))); \ + val = _mm_add_##suffix(val, cast_to(_mm_srli_si128(cast_from(val), 4))); \ + return (scalartype)_mm_cvt##extract(val); \ +} + +#define OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(_Tpvec, scalartype, func, scalar_func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + scalartype CV_DECL_ALIGNED(16) buf[4]; \ + v_store_aligned(buf, a); \ + scalartype s0 = scalar_func(buf[0], buf[1]); \ + scalartype s1 = scalar_func(buf[2], buf[3]); \ + return scalar_func(s0, s1); \ +} + +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_uint32x4, unsigned, __m128i, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP, si128_si32) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_int32x4, int, __m128i, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP, si128_si32) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4_SUM(v_float32x4, float, __m128, ps, _mm_castps_si128, _mm_castsi128_ps, ss_f32) + +inline int v_reduce_sum(const v_int16x8& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } +inline unsigned v_reduce_sum(const v_uint16x8& a) +{ return v_reduce_sum(v_expand_low(a) + v_expand_high(a)); } + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ + uint64 CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} +inline int64 v_reduce_sum(const v_int64x2& a) +{ + int64 CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} +inline double v_reduce_sum(const v_float64x2& a) +{ + double CV_DECL_ALIGNED(32) idx[2]; + v_store_aligned(idx, a); + return idx[0] + idx[1]; +} + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ +#if CV_SSE3 + __m128 ab = _mm_hadd_ps(a.val, b.val); + __m128 cd = _mm_hadd_ps(c.val, d.val); + return v_float32x4(_mm_hadd_ps(ab, cd)); +#else + __m128 ac = _mm_add_ps(_mm_unpacklo_ps(a.val, c.val), _mm_unpackhi_ps(a.val, c.val)); + __m128 bd = _mm_add_ps(_mm_unpacklo_ps(b.val, d.val), _mm_unpackhi_ps(b.val, d.val)); + return v_float32x4(_mm_add_ps(_mm_unpacklo_ps(ac, bd), _mm_unpackhi_ps(ac, bd))); +#endif +} + +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_SSE_REDUCE_OP_4(v_float32x4, float, min, std::min) + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + __m128i half = _mm_sad_epu8(a.val, b.val); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + __m128i half = _mm_set1_epi8(0x7f); + half = _mm_sad_epu8(_mm_add_epi8(a.val, half), _mm_add_epi8(b.val, half)); + return (unsigned)_mm_cvtsi128_si32(_mm_add_epi32(half, _mm_unpackhi_epi64(half, half))); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ + __m128i m1 = _mm_set1_epi32(0x55555555); + __m128i m2 = _mm_set1_epi32(0x33333333); + __m128i m4 = _mm_set1_epi32(0x0f0f0f0f); + __m128i p = a.val; + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 1), m1), _mm_and_si128(p, m1)); + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 2), m2), _mm_and_si128(p, m2)); + p = _mm_add_epi32(_mm_and_si128(_mm_srli_epi32(p, 4), m4), _mm_and_si128(p, m4)); + return v_uint8x16(p); +} +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v_setall_u16(0x00ff); +} +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v_setall_u32(0x000000ff); +} +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ + return v_uint64x2(_mm_sad_epu8(v_popcount(v_reinterpret_as_u8(a)).val, _mm_setzero_si128())); +} +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +#define OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(_Tpvec, suffix, cast_op, allmask) \ +inline int v_signmask(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)); } \ +inline bool v_check_all(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)) == allmask; } \ +inline bool v_check_any(const _Tpvec& a) { return _mm_movemask_##suffix(cast_op(a.val)) != 0; } +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint8x16, epi8, OPENCV_HAL_NOP, 65535) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int8x16, epi8, OPENCV_HAL_NOP, 65535) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint32x4, ps, _mm_castsi128_ps, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int32x4, ps, _mm_castsi128_ps, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_uint64x2, pd, _mm_castsi128_pd, 3) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int64x2, pd, _mm_castsi128_pd, 3) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float32x4, ps, OPENCV_HAL_NOP, 15) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float64x2, pd, OPENCV_HAL_NOP, 3) + +#define OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(_Tpvec) \ +inline int v_signmask(const _Tpvec& a) { return _mm_movemask_epi8(_mm_packs_epi16(a.val, a.val)) & 255; } \ +inline bool v_check_all(const _Tpvec& a) { return (_mm_movemask_epi8(a.val) & 0xaaaa) == 0xaaaa; } \ +inline bool v_check_any(const _Tpvec& a) { return (_mm_movemask_epi8(a.val) & 0xaaaa) != 0; } +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(v_uint16x8) +OPENCV_HAL_IMPL_SSE_CHECK_SIGNS_SHORT(v_int16x8) + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +#if CV_SSE4_1 +#define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, cast_ret, cast, suffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(cast_ret(_mm_blendv_##suffix(cast(b.val), cast(a.val), cast(mask.val)))); \ +} + +OPENCV_HAL_IMPL_SSE_SELECT(v_uint8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +// OPENCV_HAL_IMPL_SSE_SELECT(v_uint64x2, TBD, TBD, pd) +// OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, TBD, TBD, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, OPENCV_HAL_NOP, OPENCV_HAL_NOP, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, OPENCV_HAL_NOP, OPENCV_HAL_NOP, pd) + +#else // CV_SSE4_1 + +#define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, suffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(_mm_xor_##suffix(b.val, _mm_and_##suffix(_mm_xor_##suffix(b.val, a.val), mask.val))); \ +} + +OPENCV_HAL_IMPL_SSE_SELECT(v_uint8x16, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int8x16, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint16x8, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int16x8, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint32x4, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, si128) +// OPENCV_HAL_IMPL_SSE_SELECT(v_uint64x2, si128) +// OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, si128) +OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, pd) +#endif + +/* Expand */ +#define OPENCV_HAL_IMPL_SSE_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ + inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ + { \ + b0.val = intrin(a.val); \ + b1.val = __CV_CAT(intrin, _high)(a.val); \ + } \ + inline _Tpwvec v_expand_low(const _Tpvec& a) \ + { return _Tpwvec(intrin(a.val)); } \ + inline _Tpwvec v_expand_high(const _Tpvec& a) \ + { return _Tpwvec(__CV_CAT(intrin, _high)(a.val)); } \ + inline _Tpwvec v_load_expand(const _Tp* ptr) \ + { \ + __m128i a = _mm_loadl_epi64((const __m128i*)ptr); \ + return _Tpwvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint8x16, v_uint16x8, uchar, _v128_cvtepu8_epi16) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int8x16, v_int16x8, schar, _v128_cvtepi8_epi16) +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint16x8, v_uint32x4, ushort, _v128_cvtepu16_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int16x8, v_int32x4, short, _v128_cvtepi16_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND(v_uint32x4, v_uint64x2, unsigned, _v128_cvtepu32_epi64) +OPENCV_HAL_IMPL_SSE_EXPAND(v_int32x4, v_int64x2, int, _v128_cvtepi32_epi64) + +#define OPENCV_HAL_IMPL_SSE_EXPAND_Q(_Tpvec, _Tp, intrin) \ + inline _Tpvec v_load_expand_q(const _Tp* ptr) \ + { \ + __m128i a = _mm_cvtsi32_si128(*(const int*)ptr); \ + return _Tpvec(intrin(a)); \ + } + +OPENCV_HAL_IMPL_SSE_EXPAND_Q(v_uint32x4, uchar, _v128_cvtepu8_epi32) +OPENCV_HAL_IMPL_SSE_EXPAND_Q(v_int32x4, schar, _v128_cvtepi8_epi32) + +#define OPENCV_HAL_IMPL_SSE_UNPACKS(_Tpvec, suffix, cast_from, cast_to) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = _mm_unpacklo_##suffix(a0.val, a1.val); \ + b1.val = _mm_unpackhi_##suffix(a0.val, a1.val); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + return _Tpvec(cast_to(_mm_unpacklo_epi64(a1, b1))); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + return _Tpvec(cast_to(_mm_unpackhi_epi64(a1, b1))); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + __m128i a1 = cast_from(a.val), b1 = cast_from(b.val); \ + c.val = cast_to(_mm_unpacklo_epi64(a1, b1)); \ + d.val = cast_to(_mm_unpackhi_epi64(a1, b1)); \ +} + +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint8x16, epi8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int8x16, epi8, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint16x8, epi16, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int16x8, epi16, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_uint32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_int32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_float32x4, ps, _mm_castps_si128, _mm_castsi128_ps) +OPENCV_HAL_IMPL_SSE_UNPACKS(v_float64x2, pd, _mm_castpd_si128, _mm_castsi128_pd) + +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ +#if CV_SSSE3 + static const __m128i perm = _mm_setr_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + return v_uint8x16(_mm_shuffle_epi8(a.val, perm)); +#else + uchar CV_DECL_ALIGNED(32) d[16]; + v_store_aligned(d, a); + return v_uint8x16(d[15], d[14], d[13], d[12], d[11], d[10], d[9], d[8], d[7], d[6], d[5], d[4], d[3], d[2], d[1], d[0]); +#endif +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ +#if CV_SSSE3 + static const __m128i perm = _mm_setr_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + return v_uint16x8(_mm_shuffle_epi8(a.val, perm)); +#else + __m128i r = _mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 1, 2, 3)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(2, 3, 0, 1)); + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(2, 3, 0, 1)); + return v_uint16x8(r); +#endif +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + return v_uint32x4(_mm_shuffle_epi32(a.val, _MM_SHUFFLE(0, 1, 2, 3))); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + return v_uint64x2(_mm_shuffle_epi32(a.val, _MM_SHUFFLE(1, 0, 3, 2))); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ + return v_rotate_right(a, b); +} + +inline v_int32x4 v_round(const v_float32x4& a) +{ return v_int32x4(_mm_cvtps_epi32(a.val)); } + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + __m128i a1 = _mm_cvtps_epi32(a.val); + __m128i mask = _mm_castps_si128(_mm_cmpgt_ps(_mm_cvtepi32_ps(a1), a.val)); + return v_int32x4(_mm_add_epi32(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + __m128i a1 = _mm_cvtps_epi32(a.val); + __m128i mask = _mm_castps_si128(_mm_cmpgt_ps(a.val, _mm_cvtepi32_ps(a1))); + return v_int32x4(_mm_sub_epi32(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(_mm_cvttps_epi32(a.val)); } + +inline v_int32x4 v_round(const v_float64x2& a) +{ return v_int32x4(_mm_cvtpd_epi32(a.val)); } + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + __m128i ai = _mm_cvtpd_epi32(a.val), bi = _mm_cvtpd_epi32(b.val); + return v_int32x4(_mm_unpacklo_epi64(ai, bi)); +} + +inline v_int32x4 v_floor(const v_float64x2& a) +{ + __m128i a1 = _mm_cvtpd_epi32(a.val); + __m128i mask = _mm_castpd_si128(_mm_cmpgt_pd(_mm_cvtepi32_pd(a1), a.val)); + mask = _mm_srli_si128(_mm_slli_si128(mask, 4), 8); // m0 m0 m1 m1 => m0 m1 0 0 + return v_int32x4(_mm_add_epi32(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ + __m128i a1 = _mm_cvtpd_epi32(a.val); + __m128i mask = _mm_castpd_si128(_mm_cmpgt_pd(a.val, _mm_cvtepi32_pd(a1))); + mask = _mm_srli_si128(_mm_slli_si128(mask, 4), 8); // m0 m0 m1 m1 => m0 m1 0 0 + return v_int32x4(_mm_sub_epi32(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ return v_int32x4(_mm_cvttpd_epi32(a.val)); } + +#define OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(_Tpvec, suffix, cast_from, cast_to) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + __m128i t0 = cast_from(_mm_unpacklo_##suffix(a0.val, a1.val)); \ + __m128i t1 = cast_from(_mm_unpacklo_##suffix(a2.val, a3.val)); \ + __m128i t2 = cast_from(_mm_unpackhi_##suffix(a0.val, a1.val)); \ + __m128i t3 = cast_from(_mm_unpackhi_##suffix(a2.val, a3.val)); \ +\ + b0.val = cast_to(_mm_unpacklo_epi64(t0, t1)); \ + b1.val = cast_to(_mm_unpackhi_epi64(t0, t1)); \ + b2.val = cast_to(_mm_unpacklo_epi64(t2, t3)); \ + b3.val = cast_to(_mm_unpackhi_epi64(t2, t3)); \ +} + +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_uint32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_int32x4, epi32, OPENCV_HAL_NOP, OPENCV_HAL_NOP) +OPENCV_HAL_IMPL_SSE_TRANSPOSE4x4(v_float32x4, ps, _mm_castps_si128, _mm_castsi128_ps) + +// load deinterleave +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b) +{ + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + + __m128i t10 = _mm_unpacklo_epi8(t00, t01); + __m128i t11 = _mm_unpackhi_epi8(t00, t01); + + __m128i t20 = _mm_unpacklo_epi8(t10, t11); + __m128i t21 = _mm_unpackhi_epi8(t10, t11); + + __m128i t30 = _mm_unpacklo_epi8(t20, t21); + __m128i t31 = _mm_unpackhi_epi8(t20, t21); + + a.val = _mm_unpacklo_epi8(t30, t31); + b.val = _mm_unpackhi_epi8(t30, t31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c) +{ +#if CV_SSE4_1 + const __m128i m0 = _mm_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + const __m128i m1 = _mm_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + __m128i s0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i s1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i s2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + __m128i a0 = _mm_blendv_epi8(_mm_blendv_epi8(s0, s1, m0), s2, m1); + __m128i b0 = _mm_blendv_epi8(_mm_blendv_epi8(s1, s2, m0), s0, m1); + __m128i c0 = _mm_blendv_epi8(_mm_blendv_epi8(s2, s0, m0), s1, m1); + const __m128i sh_b = _mm_setr_epi8(0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13); + const __m128i sh_g = _mm_setr_epi8(1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14); + const __m128i sh_r = _mm_setr_epi8(2, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15); + a0 = _mm_shuffle_epi8(a0, sh_b); + b0 = _mm_shuffle_epi8(b0, sh_g); + c0 = _mm_shuffle_epi8(c0, sh_r); + a.val = a0; + b.val = b0; + c.val = c0; +#elif CV_SSSE3 + const __m128i m0 = _mm_setr_epi8(0, 3, 6, 9, 12, 15, 1, 4, 7, 10, 13, 2, 5, 8, 11, 14); + const __m128i m1 = _mm_alignr_epi8(m0, m0, 11); + const __m128i m2 = _mm_alignr_epi8(m0, m0, 6); + + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + + __m128i s0 = _mm_shuffle_epi8(t0, m0); + __m128i s1 = _mm_shuffle_epi8(t1, m1); + __m128i s2 = _mm_shuffle_epi8(t2, m2); + + t0 = _mm_alignr_epi8(s1, _mm_slli_si128(s0, 10), 5); + a.val = _mm_alignr_epi8(s2, t0, 5); + + t1 = _mm_alignr_epi8(_mm_srli_si128(s1, 5), _mm_slli_si128(s0, 5), 6); + b.val = _mm_alignr_epi8(_mm_srli_si128(s2, 5), t1, 5); + + t2 = _mm_alignr_epi8(_mm_srli_si128(s2, 10), s1, 11); + c.val = _mm_alignr_epi8(t2, s0, 11); +#else + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + + __m128i t10 = _mm_unpacklo_epi8(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi8(t01, _mm_unpackhi_epi64(t02, t02)); + + __m128i t20 = _mm_unpacklo_epi8(t10, _mm_unpackhi_epi64(t11, t11)); + __m128i t21 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t10, t10), t12); + __m128i t22 = _mm_unpacklo_epi8(t11, _mm_unpackhi_epi64(t12, t12)); + + __m128i t30 = _mm_unpacklo_epi8(t20, _mm_unpackhi_epi64(t21, t21)); + __m128i t31 = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t20, t20), t22); + __m128i t32 = _mm_unpacklo_epi8(t21, _mm_unpackhi_epi64(t22, t22)); + + a.val = _mm_unpacklo_epi8(t30, _mm_unpackhi_epi64(t31, t31)); + b.val = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t30, t30), t32); + c.val = _mm_unpacklo_epi8(t31, _mm_unpackhi_epi64(t32, t32)); +#endif +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c, v_uint8x16& d) +{ + __m128i u0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 c0 d0 a1 b1 c1 d1 ... + __m128i u1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); // a4 b4 c4 d4 ... + __m128i u2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); // a8 b8 c8 d8 ... + __m128i u3 = _mm_loadu_si128((const __m128i*)(ptr + 48)); // a12 b12 c12 d12 ... + + __m128i v0 = _mm_unpacklo_epi8(u0, u2); // a0 a8 b0 b8 ... + __m128i v1 = _mm_unpackhi_epi8(u0, u2); // a2 a10 b2 b10 ... + __m128i v2 = _mm_unpacklo_epi8(u1, u3); // a4 a12 b4 b12 ... + __m128i v3 = _mm_unpackhi_epi8(u1, u3); // a6 a14 b6 b14 ... + + u0 = _mm_unpacklo_epi8(v0, v2); // a0 a4 a8 a12 ... + u1 = _mm_unpacklo_epi8(v1, v3); // a2 a6 a10 a14 ... + u2 = _mm_unpackhi_epi8(v0, v2); // a1 a5 a9 a13 ... + u3 = _mm_unpackhi_epi8(v1, v3); // a3 a7 a11 a15 ... + + v0 = _mm_unpacklo_epi8(u0, u1); // a0 a2 a4 a6 ... + v1 = _mm_unpacklo_epi8(u2, u3); // a1 a3 a5 a7 ... + v2 = _mm_unpackhi_epi8(u0, u1); // c0 c2 c4 c6 ... + v3 = _mm_unpackhi_epi8(u2, u3); // c1 c3 c5 c7 ... + + a.val = _mm_unpacklo_epi8(v0, v1); + b.val = _mm_unpackhi_epi8(v0, v1); + c.val = _mm_unpacklo_epi8(v2, v3); + d.val = _mm_unpackhi_epi8(v2, v3); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b) +{ + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); // a0 b0 a1 b1 a2 b2 a3 b3 + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 8)); // a4 b4 a5 b5 a6 b6 a7 b7 + + __m128i v2 = _mm_unpacklo_epi16(v0, v1); // a0 a4 b0 b4 a1 a5 b1 b5 + __m128i v3 = _mm_unpackhi_epi16(v0, v1); // a2 a6 b2 b6 a3 a7 b3 b7 + __m128i v4 = _mm_unpacklo_epi16(v2, v3); // a0 a2 a4 a6 b0 b2 b4 b6 + __m128i v5 = _mm_unpackhi_epi16(v2, v3); // a1 a3 a5 a7 b1 b3 b5 b7 + + a.val = _mm_unpacklo_epi16(v4, v5); // a0 a1 a2 a3 a4 a5 a6 a7 + b.val = _mm_unpackhi_epi16(v4, v5); // b0 b1 ab b3 b4 b5 b6 b7 +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c) +{ +#if CV_SSE4_1 + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 8)); + __m128i v2 = _mm_loadu_si128((__m128i*)(ptr + 16)); + __m128i a0 = _mm_blend_epi16(_mm_blend_epi16(v0, v1, 0x92), v2, 0x24); + __m128i b0 = _mm_blend_epi16(_mm_blend_epi16(v2, v0, 0x92), v1, 0x24); + __m128i c0 = _mm_blend_epi16(_mm_blend_epi16(v1, v2, 0x92), v0, 0x24); + + const __m128i sh_a = _mm_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m128i sh_b = _mm_setr_epi8(2, 3, 8, 9, 14, 15, 4, 5, 10, 11, 0, 1, 6, 7, 12, 13); + const __m128i sh_c = _mm_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + a0 = _mm_shuffle_epi8(a0, sh_a); + b0 = _mm_shuffle_epi8(b0, sh_b); + c0 = _mm_shuffle_epi8(c0, sh_c); + + a.val = a0; + b.val = b0; + c.val = c0; +#else + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 8)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + + __m128i t10 = _mm_unpacklo_epi16(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi16(t01, _mm_unpackhi_epi64(t02, t02)); + + __m128i t20 = _mm_unpacklo_epi16(t10, _mm_unpackhi_epi64(t11, t11)); + __m128i t21 = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t10, t10), t12); + __m128i t22 = _mm_unpacklo_epi16(t11, _mm_unpackhi_epi64(t12, t12)); + + a.val = _mm_unpacklo_epi16(t20, _mm_unpackhi_epi64(t21, t21)); + b.val = _mm_unpacklo_epi16(_mm_unpackhi_epi64(t20, t20), t22); + c.val = _mm_unpacklo_epi16(t21, _mm_unpackhi_epi64(t22, t22)); +#endif +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c, v_uint16x8& d) +{ + __m128i u0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 c0 d0 a1 b1 c1 d1 + __m128i u1 = _mm_loadu_si128((const __m128i*)(ptr + 8)); // a2 b2 c2 d2 ... + __m128i u2 = _mm_loadu_si128((const __m128i*)(ptr + 16)); // a4 b4 c4 d4 ... + __m128i u3 = _mm_loadu_si128((const __m128i*)(ptr + 24)); // a6 b6 c6 d6 ... + + __m128i v0 = _mm_unpacklo_epi16(u0, u2); // a0 a4 b0 b4 ... + __m128i v1 = _mm_unpackhi_epi16(u0, u2); // a1 a5 b1 b5 ... + __m128i v2 = _mm_unpacklo_epi16(u1, u3); // a2 a6 b2 b6 ... + __m128i v3 = _mm_unpackhi_epi16(u1, u3); // a3 a7 b3 b7 ... + + u0 = _mm_unpacklo_epi16(v0, v2); // a0 a2 a4 a6 ... + u1 = _mm_unpacklo_epi16(v1, v3); // a1 a3 a5 a7 ... + u2 = _mm_unpackhi_epi16(v0, v2); // c0 c2 c4 c6 ... + u3 = _mm_unpackhi_epi16(v1, v3); // c1 c3 c5 c7 ... + + a.val = _mm_unpacklo_epi16(u0, u1); + b.val = _mm_unpackhi_epi16(u0, u1); + c.val = _mm_unpacklo_epi16(u2, u3); + d.val = _mm_unpackhi_epi16(u2, u3); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b) +{ + __m128i v0 = _mm_loadu_si128((__m128i*)(ptr)); // a0 b0 a1 b1 + __m128i v1 = _mm_loadu_si128((__m128i*)(ptr + 4)); // a2 b2 a3 b3 + + __m128i v2 = _mm_unpacklo_epi32(v0, v1); // a0 a2 b0 b2 + __m128i v3 = _mm_unpackhi_epi32(v0, v1); // a1 a3 b1 b3 + + a.val = _mm_unpacklo_epi32(v2, v3); // a0 a1 a2 a3 + b.val = _mm_unpackhi_epi32(v2, v3); // b0 b1 ab b3 +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c) +{ + __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 4)); + __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 8)); + + __m128i t10 = _mm_unpacklo_epi32(t00, _mm_unpackhi_epi64(t01, t01)); + __m128i t11 = _mm_unpacklo_epi32(_mm_unpackhi_epi64(t00, t00), t02); + __m128i t12 = _mm_unpacklo_epi32(t01, _mm_unpackhi_epi64(t02, t02)); + + a.val = _mm_unpacklo_epi32(t10, _mm_unpackhi_epi64(t11, t11)); + b.val = _mm_unpacklo_epi32(_mm_unpackhi_epi64(t10, t10), t12); + c.val = _mm_unpacklo_epi32(t11, _mm_unpackhi_epi64(t12, t12)); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 s0(_mm_loadu_si128((const __m128i*)ptr)); // a0 b0 c0 d0 + v_uint32x4 s1(_mm_loadu_si128((const __m128i*)(ptr + 4))); // a1 b1 c1 d1 + v_uint32x4 s2(_mm_loadu_si128((const __m128i*)(ptr + 8))); // a2 b2 c2 d2 + v_uint32x4 s3(_mm_loadu_si128((const __m128i*)(ptr + 12))); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b) +{ + __m128 u0 = _mm_loadu_ps(ptr); // a0 b0 a1 b1 + __m128 u1 = _mm_loadu_ps((ptr + 4)); // a2 b2 a3 b3 + + a.val = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(2, 0, 2, 0)); // a0 a1 a2 a3 + b.val = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(3, 1, 3, 1)); // b0 b1 ab b3 +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c) +{ + __m128 t0 = _mm_loadu_ps(ptr + 0); + __m128 t1 = _mm_loadu_ps(ptr + 4); + __m128 t2 = _mm_loadu_ps(ptr + 8); + + __m128 at12 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(0, 1, 0, 2)); + a.val = _mm_shuffle_ps(t0, at12, _MM_SHUFFLE(2, 0, 3, 0)); + + __m128 bt01 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(0, 0, 0, 1)); + __m128 bt12 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(0, 2, 0, 3)); + b.val = _mm_shuffle_ps(bt01, bt12, _MM_SHUFFLE(2, 0, 2, 0)); + + __m128 ct01 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(0, 1, 0, 2)); + c.val = _mm_shuffle_ps(ct01, t2, _MM_SHUFFLE(3, 0, 2, 0)); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c, v_float32x4& d) +{ + __m128 t0 = _mm_loadu_ps(ptr + 0); + __m128 t1 = _mm_loadu_ps(ptr + 4); + __m128 t2 = _mm_loadu_ps(ptr + 8); + __m128 t3 = _mm_loadu_ps(ptr + 12); + __m128 t02lo = _mm_unpacklo_ps(t0, t2); + __m128 t13lo = _mm_unpacklo_ps(t1, t3); + __m128 t02hi = _mm_unpackhi_ps(t0, t2); + __m128 t13hi = _mm_unpackhi_ps(t1, t3); + a.val = _mm_unpacklo_ps(t02lo, t13lo); + b.val = _mm_unpackhi_ps(t02lo, t13lo); + c.val = _mm_unpacklo_ps(t02hi, t13hi); + d.val = _mm_unpackhi_ps(t02hi, t13hi); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t1)); + b = v_uint64x2(_mm_unpackhi_epi64(t0, t1)); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b, v_uint64x2& c) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); // a0, b0 + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); // c0, a1 + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 4)); // b1, c1 + + t1 = _mm_shuffle_epi32(t1, 0x4e); // a1, c0 + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t1)); + b = v_uint64x2(_mm_unpacklo_epi64(_mm_unpackhi_epi64(t0, t0), t2)); + c = v_uint64x2(_mm_unpackhi_epi64(t1, t2)); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, + v_uint64x2& b, v_uint64x2& c, v_uint64x2& d) +{ + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); // a0 b0 + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 2)); // c0 d0 + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 4)); // a1 b1 + __m128i t3 = _mm_loadu_si128((const __m128i*)(ptr + 6)); // c1 d1 + + a = v_uint64x2(_mm_unpacklo_epi64(t0, t2)); + b = v_uint64x2(_mm_unpackhi_epi64(t0, t2)); + c = v_uint64x2(_mm_unpacklo_epi64(t1, t3)); + d = v_uint64x2(_mm_unpackhi_epi64(t1, t3)); +} + +// store interleave + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi8(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi8(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ +#if CV_SSE4_1 + const __m128i sh_a = _mm_setr_epi8(0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5); + const __m128i sh_b = _mm_setr_epi8(5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10); + const __m128i sh_c = _mm_setr_epi8(10, 5, 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15); + __m128i a0 = _mm_shuffle_epi8(a.val, sh_a); + __m128i b0 = _mm_shuffle_epi8(b.val, sh_b); + __m128i c0 = _mm_shuffle_epi8(c.val, sh_c); + + const __m128i m0 = _mm_setr_epi8(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0); + const __m128i m1 = _mm_setr_epi8(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0); + __m128i v0 = _mm_blendv_epi8(_mm_blendv_epi8(a0, b0, m1), c0, m0); + __m128i v1 = _mm_blendv_epi8(_mm_blendv_epi8(b0, c0, m1), a0, m0); + __m128i v2 = _mm_blendv_epi8(_mm_blendv_epi8(c0, a0, m1), b0, m0); +#elif CV_SSSE3 + const __m128i m0 = _mm_setr_epi8(0, 6, 11, 1, 7, 12, 2, 8, 13, 3, 9, 14, 4, 10, 15, 5); + const __m128i m1 = _mm_setr_epi8(5, 11, 0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10); + const __m128i m2 = _mm_setr_epi8(10, 0, 5, 11, 1, 6, 12, 2, 7, 13, 3, 8, 14, 4, 9, 15); + + __m128i t0 = _mm_alignr_epi8(b.val, _mm_slli_si128(a.val, 10), 5); + t0 = _mm_alignr_epi8(c.val, t0, 5); + __m128i v0 = _mm_shuffle_epi8(t0, m0); + + __m128i t1 = _mm_alignr_epi8(_mm_srli_si128(b.val, 5), _mm_slli_si128(a.val, 5), 6); + t1 = _mm_alignr_epi8(_mm_srli_si128(c.val, 5), t1, 5); + __m128i v1 = _mm_shuffle_epi8(t1, m1); + + __m128i t2 = _mm_alignr_epi8(_mm_srli_si128(c.val, 10), b.val, 11); + t2 = _mm_alignr_epi8(t2, a.val, 11); + __m128i v2 = _mm_shuffle_epi8(t2, m2); +#else + __m128i z = _mm_setzero_si128(); + __m128i ab0 = _mm_unpacklo_epi8(a.val, b.val); + __m128i ab1 = _mm_unpackhi_epi8(a.val, b.val); + __m128i c0 = _mm_unpacklo_epi8(c.val, z); + __m128i c1 = _mm_unpackhi_epi8(c.val, z); + + __m128i p00 = _mm_unpacklo_epi16(ab0, c0); + __m128i p01 = _mm_unpackhi_epi16(ab0, c0); + __m128i p02 = _mm_unpacklo_epi16(ab1, c1); + __m128i p03 = _mm_unpackhi_epi16(ab1, c1); + + __m128i p10 = _mm_unpacklo_epi32(p00, p01); + __m128i p11 = _mm_unpackhi_epi32(p00, p01); + __m128i p12 = _mm_unpacklo_epi32(p02, p03); + __m128i p13 = _mm_unpackhi_epi32(p02, p03); + + __m128i p20 = _mm_unpacklo_epi64(p10, p11); + __m128i p21 = _mm_unpackhi_epi64(p10, p11); + __m128i p22 = _mm_unpacklo_epi64(p12, p13); + __m128i p23 = _mm_unpackhi_epi64(p12, p13); + + p20 = _mm_slli_si128(p20, 1); + p22 = _mm_slli_si128(p22, 1); + + __m128i p30 = _mm_slli_epi64(_mm_unpacklo_epi32(p20, p21), 8); + __m128i p31 = _mm_srli_epi64(_mm_unpackhi_epi32(p20, p21), 8); + __m128i p32 = _mm_slli_epi64(_mm_unpacklo_epi32(p22, p23), 8); + __m128i p33 = _mm_srli_epi64(_mm_unpackhi_epi32(p22, p23), 8); + + __m128i p40 = _mm_unpacklo_epi64(p30, p31); + __m128i p41 = _mm_unpackhi_epi64(p30, p31); + __m128i p42 = _mm_unpacklo_epi64(p32, p33); + __m128i p43 = _mm_unpackhi_epi64(p32, p33); + + __m128i v0 = _mm_or_si128(_mm_srli_si128(p40, 2), _mm_slli_si128(p41, 10)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(p41, 6), _mm_slli_si128(p42, 6)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(p42, 10), _mm_slli_si128(p43, 2)); +#endif + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + _mm_stream_si128((__m128i*)(ptr + 32), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + _mm_store_si128((__m128i*)(ptr + 32), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + _mm_storeu_si128((__m128i*)(ptr + 32), v2); + } +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, const v_uint8x16& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + __m128i u0 = _mm_unpacklo_epi8(a.val, c.val); // a0 c0 a1 c1 ... + __m128i u1 = _mm_unpackhi_epi8(a.val, c.val); // a8 c8 a9 c9 ... + __m128i u2 = _mm_unpacklo_epi8(b.val, d.val); // b0 d0 b1 d1 ... + __m128i u3 = _mm_unpackhi_epi8(b.val, d.val); // b8 d8 b9 d9 ... + + __m128i v0 = _mm_unpacklo_epi8(u0, u2); // a0 b0 c0 d0 ... + __m128i v1 = _mm_unpackhi_epi8(u0, u2); // a4 b4 c4 d4 ... + __m128i v2 = _mm_unpacklo_epi8(u1, u3); // a8 b8 c8 d8 ... + __m128i v3 = _mm_unpackhi_epi8(u1, u3); // a12 b12 c12 d12 ... + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 16), v1); + _mm_stream_si128((__m128i*)(ptr + 32), v2); + _mm_stream_si128((__m128i*)(ptr + 48), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 16), v1); + _mm_store_si128((__m128i*)(ptr + 32), v2); + _mm_store_si128((__m128i*)(ptr + 48), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 16), v1); + _mm_storeu_si128((__m128i*)(ptr + 32), v2); + _mm_storeu_si128((__m128i*)(ptr + 48), v3); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi16(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi16(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, + const v_uint16x8& b, const v_uint16x8& c, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ +#if CV_SSE4_1 + const __m128i sh_a = _mm_setr_epi8(0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11); + const __m128i sh_b = _mm_setr_epi8(10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5); + const __m128i sh_c = _mm_setr_epi8(4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15); + __m128i a0 = _mm_shuffle_epi8(a.val, sh_a); + __m128i b0 = _mm_shuffle_epi8(b.val, sh_b); + __m128i c0 = _mm_shuffle_epi8(c.val, sh_c); + + __m128i v0 = _mm_blend_epi16(_mm_blend_epi16(a0, b0, 0x92), c0, 0x24); + __m128i v1 = _mm_blend_epi16(_mm_blend_epi16(c0, a0, 0x92), b0, 0x24); + __m128i v2 = _mm_blend_epi16(_mm_blend_epi16(b0, c0, 0x92), a0, 0x24); +#else + __m128i z = _mm_setzero_si128(); + __m128i ab0 = _mm_unpacklo_epi16(a.val, b.val); + __m128i ab1 = _mm_unpackhi_epi16(a.val, b.val); + __m128i c0 = _mm_unpacklo_epi16(c.val, z); + __m128i c1 = _mm_unpackhi_epi16(c.val, z); + + __m128i p10 = _mm_unpacklo_epi32(ab0, c0); + __m128i p11 = _mm_unpackhi_epi32(ab0, c0); + __m128i p12 = _mm_unpacklo_epi32(ab1, c1); + __m128i p13 = _mm_unpackhi_epi32(ab1, c1); + + __m128i p20 = _mm_unpacklo_epi64(p10, p11); + __m128i p21 = _mm_unpackhi_epi64(p10, p11); + __m128i p22 = _mm_unpacklo_epi64(p12, p13); + __m128i p23 = _mm_unpackhi_epi64(p12, p13); + + p20 = _mm_slli_si128(p20, 2); + p22 = _mm_slli_si128(p22, 2); + + __m128i p30 = _mm_unpacklo_epi64(p20, p21); + __m128i p31 = _mm_unpackhi_epi64(p20, p21); + __m128i p32 = _mm_unpacklo_epi64(p22, p23); + __m128i p33 = _mm_unpackhi_epi64(p22, p23); + + __m128i v0 = _mm_or_si128(_mm_srli_si128(p30, 2), _mm_slli_si128(p31, 10)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(p31, 6), _mm_slli_si128(p32, 6)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(p32, 10), _mm_slli_si128(p33, 2)); +#endif + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + _mm_stream_si128((__m128i*)(ptr + 16), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + _mm_store_si128((__m128i*)(ptr + 16), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + _mm_storeu_si128((__m128i*)(ptr + 16), v2); + } +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + const v_uint16x8& c, const v_uint16x8& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + __m128i u0 = _mm_unpacklo_epi16(a.val, c.val); // a0 c0 a1 c1 ... + __m128i u1 = _mm_unpackhi_epi16(a.val, c.val); // a4 c4 a5 c5 ... + __m128i u2 = _mm_unpacklo_epi16(b.val, d.val); // b0 d0 b1 d1 ... + __m128i u3 = _mm_unpackhi_epi16(b.val, d.val); // b4 d4 b5 d5 ... + + __m128i v0 = _mm_unpacklo_epi16(u0, u2); // a0 b0 c0 d0 ... + __m128i v1 = _mm_unpackhi_epi16(u0, u2); // a2 b2 c2 d2 ... + __m128i v2 = _mm_unpacklo_epi16(u1, u3); // a4 b4 c4 d4 ... + __m128i v3 = _mm_unpackhi_epi16(u1, u3); // a6 b6 c6 d6 ... + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 8), v1); + _mm_stream_si128((__m128i*)(ptr + 16), v2); + _mm_stream_si128((__m128i*)(ptr + 24), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 8), v1); + _mm_store_si128((__m128i*)(ptr + 16), v2); + _mm_store_si128((__m128i*)(ptr + 24), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 8), v1); + _mm_storeu_si128((__m128i*)(ptr + 16), v2); + _mm_storeu_si128((__m128i*)(ptr + 24), v3); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi32(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi32(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 4), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 4), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 4), v1); + } +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + v_uint32x4 z = v_setzero_u32(), u0, u1, u2, u3; + v_transpose4x4(a, b, c, z, u0, u1, u2, u3); + + __m128i v0 = _mm_or_si128(u0.val, _mm_slli_si128(u1.val, 12)); + __m128i v1 = _mm_or_si128(_mm_srli_si128(u1.val, 4), _mm_slli_si128(u2.val, 8)); + __m128i v2 = _mm_or_si128(_mm_srli_si128(u2.val, 8), _mm_slli_si128(u3.val, 4)); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 4), v1); + _mm_stream_si128((__m128i*)(ptr + 8), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 4), v1); + _mm_store_si128((__m128i*)(ptr + 8), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 4), v1); + _mm_storeu_si128((__m128i*)(ptr + 8), v2); + } +} + +inline void v_store_interleave(unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + v_uint32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0.val); + _mm_stream_si128((__m128i*)(ptr + 4), v1.val); + _mm_stream_si128((__m128i*)(ptr + 8), v2.val); + _mm_stream_si128((__m128i*)(ptr + 12), v3.val); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0.val); + _mm_store_si128((__m128i*)(ptr + 4), v1.val); + _mm_store_si128((__m128i*)(ptr + 8), v2.val); + _mm_store_si128((__m128i*)(ptr + 12), v3.val); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0.val); + _mm_storeu_si128((__m128i*)(ptr + 4), v1.val); + _mm_storeu_si128((__m128i*)(ptr + 8), v2.val); + _mm_storeu_si128((__m128i*)(ptr + 12), v3.val); + } +} + +// 2-channel, float only +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 v0 = _mm_unpacklo_ps(a.val, b.val); // a0 b0 a1 b1 + __m128 v1 = _mm_unpackhi_ps(a.val, b.val); // a2 b2 a3 b3 + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + } +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 u0 = _mm_shuffle_ps(a.val, b.val, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 u1 = _mm_shuffle_ps(c.val, a.val, _MM_SHUFFLE(1, 1, 0, 0)); + __m128 v0 = _mm_shuffle_ps(u0, u1, _MM_SHUFFLE(2, 0, 2, 0)); + __m128 u2 = _mm_shuffle_ps(b.val, c.val, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 u3 = _mm_shuffle_ps(a.val, b.val, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v1 = _mm_shuffle_ps(u2, u3, _MM_SHUFFLE(2, 0, 2, 0)); + __m128 u4 = _mm_shuffle_ps(c.val, a.val, _MM_SHUFFLE(3, 3, 2, 2)); + __m128 u5 = _mm_shuffle_ps(b.val, c.val, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 v2 = _mm_shuffle_ps(u4, u5, _MM_SHUFFLE(2, 0, 2, 0)); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + _mm_stream_ps(ptr + 8, v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + _mm_store_ps(ptr + 8, v2); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + _mm_storeu_ps(ptr + 8, v2); + } +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128 u0 = _mm_unpacklo_ps(a.val, c.val); + __m128 u1 = _mm_unpacklo_ps(b.val, d.val); + __m128 u2 = _mm_unpackhi_ps(a.val, c.val); + __m128 u3 = _mm_unpackhi_ps(b.val, d.val); + __m128 v0 = _mm_unpacklo_ps(u0, u1); + __m128 v2 = _mm_unpacklo_ps(u2, u3); + __m128 v1 = _mm_unpackhi_ps(u0, u1); + __m128 v3 = _mm_unpackhi_ps(u2, u3); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_ps(ptr, v0); + _mm_stream_ps(ptr + 4, v1); + _mm_stream_ps(ptr + 8, v2); + _mm_stream_ps(ptr + 12, v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_ps(ptr, v0); + _mm_store_ps(ptr + 4, v1); + _mm_store_ps(ptr + 8, v2); + _mm_store_ps(ptr + 12, v3); + } + else + { + _mm_storeu_ps(ptr, v0); + _mm_storeu_ps(ptr + 4, v1); + _mm_storeu_ps(ptr + 8, v2); + _mm_storeu_ps(ptr + 12, v3); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpackhi_epi64(a.val, b.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpacklo_epi64(c.val, _mm_unpackhi_epi64(a.val, a.val)); + __m128i v2 = _mm_unpackhi_epi64(b.val, c.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + _mm_stream_si128((__m128i*)(ptr + 4), v2); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + _mm_store_si128((__m128i*)(ptr + 4), v2); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + _mm_storeu_si128((__m128i*)(ptr + 4), v2); + } +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, const v_uint64x2& d, + hal::StoreMode mode = hal::STORE_UNALIGNED) +{ + __m128i v0 = _mm_unpacklo_epi64(a.val, b.val); + __m128i v1 = _mm_unpacklo_epi64(c.val, d.val); + __m128i v2 = _mm_unpackhi_epi64(a.val, b.val); + __m128i v3 = _mm_unpackhi_epi64(c.val, d.val); + + if( mode == hal::STORE_ALIGNED_NOCACHE ) + { + _mm_stream_si128((__m128i*)(ptr), v0); + _mm_stream_si128((__m128i*)(ptr + 2), v1); + _mm_stream_si128((__m128i*)(ptr + 4), v2); + _mm_stream_si128((__m128i*)(ptr + 6), v3); + } + else if( mode == hal::STORE_ALIGNED ) + { + _mm_store_si128((__m128i*)(ptr), v0); + _mm_store_si128((__m128i*)(ptr + 2), v1); + _mm_store_si128((__m128i*)(ptr + 4), v2); + _mm_store_si128((__m128i*)(ptr + 6), v3); + } + else + { + _mm_storeu_si128((__m128i*)(ptr), v0); + _mm_storeu_si128((__m128i*)(ptr + 2), v1); + _mm_storeu_si128((__m128i*)(ptr + 4), v2); + _mm_storeu_si128((__m128i*)(ptr + 6), v3); + } +} + +#define OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int8x16, schar, s8, v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int16x8, short, s16, v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int32x4, int, s32, v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_int64x2, int64, s64, v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_SSE_LOADSTORE_INTERLEAVE(v_float64x2, double, f64, v_uint64x2, uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(_mm_cvtepi32_ps(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + return v_float32x4(_mm_cvtpd_ps(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + return v_float32x4(_mm_movelh_ps(_mm_cvtpd_ps(a.val), _mm_cvtpd_ps(b.val))); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ + return v_float64x2(_mm_cvtepi32_pd(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ + return v_float64x2(_mm_cvtepi32_pd(_mm_srli_si128(a.val,8))); +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + return v_float64x2(_mm_cvtps_pd(a.val)); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + return v_float64x2(_mm_cvtps_pd(_mm_movehl_ps(a.val, a.val))); +} + +// from (Mysticial and wim) https://stackoverflow.com/q/41144668 +inline v_float64x2 v_cvt_f64(const v_int64x2& v) +{ + // constants encoded as floating-point + __m128i magic_i_hi32 = _mm_set1_epi64x(0x4530000080000000); // 2^84 + 2^63 + __m128i magic_i_all = _mm_set1_epi64x(0x4530000080100000); // 2^84 + 2^63 + 2^52 + __m128d magic_d_all = _mm_castsi128_pd(magic_i_all); + // Blend the 32 lowest significant bits of v with magic_int_lo +#if CV_SSE4_1 + __m128i magic_i_lo = _mm_set1_epi64x(0x4330000000000000); // 2^52 + __m128i v_lo = _mm_blend_epi16(v.val, magic_i_lo, 0xcc); +#else + __m128i magic_i_lo = _mm_set1_epi32(0x43300000); // 2^52 + __m128i v_lo = _mm_unpacklo_epi32(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(0, 0, 2, 0)), magic_i_lo); +#endif + // Extract the 32 most significant bits of v + __m128i v_hi = _mm_srli_epi64(v.val, 32); + // Flip the msb of v_hi and blend with 0x45300000 + v_hi = _mm_xor_si128(v_hi, magic_i_hi32); + // Compute in double precision + __m128d v_hi_dbl = _mm_sub_pd(_mm_castsi128_pd(v_hi), magic_d_all); + // (v_hi - magic_d_all) + v_lo Do not assume associativity of floating point addition + __m128d result = _mm_add_pd(v_hi_dbl, _mm_castsi128_pd(v_lo)); + return v_float64x2(result); +} + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]]), + _mm_setr_pi8(tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]) + )); +#endif +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3]), + *(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7]))); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3])), + _mm_setr_pi16(*(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7])) + )); +#endif +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int8x16(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +#else + return v_int8x16(_mm_setr_epi64( + _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])), + _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])) + )); +#endif +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int16x8(_mm_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], + tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]])); +#else + return v_int16x8(_mm_setr_epi64( + _mm_setr_pi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]), + _mm_setr_pi16(tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]) + )); +#endif +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int16x8(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), + *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +#else + return v_int16x8(_mm_setr_epi64( + _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])), + _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])) + )); +#endif +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short *)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ +#if defined(_MSC_VER) + return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]], + tab[idx[2]], tab[idx[3]])); +#else + return v_int32x4(_mm_setr_epi64( + _mm_setr_pi32(tab[idx[0]], tab[idx[1]]), + _mm_setr_pi32(tab[idx[2]], tab[idx[3]]) + )); +#endif +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(_mm_loadu_si128((const __m128i*)(tab + idx[0]))); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int *)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int *)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int *)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(_mm_set_epi64x(tab[idx[1]], tab[idx[0]])); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(_mm_loadu_si128((const __m128i*)(tab + idx[0]))); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(_mm_setr_ps(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int *)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_quads((const int *)tab, idx)); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(_mm_setr_pd(tab[idx[0]], tab[idx[1]])); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_float64x2(_mm_castsi128_pd(_mm_loadu_si128((const __m128i*)(tab + idx[0])))); } + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + return v_float32x4(_mm_setr_ps(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]])); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + int idx[2]; + v_store_low(idx, idxvec); + return v_float64x2(_mm_setr_pd(tab[idx[0]], tab[idx[1]])); +} + +// loads pairs from the table and deinterleaves them, e.g. returns: +// x = (tab[idxvec[0], tab[idxvec[1]], tab[idxvec[2]], tab[idxvec[3]]), +// y = (tab[idxvec[0]+1], tab[idxvec[1]+1], tab[idxvec[2]+1], tab[idxvec[3]+1]) +// note that the indices are float's indices, not the float-pair indices. +// in theory, this function can be used to implement bilinear interpolation, +// when idxvec are the offsets within the image. +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + int CV_DECL_ALIGNED(32) idx[4]; + v_store_aligned(idx, idxvec); + __m128 z = _mm_setzero_ps(); + __m128 xy01 = _mm_loadl_pi(z, (__m64*)(tab + idx[0])); + __m128 xy23 = _mm_loadl_pi(z, (__m64*)(tab + idx[2])); + xy01 = _mm_loadh_pi(xy01, (__m64*)(tab + idx[1])); + xy23 = _mm_loadh_pi(xy23, (__m64*)(tab + idx[3])); + __m128 xxyy02 = _mm_unpacklo_ps(xy01, xy23); + __m128 xxyy13 = _mm_unpackhi_ps(xy01, xy23); + x = v_float32x4(_mm_unpacklo_ps(xxyy02, xxyy13)); + y = v_float32x4(_mm_unpackhi_ps(xxyy02, xxyy13)); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + int idx[2]; + v_store_low(idx, idxvec); + __m128d xy0 = _mm_loadu_pd(tab + idx[0]); + __m128d xy1 = _mm_loadu_pd(tab + idx[1]); + x = v_float64x2(_mm_unpacklo_pd(xy0, xy1)); + y = v_float64x2(_mm_unpackhi_pd(xy0, xy1)); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0d0e0c0b090a08, 0x0705060403010200))); +#else + __m128i a = _mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(3, 1, 2, 0)); + a = _mm_shuffle_epi32(a, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int8x16(_mm_unpacklo_epi8(a, _mm_unpackhi_epi64(a, a))); +#endif +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0b0e0a0d090c08, 0x0703060205010400))); +#else + __m128i a = _mm_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int8x16(_mm_unpacklo_epi8(a, _mm_unpackhi_epi64(a, a))); +#endif +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0e0b0a0d0c0908, 0x0706030205040100))); +#else + __m128i a = _mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(3, 1, 2, 0)); + return v_int16x8(_mm_shufflehi_epi16(a, _MM_SHUFFLE(3, 1, 2, 0))); +#endif +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0x0f0e07060d0c0504, 0x0b0a030209080100))); +#else + return v_int16x8(_mm_unpacklo_epi16(vec.val, _mm_unpackhi_epi64(vec.val, vec.val))); +#endif +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + return v_int32x4(_mm_shuffle_epi32(vec.val, _MM_SHUFFLE(3, 1, 2, 0))); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) { return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ +#if CV_SSSE3 + return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0xffffff0f0e0d0c0a, 0x0908060504020100))); +#else + __m128i mask = _mm_set1_epi64x(0x00000000FFFFFFFF); + __m128i a = _mm_srli_si128(_mm_or_si128(_mm_andnot_si128(mask, vec.val), _mm_and_si128(mask, _mm_sll_epi32(vec.val, _mm_set_epi64x(0, 8)))), 1); + return v_int8x16(_mm_srli_si128(_mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 1, 0, 3)), 2)); +#endif +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ +#if CV_SSSE3 + return v_int16x8(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0xffff0f0e0d0c0b0a, 0x0908050403020100))); +#else + return v_int16x8(_mm_srli_si128(_mm_shufflelo_epi16(vec.val, _MM_SHUFFLE(2, 1, 0, 3)), 2)); +#endif +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +template +inline uchar v_extract_n(const v_uint8x16& v) +{ +#if CV_SSE4_1 + return (uchar)_mm_extract_epi8(v.val, i); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline schar v_extract_n(const v_int8x16& v) +{ + return (schar)v_extract_n(v_reinterpret_as_u8(v)); +} + +template +inline ushort v_extract_n(const v_uint16x8& v) +{ + return (ushort)_mm_extract_epi16(v.val, i); +} + +template +inline short v_extract_n(const v_int16x8& v) +{ + return (short)v_extract_n(v_reinterpret_as_u16(v)); +} + +template +inline uint v_extract_n(const v_uint32x4& v) +{ +#if CV_SSE4_1 + return (uint)_mm_extract_epi32(v.val, i); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline int v_extract_n(const v_int32x4& v) +{ + return (int)v_extract_n(v_reinterpret_as_u32(v)); +} + +template +inline uint64 v_extract_n(const v_uint64x2& v) +{ +#ifdef CV__SIMD_NATIVE_mm_extract_epi64 + return (uint64)_v128_extract_epi64(v.val); +#else + return v_rotate_right(v).get0(); +#endif +} + +template +inline int64 v_extract_n(const v_int64x2& v) +{ + return (int64)v_extract_n(v_reinterpret_as_u64(v)); +} + +template +inline float v_extract_n(const v_float32x4& v) +{ + union { uint iv; float fv; } d; + d.iv = v_extract_n(v_reinterpret_as_u32(v)); + return d.fv; +} + +template +inline double v_extract_n(const v_float64x2& v) +{ + union { uint64 iv; double dv; } d; + d.iv = v_extract_n(v_reinterpret_as_u64(v)); + return d.dv; +} + +template +inline v_int32x4 v_broadcast_element(const v_int32x4& v) +{ + return v_int32x4(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(i,i,i,i))); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& v) +{ + return v_uint32x4(_mm_shuffle_epi32(v.val, _MM_SHUFFLE(i,i,i,i))); +} + +template +inline v_float32x4 v_broadcast_element(const v_float32x4& v) +{ + return v_float32x4(_mm_shuffle_ps(v.val, v.val, _MM_SHUFFLE((char)i,(char)i,(char)i,(char)i))); +} + +////////////// FP16 support /////////////////////////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ +#if CV_FP16 + return v_float32x4(_mm_cvtph_ps(_mm_loadu_si128((const __m128i*)ptr))); +#else + const __m128i z = _mm_setzero_si128(), delta = _mm_set1_epi32(0x38000000); + const __m128i signmask = _mm_set1_epi32(0x80000000), maxexp = _mm_set1_epi32(0x7c000000); + const __m128 deltaf = _mm_castsi128_ps(_mm_set1_epi32(0x38800000)); + __m128i bits = _mm_unpacklo_epi16(z, _mm_loadl_epi64((const __m128i*)ptr)); // h << 16 + __m128i e = _mm_and_si128(bits, maxexp), sign = _mm_and_si128(bits, signmask); + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_xor_si128(bits, sign), 3), delta); // ((h & 0x7fff) << 13) + delta + __m128i zt = _mm_castps_si128(_mm_sub_ps(_mm_castsi128_ps(_mm_add_epi32(t, _mm_set1_epi32(1 << 23))), deltaf)); + + t = _mm_add_epi32(t, _mm_and_si128(delta, _mm_cmpeq_epi32(maxexp, e))); + __m128i zmask = _mm_cmpeq_epi32(e, z); + __m128i ft = v_select_si128(zmask, zt, t); + return v_float32x4(_mm_castsi128_ps(_mm_or_si128(ft, sign))); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ +#if CV_FP16 + __m128i fp16_value = _mm_cvtps_ph(v.val, 0); + _mm_storel_epi64((__m128i*)ptr, fp16_value); +#else + const __m128i signmask = _mm_set1_epi32(0x80000000); + const __m128i rval = _mm_set1_epi32(0x3f000000); + + __m128i t = _mm_castps_si128(v.val); + __m128i sign = _mm_srai_epi32(_mm_and_si128(t, signmask), 16); + t = _mm_andnot_si128(signmask, t); + + __m128i finitemask = _mm_cmpgt_epi32(_mm_set1_epi32(0x47800000), t); + __m128i isnan = _mm_cmpgt_epi32(t, _mm_set1_epi32(0x7f800000)); + __m128i naninf = v_select_si128(isnan, _mm_set1_epi32(0x7e00), _mm_set1_epi32(0x7c00)); + __m128i tinymask = _mm_cmpgt_epi32(_mm_set1_epi32(0x38800000), t); + __m128i tt = _mm_castps_si128(_mm_add_ps(_mm_castsi128_ps(t), _mm_castsi128_ps(rval))); + tt = _mm_sub_epi32(tt, rval); + __m128i odd = _mm_and_si128(_mm_srli_epi32(t, 13), _mm_set1_epi32(1)); + __m128i nt = _mm_add_epi32(t, _mm_set1_epi32(0xc8000fff)); + nt = _mm_srli_epi32(_mm_add_epi32(nt, odd), 13); + t = v_select_si128(tinymask, tt, nt); + t = v_select_si128(finitemask, t, naninf); + t = _mm_or_si128(t, sign); + t = _mm_packs_epi32(t, t); + _mm_storel_epi64((__m128i*)ptr, t); +#endif +} + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse_em.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse_em.hpp new file mode 100644 index 0000000..6fb0881 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_sse_em.hpp @@ -0,0 +1,180 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_INTRIN_SSE_EM_HPP +#define OPENCV_HAL_INTRIN_SSE_EM_HPP + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#define OPENCV_HAL_SSE_WRAP_1(fun, tp) \ + inline tp _v128_##fun(const tp& a) \ + { return _mm_##fun(a); } + +#define OPENCV_HAL_SSE_WRAP_2(fun, tp) \ + inline tp _v128_##fun(const tp& a, const tp& b) \ + { return _mm_##fun(a, b); } + +#define OPENCV_HAL_SSE_WRAP_3(fun, tp) \ + inline tp _v128_##fun(const tp& a, const tp& b, const tp& c) \ + { return _mm_##fun(a, b, c); } + +///////////////////////////// XOP ///////////////////////////// + +// [todo] define CV_XOP +#if 1 // CV_XOP +inline __m128i _v128_comgt_epu32(const __m128i& a, const __m128i& b) +{ + const __m128i delta = _mm_set1_epi32((int)0x80000000); + return _mm_cmpgt_epi32(_mm_xor_si128(a, delta), _mm_xor_si128(b, delta)); +} +// wrapping XOP +#else +OPENCV_HAL_SSE_WRAP_2(_v128_comgt_epu32, __m128i) +#endif // !CV_XOP + +///////////////////////////// SSE4.1 ///////////////////////////// + +#if !CV_SSE4_1 + +/** Swizzle **/ +inline __m128i _v128_blendv_epi8(const __m128i& a, const __m128i& b, const __m128i& mask) +{ return _mm_xor_si128(a, _mm_and_si128(_mm_xor_si128(b, a), mask)); } + +/** Convert **/ +// 8 >> 16 +inline __m128i _v128_cvtepu8_epi16(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi8(a, z); +} +inline __m128i _v128_cvtepi8_epi16(const __m128i& a) +{ return _mm_srai_epi16(_mm_unpacklo_epi8(a, a), 8); } +// 8 >> 32 +inline __m128i _v128_cvtepu8_epi32(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi16(_mm_unpacklo_epi8(a, z), z); +} +inline __m128i _v128_cvtepi8_epi32(const __m128i& a) +{ + __m128i r = _mm_unpacklo_epi8(a, a); + r = _mm_unpacklo_epi8(r, r); + return _mm_srai_epi32(r, 24); +} +// 16 >> 32 +inline __m128i _v128_cvtepu16_epi32(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi16(a, z); +} +inline __m128i _v128_cvtepi16_epi32(const __m128i& a) +{ return _mm_srai_epi32(_mm_unpacklo_epi16(a, a), 16); } +// 32 >> 64 +inline __m128i _v128_cvtepu32_epi64(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpacklo_epi32(a, z); +} +inline __m128i _v128_cvtepi32_epi64(const __m128i& a) +{ return _mm_unpacklo_epi32(a, _mm_srai_epi32(a, 31)); } + +/** Arithmetic **/ +inline __m128i _v128_mullo_epi32(const __m128i& a, const __m128i& b) +{ + __m128i c0 = _mm_mul_epu32(a, b); + __m128i c1 = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)); + __m128i d0 = _mm_unpacklo_epi32(c0, c1); + __m128i d1 = _mm_unpackhi_epi32(c0, c1); + return _mm_unpacklo_epi64(d0, d1); +} + +/** Math **/ +inline __m128i _v128_min_epu32(const __m128i& a, const __m128i& b) +{ return _v128_blendv_epi8(a, b, _v128_comgt_epu32(a, b)); } + +// wrapping SSE4.1 +#else +OPENCV_HAL_SSE_WRAP_1(cvtepu8_epi16, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi8_epi16, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu8_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi8_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu16_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi16_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepu32_epi64, __m128i) +OPENCV_HAL_SSE_WRAP_1(cvtepi32_epi64, __m128i) +OPENCV_HAL_SSE_WRAP_2(min_epu32, __m128i) +OPENCV_HAL_SSE_WRAP_2(mullo_epi32, __m128i) +OPENCV_HAL_SSE_WRAP_3(blendv_epi8, __m128i) +#endif // !CV_SSE4_1 + +///////////////////////////// Revolutionary ///////////////////////////// + +/** Convert **/ +// 16 << 8 +inline __m128i _v128_cvtepu8_epi16_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi8(a, z); +} +inline __m128i _v128_cvtepi8_epi16_high(const __m128i& a) +{ return _mm_srai_epi16(_mm_unpackhi_epi8(a, a), 8); } +// 32 << 16 +inline __m128i _v128_cvtepu16_epi32_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi16(a, z); +} +inline __m128i _v128_cvtepi16_epi32_high(const __m128i& a) +{ return _mm_srai_epi32(_mm_unpackhi_epi16(a, a), 16); } +// 64 << 32 +inline __m128i _v128_cvtepu32_epi64_high(const __m128i& a) +{ + const __m128i z = _mm_setzero_si128(); + return _mm_unpackhi_epi32(a, z); +} +inline __m128i _v128_cvtepi32_epi64_high(const __m128i& a) +{ return _mm_unpackhi_epi32(a, _mm_srai_epi32(a, 31)); } + +/** Miscellaneous **/ +inline __m128i _v128_packs_epu32(const __m128i& a, const __m128i& b) +{ + const __m128i m = _mm_set1_epi32(65535); + __m128i am = _v128_min_epu32(a, m); + __m128i bm = _v128_min_epu32(b, m); +#if CV_SSE4_1 + return _mm_packus_epi32(am, bm); +#else + const __m128i d = _mm_set1_epi32(32768), nd = _mm_set1_epi16(-32768); + am = _mm_sub_epi32(am, d); + bm = _mm_sub_epi32(bm, d); + am = _mm_packs_epi32(am, bm); + return _mm_sub_epi16(am, nd); +#endif +} + +template +inline int64 _v128_extract_epi64(const __m128i& a) +{ +#if defined(CV__SIMD_HAVE_mm_extract_epi64) || (CV_SSE4_1 && (defined(__x86_64__)/*GCC*/ || defined(_M_X64)/*MSVC*/)) +#define CV__SIMD_NATIVE_mm_extract_epi64 1 + return _mm_extract_epi64(a, i); +#else + CV_DECL_ALIGNED(16) int64 tmp[2]; + _mm_store_si128((__m128i*)tmp, a); + return tmp[i]; +#endif +} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} // cv:: + +#endif // OPENCV_HAL_INTRIN_SSE_EM_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_vsx.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_vsx.hpp new file mode 100644 index 0000000..b198643 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_vsx.hpp @@ -0,0 +1,1608 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_VSX_HPP +#define OPENCV_HAL_VSX_HPP + +#include +#include "opencv2/core/utility.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 1 + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +///////// Types //////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + enum { nlanes = 16 }; + vec_uchar16 val; + + explicit v_uint8x16(const vec_uchar16& v) : val(v) + {} + v_uint8x16() + {} + v_uint8x16(vec_bchar16 v) : val(vec_uchar16_c(v)) + {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + : val(vec_uchar16_set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + {} + + static inline v_uint8x16 zero() { return v_uint8x16(vec_uchar16_z); } + + uchar get0() const + { return vec_extract(val, 0); } +}; + +struct v_int8x16 +{ + typedef schar lane_type; + enum { nlanes = 16 }; + vec_char16 val; + + explicit v_int8x16(const vec_char16& v) : val(v) + {} + v_int8x16() + {} + v_int8x16(vec_bchar16 v) : val(vec_char16_c(v)) + {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + : val(vec_char16_set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + {} + + static inline v_int8x16 zero() { return v_int8x16(vec_char16_z); } + + schar get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + enum { nlanes = 8 }; + vec_ushort8 val; + + explicit v_uint16x8(const vec_ushort8& v) : val(v) + {} + v_uint16x8() + {} + v_uint16x8(vec_bshort8 v) : val(vec_ushort8_c(v)) + {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + : val(vec_ushort8_set(v0, v1, v2, v3, v4, v5, v6, v7)) + {} + + static inline v_uint16x8 zero() { return v_uint16x8(vec_ushort8_z); } + + ushort get0() const + { return vec_extract(val, 0); } +}; + +struct v_int16x8 +{ + typedef short lane_type; + enum { nlanes = 8 }; + vec_short8 val; + + explicit v_int16x8(const vec_short8& v) : val(v) + {} + v_int16x8() + {} + v_int16x8(vec_bshort8 v) : val(vec_short8_c(v)) + {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + : val(vec_short8_set(v0, v1, v2, v3, v4, v5, v6, v7)) + {} + + static inline v_int16x8 zero() { return v_int16x8(vec_short8_z); } + + short get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + enum { nlanes = 4 }; + vec_uint4 val; + + explicit v_uint32x4(const vec_uint4& v) : val(v) + {} + v_uint32x4() + {} + v_uint32x4(vec_bint4 v) : val(vec_uint4_c(v)) + {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) : val(vec_uint4_set(v0, v1, v2, v3)) + {} + + static inline v_uint32x4 zero() { return v_uint32x4(vec_uint4_z); } + + uint get0() const + { return vec_extract(val, 0); } +}; + +struct v_int32x4 +{ + typedef int lane_type; + enum { nlanes = 4 }; + vec_int4 val; + + explicit v_int32x4(const vec_int4& v) : val(v) + {} + v_int32x4() + {} + v_int32x4(vec_bint4 v) : val(vec_int4_c(v)) + {} + v_int32x4(int v0, int v1, int v2, int v3) : val(vec_int4_set(v0, v1, v2, v3)) + {} + + static inline v_int32x4 zero() { return v_int32x4(vec_int4_z); } + + int get0() const + { return vec_extract(val, 0); } +}; + +struct v_float32x4 +{ + typedef float lane_type; + enum { nlanes = 4 }; + vec_float4 val; + + explicit v_float32x4(const vec_float4& v) : val(v) + {} + v_float32x4() + {} + v_float32x4(vec_bint4 v) : val(vec_float4_c(v)) + {} + v_float32x4(float v0, float v1, float v2, float v3) : val(vec_float4_set(v0, v1, v2, v3)) + {} + + static inline v_float32x4 zero() { return v_float32x4(vec_float4_z); } + + float get0() const + { return vec_extract(val, 0); } +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + enum { nlanes = 2 }; + vec_udword2 val; + + explicit v_uint64x2(const vec_udword2& v) : val(v) + {} + v_uint64x2() + {} + v_uint64x2(vec_bdword2 v) : val(vec_udword2_c(v)) + {} + v_uint64x2(uint64 v0, uint64 v1) : val(vec_udword2_set(v0, v1)) + {} + + static inline v_uint64x2 zero() { return v_uint64x2(vec_udword2_z); } + + uint64 get0() const + { return vec_extract(val, 0); } +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + enum { nlanes = 2 }; + vec_dword2 val; + + explicit v_int64x2(const vec_dword2& v) : val(v) + {} + v_int64x2() + {} + v_int64x2(vec_bdword2 v) : val(vec_dword2_c(v)) + {} + v_int64x2(int64 v0, int64 v1) : val(vec_dword2_set(v0, v1)) + {} + + static inline v_int64x2 zero() { return v_int64x2(vec_dword2_z); } + + int64 get0() const + { return vec_extract(val, 0); } +}; + +struct v_float64x2 +{ + typedef double lane_type; + enum { nlanes = 2 }; + vec_double2 val; + + explicit v_float64x2(const vec_double2& v) : val(v) + {} + v_float64x2() + {} + v_float64x2(vec_bdword2 v) : val(vec_double2_c(v)) + {} + v_float64x2(double v0, double v1) : val(vec_double2_set(v0, v1)) + {} + + static inline v_float64x2 zero() { return v_float64x2(vec_double2_z); } + + double get0() const + { return vec_extract(val, 0); } +}; + +#define OPENCV_HAL_IMPL_VSX_EXTRACT_N(_Tpvec, _Tp) \ +template inline _Tp v_extract_n(VSX_UNUSED(_Tpvec v)) { return vec_extract(v.val, i); } + +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint8x16, uchar) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int8x16, schar) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint16x8, ushort) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int16x8, short) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint32x4, uint) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int32x4, int) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_uint64x2, uint64) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_int64x2, int64) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_float32x4, float) +OPENCV_HAL_IMPL_VSX_EXTRACT_N(v_float64x2, double) + +//////////////// Load and store operations /////////////// + +/* + * clang-5 aborted during parse "vec_xxx_c" only if it's + * inside a function template which is defined by preprocessor macro. + * + * if vec_xxx_c defined as C++ cast, clang-5 will pass it +*/ +#define OPENCV_HAL_IMPL_VSX_INITVEC(_Tpvec, _Tp, suffix, cast) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(vec_splats((_Tp)0)); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(vec_splats((_Tp)v));} \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0 &a) \ +{ return _Tpvec((cast)a.val); } + +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint8x16, uchar, u8, vec_uchar16) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int8x16, schar, s8, vec_char16) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint16x8, ushort, u16, vec_ushort8) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int16x8, short, s16, vec_short8) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint32x4, uint, u32, vec_uint4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int32x4, int, s32, vec_int4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_uint64x2, uint64, u64, vec_udword2) +OPENCV_HAL_IMPL_VSX_INITVEC(v_int64x2, int64, s64, vec_dword2) +OPENCV_HAL_IMPL_VSX_INITVEC(v_float32x4, float, f32, vec_float4) +OPENCV_HAL_IMPL_VSX_INITVEC(v_float64x2, double, f64, vec_double2) + +#define OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, ld, ld_a, st, st_a) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(ld(0, ptr)); } \ +inline _Tpvec v_load_aligned(VSX_UNUSED(const _Tp* ptr)) \ +{ return _Tpvec(ld_a(0, ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ return _Tpvec(vec_ld_l8(ptr)); } \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ return _Tpvec(vec_mergesqh(vec_ld_l8(ptr0), vec_ld_l8(ptr1))); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ st(a.val, 0, ptr); } \ +inline void v_store_aligned(VSX_UNUSED(_Tp* ptr), const _Tpvec& a) \ +{ st_a(a.val, 0, ptr); } \ +inline void v_store_aligned_nocache(VSX_UNUSED(_Tp* ptr), const _Tpvec& a) \ +{ st_a(a.val, 0, ptr); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode mode) \ +{ if(mode == hal::STORE_UNALIGNED) st(a.val, 0, ptr); else st_a(a.val, 0, ptr); } \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ vec_st_l8(a.val, ptr); } \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ vec_st_h8(a.val, ptr); } + +// working around gcc bug for aligned ld/st +// if runtime check for vec_ld/st fail we failback to unaligned ld/st +// https://github.com/opencv/opencv/issues/13211 +#ifdef CV_COMPILER_VSX_BROKEN_ALIGNED + #define OPENCV_HAL_IMPL_VSX_LOADSTORE(_Tpvec, _Tp) \ + OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, vsx_ld, vsx_ld, vsx_st, vsx_st) +#else + #define OPENCV_HAL_IMPL_VSX_LOADSTORE(_Tpvec, _Tp) \ + OPENCV_HAL_IMPL_VSX_LOADSTORE_C(_Tpvec, _Tp, vsx_ld, vec_ld, vsx_st, vec_st) +#endif + +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint8x16, uchar) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int8x16, schar) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint16x8, ushort) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int16x8, short) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_uint32x4, uint) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_int32x4, int) +OPENCV_HAL_IMPL_VSX_LOADSTORE(v_float32x4, float) + +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_float64x2, double, vsx_ld, vsx_ld, vsx_st, vsx_st) +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_uint64x2, uint64, vsx_ld2, vsx_ld2, vsx_st2, vsx_st2) +OPENCV_HAL_IMPL_VSX_LOADSTORE_C(v_int64x2, int64, vsx_ld2, vsx_ld2, vsx_st2, vsx_st2) + +//////////////// Value reordering /////////////// + +/* de&interleave */ +#define OPENCV_HAL_IMPL_VSX_INTERLEAVE(_Tp, _Tpvec) \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, _Tpvec& b) \ +{ vec_ld_deinterleave(ptr, a.val, b.val);} \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, \ + _Tpvec& b, _Tpvec& c) \ +{ vec_ld_deinterleave(ptr, a.val, b.val, c.val); } \ +inline void v_load_deinterleave(const _Tp* ptr, _Tpvec& a, _Tpvec& b, \ + _Tpvec& c, _Tpvec& d) \ +{ vec_ld_deinterleave(ptr, a.val, b.val, c.val, d.val); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, const _Tpvec& b, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, ptr); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, \ + const _Tpvec& b, const _Tpvec& c, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, c.val, ptr); } \ +inline void v_store_interleave(_Tp* ptr, const _Tpvec& a, const _Tpvec& b, \ + const _Tpvec& c, const _Tpvec& d, \ + hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \ +{ vec_st_interleave(a.val, b.val, c.val, d.val, ptr); } + +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uchar, v_uint8x16) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(schar, v_int8x16) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(ushort, v_uint16x8) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(short, v_int16x8) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uint, v_uint32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(int, v_int32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(float, v_float32x4) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(double, v_float64x2) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(int64, v_int64x2) +OPENCV_HAL_IMPL_VSX_INTERLEAVE(uint64, v_uint64x2) + +/* Expand */ +#define OPENCV_HAL_IMPL_VSX_EXPAND(_Tpvec, _Tpwvec, _Tp, fl, fh) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = fh(a.val); \ + b1.val = fl(a.val); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ return _Tpwvec(fh(a.val)); } \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ return _Tpwvec(fl(a.val)); } \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ return _Tpwvec(fh(vec_ld_l8(ptr))); } + +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint8x16, v_uint16x8, uchar, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int8x16, v_int16x8, schar, vec_unpackl, vec_unpackh) +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint16x8, v_uint32x4, ushort, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int16x8, v_int32x4, short, vec_unpackl, vec_unpackh) +OPENCV_HAL_IMPL_VSX_EXPAND(v_uint32x4, v_uint64x2, uint, vec_unpacklu, vec_unpackhu) +OPENCV_HAL_IMPL_VSX_EXPAND(v_int32x4, v_int64x2, int, vec_unpackl, vec_unpackh) + +/* Load and zero expand a 4 byte value into the second dword, first is don't care. */ +#if !defined(CV_COMPILER_VSX_BROKEN_ASM) + #define _LXSIWZX(out, ptr, T) __asm__ ("lxsiwzx %x0, 0, %1\r\n" : "=wa"(out) : "r" (ptr) : "memory"); +#else + /* This is compiler-agnostic, but will introduce an unneeded splat on the critical path. */ + #define _LXSIWZX(out, ptr, T) out = (T)vec_udword2_sp(*(uint32_t*)(ptr)); +#endif + +inline v_uint32x4 v_load_expand_q(const uchar* ptr) +{ + // Zero-extend the extra 24B instead of unpacking. Usually faster in small kernel + // Likewise note, value is zero extended and upper 4 bytes are zero'ed. + vec_uchar16 pmu = {8, 12, 12, 12, 9, 12, 12, 12, 10, 12, 12, 12, 11, 12, 12, 12}; + vec_uchar16 out; + + _LXSIWZX(out, ptr, vec_uchar16); + out = vec_perm(out, out, pmu); + return v_uint32x4((vec_uint4)out); +} + +inline v_int32x4 v_load_expand_q(const schar* ptr) +{ + vec_char16 out; + vec_short8 outs; + vec_int4 outw; + + _LXSIWZX(out, ptr, vec_char16); + outs = vec_unpackl(out); + outw = vec_unpackh(outs); + return v_int32x4(outw); +} + +/* pack */ +#define OPENCV_HAL_IMPL_VSX_PACK(_Tpvec, _Tp, _Tpwvec, _Tpvn, _Tpdel, sfnc, pkfnc, addfnc, pack) \ +inline _Tpvec v_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + return _Tpvec(pkfnc(a.val, b.val)); \ +} \ +inline void v_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + vec_st_l8(pkfnc(a.val, a.val), ptr); \ +} \ +template \ +inline _Tpvec v_rshr_##pack(const _Tpwvec& a, const _Tpwvec& b) \ +{ \ + const __vector _Tpvn vn = vec_splats((_Tpvn)n); \ + const __vector _Tpdel delta = vec_splats((_Tpdel)((_Tpdel)1 << (n-1))); \ + return _Tpvec(pkfnc(sfnc(addfnc(a.val, delta), vn), sfnc(addfnc(b.val, delta), vn))); \ +} \ +template \ +inline void v_rshr_##pack##_store(_Tp* ptr, const _Tpwvec& a) \ +{ \ + const __vector _Tpvn vn = vec_splats((_Tpvn)n); \ + const __vector _Tpdel delta = vec_splats((_Tpdel)((_Tpdel)1 << (n-1))); \ + vec_st_l8(pkfnc(sfnc(addfnc(a.val, delta), vn), delta), ptr); \ +} + +OPENCV_HAL_IMPL_VSX_PACK(v_uint8x16, uchar, v_uint16x8, unsigned short, unsigned short, + vec_sr, vec_packs, vec_adds, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int8x16, schar, v_int16x8, unsigned short, short, + vec_sra, vec_packs, vec_adds, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint16x8, ushort, v_uint32x4, unsigned int, unsigned int, + vec_sr, vec_packs, vec_add, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int16x8, short, v_int32x4, unsigned int, int, + vec_sra, vec_packs, vec_add, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint32x4, uint, v_uint64x2, unsigned long long, unsigned long long, + vec_sr, vec_pack, vec_add, pack) +OPENCV_HAL_IMPL_VSX_PACK(v_int32x4, int, v_int64x2, unsigned long long, long long, + vec_sra, vec_pack, vec_add, pack) + +OPENCV_HAL_IMPL_VSX_PACK(v_uint8x16, uchar, v_int16x8, unsigned short, short, + vec_sra, vec_packsu, vec_adds, pack_u) +OPENCV_HAL_IMPL_VSX_PACK(v_uint16x8, ushort, v_int32x4, unsigned int, int, + vec_sra, vec_packsu, vec_add, pack_u) +// Following variant is not implemented on other platforms: +//OPENCV_HAL_IMPL_VSX_PACK(v_uint32x4, uint, v_int64x2, unsigned long long, long long, +// vec_sra, vec_packsu, vec_add, pack_u) + +// pack boolean +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_uchar16 ab = vec_pack(a.val, b.val); + return v_uint8x16(ab); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + vec_ushort8 ab = vec_pack(a.val, b.val); + vec_ushort8 cd = vec_pack(c.val, d.val); + return v_uint8x16(vec_pack(ab, cd)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + vec_uint4 ab = vec_pack(a.val, b.val); + vec_uint4 cd = vec_pack(c.val, d.val); + vec_uint4 ef = vec_pack(e.val, f.val); + vec_uint4 gh = vec_pack(g.val, h.val); + + vec_ushort8 abcd = vec_pack(ab, cd); + vec_ushort8 efgh = vec_pack(ef, gh); + return v_uint8x16(vec_pack(abcd, efgh)); +} + +/* Recombine */ +template +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) +{ + b0.val = vec_mergeh(a0.val, a1.val); + b1.val = vec_mergel(a0.val, a1.val); +} + +template +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(vec_mergesql(a.val, b.val)); } + +template +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) +{ return _Tpvec(vec_mergesqh(a.val, b.val)); } + +template +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) +{ + c.val = vec_mergesqh(a.val, b.val); + d.val = vec_mergesql(a.val, b.val); +} + +////////// Arithmetic, bitwise and comparison operations ///////// + +/* Element-wise binary and unary operations */ +/** Arithmetics **/ +#define OPENCV_HAL_IMPL_VSX_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(intrin(a.val, b.val)); } \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ a.val = intrin(a.val, b.val); return a; } + +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint8x16, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint8x16, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int8x16, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int8x16, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint16x8, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint16x8, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int16x8, vec_adds) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int16x8, vec_subs) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_uint32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_int32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_float32x4, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_float32x4, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_float32x4, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(/, v_float32x4, vec_div) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_float64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_float64x2, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(*, v_float64x2, vec_mul) +OPENCV_HAL_IMPL_VSX_BIN_OP(/, v_float64x2, vec_div) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_uint64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_uint64x2, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_OP(+, v_int64x2, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_OP(-, v_int64x2, vec_sub) + +// saturating multiply +#define OPENCV_HAL_IMPL_VSX_MUL_SAT(_Tpvec, _Tpwvec) \ + inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ + { \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ + } \ + inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ + { a = a * b; return a; } + +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_int16x8, v_int32x4) +OPENCV_HAL_IMPL_VSX_MUL_SAT(v_uint16x8, v_uint32x4) + +template +inline void v_mul_expand(const Tvec& a, const Tvec& b, Twvec& c, Twvec& d) +{ + Twvec p0 = Twvec(vec_mule(a.val, b.val)); + Twvec p1 = Twvec(vec_mulo(a.val, b.val)); + v_zip(p0, p1, c, d); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + vec_int4 p0 = vec_mule(a.val, b.val); + vec_int4 p1 = vec_mulo(a.val, b.val); + static const vec_uchar16 perm = {2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}; + return v_int16x8(vec_perm(vec_short8_c(p0), vec_short8_c(p1), perm)); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_uint4 p0 = vec_mule(a.val, b.val); + vec_uint4 p1 = vec_mulo(a.val, b.val); + static const vec_uchar16 perm = {2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}; + return v_uint16x8(vec_perm(vec_ushort8_c(p0), vec_ushort8_c(p1), perm)); +} + +/** Non-saturating arithmetics **/ +#define OPENCV_HAL_IMPL_VSX_BIN_FUNC(func, intrin) \ +template \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(intrin(a.val, b.val)); } + +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_add_wrap, vec_add) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_sub_wrap, vec_sub) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_mul_wrap, vec_mul) + +/** Bitwise shifts **/ +#define OPENCV_HAL_IMPL_VSX_SHIFT_OP(_Tpvec, shr, splfunc) \ +inline _Tpvec operator << (const _Tpvec& a, int imm) \ +{ return _Tpvec(vec_sl(a.val, splfunc(imm))); } \ +inline _Tpvec operator >> (const _Tpvec& a, int imm) \ +{ return _Tpvec(shr(a.val, splfunc(imm))); } \ +template inline _Tpvec v_shl(const _Tpvec& a) \ +{ return _Tpvec(vec_sl(a.val, splfunc(imm))); } \ +template inline _Tpvec v_shr(const _Tpvec& a) \ +{ return _Tpvec(shr(a.val, splfunc(imm))); } + +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint8x16, vec_sr, vec_uchar16_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint16x8, vec_sr, vec_ushort8_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint32x4, vec_sr, vec_uint4_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_uint64x2, vec_sr, vec_udword2_sp) +// algebraic right shift +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int8x16, vec_sra, vec_uchar16_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int16x8, vec_sra, vec_ushort8_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int32x4, vec_sra, vec_uint4_sp) +OPENCV_HAL_IMPL_VSX_SHIFT_OP(v_int64x2, vec_sra, vec_udword2_sp) + +/** Bitwise logic **/ +#define OPENCV_HAL_IMPL_VSX_LOGIC_OP(_Tpvec) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(&, _Tpvec, vec_and) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(|, _Tpvec, vec_or) \ +OPENCV_HAL_IMPL_VSX_BIN_OP(^, _Tpvec, vec_xor) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ return _Tpvec(vec_not(a.val)); } + +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint8x16) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int8x16) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint16x8) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int16x8) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_uint64x2) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_int64x2) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_float32x4) +OPENCV_HAL_IMPL_VSX_LOGIC_OP(v_float64x2) + +/** Bitwise select **/ +#define OPENCV_HAL_IMPL_VSX_SELECT(_Tpvec, cast) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_sel(b.val, a.val, cast(mask.val))); } + +OPENCV_HAL_IMPL_VSX_SELECT(v_uint8x16, vec_bchar16_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int8x16, vec_bchar16_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_uint16x8, vec_bshort8_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int16x8, vec_bshort8_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_uint32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_int32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_float32x4, vec_bint4_c) +OPENCV_HAL_IMPL_VSX_SELECT(v_float64x2, vec_bdword2_c) + +/** Comparison **/ +#define OPENCV_HAL_IMPL_VSX_INT_CMP_OP(_Tpvec) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpeq(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpne(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmplt(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpgt(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmple(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_cmpge(a.val, b.val)); } + +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint8x16) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int8x16) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint16x8) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int16x8) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_float32x4) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_float64x2) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_uint64x2) +OPENCV_HAL_IMPL_VSX_INT_CMP_OP(v_int64x2) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ return v_float32x4(vec_cmpeq(a.val, a.val)); } +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ return v_float64x2(vec_cmpeq(a.val, a.val)); } + +/** min/max **/ +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_min, vec_min) +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_max, vec_max) + +/** Rotate **/ +#define OPENCV_IMPL_VSX_ROTATE(_Tpvec, suffix, shf, cast) \ +template \ +inline _Tpvec v_rotate_##suffix(const _Tpvec& a) \ +{ \ + const int wd = imm * sizeof(typename _Tpvec::lane_type); \ + if (wd > 15) \ + return _Tpvec::zero(); \ + return _Tpvec((cast)shf(vec_uchar16_c(a.val), vec_uchar16_sp(wd << 3))); \ +} + +#define OPENCV_IMPL_VSX_ROTATE_LR(_Tpvec, cast) \ +OPENCV_IMPL_VSX_ROTATE(_Tpvec, left, vec_slo, cast) \ +OPENCV_IMPL_VSX_ROTATE(_Tpvec, right, vec_sro, cast) + +OPENCV_IMPL_VSX_ROTATE_LR(v_uint8x16, vec_uchar16) +OPENCV_IMPL_VSX_ROTATE_LR(v_int8x16, vec_char16) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint16x8, vec_ushort8) +OPENCV_IMPL_VSX_ROTATE_LR(v_int16x8, vec_short8) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint32x4, vec_uint4) +OPENCV_IMPL_VSX_ROTATE_LR(v_int32x4, vec_int4) +OPENCV_IMPL_VSX_ROTATE_LR(v_float32x4, vec_float4) +OPENCV_IMPL_VSX_ROTATE_LR(v_uint64x2, vec_udword2) +OPENCV_IMPL_VSX_ROTATE_LR(v_int64x2, vec_dword2) +OPENCV_IMPL_VSX_ROTATE_LR(v_float64x2, vec_double2) + +template +inline _Tpvec v_rotate_right(const _Tpvec& a, const _Tpvec& b) +{ + enum { CV_SHIFT = 16 - imm * (sizeof(typename _Tpvec::lane_type)) }; + if (CV_SHIFT == 16) + return a; +#ifdef __IBMCPP__ + return _Tpvec(vec_sld(b.val, a.val, CV_SHIFT & 15)); +#else + return _Tpvec(vec_sld(b.val, a.val, CV_SHIFT)); +#endif +} + +template +inline _Tpvec v_rotate_left(const _Tpvec& a, const _Tpvec& b) +{ + enum { CV_SHIFT = imm * (sizeof(typename _Tpvec::lane_type)) }; + if (CV_SHIFT == 16) + return b; + return _Tpvec(vec_sld(a.val, b.val, CV_SHIFT)); +} + +#define OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, suffix, rg1, rg2) \ +template \ +inline _Tpvec v_rotate_##suffix(const _Tpvec& a, const _Tpvec& b) \ +{ \ + if (imm == 1) \ + return _Tpvec(vec_permi(rg1.val, rg2.val, 2)); \ + return imm ? b : a; \ +} + +#define OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(_Tpvec) \ +OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, left, b, a) \ +OPENCV_IMPL_VSX_ROTATE_64_2RG(_Tpvec, right, a, b) + +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_float64x2) +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_uint64x2) +OPENCV_IMPL_VSX_ROTATE_64_2RG_LR(v_int64x2) + +/* Reverse */ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ + static const vec_uchar16 perm = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_uint8x16(vec_perm(vec, vec, perm)); +} + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ + static const vec_uchar16 perm = {14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u16(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ + static const vec_uchar16 perm = {12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u32(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ + static const vec_uchar16 perm = {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}; + vec_uchar16 vec = (vec_uchar16)a.val; + return v_reinterpret_as_u64(v_uint8x16(vec_perm(vec, vec, perm))); +} + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + +/* Extract */ +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ return v_rotate_right(a, b); } + +////////// Reduce and mask ///////// + +/** Reduce **/ +inline uint v_reduce_sum(const v_uint8x16& a) +{ + const vec_uint4 zero4 = vec_uint4_z; + vec_uint4 sum4 = vec_sum4s(a.val, zero4); + return (uint)vec_extract(vec_sums(vec_int4_c(sum4), vec_int4_c(zero4)), 3); +} +inline int v_reduce_sum(const v_int8x16& a) +{ + const vec_int4 zero4 = vec_int4_z; + vec_int4 sum4 = vec_sum4s(a.val, zero4); + return (int)vec_extract(vec_sums(sum4, zero4), 3); +} +inline int v_reduce_sum(const v_int16x8& a) +{ + const vec_int4 zero = vec_int4_z; + return saturate_cast(vec_extract(vec_sums(vec_sum4s(a.val, zero), zero), 3)); +} +inline uint v_reduce_sum(const v_uint16x8& a) +{ + const vec_int4 v4 = vec_int4_c(vec_unpackhu(vec_adds(a.val, vec_sld(a.val, a.val, 8)))); + return saturate_cast(vec_extract(vec_sums(v4, vec_int4_z), 3)); +} + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + const _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 4)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_uint32x4, vec_uint4, uint, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_int32x4, vec_int4, int, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, sum, vec_add) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_4(v_float32x4, vec_float4, float, min, vec_min) + +inline uint64 v_reduce_sum(const v_uint64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} +inline int64 v_reduce_sum(const v_int64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} +inline double v_reduce_sum(const v_float64x2& a) +{ + return vec_extract(vec_add(a.val, vec_permi(a.val, a.val, 3)), 0); +} + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + rs = func(rs, vec_sld(rs, rs, 4)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 2)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_uint16x8, vec_ushort8, ushort, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_uint16x8, vec_ushort8, ushort, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_int16x8, vec_short8, short, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_8(v_int16x8, vec_short8, short, min, vec_min) + +#define OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(_Tpvec, _Tpvec2, scalartype, suffix, func) \ +inline scalartype v_reduce_##suffix(const _Tpvec& a) \ +{ \ + _Tpvec2 rs = func(a.val, vec_sld(a.val, a.val, 8)); \ + rs = func(rs, vec_sld(rs, rs, 4)); \ + rs = func(rs, vec_sld(rs, rs, 2)); \ + return vec_extract(func(rs, vec_sld(rs, rs, 1)), 0); \ +} +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_uint8x16, vec_uchar16, uchar, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_uint8x16, vec_uchar16, uchar, min, vec_min) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_int8x16, vec_char16, schar, max, vec_max) +OPENCV_HAL_IMPL_VSX_REDUCE_OP_16(v_int8x16, vec_char16, schar, min, vec_min) + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + vec_float4 ac = vec_add(vec_mergel(a.val, c.val), vec_mergeh(a.val, c.val)); + ac = vec_add(ac, vec_sld(ac, ac, 8)); + + vec_float4 bd = vec_add(vec_mergel(b.val, d.val), vec_mergeh(b.val, d.val)); + bd = vec_add(bd, vec_sld(bd, bd, 8)); + return v_float32x4(vec_mergeh(ac, bd)); +} + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + const vec_uint4 zero4 = vec_uint4_z; + vec_uint4 sum4 = vec_sum4s(vec_absd(a.val, b.val), zero4); + return (unsigned)vec_extract(vec_sums(vec_int4_c(sum4), vec_int4_c(zero4)), 3); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + const vec_int4 zero4 = vec_int4_z; + vec_char16 ad = vec_abss(vec_subs(a.val, b.val)); + vec_int4 sum4 = vec_sum4s(ad, zero4); + return (unsigned)vec_extract(vec_sums(sum4, zero4), 3); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + vec_ushort8 ad = vec_absd(a.val, b.val); + VSX_UNUSED(vec_int4) sum = vec_sums(vec_int4_c(vec_unpackhu(ad)) + vec_int4_c(vec_unpacklu(ad)), vec_int4_z); + return (unsigned)vec_extract(sum, 3); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + const vec_int4 zero4 = vec_int4_z; + vec_short8 ad = vec_abss(vec_subs(a.val, b.val)); + vec_int4 sum4 = vec_sum4s(ad, zero4); + return (unsigned)vec_extract(vec_sums(sum4, zero4), 3); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + const vec_uint4 ad = vec_absd(a.val, b.val); + const vec_uint4 rd = vec_add(ad, vec_sld(ad, ad, 8)); + return vec_extract(vec_add(rd, vec_sld(rd, rd, 4)), 0); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + vec_int4 ad = vec_abss(vec_sub(a.val, b.val)); + return (unsigned)vec_extract(vec_sums(ad, vec_int4_z), 3); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + const vec_float4 ad = vec_abs(vec_sub(a.val, b.val)); + const vec_float4 rd = vec_add(ad, vec_sld(ad, ad, 8)); + return vec_extract(vec_add(rd, vec_sld(rd, rd, 4)), 0); +} + +/** Popcount **/ +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ return v_uint8x16(vec_popcntu(a.val)); } +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_uint8x16(vec_popcntu(a.val)); } +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ return v_uint16x8(vec_popcntu(a.val)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_uint16x8(vec_popcntu(a.val)); } +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ return v_uint32x4(vec_popcntu(a.val)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_uint32x4(vec_popcntu(a.val)); } +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ return v_uint64x2(vec_popcntu(a.val)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_uint64x2(vec_popcntu(a.val)); } + +/** Mask **/ +inline int v_signmask(const v_uint8x16& a) +{ + static const vec_uchar16 qperm = {120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_int8x16& a) +{ return v_signmask(v_reinterpret_as_u8(a)); } + +inline int v_signmask(const v_int16x8& a) +{ + static const vec_uchar16 qperm = {112, 96, 80, 64, 48, 32, 16, 0, 128, 128, 128, 128, 128, 128, 128, 128}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_uint16x8& a) +{ return v_signmask(v_reinterpret_as_s16(a)); } + +inline int v_signmask(const v_int32x4& a) +{ + static const vec_uchar16 qperm = {96, 64, 32, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}; + return vec_extract((vec_int4)vec_vbpermq(v_reinterpret_as_u8(a).val, qperm), 2); +} +inline int v_signmask(const v_uint32x4& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } +inline int v_signmask(const v_float32x4& a) +{ return v_signmask(v_reinterpret_as_s32(a)); } + +inline int v_signmask(const v_int64x2& a) +{ + VSX_UNUSED(const vec_dword2) sv = vec_sr(a.val, vec_udword2_sp(63)); + return (int)vec_extract(sv, 0) | (int)vec_extract(sv, 1) << 1; +} +inline int v_signmask(const v_uint64x2& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } +inline int v_signmask(const v_float64x2& a) +{ return v_signmask(v_reinterpret_as_s64(a)); } + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(a)); } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(a)); } + +template +inline bool v_check_all(const _Tpvec& a) +{ return vec_all_lt(a.val, _Tpvec::zero().val); } +inline bool v_check_all(const v_uint8x16& a) +{ return v_check_all(v_reinterpret_as_s8(a)); } +inline bool v_check_all(const v_uint16x8& a) +{ return v_check_all(v_reinterpret_as_s16(a)); } +inline bool v_check_all(const v_uint32x4& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_uint64x2& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } +inline bool v_check_all(const v_float32x4& a) +{ return v_check_all(v_reinterpret_as_s32(a)); } +inline bool v_check_all(const v_float64x2& a) +{ return v_check_all(v_reinterpret_as_s64(a)); } + +template +inline bool v_check_any(const _Tpvec& a) +{ return vec_any_lt(a.val, _Tpvec::zero().val); } +inline bool v_check_any(const v_uint8x16& a) +{ return v_check_any(v_reinterpret_as_s8(a)); } +inline bool v_check_any(const v_uint16x8& a) +{ return v_check_any(v_reinterpret_as_s16(a)); } +inline bool v_check_any(const v_uint32x4& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_uint64x2& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } +inline bool v_check_any(const v_float32x4& a) +{ return v_check_any(v_reinterpret_as_s32(a)); } +inline bool v_check_any(const v_float64x2& a) +{ return v_check_any(v_reinterpret_as_s64(a)); } + +////////// Other math ///////// + +/** Some frequent operations **/ +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ return v_float32x4(vec_sqrt(x.val)); } +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ return v_float64x2(vec_sqrt(x.val)); } + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ return v_float32x4(vec_rsqrt(x.val)); } +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ return v_float64x2(vec_rsqrt(x.val)); } + +#define OPENCV_HAL_IMPL_VSX_MULADD(_Tpvec) \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_sqrt(vec_madd(a.val, a.val, vec_mul(b.val, b.val)))); } \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(vec_madd(a.val, a.val, vec_mul(b.val, b.val))); } \ +inline _Tpvec v_fma(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ return _Tpvec(vec_madd(a.val, b.val, c.val)); } \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ return _Tpvec(vec_madd(a.val, b.val, c.val)); } + +OPENCV_HAL_IMPL_VSX_MULADD(v_float32x4) +OPENCV_HAL_IMPL_VSX_MULADD(v_float64x2) + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ return a * b + c; } + +// TODO: exp, log, sin, cos + +/** Absolute values **/ +inline v_uint8x16 v_abs(const v_int8x16& x) +{ return v_uint8x16(vec_uchar16_c(vec_abs(x.val))); } + +inline v_uint16x8 v_abs(const v_int16x8& x) +{ return v_uint16x8(vec_ushort8_c(vec_abs(x.val))); } + +inline v_uint32x4 v_abs(const v_int32x4& x) +{ return v_uint32x4(vec_uint4_c(vec_abs(x.val))); } + +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(vec_abs(x.val)); } + +inline v_float64x2 v_abs(const v_float64x2& x) +{ return v_float64x2(vec_abs(x.val)); } + +/** Absolute difference **/ +// unsigned +OPENCV_HAL_IMPL_VSX_BIN_FUNC(v_absdiff, vec_absd) + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ return v_reinterpret_as_u8(v_sub_wrap(v_max(a, b), v_min(a, b))); } +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); } +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ return v_reinterpret_as_u32(v_max(a, b) - v_min(a, b)); } + +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ return v_abs(a - b); } +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ return v_abs(a - b); } + +/** Absolute difference for signed integers **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ return v_int8x16(vec_abss(vec_subs(a.val, b.val))); } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_int16x8(vec_abss(vec_subs(a.val, b.val))); } + +////////// Conversions ///////// + +/** Rounding **/ +inline v_int32x4 v_round(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_rint(a.val))); } + +inline v_int32x4 v_round(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_rint(a.val)), vec_int4_z)); } + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_rint(a.val)), vec_ctso(vec_rint(b.val)))); } + +inline v_int32x4 v_floor(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_floor(a.val))); } + +inline v_int32x4 v_floor(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_floor(a.val)), vec_int4_z)); } + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ return v_int32x4(vec_cts(vec_ceil(a.val))); } + +inline v_int32x4 v_ceil(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(vec_ceil(a.val)), vec_int4_z)); } + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(vec_cts(a.val)); } + +inline v_int32x4 v_trunc(const v_float64x2& a) +{ return v_int32x4(vec_mergesqo(vec_ctso(a.val), vec_int4_z)); } + +/** To float **/ +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ return v_float32x4(vec_ctf(a.val)); } + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ return v_float32x4(vec_mergesqo(vec_cvfo(a.val), vec_float4_z)); } + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ return v_float32x4(vec_mergesqo(vec_cvfo(a.val), vec_cvfo(b.val))); } + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ return v_float64x2(vec_ctdo(vec_mergeh(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ return v_float64x2(vec_ctdo(vec_mergel(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ return v_float64x2(vec_cvfo(vec_mergeh(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ return v_float64x2(vec_cvfo(vec_mergel(a.val, a.val))); } + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ return v_float64x2(vec_ctd(a.val)); } + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + return v_reinterpret_as_s8(v_int16x8(*(const short*)(tab+idx[0]), *(const short*)(tab+idx[1]), *(const short*)(tab+idx[2]), *(const short*)(tab+idx[3]), + *(const short*)(tab+idx[4]), *(const short*)(tab+idx[5]), *(const short*)(tab+idx[6]), *(const short*)(tab+idx[7]))); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + return v_reinterpret_as_s8(v_int32x4(*(const int*)(tab+idx[0]), *(const int*)(tab+idx[1]), *(const int*)(tab+idx[2]), *(const int*)(tab+idx[3]))); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar*)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar*)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + return v_reinterpret_as_s16(v_int32x4(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]), *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_reinterpret_as_s16(v_int64x2(*(const int64*)(tab + idx[0]), *(const int64*)(tab + idx[1]))); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short*)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short*)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short*)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_reinterpret_as_s32(v_int64x2(*(const int64*)(tab + idx[0]), *(const int64*)(tab + idx[1]))); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(vsx_ld(0, tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int*)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int*)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int*)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(vsx_ld2(0, tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int*)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_load(tab + *idx); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_load(tab + *idx); } + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_uint32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + const int idx[4] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1), + vec_extract(idxvec.val, 2), + vec_extract(idxvec.val, 3) + }; + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + const int idx[2] = { + vec_extract(idxvec.val, 0), + vec_extract(idxvec.val, 1) + }; + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} + +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + vec_float4 xy0 = vec_ld_l8(tab + vec_extract(idxvec.val, 0)); + vec_float4 xy1 = vec_ld_l8(tab + vec_extract(idxvec.val, 1)); + vec_float4 xy2 = vec_ld_l8(tab + vec_extract(idxvec.val, 2)); + vec_float4 xy3 = vec_ld_l8(tab + vec_extract(idxvec.val, 3)); + vec_float4 xy02 = vec_mergeh(xy0, xy2); // x0, x2, y0, y2 + vec_float4 xy13 = vec_mergeh(xy1, xy3); // x1, x3, y1, y3 + x.val = vec_mergeh(xy02, xy13); + y.val = vec_mergel(xy02, xy13); +} +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + vec_double2 xy0 = vsx_ld(vec_extract(idxvec.val, 0), tab); + vec_double2 xy1 = vsx_ld(vec_extract(idxvec.val, 1), tab); + x.val = vec_mergeh(xy0, xy1); + y.val = vec_mergel(xy0, xy1); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11, 12, 14, 13, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } + +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 4, 1, 5, 2, 6, 3, 7, 8, 12, 9, 13, 10, 14, 11, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 4,5, 2,3, 6,7, 8,9, 12,13, 10,11, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } + +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 8,9, 2,3, 10,11, 4,5, 12,13, 6,7, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + static const vec_uchar16 perm = {0,1,2,3, 8,9,10,11, 4,5,6,7, 12,13,14,15}; + return v_int32x4(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) +{ return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) +{ return v_reinterpret_as_f32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + static const vec_uchar16 perm = {0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 15, 15, 15}; + return v_int8x16(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) +{ return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + static const vec_uchar16 perm = {0,1, 2,3, 4,5, 8,9, 10,11, 12,13, 14,15, 14,15}; + return v_int16x8(vec_perm(vec.val, vec.val, perm)); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) +{ return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) +{ return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) +{ return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) +{ return vec; } + +/////// FP16 support //////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + vec_ushort8 vf16 = vec_ld_l8((const ushort*)ptr); +#if CV_VSX3 && defined(vec_extract_fp_from_shorth) + return v_float32x4(vec_extract_fp_from_shorth(vf16)); +#elif CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM) + vec_float4 vf32; + __asm__ __volatile__ ("xvcvhpsp %x0,%x1" : "=wa" (vf32) : "wa" (vec_mergeh(vf16, vf16))); + return v_float32x4(vf32); +#else + const vec_int4 z = vec_int4_z, delta = vec_int4_sp(0x38000000); + const vec_int4 signmask = vec_int4_sp(0x80000000); + const vec_int4 maxexp = vec_int4_sp(0x7c000000); + const vec_float4 deltaf = vec_float4_c(vec_int4_sp(0x38800000)); + + vec_int4 bits = vec_int4_c(vec_mergeh(vec_short8_c(z), vec_short8_c(vf16))); + vec_int4 e = vec_and(bits, maxexp), sign = vec_and(bits, signmask); + vec_int4 t = vec_add(vec_sr(vec_xor(bits, sign), vec_uint4_sp(3)), delta); // ((h & 0x7fff) << 13) + delta + vec_int4 zt = vec_int4_c(vec_sub(vec_float4_c(vec_add(t, vec_int4_sp(1 << 23))), deltaf)); + + t = vec_add(t, vec_and(delta, vec_cmpeq(maxexp, e))); + vec_bint4 zmask = vec_cmpeq(e, z); + vec_int4 ft = vec_sel(t, zt, zmask); + return v_float32x4(vec_float4_c(vec_or(ft, sign))); +#endif +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ +// fixme: Is there any builtin op or intrinsic that cover "xvcvsphp"? +#if CV_VSX3 && !defined(CV_COMPILER_VSX_BROKEN_ASM) + vec_ushort8 vf16; + __asm__ __volatile__ ("xvcvsphp %x0,%x1" : "=wa" (vf16) : "wa" (v.val)); + vec_st_l8(vec_mergesqe(vf16, vf16), ptr); +#else + const vec_int4 signmask = vec_int4_sp(0x80000000); + const vec_int4 rval = vec_int4_sp(0x3f000000); + + vec_int4 t = vec_int4_c(v.val); + vec_int4 sign = vec_sra(vec_and(t, signmask), vec_uint4_sp(16)); + t = vec_and(vec_nor(signmask, signmask), t); + + vec_bint4 finitemask = vec_cmpgt(vec_int4_sp(0x47800000), t); + vec_bint4 isnan = vec_cmpgt(t, vec_int4_sp(0x7f800000)); + vec_int4 naninf = vec_sel(vec_int4_sp(0x7c00), vec_int4_sp(0x7e00), isnan); + vec_bint4 tinymask = vec_cmpgt(vec_int4_sp(0x38800000), t); + vec_int4 tt = vec_int4_c(vec_add(vec_float4_c(t), vec_float4_c(rval))); + tt = vec_sub(tt, rval); + vec_int4 odd = vec_and(vec_sr(t, vec_uint4_sp(13)), vec_int4_sp(1)); + vec_int4 nt = vec_add(t, vec_int4_sp(0xc8000fff)); + nt = vec_sr(vec_add(nt, odd), vec_uint4_sp(13)); + t = vec_sel(nt, tt, tinymask); + t = vec_sel(naninf, t, finitemask); + t = vec_or(t, sign); + vec_st_l8(vec_packs(t, t), ptr); +#endif +} + +inline void v_cleanup() {} + + +/** Reinterpret **/ +/** its up there with load and store operations **/ + +////////// Matrix operations ///////// + +//////// Dot Product //////// +// 16 >> 32 +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ return v_int32x4(vec_msum(a.val, b.val, vec_int4_z)); } +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(vec_msum(a.val, b.val, c.val)); } + +// 32 >> 64 +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ + vec_dword2 even = vec_mule(a.val, b.val); + vec_dword2 odd = vec_mulo(a.val, b.val); + return v_int64x2(vec_add(even, odd)); +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b) + c; } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_uint32x4(vec_msum(a.val, b.val, c.val)); } +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ return v_uint32x4(vec_msum(a.val, b.val, vec_uint4_z)); } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + const vec_ushort8 eight = vec_ushort8_sp(8); + vec_short8 a0 = vec_sra((vec_short8)vec_sld(a.val, a.val, 1), eight); // even + vec_short8 a1 = vec_sra((vec_short8)a.val, eight); // odd + vec_short8 b0 = vec_sra((vec_short8)vec_sld(b.val, b.val, 1), eight); + vec_short8 b1 = vec_sra((vec_short8)b.val, eight); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, vec_int4_z))); +} + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ + const vec_ushort8 eight = vec_ushort8_sp(8); + vec_short8 a0 = vec_sra((vec_short8)vec_sld(a.val, a.val, 1), eight); // even + vec_short8 a1 = vec_sra((vec_short8)a.val, eight); // odd + vec_short8 b0 = vec_sra((vec_short8)vec_sld(b.val, b.val, 1), eight); + vec_short8 b1 = vec_sra((vec_short8)b.val, eight); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, c.val))); +} + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + const vec_uint4 zero = vec_uint4_z; + vec_uint4 even = vec_mule(a.val, b.val); + vec_uint4 odd = vec_mulo(a.val, b.val); + vec_udword2 e0 = (vec_udword2)vec_mergee(even, zero); + vec_udword2 e1 = (vec_udword2)vec_mergeo(even, zero); + vec_udword2 o0 = (vec_udword2)vec_mergee(odd, zero); + vec_udword2 o1 = (vec_udword2)vec_mergeo(odd, zero); + vec_udword2 s0 = vec_add(e0, o0); + vec_udword2 s1 = vec_add(e1, o1); + return v_uint64x2(vec_add(s0, s1)); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return v_int64x2(vec_add(vec_mergeh(c.val, d.val), vec_mergel(c.val, d.val))); +} +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_int32x4(vec_msum(a.val, b.val, vec_int4_z)) + c; } +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_uint32x4(vec_msum(a.val, b.val, vec_uint4_z)) + c; } + +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ + vec_short8 a0 = vec_unpackh(a.val); + vec_short8 a1 = vec_unpackl(a.val); + vec_short8 b0 = vec_unpackh(b.val); + vec_short8 b1 = vec_unpackl(b.val); + return v_int32x4(vec_msum(a0, b0, vec_msum(a1, b1, vec_int4_z))); +} +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 prod = v_dotprod(a, b); + v_int64x2 c, d; + v_expand(prod, c, d); + return c + d; +} +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand_fast(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + const vec_float4 v0 = vec_splat(v.val, 0); + const vec_float4 v1 = vec_splat(v.val, 1); + const vec_float4 v2 = vec_splat(v.val, 2); + VSX_UNUSED(const vec_float4) v3 = vec_splat(v.val, 3); + return v_float32x4(vec_madd(v0, m0.val, vec_madd(v1, m1.val, vec_madd(v2, m2.val, vec_mul(v3, m3.val))))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + const vec_float4 v0 = vec_splat(v.val, 0); + const vec_float4 v1 = vec_splat(v.val, 1); + const vec_float4 v2 = vec_splat(v.val, 2); + return v_float32x4(vec_madd(v0, m0.val, vec_madd(v1, m1.val, vec_madd(v2, m2.val, a.val)))); +} + +#define OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(_Tpvec, _Tpvec2) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, _Tpvec& b2, _Tpvec& b3) \ +{ \ + _Tpvec2 a02 = vec_mergeh(a0.val, a2.val); \ + _Tpvec2 a13 = vec_mergeh(a1.val, a3.val); \ + b0.val = vec_mergeh(a02, a13); \ + b1.val = vec_mergel(a02, a13); \ + a02 = vec_mergel(a0.val, a2.val); \ + a13 = vec_mergel(a1.val, a3.val); \ + b2.val = vec_mergeh(a02, a13); \ + b3.val = vec_mergel(a02, a13); \ +} +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_uint32x4, vec_uint4) +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_int32x4, vec_int4) +OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_float32x4, vec_float4) + +template +inline Tvec v_broadcast_element(const Tvec& v) +{ return Tvec(vec_splat(v.val, i)); } + + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif // OPENCV_HAL_VSX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_wasm.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_wasm.hpp new file mode 100644 index 0000000..b4178af --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/intrin_wasm.hpp @@ -0,0 +1,2782 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_HAL_INTRIN_WASM_HPP +#define OPENCV_HAL_INTRIN_WASM_HPP + +#include +#include +#include +#include "opencv2/core/saturate.hpp" + +#define CV_SIMD128 1 +#define CV_SIMD128_64F 0 // Now all implementation of f64 use fallback, so disable it. +#define CV_SIMD128_FP16 0 + +namespace cv +{ + +//! @cond IGNORED + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN + +#if (__EMSCRIPTEN_major__ * 1000000 + __EMSCRIPTEN_minor__ * 1000 + __EMSCRIPTEN_tiny__) < (1038046) +// handle renames: https://github.com/emscripten-core/emscripten/pull/9440 (https://github.com/emscripten-core/emscripten/commit/755d5b46cb84d0aa120c10981b11d05646c29673) +#define wasm_i32x4_trunc_saturate_f32x4 wasm_trunc_saturate_i32x4_f32x4 +#define wasm_u32x4_trunc_saturate_f32x4 wasm_trunc_saturate_u32x4_f32x4 +#define wasm_i64x2_trunc_saturate_f64x2 wasm_trunc_saturate_i64x2_f64x2 +#define wasm_u64x2_trunc_saturate_f64x2 wasm_trunc_saturate_u64x2_f64x2 +#define wasm_f32x4_convert_i32x4 wasm_convert_f32x4_i32x4 +#define wasm_f32x4_convert_u32x4 wasm_convert_f32x4_u32x4 +#define wasm_f64x2_convert_i64x2 wasm_convert_f64x2_i64x2 +#define wasm_f64x2_convert_u64x2 wasm_convert_f64x2_u64x2 +#endif // COMPATIBILITY: <1.38.46 + +///////// Types /////////// + +struct v_uint8x16 +{ + typedef uchar lane_type; + typedef v128_t vector_type; + enum { nlanes = 16 }; + + v_uint8x16() {} + explicit v_uint8x16(v128_t v) : val(v) {} + v_uint8x16(uchar v0, uchar v1, uchar v2, uchar v3, uchar v4, uchar v5, uchar v6, uchar v7, + uchar v8, uchar v9, uchar v10, uchar v11, uchar v12, uchar v13, uchar v14, uchar v15) + { + uchar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = wasm_v128_load(v); + } + + uchar get0() const + { + return (uchar)wasm_i8x16_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_int8x16 +{ + typedef schar lane_type; + typedef v128_t vector_type; + enum { nlanes = 16 }; + + v_int8x16() {} + explicit v_int8x16(v128_t v) : val(v) {} + v_int8x16(schar v0, schar v1, schar v2, schar v3, schar v4, schar v5, schar v6, schar v7, + schar v8, schar v9, schar v10, schar v11, schar v12, schar v13, schar v14, schar v15) + { + schar v[] = {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15}; + val = wasm_v128_load(v); + } + + schar get0() const + { + return wasm_i8x16_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint16x8 +{ + typedef ushort lane_type; + typedef v128_t vector_type; + enum { nlanes = 8 }; + + v_uint16x8() {} + explicit v_uint16x8(v128_t v) : val(v) {} + v_uint16x8(ushort v0, ushort v1, ushort v2, ushort v3, ushort v4, ushort v5, ushort v6, ushort v7) + { + ushort v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = wasm_v128_load(v); + } + + ushort get0() const + { + return (ushort)wasm_i16x8_extract_lane(val, 0); // wasm_u16x8_extract_lane() unimplemented yet + } + + v128_t val; +}; + +struct v_int16x8 +{ + typedef short lane_type; + typedef v128_t vector_type; + enum { nlanes = 8 }; + + v_int16x8() {} + explicit v_int16x8(v128_t v) : val(v) {} + v_int16x8(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7) + { + short v[] = {v0, v1, v2, v3, v4, v5, v6, v7}; + val = wasm_v128_load(v); + } + + short get0() const + { + return wasm_i16x8_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint32x4 +{ + typedef unsigned lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_uint32x4() {} + explicit v_uint32x4(v128_t v) : val(v) {} + v_uint32x4(unsigned v0, unsigned v1, unsigned v2, unsigned v3) + { + unsigned v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + + unsigned get0() const + { + return (unsigned)wasm_i32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_int32x4 +{ + typedef int lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_int32x4() {} + explicit v_int32x4(v128_t v) : val(v) {} + v_int32x4(int v0, int v1, int v2, int v3) + { + int v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + + int get0() const + { + return wasm_i32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_float32x4 +{ + typedef float lane_type; + typedef v128_t vector_type; + enum { nlanes = 4 }; + + v_float32x4() {} + explicit v_float32x4(v128_t v) : val(v) {} + v_float32x4(float v0, float v1, float v2, float v3) + { + float v[] = {v0, v1, v2, v3}; + val = wasm_v128_load(v); + } + + float get0() const + { + return wasm_f32x4_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_uint64x2 +{ + typedef uint64 lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + + v_uint64x2() {} + explicit v_uint64x2(v128_t v) : val(v) {} + v_uint64x2(uint64 v0, uint64 v1) + { + uint64 v[] = {v0, v1}; + val = wasm_v128_load(v); + } + + uint64 get0() const + { + return (uint64)wasm_i64x2_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_int64x2 +{ + typedef int64 lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + + v_int64x2() {} + explicit v_int64x2(v128_t v) : val(v) {} + v_int64x2(int64 v0, int64 v1) + { + int64 v[] = {v0, v1}; + val = wasm_v128_load(v); + } + + int64 get0() const + { + return wasm_i64x2_extract_lane(val, 0); + } + + v128_t val; +}; + +struct v_float64x2 +{ + typedef double lane_type; + typedef v128_t vector_type; + enum { nlanes = 2 }; + + v_float64x2() {} + explicit v_float64x2(v128_t v) : val(v) {} + v_float64x2(double v0, double v1) + { + double v[] = {v0, v1}; + val = wasm_v128_load(v); + } + + double get0() const + { + return wasm_f64x2_extract_lane(val, 0); + } + + v128_t val; +}; + +namespace +{ +#define OPENCV_HAL_IMPL_REINTERPRET_INT(ft, tt) \ +inline tt reinterpret_int(ft x) { union { ft l; tt i; } v; v.l = x; return v.i; } +OPENCV_HAL_IMPL_REINTERPRET_INT(uchar, schar) +OPENCV_HAL_IMPL_REINTERPRET_INT(schar, schar) +OPENCV_HAL_IMPL_REINTERPRET_INT(ushort, short) +OPENCV_HAL_IMPL_REINTERPRET_INT(short, short) +OPENCV_HAL_IMPL_REINTERPRET_INT(unsigned, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(int, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(float, int) +OPENCV_HAL_IMPL_REINTERPRET_INT(uint64, int64) +OPENCV_HAL_IMPL_REINTERPRET_INT(int64, int64) +OPENCV_HAL_IMPL_REINTERPRET_INT(double, int64) + +static const unsigned char popCountTable[] = +{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; +} // namespace + +static v128_t wasm_unpacklo_i8x16(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,16,1,17,2,18,3,19,4,20,5,21,6,22,7,23); +} + +static v128_t wasm_unpacklo_i16x8(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,16,17,2,3,18,19,4,5,20,21,6,7,22,23); +} + +static v128_t wasm_unpacklo_i32x4(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,2,3,16,17,18,19,4,5,6,7,20,21,22,23); +} + +static v128_t wasm_unpacklo_i64x2(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); +} + +static v128_t wasm_unpackhi_i8x16(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,24,9,25,10,26,11,27,12,28,13,29,14,30,15,31); +} + +static v128_t wasm_unpackhi_i16x8(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,24,25,10,11,26,27,12,13,28,29,14,15,30,31); +} + +static v128_t wasm_unpackhi_i32x4(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,10,11,24,25,26,27,12,13,14,15,28,29,30,31); +} + +static v128_t wasm_unpackhi_i64x2(v128_t a, v128_t b) { + return wasm_v8x16_shuffle(a, b, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +/** Convert **/ +// 8 >> 16 +inline v128_t v128_cvtu8x16_i16x8(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i8x16(a, z); +} +inline v128_t v128_cvti8x16_i16x8(const v128_t& a) +{ return wasm_i16x8_shr(wasm_unpacklo_i8x16(a, a), 8); } +// 8 >> 32 +inline v128_t v128_cvtu8x16_i32x4(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i16x8(wasm_unpacklo_i8x16(a, z), z); +} +inline v128_t v128_cvti8x16_i32x4(const v128_t& a) +{ + v128_t r = wasm_unpacklo_i8x16(a, a); + r = wasm_unpacklo_i8x16(r, r); + return wasm_i32x4_shr(r, 24); +} +// 16 >> 32 +inline v128_t v128_cvtu16x8_i32x4(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i16x8(a, z); +} +inline v128_t v128_cvti16x8_i32x4(const v128_t& a) +{ return wasm_i32x4_shr(wasm_unpacklo_i16x8(a, a), 16); } +// 32 >> 64 +inline v128_t v128_cvtu32x4_i64x2(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpacklo_i32x4(a, z); +} +inline v128_t v128_cvti32x4_i64x2(const v128_t& a) +{ return wasm_unpacklo_i32x4(a, wasm_i32x4_shr(a, 31)); } + +// 16 << 8 +inline v128_t v128_cvtu8x16_i16x8_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i8x16(a, z); +} +inline v128_t v128_cvti8x16_i16x8_high(const v128_t& a) +{ return wasm_i16x8_shr(wasm_unpackhi_i8x16(a, a), 8); } +// 32 << 16 +inline v128_t v128_cvtu16x8_i32x4_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i16x8(a, z); +} +inline v128_t v128_cvti16x8_i32x4_high(const v128_t& a) +{ return wasm_i32x4_shr(wasm_unpackhi_i16x8(a, a), 16); } +// 64 << 32 +inline v128_t v128_cvtu32x4_i64x2_high(const v128_t& a) +{ + const v128_t z = wasm_i8x16_splat(0); + return wasm_unpackhi_i32x4(a, z); +} +inline v128_t v128_cvti32x4_i64x2_high(const v128_t& a) +{ return wasm_unpackhi_i32x4(a, wasm_i32x4_shr(a, 31)); } + +#define OPENCV_HAL_IMPL_WASM_INITVEC(_Tpvec, _Tp, suffix, zsuffix, _Tps) \ +inline _Tpvec v_setzero_##suffix() { return _Tpvec(wasm_##zsuffix##_splat((_Tps)0)); } \ +inline _Tpvec v_setall_##suffix(_Tp v) { return _Tpvec(wasm_##zsuffix##_splat((_Tps)v)); } \ +template inline _Tpvec v_reinterpret_as_##suffix(const _Tpvec0& a) \ +{ return _Tpvec(a.val); } + +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint8x16, uchar, u8, i8x16, schar) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int8x16, schar, s8, i8x16, schar) +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint16x8, ushort, u16, i16x8, short) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int16x8, short, s16, i16x8, short) +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint32x4, unsigned, u32, i32x4, int) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int32x4, int, s32, i32x4, int) +OPENCV_HAL_IMPL_WASM_INITVEC(v_float32x4, float, f32, f32x4, float) +OPENCV_HAL_IMPL_WASM_INITVEC(v_uint64x2, uint64, u64, i64x2, int64) +OPENCV_HAL_IMPL_WASM_INITVEC(v_int64x2, int64, s64, i64x2, int64) +OPENCV_HAL_IMPL_WASM_INITVEC(v_float64x2, double, f64, f64x2, double) + +//////////////// PACK /////////////// +inline v_uint8x16 v_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u16x8_gt(b.val, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a1, b1, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_int8x16 v_pack(const v_int16x8& a, const v_int16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i16x8_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i16x8_lt(b1, minval)); + return v_int8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_uint16x8 v_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u32x4_gt(b.val, maxval)); + return v_uint16x8(wasm_v8x16_shuffle(a1, b1, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +inline v_int16x8 v_pack(const v_int32x4& a, const v_int32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i32x4_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i32x4_lt(b1, minval)); + return v_int16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +inline v_uint32x4 v_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + return v_uint32x4(wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +inline v_int32x4 v_pack(const v_int64x2& a, const v_int64x2& b) +{ + return v_int32x4(wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +inline v_uint8x16 v_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i16x8_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i16x8_lt(b1, minval)); + return v_uint8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +inline v_uint16x8 v_pack_u(const v_int32x4& a, const v_int32x4& b) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_i32x4_gt(b.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t b2 = wasm_v128_bitselect(minval, b1, wasm_i32x4_lt(b1, minval)); + return v_uint16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} + +template +inline v_uint8x16 v_rshr_pack(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_u16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_u16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_u16x8_gt(b1, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a2, b2, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_int8x16 v_rshr_pack(const v_int16x8& a, const v_int16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_i16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i16x8_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i16x8_lt(b1, minval)); + return v_int8x16(wasm_v8x16_shuffle(a3, b3, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_uint16x8 v_rshr_pack(const v_uint32x4& a, const v_uint32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_u32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_u32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_u32x4_gt(b1, maxval)); + return v_uint16x8(wasm_v8x16_shuffle(a2, b2, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +template +inline v_int16x8 v_rshr_pack(const v_int32x4& a, const v_int32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_i32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i16x8_splat(-32768); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i32x4_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i32x4_lt(b1, minval)); + return v_int16x8(wasm_v8x16_shuffle(a3, b3, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} +template +inline v_uint32x4 v_rshr_pack(const v_uint64x2& a, const v_uint64x2& b) +{ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_u64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t b1 = wasm_u64x2_shr(wasm_i64x2_add(b.val, delta), n); + return v_uint32x4(wasm_v8x16_shuffle(a1, b1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +template +inline v_int32x4 v_rshr_pack(const v_int64x2& a, const v_int64x2& b) +{ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_i64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t b1 = wasm_i64x2_shr(wasm_i64x2_add(b.val, delta), n); + return v_int32x4(wasm_v8x16_shuffle(a1, b1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27)); +} +template +inline v_uint8x16 v_rshr_pack_u(const v_int16x8& a, const v_int16x8& b) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t b1 = wasm_i16x8_shr(wasm_i16x8_add(b.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i16x8_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i16x8_lt(b1, minval)); + return v_uint8x16(wasm_v8x16_shuffle(a3, b3, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} +template +inline v_uint16x8 v_rshr_pack_u(const v_int32x4& a, const v_int32x4& b) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t b1 = wasm_i32x4_shr(wasm_i32x4_add(b.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t b2 = wasm_v128_bitselect(maxval, b1, wasm_i32x4_gt(b1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t b3 = wasm_v128_bitselect(minval, b2, wasm_i32x4_lt(b1, minval)); + return v_uint16x8(wasm_v8x16_shuffle(a3, b3, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29)); +} + +inline void v_pack_store(uchar* ptr, const v_uint16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(schar* ptr, const v_int16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + schar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(ushort* ptr, const v_uint32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(short* ptr, const v_int32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + short t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + v128_t r = wasm_v8x16_shuffle(a.val, a.val, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + unsigned t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_store(int* ptr, const v_int64x2& a) +{ + v128_t r = wasm_v8x16_shuffle(a.val, a.val, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + int t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i16x8_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +inline void v_pack_u_store(ushort* ptr, const v_int32x4& a) +{ + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_i32x4_gt(a.val, maxval)); + v128_t a2 = wasm_v128_bitselect(minval, a1, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} + +template +inline void v_rshr_pack_store(uchar* ptr, const v_uint16x8& a) +{ + v128_t delta = wasm_i16x8_splat((short)(1 << (n-1))); + v128_t a1 = wasm_u16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u16x8_gt(a1, maxval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(schar* ptr, const v_int16x8& a) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(127); + v128_t minval = wasm_i16x8_splat(-128); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + schar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(ushort* ptr, const v_uint32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_u32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_u32x4_gt(a1, maxval)); + v128_t r = wasm_v8x16_shuffle(a2, a2, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(short* ptr, const v_int32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(32767); + v128_t minval = wasm_i32x4_splat(-32768); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + short t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(unsigned* ptr, const v_uint64x2& a) +{ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_u64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + unsigned t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_store(int* ptr, const v_int64x2& a) +{ + v128_t delta = wasm_i64x2_splat(((int64)1 << (n-1))); + v128_t a1 = wasm_i64x2_shr(wasm_i64x2_add(a.val, delta), n); + v128_t r = wasm_v8x16_shuffle(a1, a1, 0,1,2,3,8,9,10,11,0,1,2,3,8,9,10,11); + int t_ptr[4]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<2; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_u_store(uchar* ptr, const v_int16x8& a) +{ + v128_t delta = wasm_i16x8_splat(((short)1 << (n-1))); + v128_t a1 = wasm_i16x8_shr(wasm_i16x8_add(a.val, delta), n); + v128_t maxval = wasm_i16x8_splat(255); + v128_t minval = wasm_i16x8_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i16x8_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i16x8_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14); + uchar t_ptr[16]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<8; ++i) { + ptr[i] = t_ptr[i]; + } +} +template +inline void v_rshr_pack_u_store(ushort* ptr, const v_int32x4& a) +{ + v128_t delta = wasm_i32x4_splat(((int)1 << (n-1))); + v128_t a1 = wasm_i32x4_shr(wasm_i32x4_add(a.val, delta), n); + v128_t maxval = wasm_i32x4_splat(65535); + v128_t minval = wasm_i32x4_splat(0); + v128_t a2 = wasm_v128_bitselect(maxval, a1, wasm_i32x4_gt(a1, maxval)); + v128_t a3 = wasm_v128_bitselect(minval, a2, wasm_i32x4_lt(a1, minval)); + v128_t r = wasm_v8x16_shuffle(a3, a3, 0,1,4,5,8,9,12,13,0,1,4,5,8,9,12,13); + ushort t_ptr[8]; + wasm_v128_store(t_ptr, r); + for (int i=0; i<4; ++i) { + ptr[i] = t_ptr[i]; + } +} + +inline v_uint8x16 v_pack_b(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t maxval = wasm_i16x8_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u16x8_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u16x8_gt(b.val, maxval)); + return v_uint8x16(wasm_v8x16_shuffle(a1, b1, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)); +} + +inline v_uint8x16 v_pack_b(const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d) +{ + v128_t maxval = wasm_i32x4_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, wasm_u32x4_gt(a.val, maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, wasm_u32x4_gt(b.val, maxval)); + v128_t c1 = wasm_v128_bitselect(maxval, c.val, wasm_u32x4_gt(c.val, maxval)); + v128_t d1 = wasm_v128_bitselect(maxval, d.val, wasm_u32x4_gt(d.val, maxval)); + v128_t ab = wasm_v8x16_shuffle(a1, b1, 0,4,8,12,16,20,24,28,0,4,8,12,16,20,24,28); + v128_t cd = wasm_v8x16_shuffle(c1, d1, 0,4,8,12,16,20,24,28,0,4,8,12,16,20,24,28); + return v_uint8x16(wasm_v8x16_shuffle(ab, cd, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23)); +} + +inline v_uint8x16 v_pack_b(const v_uint64x2& a, const v_uint64x2& b, const v_uint64x2& c, + const v_uint64x2& d, const v_uint64x2& e, const v_uint64x2& f, + const v_uint64x2& g, const v_uint64x2& h) +{ + v128_t maxval = wasm_i32x4_splat(255); + v128_t a1 = wasm_v128_bitselect(maxval, a.val, ((__u64x2)(a.val) > (__u64x2)maxval)); + v128_t b1 = wasm_v128_bitselect(maxval, b.val, ((__u64x2)(b.val) > (__u64x2)maxval)); + v128_t c1 = wasm_v128_bitselect(maxval, c.val, ((__u64x2)(c.val) > (__u64x2)maxval)); + v128_t d1 = wasm_v128_bitselect(maxval, d.val, ((__u64x2)(d.val) > (__u64x2)maxval)); + v128_t e1 = wasm_v128_bitselect(maxval, e.val, ((__u64x2)(e.val) > (__u64x2)maxval)); + v128_t f1 = wasm_v128_bitselect(maxval, f.val, ((__u64x2)(f.val) > (__u64x2)maxval)); + v128_t g1 = wasm_v128_bitselect(maxval, g.val, ((__u64x2)(g.val) > (__u64x2)maxval)); + v128_t h1 = wasm_v128_bitselect(maxval, h.val, ((__u64x2)(h.val) > (__u64x2)maxval)); + v128_t ab = wasm_v8x16_shuffle(a1, b1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t cd = wasm_v8x16_shuffle(c1, d1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t ef = wasm_v8x16_shuffle(e1, f1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t gh = wasm_v8x16_shuffle(g1, h1, 0,8,16,24,0,8,16,24,0,8,16,24,0,8,16,24); + v128_t abcd = wasm_v8x16_shuffle(ab, cd, 0,1,2,3,16,17,18,19,0,1,2,3,16,17,18,19); + v128_t efgh = wasm_v8x16_shuffle(ef, gh, 0,1,2,3,16,17,18,19,0,1,2,3,16,17,18,19); + return v_uint8x16(wasm_v8x16_shuffle(abcd, efgh, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23)); +} + +inline v_float32x4 v_matmul(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& m3) +{ + v128_t v0 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 0)); + v128_t v1 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 1)); + v128_t v2 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 2)); + v128_t v3 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 3)); + v0 = wasm_f32x4_mul(v0, m0.val); + v1 = wasm_f32x4_mul(v1, m1.val); + v2 = wasm_f32x4_mul(v2, m2.val); + v3 = wasm_f32x4_mul(v3, m3.val); + + return v_float32x4(wasm_f32x4_add(wasm_f32x4_add(v0, v1), wasm_f32x4_add(v2, v3))); +} + +inline v_float32x4 v_matmuladd(const v_float32x4& v, const v_float32x4& m0, + const v_float32x4& m1, const v_float32x4& m2, + const v_float32x4& a) +{ + v128_t v0 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 0)); + v128_t v1 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 1)); + v128_t v2 = wasm_f32x4_splat(wasm_f32x4_extract_lane(v.val, 2)); + v0 = wasm_f32x4_mul(v0, m0.val); + v1 = wasm_f32x4_mul(v1, m1.val); + v2 = wasm_f32x4_mul(v2, m2.val); + + return v_float32x4(wasm_f32x4_add(wasm_f32x4_add(v0, v1), wasm_f32x4_add(v2, a.val))); +} + +#define OPENCV_HAL_IMPL_WASM_BIN_OP(bin_op, _Tpvec, intrin) \ +inline _Tpvec operator bin_op (const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} \ +inline _Tpvec& operator bin_op##= (_Tpvec& a, const _Tpvec& b) \ +{ \ + a.val = intrin(a.val, b.val); \ + return a; \ +} + +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint8x16, wasm_u8x16_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint8x16, wasm_u8x16_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int8x16, wasm_i8x16_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int8x16, wasm_i8x16_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint16x8, wasm_u16x8_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint16x8, wasm_u16x8_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int16x8, wasm_i16x8_add_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int16x8, wasm_i16x8_sub_saturate) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint32x4, wasm_i32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint32x4, wasm_i32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_uint32x4, wasm_i32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int32x4, wasm_i32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int32x4, wasm_i32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_int32x4, wasm_i32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_float32x4, wasm_f32x4_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_float32x4, wasm_f32x4_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_float32x4, wasm_f32x4_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(/, v_float32x4, wasm_f32x4_div) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_uint64x2, wasm_i64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_uint64x2, wasm_i64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_int64x2, wasm_i64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_int64x2, wasm_i64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(+, v_float64x2, wasm_f64x2_add) +OPENCV_HAL_IMPL_WASM_BIN_OP(-, v_float64x2, wasm_f64x2_sub) +OPENCV_HAL_IMPL_WASM_BIN_OP(*, v_float64x2, wasm_f64x2_mul) +OPENCV_HAL_IMPL_WASM_BIN_OP(/, v_float64x2, wasm_f64x2_div) + +// saturating multiply 8-bit, 16-bit +#define OPENCV_HAL_IMPL_WASM_MUL_SAT(_Tpvec, _Tpwvec) \ +inline _Tpvec operator * (const _Tpvec& a, const _Tpvec& b) \ +{ \ + _Tpwvec c, d; \ + v_mul_expand(a, b, c, d); \ + return v_pack(c, d); \ +} \ +inline _Tpvec& operator *= (_Tpvec& a, const _Tpvec& b) \ +{ a = a * b; return a; } + +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_uint8x16, v_uint16x8) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_int8x16, v_int16x8) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_uint16x8, v_uint32x4) +OPENCV_HAL_IMPL_WASM_MUL_SAT(v_int16x8, v_int32x4) + +// Multiply and expand +inline void v_mul_expand(const v_uint8x16& a, const v_uint8x16& b, + v_uint16x8& c, v_uint16x8& d) +{ + v_uint16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int8x16& a, const v_int8x16& b, + v_int16x8& c, v_int16x8& d) +{ + v_int16x8 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c = v_mul_wrap(a0, b0); + d = v_mul_wrap(a1, b1); +} + +inline void v_mul_expand(const v_int16x8& a, const v_int16x8& b, + v_int32x4& c, v_int32x4& d) +{ + v_int32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = wasm_i32x4_mul(a0.val, b0.val); + d.val = wasm_i32x4_mul(a1.val, b1.val); +} + +inline void v_mul_expand(const v_uint16x8& a, const v_uint16x8& b, + v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = wasm_i32x4_mul(a0.val, b0.val); + d.val = wasm_i32x4_mul(a1.val, b1.val); +} + +inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, + v_uint64x2& c, v_uint64x2& d) +{ + v_uint64x2 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + c.val = ((__u64x2)(a0.val) * (__u64x2)(b0.val)); + d.val = ((__u64x2)(a1.val) * (__u64x2)(b1.val)); +} + +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + v_int32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + v128_t c = wasm_i32x4_mul(a0.val, b0.val); + v128_t d = wasm_i32x4_mul(a1.val, b1.val); + return v_int16x8(wasm_v8x16_shuffle(c, d, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31)); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 a0, a1, b0, b1; + v_expand(a, a0, a1); + v_expand(b, b0, b1); + v128_t c = wasm_i32x4_mul(a0.val, b0.val); + v128_t d = wasm_i32x4_mul(a1.val, b1.val); + return v_uint16x8(wasm_v8x16_shuffle(c, d, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31)); +} + +//////// Dot Product //////// + +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) +{ + v128_t a0 = wasm_i32x4_shr(wasm_i32x4_shl(a.val, 16), 16); + v128_t a1 = wasm_i32x4_shr(a.val, 16); + v128_t b0 = wasm_i32x4_shr(wasm_i32x4_shl(b.val, 16), 16); + v128_t b1 = wasm_i32x4_shr(b.val, 16); + v128_t c = wasm_i32x4_mul(a0, b0); + v128_t d = wasm_i32x4_mul(a1, b1); + return v_int32x4(wasm_i32x4_add(c, d)); +} + +inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b) + c; } + +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b) +{ + v128_t a0 = wasm_i64x2_shr(wasm_i64x2_shl(a.val, 32), 32); + v128_t a1 = wasm_i64x2_shr(a.val, 32); + v128_t b0 = wasm_i64x2_shr(wasm_i64x2_shl(b.val, 32), 32); + v128_t b1 = wasm_i64x2_shr(b.val, 32); + v128_t c = (v128_t)((__i64x2)a0 * (__i64x2)b0); + v128_t d = (v128_t)((__i64x2)a1 * (__i64x2)b1); + return v_int64x2(wasm_i64x2_add(c, d)); +} +inline v_int64x2 v_dotprod(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ + return v_dotprod(a, b) + c; +} + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b) +{ + v128_t a0 = wasm_u16x8_shr(wasm_i16x8_shl(a.val, 8), 8); + v128_t a1 = wasm_u16x8_shr(a.val, 8); + v128_t b0 = wasm_u16x8_shr(wasm_i16x8_shl(b.val, 8), 8); + v128_t b1 = wasm_u16x8_shr(b.val, 8); + return v_uint32x4(( + v_dotprod(v_int16x8(a0), v_int16x8(b0)) + + v_dotprod(v_int16x8(a1), v_int16x8(b1))).val + ); +} +inline v_uint32x4 v_dotprod_expand(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b) +{ + v128_t a0 = wasm_i16x8_shr(wasm_i16x8_shl(a.val, 8), 8); + v128_t a1 = wasm_i16x8_shr(a.val, 8); + v128_t b0 = wasm_i16x8_shr(wasm_i16x8_shl(b.val, 8), 8); + v128_t b1 = wasm_i16x8_shr(b.val, 8); + return v_int32x4( + v_dotprod(v_int16x8(a0), v_int16x8(b0)) + + v_dotprod(v_int16x8(a1), v_int16x8(b1)) + ); +} +inline v_int32x4 v_dotprod_expand(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b) + c; } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b) +{ + v128_t a0 = wasm_u32x4_shr(wasm_i32x4_shl(a.val, 16), 16); + v128_t a1 = wasm_u32x4_shr(a.val, 16); + v128_t b0 = wasm_u32x4_shr(wasm_i32x4_shl(b.val, 16), 16); + v128_t b1 = wasm_u32x4_shr(b.val, 16); + return v_uint64x2(( + v_dotprod(v_int32x4(a0), v_int32x4(b0)) + + v_dotprod(v_int32x4(a1), v_int32x4(b1))).val + ); +} +inline v_uint64x2 v_dotprod_expand(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b) +{ + v128_t a0 = wasm_i32x4_shr(wasm_i32x4_shl(a.val, 16), 16); + v128_t a1 = wasm_i32x4_shr(a.val, 16); + v128_t b0 = wasm_i32x4_shr(wasm_i32x4_shl(b.val, 16), 16); + v128_t b1 = wasm_i32x4_shr(b.val, 16); + return v_int64x2(( + v_dotprod(v_int32x4(a0), v_int32x4(b0)) + + v_dotprod(v_int32x4(a1), v_int32x4(b1))) + ); +} + +inline v_int64x2 v_dotprod_expand(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b) +{ return v_cvt_f64(v_dotprod(a, b)); } +inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b) + c; } + +//////// Fast Dot Product //////// + +// 16 >> 32 +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod(a, b); } +inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) +{ return v_dotprod(a, b, c); } + +// 32 >> 64 +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod(a, b); } +inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) +{ return v_dotprod(a, b, c); } + +// 8 >> 32 +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b, const v_uint32x4& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) +{ return v_dotprod_expand(a, b); } +inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) +{ return v_dotprod_expand(a, b, c); } + +// 16 >> 64 +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_uint64x2 v_dotprod_expand_fast(const v_uint16x8& a, const v_uint16x8& b, const v_uint64x2& c) +{ return v_dotprod_expand(a, b, c); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b) +{ return v_dotprod_expand(a, b); } +inline v_int64x2 v_dotprod_expand_fast(const v_int16x8& a, const v_int16x8& b, const v_int64x2& c) +{ return v_dotprod_expand(a, b, c); } + +// 32 >> 64f +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b) +{ return v_dotprod_expand(a, b); } +inline v_float64x2 v_dotprod_expand_fast(const v_int32x4& a, const v_int32x4& b, const v_float64x2& c) +{ return v_dotprod_expand(a, b, c); } + +#define OPENCV_HAL_IMPL_WASM_LOGIC_OP(_Tpvec) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(&, _Tpvec, wasm_v128_and) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(|, _Tpvec, wasm_v128_or) \ +OPENCV_HAL_IMPL_WASM_BIN_OP(^, _Tpvec, wasm_v128_xor) \ +inline _Tpvec operator ~ (const _Tpvec& a) \ +{ \ + return _Tpvec(wasm_v128_not(a.val)); \ +} + +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint8x16) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int8x16) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint16x8) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int16x8) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_uint64x2) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_int64x2) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_float32x4) +OPENCV_HAL_IMPL_WASM_LOGIC_OP(v_float64x2) + +inline v_float32x4 v_sqrt(const v_float32x4& x) +{ + return v_float32x4(wasm_f32x4_sqrt(x.val)); +} + +inline v_float32x4 v_invsqrt(const v_float32x4& x) +{ + const v128_t _1_0 = wasm_f32x4_splat(1.0); + return v_float32x4(wasm_f32x4_div(_1_0, wasm_f32x4_sqrt(x.val))); +} + +inline v_float64x2 v_sqrt(const v_float64x2& x) +{ + return v_float64x2(wasm_f64x2_sqrt(x.val)); +} + +inline v_float64x2 v_invsqrt(const v_float64x2& x) +{ + const v128_t _1_0 = wasm_f64x2_splat(1.0); + return v_float64x2(wasm_f64x2_div(_1_0, wasm_f64x2_sqrt(x.val))); +} + +#define OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(_Tpuvec, _Tpsvec, suffix, zsuffix, shiftWidth) \ +inline _Tpuvec v_abs(const _Tpsvec& x) \ +{ \ + v128_t s = wasm_##suffix##_shr(x.val, shiftWidth); \ + v128_t f = wasm_##zsuffix##_shr(x.val, shiftWidth); \ + return _Tpuvec(wasm_##zsuffix##_add(wasm_v128_xor(x.val, f), s)); \ +} + +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint8x16, v_int8x16, u8x16, i8x16, 7) +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint16x8, v_int16x8, u16x8, i16x8, 15) +OPENCV_HAL_IMPL_WASM_ABS_INT_FUNC(v_uint32x4, v_int32x4, u32x4, i32x4, 31) + +inline v_float32x4 v_abs(const v_float32x4& x) +{ return v_float32x4(wasm_f32x4_abs(x.val)); } +inline v_float64x2 v_abs(const v_float64x2& x) +{ + return v_float64x2(wasm_f64x2_abs(x.val)); +} + +// TODO: exp, log, sin, cos + +#define OPENCV_HAL_IMPL_WASM_BIN_FUNC(_Tpvec, func, intrin) \ +inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(intrin(a.val, b.val)); \ +} + +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_min, wasm_f32x4_min) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_max, wasm_f32x4_max) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float64x2, v_min, wasm_f64x2_min) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float64x2, v_max, wasm_f64x2_max) + +#define OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(_Tpvec, suffix) \ +inline _Tpvec v_min(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(b.val, a.val, wasm_##suffix##_gt(a.val, b.val))); \ +} \ +inline _Tpvec v_max(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, wasm_##suffix##_gt(a.val, b.val))); \ +} + +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int8x16, i8x16) +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int16x8, i16x8) +OPENCV_HAL_IMPL_WASM_MINMAX_S_INIT_FUNC(v_int32x4, i32x4) + +#define OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(_Tpvec, suffix, deltaNum) \ +inline _Tpvec v_min(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t delta = wasm_##suffix##_splat(deltaNum); \ + v128_t mask = wasm_##suffix##_gt(wasm_v128_xor(a.val, delta), wasm_v128_xor(b.val, delta)); \ + return _Tpvec(wasm_v128_bitselect(b.val, a.val, mask)); \ +} \ +inline _Tpvec v_max(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t delta = wasm_##suffix##_splat(deltaNum); \ + v128_t mask = wasm_##suffix##_gt(wasm_v128_xor(a.val, delta), wasm_v128_xor(b.val, delta)); \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, mask)); \ +} + +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint8x16, i8x16, (schar)0x80) +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint16x8, i16x8, (short)0x8000) +OPENCV_HAL_IMPL_WASM_MINMAX_U_INIT_FUNC(v_uint32x4, i32x4, (int)0x80000000) + +#define OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(_Tpvec, suffix, esuffix) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##esuffix##_eq(a.val, b.val)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##esuffix##_ne(a.val, b.val)); } \ +inline _Tpvec operator < (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_lt(a.val, b.val)); } \ +inline _Tpvec operator > (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_gt(a.val, b.val)); } \ +inline _Tpvec operator <= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_le(a.val, b.val)); } \ +inline _Tpvec operator >= (const _Tpvec& a, const _Tpvec& b) \ +{ return _Tpvec(wasm_##suffix##_ge(a.val, b.val)); } + +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint8x16, u8x16, i8x16) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int8x16, i8x16, i8x16) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint16x8, u16x8, i16x8) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int16x8, i16x8, i16x8) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_uint32x4, u32x4, i32x4) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_int32x4, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_float32x4, f32x4, f32x4) +OPENCV_HAL_IMPL_WASM_INIT_CMP_OP(v_float64x2, f64x2, f64x2) + +#define OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(_Tpvec, cast) \ +inline _Tpvec operator == (const _Tpvec& a, const _Tpvec& b) \ +{ return cast(v_reinterpret_as_f64(a) == v_reinterpret_as_f64(b)); } \ +inline _Tpvec operator != (const _Tpvec& a, const _Tpvec& b) \ +{ return cast(v_reinterpret_as_f64(a) != v_reinterpret_as_f64(b)); } + +OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(v_uint64x2, v_reinterpret_as_u64) +OPENCV_HAL_IMPL_WASM_64BIT_CMP_OP(v_int64x2, v_reinterpret_as_s64) + +inline v_float32x4 v_not_nan(const v_float32x4& a) +{ + v128_t z = wasm_i32x4_splat(0x7fffffff); + v128_t t = wasm_i32x4_splat(0x7f800000); + return v_float32x4(wasm_u32x4_lt(wasm_v128_and(a.val, z), t)); +} +inline v_float64x2 v_not_nan(const v_float64x2& a) +{ + v128_t z = wasm_i64x2_splat(0x7fffffffffffffff); + v128_t t = wasm_i64x2_splat(0x7ff0000000000000); + return v_float64x2((__u64x2)(wasm_v128_and(a.val, z)) < (__u64x2)t); +} + +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_add_wrap, wasm_i8x16_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_add_wrap, wasm_i8x16_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_add_wrap, wasm_i16x8_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_add_wrap, wasm_i16x8_add) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_sub_wrap, wasm_i8x16_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_sub_wrap, wasm_i8x16_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_sub_wrap, wasm_i16x8_sub) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_sub_wrap, wasm_i16x8_sub) +#if (__EMSCRIPTEN_major__ * 1000000 + __EMSCRIPTEN_minor__ * 1000 + __EMSCRIPTEN_tiny__) >= (1039012) +// details: https://github.com/opencv/opencv/issues/18097 ( https://github.com/emscripten-core/emscripten/issues/12018 ) +// 1.39.12: https://github.com/emscripten-core/emscripten/commit/cd801d0f110facfd694212a3c8b2ed2ffcd630e2 +inline v_uint8x16 v_mul_wrap(const v_uint8x16& a, const v_uint8x16& b) +{ + uchar a_[16], b_[16]; + wasm_v128_store(a_, a.val); + wasm_v128_store(b_, b.val); + for (int i = 0; i < 16; i++) + a_[i] = (uchar)(a_[i] * b_[i]); + return v_uint8x16(wasm_v128_load(a_)); +} +inline v_int8x16 v_mul_wrap(const v_int8x16& a, const v_int8x16& b) +{ + schar a_[16], b_[16]; + wasm_v128_store(a_, a.val); + wasm_v128_store(b_, b.val); + for (int i = 0; i < 16; i++) + a_[i] = (schar)(a_[i] * b_[i]); + return v_int8x16(wasm_v128_load(a_)); +} +#else +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_mul_wrap, wasm_i8x16_mul) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_mul_wrap, wasm_i8x16_mul) +#endif +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_mul_wrap, wasm_i16x8_mul) +OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_mul_wrap, wasm_i16x8_mul) + + +/** Absolute difference **/ + +inline v_uint8x16 v_absdiff(const v_uint8x16& a, const v_uint8x16& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint16x8 v_absdiff(const v_uint16x8& a, const v_uint16x8& b) +{ return v_add_wrap(a - b, b - a); } +inline v_uint32x4 v_absdiff(const v_uint32x4& a, const v_uint32x4& b) +{ return v_max(a, b) - v_min(a, b); } + +inline v_uint8x16 v_absdiff(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = v_sub_wrap(a, b); + v_int8x16 m = a < b; + return v_reinterpret_as_u8(v_sub_wrap(d ^ m, m)); +} +inline v_uint16x8 v_absdiff(const v_int16x8& a, const v_int16x8& b) +{ + return v_reinterpret_as_u16(v_sub_wrap(v_max(a, b), v_min(a, b))); +} +inline v_uint32x4 v_absdiff(const v_int32x4& a, const v_int32x4& b) +{ + v_int32x4 d = a - b; + v_int32x4 m = a < b; + return v_reinterpret_as_u32((d ^ m) - m); +} + +/** Saturating absolute difference **/ +inline v_int8x16 v_absdiffs(const v_int8x16& a, const v_int8x16& b) +{ + v_int8x16 d = a - b; + v_int8x16 m = a < b; + return (d ^ m) - m; + } +inline v_int16x8 v_absdiffs(const v_int16x8& a, const v_int16x8& b) +{ return v_max(a, b) - v_min(a, b); } + + +inline v_int32x4 v_fma(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return a * b + c; +} + +inline v_int32x4 v_muladd(const v_int32x4& a, const v_int32x4& b, const v_int32x4& c) +{ + return v_fma(a, b, c); +} + +inline v_float32x4 v_fma(const v_float32x4& a, const v_float32x4& b, const v_float32x4& c) +{ + return a * b + c; +} + +inline v_float64x2 v_fma(const v_float64x2& a, const v_float64x2& b, const v_float64x2& c) +{ + return a * b + c; +} + +inline v_float32x4 v_absdiff(const v_float32x4& a, const v_float32x4& b) +{ + v128_t absmask_vec = wasm_i32x4_splat(0x7fffffff); + return v_float32x4(wasm_v128_and(wasm_f32x4_sub(a.val, b.val), absmask_vec)); +} +inline v_float64x2 v_absdiff(const v_float64x2& a, const v_float64x2& b) +{ + v128_t absmask_vec = wasm_u64x2_shr(wasm_i32x4_splat(-1), 1); + return v_float64x2(wasm_v128_and(wasm_f64x2_sub(a.val, b.val), absmask_vec)); +} + +#define OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(_Tpvec, suffix) \ +inline _Tpvec v_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t a_Square = wasm_##suffix##_mul(a.val, a.val); \ + v128_t b_Square = wasm_##suffix##_mul(b.val, b.val); \ + return _Tpvec(wasm_##suffix##_sqrt(wasm_##suffix##_add(a_Square, b_Square))); \ +} \ +inline _Tpvec v_sqr_magnitude(const _Tpvec& a, const _Tpvec& b) \ +{ \ + v128_t a_Square = wasm_##suffix##_mul(a.val, a.val); \ + v128_t b_Square = wasm_##suffix##_mul(b.val, b.val); \ + return _Tpvec(wasm_##suffix##_add(a_Square, b_Square)); \ +} \ +inline _Tpvec v_muladd(const _Tpvec& a, const _Tpvec& b, const _Tpvec& c) \ +{ \ + return _Tpvec(wasm_##suffix##_add(wasm_##suffix##_mul(a.val, b.val), c.val)); \ +} + +OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(v_float32x4, f32x4) +OPENCV_HAL_IMPL_WASM_MISC_FLT_OP(v_float64x2, f64x2) + +#define OPENCV_HAL_IMPL_WASM_SHIFT_OP(_Tpuvec, _Tpsvec, suffix, ssuffix) \ +inline _Tpuvec operator << (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +inline _Tpsvec operator << (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +inline _Tpuvec operator >> (const _Tpuvec& a, int imm) \ +{ \ + return _Tpuvec(wasm_##ssuffix##_shr(a.val, imm)); \ +} \ +inline _Tpsvec operator >> (const _Tpsvec& a, int imm) \ +{ \ + return _Tpsvec(wasm_##suffix##_shr(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shl(const _Tpuvec& a) \ +{ \ + return _Tpuvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shl(const _Tpsvec& a) \ +{ \ + return _Tpsvec(wasm_##suffix##_shl(a.val, imm)); \ +} \ +template \ +inline _Tpuvec v_shr(const _Tpuvec& a) \ +{ \ + return _Tpuvec(wasm_##ssuffix##_shr(a.val, imm)); \ +} \ +template \ +inline _Tpsvec v_shr(const _Tpsvec& a) \ +{ \ + return _Tpsvec(wasm_##suffix##_shr(a.val, imm)); \ +} + +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint8x16, v_int8x16, i8x16, u8x16) +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint16x8, v_int16x8, i16x8, u16x8) +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint32x4, v_int32x4, i32x4, u32x4) +OPENCV_HAL_IMPL_WASM_SHIFT_OP(v_uint64x2, v_int64x2, i64x2, u64x2) + +namespace hal_wasm_internal +{ + template 16)), + bool is_first = (imm == 0), + bool is_second = (imm == 16), + bool is_other = (((imm > 0) && (imm < 16)))> + class v_wasm_palignr_u8_class; + + template + class v_wasm_palignr_u8_class; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t& a, const v128_t&) const + { + return a; + } + }; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t&, const v128_t& b) const + { + return b; + } + }; + + template + class v_wasm_palignr_u8_class + { + public: + inline v128_t operator()(const v128_t& a, const v128_t& b) const + { + enum { imm2 = (sizeof(v128_t) - imm) }; + return wasm_v8x16_shuffle(a, b, + imm, imm+1, imm+2, imm+3, + imm+4, imm+5, imm+6, imm+7, + imm+8, imm+9, imm+10, imm+11, + imm+12, imm+13, imm+14, imm+15); + } + }; + + template + inline v128_t v_wasm_palignr_u8(const v128_t& a, const v128_t& b) + { + CV_StaticAssert((imm >= 0) && (imm <= 16), "Invalid imm for v_wasm_palignr_u8."); + return v_wasm_palignr_u8_class()(a, b); + } +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a) +{ + using namespace hal_wasm_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + v128_t z = wasm_i8x16_splat(0); + return _Tpvec(v_wasm_palignr_u8(a.val, z)); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a) +{ + using namespace hal_wasm_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + v128_t z = wasm_i8x16_splat(0); + return _Tpvec(v_wasm_palignr_u8(z, a.val)); +} + +template +inline _Tpvec v_rotate_right(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_wasm_internal; + enum { imm2 = (imm * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_wasm_palignr_u8(a.val, b.val)); +} + +template +inline _Tpvec v_rotate_left(const _Tpvec &a, const _Tpvec &b) +{ + using namespace hal_wasm_internal; + enum { imm2 = ((_Tpvec::nlanes - imm) * sizeof(typename _Tpvec::lane_type)) }; + return _Tpvec(v_wasm_palignr_u8(b.val, a.val)); +} + +#define OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(_Tpvec, _Tp) \ +inline _Tpvec v_load(const _Tp* ptr) \ +{ return _Tpvec(wasm_v128_load(ptr)); } \ +inline _Tpvec v_load_aligned(const _Tp* ptr) \ +{ return _Tpvec(wasm_v128_load(ptr)); } \ +inline _Tpvec v_load_low(const _Tp* ptr) \ +{ \ + _Tp tmp[_Tpvec::nlanes] = {0}; \ + for (int i=0; i<_Tpvec::nlanes/2; ++i) { \ + tmp[i] = ptr[i]; \ + } \ + return _Tpvec(wasm_v128_load(tmp)); \ +} \ +inline _Tpvec v_load_halves(const _Tp* ptr0, const _Tp* ptr1) \ +{ \ + _Tp tmp[_Tpvec::nlanes]; \ + for (int i=0; i<_Tpvec::nlanes/2; ++i) { \ + tmp[i] = ptr0[i]; \ + tmp[i+_Tpvec::nlanes/2] = ptr1[i]; \ + } \ + return _Tpvec(wasm_v128_load(tmp)); \ +} \ +inline void v_store(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store_aligned(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store_aligned_nocache(_Tp* ptr, const _Tpvec& a) \ +{ wasm_v128_store(ptr, a.val); } \ +inline void v_store(_Tp* ptr, const _Tpvec& a, hal::StoreMode /*mode*/) \ +{ \ + wasm_v128_store(ptr, a.val); \ +} \ +inline void v_store_low(_Tp* ptr, const _Tpvec& a) \ +{ \ + _Tpvec::lane_type a_[_Tpvec::nlanes]; \ + wasm_v128_store(a_, a.val); \ + for (int i = 0; i < (_Tpvec::nlanes / 2); i++) \ + ptr[i] = a_[i]; \ +} \ +inline void v_store_high(_Tp* ptr, const _Tpvec& a) \ +{ \ + _Tpvec::lane_type a_[_Tpvec::nlanes]; \ + wasm_v128_store(a_, a.val); \ + for (int i = 0; i < (_Tpvec::nlanes / 2); i++) \ + ptr[i] = a_[i + (_Tpvec::nlanes / 2)]; \ +} + +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint8x16, uchar) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int8x16, schar) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint16x8, ushort) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int16x8, short) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint32x4, unsigned) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int32x4, int) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_uint64x2, uint64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_int64x2, int64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_float32x4, float) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INT_OP(v_float64x2, double) + + +/** Reverse **/ +inline v_uint8x16 v_reverse(const v_uint8x16 &a) +{ return v_uint8x16(wasm_v8x16_shuffle(a.val, a.val, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); } + +inline v_int8x16 v_reverse(const v_int8x16 &a) +{ return v_reinterpret_as_s8(v_reverse(v_reinterpret_as_u8(a))); } + +inline v_uint16x8 v_reverse(const v_uint16x8 &a) +{ return v_uint16x8(wasm_v8x16_shuffle(a.val, a.val, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1)); } + +inline v_int16x8 v_reverse(const v_int16x8 &a) +{ return v_reinterpret_as_s16(v_reverse(v_reinterpret_as_u16(a))); } + +inline v_uint32x4 v_reverse(const v_uint32x4 &a) +{ return v_uint32x4(wasm_v8x16_shuffle(a.val, a.val, 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3)); } + +inline v_int32x4 v_reverse(const v_int32x4 &a) +{ return v_reinterpret_as_s32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_float32x4 v_reverse(const v_float32x4 &a) +{ return v_reinterpret_as_f32(v_reverse(v_reinterpret_as_u32(a))); } + +inline v_uint64x2 v_reverse(const v_uint64x2 &a) +{ return v_uint64x2(wasm_v8x16_shuffle(a.val, a.val, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)); } + +inline v_int64x2 v_reverse(const v_int64x2 &a) +{ return v_reinterpret_as_s64(v_reverse(v_reinterpret_as_u64(a))); } + +inline v_float64x2 v_reverse(const v_float64x2 &a) +{ return v_reinterpret_as_f64(v_reverse(v_reinterpret_as_u64(a))); } + + +#define OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(_Tpvec, scalartype, regtype, suffix, esuffix) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + regtype val = a.val; \ + val = wasm_##suffix##_add(val, wasm_v8x16_shuffle(val, val, 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); \ + val = wasm_##suffix##_add(val, wasm_v8x16_shuffle(val, val, 4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3)); \ + return (scalartype)wasm_##esuffix##_extract_lane(val, 0); \ +} + +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_uint32x4, unsigned, v128_t, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_int32x4, int, v128_t, i32x4, i32x4) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_4_SUM(v_float32x4, float, v128_t, f32x4, f32x4) + +// To do: Optimize v_reduce_sum with wasm intrin. +// Now use fallback implementation as there is no widening op in wasm intrin. + +#define OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(_Tpvec, scalartype) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + _Tpvec::lane_type a_[_Tpvec::nlanes]; \ + wasm_v128_store(a_, a.val); \ + scalartype c = a_[0]; \ + for (int i = 1; i < _Tpvec::nlanes; i++) \ + c += a_[i]; \ + return c; \ +} + +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_uint8x16, unsigned) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_int8x16, int) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_uint16x8, unsigned) +OPENCV_HAL_IMPL_FALLBACK_REDUCE_OP_SUM(v_int16x8, int) + + +#define OPENCV_HAL_IMPL_WASM_REDUCE_OP_2_SUM(_Tpvec, scalartype, regtype, suffix, esuffix) \ +inline scalartype v_reduce_sum(const _Tpvec& a) \ +{ \ + regtype val = a.val; \ + val = wasm_##suffix##_add(val, wasm_v8x16_shuffle(val, val, 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); \ + return (scalartype)wasm_##esuffix##_extract_lane(val, 0); \ +} +OPENCV_HAL_IMPL_WASM_REDUCE_OP_2_SUM(v_uint64x2, uint64, v128_t, i64x2, i64x2) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_2_SUM(v_int64x2, int64, v128_t, i64x2, i64x2) +OPENCV_HAL_IMPL_WASM_REDUCE_OP_2_SUM(v_float64x2, double, v128_t, f64x2,f64x2) + +inline v_float32x4 v_reduce_sum4(const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d) +{ + v128_t ac = wasm_f32x4_add(wasm_unpacklo_i32x4(a.val, c.val), wasm_unpackhi_i32x4(a.val, c.val)); + v128_t bd = wasm_f32x4_add(wasm_unpacklo_i32x4(b.val, d.val), wasm_unpackhi_i32x4(b.val, d.val)); + return v_float32x4(wasm_f32x4_add(wasm_unpacklo_i32x4(ac, bd), wasm_unpackhi_i32x4(ac, bd))); +} + +#define OPENCV_HAL_IMPL_WASM_REDUCE_OP(_Tpvec, scalartype, func, scalar_func) \ +inline scalartype v_reduce_##func(const _Tpvec& a) \ +{ \ + scalartype buf[_Tpvec::nlanes]; \ + v_store(buf, a); \ + scalartype tmp = buf[0]; \ + for (int i=1; i<_Tpvec::nlanes; ++i) { \ + tmp = scalar_func(tmp, buf[i]); \ + } \ + return tmp; \ +} + +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint8x16, uchar, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint8x16, uchar, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int8x16, schar, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int8x16, schar, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint16x8, ushort, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint16x8, ushort, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int16x8, short, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int16x8, short, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint32x4, unsigned, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_uint32x4, unsigned, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int32x4, int, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_int32x4, int, min, std::min) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_float32x4, float, max, std::max) +OPENCV_HAL_IMPL_WASM_REDUCE_OP(v_float32x4, float, min, std::min) + +inline unsigned v_reduce_sad(const v_uint8x16& a, const v_uint8x16& b) +{ + v_uint16x8 l16, h16; + v_uint32x4 l16_l32, l16_h32, h16_l32, h16_h32; + v_expand(v_absdiff(a, b), l16, h16); + v_expand(l16, l16_l32, l16_h32); + v_expand(h16, h16_l32, h16_h32); + return v_reduce_sum(l16_l32+l16_h32+h16_l32+h16_h32); +} +inline unsigned v_reduce_sad(const v_int8x16& a, const v_int8x16& b) +{ + v_uint16x8 l16, h16; + v_uint32x4 l16_l32, l16_h32, h16_l32, h16_h32; + v_expand(v_absdiff(a, b), l16, h16); + v_expand(l16, l16_l32, l16_h32); + v_expand(h16, h16_l32, h16_h32); + return v_reduce_sum(l16_l32+l16_h32+h16_l32+h16_h32); +} +inline unsigned v_reduce_sad(const v_uint16x8& a, const v_uint16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_int16x8& a, const v_int16x8& b) +{ + v_uint32x4 l, h; + v_expand(v_absdiff(a, b), l, h); + return v_reduce_sum(l + h); +} +inline unsigned v_reduce_sad(const v_uint32x4& a, const v_uint32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline unsigned v_reduce_sad(const v_int32x4& a, const v_int32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} +inline float v_reduce_sad(const v_float32x4& a, const v_float32x4& b) +{ + return v_reduce_sum(v_absdiff(a, b)); +} + +inline v_uint8x16 v_popcount(const v_uint8x16& a) +{ + v128_t m1 = wasm_i32x4_splat(0x55555555); + v128_t m2 = wasm_i32x4_splat(0x33333333); + v128_t m4 = wasm_i32x4_splat(0x0f0f0f0f); + v128_t p = a.val; + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 1), m1), wasm_v128_and(p, m1)); + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 2), m2), wasm_v128_and(p, m2)); + p = wasm_i32x4_add(wasm_v128_and(wasm_u32x4_shr(p, 4), m4), wasm_v128_and(p, m4)); + return v_uint8x16(p); +} +inline v_uint16x8 v_popcount(const v_uint16x8& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + return v_reinterpret_as_u16(p) & v_setall_u16(0x00ff); +} +inline v_uint32x4 v_popcount(const v_uint32x4& a) +{ + v_uint8x16 p = v_popcount(v_reinterpret_as_u8(a)); + p += v_rotate_right<1>(p); + p += v_rotate_right<2>(p); + return v_reinterpret_as_u32(p) & v_setall_u32(0x000000ff); +} +inline v_uint64x2 v_popcount(const v_uint64x2& a) +{ + uint64 a_[2], b_[2] = { 0 }; + wasm_v128_store(a_, a.val); + for (int i = 0; i < 16; i++) + b_[i / 8] += popCountTable[((uint8_t*)a_)[i]]; + return v_uint64x2(wasm_v128_load(b_)); +} +inline v_uint8x16 v_popcount(const v_int8x16& a) +{ return v_popcount(v_reinterpret_as_u8(a)); } +inline v_uint16x8 v_popcount(const v_int16x8& a) +{ return v_popcount(v_reinterpret_as_u16(a)); } +inline v_uint32x4 v_popcount(const v_int32x4& a) +{ return v_popcount(v_reinterpret_as_u32(a)); } +inline v_uint64x2 v_popcount(const v_int64x2& a) +{ return v_popcount(v_reinterpret_as_u64(a)); } + +#define OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(_Tpvec, suffix, scalarType) \ +inline int v_signmask(const _Tpvec& a) \ +{ \ + _Tpvec::lane_type a_[_Tpvec::nlanes]; \ + wasm_v128_store(a_, a.val); \ + int mask = 0; \ + for (int i = 0; i < _Tpvec::nlanes; i++) \ + mask |= (reinterpret_int(a_[i]) < 0) << i; \ + return mask; \ +} \ +inline bool v_check_all(const _Tpvec& a) \ +{ return wasm_i8x16_all_true(wasm_##suffix##_lt(a.val, wasm_##suffix##_splat(0))); } \ +inline bool v_check_any(const _Tpvec& a) \ +{ return wasm_i8x16_any_true(wasm_##suffix##_lt(a.val, wasm_##suffix##_splat(0)));; } + +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint8x16, i8x16, schar) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int8x16, i8x16, schar) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint16x8, i16x8, short) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int16x8, i16x8, short) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_uint32x4, i32x4, int) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_int32x4, i32x4, int) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_float32x4, i32x4, float) +OPENCV_HAL_IMPL_WASM_CHECK_SIGNS(v_float64x2, f64x2, double) + +#define OPENCV_HAL_IMPL_WASM_CHECK_ALL_ANY(_Tpvec, suffix, esuffix) \ +inline bool v_check_all(const _Tpvec& a) \ +{ \ + v128_t masked = v_reinterpret_as_##esuffix(a).val; \ + masked = wasm_i32x4_replace_lane(masked, 0, 0xffffffff); \ + masked = wasm_i32x4_replace_lane(masked, 2, 0xffffffff); \ + return wasm_i8x16_all_true(wasm_##suffix##_lt(masked, wasm_##suffix##_splat(0))); \ +} \ +inline bool v_check_any(const _Tpvec& a) \ +{ \ + v128_t masked = v_reinterpret_as_##esuffix(a).val; \ + masked = wasm_i32x4_replace_lane(masked, 0, 0x0); \ + masked = wasm_i32x4_replace_lane(masked, 2, 0x0); \ + return wasm_i8x16_any_true(wasm_##suffix##_lt(masked, wasm_##suffix##_splat(0))); \ +} \ + +OPENCV_HAL_IMPL_WASM_CHECK_ALL_ANY(v_int64x2, i32x4, s32) +OPENCV_HAL_IMPL_WASM_CHECK_ALL_ANY(v_uint64x2, i32x4, u32) + + +inline int v_scan_forward(const v_int8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_uint8x16& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))); } +inline int v_scan_forward(const v_int16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_uint16x8& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 2; } +inline int v_scan_forward(const v_int32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_uint32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_float32x4& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 4; } +inline int v_scan_forward(const v_int64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_uint64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } +inline int v_scan_forward(const v_float64x2& a) { return trailingZeros32(v_signmask(v_reinterpret_as_s8(a))) / 8; } + +#define OPENCV_HAL_IMPL_WASM_SELECT(_Tpvec) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_v128_bitselect(a.val, b.val, mask.val)); \ +} + +OPENCV_HAL_IMPL_WASM_SELECT(v_uint8x16) +OPENCV_HAL_IMPL_WASM_SELECT(v_int8x16) +OPENCV_HAL_IMPL_WASM_SELECT(v_uint16x8) +OPENCV_HAL_IMPL_WASM_SELECT(v_int16x8) +OPENCV_HAL_IMPL_WASM_SELECT(v_uint32x4) +OPENCV_HAL_IMPL_WASM_SELECT(v_int32x4) +OPENCV_HAL_IMPL_WASM_SELECT(v_uint64x2) +OPENCV_HAL_IMPL_WASM_SELECT(v_int64x2) +OPENCV_HAL_IMPL_WASM_SELECT(v_float32x4) +OPENCV_HAL_IMPL_WASM_SELECT(v_float64x2) + +#define OPENCV_HAL_IMPL_WASM_EXPAND(_Tpvec, _Tpwvec, _Tp, intrin) \ +inline void v_expand(const _Tpvec& a, _Tpwvec& b0, _Tpwvec& b1) \ +{ \ + b0.val = intrin(a.val); \ + b1.val = __CV_CAT(intrin, _high)(a.val); \ +} \ +inline _Tpwvec v_expand_low(const _Tpvec& a) \ +{ return _Tpwvec(intrin(a.val)); } \ +inline _Tpwvec v_expand_high(const _Tpvec& a) \ +{ return _Tpwvec(__CV_CAT(intrin, _high)(a.val)); } \ +inline _Tpwvec v_load_expand(const _Tp* ptr) \ +{ \ + v128_t a = wasm_v128_load(ptr); \ + return _Tpwvec(intrin(a)); \ +} + +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint8x16, v_uint16x8, uchar, v128_cvtu8x16_i16x8) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int8x16, v_int16x8, schar, v128_cvti8x16_i16x8) +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint16x8, v_uint32x4, ushort, v128_cvtu16x8_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int16x8, v_int32x4, short, v128_cvti16x8_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND(v_uint32x4, v_uint64x2, unsigned, v128_cvtu32x4_i64x2) +OPENCV_HAL_IMPL_WASM_EXPAND(v_int32x4, v_int64x2, int, v128_cvti32x4_i64x2) + +#define OPENCV_HAL_IMPL_WASM_EXPAND_Q(_Tpvec, _Tp, intrin) \ +inline _Tpvec v_load_expand_q(const _Tp* ptr) \ +{ \ + v128_t a = wasm_v128_load(ptr); \ + return _Tpvec(intrin(a)); \ +} + +OPENCV_HAL_IMPL_WASM_EXPAND_Q(v_uint32x4, uchar, v128_cvtu8x16_i32x4) +OPENCV_HAL_IMPL_WASM_EXPAND_Q(v_int32x4, schar, v128_cvti8x16_i32x4) + +#define OPENCV_HAL_IMPL_WASM_UNPACKS(_Tpvec, suffix) \ +inline void v_zip(const _Tpvec& a0, const _Tpvec& a1, _Tpvec& b0, _Tpvec& b1) \ +{ \ + b0.val = wasm_unpacklo_##suffix(a0.val, a1.val); \ + b1.val = wasm_unpackhi_##suffix(a0.val, a1.val); \ +} \ +inline _Tpvec v_combine_low(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_unpacklo_i64x2(a.val, b.val)); \ +} \ +inline _Tpvec v_combine_high(const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(wasm_unpackhi_i64x2(a.val, b.val)); \ +} \ +inline void v_recombine(const _Tpvec& a, const _Tpvec& b, _Tpvec& c, _Tpvec& d) \ +{ \ + c.val = wasm_unpacklo_i64x2(a.val, b.val); \ + d.val = wasm_unpackhi_i64x2(a.val, b.val); \ +} + +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint8x16, i8x16) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int8x16, i8x16) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint16x8, i16x8) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int16x8, i16x8) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_uint32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_int32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_float32x4, i32x4) +OPENCV_HAL_IMPL_WASM_UNPACKS(v_float64x2, i64x2) + +template +inline _Tpvec v_extract(const _Tpvec& a, const _Tpvec& b) +{ + return v_rotate_right(a, b); +} + +inline v_int32x4 v_round(const v_float32x4& a) +{ + v128_t h = wasm_f32x4_splat(0.5); + return v_int32x4(wasm_i32x4_trunc_saturate_f32x4(wasm_f32x4_add(a.val, h))); +} + +inline v_int32x4 v_floor(const v_float32x4& a) +{ + v128_t a1 = wasm_i32x4_trunc_saturate_f32x4(a.val); + v128_t mask = wasm_f32x4_lt(a.val, wasm_f32x4_convert_i32x4(a1)); + return v_int32x4(wasm_i32x4_add(a1, mask)); +} + +inline v_int32x4 v_ceil(const v_float32x4& a) +{ + v128_t a1 = wasm_i32x4_trunc_saturate_f32x4(a.val); + v128_t mask = wasm_f32x4_gt(a.val, wasm_f32x4_convert_i32x4(a1)); + return v_int32x4(wasm_i32x4_sub(a1, mask)); +} + +inline v_int32x4 v_trunc(const v_float32x4& a) +{ return v_int32x4(wasm_i32x4_trunc_saturate_f32x4(a.val)); } + +#define OPENCV_HAL_IMPL_WASM_MATH_FUNC(func, cfunc) \ +inline v_int32x4 func(const v_float64x2& a) \ +{ \ + double a_[2]; \ + wasm_v128_store(a_, a.val); \ + int c_[4]; \ + c_[0] = cfunc(a_[0]); \ + c_[1] = cfunc(a_[1]); \ + c_[2] = 0; \ + c_[3] = 0; \ + return v_int32x4(wasm_v128_load(c_)); \ +} + +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_round, cvRound) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_floor, cvFloor) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_ceil, cvCeil) +OPENCV_HAL_IMPL_WASM_MATH_FUNC(v_trunc, int) + +inline v_int32x4 v_round(const v_float64x2& a, const v_float64x2& b) +{ + double a_[2], b_[2]; + wasm_v128_store(a_, a.val); + wasm_v128_store(b_, b.val); + int c_[4]; + c_[0] = cvRound(a_[0]); + c_[1] = cvRound(a_[1]); + c_[2] = cvRound(b_[0]); + c_[3] = cvRound(b_[1]); + return v_int32x4(wasm_v128_load(c_)); +} + +#define OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(_Tpvec, suffix) \ +inline void v_transpose4x4(const _Tpvec& a0, const _Tpvec& a1, \ + const _Tpvec& a2, const _Tpvec& a3, \ + _Tpvec& b0, _Tpvec& b1, \ + _Tpvec& b2, _Tpvec& b3) \ +{ \ + v128_t t0 = wasm_unpacklo_##suffix(a0.val, a1.val); \ + v128_t t1 = wasm_unpacklo_##suffix(a2.val, a3.val); \ + v128_t t2 = wasm_unpackhi_##suffix(a0.val, a1.val); \ + v128_t t3 = wasm_unpackhi_##suffix(a2.val, a3.val); \ +\ + b0.val = wasm_unpacklo_i64x2(t0, t1); \ + b1.val = wasm_unpackhi_i64x2(t0, t1); \ + b2.val = wasm_unpacklo_i64x2(t2, t3); \ + b3.val = wasm_unpackhi_i64x2(t2, t3); \ +} + +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_uint32x4, i32x4) +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_int32x4, i32x4) +OPENCV_HAL_IMPL_WASM_TRANSPOSE4x4(v_float32x4, i32x4) + +// load deinterleave +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b) +{ + v128_t t00 = wasm_v128_load(ptr); + v128_t t01 = wasm_v128_load(ptr + 16); + + a.val = wasm_v8x16_shuffle(t00, t01, 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30); + b.val = wasm_v8x16_shuffle(t00, t01, 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c) +{ + v128_t t00 = wasm_v128_load(ptr); + v128_t t01 = wasm_v128_load(ptr + 16); + v128_t t02 = wasm_v128_load(ptr + 32); + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,3,6,9,12,15,18,21,24,27,30,1,2,4,5,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 1,4,7,10,13,16,19,22,25,28,31,0,2,3,5,6); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 2,5,8,11,14,17,20,23,26,29,0,1,3,4,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,17,20,23,26,29); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,18,21,24,27,30); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,8,9,16,19,22,25,28,31); +} + +inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c, v_uint8x16& d) +{ + v128_t u0 = wasm_v128_load(ptr); // a0 b0 c0 d0 a1 b1 c1 d1 ... + v128_t u1 = wasm_v128_load(ptr + 16); // a4 b4 c4 d4 ... + v128_t u2 = wasm_v128_load(ptr + 32); // a8 b8 c8 d8 ... + v128_t u3 = wasm_v128_load(ptr + 48); // a12 b12 c12 d12 ... + + v128_t v0 = wasm_v8x16_shuffle(u0, u1, 0,4,8,12,16,20,24,28,1,5,9,13,17,21,25,29); + v128_t v1 = wasm_v8x16_shuffle(u2, u3, 0,4,8,12,16,20,24,28,1,5,9,13,17,21,25,29); + v128_t v2 = wasm_v8x16_shuffle(u0, u1, 2,6,10,14,18,22,26,30,3,7,11,15,19,23,27,31); + v128_t v3 = wasm_v8x16_shuffle(u2, u3, 2,6,10,14,18,22,26,30,3,7,11,15,19,23,27,31); + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + b.val = wasm_v8x16_shuffle(v0, v1, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + c.val = wasm_v8x16_shuffle(v2, v3, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + d.val = wasm_v8x16_shuffle(v2, v3, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 a2 b2 a3 b3 + v128_t v1 = wasm_v128_load(ptr + 8); // a4 b4 a5 b5 a6 b6 a7 b7 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,4,5,8,9,12,13,16,17,20,21,24,25,28,29); // a0 a1 a2 a3 a4 a5 a6 a7 + b.val = wasm_v8x16_shuffle(v0, v1, 2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31); // b0 b1 ab b3 b4 b5 b6 b7 +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 b1 c1 a2 b2 + v128_t t01 = wasm_v128_load(ptr + 8); // c2 a3 b3 c3 a4 b4 c4 a5 + v128_t t02 = wasm_v128_load(ptr + 16); // b5 c5 a6 b6 c6 a7 b7 c7 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,6,7,12,13,18,19,24,25,30,31,2,3,4,5); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 2,3,8,9,14,15,20,21,26,27,0,1,4,5,6,7); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 4,5,10,11,16,17,22,23,28,29,0,1,2,3,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,26,27); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,16,17,22,23,28,29); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,8,9,18,19,24,25,30,31); +} + +inline void v_load_deinterleave(const ushort* ptr, v_uint16x8& a, v_uint16x8& b, v_uint16x8& c, v_uint16x8& d) +{ + v128_t u0 = wasm_v128_load(ptr); // a0 b0 c0 d0 a1 b1 c1 d1 + v128_t u1 = wasm_v128_load(ptr + 8); // a2 b2 c2 d2 ... + v128_t u2 = wasm_v128_load(ptr + 16); // a4 b4 c4 d4 ... + v128_t u3 = wasm_v128_load(ptr + 24); // a6 b6 c6 d6 ... + + v128_t v0 = wasm_v8x16_shuffle(u0, u1, 0,1,8,9,16,17,24,25,2,3,10,11,18,19,26,27); // a0 a1 a2 a3 b0 b1 b2 b3 + v128_t v1 = wasm_v8x16_shuffle(u2, u3, 0,1,8,9,16,17,24,25,2,3,10,11,18,19,26,27); // a4 a5 a6 a7 b4 b5 b6 b7 + v128_t v2 = wasm_v8x16_shuffle(u0, u1, 4,5,12,13,20,21,28,29,6,7,14,15,22,23,30,31); // c0 c1 c2 c3 d0 d1 d2 d3 + v128_t v3 = wasm_v8x16_shuffle(u2, u3, 4,5,12,13,20,21,28,29,6,7,14,15,22,23,30,31); // c4 c5 c6 c7 d4 d5 d6 d7 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + b.val = wasm_v8x16_shuffle(v0, v1, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + c.val = wasm_v8x16_shuffle(v2, v3, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + d.val = wasm_v8x16_shuffle(v2, v3, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 + v128_t v1 = wasm_v128_load(ptr + 4); // a2 b2 a3 b3 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); // a0 a1 a2 a3 + b.val = wasm_v8x16_shuffle(v0, v1, 4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); // b0 b1 b2 b3 +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 + v128_t t01 = wasm_v128_load(ptr + 4); // b2 c2 a3 b3 + v128_t t02 = wasm_v128_load(ptr + 8); // c3 a4 b4 c4 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,2,3,12,13,14,15,24,25,26,27,4,5,6,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 4,5,6,7,16,17,18,19,28,29,30,31,0,1,2,3); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 8,9,10,11,20,21,22,23,0,1,2,3,4,5,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,22,23); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,11,24,25,26,27); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,16,17,18,19,28,29,30,31); +} + +inline void v_load_deinterleave(const unsigned* ptr, v_uint32x4& a, v_uint32x4& b, v_uint32x4& c, v_uint32x4& d) +{ + v_uint32x4 s0(wasm_v128_load(ptr)); // a0 b0 c0 d0 + v_uint32x4 s1(wasm_v128_load(ptr + 4)); // a1 b1 c1 d1 + v_uint32x4 s2(wasm_v128_load(ptr + 8)); // a2 b2 c2 d2 + v_uint32x4 s3(wasm_v128_load(ptr + 12)); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b) +{ + v128_t v0 = wasm_v128_load(ptr); // a0 b0 a1 b1 + v128_t v1 = wasm_v128_load((ptr + 4)); // a2 b2 a3 b3 + + a.val = wasm_v8x16_shuffle(v0, v1, 0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); // a0 a1 a2 a3 + b.val = wasm_v8x16_shuffle(v0, v1, 4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); // b0 b1 b2 b3 +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c) +{ + v128_t t00 = wasm_v128_load(ptr); // a0 b0 c0 a1 + v128_t t01 = wasm_v128_load(ptr + 4); // b2 c2 a3 b3 + v128_t t02 = wasm_v128_load(ptr + 8); // c3 a4 b4 c4 + + v128_t t10 = wasm_v8x16_shuffle(t00, t01, 0,1,2,3,12,13,14,15,24,25,26,27,4,5,6,7); + v128_t t11 = wasm_v8x16_shuffle(t00, t01, 4,5,6,7,16,17,18,19,28,29,30,31,0,1,2,3); + v128_t t12 = wasm_v8x16_shuffle(t00, t01, 8,9,10,11,20,21,22,23,0,1,2,3,4,5,6,7); + + a.val = wasm_v8x16_shuffle(t10, t02, 0,1,2,3,4,5,6,7,8,9,10,11,20,21,22,23); + b.val = wasm_v8x16_shuffle(t11, t02, 0,1,2,3,4,5,6,7,8,9,10,11,24,25,26,27); + c.val = wasm_v8x16_shuffle(t12, t02, 0,1,2,3,4,5,6,7,16,17,18,19,28,29,30,31); +} + +inline void v_load_deinterleave(const float* ptr, v_float32x4& a, v_float32x4& b, v_float32x4& c, v_float32x4& d) +{ + v_float32x4 s0(wasm_v128_load(ptr)); // a0 b0 c0 d0 + v_float32x4 s1(wasm_v128_load(ptr + 4)); // a1 b1 c1 d1 + v_float32x4 s2(wasm_v128_load(ptr + 8)); // a2 b2 c2 d2 + v_float32x4 s3(wasm_v128_load(ptr + 12)); // a3 b3 c3 d3 + + v_transpose4x4(s0, s1, s2, s3, a, b, c, d); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b) +{ + v128_t t0 = wasm_v128_load(ptr); // a0 b0 + v128_t t1 = wasm_v128_load(ptr + 2); // a1 b1 + + a.val = wasm_unpacklo_i64x2(t0, t1); + b.val = wasm_unpackhi_i64x2(t0, t1); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, v_uint64x2& b, v_uint64x2& c) +{ + v128_t t0 = wasm_v128_load(ptr); // a0, b0 + v128_t t1 = wasm_v128_load(ptr + 2); // c0, a1 + v128_t t2 = wasm_v128_load(ptr + 4); // b1, c1 + + a.val = wasm_v8x16_shuffle(t0, t1, 0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31); + b.val = wasm_v8x16_shuffle(t0, t2, 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23); + c.val = wasm_v8x16_shuffle(t1, t2, 0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31); +} + +inline void v_load_deinterleave(const uint64 *ptr, v_uint64x2& a, + v_uint64x2& b, v_uint64x2& c, v_uint64x2& d) +{ + v128_t t0 = wasm_v128_load(ptr); // a0 b0 + v128_t t1 = wasm_v128_load(ptr + 2); // c0 d0 + v128_t t2 = wasm_v128_load(ptr + 4); // a1 b1 + v128_t t3 = wasm_v128_load(ptr + 6); // c1 d1 + + a.val = wasm_unpacklo_i64x2(t0, t2); + b.val = wasm_unpackhi_i64x2(t0, t2); + c.val = wasm_unpacklo_i64x2(t1, t3); + d.val = wasm_unpackhi_i64x2(t1, t3); +} + +// store interleave + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i8x16(a.val, b.val); + v128_t v1 = wasm_unpackhi_i8x16(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 16, v1); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,16,0,1,17,0,2,18,0,3,19,0,4,20,0,5); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 21,0,6,22,0,7,23,0,8,24,0,9,25,0,10,26); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,11,27,0,12,28,0,13,29,0,14,30,0,15,31,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,16,3,4,17,6,7,18,9,10,19,12,13,20,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,21,2,3,22,5,6,23,8,9,24,11,12,25,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 26,1,2,27,4,5,28,7,8,29,10,11,30,13,14,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 16, t11); + wasm_v128_store(ptr + 32, t12); +} + +inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, + const v_uint8x16& c, const v_uint8x16& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + v128_t u0 = wasm_unpacklo_i8x16(a.val, c.val); // a0 c0 a1 c1 ... + v128_t u1 = wasm_unpackhi_i8x16(a.val, c.val); // a8 c8 a9 c9 ... + v128_t u2 = wasm_unpacklo_i8x16(b.val, d.val); // b0 d0 b1 d1 ... + v128_t u3 = wasm_unpackhi_i8x16(b.val, d.val); // b8 d8 b9 d9 ... + + v128_t v0 = wasm_unpacklo_i8x16(u0, u2); // a0 b0 c0 d0 ... + v128_t v1 = wasm_unpackhi_i8x16(u0, u2); // a4 b4 c4 d4 ... + v128_t v2 = wasm_unpacklo_i8x16(u1, u3); // a8 b8 c8 d8 ... + v128_t v3 = wasm_unpackhi_i8x16(u1, u3); // a12 b12 c12 d12 ... + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 16, v1); + wasm_v128_store(ptr + 32, v2); + wasm_v128_store(ptr + 48, v3); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i16x8(a.val, b.val); + v128_t v1 = wasm_unpackhi_i16x8(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 8, v1); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, + const v_uint16x8& b, const v_uint16x8& c, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,16,17,0,0,2,3,18,19,0,0,4,5,20,21); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 0,0,6,7,22,23,0,0,8,9,24,25,0,0,10,11); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 26,27,0,0,12,13,28,29,0,0,14,15,30,31,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,16,17,6,7,8,9,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 20,21,2,3,4,5,22,23,8,9,10,11,24,25,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 0,1,26,27,4,5,6,7,28,29,10,11,12,13,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 8, t11); + wasm_v128_store(ptr + 16, t12); +} + +inline void v_store_interleave( ushort* ptr, const v_uint16x8& a, const v_uint16x8& b, + const v_uint16x8& c, const v_uint16x8& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + // a0 a1 a2 a3 .... + // b0 b1 b2 b3 .... + // c0 c1 c2 c3 .... + // d0 d1 d2 d3 .... + v128_t u0 = wasm_unpacklo_i16x8(a.val, c.val); // a0 c0 a1 c1 ... + v128_t u1 = wasm_unpackhi_i16x8(a.val, c.val); // a4 c4 a5 c5 ... + v128_t u2 = wasm_unpacklo_i16x8(b.val, d.val); // b0 d0 b1 d1 ... + v128_t u3 = wasm_unpackhi_i16x8(b.val, d.val); // b4 d4 b5 d5 ... + + v128_t v0 = wasm_unpacklo_i16x8(u0, u2); // a0 b0 c0 d0 ... + v128_t v1 = wasm_unpackhi_i16x8(u0, u2); // a2 b2 c2 d2 ... + v128_t v2 = wasm_unpacklo_i16x8(u1, u3); // a4 b4 c4 d4 ... + v128_t v3 = wasm_unpackhi_i16x8(u1, u3); // a6 b6 c6 d6 ... + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 8, v1); + wasm_v128_store(ptr + 16, v2); + wasm_v128_store(ptr + 24, v3); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i32x4(a.val, b.val); + v128_t v1 = wasm_unpackhi_i32x4(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 4, v1); +} + +inline void v_store_interleave( unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,16,17,18,19,0,0,0,0,4,5,6,7); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 20,21,22,23,0,0,0,0,8,9,10,11,24,25,26,27); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,0,0,0,12,13,14,15,28,29,30,31,0,0,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,4,5,6,7,16,17,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,1,2,3,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 24,25,26,27,4,5,6,7,8,9,10,11,28,29,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 4, t11); + wasm_v128_store(ptr + 8, t12); +} + +inline void v_store_interleave(unsigned* ptr, const v_uint32x4& a, const v_uint32x4& b, + const v_uint32x4& c, const v_uint32x4& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v_uint32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + wasm_v128_store(ptr, v0.val); + wasm_v128_store(ptr + 4, v1.val); + wasm_v128_store(ptr + 8, v2.val); + wasm_v128_store(ptr + 12, v3.val); +} + +// 2-channel, float only +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i32x4(a.val, b.val); + v128_t v1 = wasm_unpackhi_i32x4(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 4, v1); +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t t00 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,16,17,18,19,0,0,0,0,4,5,6,7); + v128_t t01 = wasm_v8x16_shuffle(a.val, b.val, 20,21,22,23,0,0,0,0,8,9,10,11,24,25,26,27); + v128_t t02 = wasm_v8x16_shuffle(a.val, b.val, 0,0,0,0,12,13,14,15,28,29,30,31,0,0,0,0); + + v128_t t10 = wasm_v8x16_shuffle(t00, c.val, 0,1,2,3,4,5,6,7,16,17,18,19,12,13,14,15); + v128_t t11 = wasm_v8x16_shuffle(t01, c.val, 0,1,2,3,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t t12 = wasm_v8x16_shuffle(t02, c.val, 24,25,26,27,4,5,6,7,8,9,10,11,28,29,30,31); + + wasm_v128_store(ptr, t10); + wasm_v128_store(ptr + 4, t11); + wasm_v128_store(ptr + 8, t12); +} + +inline void v_store_interleave(float* ptr, const v_float32x4& a, const v_float32x4& b, + const v_float32x4& c, const v_float32x4& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v_float32x4 v0, v1, v2, v3; + v_transpose4x4(a, b, c, d, v0, v1, v2, v3); + + wasm_v128_store(ptr, v0.val); + wasm_v128_store(ptr + 4, v1.val); + wasm_v128_store(ptr + 8, v2.val); + wasm_v128_store(ptr + 12, v3.val); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i64x2(a.val, b.val); + v128_t v1 = wasm_unpackhi_i64x2(a.val, b.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_v8x16_shuffle(a.val, b.val, 0,1,2,3,4,5,6,7,16,17,18,19,20,21,22,23); + v128_t v1 = wasm_v8x16_shuffle(a.val, c.val, 16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15); + v128_t v2 = wasm_v8x16_shuffle(b.val, c.val, 8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); + wasm_v128_store(ptr + 4, v2); +} + +inline void v_store_interleave(uint64 *ptr, const v_uint64x2& a, const v_uint64x2& b, + const v_uint64x2& c, const v_uint64x2& d, + hal::StoreMode /*mode*/ = hal::STORE_UNALIGNED) +{ + v128_t v0 = wasm_unpacklo_i64x2(a.val, b.val); + v128_t v1 = wasm_unpacklo_i64x2(c.val, d.val); + v128_t v2 = wasm_unpackhi_i64x2(a.val, b.val); + v128_t v3 = wasm_unpackhi_i64x2(c.val, d.val); + + wasm_v128_store(ptr, v0); + wasm_v128_store(ptr + 2, v1); + wasm_v128_store(ptr + 4, v2); + wasm_v128_store(ptr + 6, v3); +} + +#define OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(_Tpvec0, _Tp0, suffix0, _Tpvec1, _Tp1, suffix1) \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0 ) \ +{ \ + _Tpvec1 a1, b1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0 ) \ +{ \ + _Tpvec1 a1, b1, c1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ +} \ +inline void v_load_deinterleave( const _Tp0* ptr, _Tpvec0& a0, _Tpvec0& b0, _Tpvec0& c0, _Tpvec0& d0 ) \ +{ \ + _Tpvec1 a1, b1, c1, d1; \ + v_load_deinterleave((const _Tp1*)ptr, a1, b1, c1, d1); \ + a0 = v_reinterpret_as_##suffix0(a1); \ + b0 = v_reinterpret_as_##suffix0(b1); \ + c0 = v_reinterpret_as_##suffix0(c1); \ + d0 = v_reinterpret_as_##suffix0(d1); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, mode); \ +} \ +inline void v_store_interleave( _Tp0* ptr, const _Tpvec0& a0, const _Tpvec0& b0, \ + const _Tpvec0& c0, const _Tpvec0& d0, \ + hal::StoreMode mode = hal::STORE_UNALIGNED ) \ +{ \ + _Tpvec1 a1 = v_reinterpret_as_##suffix1(a0); \ + _Tpvec1 b1 = v_reinterpret_as_##suffix1(b0); \ + _Tpvec1 c1 = v_reinterpret_as_##suffix1(c0); \ + _Tpvec1 d1 = v_reinterpret_as_##suffix1(d0); \ + v_store_interleave((_Tp1*)ptr, a1, b1, c1, d1, mode); \ +} + +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int8x16, schar, s8, v_uint8x16, uchar, u8) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int16x8, short, s16, v_uint16x8, ushort, u16) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int32x4, int, s32, v_uint32x4, unsigned, u32) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_int64x2, int64, s64, v_uint64x2, uint64, u64) +OPENCV_HAL_IMPL_WASM_LOADSTORE_INTERLEAVE(v_float64x2, double, f64, v_uint64x2, uint64, u64) + +inline v_float32x4 v_cvt_f32(const v_int32x4& a) +{ + return v_float32x4(wasm_f32x4_convert_i32x4(a.val)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a) +{ + double a_[2]; + wasm_v128_store(a_, a.val); + float c_[4]; + c_[0] = (float)(a_[0]); + c_[1] = (float)(a_[1]); + c_[2] = 0; + c_[3] = 0; + return v_float32x4(wasm_v128_load(c_)); +} + +inline v_float32x4 v_cvt_f32(const v_float64x2& a, const v_float64x2& b) +{ + double a_[2], b_[2]; + wasm_v128_store(a_, a.val); + wasm_v128_store(b_, b.val); + float c_[4]; + c_[0] = (float)(a_[0]); + c_[1] = (float)(a_[1]); + c_[2] = (float)(b_[0]); + c_[3] = (float)(b_[1]); + return v_float32x4(wasm_v128_load(c_)); +} + +inline v_float64x2 v_cvt_f64(const v_int32x4& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t p = v128_cvti32x4_i64x2(a.val); + return v_float64x2(wasm_f64x2_convert_i64x2(p)); +#else + int a_[4]; + wasm_v128_store(a_, a.val); + double c_[2]; + c_[0] = (double)(a_[0]); + c_[1] = (double)(a_[1]); + return v_float64x2(wasm_v128_load(c_)); +#endif +} + +inline v_float64x2 v_cvt_f64_high(const v_int32x4& a) +{ +#ifdef __wasm_unimplemented_simd128__ + v128_t p = v128_cvti32x4_i64x2_high(a.val); + return v_float64x2(wasm_f64x2_convert_i64x2(p)); +#else + int a_[4]; + wasm_v128_store(a_, a.val); + double c_[2]; + c_[0] = (double)(a_[2]); + c_[1] = (double)(a_[3]); + return v_float64x2(wasm_v128_load(c_)); +#endif +} + +inline v_float64x2 v_cvt_f64(const v_float32x4& a) +{ + float a_[4]; + wasm_v128_store(a_, a.val); + double c_[2]; + c_[0] = (double)(a_[0]); + c_[1] = (double)(a_[1]); + return v_float64x2(wasm_v128_load(c_)); +} + +inline v_float64x2 v_cvt_f64_high(const v_float32x4& a) +{ + float a_[4]; + wasm_v128_store(a_, a.val); + double c_[2]; + c_[0] = (double)(a_[2]); + c_[1] = (double)(a_[3]); + return v_float64x2(wasm_v128_load(c_)); +} + +inline v_float64x2 v_cvt_f64(const v_int64x2& a) +{ +#ifdef __wasm_unimplemented_simd128__ + return v_float64x2(wasm_f64x2_convert_i64x2(a.val)); +#else + int64 a_[2]; + wasm_v128_store(a_, a.val); + double c_[2]; + c_[0] = (double)(a_[0]); + c_[1] = (double)(a_[1]); + return v_float64x2(wasm_v128_load(c_)); +#endif +} + +////////////// Lookup table access //////////////////// + +inline v_int8x16 v_lut(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], + tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]); +} +inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[0]+1], tab[idx[1]], tab[idx[1]+1], tab[idx[2]], tab[idx[2]+1], tab[idx[3]], tab[idx[3]+1], + tab[idx[4]], tab[idx[4]+1], tab[idx[5]], tab[idx[5]+1], tab[idx[6]], tab[idx[6]+1], tab[idx[7]], tab[idx[7]+1]); +} +inline v_int8x16 v_lut_quads(const schar* tab, const int* idx) +{ + return v_int8x16(tab[idx[0]], tab[idx[0]+1], tab[idx[0]+2], tab[idx[0]+3], tab[idx[1]], tab[idx[1]+1], tab[idx[1]+2], tab[idx[1]+3], + tab[idx[2]], tab[idx[2]+1], tab[idx[2]+2], tab[idx[2]+3], tab[idx[3]], tab[idx[3]+1], tab[idx[3]+2], tab[idx[3]+3]); +} +inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } +inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } + +inline v_int16x8 v_lut(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], + tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]); +} +inline v_int16x8 v_lut_pairs(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[0]+1], tab[idx[1]], tab[idx[1]+1], + tab[idx[2]], tab[idx[2]+1], tab[idx[3]], tab[idx[3]+1]); +} +inline v_int16x8 v_lut_quads(const short* tab, const int* idx) +{ + return v_int16x8(tab[idx[0]], tab[idx[0]+1], tab[idx[0]+2], tab[idx[0]+3], + tab[idx[1]], tab[idx[1]+1], tab[idx[1]+2], tab[idx[1]+3]); +} +inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); } +inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); } +inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((const short *)tab, idx)); } + +inline v_int32x4 v_lut(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[1]], + tab[idx[2]], tab[idx[3]]); +} +inline v_int32x4 v_lut_pairs(const int* tab, const int* idx) +{ + return v_int32x4(tab[idx[0]], tab[idx[0]+1], + tab[idx[1]], tab[idx[1]+1]); +} +inline v_int32x4 v_lut_quads(const int* tab, const int* idx) +{ + return v_int32x4(wasm_v128_load(tab + idx[0])); +} +inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int *)tab, idx)); } +inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int *)tab, idx)); } +inline v_uint32x4 v_lut_quads(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_quads((const int *)tab, idx)); } + +inline v_int64x2 v_lut(const int64_t* tab, const int* idx) +{ + return v_int64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx) +{ + return v_int64x2(wasm_v128_load(tab + idx[0])); +} +inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); } +inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); } + +inline v_float32x4 v_lut(const float* tab, const int* idx) +{ + return v_float32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]); +} +inline v_float32x4 v_lut_pairs(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_pairs((const int *)tab, idx)); } +inline v_float32x4 v_lut_quads(const float* tab, const int* idx) { return v_reinterpret_as_f32(v_lut_quads((const int *)tab, idx)); } + +inline v_float64x2 v_lut(const double* tab, const int* idx) +{ + return v_float64x2(tab[idx[0]], tab[idx[1]]); +} +inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) +{ + return v_float64x2(wasm_v128_load(tab + idx[0])); +} + +inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec) +{ + return v_int32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); +} + +inline v_uint32x4 v_lut(const unsigned* tab, const v_int32x4& idxvec) +{ + return v_reinterpret_as_u32(v_lut((const int *)tab, idxvec)); +} + +inline v_float32x4 v_lut(const float* tab, const v_int32x4& idxvec) +{ + return v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); +} + +inline v_float64x2 v_lut(const double* tab, const v_int32x4& idxvec) +{ + return v_float64x2(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)]); +} + +// loads pairs from the table and deinterleaves them, e.g. returns: +// x = (tab[idxvec[0], tab[idxvec[1]], tab[idxvec[2]], tab[idxvec[3]]), +// y = (tab[idxvec[0]+1], tab[idxvec[1]+1], tab[idxvec[2]+1], tab[idxvec[3]+1]) +// note that the indices are float's indices, not the float-pair indices. +// in theory, this function can be used to implement bilinear interpolation, +// when idxvec are the offsets within the image. +inline void v_lut_deinterleave(const float* tab, const v_int32x4& idxvec, v_float32x4& x, v_float32x4& y) +{ + x = v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)]); + y = v_float32x4(tab[wasm_i32x4_extract_lane(idxvec.val, 0)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 1)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 2)+1], + tab[wasm_i32x4_extract_lane(idxvec.val, 3)+1]); +} + +inline void v_lut_deinterleave(const double* tab, const v_int32x4& idxvec, v_float64x2& x, v_float64x2& y) +{ + v128_t xy0 = wasm_v128_load(tab + wasm_i32x4_extract_lane(idxvec.val, 0)); + v128_t xy1 = wasm_v128_load(tab + wasm_i32x4_extract_lane(idxvec.val, 1)); + x.val = wasm_unpacklo_i64x2(xy0, xy1); + y.val = wasm_unpacklo_i64x2(xy0, xy1); +} + +inline v_int8x16 v_interleave_pairs(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15)); +} +inline v_uint8x16 v_interleave_pairs(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_pairs(v_reinterpret_as_s8(vec))); } +inline v_int8x16 v_interleave_quads(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,4,1,5,2,6,3,7,8,12,9,13,10,14,11,15)); +} +inline v_uint8x16 v_interleave_quads(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_interleave_quads(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_interleave_pairs(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15)); +} +inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); } +inline v_int16x8 v_interleave_quads(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,8,9,2,3,10,11,4,5,12,13,6,7,14,15)); +} +inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_interleave_pairs(const v_int32x4& vec) +{ + return v_int32x4(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,8,9,10,11,4,5,6,7,12,13,14,15)); +} +inline v_uint32x4 v_interleave_pairs(const v_uint32x4& vec) { return v_reinterpret_as_u32(v_interleave_pairs(v_reinterpret_as_s32(vec))); } +inline v_float32x4 v_interleave_pairs(const v_float32x4& vec) +{ + return v_float32x4(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,8,9,10,11,4,5,6,7,12,13,14,15)); +} + +inline v_int8x16 v_pack_triplets(const v_int8x16& vec) +{ + return v_int8x16(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,4,5,6,8,9,10,12,13,14,16,16,16,16)); +} +inline v_uint8x16 v_pack_triplets(const v_uint8x16& vec) { return v_reinterpret_as_u8(v_pack_triplets(v_reinterpret_as_s8(vec))); } + +inline v_int16x8 v_pack_triplets(const v_int16x8& vec) +{ + return v_int16x8(wasm_v8x16_shuffle(vec.val, vec.val, 0,1,2,3,4,5,8,9,10,11,12,13,14,15,6,7)); +} +inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); } + +inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; } +inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; } +inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; } + +template +inline typename _Tp::lane_type v_extract_n(const _Tp& a) +{ + return v_rotate_right(a).get0(); +} + +template +inline v_uint32x4 v_broadcast_element(const v_uint32x4& a) +{ + return v_setall_u32(v_extract_n(a)); +} +template +inline v_int32x4 v_broadcast_element(const v_int32x4& a) +{ + return v_setall_s32(v_extract_n(a)); +} +template +inline v_float32x4 v_broadcast_element(const v_float32x4& a) +{ + return v_setall_f32(v_extract_n(a)); +} + + +////////////// FP16 support /////////////////////////// + +inline v_float32x4 v_load_expand(const float16_t* ptr) +{ + float a[4]; + for (int i = 0; i < 4; i++) + a[i] = ptr[i]; + return v_float32x4(wasm_v128_load(a)); +} + +inline void v_pack_store(float16_t* ptr, const v_float32x4& v) +{ + double v_[4]; + wasm_v128_store(v_, v.val); + ptr[0] = float16_t(v_[0]); + ptr[1] = float16_t(v_[1]); + ptr[2] = float16_t(v_[2]); + ptr[3] = float16_t(v_[3]); +} + +inline void v_cleanup() {} + +CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END + +//! @endcond + +} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/msa_macros.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/msa_macros.h new file mode 100644 index 0000000..bd6ddb1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/msa_macros.h @@ -0,0 +1,1558 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_HAL_MSA_MACROS_H +#define OPENCV_CORE_HAL_MSA_MACROS_H + +#ifdef __mips_msa +#include "msa.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define 64 bits vector types */ +typedef signed char v8i8 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned char v8u8 __attribute__ ((vector_size(8), aligned(8))); +typedef short v4i16 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned short v4u16 __attribute__ ((vector_size(8), aligned(8))); +typedef int v2i32 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned int v2u32 __attribute__ ((vector_size(8), aligned(8))); +typedef long long v1i64 __attribute__ ((vector_size(8), aligned(8))); +typedef unsigned long long v1u64 __attribute__ ((vector_size(8), aligned(8))); +typedef float v2f32 __attribute__ ((vector_size(8), aligned(8))); +typedef double v1f64 __attribute__ ((vector_size(8), aligned(8))); + + +/* Load values from the given memory a 64-bit vector. */ +#define msa_ld1_s8(__a) (*((v8i8*)(__a))) +#define msa_ld1_s16(__a) (*((v4i16*)(__a))) +#define msa_ld1_s32(__a) (*((v2i32*)(__a))) +#define msa_ld1_s64(__a) (*((v1i64*)(__a))) +#define msa_ld1_u8(__a) (*((v8u8*)(__a))) +#define msa_ld1_u16(__a) (*((v4u16*)(__a))) +#define msa_ld1_u32(__a) (*((v2u32*)(__a))) +#define msa_ld1_u64(__a) (*((v1u64*)(__a))) +#define msa_ld1_f32(__a) (*((v2f32*)(__a))) +#define msa_ld1_f64(__a) (*((v1f64*)(__a))) + +/* Load values from the given memory address to a 128-bit vector */ +#define msa_ld1q_s8(__a) ((v16i8)__builtin_msa_ld_b(__a, 0)) +#define msa_ld1q_s16(__a) ((v8i16)__builtin_msa_ld_h(__a, 0)) +#define msa_ld1q_s32(__a) ((v4i32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_s64(__a) ((v2i64)__builtin_msa_ld_d(__a, 0)) +#define msa_ld1q_u8(__a) ((v16u8)__builtin_msa_ld_b(__a, 0)) +#define msa_ld1q_u16(__a) ((v8u16)__builtin_msa_ld_h(__a, 0)) +#define msa_ld1q_u32(__a) ((v4u32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_u64(__a) ((v2u64)__builtin_msa_ld_d(__a, 0)) +#define msa_ld1q_f32(__a) ((v4f32)__builtin_msa_ld_w(__a, 0)) +#define msa_ld1q_f64(__a) ((v2f64)__builtin_msa_ld_d(__a, 0)) + +/* Store 64bits vector elements values to the given memory address. */ +#define msa_st1_s8(__a, __b) (*((v8i8*)(__a)) = __b) +#define msa_st1_s16(__a, __b) (*((v4i16*)(__a)) = __b) +#define msa_st1_s32(__a, __b) (*((v2i32*)(__a)) = __b) +#define msa_st1_s64(__a, __b) (*((v1i64*)(__a)) = __b) +#define msa_st1_u8(__a, __b) (*((v8u8*)(__a)) = __b) +#define msa_st1_u16(__a, __b) (*((v4u16*)(__a)) = __b) +#define msa_st1_u32(__a, __b) (*((v2u32*)(__a)) = __b) +#define msa_st1_u64(__a, __b) (*((v1u64*)(__a)) = __b) +#define msa_st1_f32(__a, __b) (*((v2f32*)(__a)) = __b) +#define msa_st1_f64(__a, __b) (*((v1f64*)(__a)) = __b) + +/* Store the values of elements in the 128 bits vector __a to the given memory address __a. */ +#define msa_st1q_s8(__a, __b) (__builtin_msa_st_b((v16i8)(__b), __a, 0)) +#define msa_st1q_s16(__a, __b) (__builtin_msa_st_h((v8i16)(__b), __a, 0)) +#define msa_st1q_s32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_s64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) +#define msa_st1q_u8(__a, __b) (__builtin_msa_st_b((v16i8)(__b), __a, 0)) +#define msa_st1q_u16(__a, __b) (__builtin_msa_st_h((v8i16)(__b), __a, 0)) +#define msa_st1q_u32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_u64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) +#define msa_st1q_f32(__a, __b) (__builtin_msa_st_w((v4i32)(__b), __a, 0)) +#define msa_st1q_f64(__a, __b) (__builtin_msa_st_d((v2i64)(__b), __a, 0)) + +/* Store the value of the element with the index __c in vector __a to the given memory address __a. */ +#define msa_st1_lane_s8(__a, __b, __c) (*((int8_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s16(__a, __b, __c) (*((int16_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s32(__a, __b, __c) (*((int32_t*)(__a)) = __b[__c]) +#define msa_st1_lane_s64(__a, __b, __c) (*((int64_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u8(__a, __b, __c) (*((uint8_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u16(__a, __b, __c) (*((uint16_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u32(__a, __b, __c) (*((uint32_t*)(__a)) = __b[__c]) +#define msa_st1_lane_u64(__a, __b, __c) (*((uint64_t*)(__a)) = __b[__c]) +#define msa_st1_lane_f32(__a, __b, __c) (*((float*)(__a)) = __b[__c]) +#define msa_st1_lane_f64(__a, __b, __c) (*((double*)(__a)) = __b[__c]) +#define msa_st1q_lane_s8(__a, __b, __c) (*((int8_t*)(__a)) = (int8_t)__builtin_msa_copy_s_b(__b, __c)) +#define msa_st1q_lane_s16(__a, __b, __c) (*((int16_t*)(__a)) = (int16_t)__builtin_msa_copy_s_h(__b, __c)) +#define msa_st1q_lane_s32(__a, __b, __c) (*((int32_t*)(__a)) = __builtin_msa_copy_s_w(__b, __c)) +#define msa_st1q_lane_s64(__a, __b, __c) (*((int64_t*)(__a)) = __builtin_msa_copy_s_d(__b, __c)) +#define msa_st1q_lane_u8(__a, __b, __c) (*((uint8_t*)(__a)) = (uint8_t)__builtin_msa_copy_u_b((v16i8)(__b), __c)) +#define msa_st1q_lane_u16(__a, __b, __c) (*((uint16_t*)(__a)) = (uint16_t)__builtin_msa_copy_u_h((v8i16)(__b), __c)) +#define msa_st1q_lane_u32(__a, __b, __c) (*((uint32_t*)(__a)) = __builtin_msa_copy_u_w((v4i32)(__b), __c)) +#define msa_st1q_lane_u64(__a, __b, __c) (*((uint64_t*)(__a)) = __builtin_msa_copy_u_d((v2i64)(__b), __c)) +#define msa_st1q_lane_f32(__a, __b, __c) (*((float*)(__a)) = __b[__c]) +#define msa_st1q_lane_f64(__a, __b, __c) (*((double*)(__a)) = __b[__c]) + +/* Duplicate elements for 64-bit doubleword vectors */ +#define msa_dup_n_s8(__a) ((v8i8)__builtin_msa_copy_s_d((v2i64)__builtin_msa_fill_b((int32_t)(__a)), 0)) +#define msa_dup_n_s16(__a) ((v4i16)__builtin_msa_copy_s_d((v2i64)__builtin_msa_fill_h((int32_t)(__a)), 0)) +#define msa_dup_n_s32(__a) ((v2i32){__a, __a}) +#define msa_dup_n_s64(__a) ((v1i64){__a}) +#define msa_dup_n_u8(__a) ((v8u8)__builtin_msa_copy_u_d((v2i64)__builtin_msa_fill_b((int32_t)(__a)), 0)) +#define msa_dup_n_u16(__a) ((v4u16)__builtin_msa_copy_u_d((v2i64)__builtin_msa_fill_h((int32_t)(__a)), 0)) +#define msa_dup_n_u32(__a) ((v2u32){__a, __a}) +#define msa_dup_n_u64(__a) ((v1u64){__a}) +#define msa_dup_n_f32(__a) ((v2f32){__a, __a}) +#define msa_dup_n_f64(__a) ((v1f64){__a}) + +/* Duplicate elements for 128-bit quadword vectors */ +#define msa_dupq_n_s8(__a) (__builtin_msa_fill_b((int32_t)(__a))) +#define msa_dupq_n_s16(__a) (__builtin_msa_fill_h((int32_t)(__a))) +#define msa_dupq_n_s32(__a) (__builtin_msa_fill_w((int32_t)(__a))) +#define msa_dupq_n_s64(__a) (__builtin_msa_fill_d((int64_t)(__a))) +#define msa_dupq_n_u8(__a) ((v16u8)__builtin_msa_fill_b((int32_t)(__a))) +#define msa_dupq_n_u16(__a) ((v8u16)__builtin_msa_fill_h((int32_t)(__a))) +#define msa_dupq_n_u32(__a) ((v4u32)__builtin_msa_fill_w((int32_t)(__a))) +#define msa_dupq_n_u64(__a) ((v2u64)__builtin_msa_fill_d((int64_t)(__a))) +#define msa_dupq_n_f32(__a) ((v4f32){__a, __a, __a, __a}) +#define msa_dupq_n_f64(__a) ((v2f64){__a, __a}) +#define msa_dupq_lane_s8(__a, __b) (__builtin_msa_splat_b(__a, __b)) +#define msa_dupq_lane_s16(__a, __b) (__builtin_msa_splat_h(__a, __b)) +#define msa_dupq_lane_s32(__a, __b) (__builtin_msa_splat_w(__a, __b)) +#define msa_dupq_lane_s64(__a, __b) (__builtin_msa_splat_d(__a, __b)) +#define msa_dupq_lane_u8(__a, __b) ((v16u8)__builtin_msa_splat_b((v16i8)(__a), __b)) +#define msa_dupq_lane_u16(__a, __b) ((v8u16)__builtin_msa_splat_h((v8i16)(__a), __b)) +#define msa_dupq_lane_u32(__a, __b) ((v4u32)__builtin_msa_splat_w((v4i32)(__a), __b)) +#define msa_dupq_lane_u64(__a, __b) ((v2u64)__builtin_msa_splat_d((v2i64)(__a), __b)) + +/* Create a 64 bits vector */ +#define msa_create_s8(__a) ((v8i8)((uint64_t)(__a))) +#define msa_create_s16(__a) ((v4i16)((uint64_t)(__a))) +#define msa_create_s32(__a) ((v2i32)((uint64_t)(__a))) +#define msa_create_s64(__a) ((v1i64)((uint64_t)(__a))) +#define msa_create_u8(__a) ((v8u8)((uint64_t)(__a))) +#define msa_create_u16(__a) ((v4u16)((uint64_t)(__a))) +#define msa_create_u32(__a) ((v2u32)((uint64_t)(__a))) +#define msa_create_u64(__a) ((v1u64)((uint64_t)(__a))) +#define msa_create_f32(__a) ((v2f32)((uint64_t)(__a))) +#define msa_create_f64(__a) ((v1f64)((uint64_t)(__a))) + +/* Sign extends or zero extends each element in a 64 bits vector to twice its original length, and places the results in a 128 bits vector. */ +/*Transform v8i8 to v8i16*/ +#define msa_movl_s8(__a) \ +((v8i16){(__a)[0], (__a)[1], (__a)[2], (__a)[3], \ + (__a)[4], (__a)[5], (__a)[6], (__a)[7]}) + +/*Transform v8u8 to v8u16*/ +#define msa_movl_u8(__a) \ +((v8u16){(__a)[0], (__a)[1], (__a)[2], (__a)[3], \ + (__a)[4], (__a)[5], (__a)[6], (__a)[7]}) + +/*Transform v4i16 to v8i16*/ +#define msa_movl_s16(__a) ((v4i32){(__a)[0], (__a)[1], (__a)[2], (__a)[3]}) + +/*Transform v2i32 to v4i32*/ +#define msa_movl_s32(__a) ((v2i64){(__a)[0], (__a)[1]}) + +/*Transform v4u16 to v8u16*/ +#define msa_movl_u16(__a) ((v4u32){(__a)[0], (__a)[1], (__a)[2], (__a)[3]}) + +/*Transform v2u32 to v4u32*/ +#define msa_movl_u32(__a) ((v2u64){(__a)[0], (__a)[1]}) + +/* Copies the least significant half of each element of a 128 bits vector into the corresponding elements of a 64 bits vector. */ +#define msa_movn_s16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)(__a)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_s32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_s64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)(__a)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_movn_u64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* qmovn */ +#define msa_qmovn_s16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_s_h((v8i16)(__a), 7)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_s32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_s_w((v4i32)(__a), 15)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_s64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_s_d((v2i64)(__a), 31)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u16(__a) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)(__a), 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u32(__a) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)(__a), 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_qmovn_u64(__a) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)(__a), 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* qmovun */ +#define msa_qmovun_s16(__a) \ +({ \ + v8i16 __d = __builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qmovun_s32(__a) \ +({ \ + v4i32 __d = __builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qmovun_s64(__a) \ +({ \ + v2i64 __d = __builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, and places the results in a 64 bits vector. */ +#define msa_shrn_n_s16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srai_h((v8i16)(__a), (int)(__b))); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_s32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srai_w((v4i32)(__a), (int)(__b))); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_s64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srai_d((v2i64)(__a), (int)(__b))); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srli_h((v8i16)(__a), (int)(__b))); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srli_w((v4i32)(__a), (int)(__b))); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_shrn_n_u64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srli_d((v2i64)(__a), (int)(__b))); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, and places the results in a 64 bits vector. */ +#define msa_rshrn_n_s16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srari_h((v8i16)(__a), (int)__b)); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_s32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srari_w((v4i32)(__a), (int)__b)); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_s64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srari_d((v2i64)(__a), (int)__b)); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u16(__a, __b) \ +({ \ + v16i8 __d = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_srlri_h((v8i16)(__a), (int)__b)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u32(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_srlri_w((v4i32)(__a), (int)__b)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +#define msa_rshrn_n_u64(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_srlri_d((v2i64)(__a), (int)__b)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__d, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, saturate the results and them in a 64 bits vector. */ +#define msa_qrshrn_n_s16(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__a), (int)(__b)), 7); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__d); \ + (v8i8)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_s32(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__a), (int)(__b)), 15); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__d); \ + (v4i16)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_s64(__a, __b) \ +({ \ + v2i64 __d = __builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__a), (int)(__b)), 31); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__d); \ + (v2i32)__builtin_msa_copy_s_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u16(__a, __b) \ +({ \ + v8u16 __d = __builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__a), (int)(__b)), 7); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__d); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u32(__a, __b) \ +({ \ + v4u32 __d = __builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__a), (int)(__b)), 15); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__d); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrn_n_u64(__a, __b) \ +({ \ + v2u64 __d = __builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__a), (int)(__b)), 31); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__d); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* Right shift elements in a 128 bits vector by an immediate value, saturate the results and them in a 64 bits vector. + Input is signed and output is unsigned. */ +#define msa_qrshrun_n_s16(__a, __b) \ +({ \ + v8i16 __d = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)), (int)(__b)); \ + v16i8 __e = __builtin_msa_pckev_b(__builtin_msa_fill_b(0), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ + (v8u8)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrun_n_s32(__a, __b) \ +({ \ + v4i32 __d = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)), (int)(__b)); \ + v8i16 __e = __builtin_msa_pckev_h(__builtin_msa_fill_h(0), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ + (v4u16)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +#define msa_qrshrun_n_s64(__a, __b) \ +({ \ + v2i64 __d = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)), (int)(__b)); \ + v4i32 __e = __builtin_msa_pckev_w(__builtin_msa_fill_w(0), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ + (v2u32)__builtin_msa_copy_u_d((v2i64)__e, 0); \ +}) + +/* pack */ +#define msa_pack_s16(__a, __b) (__builtin_msa_pckev_b((v16i8)(__b), (v16i8)(__a))) +#define msa_pack_s32(__a, __b) (__builtin_msa_pckev_h((v8i16)(__b), (v8i16)(__a))) +#define msa_pack_s64(__a, __b) (__builtin_msa_pckev_w((v4i32)(__b), (v4i32)(__a))) +#define msa_pack_u16(__a, __b) ((v16u8)__builtin_msa_pckev_b((v16i8)(__b), (v16i8)(__a))) +#define msa_pack_u32(__a, __b) ((v8u16)__builtin_msa_pckev_h((v8i16)(__b), (v8i16)(__a))) +#define msa_pack_u64(__a, __b) ((v4u32)__builtin_msa_pckev_w((v4i32)(__b), (v4i32)(__a))) + +/* qpack */ +#define msa_qpack_s16(__a, __b) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_s_h((v8i16)(__b), 7), (v16i8)__builtin_msa_sat_s_h((v8i16)(__a), 7))) +#define msa_qpack_s32(__a, __b) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_s_w((v4i32)(__b), 15), (v8i16)__builtin_msa_sat_s_w((v4i32)(__a), 15))) +#define msa_qpack_s64(__a, __b) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_s_d((v2i64)(__b), 31), (v4i32)__builtin_msa_sat_s_d((v2i64)(__a), 31))) +#define msa_qpack_u16(__a, __b) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)(__b), 7), (v16i8)__builtin_msa_sat_u_h((v8u16)(__a), 7))) +#define msa_qpack_u32(__a, __b) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)(__b), 15), (v8i16)__builtin_msa_sat_u_w((v4u32)(__a), 15))) +#define msa_qpack_u64(__a, __b) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)(__b), 31), (v4i32)__builtin_msa_sat_u_d((v2u64)(__a), 31))) + +/* qpacku */ +#define msa_qpacku_s16(__a, __b) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__b))), 7), \ + (v16i8)__builtin_msa_sat_u_h((v8u16)(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a))), 7))) +#define msa_qpacku_s32(__a, __b) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__b))), 15), \ + (v8i16)__builtin_msa_sat_u_w((v4u32)(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a))), 15))) +#define msa_qpacku_s64(__a, __b) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__b))), 31), \ + (v4i32)__builtin_msa_sat_u_d((v2u64)(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a))), 31))) + +/* packr */ +#define msa_packr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_srai_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srai_h((v8i16)(__a), (int)(__c)))) +#define msa_packr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_srai_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srai_w((v4i32)(__a), (int)(__c)))) +#define msa_packr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_srai_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srai_d((v2i64)(__a), (int)(__c)))) +#define msa_packr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_srli_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srli_h((v8i16)(__a), (int)(__c)))) +#define msa_packr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_srli_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srli_w((v4i32)(__a), (int)(__c)))) +#define msa_packr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_srli_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srli_d((v2i64)(__a), (int)(__c)))) + +/* rpackr */ +#define msa_rpackr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_srari_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srari_h((v8i16)(__a), (int)(__c)))) +#define msa_rpackr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_srari_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srari_w((v4i32)(__a), (int)(__c)))) +#define msa_rpackr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_srari_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srari_d((v2i64)(__a), (int)(__c)))) +#define msa_rpackr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_srlri_h((v8i16)(__b), (int)(__c)), (v16i8)__builtin_msa_srlri_h((v8i16)(__a), (int)(__c)))) +#define msa_rpackr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_srlri_w((v4i32)(__b), (int)(__c)), (v8i16)__builtin_msa_srlri_w((v4i32)(__a), (int)(__c)))) +#define msa_rpackr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_srlri_d((v2i64)(__b), (int)(__c)), (v4i32)__builtin_msa_srlri_d((v2i64)(__a), (int)(__c)))) + +/* qrpackr */ +#define msa_qrpackr_s16(__a, __b, __c) \ +(__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__b), (int)(__c)), 7), \ + (v16i8)__builtin_msa_sat_s_h(__builtin_msa_srari_h((v8i16)(__a), (int)(__c)), 7))) +#define msa_qrpackr_s32(__a, __b, __c) \ +(__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__b), (int)(__c)), 15), \ + (v8i16)__builtin_msa_sat_s_w(__builtin_msa_srari_w((v4i32)(__a), (int)(__c)), 15))) +#define msa_qrpackr_s64(__a, __b, __c) \ +(__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__b), (int)(__c)), 31), \ + (v4i32)__builtin_msa_sat_s_d(__builtin_msa_srari_d((v2i64)(__a), (int)(__c)), 31))) +#define msa_qrpackr_u16(__a, __b, __c) \ +((v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__b), (int)(__c)), 7), \ + (v16i8)__builtin_msa_sat_u_h((v8u16)__builtin_msa_srlri_h((v8i16)(__a), (int)(__c)), 7))) +#define msa_qrpackr_u32(__a, __b, __c) \ +((v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__b), (int)(__c)), 15), \ + (v8i16)__builtin_msa_sat_u_w((v4u32)__builtin_msa_srlri_w((v4i32)(__a), (int)(__c)), 15))) +#define msa_qrpackr_u64(__a, __b, __c) \ +((v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__b), (int)(__c)), 31), \ + (v4i32)__builtin_msa_sat_u_d((v2u64)__builtin_msa_srlri_d((v2i64)(__a), (int)(__c)), 31))) + +/* qrpackru */ +#define msa_qrpackru_s16(__a, __b, __c) \ +({ \ + v8i16 __d = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__a)), (int)(__c)); \ + v8i16 __e = __builtin_msa_srlri_h(__builtin_msa_max_s_h(__builtin_msa_fill_h(0), (v8i16)(__b)), (int)(__c)); \ + (v16u8)__builtin_msa_pckev_b((v16i8)__builtin_msa_sat_u_h((v8u16)__e, 7), (v16i8)__builtin_msa_sat_u_h((v8u16)__d, 7)); \ +}) + +#define msa_qrpackru_s32(__a, __b, __c) \ +({ \ + v4i32 __d = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__a)), (int)(__c)); \ + v4i32 __e = __builtin_msa_srlri_w(__builtin_msa_max_s_w(__builtin_msa_fill_w(0), (v4i32)(__b)), (int)(__c)); \ + (v8u16)__builtin_msa_pckev_h((v8i16)__builtin_msa_sat_u_w((v4u32)__e, 15), (v8i16)__builtin_msa_sat_u_w((v4u32)__d, 15)); \ +}) + +#define msa_qrpackru_s64(__a, __b, __c) \ +({ \ + v2i64 __d = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__a)), (int)(__c)); \ + v2i64 __e = __builtin_msa_srlri_d(__builtin_msa_max_s_d(__builtin_msa_fill_d(0), (v2i64)(__b)), (int)(__c)); \ + (v4u32)__builtin_msa_pckev_w((v4i32)__builtin_msa_sat_u_d((v2u64)__e, 31), (v4i32)__builtin_msa_sat_u_d((v2u64)__d, 31)); \ +}) + +/* Minimum values between corresponding elements in the two vectors are written to the returned vector. */ +#define msa_minq_s8(__a, __b) (__builtin_msa_min_s_b(__a, __b)) +#define msa_minq_s16(__a, __b) (__builtin_msa_min_s_h(__a, __b)) +#define msa_minq_s32(__a, __b) (__builtin_msa_min_s_w(__a, __b)) +#define msa_minq_s64(__a, __b) (__builtin_msa_min_s_d(__a, __b)) +#define msa_minq_u8(__a, __b) ((v16u8)__builtin_msa_min_u_b(__a, __b)) +#define msa_minq_u16(__a, __b) ((v8u16)__builtin_msa_min_u_h(__a, __b)) +#define msa_minq_u32(__a, __b) ((v4u32)__builtin_msa_min_u_w(__a, __b)) +#define msa_minq_u64(__a, __b) ((v2u64)__builtin_msa_min_u_d(__a, __b)) +#define msa_minq_f32(__a, __b) (__builtin_msa_fmin_w(__a, __b)) +#define msa_minq_f64(__a, __b) (__builtin_msa_fmin_d(__a, __b)) + +/* Maximum values between corresponding elements in the two vectors are written to the returned vector. */ +#define msa_maxq_s8(__a, __b) (__builtin_msa_max_s_b(__a, __b)) +#define msa_maxq_s16(__a, __b) (__builtin_msa_max_s_h(__a, __b)) +#define msa_maxq_s32(__a, __b) (__builtin_msa_max_s_w(__a, __b)) +#define msa_maxq_s64(__a, __b) (__builtin_msa_max_s_d(__a, __b)) +#define msa_maxq_u8(__a, __b) ((v16u8)__builtin_msa_max_u_b(__a, __b)) +#define msa_maxq_u16(__a, __b) ((v8u16)__builtin_msa_max_u_h(__a, __b)) +#define msa_maxq_u32(__a, __b) ((v4u32)__builtin_msa_max_u_w(__a, __b)) +#define msa_maxq_u64(__a, __b) ((v2u64)__builtin_msa_max_u_d(__a, __b)) +#define msa_maxq_f32(__a, __b) (__builtin_msa_fmax_w(__a, __b)) +#define msa_maxq_f64(__a, __b) (__builtin_msa_fmax_d(__a, __b)) + +/* Vector type reinterpretion */ +#define MSA_TPV_REINTERPRET(_Tpv, Vec) ((_Tpv)(Vec)) + +/* Add the odd elements in vector __a with the even elements in vector __b to double width elements in the returned vector. */ +/* v8i16 msa_hadd_s16 ((v16i8)__a, (v16i8)__b) */ +#define msa_hadd_s16(__a, __b) (__builtin_msa_hadd_s_h((v16i8)(__a), (v16i8)(__b))) +/* v4i32 msa_hadd_s32 ((v8i16)__a, (v8i16)__b) */ +#define msa_hadd_s32(__a, __b) (__builtin_msa_hadd_s_w((v8i16)(__a), (v8i16)(__b))) +/* v2i64 msa_hadd_s64 ((v4i32)__a, (v4i32)__b) */ +#define msa_hadd_s64(__a, __b) (__builtin_msa_hadd_s_d((v4i32)(__a), (v4i32)(__b))) + +/* Copy even elements in __a to the left half and even elements in __b to the right half and return the result vector. */ +#define msa_pckev_s8(__a, __b) (__builtin_msa_pckev_b((v16i8)(__a), (v16i8)(__b))) +#define msa_pckev_s16(__a, __b) (__builtin_msa_pckev_h((v8i16)(__a), (v8i16)(__b))) +#define msa_pckev_s32(__a, __b) (__builtin_msa_pckev_w((v4i32)(__a), (v4i32)(__b))) +#define msa_pckev_s64(__a, __b) (__builtin_msa_pckev_d((v2i64)(__a), (v2i64)(__b))) + +/* Copy even elements in __a to the left half and even elements in __b to the right half and return the result vector. */ +#define msa_pckod_s8(__a, __b) (__builtin_msa_pckod_b((v16i8)(__a), (v16i8)(__b))) +#define msa_pckod_s16(__a, __b) (__builtin_msa_pckod_h((v8i16)(__a), (v8i16)(__b))) +#define msa_pckod_s32(__a, __b) (__builtin_msa_pckod_w((v4i32)(__a), (v4i32)(__b))) +#define msa_pckod_s64(__a, __b) (__builtin_msa_pckod_d((v2i64)(__a), (v2i64)(__b))) + +#ifdef _MIPSEB +#define LANE_IMM0_1(x) (0b1 - ((x) & 0b1)) +#define LANE_IMM0_3(x) (0b11 - ((x) & 0b11)) +#define LANE_IMM0_7(x) (0b111 - ((x) & 0b111)) +#define LANE_IMM0_15(x) (0b1111 - ((x) & 0b1111)) +#else +#define LANE_IMM0_1(x) ((x) & 0b1) +#define LANE_IMM0_3(x) ((x) & 0b11) +#define LANE_IMM0_7(x) ((x) & 0b111) +#define LANE_IMM0_15(x) ((x) & 0b1111) +#endif + +#define msa_get_lane_u8(__a, __b) ((uint8_t)(__a)[LANE_IMM0_7(__b)]) +#define msa_get_lane_s8(__a, __b) ((int8_t)(__a)[LANE_IMM0_7(__b)]) +#define msa_get_lane_u16(__a, __b) ((uint16_t)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_s16(__a, __b) ((int16_t)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_u32(__a, __b) ((uint32_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_s32(__a, __b) ((int32_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_f32(__a, __b) ((float)(__a)[LANE_IMM0_3(__b)]) +#define msa_get_lane_s64(__a, __b) ((int64_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_u64(__a, __b) ((uint64_t)(__a)[LANE_IMM0_1(__b)]) +#define msa_get_lane_f64(__a, __b) ((double)(__a)[LANE_IMM0_1(__b)]) +#define msa_getq_lane_u8(__a, imm0_15) ((uint8_t)__builtin_msa_copy_u_b((v16i8)(__a), imm0_15)) +#define msa_getq_lane_s8(__a, imm0_15) ((int8_t)__builtin_msa_copy_s_b(__a, imm0_15)) +#define msa_getq_lane_u16(__a, imm0_7) ((uint16_t)__builtin_msa_copy_u_h((v8i16)(__a), imm0_7)) +#define msa_getq_lane_s16(__a, imm0_7) ((int16_t)__builtin_msa_copy_s_h(__a, imm0_7)) +#define msa_getq_lane_u32(__a, imm0_3) __builtin_msa_copy_u_w((v4i32)(__a), imm0_3) +#define msa_getq_lane_s32 __builtin_msa_copy_s_w +#define msa_getq_lane_f32(__a, __b) ((float)(__a)[LANE_IMM0_3(__b)]) +#define msa_getq_lane_f64(__a, __b) ((double)(__a)[LANE_IMM0_1(__b)]) +#if (__mips == 64) +#define msa_getq_lane_u64(__a, imm0_1) __builtin_msa_copy_u_d((v2i64)(__a), imm0_1) +#define msa_getq_lane_s64 __builtin_msa_copy_s_d +#else +#define msa_getq_lane_u64(__a, imm0_1) ((uint64_t)(__a)[LANE_IMM0_1(imm0_1)]) +#define msa_getq_lane_s64(__a, imm0_1) ((int64_t)(__a)[LANE_IMM0_1(imm0_1)]) +#endif + +/* combine */ +#if (__mips == 64) +#define __COMBINE_64_64(__TYPE, a, b) ((__TYPE)((v2u64){((v1u64)(a))[0], ((v1u64)(b))[0]})) +#else +#define __COMBINE_64_64(__TYPE, a, b) ((__TYPE)((v4u32){((v2u32)(a))[0], ((v2u32)(a))[1], \ + ((v2u32)(b))[0], ((v2u32)(b))[1]})) +#endif + +/* v16i8 msa_combine_s8 (v8i8 __a, v8i8 __b) */ +#define msa_combine_s8(__a, __b) __COMBINE_64_64(v16i8, __a, __b) + +/* v8i16 msa_combine_s16(v4i16 __a, v4i16 __b) */ +#define msa_combine_s16(__a, __b) __COMBINE_64_64(v8i16, __a, __b) + +/* v4i32 msa_combine_s32(v2i32 __a, v2i32 __b) */ +#define msa_combine_s32(__a, __b) __COMBINE_64_64(v4i32, __a, __b) + +/* v2i64 msa_combine_s64(v1i64 __a, v1i64 __b) */ +#define msa_combine_s64(__a, __b) __COMBINE_64_64(v2i64, __a, __b) + +/* v4f32 msa_combine_f32(v2f32 __a, v2f32 __b) */ +#define msa_combine_f32(__a, __b) __COMBINE_64_64(v4f32, __a, __b) + +/* v16u8 msa_combine_u8(v8u8 __a, v8u8 __b) */ +#define msa_combine_u8(__a, __b) __COMBINE_64_64(v16u8, __a, __b) + +/* v8u16 msa_combine_u16(v4u16 __a, v4u16 __b) */ +#define msa_combine_u16(__a, __b) __COMBINE_64_64(v8u16, __a, __b) + +/* v4u32 msa_combine_u32(v2u32 __a, v2u32 __b) */ +#define msa_combine_u32(__a, __b) __COMBINE_64_64(v4u32, __a, __b) + +/* v2u64 msa_combine_u64(v1u64 __a, v1u64 __b) */ +#define msa_combine_u64(__a, __b) __COMBINE_64_64(v2u64, __a, __b) + +/* v2f64 msa_combine_f64(v1f64 __a, v1f64 __b) */ +#define msa_combine_f64(__a, __b) __COMBINE_64_64(v2f64, __a, __b) + +/* get_low, get_high */ +#if (__mips == 64) +#define __GET_LOW(__TYPE, a) ((__TYPE)((v1u64)(__builtin_msa_copy_u_d((v2i64)(a), 0)))) +#define __GET_HIGH(__TYPE, a) ((__TYPE)((v1u64)(__builtin_msa_copy_u_d((v2i64)(a), 1)))) +#else +#define __GET_LOW(__TYPE, a) ((__TYPE)(((v2u64)(a))[0])) +#define __GET_HIGH(__TYPE, a) ((__TYPE)(((v2u64)(a))[1])) +#endif + +/* v8i8 msa_get_low_s8(v16i8 __a) */ +#define msa_get_low_s8(__a) __GET_LOW(v8i8, __a) + +/* v4i16 msa_get_low_s16(v8i16 __a) */ +#define msa_get_low_s16(__a) __GET_LOW(v4i16, __a) + +/* v2i32 msa_get_low_s32(v4i32 __a) */ +#define msa_get_low_s32(__a) __GET_LOW(v2i32, __a) + +/* v1i64 msa_get_low_s64(v2i64 __a) */ +#define msa_get_low_s64(__a) __GET_LOW(v1i64, __a) + +/* v8u8 msa_get_low_u8(v16u8 __a) */ +#define msa_get_low_u8(__a) __GET_LOW(v8u8, __a) + +/* v4u16 msa_get_low_u16(v8u16 __a) */ +#define msa_get_low_u16(__a) __GET_LOW(v4u16, __a) + +/* v2u32 msa_get_low_u32(v4u32 __a) */ +#define msa_get_low_u32(__a) __GET_LOW(v2u32, __a) + +/* v1u64 msa_get_low_u64(v2u64 __a) */ +#define msa_get_low_u64(__a) __GET_LOW(v1u64, __a) + +/* v2f32 msa_get_low_f32(v4f32 __a) */ +#define msa_get_low_f32(__a) __GET_LOW(v2f32, __a) + +/* v1f64 msa_get_low_f64(v2f64 __a) */ +#define msa_get_low_f64(__a) __GET_LOW(v1f64, __a) + +/* v8i8 msa_get_high_s8(v16i8 __a) */ +#define msa_get_high_s8(__a) __GET_HIGH(v8i8, __a) + +/* v4i16 msa_get_high_s16(v8i16 __a) */ +#define msa_get_high_s16(__a) __GET_HIGH(v4i16, __a) + +/* v2i32 msa_get_high_s32(v4i32 __a) */ +#define msa_get_high_s32(__a) __GET_HIGH(v2i32, __a) + +/* v1i64 msa_get_high_s64(v2i64 __a) */ +#define msa_get_high_s64(__a) __GET_HIGH(v1i64, __a) + +/* v8u8 msa_get_high_u8(v16u8 __a) */ +#define msa_get_high_u8(__a) __GET_HIGH(v8u8, __a) + +/* v4u16 msa_get_high_u16(v8u16 __a) */ +#define msa_get_high_u16(__a) __GET_HIGH(v4u16, __a) + +/* v2u32 msa_get_high_u32(v4u32 __a) */ +#define msa_get_high_u32(__a) __GET_HIGH(v2u32, __a) + +/* v1u64 msa_get_high_u64(v2u64 __a) */ +#define msa_get_high_u64(__a) __GET_HIGH(v1u64, __a) + +/* v2f32 msa_get_high_f32(v4f32 __a) */ +#define msa_get_high_f32(__a) __GET_HIGH(v2f32, __a) + +/* v1f64 msa_get_high_f64(v2f64 __a) */ +#define msa_get_high_f64(__a) __GET_HIGH(v1f64, __a) + +/* ri = ai * b[lane] */ +/* v4f32 msa_mulq_lane_f32(v4f32 __a, v4f32 __b, const int __lane) */ +#define msa_mulq_lane_f32(__a, __b, __lane) ((__a) * msa_getq_lane_f32(__b, __lane)) + +/* ri = ai + bi * c[lane] */ +/* v4f32 msa_mlaq_lane_f32(v4f32 __a, v4f32 __b, v4f32 __c, const int __lane) */ +#define msa_mlaq_lane_f32(__a, __b, __c, __lane) ((__a) + ((__b) * msa_getq_lane_f32(__c, __lane))) + +/* uint16_t msa_sum_u16(v8u16 __a)*/ +#define msa_sum_u16(__a) \ +({ \ + v4u32 _b; \ + v2u64 _c; \ + _b = __builtin_msa_hadd_u_w(__a, __a); \ + _c = __builtin_msa_hadd_u_d(_b, _b); \ + (uint16_t)(_c[0] + _c[1]); \ +}) + +/* int16_t msa_sum_s16(v8i16 __a) */ +#define msa_sum_s16(__a) \ +({ \ + v4i32 _b; \ + v2i64 _c; \ + _b = __builtin_msa_hadd_s_w(__a, __a); \ + _c = __builtin_msa_hadd_s_d(_b, _b); \ + (int16_t)(_c[0] + _c[1]); \ +}) + + +/* uint32_t msa_sum_u32(v4u32 __a)*/ +#define msa_sum_u32(__a) \ +({ \ + v2u64 _b; \ + _b = __builtin_msa_hadd_u_d(__a, __a); \ + (uint32_t)(_b[0] + _b[1]); \ +}) + +/* int32_t msa_sum_s32(v4i32 __a)*/ +#define msa_sum_s32(__a) \ +({ \ + v2i64 _b; \ + _b = __builtin_msa_hadd_s_d(__a, __a); \ + (int32_t)(_b[0] + _b[1]); \ +}) + +/* uint8_t msa_sum_u8(v16u8 __a)*/ +#define msa_sum_u8(__a) \ +({ \ + v8u16 _b16; \ + v4u32 _c32; \ + _b16 = __builtin_msa_hadd_u_h(__a, __a); \ + _c32 = __builtin_msa_hadd_u_w(_b16, _b16); \ + (uint8_t)msa_sum_u32(_c32); \ +}) + +/* int8_t msa_sum_s8(v16s8 __a)*/ +#define msa_sum_s8(__a) \ +({ \ + v8i16 _b16; \ + v4i32 _c32; \ + _b16 = __builtin_msa_hadd_s_h(__a, __a); \ + _c32 = __builtin_msa_hadd_s_w(_b16, _b16); \ + (int8_t)msa_sum_s32(_c32); \ +}) + +/* float msa_sum_f32(v4f32 __a)*/ +#define msa_sum_f32(__a) ((__a)[0] + (__a)[1] + (__a)[2] + (__a)[3]) + +/* v8u16 msa_paddlq_u8(v16u8 __a) */ +#define msa_paddlq_u8(__a) (__builtin_msa_hadd_u_h(__a, __a)) + +/* v8i16 msa_paddlq_s8(v16i8 __a) */ +#define msa_paddlq_s8(__a) (__builtin_msa_hadd_s_h(__a, __a)) + +/* v4u32 msa_paddlq_u16 (v8u16 __a)*/ +#define msa_paddlq_u16(__a) (__builtin_msa_hadd_u_w(__a, __a)) + +/* v4i32 msa_paddlq_s16 (v8i16 __a)*/ +#define msa_paddlq_s16(__a) (__builtin_msa_hadd_s_w(__a, __a)) + +/* v2u64 msa_paddlq_u32(v4u32 __a) */ +#define msa_paddlq_u32(__a) (__builtin_msa_hadd_u_d(__a, __a)) + +/* v2i64 msa_paddlq_s32(v4i32 __a) */ +#define msa_paddlq_s32(__a) (__builtin_msa_hadd_s_d(__a, __a)) + +#define V8U8_2_V8U16(x) {(uint16_t)x[0], (uint16_t)x[1], (uint16_t)x[2], (uint16_t)x[3], \ + (uint16_t)x[4], (uint16_t)x[5], (uint16_t)x[6], (uint16_t)x[7]} +#define V8U8_2_V8I16(x) {(int16_t)x[0], (int16_t)x[1], (int16_t)x[2], (int16_t)x[3], \ + (int16_t)x[4], (int16_t)x[5], (int16_t)x[6], (int16_t)x[7]} +#define V8I8_2_V8I16(x) {(int16_t)x[0], (int16_t)x[1], (int16_t)x[2], (int16_t)x[3], \ + (int16_t)x[4], (int16_t)x[5], (int16_t)x[6], (int16_t)x[7]} +#define V4U16_2_V4U32(x) {(uint32_t)x[0], (uint32_t)x[1], (uint32_t)x[2], (uint32_t)x[3]} +#define V4U16_2_V4I32(x) {(int32_t)x[0], (int32_t)x[1], (int32_t)x[2], (int32_t)x[3]} +#define V4I16_2_V4I32(x) {(int32_t)x[0], (int32_t)x[1], (int32_t)x[2], (int32_t)x[3]} +#define V2U32_2_V2U64(x) {(uint64_t)x[0], (uint64_t)x[1]} +#define V2U32_2_V2I64(x) {(int64_t)x[0], (int64_t)x[1]} + +/* v8u16 msa_mull_u8(v8u8 __a, v8u8 __b) */ +#define msa_mull_u8(__a, __b) ((v8u16)__builtin_msa_mulv_h((v8i16)V8U8_2_V8I16(__a), (v8i16)V8U8_2_V8I16(__b))) + +/* v8i16 msa_mull_s8(v8i8 __a, v8i8 __b)*/ +#define msa_mull_s8(__a, __b) (__builtin_msa_mulv_h((v8i16)V8I8_2_V8I16(__a), (v8i16)V8I8_2_V8I16(__b))) + +/* v4u32 msa_mull_u16(v4u16 __a, v4u16 __b) */ +#define msa_mull_u16(__a, __b) ((v4u32)__builtin_msa_mulv_w((v4i32)V4U16_2_V4I32(__a), (v4i32)V4U16_2_V4I32(__b))) + +/* v4i32 msa_mull_s16(v4i16 __a, v4i16 __b) */ +#define msa_mull_s16(__a, __b) (__builtin_msa_mulv_w((v4i32)V4I16_2_V4I32(__a), (v4i32)V4I16_2_V4I32(__b))) + +/* v2u64 msa_mull_u32(v2u32 __a, v2u32 __b) */ +#define msa_mull_u32(__a, __b) ((v2u64)__builtin_msa_mulv_d((v2i64)V2U32_2_V2I64(__a), (v2i64)V2U32_2_V2I64(__b))) + +/* bitwise and: __builtin_msa_and_v */ +#define msa_andq_u8(__a, __b) ((v16u8)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s8(__a, __b) ((v16i8)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u16(__a, __b) ((v8u16)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s16(__a, __b) ((v8i16)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u32(__a, __b) ((v4u32)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s32(__a, __b) ((v4i32)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_u64(__a, __b) ((v2u64)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) +#define msa_andq_s64(__a, __b) ((v2i64)__builtin_msa_and_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise or: __builtin_msa_or_v */ +#define msa_orrq_u8(__a, __b) ((v16u8)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s8(__a, __b) ((v16i8)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u16(__a, __b) ((v8u16)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s16(__a, __b) ((v8i16)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u32(__a, __b) ((v4u32)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s32(__a, __b) ((v4i32)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_u64(__a, __b) ((v2u64)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) +#define msa_orrq_s64(__a, __b) ((v2i64)__builtin_msa_or_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise xor: __builtin_msa_xor_v */ +#define msa_eorq_u8(__a, __b) ((v16u8)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s8(__a, __b) ((v16i8)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u16(__a, __b) ((v8u16)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s16(__a, __b) ((v8i16)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u32(__a, __b) ((v4u32)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s32(__a, __b) ((v4i32)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_u64(__a, __b) ((v2u64)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) +#define msa_eorq_s64(__a, __b) ((v2i64)__builtin_msa_xor_v((v16u8)(__a), (v16u8)(__b))) + +/* bitwise not: v16u8 __builtin_msa_xori_b (v16u8, 0xff) */ +#define msa_mvnq_u8(__a) ((v16u8)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s8(__a) ((v16i8)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u16(__a) ((v8u16)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s16(__a) ((v8i16)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u32(__a) ((v4u32)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s32(__a) ((v4i32)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_u64(__a) ((v2u64)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) +#define msa_mvnq_s64(__a) ((v2i64)__builtin_msa_xori_b((v16u8)(__a), 0xFF)) + +/* compare equal: ceq -> ri = ai == bi ? 1...1:0...0 */ +#define msa_ceqq_u8(__a, __b) ((v16u8)__builtin_msa_ceq_b((v16i8)(__a), (v16i8)(__b))) +#define msa_ceqq_s8(__a, __b) ((v16u8)__builtin_msa_ceq_b((v16i8)(__a), (v16i8)(__b))) +#define msa_ceqq_u16(__a, __b) ((v8u16)__builtin_msa_ceq_h((v8i16)(__a), (v8i16)(__b))) +#define msa_ceqq_s16(__a, __b) ((v8u16)__builtin_msa_ceq_h((v8i16)(__a), (v8i16)(__b))) +#define msa_ceqq_u32(__a, __b) ((v4u32)__builtin_msa_ceq_w((v4i32)(__a), (v4i32)(__b))) +#define msa_ceqq_s32(__a, __b) ((v4u32)__builtin_msa_ceq_w((v4i32)(__a), (v4i32)(__b))) +#define msa_ceqq_f32(__a, __b) ((v4u32)__builtin_msa_fceq_w((v4f32)(__a), (v4f32)(__b))) +#define msa_ceqq_u64(__a, __b) ((v2u64)__builtin_msa_ceq_d((v2i64)(__a), (v2i64)(__b))) +#define msa_ceqq_s64(__a, __b) ((v2u64)__builtin_msa_ceq_d((v2i64)(__a), (v2i64)(__b))) +#define msa_ceqq_f64(__a, __b) ((v2u64)__builtin_msa_fceq_d((v2f64)(__a), (v2f64)(__b))) + +/* Compare less-than: clt -> ri = ai < bi ? 1...1:0...0 */ +#define msa_cltq_u8(__a, __b) ((v16u8)__builtin_msa_clt_u_b((v16u8)(__a), (v16u8)(__b))) +#define msa_cltq_s8(__a, __b) ((v16u8)__builtin_msa_clt_s_b((v16i8)(__a), (v16i8)(__b))) +#define msa_cltq_u16(__a, __b) ((v8u16)__builtin_msa_clt_u_h((v8u16)(__a), (v8u16)(__b))) +#define msa_cltq_s16(__a, __b) ((v8u16)__builtin_msa_clt_s_h((v8i16)(__a), (v8i16)(__b))) +#define msa_cltq_u32(__a, __b) ((v4u32)__builtin_msa_clt_u_w((v4u32)(__a), (v4u32)(__b))) +#define msa_cltq_s32(__a, __b) ((v4u32)__builtin_msa_clt_s_w((v4i32)(__a), (v4i32)(__b))) +#define msa_cltq_f32(__a, __b) ((v4u32)__builtin_msa_fclt_w((v4f32)(__a), (v4f32)(__b))) +#define msa_cltq_u64(__a, __b) ((v2u64)__builtin_msa_clt_u_d((v2u64)(__a), (v2u64)(__b))) +#define msa_cltq_s64(__a, __b) ((v2u64)__builtin_msa_clt_s_d((v2i64)(__a), (v2i64)(__b))) +#define msa_cltq_f64(__a, __b) ((v2u64)__builtin_msa_fclt_d((v2f64)(__a), (v2f64)(__b))) + +/* compare greater-than: cgt -> ri = ai > bi ? 1...1:0...0 */ +#define msa_cgtq_u8(__a, __b) ((v16u8)__builtin_msa_clt_u_b((v16u8)(__b), (v16u8)(__a))) +#define msa_cgtq_s8(__a, __b) ((v16u8)__builtin_msa_clt_s_b((v16i8)(__b), (v16i8)(__a))) +#define msa_cgtq_u16(__a, __b) ((v8u16)__builtin_msa_clt_u_h((v8u16)(__b), (v8u16)(__a))) +#define msa_cgtq_s16(__a, __b) ((v8u16)__builtin_msa_clt_s_h((v8i16)(__b), (v8i16)(__a))) +#define msa_cgtq_u32(__a, __b) ((v4u32)__builtin_msa_clt_u_w((v4u32)(__b), (v4u32)(__a))) +#define msa_cgtq_s32(__a, __b) ((v4u32)__builtin_msa_clt_s_w((v4i32)(__b), (v4i32)(__a))) +#define msa_cgtq_f32(__a, __b) ((v4u32)__builtin_msa_fclt_w((v4f32)(__b), (v4f32)(__a))) +#define msa_cgtq_u64(__a, __b) ((v2u64)__builtin_msa_clt_u_d((v2u64)(__b), (v2u64)(__a))) +#define msa_cgtq_s64(__a, __b) ((v2u64)__builtin_msa_clt_s_d((v2i64)(__b), (v2i64)(__a))) +#define msa_cgtq_f64(__a, __b) ((v2u64)__builtin_msa_fclt_d((v2f64)(__b), (v2f64)(__a))) + +/* compare less-equal: cle -> ri = ai <= bi ? 1...1:0...0 */ +#define msa_cleq_u8(__a, __b) ((v16u8)__builtin_msa_cle_u_b((v16u8)(__a), (v16u8)(__b))) +#define msa_cleq_s8(__a, __b) ((v16u8)__builtin_msa_cle_s_b((v16i8)(__a), (v16i8)(__b))) +#define msa_cleq_u16(__a, __b) ((v8u16)__builtin_msa_cle_u_h((v8u16)(__a), (v8u16)(__b))) +#define msa_cleq_s16(__a, __b) ((v8u16)__builtin_msa_cle_s_h((v8i16)(__a), (v8i16)(__b))) +#define msa_cleq_u32(__a, __b) ((v4u32)__builtin_msa_cle_u_w((v4u32)(__a), (v4u32)(__b))) +#define msa_cleq_s32(__a, __b) ((v4u32)__builtin_msa_cle_s_w((v4i32)(__a), (v4i32)(__b))) +#define msa_cleq_f32(__a, __b) ((v4u32)__builtin_msa_fcle_w((v4f32)(__a), (v4f32)(__b))) +#define msa_cleq_u64(__a, __b) ((v2u64)__builtin_msa_cle_u_d((v2u64)(__a), (v2u64)(__b))) +#define msa_cleq_s64(__a, __b) ((v2u64)__builtin_msa_cle_s_d((v2i64)(__a), (v2i64)(__b))) +#define msa_cleq_f64(__a, __b) ((v2u64)__builtin_msa_fcle_d((v2f64)(__a), (v2f64)(__b))) + +/* compare greater-equal: cge -> ri = ai >= bi ? 1...1:0...0 */ +#define msa_cgeq_u8(__a, __b) ((v16u8)__builtin_msa_cle_u_b((v16u8)(__b), (v16u8)(__a))) +#define msa_cgeq_s8(__a, __b) ((v16u8)__builtin_msa_cle_s_b((v16i8)(__b), (v16i8)(__a))) +#define msa_cgeq_u16(__a, __b) ((v8u16)__builtin_msa_cle_u_h((v8u16)(__b), (v8u16)(__a))) +#define msa_cgeq_s16(__a, __b) ((v8u16)__builtin_msa_cle_s_h((v8i16)(__b), (v8i16)(__a))) +#define msa_cgeq_u32(__a, __b) ((v4u32)__builtin_msa_cle_u_w((v4u32)(__b), (v4u32)(__a))) +#define msa_cgeq_s32(__a, __b) ((v4u32)__builtin_msa_cle_s_w((v4i32)(__b), (v4i32)(__a))) +#define msa_cgeq_f32(__a, __b) ((v4u32)__builtin_msa_fcle_w((v4f32)(__b), (v4f32)(__a))) +#define msa_cgeq_u64(__a, __b) ((v2u64)__builtin_msa_cle_u_d((v2u64)(__b), (v2u64)(__a))) +#define msa_cgeq_s64(__a, __b) ((v2u64)__builtin_msa_cle_s_d((v2i64)(__b), (v2i64)(__a))) +#define msa_cgeq_f64(__a, __b) ((v2u64)__builtin_msa_fcle_d((v2f64)(__b), (v2f64)(__a))) + +/* Shift Left Logical: shl -> ri = ai << bi; */ +#define msa_shlq_u8(__a, __b) ((v16u8)__builtin_msa_sll_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shlq_s8(__a, __b) ((v16i8)__builtin_msa_sll_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shlq_u16(__a, __b) ((v8u16)__builtin_msa_sll_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shlq_s16(__a, __b) ((v8i16)__builtin_msa_sll_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shlq_u32(__a, __b) ((v4u32)__builtin_msa_sll_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shlq_s32(__a, __b) ((v4i32)__builtin_msa_sll_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shlq_u64(__a, __b) ((v2u64)__builtin_msa_sll_d((v2i64)(__a), (v2i64)(__b))) +#define msa_shlq_s64(__a, __b) ((v2i64)__builtin_msa_sll_d((v2i64)(__a), (v2i64)(__b))) + +/* Immediate Shift Left Logical: shl -> ri = ai << imm; */ +#define msa_shlq_n_u8(__a, __imm) ((v16u8)__builtin_msa_slli_b((v16i8)(__a), __imm)) +#define msa_shlq_n_s8(__a, __imm) ((v16i8)__builtin_msa_slli_b((v16i8)(__a), __imm)) +#define msa_shlq_n_u16(__a, __imm) ((v8u16)__builtin_msa_slli_h((v8i16)(__a), __imm)) +#define msa_shlq_n_s16(__a, __imm) ((v8i16)__builtin_msa_slli_h((v8i16)(__a), __imm)) +#define msa_shlq_n_u32(__a, __imm) ((v4u32)__builtin_msa_slli_w((v4i32)(__a), __imm)) +#define msa_shlq_n_s32(__a, __imm) ((v4i32)__builtin_msa_slli_w((v4i32)(__a), __imm)) +#define msa_shlq_n_u64(__a, __imm) ((v2u64)__builtin_msa_slli_d((v2i64)(__a), __imm)) +#define msa_shlq_n_s64(__a, __imm) ((v2i64)__builtin_msa_slli_d((v2i64)(__a), __imm)) + +/* shift right: shrq -> ri = ai >> bi; */ +#define msa_shrq_u8(__a, __b) ((v16u8)__builtin_msa_srl_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shrq_s8(__a, __b) ((v16i8)__builtin_msa_sra_b((v16i8)(__a), (v16i8)(__b))) +#define msa_shrq_u16(__a, __b) ((v8u16)__builtin_msa_srl_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shrq_s16(__a, __b) ((v8i16)__builtin_msa_sra_h((v8i16)(__a), (v8i16)(__b))) +#define msa_shrq_u32(__a, __b) ((v4u32)__builtin_msa_srl_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shrq_s32(__a, __b) ((v4i32)__builtin_msa_sra_w((v4i32)(__a), (v4i32)(__b))) +#define msa_shrq_u64(__a, __b) ((v2u64)__builtin_msa_srl_d((v2i64)(__a), (v2i64)(__b))) +#define msa_shrq_s64(__a, __b) ((v2i64)__builtin_msa_sra_d((v2i64)(__a), (v2i64)(__b))) + +/* Immediate Shift Right: shr -> ri = ai >> imm; */ +#define msa_shrq_n_u8(__a, __imm) ((v16u8)__builtin_msa_srli_b((v16i8)(__a), __imm)) +#define msa_shrq_n_s8(__a, __imm) ((v16i8)__builtin_msa_srai_b((v16i8)(__a), __imm)) +#define msa_shrq_n_u16(__a, __imm) ((v8u16)__builtin_msa_srli_h((v8i16)(__a), __imm)) +#define msa_shrq_n_s16(__a, __imm) ((v8i16)__builtin_msa_srai_h((v8i16)(__a), __imm)) +#define msa_shrq_n_u32(__a, __imm) ((v4u32)__builtin_msa_srli_w((v4i32)(__a), __imm)) +#define msa_shrq_n_s32(__a, __imm) ((v4i32)__builtin_msa_srai_w((v4i32)(__a), __imm)) +#define msa_shrq_n_u64(__a, __imm) ((v2u64)__builtin_msa_srli_d((v2i64)(__a), __imm)) +#define msa_shrq_n_s64(__a, __imm) ((v2i64)__builtin_msa_srai_d((v2i64)(__a), __imm)) + +/* Immediate Shift Right Rounded: shr -> ri = ai >> (rounded)imm; */ +#define msa_rshrq_n_u8(__a, __imm) ((v16u8)__builtin_msa_srlri_b((v16i8)(__a), __imm)) +#define msa_rshrq_n_s8(__a, __imm) ((v16i8)__builtin_msa_srari_b((v16i8)(__a), __imm)) +#define msa_rshrq_n_u16(__a, __imm) ((v8u16)__builtin_msa_srlri_h((v8i16)(__a), __imm)) +#define msa_rshrq_n_s16(__a, __imm) ((v8i16)__builtin_msa_srari_h((v8i16)(__a), __imm)) +#define msa_rshrq_n_u32(__a, __imm) ((v4u32)__builtin_msa_srlri_w((v4i32)(__a), __imm)) +#define msa_rshrq_n_s32(__a, __imm) ((v4i32)__builtin_msa_srari_w((v4i32)(__a), __imm)) +#define msa_rshrq_n_u64(__a, __imm) ((v2u64)__builtin_msa_srlri_d((v2i64)(__a), __imm)) +#define msa_rshrq_n_s64(__a, __imm) ((v2i64)__builtin_msa_srari_d((v2i64)(__a), __imm)) + +/* Vector saturating rounding shift left, qrshl -> ri = ai << bi; */ +#define msa_qrshrq_s32(a, b) ((v4i32)__msa_srar_w((v4i32)(a), (v4i32)(b))) + +/* Rename the msa builtin func to unify the name style for intrin_msa.hpp */ +#define msa_qaddq_u8 __builtin_msa_adds_u_b +#define msa_qaddq_s8 __builtin_msa_adds_s_b +#define msa_qaddq_u16 __builtin_msa_adds_u_h +#define msa_qaddq_s16 __builtin_msa_adds_s_h +#define msa_qaddq_u32 __builtin_msa_adds_u_w +#define msa_qaddq_s32 __builtin_msa_adds_s_w +#define msa_qaddq_u64 __builtin_msa_adds_u_d +#define msa_qaddq_s64 __builtin_msa_adds_s_d +#define msa_addq_u8(a, b) ((v16u8)__builtin_msa_addv_b((v16i8)(a), (v16i8)(b))) +#define msa_addq_s8 __builtin_msa_addv_b +#define msa_addq_u16(a, b) ((v8u16)__builtin_msa_addv_h((v8i16)(a), (v8i16)(b))) +#define msa_addq_s16 __builtin_msa_addv_h +#define msa_addq_u32(a, b) ((v4u32)__builtin_msa_addv_w((v4i32)(a), (v4i32)(b))) +#define msa_addq_s32 __builtin_msa_addv_w +#define msa_addq_f32 __builtin_msa_fadd_w +#define msa_addq_u64(a, b) ((v2u64)__builtin_msa_addv_d((v2i64)(a), (v2i64)(b))) +#define msa_addq_s64 __builtin_msa_addv_d +#define msa_addq_f64 __builtin_msa_fadd_d +#define msa_qsubq_u8 __builtin_msa_subs_u_b +#define msa_qsubq_s8 __builtin_msa_subs_s_b +#define msa_qsubq_u16 __builtin_msa_subs_u_h +#define msa_qsubq_s16 __builtin_msa_subs_s_h +#define msa_subq_u8(a, b) ((v16u8)__builtin_msa_subv_b((v16i8)(a), (v16i8)(b))) +#define msa_subq_s8 __builtin_msa_subv_b +#define msa_subq_u16(a, b) ((v8u16)__builtin_msa_subv_h((v8i16)(a), (v8i16)(b))) +#define msa_subq_s16 __builtin_msa_subv_h +#define msa_subq_u32(a, b) ((v4u32)__builtin_msa_subv_w((v4i32)(a), (v4i32)(b))) +#define msa_subq_s32 __builtin_msa_subv_w +#define msa_subq_f32 __builtin_msa_fsub_w +#define msa_subq_u64(a, b) ((v2u64)__builtin_msa_subv_d((v2i64)(a), (v2i64)(b))) +#define msa_subq_s64 __builtin_msa_subv_d +#define msa_subq_f64 __builtin_msa_fsub_d +#define msa_mulq_u8(a, b) ((v16u8)__builtin_msa_mulv_b((v16i8)(a), (v16i8)(b))) +#define msa_mulq_s8(a, b) ((v16i8)__builtin_msa_mulv_b((v16i8)(a), (v16i8)(b))) +#define msa_mulq_u16(a, b) ((v8u16)__builtin_msa_mulv_h((v8i16)(a), (v8i16)(b))) +#define msa_mulq_s16(a, b) ((v8i16)__builtin_msa_mulv_h((v8i16)(a), (v8i16)(b))) +#define msa_mulq_u32(a, b) ((v4u32)__builtin_msa_mulv_w((v4i32)(a), (v4i32)(b))) +#define msa_mulq_s32(a, b) ((v4i32)__builtin_msa_mulv_w((v4i32)(a), (v4i32)(b))) +#define msa_mulq_u64(a, b) ((v2u64)__builtin_msa_mulv_d((v2i64)(a), (v2i64)(b))) +#define msa_mulq_s64(a, b) ((v2i64)__builtin_msa_mulv_d((v2i64)(a), (v2i64)(b))) +#define msa_mulq_f32 __builtin_msa_fmul_w +#define msa_mulq_f64 __builtin_msa_fmul_d +#define msa_divq_f32 __builtin_msa_fdiv_w +#define msa_divq_f64 __builtin_msa_fdiv_d +#define msa_dotp_s_h __builtin_msa_dotp_s_h +#define msa_dotp_s_w __builtin_msa_dotp_s_w +#define msa_dotp_s_d __builtin_msa_dotp_s_d +#define msa_dotp_u_h __builtin_msa_dotp_u_h +#define msa_dotp_u_w __builtin_msa_dotp_u_w +#define msa_dotp_u_d __builtin_msa_dotp_u_d +#define msa_dpadd_s_h __builtin_msa_dpadd_s_h +#define msa_dpadd_s_w __builtin_msa_dpadd_s_w +#define msa_dpadd_s_d __builtin_msa_dpadd_s_d +#define msa_dpadd_u_h __builtin_msa_dpadd_u_h +#define msa_dpadd_u_w __builtin_msa_dpadd_u_w +#define msa_dpadd_u_d __builtin_msa_dpadd_u_d + +#define ILVRL_B2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_b((v16i8)(in0), (v16i8)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_b((v16i8)(in0), (v16i8)(in1)); \ + } while (0) +#define ILVRL_B2_UB(...) ILVRL_B2(v16u8, __VA_ARGS__) +#define ILVRL_B2_SB(...) ILVRL_B2(v16i8, __VA_ARGS__) +#define ILVRL_B2_UH(...) ILVRL_B2(v8u16, __VA_ARGS__) +#define ILVRL_B2_SH(...) ILVRL_B2(v8i16, __VA_ARGS__) +#define ILVRL_B2_SW(...) ILVRL_B2(v4i32, __VA_ARGS__) + +#define ILVRL_H2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_h((v8i16)(in0), (v8i16)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_h((v8i16)(in0), (v8i16)(in1)); \ + } while (0) +#define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__) +#define ILVRL_H2_SB(...) ILVRL_H2(v16i8, __VA_ARGS__) +#define ILVRL_H2_UH(...) ILVRL_H2(v8u16, __VA_ARGS__) +#define ILVRL_H2_SH(...) ILVRL_H2(v8i16, __VA_ARGS__) +#define ILVRL_H2_SW(...) ILVRL_H2(v4i32, __VA_ARGS__) +#define ILVRL_H2_UW(...) ILVRL_H2(v4u32, __VA_ARGS__) + +#define ILVRL_W2(RTYPE, in0, in1, low, hi) do { \ + low = (RTYPE)__builtin_msa_ilvr_w((v4i32)(in0), (v4i32)(in1)); \ + hi = (RTYPE)__builtin_msa_ilvl_w((v4i32)(in0), (v4i32)(in1)); \ + } while (0) +#define ILVRL_W2_UB(...) ILVRL_W2(v16u8, __VA_ARGS__) +#define ILVRL_W2_SH(...) ILVRL_W2(v8i16, __VA_ARGS__) +#define ILVRL_W2_SW(...) ILVRL_W2(v4i32, __VA_ARGS__) +#define ILVRL_W2_UW(...) ILVRL_W2(v4u32, __VA_ARGS__) + +/* absq, qabsq (r = |a|;) */ +#define msa_absq_s8(a) __builtin_msa_add_a_b(a, __builtin_msa_fill_b(0)) +#define msa_absq_s16(a) __builtin_msa_add_a_h(a, __builtin_msa_fill_h(0)) +#define msa_absq_s32(a) __builtin_msa_add_a_w(a, __builtin_msa_fill_w(0)) +#define msa_absq_s64(a) __builtin_msa_add_a_d(a, __builtin_msa_fill_d(0)) +#define msa_absq_f32(a) ((v4f32)__builtin_msa_bclri_w((v4u32)(a), 31)) +#define msa_absq_f64(a) ((v2f64)__builtin_msa_bclri_d((v2u64)(a), 63)) +#define msa_qabsq_s8(a) __builtin_msa_adds_a_b(a, __builtin_msa_fill_b(0)) +#define msa_qabsq_s16(a) __builtin_msa_adds_a_h(a, __builtin_msa_fill_h(0)) +#define msa_qabsq_s32(a) __builtin_msa_adds_a_w(a, __builtin_msa_fill_w(0)) +#define msa_qabsq_s64(a) __builtin_msa_adds_a_d(a, __builtin_msa_fill_d(0)) + +/* abdq, qabdq (r = |a - b|;) */ +#define msa_abdq_u8 __builtin_msa_asub_u_b +#define msa_abdq_s8 __builtin_msa_asub_s_b +#define msa_abdq_u16 __builtin_msa_asub_u_h +#define msa_abdq_s16 __builtin_msa_asub_s_h +#define msa_abdq_u32 __builtin_msa_asub_u_w +#define msa_abdq_s32 __builtin_msa_asub_s_w +#define msa_abdq_u64 __builtin_msa_asub_u_d +#define msa_abdq_s64 __builtin_msa_asub_s_d +#define msa_abdq_f32(a, b) msa_absq_f32(__builtin_msa_fsub_w(a, b)) +#define msa_abdq_f64(a, b) msa_absq_f64(__builtin_msa_fsub_d(a, b)) +#define msa_qabdq_s8(a, b) msa_qabsq_s8(__builtin_msa_subs_s_b(a, b)) +#define msa_qabdq_s16(a, b) msa_qabsq_s16(__builtin_msa_subs_s_h(a, b)) +#define msa_qabdq_s32(a, b) msa_qabsq_s32(__builtin_msa_subs_s_w(a, b)) +#define msa_qabdq_s64(a, b) msa_qabsq_s64(__builtin_msa_subs_s_d(a, b)) + +/* sqrtq, rsqrtq */ +#define msa_sqrtq_f32 __builtin_msa_fsqrt_w +#define msa_sqrtq_f64 __builtin_msa_fsqrt_d +#define msa_rsqrtq_f32 __builtin_msa_frsqrt_w +#define msa_rsqrtq_f64 __builtin_msa_frsqrt_d + + +/* mlaq: r = a + b * c; */ +__extension__ extern __inline v4i32 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_s32(v4i32 __a, v4i32 __b, v4i32 __c) +{ + __asm__ volatile("maddv.w %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v2i64 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_s64(v2i64 __a, v2i64 __b, v2i64 __c) +{ + __asm__ volatile("maddv.d %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v4f32 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_f32(v4f32 __a, v4f32 __b, v4f32 __c) +{ + __asm__ volatile("fmadd.w %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +__extension__ extern __inline v2f64 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_mlaq_f64(v2f64 __a, v2f64 __b, v2f64 __c) +{ + __asm__ volatile("fmadd.d %w[__a], %w[__b], %w[__c]\n" + // Outputs + : [__a] "+f"(__a) + // Inputs + : [__b] "f"(__b), [__c] "f"(__c)); + return __a; +} + +/* cntq */ +#define msa_cntq_s8 __builtin_msa_pcnt_b +#define msa_cntq_s16 __builtin_msa_pcnt_h +#define msa_cntq_s32 __builtin_msa_pcnt_w +#define msa_cntq_s64 __builtin_msa_pcnt_d + +/* bslq (a: mask; r = b(if a == 0); r = c(if a == 1);) */ +#define msa_bslq_u8 __builtin_msa_bsel_v + +/* ilvrq, ilvlq (For EL only, ilvrq: b0, a0, b1, a1; ilvlq: b2, a2, b3, a3;) */ +#define msa_ilvrq_s8 __builtin_msa_ilvr_b +#define msa_ilvrq_s16 __builtin_msa_ilvr_h +#define msa_ilvrq_s32 __builtin_msa_ilvr_w +#define msa_ilvrq_s64 __builtin_msa_ilvr_d +#define msa_ilvlq_s8 __builtin_msa_ilvl_b +#define msa_ilvlq_s16 __builtin_msa_ilvl_h +#define msa_ilvlq_s32 __builtin_msa_ilvl_w +#define msa_ilvlq_s64 __builtin_msa_ilvl_d + +/* ilvevq, ilvodq (ilvevq: b0, a0, b2, a2; ilvodq: b1, a1, b3, a3; ) */ +#define msa_ilvevq_s8 __builtin_msa_ilvev_b +#define msa_ilvevq_s16 __builtin_msa_ilvev_h +#define msa_ilvevq_s32 __builtin_msa_ilvev_w +#define msa_ilvevq_s64 __builtin_msa_ilvev_d +#define msa_ilvodq_s8 __builtin_msa_ilvod_b +#define msa_ilvodq_s16 __builtin_msa_ilvod_h +#define msa_ilvodq_s32 __builtin_msa_ilvod_w +#define msa_ilvodq_s64 __builtin_msa_ilvod_d + +/* extq (r = (a || b); a concatenation b and get elements from index c) */ +#ifdef _MIPSEB +#define msa_extq_s8(a, b, c) \ +(__builtin_msa_vshf_b(__builtin_msa_subv_b((v16i8)((v2i64){0x1716151413121110, 0x1F1E1D1C1B1A1918}), __builtin_msa_fill_b(c)), a, b)) +#define msa_extq_s16(a, b, c) \ +(__builtin_msa_vshf_h(__builtin_msa_subv_h((v8i16)((v2i64){0x000B000A00090008, 0x000F000E000D000C}), __builtin_msa_fill_h(c)), a, b)) +#define msa_extq_s32(a, b, c) \ +(__builtin_msa_vshf_w(__builtin_msa_subv_w((v4i32)((v2i64){0x0000000500000004, 0x0000000700000006}), __builtin_msa_fill_w(c)), a, b)) +#define msa_extq_s64(a, b, c) \ +(__builtin_msa_vshf_d(__builtin_msa_subv_d((v2i64){0x0000000000000002, 0x0000000000000003}, __builtin_msa_fill_d(c)), a, b)) +#else +#define msa_extq_s8(a, b, c) \ +(__builtin_msa_vshf_b(__builtin_msa_addv_b((v16i8)((v2i64){0x0706050403020100, 0x0F0E0D0C0B0A0908}), __builtin_msa_fill_b(c)), b, a)) +#define msa_extq_s16(a, b, c) \ +(__builtin_msa_vshf_h(__builtin_msa_addv_h((v8i16)((v2i64){0x0003000200010000, 0x0007000600050004}), __builtin_msa_fill_h(c)), b, a)) +#define msa_extq_s32(a, b, c) \ +(__builtin_msa_vshf_w(__builtin_msa_addv_w((v4i32)((v2i64){0x0000000100000000, 0x0000000300000002}), __builtin_msa_fill_w(c)), b, a)) +#define msa_extq_s64(a, b, c) \ +(__builtin_msa_vshf_d(__builtin_msa_addv_d((v2i64){0x0000000000000000, 0x0000000000000001}, __builtin_msa_fill_d(c)), b, a)) +#endif /* _MIPSEB */ + +/* cvttruncq, cvttintq, cvtrintq */ +#define msa_cvttruncq_u32_f32 __builtin_msa_ftrunc_u_w +#define msa_cvttruncq_s32_f32 __builtin_msa_ftrunc_s_w +#define msa_cvttruncq_u64_f64 __builtin_msa_ftrunc_u_d +#define msa_cvttruncq_s64_f64 __builtin_msa_ftrunc_s_d +#define msa_cvttintq_u32_f32 __builtin_msa_ftint_u_w +#define msa_cvttintq_s32_f32 __builtin_msa_ftint_s_w +#define msa_cvttintq_u64_f64 __builtin_msa_ftint_u_d +#define msa_cvttintq_s64_f64 __builtin_msa_ftint_s_d +#define msa_cvtrintq_f32 __builtin_msa_frint_w +#define msa_cvtrintq_f64 __builtin_msa_frint_d + +/* cvtfintq, cvtfq */ +#define msa_cvtfintq_f32_u32 __builtin_msa_ffint_u_w +#define msa_cvtfintq_f32_s32 __builtin_msa_ffint_s_w +#define msa_cvtfintq_f64_u64 __builtin_msa_ffint_u_d +#define msa_cvtfintq_f64_s64 __builtin_msa_ffint_s_d +#define msa_cvtfq_f32_f64 __builtin_msa_fexdo_w +#define msa_cvtflq_f64_f32 __builtin_msa_fexupr_d +#define msa_cvtfhq_f64_f32 __builtin_msa_fexupl_d + +#define msa_addl_u8(a, b) ((v8u16)__builtin_msa_addv_h((v8i16)V8U8_2_V8I16(a), (v8i16)V8U8_2_V8I16(b))) +#define msa_addl_s8(a, b) (__builtin_msa_addv_h((v8i16)V8I8_2_V8I16(a), (v8i16)V8I8_2_V8I16(b))) +#define msa_addl_u16(a, b) ((v4u32)__builtin_msa_addv_w((v4i32)V4U16_2_V4I32(a), (v4i32)V4U16_2_V4I32(b))) +#define msa_addl_s16(a, b) (__builtin_msa_addv_w((v4i32)V4I16_2_V4I32(a), (v4i32)V4I16_2_V4I32(b))) +#define msa_subl_s16(a, b) (__builtin_msa_subv_w((v4i32)V4I16_2_V4I32(a), (v4i32)V4I16_2_V4I32(b))) +#define msa_recpeq_f32 __builtin_msa_frcp_w +#define msa_recpsq_f32(a, b) (__builtin_msa_fsub_w(msa_dupq_n_f32(2.0f), __builtin_msa_fmul_w(a, b))) + +#define MSA_INTERLEAVED_IMPL_LOAD2_STORE2(_Tp, _Tpv, _Tpvs, suffix, df, nlanes) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld2q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + nlanes); \ + *a = (_Tpv)__builtin_msa_pckev_##df((_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_pckod_##df((_Tpvs)v1, (_Tpvs)v0); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st2q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b) \ +{ \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_##df((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + nlanes, (_Tpv)__builtin_msa_ilvl_##df((_Tpvs)b, (_Tpvs)a)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint8_t, v16u8, v16i8, u8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int8_t, v16i8, v16i8, s8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint16_t, v8u16, v8i16, u16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int16_t, v8i16, v8i16, s16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint32_t, v4u32, v4i32, u32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int32_t, v4i32, v4i32, s32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(float, v4f32, v4i32, f32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(uint64_t, v2u64, v2i64, u64, d, 2) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(int64_t, v2i64, v2i64, s64, d, 2) +MSA_INTERLEAVED_IMPL_LOAD2_STORE2(double, v2f64, v2i64, f64, d, 2) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_LOAD3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 16); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 32); \ + _Tpvs v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0704011F1F1F1F1F, 0x1F1C191613100D0A}), (_Tpvs)v0, (_Tpvs)v1); \ + *a = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x1716150E0B080502, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0603001F1F1F1F1F, 0x1E1B1815120F0C09}), (_Tpvs)v0, (_Tpvs)v1); \ + *b = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x1716150D0A070401, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x05021F1F1F1F1F1F, 0x1D1A1714110E0B08}), (_Tpvs)v0, (_Tpvs)v1); \ + *c = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x17160F0C09060300, 0x1F1E1D1C1B1A1918}), v3, (_Tpvs)v2); \ +} +#else +#define MSA_INTERLEAVED_IMPL_LOAD3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 16); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 32); \ + _Tpvs v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x15120F0C09060300, 0x00000000001E1B18}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1D1A1714110A0908}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1613100D0A070401, 0x00000000001F1C19}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1E1B1815120A0908}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1714110E0B080502, 0x0000000000001D1A}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_b((_Tpvs)((v2i64){0x0706050403020100, 0x1F1C191613100908}), (_Tpvs)v2, v3); \ +} +#endif + +MSA_INTERLEAVED_IMPL_LOAD3_8(uint8_t, v16u8, v16i8, u8) +MSA_INTERLEAVED_IMPL_LOAD3_8(int8_t, v16i8, v16i8, s8) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_LOAD3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 8); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 16); \ + _Tpvs v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00030000000F000F, 0x000F000C00090006}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000A00050002, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0002000F000F000F, 0x000E000B00080005}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000700040001, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000F000F000F, 0x000D000A00070004}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000600030000, 0x000F000E000D000C}), (_Tpvs)v2, v3); \ +} +#else +#define MSA_INTERLEAVED_IMPL_LOAD3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 8); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 16); \ + _Tpvs v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0009000600030000, 0x00000000000F000C}), (_Tpvs)v1, (_Tpvs)v0); \ + *a = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000D000A00050004}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000A000700040001, 0x000000000000000D}), (_Tpvs)v1, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000E000B00080004}), (_Tpvs)v2, v3); \ + v3 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B000800050002, 0x000000000000000E}), (_Tpvs)v1, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_vshf_h((_Tpvs)((v2i64){0x0003000200010000, 0x000F000C00090004}), (_Tpvs)v2, v3); \ +} +#endif + +MSA_INTERLEAVED_IMPL_LOAD3_16(uint16_t, v8u16, v8i16, u16) +MSA_INTERLEAVED_IMPL_LOAD3_16(int16_t, v8i16, v8i16, s16) + +#define MSA_INTERLEAVED_IMPL_LOAD3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + _Tpv v00 = msa_ld1q_##suffix(ptr); \ + _Tpv v01 = msa_ld1q_##suffix(ptr + 4); \ + _Tpv v02 = msa_ld1q_##suffix(ptr + 8); \ + _Tpvs v10 = __builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v01, (v2i64)v01), (_Tpvs)v00); \ + _Tpvs v11 = __builtin_msa_ilvr_w((_Tpvs)v02, (_Tpvs)__builtin_msa_ilvl_d((v2i64)v00, (v2i64)v00)); \ + _Tpvs v12 = __builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v02, (v2i64)v02), (_Tpvs)v01); \ + *a = (_Tpv)__builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v11, (v2i64)v11), v10); \ + *b = (_Tpv)__builtin_msa_ilvr_w(v12, (_Tpvs)__builtin_msa_ilvl_d((v2i64)v10, (v2i64)v10)); \ + *c = (_Tpv)__builtin_msa_ilvr_w((_Tpvs)__builtin_msa_ilvl_d((v2i64)v12, (v2i64)v12), v11); \ +} + +MSA_INTERLEAVED_IMPL_LOAD3_32(uint32_t, v4u32, v4i32, u32) +MSA_INTERLEAVED_IMPL_LOAD3_32(int32_t, v4i32, v4i32, s32) +MSA_INTERLEAVED_IMPL_LOAD3_32(float, v4f32, v4i32, f32) + +#define MSA_INTERLEAVED_IMPL_LOAD3_64(_Tp, _Tpv, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld3q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c) \ +{ \ + *((_Tp*)a) = *ptr; *((_Tp*)b) = *(ptr + 1); *((_Tp*)c) = *(ptr + 2); \ + *((_Tp*)a + 1) = *(ptr + 3); *((_Tp*)b + 1) = *(ptr + 4); *((_Tp*)c + 1) = *(ptr + 5); \ +} + +MSA_INTERLEAVED_IMPL_LOAD3_64(uint64_t, v2u64, u64) +MSA_INTERLEAVED_IMPL_LOAD3_64(int64_t, v2i64, s64) +MSA_INTERLEAVED_IMPL_LOAD3_64(double, v2f64, f64) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0F0E0D0C0B1F1F1F, 0x1F1E1D1C1B1A1F1F}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0D1C140C1B130B1A, 0x1F170F1E160E1D15}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0A09080706051F1F, 0x19181716151F1F1F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x1D14071C13061B12, 0x170A1F16091E1508}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x04030201001F1F1F, 0x14131211101F1F1F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x15021C14011B1300, 0x051F17041E16031D}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 32, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_8(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000050403020100, 0x0000001413121110}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0A02110901100800, 0x05140C04130B0312}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000000A09080706, 0x00001A1918171615}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x170A011609001508, 0x0D04190C03180B02}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x0000000F0E0D0C0B, 0x0000001F1E1D1C1B}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_b((_Tpvs)((v2i64){0x021C09011B08001A, 0x1F0C041E0B031D0A}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 32, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_8(uint8_t, v16u8, v16i8, u8) +MSA_INTERLEAVED_IMPL_STORE3_8(int8_t, v16i8, v16i8, s8) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000700060005000F, 0x000F000E000D000F}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000A0006000D0009, 0x000F000B0007000E}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00040003000F000F, 0x000C000B000A000F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000E000A0003000D, 0x0005000F000B0004}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000200010000000F, 0x00090008000F000F}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000E00090000, 0x000B0002000F000A}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_16(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000200010000, 0x0000000A00090008}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0001000800040000, 0x0006000200090005}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000500040003, 0x00000000000C000B}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x000B00040000000A, 0x0002000C00050001}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x0000000000070006, 0x0000000F000E000D}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_h((_Tpvs)((v2i64){0x00050000000D0004, 0x000F00060001000E}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 16, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_16(uint16_t, v8u16, v8i16, u16) +MSA_INTERLEAVED_IMPL_STORE3_16(int16_t, v8i16, v8i16, s16) + +#ifdef _MIPSEB +#define MSA_INTERLEAVED_IMPL_STORE3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000300000007, 0x0000000700000006}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000300000006, 0x0000000700000005}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000200000001, 0x0000000500000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000700000004, 0x0000000500000002}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000007, 0x0000000400000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000500000000, 0x0000000100000007}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ +} +#else +#define MSA_INTERLEAVED_IMPL_STORE3_32(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + _Tpvs v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000100000000, 0x0000000000000004}), (_Tpvs)b, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000200000000, 0x0000000100000004}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000002, 0x0000000600000005}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000500000002, 0x0000000300000000}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)v1); \ + v0 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000003, 0x0000000000000007}), (_Tpvs)b, (_Tpvs)a); \ + v1 = __builtin_msa_vshf_w((_Tpvs)((v2i64){0x0000000000000006, 0x0000000700000002}), (_Tpvs)c, (_Tpvs)v0); \ + msa_st1q_##suffix(ptr + 8, (_Tpv)v1); \ +} +#endif + +MSA_INTERLEAVED_IMPL_STORE3_32(uint32_t, v4u32, v4i32, u32) +MSA_INTERLEAVED_IMPL_STORE3_32(int32_t, v4i32, v4i32, s32) +MSA_INTERLEAVED_IMPL_STORE3_32(float, v4f32, v4i32, f32) + +#define MSA_INTERLEAVED_IMPL_STORE3_64(_Tp, _Tpv, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st3q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c) \ +{ \ + *ptr = a[0]; *(ptr + 1) = b[0]; *(ptr + 2) = c[0]; \ + *(ptr + 3) = a[1]; *(ptr + 4) = b[1]; *(ptr + 5) = c[1]; \ +} + +MSA_INTERLEAVED_IMPL_STORE3_64(uint64_t, v2u64, u64) +MSA_INTERLEAVED_IMPL_STORE3_64(int64_t, v2i64, s64) +MSA_INTERLEAVED_IMPL_STORE3_64(double, v2f64, f64) + +#define MSA_INTERLEAVED_IMPL_LOAD4_STORE4(_Tp, _Tpv, _Tpvs, suffix, df, nlanes) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld4q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c, _Tpv* d) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + nlanes); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + nlanes * 2); \ + _Tpv v3 = msa_ld1q_##suffix(ptr + nlanes * 3); \ + _Tpvs t0 = __builtin_msa_pckev_##df((_Tpvs)v1, (_Tpvs)v0); \ + _Tpvs t1 = __builtin_msa_pckev_##df((_Tpvs)v3, (_Tpvs)v2); \ + _Tpvs t2 = __builtin_msa_pckod_##df((_Tpvs)v1, (_Tpvs)v0); \ + _Tpvs t3 = __builtin_msa_pckod_##df((_Tpvs)v3, (_Tpvs)v2); \ + *a = (_Tpv)__builtin_msa_pckev_##df(t1, t0); \ + *b = (_Tpv)__builtin_msa_pckev_##df(t3, t2); \ + *c = (_Tpv)__builtin_msa_pckod_##df(t1, t0); \ + *d = (_Tpv)__builtin_msa_pckod_##df(t3, t2); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st4q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c, const _Tpv d) \ +{ \ + _Tpvs v0 = __builtin_msa_ilvr_##df((_Tpvs)c, (_Tpvs)a); \ + _Tpvs v1 = __builtin_msa_ilvr_##df((_Tpvs)d, (_Tpvs)b); \ + _Tpvs v2 = __builtin_msa_ilvl_##df((_Tpvs)c, (_Tpvs)a); \ + _Tpvs v3 = __builtin_msa_ilvl_##df((_Tpvs)d, (_Tpvs)b); \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_##df(v1, v0)); \ + msa_st1q_##suffix(ptr + nlanes, (_Tpv)__builtin_msa_ilvl_##df(v1, v0)); \ + msa_st1q_##suffix(ptr + 2 * nlanes, (_Tpv)__builtin_msa_ilvr_##df(v3, v2)); \ + msa_st1q_##suffix(ptr + 3 * nlanes, (_Tpv)__builtin_msa_ilvl_##df(v3, v2)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint8_t, v16u8, v16i8, u8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int8_t, v16i8, v16i8, s8, b, 16) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint16_t, v8u16, v8i16, u16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int16_t, v8i16, v8i16, s16, h, 8) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(uint32_t, v4u32, v4i32, u32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(int32_t, v4i32, v4i32, s32, w, 4) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4(float, v4f32, v4i32, f32, w, 4) + +#define MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(_Tp, _Tpv, _Tpvs, suffix) \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_ld4q_##suffix(const _Tp* ptr, _Tpv* a, _Tpv* b, _Tpv* c, _Tpv* d) \ +{ \ + _Tpv v0 = msa_ld1q_##suffix(ptr); \ + _Tpv v1 = msa_ld1q_##suffix(ptr + 2); \ + _Tpv v2 = msa_ld1q_##suffix(ptr + 4); \ + _Tpv v3 = msa_ld1q_##suffix(ptr + 6); \ + *a = (_Tpv)__builtin_msa_ilvr_d((_Tpvs)v2, (_Tpvs)v0); \ + *b = (_Tpv)__builtin_msa_ilvl_d((_Tpvs)v2, (_Tpvs)v0); \ + *c = (_Tpv)__builtin_msa_ilvr_d((_Tpvs)v3, (_Tpvs)v1); \ + *d = (_Tpv)__builtin_msa_ilvl_d((_Tpvs)v3, (_Tpvs)v1); \ +} \ +__extension__ extern __inline void \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +msa_st4q_##suffix(_Tp* ptr, const _Tpv a, const _Tpv b, const _Tpv c, const _Tpv d) \ +{ \ + msa_st1q_##suffix(ptr, (_Tpv)__builtin_msa_ilvr_d((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + 2, (_Tpv)__builtin_msa_ilvr_d((_Tpvs)d, (_Tpvs)c)); \ + msa_st1q_##suffix(ptr + 4, (_Tpv)__builtin_msa_ilvl_d((_Tpvs)b, (_Tpvs)a)); \ + msa_st1q_##suffix(ptr + 6, (_Tpv)__builtin_msa_ilvl_d((_Tpvs)d, (_Tpvs)c)); \ +} + +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(uint64_t, v2u64, v2i64, u64) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(int64_t, v2i64, v2i64, s64) +MSA_INTERLEAVED_IMPL_LOAD4_STORE4_64(double, v2f64, v2i64, f64) + +__extension__ extern __inline v8i16 +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +msa_qdmulhq_n_s16(v8i16 a, int16_t b) +{ + v8i16 a_lo, a_hi; + ILVRL_H2_SH(a, msa_dupq_n_s16(0), a_lo, a_hi); + return msa_packr_s32(msa_shlq_n_s32(msa_mulq_s32(msa_paddlq_s16(a_lo), msa_dupq_n_s32(b)), 1), + msa_shlq_n_s32(msa_mulq_s32(msa_paddlq_s16(a_hi), msa_dupq_n_s32(b)), 1), 16); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /*__mips_msa*/ +#endif /* OPENCV_CORE_MSA_MACROS_H */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/simd_utils.impl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/simd_utils.impl.hpp new file mode 100644 index 0000000..fff8f94 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/hal/simd_utils.impl.hpp @@ -0,0 +1,146 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +// This header is not standalone. Don't include directly, use "intrin.hpp" instead. +#ifdef OPENCV_HAL_INTRIN_HPP // defined in intrin.hpp + + +#if CV_SIMD128 || CV_SIMD128_CPP + +template struct Type2Vec128_Traits; +#define CV_INTRIN_DEF_TYPE2VEC128_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec128_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(uchar, v_uint8x16); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(schar, v_int8x16); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(ushort, v_uint16x8); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(short, v_int16x8); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(unsigned, v_uint32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(int, v_int32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(float, v_float32x4); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(uint64, v_uint64x2); +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(int64, v_int64x2); +#if CV_SIMD128_64F +CV_INTRIN_DEF_TYPE2VEC128_TRAITS(double, v_float64x2); +#endif + +template static inline +typename Type2Vec128_Traits<_T>::vec_type v_setall(const _T& a); + +template<> inline Type2Vec128_Traits< uchar>::vec_type v_setall< uchar>(const uchar& a) { return v_setall_u8(a); } +template<> inline Type2Vec128_Traits< schar>::vec_type v_setall< schar>(const schar& a) { return v_setall_s8(a); } +template<> inline Type2Vec128_Traits::vec_type v_setall(const ushort& a) { return v_setall_u16(a); } +template<> inline Type2Vec128_Traits< short>::vec_type v_setall< short>(const short& a) { return v_setall_s16(a); } +template<> inline Type2Vec128_Traits< uint>::vec_type v_setall< uint>(const uint& a) { return v_setall_u32(a); } +template<> inline Type2Vec128_Traits< int>::vec_type v_setall< int>(const int& a) { return v_setall_s32(a); } +template<> inline Type2Vec128_Traits::vec_type v_setall(const uint64& a) { return v_setall_u64(a); } +template<> inline Type2Vec128_Traits< int64>::vec_type v_setall< int64>(const int64& a) { return v_setall_s64(a); } +template<> inline Type2Vec128_Traits< float>::vec_type v_setall< float>(const float& a) { return v_setall_f32(a); } +#if CV_SIMD128_64F +template<> inline Type2Vec128_Traits::vec_type v_setall(const double& a) { return v_setall_f64(a); } +#endif + +#endif // SIMD128 + + +#if CV_SIMD256 + +template struct Type2Vec256_Traits; +#define CV_INTRIN_DEF_TYPE2VEC256_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec256_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(uchar, v_uint8x32); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(schar, v_int8x32); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(ushort, v_uint16x16); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(short, v_int16x16); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(unsigned, v_uint32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(int, v_int32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(float, v_float32x8); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(uint64, v_uint64x4); +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(int64, v_int64x4); +#if CV_SIMD256_64F +CV_INTRIN_DEF_TYPE2VEC256_TRAITS(double, v_float64x4); +#endif + +template static inline +typename Type2Vec256_Traits<_T>::vec_type v256_setall(const _T& a); + +template<> inline Type2Vec256_Traits< uchar>::vec_type v256_setall< uchar>(const uchar& a) { return v256_setall_u8(a); } +template<> inline Type2Vec256_Traits< schar>::vec_type v256_setall< schar>(const schar& a) { return v256_setall_s8(a); } +template<> inline Type2Vec256_Traits::vec_type v256_setall(const ushort& a) { return v256_setall_u16(a); } +template<> inline Type2Vec256_Traits< short>::vec_type v256_setall< short>(const short& a) { return v256_setall_s16(a); } +template<> inline Type2Vec256_Traits< uint>::vec_type v256_setall< uint>(const uint& a) { return v256_setall_u32(a); } +template<> inline Type2Vec256_Traits< int>::vec_type v256_setall< int>(const int& a) { return v256_setall_s32(a); } +template<> inline Type2Vec256_Traits::vec_type v256_setall(const uint64& a) { return v256_setall_u64(a); } +template<> inline Type2Vec256_Traits< int64>::vec_type v256_setall< int64>(const int64& a) { return v256_setall_s64(a); } +template<> inline Type2Vec256_Traits< float>::vec_type v256_setall< float>(const float& a) { return v256_setall_f32(a); } +#if CV_SIMD256_64F +template<> inline Type2Vec256_Traits::vec_type v256_setall(const double& a) { return v256_setall_f64(a); } +#endif + +#endif // SIMD256 + + +#if CV_SIMD512 + +template struct Type2Vec512_Traits; +#define CV_INTRIN_DEF_TYPE2VEC512_TRAITS(type_, vec_type_) \ + template<> struct Type2Vec512_Traits \ + { \ + typedef vec_type_ vec_type; \ + } + +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(uchar, v_uint8x64); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(schar, v_int8x64); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(ushort, v_uint16x32); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(short, v_int16x32); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(unsigned, v_uint32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(int, v_int32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(float, v_float32x16); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(uint64, v_uint64x8); +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(int64, v_int64x8); +#if CV_SIMD512_64F +CV_INTRIN_DEF_TYPE2VEC512_TRAITS(double, v_float64x8); +#endif + +template static inline +typename Type2Vec512_Traits<_T>::vec_type v512_setall(const _T& a); + +template<> inline Type2Vec512_Traits< uchar>::vec_type v512_setall< uchar>(const uchar& a) { return v512_setall_u8(a); } +template<> inline Type2Vec512_Traits< schar>::vec_type v512_setall< schar>(const schar& a) { return v512_setall_s8(a); } +template<> inline Type2Vec512_Traits::vec_type v512_setall(const ushort& a) { return v512_setall_u16(a); } +template<> inline Type2Vec512_Traits< short>::vec_type v512_setall< short>(const short& a) { return v512_setall_s16(a); } +template<> inline Type2Vec512_Traits< uint>::vec_type v512_setall< uint>(const uint& a) { return v512_setall_u32(a); } +template<> inline Type2Vec512_Traits< int>::vec_type v512_setall< int>(const int& a) { return v512_setall_s32(a); } +template<> inline Type2Vec512_Traits::vec_type v512_setall(const uint64& a) { return v512_setall_u64(a); } +template<> inline Type2Vec512_Traits< int64>::vec_type v512_setall< int64>(const int64& a) { return v512_setall_s64(a); } +template<> inline Type2Vec512_Traits< float>::vec_type v512_setall< float>(const float& a) { return v512_setall_f32(a); } +#if CV_SIMD512_64F +template<> inline Type2Vec512_Traits::vec_type v512_setall(const double& a) { return v512_setall_f64(a); } +#endif + +#endif // SIMD512 + + +#if CV_SIMD_WIDTH == 16 +template static inline +typename Type2Vec128_Traits<_T>::vec_type vx_setall(const _T& a) { return v_setall(a); } +#elif CV_SIMD_WIDTH == 32 +template static inline +typename Type2Vec256_Traits<_T>::vec_type vx_setall(const _T& a) { return v256_setall(a); } +#elif CV_SIMD_WIDTH == 64 +template static inline +typename Type2Vec512_Traits<_T>::vec_type vx_setall(const _T& a) { return v512_setall(a); } +#else +#error "Build configuration error, unsupported CV_SIMD_WIDTH" +#endif + + +#endif // OPENCV_HAL_INTRIN_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/ippasync.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ippasync.hpp new file mode 100644 index 0000000..c35d8d8 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ippasync.hpp @@ -0,0 +1,195 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_IPPASYNC_HPP +#define OPENCV_CORE_IPPASYNC_HPP + +#ifdef HAVE_IPP_A // this file will be removed in OpenCV 4.0 + +#include "opencv2/core.hpp" +#include +#include + +namespace cv +{ + +namespace hpp +{ + +/** @addtogroup core_ipp +This section describes conversion between OpenCV and [Intel® IPP Asynchronous +C/C++](http://software.intel.com/en-us/intel-ipp-preview) library. [Getting Started +Guide](http://registrationcenter.intel.com/irc_nas/3727/ipp_async_get_started.htm) help you to +install the library, configure header and library build paths. + */ +//! @{ + + //! convert OpenCV data type to hppDataType + inline int toHppType(const int cvType) + { + int depth = CV_MAT_DEPTH(cvType); + int hppType = depth == CV_8U ? HPP_DATA_TYPE_8U : + depth == CV_16U ? HPP_DATA_TYPE_16U : + depth == CV_16S ? HPP_DATA_TYPE_16S : + depth == CV_32S ? HPP_DATA_TYPE_32S : + depth == CV_32F ? HPP_DATA_TYPE_32F : + depth == CV_64F ? HPP_DATA_TYPE_64F : -1; + CV_Assert( hppType >= 0 ); + return hppType; + } + + //! convert hppDataType to OpenCV data type + inline int toCvType(const int hppType) + { + int cvType = hppType == HPP_DATA_TYPE_8U ? CV_8U : + hppType == HPP_DATA_TYPE_16U ? CV_16U : + hppType == HPP_DATA_TYPE_16S ? CV_16S : + hppType == HPP_DATA_TYPE_32S ? CV_32S : + hppType == HPP_DATA_TYPE_32F ? CV_32F : + hppType == HPP_DATA_TYPE_64F ? CV_64F : -1; + CV_Assert( cvType >= 0 ); + return cvType; + } + + /** @brief Convert hppiMatrix to Mat. + + This function allocates and initializes new matrix (if needed) that has the same size and type as + input matrix. Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + @param src input hppiMatrix. + @param dst output matrix. + @param accel accelerator instance (see hpp::getHpp for the list of acceleration framework types). + @param cn number of channels. + */ + inline void copyHppToMat(hppiMatrix* src, Mat& dst, hppAccel accel, int cn) + { + hppDataType type; + hpp32u width, height; + hppStatus sts; + + if (src == NULL) + return dst.release(); + + sts = hppiInquireMatrix(src, &type, &width, &height); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + + int matType = CV_MAKETYPE(toCvType(type), cn); + + CV_Assert(width%cn == 0); + + width /= cn; + + dst.create((int)height, (int)width, (int)matType); + + size_t newSize = (size_t)(height*(hpp32u)(dst.step)); + + sts = hppiGetMatrixData(accel,src,(hpp32u)(dst.step),dst.data,&newSize); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + } + + /** @brief Create Mat from hppiMatrix. + + This function allocates and initializes the Mat that has the same size and type as input matrix. + Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + @param src input hppiMatrix. + @param accel accelerator instance (see hpp::getHpp for the list of acceleration framework types). + @param cn number of channels. + @sa howToUseIPPAconversion, hpp::copyHppToMat, hpp::getHpp. + */ + inline Mat getMat(hppiMatrix* src, hppAccel accel, int cn) + { + Mat dst; + copyHppToMat(src, dst, accel, cn); + return dst; + } + + /** @brief Create hppiMatrix from Mat. + + This function allocates and initializes the hppiMatrix that has the same size and type as input + matrix, returns the hppiMatrix*. + + If you want to use zero-copy for GPU you should to have 4KB aligned matrix data. See details + [hppiCreateSharedMatrix](http://software.intel.com/ru-ru/node/501697). + + Supports CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. + + @note The hppiMatrix pointer to the image buffer in system memory refers to the src.data. Control + the lifetime of the matrix and don't change its data, if there is no special need. + @param src input matrix. + @param accel accelerator instance. Supports type: + - **HPP_ACCEL_TYPE_CPU** - accelerated by optimized CPU instructions. + - **HPP_ACCEL_TYPE_GPU** - accelerated by GPU programmable units or fixed-function + accelerators. + - **HPP_ACCEL_TYPE_ANY** - any acceleration or no acceleration available. + @sa howToUseIPPAconversion, hpp::getMat + */ + inline hppiMatrix* getHpp(const Mat& src, hppAccel accel) + { + int htype = toHppType(src.type()); + int cn = src.channels(); + + CV_Assert(src.data); + hppAccelType accelType = hppQueryAccelType(accel); + + if (accelType!=HPP_ACCEL_TYPE_CPU) + { + hpp32u pitch, size; + hppQueryMatrixAllocParams(accel, src.cols*cn, src.rows, htype, &pitch, &size); + if (pitch!=0 && size!=0) + if ((int)(src.data)%4096==0 && pitch==(hpp32u)(src.step)) + { + return hppiCreateSharedMatrix(htype, src.cols*cn, src.rows, src.data, pitch, size); + } + } + + return hppiCreateMatrix(htype, src.cols*cn, src.rows, src.data, (hpp32s)(src.step));; + } + +//! @} +}} + +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.hpp new file mode 100644 index 0000000..d0ce61e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.hpp @@ -0,0 +1,3766 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MAT_HPP +#define OPENCV_CORE_MAT_HPP + +#ifndef __cplusplus +# error mat.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/matx.hpp" +#include "opencv2/core/types.hpp" + +#include "opencv2/core/bufferpool.hpp" + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +enum { ACCESS_READ=1<<24, ACCESS_WRITE=1<<25, + ACCESS_RW=3<<24, ACCESS_MASK=ACCESS_RW, ACCESS_FAST=1<<26 }; + +CV__DEBUG_NS_BEGIN + +class CV_EXPORTS _OutputArray; + +//////////////////////// Input/Output Array Arguments ///////////////////////////////// + +/** @brief This is the proxy class for passing read-only input arrays into OpenCV functions. + +It is defined as: +@code + typedef const _InputArray& InputArray; +@endcode +where _InputArray is a class that can be constructed from `Mat`, `Mat_`, `Matx`, +`std::vector`, `std::vector >`, `std::vector`, `std::vector >`, +`UMat`, `std::vector` or `double`. It can also be constructed from a matrix expression. + +Since this is mostly implementation-level class, and its interface may change in future versions, we +do not describe it in details. There are a few key things, though, that should be kept in mind: + +- When you see in the reference manual or in OpenCV source code a function that takes + InputArray, it means that you can actually pass `Mat`, `Matx`, `vector` etc. (see above the + complete list). +- Optional input arguments: If some of the input arrays may be empty, pass cv::noArray() (or + simply cv::Mat() as you probably did before). +- The class is designed solely for passing parameters. That is, normally you *should not* + declare class members, local and global variables of this type. +- If you want to design your own function or a class method that can operate of arrays of + multiple types, you can use InputArray (or OutputArray) for the respective parameters. Inside + a function you should use _InputArray::getMat() method to construct a matrix header for the + array (without copying data). _InputArray::kind() can be used to distinguish Mat from + `vector<>` etc., but normally it is not needed. + +Here is how you can use a function that takes InputArray : +@code + std::vector vec; + // points or a circle + for( int i = 0; i < 30; i++ ) + vec.push_back(Point2f((float)(100 + 30*cos(i*CV_PI*2/5)), + (float)(100 - 30*sin(i*CV_PI*2/5)))); + cv::transform(vec, vec, cv::Matx23f(0.707, -0.707, 10, 0.707, 0.707, 20)); +@endcode +That is, we form an STL vector containing points, and apply in-place affine transformation to the +vector using the 2x3 matrix created inline as `Matx` instance. + +Here is how such a function can be implemented (for simplicity, we implement a very specific case of +it, according to the assertion statement inside) : +@code + void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m) + { + // get Mat headers for input arrays. This is O(1) operation, + // unless _src and/or _m are matrix expressions. + Mat src = _src.getMat(), m = _m.getMat(); + CV_Assert( src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2) ); + + // [re]create the output array so that it has the proper size and type. + // In case of Mat it calls Mat::create, in case of STL vector it calls vector::resize. + _dst.create(src.size(), src.type()); + Mat dst = _dst.getMat(); + + for( int i = 0; i < src.rows; i++ ) + for( int j = 0; j < src.cols; j++ ) + { + Point2f pt = src.at(i, j); + dst.at(i, j) = Point2f(m.at(0, 0)*pt.x + + m.at(0, 1)*pt.y + + m.at(0, 2), + m.at(1, 0)*pt.x + + m.at(1, 1)*pt.y + + m.at(1, 2)); + } + } +@endcode +There is another related type, InputArrayOfArrays, which is currently defined as a synonym for +InputArray: +@code + typedef InputArray InputArrayOfArrays; +@endcode +It denotes function arguments that are either vectors of vectors or vectors of matrices. A separate +synonym is needed to generate Python/Java etc. wrappers properly. At the function implementation +level their use is similar, but _InputArray::getMat(idx) should be used to get header for the +idx-th component of the outer vector and _InputArray::size().area() should be used to find the +number of components (vectors/matrices) of the outer vector. + +In general, type support is limited to cv::Mat types. Other types are forbidden. +But in some cases we need to support passing of custom non-general Mat types, like arrays of cv::KeyPoint, cv::DMatch, etc. +This data is not intended to be interpreted as an image data, or processed somehow like regular cv::Mat. +To pass such custom type use rawIn() / rawOut() / rawInOut() wrappers. +Custom type is wrapped as Mat-compatible `CV_8UC` values (N = sizeof(T), N <= CV_CN_MAX). + */ +class CV_EXPORTS _InputArray +{ +public: + enum { + KIND_SHIFT = 16, + FIXED_TYPE = 0x8000 << KIND_SHIFT, + FIXED_SIZE = 0x4000 << KIND_SHIFT, + KIND_MASK = 31 << KIND_SHIFT, + + NONE = 0 << KIND_SHIFT, + MAT = 1 << KIND_SHIFT, + MATX = 2 << KIND_SHIFT, + STD_VECTOR = 3 << KIND_SHIFT, + STD_VECTOR_VECTOR = 4 << KIND_SHIFT, + STD_VECTOR_MAT = 5 << KIND_SHIFT, +#if OPENCV_ABI_COMPATIBILITY < 500 + EXPR = 6 << KIND_SHIFT, //!< removed: https://github.com/opencv/opencv/pull/17046 +#endif + OPENGL_BUFFER = 7 << KIND_SHIFT, + CUDA_HOST_MEM = 8 << KIND_SHIFT, + CUDA_GPU_MAT = 9 << KIND_SHIFT, + UMAT =10 << KIND_SHIFT, + STD_VECTOR_UMAT =11 << KIND_SHIFT, + STD_BOOL_VECTOR =12 << KIND_SHIFT, + STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT, +#if OPENCV_ABI_COMPATIBILITY < 500 + STD_ARRAY =14 << KIND_SHIFT, //!< removed: https://github.com/opencv/opencv/issues/18897 +#endif + STD_ARRAY_MAT =15 << KIND_SHIFT + }; + + _InputArray(); + _InputArray(int _flags, void* _obj); + _InputArray(const Mat& m); + _InputArray(const MatExpr& expr); + _InputArray(const std::vector& vec); + template _InputArray(const Mat_<_Tp>& m); + template _InputArray(const std::vector<_Tp>& vec); + _InputArray(const std::vector& vec); + template _InputArray(const std::vector >& vec); + _InputArray(const std::vector >&); + template _InputArray(const std::vector >& vec); + template _InputArray(const _Tp* vec, int n); + template _InputArray(const Matx<_Tp, m, n>& matx); + _InputArray(const double& val); + _InputArray(const cuda::GpuMat& d_mat); + _InputArray(const std::vector& d_mat_array); + _InputArray(const ogl::Buffer& buf); + _InputArray(const cuda::HostMem& cuda_mem); + template _InputArray(const cudev::GpuMat_<_Tp>& m); + _InputArray(const UMat& um); + _InputArray(const std::vector& umv); + +#ifdef CV_CXX_STD_ARRAY + template _InputArray(const std::array<_Tp, _Nm>& arr); + template _InputArray(const std::array& arr); +#endif + + template static _InputArray rawIn(const std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _InputArray rawIn(const std::array<_Tp, _Nm>& arr); +#endif + + Mat getMat(int idx=-1) const; + Mat getMat_(int idx=-1) const; + UMat getUMat(int idx=-1) const; + void getMatVector(std::vector& mv) const; + void getUMatVector(std::vector& umv) const; + void getGpuMatVector(std::vector& gpumv) const; + cuda::GpuMat getGpuMat() const; + ogl::Buffer getOGlBuffer() const; + + int getFlags() const; + void* getObj() const; + Size getSz() const; + + int kind() const; + int dims(int i=-1) const; + int cols(int i=-1) const; + int rows(int i=-1) const; + Size size(int i=-1) const; + int sizend(int* sz, int i=-1) const; + bool sameSize(const _InputArray& arr) const; + size_t total(int i=-1) const; + int type(int i=-1) const; + int depth(int i=-1) const; + int channels(int i=-1) const; + bool isContinuous(int i=-1) const; + bool isSubmatrix(int i=-1) const; + bool empty() const; + void copyTo(const _OutputArray& arr) const; + void copyTo(const _OutputArray& arr, const _InputArray & mask) const; + size_t offset(int i=-1) const; + size_t step(int i=-1) const; + bool isMat() const; + bool isUMat() const; + bool isMatVector() const; + bool isUMatVector() const; + bool isMatx() const; + bool isVector() const; + bool isGpuMat() const; + bool isGpuMatVector() const; + ~_InputArray(); + +protected: + int flags; + void* obj; + Size sz; + + void init(int _flags, const void* _obj); + void init(int _flags, const void* _obj, Size _sz); +}; + + +/** @brief This type is very similar to InputArray except that it is used for input/output and output function +parameters. + +Just like with InputArray, OpenCV users should not care about OutputArray, they just pass `Mat`, +`vector` etc. to the functions. The same limitation as for `InputArray`: *Do not explicitly +create OutputArray instances* applies here too. + +If you want to make your function polymorphic (i.e. accept different arrays as output parameters), +it is also not very difficult. Take the sample above as the reference. Note that +_OutputArray::create() needs to be called before _OutputArray::getMat(). This way you guarantee +that the output array is properly allocated. + +Optional output parameters. If you do not need certain output array to be computed and returned to +you, pass cv::noArray(), just like you would in the case of optional input array. At the +implementation level, use _OutputArray::needed() to check if certain output array needs to be +computed or not. + +There are several synonyms for OutputArray that are used to assist automatic Python/Java/... wrapper +generators: +@code + typedef OutputArray OutputArrayOfArrays; + typedef OutputArray InputOutputArray; + typedef OutputArray InputOutputArrayOfArrays; +@endcode + */ +class CV_EXPORTS _OutputArray : public _InputArray +{ +public: + enum + { + DEPTH_MASK_8U = 1 << CV_8U, + DEPTH_MASK_8S = 1 << CV_8S, + DEPTH_MASK_16U = 1 << CV_16U, + DEPTH_MASK_16S = 1 << CV_16S, + DEPTH_MASK_32S = 1 << CV_32S, + DEPTH_MASK_32F = 1 << CV_32F, + DEPTH_MASK_64F = 1 << CV_64F, + DEPTH_MASK_ALL = (DEPTH_MASK_64F<<1)-1, + DEPTH_MASK_ALL_BUT_8S = DEPTH_MASK_ALL & ~DEPTH_MASK_8S, + DEPTH_MASK_FLT = DEPTH_MASK_32F + DEPTH_MASK_64F + }; + + _OutputArray(); + _OutputArray(int _flags, void* _obj); + _OutputArray(Mat& m); + _OutputArray(std::vector& vec); + _OutputArray(cuda::GpuMat& d_mat); + _OutputArray(std::vector& d_mat); + _OutputArray(ogl::Buffer& buf); + _OutputArray(cuda::HostMem& cuda_mem); + template _OutputArray(cudev::GpuMat_<_Tp>& m); + template _OutputArray(std::vector<_Tp>& vec); + _OutputArray(std::vector& vec); + template _OutputArray(std::vector >& vec); + _OutputArray(std::vector >&); + template _OutputArray(std::vector >& vec); + template _OutputArray(Mat_<_Tp>& m); + template _OutputArray(_Tp* vec, int n); + template _OutputArray(Matx<_Tp, m, n>& matx); + _OutputArray(UMat& m); + _OutputArray(std::vector& vec); + + _OutputArray(const Mat& m); + _OutputArray(const std::vector& vec); + _OutputArray(const cuda::GpuMat& d_mat); + _OutputArray(const std::vector& d_mat); + _OutputArray(const ogl::Buffer& buf); + _OutputArray(const cuda::HostMem& cuda_mem); + template _OutputArray(const cudev::GpuMat_<_Tp>& m); + template _OutputArray(const std::vector<_Tp>& vec); + template _OutputArray(const std::vector >& vec); + template _OutputArray(const std::vector >& vec); + template _OutputArray(const Mat_<_Tp>& m); + template _OutputArray(const _Tp* vec, int n); + template _OutputArray(const Matx<_Tp, m, n>& matx); + _OutputArray(const UMat& m); + _OutputArray(const std::vector& vec); + +#ifdef CV_CXX_STD_ARRAY + template _OutputArray(std::array<_Tp, _Nm>& arr); + template _OutputArray(const std::array<_Tp, _Nm>& arr); + template _OutputArray(std::array& arr); + template _OutputArray(const std::array& arr); +#endif + + template static _OutputArray rawOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _OutputArray rawOut(std::array<_Tp, _Nm>& arr); +#endif + + bool fixedSize() const; + bool fixedType() const; + bool needed() const; + Mat& getMatRef(int i=-1) const; + UMat& getUMatRef(int i=-1) const; + cuda::GpuMat& getGpuMatRef() const; + std::vector& getGpuMatVecRef() const; + ogl::Buffer& getOGlBufferRef() const; + cuda::HostMem& getHostMemRef() const; + void create(Size sz, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void create(int rows, int cols, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void create(int dims, const int* size, int type, int i=-1, bool allowTransposed=false, int fixedDepthMask=0) const; + void createSameSize(const _InputArray& arr, int mtype) const; + void release() const; + void clear() const; + void setTo(const _InputArray& value, const _InputArray & mask = _InputArray()) const; + + void assign(const UMat& u) const; + void assign(const Mat& m) const; + + void assign(const std::vector& v) const; + void assign(const std::vector& v) const; + + void move(UMat& u) const; + void move(Mat& m) const; +}; + + +class CV_EXPORTS _InputOutputArray : public _OutputArray +{ +public: + _InputOutputArray(); + _InputOutputArray(int _flags, void* _obj); + _InputOutputArray(Mat& m); + _InputOutputArray(std::vector& vec); + _InputOutputArray(cuda::GpuMat& d_mat); + _InputOutputArray(ogl::Buffer& buf); + _InputOutputArray(cuda::HostMem& cuda_mem); + template _InputOutputArray(cudev::GpuMat_<_Tp>& m); + template _InputOutputArray(std::vector<_Tp>& vec); + _InputOutputArray(std::vector& vec); + template _InputOutputArray(std::vector >& vec); + template _InputOutputArray(std::vector >& vec); + template _InputOutputArray(Mat_<_Tp>& m); + template _InputOutputArray(_Tp* vec, int n); + template _InputOutputArray(Matx<_Tp, m, n>& matx); + _InputOutputArray(UMat& m); + _InputOutputArray(std::vector& vec); + + _InputOutputArray(const Mat& m); + _InputOutputArray(const std::vector& vec); + _InputOutputArray(const cuda::GpuMat& d_mat); + _InputOutputArray(const std::vector& d_mat); + _InputOutputArray(const ogl::Buffer& buf); + _InputOutputArray(const cuda::HostMem& cuda_mem); + template _InputOutputArray(const cudev::GpuMat_<_Tp>& m); + template _InputOutputArray(const std::vector<_Tp>& vec); + template _InputOutputArray(const std::vector >& vec); + template _InputOutputArray(const std::vector >& vec); + template _InputOutputArray(const Mat_<_Tp>& m); + template _InputOutputArray(const _Tp* vec, int n); + template _InputOutputArray(const Matx<_Tp, m, n>& matx); + _InputOutputArray(const UMat& m); + _InputOutputArray(const std::vector& vec); + +#ifdef CV_CXX_STD_ARRAY + template _InputOutputArray(std::array<_Tp, _Nm>& arr); + template _InputOutputArray(const std::array<_Tp, _Nm>& arr); + template _InputOutputArray(std::array& arr); + template _InputOutputArray(const std::array& arr); +#endif + + template static _InputOutputArray rawInOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template _InputOutputArray rawInOut(std::array<_Tp, _Nm>& arr); +#endif + +}; + +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputArray rawIn(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _OutputArray rawOut(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputOutputArray rawInOut(_Tp& v); + +CV__DEBUG_NS_END + +typedef const _InputArray& InputArray; +typedef InputArray InputArrayOfArrays; +typedef const _OutputArray& OutputArray; +typedef OutputArray OutputArrayOfArrays; +typedef const _InputOutputArray& InputOutputArray; +typedef InputOutputArray InputOutputArrayOfArrays; + +CV_EXPORTS InputOutputArray noArray(); + +/////////////////////////////////// MatAllocator ////////////////////////////////////// + +//! Usage flags for allocator +enum UMatUsageFlags +{ + USAGE_DEFAULT = 0, + + // buffer allocation policy is platform and usage specific + USAGE_ALLOCATE_HOST_MEMORY = 1 << 0, + USAGE_ALLOCATE_DEVICE_MEMORY = 1 << 1, + USAGE_ALLOCATE_SHARED_MEMORY = 1 << 2, // It is not equal to: USAGE_ALLOCATE_HOST_MEMORY | USAGE_ALLOCATE_DEVICE_MEMORY + + __UMAT_USAGE_FLAGS_32BIT = 0x7fffffff // Binary compatibility hint +}; + +struct CV_EXPORTS UMatData; + +/** @brief Custom array allocator +*/ +class CV_EXPORTS MatAllocator +{ +public: + MatAllocator() {} + virtual ~MatAllocator() {} + + // let's comment it off for now to detect and fix all the uses of allocator + //virtual void allocate(int dims, const int* sizes, int type, int*& refcount, + // uchar*& datastart, uchar*& data, size_t* step) = 0; + //virtual void deallocate(int* refcount, uchar* datastart, uchar* data) = 0; + virtual UMatData* allocate(int dims, const int* sizes, int type, + void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const = 0; + virtual bool allocate(UMatData* data, int accessflags, UMatUsageFlags usageFlags) const = 0; + virtual void deallocate(UMatData* data) const = 0; + virtual void map(UMatData* data, int accessflags) const; + virtual void unmap(UMatData* data) const; + virtual void download(UMatData* data, void* dst, int dims, const size_t sz[], + const size_t srcofs[], const size_t srcstep[], + const size_t dststep[]) const; + virtual void upload(UMatData* data, const void* src, int dims, const size_t sz[], + const size_t dstofs[], const size_t dststep[], + const size_t srcstep[]) const; + virtual void copy(UMatData* srcdata, UMatData* dstdata, int dims, const size_t sz[], + const size_t srcofs[], const size_t srcstep[], + const size_t dstofs[], const size_t dststep[], bool sync) const; + + // default implementation returns DummyBufferPoolController + virtual BufferPoolController* getBufferPoolController(const char* id = NULL) const; +}; + + +//////////////////////////////// MatCommaInitializer ////////////////////////////////// + +/** @brief Comma-separated Matrix Initializer + + The class instances are usually not created explicitly. + Instead, they are created on "matrix << firstValue" operator. + + The sample below initializes 2x2 rotation matrix: + + \code + double angle = 30, a = cos(angle*CV_PI/180), b = sin(angle*CV_PI/180); + Mat R = (Mat_(2,2) << a, -b, b, a); + \endcode +*/ +template class MatCommaInitializer_ +{ +public: + //! the constructor, created by "matrix << firstValue" operator, where matrix is cv::Mat + MatCommaInitializer_(Mat_<_Tp>* _m); + //! the operator that takes the next value and put it to the matrix + template MatCommaInitializer_<_Tp>& operator , (T2 v); + //! another form of conversion operator + operator Mat_<_Tp>() const; +protected: + MatIterator_<_Tp> it; +}; + + +/////////////////////////////////////// Mat /////////////////////////////////////////// + +// note that umatdata might be allocated together +// with the matrix data, not as a separate object. +// therefore, it does not have constructor or destructor; +// it should be explicitly initialized using init(). +struct CV_EXPORTS UMatData +{ + enum { COPY_ON_MAP=1, HOST_COPY_OBSOLETE=2, + DEVICE_COPY_OBSOLETE=4, TEMP_UMAT=8, TEMP_COPIED_UMAT=24, + USER_ALLOCATED=32, DEVICE_MEM_MAPPED=64, + ASYNC_CLEANUP=128 + }; + UMatData(const MatAllocator* allocator); + ~UMatData(); + + // provide atomic access to the structure + void lock(); + void unlock(); + + bool hostCopyObsolete() const; + bool deviceCopyObsolete() const; + bool deviceMemMapped() const; + bool copyOnMap() const; + bool tempUMat() const; + bool tempCopiedUMat() const; + void markHostCopyObsolete(bool flag); + void markDeviceCopyObsolete(bool flag); + void markDeviceMemMapped(bool flag); + + const MatAllocator* prevAllocator; + const MatAllocator* currAllocator; + int urefcount; + int refcount; + uchar* data; + uchar* origdata; + size_t size; + + int flags; + void* handle; + void* userdata; + int allocatorFlags_; + int mapcount; + UMatData* originalUMatData; +}; + + +struct CV_EXPORTS MatSize +{ + explicit MatSize(int* _p) CV_NOEXCEPT; + int dims() const CV_NOEXCEPT; + Size operator()() const; + const int& operator[](int i) const; + int& operator[](int i); + operator const int*() const CV_NOEXCEPT; // TODO OpenCV 4.0: drop this + bool operator == (const MatSize& sz) const CV_NOEXCEPT; + bool operator != (const MatSize& sz) const CV_NOEXCEPT; + + int* p; +}; + +struct CV_EXPORTS MatStep +{ + MatStep() CV_NOEXCEPT; + explicit MatStep(size_t s) CV_NOEXCEPT; + const size_t& operator[](int i) const CV_NOEXCEPT; + size_t& operator[](int i) CV_NOEXCEPT; + operator size_t() const; + MatStep& operator = (size_t s); + + size_t* p; + size_t buf[2]; +protected: + MatStep& operator = (const MatStep&); +}; + +/** @example samples/cpp/cout_mat.cpp +An example demonstrating the serial out capabilities of cv::Mat +*/ + + /** @brief n-dimensional dense array class \anchor CVMat_Details + +The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It +can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel +volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms +may be better stored in a SparseMat ). The data layout of the array `M` is defined by the array +`M.step[]`, so that the address of element \f$(i_0,...,i_{M.dims-1})\f$, where \f$0\leq i_k= M.step[i+1]` (in fact, `M.step[i] >= M.step[i+1]*M.size[i+1]` ). This means +that 2-dimensional matrices are stored row-by-row, 3-dimensional matrices are stored plane-by-plane, +and so on. M.step[M.dims-1] is minimal and always equal to the element size M.elemSize() . + +So, the data layout in Mat is fully compatible with CvMat, IplImage, and CvMatND types from OpenCV +1.x. It is also compatible with the majority of dense array types from the standard toolkits and +SDKs, such as Numpy (ndarray), Win32 (independent device bitmaps), and others, that is, with any +array that uses *steps* (or *strides*) to compute the position of a pixel. Due to this +compatibility, it is possible to make a Mat header for user-allocated data and process it in-place +using OpenCV functions. + +There are many different ways to create a Mat object. The most popular options are listed below: + +- Use the create(nrows, ncols, type) method or the similar Mat(nrows, ncols, type[, fillValue]) +constructor. A new array of the specified size and type is allocated. type has the same meaning as +in the cvCreateMat method. For example, CV_8UC1 means a 8-bit single-channel array, CV_32FC2 +means a 2-channel (complex) floating-point array, and so on. +@code + // make a 7x7 complex matrix filled with 1+3j. + Mat M(7,7,CV_32FC2,Scalar(1,3)); + // and now turn M to a 100x60 15-channel 8-bit matrix. + // The old content will be deallocated + M.create(100,60,CV_8UC(15)); +@endcode +As noted in the introduction to this chapter, create() allocates only a new array when the shape +or type of the current array are different from the specified ones. + +- Create a multi-dimensional array: +@code + // create a 100x100x100 8-bit array + int sz[] = {100, 100, 100}; + Mat bigCube(3, sz, CV_8U, Scalar::all(0)); +@endcode +It passes the number of dimensions =1 to the Mat constructor but the created array will be +2-dimensional with the number of columns set to 1. So, Mat::dims is always \>= 2 (can also be 0 +when the array is empty). + +- Use a copy constructor or assignment operator where there can be an array or expression on the +right side (see below). As noted in the introduction, the array assignment is an O(1) operation +because it only copies the header and increases the reference counter. The Mat::clone() method can +be used to get a full (deep) copy of the array when you need it. + +- Construct a header for a part of another array. It can be a single row, single column, several +rows, several columns, rectangular region in the array (called a *minor* in algebra) or a +diagonal. Such operations are also O(1) because the new header references the same data. You can +actually modify a part of the array using this feature, for example: +@code + // add the 5-th row, multiplied by 3 to the 3rd row + M.row(3) = M.row(3) + M.row(5)*3; + // now copy the 7-th column to the 1-st column + // M.col(1) = M.col(7); // this will not work + Mat M1 = M.col(1); + M.col(7).copyTo(M1); + // create a new 320x240 image + Mat img(Size(320,240),CV_8UC3); + // select a ROI + Mat roi(img, Rect(10,10,100,100)); + // fill the ROI with (0,255,0) (which is green in RGB space); + // the original 320x240 image will be modified + roi = Scalar(0,255,0); +@endcode +Due to the additional datastart and dataend members, it is possible to compute a relative +sub-array position in the main *container* array using locateROI(): +@code + Mat A = Mat::eye(10, 10, CV_32S); + // extracts A columns, 1 (inclusive) to 3 (exclusive). + Mat B = A(Range::all(), Range(1, 3)); + // extracts B rows, 5 (inclusive) to 9 (exclusive). + // that is, C \~ A(Range(5, 9), Range(1, 3)) + Mat C = B(Range(5, 9), Range::all()); + Size size; Point ofs; + C.locateROI(size, ofs); + // size will be (width=10,height=10) and the ofs will be (x=1, y=5) +@endcode +As in case of whole matrices, if you need a deep copy, use the `clone()` method of the extracted +sub-matrices. + +- Make a header for user-allocated data. It can be useful to do the following: + -# Process "foreign" data using OpenCV (for example, when you implement a DirectShow\* filter or + a processing module for gstreamer, and so on). For example: + @code + Mat process_video_frame(const unsigned char* pixels, + int width, int height, int step) + { + // wrap input buffer + Mat img(height, width, CV_8UC3, (unsigned char*)pixels, step); + + Mat result; + GaussianBlur(img, result, Size(7, 7), 1.5, 1.5); + + return result; + } + @endcode + -# Quickly initialize small matrices and/or get a super-fast element access. + @code + double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}}; + Mat M = Mat(3, 3, CV_64F, m).inv(); + @endcode + . + Partial yet very common cases of this *user-allocated data* case are conversions from CvMat and + IplImage to Mat. For this purpose, there is function cv::cvarrToMat taking pointers to CvMat or + IplImage and the optional flag indicating whether to copy the data or not. + @snippet samples/cpp/image.cpp iplimage + +- Use MATLAB-style array initializers, zeros(), ones(), eye(), for example: +@code + // create a double-precision identity matrix and add it to M. + M += Mat::eye(M.rows, M.cols, CV_64F); +@endcode + +- Use a comma-separated initializer: +@code + // create a 3x3 double-precision identity matrix + Mat M = (Mat_(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); +@endcode +With this approach, you first call a constructor of the Mat class with the proper parameters, and +then you just put `<< operator` followed by comma-separated values that can be constants, +variables, expressions, and so on. Also, note the extra parentheses required to avoid compilation +errors. + +Once the array is created, it is automatically managed via a reference-counting mechanism. If the +array header is built on top of user-allocated data, you should handle the data by yourself. The +array data is deallocated when no one points to it. If you want to release the data pointed by a +array header before the array destructor is called, use Mat::release(). + +The next important thing to learn about the array class is element access. This manual already +described how to compute an address of each array element. Normally, you are not required to use the +formula directly in the code. If you know the array element type (which can be retrieved using the +method Mat::type() ), you can access the element \f$M_{ij}\f$ of a 2-dimensional array as: +@code + M.at(i,j) += 1.f; +@endcode +assuming that `M` is a double-precision floating-point array. There are several variants of the method +at for a different number of dimensions. + +If you need to process a whole row of a 2D array, the most efficient way is to get the pointer to +the row first, and then just use the plain C operator [] : +@code + // compute sum of positive matrix elements + // (assuming that M is a double-precision matrix) + double sum=0; + for(int i = 0; i < M.rows; i++) + { + const double* Mi = M.ptr(i); + for(int j = 0; j < M.cols; j++) + sum += std::max(Mi[j], 0.); + } +@endcode +Some operations, like the one above, do not actually depend on the array shape. They just process +elements of an array one by one (or elements from multiple arrays that have the same coordinates, +for example, array addition). Such operations are called *element-wise*. It makes sense to check +whether all the input/output arrays are continuous, namely, have no gaps at the end of each row. If +yes, process them as a long single row: +@code + // compute the sum of positive matrix elements, optimized variant + double sum=0; + int cols = M.cols, rows = M.rows; + if(M.isContinuous()) + { + cols *= rows; + rows = 1; + } + for(int i = 0; i < rows; i++) + { + const double* Mi = M.ptr(i); + for(int j = 0; j < cols; j++) + sum += std::max(Mi[j], 0.); + } +@endcode +In case of the continuous matrix, the outer loop body is executed just once. So, the overhead is +smaller, which is especially noticeable in case of small matrices. + +Finally, there are STL-style iterators that are smart enough to skip gaps between successive rows: +@code + // compute sum of positive matrix elements, iterator-based variant + double sum=0; + MatConstIterator_ it = M.begin(), it_end = M.end(); + for(; it != it_end; ++it) + sum += std::max(*it, 0.); +@endcode +The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, +including std::sort(). + +@note Matrix Expressions and arithmetic see MatExpr +*/ +class CV_EXPORTS Mat +{ +public: + /** + These are various constructors that form a matrix. As noted in the AutomaticAllocation, often + the default constructor is enough, and the proper matrix will be allocated by an OpenCV function. + The constructed matrix can further be assigned to another matrix or matrix expression or can be + allocated with Mat::create . In the former case, the old content is de-referenced. + */ + Mat() CV_NOEXCEPT; + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(int rows, int cols, int type); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(Size size, int type); + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(int rows, int cols, int type, const Scalar& s); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(Size size, int type, const Scalar& s); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(int ndims, const int* sizes, int type); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + */ + Mat(const std::vector& sizes, int type); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(int ndims, const int* sizes, int type, const Scalar& s); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param s An optional value to initialize each matrix element with. To set all the matrix elements to + the particular value after the construction, use the assignment operator + Mat::operator=(const Scalar& value) . + */ + Mat(const std::vector& sizes, int type, const Scalar& s); + + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + */ + Mat(const Mat& m); + + /** @overload + @param rows Number of rows in a 2D array. + @param cols Number of columns in a 2D array. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param step Number of bytes each matrix row occupies. The value should include the padding bytes at + the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed + and the actual step is calculated as cols*elemSize(). See Mat::elemSize. + */ + Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP); + + /** @overload + @param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and the + number of columns go in the reverse order. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param step Number of bytes each matrix row occupies. The value should include the padding bytes at + the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed + and the actual step is calculated as cols*elemSize(). See Mat::elemSize. + */ + Mat(Size size, int type, void* data, size_t step=AUTO_STEP); + + /** @overload + @param ndims Array dimensionality. + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param steps Array of ndims-1 steps in case of a multi-dimensional array (the last step is always + set to the element size). If not specified, the matrix is assumed to be continuous. + */ + Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0); + + /** @overload + @param sizes Array of integers specifying an n-dimensional array shape. + @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or + CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices. + @param data Pointer to the user data. Matrix constructors that take data and step parameters do not + allocate matrix data. Instead, they just initialize the matrix header that points to the specified + data, which means that no data is copied. This operation is very efficient and can be used to + process external data using OpenCV functions. The external data is not automatically deallocated, so + you should take care of it. + @param steps Array of ndims-1 steps in case of a multi-dimensional array (the last step is always + set to the element size). If not specified, the matrix is assumed to be continuous. + */ + Mat(const std::vector& sizes, int type, void* data, const size_t* steps=0); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param rowRange Range of the m rows to take. As usual, the range start is inclusive and the range + end is exclusive. Use Range::all() to take all the rows. + @param colRange Range of the m columns to take. Use Range::all() to take all the columns. + */ + Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param roi Region of interest. + */ + Mat(const Mat& m, const Rect& roi); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param ranges Array of selected ranges of m along each dimensionality. + */ + Mat(const Mat& m, const Range* ranges); + + /** @overload + @param m Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied + by these constructors. Instead, the header pointing to m data or its sub-array is constructed and + associated with it. The reference counter, if any, is incremented. So, when you modify the matrix + formed using such a constructor, you also modify the corresponding elements of m . If you want to + have an independent copy of the sub-array, use Mat::clone() . + @param ranges Array of selected ranges of m along each dimensionality. + */ + Mat(const Mat& m, const std::vector& ranges); + + /** @overload + @param vec STL vector whose elements form the matrix. The matrix has a single column and the number + of rows equal to the number of vector elements. Type of the matrix matches the type of vector + elements. The constructor can handle arbitrary types, for which there is a properly declared + DataType . This means that the vector elements must be primitive numbers or uni-type numerical + tuples of numbers. Mixed-type structures are not supported. The corresponding constructor is + explicit. Since STL vectors are not automatically converted to Mat instances, you should write + Mat(vec) explicitly. Unless you copy the data into the matrix ( copyData=true ), no new elements + will be added to the vector because it can potentially yield vector data reallocation, and, thus, + the matrix data pointer will be invalid. + @param copyData Flag to specify whether the underlying data of the STL vector should be copied + to (true) or shared with (false) the newly constructed matrix. When the data is copied, the + allocated buffer is managed using Mat reference counting mechanism. While the data is shared, + the reference counter is NULL, and you should not deallocate the data until the matrix is not + destructed. + */ + template explicit Mat(const std::vector<_Tp>& vec, bool copyData=false); + +#ifdef CV_CXX11 + /** @overload + */ + template::value>::type> + explicit Mat(const std::initializer_list<_Tp> list); + + /** @overload + */ + template explicit Mat(const std::initializer_list sizes, const std::initializer_list<_Tp> list); +#endif + +#ifdef CV_CXX_STD_ARRAY + /** @overload + */ + template explicit Mat(const std::array<_Tp, _Nm>& arr, bool copyData=false); +#endif + + /** @overload + */ + template explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Point_<_Tp>& pt, bool copyData=true); + + /** @overload + */ + template explicit Mat(const Point3_<_Tp>& pt, bool copyData=true); + + /** @overload + */ + template explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer); + + //! download data from GpuMat + explicit Mat(const cuda::GpuMat& m); + + //! destructor - calls release() + ~Mat(); + + /** @brief assignment operators + + These are available assignment operators. Since they all are very different, make sure to read the + operator parameters description. + @param m Assigned, right-hand-side matrix. Matrix assignment is an O(1) operation. This means that + no data is copied but the data is shared and the reference counter, if any, is incremented. Before + assigning new data, the old data is de-referenced via Mat::release . + */ + Mat& operator = (const Mat& m); + + /** @overload + @param expr Assigned matrix expression object. As opposite to the first form of the assignment + operation, the second form can reuse already allocated matrix if it has the right size and type to + fit the matrix expression result. It is automatically handled by the real function that the matrix + expressions is expanded to. For example, C=A+B is expanded to add(A, B, C), and add takes care of + automatic C reallocation. + */ + Mat& operator = (const MatExpr& expr); + + //! retrieve UMat from Mat + UMat getUMat(int accessFlags, UMatUsageFlags usageFlags = USAGE_DEFAULT) const; + + /** @brief Creates a matrix header for the specified matrix row. + + The method makes a new header for the specified matrix row and returns it. This is an O(1) + operation, regardless of the matrix size. The underlying data of the new matrix is shared with the + original matrix. Here is the example of one of the classical basic matrix processing operations, + axpy, used by LU and many other algorithms: + @code + inline void matrix_axpy(Mat& A, int i, int j, double alpha) + { + A.row(i) += A.row(j)*alpha; + } + @endcode + @note In the current implementation, the following code does not work as expected: + @code + Mat A; + ... + A.row(i) = A.row(j); // will not work + @endcode + This happens because A.row(i) forms a temporary header that is further assigned to another header. + Remember that each of these operations is O(1), that is, no data is copied. Thus, the above + assignment is not true if you may have expected the j-th row to be copied to the i-th row. To + achieve that, you should either turn this simple assignment into an expression or use the + Mat::copyTo method: + @code + Mat A; + ... + // works, but looks a bit obscure. + A.row(i) = A.row(j) + 0; + // this is a bit longer, but the recommended method. + A.row(j).copyTo(A.row(i)); + @endcode + @param y A 0-based row index. + */ + Mat row(int y) const; + + /** @brief Creates a matrix header for the specified matrix column. + + The method makes a new header for the specified matrix column and returns it. This is an O(1) + operation, regardless of the matrix size. The underlying data of the new matrix is shared with the + original matrix. See also the Mat::row description. + @param x A 0-based column index. + */ + Mat col(int x) const; + + /** @brief Creates a matrix header for the specified row span. + + The method makes a new header for the specified row span of the matrix. Similarly to Mat::row and + Mat::col , this is an O(1) operation. + @param startrow An inclusive 0-based start index of the row span. + @param endrow An exclusive 0-based ending index of the row span. + */ + Mat rowRange(int startrow, int endrow) const; + + /** @overload + @param r Range structure containing both the start and the end indices. + */ + Mat rowRange(const Range& r) const; + + /** @brief Creates a matrix header for the specified column span. + + The method makes a new header for the specified column span of the matrix. Similarly to Mat::row and + Mat::col , this is an O(1) operation. + @param startcol An inclusive 0-based start index of the column span. + @param endcol An exclusive 0-based ending index of the column span. + */ + Mat colRange(int startcol, int endcol) const; + + /** @overload + @param r Range structure containing both the start and the end indices. + */ + Mat colRange(const Range& r) const; + + /** @brief Extracts a diagonal from a matrix + + The method makes a new header for the specified matrix diagonal. The new matrix is represented as a + single-column matrix. Similarly to Mat::row and Mat::col, this is an O(1) operation. + @param d index of the diagonal, with the following values: + - `d=0` is the main diagonal. + - `d<0` is a diagonal from the lower half. For example, d=-1 means the diagonal is set + immediately below the main one. + - `d>0` is a diagonal from the upper half. For example, d=1 means the diagonal is set + immediately above the main one. + For example: + @code + Mat m = (Mat_(3,3) << + 1,2,3, + 4,5,6, + 7,8,9); + Mat d0 = m.diag(0); + Mat d1 = m.diag(1); + Mat d_1 = m.diag(-1); + @endcode + The resulting matrices are + @code + d0 = + [1; + 5; + 9] + d1 = + [2; + 6] + d_1 = + [4; + 8] + @endcode + */ + Mat diag(int d=0) const; + + /** @brief creates a diagonal matrix + + The method creates a square diagonal matrix from specified main diagonal. + @param d One-dimensional matrix that represents the main diagonal. + */ + static Mat diag(const Mat& d); + + /** @brief Creates a full copy of the array and the underlying data. + + The method creates a full copy of the array. The original step[] is not taken into account. So, the + array copy is a continuous array occupying total()*elemSize() bytes. + */ + Mat clone() const CV_NODISCARD; + + /** @brief Copies the matrix to another one. + + The method copies the matrix data to another matrix. Before copying the data, the method invokes : + @code + m.create(this->size(), this->type()); + @endcode + so that the destination matrix is reallocated if needed. While m.copyTo(m); works flawlessly, the + function does not handle the case of a partial overlap between the source and the destination + matrices. + + When the operation mask is specified, if the Mat::create call shown above reallocates the matrix, + the newly allocated matrix is initialized with all zeros before copying the data. + @param m Destination matrix. If it does not have a proper size or type before the operation, it is + reallocated. + */ + void copyTo( OutputArray m ) const; + + /** @overload + @param m Destination matrix. If it does not have a proper size or type before the operation, it is + reallocated. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. + */ + void copyTo( OutputArray m, InputArray mask ) const; + + /** @brief Converts an array to another data type with optional scaling. + + The method converts source pixel values to the target data type. saturate_cast\<\> is applied at + the end to avoid possible overflows: + + \f[m(x,y) = saturate \_ cast( \alpha (*this)(x,y) + \beta )\f] + @param m output matrix; if it does not have a proper size or type before the operation, it is + reallocated. + @param rtype desired output matrix type or, rather, the depth since the number of channels are the + same as the input has; if rtype is negative, the output matrix will have the same type as the input. + @param alpha optional scale factor. + @param beta optional delta added to the scaled values. + */ + void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; + + /** @brief Provides a functional form of convertTo. + + This is an internally used method called by the @ref MatrixExpressions engine. + @param m Destination array. + @param type Desired destination array depth (or -1 if it should be the same as the source type). + */ + void assignTo( Mat& m, int type=-1 ) const; + + /** @brief Sets all or some of the array elements to the specified value. + @param s Assigned scalar converted to the actual array type. + */ + Mat& operator = (const Scalar& s); + + /** @brief Sets all or some of the array elements to the specified value. + + This is an advanced variant of the Mat::operator=(const Scalar& s) operator. + @param value Assigned scalar converted to the actual array type. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels + */ + Mat& setTo(InputArray value, InputArray mask=noArray()); + + /** @brief Changes the shape and/or the number of channels of a 2D matrix without copying the data. + + The method makes a new matrix header for \*this elements. The new matrix may have a different size + and/or different number of channels. Any combination is possible if: + - No extra elements are included into the new matrix and no elements are excluded. Consequently, + the product rows\*cols\*channels() must stay the same after the transformation. + - No data is copied. That is, this is an O(1) operation. Consequently, if you change the number of + rows, or the operation changes the indices of elements row in some other way, the matrix must be + continuous. See Mat::isContinuous . + + For example, if there is a set of 3D points stored as an STL vector, and you want to represent the + points as a 3xN matrix, do the following: + @code + std::vector vec; + ... + Mat pointMat = Mat(vec). // convert vector to Mat, O(1) operation + reshape(1). // make Nx3 1-channel matrix out of Nx1 3-channel. + // Also, an O(1) operation + t(); // finally, transpose the Nx3 matrix. + // This involves copying all the elements + @endcode + @param cn New number of channels. If the parameter is 0, the number of channels remains the same. + @param rows New number of rows. If the parameter is 0, the number of rows remains the same. + */ + Mat reshape(int cn, int rows=0) const; + + /** @overload */ + Mat reshape(int cn, int newndims, const int* newsz) const; + + /** @overload */ + Mat reshape(int cn, const std::vector& newshape) const; + + /** @brief Transposes a matrix. + + The method performs matrix transposition by means of matrix expressions. It does not perform the + actual transposition but returns a temporary matrix transposition object that can be further used as + a part of more complex matrix expressions or can be assigned to a matrix: + @code + Mat A1 = A + Mat::eye(A.size(), A.type())*lambda; + Mat C = A1.t()*A1; // compute (A + lambda*I)^t * (A + lamda*I) + @endcode + */ + MatExpr t() const; + + /** @brief Inverses a matrix. + + The method performs a matrix inversion by means of matrix expressions. This means that a temporary + matrix inversion object is returned by the method and can be used further as a part of more complex + matrix expressions or can be assigned to a matrix. + @param method Matrix inversion method. One of cv::DecompTypes + */ + MatExpr inv(int method=DECOMP_LU) const; + + /** @brief Performs an element-wise multiplication or division of the two matrices. + + The method returns a temporary object encoding per-element array multiplication, with optional + scale. Note that this is not a matrix multiplication that corresponds to a simpler "\*" operator. + + Example: + @code + Mat C = A.mul(5/B); // equivalent to divide(A, B, C, 5) + @endcode + @param m Another array of the same type and the same size as \*this, or a matrix expression. + @param scale Optional scale factor. + */ + MatExpr mul(InputArray m, double scale=1) const; + + /** @brief Computes a cross-product of two 3-element vectors. + + The method computes a cross-product of two 3-element vectors. The vectors must be 3-element + floating-point vectors of the same shape and size. The result is another 3-element vector of the + same shape and type as operands. + @param m Another cross-product operand. + */ + Mat cross(InputArray m) const; + + /** @brief Computes a dot-product of two vectors. + + The method computes a dot-product of two matrices. If the matrices are not single-column or + single-row vectors, the top-to-bottom left-to-right scan ordering is used to treat them as 1D + vectors. The vectors must have the same size and type. If the matrices have more than one channel, + the dot products from all the channels are summed together. + @param m another dot-product operand. + */ + double dot(InputArray m) const; + + /** @brief Returns a zero array of the specified size and type. + + The method returns a Matlab-style zero array initializer. It can be used to quickly form a constant + array as a function parameter, part of a matrix expression, or as a matrix initializer: + @code + Mat A; + A = Mat::zeros(3, 3, CV_32F); + @endcode + In the example above, a new matrix is allocated only if A is not a 3x3 floating-point matrix. + Otherwise, the existing matrix A is filled with zeros. + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr zeros(int rows, int cols, int type); + + /** @overload + @param size Alternative to the matrix size specification Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr zeros(Size size, int type); + + /** @overload + @param ndims Array dimensionality. + @param sz Array of integers specifying the array shape. + @param type Created matrix type. + */ + static MatExpr zeros(int ndims, const int* sz, int type); + + /** @brief Returns an array of all 1's of the specified size and type. + + The method returns a Matlab-style 1's array initializer, similarly to Mat::zeros. Note that using + this method you can initialize an array with an arbitrary value, using the following Matlab idiom: + @code + Mat A = Mat::ones(100, 100, CV_8U)*3; // make 100x100 matrix filled with 3. + @endcode + The above operation does not form a 100x100 matrix of 1's and then multiply it by 3. Instead, it + just remembers the scale factor (3 in this case) and use it when actually invoking the matrix + initializer. + @note In case of multi-channels type, only the first channel will be initialized with 1's, the + others will be set to 0's. + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr ones(int rows, int cols, int type); + + /** @overload + @param size Alternative to the matrix size specification Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr ones(Size size, int type); + + /** @overload + @param ndims Array dimensionality. + @param sz Array of integers specifying the array shape. + @param type Created matrix type. + */ + static MatExpr ones(int ndims, const int* sz, int type); + + /** @brief Returns an identity matrix of the specified size and type. + + The method returns a Matlab-style identity matrix initializer, similarly to Mat::zeros. Similarly to + Mat::ones, you can use a scale operation to create a scaled identity matrix efficiently: + @code + // make a 4x4 diagonal matrix with 0.1's on the diagonal. + Mat A = Mat::eye(4, 4, CV_32F)*0.1; + @endcode + @note In case of multi-channels type, identity matrix will be initialized only for the first channel, + the others will be set to 0's + @param rows Number of rows. + @param cols Number of columns. + @param type Created matrix type. + */ + static MatExpr eye(int rows, int cols, int type); + + /** @overload + @param size Alternative matrix size specification as Size(cols, rows) . + @param type Created matrix type. + */ + static MatExpr eye(Size size, int type); + + /** @brief Allocates new array data if needed. + + This is one of the key Mat methods. Most new-style OpenCV functions and methods that produce arrays + call this method for each output array. The method uses the following algorithm: + + -# If the current array shape and the type match the new ones, return immediately. Otherwise, + de-reference the previous data by calling Mat::release. + -# Initialize the new header. + -# Allocate the new data of total()\*elemSize() bytes. + -# Allocate the new, associated with the data, reference counter and set it to 1. + + Such a scheme makes the memory management robust and efficient at the same time and helps avoid + extra typing for you. This means that usually there is no need to explicitly allocate output arrays. + That is, instead of writing: + @code + Mat color; + ... + Mat gray(color.rows, color.cols, color.depth()); + cvtColor(color, gray, COLOR_BGR2GRAY); + @endcode + you can simply write: + @code + Mat color; + ... + Mat gray; + cvtColor(color, gray, COLOR_BGR2GRAY); + @endcode + because cvtColor, as well as the most of OpenCV functions, calls Mat::create() for the output array + internally. + @param rows New number of rows. + @param cols New number of columns. + @param type New matrix type. + */ + void create(int rows, int cols, int type); + + /** @overload + @param size Alternative new matrix size specification: Size(cols, rows) + @param type New matrix type. + */ + void create(Size size, int type); + + /** @overload + @param ndims New array dimensionality. + @param sizes Array of integers specifying a new array shape. + @param type New matrix type. + */ + void create(int ndims, const int* sizes, int type); + + /** @overload + @param sizes Array of integers specifying a new array shape. + @param type New matrix type. + */ + void create(const std::vector& sizes, int type); + + /** @brief Increments the reference counter. + + The method increments the reference counter associated with the matrix data. If the matrix header + points to an external data set (see Mat::Mat ), the reference counter is NULL, and the method has no + effect in this case. Normally, to avoid memory leaks, the method should not be called explicitly. It + is called implicitly by the matrix assignment operator. The reference counter increment is an atomic + operation on the platforms that support it. Thus, it is safe to operate on the same matrices + asynchronously in different threads. + */ + void addref(); + + /** @brief Decrements the reference counter and deallocates the matrix if needed. + + The method decrements the reference counter associated with the matrix data. When the reference + counter reaches 0, the matrix data is deallocated and the data and the reference counter pointers + are set to NULL's. If the matrix header points to an external data set (see Mat::Mat ), the + reference counter is NULL, and the method has no effect in this case. + + This method can be called manually to force the matrix data deallocation. But since this method is + automatically called in the destructor, or by any other method that changes the data pointer, it is + usually not needed. The reference counter decrement and check for 0 is an atomic operation on the + platforms that support it. Thus, it is safe to operate on the same matrices asynchronously in + different threads. + */ + void release(); + + //! internal use function, consider to use 'release' method instead; deallocates the matrix data + void deallocate(); + //! internal use function; properly re-allocates _size, _step arrays + void copySize(const Mat& m); + + /** @brief Reserves space for the certain number of rows. + + The method reserves space for sz rows. If the matrix already has enough space to store sz rows, + nothing happens. If the matrix is reallocated, the first Mat::rows rows are preserved. The method + emulates the corresponding method of the STL vector class. + @param sz Number of rows. + */ + void reserve(size_t sz); + + /** @brief Reserves space for the certain number of bytes. + + The method reserves space for sz bytes. If the matrix already has enough space to store sz bytes, + nothing happens. If matrix has to be reallocated its previous content could be lost. + @param sz Number of bytes. + */ + void reserveBuffer(size_t sz); + + /** @brief Changes the number of matrix rows. + + The methods change the number of matrix rows. If the matrix is reallocated, the first + min(Mat::rows, sz) rows are preserved. The methods emulate the corresponding methods of the STL + vector class. + @param sz New number of rows. + */ + void resize(size_t sz); + + /** @overload + @param sz New number of rows. + @param s Value assigned to the newly added elements. + */ + void resize(size_t sz, const Scalar& s); + + //! internal function + void push_back_(const void* elem); + + /** @brief Adds elements to the bottom of the matrix. + + The methods add one or more elements to the bottom of the matrix. They emulate the corresponding + method of the STL vector class. When elem is Mat , its type and the number of columns must be the + same as in the container matrix. + @param elem Added element(s). + */ + template void push_back(const _Tp& elem); + + /** @overload + @param elem Added element(s). + */ + template void push_back(const Mat_<_Tp>& elem); + + /** @overload + @param elem Added element(s). + */ + template void push_back(const std::vector<_Tp>& elem); + + /** @overload + @param m Added line(s). + */ + void push_back(const Mat& m); + + /** @brief Removes elements from the bottom of the matrix. + + The method removes one or more rows from the bottom of the matrix. + @param nelems Number of removed rows. If it is greater than the total number of rows, an exception + is thrown. + */ + void pop_back(size_t nelems=1); + + /** @brief Locates the matrix header within a parent matrix. + + After you extracted a submatrix from a matrix using Mat::row, Mat::col, Mat::rowRange, + Mat::colRange, and others, the resultant submatrix points just to the part of the original big + matrix. However, each submatrix contains information (represented by datastart and dataend + fields) that helps reconstruct the original matrix size and the position of the extracted + submatrix within the original matrix. The method locateROI does exactly that. + @param wholeSize Output parameter that contains the size of the whole matrix containing *this* + as a part. + @param ofs Output parameter that contains an offset of *this* inside the whole matrix. + */ + void locateROI( Size& wholeSize, Point& ofs ) const; + + /** @brief Adjusts a submatrix size and position within the parent matrix. + + The method is complimentary to Mat::locateROI . The typical use of these functions is to determine + the submatrix position within the parent matrix and then shift the position somehow. Typically, it + can be required for filtering operations when pixels outside of the ROI should be taken into + account. When all the method parameters are positive, the ROI needs to grow in all directions by the + specified amount, for example: + @code + A.adjustROI(2, 2, 2, 2); + @endcode + In this example, the matrix size is increased by 4 elements in each direction. The matrix is shifted + by 2 elements to the left and 2 elements up, which brings in all the necessary pixels for the + filtering with the 5x5 kernel. + + adjustROI forces the adjusted ROI to be inside of the parent matrix that is boundaries of the + adjusted ROI are constrained by boundaries of the parent matrix. For example, if the submatrix A is + located in the first row of a parent matrix and you called A.adjustROI(2, 2, 2, 2) then A will not + be increased in the upward direction. + + The function is used internally by the OpenCV filtering functions, like filter2D , morphological + operations, and so on. + @param dtop Shift of the top submatrix boundary upwards. + @param dbottom Shift of the bottom submatrix boundary downwards. + @param dleft Shift of the left submatrix boundary to the left. + @param dright Shift of the right submatrix boundary to the right. + @sa copyMakeBorder + */ + Mat& adjustROI( int dtop, int dbottom, int dleft, int dright ); + + /** @brief Extracts a rectangular submatrix. + + The operators make a new header for the specified sub-array of \*this . They are the most + generalized forms of Mat::row, Mat::col, Mat::rowRange, and Mat::colRange . For example, + `A(Range(0, 10), Range::all())` is equivalent to `A.rowRange(0, 10)`. Similarly to all of the above, + the operators are O(1) operations, that is, no matrix data is copied. + @param rowRange Start and end row of the extracted submatrix. The upper boundary is not included. To + select all the rows, use Range::all(). + @param colRange Start and end column of the extracted submatrix. The upper boundary is not included. + To select all the columns, use Range::all(). + */ + Mat operator()( Range rowRange, Range colRange ) const; + + /** @overload + @param roi Extracted submatrix specified as a rectangle. + */ + Mat operator()( const Rect& roi ) const; + + /** @overload + @param ranges Array of selected ranges along each array dimension. + */ + Mat operator()( const Range* ranges ) const; + + /** @overload + @param ranges Array of selected ranges along each array dimension. + */ + Mat operator()(const std::vector& ranges) const; + + // //! converts header to CvMat; no data is copied + // operator CvMat() const; + // //! converts header to CvMatND; no data is copied + // operator CvMatND() const; + // //! converts header to IplImage; no data is copied + // operator IplImage() const; + + template operator std::vector<_Tp>() const; + template operator Vec<_Tp, n>() const; + template operator Matx<_Tp, m, n>() const; + +#ifdef CV_CXX_STD_ARRAY + template operator std::array<_Tp, _Nm>() const; +#endif + + /** @brief Reports whether the matrix is continuous or not. + + The method returns true if the matrix elements are stored continuously without gaps at the end of + each row. Otherwise, it returns false. Obviously, 1x1 or 1xN matrices are always continuous. + Matrices created with Mat::create are always continuous. But if you extract a part of the matrix + using Mat::col, Mat::diag, and so on, or constructed a matrix header for externally allocated data, + such matrices may no longer have this property. + + The continuity flag is stored as a bit in the Mat::flags field and is computed automatically when + you construct a matrix header. Thus, the continuity check is a very fast operation, though + theoretically it could be done as follows: + @code + // alternative implementation of Mat::isContinuous() + bool myCheckMatContinuity(const Mat& m) + { + //return (m.flags & Mat::CONTINUOUS_FLAG) != 0; + return m.rows == 1 || m.step == m.cols*m.elemSize(); + } + @endcode + The method is used in quite a few of OpenCV functions. The point is that element-wise operations + (such as arithmetic and logical operations, math functions, alpha blending, color space + transformations, and others) do not depend on the image geometry. Thus, if all the input and output + arrays are continuous, the functions can process them as very long single-row vectors. The example + below illustrates how an alpha-blending function can be implemented: + @code + template + void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) + { + const float alpha_scale = (float)std::numeric_limits::max(), + inv_scale = 1.f/alpha_scale; + + CV_Assert( src1.type() == src2.type() && + src1.type() == CV_MAKETYPE(traits::Depth::value, 4) && + src1.size() == src2.size()); + Size size = src1.size(); + dst.create(size, src1.type()); + + // here is the idiom: check the arrays for continuity and, + // if this is the case, + // treat the arrays as 1D vectors + if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() ) + { + size.width *= size.height; + size.height = 1; + } + size.width *= 4; + + for( int i = 0; i < size.height; i++ ) + { + // when the arrays are continuous, + // the outer loop is executed only once + const T* ptr1 = src1.ptr(i); + const T* ptr2 = src2.ptr(i); + T* dptr = dst.ptr(i); + + for( int j = 0; j < size.width; j += 4 ) + { + float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale; + dptr[j] = saturate_cast(ptr1[j]*alpha + ptr2[j]*beta); + dptr[j+1] = saturate_cast(ptr1[j+1]*alpha + ptr2[j+1]*beta); + dptr[j+2] = saturate_cast(ptr1[j+2]*alpha + ptr2[j+2]*beta); + dptr[j+3] = saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale); + } + } + } + @endcode + This approach, while being very simple, can boost the performance of a simple element-operation by + 10-20 percents, especially if the image is rather small and the operation is quite simple. + + Another OpenCV idiom in this function, a call of Mat::create for the destination array, that + allocates the destination array unless it already has the proper size and type. And while the newly + allocated arrays are always continuous, you still need to check the destination array because + Mat::create does not always allocate a new matrix. + */ + bool isContinuous() const; + + //! returns true if the matrix is a submatrix of another matrix + bool isSubmatrix() const; + + /** @brief Returns the matrix element size in bytes. + + The method returns the matrix element size in bytes. For example, if the matrix type is CV_16SC3 , + the method returns 3\*sizeof(short) or 6. + */ + size_t elemSize() const; + + /** @brief Returns the size of each matrix element channel in bytes. + + The method returns the matrix element channel size in bytes, that is, it ignores the number of + channels. For example, if the matrix type is CV_16SC3 , the method returns sizeof(short) or 2. + */ + size_t elemSize1() const; + + /** @brief Returns the type of a matrix element. + + The method returns a matrix element type. This is an identifier compatible with the CvMat type + system, like CV_16SC3 or 16-bit signed 3-channel array, and so on. + */ + int type() const; + + /** @brief Returns the depth of a matrix element. + + The method returns the identifier of the matrix element depth (the type of each individual channel). + For example, for a 16-bit signed element array, the method returns CV_16S . A complete list of + matrix types contains the following values: + - CV_8U - 8-bit unsigned integers ( 0..255 ) + - CV_8S - 8-bit signed integers ( -128..127 ) + - CV_16U - 16-bit unsigned integers ( 0..65535 ) + - CV_16S - 16-bit signed integers ( -32768..32767 ) + - CV_32S - 32-bit signed integers ( -2147483648..2147483647 ) + - CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN ) + - CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN ) + */ + int depth() const; + + /** @brief Returns the number of matrix channels. + + The method returns the number of matrix channels. + */ + int channels() const; + + /** @brief Returns a normalized step. + + The method returns a matrix step divided by Mat::elemSize1() . It can be useful to quickly access an + arbitrary matrix element. + */ + size_t step1(int i=0) const; + + /** @brief Returns true if the array has no elements. + + The method returns true if Mat::total() is 0 or if Mat::data is NULL. Because of pop_back() and + resize() methods `M.total() == 0` does not imply that `M.data == NULL`. + */ + bool empty() const; + + /** @brief Returns the total number of array elements. + + The method returns the number of array elements (a number of pixels if the array represents an + image). + */ + size_t total() const; + + /** @brief Returns the total number of array elements. + + The method returns the number of elements within a certain sub-array slice with startDim <= dim < endDim + */ + size_t total(int startDim, int endDim=INT_MAX) const; + + /** + * @param elemChannels Number of channels or number of columns the matrix should have. + * For a 2-D matrix, when the matrix has only 1 column, then it should have + * elemChannels channels; When the matrix has only 1 channel, + * then it should have elemChannels columns. + * For a 3-D matrix, it should have only one channel. Furthermore, + * if the number of planes is not one, then the number of rows + * within every plane has to be 1; if the number of rows within + * every plane is not 1, then the number of planes has to be 1. + * @param depth The depth the matrix should have. Set it to -1 when any depth is fine. + * @param requireContinuous Set it to true to require the matrix to be continuous + * @return -1 if the requirement is not satisfied. + * Otherwise, it returns the number of elements in the matrix. Note + * that an element may have multiple channels. + * + * The following code demonstrates its usage for a 2-d matrix: + * @snippet snippets/core_mat_checkVector.cpp example-2d + * + * The following code demonstrates its usage for a 3-d matrix: + * @snippet snippets/core_mat_checkVector.cpp example-3d + */ + int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const; + + /** @brief Returns a pointer to the specified matrix row. + + The methods return `uchar*` or typed pointer to the specified matrix row. See the sample in + Mat::isContinuous to know how to use these methods. + @param i0 A 0-based row index. + */ + uchar* ptr(int i0=0); + /** @overload */ + const uchar* ptr(int i0=0) const; + + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + uchar* ptr(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + const uchar* ptr(int row, int col) const; + + /** @overload */ + uchar* ptr(int i0, int i1, int i2); + /** @overload */ + const uchar* ptr(int i0, int i1, int i2) const; + + /** @overload */ + uchar* ptr(const int* idx); + /** @overload */ + const uchar* ptr(const int* idx) const; + /** @overload */ + template uchar* ptr(const Vec& idx); + /** @overload */ + template const uchar* ptr(const Vec& idx) const; + + /** @overload */ + template _Tp* ptr(int i0=0); + /** @overload */ + template const _Tp* ptr(int i0=0) const; + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template _Tp* ptr(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template const _Tp* ptr(int row, int col) const; + /** @overload */ + template _Tp* ptr(int i0, int i1, int i2); + /** @overload */ + template const _Tp* ptr(int i0, int i1, int i2) const; + /** @overload */ + template _Tp* ptr(const int* idx); + /** @overload */ + template const _Tp* ptr(const int* idx) const; + /** @overload */ + template _Tp* ptr(const Vec& idx); + /** @overload */ + template const _Tp* ptr(const Vec& idx) const; + + /** @brief Returns a reference to the specified array element. + + The template methods return a reference to the specified array element. For the sake of higher + performance, the index range checks are only performed in the Debug configuration. + + Note that the variants with a single index (i) can be used to access elements of single-row or + single-column 2-dimensional arrays. That is, if, for example, A is a 1 x N floating-point matrix and + B is an M x 1 integer matrix, you can simply write `A.at(k+4)` and `B.at(2*i+1)` + instead of `A.at(0,k+4)` and `B.at(2*i+1,0)`, respectively. + + The example below initializes a Hilbert matrix: + @code + Mat H(100, 100, CV_64F); + for(int i = 0; i < H.rows; i++) + for(int j = 0; j < H.cols; j++) + H.at(i,j)=1./(i+j+1); + @endcode + + Keep in mind that the size identifier used in the at operator cannot be chosen at random. It depends + on the image from which you are trying to retrieve the data. The table below gives a better insight in this: + - If matrix is of type `CV_8U` then use `Mat.at(y,x)`. + - If matrix is of type `CV_8S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_16U` then use `Mat.at(y,x)`. + - If matrix is of type `CV_16S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_32S` then use `Mat.at(y,x)`. + - If matrix is of type `CV_32F` then use `Mat.at(y,x)`. + - If matrix is of type `CV_64F` then use `Mat.at(y,x)`. + + @param i0 Index along the dimension 0 + */ + template _Tp& at(int i0=0); + /** @overload + @param i0 Index along the dimension 0 + */ + template const _Tp& at(int i0=0) const; + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template _Tp& at(int row, int col); + /** @overload + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + template const _Tp& at(int row, int col) const; + + /** @overload + @param i0 Index along the dimension 0 + @param i1 Index along the dimension 1 + @param i2 Index along the dimension 2 + */ + template _Tp& at(int i0, int i1, int i2); + /** @overload + @param i0 Index along the dimension 0 + @param i1 Index along the dimension 1 + @param i2 Index along the dimension 2 + */ + template const _Tp& at(int i0, int i1, int i2) const; + + /** @overload + @param idx Array of Mat::dims indices. + */ + template _Tp& at(const int* idx); + /** @overload + @param idx Array of Mat::dims indices. + */ + template const _Tp& at(const int* idx) const; + + /** @overload */ + template _Tp& at(const Vec& idx); + /** @overload */ + template const _Tp& at(const Vec& idx) const; + + /** @overload + special versions for 2D arrays (especially convenient for referencing image pixels) + @param pt Element position specified as Point(j,i) . + */ + template _Tp& at(Point pt); + /** @overload + special versions for 2D arrays (especially convenient for referencing image pixels) + @param pt Element position specified as Point(j,i) . + */ + template const _Tp& at(Point pt) const; + + /** @brief Returns the matrix iterator and sets it to the first matrix element. + + The methods return the matrix read-only or read-write iterators. The use of matrix iterators is very + similar to the use of bi-directional STL iterators. In the example below, the alpha blending + function is rewritten using the matrix iterators: + @code + template + void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) + { + typedef Vec VT; + + const float alpha_scale = (float)std::numeric_limits::max(), + inv_scale = 1.f/alpha_scale; + + CV_Assert( src1.type() == src2.type() && + src1.type() == traits::Type::value && + src1.size() == src2.size()); + Size size = src1.size(); + dst.create(size, src1.type()); + + MatConstIterator_ it1 = src1.begin(), it1_end = src1.end(); + MatConstIterator_ it2 = src2.begin(); + MatIterator_ dst_it = dst.begin(); + + for( ; it1 != it1_end; ++it1, ++it2, ++dst_it ) + { + VT pix1 = *it1, pix2 = *it2; + float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale; + *dst_it = VT(saturate_cast(pix1[0]*alpha + pix2[0]*beta), + saturate_cast(pix1[1]*alpha + pix2[1]*beta), + saturate_cast(pix1[2]*alpha + pix2[2]*beta), + saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale)); + } + } + @endcode + */ + template MatIterator_<_Tp> begin(); + template MatConstIterator_<_Tp> begin() const; + + /** @brief Returns the matrix iterator and sets it to the after-last matrix element. + + The methods return the matrix read-only or read-write iterators, set to the point following the last + matrix element. + */ + template MatIterator_<_Tp> end(); + template MatConstIterator_<_Tp> end() const; + + /** @brief Runs the given functor over all matrix elements in parallel. + + The operation passed as argument has to be a function pointer, a function object or a lambda(C++11). + + Example 1. All of the operations below put 0xFF the first channel of all matrix elements: + @code + Mat image(1920, 1080, CV_8UC3); + typedef cv::Point3_ Pixel; + + // first. raw pointer access. + for (int r = 0; r < image.rows; ++r) { + Pixel* ptr = image.ptr(r, 0); + const Pixel* ptr_end = ptr + image.cols; + for (; ptr != ptr_end; ++ptr) { + ptr->x = 255; + } + } + + // Using MatIterator. (Simple but there are a Iterator's overhead) + for (Pixel &p : cv::Mat_(image)) { + p.x = 255; + } + + // Parallel execution with function object. + struct Operator { + void operator ()(Pixel &pixel, const int * position) { + pixel.x = 255; + } + }; + image.forEach(Operator()); + + // Parallel execution using C++11 lambda. + image.forEach([](Pixel &p, const int * position) -> void { + p.x = 255; + }); + @endcode + Example 2. Using the pixel's position: + @code + // Creating 3D matrix (255 x 255 x 255) typed uint8_t + // and initialize all elements by the value which equals elements position. + // i.e. pixels (x,y,z) = (1,2,3) is (b,g,r) = (1,2,3). + + int sizes[] = { 255, 255, 255 }; + typedef cv::Point3_ Pixel; + + Mat_ image = Mat::zeros(3, sizes, CV_8UC3); + + image.forEach([&](Pixel& pixel, const int position[]) -> void { + pixel.x = position[0]; + pixel.y = position[1]; + pixel.z = position[2]; + }); + @endcode + */ + template void forEach(const Functor& operation); + /** @overload */ + template void forEach(const Functor& operation) const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Mat(Mat&& m); + Mat& operator = (Mat&& m); +#endif + + enum { MAGIC_VAL = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG }; + enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 }; + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + //! the matrix dimensionality, >= 2 + int dims; + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows, cols; + //! pointer to the data + uchar* data; + + //! helper fields used in locateROI and adjustROI + const uchar* datastart; + const uchar* dataend; + const uchar* datalimit; + + //! custom allocator + MatAllocator* allocator; + //! and the standard allocator + static MatAllocator* getStdAllocator(); + static MatAllocator* getDefaultAllocator(); + static void setDefaultAllocator(MatAllocator* allocator); + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + //! interaction with UMat + UMatData* u; + + MatSize size; + MatStep step; + +protected: + template void forEach_impl(const Functor& operation); +}; + + +///////////////////////////////// Mat_<_Tp> //////////////////////////////////// + +/** @brief Template matrix class derived from Mat + +@code{.cpp} + template class Mat_ : public Mat + { + public: + // ... some specific methods + // and + // no new extra fields + }; +@endcode +The class `Mat_<_Tp>` is a *thin* template wrapper on top of the Mat class. It does not have any +extra data fields. Nor this class nor Mat has any virtual methods. Thus, references or pointers to +these two classes can be freely but carefully converted one to another. For example: +@code{.cpp} + // create a 100x100 8-bit matrix + Mat M(100,100,CV_8U); + // this will be compiled fine. no any data conversion will be done. + Mat_& M1 = (Mat_&)M; + // the program is likely to crash at the statement below + M1(99,99) = 1.f; +@endcode +While Mat is sufficient in most cases, Mat_ can be more convenient if you use a lot of element +access operations and if you know matrix type at the compilation time. Note that +`Mat::at(int y,int x)` and `Mat_::operator()(int y,int x)` do absolutely the same +and run at the same speed, but the latter is certainly shorter: +@code{.cpp} + Mat_ M(20,20); + for(int i = 0; i < M.rows; i++) + for(int j = 0; j < M.cols; j++) + M(i,j) = 1./(i+j+1); + Mat E, V; + eigen(M,E,V); + cout << E.at(0,0)/E.at(M.rows-1,0); +@endcode +To use Mat_ for multi-channel images/matrices, pass Vec as a Mat_ parameter: +@code{.cpp} + // allocate a 320x240 color image and fill it with green (in RGB space) + Mat_ img(240, 320, Vec3b(0,255,0)); + // now draw a diagonal white line + for(int i = 0; i < 100; i++) + img(i,i)=Vec3b(255,255,255); + // and now scramble the 2nd (red) channel of each pixel + for(int i = 0; i < img.rows; i++) + for(int j = 0; j < img.cols; j++) + img(i,j)[2] ^= (uchar)(i ^ j); +@endcode +Mat_ is fully compatible with C++11 range-based for loop. For example such loop +can be used to safely apply look-up table: +@code{.cpp} +void applyTable(Mat_& I, const uchar* const table) +{ + for(auto& pixel : I) + { + pixel = table[pixel]; + } +} +@endcode + */ +template class Mat_ : public Mat +{ +public: + typedef _Tp value_type; + typedef typename DataType<_Tp>::channel_type channel_type; + typedef MatIterator_<_Tp> iterator; + typedef MatConstIterator_<_Tp> const_iterator; + + //! default constructor + Mat_() CV_NOEXCEPT; + //! equivalent to Mat(_rows, _cols, DataType<_Tp>::type) + Mat_(int _rows, int _cols); + //! constructor that sets each matrix element to specified value + Mat_(int _rows, int _cols, const _Tp& value); + //! equivalent to Mat(_size, DataType<_Tp>::type) + explicit Mat_(Size _size); + //! constructor that sets each matrix element to specified value + Mat_(Size _size, const _Tp& value); + //! n-dim array constructor + Mat_(int _ndims, const int* _sizes); + //! n-dim array constructor that sets each matrix element to specified value + Mat_(int _ndims, const int* _sizes, const _Tp& value); + //! copy/conversion constructor. If m is of different type, it's converted + Mat_(const Mat& m); + //! copy constructor + Mat_(const Mat_& m); + //! constructs a matrix on top of user-allocated data. step is in bytes(!!!), regardless of the type + Mat_(int _rows, int _cols, _Tp* _data, size_t _step=AUTO_STEP); + //! constructs n-dim matrix on top of user-allocated data. steps are in bytes(!!!), regardless of the type + Mat_(int _ndims, const int* _sizes, _Tp* _data, const size_t* _steps=0); + //! selects a submatrix + Mat_(const Mat_& m, const Range& rowRange, const Range& colRange=Range::all()); + //! selects a submatrix + Mat_(const Mat_& m, const Rect& roi); + //! selects a submatrix, n-dim version + Mat_(const Mat_& m, const Range* ranges); + //! selects a submatrix, n-dim version + Mat_(const Mat_& m, const std::vector& ranges); + //! from a matrix expression + explicit Mat_(const MatExpr& e); + //! makes a matrix out of Vec, std::vector, Point_ or Point3_. The matrix will have a single column + explicit Mat_(const std::vector<_Tp>& vec, bool copyData=false); + template explicit Mat_(const Vec::channel_type, n>& vec, bool copyData=true); + template explicit Mat_(const Matx::channel_type, m, n>& mtx, bool copyData=true); + explicit Mat_(const Point_::channel_type>& pt, bool copyData=true); + explicit Mat_(const Point3_::channel_type>& pt, bool copyData=true); + explicit Mat_(const MatCommaInitializer_<_Tp>& commaInitializer); + +#ifdef CV_CXX11 + Mat_(std::initializer_list<_Tp> values); + explicit Mat_(const std::initializer_list sizes, const std::initializer_list<_Tp> values); +#endif + +#ifdef CV_CXX_STD_ARRAY + template explicit Mat_(const std::array<_Tp, _Nm>& arr, bool copyData=false); +#endif + + Mat_& operator = (const Mat& m); + Mat_& operator = (const Mat_& m); + //! set all the elements to s. + Mat_& operator = (const _Tp& s); + //! assign a matrix expression + Mat_& operator = (const MatExpr& e); + + //! iterators; they are smart enough to skip gaps in the end of rows + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + //! template methods for for operation over all matrix elements. + // the operations take care of skipping gaps in the end of rows (if any) + template void forEach(const Functor& operation); + template void forEach(const Functor& operation) const; + + //! equivalent to Mat::create(_rows, _cols, DataType<_Tp>::type) + void create(int _rows, int _cols); + //! equivalent to Mat::create(_size, DataType<_Tp>::type) + void create(Size _size); + //! equivalent to Mat::create(_ndims, _sizes, DatType<_Tp>::type) + void create(int _ndims, const int* _sizes); + //! equivalent to Mat::release() + void release(); + //! cross-product + Mat_ cross(const Mat_& m) const; + //! data type conversion + template operator Mat_() const; + //! overridden forms of Mat::row() etc. + Mat_ row(int y) const; + Mat_ col(int x) const; + Mat_ diag(int d=0) const; + Mat_ clone() const CV_NODISCARD; + + //! overridden forms of Mat::elemSize() etc. + size_t elemSize() const; + size_t elemSize1() const; + int type() const; + int depth() const; + int channels() const; + size_t step1(int i=0) const; + //! returns step()/sizeof(_Tp) + size_t stepT(int i=0) const; + + //! overridden forms of Mat::zeros() etc. Data type is omitted, of course + static MatExpr zeros(int rows, int cols); + static MatExpr zeros(Size size); + static MatExpr zeros(int _ndims, const int* _sizes); + static MatExpr ones(int rows, int cols); + static MatExpr ones(Size size); + static MatExpr ones(int _ndims, const int* _sizes); + static MatExpr eye(int rows, int cols); + static MatExpr eye(Size size); + + //! some more overridden methods + Mat_& adjustROI( int dtop, int dbottom, int dleft, int dright ); + Mat_ operator()( const Range& rowRange, const Range& colRange ) const; + Mat_ operator()( const Rect& roi ) const; + Mat_ operator()( const Range* ranges ) const; + Mat_ operator()(const std::vector& ranges) const; + + //! more convenient forms of row and element access operators + _Tp* operator [](int y); + const _Tp* operator [](int y) const; + + //! returns reference to the specified element + _Tp& operator ()(const int* idx); + //! returns read-only reference to the specified element + const _Tp& operator ()(const int* idx) const; + + //! returns reference to the specified element + template _Tp& operator ()(const Vec& idx); + //! returns read-only reference to the specified element + template const _Tp& operator ()(const Vec& idx) const; + + //! returns reference to the specified element (1D case) + _Tp& operator ()(int idx0); + //! returns read-only reference to the specified element (1D case) + const _Tp& operator ()(int idx0) const; + //! returns reference to the specified element (2D case) + _Tp& operator ()(int row, int col); + //! returns read-only reference to the specified element (2D case) + const _Tp& operator ()(int row, int col) const; + //! returns reference to the specified element (3D case) + _Tp& operator ()(int idx0, int idx1, int idx2); + //! returns read-only reference to the specified element (3D case) + const _Tp& operator ()(int idx0, int idx1, int idx2) const; + + _Tp& operator ()(Point pt); + const _Tp& operator ()(Point pt) const; + + //! conversion to vector. + operator std::vector<_Tp>() const; + +#ifdef CV_CXX_STD_ARRAY + //! conversion to array. + template operator std::array<_Tp, _Nm>() const; +#endif + + //! conversion to Vec + template operator Vec::channel_type, n>() const; + //! conversion to Matx + template operator Matx::channel_type, m, n>() const; + +#ifdef CV_CXX_MOVE_SEMANTICS + Mat_(Mat_&& m); + Mat_& operator = (Mat_&& m); + + Mat_(Mat&& m); + Mat_& operator = (Mat&& m); + + Mat_(MatExpr&& e); +#endif +}; + +typedef Mat_ Mat1b; +typedef Mat_ Mat2b; +typedef Mat_ Mat3b; +typedef Mat_ Mat4b; + +typedef Mat_ Mat1s; +typedef Mat_ Mat2s; +typedef Mat_ Mat3s; +typedef Mat_ Mat4s; + +typedef Mat_ Mat1w; +typedef Mat_ Mat2w; +typedef Mat_ Mat3w; +typedef Mat_ Mat4w; + +typedef Mat_ Mat1i; +typedef Mat_ Mat2i; +typedef Mat_ Mat3i; +typedef Mat_ Mat4i; + +typedef Mat_ Mat1f; +typedef Mat_ Mat2f; +typedef Mat_ Mat3f; +typedef Mat_ Mat4f; + +typedef Mat_ Mat1d; +typedef Mat_ Mat2d; +typedef Mat_ Mat3d; +typedef Mat_ Mat4d; + +/** @todo document */ +class CV_EXPORTS UMat +{ +public: + //! default constructor + UMat(UMatUsageFlags usageFlags = USAGE_DEFAULT) CV_NOEXCEPT; + //! constructs 2D matrix of the specified size and type + // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) + UMat(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(Size size, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + //! constructs 2D matrix and fills it with the specified value _s. + UMat(int rows, int cols, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(Size size, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! constructs n-dimensional matrix + UMat(int ndims, const int* sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + UMat(int ndims, const int* sizes, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! copy constructor + UMat(const UMat& m); + + //! creates a matrix header for a part of the bigger matrix + UMat(const UMat& m, const Range& rowRange, const Range& colRange=Range::all()); + UMat(const UMat& m, const Rect& roi); + UMat(const UMat& m, const Range* ranges); + UMat(const UMat& m, const std::vector& ranges); + + // FIXIT copyData=false is not implemented, drop this in favor of cv::Mat (OpenCV 5.0) + //! builds matrix from std::vector with or without copying the data + template explicit UMat(const std::vector<_Tp>& vec, bool copyData=false); + + //! destructor - calls release() + ~UMat(); + //! assignment operators + UMat& operator = (const UMat& m); + + Mat getMat(int flags) const; + + //! returns a new matrix header for the specified row + UMat row(int y) const; + //! returns a new matrix header for the specified column + UMat col(int x) const; + //! ... for the specified row span + UMat rowRange(int startrow, int endrow) const; + UMat rowRange(const Range& r) const; + //! ... for the specified column span + UMat colRange(int startcol, int endcol) const; + UMat colRange(const Range& r) const; + //! ... for the specified diagonal + //! (d=0 - the main diagonal, + //! >0 - a diagonal from the upper half, + //! <0 - a diagonal from the lower half) + UMat diag(int d=0) const; + //! constructs a square diagonal matrix which main diagonal is vector "d" + static UMat diag(const UMat& d); + + //! returns deep copy of the matrix, i.e. the data is copied + UMat clone() const CV_NODISCARD; + //! copies the matrix content to "m". + // It calls m.create(this->size(), this->type()). + void copyTo( OutputArray m ) const; + //! copies those matrix elements to "m" that are marked with non-zero mask elements. + void copyTo( OutputArray m, InputArray mask ) const; + //! converts matrix to another datatype with optional scaling. See cvConvertScale. + void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; + + void assignTo( UMat& m, int type=-1 ) const; + + //! sets every matrix element to s + UMat& operator = (const Scalar& s); + //! sets some of the matrix elements to s, according to the mask + UMat& setTo(InputArray value, InputArray mask=noArray()); + //! creates alternative matrix header for the same data, with different + // number of channels and/or different number of rows. see cvReshape. + UMat reshape(int cn, int rows=0) const; + UMat reshape(int cn, int newndims, const int* newsz) const; + + //! matrix transposition by means of matrix expressions + UMat t() const; + //! matrix inversion by means of matrix expressions + UMat inv(int method=DECOMP_LU) const; + //! per-element matrix multiplication by means of matrix expressions + UMat mul(InputArray m, double scale=1) const; + + //! computes dot-product + double dot(InputArray m) const; + + //! Matlab-style matrix initialization + static UMat zeros(int rows, int cols, int type); + static UMat zeros(Size size, int type); + static UMat zeros(int ndims, const int* sz, int type); + static UMat ones(int rows, int cols, int type); + static UMat ones(Size size, int type); + static UMat ones(int ndims, const int* sz, int type); + static UMat eye(int rows, int cols, int type); + static UMat eye(Size size, int type); + + //! allocates new matrix data unless the matrix already has specified size and type. + // previous data is unreferenced if needed. + void create(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(Size size, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(int ndims, const int* sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + void create(const std::vector& sizes, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! increases the reference counter; use with care to avoid memleaks + void addref(); + //! decreases reference counter; + // deallocates the data when reference counter reaches 0. + void release(); + + //! deallocates the matrix data + void deallocate(); + //! internal use function; properly re-allocates _size, _step arrays + void copySize(const UMat& m); + + //! locates matrix header within a parent matrix. See below + void locateROI( Size& wholeSize, Point& ofs ) const; + //! moves/resizes the current matrix ROI inside the parent matrix. + UMat& adjustROI( int dtop, int dbottom, int dleft, int dright ); + //! extracts a rectangular sub-matrix + // (this is a generalized form of row, rowRange etc.) + UMat operator()( Range rowRange, Range colRange ) const; + UMat operator()( const Rect& roi ) const; + UMat operator()( const Range* ranges ) const; + UMat operator()(const std::vector& ranges) const; + + //! returns true iff the matrix data is continuous + // (i.e. when there are no gaps between successive rows). + // similar to CV_IS_MAT_CONT(cvmat->type) + bool isContinuous() const; + + //! returns true if the matrix is a submatrix of another matrix + bool isSubmatrix() const; + + //! returns element size in bytes, + // similar to CV_ELEM_SIZE(cvmat->type) + size_t elemSize() const; + //! returns the size of element channel in bytes. + size_t elemSize1() const; + //! returns element type, similar to CV_MAT_TYPE(cvmat->type) + int type() const; + //! returns element type, similar to CV_MAT_DEPTH(cvmat->type) + int depth() const; + //! returns element type, similar to CV_MAT_CN(cvmat->type) + int channels() const; + //! returns step/elemSize1() + size_t step1(int i=0) const; + //! returns true if matrix data is NULL + bool empty() const; + //! returns the total number of matrix elements + size_t total() const; + + //! returns N if the matrix is 1-channel (N x ptdim) or ptdim-channel (1 x N) or (N x 1); negative number otherwise + int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const; + +#ifdef CV_CXX_MOVE_SEMANTICS + UMat(UMat&& m); + UMat& operator = (UMat&& m); +#endif + + /*! Returns the OpenCL buffer handle on which UMat operates on. + The UMat instance should be kept alive during the use of the handle to prevent the buffer to be + returned to the OpenCV buffer pool. + */ + void* handle(int accessFlags) const; + void ndoffset(size_t* ofs) const; + + enum { MAGIC_VAL = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG }; + enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 }; + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + //! the matrix dimensionality, >= 2 + int dims; + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows, cols; + + //! custom allocator + MatAllocator* allocator; + UMatUsageFlags usageFlags; // usage flags for allocator + //! and the standard allocator + static MatAllocator* getStdAllocator(); + + //! internal use method: updates the continuity flag + void updateContinuityFlag(); + + // black-box container of UMat data + UMatData* u; + + // offset of the submatrix (or 0) + size_t offset; + + MatSize size; + MatStep step; + +protected: +}; + + +/////////////////////////// multi-dimensional sparse matrix ////////////////////////// + +/** @brief The class SparseMat represents multi-dimensional sparse numerical arrays. + +Such a sparse array can store elements of any type that Mat can store. *Sparse* means that only +non-zero elements are stored (though, as a result of operations on a sparse matrix, some of its +stored elements can actually become 0. It is up to you to detect such elements and delete them +using SparseMat::erase ). The non-zero elements are stored in a hash table that grows when it is +filled so that the search time is O(1) in average (regardless of whether element is there or not). +Elements can be accessed using the following methods: +- Query operations (SparseMat::ptr and the higher-level SparseMat::ref, SparseMat::value and + SparseMat::find), for example: + @code + const int dims = 5; + int size[5] = {10, 10, 10, 10, 10}; + SparseMat sparse_mat(dims, size, CV_32F); + for(int i = 0; i < 1000; i++) + { + int idx[dims]; + for(int k = 0; k < dims; k++) + idx[k] = rand() % size[k]; + sparse_mat.ref(idx) += 1.f; + } + cout << "nnz = " << sparse_mat.nzcount() << endl; + @endcode +- Sparse matrix iterators. They are similar to MatIterator but different from NAryMatIterator. + That is, the iteration loop is familiar to STL users: + @code + // prints elements of a sparse floating-point matrix + // and the sum of elements. + SparseMatConstIterator_ + it = sparse_mat.begin(), + it_end = sparse_mat.end(); + double s = 0; + int dims = sparse_mat.dims(); + for(; it != it_end; ++it) + { + // print element indices and the element value + const SparseMat::Node* n = it.node(); + printf("("); + for(int i = 0; i < dims; i++) + printf("%d%s", n->idx[i], i < dims-1 ? ", " : ")"); + printf(": %g\n", it.value()); + s += *it; + } + printf("Element sum is %g\n", s); + @endcode + If you run this loop, you will notice that elements are not enumerated in a logical order + (lexicographical, and so on). They come in the same order as they are stored in the hash table + (semi-randomly). You may collect pointers to the nodes and sort them to get the proper ordering. + Note, however, that pointers to the nodes may become invalid when you add more elements to the + matrix. This may happen due to possible buffer reallocation. +- Combination of the above 2 methods when you need to process 2 or more sparse matrices + simultaneously. For example, this is how you can compute unnormalized cross-correlation of the 2 + floating-point sparse matrices: + @code + double cross_corr(const SparseMat& a, const SparseMat& b) + { + const SparseMat *_a = &a, *_b = &b; + // if b contains less elements than a, + // it is faster to iterate through b + if(_a->nzcount() > _b->nzcount()) + std::swap(_a, _b); + SparseMatConstIterator_ it = _a->begin(), + it_end = _a->end(); + double ccorr = 0; + for(; it != it_end; ++it) + { + // take the next element from the first matrix + float avalue = *it; + const Node* anode = it.node(); + // and try to find an element with the same index in the second matrix. + // since the hash value depends only on the element index, + // reuse the hash value stored in the node + float bvalue = _b->value(anode->idx,&anode->hashval); + ccorr += avalue*bvalue; + } + return ccorr; + } + @endcode + */ +class CV_EXPORTS SparseMat +{ +public: + typedef SparseMatIterator iterator; + typedef SparseMatConstIterator const_iterator; + + enum { MAGIC_VAL=0x42FD0000, MAX_DIM=32, HASH_SCALE=0x5bd1e995, HASH_BIT=0x80000000 }; + + //! the sparse matrix header + struct CV_EXPORTS Hdr + { + Hdr(int _dims, const int* _sizes, int _type); + void clear(); + int refcount; + int dims; + int valueOffset; + size_t nodeSize; + size_t nodeCount; + size_t freeList; + std::vector pool; + std::vector hashtab; + int size[MAX_DIM]; + }; + + //! sparse matrix node - element of a hash table + struct CV_EXPORTS Node + { + //! hash value + size_t hashval; + //! index of the next node in the same hash table entry + size_t next; + //! index of the matrix element + int idx[MAX_DIM]; + }; + + /** @brief Various SparseMat constructors. + */ + SparseMat(); + + /** @overload + @param dims Array dimensionality. + @param _sizes Sparce matrix size on all dementions. + @param _type Sparse matrix data type. + */ + SparseMat(int dims, const int* _sizes, int _type); + + /** @overload + @param m Source matrix for copy constructor. If m is dense matrix (ocvMat) then it will be converted + to sparse representation. + */ + SparseMat(const SparseMat& m); + + /** @overload + @param m Source matrix for copy constructor. If m is dense matrix (ocvMat) then it will be converted + to sparse representation. + */ + explicit SparseMat(const Mat& m); + + //! the destructor + ~SparseMat(); + + //! assignment operator. This is O(1) operation, i.e. no data is copied + SparseMat& operator = (const SparseMat& m); + //! equivalent to the corresponding constructor + SparseMat& operator = (const Mat& m); + + //! creates full copy of the matrix + SparseMat clone() const CV_NODISCARD; + + //! copies all the data to the destination matrix. All the previous content of m is erased + void copyTo( SparseMat& m ) const; + //! converts sparse matrix to dense matrix. + void copyTo( Mat& m ) const; + //! multiplies all the matrix elements by the specified scale factor alpha and converts the results to the specified data type + void convertTo( SparseMat& m, int rtype, double alpha=1 ) const; + //! converts sparse matrix to dense n-dim matrix with optional type conversion and scaling. + /*! + @param [out] m - output matrix; if it does not have a proper size or type before the operation, + it is reallocated + @param [in] rtype - desired output matrix type or, rather, the depth since the number of channels + are the same as the input has; if rtype is negative, the output matrix will have the + same type as the input. + @param [in] alpha - optional scale factor + @param [in] beta - optional delta added to the scaled values + */ + void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 ) const; + + // not used now + void assignTo( SparseMat& m, int type=-1 ) const; + + //! reallocates sparse matrix. + /*! + If the matrix already had the proper size and type, + it is simply cleared with clear(), otherwise, + the old matrix is released (using release()) and the new one is allocated. + */ + void create(int dims, const int* _sizes, int _type); + //! sets all the sparse matrix elements to 0, which means clearing the hash table. + void clear(); + //! manually increments the reference counter to the header. + void addref(); + // decrements the header reference counter. When the counter reaches 0, the header and all the underlying data are deallocated. + void release(); + + //! converts sparse matrix to the old-style representation; all the elements are copied. + //operator CvSparseMat*() const; + //! returns the size of each element in bytes (not including the overhead - the space occupied by SparseMat::Node elements) + size_t elemSize() const; + //! returns elemSize()/channels() + size_t elemSize1() const; + + //! returns type of sparse matrix elements + int type() const; + //! returns the depth of sparse matrix elements + int depth() const; + //! returns the number of channels + int channels() const; + + //! returns the array of sizes, or NULL if the matrix is not allocated + const int* size() const; + //! returns the size of i-th matrix dimension (or 0) + int size(int i) const; + //! returns the matrix dimensionality + int dims() const; + //! returns the number of non-zero elements (=the number of hash table nodes) + size_t nzcount() const; + + //! computes the element hash value (1D case) + size_t hash(int i0) const; + //! computes the element hash value (2D case) + size_t hash(int i0, int i1) const; + //! computes the element hash value (3D case) + size_t hash(int i0, int i1, int i2) const; + //! computes the element hash value (nD case) + size_t hash(const int* idx) const; + + //!@{ + /*! + specialized variants for 1D, 2D, 3D cases and the generic_type one for n-D case. + return pointer to the matrix element. + - if the element is there (it's non-zero), the pointer to it is returned + - if it's not there and createMissing=false, NULL pointer is returned + - if it's not there and createMissing=true, then the new element + is created and initialized with 0. Pointer to it is returned + - if the optional hashval pointer is not NULL, the element hash value is + not computed, but *hashval is taken instead. + */ + //! returns pointer to the specified element (1D case) + uchar* ptr(int i0, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (2D case) + uchar* ptr(int i0, int i1, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (3D case) + uchar* ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0); + //! returns pointer to the specified element (nD case) + uchar* ptr(const int* idx, bool createMissing, size_t* hashval=0); + //!@} + + //!@{ + /*! + return read-write reference to the specified sparse matrix element. + + `ref<_Tp>(i0,...[,hashval])` is equivalent to `*(_Tp*)ptr(i0,...,true[,hashval])`. + The methods always return a valid reference. + If the element did not exist, it is created and initialized with 0. + */ + //! returns reference to the specified element (1D case) + template _Tp& ref(int i0, size_t* hashval=0); + //! returns reference to the specified element (2D case) + template _Tp& ref(int i0, int i1, size_t* hashval=0); + //! returns reference to the specified element (3D case) + template _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); + //! returns reference to the specified element (nD case) + template _Tp& ref(const int* idx, size_t* hashval=0); + //!@} + + //!@{ + /*! + return value of the specified sparse matrix element. + + `value<_Tp>(i0,...[,hashval])` is equivalent to + @code + { const _Tp* p = find<_Tp>(i0,...[,hashval]); return p ? *p : _Tp(); } + @endcode + + That is, if the element did not exist, the methods return 0. + */ + //! returns value of the specified element (1D case) + template _Tp value(int i0, size_t* hashval=0) const; + //! returns value of the specified element (2D case) + template _Tp value(int i0, int i1, size_t* hashval=0) const; + //! returns value of the specified element (3D case) + template _Tp value(int i0, int i1, int i2, size_t* hashval=0) const; + //! returns value of the specified element (nD case) + template _Tp value(const int* idx, size_t* hashval=0) const; + //!@} + + //!@{ + /*! + Return pointer to the specified sparse matrix element if it exists + + `find<_Tp>(i0,...[,hashval])` is equivalent to `(_const Tp*)ptr(i0,...false[,hashval])`. + + If the specified element does not exist, the methods return NULL. + */ + //! returns pointer to the specified element (1D case) + template const _Tp* find(int i0, size_t* hashval=0) const; + //! returns pointer to the specified element (2D case) + template const _Tp* find(int i0, int i1, size_t* hashval=0) const; + //! returns pointer to the specified element (3D case) + template const _Tp* find(int i0, int i1, int i2, size_t* hashval=0) const; + //! returns pointer to the specified element (nD case) + template const _Tp* find(const int* idx, size_t* hashval=0) const; + //!@} + + //! erases the specified element (2D case) + void erase(int i0, int i1, size_t* hashval=0); + //! erases the specified element (3D case) + void erase(int i0, int i1, int i2, size_t* hashval=0); + //! erases the specified element (nD case) + void erase(const int* idx, size_t* hashval=0); + + //!@{ + /*! + return the sparse matrix iterator pointing to the first sparse matrix element + */ + //! returns the sparse matrix iterator at the matrix beginning + SparseMatIterator begin(); + //! returns the sparse matrix iterator at the matrix beginning + template SparseMatIterator_<_Tp> begin(); + //! returns the read-only sparse matrix iterator at the matrix beginning + SparseMatConstIterator begin() const; + //! returns the read-only sparse matrix iterator at the matrix beginning + template SparseMatConstIterator_<_Tp> begin() const; + //!@} + /*! + return the sparse matrix iterator pointing to the element following the last sparse matrix element + */ + //! returns the sparse matrix iterator at the matrix end + SparseMatIterator end(); + //! returns the read-only sparse matrix iterator at the matrix end + SparseMatConstIterator end() const; + //! returns the typed sparse matrix iterator at the matrix end + template SparseMatIterator_<_Tp> end(); + //! returns the typed read-only sparse matrix iterator at the matrix end + template SparseMatConstIterator_<_Tp> end() const; + + //! returns the value stored in the sparse martix node + template _Tp& value(Node* n); + //! returns the value stored in the sparse martix node + template const _Tp& value(const Node* n) const; + + ////////////// some internal-use methods /////////////// + Node* node(size_t nidx); + const Node* node(size_t nidx) const; + + uchar* newNode(const int* idx, size_t hashval); + void removeNode(size_t hidx, size_t nidx, size_t previdx); + void resizeHashTab(size_t newsize); + + int flags; + Hdr* hdr; +}; + + + +///////////////////////////////// SparseMat_<_Tp> //////////////////////////////////// + +/** @brief Template sparse n-dimensional array class derived from SparseMat + +SparseMat_ is a thin wrapper on top of SparseMat created in the same way as Mat_ . It simplifies +notation of some operations: +@code + int sz[] = {10, 20, 30}; + SparseMat_ M(3, sz); + ... + M.ref(1, 2, 3) = M(4, 5, 6) + M(7, 8, 9); +@endcode + */ +template class SparseMat_ : public SparseMat +{ +public: + typedef SparseMatIterator_<_Tp> iterator; + typedef SparseMatConstIterator_<_Tp> const_iterator; + + //! the default constructor + SparseMat_(); + //! the full constructor equivalent to SparseMat(dims, _sizes, DataType<_Tp>::type) + SparseMat_(int dims, const int* _sizes); + //! the copy constructor. If DataType<_Tp>.type != m.type(), the m elements are converted + SparseMat_(const SparseMat& m); + //! the copy constructor. This is O(1) operation - no data is copied + SparseMat_(const SparseMat_& m); + //! converts dense matrix to the sparse form + SparseMat_(const Mat& m); + //! converts the old-style sparse matrix to the C++ class. All the elements are copied + //SparseMat_(const CvSparseMat* m); + //! the assignment operator. If DataType<_Tp>.type != m.type(), the m elements are converted + SparseMat_& operator = (const SparseMat& m); + //! the assignment operator. This is O(1) operation - no data is copied + SparseMat_& operator = (const SparseMat_& m); + //! converts dense matrix to the sparse form + SparseMat_& operator = (const Mat& m); + + //! makes full copy of the matrix. All the elements are duplicated + SparseMat_ clone() const CV_NODISCARD; + //! equivalent to cv::SparseMat::create(dims, _sizes, DataType<_Tp>::type) + void create(int dims, const int* _sizes); + //! converts sparse matrix to the old-style CvSparseMat. All the elements are copied + //operator CvSparseMat*() const; + + //! returns type of the matrix elements + int type() const; + //! returns depth of the matrix elements + int depth() const; + //! returns the number of channels in each matrix element + int channels() const; + + //! equivalent to SparseMat::ref<_Tp>(i0, hashval) + _Tp& ref(int i0, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(i0, i1, hashval) + _Tp& ref(int i0, int i1, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(i0, i1, i2, hashval) + _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); + //! equivalent to SparseMat::ref<_Tp>(idx, hashval) + _Tp& ref(const int* idx, size_t* hashval=0); + + //! equivalent to SparseMat::value<_Tp>(i0, hashval) + _Tp operator()(int i0, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(i0, i1, hashval) + _Tp operator()(int i0, int i1, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(i0, i1, i2, hashval) + _Tp operator()(int i0, int i1, int i2, size_t* hashval=0) const; + //! equivalent to SparseMat::value<_Tp>(idx, hashval) + _Tp operator()(const int* idx, size_t* hashval=0) const; + + //! returns sparse matrix iterator pointing to the first sparse matrix element + SparseMatIterator_<_Tp> begin(); + //! returns read-only sparse matrix iterator pointing to the first sparse matrix element + SparseMatConstIterator_<_Tp> begin() const; + //! returns sparse matrix iterator pointing to the element following the last sparse matrix element + SparseMatIterator_<_Tp> end(); + //! returns read-only sparse matrix iterator pointing to the element following the last sparse matrix element + SparseMatConstIterator_<_Tp> end() const; +}; + + + +////////////////////////////////// MatConstIterator ////////////////////////////////// + +class CV_EXPORTS MatConstIterator +{ +public: + typedef uchar* value_type; + typedef ptrdiff_t difference_type; + typedef const uchar** pointer; + typedef uchar* reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! default constructor + MatConstIterator(); + //! constructor that sets the iterator to the beginning of the matrix + MatConstIterator(const Mat* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator(const Mat* _m, const int* _idx); + //! copy constructor + MatConstIterator(const MatConstIterator& it); + + //! copy operator + MatConstIterator& operator = (const MatConstIterator& it); + //! returns the current matrix element + const uchar* operator *() const; + //! returns the i-th matrix element, relative to the current + const uchar* operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatConstIterator& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatConstIterator& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatConstIterator& operator --(); + //! decrements the iterator + MatConstIterator operator --(int); + //! increments the iterator + MatConstIterator& operator ++(); + //! increments the iterator + MatConstIterator operator ++(int); + //! returns the current iterator position + Point pos() const; + //! returns the current iterator position + void pos(int* _idx) const; + + ptrdiff_t lpos() const; + void seek(ptrdiff_t ofs, bool relative = false); + void seek(const int* _idx, bool relative = false); + + const Mat* m; + size_t elemSize; + const uchar* ptr; + const uchar* sliceStart; + const uchar* sliceEnd; +}; + + + +////////////////////////////////// MatConstIterator_ ///////////////////////////////// + +/** @brief Matrix read-only iterator + */ +template +class MatConstIterator_ : public MatConstIterator +{ +public: + typedef _Tp value_type; + typedef ptrdiff_t difference_type; + typedef const _Tp* pointer; + typedef const _Tp& reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! default constructor + MatConstIterator_(); + //! constructor that sets the iterator to the beginning of the matrix + MatConstIterator_(const Mat_<_Tp>* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatConstIterator_(const Mat_<_Tp>* _m, const int* _idx); + //! copy constructor + MatConstIterator_(const MatConstIterator_& it); + + //! copy operator + MatConstIterator_& operator = (const MatConstIterator_& it); + //! returns the current matrix element + const _Tp& operator *() const; + //! returns the i-th matrix element, relative to the current + const _Tp& operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatConstIterator_& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatConstIterator_& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatConstIterator_& operator --(); + //! decrements the iterator + MatConstIterator_ operator --(int); + //! increments the iterator + MatConstIterator_& operator ++(); + //! increments the iterator + MatConstIterator_ operator ++(int); + //! returns the current iterator position + Point pos() const; +}; + + + +//////////////////////////////////// MatIterator_ //////////////////////////////////// + +/** @brief Matrix read-write iterator +*/ +template +class MatIterator_ : public MatConstIterator_<_Tp> +{ +public: + typedef _Tp* pointer; + typedef _Tp& reference; + + typedef std::random_access_iterator_tag iterator_category; + + //! the default constructor + MatIterator_(); + //! constructor that sets the iterator to the beginning of the matrix + MatIterator_(Mat_<_Tp>* _m); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, int _row, int _col=0); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, Point _pt); + //! constructor that sets the iterator to the specified element of the matrix + MatIterator_(Mat_<_Tp>* _m, const int* _idx); + //! copy constructor + MatIterator_(const MatIterator_& it); + //! copy operator + MatIterator_& operator = (const MatIterator_<_Tp>& it ); + + //! returns the current matrix element + _Tp& operator *() const; + //! returns the i-th matrix element, relative to the current + _Tp& operator [](ptrdiff_t i) const; + + //! shifts the iterator forward by the specified number of elements + MatIterator_& operator += (ptrdiff_t ofs); + //! shifts the iterator backward by the specified number of elements + MatIterator_& operator -= (ptrdiff_t ofs); + //! decrements the iterator + MatIterator_& operator --(); + //! decrements the iterator + MatIterator_ operator --(int); + //! increments the iterator + MatIterator_& operator ++(); + //! increments the iterator + MatIterator_ operator ++(int); +}; + + + +/////////////////////////////// SparseMatConstIterator /////////////////////////////// + +/** @brief Read-Only Sparse Matrix Iterator. + + Here is how to use the iterator to compute the sum of floating-point sparse matrix elements: + + \code + SparseMatConstIterator it = m.begin(), it_end = m.end(); + double s = 0; + CV_Assert( m.type() == CV_32F ); + for( ; it != it_end; ++it ) + s += it.value(); + \endcode +*/ +class CV_EXPORTS SparseMatConstIterator +{ +public: + //! the default constructor + SparseMatConstIterator(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatConstIterator(const SparseMat* _m); + //! the copy constructor + SparseMatConstIterator(const SparseMatConstIterator& it); + + //! the assignment operator + SparseMatConstIterator& operator = (const SparseMatConstIterator& it); + + //! template method returning the current matrix element + template const _Tp& value() const; + //! returns the current node of the sparse matrix. it.node->idx is the current element index + const SparseMat::Node* node() const; + + //! moves iterator to the previous element + SparseMatConstIterator& operator --(); + //! moves iterator to the previous element + SparseMatConstIterator operator --(int); + //! moves iterator to the next element + SparseMatConstIterator& operator ++(); + //! moves iterator to the next element + SparseMatConstIterator operator ++(int); + + //! moves iterator to the element after the last element + void seekEnd(); + + const SparseMat* m; + size_t hashidx; + uchar* ptr; +}; + + + +////////////////////////////////// SparseMatIterator ///////////////////////////////// + +/** @brief Read-write Sparse Matrix Iterator + + The class is similar to cv::SparseMatConstIterator, + but can be used for in-place modification of the matrix elements. +*/ +class CV_EXPORTS SparseMatIterator : public SparseMatConstIterator +{ +public: + //! the default constructor + SparseMatIterator(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatIterator(SparseMat* _m); + //! the full constructor setting the iterator to the specified sparse matrix element + SparseMatIterator(SparseMat* _m, const int* idx); + //! the copy constructor + SparseMatIterator(const SparseMatIterator& it); + + //! the assignment operator + SparseMatIterator& operator = (const SparseMatIterator& it); + //! returns read-write reference to the current sparse matrix element + template _Tp& value() const; + //! returns pointer to the current sparse matrix node. it.node->idx is the index of the current element (do not modify it!) + SparseMat::Node* node() const; + + //! moves iterator to the next element + SparseMatIterator& operator ++(); + //! moves iterator to the next element + SparseMatIterator operator ++(int); +}; + + + +/////////////////////////////// SparseMatConstIterator_ ////////////////////////////// + +/** @brief Template Read-Only Sparse Matrix Iterator Class. + + This is the derived from SparseMatConstIterator class that + introduces more convenient operator *() for accessing the current element. +*/ +template class SparseMatConstIterator_ : public SparseMatConstIterator +{ +public: + + typedef std::forward_iterator_tag iterator_category; + + //! the default constructor + SparseMatConstIterator_(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatConstIterator_(const SparseMat_<_Tp>* _m); + SparseMatConstIterator_(const SparseMat* _m); + //! the copy constructor + SparseMatConstIterator_(const SparseMatConstIterator_& it); + + //! the assignment operator + SparseMatConstIterator_& operator = (const SparseMatConstIterator_& it); + //! the element access operator + const _Tp& operator *() const; + + //! moves iterator to the next element + SparseMatConstIterator_& operator ++(); + //! moves iterator to the next element + SparseMatConstIterator_ operator ++(int); +}; + + + +///////////////////////////////// SparseMatIterator_ ///////////////////////////////// + +/** @brief Template Read-Write Sparse Matrix Iterator Class. + + This is the derived from cv::SparseMatConstIterator_ class that + introduces more convenient operator *() for accessing the current element. +*/ +template class SparseMatIterator_ : public SparseMatConstIterator_<_Tp> +{ +public: + + typedef std::forward_iterator_tag iterator_category; + + //! the default constructor + SparseMatIterator_(); + //! the full constructor setting the iterator to the first sparse matrix element + SparseMatIterator_(SparseMat_<_Tp>* _m); + SparseMatIterator_(SparseMat* _m); + //! the copy constructor + SparseMatIterator_(const SparseMatIterator_& it); + + //! the assignment operator + SparseMatIterator_& operator = (const SparseMatIterator_& it); + //! returns the reference to the current element + _Tp& operator *() const; + + //! moves the iterator to the next element + SparseMatIterator_& operator ++(); + //! moves the iterator to the next element + SparseMatIterator_ operator ++(int); +}; + + + +/////////////////////////////////// NAryMatIterator ////////////////////////////////// + +/** @brief n-ary multi-dimensional array iterator. + +Use the class to implement unary, binary, and, generally, n-ary element-wise operations on +multi-dimensional arrays. Some of the arguments of an n-ary function may be continuous arrays, some +may be not. It is possible to use conventional MatIterator 's for each array but incrementing all of +the iterators after each small operations may be a big overhead. In this case consider using +NAryMatIterator to iterate through several matrices simultaneously as long as they have the same +geometry (dimensionality and all the dimension sizes are the same). On each iteration `it.planes[0]`, +`it.planes[1]`,... will be the slices of the corresponding matrices. + +The example below illustrates how you can compute a normalized and threshold 3D color histogram: +@code + void computeNormalizedColorHist(const Mat& image, Mat& hist, int N, double minProb) + { + const int histSize[] = {N, N, N}; + + // make sure that the histogram has a proper size and type + hist.create(3, histSize, CV_32F); + + // and clear it + hist = Scalar(0); + + // the loop below assumes that the image + // is a 8-bit 3-channel. check it. + CV_Assert(image.type() == CV_8UC3); + MatConstIterator_ it = image.begin(), + it_end = image.end(); + for( ; it != it_end; ++it ) + { + const Vec3b& pix = *it; + hist.at(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f; + } + + minProb *= image.rows*image.cols; + + // initialize iterator (the style is different from STL). + // after initialization the iterator will contain + // the number of slices or planes the iterator will go through. + // it simultaneously increments iterators for several matrices + // supplied as a null terminated list of pointers + const Mat* arrays[] = {&hist, 0}; + Mat planes[1]; + NAryMatIterator itNAry(arrays, planes, 1); + double s = 0; + // iterate through the matrix. on each iteration + // itNAry.planes[i] (of type Mat) will be set to the current plane + // of the i-th n-dim matrix passed to the iterator constructor. + for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) + { + threshold(itNAry.planes[0], itNAry.planes[0], minProb, 0, THRESH_TOZERO); + s += sum(itNAry.planes[0])[0]; + } + + s = 1./s; + itNAry = NAryMatIterator(arrays, planes, 1); + for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) + itNAry.planes[0] *= s; + } +@endcode + */ +class CV_EXPORTS NAryMatIterator +{ +public: + //! the default constructor + NAryMatIterator(); + //! the full constructor taking arbitrary number of n-dim matrices + NAryMatIterator(const Mat** arrays, uchar** ptrs, int narrays=-1); + //! the full constructor taking arbitrary number of n-dim matrices + NAryMatIterator(const Mat** arrays, Mat* planes, int narrays=-1); + //! the separate iterator initialization method + void init(const Mat** arrays, Mat* planes, uchar** ptrs, int narrays=-1); + + //! proceeds to the next plane of every iterated matrix + NAryMatIterator& operator ++(); + //! proceeds to the next plane of every iterated matrix (postfix increment operator) + NAryMatIterator operator ++(int); + + //! the iterated arrays + const Mat** arrays; + //! the current planes + Mat* planes; + //! data pointers + uchar** ptrs; + //! the number of arrays + int narrays; + //! the number of hyper-planes that the iterator steps through + size_t nplanes; + //! the size of each segment (in elements) + size_t size; +protected: + int iterdepth; + size_t idx; +}; + + + +///////////////////////////////// Matrix Expressions ///////////////////////////////// + +class CV_EXPORTS MatOp +{ +public: + MatOp(); + virtual ~MatOp(); + + virtual bool elementWise(const MatExpr& expr) const; + virtual void assign(const MatExpr& expr, Mat& m, int type=-1) const = 0; + virtual void roi(const MatExpr& expr, const Range& rowRange, + const Range& colRange, MatExpr& res) const; + virtual void diag(const MatExpr& expr, int d, MatExpr& res) const; + virtual void augAssignAdd(const MatExpr& expr, Mat& m) const; + virtual void augAssignSubtract(const MatExpr& expr, Mat& m) const; + virtual void augAssignMultiply(const MatExpr& expr, Mat& m) const; + virtual void augAssignDivide(const MatExpr& expr, Mat& m) const; + virtual void augAssignAnd(const MatExpr& expr, Mat& m) const; + virtual void augAssignOr(const MatExpr& expr, Mat& m) const; + virtual void augAssignXor(const MatExpr& expr, Mat& m) const; + + virtual void add(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void add(const MatExpr& expr1, const Scalar& s, MatExpr& res) const; + + virtual void subtract(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void subtract(const Scalar& s, const MatExpr& expr, MatExpr& res) const; + + virtual void multiply(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res, double scale=1) const; + virtual void multiply(const MatExpr& expr1, double s, MatExpr& res) const; + + virtual void divide(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res, double scale=1) const; + virtual void divide(double s, const MatExpr& expr, MatExpr& res) const; + + virtual void abs(const MatExpr& expr, MatExpr& res) const; + + virtual void transpose(const MatExpr& expr, MatExpr& res) const; + virtual void matmul(const MatExpr& expr1, const MatExpr& expr2, MatExpr& res) const; + virtual void invert(const MatExpr& expr, int method, MatExpr& res) const; + + virtual Size size(const MatExpr& expr) const; + virtual int type(const MatExpr& expr) const; +}; + +/** @brief Matrix expression representation +@anchor MatrixExpressions +This is a list of implemented matrix operations that can be combined in arbitrary complex +expressions (here A, B stand for matrices ( Mat ), s for a scalar ( Scalar ), alpha for a +real-valued scalar ( double )): +- Addition, subtraction, negation: `A+B`, `A-B`, `A+s`, `A-s`, `s+A`, `s-A`, `-A` +- Scaling: `A*alpha` +- Per-element multiplication and division: `A.mul(B)`, `A/B`, `alpha/A` +- Matrix multiplication: `A*B` +- Transposition: `A.t()` (means AT) +- Matrix inversion and pseudo-inversion, solving linear systems and least-squares problems: + `A.inv([method]) (~ A-1)`, `A.inv([method])*B (~ X: AX=B)` +- Comparison: `A cmpop B`, `A cmpop alpha`, `alpha cmpop A`, where *cmpop* is one of + `>`, `>=`, `==`, `!=`, `<=`, `<`. The result of comparison is an 8-bit single channel mask whose + elements are set to 255 (if the particular element or pair of elements satisfy the condition) or + 0. +- Bitwise logical operations: `A logicop B`, `A logicop s`, `s logicop A`, `~A`, where *logicop* is one of + `&`, `|`, `^`. +- Element-wise minimum and maximum: `min(A, B)`, `min(A, alpha)`, `max(A, B)`, `max(A, alpha)` +- Element-wise absolute value: `abs(A)` +- Cross-product, dot-product: `A.cross(B)`, `A.dot(B)` +- Any function of matrix or matrices and scalars that returns a matrix or a scalar, such as norm, + mean, sum, countNonZero, trace, determinant, repeat, and others. +- Matrix initializers ( Mat::eye(), Mat::zeros(), Mat::ones() ), matrix comma-separated + initializers, matrix constructors and operators that extract sub-matrices (see Mat description). +- Mat_() constructors to cast the result to the proper type. +@note Comma-separated initializers and probably some other operations may require additional +explicit Mat() or Mat_() constructor calls to resolve a possible ambiguity. + +Here are examples of matrix expressions: +@code + // compute pseudo-inverse of A, equivalent to A.inv(DECOMP_SVD) + SVD svd(A); + Mat pinvA = svd.vt.t()*Mat::diag(1./svd.w)*svd.u.t(); + + // compute the new vector of parameters in the Levenberg-Marquardt algorithm + x -= (A.t()*A + lambda*Mat::eye(A.cols,A.cols,A.type())).inv(DECOMP_CHOLESKY)*(A.t()*err); + + // sharpen image using "unsharp mask" algorithm + Mat blurred; double sigma = 1, threshold = 5, amount = 1; + GaussianBlur(img, blurred, Size(), sigma, sigma); + Mat lowContrastMask = abs(img - blurred) < threshold; + Mat sharpened = img*(1+amount) + blurred*(-amount); + img.copyTo(sharpened, lowContrastMask); +@endcode +*/ +class CV_EXPORTS MatExpr +{ +public: + MatExpr(); + explicit MatExpr(const Mat& m); + + MatExpr(const MatOp* _op, int _flags, const Mat& _a = Mat(), const Mat& _b = Mat(), + const Mat& _c = Mat(), double _alpha = 1, double _beta = 1, const Scalar& _s = Scalar()); + + operator Mat() const; + template operator Mat_<_Tp>() const; + + Size size() const; + int type() const; + + MatExpr row(int y) const; + MatExpr col(int x) const; + MatExpr diag(int d = 0) const; + MatExpr operator()( const Range& rowRange, const Range& colRange ) const; + MatExpr operator()( const Rect& roi ) const; + + MatExpr t() const; + MatExpr inv(int method = DECOMP_LU) const; + MatExpr mul(const MatExpr& e, double scale=1) const; + MatExpr mul(const Mat& m, double scale=1) const; + + Mat cross(const Mat& m) const; + double dot(const Mat& m) const; + + void swap(MatExpr& b); + + const MatOp* op; + int flags; + + Mat a, b, c; + double alpha, beta; + Scalar s; +}; + +//! @} core_basic + +//! @relates cv::MatExpr +//! @{ +CV_EXPORTS MatExpr operator + (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator + (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator + (const Scalar& s, const Mat& a); +CV_EXPORTS MatExpr operator + (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator + (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator + (const MatExpr& e, const Scalar& s); +CV_EXPORTS MatExpr operator + (const Scalar& s, const MatExpr& e); +CV_EXPORTS MatExpr operator + (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator + (const Mat& a, const Matx<_Tp, m, n>& b) { return a + Mat(b); } +template static inline +MatExpr operator + (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) + b; } + +CV_EXPORTS MatExpr operator - (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator - (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator - (const Scalar& s, const Mat& a); +CV_EXPORTS MatExpr operator - (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator - (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator - (const MatExpr& e, const Scalar& s); +CV_EXPORTS MatExpr operator - (const Scalar& s, const MatExpr& e); +CV_EXPORTS MatExpr operator - (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator - (const Mat& a, const Matx<_Tp, m, n>& b) { return a - Mat(b); } +template static inline +MatExpr operator - (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) - b; } + +CV_EXPORTS MatExpr operator - (const Mat& m); +CV_EXPORTS MatExpr operator - (const MatExpr& e); + +CV_EXPORTS MatExpr operator * (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator * (const Mat& a, double s); +CV_EXPORTS MatExpr operator * (double s, const Mat& a); +CV_EXPORTS MatExpr operator * (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator * (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator * (const MatExpr& e, double s); +CV_EXPORTS MatExpr operator * (double s, const MatExpr& e); +CV_EXPORTS MatExpr operator * (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator * (const Mat& a, const Matx<_Tp, m, n>& b) { return a * Mat(b); } +template static inline +MatExpr operator * (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) * b; } + +CV_EXPORTS MatExpr operator / (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator / (const Mat& a, double s); +CV_EXPORTS MatExpr operator / (double s, const Mat& a); +CV_EXPORTS MatExpr operator / (const MatExpr& e, const Mat& m); +CV_EXPORTS MatExpr operator / (const Mat& m, const MatExpr& e); +CV_EXPORTS MatExpr operator / (const MatExpr& e, double s); +CV_EXPORTS MatExpr operator / (double s, const MatExpr& e); +CV_EXPORTS MatExpr operator / (const MatExpr& e1, const MatExpr& e2); +template static inline +MatExpr operator / (const Mat& a, const Matx<_Tp, m, n>& b) { return a / Mat(b); } +template static inline +MatExpr operator / (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) / b; } + +CV_EXPORTS MatExpr operator < (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator < (const Mat& a, double s); +CV_EXPORTS MatExpr operator < (double s, const Mat& a); +template static inline +MatExpr operator < (const Mat& a, const Matx<_Tp, m, n>& b) { return a < Mat(b); } +template static inline +MatExpr operator < (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) < b; } + +CV_EXPORTS MatExpr operator <= (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator <= (const Mat& a, double s); +CV_EXPORTS MatExpr operator <= (double s, const Mat& a); +template static inline +MatExpr operator <= (const Mat& a, const Matx<_Tp, m, n>& b) { return a <= Mat(b); } +template static inline +MatExpr operator <= (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) <= b; } + +CV_EXPORTS MatExpr operator == (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator == (const Mat& a, double s); +CV_EXPORTS MatExpr operator == (double s, const Mat& a); +template static inline +MatExpr operator == (const Mat& a, const Matx<_Tp, m, n>& b) { return a == Mat(b); } +template static inline +MatExpr operator == (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) == b; } + +CV_EXPORTS MatExpr operator != (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator != (const Mat& a, double s); +CV_EXPORTS MatExpr operator != (double s, const Mat& a); +template static inline +MatExpr operator != (const Mat& a, const Matx<_Tp, m, n>& b) { return a != Mat(b); } +template static inline +MatExpr operator != (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) != b; } + +CV_EXPORTS MatExpr operator >= (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator >= (const Mat& a, double s); +CV_EXPORTS MatExpr operator >= (double s, const Mat& a); +template static inline +MatExpr operator >= (const Mat& a, const Matx<_Tp, m, n>& b) { return a >= Mat(b); } +template static inline +MatExpr operator >= (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) >= b; } + +CV_EXPORTS MatExpr operator > (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator > (const Mat& a, double s); +CV_EXPORTS MatExpr operator > (double s, const Mat& a); +template static inline +MatExpr operator > (const Mat& a, const Matx<_Tp, m, n>& b) { return a > Mat(b); } +template static inline +MatExpr operator > (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) > b; } + +CV_EXPORTS MatExpr operator & (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator & (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator & (const Scalar& s, const Mat& a); +template static inline +MatExpr operator & (const Mat& a, const Matx<_Tp, m, n>& b) { return a & Mat(b); } +template static inline +MatExpr operator & (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) & b; } + +CV_EXPORTS MatExpr operator | (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator | (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator | (const Scalar& s, const Mat& a); +template static inline +MatExpr operator | (const Mat& a, const Matx<_Tp, m, n>& b) { return a | Mat(b); } +template static inline +MatExpr operator | (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) | b; } + +CV_EXPORTS MatExpr operator ^ (const Mat& a, const Mat& b); +CV_EXPORTS MatExpr operator ^ (const Mat& a, const Scalar& s); +CV_EXPORTS MatExpr operator ^ (const Scalar& s, const Mat& a); +template static inline +MatExpr operator ^ (const Mat& a, const Matx<_Tp, m, n>& b) { return a ^ Mat(b); } +template static inline +MatExpr operator ^ (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) ^ b; } + +CV_EXPORTS MatExpr operator ~(const Mat& m); + +CV_EXPORTS MatExpr min(const Mat& a, const Mat& b); +CV_EXPORTS MatExpr min(const Mat& a, double s); +CV_EXPORTS MatExpr min(double s, const Mat& a); +template static inline +MatExpr min (const Mat& a, const Matx<_Tp, m, n>& b) { return min(a, Mat(b)); } +template static inline +MatExpr min (const Matx<_Tp, m, n>& a, const Mat& b) { return min(Mat(a), b); } + +CV_EXPORTS MatExpr max(const Mat& a, const Mat& b); +CV_EXPORTS MatExpr max(const Mat& a, double s); +CV_EXPORTS MatExpr max(double s, const Mat& a); +template static inline +MatExpr max (const Mat& a, const Matx<_Tp, m, n>& b) { return max(a, Mat(b)); } +template static inline +MatExpr max (const Matx<_Tp, m, n>& a, const Mat& b) { return max(Mat(a), b); } + +/** @brief Calculates an absolute value of each matrix element. + +abs is a meta-function that is expanded to one of absdiff or convertScaleAbs forms: +- C = abs(A-B) is equivalent to `absdiff(A, B, C)` +- C = abs(A) is equivalent to `absdiff(A, Scalar::all(0), C)` +- C = `Mat_ >(abs(A*alpha + beta))` is equivalent to `convertScaleAbs(A, C, alpha, +beta)` + +The output matrix has the same size and the same type as the input one except for the last case, +where C is depth=CV_8U . +@param m matrix. +@sa @ref MatrixExpressions, absdiff, convertScaleAbs + */ +CV_EXPORTS MatExpr abs(const Mat& m); +/** @overload +@param e matrix expression. +*/ +CV_EXPORTS MatExpr abs(const MatExpr& e); +//! @} relates cv::MatExpr + +} // cv + +#include "opencv2/core/mat.inl.hpp" + +#endif // OPENCV_CORE_MAT_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.inl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.inl.hpp new file mode 100644 index 0000000..4935755 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/mat.inl.hpp @@ -0,0 +1,3506 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MATRIX_OPERATIONS_HPP +#define OPENCV_CORE_MATRIX_OPERATIONS_HPP + +#ifndef __cplusplus +# error mat.inl.hpp header must be compiled as C++ +#endif + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable: 4127 ) +#endif + +#if defined(CV_SKIP_DISABLE_CLANG_ENUM_WARNINGS) + // nothing +#elif defined(CV_FORCE_DISABLE_CLANG_ENUM_WARNINGS) + #define CV_DISABLE_CLANG_ENUM_WARNINGS +#elif defined(__clang__) && defined(__has_warning) + #if __has_warning("-Wdeprecated-enum-enum-conversion") && __has_warning("-Wdeprecated-anon-enum-enum-conversion") + #define CV_DISABLE_CLANG_ENUM_WARNINGS + #endif +#endif +#ifdef CV_DISABLE_CLANG_ENUM_WARNINGS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" +#pragma clang diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion" +#endif + +namespace cv +{ +CV__DEBUG_NS_BEGIN + + +//! @cond IGNORED + +////////////////////////// Custom (raw) type wrapper ////////////////////////// + +template static inline +int rawType() +{ + CV_StaticAssert(sizeof(_Tp) <= CV_CN_MAX, "sizeof(_Tp) is too large"); + const int elemSize = sizeof(_Tp); + return (int)CV_MAKETYPE(CV_8U, elemSize); +} + +//////////////////////// Input/Output Arrays //////////////////////// + +inline void _InputArray::init(int _flags, const void* _obj) +{ flags = _flags; obj = (void*)_obj; } + +inline void _InputArray::init(int _flags, const void* _obj, Size _sz) +{ flags = _flags; obj = (void*)_obj; sz = _sz; } + +inline void* _InputArray::getObj() const { return obj; } +inline int _InputArray::getFlags() const { return flags; } +inline Size _InputArray::getSz() const { return sz; } + +inline _InputArray::_InputArray() { init(NONE, 0); } +inline _InputArray::_InputArray(int _flags, void* _obj) { init(_flags, _obj); } +inline _InputArray::_InputArray(const Mat& m) { init(MAT+ACCESS_READ, &m); } +inline _InputArray::_InputArray(const std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_READ, &vec); } +inline _InputArray::_InputArray(const UMat& m) { init(UMAT+ACCESS_READ, &m); } +inline _InputArray::_InputArray(const std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputArray::_InputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, arr.data(), Size(1, _Nm)); } + +template inline +_InputArray::_InputArray(const std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_READ, arr.data(), Size(1, _Nm)); } +#endif + +inline +_InputArray::_InputArray(const std::vector& vec) +{ init(FIXED_TYPE + STD_BOOL_VECTOR + traits::Type::value + ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +inline +_InputArray::_InputArray(const std::vector >&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector > is not supported!\n"); } + +template inline +_InputArray::_InputArray(const std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_READ, &vec); } + +template inline +_InputArray::_InputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, &mtx, Size(n, m)); } + +template inline +_InputArray::_InputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, vec, Size(n, 1)); } + +template inline +_InputArray::_InputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_READ, &m); } + +inline _InputArray::_InputArray(const double& val) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + CV_64F + ACCESS_READ, &val, Size(1,1)); } + +inline _InputArray::_InputArray(const cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_READ, &d_mat); } + +inline _InputArray::_InputArray(const std::vector& d_mat) +{ init(STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);} + +inline _InputArray::_InputArray(const ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_READ, &buf); } + +inline _InputArray::_InputArray(const cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_READ, &cuda_mem); } + +template inline +_InputArray _InputArray::rawIn(const std::vector<_Tp>& vec) +{ + _InputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_READ; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputArray _InputArray::rawIn(const std::array<_Tp, _Nm>& arr) +{ + _InputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + +inline _InputArray::~_InputArray() {} + +inline Mat _InputArray::getMat(int i) const +{ + if( kind() == MAT && i < 0 ) + return *(const Mat*)obj; + return getMat_(i); +} + +inline bool _InputArray::isMat() const { return kind() == _InputArray::MAT; } +inline bool _InputArray::isUMat() const { return kind() == _InputArray::UMAT; } +inline bool _InputArray::isMatVector() const { return kind() == _InputArray::STD_VECTOR_MAT; } +inline bool _InputArray::isUMatVector() const { return kind() == _InputArray::STD_VECTOR_UMAT; } +inline bool _InputArray::isMatx() const { return kind() == _InputArray::MATX; } +inline bool _InputArray::isVector() const { return kind() == _InputArray::STD_VECTOR || + kind() == _InputArray::STD_BOOL_VECTOR || + (kind() == _InputArray::MATX && (sz.width <= 1 || sz.height <= 1)); } +inline bool _InputArray::isGpuMat() const { return kind() == _InputArray::CUDA_GPU_MAT; } +inline bool _InputArray::isGpuMatVector() const { return kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT; } + +//////////////////////////////////////////////////////////////////////////////////////// + +inline _OutputArray::_OutputArray() { init(ACCESS_WRITE, 0); } +inline _OutputArray::_OutputArray(int _flags, void* _obj) { init(_flags|ACCESS_WRITE, _obj); } +inline _OutputArray::_OutputArray(Mat& m) { init(MAT+ACCESS_WRITE, &m); } +inline _OutputArray::_OutputArray(std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_WRITE, &vec); } +inline _OutputArray::_OutputArray(UMat& m) { init(UMAT+ACCESS_WRITE, &m); } +inline _OutputArray::_OutputArray(std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray::_OutputArray(std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } + +template inline +_OutputArray::_OutputArray(std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +#endif + +inline +_OutputArray::_OutputArray(std::vector&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector cannot be an output array\n"); } + +template inline +_OutputArray::_OutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +inline +_OutputArray::_OutputArray(std::vector >&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector > cannot be an output array\n"); } + +template inline +_OutputArray::_OutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &m); } + +template inline +_OutputArray::_OutputArray(Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, &mtx, Size(n, m)); } + +template inline +_OutputArray::_OutputArray(_Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, vec, Size(n, 1)); } + +template inline +_OutputArray::_OutputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray::_OutputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } + +template inline +_OutputArray::_OutputArray(const std::array& arr) +{ init(FIXED_SIZE + STD_ARRAY_MAT + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +#endif + +template inline +_OutputArray::_OutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &vec); } + +template inline +_OutputArray::_OutputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + traits::Type<_Tp>::value + ACCESS_WRITE, &m); } + +template inline +_OutputArray::_OutputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, &mtx, Size(n, m)); } + +template inline +_OutputArray::_OutputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, vec, Size(n, 1)); } + +inline _OutputArray::_OutputArray(cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } + +inline _OutputArray::_OutputArray(std::vector& d_mat) +{ init(STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);} + +inline _OutputArray::_OutputArray(ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_WRITE, &buf); } + +inline _OutputArray::_OutputArray(cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_WRITE, &cuda_mem); } + +inline _OutputArray::_OutputArray(const Mat& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + ACCESS_WRITE, &m); } + +inline _OutputArray::_OutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_MAT + ACCESS_WRITE, &vec); } + +inline _OutputArray::_OutputArray(const UMat& m) +{ init(FIXED_TYPE + FIXED_SIZE + UMAT + ACCESS_WRITE, &m); } + +inline _OutputArray::_OutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_UMAT + ACCESS_WRITE, &vec); } + +inline _OutputArray::_OutputArray(const cuda::GpuMat& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } + + +inline _OutputArray::_OutputArray(const ogl::Buffer& buf) +{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_WRITE, &buf); } + +inline _OutputArray::_OutputArray(const cuda::HostMem& cuda_mem) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_WRITE, &cuda_mem); } + +template inline +_OutputArray _OutputArray::rawOut(std::vector<_Tp>& vec) +{ + _OutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_WRITE; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray _OutputArray::rawOut(std::array<_Tp, _Nm>& arr) +{ + _OutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////// + +inline _InputOutputArray::_InputOutputArray() { init(ACCESS_RW, 0); } +inline _InputOutputArray::_InputOutputArray(int _flags, void* _obj) { init(_flags|ACCESS_RW, _obj); } +inline _InputOutputArray::_InputOutputArray(Mat& m) { init(MAT+ACCESS_RW, &m); } +inline _InputOutputArray::_InputOutputArray(std::vector& vec) { init(STD_VECTOR_MAT+ACCESS_RW, &vec); } +inline _InputOutputArray::_InputOutputArray(UMat& m) { init(UMAT+ACCESS_RW, &m); } +inline _InputOutputArray::_InputOutputArray(std::vector& vec) { init(STD_VECTOR_UMAT+ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector<_Tp>& vec) +{ init(FIXED_TYPE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray::_InputOutputArray(std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } + +template inline +_InputOutputArray::_InputOutputArray(std::array& arr) +{ init(STD_ARRAY_MAT + ACCESS_RW, arr.data(), Size(1, _Nm)); } +#endif + +inline _InputOutputArray::_InputOutputArray(std::vector&) +{ CV_Error(Error::StsUnsupportedFormat, "std::vector cannot be an input/output array\n"); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(std::vector >& vec) +{ init(FIXED_TYPE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(Mat_<_Tp>& m) +{ init(FIXED_TYPE + MAT + traits::Type<_Tp>::value + ACCESS_RW, &m); } + +template inline +_InputOutputArray::_InputOutputArray(Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, &mtx, Size(n, m)); } + +template inline +_InputOutputArray::_InputOutputArray(_Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, vec, Size(n, 1)); } + +template inline +_InputOutputArray::_InputOutputArray(const std::vector<_Tp>& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray::_InputOutputArray(const std::array<_Tp, _Nm>& arr) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } + +template inline +_InputOutputArray::_InputOutputArray(const std::array& arr) +{ init(FIXED_SIZE + STD_ARRAY_MAT + ACCESS_RW, arr.data(), Size(1, _Nm)); } +#endif + +template inline +_InputOutputArray::_InputOutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_VECTOR + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(const std::vector >& vec) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_MAT + traits::Type<_Tp>::value + ACCESS_RW, &vec); } + +template inline +_InputOutputArray::_InputOutputArray(const Mat_<_Tp>& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + traits::Type<_Tp>::value + ACCESS_RW, &m); } + +template inline +_InputOutputArray::_InputOutputArray(const Matx<_Tp, m, n>& mtx) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, &mtx, Size(n, m)); } + +template inline +_InputOutputArray::_InputOutputArray(const _Tp* vec, int n) +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, vec, Size(n, 1)); } + +inline _InputOutputArray::_InputOutputArray(cuda::GpuMat& d_mat) +{ init(CUDA_GPU_MAT + ACCESS_RW, &d_mat); } + +inline _InputOutputArray::_InputOutputArray(ogl::Buffer& buf) +{ init(OPENGL_BUFFER + ACCESS_RW, &buf); } + +inline _InputOutputArray::_InputOutputArray(cuda::HostMem& cuda_mem) +{ init(CUDA_HOST_MEM + ACCESS_RW, &cuda_mem); } + +inline _InputOutputArray::_InputOutputArray(const Mat& m) +{ init(FIXED_TYPE + FIXED_SIZE + MAT + ACCESS_RW, &m); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_MAT + ACCESS_RW, &vec); } + +inline _InputOutputArray::_InputOutputArray(const UMat& m) +{ init(FIXED_TYPE + FIXED_SIZE + UMAT + ACCESS_RW, &m); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& vec) +{ init(FIXED_SIZE + STD_VECTOR_UMAT + ACCESS_RW, &vec); } + +inline _InputOutputArray::_InputOutputArray(const cuda::GpuMat& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_RW, &d_mat); } + +inline _InputOutputArray::_InputOutputArray(const std::vector& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);} + +template<> inline _InputOutputArray::_InputOutputArray(std::vector& d_mat) +{ init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);} + +inline _InputOutputArray::_InputOutputArray(const ogl::Buffer& buf) +{ init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_RW, &buf); } + +inline _InputOutputArray::_InputOutputArray(const cuda::HostMem& cuda_mem) +{ init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_RW, &cuda_mem); } + +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::vector<_Tp>& vec) +{ + _InputOutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_RW; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::array<_Tp, _Nm>& arr) +{ + _InputOutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + + +template static inline _InputArray rawIn(_Tp& v) { return _InputArray::rawIn(v); } +template static inline _OutputArray rawOut(_Tp& v) { return _OutputArray::rawOut(v); } +template static inline _InputOutputArray rawInOut(_Tp& v) { return _InputOutputArray::rawInOut(v); } + +CV__DEBUG_NS_END + +//////////////////////////////////////////// Mat ////////////////////////////////////////// + +template inline +Mat::Mat(const std::vector<_Tp>& vec, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), + cols(1), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if(vec.empty()) + return; + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&vec[0]; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat((int)vec.size(), 1, traits::Type<_Tp>::value, (uchar*)&vec[0]).copyTo(*this); +} + +#ifdef CV_CXX11 +template inline +Mat::Mat(const std::initializer_list<_Tp> list) + : Mat() +{ + CV_Assert(list.size() != 0); + Mat((int)list.size(), 1, traits::Type<_Tp>::value, (uchar*)list.begin()).copyTo(*this); +} + +template inline +Mat::Mat(const std::initializer_list sizes, const std::initializer_list<_Tp> list) + : Mat() +{ + size_t size_total = 1; + for(auto s : sizes) + size_total *= s; + CV_Assert(list.size() != 0); + CV_Assert(size_total == list.size()); + Mat((int)sizes.size(), (int*)sizes.begin(), traits::Type<_Tp>::value, (uchar*)list.begin()).copyTo(*this); +} +#endif + +#ifdef CV_CXX_STD_ARRAY +template inline +Mat::Mat(const std::array<_Tp, _Nm>& arr, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)arr.size()), + cols(1), data(0), datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if(arr.empty()) + return; + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)arr.data(); + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat((int)arr.size(), 1, traits::Type<_Tp>::value, (uchar*)arr.data()).copyTo(*this); +} +#endif + +template inline +Mat::Mat(const Vec<_Tp, n>& vec, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(n), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)vec.val; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat(n, 1, traits::Type<_Tp>::value, (void*)vec.val).copyTo(*this); +} + + +template inline +Mat::Mat(const Matx<_Tp,m,n>& M, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(m), cols(n), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = cols * sizeof(_Tp); + step[1] = sizeof(_Tp); + datastart = data = (uchar*)M.val; + datalimit = dataend = datastart + rows * step[0]; + } + else + Mat(m, n, traits::Type<_Tp>::value, (uchar*)M.val).copyTo(*this); +} + +template inline +Mat::Mat(const Point_<_Tp>& pt, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(2), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&pt.x; + datalimit = dataend = datastart + rows * step[0]; + } + else + { + create(2, 1, traits::Type<_Tp>::value); + ((_Tp*)data)[0] = pt.x; + ((_Tp*)data)[1] = pt.y; + } +} + +template inline +Mat::Mat(const Point3_<_Tp>& pt, bool copyData) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows(3), cols(1), data(0), + datastart(0), dataend(0), datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + if( !copyData ) + { + step[0] = step[1] = sizeof(_Tp); + datastart = data = (uchar*)&pt.x; + datalimit = dataend = datastart + rows * step[0]; + } + else + { + create(3, 1, traits::Type<_Tp>::value); + ((_Tp*)data)[0] = pt.x; + ((_Tp*)data)[1] = pt.y; + ((_Tp*)data)[2] = pt.z; + } +} + +template inline +Mat::Mat(const MatCommaInitializer_<_Tp>& commaInitializer) + : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(0), rows(0), cols(0), data(0), + datastart(0), dataend(0), allocator(0), u(0), size(&rows) +{ + *this = commaInitializer.operator Mat_<_Tp>(); +} + +inline +Mat Mat::row(int y) const +{ + return Mat(*this, Range(y, y + 1), Range::all()); +} + +inline +Mat Mat::col(int x) const +{ + return Mat(*this, Range::all(), Range(x, x + 1)); +} + +inline +Mat Mat::rowRange(int startrow, int endrow) const +{ + return Mat(*this, Range(startrow, endrow), Range::all()); +} + +inline +Mat Mat::rowRange(const Range& r) const +{ + return Mat(*this, r, Range::all()); +} + +inline +Mat Mat::colRange(int startcol, int endcol) const +{ + return Mat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +Mat Mat::colRange(const Range& r) const +{ + return Mat(*this, Range::all(), r); +} + +inline +Mat Mat::operator()( Range _rowRange, Range _colRange ) const +{ + return Mat(*this, _rowRange, _colRange); +} + +inline +Mat Mat::operator()( const Rect& roi ) const +{ + return Mat(*this, roi); +} + +inline +Mat Mat::operator()(const Range* ranges) const +{ + return Mat(*this, ranges); +} + +inline +Mat Mat::operator()(const std::vector& ranges) const +{ + return Mat(*this, ranges); +} + +inline +bool Mat::isContinuous() const +{ + return (flags & CONTINUOUS_FLAG) != 0; +} + +inline +bool Mat::isSubmatrix() const +{ + return (flags & SUBMATRIX_FLAG) != 0; +} + +inline +size_t Mat::elemSize() const +{ + size_t res = dims > 0 ? step.p[dims - 1] : 0; + CV_DbgAssert(res != 0); + return res; +} + +inline +size_t Mat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int Mat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int Mat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int Mat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +uchar* Mat::ptr(int y) +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return data + step.p[0] * y; +} + +inline +const uchar* Mat::ptr(int y) const +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return data + step.p[0] * y; +} + +template inline +_Tp* Mat::ptr(int y) +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return (_Tp*)(data + step.p[0] * y); +} + +template inline +const _Tp* Mat::ptr(int y) const +{ + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); + return (const _Tp*)(data + step.p[0] * y); +} + +inline +uchar* Mat::ptr(int i0, int i1) +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return data + i0 * step.p[0] + i1 * step.p[1]; +} + +inline +const uchar* Mat::ptr(int i0, int i1) const +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return data + i0 * step.p[0] + i1 * step.p[1]; +} + +template inline +_Tp* Mat::ptr(int i0, int i1) +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return (_Tp*)(data + i0 * step.p[0] + i1 * step.p[1]); +} + +template inline +const _Tp* Mat::ptr(int i0, int i1) const +{ + CV_DbgAssert(dims >= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + return (const _Tp*)(data + i0 * step.p[0] + i1 * step.p[1]); +} + +inline +uchar* Mat::ptr(int i0, int i1, int i2) +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]; +} + +inline +const uchar* Mat::ptr(int i0, int i1, int i2) const +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]; +} + +template inline +_Tp* Mat::ptr(int i0, int i1, int i2) +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return (_Tp*)(data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]); +} + +template inline +const _Tp* Mat::ptr(int i0, int i1, int i2) const +{ + CV_DbgAssert(dims >= 3); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert((unsigned)i2 < (unsigned)size.p[2]); + return (const _Tp*)(data + i0 * step.p[0] + i1 * step.p[1] + i2 * step.p[2]); +} + +inline +uchar* Mat::ptr(const int* idx) +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return p; +} + +inline +const uchar* Mat::ptr(const int* idx) const +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return p; +} + +template inline +_Tp* Mat::ptr(const int* idx) +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return (_Tp*)p; +} + +template inline +const _Tp* Mat::ptr(const int* idx) const +{ + int i, d = dims; + uchar* p = data; + CV_DbgAssert( d >= 1 && p ); + for( i = 0; i < d; i++ ) + { + CV_DbgAssert( (unsigned)idx[i] < (unsigned)size.p[i] ); + p += idx[i] * step.p[i]; + } + return (const _Tp*)p; +} + +template inline +_Tp& Mat::at(int i0, int i1) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((_Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +const _Tp& Mat::at(int i0, int i1) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((const _Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +_Tp& Mat::at(Point pt) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(pt.x * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((_Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +const _Tp& Mat::at(Point pt) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)(pt.x * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels())); + CV_DbgAssert(CV_ELEM_SIZE1(traits::Depth<_Tp>::value) == elemSize1()); + return ((const _Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +_Tp& Mat::at(int i0) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)(size.p[0] * size.p[1])); + CV_DbgAssert(elemSize() == sizeof(_Tp)); + if( isContinuous() || size.p[0] == 1 ) + return ((_Tp*)data)[i0]; + if( size.p[1] == 1 ) + return *(_Tp*)(data + step.p[0] * i0); + int i = i0 / cols, j = i0 - i * cols; + return ((_Tp*)(data + step.p[0] * i))[j]; +} + +template inline +const _Tp& Mat::at(int i0) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)(size.p[0] * size.p[1])); + CV_DbgAssert(elemSize() == sizeof(_Tp)); + if( isContinuous() || size.p[0] == 1 ) + return ((const _Tp*)data)[i0]; + if( size.p[1] == 1 ) + return *(const _Tp*)(data + step.p[0] * i0); + int i = i0 / cols, j = i0 - i * cols; + return ((const _Tp*)(data + step.p[0] * i))[j]; +} + +template inline +_Tp& Mat::at(int i0, int i1, int i2) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(i0, i1, i2); +} + +template inline +const _Tp& Mat::at(int i0, int i1, int i2) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(i0, i1, i2); +} + +template inline +_Tp& Mat::at(const int* idx) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(idx); +} + +template inline +const _Tp& Mat::at(const int* idx) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(idx); +} + +template inline +_Tp& Mat::at(const Vec& idx) +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(_Tp*)ptr(idx.val); +} + +template inline +const _Tp& Mat::at(const Vec& idx) const +{ + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return *(const _Tp*)ptr(idx.val); +} + +template inline +MatConstIterator_<_Tp> Mat::begin() const +{ + if (empty()) + return MatConstIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return MatConstIterator_<_Tp>((const Mat_<_Tp>*)this); +} + +template inline +MatConstIterator_<_Tp> Mat::end() const +{ + if (empty()) + return MatConstIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatConstIterator_<_Tp> it((const Mat_<_Tp>*)this); + it += total(); + return it; +} + +template inline +MatIterator_<_Tp> Mat::begin() +{ + if (empty()) + return MatIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return MatIterator_<_Tp>((Mat_<_Tp>*)this); +} + +template inline +MatIterator_<_Tp> Mat::end() +{ + if (empty()) + return MatIterator_<_Tp>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatIterator_<_Tp> it((Mat_<_Tp>*)this); + it += total(); + return it; +} + +template inline +void Mat::forEach(const Functor& operation) { + this->forEach_impl<_Tp>(operation); +} + +template inline +void Mat::forEach(const Functor& operation) const { + // call as not const + (const_cast(this))->forEach<_Tp>(operation); +} + +template inline +Mat::operator std::vector<_Tp>() const +{ + std::vector<_Tp> v; + copyTo(v); + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +Mat::operator std::array<_Tp, _Nm>() const +{ + std::array<_Tp, _Nm> v; + copyTo(v); + return v; +} +#endif + +template inline +Mat::operator Vec<_Tp, n>() const +{ + CV_Assert( data && dims <= 2 && (rows == 1 || cols == 1) && + rows + cols - 1 == n && channels() == 1 ); + + if( isContinuous() && type() == traits::Type<_Tp>::value ) + return Vec<_Tp, n>((_Tp*)data); + Vec<_Tp, n> v; + Mat tmp(rows, cols, traits::Type<_Tp>::value, v.val); + convertTo(tmp, tmp.type()); + return v; +} + +template inline +Mat::operator Matx<_Tp, m, n>() const +{ + CV_Assert( data && dims <= 2 && rows == m && cols == n && channels() == 1 ); + + if( isContinuous() && type() == traits::Type<_Tp>::value ) + return Matx<_Tp, m, n>((_Tp*)data); + Matx<_Tp, m, n> mtx; + Mat tmp(rows, cols, traits::Type<_Tp>::value, mtx.val); + convertTo(tmp, tmp.type()); + return mtx; +} + +template inline +void Mat::push_back(const _Tp& elem) +{ + if( !data ) + { + *this = Mat(1, 1, traits::Type<_Tp>::value, (void*)&elem).clone(); + return; + } + CV_Assert(traits::Type<_Tp>::value == type() && cols == 1 + /* && dims == 2 (cols == 1 implies dims == 2) */); + const uchar* tmp = dataend + step[0]; + if( !isSubmatrix() && isContinuous() && tmp <= datalimit ) + { + *(_Tp*)(data + (size.p[0]++) * step.p[0]) = elem; + dataend = tmp; + } + else + push_back_(&elem); +} + +template inline +void Mat::push_back(const Mat_<_Tp>& m) +{ + push_back((const Mat&)m); +} + +template<> inline +void Mat::push_back(const MatExpr& expr) +{ + push_back(static_cast(expr)); +} + + +template inline +void Mat::push_back(const std::vector<_Tp>& v) +{ + push_back(Mat(v)); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +inline +Mat::Mat(Mat&& m) + : flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), data(m.data), + datastart(m.datastart), dataend(m.dataend), datalimit(m.datalimit), allocator(m.allocator), + u(m.u), size(&rows) +{ + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.data = NULL; m.datastart = NULL; m.dataend = NULL; m.datalimit = NULL; + m.allocator = NULL; + m.u = NULL; +} + +inline +Mat& Mat::operator = (Mat&& m) +{ + if (this == &m) + return *this; + + release(); + flags = m.flags; dims = m.dims; rows = m.rows; cols = m.cols; data = m.data; + datastart = m.datastart; dataend = m.dataend; datalimit = m.datalimit; allocator = m.allocator; + u = m.u; + if (step.p != step.buf) // release self step/size + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.data = NULL; m.datastart = NULL; m.dataend = NULL; m.datalimit = NULL; + m.allocator = NULL; + m.u = NULL; + return *this; +} + +#endif + + +///////////////////////////// MatSize //////////////////////////// + +inline +MatSize::MatSize(int* _p) CV_NOEXCEPT + : p(_p) {} + +inline +int MatSize::dims() const CV_NOEXCEPT +{ + return (p - 1)[0]; +} + +inline +Size MatSize::operator()() const +{ + CV_DbgAssert(dims() <= 2); + return Size(p[1], p[0]); +} + +inline +const int& MatSize::operator[](int i) const +{ + CV_DbgAssert(i < dims()); +#ifdef __OPENCV_BUILD + CV_DbgAssert(i >= 0); +#endif + return p[i]; +} + +inline +int& MatSize::operator[](int i) +{ + CV_DbgAssert(i < dims()); +#ifdef __OPENCV_BUILD + CV_DbgAssert(i >= 0); +#endif + return p[i]; +} + +inline +MatSize::operator const int*() const CV_NOEXCEPT +{ + return p; +} + +inline +bool MatSize::operator != (const MatSize& sz) const CV_NOEXCEPT +{ + return !(*this == sz); +} + + + +///////////////////////////// MatStep //////////////////////////// + +inline +MatStep::MatStep() CV_NOEXCEPT +{ + p = buf; p[0] = p[1] = 0; +} + +inline +MatStep::MatStep(size_t s) CV_NOEXCEPT +{ + p = buf; p[0] = s; p[1] = 0; +} + +inline +const size_t& MatStep::operator[](int i) const CV_NOEXCEPT +{ + return p[i]; +} + +inline +size_t& MatStep::operator[](int i) CV_NOEXCEPT +{ + return p[i]; +} + +inline MatStep::operator size_t() const +{ + CV_DbgAssert( p == buf ); + return buf[0]; +} + +inline MatStep& MatStep::operator = (size_t s) +{ + CV_DbgAssert( p == buf ); + buf[0] = s; + return *this; +} + + + +////////////////////////////// Mat_<_Tp> //////////////////////////// + +template inline +Mat_<_Tp>::Mat_() CV_NOEXCEPT + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; +} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols) + : Mat(_rows, _cols, traits::Type<_Tp>::value) +{ +} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols, const _Tp& value) + : Mat(_rows, _cols, traits::Type<_Tp>::value) +{ + *this = value; +} + +template inline +Mat_<_Tp>::Mat_(Size _sz) + : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) +{} + +template inline +Mat_<_Tp>::Mat_(Size _sz, const _Tp& value) + : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) +{ + *this = value; +} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz) + : Mat(_dims, _sz, traits::Type<_Tp>::value) +{} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz, const _Tp& _s) + : Mat(_dims, _sz, traits::Type<_Tp>::value, Scalar(_s)) +{} + +template inline +Mat_<_Tp>::Mat_(int _dims, const int* _sz, _Tp* _data, const size_t* _steps) + : Mat(_dims, _sz, traits::Type<_Tp>::value, _data, _steps) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_<_Tp>& m, const Range* ranges) + : Mat(m, ranges) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_<_Tp>& m, const std::vector& ranges) + : Mat(m, ranges) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat& m) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = m; +} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m) + : Mat(m) +{} + +template inline +Mat_<_Tp>::Mat_(int _rows, int _cols, _Tp* _data, size_t steps) + : Mat(_rows, _cols, traits::Type<_Tp>::value, _data, steps) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m, const Range& _rowRange, const Range& _colRange) + : Mat(m, _rowRange, _colRange) +{} + +template inline +Mat_<_Tp>::Mat_(const Mat_& m, const Rect& roi) + : Mat(m, roi) +{} + +template template inline +Mat_<_Tp>::Mat_(const Vec::channel_type, n>& vec, bool copyData) + : Mat(n / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&vec) +{ + CV_Assert(n%DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template template inline +Mat_<_Tp>::Mat_(const Matx::channel_type, m, n>& M, bool copyData) + : Mat(m, n / DataType<_Tp>::channels, traits::Type<_Tp>::value, (void*)&M) +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const Point_::channel_type>& pt, bool copyData) + : Mat(2 / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&pt) +{ + CV_Assert(2 % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const Point3_::channel_type>& pt, bool copyData) + : Mat(3 / DataType<_Tp>::channels, 1, traits::Type<_Tp>::value, (void*)&pt) +{ + CV_Assert(3 % DataType<_Tp>::channels == 0); + if( copyData ) + *this = clone(); +} + +template inline +Mat_<_Tp>::Mat_(const MatCommaInitializer_<_Tp>& commaInitializer) + : Mat(commaInitializer) +{} + +template inline +Mat_<_Tp>::Mat_(const std::vector<_Tp>& vec, bool copyData) + : Mat(vec, copyData) +{} + +#ifdef CV_CXX11 +template inline +Mat_<_Tp>::Mat_(std::initializer_list<_Tp> list) + : Mat(list) +{} + +template inline +Mat_<_Tp>::Mat_(const std::initializer_list sizes, std::initializer_list<_Tp> list) + : Mat(sizes, list) +{} +#endif + +#ifdef CV_CXX_STD_ARRAY +template template inline +Mat_<_Tp>::Mat_(const std::array<_Tp, _Nm>& arr, bool copyData) + : Mat(arr, copyData) +{} +#endif + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const Mat& m) +{ + if (m.empty()) + { + release(); + return *this; + } + if( traits::Type<_Tp>::value == m.type() ) + { + Mat::operator = (m); + return *this; + } + if( traits::Depth<_Tp>::value == m.depth() ) + { + return (*this = m.reshape(DataType<_Tp>::channels, m.dims, 0)); + } + CV_Assert(DataType<_Tp>::channels == m.channels() || m.empty()); + m.convertTo(*this, type()); + return *this; +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const Mat_& m) +{ + Mat::operator=(m); + return *this; +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const _Tp& s) +{ + typedef typename DataType<_Tp>::vec_type VT; + Mat::operator=(Scalar((const VT&)s)); + return *this; +} + +template inline +void Mat_<_Tp>::create(int _rows, int _cols) +{ + Mat::create(_rows, _cols, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::create(Size _sz) +{ + Mat::create(_sz, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::create(int _dims, const int* _sz) +{ + Mat::create(_dims, _sz, traits::Type<_Tp>::value); +} + +template inline +void Mat_<_Tp>::release() +{ + Mat::release(); + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; +} + +template inline +Mat_<_Tp> Mat_<_Tp>::cross(const Mat_& m) const +{ + return Mat_<_Tp>(Mat::cross(m)); +} + +template template inline +Mat_<_Tp>::operator Mat_() const +{ + return Mat_(static_cast(*this)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::row(int y) const +{ + return Mat_(*this, Range(y, y+1), Range::all()); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::col(int x) const +{ + return Mat_(*this, Range::all(), Range(x, x+1)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::diag(int d) const +{ + return Mat_(Mat::diag(d)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::clone() const +{ + return Mat_(Mat::clone()); +} + +template inline +size_t Mat_<_Tp>::elemSize() const +{ + CV_DbgAssert( Mat::elemSize() == sizeof(_Tp) ); + return sizeof(_Tp); +} + +template inline +size_t Mat_<_Tp>::elemSize1() const +{ + CV_DbgAssert( Mat::elemSize1() == sizeof(_Tp) / DataType<_Tp>::channels ); + return sizeof(_Tp) / DataType<_Tp>::channels; +} + +template inline +int Mat_<_Tp>::type() const +{ + CV_DbgAssert( Mat::type() == traits::Type<_Tp>::value ); + return traits::Type<_Tp>::value; +} + +template inline +int Mat_<_Tp>::depth() const +{ + CV_DbgAssert( Mat::depth() == traits::Depth<_Tp>::value ); + return traits::Depth<_Tp>::value; +} + +template inline +int Mat_<_Tp>::channels() const +{ + CV_DbgAssert( Mat::channels() == DataType<_Tp>::channels ); + return DataType<_Tp>::channels; +} + +template inline +size_t Mat_<_Tp>::stepT(int i) const +{ + return step.p[i] / elemSize(); +} + +template inline +size_t Mat_<_Tp>::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::adjustROI( int dtop, int dbottom, int dleft, int dright ) +{ + return (Mat_<_Tp>&)(Mat::adjustROI(dtop, dbottom, dleft, dright)); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Range& _rowRange, const Range& _colRange ) const +{ + return Mat_<_Tp>(*this, _rowRange, _colRange); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Rect& roi ) const +{ + return Mat_<_Tp>(*this, roi); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()( const Range* ranges ) const +{ + return Mat_<_Tp>(*this, ranges); +} + +template inline +Mat_<_Tp> Mat_<_Tp>::operator()(const std::vector& ranges) const +{ + return Mat_<_Tp>(*this, ranges); +} + +template inline +_Tp* Mat_<_Tp>::operator [](int y) +{ + CV_DbgAssert( 0 <= y && y < size.p[0] ); + return (_Tp*)(data + y*step.p[0]); +} + +template inline +const _Tp* Mat_<_Tp>::operator [](int y) const +{ + CV_DbgAssert( 0 <= y && y < size.p[0] ); + return (const _Tp*)(data + y*step.p[0]); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0, int i1) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((_Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0, int i1) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((const _Tp*)(data + step.p[0] * i0))[i1]; +} + +template inline +_Tp& Mat_<_Tp>::operator ()(Point pt) +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)pt.x < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((_Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(Point pt) const +{ + CV_DbgAssert(dims <= 2); + CV_DbgAssert(data); + CV_DbgAssert((unsigned)pt.y < (unsigned)size.p[0]); + CV_DbgAssert((unsigned)pt.x < (unsigned)size.p[1]); + CV_DbgAssert(type() == traits::Type<_Tp>::value); + return ((const _Tp*)(data + step.p[0] * pt.y))[pt.x]; +} + +template inline +_Tp& Mat_<_Tp>::operator ()(const int* idx) +{ + return Mat::at<_Tp>(idx); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(const int* idx) const +{ + return Mat::at<_Tp>(idx); +} + +template template inline +_Tp& Mat_<_Tp>::operator ()(const Vec& idx) +{ + return Mat::at<_Tp>(idx); +} + +template template inline +const _Tp& Mat_<_Tp>::operator ()(const Vec& idx) const +{ + return Mat::at<_Tp>(idx); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0) +{ + return this->at<_Tp>(i0); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0) const +{ + return this->at<_Tp>(i0); +} + +template inline +_Tp& Mat_<_Tp>::operator ()(int i0, int i1, int i2) +{ + return this->at<_Tp>(i0, i1, i2); +} + +template inline +const _Tp& Mat_<_Tp>::operator ()(int i0, int i1, int i2) const +{ + return this->at<_Tp>(i0, i1, i2); +} + +template inline +Mat_<_Tp>::operator std::vector<_Tp>() const +{ + std::vector<_Tp> v; + copyTo(v); + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template template inline +Mat_<_Tp>::operator std::array<_Tp, _Nm>() const +{ + std::array<_Tp, _Nm> a; + copyTo(a); + return a; +} +#endif + +template template inline +Mat_<_Tp>::operator Vec::channel_type, n>() const +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + return pMat->operator Vec::channel_type, n>(); +#else + return this->Mat::operator Vec::channel_type, n>(); +#endif +} + +template template inline +Mat_<_Tp>::operator Matx::channel_type, m, n>() const +{ + CV_Assert(n % DataType<_Tp>::channels == 0); + +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + Matx::channel_type, m, n> res = pMat->operator Matx::channel_type, m, n>(); + return res; +#else + Matx::channel_type, m, n> res = this->Mat::operator Matx::channel_type, m, n>(); + return res; +#endif +} + +template inline +MatConstIterator_<_Tp> Mat_<_Tp>::begin() const +{ + return Mat::begin<_Tp>(); +} + +template inline +MatConstIterator_<_Tp> Mat_<_Tp>::end() const +{ + return Mat::end<_Tp>(); +} + +template inline +MatIterator_<_Tp> Mat_<_Tp>::begin() +{ + return Mat::begin<_Tp>(); +} + +template inline +MatIterator_<_Tp> Mat_<_Tp>::end() +{ + return Mat::end<_Tp>(); +} + +template template inline +void Mat_<_Tp>::forEach(const Functor& operation) { + Mat::forEach<_Tp, Functor>(operation); +} + +template template inline +void Mat_<_Tp>::forEach(const Functor& operation) const { + Mat::forEach<_Tp, Functor>(operation); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +template inline +Mat_<_Tp>::Mat_(Mat_&& m) + : Mat(std::move(m)) +{ +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (Mat_&& m) +{ + Mat::operator = (std::move(m)); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(Mat&& m) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = std::move(m); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (Mat&& m) +{ + if (m.empty()) + { + release(); + return *this; + } + if( traits::Type<_Tp>::value == m.type() ) + { + Mat::operator = ((Mat&&)m); + return *this; + } + if( traits::Depth<_Tp>::value == m.depth() ) + { + Mat::operator = ((Mat&&)m.reshape(DataType<_Tp>::channels, m.dims, 0)); + return *this; + } + CV_DbgAssert(DataType<_Tp>::channels == m.channels()); + m.convertTo(*this, type()); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(MatExpr&& e) + : Mat() +{ + flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; + *this = Mat(e); +} + +#endif + +///////////////////////////// SparseMat ///////////////////////////// + +inline +SparseMat SparseMat::clone() const +{ + SparseMat temp; + this->copyTo(temp); + return temp; +} + +inline +size_t SparseMat::elemSize() const +{ + return CV_ELEM_SIZE(flags); +} + +inline +size_t SparseMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int SparseMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int SparseMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int SparseMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +const int* SparseMat::size() const +{ + return hdr ? hdr->size : 0; +} + +inline +int SparseMat::size(int i) const +{ + if( hdr ) + { + CV_DbgAssert((unsigned)i < (unsigned)hdr->dims); + return hdr->size[i]; + } + return 0; +} + +inline +int SparseMat::dims() const +{ + return hdr ? hdr->dims : 0; +} + +inline +size_t SparseMat::nzcount() const +{ + return hdr ? hdr->nodeCount : 0; +} + +template inline +_Tp& SparseMat::ref(int i0, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, true, hashval); +} + +template inline +_Tp& SparseMat::ref(int i0, int i1, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, i1, true, hashval); +} + +template inline +_Tp& SparseMat::ref(int i0, int i1, int i2, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(i0, i1, i2, true, hashval); +} + +template inline +_Tp& SparseMat::ref(const int* idx, size_t* hashval) +{ + return *(_Tp*)((SparseMat*)this)->ptr(idx, true, hashval); +} + +template inline +_Tp SparseMat::value(int i0, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(int i0, int i1, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, i1, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(int i0, int i1, int i2, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(i0, i1, i2, false, hashval); + return p ? *p : _Tp(); +} + +template inline +_Tp SparseMat::value(const int* idx, size_t* hashval) const +{ + const _Tp* p = (const _Tp*)((SparseMat*)this)->ptr(idx, false, hashval); + return p ? *p : _Tp(); +} + +template inline +const _Tp* SparseMat::find(int i0, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, false, hashval); +} + +template inline +const _Tp* SparseMat::find(int i0, int i1, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, i1, false, hashval); +} + +template inline +const _Tp* SparseMat::find(int i0, int i1, int i2, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(i0, i1, i2, false, hashval); +} + +template inline +const _Tp* SparseMat::find(const int* idx, size_t* hashval) const +{ + return (const _Tp*)((SparseMat*)this)->ptr(idx, false, hashval); +} + +template inline +_Tp& SparseMat::value(Node* n) +{ + return *(_Tp*)((uchar*)n + hdr->valueOffset); +} + +template inline +const _Tp& SparseMat::value(const Node* n) const +{ + return *(const _Tp*)((const uchar*)n + hdr->valueOffset); +} + +inline +SparseMat::Node* SparseMat::node(size_t nidx) +{ + return (Node*)(void*)&hdr->pool[nidx]; +} + +inline +const SparseMat::Node* SparseMat::node(size_t nidx) const +{ + return (const Node*)(const void*)&hdr->pool[nidx]; +} + +inline +SparseMatIterator SparseMat::begin() +{ + return SparseMatIterator(this); +} + +inline +SparseMatConstIterator SparseMat::begin() const +{ + return SparseMatConstIterator(this); +} + +inline +SparseMatIterator SparseMat::end() +{ + SparseMatIterator it(this); + it.seekEnd(); + return it; +} + +inline +SparseMatConstIterator SparseMat::end() const +{ + SparseMatConstIterator it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatIterator_<_Tp> SparseMat::begin() +{ + return SparseMatIterator_<_Tp>(this); +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat::begin() const +{ + return SparseMatConstIterator_<_Tp>(this); +} + +template inline +SparseMatIterator_<_Tp> SparseMat::end() +{ + SparseMatIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat::end() const +{ + SparseMatConstIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + + + +///////////////////////////// SparseMat_ //////////////////////////// + +template inline +SparseMat_<_Tp>::SparseMat_() +{ + flags = MAGIC_VAL | traits::Type<_Tp>::value; +} + +template inline +SparseMat_<_Tp>::SparseMat_(int _dims, const int* _sizes) + : SparseMat(_dims, _sizes, traits::Type<_Tp>::value) +{} + +template inline +SparseMat_<_Tp>::SparseMat_(const SparseMat& m) +{ + if( m.type() == traits::Type<_Tp>::value ) + *this = (const SparseMat_<_Tp>&)m; + else + m.convertTo(*this, traits::Type<_Tp>::value); +} + +template inline +SparseMat_<_Tp>::SparseMat_(const SparseMat_<_Tp>& m) +{ + this->flags = m.flags; + this->hdr = m.hdr; + if( this->hdr ) + CV_XADD(&this->hdr->refcount, 1); +} + +template inline +SparseMat_<_Tp>::SparseMat_(const Mat& m) +{ + SparseMat sm(m); + *this = sm; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const SparseMat_<_Tp>& m) +{ + if( this != &m ) + { + if( m.hdr ) CV_XADD(&m.hdr->refcount, 1); + release(); + flags = m.flags; + hdr = m.hdr; + } + return *this; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const SparseMat& m) +{ + if( m.type() == traits::Type<_Tp>::value ) + return (*this = (const SparseMat_<_Tp>&)m); + m.convertTo(*this, traits::Type<_Tp>::value); + return *this; +} + +template inline +SparseMat_<_Tp>& SparseMat_<_Tp>::operator = (const Mat& m) +{ + return (*this = SparseMat(m)); +} + +template inline +SparseMat_<_Tp> SparseMat_<_Tp>::clone() const +{ + SparseMat_<_Tp> m; + this->copyTo(m); + return m; +} + +template inline +void SparseMat_<_Tp>::create(int _dims, const int* _sizes) +{ + SparseMat::create(_dims, _sizes, traits::Type<_Tp>::value); +} + +template inline +int SparseMat_<_Tp>::type() const +{ + return traits::Type<_Tp>::value; +} + +template inline +int SparseMat_<_Tp>::depth() const +{ + return traits::Depth<_Tp>::value; +} + +template inline +int SparseMat_<_Tp>::channels() const +{ + return DataType<_Tp>::channels; +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, int i1, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, i1, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, int i1, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, i1, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(int i0, int i1, int i2, size_t* hashval) +{ + return SparseMat::ref<_Tp>(i0, i1, i2, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(int i0, int i1, int i2, size_t* hashval) const +{ + return SparseMat::value<_Tp>(i0, i1, i2, hashval); +} + +template inline +_Tp& SparseMat_<_Tp>::ref(const int* idx, size_t* hashval) +{ + return SparseMat::ref<_Tp>(idx, hashval); +} + +template inline +_Tp SparseMat_<_Tp>::operator()(const int* idx, size_t* hashval) const +{ + return SparseMat::value<_Tp>(idx, hashval); +} + +template inline +SparseMatIterator_<_Tp> SparseMat_<_Tp>::begin() +{ + return SparseMatIterator_<_Tp>(this); +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat_<_Tp>::begin() const +{ + return SparseMatConstIterator_<_Tp>(this); +} + +template inline +SparseMatIterator_<_Tp> SparseMat_<_Tp>::end() +{ + SparseMatIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMat_<_Tp>::end() const +{ + SparseMatConstIterator_<_Tp> it(this); + it.seekEnd(); + return it; +} + + + +////////////////////////// MatConstIterator ///////////////////////// + +inline +MatConstIterator::MatConstIterator() + : m(0), elemSize(0), ptr(0), sliceStart(0), sliceEnd(0) +{} + +inline +MatConstIterator::MatConstIterator(const Mat* _m) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + if( m && m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + seek((const int*)0); +} + +inline +MatConstIterator::MatConstIterator(const Mat* _m, int _row, int _col) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + CV_Assert(m && m->dims <= 2); + if( m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + int idx[] = {_row, _col}; + seek(idx); +} + +inline +MatConstIterator::MatConstIterator(const Mat* _m, Point _pt) + : m(_m), elemSize(_m->elemSize()), ptr(0), sliceStart(0), sliceEnd(0) +{ + CV_Assert(m && m->dims <= 2); + if( m->isContinuous() ) + { + CV_Assert(!m->empty()); + sliceStart = m->ptr(); + sliceEnd = sliceStart + m->total()*elemSize; + } + int idx[] = {_pt.y, _pt.x}; + seek(idx); +} + +inline +MatConstIterator::MatConstIterator(const MatConstIterator& it) + : m(it.m), elemSize(it.elemSize), ptr(it.ptr), sliceStart(it.sliceStart), sliceEnd(it.sliceEnd) +{} + +inline +MatConstIterator& MatConstIterator::operator = (const MatConstIterator& it ) +{ + m = it.m; elemSize = it.elemSize; ptr = it.ptr; + sliceStart = it.sliceStart; sliceEnd = it.sliceEnd; + return *this; +} + +inline +const uchar* MatConstIterator::operator *() const +{ + return ptr; +} + +inline MatConstIterator& MatConstIterator::operator += (ptrdiff_t ofs) +{ + if( !m || ofs == 0 ) + return *this; + ptrdiff_t ofsb = ofs*elemSize; + ptr += ofsb; + if( ptr < sliceStart || sliceEnd <= ptr ) + { + ptr -= ofsb; + seek(ofs, true); + } + return *this; +} + +inline +MatConstIterator& MatConstIterator::operator -= (ptrdiff_t ofs) +{ + return (*this += -ofs); +} + +inline +MatConstIterator& MatConstIterator::operator --() +{ + if( m && (ptr -= elemSize) < sliceStart ) + { + ptr += elemSize; + seek(-1, true); + } + return *this; +} + +inline +MatConstIterator MatConstIterator::operator --(int) +{ + MatConstIterator b = *this; + *this += -1; + return b; +} + +inline +MatConstIterator& MatConstIterator::operator ++() +{ + if( m && (ptr += elemSize) >= sliceEnd ) + { + ptr -= elemSize; + seek(1, true); + } + return *this; +} + +inline MatConstIterator MatConstIterator::operator ++(int) +{ + MatConstIterator b = *this; + *this += 1; + return b; +} + + +static inline +bool operator == (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +static inline +bool operator != (const MatConstIterator& a, const MatConstIterator& b) +{ + return !(a == b); +} + +static inline +bool operator < (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr < b.ptr; +} + +static inline +bool operator > (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr > b.ptr; +} + +static inline +bool operator <= (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr <= b.ptr; +} + +static inline +bool operator >= (const MatConstIterator& a, const MatConstIterator& b) +{ + return a.ptr >= b.ptr; +} + +static inline +ptrdiff_t operator - (const MatConstIterator& b, const MatConstIterator& a) +{ + if( a.m != b.m ) + return ((size_t)(-1) >> 1); + if( a.sliceEnd == b.sliceEnd ) + return (b.ptr - a.ptr)/static_cast(b.elemSize); + + return b.lpos() - a.lpos(); +} + +static inline +MatConstIterator operator + (const MatConstIterator& a, ptrdiff_t ofs) +{ + MatConstIterator b = a; + return b += ofs; +} + +static inline +MatConstIterator operator + (ptrdiff_t ofs, const MatConstIterator& a) +{ + MatConstIterator b = a; + return b += ofs; +} + +static inline +MatConstIterator operator - (const MatConstIterator& a, ptrdiff_t ofs) +{ + MatConstIterator b = a; + return b += -ofs; +} + + +inline +const uchar* MatConstIterator::operator [](ptrdiff_t i) const +{ + return *(*this + i); +} + + + +///////////////////////// MatConstIterator_ ///////////////////////// + +template inline +MatConstIterator_<_Tp>::MatConstIterator_() +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m) + : MatConstIterator(_m) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m, int _row, int _col) + : MatConstIterator(_m, _row, _col) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const Mat_<_Tp>* _m, Point _pt) + : MatConstIterator(_m, _pt) +{} + +template inline +MatConstIterator_<_Tp>::MatConstIterator_(const MatConstIterator_& it) + : MatConstIterator(it) +{} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator = (const MatConstIterator_& it ) +{ + MatConstIterator::operator = (it); + return *this; +} + +template inline +const _Tp& MatConstIterator_<_Tp>::operator *() const +{ + return *(_Tp*)(this->ptr); +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator += (ptrdiff_t ofs) +{ + MatConstIterator::operator += (ofs); + return *this; +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator -= (ptrdiff_t ofs) +{ + return (*this += -ofs); +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator --() +{ + MatConstIterator::operator --(); + return *this; +} + +template inline +MatConstIterator_<_Tp> MatConstIterator_<_Tp>::operator --(int) +{ + MatConstIterator_ b = *this; + MatConstIterator::operator --(); + return b; +} + +template inline +MatConstIterator_<_Tp>& MatConstIterator_<_Tp>::operator ++() +{ + MatConstIterator::operator ++(); + return *this; +} + +template inline +MatConstIterator_<_Tp> MatConstIterator_<_Tp>::operator ++(int) +{ + MatConstIterator_ b = *this; + MatConstIterator::operator ++(); + return b; +} + + +template inline +Point MatConstIterator_<_Tp>::pos() const +{ + if( !m ) + return Point(); + CV_DbgAssert( m->dims <= 2 ); + if( m->isContinuous() ) + { + ptrdiff_t ofs = (const _Tp*)ptr - (const _Tp*)m->data; + int y = (int)(ofs / m->cols); + int x = (int)(ofs - (ptrdiff_t)y * m->cols); + return Point(x, y); + } + else + { + ptrdiff_t ofs = (uchar*)ptr - m->data; + int y = (int)(ofs / m->step); + int x = (int)((ofs - y * m->step)/sizeof(_Tp)); + return Point(x, y); + } +} + + +template static inline +bool operator == (const MatConstIterator_<_Tp>& a, const MatConstIterator_<_Tp>& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +template static inline +bool operator != (const MatConstIterator_<_Tp>& a, const MatConstIterator_<_Tp>& b) +{ + return a.m != b.m || a.ptr != b.ptr; +} + +template static inline +MatConstIterator_<_Tp> operator + (const MatConstIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template static inline +MatConstIterator_<_Tp> operator + (ptrdiff_t ofs, const MatConstIterator_<_Tp>& a) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template static inline +MatConstIterator_<_Tp> operator - (const MatConstIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a - ofs; + return (MatConstIterator_<_Tp>&)t; +} + +template inline +const _Tp& MatConstIterator_<_Tp>::operator [](ptrdiff_t i) const +{ + return *(_Tp*)MatConstIterator::operator [](i); +} + + + +//////////////////////////// MatIterator_ /////////////////////////// + +template inline +MatIterator_<_Tp>::MatIterator_() + : MatConstIterator_<_Tp>() +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m) + : MatConstIterator_<_Tp>(_m) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, int _row, int _col) + : MatConstIterator_<_Tp>(_m, _row, _col) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, Point _pt) + : MatConstIterator_<_Tp>(_m, _pt) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(Mat_<_Tp>* _m, const int* _idx) + : MatConstIterator_<_Tp>(_m, _idx) +{} + +template inline +MatIterator_<_Tp>::MatIterator_(const MatIterator_& it) + : MatConstIterator_<_Tp>(it) +{} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator = (const MatIterator_<_Tp>& it ) +{ + MatConstIterator::operator = (it); + return *this; +} + +template inline +_Tp& MatIterator_<_Tp>::operator *() const +{ + return *(_Tp*)(this->ptr); +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator += (ptrdiff_t ofs) +{ + MatConstIterator::operator += (ofs); + return *this; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator -= (ptrdiff_t ofs) +{ + MatConstIterator::operator += (-ofs); + return *this; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator --() +{ + MatConstIterator::operator --(); + return *this; +} + +template inline +MatIterator_<_Tp> MatIterator_<_Tp>::operator --(int) +{ + MatIterator_ b = *this; + MatConstIterator::operator --(); + return b; +} + +template inline +MatIterator_<_Tp>& MatIterator_<_Tp>::operator ++() +{ + MatConstIterator::operator ++(); + return *this; +} + +template inline +MatIterator_<_Tp> MatIterator_<_Tp>::operator ++(int) +{ + MatIterator_ b = *this; + MatConstIterator::operator ++(); + return b; +} + +template inline +_Tp& MatIterator_<_Tp>::operator [](ptrdiff_t i) const +{ + return *(*this + i); +} + + +template static inline +bool operator == (const MatIterator_<_Tp>& a, const MatIterator_<_Tp>& b) +{ + return a.m == b.m && a.ptr == b.ptr; +} + +template static inline +bool operator != (const MatIterator_<_Tp>& a, const MatIterator_<_Tp>& b) +{ + return a.m != b.m || a.ptr != b.ptr; +} + +template static inline +MatIterator_<_Tp> operator + (const MatIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatIterator_<_Tp>&)t; +} + +template static inline +MatIterator_<_Tp> operator + (ptrdiff_t ofs, const MatIterator_<_Tp>& a) +{ + MatConstIterator t = (const MatConstIterator&)a + ofs; + return (MatIterator_<_Tp>&)t; +} + +template static inline +MatIterator_<_Tp> operator - (const MatIterator_<_Tp>& a, ptrdiff_t ofs) +{ + MatConstIterator t = (const MatConstIterator&)a - ofs; + return (MatIterator_<_Tp>&)t; +} + + + +/////////////////////// SparseMatConstIterator ////////////////////// + +inline +SparseMatConstIterator::SparseMatConstIterator() + : m(0), hashidx(0), ptr(0) +{} + +inline +SparseMatConstIterator::SparseMatConstIterator(const SparseMatConstIterator& it) + : m(it.m), hashidx(it.hashidx), ptr(it.ptr) +{} + +inline SparseMatConstIterator& SparseMatConstIterator::operator = (const SparseMatConstIterator& it) +{ + if( this != &it ) + { + m = it.m; + hashidx = it.hashidx; + ptr = it.ptr; + } + return *this; +} + +template inline +const _Tp& SparseMatConstIterator::value() const +{ + return *(const _Tp*)ptr; +} + +inline +const SparseMat::Node* SparseMatConstIterator::node() const +{ + return (ptr && m && m->hdr) ? (const SparseMat::Node*)(const void*)(ptr - m->hdr->valueOffset) : 0; +} + +inline +SparseMatConstIterator SparseMatConstIterator::operator ++(int) +{ + SparseMatConstIterator it = *this; + ++*this; + return it; +} + +inline +void SparseMatConstIterator::seekEnd() +{ + if( m && m->hdr ) + { + hashidx = m->hdr->hashtab.size(); + ptr = 0; + } +} + + +static inline +bool operator == (const SparseMatConstIterator& it1, const SparseMatConstIterator& it2) +{ + return it1.m == it2.m && it1.ptr == it2.ptr; +} + +static inline +bool operator != (const SparseMatConstIterator& it1, const SparseMatConstIterator& it2) +{ + return !(it1 == it2); +} + + + +///////////////////////// SparseMatIterator ///////////////////////// + +inline +SparseMatIterator::SparseMatIterator() +{} + +inline +SparseMatIterator::SparseMatIterator(SparseMat* _m) + : SparseMatConstIterator(_m) +{} + +inline +SparseMatIterator::SparseMatIterator(const SparseMatIterator& it) + : SparseMatConstIterator(it) +{} + +inline +SparseMatIterator& SparseMatIterator::operator = (const SparseMatIterator& it) +{ + (SparseMatConstIterator&)*this = it; + return *this; +} + +template inline +_Tp& SparseMatIterator::value() const +{ + return *(_Tp*)ptr; +} + +inline +SparseMat::Node* SparseMatIterator::node() const +{ + return (SparseMat::Node*)SparseMatConstIterator::node(); +} + +inline +SparseMatIterator& SparseMatIterator::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +inline +SparseMatIterator SparseMatIterator::operator ++(int) +{ + SparseMatIterator it = *this; + ++*this; + return it; +} + + + +////////////////////// SparseMatConstIterator_ ////////////////////// + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_() +{} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMat_<_Tp>* _m) + : SparseMatConstIterator(_m) +{} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMat* _m) + : SparseMatConstIterator(_m) +{ + CV_Assert( _m->type() == traits::Type<_Tp>::value ); +} + +template inline +SparseMatConstIterator_<_Tp>::SparseMatConstIterator_(const SparseMatConstIterator_<_Tp>& it) + : SparseMatConstIterator(it) +{} + +template inline +SparseMatConstIterator_<_Tp>& SparseMatConstIterator_<_Tp>::operator = (const SparseMatConstIterator_<_Tp>& it) +{ + return reinterpret_cast&> + (*reinterpret_cast(this) = + reinterpret_cast(it)); +} + +template inline +const _Tp& SparseMatConstIterator_<_Tp>::operator *() const +{ + return *(const _Tp*)this->ptr; +} + +template inline +SparseMatConstIterator_<_Tp>& SparseMatConstIterator_<_Tp>::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +template inline +SparseMatConstIterator_<_Tp> SparseMatConstIterator_<_Tp>::operator ++(int) +{ + SparseMatConstIterator_<_Tp> it = *this; + SparseMatConstIterator::operator ++(); + return it; +} + + + +///////////////////////// SparseMatIterator_ //////////////////////// + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_() +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(SparseMat_<_Tp>* _m) + : SparseMatConstIterator_<_Tp>(_m) +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(SparseMat* _m) + : SparseMatConstIterator_<_Tp>(_m) +{} + +template inline +SparseMatIterator_<_Tp>::SparseMatIterator_(const SparseMatIterator_<_Tp>& it) + : SparseMatConstIterator_<_Tp>(it) +{} + +template inline +SparseMatIterator_<_Tp>& SparseMatIterator_<_Tp>::operator = (const SparseMatIterator_<_Tp>& it) +{ + return reinterpret_cast&> + (*reinterpret_cast(this) = + reinterpret_cast(it)); +} + +template inline +_Tp& SparseMatIterator_<_Tp>::operator *() const +{ + return *(_Tp*)this->ptr; +} + +template inline +SparseMatIterator_<_Tp>& SparseMatIterator_<_Tp>::operator ++() +{ + SparseMatConstIterator::operator ++(); + return *this; +} + +template inline +SparseMatIterator_<_Tp> SparseMatIterator_<_Tp>::operator ++(int) +{ + SparseMatIterator_<_Tp> it = *this; + SparseMatConstIterator::operator ++(); + return it; +} + + + +//////////////////////// MatCommaInitializer_ /////////////////////// + +template inline +MatCommaInitializer_<_Tp>::MatCommaInitializer_(Mat_<_Tp>* _m) + : it(_m) +{} + +template template inline +MatCommaInitializer_<_Tp>& MatCommaInitializer_<_Tp>::operator , (T2 v) +{ + CV_DbgAssert( this->it < ((const Mat_<_Tp>*)this->it.m)->end() ); + *this->it = _Tp(v); + ++this->it; + return *this; +} + +template inline +MatCommaInitializer_<_Tp>::operator Mat_<_Tp>() const +{ + CV_DbgAssert( this->it == ((const Mat_<_Tp>*)this->it.m)->end() ); + return Mat_<_Tp>(*this->it.m); +} + + +template static inline +MatCommaInitializer_<_Tp> operator << (const Mat_<_Tp>& m, T2 val) +{ + MatCommaInitializer_<_Tp> commaInitializer((Mat_<_Tp>*)&m); + return (commaInitializer, val); +} + + + +///////////////////////// Matrix Expressions //////////////////////// + +inline +Mat& Mat::operator = (const MatExpr& e) +{ + e.op->assign(e, *this); + return *this; +} + +template inline +Mat_<_Tp>::Mat_(const MatExpr& e) +{ + e.op->assign(e, *this, traits::Type<_Tp>::value); +} + +template inline +Mat_<_Tp>& Mat_<_Tp>::operator = (const MatExpr& e) +{ + e.op->assign(e, *this, traits::Type<_Tp>::value); + return *this; +} + +template inline +MatExpr Mat_<_Tp>::zeros(int rows, int cols) +{ + return Mat::zeros(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::zeros(Size sz) +{ + return Mat::zeros(sz, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::ones(int rows, int cols) +{ + return Mat::ones(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::ones(Size sz) +{ + return Mat::ones(sz, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::eye(int rows, int cols) +{ + return Mat::eye(rows, cols, traits::Type<_Tp>::value); +} + +template inline +MatExpr Mat_<_Tp>::eye(Size sz) +{ + return Mat::eye(sz, traits::Type<_Tp>::value); +} + +inline +MatExpr::MatExpr() + : op(0), flags(0), a(Mat()), b(Mat()), c(Mat()), alpha(0), beta(0), s() +{} + +inline +MatExpr::MatExpr(const MatOp* _op, int _flags, const Mat& _a, const Mat& _b, + const Mat& _c, double _alpha, double _beta, const Scalar& _s) + : op(_op), flags(_flags), a(_a), b(_b), c(_c), alpha(_alpha), beta(_beta), s(_s) +{} + +inline +MatExpr::operator Mat() const +{ + Mat m; + op->assign(*this, m); + return m; +} + +template inline +MatExpr::operator Mat_<_Tp>() const +{ + Mat_<_Tp> m; + op->assign(*this, m, traits::Type<_Tp>::value); + return m; +} + + +template static inline +MatExpr min(const Mat_<_Tp>& a, const Mat_<_Tp>& b) +{ + return cv::min((const Mat&)a, (const Mat&)b); +} + +template static inline +MatExpr min(const Mat_<_Tp>& a, double s) +{ + return cv::min((const Mat&)a, s); +} + +template static inline +MatExpr min(double s, const Mat_<_Tp>& a) +{ + return cv::min((const Mat&)a, s); +} + +template static inline +MatExpr max(const Mat_<_Tp>& a, const Mat_<_Tp>& b) +{ + return cv::max((const Mat&)a, (const Mat&)b); +} + +template static inline +MatExpr max(const Mat_<_Tp>& a, double s) +{ + return cv::max((const Mat&)a, s); +} + +template static inline +MatExpr max(double s, const Mat_<_Tp>& a) +{ + return cv::max((const Mat&)a, s); +} + +template static inline +MatExpr abs(const Mat_<_Tp>& m) +{ + return cv::abs((const Mat&)m); +} + + +static inline +Mat& operator += (Mat& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, a); + return a; +} + +static inline +const Mat& operator += (const Mat& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator += (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator += (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignAdd(b, (Mat&)a); + return a; +} + +static inline +Mat& operator -= (Mat& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, a); + return a; +} + +static inline +const Mat& operator -= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator -= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator -= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignSubtract(b, (Mat&)a); + return a; +} + +static inline +Mat& operator *= (Mat& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, a); + return a; +} + +static inline +const Mat& operator *= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator *= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator *= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignMultiply(b, (Mat&)a); + return a; +} + +static inline +Mat& operator /= (Mat& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, a); + return a; +} + +static inline +const Mat& operator /= (const Mat& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, (Mat&)a); + return a; +} + +template static inline +Mat_<_Tp>& operator /= (Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, a); + return a; +} + +template static inline +const Mat_<_Tp>& operator /= (const Mat_<_Tp>& a, const MatExpr& b) +{ + b.op->augAssignDivide(b, (Mat&)a); + return a; +} + + +//////////////////////////////// UMat //////////////////////////////// + +template inline +UMat::UMat(const std::vector<_Tp>& vec, bool copyData) +: flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), +cols(1), allocator(0), usageFlags(USAGE_DEFAULT), u(0), offset(0), size(&rows) +{ + if(vec.empty()) + return; + if( !copyData ) + { + // !!!TODO!!! + CV_Error(Error::StsNotImplemented, ""); + } + else + Mat((int)vec.size(), 1, traits::Type<_Tp>::value, (uchar*)&vec[0]).copyTo(*this); +} + +inline +UMat UMat::row(int y) const +{ + return UMat(*this, Range(y, y + 1), Range::all()); +} + +inline +UMat UMat::col(int x) const +{ + return UMat(*this, Range::all(), Range(x, x + 1)); +} + +inline +UMat UMat::rowRange(int startrow, int endrow) const +{ + return UMat(*this, Range(startrow, endrow), Range::all()); +} + +inline +UMat UMat::rowRange(const Range& r) const +{ + return UMat(*this, r, Range::all()); +} + +inline +UMat UMat::colRange(int startcol, int endcol) const +{ + return UMat(*this, Range::all(), Range(startcol, endcol)); +} + +inline +UMat UMat::colRange(const Range& r) const +{ + return UMat(*this, Range::all(), r); +} + +inline +UMat UMat::operator()( Range _rowRange, Range _colRange ) const +{ + return UMat(*this, _rowRange, _colRange); +} + +inline +UMat UMat::operator()( const Rect& roi ) const +{ + return UMat(*this, roi); +} + +inline +UMat UMat::operator()(const Range* ranges) const +{ + return UMat(*this, ranges); +} + +inline +UMat UMat::operator()(const std::vector& ranges) const +{ + return UMat(*this, ranges); +} + +inline +bool UMat::isContinuous() const +{ + return (flags & CONTINUOUS_FLAG) != 0; +} + +inline +bool UMat::isSubmatrix() const +{ + return (flags & SUBMATRIX_FLAG) != 0; +} + +inline +size_t UMat::elemSize() const +{ + size_t res = dims > 0 ? step.p[dims - 1] : 0; + CV_DbgAssert(res != 0); + return res; +} + +inline +size_t UMat::elemSize1() const +{ + return CV_ELEM_SIZE1(flags); +} + +inline +int UMat::type() const +{ + return CV_MAT_TYPE(flags); +} + +inline +int UMat::depth() const +{ + return CV_MAT_DEPTH(flags); +} + +inline +int UMat::channels() const +{ + return CV_MAT_CN(flags); +} + +inline +size_t UMat::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +inline +UMat::UMat(UMat&& m) +: flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), allocator(m.allocator), + usageFlags(m.usageFlags), u(m.u), offset(m.offset), size(&rows) +{ + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.allocator = NULL; + m.u = NULL; + m.offset = 0; +} + +inline +UMat& UMat::operator = (UMat&& m) +{ + if (this == &m) + return *this; + release(); + flags = m.flags; dims = m.dims; rows = m.rows; cols = m.cols; + allocator = m.allocator; usageFlags = m.usageFlags; + u = m.u; + offset = m.offset; + if (step.p != step.buf) // release self step/size + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } + if (m.dims <= 2) // move new step/size info + { + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + { + CV_DbgAssert(m.step.p != m.step.buf); + step.p = m.step.p; + size.p = m.size.p; + m.step.p = m.step.buf; + m.size.p = &m.rows; + } + m.flags = MAGIC_VAL; m.dims = m.rows = m.cols = 0; + m.allocator = NULL; + m.u = NULL; + m.offset = 0; + return *this; +} + +#endif + + +inline bool UMatData::hostCopyObsolete() const { return (flags & HOST_COPY_OBSOLETE) != 0; } +inline bool UMatData::deviceCopyObsolete() const { return (flags & DEVICE_COPY_OBSOLETE) != 0; } +inline bool UMatData::deviceMemMapped() const { return (flags & DEVICE_MEM_MAPPED) != 0; } +inline bool UMatData::copyOnMap() const { return (flags & COPY_ON_MAP) != 0; } +inline bool UMatData::tempUMat() const { return (flags & TEMP_UMAT) != 0; } +inline bool UMatData::tempCopiedUMat() const { return (flags & TEMP_COPIED_UMAT) == TEMP_COPIED_UMAT; } + +inline void UMatData::markDeviceMemMapped(bool flag) +{ + if(flag) + flags |= DEVICE_MEM_MAPPED; + else + flags &= ~DEVICE_MEM_MAPPED; +} + +inline void UMatData::markHostCopyObsolete(bool flag) +{ + if(flag) + flags |= HOST_COPY_OBSOLETE; + else + flags &= ~HOST_COPY_OBSOLETE; +} +inline void UMatData::markDeviceCopyObsolete(bool flag) +{ + if(flag) + flags |= DEVICE_COPY_OBSOLETE; + else + flags &= ~DEVICE_COPY_OBSOLETE; +} + +//! @endcond + +static inline +void swap(MatExpr& a, MatExpr& b) { a.swap(b); } + +} //cv + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#ifdef CV_DISABLE_CLANG_ENUM_WARNINGS +#undef CV_DISABLE_CLANG_ENUM_WARNINGS +#pragma clang diagnostic pop +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/matx.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/matx.hpp new file mode 100644 index 0000000..733f675 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/matx.hpp @@ -0,0 +1,1518 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_MATX_HPP +#define OPENCV_CORE_MATX_HPP + +#ifndef __cplusplus +# error matx.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/base.hpp" +#include "opencv2/core/traits.hpp" +#include "opencv2/core/saturate.hpp" + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +////////////////////////////// Small Matrix /////////////////////////// + +//! @cond IGNORED +// FIXIT Remove this (especially CV_EXPORTS modifier) +struct CV_EXPORTS Matx_AddOp { Matx_AddOp() {} Matx_AddOp(const Matx_AddOp&) {} }; +struct CV_EXPORTS Matx_SubOp { Matx_SubOp() {} Matx_SubOp(const Matx_SubOp&) {} }; +struct CV_EXPORTS Matx_ScaleOp { Matx_ScaleOp() {} Matx_ScaleOp(const Matx_ScaleOp&) {} }; +struct CV_EXPORTS Matx_MulOp { Matx_MulOp() {} Matx_MulOp(const Matx_MulOp&) {} }; +struct CV_EXPORTS Matx_DivOp { Matx_DivOp() {} Matx_DivOp(const Matx_DivOp&) {} }; +struct CV_EXPORTS Matx_MatMulOp { Matx_MatMulOp() {} Matx_MatMulOp(const Matx_MatMulOp&) {} }; +struct CV_EXPORTS Matx_TOp { Matx_TOp() {} Matx_TOp(const Matx_TOp&) {} }; +//! @endcond + +/** @brief Template class for small matrices whose type and size are known at compilation time + +If you need a more flexible type, use Mat . The elements of the matrix M are accessible using the +M(i,j) notation. Most of the common matrix operations (see also @ref MatrixExpressions ) are +available. To do an operation on Matx that is not implemented, you can easily convert the matrix to +Mat and backwards: +@code{.cpp} + Matx33f m(1, 2, 3, + 4, 5, 6, + 7, 8, 9); + cout << sum(Mat(m*m.t())) << endl; +@endcode +Except of the plain constructor which takes a list of elements, Matx can be initialized from a C-array: +@code{.cpp} + float values[] = { 1, 2, 3}; + Matx31f m(values); +@endcode +In case if C++11 features are available, std::initializer_list can be also used to initialize Matx: +@code{.cpp} + Matx31f m = { 1, 2, 3}; +@endcode + */ +template class Matx +{ +public: + enum { + rows = m, + cols = n, + channels = rows*cols, +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = traits::Type<_Tp>::value, + type = CV_MAKETYPE(depth, channels), +#endif + shortdim = (m < n ? m : n) + }; + + typedef _Tp value_type; + typedef Matx<_Tp, m, n> mat_type; + typedef Matx<_Tp, shortdim, 1> diag_type; + + //! default constructor + Matx(); + + explicit Matx(_Tp v0); //!< 1x1 matrix + Matx(_Tp v0, _Tp v1); //!< 1x2 or 2x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2); //!< 1x3 or 3x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 1x4, 2x2 or 4x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 1x5 or 5x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 1x6, 2x3, 3x2 or 6x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 1x7 or 7x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 1x8, 2x4, 4x2 or 8x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 1x9, 3x3 or 9x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 1x10, 2x5 or 5x2 or 10x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11); //!< 1x12, 2x6, 3x4, 4x3, 6x2 or 12x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11, + _Tp v12, _Tp v13); //!< 1x14, 2x7, 7x2 or 14x1 matrix + Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, + _Tp v4, _Tp v5, _Tp v6, _Tp v7, + _Tp v8, _Tp v9, _Tp v10, _Tp v11, + _Tp v12, _Tp v13, _Tp v14, _Tp v15); //!< 1x16, 4x4 or 16x1 matrix + explicit Matx(const _Tp* vals); //!< initialize from a plain array + +#ifdef CV_CXX11 + Matx(std::initializer_list<_Tp>); //!< initialize from an initializer list +#endif + + static Matx all(_Tp alpha); + static Matx zeros(); + static Matx ones(); + static Matx eye(); + static Matx diag(const diag_type& d); + /** @brief Generates uniformly distributed random numbers + @param a Range boundary. + @param b The other range boundary (boundaries don't have to be ordered, the lower boundary is inclusive, + the upper one is exclusive). + */ + static Matx randu(_Tp a, _Tp b); + /** @brief Generates normally distributed random numbers + @param a Mean value. + @param b Standard deviation. + */ + static Matx randn(_Tp a, _Tp b); + + //! dot product computed with the default precision + _Tp dot(const Matx<_Tp, m, n>& v) const; + + //! dot product computed in double-precision arithmetics + double ddot(const Matx<_Tp, m, n>& v) const; + + //! conversion to another data type + template operator Matx() const; + + //! change the matrix shape + template Matx<_Tp, m1, n1> reshape() const; + + //! extract part of the matrix + template Matx<_Tp, m1, n1> get_minor(int base_row, int base_col) const; + + //! extract the matrix row + Matx<_Tp, 1, n> row(int i) const; + + //! extract the matrix column + Matx<_Tp, m, 1> col(int i) const; + + //! extract the matrix diagonal + diag_type diag() const; + + //! transpose the matrix + Matx<_Tp, n, m> t() const; + + //! invert the matrix + Matx<_Tp, n, m> inv(int method=DECOMP_LU, bool *p_is_ok = NULL) const; + + //! solve linear system + template Matx<_Tp, n, l> solve(const Matx<_Tp, m, l>& rhs, int flags=DECOMP_LU) const; + Vec<_Tp, n> solve(const Vec<_Tp, m>& rhs, int method) const; + + //! multiply two matrices element-wise + Matx<_Tp, m, n> mul(const Matx<_Tp, m, n>& a) const; + + //! divide two matrices element-wise + Matx<_Tp, m, n> div(const Matx<_Tp, m, n>& a) const; + + //! element access + const _Tp& operator ()(int row, int col) const; + _Tp& operator ()(int row, int col); + + //! 1D element access + const _Tp& operator ()(int i) const; + _Tp& operator ()(int i); + + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_AddOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_SubOp); + template Matx(const Matx<_Tp, m, n>& a, _T2 alpha, Matx_ScaleOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_MulOp); + Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_DivOp); + template Matx(const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b, Matx_MatMulOp); + Matx(const Matx<_Tp, n, m>& a, Matx_TOp); + + _Tp val[m*n]; //< matrix elements +}; + +typedef Matx Matx12f; +typedef Matx Matx12d; +typedef Matx Matx13f; +typedef Matx Matx13d; +typedef Matx Matx14f; +typedef Matx Matx14d; +typedef Matx Matx16f; +typedef Matx Matx16d; + +typedef Matx Matx21f; +typedef Matx Matx21d; +typedef Matx Matx31f; +typedef Matx Matx31d; +typedef Matx Matx41f; +typedef Matx Matx41d; +typedef Matx Matx61f; +typedef Matx Matx61d; + +typedef Matx Matx22f; +typedef Matx Matx22d; +typedef Matx Matx23f; +typedef Matx Matx23d; +typedef Matx Matx32f; +typedef Matx Matx32d; + +typedef Matx Matx33f; +typedef Matx Matx33d; + +typedef Matx Matx34f; +typedef Matx Matx34d; +typedef Matx Matx43f; +typedef Matx Matx43d; + +typedef Matx Matx44f; +typedef Matx Matx44d; +typedef Matx Matx66f; +typedef Matx Matx66d; + +/*! + traits +*/ +template class DataType< Matx<_Tp, m, n> > +{ +public: + typedef Matx<_Tp, m, n> value_type; + typedef Matx::work_type, m, n> work_type; + typedef _Tp channel_type; + typedef value_type vec_type; + + enum { generic_type = 0, + channels = m * n, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; +}; + +namespace traits { +template +struct Depth< Matx<_Tp, m, n> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Matx<_Tp, m, n> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, n*m) }; }; +} // namespace + + +/** @brief Comma-separated Matrix Initializer +*/ +template class MatxCommaInitializer +{ +public: + MatxCommaInitializer(Matx<_Tp, m, n>* _mtx); + template MatxCommaInitializer<_Tp, m, n>& operator , (T2 val); + Matx<_Tp, m, n> operator *() const; + + Matx<_Tp, m, n>* dst; + int idx; +}; + +/* + Utility methods +*/ +template static double determinant(const Matx<_Tp, m, m>& a); +template static double trace(const Matx<_Tp, m, n>& a); +template static double norm(const Matx<_Tp, m, n>& M); +template static double norm(const Matx<_Tp, m, n>& M, int normType); + + + +/////////////////////// Vec (used as element of multi-channel images ///////////////////// + +/** @brief Template class for short numerical vectors, a partial case of Matx + +This template class represents short numerical vectors (of 1, 2, 3, 4 ... elements) on which you +can perform basic arithmetical operations, access individual elements using [] operator etc. The +vectors are allocated on stack, as opposite to std::valarray, std::vector, cv::Mat etc., which +elements are dynamically allocated in the heap. + +The template takes 2 parameters: +@tparam _Tp element type +@tparam cn the number of elements + +In addition to the universal notation like Vec, you can use shorter aliases +for the most popular specialized variants of Vec, e.g. Vec3f ~ Vec. + +It is possible to convert Vec\ to/from Point_, Vec\ to/from Point3_ , and Vec\ +to CvScalar or Scalar_. Use operator[] to access the elements of Vec. + +All the expected vector operations are also implemented: +- v1 = v2 + v3 +- v1 = v2 - v3 +- v1 = v2 \* scale +- v1 = scale \* v2 +- v1 = -v2 +- v1 += v2 and other augmenting operations +- v1 == v2, v1 != v2 +- norm(v1) (euclidean norm) +The Vec class is commonly used to describe pixel types of multi-channel arrays. See Mat for details. +*/ +template class Vec : public Matx<_Tp, cn, 1> +{ +public: + typedef _Tp value_type; + enum { + channels = cn, +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = Matx<_Tp, cn, 1>::depth, + type = CV_MAKETYPE(depth, channels), +#endif + _dummy_enum_finalizer = 0 + }; + + //! default constructor + Vec(); + + Vec(_Tp v0); //!< 1-element vector constructor + Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2); //!< 3-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 4-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 5-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 6-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 7-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 8-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 9-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 10-element vector constructor + Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13); //!< 14-element vector constructor + explicit Vec(const _Tp* values); + +#ifdef CV_CXX11 + Vec(std::initializer_list<_Tp>); +#endif + + Vec(const Vec<_Tp, cn>& v); + + static Vec all(_Tp alpha); + + //! per-element multiplication + Vec mul(const Vec<_Tp, cn>& v) const; + + //! conjugation (makes sense for complex numbers and quaternions) + Vec conj() const; + + /*! + cross product of the two 3D vectors. + + For other dimensionalities the exception is raised + */ + Vec cross(const Vec& v) const; + //! conversion to another data type + template operator Vec() const; + + /*! element access */ + const _Tp& operator [](int i) const; + _Tp& operator[](int i); + const _Tp& operator ()(int i) const; + _Tp& operator ()(int i); + +#ifdef CV_CXX11 + Vec<_Tp, cn>& operator=(const Vec<_Tp, cn>& rhs) = default; +#endif + + Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp); + Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp); + template Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp); +}; + +/** @name Shorter aliases for the most popular specializations of Vec + @{ +*/ +typedef Vec Vec2b; +typedef Vec Vec3b; +typedef Vec Vec4b; + +typedef Vec Vec2s; +typedef Vec Vec3s; +typedef Vec Vec4s; + +typedef Vec Vec2w; +typedef Vec Vec3w; +typedef Vec Vec4w; + +typedef Vec Vec2i; +typedef Vec Vec3i; +typedef Vec Vec4i; +typedef Vec Vec6i; +typedef Vec Vec8i; + +typedef Vec Vec2f; +typedef Vec Vec3f; +typedef Vec Vec4f; +typedef Vec Vec6f; + +typedef Vec Vec2d; +typedef Vec Vec3d; +typedef Vec Vec4d; +typedef Vec Vec6d; +/** @} */ + +/*! + traits +*/ +template class DataType< Vec<_Tp, cn> > +{ +public: + typedef Vec<_Tp, cn> value_type; + typedef Vec::work_type, cn> work_type; + typedef _Tp channel_type; + typedef value_type vec_type; + + enum { generic_type = 0, + channels = cn, + fmt = DataType::fmt + ((channels - 1) << 8), +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + depth = DataType::depth, + type = CV_MAKETYPE(depth, channels), +#endif + _dummy_enum_finalizer = 0 + }; +}; + +namespace traits { +template +struct Depth< Vec<_Tp, cn> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Vec<_Tp, cn> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, cn) }; }; +} // namespace + + +/** @brief Comma-separated Vec Initializer +*/ +template class VecCommaInitializer : public MatxCommaInitializer<_Tp, m, 1> +{ +public: + VecCommaInitializer(Vec<_Tp, m>* _vec); + template VecCommaInitializer<_Tp, m>& operator , (T2 val); + Vec<_Tp, m> operator *() const; +}; + +template static Vec<_Tp, cn> normalize(const Vec<_Tp, cn>& v); + +//! @} core_basic + +//! @cond IGNORED + +///////////////////////////////////// helper classes ///////////////////////////////////// +namespace internal +{ + +template struct Matx_DetOp +{ + double operator ()(const Matx<_Tp, m, m>& a) const + { + Matx<_Tp, m, m> temp = a; + double p = LU(temp.val, m*sizeof(_Tp), m, 0, 0, 0); + if( p == 0 ) + return p; + for( int i = 0; i < m; i++ ) + p *= temp(i, i); + return p; + } +}; + +template struct Matx_DetOp<_Tp, 1> +{ + double operator ()(const Matx<_Tp, 1, 1>& a) const + { + return a(0,0); + } +}; + +template struct Matx_DetOp<_Tp, 2> +{ + double operator ()(const Matx<_Tp, 2, 2>& a) const + { + return a(0,0)*a(1,1) - a(0,1)*a(1,0); + } +}; + +template struct Matx_DetOp<_Tp, 3> +{ + double operator ()(const Matx<_Tp, 3, 3>& a) const + { + return a(0,0)*(a(1,1)*a(2,2) - a(2,1)*a(1,2)) - + a(0,1)*(a(1,0)*a(2,2) - a(2,0)*a(1,2)) + + a(0,2)*(a(1,0)*a(2,1) - a(2,0)*a(1,1)); + } +}; + +template Vec<_Tp, 2> inline conjugate(const Vec<_Tp, 2>& v) +{ + return Vec<_Tp, 2>(v[0], -v[1]); +} + +template Vec<_Tp, 4> inline conjugate(const Vec<_Tp, 4>& v) +{ + return Vec<_Tp, 4>(v[0], -v[1], -v[2], -v[3]); +} + +} // internal + + + +////////////////////////////////// Matx Implementation /////////////////////////////////// + +template inline +Matx<_Tp, m, n>::Matx() +{ + for(int i = 0; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0) +{ + val[0] = v0; + for(int i = 1; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1) +{ + CV_StaticAssert(channels >= 2, "Matx should have at least 2 elements."); + val[0] = v0; val[1] = v1; + for(int i = 2; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2) +{ + CV_StaticAssert(channels >= 3, "Matx should have at least 3 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; + for(int i = 3; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3) +{ + CV_StaticAssert(channels >= 4, "Matx should have at least 4 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + for(int i = 4; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4) +{ + CV_StaticAssert(channels >= 5, "Matx should have at least 5 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; val[4] = v4; + for(int i = 5; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5) +{ + CV_StaticAssert(channels >= 6, "Matx should have at least 6 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; + for(int i = 6; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6) +{ + CV_StaticAssert(channels >= 7, "Matx should have at least 7 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; + for(int i = 7; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7) +{ + CV_StaticAssert(channels >= 8, "Matx should have at least 8 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + for(int i = 8; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8) +{ + CV_StaticAssert(channels >= 9, "Matx should have at least 9 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; + for(int i = 9; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9) +{ + CV_StaticAssert(channels >= 10, "Matx should have at least 10 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; + for(int i = 10; i < channels; i++) val[i] = _Tp(0); +} + + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11) +{ + CV_StaticAssert(channels >= 12, "Matx should have at least 12 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + for(int i = 12; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13) +{ + CV_StaticAssert(channels >= 14, "Matx should have at least 14 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + val[12] = v12; val[13] = v13; + for (int i = 14; i < channels; i++) val[i] = _Tp(0); +} + + +template inline +Matx<_Tp,m,n>::Matx(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13, _Tp v14, _Tp v15) +{ + CV_StaticAssert(channels >= 16, "Matx should have at least 16 elements."); + val[0] = v0; val[1] = v1; val[2] = v2; val[3] = v3; + val[4] = v4; val[5] = v5; val[6] = v6; val[7] = v7; + val[8] = v8; val[9] = v9; val[10] = v10; val[11] = v11; + val[12] = v12; val[13] = v13; val[14] = v14; val[15] = v15; + for(int i = 16; i < channels; i++) val[i] = _Tp(0); +} + +template inline +Matx<_Tp, m, n>::Matx(const _Tp* values) +{ + for( int i = 0; i < channels; i++ ) val[i] = values[i]; +} + +#ifdef CV_CXX11 +template inline +Matx<_Tp, m, n>::Matx(std::initializer_list<_Tp> list) +{ + CV_DbgAssert(list.size() == channels); + int i = 0; + for(const auto& elem : list) + { + val[i++] = elem; + } +} +#endif + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::all(_Tp alpha) +{ + Matx<_Tp, m, n> M; + for( int i = 0; i < m*n; i++ ) M.val[i] = alpha; + return M; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::zeros() +{ + return all(0); +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::ones() +{ + return all(1); +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::eye() +{ + Matx<_Tp,m,n> M; + for(int i = 0; i < shortdim; i++) + M(i,i) = 1; + return M; +} + +template inline +_Tp Matx<_Tp, m, n>::dot(const Matx<_Tp, m, n>& M) const +{ + _Tp s = 0; + for( int i = 0; i < channels; i++ ) s += val[i]*M.val[i]; + return s; +} + +template inline +double Matx<_Tp, m, n>::ddot(const Matx<_Tp, m, n>& M) const +{ + double s = 0; + for( int i = 0; i < channels; i++ ) s += (double)val[i]*M.val[i]; + return s; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::diag(const typename Matx<_Tp,m,n>::diag_type& d) +{ + Matx<_Tp,m,n> M; + for(int i = 0; i < shortdim; i++) + M(i,i) = d(i, 0); + return M; +} + +template template +inline Matx<_Tp, m, n>::operator Matx() const +{ + Matx M; + for( int i = 0; i < m*n; i++ ) M.val[i] = saturate_cast(val[i]); + return M; +} + +template template inline +Matx<_Tp, m1, n1> Matx<_Tp, m, n>::reshape() const +{ + CV_StaticAssert(m1*n1 == m*n, "Input and destnarion matrices must have the same number of elements"); + return (const Matx<_Tp, m1, n1>&)*this; +} + +template +template inline +Matx<_Tp, m1, n1> Matx<_Tp, m, n>::get_minor(int base_row, int base_col) const +{ + CV_DbgAssert(0 <= base_row && base_row+m1 <= m && 0 <= base_col && base_col+n1 <= n); + Matx<_Tp, m1, n1> s; + for( int di = 0; di < m1; di++ ) + for( int dj = 0; dj < n1; dj++ ) + s(di, dj) = (*this)(base_row+di, base_col+dj); + return s; +} + +template inline +Matx<_Tp, 1, n> Matx<_Tp, m, n>::row(int i) const +{ + CV_DbgAssert((unsigned)i < (unsigned)m); + return Matx<_Tp, 1, n>(&val[i*n]); +} + +template inline +Matx<_Tp, m, 1> Matx<_Tp, m, n>::col(int j) const +{ + CV_DbgAssert((unsigned)j < (unsigned)n); + Matx<_Tp, m, 1> v; + for( int i = 0; i < m; i++ ) + v.val[i] = val[i*n + j]; + return v; +} + +template inline +typename Matx<_Tp, m, n>::diag_type Matx<_Tp, m, n>::diag() const +{ + diag_type d; + for( int i = 0; i < shortdim; i++ ) + d.val[i] = val[i*n + i]; + return d; +} + +template inline +const _Tp& Matx<_Tp, m, n>::operator()(int row_idx, int col_idx) const +{ + CV_DbgAssert( (unsigned)row_idx < (unsigned)m && (unsigned)col_idx < (unsigned)n ); + return this->val[row_idx*n + col_idx]; +} + +template inline +_Tp& Matx<_Tp, m, n>::operator ()(int row_idx, int col_idx) +{ + CV_DbgAssert( (unsigned)row_idx < (unsigned)m && (unsigned)col_idx < (unsigned)n ); + return val[row_idx*n + col_idx]; +} + +template inline +const _Tp& Matx<_Tp, m, n>::operator ()(int i) const +{ + CV_StaticAssert(m == 1 || n == 1, "Single index indexation requires matrix to be a column or a row"); + CV_DbgAssert( (unsigned)i < (unsigned)(m+n-1) ); + return val[i]; +} + +template inline +_Tp& Matx<_Tp, m, n>::operator ()(int i) +{ + CV_StaticAssert(m == 1 || n == 1, "Single index indexation requires matrix to be a column or a row"); + CV_DbgAssert( (unsigned)i < (unsigned)(m+n-1) ); + return val[i]; +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_AddOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] + b.val[i]); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_SubOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] - b.val[i]); +} + +template template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, _T2 alpha, Matx_ScaleOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] * alpha); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_MulOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] * b.val[i]); +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b, Matx_DivOp) +{ + for( int i = 0; i < channels; i++ ) + val[i] = saturate_cast<_Tp>(a.val[i] / b.val[i]); +} + +template template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b, Matx_MatMulOp) +{ + for( int i = 0; i < m; i++ ) + for( int j = 0; j < n; j++ ) + { + _Tp s = 0; + for( int k = 0; k < l; k++ ) + s += a(i, k) * b(k, j); + val[i*n + j] = s; + } +} + +template inline +Matx<_Tp,m,n>::Matx(const Matx<_Tp, n, m>& a, Matx_TOp) +{ + for( int i = 0; i < m; i++ ) + for( int j = 0; j < n; j++ ) + val[i*n + j] = a(j, i); +} + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::mul(const Matx<_Tp, m, n>& a) const +{ + return Matx<_Tp, m, n>(*this, a, Matx_MulOp()); +} + +template inline +Matx<_Tp, m, n> Matx<_Tp, m, n>::div(const Matx<_Tp, m, n>& a) const +{ + return Matx<_Tp, m, n>(*this, a, Matx_DivOp()); +} + +template inline +Matx<_Tp, n, m> Matx<_Tp, m, n>::t() const +{ + return Matx<_Tp, n, m>(*this, Matx_TOp()); +} + +template inline +Vec<_Tp, n> Matx<_Tp, m, n>::solve(const Vec<_Tp, m>& rhs, int method) const +{ + Matx<_Tp, n, 1> x = solve((const Matx<_Tp, m, 1>&)(rhs), method); + return (Vec<_Tp, n>&)(x); +} + +template static inline +double determinant(const Matx<_Tp, m, m>& a) +{ + return cv::internal::Matx_DetOp<_Tp, m>()(a); +} + +template static inline +double trace(const Matx<_Tp, m, n>& a) +{ + _Tp s = 0; + for( int i = 0; i < std::min(m, n); i++ ) + s += a(i,i); + return s; +} + +template static inline +double norm(const Matx<_Tp, m, n>& M) +{ + return std::sqrt(normL2Sqr<_Tp, double>(M.val, m*n)); +} + +template static inline +double norm(const Matx<_Tp, m, n>& M, int normType) +{ + switch(normType) { + case NORM_INF: + return (double)normInf<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + case NORM_L1: + return (double)normL1<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + case NORM_L2SQR: + return (double)normL2Sqr<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n); + default: + case NORM_L2: + return std::sqrt((double)normL2Sqr<_Tp, typename DataType<_Tp>::work_type>(M.val, m*n)); + } +} + + + +//////////////////////////////// matx comma initializer ////////////////////////////////// + +template static inline +MatxCommaInitializer<_Tp, m, n> operator << (const Matx<_Tp, m, n>& mtx, _T2 val) +{ + MatxCommaInitializer<_Tp, m, n> commaInitializer((Matx<_Tp, m, n>*)&mtx); + return (commaInitializer, val); +} + +template inline +MatxCommaInitializer<_Tp, m, n>::MatxCommaInitializer(Matx<_Tp, m, n>* _mtx) + : dst(_mtx), idx(0) +{} + +template template inline +MatxCommaInitializer<_Tp, m, n>& MatxCommaInitializer<_Tp, m, n>::operator , (_T2 value) +{ + CV_DbgAssert( idx < m*n ); + dst->val[idx++] = saturate_cast<_Tp>(value); + return *this; +} + +template inline +Matx<_Tp, m, n> MatxCommaInitializer<_Tp, m, n>::operator *() const +{ + CV_DbgAssert( idx == n*m ); + return *dst; +} + + + +/////////////////////////////////// Vec Implementation /////////////////////////////////// + +template inline +Vec<_Tp, cn>::Vec() {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0) + : Matx<_Tp, cn, 1>(v0) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1) + : Matx<_Tp, cn, 1>(v0, v1) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2) + : Matx<_Tp, cn, 1>(v0, v1, v2) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) {} + +template inline +Vec<_Tp, cn>::Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9, _Tp v10, _Tp v11, _Tp v12, _Tp v13) + : Matx<_Tp, cn, 1>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {} + +template inline +Vec<_Tp, cn>::Vec(const _Tp* values) + : Matx<_Tp, cn, 1>(values) {} + +#ifdef CV_CXX11 +template inline +Vec<_Tp, cn>::Vec(std::initializer_list<_Tp> list) + : Matx<_Tp, cn, 1>(list) {} +#endif + +template inline +Vec<_Tp, cn>::Vec(const Vec<_Tp, cn>& m) + : Matx<_Tp, cn, 1>(m.val) {} + +template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp op) + : Matx<_Tp, cn, 1>(a, b, op) {} + +template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp op) + : Matx<_Tp, cn, 1>(a, b, op) {} + +template template inline +Vec<_Tp, cn>::Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp op) + : Matx<_Tp, cn, 1>(a, alpha, op) {} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::all(_Tp alpha) +{ + Vec v; + for( int i = 0; i < cn; i++ ) v.val[i] = alpha; + return v; +} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::mul(const Vec<_Tp, cn>& v) const +{ + Vec<_Tp, cn> w; + for( int i = 0; i < cn; i++ ) w.val[i] = saturate_cast<_Tp>(this->val[i]*v.val[i]); + return w; +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template<> inline +Vec Vec::conj() const +{ + return cv::internal::conjugate(*this); +} + +template inline +Vec<_Tp, cn> Vec<_Tp, cn>::cross(const Vec<_Tp, cn>&) const +{ + CV_StaticAssert(cn == 3, "for arbitrary-size vector there is no cross-product defined"); + return Vec<_Tp, cn>(); +} + +template<> inline +Vec Vec::cross(const Vec& v) const +{ + return Vec(this->val[1]*v.val[2] - this->val[2]*v.val[1], + this->val[2]*v.val[0] - this->val[0]*v.val[2], + this->val[0]*v.val[1] - this->val[1]*v.val[0]); +} + +template<> inline +Vec Vec::cross(const Vec& v) const +{ + return Vec(this->val[1]*v.val[2] - this->val[2]*v.val[1], + this->val[2]*v.val[0] - this->val[0]*v.val[2], + this->val[0]*v.val[1] - this->val[1]*v.val[0]); +} + +template template inline +Vec<_Tp, cn>::operator Vec() const +{ + Vec v; + for( int i = 0; i < cn; i++ ) v.val[i] = saturate_cast(this->val[i]); + return v; +} + +template inline +const _Tp& Vec<_Tp, cn>::operator [](int i) const +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +_Tp& Vec<_Tp, cn>::operator [](int i) +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +const _Tp& Vec<_Tp, cn>::operator ()(int i) const +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +_Tp& Vec<_Tp, cn>::operator ()(int i) +{ + CV_DbgAssert( (unsigned)i < (unsigned)cn ); + return this->val[i]; +} + +template inline +Vec<_Tp, cn> normalize(const Vec<_Tp, cn>& v) +{ + double nv = norm(v); + return v * (nv ? 1./nv : 0.); +} + + + +//////////////////////////////// vec comma initializer ////////////////////////////////// + + +template static inline +VecCommaInitializer<_Tp, cn> operator << (const Vec<_Tp, cn>& vec, _T2 val) +{ + VecCommaInitializer<_Tp, cn> commaInitializer((Vec<_Tp, cn>*)&vec); + return (commaInitializer, val); +} + +template inline +VecCommaInitializer<_Tp, cn>::VecCommaInitializer(Vec<_Tp, cn>* _vec) + : MatxCommaInitializer<_Tp, cn, 1>(_vec) +{} + +template template inline +VecCommaInitializer<_Tp, cn>& VecCommaInitializer<_Tp, cn>::operator , (_T2 value) +{ + CV_DbgAssert( this->idx < cn ); + this->dst->val[this->idx++] = saturate_cast<_Tp>(value); + return *this; +} + +template inline +Vec<_Tp, cn> VecCommaInitializer<_Tp, cn>::operator *() const +{ + CV_DbgAssert( this->idx == cn ); + return *this->dst; +} + +//! @endcond + +///////////////////////////// Matx out-of-class operators //////////////////////////////// + +//! @relates cv::Matx +//! @{ + +template static inline +Matx<_Tp1, m, n>& operator += (Matx<_Tp1, m, n>& a, const Matx<_Tp2, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] + b.val[i]); + return a; +} + +template static inline +Matx<_Tp1, m, n>& operator -= (Matx<_Tp1, m, n>& a, const Matx<_Tp2, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] - b.val[i]); + return a; +} + +template static inline +Matx<_Tp, m, n> operator + (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_AddOp()); +} + +template static inline +Matx<_Tp, m, n> operator - (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_SubOp()); +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, int alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, float alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n>& operator *= (Matx<_Tp, m, n>& a, double alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = saturate_cast<_Tp>(a.val[i] * alpha); + return a; +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, int alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, float alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, n>& a, double alpha) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (int alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (float alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (double alpha, const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n>& operator /= (Matx<_Tp, m, n>& a, float alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = a.val[i] / alpha; + return a; +} + +template static inline +Matx<_Tp, m, n>& operator /= (Matx<_Tp, m, n>& a, double alpha) +{ + for( int i = 0; i < m*n; i++ ) + a.val[i] = a.val[i] / alpha; + return a; +} + +template static inline +Matx<_Tp, m, n> operator / (const Matx<_Tp, m, n>& a, float alpha) +{ + return Matx<_Tp, m, n>(a, 1.f/alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator / (const Matx<_Tp, m, n>& a, double alpha) +{ + return Matx<_Tp, m, n>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator - (const Matx<_Tp, m, n>& a) +{ + return Matx<_Tp, m, n>(a, -1, Matx_ScaleOp()); +} + +template static inline +Matx<_Tp, m, n> operator * (const Matx<_Tp, m, l>& a, const Matx<_Tp, l, n>& b) +{ + return Matx<_Tp, m, n>(a, b, Matx_MatMulOp()); +} + +template static inline +Vec<_Tp, m> operator * (const Matx<_Tp, m, n>& a, const Vec<_Tp, n>& b) +{ + Matx<_Tp, m, 1> c(a, b, Matx_MatMulOp()); + return (const Vec<_Tp, m>&)(c); +} + +template static inline +bool operator == (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + for( int i = 0; i < m*n; i++ ) + if( a.val[i] != b.val[i] ) return false; + return true; +} + +template static inline +bool operator != (const Matx<_Tp, m, n>& a, const Matx<_Tp, m, n>& b) +{ + return !(a == b); +} + +//! @} + +////////////////////////////// Vec out-of-class operators //////////////////////////////// + +//! @relates cv::Vec +//! @{ + +template static inline +Vec<_Tp1, cn>& operator += (Vec<_Tp1, cn>& a, const Vec<_Tp2, cn>& b) +{ + for( int i = 0; i < cn; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] + b.val[i]); + return a; +} + +template static inline +Vec<_Tp1, cn>& operator -= (Vec<_Tp1, cn>& a, const Vec<_Tp2, cn>& b) +{ + for( int i = 0; i < cn; i++ ) + a.val[i] = saturate_cast<_Tp1>(a.val[i] - b.val[i]); + return a; +} + +template static inline +Vec<_Tp, cn> operator + (const Vec<_Tp, cn>& a, const Vec<_Tp, cn>& b) +{ + return Vec<_Tp, cn>(a, b, Matx_AddOp()); +} + +template static inline +Vec<_Tp, cn> operator - (const Vec<_Tp, cn>& a, const Vec<_Tp, cn>& b) +{ + return Vec<_Tp, cn>(a, b, Matx_SubOp()); +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, int alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, float alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator *= (Vec<_Tp, cn>& a, double alpha) +{ + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*alpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, int alpha) +{ + double ialpha = 1./alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, float alpha) +{ + float ialpha = 1.f/alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn>& operator /= (Vec<_Tp, cn>& a, double alpha) +{ + double ialpha = 1./alpha; + for( int i = 0; i < cn; i++ ) + a[i] = saturate_cast<_Tp>(a[i]*ialpha); + return a; +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, int alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (int alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, float alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (float alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (const Vec<_Tp, cn>& a, double alpha) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator * (double alpha, const Vec<_Tp, cn>& a) +{ + return Vec<_Tp, cn>(a, alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, int alpha) +{ + return Vec<_Tp, cn>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, float alpha) +{ + return Vec<_Tp, cn>(a, 1.f/alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator / (const Vec<_Tp, cn>& a, double alpha) +{ + return Vec<_Tp, cn>(a, 1./alpha, Matx_ScaleOp()); +} + +template static inline +Vec<_Tp, cn> operator - (const Vec<_Tp, cn>& a) +{ + Vec<_Tp,cn> t; + for( int i = 0; i < cn; i++ ) t.val[i] = saturate_cast<_Tp>(-a.val[i]); + return t; +} + +template inline Vec<_Tp, 4> operator * (const Vec<_Tp, 4>& v1, const Vec<_Tp, 4>& v2) +{ + return Vec<_Tp, 4>(saturate_cast<_Tp>(v1[0]*v2[0] - v1[1]*v2[1] - v1[2]*v2[2] - v1[3]*v2[3]), + saturate_cast<_Tp>(v1[0]*v2[1] + v1[1]*v2[0] + v1[2]*v2[3] - v1[3]*v2[2]), + saturate_cast<_Tp>(v1[0]*v2[2] - v1[1]*v2[3] + v1[2]*v2[0] + v1[3]*v2[1]), + saturate_cast<_Tp>(v1[0]*v2[3] + v1[1]*v2[2] - v1[2]*v2[1] + v1[3]*v2[0])); +} + +template inline Vec<_Tp, 4>& operator *= (Vec<_Tp, 4>& v1, const Vec<_Tp, 4>& v2) +{ + v1 = v1 * v2; + return v1; +} + +//! @} + +} // cv + +#endif // OPENCV_CORE_MATX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/neon_utils.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/neon_utils.hpp new file mode 100644 index 0000000..573ba99 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/neon_utils.hpp @@ -0,0 +1,128 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HAL_NEON_UTILS_HPP +#define OPENCV_HAL_NEON_UTILS_HPP + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils_neon +//! @{ + +#if CV_NEON + +inline int32x2_t cv_vrnd_s32_f32(float32x2_t v) +{ + static int32x2_t v_sign = vdup_n_s32(1 << 31), + v_05 = vreinterpret_s32_f32(vdup_n_f32(0.5f)); + + int32x2_t v_addition = vorr_s32(v_05, vand_s32(v_sign, vreinterpret_s32_f32(v))); + return vcvt_s32_f32(vadd_f32(v, vreinterpret_f32_s32(v_addition))); +} + +inline int32x4_t cv_vrndq_s32_f32(float32x4_t v) +{ + static int32x4_t v_sign = vdupq_n_s32(1 << 31), + v_05 = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); + + int32x4_t v_addition = vorrq_s32(v_05, vandq_s32(v_sign, vreinterpretq_s32_f32(v))); + return vcvtq_s32_f32(vaddq_f32(v, vreinterpretq_f32_s32(v_addition))); +} + +inline uint32x2_t cv_vrnd_u32_f32(float32x2_t v) +{ + static float32x2_t v_05 = vdup_n_f32(0.5f); + return vcvt_u32_f32(vadd_f32(v, v_05)); +} + +inline uint32x4_t cv_vrndq_u32_f32(float32x4_t v) +{ + static float32x4_t v_05 = vdupq_n_f32(0.5f); + return vcvtq_u32_f32(vaddq_f32(v, v_05)); +} + +inline float32x4_t cv_vrecpq_f32(float32x4_t val) +{ + float32x4_t reciprocal = vrecpeq_f32(val); + reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + return reciprocal; +} + +inline float32x2_t cv_vrecp_f32(float32x2_t val) +{ + float32x2_t reciprocal = vrecpe_f32(val); + reciprocal = vmul_f32(vrecps_f32(val, reciprocal), reciprocal); + reciprocal = vmul_f32(vrecps_f32(val, reciprocal), reciprocal); + return reciprocal; +} + +inline float32x4_t cv_vrsqrtq_f32(float32x4_t val) +{ + float32x4_t e = vrsqrteq_f32(val); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(e, e), val), e); + e = vmulq_f32(vrsqrtsq_f32(vmulq_f32(e, e), val), e); + return e; +} + +inline float32x2_t cv_vrsqrt_f32(float32x2_t val) +{ + float32x2_t e = vrsqrte_f32(val); + e = vmul_f32(vrsqrts_f32(vmul_f32(e, e), val), e); + e = vmul_f32(vrsqrts_f32(vmul_f32(e, e), val), e); + return e; +} + +inline float32x4_t cv_vsqrtq_f32(float32x4_t val) +{ + return cv_vrecpq_f32(cv_vrsqrtq_f32(val)); +} + +inline float32x2_t cv_vsqrt_f32(float32x2_t val) +{ + return cv_vrecp_f32(cv_vrsqrt_f32(val)); +} + +#endif + +//! @} + +#endif // OPENCV_HAL_NEON_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl.hpp new file mode 100644 index 0000000..f03de18 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl.hpp @@ -0,0 +1,848 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPENCL_HPP +#define OPENCV_OPENCL_HPP + +#include "opencv2/core.hpp" + +namespace cv { namespace ocl { + +//! @addtogroup core_opencl +//! @{ + +CV_EXPORTS_W bool haveOpenCL(); +CV_EXPORTS_W bool useOpenCL(); +CV_EXPORTS_W bool haveAmdBlas(); +CV_EXPORTS_W bool haveAmdFft(); +CV_EXPORTS_W void setUseOpenCL(bool flag); +CV_EXPORTS_W void finish(); + +CV_EXPORTS bool haveSVM(); + +class CV_EXPORTS Context; +class CV_EXPORTS_W_SIMPLE Device; +class CV_EXPORTS Kernel; +class CV_EXPORTS Program; +class CV_EXPORTS ProgramSource; +class CV_EXPORTS Queue; +class CV_EXPORTS PlatformInfo; +class CV_EXPORTS Image2D; + +class CV_EXPORTS_W_SIMPLE Device +{ +public: + CV_WRAP Device() CV_NOEXCEPT; + explicit Device(void* d); + Device(const Device& d); + Device& operator = (const Device& d); + CV_WRAP ~Device(); + + void set(void* d); + + enum + { + TYPE_DEFAULT = (1 << 0), + TYPE_CPU = (1 << 1), + TYPE_GPU = (1 << 2), + TYPE_ACCELERATOR = (1 << 3), + TYPE_DGPU = TYPE_GPU + (1 << 16), + TYPE_IGPU = TYPE_GPU + (1 << 17), + TYPE_ALL = 0xFFFFFFFF + }; + + CV_WRAP String name() const; + CV_WRAP String extensions() const; + CV_WRAP bool isExtensionSupported(const String& extensionName) const; + CV_WRAP String version() const; + CV_WRAP String vendorName() const; + CV_WRAP String OpenCL_C_Version() const; + CV_WRAP String OpenCLVersion() const; + CV_WRAP int deviceVersionMajor() const; + CV_WRAP int deviceVersionMinor() const; + CV_WRAP String driverVersion() const; + void* ptr() const; + + CV_WRAP int type() const; + + CV_WRAP int addressBits() const; + CV_WRAP bool available() const; + CV_WRAP bool compilerAvailable() const; + CV_WRAP bool linkerAvailable() const; + + enum + { + FP_DENORM=(1 << 0), + FP_INF_NAN=(1 << 1), + FP_ROUND_TO_NEAREST=(1 << 2), + FP_ROUND_TO_ZERO=(1 << 3), + FP_ROUND_TO_INF=(1 << 4), + FP_FMA=(1 << 5), + FP_SOFT_FLOAT=(1 << 6), + FP_CORRECTLY_ROUNDED_DIVIDE_SQRT=(1 << 7) + }; + CV_WRAP int doubleFPConfig() const; + CV_WRAP int singleFPConfig() const; + CV_WRAP int halfFPConfig() const; + + CV_WRAP bool endianLittle() const; + CV_WRAP bool errorCorrectionSupport() const; + + enum + { + EXEC_KERNEL=(1 << 0), + EXEC_NATIVE_KERNEL=(1 << 1) + }; + CV_WRAP int executionCapabilities() const; + + CV_WRAP size_t globalMemCacheSize() const; + + enum + { + NO_CACHE=0, + READ_ONLY_CACHE=1, + READ_WRITE_CACHE=2 + }; + CV_WRAP int globalMemCacheType() const; + CV_WRAP int globalMemCacheLineSize() const; + CV_WRAP size_t globalMemSize() const; + + CV_WRAP size_t localMemSize() const; + enum + { + NO_LOCAL_MEM=0, + LOCAL_IS_LOCAL=1, + LOCAL_IS_GLOBAL=2 + }; + CV_WRAP int localMemType() const; + CV_WRAP bool hostUnifiedMemory() const; + + CV_WRAP bool imageSupport() const; + + CV_WRAP bool imageFromBufferSupport() const; + uint imagePitchAlignment() const; + uint imageBaseAddressAlignment() const; + + /// deprecated, use isExtensionSupported() method (probably with "cl_khr_subgroups" value) + CV_WRAP bool intelSubgroupsSupport() const; + + CV_WRAP size_t image2DMaxWidth() const; + CV_WRAP size_t image2DMaxHeight() const; + + CV_WRAP size_t image3DMaxWidth() const; + CV_WRAP size_t image3DMaxHeight() const; + CV_WRAP size_t image3DMaxDepth() const; + + CV_WRAP size_t imageMaxBufferSize() const; + CV_WRAP size_t imageMaxArraySize() const; + + enum + { + UNKNOWN_VENDOR=0, + VENDOR_AMD=1, + VENDOR_INTEL=2, + VENDOR_NVIDIA=3 + }; + CV_WRAP int vendorID() const; + // FIXIT + // dev.isAMD() doesn't work for OpenCL CPU devices from AMD OpenCL platform. + // This method should use platform name instead of vendor name. + // After fix restore code in arithm.cpp: ocl_compare() + CV_WRAP inline bool isAMD() const { return vendorID() == VENDOR_AMD; } + CV_WRAP inline bool isIntel() const { return vendorID() == VENDOR_INTEL; } + CV_WRAP inline bool isNVidia() const { return vendorID() == VENDOR_NVIDIA; } + + CV_WRAP int maxClockFrequency() const; + CV_WRAP int maxComputeUnits() const; + CV_WRAP int maxConstantArgs() const; + CV_WRAP size_t maxConstantBufferSize() const; + + CV_WRAP size_t maxMemAllocSize() const; + CV_WRAP size_t maxParameterSize() const; + + CV_WRAP int maxReadImageArgs() const; + CV_WRAP int maxWriteImageArgs() const; + CV_WRAP int maxSamplers() const; + + CV_WRAP size_t maxWorkGroupSize() const; + CV_WRAP int maxWorkItemDims() const; + void maxWorkItemSizes(size_t*) const; + + CV_WRAP int memBaseAddrAlign() const; + + CV_WRAP int nativeVectorWidthChar() const; + CV_WRAP int nativeVectorWidthShort() const; + CV_WRAP int nativeVectorWidthInt() const; + CV_WRAP int nativeVectorWidthLong() const; + CV_WRAP int nativeVectorWidthFloat() const; + CV_WRAP int nativeVectorWidthDouble() const; + CV_WRAP int nativeVectorWidthHalf() const; + + CV_WRAP int preferredVectorWidthChar() const; + CV_WRAP int preferredVectorWidthShort() const; + CV_WRAP int preferredVectorWidthInt() const; + CV_WRAP int preferredVectorWidthLong() const; + CV_WRAP int preferredVectorWidthFloat() const; + CV_WRAP int preferredVectorWidthDouble() const; + CV_WRAP int preferredVectorWidthHalf() const; + + CV_WRAP size_t printfBufferSize() const; + CV_WRAP size_t profilingTimerResolution() const; + + CV_WRAP static const Device& getDefault(); + +protected: + struct Impl; + Impl* p; +}; + + +class CV_EXPORTS Context +{ +public: + Context() CV_NOEXCEPT; + explicit Context(int dtype); + ~Context(); + Context(const Context& c); + Context& operator = (const Context& c); + + bool create(); + bool create(int dtype); + size_t ndevices() const; + const Device& device(size_t idx) const; + Program getProg(const ProgramSource& prog, + const String& buildopt, String& errmsg); + void unloadProg(Program& prog); + + static Context& getDefault(bool initialize = true); + void* ptr() const; + + friend void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); + + bool useSVM() const; + void setUseSVM(bool enabled); + + struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +//protected: + Impl* p; +}; + +class CV_EXPORTS Platform +{ +public: + Platform() CV_NOEXCEPT; + ~Platform(); + Platform(const Platform& p); + Platform& operator = (const Platform& p); + + void* ptr() const; + static Platform& getDefault(); + + friend void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); +protected: + struct Impl; + Impl* p; +}; + +/** @brief Attaches OpenCL context to OpenCV +@note + OpenCV will check if available OpenCL platform has platformName name, then assign context to + OpenCV and call `clRetainContext` function. The deviceID device will be used as target device and + new command queue will be created. +@param platformName name of OpenCL platform to attach, this string is used to check if platform is available to OpenCV at runtime +@param platformID ID of platform attached context was created for +@param context OpenCL context to be attached to OpenCV +@param deviceID ID of device, must be created from attached context +*/ +CV_EXPORTS void attachContext(const String& platformName, void* platformID, void* context, void* deviceID); + +/** @brief Convert OpenCL buffer to UMat +@note + OpenCL buffer (cl_mem_buffer) should contain 2D image data, compatible with OpenCV. Memory + content is not copied from `clBuffer` to UMat. Instead, buffer handle assigned to UMat and + `clRetainMemObject` is called. +@param cl_mem_buffer source clBuffer handle +@param step num of bytes in single row +@param rows number of rows +@param cols number of cols +@param type OpenCV type of image +@param dst destination UMat +*/ +CV_EXPORTS void convertFromBuffer(void* cl_mem_buffer, size_t step, int rows, int cols, int type, UMat& dst); + +/** @brief Convert OpenCL image2d_t to UMat +@note + OpenCL `image2d_t` (cl_mem_image), should be compatible with OpenCV UMat formats. Memory content + is copied from image to UMat with `clEnqueueCopyImageToBuffer` function. +@param cl_mem_image source image2d_t handle +@param dst destination UMat +*/ +CV_EXPORTS void convertFromImage(void* cl_mem_image, UMat& dst); + +// TODO Move to internal header +void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); + +class CV_EXPORTS Queue +{ +public: + Queue() CV_NOEXCEPT; + explicit Queue(const Context& c, const Device& d=Device()); + ~Queue(); + Queue(const Queue& q); + Queue& operator = (const Queue& q); + + bool create(const Context& c=Context(), const Device& d=Device()); + void finish(); + void* ptr() const; + static Queue& getDefault(); + + /// @brief Returns OpenCL command queue with enable profiling mode support + const Queue& getProfilingQueue() const; + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return p; } +protected: + Impl* p; +}; + + +class CV_EXPORTS KernelArg +{ +public: + enum { LOCAL=1, READ_ONLY=2, WRITE_ONLY=4, READ_WRITE=6, CONSTANT=8, PTR_ONLY = 16, NO_SIZE=256 }; + KernelArg(int _flags, UMat* _m, int wscale=1, int iwscale=1, const void* _obj=0, size_t _sz=0); + KernelArg() CV_NOEXCEPT; + + static KernelArg Local(size_t localMemSize) + { return KernelArg(LOCAL, 0, 1, 1, 0, localMemSize); } + static KernelArg PtrWriteOnly(const UMat& m) + { return KernelArg(PTR_ONLY+WRITE_ONLY, (UMat*)&m); } + static KernelArg PtrReadOnly(const UMat& m) + { return KernelArg(PTR_ONLY+READ_ONLY, (UMat*)&m); } + static KernelArg PtrReadWrite(const UMat& m) + { return KernelArg(PTR_ONLY+READ_WRITE, (UMat*)&m); } + static KernelArg ReadWrite(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_WRITE, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadWriteNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_WRITE+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadOnly(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_ONLY, (UMat*)&m, wscale, iwscale); } + static KernelArg WriteOnly(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(WRITE_ONLY, (UMat*)&m, wscale, iwscale); } + static KernelArg ReadOnlyNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(READ_ONLY+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg WriteOnlyNoSize(const UMat& m, int wscale=1, int iwscale=1) + { return KernelArg(WRITE_ONLY+NO_SIZE, (UMat*)&m, wscale, iwscale); } + static KernelArg Constant(const Mat& m); + template static KernelArg Constant(const _Tp* arr, size_t n) + { return KernelArg(CONSTANT, 0, 1, 1, (void*)arr, n); } + + int flags; + UMat* m; + const void* obj; + size_t sz; + int wscale, iwscale; +}; + + +class CV_EXPORTS Kernel +{ +public: + Kernel() CV_NOEXCEPT; + Kernel(const char* kname, const Program& prog); + Kernel(const char* kname, const ProgramSource& prog, + const String& buildopts = String(), String* errmsg=0); + ~Kernel(); + Kernel(const Kernel& k); + Kernel& operator = (const Kernel& k); + + bool empty() const; + bool create(const char* kname, const Program& prog); + bool create(const char* kname, const ProgramSource& prog, + const String& buildopts, String* errmsg=0); + + int set(int i, const void* value, size_t sz); + int set(int i, const Image2D& image2D); + int set(int i, const UMat& m); + int set(int i, const KernelArg& arg); + template int set(int i, const _Tp& value) + { return set(i, &value, sizeof(value)); } + + template + Kernel& args(const _Tp0& a0) + { + set(0, a0); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1) + { + int i = set(0, a0); set(i, a1); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2) + { + int i = set(0, a0); i = set(i, a1); set(i, a2); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, + const _Tp3& a3, const _Tp4& a4) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); + i = set(i, a3); set(i, a4); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, + const _Tp3& a3, const _Tp4& a4, const _Tp5& a5) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); + i = set(i, a3); i = set(i, a4); set(i, a5); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); + i = set(i, a4); i = set(i, a5); set(i, a6); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); + i = set(i, a4); i = set(i, a5); i = set(i, a6); set(i, a7); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); + i = set(i, a5); i = set(i, a6); i = set(i, a7); set(i, a8); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); set(i, a9); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); set(i, a10); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); set(i, a11); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + set(i, a12); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); set(i, a13); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13, const _Tp14& a14) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); i = set(i, a13); set(i, a14); return *this; + } + + template + Kernel& args(const _Tp0& a0, const _Tp1& a1, const _Tp2& a2, const _Tp3& a3, + const _Tp4& a4, const _Tp5& a5, const _Tp6& a6, const _Tp7& a7, + const _Tp8& a8, const _Tp9& a9, const _Tp10& a10, const _Tp11& a11, + const _Tp12& a12, const _Tp13& a13, const _Tp14& a14, const _Tp15& a15) + { + int i = set(0, a0); i = set(i, a1); i = set(i, a2); i = set(i, a3); i = set(i, a4); i = set(i, a5); + i = set(i, a6); i = set(i, a7); i = set(i, a8); i = set(i, a9); i = set(i, a10); i = set(i, a11); + i = set(i, a12); i = set(i, a13); i = set(i, a14); set(i, a15); return *this; + } + /** @brief Run the OpenCL kernel. + @param dims the work problem dimensions. It is the length of globalsize and localsize. It can be either 1, 2 or 3. + @param globalsize work items for each dimension. It is not the final globalsize passed to + OpenCL. Each dimension will be adjusted to the nearest integer divisible by the corresponding + value in localsize. If localsize is NULL, it will still be adjusted depending on dims. The + adjusted values are greater than or equal to the original values. + @param localsize work-group size for each dimension. + @param sync specify whether to wait for OpenCL computation to finish before return. + @param q command queue + */ + bool run(int dims, size_t globalsize[], + size_t localsize[], bool sync, const Queue& q=Queue()); + bool runTask(bool sync, const Queue& q=Queue()); + + /** @brief Similar to synchronized run() call with returning of kernel execution time + * Separate OpenCL command queue may be used (with CL_QUEUE_PROFILING_ENABLE) + * @return Execution time in nanoseconds or negative number on error + */ + int64 runProfiling(int dims, size_t globalsize[], size_t localsize[], const Queue& q=Queue()); + + size_t workGroupSize() const; + size_t preferedWorkGroupSizeMultiple() const; + bool compileWorkGroupSize(size_t wsz[]) const; + size_t localMemSize() const; + + void* ptr() const; + struct Impl; + +protected: + Impl* p; +}; + +class CV_EXPORTS Program +{ +public: + Program() CV_NOEXCEPT; + Program(const ProgramSource& src, + const String& buildflags, String& errmsg); + Program(const Program& prog); + + Program& operator = (const Program& prog); + ~Program(); + + bool create(const ProgramSource& src, + const String& buildflags, String& errmsg); + + void* ptr() const; + + /** + * @brief Query device-specific program binary. + * + * Returns RAW OpenCL executable binary without additional attachments. + * + * @sa ProgramSource::fromBinary + * + * @param[out] binary output buffer + */ + void getBinary(std::vector& binary) const; + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +protected: + Impl* p; +public: +#ifndef OPENCV_REMOVE_DEPRECATED_API + // TODO Remove this + CV_DEPRECATED bool read(const String& buf, const String& buildflags); // removed, use ProgramSource instead + CV_DEPRECATED bool write(String& buf) const; // removed, use getBinary() method instead (RAW OpenCL binary) + CV_DEPRECATED const ProgramSource& source() const; // implementation removed + CV_DEPRECATED String getPrefix() const; // deprecated, implementation replaced + CV_DEPRECATED static String getPrefix(const String& buildflags); // deprecated, implementation replaced +#endif +}; + + +class CV_EXPORTS ProgramSource +{ +public: + typedef uint64 hash_t; // deprecated + + ProgramSource() CV_NOEXCEPT; + explicit ProgramSource(const String& module, const String& name, const String& codeStr, const String& codeHash); + explicit ProgramSource(const String& prog); // deprecated + explicit ProgramSource(const char* prog); // deprecated + ~ProgramSource(); + ProgramSource(const ProgramSource& prog); + ProgramSource& operator = (const ProgramSource& prog); + + const String& source() const; // deprecated + hash_t hash() const; // deprecated + + + /** @brief Describe OpenCL program binary. + * Do not call clCreateProgramWithBinary() and/or clBuildProgram(). + * + * Caller should guarantee binary buffer lifetime greater than ProgramSource object (and any of its copies). + * + * This kind of binary is not portable between platforms in general - it is specific to OpenCL vendor / device / driver version. + * + * @param module name of program owner module + * @param name unique name of program (module+name is used as key for OpenCL program caching) + * @param binary buffer address. See buffer lifetime requirement in description. + * @param size buffer size + * @param buildOptions additional program-related build options passed to clBuildProgram() + * @return created ProgramSource object + */ + static ProgramSource fromBinary(const String& module, const String& name, + const unsigned char* binary, const size_t size, + const cv::String& buildOptions = cv::String()); + + /** @brief Describe OpenCL program in SPIR format. + * Do not call clCreateProgramWithBinary() and/or clBuildProgram(). + * + * Supports SPIR 1.2 by default (pass '-spir-std=X.Y' in buildOptions to override this behavior) + * + * Caller should guarantee binary buffer lifetime greater than ProgramSource object (and any of its copies). + * + * Programs in this format are portable between OpenCL implementations with 'khr_spir' extension: + * https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/cl_khr_spir.html + * (but they are not portable between different platforms: 32-bit / 64-bit) + * + * Note: these programs can't support vendor specific extensions, like 'cl_intel_subgroups'. + * + * @param module name of program owner module + * @param name unique name of program (module+name is used as key for OpenCL program caching) + * @param binary buffer address. See buffer lifetime requirement in description. + * @param size buffer size + * @param buildOptions additional program-related build options passed to clBuildProgram() + * (these options are added automatically: '-x spir' and '-spir-std=1.2') + * @return created ProgramSource object. + */ + static ProgramSource fromSPIR(const String& module, const String& name, + const unsigned char* binary, const size_t size, + const cv::String& buildOptions = cv::String()); + + //OpenCL 2.1+ only + //static Program fromSPIRV(const String& module, const String& name, + // const unsigned char* binary, const size_t size, + // const cv::String& buildOptions = cv::String()); + + struct Impl; friend struct Impl; + inline Impl* getImpl() const { return (Impl*)p; } +protected: + Impl* p; +}; + +class CV_EXPORTS PlatformInfo +{ +public: + PlatformInfo() CV_NOEXCEPT; + explicit PlatformInfo(void* id); + ~PlatformInfo(); + + PlatformInfo(const PlatformInfo& i); + PlatformInfo& operator =(const PlatformInfo& i); + + String name() const; + String vendor() const; + + /// See CL_PLATFORM_VERSION + String version() const; + int versionMajor() const; + int versionMinor() const; + + int deviceNumber() const; + void getDevice(Device& device, int d) const; + +protected: + struct Impl; + Impl* p; +}; + +CV_EXPORTS const char* convertTypeStr(int sdepth, int ddepth, int cn, char* buf); +CV_EXPORTS const char* typeToStr(int t); +CV_EXPORTS const char* memopTypeToStr(int t); +CV_EXPORTS const char* vecopTypeToStr(int t); +CV_EXPORTS const char* getOpenCLErrorString(int errorCode); +CV_EXPORTS String kernelToStr(InputArray _kernel, int ddepth = -1, const char * name = NULL); +CV_EXPORTS void getPlatfomsInfo(std::vector& platform_info); + + +enum OclVectorStrategy +{ + // all matrices have its own vector width + OCL_VECTOR_OWN = 0, + // all matrices have maximal vector width among all matrices + // (useful for cases when matrices have different data types) + OCL_VECTOR_MAX = 1, + + // default strategy + OCL_VECTOR_DEFAULT = OCL_VECTOR_OWN +}; + +CV_EXPORTS int predictOptimalVectorWidth(InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray(), + OclVectorStrategy strat = OCL_VECTOR_DEFAULT); + +CV_EXPORTS int checkOptimalVectorWidth(const int *vectorWidths, + InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray(), + OclVectorStrategy strat = OCL_VECTOR_DEFAULT); + +// with OCL_VECTOR_MAX strategy +CV_EXPORTS int predictOptimalVectorWidthMax(InputArray src1, InputArray src2 = noArray(), InputArray src3 = noArray(), + InputArray src4 = noArray(), InputArray src5 = noArray(), InputArray src6 = noArray(), + InputArray src7 = noArray(), InputArray src8 = noArray(), InputArray src9 = noArray()); + +CV_EXPORTS void buildOptionsAddMatrixDescription(String& buildOptions, const String& name, InputArray _m); + +class CV_EXPORTS Image2D +{ +public: + Image2D() CV_NOEXCEPT; + + /** + @param src UMat object from which to get image properties and data + @param norm flag to enable the use of normalized channel data types + @param alias flag indicating that the image should alias the src UMat. If true, changes to the + image or src will be reflected in both objects. + */ + explicit Image2D(const UMat &src, bool norm = false, bool alias = false); + Image2D(const Image2D & i); + ~Image2D(); + + Image2D & operator = (const Image2D & i); + + /** Indicates if creating an aliased image should succeed. + Depends on the underlying platform and the dimensions of the UMat. + */ + static bool canCreateAlias(const UMat &u); + + /** Indicates if the image format is supported. + */ + static bool isFormatSupported(int depth, int cn, bool norm); + + void* ptr() const; +protected: + struct Impl; + Impl* p; +}; + +class CV_EXPORTS Timer +{ +public: + Timer(const Queue& q); + ~Timer(); + void start(); + void stop(); + + uint64 durationNS() const; //< duration in nanoseconds + +protected: + struct Impl; + Impl* const p; + +private: + Timer(const Timer&); // disabled + Timer& operator=(const Timer&); // disabled +}; + +CV_EXPORTS MatAllocator* getOpenCLAllocator(); + + +#ifdef __OPENCV_BUILD +namespace internal { + +CV_EXPORTS bool isOpenCLForced(); +#define OCL_FORCE_CHECK(condition) (cv::ocl::internal::isOpenCLForced() || (condition)) + +CV_EXPORTS bool isPerformanceCheckBypassed(); +#define OCL_PERFORMANCE_CHECK(condition) (cv::ocl::internal::isPerformanceCheckBypassed() || (condition)) + +CV_EXPORTS bool isCLBuffer(UMat& u); + +} // namespace internal +#endif + +//! @} + +}} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl_genbase.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl_genbase.hpp new file mode 100644 index 0000000..5334cf1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ocl_genbase.hpp @@ -0,0 +1,69 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPENCL_GENBASE_HPP +#define OPENCV_OPENCL_GENBASE_HPP + +//! @cond IGNORED + +namespace cv { +namespace ocl { + +class ProgramSource; + +namespace internal { + +struct CV_EXPORTS ProgramEntry +{ + const char* module; + const char* name; + const char* programCode; + const char* programHash; + ProgramSource* pProgramSource; + + operator ProgramSource& () const; +}; + +} } } // namespace + +//! @endcond + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/ocl_defs.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/ocl_defs.hpp new file mode 100644 index 0000000..605a65f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/ocl_defs.hpp @@ -0,0 +1,75 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_CORE_OPENCL_DEFS_HPP +#define OPENCV_CORE_OPENCL_DEFS_HPP + +#include "opencv2/core/utility.hpp" +#include "cvconfig.h" + +namespace cv { namespace ocl { +#ifdef HAVE_OPENCL +/// Call is similar to useOpenCL() but doesn't try to load OpenCL runtime or create OpenCL context +CV_EXPORTS bool isOpenCLActivated(); +#else +static inline bool isOpenCLActivated() { return false; } +#endif +}} // namespace + + +//#define CV_OPENCL_RUN_ASSERT + +#ifdef HAVE_OPENCL + +#ifdef CV_OPENCL_RUN_VERBOSE +#define CV_OCL_RUN_(condition, func, ...) \ + { \ + if (cv::ocl::isOpenCLActivated() && (condition) && func) \ + { \ + printf("%s: OpenCL implementation is running\n", CV_Func); \ + fflush(stdout); \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + return __VA_ARGS__; \ + } \ + else \ + { \ + printf("%s: Plain implementation is running\n", CV_Func); \ + fflush(stdout); \ + } \ + } +#elif defined CV_OPENCL_RUN_ASSERT +#define CV_OCL_RUN_(condition, func, ...) \ + { \ + if (cv::ocl::isOpenCLActivated() && (condition)) \ + { \ + if(func) \ + { \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + } \ + else \ + { \ + CV_Error(cv::Error::StsAssert, #func); \ + } \ + return __VA_ARGS__; \ + } \ + } +#else +#define CV_OCL_RUN_(condition, func, ...) \ + if (cv::ocl::isOpenCLActivated() && (condition) && func) \ + { \ + CV_IMPL_ADD(CV_IMPL_OCL); \ + return __VA_ARGS__; \ + } +#endif + +#else +#define CV_OCL_RUN_(condition, func, ...) +#endif + +#define CV_OCL_RUN(condition, func) CV_OCL_RUN_(condition, func) + +#endif // OPENCV_CORE_OPENCL_DEFS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_info.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_info.hpp new file mode 100644 index 0000000..5e5c846 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_info.hpp @@ -0,0 +1,205 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include + +#include +#include + +#ifndef DUMP_CONFIG_PROPERTY +#define DUMP_CONFIG_PROPERTY(...) +#endif + +#ifndef DUMP_MESSAGE_STDOUT +#define DUMP_MESSAGE_STDOUT(...) do { std::cout << __VA_ARGS__ << std::endl; } while (false) +#endif + +namespace cv { + +namespace { +static std::string bytesToStringRepr(size_t value) +{ + size_t b = value % 1024; + value /= 1024; + + size_t kb = value % 1024; + value /= 1024; + + size_t mb = value % 1024; + value /= 1024; + + size_t gb = value; + + std::ostringstream stream; + + if (gb > 0) + stream << gb << " GB "; + if (mb > 0) + stream << mb << " MB "; + if (kb > 0) + stream << kb << " KB "; + if (b > 0) + stream << b << " B"; + + std::string s = stream.str(); + if (s[s.size() - 1] == ' ') + s = s.substr(0, s.size() - 1); + return s; +} + +static String getDeviceTypeString(const cv::ocl::Device& device) +{ + if (device.type() == cv::ocl::Device::TYPE_CPU) { + return "CPU"; + } + + if (device.type() == cv::ocl::Device::TYPE_GPU) { + if (device.hostUnifiedMemory()) { + return "iGPU"; + } else { + return "dGPU"; + } + } + + return "unknown"; +} +} // namespace + +static void dumpOpenCLInformation() +{ + using namespace cv::ocl; + + try + { + if (!haveOpenCL() || !useOpenCL()) + { + DUMP_MESSAGE_STDOUT("OpenCL is disabled"); + DUMP_CONFIG_PROPERTY("cv_ocl", "disabled"); + return; + } + + std::vector platforms; + cv::ocl::getPlatfomsInfo(platforms); + if (platforms.empty()) + { + DUMP_MESSAGE_STDOUT("OpenCL is not available"); + DUMP_CONFIG_PROPERTY("cv_ocl", "not available"); + return; + } + + DUMP_MESSAGE_STDOUT("OpenCL Platforms: "); + for (size_t i = 0; i < platforms.size(); i++) + { + const PlatformInfo* platform = &platforms[i]; + DUMP_MESSAGE_STDOUT(" " << platform->name()); + Device current_device; + for (int j = 0; j < platform->deviceNumber(); j++) + { + platform->getDevice(current_device, j); + String deviceTypeStr = getDeviceTypeString(current_device); + DUMP_MESSAGE_STDOUT( " " << deviceTypeStr << ": " << current_device.name() << " (" << current_device.version() << ")"); + DUMP_CONFIG_PROPERTY( cv::format("cv_ocl_platform_%d_device_%d", (int)i, j ), + cv::format("(Platform=%s)(Type=%s)(Name=%s)(Version=%s)", + platform->name().c_str(), deviceTypeStr.c_str(), current_device.name().c_str(), current_device.version().c_str()) ); + } + } + const Device& device = Device::getDefault(); + if (!device.available()) + CV_Error(Error::OpenCLInitError, "OpenCL device is not available"); + + DUMP_MESSAGE_STDOUT("Current OpenCL device: "); + + String deviceTypeStr = getDeviceTypeString(device); + DUMP_MESSAGE_STDOUT(" Type = " << deviceTypeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceType", deviceTypeStr); + + DUMP_MESSAGE_STDOUT(" Name = " << device.name()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceName", device.name()); + + DUMP_MESSAGE_STDOUT(" Version = " << device.version()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_deviceVersion", device.version()); + + DUMP_MESSAGE_STDOUT(" Driver version = " << device.driverVersion()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_driverVersion", device.driverVersion()); + + DUMP_MESSAGE_STDOUT(" Address bits = " << device.addressBits()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_addressBits", device.addressBits()); + + DUMP_MESSAGE_STDOUT(" Compute units = " << device.maxComputeUnits()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxComputeUnits", device.maxComputeUnits()); + + DUMP_MESSAGE_STDOUT(" Max work group size = " << device.maxWorkGroupSize()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxWorkGroupSize", device.maxWorkGroupSize()); + + std::string localMemorySizeStr = bytesToStringRepr(device.localMemSize()); + DUMP_MESSAGE_STDOUT(" Local memory size = " << localMemorySizeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_localMemSize", device.localMemSize()); + + std::string maxMemAllocSizeStr = bytesToStringRepr(device.maxMemAllocSize()); + DUMP_MESSAGE_STDOUT(" Max memory allocation size = " << maxMemAllocSizeStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_maxMemAllocSize", device.maxMemAllocSize()); + + const char* doubleSupportStr = device.doubleFPConfig() > 0 ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Double support = " << doubleSupportStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_haveDoubleSupport", device.doubleFPConfig() > 0); + + const char* isUnifiedMemoryStr = device.hostUnifiedMemory() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Host unified memory = " << isUnifiedMemoryStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_hostUnifiedMemory", device.hostUnifiedMemory()); + + DUMP_MESSAGE_STDOUT(" Device extensions:"); + String extensionsStr = device.extensions(); + size_t pos = 0; + while (pos < extensionsStr.size()) + { + size_t pos2 = extensionsStr.find(' ', pos); + if (pos2 == String::npos) + pos2 = extensionsStr.size(); + if (pos2 > pos) + { + String extensionName = extensionsStr.substr(pos, pos2 - pos); + DUMP_MESSAGE_STDOUT(" " << extensionName); + } + pos = pos2 + 1; + } + DUMP_CONFIG_PROPERTY("cv_ocl_current_extensions", extensionsStr); + + const char* haveAmdBlasStr = haveAmdBlas() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Has AMD Blas = " << haveAmdBlasStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_AmdBlas", haveAmdBlas()); + + const char* haveAmdFftStr = haveAmdFft() ? "Yes" : "No"; + DUMP_MESSAGE_STDOUT(" Has AMD Fft = " << haveAmdFftStr); + DUMP_CONFIG_PROPERTY("cv_ocl_current_AmdFft", haveAmdFft()); + + + DUMP_MESSAGE_STDOUT(" Preferred vector width char = " << device.preferredVectorWidthChar()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthChar", device.preferredVectorWidthChar()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width short = " << device.preferredVectorWidthShort()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthShort", device.preferredVectorWidthShort()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width int = " << device.preferredVectorWidthInt()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthInt", device.preferredVectorWidthInt()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width long = " << device.preferredVectorWidthLong()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthLong", device.preferredVectorWidthLong()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width float = " << device.preferredVectorWidthFloat()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthFloat", device.preferredVectorWidthFloat()); + + DUMP_MESSAGE_STDOUT(" Preferred vector width double = " << device.preferredVectorWidthDouble()); + DUMP_CONFIG_PROPERTY("cv_ocl_current_preferredVectorWidthDouble", device.preferredVectorWidthDouble()); + } + catch (...) + { + DUMP_MESSAGE_STDOUT("Exception. Can't dump OpenCL info"); + DUMP_MESSAGE_STDOUT("OpenCL device not available"); + DUMP_CONFIG_PROPERTY("cv_ocl", "not available"); + } +} +#undef DUMP_MESSAGE_STDOUT +#undef DUMP_CONFIG_PROPERTY + +} // namespace diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_svm.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_svm.hpp new file mode 100644 index 0000000..7453082 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/opencl_svm.hpp @@ -0,0 +1,81 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OPENCL_SVM_HPP +#define OPENCV_CORE_OPENCL_SVM_HPP + +// +// Internal usage only (binary compatibility is not guaranteed) +// +#ifndef __OPENCV_BUILD +#error Internal header file +#endif + +#if defined(HAVE_OPENCL) && defined(HAVE_OPENCL_SVM) +#include "runtime/opencl_core.hpp" +#include "runtime/opencl_svm_20.hpp" +#include "runtime/opencl_svm_hsa_extension.hpp" + +namespace cv { namespace ocl { namespace svm { + +struct SVMCapabilities +{ + enum Value + { + SVM_COARSE_GRAIN_BUFFER = (1 << 0), + SVM_FINE_GRAIN_BUFFER = (1 << 1), + SVM_FINE_GRAIN_SYSTEM = (1 << 2), + SVM_ATOMICS = (1 << 3), + }; + int value_; + + SVMCapabilities(int capabilities = 0) : value_(capabilities) { } + operator int() const { return value_; } + + inline bool isNoSVMSupport() const { return value_ == 0; } + inline bool isSupportCoarseGrainBuffer() const { return (value_ & SVM_COARSE_GRAIN_BUFFER) != 0; } + inline bool isSupportFineGrainBuffer() const { return (value_ & SVM_FINE_GRAIN_BUFFER) != 0; } + inline bool isSupportFineGrainSystem() const { return (value_ & SVM_FINE_GRAIN_SYSTEM) != 0; } + inline bool isSupportAtomics() const { return (value_ & SVM_ATOMICS) != 0; } +}; + +CV_EXPORTS const SVMCapabilities getSVMCapabilitites(const ocl::Context& context); + +struct SVMFunctions +{ + clSVMAllocAMD_fn fn_clSVMAlloc; + clSVMFreeAMD_fn fn_clSVMFree; + clSetKernelArgSVMPointerAMD_fn fn_clSetKernelArgSVMPointer; + //clSetKernelExecInfoAMD_fn fn_clSetKernelExecInfo; + //clEnqueueSVMFreeAMD_fn fn_clEnqueueSVMFree; + clEnqueueSVMMemcpyAMD_fn fn_clEnqueueSVMMemcpy; + clEnqueueSVMMemFillAMD_fn fn_clEnqueueSVMMemFill; + clEnqueueSVMMapAMD_fn fn_clEnqueueSVMMap; + clEnqueueSVMUnmapAMD_fn fn_clEnqueueSVMUnmap; + + inline SVMFunctions() + : fn_clSVMAlloc(NULL), fn_clSVMFree(NULL), + fn_clSetKernelArgSVMPointer(NULL), /*fn_clSetKernelExecInfo(NULL),*/ + /*fn_clEnqueueSVMFree(NULL),*/ fn_clEnqueueSVMMemcpy(NULL), fn_clEnqueueSVMMemFill(NULL), + fn_clEnqueueSVMMap(NULL), fn_clEnqueueSVMUnmap(NULL) + { + // nothing + } + + inline bool isValid() const + { + return fn_clSVMAlloc != NULL && fn_clSVMFree && fn_clSetKernelArgSVMPointer && + /*fn_clSetKernelExecInfo && fn_clEnqueueSVMFree &&*/ fn_clEnqueueSVMMemcpy && + fn_clEnqueueSVMMemFill && fn_clEnqueueSVMMap && fn_clEnqueueSVMUnmap; + } +}; + +// We should guarantee that SVMFunctions lifetime is not less than context's lifetime +CV_EXPORTS const SVMFunctions* getSVMFunctions(const ocl::Context& context); + +CV_EXPORTS bool useSVM(UMatUsageFlags usageFlags); + +}}} //namespace cv::ocl::svm +#endif + +#endif // OPENCV_CORE_OPENCL_SVM_HPP +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp new file mode 100644 index 0000000..65c8493 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdblas.hpp @@ -0,0 +1,714 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP +#error "Invalid usage" +#endif + +// generated by parser_clamdblas.py +#define clAmdBlasAddScratchImage clAmdBlasAddScratchImage_ +#define clAmdBlasCaxpy clAmdBlasCaxpy_ +#define clAmdBlasCcopy clAmdBlasCcopy_ +#define clAmdBlasCdotc clAmdBlasCdotc_ +#define clAmdBlasCdotu clAmdBlasCdotu_ +#define clAmdBlasCgbmv clAmdBlasCgbmv_ +#define clAmdBlasCgemm clAmdBlasCgemm_ +#define clAmdBlasCgemmEx clAmdBlasCgemmEx_ +#define clAmdBlasCgemv clAmdBlasCgemv_ +#define clAmdBlasCgemvEx clAmdBlasCgemvEx_ +#define clAmdBlasCgerc clAmdBlasCgerc_ +#define clAmdBlasCgeru clAmdBlasCgeru_ +#define clAmdBlasChbmv clAmdBlasChbmv_ +#define clAmdBlasChemm clAmdBlasChemm_ +#define clAmdBlasChemv clAmdBlasChemv_ +#define clAmdBlasCher clAmdBlasCher_ +#define clAmdBlasCher2 clAmdBlasCher2_ +#define clAmdBlasCher2k clAmdBlasCher2k_ +#define clAmdBlasCherk clAmdBlasCherk_ +#define clAmdBlasChpmv clAmdBlasChpmv_ +#define clAmdBlasChpr clAmdBlasChpr_ +#define clAmdBlasChpr2 clAmdBlasChpr2_ +#define clAmdBlasCrotg clAmdBlasCrotg_ +#define clAmdBlasCscal clAmdBlasCscal_ +#define clAmdBlasCsrot clAmdBlasCsrot_ +#define clAmdBlasCsscal clAmdBlasCsscal_ +#define clAmdBlasCswap clAmdBlasCswap_ +#define clAmdBlasCsymm clAmdBlasCsymm_ +#define clAmdBlasCsyr2k clAmdBlasCsyr2k_ +#define clAmdBlasCsyr2kEx clAmdBlasCsyr2kEx_ +#define clAmdBlasCsyrk clAmdBlasCsyrk_ +#define clAmdBlasCsyrkEx clAmdBlasCsyrkEx_ +#define clAmdBlasCtbmv clAmdBlasCtbmv_ +#define clAmdBlasCtbsv clAmdBlasCtbsv_ +#define clAmdBlasCtpmv clAmdBlasCtpmv_ +#define clAmdBlasCtpsv clAmdBlasCtpsv_ +#define clAmdBlasCtrmm clAmdBlasCtrmm_ +#define clAmdBlasCtrmmEx clAmdBlasCtrmmEx_ +#define clAmdBlasCtrmv clAmdBlasCtrmv_ +#define clAmdBlasCtrsm clAmdBlasCtrsm_ +#define clAmdBlasCtrsmEx clAmdBlasCtrsmEx_ +#define clAmdBlasCtrsv clAmdBlasCtrsv_ +#define clAmdBlasDasum clAmdBlasDasum_ +#define clAmdBlasDaxpy clAmdBlasDaxpy_ +#define clAmdBlasDcopy clAmdBlasDcopy_ +#define clAmdBlasDdot clAmdBlasDdot_ +#define clAmdBlasDgbmv clAmdBlasDgbmv_ +#define clAmdBlasDgemm clAmdBlasDgemm_ +#define clAmdBlasDgemmEx clAmdBlasDgemmEx_ +#define clAmdBlasDgemv clAmdBlasDgemv_ +#define clAmdBlasDgemvEx clAmdBlasDgemvEx_ +#define clAmdBlasDger clAmdBlasDger_ +#define clAmdBlasDnrm2 clAmdBlasDnrm2_ +#define clAmdBlasDrot clAmdBlasDrot_ +#define clAmdBlasDrotg clAmdBlasDrotg_ +#define clAmdBlasDrotm clAmdBlasDrotm_ +#define clAmdBlasDrotmg clAmdBlasDrotmg_ +#define clAmdBlasDsbmv clAmdBlasDsbmv_ +#define clAmdBlasDscal clAmdBlasDscal_ +#define clAmdBlasDspmv clAmdBlasDspmv_ +#define clAmdBlasDspr clAmdBlasDspr_ +#define clAmdBlasDspr2 clAmdBlasDspr2_ +#define clAmdBlasDswap clAmdBlasDswap_ +#define clAmdBlasDsymm clAmdBlasDsymm_ +#define clAmdBlasDsymv clAmdBlasDsymv_ +#define clAmdBlasDsymvEx clAmdBlasDsymvEx_ +#define clAmdBlasDsyr clAmdBlasDsyr_ +#define clAmdBlasDsyr2 clAmdBlasDsyr2_ +#define clAmdBlasDsyr2k clAmdBlasDsyr2k_ +#define clAmdBlasDsyr2kEx clAmdBlasDsyr2kEx_ +#define clAmdBlasDsyrk clAmdBlasDsyrk_ +#define clAmdBlasDsyrkEx clAmdBlasDsyrkEx_ +#define clAmdBlasDtbmv clAmdBlasDtbmv_ +#define clAmdBlasDtbsv clAmdBlasDtbsv_ +#define clAmdBlasDtpmv clAmdBlasDtpmv_ +#define clAmdBlasDtpsv clAmdBlasDtpsv_ +#define clAmdBlasDtrmm clAmdBlasDtrmm_ +#define clAmdBlasDtrmmEx clAmdBlasDtrmmEx_ +#define clAmdBlasDtrmv clAmdBlasDtrmv_ +#define clAmdBlasDtrsm clAmdBlasDtrsm_ +#define clAmdBlasDtrsmEx clAmdBlasDtrsmEx_ +#define clAmdBlasDtrsv clAmdBlasDtrsv_ +#define clAmdBlasDzasum clAmdBlasDzasum_ +#define clAmdBlasDznrm2 clAmdBlasDznrm2_ +#define clAmdBlasGetVersion clAmdBlasGetVersion_ +#define clAmdBlasRemoveScratchImage clAmdBlasRemoveScratchImage_ +#define clAmdBlasSasum clAmdBlasSasum_ +#define clAmdBlasSaxpy clAmdBlasSaxpy_ +#define clAmdBlasScasum clAmdBlasScasum_ +#define clAmdBlasScnrm2 clAmdBlasScnrm2_ +#define clAmdBlasScopy clAmdBlasScopy_ +#define clAmdBlasSdot clAmdBlasSdot_ +#define clAmdBlasSetup clAmdBlasSetup_ +#define clAmdBlasSgbmv clAmdBlasSgbmv_ +#define clAmdBlasSgemm clAmdBlasSgemm_ +#define clAmdBlasSgemmEx clAmdBlasSgemmEx_ +#define clAmdBlasSgemv clAmdBlasSgemv_ +#define clAmdBlasSgemvEx clAmdBlasSgemvEx_ +#define clAmdBlasSger clAmdBlasSger_ +#define clAmdBlasSnrm2 clAmdBlasSnrm2_ +#define clAmdBlasSrot clAmdBlasSrot_ +#define clAmdBlasSrotg clAmdBlasSrotg_ +#define clAmdBlasSrotm clAmdBlasSrotm_ +#define clAmdBlasSrotmg clAmdBlasSrotmg_ +#define clAmdBlasSsbmv clAmdBlasSsbmv_ +#define clAmdBlasSscal clAmdBlasSscal_ +#define clAmdBlasSspmv clAmdBlasSspmv_ +#define clAmdBlasSspr clAmdBlasSspr_ +#define clAmdBlasSspr2 clAmdBlasSspr2_ +#define clAmdBlasSswap clAmdBlasSswap_ +#define clAmdBlasSsymm clAmdBlasSsymm_ +#define clAmdBlasSsymv clAmdBlasSsymv_ +#define clAmdBlasSsymvEx clAmdBlasSsymvEx_ +#define clAmdBlasSsyr clAmdBlasSsyr_ +#define clAmdBlasSsyr2 clAmdBlasSsyr2_ +#define clAmdBlasSsyr2k clAmdBlasSsyr2k_ +#define clAmdBlasSsyr2kEx clAmdBlasSsyr2kEx_ +#define clAmdBlasSsyrk clAmdBlasSsyrk_ +#define clAmdBlasSsyrkEx clAmdBlasSsyrkEx_ +#define clAmdBlasStbmv clAmdBlasStbmv_ +#define clAmdBlasStbsv clAmdBlasStbsv_ +#define clAmdBlasStpmv clAmdBlasStpmv_ +#define clAmdBlasStpsv clAmdBlasStpsv_ +#define clAmdBlasStrmm clAmdBlasStrmm_ +#define clAmdBlasStrmmEx clAmdBlasStrmmEx_ +#define clAmdBlasStrmv clAmdBlasStrmv_ +#define clAmdBlasStrsm clAmdBlasStrsm_ +#define clAmdBlasStrsmEx clAmdBlasStrsmEx_ +#define clAmdBlasStrsv clAmdBlasStrsv_ +#define clAmdBlasTeardown clAmdBlasTeardown_ +#define clAmdBlasZaxpy clAmdBlasZaxpy_ +#define clAmdBlasZcopy clAmdBlasZcopy_ +#define clAmdBlasZdotc clAmdBlasZdotc_ +#define clAmdBlasZdotu clAmdBlasZdotu_ +#define clAmdBlasZdrot clAmdBlasZdrot_ +#define clAmdBlasZdscal clAmdBlasZdscal_ +#define clAmdBlasZgbmv clAmdBlasZgbmv_ +#define clAmdBlasZgemm clAmdBlasZgemm_ +#define clAmdBlasZgemmEx clAmdBlasZgemmEx_ +#define clAmdBlasZgemv clAmdBlasZgemv_ +#define clAmdBlasZgemvEx clAmdBlasZgemvEx_ +#define clAmdBlasZgerc clAmdBlasZgerc_ +#define clAmdBlasZgeru clAmdBlasZgeru_ +#define clAmdBlasZhbmv clAmdBlasZhbmv_ +#define clAmdBlasZhemm clAmdBlasZhemm_ +#define clAmdBlasZhemv clAmdBlasZhemv_ +#define clAmdBlasZher clAmdBlasZher_ +#define clAmdBlasZher2 clAmdBlasZher2_ +#define clAmdBlasZher2k clAmdBlasZher2k_ +#define clAmdBlasZherk clAmdBlasZherk_ +#define clAmdBlasZhpmv clAmdBlasZhpmv_ +#define clAmdBlasZhpr clAmdBlasZhpr_ +#define clAmdBlasZhpr2 clAmdBlasZhpr2_ +#define clAmdBlasZrotg clAmdBlasZrotg_ +#define clAmdBlasZscal clAmdBlasZscal_ +#define clAmdBlasZswap clAmdBlasZswap_ +#define clAmdBlasZsymm clAmdBlasZsymm_ +#define clAmdBlasZsyr2k clAmdBlasZsyr2k_ +#define clAmdBlasZsyr2kEx clAmdBlasZsyr2kEx_ +#define clAmdBlasZsyrk clAmdBlasZsyrk_ +#define clAmdBlasZsyrkEx clAmdBlasZsyrkEx_ +#define clAmdBlasZtbmv clAmdBlasZtbmv_ +#define clAmdBlasZtbsv clAmdBlasZtbsv_ +#define clAmdBlasZtpmv clAmdBlasZtpmv_ +#define clAmdBlasZtpsv clAmdBlasZtpsv_ +#define clAmdBlasZtrmm clAmdBlasZtrmm_ +#define clAmdBlasZtrmmEx clAmdBlasZtrmmEx_ +#define clAmdBlasZtrmv clAmdBlasZtrmv_ +#define clAmdBlasZtrsm clAmdBlasZtrsm_ +#define clAmdBlasZtrsmEx clAmdBlasZtrsmEx_ +#define clAmdBlasZtrsv clAmdBlasZtrsv_ +#define clAmdBlasiCamax clAmdBlasiCamax_ +#define clAmdBlasiDamax clAmdBlasiDamax_ +#define clAmdBlasiSamax clAmdBlasiSamax_ +#define clAmdBlasiZamax clAmdBlasiZamax_ + +#include + +// generated by parser_clamdblas.py +#undef clAmdBlasAddScratchImage +//#define clAmdBlasAddScratchImage clAmdBlasAddScratchImage_pfn +#undef clAmdBlasCaxpy +//#define clAmdBlasCaxpy clAmdBlasCaxpy_pfn +#undef clAmdBlasCcopy +//#define clAmdBlasCcopy clAmdBlasCcopy_pfn +#undef clAmdBlasCdotc +//#define clAmdBlasCdotc clAmdBlasCdotc_pfn +#undef clAmdBlasCdotu +//#define clAmdBlasCdotu clAmdBlasCdotu_pfn +#undef clAmdBlasCgbmv +//#define clAmdBlasCgbmv clAmdBlasCgbmv_pfn +#undef clAmdBlasCgemm +//#define clAmdBlasCgemm clAmdBlasCgemm_pfn +#undef clAmdBlasCgemmEx +#define clAmdBlasCgemmEx clAmdBlasCgemmEx_pfn +#undef clAmdBlasCgemv +//#define clAmdBlasCgemv clAmdBlasCgemv_pfn +#undef clAmdBlasCgemvEx +//#define clAmdBlasCgemvEx clAmdBlasCgemvEx_pfn +#undef clAmdBlasCgerc +//#define clAmdBlasCgerc clAmdBlasCgerc_pfn +#undef clAmdBlasCgeru +//#define clAmdBlasCgeru clAmdBlasCgeru_pfn +#undef clAmdBlasChbmv +//#define clAmdBlasChbmv clAmdBlasChbmv_pfn +#undef clAmdBlasChemm +//#define clAmdBlasChemm clAmdBlasChemm_pfn +#undef clAmdBlasChemv +//#define clAmdBlasChemv clAmdBlasChemv_pfn +#undef clAmdBlasCher +//#define clAmdBlasCher clAmdBlasCher_pfn +#undef clAmdBlasCher2 +//#define clAmdBlasCher2 clAmdBlasCher2_pfn +#undef clAmdBlasCher2k +//#define clAmdBlasCher2k clAmdBlasCher2k_pfn +#undef clAmdBlasCherk +//#define clAmdBlasCherk clAmdBlasCherk_pfn +#undef clAmdBlasChpmv +//#define clAmdBlasChpmv clAmdBlasChpmv_pfn +#undef clAmdBlasChpr +//#define clAmdBlasChpr clAmdBlasChpr_pfn +#undef clAmdBlasChpr2 +//#define clAmdBlasChpr2 clAmdBlasChpr2_pfn +#undef clAmdBlasCrotg +//#define clAmdBlasCrotg clAmdBlasCrotg_pfn +#undef clAmdBlasCscal +//#define clAmdBlasCscal clAmdBlasCscal_pfn +#undef clAmdBlasCsrot +//#define clAmdBlasCsrot clAmdBlasCsrot_pfn +#undef clAmdBlasCsscal +//#define clAmdBlasCsscal clAmdBlasCsscal_pfn +#undef clAmdBlasCswap +//#define clAmdBlasCswap clAmdBlasCswap_pfn +#undef clAmdBlasCsymm +//#define clAmdBlasCsymm clAmdBlasCsymm_pfn +#undef clAmdBlasCsyr2k +//#define clAmdBlasCsyr2k clAmdBlasCsyr2k_pfn +#undef clAmdBlasCsyr2kEx +//#define clAmdBlasCsyr2kEx clAmdBlasCsyr2kEx_pfn +#undef clAmdBlasCsyrk +//#define clAmdBlasCsyrk clAmdBlasCsyrk_pfn +#undef clAmdBlasCsyrkEx +//#define clAmdBlasCsyrkEx clAmdBlasCsyrkEx_pfn +#undef clAmdBlasCtbmv +//#define clAmdBlasCtbmv clAmdBlasCtbmv_pfn +#undef clAmdBlasCtbsv +//#define clAmdBlasCtbsv clAmdBlasCtbsv_pfn +#undef clAmdBlasCtpmv +//#define clAmdBlasCtpmv clAmdBlasCtpmv_pfn +#undef clAmdBlasCtpsv +//#define clAmdBlasCtpsv clAmdBlasCtpsv_pfn +#undef clAmdBlasCtrmm +//#define clAmdBlasCtrmm clAmdBlasCtrmm_pfn +#undef clAmdBlasCtrmmEx +//#define clAmdBlasCtrmmEx clAmdBlasCtrmmEx_pfn +#undef clAmdBlasCtrmv +//#define clAmdBlasCtrmv clAmdBlasCtrmv_pfn +#undef clAmdBlasCtrsm +//#define clAmdBlasCtrsm clAmdBlasCtrsm_pfn +#undef clAmdBlasCtrsmEx +//#define clAmdBlasCtrsmEx clAmdBlasCtrsmEx_pfn +#undef clAmdBlasCtrsv +//#define clAmdBlasCtrsv clAmdBlasCtrsv_pfn +#undef clAmdBlasDasum +//#define clAmdBlasDasum clAmdBlasDasum_pfn +#undef clAmdBlasDaxpy +//#define clAmdBlasDaxpy clAmdBlasDaxpy_pfn +#undef clAmdBlasDcopy +//#define clAmdBlasDcopy clAmdBlasDcopy_pfn +#undef clAmdBlasDdot +//#define clAmdBlasDdot clAmdBlasDdot_pfn +#undef clAmdBlasDgbmv +//#define clAmdBlasDgbmv clAmdBlasDgbmv_pfn +#undef clAmdBlasDgemm +//#define clAmdBlasDgemm clAmdBlasDgemm_pfn +#undef clAmdBlasDgemmEx +#define clAmdBlasDgemmEx clAmdBlasDgemmEx_pfn +#undef clAmdBlasDgemv +//#define clAmdBlasDgemv clAmdBlasDgemv_pfn +#undef clAmdBlasDgemvEx +//#define clAmdBlasDgemvEx clAmdBlasDgemvEx_pfn +#undef clAmdBlasDger +//#define clAmdBlasDger clAmdBlasDger_pfn +#undef clAmdBlasDnrm2 +//#define clAmdBlasDnrm2 clAmdBlasDnrm2_pfn +#undef clAmdBlasDrot +//#define clAmdBlasDrot clAmdBlasDrot_pfn +#undef clAmdBlasDrotg +//#define clAmdBlasDrotg clAmdBlasDrotg_pfn +#undef clAmdBlasDrotm +//#define clAmdBlasDrotm clAmdBlasDrotm_pfn +#undef clAmdBlasDrotmg +//#define clAmdBlasDrotmg clAmdBlasDrotmg_pfn +#undef clAmdBlasDsbmv +//#define clAmdBlasDsbmv clAmdBlasDsbmv_pfn +#undef clAmdBlasDscal +//#define clAmdBlasDscal clAmdBlasDscal_pfn +#undef clAmdBlasDspmv +//#define clAmdBlasDspmv clAmdBlasDspmv_pfn +#undef clAmdBlasDspr +//#define clAmdBlasDspr clAmdBlasDspr_pfn +#undef clAmdBlasDspr2 +//#define clAmdBlasDspr2 clAmdBlasDspr2_pfn +#undef clAmdBlasDswap +//#define clAmdBlasDswap clAmdBlasDswap_pfn +#undef clAmdBlasDsymm +//#define clAmdBlasDsymm clAmdBlasDsymm_pfn +#undef clAmdBlasDsymv +//#define clAmdBlasDsymv clAmdBlasDsymv_pfn +#undef clAmdBlasDsymvEx +//#define clAmdBlasDsymvEx clAmdBlasDsymvEx_pfn +#undef clAmdBlasDsyr +//#define clAmdBlasDsyr clAmdBlasDsyr_pfn +#undef clAmdBlasDsyr2 +//#define clAmdBlasDsyr2 clAmdBlasDsyr2_pfn +#undef clAmdBlasDsyr2k +//#define clAmdBlasDsyr2k clAmdBlasDsyr2k_pfn +#undef clAmdBlasDsyr2kEx +//#define clAmdBlasDsyr2kEx clAmdBlasDsyr2kEx_pfn +#undef clAmdBlasDsyrk +//#define clAmdBlasDsyrk clAmdBlasDsyrk_pfn +#undef clAmdBlasDsyrkEx +//#define clAmdBlasDsyrkEx clAmdBlasDsyrkEx_pfn +#undef clAmdBlasDtbmv +//#define clAmdBlasDtbmv clAmdBlasDtbmv_pfn +#undef clAmdBlasDtbsv +//#define clAmdBlasDtbsv clAmdBlasDtbsv_pfn +#undef clAmdBlasDtpmv +//#define clAmdBlasDtpmv clAmdBlasDtpmv_pfn +#undef clAmdBlasDtpsv +//#define clAmdBlasDtpsv clAmdBlasDtpsv_pfn +#undef clAmdBlasDtrmm +//#define clAmdBlasDtrmm clAmdBlasDtrmm_pfn +#undef clAmdBlasDtrmmEx +//#define clAmdBlasDtrmmEx clAmdBlasDtrmmEx_pfn +#undef clAmdBlasDtrmv +//#define clAmdBlasDtrmv clAmdBlasDtrmv_pfn +#undef clAmdBlasDtrsm +//#define clAmdBlasDtrsm clAmdBlasDtrsm_pfn +#undef clAmdBlasDtrsmEx +//#define clAmdBlasDtrsmEx clAmdBlasDtrsmEx_pfn +#undef clAmdBlasDtrsv +//#define clAmdBlasDtrsv clAmdBlasDtrsv_pfn +#undef clAmdBlasDzasum +//#define clAmdBlasDzasum clAmdBlasDzasum_pfn +#undef clAmdBlasDznrm2 +//#define clAmdBlasDznrm2 clAmdBlasDznrm2_pfn +#undef clAmdBlasGetVersion +//#define clAmdBlasGetVersion clAmdBlasGetVersion_pfn +#undef clAmdBlasRemoveScratchImage +//#define clAmdBlasRemoveScratchImage clAmdBlasRemoveScratchImage_pfn +#undef clAmdBlasSasum +//#define clAmdBlasSasum clAmdBlasSasum_pfn +#undef clAmdBlasSaxpy +//#define clAmdBlasSaxpy clAmdBlasSaxpy_pfn +#undef clAmdBlasScasum +//#define clAmdBlasScasum clAmdBlasScasum_pfn +#undef clAmdBlasScnrm2 +//#define clAmdBlasScnrm2 clAmdBlasScnrm2_pfn +#undef clAmdBlasScopy +//#define clAmdBlasScopy clAmdBlasScopy_pfn +#undef clAmdBlasSdot +//#define clAmdBlasSdot clAmdBlasSdot_pfn +#undef clAmdBlasSetup +#define clAmdBlasSetup clAmdBlasSetup_pfn +#undef clAmdBlasSgbmv +//#define clAmdBlasSgbmv clAmdBlasSgbmv_pfn +#undef clAmdBlasSgemm +//#define clAmdBlasSgemm clAmdBlasSgemm_pfn +#undef clAmdBlasSgemmEx +#define clAmdBlasSgemmEx clAmdBlasSgemmEx_pfn +#undef clAmdBlasSgemv +//#define clAmdBlasSgemv clAmdBlasSgemv_pfn +#undef clAmdBlasSgemvEx +//#define clAmdBlasSgemvEx clAmdBlasSgemvEx_pfn +#undef clAmdBlasSger +//#define clAmdBlasSger clAmdBlasSger_pfn +#undef clAmdBlasSnrm2 +//#define clAmdBlasSnrm2 clAmdBlasSnrm2_pfn +#undef clAmdBlasSrot +//#define clAmdBlasSrot clAmdBlasSrot_pfn +#undef clAmdBlasSrotg +//#define clAmdBlasSrotg clAmdBlasSrotg_pfn +#undef clAmdBlasSrotm +//#define clAmdBlasSrotm clAmdBlasSrotm_pfn +#undef clAmdBlasSrotmg +//#define clAmdBlasSrotmg clAmdBlasSrotmg_pfn +#undef clAmdBlasSsbmv +//#define clAmdBlasSsbmv clAmdBlasSsbmv_pfn +#undef clAmdBlasSscal +//#define clAmdBlasSscal clAmdBlasSscal_pfn +#undef clAmdBlasSspmv +//#define clAmdBlasSspmv clAmdBlasSspmv_pfn +#undef clAmdBlasSspr +//#define clAmdBlasSspr clAmdBlasSspr_pfn +#undef clAmdBlasSspr2 +//#define clAmdBlasSspr2 clAmdBlasSspr2_pfn +#undef clAmdBlasSswap +//#define clAmdBlasSswap clAmdBlasSswap_pfn +#undef clAmdBlasSsymm +//#define clAmdBlasSsymm clAmdBlasSsymm_pfn +#undef clAmdBlasSsymv +//#define clAmdBlasSsymv clAmdBlasSsymv_pfn +#undef clAmdBlasSsymvEx +//#define clAmdBlasSsymvEx clAmdBlasSsymvEx_pfn +#undef clAmdBlasSsyr +//#define clAmdBlasSsyr clAmdBlasSsyr_pfn +#undef clAmdBlasSsyr2 +//#define clAmdBlasSsyr2 clAmdBlasSsyr2_pfn +#undef clAmdBlasSsyr2k +//#define clAmdBlasSsyr2k clAmdBlasSsyr2k_pfn +#undef clAmdBlasSsyr2kEx +//#define clAmdBlasSsyr2kEx clAmdBlasSsyr2kEx_pfn +#undef clAmdBlasSsyrk +//#define clAmdBlasSsyrk clAmdBlasSsyrk_pfn +#undef clAmdBlasSsyrkEx +//#define clAmdBlasSsyrkEx clAmdBlasSsyrkEx_pfn +#undef clAmdBlasStbmv +//#define clAmdBlasStbmv clAmdBlasStbmv_pfn +#undef clAmdBlasStbsv +//#define clAmdBlasStbsv clAmdBlasStbsv_pfn +#undef clAmdBlasStpmv +//#define clAmdBlasStpmv clAmdBlasStpmv_pfn +#undef clAmdBlasStpsv +//#define clAmdBlasStpsv clAmdBlasStpsv_pfn +#undef clAmdBlasStrmm +//#define clAmdBlasStrmm clAmdBlasStrmm_pfn +#undef clAmdBlasStrmmEx +//#define clAmdBlasStrmmEx clAmdBlasStrmmEx_pfn +#undef clAmdBlasStrmv +//#define clAmdBlasStrmv clAmdBlasStrmv_pfn +#undef clAmdBlasStrsm +//#define clAmdBlasStrsm clAmdBlasStrsm_pfn +#undef clAmdBlasStrsmEx +//#define clAmdBlasStrsmEx clAmdBlasStrsmEx_pfn +#undef clAmdBlasStrsv +//#define clAmdBlasStrsv clAmdBlasStrsv_pfn +#undef clAmdBlasTeardown +#define clAmdBlasTeardown clAmdBlasTeardown_pfn +#undef clAmdBlasZaxpy +//#define clAmdBlasZaxpy clAmdBlasZaxpy_pfn +#undef clAmdBlasZcopy +//#define clAmdBlasZcopy clAmdBlasZcopy_pfn +#undef clAmdBlasZdotc +//#define clAmdBlasZdotc clAmdBlasZdotc_pfn +#undef clAmdBlasZdotu +//#define clAmdBlasZdotu clAmdBlasZdotu_pfn +#undef clAmdBlasZdrot +//#define clAmdBlasZdrot clAmdBlasZdrot_pfn +#undef clAmdBlasZdscal +//#define clAmdBlasZdscal clAmdBlasZdscal_pfn +#undef clAmdBlasZgbmv +//#define clAmdBlasZgbmv clAmdBlasZgbmv_pfn +#undef clAmdBlasZgemm +//#define clAmdBlasZgemm clAmdBlasZgemm_pfn +#undef clAmdBlasZgemmEx +#define clAmdBlasZgemmEx clAmdBlasZgemmEx_pfn +#undef clAmdBlasZgemv +//#define clAmdBlasZgemv clAmdBlasZgemv_pfn +#undef clAmdBlasZgemvEx +//#define clAmdBlasZgemvEx clAmdBlasZgemvEx_pfn +#undef clAmdBlasZgerc +//#define clAmdBlasZgerc clAmdBlasZgerc_pfn +#undef clAmdBlasZgeru +//#define clAmdBlasZgeru clAmdBlasZgeru_pfn +#undef clAmdBlasZhbmv +//#define clAmdBlasZhbmv clAmdBlasZhbmv_pfn +#undef clAmdBlasZhemm +//#define clAmdBlasZhemm clAmdBlasZhemm_pfn +#undef clAmdBlasZhemv +//#define clAmdBlasZhemv clAmdBlasZhemv_pfn +#undef clAmdBlasZher +//#define clAmdBlasZher clAmdBlasZher_pfn +#undef clAmdBlasZher2 +//#define clAmdBlasZher2 clAmdBlasZher2_pfn +#undef clAmdBlasZher2k +//#define clAmdBlasZher2k clAmdBlasZher2k_pfn +#undef clAmdBlasZherk +//#define clAmdBlasZherk clAmdBlasZherk_pfn +#undef clAmdBlasZhpmv +//#define clAmdBlasZhpmv clAmdBlasZhpmv_pfn +#undef clAmdBlasZhpr +//#define clAmdBlasZhpr clAmdBlasZhpr_pfn +#undef clAmdBlasZhpr2 +//#define clAmdBlasZhpr2 clAmdBlasZhpr2_pfn +#undef clAmdBlasZrotg +//#define clAmdBlasZrotg clAmdBlasZrotg_pfn +#undef clAmdBlasZscal +//#define clAmdBlasZscal clAmdBlasZscal_pfn +#undef clAmdBlasZswap +//#define clAmdBlasZswap clAmdBlasZswap_pfn +#undef clAmdBlasZsymm +//#define clAmdBlasZsymm clAmdBlasZsymm_pfn +#undef clAmdBlasZsyr2k +//#define clAmdBlasZsyr2k clAmdBlasZsyr2k_pfn +#undef clAmdBlasZsyr2kEx +//#define clAmdBlasZsyr2kEx clAmdBlasZsyr2kEx_pfn +#undef clAmdBlasZsyrk +//#define clAmdBlasZsyrk clAmdBlasZsyrk_pfn +#undef clAmdBlasZsyrkEx +//#define clAmdBlasZsyrkEx clAmdBlasZsyrkEx_pfn +#undef clAmdBlasZtbmv +//#define clAmdBlasZtbmv clAmdBlasZtbmv_pfn +#undef clAmdBlasZtbsv +//#define clAmdBlasZtbsv clAmdBlasZtbsv_pfn +#undef clAmdBlasZtpmv +//#define clAmdBlasZtpmv clAmdBlasZtpmv_pfn +#undef clAmdBlasZtpsv +//#define clAmdBlasZtpsv clAmdBlasZtpsv_pfn +#undef clAmdBlasZtrmm +//#define clAmdBlasZtrmm clAmdBlasZtrmm_pfn +#undef clAmdBlasZtrmmEx +//#define clAmdBlasZtrmmEx clAmdBlasZtrmmEx_pfn +#undef clAmdBlasZtrmv +//#define clAmdBlasZtrmv clAmdBlasZtrmv_pfn +#undef clAmdBlasZtrsm +//#define clAmdBlasZtrsm clAmdBlasZtrsm_pfn +#undef clAmdBlasZtrsmEx +//#define clAmdBlasZtrsmEx clAmdBlasZtrsmEx_pfn +#undef clAmdBlasZtrsv +//#define clAmdBlasZtrsv clAmdBlasZtrsv_pfn +#undef clAmdBlasiCamax +//#define clAmdBlasiCamax clAmdBlasiCamax_pfn +#undef clAmdBlasiDamax +//#define clAmdBlasiDamax clAmdBlasiDamax_pfn +#undef clAmdBlasiSamax +//#define clAmdBlasiSamax clAmdBlasiSamax_pfn +#undef clAmdBlasiZamax +//#define clAmdBlasiZamax clAmdBlasiZamax_pfn + +// generated by parser_clamdblas.py +//extern CL_RUNTIME_EXPORT cl_ulong (*clAmdBlasAddScratchImage)(cl_context context, size_t width, size_t height, clAmdBlasStatus* status); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCaxpy)(size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCdotc)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCdotu)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, FloatComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, FloatComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgerc)(clAmdBlasOrder order, size_t M, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCgeru)(clAmdBlasOrder order, size_t M, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChemm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChemv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, FloatComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, FloatComplex beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCher2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCherk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, float alpha, const cl_mem A, size_t offa, size_t lda, float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_float2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasChpr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCrotg)(cl_mem CA, size_t offCA, cl_mem CB, size_t offCB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCscal)(size_t N, cl_float2 alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_float C, cl_float S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsscal)(size_t N, cl_float alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t lda, FloatComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, FloatComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, FloatComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasCtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDaxpy)(size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDdot)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDger)(clAmdBlasOrder order, size_t M, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_double C, cl_double S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotg)(cl_mem DA, size_t offDA, cl_mem DB, size_t offDB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotm)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, const cl_mem DPARAM, size_t offDparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDrotmg)(cl_mem DD1, size_t offDD1, cl_mem DD2, size_t offDD2, cl_mem DX1, size_t offDX1, const cl_mem DY1, size_t offDY1, cl_mem DPARAM, size_t offDparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDscal)(size_t N, cl_double alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_double beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDspr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsymvEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_double beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t lda, cl_double beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_double beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_double alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDzasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasDznrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasGetVersion)(cl_uint* major, cl_uint* minor, cl_uint* patch); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasRemoveScratchImage)(cl_ulong imageID); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSaxpy)(size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScasum)(size_t N, cl_mem asum, size_t offAsum, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasScopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSdot)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSetup)(); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSger)(clAmdBlasOrder order, size_t M, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSnrm2)(size_t N, cl_mem NRM2, size_t offNRM2, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_float C, cl_float S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotg)(cl_mem SA, size_t offSA, cl_mem SB, size_t offSB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotm)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, const cl_mem SPARAM, size_t offSparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSrotmg)(cl_mem SD1, size_t offSD1, cl_mem SD2, size_t offSD2, cl_mem SX1, size_t offSX1, const cl_mem SY1, size_t offSY1, cl_mem SPARAM, size_t offSparam, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSscal)(size_t N, cl_float alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_float beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSspr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_float beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsymvEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, cl_float beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_float alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t lda, cl_float beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasSsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_float beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, cl_float alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasStrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT void (*clAmdBlasTeardown)(); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZaxpy)(size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZcopy)(size_t N, const cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdotc)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdotu)(size_t N, cl_mem dotProduct, size_t offDP, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdrot)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_double C, cl_double S, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZdscal)(size_t N, cl_double alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgbmv)(clAmdBlasOrder order, clAmdBlasTranspose trans, size_t M, size_t N, size_t KL, size_t KU, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemm)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemmEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, clAmdBlasTranspose transB, size_t M, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemv)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem x, size_t offx, int incx, DoubleComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgemvEx)(clAmdBlasOrder order, clAmdBlasTranspose transA, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem x, size_t offx, int incx, DoubleComplex beta, cl_mem y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgerc)(clAmdBlasOrder order, size_t M, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZgeru)(clAmdBlasOrder order, size_t M, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, size_t K, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhemm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhemv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, DoubleComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem X, size_t offx, int incx, DoubleComplex beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem A, size_t offa, size_t lda, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZher2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZherk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, double alpha, const cl_mem A, size_t offa, size_t lda, double beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem AP, size_t offa, const cl_mem X, size_t offx, int incx, cl_double2 beta, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpr)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double alpha, const cl_mem X, size_t offx, int incx, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZhpr2)(clAmdBlasOrder order, clAmdBlasUplo uplo, size_t N, cl_double2 alpha, const cl_mem X, size_t offx, int incx, const cl_mem Y, size_t offy, int incy, cl_mem AP, size_t offa, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZrotg)(cl_mem CA, size_t offCA, cl_mem CB, size_t offCB, cl_mem C, size_t offC, cl_mem S, size_t offS, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZscal)(size_t N, cl_double2 alpha, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZswap)(size_t N, cl_mem X, size_t offx, int incx, cl_mem Y, size_t offy, int incy, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsymm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, size_t M, size_t N, cl_double2 alpha, const cl_mem A, size_t offa, size_t lda, const cl_mem B, size_t offb, size_t ldb, cl_double2 beta, cl_mem C, size_t offc, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyr2k)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, const cl_mem B, size_t ldb, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyr2kEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transAB, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, const cl_mem B, size_t offB, size_t ldb, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyrk)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t lda, DoubleComplex beta, cl_mem C, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZsyrkEx)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose transA, size_t N, size_t K, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, DoubleComplex beta, cl_mem C, size_t offC, size_t ldc, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtbmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtbsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, size_t K, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtpmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem AP, size_t offa, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtpsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrmv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsm)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t lda, cl_mem B, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsmEx)(clAmdBlasOrder order, clAmdBlasSide side, clAmdBlasUplo uplo, clAmdBlasTranspose transA, clAmdBlasDiag diag, size_t M, size_t N, DoubleComplex alpha, const cl_mem A, size_t offA, size_t lda, cl_mem B, size_t offB, size_t ldb, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasZtrsv)(clAmdBlasOrder order, clAmdBlasUplo uplo, clAmdBlasTranspose trans, clAmdBlasDiag diag, size_t N, const cl_mem A, size_t offa, size_t lda, cl_mem X, size_t offx, int incx, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiCamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiDamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiSamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); +//extern CL_RUNTIME_EXPORT clAmdBlasStatus (*clAmdBlasiZamax)(size_t N, cl_mem iMax, size_t offiMax, const cl_mem X, size_t offx, int incx, cl_mem scratchBuff, cl_uint numCommandQueues, cl_command_queue* commandQueues, cl_uint numEventsInWaitList, const cl_event* eventWaitList, cl_event* events); diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp new file mode 100644 index 0000000..1457d7e --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_clamdfft.hpp @@ -0,0 +1,142 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP +#error "Invalid usage" +#endif + +// generated by parser_clamdfft.py +#define clAmdFftBakePlan clAmdFftBakePlan_ +#define clAmdFftCopyPlan clAmdFftCopyPlan_ +#define clAmdFftCreateDefaultPlan clAmdFftCreateDefaultPlan_ +#define clAmdFftDestroyPlan clAmdFftDestroyPlan_ +#define clAmdFftEnqueueTransform clAmdFftEnqueueTransform_ +#define clAmdFftGetLayout clAmdFftGetLayout_ +#define clAmdFftGetPlanBatchSize clAmdFftGetPlanBatchSize_ +#define clAmdFftGetPlanContext clAmdFftGetPlanContext_ +#define clAmdFftGetPlanDim clAmdFftGetPlanDim_ +#define clAmdFftGetPlanDistance clAmdFftGetPlanDistance_ +#define clAmdFftGetPlanInStride clAmdFftGetPlanInStride_ +#define clAmdFftGetPlanLength clAmdFftGetPlanLength_ +#define clAmdFftGetPlanOutStride clAmdFftGetPlanOutStride_ +#define clAmdFftGetPlanPrecision clAmdFftGetPlanPrecision_ +#define clAmdFftGetPlanScale clAmdFftGetPlanScale_ +#define clAmdFftGetPlanTransposeResult clAmdFftGetPlanTransposeResult_ +#define clAmdFftGetResultLocation clAmdFftGetResultLocation_ +#define clAmdFftGetTmpBufSize clAmdFftGetTmpBufSize_ +#define clAmdFftGetVersion clAmdFftGetVersion_ +#define clAmdFftSetLayout clAmdFftSetLayout_ +#define clAmdFftSetPlanBatchSize clAmdFftSetPlanBatchSize_ +#define clAmdFftSetPlanDim clAmdFftSetPlanDim_ +#define clAmdFftSetPlanDistance clAmdFftSetPlanDistance_ +#define clAmdFftSetPlanInStride clAmdFftSetPlanInStride_ +#define clAmdFftSetPlanLength clAmdFftSetPlanLength_ +#define clAmdFftSetPlanOutStride clAmdFftSetPlanOutStride_ +#define clAmdFftSetPlanPrecision clAmdFftSetPlanPrecision_ +#define clAmdFftSetPlanScale clAmdFftSetPlanScale_ +#define clAmdFftSetPlanTransposeResult clAmdFftSetPlanTransposeResult_ +#define clAmdFftSetResultLocation clAmdFftSetResultLocation_ +#define clAmdFftSetup clAmdFftSetup_ +#define clAmdFftTeardown clAmdFftTeardown_ + +#include + +// generated by parser_clamdfft.py +#undef clAmdFftBakePlan +#define clAmdFftBakePlan clAmdFftBakePlan_pfn +#undef clAmdFftCopyPlan +//#define clAmdFftCopyPlan clAmdFftCopyPlan_pfn +#undef clAmdFftCreateDefaultPlan +#define clAmdFftCreateDefaultPlan clAmdFftCreateDefaultPlan_pfn +#undef clAmdFftDestroyPlan +#define clAmdFftDestroyPlan clAmdFftDestroyPlan_pfn +#undef clAmdFftEnqueueTransform +#define clAmdFftEnqueueTransform clAmdFftEnqueueTransform_pfn +#undef clAmdFftGetLayout +//#define clAmdFftGetLayout clAmdFftGetLayout_pfn +#undef clAmdFftGetPlanBatchSize +//#define clAmdFftGetPlanBatchSize clAmdFftGetPlanBatchSize_pfn +#undef clAmdFftGetPlanContext +//#define clAmdFftGetPlanContext clAmdFftGetPlanContext_pfn +#undef clAmdFftGetPlanDim +//#define clAmdFftGetPlanDim clAmdFftGetPlanDim_pfn +#undef clAmdFftGetPlanDistance +//#define clAmdFftGetPlanDistance clAmdFftGetPlanDistance_pfn +#undef clAmdFftGetPlanInStride +//#define clAmdFftGetPlanInStride clAmdFftGetPlanInStride_pfn +#undef clAmdFftGetPlanLength +//#define clAmdFftGetPlanLength clAmdFftGetPlanLength_pfn +#undef clAmdFftGetPlanOutStride +//#define clAmdFftGetPlanOutStride clAmdFftGetPlanOutStride_pfn +#undef clAmdFftGetPlanPrecision +//#define clAmdFftGetPlanPrecision clAmdFftGetPlanPrecision_pfn +#undef clAmdFftGetPlanScale +//#define clAmdFftGetPlanScale clAmdFftGetPlanScale_pfn +#undef clAmdFftGetPlanTransposeResult +//#define clAmdFftGetPlanTransposeResult clAmdFftGetPlanTransposeResult_pfn +#undef clAmdFftGetResultLocation +//#define clAmdFftGetResultLocation clAmdFftGetResultLocation_pfn +#undef clAmdFftGetTmpBufSize +#define clAmdFftGetTmpBufSize clAmdFftGetTmpBufSize_pfn +#undef clAmdFftGetVersion +#define clAmdFftGetVersion clAmdFftGetVersion_pfn +#undef clAmdFftSetLayout +#define clAmdFftSetLayout clAmdFftSetLayout_pfn +#undef clAmdFftSetPlanBatchSize +#define clAmdFftSetPlanBatchSize clAmdFftSetPlanBatchSize_pfn +#undef clAmdFftSetPlanDim +//#define clAmdFftSetPlanDim clAmdFftSetPlanDim_pfn +#undef clAmdFftSetPlanDistance +#define clAmdFftSetPlanDistance clAmdFftSetPlanDistance_pfn +#undef clAmdFftSetPlanInStride +#define clAmdFftSetPlanInStride clAmdFftSetPlanInStride_pfn +#undef clAmdFftSetPlanLength +//#define clAmdFftSetPlanLength clAmdFftSetPlanLength_pfn +#undef clAmdFftSetPlanOutStride +#define clAmdFftSetPlanOutStride clAmdFftSetPlanOutStride_pfn +#undef clAmdFftSetPlanPrecision +#define clAmdFftSetPlanPrecision clAmdFftSetPlanPrecision_pfn +#undef clAmdFftSetPlanScale +#define clAmdFftSetPlanScale clAmdFftSetPlanScale_pfn +#undef clAmdFftSetPlanTransposeResult +//#define clAmdFftSetPlanTransposeResult clAmdFftSetPlanTransposeResult_pfn +#undef clAmdFftSetResultLocation +#define clAmdFftSetResultLocation clAmdFftSetResultLocation_pfn +#undef clAmdFftSetup +#define clAmdFftSetup clAmdFftSetup_pfn +#undef clAmdFftTeardown +#define clAmdFftTeardown clAmdFftTeardown_pfn + +// generated by parser_clamdfft.py +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftBakePlan)(clAmdFftPlanHandle plHandle, cl_uint numQueues, cl_command_queue* commQueueFFT, void (CL_CALLBACK* pfn_notify) (clAmdFftPlanHandle plHandle, void* user_data), void* user_data); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftCopyPlan)(clAmdFftPlanHandle* out_plHandle, cl_context new_context, clAmdFftPlanHandle in_plHandle); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftCreateDefaultPlan)(clAmdFftPlanHandle* plHandle, cl_context context, const clAmdFftDim dim, const size_t* clLengths); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftDestroyPlan)(clAmdFftPlanHandle* plHandle); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftEnqueueTransform)(clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_uint numQueuesAndEvents, cl_command_queue* commQueues, cl_uint numWaitEvents, const cl_event* waitEvents, cl_event* outEvents, cl_mem* inputBuffers, cl_mem* outputBuffers, cl_mem tmpBuffer); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetLayout)(const clAmdFftPlanHandle plHandle, clAmdFftLayout* iLayout, clAmdFftLayout* oLayout); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanBatchSize)(const clAmdFftPlanHandle plHandle, size_t* batchSize); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanContext)(const clAmdFftPlanHandle plHandle, cl_context* context); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanDim)(const clAmdFftPlanHandle plHandle, clAmdFftDim* dim, cl_uint* size); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanDistance)(const clAmdFftPlanHandle plHandle, size_t* iDist, size_t* oDist); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanInStride)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanLength)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clLengths); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanOutStride)(const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanPrecision)(const clAmdFftPlanHandle plHandle, clAmdFftPrecision* precision); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanScale)(const clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float* scale); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetPlanTransposeResult)(const clAmdFftPlanHandle plHandle, clAmdFftResultTransposed* transposed); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetResultLocation)(const clAmdFftPlanHandle plHandle, clAmdFftResultLocation* placeness); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetTmpBufSize)(const clAmdFftPlanHandle plHandle, size_t* buffersize); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftGetVersion)(cl_uint* major, cl_uint* minor, cl_uint* patch); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetLayout)(clAmdFftPlanHandle plHandle, clAmdFftLayout iLayout, clAmdFftLayout oLayout); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanBatchSize)(clAmdFftPlanHandle plHandle, size_t batchSize); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanDim)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanDistance)(clAmdFftPlanHandle plHandle, size_t iDist, size_t oDist); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanInStride)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanLength)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, const size_t* clLengths); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanOutStride)(clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanPrecision)(clAmdFftPlanHandle plHandle, clAmdFftPrecision precision); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanScale)(clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float scale); +//extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetPlanTransposeResult)(clAmdFftPlanHandle plHandle, clAmdFftResultTransposed transposed); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetResultLocation)(clAmdFftPlanHandle plHandle, clAmdFftResultLocation placeness); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftSetup)(const clAmdFftSetupData* setupData); +extern CL_RUNTIME_EXPORT clAmdFftStatus (*clAmdFftTeardown)(); diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp new file mode 100644 index 0000000..28618a1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core.hpp @@ -0,0 +1,371 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#define clBuildProgram clBuildProgram_ +#define clCompileProgram clCompileProgram_ +#define clCreateBuffer clCreateBuffer_ +#define clCreateCommandQueue clCreateCommandQueue_ +#define clCreateContext clCreateContext_ +#define clCreateContextFromType clCreateContextFromType_ +#define clCreateImage clCreateImage_ +#define clCreateImage2D clCreateImage2D_ +#define clCreateImage3D clCreateImage3D_ +#define clCreateKernel clCreateKernel_ +#define clCreateKernelsInProgram clCreateKernelsInProgram_ +#define clCreateProgramWithBinary clCreateProgramWithBinary_ +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_ +#define clCreateProgramWithSource clCreateProgramWithSource_ +#define clCreateSampler clCreateSampler_ +#define clCreateSubBuffer clCreateSubBuffer_ +#define clCreateSubDevices clCreateSubDevices_ +#define clCreateUserEvent clCreateUserEvent_ +#define clEnqueueBarrier clEnqueueBarrier_ +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_ +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_ +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_ +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_ +#define clEnqueueCopyImage clEnqueueCopyImage_ +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_ +#define clEnqueueFillBuffer clEnqueueFillBuffer_ +#define clEnqueueFillImage clEnqueueFillImage_ +#define clEnqueueMapBuffer clEnqueueMapBuffer_ +#define clEnqueueMapImage clEnqueueMapImage_ +#define clEnqueueMarker clEnqueueMarker_ +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_ +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_ +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_ +#define clEnqueueNativeKernel clEnqueueNativeKernel_ +#define clEnqueueReadBuffer clEnqueueReadBuffer_ +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_ +#define clEnqueueReadImage clEnqueueReadImage_ +#define clEnqueueTask clEnqueueTask_ +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_ +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_ +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_ +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_ +#define clEnqueueWriteImage clEnqueueWriteImage_ +#define clFinish clFinish_ +#define clFlush clFlush_ +#define clGetCommandQueueInfo clGetCommandQueueInfo_ +#define clGetContextInfo clGetContextInfo_ +#define clGetDeviceIDs clGetDeviceIDs_ +#define clGetDeviceInfo clGetDeviceInfo_ +#define clGetEventInfo clGetEventInfo_ +#define clGetEventProfilingInfo clGetEventProfilingInfo_ +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_ +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_ +#define clGetImageInfo clGetImageInfo_ +#define clGetKernelArgInfo clGetKernelArgInfo_ +#define clGetKernelInfo clGetKernelInfo_ +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_ +#define clGetMemObjectInfo clGetMemObjectInfo_ +#define clGetPlatformIDs clGetPlatformIDs_ +#define clGetPlatformInfo clGetPlatformInfo_ +#define clGetProgramBuildInfo clGetProgramBuildInfo_ +#define clGetProgramInfo clGetProgramInfo_ +#define clGetSamplerInfo clGetSamplerInfo_ +#define clGetSupportedImageFormats clGetSupportedImageFormats_ +#define clLinkProgram clLinkProgram_ +#define clReleaseCommandQueue clReleaseCommandQueue_ +#define clReleaseContext clReleaseContext_ +#define clReleaseDevice clReleaseDevice_ +#define clReleaseEvent clReleaseEvent_ +#define clReleaseKernel clReleaseKernel_ +#define clReleaseMemObject clReleaseMemObject_ +#define clReleaseProgram clReleaseProgram_ +#define clReleaseSampler clReleaseSampler_ +#define clRetainCommandQueue clRetainCommandQueue_ +#define clRetainContext clRetainContext_ +#define clRetainDevice clRetainDevice_ +#define clRetainEvent clRetainEvent_ +#define clRetainKernel clRetainKernel_ +#define clRetainMemObject clRetainMemObject_ +#define clRetainProgram clRetainProgram_ +#define clRetainSampler clRetainSampler_ +#define clSetEventCallback clSetEventCallback_ +#define clSetKernelArg clSetKernelArg_ +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_ +#define clSetUserEventStatus clSetUserEventStatus_ +#define clUnloadCompiler clUnloadCompiler_ +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_ +#define clWaitForEvents clWaitForEvents_ + +#if defined __APPLE__ +#define CL_SILENCE_DEPRECATION +#include +#else +#include +#endif + +// generated by parser_cl.py +#undef clBuildProgram +#define clBuildProgram clBuildProgram_pfn +#undef clCompileProgram +#define clCompileProgram clCompileProgram_pfn +#undef clCreateBuffer +#define clCreateBuffer clCreateBuffer_pfn +#undef clCreateCommandQueue +#define clCreateCommandQueue clCreateCommandQueue_pfn +#undef clCreateContext +#define clCreateContext clCreateContext_pfn +#undef clCreateContextFromType +#define clCreateContextFromType clCreateContextFromType_pfn +#undef clCreateImage +#define clCreateImage clCreateImage_pfn +#undef clCreateImage2D +#define clCreateImage2D clCreateImage2D_pfn +#undef clCreateImage3D +#define clCreateImage3D clCreateImage3D_pfn +#undef clCreateKernel +#define clCreateKernel clCreateKernel_pfn +#undef clCreateKernelsInProgram +#define clCreateKernelsInProgram clCreateKernelsInProgram_pfn +#undef clCreateProgramWithBinary +#define clCreateProgramWithBinary clCreateProgramWithBinary_pfn +#undef clCreateProgramWithBuiltInKernels +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_pfn +#undef clCreateProgramWithSource +#define clCreateProgramWithSource clCreateProgramWithSource_pfn +#undef clCreateSampler +#define clCreateSampler clCreateSampler_pfn +#undef clCreateSubBuffer +#define clCreateSubBuffer clCreateSubBuffer_pfn +#undef clCreateSubDevices +#define clCreateSubDevices clCreateSubDevices_pfn +#undef clCreateUserEvent +#define clCreateUserEvent clCreateUserEvent_pfn +#undef clEnqueueBarrier +#define clEnqueueBarrier clEnqueueBarrier_pfn +#undef clEnqueueBarrierWithWaitList +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_pfn +#undef clEnqueueCopyBuffer +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_pfn +#undef clEnqueueCopyBufferRect +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_pfn +#undef clEnqueueCopyBufferToImage +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_pfn +#undef clEnqueueCopyImage +#define clEnqueueCopyImage clEnqueueCopyImage_pfn +#undef clEnqueueCopyImageToBuffer +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_pfn +#undef clEnqueueFillBuffer +#define clEnqueueFillBuffer clEnqueueFillBuffer_pfn +#undef clEnqueueFillImage +#define clEnqueueFillImage clEnqueueFillImage_pfn +#undef clEnqueueMapBuffer +#define clEnqueueMapBuffer clEnqueueMapBuffer_pfn +#undef clEnqueueMapImage +#define clEnqueueMapImage clEnqueueMapImage_pfn +#undef clEnqueueMarker +#define clEnqueueMarker clEnqueueMarker_pfn +#undef clEnqueueMarkerWithWaitList +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_pfn +#undef clEnqueueMigrateMemObjects +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_pfn +#undef clEnqueueNDRangeKernel +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_pfn +#undef clEnqueueNativeKernel +#define clEnqueueNativeKernel clEnqueueNativeKernel_pfn +#undef clEnqueueReadBuffer +#define clEnqueueReadBuffer clEnqueueReadBuffer_pfn +#undef clEnqueueReadBufferRect +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_pfn +#undef clEnqueueReadImage +#define clEnqueueReadImage clEnqueueReadImage_pfn +#undef clEnqueueTask +#define clEnqueueTask clEnqueueTask_pfn +#undef clEnqueueUnmapMemObject +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_pfn +#undef clEnqueueWaitForEvents +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_pfn +#undef clEnqueueWriteBuffer +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_pfn +#undef clEnqueueWriteBufferRect +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_pfn +#undef clEnqueueWriteImage +#define clEnqueueWriteImage clEnqueueWriteImage_pfn +#undef clFinish +#define clFinish clFinish_pfn +#undef clFlush +#define clFlush clFlush_pfn +#undef clGetCommandQueueInfo +#define clGetCommandQueueInfo clGetCommandQueueInfo_pfn +#undef clGetContextInfo +#define clGetContextInfo clGetContextInfo_pfn +#undef clGetDeviceIDs +#define clGetDeviceIDs clGetDeviceIDs_pfn +#undef clGetDeviceInfo +#define clGetDeviceInfo clGetDeviceInfo_pfn +#undef clGetEventInfo +#define clGetEventInfo clGetEventInfo_pfn +#undef clGetEventProfilingInfo +#define clGetEventProfilingInfo clGetEventProfilingInfo_pfn +#undef clGetExtensionFunctionAddress +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_pfn +#undef clGetExtensionFunctionAddressForPlatform +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_pfn +#undef clGetImageInfo +#define clGetImageInfo clGetImageInfo_pfn +#undef clGetKernelArgInfo +#define clGetKernelArgInfo clGetKernelArgInfo_pfn +#undef clGetKernelInfo +#define clGetKernelInfo clGetKernelInfo_pfn +#undef clGetKernelWorkGroupInfo +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_pfn +#undef clGetMemObjectInfo +#define clGetMemObjectInfo clGetMemObjectInfo_pfn +#undef clGetPlatformIDs +#define clGetPlatformIDs clGetPlatformIDs_pfn +#undef clGetPlatformInfo +#define clGetPlatformInfo clGetPlatformInfo_pfn +#undef clGetProgramBuildInfo +#define clGetProgramBuildInfo clGetProgramBuildInfo_pfn +#undef clGetProgramInfo +#define clGetProgramInfo clGetProgramInfo_pfn +#undef clGetSamplerInfo +#define clGetSamplerInfo clGetSamplerInfo_pfn +#undef clGetSupportedImageFormats +#define clGetSupportedImageFormats clGetSupportedImageFormats_pfn +#undef clLinkProgram +#define clLinkProgram clLinkProgram_pfn +#undef clReleaseCommandQueue +#define clReleaseCommandQueue clReleaseCommandQueue_pfn +#undef clReleaseContext +#define clReleaseContext clReleaseContext_pfn +#undef clReleaseDevice +#define clReleaseDevice clReleaseDevice_pfn +#undef clReleaseEvent +#define clReleaseEvent clReleaseEvent_pfn +#undef clReleaseKernel +#define clReleaseKernel clReleaseKernel_pfn +#undef clReleaseMemObject +#define clReleaseMemObject clReleaseMemObject_pfn +#undef clReleaseProgram +#define clReleaseProgram clReleaseProgram_pfn +#undef clReleaseSampler +#define clReleaseSampler clReleaseSampler_pfn +#undef clRetainCommandQueue +#define clRetainCommandQueue clRetainCommandQueue_pfn +#undef clRetainContext +#define clRetainContext clRetainContext_pfn +#undef clRetainDevice +#define clRetainDevice clRetainDevice_pfn +#undef clRetainEvent +#define clRetainEvent clRetainEvent_pfn +#undef clRetainKernel +#define clRetainKernel clRetainKernel_pfn +#undef clRetainMemObject +#define clRetainMemObject clRetainMemObject_pfn +#undef clRetainProgram +#define clRetainProgram clRetainProgram_pfn +#undef clRetainSampler +#define clRetainSampler clRetainSampler_pfn +#undef clSetEventCallback +#define clSetEventCallback clSetEventCallback_pfn +#undef clSetKernelArg +#define clSetKernelArg clSetKernelArg_pfn +#undef clSetMemObjectDestructorCallback +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_pfn +#undef clSetUserEventStatus +#define clSetUserEventStatus clSetUserEventStatus_pfn +#undef clUnloadCompiler +#define clUnloadCompiler clUnloadCompiler_pfn +#undef clUnloadPlatformCompiler +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_pfn +#undef clWaitForEvents +#define clWaitForEvents clWaitForEvents_pfn + +// generated by parser_cl.py +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clBuildProgram)(cl_program, cl_uint, const cl_device_id*, const char*, void (CL_CALLBACK*) (cl_program, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCompileProgram)(cl_program, cl_uint, const cl_device_id*, const char*, cl_uint, const cl_program*, const char**, void (CL_CALLBACK*) (cl_program, void*), void*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateBuffer)(cl_context, cl_mem_flags, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_command_queue (CL_API_CALL*clCreateCommandQueue)(cl_context, cl_device_id, cl_command_queue_properties, cl_int*); +extern CL_RUNTIME_EXPORT cl_context (CL_API_CALL*clCreateContext)(const cl_context_properties*, cl_uint, const cl_device_id*, void (CL_CALLBACK*) (const char*, const void*, size_t, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_context (CL_API_CALL*clCreateContextFromType)(const cl_context_properties*, cl_device_type, void (CL_CALLBACK*) (const char*, const void*, size_t, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage)(cl_context, cl_mem_flags, const cl_image_format*, const cl_image_desc*, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage2D)(cl_context, cl_mem_flags, const cl_image_format*, size_t, size_t, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateImage3D)(cl_context, cl_mem_flags, const cl_image_format*, size_t, size_t, size_t, size_t, size_t, void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_kernel (CL_API_CALL*clCreateKernel)(cl_program, const char*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCreateKernelsInProgram)(cl_program, cl_uint, cl_kernel*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithBinary)(cl_context, cl_uint, const cl_device_id*, const size_t*, const unsigned char**, cl_int*, cl_int*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithBuiltInKernels)(cl_context, cl_uint, const cl_device_id*, const char*, cl_int*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clCreateProgramWithSource)(cl_context, cl_uint, const char**, const size_t*, cl_int*); +extern CL_RUNTIME_EXPORT cl_sampler (CL_API_CALL*clCreateSampler)(cl_context, cl_bool, cl_addressing_mode, cl_filter_mode, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateSubBuffer)(cl_mem, cl_mem_flags, cl_buffer_create_type, const void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clCreateSubDevices)(cl_device_id, const cl_device_partition_property*, cl_uint, cl_device_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_event (CL_API_CALL*clCreateUserEvent)(cl_context, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueBarrier)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueBarrierWithWaitList)(cl_command_queue, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBuffer)(cl_command_queue, cl_mem, cl_mem, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBufferRect)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyBufferToImage)(cl_command_queue, cl_mem, cl_mem, size_t, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyImage)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueCopyImageToBuffer)(cl_command_queue, cl_mem, cl_mem, const size_t*, const size_t*, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueFillBuffer)(cl_command_queue, cl_mem, const void*, size_t, size_t, size_t, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueFillImage)(cl_command_queue, cl_mem, const void*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clEnqueueMapBuffer)(cl_command_queue, cl_mem, cl_bool, cl_map_flags, size_t, size_t, cl_uint, const cl_event*, cl_event*, cl_int*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clEnqueueMapImage)(cl_command_queue, cl_mem, cl_bool, cl_map_flags, const size_t*, const size_t*, size_t*, size_t*, cl_uint, const cl_event*, cl_event*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMarker)(cl_command_queue, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMarkerWithWaitList)(cl_command_queue, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueMigrateMemObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_mem_migration_flags, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueNDRangeKernel)(cl_command_queue, cl_kernel, cl_uint, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueNativeKernel)(cl_command_queue, void (CL_CALLBACK*) (void*), void*, size_t, cl_uint, const cl_mem*, const void**, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadBufferRect)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReadImage)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, size_t, size_t, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueTask)(cl_command_queue, cl_kernel, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueUnmapMemObject)(cl_command_queue, cl_mem, void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWaitForEvents)(cl_command_queue, cl_uint, const cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteBufferRect)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, const size_t*, size_t, size_t, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueWriteImage)(cl_command_queue, cl_mem, cl_bool, const size_t*, const size_t*, size_t, size_t, const void*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clFinish)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clFlush)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetCommandQueueInfo)(cl_command_queue, cl_command_queue_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetContextInfo)(cl_context, cl_context_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetDeviceIDs)(cl_platform_id, cl_device_type, cl_uint, cl_device_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetDeviceInfo)(cl_device_id, cl_device_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetEventInfo)(cl_event, cl_event_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetEventProfilingInfo)(cl_event, cl_profiling_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clGetExtensionFunctionAddress)(const char*); +extern CL_RUNTIME_EXPORT void* (CL_API_CALL*clGetExtensionFunctionAddressForPlatform)(cl_platform_id, const char*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetImageInfo)(cl_mem, cl_image_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelArgInfo)(cl_kernel, cl_uint, cl_kernel_arg_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelInfo)(cl_kernel, cl_kernel_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetKernelWorkGroupInfo)(cl_kernel, cl_device_id, cl_kernel_work_group_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetMemObjectInfo)(cl_mem, cl_mem_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetPlatformInfo)(cl_platform_id, cl_platform_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetProgramBuildInfo)(cl_program, cl_device_id, cl_program_build_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetProgramInfo)(cl_program, cl_program_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetSamplerInfo)(cl_sampler, cl_sampler_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetSupportedImageFormats)(cl_context, cl_mem_flags, cl_mem_object_type, cl_uint, cl_image_format*, cl_uint*); +extern CL_RUNTIME_EXPORT cl_program (CL_API_CALL*clLinkProgram)(cl_context, cl_uint, const cl_device_id*, const char*, cl_uint, const cl_program*, void (CL_CALLBACK*) (cl_program, void*), void*, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseCommandQueue)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseContext)(cl_context); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseDevice)(cl_device_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseEvent)(cl_event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseKernel)(cl_kernel); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseMemObject)(cl_mem); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseProgram)(cl_program); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clReleaseSampler)(cl_sampler); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainCommandQueue)(cl_command_queue); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainContext)(cl_context); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainDevice)(cl_device_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainEvent)(cl_event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainKernel)(cl_kernel); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainMemObject)(cl_mem); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainProgram)(cl_program); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clRetainSampler)(cl_sampler); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetEventCallback)(cl_event, cl_int, void (CL_CALLBACK*) (cl_event, cl_int, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetKernelArg)(cl_kernel, cl_uint, size_t, const void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetMemObjectDestructorCallback)(cl_mem, void (CL_CALLBACK*) (cl_mem, void*), void*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clSetUserEventStatus)(cl_event, cl_int); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clUnloadCompiler)(); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clUnloadPlatformCompiler)(cl_platform_id); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clWaitForEvents)(cl_uint, const cl_event*); diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp new file mode 100644 index 0000000..216b22b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_core_wrappers.hpp @@ -0,0 +1,272 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#undef clBuildProgram +#define clBuildProgram clBuildProgram_fn +inline cl_int clBuildProgram(cl_program p0, cl_uint p1, const cl_device_id* p2, const char* p3, void (CL_CALLBACK*p4) (cl_program, void*), void* p5) { return clBuildProgram_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCompileProgram +#define clCompileProgram clCompileProgram_fn +inline cl_int clCompileProgram(cl_program p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_uint p4, const cl_program* p5, const char** p6, void (CL_CALLBACK*p7) (cl_program, void*), void* p8) { return clCompileProgram_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clCreateBuffer +#define clCreateBuffer clCreateBuffer_fn +inline cl_mem clCreateBuffer(cl_context p0, cl_mem_flags p1, size_t p2, void* p3, cl_int* p4) { return clCreateBuffer_pfn(p0, p1, p2, p3, p4); } +#undef clCreateCommandQueue +#define clCreateCommandQueue clCreateCommandQueue_fn +inline cl_command_queue clCreateCommandQueue(cl_context p0, cl_device_id p1, cl_command_queue_properties p2, cl_int* p3) { return clCreateCommandQueue_pfn(p0, p1, p2, p3); } +#undef clCreateContext +#define clCreateContext clCreateContext_fn +inline cl_context clCreateContext(const cl_context_properties* p0, cl_uint p1, const cl_device_id* p2, void (CL_CALLBACK*p3) (const char*, const void*, size_t, void*), void* p4, cl_int* p5) { return clCreateContext_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateContextFromType +#define clCreateContextFromType clCreateContextFromType_fn +inline cl_context clCreateContextFromType(const cl_context_properties* p0, cl_device_type p1, void (CL_CALLBACK*p2) (const char*, const void*, size_t, void*), void* p3, cl_int* p4) { return clCreateContextFromType_pfn(p0, p1, p2, p3, p4); } +#undef clCreateImage +#define clCreateImage clCreateImage_fn +inline cl_mem clCreateImage(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, const cl_image_desc* p3, void* p4, cl_int* p5) { return clCreateImage_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateImage2D +#define clCreateImage2D clCreateImage2D_fn +inline cl_mem clCreateImage2D(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, size_t p3, size_t p4, size_t p5, void* p6, cl_int* p7) { return clCreateImage2D_pfn(p0, p1, p2, p3, p4, p5, p6, p7); } +#undef clCreateImage3D +#define clCreateImage3D clCreateImage3D_fn +inline cl_mem clCreateImage3D(cl_context p0, cl_mem_flags p1, const cl_image_format* p2, size_t p3, size_t p4, size_t p5, size_t p6, size_t p7, void* p8, cl_int* p9) { return clCreateImage3D_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clCreateKernel +#define clCreateKernel clCreateKernel_fn +inline cl_kernel clCreateKernel(cl_program p0, const char* p1, cl_int* p2) { return clCreateKernel_pfn(p0, p1, p2); } +#undef clCreateKernelsInProgram +#define clCreateKernelsInProgram clCreateKernelsInProgram_fn +inline cl_int clCreateKernelsInProgram(cl_program p0, cl_uint p1, cl_kernel* p2, cl_uint* p3) { return clCreateKernelsInProgram_pfn(p0, p1, p2, p3); } +#undef clCreateProgramWithBinary +#define clCreateProgramWithBinary clCreateProgramWithBinary_fn +inline cl_program clCreateProgramWithBinary(cl_context p0, cl_uint p1, const cl_device_id* p2, const size_t* p3, const unsigned char** p4, cl_int* p5, cl_int* p6) { return clCreateProgramWithBinary_pfn(p0, p1, p2, p3, p4, p5, p6); } +#undef clCreateProgramWithBuiltInKernels +#define clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels_fn +inline cl_program clCreateProgramWithBuiltInKernels(cl_context p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_int* p4) { return clCreateProgramWithBuiltInKernels_pfn(p0, p1, p2, p3, p4); } +#undef clCreateProgramWithSource +#define clCreateProgramWithSource clCreateProgramWithSource_fn +inline cl_program clCreateProgramWithSource(cl_context p0, cl_uint p1, const char** p2, const size_t* p3, cl_int* p4) { return clCreateProgramWithSource_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSampler +#define clCreateSampler clCreateSampler_fn +inline cl_sampler clCreateSampler(cl_context p0, cl_bool p1, cl_addressing_mode p2, cl_filter_mode p3, cl_int* p4) { return clCreateSampler_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSubBuffer +#define clCreateSubBuffer clCreateSubBuffer_fn +inline cl_mem clCreateSubBuffer(cl_mem p0, cl_mem_flags p1, cl_buffer_create_type p2, const void* p3, cl_int* p4) { return clCreateSubBuffer_pfn(p0, p1, p2, p3, p4); } +#undef clCreateSubDevices +#define clCreateSubDevices clCreateSubDevices_fn +inline cl_int clCreateSubDevices(cl_device_id p0, const cl_device_partition_property* p1, cl_uint p2, cl_device_id* p3, cl_uint* p4) { return clCreateSubDevices_pfn(p0, p1, p2, p3, p4); } +#undef clCreateUserEvent +#define clCreateUserEvent clCreateUserEvent_fn +inline cl_event clCreateUserEvent(cl_context p0, cl_int* p1) { return clCreateUserEvent_pfn(p0, p1); } +#undef clEnqueueBarrier +#define clEnqueueBarrier clEnqueueBarrier_fn +inline cl_int clEnqueueBarrier(cl_command_queue p0) { return clEnqueueBarrier_pfn(p0); } +#undef clEnqueueBarrierWithWaitList +#define clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList_fn +inline cl_int clEnqueueBarrierWithWaitList(cl_command_queue p0, cl_uint p1, const cl_event* p2, cl_event* p3) { return clEnqueueBarrierWithWaitList_pfn(p0, p1, p2, p3); } +#undef clEnqueueCopyBuffer +#define clEnqueueCopyBuffer clEnqueueCopyBuffer_fn +inline cl_int clEnqueueCopyBuffer(cl_command_queue p0, cl_mem p1, cl_mem p2, size_t p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyBufferRect +#define clEnqueueCopyBufferRect clEnqueueCopyBufferRect_fn +inline cl_int clEnqueueCopyBufferRect(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, cl_uint p10, const cl_event* p11, cl_event* p12) { return clEnqueueCopyBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); } +#undef clEnqueueCopyBufferToImage +#define clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage_fn +inline cl_int clEnqueueCopyBufferToImage(cl_command_queue p0, cl_mem p1, cl_mem p2, size_t p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyBufferToImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyImage +#define clEnqueueCopyImage clEnqueueCopyImage_fn +inline cl_int clEnqueueCopyImage(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueCopyImageToBuffer +#define clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer_fn +inline cl_int clEnqueueCopyImageToBuffer(cl_command_queue p0, cl_mem p1, cl_mem p2, const size_t* p3, const size_t* p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueCopyImageToBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueFillBuffer +#define clEnqueueFillBuffer clEnqueueFillBuffer_fn +inline cl_int clEnqueueFillBuffer(cl_command_queue p0, cl_mem p1, const void* p2, size_t p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueFillBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueFillImage +#define clEnqueueFillImage clEnqueueFillImage_fn +inline cl_int clEnqueueFillImage(cl_command_queue p0, cl_mem p1, const void* p2, const size_t* p3, const size_t* p4, cl_uint p5, const cl_event* p6, cl_event* p7) { return clEnqueueFillImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7); } +#undef clEnqueueMapBuffer +#define clEnqueueMapBuffer clEnqueueMapBuffer_fn +inline void* clEnqueueMapBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, cl_map_flags p3, size_t p4, size_t p5, cl_uint p6, const cl_event* p7, cl_event* p8, cl_int* p9) { return clEnqueueMapBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clEnqueueMapImage +#define clEnqueueMapImage clEnqueueMapImage_fn +inline void* clEnqueueMapImage(cl_command_queue p0, cl_mem p1, cl_bool p2, cl_map_flags p3, const size_t* p4, const size_t* p5, size_t* p6, size_t* p7, cl_uint p8, const cl_event* p9, cl_event* p10, cl_int* p11) { return clEnqueueMapImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); } +#undef clEnqueueMarker +#define clEnqueueMarker clEnqueueMarker_fn +inline cl_int clEnqueueMarker(cl_command_queue p0, cl_event* p1) { return clEnqueueMarker_pfn(p0, p1); } +#undef clEnqueueMarkerWithWaitList +#define clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList_fn +inline cl_int clEnqueueMarkerWithWaitList(cl_command_queue p0, cl_uint p1, const cl_event* p2, cl_event* p3) { return clEnqueueMarkerWithWaitList_pfn(p0, p1, p2, p3); } +#undef clEnqueueMigrateMemObjects +#define clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects_fn +inline cl_int clEnqueueMigrateMemObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_mem_migration_flags p3, cl_uint p4, const cl_event* p5, cl_event* p6) { return clEnqueueMigrateMemObjects_pfn(p0, p1, p2, p3, p4, p5, p6); } +#undef clEnqueueNDRangeKernel +#define clEnqueueNDRangeKernel clEnqueueNDRangeKernel_fn +inline cl_int clEnqueueNDRangeKernel(cl_command_queue p0, cl_kernel p1, cl_uint p2, const size_t* p3, const size_t* p4, const size_t* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueNDRangeKernel_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueNativeKernel +#define clEnqueueNativeKernel clEnqueueNativeKernel_fn +inline cl_int clEnqueueNativeKernel(cl_command_queue p0, void (CL_CALLBACK*p1) (void*), void* p2, size_t p3, cl_uint p4, const cl_mem* p5, const void** p6, cl_uint p7, const cl_event* p8, cl_event* p9) { return clEnqueueNativeKernel_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } +#undef clEnqueueReadBuffer +#define clEnqueueReadBuffer clEnqueueReadBuffer_fn +inline cl_int clEnqueueReadBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, size_t p3, size_t p4, void* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueReadBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueReadBufferRect +#define clEnqueueReadBufferRect clEnqueueReadBufferRect_fn +inline cl_int clEnqueueReadBufferRect(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, void* p10, cl_uint p11, const cl_event* p12, cl_event* p13) { return clEnqueueReadBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); } +#undef clEnqueueReadImage +#define clEnqueueReadImage clEnqueueReadImage_fn +inline cl_int clEnqueueReadImage(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, size_t p5, size_t p6, void* p7, cl_uint p8, const cl_event* p9, cl_event* p10) { return clEnqueueReadImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } +#undef clEnqueueTask +#define clEnqueueTask clEnqueueTask_fn +inline cl_int clEnqueueTask(cl_command_queue p0, cl_kernel p1, cl_uint p2, const cl_event* p3, cl_event* p4) { return clEnqueueTask_pfn(p0, p1, p2, p3, p4); } +#undef clEnqueueUnmapMemObject +#define clEnqueueUnmapMemObject clEnqueueUnmapMemObject_fn +inline cl_int clEnqueueUnmapMemObject(cl_command_queue p0, cl_mem p1, void* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueUnmapMemObject_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueWaitForEvents +#define clEnqueueWaitForEvents clEnqueueWaitForEvents_fn +inline cl_int clEnqueueWaitForEvents(cl_command_queue p0, cl_uint p1, const cl_event* p2) { return clEnqueueWaitForEvents_pfn(p0, p1, p2); } +#undef clEnqueueWriteBuffer +#define clEnqueueWriteBuffer clEnqueueWriteBuffer_fn +inline cl_int clEnqueueWriteBuffer(cl_command_queue p0, cl_mem p1, cl_bool p2, size_t p3, size_t p4, const void* p5, cl_uint p6, const cl_event* p7, cl_event* p8) { return clEnqueueWriteBuffer_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clEnqueueWriteBufferRect +#define clEnqueueWriteBufferRect clEnqueueWriteBufferRect_fn +inline cl_int clEnqueueWriteBufferRect(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, const size_t* p5, size_t p6, size_t p7, size_t p8, size_t p9, const void* p10, cl_uint p11, const cl_event* p12, cl_event* p13) { return clEnqueueWriteBufferRect_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); } +#undef clEnqueueWriteImage +#define clEnqueueWriteImage clEnqueueWriteImage_fn +inline cl_int clEnqueueWriteImage(cl_command_queue p0, cl_mem p1, cl_bool p2, const size_t* p3, const size_t* p4, size_t p5, size_t p6, const void* p7, cl_uint p8, const cl_event* p9, cl_event* p10) { return clEnqueueWriteImage_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } +#undef clFinish +#define clFinish clFinish_fn +inline cl_int clFinish(cl_command_queue p0) { return clFinish_pfn(p0); } +#undef clFlush +#define clFlush clFlush_fn +inline cl_int clFlush(cl_command_queue p0) { return clFlush_pfn(p0); } +#undef clGetCommandQueueInfo +#define clGetCommandQueueInfo clGetCommandQueueInfo_fn +inline cl_int clGetCommandQueueInfo(cl_command_queue p0, cl_command_queue_info p1, size_t p2, void* p3, size_t* p4) { return clGetCommandQueueInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetContextInfo +#define clGetContextInfo clGetContextInfo_fn +inline cl_int clGetContextInfo(cl_context p0, cl_context_info p1, size_t p2, void* p3, size_t* p4) { return clGetContextInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetDeviceIDs +#define clGetDeviceIDs clGetDeviceIDs_fn +inline cl_int clGetDeviceIDs(cl_platform_id p0, cl_device_type p1, cl_uint p2, cl_device_id* p3, cl_uint* p4) { return clGetDeviceIDs_pfn(p0, p1, p2, p3, p4); } +#undef clGetDeviceInfo +#define clGetDeviceInfo clGetDeviceInfo_fn +inline cl_int clGetDeviceInfo(cl_device_id p0, cl_device_info p1, size_t p2, void* p3, size_t* p4) { return clGetDeviceInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetEventInfo +#define clGetEventInfo clGetEventInfo_fn +inline cl_int clGetEventInfo(cl_event p0, cl_event_info p1, size_t p2, void* p3, size_t* p4) { return clGetEventInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetEventProfilingInfo +#define clGetEventProfilingInfo clGetEventProfilingInfo_fn +inline cl_int clGetEventProfilingInfo(cl_event p0, cl_profiling_info p1, size_t p2, void* p3, size_t* p4) { return clGetEventProfilingInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetExtensionFunctionAddress +#define clGetExtensionFunctionAddress clGetExtensionFunctionAddress_fn +inline void* clGetExtensionFunctionAddress(const char* p0) { return clGetExtensionFunctionAddress_pfn(p0); } +#undef clGetExtensionFunctionAddressForPlatform +#define clGetExtensionFunctionAddressForPlatform clGetExtensionFunctionAddressForPlatform_fn +inline void* clGetExtensionFunctionAddressForPlatform(cl_platform_id p0, const char* p1) { return clGetExtensionFunctionAddressForPlatform_pfn(p0, p1); } +#undef clGetImageInfo +#define clGetImageInfo clGetImageInfo_fn +inline cl_int clGetImageInfo(cl_mem p0, cl_image_info p1, size_t p2, void* p3, size_t* p4) { return clGetImageInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetKernelArgInfo +#define clGetKernelArgInfo clGetKernelArgInfo_fn +inline cl_int clGetKernelArgInfo(cl_kernel p0, cl_uint p1, cl_kernel_arg_info p2, size_t p3, void* p4, size_t* p5) { return clGetKernelArgInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetKernelInfo +#define clGetKernelInfo clGetKernelInfo_fn +inline cl_int clGetKernelInfo(cl_kernel p0, cl_kernel_info p1, size_t p2, void* p3, size_t* p4) { return clGetKernelInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetKernelWorkGroupInfo +#define clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo_fn +inline cl_int clGetKernelWorkGroupInfo(cl_kernel p0, cl_device_id p1, cl_kernel_work_group_info p2, size_t p3, void* p4, size_t* p5) { return clGetKernelWorkGroupInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetMemObjectInfo +#define clGetMemObjectInfo clGetMemObjectInfo_fn +inline cl_int clGetMemObjectInfo(cl_mem p0, cl_mem_info p1, size_t p2, void* p3, size_t* p4) { return clGetMemObjectInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetPlatformIDs +#define clGetPlatformIDs clGetPlatformIDs_fn +inline cl_int clGetPlatformIDs(cl_uint p0, cl_platform_id* p1, cl_uint* p2) { return clGetPlatformIDs_pfn(p0, p1, p2); } +#undef clGetPlatformInfo +#define clGetPlatformInfo clGetPlatformInfo_fn +inline cl_int clGetPlatformInfo(cl_platform_id p0, cl_platform_info p1, size_t p2, void* p3, size_t* p4) { return clGetPlatformInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetProgramBuildInfo +#define clGetProgramBuildInfo clGetProgramBuildInfo_fn +inline cl_int clGetProgramBuildInfo(cl_program p0, cl_device_id p1, cl_program_build_info p2, size_t p3, void* p4, size_t* p5) { return clGetProgramBuildInfo_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetProgramInfo +#define clGetProgramInfo clGetProgramInfo_fn +inline cl_int clGetProgramInfo(cl_program p0, cl_program_info p1, size_t p2, void* p3, size_t* p4) { return clGetProgramInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetSamplerInfo +#define clGetSamplerInfo clGetSamplerInfo_fn +inline cl_int clGetSamplerInfo(cl_sampler p0, cl_sampler_info p1, size_t p2, void* p3, size_t* p4) { return clGetSamplerInfo_pfn(p0, p1, p2, p3, p4); } +#undef clGetSupportedImageFormats +#define clGetSupportedImageFormats clGetSupportedImageFormats_fn +inline cl_int clGetSupportedImageFormats(cl_context p0, cl_mem_flags p1, cl_mem_object_type p2, cl_uint p3, cl_image_format* p4, cl_uint* p5) { return clGetSupportedImageFormats_pfn(p0, p1, p2, p3, p4, p5); } +#undef clLinkProgram +#define clLinkProgram clLinkProgram_fn +inline cl_program clLinkProgram(cl_context p0, cl_uint p1, const cl_device_id* p2, const char* p3, cl_uint p4, const cl_program* p5, void (CL_CALLBACK*p6) (cl_program, void*), void* p7, cl_int* p8) { return clLinkProgram_pfn(p0, p1, p2, p3, p4, p5, p6, p7, p8); } +#undef clReleaseCommandQueue +#define clReleaseCommandQueue clReleaseCommandQueue_fn +inline cl_int clReleaseCommandQueue(cl_command_queue p0) { return clReleaseCommandQueue_pfn(p0); } +#undef clReleaseContext +#define clReleaseContext clReleaseContext_fn +inline cl_int clReleaseContext(cl_context p0) { return clReleaseContext_pfn(p0); } +#undef clReleaseDevice +#define clReleaseDevice clReleaseDevice_fn +inline cl_int clReleaseDevice(cl_device_id p0) { return clReleaseDevice_pfn(p0); } +#undef clReleaseEvent +#define clReleaseEvent clReleaseEvent_fn +inline cl_int clReleaseEvent(cl_event p0) { return clReleaseEvent_pfn(p0); } +#undef clReleaseKernel +#define clReleaseKernel clReleaseKernel_fn +inline cl_int clReleaseKernel(cl_kernel p0) { return clReleaseKernel_pfn(p0); } +#undef clReleaseMemObject +#define clReleaseMemObject clReleaseMemObject_fn +inline cl_int clReleaseMemObject(cl_mem p0) { return clReleaseMemObject_pfn(p0); } +#undef clReleaseProgram +#define clReleaseProgram clReleaseProgram_fn +inline cl_int clReleaseProgram(cl_program p0) { return clReleaseProgram_pfn(p0); } +#undef clReleaseSampler +#define clReleaseSampler clReleaseSampler_fn +inline cl_int clReleaseSampler(cl_sampler p0) { return clReleaseSampler_pfn(p0); } +#undef clRetainCommandQueue +#define clRetainCommandQueue clRetainCommandQueue_fn +inline cl_int clRetainCommandQueue(cl_command_queue p0) { return clRetainCommandQueue_pfn(p0); } +#undef clRetainContext +#define clRetainContext clRetainContext_fn +inline cl_int clRetainContext(cl_context p0) { return clRetainContext_pfn(p0); } +#undef clRetainDevice +#define clRetainDevice clRetainDevice_fn +inline cl_int clRetainDevice(cl_device_id p0) { return clRetainDevice_pfn(p0); } +#undef clRetainEvent +#define clRetainEvent clRetainEvent_fn +inline cl_int clRetainEvent(cl_event p0) { return clRetainEvent_pfn(p0); } +#undef clRetainKernel +#define clRetainKernel clRetainKernel_fn +inline cl_int clRetainKernel(cl_kernel p0) { return clRetainKernel_pfn(p0); } +#undef clRetainMemObject +#define clRetainMemObject clRetainMemObject_fn +inline cl_int clRetainMemObject(cl_mem p0) { return clRetainMemObject_pfn(p0); } +#undef clRetainProgram +#define clRetainProgram clRetainProgram_fn +inline cl_int clRetainProgram(cl_program p0) { return clRetainProgram_pfn(p0); } +#undef clRetainSampler +#define clRetainSampler clRetainSampler_fn +inline cl_int clRetainSampler(cl_sampler p0) { return clRetainSampler_pfn(p0); } +#undef clSetEventCallback +#define clSetEventCallback clSetEventCallback_fn +inline cl_int clSetEventCallback(cl_event p0, cl_int p1, void (CL_CALLBACK*p2) (cl_event, cl_int, void*), void* p3) { return clSetEventCallback_pfn(p0, p1, p2, p3); } +#undef clSetKernelArg +#define clSetKernelArg clSetKernelArg_fn +inline cl_int clSetKernelArg(cl_kernel p0, cl_uint p1, size_t p2, const void* p3) { return clSetKernelArg_pfn(p0, p1, p2, p3); } +#undef clSetMemObjectDestructorCallback +#define clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback_fn +inline cl_int clSetMemObjectDestructorCallback(cl_mem p0, void (CL_CALLBACK*p1) (cl_mem, void*), void* p2) { return clSetMemObjectDestructorCallback_pfn(p0, p1, p2); } +#undef clSetUserEventStatus +#define clSetUserEventStatus clSetUserEventStatus_fn +inline cl_int clSetUserEventStatus(cl_event p0, cl_int p1) { return clSetUserEventStatus_pfn(p0, p1); } +#undef clUnloadCompiler +#define clUnloadCompiler clUnloadCompiler_fn +inline cl_int clUnloadCompiler() { return clUnloadCompiler_pfn(); } +#undef clUnloadPlatformCompiler +#define clUnloadPlatformCompiler clUnloadPlatformCompiler_fn +inline cl_int clUnloadPlatformCompiler(cl_platform_id p0) { return clUnloadPlatformCompiler_pfn(p0); } +#undef clWaitForEvents +#define clWaitForEvents clWaitForEvents_fn +inline cl_int clWaitForEvents(cl_uint p0, const cl_event* p1) { return clWaitForEvents_pfn(p0, p1); } diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp new file mode 100644 index 0000000..0b12aed --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp @@ -0,0 +1,62 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#define clCreateFromGLBuffer clCreateFromGLBuffer_ +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_ +#define clCreateFromGLTexture clCreateFromGLTexture_ +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_ +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_ +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_ +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_ +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_ +#define clGetGLObjectInfo clGetGLObjectInfo_ +#define clGetGLTextureInfo clGetGLTextureInfo_ + +#if defined __APPLE__ +#include +#else +#include +#endif + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_pfn +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_pfn +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_pfn +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_pfn +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_pfn +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_pfn +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_pfn +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_pfn +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_pfn +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_pfn + +#ifdef cl_khr_gl_sharing + +// generated by parser_cl.py +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLBuffer)(cl_context, cl_mem_flags, cl_GLuint, int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLRenderbuffer)(cl_context, cl_mem_flags, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture2D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture3D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueAcquireGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReleaseGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLContextInfoKHR)(const cl_context_properties*, cl_gl_context_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLObjectInfo)(cl_mem, cl_gl_object_type*, cl_GLuint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLTextureInfo)(cl_mem, cl_gl_texture_info, size_t, void*, size_t*); + +#endif // cl_khr_gl_sharing diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp new file mode 100644 index 0000000..12f342b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp @@ -0,0 +1,42 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP +#error "Invalid usage" +#endif + +#ifdef cl_khr_gl_sharing + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_fn +inline cl_mem clCreateFromGLBuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, int* p3) { return clCreateFromGLBuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_fn +inline cl_mem clCreateFromGLRenderbuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, cl_int* p3) { return clCreateFromGLRenderbuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_fn +inline cl_mem clCreateFromGLTexture(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_fn +inline cl_mem clCreateFromGLTexture2D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture2D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_fn +inline cl_mem clCreateFromGLTexture3D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture3D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_fn +inline cl_int clEnqueueAcquireGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueAcquireGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_fn +inline cl_int clEnqueueReleaseGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueReleaseGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_fn +inline cl_int clGetGLContextInfoKHR(const cl_context_properties* p0, cl_gl_context_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLContextInfoKHR_pfn(p0, p1, p2, p3, p4); } +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_fn +inline cl_int clGetGLObjectInfo(cl_mem p0, cl_gl_object_type* p1, cl_GLuint* p2) { return clGetGLObjectInfo_pfn(p0, p1, p2); } +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_fn +inline cl_int clGetGLTextureInfo(cl_mem p0, cl_gl_texture_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLTextureInfo_pfn(p0, p1, p2, p3, p4); } + +#endif // cl_khr_gl_sharing diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdblas.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdblas.hpp new file mode 100644 index 0000000..2ad8ac0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdblas.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP +#define OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP + +#ifdef HAVE_CLAMDBLAS + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_clamdblas.hpp" + +#endif // HAVE_CLAMDBLAS + +#endif // OPENCV_CORE_OCL_RUNTIME_CLAMDBLAS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdfft.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdfft.hpp new file mode 100644 index 0000000..a328f72 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_clamdfft.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP +#define OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP + +#ifdef HAVE_CLAMDFFT + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_clamdfft.hpp" + +#endif // HAVE_CLAMDFFT + +#endif // OPENCV_CORE_OCL_RUNTIME_CLAMDFFT_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core.hpp new file mode 100644 index 0000000..0404b31 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core.hpp @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP + +#ifdef HAVE_OPENCL + +#ifndef CL_RUNTIME_EXPORT +#if (defined(BUILD_SHARED_LIBS) || defined(OPENCV_CORE_SHARED)) && (defined _WIN32 || defined WINCE) && \ + !(defined(__OPENCV_BUILD) && defined(OPENCV_MODULE_IS_PART_OF_WORLD)) +#define CL_RUNTIME_EXPORT __declspec(dllimport) +#else +#define CL_RUNTIME_EXPORT +#endif +#endif + +#ifdef HAVE_OPENCL_SVM +#define clSVMAlloc clSVMAlloc_ +#define clSVMFree clSVMFree_ +#define clSetKernelArgSVMPointer clSetKernelArgSVMPointer_ +#define clSetKernelExecInfo clSetKernelExecInfo_ +#define clEnqueueSVMFree clEnqueueSVMFree_ +#define clEnqueueSVMMemcpy clEnqueueSVMMemcpy_ +#define clEnqueueSVMMemFill clEnqueueSVMMemFill_ +#define clEnqueueSVMMap clEnqueueSVMMap_ +#define clEnqueueSVMUnmap clEnqueueSVMUnmap_ +#endif + +#include "autogenerated/opencl_core.hpp" + +#ifndef CL_DEVICE_DOUBLE_FP_CONFIG +#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 +#endif + +#ifndef CL_DEVICE_HALF_FP_CONFIG +#define CL_DEVICE_HALF_FP_CONFIG 0x1033 +#endif + +#ifndef CL_VERSION_1_2 +#define CV_REQUIRE_OPENCL_1_2_ERROR CV_Error(cv::Error::OpenCLApiCallError, "OpenCV compiled without OpenCL v1.2 support, so we can't use functionality from OpenCL v1.2") +#endif + +#endif // HAVE_OPENCL + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_CORE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp new file mode 100644 index 0000000..38fcae9 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_core_wrappers.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP + +#include "autogenerated/opencl_core_wrappers.hpp" + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_WRAPPERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl.hpp new file mode 100644 index 0000000..659c7d8 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl.hpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP + +#if defined HAVE_OPENCL && defined HAVE_OPENGL + +#include "opencl_core.hpp" + +#include "autogenerated/opencl_gl.hpp" + +#endif // defined HAVE_OPENCL && defined HAVE_OPENGL + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp new file mode 100644 index 0000000..9700004 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP + +#include "autogenerated/opencl_gl_wrappers.hpp" + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_20.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_20.hpp new file mode 100644 index 0000000..9636b19 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_20.hpp @@ -0,0 +1,48 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP + +#if defined(HAVE_OPENCL_SVM) +#include "opencl_core.hpp" + +#include "opencl_svm_definitions.hpp" + +#undef clSVMAlloc +#define clSVMAlloc clSVMAlloc_pfn +#undef clSVMFree +#define clSVMFree clSVMFree_pfn +#undef clSetKernelArgSVMPointer +#define clSetKernelArgSVMPointer clSetKernelArgSVMPointer_pfn +#undef clSetKernelExecInfo +//#define clSetKernelExecInfo clSetKernelExecInfo_pfn +#undef clEnqueueSVMFree +//#define clEnqueueSVMFree clEnqueueSVMFree_pfn +#undef clEnqueueSVMMemcpy +#define clEnqueueSVMMemcpy clEnqueueSVMMemcpy_pfn +#undef clEnqueueSVMMemFill +#define clEnqueueSVMMemFill clEnqueueSVMMemFill_pfn +#undef clEnqueueSVMMap +#define clEnqueueSVMMap clEnqueueSVMMap_pfn +#undef clEnqueueSVMUnmap +#define clEnqueueSVMUnmap clEnqueueSVMUnmap_pfn + +extern CL_RUNTIME_EXPORT void* (CL_API_CALL *clSVMAlloc)(cl_context context, cl_svm_mem_flags flags, size_t size, unsigned int alignment); +extern CL_RUNTIME_EXPORT void (CL_API_CALL *clSVMFree)(cl_context context, void* svm_pointer); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clSetKernelArgSVMPointer)(cl_kernel kernel, cl_uint arg_index, const void* arg_value); +//extern CL_RUNTIME_EXPORT void* (CL_API_CALL *clSetKernelExecInfo)(cl_kernel kernel, cl_kernel_exec_info param_name, size_t param_value_size, const void* param_value); +//extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMFree)(cl_command_queue command_queue, cl_uint num_svm_pointers, void* svm_pointers[], +// void (CL_CALLBACK *pfn_free_func)(cl_command_queue queue, cl_uint num_svm_pointers, void* svm_pointers[], void* user_data), void* user_data, +// cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMemcpy)(cl_command_queue command_queue, cl_bool blocking_copy, void* dst_ptr, const void* src_ptr, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMemFill)(cl_command_queue command_queue, void* svm_ptr, const void* pattern, size_t pattern_size, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMMap)(cl_command_queue command_queue, cl_bool blocking_map, cl_map_flags map_flags, void* svm_ptr, size_t size, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL *clEnqueueSVMUnmap)(cl_command_queue command_queue, void* svm_ptr, + cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); + +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_2_0_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp new file mode 100644 index 0000000..97c927b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_definitions.hpp @@ -0,0 +1,42 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP + +#if defined(HAVE_OPENCL_SVM) +#if defined(CL_VERSION_2_0) + +// OpenCL 2.0 contains SVM definitions + +#else + +typedef cl_bitfield cl_device_svm_capabilities; +typedef cl_bitfield cl_svm_mem_flags; +typedef cl_uint cl_kernel_exec_info; + +// +// TODO Add real values after OpenCL 2.0 release +// + +#ifndef CL_DEVICE_SVM_CAPABILITIES +#define CL_DEVICE_SVM_CAPABILITIES 0x1053 + +#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) +#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) +#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) +#define CL_DEVICE_SVM_ATOMICS (1 << 3) +#endif + +#ifndef CL_MEM_SVM_FINE_GRAIN_BUFFER +#define CL_MEM_SVM_FINE_GRAIN_BUFFER (1 << 10) +#endif + +#ifndef CL_MEM_SVM_ATOMICS +#define CL_MEM_SVM_ATOMICS (1 << 11) +#endif + + +#endif // CL_VERSION_2_0 +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_DEFINITIONS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp new file mode 100644 index 0000000..497bc3d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opencl/runtime/opencl_svm_hsa_extension.hpp @@ -0,0 +1,166 @@ +/* See LICENSE file in the root OpenCV directory */ + +#ifndef OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP +#define OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP + +#if defined(HAVE_OPENCL_SVM) +#include "opencl_core.hpp" + +#ifndef CL_DEVICE_SVM_CAPABILITIES_AMD +// +// Part of the file is an extract from the cl_ext.h file from AMD APP SDK package. +// Below is the original copyright. +// +/******************************************************************************* + * Copyright (c) 2008-2013 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +/******************************************* + * Shared Virtual Memory (SVM) extension + *******************************************/ +typedef cl_bitfield cl_device_svm_capabilities_amd; +typedef cl_bitfield cl_svm_mem_flags_amd; +typedef cl_uint cl_kernel_exec_info_amd; + +/* cl_device_info */ +#define CL_DEVICE_SVM_CAPABILITIES_AMD 0x1053 +#define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT_AMD 0x1054 + +/* cl_device_svm_capabilities_amd */ +#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER_AMD (1 << 0) +#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER_AMD (1 << 1) +#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM_AMD (1 << 2) +#define CL_DEVICE_SVM_ATOMICS_AMD (1 << 3) + +/* cl_svm_mem_flags_amd */ +#define CL_MEM_SVM_FINE_GRAIN_BUFFER_AMD (1 << 10) +#define CL_MEM_SVM_ATOMICS_AMD (1 << 11) + +/* cl_mem_info */ +#define CL_MEM_USES_SVM_POINTER_AMD 0x1109 + +/* cl_kernel_exec_info_amd */ +#define CL_KERNEL_EXEC_INFO_SVM_PTRS_AMD 0x11B6 +#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM_AMD 0x11B7 + +/* cl_command_type */ +#define CL_COMMAND_SVM_FREE_AMD 0x1209 +#define CL_COMMAND_SVM_MEMCPY_AMD 0x120A +#define CL_COMMAND_SVM_MEMFILL_AMD 0x120B +#define CL_COMMAND_SVM_MAP_AMD 0x120C +#define CL_COMMAND_SVM_UNMAP_AMD 0x120D + +typedef CL_API_ENTRY void* +(CL_API_CALL * clSVMAllocAMD_fn)( + cl_context /* context */, + cl_svm_mem_flags_amd /* flags */, + size_t /* size */, + unsigned int /* alignment */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY void +(CL_API_CALL * clSVMFreeAMD_fn)( + cl_context /* context */, + void* /* svm_pointer */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMFreeAMD_fn)( + cl_command_queue /* command_queue */, + cl_uint /* num_svm_pointers */, + void** /* svm_pointers */, + void (CL_CALLBACK *)( /*pfn_free_func*/ + cl_command_queue /* queue */, + cl_uint /* num_svm_pointers */, + void** /* svm_pointers */, + void* /* user_data */), + void* /* user_data */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMemcpyAMD_fn)( + cl_command_queue /* command_queue */, + cl_bool /* blocking_copy */, + void* /* dst_ptr */, + const void* /* src_ptr */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMemFillAMD_fn)( + cl_command_queue /* command_queue */, + void* /* svm_ptr */, + const void* /* pattern */, + size_t /* pattern_size */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMMapAMD_fn)( + cl_command_queue /* command_queue */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + void* /* svm_ptr */, + size_t /* size */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clEnqueueSVMUnmapAMD_fn)( + cl_command_queue /* command_queue */, + void* /* svm_ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event* /* event_wait_list */, + cl_event* /* event */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clSetKernelArgSVMPointerAMD_fn)( + cl_kernel /* kernel */, + cl_uint /* arg_index */, + const void * /* arg_value */ +) CL_EXT_SUFFIX__VERSION_1_2; + +typedef CL_API_ENTRY cl_int +(CL_API_CALL * clSetKernelExecInfoAMD_fn)( + cl_kernel /* kernel */, + cl_kernel_exec_info_amd /* param_name */, + size_t /* param_value_size */, + const void * /* param_value */ +) CL_EXT_SUFFIX__VERSION_1_2; + +#endif + +#endif // HAVE_OPENCL_SVM + +#endif // OPENCV_CORE_OCL_RUNTIME_OPENCL_SVM_HSA_EXTENSION_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/opengl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opengl.hpp new file mode 100644 index 0000000..a6288be --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/opengl.hpp @@ -0,0 +1,725 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OPENGL_HPP +#define OPENCV_CORE_OPENGL_HPP + +#ifndef __cplusplus +# error opengl.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "ocl.hpp" + +namespace cv { namespace ogl { + +/** @addtogroup core_opengl +This section describes OpenGL interoperability. + +To enable OpenGL support, configure OpenCV using CMake with WITH_OPENGL=ON . Currently OpenGL is +supported only with WIN32, GTK and Qt backends on Windows and Linux (MacOS and Android are not +supported). For GTK backend gtkglext-1.0 library is required. + +To use OpenGL functionality you should first create OpenGL context (window or frame buffer). You can +do this with namedWindow function or with other OpenGL toolkit (GLUT, for example). +*/ +//! @{ + +/////////////////// OpenGL Objects /////////////////// + +/** @brief Smart pointer for OpenGL buffer object with reference counting. + +Buffer Objects are OpenGL objects that store an array of unformatted memory allocated by the OpenGL +context. These can be used to store vertex data, pixel data retrieved from images or the +framebuffer, and a variety of other things. + +ogl::Buffer has interface similar with Mat interface and represents 2D array memory. + +ogl::Buffer supports memory transfers between host and device and also can be mapped to CUDA memory. + */ +class CV_EXPORTS Buffer +{ +public: + /** @brief The target defines how you intend to use the buffer object. + */ + enum Target + { + ARRAY_BUFFER = 0x8892, //!< The buffer will be used as a source for vertex data + ELEMENT_ARRAY_BUFFER = 0x8893, //!< The buffer will be used for indices (in glDrawElements, for example) + PIXEL_PACK_BUFFER = 0x88EB, //!< The buffer will be used for reading from OpenGL textures + PIXEL_UNPACK_BUFFER = 0x88EC //!< The buffer will be used for writing to OpenGL textures + }; + + enum Access + { + READ_ONLY = 0x88B8, + WRITE_ONLY = 0x88B9, + READ_WRITE = 0x88BA + }; + + /** @brief The constructors. + + Creates empty ogl::Buffer object, creates ogl::Buffer object from existed buffer ( abufId + parameter), allocates memory for ogl::Buffer object or copies from host/device memory. + */ + Buffer(); + + /** @overload + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param abufId Buffer object name. + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(int arows, int acols, int atype, unsigned int abufId, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param abufId Buffer object name. + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(Size asize, int atype, unsigned int abufId, bool autoRelease = false); + + /** @overload + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(int arows, int acols, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Buffer(Size asize, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or std::vector ). + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + explicit Buffer(InputArray arr, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Allocates memory for ogl::Buffer object. + + @param arows Number of rows in a 2D array. + @param acols Number of columns in a 2D array. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(int arows, int acols, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param atype Array type ( CV_8UC1, ..., CV_64FC4 ). See Mat for details. + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(Size asize, int atype, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Decrements the reference counter and destroys the buffer object if needed. + + The function will call setAutoRelease(true) . + */ + void release(); + + /** @brief Sets auto release mode. + + The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was + bound to a window it could be released at any time (user can close a window). If object's destructor + is called after destruction of the context it will cause an error. Thus ogl::Buffer doesn't destroy + OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL context). + This function can force ogl::Buffer destructor to destroy OpenGL object. + @param flag Auto release mode (if true, release will be called in object's destructor). + */ + void setAutoRelease(bool flag); + + /** @brief Copies from host/device memory to OpenGL buffer. + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or std::vector ). + @param target Buffer usage. See cv::ogl::Buffer::Target . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void copyFrom(InputArray arr, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @overload */ + void copyFrom(InputArray arr, cuda::Stream& stream, Target target = ARRAY_BUFFER, bool autoRelease = false); + + /** @brief Copies from OpenGL buffer to host/device memory or another OpenGL buffer object. + + @param arr Destination array (host or device memory, can be Mat , cuda::GpuMat , std::vector or + ogl::Buffer ). + */ + void copyTo(OutputArray arr) const; + + /** @overload */ + void copyTo(OutputArray arr, cuda::Stream& stream) const; + + /** @brief Creates a full copy of the buffer object and the underlying data. + + @param target Buffer usage for destination buffer. + @param autoRelease Auto release mode for destination buffer. + */ + Buffer clone(Target target = ARRAY_BUFFER, bool autoRelease = false) const; + + /** @brief Binds OpenGL buffer to the specified buffer binding point. + + @param target Binding point. See cv::ogl::Buffer::Target . + */ + void bind(Target target) const; + + /** @brief Unbind any buffers from the specified binding point. + + @param target Binding point. See cv::ogl::Buffer::Target . + */ + static void unbind(Target target); + + /** @brief Maps OpenGL buffer to host memory. + + mapHost maps to the client's address space the entire data store of the buffer object. The data can + then be directly read and/or written relative to the returned pointer, depending on the specified + access policy. + + A mapped data store must be unmapped with ogl::Buffer::unmapHost before its buffer object is used. + + This operation can lead to memory transfers between host and device. + + Only one buffer object can be mapped at a time. + @param access Access policy, indicating whether it will be possible to read from, write to, or both + read from and write to the buffer object's mapped data store. The symbolic constant must be + ogl::Buffer::READ_ONLY , ogl::Buffer::WRITE_ONLY or ogl::Buffer::READ_WRITE . + */ + Mat mapHost(Access access); + + /** @brief Unmaps OpenGL buffer. + */ + void unmapHost(); + + //! map to device memory (blocking) + cuda::GpuMat mapDevice(); + void unmapDevice(); + + /** @brief Maps OpenGL buffer to CUDA device memory. + + This operation doesn't copy data. Several buffer objects can be mapped to CUDA memory at a time. + + A mapped data store must be unmapped with ogl::Buffer::unmapDevice before its buffer object is used. + */ + cuda::GpuMat mapDevice(cuda::Stream& stream); + + /** @brief Unmaps OpenGL buffer. + */ + void unmapDevice(cuda::Stream& stream); + + int rows() const; + int cols() const; + Size size() const; + bool empty() const; + + int type() const; + int depth() const; + int channels() const; + int elemSize() const; + int elemSize1() const; + + //! get OpenGL opject id + unsigned int bufId() const; + + class Impl; + +private: + Ptr impl_; + int rows_; + int cols_; + int type_; +}; + +/** @brief Smart pointer for OpenGL 2D texture memory with reference counting. + */ +class CV_EXPORTS Texture2D +{ +public: + /** @brief An Image Format describes the way that the images in Textures store their data. + */ + enum Format + { + NONE = 0, + DEPTH_COMPONENT = 0x1902, //!< Depth + RGB = 0x1907, //!< Red, Green, Blue + RGBA = 0x1908 //!< Red, Green, Blue, Alpha + }; + + /** @brief The constructors. + + Creates empty ogl::Texture2D object, allocates memory for ogl::Texture2D object or copies from + host/device memory. + */ + Texture2D(); + + /** @overload */ + Texture2D(int arows, int acols, Format aformat, unsigned int atexId, bool autoRelease = false); + + /** @overload */ + Texture2D(Size asize, Format aformat, unsigned int atexId, bool autoRelease = false); + + /** @overload + @param arows Number of rows. + @param acols Number of columns. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Texture2D(int arows, int acols, Format aformat, bool autoRelease = false); + + /** @overload + @param asize 2D array size. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + Texture2D(Size asize, Format aformat, bool autoRelease = false); + + /** @overload + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or ogl::Buffer ). + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + explicit Texture2D(InputArray arr, bool autoRelease = false); + + /** @brief Allocates memory for ogl::Texture2D object. + + @param arows Number of rows. + @param acols Number of columns. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(int arows, int acols, Format aformat, bool autoRelease = false); + /** @overload + @param asize 2D array size. + @param aformat Image format. See cv::ogl::Texture2D::Format . + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void create(Size asize, Format aformat, bool autoRelease = false); + + /** @brief Decrements the reference counter and destroys the texture object if needed. + + The function will call setAutoRelease(true) . + */ + void release(); + + /** @brief Sets auto release mode. + + @param flag Auto release mode (if true, release will be called in object's destructor). + + The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was + bound to a window it could be released at any time (user can close a window). If object's destructor + is called after destruction of the context it will cause an error. Thus ogl::Texture2D doesn't + destroy OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL + context). This function can force ogl::Texture2D destructor to destroy OpenGL object. + */ + void setAutoRelease(bool flag); + + /** @brief Copies from host/device memory to OpenGL texture. + + @param arr Input array (host or device memory, it can be Mat , cuda::GpuMat or ogl::Buffer ). + @param autoRelease Auto release mode (if true, release will be called in object's destructor). + */ + void copyFrom(InputArray arr, bool autoRelease = false); + + /** @brief Copies from OpenGL texture to host/device memory or another OpenGL texture object. + + @param arr Destination array (host or device memory, can be Mat , cuda::GpuMat , ogl::Buffer or + ogl::Texture2D ). + @param ddepth Destination depth. + @param autoRelease Auto release mode for destination buffer (if arr is OpenGL buffer or texture). + */ + void copyTo(OutputArray arr, int ddepth = CV_32F, bool autoRelease = false) const; + + /** @brief Binds texture to current active texture unit for GL_TEXTURE_2D target. + */ + void bind() const; + + int rows() const; + int cols() const; + Size size() const; + bool empty() const; + + Format format() const; + + //! get OpenGL opject id + unsigned int texId() const; + + class Impl; + +private: + Ptr impl_; + int rows_; + int cols_; + Format format_; +}; + +/** @brief Wrapper for OpenGL Client-Side Vertex arrays. + +ogl::Arrays stores vertex data in ogl::Buffer objects. + */ +class CV_EXPORTS Arrays +{ +public: + /** @brief Default constructor + */ + Arrays(); + + /** @brief Sets an array of vertex coordinates. + @param vertex array with vertex coordinates, can be both host and device memory. + */ + void setVertexArray(InputArray vertex); + + /** @brief Resets vertex coordinates. + */ + void resetVertexArray(); + + /** @brief Sets an array of vertex colors. + @param color array with vertex colors, can be both host and device memory. + */ + void setColorArray(InputArray color); + + /** @brief Resets vertex colors. + */ + void resetColorArray(); + + /** @brief Sets an array of vertex normals. + @param normal array with vertex normals, can be both host and device memory. + */ + void setNormalArray(InputArray normal); + + /** @brief Resets vertex normals. + */ + void resetNormalArray(); + + /** @brief Sets an array of vertex texture coordinates. + @param texCoord array with vertex texture coordinates, can be both host and device memory. + */ + void setTexCoordArray(InputArray texCoord); + + /** @brief Resets vertex texture coordinates. + */ + void resetTexCoordArray(); + + /** @brief Releases all inner buffers. + */ + void release(); + + /** @brief Sets auto release mode all inner buffers. + @param flag Auto release mode. + */ + void setAutoRelease(bool flag); + + /** @brief Binds all vertex arrays. + */ + void bind() const; + + /** @brief Returns the vertex count. + */ + int size() const; + bool empty() const; + +private: + int size_; + Buffer vertex_; + Buffer color_; + Buffer normal_; + Buffer texCoord_; +}; + +/////////////////// Render Functions /////////////////// + +//! render mode +enum RenderModes { + POINTS = 0x0000, + LINES = 0x0001, + LINE_LOOP = 0x0002, + LINE_STRIP = 0x0003, + TRIANGLES = 0x0004, + TRIANGLE_STRIP = 0x0005, + TRIANGLE_FAN = 0x0006, + QUADS = 0x0007, + QUAD_STRIP = 0x0008, + POLYGON = 0x0009 +}; + +/** @brief Render OpenGL texture or primitives. +@param tex Texture to draw. +@param wndRect Region of window, where to draw a texture (normalized coordinates). +@param texRect Region of texture to draw (normalized coordinates). + */ +CV_EXPORTS void render(const Texture2D& tex, + Rect_ wndRect = Rect_(0.0, 0.0, 1.0, 1.0), + Rect_ texRect = Rect_(0.0, 0.0, 1.0, 1.0)); + +/** @overload +@param arr Array of privitives vertices. +@param mode Render mode. One of cv::ogl::RenderModes +@param color Color for all vertices. Will be used if arr doesn't contain color array. +*/ +CV_EXPORTS void render(const Arrays& arr, int mode = POINTS, Scalar color = Scalar::all(255)); + +/** @overload +@param arr Array of privitives vertices. +@param indices Array of vertices indices (host or device memory). +@param mode Render mode. One of cv::ogl::RenderModes +@param color Color for all vertices. Will be used if arr doesn't contain color array. +*/ +CV_EXPORTS void render(const Arrays& arr, InputArray indices, int mode = POINTS, Scalar color = Scalar::all(255)); + +/////////////////// CL-GL Interoperability Functions /////////////////// + +namespace ocl { +using namespace cv::ocl; + +// TODO static functions in the Context class +/** @brief Creates OpenCL context from GL. +@return Returns reference to OpenCL Context + */ +CV_EXPORTS Context& initializeContextFromGL(); + +} // namespace cv::ogl::ocl + +/** @brief Converts InputArray to Texture2D object. +@param src - source InputArray. +@param texture - destination Texture2D object. + */ +CV_EXPORTS void convertToGLTexture2D(InputArray src, Texture2D& texture); + +/** @brief Converts Texture2D object to OutputArray. +@param texture - source Texture2D object. +@param dst - destination OutputArray. + */ +CV_EXPORTS void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst); + +/** @brief Maps Buffer object to process on CL side (convert to UMat). + +Function creates CL buffer from GL one, and then constructs UMat that can be used +to process buffer data with OpenCV functions. Note that in current implementation +UMat constructed this way doesn't own corresponding GL buffer object, so it is +the user responsibility to close down CL/GL buffers relationships by explicitly +calling unmapGLBuffer() function. +@param buffer - source Buffer object. +@param accessFlags - data access flags (ACCESS_READ|ACCESS_WRITE). +@return Returns UMat object + */ +CV_EXPORTS UMat mapGLBuffer(const Buffer& buffer, int accessFlags = ACCESS_READ|ACCESS_WRITE); + +/** @brief Unmaps Buffer object (releases UMat, previously mapped from Buffer). + +Function must be called explicitly by the user for each UMat previously constructed +by the call to mapGLBuffer() function. +@param u - source UMat, created by mapGLBuffer(). + */ +CV_EXPORTS void unmapGLBuffer(UMat& u); + +//! @} +}} // namespace cv::ogl + +namespace cv { namespace cuda { + +/** @brief Sets a CUDA device and initializes it for the current thread with OpenGL interoperability. + +This function should be explicitly called after OpenGL context creation and before any CUDA calls. +@param device System index of a CUDA device starting with 0. +@ingroup core_opengl + */ +CV_EXPORTS void setGlDevice(int device = 0); + +}} + +//! @cond IGNORED + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +inline +cv::ogl::Buffer::Buffer(int arows, int acols, int atype, Target target, bool autoRelease) : rows_(0), cols_(0), type_(0) +{ + create(arows, acols, atype, target, autoRelease); +} + +inline +cv::ogl::Buffer::Buffer(Size asize, int atype, Target target, bool autoRelease) : rows_(0), cols_(0), type_(0) +{ + create(asize, atype, target, autoRelease); +} + +inline +void cv::ogl::Buffer::create(Size asize, int atype, Target target, bool autoRelease) +{ + create(asize.height, asize.width, atype, target, autoRelease); +} + +inline +int cv::ogl::Buffer::rows() const +{ + return rows_; +} + +inline +int cv::ogl::Buffer::cols() const +{ + return cols_; +} + +inline +cv::Size cv::ogl::Buffer::size() const +{ + return Size(cols_, rows_); +} + +inline +bool cv::ogl::Buffer::empty() const +{ + return rows_ == 0 || cols_ == 0; +} + +inline +int cv::ogl::Buffer::type() const +{ + return type_; +} + +inline +int cv::ogl::Buffer::depth() const +{ + return CV_MAT_DEPTH(type_); +} + +inline +int cv::ogl::Buffer::channels() const +{ + return CV_MAT_CN(type_); +} + +inline +int cv::ogl::Buffer::elemSize() const +{ + return CV_ELEM_SIZE(type_); +} + +inline +int cv::ogl::Buffer::elemSize1() const +{ + return CV_ELEM_SIZE1(type_); +} + +/////// + +inline +cv::ogl::Texture2D::Texture2D(int arows, int acols, Format aformat, bool autoRelease) : rows_(0), cols_(0), format_(NONE) +{ + create(arows, acols, aformat, autoRelease); +} + +inline +cv::ogl::Texture2D::Texture2D(Size asize, Format aformat, bool autoRelease) : rows_(0), cols_(0), format_(NONE) +{ + create(asize, aformat, autoRelease); +} + +inline +void cv::ogl::Texture2D::create(Size asize, Format aformat, bool autoRelease) +{ + create(asize.height, asize.width, aformat, autoRelease); +} + +inline +int cv::ogl::Texture2D::rows() const +{ + return rows_; +} + +inline +int cv::ogl::Texture2D::cols() const +{ + return cols_; +} + +inline +cv::Size cv::ogl::Texture2D::size() const +{ + return Size(cols_, rows_); +} + +inline +bool cv::ogl::Texture2D::empty() const +{ + return rows_ == 0 || cols_ == 0; +} + +inline +cv::ogl::Texture2D::Format cv::ogl::Texture2D::format() const +{ + return format_; +} + +/////// + +inline +cv::ogl::Arrays::Arrays() : size_(0) +{ +} + +inline +int cv::ogl::Arrays::size() const +{ + return size_; +} + +inline +bool cv::ogl::Arrays::empty() const +{ + return size_ == 0; +} + +//! @endcond + +#endif /* OPENCV_CORE_OPENGL_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/operations.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/operations.hpp new file mode 100644 index 0000000..ef1808a --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/operations.hpp @@ -0,0 +1,573 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_OPERATIONS_HPP +#define OPENCV_CORE_OPERATIONS_HPP + +#ifndef __cplusplus +# error operations.hpp header must be compiled as C++ +#endif + +#include + +//! @cond IGNORED + +namespace cv +{ + +////////////////////////////// Matx methods depending on core API ///////////////////////////// + +namespace internal +{ + +template struct Matx_FastInvOp +{ + bool operator()(const Matx<_Tp, m, n>& a, Matx<_Tp, n, m>& b, int method) const + { + return invert(a, b, method) != 0; + } +}; + +template struct Matx_FastInvOp<_Tp, m, m> +{ + bool operator()(const Matx<_Tp, m, m>& a, Matx<_Tp, m, m>& b, int method) const + { + if (method == DECOMP_LU || method == DECOMP_CHOLESKY) + { + Matx<_Tp, m, m> temp = a; + + // assume that b is all 0's on input => make it a unity matrix + for (int i = 0; i < m; i++) + b(i, i) = (_Tp)1; + + if (method == DECOMP_CHOLESKY) + return Cholesky(temp.val, m*sizeof(_Tp), m, b.val, m*sizeof(_Tp), m); + + return LU(temp.val, m*sizeof(_Tp), m, b.val, m*sizeof(_Tp), m) != 0; + } + else + { + return invert(a, b, method) != 0; + } + } +}; + +template struct Matx_FastInvOp<_Tp, 2, 2> +{ + bool operator()(const Matx<_Tp, 2, 2>& a, Matx<_Tp, 2, 2>& b, int /*method*/) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + b(1,1) = a(0,0)*d; + b(0,0) = a(1,1)*d; + b(0,1) = -a(0,1)*d; + b(1,0) = -a(1,0)*d; + return true; + } +}; + +template struct Matx_FastInvOp<_Tp, 3, 3> +{ + bool operator()(const Matx<_Tp, 3, 3>& a, Matx<_Tp, 3, 3>& b, int /*method*/) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + b(0,0) = (a(1,1) * a(2,2) - a(1,2) * a(2,1)) * d; + b(0,1) = (a(0,2) * a(2,1) - a(0,1) * a(2,2)) * d; + b(0,2) = (a(0,1) * a(1,2) - a(0,2) * a(1,1)) * d; + + b(1,0) = (a(1,2) * a(2,0) - a(1,0) * a(2,2)) * d; + b(1,1) = (a(0,0) * a(2,2) - a(0,2) * a(2,0)) * d; + b(1,2) = (a(0,2) * a(1,0) - a(0,0) * a(1,2)) * d; + + b(2,0) = (a(1,0) * a(2,1) - a(1,1) * a(2,0)) * d; + b(2,1) = (a(0,1) * a(2,0) - a(0,0) * a(2,1)) * d; + b(2,2) = (a(0,0) * a(1,1) - a(0,1) * a(1,0)) * d; + return true; + } +}; + + +template struct Matx_FastSolveOp +{ + bool operator()(const Matx<_Tp, m, l>& a, const Matx<_Tp, m, n>& b, + Matx<_Tp, l, n>& x, int method) const + { + return cv::solve(a, b, x, method); + } +}; + +template struct Matx_FastSolveOp<_Tp, m, m, n> +{ + bool operator()(const Matx<_Tp, m, m>& a, const Matx<_Tp, m, n>& b, + Matx<_Tp, m, n>& x, int method) const + { + if (method == DECOMP_LU || method == DECOMP_CHOLESKY) + { + Matx<_Tp, m, m> temp = a; + x = b; + if( method == DECOMP_CHOLESKY ) + return Cholesky(temp.val, m*sizeof(_Tp), m, x.val, n*sizeof(_Tp), n); + + return LU(temp.val, m*sizeof(_Tp), m, x.val, n*sizeof(_Tp), n) != 0; + } + else + { + return cv::solve(a, b, x, method); + } + } +}; + +template struct Matx_FastSolveOp<_Tp, 2, 2, 1> +{ + bool operator()(const Matx<_Tp, 2, 2>& a, const Matx<_Tp, 2, 1>& b, + Matx<_Tp, 2, 1>& x, int) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + x(0) = (b(0)*a(1,1) - b(1)*a(0,1))*d; + x(1) = (b(1)*a(0,0) - b(0)*a(1,0))*d; + return true; + } +}; + +template struct Matx_FastSolveOp<_Tp, 3, 3, 1> +{ + bool operator()(const Matx<_Tp, 3, 3>& a, const Matx<_Tp, 3, 1>& b, + Matx<_Tp, 3, 1>& x, int) const + { + _Tp d = (_Tp)determinant(a); + if (d == 0) + return false; + d = 1/d; + x(0) = d*(b(0)*(a(1,1)*a(2,2) - a(1,2)*a(2,1)) - + a(0,1)*(b(1)*a(2,2) - a(1,2)*b(2)) + + a(0,2)*(b(1)*a(2,1) - a(1,1)*b(2))); + + x(1) = d*(a(0,0)*(b(1)*a(2,2) - a(1,2)*b(2)) - + b(0)*(a(1,0)*a(2,2) - a(1,2)*a(2,0)) + + a(0,2)*(a(1,0)*b(2) - b(1)*a(2,0))); + + x(2) = d*(a(0,0)*(a(1,1)*b(2) - b(1)*a(2,1)) - + a(0,1)*(a(1,0)*b(2) - b(1)*a(2,0)) + + b(0)*(a(1,0)*a(2,1) - a(1,1)*a(2,0))); + return true; + } +}; + +} // internal + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::randu(_Tp a, _Tp b) +{ + Matx<_Tp,m,n> M; + cv::randu(M, Scalar(a), Scalar(b)); + return M; +} + +template inline +Matx<_Tp,m,n> Matx<_Tp,m,n>::randn(_Tp a, _Tp b) +{ + Matx<_Tp,m,n> M; + cv::randn(M, Scalar(a), Scalar(b)); + return M; +} + +template inline +Matx<_Tp, n, m> Matx<_Tp, m, n>::inv(int method, bool *p_is_ok /*= NULL*/) const +{ + Matx<_Tp, n, m> b; + bool ok = cv::internal::Matx_FastInvOp<_Tp, m, n>()(*this, b, method); + if (p_is_ok) *p_is_ok = ok; + return ok ? b : Matx<_Tp, n, m>::zeros(); +} + +template template inline +Matx<_Tp, n, l> Matx<_Tp, m, n>::solve(const Matx<_Tp, m, l>& rhs, int method) const +{ + Matx<_Tp, n, l> x; + bool ok = cv::internal::Matx_FastSolveOp<_Tp, m, n, l>()(*this, rhs, x, method); + return ok ? x : Matx<_Tp, n, l>::zeros(); +} + + + +////////////////////////// Augmenting algebraic & logical operations ////////////////////////// + +#define CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + static inline A& operator op (A& a, const B& b) { cvop; return a; } + +#define CV_MAT_AUG_OPERATOR(op, cvop, A, B) \ + CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + CV_MAT_AUG_OPERATOR1(op, cvop, const A, B) + +#define CV_MAT_AUG_OPERATOR_T(op, cvop, A, B) \ + template CV_MAT_AUG_OPERATOR1(op, cvop, A, B) \ + template CV_MAT_AUG_OPERATOR1(op, cvop, const A, B) + +#define CV_MAT_AUG_OPERATOR_TN(op, cvop, A) \ + template static inline A& operator op (A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } \ + template static inline const A& operator op (const A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } + +CV_MAT_AUG_OPERATOR (+=, cv::add(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (+=, cv::add(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(+=, cv::add(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (-=, cv::subtract(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (-=, cv::subtract(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat, Mat) +CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR (*=, a.convertTo(a, -1, b), Mat, double) +CV_MAT_AUG_OPERATOR_T(*=, a.convertTo(a, -1, b), Mat_<_Tp>, double) +CV_MAT_AUG_OPERATOR_TN(*=, cv::gemm(a, Mat(b), 1, Mat(), 0, a, 0), Mat) +CV_MAT_AUG_OPERATOR_TN(*=, cv::gemm(a, Mat(b), 1, Mat(), 0, a, 0), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (/=, cv::divide(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR_T(/=, cv::divide(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(/=, cv::divide(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR (/=, a.convertTo((Mat&)a, -1, 1./b), Mat, double) +CV_MAT_AUG_OPERATOR_T(/=, a.convertTo((Mat&)a, -1, 1./b), Mat_<_Tp>, double) +CV_MAT_AUG_OPERATOR_TN(/=, cv::divide(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(/=, cv::divide(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (&=, cv::bitwise_and(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (&=, cv::bitwise_and(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat, Mat) +CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat, Scalar) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Mat) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Scalar) +CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a, b, (const Mat&)a), Mat_<_Tp>, Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), (const Mat&)a), Mat) +CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), (const Mat&)a), Mat_<_Tp>) + +#undef CV_MAT_AUG_OPERATOR_TN +#undef CV_MAT_AUG_OPERATOR_T +#undef CV_MAT_AUG_OPERATOR +#undef CV_MAT_AUG_OPERATOR1 + + + +///////////////////////////////////////////// SVD ///////////////////////////////////////////// + +inline SVD::SVD() {} +inline SVD::SVD( InputArray m, int flags ) { operator ()(m, flags); } +inline void SVD::solveZ( InputArray m, OutputArray _dst ) +{ + Mat mtx = m.getMat(); + SVD svd(mtx, (mtx.rows >= mtx.cols ? 0 : SVD::FULL_UV)); + _dst.create(svd.vt.cols, 1, svd.vt.type()); + Mat dst = _dst.getMat(); + svd.vt.row(svd.vt.rows-1).reshape(1,svd.vt.cols).copyTo(dst); +} + +template inline void + SVD::compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w, Matx<_Tp, m, nm>& u, Matx<_Tp, n, nm>& vt ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _a(a, false), _u(u, false), _w(w, false), _vt(vt, false); + SVD::compute(_a, _w, _u, _vt); + CV_Assert(_w.data == (uchar*)&w.val[0] && _u.data == (uchar*)&u.val[0] && _vt.data == (uchar*)&vt.val[0]); +} + +template inline void +SVD::compute( const Matx<_Tp, m, n>& a, Matx<_Tp, nm, 1>& w ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _a(a, false), _w(w, false); + SVD::compute(_a, _w); + CV_Assert(_w.data == (uchar*)&w.val[0]); +} + +template inline void +SVD::backSubst( const Matx<_Tp, nm, 1>& w, const Matx<_Tp, m, nm>& u, + const Matx<_Tp, n, nm>& vt, const Matx<_Tp, m, nb>& rhs, + Matx<_Tp, n, nb>& dst ) +{ + CV_StaticAssert( nm == MIN(m, n), "Invalid size of output vector."); + Mat _u(u, false), _w(w, false), _vt(vt, false), _rhs(rhs, false), _dst(dst, false); + SVD::backSubst(_w, _u, _vt, _rhs, _dst); + CV_Assert(_dst.data == (uchar*)&dst.val[0]); +} + + + +/////////////////////////////////// Multiply-with-Carry RNG /////////////////////////////////// + +inline RNG::RNG() { state = 0xffffffff; } +inline RNG::RNG(uint64 _state) { state = _state ? _state : 0xffffffff; } + +inline RNG::operator uchar() { return (uchar)next(); } +inline RNG::operator schar() { return (schar)next(); } +inline RNG::operator ushort() { return (ushort)next(); } +inline RNG::operator short() { return (short)next(); } +inline RNG::operator int() { return (int)next(); } +inline RNG::operator unsigned() { return next(); } +inline RNG::operator float() { return next()*2.3283064365386962890625e-10f; } +inline RNG::operator double() { unsigned t = next(); return (((uint64)t << 32) | next()) * 5.4210108624275221700372640043497e-20; } + +inline unsigned RNG::operator ()(unsigned N) { return (unsigned)uniform(0,N); } +inline unsigned RNG::operator ()() { return next(); } + +inline int RNG::uniform(int a, int b) { return a == b ? a : (int)(next() % (b - a) + a); } +inline float RNG::uniform(float a, float b) { return ((float)*this)*(b - a) + a; } +inline double RNG::uniform(double a, double b) { return ((double)*this)*(b - a) + a; } + +inline bool RNG::operator ==(const RNG& other) const { return state == other.state; } + +inline unsigned RNG::next() +{ + state = (uint64)(unsigned)state* /*CV_RNG_COEFF*/ 4164903690U + (unsigned)(state >> 32); + return (unsigned)state; +} + +//! returns the next uniformly-distributed random number of the specified type +template static inline _Tp randu() +{ + return (_Tp)theRNG(); +} + +///////////////////////////////// Formatted string generation ///////////////////////////////// + +/** @brief Returns a text string formatted using the printf-like expression. + +The function acts like sprintf but forms and returns an STL string. It can be used to form an error +message in the Exception constructor. +@param fmt printf-compatible formatting specifiers. + */ +CV_EXPORTS String format( const char* fmt, ... ); + +///////////////////////////////// Formatted output of cv::Mat ///////////////////////////////// + +static inline +Ptr format(InputArray mtx, int fmt) +{ + return Formatter::get(fmt)->format(mtx.getMat()); +} + +static inline +int print(Ptr fmtd, FILE* stream = stdout) +{ + int written = 0; + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + written += fputs(str, stream); + + return written; +} + +static inline +int print(const Mat& mtx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(mtx), stream); +} + +static inline +int print(const UMat& mtx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(mtx.getMat(ACCESS_READ)), stream); +} + +template static inline +int print(const std::vector >& vec, FILE* stream = stdout) +{ + return print(Formatter::get()->format(Mat(vec)), stream); +} + +template static inline +int print(const std::vector >& vec, FILE* stream = stdout) +{ + return print(Formatter::get()->format(Mat(vec)), stream); +} + +template static inline +int print(const Matx<_Tp, m, n>& matx, FILE* stream = stdout) +{ + return print(Formatter::get()->format(cv::Mat(matx)), stream); +} + +//! @endcond + +/****************************************************************************************\ +* Auxiliary algorithms * +\****************************************************************************************/ + +/** @brief Splits an element set into equivalency classes. + +The generic function partition implements an \f$O(N^2)\f$ algorithm for splitting a set of \f$N\f$ elements +into one or more equivalency classes, as described in + . The function returns the number of +equivalency classes. +@param _vec Set of elements stored as a vector. +@param labels Output vector of labels. It contains as many elements as vec. Each label labels[i] is +a 0-based cluster index of `vec[i]`. +@param predicate Equivalence predicate (pointer to a boolean function of two arguments or an +instance of the class that has the method bool operator()(const _Tp& a, const _Tp& b) ). The +predicate returns true when the elements are certainly in the same class, and returns false if they +may or may not be in the same class. +@ingroup core_cluster +*/ +template int +partition( const std::vector<_Tp>& _vec, std::vector& labels, + _EqPredicate predicate=_EqPredicate()) +{ + int i, j, N = (int)_vec.size(); + const _Tp* vec = &_vec[0]; + + const int PARENT=0; + const int RANK=1; + + std::vector _nodes(N*2); + int (*nodes)[2] = (int(*)[2])&_nodes[0]; + + // The first O(N) pass: create N single-vertex trees + for(i = 0; i < N; i++) + { + nodes[i][PARENT]=-1; + nodes[i][RANK] = 0; + } + + // The main O(N^2) pass: merge connected components + for( i = 0; i < N; i++ ) + { + int root = i; + + // find root + while( nodes[root][PARENT] >= 0 ) + root = nodes[root][PARENT]; + + for( j = 0; j < N; j++ ) + { + if( i == j || !predicate(vec[i], vec[j])) + continue; + int root2 = j; + + while( nodes[root2][PARENT] >= 0 ) + root2 = nodes[root2][PARENT]; + + if( root2 != root ) + { + // unite both trees + int rank = nodes[root][RANK], rank2 = nodes[root2][RANK]; + if( rank > rank2 ) + nodes[root2][PARENT] = root; + else + { + nodes[root][PARENT] = root2; + nodes[root2][RANK] += rank == rank2; + root = root2; + } + CV_Assert( nodes[root][PARENT] < 0 ); + + int k = j, parent; + + // compress the path from node2 to root + while( (parent = nodes[k][PARENT]) >= 0 ) + { + nodes[k][PARENT] = root; + k = parent; + } + + // compress the path from node to root + k = i; + while( (parent = nodes[k][PARENT]) >= 0 ) + { + nodes[k][PARENT] = root; + k = parent; + } + } + } + } + + // Final O(N) pass: enumerate classes + labels.resize(N); + int nclasses = 0; + + for( i = 0; i < N; i++ ) + { + int root = i; + while( nodes[root][PARENT] >= 0 ) + root = nodes[root][PARENT]; + // re-use the rank as the class label + if( nodes[root][RANK] >= 0 ) + nodes[root][RANK] = ~nclasses++; + labels[i] = ~nodes[root][RANK]; + } + + return nclasses; +} + +} // cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/optim.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/optim.hpp new file mode 100644 index 0000000..5a09400 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/optim.hpp @@ -0,0 +1,302 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 OpenCV Foundation or contributors 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. +// +//M*/ + +#ifndef OPENCV_OPTIM_HPP +#define OPENCV_OPTIM_HPP + +#include "opencv2/core.hpp" + +namespace cv +{ + +/** @addtogroup core_optim +The algorithms in this section minimize or maximize function value within specified constraints or +without any constraints. +@{ +*/ + +/** @brief Basic interface for all solvers + */ +class CV_EXPORTS MinProblemSolver : public Algorithm +{ +public: + /** @brief Represents function being optimized + */ + class CV_EXPORTS Function + { + public: + virtual ~Function() {} + virtual int getDims() const = 0; + virtual double getGradientEps() const; + virtual double calc(const double* x) const = 0; + virtual void getGradient(const double* x,double* grad); + }; + + /** @brief Getter for the optimized function. + + The optimized function is represented by Function interface, which requires derivatives to + implement the calc(double*) and getDim() methods to evaluate the function. + + @return Smart-pointer to an object that implements Function interface - it represents the + function that is being optimized. It can be empty, if no function was given so far. + */ + virtual Ptr getFunction() const = 0; + + /** @brief Setter for the optimized function. + + *It should be called at least once before the call to* minimize(), as default value is not usable. + + @param f The new function to optimize. + */ + virtual void setFunction(const Ptr& f) = 0; + + /** @brief Getter for the previously set terminal criteria for this algorithm. + + @return Deep copy of the terminal criteria used at the moment. + */ + virtual TermCriteria getTermCriteria() const = 0; + + /** @brief Set terminal criteria for solver. + + This method *is not necessary* to be called before the first call to minimize(), as the default + value is sensible. + + Algorithm stops when the number of function evaluations done exceeds termcrit.maxCount, when + the function values at the vertices of simplex are within termcrit.epsilon range or simplex + becomes so small that it can enclosed in a box with termcrit.epsilon sides, whatever comes + first. + @param termcrit Terminal criteria to be used, represented as cv::TermCriteria structure. + */ + virtual void setTermCriteria(const TermCriteria& termcrit) = 0; + + /** @brief actually runs the algorithm and performs the minimization. + + The sole input parameter determines the centroid of the starting simplex (roughly, it tells + where to start), all the others (terminal criteria, initial step, function to be minimized) are + supposed to be set via the setters before the call to this method or the default values (not + always sensible) will be used. + + @param x The initial point, that will become a centroid of an initial simplex. After the algorithm + will terminate, it will be set to the point where the algorithm stops, the point of possible + minimum. + @return The value of a function at the point found. + */ + virtual double minimize(InputOutputArray x) = 0; +}; + +/** @brief This class is used to perform the non-linear non-constrained minimization of a function, + +defined on an `n`-dimensional Euclidean space, using the **Nelder-Mead method**, also known as +**downhill simplex method**. The basic idea about the method can be obtained from +. + +It should be noted, that this method, although deterministic, is rather a heuristic and therefore +may converge to a local minima, not necessary a global one. It is iterative optimization technique, +which at each step uses an information about the values of a function evaluated only at `n+1` +points, arranged as a *simplex* in `n`-dimensional space (hence the second name of the method). At +each step new point is chosen to evaluate function at, obtained value is compared with previous +ones and based on this information simplex changes it's shape , slowly moving to the local minimum. +Thus this method is using *only* function values to make decision, on contrary to, say, Nonlinear +Conjugate Gradient method (which is also implemented in optim). + +Algorithm stops when the number of function evaluations done exceeds termcrit.maxCount, when the +function values at the vertices of simplex are within termcrit.epsilon range or simplex becomes so +small that it can enclosed in a box with termcrit.epsilon sides, whatever comes first, for some +defined by user positive integer termcrit.maxCount and positive non-integer termcrit.epsilon. + +@note DownhillSolver is a derivative of the abstract interface +cv::MinProblemSolver, which in turn is derived from the Algorithm interface and is used to +encapsulate the functionality, common to all non-linear optimization algorithms in the optim +module. + +@note term criteria should meet following condition: +@code + termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) && termcrit.epsilon > 0 && termcrit.maxCount > 0 +@endcode + */ +class CV_EXPORTS DownhillSolver : public MinProblemSolver +{ +public: + /** @brief Returns the initial step that will be used in downhill simplex algorithm. + + @param step Initial step that will be used in algorithm. Note, that although corresponding setter + accepts column-vectors as well as row-vectors, this method will return a row-vector. + @see DownhillSolver::setInitStep + */ + virtual void getInitStep(OutputArray step) const=0; + + /** @brief Sets the initial step that will be used in downhill simplex algorithm. + + Step, together with initial point (given in DownhillSolver::minimize) are two `n`-dimensional + vectors that are used to determine the shape of initial simplex. Roughly said, initial point + determines the position of a simplex (it will become simplex's centroid), while step determines the + spread (size in each dimension) of a simplex. To be more precise, if \f$s,x_0\in\mathbb{R}^n\f$ are + the initial step and initial point respectively, the vertices of a simplex will be: + \f$v_0:=x_0-\frac{1}{2} s\f$ and \f$v_i:=x_0+s_i\f$ for \f$i=1,2,\dots,n\f$ where \f$s_i\f$ denotes + projections of the initial step of *n*-th coordinate (the result of projection is treated to be + vector given by \f$s_i:=e_i\cdot\left\f$, where \f$e_i\f$ form canonical basis) + + @param step Initial step that will be used in algorithm. Roughly said, it determines the spread + (size in each dimension) of an initial simplex. + */ + virtual void setInitStep(InputArray step)=0; + + /** @brief This function returns the reference to the ready-to-use DownhillSolver object. + + All the parameters are optional, so this procedure can be called even without parameters at + all. In this case, the default values will be used. As default value for terminal criteria are + the only sensible ones, MinProblemSolver::setFunction() and DownhillSolver::setInitStep() + should be called upon the obtained object, if the respective parameters were not given to + create(). Otherwise, the two ways (give parameters to createDownhillSolver() or miss them out + and call the MinProblemSolver::setFunction() and DownhillSolver::setInitStep()) are absolutely + equivalent (and will drop the same errors in the same way, should invalid input be detected). + @param f Pointer to the function that will be minimized, similarly to the one you submit via + MinProblemSolver::setFunction. + @param initStep Initial step, that will be used to construct the initial simplex, similarly to the one + you submit via MinProblemSolver::setInitStep. + @param termcrit Terminal criteria to the algorithm, similarly to the one you submit via + MinProblemSolver::setTermCriteria. + */ + static Ptr create(const Ptr& f=Ptr(), + InputArray initStep=Mat_(1,1,0.0), + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001)); +}; + +/** @brief This class is used to perform the non-linear non-constrained minimization of a function +with known gradient, + +defined on an *n*-dimensional Euclidean space, using the **Nonlinear Conjugate Gradient method**. +The implementation was done based on the beautifully clear explanatory article [An Introduction to +the Conjugate Gradient Method Without the Agonizing +Pain](http://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf) by Jonathan Richard +Shewchuk. The method can be seen as an adaptation of a standard Conjugate Gradient method (see, for +example ) for numerically solving the +systems of linear equations. + +It should be noted, that this method, although deterministic, is rather a heuristic method and +therefore may converge to a local minima, not necessary a global one. What is even more disastrous, +most of its behaviour is ruled by gradient, therefore it essentially cannot distinguish between +local minima and maxima. Therefore, if it starts sufficiently near to the local maximum, it may +converge to it. Another obvious restriction is that it should be possible to compute the gradient of +a function at any point, thus it is preferable to have analytic expression for gradient and +computational burden should be born by the user. + +The latter responsibility is accomplished via the getGradient method of a +MinProblemSolver::Function interface (which represents function being optimized). This method takes +point a point in *n*-dimensional space (first argument represents the array of coordinates of that +point) and compute its gradient (it should be stored in the second argument as an array). + +@note class ConjGradSolver thus does not add any new methods to the basic MinProblemSolver interface. + +@note term criteria should meet following condition: +@code + termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) && termcrit.epsilon > 0 && termcrit.maxCount > 0 + // or + termcrit.type == TermCriteria::MAX_ITER) && termcrit.maxCount > 0 +@endcode + */ +class CV_EXPORTS ConjGradSolver : public MinProblemSolver +{ +public: + /** @brief This function returns the reference to the ready-to-use ConjGradSolver object. + + All the parameters are optional, so this procedure can be called even without parameters at + all. In this case, the default values will be used. As default value for terminal criteria are + the only sensible ones, MinProblemSolver::setFunction() should be called upon the obtained + object, if the function was not given to create(). Otherwise, the two ways (submit it to + create() or miss it out and call the MinProblemSolver::setFunction()) are absolutely equivalent + (and will drop the same errors in the same way, should invalid input be detected). + @param f Pointer to the function that will be minimized, similarly to the one you submit via + MinProblemSolver::setFunction. + @param termcrit Terminal criteria to the algorithm, similarly to the one you submit via + MinProblemSolver::setTermCriteria. + */ + static Ptr create(const Ptr& f=Ptr(), + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001)); +}; + +//! return codes for cv::solveLP() function +enum SolveLPResult +{ + SOLVELP_UNBOUNDED = -2, //!< problem is unbounded (target function can achieve arbitrary high values) + SOLVELP_UNFEASIBLE = -1, //!< problem is unfeasible (there are no points that satisfy all the constraints imposed) + SOLVELP_SINGLE = 0, //!< there is only one maximum for target function + SOLVELP_MULTI = 1 //!< there are multiple maxima for target function - the arbitrary one is returned +}; + +/** @brief Solve given (non-integer) linear programming problem using the Simplex Algorithm (Simplex Method). + +What we mean here by "linear programming problem" (or LP problem, for short) can be formulated as: + +\f[\mbox{Maximize } c\cdot x\\ + \mbox{Subject to:}\\ + Ax\leq b\\ + x\geq 0\f] + +Where \f$c\f$ is fixed `1`-by-`n` row-vector, \f$A\f$ is fixed `m`-by-`n` matrix, \f$b\f$ is fixed `m`-by-`1` +column vector and \f$x\f$ is an arbitrary `n`-by-`1` column vector, which satisfies the constraints. + +Simplex algorithm is one of many algorithms that are designed to handle this sort of problems +efficiently. Although it is not optimal in theoretical sense (there exist algorithms that can solve +any problem written as above in polynomial time, while simplex method degenerates to exponential +time for some special cases), it is well-studied, easy to implement and is shown to work well for +real-life purposes. + +The particular implementation is taken almost verbatim from **Introduction to Algorithms, third +edition** by T. H. Cormen, C. E. Leiserson, R. L. Rivest and Clifford Stein. In particular, the +Bland's rule is used to prevent cycling. + +@param Func This row-vector corresponds to \f$c\f$ in the LP problem formulation (see above). It should +contain 32- or 64-bit floating point numbers. As a convenience, column-vector may be also submitted, +in the latter case it is understood to correspond to \f$c^T\f$. +@param Constr `m`-by-`n+1` matrix, whose rightmost column corresponds to \f$b\f$ in formulation above +and the remaining to \f$A\f$. It should contain 32- or 64-bit floating point numbers. +@param z The solution will be returned here as a column-vector - it corresponds to \f$c\f$ in the +formulation above. It will contain 64-bit floating point numbers. +@return One of cv::SolveLPResult + */ +CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z); + +//! @} + +}// cv + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/ovx.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ovx.hpp new file mode 100644 index 0000000..8bb7d54 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ovx.hpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2016, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. + +// OpenVX related definitions and declarations + +#pragma once +#ifndef OPENCV_OVX_HPP +#define OPENCV_OVX_HPP + +#include "cvdef.h" + +namespace cv +{ +/// Check if use of OpenVX is possible +CV_EXPORTS_W bool haveOpenVX(); + +/// Check if use of OpenVX is enabled +CV_EXPORTS_W bool useOpenVX(); + +/// Enable/disable use of OpenVX +CV_EXPORTS_W void setUseOpenVX(bool flag); +} // namespace cv + +#endif // OPENCV_OVX_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/persistence.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/persistence.hpp new file mode 100644 index 0000000..e3026e3 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/persistence.hpp @@ -0,0 +1,1383 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_PERSISTENCE_HPP +#define OPENCV_CORE_PERSISTENCE_HPP + +#ifndef CV_DOXYGEN +/// Define to support persistence legacy formats +#define CV__LEGACY_PERSISTENCE +#endif + +#ifndef __cplusplus +# error persistence.hpp header must be compiled as C++ +#endif + +//! @addtogroup core_c +//! @{ + +/** @brief "black box" representation of the file storage associated with a file on disk. + +Several functions that are described below take CvFileStorage\* as inputs and allow the user to +save or to load hierarchical collections that consist of scalar values, standard CXCore objects +(such as matrices, sequences, graphs), and user-defined objects. + +OpenCV can read and write data in XML (), YAML () or +JSON () formats. Below is an example of 3x3 floating-point identity matrix A, +stored in XML and YAML files +using CXCore functions: +XML: +@code{.xml} + + + + 3 + 3 +
f
+ 1. 0. 0. 0. 1. 0. 0. 0. 1. +
+
+@endcode +YAML: +@code{.yaml} + %YAML:1.0 + A: !!opencv-matrix + rows: 3 + cols: 3 + dt: f + data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1.] +@endcode +As it can be seen from the examples, XML uses nested tags to represent hierarchy, while YAML uses +indentation for that purpose (similar to the Python programming language). + +The same functions can read and write data in both formats; the particular format is determined by +the extension of the opened file, ".xml" for XML files, ".yml" or ".yaml" for YAML and ".json" for +JSON. + */ +typedef struct CvFileStorage CvFileStorage; +typedef struct CvFileNode CvFileNode; +typedef struct CvMat CvMat; +typedef struct CvMatND CvMatND; + +//! @} core_c + +#include "opencv2/core/types.hpp" +#include "opencv2/core/mat.hpp" + +namespace cv { + +/** @addtogroup core_xml + +XML/YAML/JSON file storages. {#xml_storage} +======================= +Writing to a file storage. +-------------------------- +You can store and then restore various OpenCV data structures to/from XML (), +YAML () or JSON () formats. Also, it is possible to store +and load arbitrarily complex data structures, which include OpenCV data structures, as well as +primitive data types (integer and floating-point numbers and text strings) as their elements. + +Use the following procedure to write something to XML, YAML or JSON: +-# Create new FileStorage and open it for writing. It can be done with a single call to +FileStorage::FileStorage constructor that takes a filename, or you can use the default constructor +and then call FileStorage::open. Format of the file (XML, YAML or JSON) is determined from the filename +extension (".xml", ".yml"/".yaml" and ".json", respectively) +-# Write all the data you want using the streaming operator `<<`, just like in the case of STL +streams. +-# Close the file using FileStorage::release. FileStorage destructor also closes the file. + +Here is an example: +@code + #include "opencv2/opencv.hpp" + #include + + using namespace cv; + + int main(int, char** argv) + { + FileStorage fs("test.yml", FileStorage::WRITE); + + fs << "frameCount" << 5; + time_t rawtime; time(&rawtime); + fs << "calibrationDate" << asctime(localtime(&rawtime)); + Mat cameraMatrix = (Mat_(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); + Mat distCoeffs = (Mat_(5,1) << 0.1, 0.01, -0.001, 0, 0); + fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs; + fs << "features" << "["; + for( int i = 0; i < 3; i++ ) + { + int x = rand() % 640; + int y = rand() % 480; + uchar lbp = rand() % 256; + + fs << "{:" << "x" << x << "y" << y << "lbp" << "[:"; + for( int j = 0; j < 8; j++ ) + fs << ((lbp >> j) & 1); + fs << "]" << "}"; + } + fs << "]"; + fs.release(); + return 0; + } +@endcode +The sample above stores to YML an integer, a text string (calibration date), 2 matrices, and a custom +structure "feature", which includes feature coordinates and LBP (local binary pattern) value. Here +is output of the sample: +@code{.yaml} +%YAML:1.0 +frameCount: 5 +calibrationDate: "Fri Jun 17 14:09:29 2011\n" +cameraMatrix: !!opencv-matrix + rows: 3 + cols: 3 + dt: d + data: [ 1000., 0., 320., 0., 1000., 240., 0., 0., 1. ] +distCoeffs: !!opencv-matrix + rows: 5 + cols: 1 + dt: d + data: [ 1.0000000000000001e-01, 1.0000000000000000e-02, + -1.0000000000000000e-03, 0., 0. ] +features: + - { x:167, y:49, lbp:[ 1, 0, 0, 1, 1, 0, 1, 1 ] } + - { x:298, y:130, lbp:[ 0, 0, 0, 1, 0, 0, 1, 1 ] } + - { x:344, y:158, lbp:[ 1, 1, 0, 0, 0, 0, 1, 0 ] } +@endcode + +As an exercise, you can replace ".yml" with ".xml" or ".json" in the sample above and see, how the +corresponding XML file will look like. + +Several things can be noted by looking at the sample code and the output: + +- The produced YAML (and XML/JSON) consists of heterogeneous collections that can be nested. There are + 2 types of collections: named collections (mappings) and unnamed collections (sequences). In mappings + each element has a name and is accessed by name. This is similar to structures and std::map in + C/C++ and dictionaries in Python. In sequences elements do not have names, they are accessed by + indices. This is similar to arrays and std::vector in C/C++ and lists, tuples in Python. + "Heterogeneous" means that elements of each single collection can have different types. + + Top-level collection in YAML/XML/JSON is a mapping. Each matrix is stored as a mapping, and the matrix + elements are stored as a sequence. Then, there is a sequence of features, where each feature is + represented a mapping, and lbp value in a nested sequence. + +- When you write to a mapping (a structure), you write element name followed by its value. When you + write to a sequence, you simply write the elements one by one. OpenCV data structures (such as + cv::Mat) are written in absolutely the same way as simple C data structures - using `<<` + operator. + +- To write a mapping, you first write the special string `{` to the storage, then write the + elements as pairs (`fs << << `) and then write the closing + `}`. + +- To write a sequence, you first write the special string `[`, then write the elements, then + write the closing `]`. + +- In YAML/JSON (but not XML), mappings and sequences can be written in a compact Python-like inline + form. In the sample above matrix elements, as well as each feature, including its lbp value, is + stored in such inline form. To store a mapping/sequence in a compact form, put `:` after the + opening character, e.g. use `{:` instead of `{` and `[:` instead of `[`. When the + data is written to XML, those extra `:` are ignored. + +Reading data from a file storage. +--------------------------------- +To read the previously written XML, YAML or JSON file, do the following: +-# Open the file storage using FileStorage::FileStorage constructor or FileStorage::open method. + In the current implementation the whole file is parsed and the whole representation of file + storage is built in memory as a hierarchy of file nodes (see FileNode) + +-# Read the data you are interested in. Use FileStorage::operator [], FileNode::operator [] + and/or FileNodeIterator. + +-# Close the storage using FileStorage::release. + +Here is how to read the file created by the code sample above: +@code + FileStorage fs2("test.yml", FileStorage::READ); + + // first method: use (type) operator on FileNode. + int frameCount = (int)fs2["frameCount"]; + + String date; + // second method: use FileNode::operator >> + fs2["calibrationDate"] >> date; + + Mat cameraMatrix2, distCoeffs2; + fs2["cameraMatrix"] >> cameraMatrix2; + fs2["distCoeffs"] >> distCoeffs2; + + cout << "frameCount: " << frameCount << endl + << "calibration date: " << date << endl + << "camera matrix: " << cameraMatrix2 << endl + << "distortion coeffs: " << distCoeffs2 << endl; + + FileNode features = fs2["features"]; + FileNodeIterator it = features.begin(), it_end = features.end(); + int idx = 0; + std::vector lbpval; + + // iterate through a sequence using FileNodeIterator + for( ; it != it_end; ++it, idx++ ) + { + cout << "feature #" << idx << ": "; + cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: ("; + // you can also easily read numerical arrays using FileNode >> std::vector operator. + (*it)["lbp"] >> lbpval; + for( int i = 0; i < (int)lbpval.size(); i++ ) + cout << " " << (int)lbpval[i]; + cout << ")" << endl; + } + fs2.release(); +@endcode + +Format specification {#format_spec} +-------------------- +`([count]{u|c|w|s|i|f|d})`... where the characters correspond to fundamental C++ types: +- `u` 8-bit unsigned number +- `c` 8-bit signed number +- `w` 16-bit unsigned number +- `s` 16-bit signed number +- `i` 32-bit signed number +- `f` single precision floating-point number +- `d` double precision floating-point number +- `r` pointer, 32 lower bits of which are written as a signed integer. The type can be used to + store structures with links between the elements. + +`count` is the optional counter of values of a given type. For example, `2if` means that each array +element is a structure of 2 integers, followed by a single-precision floating-point number. The +equivalent notations of the above specification are `iif`, `2i1f` and so forth. Other examples: `u` +means that the array consists of bytes, and `2d` means the array consists of pairs of doubles. + +@see @ref samples/cpp/filestorage.cpp +*/ + +//! @{ + +/** @example samples/cpp/filestorage.cpp +A complete example using the FileStorage interface +*/ + +////////////////////////// XML & YAML I/O ////////////////////////// + +class CV_EXPORTS FileNode; +class CV_EXPORTS FileNodeIterator; + +/** @brief XML/YAML/JSON file storage class that encapsulates all the information necessary for writing or +reading data to/from a file. + */ +class CV_EXPORTS_W FileStorage +{ +public: + //! file storage mode + enum Mode + { + READ = 0, //!< value, open the file for reading + WRITE = 1, //!< value, open the file for writing + APPEND = 2, //!< value, open the file for appending + MEMORY = 4, //!< flag, read data from source or write data to the internal buffer (which is + //!< returned by FileStorage::release) + FORMAT_MASK = (7<<3), //!< mask for format flags + FORMAT_AUTO = 0, //!< flag, auto format + FORMAT_XML = (1<<3), //!< flag, XML format + FORMAT_YAML = (2<<3), //!< flag, YAML format + FORMAT_JSON = (3<<3), //!< flag, JSON format + + BASE64 = 64, //!< flag, write rawdata in Base64 by default. (consider using WRITE_BASE64) + WRITE_BASE64 = BASE64 | WRITE, //!< flag, enable both WRITE and BASE64 + }; + enum + { + UNDEFINED = 0, + VALUE_EXPECTED = 1, + NAME_EXPECTED = 2, + INSIDE_MAP = 4 + }; + + /** @brief The constructors. + + The full constructor opens the file. Alternatively you can use the default constructor and then + call FileStorage::open. + */ + CV_WRAP FileStorage(); + + /** @overload + @copydoc open() + */ + CV_WRAP FileStorage(const String& filename, int flags, const String& encoding=String()); + + /** @overload */ + FileStorage(CvFileStorage* fs, bool owning=true); + + //! the destructor. calls release() + virtual ~FileStorage(); + + /** @brief Opens a file. + + See description of parameters in FileStorage::FileStorage. The method calls FileStorage::release + before opening the file. + @param filename Name of the file to open or the text string to read the data from. + Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON + respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz. If both + FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify + the output file format (e.g. mydata.xml, .yml etc.). A file name can also contain parameters. + You can use this format, "*?base64" (e.g. "file.json?base64" (case sensitive)), as an alternative to + FileStorage::BASE64 flag. + @param flags Mode of operation. One of FileStorage::Mode + @param encoding Encoding of the file. Note that UTF-16 XML encoding is not supported currently and + you should use 8-bit encoding instead of it. + */ + CV_WRAP virtual bool open(const String& filename, int flags, const String& encoding=String()); + + /** @brief Checks whether the file is opened. + + @returns true if the object is associated with the current file and false otherwise. It is a + good practice to call this method after you tried to open a file. + */ + CV_WRAP virtual bool isOpened() const; + + /** @brief Closes the file and releases all the memory buffers. + + Call this method after all I/O operations with the storage are finished. + */ + CV_WRAP virtual void release(); + + /** @brief Closes the file and releases all the memory buffers. + + Call this method after all I/O operations with the storage are finished. If the storage was + opened for writing data and FileStorage::WRITE was specified + */ + CV_WRAP virtual String releaseAndGetString(); + + /** @brief Returns the first element of the top-level mapping. + @returns The first element of the top-level mapping. + */ + CV_WRAP FileNode getFirstTopLevelNode() const; + + /** @brief Returns the top-level mapping + @param streamidx Zero-based index of the stream. In most cases there is only one stream in the file. + However, YAML supports multiple streams and so there can be several. + @returns The top-level mapping. + */ + CV_WRAP FileNode root(int streamidx=0) const; + + /** @brief Returns the specified element of the top-level mapping. + @param nodename Name of the file node. + @returns Node with the given name. + */ + FileNode operator[](const String& nodename) const; + + /** @overload */ + CV_WRAP_AS(getNode) FileNode operator[](const char* nodename) const; + + /** @brief Returns the obsolete C FileStorage structure. + @returns Pointer to the underlying C FileStorage structure + */ + CvFileStorage* operator *() { return fs.get(); } + + /** @overload */ + const CvFileStorage* operator *() const { return fs.get(); } + + /** @brief Writes multiple numbers. + + Writes one or more numbers of the specified format to the currently written structure. Usually it is + more convenient to use operator `<<` instead of this method. + @param fmt Specification of each array element, see @ref format_spec "format specification" + @param vec Pointer to the written array. + @param len Number of the uchar elements to write. + */ + void writeRaw( const String& fmt, const uchar* vec, size_t len ); + + /** @brief Writes the registered C structure (CvMat, CvMatND, CvSeq). + @param name Name of the written object. + @param obj Pointer to the object. + @see cvWrite for details. + */ + void writeObj( const String& name, const void* obj ); + + /** + * @brief Simplified writing API to use with bindings. + * @param name Name of the written object + * @param val Value of the written object + */ + CV_WRAP void write(const String& name, int val); + /// @overload + CV_WRAP void write(const String& name, double val); + /// @overload + CV_WRAP void write(const String& name, const String& val); + /// @overload + CV_WRAP void write(const String& name, InputArray val); + + /** @brief Writes a comment. + + The function writes a comment into file storage. The comments are skipped when the storage is read. + @param comment The written comment, single-line or multi-line + @param append If true, the function tries to put the comment at the end of current line. + Else if the comment is multi-line, or if it does not fit at the end of the current + line, the comment starts a new line. + */ + CV_WRAP void writeComment(const String& comment, bool append = false); + + /** @brief Starts to write a nested structure (sequence or a mapping). + @param name name of the structure (if it's a member of parent mapping, otherwise it should be empty + @param flags type of the structure (FileNode::MAP or FileNode::SEQ (both with optional FileNode::FLOW)). + @param typeName usually an empty string + */ + CV_WRAP void startWriteStruct(const String& name, int flags, const String& typeName=String()); + + /** @brief Finishes writing nested structure (should pair startWriteStruct()) + */ + CV_WRAP void endWriteStruct(); + + /** @brief Returns the normalized object name for the specified name of a file. + @param filename Name of a file + @returns The normalized object name. + */ + static String getDefaultObjectName(const String& filename); + + /** @brief Returns the current format. + * @returns The current format, see FileStorage::Mode + */ + CV_WRAP int getFormat() const; + + Ptr fs; //!< the underlying C FileStorage structure + String elname; //!< the currently written element + std::vector structs; //!< the stack of written structures + int state; //!< the writer state +}; + +template<> CV_EXPORTS void DefaultDeleter::operator ()(CvFileStorage* obj) const; + +/** @brief File Storage Node class. + +The node is used to store each and every element of the file storage opened for reading. When +XML/YAML file is read, it is first parsed and stored in the memory as a hierarchical collection of +nodes. Each node can be a "leaf" that is contain a single number or a string, or be a collection of +other nodes. There can be named collections (mappings) where each element has a name and it is +accessed by a name, and ordered collections (sequences) where elements do not have names but rather +accessed by index. Type of the file node can be determined using FileNode::type method. + +Note that file nodes are only used for navigating file storages opened for reading. When a file +storage is opened for writing, no data is stored in memory after it is written. + */ +class CV_EXPORTS_W_SIMPLE FileNode +{ +public: + //! type of the file storage node + enum Type + { + NONE = 0, //!< empty node + INT = 1, //!< an integer + REAL = 2, //!< floating-point number + FLOAT = REAL, //!< synonym or REAL + STR = 3, //!< text string in UTF-8 encoding + STRING = STR, //!< synonym for STR + REF = 4, //!< integer of size size_t. Typically used for storing complex dynamic structures where some elements reference the others + SEQ = 5, //!< sequence + MAP = 6, //!< mapping + TYPE_MASK = 7, + FLOW = 8, //!< compact representation of a sequence or mapping. Used only by YAML writer + USER = 16, //!< a registered object (e.g. a matrix) + EMPTY = 32, //!< empty structure (sequence or mapping) + NAMED = 64 //!< the node has a name (i.e. it is element of a mapping) + }; + /** @brief The constructors. + + These constructors are used to create a default file node, construct it from obsolete structures or + from the another file node. + */ + CV_WRAP FileNode(); + + /** @overload + @param fs Pointer to the obsolete file storage structure. + @param node File node to be used as initialization for the created file node. + */ + FileNode(const CvFileStorage* fs, const CvFileNode* node); + + /** @overload + @param node File node to be used as initialization for the created file node. + */ + FileNode(const FileNode& node); + + FileNode& operator=(const FileNode& node); + + /** @brief Returns element of a mapping node or a sequence node. + @param nodename Name of an element in the mapping node. + @returns Returns the element with the given identifier. + */ + FileNode operator[](const String& nodename) const; + + /** @overload + @param nodename Name of an element in the mapping node. + */ + CV_WRAP_AS(getNode) FileNode operator[](const char* nodename) const; + + /** @overload + @param i Index of an element in the sequence node. + */ + CV_WRAP_AS(at) FileNode operator[](int i) const; + + /** @brief Returns keys of a mapping node. + @returns Keys of a mapping node. + */ + CV_WRAP std::vector keys() const; + + /** @brief Returns type of the node. + @returns Type of the node. See FileNode::Type + */ + CV_WRAP int type() const; + + //! returns true if the node is empty + CV_WRAP bool empty() const; + //! returns true if the node is a "none" object + CV_WRAP bool isNone() const; + //! returns true if the node is a sequence + CV_WRAP bool isSeq() const; + //! returns true if the node is a mapping + CV_WRAP bool isMap() const; + //! returns true if the node is an integer + CV_WRAP bool isInt() const; + //! returns true if the node is a floating-point number + CV_WRAP bool isReal() const; + //! returns true if the node is a text string + CV_WRAP bool isString() const; + //! returns true if the node has a name + CV_WRAP bool isNamed() const; + //! returns the node name or an empty string if the node is nameless + CV_WRAP String name() const; + //! returns the number of elements in the node, if it is a sequence or mapping, or 1 otherwise. + CV_WRAP size_t size() const; + //! returns the node content as an integer. If the node stores floating-point number, it is rounded. + operator int() const; + //! returns the node content as float + operator float() const; + //! returns the node content as double + operator double() const; + //! returns the node content as text string + operator String() const; + operator std::string() const; + + //! returns pointer to the underlying file node + CvFileNode* operator *(); + //! returns pointer to the underlying file node + const CvFileNode* operator* () const; + + //! returns iterator pointing to the first node element + FileNodeIterator begin() const; + //! returns iterator pointing to the element following the last node element + FileNodeIterator end() const; + + /** @brief Reads node elements to the buffer with the specified format. + + Usually it is more convenient to use operator `>>` instead of this method. + @param fmt Specification of each array element. See @ref format_spec "format specification" + @param vec Pointer to the destination array. + @param len Number of bytes to read (buffer size limit). If it is greater than number of + remaining elements then all of them will be read. + */ + void readRaw( const String& fmt, uchar* vec, size_t len ) const; + + //! reads the registered object and returns pointer to it + void* readObj() const; + + //! Simplified reading API to use with bindings. + CV_WRAP double real() const; + //! Simplified reading API to use with bindings. + CV_WRAP String string() const; + //! Simplified reading API to use with bindings. + CV_WRAP Mat mat() const; + + // do not use wrapper pointer classes for better efficiency + const CvFileStorage* fs; + const CvFileNode* node; +}; + + +/** @brief used to iterate through sequences and mappings. + +A standard STL notation, with node.begin(), node.end() denoting the beginning and the end of a +sequence, stored in node. See the data reading sample in the beginning of the section. + */ +class CV_EXPORTS FileNodeIterator +{ +public: + /** @brief The constructors. + + These constructors are used to create a default iterator, set it to specific element in a file node + or construct it from another iterator. + */ + FileNodeIterator(); + + /** @overload + @param fs File storage for the iterator. + @param node File node for the iterator. + @param ofs Index of the element in the node. The created iterator will point to this element. + */ + FileNodeIterator(const CvFileStorage* fs, const CvFileNode* node, size_t ofs=0); + + /** @overload + @param it Iterator to be used as initialization for the created iterator. + */ + FileNodeIterator(const FileNodeIterator& it); + + FileNodeIterator& operator=(const FileNodeIterator& it); + + //! returns the currently observed element + FileNode operator *() const; + //! accesses the currently observed element methods + FileNode operator ->() const; + + //! moves iterator to the next node + FileNodeIterator& operator ++ (); + //! moves iterator to the next node + FileNodeIterator operator ++ (int); + //! moves iterator to the previous node + FileNodeIterator& operator -- (); + //! moves iterator to the previous node + FileNodeIterator operator -- (int); + //! moves iterator forward by the specified offset (possibly negative) + FileNodeIterator& operator += (int ofs); + //! moves iterator backward by the specified offset (possibly negative) + FileNodeIterator& operator -= (int ofs); + + /** @brief Reads node elements to the buffer with the specified format. + + Usually it is more convenient to use operator `>>` instead of this method. + @param fmt Specification of each array element. See @ref format_spec "format specification" + @param vec Pointer to the destination array. + @param len Number of bytes to read (buffer size limit). If it is greater than number of + remaining elements then all of them will be read. + + */ + FileNodeIterator& readRaw( const String& fmt, uchar* vec, + size_t len=(size_t)INT_MAX ); + + struct SeqReader + { + int header_size; + void* seq; /* sequence, beign read; CvSeq */ + void* block; /* current block; CvSeqBlock */ + schar* ptr; /* pointer to element be read next */ + schar* block_min; /* pointer to the beginning of block */ + schar* block_max; /* pointer to the end of block */ + int delta_index;/* = seq->first->start_index */ + schar* prev_elem; /* pointer to previous element */ + }; + + const CvFileStorage* fs; + const CvFileNode* container; + SeqReader reader; + size_t remaining; +}; + +//! @} core_xml + +/////////////////// XML & YAML I/O implementation ////////////////// + +//! @relates cv::FileStorage +//! @{ + +CV_EXPORTS void write( FileStorage& fs, const String& name, int value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, float value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, double value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const String& value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const Mat& value ); +CV_EXPORTS void write( FileStorage& fs, const String& name, const SparseMat& value ); +#ifdef CV__LEGACY_PERSISTENCE +CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector& value); +CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector& value); +#endif + +CV_EXPORTS void writeScalar( FileStorage& fs, int value ); +CV_EXPORTS void writeScalar( FileStorage& fs, float value ); +CV_EXPORTS void writeScalar( FileStorage& fs, double value ); +CV_EXPORTS void writeScalar( FileStorage& fs, const String& value ); + +//! @} + +//! @relates cv::FileNode +//! @{ + +CV_EXPORTS void read(const FileNode& node, int& value, int default_value); +CV_EXPORTS void read(const FileNode& node, float& value, float default_value); +CV_EXPORTS void read(const FileNode& node, double& value, double default_value); +CV_EXPORTS void read(const FileNode& node, String& value, const String& default_value); +CV_EXPORTS void read(const FileNode& node, std::string& value, const std::string& default_value); +CV_EXPORTS void read(const FileNode& node, Mat& mat, const Mat& default_mat = Mat() ); +CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat = SparseMat() ); +#ifdef CV__LEGACY_PERSISTENCE +CV_EXPORTS void read(const FileNode& node, std::vector& keypoints); +CV_EXPORTS void read(const FileNode& node, std::vector& matches); +#endif +CV_EXPORTS void read(const FileNode& node, KeyPoint& value, const KeyPoint& default_value); +CV_EXPORTS void read(const FileNode& node, DMatch& value, const DMatch& default_value); + +template static inline void read(const FileNode& node, Point_<_Tp>& value, const Point_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Point_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Point3_<_Tp>& value, const Point3_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 3 ? default_value : Point3_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2])); +} + +template static inline void read(const FileNode& node, Size_<_Tp>& value, const Size_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Size_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Complex<_Tp>& value, const Complex<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 2 ? default_value : Complex<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1])); +} + +template static inline void read(const FileNode& node, Rect_<_Tp>& value, const Rect_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 4 ? default_value : Rect_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2]), saturate_cast<_Tp>(temp[3])); +} + +template static inline void read(const FileNode& node, Vec<_Tp, cn>& value, const Vec<_Tp, cn>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != cn ? default_value : Vec<_Tp, cn>(&temp[0]); +} + +template static inline void read(const FileNode& node, Scalar_<_Tp>& value, const Scalar_<_Tp>& default_value) +{ + std::vector<_Tp> temp; FileNodeIterator it = node.begin(); it >> temp; + value = temp.size() != 4 ? default_value : Scalar_<_Tp>(saturate_cast<_Tp>(temp[0]), saturate_cast<_Tp>(temp[1]), + saturate_cast<_Tp>(temp[2]), saturate_cast<_Tp>(temp[3])); +} + +static inline void read(const FileNode& node, Range& value, const Range& default_value) +{ + Point2i temp(value.start, value.end); const Point2i default_temp = Point2i(default_value.start, default_value.end); + read(node, temp, default_temp); + value.start = temp.x; value.end = temp.y; +} + +//! @} + +/** @brief Writes string to a file storage. +@relates cv::FileStorage + */ +CV_EXPORTS FileStorage& operator << (FileStorage& fs, const String& str); + +//! @cond IGNORED + +namespace internal +{ + class CV_EXPORTS WriteStructContext + { + public: + WriteStructContext(FileStorage& _fs, const String& name, int flags, const String& typeName = String()); + ~WriteStructContext(); + private: + FileStorage* fs; + }; + + template class VecWriterProxy + { + public: + VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} + void operator()(const std::vector<_Tp>& vec) const + { + size_t count = vec.size(); + for (size_t i = 0; i < count; i++) + write(*fs, vec[i]); + } + private: + FileStorage* fs; + }; + + template class VecWriterProxy<_Tp, 1> + { + public: + VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} + void operator()(const std::vector<_Tp>& vec) const + { + int _fmt = traits::SafeFmt<_Tp>::fmt; + char fmt[] = { (char)((_fmt >> 8) + '1'), (char)_fmt, '\0' }; + fs->writeRaw(fmt, !vec.empty() ? (uchar*)&vec[0] : 0, vec.size() * sizeof(_Tp)); + } + private: + FileStorage* fs; + }; + + template class VecReaderProxy + { + public: + VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} + void operator()(std::vector<_Tp>& vec, size_t count) const + { + count = std::min(count, it->remaining); + vec.resize(count); + for (size_t i = 0; i < count; i++, ++(*it)) + read(**it, vec[i], _Tp()); + } + private: + FileNodeIterator* it; + }; + + template class VecReaderProxy<_Tp, 1> + { + public: + VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} + void operator()(std::vector<_Tp>& vec, size_t count) const + { + size_t remaining = it->remaining; + size_t cn = DataType<_Tp>::channels; + int _fmt = traits::SafeFmt<_Tp>::fmt; + CV_Assert((_fmt >> 8) < 9); + char fmt[] = { (char)((_fmt >> 8)+'1'), (char)_fmt, '\0' }; + CV_Assert((remaining % cn) == 0); + size_t remaining1 = remaining / cn; + count = count < remaining1 ? count : remaining1; + vec.resize(count); + it->readRaw(fmt, !vec.empty() ? (uchar*)&vec[0] : 0, count*sizeof(_Tp)); + } + private: + FileNodeIterator* it; + }; + +} // internal + +//! @endcond + +//! @relates cv::FileStorage +//! @{ + +template static inline +void write(FileStorage& fs, const _Tp& value) +{ + write(fs, String(), value); +} + +template<> inline +void write( FileStorage& fs, const int& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const float& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const double& value ) +{ + writeScalar(fs, value); +} + +template<> inline +void write( FileStorage& fs, const String& value ) +{ + writeScalar(fs, value); +} + +template static inline +void write(FileStorage& fs, const Point_<_Tp>& pt ) +{ + write(fs, pt.x); + write(fs, pt.y); +} + +template static inline +void write(FileStorage& fs, const Point3_<_Tp>& pt ) +{ + write(fs, pt.x); + write(fs, pt.y); + write(fs, pt.z); +} + +template static inline +void write(FileStorage& fs, const Size_<_Tp>& sz ) +{ + write(fs, sz.width); + write(fs, sz.height); +} + +template static inline +void write(FileStorage& fs, const Complex<_Tp>& c ) +{ + write(fs, c.re); + write(fs, c.im); +} + +template static inline +void write(FileStorage& fs, const Rect_<_Tp>& r ) +{ + write(fs, r.x); + write(fs, r.y); + write(fs, r.width); + write(fs, r.height); +} + +template static inline +void write(FileStorage& fs, const Vec<_Tp, cn>& v ) +{ + for(int i = 0; i < cn; i++) + write(fs, v.val[i]); +} + +template static inline +void write(FileStorage& fs, const Scalar_<_Tp>& s ) +{ + write(fs, s.val[0]); + write(fs, s.val[1]); + write(fs, s.val[2]); + write(fs, s.val[3]); +} + +static inline +void write(FileStorage& fs, const Range& r ) +{ + write(fs, r.start); + write(fs, r.end); +} + +template static inline +void write( FileStorage& fs, const std::vector<_Tp>& vec ) +{ + cv::internal::VecWriterProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> w(&fs); + w(vec); +} + +template static inline +void write(FileStorage& fs, const String& name, const Point_<_Tp>& pt ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, pt); +} + +template static inline +void write(FileStorage& fs, const String& name, const Point3_<_Tp>& pt ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, pt); +} + +template static inline +void write(FileStorage& fs, const String& name, const Size_<_Tp>& sz ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, sz); +} + +template static inline +void write(FileStorage& fs, const String& name, const Complex<_Tp>& c ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, c); +} + +template static inline +void write(FileStorage& fs, const String& name, const Rect_<_Tp>& r ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, r); +} + +template static inline +void write(FileStorage& fs, const String& name, const Vec<_Tp, cn>& v ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, v); +} + +template static inline +void write(FileStorage& fs, const String& name, const Scalar_<_Tp>& s ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, s); +} + +static inline +void write(FileStorage& fs, const String& name, const Range& r ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, r); +} + +static inline +void write(FileStorage& fs, const String& name, const KeyPoint& kpt) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, kpt.pt.x); + write(fs, kpt.pt.y); + write(fs, kpt.size); + write(fs, kpt.angle); + write(fs, kpt.response); + write(fs, kpt.octave); + write(fs, kpt.class_id); +} + +static inline +void write(FileStorage& fs, const String& name, const DMatch& m) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+FileNode::FLOW); + write(fs, m.queryIdx); + write(fs, m.trainIdx); + write(fs, m.imgIdx); + write(fs, m.distance); +} + +template static inline +void write( FileStorage& fs, const String& name, const std::vector<_Tp>& vec ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ+(traits::SafeFmt<_Tp>::fmt != 0 ? FileNode::FLOW : 0)); + write(fs, vec); +} + +template static inline +void write( FileStorage& fs, const String& name, const std::vector< std::vector<_Tp> >& vec ) +{ + cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ); + for(size_t i = 0; i < vec.size(); i++) + { + cv::internal::WriteStructContext ws_(fs, name, FileNode::SEQ+(traits::SafeFmt<_Tp>::fmt != 0 ? FileNode::FLOW : 0)); + write(fs, vec[i]); + } +} + +#ifdef CV__LEGACY_PERSISTENCE +// This code is not needed anymore, but it is preserved here to keep source compatibility +// Implementation is similar to templates instantiations +static inline void write(FileStorage& fs, const KeyPoint& kpt) { write(fs, String(), kpt); } +static inline void write(FileStorage& fs, const DMatch& m) { write(fs, String(), m); } +static inline void write(FileStorage& fs, const std::vector& vec) +{ + cv::internal::VecWriterProxy w(&fs); + w(vec); +} +static inline void write(FileStorage& fs, const std::vector& vec) +{ + cv::internal::VecWriterProxy w(&fs); + w(vec); + +} +#endif + +//! @} FileStorage + +//! @relates cv::FileNode +//! @{ + +static inline +void read(const FileNode& node, bool& value, bool default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = temp != 0; +} + +static inline +void read(const FileNode& node, uchar& value, uchar default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, schar& value, schar default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, ushort& value, ushort default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +static inline +void read(const FileNode& node, short& value, short default_value) +{ + int temp; + read(node, temp, (int)default_value); + value = saturate_cast(temp); +} + +template static inline +void read( FileNodeIterator& it, std::vector<_Tp>& vec, size_t maxCount = (size_t)INT_MAX ) +{ + cv::internal::VecReaderProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> r(&it); + r(vec, maxCount); +} + +template static inline +void read( const FileNode& node, std::vector<_Tp>& vec, const std::vector<_Tp>& default_value = std::vector<_Tp>() ) +{ + if(!node.node) + vec = default_value; + else + { + FileNodeIterator it = node.begin(); + read( it, vec ); + } +} + +static inline +void read( const FileNode& node, std::vector& vec, const std::vector& default_value ) +{ + if(!node.node) + vec = default_value; + else + read(node, vec); +} + +static inline +void read( const FileNode& node, std::vector& vec, const std::vector& default_value ) +{ + if(!node.node) + vec = default_value; + else + read(node, vec); +} + +//! @} FileNode + +//! @relates cv::FileStorage +//! @{ + +/** @brief Writes data to a file storage. + */ +template static inline +FileStorage& operator << (FileStorage& fs, const _Tp& value) +{ + if( !fs.isOpened() ) + return fs; + if( fs.state == FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP ) + CV_Error( Error::StsError, "No element name has been given" ); + write( fs, fs.elname, value ); + if( fs.state & FileStorage::INSIDE_MAP ) + fs.state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; + return fs; +} + +/** @brief Writes data to a file storage. + */ +static inline +FileStorage& operator << (FileStorage& fs, const char* str) +{ + return (fs << String(str)); +} + +/** @brief Writes data to a file storage. + */ +static inline +FileStorage& operator << (FileStorage& fs, char* value) +{ + return (fs << String(value)); +} + +//! @} FileStorage + +//! @relates cv::FileNodeIterator +//! @{ + +/** @brief Reads data from a file storage. + */ +template static inline +FileNodeIterator& operator >> (FileNodeIterator& it, _Tp& value) +{ + read( *it, value, _Tp()); + return ++it; +} + +/** @brief Reads data from a file storage. + */ +template static inline +FileNodeIterator& operator >> (FileNodeIterator& it, std::vector<_Tp>& vec) +{ + cv::internal::VecReaderProxy<_Tp, traits::SafeFmt<_Tp>::fmt != 0> r(&it); + r(vec, (size_t)INT_MAX); + return it; +} + +//! @} FileNodeIterator + +//! @relates cv::FileNode +//! @{ + +/** @brief Reads data from a file storage. + */ +template static inline +void operator >> (const FileNode& n, _Tp& value) +{ + read( n, value, _Tp()); +} + +/** @brief Reads data from a file storage. + */ +template static inline +void operator >> (const FileNode& n, std::vector<_Tp>& vec) +{ + FileNodeIterator it = n.begin(); + it >> vec; +} + +/** @brief Reads KeyPoint from a file storage. +*/ +//It needs special handling because it contains two types of fields, int & float. +static inline +void operator >> (const FileNode& n, KeyPoint& kpt) +{ + FileNodeIterator it = n.begin(); + it >> kpt.pt.x >> kpt.pt.y >> kpt.size >> kpt.angle >> kpt.response >> kpt.octave >> kpt.class_id; +} + +#ifdef CV__LEGACY_PERSISTENCE +static inline +void operator >> (const FileNode& n, std::vector& vec) +{ + read(n, vec); +} +static inline +void operator >> (const FileNode& n, std::vector& vec) +{ + read(n, vec); +} +#endif + +/** @brief Reads DMatch from a file storage. +*/ +//It needs special handling because it contains two types of fields, int & float. +static inline +void operator >> (const FileNode& n, DMatch& m) +{ + FileNodeIterator it = n.begin(); + it >> m.queryIdx >> m.trainIdx >> m.imgIdx >> m.distance; +} + +//! @} FileNode + +//! @relates cv::FileNodeIterator +//! @{ + +static inline +bool operator == (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it1.fs == it2.fs && it1.container == it2.container && + it1.reader.ptr == it2.reader.ptr && it1.remaining == it2.remaining; +} + +static inline +bool operator != (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return !(it1 == it2); +} + +static inline +ptrdiff_t operator - (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it2.remaining - it1.remaining; +} + +static inline +bool operator < (const FileNodeIterator& it1, const FileNodeIterator& it2) +{ + return it1.remaining > it2.remaining; +} + +//! @} FileNodeIterator + +//! @cond IGNORED + +inline FileNode FileStorage::getFirstTopLevelNode() const { FileNode r = root(); FileNodeIterator it = r.begin(); return it != r.end() ? *it : FileNode(); } +inline FileNode::FileNode() : fs(0), node(0) {} +inline FileNode::FileNode(const CvFileStorage* _fs, const CvFileNode* _node) : fs(_fs), node(_node) {} +inline FileNode::FileNode(const FileNode& _node) : fs(_node.fs), node(_node.node) {} +inline FileNode& FileNode::operator=(const FileNode& _node) { fs = _node.fs; node = _node.node; return *this; } +inline bool FileNode::empty() const { return node == 0; } +inline bool FileNode::isNone() const { return type() == NONE; } +inline bool FileNode::isSeq() const { return type() == SEQ; } +inline bool FileNode::isMap() const { return type() == MAP; } +inline bool FileNode::isInt() const { return type() == INT; } +inline bool FileNode::isReal() const { return type() == REAL; } +inline bool FileNode::isString() const { return type() == STR; } +inline CvFileNode* FileNode::operator *() { return (CvFileNode*)node; } +inline const CvFileNode* FileNode::operator* () const { return node; } +inline FileNode::operator int() const { int value; read(*this, value, 0); return value; } +inline FileNode::operator float() const { float value; read(*this, value, 0.f); return value; } +inline FileNode::operator double() const { double value; read(*this, value, 0.); return value; } +inline FileNode::operator String() const { String value; read(*this, value, value); return value; } +inline double FileNode::real() const { return double(*this); } +inline String FileNode::string() const { return String(*this); } +inline Mat FileNode::mat() const { Mat value; read(*this, value, value); return value; } +inline FileNodeIterator FileNode::begin() const { return FileNodeIterator(fs, node); } +inline FileNodeIterator FileNode::end() const { return FileNodeIterator(fs, node, size()); } +inline void FileNode::readRaw( const String& fmt, uchar* vec, size_t len ) const { begin().readRaw( fmt, vec, len ); } +inline FileNode FileNodeIterator::operator *() const { return FileNode(fs, (const CvFileNode*)(const void*)reader.ptr); } +inline FileNode FileNodeIterator::operator ->() const { return FileNode(fs, (const CvFileNode*)(const void*)reader.ptr); } +inline String::String(const FileNode& fn): cstr_(0), len_(0) { read(fn, *this, *this); } + +//! @endcond + + +CV_EXPORTS void cvStartWriteRawData_Base64(::CvFileStorage * fs, const char* name, int len, const char* dt); + +CV_EXPORTS void cvWriteRawData_Base64(::CvFileStorage * fs, const void* _data, int len); + +CV_EXPORTS void cvEndWriteRawData_Base64(::CvFileStorage * fs); + +CV_EXPORTS void cvWriteMat_Base64(::CvFileStorage* fs, const char* name, const ::CvMat* mat); + +CV_EXPORTS void cvWriteMatND_Base64(::CvFileStorage* fs, const char* name, const ::CvMatND* mat); + +} // cv + +#endif // OPENCV_CORE_PERSISTENCE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/ptr.inl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ptr.inl.hpp new file mode 100644 index 0000000..466f634 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/ptr.inl.hpp @@ -0,0 +1,379 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, NVIDIA Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 copyright holders or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_PTR_INL_HPP +#define OPENCV_CORE_PTR_INL_HPP + +#include + +//! @cond IGNORED + +namespace cv { + +template +void DefaultDeleter::operator () (Y* p) const +{ + delete p; +} + +namespace detail +{ + +struct PtrOwner +{ + PtrOwner() : refCount(1) + {} + + void incRef() + { + CV_XADD(&refCount, 1); + } + + void decRef() + { + if (CV_XADD(&refCount, -1) == 1) deleteSelf(); + } + +protected: + /* This doesn't really need to be virtual, since PtrOwner is never deleted + directly, but it doesn't hurt and it helps avoid warnings. */ + virtual ~PtrOwner() + {} + + virtual void deleteSelf() = 0; + +private: + unsigned int refCount; + + // noncopyable + PtrOwner(const PtrOwner&); + PtrOwner& operator = (const PtrOwner&); +}; + +template +struct PtrOwnerImpl CV_FINAL : PtrOwner +{ + PtrOwnerImpl(Y* p, D d) : owned(p), deleter(d) + {} + + void deleteSelf() CV_OVERRIDE + { + deleter(owned); + delete this; + } + +private: + Y* owned; + D deleter; +}; + + +} + +template +Ptr::Ptr() : owner(NULL), stored(NULL) +{} + +template +template +Ptr::Ptr(Y* p) + : owner(p + ? new detail::PtrOwnerImpl >(p, DefaultDeleter()) + : NULL), + stored(p) +{} + +template +template +Ptr::Ptr(Y* p, D d) + : owner(p + ? new detail::PtrOwnerImpl(p, d) + : NULL), + stored(p) +{} + +template +Ptr::Ptr(const Ptr& o) : owner(o.owner), stored(o.stored) +{ + if (owner) owner->incRef(); +} + +template +template +Ptr::Ptr(const Ptr& o) : owner(o.owner), stored(o.stored) +{ + if (owner) owner->incRef(); +} + +template +template +Ptr::Ptr(const Ptr& o, T* p) : owner(o.owner), stored(p) +{ + if (owner) owner->incRef(); +} + +template +Ptr::~Ptr() +{ + release(); +} + +template +Ptr& Ptr::operator = (const Ptr& o) +{ + Ptr(o).swap(*this); + return *this; +} + +template +template +Ptr& Ptr::operator = (const Ptr& o) +{ + Ptr(o).swap(*this); + return *this; +} + +template +void Ptr::release() +{ + if (owner) owner->decRef(); + owner = NULL; + stored = NULL; +} + +template +template +void Ptr::reset(Y* p) +{ + Ptr(p).swap(*this); +} + +template +template +void Ptr::reset(Y* p, D d) +{ + Ptr(p, d).swap(*this); +} + +template +void Ptr::swap(Ptr& o) +{ + std::swap(owner, o.owner); + std::swap(stored, o.stored); +} + +template +T* Ptr::get() const +{ + return stored; +} + +template +typename detail::RefOrVoid::type Ptr::operator * () const +{ + return *stored; +} + +template +T* Ptr::operator -> () const +{ + return stored; +} + +template +Ptr::operator T* () const +{ + return stored; +} + + +template +bool Ptr::empty() const +{ + return !stored; +} + +template +template +Ptr Ptr::staticCast() const +{ + return Ptr(*this, static_cast(stored)); +} + +template +template +Ptr Ptr::constCast() const +{ + return Ptr(*this, const_cast(stored)); +} + +template +template +Ptr Ptr::dynamicCast() const +{ + return Ptr(*this, dynamic_cast(stored)); +} + +#ifdef CV_CXX_MOVE_SEMANTICS + +template +Ptr::Ptr(Ptr&& o) : owner(o.owner), stored(o.stored) +{ + o.owner = NULL; + o.stored = NULL; +} + +template +Ptr& Ptr::operator = (Ptr&& o) +{ + if (this == &o) + return *this; + + release(); + owner = o.owner; + stored = o.stored; + o.owner = NULL; + o.stored = NULL; + return *this; +} + +#endif + + +template +void swap(Ptr& ptr1, Ptr& ptr2){ + ptr1.swap(ptr2); +} + +template +bool operator == (const Ptr& ptr1, const Ptr& ptr2) +{ + return ptr1.get() == ptr2.get(); +} + +template +bool operator != (const Ptr& ptr1, const Ptr& ptr2) +{ + return ptr1.get() != ptr2.get(); +} + +template +Ptr makePtr() +{ + return Ptr(new T()); +} + +template +Ptr makePtr(const A1& a1) +{ + return Ptr(new T(a1)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2) +{ + return Ptr(new T(a1, a2)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3) +{ + return Ptr(new T(a1, a2, a3)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4) +{ + return Ptr(new T(a1, a2, a3, a4)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) +{ + return Ptr(new T(a1, a2, a3, a4, a5)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10, const A11& a11) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); +} + +template +Ptr makePtr(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10, const A11& a11, const A12& a12) +{ + return Ptr(new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); +} +} // namespace cv + +//! @endcond + +#endif // OPENCV_CORE_PTR_INL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/saturate.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/saturate.hpp new file mode 100644 index 0000000..36d3121 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/saturate.hpp @@ -0,0 +1,163 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2014, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_SATURATE_HPP +#define OPENCV_CORE_SATURATE_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/fast_math.hpp" + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +/////////////// saturate_cast (used in image & signal processing) /////////////////// + +/** @brief Template function for accurate conversion from one primitive type to another. + + The function saturate_cast resembles the standard C++ cast operations, such as static_cast\() + and others. It perform an efficient and accurate conversion from one primitive type to another + (see the introduction chapter). saturate in the name means that when the input value v is out of the + range of the target type, the result is not formed just by taking low bits of the input, but instead + the value is clipped. For example: + @code + uchar a = saturate_cast(-100); // a = 0 (UCHAR_MIN) + short b = saturate_cast(33333.33333); // b = 32767 (SHRT_MAX) + @endcode + Such clipping is done when the target type is unsigned char , signed char , unsigned short or + signed short . For 32-bit integers, no clipping is done. + + When the parameter is a floating-point value and the target type is an integer (8-, 16- or 32-bit), + the floating-point value is first rounded to the nearest integer and then clipped if needed (when + the target type is 8- or 16-bit). + + @param v Function parameter. + @sa add, subtract, multiply, divide, Mat::convertTo + */ +template static inline _Tp saturate_cast(uchar v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(schar v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(ushort v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(short v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(unsigned v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(int v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(float v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(double v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(int64 v) { return _Tp(v); } +/** @overload */ +template static inline _Tp saturate_cast(uint64 v) { return _Tp(v); } + +template<> inline uchar saturate_cast(schar v) { return (uchar)std::max((int)v, 0); } +template<> inline uchar saturate_cast(ushort v) { return (uchar)std::min((unsigned)v, (unsigned)UCHAR_MAX); } +template<> inline uchar saturate_cast(int v) { return (uchar)((unsigned)v <= UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); } +template<> inline uchar saturate_cast(short v) { return saturate_cast((int)v); } +template<> inline uchar saturate_cast(unsigned v) { return (uchar)std::min(v, (unsigned)UCHAR_MAX); } +template<> inline uchar saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline uchar saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline uchar saturate_cast(int64 v) { return (uchar)((uint64)v <= (uint64)UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); } +template<> inline uchar saturate_cast(uint64 v) { return (uchar)std::min(v, (uint64)UCHAR_MAX); } + +template<> inline schar saturate_cast(uchar v) { return (schar)std::min((int)v, SCHAR_MAX); } +template<> inline schar saturate_cast(ushort v) { return (schar)std::min((unsigned)v, (unsigned)SCHAR_MAX); } +template<> inline schar saturate_cast(int v) { return (schar)((unsigned)(v-SCHAR_MIN) <= (unsigned)UCHAR_MAX ? v : v > 0 ? SCHAR_MAX : SCHAR_MIN); } +template<> inline schar saturate_cast(short v) { return saturate_cast((int)v); } +template<> inline schar saturate_cast(unsigned v) { return (schar)std::min(v, (unsigned)SCHAR_MAX); } +template<> inline schar saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline schar saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline schar saturate_cast(int64 v) { return (schar)((uint64)((int64)v-SCHAR_MIN) <= (uint64)UCHAR_MAX ? v : v > 0 ? SCHAR_MAX : SCHAR_MIN); } +template<> inline schar saturate_cast(uint64 v) { return (schar)std::min(v, (uint64)SCHAR_MAX); } + +template<> inline ushort saturate_cast(schar v) { return (ushort)std::max((int)v, 0); } +template<> inline ushort saturate_cast(short v) { return (ushort)std::max((int)v, 0); } +template<> inline ushort saturate_cast(int v) { return (ushort)((unsigned)v <= (unsigned)USHRT_MAX ? v : v > 0 ? USHRT_MAX : 0); } +template<> inline ushort saturate_cast(unsigned v) { return (ushort)std::min(v, (unsigned)USHRT_MAX); } +template<> inline ushort saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline ushort saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline ushort saturate_cast(int64 v) { return (ushort)((uint64)v <= (uint64)USHRT_MAX ? v : v > 0 ? USHRT_MAX : 0); } +template<> inline ushort saturate_cast(uint64 v) { return (ushort)std::min(v, (uint64)USHRT_MAX); } + +template<> inline short saturate_cast(ushort v) { return (short)std::min((int)v, SHRT_MAX); } +template<> inline short saturate_cast(int v) { return (short)((unsigned)(v - SHRT_MIN) <= (unsigned)USHRT_MAX ? v : v > 0 ? SHRT_MAX : SHRT_MIN); } +template<> inline short saturate_cast(unsigned v) { return (short)std::min(v, (unsigned)SHRT_MAX); } +template<> inline short saturate_cast(float v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline short saturate_cast(double v) { int iv = cvRound(v); return saturate_cast(iv); } +template<> inline short saturate_cast(int64 v) { return (short)((uint64)((int64)v - SHRT_MIN) <= (uint64)USHRT_MAX ? v : v > 0 ? SHRT_MAX : SHRT_MIN); } +template<> inline short saturate_cast(uint64 v) { return (short)std::min(v, (uint64)SHRT_MAX); } + +template<> inline int saturate_cast(unsigned v) { return (int)std::min(v, (unsigned)INT_MAX); } +template<> inline int saturate_cast(int64 v) { return (int)((uint64)(v - INT_MIN) <= (uint64)UINT_MAX ? v : v > 0 ? INT_MAX : INT_MIN); } +template<> inline int saturate_cast(uint64 v) { return (int)std::min(v, (uint64)INT_MAX); } +template<> inline int saturate_cast(float v) { return cvRound(v); } +template<> inline int saturate_cast(double v) { return cvRound(v); } + +template<> inline unsigned saturate_cast(schar v) { return (unsigned)std::max(v, (schar)0); } +template<> inline unsigned saturate_cast(short v) { return (unsigned)std::max(v, (short)0); } +template<> inline unsigned saturate_cast(int v) { return (unsigned)std::max(v, (int)0); } +template<> inline unsigned saturate_cast(int64 v) { return (unsigned)((uint64)v <= (uint64)UINT_MAX ? v : v > 0 ? UINT_MAX : 0); } +template<> inline unsigned saturate_cast(uint64 v) { return (unsigned)std::min(v, (uint64)UINT_MAX); } +// we intentionally do not clip negative numbers, to make -1 become 0xffffffff etc. +template<> inline unsigned saturate_cast(float v) { return static_cast(cvRound(v)); } +template<> inline unsigned saturate_cast(double v) { return static_cast(cvRound(v)); } + +template<> inline uint64 saturate_cast(schar v) { return (uint64)std::max(v, (schar)0); } +template<> inline uint64 saturate_cast(short v) { return (uint64)std::max(v, (short)0); } +template<> inline uint64 saturate_cast(int v) { return (uint64)std::max(v, (int)0); } +template<> inline uint64 saturate_cast(int64 v) { return (uint64)std::max(v, (int64)0); } + +template<> inline int64 saturate_cast(uint64 v) { return (int64)std::min(v, (uint64)LLONG_MAX); } + +//! @} + +} // cv + +#endif // OPENCV_CORE_SATURATE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/simd_intrinsics.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/simd_intrinsics.hpp new file mode 100644 index 0000000..309202d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/simd_intrinsics.hpp @@ -0,0 +1,87 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_SIMD_INTRINSICS_HPP +#define OPENCV_CORE_SIMD_INTRINSICS_HPP + +/** +Helper header to support SIMD intrinsics (universal intrinsics) in user code. +Intrinsics documentation: https://docs.opencv.org/3.4/df/d91/group__core__hal__intrin.html + + +Checks of target CPU instruction set based on compiler definitions don't work well enough. +More reliable solutions require utilization of configuration systems (like CMake). + +So, probably you need to specify your own configuration. + +You can do that via CMake in this way: + add_definitions(/DOPENCV_SIMD_CONFIG_HEADER=opencv_simd_config_custom.hpp) +or + add_definitions(/DOPENCV_SIMD_CONFIG_INCLUDE_DIR=1) + +Additionally you may need to add include directory to your files: + include_directories("${CMAKE_CURRENT_LIST_DIR}/opencv_config_${MYTARGET}") + +These files can be pre-generated for target configurations of your application +or generated by CMake on the fly (use CMAKE_BINARY_DIR for that). + +Notes: +- H/W capability checks are still responsibility of your application +- runtime dispatching is not covered by this helper header +*/ + +#ifdef __OPENCV_BUILD +#error "Use core/hal/intrin.hpp during OpenCV build" +#endif + +#ifdef OPENCV_HAL_INTRIN_HPP +#error "core/simd_intrinsics.hpp must be included before core/hal/intrin.hpp" +#endif + +#include "opencv2/core/cvdef.h" + +#ifdef OPENCV_SIMD_CONFIG_HEADER +#include CVAUX_STR(OPENCV_SIMD_CONFIG_HEADER) +#elif defined(OPENCV_SIMD_CONFIG_INCLUDE_DIR) +#include "opencv_simd_config.hpp" // corresponding directory should be added via -I compiler parameter +#else // custom config headers + +#if (!defined(CV_AVX_512F) || !CV_AVX_512F) && (defined(__AVX512__) || defined(__AVX512F__)) +# include +# undef CV_AVX_512F +# define CV_AVX_512F 1 +# ifndef OPENCV_SIMD_DONT_ASSUME_SKX // Skylake-X with AVX-512F/CD/BW/DQ/VL +# undef CV_AVX512_SKX +# define CV_AVX512_SKX 1 +# undef CV_AVX_512CD +# define CV_AVX_512CD 1 +# undef CV_AVX_512BW +# define CV_AVX_512BW 1 +# undef CV_AVX_512DQ +# define CV_AVX_512DQ 1 +# undef CV_AVX_512VL +# define CV_AVX_512VL 1 +# endif +#endif // AVX512 + +// GCC/Clang: -mavx2 +// MSVC: /arch:AVX2 +#if defined __AVX2__ +# include +# undef CV_AVX2 +# define CV_AVX2 1 +# if defined __F16C__ +# undef CV_FP16 +# define CV_FP16 1 +# endif +#endif + +#endif + +// SSE / NEON / VSX is handled by cv_cpu_dispatch.h compatibility block +#include "cv_cpu_dispatch.h" + +#include "hal/intrin.hpp" + +#endif // OPENCV_CORE_SIMD_INTRINSICS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/softfloat.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/softfloat.hpp new file mode 100644 index 0000000..485e15c --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/softfloat.hpp @@ -0,0 +1,514 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +// This file is based on files from package issued with the following license: + +/*============================================================================ + +This C header file is part of the SoftFloat IEEE Floating-Point Arithmetic +Package, Release 3c, by John R. Hauser. + +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 The Regents of the +University of California. All rights reserved. + +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. Neither the name of the University nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "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 REGENTS OR CONTRIBUTORS 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. + +=============================================================================*/ + +#pragma once +#ifndef softfloat_h +#define softfloat_h 1 + +#include "cvdef.h" + +namespace cv +{ + +/** @addtogroup core_utils_softfloat + + [SoftFloat](http://www.jhauser.us/arithmetic/SoftFloat.html) is a software implementation + of floating-point calculations according to IEEE 754 standard. + All calculations are done in integers, that's why they are machine-independent and bit-exact. + This library can be useful in accuracy-critical parts like look-up tables generation, tests, etc. + OpenCV contains a subset of SoftFloat partially rewritten to C++. + + ### Types + + There are two basic types: @ref softfloat and @ref softdouble. + These types are binary compatible with float and double types respectively + and support conversions to/from them. + Other types from original SoftFloat library like fp16 or fp128 were thrown away + as well as quiet/signaling NaN support, on-the-fly rounding mode switch + and exception flags (though exceptions can be implemented in the future). + + ### Operations + + Both types support the following: + - Construction from signed and unsigned 32-bit and 64 integers, + float/double or raw binary representation + - Conversions between each other, to float or double and to int + using @ref cvRound, @ref cvTrunc, @ref cvFloor, @ref cvCeil or a bunch of + saturate_cast functions + - Add, subtract, multiply, divide, remainder, square root, FMA with absolute precision + - Comparison operations + - Explicit sign, exponent and significand manipulation through get/set methods, + number state indicators (isInf, isNan, isSubnormal) + - Type-specific constants like eps, minimum/maximum value, best pi approximation, etc. + - min(), max(), abs(), exp(), log() and pow() functions + +*/ +//! @{ + +struct softfloat; +struct softdouble; + +struct CV_EXPORTS softfloat +{ +public: + /** @brief Default constructor */ + softfloat() { v = 0; } + /** @brief Copy constructor */ + softfloat( const softfloat& c) { v = c.v; } + /** @brief Assign constructor */ + softfloat& operator=( const softfloat& c ) + { + if(&c != this) v = c.v; + return *this; + } + /** @brief Construct from raw + + Builds new value from raw binary representation + */ + static const softfloat fromRaw( const uint32_t a ) { softfloat x; x.v = a; return x; } + + /** @brief Construct from integer */ + explicit softfloat( const uint32_t ); + explicit softfloat( const uint64_t ); + explicit softfloat( const int32_t ); + explicit softfloat( const int64_t ); + +#ifdef CV_INT32_T_IS_LONG_INT + // for platforms with int32_t = long int + explicit softfloat( const int a ) { *this = softfloat(static_cast(a)); } +#endif + + /** @brief Construct from float */ + explicit softfloat( const float a ) { Cv32suf s; s.f = a; v = s.u; } + + /** @brief Type casts */ + operator softdouble() const; + operator float() const { Cv32suf s; s.u = v; return s.f; } + + /** @brief Basic arithmetics */ + softfloat operator + (const softfloat&) const; + softfloat operator - (const softfloat&) const; + softfloat operator * (const softfloat&) const; + softfloat operator / (const softfloat&) const; + softfloat operator - () const { softfloat x; x.v = v ^ (1U << 31); return x; } + + /** @brief Remainder operator + + A quote from original SoftFloat manual: + + > The IEEE Standard remainder operation computes the value + > a - n * b, where n is the integer closest to a / b. + > If a / b is exactly halfway between two integers, n is the even integer + > closest to a / b. The IEEE Standard鈥檚 remainder operation is always exact and so requires no rounding. + > Depending on the relative magnitudes of the operands, the remainder functions + > can take considerably longer to execute than the other SoftFloat functions. + > This is an inherent characteristic of the remainder operation itself and is not a flaw + > in the SoftFloat implementation. + */ + softfloat operator % (const softfloat&) const; + + softfloat& operator += (const softfloat& a) { *this = *this + a; return *this; } + softfloat& operator -= (const softfloat& a) { *this = *this - a; return *this; } + softfloat& operator *= (const softfloat& a) { *this = *this * a; return *this; } + softfloat& operator /= (const softfloat& a) { *this = *this / a; return *this; } + softfloat& operator %= (const softfloat& a) { *this = *this % a; return *this; } + + /** @brief Comparison operations + + - Any operation with NaN produces false + + The only exception is when x is NaN: x != y for any y. + - Positive and negative zeros are equal + */ + bool operator == ( const softfloat& ) const; + bool operator != ( const softfloat& ) const; + bool operator > ( const softfloat& ) const; + bool operator >= ( const softfloat& ) const; + bool operator < ( const softfloat& ) const; + bool operator <= ( const softfloat& ) const; + + /** @brief NaN state indicator */ + inline bool isNaN() const { return (v & 0x7fffffff) > 0x7f800000; } + /** @brief Inf state indicator */ + inline bool isInf() const { return (v & 0x7fffffff) == 0x7f800000; } + /** @brief Subnormal number indicator */ + inline bool isSubnormal() const { return ((v >> 23) & 0xFF) == 0; } + + /** @brief Get sign bit */ + inline bool getSign() const { return (v >> 31) != 0; } + /** @brief Construct a copy with new sign bit */ + inline softfloat setSign(bool sign) const { softfloat x; x.v = (v & ((1U << 31) - 1)) | ((uint32_t)sign << 31); return x; } + /** @brief Get 0-based exponent */ + inline int getExp() const { return ((v >> 23) & 0xFF) - 127; } + /** @brief Construct a copy with new 0-based exponent */ + inline softfloat setExp(int e) const { softfloat x; x.v = (v & 0x807fffff) | (((e + 127) & 0xFF) << 23 ); return x; } + + /** @brief Get a fraction part + + Returns a number 1 <= x < 2 with the same significand + */ + inline softfloat getFrac() const + { + uint_fast32_t vv = (v & 0x007fffff) | (127 << 23); + return softfloat::fromRaw(vv); + } + /** @brief Construct a copy with provided significand + + Constructs a copy of a number with significand taken from parameter + */ + inline softfloat setFrac(const softfloat& s) const + { + softfloat x; + x.v = (v & 0xff800000) | (s.v & 0x007fffff); + return x; + } + + /** @brief Zero constant */ + static softfloat zero() { return softfloat::fromRaw( 0 ); } + /** @brief Positive infinity constant */ + static softfloat inf() { return softfloat::fromRaw( 0xFF << 23 ); } + /** @brief Default NaN constant */ + static softfloat nan() { return softfloat::fromRaw( 0x7fffffff ); } + /** @brief One constant */ + static softfloat one() { return softfloat::fromRaw( 127 << 23 ); } + /** @brief Smallest normalized value */ + static softfloat min() { return softfloat::fromRaw( 0x01 << 23 ); } + /** @brief Difference between 1 and next representable value */ + static softfloat eps() { return softfloat::fromRaw( (127 - 23) << 23 ); } + /** @brief Biggest finite value */ + static softfloat max() { return softfloat::fromRaw( (0xFF << 23) - 1 ); } + /** @brief Correct pi approximation */ + static softfloat pi() { return softfloat::fromRaw( 0x40490fdb ); } + + uint32_t v; +}; + +/*---------------------------------------------------------------------------- +*----------------------------------------------------------------------------*/ + +struct CV_EXPORTS softdouble +{ +public: + /** @brief Default constructor */ + softdouble() : v(0) { } + /** @brief Copy constructor */ + softdouble( const softdouble& c) { v = c.v; } + /** @brief Assign constructor */ + softdouble& operator=( const softdouble& c ) + { + if(&c != this) v = c.v; + return *this; + } + /** @brief Construct from raw + + Builds new value from raw binary representation + */ + static softdouble fromRaw( const uint64_t a ) { softdouble x; x.v = a; return x; } + + /** @brief Construct from integer */ + explicit softdouble( const uint32_t ); + explicit softdouble( const uint64_t ); + explicit softdouble( const int32_t ); + explicit softdouble( const int64_t ); + +#ifdef CV_INT32_T_IS_LONG_INT + // for platforms with int32_t = long int + explicit softdouble( const int a ) { *this = softdouble(static_cast(a)); } +#endif + + /** @brief Construct from double */ + explicit softdouble( const double a ) { Cv64suf s; s.f = a; v = s.u; } + + /** @brief Type casts */ + operator softfloat() const; + operator double() const { Cv64suf s; s.u = v; return s.f; } + + /** @brief Basic arithmetics */ + softdouble operator + (const softdouble&) const; + softdouble operator - (const softdouble&) const; + softdouble operator * (const softdouble&) const; + softdouble operator / (const softdouble&) const; + softdouble operator - () const { softdouble x; x.v = v ^ (1ULL << 63); return x; } + + /** @brief Remainder operator + + A quote from original SoftFloat manual: + + > The IEEE Standard remainder operation computes the value + > a - n * b, where n is the integer closest to a / b. + > If a / b is exactly halfway between two integers, n is the even integer + > closest to a / b. The IEEE Standard鈥檚 remainder operation is always exact and so requires no rounding. + > Depending on the relative magnitudes of the operands, the remainder functions + > can take considerably longer to execute than the other SoftFloat functions. + > This is an inherent characteristic of the remainder operation itself and is not a flaw + > in the SoftFloat implementation. + */ + softdouble operator % (const softdouble&) const; + + softdouble& operator += (const softdouble& a) { *this = *this + a; return *this; } + softdouble& operator -= (const softdouble& a) { *this = *this - a; return *this; } + softdouble& operator *= (const softdouble& a) { *this = *this * a; return *this; } + softdouble& operator /= (const softdouble& a) { *this = *this / a; return *this; } + softdouble& operator %= (const softdouble& a) { *this = *this % a; return *this; } + + /** @brief Comparison operations + + - Any operation with NaN produces false + + The only exception is when x is NaN: x != y for any y. + - Positive and negative zeros are equal + */ + bool operator == ( const softdouble& ) const; + bool operator != ( const softdouble& ) const; + bool operator > ( const softdouble& ) const; + bool operator >= ( const softdouble& ) const; + bool operator < ( const softdouble& ) const; + bool operator <= ( const softdouble& ) const; + + /** @brief NaN state indicator */ + inline bool isNaN() const { return (v & 0x7fffffffffffffff) > 0x7ff0000000000000; } + /** @brief Inf state indicator */ + inline bool isInf() const { return (v & 0x7fffffffffffffff) == 0x7ff0000000000000; } + /** @brief Subnormal number indicator */ + inline bool isSubnormal() const { return ((v >> 52) & 0x7FF) == 0; } + + /** @brief Get sign bit */ + inline bool getSign() const { return (v >> 63) != 0; } + /** @brief Construct a copy with new sign bit */ + softdouble setSign(bool sign) const { softdouble x; x.v = (v & ((1ULL << 63) - 1)) | ((uint_fast64_t)(sign) << 63); return x; } + /** @brief Get 0-based exponent */ + inline int getExp() const { return ((v >> 52) & 0x7FF) - 1023; } + /** @brief Construct a copy with new 0-based exponent */ + inline softdouble setExp(int e) const + { + softdouble x; + x.v = (v & 0x800FFFFFFFFFFFFF) | ((uint_fast64_t)((e + 1023) & 0x7FF) << 52); + return x; + } + + /** @brief Get a fraction part + + Returns a number 1 <= x < 2 with the same significand + */ + inline softdouble getFrac() const + { + uint_fast64_t vv = (v & 0x000FFFFFFFFFFFFF) | ((uint_fast64_t)(1023) << 52); + return softdouble::fromRaw(vv); + } + /** @brief Construct a copy with provided significand + + Constructs a copy of a number with significand taken from parameter + */ + inline softdouble setFrac(const softdouble& s) const + { + softdouble x; + x.v = (v & 0xFFF0000000000000) | (s.v & 0x000FFFFFFFFFFFFF); + return x; + } + + /** @brief Zero constant */ + static softdouble zero() { return softdouble::fromRaw( 0 ); } + /** @brief Positive infinity constant */ + static softdouble inf() { return softdouble::fromRaw( (uint_fast64_t)(0x7FF) << 52 ); } + /** @brief Default NaN constant */ + static softdouble nan() { return softdouble::fromRaw( CV_BIG_INT(0x7FFFFFFFFFFFFFFF) ); } + /** @brief One constant */ + static softdouble one() { return softdouble::fromRaw( (uint_fast64_t)( 1023) << 52 ); } + /** @brief Smallest normalized value */ + static softdouble min() { return softdouble::fromRaw( (uint_fast64_t)( 0x01) << 52 ); } + /** @brief Difference between 1 and next representable value */ + static softdouble eps() { return softdouble::fromRaw( (uint_fast64_t)( 1023 - 52 ) << 52 ); } + /** @brief Biggest finite value */ + static softdouble max() { return softdouble::fromRaw( ((uint_fast64_t)(0x7FF) << 52) - 1 ); } + /** @brief Correct pi approximation */ + static softdouble pi() { return softdouble::fromRaw( CV_BIG_INT(0x400921FB54442D18) ); } + + uint64_t v; +}; + +/*---------------------------------------------------------------------------- +*----------------------------------------------------------------------------*/ + +/** @brief Fused Multiplication and Addition + +Computes (a*b)+c with single rounding +*/ +CV_EXPORTS softfloat mulAdd( const softfloat& a, const softfloat& b, const softfloat & c); +CV_EXPORTS softdouble mulAdd( const softdouble& a, const softdouble& b, const softdouble& c); + +/** @brief Square root */ +CV_EXPORTS softfloat sqrt( const softfloat& a ); +CV_EXPORTS softdouble sqrt( const softdouble& a ); +} + +/*---------------------------------------------------------------------------- +| Ported from OpenCV and added for usability +*----------------------------------------------------------------------------*/ + +/** @brief Truncates number to integer with minimum magnitude */ +CV_EXPORTS int cvTrunc(const cv::softfloat& a); +CV_EXPORTS int cvTrunc(const cv::softdouble& a); + +/** @brief Rounds a number to nearest even integer */ +CV_EXPORTS int cvRound(const cv::softfloat& a); +CV_EXPORTS int cvRound(const cv::softdouble& a); + +/** @brief Rounds a number to nearest even long long integer */ +CV_EXPORTS int64_t cvRound64(const cv::softdouble& a); + +/** @brief Rounds a number down to integer */ +CV_EXPORTS int cvFloor(const cv::softfloat& a); +CV_EXPORTS int cvFloor(const cv::softdouble& a); + +/** @brief Rounds number up to integer */ +CV_EXPORTS int cvCeil(const cv::softfloat& a); +CV_EXPORTS int cvCeil(const cv::softdouble& a); + +namespace cv +{ +/** @brief Saturate casts */ +template static inline _Tp saturate_cast(softfloat a) { return _Tp(a); } +template static inline _Tp saturate_cast(softdouble a) { return _Tp(a); } + +template<> inline uchar saturate_cast(softfloat a) { return (uchar)std::max(std::min(cvRound(a), (int)UCHAR_MAX), 0); } +template<> inline uchar saturate_cast(softdouble a) { return (uchar)std::max(std::min(cvRound(a), (int)UCHAR_MAX), 0); } + +template<> inline schar saturate_cast(softfloat a) { return (schar)std::min(std::max(cvRound(a), (int)SCHAR_MIN), (int)SCHAR_MAX); } +template<> inline schar saturate_cast(softdouble a) { return (schar)std::min(std::max(cvRound(a), (int)SCHAR_MIN), (int)SCHAR_MAX); } + +template<> inline ushort saturate_cast(softfloat a) { return (ushort)std::max(std::min(cvRound(a), (int)USHRT_MAX), 0); } +template<> inline ushort saturate_cast(softdouble a) { return (ushort)std::max(std::min(cvRound(a), (int)USHRT_MAX), 0); } + +template<> inline short saturate_cast(softfloat a) { return (short)std::min(std::max(cvRound(a), (int)SHRT_MIN), (int)SHRT_MAX); } +template<> inline short saturate_cast(softdouble a) { return (short)std::min(std::max(cvRound(a), (int)SHRT_MIN), (int)SHRT_MAX); } + +template<> inline int saturate_cast(softfloat a) { return cvRound(a); } +template<> inline int saturate_cast(softdouble a) { return cvRound(a); } + +template<> inline int64_t saturate_cast(softfloat a) { return cvRound(a); } +template<> inline int64_t saturate_cast(softdouble a) { return cvRound64(a); } + +/** @brief Saturate cast to unsigned integer and unsigned long long integer +We intentionally do not clip negative numbers, to make -1 become 0xffffffff etc. +*/ +template<> inline unsigned saturate_cast(softfloat a) { return cvRound(a); } +template<> inline unsigned saturate_cast(softdouble a) { return cvRound(a); } + +template<> inline uint64_t saturate_cast(softfloat a) { return cvRound(a); } +template<> inline uint64_t saturate_cast(softdouble a) { return cvRound64(a); } + +/** @brief Min and Max functions */ +inline softfloat min(const softfloat& a, const softfloat& b) { return (a > b) ? b : a; } +inline softdouble min(const softdouble& a, const softdouble& b) { return (a > b) ? b : a; } + +inline softfloat max(const softfloat& a, const softfloat& b) { return (a > b) ? a : b; } +inline softdouble max(const softdouble& a, const softdouble& b) { return (a > b) ? a : b; } + +/** @brief Absolute value */ +inline softfloat abs( softfloat a) { softfloat x; x.v = a.v & ((1U << 31) - 1); return x; } +inline softdouble abs( softdouble a) { softdouble x; x.v = a.v & ((1ULL << 63) - 1); return x; } + +/** @brief Exponent + +Special cases: +- exp(NaN) is NaN +- exp(-Inf) == 0 +- exp(+Inf) == +Inf +*/ +CV_EXPORTS softfloat exp( const softfloat& a); +CV_EXPORTS softdouble exp( const softdouble& a); + +/** @brief Natural logarithm + +Special cases: +- log(NaN), log(x < 0) are NaN +- log(0) == -Inf +*/ +CV_EXPORTS softfloat log( const softfloat& a ); +CV_EXPORTS softdouble log( const softdouble& a ); + +/** @brief Raising to the power + +Special cases: +- x**NaN is NaN for any x +- ( |x| == 1 )**Inf is NaN +- ( |x| > 1 )**+Inf or ( |x| < 1 )**-Inf is +Inf +- ( |x| > 1 )**-Inf or ( |x| < 1 )**+Inf is 0 +- x ** 0 == 1 for any x +- x ** 1 == 1 for any x +- NaN ** y is NaN for any other y +- Inf**(y < 0) == 0 +- Inf ** y is +Inf for any other y +- (x < 0)**y is NaN for any other y if x can't be correctly rounded to integer +- 0 ** 0 == 1 +- 0 ** (y < 0) is +Inf +- 0 ** (y > 0) is 0 +*/ +CV_EXPORTS softfloat pow( const softfloat& a, const softfloat& b); +CV_EXPORTS softdouble pow( const softdouble& a, const softdouble& b); + +/** @brief Cube root + +Special cases: +- cbrt(NaN) is NaN +- cbrt(+/-Inf) is +/-Inf +*/ +CV_EXPORTS softfloat cbrt( const softfloat& a ); + +/** @brief Sine + +Special cases: +- sin(Inf) or sin(NaN) is NaN +- sin(x) == x when sin(x) is close to zero +*/ +CV_EXPORTS softdouble sin( const softdouble& a ); + +/** @brief Cosine + * +Special cases: +- cos(Inf) or cos(NaN) is NaN +- cos(x) == +/- 1 when cos(x) is close to +/- 1 +*/ +CV_EXPORTS softdouble cos( const softdouble& a ); + +//! @} core_utils_softfloat + +} // cv:: + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/sse_utils.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/sse_utils.hpp new file mode 100644 index 0000000..0906583 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/sse_utils.hpp @@ -0,0 +1,652 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_SSE_UTILS_HPP +#define OPENCV_CORE_SSE_UTILS_HPP + +#ifndef __cplusplus +# error sse_utils.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cvdef.h" + +//! @addtogroup core_utils_sse +//! @{ + +#if CV_SSE2 + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_g0); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_g0); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_g1); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_g1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk3); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk3); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk3); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk3); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk2); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk2); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk3); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk3); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk2); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk2); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk3); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk3); +} + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_g1); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_g1); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_b0); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_b0); + __m128i layer1_chunk4 = _mm_unpacklo_epi8(v_g0, v_b1); + __m128i layer1_chunk5 = _mm_unpackhi_epi8(v_g0, v_b1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk4 = _mm_unpacklo_epi8(layer1_chunk2, layer1_chunk5); + __m128i layer2_chunk5 = _mm_unpackhi_epi8(layer1_chunk2, layer1_chunk5); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk4 = _mm_unpacklo_epi8(layer2_chunk2, layer2_chunk5); + __m128i layer3_chunk5 = _mm_unpackhi_epi8(layer2_chunk2, layer2_chunk5); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk3); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk3); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk4); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk4); + __m128i layer4_chunk4 = _mm_unpacklo_epi8(layer3_chunk2, layer3_chunk5); + __m128i layer4_chunk5 = _mm_unpackhi_epi8(layer3_chunk2, layer3_chunk5); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk3); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk3); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk4); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk4); + v_b0 = _mm_unpacklo_epi8(layer4_chunk2, layer4_chunk5); + v_b1 = _mm_unpackhi_epi8(layer4_chunk2, layer4_chunk5); +} + +inline void _mm_deinterleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi8(v_r0, v_b0); + __m128i layer1_chunk1 = _mm_unpackhi_epi8(v_r0, v_b0); + __m128i layer1_chunk2 = _mm_unpacklo_epi8(v_r1, v_b1); + __m128i layer1_chunk3 = _mm_unpackhi_epi8(v_r1, v_b1); + __m128i layer1_chunk4 = _mm_unpacklo_epi8(v_g0, v_a0); + __m128i layer1_chunk5 = _mm_unpackhi_epi8(v_g0, v_a0); + __m128i layer1_chunk6 = _mm_unpacklo_epi8(v_g1, v_a1); + __m128i layer1_chunk7 = _mm_unpackhi_epi8(v_g1, v_a1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi8(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk1 = _mm_unpackhi_epi8(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk2 = _mm_unpacklo_epi8(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk3 = _mm_unpackhi_epi8(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk4 = _mm_unpacklo_epi8(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk5 = _mm_unpackhi_epi8(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk6 = _mm_unpacklo_epi8(layer1_chunk3, layer1_chunk7); + __m128i layer2_chunk7 = _mm_unpackhi_epi8(layer1_chunk3, layer1_chunk7); + + __m128i layer3_chunk0 = _mm_unpacklo_epi8(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk1 = _mm_unpackhi_epi8(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk2 = _mm_unpacklo_epi8(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk3 = _mm_unpackhi_epi8(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk4 = _mm_unpacklo_epi8(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk5 = _mm_unpackhi_epi8(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk6 = _mm_unpacklo_epi8(layer2_chunk3, layer2_chunk7); + __m128i layer3_chunk7 = _mm_unpackhi_epi8(layer2_chunk3, layer2_chunk7); + + __m128i layer4_chunk0 = _mm_unpacklo_epi8(layer3_chunk0, layer3_chunk4); + __m128i layer4_chunk1 = _mm_unpackhi_epi8(layer3_chunk0, layer3_chunk4); + __m128i layer4_chunk2 = _mm_unpacklo_epi8(layer3_chunk1, layer3_chunk5); + __m128i layer4_chunk3 = _mm_unpackhi_epi8(layer3_chunk1, layer3_chunk5); + __m128i layer4_chunk4 = _mm_unpacklo_epi8(layer3_chunk2, layer3_chunk6); + __m128i layer4_chunk5 = _mm_unpackhi_epi8(layer3_chunk2, layer3_chunk6); + __m128i layer4_chunk6 = _mm_unpacklo_epi8(layer3_chunk3, layer3_chunk7); + __m128i layer4_chunk7 = _mm_unpackhi_epi8(layer3_chunk3, layer3_chunk7); + + v_r0 = _mm_unpacklo_epi8(layer4_chunk0, layer4_chunk4); + v_r1 = _mm_unpackhi_epi8(layer4_chunk0, layer4_chunk4); + v_g0 = _mm_unpacklo_epi8(layer4_chunk1, layer4_chunk5); + v_g1 = _mm_unpackhi_epi8(layer4_chunk1, layer4_chunk5); + v_b0 = _mm_unpacklo_epi8(layer4_chunk2, layer4_chunk6); + v_b1 = _mm_unpackhi_epi8(layer4_chunk2, layer4_chunk6); + v_a0 = _mm_unpacklo_epi8(layer4_chunk3, layer4_chunk7); + v_a1 = _mm_unpackhi_epi8(layer4_chunk3, layer4_chunk7); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_g1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk4 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer4_chunk5 = _mm_packus_epi16(_mm_srli_epi16(v_b0, 8), _mm_srli_epi16(v_b1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_and_si128(layer4_chunk4, v_mask), _mm_and_si128(layer4_chunk5, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk4, 8), _mm_srli_epi16(layer4_chunk5, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk4, 8), _mm_srli_epi16(layer3_chunk5, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk4, 8), _mm_srli_epi16(layer2_chunk5, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); + v_g0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_b1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk4, 8), _mm_srli_epi16(layer1_chunk5, 8)); +} + +inline void _mm_interleave_epi8(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i v_mask = _mm_set1_epi16(0x00ff); + + __m128i layer4_chunk0 = _mm_packus_epi16(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer4_chunk4 = _mm_packus_epi16(_mm_srli_epi16(v_r0, 8), _mm_srli_epi16(v_r1, 8)); + __m128i layer4_chunk1 = _mm_packus_epi16(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer4_chunk5 = _mm_packus_epi16(_mm_srli_epi16(v_g0, 8), _mm_srli_epi16(v_g1, 8)); + __m128i layer4_chunk2 = _mm_packus_epi16(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer4_chunk6 = _mm_packus_epi16(_mm_srli_epi16(v_b0, 8), _mm_srli_epi16(v_b1, 8)); + __m128i layer4_chunk3 = _mm_packus_epi16(_mm_and_si128(v_a0, v_mask), _mm_and_si128(v_a1, v_mask)); + __m128i layer4_chunk7 = _mm_packus_epi16(_mm_srli_epi16(v_a0, 8), _mm_srli_epi16(v_a1, 8)); + + __m128i layer3_chunk0 = _mm_packus_epi16(_mm_and_si128(layer4_chunk0, v_mask), _mm_and_si128(layer4_chunk1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk0, 8), _mm_srli_epi16(layer4_chunk1, 8)); + __m128i layer3_chunk1 = _mm_packus_epi16(_mm_and_si128(layer4_chunk2, v_mask), _mm_and_si128(layer4_chunk3, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk2, 8), _mm_srli_epi16(layer4_chunk3, 8)); + __m128i layer3_chunk2 = _mm_packus_epi16(_mm_and_si128(layer4_chunk4, v_mask), _mm_and_si128(layer4_chunk5, v_mask)); + __m128i layer3_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk4, 8), _mm_srli_epi16(layer4_chunk5, 8)); + __m128i layer3_chunk3 = _mm_packus_epi16(_mm_and_si128(layer4_chunk6, v_mask), _mm_and_si128(layer4_chunk7, v_mask)); + __m128i layer3_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer4_chunk6, 8), _mm_srli_epi16(layer4_chunk7, 8)); + + __m128i layer2_chunk0 = _mm_packus_epi16(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk0, 8), _mm_srli_epi16(layer3_chunk1, 8)); + __m128i layer2_chunk1 = _mm_packus_epi16(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk2, 8), _mm_srli_epi16(layer3_chunk3, 8)); + __m128i layer2_chunk2 = _mm_packus_epi16(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk4, 8), _mm_srli_epi16(layer3_chunk5, 8)); + __m128i layer2_chunk3 = _mm_packus_epi16(_mm_and_si128(layer3_chunk6, v_mask), _mm_and_si128(layer3_chunk7, v_mask)); + __m128i layer2_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer3_chunk6, 8), _mm_srli_epi16(layer3_chunk7, 8)); + + __m128i layer1_chunk0 = _mm_packus_epi16(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk0, 8), _mm_srli_epi16(layer2_chunk1, 8)); + __m128i layer1_chunk1 = _mm_packus_epi16(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk2, 8), _mm_srli_epi16(layer2_chunk3, 8)); + __m128i layer1_chunk2 = _mm_packus_epi16(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk6 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk4, 8), _mm_srli_epi16(layer2_chunk5, 8)); + __m128i layer1_chunk3 = _mm_packus_epi16(_mm_and_si128(layer2_chunk6, v_mask), _mm_and_si128(layer2_chunk7, v_mask)); + __m128i layer1_chunk7 = _mm_packus_epi16(_mm_srli_epi16(layer2_chunk6, 8), _mm_srli_epi16(layer2_chunk7, 8)); + + v_r0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_b0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk0, 8), _mm_srli_epi16(layer1_chunk1, 8)); + v_r1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk2, 8), _mm_srli_epi16(layer1_chunk3, 8)); + v_g0 = _mm_packus_epi16(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_a0 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk4, 8), _mm_srli_epi16(layer1_chunk5, 8)); + v_g1 = _mm_packus_epi16(_mm_and_si128(layer1_chunk6, v_mask), _mm_and_si128(layer1_chunk7, v_mask)); + v_a1 = _mm_packus_epi16(_mm_srli_epi16(layer1_chunk6, 8), _mm_srli_epi16(layer1_chunk7, 8)); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_g0); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_g0); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_g1); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_g1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk2); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk3); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk3); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk2); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk3); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk3); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk2); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk2); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk3); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk3); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_g1); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_g1); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_b0); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_b0); + __m128i layer1_chunk4 = _mm_unpacklo_epi16(v_g0, v_b1); + __m128i layer1_chunk5 = _mm_unpackhi_epi16(v_g0, v_b1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk3); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk4); + __m128i layer2_chunk4 = _mm_unpacklo_epi16(layer1_chunk2, layer1_chunk5); + __m128i layer2_chunk5 = _mm_unpackhi_epi16(layer1_chunk2, layer1_chunk5); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk3); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk4); + __m128i layer3_chunk4 = _mm_unpacklo_epi16(layer2_chunk2, layer2_chunk5); + __m128i layer3_chunk5 = _mm_unpackhi_epi16(layer2_chunk2, layer2_chunk5); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk3); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk3); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk4); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk4); + v_b0 = _mm_unpacklo_epi16(layer3_chunk2, layer3_chunk5); + v_b1 = _mm_unpackhi_epi16(layer3_chunk2, layer3_chunk5); +} + +inline void _mm_deinterleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i layer1_chunk0 = _mm_unpacklo_epi16(v_r0, v_b0); + __m128i layer1_chunk1 = _mm_unpackhi_epi16(v_r0, v_b0); + __m128i layer1_chunk2 = _mm_unpacklo_epi16(v_r1, v_b1); + __m128i layer1_chunk3 = _mm_unpackhi_epi16(v_r1, v_b1); + __m128i layer1_chunk4 = _mm_unpacklo_epi16(v_g0, v_a0); + __m128i layer1_chunk5 = _mm_unpackhi_epi16(v_g0, v_a0); + __m128i layer1_chunk6 = _mm_unpacklo_epi16(v_g1, v_a1); + __m128i layer1_chunk7 = _mm_unpackhi_epi16(v_g1, v_a1); + + __m128i layer2_chunk0 = _mm_unpacklo_epi16(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk1 = _mm_unpackhi_epi16(layer1_chunk0, layer1_chunk4); + __m128i layer2_chunk2 = _mm_unpacklo_epi16(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk3 = _mm_unpackhi_epi16(layer1_chunk1, layer1_chunk5); + __m128i layer2_chunk4 = _mm_unpacklo_epi16(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk5 = _mm_unpackhi_epi16(layer1_chunk2, layer1_chunk6); + __m128i layer2_chunk6 = _mm_unpacklo_epi16(layer1_chunk3, layer1_chunk7); + __m128i layer2_chunk7 = _mm_unpackhi_epi16(layer1_chunk3, layer1_chunk7); + + __m128i layer3_chunk0 = _mm_unpacklo_epi16(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk1 = _mm_unpackhi_epi16(layer2_chunk0, layer2_chunk4); + __m128i layer3_chunk2 = _mm_unpacklo_epi16(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk3 = _mm_unpackhi_epi16(layer2_chunk1, layer2_chunk5); + __m128i layer3_chunk4 = _mm_unpacklo_epi16(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk5 = _mm_unpackhi_epi16(layer2_chunk2, layer2_chunk6); + __m128i layer3_chunk6 = _mm_unpacklo_epi16(layer2_chunk3, layer2_chunk7); + __m128i layer3_chunk7 = _mm_unpackhi_epi16(layer2_chunk3, layer2_chunk7); + + v_r0 = _mm_unpacklo_epi16(layer3_chunk0, layer3_chunk4); + v_r1 = _mm_unpackhi_epi16(layer3_chunk0, layer3_chunk4); + v_g0 = _mm_unpacklo_epi16(layer3_chunk1, layer3_chunk5); + v_g1 = _mm_unpackhi_epi16(layer3_chunk1, layer3_chunk5); + v_b0 = _mm_unpacklo_epi16(layer3_chunk2, layer3_chunk6); + v_b1 = _mm_unpackhi_epi16(layer3_chunk2, layer3_chunk6); + v_a0 = _mm_unpacklo_epi16(layer3_chunk3, layer3_chunk7); + v_a1 = _mm_unpackhi_epi16(layer3_chunk3, layer3_chunk7); +} + +#if CV_SSE4_1 + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_g1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); +} + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, + __m128i & v_g1, __m128i & v_b0, __m128i & v_b1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi32(_mm_srli_epi32(v_b0, 16), _mm_srli_epi32(v_b1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk4, 16), _mm_srli_epi32(layer3_chunk5, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk4, 16), _mm_srli_epi32(layer2_chunk5, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_g1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); + v_g0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_b1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk4, 16), _mm_srli_epi32(layer1_chunk5, 16)); +} + +inline void _mm_interleave_epi16(__m128i & v_r0, __m128i & v_r1, __m128i & v_g0, __m128i & v_g1, + __m128i & v_b0, __m128i & v_b1, __m128i & v_a0, __m128i & v_a1) +{ + __m128i v_mask = _mm_set1_epi32(0x0000ffff); + + __m128i layer3_chunk0 = _mm_packus_epi32(_mm_and_si128(v_r0, v_mask), _mm_and_si128(v_r1, v_mask)); + __m128i layer3_chunk4 = _mm_packus_epi32(_mm_srli_epi32(v_r0, 16), _mm_srli_epi32(v_r1, 16)); + __m128i layer3_chunk1 = _mm_packus_epi32(_mm_and_si128(v_g0, v_mask), _mm_and_si128(v_g1, v_mask)); + __m128i layer3_chunk5 = _mm_packus_epi32(_mm_srli_epi32(v_g0, 16), _mm_srli_epi32(v_g1, 16)); + __m128i layer3_chunk2 = _mm_packus_epi32(_mm_and_si128(v_b0, v_mask), _mm_and_si128(v_b1, v_mask)); + __m128i layer3_chunk6 = _mm_packus_epi32(_mm_srli_epi32(v_b0, 16), _mm_srli_epi32(v_b1, 16)); + __m128i layer3_chunk3 = _mm_packus_epi32(_mm_and_si128(v_a0, v_mask), _mm_and_si128(v_a1, v_mask)); + __m128i layer3_chunk7 = _mm_packus_epi32(_mm_srli_epi32(v_a0, 16), _mm_srli_epi32(v_a1, 16)); + + __m128i layer2_chunk0 = _mm_packus_epi32(_mm_and_si128(layer3_chunk0, v_mask), _mm_and_si128(layer3_chunk1, v_mask)); + __m128i layer2_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk0, 16), _mm_srli_epi32(layer3_chunk1, 16)); + __m128i layer2_chunk1 = _mm_packus_epi32(_mm_and_si128(layer3_chunk2, v_mask), _mm_and_si128(layer3_chunk3, v_mask)); + __m128i layer2_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk2, 16), _mm_srli_epi32(layer3_chunk3, 16)); + __m128i layer2_chunk2 = _mm_packus_epi32(_mm_and_si128(layer3_chunk4, v_mask), _mm_and_si128(layer3_chunk5, v_mask)); + __m128i layer2_chunk6 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk4, 16), _mm_srli_epi32(layer3_chunk5, 16)); + __m128i layer2_chunk3 = _mm_packus_epi32(_mm_and_si128(layer3_chunk6, v_mask), _mm_and_si128(layer3_chunk7, v_mask)); + __m128i layer2_chunk7 = _mm_packus_epi32(_mm_srli_epi32(layer3_chunk6, 16), _mm_srli_epi32(layer3_chunk7, 16)); + + __m128i layer1_chunk0 = _mm_packus_epi32(_mm_and_si128(layer2_chunk0, v_mask), _mm_and_si128(layer2_chunk1, v_mask)); + __m128i layer1_chunk4 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk0, 16), _mm_srli_epi32(layer2_chunk1, 16)); + __m128i layer1_chunk1 = _mm_packus_epi32(_mm_and_si128(layer2_chunk2, v_mask), _mm_and_si128(layer2_chunk3, v_mask)); + __m128i layer1_chunk5 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk2, 16), _mm_srli_epi32(layer2_chunk3, 16)); + __m128i layer1_chunk2 = _mm_packus_epi32(_mm_and_si128(layer2_chunk4, v_mask), _mm_and_si128(layer2_chunk5, v_mask)); + __m128i layer1_chunk6 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk4, 16), _mm_srli_epi32(layer2_chunk5, 16)); + __m128i layer1_chunk3 = _mm_packus_epi32(_mm_and_si128(layer2_chunk6, v_mask), _mm_and_si128(layer2_chunk7, v_mask)); + __m128i layer1_chunk7 = _mm_packus_epi32(_mm_srli_epi32(layer2_chunk6, 16), _mm_srli_epi32(layer2_chunk7, 16)); + + v_r0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk0, v_mask), _mm_and_si128(layer1_chunk1, v_mask)); + v_b0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk0, 16), _mm_srli_epi32(layer1_chunk1, 16)); + v_r1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk2, v_mask), _mm_and_si128(layer1_chunk3, v_mask)); + v_b1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk2, 16), _mm_srli_epi32(layer1_chunk3, 16)); + v_g0 = _mm_packus_epi32(_mm_and_si128(layer1_chunk4, v_mask), _mm_and_si128(layer1_chunk5, v_mask)); + v_a0 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk4, 16), _mm_srli_epi32(layer1_chunk5, 16)); + v_g1 = _mm_packus_epi32(_mm_and_si128(layer1_chunk6, v_mask), _mm_and_si128(layer1_chunk7, v_mask)); + v_a1 = _mm_packus_epi32(_mm_srli_epi32(layer1_chunk6, 16), _mm_srli_epi32(layer1_chunk7, 16)); +} + +#endif // CV_SSE4_1 + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_g0); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_g0); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_g1); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_g1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk2); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk2); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk3); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk3); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk2); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk2); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk3); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk3); +} + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, + __m128 & v_g1, __m128 & v_b0, __m128 & v_b1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_g1); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_g1); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_b0); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_b0); + __m128 layer1_chunk4 = _mm_unpacklo_ps(v_g0, v_b1); + __m128 layer1_chunk5 = _mm_unpackhi_ps(v_g0, v_b1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk3); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk3); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk4); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk4); + __m128 layer2_chunk4 = _mm_unpacklo_ps(layer1_chunk2, layer1_chunk5); + __m128 layer2_chunk5 = _mm_unpackhi_ps(layer1_chunk2, layer1_chunk5); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk3); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk3); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk4); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk4); + v_b0 = _mm_unpacklo_ps(layer2_chunk2, layer2_chunk5); + v_b1 = _mm_unpackhi_ps(layer2_chunk2, layer2_chunk5); +} + +inline void _mm_deinterleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1, + __m128 & v_b0, __m128 & v_b1, __m128 & v_a0, __m128 & v_a1) +{ + __m128 layer1_chunk0 = _mm_unpacklo_ps(v_r0, v_b0); + __m128 layer1_chunk1 = _mm_unpackhi_ps(v_r0, v_b0); + __m128 layer1_chunk2 = _mm_unpacklo_ps(v_r1, v_b1); + __m128 layer1_chunk3 = _mm_unpackhi_ps(v_r1, v_b1); + __m128 layer1_chunk4 = _mm_unpacklo_ps(v_g0, v_a0); + __m128 layer1_chunk5 = _mm_unpackhi_ps(v_g0, v_a0); + __m128 layer1_chunk6 = _mm_unpacklo_ps(v_g1, v_a1); + __m128 layer1_chunk7 = _mm_unpackhi_ps(v_g1, v_a1); + + __m128 layer2_chunk0 = _mm_unpacklo_ps(layer1_chunk0, layer1_chunk4); + __m128 layer2_chunk1 = _mm_unpackhi_ps(layer1_chunk0, layer1_chunk4); + __m128 layer2_chunk2 = _mm_unpacklo_ps(layer1_chunk1, layer1_chunk5); + __m128 layer2_chunk3 = _mm_unpackhi_ps(layer1_chunk1, layer1_chunk5); + __m128 layer2_chunk4 = _mm_unpacklo_ps(layer1_chunk2, layer1_chunk6); + __m128 layer2_chunk5 = _mm_unpackhi_ps(layer1_chunk2, layer1_chunk6); + __m128 layer2_chunk6 = _mm_unpacklo_ps(layer1_chunk3, layer1_chunk7); + __m128 layer2_chunk7 = _mm_unpackhi_ps(layer1_chunk3, layer1_chunk7); + + v_r0 = _mm_unpacklo_ps(layer2_chunk0, layer2_chunk4); + v_r1 = _mm_unpackhi_ps(layer2_chunk0, layer2_chunk4); + v_g0 = _mm_unpacklo_ps(layer2_chunk1, layer2_chunk5); + v_g1 = _mm_unpackhi_ps(layer2_chunk1, layer2_chunk5); + v_b0 = _mm_unpacklo_ps(layer2_chunk2, layer2_chunk6); + v_b1 = _mm_unpackhi_ps(layer2_chunk2, layer2_chunk6); + v_a0 = _mm_unpacklo_ps(layer2_chunk3, layer2_chunk7); + v_a1 = _mm_unpackhi_ps(layer2_chunk3, layer2_chunk7); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_g0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_g1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, + __m128 & v_g1, __m128 & v_b0, __m128 & v_b1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk4 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_b0, v_b1, mask_lo); + __m128 layer2_chunk5 = _mm_shuffle_ps(v_b0, v_b1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk4 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_lo); + __m128 layer1_chunk5 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_g1 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_b0 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); + v_g0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_lo); + v_b1 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_hi); +} + +inline void _mm_interleave_ps(__m128 & v_r0, __m128 & v_r1, __m128 & v_g0, __m128 & v_g1, + __m128 & v_b0, __m128 & v_b1, __m128 & v_a0, __m128 & v_a1) +{ + enum { mask_lo = _MM_SHUFFLE(2, 0, 2, 0), mask_hi = _MM_SHUFFLE(3, 1, 3, 1) }; + + __m128 layer2_chunk0 = _mm_shuffle_ps(v_r0, v_r1, mask_lo); + __m128 layer2_chunk4 = _mm_shuffle_ps(v_r0, v_r1, mask_hi); + __m128 layer2_chunk1 = _mm_shuffle_ps(v_g0, v_g1, mask_lo); + __m128 layer2_chunk5 = _mm_shuffle_ps(v_g0, v_g1, mask_hi); + __m128 layer2_chunk2 = _mm_shuffle_ps(v_b0, v_b1, mask_lo); + __m128 layer2_chunk6 = _mm_shuffle_ps(v_b0, v_b1, mask_hi); + __m128 layer2_chunk3 = _mm_shuffle_ps(v_a0, v_a1, mask_lo); + __m128 layer2_chunk7 = _mm_shuffle_ps(v_a0, v_a1, mask_hi); + + __m128 layer1_chunk0 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_lo); + __m128 layer1_chunk4 = _mm_shuffle_ps(layer2_chunk0, layer2_chunk1, mask_hi); + __m128 layer1_chunk1 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_lo); + __m128 layer1_chunk5 = _mm_shuffle_ps(layer2_chunk2, layer2_chunk3, mask_hi); + __m128 layer1_chunk2 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_lo); + __m128 layer1_chunk6 = _mm_shuffle_ps(layer2_chunk4, layer2_chunk5, mask_hi); + __m128 layer1_chunk3 = _mm_shuffle_ps(layer2_chunk6, layer2_chunk7, mask_lo); + __m128 layer1_chunk7 = _mm_shuffle_ps(layer2_chunk6, layer2_chunk7, mask_hi); + + v_r0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_lo); + v_b0 = _mm_shuffle_ps(layer1_chunk0, layer1_chunk1, mask_hi); + v_r1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_lo); + v_b1 = _mm_shuffle_ps(layer1_chunk2, layer1_chunk3, mask_hi); + v_g0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_lo); + v_a0 = _mm_shuffle_ps(layer1_chunk4, layer1_chunk5, mask_hi); + v_g1 = _mm_shuffle_ps(layer1_chunk6, layer1_chunk7, mask_lo); + v_a1 = _mm_shuffle_ps(layer1_chunk6, layer1_chunk7, mask_hi); +} + +#endif // CV_SSE2 + +//! @} + +#endif //OPENCV_CORE_SSE_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/traits.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/traits.hpp new file mode 100644 index 0000000..6cb10f4 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/traits.hpp @@ -0,0 +1,397 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TRAITS_HPP +#define OPENCV_CORE_TRAITS_HPP + +#include "opencv2/core/cvdef.h" + +namespace cv +{ + +//#define OPENCV_TRAITS_ENABLE_DEPRECATED + +//! @addtogroup core_basic +//! @{ + +/** @brief Template "trait" class for OpenCV primitive data types. + +@note Deprecated. This is replaced by "single purpose" traits: traits::Type and traits::Depth + +A primitive OpenCV data type is one of unsigned char, bool, signed char, unsigned short, signed +short, int, float, double, or a tuple of values of one of these types, where all the values in the +tuple have the same type. Any primitive type from the list can be defined by an identifier in the +form CV_\{U|S|F}C(\), for example: uchar \~ CV_8UC1, 3-element +floating-point tuple \~ CV_32FC3, and so on. A universal OpenCV structure that is able to store a +single instance of such a primitive data type is Vec. Multiple instances of such a type can be +stored in a std::vector, Mat, Mat_, SparseMat, SparseMat_, or any other container that is able to +store Vec instances. + +The DataType class is basically used to provide a description of such primitive data types without +adding any fields or methods to the corresponding classes (and it is actually impossible to add +anything to primitive C/C++ data types). This technique is known in C++ as class traits. It is not +DataType itself that is used but its specialized versions, such as: +@code + template<> class DataType + { + typedef uchar value_type; + typedef int work_type; + typedef uchar channel_type; + enum { channel_type = CV_8U, channels = 1, fmt='u', type = CV_8U }; + }; + ... + template DataType > + { + typedef std::complex<_Tp> value_type; + typedef std::complex<_Tp> work_type; + typedef _Tp channel_type; + // DataDepth is another helper trait class + enum { depth = DataDepth<_Tp>::value, channels=2, + fmt=(channels-1)*256+DataDepth<_Tp>::fmt, + type=CV_MAKETYPE(depth, channels) }; + }; + ... +@endcode +The main purpose of this class is to convert compilation-time type information to an +OpenCV-compatible data type identifier, for example: +@code + // allocates a 30x40 floating-point matrix + Mat A(30, 40, DataType::type); + + Mat B = Mat_ >(3, 3); + // the statement below will print 6, 2 , that is depth == CV_64F, channels == 2 + cout << B.depth() << ", " << B.channels() << endl; +@endcode +So, such traits are used to tell OpenCV which data type you are working with, even if such a type is +not native to OpenCV. For example, the matrix B initialization above is compiled because OpenCV +defines the proper specialized template class DataType\ \> . This mechanism is also +useful (and used in OpenCV this way) for generic algorithms implementations. + +@note Default values were dropped to stop confusing developers about using of unsupported types (see #7599) +*/ +template class DataType +{ +public: +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + typedef _Tp value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 1, + depth = -1, + channels = 1, + fmt = 0, + type = CV_MAKETYPE(depth, channels) + }; +#endif +}; + +template<> class DataType +{ +public: + typedef bool value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8U, + channels = 1, + fmt = (int)'u', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef uchar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8U, + channels = 1, + fmt = (int)'u', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef schar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8S, + channels = 1, + fmt = (int)'c', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef schar value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_8S, + channels = 1, + fmt = (int)'c', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef ushort value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_16U, + channels = 1, + fmt = (int)'w', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef short value_type; + typedef int work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_16S, + channels = 1, + fmt = (int)'s', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef int value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_32S, + channels = 1, + fmt = (int)'i', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef float value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_32F, + channels = 1, + fmt = (int)'f', + type = CV_MAKETYPE(depth, channels) + }; +}; + +template<> class DataType +{ +public: + typedef double value_type; + typedef value_type work_type; + typedef value_type channel_type; + typedef value_type vec_type; + enum { generic_type = 0, + depth = CV_64F, + channels = 1, + fmt = (int)'d', + type = CV_MAKETYPE(depth, channels) + }; +}; + + +/** @brief A helper class for cv::DataType + +The class is specialized for each fundamental numerical data type supported by OpenCV. It provides +DataDepth::value constant. +*/ +template class DataDepth +{ +public: + enum + { + value = DataType<_Tp>::depth, + fmt = DataType<_Tp>::fmt + }; +}; + + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + +template class TypeDepth +{ +#ifdef OPENCV_TRAITS_ENABLE_LEGACY_DEFAULTS + enum { depth = CV_USRTYPE1 }; + typedef void value_type; +#endif +}; + +template<> class TypeDepth +{ + enum { depth = CV_8U }; + typedef uchar value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_8S }; + typedef schar value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_16U }; + typedef ushort value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_16S }; + typedef short value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_32S }; + typedef int value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_32F }; + typedef float value_type; +}; + +template<> class TypeDepth +{ + enum { depth = CV_64F }; + typedef double value_type; +}; + +#endif + +//! @} + +namespace traits { + +namespace internal { +#define CV_CREATE_MEMBER_CHECK(X) \ +template class CheckMember_##X { \ + struct Fallback { int X; }; \ + struct Derived : T, Fallback { }; \ + template struct Check; \ + typedef char CV_NO[1]; \ + typedef char CV_YES[2]; \ + template static CV_NO & func(Check *); \ + template static CV_YES & func(...); \ +public: \ + typedef CheckMember_##X type; \ + enum { value = sizeof(func(0)) == sizeof(CV_YES) }; \ +}; + +CV_CREATE_MEMBER_CHECK(fmt) +CV_CREATE_MEMBER_CHECK(type) + +} // namespace internal + + +template +struct Depth +{ enum { value = DataType::depth }; }; + +template +struct Type +{ enum { value = DataType::type }; }; + +/** Similar to traits::Type but has value = -1 in case of unknown type (instead of compiler error) */ +template >::value > +struct SafeType {}; + +template +struct SafeType +{ enum { value = -1 }; }; + +template +struct SafeType +{ enum { value = Type::value }; }; + + +template >::value > +struct SafeFmt {}; + +template +struct SafeFmt +{ enum { fmt = 0 }; }; + +template +struct SafeFmt +{ enum { fmt = DataType::fmt }; }; + + +} // namespace + +} // cv + +#endif // OPENCV_CORE_TRAITS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/types.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/types.hpp new file mode 100644 index 0000000..4d04bef --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/types.hpp @@ -0,0 +1,2391 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TYPES_HPP +#define OPENCV_CORE_TYPES_HPP + +#ifndef __cplusplus +# error types.hpp header must be compiled as C++ +#endif + +#include +#include +#include +#include + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/matx.hpp" + +namespace cv +{ + +//! @addtogroup core_basic +//! @{ + +//////////////////////////////// Complex ////////////////////////////// + +/** @brief A complex number class. + + The template class is similar and compatible with std::complex, however it provides slightly + more convenient access to the real and imaginary parts using through the simple field access, as opposite + to std::complex::real() and std::complex::imag(). +*/ +template class Complex +{ +public: + + //! default constructor + Complex(); + Complex( _Tp _re, _Tp _im = 0 ); + + //! conversion to another data type + template operator Complex() const; + //! conjugation + Complex conj() const; + + _Tp re, im; //< the real and the imaginary parts +}; + +typedef Complex Complexf; +typedef Complex Complexd; + +template class DataType< Complex<_Tp> > +{ +public: + typedef Complex<_Tp> value_type; + typedef value_type work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Complex<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Complex<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + + +//////////////////////////////// Point_ //////////////////////////////// + +/** @brief Template class for 2D points specified by its coordinates `x` and `y`. + +An instance of the class is interchangeable with C structures, CvPoint and CvPoint2D32f . There is +also a cast operator to convert point coordinates to the specified type. The conversion from +floating-point coordinates to integer coordinates is done by rounding. Commonly, the conversion +uses this operation for each of the coordinates. Besides the class members listed in the +declaration above, the following operations on points are implemented: +@code + pt1 = pt2 + pt3; + pt1 = pt2 - pt3; + pt1 = pt2 * a; + pt1 = a * pt2; + pt1 = pt2 / a; + pt1 += pt2; + pt1 -= pt2; + pt1 *= a; + pt1 /= a; + double value = norm(pt); // L2 norm + pt1 == pt2; + pt1 != pt2; +@endcode +For your convenience, the following type aliases are defined: +@code + typedef Point_ Point2i; + typedef Point2i Point; + typedef Point_ Point2f; + typedef Point_ Point2d; +@endcode +Example: +@code + Point2f a(0.3f, 0.f), b(0.f, 0.4f); + Point pt = (a + b)*10.f; + cout << pt.x << ", " << pt.y << endl; +@endcode +*/ +template class Point_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Point_(); + Point_(_Tp _x, _Tp _y); + Point_(const Point_& pt); + Point_(const Size_<_Tp>& sz); + Point_(const Vec<_Tp, 2>& v); + + Point_& operator = (const Point_& pt); + //! conversion to another data type + template operator Point_<_Tp2>() const; + + //! conversion to the old-style C structures + operator Vec<_Tp, 2>() const; + + //! dot product + _Tp dot(const Point_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point_& pt) const; + //! cross-product + double cross(const Point_& pt) const; + //! checks whether the point is inside the specified rectangle + bool inside(const Rect_<_Tp>& r) const; + _Tp x; //!< x coordinate of the point + _Tp y; //!< y coordinate of the point +}; + +typedef Point_ Point2i; +typedef Point_ Point2l; +typedef Point_ Point2f; +typedef Point_ Point2d; +typedef Point2i Point; + +template class DataType< Point_<_Tp> > +{ +public: + typedef Point_<_Tp> value_type; + typedef Point_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Point_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Point_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + + +//////////////////////////////// Point3_ //////////////////////////////// + +/** @brief Template class for 3D points specified by its coordinates `x`, `y` and `z`. + +An instance of the class is interchangeable with the C structure CvPoint2D32f . Similarly to +Point_ , the coordinates of 3D points can be converted to another type. The vector arithmetic and +comparison operations are also supported. + +The following Point3_\<\> aliases are available: +@code + typedef Point3_ Point3i; + typedef Point3_ Point3f; + typedef Point3_ Point3d; +@endcode +@see cv::Point3i, cv::Point3f and cv::Point3d +*/ +template class Point3_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Point3_(); + Point3_(_Tp _x, _Tp _y, _Tp _z); + Point3_(const Point3_& pt); + explicit Point3_(const Point_<_Tp>& pt); + Point3_(const Vec<_Tp, 3>& v); + + Point3_& operator = (const Point3_& pt); + //! conversion to another data type + template operator Point3_<_Tp2>() const; + //! conversion to cv::Vec<> +#if OPENCV_ABI_COMPATIBILITY > 300 + template operator Vec<_Tp2, 3>() const; +#else + operator Vec<_Tp, 3>() const; +#endif + + //! dot product + _Tp dot(const Point3_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point3_& pt) const; + //! cross product of the 2 3D points + Point3_ cross(const Point3_& pt) const; + _Tp x; //!< x coordinate of the 3D point + _Tp y; //!< y coordinate of the 3D point + _Tp z; //!< z coordinate of the 3D point +}; + +typedef Point3_ Point3i; +typedef Point3_ Point3f; +typedef Point3_ Point3d; + +template class DataType< Point3_<_Tp> > +{ +public: + typedef Point3_<_Tp> value_type; + typedef Point3_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 3, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Point3_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Point3_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 3) }; }; +} // namespace + +//////////////////////////////// Size_ //////////////////////////////// + +/** @brief Template class for specifying the size of an image or rectangle. + +The class includes two members called width and height. The structure can be converted to and from +the old OpenCV structures CvSize and CvSize2D32f . The same set of arithmetic and comparison +operations as for Point_ is available. + +OpenCV defines the following Size_\<\> aliases: +@code + typedef Size_ Size2i; + typedef Size2i Size; + typedef Size_ Size2f; +@endcode +*/ +template class Size_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Size_(); + Size_(_Tp _width, _Tp _height); + Size_(const Size_& sz); + Size_(const Point_<_Tp>& pt); + + Size_& operator = (const Size_& sz); + //! the area (width*height) + _Tp area() const; + //! true if empty + bool empty() const; + + //! conversion of another data type. + template operator Size_<_Tp2>() const; + + _Tp width; //!< the width + _Tp height; //!< the height +}; + +typedef Size_ Size2i; +typedef Size_ Size2l; +typedef Size_ Size2f; +typedef Size_ Size2d; +typedef Size2i Size; + +template class DataType< Size_<_Tp> > +{ +public: + typedef Size_<_Tp> value_type; + typedef Size_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Size_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Size_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 2) }; }; +} // namespace + +//////////////////////////////// Rect_ //////////////////////////////// + +/** @brief Template class for 2D rectangles + +described by the following parameters: +- Coordinates of the top-left corner. This is a default interpretation of Rect_::x and Rect_::y + in OpenCV. Though, in your algorithms you may count x and y from the bottom-left corner. +- Rectangle width and height. + +OpenCV typically assumes that the top and left boundary of the rectangle are inclusive, while the +right and bottom boundaries are not. For example, the method Rect_::contains returns true if + +\f[x \leq pt.x < x+width, + y \leq pt.y < y+height\f] + +Virtually every loop over an image ROI in OpenCV (where ROI is specified by Rect_\ ) is +implemented as: +@code + for(int y = roi.y; y < roi.y + roi.height; y++) + for(int x = roi.x; x < roi.x + roi.width; x++) + { + // ... + } +@endcode +In addition to the class members, the following operations on rectangles are implemented: +- \f$\texttt{rect} = \texttt{rect} \pm \texttt{point}\f$ (shifting a rectangle by a certain offset) +- \f$\texttt{rect} = \texttt{rect} \pm \texttt{size}\f$ (expanding or shrinking a rectangle by a + certain amount) +- rect += point, rect -= point, rect += size, rect -= size (augmenting operations) +- rect = rect1 & rect2 (rectangle intersection) +- rect = rect1 | rect2 (minimum area rectangle containing rect1 and rect2 ) +- rect &= rect1, rect |= rect1 (and the corresponding augmenting operations) +- rect == rect1, rect != rect1 (rectangle comparison) + +This is an example how the partial ordering on rectangles can be established (rect1 \f$\subseteq\f$ +rect2): +@code + template inline bool + operator <= (const Rect_<_Tp>& r1, const Rect_<_Tp>& r2) + { + return (r1 & r2) == r1; + } +@endcode +For your convenience, the Rect_\<\> alias is available: cv::Rect +*/ +template class Rect_ +{ +public: + typedef _Tp value_type; + + //! default constructor + Rect_(); + Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height); + Rect_(const Rect_& r); + Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz); + Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2); + + Rect_& operator = ( const Rect_& r ); + //! the top-left corner + Point_<_Tp> tl() const; + //! the bottom-right corner + Point_<_Tp> br() const; + + //! size (width, height) of the rectangle + Size_<_Tp> size() const; + //! area (width*height) of the rectangle + _Tp area() const; + //! true if empty + bool empty() const; + + //! conversion to another data type + template operator Rect_<_Tp2>() const; + + //! checks whether the rectangle contains the point + bool contains(const Point_<_Tp>& pt) const; + + _Tp x; //!< x coordinate of the top-left corner + _Tp y; //!< y coordinate of the top-left corner + _Tp width; //!< width of the rectangle + _Tp height; //!< height of the rectangle +}; + +typedef Rect_ Rect2i; +typedef Rect_ Rect2f; +typedef Rect_ Rect2d; +typedef Rect2i Rect; + +template class DataType< Rect_<_Tp> > +{ +public: + typedef Rect_<_Tp> value_type; + typedef Rect_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 4, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Rect_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Rect_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 4) }; }; +} // namespace + +///////////////////////////// RotatedRect ///////////////////////////// + +/** @brief The class represents rotated (i.e. not up-right) rectangles on a plane. + +Each rectangle is specified by the center point (mass center), length of each side (represented by +#Size2f structure) and the rotation angle in degrees. + +The sample below demonstrates how to use RotatedRect: +@snippet snippets/core_various.cpp RotatedRect_demo +![image](pics/rotatedrect.png) + +@sa CamShift, fitEllipse, minAreaRect, CvBox2D +*/ +class CV_EXPORTS RotatedRect +{ +public: + //! default constructor + RotatedRect(); + /** full constructor + @param center The rectangle mass center. + @param size Width and height of the rectangle. + @param angle The rotation angle in a clockwise direction. When the angle is 0, 90, 180, 270 etc., + the rectangle becomes an up-right rectangle. + */ + RotatedRect(const Point2f& center, const Size2f& size, float angle); + /** + Any 3 end points of the RotatedRect. They must be given in order (either clockwise or + anticlockwise). + */ + RotatedRect(const Point2f& point1, const Point2f& point2, const Point2f& point3); + + /** returns 4 vertices of the rectangle + @param pts The points array for storing rectangle vertices. The order is bottomLeft, topLeft, topRight, bottomRight. + */ + void points(Point2f pts[]) const; + //! returns the minimal up-right integer rectangle containing the rotated rectangle + Rect boundingRect() const; + //! returns the minimal (exact) floating point rectangle containing the rotated rectangle, not intended for use with images + Rect_ boundingRect2f() const; + //! returns the rectangle mass center + Point2f center; + //! returns width and height of the rectangle + Size2f size; + //! returns the rotation angle. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle. + float angle; +}; + +template<> class DataType< RotatedRect > +{ +public: + typedef RotatedRect value_type; + typedef value_type work_type; + typedef float channel_type; + + enum { generic_type = 0, + channels = (int)sizeof(value_type)/sizeof(channel_type), // 5 + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< RotatedRect > { enum { value = Depth::value }; }; +template<> +struct Type< RotatedRect > { enum { value = CV_MAKETYPE(Depth::value, (int)sizeof(RotatedRect)/sizeof(float)) }; }; +} // namespace + + +//////////////////////////////// Range ///////////////////////////////// + +/** @brief Template class specifying a continuous subsequence (slice) of a sequence. + +The class is used to specify a row or a column span in a matrix ( Mat ) and for many other purposes. +Range(a,b) is basically the same as a:b in Matlab or a..b in Python. As in Python, start is an +inclusive left boundary of the range and end is an exclusive right boundary of the range. Such a +half-opened interval is usually denoted as \f$[start,end)\f$ . + +The static method Range::all() returns a special variable that means "the whole sequence" or "the +whole range", just like " : " in Matlab or " ... " in Python. All the methods and functions in +OpenCV that take Range support this special Range::all() value. But, of course, in case of your own +custom processing, you will probably have to check and handle it explicitly: +@code + void my_function(..., const Range& r, ....) + { + if(r == Range::all()) { + // process all the data + } + else { + // process [r.start, r.end) + } + } +@endcode +*/ +class CV_EXPORTS Range +{ +public: + Range(); + Range(int _start, int _end); + int size() const; + bool empty() const; + static Range all(); + + int start, end; +}; + +template<> class DataType +{ +public: + typedef Range value_type; + typedef value_type work_type; + typedef int channel_type; + + enum { generic_type = 0, + channels = 2, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< Range > { enum { value = Depth::value }; }; +template<> +struct Type< Range > { enum { value = CV_MAKETYPE(Depth::value, 2) }; }; +} // namespace + + +//////////////////////////////// Scalar_ /////////////////////////////// + +/** @brief Template class for a 4-element vector derived from Vec. + +Being derived from Vec\<_Tp, 4\> , Scalar\_ and Scalar can be used just as typical 4-element +vectors. In addition, they can be converted to/from CvScalar . The type Scalar is widely used in +OpenCV to pass pixel values. +*/ +template class Scalar_ : public Vec<_Tp, 4> +{ +public: + //! default constructor + Scalar_(); + Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0); + Scalar_(_Tp v0); + + template + Scalar_(const Vec<_Tp2, cn>& v); + + //! returns a scalar with all elements set to v0 + static Scalar_<_Tp> all(_Tp v0); + + //! conversion to another data type + template operator Scalar_() const; + + //! per-element product + Scalar_<_Tp> mul(const Scalar_<_Tp>& a, double scale=1 ) const; + + //! returns (v0, -v1, -v2, -v3) + Scalar_<_Tp> conj() const; + + //! returns true iff v1 == v2 == v3 == 0 + bool isReal() const; +}; + +typedef Scalar_ Scalar; + +template class DataType< Scalar_<_Tp> > +{ +public: + typedef Scalar_<_Tp> value_type; + typedef Scalar_::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + channels = 4, + fmt = traits::SafeFmt::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template +struct Depth< Scalar_<_Tp> > { enum { value = Depth<_Tp>::value }; }; +template +struct Type< Scalar_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 4) }; }; +} // namespace + + +/////////////////////////////// KeyPoint //////////////////////////////// + +/** @brief Data structure for salient point detectors. + +The class instance stores a keypoint, i.e. a point feature found by one of many available keypoint +detectors, such as Harris corner detector, #FAST, %StarDetector, %SURF, %SIFT etc. + +The keypoint is characterized by the 2D position, scale (proportional to the diameter of the +neighborhood that needs to be taken into account), orientation and some other parameters. The +keypoint neighborhood is then analyzed by another algorithm that builds a descriptor (usually +represented as a feature vector). The keypoints representing the same object in different images +can then be matched using %KDTree or another method. +*/ +class CV_EXPORTS_W_SIMPLE KeyPoint +{ +public: + //! the default constructor + CV_WRAP KeyPoint(); + /** + @param _pt x & y coordinates of the keypoint + @param _size keypoint diameter + @param _angle keypoint orientation + @param _response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param _octave pyramid octave in which the keypoint has been detected + @param _class_id object id + */ + KeyPoint(Point2f _pt, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1); + /** + @param x x-coordinate of the keypoint + @param y y-coordinate of the keypoint + @param _size keypoint diameter + @param _angle keypoint orientation + @param _response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param _octave pyramid octave in which the keypoint has been detected + @param _class_id object id + */ + CV_WRAP KeyPoint(float x, float y, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1); + + size_t hash() const; + + /** + This method converts vector of keypoints to vector of points or the reverse, where each keypoint is + assigned the same size and the same orientation. + + @param keypoints Keypoints obtained from any feature detection algorithm like SIFT/SURF/ORB + @param points2f Array of (x,y) coordinates of each keypoint + @param keypointIndexes Array of indexes of keypoints to be converted to points. (Acts like a mask to + convert only specified keypoints) + */ + CV_WRAP static void convert(const std::vector& keypoints, + CV_OUT std::vector& points2f, + const std::vector& keypointIndexes=std::vector()); + /** @overload + @param points2f Array of (x,y) coordinates of each keypoint + @param keypoints Keypoints obtained from any feature detection algorithm like SIFT/SURF/ORB + @param size keypoint diameter + @param response keypoint detector response on the keypoint (that is, strength of the keypoint) + @param octave pyramid octave in which the keypoint has been detected + @param class_id object id + */ + CV_WRAP static void convert(const std::vector& points2f, + CV_OUT std::vector& keypoints, + float size=1, float response=1, int octave=0, int class_id=-1); + + /** + This method computes overlap for pair of keypoints. Overlap is the ratio between area of keypoint + regions' intersection and area of keypoint regions' union (considering keypoint region as circle). + If they don't overlap, we get zero. If they coincide at same location with same size, we get 1. + @param kp1 First keypoint + @param kp2 Second keypoint + */ + CV_WRAP static float overlap(const KeyPoint& kp1, const KeyPoint& kp2); + + CV_PROP_RW Point2f pt; //!< coordinates of the keypoints + CV_PROP_RW float size; //!< diameter of the meaningful keypoint neighborhood + CV_PROP_RW float angle; //!< computed orientation of the keypoint (-1 if not applicable); + //!< it's in [0,360) degrees and measured relative to + //!< image coordinate system, ie in clockwise. + CV_PROP_RW float response; //!< the response by which the most strong keypoints have been selected. Can be used for the further sorting or subsampling + CV_PROP_RW int octave; //!< octave (pyramid layer) from which the keypoint has been extracted + CV_PROP_RW int class_id; //!< object class (if the keypoints need to be clustered by an object they belong to) +}; + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED +template<> class DataType +{ +public: + typedef KeyPoint value_type; + typedef float work_type; + typedef float channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 7 + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) + }; + + typedef Vec vec_type; +}; +#endif + + +//////////////////////////////// DMatch ///////////////////////////////// + +/** @brief Class for matching keypoint descriptors + +query descriptor index, train descriptor index, train image index, and distance between +descriptors. +*/ +class CV_EXPORTS_W_SIMPLE DMatch +{ +public: + CV_WRAP DMatch(); + CV_WRAP DMatch(int _queryIdx, int _trainIdx, float _distance); + CV_WRAP DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance); + + CV_PROP_RW int queryIdx; //!< query descriptor index + CV_PROP_RW int trainIdx; //!< train descriptor index + CV_PROP_RW int imgIdx; //!< train image index + + CV_PROP_RW float distance; + + // less is better + bool operator<(const DMatch &m) const; +}; + +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED +template<> class DataType +{ +public: + typedef DMatch value_type; + typedef int work_type; + typedef int channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 4 + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) + }; + + typedef Vec vec_type; +}; +#endif + + +///////////////////////////// TermCriteria ////////////////////////////// + +/** @brief The class defining termination criteria for iterative algorithms. + +You can initialize it by default constructor and then override any parameters, or the structure may +be fully initialized using the advanced variant of the constructor. +*/ +class CV_EXPORTS TermCriteria +{ +public: + /** + Criteria type, can be one of: COUNT, EPS or COUNT + EPS + */ + enum Type + { + COUNT=1, //!< the maximum number of iterations or elements to compute + MAX_ITER=COUNT, //!< ditto + EPS=2 //!< the desired accuracy or change in parameters at which the iterative algorithm stops + }; + + //! default constructor + TermCriteria(); + /** + @param type The type of termination criteria, one of TermCriteria::Type + @param maxCount The maximum number of iterations or elements to compute. + @param epsilon The desired accuracy or change in parameters at which the iterative algorithm stops. + */ + TermCriteria(int type, int maxCount, double epsilon); + + inline bool isValid() const + { + const bool isCount = (type & COUNT) && maxCount > 0; + const bool isEps = (type & EPS) && !cvIsNaN(epsilon); + return isCount || isEps; + } + + int type; //!< the type of termination criteria: COUNT, EPS or COUNT + EPS + int maxCount; //!< the maximum number of iterations/elements + double epsilon; //!< the desired accuracy +}; + + +//! @} core_basic + +///////////////////////// raster image moments ////////////////////////// + +//! @addtogroup imgproc_shape +//! @{ + +/** @brief struct returned by cv::moments + +The spatial moments \f$\texttt{Moments::m}_{ji}\f$ are computed as: + +\f[\texttt{m} _{ji}= \sum _{x,y} \left ( \texttt{array} (x,y) \cdot x^j \cdot y^i \right )\f] + +The central moments \f$\texttt{Moments::mu}_{ji}\f$ are computed as: + +\f[\texttt{mu} _{ji}= \sum _{x,y} \left ( \texttt{array} (x,y) \cdot (x - \bar{x} )^j \cdot (y - \bar{y} )^i \right )\f] + +where \f$(\bar{x}, \bar{y})\f$ is the mass center: + +\f[\bar{x} = \frac{\texttt{m}_{10}}{\texttt{m}_{00}} , \; \bar{y} = \frac{\texttt{m}_{01}}{\texttt{m}_{00}}\f] + +The normalized central moments \f$\texttt{Moments::nu}_{ij}\f$ are computed as: + +\f[\texttt{nu} _{ji}= \frac{\texttt{mu}_{ji}}{\texttt{m}_{00}^{(i+j)/2+1}} .\f] + +@note +\f$\texttt{mu}_{00}=\texttt{m}_{00}\f$, \f$\texttt{nu}_{00}=1\f$ +\f$\texttt{nu}_{10}=\texttt{mu}_{10}=\texttt{mu}_{01}=\texttt{mu}_{10}=0\f$ , hence the values are not +stored. + +The moments of a contour are defined in the same way but computed using the Green's formula (see +). So, due to a limited raster resolution, the moments +computed for a contour are slightly different from the moments computed for the same rasterized +contour. + +@note +Since the contour moments are computed using Green formula, you may get seemingly odd results for +contours with self-intersections, e.g. a zero area (m00) for butterfly-shaped contours. + */ +class CV_EXPORTS_W_MAP Moments +{ +public: + //! the default constructor + Moments(); + //! the full constructor + Moments(double m00, double m10, double m01, double m20, double m11, + double m02, double m30, double m21, double m12, double m03 ); + ////! the conversion from CvMoments + //Moments( const CvMoments& moments ); + ////! the conversion to CvMoments + //operator CvMoments() const; + + //! @name spatial moments + //! @{ + CV_PROP_RW double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; + //! @} + + //! @name central moments + //! @{ + CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03; + //! @} + + //! @name central normalized moments + //! @{ + CV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03; + //! @} +}; + +template<> class DataType +{ +public: + typedef Moments value_type; + typedef double work_type; + typedef double channel_type; + + enum { generic_type = 0, + channels = (int)(sizeof(value_type)/sizeof(channel_type)), // 24 + fmt = DataType::fmt + ((channels - 1) << 8) +#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED + ,depth = DataType::depth + ,type = CV_MAKETYPE(depth, channels) +#endif + }; + + typedef Vec vec_type; +}; + +namespace traits { +template<> +struct Depth< Moments > { enum { value = Depth::value }; }; +template<> +struct Type< Moments > { enum { value = CV_MAKETYPE(Depth::value, (int)(sizeof(Moments)/sizeof(double))) }; }; +} // namespace + +//! @} imgproc_shape + +//! @cond IGNORED + +///////////////////////////////////////////////////////////////////////// +///////////////////////////// Implementation //////////////////////////// +///////////////////////////////////////////////////////////////////////// + +//////////////////////////////// Complex //////////////////////////////// + +template inline +Complex<_Tp>::Complex() + : re(0), im(0) {} + +template inline +Complex<_Tp>::Complex( _Tp _re, _Tp _im ) + : re(_re), im(_im) {} + +template template inline +Complex<_Tp>::operator Complex() const +{ + return Complex(saturate_cast(re), saturate_cast(im)); +} + +template inline +Complex<_Tp> Complex<_Tp>::conj() const +{ + return Complex<_Tp>(re, -im); +} + + +template static inline +bool operator == (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return a.re == b.re && a.im == b.im; +} + +template static inline +bool operator != (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return a.re != b.re || a.im != b.im; +} + +template static inline +Complex<_Tp> operator + (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re + b.re, a.im + b.im ); +} + +template static inline +Complex<_Tp>& operator += (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a.re += b.re; a.im += b.im; + return a; +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re - b.re, a.im - b.im ); +} + +template static inline +Complex<_Tp>& operator -= (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a.re -= b.re; a.im -= b.im; + return a; +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a) +{ + return Complex<_Tp>(-a.re, -a.im); +} + +template static inline +Complex<_Tp> operator * (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + return Complex<_Tp>( a.re*b.re - a.im*b.im, a.re*b.im + a.im*b.re ); +} + +template static inline +Complex<_Tp> operator * (const Complex<_Tp>& a, _Tp b) +{ + return Complex<_Tp>( a.re*b, a.im*b ); +} + +template static inline +Complex<_Tp> operator * (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( a.re*b, a.im*b ); +} + +template static inline +Complex<_Tp> operator + (const Complex<_Tp>& a, _Tp b) +{ + return Complex<_Tp>( a.re + b, a.im ); +} + +template static inline +Complex<_Tp> operator - (const Complex<_Tp>& a, _Tp b) +{ return Complex<_Tp>( a.re - b, a.im ); } + +template static inline +Complex<_Tp> operator + (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( a.re + b, a.im ); +} + +template static inline +Complex<_Tp> operator - (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>( b - a.re, -a.im ); +} + +template static inline +Complex<_Tp>& operator += (Complex<_Tp>& a, _Tp b) +{ + a.re += b; return a; +} + +template static inline +Complex<_Tp>& operator -= (Complex<_Tp>& a, _Tp b) +{ + a.re -= b; return a; +} + +template static inline +Complex<_Tp>& operator *= (Complex<_Tp>& a, _Tp b) +{ + a.re *= b; a.im *= b; return a; +} + +template static inline +double abs(const Complex<_Tp>& a) +{ + return std::sqrt( (double)a.re*a.re + (double)a.im*a.im); +} + +template static inline +Complex<_Tp> operator / (const Complex<_Tp>& a, const Complex<_Tp>& b) +{ + double t = 1./((double)b.re*b.re + (double)b.im*b.im); + return Complex<_Tp>( (_Tp)((a.re*b.re + a.im*b.im)*t), + (_Tp)((-a.re*b.im + a.im*b.re)*t) ); +} + +template static inline +Complex<_Tp>& operator /= (Complex<_Tp>& a, const Complex<_Tp>& b) +{ + a = a / b; + return a; +} + +template static inline +Complex<_Tp> operator / (const Complex<_Tp>& a, _Tp b) +{ + _Tp t = (_Tp)1/b; + return Complex<_Tp>( a.re*t, a.im*t ); +} + +template static inline +Complex<_Tp> operator / (_Tp b, const Complex<_Tp>& a) +{ + return Complex<_Tp>(b)/a; +} + +template static inline +Complex<_Tp> operator /= (const Complex<_Tp>& a, _Tp b) +{ + _Tp t = (_Tp)1/b; + a.re *= t; a.im *= t; return a; +} + + + +//////////////////////////////// 2D Point /////////////////////////////// + +template inline +Point_<_Tp>::Point_() + : x(0), y(0) {} + +template inline +Point_<_Tp>::Point_(_Tp _x, _Tp _y) + : x(_x), y(_y) {} + +template inline +Point_<_Tp>::Point_(const Point_& pt) + : x(pt.x), y(pt.y) {} + +template inline +Point_<_Tp>::Point_(const Size_<_Tp>& sz) + : x(sz.width), y(sz.height) {} + +template inline +Point_<_Tp>::Point_(const Vec<_Tp,2>& v) + : x(v[0]), y(v[1]) {} + +template inline +Point_<_Tp>& Point_<_Tp>::operator = (const Point_& pt) +{ + x = pt.x; y = pt.y; + return *this; +} + +template template inline +Point_<_Tp>::operator Point_<_Tp2>() const +{ + return Point_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y)); +} + +template inline +Point_<_Tp>::operator Vec<_Tp, 2>() const +{ + return Vec<_Tp, 2>(x, y); +} + +template inline +_Tp Point_<_Tp>::dot(const Point_& pt) const +{ + return saturate_cast<_Tp>(x*pt.x + y*pt.y); +} + +template inline +double Point_<_Tp>::ddot(const Point_& pt) const +{ + return (double)x*(double)(pt.x) + (double)y*(double)(pt.y); +} + +template inline +double Point_<_Tp>::cross(const Point_& pt) const +{ + return (double)x*pt.y - (double)y*pt.x; +} + +template inline bool +Point_<_Tp>::inside( const Rect_<_Tp>& r ) const +{ + return r.contains(*this); +} + + +template static inline +Point_<_Tp>& operator += (Point_<_Tp>& a, const Point_<_Tp>& b) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +template static inline +Point_<_Tp>& operator -= (Point_<_Tp>& a, const Point_<_Tp>& b) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator *= (Point_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +Point_<_Tp>& operator /= (Point_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + return a; +} + +template static inline +double norm(const Point_<_Tp>& pt) +{ + return std::sqrt((double)pt.x*pt.x + (double)pt.y*pt.y); +} + +template static inline +bool operator == (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y; +} + +template static inline +bool operator != (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y; +} + +template static inline +Point_<_Tp> operator + (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x + b.x), saturate_cast<_Tp>(a.y + b.y) ); +} + +template static inline +Point_<_Tp> operator - (const Point_<_Tp>& a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x - b.x), saturate_cast<_Tp>(a.y - b.y) ); +} + +template static inline +Point_<_Tp> operator - (const Point_<_Tp>& a) +{ + return Point_<_Tp>( saturate_cast<_Tp>(-a.x), saturate_cast<_Tp>(-a.y) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, int b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (int a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, float b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (float a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Point_<_Tp>& a, double b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b) ); +} + +template static inline +Point_<_Tp> operator * (double a, const Point_<_Tp>& b) +{ + return Point_<_Tp>( saturate_cast<_Tp>(b.x*a), saturate_cast<_Tp>(b.y*a) ); +} + +template static inline +Point_<_Tp> operator * (const Matx<_Tp, 2, 2>& a, const Point_<_Tp>& b) +{ + Matx<_Tp, 2, 1> tmp = a * Vec<_Tp,2>(b.x, b.y); + return Point_<_Tp>(tmp.val[0], tmp.val[1]); +} + +template static inline +Point3_<_Tp> operator * (const Matx<_Tp, 3, 3>& a, const Point_<_Tp>& b) +{ + Matx<_Tp, 3, 1> tmp = a * Vec<_Tp,3>(b.x, b.y, 1); + return Point3_<_Tp>(tmp.val[0], tmp.val[1], tmp.val[2]); +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, int b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, float b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point_<_Tp> operator / (const Point_<_Tp>& a, double b) +{ + Point_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + + +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); + +template<> inline int normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline int64 normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline float normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.dot(pt); } + +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } + + + +//////////////////////////////// 3D Point /////////////////////////////// + +template inline +Point3_<_Tp>::Point3_() + : x(0), y(0), z(0) {} + +template inline +Point3_<_Tp>::Point3_(_Tp _x, _Tp _y, _Tp _z) + : x(_x), y(_y), z(_z) {} + +template inline +Point3_<_Tp>::Point3_(const Point3_& pt) + : x(pt.x), y(pt.y), z(pt.z) {} + +template inline +Point3_<_Tp>::Point3_(const Point_<_Tp>& pt) + : x(pt.x), y(pt.y), z(_Tp()) {} + +template inline +Point3_<_Tp>::Point3_(const Vec<_Tp, 3>& v) + : x(v[0]), y(v[1]), z(v[2]) {} + +template template inline +Point3_<_Tp>::operator Point3_<_Tp2>() const +{ + return Point3_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y), saturate_cast<_Tp2>(z)); +} + +#if OPENCV_ABI_COMPATIBILITY > 300 +template template inline +Point3_<_Tp>::operator Vec<_Tp2, 3>() const +{ + return Vec<_Tp2, 3>(x, y, z); +} +#else +template inline +Point3_<_Tp>::operator Vec<_Tp, 3>() const +{ + return Vec<_Tp, 3>(x, y, z); +} +#endif + +template inline +Point3_<_Tp>& Point3_<_Tp>::operator = (const Point3_& pt) +{ + x = pt.x; y = pt.y; z = pt.z; + return *this; +} + +template inline +_Tp Point3_<_Tp>::dot(const Point3_& pt) const +{ + return saturate_cast<_Tp>(x*pt.x + y*pt.y + z*pt.z); +} + +template inline +double Point3_<_Tp>::ddot(const Point3_& pt) const +{ + return (double)x*pt.x + (double)y*pt.y + (double)z*pt.z; +} + +template inline +Point3_<_Tp> Point3_<_Tp>::cross(const Point3_<_Tp>& pt) const +{ + return Point3_<_Tp>(y*pt.z - z*pt.y, z*pt.x - x*pt.z, x*pt.y - y*pt.x); +} + + +template static inline +Point3_<_Tp>& operator += (Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + a.x += b.x; + a.y += b.y; + a.z += b.z; + return a; +} + +template static inline +Point3_<_Tp>& operator -= (Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator *= (Point3_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x * b); + a.y = saturate_cast<_Tp>(a.y * b); + a.z = saturate_cast<_Tp>(a.z * b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, int b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, float b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +Point3_<_Tp>& operator /= (Point3_<_Tp>& a, double b) +{ + a.x = saturate_cast<_Tp>(a.x / b); + a.y = saturate_cast<_Tp>(a.y / b); + a.z = saturate_cast<_Tp>(a.z / b); + return a; +} + +template static inline +double norm(const Point3_<_Tp>& pt) +{ + return std::sqrt((double)pt.x*pt.x + (double)pt.y*pt.y + (double)pt.z*pt.z); +} + +template static inline +bool operator == (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +template static inline +bool operator != (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y || a.z != b.z; +} + +template static inline +Point3_<_Tp> operator + (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x + b.x), saturate_cast<_Tp>(a.y + b.y), saturate_cast<_Tp>(a.z + b.z)); +} + +template static inline +Point3_<_Tp> operator - (const Point3_<_Tp>& a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x - b.x), saturate_cast<_Tp>(a.y - b.y), saturate_cast<_Tp>(a.z - b.z)); +} + +template static inline +Point3_<_Tp> operator - (const Point3_<_Tp>& a) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(-a.x), saturate_cast<_Tp>(-a.y), saturate_cast<_Tp>(-a.z) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, int b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x*b), saturate_cast<_Tp>(a.y*b), saturate_cast<_Tp>(a.z*b) ); +} + +template static inline +Point3_<_Tp> operator * (int a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, float b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x * b), saturate_cast<_Tp>(a.y * b), saturate_cast<_Tp>(a.z * b) ); +} + +template static inline +Point3_<_Tp> operator * (float a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Point3_<_Tp>& a, double b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(a.x * b), saturate_cast<_Tp>(a.y * b), saturate_cast<_Tp>(a.z * b) ); +} + +template static inline +Point3_<_Tp> operator * (double a, const Point3_<_Tp>& b) +{ + return Point3_<_Tp>( saturate_cast<_Tp>(b.x * a), saturate_cast<_Tp>(b.y * a), saturate_cast<_Tp>(b.z * a) ); +} + +template static inline +Point3_<_Tp> operator * (const Matx<_Tp, 3, 3>& a, const Point3_<_Tp>& b) +{ + Matx<_Tp, 3, 1> tmp = a * Vec<_Tp,3>(b.x, b.y, b.z); + return Point3_<_Tp>(tmp.val[0], tmp.val[1], tmp.val[2]); +} + +template static inline +Matx<_Tp, 4, 1> operator * (const Matx<_Tp, 4, 4>& a, const Point3_<_Tp>& b) +{ + return a * Matx<_Tp, 4, 1>(b.x, b.y, b.z, 1); +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, int b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, float b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Point3_<_Tp> operator / (const Point3_<_Tp>& a, double b) +{ + Point3_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + + + +////////////////////////////////// Size ///////////////////////////////// + +template inline +Size_<_Tp>::Size_() + : width(0), height(0) {} + +template inline +Size_<_Tp>::Size_(_Tp _width, _Tp _height) + : width(_width), height(_height) {} + +template inline +Size_<_Tp>::Size_(const Size_& sz) + : width(sz.width), height(sz.height) {} + +template inline +Size_<_Tp>::Size_(const Point_<_Tp>& pt) + : width(pt.x), height(pt.y) {} + +template template inline +Size_<_Tp>::operator Size_<_Tp2>() const +{ + return Size_<_Tp2>(saturate_cast<_Tp2>(width), saturate_cast<_Tp2>(height)); +} + +template inline +Size_<_Tp>& Size_<_Tp>::operator = (const Size_<_Tp>& sz) +{ + width = sz.width; height = sz.height; + return *this; +} + +template inline +_Tp Size_<_Tp>::area() const +{ + const _Tp result = width * height; + CV_DbgAssert(!std::numeric_limits<_Tp>::is_integer + || width == 0 || result / width == height); // make sure the result fits in the return value + return result; +} + +template inline +bool Size_<_Tp>::empty() const +{ + return width <= 0 || height <= 0; +} + + +template static inline +Size_<_Tp>& operator *= (Size_<_Tp>& a, _Tp b) +{ + a.width *= b; + a.height *= b; + return a; +} + +template static inline +Size_<_Tp> operator * (const Size_<_Tp>& a, _Tp b) +{ + Size_<_Tp> tmp(a); + tmp *= b; + return tmp; +} + +template static inline +Size_<_Tp>& operator /= (Size_<_Tp>& a, _Tp b) +{ + a.width /= b; + a.height /= b; + return a; +} + +template static inline +Size_<_Tp> operator / (const Size_<_Tp>& a, _Tp b) +{ + Size_<_Tp> tmp(a); + tmp /= b; + return tmp; +} + +template static inline +Size_<_Tp>& operator += (Size_<_Tp>& a, const Size_<_Tp>& b) +{ + a.width += b.width; + a.height += b.height; + return a; +} + +template static inline +Size_<_Tp> operator + (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + Size_<_Tp> tmp(a); + tmp += b; + return tmp; +} + +template static inline +Size_<_Tp>& operator -= (Size_<_Tp>& a, const Size_<_Tp>& b) +{ + a.width -= b.width; + a.height -= b.height; + return a; +} + +template static inline +Size_<_Tp> operator - (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + Size_<_Tp> tmp(a); + tmp -= b; + return tmp; +} + +template static inline +bool operator == (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + return a.width == b.width && a.height == b.height; +} + +template static inline +bool operator != (const Size_<_Tp>& a, const Size_<_Tp>& b) +{ + return !(a == b); +} + + + +////////////////////////////////// Rect ///////////////////////////////// + +template inline +Rect_<_Tp>::Rect_() + : x(0), y(0), width(0), height(0) {} + +template inline +Rect_<_Tp>::Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height) + : x(_x), y(_y), width(_width), height(_height) {} + +template inline +Rect_<_Tp>::Rect_(const Rect_<_Tp>& r) + : x(r.x), y(r.y), width(r.width), height(r.height) {} + +template inline +Rect_<_Tp>::Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz) + : x(org.x), y(org.y), width(sz.width), height(sz.height) {} + +template inline +Rect_<_Tp>::Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2) +{ + x = std::min(pt1.x, pt2.x); + y = std::min(pt1.y, pt2.y); + width = std::max(pt1.x, pt2.x) - x; + height = std::max(pt1.y, pt2.y) - y; +} + +template inline +Rect_<_Tp>& Rect_<_Tp>::operator = ( const Rect_<_Tp>& r ) +{ + x = r.x; + y = r.y; + width = r.width; + height = r.height; + return *this; +} + +template inline +Point_<_Tp> Rect_<_Tp>::tl() const +{ + return Point_<_Tp>(x,y); +} + +template inline +Point_<_Tp> Rect_<_Tp>::br() const +{ + return Point_<_Tp>(x + width, y + height); +} + +template inline +Size_<_Tp> Rect_<_Tp>::size() const +{ + return Size_<_Tp>(width, height); +} + +template inline +_Tp Rect_<_Tp>::area() const +{ + const _Tp result = width * height; + CV_DbgAssert(!std::numeric_limits<_Tp>::is_integer + || width == 0 || result / width == height); // make sure the result fits in the return value + return result; +} + +template inline +bool Rect_<_Tp>::empty() const +{ + return width <= 0 || height <= 0; +} + +template template inline +Rect_<_Tp>::operator Rect_<_Tp2>() const +{ + return Rect_<_Tp2>(saturate_cast<_Tp2>(x), saturate_cast<_Tp2>(y), saturate_cast<_Tp2>(width), saturate_cast<_Tp2>(height)); +} + +template inline +bool Rect_<_Tp>::contains(const Point_<_Tp>& pt) const +{ + return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height; +} + + +template static inline +Rect_<_Tp>& operator += ( Rect_<_Tp>& a, const Point_<_Tp>& b ) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +template static inline +Rect_<_Tp>& operator -= ( Rect_<_Tp>& a, const Point_<_Tp>& b ) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +template static inline +Rect_<_Tp>& operator += ( Rect_<_Tp>& a, const Size_<_Tp>& b ) +{ + a.width += b.width; + a.height += b.height; + return a; +} + +template static inline +Rect_<_Tp>& operator -= ( Rect_<_Tp>& a, const Size_<_Tp>& b ) +{ + const _Tp width = a.width - b.width; + const _Tp height = a.height - b.height; + CV_DbgAssert(width >= 0 && height >= 0); + a.width = width; + a.height = height; + return a; +} + +template static inline +Rect_<_Tp>& operator &= ( Rect_<_Tp>& a, const Rect_<_Tp>& b ) +{ + _Tp x1 = std::max(a.x, b.x); + _Tp y1 = std::max(a.y, b.y); + a.width = std::min(a.x + a.width, b.x + b.width) - x1; + a.height = std::min(a.y + a.height, b.y + b.height) - y1; + a.x = x1; + a.y = y1; + if( a.width <= 0 || a.height <= 0 ) + a = Rect(); + return a; +} + +template static inline +Rect_<_Tp>& operator |= ( Rect_<_Tp>& a, const Rect_<_Tp>& b ) +{ + if (a.empty()) { + a = b; + } + else if (!b.empty()) { + _Tp x1 = std::min(a.x, b.x); + _Tp y1 = std::min(a.y, b.y); + a.width = std::max(a.x + a.width, b.x + b.width) - x1; + a.height = std::max(a.y + a.height, b.y + b.height) - y1; + a.x = x1; + a.y = y1; + } + return a; +} + +template static inline +bool operator == (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; +} + +template static inline +bool operator != (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + return a.x != b.x || a.y != b.y || a.width != b.width || a.height != b.height; +} + +template static inline +Rect_<_Tp> operator + (const Rect_<_Tp>& a, const Point_<_Tp>& b) +{ + return Rect_<_Tp>( a.x + b.x, a.y + b.y, a.width, a.height ); +} + +template static inline +Rect_<_Tp> operator - (const Rect_<_Tp>& a, const Point_<_Tp>& b) +{ + return Rect_<_Tp>( a.x - b.x, a.y - b.y, a.width, a.height ); +} + +template static inline +Rect_<_Tp> operator + (const Rect_<_Tp>& a, const Size_<_Tp>& b) +{ + return Rect_<_Tp>( a.x, a.y, a.width + b.width, a.height + b.height ); +} + +template static inline +Rect_<_Tp> operator - (const Rect_<_Tp>& a, const Size_<_Tp>& b) +{ + const _Tp width = a.width - b.width; + const _Tp height = a.height - b.height; + CV_DbgAssert(width >= 0 && height >= 0); + return Rect_<_Tp>( a.x, a.y, width, height ); +} + +template static inline +Rect_<_Tp> operator & (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + Rect_<_Tp> c = a; + return c &= b; +} + +template static inline +Rect_<_Tp> operator | (const Rect_<_Tp>& a, const Rect_<_Tp>& b) +{ + Rect_<_Tp> c = a; + return c |= b; +} + +/** + * @brief measure dissimilarity between two sample sets + * + * computes the complement of the Jaccard Index as described in . + * For rectangles this reduces to computing the intersection over the union. + */ +template static inline +double jaccardDistance(const Rect_<_Tp>& a, const Rect_<_Tp>& b) { + _Tp Aa = a.area(); + _Tp Ab = b.area(); + + if ((Aa + Ab) <= std::numeric_limits<_Tp>::epsilon()) { + // jaccard_index = 1 -> distance = 0 + return 0.0; + } + + double Aab = (a & b).area(); + // distance = 1 - jaccard_index + return 1.0 - Aab / (Aa + Ab - Aab); +} + +////////////////////////////// RotatedRect ////////////////////////////// + +inline +RotatedRect::RotatedRect() + : center(), size(), angle(0) {} + +inline +RotatedRect::RotatedRect(const Point2f& _center, const Size2f& _size, float _angle) + : center(_center), size(_size), angle(_angle) {} + + + +///////////////////////////////// Range ///////////////////////////////// + +inline +Range::Range() + : start(0), end(0) {} + +inline +Range::Range(int _start, int _end) + : start(_start), end(_end) {} + +inline +int Range::size() const +{ + return end - start; +} + +inline +bool Range::empty() const +{ + return start == end; +} + +inline +Range Range::all() +{ + return Range(INT_MIN, INT_MAX); +} + + +static inline +bool operator == (const Range& r1, const Range& r2) +{ + return r1.start == r2.start && r1.end == r2.end; +} + +static inline +bool operator != (const Range& r1, const Range& r2) +{ + return !(r1 == r2); +} + +static inline +bool operator !(const Range& r) +{ + return r.start == r.end; +} + +static inline +Range operator & (const Range& r1, const Range& r2) +{ + Range r(std::max(r1.start, r2.start), std::min(r1.end, r2.end)); + r.end = std::max(r.end, r.start); + return r; +} + +static inline +Range& operator &= (Range& r1, const Range& r2) +{ + r1 = r1 & r2; + return r1; +} + +static inline +Range operator + (const Range& r1, int delta) +{ + return Range(r1.start + delta, r1.end + delta); +} + +static inline +Range operator + (int delta, const Range& r1) +{ + return Range(r1.start + delta, r1.end + delta); +} + +static inline +Range operator - (const Range& r1, int delta) +{ + return r1 + (-delta); +} + + + +///////////////////////////////// Scalar //////////////////////////////// + +template inline +Scalar_<_Tp>::Scalar_() +{ + this->val[0] = this->val[1] = this->val[2] = this->val[3] = 0; +} + +template inline +Scalar_<_Tp>::Scalar_(_Tp v0, _Tp v1, _Tp v2, _Tp v3) +{ + this->val[0] = v0; + this->val[1] = v1; + this->val[2] = v2; + this->val[3] = v3; +} + +template template inline +Scalar_<_Tp>::Scalar_(const Vec<_Tp2, cn>& v) +{ + int i; + for( i = 0; i < (cn < 4 ? cn : 4); i++ ) + this->val[i] = cv::saturate_cast<_Tp>(v.val[i]); + for( ; i < 4; i++ ) + this->val[i] = 0; +} + +template inline +Scalar_<_Tp>::Scalar_(_Tp v0) +{ + this->val[0] = v0; + this->val[1] = this->val[2] = this->val[3] = 0; +} + +template inline +Scalar_<_Tp> Scalar_<_Tp>::all(_Tp v0) +{ + return Scalar_<_Tp>(v0, v0, v0, v0); +} + + +template inline +Scalar_<_Tp> Scalar_<_Tp>::mul(const Scalar_<_Tp>& a, double scale ) const +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(this->val[0] * a.val[0] * scale), + saturate_cast<_Tp>(this->val[1] * a.val[1] * scale), + saturate_cast<_Tp>(this->val[2] * a.val[2] * scale), + saturate_cast<_Tp>(this->val[3] * a.val[3] * scale)); +} + +template inline +Scalar_<_Tp> Scalar_<_Tp>::conj() const +{ + return Scalar_<_Tp>(saturate_cast<_Tp>( this->val[0]), + saturate_cast<_Tp>(-this->val[1]), + saturate_cast<_Tp>(-this->val[2]), + saturate_cast<_Tp>(-this->val[3])); +} + +template inline +bool Scalar_<_Tp>::isReal() const +{ + return this->val[1] == 0 && this->val[2] == 0 && this->val[3] == 0; +} + + +template template inline +Scalar_<_Tp>::operator Scalar_() const +{ + return Scalar_(saturate_cast(this->val[0]), + saturate_cast(this->val[1]), + saturate_cast(this->val[2]), + saturate_cast(this->val[3])); +} + + +template static inline +Scalar_<_Tp>& operator += (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a.val[0] += b.val[0]; + a.val[1] += b.val[1]; + a.val[2] += b.val[2]; + a.val[3] += b.val[3]; + return a; +} + +template static inline +Scalar_<_Tp>& operator -= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a.val[0] -= b.val[0]; + a.val[1] -= b.val[1]; + a.val[2] -= b.val[2]; + a.val[3] -= b.val[3]; + return a; +} + +template static inline +Scalar_<_Tp>& operator *= ( Scalar_<_Tp>& a, _Tp v ) +{ + a.val[0] *= v; + a.val[1] *= v; + a.val[2] *= v; + a.val[3] *= v; + return a; +} + +template static inline +bool operator == ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b ) +{ + return a.val[0] == b.val[0] && a.val[1] == b.val[1] && + a.val[2] == b.val[2] && a.val[3] == b.val[3]; +} + +template static inline +bool operator != ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b ) +{ + return a.val[0] != b.val[0] || a.val[1] != b.val[1] || + a.val[2] != b.val[2] || a.val[3] != b.val[3]; +} + +template static inline +Scalar_<_Tp> operator + (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(a.val[0] + b.val[0], + a.val[1] + b.val[1], + a.val[2] + b.val[2], + a.val[3] + b.val[3]); +} + +template static inline +Scalar_<_Tp> operator - (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(a.val[0] - b.val[0]), + saturate_cast<_Tp>(a.val[1] - b.val[1]), + saturate_cast<_Tp>(a.val[2] - b.val[2]), + saturate_cast<_Tp>(a.val[3] - b.val[3])); +} + +template static inline +Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, _Tp alpha) +{ + return Scalar_<_Tp>(a.val[0] * alpha, + a.val[1] * alpha, + a.val[2] * alpha, + a.val[3] * alpha); +} + +template static inline +Scalar_<_Tp> operator * (_Tp alpha, const Scalar_<_Tp>& a) +{ + return a*alpha; +} + +template static inline +Scalar_<_Tp> operator - (const Scalar_<_Tp>& a) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(-a.val[0]), + saturate_cast<_Tp>(-a.val[1]), + saturate_cast<_Tp>(-a.val[2]), + saturate_cast<_Tp>(-a.val[3])); +} + + +template static inline +Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return Scalar_<_Tp>(saturate_cast<_Tp>(a[0]*b[0] - a[1]*b[1] - a[2]*b[2] - a[3]*b[3]), + saturate_cast<_Tp>(a[0]*b[1] + a[1]*b[0] + a[2]*b[3] - a[3]*b[2]), + saturate_cast<_Tp>(a[0]*b[2] - a[1]*b[3] + a[2]*b[0] + a[3]*b[1]), + saturate_cast<_Tp>(a[0]*b[3] + a[1]*b[2] - a[2]*b[1] + a[3]*b[0])); +} + +template static inline +Scalar_<_Tp>& operator *= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a = a * b; + return a; +} + +template static inline +Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, _Tp alpha) +{ + return Scalar_<_Tp>(a.val[0] / alpha, + a.val[1] / alpha, + a.val[2] / alpha, + a.val[3] / alpha); +} + +template static inline +Scalar_ operator / (const Scalar_& a, float alpha) +{ + float s = 1 / alpha; + return Scalar_(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s); +} + +template static inline +Scalar_ operator / (const Scalar_& a, double alpha) +{ + double s = 1 / alpha; + return Scalar_(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s); +} + +template static inline +Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, _Tp alpha) +{ + a = a / alpha; + return a; +} + +template static inline +Scalar_<_Tp> operator / (_Tp a, const Scalar_<_Tp>& b) +{ + _Tp s = a / (b[0]*b[0] + b[1]*b[1] + b[2]*b[2] + b[3]*b[3]); + return b.conj() * s; +} + +template static inline +Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + return a * ((_Tp)1 / b); +} + +template static inline +Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b) +{ + a = a / b; + return a; +} + +template static inline +Scalar operator * (const Matx<_Tp, 4, 4>& a, const Scalar& b) +{ + Matx c((Matx)a, b, Matx_MatMulOp()); + return reinterpret_cast(c); +} + +template<> inline +Scalar operator * (const Matx& a, const Scalar& b) +{ + Matx c(a, b, Matx_MatMulOp()); + return reinterpret_cast(c); +} + + + +//////////////////////////////// KeyPoint /////////////////////////////// + +inline +KeyPoint::KeyPoint() + : pt(0,0), size(0), angle(-1), response(0), octave(0), class_id(-1) {} + +inline +KeyPoint::KeyPoint(Point2f _pt, float _size, float _angle, float _response, int _octave, int _class_id) + : pt(_pt), size(_size), angle(_angle), response(_response), octave(_octave), class_id(_class_id) {} + +inline +KeyPoint::KeyPoint(float x, float y, float _size, float _angle, float _response, int _octave, int _class_id) + : pt(x, y), size(_size), angle(_angle), response(_response), octave(_octave), class_id(_class_id) {} + + + +///////////////////////////////// DMatch //////////////////////////////// + +inline +DMatch::DMatch() + : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} + +inline +DMatch::DMatch(int _queryIdx, int _trainIdx, float _distance) + : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} + +inline +DMatch::DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance) + : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} + +inline +bool DMatch::operator < (const DMatch &m) const +{ + return distance < m.distance; +} + + + +////////////////////////////// TermCriteria ///////////////////////////// + +inline +TermCriteria::TermCriteria() + : type(0), maxCount(0), epsilon(0) {} + +inline +TermCriteria::TermCriteria(int _type, int _maxCount, double _epsilon) + : type(_type), maxCount(_maxCount), epsilon(_epsilon) {} + +//! @endcond + +} // cv + +#endif //OPENCV_CORE_TYPES_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/types_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/core/types_c.h new file mode 100644 index 0000000..eddbe7d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/types_c.h @@ -0,0 +1,2141 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_TYPES_H +#define OPENCV_CORE_TYPES_H + +#ifdef CV__ENABLE_C_API_CTORS // invalid C API ctors (must be removed) +#if defined(_WIN32) && !defined(CV__SKIP_MESSAGE_MALFORMED_C_API_CTORS) +#error "C API ctors don't work on Win32: https://github.com/opencv/opencv/issues/15990" +#endif +#endif + +//#define CV__VALIDATE_UNUNITIALIZED_VARS 1 // C++11 & GCC only + +#ifdef __cplusplus + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#define CV_STRUCT_INITIALIZER {0,} +#else +#if defined(__GNUC__) && __GNUC__ == 4 // GCC 4.x warns on "= {}" initialization, fixed in GCC 5.0 +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif +#define CV_STRUCT_INITIALIZER {} +#endif + +#else +#define CV_STRUCT_INITIALIZER {0} +#endif + + +#ifdef HAVE_IPL +# ifndef __IPL_H__ +# if defined _WIN32 +# include +# else +# include +# endif +# endif +#elif defined __IPL_H__ +# define HAVE_IPL +#endif + +#include "opencv2/core/cvdef.h" + +#ifndef SKIP_INCLUDES +#include +#include +#include +#include +#endif // SKIP_INCLUDES + +#if defined _WIN32 +# define CV_CDECL __cdecl +# define CV_STDCALL __stdcall +#else +# define CV_CDECL +# define CV_STDCALL +#endif + +#ifndef CV_DEFAULT +# ifdef __cplusplus +# define CV_DEFAULT(val) = val +# else +# define CV_DEFAULT(val) +# endif +#endif + +#ifndef CV_EXTERN_C_FUNCPTR +# ifdef __cplusplus +# define CV_EXTERN_C_FUNCPTR(x) extern "C" { typedef x; } +# else +# define CV_EXTERN_C_FUNCPTR(x) typedef x +# endif +#endif + +#ifndef CVAPI +# define CVAPI(rettype) CV_EXTERN_C CV_EXPORTS rettype CV_CDECL +#endif + +#ifndef CV_IMPL +# define CV_IMPL CV_EXTERN_C +#endif + +#ifdef __cplusplus +# include "opencv2/core.hpp" +#endif + +/** @addtogroup core_c + @{ +*/ + +/** @brief This is the "metatype" used *only* as a function parameter. + +It denotes that the function accepts arrays of multiple types, such as IplImage*, CvMat* or even +CvSeq* sometimes. The particular array type is determined at runtime by analyzing the first 4 +bytes of the header. In C++ interface the role of CvArr is played by InputArray and OutputArray. + */ +typedef void CvArr; + +typedef int CVStatus; + +/** @see cv::Error::Code */ +enum { + CV_StsOk= 0, /**< everything is ok */ + CV_StsBackTrace= -1, /**< pseudo error for back trace */ + CV_StsError= -2, /**< unknown /unspecified error */ + CV_StsInternal= -3, /**< internal error (bad state) */ + CV_StsNoMem= -4, /**< insufficient memory */ + CV_StsBadArg= -5, /**< function arg/param is bad */ + CV_StsBadFunc= -6, /**< unsupported function */ + CV_StsNoConv= -7, /**< iter. didn't converge */ + CV_StsAutoTrace= -8, /**< tracing */ + CV_HeaderIsNull= -9, /**< image header is NULL */ + CV_BadImageSize= -10, /**< image size is invalid */ + CV_BadOffset= -11, /**< offset is invalid */ + CV_BadDataPtr= -12, /**/ + CV_BadStep= -13, /**< image step is wrong, this may happen for a non-continuous matrix */ + CV_BadModelOrChSeq= -14, /**/ + CV_BadNumChannels= -15, /**< bad number of channels, for example, some functions accept only single channel matrices */ + CV_BadNumChannel1U= -16, /**/ + CV_BadDepth= -17, /**< input image depth is not supported by the function */ + CV_BadAlphaChannel= -18, /**/ + CV_BadOrder= -19, /**< number of dimensions is out of range */ + CV_BadOrigin= -20, /**< incorrect input origin */ + CV_BadAlign= -21, /**< incorrect input align */ + CV_BadCallBack= -22, /**/ + CV_BadTileSize= -23, /**/ + CV_BadCOI= -24, /**< input COI is not supported */ + CV_BadROISize= -25, /**< incorrect input roi */ + CV_MaskIsTiled= -26, /**/ + CV_StsNullPtr= -27, /**< null pointer */ + CV_StsVecLengthErr= -28, /**< incorrect vector length */ + CV_StsFilterStructContentErr= -29, /**< incorrect filter structure content */ + CV_StsKernelStructContentErr= -30, /**< incorrect transform kernel content */ + CV_StsFilterOffsetErr= -31, /**< incorrect filter offset value */ + CV_StsBadSize= -201, /**< the input/output structure size is incorrect */ + CV_StsDivByZero= -202, /**< division by zero */ + CV_StsInplaceNotSupported= -203, /**< in-place operation is not supported */ + CV_StsObjectNotFound= -204, /**< request can't be completed */ + CV_StsUnmatchedFormats= -205, /**< formats of input/output arrays differ */ + CV_StsBadFlag= -206, /**< flag is wrong or not supported */ + CV_StsBadPoint= -207, /**< bad CvPoint */ + CV_StsBadMask= -208, /**< bad format of mask (neither 8uC1 nor 8sC1)*/ + CV_StsUnmatchedSizes= -209, /**< sizes of input/output structures do not match */ + CV_StsUnsupportedFormat= -210, /**< the data format/type is not supported by the function*/ + CV_StsOutOfRange= -211, /**< some of parameters are out of range */ + CV_StsParseError= -212, /**< invalid syntax/structure of the parsed file */ + CV_StsNotImplemented= -213, /**< the requested function/feature is not implemented */ + CV_StsBadMemBlock= -214, /**< an allocated block has been corrupted */ + CV_StsAssert= -215, /**< assertion failed */ + CV_GpuNotSupported= -216, /**< no CUDA support */ + CV_GpuApiCallError= -217, /**< GPU API call error */ + CV_OpenGlNotSupported= -218, /**< no OpenGL support */ + CV_OpenGlApiCallError= -219, /**< OpenGL API call error */ + CV_OpenCLApiCallError= -220, /**< OpenCL API call error */ + CV_OpenCLDoubleNotSupported= -221, + CV_OpenCLInitError= -222, /**< OpenCL initialization error */ + CV_OpenCLNoAMDBlasFft= -223 +}; + +/****************************************************************************************\ +* Common macros and inline functions * +\****************************************************************************************/ + +#define CV_SWAP(a,b,t) ((t) = (a), (a) = (b), (b) = (t)) + +/** min & max without jumps */ +#define CV_IMIN(a, b) ((a) ^ (((a)^(b)) & (((a) < (b)) - 1))) + +#define CV_IMAX(a, b) ((a) ^ (((a)^(b)) & (((a) > (b)) - 1))) + +/** absolute value without jumps */ +#ifndef __cplusplus +# define CV_IABS(a) (((a) ^ ((a) < 0 ? -1 : 0)) - ((a) < 0 ? -1 : 0)) +#else +# define CV_IABS(a) abs(a) +#endif +#define CV_CMP(a,b) (((a) > (b)) - ((a) < (b))) +#define CV_SIGN(a) CV_CMP((a),0) + +#define cvInvSqrt(value) ((float)(1./sqrt(value))) +#define cvSqrt(value) ((float)sqrt(value)) + + +/*************** Random number generation *******************/ + +typedef uint64 CvRNG; + +#define CV_RNG_COEFF 4164903690U + +/** @brief Initializes a random number generator state. + +The function initializes a random number generator and returns the state. The pointer to the state +can be then passed to the cvRandInt, cvRandReal and cvRandArr functions. In the current +implementation a multiply-with-carry generator is used. +@param seed 64-bit value used to initiate a random sequence +@sa the C++ class RNG replaced CvRNG. + */ +CV_INLINE CvRNG cvRNG( int64 seed CV_DEFAULT(-1)) +{ + CvRNG rng = seed ? (uint64)seed : (uint64)(int64)-1; + return rng; +} + +/** @brief Returns a 32-bit unsigned integer and updates RNG. + +The function returns a uniformly-distributed random 32-bit unsigned integer and updates the RNG +state. It is similar to the rand() function from the C runtime library, except that OpenCV functions +always generates a 32-bit random number, regardless of the platform. +@param rng CvRNG state initialized by cvRNG. + */ +CV_INLINE unsigned cvRandInt( CvRNG* rng ) +{ + uint64 temp = *rng; + temp = (uint64)(unsigned)temp*CV_RNG_COEFF + (temp >> 32); + *rng = temp; + return (unsigned)temp; +} + +/** @brief Returns a floating-point random number and updates RNG. + +The function returns a uniformly-distributed random floating-point number between 0 and 1 (1 is not +included). +@param rng RNG state initialized by cvRNG + */ +CV_INLINE double cvRandReal( CvRNG* rng ) +{ + return cvRandInt(rng)*2.3283064365386962890625e-10 /* 2^-32 */; +} + +/****************************************************************************************\ +* Image type (IplImage) * +\****************************************************************************************/ + +#ifndef HAVE_IPL + +/* + * The following definitions (until #endif) + * is an extract from IPL headers. + * Copyright (c) 1995 Intel Corporation. + */ +#define IPL_DEPTH_SIGN 0x80000000 + +#define IPL_DEPTH_1U 1 +#define IPL_DEPTH_8U 8 +#define IPL_DEPTH_16U 16 +#define IPL_DEPTH_32F 32 + +#define IPL_DEPTH_8S (IPL_DEPTH_SIGN| 8) +#define IPL_DEPTH_16S (IPL_DEPTH_SIGN|16) +#define IPL_DEPTH_32S (IPL_DEPTH_SIGN|32) + +#define IPL_DATA_ORDER_PIXEL 0 +#define IPL_DATA_ORDER_PLANE 1 + +#define IPL_ORIGIN_TL 0 +#define IPL_ORIGIN_BL 1 + +#define IPL_ALIGN_4BYTES 4 +#define IPL_ALIGN_8BYTES 8 +#define IPL_ALIGN_16BYTES 16 +#define IPL_ALIGN_32BYTES 32 + +#define IPL_ALIGN_DWORD IPL_ALIGN_4BYTES +#define IPL_ALIGN_QWORD IPL_ALIGN_8BYTES + +#define IPL_BORDER_CONSTANT 0 +#define IPL_BORDER_REPLICATE 1 +#define IPL_BORDER_REFLECT 2 +#define IPL_BORDER_WRAP 3 + +#ifdef __cplusplus +typedef struct _IplImage IplImage; +CV_EXPORTS _IplImage cvIplImage(const cv::Mat& m); +#endif + +/** The IplImage is taken from the Intel Image Processing Library, in which the format is native. OpenCV +only supports a subset of possible IplImage formats, as outlined in the parameter list above. + +In addition to the above restrictions, OpenCV handles ROIs differently. OpenCV functions require +that the image size or ROI size of all source and destination images match exactly. On the other +hand, the Intel Image Processing Library processes the area of intersection between the source and +destination images (or ROIs), allowing them to vary independently. +*/ +typedef struct +_IplImage +{ + int nSize; /**< sizeof(IplImage) */ + int ID; /**< version (=0)*/ + int nChannels; /**< Most of OpenCV functions support 1,2,3 or 4 channels */ + int alphaChannel; /**< Ignored by OpenCV */ + int depth; /**< Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, + IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */ + char colorModel[4]; /**< Ignored by OpenCV */ + char channelSeq[4]; /**< ditto */ + int dataOrder; /**< 0 - interleaved color channels, 1 - separate color channels. + cvCreateImage can only create interleaved images */ + int origin; /**< 0 - top-left origin, + 1 - bottom-left origin (Windows bitmaps style). */ + int align; /**< Alignment of image rows (4 or 8). + OpenCV ignores it and uses widthStep instead. */ + int width; /**< Image width in pixels. */ + int height; /**< Image height in pixels. */ + struct _IplROI *roi; /**< Image ROI. If NULL, the whole image is selected. */ + struct _IplImage *maskROI; /**< Must be NULL. */ + void *imageId; /**< " " */ + struct _IplTileInfo *tileInfo; /**< " " */ + int imageSize; /**< Image data size in bytes + (==image->height*image->widthStep + in case of interleaved data)*/ + char *imageData; /**< Pointer to aligned image data. */ + int widthStep; /**< Size of aligned image row in bytes. */ + int BorderMode[4]; /**< Ignored by OpenCV. */ + int BorderConst[4]; /**< Ditto. */ + char *imageDataOrigin; /**< Pointer to very origin of image data + (not necessarily aligned) - + needed for correct deallocation */ + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + _IplImage() {} + _IplImage(const cv::Mat& m) { *this = cvIplImage(m); } +#endif +} +IplImage; + +CV_INLINE IplImage cvIplImage() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + IplImage self = CV_STRUCT_INITIALIZER; self.nSize = sizeof(IplImage); return self; +#else + return _IplImage(); +#endif +} + +typedef struct _IplTileInfo IplTileInfo; + +typedef struct _IplROI +{ + int coi; /**< 0 - no COI (all channels are selected), 1 - 0th channel is selected ...*/ + int xOffset; + int yOffset; + int width; + int height; +} +IplROI; + +typedef struct _IplConvKernel +{ + int nCols; + int nRows; + int anchorX; + int anchorY; + int *values; + int nShiftR; +} +IplConvKernel; + +typedef struct _IplConvKernelFP +{ + int nCols; + int nRows; + int anchorX; + int anchorY; + float *values; +} +IplConvKernelFP; + +#define IPL_IMAGE_HEADER 1 +#define IPL_IMAGE_DATA 2 +#define IPL_IMAGE_ROI 4 + +#endif/*HAVE_IPL*/ + +/** extra border mode */ +#define IPL_BORDER_REFLECT_101 4 +#define IPL_BORDER_TRANSPARENT 5 + +#define IPL_IMAGE_MAGIC_VAL ((int)sizeof(IplImage)) +#define CV_TYPE_NAME_IMAGE "opencv-image" + +#define CV_IS_IMAGE_HDR(img) \ + ((img) != NULL && ((const IplImage*)(img))->nSize == sizeof(IplImage)) + +#define CV_IS_IMAGE(img) \ + (CV_IS_IMAGE_HDR(img) && ((IplImage*)img)->imageData != NULL) + +/** for storing double-precision + floating point data in IplImage's */ +#define IPL_DEPTH_64F 64 + +/** get reference to pixel at (col,row), + for multi-channel images (col) should be multiplied by number of channels */ +#define CV_IMAGE_ELEM( image, elemtype, row, col ) \ + (((elemtype*)((image)->imageData + (image)->widthStep*(row)))[(col)]) + +/****************************************************************************************\ +* Matrix type (CvMat) * +\****************************************************************************************/ + +#define CV_AUTO_STEP 0x7fffffff +#define CV_WHOLE_ARR cvSlice( 0, 0x3fffffff ) + +#define CV_MAGIC_MASK 0xFFFF0000 +#define CV_MAT_MAGIC_VAL 0x42420000 +#define CV_TYPE_NAME_MAT "opencv-matrix" + +#ifdef __cplusplus +typedef struct CvMat CvMat; +CV_INLINE CvMat cvMat(const cv::Mat& m); +#endif + +/** Matrix elements are stored row by row. Element (i, j) (i - 0-based row index, j - 0-based column +index) of a matrix can be retrieved or modified using CV_MAT_ELEM macro: + + uchar pixval = CV_MAT_ELEM(grayimg, uchar, i, j) + CV_MAT_ELEM(cameraMatrix, float, 0, 2) = image.width*0.5f; + +To access multiple-channel matrices, you can use +CV_MAT_ELEM(matrix, type, i, j\*nchannels + channel_idx). + +@deprecated CvMat is now obsolete; consider using Mat instead. + */ +typedef struct CvMat +{ + int type; + int step; + + /* for internal use only */ + int* refcount; + int hdr_refcount; + + union + { + uchar* ptr; + short* s; + int* i; + float* fl; + double* db; + } data; + +#ifdef __cplusplus + union + { + int rows; + int height; + }; + + union + { + int cols; + int width; + }; +#else + int rows; + int cols; +#endif + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvMat() {} + CvMat(const cv::Mat& m) { *this = cvMat(m); } +#endif +} +CvMat; + + +#define CV_IS_MAT_HDR(mat) \ + ((mat) != NULL && \ + (((const CvMat*)(mat))->type & CV_MAGIC_MASK) == CV_MAT_MAGIC_VAL && \ + ((const CvMat*)(mat))->cols > 0 && ((const CvMat*)(mat))->rows > 0) + +#define CV_IS_MAT_HDR_Z(mat) \ + ((mat) != NULL && \ + (((const CvMat*)(mat))->type & CV_MAGIC_MASK) == CV_MAT_MAGIC_VAL && \ + ((const CvMat*)(mat))->cols >= 0 && ((const CvMat*)(mat))->rows >= 0) + +#define CV_IS_MAT(mat) \ + (CV_IS_MAT_HDR(mat) && ((const CvMat*)(mat))->data.ptr != NULL) + +#define CV_IS_MASK_ARR(mat) \ + (((mat)->type & (CV_MAT_TYPE_MASK & ~CV_8SC1)) == 0) + +#define CV_ARE_TYPES_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_TYPE_MASK) == 0) + +#define CV_ARE_CNS_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_CN_MASK) == 0) + +#define CV_ARE_DEPTHS_EQ(mat1, mat2) \ + ((((mat1)->type ^ (mat2)->type) & CV_MAT_DEPTH_MASK) == 0) + +#define CV_ARE_SIZES_EQ(mat1, mat2) \ + ((mat1)->rows == (mat2)->rows && (mat1)->cols == (mat2)->cols) + +#define CV_IS_MAT_CONST(mat) \ + (((mat)->rows|(mat)->cols) == 1) + +#define IPL2CV_DEPTH(depth) \ + ((((CV_8U)+(CV_16U<<4)+(CV_32F<<8)+(CV_64F<<16)+(CV_8S<<20)+ \ + (CV_16S<<24)+(CV_32S<<28)) >> ((((depth) & 0xF0) >> 2) + \ + (((depth) & IPL_DEPTH_SIGN) ? 20 : 0))) & 15) + +/** Inline constructor. No data is allocated internally!!! + * (Use together with cvCreateData, or use cvCreateMat instead to + * get a matrix with allocated data): + */ +CV_INLINE CvMat cvMat( int rows, int cols, int type, void* data CV_DEFAULT(NULL)) +{ + CvMat m; + + assert( (unsigned)CV_MAT_DEPTH(type) <= CV_64F ); + type = CV_MAT_TYPE(type); + m.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type; + m.cols = cols; + m.rows = rows; + m.step = m.cols*CV_ELEM_SIZE(type); + m.data.ptr = (uchar*)data; + m.refcount = NULL; + m.hdr_refcount = 0; + + return m; +} + +#ifdef __cplusplus + +CV_INLINE CvMat cvMat(const cv::Mat& m) +{ + CvMat self; + CV_DbgAssert(m.dims <= 2); + self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data); + self.step = (int)m.step[0]; + self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG); + return self; +} +CV_INLINE CvMat cvMat() +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMat self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMat(); +#endif +} +CV_INLINE CvMat cvMat(const CvMat& m) +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMat self = CV_STRUCT_INITIALIZER; memcpy(&self, &m, sizeof(self)); return self; +#else + return CvMat(m); +#endif +} + +#endif // __cplusplus + + +#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \ + (assert( (unsigned)(row) < (unsigned)(mat).rows && \ + (unsigned)(col) < (unsigned)(mat).cols ), \ + (mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col)) + +#define CV_MAT_ELEM_PTR( mat, row, col ) \ + CV_MAT_ELEM_PTR_FAST( mat, row, col, CV_ELEM_SIZE((mat).type) ) + +#define CV_MAT_ELEM( mat, elemtype, row, col ) \ + (*(elemtype*)CV_MAT_ELEM_PTR_FAST( mat, row, col, sizeof(elemtype))) + +/** @brief Returns the particular element of single-channel floating-point matrix. + +The function is a fast replacement for cvGetReal2D in the case of single-channel floating-point +matrices. It is faster because it is inline, it does fewer checks for array type and array element +type, and it checks for the row and column ranges only in debug mode. +@param mat Input matrix +@param row The zero-based index of row +@param col The zero-based index of column + */ +CV_INLINE double cvmGet( const CvMat* mat, int row, int col ) +{ + int type; + + type = CV_MAT_TYPE(mat->type); + assert( (unsigned)row < (unsigned)mat->rows && + (unsigned)col < (unsigned)mat->cols ); + + if( type == CV_32FC1 ) + return ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; + else + { + assert( type == CV_64FC1 ); + return ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; + } +} + +/** @brief Sets a specific element of a single-channel floating-point matrix. + +The function is a fast replacement for cvSetReal2D in the case of single-channel floating-point +matrices. It is faster because it is inline, it does fewer checks for array type and array element +type, and it checks for the row and column ranges only in debug mode. +@param mat The matrix +@param row The zero-based index of row +@param col The zero-based index of column +@param value The new value of the matrix element + */ +CV_INLINE void cvmSet( CvMat* mat, int row, int col, double value ) +{ + int type; + type = CV_MAT_TYPE(mat->type); + assert( (unsigned)row < (unsigned)mat->rows && + (unsigned)col < (unsigned)mat->cols ); + + if( type == CV_32FC1 ) + ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = (float)value; + else + { + assert( type == CV_64FC1 ); + ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = value; + } +} + + +CV_INLINE int cvIplDepth( int type ) +{ + int depth = CV_MAT_DEPTH(type); + return CV_ELEM_SIZE1(depth)*8 | (depth == CV_8S || depth == CV_16S || + depth == CV_32S ? IPL_DEPTH_SIGN : 0); +} + + +/****************************************************************************************\ +* Multi-dimensional dense array (CvMatND) * +\****************************************************************************************/ + +#define CV_MATND_MAGIC_VAL 0x42430000 +#define CV_TYPE_NAME_MATND "opencv-nd-matrix" + +#define CV_MAX_DIM 32 + +#ifdef __cplusplus +typedef struct CvMatND CvMatND; +CV_EXPORTS CvMatND cvMatND(const cv::Mat& m); +#endif + +/** + @deprecated consider using cv::Mat instead + */ +typedef struct +CvMatND +{ + int type; + int dims; + + int* refcount; + int hdr_refcount; + + union + { + uchar* ptr; + float* fl; + double* db; + int* i; + short* s; + } data; + + struct + { + int size; + int step; + } + dim[CV_MAX_DIM]; + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvMatND() {} + CvMatND(const cv::Mat& m) { *this = cvMatND(m); } +#endif +} +CvMatND; + + +CV_INLINE CvMatND cvMatND() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvMatND self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMatND(); +#endif +} + +#define CV_IS_MATND_HDR(mat) \ + ((mat) != NULL && (((const CvMatND*)(mat))->type & CV_MAGIC_MASK) == CV_MATND_MAGIC_VAL) + +#define CV_IS_MATND(mat) \ + (CV_IS_MATND_HDR(mat) && ((const CvMatND*)(mat))->data.ptr != NULL) + + +/****************************************************************************************\ +* Multi-dimensional sparse array (CvSparseMat) * +\****************************************************************************************/ + +#define CV_SPARSE_MAT_MAGIC_VAL 0x42440000 +#define CV_TYPE_NAME_SPARSE_MAT "opencv-sparse-matrix" + +struct CvSet; + +typedef struct CvSparseMat +{ + int type; + int dims; + int* refcount; + int hdr_refcount; + + struct CvSet* heap; + void** hashtable; + int hashsize; + int valoffset; + int idxoffset; + int size[CV_MAX_DIM]; + +#ifdef __cplusplus + CV_EXPORTS void copyToSparseMat(cv::SparseMat& m) const; +#endif +} +CvSparseMat; + +#ifdef __cplusplus +CV_EXPORTS CvSparseMat* cvCreateSparseMat(const cv::SparseMat& m); +#endif + +#define CV_IS_SPARSE_MAT_HDR(mat) \ + ((mat) != NULL && \ + (((const CvSparseMat*)(mat))->type & CV_MAGIC_MASK) == CV_SPARSE_MAT_MAGIC_VAL) + +#define CV_IS_SPARSE_MAT(mat) \ + CV_IS_SPARSE_MAT_HDR(mat) + +/**************** iteration through a sparse array *****************/ + +typedef struct CvSparseNode +{ + unsigned hashval; + struct CvSparseNode* next; +} +CvSparseNode; + +typedef struct CvSparseMatIterator +{ + CvSparseMat* mat; + CvSparseNode* node; + int curidx; +} +CvSparseMatIterator; + +#define CV_NODE_VAL(mat,node) ((void*)((uchar*)(node) + (mat)->valoffset)) +#define CV_NODE_IDX(mat,node) ((int*)((uchar*)(node) + (mat)->idxoffset)) + +/****************************************************************************************\ +* Histogram * +\****************************************************************************************/ + +typedef int CvHistType; + +#define CV_HIST_MAGIC_VAL 0x42450000 +#define CV_HIST_UNIFORM_FLAG (1 << 10) + +/** indicates whether bin ranges are set already or not */ +#define CV_HIST_RANGES_FLAG (1 << 11) + +#define CV_HIST_ARRAY 0 +#define CV_HIST_SPARSE 1 +#define CV_HIST_TREE CV_HIST_SPARSE + +/** should be used as a parameter only, + it turns to CV_HIST_UNIFORM_FLAG of hist->type */ +#define CV_HIST_UNIFORM 1 + +typedef struct CvHistogram +{ + int type; + CvArr* bins; + float thresh[CV_MAX_DIM][2]; /**< For uniform histograms. */ + float** thresh2; /**< For non-uniform histograms. */ + CvMatND mat; /**< Embedded matrix header for array histograms. */ +} +CvHistogram; + +#define CV_IS_HIST( hist ) \ + ((hist) != NULL && \ + (((CvHistogram*)(hist))->type & CV_MAGIC_MASK) == CV_HIST_MAGIC_VAL && \ + (hist)->bins != NULL) + +#define CV_IS_UNIFORM_HIST( hist ) \ + (((hist)->type & CV_HIST_UNIFORM_FLAG) != 0) + +#define CV_IS_SPARSE_HIST( hist ) \ + CV_IS_SPARSE_MAT((hist)->bins) + +#define CV_HIST_HAS_RANGES( hist ) \ + (((hist)->type & CV_HIST_RANGES_FLAG) != 0) + +/****************************************************************************************\ +* Other supplementary data type definitions * +\****************************************************************************************/ + +/*************************************** CvRect *****************************************/ +/** @sa Rect_ */ +typedef struct CvRect +{ + int x; + int y; + int width; + int height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvRect() __attribute__(( warning("Non-initialized variable") )) {}; + template CvRect(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 4); + x = y = width = height = 0; + if (list.size() == 4) + { + x = list.begin()[0]; y = list.begin()[1]; width = list.begin()[2]; height = list.begin()[3]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvRect(int _x = 0, int _y = 0, int w = 0, int h = 0): x(_x), y(_y), width(w), height(h) {} + template + CvRect(const cv::Rect_<_Tp>& r): x(cv::saturate_cast(r.x)), y(cv::saturate_cast(r.y)), width(cv::saturate_cast(r.width)), height(cv::saturate_cast(r.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Rect_<_Tp>() const { return cv::Rect_<_Tp>((_Tp)x, (_Tp)y, (_Tp)width, (_Tp)height); } +#endif +} +CvRect; + +/** constructs CvRect structure. */ +CV_INLINE CvRect cvRect( int x, int y, int width, int height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvRect r = {x, y, width, height}; +#else + CvRect r(x, y , width, height); +#endif + return r; +} +#ifdef __cplusplus +CV_INLINE CvRect cvRect(const cv::Rect& rc) { return cvRect(rc.x, rc.y, rc.width, rc.height); } +#endif + +CV_INLINE IplROI cvRectToROI( CvRect rect, int coi ) +{ + IplROI roi; + roi.xOffset = rect.x; + roi.yOffset = rect.y; + roi.width = rect.width; + roi.height = rect.height; + roi.coi = coi; + + return roi; +} + + +CV_INLINE CvRect cvROIToRect( IplROI roi ) +{ + return cvRect( roi.xOffset, roi.yOffset, roi.width, roi.height ); +} + +/*********************************** CvTermCriteria *************************************/ + +#define CV_TERMCRIT_ITER 1 +#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER +#define CV_TERMCRIT_EPS 2 + +/** @sa TermCriteria + */ +typedef struct CvTermCriteria +{ + int type; /**< may be combination of + CV_TERMCRIT_ITER + CV_TERMCRIT_EPS */ + int max_iter; + double epsilon; +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvTermCriteria(int _type = 0, int _iter = 0, double _eps = 0) : type(_type), max_iter(_iter), epsilon(_eps) {} + CvTermCriteria(const cv::TermCriteria& t) : type(t.type), max_iter(t.maxCount), epsilon(t.epsilon) {} +#endif +#ifdef __cplusplus + operator cv::TermCriteria() const { return cv::TermCriteria(type, max_iter, epsilon); } +#endif +} +CvTermCriteria; + +CV_INLINE CvTermCriteria cvTermCriteria( int type, int max_iter, double epsilon ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvTermCriteria t = { type, max_iter, (float)epsilon}; +#else + CvTermCriteria t(type, max_iter, epsilon); +#endif + return t; +} +#ifdef __cplusplus +CV_INLINE CvTermCriteria cvTermCriteria(const cv::TermCriteria& t) { return cvTermCriteria(t.type, t.maxCount, t.epsilon); } +#endif + + +/******************************* CvPoint and variants ***********************************/ + +typedef struct CvPoint +{ + int x; + int y; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint(int _x = 0, int _y = 0): x(_x), y(_y) {} + template + CvPoint(const cv::Point_<_Tp>& pt): x((int)pt.x), y((int)pt.y) {} +#endif +#ifdef __cplusplus + template + operator cv::Point_<_Tp>() const { return cv::Point_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y)); } +#endif +} +CvPoint; + +/** constructs CvPoint structure. */ +CV_INLINE CvPoint cvPoint( int x, int y ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint p = {x, y}; +#else + CvPoint p(x, y); +#endif + return p; +} +#ifdef __cplusplus +CV_INLINE CvPoint cvPoint(const cv::Point& pt) { return cvPoint(pt.x, pt.y); } +#endif + +typedef struct CvPoint2D32f +{ + float x; + float y; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint2D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint2D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint2D32f(float _x = 0, float _y = 0): x(_x), y(_y) {} + template + CvPoint2D32f(const cv::Point_<_Tp>& pt): x((float)pt.x), y((float)pt.y) {} +#endif +#ifdef __cplusplus + template + operator cv::Point_<_Tp>() const { return cv::Point_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y)); } +#endif +} +CvPoint2D32f; + +/** constructs CvPoint2D32f structure. */ +CV_INLINE CvPoint2D32f cvPoint2D32f( double x, double y ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint2D32f p = { (float)x, (float)y }; +#else + CvPoint2D32f p((float)x, (float)y); +#endif + return p; +} + +#ifdef __cplusplus +template +CvPoint2D32f cvPoint2D32f(const cv::Point_<_Tp>& pt) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint2D32f p = { (float)pt.x, (float)pt.y }; +#else + CvPoint2D32f p((float)pt.x, (float)pt.y); +#endif + return p; +} +#endif + +/** converts CvPoint to CvPoint2D32f. */ +CV_INLINE CvPoint2D32f cvPointTo32f( CvPoint point ) +{ + return cvPoint2D32f( (float)point.x, (float)point.y ); +} + +/** converts CvPoint2D32f to CvPoint. */ +CV_INLINE CvPoint cvPointFrom32f( CvPoint2D32f point ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint ipt = { cvRound(point.x), cvRound(point.y) }; +#else + CvPoint ipt(cvRound(point.x), cvRound(point.y)); +#endif + return ipt; +} + + +typedef struct CvPoint3D32f +{ + float x; + float y; + float z; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint3D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint3D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 3); + x = y = z = 0; + if (list.size() == 3) + { + x = list.begin()[0]; y = list.begin()[1]; z = list.begin()[2]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvPoint3D32f(float _x = 0, float _y = 0, float _z = 0): x(_x), y(_y), z(_z) {} + template + CvPoint3D32f(const cv::Point3_<_Tp>& pt): x((float)pt.x), y((float)pt.y), z((float)pt.z) {} +#endif +#ifdef __cplusplus + template + operator cv::Point3_<_Tp>() const { return cv::Point3_<_Tp>(cv::saturate_cast<_Tp>(x), cv::saturate_cast<_Tp>(y), cv::saturate_cast<_Tp>(z)); } +#endif +} +CvPoint3D32f; + +/** constructs CvPoint3D32f structure. */ +CV_INLINE CvPoint3D32f cvPoint3D32f( double x, double y, double z ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint3D32f p = { (float)x, (float)y, (float)z }; +#else + CvPoint3D32f p((float)x, (float)y, (float)z); +#endif + return p; +} + +#ifdef __cplusplus +template +CvPoint3D32f cvPoint3D32f(const cv::Point3_<_Tp>& pt) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvPoint3D32f p = { (float)pt.x, (float)pt.y, (float)pt.z }; +#else + CvPoint3D32f p((float)pt.x, (float)pt.y, (float)pt.z); +#endif + return p; +} +#endif + + +typedef struct CvPoint2D64f +{ + double x; + double y; +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint2D64f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint2D64f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + x = y = 0; + if (list.size() == 2) + { + x = list.begin()[0]; y = list.begin()[1]; + } + }; +#endif +} +CvPoint2D64f; + +/** constructs CvPoint2D64f structure.*/ +CV_INLINE CvPoint2D64f cvPoint2D64f( double x, double y ) +{ + CvPoint2D64f p = { x, y }; + return p; +} + + +typedef struct CvPoint3D64f +{ + double x; + double y; + double z; +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvPoint3D64f() __attribute__(( warning("Non-initialized variable") )) {} + template CvPoint3D64f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 3); + x = y = z = 0; + if (list.size() == 3) + { + x = list.begin()[0]; y = list.begin()[1]; z = list.begin()[2]; + } + }; +#endif +} +CvPoint3D64f; + +/** constructs CvPoint3D64f structure. */ +CV_INLINE CvPoint3D64f cvPoint3D64f( double x, double y, double z ) +{ + CvPoint3D64f p = { x, y, z }; + return p; +} + + +/******************************** CvSize's & CvBox **************************************/ + +typedef struct CvSize +{ + int width; + int height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSize() __attribute__(( warning("Non-initialized variable") )) {} + template CvSize(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + width = 0; height = 0; + if (list.size() == 2) + { + width = list.begin()[0]; height = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvSize(int w = 0, int h = 0): width(w), height(h) {} + template + CvSize(const cv::Size_<_Tp>& sz): width(cv::saturate_cast(sz.width)), height(cv::saturate_cast(sz.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Size_<_Tp>() const { return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(width), cv::saturate_cast<_Tp>(height)); } +#endif +} +CvSize; + +/** constructs CvSize structure. */ +CV_INLINE CvSize cvSize( int width, int height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize s = { width, height }; +#else + CvSize s(width, height); +#endif + return s; +} + +#ifdef __cplusplus +CV_INLINE CvSize cvSize(const cv::Size& sz) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize s = { sz.width, sz.height }; +#else + CvSize s(sz.width, sz.height); +#endif + return s; +} +#endif + +typedef struct CvSize2D32f +{ + float width; + float height; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSize2D32f() __attribute__(( warning("Non-initialized variable") )) {} + template CvSize2D32f(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + width = 0; height = 0; + if (list.size() == 2) + { + width = list.begin()[0]; height = list.begin()[1]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvSize2D32f(float w = 0, float h = 0): width(w), height(h) {} + template + CvSize2D32f(const cv::Size_<_Tp>& sz): width(cv::saturate_cast(sz.width)), height(cv::saturate_cast(sz.height)) {} +#endif +#ifdef __cplusplus + template + operator cv::Size_<_Tp>() const { return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(width), cv::saturate_cast<_Tp>(height)); } +#endif +} +CvSize2D32f; + +/** constructs CvSize2D32f structure. */ +CV_INLINE CvSize2D32f cvSize2D32f( double width, double height ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize2D32f s = { (float)width, (float)height }; +#else + CvSize2D32f s((float)width, (float)height); +#endif + return s; +} +#ifdef __cplusplus +template +CvSize2D32f cvSize2D32f(const cv::Size_<_Tp>& sz) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvSize2D32f s = { (float)sz.width, (float)sz.height }; +#else + CvSize2D32f s((float)sz.width, (float)sz.height); +#endif + return s; +} +#endif + +/** @sa RotatedRect + */ +typedef struct CvBox2D +{ + CvPoint2D32f center; /**< Center of the box. */ + CvSize2D32f size; /**< Box width and length. */ + float angle; /**< Angle between the horizontal axis */ + /**< and the first side (i.e. length) in degrees */ + +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvBox2D(CvPoint2D32f c = CvPoint2D32f(), CvSize2D32f s = CvSize2D32f(), float a = 0) : center(c), size(s), angle(a) {} + CvBox2D(const cv::RotatedRect& rr) : center(rr.center), size(rr.size), angle(rr.angle) {} +#endif +#ifdef __cplusplus + operator cv::RotatedRect() const { return cv::RotatedRect(center, size, angle); } +#endif +} +CvBox2D; + + +#ifdef __cplusplus +CV_INLINE CvBox2D cvBox2D(CvPoint2D32f c = CvPoint2D32f(), CvSize2D32f s = CvSize2D32f(), float a = 0) +{ + CvBox2D self; + self.center = c; + self.size = s; + self.angle = a; + return self; +} +CV_INLINE CvBox2D cvBox2D(const cv::RotatedRect& rr) +{ + CvBox2D self; + self.center = cvPoint2D32f(rr.center); + self.size = cvSize2D32f(rr.size); + self.angle = rr.angle; + return self; +} +#endif + + +/** Line iterator state: */ +typedef struct CvLineIterator +{ + /** Pointer to the current point: */ + uchar* ptr; + + /* Bresenham algorithm state: */ + int err; + int plus_delta; + int minus_delta; + int plus_step; + int minus_step; +} +CvLineIterator; + + + +/************************************* CvSlice ******************************************/ +#define CV_WHOLE_SEQ_END_INDEX 0x3fffffff +#define CV_WHOLE_SEQ cvSlice(0, CV_WHOLE_SEQ_END_INDEX) + +typedef struct CvSlice +{ + int start_index, end_index; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvSlice() __attribute__(( warning("Non-initialized variable") )) {} + template CvSlice(const std::initializer_list<_Tp> list) + { + CV_Assert(list.size() == 0 || list.size() == 2); + start_index = end_index = 0; + if (list.size() == 2) + { + start_index = list.begin()[0]; end_index = list.begin()[1]; + } + }; +#endif +#if defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) && !defined(__CUDACC__) + CvSlice(int start = 0, int end = 0) : start_index(start), end_index(end) {} + CvSlice(const cv::Range& r) { *this = (r.start != INT_MIN && r.end != INT_MAX) ? CvSlice(r.start, r.end) : CvSlice(0, CV_WHOLE_SEQ_END_INDEX); } + operator cv::Range() const { return (start_index == 0 && end_index == CV_WHOLE_SEQ_END_INDEX ) ? cv::Range::all() : cv::Range(start_index, end_index); } +#endif +} +CvSlice; + +CV_INLINE CvSlice cvSlice( int start, int end ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) && !defined(__CUDACC__)) + CvSlice slice = { start, end }; +#else + CvSlice slice(start, end); +#endif + return slice; +} + +#if defined(__cplusplus) +CV_INLINE CvSlice cvSlice(const cv::Range& r) +{ + CvSlice slice = (r.start != INT_MIN && r.end != INT_MAX) ? cvSlice(r.start, r.end) : cvSlice(0, CV_WHOLE_SEQ_END_INDEX); + return slice; +} +#endif + + +/************************************* CvScalar *****************************************/ +/** @sa Scalar_ + */ +typedef struct CvScalar +{ + double val[4]; + +#ifdef CV__VALIDATE_UNUNITIALIZED_VARS + CvScalar() __attribute__(( warning("Non-initialized variable") )) {} + CvScalar(const std::initializer_list list) + { + CV_Assert(list.size() == 0 || list.size() == 4); + val[0] = val[1] = val[2] = val[3] = 0; + if (list.size() == 4) + { + val[0] = list.begin()[0]; val[1] = list.begin()[1]; val[2] = list.begin()[2]; val[3] = list.begin()[3]; + } + }; +#elif defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) + CvScalar() {} + CvScalar(double d0, double d1 = 0, double d2 = 0, double d3 = 0) { val[0] = d0; val[1] = d1; val[2] = d2; val[3] = d3; } + template + CvScalar(const cv::Scalar_<_Tp>& s) { val[0] = s.val[0]; val[1] = s.val[1]; val[2] = s.val[2]; val[3] = s.val[3]; } + template + CvScalar(const cv::Vec<_Tp, cn>& v) + { + int i; + for( i = 0; i < (cn < 4 ? cn : 4); i++ ) val[i] = v.val[i]; + for( ; i < 4; i++ ) val[i] = 0; + } +#endif +#ifdef __cplusplus + template + operator cv::Scalar_<_Tp>() const { return cv::Scalar_<_Tp>(cv::saturate_cast<_Tp>(val[0]), cv::saturate_cast<_Tp>(val[1]), cv::saturate_cast<_Tp>(val[2]), cv::saturate_cast<_Tp>(val[3])); } +#endif +} +CvScalar; + +CV_INLINE CvScalar cvScalar( double val0, double val1 CV_DEFAULT(0), + double val2 CV_DEFAULT(0), double val3 CV_DEFAULT(0)) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0; scalar.val[1] = val1; + scalar.val[2] = val2; scalar.val[3] = val3; + return scalar; +} + +#ifdef __cplusplus +CV_INLINE CvScalar cvScalar() +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = scalar.val[1] = scalar.val[2] = scalar.val[3] = 0; + return scalar; +} +CV_INLINE CvScalar cvScalar(const cv::Scalar& s) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = s.val[0]; + scalar.val[1] = s.val[1]; + scalar.val[2] = s.val[2]; + scalar.val[3] = s.val[3]; + return scalar; +} +#endif + +CV_INLINE CvScalar cvRealScalar( double val0 ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0; + scalar.val[1] = scalar.val[2] = scalar.val[3] = 0; + return scalar; +} + +CV_INLINE CvScalar cvScalarAll( double val0123 ) +{ +#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus)) + CvScalar scalar = CV_STRUCT_INITIALIZER; +#else + CvScalar scalar; +#endif + scalar.val[0] = val0123; + scalar.val[1] = val0123; + scalar.val[2] = val0123; + scalar.val[3] = val0123; + return scalar; +} + +/****************************************************************************************\ +* Dynamic Data structures * +\****************************************************************************************/ + +/******************************** Memory storage ****************************************/ + +typedef struct CvMemBlock +{ + struct CvMemBlock* prev; + struct CvMemBlock* next; +} +CvMemBlock; + +#define CV_STORAGE_MAGIC_VAL 0x42890000 + +typedef struct CvMemStorage +{ + int signature; + CvMemBlock* bottom; /**< First allocated block. */ + CvMemBlock* top; /**< Current memory block - top of the stack. */ + struct CvMemStorage* parent; /**< We get new blocks from parent as needed. */ + int block_size; /**< Block size. */ + int free_space; /**< Remaining free space in current block. */ +} +CvMemStorage; + +#define CV_IS_STORAGE(storage) \ + ((storage) != NULL && \ + (((CvMemStorage*)(storage))->signature & CV_MAGIC_MASK) == CV_STORAGE_MAGIC_VAL) + + +typedef struct CvMemStoragePos +{ + CvMemBlock* top; + int free_space; +} +CvMemStoragePos; + + +/*********************************** Sequence *******************************************/ + +typedef struct CvSeqBlock +{ + struct CvSeqBlock* prev; /**< Previous sequence block. */ + struct CvSeqBlock* next; /**< Next sequence block. */ + int start_index; /**< Index of the first element in the block + */ + /**< sequence->first->start_index. */ + int count; /**< Number of elements in the block. */ + schar* data; /**< Pointer to the first element of the block. */ +} +CvSeqBlock; + + +#define CV_TREE_NODE_FIELDS(node_type) \ + int flags; /**< Miscellaneous flags. */ \ + int header_size; /**< Size of sequence header. */ \ + struct node_type* h_prev; /**< Previous sequence. */ \ + struct node_type* h_next; /**< Next sequence. */ \ + struct node_type* v_prev; /**< 2nd previous sequence. */ \ + struct node_type* v_next /**< 2nd next sequence. */ + +/** + Read/Write sequence. + Elements can be dynamically inserted to or deleted from the sequence. +*/ +#define CV_SEQUENCE_FIELDS() \ + CV_TREE_NODE_FIELDS(CvSeq); \ + int total; /**< Total number of elements. */ \ + int elem_size; /**< Size of sequence element in bytes. */ \ + schar* block_max; /**< Maximal bound of the last block. */ \ + schar* ptr; /**< Current write pointer. */ \ + int delta_elems; /**< Grow seq this many at a time. */ \ + CvMemStorage* storage; /**< Where the seq is stored. */ \ + CvSeqBlock* free_blocks; /**< Free blocks list. */ \ + CvSeqBlock* first; /**< Pointer to the first sequence block. */ + +typedef struct CvSeq +{ + CV_SEQUENCE_FIELDS() +} +CvSeq; + +#define CV_TYPE_NAME_SEQ "opencv-sequence" +#define CV_TYPE_NAME_SEQ_TREE "opencv-sequence-tree" + +/*************************************** Set ********************************************/ +/** @brief Set + Order is not preserved. There can be gaps between sequence elements. + After the element has been inserted it stays in the same place all the time. + The MSB(most-significant or sign bit) of the first field (flags) is 0 iff the element exists. +*/ +#define CV_SET_ELEM_FIELDS(elem_type) \ + int flags; \ + struct elem_type* next_free; + +typedef struct CvSetElem +{ + CV_SET_ELEM_FIELDS(CvSetElem) +} +CvSetElem; + +#define CV_SET_FIELDS() \ + CV_SEQUENCE_FIELDS() \ + CvSetElem* free_elems; \ + int active_count; + +typedef struct CvSet +{ + CV_SET_FIELDS() +} +CvSet; + + +#define CV_SET_ELEM_IDX_MASK ((1 << 26) - 1) +#define CV_SET_ELEM_FREE_FLAG (1 << (sizeof(int)*8-1)) + +/** Checks whether the element pointed by ptr belongs to a set or not */ +#define CV_IS_SET_ELEM( ptr ) (((CvSetElem*)(ptr))->flags >= 0) + +/************************************* Graph ********************************************/ + +/** @name Graph + +We represent a graph as a set of vertices. Vertices contain their adjacency lists (more exactly, +pointers to first incoming or outcoming edge (or 0 if isolated vertex)). Edges are stored in +another set. There is a singly-linked list of incoming/outcoming edges for each vertex. + +Each edge consists of: + +- Two pointers to the starting and ending vertices (vtx[0] and vtx[1] respectively). + + A graph may be oriented or not. In the latter case, edges between vertex i to vertex j are not +distinguished during search operations. + +- Two pointers to next edges for the starting and ending vertices, where next[0] points to the +next edge in the vtx[0] adjacency list and next[1] points to the next edge in the vtx[1] +adjacency list. + +@see CvGraphEdge, CvGraphVtx, CvGraphVtx2D, CvGraph +@{ +*/ +#define CV_GRAPH_EDGE_FIELDS() \ + int flags; \ + float weight; \ + struct CvGraphEdge* next[2]; \ + struct CvGraphVtx* vtx[2]; + + +#define CV_GRAPH_VERTEX_FIELDS() \ + int flags; \ + struct CvGraphEdge* first; + + +typedef struct CvGraphEdge +{ + CV_GRAPH_EDGE_FIELDS() +} +CvGraphEdge; + +typedef struct CvGraphVtx +{ + CV_GRAPH_VERTEX_FIELDS() +} +CvGraphVtx; + +typedef struct CvGraphVtx2D +{ + CV_GRAPH_VERTEX_FIELDS() + CvPoint2D32f* ptr; +} +CvGraphVtx2D; + +/** + Graph is "derived" from the set (this is set a of vertices) + and includes another set (edges) +*/ +#define CV_GRAPH_FIELDS() \ + CV_SET_FIELDS() \ + CvSet* edges; + +typedef struct CvGraph +{ + CV_GRAPH_FIELDS() +} +CvGraph; + +#define CV_TYPE_NAME_GRAPH "opencv-graph" + +/** @} */ + +/*********************************** Chain/Contour *************************************/ + +typedef struct CvChain +{ + CV_SEQUENCE_FIELDS() + CvPoint origin; +} +CvChain; + +#define CV_CONTOUR_FIELDS() \ + CV_SEQUENCE_FIELDS() \ + CvRect rect; \ + int color; \ + int reserved[3]; + +typedef struct CvContour +{ + CV_CONTOUR_FIELDS() +} +CvContour; + +typedef CvContour CvPoint2DSeq; + +/****************************************************************************************\ +* Sequence types * +\****************************************************************************************/ + +#define CV_SEQ_MAGIC_VAL 0x42990000 + +#define CV_IS_SEQ(seq) \ + ((seq) != NULL && (((CvSeq*)(seq))->flags & CV_MAGIC_MASK) == CV_SEQ_MAGIC_VAL) + +#define CV_SET_MAGIC_VAL 0x42980000 +#define CV_IS_SET(set) \ + ((set) != NULL && (((CvSeq*)(set))->flags & CV_MAGIC_MASK) == CV_SET_MAGIC_VAL) + +#define CV_SEQ_ELTYPE_BITS 12 +#define CV_SEQ_ELTYPE_MASK ((1 << CV_SEQ_ELTYPE_BITS) - 1) + +#define CV_SEQ_ELTYPE_POINT CV_32SC2 /**< (x,y) */ +#define CV_SEQ_ELTYPE_CODE CV_8UC1 /**< freeman code: 0..7 */ +#define CV_SEQ_ELTYPE_GENERIC 0 +#define CV_SEQ_ELTYPE_PTR CV_USRTYPE1 +#define CV_SEQ_ELTYPE_PPOINT CV_SEQ_ELTYPE_PTR /**< &(x,y) */ +#define CV_SEQ_ELTYPE_INDEX CV_32SC1 /**< #(x,y) */ +#define CV_SEQ_ELTYPE_GRAPH_EDGE 0 /**< &next_o, &next_d, &vtx_o, &vtx_d */ +#define CV_SEQ_ELTYPE_GRAPH_VERTEX 0 /**< first_edge, &(x,y) */ +#define CV_SEQ_ELTYPE_TRIAN_ATR 0 /**< vertex of the binary tree */ +#define CV_SEQ_ELTYPE_CONNECTED_COMP 0 /**< connected component */ +#define CV_SEQ_ELTYPE_POINT3D CV_32FC3 /**< (x,y,z) */ + +#define CV_SEQ_KIND_BITS 2 +#define CV_SEQ_KIND_MASK (((1 << CV_SEQ_KIND_BITS) - 1)<flags & CV_SEQ_ELTYPE_MASK) +#define CV_SEQ_KIND( seq ) ((seq)->flags & CV_SEQ_KIND_MASK ) + +/** flag checking */ +#define CV_IS_SEQ_INDEX( seq ) ((CV_SEQ_ELTYPE(seq) == CV_SEQ_ELTYPE_INDEX) && \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_GENERIC)) + +#define CV_IS_SEQ_CURVE( seq ) (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE) +#define CV_IS_SEQ_CLOSED( seq ) (((seq)->flags & CV_SEQ_FLAG_CLOSED) != 0) +#define CV_IS_SEQ_CONVEX( seq ) 0 +#define CV_IS_SEQ_HOLE( seq ) (((seq)->flags & CV_SEQ_FLAG_HOLE) != 0) +#define CV_IS_SEQ_SIMPLE( seq ) 1 + +/** type checking macros */ +#define CV_IS_SEQ_POINT_SET( seq ) \ + ((CV_SEQ_ELTYPE(seq) == CV_32SC2 || CV_SEQ_ELTYPE(seq) == CV_32FC2)) + +#define CV_IS_SEQ_POINT_SUBSET( seq ) \ + (CV_IS_SEQ_INDEX( seq ) || CV_SEQ_ELTYPE(seq) == CV_SEQ_ELTYPE_PPOINT) + +#define CV_IS_SEQ_POLYLINE( seq ) \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE && CV_IS_SEQ_POINT_SET(seq)) + +#define CV_IS_SEQ_POLYGON( seq ) \ + (CV_IS_SEQ_POLYLINE(seq) && CV_IS_SEQ_CLOSED(seq)) + +#define CV_IS_SEQ_CHAIN( seq ) \ + (CV_SEQ_KIND(seq) == CV_SEQ_KIND_CURVE && (seq)->elem_size == 1) + +#define CV_IS_SEQ_CONTOUR( seq ) \ + (CV_IS_SEQ_CLOSED(seq) && (CV_IS_SEQ_POLYLINE(seq) || CV_IS_SEQ_CHAIN(seq))) + +#define CV_IS_SEQ_CHAIN_CONTOUR( seq ) \ + (CV_IS_SEQ_CHAIN( seq ) && CV_IS_SEQ_CLOSED( seq )) + +#define CV_IS_SEQ_POLYGON_TREE( seq ) \ + (CV_SEQ_ELTYPE (seq) == CV_SEQ_ELTYPE_TRIAN_ATR && \ + CV_SEQ_KIND( seq ) == CV_SEQ_KIND_BIN_TREE ) + +#define CV_IS_GRAPH( seq ) \ + (CV_IS_SET(seq) && CV_SEQ_KIND((CvSet*)(seq)) == CV_SEQ_KIND_GRAPH) + +#define CV_IS_GRAPH_ORIENTED( seq ) \ + (((seq)->flags & CV_GRAPH_FLAG_ORIENTED) != 0) + +#define CV_IS_SUBDIV2D( seq ) \ + (CV_IS_SET(seq) && CV_SEQ_KIND((CvSet*)(seq)) == CV_SEQ_KIND_SUBDIV2D) + +/****************************************************************************************/ +/* Sequence writer & reader */ +/****************************************************************************************/ + +#define CV_SEQ_WRITER_FIELDS() \ + int header_size; \ + CvSeq* seq; /**< the sequence written */ \ + CvSeqBlock* block; /**< current block */ \ + schar* ptr; /**< pointer to free space */ \ + schar* block_min; /**< pointer to the beginning of block*/\ + schar* block_max; /**< pointer to the end of block */ + +typedef struct CvSeqWriter +{ + CV_SEQ_WRITER_FIELDS() +} +CvSeqWriter; + + +#define CV_SEQ_READER_FIELDS() \ + int header_size; \ + CvSeq* seq; /**< sequence, beign read */ \ + CvSeqBlock* block; /**< current block */ \ + schar* ptr; /**< pointer to element be read next */ \ + schar* block_min; /**< pointer to the beginning of block */\ + schar* block_max; /**< pointer to the end of block */ \ + int delta_index;/**< = seq->first->start_index */ \ + schar* prev_elem; /**< pointer to previous element */ + +typedef struct CvSeqReader +{ + CV_SEQ_READER_FIELDS() +} +CvSeqReader; + +/****************************************************************************************/ +/* Operations on sequences */ +/****************************************************************************************/ + +#define CV_SEQ_ELEM( seq, elem_type, index ) \ +/** assert gives some guarantee that parameter is valid */ \ +( assert(sizeof((seq)->first[0]) == sizeof(CvSeqBlock) && \ + (seq)->elem_size == sizeof(elem_type)), \ + (elem_type*)((seq)->first && (unsigned)index < \ + (unsigned)((seq)->first->count) ? \ + (seq)->first->data + (index) * sizeof(elem_type) : \ + cvGetSeqElem( (CvSeq*)(seq), (index) ))) +#define CV_GET_SEQ_ELEM( elem_type, seq, index ) CV_SEQ_ELEM( (seq), elem_type, (index) ) + +/** Add element to sequence: */ +#define CV_WRITE_SEQ_ELEM_VAR( elem_ptr, writer ) \ +{ \ + if( (writer).ptr >= (writer).block_max ) \ + { \ + cvCreateSeqBlock( &writer); \ + } \ + memcpy((writer).ptr, elem_ptr, (writer).seq->elem_size);\ + (writer).ptr += (writer).seq->elem_size; \ +} + +#define CV_WRITE_SEQ_ELEM( elem, writer ) \ +{ \ + assert( (writer).seq->elem_size == sizeof(elem)); \ + if( (writer).ptr >= (writer).block_max ) \ + { \ + cvCreateSeqBlock( &writer); \ + } \ + assert( (writer).ptr <= (writer).block_max - sizeof(elem));\ + memcpy((writer).ptr, &(elem), sizeof(elem)); \ + (writer).ptr += sizeof(elem); \ +} + + +/** Move reader position forward: */ +#define CV_NEXT_SEQ_ELEM( elem_size, reader ) \ +{ \ + if( ((reader).ptr += (elem_size)) >= (reader).block_max ) \ + { \ + cvChangeSeqBlock( &(reader), 1 ); \ + } \ +} + + +/** Move reader position backward: */ +#define CV_PREV_SEQ_ELEM( elem_size, reader ) \ +{ \ + if( ((reader).ptr -= (elem_size)) < (reader).block_min ) \ + { \ + cvChangeSeqBlock( &(reader), -1 ); \ + } \ +} + +/** Read element and move read position forward: */ +#define CV_READ_SEQ_ELEM( elem, reader ) \ +{ \ + assert( (reader).seq->elem_size == sizeof(elem)); \ + memcpy( &(elem), (reader).ptr, sizeof((elem))); \ + CV_NEXT_SEQ_ELEM( sizeof(elem), reader ) \ +} + +/** Read element and move read position backward: */ +#define CV_REV_READ_SEQ_ELEM( elem, reader ) \ +{ \ + assert( (reader).seq->elem_size == sizeof(elem)); \ + memcpy(&(elem), (reader).ptr, sizeof((elem))); \ + CV_PREV_SEQ_ELEM( sizeof(elem), reader ) \ +} + + +#define CV_READ_CHAIN_POINT( _pt, reader ) \ +{ \ + (_pt) = (reader).pt; \ + if( (reader).ptr ) \ + { \ + CV_READ_SEQ_ELEM( (reader).code, (reader)); \ + assert( ((reader).code & ~7) == 0 ); \ + (reader).pt.x += (reader).deltas[(int)(reader).code][0]; \ + (reader).pt.y += (reader).deltas[(int)(reader).code][1]; \ + } \ +} + +#define CV_CURRENT_POINT( reader ) (*((CvPoint*)((reader).ptr))) +#define CV_PREV_POINT( reader ) (*((CvPoint*)((reader).prev_elem))) + +#define CV_READ_EDGE( pt1, pt2, reader ) \ +{ \ + assert( sizeof(pt1) == sizeof(CvPoint) && \ + sizeof(pt2) == sizeof(CvPoint) && \ + reader.seq->elem_size == sizeof(CvPoint)); \ + (pt1) = CV_PREV_POINT( reader ); \ + (pt2) = CV_CURRENT_POINT( reader ); \ + (reader).prev_elem = (reader).ptr; \ + CV_NEXT_SEQ_ELEM( sizeof(CvPoint), (reader)); \ +} + +/************ Graph macros ************/ + +/** Return next graph edge for given vertex: */ +#define CV_NEXT_GRAPH_EDGE( edge, vertex ) \ + (assert((edge)->vtx[0] == (vertex) || (edge)->vtx[1] == (vertex)), \ + (edge)->next[(edge)->vtx[1] == (vertex)]) + + + +/****************************************************************************************\ +* Data structures for persistence (a.k.a serialization) functionality * +\****************************************************************************************/ + +/** "black box" file storage */ +typedef struct CvFileStorage CvFileStorage; + +/** Storage flags: */ +#define CV_STORAGE_READ 0 +#define CV_STORAGE_WRITE 1 +#define CV_STORAGE_WRITE_TEXT CV_STORAGE_WRITE +#define CV_STORAGE_WRITE_BINARY CV_STORAGE_WRITE +#define CV_STORAGE_APPEND 2 +#define CV_STORAGE_MEMORY 4 +#define CV_STORAGE_FORMAT_MASK (7<<3) +#define CV_STORAGE_FORMAT_AUTO 0 +#define CV_STORAGE_FORMAT_XML 8 +#define CV_STORAGE_FORMAT_YAML 16 +#define CV_STORAGE_FORMAT_JSON 24 +#define CV_STORAGE_BASE64 64 +#define CV_STORAGE_WRITE_BASE64 (CV_STORAGE_BASE64 | CV_STORAGE_WRITE) + +/** @brief List of attributes. : + +In the current implementation, attributes are used to pass extra parameters when writing user +objects (see cvWrite). XML attributes inside tags are not supported, aside from the object type +specification (type_id attribute). +@see cvAttrList, cvAttrValue + */ +typedef struct CvAttrList +{ + const char** attr; /**< NULL-terminated array of (attribute_name,attribute_value) pairs. */ + struct CvAttrList* next; /**< Pointer to next chunk of the attributes list. */ +} +CvAttrList; + +/** initializes CvAttrList structure */ +CV_INLINE CvAttrList cvAttrList( const char** attr CV_DEFAULT(NULL), + CvAttrList* next CV_DEFAULT(NULL) ) +{ + CvAttrList l; + l.attr = attr; + l.next = next; + + return l; +} + +struct CvTypeInfo; + +#define CV_NODE_NONE 0 +#define CV_NODE_INT 1 +#define CV_NODE_INTEGER CV_NODE_INT +#define CV_NODE_REAL 2 +#define CV_NODE_FLOAT CV_NODE_REAL +#define CV_NODE_STR 3 +#define CV_NODE_STRING CV_NODE_STR +#define CV_NODE_REF 4 /**< not used */ +#define CV_NODE_SEQ 5 +#define CV_NODE_MAP 6 +#define CV_NODE_TYPE_MASK 7 + +#define CV_NODE_TYPE(flags) ((flags) & CV_NODE_TYPE_MASK) + +/** file node flags */ +#define CV_NODE_FLOW 8 /**= CV_NODE_SEQ) +#define CV_NODE_IS_FLOW(flags) (((flags) & CV_NODE_FLOW) != 0) +#define CV_NODE_IS_EMPTY(flags) (((flags) & CV_NODE_EMPTY) != 0) +#define CV_NODE_IS_USER(flags) (((flags) & CV_NODE_USER) != 0) +#define CV_NODE_HAS_NAME(flags) (((flags) & CV_NODE_NAMED) != 0) + +#define CV_NODE_SEQ_SIMPLE 256 +#define CV_NODE_SEQ_IS_SIMPLE(seq) (((seq)->flags & CV_NODE_SEQ_SIMPLE) != 0) + +typedef struct CvString +{ + int len; + char* ptr; +} +CvString; + +/** All the keys (names) of elements in the read file storage + are stored in the hash to speed up the lookup operations: */ +typedef struct CvStringHashNode +{ + unsigned hashval; + CvString str; + struct CvStringHashNode* next; +} +CvStringHashNode; + +typedef struct CvGenericHash CvFileNodeHash; + +/** Basic element of the file storage - scalar or collection: */ +typedef struct CvFileNode +{ + int tag; + struct CvTypeInfo* info; /**< type information + (only for user-defined object, for others it is 0) */ + union + { + double f; /**< scalar floating-point number */ + int i; /**< scalar integer number */ + CvString str; /**< text string */ + CvSeq* seq; /**< sequence (ordered collection of file nodes) */ + CvFileNodeHash* map; /**< map (collection of named file nodes) */ + } data; +} +CvFileNode; + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (CV_CDECL *CvIsInstanceFunc)( const void* struct_ptr ); +typedef void (CV_CDECL *CvReleaseFunc)( void** struct_dblptr ); +typedef void* (CV_CDECL *CvReadFunc)( CvFileStorage* storage, CvFileNode* node ); +typedef void (CV_CDECL *CvWriteFunc)( CvFileStorage* storage, const char* name, + const void* struct_ptr, CvAttrList attributes ); +typedef void* (CV_CDECL *CvCloneFunc)( const void* struct_ptr ); +#ifdef __cplusplus +} +#endif + +/** @brief Type information + +The structure contains information about one of the standard or user-defined types. Instances of the +type may or may not contain a pointer to the corresponding CvTypeInfo structure. In any case, there +is a way to find the type info structure for a given object using the cvTypeOf function. +Alternatively, type info can be found by type name using cvFindType, which is used when an object +is read from file storage. The user can register a new type with cvRegisterType that adds the type +information structure into the beginning of the type list. Thus, it is possible to create +specialized types from generic standard types and override the basic methods. + */ +typedef struct CvTypeInfo +{ + int flags; /**< not used */ + int header_size; /**< sizeof(CvTypeInfo) */ + struct CvTypeInfo* prev; /**< previous registered type in the list */ + struct CvTypeInfo* next; /**< next registered type in the list */ + const char* type_name; /**< type name, written to file storage */ + CvIsInstanceFunc is_instance; /**< checks if the passed object belongs to the type */ + CvReleaseFunc release; /**< releases object (memory etc.) */ + CvReadFunc read; /**< reads object from file storage */ + CvWriteFunc write; /**< writes object to file storage */ + CvCloneFunc clone; /**< creates a copy of the object */ +} +CvTypeInfo; + + +/**** System data types ******/ + +typedef struct CvPluginFuncInfo +{ + void** func_addr; + void* default_func_addr; + const char* func_names; + int search_modules; + int loaded_from; +} +CvPluginFuncInfo; + +typedef struct CvModuleInfo +{ + struct CvModuleInfo* next; + const char* name; + const char* version; + CvPluginFuncInfo* func_tab; +} +CvModuleInfo; + +/** @} */ + +#endif /*OPENCV_CORE_TYPES_H*/ + +/* End of file. */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utility.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utility.hpp new file mode 100644 index 0000000..b97458d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utility.hpp @@ -0,0 +1,1237 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_CORE_UTILITY_H +#define OPENCV_CORE_UTILITY_H + +#ifndef __cplusplus +# error utility.hpp header must be compiled as C++ +#endif + +#if defined(check) +# warning Detected Apple 'check' macro definition, it can cause build conflicts. Please, include this header before any Apple headers. +#endif + +#include "opencv2/core.hpp" +#include + +#ifdef CV_CXX11 +#include +#endif + +namespace cv +{ + +//! @addtogroup core_utils +//! @{ + +/** @brief Automatically Allocated Buffer Class + + The class is used for temporary buffers in functions and methods. + If a temporary buffer is usually small (a few K's of memory), + but its size depends on the parameters, it makes sense to create a small + fixed-size array on stack and use it if it's large enough. If the required buffer size + is larger than the fixed size, another buffer of sufficient size is allocated dynamically + and released after the processing. Therefore, in typical cases, when the buffer size is small, + there is no overhead associated with malloc()/free(). + At the same time, there is no limit on the size of processed data. + + This is what AutoBuffer does. The template takes 2 parameters - type of the buffer elements and + the number of stack-allocated elements. Here is how the class is used: + + \code + void my_func(const cv::Mat& m) + { + cv::AutoBuffer buf(1000); // create automatic buffer containing 1000 floats + + buf.allocate(m.rows); // if m.rows <= 1000, the pre-allocated buffer is used, + // otherwise the buffer of "m.rows" floats will be allocated + // dynamically and deallocated in cv::AutoBuffer destructor + ... + } + \endcode +*/ +#ifdef OPENCV_ENABLE_MEMORY_SANITIZER +template class AutoBuffer +#else +template class AutoBuffer +#endif +{ +public: + typedef _Tp value_type; + + //! the default constructor + AutoBuffer(); + //! constructor taking the real buffer size + explicit AutoBuffer(size_t _size); + + //! the copy constructor + AutoBuffer(const AutoBuffer<_Tp, fixed_size>& buf); + //! the assignment operator + AutoBuffer<_Tp, fixed_size>& operator = (const AutoBuffer<_Tp, fixed_size>& buf); + + //! destructor. calls deallocate() + ~AutoBuffer(); + + //! allocates the new buffer of size _size. if the _size is small enough, stack-allocated buffer is used + void allocate(size_t _size); + //! deallocates the buffer if it was dynamically allocated + void deallocate(); + //! resizes the buffer and preserves the content + void resize(size_t _size); + //! returns the current buffer size + size_t size() const; + //! returns pointer to the real buffer, stack-allocated or heap-allocated + inline _Tp* data() { return ptr; } + //! returns read-only pointer to the real buffer, stack-allocated or heap-allocated + inline const _Tp* data() const { return ptr; } + +#if !defined(OPENCV_DISABLE_DEPRECATED_COMPATIBILITY) // use to .data() calls instead + //! returns pointer to the real buffer, stack-allocated or heap-allocated + operator _Tp* () { return ptr; } + //! returns read-only pointer to the real buffer, stack-allocated or heap-allocated + operator const _Tp* () const { return ptr; } +#else + //! returns a reference to the element at specified location. No bounds checking is performed in Release builds. + inline _Tp& operator[] (size_t i) { CV_DbgCheckLT(i, sz, "out of range"); return ptr[i]; } + //! returns a reference to the element at specified location. No bounds checking is performed in Release builds. + inline const _Tp& operator[] (size_t i) const { CV_DbgCheckLT(i, sz, "out of range"); return ptr[i]; } +#endif + +protected: + //! pointer to the real buffer, can point to buf if the buffer is small enough + _Tp* ptr; + //! size of the real buffer + size_t sz; + //! pre-allocated buffer. At least 1 element to confirm C++ standard requirements + _Tp buf[(fixed_size > 0) ? fixed_size : 1]; +}; + +/** @brief Sets/resets the break-on-error mode. + +When the break-on-error mode is set, the default error handler issues a hardware exception, which +can make debugging more convenient. + +\return the previous state + */ +CV_EXPORTS bool setBreakOnError(bool flag); + +extern "C" typedef int (*ErrorCallback)( int status, const char* func_name, + const char* err_msg, const char* file_name, + int line, void* userdata ); + + +/** @brief Sets the new error handler and the optional user data. + + The function sets the new error handler, called from cv::error(). + + \param errCallback the new error handler. If NULL, the default error handler is used. + \param userdata the optional user data pointer, passed to the callback. + \param prevUserdata the optional output parameter where the previous user data pointer is stored + + \return the previous error handler +*/ +CV_EXPORTS ErrorCallback redirectError( ErrorCallback errCallback, void* userdata=0, void** prevUserdata=0); + +CV_EXPORTS String tempfile( const char* suffix = 0); +CV_EXPORTS void glob(String pattern, std::vector& result, bool recursive = false); + +/** @brief OpenCV will try to set the number of threads for the next parallel region. + +If threads == 0, OpenCV will disable threading optimizations and run all it's functions +sequentially. Passing threads \< 0 will reset threads number to system default. This function must +be called outside of parallel region. + +OpenCV will try to run its functions with specified threads number, but some behaviour differs from +framework: +- `TBB` - User-defined parallel constructions will run with the same threads number, if + another is not specified. If later on user creates his own scheduler, OpenCV will use it. +- `OpenMP` - No special defined behaviour. +- `Concurrency` - If threads == 1, OpenCV will disable threading optimizations and run its + functions sequentially. +- `GCD` - Supports only values \<= 0. +- `C=` - No special defined behaviour. +@param nthreads Number of threads used by OpenCV. +@sa getNumThreads, getThreadNum + */ +CV_EXPORTS_W void setNumThreads(int nthreads); + +/** @brief Returns the number of threads used by OpenCV for parallel regions. + +Always returns 1 if OpenCV is built without threading support. + +The exact meaning of return value depends on the threading framework used by OpenCV library: +- `TBB` - The number of threads, that OpenCV will try to use for parallel regions. If there is + any tbb::thread_scheduler_init in user code conflicting with OpenCV, then function returns + default number of threads used by TBB library. +- `OpenMP` - An upper bound on the number of threads that could be used to form a new team. +- `Concurrency` - The number of threads, that OpenCV will try to use for parallel regions. +- `GCD` - Unsupported; returns the GCD thread pool limit (512) for compatibility. +- `C=` - The number of threads, that OpenCV will try to use for parallel regions, if before + called setNumThreads with threads \> 0, otherwise returns the number of logical CPUs, + available for the process. +@sa setNumThreads, getThreadNum + */ +CV_EXPORTS_W int getNumThreads(); + +/** @brief Returns the index of the currently executed thread within the current parallel region. Always +returns 0 if called outside of parallel region. + +@deprecated Current implementation doesn't corresponding to this documentation. + +The exact meaning of the return value depends on the threading framework used by OpenCV library: +- `TBB` - Unsupported with current 4.1 TBB release. Maybe will be supported in future. +- `OpenMP` - The thread number, within the current team, of the calling thread. +- `Concurrency` - An ID for the virtual processor that the current context is executing on (0 + for master thread and unique number for others, but not necessary 1,2,3,...). +- `GCD` - System calling thread's ID. Never returns 0 inside parallel region. +- `C=` - The index of the current parallel task. +@sa setNumThreads, getNumThreads + */ +CV_EXPORTS_W int getThreadNum(); + +/** @brief Returns full configuration time cmake output. + +Returned value is raw cmake output including version control system revision, compiler version, +compiler flags, enabled modules and third party libraries, etc. Output format depends on target +architecture. + */ +CV_EXPORTS_W const String& getBuildInformation(); + +/** @brief Returns library version string + +For example "3.4.1-dev". + +@sa getMajorVersion, getMinorVersion, getRevisionVersion +*/ +CV_EXPORTS_W String getVersionString(); + +/** @brief Returns major library version */ +CV_EXPORTS_W int getVersionMajor(); + +/** @brief Returns minor library version */ +CV_EXPORTS_W int getVersionMinor(); + +/** @brief Returns revision field of the library version */ +CV_EXPORTS_W int getVersionRevision(); + +/** @brief Returns the number of ticks. + +The function returns the number of ticks after the certain event (for example, when the machine was +turned on). It can be used to initialize RNG or to measure a function execution time by reading the +tick count before and after the function call. +@sa getTickFrequency, TickMeter + */ +CV_EXPORTS_W int64 getTickCount(); + +/** @brief Returns the number of ticks per second. + +The function returns the number of ticks per second. That is, the following code computes the +execution time in seconds: +@code + double t = (double)getTickCount(); + // do something ... + t = ((double)getTickCount() - t)/getTickFrequency(); +@endcode +@sa getTickCount, TickMeter + */ +CV_EXPORTS_W double getTickFrequency(); + +/** @brief a Class to measure passing time. + +The class computes passing time by counting the number of ticks per second. That is, the following code computes the +execution time in seconds: +@snippet snippets/core_various.cpp TickMeter_total + +It is also possible to compute the average time over multiple runs: +@snippet snippets/core_various.cpp TickMeter_average + +@sa getTickCount, getTickFrequency +*/ +class CV_EXPORTS_W TickMeter +{ +public: + //! the default constructor + CV_WRAP TickMeter() + { + reset(); + } + + //! starts counting ticks. + CV_WRAP void start() + { + startTime = cv::getTickCount(); + } + + //! stops counting ticks. + CV_WRAP void stop() + { + int64 time = cv::getTickCount(); + if (startTime == 0) + return; + ++counter; + sumTime += (time - startTime); + startTime = 0; + } + + //! returns counted ticks. + CV_WRAP int64 getTimeTicks() const + { + return sumTime; + } + + //! returns passed time in microseconds. + CV_WRAP double getTimeMicro() const + { + return getTimeMilli()*1e3; + } + + //! returns passed time in milliseconds. + CV_WRAP double getTimeMilli() const + { + return getTimeSec()*1e3; + } + + //! returns passed time in seconds. + CV_WRAP double getTimeSec() const + { + return (double)getTimeTicks() / getTickFrequency(); + } + + //! returns internal counter value. + CV_WRAP int64 getCounter() const + { + return counter; + } + + //! returns average FPS (frames per second) value. + CV_WRAP double getFPS() const + { + const double sec = getTimeSec(); + if (sec < DBL_EPSILON) + return 0.; + return counter / sec; + } + + //! returns average time in seconds + CV_WRAP double getAvgTimeSec() const + { + if (counter <= 0) + return 0.; + return getTimeSec() / counter; + } + + //! returns average time in milliseconds + CV_WRAP double getAvgTimeMilli() const + { + return getAvgTimeSec() * 1e3; + } + + //! resets internal values. + CV_WRAP void reset() + { + startTime = 0; + sumTime = 0; + counter = 0; + } + +private: + int64 counter; + int64 sumTime; + int64 startTime; +}; + +/** @brief output operator +@code +TickMeter tm; +tm.start(); +// do something ... +tm.stop(); +std::cout << tm; +@endcode +*/ + +static inline +std::ostream& operator << (std::ostream& out, const TickMeter& tm) +{ + return out << tm.getTimeSec() << "sec"; +} + +/** @brief Returns the number of CPU ticks. + +The function returns the current number of CPU ticks on some architectures (such as x86, x64, +PowerPC). On other platforms the function is equivalent to getTickCount. It can also be used for +very accurate time measurements, as well as for RNG initialization. Note that in case of multi-CPU +systems a thread, from which getCPUTickCount is called, can be suspended and resumed at another CPU +with its own counter. So, theoretically (and practically) the subsequent calls to the function do +not necessary return the monotonously increasing values. Also, since a modern CPU varies the CPU +frequency depending on the load, the number of CPU clocks spent in some code cannot be directly +converted to time units. Therefore, getTickCount is generally a preferable solution for measuring +execution time. + */ +CV_EXPORTS_W int64 getCPUTickCount(); + +/** @brief Returns true if the specified feature is supported by the host hardware. + +The function returns true if the host hardware supports the specified feature. When user calls +setUseOptimized(false), the subsequent calls to checkHardwareSupport() will return false until +setUseOptimized(true) is called. This way user can dynamically switch on and off the optimized code +in OpenCV. +@param feature The feature of interest, one of cv::CpuFeatures + */ +CV_EXPORTS_W bool checkHardwareSupport(int feature); + +/** @brief Returns feature name by ID + +Returns empty string if feature is not defined +*/ +CV_EXPORTS_W String getHardwareFeatureName(int feature); + +/** @brief Returns list of CPU features enabled during compilation. + +Returned value is a string containing space separated list of CPU features with following markers: + +- no markers - baseline features +- prefix `*` - features enabled in dispatcher +- suffix `?` - features enabled but not available in HW + +Example: `SSE SSE2 SSE3 *SSE4.1 *SSE4.2 *FP16 *AVX *AVX2 *AVX512-SKX?` +*/ +CV_EXPORTS_W std::string getCPUFeaturesLine(); + +/** @brief Returns the number of logical CPUs available for the process. + */ +CV_EXPORTS_W int getNumberOfCPUs(); + + +/** @brief Aligns a pointer to the specified number of bytes. + +The function returns the aligned pointer of the same type as the input pointer: +\f[\texttt{(_Tp*)(((size_t)ptr + n-1) & -n)}\f] +@param ptr Aligned pointer. +@param n Alignment size that must be a power of two. + */ +template static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_Tp)) +{ + CV_DbgAssert((n & (n - 1)) == 0); // n is a power of 2 + return (_Tp*)(((size_t)ptr + n-1) & -n); +} + +/** @brief Aligns a buffer size to the specified number of bytes. + +The function returns the minimum number that is greater than or equal to sz and is divisible by n : +\f[\texttt{(sz + n-1) & -n}\f] +@param sz Buffer size to align. +@param n Alignment size that must be a power of two. + */ +static inline size_t alignSize(size_t sz, int n) +{ + CV_DbgAssert((n & (n - 1)) == 0); // n is a power of 2 + return (sz + n-1) & -n; +} + +/** @brief Integer division with result round up. + +Use this function instead of `ceil((float)a / b)` expressions. + +@sa alignSize +*/ +static inline int divUp(int a, unsigned int b) +{ + CV_DbgAssert(a >= 0); + return (a + b - 1) / b; +} +/** @overload */ +static inline size_t divUp(size_t a, unsigned int b) +{ + return (a + b - 1) / b; +} + +/** @brief Round first value up to the nearest multiple of second value. + +Use this function instead of `ceil((float)a / b) * b` expressions. + +@sa divUp +*/ +static inline int roundUp(int a, unsigned int b) +{ + CV_DbgAssert(a >= 0); + return a + b - 1 - (a + b -1) % b; +} +/** @overload */ +static inline size_t roundUp(size_t a, unsigned int b) +{ + return a + b - 1 - (a + b - 1) % b; +} + +/** @brief Alignment check of passed values + +Usage: `isAligned(...)` + +@note Alignment(N) must be a power of 2 (2**k, 2^k) +*/ +template static inline +bool isAligned(const T& data) +{ + CV_StaticAssert((N & (N - 1)) == 0, ""); // power of 2 + return (((size_t)data) & (N - 1)) == 0; +} +/** @overload */ +template static inline +bool isAligned(const void* p1) +{ + return isAligned((size_t)p1); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2) +{ + return isAligned(((size_t)p1)|((size_t)p2)); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2, const void* p3) +{ + return isAligned(((size_t)p1)|((size_t)p2)|((size_t)p3)); +} +/** @overload */ +template static inline +bool isAligned(const void* p1, const void* p2, const void* p3, const void* p4) +{ + return isAligned(((size_t)p1)|((size_t)p2)|((size_t)p3)|((size_t)p4)); +} + +/** @brief Enables or disables the optimized code. + +The function can be used to dynamically turn on and off optimized dispatched code (code that uses SSE4.2, AVX/AVX2, +and other instructions on the platforms that support it). It sets a global flag that is further +checked by OpenCV functions. Since the flag is not checked in the inner OpenCV loops, it is only +safe to call the function on the very top level in your application where you can be sure that no +other OpenCV function is currently executed. + +By default, the optimized code is enabled unless you disable it in CMake. The current status can be +retrieved using useOptimized. +@param onoff The boolean flag specifying whether the optimized code should be used (onoff=true) +or not (onoff=false). + */ +CV_EXPORTS_W void setUseOptimized(bool onoff); + +/** @brief Returns the status of optimized code usage. + +The function returns true if the optimized code is enabled. Otherwise, it returns false. + */ +CV_EXPORTS_W bool useOptimized(); + +static inline size_t getElemSize(int type) { return (size_t)CV_ELEM_SIZE(type); } + +/////////////////////////////// Parallel Primitives ////////////////////////////////// + +/** @brief Base class for parallel data processors +*/ +class CV_EXPORTS ParallelLoopBody +{ +public: + virtual ~ParallelLoopBody(); + virtual void operator() (const Range& range) const = 0; +}; + +/** @brief Parallel data processor +*/ +CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body, double nstripes=-1.); + +#ifdef CV_CXX11 +class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody +{ +private: + std::function m_functor; +public: + ParallelLoopBodyLambdaWrapper(std::function functor) : + m_functor(functor) + { } + + virtual void operator() (const cv::Range& range) const CV_OVERRIDE + { + m_functor(range); + } +}; + +inline void parallel_for_(const Range& range, std::function functor, double nstripes=-1.) +{ + parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes); +} +#endif + +/////////////////////////////// forEach method of cv::Mat //////////////////////////// +template inline +void Mat::forEach_impl(const Functor& operation) { + if (false) { + operation(*reinterpret_cast<_Tp*>(0), reinterpret_cast(0)); + // If your compiler fails in this line. + // Please check that your functor signature is + // (_Tp&, const int*) <- multi-dimensional + // or (_Tp&, void*) <- in case you don't need current idx. + } + + CV_Assert(!empty()); + CV_Assert(this->total() / this->size[this->dims - 1] <= INT_MAX); + const int LINES = static_cast(this->total() / this->size[this->dims - 1]); + + class PixelOperationWrapper :public ParallelLoopBody + { + public: + PixelOperationWrapper(Mat_<_Tp>* const frame, const Functor& _operation) + : mat(frame), op(_operation) {} + virtual ~PixelOperationWrapper(){} + // ! Overloaded virtual operator + // convert range call to row call. + virtual void operator()(const Range &range) const CV_OVERRIDE + { + const int DIMS = mat->dims; + const int COLS = mat->size[DIMS - 1]; + if (DIMS <= 2) { + for (int row = range.start; row < range.end; ++row) { + this->rowCall2(row, COLS); + } + } else { + std::vector idx(DIMS); /// idx is modified in this->rowCall + idx[DIMS - 2] = range.start - 1; + + for (int line_num = range.start; line_num < range.end; ++line_num) { + idx[DIMS - 2]++; + for (int i = DIMS - 2; i >= 0; --i) { + if (idx[i] >= mat->size[i]) { + idx[i - 1] += idx[i] / mat->size[i]; + idx[i] %= mat->size[i]; + continue; // carry-over; + } + else { + break; + } + } + this->rowCall(&idx[0], COLS, DIMS); + } + } + } + private: + Mat_<_Tp>* const mat; + const Functor op; + // ! Call operator for each elements in this row. + inline void rowCall(int* const idx, const int COLS, const int DIMS) const { + int &col = idx[DIMS - 1]; + col = 0; + _Tp* pixel = &(mat->template at<_Tp>(idx)); + + while (col < COLS) { + op(*pixel, const_cast(idx)); + pixel++; col++; + } + col = 0; + } + // ! Call operator for each elements in this row. 2d mat special version. + inline void rowCall2(const int row, const int COLS) const { + union Index{ + int body[2]; + operator const int*() const { + return reinterpret_cast(this); + } + int& operator[](const int i) { + return body[i]; + } + } idx = {{row, 0}}; + // Special union is needed to avoid + // "error: array subscript is above array bounds [-Werror=array-bounds]" + // when call the functor `op` such that access idx[3]. + + _Tp* pixel = &(mat->template at<_Tp>(idx)); + const _Tp* const pixel_end = pixel + COLS; + while(pixel < pixel_end) { + op(*pixel++, static_cast(idx)); + idx[1]++; + } + } + PixelOperationWrapper& operator=(const PixelOperationWrapper &) { + CV_Assert(false); + // We can not remove this implementation because Visual Studio warning C4822. + return *this; + } + }; + + parallel_for_(cv::Range(0, LINES), PixelOperationWrapper(reinterpret_cast*>(this), operation)); +} + +/////////////////////////// Synchronization Primitives /////////////////////////////// + +class CV_EXPORTS Mutex +{ +public: + Mutex(); + ~Mutex(); + Mutex(const Mutex& m); + Mutex& operator = (const Mutex& m); + + void lock(); + bool trylock(); + void unlock(); + + struct Impl; +protected: + Impl* impl; +}; + +class CV_EXPORTS AutoLock +{ +public: + AutoLock(Mutex& m) : mutex(&m) { mutex->lock(); } + ~AutoLock() { mutex->unlock(); } +protected: + Mutex* mutex; +private: + AutoLock(const AutoLock&); + AutoLock& operator = (const AutoLock&); +}; + + +/** @brief Designed for command line parsing + +The sample below demonstrates how to use CommandLineParser: +@code + CommandLineParser parser(argc, argv, keys); + parser.about("Application name v1.0.0"); + + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + int N = parser.get("N"); + double fps = parser.get("fps"); + String path = parser.get("path"); + + use_time_stamp = parser.has("timestamp"); + + String img1 = parser.get(0); + String img2 = parser.get(1); + + int repeat = parser.get(2); + + if (!parser.check()) + { + parser.printErrors(); + return 0; + } +@endcode + +### Keys syntax + +The keys parameter is a string containing several blocks, each one is enclosed in curly braces and +describes one argument. Each argument contains three parts separated by the `|` symbol: + +-# argument names is a space-separated list of option synonyms (to mark argument as positional, prefix it with the `@` symbol) +-# default value will be used if the argument was not provided (can be empty) +-# help message (can be empty) + +For example: + +@code{.cpp} + const String keys = + "{help h usage ? | | print this message }" + "{@image1 | | image1 for compare }" + "{@image2 || image2 for compare }" + "{@repeat |1 | number }" + "{path |. | path to file }" + "{fps | -1.0 | fps for output video }" + "{N count |100 | count of objects }" + "{ts timestamp | | use time stamp }" + ; +} +@endcode + +Note that there are no default values for `help` and `timestamp` so we can check their presence using the `has()` method. +Arguments with default values are considered to be always present. Use the `get()` method in these cases to check their +actual value instead. + +String keys like `get("@image1")` return the empty string `""` by default - even with an empty default value. +Use the special `` default value to enforce that the returned string must not be empty. (like in `get("@image2")`) + +### Usage + +For the described keys: + +@code{.sh} + # Good call (3 positional parameters: image1, image2 and repeat; N is 200, ts is true) + $ ./app -N=200 1.png 2.jpg 19 -ts + + # Bad call + $ ./app -fps=aaa + ERRORS: + Parameter 'fps': can not convert: [aaa] to [double] +@endcode + */ +class CV_EXPORTS CommandLineParser +{ +public: + + /** @brief Constructor + + Initializes command line parser object + + @param argc number of command line arguments (from main()) + @param argv array of command line arguments (from main()) + @param keys string describing acceptable command line parameters (see class description for syntax) + */ + CommandLineParser(int argc, const char* const argv[], const String& keys); + + /** @brief Copy constructor */ + CommandLineParser(const CommandLineParser& parser); + + /** @brief Assignment operator */ + CommandLineParser& operator = (const CommandLineParser& parser); + + /** @brief Destructor */ + ~CommandLineParser(); + + /** @brief Returns application path + + This method returns the path to the executable from the command line (`argv[0]`). + + For example, if the application has been started with such a command: + @code{.sh} + $ ./bin/my-executable + @endcode + this method will return `./bin`. + */ + String getPathToApplication() const; + + /** @brief Access arguments by name + + Returns argument converted to selected type. If the argument is not known or can not be + converted to selected type, the error flag is set (can be checked with @ref check). + + For example, define: + @code{.cpp} + String keys = "{N count||}"; + @endcode + + Call: + @code{.sh} + $ ./my-app -N=20 + # or + $ ./my-app --count=20 + @endcode + + Access: + @code{.cpp} + int N = parser.get("N"); + @endcode + + @param name name of the argument + @param space_delete remove spaces from the left and right of the string + @tparam T the argument will be converted to this type if possible + + @note You can access positional arguments by their `@`-prefixed name: + @code{.cpp} + parser.get("@image"); + @endcode + */ + template + T get(const String& name, bool space_delete = true) const + { + T val = T(); + getByName(name, space_delete, ParamType::type, (void*)&val); + return val; + } + + /** @brief Access positional arguments by index + + Returns argument converted to selected type. Indexes are counted from zero. + + For example, define: + @code{.cpp} + String keys = "{@arg1||}{@arg2||}" + @endcode + + Call: + @code{.sh} + ./my-app abc qwe + @endcode + + Access arguments: + @code{.cpp} + String val_1 = parser.get(0); // returns "abc", arg1 + String val_2 = parser.get(1); // returns "qwe", arg2 + @endcode + + @param index index of the argument + @param space_delete remove spaces from the left and right of the string + @tparam T the argument will be converted to this type if possible + */ + template + T get(int index, bool space_delete = true) const + { + T val = T(); + getByIndex(index, space_delete, ParamType::type, (void*)&val); + return val; + } + + /** @brief Check if field was provided in the command line + + @param name argument name to check + */ + bool has(const String& name) const; + + /** @brief Check for parsing errors + + Returns false if error occurred while accessing the parameters (bad conversion, missing arguments, + etc.). Call @ref printErrors to print error messages list. + */ + bool check() const; + + /** @brief Set the about message + + The about message will be shown when @ref printMessage is called, right before arguments table. + */ + void about(const String& message); + + /** @brief Print help message + + This method will print standard help message containing the about message and arguments description. + + @sa about + */ + void printMessage() const; + + /** @brief Print list of errors occurred + + @sa check + */ + void printErrors() const; + +protected: + void getByName(const String& name, bool space_delete, int type, void* dst) const; + void getByIndex(int index, bool space_delete, int type, void* dst) const; + + struct Impl; + Impl* impl; +}; + +//! @} core_utils + +//! @cond IGNORED + +/////////////////////////////// AutoBuffer implementation //////////////////////////////////////// + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer() +{ + ptr = buf; + sz = fixed_size; +} + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size) +{ + ptr = buf; + sz = fixed_size; + allocate(_size); +} + +template inline +AutoBuffer<_Tp, fixed_size>::AutoBuffer(const AutoBuffer<_Tp, fixed_size>& abuf ) +{ + ptr = buf; + sz = fixed_size; + allocate(abuf.size()); + for( size_t i = 0; i < sz; i++ ) + ptr[i] = abuf.ptr[i]; +} + +template inline AutoBuffer<_Tp, fixed_size>& +AutoBuffer<_Tp, fixed_size>::operator = (const AutoBuffer<_Tp, fixed_size>& abuf) +{ + if( this != &abuf ) + { + deallocate(); + allocate(abuf.size()); + for( size_t i = 0; i < sz; i++ ) + ptr[i] = abuf.ptr[i]; + } + return *this; +} + +template inline +AutoBuffer<_Tp, fixed_size>::~AutoBuffer() +{ deallocate(); } + +template inline void +AutoBuffer<_Tp, fixed_size>::allocate(size_t _size) +{ + if(_size <= sz) + { + sz = _size; + return; + } + deallocate(); + sz = _size; + if(_size > fixed_size) + { + ptr = new _Tp[_size]; + } +} + +template inline void +AutoBuffer<_Tp, fixed_size>::deallocate() +{ + if( ptr != buf ) + { + delete[] ptr; + ptr = buf; + sz = fixed_size; + } +} + +template inline void +AutoBuffer<_Tp, fixed_size>::resize(size_t _size) +{ + if(_size <= sz) + { + sz = _size; + return; + } + size_t i, prevsize = sz, minsize = MIN(prevsize, _size); + _Tp* prevptr = ptr; + + ptr = _size > fixed_size ? new _Tp[_size] : buf; + sz = _size; + + if( ptr != prevptr ) + for( i = 0; i < minsize; i++ ) + ptr[i] = prevptr[i]; + for( i = prevsize; i < _size; i++ ) + ptr[i] = _Tp(); + + if( prevptr != buf ) + delete[] prevptr; +} + +template inline size_t +AutoBuffer<_Tp, fixed_size>::size() const +{ return sz; } + +template<> inline std::string CommandLineParser::get(int index, bool space_delete) const +{ + return get(index, space_delete); +} +template<> inline std::string CommandLineParser::get(const String& name, bool space_delete) const +{ + return get(name, space_delete); +} + +//! @endcond + + +// Basic Node class for tree building +template +class CV_EXPORTS Node +{ +public: + Node() + { + m_pParent = 0; + } + Node(OBJECT& payload) : m_payload(payload) + { + m_pParent = 0; + } + ~Node() + { + removeChilds(); + if (m_pParent) + { + int idx = m_pParent->findChild(this); + if (idx >= 0) + m_pParent->m_childs.erase(m_pParent->m_childs.begin() + idx); + } + } + + Node* findChild(OBJECT& payload) const + { + for(size_t i = 0; i < this->m_childs.size(); i++) + { + if(this->m_childs[i]->m_payload == payload) + return this->m_childs[i]; + } + return NULL; + } + + int findChild(Node *pNode) const + { + for (size_t i = 0; i < this->m_childs.size(); i++) + { + if(this->m_childs[i] == pNode) + return (int)i; + } + return -1; + } + + void addChild(Node *pNode) + { + if(!pNode) + return; + + CV_Assert(pNode->m_pParent == 0); + pNode->m_pParent = this; + this->m_childs.push_back(pNode); + } + + void removeChilds() + { + for(size_t i = 0; i < m_childs.size(); i++) + { + m_childs[i]->m_pParent = 0; // avoid excessive parent vector trimming + delete m_childs[i]; + } + m_childs.clear(); + } + + int getDepth() + { + int count = 0; + Node *pParent = m_pParent; + while(pParent) count++, pParent = pParent->m_pParent; + return count; + } + +public: + OBJECT m_payload; + Node* m_pParent; + std::vector*> m_childs; +}; + + +namespace samples { + +//! @addtogroup core_utils_samples +// This section describes utility functions for OpenCV samples. +// +// @note Implementation of these utilities is not thread-safe. +// +//! @{ + +/** @brief Try to find requested data file + +Search directories: + +1. Directories passed via `addSamplesDataSearchPath()` +2. OPENCV_SAMPLES_DATA_PATH_HINT environment variable +3. OPENCV_SAMPLES_DATA_PATH environment variable + If parameter value is not empty and nothing is found then stop searching. +4. Detects build/install path based on: + a. current working directory (CWD) + b. and/or binary module location (opencv_core/opencv_world, doesn't work with static linkage) +5. Scan `/{,data,samples/data}` directories if build directory is detected or the current directory is in source tree. +6. Scan `/share/OpenCV` directory if install directory is detected. + +@see cv::utils::findDataFile + +@param relative_path Relative path to data file +@param required Specify "file not found" handling. + If true, function prints information message and raises cv::Exception. + If false, function returns empty result +@param silentMode Disables messages +@return Returns path (absolute or relative to the current directory) or empty string if file is not found +*/ +CV_EXPORTS_W cv::String findFile(const cv::String& relative_path, bool required = true, bool silentMode = false); + +CV_EXPORTS_W cv::String findFileOrKeep(const cv::String& relative_path, bool silentMode = false); + +inline cv::String findFileOrKeep(const cv::String& relative_path, bool silentMode) +{ + cv::String res = findFile(relative_path, false, silentMode); + if (res.empty()) + return relative_path; + return res; +} + +/** @brief Override search data path by adding new search location + +Use this only to override default behavior +Passed paths are used in LIFO order. + +@param path Path to used samples data +*/ +CV_EXPORTS_W void addSamplesDataSearchPath(const cv::String& path); + +/** @brief Append samples search data sub directory + +General usage is to add OpenCV modules name (`/modules//samples/data` -> `/samples/data` + `modules//samples/data`). +Passed subdirectories are used in LIFO order. + +@param subdir samples data sub directory +*/ +CV_EXPORTS_W void addSamplesDataSearchSubDirectory(const cv::String& subdir); + +//! @} +} // namespace samples + +namespace utils { + +CV_EXPORTS int getThreadID(); + +} // namespace + +} //namespace cv + +#ifdef CV_COLLECT_IMPL_DATA +#include "opencv2/core/utils/instrumentation.hpp" +#else +/// Collect implementation data on OpenCV function call. Requires ENABLE_IMPL_COLLECTION build option. +#define CV_IMPL_ADD(impl) +#endif + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/core/core_c.h" +#endif + +#endif //OPENCV_CORE_UTILITY_H diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.hpp new file mode 100644 index 0000000..79e9338 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.hpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ALLOCATOR_STATS_HPP +#define OPENCV_CORE_ALLOCATOR_STATS_HPP + +#include "../cvdef.h" + +namespace cv { namespace utils { + +class AllocatorStatisticsInterface +{ +protected: + AllocatorStatisticsInterface() {} + virtual ~AllocatorStatisticsInterface() {} +public: + virtual uint64_t getCurrentUsage() const = 0; + virtual uint64_t getTotalUsage() const = 0; + virtual uint64_t getNumberOfAllocations() const = 0; + virtual uint64_t getPeakUsage() const = 0; + + /** set peak usage = current usage */ + virtual void resetPeakUsage() = 0; +}; + +}} // namespace + +#endif // OPENCV_CORE_ALLOCATOR_STATS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.impl.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.impl.hpp new file mode 100644 index 0000000..eb5ecde --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/allocator_stats.impl.hpp @@ -0,0 +1,158 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP +#define OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP + +#include "./allocator_stats.hpp" + +//#define OPENCV_DISABLE_ALLOCATOR_STATS + +#ifdef CV_CXX11 + +#include + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#if defined(__GNUC__) && (\ + (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4) || \ + (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + ) +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE int +#endif +#endif + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE long long +#endif + +#else // CV_CXX11 + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE int // CV_XADD supports int only +#endif + +#endif // CV_CXX11 + +namespace cv { namespace utils { + +#ifdef CV__ALLOCATOR_STATS_LOG +namespace { +#endif + +class AllocatorStatistics : public AllocatorStatisticsInterface +{ +#ifdef OPENCV_DISABLE_ALLOCATOR_STATS + +public: + AllocatorStatistics() {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return 0; } + uint64_t getTotalUsage() const CV_OVERRIDE { return 0; } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return 0; } + uint64_t getPeakUsage() const CV_OVERRIDE { return 0; } + + /** set peak usage = current usage */ + void resetPeakUsage() CV_OVERRIDE {}; + + void onAllocate(size_t /*sz*/) {} + void onFree(size_t /*sz*/) {} + +#elif defined(CV_CXX11) + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + std::atomic curr, total, total_allocs, peak; +public: + AllocatorStatistics() {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr.load(); } + uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total.load(); } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs.load(); } + uint64_t getPeakUsage() const CV_OVERRIDE { return (uint64_t)peak.load(); } + + /** set peak usage = current usage */ + void resetPeakUsage() CV_OVERRIDE { peak.store(curr.load()); } + + // Controller interface + void onAllocate(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); +#endif + + counter_t new_curr = curr.fetch_add((counter_t)sz) + (counter_t)sz; + + // peak = std::max((uint64_t)peak, new_curr); + auto prev_peak = peak.load(); + while (prev_peak < new_curr) + { + if (peak.compare_exchange_weak(prev_peak, new_curr)) + break; + } + // end of peak = max(...) + + total += (counter_t)sz; + total_allocs++; + } + void onFree(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); +#endif + curr -= (counter_t)sz; + } + +#else // non C++11 + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + volatile counter_t curr, total, total_allocs, peak; // overflow is possible, CV_XADD operates with 'int' only +public: + AllocatorStatistics() + : curr(0), total(0), total_allocs(0), peak(0) + {} + ~AllocatorStatistics() CV_OVERRIDE {} + + uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr; } + uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total; } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs; } + uint64_t getPeakUsage() const CV_OVERRIDE { return (uint64_t)peak; } + + void resetPeakUsage() CV_OVERRIDE { peak = curr; } + + // Controller interface + void onAllocate(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); +#endif + + counter_t new_curr = (counter_t)CV_XADD(&curr, (counter_t)sz) + (counter_t)sz; + + peak = std::max((counter_t)peak, new_curr); // non-thread safe + + //CV_XADD(&total, (uint64_t)sz); // overflow with int, non-reliable... + total += sz; + + CV_XADD(&total_allocs, (counter_t)1); + } + void onFree(size_t sz) + { +#ifdef CV__ALLOCATOR_STATS_LOG + CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); +#endif + CV_XADD(&curr, (counter_t)-sz); + } +#endif +}; + +#ifdef CV__ALLOCATOR_STATS_LOG +} // namespace +#endif + +}} // namespace + +#endif // OPENCV_CORE_ALLOCATOR_STATS_IMPL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/filesystem.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/filesystem.hpp new file mode 100644 index 0000000..00b0dd1 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/filesystem.hpp @@ -0,0 +1,78 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_FILESYSTEM_HPP +#define OPENCV_UTILS_FILESYSTEM_HPP + +namespace cv { namespace utils { namespace fs { + + +CV_EXPORTS bool exists(const cv::String& path); +CV_EXPORTS bool isDirectory(const cv::String& path); + +CV_EXPORTS void remove_all(const cv::String& path); + + +CV_EXPORTS cv::String getcwd(); + +/** @brief Converts path p to a canonical absolute path + * Symlinks are processed if there is support for them on running platform. + * + * @param path input path. Target file/directory should exist. + */ +CV_EXPORTS cv::String canonical(const cv::String& path); + +/** Join path components */ +CV_EXPORTS cv::String join(const cv::String& base, const cv::String& path); + +/** + * Generate a list of all files that match the globbing pattern. + * + * Result entries are prefixed by base directory path. + * + * @param directory base directory + * @param pattern filter pattern (based on '*'/'?' symbols). Use empty string to disable filtering and return all results + * @param[out] result result of globing. + * @param recursive scan nested directories too + * @param includeDirectories include directories into results list + */ +CV_EXPORTS void glob(const cv::String& directory, const cv::String& pattern, + CV_OUT std::vector& result, + bool recursive = false, bool includeDirectories = false); + +/** + * Generate a list of all files that match the globbing pattern. + * + * @param directory base directory + * @param pattern filter pattern (based on '*'/'?' symbols). Use empty string to disable filtering and return all results + * @param[out] result globbing result with relative paths from base directory + * @param recursive scan nested directories too + * @param includeDirectories include directories into results list + */ +CV_EXPORTS void glob_relative(const cv::String& directory, const cv::String& pattern, + CV_OUT std::vector& result, + bool recursive = false, bool includeDirectories = false); + + +CV_EXPORTS bool createDirectory(const cv::String& path); +CV_EXPORTS bool createDirectories(const cv::String& path); + +#ifdef __OPENCV_BUILD +// TODO +//CV_EXPORTS cv::String getTempDirectory(); + +/** + * @brief Returns directory to store OpenCV cache files + * Create sub-directory in common OpenCV cache directory if it doesn't exist. + * @param sub_directory_name name of sub-directory. NULL or "" value asks to return root cache directory. + * @param configuration_name optional name of configuration parameter name which overrides default behavior. + * @return Path to cache directory. Returns empty string if cache directories support is not available. Returns "disabled" if cache disabled by user. + */ +CV_EXPORTS cv::String getCacheDirectory(const char* sub_directory_name, const char* configuration_name = NULL); + +#endif + +}}} // namespace + +#endif // OPENCV_UTILS_FILESYSTEM_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/instrumentation.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/instrumentation.hpp new file mode 100644 index 0000000..3639867 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/instrumentation.hpp @@ -0,0 +1,125 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_INSTR_HPP +#define OPENCV_UTILS_INSTR_HPP + +#include +#include + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +#ifdef CV_COLLECT_IMPL_DATA +CV_EXPORTS void setImpl(int flags); // set implementation flags and reset storage arrays +CV_EXPORTS void addImpl(int flag, const char* func = 0); // add implementation and function name to storage arrays +// Get stored implementation flags and functions names arrays +// Each implementation entry correspond to function name entry, so you can find which implementation was executed in which function +CV_EXPORTS int getImpl(std::vector &impl, std::vector &funName); + +CV_EXPORTS bool useCollection(); // return implementation collection state +CV_EXPORTS void setUseCollection(bool flag); // set implementation collection state + +#define CV_IMPL_PLAIN 0x01 // native CPU OpenCV implementation +#define CV_IMPL_OCL 0x02 // OpenCL implementation +#define CV_IMPL_IPP 0x04 // IPP implementation +#define CV_IMPL_MT 0x10 // multithreaded implementation + +#undef CV_IMPL_ADD +#define CV_IMPL_ADD(impl) \ + if(cv::useCollection()) \ + { \ + cv::addImpl(impl, CV_Func); \ + } +#endif + +// Instrumentation external interface +namespace instr +{ + +#if !defined OPENCV_ABI_CHECK + +enum TYPE +{ + TYPE_GENERAL = 0, // OpenCV API function, e.g. exported function + TYPE_MARKER, // Information marker + TYPE_WRAPPER, // Wrapper function for implementation + TYPE_FUN, // Simple function call +}; + +enum IMPL +{ + IMPL_PLAIN = 0, + IMPL_IPP, + IMPL_OPENCL, +}; + +struct NodeDataTls +{ + NodeDataTls() + { + m_ticksTotal = 0; + } + uint64 m_ticksTotal; +}; + +class CV_EXPORTS NodeData +{ +public: + NodeData(const char* funName = 0, const char* fileName = NULL, int lineNum = 0, void* retAddress = NULL, bool alwaysExpand = false, cv::instr::TYPE instrType = TYPE_GENERAL, cv::instr::IMPL implType = IMPL_PLAIN); + NodeData(NodeData &ref); + ~NodeData(); + NodeData& operator=(const NodeData&); + + cv::String m_funName; + cv::instr::TYPE m_instrType; + cv::instr::IMPL m_implType; + const char* m_fileName; + int m_lineNum; + void* m_retAddress; + bool m_alwaysExpand; + bool m_funError; + + volatile int m_counter; + volatile uint64 m_ticksTotal; + TLSDataAccumulator m_tls; + int m_threads; + + // No synchronization + double getTotalMs() const { return ((double)m_ticksTotal / cv::getTickFrequency()) * 1000; } + double getMeanMs() const { return (((double)m_ticksTotal/m_counter) / cv::getTickFrequency()) * 1000; } +}; +bool operator==(const NodeData& lhs, const NodeData& rhs); + +typedef Node InstrNode; + +CV_EXPORTS InstrNode* getTrace(); + +#endif // !defined OPENCV_ABI_CHECK + + +CV_EXPORTS bool useInstrumentation(); +CV_EXPORTS void setUseInstrumentation(bool flag); +CV_EXPORTS void resetTrace(); + +enum FLAGS +{ + FLAGS_NONE = 0, + FLAGS_MAPPING = 0x01, + FLAGS_EXPAND_SAME_NAMES = 0x02, +}; + +CV_EXPORTS void setFlags(FLAGS modeFlags); +static inline void setFlags(int modeFlags) { setFlags((FLAGS)modeFlags); } +CV_EXPORTS FLAGS getFlags(); + +} // namespace instr + +//! @} + +} // namespace + +#endif // OPENCV_UTILS_TLS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.defines.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.defines.hpp new file mode 100644 index 0000000..b2dfc41 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.defines.hpp @@ -0,0 +1,22 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_LOGGER_DEFINES_HPP +#define OPENCV_LOGGER_DEFINES_HPP + +//! @addtogroup core_logging +//! @{ + +// Supported logging levels and their semantic +#define CV_LOG_LEVEL_SILENT 0 //!< for using in setLogLevel() call +#define CV_LOG_LEVEL_FATAL 1 //!< Fatal (critical) error (unrecoverable internal error) +#define CV_LOG_LEVEL_ERROR 2 //!< Error message +#define CV_LOG_LEVEL_WARN 3 //!< Warning message +#define CV_LOG_LEVEL_INFO 4 //!< Info message +#define CV_LOG_LEVEL_DEBUG 5 //!< Debug message. Disabled in the "Release" build. +#define CV_LOG_LEVEL_VERBOSE 6 //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build. + +//! @} + +#endif // OPENCV_LOGGER_DEFINES_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.hpp new file mode 100644 index 0000000..501d140 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/logger.hpp @@ -0,0 +1,159 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_LOGGER_HPP +#define OPENCV_LOGGER_HPP + +#include +#include +#include // INT_MAX + +#include "logger.defines.hpp" + +namespace cv { +namespace utils { +namespace logging { + +//! @addtogroup core_logging +//! @{ + +//! Supported logging levels and their semantic +enum LogLevel { + LOG_LEVEL_SILENT = 0, //!< for using in setLogVevel() call + LOG_LEVEL_FATAL = 1, //!< Fatal (critical) error (unrecoverable internal error) + LOG_LEVEL_ERROR = 2, //!< Error message + LOG_LEVEL_WARNING = 3, //!< Warning message + LOG_LEVEL_INFO = 4, //!< Info message + LOG_LEVEL_DEBUG = 5, //!< Debug message. Disabled in the "Release" build. + LOG_LEVEL_VERBOSE = 6, //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build. +#ifndef CV_DOXYGEN + ENUM_LOG_LEVEL_FORCE_INT = INT_MAX +#endif +}; + +/** Set global logging level +@return previous logging level +*/ +CV_EXPORTS LogLevel setLogLevel(LogLevel logLevel); +/** Get global logging level */ +CV_EXPORTS LogLevel getLogLevel(); + +namespace internal { +/** Write log message */ +CV_EXPORTS void writeLogMessage(LogLevel logLevel, const char* message); +} // namespace + +/** + * \def CV_LOG_STRIP_LEVEL + * + * Define CV_LOG_STRIP_LEVEL=CV_LOG_LEVEL_[DEBUG|INFO|WARN|ERROR|FATAL|DISABLED] to compile out anything at that and before that logging level + */ +#ifndef CV_LOG_STRIP_LEVEL +# if defined NDEBUG +# define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG +# else +# define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE +# endif +#endif + +#define CV_LOG_WITH_TAG(tag, msgLevel, extra_check0, extra_check1, msg_prefix, ...) \ + for(;;) { \ + extra_check0; \ + if (cv::utils::logging::getLogLevel() < msgLevel) break; \ + extra_check1; \ + std::stringstream ss; ss msg_prefix << __VA_ARGS__; \ + cv::utils::logging::internal::writeLogMessage(msgLevel, ss.str().c_str()); \ + break; \ + } + +#define CV_LOG_FATAL(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_FATAL, , , , __VA_ARGS__) +#define CV_LOG_ERROR(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, , , , __VA_ARGS__) +#define CV_LOG_WARNING(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, , , , __VA_ARGS__) +#define CV_LOG_INFO(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, , , , __VA_ARGS__) +#define CV_LOG_DEBUG(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, , , , __VA_ARGS__) +#define CV_LOG_VERBOSE(tag, v, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, , , << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_INFO +#define CV_LOG_INFO(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_DEBUG +#define CV_LOG_DEBUG(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_VERBOSE +#define CV_LOG_VERBOSE(tag, v, ...) +#endif + +//! @cond IGNORED +#define CV__LOG_ONCE_CHECK_PRE \ + static bool _cv_log_once_ ## __LINE__ = false; \ + if (_cv_log_once_ ## __LINE__) break; + +#define CV__LOG_ONCE_CHECK_POST \ + _cv_log_once_ ## __LINE__ = true; + +#define CV__LOG_IF_CHECK(logging_cond) \ + if (!(logging_cond)) break; + +//! @endcond + + +// CV_LOG_ONCE_XXX macros + +#define CV_LOG_ONCE_ERROR(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_WARNING(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_INFO(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_DEBUG(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, , __VA_ARGS__) +#define CV_LOG_ONCE_VERBOSE(tag, v, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, CV__LOG_ONCE_CHECK_PRE, CV__LOG_ONCE_CHECK_POST, << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_ONCE_INFO +#define CV_LOG_ONCE_INFO(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_ONCE_DEBUG +#define CV_LOG_ONCE_DEBUG(tag, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_ONCE_VERBOSE +#define CV_LOG_ONCE_VERBOSE(tag, v, ...) +#endif + + +// CV_LOG_IF_XXX macros + +#define CV_LOG_IF_FATAL(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_FATAL, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_ERROR(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_WARNING(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_INFO(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_DEBUG(tag, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, , CV__LOG_IF_CHECK(logging_cond), , __VA_ARGS__) +#define CV_LOG_IF_VERBOSE(tag, v, logging_cond, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_VERBOSE, , CV__LOG_IF_CHECK(logging_cond), << "[VERB" << v << ":" << cv::utils::getThreadID() << "] ", __VA_ARGS__) + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO +#undef CV_LOG_IF_INFO +#define CV_LOG_IF_INFO(tag, logging_cond, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG +#undef CV_LOG_IF_DEBUG +#define CV_LOG_IF_DEBUG(tag, logging_cond, ...) +#endif + +#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE +#undef CV_LOG_IF_VERBOSE +#define CV_LOG_IF_VERBOSE(tag, v, logging_cond, ...) +#endif + + +//! @} + +}}} // namespace + +#endif // OPENCV_LOGGER_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/tls.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/tls.hpp new file mode 100644 index 0000000..c0d2962 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/tls.hpp @@ -0,0 +1,239 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_UTILS_TLS_HPP +#define OPENCV_UTILS_TLS_HPP + +#ifndef OPENCV_CORE_UTILITY_H +#error "tls.hpp must be included after opencv2/core/utility.hpp or opencv2/core.hpp" +#endif + +namespace cv { + +//! @addtogroup core_utils +//! @{ + +namespace details { class TlsStorage; } + +/** TLS container base implementation + * + * Don't use directly. + * + * @sa TLSData, TLSDataAccumulator templates + */ +class CV_EXPORTS TLSDataContainer +{ +protected: + TLSDataContainer(); + virtual ~TLSDataContainer(); + + /// @deprecated use detachData() instead + void gatherData(std::vector &data) const; + /// get TLS data and detach all data from threads (similar to cleanup() call) + void detachData(std::vector& data); + + void* getData() const; + void release(); + +protected: + virtual void* createDataInstance() const = 0; + virtual void deleteDataInstance(void* pData) const = 0; + +#if OPENCV_ABI_COMPATIBILITY > 300 +private: +#else +public: +#endif + int key_; + + friend class cv::details::TlsStorage; // core/src/system.cpp + +public: + void cleanup(); //!< Release created TLS data container objects. It is similar to release() call, but it keeps TLS container valid. + +private: + // Disable copy/assign (noncopyable pattern) + TLSDataContainer(TLSDataContainer &); + TLSDataContainer& operator =(const TLSDataContainer &); +}; + + +/** @brief Simple TLS data class + * + * @sa TLSDataAccumulator + */ +template +class TLSData : protected TLSDataContainer +{ +public: + inline TLSData() {} + inline ~TLSData() { release(); } + + inline T* get() const { return (T*)getData(); } //!< Get data associated with key + inline T& getRef() const { T* ptr = (T*)getData(); CV_DbgAssert(ptr); return *ptr; } //!< Get data associated with key + + /// Release associated thread data + inline void cleanup() + { + TLSDataContainer::cleanup(); + } + +protected: + /// Wrapper to allocate data by template + virtual void* createDataInstance() const CV_OVERRIDE { return new T; } + /// Wrapper to release data by template + virtual void deleteDataInstance(void* pData) const CV_OVERRIDE { delete (T*)pData; } +}; + + +/// TLS data accumulator with gathering methods +template +class TLSDataAccumulator : public TLSData +{ + mutable cv::Mutex mutex; + mutable std::vector dataFromTerminatedThreads; + std::vector detachedData; + bool cleanupMode; +public: + TLSDataAccumulator() : cleanupMode(false) {} + ~TLSDataAccumulator() + { + release(); + } + + /** @brief Get data from all threads + * @deprecated replaced by detachData() + * + * Lifetime of vector data is valid until next detachData()/cleanup()/release() calls + * + * @param[out] data result buffer (should be empty) + */ + void gather(std::vector &data) const + { + CV_Assert(cleanupMode == false); // state is not valid + CV_Assert(data.empty()); + { + std::vector &dataVoid = reinterpret_cast&>(data); + TLSDataContainer::gatherData(dataVoid); + } + { + AutoLock lock(mutex); + data.reserve(data.size() + dataFromTerminatedThreads.size()); + for (typename std::vector::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + data.push_back((T*)*i); + } + } + } + + /** @brief Get and detach data from all threads + * + * Call cleanupDetachedData() when returned vector is not needed anymore. + * + * @return Vector with associated data. Content is preserved (including lifetime of attached data pointers) until next detachData()/cleanupDetachedData()/cleanup()/release() calls + */ + std::vector& detachData() + { + CV_Assert(cleanupMode == false); // state is not valid + std::vector dataVoid; + { + TLSDataContainer::detachData(dataVoid); + } + { + AutoLock lock(mutex); + detachedData.reserve(dataVoid.size() + dataFromTerminatedThreads.size()); + for (typename std::vector::const_iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + detachedData.push_back((T*)*i); + } + dataFromTerminatedThreads.clear(); + for (typename std::vector::const_iterator i = dataVoid.begin(); i != dataVoid.end(); ++i) + { + detachedData.push_back((T*)(void*)*i); + } + } + dataVoid.clear(); + return detachedData; + } + + /// Release associated thread data returned by detachData() call + void cleanupDetachedData() + { + AutoLock lock(mutex); + cleanupMode = true; + _cleanupDetachedData(); + cleanupMode = false; + } + + /// Release associated thread data + void cleanup() + { + cleanupMode = true; + TLSDataContainer::cleanup(); + + AutoLock lock(mutex); + _cleanupDetachedData(); + _cleanupTerminatedData(); + cleanupMode = false; + } + + /// Release associated thread data and free TLS key + void release() + { + cleanupMode = true; + TLSDataContainer::release(); + { + AutoLock lock(mutex); + _cleanupDetachedData(); + _cleanupTerminatedData(); + } + } + +protected: + // synchronized + void _cleanupDetachedData() + { + for (typename std::vector::iterator i = detachedData.begin(); i != detachedData.end(); ++i) + { + deleteDataInstance((T*)*i); + } + detachedData.clear(); + } + + // synchronized + void _cleanupTerminatedData() + { + for (typename std::vector::iterator i = dataFromTerminatedThreads.begin(); i != dataFromTerminatedThreads.end(); ++i) + { + deleteDataInstance((T*)*i); + } + dataFromTerminatedThreads.clear(); + } + +protected: + virtual void* createDataInstance() const CV_OVERRIDE + { + // Note: we can collect all allocated data here, but this would require raced mutex locks + return new T; + } + virtual void deleteDataInstance(void* pData) const CV_OVERRIDE + { + if (cleanupMode) + { + delete (T*)pData; + } + else + { + AutoLock lock(mutex); + dataFromTerminatedThreads.push_back((T*)pData); + } + } +}; + + +//! @} + +} // namespace + +#endif // OPENCV_UTILS_TLS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/trace.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/trace.hpp new file mode 100644 index 0000000..ef5d35b --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/utils/trace.hpp @@ -0,0 +1,252 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_TRACE_HPP +#define OPENCV_TRACE_HPP + +#include + +namespace cv { +namespace utils { +namespace trace { + +//! @addtogroup core_logging +//! @{ + +//! Macro to trace function +#define CV_TRACE_FUNCTION() + +#define CV_TRACE_FUNCTION_SKIP_NESTED() + +//! Trace code scope. +//! @note Dynamic names are not supported in this macro (on stack or heap). Use string literals here only, like "initialize". +#define CV_TRACE_REGION(name_as_static_string_literal) +//! mark completed of the current opened region and create new one +//! @note Dynamic names are not supported in this macro (on stack or heap). Use string literals here only, like "step1". +#define CV_TRACE_REGION_NEXT(name_as_static_string_literal) + +//! Macro to trace argument value +#define CV_TRACE_ARG(arg_id) + +//! Macro to trace argument value (expanded version) +#define CV_TRACE_ARG_VALUE(arg_id, arg_name, value) + +//! @cond IGNORED +#define CV_TRACE_NS cv::utils::trace + +#if !defined(OPENCV_DISABLE_TRACE) && defined(__EMSCRIPTEN__) +#define OPENCV_DISABLE_TRACE 1 +#endif + +namespace details { + +#ifndef __OPENCV_TRACE +# if defined __OPENCV_BUILD && !defined __OPENCV_TESTS && !defined __OPENCV_APPS +# define __OPENCV_TRACE 1 +# else +# define __OPENCV_TRACE 0 +# endif +#endif + +#ifndef CV_TRACE_FILENAME +# define CV_TRACE_FILENAME __FILE__ +#endif + +#ifndef CV__TRACE_FUNCTION +# if defined _MSC_VER +# define CV__TRACE_FUNCTION __FUNCSIG__ +# elif defined __GNUC__ +# define CV__TRACE_FUNCTION __PRETTY_FUNCTION__ +# else +# define CV__TRACE_FUNCTION "" +# endif +#endif + +//! Thread-local instance (usually allocated on stack) +class CV_EXPORTS Region +{ +public: + struct LocationExtraData; + struct LocationStaticStorage + { + LocationExtraData** ppExtra; //< implementation specific data + const char* name; //< region name (function name or other custom name) + const char* filename; //< source code filename + int line; //< source code line + int flags; //< flags (implementation code path: Plain, IPP, OpenCL) + }; + + Region(const LocationStaticStorage& location); + inline ~Region() + { + if (implFlags != 0) + destroy(); + CV_DbgAssert(implFlags == 0); + CV_DbgAssert(pImpl == NULL); + } + + class Impl; + Impl* pImpl; // NULL if current region is not active + int implFlags; // see RegionFlag, 0 if region is ignored + + bool isActive() const { return pImpl != NULL; } + + void destroy(); +private: + Region(const Region&); // disabled + Region& operator= (const Region&); // disabled +}; + +//! Specify region flags +enum RegionLocationFlag { + REGION_FLAG_FUNCTION = (1 << 0), //< region is function (=1) / nested named region (=0) + REGION_FLAG_APP_CODE = (1 << 1), //< region is Application code (=1) / OpenCV library code (=0) + REGION_FLAG_SKIP_NESTED = (1 << 2), //< avoid processing of nested regions + + REGION_FLAG_IMPL_IPP = (1 << 16), //< region is part of IPP code path + REGION_FLAG_IMPL_OPENCL = (2 << 16), //< region is part of OpenCL code path + REGION_FLAG_IMPL_OPENVX = (3 << 16), //< region is part of OpenVX code path + + REGION_FLAG_IMPL_MASK = (15 << 16), + + REGION_FLAG_REGION_FORCE = (1 << 30), + REGION_FLAG_REGION_NEXT = (1 << 31), //< close previous region (see #CV_TRACE_REGION_NEXT macro) + + ENUM_REGION_FLAG_FORCE_INT = INT_MAX +}; + +struct CV_EXPORTS TraceArg { +public: + struct ExtraData; + ExtraData** ppExtra; + const char* name; + int flags; +}; +/** @brief Add meta information to current region (function) + * See CV_TRACE_ARG macro + * @param arg argument information structure (global static cache) + * @param value argument value (can by dynamic string literal in case of string, static allocation is not required) + */ +CV_EXPORTS void traceArg(const TraceArg& arg, const char* value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, int value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, int64 value); +//! @overload +CV_EXPORTS void traceArg(const TraceArg& arg, double value); + +#define CV__TRACE_LOCATION_VARNAME(loc_id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_trace_location_, loc_id), __LINE__) +#define CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id) CVAUX_CONCAT(CVAUX_CONCAT(__cv_trace_location_extra_, loc_id) , __LINE__) + +#define CV__TRACE_DEFINE_LOCATION_(loc_id, name, flags) \ + static CV_TRACE_NS::details::Region::LocationExtraData* CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id) = 0; \ + static const CV_TRACE_NS::details::Region::LocationStaticStorage \ + CV__TRACE_LOCATION_VARNAME(loc_id) = { &(CV__TRACE_LOCATION_EXTRA_VARNAME(loc_id)), name, CV_TRACE_FILENAME, __LINE__, flags}; + +#define CV__TRACE_DEFINE_LOCATION_FN(name, flags) CV__TRACE_DEFINE_LOCATION_(fn, name, ((flags) | CV_TRACE_NS::details::REGION_FLAG_FUNCTION)) + + +#define CV__TRACE_OPENCV_FUNCTION() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, 0); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_OPENCV_FUNCTION_NAME(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, 0); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION_NAME(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + + +#define CV__TRACE_OPENCV_FUNCTION_SKIP_NESTED() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_OPENCV_FUNCTION_NAME_SKIP_NESTED(name) \ + CV__TRACE_DEFINE_LOCATION_FN(name, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + +#define CV__TRACE_APP_FUNCTION_SKIP_NESTED() \ + CV__TRACE_DEFINE_LOCATION_FN(CV__TRACE_FUNCTION, CV_TRACE_NS::details::REGION_FLAG_SKIP_NESTED | CV_TRACE_NS::details::REGION_FLAG_APP_CODE); \ + const CV_TRACE_NS::details::Region __region_fn(CV__TRACE_LOCATION_VARNAME(fn)); + + +#define CV__TRACE_REGION_(name_as_static_string_literal, flags) \ + CV__TRACE_DEFINE_LOCATION_(region, name_as_static_string_literal, flags); \ + CV_TRACE_NS::details::Region CVAUX_CONCAT(__region_, __LINE__)(CV__TRACE_LOCATION_VARNAME(region)); + +#define CV__TRACE_REGION(name_as_static_string_literal) CV__TRACE_REGION_(name_as_static_string_literal, 0) +#define CV__TRACE_REGION_NEXT(name_as_static_string_literal) CV__TRACE_REGION_(name_as_static_string_literal, CV_TRACE_NS::details::REGION_FLAG_REGION_NEXT) + +#define CV__TRACE_ARG_VARNAME(arg_id) CVAUX_CONCAT(__cv_trace_arg_ ## arg_id, __LINE__) +#define CV__TRACE_ARG_EXTRA_VARNAME(arg_id) CVAUX_CONCAT(__cv_trace_arg_extra_ ## arg_id, __LINE__) + +#define CV__TRACE_DEFINE_ARG_(arg_id, name, flags) \ + static CV_TRACE_NS::details::TraceArg::ExtraData* CV__TRACE_ARG_EXTRA_VARNAME(arg_id) = 0; \ + static const CV_TRACE_NS::details::TraceArg \ + CV__TRACE_ARG_VARNAME(arg_id) = { &(CV__TRACE_ARG_EXTRA_VARNAME(arg_id)), name, flags }; + +#define CV__TRACE_ARG_VALUE(arg_id, arg_name, value) \ + CV__TRACE_DEFINE_ARG_(arg_id, arg_name, 0); \ + CV_TRACE_NS::details::traceArg((CV__TRACE_ARG_VARNAME(arg_id)), value); + +#define CV__TRACE_ARG(arg_id) CV_TRACE_ARG_VALUE(arg_id, #arg_id, (arg_id)) + +} // namespace + +#ifndef OPENCV_DISABLE_TRACE +#undef CV_TRACE_FUNCTION +#undef CV_TRACE_FUNCTION_SKIP_NESTED +#if __OPENCV_TRACE +#define CV_TRACE_FUNCTION CV__TRACE_OPENCV_FUNCTION +#define CV_TRACE_FUNCTION_SKIP_NESTED CV__TRACE_OPENCV_FUNCTION_SKIP_NESTED +#else +#define CV_TRACE_FUNCTION CV__TRACE_APP_FUNCTION +#define CV_TRACE_FUNCTION_SKIP_NESTED CV__TRACE_APP_FUNCTION_SKIP_NESTED +#endif + +#undef CV_TRACE_REGION +#define CV_TRACE_REGION CV__TRACE_REGION + +#undef CV_TRACE_REGION_NEXT +#define CV_TRACE_REGION_NEXT CV__TRACE_REGION_NEXT + +#undef CV_TRACE_ARG_VALUE +#define CV_TRACE_ARG_VALUE(arg_id, arg_name, value) \ + if (__region_fn.isActive()) \ + { \ + CV__TRACE_ARG_VALUE(arg_id, arg_name, value); \ + } + +#undef CV_TRACE_ARG +#define CV_TRACE_ARG CV__TRACE_ARG + +#endif // OPENCV_DISABLE_TRACE + +#ifdef OPENCV_TRACE_VERBOSE +#define CV_TRACE_FUNCTION_VERBOSE CV_TRACE_FUNCTION +#define CV_TRACE_REGION_VERBOSE CV_TRACE_REGION +#define CV_TRACE_REGION_NEXT_VERBOSE CV_TRACE_REGION_NEXT +#define CV_TRACE_ARG_VALUE_VERBOSE CV_TRACE_ARG_VALUE +#define CV_TRACE_ARG_VERBOSE CV_TRACE_ARG +#else +#define CV_TRACE_FUNCTION_VERBOSE(...) +#define CV_TRACE_REGION_VERBOSE(...) +#define CV_TRACE_REGION_NEXT_VERBOSE(...) +#define CV_TRACE_ARG_VALUE_VERBOSE(...) +#define CV_TRACE_ARG_VERBOSE(...) +#endif + +//! @endcond + +//! @} + +}}} // namespace + +#endif // OPENCV_TRACE_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/va_intel.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/va_intel.hpp new file mode 100644 index 0000000..f665470 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/va_intel.hpp @@ -0,0 +1,78 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2015, Itseez, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_CORE_VA_INTEL_HPP +#define OPENCV_CORE_VA_INTEL_HPP + +#ifndef __cplusplus +# error va_intel.hpp header must be compiled as C++ +#endif + +#include "opencv2/core.hpp" +#include "ocl.hpp" + +#if defined(HAVE_VA) +# include "va/va.h" +#else // HAVE_VA +# if !defined(_VA_H_) + typedef void* VADisplay; + typedef unsigned int VASurfaceID; +# endif // !_VA_H_ +#endif // HAVE_VA + +namespace cv { namespace va_intel { + +/** @addtogroup core_va_intel +This section describes Intel VA-API/OpenCL (CL-VA) interoperability. + +To enable CL-VA interoperability support, configure OpenCV using CMake with WITH_VA_INTEL=ON . Currently VA-API is +supported on Linux only. You should also install Intel Media Server Studio (MSS) to use this feature. You may +have to specify the path(s) to MSS components for cmake in environment variables: + +- VA_INTEL_IOCL_ROOT for Intel OpenCL (default is "/opt/intel/opencl"). + +To use CL-VA interoperability you should first create VADisplay (libva), and then call initializeContextFromVA() +function to create OpenCL context and set up interoperability. +*/ +//! @{ + +/////////////////// CL-VA Interoperability Functions /////////////////// + +namespace ocl { +using namespace cv::ocl; + +// TODO static functions in the Context class +/** @brief Creates OpenCL context from VA. +@param display - VADisplay for which CL interop should be established. +@param tryInterop - try to set up for interoperability, if true; set up for use slow copy if false. +@return Returns reference to OpenCL Context + */ +CV_EXPORTS Context& initializeContextFromVA(VADisplay display, bool tryInterop = true); + +} // namespace cv::va_intel::ocl + +/** @brief Converts InputArray to VASurfaceID object. +@param display - VADisplay object. +@param src - source InputArray. +@param surface - destination VASurfaceID object. +@param size - size of image represented by VASurfaceID object. + */ +CV_EXPORTS void convertToVASurface(VADisplay display, InputArray src, VASurfaceID surface, Size size); + +/** @brief Converts VASurfaceID object to OutputArray. +@param display - VADisplay object. +@param surface - source VASurfaceID object. +@param size - size of image represented by VASurfaceID object. +@param dst - destination OutputArray. + */ +CV_EXPORTS void convertFromVASurface(VADisplay display, VASurfaceID surface, Size size, OutputArray dst); + +//! @} + +}} // namespace cv::va_intel + +#endif /* OPENCV_CORE_VA_INTEL_HPP */ diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/version.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/version.hpp new file mode 100644 index 0000000..2d602d6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/version.hpp @@ -0,0 +1,26 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_VERSION_HPP +#define OPENCV_VERSION_HPP + +#define CV_VERSION_MAJOR 3 +#define CV_VERSION_MINOR 4 +#define CV_VERSION_REVISION 14 +#define CV_VERSION_STATUS "" + +#define CVAUX_STR_EXP(__A) #__A +#define CVAUX_STR(__A) CVAUX_STR_EXP(__A) + +#define CVAUX_STRW_EXP(__A) L ## #__A +#define CVAUX_STRW(__A) CVAUX_STRW_EXP(__A) + +#define CV_VERSION CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) "." CVAUX_STR(CV_VERSION_REVISION) CV_VERSION_STATUS + +/* old style version constants*/ +#define CV_MAJOR_VERSION CV_VERSION_MAJOR +#define CV_MINOR_VERSION CV_VERSION_MINOR +#define CV_SUBMINOR_VERSION CV_VERSION_REVISION + +#endif // OPENCV_VERSION_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/vsx_utils.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/vsx_utils.hpp new file mode 100644 index 0000000..68863ff --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/vsx_utils.hpp @@ -0,0 +1,1042 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#ifndef OPENCV_HAL_VSX_UTILS_HPP +#define OPENCV_HAL_VSX_UTILS_HPP + +#include "opencv2/core/cvdef.h" + +#ifndef SKIP_INCLUDES +# include +#endif + +//! @addtogroup core_utils_vsx +//! @{ +#if CV_VSX + +#define __VSX_S16__(c, v) (c){v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v} +#define __VSX_S8__(c, v) (c){v, v, v, v, v, v, v, v} +#define __VSX_S4__(c, v) (c){v, v, v, v} +#define __VSX_S2__(c, v) (c){v, v} + +typedef __vector unsigned char vec_uchar16; +#define vec_uchar16_set(...) (vec_uchar16){__VA_ARGS__} +#define vec_uchar16_sp(c) (__VSX_S16__(vec_uchar16, (unsigned char)c)) +#define vec_uchar16_c(v) ((vec_uchar16)(v)) +#define vec_uchar16_z vec_uchar16_sp(0) + +typedef __vector signed char vec_char16; +#define vec_char16_set(...) (vec_char16){__VA_ARGS__} +#define vec_char16_sp(c) (__VSX_S16__(vec_char16, (signed char)c)) +#define vec_char16_c(v) ((vec_char16)(v)) +#define vec_char16_z vec_char16_sp(0) + +typedef __vector unsigned short vec_ushort8; +#define vec_ushort8_set(...) (vec_ushort8){__VA_ARGS__} +#define vec_ushort8_sp(c) (__VSX_S8__(vec_ushort8, (unsigned short)c)) +#define vec_ushort8_c(v) ((vec_ushort8)(v)) +#define vec_ushort8_z vec_ushort8_sp(0) + +typedef __vector signed short vec_short8; +#define vec_short8_set(...) (vec_short8){__VA_ARGS__} +#define vec_short8_sp(c) (__VSX_S8__(vec_short8, (signed short)c)) +#define vec_short8_c(v) ((vec_short8)(v)) +#define vec_short8_z vec_short8_sp(0) + +typedef __vector unsigned int vec_uint4; +#define vec_uint4_set(...) (vec_uint4){__VA_ARGS__} +#define vec_uint4_sp(c) (__VSX_S4__(vec_uint4, (unsigned int)c)) +#define vec_uint4_c(v) ((vec_uint4)(v)) +#define vec_uint4_z vec_uint4_sp(0) + +typedef __vector signed int vec_int4; +#define vec_int4_set(...) (vec_int4){__VA_ARGS__} +#define vec_int4_sp(c) (__VSX_S4__(vec_int4, (signed int)c)) +#define vec_int4_c(v) ((vec_int4)(v)) +#define vec_int4_z vec_int4_sp(0) + +typedef __vector float vec_float4; +#define vec_float4_set(...) (vec_float4){__VA_ARGS__} +#define vec_float4_sp(c) (__VSX_S4__(vec_float4, c)) +#define vec_float4_c(v) ((vec_float4)(v)) +#define vec_float4_z vec_float4_sp(0) + +typedef __vector unsigned long long vec_udword2; +#define vec_udword2_set(...) (vec_udword2){__VA_ARGS__} +#define vec_udword2_sp(c) (__VSX_S2__(vec_udword2, (unsigned long long)c)) +#define vec_udword2_c(v) ((vec_udword2)(v)) +#define vec_udword2_z vec_udword2_sp(0) + +typedef __vector signed long long vec_dword2; +#define vec_dword2_set(...) (vec_dword2){__VA_ARGS__} +#define vec_dword2_sp(c) (__VSX_S2__(vec_dword2, (signed long long)c)) +#define vec_dword2_c(v) ((vec_dword2)(v)) +#define vec_dword2_z vec_dword2_sp(0) + +typedef __vector double vec_double2; +#define vec_double2_set(...) (vec_double2){__VA_ARGS__} +#define vec_double2_c(v) ((vec_double2)(v)) +#define vec_double2_sp(c) (__VSX_S2__(vec_double2, c)) +#define vec_double2_z vec_double2_sp(0) + +#define vec_bchar16 __vector __bool char +#define vec_bchar16_set(...) (vec_bchar16){__VA_ARGS__} +#define vec_bchar16_c(v) ((vec_bchar16)(v)) + +#define vec_bshort8 __vector __bool short +#define vec_bshort8_set(...) (vec_bshort8){__VA_ARGS__} +#define vec_bshort8_c(v) ((vec_bshort8)(v)) + +#define vec_bint4 __vector __bool int +#define vec_bint4_set(...) (vec_bint4){__VA_ARGS__} +#define vec_bint4_c(v) ((vec_bint4)(v)) + +#define vec_bdword2 __vector __bool long long +#define vec_bdword2_set(...) (vec_bdword2){__VA_ARGS__} +#define vec_bdword2_c(v) ((vec_bdword2)(v)) + +#define VSX_FINLINE(tp) extern inline tp __attribute__((always_inline)) + +#define VSX_REDIRECT_1RG(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fn2(a); } + +#define VSX_REDIRECT_2RG(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a, const rg& b) { return fn2(a, b); } + +/* + * GCC VSX compatibility +**/ +#if defined(__GNUG__) && !defined(__clang__) + +// inline asm helper +#define VSX_IMPL_1RG(rt, rg, opc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ rt rs; __asm__ __volatile__(#opc" %x0,%x1" : "=wa" (rs) : "wa" (a)); return rs; } + +#define VSX_IMPL_1VRG(rt, rg, opc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ rt rs; __asm__ __volatile__(#opc" %0,%1" : "=v" (rs) : "v" (a)); return rs; } + +#define VSX_IMPL_2VRG_F(rt, rg, fopc, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a, const rg& b) \ +{ rt rs; __asm__ __volatile__(fopc : "=v" (rs) : "v" (a), "v" (b)); return rs; } + +#define VSX_IMPL_2VRG(rt, rg, opc, fnm) VSX_IMPL_2VRG_F(rt, rg, #opc" %0,%1,%2", fnm) + +#if __GNUG__ < 8 + + // Support for int4 -> dword2 expanding multiply was added in GCC 8. + #ifdef vec_mule + #undef vec_mule + #endif + #ifdef vec_mulo + #undef vec_mulo + #endif + + VSX_REDIRECT_2RG(vec_ushort8, vec_uchar16, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_short8, vec_char16, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_int4, vec_short8, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_uint4, vec_ushort8, vec_mule, __builtin_vec_mule) + VSX_REDIRECT_2RG(vec_ushort8, vec_uchar16, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_short8, vec_char16, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_int4, vec_short8, vec_mulo, __builtin_vec_mulo) + VSX_REDIRECT_2RG(vec_uint4, vec_ushort8, vec_mulo, __builtin_vec_mulo) + + // dword2 support arrived in ISA 2.07 and GCC 8+ + VSX_IMPL_2VRG(vec_dword2, vec_int4, vmulosw, vec_mule) + VSX_IMPL_2VRG(vec_udword2, vec_uint4, vmulouw, vec_mule) + VSX_IMPL_2VRG(vec_dword2, vec_int4, vmulesw, vec_mulo) + VSX_IMPL_2VRG(vec_udword2, vec_uint4, vmuleuw, vec_mulo) + +#endif + +#if __GNUG__ < 7 +// up to GCC 6 vec_mul only supports precisions and llong +# ifdef vec_mul +# undef vec_mul +# endif +/* + * there's no a direct instruction for supporting 8-bit, 16-bit multiplication in ISA 2.07, + * XLC Implement it by using instruction "multiply even", "multiply odd" and "permute" +**/ +# define VSX_IMPL_MULH(Tvec, cperm) \ + VSX_FINLINE(Tvec) vec_mul(const Tvec& a, const Tvec& b) \ + { \ + static const vec_uchar16 ev_od = {cperm}; \ + return vec_perm((Tvec)vec_mule(a, b), (Tvec)vec_mulo(a, b), ev_od); \ + } + #define VSX_IMPL_MULH_P16 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30 + VSX_IMPL_MULH(vec_char16, VSX_IMPL_MULH_P16) + VSX_IMPL_MULH(vec_uchar16, VSX_IMPL_MULH_P16) + #define VSX_IMPL_MULH_P8 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29 + VSX_IMPL_MULH(vec_short8, VSX_IMPL_MULH_P8) + VSX_IMPL_MULH(vec_ushort8, VSX_IMPL_MULH_P8) + // vmuluwm can be used for unsigned or signed integers, that's what they said + VSX_IMPL_2VRG(vec_int4, vec_int4, vmuluwm, vec_mul) + VSX_IMPL_2VRG(vec_uint4, vec_uint4, vmuluwm, vec_mul) + // redirect to GCC builtin vec_mul, since it already supports precisions and llong + VSX_REDIRECT_2RG(vec_float4, vec_float4, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mul, __builtin_vec_mul) + VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mul, __builtin_vec_mul) +#endif // __GNUG__ < 7 + +#if __GNUG__ < 6 +/* + * Instruction "compare greater than or equal" in ISA 2.07 only supports single + * and double precision. + * In XLC and new versions of GCC implement integers by using instruction "greater than" and NOR. +**/ +# ifdef vec_cmpge +# undef vec_cmpge +# endif +# ifdef vec_cmple +# undef vec_cmple +# endif +# define vec_cmple(a, b) vec_cmpge(b, a) +# define VSX_IMPL_CMPGE(rt, rg, opc, fnm) \ + VSX_IMPL_2VRG_F(rt, rg, #opc" %0,%2,%1\n\t xxlnor %x0,%x0,%x0", fnm) + + VSX_IMPL_CMPGE(vec_bchar16, vec_char16, vcmpgtsb, vec_cmpge) + VSX_IMPL_CMPGE(vec_bchar16, vec_uchar16, vcmpgtub, vec_cmpge) + VSX_IMPL_CMPGE(vec_bshort8, vec_short8, vcmpgtsh, vec_cmpge) + VSX_IMPL_CMPGE(vec_bshort8, vec_ushort8, vcmpgtuh, vec_cmpge) + VSX_IMPL_CMPGE(vec_bint4, vec_int4, vcmpgtsw, vec_cmpge) + VSX_IMPL_CMPGE(vec_bint4, vec_uint4, vcmpgtuw, vec_cmpge) + VSX_IMPL_CMPGE(vec_bdword2, vec_dword2, vcmpgtsd, vec_cmpge) + VSX_IMPL_CMPGE(vec_bdword2, vec_udword2, vcmpgtud, vec_cmpge) + +// redirect to GCC builtin cmpge, since it already supports precisions + VSX_REDIRECT_2RG(vec_bint4, vec_float4, vec_cmpge, __builtin_vec_cmpge) + VSX_REDIRECT_2RG(vec_bdword2, vec_double2, vec_cmpge, __builtin_vec_cmpge) + +// up to gcc5 vec_nor doesn't support bool long long +# undef vec_nor + template + VSX_REDIRECT_2RG(T, T, vec_nor, __builtin_vec_nor) + + VSX_FINLINE(vec_bdword2) vec_nor(const vec_bdword2& a, const vec_bdword2& b) + { return vec_bdword2_c(__builtin_vec_nor(vec_dword2_c(a), vec_dword2_c(b))); } + +// vec_packs doesn't support double words in gcc4 and old versions of gcc5 +# undef vec_packs + VSX_REDIRECT_2RG(vec_char16, vec_short8, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_uchar16, vec_ushort8, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_short8, vec_int4, vec_packs, __builtin_vec_packs) + VSX_REDIRECT_2RG(vec_ushort8, vec_uint4, vec_packs, __builtin_vec_packs) + + VSX_IMPL_2VRG_F(vec_int4, vec_dword2, "vpksdss %0,%2,%1", vec_packs) + VSX_IMPL_2VRG_F(vec_uint4, vec_udword2, "vpkudus %0,%2,%1", vec_packs) +#endif // __GNUG__ < 6 + +#if __GNUG__ < 5 +// vec_xxpermdi in gcc4 missing little-endian supports just like clang +# define vec_permi(a, b, c) vec_xxpermdi(b, a, (3 ^ (((c) & 1) << 1 | (c) >> 1))) +// same as vec_xxpermdi +# undef vec_vbpermq + VSX_IMPL_2VRG(vec_udword2, vec_uchar16, vbpermq, vec_vbpermq) + VSX_IMPL_2VRG(vec_dword2, vec_char16, vbpermq, vec_vbpermq) +#else +# define vec_permi vec_xxpermdi +#endif // __GNUG__ < 5 + +// shift left double by word immediate +#ifndef vec_sldw +# define vec_sldw __builtin_vsx_xxsldwi +#endif + +// vector population count +VSX_IMPL_1VRG(vec_uchar16, vec_uchar16, vpopcntb, vec_popcntu) +VSX_IMPL_1VRG(vec_uchar16, vec_char16, vpopcntb, vec_popcntu) +VSX_IMPL_1VRG(vec_ushort8, vec_ushort8, vpopcnth, vec_popcntu) +VSX_IMPL_1VRG(vec_ushort8, vec_short8, vpopcnth, vec_popcntu) +VSX_IMPL_1VRG(vec_uint4, vec_uint4, vpopcntw, vec_popcntu) +VSX_IMPL_1VRG(vec_uint4, vec_int4, vpopcntw, vec_popcntu) +VSX_IMPL_1VRG(vec_udword2, vec_udword2, vpopcntd, vec_popcntu) +VSX_IMPL_1VRG(vec_udword2, vec_dword2, vpopcntd, vec_popcntu) + +// converts between single and double-precision +VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, __builtin_vsx_xvcvdpsp) +VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, __builtin_vsx_xvcvspdp) + +// converts word and doubleword to double-precision +#undef vec_ctd +VSX_IMPL_1RG(vec_double2, vec_int4, xvcvsxwdp, vec_ctdo) +VSX_IMPL_1RG(vec_double2, vec_uint4, xvcvuxwdp, vec_ctdo) +VSX_IMPL_1RG(vec_double2, vec_dword2, xvcvsxddp, vec_ctd) +VSX_IMPL_1RG(vec_double2, vec_udword2, xvcvuxddp, vec_ctd) + +// converts word and doubleword to single-precision +#undef vec_ctf +VSX_IMPL_1RG(vec_float4, vec_int4, xvcvsxwsp, vec_ctf) +VSX_IMPL_1RG(vec_float4, vec_uint4, xvcvuxwsp, vec_ctf) +VSX_IMPL_1RG(vec_float4, vec_dword2, xvcvsxdsp, vec_ctfo) +VSX_IMPL_1RG(vec_float4, vec_udword2, xvcvuxdsp, vec_ctfo) + +// converts single and double precision to signed word +#undef vec_cts +VSX_IMPL_1RG(vec_int4, vec_double2, xvcvdpsxws, vec_ctso) +VSX_IMPL_1RG(vec_int4, vec_float4, xvcvspsxws, vec_cts) + +// converts single and double precision to unsigned word +#undef vec_ctu +VSX_IMPL_1RG(vec_uint4, vec_double2, xvcvdpuxws, vec_ctuo) +VSX_IMPL_1RG(vec_uint4, vec_float4, xvcvspuxws, vec_ctu) + +// converts single and double precision to signed doubleword +#undef vec_ctsl +VSX_IMPL_1RG(vec_dword2, vec_double2, xvcvdpsxds, vec_ctsl) +VSX_IMPL_1RG(vec_dword2, vec_float4, xvcvspsxds, vec_ctslo) + +// converts single and double precision to unsigned doubleword +#undef vec_ctul +VSX_IMPL_1RG(vec_udword2, vec_double2, xvcvdpuxds, vec_ctul) +VSX_IMPL_1RG(vec_udword2, vec_float4, xvcvspuxds, vec_ctulo) + +// just in case if GCC doesn't define it +#ifndef vec_xl +# define vec_xl vec_vsx_ld +# define vec_xst vec_vsx_st +#endif + +#endif // GCC VSX compatibility + +/* + * CLANG VSX compatibility +**/ +#if defined(__clang__) && !defined(__IBMCPP__) + +/* + * CLANG doesn't support %x in the inline asm template which fixes register number + * when using any of the register constraints wa, wd, wf + * + * For more explanation checkout PowerPC and IBM RS6000 in https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html + * Also there's already an open bug https://bugs.llvm.org/show_bug.cgi?id=31837 + * + * So we're not able to use inline asm and only use built-in functions that CLANG supports + * and use __builtin_convertvector if clang missing any of vector conversions built-in functions + * + * todo: clang asm template bug is fixed, need to reconsider the current workarounds. +*/ + +// convert vector helper +#define VSX_IMPL_CONVERT(rt, rg, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) { return __builtin_convertvector(a, rt); } + +#if __clang_major__ < 5 +// implement vec_permi in a dirty way +# define VSX_IMPL_CLANG_4_PERMI(Tvec) \ + VSX_FINLINE(Tvec) vec_permi(const Tvec& a, const Tvec& b, unsigned const char c) \ + { \ + switch (c) \ + { \ + case 0: \ + return vec_mergeh(a, b); \ + case 1: \ + return vec_mergel(vec_mergeh(a, a), b); \ + case 2: \ + return vec_mergeh(vec_mergel(a, a), b); \ + default: \ + return vec_mergel(a, b); \ + } \ + } + VSX_IMPL_CLANG_4_PERMI(vec_udword2) + VSX_IMPL_CLANG_4_PERMI(vec_dword2) + VSX_IMPL_CLANG_4_PERMI(vec_double2) + +// vec_xxsldwi is missing in clang 4 +# define vec_xxsldwi(a, b, c) vec_sld(a, b, (c) * 4) +#else +// vec_xxpermdi is missing little-endian supports in clang 4 just like gcc4 +# define vec_permi(a, b, c) vec_xxpermdi(b, a, (3 ^ (((c) & 1) << 1 | (c) >> 1))) +#endif // __clang_major__ < 5 + +// shift left double by word immediate +#ifndef vec_sldw +# define vec_sldw vec_xxsldwi +#endif + +// Implement vec_rsqrt since clang only supports vec_rsqrte +#ifndef vec_rsqrt + VSX_FINLINE(vec_float4) vec_rsqrt(const vec_float4& a) + { return vec_div(vec_float4_sp(1), vec_sqrt(a)); } + + VSX_FINLINE(vec_double2) vec_rsqrt(const vec_double2& a) + { return vec_div(vec_double2_sp(1), vec_sqrt(a)); } +#endif + +// vec_promote missing support for doubleword +VSX_FINLINE(vec_dword2) vec_promote(long long a, int b) +{ + vec_dword2 ret = vec_dword2_z; + ret[b & 1] = a; + return ret; +} + +VSX_FINLINE(vec_udword2) vec_promote(unsigned long long a, int b) +{ + vec_udword2 ret = vec_udword2_z; + ret[b & 1] = a; + return ret; +} + +// vec_popcnt should return unsigned but clang has different thought just like gcc in vec_vpopcnt +#define VSX_IMPL_POPCNTU(Tvec, Tvec2, ucast) \ +VSX_FINLINE(Tvec) vec_popcntu(const Tvec2& a) \ +{ return ucast(vec_popcnt(a)); } +VSX_IMPL_POPCNTU(vec_uchar16, vec_char16, vec_uchar16_c); +VSX_IMPL_POPCNTU(vec_ushort8, vec_short8, vec_ushort8_c); +VSX_IMPL_POPCNTU(vec_uint4, vec_int4, vec_uint4_c); +VSX_IMPL_POPCNTU(vec_udword2, vec_dword2, vec_udword2_c); +// redirect unsigned types +VSX_REDIRECT_1RG(vec_uchar16, vec_uchar16, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_ushort8, vec_ushort8, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_uint4, vec_uint4, vec_popcntu, vec_popcnt) +VSX_REDIRECT_1RG(vec_udword2, vec_udword2, vec_popcntu, vec_popcnt) + +// converts between single and double precision +VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, __builtin_vsx_xvcvdpsp) +VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, __builtin_vsx_xvcvspdp) + +// converts word and doubleword to double-precision +#ifdef vec_ctd +# undef vec_ctd +#endif +VSX_REDIRECT_1RG(vec_double2, vec_int4, vec_ctdo, __builtin_vsx_xvcvsxwdp) +VSX_REDIRECT_1RG(vec_double2, vec_uint4, vec_ctdo, __builtin_vsx_xvcvuxwdp) + +VSX_IMPL_CONVERT(vec_double2, vec_dword2, vec_ctd) +VSX_IMPL_CONVERT(vec_double2, vec_udword2, vec_ctd) + +// converts word and doubleword to single-precision +#if __clang_major__ > 4 +# undef vec_ctf +#endif +VSX_IMPL_CONVERT(vec_float4, vec_int4, vec_ctf) +VSX_IMPL_CONVERT(vec_float4, vec_uint4, vec_ctf) +VSX_REDIRECT_1RG(vec_float4, vec_dword2, vec_ctfo, __builtin_vsx_xvcvsxdsp) +VSX_REDIRECT_1RG(vec_float4, vec_udword2, vec_ctfo, __builtin_vsx_xvcvuxdsp) + +// converts single and double precision to signed word +#if __clang_major__ > 4 +# undef vec_cts +#endif +VSX_REDIRECT_1RG(vec_int4, vec_double2, vec_ctso, __builtin_vsx_xvcvdpsxws) +VSX_IMPL_CONVERT(vec_int4, vec_float4, vec_cts) + +// converts single and double precision to unsigned word +#if __clang_major__ > 4 +# undef vec_ctu +#endif +VSX_REDIRECT_1RG(vec_uint4, vec_double2, vec_ctuo, __builtin_vsx_xvcvdpuxws) +VSX_IMPL_CONVERT(vec_uint4, vec_float4, vec_ctu) + +// converts single and double precision to signed doubleword +#ifdef vec_ctsl +# undef vec_ctsl +#endif +VSX_IMPL_CONVERT(vec_dword2, vec_double2, vec_ctsl) +// __builtin_convertvector unable to convert, xvcvspsxds is missing on it +VSX_FINLINE(vec_dword2) vec_ctslo(const vec_float4& a) +{ return vec_ctsl(vec_cvfo(a)); } + +// converts single and double precision to unsigned doubleword +#ifdef vec_ctul +# undef vec_ctul +#endif +VSX_IMPL_CONVERT(vec_udword2, vec_double2, vec_ctul) +// __builtin_convertvector unable to convert, xvcvspuxds is missing on it +VSX_FINLINE(vec_udword2) vec_ctulo(const vec_float4& a) +{ return vec_ctul(vec_cvfo(a)); } + +#endif // CLANG VSX compatibility + +/* + * Common GCC, CLANG compatibility +**/ +#if defined(__GNUG__) && !defined(__IBMCPP__) + +#ifdef vec_cvf +# undef vec_cvf +#endif + +#define VSX_IMPL_CONV_EVEN_4_2(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ return fn2(vec_sldw(a, a, 1)); } + +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_float4, vec_cvf, vec_cvfo) +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_int4, vec_ctd, vec_ctdo) +VSX_IMPL_CONV_EVEN_4_2(vec_double2, vec_uint4, vec_ctd, vec_ctdo) + +VSX_IMPL_CONV_EVEN_4_2(vec_dword2, vec_float4, vec_ctsl, vec_ctslo) +VSX_IMPL_CONV_EVEN_4_2(vec_udword2, vec_float4, vec_ctul, vec_ctulo) + +#define VSX_IMPL_CONV_EVEN_2_4(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ \ + rt v4 = fn2(a); \ + return vec_sldw(v4, v4, 3); \ +} + +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_double2, vec_cvf, vec_cvfo) +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_dword2, vec_ctf, vec_ctfo) +VSX_IMPL_CONV_EVEN_2_4(vec_float4, vec_udword2, vec_ctf, vec_ctfo) + +VSX_IMPL_CONV_EVEN_2_4(vec_int4, vec_double2, vec_cts, vec_ctso) +VSX_IMPL_CONV_EVEN_2_4(vec_uint4, vec_double2, vec_ctu, vec_ctuo) + +// Only for Eigen! +/* + * changing behavior of conversion intrinsics for gcc has effect on Eigen + * so we redefine old behavior again only on gcc, clang +*/ +#if !defined(__clang__) || __clang_major__ > 4 + // ignoring second arg since Eigen only truncates toward zero +# define VSX_IMPL_CONV_2VARIANT(rt, rg, fnm, fn2) \ + VSX_FINLINE(rt) fnm(const rg& a, int only_truncate) \ + { \ + assert(only_truncate == 0); \ + CV_UNUSED(only_truncate); \ + return fn2(a); \ + } + VSX_IMPL_CONV_2VARIANT(vec_int4, vec_float4, vec_cts, vec_cts) + VSX_IMPL_CONV_2VARIANT(vec_uint4, vec_float4, vec_ctu, vec_ctu) + VSX_IMPL_CONV_2VARIANT(vec_float4, vec_int4, vec_ctf, vec_ctf) + VSX_IMPL_CONV_2VARIANT(vec_float4, vec_uint4, vec_ctf, vec_ctf) + // define vec_cts for converting double precision to signed doubleword + // which isn't compatible with xlc but its okay since Eigen only uses it for gcc + VSX_IMPL_CONV_2VARIANT(vec_dword2, vec_double2, vec_cts, vec_ctsl) +#endif // Eigen + +#endif // Common GCC, CLANG compatibility + +/* + * XLC VSX compatibility +**/ +#if defined(__IBMCPP__) + +// vector population count +#define vec_popcntu vec_popcnt + +// overload and redirect with setting second arg to zero +// since we only support conversions without the second arg +#define VSX_IMPL_OVERLOAD_Z2(rt, rg, fnm) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fnm(a, 0); } + +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_int4, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_uint4, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_dword2, vec_ctd) +VSX_IMPL_OVERLOAD_Z2(vec_double2, vec_udword2, vec_ctd) + +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_int4, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_uint4, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_dword2, vec_ctf) +VSX_IMPL_OVERLOAD_Z2(vec_float4, vec_udword2, vec_ctf) + +VSX_IMPL_OVERLOAD_Z2(vec_int4, vec_double2, vec_cts) +VSX_IMPL_OVERLOAD_Z2(vec_int4, vec_float4, vec_cts) + +VSX_IMPL_OVERLOAD_Z2(vec_uint4, vec_double2, vec_ctu) +VSX_IMPL_OVERLOAD_Z2(vec_uint4, vec_float4, vec_ctu) + +VSX_IMPL_OVERLOAD_Z2(vec_dword2, vec_double2, vec_ctsl) +VSX_IMPL_OVERLOAD_Z2(vec_dword2, vec_float4, vec_ctsl) + +VSX_IMPL_OVERLOAD_Z2(vec_udword2, vec_double2, vec_ctul) +VSX_IMPL_OVERLOAD_Z2(vec_udword2, vec_float4, vec_ctul) + +// fixme: implement conversions of odd-numbered elements in a dirty way +// since xlc doesn't support VSX registers operand in inline asm. +#define VSX_IMPL_CONV_ODD_4_2(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) { return fn2(vec_sldw(a, a, 3)); } + +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_float4, vec_cvfo, vec_cvf) +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_int4, vec_ctdo, vec_ctd) +VSX_IMPL_CONV_ODD_4_2(vec_double2, vec_uint4, vec_ctdo, vec_ctd) + +VSX_IMPL_CONV_ODD_4_2(vec_dword2, vec_float4, vec_ctslo, vec_ctsl) +VSX_IMPL_CONV_ODD_4_2(vec_udword2, vec_float4, vec_ctulo, vec_ctul) + +#define VSX_IMPL_CONV_ODD_2_4(rt, rg, fnm, fn2) \ +VSX_FINLINE(rt) fnm(const rg& a) \ +{ \ + rt v4 = fn2(a); \ + return vec_sldw(v4, v4, 1); \ +} + +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_double2, vec_cvfo, vec_cvf) +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_dword2, vec_ctfo, vec_ctf) +VSX_IMPL_CONV_ODD_2_4(vec_float4, vec_udword2, vec_ctfo, vec_ctf) + +VSX_IMPL_CONV_ODD_2_4(vec_int4, vec_double2, vec_ctso, vec_cts) +VSX_IMPL_CONV_ODD_2_4(vec_uint4, vec_double2, vec_ctuo, vec_ctu) + +#endif // XLC VSX compatibility + +// ignore GCC warning that caused by -Wunused-but-set-variable in rare cases +#if defined(__GNUG__) && !defined(__clang__) +# define VSX_UNUSED(Tvec) Tvec __attribute__((__unused__)) +#else // CLANG, XLC +# define VSX_UNUSED(Tvec) Tvec +#endif + +// gcc can find his way in casting log int and XLC, CLANG ambiguous +#if defined(__clang__) || defined(__IBMCPP__) + VSX_FINLINE(vec_udword2) vec_splats(uint64 v) + { return vec_splats((unsigned long long) v); } + + VSX_FINLINE(vec_dword2) vec_splats(int64 v) + { return vec_splats((long long) v); } + + VSX_FINLINE(vec_udword2) vec_promote(uint64 a, int b) + { return vec_promote((unsigned long long) a, b); } + + VSX_FINLINE(vec_dword2) vec_promote(int64 a, int b) + { return vec_promote((long long) a, b); } +#endif + +/* + * implement vsx_ld(offset, pointer), vsx_st(vector, offset, pointer) + * load and set using offset depend on the pointer type + * + * implement vsx_ldf(offset, pointer), vsx_stf(vector, offset, pointer) + * load and set using offset depend on fixed bytes size + * + * Note: In clang vec_xl and vec_xst fails to load unaligned addresses + * so we are using vec_vsx_ld, vec_vsx_st instead +*/ + +#if defined(__clang__) && !defined(__IBMCPP__) +# define vsx_ldf vec_vsx_ld +# define vsx_stf vec_vsx_st +#else // GCC , XLC +# define vsx_ldf vec_xl +# define vsx_stf vec_xst +#endif + +#define VSX_OFFSET(o, p) ((o) * sizeof(*(p))) +#define vsx_ld(o, p) vsx_ldf(VSX_OFFSET(o, p), p) +#define vsx_st(v, o, p) vsx_stf(v, VSX_OFFSET(o, p), p) + +/* + * implement vsx_ld2(offset, pointer), vsx_st2(vector, offset, pointer) to load and store double words + * In GCC vec_xl and vec_xst it maps to vec_vsx_ld, vec_vsx_st which doesn't support long long + * and in CLANG we are using vec_vsx_ld, vec_vsx_st because vec_xl, vec_xst fails to load unaligned addresses + * + * In XLC vec_xl and vec_xst fail to cast int64(long int) to long long +*/ +#if (defined(__GNUG__) || defined(__clang__)) && !defined(__IBMCPP__) + VSX_FINLINE(vec_udword2) vsx_ld2(long o, const uint64* p) + { return vec_udword2_c(vsx_ldf(VSX_OFFSET(o, p), (unsigned int*)p)); } + + VSX_FINLINE(vec_dword2) vsx_ld2(long o, const int64* p) + { return vec_dword2_c(vsx_ldf(VSX_OFFSET(o, p), (int*)p)); } + + VSX_FINLINE(void) vsx_st2(const vec_udword2& vec, long o, uint64* p) + { vsx_stf(vec_uint4_c(vec), VSX_OFFSET(o, p), (unsigned int*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_dword2& vec, long o, int64* p) + { vsx_stf(vec_int4_c(vec), VSX_OFFSET(o, p), (int*)p); } +#else // XLC + VSX_FINLINE(vec_udword2) vsx_ld2(long o, const uint64* p) + { return vsx_ldf(VSX_OFFSET(o, p), (unsigned long long*)p); } + + VSX_FINLINE(vec_dword2) vsx_ld2(long o, const int64* p) + { return vsx_ldf(VSX_OFFSET(o, p), (long long*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_udword2& vec, long o, uint64* p) + { vsx_stf(vec, VSX_OFFSET(o, p), (unsigned long long*)p); } + + VSX_FINLINE(void) vsx_st2(const vec_dword2& vec, long o, int64* p) + { vsx_stf(vec, VSX_OFFSET(o, p), (long long*)p); } +#endif + +// Store lower 8 byte +#define vec_st_l8(v, p) *((uint64*)(p)) = vec_extract(vec_udword2_c(v), 0) + +// Store higher 8 byte +#define vec_st_h8(v, p) *((uint64*)(p)) = vec_extract(vec_udword2_c(v), 1) + +// Load 64-bits of integer data to lower part +#define VSX_IMPL_LOAD_L8(Tvec, Tp) \ +VSX_FINLINE(Tvec) vec_ld_l8(const Tp *p) \ +{ return ((Tvec)vec_promote(*((uint64*)p), 0)); } + +VSX_IMPL_LOAD_L8(vec_uchar16, uchar) +VSX_IMPL_LOAD_L8(vec_char16, schar) +VSX_IMPL_LOAD_L8(vec_ushort8, ushort) +VSX_IMPL_LOAD_L8(vec_short8, short) +VSX_IMPL_LOAD_L8(vec_uint4, uint) +VSX_IMPL_LOAD_L8(vec_int4, int) +VSX_IMPL_LOAD_L8(vec_float4, float) +VSX_IMPL_LOAD_L8(vec_udword2, uint64) +VSX_IMPL_LOAD_L8(vec_dword2, int64) +VSX_IMPL_LOAD_L8(vec_double2, double) + +// logical not +#define vec_not(a) vec_nor(a, a) + +// power9 yaya +// not equal +#ifndef vec_cmpne +# define vec_cmpne(a, b) vec_not(vec_cmpeq(a, b)) +#endif + +// absolute difference +#ifndef vec_absd +# define vec_absd(a, b) vec_sub(vec_max(a, b), vec_min(a, b)) +#endif + +/* + * Implement vec_unpacklu and vec_unpackhu + * since vec_unpackl, vec_unpackh only support signed integers +**/ +#define VSX_IMPL_UNPACKU(rt, rg, zero) \ +VSX_FINLINE(rt) vec_unpacklu(const rg& a) \ +{ return (rt)(vec_mergel(a, zero)); } \ +VSX_FINLINE(rt) vec_unpackhu(const rg& a) \ +{ return (rt)(vec_mergeh(a, zero)); } + +VSX_IMPL_UNPACKU(vec_ushort8, vec_uchar16, vec_uchar16_z) +VSX_IMPL_UNPACKU(vec_uint4, vec_ushort8, vec_ushort8_z) +VSX_IMPL_UNPACKU(vec_udword2, vec_uint4, vec_uint4_z) + +/* + * Implement vec_mergesqe and vec_mergesqo + * Merges the sequence values of even and odd elements of two vectors +*/ +#define VSX_IMPL_PERM(rt, fnm, ...) \ +VSX_FINLINE(rt) fnm(const rt& a, const rt& b) \ +{ static const vec_uchar16 perm = {__VA_ARGS__}; return vec_perm(a, b, perm); } + +// 16 +#define perm16_mergesqe 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 +#define perm16_mergesqo 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 +VSX_IMPL_PERM(vec_uchar16, vec_mergesqe, perm16_mergesqe) +VSX_IMPL_PERM(vec_uchar16, vec_mergesqo, perm16_mergesqo) +VSX_IMPL_PERM(vec_char16, vec_mergesqe, perm16_mergesqe) +VSX_IMPL_PERM(vec_char16, vec_mergesqo, perm16_mergesqo) +// 8 +#define perm8_mergesqe 0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29 +#define perm8_mergesqo 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31 +VSX_IMPL_PERM(vec_ushort8, vec_mergesqe, perm8_mergesqe) +VSX_IMPL_PERM(vec_ushort8, vec_mergesqo, perm8_mergesqo) +VSX_IMPL_PERM(vec_short8, vec_mergesqe, perm8_mergesqe) +VSX_IMPL_PERM(vec_short8, vec_mergesqo, perm8_mergesqo) +// 4 +#define perm4_mergesqe 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 +#define perm4_mergesqo 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31 +VSX_IMPL_PERM(vec_uint4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_uint4, vec_mergesqo, perm4_mergesqo) +VSX_IMPL_PERM(vec_int4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_int4, vec_mergesqo, perm4_mergesqo) +VSX_IMPL_PERM(vec_float4, vec_mergesqe, perm4_mergesqe) +VSX_IMPL_PERM(vec_float4, vec_mergesqo, perm4_mergesqo) +// 2 +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqo, vec_mergel) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqo, vec_mergel) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqe, vec_mergeh) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqo, vec_mergel) + +/* + * Implement vec_mergesqh and vec_mergesql + * Merges the sequence most and least significant halves of two vectors +*/ +#define VSX_IMPL_MERGESQHL(Tvec) \ +VSX_FINLINE(Tvec) vec_mergesqh(const Tvec& a, const Tvec& b) \ +{ return (Tvec)vec_mergeh(vec_udword2_c(a), vec_udword2_c(b)); } \ +VSX_FINLINE(Tvec) vec_mergesql(const Tvec& a, const Tvec& b) \ +{ return (Tvec)vec_mergel(vec_udword2_c(a), vec_udword2_c(b)); } +VSX_IMPL_MERGESQHL(vec_uchar16) +VSX_IMPL_MERGESQHL(vec_char16) +VSX_IMPL_MERGESQHL(vec_ushort8) +VSX_IMPL_MERGESQHL(vec_short8) +VSX_IMPL_MERGESQHL(vec_uint4) +VSX_IMPL_MERGESQHL(vec_int4) +VSX_IMPL_MERGESQHL(vec_float4) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_udword2, vec_udword2, vec_mergesql, vec_mergel) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_dword2, vec_dword2, vec_mergesql, vec_mergel) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesqh, vec_mergeh) +VSX_REDIRECT_2RG(vec_double2, vec_double2, vec_mergesql, vec_mergel) + + +// 2 and 4 channels interleave for all types except 2 lanes +#define VSX_IMPL_ST_INTERLEAVE(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, Tp* ptr) \ +{ \ + vsx_stf(vec_mergeh(a, b), 0, ptr); \ + vsx_stf(vec_mergel(a, b), 16, ptr); \ +} \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, const Tvec& d, Tp* ptr) \ +{ \ + Tvec ac = vec_mergeh(a, c); \ + Tvec bd = vec_mergeh(b, d); \ + vsx_stf(vec_mergeh(ac, bd), 0, ptr); \ + vsx_stf(vec_mergel(ac, bd), 16, ptr); \ + ac = vec_mergel(a, c); \ + bd = vec_mergel(b, d); \ + vsx_stf(vec_mergeh(ac, bd), 32, ptr); \ + vsx_stf(vec_mergel(ac, bd), 48, ptr); \ +} +VSX_IMPL_ST_INTERLEAVE(uchar, vec_uchar16) +VSX_IMPL_ST_INTERLEAVE(schar, vec_char16) +VSX_IMPL_ST_INTERLEAVE(ushort, vec_ushort8) +VSX_IMPL_ST_INTERLEAVE(short, vec_short8) +VSX_IMPL_ST_INTERLEAVE(uint, vec_uint4) +VSX_IMPL_ST_INTERLEAVE(int, vec_int4) +VSX_IMPL_ST_INTERLEAVE(float, vec_float4) + +// 2 and 4 channels deinterleave for 16 lanes +#define VSX_IMPL_ST_DINTERLEAVE_8(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(16, ptr); \ + a = vec_mergesqe(v0, v1); \ + b = vec_mergesqo(v0, v1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(16, ptr); \ + Tvec v2 = vsx_ld(32, ptr); \ + Tvec v3 = vsx_ld(48, ptr); \ + Tvec m0 = vec_mergesqe(v0, v1); \ + Tvec m1 = vec_mergesqe(v2, v3); \ + a = vec_mergesqe(m0, m1); \ + c = vec_mergesqo(m0, m1); \ + m0 = vec_mergesqo(v0, v1); \ + m1 = vec_mergesqo(v2, v3); \ + b = vec_mergesqe(m0, m1); \ + d = vec_mergesqo(m0, m1); \ +} +VSX_IMPL_ST_DINTERLEAVE_8(uchar, vec_uchar16) +VSX_IMPL_ST_DINTERLEAVE_8(schar, vec_char16) + +// 2 and 4 channels deinterleave for 8 lanes +#define VSX_IMPL_ST_DINTERLEAVE_16(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(8, ptr); \ + a = vec_mergesqe(v0, v1); \ + b = vec_mergesqo(v0, v1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(8, ptr); \ + Tvec m0 = vec_mergeh(v0, v1); \ + Tvec m1 = vec_mergel(v0, v1); \ + Tvec ab0 = vec_mergeh(m0, m1); \ + Tvec cd0 = vec_mergel(m0, m1); \ + v0 = vsx_ld(16, ptr); \ + v1 = vsx_ld(24, ptr); \ + m0 = vec_mergeh(v0, v1); \ + m1 = vec_mergel(v0, v1); \ + Tvec ab1 = vec_mergeh(m0, m1); \ + Tvec cd1 = vec_mergel(m0, m1); \ + a = vec_mergesqh(ab0, ab1); \ + b = vec_mergesql(ab0, ab1); \ + c = vec_mergesqh(cd0, cd1); \ + d = vec_mergesql(cd0, cd1); \ +} +VSX_IMPL_ST_DINTERLEAVE_16(ushort, vec_ushort8) +VSX_IMPL_ST_DINTERLEAVE_16(short, vec_short8) + +// 2 and 4 channels deinterleave for 4 lanes +#define VSX_IMPL_ST_DINTERLEAVE_32(Tp, Tvec) \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + a = vsx_ld(0, ptr); \ + b = vsx_ld(4, ptr); \ + Tvec m0 = vec_mergeh(a, b); \ + Tvec m1 = vec_mergel(a, b); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = vsx_ld(0, ptr); \ + Tvec v1 = vsx_ld(4, ptr); \ + Tvec v2 = vsx_ld(8, ptr); \ + Tvec v3 = vsx_ld(12, ptr); \ + Tvec m0 = vec_mergeh(v0, v2); \ + Tvec m1 = vec_mergeh(v1, v3); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ + m0 = vec_mergel(v0, v2); \ + m1 = vec_mergel(v1, v3); \ + c = vec_mergeh(m0, m1); \ + d = vec_mergel(m0, m1); \ +} +VSX_IMPL_ST_DINTERLEAVE_32(uint, vec_uint4) +VSX_IMPL_ST_DINTERLEAVE_32(int, vec_int4) +VSX_IMPL_ST_DINTERLEAVE_32(float, vec_float4) + +// 2 and 4 channels interleave and deinterleave for 2 lanes +#define VSX_IMPL_ST_D_INTERLEAVE_64(Tp, Tvec, ld_func, st_func) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_mergel(a, b), 2, ptr); \ +} \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, const Tvec& d, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_mergeh(c, d), 2, ptr); \ + st_func(vec_mergel(a, b), 4, ptr); \ + st_func(vec_mergel(c, d), 6, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b) \ +{ \ + Tvec m0 = ld_func(0, ptr); \ + Tvec m1 = ld_func(2, ptr); \ + a = vec_mergeh(m0, m1); \ + b = vec_mergel(m0, m1); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, \ + Tvec& c, Tvec& d) \ +{ \ + Tvec v0 = ld_func(0, ptr); \ + Tvec v1 = ld_func(2, ptr); \ + Tvec v2 = ld_func(4, ptr); \ + Tvec v3 = ld_func(6, ptr); \ + a = vec_mergeh(v0, v2); \ + b = vec_mergel(v0, v2); \ + c = vec_mergeh(v1, v3); \ + d = vec_mergel(v1, v3); \ +} +VSX_IMPL_ST_D_INTERLEAVE_64(int64, vec_dword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_D_INTERLEAVE_64(uint64, vec_udword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_D_INTERLEAVE_64(double, vec_double2, vsx_ld, vsx_st) + +/* 3 channels */ +#define VSX_IMPL_ST_INTERLEAVE_3CH_16(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + static const vec_uchar16 a12 = {0, 16, 0, 1, 17, 0, 2, 18, 0, 3, 19, 0, 4, 20, 0, 5}; \ + static const vec_uchar16 a123 = {0, 1, 16, 3, 4, 17, 6, 7, 18, 9, 10, 19, 12, 13, 20, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, a12), c, a123), 0, ptr); \ + static const vec_uchar16 b12 = {21, 0, 6, 22, 0, 7, 23, 0, 8, 24, 0, 9, 25, 0, 10, 26}; \ + static const vec_uchar16 b123 = {0, 21, 2, 3, 22, 5, 6, 23, 8, 9, 24, 11, 12, 25, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, b12), c, b123), 16, ptr); \ + static const vec_uchar16 c12 = {0, 11, 27, 0, 12, 28, 0, 13, 29, 0, 14, 30, 0, 15, 31, 0}; \ + static const vec_uchar16 c123 = {26, 1, 2, 27, 4, 5, 28, 7, 8, 29, 10, 11, 30, 13, 14, 31}; \ + vsx_st(vec_perm(vec_perm(a, b, c12), c, c123), 32, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(16, ptr); \ + Tvec v3 = vsx_ld(32, ptr); \ + static const vec_uchar16 a12_perm = {0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 a123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 17, 20, 23, 26, 29}; \ + a = vec_perm(vec_perm(v1, v2, a12_perm), v3, a123_perm); \ + static const vec_uchar16 b12_perm = {1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 b123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 21, 24, 27, 30}; \ + b = vec_perm(vec_perm(v1, v2, b12_perm), v3, b123_perm); \ + static const vec_uchar16 c12_perm = {2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 c123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 19, 22, 25, 28, 31}; \ + c = vec_perm(vec_perm(v1, v2, c12_perm), v3, c123_perm); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_16(uchar, vec_uchar16) +VSX_IMPL_ST_INTERLEAVE_3CH_16(schar, vec_char16) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_8(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + static const vec_uchar16 a12 = {0, 1, 16, 17, 0, 0, 2, 3, 18, 19, 0, 0, 4, 5, 20, 21}; \ + static const vec_uchar16 a123 = {0, 1, 2, 3, 16, 17, 6, 7, 8, 9, 18, 19, 12, 13, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, a12), c, a123), 0, ptr); \ + static const vec_uchar16 b12 = {0, 0, 6, 7, 22, 23, 0, 0, 8, 9, 24, 25, 0, 0, 10, 11}; \ + static const vec_uchar16 b123 = {20, 21, 2, 3, 4, 5, 22, 23, 8, 9, 10, 11, 24, 25, 14, 15}; \ + vsx_st(vec_perm(vec_perm(a, b, b12), c, b123), 8, ptr); \ + static const vec_uchar16 c12 = {26, 27, 0, 0, 12, 13, 28, 29, 0, 0, 14, 15, 30, 31, 0, 0}; \ + static const vec_uchar16 c123 = {0, 1, 26, 27, 4, 5, 6, 7, 28, 29, 10, 11, 12, 13, 30, 31}; \ + vsx_st(vec_perm(vec_perm(a, b, c12), c, c123), 16, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(8, ptr); \ + Tvec v3 = vsx_ld(16, ptr); \ + static const vec_uchar16 a12_perm = {0, 1, 6, 7, 12, 13, 18, 19, 24, 25, 30, 31, 0, 0, 0, 0}; \ + static const vec_uchar16 a123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 21, 26, 27}; \ + a = vec_perm(vec_perm(v1, v2, a12_perm), v3, a123_perm); \ + static const vec_uchar16 b12_perm = {2, 3, 8, 9, 14, 15, 20, 21, 26, 27, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 b123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 22, 23, 28, 29}; \ + b = vec_perm(vec_perm(v1, v2, b12_perm), v3, b123_perm); \ + static const vec_uchar16 c12_perm = {4, 5, 10, 11, 16, 17, 22, 23, 28, 29, 0, 0, 0, 0, 0, 0}; \ + static const vec_uchar16 c123_perm = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 18, 19, 24, 25, 30, 31}; \ + c = vec_perm(vec_perm(v1, v2, c12_perm), v3, c123_perm); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_8(ushort, vec_ushort8) +VSX_IMPL_ST_INTERLEAVE_3CH_8(short, vec_short8) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_4(Tp, Tvec) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + Tvec hbc = vec_mergeh(b, c); \ + static const vec_uchar16 ahbc = {0, 1, 2, 3, 16, 17, 18, 19, 20, 21, 22, 23, 4, 5, 6, 7}; \ + vsx_st(vec_perm(a, hbc, ahbc), 0, ptr); \ + Tvec lab = vec_mergel(a, b); \ + vsx_st(vec_sld(lab, hbc, 8), 4, ptr); \ + static const vec_uchar16 clab = {8, 9, 10, 11, 24, 25, 26, 27, 28, 29, 30, 31, 12, 13, 14, 15};\ + vsx_st(vec_perm(c, lab, clab), 8, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = vsx_ld(0, ptr); \ + Tvec v2 = vsx_ld(4, ptr); \ + Tvec v3 = vsx_ld(8, ptr); \ + static const vec_uchar16 flp = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 28, 29, 30, 31}; \ + a = vec_perm(v1, vec_sld(v3, v2, 8), flp); \ + static const vec_uchar16 flp2 = {28, 29, 30, 31, 0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19}; \ + b = vec_perm(v2, vec_sld(v1, v3, 8), flp2); \ + c = vec_perm(vec_sld(v2, v1, 8), v3, flp); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_4(uint, vec_uint4) +VSX_IMPL_ST_INTERLEAVE_3CH_4(int, vec_int4) +VSX_IMPL_ST_INTERLEAVE_3CH_4(float, vec_float4) + +#define VSX_IMPL_ST_INTERLEAVE_3CH_2(Tp, Tvec, ld_func, st_func) \ +VSX_FINLINE(void) vec_st_interleave(const Tvec& a, const Tvec& b, \ + const Tvec& c, Tp* ptr) \ +{ \ + st_func(vec_mergeh(a, b), 0, ptr); \ + st_func(vec_permi(c, a, 1), 2, ptr); \ + st_func(vec_mergel(b, c), 4, ptr); \ +} \ +VSX_FINLINE(void) vec_ld_deinterleave(const Tp* ptr, Tvec& a, \ + Tvec& b, Tvec& c) \ +{ \ + Tvec v1 = ld_func(0, ptr); \ + Tvec v2 = ld_func(2, ptr); \ + Tvec v3 = ld_func(4, ptr); \ + a = vec_permi(v1, v2, 1); \ + b = vec_permi(v1, v3, 2); \ + c = vec_permi(v2, v3, 1); \ +} +VSX_IMPL_ST_INTERLEAVE_3CH_2(int64, vec_dword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_INTERLEAVE_3CH_2(uint64, vec_udword2, vsx_ld2, vsx_st2) +VSX_IMPL_ST_INTERLEAVE_3CH_2(double, vec_double2, vsx_ld, vsx_st) + +#endif // CV_VSX + +//! @} + +#endif // OPENCV_HAL_VSX_UTILS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/core/wimage.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/core/wimage.hpp new file mode 100644 index 0000000..c7b6efa --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/core/wimage.hpp @@ -0,0 +1,603 @@ +/*M////////////////////////////////////////////////////////////////////////////// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to +// this license. If you do not agree to this license, do not download, +// install, copy or use the software. +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2008, Google, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation or contributors may not be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +///////////////////////////////////////////////////////////////////////////////// +//M*/ + +#ifndef OPENCV_CORE_WIMAGE_HPP +#define OPENCV_CORE_WIMAGE_HPP + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus + +namespace cv { + +//! @addtogroup core +//! @{ + +template class WImage; +template class WImageBuffer; +template class WImageView; + +template class WImageC; +template class WImageBufferC; +template class WImageViewC; + +// Commonly used typedefs. +typedef WImage WImage_b; +typedef WImageView WImageView_b; +typedef WImageBuffer WImageBuffer_b; + +typedef WImageC WImage1_b; +typedef WImageViewC WImageView1_b; +typedef WImageBufferC WImageBuffer1_b; + +typedef WImageC WImage3_b; +typedef WImageViewC WImageView3_b; +typedef WImageBufferC WImageBuffer3_b; + +typedef WImage WImage_f; +typedef WImageView WImageView_f; +typedef WImageBuffer WImageBuffer_f; + +typedef WImageC WImage1_f; +typedef WImageViewC WImageView1_f; +typedef WImageBufferC WImageBuffer1_f; + +typedef WImageC WImage3_f; +typedef WImageViewC WImageView3_f; +typedef WImageBufferC WImageBuffer3_f; + +// There isn't a standard for signed and unsigned short so be more +// explicit in the typename for these cases. +typedef WImage WImage_16s; +typedef WImageView WImageView_16s; +typedef WImageBuffer WImageBuffer_16s; + +typedef WImageC WImage1_16s; +typedef WImageViewC WImageView1_16s; +typedef WImageBufferC WImageBuffer1_16s; + +typedef WImageC WImage3_16s; +typedef WImageViewC WImageView3_16s; +typedef WImageBufferC WImageBuffer3_16s; + +typedef WImage WImage_16u; +typedef WImageView WImageView_16u; +typedef WImageBuffer WImageBuffer_16u; + +typedef WImageC WImage1_16u; +typedef WImageViewC WImageView1_16u; +typedef WImageBufferC WImageBuffer1_16u; + +typedef WImageC WImage3_16u; +typedef WImageViewC WImageView3_16u; +typedef WImageBufferC WImageBuffer3_16u; + +/** @brief Image class which provides a thin layer around an IplImage. + +The goals of the class design are: + + -# All the data has explicit ownership to avoid memory leaks + -# No hidden allocations or copies for performance. + -# Easy access to OpenCV methods (which will access IPP if available) + -# Can easily treat external data as an image + -# Easy to create images which are subsets of other images + -# Fast pixel access which can take advantage of number of channels if known at compile time. + +The WImage class is the image class which provides the data accessors. The 'W' comes from the fact +that it is also a wrapper around the popular but inconvenient IplImage class. A WImage can be +constructed either using a WImageBuffer class which allocates and frees the data, or using a +WImageView class which constructs a subimage or a view into external data. The view class does no +memory management. Each class actually has two versions, one when the number of channels is known +at compile time and one when it isn't. Using the one with the number of channels specified can +provide some compile time optimizations by using the fact that the number of channels is a +constant. + +We use the convention (c,r) to refer to column c and row r with (0,0) being the upper left corner. +This is similar to standard Euclidean coordinates with the first coordinate varying in the +horizontal direction and the second coordinate varying in the vertical direction. Thus (c,r) is +usually in the domain [0, width) X [0, height) + +Example usage: +@code +WImageBuffer3_b im(5,7); // Make a 5X7 3 channel image of type uchar +WImageView3_b sub_im(im, 2,2, 3,3); // 3X3 submatrix +vector vec(10, 3.0f); +WImageView1_f user_im(&vec[0], 2, 5); // 2X5 image w/ supplied data + +im.SetZero(); // same as cvSetZero(im.Ipl()) +*im(2, 3) = 15; // Modify the element at column 2, row 3 +MySetRand(&sub_im); + +// Copy the second row into the first. This can be done with no memory +// allocation and will use SSE if IPP is available. +int w = im.Width(); +im.View(0,0, w,1).CopyFrom(im.View(0,1, w,1)); + +// Doesn't care about source of data since using WImage +void MySetRand(WImage_b* im) { // Works with any number of channels +for (int r = 0; r < im->Height(); ++r) { + float* row = im->Row(r); + for (int c = 0; c < im->Width(); ++c) { + for (int ch = 0; ch < im->Channels(); ++ch, ++row) { + *row = uchar(rand() & 255); + } + } +} +} +@endcode + +Functions that are not part of the basic image allocation, viewing, and access should come from +OpenCV, except some useful functions that are not part of OpenCV can be found in wimage_util.h +*/ +template +class WImage +{ +public: + typedef T BaseType; + + // WImage is an abstract class with no other virtual methods so make the + // destructor virtual. + virtual ~WImage() = 0; + + // Accessors + IplImage* Ipl() {return image_; } + const IplImage* Ipl() const {return image_; } + T* ImageData() { return reinterpret_cast(image_->imageData); } + const T* ImageData() const { + return reinterpret_cast(image_->imageData); + } + + int Width() const {return image_->width; } + int Height() const {return image_->height; } + + // WidthStep is the number of bytes to go to the pixel with the next y coord + int WidthStep() const {return image_->widthStep; } + + int Channels() const {return image_->nChannels; } + int ChannelSize() const {return sizeof(T); } // number of bytes per channel + + // Number of bytes per pixel + int PixelSize() const {return Channels() * ChannelSize(); } + + // Return depth type (e.g. IPL_DEPTH_8U, IPL_DEPTH_32F) which is the number + // of bits per channel and with the signed bit set. + // This is known at compile time using specializations. + int Depth() const; + + inline const T* Row(int r) const { + return reinterpret_cast(image_->imageData + r*image_->widthStep); + } + + inline T* Row(int r) { + return reinterpret_cast(image_->imageData + r*image_->widthStep); + } + + // Pixel accessors which returns a pointer to the start of the channel + inline T* operator() (int c, int r) { + return reinterpret_cast(image_->imageData + r*image_->widthStep) + + c*Channels(); + } + + inline const T* operator() (int c, int r) const { + return reinterpret_cast(image_->imageData + r*image_->widthStep) + + c*Channels(); + } + + // Copy the contents from another image which is just a convenience to cvCopy + void CopyFrom(const WImage& src) { cvCopy(src.Ipl(), image_); } + + // Set contents to zero which is just a convenient to cvSetZero + void SetZero() { cvSetZero(image_); } + + // Construct a view into a region of this image + WImageView View(int c, int r, int width, int height); + +protected: + // Disallow copy and assignment + WImage(const WImage&); + void operator=(const WImage&); + + explicit WImage(IplImage* img) : image_(img) { + assert(!img || img->depth == Depth()); + } + + void SetIpl(IplImage* image) { + assert(!image || image->depth == Depth()); + image_ = image; + } + + IplImage* image_; +}; + + +/** Image class when both the pixel type and number of channels +are known at compile time. This wrapper will speed up some of the operations +like accessing individual pixels using the () operator. +*/ +template +class WImageC : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + explicit WImageC(IplImage* img) : WImage(img) { + assert(!img || img->nChannels == Channels()); + } + + // Construct a view into a region of this image + WImageViewC View(int c, int r, int width, int height); + + // Copy the contents from another image which is just a convenience to cvCopy + void CopyFrom(const WImageC& src) { + cvCopy(src.Ipl(), WImage::image_); + } + + // WImageC is an abstract class with no other virtual methods so make the + // destructor virtual. + virtual ~WImageC() = 0; + + int Channels() const {return C; } + +protected: + // Disallow copy and assignment + WImageC(const WImageC&); + void operator=(const WImageC&); + + void SetIpl(IplImage* image) { + assert(!image || image->depth == WImage::Depth()); + WImage::SetIpl(image); + } +}; + +/** Image class which owns the data, so it can be allocated and is always +freed. It cannot be copied but can be explicitly cloned. +*/ +template +class WImageBuffer : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + + // Default constructor which creates an object that can be + WImageBuffer() : WImage(0) {} + + WImageBuffer(int width, int height, int nchannels) : WImage(0) { + Allocate(width, height, nchannels); + } + + // Constructor which takes ownership of a given IplImage so releases + // the image on destruction. + explicit WImageBuffer(IplImage* img) : WImage(img) {} + + // Allocate an image. Does nothing if current size is the same as + // the new size. + void Allocate(int width, int height, int nchannels); + + // Set the data to point to an image, releasing the old data + void SetIpl(IplImage* img) { + ReleaseImage(); + WImage::SetIpl(img); + } + + // Clone an image which reallocates the image if of a different dimension. + void CloneFrom(const WImage& src) { + Allocate(src.Width(), src.Height(), src.Channels()); + CopyFrom(src); + } + + ~WImageBuffer() { + ReleaseImage(); + } + + // Release the image if it isn't null. + void ReleaseImage() { + if (WImage::image_) { + IplImage* image = WImage::image_; + cvReleaseImage(&image); + WImage::SetIpl(0); + } + } + + bool IsNull() const {return WImage::image_ == NULL; } + +private: + // Disallow copy and assignment + WImageBuffer(const WImageBuffer&); + void operator=(const WImageBuffer&); +}; + +/** Like a WImageBuffer class but when the number of channels is known at compile time. +*/ +template +class WImageBufferC : public WImageC +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + // Default constructor which creates an object that can be + WImageBufferC() : WImageC(0) {} + + WImageBufferC(int width, int height) : WImageC(0) { + Allocate(width, height); + } + + // Constructor which takes ownership of a given IplImage so releases + // the image on destruction. + explicit WImageBufferC(IplImage* img) : WImageC(img) {} + + // Allocate an image. Does nothing if current size is the same as + // the new size. + void Allocate(int width, int height); + + // Set the data to point to an image, releasing the old data + void SetIpl(IplImage* img) { + ReleaseImage(); + WImageC::SetIpl(img); + } + + // Clone an image which reallocates the image if of a different dimension. + void CloneFrom(const WImageC& src) { + Allocate(src.Width(), src.Height()); + CopyFrom(src); + } + + ~WImageBufferC() { + ReleaseImage(); + } + + // Release the image if it isn't null. + void ReleaseImage() { + if (WImage::image_) { + IplImage* image = WImage::image_; + cvReleaseImage(&image); + WImageC::SetIpl(0); + } + } + + bool IsNull() const {return WImage::image_ == NULL; } + +private: + // Disallow copy and assignment + WImageBufferC(const WImageBufferC&); + void operator=(const WImageBufferC&); +}; + +/** View into an image class which allows treating a subimage as an image or treating external data +as an image +*/ +template class WImageView : public WImage +{ +public: + typedef typename WImage::BaseType BaseType; + + // Construct a subimage. No checks are done that the subimage lies + // completely inside the original image. + WImageView(WImage* img, int c, int r, int width, int height); + + // Refer to external data. + // If not given width_step assumed to be same as width. + WImageView(T* data, int width, int height, int channels, int width_step = -1); + + // Refer to external data. This does NOT take ownership + // of the supplied IplImage. + WImageView(IplImage* img) : WImage(img) {} + + // Copy constructor + WImageView(const WImage& img) : WImage(0) { + header_ = *(img.Ipl()); + WImage::SetIpl(&header_); + } + + WImageView& operator=(const WImage& img) { + header_ = *(img.Ipl()); + WImage::SetIpl(&header_); + return *this; + } + +protected: + IplImage header_; +}; + + +template +class WImageViewC : public WImageC +{ +public: + typedef typename WImage::BaseType BaseType; + enum { kChannels = C }; + + // Default constructor needed for vectors of views. + WImageViewC(); + + virtual ~WImageViewC() {} + + // Construct a subimage. No checks are done that the subimage lies + // completely inside the original image. + WImageViewC(WImageC* img, + int c, int r, int width, int height); + + // Refer to external data + WImageViewC(T* data, int width, int height, int width_step = -1); + + // Refer to external data. This does NOT take ownership + // of the supplied IplImage. + WImageViewC(IplImage* img) : WImageC(img) {} + + // Copy constructor which does a shallow copy to allow multiple views + // of same data. gcc-4.1.1 gets confused if both versions of + // the constructor and assignment operator are not provided. + WImageViewC(const WImageC& img) : WImageC(0) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + } + WImageViewC(const WImageViewC& img) : WImageC(0) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + } + + WImageViewC& operator=(const WImageC& img) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + return *this; + } + WImageViewC& operator=(const WImageViewC& img) { + header_ = *(img.Ipl()); + WImageC::SetIpl(&header_); + return *this; + } + +protected: + IplImage header_; +}; + + +// Specializations for depth +template<> +inline int WImage::Depth() const {return IPL_DEPTH_8U; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_8S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_16S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_16U; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_32S; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_32F; } +template<> +inline int WImage::Depth() const {return IPL_DEPTH_64F; } + +template inline WImage::~WImage() {} +template inline WImageC::~WImageC() {} + +template +inline void WImageBuffer::Allocate(int width, int height, int nchannels) +{ + if (IsNull() || WImage::Width() != width || + WImage::Height() != height || WImage::Channels() != nchannels) { + ReleaseImage(); + WImage::image_ = cvCreateImage(cvSize(width, height), + WImage::Depth(), nchannels); + } +} + +template +inline void WImageBufferC::Allocate(int width, int height) +{ + if (IsNull() || WImage::Width() != width || WImage::Height() != height) { + ReleaseImage(); + WImageC::SetIpl(cvCreateImage(cvSize(width, height),WImage::Depth(), C)); + } +} + +template +WImageView::WImageView(WImage* img, int c, int r, int width, int height) + : WImage(0) +{ + header_ = *(img->Ipl()); + header_.imageData = reinterpret_cast((*img)(c, r)); + header_.width = width; + header_.height = height; + WImage::SetIpl(&header_); +} + +template +WImageView::WImageView(T* data, int width, int height, int nchannels, int width_step) + : WImage(0) +{ + cvInitImageHeader(&header_, cvSize(width, height), WImage::Depth(), nchannels); + header_.imageData = reinterpret_cast(data); + if (width_step > 0) { + header_.widthStep = width_step; + } + WImage::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC(WImageC* img, int c, int r, int width, int height) + : WImageC(0) +{ + header_ = *(img->Ipl()); + header_.imageData = reinterpret_cast((*img)(c, r)); + header_.width = width; + header_.height = height; + WImageC::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC() : WImageC(0) { + cvInitImageHeader(&header_, cvSize(0, 0), WImage::Depth(), C); + header_.imageData = reinterpret_cast(0); + WImageC::SetIpl(&header_); +} + +template +WImageViewC::WImageViewC(T* data, int width, int height, int width_step) + : WImageC(0) +{ + cvInitImageHeader(&header_, cvSize(width, height), WImage::Depth(), C); + header_.imageData = reinterpret_cast(data); + if (width_step > 0) { + header_.widthStep = width_step; + } + WImageC::SetIpl(&header_); +} + +// Construct a view into a region of an image +template +WImageView WImage::View(int c, int r, int width, int height) { + return WImageView(this, c, r, width, height); +} + +template +WImageViewC WImageC::View(int c, int r, int width, int height) { + return WImageViewC(this, c, r, width, height); +} + +//! @} core + +} // end of namespace + +#endif // __cplusplus + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/cvconfig.h b/hgdriver/3rdparty/opencv/include/win/opencv2/cvconfig.h new file mode 100644 index 0000000..0fd8109 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/cvconfig.h @@ -0,0 +1,250 @@ +#ifndef OPENCV_CVCONFIG_H_INCLUDED +#define OPENCV_CVCONFIG_H_INCLUDED + +/* OpenCV compiled as static or dynamic libs */ +/* #undef BUILD_SHARED_LIBS */ + +/* OpenCV intrinsics optimized code */ +#define CV_ENABLE_INTRINSICS + +/* OpenCV additional optimized code */ +/* #undef CV_DISABLE_OPTIMIZATION */ + +/* Compile for 'real' NVIDIA GPU architectures */ +#define CUDA_ARCH_BIN "" + +/* NVIDIA GPU features are used */ +#define CUDA_ARCH_FEATURES "" + +/* Compile for 'virtual' NVIDIA PTX architectures */ +#define CUDA_ARCH_PTX "" + +/* AVFoundation video libraries */ +/* #undef HAVE_AVFOUNDATION */ + +/* V4L capturing support */ +/* #undef HAVE_CAMV4L */ + +/* V4L2 capturing support */ +/* #undef HAVE_CAMV4L2 */ + +/* Carbon windowing environment */ +/* #undef HAVE_CARBON */ + +/* AMD's Basic Linear Algebra Subprograms Library*/ +/* #undef HAVE_CLAMDBLAS */ + +/* AMD's OpenCL Fast Fourier Transform Library*/ +/* #undef HAVE_CLAMDFFT */ + +/* Clp support */ +/* #undef HAVE_CLP */ + +/* Cocoa API */ +/* #undef HAVE_COCOA */ + +/* C= */ +/* #undef HAVE_CSTRIPES */ + +/* NVIDIA CUDA Basic Linear Algebra Subprograms (BLAS) API*/ +/* #undef HAVE_CUBLAS */ + +/* NVIDIA CUDA Runtime API*/ +/* #undef HAVE_CUDA */ + +/* NVIDIA CUDA Fast Fourier Transform (FFT) API*/ +/* #undef HAVE_CUFFT */ + +/* IEEE1394 capturing support */ +/* #undef HAVE_DC1394 */ + +/* IEEE1394 capturing support - libdc1394 v2.x */ +/* #undef HAVE_DC1394_2 */ + +/* DirectX */ +#define HAVE_DIRECTX +#define HAVE_DIRECTX_NV12 +#define HAVE_D3D11 +#define HAVE_D3D10 +#define HAVE_D3D9 + +/* DirectShow Video Capture library */ +#define HAVE_DSHOW + +/* Eigen Matrix & Linear Algebra Library */ +/* #undef HAVE_EIGEN */ + +/* FFMpeg video library */ +/* #undef HAVE_FFMPEG */ + +/* Geospatial Data Abstraction Library */ +/* #undef HAVE_GDAL */ + +/* GStreamer multimedia framework */ +/* #undef HAVE_GSTREAMER */ + +/* GTK+ 2.0 Thread support */ +/* #undef HAVE_GTHREAD */ + +/* GTK+ 2.x toolkit */ +/* #undef HAVE_GTK */ + +/* Halide support */ +/* #undef HAVE_HALIDE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Intel Perceptual Computing SDK library */ +/* #undef HAVE_INTELPERC */ + +/* Intel Integrated Performance Primitives */ +/* #undef HAVE_IPP */ +/* #undef HAVE_IPP_ICV */ +/* #undef HAVE_IPP_IW */ +/* #undef HAVE_IPP_IW_LL */ + +/* JPEG-2000 codec */ +#define HAVE_JASPER + +/* IJG JPEG codec */ +#define HAVE_JPEG + +/* libpng/png.h needs to be included */ +/* #undef HAVE_LIBPNG_PNG_H */ + +/* GDCM DICOM codec */ +/* #undef HAVE_GDCM */ + +/* V4L/V4L2 capturing support via libv4l */ +/* #undef HAVE_LIBV4L */ + +/* Microsoft Media Foundation Capture library */ +#define HAVE_MSMF + +/* NVIDIA Video Decoding API*/ +/* #undef HAVE_NVCUVID */ +/* #undef HAVE_NVCUVID_HEADER */ +/* #undef HAVE_DYNLINK_NVCUVID_HEADER */ + +/* NVIDIA Video Encoding API*/ +/* #undef HAVE_NVCUVENC */ + +/* OpenCL Support */ +#define HAVE_OPENCL +/* #undef HAVE_OPENCL_STATIC */ +/* #undef HAVE_OPENCL_SVM */ + +/* NVIDIA OpenCL D3D Extensions support */ +#define HAVE_OPENCL_D3D11_NV + +/* OpenEXR codec */ +#define HAVE_OPENEXR + +/* OpenGL support*/ +/* #undef HAVE_OPENGL */ + +/* OpenNI library */ +/* #undef HAVE_OPENNI */ + +/* OpenNI library */ +/* #undef HAVE_OPENNI2 */ + +/* PNG codec */ +#define HAVE_PNG + +/* Posix threads (pthreads) */ +/* #undef HAVE_PTHREAD */ + +/* parallel_for with pthreads */ +/* #undef HAVE_PTHREADS_PF */ + +/* Qt support */ +/* #undef HAVE_QT */ + +/* Qt OpenGL support */ +/* #undef HAVE_QT_OPENGL */ + +/* QuickTime video libraries */ +/* #undef HAVE_QUICKTIME */ + +/* QTKit video libraries */ +/* #undef HAVE_QTKIT */ + +/* Intel Threading Building Blocks */ +/* #undef HAVE_TBB */ + +/* TIFF codec */ +#define HAVE_TIFF + +/* Unicap video capture library */ +/* #undef HAVE_UNICAP */ + +/* Video for Windows support */ +/* #undef HAVE_VFW */ + +/* V4L2 capturing support in videoio.h */ +/* #undef HAVE_VIDEOIO */ + +/* Win32 UI */ +/* #undef HAVE_WIN32UI */ + +/* XIMEA camera support */ +/* #undef HAVE_XIMEA */ + +/* Xine video library */ +/* #undef HAVE_XINE */ + +/* Define if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* gPhoto2 library */ +/* #undef HAVE_GPHOTO2 */ + +/* VA library (libva) */ +/* #undef HAVE_VA */ + +/* Intel VA-API/OpenCL */ +/* #undef HAVE_VA_INTEL */ + +/* Intel Media SDK */ +/* #undef HAVE_MFX */ + +/* Lapack */ +/* #undef HAVE_LAPACK */ + +/* Library was compiled with functions instrumentation */ +/* #undef ENABLE_INSTRUMENTATION */ + +/* OpenVX */ +/* #undef HAVE_OPENVX */ + +#if defined(HAVE_XINE) || \ + defined(HAVE_GSTREAMER) || \ + defined(HAVE_QUICKTIME) || \ + defined(HAVE_QTKIT) || \ + defined(HAVE_AVFOUNDATION) || \ + /*defined(HAVE_OPENNI) || too specialized */ \ + defined(HAVE_FFMPEG) || \ + defined(HAVE_MSMF) +#define HAVE_VIDEO_INPUT +#endif + +#if /*defined(HAVE_XINE) || */\ + defined(HAVE_GSTREAMER) || \ + defined(HAVE_QUICKTIME) || \ + defined(HAVE_QTKIT) || \ + defined(HAVE_AVFOUNDATION) || \ + defined(HAVE_FFMPEG) || \ + defined(HAVE_MSMF) +#define HAVE_VIDEO_OUTPUT +#endif + +/* OpenCV trace utilities */ +#define OPENCV_TRACE + +/* Library QR-code decoding */ +#define HAVE_QUIRC + +#endif // OPENCV_CVCONFIG_H_INCLUDED diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/highgui.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui.hpp new file mode 100644 index 0000000..6a8c598 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui.hpp @@ -0,0 +1,857 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HIGHGUI_HPP +#define OPENCV_HIGHGUI_HPP + +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio.hpp" +#endif + +/** +@defgroup highgui High-level GUI + +While OpenCV was designed for use in full-scale applications and can be used within functionally +rich UI frameworks (such as Qt\*, WinForms\*, or Cocoa\*) or without any UI at all, sometimes there +it is required to try functionality quickly and visualize the results. This is what the HighGUI +module has been designed for. + +It provides easy interface to: + +- Create and manipulate windows that can display images and "remember" their content (no need to + handle repaint events from OS). +- Add trackbars to the windows, handle simple mouse events as well as keyboard commands. + +@{ + @defgroup highgui_window_flags Flags related creating and manipulating HighGUI windows and mouse events + @defgroup highgui_opengl OpenGL support + @defgroup highgui_qt Qt New Functions + + ![image](pics/qtgui.png) + + This figure explains new functionality implemented with Qt\* GUI. The new GUI provides a statusbar, + a toolbar, and a control panel. The control panel can have trackbars and buttonbars attached to it. + If you cannot see the control panel, press Ctrl+P or right-click any Qt window and select **Display + properties window**. + + - To attach a trackbar, the window name parameter must be NULL. + + - To attach a buttonbar, a button must be created. If the last bar attached to the control panel + is a buttonbar, the new button is added to the right of the last button. If the last bar + attached to the control panel is a trackbar, or the control panel is empty, a new buttonbar is + created. Then, a new button is attached to it. + + See below the example used to generate the figure: + @code + int main(int argc, char *argv[]) + { + + int value = 50; + int value2 = 0; + + + namedWindow("main1",WINDOW_NORMAL); + namedWindow("main2",WINDOW_AUTOSIZE | WINDOW_GUI_NORMAL); + createTrackbar( "track1", "main1", &value, 255, NULL); + + String nameb1 = "button1"; + String nameb2 = "button2"; + + createButton(nameb1,callbackButton,&nameb1,QT_CHECKBOX,1); + createButton(nameb2,callbackButton,NULL,QT_CHECKBOX,0); + createTrackbar( "track2", NULL, &value2, 255, NULL); + createButton("button5",callbackButton1,NULL,QT_RADIOBOX,0); + createButton("button6",callbackButton2,NULL,QT_RADIOBOX,1); + + setMouseCallback( "main2",on_mouse,NULL ); + + Mat img1 = imread("files/flower.jpg"); + VideoCapture video; + video.open("files/hockey.avi"); + + Mat img2,img3; + + while( waitKey(33) != 27 ) + { + img1.convertTo(img2,-1,1,value); + video >> img3; + + imshow("main1",img2); + imshow("main2",img3); + } + + destroyAllWindows(); + + return 0; + } + @endcode + + + @defgroup highgui_winrt WinRT support + + This figure explains new functionality implemented with WinRT GUI. The new GUI provides an Image control, + and a slider panel. Slider panel holds trackbars attached to it. + + Sliders are attached below the image control. Every new slider is added below the previous one. + + See below the example used to generate the figure: + @code + void sample_app::MainPage::ShowWindow() + { + static cv::String windowName("sample"); + cv::winrt_initContainer(this->cvContainer); + cv::namedWindow(windowName); // not required + + cv::Mat image = cv::imread("Assets/sample.jpg"); + cv::Mat converted = cv::Mat(image.rows, image.cols, CV_8UC4); + cv::cvtColor(image, converted, COLOR_BGR2BGRA); + cv::imshow(windowName, converted); // this will create window if it hasn't been created before + + int state = 42; + cv::TrackbarCallback callback = [](int pos, void* userdata) + { + if (pos == 0) { + cv::destroyWindow(windowName); + } + }; + cv::TrackbarCallback callbackTwin = [](int pos, void* userdata) + { + if (pos >= 70) { + cv::destroyAllWindows(); + } + }; + cv::createTrackbar("Sample trackbar", windowName, &state, 100, callback); + cv::createTrackbar("Twin brother", windowName, &state, 100, callbackTwin); + } + @endcode + + @defgroup highgui_c C API +@} +*/ + +///////////////////////// graphical user interface ////////////////////////// +namespace cv +{ + +//! @addtogroup highgui +//! @{ + +//! @addtogroup highgui_window_flags +//! @{ + +//! Flags for cv::namedWindow +enum WindowFlags { + WINDOW_NORMAL = 0x00000000, //!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size. + WINDOW_AUTOSIZE = 0x00000001, //!< the user cannot resize the window, the size is constrainted by the image displayed. + WINDOW_OPENGL = 0x00001000, //!< window with opengl support. + + WINDOW_FULLSCREEN = 1, //!< change the window to fullscreen. + WINDOW_FREERATIO = 0x00000100, //!< the image expends as much as it can (no ratio constraint). + WINDOW_KEEPRATIO = 0x00000000, //!< the ratio of the image is respected. + WINDOW_GUI_EXPANDED=0x00000000, //!< status bar and tool bar + WINDOW_GUI_NORMAL = 0x00000010, //!< old fashious way + }; + +//! Flags for cv::setWindowProperty / cv::getWindowProperty +enum WindowPropertyFlags { + WND_PROP_FULLSCREEN = 0, //!< fullscreen property (can be WINDOW_NORMAL or WINDOW_FULLSCREEN). + WND_PROP_AUTOSIZE = 1, //!< autosize property (can be WINDOW_NORMAL or WINDOW_AUTOSIZE). + WND_PROP_ASPECT_RATIO = 2, //!< window's aspect ration (can be set to WINDOW_FREERATIO or WINDOW_KEEPRATIO). + WND_PROP_OPENGL = 3, //!< opengl support. + WND_PROP_VISIBLE = 4, //!< checks whether the window exists and is visible + WND_PROP_TOPMOST = 5 //!< property to toggle normal window being topmost or not + }; + +//! Mouse Events see cv::MouseCallback +enum MouseEventTypes { + EVENT_MOUSEMOVE = 0, //!< indicates that the mouse pointer has moved over the window. + EVENT_LBUTTONDOWN = 1, //!< indicates that the left mouse button is pressed. + EVENT_RBUTTONDOWN = 2, //!< indicates that the right mouse button is pressed. + EVENT_MBUTTONDOWN = 3, //!< indicates that the middle mouse button is pressed. + EVENT_LBUTTONUP = 4, //!< indicates that left mouse button is released. + EVENT_RBUTTONUP = 5, //!< indicates that right mouse button is released. + EVENT_MBUTTONUP = 6, //!< indicates that middle mouse button is released. + EVENT_LBUTTONDBLCLK = 7, //!< indicates that left mouse button is double clicked. + EVENT_RBUTTONDBLCLK = 8, //!< indicates that right mouse button is double clicked. + EVENT_MBUTTONDBLCLK = 9, //!< indicates that middle mouse button is double clicked. + EVENT_MOUSEWHEEL = 10,//!< positive and negative values mean forward and backward scrolling, respectively. + EVENT_MOUSEHWHEEL = 11 //!< positive and negative values mean right and left scrolling, respectively. + }; + +//! Mouse Event Flags see cv::MouseCallback +enum MouseEventFlags { + EVENT_FLAG_LBUTTON = 1, //!< indicates that the left mouse button is down. + EVENT_FLAG_RBUTTON = 2, //!< indicates that the right mouse button is down. + EVENT_FLAG_MBUTTON = 4, //!< indicates that the middle mouse button is down. + EVENT_FLAG_CTRLKEY = 8, //!< indicates that CTRL Key is pressed. + EVENT_FLAG_SHIFTKEY = 16,//!< indicates that SHIFT Key is pressed. + EVENT_FLAG_ALTKEY = 32 //!< indicates that ALT Key is pressed. + }; + +//! @} highgui_window_flags + +//! @addtogroup highgui_qt +//! @{ + +//! Qt font weight +enum QtFontWeights { + QT_FONT_LIGHT = 25, //!< Weight of 25 + QT_FONT_NORMAL = 50, //!< Weight of 50 + QT_FONT_DEMIBOLD = 63, //!< Weight of 63 + QT_FONT_BOLD = 75, //!< Weight of 75 + QT_FONT_BLACK = 87 //!< Weight of 87 + }; + +//! Qt font style +enum QtFontStyles { + QT_STYLE_NORMAL = 0, //!< Normal font. + QT_STYLE_ITALIC = 1, //!< Italic font. + QT_STYLE_OBLIQUE = 2 //!< Oblique font. + }; + +//! Qt "button" type +enum QtButtonTypes { + QT_PUSH_BUTTON = 0, //!< Push button. + QT_CHECKBOX = 1, //!< Checkbox button. + QT_RADIOBOX = 2, //!< Radiobox button. + QT_NEW_BUTTONBAR = 1024 //!< Button should create a new buttonbar + }; + +//! @} highgui_qt + +/** @brief Callback function for mouse events. see cv::setMouseCallback +@param event one of the cv::MouseEventTypes constants. +@param x The x-coordinate of the mouse event. +@param y The y-coordinate of the mouse event. +@param flags one of the cv::MouseEventFlags constants. +@param userdata The optional parameter. + */ +typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata); + +/** @brief Callback function for Trackbar see cv::createTrackbar +@param pos current position of the specified trackbar. +@param userdata The optional parameter. + */ +typedef void (*TrackbarCallback)(int pos, void* userdata); + +/** @brief Callback function defined to be called every frame. See cv::setOpenGlDrawCallback +@param userdata The optional parameter. + */ +typedef void (*OpenGlDrawCallback)(void* userdata); + +/** @brief Callback function for a button created by cv::createButton +@param state current state of the button. It could be -1 for a push button, 0 or 1 for a check/radio box button. +@param userdata The optional parameter. + */ +typedef void (*ButtonCallback)(int state, void* userdata); + +/** @brief Creates a window. + +The function namedWindow creates a window that can be used as a placeholder for images and +trackbars. Created windows are referred to by their names. + +If a window with the same name already exists, the function does nothing. + +You can call cv::destroyWindow or cv::destroyAllWindows to close the window and de-allocate any associated +memory usage. For a simple program, you do not really have to call these functions because all the +resources and windows of the application are closed automatically by the operating system upon exit. + +@note + +Qt backend supports additional flags: + - **WINDOW_NORMAL or WINDOW_AUTOSIZE:** WINDOW_NORMAL enables you to resize the + window, whereas WINDOW_AUTOSIZE adjusts automatically the window size to fit the + displayed image (see imshow ), and you cannot change the window size manually. + - **WINDOW_FREERATIO or WINDOW_KEEPRATIO:** WINDOW_FREERATIO adjusts the image + with no respect to its ratio, whereas WINDOW_KEEPRATIO keeps the image ratio. + - **WINDOW_GUI_NORMAL or WINDOW_GUI_EXPANDED:** WINDOW_GUI_NORMAL is the old way to draw the window + without statusbar and toolbar, whereas WINDOW_GUI_EXPANDED is a new enhanced GUI. +By default, flags == WINDOW_AUTOSIZE | WINDOW_KEEPRATIO | WINDOW_GUI_EXPANDED + +@param winname Name of the window in the window caption that may be used as a window identifier. +@param flags Flags of the window. The supported flags are: (cv::WindowFlags) + */ +CV_EXPORTS_W void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE); + +/** @brief Destroys the specified window. + +The function destroyWindow destroys the window with the given name. + +@param winname Name of the window to be destroyed. + */ +CV_EXPORTS_W void destroyWindow(const String& winname); + +/** @brief Destroys all of the HighGUI windows. + +The function destroyAllWindows destroys all of the opened HighGUI windows. + */ +CV_EXPORTS_W void destroyAllWindows(); + +CV_EXPORTS_W int startWindowThread(); + +/** @brief Similar to #waitKey, but returns full key code. + +@note + +Key code is implementation specific and depends on used backend: QT/GTK/Win32/etc + +*/ +CV_EXPORTS_W int waitKeyEx(int delay = 0); + +/** @brief Waits for a pressed key. + +The function waitKey waits for a key event infinitely (when \f$\texttt{delay}\leq 0\f$ ) or for delay +milliseconds, when it is positive. Since the OS has a minimum time between switching threads, the +function will not wait exactly delay ms, it will wait at least delay ms, depending on what else is +running on your computer at that time. It returns the code of the pressed key or -1 if no key was +pressed before the specified time had elapsed. + +@note + +This function is the only method in HighGUI that can fetch and handle events, so it needs to be +called periodically for normal event processing unless HighGUI is used within an environment that +takes care of event processing. + +@note + +The function only works if there is at least one HighGUI window created and the window is active. +If there are several HighGUI windows, any of them can be active. + +@param delay Delay in milliseconds. 0 is the special value that means "forever". + */ +CV_EXPORTS_W int waitKey(int delay = 0); + +/** @brief Displays an image in the specified window. + +The function imshow displays an image in the specified window. If the window was created with the +cv::WINDOW_AUTOSIZE flag, the image is shown with its original size, however it is still limited by the screen resolution. +Otherwise, the image is scaled to fit the window. The function may scale the image, depending on its depth: + +- If the image is 8-bit unsigned, it is displayed as is. +- If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the + value range [0,255\*256] is mapped to [0,255]. +- If the image is 32-bit or 64-bit floating-point, the pixel values are multiplied by 255. That is, the + value range [0,1] is mapped to [0,255]. + +If window was created with OpenGL support, cv::imshow also support ogl::Buffer , ogl::Texture2D and +cuda::GpuMat as input. + +If the window was not created before this function, it is assumed creating a window with cv::WINDOW_AUTOSIZE. + +If you need to show an image that is bigger than the screen resolution, you will need to call namedWindow("", WINDOW_NORMAL) before the imshow. + +@note This function should be followed by cv::waitKey function which displays the image for specified +milliseconds. Otherwise, it won't display the image. For example, **waitKey(0)** will display the window +infinitely until any keypress (it is suitable for image display). **waitKey(25)** will display a frame +for 25 ms, after which display will be automatically closed. (If you put it in a loop to read +videos, it will display the video frame-by-frame) + +@note + +[__Windows Backend Only__] Pressing Ctrl+C will copy the image to the clipboard. + +[__Windows Backend Only__] Pressing Ctrl+S will show a dialog to save the image. + +@param winname Name of the window. +@param mat Image to be shown. + */ +CV_EXPORTS_W void imshow(const String& winname, InputArray mat); + +/** @brief Resizes the window to the specified size + +@note + +- The specified window size is for the image area. Toolbars are not counted. +- Only windows created without cv::WINDOW_AUTOSIZE flag can be resized. + +@param winname Window name. +@param width The new window width. +@param height The new window height. + */ +CV_EXPORTS_W void resizeWindow(const String& winname, int width, int height); + +/** @overload +@param winname Window name. +@param size The new window size. +*/ +CV_EXPORTS_W void resizeWindow(const String& winname, const cv::Size& size); + +/** @brief Moves the window to the specified position + +@param winname Name of the window. +@param x The new x-coordinate of the window. +@param y The new y-coordinate of the window. + */ +CV_EXPORTS_W void moveWindow(const String& winname, int x, int y); + +/** @brief Changes parameters of a window dynamically. + +The function setWindowProperty enables changing properties of a window. + +@param winname Name of the window. +@param prop_id Window property to edit. The supported operation flags are: (cv::WindowPropertyFlags) +@param prop_value New value of the window property. The supported flags are: (cv::WindowFlags) + */ +CV_EXPORTS_W void setWindowProperty(const String& winname, int prop_id, double prop_value); + +/** @brief Updates window title +@param winname Name of the window. +@param title New title. +*/ +CV_EXPORTS_W void setWindowTitle(const String& winname, const String& title); + +/** @brief Provides parameters of a window. + +The function getWindowProperty returns properties of a window. + +@param winname Name of the window. +@param prop_id Window property to retrieve. The following operation flags are available: (cv::WindowPropertyFlags) + +@sa setWindowProperty + */ +CV_EXPORTS_W double getWindowProperty(const String& winname, int prop_id); + +/** @brief Provides rectangle of image in the window. + +The function getWindowImageRect returns the client screen coordinates, width and height of the image rendering area. + +@param winname Name of the window. + +@sa resizeWindow moveWindow + */ +CV_EXPORTS_W Rect getWindowImageRect(const String& winname); + +/** @example samples/cpp/create_mask.cpp +This program demonstrates using mouse events and how to make and use a mask image (black and white) . +*/ +/** @brief Sets mouse handler for the specified window + +@param winname Name of the window. +@param onMouse Callback function for mouse events. See OpenCV samples on how to specify and use the callback. +@param userdata The optional parameter passed to the callback. + */ +CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0); + +/** @brief Gets the mouse-wheel motion delta, when handling mouse-wheel events cv::EVENT_MOUSEWHEEL and +cv::EVENT_MOUSEHWHEEL. + +For regular mice with a scroll-wheel, delta will be a multiple of 120. The value 120 corresponds to +a one notch rotation of the wheel or the threshold for action to be taken and one such action should +occur for each delta. Some high-precision mice with higher-resolution freely-rotating wheels may +generate smaller values. + +For cv::EVENT_MOUSEWHEEL positive and negative values mean forward and backward scrolling, +respectively. For cv::EVENT_MOUSEHWHEEL, where available, positive and negative values mean right and +left scrolling, respectively. + +@note + +Mouse-wheel events are currently supported only on Windows. + +@param flags The mouse callback flags parameter. + */ +CV_EXPORTS int getMouseWheelDelta(int flags); + +/** @brief Allows users to select a ROI on the given image. + +The function creates a window and allows users to select a ROI using the mouse. +Controls: use `space` or `enter` to finish selection, use key `c` to cancel selection (function will return the zero cv::Rect). + +@param windowName name of the window where selection process will be shown. +@param img image to select a ROI. +@param showCrosshair if true crosshair of selection rectangle will be shown. +@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of +selection rectangle will correspont to the initial mouse position. +@return selected ROI or empty rect if selection canceled. + +@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...). +After finish of work an empty callback will be set for the used window. + */ +CV_EXPORTS_W Rect selectROI(const String& windowName, InputArray img, bool showCrosshair = true, bool fromCenter = false); + +/** @overload + */ +CV_EXPORTS_W Rect selectROI(InputArray img, bool showCrosshair = true, bool fromCenter = false); + +/** @brief Allows users to select multiple ROIs on the given image. + +The function creates a window and allows users to select multiple ROIs using the mouse. +Controls: use `space` or `enter` to finish current selection and start a new one, +use `esc` to terminate multiple ROI selection process. + +@param windowName name of the window where selection process will be shown. +@param img image to select a ROI. +@param boundingBoxes selected ROIs. +@param showCrosshair if true crosshair of selection rectangle will be shown. +@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of +selection rectangle will correspont to the initial mouse position. + +@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...). +After finish of work an empty callback will be set for the used window. + */ +CV_EXPORTS_W void selectROIs(const String& windowName, InputArray img, + CV_OUT std::vector& boundingBoxes, bool showCrosshair = true, bool fromCenter = false); + +/** @brief Creates a trackbar and attaches it to the specified window. + +The function createTrackbar creates a trackbar (a slider or range control) with the specified name +and range, assigns a variable value to be a position synchronized with the trackbar and specifies +the callback function onChange to be called on the trackbar position change. The created trackbar is +displayed in the specified window winname. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar should be attached to the +control panel. + +Clicking the label of each trackbar enables editing the trackbar values manually. + +@param trackbarname Name of the created trackbar. +@param winname Name of the window that will be used as a parent of the created trackbar. +@param value Optional pointer to an integer variable whose value reflects the position of the +slider. Upon creation, the slider position is defined by this variable. +@param count Maximal position of the slider. The minimal position is always 0. +@param onChange Pointer to the function to be called every time the slider changes position. This +function should be prototyped as void Foo(int,void\*); , where the first parameter is the trackbar +position and the second parameter is the user data (see the next parameter). If the callback is +the NULL pointer, no callbacks are called, but only value is updated. +@param userdata User data that is passed as is to the callback. It can be used to handle trackbar +events without using global variables. + */ +CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname, + int* value, int count, + TrackbarCallback onChange = 0, + void* userdata = 0); + +/** @brief Returns the trackbar position. + +The function returns the current position of the specified trackbar. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of the trackbar. + */ +CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname); + +/** @brief Sets the trackbar position. + +The function sets the position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param pos New position. + */ +CV_EXPORTS_W void setTrackbarPos(const String& trackbarname, const String& winname, int pos); + +/** @brief Sets the trackbar maximum position. + +The function sets the maximum position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param maxval New maximum position. + */ +CV_EXPORTS_W void setTrackbarMax(const String& trackbarname, const String& winname, int maxval); + +/** @brief Sets the trackbar minimum position. + +The function sets the minimum position of the specified trackbar in the specified window. + +@note + +[__Qt Backend Only__] winname can be empty (or NULL) if the trackbar is attached to the control +panel. + +@param trackbarname Name of the trackbar. +@param winname Name of the window that is the parent of trackbar. +@param minval New minimum position. + */ +CV_EXPORTS_W void setTrackbarMin(const String& trackbarname, const String& winname, int minval); + +//! @addtogroup highgui_opengl OpenGL support +//! @{ + +/** @brief Displays OpenGL 2D texture in the specified window. + +@param winname Name of the window. +@param tex OpenGL 2D texture data. + */ +CV_EXPORTS void imshow(const String& winname, const ogl::Texture2D& tex); + +/** @brief Sets a callback function to be called to draw on top of displayed image. + +The function setOpenGlDrawCallback can be used to draw 3D data on the window. See the example of +callback function below: +@code + void on_opengl(void* param) + { + glLoadIdentity(); + + glTranslated(0.0, 0.0, -1.0); + + glRotatef( 55, 1, 0, 0 ); + glRotatef( 45, 0, 1, 0 ); + glRotatef( 0, 0, 0, 1 ); + + static const int coords[6][4][3] = { + { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } }, + { { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } }, + { { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } }, + { { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } }, + { { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } }, + { { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } } + }; + + for (int i = 0; i < 6; ++i) { + glColor3ub( i*20, 100+i*10, i*42 ); + glBegin(GL_QUADS); + for (int j = 0; j < 4; ++j) { + glVertex3d(0.2 * coords[i][j][0], 0.2 * coords[i][j][1], 0.2 * coords[i][j][2]); + } + glEnd(); + } + } +@endcode + +@param winname Name of the window. +@param onOpenGlDraw Pointer to the function to be called every frame. This function should be +prototyped as void Foo(void\*) . +@param userdata Pointer passed to the callback function.(__Optional__) + */ +CV_EXPORTS void setOpenGlDrawCallback(const String& winname, OpenGlDrawCallback onOpenGlDraw, void* userdata = 0); + +/** @brief Sets the specified window as current OpenGL context. + +@param winname Name of the window. + */ +CV_EXPORTS void setOpenGlContext(const String& winname); + +/** @brief Force window to redraw its context and call draw callback ( See cv::setOpenGlDrawCallback ). + +@param winname Name of the window. + */ +CV_EXPORTS void updateWindow(const String& winname); + +//! @} highgui_opengl + +//! @addtogroup highgui_qt +//! @{ + +/** @brief QtFont available only for Qt. See cv::fontQt + */ +struct QtFont +{ + const char* nameFont; //!< Name of the font + Scalar color; //!< Color of the font. Scalar(blue_component, green_component, red_component[, alpha_component]) + int font_face; //!< See cv::QtFontStyles + const int* ascii; //!< font data and metrics + const int* greek; + const int* cyrillic; + float hscale, vscale; + float shear; //!< slope coefficient: 0 - normal, >0 - italic + int thickness; //!< See cv::QtFontWeights + float dx; //!< horizontal interval between letters + int line_type; //!< PointSize +}; + +/** @brief Creates the font to draw a text on an image. + +The function fontQt creates a cv::QtFont object. This cv::QtFont is not compatible with putText . + +A basic usage of this function is the following: : +@code + QtFont font = fontQt("Times"); + addText( img1, "Hello World !", Point(50,50), font); +@endcode + +@param nameFont Name of the font. The name should match the name of a system font (such as +*Times*). If the font is not found, a default one is used. +@param pointSize Size of the font. If not specified, equal zero or negative, the point size of the +font is set to a system-dependent default value. Generally, this is 12 points. +@param color Color of the font in BGRA where A = 255 is fully transparent. Use the macro CV_RGB +for simplicity. +@param weight Font weight. Available operation flags are : cv::QtFontWeights You can also specify a positive integer for better control. +@param style Font style. Available operation flags are : cv::QtFontStyles +@param spacing Spacing between characters. It can be negative or positive. + */ +CV_EXPORTS QtFont fontQt(const String& nameFont, int pointSize = -1, + Scalar color = Scalar::all(0), int weight = QT_FONT_NORMAL, + int style = QT_STYLE_NORMAL, int spacing = 0); + +/** @brief Draws a text on the image. + +The function addText draws *text* on the image *img* using a specific font *font* (see example cv::fontQt +) + +@param img 8-bit 3-channel image where the text should be drawn. +@param text Text to write on an image. +@param org Point(x,y) where the text should start on an image. +@param font Font to use to draw a text. + */ +CV_EXPORTS void addText( const Mat& img, const String& text, Point org, const QtFont& font); + +/** @brief Draws a text on the image. + +@param img 8-bit 3-channel image where the text should be drawn. +@param text Text to write on an image. +@param org Point(x,y) where the text should start on an image. +@param nameFont Name of the font. The name should match the name of a system font (such as +*Times*). If the font is not found, a default one is used. +@param pointSize Size of the font. If not specified, equal zero or negative, the point size of the +font is set to a system-dependent default value. Generally, this is 12 points. +@param color Color of the font in BGRA where A = 255 is fully transparent. +@param weight Font weight. Available operation flags are : cv::QtFontWeights You can also specify a positive integer for better control. +@param style Font style. Available operation flags are : cv::QtFontStyles +@param spacing Spacing between characters. It can be negative or positive. + */ +CV_EXPORTS_W void addText(const Mat& img, const String& text, Point org, const String& nameFont, int pointSize = -1, Scalar color = Scalar::all(0), + int weight = QT_FONT_NORMAL, int style = QT_STYLE_NORMAL, int spacing = 0); + +/** @brief Displays a text on a window image as an overlay for a specified duration. + +The function displayOverlay displays useful information/tips on top of the window for a certain +amount of time *delayms*. The function does not modify the image, displayed in the window, that is, +after the specified delay the original content of the window is restored. + +@param winname Name of the window. +@param text Overlay text to write on a window image. +@param delayms The period (in milliseconds), during which the overlay text is displayed. If this +function is called before the previous overlay text timed out, the timer is restarted and the text +is updated. If this value is zero, the text never disappears. + */ +CV_EXPORTS_W void displayOverlay(const String& winname, const String& text, int delayms = 0); + +/** @brief Displays a text on the window statusbar during the specified period of time. + +The function displayStatusBar displays useful information/tips on top of the window for a certain +amount of time *delayms* . This information is displayed on the window statusbar (the window must be +created with the CV_GUI_EXPANDED flags). + +@param winname Name of the window. +@param text Text to write on the window statusbar. +@param delayms Duration (in milliseconds) to display the text. If this function is called before +the previous text timed out, the timer is restarted and the text is updated. If this value is +zero, the text never disappears. + */ +CV_EXPORTS_W void displayStatusBar(const String& winname, const String& text, int delayms = 0); + +/** @brief Saves parameters of the specified window. + +The function saveWindowParameters saves size, location, flags, trackbars value, zoom and panning +location of the window windowName. + +@param windowName Name of the window. + */ +CV_EXPORTS void saveWindowParameters(const String& windowName); + +/** @brief Loads parameters of the specified window. + +The function loadWindowParameters loads size, location, flags, trackbars value, zoom and panning +location of the window windowName. + +@param windowName Name of the window. + */ +CV_EXPORTS void loadWindowParameters(const String& windowName); + +CV_EXPORTS int startLoop(int (*pt2Func)(int argc, char *argv[]), int argc, char* argv[]); + +CV_EXPORTS void stopLoop(); + +/** @brief Attaches a button to the control panel. + +The function createButton attaches a button to the control panel. Each button is added to a +buttonbar to the right of the last button. A new buttonbar is created if nothing was attached to the +control panel before, or if the last element attached to the control panel was a trackbar or if the +QT_NEW_BUTTONBAR flag is added to the type. + +See below various examples of the cv::createButton function call: : +@code + createButton(NULL,callbackButton);//create a push button "button 0", that will call callbackButton. + createButton("button2",callbackButton,NULL,QT_CHECKBOX,0); + createButton("button3",callbackButton,&value); + createButton("button5",callbackButton1,NULL,QT_RADIOBOX); + createButton("button6",callbackButton2,NULL,QT_PUSH_BUTTON,1); + createButton("button6",callbackButton2,NULL,QT_PUSH_BUTTON|QT_NEW_BUTTONBAR);// create a push button in a new row +@endcode + +@param bar_name Name of the button. +@param on_change Pointer to the function to be called every time the button changes its state. +This function should be prototyped as void Foo(int state,\*void); . *state* is the current state +of the button. It could be -1 for a push button, 0 or 1 for a check/radio box button. +@param userdata Pointer passed to the callback function. +@param type Optional type of the button. Available types are: (cv::QtButtonTypes) +@param initial_button_state Default state of the button. Use for checkbox and radiobox. Its +value could be 0 or 1. (__Optional__) +*/ +CV_EXPORTS int createButton( const String& bar_name, ButtonCallback on_change, + void* userdata = 0, int type = QT_PUSH_BUTTON, + bool initial_button_state = false); + +//! @} highgui_qt + +//! @} highgui + +} // cv + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/highgui/highgui_c.h" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui.hpp new file mode 100644 index 0000000..160c9cf --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/highgui.hpp" diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui_c.h new file mode 100644 index 0000000..3541313 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/highgui/highgui_c.h @@ -0,0 +1,262 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_HIGHGUI_H +#define OPENCV_HIGHGUI_H + +#include "opencv2/core/core_c.h" +#include "opencv2/imgproc/imgproc_c.h" +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs/imgcodecs_c.h" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio/videoio_c.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup highgui_c + @{ + */ + +/****************************************************************************************\ +* Basic GUI functions * +\****************************************************************************************/ +//YV +//-----------New for Qt +/* For font */ +enum { CV_FONT_LIGHT = 25,//QFont::Light, + CV_FONT_NORMAL = 50,//QFont::Normal, + CV_FONT_DEMIBOLD = 63,//QFont::DemiBold, + CV_FONT_BOLD = 75,//QFont::Bold, + CV_FONT_BLACK = 87 //QFont::Black +}; + +enum { CV_STYLE_NORMAL = 0,//QFont::StyleNormal, + CV_STYLE_ITALIC = 1,//QFont::StyleItalic, + CV_STYLE_OBLIQUE = 2 //QFont::StyleOblique +}; +/* ---------*/ + +//for color cvScalar(blue_component, green_component, red_component[, alpha_component]) +//and alpha= 0 <-> 0xFF (not transparent <-> transparent) +CVAPI(CvFont) cvFontQt(const char* nameFont, int pointSize CV_DEFAULT(-1), CvScalar color CV_DEFAULT(cvScalarAll(0)), int weight CV_DEFAULT(CV_FONT_NORMAL), int style CV_DEFAULT(CV_STYLE_NORMAL), int spacing CV_DEFAULT(0)); + +CVAPI(void) cvAddText(const CvArr* img, const char* text, CvPoint org, CvFont *arg2); + +CVAPI(void) cvDisplayOverlay(const char* name, const char* text, int delayms CV_DEFAULT(0)); +CVAPI(void) cvDisplayStatusBar(const char* name, const char* text, int delayms CV_DEFAULT(0)); + +CVAPI(void) cvSaveWindowParameters(const char* name); +CVAPI(void) cvLoadWindowParameters(const char* name); +CVAPI(int) cvStartLoop(int (*pt2Func)(int argc, char *argv[]), int argc, char* argv[]); +CVAPI(void) cvStopLoop( void ); + +typedef void (CV_CDECL *CvButtonCallback)(int state, void* userdata); +enum {CV_PUSH_BUTTON = 0, CV_CHECKBOX = 1, CV_RADIOBOX = 2}; +CVAPI(int) cvCreateButton( const char* button_name CV_DEFAULT(NULL),CvButtonCallback on_change CV_DEFAULT(NULL), void* userdata CV_DEFAULT(NULL) , int button_type CV_DEFAULT(CV_PUSH_BUTTON), int initial_button_state CV_DEFAULT(0)); +//---------------------- + + +/* this function is used to set some external parameters in case of X Window */ +CVAPI(int) cvInitSystem( int argc, char** argv ); + +CVAPI(int) cvStartWindowThread( void ); + +// --------- YV --------- +enum +{ + //These 3 flags are used by cvSet/GetWindowProperty + CV_WND_PROP_FULLSCREEN = 0, //to change/get window's fullscreen property + CV_WND_PROP_AUTOSIZE = 1, //to change/get window's autosize property + CV_WND_PROP_ASPECTRATIO= 2, //to change/get window's aspectratio property + CV_WND_PROP_OPENGL = 3, //to change/get window's opengl support + CV_WND_PROP_VISIBLE = 4, + + //These 2 flags are used by cvNamedWindow and cvSet/GetWindowProperty + CV_WINDOW_NORMAL = 0x00000000, //the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size + CV_WINDOW_AUTOSIZE = 0x00000001, //the user cannot resize the window, the size is constrainted by the image displayed + CV_WINDOW_OPENGL = 0x00001000, //window with opengl support + + //Those flags are only for Qt + CV_GUI_EXPANDED = 0x00000000, //status bar and tool bar + CV_GUI_NORMAL = 0x00000010, //old fashious way + + //These 3 flags are used by cvNamedWindow and cvSet/GetWindowProperty + CV_WINDOW_FULLSCREEN = 1,//change the window to fullscreen + CV_WINDOW_FREERATIO = 0x00000100,//the image expends as much as it can (no ratio constraint) + CV_WINDOW_KEEPRATIO = 0x00000000//the ration image is respected. +}; + +/* create window */ +CVAPI(int) cvNamedWindow( const char* name, int flags CV_DEFAULT(CV_WINDOW_AUTOSIZE) ); + +/* Set and Get Property of the window */ +CVAPI(void) cvSetWindowProperty(const char* name, int prop_id, double prop_value); +CVAPI(double) cvGetWindowProperty(const char* name, int prop_id); + +#ifdef __cplusplus // FIXIT remove in OpenCV 4.0 +/* Get window image rectangle coordinates, width and height */ +CVAPI(cv::Rect)cvGetWindowImageRect(const char* name); +#endif + +/* display image within window (highgui windows remember their content) */ +CVAPI(void) cvShowImage( const char* name, const CvArr* image ); + +/* resize/move window */ +CVAPI(void) cvResizeWindow( const char* name, int width, int height ); +CVAPI(void) cvMoveWindow( const char* name, int x, int y ); + + +/* destroy window and all the trackers associated with it */ +CVAPI(void) cvDestroyWindow( const char* name ); + +CVAPI(void) cvDestroyAllWindows(void); + +/* get native window handle (HWND in case of Win32 and Widget in case of X Window) */ +CVAPI(void*) cvGetWindowHandle( const char* name ); + +/* get name of highgui window given its native handle */ +CVAPI(const char*) cvGetWindowName( void* window_handle ); + + +typedef void (CV_CDECL *CvTrackbarCallback)(int pos); + +/* create trackbar and display it on top of given window, set callback */ +CVAPI(int) cvCreateTrackbar( const char* trackbar_name, const char* window_name, + int* value, int count, CvTrackbarCallback on_change CV_DEFAULT(NULL)); + +typedef void (CV_CDECL *CvTrackbarCallback2)(int pos, void* userdata); + +CVAPI(int) cvCreateTrackbar2( const char* trackbar_name, const char* window_name, + int* value, int count, CvTrackbarCallback2 on_change, + void* userdata CV_DEFAULT(0)); + +/* retrieve or set trackbar position */ +CVAPI(int) cvGetTrackbarPos( const char* trackbar_name, const char* window_name ); +CVAPI(void) cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ); +CVAPI(void) cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval); +CVAPI(void) cvSetTrackbarMin(const char* trackbar_name, const char* window_name, int minval); + +enum +{ + CV_EVENT_MOUSEMOVE =0, + CV_EVENT_LBUTTONDOWN =1, + CV_EVENT_RBUTTONDOWN =2, + CV_EVENT_MBUTTONDOWN =3, + CV_EVENT_LBUTTONUP =4, + CV_EVENT_RBUTTONUP =5, + CV_EVENT_MBUTTONUP =6, + CV_EVENT_LBUTTONDBLCLK =7, + CV_EVENT_RBUTTONDBLCLK =8, + CV_EVENT_MBUTTONDBLCLK =9, + CV_EVENT_MOUSEWHEEL =10, + CV_EVENT_MOUSEHWHEEL =11 +}; + +enum +{ + CV_EVENT_FLAG_LBUTTON =1, + CV_EVENT_FLAG_RBUTTON =2, + CV_EVENT_FLAG_MBUTTON =4, + CV_EVENT_FLAG_CTRLKEY =8, + CV_EVENT_FLAG_SHIFTKEY =16, + CV_EVENT_FLAG_ALTKEY =32 +}; + + +#define CV_GET_WHEEL_DELTA(flags) ((short)((flags >> 16) & 0xffff)) // upper 16 bits + +typedef void (CV_CDECL *CvMouseCallback )(int event, int x, int y, int flags, void* param); + +/* assign callback for mouse events */ +CVAPI(void) cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, + void* param CV_DEFAULT(NULL)); + +/* wait for key event infinitely (delay<=0) or for "delay" milliseconds */ +CVAPI(int) cvWaitKey(int delay CV_DEFAULT(0)); + +// OpenGL support + +typedef void (CV_CDECL *CvOpenGlDrawCallback)(void* userdata); +CVAPI(void) cvSetOpenGlDrawCallback(const char* window_name, CvOpenGlDrawCallback callback, void* userdata CV_DEFAULT(NULL)); + +CVAPI(void) cvSetOpenGlContext(const char* window_name); +CVAPI(void) cvUpdateWindow(const char* window_name); + + +/****************************************************************************************\ + +* Obsolete functions/synonyms * +\****************************************************************************************/ + +#define cvAddSearchPath(path) +#define cvvInitSystem cvInitSystem +#define cvvNamedWindow cvNamedWindow +#define cvvShowImage cvShowImage +#define cvvResizeWindow cvResizeWindow +#define cvvDestroyWindow cvDestroyWindow +#define cvvCreateTrackbar cvCreateTrackbar +#define cvvAddSearchPath cvAddSearchPath +#define cvvWaitKey(name) cvWaitKey(0) +#define cvvWaitKeyEx(name,delay) cvWaitKey(delay) +#define HG_AUTOSIZE CV_WINDOW_AUTOSIZE +#define set_preprocess_func cvSetPreprocessFuncWin32 +#define set_postprocess_func cvSetPostprocessFuncWin32 + +#if defined _WIN32 + +CVAPI(void) cvSetPreprocessFuncWin32_(const void* callback); +CVAPI(void) cvSetPostprocessFuncWin32_(const void* callback); +#define cvSetPreprocessFuncWin32(callback) cvSetPreprocessFuncWin32_((const void*)(callback)) +#define cvSetPostprocessFuncWin32(callback) cvSetPostprocessFuncWin32_((const void*)(callback)) + +#endif + +/** @} highgui_c */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs.hpp new file mode 100644 index 0000000..7f6b24f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs.hpp @@ -0,0 +1,276 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGCODECS_HPP +#define OPENCV_IMGCODECS_HPP + +#include "opencv2/core.hpp" + +/** + @defgroup imgcodecs Image file reading and writing + @{ + @defgroup imgcodecs_c C API + @defgroup imgcodecs_flags Flags used for image file reading and writing + @defgroup imgcodecs_ios iOS glue + @} +*/ + +//////////////////////////////// image codec //////////////////////////////// +namespace cv +{ + +//! @addtogroup imgcodecs +//! @{ + +//! @addtogroup imgcodecs_flags +//! @{ + +//! Imread flags +enum ImreadModes { + IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation. + IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion). + IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image. + IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit. + IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format. + IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image. + IMREAD_REDUCED_GRAYSCALE_2 = 16, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2. + IMREAD_REDUCED_COLOR_2 = 17, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2. + IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4. + IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4. + IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8. + IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. + IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag. + }; + +//! Imwrite flags +enum ImwriteFlags { + IMWRITE_JPEG_QUALITY = 1, //!< For JPEG, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. + IMWRITE_JPEG_PROGRESSIVE = 2, //!< Enable JPEG features, 0 or 1, default is False. + IMWRITE_JPEG_OPTIMIZE = 3, //!< Enable JPEG features, 0 or 1, default is False. + IMWRITE_JPEG_RST_INTERVAL = 4, //!< JPEG restart interval, 0 - 65535, default is 0 - no restart. + IMWRITE_JPEG_LUMA_QUALITY = 5, //!< Separate luma quality level, 0 - 100, default is 0 - don't use. + IMWRITE_JPEG_CHROMA_QUALITY = 6, //!< Separate chroma quality level, 0 - 100, default is 0 - don't use. + IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting). + IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE. + IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0. + IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. + IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default) + IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. + IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format + IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values. + IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI. + IMWRITE_TIFF_YDPI = 258, //!< For TIFF, use to specify the Y direction DPI. + IMWRITE_TIFF_COMPRESSION = 259 //!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default. + }; + +enum ImwriteEXRTypeFlags { + /*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */ + IMWRITE_EXR_TYPE_HALF = 1, //!< store as HALF (FP16) + IMWRITE_EXR_TYPE_FLOAT = 2 //!< store as FP32 (default) + }; + +//! Imwrite PNG specific flags used to tune the compression algorithm. +/** These flags will be modify the way of PNG image compression and will be passed to the underlying zlib processing stage. + +- The effect of IMWRITE_PNG_STRATEGY_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between IMWRITE_PNG_STRATEGY_DEFAULT and IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY. +- IMWRITE_PNG_STRATEGY_RLE is designed to be almost as fast as IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY, but give better compression for PNG image data. +- The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. +- IMWRITE_PNG_STRATEGY_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. +*/ +enum ImwritePNGFlags { + IMWRITE_PNG_STRATEGY_DEFAULT = 0, //!< Use this value for normal data. + IMWRITE_PNG_STRATEGY_FILTERED = 1, //!< Use this value for data produced by a filter (or predictor).Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. + IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY = 2, //!< Use this value to force Huffman encoding only (no string match). + IMWRITE_PNG_STRATEGY_RLE = 3, //!< Use this value to limit match distances to one (run-length encoding). + IMWRITE_PNG_STRATEGY_FIXED = 4 //!< Using this value prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. + }; + +//! Imwrite PAM specific tupletype flags used to define the 'TUPETYPE' field of a PAM file. +enum ImwritePAMFlags { + IMWRITE_PAM_FORMAT_NULL = 0, + IMWRITE_PAM_FORMAT_BLACKANDWHITE = 1, + IMWRITE_PAM_FORMAT_GRAYSCALE = 2, + IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA = 3, + IMWRITE_PAM_FORMAT_RGB = 4, + IMWRITE_PAM_FORMAT_RGB_ALPHA = 5, + }; + +//! @} imgcodecs_flags + +/** @brief Loads an image from a file. + +@anchor imread + +The function imread loads an image from the specified file and returns it. If the image cannot be +read (because of missing file, improper permissions, unsupported or invalid format), the function +returns an empty matrix ( Mat::data==NULL ). + +Currently, the following file formats are supported: + +- Windows bitmaps - \*.bmp, \*.dib (always supported) +- JPEG files - \*.jpeg, \*.jpg, \*.jpe (see the *Note* section) +- JPEG 2000 files - \*.jp2 (see the *Note* section) +- Portable Network Graphics - \*.png (see the *Note* section) +- WebP - \*.webp (see the *Note* section) +- Portable image format - \*.pbm, \*.pgm, \*.ppm \*.pxm, \*.pnm (always supported) +- Sun rasters - \*.sr, \*.ras (always supported) +- TIFF files - \*.tiff, \*.tif (see the *Note* section) +- OpenEXR Image files - \*.exr (see the *Note* section) +- Radiance HDR - \*.hdr, \*.pic (always supported) +- Raster and Vector geospatial data supported by GDAL (see the *Note* section) + +@note +- The function determines the type of an image by the content, not by the file extension. +- In the case of color images, the decoded images will have the channels stored in **B G R** order. +- When using IMREAD_GRAYSCALE, the codec's internal grayscale conversion will be used, if available. + Results may differ to the output of cvtColor() +- On Microsoft Windows\* OS and MacOSX\*, the codecs shipped with an OpenCV image (libjpeg, + libpng, libtiff, and libjasper) are used by default. So, OpenCV can always read JPEGs, PNGs, + and TIFFs. On MacOSX, there is also an option to use native MacOSX image readers. But beware + that currently these native image loaders give images with different pixel values because of + the color management embedded into MacOSX. +- On Linux\*, BSD flavors and other Unix-like open-source operating systems, OpenCV looks for + codecs supplied with an OS image. Install the relevant packages (do not forget the development + files, for example, "libjpeg-dev", in Debian\* and Ubuntu\*) to get the codec support or turn + on the OPENCV_BUILD_3RDPARTY_LIBS flag in CMake. +- In the case you set *WITH_GDAL* flag to true in CMake and @ref IMREAD_LOAD_GDAL to load the image, + then the [GDAL](http://www.gdal.org) driver will be used in order to decode the image, supporting + the following formats: [Raster](http://www.gdal.org/formats_list.html), + [Vector](http://www.gdal.org/ogr_formats.html). +- If EXIF information is embedded in the image file, the EXIF orientation will be taken into account + and thus the image will be rotated accordingly except if the flags @ref IMREAD_IGNORE_ORIENTATION + or @ref IMREAD_UNCHANGED are passed. +- By default number of pixels must be less than 2^30. Limit can be set using system + variable OPENCV_IO_MAX_IMAGE_PIXELS + +@param filename Name of file to be loaded. +@param flags Flag that can take values of cv::ImreadModes +*/ +CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); + +/** @brief Loads a multi-page image from a file. + +The function imreadmulti loads a multi-page image from the specified file into a vector of Mat objects. +@param filename Name of file to be loaded. +@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR. +@param mats A vector of Mat objects holding each page, if more than one. +@sa cv::imread +*/ +CV_EXPORTS_W bool imreadmulti(const String& filename, CV_OUT std::vector& mats, int flags = IMREAD_ANYCOLOR); + +/** @brief Saves an image to a specified file. + +The function imwrite saves the image to the specified file. The image format is chosen based on the +filename extension (see cv::imread for the list of extensions). In general, only 8-bit +single-channel or 3-channel (with 'BGR' channel order) images +can be saved using this function, with these exceptions: + +- 16-bit unsigned (CV_16U) images can be saved in the case of PNG, JPEG 2000, and TIFF formats +- 32-bit float (CV_32F) images can be saved in TIFF, OpenEXR, and Radiance HDR formats; 3-channel +(CV_32FC3) TIFF images will be saved using the LogLuv high dynamic range encoding (4 bytes per pixel) +- PNG images with an alpha channel can be saved using this function. To do this, create +8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels +should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535 (see the code sample below). +- Multiple images (vector of Mat) can be saved in TIFF format (see the code sample below). + +If the format, depth or channel order is different, use +Mat::convertTo and cv::cvtColor to convert it before saving. Or, use the universal FileStorage I/O +functions to save the image to XML or YAML format. + +The sample below shows how to create a BGRA image, how to set custom compression parameters and save it to a PNG file. +It also demonstrates how to save multiple images in a TIFF file: +@include snippets/imgcodecs_imwrite.cpp +@param filename Name of the file. +@param img (Mat or vector of Mat) Image or Images to be saved. +@param params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags +*/ +CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, + const std::vector& params = std::vector()); + +/// @overload multi-image overload for bindings +CV_WRAP static inline +bool imwritemulti(const String& filename, InputArrayOfArrays img, + const std::vector& params = std::vector()) +{ + return imwrite(filename, img, params); +} + +/** @brief Reads an image from a buffer in memory. + +The function imdecode reads an image from the specified buffer in the memory. If the buffer is too short or +contains invalid data, the function returns an empty matrix ( Mat::data==NULL ). + +See cv::imread for the list of supported formats and flags description. + +@note In the case of color images, the decoded images will have the channels stored in **B G R** order. +@param buf Input array or vector of bytes. +@param flags The same flags as in cv::imread, see cv::ImreadModes. +*/ +CV_EXPORTS_W Mat imdecode( InputArray buf, int flags ); + +/** @overload +@param buf +@param flags +@param dst The optional output placeholder for the decoded matrix. It can save the image +reallocations when the function is called repeatedly for images of the same size. +*/ +CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst); + +/** @brief Encodes an image into a memory buffer. + +The function imencode compresses the image and stores it in the memory buffer that is resized to fit the +result. See cv::imwrite for the list of supported formats and flags description. + +@param ext File extension that defines the output format. +@param img Image to be written. +@param buf Output buffer resized to fit the compressed image. +@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags. +*/ +CV_EXPORTS_W bool imencode( const String& ext, InputArray img, + CV_OUT std::vector& buf, + const std::vector& params = std::vector()); + +//! @} imgcodecs + +} // cv + +#endif //OPENCV_IMGCODECS_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs.hpp new file mode 100644 index 0000000..a3cd232 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/imgcodecs.hpp" diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs_c.h new file mode 100644 index 0000000..c36dac3 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/imgcodecs_c.h @@ -0,0 +1,149 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGCODECS_H +#define OPENCV_IMGCODECS_H + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup imgcodecs_c + @{ + */ + +enum +{ +/* 8bit, color or not */ + CV_LOAD_IMAGE_UNCHANGED =-1, +/* 8bit, gray */ + CV_LOAD_IMAGE_GRAYSCALE =0, +/* ?, color */ + CV_LOAD_IMAGE_COLOR =1, +/* any depth, ? */ + CV_LOAD_IMAGE_ANYDEPTH =2, +/* ?, any color */ + CV_LOAD_IMAGE_ANYCOLOR =4, +/* ?, no rotate */ + CV_LOAD_IMAGE_IGNORE_ORIENTATION =128 +}; + +/* load image from file + iscolor can be a combination of above flags where CV_LOAD_IMAGE_UNCHANGED + overrides the other flags + using CV_LOAD_IMAGE_ANYCOLOR alone is equivalent to CV_LOAD_IMAGE_UNCHANGED + unless CV_LOAD_IMAGE_ANYDEPTH is specified images are converted to 8bit +*/ +CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvLoadImageM( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +enum +{ + CV_IMWRITE_JPEG_QUALITY =1, + CV_IMWRITE_JPEG_PROGRESSIVE =2, + CV_IMWRITE_JPEG_OPTIMIZE =3, + CV_IMWRITE_JPEG_RST_INTERVAL =4, + CV_IMWRITE_JPEG_LUMA_QUALITY =5, + CV_IMWRITE_JPEG_CHROMA_QUALITY =6, + CV_IMWRITE_PNG_COMPRESSION =16, + CV_IMWRITE_PNG_STRATEGY =17, + CV_IMWRITE_PNG_BILEVEL =18, + CV_IMWRITE_PNG_STRATEGY_DEFAULT =0, + CV_IMWRITE_PNG_STRATEGY_FILTERED =1, + CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, + CV_IMWRITE_PNG_STRATEGY_RLE =3, + CV_IMWRITE_PNG_STRATEGY_FIXED =4, + CV_IMWRITE_PXM_BINARY =32, + CV_IMWRITE_EXR_TYPE = 48, + CV_IMWRITE_WEBP_QUALITY =64, + CV_IMWRITE_PAM_TUPLETYPE = 128, + CV_IMWRITE_PAM_FORMAT_NULL = 0, + CV_IMWRITE_PAM_FORMAT_BLACKANDWHITE = 1, + CV_IMWRITE_PAM_FORMAT_GRAYSCALE = 2, + CV_IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA = 3, + CV_IMWRITE_PAM_FORMAT_RGB = 4, + CV_IMWRITE_PAM_FORMAT_RGB_ALPHA = 5, +}; + + + +/* save image to file */ +CVAPI(int) cvSaveImage( const char* filename, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +/* decode image stored in the buffer */ +CVAPI(IplImage*) cvDecodeImage( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvDecodeImageM( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +/* encode image and store the result as a byte vector (single-row 8uC1 matrix) */ +CVAPI(CvMat*) cvEncodeImage( const char* ext, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +enum +{ + CV_CVTIMG_FLIP =1, + CV_CVTIMG_SWAP_RB =2 +}; + +/* utility function: convert one image to another with optional vertical flip */ +CVAPI(void) cvConvertImage( const CvArr* src, CvArr* dst, int flags CV_DEFAULT(0)); + +CVAPI(int) cvHaveImageReader(const char* filename); +CVAPI(int) cvHaveImageWriter(const char* filename); + + +/****************************************************************************************\ +* Obsolete functions/synonyms * +\****************************************************************************************/ + +#define cvvLoadImage(name) cvLoadImage((name),1) +#define cvvSaveImage cvSaveImage +#define cvvConvertImage cvConvertImage + +/** @} imgcodecs_c */ + +#ifdef __cplusplus +} +#endif + +#endif // OPENCV_IMGCODECS_H diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/ios.h b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/ios.h new file mode 100644 index 0000000..a90c6d3 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgcodecs/ios.h @@ -0,0 +1,57 @@ + +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#import +#import +#import +#import +#include "opencv2/core/core.hpp" + +//! @addtogroup imgcodecs_ios +//! @{ + +CV_EXPORTS UIImage* MatToUIImage(const cv::Mat& image); +CV_EXPORTS void UIImageToMat(const UIImage* image, + cv::Mat& m, bool alphaExist = false); + +//! @} diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc.hpp new file mode 100644 index 0000000..2ded15d --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc.hpp @@ -0,0 +1,4950 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_HPP +#define OPENCV_IMGPROC_HPP + +#include "opencv2/core.hpp" + +/** + @defgroup imgproc Image Processing + +This module includes image-processing functions. + + @{ + @defgroup imgproc_filter Image Filtering + +Functions and classes described in this section are used to perform various linear or non-linear +filtering operations on 2D images (represented as Mat's). It means that for each pixel location +\f$(x,y)\f$ in the source image (normally, rectangular), its neighborhood is considered and used to +compute the response. In case of a linear filter, it is a weighted sum of pixel values. In case of +morphological operations, it is the minimum or maximum values, and so on. The computed response is +stored in the destination image at the same location \f$(x,y)\f$. It means that the output image +will be of the same size as the input image. Normally, the functions support multi-channel arrays, +in which case every channel is processed independently. Therefore, the output image will also have +the same number of channels as the input one. + +Another common feature of the functions and classes described in this section is that, unlike +simple arithmetic functions, they need to extrapolate values of some non-existing pixels. For +example, if you want to smooth an image using a Gaussian \f$3 \times 3\f$ filter, then, when +processing the left-most pixels in each row, you need pixels to the left of them, that is, outside +of the image. You can let these pixels be the same as the left-most image pixels ("replicated +border" extrapolation method), or assume that all the non-existing pixels are zeros ("constant +border" extrapolation method), and so on. OpenCV enables you to specify the extrapolation method. +For details, see #BorderTypes + +@anchor filter_depths +### Depth combinations +Input depth (src.depth()) | Output depth (ddepth) +--------------------------|---------------------- +CV_8U | -1/CV_16S/CV_32F/CV_64F +CV_16U/CV_16S | -1/CV_32F/CV_64F +CV_32F | -1/CV_32F/CV_64F +CV_64F | -1/CV_64F + +@note when ddepth=-1, the output image will have the same depth as the source. + + @defgroup imgproc_transform Geometric Image Transformations + +The functions in this section perform various geometrical transformations of 2D images. They do not +change the image content but deform the pixel grid and map this deformed grid to the destination +image. In fact, to avoid sampling artifacts, the mapping is done in the reverse order, from +destination to the source. That is, for each pixel \f$(x, y)\f$ of the destination image, the +functions compute coordinates of the corresponding "donor" pixel in the source image and copy the +pixel value: + +\f[\texttt{dst} (x,y)= \texttt{src} (f_x(x,y), f_y(x,y))\f] + +In case when you specify the forward mapping \f$\left: \texttt{src} \rightarrow +\texttt{dst}\f$, the OpenCV functions first compute the corresponding inverse mapping +\f$\left: \texttt{dst} \rightarrow \texttt{src}\f$ and then use the above formula. + +The actual implementations of the geometrical transformations, from the most generic remap and to +the simplest and the fastest resize, need to solve two main problems with the above formula: + +- Extrapolation of non-existing pixels. Similarly to the filtering functions described in the +previous section, for some \f$(x,y)\f$, either one of \f$f_x(x,y)\f$, or \f$f_y(x,y)\f$, or both +of them may fall outside of the image. In this case, an extrapolation method needs to be used. +OpenCV provides the same selection of extrapolation methods as in the filtering functions. In +addition, it provides the method #BORDER_TRANSPARENT. This means that the corresponding pixels in +the destination image will not be modified at all. + +- Interpolation of pixel values. Usually \f$f_x(x,y)\f$ and \f$f_y(x,y)\f$ are floating-point +numbers. This means that \f$\left\f$ can be either an affine or perspective +transformation, or radial lens distortion correction, and so on. So, a pixel value at fractional +coordinates needs to be retrieved. In the simplest case, the coordinates can be just rounded to the +nearest integer coordinates and the corresponding pixel can be used. This is called a +nearest-neighbor interpolation. However, a better result can be achieved by using more +sophisticated [interpolation methods](http://en.wikipedia.org/wiki/Multivariate_interpolation) , +where a polynomial function is fit into some neighborhood of the computed pixel \f$(f_x(x,y), +f_y(x,y))\f$, and then the value of the polynomial at \f$(f_x(x,y), f_y(x,y))\f$ is taken as the +interpolated pixel value. In OpenCV, you can choose between several interpolation methods. See +resize for details. + +@note The geometrical transformations do not work with `CV_8S` or `CV_32S` images. + + @defgroup imgproc_misc Miscellaneous Image Transformations + @defgroup imgproc_draw Drawing Functions + +Drawing functions work with matrices/images of arbitrary depth. The boundaries of the shapes can be +rendered with antialiasing (implemented only for 8-bit images for now). All the functions include +the parameter color that uses an RGB value (that may be constructed with the Scalar constructor ) +for color images and brightness for grayscale images. For color images, the channel ordering is +normally *Blue, Green, Red*. This is what imshow, imread, and imwrite expect. So, if you form a +color using the Scalar constructor, it should look like: + +\f[\texttt{Scalar} (blue \_ component, green \_ component, red \_ component[, alpha \_ component])\f] + +If you are using your own image rendering and I/O functions, you can use any channel ordering. The +drawing functions process each channel independently and do not depend on the channel order or even +on the used color space. The whole image can be converted from BGR to RGB or to a different color +space using cvtColor . + +If a drawn figure is partially or completely outside the image, the drawing functions clip it. Also, +many drawing functions can handle pixel coordinates specified with sub-pixel accuracy. This means +that the coordinates can be passed as fixed-point numbers encoded as integers. The number of +fractional bits is specified by the shift parameter and the real point coordinates are calculated as +\f$\texttt{Point}(x,y)\rightarrow\texttt{Point2f}(x*2^{-shift},y*2^{-shift})\f$ . This feature is +especially effective when rendering antialiased shapes. + +@note The functions do not support alpha-transparency when the target image is 4-channel. In this +case, the color[3] is simply copied to the repainted pixels. Thus, if you want to paint +semi-transparent shapes, you can paint them in a separate buffer and then blend it with the main +image. + + @defgroup imgproc_color_conversions Color Space Conversions + @defgroup imgproc_colormap ColorMaps in OpenCV + +The human perception isn't built for observing fine changes in grayscale images. Human eyes are more +sensitive to observing changes between colors, so you often need to recolor your grayscale images to +get a clue about them. OpenCV now comes with various colormaps to enhance the visualization in your +computer vision application. + +In OpenCV you only need applyColorMap to apply a colormap on a given image. The following sample +code reads the path to an image from command line, applies a Jet colormap on it and shows the +result: + +@include snippets/imgproc_applyColorMap.cpp + +@see #ColormapTypes + + @defgroup imgproc_subdiv2d Planar Subdivision + +The Subdiv2D class described in this section is used to perform various planar subdivision on +a set of 2D points (represented as vector of Point2f). OpenCV subdivides a plane into triangles +using the Delaunay's algorithm, which corresponds to the dual graph of the Voronoi diagram. +In the figure below, the Delaunay's triangulation is marked with black lines and the Voronoi +diagram with red lines. + +![Delaunay triangulation (black) and Voronoi (red)](pics/delaunay_voronoi.png) + +The subdivisions can be used for the 3D piece-wise transformation of a plane, morphing, fast +location of points on the plane, building special graphs (such as NNG,RNG), and so forth. + + @defgroup imgproc_hist Histograms + @defgroup imgproc_shape Structural Analysis and Shape Descriptors + @defgroup imgproc_motion Motion Analysis and Object Tracking + @defgroup imgproc_feature Feature Detection + @defgroup imgproc_object Object Detection + @defgroup imgproc_c C API + @defgroup imgproc_hal Hardware Acceleration Layer + @{ + @defgroup imgproc_hal_functions Functions + @defgroup imgproc_hal_interface Interface + @} + @} +*/ + +namespace cv +{ + +/** @addtogroup imgproc +@{ +*/ + +//! @addtogroup imgproc_filter +//! @{ + +//! type of morphological operation +enum MorphTypes{ + MORPH_ERODE = 0, //!< see #erode + MORPH_DILATE = 1, //!< see #dilate + MORPH_OPEN = 2, //!< an opening operation + //!< \f[\texttt{dst} = \mathrm{open} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \mathrm{erode} ( \texttt{src} , \texttt{element} ))\f] + MORPH_CLOSE = 3, //!< a closing operation + //!< \f[\texttt{dst} = \mathrm{close} ( \texttt{src} , \texttt{element} )= \mathrm{erode} ( \mathrm{dilate} ( \texttt{src} , \texttt{element} ))\f] + MORPH_GRADIENT = 4, //!< a morphological gradient + //!< \f[\texttt{dst} = \mathrm{morph\_grad} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \texttt{src} , \texttt{element} )- \mathrm{erode} ( \texttt{src} , \texttt{element} )\f] + MORPH_TOPHAT = 5, //!< "top hat" + //!< \f[\texttt{dst} = \mathrm{tophat} ( \texttt{src} , \texttt{element} )= \texttt{src} - \mathrm{open} ( \texttt{src} , \texttt{element} )\f] + MORPH_BLACKHAT = 6, //!< "black hat" + //!< \f[\texttt{dst} = \mathrm{blackhat} ( \texttt{src} , \texttt{element} )= \mathrm{close} ( \texttt{src} , \texttt{element} )- \texttt{src}\f] + MORPH_HITMISS = 7 //!< "hit or miss" + //!< .- Only supported for CV_8UC1 binary images. A tutorial can be found in the documentation +}; + +//! shape of the structuring element +enum MorphShapes { + MORPH_RECT = 0, //!< a rectangular structuring element: \f[E_{ij}=1\f] + MORPH_CROSS = 1, //!< a cross-shaped structuring element: + //!< \f[E_{ij} = \begin{cases} 1 & \texttt{if } {i=\texttt{anchor.y } {or } {j=\texttt{anchor.x}}} \\0 & \texttt{otherwise} \end{cases}\f] + MORPH_ELLIPSE = 2 //!< an elliptic structuring element, that is, a filled ellipse inscribed + //!< into the rectangle Rect(0, 0, esize.width, 0.esize.height) +}; + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +//! interpolation algorithm +enum InterpolationFlags{ + /** nearest neighbor interpolation */ + INTER_NEAREST = 0, + /** bilinear interpolation */ + INTER_LINEAR = 1, + /** bicubic interpolation */ + INTER_CUBIC = 2, + /** resampling using pixel area relation. It may be a preferred method for image decimation, as + it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST + method. */ + INTER_AREA = 3, + /** Lanczos interpolation over 8x8 neighborhood */ + INTER_LANCZOS4 = 4, + /** Bit exact bilinear interpolation */ + INTER_LINEAR_EXACT = 5, + /** Bit exact nearest neighbor interpolation. This will produce same results as + the nearest neighbor method in PIL, scikit-image or Matlab. */ + INTER_NEAREST_EXACT = 6, + /** mask for interpolation codes */ + INTER_MAX = 7, + /** flag, fills all of the destination image pixels. If some of them correspond to outliers in the + source image, they are set to zero */ + WARP_FILL_OUTLIERS = 8, + /** flag, inverse transformation + + For example, #linearPolar or #logPolar transforms: + - flag is __not__ set: \f$dst( \rho , \phi ) = src(x,y)\f$ + - flag is set: \f$dst(x,y) = src( \rho , \phi )\f$ + */ + WARP_INVERSE_MAP = 16 +}; + +/** \brief Specify the polar mapping mode +@sa warpPolar +*/ +enum WarpPolarMode +{ + WARP_POLAR_LINEAR = 0, ///< Remaps an image to/from polar space. + WARP_POLAR_LOG = 256 ///< Remaps an image to/from semilog-polar space. +}; + +enum InterpolationMasks { + INTER_BITS = 5, + INTER_BITS2 = INTER_BITS * 2, + INTER_TAB_SIZE = 1 << INTER_BITS, + INTER_TAB_SIZE2 = INTER_TAB_SIZE * INTER_TAB_SIZE + }; + +//! @} imgproc_transform + +//! @addtogroup imgproc_misc +//! @{ + +//! Distance types for Distance Transform and M-estimators +//! @see distanceTransform, fitLine +enum DistanceTypes { + DIST_USER = -1, //!< User defined distance + DIST_L1 = 1, //!< distance = |x1-x2| + |y1-y2| + DIST_L2 = 2, //!< the simple euclidean distance + DIST_C = 3, //!< distance = max(|x1-x2|,|y1-y2|) + DIST_L12 = 4, //!< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1)) + DIST_FAIR = 5, //!< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998 + DIST_WELSCH = 6, //!< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846 + DIST_HUBER = 7 //!< distance = |x| \texttt{thresh}\)}{0}{otherwise}\f] + THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f] + THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] + THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] + THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] + THRESH_MASK = 7, + THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value + THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value +}; + +//! adaptive threshold algorithm +//! @see adaptiveThreshold +enum AdaptiveThresholdTypes { + /** the threshold value \f$T(x,y)\f$ is a mean of the \f$\texttt{blockSize} \times + \texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$ minus C */ + ADAPTIVE_THRESH_MEAN_C = 0, + /** the threshold value \f$T(x, y)\f$ is a weighted sum (cross-correlation with a Gaussian + window) of the \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$ + minus C . The default sigma (standard deviation) is used for the specified blockSize . See + #getGaussianKernel*/ + ADAPTIVE_THRESH_GAUSSIAN_C = 1 +}; + +//! cv::undistort mode +enum UndistortTypes { + PROJ_SPHERICAL_ORTHO = 0, + PROJ_SPHERICAL_EQRECT = 1 + }; + +//! class of the pixel in GrabCut algorithm +enum GrabCutClasses { + GC_BGD = 0, //!< an obvious background pixels + GC_FGD = 1, //!< an obvious foreground (object) pixel + GC_PR_BGD = 2, //!< a possible background pixel + GC_PR_FGD = 3 //!< a possible foreground pixel +}; + +//! GrabCut algorithm flags +enum GrabCutModes { + /** The function initializes the state and the mask using the provided rectangle. After that it + runs iterCount iterations of the algorithm. */ + GC_INIT_WITH_RECT = 0, + /** The function initializes the state using the provided mask. Note that GC_INIT_WITH_RECT + and GC_INIT_WITH_MASK can be combined. Then, all the pixels outside of the ROI are + automatically initialized with GC_BGD .*/ + GC_INIT_WITH_MASK = 1, + /** The value means that the algorithm should just resume. */ + GC_EVAL = 2, + /** The value means that the algorithm should just run the grabCut algorithm (a single iteration) with the fixed model */ + GC_EVAL_FREEZE_MODEL = 3 +}; + +//! distanceTransform algorithm flags +enum DistanceTransformLabelTypes { + /** each connected component of zeros in src (as well as all the non-zero pixels closest to the + connected component) will be assigned the same label */ + DIST_LABEL_CCOMP = 0, + /** each zero pixel (and all the non-zero pixels closest to it) gets its own label. */ + DIST_LABEL_PIXEL = 1 +}; + +//! floodfill algorithm flags +enum FloodFillFlags { + /** If set, the difference between the current pixel and seed pixel is considered. Otherwise, + the difference between neighbor pixels is considered (that is, the range is floating). */ + FLOODFILL_FIXED_RANGE = 1 << 16, + /** If set, the function does not change the image ( newVal is ignored), and only fills the + mask with the value specified in bits 8-16 of flags as described above. This option only make + sense in function variants that have the mask parameter. */ + FLOODFILL_MASK_ONLY = 1 << 17 +}; + +//! @} imgproc_misc + +//! @addtogroup imgproc_shape +//! @{ + +//! connected components statistics +enum ConnectedComponentsTypes { + CC_STAT_LEFT = 0, //!< The leftmost (x) coordinate which is the inclusive start of the bounding + //!< box in the horizontal direction. + CC_STAT_TOP = 1, //!< The topmost (y) coordinate which is the inclusive start of the bounding + //!< box in the vertical direction. + CC_STAT_WIDTH = 2, //!< The horizontal size of the bounding box + CC_STAT_HEIGHT = 3, //!< The vertical size of the bounding box + CC_STAT_AREA = 4, //!< The total area (in pixels) of the connected component +#ifndef CV_DOXYGEN + CC_STAT_MAX = 5 //!< Max enumeration value. Used internally only for memory allocation +#endif +}; + +//! connected components algorithm +enum ConnectedComponentsAlgorithmsTypes { + CCL_DEFAULT = -1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF. + CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for SAUF. + CCL_GRANA = 1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF. + CCL_BOLELLI = 2, //!< Spaghetti @cite Bolelli2019 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. + CCL_SAUF = 3, //!< Same as CCL_WU. It is preferable to use the flag with the name of the algorithm (CCL_SAUF) rather than the one with the name of the first author (CCL_WU). + CCL_BBDT = 4, //!< Same as CCL_GRANA. It is preferable to use the flag with the name of the algorithm (CCL_BBDT) rather than the one with the name of the first author (CCL_GRANA). + CCL_SPAGHETTI = 5, //!< Same as CCL_BOLELLI. It is preferable to use the flag with the name of the algorithm (CCL_SPAGHETTI) rather than the one with the name of the first author (CCL_BOLELLI). +}; + +//! mode of the contour retrieval algorithm +enum RetrievalModes { + /** retrieves only the extreme outer contours. It sets `hierarchy[i][2]=hierarchy[i][3]=-1` for + all the contours. */ + RETR_EXTERNAL = 0, + /** retrieves all of the contours without establishing any hierarchical relationships. */ + RETR_LIST = 1, + /** retrieves all of the contours and organizes them into a two-level hierarchy. At the top + level, there are external boundaries of the components. At the second level, there are + boundaries of the holes. If there is another contour inside a hole of a connected component, it + is still put at the top level. */ + RETR_CCOMP = 2, + /** retrieves all of the contours and reconstructs a full hierarchy of nested contours.*/ + RETR_TREE = 3, + RETR_FLOODFILL = 4 //!< +}; + +//! the contour approximation algorithm +enum ContourApproximationModes { + /** stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and + (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is, + max(abs(x1-x2),abs(y2-y1))==1. */ + CHAIN_APPROX_NONE = 1, + /** compresses horizontal, vertical, and diagonal segments and leaves only their end points. + For example, an up-right rectangular contour is encoded with 4 points. */ + CHAIN_APPROX_SIMPLE = 2, + /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */ + CHAIN_APPROX_TC89_L1 = 3, + /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */ + CHAIN_APPROX_TC89_KCOS = 4 +}; + +/** @brief Shape matching methods + +\f$A\f$ denotes object1,\f$B\f$ denotes object2 + +\f$\begin{array}{l} m^A_i = \mathrm{sign} (h^A_i) \cdot \log{h^A_i} \\ m^B_i = \mathrm{sign} (h^B_i) \cdot \log{h^B_i} \end{array}\f$ + +and \f$h^A_i, h^B_i\f$ are the Hu moments of \f$A\f$ and \f$B\f$ , respectively. +*/ +enum ShapeMatchModes { + CONTOURS_MATCH_I1 =1, //!< \f[I_1(A,B) = \sum _{i=1...7} \left | \frac{1}{m^A_i} - \frac{1}{m^B_i} \right |\f] + CONTOURS_MATCH_I2 =2, //!< \f[I_2(A,B) = \sum _{i=1...7} \left | m^A_i - m^B_i \right |\f] + CONTOURS_MATCH_I3 =3 //!< \f[I_3(A,B) = \max _{i=1...7} \frac{ \left| m^A_i - m^B_i \right| }{ \left| m^A_i \right| }\f] +}; + +//! @} imgproc_shape + +//! @addtogroup imgproc_feature +//! @{ + +//! Variants of a Hough transform +enum HoughModes { + + /** classical or standard Hough transform. Every line is represented by two floating-point + numbers \f$(\rho, \theta)\f$ , where \f$\rho\f$ is a distance between (0,0) point and the line, + and \f$\theta\f$ is the angle between x-axis and the normal to the line. Thus, the matrix must + be (the created sequence will be) of CV_32FC2 type */ + HOUGH_STANDARD = 0, + /** probabilistic Hough transform (more efficient in case if the picture contains a few long + linear segments). It returns line segments rather than the whole line. Each segment is + represented by starting and ending points, and the matrix must be (the created sequence will + be) of the CV_32SC4 type. */ + HOUGH_PROBABILISTIC = 1, + /** multi-scale variant of the classical Hough transform. The lines are encoded the same way as + HOUGH_STANDARD. */ + HOUGH_MULTI_SCALE = 2, + HOUGH_GRADIENT = 3 //!< basically *21HT*, described in @cite Yuen90 +}; + +//! Variants of Line Segment %Detector +enum LineSegmentDetectorModes { + LSD_REFINE_NONE = 0, //!< No refinement applied + LSD_REFINE_STD = 1, //!< Standard refinement is applied. E.g. breaking arches into smaller straighter line approximations. + LSD_REFINE_ADV = 2 //!< Advanced refinement. Number of false alarms is calculated, lines are + //!< refined through increase of precision, decrement in size, etc. +}; + +//! @} imgproc_feature + +/** Histogram comparison methods + @ingroup imgproc_hist +*/ +enum HistCompMethods { + /** Correlation + \f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f] + where + \f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f] + and \f$N\f$ is a total number of histogram bins. */ + HISTCMP_CORREL = 0, + /** Chi-Square + \f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f] */ + HISTCMP_CHISQR = 1, + /** Intersection + \f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f] */ + HISTCMP_INTERSECT = 2, + /** Bhattacharyya distance + (In fact, OpenCV computes Hellinger distance, which is related to Bhattacharyya coefficient.) + \f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f] */ + HISTCMP_BHATTACHARYYA = 3, + HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA, //!< Synonym for HISTCMP_BHATTACHARYYA + /** Alternative Chi-Square + \f[d(H_1,H_2) = 2 * \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)+H_2(I)}\f] + This alternative formula is regularly used for texture comparison. See e.g. @cite Puzicha1997 */ + HISTCMP_CHISQR_ALT = 4, + /** Kullback-Leibler divergence + \f[d(H_1,H_2) = \sum _I H_1(I) \log \left(\frac{H_1(I)}{H_2(I)}\right)\f] */ + HISTCMP_KL_DIV = 5 +}; + +/** the color conversion codes +@see @ref imgproc_color_conversions +@ingroup imgproc_color_conversions + */ +enum ColorConversionCodes { + COLOR_BGR2BGRA = 0, //!< add alpha channel to RGB or BGR image + COLOR_RGB2RGBA = COLOR_BGR2BGRA, + + COLOR_BGRA2BGR = 1, //!< remove alpha channel from RGB or BGR image + COLOR_RGBA2RGB = COLOR_BGRA2BGR, + + COLOR_BGR2RGBA = 2, //!< convert between RGB and BGR color spaces (with or without alpha channel) + COLOR_RGB2BGRA = COLOR_BGR2RGBA, + + COLOR_RGBA2BGR = 3, + COLOR_BGRA2RGB = COLOR_RGBA2BGR, + + COLOR_BGR2RGB = 4, + COLOR_RGB2BGR = COLOR_BGR2RGB, + + COLOR_BGRA2RGBA = 5, + COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, + + COLOR_BGR2GRAY = 6, //!< convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions" + COLOR_RGB2GRAY = 7, + COLOR_GRAY2BGR = 8, + COLOR_GRAY2RGB = COLOR_GRAY2BGR, + COLOR_GRAY2BGRA = 9, + COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, + COLOR_BGRA2GRAY = 10, + COLOR_RGBA2GRAY = 11, + + COLOR_BGR2BGR565 = 12, //!< convert between RGB/BGR and BGR565 (16-bit images) + COLOR_RGB2BGR565 = 13, + COLOR_BGR5652BGR = 14, + COLOR_BGR5652RGB = 15, + COLOR_BGRA2BGR565 = 16, + COLOR_RGBA2BGR565 = 17, + COLOR_BGR5652BGRA = 18, + COLOR_BGR5652RGBA = 19, + + COLOR_GRAY2BGR565 = 20, //!< convert between grayscale to BGR565 (16-bit images) + COLOR_BGR5652GRAY = 21, + + COLOR_BGR2BGR555 = 22, //!< convert between RGB/BGR and BGR555 (16-bit images) + COLOR_RGB2BGR555 = 23, + COLOR_BGR5552BGR = 24, + COLOR_BGR5552RGB = 25, + COLOR_BGRA2BGR555 = 26, + COLOR_RGBA2BGR555 = 27, + COLOR_BGR5552BGRA = 28, + COLOR_BGR5552RGBA = 29, + + COLOR_GRAY2BGR555 = 30, //!< convert between grayscale and BGR555 (16-bit images) + COLOR_BGR5552GRAY = 31, + + COLOR_BGR2XYZ = 32, //!< convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions" + COLOR_RGB2XYZ = 33, + COLOR_XYZ2BGR = 34, + COLOR_XYZ2RGB = 35, + + COLOR_BGR2YCrCb = 36, //!< convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions" + COLOR_RGB2YCrCb = 37, + COLOR_YCrCb2BGR = 38, + COLOR_YCrCb2RGB = 39, + + COLOR_BGR2HSV = 40, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" + COLOR_RGB2HSV = 41, + + COLOR_BGR2Lab = 44, //!< convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions" + COLOR_RGB2Lab = 45, + + COLOR_BGR2Luv = 50, //!< convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions" + COLOR_RGB2Luv = 51, + COLOR_BGR2HLS = 52, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hls "color conversions" + COLOR_RGB2HLS = 53, + + COLOR_HSV2BGR = 54, //!< backward conversions HSV to RGB/BGR with H range 0..180 if 8 bit image + COLOR_HSV2RGB = 55, + + COLOR_Lab2BGR = 56, + COLOR_Lab2RGB = 57, + COLOR_Luv2BGR = 58, + COLOR_Luv2RGB = 59, + COLOR_HLS2BGR = 60, //!< backward conversions HLS to RGB/BGR with H range 0..180 if 8 bit image + COLOR_HLS2RGB = 61, + + COLOR_BGR2HSV_FULL = 66, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" + COLOR_RGB2HSV_FULL = 67, + COLOR_BGR2HLS_FULL = 68, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hls "color conversions" + COLOR_RGB2HLS_FULL = 69, + + COLOR_HSV2BGR_FULL = 70, //!< backward conversions HSV to RGB/BGR with H range 0..255 if 8 bit image + COLOR_HSV2RGB_FULL = 71, + COLOR_HLS2BGR_FULL = 72, //!< backward conversions HLS to RGB/BGR with H range 0..255 if 8 bit image + COLOR_HLS2RGB_FULL = 73, + + COLOR_LBGR2Lab = 74, + COLOR_LRGB2Lab = 75, + COLOR_LBGR2Luv = 76, + COLOR_LRGB2Luv = 77, + + COLOR_Lab2LBGR = 78, + COLOR_Lab2LRGB = 79, + COLOR_Luv2LBGR = 80, + COLOR_Luv2LRGB = 81, + + COLOR_BGR2YUV = 82, //!< convert between RGB/BGR and YUV + COLOR_RGB2YUV = 83, + COLOR_YUV2BGR = 84, + COLOR_YUV2RGB = 85, + + //! YUV 4:2:0 family to RGB + COLOR_YUV2RGB_NV12 = 90, + COLOR_YUV2BGR_NV12 = 91, + COLOR_YUV2RGB_NV21 = 92, + COLOR_YUV2BGR_NV21 = 93, + COLOR_YUV420sp2RGB = COLOR_YUV2RGB_NV21, + COLOR_YUV420sp2BGR = COLOR_YUV2BGR_NV21, + + COLOR_YUV2RGBA_NV12 = 94, + COLOR_YUV2BGRA_NV12 = 95, + COLOR_YUV2RGBA_NV21 = 96, + COLOR_YUV2BGRA_NV21 = 97, + COLOR_YUV420sp2RGBA = COLOR_YUV2RGBA_NV21, + COLOR_YUV420sp2BGRA = COLOR_YUV2BGRA_NV21, + + COLOR_YUV2RGB_YV12 = 98, + COLOR_YUV2BGR_YV12 = 99, + COLOR_YUV2RGB_IYUV = 100, + COLOR_YUV2BGR_IYUV = 101, + COLOR_YUV2RGB_I420 = COLOR_YUV2RGB_IYUV, + COLOR_YUV2BGR_I420 = COLOR_YUV2BGR_IYUV, + COLOR_YUV420p2RGB = COLOR_YUV2RGB_YV12, + COLOR_YUV420p2BGR = COLOR_YUV2BGR_YV12, + + COLOR_YUV2RGBA_YV12 = 102, + COLOR_YUV2BGRA_YV12 = 103, + COLOR_YUV2RGBA_IYUV = 104, + COLOR_YUV2BGRA_IYUV = 105, + COLOR_YUV2RGBA_I420 = COLOR_YUV2RGBA_IYUV, + COLOR_YUV2BGRA_I420 = COLOR_YUV2BGRA_IYUV, + COLOR_YUV420p2RGBA = COLOR_YUV2RGBA_YV12, + COLOR_YUV420p2BGRA = COLOR_YUV2BGRA_YV12, + + COLOR_YUV2GRAY_420 = 106, + COLOR_YUV2GRAY_NV21 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_NV12 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_YV12 = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_IYUV = COLOR_YUV2GRAY_420, + COLOR_YUV2GRAY_I420 = COLOR_YUV2GRAY_420, + COLOR_YUV420sp2GRAY = COLOR_YUV2GRAY_420, + COLOR_YUV420p2GRAY = COLOR_YUV2GRAY_420, + + //! YUV 4:2:2 family to RGB + COLOR_YUV2RGB_UYVY = 107, + COLOR_YUV2BGR_UYVY = 108, + //COLOR_YUV2RGB_VYUY = 109, + //COLOR_YUV2BGR_VYUY = 110, + COLOR_YUV2RGB_Y422 = COLOR_YUV2RGB_UYVY, + COLOR_YUV2BGR_Y422 = COLOR_YUV2BGR_UYVY, + COLOR_YUV2RGB_UYNV = COLOR_YUV2RGB_UYVY, + COLOR_YUV2BGR_UYNV = COLOR_YUV2BGR_UYVY, + + COLOR_YUV2RGBA_UYVY = 111, + COLOR_YUV2BGRA_UYVY = 112, + //COLOR_YUV2RGBA_VYUY = 113, + //COLOR_YUV2BGRA_VYUY = 114, + COLOR_YUV2RGBA_Y422 = COLOR_YUV2RGBA_UYVY, + COLOR_YUV2BGRA_Y422 = COLOR_YUV2BGRA_UYVY, + COLOR_YUV2RGBA_UYNV = COLOR_YUV2RGBA_UYVY, + COLOR_YUV2BGRA_UYNV = COLOR_YUV2BGRA_UYVY, + + COLOR_YUV2RGB_YUY2 = 115, + COLOR_YUV2BGR_YUY2 = 116, + COLOR_YUV2RGB_YVYU = 117, + COLOR_YUV2BGR_YVYU = 118, + COLOR_YUV2RGB_YUYV = COLOR_YUV2RGB_YUY2, + COLOR_YUV2BGR_YUYV = COLOR_YUV2BGR_YUY2, + COLOR_YUV2RGB_YUNV = COLOR_YUV2RGB_YUY2, + COLOR_YUV2BGR_YUNV = COLOR_YUV2BGR_YUY2, + + COLOR_YUV2RGBA_YUY2 = 119, + COLOR_YUV2BGRA_YUY2 = 120, + COLOR_YUV2RGBA_YVYU = 121, + COLOR_YUV2BGRA_YVYU = 122, + COLOR_YUV2RGBA_YUYV = COLOR_YUV2RGBA_YUY2, + COLOR_YUV2BGRA_YUYV = COLOR_YUV2BGRA_YUY2, + COLOR_YUV2RGBA_YUNV = COLOR_YUV2RGBA_YUY2, + COLOR_YUV2BGRA_YUNV = COLOR_YUV2BGRA_YUY2, + + COLOR_YUV2GRAY_UYVY = 123, + COLOR_YUV2GRAY_YUY2 = 124, + //CV_YUV2GRAY_VYUY = CV_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_Y422 = COLOR_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_UYNV = COLOR_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_YVYU = COLOR_YUV2GRAY_YUY2, + COLOR_YUV2GRAY_YUYV = COLOR_YUV2GRAY_YUY2, + COLOR_YUV2GRAY_YUNV = COLOR_YUV2GRAY_YUY2, + + //! alpha premultiplication + COLOR_RGBA2mRGBA = 125, + COLOR_mRGBA2RGBA = 126, + + //! RGB to YUV 4:2:0 family + COLOR_RGB2YUV_I420 = 127, + COLOR_BGR2YUV_I420 = 128, + COLOR_RGB2YUV_IYUV = COLOR_RGB2YUV_I420, + COLOR_BGR2YUV_IYUV = COLOR_BGR2YUV_I420, + + COLOR_RGBA2YUV_I420 = 129, + COLOR_BGRA2YUV_I420 = 130, + COLOR_RGBA2YUV_IYUV = COLOR_RGBA2YUV_I420, + COLOR_BGRA2YUV_IYUV = COLOR_BGRA2YUV_I420, + COLOR_RGB2YUV_YV12 = 131, + COLOR_BGR2YUV_YV12 = 132, + COLOR_RGBA2YUV_YV12 = 133, + COLOR_BGRA2YUV_YV12 = 134, + + //! Demosaicing + COLOR_BayerBG2BGR = 46, + COLOR_BayerGB2BGR = 47, + COLOR_BayerRG2BGR = 48, + COLOR_BayerGR2BGR = 49, + + COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, + COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, + COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, + COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, + + COLOR_BayerBG2GRAY = 86, + COLOR_BayerGB2GRAY = 87, + COLOR_BayerRG2GRAY = 88, + COLOR_BayerGR2GRAY = 89, + + //! Demosaicing using Variable Number of Gradients + COLOR_BayerBG2BGR_VNG = 62, + COLOR_BayerGB2BGR_VNG = 63, + COLOR_BayerRG2BGR_VNG = 64, + COLOR_BayerGR2BGR_VNG = 65, + + COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, + COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, + COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, + COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, + + //! Edge-Aware Demosaicing + COLOR_BayerBG2BGR_EA = 135, + COLOR_BayerGB2BGR_EA = 136, + COLOR_BayerRG2BGR_EA = 137, + COLOR_BayerGR2BGR_EA = 138, + + COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, + COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, + COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, + COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, + + //! Demosaicing with alpha channel + COLOR_BayerBG2BGRA = 139, + COLOR_BayerGB2BGRA = 140, + COLOR_BayerRG2BGRA = 141, + COLOR_BayerGR2BGRA = 142, + + COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, + COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, + COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, + COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, + + COLOR_COLORCVT_MAX = 143 +}; + +//! @addtogroup imgproc_shape +//! @{ + +//! types of intersection between rectangles +enum RectanglesIntersectTypes { + INTERSECT_NONE = 0, //!< No intersection + INTERSECT_PARTIAL = 1, //!< There is a partial intersection + INTERSECT_FULL = 2 //!< One of the rectangle is fully enclosed in the other +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform +*/ +class CV_EXPORTS_W GeneralizedHough : public Algorithm +{ +public: + //! set template to search + CV_WRAP virtual void setTemplate(InputArray templ, Point templCenter = Point(-1, -1)) = 0; + CV_WRAP virtual void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) = 0; + + //! find template on image + CV_WRAP virtual void detect(InputArray image, OutputArray positions, OutputArray votes = noArray()) = 0; + CV_WRAP virtual void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes = noArray()) = 0; + + //! Canny low threshold. + CV_WRAP virtual void setCannyLowThresh(int cannyLowThresh) = 0; + CV_WRAP virtual int getCannyLowThresh() const = 0; + + //! Canny high threshold. + CV_WRAP virtual void setCannyHighThresh(int cannyHighThresh) = 0; + CV_WRAP virtual int getCannyHighThresh() const = 0; + + //! Minimum distance between the centers of the detected objects. + CV_WRAP virtual void setMinDist(double minDist) = 0; + CV_WRAP virtual double getMinDist() const = 0; + + //! Inverse ratio of the accumulator resolution to the image resolution. + CV_WRAP virtual void setDp(double dp) = 0; + CV_WRAP virtual double getDp() const = 0; + + //! Maximal size of inner buffers. + CV_WRAP virtual void setMaxBufferSize(int maxBufferSize) = 0; + CV_WRAP virtual int getMaxBufferSize() const = 0; +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform + +Detects position only without translation and rotation @cite Ballard1981 . +*/ +class CV_EXPORTS_W GeneralizedHoughBallard : public GeneralizedHough +{ +public: + //! R-Table levels. + CV_WRAP virtual void setLevels(int levels) = 0; + CV_WRAP virtual int getLevels() const = 0; + + //! The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected. + CV_WRAP virtual void setVotesThreshold(int votesThreshold) = 0; + CV_WRAP virtual int getVotesThreshold() const = 0; +}; + +/** @brief finds arbitrary template in the grayscale image using Generalized Hough Transform + +Detects position, translation and rotation @cite Guil1999 . +*/ +class CV_EXPORTS_W GeneralizedHoughGuil : public GeneralizedHough +{ +public: + //! Angle difference in degrees between two points in feature. + CV_WRAP virtual void setXi(double xi) = 0; + CV_WRAP virtual double getXi() const = 0; + + //! Feature table levels. + CV_WRAP virtual void setLevels(int levels) = 0; + CV_WRAP virtual int getLevels() const = 0; + + //! Maximal difference between angles that treated as equal. + CV_WRAP virtual void setAngleEpsilon(double angleEpsilon) = 0; + CV_WRAP virtual double getAngleEpsilon() const = 0; + + //! Minimal rotation angle to detect in degrees. + CV_WRAP virtual void setMinAngle(double minAngle) = 0; + CV_WRAP virtual double getMinAngle() const = 0; + + //! Maximal rotation angle to detect in degrees. + CV_WRAP virtual void setMaxAngle(double maxAngle) = 0; + CV_WRAP virtual double getMaxAngle() const = 0; + + //! Angle step in degrees. + CV_WRAP virtual void setAngleStep(double angleStep) = 0; + CV_WRAP virtual double getAngleStep() const = 0; + + //! Angle votes threshold. + CV_WRAP virtual void setAngleThresh(int angleThresh) = 0; + CV_WRAP virtual int getAngleThresh() const = 0; + + //! Minimal scale to detect. + CV_WRAP virtual void setMinScale(double minScale) = 0; + CV_WRAP virtual double getMinScale() const = 0; + + //! Maximal scale to detect. + CV_WRAP virtual void setMaxScale(double maxScale) = 0; + CV_WRAP virtual double getMaxScale() const = 0; + + //! Scale step. + CV_WRAP virtual void setScaleStep(double scaleStep) = 0; + CV_WRAP virtual double getScaleStep() const = 0; + + //! Scale votes threshold. + CV_WRAP virtual void setScaleThresh(int scaleThresh) = 0; + CV_WRAP virtual int getScaleThresh() const = 0; + + //! Position votes threshold. + CV_WRAP virtual void setPosThresh(int posThresh) = 0; + CV_WRAP virtual int getPosThresh() const = 0; +}; + +//! @} imgproc_shape + +//! @addtogroup imgproc_hist +//! @{ + +/** @brief Base class for Contrast Limited Adaptive Histogram Equalization. +*/ +class CV_EXPORTS_W CLAHE : public Algorithm +{ +public: + /** @brief Equalizes the histogram of a grayscale image using Contrast Limited Adaptive Histogram Equalization. + + @param src Source image of type CV_8UC1 or CV_16UC1. + @param dst Destination image. + */ + CV_WRAP virtual void apply(InputArray src, OutputArray dst) = 0; + + /** @brief Sets threshold for contrast limiting. + + @param clipLimit threshold value. + */ + CV_WRAP virtual void setClipLimit(double clipLimit) = 0; + + //! Returns threshold value for contrast limiting. + CV_WRAP virtual double getClipLimit() const = 0; + + /** @brief Sets size of grid for histogram equalization. Input image will be divided into + equally sized rectangular tiles. + + @param tileGridSize defines the number of tiles in row and column. + */ + CV_WRAP virtual void setTilesGridSize(Size tileGridSize) = 0; + + //!@brief Returns Size defines the number of tiles in row and column. + CV_WRAP virtual Size getTilesGridSize() const = 0; + + CV_WRAP virtual void collectGarbage() = 0; +}; + +//! @} imgproc_hist + +//! @addtogroup imgproc_subdiv2d +//! @{ + +class CV_EXPORTS_W Subdiv2D +{ +public: + /** Subdiv2D point location cases */ + enum { PTLOC_ERROR = -2, //!< Point location error + PTLOC_OUTSIDE_RECT = -1, //!< Point outside the subdivision bounding rect + PTLOC_INSIDE = 0, //!< Point inside some facet + PTLOC_VERTEX = 1, //!< Point coincides with one of the subdivision vertices + PTLOC_ON_EDGE = 2 //!< Point on some edge + }; + + /** Subdiv2D edge type navigation (see: getEdge()) */ + enum { NEXT_AROUND_ORG = 0x00, + NEXT_AROUND_DST = 0x22, + PREV_AROUND_ORG = 0x11, + PREV_AROUND_DST = 0x33, + NEXT_AROUND_LEFT = 0x13, + NEXT_AROUND_RIGHT = 0x31, + PREV_AROUND_LEFT = 0x20, + PREV_AROUND_RIGHT = 0x02 + }; + + /** creates an empty Subdiv2D object. + To create a new empty Delaunay subdivision you need to use the #initDelaunay function. + */ + CV_WRAP Subdiv2D(); + + /** @overload + + @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision. + + The function creates an empty Delaunay subdivision where 2D points can be added using the function + insert() . All of the points to be added must be within the specified rectangle, otherwise a runtime + error is raised. + */ + CV_WRAP Subdiv2D(Rect rect); + + /** @brief Creates a new empty Delaunay subdivision + + @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision. + + */ + CV_WRAP void initDelaunay(Rect rect); + + /** @brief Insert a single point into a Delaunay triangulation. + + @param pt Point to insert. + + The function inserts a single point into a subdivision and modifies the subdivision topology + appropriately. If a point with the same coordinates exists already, no new point is added. + @returns the ID of the point. + + @note If the point is outside of the triangulation specified rect a runtime error is raised. + */ + CV_WRAP int insert(Point2f pt); + + /** @brief Insert multiple points into a Delaunay triangulation. + + @param ptvec Points to insert. + + The function inserts a vector of points into a subdivision and modifies the subdivision topology + appropriately. + */ + CV_WRAP void insert(const std::vector& ptvec); + + /** @brief Returns the location of a point within a Delaunay triangulation. + + @param pt Point to locate. + @param edge Output edge that the point belongs to or is located to the right of it. + @param vertex Optional output vertex the input point coincides with. + + The function locates the input point within the subdivision and gives one of the triangle edges + or vertices. + + @returns an integer which specify one of the following five cases for point location: + - The point falls into some facet. The function returns #PTLOC_INSIDE and edge will contain one of + edges of the facet. + - The point falls onto the edge. The function returns #PTLOC_ON_EDGE and edge will contain this edge. + - The point coincides with one of the subdivision vertices. The function returns #PTLOC_VERTEX and + vertex will contain a pointer to the vertex. + - The point is outside the subdivision reference rectangle. The function returns #PTLOC_OUTSIDE_RECT + and no pointers are filled. + - One of input arguments is invalid. A runtime error is raised or, if silent or "parent" error + processing mode is selected, #PTLOC_ERROR is returned. + */ + CV_WRAP int locate(Point2f pt, CV_OUT int& edge, CV_OUT int& vertex); + + /** @brief Finds the subdivision vertex closest to the given point. + + @param pt Input point. + @param nearestPt Output subdivision vertex point. + + The function is another function that locates the input point within the subdivision. It finds the + subdivision vertex that is the closest to the input point. It is not necessarily one of vertices + of the facet containing the input point, though the facet (located using locate() ) is used as a + starting point. + + @returns vertex ID. + */ + CV_WRAP int findNearest(Point2f pt, CV_OUT Point2f* nearestPt = 0); + + /** @brief Returns a list of all edges. + + @param edgeList Output vector. + + The function gives each edge as a 4 numbers vector, where each two are one of the edge + vertices. i.e. org_x = v[0], org_y = v[1], dst_x = v[2], dst_y = v[3]. + */ + CV_WRAP void getEdgeList(CV_OUT std::vector& edgeList) const; + + /** @brief Returns a list of the leading edge ID connected to each triangle. + + @param leadingEdgeList Output vector. + + The function gives one edge ID for each triangle. + */ + CV_WRAP void getLeadingEdgeList(CV_OUT std::vector& leadingEdgeList) const; + + /** @brief Returns a list of all triangles. + + @param triangleList Output vector. + + The function gives each triangle as a 6 numbers vector, where each two are one of the triangle + vertices. i.e. p1_x = v[0], p1_y = v[1], p2_x = v[2], p2_y = v[3], p3_x = v[4], p3_y = v[5]. + */ + CV_WRAP void getTriangleList(CV_OUT std::vector& triangleList) const; + + /** @brief Returns a list of all Voronoi facets. + + @param idx Vector of vertices IDs to consider. For all vertices you can pass empty vector. + @param facetList Output vector of the Voronoi facets. + @param facetCenters Output vector of the Voronoi facets center points. + + */ + CV_WRAP void getVoronoiFacetList(const std::vector& idx, CV_OUT std::vector >& facetList, + CV_OUT std::vector& facetCenters); + + /** @brief Returns vertex location from vertex ID. + + @param vertex vertex ID. + @param firstEdge Optional. The first edge ID which is connected to the vertex. + @returns vertex (x,y) + + */ + CV_WRAP Point2f getVertex(int vertex, CV_OUT int* firstEdge = 0) const; + + /** @brief Returns one of the edges related to the given edge. + + @param edge Subdivision edge ID. + @param nextEdgeType Parameter specifying which of the related edges to return. + The following values are possible: + - NEXT_AROUND_ORG next around the edge origin ( eOnext on the picture below if e is the input edge) + - NEXT_AROUND_DST next around the edge vertex ( eDnext ) + - PREV_AROUND_ORG previous around the edge origin (reversed eRnext ) + - PREV_AROUND_DST previous around the edge destination (reversed eLnext ) + - NEXT_AROUND_LEFT next around the left facet ( eLnext ) + - NEXT_AROUND_RIGHT next around the right facet ( eRnext ) + - PREV_AROUND_LEFT previous around the left facet (reversed eOnext ) + - PREV_AROUND_RIGHT previous around the right facet (reversed eDnext ) + + ![sample output](pics/quadedge.png) + + @returns edge ID related to the input edge. + */ + CV_WRAP int getEdge( int edge, int nextEdgeType ) const; + + /** @brief Returns next edge around the edge origin. + + @param edge Subdivision edge ID. + + @returns an integer which is next edge ID around the edge origin: eOnext on the + picture above if e is the input edge). + */ + CV_WRAP int nextEdge(int edge) const; + + /** @brief Returns another edge of the same quad-edge. + + @param edge Subdivision edge ID. + @param rotate Parameter specifying which of the edges of the same quad-edge as the input + one to return. The following values are possible: + - 0 - the input edge ( e on the picture below if e is the input edge) + - 1 - the rotated edge ( eRot ) + - 2 - the reversed edge (reversed e (in green)) + - 3 - the reversed rotated edge (reversed eRot (in green)) + + @returns one of the edges ID of the same quad-edge as the input edge. + */ + CV_WRAP int rotateEdge(int edge, int rotate) const; + CV_WRAP int symEdge(int edge) const; + + /** @brief Returns the edge origin. + + @param edge Subdivision edge ID. + @param orgpt Output vertex location. + + @returns vertex ID. + */ + CV_WRAP int edgeOrg(int edge, CV_OUT Point2f* orgpt = 0) const; + + /** @brief Returns the edge destination. + + @param edge Subdivision edge ID. + @param dstpt Output vertex location. + + @returns vertex ID. + */ + CV_WRAP int edgeDst(int edge, CV_OUT Point2f* dstpt = 0) const; + +protected: + int newEdge(); + void deleteEdge(int edge); + int newPoint(Point2f pt, bool isvirtual, int firstEdge = 0); + void deletePoint(int vtx); + void setEdgePoints( int edge, int orgPt, int dstPt ); + void splice( int edgeA, int edgeB ); + int connectEdges( int edgeA, int edgeB ); + void swapEdges( int edge ); + int isRightOf(Point2f pt, int edge) const; + void calcVoronoi(); + void clearVoronoi(); + void checkSubdiv() const; + + struct CV_EXPORTS Vertex + { + Vertex(); + Vertex(Point2f pt, bool _isvirtual, int _firstEdge=0); + bool isvirtual() const; + bool isfree() const; + + int firstEdge; + int type; + Point2f pt; + }; + + struct CV_EXPORTS QuadEdge + { + QuadEdge(); + QuadEdge(int edgeidx); + bool isfree() const; + + int next[4]; + int pt[4]; + }; + + //! All of the vertices + std::vector vtx; + //! All of the edges + std::vector qedges; + int freeQEdge; + int freePoint; + bool validGeometry; + + int recentEdge; + //! Top left corner of the bounding rect + Point2f topLeft; + //! Bottom right corner of the bounding rect + Point2f bottomRight; +}; + +//! @} imgproc_subdiv2d + +//! @addtogroup imgproc_feature +//! @{ + +/** @brief Line segment detector class + +following the algorithm described at @cite Rafael12 . + +@note Implementation has been removed due original code license conflict + +*/ +class CV_EXPORTS_W LineSegmentDetector : public Algorithm +{ +public: + + /** @brief Finds lines in the input image. + + This is the output of the default parameters of the algorithm on the above shown image. + + ![image](pics/building_lsd.png) + + @param _image A grayscale (CV_8UC1) input image. If only a roi needs to be selected, use: + `lsd_ptr-\>detect(image(roi), lines, ...); lines += Scalar(roi.x, roi.y, roi.x, roi.y);` + @param _lines A vector of Vec4i or Vec4f elements specifying the beginning and ending point of a line. Where + Vec4i/Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. Returned lines are strictly + oriented depending on the gradient. + @param width Vector of widths of the regions, where the lines are found. E.g. Width of line. + @param prec Vector of precisions with which the lines are found. + @param nfa Vector containing number of false alarms in the line region, with precision of 10%. The + bigger the value, logarithmically better the detection. + - -1 corresponds to 10 mean false alarms + - 0 corresponds to 1 mean false alarm + - 1 corresponds to 0.1 mean false alarms + This vector will be calculated only when the objects type is #LSD_REFINE_ADV. + */ + CV_WRAP virtual void detect(InputArray _image, OutputArray _lines, + OutputArray width = noArray(), OutputArray prec = noArray(), + OutputArray nfa = noArray()) = 0; + + /** @brief Draws the line segments on a given image. + @param _image The image, where the lines will be drawn. Should be bigger or equal to the image, + where the lines were found. + @param lines A vector of the lines that needed to be drawn. + */ + CV_WRAP virtual void drawSegments(InputOutputArray _image, InputArray lines) = 0; + + /** @brief Draws two groups of lines in blue and red, counting the non overlapping (mismatching) pixels. + + @param size The size of the image, where lines1 and lines2 were found. + @param lines1 The first group of lines that needs to be drawn. It is visualized in blue color. + @param lines2 The second group of lines. They visualized in red color. + @param _image Optional image, where the lines will be drawn. The image should be color(3-channel) + in order for lines1 and lines2 to be drawn in the above mentioned colors. + */ + CV_WRAP virtual int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) = 0; + + virtual ~LineSegmentDetector() { } +}; + +/** @brief Creates a smart pointer to a LineSegmentDetector object and initializes it. + +The LineSegmentDetector algorithm is defined using the standard values. Only advanced users may want +to edit those, as to tailor it for their own application. + +@param _refine The way found lines will be refined, see #LineSegmentDetectorModes +@param _scale The scale of the image that will be used to find the lines. Range (0..1]. +@param _sigma_scale Sigma for Gaussian filter. It is computed as sigma = _sigma_scale/_scale. +@param _quant Bound to the quantization error on the gradient norm. +@param _ang_th Gradient angle tolerance in degrees. +@param _log_eps Detection threshold: -log10(NFA) \> log_eps. Used only when advance refinement +is chosen. +@param _density_th Minimal density of aligned region points in the enclosing rectangle. +@param _n_bins Number of bins in pseudo-ordering of gradient modulus. + +@note Implementation has been removed due original code license conflict + */ +CV_EXPORTS_W Ptr createLineSegmentDetector( + int _refine = LSD_REFINE_STD, double _scale = 0.8, + double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5, + double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024); + +//! @} imgproc_feature + +//! @addtogroup imgproc_filter +//! @{ + +/** @brief Returns Gaussian filter coefficients. + +The function computes and returns the \f$\texttt{ksize} \times 1\f$ matrix of Gaussian filter +coefficients: + +\f[G_i= \alpha *e^{-(i-( \texttt{ksize} -1)/2)^2/(2* \texttt{sigma}^2)},\f] + +where \f$i=0..\texttt{ksize}-1\f$ and \f$\alpha\f$ is the scale factor chosen so that \f$\sum_i G_i=1\f$. + +Two of such generated kernels can be passed to sepFilter2D. Those functions automatically recognize +smoothing kernels (a symmetrical kernel with sum of weights equal to 1) and handle them accordingly. +You may also use the higher-level GaussianBlur. +@param ksize Aperture size. It should be odd ( \f$\texttt{ksize} \mod 2 = 1\f$ ) and positive. +@param sigma Gaussian standard deviation. If it is non-positive, it is computed from ksize as +`sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`. +@param ktype Type of filter coefficients. It can be CV_32F or CV_64F . +@sa sepFilter2D, getDerivKernels, getStructuringElement, GaussianBlur + */ +CV_EXPORTS_W Mat getGaussianKernel( int ksize, double sigma, int ktype = CV_64F ); + +/** @brief Returns filter coefficients for computing spatial image derivatives. + +The function computes and returns the filter coefficients for spatial image derivatives. When +`ksize=CV_SCHARR`, the Scharr \f$3 \times 3\f$ kernels are generated (see #Scharr). Otherwise, Sobel +kernels are generated (see #Sobel). The filters are normally passed to #sepFilter2D or to + +@param kx Output matrix of row filter coefficients. It has the type ktype . +@param ky Output matrix of column filter coefficients. It has the type ktype . +@param dx Derivative order in respect of x. +@param dy Derivative order in respect of y. +@param ksize Aperture size. It can be CV_SCHARR, 1, 3, 5, or 7. +@param normalize Flag indicating whether to normalize (scale down) the filter coefficients or not. +Theoretically, the coefficients should have the denominator \f$=2^{ksize*2-dx-dy-2}\f$. If you are +going to filter floating-point images, you are likely to use the normalized kernels. But if you +compute derivatives of an 8-bit image, store the results in a 16-bit image, and wish to preserve +all the fractional bits, you may want to set normalize=false . +@param ktype Type of filter coefficients. It can be CV_32f or CV_64F . + */ +CV_EXPORTS_W void getDerivKernels( OutputArray kx, OutputArray ky, + int dx, int dy, int ksize, + bool normalize = false, int ktype = CV_32F ); + +/** @brief Returns Gabor filter coefficients. + +For more details about gabor filter equations and parameters, see: [Gabor +Filter](http://en.wikipedia.org/wiki/Gabor_filter). + +@param ksize Size of the filter returned. +@param sigma Standard deviation of the gaussian envelope. +@param theta Orientation of the normal to the parallel stripes of a Gabor function. +@param lambd Wavelength of the sinusoidal factor. +@param gamma Spatial aspect ratio. +@param psi Phase offset. +@param ktype Type of filter coefficients. It can be CV_32F or CV_64F . + */ +CV_EXPORTS_W Mat getGaborKernel( Size ksize, double sigma, double theta, double lambd, + double gamma, double psi = CV_PI*0.5, int ktype = CV_64F ); + +//! returns "magic" border value for erosion and dilation. It is automatically transformed to Scalar::all(-DBL_MAX) for dilation. +static inline Scalar morphologyDefaultBorderValue() { return Scalar::all(DBL_MAX); } + +/** @brief Returns a structuring element of the specified size and shape for morphological operations. + +The function constructs and returns the structuring element that can be further passed to #erode, +#dilate or #morphologyEx. But you can also construct an arbitrary binary mask yourself and use it as +the structuring element. + +@param shape Element shape that could be one of #MorphShapes +@param ksize Size of the structuring element. +@param anchor Anchor position within the element. The default value \f$(-1, -1)\f$ means that the +anchor is at the center. Note that only the shape of a cross-shaped element depends on the anchor +position. In other cases the anchor just regulates how much the result of the morphological +operation is shifted. + */ +CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1)); + +/** @example samples/cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp +Sample code for simple filters +![Sample screenshot](Smoothing_Tutorial_Result_Median_Filter.jpg) +Check @ref tutorial_gausian_median_blur_bilateral_filter "the corresponding tutorial" for more details + */ + +/** @brief Blurs an image using the median filter. + +The function smoothes an image using the median filter with the \f$\texttt{ksize} \times +\texttt{ksize}\f$ aperture. Each channel of a multi-channel image is processed independently. +In-place operation is supported. + +@note The median filter uses #BORDER_REPLICATE internally to cope with border pixels, see #BorderTypes + +@param src input 1-, 3-, or 4-channel image; when ksize is 3 or 5, the image depth should be +CV_8U, CV_16U, or CV_32F, for larger aperture sizes, it can only be CV_8U. +@param dst destination array of the same size and type as src. +@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ... +@sa bilateralFilter, blur, boxFilter, GaussianBlur + */ +CV_EXPORTS_W void medianBlur( InputArray src, OutputArray dst, int ksize ); + +/** @brief Blurs an image using a Gaussian filter. + +The function convolves the source image with the specified Gaussian kernel. In-place filtering is +supported. + +@param src input image; the image can have any number of channels, which are processed +independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be +positive and odd. Or, they can be zero's and then they are computed from sigma. +@param sigmaX Gaussian kernel standard deviation in X direction. +@param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be +equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height, +respectively (see #getGaussianKernel for details); to fully control the result regardless of +possible future modifications of all this semantics, it is recommended to specify all of ksize, +sigmaX, and sigmaY. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. + +@sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur + */ +CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize, + double sigmaX, double sigmaY = 0, + int borderType = BORDER_DEFAULT ); + +/** @brief Applies the bilateral filter to an image. + +The function applies bilateral filtering to the input image, as described in +http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html +bilateralFilter can reduce unwanted noise very well while keeping edges fairly sharp. However, it is +very slow compared to most filters. + +_Sigma values_: For simplicity, you can set the 2 sigma values to be the same. If they are small (\< +10), the filter will not have much effect, whereas if they are large (\> 150), they will have a very +strong effect, making the image look "cartoonish". + +_Filter size_: Large filters (d \> 5) are very slow, so it is recommended to use d=5 for real-time +applications, and perhaps d=9 for offline applications that need heavy noise filtering. + +This filter does not work inplace. +@param src Source 8-bit or floating-point, 1-channel or 3-channel image. +@param dst Destination image of the same size and type as src . +@param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, +it is computed from sigmaSpace. +@param sigmaColor Filter sigma in the color space. A larger value of the parameter means that +farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting +in larger areas of semi-equal color. +@param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that +farther pixels will influence each other as long as their colors are close enough (see sigmaColor +). When d\>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is +proportional to sigmaSpace. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes + */ +CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d, + double sigmaColor, double sigmaSpace, + int borderType = BORDER_DEFAULT ); + +/** @brief Blurs an image using the box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \alpha \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \end{bmatrix}\f] + +where + +\f[\alpha = \begin{cases} \frac{1}{\texttt{ksize.width*ksize.height}} & \texttt{when } \texttt{normalize=true} \\1 & \texttt{otherwise}\end{cases}\f] + +Unnormalized box filter is useful for computing various integral characteristics over each pixel +neighborhood, such as covariance matrices of image derivatives (used in dense optical flow +algorithms, and so on). If you need to compute pixel sums over variable-size windows, use #integral. + +@param src input image. +@param dst output image of the same size and type as src. +@param ddepth the output image depth (-1 to use src.depth()). +@param ksize blurring kernel size. +@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel +center. +@param normalize flag, specifying whether the kernel is normalized by its area or not. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa blur, bilateralFilter, GaussianBlur, medianBlur, integral + */ +CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth, + Size ksize, Point anchor = Point(-1,-1), + bool normalize = true, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the normalized sum of squares of the pixel values overlapping the filter. + +For every pixel \f$ (x, y) \f$ in the source image, the function calculates the sum of squares of those neighboring +pixel values which overlap the filter placed over the pixel \f$ (x, y) \f$. + +The unnormalized square box filter can be useful in computing local image statistics such as the the local +variance and standard deviation around the neighborhood of a pixel. + +@param src input image +@param dst output image of the same size and type as _src +@param ddepth the output image depth (-1 to use src.depth()) +@param ksize kernel size +@param anchor kernel anchor point. The default value of Point(-1, -1) denotes that the anchor is at the kernel +center. +@param normalize flag, specifying whether the kernel is to be normalized by it's area or not. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa boxFilter +*/ +CV_EXPORTS_W void sqrBoxFilter( InputArray src, OutputArray dst, int ddepth, + Size ksize, Point anchor = Point(-1, -1), + bool normalize = true, + int borderType = BORDER_DEFAULT ); + +/** @brief Blurs an image using the normalized box filter. + +The function smooths an image using the kernel: + +\f[\texttt{K} = \frac{1}{\texttt{ksize.width*ksize.height}} \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \hdotsfor{6} \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \end{bmatrix}\f] + +The call `blur(src, dst, ksize, anchor, borderType)` is equivalent to `boxFilter(src, dst, src.type(), ksize, +anchor, true, borderType)`. + +@param src input image; it can have any number of channels, which are processed independently, but +the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param ksize blurring kernel size. +@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel +center. +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. +@sa boxFilter, bilateralFilter, GaussianBlur, medianBlur + */ +CV_EXPORTS_W void blur( InputArray src, OutputArray dst, + Size ksize, Point anchor = Point(-1,-1), + int borderType = BORDER_DEFAULT ); + +/** @brief Convolves an image with the kernel. + +The function applies an arbitrary linear filter to an image. In-place operation is supported. When +the aperture is partially outside the image, the function interpolates outlier pixel values +according to the specified border mode. + +The function does actually compute correlation, not the convolution: + +\f[\texttt{dst} (x,y) = \sum _{ \substack{0\leq x' < \texttt{kernel.cols}\\{0\leq y' < \texttt{kernel.rows}}}} \texttt{kernel} (x',y')* \texttt{src} (x+x'- \texttt{anchor.x} ,y+y'- \texttt{anchor.y} )\f] + +That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip +the kernel using #flip and set the new anchor to `(kernel.cols - anchor.x - 1, kernel.rows - +anchor.y - 1)`. + +The function uses the DFT-based algorithm in case of sufficiently large kernels (~`11 x 11` or +larger) and the direct algorithm for small kernels. + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param ddepth desired depth of the destination image, see @ref filter_depths "combinations" +@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point +matrix; if you want to apply different kernels to different channels, split the image into +separate color planes using split and process them individually. +@param anchor anchor of the kernel that indicates the relative position of a filtered point within +the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor +is at the kernel center. +@param delta optional value added to the filtered pixels before storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa sepFilter2D, dft, matchTemplate + */ +CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth, + InputArray kernel, Point anchor = Point(-1,-1), + double delta = 0, int borderType = BORDER_DEFAULT ); + +/** @brief Applies a separable linear filter to an image. + +The function applies a separable linear filter to the image. That is, first, every row of src is +filtered with the 1D kernel kernelX. Then, every column of the result is filtered with the 1D +kernel kernelY. The final result shifted by delta is stored in dst . + +@param src Source image. +@param dst Destination image of the same size and the same number of channels as src . +@param ddepth Destination image depth, see @ref filter_depths "combinations" +@param kernelX Coefficients for filtering each row. +@param kernelY Coefficients for filtering each column. +@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor +is at the kernel center. +@param delta Value added to the filtered results before storing them. +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa filter2D, Sobel, GaussianBlur, boxFilter, blur + */ +CV_EXPORTS_W void sepFilter2D( InputArray src, OutputArray dst, int ddepth, + InputArray kernelX, InputArray kernelY, + Point anchor = Point(-1,-1), + double delta = 0, int borderType = BORDER_DEFAULT ); + +/** @example samples/cpp/tutorial_code/ImgTrans/Sobel_Demo.cpp +Sample code using Sobel and/or Scharr OpenCV functions to make a simple Edge Detector +![Sample screenshot](Sobel_Derivatives_Tutorial_Result.jpg) +Check @ref tutorial_sobel_derivatives "the corresponding tutorial" for more details +*/ + +/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. + +In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to +calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$ +kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first +or the second x- or y- derivatives. + +There is also the special value `ksize = #CV_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr +filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is + +\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f] + +for the x-derivative, or transposed for the y-derivative. + +The function calculates an image derivative by convolving the image with the appropriate kernel: + +\f[\texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f] + +The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less +resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3) +or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first +case corresponds to a kernel of: + +\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f] + +The second case corresponds to a kernel of: + +\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f] + +@param src input image. +@param dst output image of the same size and the same number of channels as src . +@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of + 8-bit input images it will result in truncated derivatives. +@param dx order of the derivative x. +@param dy order of the derivative y. +@param ksize size of the extended Sobel kernel; it must be 1, 3, 5, or 7. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see #getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa Scharr, Laplacian, sepFilter2D, filter2D, GaussianBlur, cartToPolar + */ +CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, + int dx, int dy, int ksize = 3, + double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the first order image derivative in both x and y using a Sobel operator + +Equivalent to calling: + +@code +Sobel( src, dx, CV_16SC1, 1, 0, 3 ); +Sobel( src, dy, CV_16SC1, 0, 1, 3 ); +@endcode + +@param src input image. +@param dx output image with first-order derivative in x. +@param dy output image with first-order derivative in y. +@param ksize size of Sobel kernel. It must be 3. +@param borderType pixel extrapolation method, see #BorderTypes. + Only #BORDER_DEFAULT=#BORDER_REFLECT_101 and #BORDER_REPLICATE are supported. + +@sa Sobel + */ + +CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, + OutputArray dy, int ksize = 3, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates the first x- or y- image derivative using Scharr operator. + +The function computes the first x- or y- spatial image derivative using the Scharr operator. The +call + +\f[\texttt{Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)}\f] + +is equivalent to + +\f[\texttt{Sobel(src, dst, ddepth, dx, dy, CV_SCHARR, scale, delta, borderType)} .\f] + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param ddepth output image depth, see @ref filter_depths "combinations" +@param dx order of the derivative x. +@param dy order of the derivative y. +@param scale optional scale factor for the computed derivative values; by default, no scaling is +applied (see #getDerivKernels for details). +@param delta optional delta value that is added to the results prior to storing them in dst. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa cartToPolar + */ +CV_EXPORTS_W void Scharr( InputArray src, OutputArray dst, int ddepth, + int dx, int dy, double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +/** @example samples/cpp/laplace.cpp +An example using Laplace transformations for edge detection +*/ + +/** @brief Calculates the Laplacian of an image. + +The function calculates the Laplacian of the source image by adding up the second x and y +derivatives calculated using the Sobel operator: + +\f[\texttt{dst} = \Delta \texttt{src} = \frac{\partial^2 \texttt{src}}{\partial x^2} + \frac{\partial^2 \texttt{src}}{\partial y^2}\f] + +This is done when `ksize > 1`. When `ksize == 1`, the Laplacian is computed by filtering the image +with the following \f$3 \times 3\f$ aperture: + +\f[\vecthreethree {0}{1}{0}{1}{-4}{1}{0}{1}{0}\f] + +@param src Source image. +@param dst Destination image of the same size and the same number of channels as src . +@param ddepth Desired depth of the destination image. +@param ksize Aperture size used to compute the second-derivative filters. See #getDerivKernels for +details. The size must be positive and odd. +@param scale Optional scale factor for the computed Laplacian values. By default, no scaling is +applied. See #getDerivKernels for details. +@param delta Optional delta value that is added to the results prior to storing them in dst . +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@sa Sobel, Scharr + */ +CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth, + int ksize = 1, double scale = 1, double delta = 0, + int borderType = BORDER_DEFAULT ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_feature +//! @{ + +/** @example samples/cpp/edge.cpp +This program demonstrates usage of the Canny edge detector + +Check @ref tutorial_canny_detector "the corresponding tutorial" for more details +*/ + +/** @brief Finds edges in an image using the Canny algorithm @cite Canny86 . + +The function finds edges in the input image and marks them in the output map edges using the +Canny algorithm. The smallest value between threshold1 and threshold2 is used for edge linking. The +largest value is used to find initial segments of strong edges. See + + +@param image 8-bit input image. +@param edges output edge map; single channels 8-bit image, which has the same size as image . +@param threshold1 first threshold for the hysteresis procedure. +@param threshold2 second threshold for the hysteresis procedure. +@param apertureSize aperture size for the Sobel operator. +@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm +\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude ( +L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( +L2gradient=false ). + */ +CV_EXPORTS_W void Canny( InputArray image, OutputArray edges, + double threshold1, double threshold2, + int apertureSize = 3, bool L2gradient = false ); + +/** \overload + +Finds edges in an image using the Canny algorithm with custom image gradient. + +@param dx 16-bit x derivative of input image (CV_16SC1 or CV_16SC3). +@param dy 16-bit y derivative of input image (same type as dx). +@param edges output edge map; single channels 8-bit image, which has the same size as image . +@param threshold1 first threshold for the hysteresis procedure. +@param threshold2 second threshold for the hysteresis procedure. +@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm +\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude ( +L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough ( +L2gradient=false ). + */ +CV_EXPORTS_W void Canny( InputArray dx, InputArray dy, + OutputArray edges, + double threshold1, double threshold2, + bool L2gradient = false ); + +/** @brief Calculates the minimal eigenvalue of gradient matrices for corner detection. + +The function is similar to cornerEigenValsAndVecs but it calculates and stores only the minimal +eigenvalue of the covariance matrix of derivatives, that is, \f$\min(\lambda_1, \lambda_2)\f$ in terms +of the formulae in the cornerEigenValsAndVecs description. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the minimal eigenvalues. It has the type CV_32FC1 and the same size as +src . +@param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). +@param ksize Aperture parameter for the Sobel operator. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void cornerMinEigenVal( InputArray src, OutputArray dst, + int blockSize, int ksize = 3, + int borderType = BORDER_DEFAULT ); + +/** @brief Harris corner detector. + +The function runs the Harris corner detector on the image. Similarly to cornerMinEigenVal and +cornerEigenValsAndVecs , for each pixel \f$(x, y)\f$ it calculates a \f$2\times2\f$ gradient covariance +matrix \f$M^{(x,y)}\f$ over a \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood. Then, it +computes the following characteristic: + +\f[\texttt{dst} (x,y) = \mathrm{det} M^{(x,y)} - k \cdot \left ( \mathrm{tr} M^{(x,y)} \right )^2\f] + +Corners in the image can be found as the local maxima of this response map. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the Harris detector responses. It has the type CV_32FC1 and the same +size as src . +@param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). +@param ksize Aperture parameter for the Sobel operator. +@param k Harris detector free parameter. See the formula above. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize, + int ksize, double k, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates eigenvalues and eigenvectors of image blocks for corner detection. + +For every pixel \f$p\f$ , the function cornerEigenValsAndVecs considers a blockSize \f$\times\f$ blockSize +neighborhood \f$S(p)\f$ . It calculates the covariation matrix of derivatives over the neighborhood as: + +\f[M = \begin{bmatrix} \sum _{S(p)}(dI/dx)^2 & \sum _{S(p)}dI/dx dI/dy \\ \sum _{S(p)}dI/dx dI/dy & \sum _{S(p)}(dI/dy)^2 \end{bmatrix}\f] + +where the derivatives are computed using the Sobel operator. + +After that, it finds eigenvectors and eigenvalues of \f$M\f$ and stores them in the destination image as +\f$(\lambda_1, \lambda_2, x_1, y_1, x_2, y_2)\f$ where + +- \f$\lambda_1, \lambda_2\f$ are the non-sorted eigenvalues of \f$M\f$ +- \f$x_1, y_1\f$ are the eigenvectors corresponding to \f$\lambda_1\f$ +- \f$x_2, y_2\f$ are the eigenvectors corresponding to \f$\lambda_2\f$ + +The output of the function can be used for robust edge or corner detection. + +@param src Input single-channel 8-bit or floating-point image. +@param dst Image to store the results. It has the same size as src and the type CV_32FC(6) . +@param blockSize Neighborhood size (see details below). +@param ksize Aperture parameter for the Sobel operator. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + +@sa cornerMinEigenVal, cornerHarris, preCornerDetect + */ +CV_EXPORTS_W void cornerEigenValsAndVecs( InputArray src, OutputArray dst, + int blockSize, int ksize, + int borderType = BORDER_DEFAULT ); + +/** @brief Calculates a feature map for corner detection. + +The function calculates the complex spatial derivative-based function of the source image + +\f[\texttt{dst} = (D_x \texttt{src} )^2 \cdot D_{yy} \texttt{src} + (D_y \texttt{src} )^2 \cdot D_{xx} \texttt{src} - 2 D_x \texttt{src} \cdot D_y \texttt{src} \cdot D_{xy} \texttt{src}\f] + +where \f$D_x\f$,\f$D_y\f$ are the first image derivatives, \f$D_{xx}\f$,\f$D_{yy}\f$ are the second image +derivatives, and \f$D_{xy}\f$ is the mixed derivative. + +The corners can be found as local maximums of the functions, as shown below: +@code + Mat corners, dilated_corners; + preCornerDetect(image, corners, 3); + // dilation with 3x3 rectangular structuring element + dilate(corners, dilated_corners, Mat(), 1); + Mat corner_mask = corners == dilated_corners; +@endcode + +@param src Source single-channel 8-bit of floating-point image. +@param dst Output image that has the type CV_32F and the same size as src . +@param ksize %Aperture size of the Sobel . +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. + */ +CV_EXPORTS_W void preCornerDetect( InputArray src, OutputArray dst, int ksize, + int borderType = BORDER_DEFAULT ); + +/** @brief Refines the corner locations. + +The function iterates to find the sub-pixel accurate location of corners or radial saddle +points as described in @cite forstner1987fast, and as shown on the figure below. + +![image](pics/cornersubpix.png) + +Sub-pixel accurate corner locator is based on the observation that every vector from the center \f$q\f$ +to a point \f$p\f$ located within a neighborhood of \f$q\f$ is orthogonal to the image gradient at \f$p\f$ +subject to image and measurement noise. Consider the expression: + +\f[\epsilon _i = {DI_{p_i}}^T \cdot (q - p_i)\f] + +where \f${DI_{p_i}}\f$ is an image gradient at one of the points \f$p_i\f$ in a neighborhood of \f$q\f$ . The +value of \f$q\f$ is to be found so that \f$\epsilon_i\f$ is minimized. A system of equations may be set up +with \f$\epsilon_i\f$ set to zero: + +\f[\sum _i(DI_{p_i} \cdot {DI_{p_i}}^T) \cdot q - \sum _i(DI_{p_i} \cdot {DI_{p_i}}^T \cdot p_i)\f] + +where the gradients are summed within a neighborhood ("search window") of \f$q\f$ . Calling the first +gradient term \f$G\f$ and the second gradient term \f$b\f$ gives: + +\f[q = G^{-1} \cdot b\f] + +The algorithm sets the center of the neighborhood window at this new center \f$q\f$ and then iterates +until the center stays within a set threshold. + +@param image Input single-channel, 8-bit or float image. +@param corners Initial coordinates of the input corners and refined coordinates provided for +output. +@param winSize Half of the side length of the search window. For example, if winSize=Size(5,5) , +then a \f$(5*2+1) \times (5*2+1) = 11 \times 11\f$ search window is used. +@param zeroZone Half of the size of the dead region in the middle of the search zone over which +the summation in the formula below is not done. It is used sometimes to avoid possible +singularities of the autocorrelation matrix. The value of (-1,-1) indicates that there is no such +a size. +@param criteria Criteria for termination of the iterative process of corner refinement. That is, +the process of corner position refinement stops either after criteria.maxCount iterations or when +the corner position moves by less than criteria.epsilon on some iteration. + */ +CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners, + Size winSize, Size zeroZone, + TermCriteria criteria ); + +/** @brief Determines strong corners on an image. + +The function finds the most prominent corners in the image or in the specified image region, as +described in @cite Shi94 + +- Function calculates the corner quality measure at every source image pixel using the + #cornerMinEigenVal or #cornerHarris . +- Function performs a non-maximum suppression (the local maximums in *3 x 3* neighborhood are + retained). +- The corners with the minimal eigenvalue less than + \f$\texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y)\f$ are rejected. +- The remaining corners are sorted by the quality measure in the descending order. +- Function throws away each corner for which there is a stronger corner at a distance less than + maxDistance. + +The function can be used to initialize a point-based tracker of an object. + +@note If the function is called with different values A and B of the parameter qualityLevel , and +A \> B, the vector of returned corners with qualityLevel=A will be the prefix of the output vector +with qualityLevel=B . + +@param image Input 8-bit or floating-point 32-bit, single-channel image. +@param corners Output vector of detected corners. +@param maxCorners Maximum number of corners to return. If there are more corners than are found, +the strongest of them is returned. `maxCorners <= 0` implies that no limit on the maximum is set +and all detected corners are returned. +@param qualityLevel Parameter characterizing the minimal accepted quality of image corners. The +parameter value is multiplied by the best corner quality measure, which is the minimal eigenvalue +(see #cornerMinEigenVal ) or the Harris function response (see #cornerHarris ). The corners with the +quality measure less than the product are rejected. For example, if the best corner has the +quality measure = 1500, and the qualityLevel=0.01 , then all the corners with the quality measure +less than 15 are rejected. +@param minDistance Minimum possible Euclidean distance between the returned corners. +@param mask Optional region of interest. If the image is not empty (it needs to have the type +CV_8UC1 and the same size as image ), it specifies the region in which the corners are detected. +@param blockSize Size of an average block for computing a derivative covariation matrix over each +pixel neighborhood. See cornerEigenValsAndVecs . +@param useHarrisDetector Parameter indicating whether to use a Harris detector (see #cornerHarris) +or #cornerMinEigenVal. +@param k Free parameter of the Harris detector. + +@sa cornerMinEigenVal, cornerHarris, calcOpticalFlowPyrLK, estimateRigidTransform, + */ + +CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners, + int maxCorners, double qualityLevel, double minDistance, + InputArray mask = noArray(), int blockSize = 3, + bool useHarrisDetector = false, double k = 0.04 ); + +CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners, + int maxCorners, double qualityLevel, double minDistance, + InputArray mask, int blockSize, + int gradientSize, bool useHarrisDetector = false, + double k = 0.04 ); +/** @example samples/cpp/tutorial_code/ImgTrans/houghlines.cpp +An example using the Hough line detector +![Sample input image](Hough_Lines_Tutorial_Original_Image.jpg) ![Output image](Hough_Lines_Tutorial_Result.jpg) +*/ + +/** @brief Finds lines in a binary image using the standard Hough transform. + +The function implements the standard or standard multi-scale Hough transform algorithm for line +detection. See for a good explanation of Hough +transform. + +@param image 8-bit, single-channel binary source image. The image may be modified by the function. +@param lines Output vector of lines. Each line is represented by a 2 or 3 element vector +\f$(\rho, \theta)\f$ or \f$(\rho, \theta, \textrm{votes})\f$ . \f$\rho\f$ is the distance from the coordinate origin \f$(0,0)\f$ (top-left corner of +the image). \f$\theta\f$ is the line rotation angle in radians ( +\f$0 \sim \textrm{vertical line}, \pi/2 \sim \textrm{horizontal line}\f$ ). +\f$\textrm{votes}\f$ is the value of accumulator. +@param rho Distance resolution of the accumulator in pixels. +@param theta Angle resolution of the accumulator in radians. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ). +@param srn For the multi-scale Hough transform, it is a divisor for the distance resolution rho . +The coarse accumulator distance resolution is rho and the accurate accumulator resolution is +rho/srn . If both srn=0 and stn=0 , the classical Hough transform is used. Otherwise, both these +parameters should be positive. +@param stn For the multi-scale Hough transform, it is a divisor for the distance resolution theta. +@param min_theta For standard and multi-scale Hough transform, minimum angle to check for lines. +Must fall between 0 and max_theta. +@param max_theta For standard and multi-scale Hough transform, maximum angle to check for lines. +Must fall between min_theta and CV_PI. + */ +CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, + double rho, double theta, int threshold, + double srn = 0, double stn = 0, + double min_theta = 0, double max_theta = CV_PI ); + +/** @brief Finds line segments in a binary image using the probabilistic Hough transform. + +The function implements the probabilistic Hough transform algorithm for line detection, described +in @cite Matas00 + +See the line detection example below: +@include snippets/imgproc_HoughLinesP.cpp +This is a sample picture the function parameters have been tuned for: + +![image](pics/building.jpg) + +And this is the output of the above program in case of the probabilistic Hough transform: + +![image](pics/houghp.png) + +@param image 8-bit, single-channel binary source image. The image may be modified by the function. +@param lines Output vector of lines. Each line is represented by a 4-element vector +\f$(x_1, y_1, x_2, y_2)\f$ , where \f$(x_1,y_1)\f$ and \f$(x_2, y_2)\f$ are the ending points of each detected +line segment. +@param rho Distance resolution of the accumulator in pixels. +@param theta Angle resolution of the accumulator in radians. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ). +@param minLineLength Minimum line length. Line segments shorter than that are rejected. +@param maxLineGap Maximum allowed gap between points on the same line to link them. + +@sa LineSegmentDetector + */ +CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines, + double rho, double theta, int threshold, + double minLineLength = 0, double maxLineGap = 0 ); + +/** @brief Finds lines in a set of points using the standard Hough transform. + +The function finds lines in a set of points using a modification of the Hough transform. +@include snippets/imgproc_HoughLinesPointSet.cpp +@param _point Input vector of points. Each vector must be encoded as a Point vector \f$(x,y)\f$. Type must be CV_32FC2 or CV_32SC2. +@param _lines Output vector of found lines. Each vector is encoded as a vector \f$(votes, rho, theta)\f$. +The larger the value of 'votes', the higher the reliability of the Hough line. +@param lines_max Max count of hough lines. +@param threshold Accumulator threshold parameter. Only those lines are returned that get enough +votes ( \f$>\texttt{threshold}\f$ ) +@param min_rho Minimum Distance value of the accumulator in pixels. +@param max_rho Maximum Distance value of the accumulator in pixels. +@param rho_step Distance resolution of the accumulator in pixels. +@param min_theta Minimum angle value of the accumulator in radians. +@param max_theta Maximum angle value of the accumulator in radians. +@param theta_step Angle resolution of the accumulator in radians. + */ +CV_EXPORTS_W void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, int threshold, + double min_rho, double max_rho, double rho_step, + double min_theta, double max_theta, double theta_step ); + +/** @example samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp +An example using the Hough circle detector +*/ + +/** @brief Finds circles in a grayscale image using the Hough transform. + +The function finds circles in a grayscale image using a modification of the Hough transform. + +Example: : +@include snippets/imgproc_HoughLinesCircles.cpp + +@note Usually the function detects the centers of circles well. However, it may fail to find correct +radii. You can assist to the function by specifying the radius range ( minRadius and maxRadius ) if +you know it. Or, you may set maxRadius to a negative number to return centers only without radius +search, and find the correct radius using an additional procedure. + +@param image 8-bit, single-channel, grayscale input image. +@param circles Output vector of found circles. Each vector is encoded as 3 or 4 element +floating-point vector \f$(x, y, radius)\f$ or \f$(x, y, radius, votes)\f$ . +@param method Detection method, see #HoughModes. Currently, the only implemented method is #HOUGH_GRADIENT +@param dp Inverse ratio of the accumulator resolution to the image resolution. For example, if +dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has +half as big width and height. +@param minDist Minimum distance between the centers of the detected circles. If the parameter is +too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is +too large, some circles may be missed. +@param param1 First method-specific parameter. In case of #HOUGH_GRADIENT , it is the higher +threshold of the two passed to the Canny edge detector (the lower one is twice smaller). +@param param2 Second method-specific parameter. In case of #HOUGH_GRADIENT , it is the +accumulator threshold for the circle centers at the detection stage. The smaller it is, the more +false circles may be detected. Circles, corresponding to the larger accumulator values, will be +returned first. +@param minRadius Minimum circle radius. +@param maxRadius Maximum circle radius. If <= 0, uses the maximum image dimension. If < 0, returns +centers without finding the radius. + +@sa fitEllipse, minEnclosingCircle + */ +CV_EXPORTS_W void HoughCircles( InputArray image, OutputArray circles, + int method, double dp, double minDist, + double param1 = 100, double param2 = 100, + int minRadius = 0, int maxRadius = 0 ); + +//! @} imgproc_feature + +//! @addtogroup imgproc_filter +//! @{ + +/** @example samples/cpp/tutorial_code/ImgProc/Morphology_2.cpp +Advanced morphology Transformations sample code +![Sample screenshot](Morphology_2_Tutorial_Result.jpg) +Check @ref tutorial_opening_closing_hats "the corresponding tutorial" for more details +*/ + +/** @brief Erodes an image by using a specific structuring element. + +The function erodes the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the minimum is taken: + +\f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +The function supports the in-place mode. Erosion can be applied several ( iterations ) times. In +case of multi-channel images, each channel is processed independently. + +@param src input image; the number of channels can be arbitrary, but the depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param kernel structuring element used for erosion; if `element=Mat()`, a `3 x 3` rectangular +structuring element is used. Kernel can be created using #getStructuringElement. +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times erosion is applied. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@param borderValue border value in case of a constant border +@sa dilate, morphologyEx, getStructuringElement + */ +CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +/** @example samples/cpp/tutorial_code/ImgProc/Morphology_1.cpp +Erosion and Dilation sample code +![Sample Screenshot-Erosion](Morphology_1_Tutorial_Erosion_Result.jpg)![Sample Screenshot-Dilation](Morphology_1_Tutorial_Dilation_Result.jpg) +Check @ref tutorial_erosion_dilatation "the corresponding tutorial" for more details +*/ + +/** @brief Dilates an image by using a specific structuring element. + +The function dilates the source image using the specified structuring element that determines the +shape of a pixel neighborhood over which the maximum is taken: +\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f] + +The function supports the in-place mode. Dilation can be applied several ( iterations ) times. In +case of multi-channel images, each channel is processed independently. + +@param src input image; the number of channels can be arbitrary, but the depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst output image of the same size and type as src. +@param kernel structuring element used for dilation; if elemenat=Mat(), a 3 x 3 rectangular +structuring element is used. Kernel can be created using #getStructuringElement +@param anchor position of the anchor within the element; default value (-1, -1) means that the +anchor is at the element center. +@param iterations number of times dilation is applied. +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not suported. +@param borderValue border value in case of a constant border +@sa erode, morphologyEx, getStructuringElement + */ +CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +/** @brief Performs advanced morphological transformations. + +The function cv::morphologyEx can perform advanced morphological transformations using an erosion and dilation as +basic operations. + +Any of the operations can be done in-place. In case of multi-channel images, each channel is +processed independently. + +@param src Source image. The number of channels can be arbitrary. The depth should be one of +CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. +@param dst Destination image of the same size and type as source image. +@param op Type of a morphological operation, see #MorphTypes +@param kernel Structuring element. It can be created using #getStructuringElement. +@param anchor Anchor position with the kernel. Negative values mean that the anchor is at the +kernel center. +@param iterations Number of times erosion and dilation are applied. +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. +@param borderValue Border value in case of a constant border. The default value has a special +meaning. +@sa dilate, erode, getStructuringElement +@note The number of iterations is the number of times erosion or dilatation operation will be applied. +For instance, an opening operation (#MORPH_OPEN) with two iterations is equivalent to apply +successively: erode -> erode -> dilate -> dilate (and not erode -> dilate -> erode -> dilate). + */ +CV_EXPORTS_W void morphologyEx( InputArray src, OutputArray dst, + int op, InputArray kernel, + Point anchor = Point(-1,-1), int iterations = 1, + int borderType = BORDER_CONSTANT, + const Scalar& borderValue = morphologyDefaultBorderValue() ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +/** @brief Resizes an image. + +The function resize resizes the image src down to or up to the specified size. Note that the +initial dst type or size are not taken into account. Instead, the size and type are derived from +the `src`,`dsize`,`fx`, and `fy`. If you want to resize src so that it fits the pre-created dst, +you may call the function as follows: +@code + // explicitly specify dsize=dst.size(); fx and fy will be computed from that. + resize(src, dst, dst.size(), 0, 0, interpolation); +@endcode +If you want to decimate the image by factor of 2 in each direction, you can call the function this +way: +@code + // specify fx and fy and let the function compute the destination image size. + resize(src, dst, Size(), 0.5, 0.5, interpolation); +@endcode +To shrink an image, it will generally look best with #INTER_AREA interpolation, whereas to +enlarge an image, it will generally look best with c#INTER_CUBIC (slow) or #INTER_LINEAR +(faster but still looks OK). + +@param src input image. +@param dst output image; it has the size dsize (when it is non-zero) or the size computed from +src.size(), fx, and fy; the type of dst is the same as of src. +@param dsize output image size; if it equals zero, it is computed as: + \f[\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}\f] + Either dsize or both fx and fy must be non-zero. +@param fx scale factor along the horizontal axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.width/src.cols}\f] +@param fy scale factor along the vertical axis; when it equals 0, it is computed as +\f[\texttt{(double)dsize.height/src.rows}\f] +@param interpolation interpolation method, see #InterpolationFlags + +@sa warpAffine, warpPerspective, remap + */ +CV_EXPORTS_W void resize( InputArray src, OutputArray dst, + Size dsize, double fx = 0, double fy = 0, + int interpolation = INTER_LINEAR ); + +/** @brief Applies an affine transformation to an image. + +The function warpAffine transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23})\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted +with #invertAffineTransform and then put in the formula above instead of M. The function cannot +operate in-place. + +@param src input image. +@param dst output image that has the size dsize and the same type as src . +@param M \f$2\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (see #InterpolationFlags) and the optional +flag #WARP_INVERSE_MAP that means that M is the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (see #BorderTypes); when +borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image corresponding to +the "outliers" in the source image are not modified by the function. +@param borderValue value used in case of a constant border; by default, it is 0. + +@sa warpPerspective, resize, remap, getRectSubPix, transform + */ +CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst, + InputArray M, Size dsize, + int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @example samples/cpp/warpPerspective_demo.cpp +An example program shows using cv::getPerspectiveTransform and cv::warpPerspective for image warping +*/ + +/** @brief Applies a perspective transformation to an image. + +The function warpPerspective transforms the source image using the specified matrix: + +\f[\texttt{dst} (x,y) = \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , + \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right )\f] + +when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted with invert +and then put in the formula above instead of M. The function cannot operate in-place. + +@param src input image. +@param dst output image that has the size dsize and the same type as src . +@param M \f$3\times 3\f$ transformation matrix. +@param dsize size of the output image. +@param flags combination of interpolation methods (#INTER_LINEAR or #INTER_NEAREST) and the +optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation ( +\f$\texttt{dst}\rightarrow\texttt{src}\f$ ). +@param borderMode pixel extrapolation method (#BORDER_CONSTANT or #BORDER_REPLICATE). +@param borderValue value used in case of a constant border; by default, it equals 0. + +@sa warpAffine, resize, remap, getRectSubPix, perspectiveTransform + */ +CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst, + InputArray M, Size dsize, + int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @brief Applies a generic geometrical transformation to an image. + +The function remap transforms the source image using the specified map: + +\f[\texttt{dst} (x,y) = \texttt{src} (map_x(x,y),map_y(x,y))\f] + +where values of pixels with non-integer coordinates are computed using one of available +interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps +in \f$map_1\f$ and \f$map_2\f$ respectively, or interleaved floating-point maps of \f$(x,y)\f$ in +\f$map_1\f$, or fixed-point maps created by using convertMaps. The reason you might want to +convert from floating to fixed-point representations of a map is that they can yield much faster +(\~2x) remapping operations. In the converted case, \f$map_1\f$ contains pairs (cvFloor(x), +cvFloor(y)) and \f$map_2\f$ contains indices in a table of interpolation coefficients. + +This function cannot operate in-place. + +@param src Source image. +@param dst Destination image. It has the same size as map1 and the same type as src . +@param map1 The first map of either (x,y) points or just x values having the type CV_16SC2 , +CV_32FC1, or CV_32FC2. See convertMaps for details on converting a floating point +representation to fixed-point for speed. +@param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map +if map1 is (x,y) points), respectively. +@param interpolation Interpolation method (see #InterpolationFlags). The methods #INTER_AREA +and #INTER_LINEAR_EXACT are not supported by this function. +@param borderMode Pixel extrapolation method (see #BorderTypes). When +borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image that +corresponds to the "outliers" in the source image are not modified by the function. +@param borderValue Value used in case of a constant border. By default, it is 0. +@note +Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + */ +CV_EXPORTS_W void remap( InputArray src, OutputArray dst, + InputArray map1, InputArray map2, + int interpolation, int borderMode = BORDER_CONSTANT, + const Scalar& borderValue = Scalar()); + +/** @brief Converts image transformation maps from one representation to another. + +The function converts a pair of maps for remap from one representation to another. The following +options ( (map1.type(), map2.type()) \f$\rightarrow\f$ (dstmap1.type(), dstmap2.type()) ) are +supported: + +- \f$\texttt{(CV_32FC1, CV_32FC1)} \rightarrow \texttt{(CV_16SC2, CV_16UC1)}\f$. This is the +most frequently used conversion operation, in which the original floating-point maps (see remap ) +are converted to a more compact and much faster fixed-point representation. The first output array +contains the rounded coordinates and the second array (created only when nninterpolation=false ) +contains indices in the interpolation tables. + +- \f$\texttt{(CV_32FC2)} \rightarrow \texttt{(CV_16SC2, CV_16UC1)}\f$. The same as above but +the original maps are stored in one 2-channel matrix. + +- Reverse conversion. Obviously, the reconstructed floating-point maps will not be exactly the same +as the originals. + +@param map1 The first input map of type CV_16SC2, CV_32FC1, or CV_32FC2 . +@param map2 The second input map of type CV_16UC1, CV_32FC1, or none (empty matrix), +respectively. +@param dstmap1 The first output map that has the type dstmap1type and the same size as src . +@param dstmap2 The second output map. +@param dstmap1type Type of the first output map that should be CV_16SC2, CV_32FC1, or +CV_32FC2 . +@param nninterpolation Flag indicating whether the fixed-point maps are used for the +nearest-neighbor or for a more complex interpolation. + +@sa remap, undistort, initUndistortRectifyMap + */ +CV_EXPORTS_W void convertMaps( InputArray map1, InputArray map2, + OutputArray dstmap1, OutputArray dstmap2, + int dstmap1type, bool nninterpolation = false ); + +/** @brief Calculates an affine matrix of 2D rotation. + +The function calculates the following matrix: + +\f[\begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot \texttt{center.x} - \beta \cdot \texttt{center.y} \\ - \beta & \alpha & \beta \cdot \texttt{center.x} + (1- \alpha ) \cdot \texttt{center.y} \end{bmatrix}\f] + +where + +\f[\begin{array}{l} \alpha = \texttt{scale} \cdot \cos \texttt{angle} , \\ \beta = \texttt{scale} \cdot \sin \texttt{angle} \end{array}\f] + +The transformation maps the rotation center to itself. If this is not the target, adjust the shift. + +@param center Center of the rotation in the source image. +@param angle Rotation angle in degrees. Positive values mean counter-clockwise rotation (the +coordinate origin is assumed to be the top-left corner). +@param scale Isotropic scale factor. + +@sa getAffineTransform, warpAffine, transform + */ +CV_EXPORTS_W Mat getRotationMatrix2D( Point2f center, double angle, double scale ); + +//! returns 3x3 perspective transformation for the corresponding 4 point pairs. +CV_EXPORTS Mat getPerspectiveTransform( const Point2f src[], const Point2f dst[] ); + +/** @brief Calculates an affine transform from three pairs of the corresponding points. + +The function calculates the \f$2 \times 3\f$ matrix of an affine transform so that: + +\f[\begin{bmatrix} x'_i \\ y'_i \end{bmatrix} = \texttt{map_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\f] + +where + +\f[dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2\f] + +@param src Coordinates of triangle vertices in the source image. +@param dst Coordinates of the corresponding triangle vertices in the destination image. + +@sa warpAffine, transform + */ +CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] ); + +/** @brief Inverts an affine transformation. + +The function computes an inverse affine transformation represented by \f$2 \times 3\f$ matrix M: + +\f[\begin{bmatrix} a_{11} & a_{12} & b_1 \\ a_{21} & a_{22} & b_2 \end{bmatrix}\f] + +The result is also a \f$2 \times 3\f$ matrix of the same type as M. + +@param M Original affine transformation. +@param iM Output reverse affine transformation. + */ +CV_EXPORTS_W void invertAffineTransform( InputArray M, OutputArray iM ); + +/** @brief Calculates a perspective transform from four pairs of the corresponding points. + +The function calculates the \f$3 \times 3\f$ matrix of a perspective transform so that: + +\f[\begin{bmatrix} t_i x'_i \\ t_i y'_i \\ t_i \end{bmatrix} = \texttt{map_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\f] + +where + +\f[dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2,3\f] + +@param src Coordinates of quadrangle vertices in the source image. +@param dst Coordinates of the corresponding quadrangle vertices in the destination image. + +@sa findHomography, warpPerspective, perspectiveTransform + */ +CV_EXPORTS_W Mat getPerspectiveTransform( InputArray src, InputArray dst ); + +CV_EXPORTS_W Mat getAffineTransform( InputArray src, InputArray dst ); + +/** @brief Retrieves a pixel rectangle from an image with sub-pixel accuracy. + +The function getRectSubPix extracts pixels from src: + +\f[patch(x, y) = src(x + \texttt{center.x} - ( \texttt{dst.cols} -1)*0.5, y + \texttt{center.y} - ( \texttt{dst.rows} -1)*0.5)\f] + +where the values of the pixels at non-integer coordinates are retrieved using bilinear +interpolation. Every channel of multi-channel images is processed independently. Also +the image should be a single channel or three channel image. While the center of the +rectangle must be inside the image, parts of the rectangle may be outside. + +@param image Source image. +@param patchSize Size of the extracted patch. +@param center Floating point coordinates of the center of the extracted rectangle within the +source image. The center must be inside the image. +@param patch Extracted patch that has the size patchSize and the same number of channels as src . +@param patchType Depth of the extracted pixels. By default, they have the same depth as src . + +@sa warpAffine, warpPerspective + */ +CV_EXPORTS_W void getRectSubPix( InputArray image, Size patchSize, + Point2f center, OutputArray patch, int patchType = -1 ); + +/** @example samples/cpp/polar_transforms.cpp +An example using the cv::linearPolar and cv::logPolar operations +*/ + +/** @brief Remaps an image to semilog-polar coordinates space. + +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags+WARP_POLAR_LOG); + +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image d)"): +\f[\begin{array}{l} + dst( \rho , \phi ) = src(x,y) \\ + dst.size() \leftarrow src.size() +\end{array}\f] + +where +\f[\begin{array}{l} + I = (dx,dy) = (x - center.x,y - center.y) \\ + \rho = M \cdot log_e(\texttt{magnitude} (I)) ,\\ + \phi = Kangle \cdot \texttt{angle} (I) \\ +\end{array}\f] + +and +\f[\begin{array}{l} + M = src.cols / log_e(maxRadius) \\ + Kangle = src.rows / 2\Pi \\ +\end{array}\f] + +The function emulates the human "foveal" vision and can be used for fast scale and +rotation-invariant template matching, for object tracking and so forth. +@param src Source image +@param dst Destination image. It will have same size and type as src. +@param center The transformation center; where the output precision is maximal +@param M Magnitude scale parameter. It determines the radius of the bounding circle to transform too. +@param flags A combination of interpolation methods, see #InterpolationFlags + +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. + +@sa cv::linearPolar +@endinternal +*/ +CV_EXPORTS_W void logPolar( InputArray src, OutputArray dst, + Point2f center, double M, int flags ); + +/** @brief Remaps an image to polar coordinates space. + +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags) + +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image c)"): +\f[\begin{array}{l} + dst( \rho , \phi ) = src(x,y) \\ + dst.size() \leftarrow src.size() +\end{array}\f] + +where +\f[\begin{array}{l} + I = (dx,dy) = (x - center.x,y - center.y) \\ + \rho = Kmag \cdot \texttt{magnitude} (I) ,\\ + \phi = angle \cdot \texttt{angle} (I) +\end{array}\f] + +and +\f[\begin{array}{l} + Kx = src.cols / maxRadius \\ + Ky = src.rows / 2\Pi +\end{array}\f] + + +@param src Source image +@param dst Destination image. It will have same size and type as src. +@param center The transformation center; +@param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. +@param flags A combination of interpolation methods, see #InterpolationFlags + +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. + +@sa cv::logPolar +@endinternal +*/ +CV_EXPORTS_W void linearPolar( InputArray src, OutputArray dst, + Point2f center, double maxRadius, int flags ); + + +/** \brief Remaps an image to polar or semilog-polar coordinates space + +@anchor polar_remaps_reference_image +![Polar remaps reference](pics/polar_remap_doc.png) + +Transform the source image using the following transformation: +\f[ +dst(\rho , \phi ) = src(x,y) +\f] + +where +\f[ +\begin{array}{l} +\vec{I} = (x - center.x, \;y - center.y) \\ +\phi = Kangle \cdot \texttt{angle} (\vec{I}) \\ +\rho = \left\{\begin{matrix} +Klin \cdot \texttt{magnitude} (\vec{I}) & default \\ +Klog \cdot log_e(\texttt{magnitude} (\vec{I})) & if \; semilog \\ +\end{matrix}\right. +\end{array} +\f] + +and +\f[ +\begin{array}{l} +Kangle = dsize.height / 2\Pi \\ +Klin = dsize.width / maxRadius \\ +Klog = dsize.width / log_e(maxRadius) \\ +\end{array} +\f] + + +\par Linear vs semilog mapping + +Polar mapping can be linear or semi-log. Add one of #WarpPolarMode to `flags` to specify the polar mapping mode. + +Linear is the default mode. + +The semilog mapping emulates the human "foveal" vision that permit very high acuity on the line of sight (central vision) +in contrast to peripheral vision where acuity is minor. + +\par Option on `dsize`: + +- if both values in `dsize <=0 ` (default), +the destination image will have (almost) same area of source bounding circle: +\f[\begin{array}{l} +dsize.area \leftarrow (maxRadius^2 \cdot \Pi) \\ +dsize.width = \texttt{cvRound}(maxRadius) \\ +dsize.height = \texttt{cvRound}(maxRadius \cdot \Pi) \\ +\end{array}\f] + + +- if only `dsize.height <= 0`, +the destination image area will be proportional to the bounding circle area but scaled by `Kx * Kx`: +\f[\begin{array}{l} +dsize.height = \texttt{cvRound}(dsize.width \cdot \Pi) \\ +\end{array} +\f] + +- if both values in `dsize > 0 `, +the destination image will have the given size therefore the area of the bounding circle will be scaled to `dsize`. + + +\par Reverse mapping + +You can get reverse mapping adding #WARP_INVERSE_MAP to `flags` +\snippet polar_transforms.cpp InverseMap + +In addiction, to calculate the original coordinate from a polar mapped coordinate \f$(rho, phi)->(x, y)\f$: +\snippet polar_transforms.cpp InverseCoordinate + +@param src Source image. +@param dst Destination image. It will have same type as src. +@param dsize The destination image size (see description for valid options). +@param center The transformation center. +@param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. +@param flags A combination of interpolation methods, #InterpolationFlags + #WarpPolarMode. + - Add #WARP_POLAR_LINEAR to select linear polar mapping (default) + - Add #WARP_POLAR_LOG to select semilog polar mapping + - Add #WARP_INVERSE_MAP for reverse mapping. +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. +- This function uses #remap. Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + +@sa cv::remap +*/ +CV_EXPORTS_W void warpPolar(InputArray src, OutputArray dst, Size dsize, + Point2f center, double maxRadius, int flags); + + +//! @} imgproc_transform + +//! @addtogroup imgproc_misc +//! @{ + +/** @overload */ +CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = -1 ); + +/** @overload */ +CV_EXPORTS_AS(integral2) void integral( InputArray src, OutputArray sum, + OutputArray sqsum, int sdepth = -1, int sqdepth = -1 ); + +/** @brief Calculates the integral of an image. + +The function calculates one or more integral images for the source image as follows: + +\f[\texttt{sum} (X,Y) = \sum _{x + +Calculates the cross-power spectrum of two supplied source arrays. The arrays are padded if needed +with getOptimalDFTSize. + +The function performs the following equations: +- First it applies a Hanning window (see ) to each +image to remove possible edge effects. This window is cached until the array size changes to speed +up processing time. +- Next it computes the forward DFTs of each source array: +\f[\mathbf{G}_a = \mathcal{F}\{src_1\}, \; \mathbf{G}_b = \mathcal{F}\{src_2\}\f] +where \f$\mathcal{F}\f$ is the forward DFT. +- It then computes the cross-power spectrum of each frequency domain array: +\f[R = \frac{ \mathbf{G}_a \mathbf{G}_b^*}{|\mathbf{G}_a \mathbf{G}_b^*|}\f] +- Next the cross-correlation is converted back into the time domain via the inverse DFT: +\f[r = \mathcal{F}^{-1}\{R\}\f] +- Finally, it computes the peak location and computes a 5x5 weighted centroid around the peak to +achieve sub-pixel accuracy. +\f[(\Delta x, \Delta y) = \texttt{weightedCentroid} \{\arg \max_{(x, y)}\{r\}\}\f] +- If non-zero, the response parameter is computed as the sum of the elements of r within the 5x5 +centroid around the peak location. It is normalized to a maximum of 1 (meaning there is a single +peak) and will be smaller when there are multiple peaks. + +@param src1 Source floating point array (CV_32FC1 or CV_64FC1) +@param src2 Source floating point array (CV_32FC1 or CV_64FC1) +@param window Floating point array with windowing coefficients to reduce edge effects (optional). +@param response Signal power within the 5x5 centroid around the peak, between 0 and 1 (optional). +@returns detected phase shift (sub-pixel) between the two arrays. + +@sa dft, getOptimalDFTSize, idft, mulSpectrums createHanningWindow + */ +CV_EXPORTS_W Point2d phaseCorrelate(InputArray src1, InputArray src2, + InputArray window = noArray(), CV_OUT double* response = 0); + +/** @brief This function computes a Hanning window coefficients in two dimensions. + +See (http://en.wikipedia.org/wiki/Hann_function) and (http://en.wikipedia.org/wiki/Window_function) +for more information. + +An example is shown below: +@code + // create hanning window of size 100x100 and type CV_32F + Mat hann; + createHanningWindow(hann, Size(100, 100), CV_32F); +@endcode +@param dst Destination array to place Hann coefficients in +@param winSize The window size specifications (both width and height must be > 1) +@param type Created array type + */ +CV_EXPORTS_W void createHanningWindow(OutputArray dst, Size winSize, int type); + +//! @} imgproc_motion + +//! @addtogroup imgproc_misc +//! @{ + +/** @brief Applies a fixed-level threshold to each array element. + +The function applies fixed-level thresholding to a multiple-channel array. The function is typically +used to get a bi-level (binary) image out of a grayscale image ( #compare could be also used for +this purpose) or for removing a noise, that is, filtering out pixels with too small or too large +values. There are several types of thresholding supported by the function. They are determined by +type parameter. + +Also, the special values #THRESH_OTSU or #THRESH_TRIANGLE may be combined with one of the +above values. In these cases, the function determines the optimal threshold value using the Otsu's +or Triangle algorithm and uses it instead of the specified thresh. + +@note Currently, the Otsu's and Triangle methods are implemented only for 8-bit single-channel images. + +@param src input array (multiple-channel, 8-bit or 32-bit floating point). +@param dst output array of the same size and type and the same number of channels as src. +@param thresh threshold value. +@param maxval maximum value to use with the #THRESH_BINARY and #THRESH_BINARY_INV thresholding +types. +@param type thresholding type (see #ThresholdTypes). +@return the computed threshold value if Otsu's or Triangle methods used. + +@sa adaptiveThreshold, findContours, compare, min, max + */ +CV_EXPORTS_W double threshold( InputArray src, OutputArray dst, + double thresh, double maxval, int type ); + + +/** @brief Applies an adaptive threshold to an array. + +The function transforms a grayscale image to a binary image according to the formulae: +- **THRESH_BINARY** + \f[dst(x,y) = \fork{\texttt{maxValue}}{if \(src(x,y) > T(x,y)\)}{0}{otherwise}\f] +- **THRESH_BINARY_INV** + \f[dst(x,y) = \fork{0}{if \(src(x,y) > T(x,y)\)}{\texttt{maxValue}}{otherwise}\f] +where \f$T(x,y)\f$ is a threshold calculated individually for each pixel (see adaptiveMethod parameter). + +The function can process the image in-place. + +@param src Source 8-bit single-channel image. +@param dst Destination image of the same size and the same type as src. +@param maxValue Non-zero value assigned to the pixels for which the condition is satisfied +@param adaptiveMethod Adaptive thresholding algorithm to use, see #AdaptiveThresholdTypes. +The #BORDER_REPLICATE | #BORDER_ISOLATED is used to process boundaries. +@param thresholdType Thresholding type that must be either #THRESH_BINARY or #THRESH_BINARY_INV, +see #ThresholdTypes. +@param blockSize Size of a pixel neighborhood that is used to calculate a threshold value for the +pixel: 3, 5, 7, and so on. +@param C Constant subtracted from the mean or weighted mean (see the details below). Normally, it +is positive but may be zero or negative as well. + +@sa threshold, blur, GaussianBlur + */ +CV_EXPORTS_W void adaptiveThreshold( InputArray src, OutputArray dst, + double maxValue, int adaptiveMethod, + int thresholdType, int blockSize, double C ); + +//! @} imgproc_misc + +//! @addtogroup imgproc_filter +//! @{ + +/** @example samples/cpp/tutorial_code/ImgProc/Pyramids/Pyramids.cpp +An example using pyrDown and pyrUp functions +*/ + +/** @brief Blurs an image and downsamples it. + +By default, size of the output image is computed as `Size((src.cols+1)/2, (src.rows+1)/2)`, but in +any case, the following conditions should be satisfied: + +\f[\begin{array}{l} | \texttt{dstsize.width} *2-src.cols| \leq 2 \\ | \texttt{dstsize.height} *2-src.rows| \leq 2 \end{array}\f] + +The function performs the downsampling step of the Gaussian pyramid construction. First, it +convolves the source image with the kernel: + +\f[\frac{1}{256} \begin{bmatrix} 1 & 4 & 6 & 4 & 1 \\ 4 & 16 & 24 & 16 & 4 \\ 6 & 24 & 36 & 24 & 6 \\ 4 & 16 & 24 & 16 & 4 \\ 1 & 4 & 6 & 4 & 1 \end{bmatrix}\f] + +Then, it downsamples the image by rejecting even rows and columns. + +@param src input image. +@param dst output image; it has the specified size and the same type as src. +@param dstsize size of the output image. +@param borderType Pixel extrapolation method, see #BorderTypes (#BORDER_CONSTANT isn't supported) + */ +CV_EXPORTS_W void pyrDown( InputArray src, OutputArray dst, + const Size& dstsize = Size(), int borderType = BORDER_DEFAULT ); + +/** @brief Upsamples an image and then blurs it. + +By default, size of the output image is computed as `Size(src.cols\*2, (src.rows\*2)`, but in any +case, the following conditions should be satisfied: + +\f[\begin{array}{l} | \texttt{dstsize.width} -src.cols*2| \leq ( \texttt{dstsize.width} \mod 2) \\ | \texttt{dstsize.height} -src.rows*2| \leq ( \texttt{dstsize.height} \mod 2) \end{array}\f] + +The function performs the upsampling step of the Gaussian pyramid construction, though it can +actually be used to construct the Laplacian pyramid. First, it upsamples the source image by +injecting even zero rows and columns and then convolves the result with the same kernel as in +pyrDown multiplied by 4. + +@param src input image. +@param dst output image. It has the specified size and the same type as src . +@param dstsize size of the output image. +@param borderType Pixel extrapolation method, see #BorderTypes (only #BORDER_DEFAULT is supported) + */ +CV_EXPORTS_W void pyrUp( InputArray src, OutputArray dst, + const Size& dstsize = Size(), int borderType = BORDER_DEFAULT ); + +/** @brief Constructs the Gaussian pyramid for an image. + +The function constructs a vector of images and builds the Gaussian pyramid by recursively applying +pyrDown to the previously built pyramid layers, starting from `dst[0]==src`. + +@param src Source image. Check pyrDown for the list of supported types. +@param dst Destination vector of maxlevel+1 images of the same type as src. dst[0] will be the +same as src. dst[1] is the next pyramid layer, a smoothed and down-sized src, and so on. +@param maxlevel 0-based index of the last (the smallest) pyramid layer. It must be non-negative. +@param borderType Pixel extrapolation method, see #BorderTypes (#BORDER_CONSTANT isn't supported) + */ +CV_EXPORTS void buildPyramid( InputArray src, OutputArrayOfArrays dst, + int maxlevel, int borderType = BORDER_DEFAULT ); + +//! @} imgproc_filter + +//! @addtogroup imgproc_transform +//! @{ + +/** @brief Transforms an image to compensate for lens distortion. + +The function transforms an image to compensate radial and tangential lens distortion. + +The function is simply a combination of #initUndistortRectifyMap (with unity R ) and #remap +(with bilinear interpolation). See the former function for details of the transformation being +performed. + +Those pixels in the destination image, for which there is no correspondent pixels in the source +image, are filled with zeros (black color). + +A particular subset of the source image that will be visible in the corrected image can be regulated +by newCameraMatrix. You can use #getOptimalNewCameraMatrix to compute the appropriate +newCameraMatrix depending on your requirements. + +The camera matrix and the distortion parameters can be determined using #calibrateCamera. If +the resolution of images is different from the resolution used at the calibration stage, \f$f_x, +f_y, c_x\f$ and \f$c_y\f$ need to be scaled accordingly, while the distortion coefficients remain +the same. + +@param src Input (distorted) image. +@param dst Output (corrected) image that has the same size and type as src . +@param cameraMatrix Input camera matrix \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param newCameraMatrix Camera matrix of the distorted image. By default, it is the same as +cameraMatrix but you may additionally scale and shift the result by using a different matrix. + */ +CV_EXPORTS_W void undistort( InputArray src, OutputArray dst, + InputArray cameraMatrix, + InputArray distCoeffs, + InputArray newCameraMatrix = noArray() ); + +/** @brief Computes the undistortion and rectification transformation map. + +The function computes the joint undistortion and rectification transformation and represents the +result in the form of maps for remap. The undistorted image looks like original, as if it is +captured with a camera using the camera matrix =newCameraMatrix and zero distortion. In case of a +monocular camera, newCameraMatrix is usually equal to cameraMatrix, or it can be computed by +#getOptimalNewCameraMatrix for a better control over scaling. In case of a stereo camera, +newCameraMatrix is normally set to P1 or P2 computed by #stereoRectify . + +Also, this new camera is oriented differently in the coordinate space, according to R. That, for +example, helps to align two heads of a stereo camera so that the epipolar lines on both images +become horizontal and have the same y- coordinate (in case of a horizontally aligned stereo camera). + +The function actually builds the maps for the inverse mapping algorithm that is used by remap. That +is, for each pixel \f$(u, v)\f$ in the destination (corrected and rectified) image, the function +computes the corresponding coordinates in the source image (that is, in the original image from +camera). The following process is applied: +\f[ +\begin{array}{l} +x \leftarrow (u - {c'}_x)/{f'}_x \\ +y \leftarrow (v - {c'}_y)/{f'}_y \\ +{[X\,Y\,W]} ^T \leftarrow R^{-1}*[x \, y \, 1]^T \\ +x' \leftarrow X/W \\ +y' \leftarrow Y/W \\ +r^2 \leftarrow x'^2 + y'^2 \\ +x'' \leftarrow x' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} ++ 2p_1 x' y' + p_2(r^2 + 2 x'^2) + s_1 r^2 + s_2 r^4\\ +y'' \leftarrow y' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} ++ p_1 (r^2 + 2 y'^2) + 2 p_2 x' y' + s_3 r^2 + s_4 r^4 \\ +s\vecthree{x'''}{y'''}{1} = +\vecthreethree{R_{33}(\tau_x, \tau_y)}{0}{-R_{13}((\tau_x, \tau_y)} +{0}{R_{33}(\tau_x, \tau_y)}{-R_{23}(\tau_x, \tau_y)} +{0}{0}{1} R(\tau_x, \tau_y) \vecthree{x''}{y''}{1}\\ +map_x(u,v) \leftarrow x''' f_x + c_x \\ +map_y(u,v) \leftarrow y''' f_y + c_y +\end{array} +\f] +where \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +are the distortion coefficients. + +In case of a stereo camera, this function is called twice: once for each camera head, after +stereoRectify, which in its turn is called after #stereoCalibrate. But if the stereo camera +was not calibrated, it is still possible to compute the rectification transformations directly from +the fundamental matrix using #stereoRectifyUncalibrated. For each camera, the function computes +homography H as the rectification transformation in a pixel domain, not a rotation matrix R in 3D +space. R can be computed from H as +\f[\texttt{R} = \texttt{cameraMatrix} ^{-1} \cdot \texttt{H} \cdot \texttt{cameraMatrix}\f] +where cameraMatrix can be chosen arbitrarily. + +@param cameraMatrix Input camera matrix \f$A=\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param R Optional rectification transformation in the object space (3x3 matrix). R1 or R2 , +computed by #stereoRectify can be passed here. If the matrix is empty, the identity transformation +is assumed. In cvInitUndistortMap R assumed to be an identity matrix. +@param newCameraMatrix New camera matrix \f$A'=\vecthreethree{f_x'}{0}{c_x'}{0}{f_y'}{c_y'}{0}{0}{1}\f$. +@param size Undistorted image size. +@param m1type Type of the first output map that can be CV_32FC1, CV_32FC2 or CV_16SC2, see #convertMaps +@param map1 The first output map. +@param map2 The second output map. + */ +CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs, + InputArray R, InputArray newCameraMatrix, + Size size, int m1type, OutputArray map1, OutputArray map2 ); + +//! initializes maps for #remap for wide-angle +CV_EXPORTS_W float initWideAngleProjMap( InputArray cameraMatrix, InputArray distCoeffs, + Size imageSize, int destImageWidth, + int m1type, OutputArray map1, OutputArray map2, + int projType = PROJ_SPHERICAL_EQRECT, double alpha = 0); + +/** @brief Returns the default new camera matrix. + +The function returns the camera matrix that is either an exact copy of the input cameraMatrix (when +centerPrinicipalPoint=false ), or the modified one (when centerPrincipalPoint=true). + +In the latter case, the new camera matrix will be: + +\f[\begin{bmatrix} f_x && 0 && ( \texttt{imgSize.width} -1)*0.5 \\ 0 && f_y && ( \texttt{imgSize.height} -1)*0.5 \\ 0 && 0 && 1 \end{bmatrix} ,\f] + +where \f$f_x\f$ and \f$f_y\f$ are \f$(0,0)\f$ and \f$(1,1)\f$ elements of cameraMatrix, respectively. + +By default, the undistortion functions in OpenCV (see #initUndistortRectifyMap, #undistort) do not +move the principal point. However, when you work with stereo, it is important to move the principal +points in both views to the same y-coordinate (which is required by most of stereo correspondence +algorithms), and may be to the same x-coordinate too. So, you can form the new camera matrix for +each view where the principal points are located at the center. + +@param cameraMatrix Input camera matrix. +@param imgsize Camera view image size in pixels. +@param centerPrincipalPoint Location of the principal point in the new camera matrix. The +parameter indicates whether this location should be at the image center or not. + */ +CV_EXPORTS_W Mat getDefaultNewCameraMatrix( InputArray cameraMatrix, Size imgsize = Size(), + bool centerPrincipalPoint = false ); + +/** @brief Computes the ideal point coordinates from the observed point coordinates. + +The function is similar to #undistort and #initUndistortRectifyMap but it operates on a +sparse set of points instead of a raster image. Also the function performs a reverse transformation +to projectPoints. In case of a 3D object, it does not reconstruct its 3D coordinates, but for a +planar object, it does, up to a translation vector, if the proper R is specified. + +For each observed point coordinate \f$(u, v)\f$ the function computes: +\f[ +\begin{array}{l} +x^{"} \leftarrow (u - c_x)/f_x \\ +y^{"} \leftarrow (v - c_y)/f_y \\ +(x',y') = undistort(x^{"},y^{"}, \texttt{distCoeffs}) \\ +{[X\,Y\,W]} ^T \leftarrow R*[x' \, y' \, 1]^T \\ +x \leftarrow X/W \\ +y \leftarrow Y/W \\ +\text{only performed if P is specified:} \\ +u' \leftarrow x {f'}_x + {c'}_x \\ +v' \leftarrow y {f'}_y + {c'}_y +\end{array} +\f] + +where *undistort* is an approximate iterative algorithm that estimates the normalized original +point coordinates out of the normalized distorted point coordinates ("normalized" means that the +coordinates do not depend on the camera matrix). + +The function can be used for both a stereo camera head or a monocular camera (when R is empty). +@param src Observed point coordinates, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel (CV_32FC2 or CV_64FC2) (or +vector\ ). +@param dst Output ideal point coordinates (1xN/Nx1 2-channel or vector\ ) after undistortion and reverse perspective +transformation. If matrix P is identity or omitted, dst will contain normalized point coordinates. +@param cameraMatrix Camera matrix \f$\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . +@param distCoeffs Input vector of distortion coefficients +\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ +of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. +@param R Rectification transformation in the object space (3x3 matrix). R1 or R2 computed by +#stereoRectify can be passed here. If the matrix is empty, the identity transformation is used. +@param P New camera matrix (3x3) or new projection matrix (3x4) \f$\begin{bmatrix} {f'}_x & 0 & {c'}_x & t_x \\ 0 & {f'}_y & {c'}_y & t_y \\ 0 & 0 & 1 & t_z \end{bmatrix}\f$. P1 or P2 computed by +#stereoRectify can be passed here. If the matrix is empty, the identity new camera matrix is used. + */ +CV_EXPORTS_W void undistortPoints( InputArray src, OutputArray dst, + InputArray cameraMatrix, InputArray distCoeffs, + InputArray R = noArray(), InputArray P = noArray() ); +/** @overload + @note Default version of #undistortPoints does 5 iterations to compute undistorted points. + + */ +CV_EXPORTS_AS(undistortPointsIter) void undistortPoints( InputArray src, OutputArray dst, + InputArray cameraMatrix, InputArray distCoeffs, + InputArray R, InputArray P, TermCriteria criteria ); + +//! @} imgproc_transform + +//! @addtogroup imgproc_hist +//! @{ + +/** @example samples/cpp/demhist.cpp +An example for creating histograms of an image +*/ + +/** @brief Calculates a histogram of a set of arrays. + +The function cv::calcHist calculates the histogram of one or more arrays. The elements of a tuple used +to increment a histogram bin are taken from the corresponding input arrays at the same location. The +sample below shows how to compute a 2D Hue-Saturation histogram for a color image. : +@include snippets/imgproc_calcHist.cpp + +@param images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same +size. Each of them can have an arbitrary number of channels. +@param nimages Number of source images. +@param channels List of the dims channels used to compute the histogram. The first array channels +are numerated from 0 to images[0].channels()-1 , the second array channels are counted from +images[0].channels() to images[0].channels() + images[1].channels()-1, and so on. +@param mask Optional mask. If the matrix is not empty, it must be an 8-bit array of the same size +as images[i] . The non-zero mask elements mark the array elements counted in the histogram. +@param hist Output histogram, which is a dense or sparse dims -dimensional array. +@param dims Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS +(equal to 32 in the current OpenCV version). +@param histSize Array of histogram sizes in each dimension. +@param ranges Array of the dims arrays of the histogram bin boundaries in each dimension. When the +histogram is uniform ( uniform =true), then for each dimension i it is enough to specify the lower +(inclusive) boundary \f$L_0\f$ of the 0-th histogram bin and the upper (exclusive) boundary +\f$U_{\texttt{histSize}[i]-1}\f$ for the last histogram bin histSize[i]-1 . That is, in case of a +uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not uniform ( +uniform=false ), then each of ranges[i] contains histSize[i]+1 elements: +\f$L_0, U_0=L_1, U_1=L_2, ..., U_{\texttt{histSize[i]}-2}=L_{\texttt{histSize[i]}-1}, U_{\texttt{histSize[i]}-1}\f$ +. The array elements, that are not between \f$L_0\f$ and \f$U_{\texttt{histSize[i]}-1}\f$ , are not +counted in the histogram. +@param uniform Flag indicating whether the histogram is uniform or not (see above). +@param accumulate Accumulation flag. If it is set, the histogram is not cleared in the beginning +when it is allocated. This feature enables you to compute a single histogram from several sets of +arrays, or to update the histogram in time. +*/ +CV_EXPORTS void calcHist( const Mat* images, int nimages, + const int* channels, InputArray mask, + OutputArray hist, int dims, const int* histSize, + const float** ranges, bool uniform = true, bool accumulate = false ); + +/** @overload + +this variant uses %SparseMat for output +*/ +CV_EXPORTS void calcHist( const Mat* images, int nimages, + const int* channels, InputArray mask, + SparseMat& hist, int dims, + const int* histSize, const float** ranges, + bool uniform = true, bool accumulate = false ); + +/** @overload */ +CV_EXPORTS_W void calcHist( InputArrayOfArrays images, + const std::vector& channels, + InputArray mask, OutputArray hist, + const std::vector& histSize, + const std::vector& ranges, + bool accumulate = false ); + +/** @brief Calculates the back projection of a histogram. + +The function cv::calcBackProject calculates the back project of the histogram. That is, similarly to +#calcHist , at each location (x, y) the function collects the values from the selected channels +in the input images and finds the corresponding histogram bin. But instead of incrementing it, the +function reads the bin value, scales it by scale , and stores in backProject(x,y) . In terms of +statistics, the function computes probability of each element value in respect with the empirical +probability distribution represented by the histogram. See how, for example, you can find and track +a bright-colored object in a scene: + +- Before tracking, show the object to the camera so that it covers almost the whole frame. +Calculate a hue histogram. The histogram may have strong maximums, corresponding to the dominant +colors in the object. + +- When tracking, calculate a back projection of a hue plane of each input video frame using that +pre-computed histogram. Threshold the back projection to suppress weak colors. It may also make +sense to suppress pixels with non-sufficient color saturation and too dark or too bright pixels. + +- Find connected components in the resulting picture and choose, for example, the largest +component. + +This is an approximate algorithm of the CamShift color object tracker. + +@param images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same +size. Each of them can have an arbitrary number of channels. +@param nimages Number of source images. +@param channels The list of channels used to compute the back projection. The number of channels +must match the histogram dimensionality. The first array channels are numerated from 0 to +images[0].channels()-1 , the second array channels are counted from images[0].channels() to +images[0].channels() + images[1].channels()-1, and so on. +@param hist Input histogram that can be dense or sparse. +@param backProject Destination back projection array that is a single-channel array of the same +size and depth as images[0] . +@param ranges Array of arrays of the histogram bin boundaries in each dimension. See #calcHist . +@param scale Optional scale factor for the output back projection. +@param uniform Flag indicating whether the histogram is uniform or not (see above). + +@sa calcHist, compareHist + */ +CV_EXPORTS void calcBackProject( const Mat* images, int nimages, + const int* channels, InputArray hist, + OutputArray backProject, const float** ranges, + double scale = 1, bool uniform = true ); + +/** @overload */ +CV_EXPORTS void calcBackProject( const Mat* images, int nimages, + const int* channels, const SparseMat& hist, + OutputArray backProject, const float** ranges, + double scale = 1, bool uniform = true ); + +/** @overload */ +CV_EXPORTS_W void calcBackProject( InputArrayOfArrays images, const std::vector& channels, + InputArray hist, OutputArray dst, + const std::vector& ranges, + double scale ); + +/** @brief Compares two histograms. + +The function cv::compareHist compares two dense or two sparse histograms using the specified method. + +The function returns \f$d(H_1, H_2)\f$ . + +While the function works well with 1-, 2-, 3-dimensional dense histograms, it may not be suitable +for high-dimensional sparse histograms. In such histograms, because of aliasing and sampling +problems, the coordinates of non-zero histogram bins can slightly shift. To compare such histograms +or more general sparse configurations of weighted points, consider using the #EMD function. + +@param H1 First compared histogram. +@param H2 Second compared histogram of the same size as H1 . +@param method Comparison method, see #HistCompMethods + */ +CV_EXPORTS_W double compareHist( InputArray H1, InputArray H2, int method ); + +/** @overload */ +CV_EXPORTS double compareHist( const SparseMat& H1, const SparseMat& H2, int method ); + +/** @brief Equalizes the histogram of a grayscale image. + +The function equalizes the histogram of the input image using the following algorithm: + +- Calculate the histogram \f$H\f$ for src . +- Normalize the histogram so that the sum of histogram bins is 255. +- Compute the integral of the histogram: +\f[H'_i = \sum _{0 \le j < i} H(j)\f] +- Transform the image using \f$H'\f$ as a look-up table: \f$\texttt{dst}(x,y) = H'(\texttt{src}(x,y))\f$ + +The algorithm normalizes the brightness and increases the contrast of the image. + +@param src Source 8-bit single channel image. +@param dst Destination image of the same size and type as src . + */ +CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst ); + +/** @brief Creates a smart pointer to a cv::CLAHE class and initializes it. + +@param clipLimit Threshold for contrast limiting. +@param tileGridSize Size of grid for histogram equalization. Input image will be divided into +equally sized rectangular tiles. tileGridSize defines the number of tiles in row and column. + */ +CV_EXPORTS_W Ptr createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8)); + +/** @brief Computes the "minimal work" distance between two weighted point configurations. + +The function computes the earth mover distance and/or a lower boundary of the distance between the +two weighted point configurations. One of the applications described in @cite RubnerSept98, +@cite Rubner2000 is multi-dimensional histogram comparison for image retrieval. EMD is a transportation +problem that is solved using some modification of a simplex algorithm, thus the complexity is +exponential in the worst case, though, on average it is much faster. In the case of a real metric +the lower boundary can be calculated even faster (using linear-time algorithm) and it can be used +to determine roughly whether the two signatures are far enough so that they cannot relate to the +same object. + +@param signature1 First signature, a \f$\texttt{size1}\times \texttt{dims}+1\f$ floating-point matrix. +Each row stores the point weight followed by the point coordinates. The matrix is allowed to have +a single column (weights only) if the user-defined cost matrix is used. The weights must be +non-negative and have at least one non-zero value. +@param signature2 Second signature of the same format as signature1 , though the number of rows +may be different. The total weights may be different. In this case an extra "dummy" point is added +to either signature1 or signature2. The weights must be non-negative and have at least one non-zero +value. +@param distType Used metric. See #DistanceTypes. +@param cost User-defined \f$\texttt{size1}\times \texttt{size2}\f$ cost matrix. Also, if a cost matrix +is used, lower boundary lowerBound cannot be calculated because it needs a metric function. +@param lowerBound Optional input/output parameter: lower boundary of a distance between the two +signatures that is a distance between mass centers. The lower boundary may not be calculated if +the user-defined cost matrix is used, the total weights of point configurations are not equal, or +if the signatures consist of weights only (the signature matrices have a single column). You +**must** initialize \*lowerBound . If the calculated distance between mass centers is greater or +equal to \*lowerBound (it means that the signatures are far enough), the function does not +calculate EMD. In any case \*lowerBound is set to the calculated distance between mass centers on +return. Thus, if you want to calculate both distance between mass centers and EMD, \*lowerBound +should be set to 0. +@param flow Resultant \f$\texttt{size1} \times \texttt{size2}\f$ flow matrix: \f$\texttt{flow}_{i,j}\f$ is +a flow from \f$i\f$ -th point of signature1 to \f$j\f$ -th point of signature2 . + */ +CV_EXPORTS float EMD( InputArray signature1, InputArray signature2, + int distType, InputArray cost=noArray(), + float* lowerBound = 0, OutputArray flow = noArray() ); + +CV_EXPORTS_AS(EMD) float wrapperEMD( InputArray signature1, InputArray signature2, + int distType, InputArray cost=noArray(), + CV_IN_OUT Ptr lowerBound = Ptr(), OutputArray flow = noArray() ); + +//! @} imgproc_hist + +/** @example samples/cpp/watershed.cpp +An example using the watershed algorithm +*/ + +/** @brief Performs a marker-based image segmentation using the watershed algorithm. + +The function implements one of the variants of watershed, non-parametric marker-based segmentation +algorithm, described in @cite Meyer92 . + +Before passing the image to the function, you have to roughly outline the desired regions in the +image markers with positive (\>0) indices. So, every region is represented as one or more connected +components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary +mask using #findContours and #drawContours (see the watershed.cpp demo). The markers are "seeds" of +the future image regions. All the other pixels in markers , whose relation to the outlined regions +is not known and should be defined by the algorithm, should be set to 0's. In the function output, +each pixel in markers is set to a value of the "seed" components or to -1 at boundaries between the +regions. + +@note Any two neighbor connected components are not necessarily separated by a watershed boundary +(-1's pixels); for example, they can touch each other in the initial marker image passed to the +function. + +@param image Input 8-bit 3-channel image. +@param markers Input/output 32-bit single-channel image (map) of markers. It should have the same +size as image . + +@sa findContours + +@ingroup imgproc_misc + */ +CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers ); + +//! @addtogroup imgproc_filter +//! @{ + +/** @brief Performs initial step of meanshift segmentation of an image. + +The function implements the filtering stage of meanshift segmentation, that is, the output of the +function is the filtered "posterized" image with color gradients and fine-grain texture flattened. +At every pixel (X,Y) of the input image (or down-sized input image, see below) the function executes +meanshift iterations, that is, the pixel (X,Y) neighborhood in the joint space-color hyperspace is +considered: + +\f[(x,y): X- \texttt{sp} \le x \le X+ \texttt{sp} , Y- \texttt{sp} \le y \le Y+ \texttt{sp} , ||(R,G,B)-(r,g,b)|| \le \texttt{sr}\f] + +where (R,G,B) and (r,g,b) are the vectors of color components at (X,Y) and (x,y), respectively +(though, the algorithm does not depend on the color space used, so any 3-component color space can +be used instead). Over the neighborhood the average spatial value (X',Y') and average color vector +(R',G',B') are found and they act as the neighborhood center on the next iteration: + +\f[(X,Y)~(X',Y'), (R,G,B)~(R',G',B').\f] + +After the iterations over, the color components of the initial pixel (that is, the pixel from where +the iterations started) are set to the final value (average color at the last iteration): + +\f[I(X,Y) <- (R*,G*,B*)\f] + +When maxLevel \> 0, the gaussian pyramid of maxLevel+1 levels is built, and the above procedure is +run on the smallest layer first. After that, the results are propagated to the larger layer and the +iterations are run again only on those pixels where the layer colors differ by more than sr from the +lower-resolution layer of the pyramid. That makes boundaries of color regions sharper. Note that the +results will be actually different from the ones obtained by running the meanshift procedure on the +whole original image (i.e. when maxLevel==0). + +@param src The source 8-bit, 3-channel image. +@param dst The destination image of the same format and the same size as the source. +@param sp The spatial window radius. +@param sr The color window radius. +@param maxLevel Maximum level of the pyramid for the segmentation. +@param termcrit Termination criteria: when to stop meanshift iterations. + */ +CV_EXPORTS_W void pyrMeanShiftFiltering( InputArray src, OutputArray dst, + double sp, double sr, int maxLevel = 1, + TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) ); + +//! @} + +//! @addtogroup imgproc_misc +//! @{ + +/** @example samples/cpp/grabcut.cpp +An example using the GrabCut algorithm +![Sample Screenshot](grabcut_output1.jpg) +*/ + +/** @brief Runs the GrabCut algorithm. + +The function implements the [GrabCut image segmentation algorithm](http://en.wikipedia.org/wiki/GrabCut). + +@param img Input 8-bit 3-channel image. +@param mask Input/output 8-bit single-channel mask. The mask is initialized by the function when +mode is set to #GC_INIT_WITH_RECT. Its elements may have one of the #GrabCutClasses. +@param rect ROI containing a segmented object. The pixels outside of the ROI are marked as +"obvious background". The parameter is only used when mode==#GC_INIT_WITH_RECT . +@param bgdModel Temporary array for the background model. Do not modify it while you are +processing the same image. +@param fgdModel Temporary arrays for the foreground model. Do not modify it while you are +processing the same image. +@param iterCount Number of iterations the algorithm should make before returning the result. Note +that the result can be refined with further calls with mode==#GC_INIT_WITH_MASK or +mode==GC_EVAL . +@param mode Operation mode that could be one of the #GrabCutModes + */ +CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect, + InputOutputArray bgdModel, InputOutputArray fgdModel, + int iterCount, int mode = GC_EVAL ); + +/** @example samples/cpp/distrans.cpp +An example on using the distance transform +*/ + +/** @brief Calculates the distance to the closest zero pixel for each pixel of the source image. + +The function cv::distanceTransform calculates the approximate or precise distance from every binary +image pixel to the nearest zero pixel. For zero image pixels, the distance will obviously be zero. + +When maskSize == #DIST_MASK_PRECISE and distanceType == #DIST_L2 , the function runs the +algorithm described in @cite Felzenszwalb04 . This algorithm is parallelized with the TBB library. + +In other cases, the algorithm @cite Borgefors86 is used. This means that for a pixel the function +finds the shortest path to the nearest zero pixel consisting of basic shifts: horizontal, vertical, +diagonal, or knight's move (the latest is available for a \f$5\times 5\f$ mask). The overall +distance is calculated as a sum of these basic distances. Since the distance function should be +symmetric, all of the horizontal and vertical shifts must have the same cost (denoted as a ), all +the diagonal shifts must have the same cost (denoted as `b`), and all knight's moves must have the +same cost (denoted as `c`). For the #DIST_C and #DIST_L1 types, the distance is calculated +precisely, whereas for #DIST_L2 (Euclidean distance) the distance can be calculated only with a +relative error (a \f$5\times 5\f$ mask gives more accurate results). For `a`,`b`, and `c`, OpenCV +uses the values suggested in the original paper: +- DIST_L1: `a = 1, b = 2` +- DIST_L2: + - `3 x 3`: `a=0.955, b=1.3693` + - `5 x 5`: `a=1, b=1.4, c=2.1969` +- DIST_C: `a = 1, b = 1` + +Typically, for a fast, coarse distance estimation #DIST_L2, a \f$3\times 3\f$ mask is used. For a +more accurate distance estimation #DIST_L2, a \f$5\times 5\f$ mask or the precise algorithm is used. +Note that both the precise and the approximate algorithms are linear on the number of pixels. + +This variant of the function does not only compute the minimum distance for each pixel \f$(x, y)\f$ +but also identifies the nearest connected component consisting of zero pixels +(labelType==#DIST_LABEL_CCOMP) or the nearest zero pixel (labelType==#DIST_LABEL_PIXEL). Index of the +component/pixel is stored in `labels(x, y)`. When labelType==#DIST_LABEL_CCOMP, the function +automatically finds connected components of zero pixels in the input image and marks them with +distinct labels. When labelType==#DIST_LABEL_PIXEL, the function scans through the input image and +marks all the zero pixels with distinct labels. + +In this mode, the complexity is still linear. That is, the function provides a very fast way to +compute the Voronoi diagram for a binary image. Currently, the second variant can use only the +approximate distance transform algorithm, i.e. maskSize=#DIST_MASK_PRECISE is not supported +yet. + +@param src 8-bit, single-channel (binary) source image. +@param dst Output image with calculated distances. It is a 8-bit or 32-bit floating-point, +single-channel image of the same size as src. +@param labels Output 2D array of labels (the discrete Voronoi diagram). It has the type +CV_32SC1 and the same size as src. +@param distanceType Type of distance, see #DistanceTypes +@param maskSize Size of the distance transform mask, see #DistanceTransformMasks. +#DIST_MASK_PRECISE is not supported by this variant. In case of the #DIST_L1 or #DIST_C distance type, +the parameter is forced to 3 because a \f$3\times 3\f$ mask gives the same result as \f$5\times +5\f$ or any larger aperture. +@param labelType Type of the label array to build, see #DistanceTransformLabelTypes. + */ +CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray src, OutputArray dst, + OutputArray labels, int distanceType, int maskSize, + int labelType = DIST_LABEL_CCOMP ); + +/** @overload +@param src 8-bit, single-channel (binary) source image. +@param dst Output image with calculated distances. It is a 8-bit or 32-bit floating-point, +single-channel image of the same size as src . +@param distanceType Type of distance, see #DistanceTypes +@param maskSize Size of the distance transform mask, see #DistanceTransformMasks. In case of the +#DIST_L1 or #DIST_C distance type, the parameter is forced to 3 because a \f$3\times 3\f$ mask gives +the same result as \f$5\times 5\f$ or any larger aperture. +@param dstType Type of output image. It can be CV_8U or CV_32F. Type CV_8U can be used only for +the first variant of the function and distanceType == #DIST_L1. +*/ +CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst, + int distanceType, int maskSize, int dstType=CV_32F); + +/** @example samples/cpp/ffilldemo.cpp +An example using the FloodFill technique +*/ + +/** @overload + +variant without `mask` parameter +*/ +CV_EXPORTS int floodFill( InputOutputArray image, + Point seedPoint, Scalar newVal, CV_OUT Rect* rect = 0, + Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), + int flags = 4 ); + +/** @brief Fills a connected component with the given color. + +The function cv::floodFill fills a connected component starting from the seed point with the specified +color. The connectivity is determined by the color/brightness closeness of the neighbor pixels. The +pixel at \f$(x,y)\f$ is considered to belong to the repainted domain if: + +- in case of a grayscale image and floating range +\f[\texttt{src} (x',y')- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} (x',y')+ \texttt{upDiff}\f] + + +- in case of a grayscale image and fixed range +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)+ \texttt{upDiff}\f] + + +- in case of a color image and floating range +\f[\texttt{src} (x',y')_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} (x',y')_r+ \texttt{upDiff} _r,\f] +\f[\texttt{src} (x',y')_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} (x',y')_g+ \texttt{upDiff} _g\f] +and +\f[\texttt{src} (x',y')_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} (x',y')_b+ \texttt{upDiff} _b\f] + + +- in case of a color image and fixed range +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r+ \texttt{upDiff} _r,\f] +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g+ \texttt{upDiff} _g\f] +and +\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b+ \texttt{upDiff} _b\f] + + +where \f$src(x',y')\f$ is the value of one of pixel neighbors that is already known to belong to the +component. That is, to be added to the connected component, a color/brightness of the pixel should +be close enough to: +- Color/brightness of one of its neighbors that already belong to the connected component in case +of a floating range. +- Color/brightness of the seed point in case of a fixed range. + +Use these functions to either mark a connected component with the specified color in-place, or build +a mask and then extract the contour, or copy the region to another image, and so on. + +@param image Input/output 1- or 3-channel, 8-bit, or floating-point image. It is modified by the +function unless the #FLOODFILL_MASK_ONLY flag is set in the second variant of the function. See +the details below. +@param mask Operation mask that should be a single-channel 8-bit image, 2 pixels wider and 2 pixels +taller than image. Since this is both an input and output parameter, you must take responsibility +of initializing it. Flood-filling cannot go across non-zero pixels in the input mask. For example, +an edge detector output can be used as a mask to stop filling at edges. On output, pixels in the +mask corresponding to filled pixels in the image are set to 1 or to the a value specified in flags +as described below. Additionally, the function fills the border of the mask with ones to simplify +internal processing. It is therefore possible to use the same mask in multiple calls to the function +to make sure the filled areas do not overlap. +@param seedPoint Starting point. +@param newVal New value of the repainted domain pixels. +@param loDiff Maximal lower brightness/color difference between the currently observed pixel and +one of its neighbors belonging to the component, or a seed pixel being added to the component. +@param upDiff Maximal upper brightness/color difference between the currently observed pixel and +one of its neighbors belonging to the component, or a seed pixel being added to the component. +@param rect Optional output parameter set by the function to the minimum bounding rectangle of the +repainted domain. +@param flags Operation flags. The first 8 bits contain a connectivity value. The default value of +4 means that only the four nearest neighbor pixels (those that share an edge) are considered. A +connectivity value of 8 means that the eight nearest neighbor pixels (those that share a corner) +will be considered. The next 8 bits (8-16) contain a value between 1 and 255 with which to fill +the mask (the default value is 1). For example, 4 | ( 255 \<\< 8 ) will consider 4 nearest +neighbours and fill the mask with a value of 255. The following additional options occupy higher +bits and therefore may be further combined with the connectivity and mask fill values using +bit-wise or (|), see #FloodFillFlags. + +@note Since the mask is larger than the filled image, a pixel \f$(x, y)\f$ in image corresponds to the +pixel \f$(x+1, y+1)\f$ in the mask . + +@sa findContours + */ +CV_EXPORTS_W int floodFill( InputOutputArray image, InputOutputArray mask, + Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0, + Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), + int flags = 4 ); + +//! Performs linear blending of two images: +//! \f[ \texttt{dst}(i,j) = \texttt{weights1}(i,j)*\texttt{src1}(i,j) + \texttt{weights2}(i,j)*\texttt{src2}(i,j) \f] +//! @param src1 It has a type of CV_8UC(n) or CV_32FC(n), where n is a positive integer. +//! @param src2 It has the same type and size as src1. +//! @param weights1 It has a type of CV_32FC1 and the same size with src1. +//! @param weights2 It has a type of CV_32FC1 and the same size with src1. +//! @param dst It is created if it does not have the same size and type with src1. +CV_EXPORTS_W void blendLinear(InputArray src1, InputArray src2, InputArray weights1, InputArray weights2, OutputArray dst); + +//! @} imgproc_misc + +//! @addtogroup imgproc_color_conversions +//! @{ + +/** @brief Converts an image from one color space to another. + +The function converts an input image from one color space to another. In case of a transformation +to-from RGB color space, the order of the channels should be specified explicitly (RGB or BGR). Note +that the default color format in OpenCV is often referred to as RGB but it is actually BGR (the +bytes are reversed). So the first byte in a standard (24-bit) color image will be an 8-bit Blue +component, the second byte will be Green, and the third byte will be Red. The fourth, fifth, and +sixth bytes would then be the second pixel (Blue, then Green, then Red), and so on. + +The conventional ranges for R, G, and B channel values are: +- 0 to 255 for CV_8U images +- 0 to 65535 for CV_16U images +- 0 to 1 for CV_32F images + +In case of linear transformations, the range does not matter. But in case of a non-linear +transformation, an input RGB image should be normalized to the proper value range to get the correct +results, for example, for RGB \f$\rightarrow\f$ L\*u\*v\* transformation. For example, if you have a +32-bit floating-point image directly converted from an 8-bit image without any scaling, then it will +have the 0..255 value range instead of 0..1 assumed by the function. So, before calling #cvtColor , +you need first to scale the image down: +@code + img *= 1./255; + cvtColor(img, img, COLOR_BGR2Luv); +@endcode +If you use #cvtColor with 8-bit images, the conversion will have some information lost. For many +applications, this will not be noticeable but it is recommended to use 32-bit images in applications +that need the full range of colors or that convert an image before an operation and then convert +back. + +If conversion adds the alpha channel, its value will set to the maximum of corresponding channel +range: 255 for CV_8U, 65535 for CV_16U, 1 for CV_32F. + +@param src input image: 8-bit unsigned, 16-bit unsigned ( CV_16UC... ), or single-precision +floating-point. +@param dst output image of the same size and depth as src. +@param code color space conversion code (see #ColorConversionCodes). +@param dstCn number of channels in the destination image; if the parameter is 0, the number of the +channels is derived automatically from src and code. + +@see @ref imgproc_color_conversions + */ +CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 ); + +/** @brief Converts an image from one color space to another where the source image is +stored in two planes. + +This function only supports YUV420 to RGB conversion as of now. + +@param src1: 8-bit image (#CV_8U) of the Y plane. +@param src2: image containing interleaved U/V plane. +@param dst: output image. +@param code: Specifies the type of conversion. It can take any of the following values: +- #COLOR_YUV2BGR_NV12 +- #COLOR_YUV2RGB_NV12 +- #COLOR_YUV2BGRA_NV12 +- #COLOR_YUV2RGBA_NV12 +- #COLOR_YUV2BGR_NV21 +- #COLOR_YUV2RGB_NV21 +- #COLOR_YUV2BGRA_NV21 +- #COLOR_YUV2RGBA_NV21 +*/ +CV_EXPORTS_W void cvtColorTwoPlane( InputArray src1, InputArray src2, OutputArray dst, int code ); + +/** @brief main function for all demosaicing processes + +@param src input image: 8-bit unsigned or 16-bit unsigned. +@param dst output image of the same size and depth as src. +@param code Color space conversion code (see the description below). +@param dstCn number of channels in the destination image; if the parameter is 0, the number of the +channels is derived automatically from src and code. + +The function can do the following transformations: + +- Demosaicing using bilinear interpolation + + #COLOR_BayerBG2BGR , #COLOR_BayerGB2BGR , #COLOR_BayerRG2BGR , #COLOR_BayerGR2BGR + + #COLOR_BayerBG2GRAY , #COLOR_BayerGB2GRAY , #COLOR_BayerRG2GRAY , #COLOR_BayerGR2GRAY + +- Demosaicing using Variable Number of Gradients. + + #COLOR_BayerBG2BGR_VNG , #COLOR_BayerGB2BGR_VNG , #COLOR_BayerRG2BGR_VNG , #COLOR_BayerGR2BGR_VNG + +- Edge-Aware Demosaicing. + + #COLOR_BayerBG2BGR_EA , #COLOR_BayerGB2BGR_EA , #COLOR_BayerRG2BGR_EA , #COLOR_BayerGR2BGR_EA + +- Demosaicing with alpha channel + + #COLOR_BayerBG2BGRA , #COLOR_BayerGB2BGRA , #COLOR_BayerRG2BGRA , #COLOR_BayerGR2BGRA + +@sa cvtColor +*/ +CV_EXPORTS_W void demosaicing(InputArray src, OutputArray dst, int code, int dstCn = 0); + +//! @} imgproc_color_conversions + +//! @addtogroup imgproc_shape +//! @{ + +/** @brief Calculates all of the moments up to the third order of a polygon or rasterized shape. + +The function computes moments, up to the 3rd order, of a vector shape or a rasterized shape. The +results are returned in the structure cv::Moments. + +@param array Raster image (single-channel, 8-bit or floating-point 2D array) or an array ( +\f$1 \times N\f$ or \f$N \times 1\f$ ) of 2D points (Point or Point2f ). +@param binaryImage If it is true, all non-zero image pixels are treated as 1's. The parameter is +used for images only. +@returns moments. + +@note Only applicable to contour moments calculations from Python bindings: Note that the numpy +type for the input array should be either np.int32 or np.float32. + +@sa contourArea, arcLength + */ +CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false ); + +/** @brief Calculates seven Hu invariants. + +The function calculates seven Hu invariants (introduced in @cite Hu62; see also +) defined as: + +\f[\begin{array}{l} hu[0]= \eta _{20}+ \eta _{02} \\ hu[1]=( \eta _{20}- \eta _{02})^{2}+4 \eta _{11}^{2} \\ hu[2]=( \eta _{30}-3 \eta _{12})^{2}+ (3 \eta _{21}- \eta _{03})^{2} \\ hu[3]=( \eta _{30}+ \eta _{12})^{2}+ ( \eta _{21}+ \eta _{03})^{2} \\ hu[4]=( \eta _{30}-3 \eta _{12})( \eta _{30}+ \eta _{12})[( \eta _{30}+ \eta _{12})^{2}-3( \eta _{21}+ \eta _{03})^{2}]+(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ hu[5]=( \eta _{20}- \eta _{02})[( \eta _{30}+ \eta _{12})^{2}- ( \eta _{21}+ \eta _{03})^{2}]+4 \eta _{11}( \eta _{30}+ \eta _{12})( \eta _{21}+ \eta _{03}) \\ hu[6]=(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}]-( \eta _{30}-3 \eta _{12})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ \end{array}\f] + +where \f$\eta_{ji}\f$ stands for \f$\texttt{Moments::nu}_{ji}\f$ . + +These values are proved to be invariants to the image scale, rotation, and reflection except the +seventh one, whose sign is changed by reflection. This invariance is proved with the assumption of +infinite image resolution. In case of raster images, the computed Hu invariants for the original and +transformed images are a bit different. + +@param moments Input moments computed with moments . +@param hu Output Hu invariants. + +@sa matchShapes + */ +CV_EXPORTS void HuMoments( const Moments& moments, double hu[7] ); + +/** @overload */ +CV_EXPORTS_W void HuMoments( const Moments& m, OutputArray hu ); + +//! @} imgproc_shape + +//! @addtogroup imgproc_object +//! @{ + +//! type of the template matching operation +enum TemplateMatchModes { + TM_SQDIFF = 0, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f] + TM_SQDIFF_NORMED = 1, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f] + TM_CCORR = 2, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f] + TM_CCORR_NORMED = 3, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f] + TM_CCOEFF = 4, //!< \f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\f] + //!< where + //!< \f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f] + TM_CCOEFF_NORMED = 5 //!< \f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f] +}; + +/** @example samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp +An example using Template Matching algorithm +*/ + +/** @brief Compares a template against overlapped image regions. + +The function slides through image , compares the overlapped patches of size \f$w \times h\f$ against +templ using the specified method and stores the comparison results in result . Here are the formulae +for the available comparison methods ( \f$I\f$ denotes image, \f$T\f$ template, \f$R\f$ result ). The summation +is done over template and/or the image patch: \f$x' = 0...w-1, y' = 0...h-1\f$ + +After the function finishes the comparison, the best matches can be found as global minimums (when +#TM_SQDIFF was used) or maximums (when #TM_CCORR or #TM_CCOEFF was used) using the +#minMaxLoc function. In case of a color image, template summation in the numerator and each sum in +the denominator is done over all of the channels and separate mean values are used for each channel. +That is, the function can take a color template and a color image. The result will still be a +single-channel image, which is easier to analyze. + +@param image Image where the search is running. It must be 8-bit or 32-bit floating-point. +@param templ Searched template. It must be not greater than the source image and have the same +data type. +@param result Map of comparison results. It must be single-channel 32-bit floating-point. If image +is \f$W \times H\f$ and templ is \f$w \times h\f$ , then result is \f$(W-w+1) \times (H-h+1)\f$ . +@param method Parameter specifying the comparison method, see #TemplateMatchModes +@param mask Mask of searched template. It must have the same datatype and size with templ. It is +not set by default. Currently, only the #TM_SQDIFF and #TM_CCORR_NORMED methods are supported. + */ +CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, + OutputArray result, int method, InputArray mask = noArray() ); + +//! @} + +//! @addtogroup imgproc_shape +//! @{ + +/** @example samples/cpp/connected_components.cpp +This program demonstrates connected components and use of the trackbar +*/ + +/** @brief computes the connected components labeled image of boolean image + +image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 +represents the background label. ltype specifies the output label image type, an important +consideration based on the total number of labels or alternatively the total number of pixels in +the source image. ccltype specifies the connected components labeling algorithm to use, currently +Grana (BBDT) and Wu's (SAUF) @cite Wu2009 algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. +This function uses parallel version of both Grana and Wu's algorithms if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +@param ccltype connected components algorithm type (see the #ConnectedComponentsAlgorithmsTypes). +*/ +CV_EXPORTS_AS(connectedComponentsWithAlgorithm) int connectedComponents(InputArray image, OutputArray labels, + int connectivity, int ltype, int ccltype); + + +/** @overload + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +*/ +CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, + int connectivity = 8, int ltype = CV_32S); + + +/** @brief computes the connected components labeled image of boolean image and also produces a statistics output for each label + +image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 +represents the background label. ltype specifies the output label image type, an important +consideration based on the total number of labels or alternatively the total number of pixels in +the source image. ccltype specifies the connected components labeling algorithm to use, currently +Grana's (BBDT) and Wu's (SAUF) @cite Wu2009 algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. +This function uses parallel version of both Grana and Wu's algorithms (statistics included) if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. + +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param stats statistics output for each label, including the background label. +Statistics are accessed via stats(label, COLUMN) where COLUMN is one of +#ConnectedComponentsTypes, selecting the statistic. The data type is CV_32S. +@param centroids centroid output for each label, including the background label. Centroids are +accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F. +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +@param ccltype connected components algorithm type (see #ConnectedComponentsAlgorithmsTypes). +*/ +CV_EXPORTS_AS(connectedComponentsWithStatsWithAlgorithm) int connectedComponentsWithStats(InputArray image, OutputArray labels, + OutputArray stats, OutputArray centroids, + int connectivity, int ltype, int ccltype); + +/** @overload +@param image the 8-bit single-channel image to be labeled +@param labels destination labeled image +@param stats statistics output for each label, including the background label. +Statistics are accessed via stats(label, COLUMN) where COLUMN is one of +#ConnectedComponentsTypes, selecting the statistic. The data type is CV_32S. +@param centroids centroid output for each label, including the background label. Centroids are +accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F. +@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively +@param ltype output image label type. Currently CV_32S and CV_16U are supported. +*/ +CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, + OutputArray stats, OutputArray centroids, + int connectivity = 8, int ltype = CV_32S); + + +/** @brief Finds contours in a binary image. + +The function retrieves contours from the binary image using the algorithm @cite Suzuki85 . The contours +are a useful tool for shape analysis and object detection and recognition. See squares.cpp in the +OpenCV sample directory. +@note Since opencv 3.2 source image is not modified by this function. + +@param image Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero +pixels remain 0's, so the image is treated as binary . You can use #compare, #inRange, #threshold , +#adaptiveThreshold, #Canny, and others to create a binary image out of a grayscale or color one. +If mode equals to #RETR_CCOMP or #RETR_FLOODFILL, the input can also be a 32-bit integer image of labels (CV_32SC1). +@param contours Detected contours. Each contour is stored as a vector of points (e.g. +std::vector >). +@param hierarchy Optional output vector (e.g. std::vector), containing information about the image topology. It has +as many elements as the number of contours. For each i-th contour contours[i], the elements +hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , and hierarchy[i][3] are set to 0-based indices +in contours of the next and previous contours at the same hierarchical level, the first child +contour and the parent contour, respectively. If for the contour i there are no next, previous, +parent, or nested contours, the corresponding elements of hierarchy[i] will be negative. +@param mode Contour retrieval mode, see #RetrievalModes +@param method Contour approximation method, see #ContourApproximationModes +@param offset Optional offset by which every contour point is shifted. This is useful if the +contours are extracted from the image ROI and then they should be analyzed in the whole image +context. + */ +CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours, + OutputArray hierarchy, int mode, + int method, Point offset = Point()); + +/** @overload */ +CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours, + int mode, int method, Point offset = Point()); + +/** @example samples/cpp/squares.cpp +A program using pyramid scaling, Canny, contours and contour simplification to find +squares in a list of images (pic1-6.png). Returns sequence of squares detected on the image. +*/ + +/** @example samples/tapi/squares.cpp +A program using pyramid scaling, Canny, contours and contour simplification to find +squares in the input image. +*/ + +/** @brief Approximates a polygonal curve(s) with the specified precision. + +The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less +vertices so that the distance between them is less or equal to the specified precision. It uses the +Douglas-Peucker algorithm + +@param curve Input vector of a 2D point stored in std::vector or Mat +@param approxCurve Result of the approximation. The type should match the type of the input curve. +@param epsilon Parameter specifying the approximation accuracy. This is the maximum distance +between the original curve and its approximation. +@param closed If true, the approximated curve is closed (its first and last vertices are +connected). Otherwise, it is not closed. + */ +CV_EXPORTS_W void approxPolyDP( InputArray curve, + OutputArray approxCurve, + double epsilon, bool closed ); + +/** @brief Calculates a contour perimeter or a curve length. + +The function computes a curve length or a closed contour perimeter. + +@param curve Input vector of 2D points, stored in std::vector or Mat. +@param closed Flag indicating whether the curve is closed or not. + */ +CV_EXPORTS_W double arcLength( InputArray curve, bool closed ); + +/** @brief Calculates the up-right bounding rectangle of a point set or non-zero pixels of gray-scale image. + +The function calculates and returns the minimal up-right bounding rectangle for the specified point set or +non-zero pixels of gray-scale image. + +@param array Input gray-scale image or 2D point set, stored in std::vector or Mat. + */ +CV_EXPORTS_W Rect boundingRect( InputArray array ); + +/** @brief Calculates a contour area. + +The function computes a contour area. Similarly to moments , the area is computed using the Green +formula. Thus, the returned area and the number of non-zero pixels, if you draw the contour using +#drawContours or #fillPoly , can be different. Also, the function will most certainly give a wrong +results for contours with self-intersections. + +Example: +@code + vector contour; + contour.push_back(Point2f(0, 0)); + contour.push_back(Point2f(10, 0)); + contour.push_back(Point2f(10, 10)); + contour.push_back(Point2f(5, 4)); + + double area0 = contourArea(contour); + vector approx; + approxPolyDP(contour, approx, 5, true); + double area1 = contourArea(approx); + + cout << "area0 =" << area0 << endl << + "area1 =" << area1 << endl << + "approx poly vertices" << approx.size() << endl; +@endcode +@param contour Input vector of 2D points (contour vertices), stored in std::vector or Mat. +@param oriented Oriented area flag. If it is true, the function returns a signed area value, +depending on the contour orientation (clockwise or counter-clockwise). Using this feature you can +determine orientation of a contour by taking the sign of an area. By default, the parameter is +false, which means that the absolute value is returned. + */ +CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false ); + +/** @brief Finds a rotated rectangle of the minimum area enclosing the input 2D point set. + +The function calculates and returns the minimum-area bounding rectangle (possibly rotated) for a +specified point set. Developer should keep in mind that the returned RotatedRect can contain negative +indices when data is close to the containing Mat element boundary. + +@param points Input vector of 2D points, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect minAreaRect( InputArray points ); + +/** @brief Finds the four vertices of a rotated rect. Useful to draw the rotated rectangle. + +The function finds the four vertices of a rotated rectangle. This function is useful to draw the +rectangle. In C++, instead of using this function, you can directly use RotatedRect::points method. Please +visit the @ref tutorial_bounding_rotated_ellipses "tutorial on Creating Bounding rotated boxes and ellipses for contours" for more information. + +@param box The input rotated rectangle. It may be the output of +@param points The output array of four vertices of rectangles. + */ +CV_EXPORTS_W void boxPoints(RotatedRect box, OutputArray points); + +/** @brief Finds a circle of the minimum area enclosing a 2D point set. + +The function finds the minimal enclosing circle of a 2D point set using an iterative algorithm. + +@param points Input vector of 2D points, stored in std::vector\<\> or Mat +@param center Output center of the circle. +@param radius Output radius of the circle. + */ +CV_EXPORTS_W void minEnclosingCircle( InputArray points, + CV_OUT Point2f& center, CV_OUT float& radius ); + +/** @example samples/cpp/minarea.cpp +*/ + +/** @brief Finds a triangle of minimum area enclosing a 2D point set and returns its area. + +The function finds a triangle of minimum area enclosing the given set of 2D points and returns its +area. The output for a given 2D point set is shown in the image below. 2D points are depicted in +*red* and the enclosing triangle in *yellow*. + +![Sample output of the minimum enclosing triangle function](pics/minenclosingtriangle.png) + +The implementation of the algorithm is based on O'Rourke's @cite ORourke86 and Klee and Laskowski's +@cite KleeLaskowski85 papers. O'Rourke provides a \f$\theta(n)\f$ algorithm for finding the minimal +enclosing triangle of a 2D convex polygon with n vertices. Since the #minEnclosingTriangle function +takes a 2D point set as input an additional preprocessing step of computing the convex hull of the +2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which is higher +than \f$\theta(n)\f$. Thus the overall complexity of the function is \f$O(n log(n))\f$. + +@param points Input vector of 2D points with depth CV_32S or CV_32F, stored in std::vector\<\> or Mat +@param triangle Output vector of three 2D points defining the vertices of the triangle. The depth +of the OutputArray must be CV_32F. + */ +CV_EXPORTS_W double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle ); + +/** @brief Compares two shapes. + +The function compares two shapes. All three implemented methods use the Hu invariants (see #HuMoments) + +@param contour1 First contour or grayscale image. +@param contour2 Second contour or grayscale image. +@param method Comparison method, see #ShapeMatchModes +@param parameter Method-specific parameter (not supported now). + */ +CV_EXPORTS_W double matchShapes( InputArray contour1, InputArray contour2, + int method, double parameter ); + +/** @example samples/cpp/convexhull.cpp +An example using the convexHull functionality +*/ + +/** @brief Finds the convex hull of a point set. + +The function cv::convexHull finds the convex hull of a 2D point set using the Sklansky's algorithm @cite Sklansky82 +that has *O(N logN)* complexity in the current implementation. + +@param points Input 2D point set, stored in std::vector or Mat. +@param hull Output convex hull. It is either an integer vector of indices or vector of points. In +the first case, the hull elements are 0-based indices of the convex hull points in the original +array (since the set of convex hull points is a subset of the original point set). In the second +case, hull elements are the convex hull points themselves. +@param clockwise Orientation flag. If it is true, the output convex hull is oriented clockwise. +Otherwise, it is oriented counter-clockwise. The assumed coordinate system has its X axis pointing +to the right, and its Y axis pointing upwards. +@param returnPoints Operation flag. In case of a matrix, when the flag is true, the function +returns convex hull points. Otherwise, it returns indices of the convex hull points. When the +output array is std::vector, the flag is ignored, and the output depends on the type of the +vector: std::vector\ implies returnPoints=false, std::vector\ implies +returnPoints=true. + +@note `points` and `hull` should be different arrays, inplace processing isn't supported. + +Check @ref tutorial_hull "the corresponding tutorial" for more details. + +useful links: + +https://www.learnopencv.com/convex-hull-using-opencv-in-python-and-c/ + */ +CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull, + bool clockwise = false, bool returnPoints = true ); + +/** @brief Finds the convexity defects of a contour. + +The figure below displays convexity defects of a hand contour: + +![image](pics/defects.png) + +@param contour Input contour. +@param convexhull Convex hull obtained using convexHull that should contain indices of the contour +points that make the hull. +@param convexityDefects The output vector of convexity defects. In C++ and the new Python/Java +interface each convexity defect is represented as 4-element integer vector (a.k.a. #Vec4i): +(start_index, end_index, farthest_pt_index, fixpt_depth), where indices are 0-based indices +in the original contour of the convexity defect beginning, end and the farthest point, and +fixpt_depth is fixed-point approximation (with 8 fractional bits) of the distance between the +farthest contour point and the hull. That is, to get the floating-point value of the depth will be +fixpt_depth/256.0. + */ +CV_EXPORTS_W void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects ); + +/** @brief Tests a contour convexity. + +The function tests whether the input contour is convex or not. The contour must be simple, that is, +without self-intersections. Otherwise, the function output is undefined. + +@param contour Input vector of 2D points, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W bool isContourConvex( InputArray contour ); + +/** @example samples/cpp/intersectExample.cpp +Examples of how intersectConvexConvex works +*/ + +/** @brief Finds intersection of two convex polygons + +@param _p1 First polygon +@param _p2 Second polygon +@param _p12 Output polygon describing the intersecting area +@param handleNested When true, an intersection is found if one of the polygons is fully enclosed in the other. +When false, no intersection is found. If the polygons share a side or the vertex of one polygon lies on an edge +of the other, they are not considered nested and an intersection will be found regardless of the value of handleNested. + +@returns Absolute value of area of intersecting polygon + +@note intersectConvexConvex doesn't confirm that both polygons are convex and will return invalid results if they aren't. + */ +CV_EXPORTS_W float intersectConvexConvex( InputArray _p1, InputArray _p2, + OutputArray _p12, bool handleNested = true ); + +/** @example samples/cpp/fitellipse.cpp +An example using the fitEllipse technique +*/ + +/** @brief Fits an ellipse around a set of 2D points. + +The function calculates the ellipse that fits (in a least-squares sense) a set of 2D points best of +all. It returns the rotated rectangle in which the ellipse is inscribed. The first algorithm described by @cite Fitzgibbon95 +is used. Developer should keep in mind that it is possible that the returned +ellipse/rotatedRect data contains negative indices, due to the data points being close to the +border of the containing Mat element. + +@param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipse( InputArray points ); + +/** @brief Fits an ellipse around a set of 2D points. + + The function calculates the ellipse that fits a set of 2D points. + It returns the rotated rectangle in which the ellipse is inscribed. + The Approximate Mean Square (AMS) proposed by @cite Taubin1991 is used. + + For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$, + which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$. + However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$, + the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines, + quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits. + If the fit is found to be a parabolic or hyperbolic function then the standard #fitEllipse method is used. + The AMS method restricts the fit to parabolic, hyperbolic and elliptical curves + by imposing the condition that \f$ A^T ( D_x^T D_x + D_y^T D_y) A = 1 \f$ where + the matrices \f$ Dx \f$ and \f$ Dy \f$ are the partial derivatives of the design matrix \f$ D \f$ with + respect to x and y. The matrices are formed row by row applying the following to + each of the points in the set: + \f{align*}{ + D(i,:)&=\left\{x_i^2, x_i y_i, y_i^2, x_i, y_i, 1\right\} & + D_x(i,:)&=\left\{2 x_i,y_i,0,1,0,0\right\} & + D_y(i,:)&=\left\{0,x_i,2 y_i,0,1,0\right\} + \f} + The AMS method minimizes the cost function + \f{equation*}{ + \epsilon ^2=\frac{ A^T D^T D A }{ A^T (D_x^T D_x + D_y^T D_y) A^T } + \f} + + The minimum cost is found by solving the generalized eigenvalue problem. + + \f{equation*}{ + D^T D A = \lambda \left( D_x^T D_x + D_y^T D_y\right) A + \f} + + @param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipseAMS( InputArray points ); + + +/** @brief Fits an ellipse around a set of 2D points. + + The function calculates the ellipse that fits a set of 2D points. + It returns the rotated rectangle in which the ellipse is inscribed. + The Direct least square (Direct) method by @cite Fitzgibbon1999 is used. + + For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$, + which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$. + However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$, + the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines, + quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits. + The Direct method confines the fit to ellipses by ensuring that \f$ 4 A_{xx} A_{yy}- A_{xy}^2 > 0 \f$. + The condition imposed is that \f$ 4 A_{xx} A_{yy}- A_{xy}^2=1 \f$ which satisfies the inequality + and as the coefficients can be arbitrarily scaled is not overly restrictive. + + \f{equation*}{ + \epsilon ^2= A^T D^T D A \quad \text{with} \quad A^T C A =1 \quad \text{and} \quad C=\left(\begin{matrix} + 0 & 0 & 2 & 0 & 0 & 0 \\ + 0 & -1 & 0 & 0 & 0 & 0 \\ + 2 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 + \end{matrix} \right) + \f} + + The minimum cost is found by solving the generalized eigenvalue problem. + + \f{equation*}{ + D^T D A = \lambda \left( C\right) A + \f} + + The system produces only one positive eigenvalue \f$ \lambda\f$ which is chosen as the solution + with its eigenvector \f$\mathbf{u}\f$. These are used to find the coefficients + + \f{equation*}{ + A = \sqrt{\frac{1}{\mathbf{u}^T C \mathbf{u}}} \mathbf{u} + \f} + The scaling factor guarantees that \f$A^T C A =1\f$. + + @param points Input 2D point set, stored in std::vector\<\> or Mat + */ +CV_EXPORTS_W RotatedRect fitEllipseDirect( InputArray points ); + +/** @brief Fits a line to a 2D or 3D point set. + +The function fitLine fits a line to a 2D or 3D point set by minimizing \f$\sum_i \rho(r_i)\f$ where +\f$r_i\f$ is a distance between the \f$i^{th}\f$ point, the line and \f$\rho(r)\f$ is a distance function, one +of the following: +- DIST_L2 +\f[\rho (r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}\f] +- DIST_L1 +\f[\rho (r) = r\f] +- DIST_L12 +\f[\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)\f] +- DIST_FAIR +\f[\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998\f] +- DIST_WELSCH +\f[\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846\f] +- DIST_HUBER +\f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f] + +The algorithm is based on the M-estimator ( ) technique +that iteratively fits the line using the weighted least-squares algorithm. After each iteration the +weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . + +@param points Input vector of 2D or 3D points, stored in std::vector\<\> or Mat. +@param line Output line parameters. In case of 2D fitting, it should be a vector of 4 elements +(like Vec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized vector collinear to the line and +(x0, y0) is a point on the line. In case of 3D fitting, it should be a vector of 6 elements (like +Vec6f) - (vx, vy, vz, x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line +and (x0, y0, z0) is a point on the line. +@param distType Distance used by the M-estimator, see #DistanceTypes +@param param Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value +is chosen. +@param reps Sufficient accuracy for the radius (distance between the coordinate origin and the line). +@param aeps Sufficient accuracy for the angle. 0.01 would be a good default value for reps and aeps. + */ +CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType, + double param, double reps, double aeps ); + +/** @brief Performs a point-in-contour test. + +The function determines whether the point is inside a contour, outside, or lies on an edge (or +coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge) +value, correspondingly. When measureDist=false , the return value is +1, -1, and 0, respectively. +Otherwise, the return value is a signed distance between the point and the nearest contour edge. + +See below a sample output of the function where each image pixel is tested against the contour: + +![sample output](pics/pointpolygon.png) + +@param contour Input contour. +@param pt Point tested against the contour. +@param measureDist If true, the function estimates the signed distance from the point to the +nearest contour edge. Otherwise, the function only checks if the point is inside a contour or not. + */ +CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist ); + +/** @brief Finds out if there is any intersection between two rotated rectangles. + +If there is then the vertices of the intersecting region are returned as well. + +Below are some examples of intersection configurations. The hatched pattern indicates the +intersecting region and the red vertices are returned by the function. + +![intersection examples](pics/intersection.png) + +@param rect1 First rectangle +@param rect2 Second rectangle +@param intersectingRegion The output array of the vertices of the intersecting region. It returns +at most 8 vertices. Stored as std::vector\ or cv::Mat as Mx1 of type CV_32FC2. +@returns One of #RectanglesIntersectTypes + */ +CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); + +/** @brief Creates a smart pointer to a cv::GeneralizedHoughBallard class and initializes it. +*/ +CV_EXPORTS_W Ptr createGeneralizedHoughBallard(); + +/** @brief Creates a smart pointer to a cv::GeneralizedHoughGuil class and initializes it. +*/ +CV_EXPORTS_W Ptr createGeneralizedHoughGuil(); + +//! @} imgproc_shape + +//! @addtogroup imgproc_colormap +//! @{ + +//! GNU Octave/MATLAB equivalent colormaps +enum ColormapTypes +{ + COLORMAP_AUTUMN = 0, //!< ![autumn](pics/colormaps/colorscale_autumn.jpg) + COLORMAP_BONE = 1, //!< ![bone](pics/colormaps/colorscale_bone.jpg) + COLORMAP_JET = 2, //!< ![jet](pics/colormaps/colorscale_jet.jpg) + COLORMAP_WINTER = 3, //!< ![winter](pics/colormaps/colorscale_winter.jpg) + COLORMAP_RAINBOW = 4, //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg) + COLORMAP_OCEAN = 5, //!< ![ocean](pics/colormaps/colorscale_ocean.jpg) + COLORMAP_SUMMER = 6, //!< ![summer](pics/colormaps/colorscale_summer.jpg) + COLORMAP_SPRING = 7, //!< ![spring](pics/colormaps/colorscale_spring.jpg) + COLORMAP_COOL = 8, //!< ![cool](pics/colormaps/colorscale_cool.jpg) + COLORMAP_HSV = 9, //!< ![HSV](pics/colormaps/colorscale_hsv.jpg) + COLORMAP_PINK = 10, //!< ![pink](pics/colormaps/colorscale_pink.jpg) + COLORMAP_HOT = 11, //!< ![hot](pics/colormaps/colorscale_hot.jpg) + COLORMAP_PARULA = 12, //!< ![parula](pics/colormaps/colorscale_parula.jpg) + COLORMAP_MAGMA = 13, //!< ![magma](pics/colormaps/colorscale_magma.jpg) + COLORMAP_INFERNO = 14, //!< ![inferno](pics/colormaps/colorscale_inferno.jpg) + COLORMAP_PLASMA = 15, //!< ![plasma](pics/colormaps/colorscale_plasma.jpg) + COLORMAP_VIRIDIS = 16, //!< ![viridis](pics/colormaps/colorscale_viridis.jpg) + COLORMAP_CIVIDIS = 17, //!< ![cividis](pics/colormaps/colorscale_cividis.jpg) + COLORMAP_TWILIGHT = 18, //!< ![twilight](pics/colormaps/colorscale_twilight.jpg) + COLORMAP_TWILIGHT_SHIFTED = 19, //!< ![twilight shifted](pics/colormaps/colorscale_twilight_shifted.jpg) + COLORMAP_TURBO = 20, //!< ![turbo](pics/colormaps/colorscale_turbo.jpg) + COLORMAP_DEEPGREEN = 21 //!< ![deepgreen](pics/colormaps/colorscale_deepgreen.jpg) +}; + +/** @example samples/cpp/falsecolor.cpp +An example using applyColorMap function +*/ + +/** @brief Applies a GNU Octave/MATLAB equivalent colormap on a given image. + +@param src The source image, grayscale or colored of type CV_8UC1 or CV_8UC3. +@param dst The result is the colormapped source image. Note: Mat::create is called on dst. +@param colormap The colormap to apply, see #ColormapTypes +*/ +CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, int colormap); + +/** @brief Applies a user colormap on a given image. + +@param src The source image, grayscale or colored of type CV_8UC1 or CV_8UC3. +@param dst The result is the colormapped source image. Note: Mat::create is called on dst. +@param userColor The colormap to apply of type CV_8UC1 or CV_8UC3 and size 256 +*/ +CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, InputArray userColor); + +//! @} imgproc_colormap + +//! @addtogroup imgproc_draw +//! @{ + + +/** OpenCV color channel order is BGR[A] */ +#define CV_RGB(r, g, b) cv::Scalar((b), (g), (r), 0) + +/** @brief Draws a line segment connecting two points. + +The function line draws the line segment between pt1 and pt2 points in the image. The line is +clipped by the image boundaries. For non-antialiased lines with integer coordinates, the 8-connected +or 4-connected Bresenham algorithm is used. Thick lines are drawn with rounding endings. Antialiased +lines are drawn using Gaussian filtering. + +@param img Image. +@param pt1 First point of the line segment. +@param pt2 Second point of the line segment. +@param color Line color. +@param thickness Line thickness. +@param lineType Type of the line. See #LineTypes. +@param shift Number of fractional bits in the point coordinates. + */ +CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0); + +/** @brief Draws a arrow segment pointing from the first point to the second one. + +The function cv::arrowedLine draws an arrow between pt1 and pt2 points in the image. See also #line. + +@param img Image. +@param pt1 The point the arrow starts from. +@param pt2 The point the arrow points to. +@param color Line color. +@param thickness Line thickness. +@param line_type Type of the line. See #LineTypes +@param shift Number of fractional bits in the point coordinates. +@param tipLength The length of the arrow tip in relation to the arrow length + */ +CV_EXPORTS_W void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness=1, int line_type=8, int shift=0, double tipLength=0.1); + +/** @brief Draws a simple, thick, or filled up-right rectangle. + +The function cv::rectangle draws a rectangle outline or a filled rectangle whose two opposite corners +are pt1 and pt2. + +@param img Image. +@param pt1 Vertex of the rectangle. +@param pt2 Vertex of the rectangle opposite to pt1 . +@param color Rectangle color or brightness (grayscale image). +@param thickness Thickness of lines that make up the rectangle. Negative values, like #FILLED, +mean that the function has to draw a filled rectangle. +@param lineType Type of the line. See #LineTypes +@param shift Number of fractional bits in the point coordinates. + */ +CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @overload + +use `rec` parameter as alternative specification of the drawn rectangle: `r.tl() and +r.br()-Point(1,1)` are opposite corners +*/ +CV_EXPORTS void rectangle(CV_IN_OUT Mat& img, Rect rec, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @example samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_2.cpp +An example using drawing functions +*/ + +/** @brief Draws a circle. + +The function cv::circle draws a simple or filled circle with a given center and radius. +@param img Image where the circle is drawn. +@param center Center of the circle. +@param radius Radius of the circle. +@param color Circle color. +@param thickness Thickness of the circle outline, if positive. Negative values, like #FILLED, +mean that a filled circle is to be drawn. +@param lineType Type of the circle boundary. See #LineTypes +@param shift Number of fractional bits in the coordinates of the center and in the radius value. + */ +CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @brief Draws a simple or thick elliptic arc or fills an ellipse sector. + +The function cv::ellipse with more parameters draws an ellipse outline, a filled ellipse, an elliptic +arc, or a filled ellipse sector. The drawing code uses general parametric form. +A piecewise-linear curve is used to approximate the elliptic arc +boundary. If you need more control of the ellipse rendering, you can retrieve the curve using +#ellipse2Poly and then render it with #polylines or fill it with #fillPoly. If you use the first +variant of the function and want to draw the whole ellipse, not an arc, pass `startAngle=0` and +`endAngle=360`. If `startAngle` is greater than `endAngle`, they are swapped. The figure below explains +the meaning of the parameters to draw the blue arc. + +![Parameters of Elliptic Arc](pics/ellipse.svg) + +@param img Image. +@param center Center of the ellipse. +@param axes Half of the size of the ellipse main axes. +@param angle Ellipse rotation angle in degrees. +@param startAngle Starting angle of the elliptic arc in degrees. +@param endAngle Ending angle of the elliptic arc in degrees. +@param color Ellipse color. +@param thickness Thickness of the ellipse arc outline, if positive. Otherwise, this indicates that +a filled ellipse sector is to be drawn. +@param lineType Type of the ellipse boundary. See #LineTypes +@param shift Number of fractional bits in the coordinates of the center and values of axes. + */ +CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes, + double angle, double startAngle, double endAngle, + const Scalar& color, int thickness = 1, + int lineType = LINE_8, int shift = 0); + +/** @overload +@param img Image. +@param box Alternative ellipse representation via RotatedRect. This means that the function draws +an ellipse inscribed in the rotated rectangle. +@param color Ellipse color. +@param thickness Thickness of the ellipse arc outline, if positive. Otherwise, this indicates that +a filled ellipse sector is to be drawn. +@param lineType Type of the ellipse boundary. See #LineTypes +*/ +CV_EXPORTS_W void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color, + int thickness = 1, int lineType = LINE_8); + +/* ----------------------------------------------------------------------------------------- */ +/* ADDING A SET OF PREDEFINED MARKERS WHICH COULD BE USED TO HIGHLIGHT POSITIONS IN AN IMAGE */ +/* ----------------------------------------------------------------------------------------- */ + +//! Possible set of marker types used for the cv::drawMarker function +enum MarkerTypes +{ + MARKER_CROSS = 0, //!< A crosshair marker shape + MARKER_TILTED_CROSS = 1, //!< A 45 degree tilted crosshair marker shape + MARKER_STAR = 2, //!< A star marker shape, combination of cross and tilted cross + MARKER_DIAMOND = 3, //!< A diamond marker shape + MARKER_SQUARE = 4, //!< A square marker shape + MARKER_TRIANGLE_UP = 5, //!< An upwards pointing triangle marker shape + MARKER_TRIANGLE_DOWN = 6 //!< A downwards pointing triangle marker shape +}; + +/** @brief Draws a marker on a predefined position in an image. + +The function cv::drawMarker draws a marker on a given position in the image. For the moment several +marker types are supported, see #MarkerTypes for more information. + +@param img Image. +@param position The point where the crosshair is positioned. +@param color Line color. +@param markerType The specific type of marker you want to use, see #MarkerTypes +@param thickness Line thickness. +@param line_type Type of the line, See #LineTypes +@param markerSize The length of the marker axis [default = 20 pixels] + */ +CV_EXPORTS_W void drawMarker(CV_IN_OUT Mat& img, Point position, const Scalar& color, + int markerType = MARKER_CROSS, int markerSize=20, int thickness=1, + int line_type=8); + +/* ----------------------------------------------------------------------------------------- */ +/* END OF MARKER SECTION */ +/* ----------------------------------------------------------------------------------------- */ + +/** @overload */ +CV_EXPORTS void fillConvexPoly(Mat& img, const Point* pts, int npts, + const Scalar& color, int lineType = LINE_8, + int shift = 0); + +/** @brief Fills a convex polygon. + +The function cv::fillConvexPoly draws a filled convex polygon. This function is much faster than the +function #fillPoly . It can fill not only convex polygons but any monotonic polygon without +self-intersections, that is, a polygon whose contour intersects every horizontal line (scan line) +twice at the most (though, its top-most and/or the bottom edge could be horizontal). + +@param img Image. +@param points Polygon vertices. +@param color Polygon color. +@param lineType Type of the polygon boundaries. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. + */ +CV_EXPORTS_W void fillConvexPoly(InputOutputArray img, InputArray points, + const Scalar& color, int lineType = LINE_8, + int shift = 0); + +/** @overload */ +CV_EXPORTS void fillPoly(Mat& img, const Point** pts, + const int* npts, int ncontours, + const Scalar& color, int lineType = LINE_8, int shift = 0, + Point offset = Point() ); + +/** @example samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp +An example using drawing functions +Check @ref tutorial_random_generator_and_text "the corresponding tutorial" for more details +*/ + +/** @brief Fills the area bounded by one or more polygons. + +The function cv::fillPoly fills an area bounded by several polygonal contours. The function can fill +complex areas, for example, areas with holes, contours with self-intersections (some of their +parts), and so forth. + +@param img Image. +@param pts Array of polygons where each polygon is represented as an array of points. +@param color Polygon color. +@param lineType Type of the polygon boundaries. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. +@param offset Optional offset of all points of the contours. + */ +CV_EXPORTS_W void fillPoly(InputOutputArray img, InputArrayOfArrays pts, + const Scalar& color, int lineType = LINE_8, int shift = 0, + Point offset = Point() ); + +/** @overload */ +CV_EXPORTS void polylines(Mat& img, const Point* const* pts, const int* npts, + int ncontours, bool isClosed, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0 ); + +/** @brief Draws several polygonal curves. + +@param img Image. +@param pts Array of polygonal curves. +@param isClosed Flag indicating whether the drawn polylines are closed or not. If they are closed, +the function draws a line from the last vertex of each curve to its first vertex. +@param color Polyline color. +@param thickness Thickness of the polyline edges. +@param lineType Type of the line segments. See #LineTypes +@param shift Number of fractional bits in the vertex coordinates. + +The function cv::polylines draws one or more polygonal curves. + */ +CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts, + bool isClosed, const Scalar& color, + int thickness = 1, int lineType = LINE_8, int shift = 0 ); + +/** @example samples/cpp/contours2.cpp +An example program illustrates the use of cv::findContours and cv::drawContours +\image html WindowsQtContoursOutput.png "Screenshot of the program" +*/ + +/** @example samples/cpp/segment_objects.cpp +An example using drawContours to clean up a background segmentation result +*/ + +/** @brief Draws contours outlines or filled contours. + +The function draws contour outlines in the image if \f$\texttt{thickness} \ge 0\f$ or fills the area +bounded by the contours if \f$\texttt{thickness}<0\f$ . The example below shows how to retrieve +connected components from the binary image and label them: : +@include snippets/imgproc_drawContours.cpp + +@param image Destination image. +@param contours All the input contours. Each contour is stored as a point vector. +@param contourIdx Parameter indicating a contour to draw. If it is negative, all the contours are drawn. +@param color Color of the contours. +@param thickness Thickness of lines the contours are drawn with. If it is negative (for example, +thickness=#FILLED ), the contour interiors are drawn. +@param lineType Line connectivity. See #LineTypes +@param hierarchy Optional information about hierarchy. It is only needed if you want to draw only +some of the contours (see maxLevel ). +@param maxLevel Maximal level for drawn contours. If it is 0, only the specified contour is drawn. +If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function +draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This +parameter is only taken into account when there is hierarchy available. +@param offset Optional contour shift parameter. Shift all the drawn contours by the specified +\f$\texttt{offset}=(dx,dy)\f$ . +@note When thickness=#FILLED, the function is designed to handle connected components with holes correctly +even when no hierarchy date is provided. This is done by analyzing all the outlines together +using even-odd rule. This may give incorrect results if you have a joint collection of separately retrieved +contours. In order to solve this problem, you need to call #drawContours separately for each sub-group +of contours, or iterate over the collection using contourIdx parameter. + */ +CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours, + int contourIdx, const Scalar& color, + int thickness = 1, int lineType = LINE_8, + InputArray hierarchy = noArray(), + int maxLevel = INT_MAX, Point offset = Point() ); + +/** @brief Clips the line against the image rectangle. + +The function cv::clipLine calculates a part of the line segment that is entirely within the specified +rectangle. it returns false if the line segment is completely outside the rectangle. Otherwise, +it returns true . +@param imgSize Image size. The image rectangle is Rect(0, 0, imgSize.width, imgSize.height) . +@param pt1 First line point. +@param pt2 Second line point. + */ +CV_EXPORTS bool clipLine(Size imgSize, CV_IN_OUT Point& pt1, CV_IN_OUT Point& pt2); + +/** @overload +@param imgSize Image size. The image rectangle is Rect(0, 0, imgSize.width, imgSize.height) . +@param pt1 First line point. +@param pt2 Second line point. +*/ +CV_EXPORTS bool clipLine(Size2l imgSize, CV_IN_OUT Point2l& pt1, CV_IN_OUT Point2l& pt2); + +/** @overload +@param imgRect Image rectangle. +@param pt1 First line point. +@param pt2 Second line point. +*/ +CV_EXPORTS_W bool clipLine(Rect imgRect, CV_OUT CV_IN_OUT Point& pt1, CV_OUT CV_IN_OUT Point& pt2); + +/** @brief Approximates an elliptic arc with a polyline. + +The function ellipse2Poly computes the vertices of a polyline that approximates the specified +elliptic arc. It is used by #ellipse. If `arcStart` is greater than `arcEnd`, they are swapped. + +@param center Center of the arc. +@param axes Half of the size of the ellipse main axes. See #ellipse for details. +@param angle Rotation angle of the ellipse in degrees. See #ellipse for details. +@param arcStart Starting angle of the elliptic arc in degrees. +@param arcEnd Ending angle of the elliptic arc in degrees. +@param delta Angle between the subsequent polyline vertices. It defines the approximation +accuracy. +@param pts Output vector of polyline vertices. + */ +CV_EXPORTS_W void ellipse2Poly( Point center, Size axes, int angle, + int arcStart, int arcEnd, int delta, + CV_OUT std::vector& pts ); + +/** @overload +@param center Center of the arc. +@param axes Half of the size of the ellipse main axes. See #ellipse for details. +@param angle Rotation angle of the ellipse in degrees. See #ellipse for details. +@param arcStart Starting angle of the elliptic arc in degrees. +@param arcEnd Ending angle of the elliptic arc in degrees. +@param delta Angle between the subsequent polyline vertices. It defines the approximation accuracy. +@param pts Output vector of polyline vertices. +*/ +CV_EXPORTS void ellipse2Poly(Point2d center, Size2d axes, int angle, + int arcStart, int arcEnd, int delta, + CV_OUT std::vector& pts); + +/** @brief Draws a text string. + +The function cv::putText renders the specified text string in the image. Symbols that cannot be rendered +using the specified font are replaced by question marks. See #getTextSize for a text rendering code +example. + +@param img Image. +@param text Text string to be drawn. +@param org Bottom-left corner of the text string in the image. +@param fontFace Font type, see #HersheyFonts. +@param fontScale Font scale factor that is multiplied by the font-specific base size. +@param color Text color. +@param thickness Thickness of the lines used to draw a text. +@param lineType Line type. See #LineTypes +@param bottomLeftOrigin When true, the image data origin is at the bottom-left corner. Otherwise, +it is at the top-left corner. + */ +CV_EXPORTS_W void putText( InputOutputArray img, const String& text, Point org, + int fontFace, double fontScale, Scalar color, + int thickness = 1, int lineType = LINE_8, + bool bottomLeftOrigin = false ); + +/** @brief Calculates the width and height of a text string. + +The function cv::getTextSize calculates and returns the size of a box that contains the specified text. +That is, the following code renders some text, the tight box surrounding it, and the baseline: : +@code + String text = "Funny text inside the box"; + int fontFace = FONT_HERSHEY_SCRIPT_SIMPLEX; + double fontScale = 2; + int thickness = 3; + + Mat img(600, 800, CV_8UC3, Scalar::all(0)); + + int baseline=0; + Size textSize = getTextSize(text, fontFace, + fontScale, thickness, &baseline); + baseline += thickness; + + // center the text + Point textOrg((img.cols - textSize.width)/2, + (img.rows + textSize.height)/2); + + // draw the box + rectangle(img, textOrg + Point(0, baseline), + textOrg + Point(textSize.width, -textSize.height), + Scalar(0,0,255)); + // ... and the baseline first + line(img, textOrg + Point(0, thickness), + textOrg + Point(textSize.width, thickness), + Scalar(0, 0, 255)); + + // then put the text itself + putText(img, text, textOrg, fontFace, fontScale, + Scalar::all(255), thickness, 8); +@endcode + +@param text Input text string. +@param fontFace Font to use, see #HersheyFonts. +@param fontScale Font scale factor that is multiplied by the font-specific base size. +@param thickness Thickness of lines used to render the text. See #putText for details. +@param[out] baseLine y-coordinate of the baseline relative to the bottom-most text +point. +@return The size of a box that contains the specified text. + +@see putText + */ +CV_EXPORTS_W Size getTextSize(const String& text, int fontFace, + double fontScale, int thickness, + CV_OUT int* baseLine); + + +/** @brief Calculates the font-specific size to use to achieve a given height in pixels. + +@param fontFace Font to use, see cv::HersheyFonts. +@param pixelHeight Pixel height to compute the fontScale for +@param thickness Thickness of lines used to render the text.See putText for details. +@return The fontSize to use for cv::putText + +@see cv::putText +*/ +CV_EXPORTS_W double getFontScaleFromHeight(const int fontFace, + const int pixelHeight, + const int thickness = 1); + +/** @brief Line iterator + +The class is used to iterate over all the pixels on the raster line +segment connecting two specified points. + +The class LineIterator is used to get each pixel of a raster line. It +can be treated as versatile implementation of the Bresenham algorithm +where you can stop at each pixel and do some extra processing, for +example, grab pixel values along the line or draw a line with an effect +(for example, with XOR operation). + +The number of pixels along the line is stored in LineIterator::count. +The method LineIterator::pos returns the current position in the image: + +@code{.cpp} +// grabs pixels along the line (pt1, pt2) +// from 8-bit 3-channel image to the buffer +LineIterator it(img, pt1, pt2, 8); +LineIterator it2 = it; +vector buf(it.count); + +for(int i = 0; i < it.count; i++, ++it) + buf[i] = *(const Vec3b*)*it; + +// alternative way of iterating through the line +for(int i = 0; i < it2.count; i++, ++it2) +{ + Vec3b val = img.at(it2.pos()); + CV_Assert(buf[i] == val); +} +@endcode +*/ +class CV_EXPORTS LineIterator +{ +public: + /** @brief initializes the iterator + + creates iterators for the line connecting pt1 and pt2 + the line will be clipped on the image boundaries + the line is 8-connected or 4-connected + If leftToRight=true, then the iteration is always done + from the left-most point to the right most, + not to depend on the ordering of pt1 and pt2 parameters + */ + LineIterator( const Mat& img, Point pt1, Point pt2, + int connectivity = 8, bool leftToRight = false ); + /** @brief returns pointer to the current pixel + */ + uchar* operator *(); + /** @brief prefix increment operator (++it). shifts iterator to the next pixel + */ + LineIterator& operator ++(); + /** @brief postfix increment operator (it++). shifts iterator to the next pixel + */ + LineIterator operator ++(int); + /** @brief returns coordinates of the current pixel + */ + Point pos() const; + + uchar* ptr; + const uchar* ptr0; + int step, elemSize; + int err, count; + int minusDelta, plusDelta; + int minusStep, plusStep; +}; + +//! @cond IGNORED + +// === LineIterator implementation === + +inline +uchar* LineIterator::operator *() +{ + return ptr; +} + +inline +LineIterator& LineIterator::operator ++() +{ + int mask = err < 0 ? -1 : 0; + err += minusDelta + (plusDelta & mask); + ptr += minusStep + (plusStep & mask); + return *this; +} + +inline +LineIterator LineIterator::operator ++(int) +{ + LineIterator it = *this; + ++(*this); + return it; +} + +inline +Point LineIterator::pos() const +{ + Point p; + p.y = (int)((ptr - ptr0)/step); + p.x = (int)(((ptr - ptr0) - p.y*step)/elemSize); + return p; +} + +//! @endcond + +//! @} imgproc_draw + +//! @} imgproc + +} // cv + +#ifndef DISABLE_OPENCV_24_COMPATIBILITY +#include "opencv2/imgproc/imgproc_c.h" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/detail/distortion_model.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/detail/distortion_model.hpp new file mode 100644 index 0000000..a9c3dde --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/detail/distortion_model.hpp @@ -0,0 +1,123 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP +#define OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP + +//! @cond IGNORED + +namespace cv { namespace detail { +/** +Computes the matrix for the projection onto a tilted image sensor +\param tauX angular parameter rotation around x-axis +\param tauY angular parameter rotation around y-axis +\param matTilt if not NULL returns the matrix +\f[ +\vecthreethree{R_{33}(\tau_x, \tau_y)}{0}{-R_{13}((\tau_x, \tau_y)} +{0}{R_{33}(\tau_x, \tau_y)}{-R_{23}(\tau_x, \tau_y)} +{0}{0}{1} R(\tau_x, \tau_y) +\f] +where +\f[ +R(\tau_x, \tau_y) = +\vecthreethree{\cos(\tau_y)}{0}{-\sin(\tau_y)}{0}{1}{0}{\sin(\tau_y)}{0}{\cos(\tau_y)} +\vecthreethree{1}{0}{0}{0}{\cos(\tau_x)}{\sin(\tau_x)}{0}{-\sin(\tau_x)}{\cos(\tau_x)} = +\vecthreethree{\cos(\tau_y)}{\sin(\tau_y)\sin(\tau_x)}{-\sin(\tau_y)\cos(\tau_x)} +{0}{\cos(\tau_x)}{\sin(\tau_x)} +{\sin(\tau_y)}{-\cos(\tau_y)\sin(\tau_x)}{\cos(\tau_y)\cos(\tau_x)}. +\f] +\param dMatTiltdTauX if not NULL it returns the derivative of matTilt with +respect to \f$\tau_x\f$. +\param dMatTiltdTauY if not NULL it returns the derivative of matTilt with +respect to \f$\tau_y\f$. +\param invMatTilt if not NULL it returns the inverse of matTilt +**/ +template +void computeTiltProjectionMatrix(FLOAT tauX, + FLOAT tauY, + Matx* matTilt = 0, + Matx* dMatTiltdTauX = 0, + Matx* dMatTiltdTauY = 0, + Matx* invMatTilt = 0) +{ + FLOAT cTauX = cos(tauX); + FLOAT sTauX = sin(tauX); + FLOAT cTauY = cos(tauY); + FLOAT sTauY = sin(tauY); + Matx matRotX = Matx(1,0,0,0,cTauX,sTauX,0,-sTauX,cTauX); + Matx matRotY = Matx(cTauY,0,-sTauY,0,1,0,sTauY,0,cTauY); + Matx matRotXY = matRotY * matRotX; + Matx matProjZ = Matx(matRotXY(2,2),0,-matRotXY(0,2),0,matRotXY(2,2),-matRotXY(1,2),0,0,1); + if (matTilt) + { + // Matrix for trapezoidal distortion of tilted image sensor + *matTilt = matProjZ * matRotXY; + } + if (dMatTiltdTauX) + { + // Derivative with respect to tauX + Matx dMatRotXYdTauX = matRotY * Matx(0,0,0,0,-sTauX,cTauX,0,-cTauX,-sTauX); + Matx dMatProjZdTauX = Matx(dMatRotXYdTauX(2,2),0,-dMatRotXYdTauX(0,2), + 0,dMatRotXYdTauX(2,2),-dMatRotXYdTauX(1,2),0,0,0); + *dMatTiltdTauX = (matProjZ * dMatRotXYdTauX) + (dMatProjZdTauX * matRotXY); + } + if (dMatTiltdTauY) + { + // Derivative with respect to tauY + Matx dMatRotXYdTauY = Matx(-sTauY,0,-cTauY,0,0,0,cTauY,0,-sTauY) * matRotX; + Matx dMatProjZdTauY = Matx(dMatRotXYdTauY(2,2),0,-dMatRotXYdTauY(0,2), + 0,dMatRotXYdTauY(2,2),-dMatRotXYdTauY(1,2),0,0,0); + *dMatTiltdTauY = (matProjZ * dMatRotXYdTauY) + (dMatProjZdTauY * matRotXY); + } + if (invMatTilt) + { + FLOAT inv = 1./matRotXY(2,2); + Matx invMatProjZ = Matx(inv,0,inv*matRotXY(0,2),0,inv,inv*matRotXY(1,2),0,0,1); + *invMatTilt = matRotXY.t()*invMatProjZ; + } +} +}} // namespace detail, cv + + +//! @endcond + +#endif // OPENCV_IMGPROC_DETAIL_DISTORTION_MODEL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/hal.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/hal.hpp new file mode 100644 index 0000000..a435fd6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/hal.hpp @@ -0,0 +1,241 @@ +#ifndef CV_IMGPROC_HAL_HPP +#define CV_IMGPROC_HAL_HPP + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +#include "opencv2/core/hal/interface.h" + +namespace cv { namespace hal { + +//! @addtogroup imgproc_hal_functions +//! @{ + +//--------------------------- +//! @cond IGNORED + +struct CV_EXPORTS Filter2D +{ + CV_DEPRECATED static Ptr create(uchar * , size_t , int , + int , int , + int , int , + int , int , + int , double , + int , int , + bool , bool ); + virtual void apply(uchar * , size_t , + uchar * , size_t , + int , int , + int , int , + int , int ) = 0; + virtual ~Filter2D() {} +}; + +struct CV_EXPORTS SepFilter2D +{ + CV_DEPRECATED static Ptr create(int , int , int , + uchar * , int , + uchar * , int , + int , int , + double , int ); + virtual void apply(uchar * , size_t , + uchar * , size_t , + int , int , + int , int , + int , int ) = 0; + virtual ~SepFilter2D() {} +}; + + +struct CV_EXPORTS Morph +{ + CV_DEPRECATED static Ptr create(int , int , int , int , int , + int , uchar * , size_t , + int , int , + int , int , + int , const double *, + int , bool , bool ); + virtual void apply(uchar * , size_t , uchar * , size_t , int , int , + int , int , int , int , + int , int , int , int ) = 0; + virtual ~Morph() {} +}; + +//! @endcond +//--------------------------- + +CV_EXPORTS void filter2D(int stype, int dtype, int kernel_type, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int full_width, int full_height, + int offset_x, int offset_y, + uchar * kernel_data, size_t kernel_step, + int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + double delta, int borderType, + bool isSubmatrix); + +CV_EXPORTS void sepFilter2D(int stype, int dtype, int ktype, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int full_width, int full_height, + int offset_x, int offset_y, + uchar * kernelx_data, int kernelx_len, + uchar * kernely_data, int kernely_len, + int anchor_x, int anchor_y, + double delta, int borderType); + +CV_EXPORTS void morph(int op, int src_type, int dst_type, + uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2, + int kernel_type, uchar * kernel_data, size_t kernel_step, + int kernel_width, int kernel_height, int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix); + + +CV_EXPORTS void resize(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + double inv_scale_x, double inv_scale_y, int interpolation); + +CV_EXPORTS void warpAffine(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + const double M[6], int interpolation, int borderType, const double borderValue[4]); + +CV_EXPORTS void warpPerspectve(int src_type, + const uchar * src_data, size_t src_step, int src_width, int src_height, + uchar * dst_data, size_t dst_step, int dst_width, int dst_height, + const double M[9], int interpolation, int borderType, const double borderValue[4]); + +CV_EXPORTS void cvtBGRtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, int dcn, bool swapBlue); + +CV_EXPORTS void cvtBGRtoBGR5x5(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int greenBits); + +CV_EXPORTS void cvtBGR5x5toBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int dcn, bool swapBlue, int greenBits); + +CV_EXPORTS void cvtBGRtoGray(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue); + +CV_EXPORTS void cvtGraytoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn); + +CV_EXPORTS void cvtBGR5x5toGray(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int greenBits); + +CV_EXPORTS void cvtGraytoBGR5x5(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int greenBits); +CV_EXPORTS void cvtBGRtoYUV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isCbCr); + +CV_EXPORTS void cvtYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isCbCr); + +CV_EXPORTS void cvtBGRtoXYZ(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue); + +CV_EXPORTS void cvtXYZtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue); + +CV_EXPORTS void cvtBGRtoHSV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isFullRange, bool isHSV); + +CV_EXPORTS void cvtHSVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isFullRange, bool isHSV); + +CV_EXPORTS void cvtBGRtoLab(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int scn, bool swapBlue, bool isLab, bool srgb); + +CV_EXPORTS void cvtLabtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int depth, int dcn, bool swapBlue, bool isLab, bool srgb); + +CV_EXPORTS void cvtTwoPlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +//! Separate Y and UV planes +CV_EXPORTS void cvtTwoPlaneYUVtoBGR(const uchar * y_data, const uchar * uv_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtThreePlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int dst_width, int dst_height, + int dcn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtBGRtoThreePlaneYUV(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int uIdx); + +//! Separate Y and UV planes +CV_EXPORTS void cvtBGRtoTwoPlaneYUV(const uchar * src_data, size_t src_step, + uchar * y_data, uchar * uv_data, size_t dst_step, + int width, int height, + int scn, bool swapBlue, int uIdx); + +CV_EXPORTS void cvtOnePlaneYUVtoBGR(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height, + int dcn, bool swapBlue, int uIdx, int ycn); + +CV_EXPORTS void cvtRGBAtoMultipliedRGBA(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height); + +CV_EXPORTS void cvtMultipliedRGBAtoRGBA(const uchar * src_data, size_t src_step, + uchar * dst_data, size_t dst_step, + int width, int height); + +CV_EXPORTS void integral(int depth, int sdepth, int sqdepth, + const uchar* src, size_t srcstep, + uchar* sum, size_t sumstep, + uchar* sqsum, size_t sqsumstep, + uchar* tilted, size_t tstep, + int width, int height, int cn); + +//! @} + +}} + +#endif // CV_IMGPROC_HAL_HPP diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/interface.h b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/interface.h new file mode 100644 index 0000000..f8dbcfe --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/hal/interface.h @@ -0,0 +1,46 @@ +#ifndef OPENCV_IMGPROC_HAL_INTERFACE_H +#define OPENCV_IMGPROC_HAL_INTERFACE_H + +//! @addtogroup imgproc_hal_interface +//! @{ + +//! @name Interpolation modes +//! @sa cv::InterpolationFlags +//! @{ +#define CV_HAL_INTER_NEAREST 0 +#define CV_HAL_INTER_LINEAR 1 +#define CV_HAL_INTER_CUBIC 2 +#define CV_HAL_INTER_AREA 3 +#define CV_HAL_INTER_LANCZOS4 4 +//! @} + +//! @name Morphology operations +//! @sa cv::MorphTypes +//! @{ +#define CV_HAL_MORPH_ERODE 0 +#define CV_HAL_MORPH_DILATE 1 +//! @} + +//! @name Threshold types +//! @sa cv::ThresholdTypes +//! @{ +#define CV_HAL_THRESH_BINARY 0 +#define CV_HAL_THRESH_BINARY_INV 1 +#define CV_HAL_THRESH_TRUNC 2 +#define CV_HAL_THRESH_TOZERO 3 +#define CV_HAL_THRESH_TOZERO_INV 4 +#define CV_HAL_THRESH_MASK 7 +#define CV_HAL_THRESH_OTSU 8 +#define CV_HAL_THRESH_TRIANGLE 16 +//! @} + +//! @name Adaptive threshold algorithm +//! @sa cv::AdaptiveThresholdTypes +//! @{ +#define CV_HAL_ADAPTIVE_THRESH_MEAN_C 0 +#define CV_HAL_ADAPTIVE_THRESH_GAUSSIAN_C 1 +//! @} + +//! @} + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc.hpp new file mode 100644 index 0000000..4175bd0 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/imgproc.hpp" diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc_c.h new file mode 100644 index 0000000..2fe85b6 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/imgproc_c.h @@ -0,0 +1,1210 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_IMGPROC_C_H +#define OPENCV_IMGPROC_IMGPROC_C_H + +#include "opencv2/imgproc/types_c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup imgproc_c +@{ +*/ + +/*********************** Background statistics accumulation *****************************/ + +/** @brief Adds image to accumulator +@see cv::accumulate +*/ +CVAPI(void) cvAcc( const CvArr* image, CvArr* sum, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds squared image to accumulator +@see cv::accumulateSquare +*/ +CVAPI(void) cvSquareAcc( const CvArr* image, CvArr* sqsum, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds a product of two images to accumulator +@see cv::accumulateProduct +*/ +CVAPI(void) cvMultiplyAcc( const CvArr* image1, const CvArr* image2, CvArr* acc, + const CvArr* mask CV_DEFAULT(NULL) ); + +/** @brief Adds image to accumulator with weights: acc = acc*(1-alpha) + image*alpha +@see cv::accumulateWeighted +*/ +CVAPI(void) cvRunningAvg( const CvArr* image, CvArr* acc, double alpha, + const CvArr* mask CV_DEFAULT(NULL) ); + +/****************************************************************************************\ +* Image Processing * +\****************************************************************************************/ + +/** Copies source 2D array inside of the larger destination array and + makes a border of the specified type (IPL_BORDER_*) around the copied area. */ +CVAPI(void) cvCopyMakeBorder( const CvArr* src, CvArr* dst, CvPoint offset, + int bordertype, CvScalar value CV_DEFAULT(cvScalarAll(0))); + +/** @brief Smooths the image in one of several ways. + +@param src The source image +@param dst The destination image +@param smoothtype Type of the smoothing, see SmoothMethod_c +@param size1 The first parameter of the smoothing operation, the aperture width. Must be a +positive odd number (1, 3, 5, ...) +@param size2 The second parameter of the smoothing operation, the aperture height. Ignored by +CV_MEDIAN and CV_BILATERAL methods. In the case of simple scaled/non-scaled and Gaussian blur if +size2 is zero, it is set to size1. Otherwise it must be a positive odd number. +@param sigma1 In the case of a Gaussian parameter this parameter may specify Gaussian \f$\sigma\f$ +(standard deviation). If it is zero, it is calculated from the kernel size: +\f[\sigma = 0.3 (n/2 - 1) + 0.8 \quad \text{where} \quad n= \begin{array}{l l} \mbox{\texttt{size1} for horizontal kernel} \\ \mbox{\texttt{size2} for vertical kernel} \end{array}\f] +Using standard sigma for small kernels ( \f$3\times 3\f$ to \f$7\times 7\f$ ) gives better speed. If +sigma1 is not zero, while size1 and size2 are zeros, the kernel size is calculated from the +sigma (to provide accurate enough operation). +@param sigma2 additional parameter for bilateral filtering + +@see cv::GaussianBlur, cv::blur, cv::medianBlur, cv::bilateralFilter. + */ +CVAPI(void) cvSmooth( const CvArr* src, CvArr* dst, + int smoothtype CV_DEFAULT(CV_GAUSSIAN), + int size1 CV_DEFAULT(3), + int size2 CV_DEFAULT(0), + double sigma1 CV_DEFAULT(0), + double sigma2 CV_DEFAULT(0)); + +/** @brief Convolves an image with the kernel. + +@param src input image. +@param dst output image of the same size and the same number of channels as src. +@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point +matrix; if you want to apply different kernels to different channels, split the image into +separate color planes using split and process them individually. +@param anchor anchor of the kernel that indicates the relative position of a filtered point within +the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor +is at the kernel center. + +@see cv::filter2D + */ +CVAPI(void) cvFilter2D( const CvArr* src, CvArr* dst, const CvMat* kernel, + CvPoint anchor CV_DEFAULT(cvPoint(-1,-1))); + +/** @brief Finds integral image: SUM(X,Y) = sum(x \texttt{hist1}(I)\)}{\frac{\texttt{hist2}(I) \cdot \texttt{scale}}{\texttt{hist1}(I)}}{if \(\texttt{hist1}(I) \ne 0\) and \(\texttt{hist2}(I) \le \texttt{hist1}(I)\)}\f] + +@param hist1 First histogram (the divisor). +@param hist2 Second histogram. +@param dst_hist Destination histogram. +@param scale Scale factor for the destination histogram. + */ +CVAPI(void) cvCalcProbDensity( const CvHistogram* hist1, const CvHistogram* hist2, + CvHistogram* dst_hist, double scale CV_DEFAULT(255) ); + +/** @brief equalizes histogram of 8-bit single-channel image +@see cv::equalizeHist +*/ +CVAPI(void) cvEqualizeHist( const CvArr* src, CvArr* dst ); + + +/** @brief Applies distance transform to binary image +@see cv::distanceTransform +*/ +CVAPI(void) cvDistTransform( const CvArr* src, CvArr* dst, + int distance_type CV_DEFAULT(CV_DIST_L2), + int mask_size CV_DEFAULT(3), + const float* mask CV_DEFAULT(NULL), + CvArr* labels CV_DEFAULT(NULL), + int labelType CV_DEFAULT(CV_DIST_LABEL_CCOMP)); + + +/** @brief Applies fixed-level threshold to grayscale image. + + This is a basic operation applied before retrieving contours +@see cv::threshold +*/ +CVAPI(double) cvThreshold( const CvArr* src, CvArr* dst, + double threshold, double max_value, + int threshold_type ); + +/** @brief Applies adaptive threshold to grayscale image. + + The two parameters for methods CV_ADAPTIVE_THRESH_MEAN_C and + CV_ADAPTIVE_THRESH_GAUSSIAN_C are: + neighborhood size (3, 5, 7 etc.), + and a constant subtracted from mean (...,-3,-2,-1,0,1,2,3,...) +@see cv::adaptiveThreshold +*/ +CVAPI(void) cvAdaptiveThreshold( const CvArr* src, CvArr* dst, double max_value, + int adaptive_method CV_DEFAULT(CV_ADAPTIVE_THRESH_MEAN_C), + int threshold_type CV_DEFAULT(CV_THRESH_BINARY), + int block_size CV_DEFAULT(3), + double param1 CV_DEFAULT(5)); + +/** @brief Fills the connected component until the color difference gets large enough +@see cv::floodFill +*/ +CVAPI(void) cvFloodFill( CvArr* image, CvPoint seed_point, + CvScalar new_val, CvScalar lo_diff CV_DEFAULT(cvScalarAll(0)), + CvScalar up_diff CV_DEFAULT(cvScalarAll(0)), + CvConnectedComp* comp CV_DEFAULT(NULL), + int flags CV_DEFAULT(4), + CvArr* mask CV_DEFAULT(NULL)); + +/****************************************************************************************\ +* Feature detection * +\****************************************************************************************/ + +/** @brief Runs canny edge detector +@see cv::Canny +*/ +CVAPI(void) cvCanny( const CvArr* image, CvArr* edges, double threshold1, + double threshold2, int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates constraint image for corner detection + + Dx^2 * Dyy + Dxx * Dy^2 - 2 * Dx * Dy * Dxy. + Applying threshold to the result gives coordinates of corners +@see cv::preCornerDetect +*/ +CVAPI(void) cvPreCornerDetect( const CvArr* image, CvArr* corners, + int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates eigen values and vectors of 2x2 + gradient covariation matrix at every image pixel +@see cv::cornerEigenValsAndVecs +*/ +CVAPI(void) cvCornerEigenValsAndVecs( const CvArr* image, CvArr* eigenvv, + int block_size, int aperture_size CV_DEFAULT(3) ); + +/** @brief Calculates minimal eigenvalue for 2x2 gradient covariation matrix at + every image pixel +@see cv::cornerMinEigenVal +*/ +CVAPI(void) cvCornerMinEigenVal( const CvArr* image, CvArr* eigenval, + int block_size, int aperture_size CV_DEFAULT(3) ); + +/** @brief Harris corner detector: + + Calculates det(M) - k*(trace(M)^2), where M is 2x2 gradient covariation matrix for each pixel +@see cv::cornerHarris +*/ +CVAPI(void) cvCornerHarris( const CvArr* image, CvArr* harris_response, + int block_size, int aperture_size CV_DEFAULT(3), + double k CV_DEFAULT(0.04) ); + +/** @brief Adjust corner position using some sort of gradient search +@see cv::cornerSubPix +*/ +CVAPI(void) cvFindCornerSubPix( const CvArr* image, CvPoint2D32f* corners, + int count, CvSize win, CvSize zero_zone, + CvTermCriteria criteria ); + +/** @brief Finds a sparse set of points within the selected region + that seem to be easy to track +@see cv::goodFeaturesToTrack +*/ +CVAPI(void) cvGoodFeaturesToTrack( const CvArr* image, CvArr* eig_image, + CvArr* temp_image, CvPoint2D32f* corners, + int* corner_count, double quality_level, + double min_distance, + const CvArr* mask CV_DEFAULT(NULL), + int block_size CV_DEFAULT(3), + int use_harris CV_DEFAULT(0), + double k CV_DEFAULT(0.04) ); + +/** @brief Finds lines on binary image using one of several methods. + + line_storage is either memory storage or 1 x _max number of lines_ CvMat, its + number of columns is changed by the function. + method is one of CV_HOUGH_*; + rho, theta and threshold are used for each of those methods; + param1 ~ line length, param2 ~ line gap - for probabilistic, + param1 ~ srn, param2 ~ stn - for multi-scale +@see cv::HoughLines +*/ +CVAPI(CvSeq*) cvHoughLines2( CvArr* image, void* line_storage, int method, + double rho, double theta, int threshold, + double param1 CV_DEFAULT(0), double param2 CV_DEFAULT(0), + double min_theta CV_DEFAULT(0), double max_theta CV_DEFAULT(CV_PI)); + +/** @brief Finds circles in the image +@see cv::HoughCircles +*/ +CVAPI(CvSeq*) cvHoughCircles( CvArr* image, void* circle_storage, + int method, double dp, double min_dist, + double param1 CV_DEFAULT(100), + double param2 CV_DEFAULT(100), + int min_radius CV_DEFAULT(0), + int max_radius CV_DEFAULT(0)); + +/** @brief Fits a line into set of 2d or 3d points in a robust way (M-estimator technique) +@see cv::fitLine +*/ +CVAPI(void) cvFitLine( const CvArr* points, int dist_type, double param, + double reps, double aeps, float* line ); + +/****************************************************************************************\ +* Drawing * +\****************************************************************************************/ + +/****************************************************************************************\ +* Drawing functions work with images/matrices of arbitrary type. * +* For color images the channel order is BGR[A] * +* Antialiasing is supported only for 8-bit image now. * +* All the functions include parameter color that means rgb value (that may be * +* constructed with CV_RGB macro) for color images and brightness * +* for grayscale images. * +* If a drawn figure is partially or completely outside of the image, it is clipped.* +\****************************************************************************************/ + +#define CV_FILLED -1 + +#define CV_AA 16 + +/** @brief Draws 4-connected, 8-connected or antialiased line segment connecting two points +@see cv::line +*/ +CVAPI(void) cvLine( CvArr* img, CvPoint pt1, CvPoint pt2, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +/** @brief Draws a rectangle given two opposite corners of the rectangle (pt1 & pt2) + + if thickness<0 (e.g. thickness == CV_FILLED), the filled box is drawn +@see cv::rectangle +*/ +CVAPI(void) cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + int shift CV_DEFAULT(0)); + +/** @brief Draws a rectangle specified by a CvRect structure +@see cv::rectangle +*/ +CVAPI(void) cvRectangleR( CvArr* img, CvRect r, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + int shift CV_DEFAULT(0)); + + +/** @brief Draws a circle with specified center and radius. + + Thickness works in the same way as with cvRectangle +@see cv::circle +*/ +CVAPI(void) cvCircle( CvArr* img, CvPoint center, int radius, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +/** @brief Draws ellipse outline, filled ellipse, elliptic arc or filled elliptic sector + + depending on _thickness_, _start_angle_ and _end_angle_ parameters. The resultant figure + is rotated by _angle_. All the angles are in degrees +@see cv::ellipse +*/ +CVAPI(void) cvEllipse( CvArr* img, CvPoint center, CvSize axes, + double angle, double start_angle, double end_angle, + CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +CV_INLINE void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color, + int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ) +{ + CvSize axes = cvSize( + cvRound(box.size.width*0.5), + cvRound(box.size.height*0.5) + ); + + cvEllipse( img, cvPointFrom32f( box.center ), axes, box.angle, + 0, 360, color, thickness, line_type, shift ); +} + +/** @brief Fills convex or monotonous polygon. +@see cv::fillConvexPoly +*/ +CVAPI(void) cvFillConvexPoly( CvArr* img, const CvPoint* pts, int npts, CvScalar color, + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0)); + +/** @brief Fills an area bounded by one or more arbitrary polygons +@see cv::fillPoly +*/ +CVAPI(void) cvFillPoly( CvArr* img, CvPoint** pts, const int* npts, + int contours, CvScalar color, + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +/** @brief Draws one or more polygonal curves +@see cv::polylines +*/ +CVAPI(void) cvPolyLine( CvArr* img, CvPoint** pts, const int* npts, int contours, + int is_closed, CvScalar color, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) ); + +#define cvDrawRect cvRectangle +#define cvDrawLine cvLine +#define cvDrawCircle cvCircle +#define cvDrawEllipse cvEllipse +#define cvDrawPolyLine cvPolyLine + +/** @brief Clips the line segment connecting *pt1 and *pt2 + by the rectangular window + + (0<=xptr will point to pt1 (or pt2, see left_to_right description) location in +the image. Returns the number of pixels on the line between the ending points. +@see cv::LineIterator +*/ +CVAPI(int) cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, + CvLineIterator* line_iterator, + int connectivity CV_DEFAULT(8), + int left_to_right CV_DEFAULT(0)); + +#define CV_NEXT_LINE_POINT( line_iterator ) \ +{ \ + int _line_iterator_mask = (line_iterator).err < 0 ? -1 : 0; \ + (line_iterator).err += (line_iterator).minus_delta + \ + ((line_iterator).plus_delta & _line_iterator_mask); \ + (line_iterator).ptr += (line_iterator).minus_step + \ + ((line_iterator).plus_step & _line_iterator_mask); \ +} + + +#define CV_FONT_HERSHEY_SIMPLEX 0 +#define CV_FONT_HERSHEY_PLAIN 1 +#define CV_FONT_HERSHEY_DUPLEX 2 +#define CV_FONT_HERSHEY_COMPLEX 3 +#define CV_FONT_HERSHEY_TRIPLEX 4 +#define CV_FONT_HERSHEY_COMPLEX_SMALL 5 +#define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6 +#define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7 + +#define CV_FONT_ITALIC 16 + +#define CV_FONT_VECTOR0 CV_FONT_HERSHEY_SIMPLEX + + +/** Font structure */ +typedef struct CvFont +{ + const char* nameFont; //Qt:nameFont + CvScalar color; //Qt:ColorFont -> cvScalar(blue_component, green_component, red_component[, alpha_component]) + int font_face; //Qt: bool italic /** =CV_FONT_* */ + const int* ascii; //!< font data and metrics + const int* greek; + const int* cyrillic; + float hscale, vscale; + float shear; //!< slope coefficient: 0 - normal, >0 - italic + int thickness; //!< Qt: weight /** letters thickness */ + float dx; //!< horizontal interval between letters + int line_type; //!< Qt: PointSize +} +CvFont; + +/** @brief Initializes font structure (OpenCV 1.x API). + +The function initializes the font structure that can be passed to text rendering functions. + +@param font Pointer to the font structure initialized by the function +@param font_face Font name identifier. See cv::HersheyFonts and corresponding old CV_* identifiers. +@param hscale Horizontal scale. If equal to 1.0f , the characters have the original width +depending on the font type. If equal to 0.5f , the characters are of half the original width. +@param vscale Vertical scale. If equal to 1.0f , the characters have the original height depending +on the font type. If equal to 0.5f , the characters are of half the original height. +@param shear Approximate tangent of the character slope relative to the vertical line. A zero +value means a non-italic font, 1.0f means about a 45 degree slope, etc. +@param thickness Thickness of the text strokes +@param line_type Type of the strokes, see line description + +@sa cvPutText + */ +CVAPI(void) cvInitFont( CvFont* font, int font_face, + double hscale, double vscale, + double shear CV_DEFAULT(0), + int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8)); + +CV_INLINE CvFont cvFont( double scale, int thickness CV_DEFAULT(1) ) +{ + CvFont font; + cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, scale, scale, 0, thickness, CV_AA ); + return font; +} + +/** @brief Renders text stroke with specified font and color at specified location. + CvFont should be initialized with cvInitFont +@see cvInitFont, cvGetTextSize, cvFont, cv::putText +*/ +CVAPI(void) cvPutText( CvArr* img, const char* text, CvPoint org, + const CvFont* font, CvScalar color ); + +/** @brief Calculates bounding box of text stroke (useful for alignment) +@see cv::getTextSize +*/ +CVAPI(void) cvGetTextSize( const char* text_string, const CvFont* font, + CvSize* text_size, int* baseline ); + +/** @brief Unpacks color value + +if arrtype is CV_8UC?, _color_ is treated as packed color value, otherwise the first channels +(depending on arrtype) of destination scalar are set to the same value = _color_ +*/ +CVAPI(CvScalar) cvColorToScalar( double packed_color, int arrtype ); + +/** @brief Returns the polygon points which make up the given ellipse. + +The ellipse is define by the box of size 'axes' rotated 'angle' around the 'center'. A partial +sweep of the ellipse arc can be done by specifying arc_start and arc_end to be something other than +0 and 360, respectively. The input array 'pts' must be large enough to hold the result. The total +number of points stored into 'pts' is returned by this function. +@see cv::ellipse2Poly +*/ +CVAPI(int) cvEllipse2Poly( CvPoint center, CvSize axes, + int angle, int arc_start, int arc_end, CvPoint * pts, int delta ); + +/** @brief Draws contour outlines or filled interiors on the image +@see cv::drawContours +*/ +CVAPI(void) cvDrawContours( CvArr *img, CvSeq* contour, + CvScalar external_color, CvScalar hole_color, + int max_level, int thickness CV_DEFAULT(1), + int line_type CV_DEFAULT(8), + CvPoint offset CV_DEFAULT(cvPoint(0,0))); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/types_c.h b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/types_c.h new file mode 100644 index 0000000..d3e55f5 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/imgproc/types_c.h @@ -0,0 +1,659 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_IMGPROC_TYPES_C_H +#define OPENCV_IMGPROC_TYPES_C_H + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup imgproc_c + @{ +*/ + +/** Connected component structure */ +typedef struct CvConnectedComp +{ + double area; /** DBL_EPSILON ? 1./std::sqrt(am00) : 0; + } + operator cv::Moments() const + { + return cv::Moments(m00, m10, m01, m20, m11, m02, m30, m21, m12, m03); + } +#endif +} +CvMoments; + +#ifdef __cplusplus +} // extern "C" + +CV_INLINE CvMoments cvMoments() +{ +#if !defined(CV__ENABLE_C_API_CTORS) + CvMoments self = CV_STRUCT_INITIALIZER; return self; +#else + return CvMoments(); +#endif +} + +CV_INLINE CvMoments cvMoments(const cv::Moments& m) +{ +#if !defined(CV__ENABLE_C_API_CTORS) + double am00 = std::abs(m.m00); + CvMoments self = { + m.m00, m.m10, m.m01, m.m20, m.m11, m.m02, m.m30, m.m21, m.m12, m.m03, + m.mu20, m.mu11, m.mu02, m.mu30, m.mu21, m.mu12, m.mu03, + am00 > DBL_EPSILON ? 1./std::sqrt(am00) : 0 + }; + return self; +#else + return CvMoments(m); +#endif +} + +extern "C" { +#endif // __cplusplus + +/** Hu invariants */ +typedef struct CvHuMoments +{ + double hu1, hu2, hu3, hu4, hu5, hu6, hu7; /**< Hu invariants */ +} +CvHuMoments; + +/** Template matching methods */ +enum +{ + CV_TM_SQDIFF =0, + CV_TM_SQDIFF_NORMED =1, + CV_TM_CCORR =2, + CV_TM_CCORR_NORMED =3, + CV_TM_CCOEFF =4, + CV_TM_CCOEFF_NORMED =5 +}; + +typedef float (CV_CDECL * CvDistanceFunction)( const float* a, const float* b, void* user_param ); + +/** Contour retrieval modes */ +enum +{ + CV_RETR_EXTERNAL=0, + CV_RETR_LIST=1, + CV_RETR_CCOMP=2, + CV_RETR_TREE=3, + CV_RETR_FLOODFILL=4 +}; + +/** Contour approximation methods */ +enum +{ + CV_CHAIN_CODE=0, + CV_CHAIN_APPROX_NONE=1, + CV_CHAIN_APPROX_SIMPLE=2, + CV_CHAIN_APPROX_TC89_L1=3, + CV_CHAIN_APPROX_TC89_KCOS=4, + CV_LINK_RUNS=5 +}; + +/* +Internal structure that is used for sequential retrieving contours from the image. +It supports both hierarchical and plane variants of Suzuki algorithm. +*/ +typedef struct _CvContourScanner* CvContourScanner; + +/** Freeman chain reader state */ +typedef struct CvChainPtReader +{ + CV_SEQ_READER_FIELDS() + char code; + CvPoint pt; + schar deltas[8][2]; +} +CvChainPtReader; + +/** initializes 8-element array for fast access to 3x3 neighborhood of a pixel */ +#define CV_INIT_3X3_DELTAS( deltas, step, nch ) \ + ((deltas)[0] = (nch), (deltas)[1] = -(step) + (nch), \ + (deltas)[2] = -(step), (deltas)[3] = -(step) - (nch), \ + (deltas)[4] = -(nch), (deltas)[5] = (step) - (nch), \ + (deltas)[6] = (step), (deltas)[7] = (step) + (nch)) + + +/** Contour approximation algorithms */ +enum +{ + CV_POLY_APPROX_DP = 0 +}; + +/** Shape matching methods */ +enum +{ + CV_CONTOURS_MATCH_I1 =1, //!< \f[I_1(A,B) = \sum _{i=1...7} \left | \frac{1}{m^A_i} - \frac{1}{m^B_i} \right |\f] + CV_CONTOURS_MATCH_I2 =2, //!< \f[I_2(A,B) = \sum _{i=1...7} \left | m^A_i - m^B_i \right |\f] + CV_CONTOURS_MATCH_I3 =3 //!< \f[I_3(A,B) = \max _{i=1...7} \frac{ \left| m^A_i - m^B_i \right| }{ \left| m^A_i \right| }\f] +}; + +/** Shape orientation */ +enum +{ + CV_CLOCKWISE =1, + CV_COUNTER_CLOCKWISE =2 +}; + + +/** Convexity defect */ +typedef struct CvConvexityDefect +{ + CvPoint* start; /**< point of the contour where the defect begins */ + CvPoint* end; /**< point of the contour where the defect ends */ + CvPoint* depth_point; /**< the farthest from the convex hull point within the defect */ + float depth; /**< distance between the farthest point and the convex hull */ +} CvConvexityDefect; + + +/** Histogram comparison methods */ +enum +{ + CV_COMP_CORREL =0, + CV_COMP_CHISQR =1, + CV_COMP_INTERSECT =2, + CV_COMP_BHATTACHARYYA =3, + CV_COMP_HELLINGER =CV_COMP_BHATTACHARYYA, + CV_COMP_CHISQR_ALT =4, + CV_COMP_KL_DIV =5 +}; + +/** Mask size for distance transform */ +enum +{ + CV_DIST_MASK_3 =3, + CV_DIST_MASK_5 =5, + CV_DIST_MASK_PRECISE =0 +}; + +/** Content of output label array: connected components or pixels */ +enum +{ + CV_DIST_LABEL_CCOMP = 0, + CV_DIST_LABEL_PIXEL = 1 +}; + +/** Distance types for Distance Transform and M-estimators */ +enum +{ + CV_DIST_USER =-1, /**< User defined distance */ + CV_DIST_L1 =1, /**< distance = |x1-x2| + |y1-y2| */ + CV_DIST_L2 =2, /**< the simple euclidean distance */ + CV_DIST_C =3, /**< distance = max(|x1-x2|,|y1-y2|) */ + CV_DIST_L12 =4, /**< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1)) */ + CV_DIST_FAIR =5, /**< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998 */ + CV_DIST_WELSCH =6, /**< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846 */ + CV_DIST_HUBER =7 /**< distance = |x| threshold ? max_value : 0 */ + CV_THRESH_BINARY_INV =1, /**< value = value > threshold ? 0 : max_value */ + CV_THRESH_TRUNC =2, /**< value = value > threshold ? threshold : value */ + CV_THRESH_TOZERO =3, /**< value = value > threshold ? value : 0 */ + CV_THRESH_TOZERO_INV =4, /**< value = value > threshold ? 0 : value */ + CV_THRESH_MASK =7, + CV_THRESH_OTSU =8, /**< use Otsu algorithm to choose the optimal threshold value; + combine the flag with one of the above CV_THRESH_* values */ + CV_THRESH_TRIANGLE =16 /**< use Triangle algorithm to choose the optimal threshold value; + combine the flag with one of the above CV_THRESH_* values, but not + with CV_THRESH_OTSU */ +}; + +/** Adaptive threshold methods */ +enum +{ + CV_ADAPTIVE_THRESH_MEAN_C =0, + CV_ADAPTIVE_THRESH_GAUSSIAN_C =1 +}; + +/** FloodFill flags */ +enum +{ + CV_FLOODFILL_FIXED_RANGE =(1 << 16), + CV_FLOODFILL_MASK_ONLY =(1 << 17) +}; + + +/** Canny edge detector flags */ +enum +{ + CV_CANNY_L2_GRADIENT =(1 << 31) +}; + +/** Variants of a Hough transform */ +enum +{ + CV_HOUGH_STANDARD =0, + CV_HOUGH_PROBABILISTIC =1, + CV_HOUGH_MULTI_SCALE =2, + CV_HOUGH_GRADIENT =3 +}; + + +/* Fast search data structures */ +struct CvFeatureTree; +struct CvLSH; +struct CvLSHOperations; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/opencv.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/opencv.hpp new file mode 100644 index 0000000..4048158 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/opencv.hpp @@ -0,0 +1,139 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2010, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_ALL_HPP +#define OPENCV_ALL_HPP + +// File that defines what modules where included during the build of OpenCV +// These are purely the defines of the correct HAVE_OPENCV_modulename values +#include "opencv2/opencv_modules.hpp" + +// Then the list of defines is checked to include the correct headers +// Core library is always included --> without no OpenCV functionality available +#include "opencv2/core.hpp" + +// Then the optional modules are checked +#ifdef HAVE_OPENCV_CALIB3D +#include "opencv2/calib3d.hpp" +#endif +#ifdef HAVE_OPENCV_FEATURES2D +#include "opencv2/features2d.hpp" +#endif +#ifdef HAVE_OPENCV_DNN +#include "opencv2/dnn.hpp" +#endif +#ifdef HAVE_OPENCV_FLANN +#include "opencv2/flann.hpp" +#endif +#ifdef HAVE_OPENCV_HIGHGUI +#include "opencv2/highgui.hpp" +#endif +#ifdef HAVE_OPENCV_IMGCODECS +#include "opencv2/imgcodecs.hpp" +#endif +#ifdef HAVE_OPENCV_IMGPROC +#include "opencv2/imgproc.hpp" +#endif +#ifdef HAVE_OPENCV_ML +#include "opencv2/ml.hpp" +#endif +#ifdef HAVE_OPENCV_OBJDETECT +#include "opencv2/objdetect.hpp" +#endif +#ifdef HAVE_OPENCV_PHOTO +#include "opencv2/photo.hpp" +#endif +#ifdef HAVE_OPENCV_SHAPE +#include "opencv2/shape.hpp" +#endif +#ifdef HAVE_OPENCV_STITCHING +#include "opencv2/stitching.hpp" +#endif +#ifdef HAVE_OPENCV_SUPERRES +#include "opencv2/superres.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEO +#include "opencv2/video.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOIO +#include "opencv2/videoio.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEOSTAB +#include "opencv2/videostab.hpp" +#endif +#ifdef HAVE_OPENCV_VIZ +#include "opencv2/viz.hpp" +#endif + +// Finally CUDA specific entries are checked and added +#ifdef HAVE_OPENCV_CUDAARITHM +#include "opencv2/cudaarithm.hpp" +#endif +#ifdef HAVE_OPENCV_CUDABGSEGM +#include "opencv2/cudabgsegm.hpp" +#endif +#ifdef HAVE_OPENCV_CUDACODEC +#include "opencv2/cudacodec.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAFEATURES2D +#include "opencv2/cudafeatures2d.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAFILTERS +#include "opencv2/cudafilters.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAIMGPROC +#include "opencv2/cudaimgproc.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAOBJDETECT +#include "opencv2/cudaobjdetect.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAOPTFLOW +#include "opencv2/cudaoptflow.hpp" +#endif +#ifdef HAVE_OPENCV_CUDASTEREO +#include "opencv2/cudastereo.hpp" +#endif +#ifdef HAVE_OPENCV_CUDAWARPING +#include "opencv2/cudawarping.hpp" +#endif + +#endif diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/opencv_modules.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/opencv_modules.hpp new file mode 100644 index 0000000..506f595 --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/opencv_modules.hpp @@ -0,0 +1,19 @@ +/* + * ** File generated automatically, do not modify ** + * + * This file defines the list of modules available in current build configuration + * + * +*/ + +// This definition means that OpenCV is built with enabled non-free code. +// For example, patented algorithms for non-profit/non-commercial use only. +/* #undef OPENCV_ENABLE_NONFREE */ + +#define HAVE_OPENCV_CORE +#define HAVE_OPENCV_HIGHGUI +#define HAVE_OPENCV_IMGCODECS +#define HAVE_OPENCV_IMGPROC +#define HAVE_OPENCV_WORLD + + diff --git a/hgdriver/3rdparty/opencv/include/win/opencv2/world.hpp b/hgdriver/3rdparty/opencv/include/win/opencv2/world.hpp new file mode 100644 index 0000000..4902c2f --- /dev/null +++ b/hgdriver/3rdparty/opencv/include/win/opencv2/world.hpp @@ -0,0 +1,58 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2010, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "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 Intel Corporation or contributors 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. +// +//M*/ + +#ifndef OPENCV_WORLD_HPP +#define OPENCV_WORLD_HPP + +#include "opencv2/core.hpp" + +#ifdef __cplusplus +namespace cv +{ + +CV_EXPORTS_W bool initAll(); + +} + +#endif + +#endif diff --git a/hgdriver/CMakeLists.txt b/hgdriver/CMakeLists.txt new file mode 100644 index 0000000..afb787e --- /dev/null +++ b/hgdriver/CMakeLists.txt @@ -0,0 +1,4 @@ +project(sane_lib_project) +add_subdirectory(hgdev) +add_subdirectory(ImageProcess) +add_subdirectory(huagaoxxx_warraper_ex) diff --git a/hgdriver/ImageProcess/CMakeLists.txt b/hgdriver/ImageProcess/CMakeLists.txt new file mode 100644 index 0000000..d118449 --- /dev/null +++ b/hgdriver/ImageProcess/CMakeLists.txt @@ -0,0 +1,30 @@ +project(gimgproc) +add_compile_options(-std=c++11) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +aux_source_directory(${PROJECT_SOURCE_DIR} DIR_SRCS) +file(GLOB DIR_HEADS "${PROJECT_SOURCE_DIR}/*.h" "${PROJECT_SOURCE_DIR}/*.hpp") +set(DIR_SRCS ${DIR_SRCS} ${DIR_HEADS}) + +add_library(${PROJECT_NAME} STATIC ${DIR_SRCS}) + +target_link_libraries(${PROJECT_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libopencv_world.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libittnotify.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibjasper.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibjpeg-turbo.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibpng.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibprotobuf.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibtiff.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libquirc.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libzlib.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibwebp.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libIlmImf.a + ) + +target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/../3rdparty/nick + ${PROJECT_SOURCE_DIR}/../3rdparty/opencv/include + ) + +set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../../tmp/) diff --git a/hgdriver/ImageProcess/IMulti.cpp b/hgdriver/ImageProcess/IMulti.cpp new file mode 100644 index 0000000..442cf4c --- /dev/null +++ b/hgdriver/ImageProcess/IMulti.cpp @@ -0,0 +1,10 @@ +#include "IMulti.h" + +IMulti::IMulti(void) +{ +} + +IMulti::~IMulti(void) +{ +} + diff --git a/hgdriver/ImageProcess/IMulti.h b/hgdriver/ImageProcess/IMulti.h new file mode 100644 index 0000000..df7ef35 --- /dev/null +++ b/hgdriver/ImageProcess/IMulti.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include "imgprocdefs.h" + +class IMulti +{ +public: + IMulti(void); + virtual ~IMulti(void); + virtual std::vector apply(cv::Mat& pDib) = 0; +}; diff --git a/hgdriver/ImageProcess/ImageApply.cpp b/hgdriver/ImageProcess/ImageApply.cpp new file mode 100644 index 0000000..80f5fbf --- /dev/null +++ b/hgdriver/ImageProcess/ImageApply.cpp @@ -0,0 +1,9 @@ +#include "ImageApply.h" + +CImageApply::CImageApply(void) +{ +} + +CImageApply::~CImageApply(void) +{ +} diff --git a/hgdriver/ImageProcess/ImageApply.h b/hgdriver/ImageProcess/ImageApply.h new file mode 100644 index 0000000..c94c0bc --- /dev/null +++ b/hgdriver/ImageProcess/ImageApply.h @@ -0,0 +1,21 @@ +锘#ifndef IMAGE_APPLY_H +#define IMAGE_APPLY_H + +#include +#include +#include + +class CImageApply +{ +public: + CImageApply(void); + virtual ~CImageApply(void); + + virtual void apply(cv::Mat& pDib,int side) = 0; + + virtual void apply(std::vector& mats, bool isTwoSide) = 0; +}; + +typedef std::shared_ptr ImageApplyPtr; + +#endif //!IMAGE_APPLY_H diff --git a/hgdriver/ImageProcess/ImageApplyAdjustColors.cpp b/hgdriver/ImageProcess/ImageApplyAdjustColors.cpp new file mode 100644 index 0000000..3e6cd5a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAdjustColors.cpp @@ -0,0 +1,90 @@ +#include "ImageApplyAdjustColors.h" + +CImageApplyAdjustColors::CImageApplyAdjustColors(void) + : m_brightness(0) + , m_contrast(0) + , m_gamma(1.0f) + , lut(1, 256, CV_8UC1) +{ + update_lutData(); +} + +CImageApplyAdjustColors::CImageApplyAdjustColors(int brightness, int contrast, float gamma) + : lut(1, 256, CV_8UC1) +{ + setAdjustColors(brightness, contrast, gamma); +} + +CImageApplyAdjustColors::~CImageApplyAdjustColors(void) +{ + +} + +void CImageApplyAdjustColors::apply(cv::Mat& pDib,int side) +{ + (void)side; + if (pDib.empty()) return; + + if (m_brightness != 0 || m_contrast != 0 || m_gamma < 0.999999f || m_gamma > 1.000001f) + cv::LUT(pDib, lut, pDib); +} + +void CImageApplyAdjustColors::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if(!var.empty()) + apply(var, 0); + i++; + } +} + +void CImageApplyAdjustColors::setAdjustColors(int brightness, int contrast, float gamma) +{ + m_brightness = cv::max(-255, cv::min(brightness, 255)); + m_contrast = cv::max(-127, cv::min(contrast, 127)); + m_gamma = cv::max(0.1f, cv::min(gamma, 5.0f)); + update_lutData(); +} + +void CImageApplyAdjustColors::setBrightness(int brightness) +{ + m_brightness = cv::max(-255, cv::min(brightness, 255)); + update_lutData(); +} + +void CImageApplyAdjustColors::setContrast(int contrast) +{ + m_contrast = cv::max(-127, cv::min(contrast, 127)); + update_lutData(); +} + +void CImageApplyAdjustColors::setGamma(float gamma) +{ + m_gamma = cv::max(0.1f, cv::min(gamma, 5.0f)); + update_lutData(); +} + +void CImageApplyAdjustColors::update_lutData() +{ + unsigned char* ptr = lut.data; + + uchar buffer[256]; + for (int i = 0; i < 256; i++) + { + //update brightness + ptr[i] = static_cast(cv::max(0, cv::min(i + m_brightness, 255))); + + //update contrast + if (ptr[i] < 128) + ptr[i] = static_cast(cv::max(0, cv::min(ptr[i] - m_contrast, 127))); + else + ptr[i] = static_cast(cv::max(127, cv::min(ptr[i] + m_contrast, 255))); + } + float g = 1.0f / m_gamma; + for (int i = 0; i < 256; i++) + ptr[i] = static_cast(cv::min(255, static_cast(cv::pow(static_cast(ptr[i]) / 255.0f, g) * 255.0f + 0.5f))); +} diff --git a/hgdriver/ImageProcess/ImageApplyAdjustColors.h b/hgdriver/ImageProcess/ImageApplyAdjustColors.h new file mode 100644 index 0000000..83ea595 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAdjustColors.h @@ -0,0 +1,53 @@ +#ifndef IMAGE_APPLY_ADJUST_COLOR_H +#define IMAGE_APPLY_ADJUST_COLOR_H + +#include "ImageApply.h" + +class CImageApplyAdjustColors : public CImageApply +{ +public: + + CImageApplyAdjustColors(void); + + /* + * brightness [in]: 亮度调节,取值范围[-255, 255] + * constrast [in]: 对比度调节,取值范围[-128, 127] + * gamma [in]: 伽马调节,取值范围[0.1, 5.0] + */ + CImageApplyAdjustColors(int brightness, int contrast, float gamma); + + virtual ~CImageApplyAdjustColors(void); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + void setAdjustColors(int brightness, int contrast, float gamma); + + int getContrast() { return m_contrast; } + + int getBrightness() { return m_brightness; } + + double getGamma() { return m_gamma; } + + void setBrightness(int brightness); + + void setContrast(int contrast); + + void setGamma(float gamma); + +private: + + void update_lutData(); + +private: + + int m_brightness; + int m_contrast; + float m_gamma; + cv::Mat lut; +}; + +#endif // !IMAGE_APPLY_ADJUST_COLOR_H + + diff --git a/hgdriver/ImageProcess/ImageApplyAutoContrast.cpp b/hgdriver/ImageProcess/ImageApplyAutoContrast.cpp new file mode 100644 index 0000000..4cd0d5f --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAutoContrast.cpp @@ -0,0 +1,43 @@ +#include "ImageApplyAutoContrast.h" +#include +using namespace std; +using namespace cv; + +CImageApplyAutoContrast::CImageApplyAutoContrast() +{ +} + +CImageApplyAutoContrast::~CImageApplyAutoContrast() +{ +} + +void CImageApplyAutoContrast::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (pDib.empty()) return; + + if(pDib.channels() == 1) + cv::equalizeHist(pDib,pDib); + else + { + std::vector mats(3); + cv::split(pDib,mats); + for(size_t i = 0; i < mats.size(); i++) + if(!mats[i].empty()) + cv::equalizeHist(mats[i], mats[i]); + cv::merge(mats,pDib); + } +} + +void CImageApplyAutoContrast::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyAutoContrast.h b/hgdriver/ImageProcess/ImageApplyAutoContrast.h new file mode 100644 index 0000000..d8abf05 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAutoContrast.h @@ -0,0 +1,32 @@ +/* + * ==================================================== + + * 功能:自动色彩校正,实现原理为直方图均衡化 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 + * 版本号:v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_AUTO_CONTRAST_H +#define IMAGE_APPLY_AUTO_CONTRAST_H + +#include "ImageApply.h" + +class CImageApplyAutoContrast : public CImageApply +{ +public: + + CImageApplyAutoContrast(); + + virtual ~CImageApplyAutoContrast(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); +private: + +}; +#endif // !IMAGE_APPLY_AUTO_CONTRAST_H diff --git a/hgdriver/ImageProcess/ImageApplyAutoCrop.cpp b/hgdriver/ImageProcess/ImageApplyAutoCrop.cpp new file mode 100644 index 0000000..6c4be07 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAutoCrop.cpp @@ -0,0 +1,393 @@ +#include "ImageApplyAutoCrop.h" +#include "ImageProcess_Public.h" + +CImageApplyAutoCrop::CImageApplyAutoCrop() + : m_isCrop(false) + , m_isDesaskew(false) + , m_isFillBlank(false) + , m_isConvexHull(true) + , m_isFillColor(false) + , m_threshold(40) + , m_noise(8) + , m_indent(5) + , m_normalCrop(false) +{ +} + +CImageApplyAutoCrop::CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFillBlank, const cv::Size& fixedSize, bool isConvex, bool isFillColor, + double threshold, int noise, int indent, bool normalCrop) + : m_isCrop(isCrop) + , m_isDesaskew(isDesaskew) + , m_isFillBlank(isFillBlank) + , m_isConvexHull(isConvex) + , m_isFillColor(isFillColor) + , m_threshold(threshold) + , m_noise(noise) + , m_indent(indent) + , m_fixedSize(fixedSize) + , m_normalCrop(normalCrop) +{ +} + +CImageApplyAutoCrop::~CImageApplyAutoCrop() +{ +} + +void matmul(double* mul1, double* mul2, double* dst) +{ + dst[0] = mul1[0] * mul2[0] + mul1[1] * mul2[3] + mul1[2] * mul2[6]; + dst[1] = mul1[0] * mul2[1] + mul1[1] * mul2[4] + mul1[2] * mul2[7]; + dst[2] = mul1[0] * mul2[2] + mul1[1] * mul2[5] + mul1[2] * mul2[8]; + dst[3] = mul1[3] * mul2[0] + mul1[4] * mul2[3] + mul1[5] * mul2[6]; + dst[4] = mul1[3] * mul2[1] + mul1[4] * mul2[4] + mul1[5] * mul2[7]; + dst[5] = mul1[3] * mul2[2] + mul1[4] * mul2[5] + mul1[5] * mul2[8]; + dst[6] = mul1[6] * mul2[0] + mul1[7] * mul2[3] + mul1[8] * mul2[6]; + dst[7] = mul1[6] * mul2[1] + mul1[7] * mul2[4] + mul1[8] * mul2[7]; + dst[8] = mul1[6] * mul2[2] + mul1[7] * mul2[5] + mul1[8] * mul2[8]; +} + +cv::Mat concatenateMatrix(const cv::Mat& first, const cv::Mat& second) +{ + double buffer1[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1} ; + double buffer2[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1} ; + cv::Mat mul1(3, 3, CV_64FC1, buffer1); //cv::Mat::eye(3, 3, CV_64F); + cv::Mat mul2(3, 3, CV_64FC1, buffer2); //cv::Mat::eye(3, 3, CV_64F); + cv::Mat mul_r; + first.convertTo(mul_r, CV_64F); + mul_r.row(0).copyTo(mul1.row(0)); + mul_r.row(1).copyTo(mul1.row(1)); + + second.convertTo(mul_r, CV_64F); + mul_r.row(0).copyTo(mul2.row(0)); + mul_r.row(1).copyTo(mul2.row(1)); + + //mul1 = mul2 * mul1; + cv::Mat temp(3, 3, CV_64FC1); + matmul(buffer2, buffer1, (double*)temp.data); + mul1 = temp; + mul_r = first.clone(); + mul1.row(0).copyTo(mul_r.row(0)); + mul1.row(1).copyTo(mul_r.row(1)); + return mul_r; +} + +std::vector comMat() +{ + std::vector mats; + cv::Point2f srcTri[3]; + srcTri[0] = cv::Point2f(1, 1); + srcTri[1] = cv::Point2f(1, 0); + srcTri[2] = cv::Point2f(0, 1); + const float fact = 0.33f; + + float pos[] = { 0, 2 * fact, fact }; + cv::Point2f dstTri[3]; + dstTri[0] = cv::Point2f(1, 1); + dstTri[1] = cv::Point2f(1, 0.5); + dstTri[2] = cv::Point2f(0, 1); + + for (int i = 0; i < 3; i++) + { + dstTri[0] = cv::Point2f(1, 1 + pos[i]); + dstTri[1] = cv::Point2f(1, pos[i]); + dstTri[2] = cv::Point2f(0, 1 + pos[i]); + + mats.push_back(cv::getAffineTransform(srcTri, dstTri)); + } + return mats; +} + +void brightSharp(cv::Mat& src) +{ + const float a = -0.49f; + const float b = 3.0f; + //float kernel_data[] = { + // a, 0, 0, 0, a, + // 0, 0, a, 0, 0, + // 0, a, b, a, 0, + // 0, 0, a, 0, 0, + // a, 0, 0, 0, a }; + + float kernel_data[] = { + 0, a, 0, + a, b, a, + 0, a, 0 + }; + cv::Mat kernel(3, 3, CV_32FC1, kernel_data); + cv::filter2D(src, src, src.depth(), kernel); +} + +void CImageApplyAutoCrop::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (pDib.empty()) return; + + if (m_normalCrop) + { + cv::Rect roi = cv::Rect((pDib.cols - m_fixedSize.width) / 2, side == 0 ? 75 : 145, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows); + pDib = pDib(roi).clone(); + m_rect = cv::RotatedRect(cv::Point2f(roi.x + roi.width / 2, roi.y + roi.height / 2), cv::Size2f(roi.width, roi.height), 0.0f); + return; + } + + if (!m_isCrop && !m_isDesaskew && !m_isFillBlank && m_fixedSize.empty()) return; + + cv::Mat src = pDib; + cv::Mat thre; + cv::Mat dst; + hg::threshold_Mat(src, thre, m_threshold); + + if (m_noise > 0) + { + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(m_noise, 1)); + cv::morphologyEx(thre, thre, cv::MORPH_OPEN, element); + } + + if (m_indent > 0) + { + cv::Mat element = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(m_indent, m_indent)); + cv::morphologyEx(thre, thre, cv::MORPH_ERODE, element); + } + + std::vector hierarchy; + std::vector> contours; + + hg::findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL); + m_maxContour = hg::getMaxContour(contours, hierarchy); + + if (m_maxContour.size() == 0) + { + thre.release(); + // + if (!m_isCrop) + pDib = pDib(cv::Rect((pDib.cols - m_fixedSize.width) / 2, (pDib.rows - m_fixedSize.height) / 2, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows)).clone(); +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit CImageApplyAutoCrop apply"); +#endif // LOG + return; + } + + thre.release(); + dst.release(); + + cv::RotatedRect rect = hg::getBoundingRect(m_maxContour); + m_rect = rect; + cv::Rect boudingRect = cv::boundingRect(m_maxContour); + boudingRect.x -= 1; + boudingRect.y -= 1; + boudingRect.width += 2; + boudingRect.height += 2; + + if (m_isDesaskew && rect.angle != 0) + { + cv::Point2f srcTri[4], srcTri_temp[3], dstTri[3]; + rect.points(srcTri); + + dstTri[0] = cv::Point2f(0, rect.size.height - 1); + dstTri[1] = cv::Point2f(0, 0); + dstTri[2] = cv::Point2f(rect.size.width - 1, 0); + + srcTri_temp[0] = dstTri[0]; + srcTri_temp[1] = dstTri[1]; + srcTri_temp[2] = dstTri[2]; + cv::Mat warp_mat; + warp_mat = cv::getAffineTransform(srcTri, dstTri); + if (src.channels() == 1) + { + cv::warpAffine(src, dst, warp_mat, rect.size, cv::INTER_LINEAR); + } + else + { + cv::Mat bgr[3]; + cv::split(src, bgr); + auto mats = comMat(); + warp_mat = cv::getAffineTransform(srcTri, dstTri); + warp_mat = concatenateMatrix(mats[0], warp_mat); + + cv::warpAffine(bgr[0], bgr[0], warp_mat, rect.size, cv::INTER_LINEAR); + + warp_mat = cv::getAffineTransform(srcTri, dstTri); + warp_mat = concatenateMatrix(mats[1], warp_mat); + cv::warpAffine(bgr[1], bgr[1], warp_mat, rect.size, cv::INTER_LINEAR); + + warp_mat = cv::getAffineTransform(srcTri, dstTri); + warp_mat = concatenateMatrix(mats[2], warp_mat); + cv::warpAffine(bgr[2], bgr[2], warp_mat, rect.size, cv::INTER_LINEAR); + + cv::merge(bgr, 3, dst); + } + + double* ptr_m = reinterpret_cast(warp_mat.data); + double a = ptr_m[0]; + double b = ptr_m[1]; + double c = ptr_m[2]; + double d = ptr_m[3]; + double e = ptr_m[4]; + double f = ptr_m[5]; + + for (cv::Point& p : m_maxContour) + { + p.x = static_cast(a * p.x + b * p.y + c); + p.y = static_cast(d * p.x + e * p.y + f); + } + + for (std::vector& sub : contours) + for (cv::Point& p : sub) + { + p.x = static_cast(a * p.x + b * p.y + c); + p.y = static_cast(d * p.x + e * p.y + f); + } + } + else + { + auto t_rect = boudingRect & cv::Rect(0, 0, src.cols, src.rows); + dst = src(t_rect); + if (dst.channels() == 3) + { + cv::Mat bgr[3]; + cv::split(dst, bgr); + auto mats = comMat(); + for (int i = 0; i < 3; i++) + cv::warpAffine(bgr[i], bgr[i], mats[i], t_rect.size(), cv::INTER_LINEAR); + cv::merge(bgr, 3, dst); + } + } + + cv::Scalar autoBGColor; + if (m_isFillBlank) + { + if (m_isConvexHull) + { + if (m_maxContour.size() == 0) + { + thre.release(); + //锟斤拷锟斤拷枪潭锟斤拷锟斤拷妫拷敕碉拷夭锟斤拷泻锟侥尺达拷 + if (!m_isCrop) + pDib = pDib(cv::Rect((pDib.cols - m_fixedSize.width) / 2, (pDib.rows - m_fixedSize.height) / 2, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows)).clone(); + return; + } + hg::convexHull(m_maxContour, m_maxContour); + contours.clear(); + contours.push_back(m_maxContour); + } + + contours.push_back(std::vector()); + contours[contours.size() - 1].push_back(cv::Point(-1, dst.rows - 1)); + contours[contours.size() - 1].push_back(cv::Point(-1, -1)); + contours[contours.size() - 1].push_back(cv::Point(dst.cols, -1)); + contours[contours.size() - 1].push_back(cv::Point(dst.cols, dst.rows)); + + autoBGColor = m_isFillColor ? getBackGroudColor(pDib, rect.size.area()) : cv::Scalar(255, 255, 255); + hg::fillPolys(dst, contours, autoBGColor); + } + else + { + m_maxContour.clear(); + m_maxContour.push_back(cv::Point(-1, dst.rows)); + m_maxContour.push_back(cv::Point(-1, -1)); + m_maxContour.push_back(cv::Point(dst.cols, -1)); + m_maxContour.push_back(cv::Point(dst.cols, dst.rows)); + } + + pDib.release(); + if (/*(m_isCrop && side == 0) || (side == 1 && m_fixedSize.width * m_fixedSize.height == 0)*/ m_isCrop) + pDib = dst.clone(); + else + { + pDib = cv::Mat(m_fixedSize, dst.type(), m_isFillBlank ? autoBGColor : cv::Scalar(0, 0, 0)); + + cv::Rect roi; + roi.x = dst.cols > pDib.cols ? (dst.cols - pDib.cols) / 2 : 0; + roi.width = cv::min(pDib.cols, dst.cols); + roi.y = dst.rows > pDib.rows ? (dst.rows - pDib.rows) / 2 : 0; + roi.height = cv::min(pDib.rows, dst.rows); + cv::Rect rect((pDib.cols - roi.width) / 2, (pDib.rows - roi.height) / 2, roi.width, roi.height); + + for (cv::Point& p : m_maxContour) + p += roi.tl(); + dst(roi).copyTo(pDib(rect)); + } +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit CImageApplyAutoCrop apply8"); +#endif // LOG +} + +void CImageApplyAutoCrop::apply(std::vector& mats, bool isTwoSide) +{ + if (mats.empty()) return; + if (!mats[0].empty()) { + apply(mats[0], 0); + m_rects.push_back(m_rect); + //brightSharp(mats[0]); + } + + if (isTwoSide && mats.size() > 1) + { + cv::Size dSize = m_fixedSize; + if (!mats[0].empty()) + m_fixedSize = mats[0].size(); + if (!mats[1].empty()) { + apply(mats[1], 1); + m_rects.push_back(m_rect); + //brightSharp(mats[1]); + } + + if (!mats[0].empty()) + m_fixedSize = dSize; + } +} + +cv::Scalar CImageApplyAutoCrop::getBackGroudColor(const cv::Mat& image, int total) +{ + if (image.channels() == 3) + { + cv::Mat image_bgr[3]; + cv::split(image, image_bgr); + + uchar bgr[3]; + for (size_t i = 0; i < 3; i++) + bgr[i] = getBackGroudChannelMean(image_bgr[i], total); + return cv::Scalar(bgr[0], bgr[1], bgr[2]); + } + else + return cv::Scalar::all(getBackGroudChannelMean(image, total)); +} + +uchar CImageApplyAutoCrop::getBackGroudChannelMean(const cv::Mat& gray, int total) +{ + cv::Mat image_clone; + cv::resize(gray, image_clone, cv::Size(), 0.25, 0.25); + + int threnshold = total / 32; + int channels[] = { 0 }; + int nHistSize[] = { 256 }; + float range[] = { 0, 256 }; + const float* fHistRanges[] = { range }; + cv::Mat hist; + cv::calcHist(&image_clone, 1, channels, cv::Mat(), hist, 1, nHistSize, fHistRanges, true, false); + + int hist_array[256]; + for (int i = 0; i < 256; i++) + hist_array[i] = hist.at(i, 0); + + int length = 1; + const int length_max = 255 - m_threshold; + while (length < length_max) + { + for (size_t i = m_threshold + 1; i < 256 - length; i++) + { + int count = 0; + uint pixSum = 0; + for (size_t j = 0; j < length; j++) + { + count += hist_array[j + i]; + pixSum += hist_array[j + i] * (i + j); + } + + if (count >= threnshold) + return pixSum / count; + } + length++; + } + return 255; +} diff --git a/hgdriver/ImageProcess/ImageApplyAutoCrop.h b/hgdriver/ImageProcess/ImageApplyAutoCrop.h new file mode 100644 index 0000000..038a16a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyAutoCrop.h @@ -0,0 +1,112 @@ +/* + * ==================================================== + + * 鍔熻兘锛氳嚜鍔ㄨ鍓佺籂鍋忋侀櫎榛戝簳 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細2020/4/21 v1.0 + 2020/7/22 v1.1 澧炲姞鑾峰彇鍥惧儚鏈夋晥鍖哄煙杞粨鐨勬帴鍙axContour锛堢敤浜庨厤鍚堜竴浣撴満鐨勨滆烦杩囩┖鐧介〉鈥濈畻娉曪紝PC绔殏鏃舵棤闇浣跨敤锛 + 2020/10/16 v1.2 淇鑷姩瑁佸壀灏哄绮惧害涓㈠け鐨凚UG锛涙彁楂橀櫎榛戝簳缂╄繘绮惧害銆 + 2020/10/28 v1.2.1 淇鍑瑰嚫澶氳竟褰㈠~鍏呰儗鏅殑閫昏緫BUG銆 + 2020/10/28 v1.2.2 淇鍥惧儚澶勭悊蹇呭畾浼氱缉灏忓昂瀵哥殑BUG銆 + 2020/10/29 v1.2.3 閬垮厤鏃犺皳鐨勭籂鍋忥紙0掳绾犲亸锛 + 2020/11/30 v1.3.0 澧炲姞鍔熻兘锛屽彲璇嗗埆鏂囩ǹ棰滆壊杩涜濉厖榛戝簳銆 + * 鐗堟湰鍙凤細v1.3.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_AUTO_CROP_H +#define IMAGE_APPLY_AUTO_CROP_H + +#include "ImageApply.h" + +class CImageApplyAutoCrop : public CImageApply +{ +public: + CImageApplyAutoCrop(); + + + /* + * isCrop [in]:鑷姩骞呴潰瑁佸壀浣胯兘锛宼rue鑷姩瑁佸壀锛宖alse涓哄浐瀹氳鍓 + * isDesaskew [in]:鑷姩绾犲亸浣胯兘锛宼rue鑷姩绾犲亸锛宖alse涓轰笉绾犲亸 + * isFillBlank [in]:榛戝簳濉厖浣胯兘锛宼rue涓哄~鍏咃紝false涓轰笉濉厖 + * fixedSize [in]:鍥哄畾骞呴潰灏哄锛屽綋isCrop涓篺alse鏃剁敓鏁堬紝缁撴灉灏哄鎸塮ixedSize澶у皬杈撳嚭锛屽崟浣嶅儚绱 + * isConvex [in]:榛戝簳濉厖鏃剁殑濉厖鏂瑰紡,true涓哄嚫澶氳竟褰㈠~鍏咃紝false涓哄嚬澶氳竟褰㈠~鍏咃紝榛樿true + * isFillColor [in]:榛戝簳濉厖鏃堕噰鐢ㄨ嚜閫傚簲鑹插僵濉厖锛宖alse涓虹櫧鑹插~鍏咃紝true涓鸿嚜閫傚簲鏂囩ǹ搴曡壊濉厖锛岄粯璁alse + * threshold [in]:浜屽煎寲闃堝硷紝鍙栧艰寖鍥(0, 255)锛岄粯璁40 + * noise [in]:闄ゅ櫔鍍忕礌锛岃兘澶熸秷闄oise瀹藉害鐨勮儗鏅珫鏉$汗骞叉壈锛岄粯璁2 + * indent [in]:杞粨缂╄繘锛岃鍓佺籂鍋忔垨鑰呴粦搴曞~鍏呮椂锛屽鎺㈢储鍒扮殑绾稿紶杞粨杩涜缂╄繘indent鍍忕礌锛岄粯璁5 + * normalCrop [in]:涓簍rue涓攎_isCrop m_isDesaskew m_isFillBlank鍧囦负false鏃剁敓鏁堬紝鍥哄畾瑁佸垏閲囩敤鏈浼犵粺鐨勮鍒囨柟寮忥紝榛樿false + */ + CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFillBlank, const cv::Size& fixedSize, bool isConvex = true, + bool isFillColor = false, double threshold = 40, int noise = 8, int indent = 5, bool normalCrop = false); + + virtual ~CImageApplyAutoCrop(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + bool isAutoCrop() { return m_isCrop; } + + bool isFillBlank() { return m_isFillBlank; } + + bool isDesaskew() { return m_isDesaskew; } + + bool isConvexHull() { return m_isConvexHull; } + + double threshold() { return m_threshold; } + + const cv::RotatedRect& rotatedROI() { return m_rect; } + + const std::vector& rotatedROIs() { return m_rects; } + + int noise() { return m_noise; } + + int indent() { return m_indent; } + + cv::Size fixedSize() { return m_fixedSize; } + + const std::vector& maxContour() { return m_maxContour; } + + void setAutoCrop(bool enabled) { m_isCrop = enabled; } + + void setFillBlank(bool enabled) { m_isFillBlank = enabled; } + + void setDesaskew(bool enabled) { m_isDesaskew = enabled; } + + void setConvexHull(bool convex) { m_isConvexHull = convex; } + + void setThreshold(double value) { m_threshold = value; } + + void setNoise(int value) { m_noise = value; } + + void setIndent(int value) { m_indent = value; } + + void setFixedSize(cv::Size size) { m_fixedSize = size; } + +private: + cv::Scalar getBackGroudColor(const cv::Mat& image, int total); + + uchar getBackGroudChannelMean(const cv::Mat& gray, int total); + +private: + bool m_isCrop; + bool m_isDesaskew; + bool m_isFillBlank; + bool m_isConvexHull; + bool m_isFillColor; + + double m_threshold; + int m_noise; + int m_indent; + bool m_normalCrop; //涓簍rue涓攎_isCrop m_isDesaskew m_isFillBlank鍧囦负false鏃剁敓鏁堬紝鍥哄畾瑁佸垏閲囩敤鏈浼犵粺鐨勮鍒囨柟寮 + cv::Size m_fixedSize; + cv::RotatedRect m_rect; + std::vector m_maxContour; + std::vector m_rects; +}; + +#endif // !IMAGE_APPLY_AUTO_CROP_H + diff --git a/hgdriver/ImageProcess/ImageApplyBWBinaray.cpp b/hgdriver/ImageProcess/ImageApplyBWBinaray.cpp new file mode 100644 index 0000000..7995a25 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyBWBinaray.cpp @@ -0,0 +1,164 @@ +#include "ImageApplyBWBinaray.h" + +CImageApplyBWBinaray::CImageApplyBWBinaray(ThresholdType type, int threshold, int blockSize, int constant) + : m_threshold(threshold) + , m_type(type) + , m_blockSize(blockSize) + , m_constant(constant) + , m_table(new uchar[256]) +{ + memset(m_table, 255, 256); + memset(m_table, 0, static_cast(m_threshold)); +} + +CImageApplyBWBinaray::CImageApplyBWBinaray() + : m_threshold(120) + , m_type(ThresholdType::THRESH_BINARY) + , m_blockSize(25) + , m_constant(5) + , m_table(new uchar[256]) +{ + memset(m_table, 255, 256); + memset(m_table, 0, static_cast(m_threshold)); +} + +CImageApplyBWBinaray::~CImageApplyBWBinaray(void) +{ + delete[] m_table; +} + +void CImageApplyBWBinaray::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (pDib.empty()) return; + + if (pDib.channels() == 3) + cv::cvtColor(pDib, pDib, cv::COLOR_BGR2GRAY); + + //20.12.29 修改参数为51 10 30 235 + //20.12.30 修改参数为51 20 30 235 + // 修改参数为17 20 110 235 + cv::Mat integ; + int blockSize = 17;//邻域尺寸 + int threshold = 20; + int low = 110; + int up = 235; + int halfSize = blockSize / 2; + int square_blockSize = blockSize * blockSize; + switch (m_type) + { + case ThresholdType::THRESH_BINARY: + cv::integral(pDib, integ, CV_32S); + + for (int j = halfSize; j < integ.rows - halfSize - 1; j++) + { + uchar* data = pDib.ptr(j); + int* idata1 = integ.ptr(j - halfSize); + int* idata2 = integ.ptr(j + halfSize + 1); + for (int i = halfSize; i < integ.cols - halfSize - 1; i++) + { + if (data[i] < low) + data[i] = 0; + else if (data[i] > up) + data[i] = 255; + else + data[i] = data[i] < ((idata2[i + halfSize + 1] - idata2[i - halfSize] - idata1[i + halfSize + 1] + idata1[i - halfSize]) / square_blockSize - threshold) ? 0 : 255; + } + } + + cv::threshold(pDib(cv::Rect(0, 0, halfSize, pDib.rows)), pDib(cv::Rect(0, 0, halfSize, pDib.rows)), m_threshold, 255, cv::THRESH_BINARY); + cv::threshold(pDib(cv::Rect(pDib.cols - halfSize, 0, halfSize, pDib.rows)), pDib(cv::Rect(pDib.cols - halfSize, 0, halfSize, pDib.rows)), m_threshold, 255, cv::THRESH_BINARY); + cv::threshold(pDib(cv::Rect(0, 0, pDib.cols, halfSize)), pDib(cv::Rect(0, 0, pDib.cols, halfSize)), m_threshold, 255, cv::THRESH_BINARY); + cv::threshold(pDib(cv::Rect(0, pDib.rows - halfSize, pDib.cols, halfSize)), pDib(cv::Rect(0, pDib.rows - halfSize, pDib.cols, halfSize)), m_threshold, 255, cv::THRESH_BINARY); + break; + case ThresholdType::THRESH_OTSU: + cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_OTSU); + break; + case ThresholdType::ADAPTIVE_GAUSSIAN: + cv::adaptiveThreshold(pDib, pDib, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, m_blockSize, m_constant); + break; + case ThresholdType::ADAPTIVE_MEAN: + cv::adaptiveThreshold(pDib, pDib, 255, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, m_blockSize, m_constant); + break; + case ThresholdType::ERROR_DIFFUSION: + errorDiffuse(pDib); + break; + default: + break; + } +} + +void CImageApplyBWBinaray::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} + +void CImageApplyBWBinaray::errorDiffuse(cv::Mat& image) +{ + if (image.rows < 3 || image.cols < 3) + { + cv::threshold(image, image, m_threshold, 255, CV_THRESH_BINARY); + return; + } + + cv::Mat dst; + image.convertTo(dst, CV_16S); + + size_t rows = static_cast(image.rows) - 1; + size_t cols = static_cast(image.cols) - 1; + + short** pixels_dst = new short* [static_cast(image.rows)]; + for (int i = 0; i < image.rows; i++) + pixels_dst[i] = reinterpret_cast(dst.data + i * static_cast(dst.step)); + + short error; + for (size_t y = 0; y < rows; y++) + for (size_t x = 1; x < cols; x++) + { + short dstPix = pixels_dst[y][x]; + if (dstPix >= m_threshold) + { + pixels_dst[y][x] = 255; + error = dstPix - 255; + } + else + { + pixels_dst[y][x] = 0; + error = dstPix; + } + + pixels_dst[y][x + 1] += error * 1 / 16; + pixels_dst[y + 1][x - 1] += error * 1 / 16; + pixels_dst[y + 1][x] += error * 1 / 16; + pixels_dst[y + 1][x + 1] += error * 1 / 16; + } + image.release(); + dst.convertTo(image, CV_8U); + + rows++; + uchar* ptr = image.data; + size_t step = image.step; + size_t offset; + for (size_t y = 0; y < rows; y++) + { + offset = y * step; + ptr[offset] = m_table[ptr[offset]]; + offset += cols; + ptr[offset] = m_table[ptr[offset]]; + } + + cols++; + ptr = image.data + step * (rows - 1); + for (size_t x = 0; x < cols; x++) + ptr[x] = m_table[ptr[x]]; + + delete[] pixels_dst; +} \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageApplyBWBinaray.h b/hgdriver/ImageProcess/ImageApplyBWBinaray.h new file mode 100644 index 0000000..d52675a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyBWBinaray.h @@ -0,0 +1,86 @@ +/* + * ==================================================== + + * 鍔熻兘锛氫簩鍊煎寲澶勭悊 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細2020/5/28 v1.1 淇敼浼犵粺浜屽煎寲绠楁硶锛屾敼鐢ㄨ嚜瀹氫箟灞閮ㄨ嚜閫傚簲闃堝肩畻娉 + 2020/5/29 v1.2 鍦ㄤ紶缁熶簩鍊煎寲涔嬪墠娣诲姞澧炲己閿愬寲鏁堟灉锛屼簩鍊煎寲涔嬪悗澧炲姞闄ゅ櫔鏁堟灉 + 2020/6/19 v1.3 缂栧啓鑷畾涔夎嚜閫傚簲闃堝间簩鍊煎寲锛涘彇娑堥攼鍖栧鐞嗭紱淇濈暀闄ゅ櫔鏁堟灉 + 2020/12/21 v1.3.1 璋冩暣鑷傚簲闃堝间笂涓嬮檺 + 2020/12/21 v1.3.2 璋冩暣blockSize,浠庡師鏉ョ殑51璋冩暣鍒25 + * 鐗堟湰鍙凤細v1.3.2 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_BW_BINARAY_H +#define IMAGE_APPLY_BW_BINARAY_H + +#include "ImageApply.h" + +class CImageApplyBWBinaray:public CImageApply +{ +public: + + enum class ThresholdType + { + THRESH_BINARY = 0, //浼犵粺浜屽煎寲 + THRESH_OTSU, //澶ф触闃堝 + + ADAPTIVE_GAUSSIAN, //楂樻柉灞閮ㄨ嚜閫傚簲闃堝 + ADAPTIVE_MEAN, //鍧囧煎眬閮ㄨ嚜閫傚簲闃堝 + + ERROR_DIFFUSION //閿欒鎵╂暎 + }; + + /* + * type [in]:浜屽煎寲妯″紡 + * threshold [in]:闃堝硷紝褰撻夋嫨THRESH_OTSU鏃舵棤鏁 + * blockSize [in]:ADAPTIVE_GAUSSIAN鍜孉DAPTIVE_MEAN妯″紡鏈夋晥锛岃〃绀哄眬閮ㄨ瀵熷潡鐨勫搴 + * constant [in]:ADAPTIVE_GAUSSIAN鍜孉DAPTIVE_MEAN妯″紡鏈夋晥锛屼笌blockSize褰㈡垚姣斾緥鍏崇郴锛屼綔涓哄眬閮ㄧ瓫閫夐槇鍊 + */ + CImageApplyBWBinaray(ThresholdType type, int threshold = 120, int blockSize = 25, int constant = 5); + + CImageApplyBWBinaray(); + + virtual ~CImageApplyBWBinaray(void); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + double getThreshold() { return m_threshold; } + + ThresholdType getThresholdType() { return m_type; } + + int getBlockSize() { return m_blockSize; } + + double getConstant() { return m_constant; } + + void setThreshold(double value) { m_threshold = value; } + + void setThresholdType(ThresholdType type) { m_type = type; } + + void setBlockSize(int value) { m_blockSize = value; } + + void setConstant(double value) { m_constant = value; } + +private: + + void errorDiffuse(cv::Mat& image); + +private: + double m_threshold; + + ThresholdType m_type; + + int m_blockSize; + + double m_constant; + + uchar* m_table; +}; + +#endif //!IMAGE_APPLY_BW_BINARAY_H + diff --git a/hgdriver/ImageProcess/ImageApplyChannel.cpp b/hgdriver/ImageProcess/ImageApplyChannel.cpp new file mode 100644 index 0000000..2997cf3 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyChannel.cpp @@ -0,0 +1,128 @@ +#include "ImageApplyChannel.h" +#include "ImageApplyAdjustColors.h" + +CImageApplyChannel::CImageApplyChannel() + : m_channel(Invalid) + , colors(new CImageApplyAdjustColors(0, 30, 1.0)) +{ +} + +CImageApplyChannel::CImageApplyChannel(Channel channel) + : m_channel(channel) + , colors(new CImageApplyAdjustColors(0, 30, 1.0)) +{ +} + +CImageApplyChannel::~CImageApplyChannel() +{ + if (colors != nullptr) delete colors; +} + +void CImageApplyChannel::apply(cv::Mat& pDib,int side) +{ + (void)side; + if (pDib.empty()) return; + + cv::Mat dst(pDib.rows, pDib.cols, CV_8UC1); + switch (m_channel) + { + case Red: + cv::extractChannel(pDib, dst, 2); + colors->apply(pDib, side); + break; + case Green: + cv::extractChannel(pDib, dst, 1); + break; + case Blue: + cv::extractChannel(pDib, dst, 0); + break; + case All: + colourless(pDib, dst, 80); + break; + case Except_Red: + except_channel(pDib, dst, 2); + break; + case Except_Green: + except_channel(pDib, dst, 1); + break; + case Except_Blue: + except_channel(pDib, dst, 0); + break; + default: + break; + } + pDib.release(); + pDib = dst; + +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit CImageApplyChannel apply"); +#endif // LOG +} + +void CImageApplyChannel::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} + +void CImageApplyChannel::except_channel(const cv::Mat & src, cv::Mat & dst, int channel) +{ + int rows = static_cast(src.total()); + cv::Mat src_temp(rows, 3, CV_8UC1, src.data); + cv::Mat dst_temp(rows, 1, CV_8UC1, dst.data); + + cv::Mat temp1, temp2; + switch (channel) + { + case 0: + temp1 = src_temp(cv::Rect(1, 0, 1, rows)); + temp2 = src_temp(cv::Rect(2, 0, 1, rows)); + cv::addWeighted(temp1, 0.587, temp2, 0.299, 0, dst_temp); + break; + case 1: + temp1 = src_temp(cv::Rect(0, 0, 1, rows)); + temp2 = src_temp(cv::Rect(2, 0, 1, rows)); + cv::addWeighted(temp1, 0.114, temp2, 0.299, 0, dst_temp); + break; + case 2: + temp1 = src_temp(cv::Rect(0, 0, 1, rows)); + temp2 = src_temp(cv::Rect(1, 0, 1, rows)); + cv::addWeighted(temp1, 0.114, temp2, 0.587, 0, dst_temp); + break; + default: + break; + } +} + +void CImageApplyChannel::colourless(const cv::Mat &src, cv::Mat &dst, uchar threshold) +{ + if (src.channels() != 3) + { + dst = src; + return; + } + + cv::Mat hsv; + cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV_FULL); + cv::Mat mv_hsv[3]; + cv::split(hsv, mv_hsv); + size_t total = mv_hsv[1].total(); + uchar* ptr_s = mv_hsv[1].data; + uchar* ptr_v = mv_hsv[2].data; + for (size_t i = 0; i < total; i++) + if (ptr_s[i] > threshold) + { + ptr_s[i] = 0; + ptr_v[i] = 255; + } + cv::merge(mv_hsv, 3, hsv); + cv::cvtColor(hsv, hsv, cv::COLOR_HSV2BGR_FULL); + cv::cvtColor(hsv, dst, cv::COLOR_BGR2GRAY); +} diff --git a/hgdriver/ImageProcess/ImageApplyChannel.h b/hgdriver/ImageProcess/ImageApplyChannel.h new file mode 100644 index 0000000..1b54d44 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyChannel.h @@ -0,0 +1,64 @@ +/* + * ==================================================== + + * 鍔熻兘锛氶氶亾鎻愬彇锛屽張鍚嶉櫎鑹层傚彲鎻愬彇BGR鍥惧儚涓殑鍗曚釜閫氶亾銆佷袱绉嶉氶亾鐨勬贩鍚堜互鍙婂幓闄ゅ僵鑹插儚绱犵殑鍥惧儚锛岀洰鏍囧浘鍍忓潎涓虹伆搴﹀浘 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細v1.0 2020/4/21 + v1.1 2020/6/11 鍦ㄩ櫎绾㈡椂锛屽鍔犲姣斿害锛屾彁楂橀櫎鑹叉晥鏋溿 + v1.2 2020/7/21 淇涔嬪墠澧炲己绾㈢豢钃濇晥鏋滅殑鑹插僵閰嶆瘮銆 + * 鐗堟湰鍙凤細v1.2 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_CHANNEL_H +#define IMAGE_APPLY_CHANNEL_H + +#include "ImageApply.h" + +class CImageApplyAdjustColors; +class CImageApplyChannel : public CImageApply +{ +public: + + typedef enum channel + { + Red, //绾㈣壊閫氶亾 + Green, //缁胯壊閫氶亾 + Blue, //钃濊壊閫氶亾 + All, //鍘婚櫎鎵鏈塇SV鑹插僵缁撴瀯涓紝S澶т簬80鐨勮壊褰 + Invalid, //鏃犳晥 + Except_Red, //缁胯摑鑹查氶亾娣峰悎 + Except_Green, //绾㈣摑鑹查氶亾娣峰悎 + Except_Blue //绾㈢豢鑹查氶亾娣峰悎 + }Channel; + +public: + + CImageApplyChannel(); + + /* + * channel [in]:閫氶亾妯″紡 + * */ + CImageApplyChannel(Channel channel); + + virtual ~CImageApplyChannel(void); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + +private: + + void except_channel(const cv::Mat& src, cv::Mat& dst, int channel); + + void colourless(const cv::Mat& src, cv::Mat& dst, uchar threshold = 80); + +private: + + Channel m_channel; + CImageApplyAdjustColors* colors; +}; + +#endif // !IMAGE_APPLY_CHANNEL_H diff --git a/hgdriver/ImageProcess/ImageApplyColorRecognition.cpp b/hgdriver/ImageProcess/ImageApplyColorRecognition.cpp new file mode 100644 index 0000000..d85896b --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyColorRecognition.cpp @@ -0,0 +1,126 @@ +#include "ImageApplyColorRecognition.h" +#include "ImageApplyHeaders.h" + +static CImageApplyBWBinaray m_bw; +static CImageApplyAdjustColors m_ac(0, 50, 1.0f); + +/// +/// 检测图像是否是彩色。当前逻辑仅针对红色像素进行判断,即存在红色像素则为彩色,否则为非彩色 +/// +/// 待测图像 +/// true为彩色,false为非彩色 +bool isColor(const cv::Mat& image) +{ + if (image.channels() != 3) return false; + + cv::Mat pDib_resize; + cv::resize(image, pDib_resize, cv::Size(image.cols / 9, image.rows / 9), 0, 0, cv::INTER_AREA); + + cv::Mat hsv; + cv::cvtColor(pDib_resize, hsv, cv::COLOR_BGR2HSV_FULL); + std::vector hsv_channels; + cv::split(hsv, hsv_channels); + + cv::Mat range_h1, range_h2, range_s, range_v; + cv::inRange(hsv_channels[0], 0, 85, range_h1); + cv::inRange(hsv_channels[0], 170, 255, range_h2); + cv::inRange(hsv_channels[1], 60, 255, range_s); + cv::inRange(hsv_channels[2], 100, 255, range_v); + + cv::Mat thre = (range_h1 | range_h2) & range_s & range_v; + return (cv::sum(thre)[0] / 255)> 4; +} + +bool isGray(const cv::Mat& image) +{ + if (image.channels() == 3) return true; + + cv::Mat image_clone; + cv::resize(image, image_clone, cv::Size(), 0.25, 0.25); + int channels[] = { 0 }; + int histsize[] = { 256 }; + float range[] = { 0, 256 }; + const float* histRanges[] = { range }; + cv::Mat hist; + cv::calcHist(&image_clone, 1, channels, cv::Mat(), hist, 1, histsize, histRanges, true, false); + + float pixel_count0 = hist.at(0, 0); + float pixel_count255 = hist.at(255, 0); + float total = image_clone.total(); + + return ((pixel_count0 + pixel_count255) / total) > 0.95; +} + +CImageApplyColorRecognition::CImageApplyColorRecognition(ColorRecognitionMode mode) + : m_mode(mode) +{ +} + +CImageApplyColorRecognition::~CImageApplyColorRecognition(void) +{ +} + +void CImageApplyColorRecognition::apply(cv::Mat& pDib, int side) +{ + //先判断是否需要判断是彩色 + if (m_mode == AllColor || m_mode == Color_Gray || m_mode == Color_Mono) + { + //如果是彩色,直接退出 + if (isColor(pDib)) + { + m_result = Color; + return; + } + } + + if (pDib.channels() == 3) + cv::cvtColor(pDib, pDib, cv::COLOR_BGR2GRAY); + + if (m_mode == Color_Gray) + { + m_result = Gray; + return; + } + + if (m_mode == Color_Mono) + { + m_bw.apply(pDib, side); + m_result = Mono; + return; + } + + if (isGray(pDib)) + m_result = Gray; + else + { + m_bw.apply(pDib, side); + m_result = Mono; + } +} + +void CImageApplyColorRecognition::apply(std::vector& mats, bool isTwoSide) +{ + m_results.clear(); + if (mats.empty()) return; + + if (!mats[0].empty()) + apply(mats[0], 0); + + m_results.push_back(m_result); + + if (isTwoSide && mats.size() > 1) + if (!mats[1].empty()) + apply(mats[1], 1); + + m_results.push_back(m_result); +} + +CImageApplyColorRecognition::ColorType CImageApplyColorRecognition::getResult() +{ + return m_result; +} + +std::vector CImageApplyColorRecognition::getResults() +{ + return m_results; +} diff --git a/hgdriver/ImageProcess/ImageApplyColorRecognition.h b/hgdriver/ImageProcess/ImageApplyColorRecognition.h new file mode 100644 index 0000000..e1361f3 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyColorRecognition.h @@ -0,0 +1,68 @@ +锘/* + * ==================================================== + + * 鍔熻兘锛氳壊褰╄瘑鍒紝灏嗚瘑鍒細鈥滅伆搴︹濈殑24浣嶅浘杞寲涓256鑹8浣嶅浘锛 鎶婅瘑鍒负鈥滈粦鐧解濆浘杞寲涓轰簩鍊煎寲鐨8浣嶅浘 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/7/17 + * 鏈杩戜慨鏀规椂闂达細2021/04/19 + * 鐗堟湰鍙凤細v1.0 2020/7/17 + * v1.1 2020/12/15 璋冩暣绛栫暐锛屼粎鍒ゆ柇绾㈣壊鍍忕礌锛屽瓨鍦ㄧ孩鑹插儚绱犱负褰╄壊锛屽惁鍒欎负鐏板害锛涘垹闄よ緭鍑虹粨鏋滐紝鐩存帴杞崲鍥惧儚銆 + * v1.2 2020/12/16 澧炲姞棰滆壊闄愬埗妯″紡锛堣緭鍑虹粨鏋滃彧鍙兘涓ょ锛夛紝澧炲姞缁撴灉璁块棶鎺ュ彛 + * v1.3 2021/04/19 淇敼璇嗗埆绛栫暐锛岃兘澶熻瘑鍒崰姣1鈥扮殑褰╄壊鍥惧儚銆傚彧鍖哄垎褰╄壊鍜岀伆搴﹀浘銆 + * v1.4 2021/06/18 璋冩暣浜岀骇鑹插僵鍖洪棿锛屼粠鍘熸潵鐨刐90, 200]璋冩暣涓篬50, 200]銆 + * ==================================================== + */ + +#ifndef IMAGE_APPLY_COLOR_RECOGNITION_H +#define IMAGE_APPLY_COLOR_RECOGNITION_H + +#include "ImageApply.h" + +class CImageApplyColorRecognition : public CImageApply +{ +public: + + //鑹插僵璇嗗埆妯″紡 + enum ColorRecognitionMode + { + AllColor, //鍏ㄨ壊妯″紡 璇嗗埆缁撴灉鍙兘浼氭槸褰╄壊銆佺伆搴︺侀粦鐧 + Color_Gray, //褰╄壊鐏板害妯″紡 璇嗗埆缁撴灉鍙細鏄僵鑹叉垨鑰呯伆搴 + Color_Mono, //褰╄壊榛戠櫧妯″紡 璇嗗埆缁撴灉鍙細鏄僵鑹叉垨鑰呴粦鐧 + Gray_Mono //鐏板害榛戠櫧妯″紡 璇嗗埆缁撴灉鍙細鏄伆搴︽垨鑰呴粦鐧 + }; + + //鑹插僵绫诲瀷 + enum ColorType + { + Color, //褰╄壊 + Gray, //鐏板害 + Mono //榛戠櫧 + }; +public: + CImageApplyColorRecognition(ColorRecognitionMode mode = AllColor); + + virtual ~CImageApplyColorRecognition(void); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + /// + /// 鑾峰彇鍥剧墖鑹插僵绫诲瀷銆傞厤鍚坴oid apply(cv::Mat&, int)鎺ュ彛浣跨敤 + /// + /// 鑹插僵绫诲瀷 + ColorType getResult(); + + /// + /// 鑾峰彇鍥剧墖鑹插僵绫诲瀷銆傞厤鍚坴oid apply(std::vector&, int)鎺ュ彛浣跨敤 + /// + /// 鑹插僵绫诲瀷鏁扮粍 + std::vector getResults(); + +private: + ColorType m_result; + std::vector m_results; + ColorRecognitionMode m_mode; +}; + +#endif // !IMAGE_APPLY_CONCATENATION_H diff --git a/hgdriver/ImageProcess/ImageApplyConcatenation.cpp b/hgdriver/ImageProcess/ImageApplyConcatenation.cpp new file mode 100644 index 0000000..be8bd0a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyConcatenation.cpp @@ -0,0 +1,108 @@ +#include "ImageApplyConcatenation.h" + +CImageApplyConcatenation::CImageApplyConcatenation() + : m_direction(autoDirection) + , m_BG_color(0, 0, 0) +{ +} + +CImageApplyConcatenation::CImageApplyConcatenation(ConcatMode dir, const cv::Scalar& background) + : m_direction(dir) + , m_BG_color(background) +{ +} + +CImageApplyConcatenation::~CImageApplyConcatenation(void) +{ +} + +void CImageApplyConcatenation::apply(cv::Mat& pDib, int side) +{ + (void)pDib; + (void)side; +} + +void CImageApplyConcatenation::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + if (mats.size() < 2) + { + mats.clear(); + return; + } + + ConcatMode direction; + if (m_direction == ConcatMode::autoDirection) + direction = (mats[0].cols > mats[0].rows) ? ConcatMode::vertical : ConcatMode::horizontal; + else + direction = m_direction; + cv::Mat dst = concat(mats[0], mats[1], direction); + mats.clear(); + mats.push_back(dst); +} + +cv::Mat CImageApplyConcatenation::concat(cv::Mat &front, cv::Mat &back, ConcatMode direction) +{ + cv::Mat dst; + if (direction == horizontal) + { +#if 0 + int top, bottom; + if (front.rows > back.rows) + { + top = (front.rows - back.rows) / 2; + bottom = front.rows - back.rows - top; + cv::copyMakeBorder(back, back, top, bottom, 0, 0, cv::BORDER_CONSTANT); + } + else if (front.rows < back.rows) + { + top = (back.rows - front.rows) / 2; + bottom = back.rows - front.rows - top; + cv::copyMakeBorder(front, front, top, bottom, 0, 0, cv::BORDER_CONSTANT); + } + cv::hconcat(front, back, dst); +#else + int width = cv::max(front.cols, back.cols) * 2; + int height = cv::max(front.rows, back.rows); + + dst = cv::Mat(height, width, front.type(), m_BG_color); + front.copyTo(dst(cv::Rect(0, 0, front.cols, front.rows))); + int offset = front.cols; + front.release(); + back.copyTo(dst(cv::Rect(offset, 0, back.cols, back.rows))); + back.release(); +#endif + } + else if (direction == vertical) + { +#if 0 + int left, right; + if (front.cols > back.cols) + { + left = (front.cols - back.cols) / 2; + right = front.cols - back.cols - left; + cv::copyMakeBorder(back, back, 0, 0, left, right, cv::BORDER_CONSTANT); + } + else if (front.cols < back.cols) + { + left = (back.cols - front.cols) / 2; + right = back.cols - front.cols - left; + cv::copyMakeBorder(front, front, 0, 0, left, right, cv::BORDER_CONSTANT); + } + cv::vconcat(front, back, dst); +#else + int width = cv::max(front.cols, back.cols); + int height = cv::max(front.rows, back.rows) * 2; + + dst = cv::Mat(height, width, front.type(), m_BG_color); + front.copyTo(dst(cv::Rect(0, 0, front.cols, front.rows))); + int offset = front.rows; + front.release(); + back.copyTo(dst(cv::Rect(0, offset, back.cols, back.rows))); + back.release(); +#endif + } + //front.release(); + //back.release(); + return dst; +} diff --git a/hgdriver/ImageProcess/ImageApplyConcatenation.h b/hgdriver/ImageProcess/ImageApplyConcatenation.h new file mode 100644 index 0000000..c4ec540 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyConcatenation.h @@ -0,0 +1,61 @@ +/* + * ==================================================== + + * 功能:拼接,又名对折 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/29 + * 版本号:v1.0 2020/4/21 + * 版本号:v1.1 2020/4/29 : 1、添加m_BG_color接口,可设置图片背景;2、优化内存消耗 + * ==================================================== + */ + +#ifndef IMAGE_APPLY_CONCATENATION_H +#define IMAGE_APPLY_CONCATENATION_H + +#include "ImageApply.h" + + class CImageApplyConcatenation : public CImageApply +{ +public: + //对折方向 + enum ConcatMode + { + horizontal = 0, //左右拼接 吅 + vertical, //上下拼接 吕 + autoDirection + + }; +public: + CImageApplyConcatenation(); //默认m_direction = autoDirection; + + /* + * dir [in]:拼接方向 + * backgroud [in]:背景颜色,默认为黑色 + * */ + CImageApplyConcatenation(ConcatMode dir, const cv::Scalar& backgroud = cv::Scalar(0, 0, 0)); + + virtual ~CImageApplyConcatenation(void); + + virtual void apply(std::vector& mats, bool isTwoSide); + + inline ConcatMode getConcatDirection() { return m_direction; } + + inline void setFildDirection(ConcatMode dir) { m_direction = dir; } + + inline cv::Scalar getBackGroundColor() const { return m_BG_color; } + + inline void setBackGroundColor(const cv::Scalar& color) { m_BG_color = color; } + +private: + + virtual void apply(cv::Mat& pDib, int side); + + cv::Mat concat(cv::Mat& front, cv::Mat& back, ConcatMode direction = autoDirection); + +private: + ConcatMode m_direction; + cv::Scalar m_BG_color; +}; + +#endif // !IMAGE_APPLY_CONCATENATION_H diff --git a/hgdriver/ImageProcess/ImageApplyCrop.cpp b/hgdriver/ImageProcess/ImageApplyCrop.cpp new file mode 100644 index 0000000..ca08427 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCrop.cpp @@ -0,0 +1,40 @@ +#include "ImageApplyCrop.h" + +CImageApplyCrop::CImageApplyCrop(void) +{ +} + + +CImageApplyCrop::~CImageApplyCrop(void) +{ +} + +void CImageApplyCrop::apply(cv::Mat& pDib,int side) +{ + + if (pDib.empty()) + { + return; + } + + if (m_roi.x < 0 || m_roi.y < 0 || m_roi.br().x >= pDib.cols || m_roi.br().y >= pDib.rows || m_roi.width == 0 || m_roi.height == 0) + { + return; + } + + pDib = pDib(m_roi).clone(); +} + +void CImageApplyCrop::apply(std::vector& mats, bool isTwoSide) +{ + if (mats.empty()) return; + + if (!mats[0].empty()) { + apply(mats[0], 0); + } + + if (isTwoSide && mats.size() > 1) { + if (!mats[1].empty()) + apply(mats[1], 1); + } +} \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageApplyCrop.h b/hgdriver/ImageProcess/ImageApplyCrop.h new file mode 100644 index 0000000..366d262 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCrop.h @@ -0,0 +1,28 @@ +#ifndef IMAGE_APPLY_CROP_H +#define IMAGE_APPLY_CROP_H + +#include "ImageApply.h" + +class CImageApplyCrop : public CImageApply +{ + +public: + + CImageApplyCrop(void); + + virtual ~CImageApplyCrop(void); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + cv::Rect getROI() { return m_roi; } + + void setROI(const cv::Rect& rect) { m_roi = rect; } + +private: + + cv::Rect m_roi; +}; + +#endif // !IMAGE_APPLY_CROP_H \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageApplyCustomCrop.cpp b/hgdriver/ImageProcess/ImageApplyCustomCrop.cpp new file mode 100644 index 0000000..9875e38 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCustomCrop.cpp @@ -0,0 +1,32 @@ +#include "ImageApplyCustomCrop.h" + +CImageApplyCustomCrop::CImageApplyCustomCrop(const cv::Rect &rect) + : m_rect(rect) +{ + +} + +CImageApplyCustomCrop::~CImageApplyCustomCrop() +{ + +} + +void CImageApplyCustomCrop::apply(cv::Mat &pDib, int side) +{ + (void)side; + if(pDib.empty()) return; + pDib = pDib(cv::Rect(0, 0, pDib.cols, pDib.rows) & m_rect).clone(); +} + +void CImageApplyCustomCrop::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyCustomCrop.h b/hgdriver/ImageProcess/ImageApplyCustomCrop.h new file mode 100644 index 0000000..3993a51 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCustomCrop.h @@ -0,0 +1,40 @@ +/* + * ==================================================== + + * 功能:自定义裁剪 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 + * 版本号:v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_CUSTOM_CROP_H +#define IMAGE_APPLY_CUSTOM_CROP_H + +#include "ImageApply.h" + +class CImageApplyCustomCrop : public CImageApply +{ +public: + + /* + * rect [in]:裁剪区域 + * */ + CImageApplyCustomCrop(const cv::Rect& rect); + + virtual~ CImageApplyCustomCrop(void); + + inline cv::Rect getROI() const { return m_rect; } + + inline void setROI(const cv::Rect& roi) { m_rect = roi; } + + virtual void apply(cv::Mat &pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); +private: + cv::Rect m_rect; +}; + +#endif // IMAGE_APPLY_CUSTOM_CROP_H diff --git a/hgdriver/ImageProcess/ImageApplyCustomGamma.cpp b/hgdriver/ImageProcess/ImageApplyCustomGamma.cpp new file mode 100644 index 0000000..8132682 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCustomGamma.cpp @@ -0,0 +1,63 @@ +#include "ImageApplyCustomGamma.h" + +CImageApplyCustomGamma::CImageApplyCustomGamma(unsigned char* table,int length) + : emptyPtr(false) +{ + if(table==nullptr) + emptyPtr = true; + + init_gamma_table(); + setLUT(table,length == 256 ? 1 : 3); +} + +CImageApplyCustomGamma::~CImageApplyCustomGamma() +{ +} + +void CImageApplyCustomGamma::setLUT(const unsigned char* table, int byteCount) +{ + if (emptyPtr) + return; + + if (byteCount == 1) + memcpy(m_table_bit8, table, 256); + else if (byteCount == 3) + memcpy(m_table_bit24, table, 768); +} + +void CImageApplyCustomGamma::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (emptyPtr) + return; + int numOfChannels = pDib.channels(); + cv::Mat mat_table(1, 256, CV_8UC(numOfChannels), (numOfChannels == 3) ? m_table_bit24 : m_table_bit8); + cv::LUT(pDib, mat_table, pDib); +} + +void CImageApplyCustomGamma::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} + +void CImageApplyCustomGamma::setTableData(const unsigned char* data, int length) +{ + setLUT(data, length == 256 ? 1 : 3); +} + +void CImageApplyCustomGamma::init_gamma_table() +{ + for (size_t i = 0; i < 256; ++i) + { + m_table_bit8[i] = static_cast(i); + m_table_bit24[i * 3] = m_table_bit24[i * 3 + 1] = m_table_bit24[i * 3 + 2] = static_cast(i); + } +} diff --git a/hgdriver/ImageProcess/ImageApplyCustomGamma.h b/hgdriver/ImageProcess/ImageApplyCustomGamma.h new file mode 100644 index 0000000..0e0613b --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCustomGamma.h @@ -0,0 +1,44 @@ +/* + * ==================================================== + + * 功能:自定义伽马校正。原理为哈希表查值 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 + * 版本号:v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_CUSTOM_CAMMA_H +#define IMAGE_APPLY_CUSTOM_CAMMA_H + +#include "ImageApply.h" + +class CImageApplyCustomGamma : public CImageApply +{ +public: + /* + * table [in]:自定义查值表 + * length [in]:表的长度,灰度表256,彩色表768 + * */ + CImageApplyCustomGamma(unsigned char* table,int length); + + virtual ~CImageApplyCustomGamma(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + void setTableData(const unsigned char* data, int length); +private: + void init_gamma_table(); + void setLUT(const unsigned char* table, int byteCount); + bool emptyPtr; +private: + unsigned char m_table_bit8[256]; + + unsigned char m_table_bit24[768]; +}; + +#endif // !IMAGE_APPLY_CUSTOM_CAMMA_H diff --git a/hgdriver/ImageProcess/ImageApplyCvtColor.cpp b/hgdriver/ImageProcess/ImageApplyCvtColor.cpp new file mode 100644 index 0000000..d69979a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCvtColor.cpp @@ -0,0 +1,57 @@ +#include "ImageApplyCvtColor.h" + +CImageApplyCvtColor::CImageApplyCvtColor(ConversionCodes type) + : m_code(type) +{ +} + +CImageApplyCvtColor::~CImageApplyCvtColor() +{ +} + +void CImageApplyCvtColor::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (pDib.channels() == 1) return; + if (m_code > 3) + cv::cvtColor(pDib, pDib, static_cast(m_code)); + else + { + cv::Mat gray(pDib.size(), CV_8UC1); + if (m_code == BGR_MAX) + { + for (size_t y = 0, rows = pDib.rows; y < rows; y++) + { + uchar* ptr_y = pDib.ptr(y); + uchar* gray_y = gray.ptr(y); + for (size_t x = 0, cols = pDib.cols; x < cols; x++) + gray_y[x] = cv::max(ptr_y[x * 3], cv::max(ptr_y[x * 3 + 1], ptr_y[x * 3 + 2])); + } + } + else if (m_code == BGR_MIN) + { + for (size_t y = 0, rows = pDib.rows; y < rows; y++) + { + uchar* ptr_y = pDib.ptr(y); + uchar* gray_y = gray.ptr(y); + for (size_t x = 0, cols = pDib.cols; x < cols; x++) + gray_y[x] = cv::min(ptr_y[x * 3], cv::min(ptr_y[x * 3 + 1], ptr_y[x * 3 + 2])); + } + } + pDib.release(); + pDib = gray; + } +} + +void CImageApplyCvtColor::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyCvtColor.h b/hgdriver/ImageProcess/ImageApplyCvtColor.h new file mode 100644 index 0000000..2010430 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyCvtColor.h @@ -0,0 +1,52 @@ +/* + * ==================================================== + + * 功能:色彩变换 + * 作者:刘丁维 + * 生成时间:2020/6/19 + * 最近修改时间:v1.0 2020/6/19 + v1.1 2020/6/20 添加BGR转HSV,添加BGR_MAX,添加BGR_MIN + * 版本号:v1.1 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_CONVERT_COLOR_H +#define IMAGE_APPLY_CONVERT_COLOR_H + +#include "ImageApply.h" + +class CImageApplyCvtColor : public CImageApply +{ +public: + + //转换模式 + enum ConversionCodes + { + BGR_MAX = 1, //BGR取大值,转GRAY + BGR_MIN = 2, //BGR取小值,转GRAY + BGR2GRAY = 6, //BGR转GRAY + BGR2HSV = 40 //BGR转HSV + }; + + /* + * type [in]:色彩转换模式,默认BGR转Gray + * */ + CImageApplyCvtColor(ConversionCodes type = BGR2GRAY); + + virtual ~CImageApplyCvtColor(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + inline ConversionCodes getConversionCode() { return m_code; } + + inline void setConversionCode(ConversionCodes code) { m_code = code; } + +private: + ConversionCodes m_code; + +}; + +#endif // !IMAGE_APPLY_CUSTOM_CAMMA_H diff --git a/hgdriver/ImageProcess/ImageApplyDetachNoise.cpp b/hgdriver/ImageProcess/ImageApplyDetachNoise.cpp new file mode 100644 index 0000000..d32951b --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDetachNoise.cpp @@ -0,0 +1,42 @@ +#include "ImageApplyDetachNoise.h" +#include "ImageProcess_Public.h" + +CImageApplyDetachNoise::CImageApplyDetachNoise(int noise) + : m_noise(noise) +{ +} + +CImageApplyDetachNoise::~CImageApplyDetachNoise(void) +{ +} + +void CImageApplyDetachNoise::apply(cv::Mat& pDib, int side) +{ + (void)side; + if (pDib.empty()||pDib.channels() != 1) + return; + + cv::Mat mask; + cv::threshold(pDib, mask, 127, 255, cv::THRESH_BINARY_INV); + + std::vector> contours; + std::vector h; + hg::findContours(mask, contours, h); + + for (const std::vector& contour : contours) + if (contourArea(contour) < m_noise) + fillConvexPoly(pDib, contour, cv::Scalar(255)); +} + +void CImageApplyDetachNoise::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyDetachNoise.h b/hgdriver/ImageProcess/ImageApplyDetachNoise.h new file mode 100644 index 0000000..200985f --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDetachNoise.h @@ -0,0 +1,46 @@ +/* + * ==================================================== + + * 功能:除噪,消除3*3以内的像素点,主要针对黑白图 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 v1.0 + 2020/5/29 v1.1 增加接口,使噪点大小阈值可调 + * 版本号:v1.1 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_DETACH_NOISE_H +#define IMAGE_APPLY_DETACH_NOISE_H + +#include "ImageApply.h" + +class CImageApplyDetachNoise : public CImageApply +{ +public: + CImageApplyDetachNoise(int + + + + + + + + = 1); + + inline int getNoise() { return m_noise; } + + inline void setNoise(int noise) { m_noise = noise; } + + virtual ~CImageApplyDetachNoise(void); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + +private: + int m_noise; +}; +#endif // !IMAGE_APPLY_DETACH_NOISE_H + diff --git a/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp b/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp new file mode 100644 index 0000000..f9863cf --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp @@ -0,0 +1,114 @@ +#include "ImageApplyDiscardBlank.h" +#include "ImageProcess_Public.h" + +CImageApplyDiscardBlank::CImageApplyDiscardBlank(double threshold, int edge, double devTh, double meanTh) + : m_threshold(threshold) + , m_edge(edge) + , m_devTh(devTh) + , m_meanTh(meanTh) +{ +} + +CImageApplyDiscardBlank::~CImageApplyDiscardBlank(void) +{ +} + +void CImageApplyDiscardBlank::apply(cv::Mat& pDib, int side) +{ + if (apply(pDib, m_threshold, m_edge, m_devTh)) + pDib.release(); +} + +void CImageApplyDiscardBlank::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} + +bool scalar_LE(const cv::Scalar& val1, const cv::Scalar& val2) +{ + for (int i = 0; i < 3; i++) + if (val1[i] > val2[i]) + return false; + return true; +} + +bool maxMinCompare(const cv::Mat& img, const cv::Mat& mask, double devTh, double meanTh) +{ + double min, max; + cv::minMaxLoc(img, &min, &max, 0, 0, mask); + if (cv::mean(img, mask)[0] < meanTh) + return false; + return (max - min) < devTh; +} + +bool CImageApplyDiscardBlank::apply(const cv::Mat& pDib, double threshold, int edge, int blockSize, double devTh, double meanTh) +{ + if (pDib.empty()) + return true; + + cv::Mat img_resize; + cv::resize(pDib, img_resize, cv::Size(), 0.2, 0.2); + + cv::Mat threshold_img; + if (img_resize.channels() == 3) + cv::cvtColor(img_resize, threshold_img, cv::COLOR_BGR2GRAY); + cv::threshold(img_resize.channels() == 3 ? threshold_img : img_resize, threshold_img, threshold, 255, CV_THRESH_BINARY); + + std::vector> contours; + std::vector h1; + hg::findContours(threshold_img, contours, h1, cv::RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); + + std::vector contour; + for (const std::vector& sub : contours) + for (const cv::Point& p : sub) + contour.push_back(p); + + cv::RotatedRect rect = hg::getBoundingRect(contour); + rect.size = cv::Size2f(rect.size.width - edge / 2.5, rect.size.height - edge / 2.5); + cv::Point2f box[4]; + rect.points(box); + contour.clear(); + contours.clear(); + + for (size_t i = 0; i < 4; i++) + contour.push_back(box[i]); + contours.push_back(contour); + cv::Mat mask = cv::Mat::zeros(img_resize.size(), CV_8UC1); + hg::fillPolys(mask, contours, cv::Scalar::all(255)); + cv::blur(img_resize, img_resize, cv::Size(3, 3)); + + bool b = true; + if (img_resize.channels() == 3) + { + cv::Mat bgr[3]; + cv::split(img_resize, bgr); + for (size_t i = 0; i < 3; i++) + { + b &= maxMinCompare(bgr[i], mask, devTh, meanTh); + if (!b) break; + } + } + else + b &= maxMinCompare(img_resize, mask, devTh, meanTh); + /* + if (b) + { + cv::imwrite("锟秸帮拷页/img1/" + std::to_string(index) + ".bmp", img_resize); + cv::imwrite("锟秸帮拷页/mask1/" + std::to_string(index) + ".bmp", mask); + } + else + { + cv::imwrite("锟秸帮拷页/img2/" + std::to_string(index) + ".bmp", img_resize); + cv::imwrite("锟秸帮拷页/mask2/" + std::to_string(index) + ".bmp", mask); + }*/ + + return b; +} diff --git a/hgdriver/ImageProcess/ImageApplyDiscardBlank.h b/hgdriver/ImageProcess/ImageApplyDiscardBlank.h new file mode 100644 index 0000000..9b1fa19 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDiscardBlank.h @@ -0,0 +1,48 @@ +锘/* + * ==================================================== + + * 鍔熻兘锛氱┖鐧介〉璇嗗埆銆 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細2020/4/21 v1.0 + 2020/8/12 v1.1 寮鏀緎etIntensity鍜宻etMinArea锛涘彇娑坕sNormal鏍囪瘑浣嶏紱鎵╁ぇsetIntensity鐨勮缃寖鍥达紝浠嶽2, 20]鎵╁ぇ鍒癧1, 100] + 2020/8/25 v1.1.1 绾稿紶妫娴嬬缉杩涳紝浠100鍍忕礌璋冩暣鍒20鍍忕礌 + 2020/10/16 v1.2 娣诲姞鏂版帴鍙o紝鑳藉楂樻晥渚挎嵎鍒ゆ柇鍥剧墖鏄惁涓虹┖鐧介〉 + 2020/10/19 v1.2.1 淇闈欐佺┖鐧介〉鍒ゆ柇璇嗗埆璇垽鐨凚UG + 2021/04/13 v1.3.0 澧炲姞鏍囧噯/绁ㄦ嵁鏍囪瘑浣 + 2021/08/12 v1.3.1 娣诲姞闃叉涓嶅悓opencv鐗堟湰瀵艰嚧璁$畻缁撴灉瀛樺湪宸紓鐨勪唬鐮併 + 2021/12/14 v1.3.2 閲嶆瀯绠楁硶銆 + 2021/12/15 v1.3.3 寰皟鍙傛暟銆 + 2021/12/17 v1.3.4 澧炲姞鑳屾櫙鑹叉帴鍙o紝瀹炵幇瀵圭函鑹茬焊寮犵殑绌虹櫧椤靛垽瀹 + * 鐗堟湰鍙凤細v1.3.4 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_DISCARD_BLANK_H +#define IMAGE_APPLY_DISCARD_BLANK_H + +#include "ImageApply.h" + +class CImageApplyDiscardBlank : public CImageApply +{ +public: + + CImageApplyDiscardBlank(double threshold = 40, int edge = 150, double devTh = 50, double meanTh = 200); + + virtual ~CImageApplyDiscardBlank(void); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + static bool apply(const cv::Mat& pDib, double threshold = 40, int edge = 150, int blockSize = 10, double devTh = 50, double meanTh = 200); + +private: + double m_threshold; + int m_edge; + double m_devTh; + double m_meanTh; +}; + +#endif // !IMAGE_APPLY_DISCARD_BLANK_H \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageApplyDogEarDetection.cpp b/hgdriver/ImageProcess/ImageApplyDogEarDetection.cpp new file mode 100644 index 0000000..40ef3a7 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDogEarDetection.cpp @@ -0,0 +1,67 @@ +#include "ImageApplyDogEarDetection.h" +#include "ImageProcess_Public.h" + +CImageApplyDogEarDetection::CImageApplyDogEarDetection() + : m_threshold(40) + , m_zoom(1.0) + , m_distance(50) + , m_result(false) +{ + +} + +CImageApplyDogEarDetection::CImageApplyDogEarDetection(double threshlod, double zoom, double distance) + : m_threshold(threshlod) + , m_zoom(zoom) + , m_distance(distance) + , m_result(false) +{ + +} + +CImageApplyDogEarDetection::~CImageApplyDogEarDetection() +{ + +} + +void CImageApplyDogEarDetection::apply(cv::Mat &pDib, int side) +{ + m_result = false; + (void)side; + if (pDib.empty()) return; + cv::Mat src; + if (m_zoom != 1.0) + cv::resize(pDib, src, cv::Size(), m_zoom, m_zoom, cv::INTER_NEAREST); + else + src = pDib; + + cv::Mat thre; + hg::threshold_Mat(src, thre, m_threshold); + std::vector hierarchy; + std::vector> contours; + hg::findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL); + + std::vector maxContour = hg::getMaxContour(contours, hierarchy); + if (maxContour.size() == 0) + { + m_result = true; + return; + } + hg::convexHull(maxContour, maxContour); + cv::RotatedRect rect = hg::getBoundingRect(maxContour); + cv::Point2f vertexes[4]; + rect.points(vertexes); + + for (int i = 0; i < 4; i++) + if ((-cv::pointPolygonTest(maxContour, vertexes[i], true)) > (m_distance * m_zoom)) + { + m_result = true; + return; + } +} + +void CImageApplyDogEarDetection::apply(std::vector &mats, bool isTwoSide) +{ + (void)mats; + (void)isTwoSide; +} diff --git a/hgdriver/ImageProcess/ImageApplyDogEarDetection.h b/hgdriver/ImageProcess/ImageApplyDogEarDetection.h new file mode 100644 index 0000000..a05f313 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyDogEarDetection.h @@ -0,0 +1,56 @@ +/* + * ==================================================== + + * 鍔熻兘锛氭姌瑙掓娴嬨傛娴嬪師鐞嗭細璁$畻绾稿紶鐨勭悊璁哄洓瑙掗《鐐癸紝鍒板疄闄呰疆寤撴渶灏忚窛绂汇傚綋浠绘剰椤剁偣鍒拌疆寤撴渶灏忚窛绂昏秴杩囬槇鍊硷紝鍒欏垽瀹氫负鎶樿 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/10/30 + * 鏈杩戜慨鏀规椂闂达細2020/10/30 + * 鐗堟湰鍙凤細v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_DOGEAR_DETECTION_H +#define IMAGE_APPLY_DOGEAR_DETECTION_H + +#include "ImageApply.h" + +class CImageApplyDogEarDetection :public CImageApply +{ +public: + + /// + /// 鎶樿妫娴嬮粯璁ゆ瀯閫犲嚱鏁帮紝threshold = 40锛 zoom = 1.0锛 distance = 50 + /// + CImageApplyDogEarDetection(); + + /// + /// 鎶樿妫娴嬫瀯閫犲嚱鏁 + /// + /// 浜屽煎寲闃堝 + /// 鍘熷浘缂╂斁姣斾緥锛屽浜庡ぇ灏哄鍥惧儚鑰岃█閫氳繃zoom缂╁皬鍥惧儚鍙噺灏戣绠楅噺銆傞粯璁ゅ1.0锛堜笉缂╂斁锛 + /// 鐞嗚椤剁偣鍒板疄闄呰疆寤撴渶灏忚窛绂荤殑闃堝硷紝澶т簬璇ラ槇鍊煎垯鍒ゅ畾涓烘姌瑙掞紝榛樿鍊50锛堝儚绱狅級 + CImageApplyDogEarDetection(double threshlod, double zoom = 1.0, double distance = 50); + + virtual ~CImageApplyDogEarDetection(void); + + /// + /// 鑾峰彇妫娴嬬粨鏋溿傝鍑芥暟椤诲湪璋冪敤apply涔嬪悗浣跨敤銆 + /// + /// true涓烘姌瑙掞紝false涓轰笉鎶樿 + inline bool getResult() { return m_result; } + + virtual void apply(cv::Mat& pDib, int side); + +private: + virtual void apply(std::vector& mats, bool isTwoSide); + +private: + double m_threshold; + double m_zoom; + double m_distance; + + bool m_result; +}; + +#endif // IMAGE_APPLY_DOGEAR_DETECTION_H diff --git a/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.cpp b/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.cpp new file mode 100644 index 0000000..d66e986 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.cpp @@ -0,0 +1,150 @@ +#include "ImageApplyFadeBackGroundColor.h" + + +CImageApplyFadeBackGroudColor::CImageApplyFadeBackGroudColor(int threshold, int offset, int range) + : m_threshold(threshold) + , m_offset(offset) + , m_range(range) +{ + memset(m_table1, 255, 768); + memset(m_table1, 0, m_threshold * 3); + + memset(m_table2, 255, 256 * 3); + for (size_t i = 0; i < 256; i++) + m_table2[i] = i; +} + +CImageApplyFadeBackGroudColor::~CImageApplyFadeBackGroudColor() +{ +} + +void CImageApplyFadeBackGroudColor::apply(cv::Mat& pDib, int side) +{ + if (pDib.channels() != 3) + return; + +#if 0 + cv::Mat mask; + cv::cvtColor(pDib, mask, cv::COLOR_BGR2GRAY); + cv::threshold(mask, mask, m_threshold, 255, cv::THRESH_BINARY); + //cv::imwrite("mask.jpg", mask); + cv::Mat bgr[3]; + cv::split(pDib, bgr); + + int histSize = 255; + float range[] = { 0, 255 }; + const float* histRange = { range }; + + cv::Mat hist_bgr[3]; + cv::Scalar mean_bgr; + for (size_t i = 0; i < 3; i++) + { + cv::calcHist(&bgr[i], 1, 0, mask, hist_bgr[i], 1, &histSize, &histRange); + double maxVal = 0; + cv::Point maxLoc; + cv::minMaxLoc(hist_bgr[i], NULL, &maxVal, NULL, &maxLoc); + mean_bgr[i] = maxLoc.y; + } + + cv::add(pDib, cv::Scalar::all(255 + m_offset) - mean_bgr, pDib, mask); +#else + fadeBackground(pDib.data, pDib.step, pDib.rows, m_threshold, m_offset, m_range); +#endif +} + +void CImageApplyFadeBackGroudColor::fadeBackground(unsigned char* data, int bytesPerLine, int height, int threshold, int offset, int range) +{ + int hist_bgr[3][256] = { 0 }; + + int width = bytesPerLine / 3; + unsigned char* mask = new unsigned char[width * height]; + unsigned char* ptr_data = data; + unsigned char* ptr_mask = mask; + + //创建掩模mask,并且统计三通道的直方图 + for (size_t i = 0; i < height; i++) + { + int x = 0; + unsigned char b = 0; + for (size_t j = 0; j < width; j++) + { + b = m_table1[ptr_data[x] + ptr_data[x + 1] + ptr_data[x + 2]]; + ptr_mask[j] = b; + for (size_t k = 0; k < 3; k++) + hist_bgr[k][ptr_data[x + k] & b]++; + x += 3; + } + ptr_data += bytesPerLine; + ptr_mask += width; + } + + //统计背景色 + int max_vals[3] = { 0 }; + int max_indexes[3]; + + for (size_t i = 1; i < 256; i++) + for (size_t j = 0; j < 3; j++) + if (hist_bgr[j][i] > max_vals[j]) + { + max_vals[j] = hist_bgr[j][i]; + max_indexes[j] = i; + } + + //创建背景色误查值表,在误差±range范围内的颜色被同样视为背景色 + for (size_t i = 0; i < 3; i++) + { + memset(m_table_rgb[i], 0, 256); + int start = cv::max(max_indexes[i] - range, 0); + int end = cv::min(max_indexes[i] + range, 255); + memset(m_table_rgb[i] + start, 255, end - start + 1); + } + + //根据背景色误差查值表,更新掩模,排除背景色以外的内容 + ptr_data = data; + ptr_mask = mask; + for (size_t i = 0; i < height; i++) + { + int x = 0; + for (size_t j = 0; j < width; j++) + { + ptr_mask[j] &= m_table_rgb[0][ptr_data[x]] & m_table_rgb[1][ptr_data[x + 1]] & m_table_rgb[2][ptr_data[x + 2]]; + x += 3; + } + ptr_data += bytesPerLine; + ptr_mask += width; + } + + //根据掩模,除背景色 + unsigned char offset_rgb[3]; + for (size_t i = 0; i < 3; i++) + offset_rgb[i] = 255 + offset - max_indexes[i]; + + ptr_data = data; + ptr_mask = mask; + for (size_t i = 0; i < height; i++) + { + int x = 0; + for (size_t j = 0; j < width; j++) + { + for (size_t k = 0; k < 3; k++) + ptr_data[x + k] = m_table2[(int)ptr_data[x + k] + (offset_rgb[k] & ptr_mask[j])]; + x += 3; + } + ptr_data += bytesPerLine; + ptr_mask += width; + } + delete[] mask; +} + +void CImageApplyFadeBackGroudColor::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) + if (!var.empty()) + { + apply(var, i); + i++; + } +} + diff --git a/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.h b/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.h new file mode 100644 index 0000000..493883a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyFadeBackGroundColor.h @@ -0,0 +1,64 @@ +/* + * ==================================================== + + * 功能:消除文稿纸张底色 + * 作者:刘丁维 + * 生成时间:2020/11/30 + * 最近修改时间:2021/04/14 v2.0 改为档位设置 + * 2021/04/14 v2.1 调整方案,与LINUX方案保持一致。 + * 2021/08/03 v2.2 自定义获取饱和度算法,避免不同opencv版本结果存在偏差。 + * 2021/10/12 v2.3 重构算法,去除原来分档设定改为自动识别颜色,并去除底色。 + * 2021/10/23 v3.0 重构算法。避开黑色背景(制作掩模),计算文稿平均底色,并进行增白。 + * 2021/10/26 v3.1 构造函数增加参数,可设置生成掩模时的阈值。 + * 2021/10/28 v3.2 重构算法,改用纯C++代码实现功能,提高处理速度。 + * 2021/10/29 v3.3 优化算法,增加range参数。针对复杂内容的图像,能够有效甄别背景和非背景内容。 + * 版本号:v3.2 + + * ==================================================== + */ + + +#ifndef IMAGE_APPLY_FADE_BACKGROUND_COLOR_H +#define IMAGE_APPLY_FADE_BACKGROUND_COLOR_H + +#include "ImageApply.h" + +class CImageApplyAdjustColors; +class CImageApplyFadeBackGroudColor : public CImageApply +{ +public: + /// + /// 构造函数 + /// + /// 在自动识别增白参数的基础上,增加的偏移量。取值范围[-255, 255] + CImageApplyFadeBackGroudColor(int threshold = 100, int offset = 0, int range = 40); + + virtual ~CImageApplyFadeBackGroudColor(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + +private: + + /// + /// 除文稿底色算法,仅支持24位图像 + /// + /// 图像数据头指针 + /// 每行数据大小 + /// 图像高度 + /// 阈值,参考值为100 + /// 文稿底色增亮偏移量,默认为0。值越大,背景越白,反之越暗 + /// 底色误差范围,色彩与识别到的底色误差在[-range, range]范围内,视为底色,否则不会被消除 + void fadeBackground(unsigned char* data, int bytesPerLine, int height, int threshold, int offset, int range); + +private: + int m_threshold; + int m_offset; + int m_range; + uchar m_table1[768]; + uchar m_table2[512]; + uchar m_table_rgb[3][256]; +}; + +#endif // !IMAGE_APPLY_FADE_BACKGROUND_COLOR_H diff --git a/hgdriver/ImageProcess/ImageApplyFilter.cpp b/hgdriver/ImageProcess/ImageApplyFilter.cpp new file mode 100644 index 0000000..80e40b9 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyFilter.cpp @@ -0,0 +1,97 @@ +#include "ImageApplyFilter.h" + +CImageApplyFilter::CImageApplyFilter(FilterMode type, int kSize) + : m_type(type) + , m_kernel(kSize) +{ + m_kSize = (m_type == FilterMode::Sharpen || m_type == FilterMode::AverBlur) ? 5 : 9; +} + +CImageApplyFilter::~CImageApplyFilter() +{ +} + +void CImageApplyFilter::apply(cv::Mat& pDib, int side) +{ +#ifdef LOG + FileTools::write_log("imgprc.txt", "enter CImageApplySharpen apply"); +#endif // LOG + + switch (m_type) + { + case FilterMode::Sharpen: + case FilterMode::Sharpen_More: + sharpen(pDib, m_kSize); + break; + case FilterMode::AverBlur: + case FilterMode::AverBlur_More: + averblur(pDib, static_cast(m_kSize)); + break; + case FilterMode::BilateralFilter: + bilateralFilter(pDib, m_kernel); + break; + case FilterMode::GaussianBlur: + gaussianBlur(pDib, m_kernel); + break; + case FilterMode::BrightSharp: + brightSharp(pDib); + break; + } + +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit CImageApplySharpen apply"); +#endif // LOG +} + +void CImageApplyFilter::apply(std::vector& mats, bool isTwoSide) +{ + if (mats.empty()) return; + + if (!mats[0].empty()) + apply(mats[0], 0); + + if (isTwoSide && mats.size() > 1) + if (!mats[1].empty()) + apply(mats[1], 1); +} + +void CImageApplyFilter::averblur(cv::Mat& src, int kSize) +{ + cv::blur(src, src, cv::Size(kSize, kSize)); +} + +void CImageApplyFilter::sharpen(cv::Mat& src, float kSize) +{ + float other = (1.0f - kSize) / 4; + float kernel_data[] = { 0, other, 0, other, kSize, other, 0, other, 0 }; + cv::Mat kernel(3, 3, CV_32FC1, kernel_data); + + cv::filter2D(src, src, src.depth(), kernel); +} + +void CImageApplyFilter::brightSharp(cv::Mat& src) +{ + const float a = -0.49f; + const float b = 3.0f; + float kernel_data[] = + { + 0, a, 0, + a, b, a, + 0, a, 0 + }; + cv::Mat kernel(3, 3, CV_32FC1, kernel_data); + cv::filter2D(src, src, src.depth(), kernel); +} + +void CImageApplyFilter::bilateralFilter(cv::Mat& src, double kernel) +{ + cv::Mat dst; + cv::bilateralFilter(src, dst, static_cast(kernel), kernel * 2, kernel / 2); + src.release(); + src = dst; +} + +void CImageApplyFilter::gaussianBlur(cv::Mat src, int kSize) +{ + cv::GaussianBlur(src, src, cv::Size(kSize, kSize), 0); +} diff --git a/hgdriver/ImageProcess/ImageApplyFilter.h b/hgdriver/ImageProcess/ImageApplyFilter.h new file mode 100644 index 0000000..09e4675 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyFilter.h @@ -0,0 +1,64 @@ +/* + * ==================================================== + + * 功能:滤镜。目前包括锐化、加强锐化、模糊和加强模糊 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 v1.0 + 2020/7/17 v1.1 增加双边滤波(BilateralFilter)效果,用于实现“背景平滑”功能; + 2020/11/30 v1.2 增加高斯模糊(GaussianBlur) + 2021/07/21 v1.2.1 增加增亮锐化(BrightSharp) + 2021/10/19 v1.2.2 调整增亮锐化参数 + * 版本号:v1.2.2 + + * ==================================================== + */ + + +#ifndef IMAGE_APPLY_FILTER_H +#define IMAGE_APPLY_FILTER_H + +#include "ImageApply.h" + +class CImageApplyFilter : public CImageApply +{ +public: + enum FilterMode + { + None, + Sharpen, //锐化 + Sharpen_More, //进一步锐化 + AverBlur, //模糊 + AverBlur_More, //进一步模糊 + BilateralFilter, //双边滤波--背景平滑,减少复杂背景的色彩数量,利于jpg等压缩比例 + GaussianBlur, + BrightSharp //5 * 5提亮锐化 + }; + + /* + * sharpentype [in]:滤镜模式 + */ + CImageApplyFilter(FilterMode type, int kSize = 30); + + virtual ~CImageApplyFilter(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); +private: + void averblur(cv::Mat& src, int kSize); + + void sharpen(cv::Mat& src, float kSize); + + void brightSharp(cv::Mat& src); + + void bilateralFilter(cv::Mat& src, double kernel); + + void gaussianBlur(cv::Mat src, int kSize); +private: + int m_type; + float m_kSize; + double m_kernel; +}; + +#endif // !IMAGE_APPLY_SHARPEN_H diff --git a/hgdriver/ImageProcess/ImageApplyHSVCorrect.cpp b/hgdriver/ImageProcess/ImageApplyHSVCorrect.cpp new file mode 100644 index 0000000..4b558a8 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyHSVCorrect.cpp @@ -0,0 +1,144 @@ +#include "ImageApplyHSVCorrect.h" +#include + +CImageApplyHSVCorrect::CImageApplyHSVCorrect() + : m_table(new uint[256 * 256 * 256]) +{ + initLUT(); +} + +CImageApplyHSVCorrect::CImageApplyHSVCorrect(CorrectOption mode) + : m_table(new uint[256 * 256 * 256]) +{ + initLUT(); + switch (mode) + { + case CImageApplyHSVCorrect::Red_Removal: + set_HSV_value(std::pair(0, 63), std::pair(20, 255), std::pair(160, 255), 0x00FFFFFF); + set_HSV_value(std::pair(191, 255), std::pair(20, 255), std::pair(160, 255), 0x00FFFFFF); + break; + default: + break; + } +} + +CImageApplyHSVCorrect::~CImageApplyHSVCorrect() +{ + delete [] m_table; +} + +void CImageApplyHSVCorrect::apply(cv::Mat &pDib, int side) +{ + (void)side; + if (pDib.empty() || pDib.channels() != 3) return; + + uchar* src = pDib.data; + cv::Mat z = cv::Mat::zeros(pDib.size(), CV_8UC3); + uchar* dst = z.data; + + int total = pDib.total(); +#pragma omp parallel for + for (int i = 0; i < total; i++) + { + int offset = i * 3; + int index = *reinterpret_cast(src + offset) & 0x00ffffff; + uint color = m_table[index]; + *reinterpret_cast(dst + offset) |= color; + } + pDib = z; +} + +void CImageApplyHSVCorrect::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} + +void CImageApplyHSVCorrect::initLUT() +{ +#if 0 + uchar h, s, v; +#endif + for (uint b = 0; b < 256; b++) + for (uint g = 0; g < 256; g++) + for (uint r = 0; r < 256; r++) + { +#if 0 + RGB_2_HSV_full(r, g, b, h, s, v); + + uint index = b | (g << 8) | (r << 16); + if (h < 12 || h > 245) + m_table[index] = index & 0x00ffffff; + else + m_table[index] = (v | (v << 8) | (v << 16)) & 0x00ffffff; +#else + m_table[b | (g << 8) | (r << 16)] = b | (g << 8) | (r << 16); +#endif + } +} + +void CImageApplyHSVCorrect::set_single(const uint src_b, const uint src_g, const uint src_r, + const uint dst_b, const uint dst_g, const uint dst_r) +{ + m_table[src_b | (src_g << 8) | (src_r << 16)] = dst_b | (dst_g << 8) | (dst_r << 16); +} + +void CImageApplyHSVCorrect::set_HSV_value(const std::pair& range_h, + const std::pair& range_s, + const std::pair& range_v, + uint bgr) +{ + uchar h, s, v; + for (int b = 0; b < 256; b++) + for (int g = 0; g < 256; g++) + for (int r = 0; r < 256; r++) + { + RGB_2_HSV_full(r, g, b, h, s, v); + if (contained(h, range_h) && contained(s, range_s) && contained(v, range_v)) + m_table[(b | (g << 8) | (r << 16)) & 0x00ffffff] = bgr & 0x00ffffff; + } +} + +void CImageApplyHSVCorrect::set_table(const uint* table) +{ + memcpy(m_table, table, 256 * 256 * 256); +} + +bool CImageApplyHSVCorrect::contained(uchar value, const std::pair &range) +{ + return value >= range.first && value <= range.second; +} + +void CImageApplyHSVCorrect::RGB_2_HSV_full(int r, int g, int b, uchar& h, uchar& s, uchar& v) +{ + int minn = cv::min(r, cv::min(g, b)); + int maxx = cv::max(r, cv::max(g, b)); + v = static_cast(maxx); //V + + int delta = maxx - minn; + float _h; + if (maxx == 0) + { + h = s = v = 0; + return; + } + else + s = static_cast(delta * 255 / maxx); + + if (r == maxx) + _h = static_cast(g - b) / static_cast(delta); + else if (g == maxx) + _h = 2 + static_cast(b - r) / static_cast(delta); + else + _h = 4 + static_cast(r - g) / static_cast(delta); + + float __h = _h * 42.6666666667f; + h = (__h >= 0) ? static_cast(__h) : static_cast(__h + 256); +} diff --git a/hgdriver/ImageProcess/ImageApplyHSVCorrect.h b/hgdriver/ImageProcess/ImageApplyHSVCorrect.h new file mode 100644 index 0000000..8816d13 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyHSVCorrect.h @@ -0,0 +1,87 @@ +/* + * ==================================================== + + * 功能:彩色图像,色彩校正。基于LUT实现,预设BGR查值表(表达所有HVS),对BGR原图进行查值校正。 + * 作者:刘丁维 + * 生成时间:2020/3/21 + * 最近修改时间:v1.0 2020/3/21 + v1.1 2020/6/15 调整除红效果的HSV取值范围 + * 版本号:v1.1 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_COLOR_CORRECT_H +#define IMAGE_APPLY_COLOR_CORRECT_H + +#include "ImageApply.h" + +class CImageApplyHSVCorrect : public CImageApply +{ +public: + enum CorrectOption + { + Red_Removal //除掉红色。红色定义H:[0, 85]∪[170, 255],S:[10, 255],V:[120,255] + }; +public: + + CImageApplyHSVCorrect(); + + /* + * mode [in]:预设初色模式 + */ + CImageApplyHSVCorrect(CorrectOption mode); + + virtual ~CImageApplyHSVCorrect(); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + /* + * 函数功能:初始化查值表,按照RGB(R在高位,B在低位)的32位数据进行索引,值与索引一致。 + 表中索引范围[0,0x00FFFFFF]。在构造函数中会默认调用该函数。 + */ + void initLUT(); + + /* + * 函数功能:将查值表指定RGB索引值设置为目标值。 + 索引 = src_b | (src_g << 8) | (src_r << 16) + 值 = dst_b | (dst_g << 8) | (dst_r << 16) + * src_b:[in] 原查值表B通道索引 + * src_g:[in] 原查值表G通道索引 + * src_r:[in] 原查值表R通道索引 + * dst_b:[in] 目标查值表B通道值 + * dst_g:[in] 目标查值表G通道值 + * dst_r:[in] 目标查值表R通道值 + */ + void set_single(const uint src_b, const uint src_g, const uint src_r, + const uint dst_b, const uint dst_g, const uint dst_r); + /* + * 函数功能:按照HSV色彩空间描述色彩范围,将该范围对应的BGR索引设置为0x00FFFFFF(默认白色) + * range_h:[in] H分量范围,取值范围[0, 255] + * range_s:[in] S分量范围,取值范围[0, 255] + * range_v:[in] V分量范围,取值范围[0, 255] + * bgr:[in] 用uint表示BGR值,B在低位,R在高位 + */ + void set_HSV_value(const std::pair& range_h, + const std::pair& range_s, + const std::pair& range_v, + uint bgr = 0x00FFFFFF); + + /* + * 函数功能:设置外部查值表,表默认长度为 256 * 256 * 256 * sizeof(uint) + * table:[in] 数组指针 + */ + void set_table(const uint* table); + +private: + static bool contained(uchar value, const std::pair& range); + + static void RGB_2_HSV_full(int r, int g, int b, uchar& h, uchar& s, uchar& v); + +private: + uint* m_table; +}; + +#endif diff --git a/hgdriver/ImageProcess/ImageApplyHeaders.h b/hgdriver/ImageProcess/ImageApplyHeaders.h new file mode 100644 index 0000000..159f71a --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyHeaders.h @@ -0,0 +1,33 @@ +锘#ifndef IMAGE_APPLY_HEADER_H +#define IMAGE_APPLY_HEADER_H + +#include "ImageApply.h" +#include "ImageApplyAdjustColors.h" +#include "ImageApplyAutoCrop.h" +#include "ImageApplyBWBinaray.h" +#include "ImageApplyChannel.h" +#include "ImageApplyCrop.h" +#include "ImageApplyDiscardBlank.h" +#include "ImageApplyOutHole.h" +#include "ImageApplyDogEarDetection.h" +#include "ImageApplyResize.h" +#include "ImageApplyRotation.h" +#include "ImageApplySharpen.h" +#include "ImageApplyConcatenation.h" +#include "ImageApplyHSVCorrect.h" +#include "ImageApplyDetachNoise.h" +#include "ImageApplyColorRecognition.h" +#include "ImageApplyUV.h" +#include "ImageApplyAutoContrast.h" +#include "ImageApplyCustomCrop.h" +#include "ImageApplyCustomGamma.h" +#include "ImageApplyCvtColor.h" +#include "ImageApplyFadeBackGroundColor.h" +#include "ImageApplyFilter.h" +#include "ImageApplyRefuseInflow.h" +#include "ImageApplySplit.h" +#include "ImageApplyTextureRemoval.h" +#include "ImageMulti.h" +#include "ImageMultiOutputRed.h" +#include "ImageApplySplit.h" +#endif diff --git a/hgdriver/ImageProcess/ImageApplyOutHole.cpp b/hgdriver/ImageProcess/ImageApplyOutHole.cpp new file mode 100644 index 0000000..3baeeff --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyOutHole.cpp @@ -0,0 +1,323 @@ +锘#include "ImageApplyOutHole.h" +#include "ImageProcess_Public.h" + +#ifdef LOG +#include "Device/filetools.h" +#endif // LOG + +CImageApplyOutHole::CImageApplyOutHole(void) + : CImageApply() + , m_borderSize(600) + , m_edgeScale(0.1f) + , m_threshold(100) +{ +} + +CImageApplyOutHole::CImageApplyOutHole(float borderSize, float edgeScale, double threshold) + : CImageApply() + , m_borderSize(borderSize) + , m_edgeScale(edgeScale) + , m_threshold(threshold) +{ +} + +CImageApplyOutHole::~CImageApplyOutHole(void) +{ +} + +void CImageApplyOutHole::apply(cv::Mat& pDib, int side) +{ + (void)pDib; + (void)side; +} + +void CImageApplyOutHole::apply(std::vector& mats, bool isTwoSide) +{ +#ifdef LOG + FileTools::write_log("imgprc.txt", "enter ImageOutHole apply"); +#endif // LOG + + if (mats.size() < 2) + { +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit ImageOutHole apply"); +#endif // LOG + return; + } + + if (mats[0].empty() || mats[1].empty()) + { +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit ImageOutHole apply"); +#endif // LOG + return; + } + + //浜屽煎寲姝e弽闈㈠浘鍍 + cv::Mat front = mats[0]; + cv::Mat back = mats[1]; + cv::Mat front_thre, back_thre; + hg::threshold_Mat(front, front_thre, m_threshold); + hg::threshold_Mat(back, back_thre, m_threshold); + + cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(10, 1)); + cv::morphologyEx(front_thre, front_thre, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + cv::morphologyEx(back_thre, back_thre, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + //鍙嶉潰浜屽煎寲鍥惧儚姘村钩缈昏浆 + cv::flip(back_thre, back_thre, 1); //1:Horizontal + + //姝e弽闈㈠浘鍍忓杈 + std::vector> contours_front, contours_back; + std::vector b1_front, b1_back; + hg::findContours(front_thre.clone(), contours_front, b1_front, cv::RETR_EXTERNAL); + hg::findContours(back_thre.clone(), contours_back, b1_back, cv::RETR_EXTERNAL); + + //鎻愬彇姝e弽闈㈠浘鍍忔渶澶ц疆寤 + std::vector maxContour_front = hg::getMaxContour(contours_front, b1_front); + std::vector maxContour_back = hg::getMaxContour(contours_back, b1_back); + + cv::RotatedRect rrect_front = hg::getBoundingRect(maxContour_front); //鎻愬彇姝i潰鏈澶ц疆寤撶殑鏈灏忓鎺ョ煩褰 + cv::RotatedRect rrect_back = hg::getBoundingRect(maxContour_back); //鎻愬彇鍙嶉潰鏈澶ц疆寤撶殑鏈灏忓鎺ョ煩褰 + + //濡傛灉姝e弽闈㈠浘鍍忓昂瀵稿樊寮傝秴杩20涓儚绱狅紝鐩存帴鏀惧純澶勭悊 + if (cv::abs(rrect_front.size.width - rrect_back.size.width) > 20 || + cv::abs(rrect_front.size.height - rrect_back.size.height) > 20) + return; + + //鎻愬彇姝e弽闈㈠浘鍍忛噸鍙犻儴鍒嗗尯鍩 + cv::Rect roi_front, roi_back; + cv::RotatedRect mask_rotatedRect; + getRoi(rrect_front, rrect_back, cv::Size(front.cols, front.rows), roi_front, roi_back, mask_rotatedRect); + + cv::Mat roiMat_front(front_thre, roi_front); //鍦ㄦ闈簩鍊煎浘鍍忎腑鎴彇閲嶅彔閮ㄥ垎 + cv::Mat roiMat_back(back_thre, roi_back); //鍦ㄥ弽闈簩鍊煎浘鍍忎腑鎴彇閲嶅彔閮ㄥ垎 + + //姝e弽闈簩鍊煎浘鍍忓仛鎴栬繍绠楋紝鐪熸闀傜┖鍖哄煙淇濈暀0锛屽叾浠栧湴鏂瑰~鍏呬负255 + cv::Mat mask; + cv::bitwise_or(roiMat_front, roiMat_back, mask); //鎴栬繍绠楋紝姝e弽闈簩鍊煎浘鍍忛噸鍙 + + //cv::imwrite("roiMat_front.jpg", roiMat_front); + //cv::imwrite("roiMat_back.jpg", roiMat_back); + + //浜屽煎浘鍍忛噸鍙犲浘鍍忛鑹插彇鍙嶏紝鑶ㄨ儉锛屾彁鍙栬疆寤 + cv::bitwise_not(mask, mask); //鍙嶈壊 + + element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10)); + cv::dilate(mask, mask, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar(255)); //鑶ㄨ儉绠楁硶锛屽澶у瓟娲炶繛閫氬尯鍩熼潰绉 + + //涓轰簡閬垮厤瀛旀礊褰诲簳璐┛绾歌竟锛屼汉涓虹粯鍒剁焊寮犺疆寤擄紝纭繚鎵鏈夊瓟娲炰负灏侀棴鍥惧舰锛屼笉浼氫笌鑳屾櫙绮樿繛 + cv::polylines(mask, hg::getVertices(mask_rotatedRect), true, cv::Scalar(0), 15); //缁樺埗绾稿紶鐭╁舰杈圭紭 + + std::vector> contours_mask; + std::vector b1_mask; + hg::findContours(mask, contours_mask, b1_mask, cv::RETR_TREE); //鎻愬彇閲嶅彔鍥惧儚杞粨 + + //杩囨护闈炲瓟娲炵殑鑱旈氬尯鍩 + std::vector> hole_contours = filterPoly(contours_mask, b1_mask, mask_rotatedRect, m_edgeScale, m_borderSize); + //for (size_t i = 0; i < hole_contours.size(); i++) + // cv::drawContours(mask, hole_contours, static_cast(i), cv::Scalar(127), 2); + //cv::imwrite("mask.jpg", mask); + cv::Scalar color = getBackGroudColor(front(roi_front), rrect_front.size.area()); + for (size_t i = 0; i < hole_contours.size(); i++) + { + std::vector> contourss_temp; + contourss_temp.push_back(hole_contours[i]); + cv::Mat front_temp = front(roi_front); + hg::fillPolys(front_temp, contourss_temp, color); + } + + if (isTwoSide) + { + int width_ = roi_back.width; + roi_back.x = back.cols - roi_back.width - roi_back.x; //鍥犱负涔嬪墠鍙嶉潰鍥惧儚缈昏浆锛屾墍浠ョ幇鍦≧OI涔熻杩涜鐩稿簲缈昏浆 + color = getBackGroudColor(back(roi_back), rrect_front.size.area()); + for (size_t i = 0; i < hole_contours.size(); i++) + { + std::vector hole_contour; + for (size_t j = 0; j < hole_contours[i].size(); j++) + hole_contour.push_back(cv::Point(width_ - hole_contours[i][j].x - 1, hole_contours[i][j].y)); + + std::vector> contours_temp; + contours_temp.push_back(hole_contour); + cv::Mat back_temp = back(roi_back); + hg::fillPolys(back_temp, contours_temp, color); + } + } +#ifdef LOG + FileTools::write_log("imgprc.txt", "exit ImageOutHole apply"); +#endif // LOG +} + +void CImageApplyOutHole::getRoi(cv::RotatedRect rrect_front, cv::RotatedRect rrect_back, cv::Size srcSize, + cv::Rect& roi_front, cv::Rect& roi_back, cv::RotatedRect& mask_rotatedRect) +{ + cv::Size size(static_cast(rrect_front.size.width + rrect_back.size.width) / 2, static_cast(rrect_front.size.height + rrect_back.size.height) / 2); + float angle = (rrect_front.angle + rrect_back.angle) / 2; + + rrect_front.size = rrect_back.size = size; + rrect_front.angle = rrect_back.angle = angle; + + roi_front = rrect_front.boundingRect(); + roi_back = rrect_back.boundingRect(); + + if (roi_front.width != roi_back.width || roi_front.height != roi_back.height) + { + roi_front.height = roi_back.height; + roi_front.width = roi_back.width; + } + + cv::Point offset(0, 0); + int top = std::min(roi_front.y, roi_back.y); + if (top < 0) + { + roi_front.y -= top; + roi_back.y -= top; + roi_front.height += top; + roi_back.height += top; + offset.y += top; + } + + int left = std::min(roi_front.x, roi_back.x); + if (left < 0) + { + roi_front.x -= left; + roi_back.x -= left; + roi_front.width += left; + roi_back.width += left; + offset.x += left; + } + + int right = std::max(roi_front.x + roi_front.width, roi_back.x + roi_back.width); + if (right >= srcSize.width) + { + roi_front.width -= (right - srcSize.width + 1); + roi_back.width -= (right - srcSize.width + 1); + } + + int bottom = std::max(roi_front.y + roi_front.height, roi_back.y + roi_back.height); + if (bottom >= srcSize.height) + { + roi_front.height -= (bottom - srcSize.height + 1); + roi_back.height -= (bottom - srcSize.height + 1); + } + + mask_rotatedRect.center = cv::Point((roi_front.width + offset.x) / 2, (roi_front.height + offset.y) / 2); + mask_rotatedRect.size = size; + mask_rotatedRect.angle = angle; +} + +std::vector> CImageApplyOutHole::filterPoly(std::vector>& contours, const std::vector& m, + cv::RotatedRect roi, float edgeScale, float areaThreshold) +{ + edgeScale = std::min(0.49f, std::max(edgeScale, 0.0f)); + cv::RotatedRect roi2(roi.center, cv::Size(static_cast(roi.size.width * (1 - edgeScale * 2)), + static_cast(roi.size.height * (1 - edgeScale * 2))), roi.angle); + + std::vector vertices_roi1 = hg::getVertices(roi); + std::vector vertices_roi2 = hg::getVertices(roi2); + + std::vector> hole_contours; + for (size_t i = 0, length = contours.size(); i < length; i++) + { + if (m[i][2] != -1) continue; + + cv::RotatedRect rrect = hg::getBoundingRect(contours[i]); + if (rrect.size.area() < areaThreshold) continue; + + bool enabled = true; + for (size_t j = 0, count = contours[i].size(); j < count; j++) + { + cv::Point p(contours[i][j]); + double temp1 = pointPolygonTest(vertices_roi1, p, false); //鍒ゆ柇鏄惁鍦ㄧ焊寮犲唴 1锛氬唴锛0锛氫笂锛-1锛氬 + double temp2 = pointPolygonTest(vertices_roi2, p, false); //鍒ゆ柇鏄惁鍦ㄨ竟缂樺尯鍩熷唴 1锛氬唴锛0锛氫笂锛-1锛氬 + //濡傛灉鍦ㄧ焊寮犲锛屾垨鑰呰竟缂樺唴锛岃涓洪潪瀛旀礊 + if (temp1 < 0 || temp2 > 0) + { + enabled = false; + break; + } + } + + if (enabled) + hole_contours.push_back(contours[i]); + } + return hole_contours; +} + +cv::Scalar CImageApplyOutHole::getBackGroudColor(const cv::Mat& image, const std::vector pixelPoints) +{ + if (pixelPoints.empty()) return cv::Scalar(255, 255, 255); + + int channels = image.channels(); + + int temp[3] = { 0 }; + for (size_t i = 0, length = pixelPoints.size(); i < length; ++i) + { + int x = cv::min(cv::max(0, pixelPoints[i].x), image.cols - 1); + int y = cv::min(cv::max(0, pixelPoints[i].y), image.rows - 1); + + const unsigned char* ptr = image.ptr(y, x); + for (int j = 0; j < channels; ++j) + temp[j] += ptr[j]; + } + + return cv::Scalar(temp[0] / static_cast(pixelPoints.size()), + temp[1] / static_cast(pixelPoints.size()), + temp[2] / static_cast(pixelPoints.size())); +} + +cv::Scalar CImageApplyOutHole::getBackGroudColor(const cv::Mat& image, int total) +{ + if (image.channels() == 3) + { + cv::Mat image_bgr[3]; + cv::split(image, image_bgr); + + uchar bgr[3]; + for (size_t i = 0; i < 3; i++) + bgr[i] = getBackGroudChannelMean(image_bgr[i], total); + return cv::Scalar(bgr[0], bgr[1], bgr[2]); + } + else + return cv::Scalar::all(getBackGroudChannelMean(image, total)); +} + +uchar CImageApplyOutHole::getBackGroudChannelMean(const cv::Mat& gray, int total) +{ + cv::Mat image_clone; + cv::resize(gray, image_clone, cv::Size(), 0.25, 0.25); + + int threnshold = total / 32; + int channels[] = { 0 }; + int nHistSize[] = { 256 }; + float range[] = { 0, 256 }; + const float* fHistRanges[] = { range }; + cv::Mat hist; + cv::calcHist(&image_clone, 1, channels, cv::Mat(), hist, 1, nHistSize, fHistRanges, true, false); + + int hist_array[256]; + for (int i = 0; i < 256; i++) + hist_array[i] = hist.at(i, 0); + + int length = 1; + const int length_max = 255 - m_threshold; + while (length < length_max) + { + for (size_t i = m_threshold + 1; i < 256 - length; i++) + { + int count = 0; + uint pixSum = 0; + for (size_t j = 0; j < length; j++) + { + count += hist_array[j + i]; + pixSum += hist_array[j + i] * (i + j); + } + + if (count >= threnshold) + return pixSum / count; + } + length++; + } + return 255; +} \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageApplyOutHole.h b/hgdriver/ImageProcess/ImageApplyOutHole.h new file mode 100644 index 0000000..4d0feaf --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyOutHole.h @@ -0,0 +1,74 @@ +锘/* + * ==================================================== + + * 鍔熻兘锛氳璁㈠瓟濉厖 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/11/21 + * 鏈杩戜慨鏀规椂闂达細2020/05/12 v1.0 + * 2020/11/17 v1.1 + * 2021/09/06 v1.2 璋冩暣榛樿浜屽煎寲闃堝硷紝浠庡師鏉ョ殑50璋冩暣涓100銆傚皢濉厖棰滆壊浠庡眬閮ㄩ鑹叉彁鍙栨敼涓哄叏灞棰滆壊鎻愬彇銆 + * 2021/11/03 v1.3 澧炲姞閫昏緫锛屽鏋滄鍙嶉潰鍥惧儚灏哄宸紓瓒呰繃10涓儚绱狅紝鐩存帴杩斿洖锛屼笉鍐嶈繘琛岄櫎绌垮瓟澶勭悊銆 + * 2021/11/04 v1.4 澧炲姞鑳屾櫙鎶楀櫔鏈哄埗锛岃兘澶熸姉5鍍忕礌鐨勮儗鏅櫔澹般 + * 2021/11/17 v1.5 璋冩暣浠g爜鏍煎紡锛岄伩鍏嶄竴浜涚敱浜巓pencv鐗堟湰瀵艰嚧鐨凚UG銆 + * 鐗堟湰鍙凤細v1.5 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_OUT_HOLE_H +#define IMAGE_APPLY_OUT_HOLE_H + +#include "ImageApply.h" + +class CImageApplyOutHole : public CImageApply +{ +public: + + CImageApplyOutHole(); + + /* + * borderSize [in]:瀛旀礊闈㈢Н闃堝 + * edgeScale [in]:绾稿紶杈圭紭鍖哄煙姣斾緥锛屽彇鍊艰寖鍥(0,0.5),榛樿鍊0.1 + * threshold [in]:浜屽煎寲闃堝 + */ + CImageApplyOutHole(float borderSize, float edgeScale, double threshold); + + ~CImageApplyOutHole(void); + + virtual void apply(std::vector& mats, bool isTwoSide); + + float getBorderSize() { return m_borderSize; } + + float getEdgeScale() { return m_edgeScale; } + + double getThreshold() { return m_threshold; } + + void setBorderSize(float size) { m_borderSize = size; } + + void setEdgeScale(float scale) { m_edgeScale = scale; } + + void setThreshold(double threshold) { m_threshold = (std::min)((std::max)(threshold, 1.0), 254.0); } + +private: + + virtual void apply(cv::Mat& pDib, int side); + + void getRoi(cv::RotatedRect rrect_front, cv::RotatedRect rrect_back, cv::Size srcSize, cv::Rect& roi_front, + cv::Rect& roi_back, cv::RotatedRect& mask_rotatedRect); + + std::vector > filterPoly(std::vector>& contours, const std::vector& m, cv::RotatedRect roi, + float edgeScale, float areaThreshold); + + cv::Scalar getBackGroudColor(const cv::Mat& image, const std::vector pixelPoints); + + cv::Scalar getBackGroudColor(const cv::Mat& image, int total); + + uchar getBackGroudChannelMean(const cv::Mat& gray, int total); + +private: + float m_borderSize; + float m_edgeScale; + double m_threshold; +}; + +#endif // !IMAGE_APPLY_OUT_HOLE_H diff --git a/hgdriver/ImageProcess/ImageApplyRefuseInflow.cpp b/hgdriver/ImageProcess/ImageApplyRefuseInflow.cpp new file mode 100644 index 0000000..d8d23ea --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyRefuseInflow.cpp @@ -0,0 +1,192 @@ +#include "ImageApplyRefuseInflow.h" + +static unsigned char table_very_light[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 }; + +static unsigned char table_light[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 }; + +static unsigned char table_medium[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 }; + +static unsigned char table_strong[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 }; + +static unsigned char table_very_strong[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255 }; + +CImageApplyRefuseInflow::CImageApplyRefuseInflow(int level) +{ + m_level = level; +} + +CImageApplyRefuseInflow::~CImageApplyRefuseInflow() +{ +} + +void CImageApplyRefuseInflow::apply(cv::Mat& pDib, int side) +{ + (void)side; + static cv::Mat mat_table; + switch(m_level) + { + case 1: + mat_table = cv::Mat(1, 256, CV_8UC1, table_very_light); + break; + case 2: + mat_table = cv::Mat(1, 256, CV_8UC1, table_light); + break; + case 3: + mat_table = cv::Mat(1, 256, CV_8UC1, table_medium); + break; + case 4: + mat_table = cv::Mat(1, 256, CV_8UC1, table_strong); + break; + case 5: + mat_table = cv::Mat(1, 256, CV_8UC1, table_very_strong); + break; + default: + mat_table = cv::Mat(1, 256, CV_8UC1, table_medium); + break; + } + + cv::LUT(pDib, mat_table, pDib); +} + +void CImageApplyRefuseInflow::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyRefuseInflow.h b/hgdriver/ImageProcess/ImageApplyRefuseInflow.h new file mode 100644 index 0000000..ae3b116 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyRefuseInflow.h @@ -0,0 +1,32 @@ +/* + * ==================================================== + + * 锟斤拷锟杰o拷锟斤拷锟斤拷透锟斤拷原锟斤拷为锟剿诧拷 + * 锟斤拷锟竭o拷锟斤拷锟斤拷维 + * 锟斤拷锟斤拷时锟戒:2020/4/21 + * 锟斤拷锟斤拷薷锟绞憋拷洌2020/4/21 + * 锟芥本锟脚o拷v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_REFUSE_INFLOW_H +#define IMAGE_APPLY_REFUSE_INFLOW_H + +#include "ImageApply.h" + +class CImageApplyRefuseInflow : public CImageApply +{ +public: + CImageApplyRefuseInflow(int level); + + virtual ~CImageApplyRefuseInflow(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + +private: + int m_level = 3; +}; +#endif // !IMAGE_APPLY_REFUSE_INFLOW_H diff --git a/hgdriver/ImageProcess/ImageApplyResize.cpp b/hgdriver/ImageProcess/ImageApplyResize.cpp new file mode 100644 index 0000000..7411eee --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyResize.cpp @@ -0,0 +1,45 @@ +#include "ImageApplyResize.h" + +CImageApplyResize::CImageApplyResize() + : m_fx(1.0) + , m_fy(1.0) + , m_type(ResizeType::RATIO) +{ +} + +CImageApplyResize::CImageApplyResize(ResizeType type, const cv::Size& size, double fx, double fy) + : m_type(type) + , m_dSize(size) + , m_fx(fx) + , m_fy(fy) +{ +} + + +CImageApplyResize::~CImageApplyResize(void) +{ +} + + +void CImageApplyResize::apply(cv::Mat& pDib,int side) +{ + (void)side; + if (pDib.empty()) return; + if (m_type == ResizeType::RATIO) + cv::resize(pDib, pDib, cv::Size(0, 0), m_fx, m_fy); + else + cv::resize(pDib, pDib, m_dSize); +} + +void CImageApplyResize::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyResize.h b/hgdriver/ImageProcess/ImageApplyResize.h new file mode 100644 index 0000000..43f752c --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyResize.h @@ -0,0 +1,69 @@ +/* + * ==================================================== + + * 鍔熻兘锛氳嚜瀹氫箟浼介┈鏍℃銆傚師鐞嗕负鍝堝笇琛ㄦ煡鍊 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細2020/4/21 + * 鐗堟湰鍙凤細v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_RESIZE_H +#define IMAGE_APPLY_RESIZE_H + +#include "ImageApply.h" + +class CImageApplyResize : public CImageApply +{ +public: + + enum class ResizeType + { + RATIO, //姣斾緥缂╂斁 + DSIZE //灏哄缂╂斁 + }; + +public: + CImageApplyResize(); + + /* + * type [in]:缂╂斁绫诲瀷 + * size [in]:鐩爣灏哄锛屽綋type涓篋SIZE鏃剁敓鏁 + * fx [in]:妯悜鐩爣姣斾緥锛屽綋type涓篟ATIO鏃剁敓鏁 + * fy [in]:绾靛悜鐩爣姣斾緥锛屽綋type涓篟ATIO鏃剁敓鏁 + */ + CImageApplyResize(ResizeType type, const cv::Size& size, double fx, double fy); + + virtual ~CImageApplyResize(void); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + + double getFX() { return m_fx; } + + double getFY() { return m_fy; } + + cv::Size getDSize() { return m_dSize; } + + ResizeType getType() { return m_type; } + + void setFX(double value) { m_fx = value; } + + void setFY(double value) { m_fy = value; } + + void setDSize(const cv::Size& size) { m_dSize = size; } + + void setType(ResizeType type) { m_type = type; } + +private: + double m_fx; + double m_fy; + cv::Size m_dSize; + ResizeType m_type; +}; + +#endif // !IMAGE_APPLY_RESIZE_H diff --git a/hgdriver/ImageProcess/ImageApplyRotation.cpp b/hgdriver/ImageProcess/ImageApplyRotation.cpp new file mode 100644 index 0000000..782d6c0 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyRotation.cpp @@ -0,0 +1,172 @@ +#include "ImageApplyRotation.h" +//#define USE_TESSERCAT + +//#define USE_HANWANG + +//#define HG_GPDF_API_BUILD +#include "hg_ocr.h" + +CImageApplyRotation::CImageApplyRotation(RotationType rotation, bool isBackTransposed, int dpi, const char* tessdataPath) + : m_rotation(rotation) + , m_backTranspose(isBackTransposed) + , m_dpi(dpi) + , osd(nullptr) +{ + if (rotation == RotationType::AutoTextOrientation) + { +#ifdef USE_TESSERCAT + osd = new HG_OCR(); + std::string strpath(tessdataPath); + reinterpret_cast(osd)->init(strpath.c_str(), HG_OCR::PSM_TYPE::Orientation); +#endif + } +} + +CImageApplyRotation::~CImageApplyRotation() +{ +#ifdef USE_TESSERCAT + if (osd) delete reinterpret_cast(osd); +#endif +} + +void CImageApplyRotation::apply(cv::Mat& pDib, int side) +{ + m_angleResult = 0; + if (pDib.empty()) + { + return; + } + + if (m_rotation == RotationType::AutoTextOrientation) //锟皆讹拷锟侥憋拷锟斤拷锟斤拷识锟斤拷 + { +#ifdef USE_HANWANG + cv::Mat temp; + if (m_dpi != 200) + { + double scale = 200 / static_cast(m_dpi); + int new_w = static_cast(pDib.cols * scale) / 4 * 4; + int new_h = pDib.rows * scale; + cv::resize(pDib, temp, cv::Size(new_w, new_h)); + } + else + temp = pDib(cv::Rect(0, 0, pDib.cols / 4 * 4, pDib.rows)).clone(); + + if (temp.channels() == 3) + cv::cvtColor(temp, temp, cv::COLOR_BGR2GRAY); + cv::threshold(temp, temp, 180, 255, cv::THRESH_OTSU); + + int orientation = HG_OCR::orientation(temp.data, temp.cols, temp.rows, temp.channels()); + + switch (orientation) + { + case 90: + cv::transpose(pDib, pDib); + cv::flip(pDib, pDib, 0); + break; + case 180: + cv::flip(pDib, pDib, 0); + cv::flip(pDib, pDib, 1); + break; + case 270: + cv::transpose(pDib, pDib); + cv::flip(pDib, pDib, 1); + break; + default: + break; + } +#endif +#ifdef USE_TESSERCAT + if (osd) + { + cv::Mat temp; + if (m_dpi != 200) + { + double scale = 200 / static_cast(m_dpi); + int new_w = (static_cast(pDib.cols * scale) + 3) / 4 * 4; + int new_h = pDib.rows * scale; + cv::resize(pDib, temp, cv::Size(new_w, new_h)); + } + else + temp = pDib(cv::Rect(0, 0, pDib.cols / 4 * 4, pDib.rows)).clone(); + + + HG_OCR* ptr_osd = reinterpret_cast(osd); + int ori = -1; + int direction = -1; + int order = -1; + float angle = -1; + ptr_osd->getOrientation(temp.data, temp.cols, temp.rows, temp.channels(), temp.step1(), + ori, direction, order, angle); + + switch (ori) + { + case 1: + cv::transpose(pDib, pDib); + cv::flip(pDib, pDib, 0); + m_angleResult = 90; + break; + case 2: + cv::flip(pDib, pDib, 0); + cv::flip(pDib, pDib, 1); + m_angleResult = 180; + break; + case 3: + cv::transpose(pDib, pDib); + cv::flip(pDib, pDib, 1); + m_angleResult = 270; + break; + default: + m_angleResult = 0; + break; + } + } +#endif + } + else if (m_backTranspose && side == 1) //锟斤拷锟斤拷锟斤拷转180 + { + if (m_rotation != RotationType::Rotate_180) //锟斤拷转180锟斤拷 + { + if (m_rotation == RotationType::Rotate_90_clockwise || m_rotation == RotationType::Rotate_90_anti_clockwise) //90锟斤拷 -90锟斤拷 + { + transpose(pDib, pDib); + flip(pDib, pDib, m_rotation == RotationType::Rotate_90_clockwise ? 0 : 1); + m_angleResult = m_rotation == RotationType::Rotate_90_clockwise ? 270 : 90; + } + else + { + flip(pDib, pDib, 0); + flip(pDib, pDib, 1); + m_angleResult = 180; + } + } + } + else //zh + { + if (m_rotation == RotationType::Rotate_90_clockwise || m_rotation == RotationType::Rotate_90_anti_clockwise) //90锟斤拷 -90锟斤拷 + { + transpose(pDib, pDib); + flip(pDib, pDib, m_rotation == RotationType::Rotate_90_clockwise ? 1 : 0); + m_angleResult = m_rotation == RotationType::Rotate_90_clockwise ? 90 : 270; + } + else if (m_rotation == RotationType::Rotate_180) + { + flip(pDib, pDib, 0); + flip(pDib, pDib, 1); + m_angleResult = 180; + } + } +} + +void CImageApplyRotation::apply(std::vector& mats, bool isTwoSide) +{ + (void)isTwoSide; + m_angleResults.clear(); + int i = 0; + for (cv::Mat& var : mats) { + if (!var.empty()) { + apply(var, i); + m_angleResults.push_back(m_angleResult); + i++; + } + } +} diff --git a/hgdriver/ImageProcess/ImageApplyRotation.h b/hgdriver/ImageProcess/ImageApplyRotation.h new file mode 100644 index 0000000..5412dd9 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyRotation.h @@ -0,0 +1,56 @@ +#ifndef IMAGE_APPLY_ROTATION_H +#define IMAGE_APPLY_ROTATION_H + +#include "ImageApply.h" + +class CImageApplyRotation : public CImageApply +{ +public: + enum class RotationType + { + Invalid, + Rotate_90_clockwise, + Rotate_180, + Rotate_90_anti_clockwise, + + AutoTextOrientation + }; + +public: + + CImageApplyRotation(RotationType rotation, bool isBackTransposed = false, int dpi = 200, const char* tessdataPath = nullptr); + + virtual ~CImageApplyRotation(); + + virtual void apply(cv::Mat& pDib, int side) override; + + virtual void apply(std::vector& mats, bool isTwoSide); + + bool isBackTransposed() { return m_backTranspose; } + + int getDPI() { return m_dpi; } + + int angleResult() { return m_angleResult; } + + const std::vector& angleResults() { return m_angleResults; } + + RotationType getRotationType() { return m_rotation; } + + void setBackTransposed(bool enabled) { m_backTranspose = enabled; } + + void setDPI(int dpi) { m_dpi = dpi; } + + void setRotationType(RotationType type) { m_rotation = type; } + +private: + RotationType m_rotation; + bool m_backTranspose; + int m_dpi; + + void* osd; + + int m_angleResult; + std::vector m_angleResults; +}; + +#endif // !IMAGE_APPLY_ROTATION_H diff --git a/hgdriver/ImageProcess/ImageApplySharpen.cpp b/hgdriver/ImageProcess/ImageApplySharpen.cpp new file mode 100644 index 0000000..34eeef9 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplySharpen.cpp @@ -0,0 +1,53 @@ +#include "ImageApplySharpen.h" +using namespace cv; +CImageApplySharpen::CImageApplySharpen(int sharpentype) +{ + sharpenType = sharpentype; + kSize = (sharpentype == FilterMethod::Sharpen || sharpentype == FilterMethod::AverBlur) ? 5 : 9; +} + +CImageApplySharpen::~CImageApplySharpen() +{ +} + +void CImageApplySharpen::apply(cv::Mat & pDib, int side) +{ + switch (sharpenType) + { + case CImageApplySharpen::Sharpen: + case CImageApplySharpen::Sharpen_More: + sharpen(pDib, kSize); + break; + case CImageApplySharpen::AverBlur: + case CImageApplySharpen::AverBlur_More: + averblur(pDib, static_cast(kSize)); + break; + } +} + +void CImageApplySharpen::apply(std::vector& mats, bool isTwoSide) +{ + if (mats.empty()) return; + + if (!mats[0].empty()) + apply(mats[0], 0); + + if (isTwoSide && mats.size() > 1) { + if (!mats[1].empty()) + apply(mats[1], 1); + } +} + +void CImageApplySharpen::averblur(Mat& src, int kSize) +{ + blur(src, src, Size(kSize, kSize)); +} + +void CImageApplySharpen::sharpen(Mat& src, float kSize) +{ + float other = (1.0f - kSize) / 4; + float kernel_data[] = { 0, other, 0, other, kSize, other, 0, other, 0 }; + Mat kernel(3, 3, CV_32FC1, kernel_data); + + filter2D(src, src, src.depth(), kernel); +} diff --git a/hgdriver/ImageProcess/ImageApplySharpen.h b/hgdriver/ImageProcess/ImageApplySharpen.h new file mode 100644 index 0000000..9401b9e --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplySharpen.h @@ -0,0 +1,32 @@ +#ifndef IMAGE_APPLY_SHARPEN_H +#define IMAGE_APPLY_SHARPEN_H + +#include "ImageApply.h" + +class CImageApplySharpen : + public CImageApply +{ +public: + enum FilterMethod + { + None, + Sharpen, + Sharpen_More, + AverBlur, + AverBlur_More + }; + CImageApplySharpen(int sharpentype); + virtual ~CImageApplySharpen(); + + virtual void apply(cv::Mat& pDib, int side); + + virtual void apply(std::vector& mats, bool isTwoSide); +private: + void averblur(cv::Mat& src, int kSize); + void sharpen(cv::Mat& src, float kSize); +private: + int sharpenType; + float kSize; +}; + +#endif // !IMAGE_APPLY_SHARPEN_H diff --git a/hgdriver/ImageProcess/ImageApplySplit.cpp b/hgdriver/ImageProcess/ImageApplySplit.cpp new file mode 100644 index 0000000..8d0cc5f --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplySplit.cpp @@ -0,0 +1,115 @@ +#include "ImageApplySplit.h" +#include + +#define BPP(type,index) +CImageApplySplit::CImageApplySplit(int multitype,bool split, bool ismulti_filter_red,int colormode) : + m_bmulti_filter_red(ismulti_filter_red) + , m_split(split) + , m_multitype(multitype) + , m_colormode(colormode) +{ +} + +CImageApplySplit::~CImageApplySplit(void) +{ +} + +std::vector CImageApplySplit::SplitMats(std::vector& mats, bool isTwoSide) +{ + std::vector rets; + for (size_t i = 0; i < mats.size(); i++) + { + if (mats[i].empty()) + continue; +// if (i != 0 && isTwoSide == false) +// break; + + int bpp = getBpp(i); + if (m_split)//拆分 + { + std::vector retmats = apply(mats[i]); + if (bpp != -1) { + } + else {//仅拆分 + if (m_colormode == 0) bpp = 1;//bw + else if (m_colormode == 1) bpp = 8; + else bpp = 24; + } + + for (size_t j = 0; j < retmats.size(); j++) + { + if (!retmats[j].empty()) { + cv::transpose(retmats[j],retmats[j]); + cv::flip(retmats[j],retmats[j],i==0?1:0); + MatEx matex(retmats[j], bpp); + rets.push_back(matex); + } + } + } + else { + MatEx matex(mats[i], bpp); + rets.push_back(matex); + } + } + return rets; +} + +std::vector CImageApplySplit::apply(cv::Mat& pDib) +{ + if (pDib.empty()) + return std::vector(); + std::vector retMats; + int heigth = pDib.rows; + int width = pDib.cols; + if (heigth > width) + { + cv::Mat matF = pDib(cv::Rect(0, 0, width, (int)(0.5 * heigth))); + cv::Mat matB = pDib(cv::Rect(0, (int)(0.5 * heigth), width, (int)(0.5 * heigth))); + retMats.push_back(matF); + retMats.push_back(matB); + } + else + { + cv::Mat matF = pDib(cv::Rect(0, 0, (int)(width*0.5), heigth)); + cv::Mat matB = pDib(cv::Rect((int)(width*0.5), 0, (int)(width * 0.5), heigth)); + retMats.push_back(matF); + retMats.push_back(matB); + } + return retMats; +} + +int CImageApplySplit::getBpp(int matIndex) +{ + int ret = -1; + if (m_bmulti_filter_red) { + ret = matIndex == 0 ? 24 : 8; + } + else + { + if (m_multitype == -1) + return ret; + switch (m_multitype) + { + case 0://all + if (matIndex == 0) ret = 24; + else if (matIndex == 1) ret = 8; + else ret = 1; + break; + case 1://clolr +gray + if (matIndex == 0) ret = 24; + else ret = 8; + break; + case 2://color+bw + if (matIndex == 0) ret = 24; + else ret = 1; + break; + case 3://gray+bw + if (matIndex == 0) ret = 8; + else ret = 1; + break; + default: + break; + } + } + return ret; +} diff --git a/hgdriver/ImageProcess/ImageApplySplit.h b/hgdriver/ImageProcess/ImageApplySplit.h new file mode 100644 index 0000000..35103a8 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplySplit.h @@ -0,0 +1,38 @@ +/* + * ==================================================== + + * 功能:图像拆分 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 + * 版本号:v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_SPLIT_H +#define IMAGE_APPLY_SPLIT_H +#include "MatEx.h" +#include +#include "imgprocdefs.h" + +class CImageApplySplit +{ +public: + CImageApplySplit(int multitype=-1,bool split=false,bool ismulti_filter_red=false,int colormode=1);//默认不多流输出 不多流除红 灰度 + + ~CImageApplySplit(void); + std::vector SplitMats(std::vector& mats, bool isTwoSide); + +//private: + std::vector apply(cv::Mat& pDib); + int getBpp(int matIndex); + +private://field + bool m_bmulti_filter_red; + int m_multitype; + int m_colormode; + bool m_split; +}; + +#endif // !IMAGE_APPLY_SPLIT_H diff --git a/hgdriver/ImageProcess/ImageApplyTextureRemoval.cpp b/hgdriver/ImageProcess/ImageApplyTextureRemoval.cpp new file mode 100644 index 0000000..5c42622 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyTextureRemoval.cpp @@ -0,0 +1,172 @@ +#include "ImageApplyTextureRemoval.h" + +//交换对角线 +void zero_to_center(cv::Mat& image, int colToCut, int rowToCut) +{ + cv::Mat q1(image, cv::Rect(0, 0, colToCut, rowToCut)); + cv::Mat q2(image, cv::Rect(colToCut, 0, colToCut, rowToCut)); + cv::Mat q3(image, cv::Rect(0, rowToCut, colToCut, rowToCut)); + cv::Mat q4(image, cv::Rect(colToCut, rowToCut, colToCut, rowToCut)); + + //第二象限和第四象限进行交换 + cv::Mat tmpImg; + q1.copyTo(tmpImg); + q4.copyTo(q1); + tmpImg.copyTo(q4); + + //第一象限和第三象限进行交换 + q2.copyTo(tmpImg); + q3.copyTo(q2); + tmpImg.copyTo(q3); +} + +//创建光谱 +cv::Mat create_spectrum(cv::Mat* matArray, double scale = 1.5) +{ + cv::Mat dst; + cv::magnitude(matArray[0], matArray[1], dst); +#if 1 + cv::divide(dst, dst.cols * dst.rows, dst, scale); + //imshow("频谱", dst); +#else + dst += Scalar::all(1); + log(dst, dst); + normalize(dst, dst, 1, 0, CV_MINMAX); +#endif + +#if 0 + imshow("频谱", dst); +#endif + return dst; +} + +//反傅里叶变换 +void inverseFourierTransform(const cv::Mat& src, cv::Mat& dst) +{ + cv::Mat complexIDFT; + cv::Mat matArray[2]; + cv::idft(src, complexIDFT); + cv::split(complexIDFT, matArray); + cv::magnitude(matArray[0], matArray[1], dst); + cv::normalize(dst, dst, 0, 1, CV_MINMAX); +} + +//制作陷波滤波器 +cv::Mat createFilter(const cv::Mat& spectrum, int dilateSize, int erodeSize) +{ + cv::Mat temp; + spectrum.convertTo(temp, CV_8UC1, 255); + cv::threshold(temp, temp, 0, 255, CV_THRESH_OTSU); + //imshow("二值化", temp); + + cv::Mat element1 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(dilateSize, dilateSize)); + cv::Mat element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(erodeSize, erodeSize)); + cv::dilate(temp, temp, element1); + cv::erode(temp, temp, element2); + cv::floodFill(temp, cv::Point(temp.cols / 2, temp.rows / 2), cv::Scalar(0)); //漫水填充中心区域 + cv::medianBlur(~temp, temp, 3); //中值滤波 + //temp = ~temp; + //cv::imshow("二值化", temp); + + //陷波滤波器复制 + cv::Mat filter; + temp.convertTo(filter, CV_32FC1); + cv::normalize(filter, filter, 1, 0.01, CV_MINMAX); + std::vector mv; + mv.push_back(filter); + mv.push_back(filter); + cv::merge(mv, filter); + + return filter; +} + +void CImageApplyTextureRemoval::textureRemovalGray(cv::Mat& img) +{ + //得到DFT的最佳尺寸(2的指数),以加速计算 + cv::Mat paddedImg; + int m = cv::getOptimalDFTSize(img.rows); + int n = cv::getOptimalDFTSize(img.cols); + + //填充图像的下端和右端 + cv::copyMakeBorder(img, paddedImg, 0, m - img.rows, 0, n - img.cols, + cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + //将填充的图像组成一个复数的二维数组(两个通道的Mat),用于DFT + cv::Mat matArray[] = { cv::Mat_(paddedImg), cv::Mat::zeros(paddedImg.size(), CV_32F) }; + cv::Mat complexInput, complexOutput; + cv::merge(matArray, 2, complexInput); + cv::dft(complexInput, complexOutput); + cv::split(complexOutput, matArray); //计算幅度谱(傅里叶谱) + + //滤波 + //将实部和虚部按照频谱图的方式换位 + //低频在图像中心,用于滤波 + zero_to_center(matArray[0], complexOutput.cols / 2, complexOutput.rows / 2); + zero_to_center(matArray[1], complexOutput.cols / 2, complexOutput.rows / 2); + cv::Mat spectrum = create_spectrum(matArray); + + //创建滤波器 + cv::Mat filter = createFilter(spectrum, m_dilateSize, m_erodeSize); + cv::merge(matArray, 2, complexOutput); + cv::multiply(complexOutput, filter, filter); + + //IDFT得到滤波结果 + cv::Size imgSize = img.size(); + inverseFourierTransform(filter, img); + img = img(cv::Rect(cv::Point(0, 0), imgSize)); + img *= 255; + img.convertTo(img, CV_8UC1); +} + +CImageApplyTextureRemoval::CImageApplyTextureRemoval() + : CImageApply() + , m_dilateSize(5) + , m_erodeSize(3) +{ + +} + +CImageApplyTextureRemoval::CImageApplyTextureRemoval(int dilateSize, int erodeSize) + : CImageApply() + , m_dilateSize(dilateSize) + , m_erodeSize(erodeSize) +{ + +} + +CImageApplyTextureRemoval::~CImageApplyTextureRemoval() +{ + +} + +void CImageApplyTextureRemoval::apply(cv::Mat &pDib, int side) +{ + (void)side; + + if (pDib.channels() == 1) + textureRemovalGray(pDib); + else + { + std::vector rgb(3); + cv::split(pDib, rgb); + for (cv::Mat& var : rgb) + textureRemovalGray(var); + cv::merge(rgb, pDib); + } + + pDib *= 1.15; +} + +void CImageApplyTextureRemoval::apply(std::vector &mats, bool isTwoSide) +{ + (void)isTwoSide; + + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyTextureRemoval.h b/hgdriver/ImageProcess/ImageApplyTextureRemoval.h new file mode 100644 index 0000000..f97080d --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyTextureRemoval.h @@ -0,0 +1,50 @@ +锘/* + * ==================================================== + + * 鍔熻兘锛氬幓闄ょ綉绾 + * 浣滆咃細鍒樹竵缁 + * 鐢熸垚鏃堕棿锛2020/4/21 + * 鏈杩戜慨鏀规椂闂达細2020/4/21 + * 鐗堟湰鍙凤細v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_APPLY_TEXTURE_REMOVAL_H +#define IMAGE_APPLY_TEXTURE_REMOVAL_H + +#include "ImageApply.h" + +class CImageApplyTextureRemoval : public CImageApply +{ +public: + CImageApplyTextureRemoval(void); + + /* + * dilateSize [in]:鑶ㄨ儉鍍忕礌 + * erodeSize [in]:鑵愯殌鍍忕礌 + */ + CImageApplyTextureRemoval(int dilateSize, int erodeSize); + + virtual ~CImageApplyTextureRemoval(void); + + virtual void apply(cv::Mat& pDib,int side); + + virtual void apply(std::vector& mats, bool isTwoSide); + + int getDilateSize() {return m_dilateSize;} + + int getErodeSize() {return m_erodeSize;} + + void setDilateSize(int size) {m_dilateSize = size;} + + void setErodeSize(int size) {m_erodeSize = size;} +private: + void textureRemovalGray(cv::Mat& img); + +private: + int m_dilateSize; + int m_erodeSize; +}; + +#endif diff --git a/hgdriver/ImageProcess/ImageApplyUV.cpp b/hgdriver/ImageProcess/ImageApplyUV.cpp new file mode 100644 index 0000000..56808fe --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyUV.cpp @@ -0,0 +1,315 @@ +#include "ImageApplyUV.h" +using namespace cv; +#define SCA 50 + +ImageApplyUV::ImageApplyUV() : lut(1, 256, CV_8UC1) +{ +} + +ImageApplyUV::~ImageApplyUV() +{ +} + +void ImageApplyUV::update_lutData(int contrast) +{ + unsigned char* ptr = lut.data; + int m_contrast = cv::max(-127, cv::min(contrast, 127)); + for (int i = 0; i < 256; i++) + { + //update contrast + if (i < 128) + ptr[i] = static_cast(cv::max(0, cv::min(i - m_contrast, 127))); + else + ptr[i] = static_cast(cv::max(127, cv::min(i + m_contrast, 255))); + } +} + +void ImageApplyUV::Apply(cv::Mat& image, const cv::Mat& uv, int dpi, int thresh) +{ + update_lutData(12); + cv::LUT(uv, lut, uv); + Mat uv_resize; + cv::resize(uv, uv_resize, cv::Size(uv.cols * SCA / dpi, uv.rows * SCA / dpi)); + if (uv_resize.channels() == 3) + cv::cvtColor(uv_resize, uv_resize, cv::COLOR_BGR2GRAY); + cv::threshold(uv_resize, uv_resize, 150, 255, THRESH_BINARY); + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1500 / dpi, 1500 / dpi)); + cv::dilate(uv_resize, uv_resize, element); + std::vector> contours; + std::vector hierarchy; + findContours1(uv_resize, contours, hierarchy, cv::RETR_EXTERNAL); + + std::map map_color; + for (int i = 0; i < contours.size(); i++) + { + cv::Rect roi = cv::boundingRect(contours[i]); + roi.x *= dpi / SCA; + roi.y *= dpi / SCA; + roi.width *= dpi / SCA; + roi.height *= dpi / SCA; + + purgeQR_kernal(image, roi, map_color, dpi, thresh); + } +} + +cv::Mat ImageApplyUV::Apply(const cv::Mat& image, const cv::Mat& uv, const cv::RotatedRect& uvRoi, bool isDesaskew, int angle, int pixtype) +{ + static int svindex = 0; + if (uvRoi.size.width == 0) + return cv::Mat(); + cv::RotatedRect uvRoi_clone = uvRoi; + cv::Mat dst = cv::Mat::zeros(image.rows > image.cols ? image.rows : (image.rows * 2), image.cols > image.rows ? image.cols : (image.cols * 2), image.type()); + image.copyTo(dst(cv::Rect(0, 0, image.cols, image.rows))); + + cv::Mat dst_uv = dst(cv::Rect(image.rows > image.cols ? image.cols : 0, image.rows > image.cols ? 0 : image.rows, image.cols, image.rows)); + if (isDesaskew) + { + cv::Point2f srcTri[4]; + cv::Point2f dstTri[3]; + uvRoi_clone.points(srcTri); + + if (angle == 90) + { + dstTri[0] = cv::Point2f(0, 0); + dstTri[1] = cv::Point2f(uvRoi_clone.size.width - 1, 0); + dstTri[2] = cv::Point2f(uvRoi_clone.size.width - 1, uvRoi_clone.size.height - 1); + } + else if (angle == 180) + { + dstTri[0] = cv::Point2f(uvRoi_clone.size.width - 1, 0); + dstTri[1] = cv::Point2f(uvRoi_clone.size.width - 1, uvRoi_clone.size.height - 1); + dstTri[2] = cv::Point2f(0, uvRoi_clone.size.height - 1); + } + else if (angle == 270) + { + dstTri[0] = cv::Point2f(uvRoi_clone.size.width - 1, uvRoi_clone.size.height - 1); + dstTri[1] = cv::Point2f(0, uvRoi_clone.size.height - 1); + dstTri[2] = cv::Point2f(0, 0); + } + else + { + dstTri[0] = cv::Point2f(0, uvRoi_clone.size.height - 1); + dstTri[1] = cv::Point2f(0, 0); + dstTri[2] = cv::Point2f(uvRoi_clone.size.width - 1, 0); + } + + cv::Mat warp_mat = cv::getAffineTransform(srcTri, dstTri); + cv::Mat uv_temp; + cv::warpAffine(uv, uv_temp, warp_mat, cv::Size(uvRoi_clone.size.width, uvRoi_clone.size.height)); + + //cv::imwrite("uv_temp.jpg", uv_temp); + if (pixtype == 0)//二值图 + { + cvtColor(uv_temp, uv_temp, cv::COLOR_BGR2GRAY); + cv::threshold(uv_temp, uv_temp, 150, 255, THRESH_OTSU); + } + + if (uv_temp.channels() == 1 && dst_uv.channels() == 3) + { + cv::cvtColor(uv_temp(cv::Rect( + uv_temp.cols > dst_uv.cols ? (uv_temp.cols - dst_uv.cols) / 2 : 0, + uv_temp.rows > dst_uv.rows ? (uv_temp.rows - dst_uv.rows) / 2 : 0, + uv_temp.cols > dst_uv.cols ? dst_uv.cols : uv_temp.cols, + uv_temp.rows > dst_uv.rows ? dst_uv.rows : uv_temp.rows)), + dst_uv(cv::Rect( + dst_uv.cols > uv_temp.cols ? (dst_uv.cols - uv_temp.cols) / 2 : 0, + dst_uv.rows > uv_temp.rows ? (dst_uv.rows - uv_temp.rows) / 2 : 0, + dst_uv.cols > uv_temp.cols ? uv_temp.cols : dst_uv.cols, + dst_uv.rows > uv_temp.rows ? uv_temp.rows : dst_uv.rows)), cv::COLOR_GRAY2BGR); + } + else if (uv_temp.channels() == 3 && dst_uv.channels() == 1) + { + cv::cvtColor(uv_temp(cv::Rect( + uv_temp.cols > dst_uv.cols ? (uv_temp.cols - dst_uv.cols) / 2 : 0, + uv_temp.rows > dst_uv.rows ? (uv_temp.rows - dst_uv.rows) / 2 : 0, + uv_temp.cols > dst_uv.cols ? dst_uv.cols : uv_temp.cols, + uv_temp.rows > dst_uv.rows ? dst_uv.rows : uv_temp.rows)), + dst_uv(cv::Rect( + dst_uv.cols > uv_temp.cols ? (dst_uv.cols - uv_temp.cols) / 2 : 0, + dst_uv.rows > uv_temp.rows ? (dst_uv.rows - uv_temp.rows) / 2 : 0, + dst_uv.cols > uv_temp.cols ? uv_temp.cols : dst_uv.cols, + dst_uv.rows > uv_temp.rows ? uv_temp.rows : dst_uv.rows)), cv::COLOR_BGR2GRAY); + } + else + { + uv_temp(cv::Rect( + uv_temp.cols > dst_uv.cols ? (uv_temp.cols - dst_uv.cols) / 2 : 0, + uv_temp.rows > dst_uv.rows ? (uv_temp.rows - dst_uv.rows) / 2 : 0, + uv_temp.cols > dst_uv.cols ? dst_uv.cols : uv_temp.cols, + uv_temp.rows > dst_uv.rows ? dst_uv.rows : uv_temp.rows)).copyTo(dst_uv(cv::Rect( + dst_uv.cols > uv_temp.cols ? (dst_uv.cols - uv_temp.cols) / 2 : 0, + dst_uv.rows > uv_temp.rows ? (dst_uv.rows - uv_temp.rows) / 2 : 0, + dst_uv.cols > uv_temp.cols ? uv_temp.cols : dst_uv.cols, + dst_uv.rows > uv_temp.rows ? uv_temp.rows : dst_uv.rows))); + } + + } + else + { + cv::Rect uvBoundingRect = uvRoi_clone.boundingRect(); + + cv::Rect uvBoundingRect_temp; + if (angle == 90 || angle == 270) + { + uvBoundingRect_temp.x = dst_uv.rows > uvBoundingRect.width ? uvBoundingRect.x : uvBoundingRect.x + (uvBoundingRect.width - dst_uv.rows) / 2; + uvBoundingRect_temp.width = cv::min(dst_uv.rows, uvBoundingRect.width); + uvBoundingRect_temp.y = dst_uv.cols > uvBoundingRect.height ? uvBoundingRect.y : uvBoundingRect.y + (uvBoundingRect.height - dst_uv.cols) / 2; + uvBoundingRect_temp.height = cv::min(dst_uv.cols, uvBoundingRect.height); + } + else + { + uvBoundingRect_temp.x = dst_uv.cols > uvBoundingRect.width ? uvBoundingRect.x : uvBoundingRect.x + (uvBoundingRect.width - dst_uv.cols) / 2; + uvBoundingRect_temp.width = cv::min(dst_uv.cols, uvBoundingRect.width); + uvBoundingRect_temp.y = dst_uv.rows > uvBoundingRect.height ? uvBoundingRect.y : uvBoundingRect.y + (uvBoundingRect.height - dst_uv.rows) / 2; + uvBoundingRect_temp.height = cv::min(dst_uv.rows, uvBoundingRect.height); + } + + int offset_left = 0, offset_right = 0, offset_top = 0, offset_bottom = 0; + if (uvBoundingRect_temp.x < 0) + { + offset_left = -uvBoundingRect_temp.x; + uvBoundingRect_temp.x = 0; + uvBoundingRect_temp.width -= offset_left; + } + + if (uvBoundingRect_temp.y < 0) + { + offset_top = -uvBoundingRect_temp.y; + uvBoundingRect_temp.y = 0; + uvBoundingRect_temp.height -= offset_top; + } + + if (uvBoundingRect_temp.br().x >= uv.cols) + { + offset_right = uvBoundingRect_temp.br().x - uv.cols + 1; + uvBoundingRect_temp.width -= offset_right; + } + + if (uvBoundingRect_temp.br().y >= uv.rows) + { + offset_bottom = uvBoundingRect_temp.br().y - uv.rows + 1; + uvBoundingRect_temp.height -= offset_bottom; + } + + cv::Mat uv_roi = uv(uvBoundingRect_temp).clone(); + + if (dst_uv.channels() == 1)//灰度 + { + cv::cvtColor(uv_roi, uv_roi, cv::COLOR_BGR2GRAY); + if (pixtype == 0)//二值图 + cv::threshold(uv_roi, uv_roi, 150, 255, THRESH_OTSU); + } + uv_roi.copyTo(dst_uv(cv::Rect((dst_uv.cols - uv_roi.cols + offset_left) / 2, (dst_uv.rows - uv_roi.rows + offset_top) / 2, uv_roi.cols, uv_roi.rows))); + } + //imwrite("D:\\dst" + std::to_string(svindex) + ".jpg", dst); + return dst; +} + +void ImageApplyUV::purgeQR_kernal(cv::Mat& image, const cv::Rect& roi, std::map map_color, int dpi, int threshold) +{ + cv::Mat image_roi = image(roi); + cv::Mat mask; + cv::cvtColor(image_roi, mask, cv::COLOR_BGR2GRAY); + cv::threshold(mask, mask, 127, 255, cv::THRESH_OTSU); + cv::Mat image_resize; + cv::resize(image, image_resize, cv::Size(image.cols, 800)); + + for (int i = 0, cols = image_roi.cols, rows = image_roi.rows; i < cols; i++) + { + cv::Scalar color_fill; + if (map_color.find(i + roi.x) == map_color.end()) + { + color_fill = getColor(image_resize, roi.x + i, threshold); + map_color[i + roi.x] = color_fill; + } + else + color_fill = map_color[i + roi.x]; + + for (int j = 0; j < rows; j++) + { + if (*mask.ptr(j, i)) + { + uchar* color = image_roi.ptr(j, i); + color[0] = color_fill[0]; + color[1] = color_fill[1]; + color[2] = color_fill[2]; + } + } + } +} + +void ImageApplyUV::findContours1(const cv::Mat& src, std::vector>& contours, std::vector& hierarchy, int retr, int method, cv::Point offset) +{ + CvMat c_image; + CV_DbgAssert(src.dims <= 2); + c_image = cvMat(src.rows, src.dims == 1 ? 1 : src.cols, src.type(), src.data); + c_image.step = (int)src.step[0]; + c_image.type = (c_image.type & ~cv::Mat::CONTINUOUS_FLAG) | (src.flags & cv::Mat::CONTINUOUS_FLAG); + cv::MemStorage storage(cvCreateMemStorage()); + CvSeq* _ccontours = nullptr; + cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint({ offset.x,offset.y })); + + if (!_ccontours) + { + contours.clear(); + return; + } + cv::Seq all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage)); + size_t total = all_contours.size(); + contours.resize(total); + + cv::SeqIterator it = all_contours.begin(); + for (size_t i = 0; i < total; i++, ++it) + { + CvSeq* c = *it; + reinterpret_cast(c)->color = static_cast(i); + int count = c->total; + int* data = new int[static_cast(count * 2)]; + cvCvtSeqToArray(c, data); + for (int j = 0; j < count; j++) + contours[i].push_back(cv::Point(data[j * 2], data[j * 2 + 1])); + + delete[] data; + } + + hierarchy.resize(total); + it = all_contours.begin(); + for (size_t i = 0; i < total; i++, ++it) + { + CvSeq* c = *it; + int h_next = c->h_next ? reinterpret_cast(c->h_next)->color : -1; + int h_prev = c->h_prev ? reinterpret_cast(c->h_prev)->color : -1; + int v_next = c->v_next ? reinterpret_cast(c->v_next)->color : -1; + int v_prev = c->v_prev ? reinterpret_cast(c->v_prev)->color : -1; + hierarchy[i] = cv::Vec4i(h_next, h_prev, v_next, v_prev); + } + + storage.release(); +} + +cv::Scalar ImageApplyUV::getColor(const cv::Mat& image, int col, int threshold) +{ + cv::Scalar color(0, 0, 0); + int num = 0; + + for (int i = 0, length = image.rows; i < length; i++) + { + const uchar* ptr = image.ptr(i, col); + int gray = (ptr[0] * 30 + ptr[1] * 59 + ptr[2] * 11) / 100; + if (gray > threshold) + { + color[0] += ptr[0]; + color[1] += ptr[1]; + color[2] += ptr[2]; + num++; + } + } + + if (num) + color /= num; + else + color[0] = color[1] = color[2] = 255; + + return color; +} + diff --git a/hgdriver/ImageProcess/ImageApplyUV.h b/hgdriver/ImageProcess/ImageApplyUV.h new file mode 100644 index 0000000..14b9e77 --- /dev/null +++ b/hgdriver/ImageProcess/ImageApplyUV.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include "ImageProcess_Public.h" +#include + +class ImageApplyUV +{ +public: + ImageApplyUV(); + ~ImageApplyUV(); + void Apply(cv::Mat& image, const cv::Mat& uv, int dpi = 200, int thresh = 100); + + static cv::Mat Apply(const cv::Mat& image, const cv::Mat& uv, const cv::RotatedRect& uvRoi, bool isDesaskew, int angle, int pixtype); +private: + void purgeQR_kernal(cv::Mat& image, const cv::Rect& roi, std::map map_color, int dpi, int threshold); + void findContours1(const cv::Mat& src, std::vector>& contours, std::vector& hierarchy, + int retr = cv::RETR_LIST, int method = cv::CHAIN_APPROX_SIMPLE, cv::Point offset = cv::Point(0, 0)); + cv::Scalar getColor(const cv::Mat& image, int col, int threshold); + void update_lutData(int contrast); + cv::Mat lut; +}; + + diff --git a/hgdriver/ImageProcess/ImageMulti.cpp b/hgdriver/ImageProcess/ImageMulti.cpp new file mode 100644 index 0000000..422f707 --- /dev/null +++ b/hgdriver/ImageProcess/ImageMulti.cpp @@ -0,0 +1,72 @@ +#include "ImageMulti.h" + +IMageMulti::IMageMulti(int multiType,int thre) +{ + m_multiType = multiType; + m_thre = thre; +} + +IMageMulti::~IMageMulti(void) +{ +} + +std::vector IMageMulti::apply(cv::Mat& pDib) +{ + std::vector retMats; + if (pDib.empty()) + return retMats; + retMats.push_back(pDib); + switch (m_multiType) + { + case ALL: + { + if (pDib.channels() == 3){ + cv::Mat dst; + cv::cvtColor(pDib, dst,cv::COLOR_BGR2GRAY); + retMats.push_back(dst); + } + cv::Mat dstThre; + cv::cvtColor(pDib, dstThre,cv::COLOR_BGR2GRAY); + //cv::threshold(dstThre, dstThre, m_thre, 255, cv::THRESH_BINARY); + cv::adaptiveThreshold(dstThre,dstThre,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY,25,5); + if (!dstThre.empty()) + { + retMats.push_back(dstThre); + } + } + break; + case COLORGRAY: + { + if (pDib.channels() == 3) { + cv::Mat dstGray; + cv::cvtColor(pDib, dstGray, cv::COLOR_BGR2GRAY); + retMats.push_back(dstGray); + } + } + break; + case COLORBW: + { + if (pDib.channels() == 3) { + cv::Mat dstGray; + cv::cvtColor(pDib, dstGray, cv::COLOR_BGR2GRAY); + cv::Mat dstBW; + cv::adaptiveThreshold(dstGray,dstBW,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY,25,5); + retMats.push_back(dstBW); + } + } + break; + case GRAYBW://pDib should be GreyImage(channels() == 1) + { + cv::Mat dstBW; + cv::adaptiveThreshold(pDib,dstBW,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY,25,5); + if (!dstBW.empty()) + { + retMats.push_back(dstBW); + } + } + break; + default: + break; + } + return retMats; +} diff --git a/hgdriver/ImageProcess/ImageMulti.h b/hgdriver/ImageProcess/ImageMulti.h new file mode 100644 index 0000000..0a79195 --- /dev/null +++ b/hgdriver/ImageProcess/ImageMulti.h @@ -0,0 +1,26 @@ +#ifndef IMAGE_MULTI_H +#define IMAGE_MULTI_H +#include "IMulti.h" + +class IMageMulti + :public IMulti +{ +public: + enum MultiOutput + { + NONE=-1, + ALL, + COLORGRAY, + COLORBW, + GRAYBW + }; +public: + IMageMulti(int multiType = 0,int thre = 128); + virtual ~IMageMulti(void); + virtual std::vector apply(cv::Mat& pDib); +private: + int m_multiType; + int m_thre; +}; + +#endif // !IMAGE_MULTI_H \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageMultiOutputRed.cpp b/hgdriver/ImageProcess/ImageMultiOutputRed.cpp new file mode 100644 index 0000000..5cc08d2 --- /dev/null +++ b/hgdriver/ImageProcess/ImageMultiOutputRed.cpp @@ -0,0 +1,63 @@ +#include "ImageMultiOutputRed.h" +#include + +using namespace std; + +ImageMultiOutputRed::ImageMultiOutputRed(short channelIndex) +{ + m_channelIndex = channelIndex; +} + +ImageMultiOutputRed::~ImageMultiOutputRed(void) +{ +} + +std::vector ImageMultiOutputRed::apply(cv::Mat& pDib) +{ + std::vector retMats; + if (pDib.empty()) + return retMats; + retMats.push_back(pDib); + cv::Mat mat = FilterColor(pDib, m_channelIndex); + if (!mat.empty()) + retMats.push_back(mat); + return retMats; +} + +cv::Mat ImageMultiOutputRed::FilterColor(cv::Mat image, short channel) +{ + cv::Mat dstImage(image.rows, image.cols, CV_8UC1); + int channels = image.channels(); + if (channel > channels - 1) + { + return cv::Mat(); + } + if ((channel == 3) && (channels != 4) && (channels != 8)) + { + return cv::Mat(); + } + if (channels <= 4) + { + int srcOffset = image.step - image.cols * channels; + int dstOffset = dstImage.step - dstImage.cols; + unsigned char *src = image.data; + unsigned char *dst = dstImage.data; + src += channel; + + for (int y = 0; y < image.rows; y++) + { + for (int x = 0; x < image.cols; x++, src += channels, dst++) + { + unsigned short pix = *src; + if (pix >= 130) + { + pix = 255; + } + *dst = pix; + } + src += srcOffset; + dst += dstOffset; + } + } + return dstImage; +} diff --git a/hgdriver/ImageProcess/ImageMultiOutputRed.h b/hgdriver/ImageProcess/ImageMultiOutputRed.h new file mode 100644 index 0000000..a7ed28c --- /dev/null +++ b/hgdriver/ImageProcess/ImageMultiOutputRed.h @@ -0,0 +1,17 @@ +#ifndef IMAGE_MULTI_OUTPUT_RED_H +#define IMAGE_MULTI_OUTPUT_RED_H +#include "IMulti.h" + +class ImageMultiOutputRed + :public IMulti +{ +public: + ImageMultiOutputRed(short channelIndex); + virtual ~ImageMultiOutputRed(void); + virtual std::vector apply(cv::Mat& pDib) override; + +private: + short m_channelIndex; + cv::Mat FilterColor(cv::Mat image, short channel); +}; +#endif //!IMAGE_MULTI_OUTPUT_RED_H diff --git a/hgdriver/ImageProcess/ImageProcess_Public.cpp b/hgdriver/ImageProcess/ImageProcess_Public.cpp new file mode 100644 index 0000000..7096570 --- /dev/null +++ b/hgdriver/ImageProcess/ImageProcess_Public.cpp @@ -0,0 +1,336 @@ +#include "ImageProcess_Public.h" + +namespace hg +{ + void convexHull(const std::vector& src, std::vector& dst, bool clockwise) + { + CvMemStorage* storage = cvCreateMemStorage(); // + CvSeq* ptseq = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage); //ptseqstorage + + // + for (const cv::Point& item : src) + { + CvPoint p; + p.x = item.x; + p.y = item.y; + cvSeqPush(ptseq, &p); + } + + //μhullstorage + CvSeq* hull = cvConvexHull2(ptseq, nullptr, clockwise ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE, 0); + + //dst + dst.clear(); + for (int i = 0, hullCount = hull->total; i < hullCount; i++) + dst.push_back(**CV_GET_SEQ_ELEM(CvPoint*, hull, i)); + + //storage + cvReleaseMemStorage(&storage); + } + +#define R_COLOR 255 + void fillConvexHull(cv::Mat& image, const std::vector& points) + { + uint index_top = 0; + uint index_bottom = 0; + for (size_t i = 0, length = points.size(); i < length; i++) + { + if (points[i].y < points[index_top].y) + index_top = i; + if (points[i].y > points[index_bottom].y) + index_bottom = i; + } + + std::vector edge_left; + uint temp = index_top; + while (temp != index_bottom) + { + edge_left.push_back(points[temp]); + temp = (temp + points.size() - 1) % points.size(); + } + edge_left.push_back(points[index_bottom]); + + std::vector edge_right; + temp = index_top; + while (temp != index_bottom) + { + edge_right.push_back(points[temp]); + temp = (temp + points.size() + 1) % points.size(); + } + edge_right.push_back(points[index_bottom]); + + std::vector left_edge_x; + std::vector left_edge_y; + for (size_t i = 0, length = edge_left.size() - 1; i < length; i++) + { + int y_top = edge_left[i].y; + int x_top = edge_left[i].x; + int y_bottom = edge_left[i + 1].y; + int x_bottom = edge_left[i + 1].x; + for (int y = y_top; y < y_bottom; y++) + if (y >= 0 && y_top != y_bottom && y < image.rows) + { + left_edge_x.push_back(((x_bottom - x_top) * y + x_top * y_bottom - x_bottom * y_top) / (y_bottom - y_top)); + left_edge_y.push_back(y); + } + } + size_t step = image.step; + unsigned char* ptr; + ptr = image.data + static_cast(left_edge_y[0]) * step; + for (size_t i = 0, length = left_edge_x.size(); i < length; i++) + { + int pix = left_edge_x[i]; + if (pix < image.cols - 1 && pix > 0) + memset(ptr + i * step, R_COLOR, static_cast((pix + 1) * image.channels())); + } + + std::vector right_edge_x; + std::vector right_edge_y; + for (size_t i = 0, length = edge_right.size() - 1; i < length; i++) + { + int y_top = edge_right[i].y; + int x_top = edge_right[i].x; + int y_bottom = edge_right[i + 1].y; + int x_bottom = edge_right[i + 1].x; + for (int y = y_top; y < y_bottom; y++) + if (y_top != y_bottom && y < image.rows && y >= 0) + { + right_edge_x.push_back(((x_bottom - x_top) * y + x_top * y_bottom - x_bottom * y_top) / (y_bottom - y_top)); + right_edge_y.push_back(y); + } + } + + ptr = image.data + static_cast(right_edge_y[0]) * step; + for (size_t i = 0, length = right_edge_x.size(); i < length; i++) + { + int pix = right_edge_x[i]; + if (pix < image.cols - 1 && pix > 0) + memset(ptr + i * step + pix * image.channels(), R_COLOR, step - static_cast(pix * image.channels())); + } + + if (edge_left[0].y > 0) + memset(image.data, R_COLOR, static_cast(edge_left[0].y) * step); + + if (edge_left.back().y < image.rows - 1) + memset(image.data + static_cast(edge_left.back().y) * step, R_COLOR, + static_cast(image.rows - edge_left.back().y) * step); + } + + void fillPolys(cv::Mat& image, const std::vector>& contours, const cv::Scalar& color) + { + if (contours.empty()) return; + + size_t count = contours.size(); + cv::Point** pointss = new cv::Point*[count]; + int* npts = new int[count]; + + for (size_t i = 0; i < count; i++) + { + size_t length = contours[i].size(); + npts[i] = length; + pointss[i] = new cv::Point[length]; + for (size_t j = 0; j < length; j++) + pointss[i][j] = contours[i][j]; + } + cv::fillPoly(image, const_cast(pointss), npts, count, color); + + for (size_t i = 0; i < count; i++) + delete[] pointss[i]; + + delete[] pointss; + delete[] npts; + } + + void findContours(const cv::Mat& src, std::vector>& contours, std::vector& hierarchy, int retr, int method, cv::Point offset) + { + CvMat c_image; + CV_DbgAssert(src.dims <= 2); + c_image = cvMat(src.rows, src.dims == 1 ? 1 : src.cols, src.type(), src.data); + c_image.step = (int)src.step[0]; + c_image.type = (c_image.type & ~cv::Mat::CONTINUOUS_FLAG) | (src.flags & cv::Mat::CONTINUOUS_FLAG); + cv::MemStorage storage(cvCreateMemStorage()); + CvSeq* _ccontours = nullptr; + cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint({ offset.x,offset.y })); + + if (!_ccontours) + { + contours.clear(); + return; + } + cv::Seq all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage)); + size_t total = all_contours.size(); + contours.resize(total); + + cv::SeqIterator it = all_contours.begin(); + for (size_t i = 0; i < total; i++, ++it) + { + CvSeq* c = *it; + reinterpret_cast(c)->color = static_cast(i); + int count = c->total; + int* data = new int[static_cast(count * 2)]; + cvCvtSeqToArray(c, data); + for (int j = 0; j < count; j++) + { + contours[i].push_back(cv::Point(data[j * 2], data[j * 2 + 1])); + } + delete[] data; + } + + hierarchy.resize(total); + it = all_contours.begin(); + for (size_t i = 0; i < total; i++, ++it) + { + CvSeq* c = *it; + int h_next = c->h_next ? reinterpret_cast(c->h_next)->color : -1; + int h_prev = c->h_prev ? reinterpret_cast(c->h_prev)->color : -1; + int v_next = c->v_next ? reinterpret_cast(c->v_next)->color : -1; + int v_prev = c->v_prev ? reinterpret_cast(c->v_prev)->color : -1; + hierarchy[i] = cv::Vec4i(h_next, h_prev, v_next, v_prev); + } + + storage.release(); + } + + cv::RotatedRect getBoundingRect(const std::vector& contour) + { + if (contour.empty()) return {}; + + cv::RotatedRect rect = minAreaRect(contour); + if (rect.angle < -45) + { + rect.angle += 90; + float temp = rect.size.width; + rect.size.width = rect.size.height; + rect.size.height = temp; + } + + if (rect.angle > 45) + { + rect.angle -= 90; + float temp = rect.size.width; + rect.size.width = rect.size.height; + rect.size.height = temp; + } + + return rect; + } + + std::vector getMaxContour(const std::vector>& contours, const std::vector& hierarchy) + { + std::vector maxContour; + if (contours.size() < 1) return {}; + + for (size_t i = 0, length = hierarchy.size(); i < length; i++) + if (hierarchy[i][3] == -1) + for (const auto &item : contours[i]) + maxContour.push_back(item); + + return maxContour; + } + + std::vector getVertices(const cv::RotatedRect& rect) + { + cv::Point2f box[4]; + rect.points(box); + std::vector points; + for (int i = 0; i < 4; i++) + points.push_back(cv::Point(box[i])); + + return points; + } + + void polyIndent(std::vector& points, const cv::Point& center, int indent) + { + static cv::Point zero(0, 0); + for (cv::Point& item : points) + { +#if 0 + cv::Point vec = item - center; + if (vec != zero) + { + int length = vec.x * vec.x + vec.y * vec.y; + float x = cv::sqrt(static_cast(vec.x * vec.x / length)) * indent; + float y = cv::sqrt(static_cast(vec.y * vec.y / length)) * indent; + + if (vec.x < 0) x *= -1.0f; + if (vec.y < 0) y *= -1.0f; + + item.x -= static_cast(x); + item.y -= static_cast(y); + } +#else + if (item.x > center.x) + item.x -= indent; + else + item.x += indent; + + if (item.y > center.y) + item.y -= indent; + else + item.y += indent; +#endif + } + } + + cv::Mat transforColor(const cv::Mat& src) + { + if (src.channels() == 1) return src.clone(); + + std::vector channels(3); + cv::split(src, channels); + + cv::Mat temp, dst; + bitwise_or(channels[0], channels[1], temp); + bitwise_or(channels[2], temp, dst); + temp.release(); + + for (cv::Mat& index : channels) + index.release(); + return dst; + } + + void threshold_Mat(const cv::Mat& src, cv::Mat& dst, double thre) + { + if (src.channels() == 3) + { +#ifdef USE_ONENCL + if (cl_res.context) + transforColor_threshold_opencl(src, dst, static_cast(thre)); + else +#endif + { + cv::Mat gray = transforColor(src); + cv::threshold(gray, dst, thre, 255, cv::THRESH_BINARY); + gray.release(); + } + } + else + cv::threshold(src, dst, thre, 255, cv::THRESH_BINARY); + } + + cv::Point warpPoint(const cv::Point& p, const cv::Mat& warp_mat) + { + double src_data[3] = { static_cast(p.x), static_cast(p.y), 1 }; + cv::Mat src(3, 1, warp_mat.type(), src_data); //warp_mat.type() == CV_64FC1 + + cv::Mat dst = warp_mat * src; + double* ptr = reinterpret_cast(dst.data); + return cv::Point(static_cast(ptr[0]), static_cast(ptr[1])); + } + + int distanceP2P(const cv::Point& p1, const cv::Point& p2) + { + return cv::sqrt(cv::pow(p1.x - p2.x, 2) + cv::pow(p1.y - p2.y, 2)); + } + + float distanceP2L(const cv::Point& p, const cv::Point& l1, const cv::Point& l2) + { + //求直线方程 + int A = 0, B = 0, C = 0; + A = l1.y - l2.y; + B = l2.x - l1.x; + C = l1.x * l2.y - l1.y * l2.x; + //代入点到直线距离公式 + return ((float)abs(A * p.x + B * p.y + C)) / ((float)sqrtf(A * A + B * B)); + } +} \ No newline at end of file diff --git a/hgdriver/ImageProcess/ImageProcess_Public.h b/hgdriver/ImageProcess/ImageProcess_Public.h new file mode 100644 index 0000000..b2cfa7d --- /dev/null +++ b/hgdriver/ImageProcess/ImageProcess_Public.h @@ -0,0 +1,123 @@ +/* + * ==================================================== + + * 功能:公共图像处理算法。部分功能可能会在ImageProcess里面多个类反复使用 + * 作者:刘丁维 + * 生成时间:2020/4/21 + * 最近修改时间:2020/4/21 + * 版本号:v1.0 + + * ==================================================== + */ + +#ifndef IMAGE_PROCESS_PUBLIC_H +#define IMAGE_PROCESS_PUBLIC_H + +#include +#include + +namespace hg +{ + /* + * 功能:计算源点集的凸多边形轮廓,输出轮廓点集 + * src: 源点集 + * dst: 目标点集 + * clockwise: true为顺时针排序,false为逆时针排序 + */ + void convexHull(const std::vector& src, std::vector& dst, bool clockwise = false); + + /* + * 功能:填充凸多边形,默认颜色为白色 + * image: 填充图像 + * points: 凸多边形轮廓点集(逆时针排序) + */ + void fillConvexHull(cv::Mat& image, const std::vector& points); + + /* + * 功能:填充凹多边形 + * image: 填充图像 + * contours: 凹多边形轮廓点集(逆时针排序) + * color: 填充颜色 + */ + void fillPolys(cv::Mat& image, const std::vector>& contours, const cv::Scalar& color); + + /* + * 功能:获取连通区域轮廓 + * src: 源图像 + * contours: 结果轮廓集 + * hierarchy: 轮廓集的排序关系。与contours的数量对应,受retr选项不同,排序会有变化 + * retr: 轮廓集排序方式,默认为链式排序 + * method: 查找算法选择,默认为普通查找 + * offset: 查找起始点,默认为(0,0)点 + */ + void findContours(const cv::Mat& src, std::vector>& contours, std::vector& hierarchy, + int retr = cv::RETR_LIST, int method = cv::CHAIN_APPROX_SIMPLE, cv::Point offset = cv::Point(0, 0)); + + cv::RotatedRect getBoundingRect(const std::vector& contour); + + /* + * 功能: 获取覆盖轮廓集的最小外接凸多边形轮廓 + * contours: 轮廓集(每个轮廓由点集组成) + * hierarchy: 轮廓集中,轮廓之间的关系。数量与contours对应 + * 返回值: 凸多边形轮廓点集 + */ + std::vector getMaxContour(const std::vector>& contours, const std::vector& hierarchy); + + /* + * 功能: 获取覆盖轮廓集的最小外接凸多边形轮廓 + * contours: 轮廓集(每个轮廓由点集组成) + * hierarchy: 轮廓集中,轮廓之间的关系。数量与contours对应 + * 返回值: 凸多边形轮廓点集 + */ + std::vector getVertices(const cv::RotatedRect& rect); + + /* + * 功能: 轮廓缩进 + * points: 轮廓点集 + * center: 围绕center点缩进 + * indent: 缩进像素 + */ + void polyIndent(std::vector& points, const cv::Point& center, int indent); + + /* + * 功能: 二值化,能够处理彩色和灰度图像。src为彩色图像时,灰度图取三个通道的最大值 + * src: 源图 + * dst: 目标图 + * thre: 阈值 + */ + void threshold_Mat(const cv::Mat& src, cv::Mat& dst, double thre); + + /* + * 功能: 彩色转灰度,灰度图取三个通道的最大值 + * src: 源图 + * 返回值: 灰度图 + */ + cv::Mat transforColor(const cv::Mat& src); + + /* + * 功能: 获取点的仿射变换 + * p: 原点 + * warp_mat: 仿射变换系数矩阵 + * 返回值: 变换后的点 + */ + cv::Point warpPoint(const cv::Point& p, const cv::Mat& warp_mat); + + /* + * 功能: 点到点距离 + * p1: 点1 + * p2: 点2 + * 返回值: 点到点距离 + */ + int distanceP2P(const cv::Point& p1, const cv::Point& p2); + + /* + * 功能: 点到直线距离 + * p: 点 + * l1: 直线端点1 + * l2: 直线端点2 + * 返回值: 点到直线距离 + */ + float distanceP2L(const cv::Point& p, const cv::Point& l1, const cv::Point& l2); +} + +#endif // !IMAGE_PROCESS_C_H diff --git a/hgdriver/ImageProcess/Makefile b/hgdriver/ImageProcess/Makefile new file mode 100644 index 0000000..945e4d2 --- /dev/null +++ b/hgdriver/ImageProcess/Makefile @@ -0,0 +1,706 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.13 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/modehua/Desktop/鏈鏂扮増sdk璋冭瘯/Iibwebscan/ImageProcess + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/modehua/Desktop/鏈鏂扮増sdk璋冭瘯/Iibwebscan/ImageProcess + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." + /usr/bin/cmake-gui -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /home/modehua/Desktop/鏈鏂扮増sdk璋冭瘯/Iibwebscan/ImageProcess/CMakeFiles /home/modehua/Desktop/鏈鏂扮増sdk璋冭瘯/Iibwebscan/ImageProcess/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /home/modehua/Desktop/鏈鏂扮増sdk璋冭瘯/Iibwebscan/ImageProcess/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named gimgproc + +# Build rule for target. +gimgproc: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 gimgproc +.PHONY : gimgproc + +# fast build rule for target. +gimgproc/fast: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/build +.PHONY : gimgproc/fast + +# target to build an object file +IMulti.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/IMulti.o +.PHONY : IMulti.o + +# target to preprocess a source file +IMulti.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/IMulti.i +.PHONY : IMulti.i + +# target to generate assembly for a file +IMulti.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/IMulti.s +.PHONY : IMulti.s + +# target to build an object file +ImageApply.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApply.o +.PHONY : ImageApply.o + +# target to preprocess a source file +ImageApply.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApply.i +.PHONY : ImageApply.i + +# target to generate assembly for a file +ImageApply.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApply.s +.PHONY : ImageApply.s + +# target to build an object file +ImageApplyAdjustColors.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAdjustColors.o +.PHONY : ImageApplyAdjustColors.o + +# target to preprocess a source file +ImageApplyAdjustColors.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAdjustColors.i +.PHONY : ImageApplyAdjustColors.i + +# target to generate assembly for a file +ImageApplyAdjustColors.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAdjustColors.s +.PHONY : ImageApplyAdjustColors.s + +# target to build an object file +ImageApplyAutoContrast.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoContrast.o +.PHONY : ImageApplyAutoContrast.o + +# target to preprocess a source file +ImageApplyAutoContrast.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoContrast.i +.PHONY : ImageApplyAutoContrast.i + +# target to generate assembly for a file +ImageApplyAutoContrast.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoContrast.s +.PHONY : ImageApplyAutoContrast.s + +# target to build an object file +ImageApplyAutoCrop.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoCrop.o +.PHONY : ImageApplyAutoCrop.o + +# target to preprocess a source file +ImageApplyAutoCrop.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoCrop.i +.PHONY : ImageApplyAutoCrop.i + +# target to generate assembly for a file +ImageApplyAutoCrop.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyAutoCrop.s +.PHONY : ImageApplyAutoCrop.s + +# target to build an object file +ImageApplyBWBinaray.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyBWBinaray.o +.PHONY : ImageApplyBWBinaray.o + +# target to preprocess a source file +ImageApplyBWBinaray.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyBWBinaray.i +.PHONY : ImageApplyBWBinaray.i + +# target to generate assembly for a file +ImageApplyBWBinaray.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyBWBinaray.s +.PHONY : ImageApplyBWBinaray.s + +# target to build an object file +ImageApplyChannel.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyChannel.o +.PHONY : ImageApplyChannel.o + +# target to preprocess a source file +ImageApplyChannel.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyChannel.i +.PHONY : ImageApplyChannel.i + +# target to generate assembly for a file +ImageApplyChannel.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyChannel.s +.PHONY : ImageApplyChannel.s + +# target to build an object file +ImageApplyColorRecognition.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyColorRecognition.o +.PHONY : ImageApplyColorRecognition.o + +# target to preprocess a source file +ImageApplyColorRecognition.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyColorRecognition.i +.PHONY : ImageApplyColorRecognition.i + +# target to generate assembly for a file +ImageApplyColorRecognition.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyColorRecognition.s +.PHONY : ImageApplyColorRecognition.s + +# target to build an object file +ImageApplyConcatenation.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyConcatenation.o +.PHONY : ImageApplyConcatenation.o + +# target to preprocess a source file +ImageApplyConcatenation.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyConcatenation.i +.PHONY : ImageApplyConcatenation.i + +# target to generate assembly for a file +ImageApplyConcatenation.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyConcatenation.s +.PHONY : ImageApplyConcatenation.s + +# target to build an object file +ImageApplyCrop.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCrop.o +.PHONY : ImageApplyCrop.o + +# target to preprocess a source file +ImageApplyCrop.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCrop.i +.PHONY : ImageApplyCrop.i + +# target to generate assembly for a file +ImageApplyCrop.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCrop.s +.PHONY : ImageApplyCrop.s + +# target to build an object file +ImageApplyCustomCrop.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomCrop.o +.PHONY : ImageApplyCustomCrop.o + +# target to preprocess a source file +ImageApplyCustomCrop.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomCrop.i +.PHONY : ImageApplyCustomCrop.i + +# target to generate assembly for a file +ImageApplyCustomCrop.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomCrop.s +.PHONY : ImageApplyCustomCrop.s + +# target to build an object file +ImageApplyCustomGamma.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomGamma.o +.PHONY : ImageApplyCustomGamma.o + +# target to preprocess a source file +ImageApplyCustomGamma.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomGamma.i +.PHONY : ImageApplyCustomGamma.i + +# target to generate assembly for a file +ImageApplyCustomGamma.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCustomGamma.s +.PHONY : ImageApplyCustomGamma.s + +# target to build an object file +ImageApplyCvtColor.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCvtColor.o +.PHONY : ImageApplyCvtColor.o + +# target to preprocess a source file +ImageApplyCvtColor.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCvtColor.i +.PHONY : ImageApplyCvtColor.i + +# target to generate assembly for a file +ImageApplyCvtColor.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyCvtColor.s +.PHONY : ImageApplyCvtColor.s + +# target to build an object file +ImageApplyDetachNoise.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDetachNoise.o +.PHONY : ImageApplyDetachNoise.o + +# target to preprocess a source file +ImageApplyDetachNoise.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDetachNoise.i +.PHONY : ImageApplyDetachNoise.i + +# target to generate assembly for a file +ImageApplyDetachNoise.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDetachNoise.s +.PHONY : ImageApplyDetachNoise.s + +# target to build an object file +ImageApplyDiscardBlank.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDiscardBlank.o +.PHONY : ImageApplyDiscardBlank.o + +# target to preprocess a source file +ImageApplyDiscardBlank.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDiscardBlank.i +.PHONY : ImageApplyDiscardBlank.i + +# target to generate assembly for a file +ImageApplyDiscardBlank.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDiscardBlank.s +.PHONY : ImageApplyDiscardBlank.s + +# target to build an object file +ImageApplyDogEarDetection.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDogEarDetection.o +.PHONY : ImageApplyDogEarDetection.o + +# target to preprocess a source file +ImageApplyDogEarDetection.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDogEarDetection.i +.PHONY : ImageApplyDogEarDetection.i + +# target to generate assembly for a file +ImageApplyDogEarDetection.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyDogEarDetection.s +.PHONY : ImageApplyDogEarDetection.s + +# target to build an object file +ImageApplyFadeBackGroundColor.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFadeBackGroundColor.o +.PHONY : ImageApplyFadeBackGroundColor.o + +# target to preprocess a source file +ImageApplyFadeBackGroundColor.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFadeBackGroundColor.i +.PHONY : ImageApplyFadeBackGroundColor.i + +# target to generate assembly for a file +ImageApplyFadeBackGroundColor.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFadeBackGroundColor.s +.PHONY : ImageApplyFadeBackGroundColor.s + +# target to build an object file +ImageApplyFilter.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFilter.o +.PHONY : ImageApplyFilter.o + +# target to preprocess a source file +ImageApplyFilter.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFilter.i +.PHONY : ImageApplyFilter.i + +# target to generate assembly for a file +ImageApplyFilter.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyFilter.s +.PHONY : ImageApplyFilter.s + +# target to build an object file +ImageApplyHSVCorrect.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyHSVCorrect.o +.PHONY : ImageApplyHSVCorrect.o + +# target to preprocess a source file +ImageApplyHSVCorrect.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyHSVCorrect.i +.PHONY : ImageApplyHSVCorrect.i + +# target to generate assembly for a file +ImageApplyHSVCorrect.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyHSVCorrect.s +.PHONY : ImageApplyHSVCorrect.s + +# target to build an object file +ImageApplyOutHole.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyOutHole.o +.PHONY : ImageApplyOutHole.o + +# target to preprocess a source file +ImageApplyOutHole.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyOutHole.i +.PHONY : ImageApplyOutHole.i + +# target to generate assembly for a file +ImageApplyOutHole.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyOutHole.s +.PHONY : ImageApplyOutHole.s + +# target to build an object file +ImageApplyRefuseInflow.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRefuseInflow.o +.PHONY : ImageApplyRefuseInflow.o + +# target to preprocess a source file +ImageApplyRefuseInflow.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRefuseInflow.i +.PHONY : ImageApplyRefuseInflow.i + +# target to generate assembly for a file +ImageApplyRefuseInflow.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRefuseInflow.s +.PHONY : ImageApplyRefuseInflow.s + +# target to build an object file +ImageApplyResize.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyResize.o +.PHONY : ImageApplyResize.o + +# target to preprocess a source file +ImageApplyResize.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyResize.i +.PHONY : ImageApplyResize.i + +# target to generate assembly for a file +ImageApplyResize.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyResize.s +.PHONY : ImageApplyResize.s + +# target to build an object file +ImageApplyRotation.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRotation.o +.PHONY : ImageApplyRotation.o + +# target to preprocess a source file +ImageApplyRotation.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRotation.i +.PHONY : ImageApplyRotation.i + +# target to generate assembly for a file +ImageApplyRotation.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyRotation.s +.PHONY : ImageApplyRotation.s + +# target to build an object file +ImageApplySharpen.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySharpen.o +.PHONY : ImageApplySharpen.o + +# target to preprocess a source file +ImageApplySharpen.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySharpen.i +.PHONY : ImageApplySharpen.i + +# target to generate assembly for a file +ImageApplySharpen.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySharpen.s +.PHONY : ImageApplySharpen.s + +# target to build an object file +ImageApplySplit.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySplit.o +.PHONY : ImageApplySplit.o + +# target to preprocess a source file +ImageApplySplit.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySplit.i +.PHONY : ImageApplySplit.i + +# target to generate assembly for a file +ImageApplySplit.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplySplit.s +.PHONY : ImageApplySplit.s + +# target to build an object file +ImageApplyTextureRemoval.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyTextureRemoval.o +.PHONY : ImageApplyTextureRemoval.o + +# target to preprocess a source file +ImageApplyTextureRemoval.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyTextureRemoval.i +.PHONY : ImageApplyTextureRemoval.i + +# target to generate assembly for a file +ImageApplyTextureRemoval.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyTextureRemoval.s +.PHONY : ImageApplyTextureRemoval.s + +# target to build an object file +ImageApplyUV.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyUV.o +.PHONY : ImageApplyUV.o + +# target to preprocess a source file +ImageApplyUV.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyUV.i +.PHONY : ImageApplyUV.i + +# target to generate assembly for a file +ImageApplyUV.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageApplyUV.s +.PHONY : ImageApplyUV.s + +# target to build an object file +ImageMulti.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMulti.o +.PHONY : ImageMulti.o + +# target to preprocess a source file +ImageMulti.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMulti.i +.PHONY : ImageMulti.i + +# target to generate assembly for a file +ImageMulti.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMulti.s +.PHONY : ImageMulti.s + +# target to build an object file +ImageMultiOutputRed.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMultiOutputRed.o +.PHONY : ImageMultiOutputRed.o + +# target to preprocess a source file +ImageMultiOutputRed.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMultiOutputRed.i +.PHONY : ImageMultiOutputRed.i + +# target to generate assembly for a file +ImageMultiOutputRed.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageMultiOutputRed.s +.PHONY : ImageMultiOutputRed.s + +# target to build an object file +ImageProcess_Public.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageProcess_Public.o +.PHONY : ImageProcess_Public.o + +# target to preprocess a source file +ImageProcess_Public.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageProcess_Public.i +.PHONY : ImageProcess_Public.i + +# target to generate assembly for a file +ImageProcess_Public.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/ImageProcess_Public.s +.PHONY : ImageProcess_Public.s + +# target to build an object file +hg_ocr.o: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/hg_ocr.o +.PHONY : hg_ocr.o + +# target to preprocess a source file +hg_ocr.i: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/hg_ocr.i +.PHONY : hg_ocr.i + +# target to generate assembly for a file +hg_ocr.s: + $(MAKE) -f CMakeFiles/gimgproc.dir/build.make CMakeFiles/gimgproc.dir/hg_ocr.s +.PHONY : hg_ocr.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... gimgproc" + @echo "... edit_cache" + @echo "... IMulti.o" + @echo "... IMulti.i" + @echo "... IMulti.s" + @echo "... ImageApply.o" + @echo "... ImageApply.i" + @echo "... ImageApply.s" + @echo "... ImageApplyAdjustColors.o" + @echo "... ImageApplyAdjustColors.i" + @echo "... ImageApplyAdjustColors.s" + @echo "... ImageApplyAutoContrast.o" + @echo "... ImageApplyAutoContrast.i" + @echo "... ImageApplyAutoContrast.s" + @echo "... ImageApplyAutoCrop.o" + @echo "... ImageApplyAutoCrop.i" + @echo "... ImageApplyAutoCrop.s" + @echo "... ImageApplyBWBinaray.o" + @echo "... ImageApplyBWBinaray.i" + @echo "... ImageApplyBWBinaray.s" + @echo "... ImageApplyChannel.o" + @echo "... ImageApplyChannel.i" + @echo "... ImageApplyChannel.s" + @echo "... ImageApplyColorRecognition.o" + @echo "... ImageApplyColorRecognition.i" + @echo "... ImageApplyColorRecognition.s" + @echo "... ImageApplyConcatenation.o" + @echo "... ImageApplyConcatenation.i" + @echo "... ImageApplyConcatenation.s" + @echo "... ImageApplyCrop.o" + @echo "... ImageApplyCrop.i" + @echo "... ImageApplyCrop.s" + @echo "... ImageApplyCustomCrop.o" + @echo "... ImageApplyCustomCrop.i" + @echo "... ImageApplyCustomCrop.s" + @echo "... ImageApplyCustomGamma.o" + @echo "... ImageApplyCustomGamma.i" + @echo "... ImageApplyCustomGamma.s" + @echo "... ImageApplyCvtColor.o" + @echo "... ImageApplyCvtColor.i" + @echo "... ImageApplyCvtColor.s" + @echo "... ImageApplyDetachNoise.o" + @echo "... ImageApplyDetachNoise.i" + @echo "... ImageApplyDetachNoise.s" + @echo "... ImageApplyDiscardBlank.o" + @echo "... ImageApplyDiscardBlank.i" + @echo "... ImageApplyDiscardBlank.s" + @echo "... ImageApplyDogEarDetection.o" + @echo "... ImageApplyDogEarDetection.i" + @echo "... ImageApplyDogEarDetection.s" + @echo "... ImageApplyFadeBackGroundColor.o" + @echo "... ImageApplyFadeBackGroundColor.i" + @echo "... ImageApplyFadeBackGroundColor.s" + @echo "... ImageApplyFilter.o" + @echo "... ImageApplyFilter.i" + @echo "... ImageApplyFilter.s" + @echo "... ImageApplyHSVCorrect.o" + @echo "... ImageApplyHSVCorrect.i" + @echo "... ImageApplyHSVCorrect.s" + @echo "... ImageApplyOutHole.o" + @echo "... ImageApplyOutHole.i" + @echo "... ImageApplyOutHole.s" + @echo "... ImageApplyRefuseInflow.o" + @echo "... ImageApplyRefuseInflow.i" + @echo "... ImageApplyRefuseInflow.s" + @echo "... ImageApplyResize.o" + @echo "... ImageApplyResize.i" + @echo "... ImageApplyResize.s" + @echo "... ImageApplyRotation.o" + @echo "... ImageApplyRotation.i" + @echo "... ImageApplyRotation.s" + @echo "... ImageApplySharpen.o" + @echo "... ImageApplySharpen.i" + @echo "... ImageApplySharpen.s" + @echo "... ImageApplySplit.o" + @echo "... ImageApplySplit.i" + @echo "... ImageApplySplit.s" + @echo "... ImageApplyTextureRemoval.o" + @echo "... ImageApplyTextureRemoval.i" + @echo "... ImageApplyTextureRemoval.s" + @echo "... ImageApplyUV.o" + @echo "... ImageApplyUV.i" + @echo "... ImageApplyUV.s" + @echo "... ImageMulti.o" + @echo "... ImageMulti.i" + @echo "... ImageMulti.s" + @echo "... ImageMultiOutputRed.o" + @echo "... ImageMultiOutputRed.i" + @echo "... ImageMultiOutputRed.s" + @echo "... ImageProcess_Public.o" + @echo "... ImageProcess_Public.i" + @echo "... ImageProcess_Public.s" + @echo "... hg_ocr.o" + @echo "... hg_ocr.i" + @echo "... hg_ocr.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/hgdriver/ImageProcess/MatEx.h b/hgdriver/ImageProcess/MatEx.h new file mode 100644 index 0000000..ab0c469 --- /dev/null +++ b/hgdriver/ImageProcess/MatEx.h @@ -0,0 +1,22 @@ +#pragma once +#include + +class MatEx { +public: + MatEx() : + mat(cv::Mat()) + , Bpp(-1) { + + }; + + MatEx(cv::Mat &mat, int bpp) : + mat(cv::Mat()) + , Bpp(bpp) + { + this->mat = mat; + this->Bpp = bpp; + } +public: + int Bpp; + cv::Mat mat; +}; \ No newline at end of file diff --git a/hgdriver/ImageProcess/hg_ocr.cpp b/hgdriver/ImageProcess/hg_ocr.cpp new file mode 100644 index 0000000..5c60f2f --- /dev/null +++ b/hgdriver/ImageProcess/hg_ocr.cpp @@ -0,0 +1,513 @@ +锘#ifdef WIN32 +#include "hg_ocr.h" +#include "allheaders.h" +#include "baseapi.h" +#include "basedir.h" +#include "osdetect.h" +#include "renderer.h" +#include "strngs.h" +#include "tprintf.h" +#include "resultiterator.h" +#include + +static unsigned char string_CIDTOGIDMAP[] = { + 120,156,236,194,1,9,0,0,0,2,160,250,127,186,33,137, + 166,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,128,123,3,0,0,255,255,236,194,1,13,0,0, + 0,194,32,223,191,180,69,24,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,235,0,0,0,255, + 255,236,194,1,13,0,0,0,194,32,223,191,180,69,24,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,235,0,0,0,255,255,237,194,1,13,0,0,0,194,32, + 223,191,180,69,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,235,0,255,0,16}; + +static unsigned char string_TTF[] = { + 0,1,0,0,0,10,0,128,0,3,0,32,79,83,47,50,86,221,200,148, + 0,0,1,40,0,0,0,96,99,109,97,112,0,18,0,78,0,0,1,144,0,0, + 0,44,103,108,121,102,0,0,0,0,0,0,1,196,0,0,0,1,104,101, + 97,100,2,80,182,226,0,0,0,172,0,0,0,54,104,104,101,97,0, + 3,0,2,0,0,0,228,0,0,0,36,104,109,116,120,0,0,0,0,0,0,1, + 136,0,0,0,8,108,111,99,97,0,0,0,0,0,0,1,188,0,0,0,6,109, + 97,120,112,0,3,0,1,0,0,1,8,0,0,0,32,110,97,109,101,165, + 232,245,73,0,0,1,200,0,0,0,80,112,111,115,116,0,1,0,1,0, + 0,2,24,0,0,0,32,0,1,0,0,0,1,0,0,167,55,179,76,95,15,60,245, + 4,7,1,0,0,0,0,0,207,154,252,110,0,0,0,0,207,154,252,110,0, + 0,128,0,0,0,0,1,0,0,0,16,0,2,0,0,0,0,0,0,0,1,0,0,0,1,255, + 255,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,1,144,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,5,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,71,79,79,71,0,64,255,255,0,0,0,1,255,255,0,0,0,1, + 0,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + 0,0,0,2,0,1,0,0,0,0,0,20,0,3,0,0,0,0,0,32,0,6,0,12,0,0,0,0, + 0,1,0,0,0,6,0,12,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,42,0,0,0,3,0,0,0,5,0,22,0,11,0,1,0,0,0,0,0,5,0,11,0, + 0,0,3,0,1,4,9,0,5,0,22,0,11,86,101,114,115,105,111,110,32, + 49,46,48,0,86,0,101,0,114,0,115,0,105,0,111,0,110,0,32,0,49, + 0,46,0,48,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0}; + +static const char* endstream = "endstream\nendobj\n"; + +Pix* createPix(const unsigned char * imgData, int width, int height, int bytes_per_pixel, int bytes_per_line); + +void PreloadRenderers(tesseract::TessBaseAPI* api, + tesseract::PointerVector* renderers, const char* outputbase); + +char* GetPDFTextObjectss(tesseract::TessBaseAPI* api, double width, double height, int& len); + +HG_OCR::HG_OCR() + : api(new tesseract::TessBaseAPI()) +{ + +} + +HG_OCR::HG_OCR(PSM_TYPE type) + : api(new tesseract::TessBaseAPI()) +{ + init_orientation("./tessdata/osd.traineddata"); +} + +HG_OCR::~HG_OCR() +{ + if (api != nullptr) + delete reinterpret_cast(api); +} + +void HG_OCR::init(HG_OCR::PSM_TYPE type) +{ + init_orientation("./tessdata/osd.traineddata"); +} + +void HG_OCR::init(const char * filename, PSM_TYPE type) +{ + switch (type) + { + case Orientation: + init_orientation(filename); + break; + } +} + +int HG_OCR::getOrientation(unsigned char *imgData, int width, int height, int channels, int step) +{ + int orientation, direction, lineOrder; + float deskewAngle; + getOrientation(imgData, width, height, channels, step, + orientation, direction, lineOrder, deskewAngle); + + return orientation; +} + +bool HG_OCR::getOrientation(unsigned char* imgData, int width, int height, int channels, int step, + int& orientation, int& direction, int& lineOrder, float& deskewAngle) +{ + if (api == nullptr) return false; + + tesseract::TessBaseAPI* ptr = reinterpret_cast(api); + ptr->SetImage(imgData, width, height, channels, step); + +#if 0 + tesseract::PageIterator* it = ptr->AnalyseLayout(); + + if (it != nullptr) + { + it->Orientation(reinterpret_cast(&orientation), + reinterpret_cast(&direction), + reinterpret_cast(&lineOrder), + &deskewAngle); + delete it; + return true; + } + else + return false; +#endif + orientation = ptr->AnalyseLayout1(); +} + +void HG_OCR::init_orientation(const char *filename) +{ + auto ret= reinterpret_cast(api)->Init(filename, "osd"); + reinterpret_cast(api)->SetPageSegMode(tesseract::PSM_AUTO_OSD); +} + +void PreloadRenderers(tesseract::TessBaseAPI* api, + tesseract::PointerVector* renderers, + const char* outputbase) +{ + bool b; + api->GetBoolVariable("tessedit_create_pdf", &b); + + if (b) + { + bool textonly; + api->GetBoolVariable("textonly_pdf", &textonly); + printf("GetDatapath%s\n", api->GetDatapath()); + renderers->push_back(new tesseract::TessPDFRenderer(outputbase, api->GetDatapath(), textonly)); + } +} + +Pix* createPix(const unsigned char * imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line) +{ + int bpp = bytes_per_pixel * 8; + if (bpp == 0) bpp = 1; + Pix* pix = pixCreate(width, height, bpp == 24 ? 32 : bpp); + l_uint32* data = pixGetData(pix); + int wpl = pixGetWpl(pix); + switch (bpp) { + case 1: + for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) + { + for (int x = 0; x < width; ++x) { + if (imagedata[x / 8] & (0x80 >> (x % 8))) + CLEAR_DATA_BIT(data, x); + else + SET_DATA_BIT(data, x); + } + } + break; + + case 8: + // Greyscale just copies the bytes in the right order. + for (int y = 0; y < height; ++y, data += wpl, imagedata += bytes_per_line) + for (int x = 0; x < width; ++x) + SET_DATA_BYTE(data, x, imagedata[x]); + break; + + case 24: + // Put the colors in the correct places in the line buffer. + for (int y = 0; y < height; ++y, imagedata += bytes_per_line) + { + for (int x = 0; x < width; ++x, ++data) { + SET_DATA_BYTE(data, COLOR_RED, imagedata[3 * x]); + SET_DATA_BYTE(data, COLOR_GREEN, imagedata[3 * x + 1]); + SET_DATA_BYTE(data, COLOR_BLUE, imagedata[3 * x + 2]); + } + } + break; + + case 32: + // Maintain byte order consistency across different endianness. + for (int y = 0; y < height; ++y, imagedata += bytes_per_line, data += wpl) + for (int x = 0; x < width; ++x) + data[x] = (imagedata[x * 4] << 24) | (imagedata[x * 4 + 1] << 16) | + (imagedata[x * 4 + 2] << 8) | imagedata[x * 4 + 3]; + break; + + default: + break; + } + + pix->informat = bytes_per_pixel == 1 ? 1 : 2; + if (bytes_per_pixel == 1) + { + PIXCMAP* colormap = pixcmapCreate(8); + LEPT_FREE(colormap->array); + colormap->array = (l_uint8 *)LEPT_CALLOC(256, sizeof(RGBA_QUAD)); + colormap->n = 256; + colormap->nalloc = 256; + colormap->depth = 8; + l_uint8* ptr = reinterpret_cast(colormap->array); + for (int i = 0; i < 256; i++) + ptr[i * 4 + 0] = ptr[i * 4 + 1] = ptr[i * 4 + 2] = ptr[i * 4 + 3] = i; + pixSetColormap(pix, colormap); + } + pixSetXRes(pix, 200); + pixSetYRes(pix, 200); + + //FILE* file = fopenWriteStream("aaa.bmp", "w"); + //pixWriteStreamBmp(file, pix); + //fclose(file); + return pix; +} + +double prec(double x) { + double kPrecision = 1000.0; + double a = round(x * kPrecision) / kPrecision; + if (a == -0) + return 0; + return a; +} + +void ClipBaseline(int ppi, int x1, int y1, int x2, int y2, + int *line_x1, int *line_y1, + int *line_x2, int *line_y2) +{ + *line_x1 = x1; + *line_y1 = y1; + *line_x2 = x2; + *line_y2 = y2; + double rise = abs(y2 - y1) * 72 / ppi; + double run = abs(x2 - x1) * 72 / ppi; + if (rise < 2.0 && 2.0 < run) + *line_y1 = *line_y2 = (y1 + y2) / 2; +} + +long dist2(int x1, int y1, int x2, int y2) +{ + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); +} + +void GetWordBaseline(int writing_direction, int ppi, int height, + int word_x1, int word_y1, int word_x2, int word_y2, + int line_x1, int line_y1, int line_x2, int line_y2, + double *x0, double *y0, double *length) +{ + if (writing_direction == tesseract::WRITING_DIRECTION_RIGHT_TO_LEFT) + { + Swap(&word_x1, &word_x2); + Swap(&word_y1, &word_y2); + } + double word_length; + double x, y; + { + int px = word_x1; + int py = word_y1; + double l2 = dist2(line_x1, line_y1, line_x2, line_y2); + if (l2 == 0) { + x = line_x1; + y = line_y1; + } + else { + double t = ((px - line_x2) * (line_x2 - line_x1) + + (py - line_y2) * (line_y2 - line_y1)) / l2; + x = line_x2 + t * (line_x2 - line_x1); + y = line_y2 + t * (line_y2 - line_y1); + } + word_length = sqrt(static_cast(dist2(word_x1, word_y1, + word_x2, word_y2))); + word_length = word_length * 72.0 / ppi; + x = x * 72 / ppi; + y = height - (y * 72.0 / ppi); + } + *x0 = x; + *y0 = y; + *length = word_length; +} + +void AffineMatrix(int writing_direction, + int line_x1, int line_y1, int line_x2, int line_y2, + double *a, double *b, double *c, double *d) +{ + double theta = atan2(static_cast(line_y1 - line_y2), + static_cast(line_x2 - line_x1)); + *a = cos(theta); + *b = sin(theta); + *c = -sin(theta); + *d = cos(theta); + switch (writing_direction) { + case tesseract::WRITING_DIRECTION_RIGHT_TO_LEFT: + *a = -*a; + *b = -*b; + break; + case tesseract::WRITING_DIRECTION_TOP_TO_BOTTOM: + // TODO(jbreiden) Consider using the vertical PDF writing mode. + break; + default: + break; + } +} + +bool CodepointToUtf16be(int code, char utf16[20]) +{ + if ((code > 0xD7FF && code < 0xE000) || code > 0x10FFFF) { + tprintf("Dropping invalid codepoint %d\n", code); + return false; + } + if (code < 0x10000) { + snprintf(utf16, 20, "%04X", code); + } + else { + int a = code - 0x010000; + int high_surrogate = (0x03FF & (a >> 10)) + 0xD800; + int low_surrogate = (0x03FF & a) + 0xDC00; + snprintf(utf16, 20, "%04X%04X", high_surrogate, low_surrogate); + } + return true; +} + +char* GetPDFTextObjectss(tesseract::TessBaseAPI* api, double width, double height, int& len) +{ + STRING pdf_str(""); + double ppi = api->GetSourceYResolution(); + + // These initial conditions are all arbitrary and will be overwritten + double old_x = 0.0, old_y = 0.0; + int old_fontsize = 0; + tesseract::WritingDirection old_writing_direction = tesseract::WRITING_DIRECTION_LEFT_TO_RIGHT; + bool new_block = true; + int fontsize = 0; + double a = 1; + double b = 0; + double c = 0; + double d = 1; + + // TODO(jbreiden) This marries the text and image together. + // Slightly cleaner from an abstraction standpoint if this were to + // live inside a separate text object. + pdf_str += "q "; + pdf_str.add_str_double("", prec(width)); + pdf_str += " 0 0 "; + pdf_str.add_str_double("", prec(height)); + pdf_str += " 0 0 cm"; + if (true) { + pdf_str += " /Im1 Do"; + } + pdf_str += " Q\n"; + + int line_x1 = 0; + int line_y1 = 0; + int line_x2 = 0; + int line_y2 = 0; + + tesseract::ResultIterator *res_it = api->GetIterator(); + while (!res_it->Empty(tesseract::RIL_BLOCK)) + { + if (res_it->IsAtBeginningOf(tesseract::RIL_BLOCK)) + { + pdf_str += "BT\n3 Tr"; // Begin text object, use invisible ink + old_fontsize = 0; // Every block will declare its fontsize + new_block = true; // Every block will declare its affine matrix + } + + if (res_it->IsAtBeginningOf(tesseract::RIL_TEXTLINE)) + { + int x1, y1, x2, y2; + res_it->Baseline(tesseract::RIL_TEXTLINE, &x1, &y1, &x2, &y2); + ClipBaseline(ppi, x1, y1, x2, y2, &line_x1, &line_y1, &line_x2, &line_y2); + } + + if (res_it->Empty(tesseract::RIL_WORD)) + { + res_it->Next(tesseract::RIL_WORD); + continue; + } + + // Writing direction changes at a per-word granularity + tesseract::WritingDirection writing_direction; + { + tesseract::Orientation orientation; + tesseract::TextlineOrder textline_order; + float deskew_angle; + res_it->Orientation(&orientation, &writing_direction, + &textline_order, &deskew_angle); + if (writing_direction != tesseract::WRITING_DIRECTION_TOP_TO_BOTTOM) + { + switch (res_it->WordDirection()) { + case DIR_LEFT_TO_RIGHT: + writing_direction = tesseract::WRITING_DIRECTION_LEFT_TO_RIGHT; + break; + case DIR_RIGHT_TO_LEFT: + writing_direction = tesseract::WRITING_DIRECTION_RIGHT_TO_LEFT; + break; + default: + writing_direction = old_writing_direction; + } + } + } + + // Where is word origin and how long is it? + double x, y, word_length; + { + int word_x1, word_y1, word_x2, word_y2; + res_it->Baseline(tesseract::RIL_WORD, &word_x1, &word_y1, &word_x2, &word_y2); + GetWordBaseline(writing_direction, ppi, height, + word_x1, word_y1, word_x2, word_y2, + line_x1, line_y1, line_x2, line_y2, + &x, &y, &word_length); + } + + if (writing_direction != old_writing_direction || new_block) + { + AffineMatrix(writing_direction, + line_x1, line_y1, line_x2, line_y2, &a, &b, &c, &d); + pdf_str.add_str_double(" ", prec(a)); // . This affine matrix + pdf_str.add_str_double(" ", prec(b)); // . sets the coordinate + pdf_str.add_str_double(" ", prec(c)); // . system for all + pdf_str.add_str_double(" ", prec(d)); // . text that follows. + pdf_str.add_str_double(" ", prec(x)); // . + pdf_str.add_str_double(" ", prec(y)); // . + pdf_str += (" Tm "); // Place cursor absolutely + new_block = false; + } + else + { + double dx = x - old_x; + double dy = y - old_y; + pdf_str.add_str_double(" ", prec(dx * a + dy * b)); + pdf_str.add_str_double(" ", prec(dx * c + dy * d)); + pdf_str += (" Td "); // Relative moveto + } + old_x = x; + old_y = y; + old_writing_direction = writing_direction; + + // Adjust font size on a per word granularity. Pay attention to + // fontsize, old_fontsize, and pdf_str. We've found that for + // in Arabic, Tesseract will happily return a fontsize of zero, + // so we make up a default number to protect ourselves. + { + bool bold, italic, underlined, monospace, serif, smallcaps; + int font_id; + res_it->WordFontAttributes(&bold, &italic, &underlined, &monospace, + &serif, &smallcaps, &fontsize, &font_id); + const int kDefaultFontsize = 8; + if (fontsize <= 0) + fontsize = kDefaultFontsize; + if (fontsize != old_fontsize) + { + char textfont[20]; + snprintf(textfont, sizeof(textfont), "/f-0-0 %d Tf ", fontsize); + pdf_str += textfont; + old_fontsize = fontsize; + } + } + + bool last_word_in_line = res_it->IsAtFinalElement(tesseract::RIL_TEXTLINE, tesseract::RIL_WORD); + bool last_word_in_block = res_it->IsAtFinalElement(tesseract::RIL_BLOCK, tesseract::RIL_WORD); + STRING pdf_word(""); + int pdf_word_len = 0; + do { + const char *grapheme = res_it->GetUTF8Text(tesseract::RIL_SYMBOL); + if (grapheme && grapheme[0] != '\0') { + GenericVector unicodes; + UNICHAR::UTF8ToUnicode(grapheme, &unicodes); + char utf16[20]; + for (int i = 0; i < unicodes.length(); i++) + { + int code = unicodes[i]; + if (CodepointToUtf16be(code, utf16)) + { + pdf_word += utf16; + pdf_word_len++; + } + } + } + delete[]grapheme; + res_it->Next(tesseract::RIL_SYMBOL); + } + while (!res_it->Empty(tesseract::RIL_BLOCK) && !res_it->IsAtBeginningOf(tesseract::RIL_WORD)); + if (word_length > 0 && pdf_word_len > 0 && fontsize > 0) + { + double h_stretch = 2 * prec(100.0 * word_length / (fontsize * pdf_word_len)); + pdf_str.add_str_double("", h_stretch); + pdf_str += " Tz"; // horizontal stretch + pdf_str += " [ <"; + pdf_str += pdf_word; // UTF-16BE representation + pdf_str += "> ] TJ"; // show the text + } + if (last_word_in_line) { + pdf_str += " \n"; + } + if (last_word_in_block) { + pdf_str += "ET\n"; // end the text object + } + } + char *ret = new char[pdf_str.length() + 1]; + strcpy(ret, pdf_str.string()); + delete res_it; + len = pdf_str.length(); + return ret; +} +#endif \ No newline at end of file diff --git a/hgdriver/ImageProcess/hg_ocr.h b/hgdriver/ImageProcess/hg_ocr.h new file mode 100644 index 0000000..b9660b2 --- /dev/null +++ b/hgdriver/ImageProcess/hg_ocr.h @@ -0,0 +1,43 @@ +锘#ifndef HG_OCR_H +#define HG_OCR_H + +class HG_OCR +{ +public: + + enum PSM_TYPE + { + Orientation, + OCR + }; + + enum SAVE_MODE + { + PDF = 0x1, + TXT = 0x2 + }; + +public: + HG_OCR(); + + HG_OCR(PSM_TYPE type); + + ~HG_OCR(); + + void init(PSM_TYPE type); + + void init(const char* filename, PSM_TYPE type); + + int getOrientation(unsigned char* imgData, int width, int height, int channels, int step); + + bool getOrientation(unsigned char* imgData, int width, int height, int channels, int step, + int& orientation, int& direction, int& lineOrder, float& deskewAngle); +private: + + void init_orientation(const char *filename); + +private: + void* api; +}; + +#endif // HG_OCR_H diff --git a/hgdriver/ImageProcess/imgprocdefs.h b/hgdriver/ImageProcess/imgprocdefs.h new file mode 100644 index 0000000..ba5fea9 --- /dev/null +++ b/hgdriver/ImageProcess/imgprocdefs.h @@ -0,0 +1,22 @@ +锘#ifndef IMAGE_PROCESS_DEFINES_H +#define IMAGE_PROCESS_DEFINES_H + +//#if defined(_MSC_VER) +//# define HT_DECL_EXPORT __declspec(dllexport) +//# define HT_DECL_IMPORT __declspec(dllimport) +//#elif defined(__linux__) || defined(__linux) +//# define HT_DECL_EXPORT __attribute__((visibility("default"))) +//# define HT_DECL_IMPORT __attribute__((visibility("default"))) +//# define HT_DECL_HIDDEN __attribute__((visibility("hidden"))) +//#endif +// +//#if defined(GIMGPROC_LIBRARY_BUILD) +//#define HT_DECL_EXPORT +//#elif defined(_DIRECT_BUILD) +//#define +//#else +////#define HT_DECL_IMPORT +//#endif +#define IMAGE_PROCESS_DEFINES_H + +#endif // !IMAGE_PROCESS_DEFINES_H diff --git a/hgdriver/hgdev/BlockingQueue.h b/hgdriver/hgdev/BlockingQueue.h new file mode 100644 index 0000000..c8ce347 --- /dev/null +++ b/hgdriver/hgdev/BlockingQueue.h @@ -0,0 +1,279 @@ +#ifndef BLOCKING_QUEUE_H +#define BLOCKING_QUEUE_H + +//#include +//#include +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include +#include +using namespace std; + +template +class BlockingQueue +{ +private: + BlockingQueue(const BlockingQueue& rhs); + BlockingQueue& operator =(const BlockingQueue& rhs); + mutable std::mutex _mutex; + std::condition_variable _condvar; + deque _queue; + bool isShutDown; + T tRet; + +public: + BlockingQueue() + : _mutex() + , _condvar() + , _queue() + , isShutDown(false) + { + } + + ~BlockingQueue() + { + ShutDown(); + std::cout << "blocking queue release" << std::endl; + } + + void Clear() + { + lock_guard lock(_mutex); + _condvar.notify_all(); + _queue.clear(); + } + + void ShutDown() + { + isShutDown = true; + _condvar.notify_all(); + _queue.clear(); + } + + bool IsShutDown() + { + return isShutDown; + } + + void Put(const T task) + { + lock_guard lock(_mutex); + if (!isShutDown) + { + { + _queue.push_back(task); + } + _condvar.notify_all(); + } + + } + + T Take() + { + unique_lock lock(_mutex); + if (_queue.size() <= 0) + _condvar.wait(lock); + + if (isShutDown || _queue.empty()) + { + return tRet; + } + + T front(_queue.front()); + _queue.pop_front(); + + return front; + } + + T Front() + { + unique_lock lock(_mutex); + if (_queue.size() <= 0) + _condvar.wait(lock); + + if (isShutDown || _queue.empty()) + { + return tRet; + } + + T front(_queue.front()); + return front; + } + + size_t Size() const + { + lock_guard lock(_mutex); + return _queue.size(); + } +}; + +#include +typedef struct _img_header +{ + int width; + int height; + int bits; + int channels; + int line_bytes; + unsigned bytes; +}IMH; +typedef struct _img +{ + IMH header; + unsigned offset; + std::shared_ptr> data; +}IMGDT; +class image_data +{ +private: + image_data(const image_data& rhs); + image_data& operator =(const image_data& rhs); + mutable std::mutex _mutex; + std::condition_variable _condvar; + deque _queue; + bool isShutDown; + IMGDT tRet; + +public: + image_data() + : _mutex() + , _condvar() + , _queue() + , isShutDown(false) + { + } + + ~image_data() + { + ShutDown(); + std::cout << "blocking queue release" << std::endl; + } + + void Clear() + { + lock_guard lock(_mutex); + _condvar.notify_all(); + _queue.clear(); + } + + void ShutDown() + { + isShutDown = true; + _condvar.notify_all(); + _queue.clear(); + } + + bool IsShutDown() + { + return isShutDown; + } + + void Put(const IMGDT task) + { + lock_guard lock(_mutex); + if (!isShutDown) + { + { + _queue.push_back(task); + } + _condvar.notify_all(); + } + + } + void put(int w, int h, int bpp, int channels, int line_bytes, void* data, unsigned bytes) + { + IMGDT img; + img.header.width = w; + img.header.height = h; + img.header.bits = bpp; + img.header.channels = channels; + img.header.line_bytes = line_bytes; + img.offset = 0; + img.header.bytes = bytes; + img.data.reset(new std::vector); + img.data->resize(bytes); + memcpy(img.data->data(), data, bytes); + Put(img); + } + IMGDT Take() + { + unique_lock lock(_mutex); + if (_queue.size() <= 0) + _condvar.wait(lock); + + if (isShutDown || _queue.empty()) + { + return tRet; + } + + IMGDT front(_queue.front()); + _queue.pop_front(); + + return front; + } + + IMGDT Front() + { + unique_lock lock(_mutex); + if (_queue.size() <= 0) + _condvar.wait(lock); + + if (isShutDown || _queue.empty()) + { + return tRet; + } + + IMGDT front(_queue.front()); + return front; + } + bool front(IMH* header) + { + unique_lock lock(_mutex); + if (_queue.size() <= 0) + _condvar.wait(lock); + + if (isShutDown || _queue.empty()) + { + return false; + } + + memcpy(header, &_queue.front().header, sizeof(*header)); + + return true; + } + size_t Size() const + { + lock_guard lock(_mutex); + return _queue.size(); + } + + void fetch_front(void* buf, int* len, bool* over) + { + if (Size() == 0) + *len = 0; + else + { + lock_guard lock(_mutex); + if (_queue.front().data->size() - _queue.front().offset <= *len) + { + *len = _queue.front().data->size() - _queue.front().offset; + memcpy(buf, _queue.front().data->data() + _queue.front().offset, *len); + _queue.pop_front(); + if (over) + *over = true; + } + else + { + memcpy(buf, _queue.front().data->data() + _queue.front().offset, *len); + _queue.front().offset += *len; + if (over) + *over = false; + } + } + } +}; +#endif diff --git a/hgdriver/hgdev/CMakeLists.txt b/hgdriver/hgdev/CMakeLists.txt new file mode 100644 index 0000000..cdc46e2 --- /dev/null +++ b/hgdriver/hgdev/CMakeLists.txt @@ -0,0 +1,34 @@ +project(gdev) +add_compile_options(-std=c++11) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +aux_source_directory(${PROJECT_SOURCE_DIR} DIR_SRCS) +file(GLOB DIR_HEADS "${PROJECT_SOURCE_DIR}/*.h" "${PROJECT_SOURCE_DIR}/*.hpp") +set(DIR_SRCS ${DIR_SRCS} ${DIR_HEADS}) + +add_library(${PROJECT_NAME} STATIC ${DIR_SRCS}) + +target_link_libraries(${PROJECT_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/../../../tmp/libgimgproc.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libopencv_world.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libittnotify.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibjasper.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibjpeg-turbo.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibpng.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibprotobuf.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibtiff.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libquirc.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libzlib.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/liblibwebp.a + ${PROJECT_SOURCE_DIR}/../3rdparty/lib/x86_64/libIlmImf.a +) + + +target_link_libraries(${PROJECT_NAME} PRIVATE usb-1.0) +target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/../3rdparty/nick + ${PROJECT_SOURCE_DIR}/../ImageProcess + ${PROJECT_SOURCE_DIR}/../3rdparty/opencv/include + ${PROJECT_SOURCE_DIR}/../../../sdk/include + ) +set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../../tmp/) diff --git a/hgdriver/hgdev/CMakeLists.txt.user b/hgdriver/hgdev/CMakeLists.txt.user new file mode 100644 index 0000000..9be6794 --- /dev/null +++ b/hgdriver/hgdev/CMakeLists.txt.user @@ -0,0 +1,245 @@ + + + + + + EnvironmentId + {7ffe135c-9a6a-47fd-aa17-30ca7cc38f36} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + 妗岄潰 + 妗岄潰 + {dd7642b7-8b16-4d2a-a3db-a41203cb41bc} + 0 + 0 + 0 + + + CMAKE_BUILD_TYPE:STRING=Debug + + /home/huagao/Desktop/3.22code/sane-driver-rewrite/sane-driver-rewrite/hgdriver/build-hgdev-unknown-Debug + + + + + all + + true + CMake Build + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMake Build + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=Release + + /home/huagao/Desktop/3.22code/sane-driver-rewrite/sane-driver-rewrite/hgdriver/build-hgdev-unknown-Release + + + + + all + + true + CMake Build + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMake Build + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + CMakeProjectManager.CMakeBuildConfiguration + + 2 + + + 0 + 閮ㄧ讲 + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + Custom Executable + + ProjectExplorer.CustomExecutableRunConfiguration + + 3768 + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 20 + + + Version + 20 + + diff --git a/hgdriver/hgdev/IConfig.h b/hgdriver/hgdev/IConfig.h new file mode 100644 index 0000000..c5ded40 --- /dev/null +++ b/hgdriver/hgdev/IConfig.h @@ -0,0 +1,196 @@ +#ifndef ICONFIG_H +#define ICONFIG_H + // !ICONFIG_H +#include "predefine.h" +#include +#include "PaperSize.h" + +#ifndef unix + //typedef char byte; +#endif + +enum G400_PaperSize { + G400_A3 = 0, + G400_A4, + G400_A4R, + G400_A5, + G400_A5R, + G400_A6, + G400_A6R, + G400_B4, + G400_B5, + G400_B5R, + G400_B6, + G400_B6R, + G400_DOUBLELETTER, + G400_LEGAL, + G400_LETTER, + G400_LETTERR, + G400_LONGLETTER, + G400_MAXSIZE +}; + +enum G400_DPI { + G400_D300 = 0, + G400_D200, + G400_D600 +}; + +static std::map SupPixelTypes = { + {0, 0},//bw + {1, 0},//gray + {2, 1}//color +}; + +static std::map SupResolutions = { + {300.0f, 0}, + {200.0f, 1}, + {600.0f, 2} +}; + + +static std::map secrewMaps = { + {1,0}, + {2,1}, + {3,2}, + {4,3}, + {5,4} +}; +typedef union Config_Param { + unsigned int value; + struct { + unsigned int paper : 5; + unsigned int color : 1; + unsigned int dpi : 2; + unsigned int double_feed_enbale : 1; + unsigned int stable_enbale : 1; + unsigned int screw_detect_enable : 1; + unsigned int screw_detect_level : 3; + unsigned int unused_one : 6; + unsigned int pc_correct : 1; + unsigned int unused_two : 11; + }; +} ConfigParam; + +static std::map SupPaperTyps_G100 +{ + {{TwSS::A3,PaperAlign::Rot0},0}, + {{TwSS::A4,PaperAlign::Rot0},1}, + {{TwSS::A4,PaperAlign::Rot270},2}, + {{TwSS::A5,PaperAlign::Rot0 },2}, + {{TwSS::A5,PaperAlign::Rot270},2}, + {{TwSS::A6,PaperAlign::Rot0},2}, + {{TwSS::A6,PaperAlign::Rot270},2}, + {{TwSS::B4,PaperAlign::Rot0},0}, + {{TwSS::B5,PaperAlign::Rot0},0}, + {{TwSS::B5,PaperAlign::Rot270},1}, + {{TwSS::B6,PaperAlign::Rot0},2}, + {{TwSS::B6,PaperAlign::Rot270},2}, + {{TwSS::USLetter,PaperAlign::Rot0},1}, + {{TwSS::USLetter,PaperAlign::Rot270},2}, + {{TwSS::USLedger,PaperAlign::Rot0},0}, + {{TwSS::USLegal,PaperAlign::Rot0},0}, + {{TwSS::None,PaperAlign::Rot0},0}, + {{TwSS::USStatement,PaperAlign::Rot0},16}, + {{TwSS::MaxSize,PaperAlign::Rot0},16} +}; + +static std::map SupPaperTyps_G300 +{ + {{TwSS::A4,PaperAlign::Rot270},G400_A4R}, + {{TwSS::A5,PaperAlign::Rot0 },G400_A5}, + {{TwSS::A5,PaperAlign::Rot270},G400_A5R}, + {{TwSS::A6,PaperAlign::Rot0},G400_A6}, + {{TwSS::A6,PaperAlign::Rot270},G400_A6R}, + {{TwSS::B5,PaperAlign::Rot0},G400_B5}, + {{TwSS::B5,PaperAlign::Rot270},G400_B5R}, + {{TwSS::B6,PaperAlign::Rot0},G400_B6}, + {{TwSS::B6,PaperAlign::Rot270},G400_B6R}, + {{TwSS::USLetter,PaperAlign::Rot0},G400_LETTER}, + {{TwSS::USLetter,PaperAlign::Rot270},G400_LETTERR}, + {{TwSS::USLegal,PaperAlign::Rot0},G400_LEGAL}, + {{TwSS::None,PaperAlign::Rot0},G400_A4}, +}; + +static std::map SupPaperTyps_G400 +{ + {{TwSS::A3,PaperAlign::Rot0},G400_A3}, + {{TwSS::A4,PaperAlign::Rot0},G400_A4}, + {{TwSS::A4,PaperAlign::Rot270},G400_A4R}, + {{TwSS::A5,PaperAlign::Rot0 },G400_A5}, + {{TwSS::A5,PaperAlign::Rot270},G400_A5R}, + {{TwSS::A6,PaperAlign::Rot0},G400_A6}, + {{TwSS::A6,PaperAlign::Rot270},G400_A6R}, + {{TwSS::B4,PaperAlign::Rot0},G400_B4}, + {{TwSS::B5,PaperAlign::Rot0},G400_B5}, + {{TwSS::B5,PaperAlign::Rot270},G400_B5R}, + {{TwSS::B6,PaperAlign::Rot0},G400_B6}, + {{TwSS::B6,PaperAlign::Rot270},G400_B6R}, + {{TwSS::USLetter,PaperAlign::Rot0},G400_LETTER}, + {{TwSS::USLetter,PaperAlign::Rot270},G400_LETTERR}, + {{TwSS::USLedger,PaperAlign::Rot0},G400_DOUBLELETTER}, + {{TwSS::USLegal,PaperAlign::Rot0},G400_LEGAL}, + {{TwSS::None,PaperAlign::Rot0},G400_A3}, + {{TwSS::MaxSize,PaperAlign::Rot0},G400_MAXSIZE}, + {{TwSS::USStatement,PaperAlign::Rot0},G400_MAXSIZE} +}; + + +// static std::map SupPaperTyps = { +// #ifdef G200SCANNER +// {{TwSS::A3,PaperAlign::Rot0},0}, +// {{TwSS::A4,PaperAlign::Rot0},1}, +// {{TwSS::A4,PaperAlign::Rot270},2}, +// {{TwSS::A5,PaperAlign::Rot0 },2}, +// {{TwSS::A5,PaperAlign::Rot270},2}, +// {{TwSS::A6,PaperAlign::Rot0},2}, +// {{TwSS::A6,PaperAlign::Rot270},2}, +// {{TwSS::B4,PaperAlign::Rot0},0}, +// {{TwSS::B5,PaperAlign::Rot0},0}, +// {{TwSS::B5,PaperAlign::Rot270},1}, +// {{TwSS::B6,PaperAlign::Rot0},2}, +// {{TwSS::B6,PaperAlign::Rot270},2}, +// {{TwSS::USLetter,PaperAlign::Rot0},1}, +// {{TwSS::USLetter,PaperAlign::Rot270},2}, +// {{TwSS::USLedger,PaperAlign::Rot0},0}, +// {{TwSS::USLegal,PaperAlign::Rot0},0}, +// {{TwSS::None,PaperAlign::Rot0},0}, +// {{TwSS::USStatement,PaperAlign::Rot0},16}, +// {{TwSS::MaxSize,PaperAlign::Rot0},16} +// #else +// {{TwSS::A3,PaperAlign::Rot0},G400_A3}, +// {{TwSS::A4,PaperAlign::Rot0},G400_A4}, +// {{TwSS::A4,PaperAlign::Rot270},G400_A4R}, +// {{TwSS::A5,PaperAlign::Rot0 },G400_A5}, +// {{TwSS::A5,PaperAlign::Rot270},G400_A5R}, +// {{TwSS::A6,PaperAlign::Rot0},G400_A6}, +// {{TwSS::A6,PaperAlign::Rot270},G400_A6R}, +// {{TwSS::B4,PaperAlign::Rot0},G400_B4}, +// {{TwSS::B5,PaperAlign::Rot0},G400_B5}, +// {{TwSS::B5,PaperAlign::Rot270},G400_B5R}, +// {{TwSS::B6,PaperAlign::Rot0},G400_B6}, +// {{TwSS::B6,PaperAlign::Rot270},G400_B6R}, +// {{TwSS::USLetter,PaperAlign::Rot0},G400_LETTER}, +// {{TwSS::USLetter,PaperAlign::Rot270},G400_LETTERR}, +// {{TwSS::USLedger,PaperAlign::Rot0},G400_DOUBLELETTER}, +// {{TwSS::USLegal,PaperAlign::Rot0},G400_LEGAL}, +// #ifdef G300SCANNER +// {{TwSS::None,PaperAlign::Rot0},G400_A4}, +// #else // G300 +// {{TwSS::None,PaperAlign::Rot0},G400_A3}, +// #endif +// {{TwSS::MaxSize,PaperAlign::Rot0},G400_MAXSIZE}, +// {{TwSS::USStatement,PaperAlign::Rot0},G400_MAXSIZE} +// #endif +// }; + +class IConfig +{ +public: + IConfig(void) {}; + virtual ~IConfig(void) {}; + virtual unsigned int GetData() { return m_param.value; } +protected: + ConfigParam m_param; +}; +#endif diff --git a/hgdriver/hgdev/ImageMatQueue.cpp b/hgdriver/hgdev/ImageMatQueue.cpp new file mode 100644 index 0000000..4bdf70a --- /dev/null +++ b/hgdriver/hgdev/ImageMatQueue.cpp @@ -0,0 +1,57 @@ +锘#include "ImageMatQueue.h" +GRawDecode::GRawDecode(std::shared_ptr> buff) +{ + m_buffs.push_back(buff); +} + +GRawDecode::GRawDecode(void* data, size_t bytes) +{ + std::shared_ptr> buf; + buf.reset(new std::vector); + buf->resize(bytes); + memcpy(buf->data(), data, bytes); + m_buffs.push_back(buf); +} + +G200Decode::G200Decode(std::shared_ptr> buff,bool is_duplex,bool is_switchFrontBack) +{ + const int int_buffer_size = 1024; + int buffer_size = buff->size(); + int b_buffer_size = 0; + int f_buffer_size = 0; + std::shared_ptr> buffB(new std::vector(buff->size())); + std::shared_ptr> buffF(new std::vector(buff->size())); + unsigned char *bbuf = (unsigned char *)(buffB->data()); + unsigned char *fbuf = (unsigned char *)(buffF->data()); + unsigned char *buf = (unsigned char *)(buff->data()); + for (int i = 0; i < (buffer_size / int_buffer_size); i++) + { + if (buf[(i + 1) * int_buffer_size - 1] == 0) + { + memcpy(bbuf + b_buffer_size, buf + i * int_buffer_size, int_buffer_size - 1); + b_buffer_size += (int_buffer_size - 1); + } + else if (buf[(i + 1) * int_buffer_size - 1] == 255) + { + memcpy(fbuf + f_buffer_size, buf + i * int_buffer_size, int_buffer_size - 1); + f_buffer_size += (int_buffer_size - 1); + } + } + buffB->resize(b_buffer_size); + buffF->resize(f_buffer_size); + + if (is_duplex) + { + m_buffs.push_back(is_switchFrontBack ? buffF : buffB); + m_buffs.push_back(is_switchFrontBack ? buffB : buffF); + } + else + m_buffs.push_back(is_switchFrontBack ? buffF : buffB); +} + +G400Decode::G400Decode(std::shared_ptr> buff,bool i) +{ + printf("G400 :buff->size()=%d\r\n",buff->size()); + m_buffs.push_back(buff); +} + diff --git a/hgdriver/hgdev/ImageMatQueue.h b/hgdriver/hgdev/ImageMatQueue.h new file mode 100644 index 0000000..ea37ed1 --- /dev/null +++ b/hgdriver/hgdev/ImageMatQueue.h @@ -0,0 +1,142 @@ +#ifndef IMAGE_MAT_QUEUE_H +#define IMAGE_MAT_QUEUE_H + +#include "../ImageProcess/ImageApplyHeaders.h" +#include "PaperSize.h" + + +#ifdef WIN32 +class IMat2Bmp { +public: + virtual ~IMat2Bmp() {} + virtual std::vector getBmpDataBuffer() { + return m_data; + }; +protected: + std::vector m_data; +}; + +class Mat2Bmp:public IMat2Bmp { +public: + Mat2Bmp(const cv::Mat& mat,float res) { + int headersize = mat.channels() == 3 ? 54 : 1078; + int bmpdatasize = mat.step1() * mat.rows; + m_data.resize(headersize + bmpdatasize); + cv::imencode(".bmp", mat, m_data); + BITMAPINFOHEADER* infoheader =(BITMAPINFOHEADER*)(m_data.data()+sizeof(BITMAPFILEHEADER)); + infoheader->biXPelsPerMeter = infoheader->biYPelsPerMeter = static_cast(res * 39.37F + 0.5); + infoheader->biClrUsed = infoheader->biClrImportant=infoheader->biBitCount == 8 ? 256 : 0; + //short palettesize = sizeof(RGBQUAD) * infoheader->biClrUsed; + //int imagedatasize = BYTES_PERLINE_ALIGN4(mat.cols, infoheader->biBitCount) * mat.rows; + //int bitmapSize = sizeof(BITMAPINFOHEADER) + palettesize + imagedatasize; + //infoheader->biSizeImage = bitmapSize; + } +}; + +class Mat2BmpBw :public IMat2Bmp { +public: + Mat2BmpBw(const cv::Mat& mat,float res) { + //cv::imwrite("bw.bmp", mat); + int headsize = 62; + int width = mat.cols; + int height = mat.rows; + int bmpdatasize = (width + 31) / 32 * 4*height; + m_data.resize(headsize + bmpdatasize); + unsigned char* binary = m_data.data()+62;//file header(14)+info header(40)+RGBQUAD(8) + setBmpFileHeader(mat,bmpdatasize); + setBmpInfoHeader(mat, bmpdatasize, res); + memcpy(m_data.data(), &m_fileHeader, sizeof(BITMAPFILEHEADER)); + memcpy(m_data.data() + sizeof(BITMAPFILEHEADER), &m_infoHeader, sizeof(BITMAPINFOHEADER)); + RGBQUAD* quad = (RGBQUAD*)(m_data.data() + sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)); + quad->rgbBlue = quad->rgbGreen = quad->rgbRed = quad->rgbReserved = 0; + quad++; + quad->rgbBlue = quad->rgbGreen = quad->rgbRed = 255; + quad->rgbReserved = 0; + + unsigned char* imageData = mat.data; + unsigned char temp; + int n_lineByte = (width + 7) >> 3; + int m_lineByte = ((n_lineByte * 8 + 31) >> 5) << 2; + for (int row = height - 1; row >= 0; row--) + { + for (int col = 0; col < width; col++) + { + int pos = col % 8; + int pix = *(imageData + row * mat.step1() + col); + temp = 1 << (7 - pos); + if (pix == 255) + { + *(binary + (height - row - 1) * m_lineByte + col / 8) |= temp ; + } + else + { + *(binary + (height - row - 1) * m_lineByte + col / 8) &= (~temp); + } + } + } + } +private: + void setBmpFileHeader(const cv::Mat& mat,const int bmpDataLen) { + memset(&m_fileHeader, 0, sizeof(BITMAPFILEHEADER)); + m_fileHeader.bfType = ((unsigned short)('M' << 8) | 'B'); + m_fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * 1); + int nDIBSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * 2) + bmpDataLen; + m_fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + nDIBSize; + } + + void setBmpInfoHeader(const cv::Mat& mat,const int bmpDataLen, const long res) { + memset(&m_infoHeader, 0, sizeof(BITMAPINFOHEADER)); + m_infoHeader.biSize = sizeof(BITMAPINFOHEADER); + m_infoHeader.biWidth = mat.cols; + m_infoHeader.biHeight = mat.rows; + m_infoHeader.biPlanes = 1; + m_infoHeader.biBitCount = 1; + m_infoHeader.biCompression = 0; + m_infoHeader.biSizeImage = bmpDataLen; + m_infoHeader.biXPelsPerMeter = m_infoHeader.biYPelsPerMeter = static_cast(res * 39.37F + 0.5);; + m_infoHeader.biClrUsed = 2; + m_infoHeader.biClrImportant = 2; + } + +private: + BITMAPFILEHEADER m_fileHeader; + BITMAPINFOHEADER m_infoHeader; +}; +#endif + +class IDecode +{ +public: + virtual ~IDecode() {} + virtual std::vector>> getImageBuffs() + { + return m_buffs; + } +protected: + std::vector>> m_buffs; +}; + +class G200Decode : public IDecode +{ +public: + G200Decode(std::shared_ptr> buff,bool is_duplex,bool is_switchFrontBack); + //G200Decode(void* buf, unsigned bytes); +}; + +class G400Decode : public IDecode +{ +public: + G400Decode(std::shared_ptr> buff,bool is_duplex); +}; + + +class GRawDecode : public IDecode +{ +public: + GRawDecode(std::shared_ptr> buff); + GRawDecode(void* data, size_t bytes); +}; + + + +#endif diff --git a/hgdriver/hgdev/ImageMultiOutput.cpp b/hgdriver/hgdev/ImageMultiOutput.cpp new file mode 100644 index 0000000..bfc544b --- /dev/null +++ b/hgdriver/hgdev/ImageMultiOutput.cpp @@ -0,0 +1,59 @@ +#include "ImageMultiOutput.h" + + +ImageMultiOutput::ImageMultiOutput(void) +{ +} + + +ImageMultiOutput::~ImageMultiOutput(void) +{ +} + +//void ImageMultiOutput::apply(cv::Mat& pDib,int side) +//{ +// //throw std::logic_error("The method or operation is not implemented."); +//} + +cv::Mat ImageMultiOutput::GetMultiFilterMat(cv::Mat &src,int channel) +{ + return FilterColor(src,channel); +} + +cv::Mat ImageMultiOutput::FilterColor(cv::Mat image,short channel) +{ + cv::Mat dstImage(image.rows,image.cols,CV_8UC1); + + //int pixelSize = image.depth(); + int channels = image.channels(); + if(channel > channels -1){ + return dstImage; + } + if ( ( channel == 3 ) && ( channels != 4 ) && ( channels != 8 )) + { + return dstImage; + } + if ( channels <= 4 ) + { + int srcOffset = image.step - image.cols* channels ; + int dstOffset = dstImage.step - dstImage.cols; + unsigned char* src = image.data; + unsigned char* dst = dstImage.data; + src += channel; + + for ( int y = 0; y < image.rows; y++ ) + { + for ( int x = 0; x < image.cols; x++, src += channels , dst++ ) + { + unsigned short pix = *src; + if(pix >=130){ + pix = 255; + } + *dst = pix; + } + src += srcOffset; + dst += dstOffset; + } + } + return dstImage; +} diff --git a/hgdriver/hgdev/ImageMultiOutput.h b/hgdriver/hgdev/ImageMultiOutput.h new file mode 100644 index 0000000..bed76d7 --- /dev/null +++ b/hgdriver/hgdev/ImageMultiOutput.h @@ -0,0 +1,18 @@ +#ifndef IMAGE_MULTI_OUTPUT +#define IMAGE_MULTI_OUTPUT + +#include "../ImageProcess/ImageApply.h" + +class ImageMultiOutput +{ +public: + ImageMultiOutput(void); + ~ImageMultiOutput(void); + + cv::Mat GetMultiFilterMat(cv::Mat &src,int channel); +private: + cv::Mat FilterColor(cv::Mat image,short channel); + +}; + +#endif diff --git a/hgdriver/hgdev/PaperSize.cpp b/hgdriver/hgdev/PaperSize.cpp new file mode 100644 index 0000000..8cf4a87 --- /dev/null +++ b/hgdriver/hgdev/PaperSize.cpp @@ -0,0 +1,73 @@ +#include "PaperSize.h" + +using namespace std; + +namespace Device { + + PaperSize::PaperSize() + { + InitPaperMap(); + } + + + PaperSize::~PaperSize() + { + } + + void PaperSize::InitPaperMap() + { + papersize.insert({ A3,SIZE{297,420} }); + papersize.insert({ A4,SIZE{210,297} }); + papersize.insert({ A5,SIZE{148,210} }); + papersize.insert({ A6,SIZE{105,148} }); + papersize.insert({ B4,SIZE{250,353} }); + papersize.insert({ B5,SIZE{176,250} }); + papersize.insert({ B6,SIZE{125,176} }); +#ifdef G400 + papersize.insert({ MaxSize,SIZE{297,420 * 1.5} }); +#else + papersize.insert({ MaxSize,SIZE{297,420 * 2} }); +#endif + papersize.insert({ USStatement,SIZE{297,long(420 * 1.5)} }); + papersize.insert({ USLetter,SIZE{216,279} }); + papersize.insert({ USLegal,SIZE{216,356} }); + papersize.insert({ USLedger,SIZE{279,432} }); + papersize.insert({ None,SIZE{297,420} }); + papersize.insert({ K8,SIZE{270,390} }); + papersize.insert({ K16,SIZE{190,270} }); + papersize.insert({ Trigeminy,SIZE{270,560} }); + } + + SIZE PaperSize::GetPaperSize(DWORD paperType, float dpi,int orentation) + { + + if (papersize.find((TwSS)paperType) != papersize.end() && (dpi > 99 && dpi < 601)) { + SIZE resize{2338,3307}; + + if (orentation == 0){ + resize.cx = papersize[(TwSS)paperType].cx * dpi / 25.4; + resize.cy = papersize[(TwSS)paperType].cy * dpi / 25.4; + return resize; + } + else{ + resize.cy = papersize[(TwSS)paperType].cx * dpi / 25.4; + resize.cx = papersize[(TwSS)paperType].cy * dpi / 25.4; + return resize; + } + } + return SIZE{2338, 3307}; + //auto iter = dpiDct.find({(TwSS)paperType, dpi}); + //if (iter != dpiDct.end()) { + // if (orentation == 0) + // return iter->second; + // else if(orentation == 3) + // { + // SIZE size; + // size.cx = iter->second.cy; + // size.cy = iter->second.cx; + // return size; + // } + //} + //return SIZE(2338, 3307); + } +} \ No newline at end of file diff --git a/hgdriver/hgdev/PaperSize.h b/hgdriver/hgdev/PaperSize.h new file mode 100644 index 0000000..9a0bc20 --- /dev/null +++ b/hgdriver/hgdev/PaperSize.h @@ -0,0 +1,48 @@ +#ifndef PAPER_SIZE_H +#define PAPER_SIZE_H + +#include + +#ifdef WIN32 +#include +#endif +#include "common_setting.h" + +#ifdef __linux__ +typedef CSSIZE Size; +typedef CSSIZE SIZE; +typedef unsigned long DWORD; +typedef long LONG; +#endif + +typedef struct Paper_Status { + unsigned int Paper; + unsigned int Orentate; + friend bool operator<(const struct Paper_Status& a, const struct Paper_Status& b) { + if (a.Paper < b.Paper || + (a.Paper == b.Paper && a.Orentate < b.Orentate)) { + return true; + } + return false; + } +}PaperStatus; + +namespace Device { + class PaperSize + { + public: + PaperSize(); + ~PaperSize(); + private: + void InitPaperMap(); + std::map papersize; + std::map, SIZE> dpiDct; + std::map dpiDct_100; + std::map dpiDct_400; + std::map dpiDct_300; + public: + SIZE GetPaperSize(DWORD paperType, float dpi, int orentation); + }; +} + +#endif diff --git a/hgdriver/hgdev/char_const.h b/hgdriver/hgdev/char_const.h new file mode 100644 index 0000000..04773f9 --- /dev/null +++ b/hgdriver/hgdev/char_const.h @@ -0,0 +1,215 @@ +锘#pragma once + +// char_const is used to define the wide-char consts .... +// +// created on 2022-04-15 +// + + +// 棰滆壊妯″紡 閫夐」 +// #define HUAGAO_SETTING_STR_COLOR_MODE_BLACK_WHITE "榛戠櫧" +#define HUAGAO_SETTING_STR_COLOR_MODE_BLACK_WHITE "\351\273\221\347\231\275" +// #define HUAGAO_SETTING_STR_COLOR_MODE_256_GRAY "256绾х伆搴" +#define HUAGAO_SETTING_STR_COLOR_MODE_256_GRAY "256\347\272\247\347\201\260\345\272\246" +// #define HUAGAO_SETTING_STR_COLOR_MODE_24_BITS "24浣嶅僵鑹" +#define HUAGAO_SETTING_STR_COLOR_MODE_24_BITS "24\344\275\215\345\275\251\350\211\262" +// #define HUAGAO_SETTING_STR_COLOR_MODE_AUTO_MATCH "棰滆壊鑷姩璇嗗埆" +#define HUAGAO_SETTING_STR_COLOR_MODE_AUTO_MATCH "\351\242\234\350\211\262\350\207\252\345\212\250\350\257\206\345\210\253" + +// 澶氭祦杈撳嚭 閫夐」 +// #define HUAGAO_SETTING_STR_MULTI_OUT_NOT "涓嶉夋嫨杈撳嚭妯″紡" +#define HUAGAO_SETTING_STR_MULTI_OUT_NOT "\344\270\215\351\200\211\346\213\251\350\276\223\345\207\272\346\250\241\345\274\217" +// #define HUAGAO_SETTING_STR_MULTI_OUT_ALL "褰╄壊+鐏板害+榛戠櫧" +#define HUAGAO_SETTING_STR_MULTI_OUT_ALL "\345\275\251\350\211\262+\347\201\260\345\272\246+\351\273\221\347\231\275" +// #define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_GRAY "褰╄壊+鐏板害" +#define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_GRAY "\345\275\251\350\211\262+\347\201\260\345\272\246" +// #define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_BW "褰╄壊+榛戠櫧" +#define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_BW "\345\275\251\350\211\262+\351\273\221\347\231\275" +// #define HUAGAO_SETTING_STR_MULTI_GRAY_ADN_BW "鐏板害+榛戠櫧" +#define HUAGAO_SETTING_STR_MULTI_GRAY_ADN_BW "\347\201\260\345\272\246+\351\273\221\347\231\275" + +// 闄よ壊 閫夐」 +// #define HUAGAO_SETTING_STR_RID_COLOR_NONE "涓嶉櫎鑹" +#define HUAGAO_SETTING_STR_RID_COLOR_NONE "\344\270\215\351\231\244\350\211\262" +// #define HUAGAO_SETTING_STR_RID_COLOR_RID_RED "闄ょ孩鑹" +#define HUAGAO_SETTING_STR_RID_COLOR_RID_RED "\351\231\244\347\272\242\350\211\262" +// #define HUAGAO_SETTING_STR_RID_COLOR_RID_GREEN "闄ょ豢鑹" +#define HUAGAO_SETTING_STR_RID_COLOR_RID_GREEN "\351\231\244\347\273\277\350\211\262" +// #define HUAGAO_SETTING_STR_RID_COLOR_RID_BLUE "闄よ摑鑹" +#define HUAGAO_SETTING_STR_RID_COLOR_RID_BLUE "\351\231\244\350\223\235\350\211\262" +// #define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_RED "绾㈣壊澧炲己" +#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_RED "\347\272\242\350\211\262\345\242\236\345\274\272" +// #define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_GREEN "缁胯壊澧炲己" +#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_GREEN "\347\273\277\350\211\262\345\242\236\345\274\272" +// #define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_BLUE "钃濊壊澧炲己" +#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_BLUE "\350\223\235\350\211\262\345\242\236\345\274\272" + +// 绾稿紶 閫夐」 +#define HUAGAO_SETTING_STR_PAPER_A3 "A3" +#define HUAGAO_SETTING_STR_PAPER_A4 "A4" +#define HUAGAO_SETTING_STR_PAPER_A5 "A5" +#define HUAGAO_SETTING_STR_PAPER_A6 "A6" +#define HUAGAO_SETTING_STR_PAPER_B4 "B4" +#define HUAGAO_SETTING_STR_PAPER_B5 "B5" +#define HUAGAO_SETTING_STR_PAPER_B6 "B6" +// #define HUAGAO_SETTING_STR_PAPER_8K "8寮" +#define HUAGAO_SETTING_STR_PAPER_8K "8\345\274\200" +// #define HUAGAO_SETTING_STR_PAPER_16K "16寮" +#define HUAGAO_SETTING_STR_PAPER_16K "16\345\274\200" +#define HUAGAO_SETTING_STR_PAPER_LETTER "Letter" +// #define HUAGAO_SETTING_STR_PAPER_A4_LATERAL "A4妯悜" +#define HUAGAO_SETTING_STR_PAPER_A4_LATERAL "A4\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_A5_LATERAL "A5妯悜" +#define HUAGAO_SETTING_STR_PAPER_A5_LATERAL "A5\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_A6_LATERAL "A6妯悜" +#define HUAGAO_SETTING_STR_PAPER_A6_LATERAL "A6\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_B5_LATERAL "B5妯悜" +#define HUAGAO_SETTING_STR_PAPER_B5_LATERAL "B5\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_B6_LATERAL "B6妯悜" +#define HUAGAO_SETTING_STR_PAPER_B6_LATERAL "B6\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_16K_LATERAL "16寮妯悜" +#define HUAGAO_SETTING_STR_PAPER_16K_LATERAL "16\345\274\200\346\250\252\345\220\221" +// #define HUAGAO_SETTING_STR_PAPER_LETTER_LATERAL "Letter妯悜" +#define HUAGAO_SETTING_STR_PAPER_LETTER_LATERAL "Letter\346\250\252\345\220\221" +#define HUAGAO_SETTING_STR_PAPER_DOUBLE_LETTER "Double Letter" +#define HUAGAO_SETTING_STR_PAPER_LEGAL "LEGAL" +// #define HUAGAO_SETTING_STR_PAPER_AUTO_MATCH "鍖归厤鍘熷灏哄" +#define HUAGAO_SETTING_STR_PAPER_AUTO_MATCH "\345\214\271\351\205\215\345\216\237\345\247\213\345\260\272\345\257\270" +// #define HUAGAO_SETTING_STR_PAPER_MAX_SIZE "鏈澶ф壂鎻忓昂瀵" +#define HUAGAO_SETTING_STR_PAPER_MAX_SIZE "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270" +// #define HUAGAO_SETTING_STR_PAPER_MAX_SIZE_CLIP "鏈澶ф壂鎻忓昂瀵歌嚜鍔ㄨ鍒" +#define HUAGAO_SETTING_STR_PAPER_MAX_SIZE_CLIP "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270\350\207\252\345\212\250\350\243\201\345\210\207" +// #define HUAGAO_SETTING_STR_PAPER_TRIGEMINY "涓夎仈璇曞嵎" +#define HUAGAO_SETTING_STR_PAPER_TRIGEMINY "\344\270\211\350\201\224\350\257\225\345\215\267" + +// 椤甸潰鎵弿妯″紡 閫夐」 +// #define HUAGAO_SETTING_STR_PAGE_SINGLE "鍗曢潰" +#define HUAGAO_SETTING_STR_PAGE_SINGLE "\345\215\225\351\235\242" +// #define HUAGAO_SETTING_STR_PAGE_DOUBLE "鍙岄潰" +#define HUAGAO_SETTING_STR_PAGE_DOUBLE "\345\217\214\351\235\242" +// #define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY "璺宠繃绌虹櫧椤碉紙閫氱敤锛" +#define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\351\200\232\347\224\250\357\274\211" +// #define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY_RECEIPT "璺宠繃绌虹櫧椤碉紙鍙戠エ绾革級" +#define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY_RECEIPT "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\345\217\221\347\245\250\347\272\270\357\274\211" +// #define HUAGAO_SETTING_STR_PAGE_FOLIO "瀵规姌" +#define HUAGAO_SETTING_STR_PAGE_FOLIO "\345\257\271\346\212\230" + +// 閿愬寲 閫夐」 +// #define HUAGAO_SETTING_STR_SHARPEN_NONE "鏃" +#define HUAGAO_SETTING_STR_SHARPEN_NONE "\346\227\240" +// #define HUAGAO_SETTING_STR_SHARPEN_SHARPEN "閿愬寲" +#define HUAGAO_SETTING_STR_SHARPEN_SHARPEN "\351\224\220\345\214\226" +// #define HUAGAO_SETTING_STR_SHARPEN_SHARPEN_MORE "杩涗竴姝ラ攼鍖" +#define HUAGAO_SETTING_STR_SHARPEN_SHARPEN_MORE "\350\277\233\344\270\200\346\255\245\351\224\220\345\214\226" +// #define HUAGAO_SETTING_STR_SHARPEN_HAZY "妯$硦" +#define HUAGAO_SETTING_STR_SHARPEN_HAZY "\346\250\241\347\263\212" +// #define HUAGAO_SETTING_STR_SHARPEN_HAZY_MORE "杩涗竴姝ユā绯" +#define HUAGAO_SETTING_STR_SHARPEN_HAZY_MORE "\350\277\233\344\270\200\346\255\245\346\250\241\347\263\212" + +//鑳屾櫙濉厖鏂瑰紡 閫夐」 +// #define HUAGAO_SETTING_STR_FILL_BKG_CONVEX_POLYGON "鍑稿杈瑰舰" +#define HUAGAO_SETTING_STR_FILL_BKG_CONVEX_POLYGON "\345\207\270\345\244\232\350\276\271\345\275\242" +// #define HUAGAO_SETTING_STR_FILL_BKG_CONCAVE_POLYGON "鍑瑰杈瑰舰" +#define HUAGAO_SETTING_STR_FILL_BKG_CONCAVE_POLYGON "\345\207\271\345\244\232\350\276\271\345\275\242" + +// 鎵弿鏁伴噺 閫夐」 +// #define HUAGAO_SETTING_STR_SCAN_MODE_CONTINUOUS "杩炵画鎵弿" +#define HUAGAO_SETTING_STR_SCAN_MODE_CONTINUOUS "\350\277\236\347\273\255\346\211\253\346\217\217" +// #define HUAGAO_SETTING_STR_SCAN_MODE_GIVEN_COUNT "鎵弿鎸囧畾寮犳暟" +#define HUAGAO_SETTING_STR_SCAN_MODE_GIVEN_COUNT "\346\211\253\346\217\217\346\214\207\345\256\232\345\274\240\346\225\260" + +// 鏂囩ǹ鏂瑰悜 閫夐」 +// #define HUAGAO_SETTING_STR_TEXT_DIRECTION_0 "0掳" +#define HUAGAO_SETTING_STR_TEXT_DIRECTION_0 "0\302\260" +// #define HUAGAO_SETTING_STR_TEXT_DIRECTION_90 "90掳" +#define HUAGAO_SETTING_STR_TEXT_DIRECTION_90 "90\302\260" +// #define HUAGAO_SETTING_STR_TEXT_DIRECTION_180 "180掳" +#define HUAGAO_SETTING_STR_TEXT_DIRECTION_180 "180\302\260" +// #define HUAGAO_SETTING_STR_TEXT_DIRECTION_270 "-90掳" +#define HUAGAO_SETTING_STR_TEXT_DIRECTION_270 "-90\302\260" +// #define HUAGAO_SETTING_STR_TEXT_DIRECTION_AUTO "鑷姩鏂囨湰鏂瑰悜璇嗗埆掳" +#define HUAGAO_SETTING_STR_TEXT_DIRECTION_AUTO "\350\207\252\345\212\250\346\226\207\346\234\254\346\226\271\345\220\221\350\257\206\345\210\253\302\260" + +// 娓楅忔娴嬪己搴 +// #define HUAGAO_SETTING_STR_PERMAEATE_WEAKER "杈冨急" +#define HUAGAO_SETTING_STR_PERMAEATE_WEAKER "\350\276\203\345\274\261" +// #define HUAGAO_SETTING_STR_PERMAEATE_WEAK "寮" +#define HUAGAO_SETTING_STR_PERMAEATE_WEAK "\345\274\261" +// #define HUAGAO_SETTING_STR_PERMAEATE_ORDINARY "涓鑸" +#define HUAGAO_SETTING_STR_PERMAEATE_ORDINARY "\344\270\200\350\210\254" +// #define HUAGAO_SETTING_STR_PERMAEATE_STRONG "寮" +#define HUAGAO_SETTING_STR_PERMAEATE_STRONG "\345\274\272" +// #define HUAGAO_SETTING_STR_PERMAEATE_STRONGER "杈冨己" +#define HUAGAO_SETTING_STR_PERMAEATE_STRONGER "\350\276\203\345\274\272" + +// 鍏徃鍚嶇О & 淇℃伅 +// #define COMPANY_NAME "瀹佹尝鍗庨珮淇℃伅绉戞妧鏈夐檺鍏徃" +#define COMPANY_NAME "\345\256\201\346\263\242\345\215\216\351\253\230\344\277\241\346\201\257\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270" +// #define BRAND_APP_NAME "鍗庨珮鎵弿搴旂敤绋嬪簭" +#define BRAND_APP_NAME "\345\215\216\351\253\230\346\211\253\346\217\217\345\272\224\347\224\250\347\250\213\345\272\217" +// #define BRAND_TITLE_VERSION "鐗堟湰" +#define BRAND_TITLE_VERSION "\347\211\210\346\234\254" +// #define BRAND_TITLE_COPYRIGHT "鐗堟潈" +#define BRAND_TITLE_COPYRIGHT "\347\211\210\346\235\203" +#define BRAND_COPYRIGHT "2022 - 2025 HuaGoScan" +// #define BRAND_TITE_MANUFACTOR "鍒堕犲晢/寮鍙戝晢" +#define BRAND_TITE_MANUFACTOR "\345\210\266\351\200\240\345\225\206/\345\274\200\345\217\221\345\225\206" +// #define BRAND_TITLE_URL "鍏徃缃戝潃" +#define BRAND_TITLE_URL "\345\205\254\345\217\270\347\275\221\345\235\200" +#define BRAND_COMPANY_URL "www.huagaochina.com" +#define BRAND_URL_COMPANY_URL "https://www.huagaochina.com/" +// #define BRAND_TITLE_TEL "鑱旂郴鐢佃瘽" +#define BRAND_TITLE_TEL "\350\201\224\347\263\273\347\224\265\350\257\235" +#define BRAND_COMPANY_TEL "+86 0574 27974866" +// #define BRAND_TITLE_ADDRESS "鍦板潃" +#define BRAND_TITLE_ADDRESS "\345\234\260\345\235\200" +// #define BRAND_COMPANY_ADDRESS "娴欐睙鐪佸畞娉㈠競閯炲窞鍖哄澹矾655鍙凤紙淇$澶у帵锛塁鏍211" +#define BRAND_COMPANY_ADDRESS "\346\265\231\346\261\237\347\234\201\345\256\201\346\263\242\345\270\202\351\204\236\345\267\236\345\214\272\345\255\246\345\243\253\350\267\257655\345\217\267\357\274\210\344\277\241\347\247\221\345\244\247\345\216\246\357\274\211C\346\240\213211" +// #define BRAND_TITLE_GPS "瀵艰埅鍦板潃" +#define BRAND_TITLE_GPS "\345\257\274\350\210\252\345\234\260\345\235\200" +// #define BRAND_COMPANY_GPS "鐧惧害鍦板浘璁块棶" +#define BRAND_COMPANY_GPS "\347\231\276\345\272\246\345\234\260\345\233\276\350\256\277\351\227\256" +#define BRAND_URL_GPS "https://j.map.baidu.com/7e/1TO" +// #define BRAND_TITLE_FIRM_VERSION "鍥轰欢鐗堟湰" +#define BRAND_TITLE_FIRM_VERSION "\345\233\272\344\273\266\347\211\210\346\234\254" +// #define BRAND_TITLE_SERIAL_NUM "搴忓垪鍙" +#define BRAND_TITLE_SERIAL_NUM "\345\272\217\345\210\227\345\217\267" +// #define BRAND_TITLE_ROLLER_COUNT "婊氳酱寮犳暟" +#define BRAND_TITLE_ROLLER_COUNT "\346\273\232\350\275\264\345\274\240\346\225\260" +// #define BRAND_TITLE_IP "璁惧IP" +#define BRAND_TITLE_IP "\350\256\276\345\244\207IP" + +// 璁惧鍚嶇О +// #define SCANNER_NAME_HG_G100 "鍗庨珮鎵弿浠擥100" +#define SCANNER_NAME_HG_G100 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G100" +// #define SCANNER_NAME_HG_G200 "鍗庨珮鎵弿浠擥200" +#define SCANNER_NAME_HG_G200 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G200" +// #define SCANNER_NAME_HG_G300 "鍗庨珮鎵弿浠擥300" +#define SCANNER_NAME_HG_G300 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G300" +// #define SCANNER_NAME_HG_G400 "鍗庨珮鎵弿浠擥400" +#define SCANNER_NAME_HG_G400 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G400" +// #define SCANNER_NAME_HG_G139 "鍗庨珮鎵弿浠擥139" +#define SCANNER_NAME_HG_G139 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G139" +// #define SCANNER_NAME_HG_G239 "鍗庨珮鎵弿浠擥239" +#define SCANNER_NAME_HG_G239 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G239" +// #define SCANNER_NAME_HG_G339 "鍗庨珮鎵弿浠擥339" +#define SCANNER_NAME_HG_G339 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G339" +// #define SCANNER_NAME_HG_G439 "鍗庨珮鎵弿浠擥439" +#define SCANNER_NAME_HG_G439 "\345\215\216\351\253\230\346\211\253\346\217\217\344\273\252\342\200\224G439" + +// #define SCANNER_NAME_LSC_G42S "绔嬫濊景鎵弿浠擥42S" +#define SCANNER_NAME_LSC_G42S "\347\253\213\346\200\235\350\276\260\346\211\253\346\217\217\344\273\252\342\200\224G42S" +// #define SCANNER_NAME_LSC_G52S "绔嬫濊景鎵弿浠擥52S" +#define SCANNER_NAME_LSC_G52S "\347\253\213\346\200\235\350\276\260\346\211\253\346\217\217\344\273\252\342\200\224G52S" +// #define SCANNER_NAME_LSC_G62S "绔嬫濊景鎵弿浠擥62S" +#define SCANNER_NAME_LSC_G62S "\347\253\213\346\200\235\350\276\260\346\211\253\346\217\217\344\273\252\342\200\224G62S" +// #define SCANNER_NAME_LSC_G73S "绔嬫濊景鎵弿浠擥73S" +#define SCANNER_NAME_LSC_G73S "\347\253\213\346\200\235\350\276\260\346\211\253\346\217\217\344\273\252\342\200\224G73S" + +// #define SCANNER_NAME_HW_7000 "姹夌帇鎵弿浠擧W7000" +#define SCANNER_NAME_HW_7000 "\346\261\211\347\216\213\346\211\253\346\217\217\344\273\252\342\200\224HW7000" +// #define SCANNER_NAME_HW_8190F "姹夌帇鎵弿浠擧W8190F" +#define SCANNER_NAME_HW_8190F "\346\261\211\347\216\213\346\211\253\346\217\217\344\273\252\342\200\224HW8190F" +// #define SCANNER_NAME_HW_9110F "姹夌帇鎵弿浠擧W9110F" +#define SCANNER_NAME_HW_9110F "\346\261\211\347\216\213\346\211\253\346\217\217\344\273\252\342\200\224HW9110F" + diff --git a/hgdriver/hgdev/common_setting.cpp b/hgdriver/hgdev/common_setting.cpp new file mode 100644 index 0000000..428bad3 --- /dev/null +++ b/hgdriver/hgdev/common_setting.cpp @@ -0,0 +1,305 @@ +锘#include "common_setting.h" + +static struct _fixed_option +{ + std::string str; + int enum_val; +} + +g_color_mode[] = + { + MAKE_STR_AND_ENUM(COLOR_MODE_BLACK_WHITE), + MAKE_STR_AND_ENUM(COLOR_MODE_256_GRAY), + MAKE_STR_AND_ENUM(COLOR_MODE_24_BITS), + MAKE_STR_AND_ENUM(COLOR_MODE_AUTO_MATCH) + }, +g_multi_out[] = + { + MAKE_STR_AND_ENUM(MULTI_OUT_NOT), + MAKE_STR_AND_ENUM(MULTI_OUT_ALL), + MAKE_STR_AND_ENUM(MULTI_COLOR_ADN_GRAY), + MAKE_STR_AND_ENUM(MULTI_COLOR_ADN_BW), + MAKE_STR_AND_ENUM(MULTI_GRAY_ADN_BW) + }, +g_rid_color[] = + { + MAKE_STR_AND_ENUM(RID_COLOR_NONE), + MAKE_STR_AND_ENUM(RID_COLOR_RID_RED), + MAKE_STR_AND_ENUM(RID_COLOR_RID_GREEN), + MAKE_STR_AND_ENUM(RID_COLOR_RID_BLUE), + MAKE_STR_AND_ENUM(RID_COLOR_ENHANCE_RED), + MAKE_STR_AND_ENUM(RID_COLOR_ENHANCE_GREEN), + MAKE_STR_AND_ENUM(RID_COLOR_ENHANCE_BLUE) + }, + +g_paper[] = + { + MAKE_STR_AND_ENUM(PAPER_A3), + MAKE_STR_AND_ENUM(PAPER_A4), + MAKE_STR_AND_ENUM(PAPER_A5), + MAKE_STR_AND_ENUM(PAPER_A6), + MAKE_STR_AND_ENUM(PAPER_B4), + MAKE_STR_AND_ENUM(PAPER_B5), + MAKE_STR_AND_ENUM(PAPER_B6), + MAKE_STR_AND_ENUM(PAPER_8K), + MAKE_STR_AND_ENUM(PAPER_16K), + MAKE_STR_AND_ENUM(PAPER_LETTER), + MAKE_STR_AND_ENUM(PAPER_A4_LATERAL), + MAKE_STR_AND_ENUM(PAPER_A5_LATERAL), + MAKE_STR_AND_ENUM(PAPER_A6_LATERAL), + MAKE_STR_AND_ENUM(PAPER_B5_LATERAL), + MAKE_STR_AND_ENUM(PAPER_B6_LATERAL), + MAKE_STR_AND_ENUM(PAPER_16K_LATERAL), + MAKE_STR_AND_ENUM(PAPER_LETTER_LATERAL), + MAKE_STR_AND_ENUM(PAPER_DOUBLE_LETTER), + MAKE_STR_AND_ENUM(PAPER_LEGAL), + MAKE_STR_AND_ENUM(PAPER_AUTO_MATCH), + MAKE_STR_AND_ENUM(PAPER_MAX_SIZE), + MAKE_STR_AND_ENUM(PAPER_MAX_SIZE_CLIP), + MAKE_STR_AND_ENUM(PAPER_TRIGEMINY) + }, + +g_page[] = + { + MAKE_STR_AND_ENUM(PAGE_SINGLE), + MAKE_STR_AND_ENUM(PAGE_DOUBLE), + MAKE_STR_AND_ENUM(PAGE_OMIT_EMPTY), + MAKE_STR_AND_ENUM(PAGE_OMIT_EMPTY_RECEIPT), + MAKE_STR_AND_ENUM(PAGE_FOLIO) + }, + +g_sharpen[] = + { + MAKE_STR_AND_ENUM(SHARPEN_NONE), + MAKE_STR_AND_ENUM(SHARPEN_SHARPEN), + MAKE_STR_AND_ENUM(SHARPEN_SHARPEN_MORE), + MAKE_STR_AND_ENUM(SHARPEN_HAZY), + MAKE_STR_AND_ENUM(SHARPEN_HAZY_MORE) + }, + +g_fill_bkg[] = + { + MAKE_STR_AND_ENUM(FILL_BKG_CONVEX_POLYGON), + MAKE_STR_AND_ENUM(FILL_BKG_CONCAVE_POLYGON) + }, + +g_scan_mode[] = + { + MAKE_STR_AND_ENUM(SCAN_MODE_CONTINUOUS), + MAKE_STR_AND_ENUM(SCAN_MODE_GIVEN_COUNT) + }, + +g_text_direction[] = + { + MAKE_STR_AND_ENUM(TEXT_DIRECTION_0), + MAKE_STR_AND_ENUM(TEXT_DIRECTION_90), + MAKE_STR_AND_ENUM(TEXT_DIRECTION_180), + MAKE_STR_AND_ENUM(TEXT_DIRECTION_270), + MAKE_STR_AND_ENUM(TEXT_DIRECTION_AUTO) + }, + +g_permeate_lv[] = + { + MAKE_STR_AND_ENUM(PERMAEATE_WEAKER), + MAKE_STR_AND_ENUM(PERMAEATE_WEAK), + MAKE_STR_AND_ENUM(PERMAEATE_ORDINARY), + MAKE_STR_AND_ENUM(PERMAEATE_STRONG), + MAKE_STR_AND_ENUM(PERMAEATE_STRONGER) + }; + +static int match_best(struct _fixed_option* arr, size_t num, std::string& val, bool& exact) +{ + exact = true; + for (size_t i = 0; i < num; ++i) + { + if (arr[i].str == val) + return arr[i].enum_val; + } + + exact = false; + + return -1; +} +static std::string get_str(struct _fixed_option* arr, int num, int enm, int default_v = 0) +{ + if (enm >= 0 && enm < num) + return arr[enm].str; + else + return arr[default_v].str; +} + +#define INVOKE_MATCH(arr, val, yes) match_best(arr, ARRAY_SIZE(arr), val, yes) +#define INVOKE_STR(arr, val, def) get_str(arr, ARRAY_SIZE(arr), val, def) +#define SET_DEFAULT_ON_FAIL(ind, val, def) \ + if(ind == -1) \ + { \ + ind = def; \ + val = STR_SETTING(def); \ + } + +int match_best_color_mode(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_color_mode, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, COLOR_MODE_24_BITS); + if (exact) + *exact = yes; + + return ind; +} +int match_best_multi_out(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_multi_out, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, MULTI_OUT_NOT); + if (exact) + *exact = yes; + + return ind; + +} +int match_best_rid_color(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_rid_color, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, RID_COLOR_NONE); + if (exact) + *exact = yes; + + return ind; +} +int match_best_paper(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_paper, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, PAPER_A4); + if (exact) + *exact = yes; + + return ind; +} +int match_best_page(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_page, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, PAGE_DOUBLE); + if (exact) + *exact = yes; + + return ind; +} +int match_best_sharpen(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_sharpen, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, SHARPEN_NONE); + if (exact) + *exact = yes; + + return ind; +} +int match_best_fill_background(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_fill_bkg, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, FILL_BKG_CONVEX_POLYGON); + if (exact) + *exact = yes; + + return ind; +} +int match_best_scan_mode(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_scan_mode, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, SCAN_MODE_CONTINUOUS); + if (exact) + *exact = yes; + + return ind; +} +int match_best_text_direction(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_text_direction, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, TEXT_DIRECTION_0); + if (exact) + *exact = yes; + + return ind; +} + +int match_best_permaeate_lv(std::string& val, bool* exact) +{ + bool yes = true; + int ind = INVOKE_MATCH(g_permeate_lv, val, yes); + + SET_DEFAULT_ON_FAIL(ind, val, TEXT_DIRECTION_0); + if (exact) + *exact = yes; + + return ind; +} + + +std::string color_mode_string(int clr_mode) +{ + return INVOKE_STR(g_color_mode, clr_mode, 0); +} +std::string multi_out_string(int multi_out) +{ + return INVOKE_STR(g_multi_out, multi_out, 0); +} +std::string rid_color_string(int rid_color) +{ + return INVOKE_STR(g_rid_color, rid_color, 0); +} +std::string paper_string(int paper) +{ + return INVOKE_STR(g_paper, paper, 1); +} +bool is_lateral(int paper) +{ + return paper == PAPER_A4_LATERAL + || paper == PAPER_A5_LATERAL + || paper == PAPER_A6_LATERAL + || paper == PAPER_B5_LATERAL + || paper == PAPER_B6_LATERAL + || paper == PAPER_16K_LATERAL + || paper == PAPER_LETTER_LATERAL; +} +std::string page_string(int page) +{ + return INVOKE_STR(g_page, page, 1); +} +std::string sharpen_string(int sharpen) +{ + return INVOKE_STR(g_sharpen, sharpen, 0); +} +std::string fill_background_string(int fill) +{ + return INVOKE_STR(g_fill_bkg, fill, 0); +} +std::string scan_mode_string(int mode) +{ + return INVOKE_STR(g_scan_mode, mode, 0); +} +std::string text_direction_string(int text_dir) +{ + return INVOKE_STR(g_text_direction, text_dir, 0); +} +std::string is_permaeate_string(int permaeate_lv) +{ + return INVOKE_STR(g_permeate_lv, permaeate_lv, 0); +} + + diff --git a/hgdriver/hgdev/common_setting.h b/hgdriver/hgdev/common_setting.h new file mode 100644 index 0000000..fe47447 --- /dev/null +++ b/hgdriver/hgdev/common_setting.h @@ -0,0 +1,773 @@ +锘#pragma once + +// hg_scanner_cfg is the base configuration definitions +// +// created on 2022-02-23 +// +#include +#include "char_const.h" + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 纭欢閰嶇疆椤瑰畾涔 +// +#define STR_SETTING(enum_val) HUAGAO_SETTING_STR_##enum_val +#define MAKE_STR_AND_ENUM(enum_val) {HUAGAO_SETTING_STR_##enum_val, enum_val} +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +enum hg_scanner_type +{ + HG_SCANNER_TYPE_NONE = 0, + HG_SCANNER_TYPE_G100, + HG_SCANNER_TYPE_G200, + HG_SCANNER_TYPE_G300, + HG_SCANNER_TYPE_G400, + HG_SCANNER_TYPE_G139, + HG_SCANNER_TYPE_G239, +}; + +//100 200 3399 鍗忚 +typedef union hg_scanner_config +{ + unsigned int value; + struct + { + unsigned int paper : 5; // 绾稿紶绫诲瀷 锛堟瘡涓瀷鍙锋湁涓嶅悓鐨勬槧灏勮〃锛 + unsigned int color : 1; // 鏄惁褰╄壊 0 - 榛戠櫧, 1 - 褰╄壊 + unsigned int dpi : 2; // DPI 锛堟槧灏勮〃锛 + unsigned int ultrasonic_enable : 1; // 鏄惁杩涜瓒呭0娉㈡娴(鍙/澶氬紶杩涚焊妫娴) + unsigned int staple_enbale : 1; // 鏄惁杩涜閽変功閽夋娴 + unsigned int screw_detect_enable : 1; // 鏄惁姝枩妫娴 + unsigned int screw_detect_level : 3; // 姝枩妫娴嬫按骞 + unsigned int iscorrect_mod : 1; // 鏄惁鑷姩鏍℃ + unsigned int is_autopaper : 1; // 鏄惁鑷姩杩涚焊 + unsigned int reserved1 : 4; // 淇濈暀 + unsigned int pc_correct : 1; // 鏄惁鍦≒C绔牎姝 + unsigned int enable_sizecheck : 1; // 鏄惁杩涜灏哄妫娴 + unsigned int enabledsp_cache : 1; // 鏄惁鍦ㄧ墖涓婄紦瀛 + unsigned int sizeerror_errorratio : 9; // 骞呴潰妫娴 + }g200params; +}HGSCANCONF; + +//300 400 +typedef union hg_scanner_config_dsp +{ + unsigned int value; + struct + { + unsigned int paper : 5; + unsigned int color : 1; + unsigned int dpi : 2; + unsigned int double_feed_enbale : 1; + unsigned int stable_enbale : 1; + unsigned int screw_detect_enable : 1; + unsigned int screw_detect_level : 3; + unsigned int unused_one : 6; + unsigned int pc_correct : 1; + unsigned int enable_sizecheck : 1; + unsigned int unused_two : 10; + }params; +}HGSCANCONF_DSP; + +typedef union hg_scanner_config_G400 + { + unsigned int value; + struct + { + unsigned int pageSize : 5; + unsigned int isColor : 1; + unsigned int dpi : 2; + unsigned int doubleFeeded : 1; + unsigned int enableUV : 1; + unsigned int enableLed : 1; + unsigned int sizedetece : 1; + unsigned int reversed1 : 5; + unsigned int isCorrect : 1; + unsigned int dstHeight : 8; + unsigned int reversed2 : 6; + }params; + }HGSCANCONF_G400; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 璁剧疆椤瑰瓧绗︿覆瀹氫箟 +// + +// int match_best_xxx(std::string& val, bool* exact) 鍑芥暟绨囧師鍨嬭鏄 +// +// Function: 鍖归厤鏈鎺ヨ繎鐨勫瓧绗︿覆锛屽苟杩斿洖瀵瑰簲鐨勬灇涓惧 +// +// Parameter: val - [in] 鐢ㄦ埛浼犲叆鐨勮缃弬鏁板瓧绗︿覆 +// [out] 濡傛灉浼犲叆鐨勫弬鏁颁笉鑳界簿纭尮閰嶅浐瀹氬硷紝鍒欎慨鏀逛负鏈鎺ヨ繎鐨勫兼垨榛樿鍊 +// +// exact - [out] 濡傛灉val涓庡浐瀹氶夐」绮剧‘鍖归厤锛 鍒欒繑鍥瀟rue锛 +// 鍚﹀垯杩斿洖false锛屽苟鍦╲al涓繑鍥炵簿纭殑閫夐」鍊 +// +// Return: 鍖归厤瀛楃涓插肩殑鏋氫妇鍊 + +// +// 娉ㄦ剰锛氫笅鍒楁墍鏈夋枃鏈畾涔夛紝蹇呴』涓庤澶嘕SON閰嶇疆鏂囦欢涓殑鐩稿叧鍐呭淇濇寔涓鑷达紝鍚﹀垯灏嗗鑷磋澶囧弬鏁拌缃笉姝g‘锛岃涓轰笉鍙帶锛侊紒锛 +// +// 棰滆壊妯″紡 閫夐」 +//#define HUAGAO_SETTING_STR_COLOR_MODE_BLACK_WHITE "榛戠櫧" +//#define HUAGAO_SETTING_STR_COLOR_MODE_256_GRAY "256绾х伆搴" +//#define HUAGAO_SETTING_STR_COLOR_MODE_24_BITS "24浣嶅僵鑹" +//#define HUAGAO_SETTING_STR_COLOR_MODE_AUTO_MATCH "棰滆壊鑷姩璇嗗埆" +enum +{ + COLOR_MODE_BLACK_WHITE = 0, + COLOR_MODE_256_GRAY, + COLOR_MODE_24_BITS, + COLOR_MODE_AUTO_MATCH, +}; +int match_best_color_mode(std::string& val, bool* exact); +std::string color_mode_string(int clr_mode); + +//#define HUAGAO_SETTING_STR_MULTI_OUT_NOT "涓嶉夋嫨杈撳嚭妯″紡" +//#define HUAGAO_SETTING_STR_MULTI_OUT_ALL "褰╄壊+鐏板害+榛戠櫧" +//#define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_GRAY "褰╄壊+鐏板害" +//#define HUAGAO_SETTING_STR_MULTI_COLOR_ADN_BW "褰╄壊+榛戠櫧" +//#define HUAGAO_SETTING_STR_MULTI_GRAY_ADN_BW "鐏板害+榛戠櫧" +enum +{ + MULTI_OUT_NOT = 0, + MULTI_OUT_ALL, + MULTI_COLOR_ADN_GRAY, + MULTI_COLOR_ADN_BW, + MULTI_GRAY_ADN_BW +}; +int match_best_multi_out(std::string& val, bool* exact); +std::string multi_out_string(int clr_mode); + + +// 闄よ壊 閫夐」 +//#define HUAGAO_SETTING_STR_RID_COLOR_NONE "涓嶉櫎鑹" +//#define HUAGAO_SETTING_STR_RID_COLOR_RID_RED "闄ょ孩鑹" +//#define HUAGAO_SETTING_STR_RID_COLOR_RID_GREEN "闄ょ豢鑹" +//#define HUAGAO_SETTING_STR_RID_COLOR_RID_BLUE "闄よ摑鑹" +//#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_RED "绾㈣壊澧炲己" +//#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_GREEN "缁胯壊澧炲己" +//#define HUAGAO_SETTING_STR_RID_COLOR_ENHANCE_BLUE "钃濊壊澧炲己" +enum +{ + RID_COLOR_NONE = 0, + RID_COLOR_RID_RED, + RID_COLOR_RID_GREEN, + RID_COLOR_RID_BLUE, + RID_COLOR_ENHANCE_RED, + RID_COLOR_ENHANCE_GREEN, + RID_COLOR_ENHANCE_BLUE, +}; +int match_best_rid_color(std::string& val, bool* exact); +std::string rid_color_string(int rid_color); + +// 绾稿紶 閫夐」 +//#define HUAGAO_SETTING_STR_PAPER_A3 "A3" +//#define HUAGAO_SETTING_STR_PAPER_A4 "A4" +//#define HUAGAO_SETTING_STR_PAPER_A5 "A5" +//#define HUAGAO_SETTING_STR_PAPER_A6 "A6" +//#define HUAGAO_SETTING_STR_PAPER_B4 "B4" +//#define HUAGAO_SETTING_STR_PAPER_B5 "B5" +//#define HUAGAO_SETTING_STR_PAPER_B6 "B6" +//#define HUAGAO_SETTING_STR_PAPER_8K "8寮" +//#define HUAGAO_SETTING_STR_PAPER_16K "16寮" +//#define HUAGAO_SETTING_STR_PAPER_LETTER "Letter" +//#define HUAGAO_SETTING_STR_PAPER_A4_LATERAL "A4妯悜" +//#define HUAGAO_SETTING_STR_PAPER_A5_LATERAL "A5妯悜" +//#define HUAGAO_SETTING_STR_PAPER_A6_LATERAL "A6妯悜" +//#define HUAGAO_SETTING_STR_PAPER_B5_LATERAL "B5妯悜" +//#define HUAGAO_SETTING_STR_PAPER_B6_LATERAL "B6妯悜" +//#define HUAGAO_SETTING_STR_PAPER_16K_LATERAL "16寮妯悜" +//#define HUAGAO_SETTING_STR_PAPER_LETTER_LATERAL "Letter妯悜" +//#define HUAGAO_SETTING_STR_PAPER_DOUBLE_LETTER "Double Letter" +//#define HUAGAO_SETTING_STR_PAPER_LEGAL "LEGAL" +//#define HUAGAO_SETTING_STR_PAPER_AUTO_MATCH "鍖归厤鍘熷灏哄" +//#define HUAGAO_SETTING_STR_PAPER_MAX_SIZE "鏈澶ф壂鎻忓昂瀵" +//#define HUAGAO_SETTING_STR_PAPER_MAX_SIZE_CLIP "鏈澶ф壂鎻忓昂瀵歌嚜鍔ㄨ鍒" +//#define HUAGAO_SETTING_STR_PAPER_TRIGEMINY "涓夎仈璇曞嵎" +enum +{ + PAPER_A3 = 0, + PAPER_A4, + PAPER_A5, + PAPER_A6, + PAPER_B4, + PAPER_B5, + PAPER_B6, + PAPER_8K, + PAPER_16K, + PAPER_LETTER, + PAPER_A4_LATERAL, + PAPER_A5_LATERAL, + PAPER_A6_LATERAL, + PAPER_B5_LATERAL, + PAPER_B6_LATERAL, + PAPER_16K_LATERAL, + PAPER_LETTER_LATERAL, + PAPER_DOUBLE_LETTER, + PAPER_LEGAL, + PAPER_AUTO_MATCH, + PAPER_MAX_SIZE, + PAPER_MAX_SIZE_CLIP, + PAPER_TRIGEMINY, +}; +int match_best_paper(std::string& val, bool* exact); +std::string paper_string(int paper); +bool is_lateral(int paper); + +// 椤甸潰鎵弿妯″紡 閫夐」 +//#define HUAGAO_SETTING_STR_PAGE_SINGLE "鍗曢潰" +//#define HUAGAO_SETTING_STR_PAGE_DOUBLE "鍙岄潰" +//#define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY "璺宠繃绌虹櫧椤碉紙閫氱敤锛" +//#define HUAGAO_SETTING_STR_PAGE_OMIT_EMPTY_RECEIPT "璺宠繃绌虹櫧椤碉紙鍙戠エ绾革級" +//#define HUAGAO_SETTING_STR_PAGE_FOLIO "瀵规姌" +enum { + PAGE_SINGLE = 0, + PAGE_DOUBLE, + PAGE_OMIT_EMPTY, + PAGE_OMIT_EMPTY_RECEIPT, + PAGE_FOLIO, +}; +int match_best_page(std::string& val, bool* exact); +std::string page_string(int page); + +// 閿愬寲 閫夐」 +//#define HUAGAO_SETTING_STR_SHARPEN_NONE "鏃" +//#define HUAGAO_SETTING_STR_SHARPEN_SHARPEN "閿愬寲" +//#define HUAGAO_SETTING_STR_SHARPEN_SHARPEN_MORE "杩涗竴姝ラ攼鍖" +//#define HUAGAO_SETTING_STR_SHARPEN_HAZY "妯$硦" +//#define HUAGAO_SETTING_STR_SHARPEN_HAZY_MORE "杩涗竴姝ユā绯" +enum +{ + SHARPEN_NONE = 0, + SHARPEN_SHARPEN, + SHARPEN_SHARPEN_MORE, + SHARPEN_HAZY, + SHARPEN_HAZY_MORE, +}; +int match_best_sharpen(std::string& val, bool* exact); +std::string sharpen_string(int sharpen); + +//鑳屾櫙濉厖鏂瑰紡 閫夐」 +//#define HUAGAO_SETTING_STR_FILL_BKG_CONVEX_POLYGON "鍑稿杈瑰舰" +//#define HUAGAO_SETTING_STR_FILL_BKG_CONCAVE_POLYGON "鍑瑰杈瑰舰" +enum +{ + FILL_BKG_CONVEX_POLYGON = 0, + FILL_BKG_CONCAVE_POLYGON, +}; +int match_best_fill_background(std::string& val, bool* exact); +std::string fill_background_string(int fill); + +// 鎵弿鏁伴噺 閫夐」 +//#define HUAGAO_SETTING_STR_SCAN_MODE_CONTINUOUS "杩炵画鎵弿" +//#define HUAGAO_SETTING_STR_SCAN_MODE_GIVEN_COUNT "鎵弿鎸囧畾寮犳暟" +enum +{ + SCAN_MODE_CONTINUOUS = 0, + SCAN_MODE_GIVEN_COUNT, +}; +int match_best_scan_mode(std::string& val, bool* exact); +std::string scan_mode_string(int mode); + +// 鏂囩ǹ鏂瑰悜 閫夐」 +//#define HUAGAO_SETTING_STR_TEXT_DIRECTION_0 "0掳" +//#define HUAGAO_SETTING_STR_TEXT_DIRECTION_90 "90掳" +//#define HUAGAO_SETTING_STR_TEXT_DIRECTION_180 "180掳" +//#define HUAGAO_SETTING_STR_TEXT_DIRECTION_270 "-90掳" +//#define HUAGAO_SETTING_STR_TEXT_DIRECTION_AUTO "鑷姩鏂囨湰鏂瑰悜璇嗗埆掳" +enum +{ + TEXT_DIRECTION_0 = 0, + TEXT_DIRECTION_90, + TEXT_DIRECTION_180, + TEXT_DIRECTION_270, + TEXT_DIRECTION_AUTO, +}; +int match_best_text_direction(std::string& val, bool* exact); +std::string text_direction_string(int text_dir); + + +//#define HUAGAO_SETTING_STR_PERMAEATE_WEAKER "杈冨急" +//#define HUAGAO_SETTING_STR_PERMAEATE_WEAK "寮" +//#define HUAGAO_SETTING_STR_PERMAEATE_ORDINARY "涓鑸" +//#define HUAGAO_SETTING_STR_PERMAEATE_STRONG "寮" +//#define HUAGAO_SETTING_STR_PERMAEATE_STRONGER "杈冨己" +enum +{ + PERMAEATE_WEAKER = 0, + PERMAEATE_WEAK, + PERMAEATE_ORDINARY, + PERMAEATE_STRONG, + PERMAEATE_STRONGER, +}; + +int match_best_permaeate_lv(std::string& val, bool* exact); +std::string is_permaeate_string(int text_dir); + + +typedef union _img_proc_fixed_param +{ + unsigned long long value; + struct + { + unsigned long long rid_red : 1; // 澶氭祦杈撳嚭闄ょ孩 + unsigned long long rid_answer_red : 1; // 绛旈鍗¢櫎绾 + unsigned long long erase_bakground : 1; // 鑳屾櫙绉婚櫎 + unsigned long long noise_optimize : 1; // 鍣偣浼樺寲 + unsigned long long exchange : 1; // 浜ゆ崲姝e弽闈 + unsigned long long split : 1; // 鍥惧儚鎷嗗垎 + unsigned long long rid_hole : 1; // 绌垮瓟绉婚櫎 + unsigned long long dark_sample : 1; // 娣辫壊鏍峰紶 + + unsigned long long rotate_back_180 : 1; // 鑳岄潰鏃嬭浆180搴 + unsigned long long fractate_check : 1; // 鎶樿妫娴 + unsigned long long is_permeate : 1; // 闃叉娓楅 + unsigned long long remove_morr : 1; // 鍘婚櫎鎽╁皵绾 + unsigned long long error_extention : 1; // 閿欒鎵╂暎 + unsigned long long remove_txtture : 1; // 鍘荤綉绾 + unsigned long long fill_background : 2; // 鑳屾櫙濉厖鏂瑰紡 + + unsigned long long is_permeate_lv_ : 3; // 娓楅忕瓑绾 1 - 5 + unsigned long long multi_out : 3; // 澶氭祦杈撳嚭 + unsigned long long erase_black_frame: 1; // 娑堥櫎榛戞 + unsigned long long automatic_skew : 1; // 鑷姩绾犲亸 + + unsigned long long rid_color : 3; // 闄よ壊 + unsigned long long color_mode : 3; // 棰滆壊妯″紡 + unsigned long long page : 3; // 椤甸潰妯″紡 + + unsigned long long paper : 8; // 绾稿紶绫诲瀷 + + unsigned long long sharpen : 4; // 閿愬寲妯″紡 + unsigned long long text_direction : 4; // 鏂囩ǹ鏂瑰悜 + + unsigned long long reserved2 : 14; // 淇濈暀 + }bits; +}IMGPRCFIXPARAM; + +// 璁剧疆椤瑰瓧绗︿覆瀹氫箟 - OVER +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 鐘舵佷俊鎭弿杩扮 +// + +// 鐘舵佷俊鎭弿杩扮 - OVER +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////鍥惧儚鍙傛暟鍙婅澶囧崗璁/////////////////////////////////////////////////////// +// #ifndef uint8_t +// #define uint8_t unsigned char +// #endif +// #ifndef uint32_t +// #define uint32_t unsigned int +// #endif + +//DSP璁惧USB璇诲啓鍗忚缁撴瀯 +// +typedef struct tag_USBCB { + unsigned int u32_CMD; + unsigned int u32_Data; + unsigned int u32_Count; +}USBCB, * PUSBCB; + +//DSP璁惧USB璇诲啓鍗忚缁撴瀯 - OVER + +typedef struct hg_tag_SIZE +{ + long cx; + long cy; +}CSSIZE, *PCSSIZE, *LPCSSIZE; + +typedef CSSIZE CSSIZEL; +typedef CSSIZE *PCSSIZEL, *LPCSSIZEL; + + +enum PaperAlign :unsigned char { + Rot0 = 0, + Rot270 = 3, + AutoTextOrientation = 5 +}; + +enum LowPowerMode :unsigned int { + Min_None = 0, + Min_5, + Min_10, + Min_20, + Min_30, + Min_60, + Min_120, + Min_240 +}; + +//绾稿紶绫诲瀷 +// +enum TwSS : unsigned short +{ + None = 0, + A4Letter = 1, + A4 = 1, + B5Letter = 2, + JISB5 = 2, + B5 = 2, + USLetter = 3, + USLegal = 4, + A5 = 5, + B4 = 6, + ISOB4 = 6, + B6 = 7, + ISOB6 = 7, + USLedger = 9, + USExecutive = 10, + A3 = 11, + B3 = 12, + ISOB3 = 12, + A6 = 13, + C4 = 14, + C5 = 15, + C6 = 16, + _4A0 = 17, + _2A0 = 18, + A0 = 19, + A1 = 20, + A2 = 21, + A7 = 22, + A8 = 23, + A9 = 24, + A10 = 25, + ISOB0 = 26, + ISOB1 = 27, + ISOB2 = 28, + ISOB5 = 29, + ISOB7 = 30, + ISOB8 = 31, + ISOB9 = 32, + ISOB10 = 33, + JISB0 = 34, + JISB1 = 35, + JISB2 = 36, + JISB3 = 37, + JISB4 = 38, + JISB6 = 39, + JISB7 = 40, + JISB8 = 41, + JISB9 = 42, + JISB10 = 43, + C0 = 44, + C1 = 45, + C2 = 46, + C3 = 47, + C7 = 48, + C8 = 49, + C9 = 50, + C10 = 51, + USStatement = 52, + BusinessCard = 53, + MaxSize = 54, + K8 = 0x81, + K16 = 0x82, + Trigeminy = 0x83, +}; + +//绾稿紶绫诲瀷 - OVER + +enum ScannerSerial +{ + G100Serial, + G200Serial, + G300Serial, + G400Serial, + G10039Serial, + G20039Serial, +}; + +//璁惧鐘舵 +// +typedef enum tagUsbSupported { + //鍋滄鎵弿 + SCAN_STOP = -2, + //寮傚父 + HAVE_ERROR = -1, + //姝e父鐘舵 + NORMAL = 0, + //寮鐩 + OPEN_COVER = 1, + // 鏃犵焊 + NO_FEED = 2, + // 鎼撶焊澶辫触 + FEED_IN_ERROR = 4, + // 鍗$焊 + PAPER_JAM = 8, + // 妫娴嬪埌鍙屽紶 + DETECT_DOUBLE_FEED = 16, + // 妫娴嬪埌璁功閽 + DETECT_STAPLE = 32, + // 绾稿紶鍊炬枩 + PAPER_SKEW = 64, + // 鑷姩妯″紡 + AUTO_SCAN_MODE = 65, + // 鎵嬪姩妯″紡 + MANAUL_SCAN_MODE = 66, + // 璁℃暟妯″紡 + COUNT_MODE = 67, + // 纭欢閿欒 + HARDWARE_ERROR = 68, + // FPGA宕╂簝 + FPGA_ERROR = 68, + // 寮濮 + START_SCAN = 69, + //鍋滄 + STOP_SCAN = 70, + //鏈夊浘 + HAVE_IMAGE = 71, + // 鏇存柊鎵弿鍙傛暟 + UPDATE_SCAN_PARAMETER = 72, + // PC绻佸繖鎴栧嚭閿 + PC_SCAN_BUSY_or_ERROR = 73, + //鎽鸿 + DOG_EAR = 74, + //骞呴潰妫娴嬮敊璇 + SIZE_ERROR = 75, + //鍙栧浘瓒呮椂 + AQUIRE_IMAGE_TIMEOUT = 76, + //鑾峰彇鍥剧墖涓庢壂鎻忓紶鏁颁笉鍖归厤 + LOSE_IMAGE = 77, + //usb璇诲彇鏁版嵁閿欒 + USB_BULK_ERROR = 78, + //v4l2鍙栧浘澶辫触 + V4L2_AQULRE_ERROR = 79, + //鎵弿浠唴閮ㄥ浘鐗囦涪澶 + V4L2_IMAGE_EMPTY = 80, + //澶勪簬浼戠湢涓 + SLEEPING = 81, + //妫娴嬪埌鏈夋姌瑙 + HAVE_DOGEAR = 82, + //USB 鏈繛鎺 + USB_DISCONNECTED = 200, + //鐢ㄦ埛鐐瑰嚮鍋滄 + USER_STOP = 201, + //鑷姩鏍℃瀹屾垚 + AUTO_FLAT_FINISHED = 202, + //usb 宸茶繛鎺 + USB_CONNECTED, + HAVE_PAPER, + /*USB閾炬帴鏂紑***/ + DEVICE_OFF_LINE, +} UsbSupported, * PUsbSupported; +// +//璁惧鐘舵 - OVER + +//G300 G400 绾稿紶绫诲瀷鍗忚 +// +enum G400_PaperSize { + G400_A3 = 0, + G400_A4, + G400_A4R, + G400_A5, + G400_A5R, + G400_A6, + G400_A6R, + G400_B4, + G400_B5, + G400_B5R, + G400_B6, + G400_B6R, + G400_DOUBLELETTER, + G400_LEGAL, + G400_LETTER, + G400_LETTERR, + G400_LONGLETTER, + G400_MAXSIZE, + G400_AUTO +}; +// +//G300 G400 绾稿紶绫诲瀷鍗忚 - OVER + +//G100 G200 3399璁惧鍗忚 +// +enum tagUsbKeyWords +{ + NO_COMMAND = 0, + GET_DSP_STATUS = 1, + GET_IMAGE = 2, + POP_IMAGE = 3, + START_COMMAND = 4, + STOP = 5, + GET_SCAN_MODE = 6, + GET_FW_VERSION = 7, + SEND_STATUS_PC = 8, + CONFIGURED_DATA = 9, + SEND_FW = 10, + GET_CONFIG_DATA = 11, + GET_SCANN_NUM = 12, + GET_PAPERFEEDER_STATUS = 13, + INIT_HARDWARE_SYS = 14, + GET_PAPER_STATUS = 0x0d, + SEND_COMPONENTS_GR = 15, + SEND_COMPONENTS_GB = 16, + SEND_SCAN_MODE = 17, + START_FLAT = 18, + STOP_FLAT = 19, + SEND_200_COLOR_FLAT_DATA = 20, + SEND_300_COLOR_FLAT_DATA = 21, + GET_200_COLOR_FLAT_DATA = 22, + GET_300_COLOR_FLAT_DATA = 23, + SEND_200_GRAY_FLAT_DATA = 24, + SEND_300_GRAY_FLAT_DATA = 25, + GET_200_GRAY_FLAT_DATA = 26, + GET_300_GRAY_FLAT_DATA = 27, + SEND_SERIAL = 28, + GET_SERIAL = 29, + GET_ROLLER_NUM = 30, + CLR_ROLLER_NUM = 31, + START_COMMAND_COUNT = 38, + SET_SLEEP_TIME = 39, + GET_SLEEP_TIME = 40, + CLR_HARDWARE_CACHE = 41 +}; + +//G100 G200 3399璁惧鍗忚 - OVER + + +#pragma pack(push) +#pragma pack(4) + +//闄よ壊鍜屽寮 +// +typedef enum tagColor_Filter +{ + FILTER_RED, + FILTER_GREEN, + FILTER_BLUE, + FILTER_NONE, + FILTER_ALL, + ENHANCE_RED, + ENHANCE_GREEN, + ENHANCE_BLUE +}ColorFilter; + +//闄よ壊鍜屽寮 - OVER + +//濉┛瀛 +// +typedef struct tagFillHole +{ + uint8_t is_fillhole; + int fillholeratio; +}FillHole; + +//濉┛瀛 - OVER + +//榛戠櫧闄嶅櫔 +// +typedef struct tagDetachNoise +{ + unsigned char is_detachnoise; + int detachnoise; +}DetachNoise; + +//榛戠櫧闄嶅櫔 -OVER + +//鑷畾涔夎鍒 +// +typedef struct tagCrop_Rect +{ + int enable; + int x; /*****鑷畾涔夎鍒囧尯鍩熷乏涓婅x鍧愭爣*/ + int y; /*****鑷畾涔夎鍒囧尯鍩熷乏涓婅y鍧愭爣*/ + int width; /*****鑷畾涔夎鍒囧尯鍩熷搴*******/ + int height; /*****鑷畾涔夎鍒囧尯鍩熼珮搴*******/ +}CropRect; + +//鑷畾涔夎鍒 -OVER + +//澶氱暀杈撳嚭 +// +typedef enum tagMulti_Output { + Unused = -1, + All, + ColorGray, + ColorBw, + GrayBw +}MultiOutput; + +//澶氱暀杈撳嚭 -OVER + +//璁剧疆璁惧纭欢鐘舵 +// +typedef struct tagHhardware_Params +{ + unsigned char capturepixtype; + unsigned char en_doublefeed; + unsigned char en_stapledetect; + unsigned char en_skrewdetect; + unsigned char skrewdetectlevel; + LowPowerMode lowpowermode; +#ifdef UV + unsigned char en_uv; +#endif +}HardwareCaps; + +//璁剧疆璁惧纭欢鐘舵 -OVER + +//鍥惧儚鍙傛暟璁剧疆 +// +typedef struct _scan_conf +{ + unsigned char papertype; /**< the current paper source ADF or Flatbed*/ + unsigned char paperAlign; + unsigned char en_sizecheck; /**< 灏哄妫娴*/ + float imageRotateDegree; + unsigned char is_duplex; /**< True to use duplex false for simplex, ignored if flatbed*/ + unsigned char en_fold; /**<瀵规姌*/ + int pixtype; /**< type of pixels to transfer image as */ + int automaticcolor; /**<椤旇壊鑷嫊璀樺垾*/ + int automaticcolortype; /**<椤旇壊鑷嫊璀樺垾鍚庨潪褰╄壊涓婂偝椤炲瀷*/ + //ScanRect scanrect; + float resolution_dst; /**< horizontal resolution */ + float resolution_native; + float gamma; /**< Gamma */ + float contrast; /**< Contrast */ + float brightness; /**< Brightness */ + float threshold; /**< Threshold */ + unsigned char is_autocontrast; /**< 鑷姩瀵规瘮搴*/ + unsigned char is_autocrop; /**< 鑷姩瑁佸垏*/ + unsigned char is_autodiscradblank_normal; /**< 鑷姩涓㈠純绌虹櫧椤甸氱敤*/ + int discardblank_percent; /**<璺宠繃绌虹櫧椤甸榾鍊*/ + unsigned char is_autodiscradblank_vince;/**鑷姩涓㈠純绌虹櫧椤靛彂绁*/ + unsigned char is_switchfrontback; /**浜ゆ崲姝e弽闈*/ + unsigned char autodescrew; /**< 鑷姩绾犲亸*/ + unsigned char multi_output_red; /*澶氭祦杈撳嚭*/ + unsigned char hsvcorrect; /**<绛旈鍗¢櫎绾*/ + unsigned char filter; /**< 闄よ壊*/ + unsigned char sharpen; + unsigned char enhance_color; /**< 棰滆壊澧炲己*/ + unsigned char fillbackground; /**< 濉粦妗*/ + bool is_convex; /**< 濉粦妗嗘ā寮忥紝true涓哄嚫澶氳竟褰㈠~鍏咃紝false涓哄嚬澶氳竟褰㈠~鍏咃紝榛樿true*/ + int noise; /**< 闄ゅ櫔鍍忕礌锛岃兘澶熸秷闄oise瀹藉害鐨勮儗鏅珫鏉$汗骞叉壈锛岄粯璁40*/ + int indent; /**< 杞粨缂╄繘锛岃鍓佺籂鍋忔垨鑰呴粦搴曞~鍏呮椂锛屽鎺㈢储鍒扮殑绾稿紶杞粨杩涜缂╄繘indent鍍忕礌锛岄粯璁5*/ + int AutoCrop_threshold; /**< 鑷姩瑁佸壀浜屽煎寲闃堝硷紝鍙栧艰寖鍥(0, 255)锛岄粯璁40*/ + unsigned short scannum; /**< 鎵弿寮犳暟*/ + unsigned char is_backrotate180; /**< 鑳岄潰鏃嬭浆180*/ + unsigned char is_dogeardetection; /**<鎶樿妫娴*/ + HardwareCaps hardwarecaps; /**< 纭欢鎵弿鍙傛暟*/ + FillHole fillhole; /**< 濉┛瀛*/ + DetachNoise detachnoise; /**< 榛戠櫧闄嶅櫔*/ + unsigned char is_autotext; /**< 鑷姩鏂囨湰鏂瑰悜璇嗗埆*/ + bool isfillcolor; /**< 鑷姩瑁佸垏棰滆壊濉厖>*/ + int refuseInflow; /**< 闃叉娓楅>*/ + int colorCorrection; /**< 鑹插僵鏍℃>*/ + int removeMorr; /**< 鍘婚櫎鎽╁皵绾>*/ + int errorExtention; /** < 閿欒鎵╂暎>*/ + int textureRemove; /** < 闄ょ綉绾>*/ + int splitImage; /** < 鍥惧儚鎷嗗垎>*/ + CropRect cropRect; /**< 鑷畾涔夎鍒>*/ + MultiOutput multiOutput; /**< 澶氭祦杈撳嚭>*/ + bool normalCrop; /**< 鑷姩瑁佸垏娣辫壊鏍峰紶>*/ + uint32_t dogeardistabce; + bool fadeback; /**< 鑳屾櫙闄よ壊>*/ + int fadebackrange; + uint32_t reserve[1024]; /**< 棰勭暀4096瀛楄妭鍋氬崗璁墿灞*/ +}SCANCONF ,*LPSCANCONF; + +//鍥惧儚鍙傛暟璁剧疆 -OVER +#pragma pack(pop) diff --git a/hgdriver/hgdev/filetools.h b/hgdriver/hgdev/filetools.h new file mode 100644 index 0000000..b380fd9 --- /dev/null +++ b/hgdriver/hgdev/filetools.h @@ -0,0 +1,65 @@ +#ifndef FILE_TOOLS_H +#define FILE_TOOLS_H + +#ifdef WIN32 +#include +#include +#include +#include + +class FileTools +{ +public: + static std::vector getFiles(std::string path) + { + std::vector files; + getFiles(path, files); + return files; + } + + static void write_log(std::string filename, std::string log) + { + std::string savepath; + std::string str = "D:"; + savepath = str+"\\"+filename; + std::ofstream ofs(savepath, std::ios::app); + + time_t timp; + struct tm* p; + time(&timp); + p=localtime(&timp); + ofs << p->tm_year << "/" << p->tm_mon << "/" << p->tm_mday << " " << p->tm_hour << ":" << p->tm_min << ":" << p->tm_sec << " "<& files) + { + //文件句柄 + long hFile = 0; + //文件信息 + struct _finddata_t fileinfo; + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo))!=-1) + { + do + { + //如果是目录,迭代之 + //如果不是,加入列表 + if ((fileinfo.attrib & _A_SUBDIR)) + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) + getFiles(p.assign(path).append("\\").append(fileinfo.name), files); + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + +}; +#endif + +#endif \ No newline at end of file diff --git a/hgdriver/hgdev/hg_ipc.cpp b/hgdriver/hgdev/hg_ipc.cpp new file mode 100644 index 0000000..86a2fb7 --- /dev/null +++ b/hgdriver/hgdev/hg_ipc.cpp @@ -0,0 +1,276 @@ +#include "hg_ipc.h" +#include "../../sdk/hginclude/hg_log.h" +#include "huagao/hgscanner_error.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#else +#include +#include +#include +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// windows event ... +#ifdef WIN32 +int __stdcall sem_init(sem_t* handle, int, int) +{ + if (!handle) + { + errno = EINVAL; + return -1; + } + + *handle = CreateEvent(NULL, TRUE, FALSE, NULL); + if (*handle) + return 0; + else + { + errno = GetLastError(); + + return -1; + } +} +void __stdcall sem_destroy(sem_t* handle) +{ + if (*handle) + { + CloseHandle(*handle); + *handle = NULL; + } +} +int __stdcall sem_trywait(sem_t* handle) +{ + return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0; +} +void __stdcall sem_wait(sem_t* handle) +{ + if(WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0) + ResetEvent(*handle); +} +int __stdcall sem_timedwait(sem_t* handle, struct timespec* to) +{ + DWORD elapse = to->tv_sec * 1000; + elapse += to->tv_nsec / (1000 * 1000); + + int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0; + + if(ret == 0) + ResetEvent(*handle); + + return ret; +} +void __stdcall sem_post(sem_t* handle) +{ + SetEvent(*handle); +} + +#define pid_t int + +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// platform_event (base on semaphore) +platform_event::platform_event() : waiting_(false), dbg_info_("") +{ + int err = sem_init(&sem_, 0, 0); + + if (err == -1) + { + err = errno; + HG_VLOG_MINI_2(HG_LOG_LEVEL_FATAL, "(%s)sem_init failed: %d\n", hg_log::format_ptr(this).c_str(), err); + } +} +platform_event::~platform_event() +{ + sem_destroy(&sem_); +} + +bool platform_event::try_wait(void) +{ + return sem_trywait(&sem_) == 0; +} +bool platform_event::wait(unsigned timeout) +{ + bool waited = true; + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> waiting...\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str()); + waiting_ = true; + if (timeout == USB_TIMEOUT_INFINITE) + sem_wait(&sem_); + else + { + struct timespec to; + to.tv_sec = timeout / 1000; + to.tv_nsec = (long)((timeout % 1000) * 1000 * 1000); + waited = sem_timedwait(&sem_, &to) == 0; + } + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> %s.\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str(), waited ? "waited" : "wait timeout"); + waiting_ = false; + + return waited; +} +void platform_event::notify(void) +{ + sem_post(&sem_); +} +bool platform_event::is_waiting(void) +{ + return waiting_; +} +void platform_event::set_debug_info(const char* info) +{ + dbg_info_ = info ? info : ""; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// shared_memory +shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0) +{ + unsigned int* ptr = (unsigned int*)&key_; + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]); + + init(); +} +shared_memory::~shared_memory() +{ + clear(); +} + +void shared_memory::init(void) +{ +#ifdef WIN32 + char name[40] = { 0 }; + DWORD* key = (DWORD*)&key_; + HANDLE h = NULL; + + sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]); + h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name); + if (h == NULL) + return; + + first_ = !(GetLastError() == ERROR_ALREADY_EXISTS); + obj_ = (void*)h; +#else + int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600); + if (obj < 0) + { + if (errno == EEXIST) + { + first_ = false; + obj = shmget(key_, bytes_, 0600); + if(read().empty()) + { + first_ = true; + clear(); + obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "shared memory id(%d) already exists but is not accessible, close and reopen it\n", obj); + } + } + else + return; + } + obj_ = (void*)obj; + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened"); +#endif + + if(first_) + { + pid_t pid = getpid(); + std::string me(""); + char buf[40] = { 0 }; + unsigned int* pn = (unsigned int*)&pid; + + if (sizeof(pid) > 4 && pn[1]) + sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]); + else + sprintf(buf, "(pid: %u)", pn[0]); + hg_log::pe_path(&me); + me += buf; + write(me.c_str(), me.length()); + } +} +void shared_memory::clear(void) +{ + if (obj_ != (void*)-1) +#ifdef WIN32 + CloseHandle((HANDLE)obj_); +#else + { + if (first_) + { + struct shmid_ds ds = { 0 }; + int* h = (int*)&obj_; + shmctl(*h, IPC_RMID, &ds); + } + } +#endif + + obj_ = (void*)-1; +} +char* shared_memory::get_buf(void) +{ +#ifdef WIN32 + char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + if (!buf) + buf = (char*)-1; +#else + int* h = (int*)&obj_; + char* buf = (char*)shmat(*h, 0, 0); + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "shared memory %d buffer = %s, error = %d\n", *h, hg_log::format_ptr(buf).c_str(), errno); +#endif + + return buf; +} +void shared_memory::release_buf(void* buf) +{ +#ifdef WIN32 + UnmapViewOfFile(buf); +#else + shmdt(buf); +#endif +} + +bool shared_memory::is_ok(void) +{ + return obj_ != nullptr; +} +bool shared_memory::is_first(void) +{ + return is_ok() && first_; +} +std::string shared_memory::read(void) +{ + if (obj_ == (void*)-1) + return ""; + + char* buf = get_buf(); + if (buf == (char*)-1) + return ""; + + std::string ret(""); + size_t len = 0; + int off = sizeof(size_t); + + memcpy(&len, buf, off); + ret = std::string(buf + off, len); + release_buf(buf); + + return ret; +} +int shared_memory::write(const char* data, size_t len) +{ + if (len > bytes_) + return HG_ERR_INSUFFICIENT_MEMORY; + + char* buf = get_buf(); + int off = sizeof(len); + if (buf == (char*)-1) + return errno; + + memcpy(buf, &len, off); + memcpy(buf + off, data, len); + len_ = len; + release_buf(buf); +} diff --git a/hgdriver/hgdev/hg_ipc.h b/hgdriver/hgdev/hg_ipc.h new file mode 100644 index 0000000..5e136d6 --- /dev/null +++ b/hgdriver/hgdev/hg_ipc.h @@ -0,0 +1,124 @@ +锘#pragma once + +// Objects for Inter-Process-Communication +// +// created on 2022-03-01 +// + +#ifdef WIN32 +#include + +#define sem_t HANDLE +#define USB_TIMEOUT_INFINITE -1 +#else +#include +#include +#include +#define USB_TIMEOUT_INFINITE 0 +#endif + +#include +#include + + +class platform_event +{ + sem_t sem_; + volatile bool waiting_; + std::string dbg_info_; + +public: + platform_event(); + ~platform_event(); + +public: + bool try_wait(void); + bool wait(unsigned timeout = USB_TIMEOUT_INFINITE/*ms*/); // USB_TIMEOUT_INFINITE is waiting unfinite, true when watied and false for wait timeout + void notify(void); + bool is_waiting(void); + + void set_debug_info(const char* info); +}; + +class refer +{ + volatile int ref_; + std::mutex mutex_; + +protected: + refer() : ref_(1) + {} + virtual ~refer() + {} + +public: + int add_ref(void) + { + std::lock_guard lock(mutex_); + + return ++ref_; + } + int release(void) + { + int ref = 0; + + { + std::lock_guard lock(mutex_); + ref = --ref_; + } + + if (ref == 0) + delete this; + + return ref; + } +}; + +template +class do_when_born_and_dead : public refer +{ + T* obj_; + void(T::* dead_)(void*); + void* param_; + +public: + do_when_born_and_dead(T* obj, void(T::* born)(void*), void(T::* dead)(void*), void* param) + : obj_(obj), dead_(dead), param_(param) + { + if(born) + (obj_->*born)(param_); + } + +protected: + ~do_when_born_and_dead() + { + (obj_->*dead_)(param_); + } +}; + +// mutex object +class shared_memory : public refer +{ + unsigned long long key_; + void* obj_; + bool first_; + size_t bytes_; + size_t len_; + + void init(void); + void clear(void); + char* get_buf(void); + void release_buf(void* buf); + +public: + shared_memory(unsigned long long key, size_t size = 1024); + +protected: + ~shared_memory(); + +public: + bool is_ok(void); + bool is_first(void); + std::string read(void); + int write(const char* data, size_t len); +}; \ No newline at end of file diff --git a/hgdriver/hgdev/hg_scanner.cpp b/hgdriver/hgdev/hg_scanner.cpp new file mode 100644 index 0000000..3f49f94 --- /dev/null +++ b/hgdriver/hgdev/hg_scanner.cpp @@ -0,0 +1,2086 @@ +锘#include "hg_scanner.h" +#include "../../sdk/hginclude/hg_log.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + + + +static int ui_default_callback(scanner_handle, int, void*, unsigned int*, void*) +{ + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// hg_scanner +hg_scanner::hg_scanner(ScannerSerial serial + , const char* dev_name, usb_io* io) + : name_(dev_name ? dev_name : ""), io_(io), status_(HG_ERR_NOT_START) + , scan_count_(-1), run_(true), paper_size_(TwSS::A4), erase_bkg_range_(10) + , noise_range_(30), omit_empty_level_(50), resolution_(200), rid_hole_range_(.1f) + , bright_(128), contrast_(4), gamma_(1.0f), threshold_(40), anti_noise_(8), margin_(5) + , fractate_level_(50), ui_ev_cb_(ui_default_callback), scan_life_(NULL) + , notify_setting_result_(false), user_cancel_(false), cb_mem_(true), test_1_paper_(false) + , setting_count_(0),img_type_(""), online_(false) +{ + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "%s(%s) constructed\n", name_.c_str(), hg_log::format_ptr(this).c_str()); + image_prc_param_.value = 0; + + if (io_) + { + io_->add_ref(); + status_ = io_->last_error(); + online_ = status_ == HG_ERR_OK; + } + + wait_usb_.set_debug_info("USB"); + wait_img_.set_debug_info("Image"); + thread_usb_read_.reset(new std::thread(&hg_scanner::thread_handle_usb, this)); + thread_img_handle_.reset(new std::thread(&hg_scanner::thread_image_handle, this)); +} +hg_scanner::~hg_scanner() +{ + close(true); + + if (thread_usb_read_.get() && thread_usb_read_->joinable()) + thread_usb_read_->join(); + if (thread_img_handle_.get() && thread_img_handle_->joinable()) + thread_img_handle_->join(); + + name_.insert(0, "\350\256\276\345\244\207 鈥"); + name_ += "鈥漒345\267\262\347\273\217\345\205\263\351\227\255\343\200\202"; + notify_ui_working_status(name_.c_str(), SANE_EVENT_SCANNER_CLOSED); + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "%s(%s) destroyed.\n", name_.c_str(), hg_log::format_ptr(this).c_str()); +} + +std::string hg_scanner::strerr(hg_err err) +{ + RETURN_IF(err, HG_ERR_OK); + RETURN_IF(err, HG_ERR_INVALID_PARAMETER); + RETURN_IF(err, HG_ERR_INSUFFICIENT_MEMORY); + RETURN_IF(err, HG_ERR_ACCESS_DENIED); + RETURN_IF(err, HG_ERR_IO_PENDING); + RETURN_IF(err, HG_ERR_NOT_EXACT); + RETURN_IF(err, HG_ERR_CONFIGURATION_CHANGED); + RETURN_IF(err, HG_ERR_NOT_OPEN); + RETURN_IF(err, HG_ERR_NOT_START); + RETURN_IF(err, HG_ERR_NO_DATA); + RETURN_IF(err, HG_ERR_HAS_DATA_YET); + RETURN_IF(err, HG_ERR_OUT_OF_RANGE); + RETURN_IF(err, HG_ERR_IO); + RETURN_IF(err, HG_ERR_TIMEOUT); + RETURN_IF(err, HG_ERR_CREATE_FILE_FAILED); + RETURN_IF(err, HG_ERR_WRITE_FILE_FAILED); + RETURN_IF(err, HG_ERR_OPENED_BY_OTHER_PROCESS); + + // 2锛歎SB閿欒 + RETURN_IF(err, HG_ERR_USB_INIT_FAILED); + RETURN_IF(err, HG_ERR_USB_REGISTER_PNP_FAILED); + RETURN_IF(err, HG_ERR_USB_CLAIM_INTERFACE_FAILED); + + // 3锛氱‖浠堕敊璇 + RETURN_IF(err, HG_ERR_DEVICE_NOT_FOUND); + RETURN_IF(err, HG_ERR_DEVICE_NOT_SUPPORT); + RETURN_IF(err, HG_ERR_DEVICE_BUSY); + RETURN_IF(err, HG_ERR_DEVICE_STOPPED); + RETURN_IF(err, HG_ERR_DEVICE_COVER_OPENNED); + RETURN_IF(err, HG_ERR_DEVICE_NO_PAPER); + RETURN_IF(err, HG_ERR_DEVICE_FEEDING_PAPER); + RETURN_IF(err, HG_ERR_DEVICE_DOUBLE_FEEDING); + RETURN_IF(err, HG_ERR_DEVICE_PAPER_JAMMED); + RETURN_IF(err, HG_ERR_DEVICE_STAPLE_ON); + RETURN_IF(err, HG_ERR_DEVICE_PAPER_SKEW); + RETURN_IF(err, HG_ERR_DEVICE_SIZE_CHECK); + RETURN_IF(err, HG_ERR_DEVICE_DOGEAR); + RETURN_IF(err, HG_ERR_DEVICE_NO_IMAGE); + RETURN_IF(err, HG_ERR_DEVICE_SCANN_ERROR); + + char unk[80]; + sprintf(unk, "\346\234\252\347\237\245\351\224\231\350\257\257\357\274\2320x%x", err); + + return unk; +} +std::string hg_scanner::error_description(hg_err err) +{ + RETURN_DESC_IF(err, HG_ERR_OK); + RETURN_DESC_IF(err, HG_ERR_INVALID_PARAMETER); + RETURN_DESC_IF(err, HG_ERR_INSUFFICIENT_MEMORY); + RETURN_DESC_IF(err, HG_ERR_ACCESS_DENIED); + RETURN_DESC_IF(err, HG_ERR_IO_PENDING); + RETURN_DESC_IF(err, HG_ERR_NOT_EXACT); + RETURN_DESC_IF(err, HG_ERR_CONFIGURATION_CHANGED); + RETURN_DESC_IF(err, HG_ERR_NOT_OPEN); + RETURN_DESC_IF(err, HG_ERR_NOT_START); + RETURN_DESC_IF(err, HG_ERR_NO_DATA); + RETURN_DESC_IF(err, HG_ERR_HAS_DATA_YET); + RETURN_DESC_IF(err, HG_ERR_OUT_OF_RANGE); + RETURN_DESC_IF(err, HG_ERR_IO); + RETURN_DESC_IF(err, HG_ERR_TIMEOUT); + RETURN_DESC_IF(err, HG_ERR_CREATE_FILE_FAILED); + RETURN_DESC_IF(err, HG_ERR_WRITE_FILE_FAILED); + RETURN_DESC_IF(err, HG_ERR_OPENED_BY_OTHER_PROCESS); + + // 2锛歎SB閿欒 + RETURN_DESC_IF(err, HG_ERR_USB_INIT_FAILED); + RETURN_DESC_IF(err, HG_ERR_USB_REGISTER_PNP_FAILED); + RETURN_DESC_IF(err, HG_ERR_USB_CLAIM_INTERFACE_FAILED); + + // 3锛氱‖浠堕敊璇 + RETURN_DESC_IF(err, HG_ERR_DEVICE_NOT_FOUND); + RETURN_DESC_IF(err, HG_ERR_DEVICE_NOT_SUPPORT); + RETURN_DESC_IF(err, HG_ERR_DEVICE_BUSY); + RETURN_DESC_IF(err, HG_ERR_DEVICE_STOPPED); + RETURN_DESC_IF(err, HG_ERR_DEVICE_COVER_OPENNED); + RETURN_DESC_IF(err, HG_ERR_DEVICE_NO_PAPER); + RETURN_DESC_IF(err, HG_ERR_DEVICE_FEEDING_PAPER); + RETURN_DESC_IF(err, HG_ERR_DEVICE_DOUBLE_FEEDING); + RETURN_DESC_IF(err, HG_ERR_DEVICE_PAPER_JAMMED); + RETURN_DESC_IF(err, HG_ERR_DEVICE_STAPLE_ON); + RETURN_DESC_IF(err, HG_ERR_DEVICE_PAPER_SKEW); + RETURN_DESC_IF(err, HG_ERR_DEVICE_SIZE_CHECK); + RETURN_DESC_IF(err, HG_ERR_DEVICE_DOGEAR); + RETURN_DESC_IF(err, HG_ERR_DEVICE_NO_IMAGE); + RETURN_DESC_IF(err, HG_ERR_DEVICE_SCANN_ERROR); + + char unk[80]; + sprintf(unk, "\346\234\252\347\237\245\351\224\231\350\257\257\357\274\2320x%x", err); + + return unk; +} +std::string hg_scanner::temporary_file(char* tail, char* head) +{ + char buf[128]; + FILE* src = NULL; + + if (!head || *head == 0) + head = (char*)"scan"; + if (!tail) + tail = (char*)""; + + srand(time(NULL)); + sprintf(buf, "/tmp/%s_%04x%s", head, rand(), tail); + while ((src = fopen(buf, "rb"))) + { + fclose(src); + sprintf(buf, "/tmp/%s_%04x%s", head, rand(), tail); + } + + return buf; +} +int hg_scanner::save_2_tempory_file(std::shared_ptr> data, std::string* path_file) +{ + std::string file(hg_scanner::temporary_file((char*)".jpg")); + FILE* dst = fopen(file.c_str(), "wb+"); + int ret = HG_ERR_OK; + + if (dst) + { + ENOSPC; EROFS; strerror(errno); + size_t wrote = fwrite(data->data(), 1, data->size(), dst); + if (wrote == data->size()) + { + if (path_file) + *path_file = file; + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "--->Wrote %u bytes to file '%s'\n", wrote, file.c_str()); + } + else + { + ret = HG_ERR_WRITE_FILE_FAILED; + HG_VLOG_MINI_3(HG_LOG_LEVEL_FATAL, "Failed in writting file(%u/%u) '%s'\n", wrote, data->size(), file.c_str()); + } + fclose(dst); + } + else + { + ret = HG_ERR_CREATE_FILE_FAILED; + HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "Failed in creating file '%s'\n", file.c_str()); + } + + return ret; +} + +void hg_scanner::init_setting_map(int* setting_map, int count) +{ + for (int i = 0; i < count; ++i) + setting_map[i] = -1; +} +void hg_scanner::thread_handle_usb(void) +{ + while (run_) + { + wait_usb_.wait(); + if (!run_) + break; + + if (scan_life_) + { + std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + if (scan_life_) + { + HG_LOG(HG_LOG_LEVEL_FATAL, "image process is still running!\n"); + continue; + } + } + + scan_life_ = new do_when_born_and_dead(this, &hg_scanner::working_begin, &hg_scanner::working_done, NULL); + thread_handle_usb_read(); + if (scan_life_->release() == 0) + scan_life_ = NULL; + } +} +void hg_scanner::thread_image_handle(void) +{ + while (run_) + { + wait_img_.wait(); + if (!run_) + break; + + scan_life_->add_ref(); + thread_handle_image_process(); + if (scan_life_->release() == 0) + scan_life_ = NULL; + } +} +void hg_scanner::get_range(int setting_no, std::vector& range, std::string& def_val, bool& is_range/*range or list*/) +{ + char sn[20]; + std::string type(""); + + sprintf(sn, "%d", setting_no); + range.clear(); + if (setting_jsn_.at(sn).contains("range")) + { + setting_jsn_.at(sn).at("type").get_to(type); + is_range = !setting_jsn_.at(sn).at("range").is_array(); + if (is_range) + { + if (type == "int") + { + int l = 0, u = 0; + setting_jsn_.at(sn).at("range").at("min").get_to(l); + setting_jsn_.at(sn).at("range").at("max").get_to(u); + + char str[20]; + sprintf(str, "%d", l); + range.push_back(str); + sprintf(str, "%d", u); + range.push_back(str); + } + else + { + double l = .0f, u = .0f; + setting_jsn_.at(sn).at("range").at("min").get_to(l); + setting_jsn_.at(sn).at("range").at("max").get_to(u); + + char str[40]; + sprintf(str, "%f", l); + range.push_back(str); + sprintf(str, "%f", u); + range.push_back(str); + } + } + else + { + char str[40]; + for (int i = 0; i < setting_jsn_.at(sn).at("range").size(); ++i) + { + if (type == "int") + { + int v = 0; + setting_jsn_.at(sn).at("range").at(i).get_to(v); + sprintf(str, "%d", v); + range.push_back(str); + } + else if(type == "float") + { + double v = 0; + setting_jsn_.at(sn).at("range").at(i).get_to(v); + sprintf(str, "%f", v); + range.push_back(str); + } + else + { + std::string v(""); + setting_jsn_.at(sn).at("range").at(i).get_to(v); + range.push_back(v); + } + } + } + } + + if (type == "int") + { + int v = 0; + setting_jsn_.at(sn).at("default").get_to(v); + sprintf(sn, "%d", v); + def_val = sn; + } + else if (type == "float") + { + double v = 0; + setting_jsn_.at(sn).at("default").get_to(v); + sprintf(sn, "%f", v); + def_val = sn; + } + else if(type == "string") + setting_jsn_.at(sn).at("default").get_to(def_val); + + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "setting %d has %d range(s) and default value is '%s'\n", setting_no, range.size(), def_val.c_str()); +} +bool hg_scanner::check_range(int setting_no, bool& val) +{ + std::vector range; + std::string init(""), in(val ? "true" : "false"); + bool is_range = false;; + + get_range(setting_no, range, init, is_range); + if (range.size() == 0) + return true; + + for (int i = 0; i < range.size(); ++i) + { + if (in == range[i]) + return true; + } + + val = init == "true" ? true : false; + + return false; +} +bool hg_scanner::check_range(int setting_no, int& val) +{ + std::vector range; + std::string init(""); + bool is_range = false;; + + get_range(setting_no, range, init, is_range); + if (range.size() == 0) + return true; + + if (is_range && range.size() == 2) + { + if (val >= atoi(range[0].c_str()) && val <= atoi(range[1].c_str())) + return true; + + if(val < atoi(range[0].c_str())) + val = atoi(range[0].c_str()); + else + val = atoi(range[1].c_str()); + + return false; + } + else if (!is_range) + { + for (int i = 0; i < range.size(); ++i) + { + if (atoi(range[i].c_str()) == val) + return true; + } + } + + val = atoi(init.c_str()); + + return false; +} +bool hg_scanner::check_range(int setting_no, double& val) +{ + std::vector range; + std::string init(""); + bool is_range = false;; + + get_range(setting_no, range, init, is_range); + if (range.size() == 0) + return true; + + if (is_range && range.size() == 2) + { + if (val >= atof(range[0].c_str()) && val <= atof(range[1].c_str())) + return true; + + if (val < atof(range[0].c_str())) + val = atof(range[0].c_str()); + else + val = atof(range[1].c_str()); + + return false; + } + else if (!is_range) + { + for (int i = 0; i < range.size(); ++i) + { + if (fabs(atof(range[i].c_str()) - val) < .000001) + return true; + } + } + + val = atof(init.c_str()); + + return false; +} +bool hg_scanner::check_range(int setting_no, std::string& val) +{ + std::vector range; + std::string init(""), in(val); + bool is_range = false;; + + get_range(setting_no, range, init, is_range); + if (range.size() == 0) + return true; + + for (int i = 0; i < range.size(); ++i) + { + if (in == range[i]) + return true; + } + + val = init; + + return false; +} +int hg_scanner::restore(int setting_no) +{ + char key[20]; + int sn = setting_no; + std::string val(""); + + sprintf(key, "%d", setting_no); + setting_jsn_.at(key).at("type").get_to(val); + if (val == "string") + { + val = ""; + setting_jsn_.at(key).at("default").get_to(val); + + char* buf = NULL; + int size = 0; + + setting_jsn_.at(key).at("size").get_to(size); + buf = (char*)malloc(size + 4); + bzero(buf, size + 4); + strcpy(buf, val.c_str()); + sn = set_setting(sn, buf, val.length()); + free(buf); + } + else if (val == "int") + { + int v = 0; + setting_jsn_.at(key).at("default").get_to(v); + sn = set_setting(sn, (char*)&v, sizeof(v)); + } + else if (val == "float") + { + double v = .0f; + setting_jsn_.at(key).at("default").get_to(v); + sn = set_setting(sn, (char*)&v, sizeof(v)); + } + else if (val == "bool") + { + bool v = false; + setting_jsn_.at(key).at("default").get_to(v); + sn = set_setting(sn, (char*)&v, sizeof(v)); + } + else + sn = HG_ERR_OK; + + return sn; +} +bool hg_scanner::get_default_value(void* buf, json* jsn) +{ + std::string type(""); + + jsn->at("type").get_to(type); + if (type == "bool") + { + bool v = false; + jsn->at("default").get_to(v); + *((SANE_Bool*)buf) = v; + } + else if (type == "int") + { + int v = 0; + jsn->at("default").get_to(v); + *((SANE_Int*)buf) = v; + } + else if (type == "float") + { + double v = 0; + jsn->at("default").get_to(v); + *((SANE_Fixed*)buf) = SANE_FIX(v); + } + else if (type == "string") + { + type = ""; + jsn->at("default").get_to(type); + strcpy((char*)buf, type.c_str()); + } + else + return false; + + return true; +} +bool hg_scanner::is_to_file(void) +{ + return resolution_ > 200 + || paper_size_ == TwSS::USStatement + || paper_size_ == TwSS::MaxSize + || paper_size_ == TwSS::Trigeminy; +} +void hg_scanner::thread_handle_image_process(void) +{ + while (run_ && !user_cancel_) + { + std::shared_ptr> buffer; + if (imgs_.Size() == 0 && paths_.Size() == 0) + { + if (wait_usb_.is_waiting()) + break; + + this_thread::sleep_for(chrono::milliseconds(30)); + continue; + } + + if (is_to_file()) + { + std::string file(paths_.Take()); + FILE* src = fopen(file.c_str(), "rb+"); + if (src) + { + unsigned length = 0; + + fseek(src, 0, SEEK_END); + length = ftell(src); + fseek(src, 0, SEEK_SET); + buffer.reset(new std::vector); + buffer->resize(length); + fread(buffer->data(), 1, length, src); + fclose(src); + remove(file.c_str()); + } + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "FATAL: open tempory image file '%s' failed.\n", file.c_str()); + } + } + else + { + buffer = imgs_.Take(); + } + + image_process(buffer); + } +} + +void hg_scanner::working_begin(void*) +{ + final_img_index_ = 0; + notify_ui_working_status(STATU_DESC_SCAN_WORKING, SANE_EVENT_WORKING, HG_ERR_OK); +} +void hg_scanner::working_done(void*) +{ + imgs_.Clear(); + while (paths_.Size()) + { + remove(paths_.Take().c_str()); + } + + switch (status_) + { + case HG_ERR_OK: + notify_ui_working_status(STATU_DESC_SCAN_STOPPED, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_BUSY: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_PC_BUSY, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_STOPPED: + notify_ui_working_status(STATU_DESC_SCAN_STOPPED, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_COVER_OPENNED: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_COVER_OPENNED, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_NO_PAPER: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_PAPER, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_FEEDING_PAPER: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_FEEDING_PAPER, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_NOT_FOUND: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NOT_FOUND, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_SLEEPING: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SLEEPING, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_COUNT_MODE: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_COUNT_MODE, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_DOUBLE_FEEDING: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_DOUBLE_FEEDING, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_PAPER_JAMMED: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_PAPER_JAMMED, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_STAPLE_ON: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_STAPLE_ON, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_PAPER_SKEW: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_PAPER_SKEW, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_SIZE_CHECK: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SIZE_CHECK, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_DOGEAR: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_DOGEAR, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_NO_IMAGE: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_IMAGE, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_SCANN_ERROR: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SCANN_ERROR, SANE_EVENT_SCAN_FINISHED, status_); + break; + case HG_ERR_DEVICE_PC_BUSY: + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_PC_BUSY, SANE_EVENT_SCAN_FINISHED, status_); + break; + default: + notify_ui_working_status(user_cancel_ ? STATU_DESC_SCAN_CANCELED : STATU_DESC_SCAN_STOPPED, SANE_EVENT_SCAN_FINISHED, status_); + break; + } + + //notify_ui_working_status(STATU_DESC_SCAN_STOPPED, SANE_EVENT_SCAN_FINISHED, status_); + + if (test_1_paper_) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "scanning mode: finished testing ONE paper, restore to normal scanning.\n"); + } + else + { + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "scanned %d picture(s) and finished with error %s.\n", final_img_index_, hg_scanner::strerr((hg_err)status_).c_str()); + } + + test_1_paper_ = false; +} + +int hg_scanner::setting_restore(void* data) +{ + // restore ... + int count = 0; + + setting_jsn_.at("option_count").get_to(count); + notify_setting_result_ = false; + for (int i = 1; i < count; ++i) + restore(i); + notify_setting_result_ = true; + + return HG_ERR_CONFIGURATION_CHANGED; +} +int hg_scanner::setting_help(void* data) +{ + int ret = HG_ERR_OK; + + if (name_ == "鍗庨珮鎵弿浠擥100") //淇濈暀 + { + /* code */ + } + + std::string helpfile = helpfile_; + std::string com = "xdg-open ";//娉ㄦ剰绌烘牸淇濈暀 + +#ifdef WIN32 + FILE* src = fopen(helpfile.c_str(), "rb"); + if (src) + fclose(src); + else +#else + if (access(helpfile.c_str(),F_OK) == -1) +#endif + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"App_Help_pdf path is:%s\r\n",hg_scanner::strerr((hg_err)ret).c_str()); + ret = HG_ERR_OPEN_FILE_FAILED; + return ret ; + } + + com += helpfile; + system(com.c_str()); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"App_Help_pdf path is:%s system is:%d\r\n",helpfile.c_str()); + return ret; +} +int hg_scanner::setting_color_mode(void* data) +{ + std::string str((char*)data); + int old = image_prc_param_.bits.color_mode, + sub = HG_ERR_OK, + val = 0, + ret = HG_ERR_OK; + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_COLOR_MODE], str); + + val = image_prc_param_.bits.color_mode = match_best_color_mode(str, NULL); + + sub = on_color_mode_changed(val); + image_prc_param_.bits.color_mode = val; + + if (sub == HG_ERR_NOT_EXACT) + { + image_prc_param_.bits.color_mode = old; + str = color_mode_string(image_prc_param_.bits.color_mode); + ret = HG_ERR_NOT_EXACT; + } + else if (sub) + { + ret = sub; + image_prc_param_.bits.color_mode = old; + } + else if (!exact) + { + ret = HG_ERR_NOT_EXACT; + } + + is_auto_matic_color = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? true :false; // 绛変簬COLOR_MODE_AUTO_MATCH 鐨勬椂鍊欓鑹叉ā寮忛渶瑕佸彉涓2 褰╄壊妯″紡鍥惧儚鍙傛暟鍜岀‖浠跺弬鏁伴兘濡傛 + + + HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Change color mode from %s to %s = %s color is =%s\n", color_mode_string(old).c_str(), (char*)data, hg_scanner::strerr((hg_err)ret).c_str(),str.c_str()); + if(ret == HG_ERR_NOT_EXACT) + strcpy((char*)data, str.c_str()); + + return ret; +} +int hg_scanner::setting_multi_out(void *data) +{ + int ret = HG_ERR_OK; + if(image_prc_param_.bits.color_mode != 2) + { + ret = HG_ERR_INVALID_PARAMETER; + return ret; + } + std::string str((char*)data); + + bool exact = check_range(setting_map_[HG_BASE_SETTING_MULTI_OUT], str); + image_prc_param_.bits.multi_out = match_best_multi_out(str,NULL); + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "set multi_out type from %s to %s = %s\n", multi_out_string(image_prc_param_.bits.multi_out).c_str(), (char*)data, hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} +int hg_scanner::setting_rid_color(void* data) +{ + std::string str((char*)data); + int ret = HG_ERR_OK, + color = -1, + old = image_prc_param_.bits.rid_color; + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_ERASE_COLOR], str); + + image_prc_param_.bits.rid_color = match_best_rid_color(str, NULL); + + + on_color_mode_changed(color); + + if (!exact) + { + ret = HG_ERR_NOT_EXACT; + strcpy((char*)data, str.c_str()); + } + + return ret; +} +int hg_scanner::setting_rid_multi_red(void* data) +{ + image_prc_param_.bits.rid_red = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_rid_answer_red(void* data) +{ + image_prc_param_.bits.rid_answer_red = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_erase_background(void* data) +{ + image_prc_param_.bits.erase_bakground = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_erase_background_range(void* data) +{ + int ret = HG_ERR_OK; + + erase_bkg_range_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE], erase_bkg_range_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = erase_bkg_range_; + } + + return ret; +} +int hg_scanner::setting_noise_optimize(void* data) +{ + image_prc_param_.bits.noise_optimize = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_noise_optimize_range(void* data) +{ + int ret = HG_ERR_OK; + + noise_range_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE], noise_range_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = noise_range_; + } + + return ret; +} +int hg_scanner::setting_paper(void* data) +{ + std::string paper((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_PAPER], paper); + int ret = HG_ERR_OK, sub = HG_ERR_OK, + val = 0, + old = image_prc_param_.bits.paper; + + val = image_prc_param_.bits.paper = match_best_paper(paper, NULL); + sub = on_paper_changed(val); + image_prc_param_.bits.paper = val; + if (sub == HG_ERR_NOT_EXACT) + { + ret = sub; + paper = paper_string(image_prc_param_.bits.paper); + } + else if (sub) + { + ret = sub; + image_prc_param_.bits.paper = old; + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Change paper from %s to %s = %s\n", paper_string(old).c_str(), (char*)data, hg_scanner::strerr((hg_err)ret).c_str()); + + if(ret == HG_ERR_NOT_EXACT) + strcpy((char*)data, paper.c_str()); + + return ret; +} +int hg_scanner::setting_paper_check(void* data) +{ + bool use = *((bool*)data); + int ret = on_paper_check_changed(use); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Change paper size-checking %s = %s\n", *((bool*)data) ? "enabled" : "disabled", hg_scanner::strerr((hg_err)ret).c_str()); + *((bool*)data) = use; + + return ret; +} +int hg_scanner::setting_page(void* data) +{ + std::string val((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_PAGE], val); + int ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Change page from %s to %s = %s\n", page_string(image_prc_param_.bits.page).c_str(), (char*)data, hg_scanner::strerr((hg_err)ret).c_str()); + image_prc_param_.bits.page = match_best_page(val, NULL); + if (!exact) + strcpy((char*)data, val.c_str()); + + return ret; +} +int hg_scanner::setting_page_omit_empty(void* data) +{ + int ret = HG_ERR_OK; + + omit_empty_level_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL], omit_empty_level_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = omit_empty_level_; + } + + return ret; +} +int hg_scanner::setting_resolution(void* data) +{ + int ret = HG_ERR_OK, + old = resolution_, + sub = HG_ERR_OK; + + resolution_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_RESOLUTION], resolution_)) + ret = HG_ERR_NOT_EXACT; + sub = on_resolution_changed(resolution_); + if (sub == HG_ERR_NOT_EXACT) + ret = sub; + else if (sub) + ret = sub; + + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Change resolution from %d to %d = %s\n", old, *((int*)data), hg_scanner::strerr((hg_err)ret).c_str()); + *((int*)data) = resolution_; + + return ret; +} +int hg_scanner::setting_exchagnge(void* data) +{ + image_prc_param_.bits.exchange = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_split_image(void* data) +{ + image_prc_param_.bits.split = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_automatic_skew(void* data) +{ + // automatic_skew_detection_ = *((bool*)data); + image_prc_param_.bits.automatic_skew = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_rid_hole(void* data) +{ + image_prc_param_.bits.rid_hole = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_rid_hoe_range(void* data) +{ + int ret = HG_ERR_OK; + + rid_hole_range_ = *((double*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_RID_HOLE_RANGE], rid_hole_range_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = rid_hole_range_; + } + rid_hole_range_*=100; + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "rid_hole_range_ = %f\r\n", rid_hole_range_); + + return ret; +} +int hg_scanner::setting_bright(void* data) +{ + int ret = HG_ERR_OK; + + bright_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_BRIGHT], bright_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = bright_; + } + + return ret; +} +int hg_scanner::setting_contrast(void* data) +{ + int ret = HG_ERR_OK; + + contrast_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_CONTRAST], contrast_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = contrast_; + } + + return ret; +} +int hg_scanner::setting_gamma(void* data) +{ + int ret = HG_ERR_OK; + + gamma_ = *((double*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_GAMMA], gamma_)) + { + ret = HG_ERR_NOT_EXACT; + *((double*)data) = gamma_; + } + + return ret; +} +int hg_scanner::setting_sharpen(void* data) +{ + std::string str((char*)data); + int ret = HG_ERR_OK; + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_SHARPEN], str); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Change sharpen from %s to %s = ", sharpen_string(image_prc_param_.bits.sharpen).c_str(), (char*)data); + + image_prc_param_.bits.sharpen = match_best_sharpen(str, NULL); + if (!exact) + { + strcpy((char*)data, str.c_str()); + ret = HG_ERR_NOT_EXACT; + } + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "%d\n", ret); + + return ret; +} +int hg_scanner::setting_dark_sample(void* data) +{ + image_prc_param_.bits.dark_sample = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_erase_black_frame(void* data) +{ + image_prc_param_.bits.erase_black_frame = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_threshold(void* data) +{ + int ret = HG_ERR_OK; + + threshold_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_THRESHOLD], threshold_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = threshold_; + } + + return ret; +} +int hg_scanner::setting_anti_noise(void* data) +{ + int ret = HG_ERR_OK; + + anti_noise_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL], anti_noise_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = anti_noise_; + } + + return ret; +} +int hg_scanner::setting_margin(void* data) +{ + int ret = HG_ERR_OK; + + margin_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_MARGIN], margin_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = margin_; + } + + return ret; +} +int hg_scanner::setting_filling_background(void* data) +{ + std::string str((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_FILL_BACKGROUND], str); + int ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + image_prc_param_.bits.fill_background = match_best_fill_background(str, NULL); + if (!exact) + { + strcpy((char*)data, str.c_str()); + ret = HG_ERR_NOT_EXACT; + } + + return ret; +} +int hg_scanner::setting_is_permeate(void* data) +{ + image_prc_param_.bits.is_permeate = *((bool*)data); + return HG_ERR_OK; +} +int hg_scanner::setting_is_permeate_lv(void* data) +{ + int ret = HG_ERR_OK; + std::string str((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_PERMEATE_LV], str); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Change is_permeate_lv from %s to %s = ", is_permaeate_string(image_prc_param_.bits.is_permeate_lv_).c_str(), (char*)data); + image_prc_param_.bits.is_permeate_lv_ = match_best_permaeate_lv(str, NULL); + + if (!exact) + { + strcpy((char*)data, str.c_str()); + ret = HG_ERR_NOT_EXACT; + } + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "%d\n", ret); + + return ret; + +} +int hg_scanner::setting_remove_morr(void* data) +{ + image_prc_param_.bits.remove_morr = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_error_extention(void* data) +{ + image_prc_param_.bits.error_extention = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_remove_texture(void* data) +{ + image_prc_param_.bits.remove_txtture = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_ultrasonic_check(void* data) +{ + bool use = *((bool*)data); + int ret = on_ultrasonic_check_changed(use); + + if (ret) + *((bool*)data) = use; + + return ret; +} +int hg_scanner::setting_staple_check(void* data) +{ + bool use = *((bool*)data); + int ret = on_staple_check_changed(use); + + if (ret) + *((bool*)data) = use; + + return ret; +} +int hg_scanner::setting_scan_mode(void* data) +{ + std::string str((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_SCAN_MODE], str); + int ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (strcmp(str.c_str(), HUAGAO_SETTING_STR_SCAN_MODE_CONTINUOUS) == 0) + { + scan_count_ = -1; + } + else + { + char key[20]; + sprintf(key, "%d", setting_map_[HG_BASE_SETTING_INDEX_SCAN_COUNT]); + setting_jsn_.at(key).at("cur").get_to(scan_count_); + } + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "set scanning pages to %d\n", scan_count_); + + return ret; +} +int hg_scanner::setting_scan_count(void* data) +{ + int ret = HG_ERR_OK; + char key[20]; + std::string val(""); + + sprintf(key, "%d", setting_map_[HG_BASE_SETTING_INDEX_SCAN_MODE]); + setting_jsn_.at(key).at("cur").get_to(val); + if (val == HUAGAO_SETTING_STR_SCAN_MODE_CONTINUOUS) + { + scan_count_ = -1; + } + else + { + scan_count_ = *((int*)data); + } + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "set scanning pages to %d\n", scan_count_); + + return ret; +} +int hg_scanner::setting_text_direction(void* data) +{ + std::string str((char*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_TEXT_DIRECTION], str); + int ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Change text direction from '%s' to '%s' = %s\n", text_direction_string(image_prc_param_.bits.text_direction).c_str() + , (char*)data, hg_scanner::strerr((hg_err)ret).c_str()); + image_prc_param_.bits.text_direction = match_best_text_direction(str, NULL); + if (!exact) + strcpy((char*)data, str.c_str()); + + return ret; +} +int hg_scanner::setting_rotate_bkg_180(void* data) +{ + image_prc_param_.bits.rotate_back_180 = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_fractate_check(void* data) +{ + image_prc_param_.bits.fractate_check = *((bool*)data); + + return HG_ERR_OK; +} +int hg_scanner::setting_fractate_check_level(void* data) +{ + int ret = HG_ERR_OK; + + fractate_level_ = *((int*)data); + if (!check_range(setting_map_[HG_BASE_SETTING_INDEX_FRACTATE_CHECK_LEVEL], fractate_level_)) + { + ret = HG_ERR_NOT_EXACT; + *((int*)data) = fractate_level_; + } + + return ret; +} +int hg_scanner::setting_skew_check(void* data) +{ + bool use = *((bool*)data); + int ret = on_skew_check_changed(use); + + if (ret) + *((bool*)data) = use; + + return ret; +} +int hg_scanner::setting_skew_check_level(void* data) +{ + int level = *((int*)data); + bool exact = check_range(setting_map_[HG_BASE_SETTING_INDEX_SKEW_CHECK_LEVEL], level); + int ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT, + sub = on_skew_check_level_changed(level); + + if (sub) + { + ret = sub; + } + if (ret) + *((int*)data) = level; + + return ret; +} + +int hg_scanner::on_color_mode_changed(int& color_mode) +{ + return HG_ERR_OK; +} +int hg_scanner::on_paper_changed(int& paper) +{ + return HG_ERR_OK; +} +int hg_scanner::on_paper_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner::on_resolution_changed(int& dpi) +{ + return HG_ERR_OK; +} +int hg_scanner::on_ultrasonic_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner::on_staple_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner::on_skew_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner::on_skew_check_level_changed(int& check) +{ + return HG_ERR_OK; +} +void hg_scanner::on_device_reconnected(void) +{ + std::lock_guard lock(io_lock_); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "%04x:%04x reconnected.\n", io_->get_vid(), io_->get_pid()); +} +int hg_scanner::set_setting_value(int setting_no, void* data, int len) +{ + return HG_ERR_OK; +} +int hg_scanner::on_scanner_closing(bool force) +{ + return HG_ERR_OK; +} +void hg_scanner::thread_handle_usb_read(void) +{ +} + +void hg_scanner::init_settings(const char* json_setting_text) +{ + setting_jsn_ = jsonconfig::load_json_from_text(json_setting_text); + + int count = 0; + + notify_setting_result_ = false; + setting_jsn_.at("option_count").get_to(count); + for (int sn = 1; sn < count; ++sn) + { + char key[20]; + std::string val(""); + + sprintf(key, "%d", sn); + setting_jsn_.at(key).at("type").get_to(val); + if (val == "string") + { + val = ""; + setting_jsn_.at(key).at("default").get_to(val); + + char* buf = NULL; + int size = 0; + + setting_jsn_.at(key).at("size").get_to(size); + buf = (char*)malloc(size + 4); + bzero(buf, size + 4); + strcpy(buf, val.c_str()); + set_setting(sn, buf, val.length()); + free(buf); + } + else if (val == "int") + { + int v = 0; + setting_jsn_.at(key).at("default").get_to(v); + set_setting(sn, (char*)&v, sizeof(v)); + } + else if (val == "float") + { + double v = .0f; + setting_jsn_.at(key).at("default").get_to(v); + set_setting(sn, (char*)&v, sizeof(v)); + } + else if (val == "bool") + { + bool v = false; + setting_jsn_.at(key).at("default").get_to(v); + set_setting(sn, (char*)&v, sizeof(v)); + } + } + setting_count_ = count; + notify_setting_result_ = true; +} +int hg_scanner::on_scann_error(int err) +{ + status_ = err; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "[xxx]Device status: 0x%x\n", err); + + unsigned int e = err; + + return ui_ev_cb_((scanner_handle)this, SANE_EVENT_ERROR, (void*)hg_scanner::strerr((hg_err)err).c_str(), &e, NULL); +} +int hg_scanner::notify_ui_working_status(const char* msg, int ev, int status) +{ + unsigned int s = status; + + return ui_ev_cb_((scanner_handle)this, ev, (void*)msg, &s, NULL); +} +bool hg_scanner::waiting_for_memory_enough(unsigned need_bytes) +{ + return true; // disable the memory control - added on 2022-03-22 + + bool ret = true; + + if(cb_mem_) + { + int ret = ui_ev_cb_((scanner_handle)this, SANE_EVENT_IS_MEMORY_ENOUGH, NULL, &need_bytes, NULL); + + if (ret == HG_ERR_INSUFFICIENT_MEMORY) + { + user_cancel_ = true; + status_ = HG_ERR_INSUFFICIENT_MEMORY; + ret = false; + } + else if (ret == HG_ERR_NOT_ANY_MORE) + cb_mem_ = false; + } + else + { + need_bytes *= 4; + + void* buf = malloc(need_bytes); + bool first = true; + while (!buf && !user_cancel_) + { + if (first) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "Memory is too small for aquiring image(%u bytes), wait for ENOUGH ...\n", need_bytes); + notify_ui_working_status(STATU_DESC_WAIT_FOR_MEM); + first = false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + buf = malloc(need_bytes); + } + if (!first) + { + if (buf) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "waited for memory need(%u)\n", need_bytes); + } + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "canceled by user while waiting for memory need(%u)\n", need_bytes); + } + } + ret = (!user_cancel_) && (buf != NULL); + if (buf) + free(buf); + } + + return ret; +} + +void hg_scanner::copy_to_sane_image_header(SANE_Parameters* header, int w, int h, int line_bytes, int channels) +{ + if (channels == 3) + header->format = SANE_FRAME_RGB; + else + header->format = SANE_FRAME_GRAY; + header->depth = 8; // 姝ゅ鎸囨瘡涓涓鑹插垎閲忕殑浣嶆繁锛屾垜浠殑鎵弿浠浐瀹氫负鈥8鈥 + header->last_frame = true; // 涓骞呭浘鐗囧鏋滃悇涓垎閲忕浉浜掑垎绂伙紝鍒欐渶鍚庝竴涓垎閲忕殑鏃跺欒缃负true銆傚僵鑹插浘鍍廟GB鏃朵篃鍙湁涓鈥滃抚鈥濓紝鎵浠ヤ篃涓簍rue + header->pixels_per_line = w; + header->lines = h; + header->bytes_per_line = line_bytes; +} +int hg_scanner::save_usb_data(std::shared_ptr> data) +{ + int ret = HG_ERR_OK; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "USB read one picture with %u bytes\n", data->size()); + if (is_to_file()) + { + std::string file(""); + + ret = hg_scanner::save_2_tempory_file(data, &file); + if (ret == HG_ERR_OK) + paths_.Put(file); + } + else + { + imgs_.Put(data); + +#ifdef SAVE_TO_FILE + hg_scanner::save_2_tempory_file(data, NULL); +#endif + } + if (wait_img_.is_waiting()) + wait_img_.notify(); + + return ret; +} +int hg_scanner::save_final_image(hg_imgproc::LPIMGHEAD head, void* buf) +{ + final_img_index_++; + if (async_io_) + { + SANE_Image img; + copy_to_sane_image_header(&img.header, head->width, head->height, head->line_bytes, head->channels); + img.data = (unsigned char*)buf; + img.bytes = head->total_bytes; + + return ui_ev_cb_((scanner_handle)this, SANE_EVENT_IMAGE_OK, &img, &final_img_index_, NULL); + } + + final_imgs_.put(head->width, head->height, head->bits, head->channels, head->line_bytes, buf, head->total_bytes); + + return HG_ERR_OK; +} + +int hg_scanner::reset_io(usb_io* io) +{ + online_ = false; + if (!io) + return HG_ERR_INVALID_PARAMETER; + + { + std::lock_guard lock(io_lock_); + + usb_io* old = io_; + + io->add_ref(); + io_ = io; + status_ = io_->last_error(); + online_ = status_ == HG_ERR_OK; + if (old) + old->release(); + } + on_device_reconnected(); + + return HG_ERR_OK; +} +int hg_scanner::io_disconnected(void) +{ + std::lock_guard lock(io_lock_); + + online_ = false; + io_->on_disconnected(); + + return HG_ERR_OK; +} +void hg_scanner::set_ui_callback(sane_callback cb, bool enable_async_io) +{ + ui_ev_cb_ = cb ? cb : ui_default_callback; + async_io_ = enable_async_io; +} +int hg_scanner::get_pid(void) +{ + std::lock_guard lock(io_lock_); + + return io_->get_pid(); +} +int hg_scanner::get_vid(void) +{ + std::lock_guard lock(io_lock_); + + return io_->get_vid(); +} +int hg_scanner::close(bool force) +{ + int ret = on_scanner_closing(force); + + online_ = false; + if (ret == HG_ERR_OK) + { + run_ = false; + wait_usb_.notify(); + wait_img_.notify(); + if(!scan_life_) + { + std::lock_guard lock(io_lock_); + + if (io_) + { + io_->close(); + io_->release(); + io_ = NULL; + } + } + else if (io_) + { + HG_LOG(HG_LOG_LEVEL_WARNING, "close scanner: USB thread or Image thread is still running.\n"); + io_->close(); + io_->release(); + io_ = NULL; + } + + status_ = HG_ERR_NOT_OPEN; + } + + return ret; +} +int hg_scanner::set_setting(int setting_no, void* data, int len) +{ + // NOTE: the array of 'func' must be ONE to ONE of the HG_BASE_SETTING_INDEX_xxx + int(hg_scanner:: * func[])(void*) = { &hg_scanner::setting_restore + , &hg_scanner::setting_help + , &hg_scanner::setting_color_mode + , &hg_scanner::setting_multi_out + , &hg_scanner::setting_rid_color + , &hg_scanner::setting_rid_multi_red + , &hg_scanner::setting_rid_answer_red + , &hg_scanner::setting_erase_background + , &hg_scanner::setting_erase_background_range + , &hg_scanner::setting_noise_optimize + , &hg_scanner::setting_noise_optimize_range + , &hg_scanner::setting_paper + , &hg_scanner::setting_paper_check + , &hg_scanner::setting_page + , &hg_scanner::setting_page_omit_empty + , &hg_scanner::setting_resolution + , &hg_scanner::setting_exchagnge + , &hg_scanner::setting_split_image + , &hg_scanner::setting_automatic_skew + , &hg_scanner::setting_rid_hole + , &hg_scanner::setting_rid_hoe_range + , &hg_scanner::setting_bright + , &hg_scanner::setting_contrast + , &hg_scanner::setting_gamma + , &hg_scanner::setting_sharpen + , &hg_scanner::setting_dark_sample + , &hg_scanner::setting_erase_black_frame + , &hg_scanner::setting_threshold + , &hg_scanner::setting_anti_noise + , &hg_scanner::setting_margin + , &hg_scanner::setting_filling_background + , &hg_scanner::setting_is_permeate + , &hg_scanner::setting_is_permeate_lv + , &hg_scanner::setting_remove_morr + , &hg_scanner::setting_error_extention + , &hg_scanner::setting_remove_texture + , &hg_scanner::setting_ultrasonic_check + , &hg_scanner::setting_staple_check + , &hg_scanner::setting_scan_mode + , &hg_scanner::setting_scan_count + , &hg_scanner::setting_text_direction + , &hg_scanner::setting_rotate_bkg_180 + , &hg_scanner::setting_fractate_check + , &hg_scanner::setting_fractate_check_level + , &hg_scanner::setting_skew_check + , &hg_scanner::setting_skew_check_level + + }; + + bool hit = false; + int ret = HG_ERR_OUT_OF_RANGE; + char sn[20]; + + sprintf(sn, "%d", setting_no); + for (size_t i = 0; i < ARRAY_SIZE(func); ++i) + { + if (setting_map_[i] == setting_no) + { + ret = (this->*func[i])(data); + hit = true; + break; + } + } + if (!hit) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "Setting %d is not found in base setting functions.\n", setting_no); + ret = set_setting_value(setting_no, data, len); + } + + if (ret == HG_ERR_OK || ret == HG_ERR_NOT_EXACT || ret == HG_ERR_CONFIGURATION_CHANGED) + { + std::string type(""), name(""); + + setting_jsn_.at(sn).at("type").get_to(type); + setting_jsn_.at(sn).at("title").get_to(name); + if (type == "string") + { + setting_jsn_.at(sn).at("cur") = (char*)data; + type = (char*)data; + } + else if (type == "int") + { + setting_jsn_.at(sn).at("cur") = *((int*)data); + sprintf(sn, "%d", *((int*)data)); + type = sn; + } + else if (type == "float") + { + setting_jsn_.at(sn).at("cur") = *((double*)data); + sprintf(sn, "%f", *((double*)data)); + type = sn; + } + else if (type == "bool") + { + setting_jsn_.at(sn).at("cur") = *((bool*)data); + type = *((bool*)data) ? "true" : "false"; + } + + if (notify_setting_result_) + { + name.insert(0, "璁剧疆 鈥"); + name += "鈥 鍊间负鈥"; + name += type + "鈥 鎴愬姛銆"; + notify_ui_working_status(name.c_str()); + } + } + else if (notify_setting_result_) + { + std::string name(""); + if (setting_jsn_.contains(sn)) + { + setting_jsn_.at(sn).at("title").get_to(name); + } + else + name = std::string("璁剧疆椤") + sn; + name.insert(0, "璁剧疆 鈥"); + name += "鈥 鍊煎け璐: " + hg_scanner::strerr((hg_err)ret); + notify_ui_working_status(name.c_str()); + } + + return ret; +} +int hg_scanner::get_setting(int setting_no, char* json_txt_buf, int* len) +{ + if (!len) + return HG_ERR_INVALID_PARAMETER; + + if (setting_no == 0) + { + int count = 0; + setting_jsn_.at("option_count").get_to(count); + *len = count; + + return HG_ERR_OK; + } + if (setting_count_ <= setting_no || setting_no < 0) + return HG_ERR_OUT_OF_RANGE; + + char sn[20]; + std::string text(""); + + sprintf(sn, "%d", setting_no); + if (!setting_jsn_.contains(sn)) + HG_VLOG_MINI_2(HG_LOG_LEVEL_FATAL, "!!!option(%d - %s) is not found.\n", setting_no, sn); + text = setting_jsn_.at(sn).dump(); + if (*len <= text.length()) + { + *len = text.length() + 8; + + return HG_ERR_INSUFFICIENT_MEMORY; + } + + strcpy(json_txt_buf, text.c_str()); + *len = text.length(); + + return HG_ERR_OK; +} +std::string hg_scanner::name(void) +{ + return name_; +} +int hg_scanner::status(void) +{ + return status_; +} +bool hg_scanner::is_online(void) +{ + return online_; +} + +int hg_scanner::start(void) +{ + user_cancel_ = false; + + return status_; +} +int hg_scanner::get_image_info(SANE_Parameters* ii) +{ + int ret = HG_ERR_OK; + IMH imh; + + bzero(&imh, sizeof(imh)); + while ((!wait_img_.is_waiting() || !wait_usb_.is_waiting()) && final_imgs_.Size() <= 0) + this_thread::sleep_for(chrono::milliseconds(10)); + + if (final_imgs_.Size() <= 0) + ret = HG_ERR_NO_DATA; + else + { + if (!final_imgs_.front(&imh)) + ret = HG_ERR_NO_DATA; + else + copy_to_sane_image_header(ii, imh.width, imh.height, imh.line_bytes, imh.channels); + } + HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Get image info(%d * %d * %d) = %s\n", ii->pixels_per_line, ii->lines, imh.bits, hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} +int hg_scanner::read_image_data(unsigned char* buf, int* len) +{ + if (!len) + return HG_ERR_INVALID_PARAMETER; + + if (!buf) + { + IMH imh; + final_imgs_.front(&imh); + *len = imh.bytes; + + return HG_ERR_INSUFFICIENT_MEMORY; + } + + if (final_imgs_.Size() > 0) + { + int fetch = *len; + bool over = false; + + final_imgs_.fetch_front(buf, len, &over); + + return over ? HG_ERR_NO_DATA : HG_ERR_OK; + } + else + return HG_ERR_NO_DATA; +} +int hg_scanner::stop(void) +{ + user_cancel_ = true; + + return status_; +} +int hg_scanner::reset(void) +{ + return status_; +} +int hg_scanner::device_io_control(unsigned long code, void* data, unsigned* len) +{ + if (code == IO_CTRL_CODE_RESTORE_SETTINGS) + { + setting_restore(data); + + return HG_ERR_CONFIGURATION_CHANGED; + } + else if (code == IO_CTRL_CODE_GET_DEFAULT_VALUE) + { + char* jsn_txt = NULL; + int size = 0; + json jsn; + int ret = get_setting(*len, jsn_txt, &size); + + if (ret == HG_ERR_INSUFFICIENT_MEMORY) + { + jsn_txt = (char*)malloc(size + 4); + bzero(jsn_txt, size + 4); + get_setting(*len, jsn_txt, &size); + jsn = jsonconfig::load_json_from_text(jsn_txt); + free(jsn_txt); + if (get_default_value(data, &jsn)) + ret = HG_ERR_OK; + else + ret = HG_ERR_DATA_DAMAGED; + } + + return ret; + } + else if (code == IO_CTRL_CODE_CLEAR_ROLLER_COUNT) + { + int count = get_roller_num(); + + if (len) + *len = count; + + return clear_roller_num(); + } + else if (code == IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT) + { + SANE_FinalImgFormat* fmt = (SANE_FinalImgFormat*)data; + + return set_final_image_format(fmt); + } + else if(code == IO_CTRL_CODE_TEST_SINGLE) + { + return set_leaflet_scan(); + } + else if (code == IO_CTRL_CODE_SET_AUTO_COLOR_TYPE) + { + return set_auto_color_type(); + } + else if(code == IO_CTRL_CODE_GET_HARDWARE_VERSION) + { + + std::string fw = get_firmware_version(); + if (*len < fw.size()) + { + *len = fw.size(); + return HG_ERR_INSUFFICIENT_MEMORY; + } + + char* buf = strcpy((char*)data, fw.c_str()); + + if (buf) + { + return HG_ERR_OK; + } + return HG_ERR_DATA_DAMAGED; + } + else if(code == IO_CTRL_CODE_GET_SERIAL) + { + std::string ser = get_serial_num(); + if (*len < ser.size()) + { + *len = ser.size(); + return HG_ERR_INSUFFICIENT_MEMORY; + } + char* buf = strcpy((char*)data, ser.c_str()); + if (buf) + { + return HG_ERR_OK; + } + return HG_ERR_DATA_DAMAGED; + } + else if (code == IO_CTRL_CODE_GET_HARDWARE_VERSION) + { + std::string ip = get_ip(); + if (*len < ip.size()) + { + *len = ip.size(); + return HG_ERR_INSUFFICIENT_MEMORY; + } + char* buf = strcpy((char*)data, ip.c_str()); + + if (buf) + { + return HG_ERR_OK; + } + return HG_ERR_DATA_DAMAGED; + } + else if (code == IO_CTRL_CODE_GET_PAPER_ON) + { + return get_scanner_paperon((SANE_Bool*)data); + } + else if(code == IO_CTRL_CODE_GET_POWER_LEVEL) + { + int val = 0, + ret = HG_ERR_OK; + + if (*len < sizeof(int *)) + { + *len = sizeof(int *); + return HG_ERR_INSUFFICIENT_MEMORY; + } + + ret = get_sleep_time(val); + if (ret == HG_ERR_OK) + { + *((int *)data) = val; + } + return ret; + } + else if(code == IO_CTRL_CODE_SET_POWER_LEVEL) + { + int val = *((int*)data); + int sleeptime = 0; + + switch (val) + { + case SANE_POWER_NONE: + sleeptime = 0; + break; + case SANE_POWER_MINUTES_5: + sleeptime = 300; + break; + case SANE_POWER_MINUTES_10: + sleeptime = 600; + break; + case SANE_POWER_MINUTES_20: + sleeptime = 1200; + break; + case SANE_POWER_MINUTES_30: + sleeptime = 1800; + break; + case SANE_POWER_MINUTES_60: + sleeptime = 3600; + break; + case SANE_POWER_MINUTES_120: + sleeptime = 7200; + break; + case SANE_POWER_MINUTES_240: + sleeptime = 14400; + break; + default: + sleeptime = 0; + break; + } + return set_sleep_time(sleeptime); + } + return HG_ERR_DEVICE_NOT_SUPPORT; +} +std::string hg_scanner::get_firmware_version(void) +{ + return ""; +} +std::string hg_scanner::get_serial_num(void) +{ + return ""; +} +std::string hg_scanner::get_ip(void) +{ + return ""; +} +int hg_scanner::get_roller_num(void) +{ + return -1; +} +int hg_scanner::clear_roller_num(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +int hg_scanner::set_leaflet_scan(void) +{ + return HG_ERR_NO_DATA; +} +int hg_scanner::get_abuot_info(void) +{ + + return HG_ERR_NO_DATA; +} +int hg_scanner::restore_default_setting(void) +{ + return HG_ERR_NO_DATA; +} +int hg_scanner::set_final_image_format(SANE_FinalImgFormat* fmt) +{ + switch (fmt->img_format) + { + case SANE_IMAGE_TYPE_BMP: + img_type_ = ".bmp"; + break; + case SANE_IMAGE_TYPE_PNG: + img_type_ = ".png"; + break; + case SANE_IMAGE_TYPE_JPG: + img_type_ = ".jpg"; + break; + case SANE_IMAGE_TYPE_TIFF: + return HG_ERR_INVALID_PARAMETER; + case SANE_IMAGE_TYPE_WEBP: + return HG_ERR_INVALID_PARAMETER; + case SANE_IMAGE_TYPE_PDF: + return HG_ERR_INVALID_PARAMETER; + case SANE_IMAGE_TYPE_GIF: + return HG_ERR_INVALID_PARAMETER; + case SANE_IMAGE_TYPE_SVG: + return HG_ERR_INVALID_PARAMETER; + default: + img_type_ = ""; + break; + } + return HG_ERR_OK; +} + +int hg_scanner::get_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_auto_color_type(void) +{ + is_auto_matic_color = true; + return HG_ERR_OK; +} + +int hg_scanner::get_device_code(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::get_sleep_time(int& getsleepime) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_sleep_time(int sleeptime) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner::get_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::get_scanner_paperon(SANE_Bool* paperon) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::get_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::get_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::set_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner::get_scan_is_sleep(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} \ No newline at end of file diff --git a/hgdriver/hgdev/hg_scanner.h b/hgdriver/hgdev/hg_scanner.h new file mode 100644 index 0000000..02f62cd --- /dev/null +++ b/hgdriver/hgdev/hg_scanner.h @@ -0,0 +1,298 @@ +锘#pragma once + +// hg_scanner is the base class of kinds of scanners +// +// created on 2022-01-30 +// +#include +#include +#include +#include +#include +#include + +#include "usb_manager.h" +#include "../../sdk/hginclude/huagaoxxx_warraper_ex.h" +#include "jsonconfig.h" +#include "../3rdparty/nick/StopWatch.h" +#include "hg_ipc.h" +#include "common_setting.h" +#include "image_process.h" +#include "StopWatch.h" +#ifdef WIN32 +#else +#include +#endif +enum +{ + HG_BASE_SETTING_INDEX_RESTORE_DEFAULT_SETTINGS = 0, // 鎭㈠榛樿璁剧疆 + HG_BASE_SETTING_INDEX_HELP, // 甯姪 + HG_BASE_SETTING_INDEX_COLOR_MODE, // 棰滆壊妯″紡 + HG_BASE_SETTING_MULTI_OUT, // 澶氭祦杈撳嚭 + HG_BASE_SETTING_INDEX_ERASE_COLOR, // 闄よ壊 + HG_BASE_SETTING_INDEX_ERASE_MULTI_OUT_RED, // 澶氭祦杈撳嚭闄ょ孩 + HG_BASE_SETTING_INDEX_ERASE_ANSWER_RED, // 绛旈鍗¢櫎绾 + HG_BASE_SETTING_INDEX_ERASE_BACKGROUND, // 绉婚櫎鑳屾櫙 + HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE, // 绉婚櫎鑳屾櫙鑼冨洿 + HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE, // 榛戠櫧鍥惧儚鍣偣浼樺寲 + HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE, // 鍣偣浼樺寲灏哄 + HG_BASE_SETTING_INDEX_PAPER, // 绾稿紶灏哄 + HG_BASE_SETTING_INDEX_PAPER_SIZE_CHECK, // 灏哄妫娴 + HG_BASE_SETTING_INDEX_PAGE, // 鎵弿椤甸潰 + HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL, // 璺宠繃绌虹櫧椤电伒鏁忓害 + HG_BASE_SETTING_INDEX_RESOLUTION, // 鍒嗚鲸鐜 + HG_BASE_SETTING_INDEX_EXCHANGE, // 浜ゆ崲姝e弽闈 + HG_BASE_SETTING_INDEX_SPLIT, // 鍥惧儚鎷嗗垎 + HG_BASE_SETTING_INDEX_AUTO_CORRECT, // 鑷姩绾犲亸 + HG_BASE_SETTING_INDEX_RID_HOLE, // 绌垮瓟绉婚櫎 + HG_BASE_SETTING_INDEX_RID_HOLE_RANGE, // 绌垮瓟鎼滅储鑼冨洿 + HG_BASE_SETTING_INDEX_BRIGHT, // 浜害 + HG_BASE_SETTING_INDEX_CONTRAST, // 瀵规瘮搴 + HG_BASE_SETTING_INDEX_GAMMA, // 浼界帥 + HG_BASE_SETTING_INDEX_SHARPEN, // 閿愬寲涓庢ā绯 + HG_BASE_SETTING_INDEX_DARK_SAMPLE, // 娣辫壊鏍峰紶 + HG_BASE_SETTING_INDEX_ERASE_BLACK_FRAME, // 娑堥櫎榛戞 + HG_BASE_SETTING_INDEX_THRESHOLD, // 闃堝 + HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL, // 鑳屾櫙鎶楀櫔绛夌骇 + HG_BASE_SETTING_INDEX_MARGIN, // 杈圭紭缂╄繘 + HG_BASE_SETTING_INDEX_FILL_BACKGROUND, // 鑳屾櫙濉厖鏂瑰紡 + HG_BASE_SETTING_INDEX_PERMEATE, // 闃叉娓楅 + HG_BASE_SETTING_INDEX_PERMEATE_LV, // 闃叉娓楅忕瓑绾 + HG_BASE_SETTING_REMOVE_MORR, // 鍘婚櫎鎽╁皵绾 + HG_BASE_SETTING_ERROR_EXTENTION, // 閿欒鎵╂暎 + HG_BASE_SETTING_REMOVE_TXTTURE, // 闄ょ綉绾 + HG_BASE_SETTING_INDEX_ULTRASONIC_CHECK, // 瓒呭0娉㈡娴 + HG_BASE_SETTING_INDEX_STAPLE_CHECK, // 瑁呰妫娴 + HG_BASE_SETTING_INDEX_SCAN_MODE, // 杩炵画鎵弿鎴栨壂鎻忔寚瀹氬紶鏁 + HG_BASE_SETTING_INDEX_SCAN_COUNT, // 鎵弿鎸囧畾鏁伴噺 + HG_BASE_SETTING_INDEX_TEXT_DIRECTION, // 鏂囩ǹ鏂瑰悜 + HG_BASE_SETTING_INDEX_ROTATE_BKG_180, // 鑳岄潰鏃嬭浆180掳 + HG_BASE_SETTING_INDEX_FRACTATE_CHECK, // 鎶樿妫娴 + HG_BASE_SETTING_INDEX_FRACTATE_CHECK_LEVEL, // 鎶樿妫娴嬪鏉傚害 + HG_BASE_SETTING_INDEX_SKEW_CHECK, // 姝枩妫娴 + HG_BASE_SETTING_INDEX_SKEW_CHECK_LEVEL, // 姝枩妫娴嬪鏉傚害 + + + // 甯歌鐨勫姛鑳界储寮曞0鏄庡繀椤诲湪姝や箣鍓 + HG_BASE_SETTING_INDEX_MAX, +}; + +class hg_scanner +{ + bool notify_setting_result_; + std::string name_; + sane_callback ui_ev_cb_; + do_when_born_and_dead* scan_life_; + + std::unique_ptr thread_usb_read_; + void thread_handle_usb(void); + + std::unique_ptr thread_img_handle_; + void thread_image_handle(void); + + void get_range(int setting_no, std::vector& range, std::string& def_val, bool& is_range/*range or list*/); + bool check_range(int setting_no, bool& val); + bool check_range(int setting_no, int& val); + bool check_range(int setting_no, double& val); + bool check_range(int setting_no, std::string& val); + int restore(int setting_no); + bool get_default_value(void* buf, json* jsn); + bool is_to_file(void); + void thread_handle_image_process(void); + + void working_begin(void*); + void working_done(void*); + + // 璁剧疆鎺ュ彛 +protected: + + // 閰嶇疆搴忓彿鏄犲皠鏁扮粍锛屽悇瀛愮被蹇呴』閲嶈浇鈥渋nit_setting_map鈥濇柟娉曚互鍒濆鍖栬鏁扮粍 + // 姣斿锛 鏌愬瀷璁惧鐨勨滈鑹叉ā寮忊濆姛鑳藉彿涓1锛屽垯 setting_map_[HG_BASE_SETTING_INDEX_COLOR_MODE] = 1 + // 濡傛灉璁惧鐨勨滈鑹叉ā寮忊濋厤缃昏緫涓庡熀绫婚瀹氫箟鐨勪笉鍚岋紝 + // 鍒 setting_map_[HG_BASE_SETTING_INDEX_COLOR_MODE] = -1锛屽湪瀛愮被閲嶈浇鐨剆et_setting_value鏂规硶涓鐞 + int setting_map_[HG_BASE_SETTING_INDEX_MAX]; + virtual void init_setting_map(int* setting_map, int count); + + int setting_restore(void* data); + int setting_help(void* data); + int setting_color_mode(void* data); + + int setting_multi_out(void *data); + + int setting_rid_color(void* data); + int setting_rid_multi_red(void* data); + int setting_rid_answer_red(void* data); + int setting_erase_background(void* data); + int setting_erase_background_range(void* data); + int setting_noise_optimize(void* data); + int setting_noise_optimize_range(void* data); + int setting_paper(void* data); + int setting_paper_check(void* data); + int setting_page(void* data); + int setting_page_omit_empty(void* data); + int setting_resolution(void* data); + int setting_exchagnge(void* data); + int setting_split_image(void* data); + int setting_automatic_skew(void* data); + int setting_rid_hole(void* data); + int setting_rid_hoe_range(void* data); + int setting_bright(void* data); + int setting_contrast(void* data); + int setting_gamma(void* data); + int setting_sharpen(void* data); + int setting_dark_sample(void* data); + int setting_erase_black_frame(void* data); + int setting_threshold(void* data); + int setting_anti_noise(void* data); + int setting_margin(void* data); + int setting_filling_background(void* data); + int setting_is_permeate(void *data); + int setting_is_permeate_lv(void *data); + int setting_remove_morr(void* data); + int setting_error_extention(void* data); + int setting_remove_texture(void* data); + int setting_ultrasonic_check(void* data); + int setting_staple_check(void* data); + int setting_scan_mode(void* data); + int setting_scan_count(void* data); + int setting_text_direction(void* data); + int setting_rotate_bkg_180(void* data); + int setting_fractate_check(void* data); + int setting_fractate_check_level(void* data); + int setting_skew_check(void* data); + int setting_skew_check_level(void* data); + + + virtual int on_color_mode_changed(int& color_mode); // COLOR_MODE_xxx + virtual int on_paper_changed(int& paper); // PAPER_xxx + virtual int on_paper_check_changed(bool& check); + virtual int on_resolution_changed(int& dpi); + virtual int on_ultrasonic_check_changed(bool& check); + virtual int on_staple_check_changed(bool& check); + virtual int on_skew_check_changed(bool& check); + virtual int on_skew_check_level_changed(int& check); + + +protected: + virtual void on_device_reconnected(void); + virtual int set_setting_value(int setting_no, void* data, int len); + virtual int on_scanner_closing(bool force); + virtual void thread_handle_usb_read(void) = 0; + virtual void image_process(std::shared_ptr>& buff) = 0; + +protected: + volatile bool run_; + volatile bool user_cancel_; + platform_event wait_usb_; + platform_event wait_img_; + usb_io* io_; + std::mutex io_lock_; + bool online_; + int status_; + bool async_io_; + bool cb_mem_; + bool test_1_paper_; // 鏄惁涓哄崟寮犳壂鎻忔ā寮 + json setting_jsn_; + int setting_count_; + + IMGPRCFIXPARAM image_prc_param_; + int erase_bkg_range_; // 鑳屾櫙绉婚櫎鍍忕礌鑼冨洿 + int noise_range_; // 鍣偣浼樺寲灏哄 + TwSS paper_size_; + int omit_empty_level_; // 璺宠繃绌虹櫧椤电伒鏁忓害 + int resolution_; // 鍒嗚鲸鐜 + double rid_hole_range_; // 绌垮瓟绉婚櫎鑼冨洿 + int bright_; // 浜害 + int contrast_; // 瀵规瘮搴 + double gamma_; // 浼界帥 + int threshold_; // 闃堝 + int anti_noise_; // 鑳屾櫙鎼炲櫔绛夌骇 + int margin_; // 杈圭紭缂╄繘 + int fractate_level_; // 鎶樿妫娴嬪鏉傚害 + int scan_count_; // 鎵弿寮犳暟锛屽悇瀹炰緥鍖栫被鍦ㄩ噸杞絪et_setting_value涓紝濡傛灉鍙戠幇璇ヨ缃」瀵硅鍙傛暟鏈夊奖鍝嶆椂锛岄渶瑕佸姝ゅ间綔鏇存敼 + bool is_auto_matic_color;// 鑷姩棰滆壊璇嗗埆 + + SCANCONF img_conf_; //姝ゅ弬鏁板閮ㄤ笉鍋氫换浣曟敼鍙橈紝璇峰湪writedown_image_configuration鍋氫慨鏀 + std::string img_type_; + + image_data final_imgs_; // JPG ... + unsigned int final_img_index_; + BlockingQueue>> imgs_; + BlockingQueue paths_; + + void init_settings(const char* json_setting_text); + int on_scann_error(int err); // 杩斿洖鈥0鈥濆拷鐣ラ敊璇户缁墽琛岋紝鍏跺畠鍊煎垯鍋滄鍚庣画宸ヤ綔 + + // callback to ui ... + int notify_ui_working_status(const char* msg, int ev = SANE_EVENT_STATUS, int status = HG_ERR_OK); + bool waiting_for_memory_enough(unsigned need_bytes); + + void copy_to_sane_image_header(SANE_Parameters* header, int w, int h, int line_bytes, int channels); + int save_usb_data(std::shared_ptr> data); + int save_final_image(hg_imgproc::LPIMGHEAD head, void* buf); + +public: + void set_ui_callback(sane_callback cb, bool enable_async_io); + int reset_io(usb_io* io); + int io_disconnected(void); + int get_pid(void); + int get_vid(void); + int close(bool force); + int set_setting(int setting_no, void* data, int len); + int get_setting(int setting_no, char* json_txt_buf, int* len); + + std::string name(void); + int status(void); + bool is_online(void); + +public: + virtual int start(void); + virtual int get_image_info(SANE_Parameters* ii); + virtual int read_image_data(unsigned char* buf, int* len); + virtual int stop(void); + virtual int reset(void); + virtual int device_io_control(unsigned long code, void* data, unsigned* len); + +public: + hg_scanner(ScannerSerial serial, const char* dev_name, usb_io* io); + virtual ~hg_scanner(); + + static std::string strerr(hg_err err); + static std::string error_description(hg_err err); + static std::string temporary_file(char* tail = NULL, char* head = NULL); + static int save_2_tempory_file(std::shared_ptr> data, std::string* path_file); + +public: +///////////////////////////////鏂板 20220425////////////////////////// + virtual int set_leaflet_scan(void);//鍗曞紶鎵弿 + virtual int get_abuot_info(void);//鑾峰彇杞欢鍏充簬淇℃伅 (鍩虹被瀹炵幇) + virtual int restore_default_setting(void);//鎭㈠榛樿璁剧疆 (鍩虹被瀹炵幇) + virtual int set_final_image_format(SANE_FinalImgFormat* fmt); // 璁剧疆鍥惧儚澶勭悊鏈缁堣緭鍑猴紙final()锛夌殑鍥惧儚鏁版嵁鏍煎紡 (鍩虹被瀹炵幇) *** + virtual int get_compression_format(void);//鑾峰彇鏀寔鐨勫帇缂╂牸寮 鍔熻兘涓嶆敮鎸 + + virtual int get_roller_num(void); //鑾峰彇婊氳疆寮犳暟 + virtual int clear_roller_num(void); // 娓呴櫎婊氳酱璁℃暟 + + virtual int set_compression_format(void);//璁剧疆鍥惧儚鏁版嵁鏈缁堣緭鍑虹殑鍘嬬缉鏍煎紡 + virtual int set_auto_color_type(void);// 璁剧疆鑷姩鍖归厤棰滆壊妯″紡 (鍩虹被瀹炵幇) *** + + virtual std::string get_firmware_version(void); + virtual std::string get_serial_num(void); + virtual std::string get_ip(void); + + virtual int get_device_code(void);//鑾峰彇璁惧缂栫爜 + virtual int get_sleep_time(int& getsleepime);//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛 + virtual int set_sleep_time(int sleeptime);//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛 + + virtual int get_dogear_distance(void);//鑾峰彇鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int set_dogear_distance(void);// 璁剧疆鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int get_scanner_paperon(SANE_Bool* paperon = NULL);//鑾峰彇璁惧鏈夋棤绾稿紶 + virtual int set_scan_when_paper_on(void);//鑾峰彇鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_when_paper_on(void);//璁剧疆鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_with_hole(void);// 鑾峰彇鏄惁涓哄甫瀛旀壂鎻 + virtual int set_scan_with_hole(void);// 璁剧疆鏄惁涓哄甫瀛旀壂鎻 + + virtual int get_scan_is_sleep(void);//鑾峰彇璁惧鏄惁浼戠湢褰撲腑 +}; + +static const std::string helpfile_ ="/opt/apps/com.huagaochina.huagoscan/entries/help/HuaGoScan_App_Help_manual.pdf";//甯姪鏂囨。璺緞 diff --git a/hgdriver/hgdev/hg_scanner_200.cpp b/hgdriver/hgdev/hg_scanner_200.cpp new file mode 100644 index 0000000..e361b0a --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_200.cpp @@ -0,0 +1,1325 @@ +锘#include "hg_scanner_200.h" +#include "../../sdk/hginclude/hg_log.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + + +static std::string jsontext("{\"device_type\":\"G100\",\"option_count\":47,\"1\":{\"category\":\"base\",\"name\":\"cfg-1\",\"title\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"desc\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"type\":\"button\",\"cur\":\"button\",\"default\":\"button\",\"size\":0},\"2\":{\"category\":\"advanced\",\"name\":\"cfg-2\",\"title\":\"\\u5e2e\\u52a9\",\"desc\":\"\\u663e\\u793a\\u8f6f\\u4ef6\\u5e2e\\u52a9\\u6587\\u6863\",\"type\":\"button\",\"cur\":\"true\",\"default\":\"true\",\"size\":4},\"3\":{\"category\":\"base\",\"name\":\"grp-1\",\"title\":\"\\u57fa\\u672c\\u8bbe\\u7f6e\",\"type\":\"group\"},\"4\":{\"category\":\"base\",\"name\":\"cfg-4\",\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6e\\u989c\\u8272\\u4f4d\\u6df1\",\"type\":\"string\",\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"size\":24,\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"]},\"5\":{\"category\":\"base\",\"name\":\"cfg-5\",\"title\":\"\\u7070\\u5ea6\\u6216\\u9ed1\\u767d\\u56fe\\u50cf - \\u9664\\u8272\",\"desc\":\"\\u9664\\u53bb\\u56fe\\u50cf\\u5f69\\u8272\",\"type\":\"string\",\"cur\":\"\\u4e0d\\u9664\\u8272\",\"default\":\"\\u4e0d\\u9664\\u8272\",\"size\":20,\"range\":[\"\\u4e0d\\u9664\\u8272\",\"\\u9664\\u7ea2\\u8272\",\"\\u9664\\u7eff\\u8272\",\"\\u9664\\u84dd\\u8272\",\"\\u7ea2\\u8272\\u589e\\u5f3a\",\"\\u7eff\\u8272\\u589e\\u5f3a\",\"\\u84dd\\u8272\\u589e\\u5f3a\"],\"depend_or\":[\"4==256\\u7ea7\\u7070\\u5ea6\",\"4==\\u9ed1\\u767d\"]},\"6\":{\"category\":\"base\",\"name\":\"cfg-6\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u591a\\u6d41\\u8f93\\u51fa\\u9664\\u7ea2\",\"desc\":\"\\u591a\\u901a\\u9053\\u8f93\\u51fa\\u4e2d\\uff0c\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"7\":{\"category\":\"base\",\"name\":\"cfg-7\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u7b54\\u9898\\u5361\\u9664\\u7ea2\",\"desc\":\"\\u7b54\\u9898\\u5361\\u626b\\u63cf\\u4e2d\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"8\":{\"category\":\"base\",\"name\":\"cfg-8\",\"title\":\"\\u80cc\\u666f\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u5f69\\u8272\\u56fe\\u50cf\\u80cc\\u666f\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"9\":{\"category\":\"base\",\"name\":\"cfg-9\",\"title\":\" \\u80cc\\u666f\\u8272\\u5f69\\u6d6e\\u52a8\\u8303\\u56f4\",\"desc\":\"\\u8bbe\\u5b9a\\u80cc\\u666f\\u8272\\u5f69\\u7684\\u6d6e\\u52a8\\u8303\\u56f4\\uff0c\\u5728\\u8be5\\u8303\\u56f4\\u5185\\u7684\\u90fd\\u5f53\\u4f5c\\u80cc\\u666f\\u79fb\\u9664\",\"type\":\"int\",\"cur\":10,\"default\":10,\"size\":4,\"range\":{\"min\":1,\"max\":40},\"depend_or\":[\"8==true\"]},\"10\":{\"category\":\"base\",\"name\":\"cfg-10\",\"title\":\"\\u9ed1\\u767d\\u56fe\\u50cf\\u566a\\u70b9\\u4f18\\u5316\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==\\u9ed1\\u767d\"]},\"11\":{\"category\":\"base\",\"name\":\"cfg-11\",\"title\":\" \\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"type\":\"int\",\"cur\":30,\"default\":30,\"size\":4,\"range\":{\"min\":10,\"max\":50},\"depend_or\":[\"10==true\"]},\"12\":{\"category\":\"base\",\"name\":\"cfg-12\",\"title\":\"\\u7eb8\\u5f20\\u5c3a\\u5bf8\",\"desc\":\"\\u8bbe\\u7f6e\\u7eb8\\u5f20\\u5927\\u5c0f\",\"type\":\"string\",\"cur\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"default\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"size\":44,\"range\":[\"A3\",\"8\\u5f00\",\"A4\",\"A4\\u6a2a\\u5411\",\"16\\u5f00\",\"16\\u5f00\\u6a2a\\u5411\",\"A5\",\"A5\\u6a2a\\u5411\",\"A6\",\"A6\\u6a2a\\u5411\",\"B4\",\"B5\",\"B5\\u6a2a\\u5411\",\"B6\",\"B6\\u6a2a\\u5411\",\"Letter\",\"Letter\\u6a2a\\u5411\",\"Double Letter\",\"LEGAL\",\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"\\u4e09\\u8054\\u8bd5\\u5377\"]},\"13\":{\"category\":\"base\",\"name\":\"cfg-13\",\"title\":\"\\u626b\\u63cf\\u9875\\u9762\",\"desc\":\"\\u8bbe\\u7f6e\\u9875\\u9762\\u626b\\u63cf\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u53cc\\u9762\",\"default\":\"\\u53cc\\u9762\",\"size\":40,\"range\":[\"\\u5355\\u9762\",\"\\u53cc\\u9762\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\",\"\\u5bf9\\u6298\"]},\"14\":{\"category\":\"base\",\"name\":\"cfg-14\",\"title\":\" \\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7075\\u654f\\u5ea6\",\"desc\":\"\\u7075\\u654f\\u5ea6\\u8d8a\\u9ad8\\uff0c\\u5219\\u8d8a\\u5bb9\\u6613\\u8df3\\u8fc7\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":1,\"max\":100},\"depend_or\":[\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\"]},\"15\":{\"category\":\"base\",\"name\":\"cfg-15\",\"title\":\"\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u626b\\u63cf\\u4eea\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":{\"min\":100,\"max\":300}},\"16\":{\"category\":\"base\",\"name\":\"cfg-16\",\"title\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"desc\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\"]},\"17\":{\"category\":\"base\",\"name\":\"cfg-17\",\"title\":\"\\u56fe\\u50cf\\u62c6\\u5206\",\"desc\":\"\\u81ea\\u52a8\\u62c6\\u5206\\u56fe\\u50cf\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"18\":{\"category\":\"base\",\"name\":\"cfg-18\",\"title\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"desc\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"19\":{\"category\":\"base\",\"name\":\"cfg-19\",\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u7eb8\\u5f20\\u4e2d\\u7684\\u7a7f\\u5b54\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"20\":{\"category\":\"base\",\"name\":\"cfg-20\",\"title\":\" \\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":0.500000},\"depend_or\":[\"19==true\"]},\"21\":{\"category\":\"base\",\"name\":\"grp-2\",\"title\":\"\\u4eae\\u5ea6\",\"type\":\"group\"},\"22\":{\"category\":\"base\",\"name\":\"cfg-22\",\"title\":\"\\u4eae\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4eae\\u5ea6\",\"type\":\"int\",\"cur\":128,\"default\":128,\"size\":4,\"range\":{\"min\":1,\"max\":255}},\"23\":{\"category\":\"base\",\"name\":\"cfg-23\",\"title\":\"\\u5bf9\\u6bd4\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u5bf9\\u6bd4\\u5ea6\",\"type\":\"int\",\"cur\":4,\"default\":4,\"size\":4,\"range\":{\"min\":1,\"max\":7}},\"24\":{\"category\":\"base\",\"name\":\"cfg-24\",\"title\":\"\\u4f3d\\u739b\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4f3d\\u739b\\u503c\",\"type\":\"float\",\"cur\":1.000000,\"default\":1.000000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":5.000000}},\"25\":{\"category\":\"base\",\"name\":\"grp-3\",\"title\":\"\\u56fe\\u50cf\\u5904\\u7406\",\"type\":\"group\"},\"26\":{\"category\":\"base\",\"name\":\"cfg-26\",\"title\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"desc\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"type\":\"string\",\"cur\":\"\\u65e0\",\"default\":\"\\u65e0\",\"size\":20,\"range\":[\"\\u65e0\",\"\\u9510\\u5316\",\"\\u8fdb\\u4e00\\u6b65\\u9510\\u5316\",\"\\u6a21\\u7cca\",\"\\u8fdb\\u4e00\\u6b65\\u6a21\\u7cca\"]},\"27\":{\"category\":\"base\",\"name\":\"cfg-27\",\"title\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"desc\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"28\":{\"category\":\"base\",\"name\":\"cfg-28\",\"title\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"desc\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5bf9\\u6298\",\"27!=true\",\"12!=\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18!=true\"]},\"29\":{\"category\":\"advanced\",\"name\":\"cfg-29\",\"title\":\"\\u9608\\u503c\",\"desc\":\"\\u9608\\u503c\",\"type\":\"int\",\"cur\":40,\"default\":40,\"size\":4,\"range\":{\"min\":30,\"max\":50},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"30\":{\"category\":\"advanced\",\"name\":\"cfg-30\",\"title\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"desc\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"type\":\"int\",\"cur\":8,\"default\":8,\"size\":4,\"range\":{\"min\":1,\"max\":20},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"31\":{\"category\":\"advanced\",\"name\":\"cfg-31\",\"title\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"desc\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"type\":\"int\",\"cur\":5,\"default\":5,\"size\":4,\"range\":{\"min\":5,\"max\":30},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\"]},\"32\":{\"category\":\"advanced\",\"name\":\"cfg-32\",\"title\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"desc\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"default\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"size\":40,\"range\":[\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"\\u51f9\\u591a\\u8fb9\\u5f62\"],\"depend_or\":[\"27==true\"]},\"33\":{\"category\":\"base\",\"name\":\"cfg-33\",\"title\":\"\\u9632\\u6b62\\u6e17\\u900f\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"34\":{\"category\":\"base\",\"name\":\"cfg-34\",\"title\":\" \\u9632\\u6b62\\u6e17\\u900f\\u7b49\\u7ea7\",\"desc\":\"\",\"type\":\"string\",\"cur\":\"\\u8f83\\u5f31\",\"default\":\"\\u8f83\\u5f31\",\"size\":12,\"range\":[\"\\u8f83\\u5f31\",\"\\u5f31\",\"\\u4e00\\u822c\",\"\\u5f3a\",\"\\u8f83\\u5f3a\"],\"depend_or\":[\"33==true\"]},\"35\":{\"category\":\"base\",\"name\":\"cfg-35\",\"title\":\"\\u53bb\\u9664\\u6469\\u5c14\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"36\":{\"category\":\"base\",\"name\":\"cfg-36\",\"title\":\"\\u9519\\u8bef\\u6269\\u6563\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"37\":{\"category\":\"base\",\"name\":\"cfg-37\",\"title\":\"\\u9664\\u7f51\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"38\":{\"category\":\"base\",\"name\":\"grp-4\",\"title\":\"\\u9001\\u7eb8\\u65b9\\u5f0f\\u8bbe\\u7f6e\",\"type\":\"group\"},\"39\":{\"category\":\"base\",\"name\":\"cfg-39\",\"title\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\",\"desc\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\\u9001\\u7eb8\\u72b6\\u6001\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"40\":{\"category\":\"base\",\"name\":\"cfg-40\",\"title\":\"\\u88c5\\u8ba2\\u68c0\\u6d4b\",\"desc\":\"\\u68c0\\u6d4b\\u7eb8\\u5f20\\u4e0a\\u662f\\u5426\\u6709\\u9489\\u4e66\\u9489\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"41\":{\"category\":\"base\",\"name\":\"cfg-41\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u626b\\u63cf\\u7eb8\\u5f20\\u6570\\u91cf\",\"type\":\"string\",\"cur\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"default\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"size\":24,\"range\":[\"\\u8fde\\u7eed\\u626b\\u63cf\",\"\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"42\":{\"category\":\"base\",\"name\":\"cfg-42\",\"title\":\" \\u626b\\u63cf\\u6570\\u91cf\",\"desc\":\"\\u626b\\u63cf\\u6307\\u5b9a\\u6570\\u91cf\",\"type\":\"int\",\"cur\":1,\"default\":1,\"size\":4,\"depend_or\":[\"41==\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"43\":{\"category\":\"base\",\"name\":\"cfg-43\",\"title\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"desc\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"type\":\"string\",\"cur\":\"0\\u00b0\",\"default\":\"0\\u00b0\",\"size\":16,\"range\":[\"0\\u00b0\",\"90\\u00b0\",\"180\\u00b0\",\"-90\\u00b0\"]},\"44\":{\"category\":\"base\",\"name\":\"cfg-44\",\"title\":\"\\u80cc\\u9762\\u65cb\\u8f6c180\\u00b0\",\"desc\":\"\\u80cc\\u9762\\u626b\\u63cf\\u7684\\u56fe\\u50cf\\u65cb\\u8f6c180\\u00b0\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\",\"13!=\\u5bf9\\u6298\",\"43!=\\u81ea\\u52a8\\u6587\\u672c\\u65b9\\u5411\\u8bc6\\u522b\\u00b0\"]},\"45\":{\"category\":\"base\",\"name\":\"cfg-45\",\"title\":\"\\u6b6a\\u659c\\u68c0\\u6d4b\",\"desc\":\"\\u7eb8\\u5f20\\u6b6a\\u659c\\u68c0\\u6d4b\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"46\":{\"category\":\"base\",\"name\":\"cfg-46\",\"title\":\" \\u6b6a\\u659c\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"desc\":\"\\u7eb8\\u5f20\\u6b6a\\u659c\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"type\":\"int\",\"cur\":3,\"default\":3,\"size\":4,\"range\":{\"min\":1,\"max\":5},\"depend_or\":[\"45==true\"]}}"); +namespace settingsdsp_200 +{ + hg_err device_status_to_hg_err(int usbdata, bool *fatal) + { + hg_err code = HG_ERR_OK; + + if (fatal) + *fatal = true; + switch (usbdata) + { + case HAVE_IMAGE: + code = HG_ERR_OK; + if (fatal) + *fatal = false; + break; + case STOP_SCAN: + case SCAN_STOP: + case USER_STOP: + if (fatal) + *fatal = false; + code = HG_ERR_DEVICE_STOPPED; + break; + case COUNT_MODE: + code = HG_ERR_DEVICE_COUNT_MODE; + break; + case NO_FEED: + code = HG_ERR_DEVICE_NO_PAPER; + break; + case OPEN_COVER: + code = HG_ERR_DEVICE_COVER_OPENNED; + break; + case FEED_IN_ERROR: + code = HG_ERR_DEVICE_FEEDING_PAPER; + break; + case PAPER_JAM: + code = HG_ERR_DEVICE_PAPER_JAMMED; + break; + case DETECT_DOUBLE_FEED: + code = HG_ERR_DEVICE_DOUBLE_FEEDING; + break; + case DETECT_STAPLE: + code = HG_ERR_DEVICE_STAPLE_ON; + break; + case PAPER_SKEW: + code = HG_ERR_DEVICE_PAPER_SKEW; + break; + case HARDWARE_ERROR: + code = HG_ERR_DEVICE_NOT_SUPPORT; + break; + case PC_SCAN_BUSY_or_ERROR: + code = HG_ERR_DEVICE_PC_BUSY; + break; + case SIZE_ERROR: + code = HG_ERR_DEVICE_SIZE_CHECK; + break; + default: + if (fatal) + *fatal = false; + break; + } + + return code; + } + + + static struct + { + int paper; + TwSS type; + int dev_value; + }paper_map[] = + { + {PAPER_A3, TwSS::A3, 0}, + {PAPER_8K, TwSS::K8, 0}, + {PAPER_A4, TwSS::A4, 1}, + {PAPER_A4_LATERAL, TwSS::A4, 2}, + {PAPER_16K, TwSS::K16, 0}, + {PAPER_16K_LATERAL, TwSS::K16, 0}, + {PAPER_A5, TwSS::A5, 2}, + {PAPER_A5_LATERAL, TwSS::A5, 2}, + {PAPER_A6, TwSS::A6, 2}, + {PAPER_A6_LATERAL, TwSS::A6, 2}, + {PAPER_B4, TwSS::B4, 0}, + {PAPER_B5, TwSS::B5, 0}, + {PAPER_B5_LATERAL, TwSS::B5, 1}, + {PAPER_B6, TwSS::B6, 2}, + {PAPER_B6_LATERAL, TwSS::B6, 2}, + {PAPER_LETTER, TwSS::USLetter, 14}, + {PAPER_LETTER_LATERAL, TwSS::USLetter, 1}, + {PAPER_DOUBLE_LETTER, TwSS::USLedger, 2}, + {PAPER_LEGAL, TwSS::USLegal, 0}, + {PAPER_AUTO_MATCH, TwSS::None, 0}, + {PAPER_MAX_SIZE_CLIP, TwSS::USStatement, 16}, + {PAPER_MAX_SIZE, TwSS::MaxSize, 16}, + {PAPER_TRIGEMINY, TwSS::Trigeminy, 16} + }; + int match_best_paper(int& paper, bool* exact, TwSS* type) + { + int ind = 0; + bool good = true, * r = exact ? exact : &good; + for (int i = 0; i < ARRAY_SIZE(paper_map); ++i) + { + if (paper == paper_map[i].paper) + { + ind = i; + break; + } + } + + if (paper == paper_map[ind].paper) + *r = true; + else + *r = false; + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Paper '%s' index = %d, device value = %d\n", paper_string(paper).c_str(), ind, paper_map[ind].dev_value); + paper = paper_map[ind].paper; + if (type) + *type = paper_map[ind].type; + + return ind; + } +} + + +enum hg_239_setting_item +{ + HG_239_SETTING_RESTORE = 1, // 鎭㈠榛樿璁剧疆 + HG_239_SETTING_HELP, // 甯姪 + + HG__239_SETTING_GROUP_1, + + HG_239_SETTING_COLOR_MODE, // 棰滆壊妯″紡 + + HG_239_SETTING_ERASE_COLOR, // 闄よ壊 + HG_239_SETTING_ERASE_MULTI_RED, // 澶氭祦杈撳嚭闄ょ孩 + HG_239_SETTING_ERASE_ANSWER_RED, // 绛旈鍗¢櫎绾 + HG_239_SETTING_ERASE_BACKGROUND, // 绉婚櫎鑳屾櫙 + HG_239_SETTING_ERASE_BKG_RANGE, // 绉婚櫎鑳屾櫙鑼冨洿 + HG_239_SETTING_NOISE, // 榛戠櫧鍥惧儚鍣偣浼樺寲 + HG_239_SETTING_NOISE_SIZE, // 鍣偣浼樺寲灏哄 + HG_239_SETTING_PAPER_SIZE, // 绾稿紶灏哄 + //HG_239_SETTING_PAPER_SIZE_CHECK, // 灏哄妫娴 + HG_239_SETTING_PAGE, // 鎵弿椤甸潰 + HG_239_SETTING_SKIP_NILL, // 璺宠繃绌虹櫧椤电伒鏁忓害 + HG_239_SETTING_RESOLUTION, // 鍒嗚鲸鐜 + HG_239_SETTING_REVERSE, // 浜ゆ崲姝e弽闈 + HG_239_SETTING_SPLIT, // 鍥惧儚鎷嗗垎 + HG_239_SETTING_CORRECT, // 鑷姩绾犲亸 + HG_239_SETTING_ERASE_HOLE, // 绌垮瓟绉婚櫎 + HG_239_SETTING_HOLE_SEARCH, // 绌垮瓟鎼滅储鑼冨洿 + + HG__239_SETTING_GROUP_2, + + HG_239_SETTING_LIGHT, // 浜害 + HG_239_SETTING_CONTRAST, // 瀵规瘮搴 + HG_239_SETTING_GAMMA, // 浼界帥 + + HG__239_SETTING_GROUP_3, + + HG_239_SETTING_SHARP, // 閿愬寲涓庢ā绯 + HG_239_SETTING_ERASE_BLACK_BORDER, // 娑堥櫎榛戞 + HG_239_SETTING_SAMPLE, // 娣辫壊鏍峰紶 + HG_239_SETTING_THRESHOLD, // 闃堝 + HG_239_SETTING_ANTI_NOISE, // 鑳屾櫙鎶楀櫔绛夌骇 + HG_239_SETTING_MARGIN, // 杈圭紭缂╄繘 + HG_239_SETTING_FILLING, // 鑳屾櫙濉厖鏂瑰紡 + HG_239_SETTING_PERMEATE, // 闃叉娓楅 + HG_239_SETTING_PERMEATE_lv, // 闃叉娓楅忕瓑绾 + HG_239_SETTING_REMOVE_MORR, // 鍘婚櫎鎽╁皵绾 + HG_239_SETTING_ERROR_EXTENTION, // 閿欒鎵╂暎 + HG_239_SETTING_REMOVE_TXTTURE, // 闄ょ綉绾 + + HG__239_SETTING_GROUP_4, + + HG_239_SETTING_ULTRASONIC, // 瓒呭0娉㈡娴 + HG_239_SETTING_STAPLE, // 瑁呰妫娴 + HG_239_SETTING_SCAN_METHOD, // 杩炵画鎵弿鎴栨壂鎻忔寚瀹氬紶鏁 + HG_239_SETTING_SCAN_COUNT, // 鎵弿鎸囧畾鏁伴噺 + HG_239_SETTING_DIRECTION, // 鏂囩ǹ鏂瑰悜 鍒犻櫎鏂囩ǹ鑷姩璇嗗埆 + HG_239_SETTING_ROTATE, // 鑳岄潰鏃嬭浆180掳 + //HG_239_SETTING_FRACTATE, // 鎶樿妫娴 + //HG_239_SETTING_FRACTATE_COMPLEX, // 鎶樿妫娴嬪鏉傚害 + HG_239_SETTING_SCREW, // 姝枩妫娴 + HG_239_SETTING_SCREW_COMPLEX, // 姝枩妫娴嬪鏉傚害 + + + HG_239_SETTING_END, + + //鏆傛椂灞忚斀 + HG_239_SETTING_MULTI_OUT = 500, // 澶氭祦杈撳嚭 +}; + + + +hg_scanner_200::hg_scanner_200(const char* dev_name,int pid, usb_io* io) : hg_scanner(G100Serial, dev_name, io),pid_(pid) +{ + string fw = get_firmware_version(); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_firmware_version is: %s\n",fw.c_str()); + // initdevice(); + + dsp_config_.value = 0; + + init_setting_map(setting_map_, ARRAY_SIZE(setting_map_));//浼樺厛鍒濆鍖 + init_settings(jsontext.c_str()); + + printf_devconfig(); +} +hg_scanner_200::~hg_scanner_200() +{} + +void hg_scanner_200::discard_prev(void) +{ + USBCB usb = { 0 }; + int ret = get_scanner_status(usb), + imgs = 0, + packets = 0, + normal = 0; + + while (ret == HG_ERR_OK) + { + if (usb.u32_Data == HAVE_IMAGE) + { + pop_image(); + imgs++; + normal = 0; + packets++; + } + else if (usb.u32_Data == NORMAL) + { + if(normal++ > 10) + break; + } + else + packets++; + } + if (imgs || packets) + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "discard prev-scanning %d packets and %d images.\n", packets, imgs); +} + +void hg_scanner_200::init_setting_map(int* setting_map, int count) +{ + setting_map[HG_BASE_SETTING_INDEX_RESTORE_DEFAULT_SETTINGS] = HG_239_SETTING_RESTORE; + setting_map[HG_BASE_SETTING_INDEX_HELP] = HG_239_SETTING_HELP; + + setting_map[HG_BASE_SETTING_INDEX_COLOR_MODE] = HG_239_SETTING_COLOR_MODE; + setting_map[HG_BASE_SETTING_MULTI_OUT] = HG_239_SETTING_MULTI_OUT; + setting_map[HG_BASE_SETTING_INDEX_ERASE_COLOR] = HG_239_SETTING_ERASE_COLOR; + setting_map[HG_BASE_SETTING_INDEX_ERASE_MULTI_OUT_RED] = HG_239_SETTING_ERASE_MULTI_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_ANSWER_RED] = HG_239_SETTING_ERASE_ANSWER_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND] = HG_239_SETTING_ERASE_BACKGROUND; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE] = HG_239_SETTING_ERASE_BKG_RANGE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE] = HG_239_SETTING_NOISE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE] = HG_239_SETTING_NOISE_SIZE; + setting_map[HG_BASE_SETTING_INDEX_PAPER] = HG_239_SETTING_PAPER_SIZE; + //setting_map[HG_BASE_SETTING_INDEX_PAPER_SIZE_CHECK] = HG_239_SETTING_PAPER_SIZE_CHECK; + setting_map[HG_BASE_SETTING_INDEX_PAGE] = HG_239_SETTING_PAGE; + setting_map[HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL] = HG_239_SETTING_SKIP_NILL; + setting_map[HG_BASE_SETTING_INDEX_RESOLUTION] = HG_239_SETTING_RESOLUTION; + setting_map[HG_BASE_SETTING_INDEX_EXCHANGE] = HG_239_SETTING_REVERSE; + setting_map[HG_BASE_SETTING_INDEX_SPLIT] = HG_239_SETTING_SPLIT; + setting_map[HG_BASE_SETTING_INDEX_AUTO_CORRECT] = HG_239_SETTING_CORRECT; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE] = HG_239_SETTING_ERASE_HOLE; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE_RANGE] = HG_239_SETTING_HOLE_SEARCH; + + setting_map[HG_BASE_SETTING_INDEX_BRIGHT] = HG_239_SETTING_LIGHT; + setting_map[HG_BASE_SETTING_INDEX_CONTRAST] = HG_239_SETTING_CONTRAST; + setting_map[HG_BASE_SETTING_INDEX_GAMMA] = HG_239_SETTING_GAMMA; + + setting_map[HG_BASE_SETTING_INDEX_SHARPEN] = HG_239_SETTING_SHARP; + setting_map[HG_BASE_SETTING_INDEX_DARK_SAMPLE] = HG_239_SETTING_SAMPLE; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BLACK_FRAME] = HG_239_SETTING_ERASE_BLACK_BORDER; + setting_map[HG_BASE_SETTING_INDEX_THRESHOLD] = HG_239_SETTING_THRESHOLD; + setting_map[HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL] = HG_239_SETTING_ANTI_NOISE; + setting_map[HG_BASE_SETTING_INDEX_MARGIN] = HG_239_SETTING_MARGIN; + setting_map[HG_BASE_SETTING_INDEX_FILL_BACKGROUND] = HG_239_SETTING_FILLING; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE] = HG_239_SETTING_PERMEATE; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE_LV] = HG_239_SETTING_PERMEATE_lv; + setting_map[HG_BASE_SETTING_REMOVE_MORR] = HG_239_SETTING_REMOVE_MORR; + setting_map[HG_BASE_SETTING_ERROR_EXTENTION] = HG_239_SETTING_ERROR_EXTENTION; + setting_map[HG_BASE_SETTING_REMOVE_TXTTURE] = HG_239_SETTING_REMOVE_TXTTURE; + + setting_map[HG_BASE_SETTING_INDEX_ULTRASONIC_CHECK] = HG_239_SETTING_ULTRASONIC; + setting_map[HG_BASE_SETTING_INDEX_STAPLE_CHECK] = HG_239_SETTING_STAPLE; + setting_map[HG_BASE_SETTING_INDEX_SCAN_MODE] = HG_239_SETTING_SCAN_METHOD; + setting_map[HG_BASE_SETTING_INDEX_SCAN_COUNT] = HG_239_SETTING_SCAN_COUNT; + setting_map[HG_BASE_SETTING_INDEX_TEXT_DIRECTION] = HG_239_SETTING_DIRECTION; + setting_map[HG_BASE_SETTING_INDEX_ROTATE_BKG_180] = HG_239_SETTING_ROTATE; + //setting_map[HG_BASE_SETTING_INDEX_FRACTATE_CHECK] = HG_239_SETTING_FRACTATE; 鏆傛椂灞忚斀 + //setting_map[HG_BASE_SETTING_INDEX_FRACTATE_CHECK_LEVEL] = HG_239_SETTING_FRACTATE_COMPLEX; + setting_map[HG_BASE_SETTING_INDEX_SKEW_CHECK] = HG_239_SETTING_SCREW; + setting_map[HG_BASE_SETTING_INDEX_SKEW_CHECK_LEVEL] = HG_239_SETTING_SCREW_COMPLEX; + + } + +int hg_scanner_200::on_scanner_closing(bool force) +{ + return HG_ERR_OK; +} +void hg_scanner_200::thread_handle_usb_read(void) +{ + int ret = HG_ERR_OK, + prev_err = HG_ERR_OK; + StopWatch sw; + bool first = true; + + while (run_) + { + //if (user_cancel_) + // break; + if (sw.elapsed_s() > 30) + { + // no any data in 30 seconds, we giveup the work ... + status_ = prev_err != HG_ERR_OK ? prev_err : HG_ERR_TIMEOUT; + break; + } + + USBCB usb = {0}; + ret = get_scanner_status(usb); + + if (ret == HG_ERR_DEVICE_NOT_FOUND) + { + // device left, the work is no meanning ... + status_ = ret; + break; + } + else if (usb.u32_Data == HAVE_IMAGE) + { + ret = get_img_data(usb.u32_Count); + io_->set_timeout(200); + pop_image(); + sw.reset(); + first = false; + } + else if (ret == HG_ERR_DEVICE_STOPPED) + { + status_ = prev_err; + break; + } + else if(ret != HG_ERR_OK) + { + if (prev_err == HG_ERR_OK) + { + if (first) + { + // if the error occurs at the 2nd paper, then the first normal paper maybe lost, we delay 2 seconds here ... + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + get_scanner_status(usb); + if (usb.u32_Data == HAVE_IMAGE) + { + get_img_data(usb.u32_Count); + io_->set_timeout(200); + pop_image(); + sw.reset(); + first = false; + prev_err = ret; + continue; + } + + status_ = ret; + break; + } + } + else + notify_ui_working_status(hg_scanner::error_description((hg_err)prev_err).c_str(), SANE_EVENT_ERROR, prev_err); + prev_err = ret; + } + this_thread::sleep_for(chrono::milliseconds(2)); + } +} + +int hg_scanner_200::start(void) +{ + int ret = HG_ERR_OK, + count = -1; + + discard_prev(); + user_cancel_ = false; + ret = writedown_device_configuration(true); + if (ret == HG_ERR_OK) + writedown_image_configuration(); + else + { + status_ = ret; + return ret; + } + printf_devconfig(); + + ret = get_scan_is_sleep(); + if (ret == HG_ERR_DEVICE_SLEEPING) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SLEEPING, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_SLEEPING); + return ret; + } + ret = get_scanner_paperon(); + if (ret == HG_ERR_DEVICE_NO_PAPER) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_PAPER, SANE_EVENT_ERROR, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_NO_PAPER); + + return ret; + } + + USBCB usb = {START_COMMAND, img_conf_.scannum, 0}; + + ret = writeusb(usb); + io_->set_timeout(500); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start is.(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + if(ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_BUSY; + wait_usb_.notify(); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + else + status_ = ret; + + return ret; +} +int hg_scanner_200::stop(void) +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {STOP, 0, 0}; + ret = writeusb(usbcb); + + user_cancel_ = true; + if (ret == HG_ERR_DEVICE_BUSY) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO,"stop status is busy\r\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + } + else if (ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_STOPPED; + } + + return status_; +} +int hg_scanner_200::writeusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(usb); + + ret = io_->write_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_200::readusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(USBCB); + + ret = io_->read_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_200::pop_image() +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {POP_IMAGE, 0, 0}; + ret = writeusb(usbcb); + + return ret; +} +int hg_scanner_200::get_scanner_status(USBCB &usb) +{ + int ret = HG_ERR_OK; + + usb = {GET_DSP_STATUS, 0, 0}; + + ret = writeusb(usb); + if (ret != HG_ERR_OK) + return ret; + + io_->set_timeout(500); + + ret = readusb(usb); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + + return settingsdsp_200::device_status_to_hg_err(usb.u32_Data, nullptr); +} +int hg_scanner_200::get_img_data(unsigned int bytes) +{ + int total = bytes, + ret = HG_ERR_OK, + index = 0, + block = total; + std::shared_ptr> imagedata(new std::vector(total)); + + USBCB usb{GET_IMAGE, 0, total}; + ret = writeusb(usb); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + io_->set_timeout(2000); + + while (total > 0) + { + block = 512 * 1024; + + if (total < block) + block = total; + + ret = io_->read_bulk(imagedata->data() + index,&block); + + if (ret != HG_ERR_OK) + break; + + index += block; + total -= block; + } + if (ret == HG_ERR_OK) + { + ret = save_usb_data(imagedata); + } + + return ret; +} + +void hg_scanner_200::image_process(std::shared_ptr>& buffer) +{ + int ret = HG_ERR_OK; + hg_imgproc::IMGPRCPARAM param; + hg_imgproc::HIMGPRC handle = NULL; + hg_imgproc::IMGHEAD ih; + int err = HG_ERR_OK, + index = 0; + void* buf = NULL; + + param.bits = 8; + param.black_white = img_conf_.pixtype == COLOR_MODE_BLACK_WHITE; + param.channels = img_conf_.pixtype == COLOR_MODE_24_BITS ? 3 : 1; + param.color_mode = img_conf_.pixtype; + param.double_side = img_conf_.is_duplex; + param.dpi = img_conf_.resolution_dst; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.black_white :%d\r\n",param.black_white); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.channels :%d\r\n",param.channels); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.color_mode :%d\r\n",param.color_mode); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.double_side :%d\r\n",param.double_side); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.dpi :%d\r\n",param.dpi); + + + handle = hg_imgproc::init(&img_conf_,¶m); + hg_imgproc::load_buffer(handle, buffer); + hg_imgproc::decode(handle,pid_); + + if (img_conf_.fillhole.is_fillhole) + { + ret = hg_imgproc::fillhole(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"濉┛瀛斿紑鍚痋r\n"); + } + + /////////////////////////////////////111111111111111111111111111////////////////////////////////// + ret = hg_imgproc::auto_crop(handle); + + if (img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince) + { + ret = hg_imgproc::discardBlank(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"涓㈠純绌虹櫧椤靛紑鍚痋r\n"); + } + if(img_conf_.fadeback) + { + hg_imgproc::fadeback(handle,img_conf_.fadebackrange,param.double_side); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鑳屾櫙闄よ壊寮鍚痋r\n"); + } + if (img_conf_.resolution_dst != img_conf_.resolution_native) + { + hg_imgproc::resolution_change(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"dpi鏀瑰彉寮鍚痋r\n"); + } + if (img_conf_.cropRect.enable && !img_conf_.is_autocrop) + { + hg_imgproc::croprect(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鎵嬪姩瑁佸浘寮鍚痋r\n"); + } + if (img_conf_.filter != ColorFilter::FILTER_NONE && (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE || img_conf_.pixtype == COLOR_MODE_256_GRAY)) + { + hg_imgproc::channel(handle); + //printf("闄よ壊寮鍚痋r\n"); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闄よ壊寮鍚痋r\n"); + } + /////////////////////////////////////2222222222222222222222222222222222222////////////////////////////////// + hg_imgproc::customgamma(handle,false); //涓存椂false + + if (img_conf_.pixtype == COLOR_MODE_24_BITS && img_conf_.hsvcorrect) + { + hg_imgproc::answerSheetFilterRed(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"绛旈鍗″嚭绾㈠紑鍚痋r\n"); + } + if (img_conf_.refuseInflow) + { + hg_imgproc::antiInflow(handle,image_prc_param_.bits.is_permeate_lv_); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闃叉娓楅忓紑鍚痋r\n"); + } + if (img_conf_.colorCorrection && img_conf_.pixtype != COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::colorCorrection(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"棰滆壊鏍℃寮鍚痋r\n"); + } + if ((img_conf_.is_autotext != TEXT_DIRECTION_0 || img_conf_.is_backrotate180) && (img_conf_.is_autotext != TEXT_DIRECTION_AUTO)) + { + hg_imgproc::orentation(handle); + printf("img_conf_ =%d 鑷姩鏃嬭浆寮鍚痋r\n",img_conf_.is_autotext); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鑷姩鏃嬭浆寮鍚痋r\n"); + } + if (img_conf_.removeMorr) + { + hg_imgproc::textureRemove(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闄ゆ懇灏旂汗寮鍚痋r\n"); + } + if (img_conf_.detachnoise.is_detachnoise) + { + hg_imgproc::nosieDetach(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鍣偣浼樺寲寮鍚痋r\n"); + } + if (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::errorextention(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"榛戠櫧鍥惧鐞嗗紑鍚痋r\n"); + } + if (img_conf_.en_fold) + { + hg_imgproc::fold(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"瀵规姌寮鍚痋r\n"); + } + + /////////////////////////////////// + + if (image_prc_param_.bits.rid_red && img_conf_.pixtype == COLOR_MODE_24_BITS) + { + err = hg_imgproc::multi_out_red(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"澶氭祦鍑虹孩寮鍚痋r\n"); + } + + + if (image_prc_param_.bits.multi_out != MULTI_OUT_NOT && !user_cancel_) + { + //err = hg_imgproc::multi_out(handle,image_prc_param_.bits.multi_out - 1); 闈炴硶鎸囦护 鏆傛椂灞忚斀 + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"澶氭祦杈撳嚭寮鍚痋r\n"); + } + if (img_conf_.sharpen) + { + hg_imgproc::sharpenType(handle); + } + if (image_prc_param_.bits.split && !user_cancel_) + { + int colormode=1; + if(img_conf_.filter == RID_COLOR_NONE) + colormode=img_conf_.pixtype; + + err = hg_imgproc::split(handle,img_conf_.multiOutput,img_conf_.splitImage,img_conf_.multi_output_red,colormode,img_conf_.is_duplex); + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "img split-> multiOutput is:%d splitImage is:%d multi_output_red is:%d pixtype is:%d is_duplex:%d\r\n" + ,img_conf_.multiOutput + ,img_conf_.splitImage + ,img_conf_.multi_output_red + ,img_conf_.pixtype + ,img_conf_.is_duplex); + + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鍥惧儚鎷嗗垎寮鍚痋r\n"); + } + + if (!image_prc_param_.bits.split || !image_prc_param_.bits.rid_red || !image_prc_param_.bits.multi_out) + { + if(img_conf_.automaticcolor) + { + hg_imgproc::auto_matic_color(handle,img_conf_.automaticcolortype); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"棰滆壊鑷姩璇嗗埆寮鍚痋r\n"); + } + } + + hg_imgproc::final(handle); + + while (hg_imgproc::get_final_data(handle, &ih, &buf, index++) == HG_ERR_OK && !user_cancel_) + { + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "Final picture %d (%d * %d * %d) with %u bytes!\n", index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + + std::vectorbmpdata; + if(!img_type_.empty()) + { + hg_imgproc::imgtypechange(handle, img_type_, buf, bmpdata); + //cv::imencode(img_type_,*((cv::Mat*)buf),bmpdata); + if(bmpdata.empty()) + { + status_ = HG_ERR_NO_DATA; + return ; + } + buf = bmpdata.data(); + ih.total_bytes = bmpdata.size(); + + HG_VLOG_MINI_6(HG_LOG_LEVEL_DEBUG_INFO, "Set img type is:%s Final picture %d (%d * %d * %d) with %u bytes!\n",img_type_.c_str() ,index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + } + + save_final_image(&ih, buf); + } + + hg_imgproc::release(handle); +} + +int hg_scanner_200::writedown_device_configuration(bool type,HGSCANCONF_DSP *d) +{ + if (!type) + return HG_ERR_OK; + + int ret = HG_ERR_OK, + len = 0; + + if (!d) + d = &dsp_config_; + + USBCB usbcb = {CONFIGURED_DATA, d->value, 0}; + len = sizeof(USBCB); + ret = io_->write_bulk(&usbcb,&len); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "writedown_device_configuration is .(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} + +int hg_scanner_200::on_color_mode_changed(int& color_mode) +{ + int ret = HG_ERR_OK; + + if (color_mode == COLOR_MODE_BLACK_WHITE || color_mode == COLOR_MODE_256_GRAY) + { + dsp_config_.params.color = 0; + } + else if(color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) + { + dsp_config_.params.color = 1; + } + + return ret; +} +int hg_scanner_200::on_paper_changed(int& paper) +{ + bool exact = true; + int ind = settingsdsp_200::match_best_paper(paper, &exact, &paper_size_), + ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (dsp_config_.params.paper != settingsdsp_200::paper_map[ind].dev_value) + { + int old = dsp_config_.params.paper; + + dsp_config_.params.paper = settingsdsp_200::paper_map[ind].dev_value; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config_.params.paper = old; + for (int i = 0; i < ARRAY_SIZE(settingsdsp_200::paper_map); ++i) + { + if (settingsdsp_200::paper_map[i].dev_value == old) + { + paper = settingsdsp_200::paper_map[i].paper; + break; + } + } + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + } + return ret; +} +int hg_scanner_200::on_paper_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + if(dsp_config_.params.enable_sizecheck ^ check) + { + dsp_config_.params.enable_sizecheck = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config_.params.enable_sizecheck = !check; + check = dsp_config_.params.enable_sizecheck; + } + } + return ret; +} + +int hg_scanner_200::on_resolution_changed(int& dpi) +{ + int ret = HG_ERR_OK; + dsp_config_.params.dpi = 1; + ret = writedown_device_configuration(); + return ret; +} + +int hg_scanner_200::on_ultrasonic_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dsp_config_.params.double_feed_enbale ^ check) + { + dsp_config_.params.double_feed_enbale = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config_.params.double_feed_enbale = !check; + check = dsp_config_.params.double_feed_enbale; + } + } + + return ret; +} + +int hg_scanner_200::on_staple_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dsp_config_.params.stable_enbale ^ check) + { + dsp_config_.params.stable_enbale = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config_.params.stable_enbale = !check; + check = dsp_config_.params.stable_enbale; + } + } + + return ret; +} +int hg_scanner_200::on_skew_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dsp_config_.params.screw_detect_enable ^ check) + { + dsp_config_.params.screw_detect_enable = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config_.params.screw_detect_enable = !check; + check = dsp_config_.params.screw_detect_enable; + } + } + + return ret; +} +int hg_scanner_200::on_skew_check_level_changed(int& check) +{ + int ret = HG_ERR_OK, + val = check, + old = dsp_config_.params.screw_detect_level; + HGSCANCONF_DSP cf; + + cf.params.screw_detect_level = -1; + + if (val < 0) + { + val = 0; + ret = HG_ERR_NOT_EXACT; + } + else if (val > cf.params.screw_detect_level) + { + val = cf.params.screw_detect_level; + ret = HG_ERR_NOT_EXACT; + } + if (val != dsp_config_.params.screw_detect_level) + { + dsp_config_.params.screw_detect_level = val; + ret = writedown_device_configuration(); + if (ret) + check = dsp_config_.params.screw_detect_level = old; + } + if (ret == HG_ERR_OK && val != check) + { + check = val; + ret = HG_ERR_NOT_EXACT; + } + + return ret; +} +int hg_scanner_200::initdevice() +{ + HGSCANCONF_DSP ds; + ds.params.paper = 0; + ds.params.color = 0; + ds.params.double_feed_enbale =0; + ds.params.dpi = 0; + ds.params.enable_sizecheck=0; + ds.params.pc_correct=0; + ds.params.screw_detect_enable=0; + ds.params.screw_detect_level = 0; + ds.params.stable_enbale = 0; + ds.params.unused_one = 0; + ds.params.unused_two = 0; + writedown_device_configuration(true,&ds); + printf_devconfig(&ds); + + return HG_ERR_OK; +} +void hg_scanner_200::writedown_image_configuration(void) +{ + SCANCONF ic; + + ic.papertype = paper_size_; + + if (is_lateral(image_prc_param_.bits.paper)) + ic.paperAlign = Rot270; + else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) + ic.paperAlign = AutoTextOrientation; + else + ic.paperAlign = Rot0; + + ic.en_sizecheck = dsp_config_.params.enable_sizecheck; + ic.imageRotateDegree = image_prc_param_.bits.text_direction * 90.0f; + ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE || image_prc_param_.bits.page == PAGE_OMIT_EMPTY || + image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT || image_prc_param_.bits.page ==PAGE_FOLIO); + + ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); + + ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; + + ic.automaticcolor = is_auto_matic_color; + ic.automaticcolortype = 1;// ic.pixtype; //瀛樼枒 + ic.resolution_dst = resolution_; + ic.resolution_native = 200; + + ic.gamma = (float)gamma_; + ic.contrast = contrast_ ; + ic.brightness = bright_ ; + ic.threshold = threshold_; + ic.is_autocontrast = 0; //鏃犲弬鏁 + ic.is_autocrop = (ic.papertype == TwSS::None || ic.papertype ==TwSS::USStatement); + ic.is_autodiscradblank_normal = image_prc_param_.bits.page == PAGE_OMIT_EMPTY; + ic.discardblank_percent = omit_empty_level_ > 70 ? 70 : omit_empty_level_; //榛樿20 + ic.is_autodiscradblank_vince = image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT; + ic.is_switchfrontback = image_prc_param_.bits.exchange; + ic.autodescrew = image_prc_param_.bits.automatic_skew; + //ic.multi_output_red = image_prc_param_.bits.rid_red; + ic.hsvcorrect = image_prc_param_.bits.rid_answer_red; + + ic.sharpen = image_prc_param_.bits.sharpen; + //ic.enhance_color = image_prc_param_.bits.rid_color; + ic.fillbackground = image_prc_param_.bits.erase_black_frame; + ic.is_convex = (image_prc_param_.bits.fill_background == FILL_BKG_CONVEX_POLYGON); + ic.noise = image_prc_param_.bits.noise_optimize; + ic.indent = margin_; + ic.AutoCrop_threshold = threshold_; + + if (test_1_paper_) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "scanning mode: testing ONE paper ...\n"); + ic.scannum = ic.is_duplex ? 2 : 1; + } + else + { + ic.scannum = (ic.is_duplex ? scan_count_ * 2 : scan_count_); + } + + ic.scannum = scan_count_; + ic.is_backrotate180 = image_prc_param_.bits.rotate_back_180; + ic.is_dogeardetection = image_prc_param_.bits.fractate_check; + + ic.hardwarecaps.en_skrewdetect = dsp_config_.params.screw_detect_enable; + ic.hardwarecaps.en_doublefeed = dsp_config_.params.double_feed_enbale; + ic.hardwarecaps.en_stapledetect = dsp_config_.params.stable_enbale; + ic.hardwarecaps.skrewdetectlevel = dsp_config_.params.screw_detect_level; + // ic.hardwarecaps.is_autopaper = dsp_config_.params.is_autopaper; + ic.hardwarecaps.capturepixtype = 0; //鏆傛棤鍙傛暟 鑾峰彇鍥惧儚绫诲瀷 + ic.hardwarecaps.lowpowermode = LowPowerMode::Min_None; //鏆傛棤鍙傛暟 璁剧疆浼戠湢鏃堕棿 涓や釜鍙傛暟3399鏈娇鐢 + + ic.fillhole.is_fillhole = image_prc_param_.bits.rid_hole; + ic.fillhole.fillholeratio = rid_hole_range_; + + ic.detachnoise.is_detachnoise = image_prc_param_.bits.noise_optimize; + ic.detachnoise.detachnoise = noise_range_; + printf("image_prc_param_.bits.text_direction = %d\r\n",image_prc_param_.bits.text_direction); + + + ic.is_autotext = image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO ? 1 :image_prc_param_.bits.text_direction; + ic.isfillcolor = false;//鏆傛棤鍙傛暟 鏈娇鐢ㄥ埌 + ic.refuseInflow = image_prc_param_.bits.is_permeate; + ic.colorCorrection = 0; + ic.removeMorr = image_prc_param_.bits.remove_morr; + ic.errorExtention = image_prc_param_.bits.error_extention ;//鏆傛棤鍙傛暟 + ic.textureRemove = image_prc_param_.bits.remove_txtture;//鏆傛棤鍙傛暟 + ic.splitImage = image_prc_param_.bits.split; + ic.cropRect.enable = 0;//鏆傛棤鍙傛暟 + ic.cropRect.height = 0;//鏆傛棤鍙傛暟 + ic.cropRect.width = 0;//鏆傛棤鍙傛暟 + ic.cropRect.x = 0;//鏆傛棤鍙傛暟 + ic.cropRect.y = 0;//鏆傛棤鍙傛暟 + ic.multiOutput = MultiOutput::Unused; //鏆傛棤鍙傛暟 + ic.normalCrop = image_prc_param_.bits.dark_sample; + ic.dogeardistabce = fractate_level_; + ic.fadeback = image_prc_param_.bits.erase_bakground; + ic.fadebackrange = erase_bkg_range_; + + int filter_clr[] = { 3, 0, 1, 2, 5, 6, 7 }; + if(image_prc_param_.bits.color_mode == COLOR_MODE_24_BITS || image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH) + ic.filter = 3; + else + { + ic.filter = filter_clr[image_prc_param_.bits.rid_color]; + // if(ic.filter != 3) + // ic.pixtype = 2; + + ic.hsvcorrect = 0; + ic.multi_output_red = 0; + ic.multiOutput = MultiOutput::Unused; + ic.fadeback = false; + } + img_conf_ = ic; + //printf ("ic.resolution_native =%f ic.resolution_dst = %f img_conf_.resolution_dst = %f \r\n",ic.resolution_native,ic.resolution_dst,img_conf_.resolution_dst); + + agreement(); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.papertype=%d", ic.papertype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.AutoCrop_threshold=%d", ic.AutoCrop_threshold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.autodescrew=%d", ic.autodescrew); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.automaticcolor=%d", ic.automaticcolor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.brightness=%f", ic.brightness); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.contrast=%f", ic.contrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_fold=%d", ic.en_fold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_sizecheck=%d", ic.en_sizecheck); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.enhance_color=%d", ic.enhance_color); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillbackground=%d", ic.fillbackground); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.filter=%d", ic.filter); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.gamma=%f", ic.gamma); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.capturepixtype=%d", ic.hardwarecaps.capturepixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.en_doublefeed=%d", ic.hardwarecaps.en_doublefeed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hsvcorrect=%d", ic.hsvcorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.imageRotateDegree=%f", ic.imageRotateDegree); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.indent=%d", 5); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocontrast=%d", ic.is_autocontrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocrop=%d", ic.is_autocrop); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_normal=%d", ic.is_autodiscradblank_normal); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_vince=%d", ic.is_autodiscradblank_vince); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autotext=%d", ic.is_autotext); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_backrotate180=%d", ic.is_backrotate180); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_convex=%d", ic.is_convex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_duplex=%d", ic.is_duplex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_switchfrontback=%d", ic.is_switchfrontback); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_dogeardetection=%d", ic.is_dogeardetection); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.multi_output_red=%d", ic.multi_output_red); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.noise=%d", 8); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_dst=%f", ic.resolution_dst); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_native=%f", ic.resolution_native); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.scannum=%d", ic.scannum); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.sharpen=%d", ic.sharpen); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.discardblank_percent=%d", ic.discardblank_percent); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.is_detachnoise=%d", ic.detachnoise.is_detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.detachnoise=%d\r ", ic.detachnoise.detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.removeMorr=%d\r ", ic.removeMorr); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.errorExtention=%d\r ", ic.errorExtention); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.textureRemove=%d\r\n ", ic.textureRemove); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.is_fillhole=%d\r\n ", ic.fillhole.is_fillhole); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.ic.fillhole.fillholeratio=%f\r\n ", ic.fillhole.fillholeratio); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fadeback=%d\r\n ",ic.fadeback); +} +void hg_scanner_200::printf_devconfig(HGSCANCONF_DSP *d) +{ + io_->set_timeout(200); + if (!d) + d = &dsp_config_; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\r\ndsp_config.params.color:%d\r\n",d->params.color); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.double_feed_enbale:%d\r\n",d->params.double_feed_enbale); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.dpi:%d\r\n",d->params.dpi); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.enable_sizecheck:%d\r\n",d->params.enable_sizecheck); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.paper:%d\r\n",d->params.paper); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.pc_correct:%d\r\n",d->params.pc_correct); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.screw_detect_enable:%d\r\n",d->params.screw_detect_enable); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.screw_detect_level:%d\r\n",d->params.screw_detect_level); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.stable_enbale:%d\r\n",d->params.stable_enbale); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.unused_one:%d\r\n",d->params.unused_one); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.unused_two:%d\r\n",d->params.unused_two); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config_.params.value:%d\r\n",d->value); +} +std::string hg_scanner_200::get_firmware_version() +{ + char buf[20] = { 0 }; + int ret = HG_ERR_OK, + len = 8; //鍗忚瀹氫箟闀垮害涓8 300 400 =10 + USBCB cmd = {GET_FW_VERSION, len, 0,}; + + ret = writeusb(cmd); + if(ret == HG_ERR_OK) + { + std::lock_guard lock(io_lock_); + ret = io_->read_bulk(buf, &len); + } + + return buf; +} + + std::string hg_scanner_200::get_serial_num() + { + char buf[20] = { 0 }; + int ret = HG_ERR_OK, + len = 12; //鍗忚瀹氫箟闀垮害涓8 300 400 =10 + USBCB cmd = {GET_SERIAL, len, 0,}; + + ret = writeusb(cmd); + if (ret == HG_ERR_OK) + { + std::lock_guard lock(io_lock_); + ret = io_->read_bulk(buf, &len); + } + + return buf; + } + + +int hg_scanner_200::agreement() +{ + int ret = HG_ERR_OK; + + + // if (image_prc_param_.bits.rid_color != RID_COLOR_NONE) + // { + // dsp_config_.params.color = 1; + // } + + // ret = writedown_device_configuration(true); + // io_->set_timeout(200); + + return ret; +} + + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int hg_scanner_200::set_leaflet_scan(void) +{ + int ret = HG_ERR_OK; + test_1_paper_ = true; + + ret = start(); + return ret; +} +int hg_scanner_200::get_abuot_info(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::restore_default_setting(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::set_img_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_200::get_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::set_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::set_auto_color_type(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_200::get_device_code(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::get_sleep_time(SANE_Power* getime) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_SLEEP_TIME, 0, 0}; + ret = writeusb(usbcb); + if (ret != HG_ERR_OK) + { + return ret; + } + len = sizeof(usbcb); + ret = io_->read_bulk(&usbcb,&len); + printf("usbcb.u32_Data = %d\r\n",usbcb.u32_Data); + if (ret == HG_ERR_OK) + { + if (getime) + { + *getime = (SANE_Power)usbcb.u32_Data; + } + if (usbcb.u32_Data) + { + return HG_ERR_DEVICE_SLEEPING; + } + } + return ret; +} +int hg_scanner_200::set_sleep_time(SANE_Power* setime) +{ + if (!setime) + { + return HG_ERR_NO_DATA; + } + + int ret = HG_ERR_OK, + time = *setime; + USBCB usbcb = {SET_SLEEP_TIME,time, 0}; + + ret = writeusb(usbcb); + + return ret; +} + +int hg_scanner_200::get_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::set_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::get_scanner_paperon(SANE_Bool* paperon) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_PAPER_STATUS, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + io_->set_timeout(500); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0) + ret = HG_ERR_DEVICE_NO_PAPER; + else + ret = HG_ERR_OK; + if (paperon) + { + *paperon = usbcb.u32_Data; + } + return ret; +} +int hg_scanner_200::set_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::get_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::get_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::set_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_200::get_scan_is_sleep() +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {0x100, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + + io_->set_timeout(200); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0x10) + { + return HG_ERR_OK; + } + else if (usbcb.u32_Data == 0x100) + { + return HG_ERR_DEVICE_SLEEPING; + } +} diff --git a/hgdriver/hgdev/hg_scanner_200.h b/hgdriver/hgdev/hg_scanner_200.h new file mode 100644 index 0000000..d1bcbba --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_200.h @@ -0,0 +1,85 @@ +锘#pragma once + +// hg_scanner is the base class of kinds of scanners +// +// created on 2022-01-30 +// +#include +#include +#include +#include + +#include "hg_scanner.h" +#include "../../sdk/hginclude/hg_log.h" + + +class hg_scanner_200 : public hg_scanner +{ + void discard_prev(void); + +protected: + virtual int on_scanner_closing(bool force) override; + virtual void thread_handle_usb_read(void) override; + virtual void image_process(std::shared_ptr>& buff) override; + + + virtual int on_color_mode_changed(int& color_mode); // COLOR_MODE_xxx + virtual int on_paper_changed(int& paper); // PAPER_xxx + virtual int on_paper_check_changed(bool& check); + virtual int on_resolution_changed(int& dpi); + virtual int on_ultrasonic_check_changed(bool& check); + virtual int on_staple_check_changed(bool& check); + virtual int on_skew_check_changed(bool& check); + virtual int on_skew_check_level_changed(int& check); + +public: + hg_scanner_200(const char* dev_name,int vid, usb_io* io); + ~hg_scanner_200(); + +public: + virtual int start(void)override; + virtual int stop(void)override; + void init_setting_map(int* setting_map, int count)override; + +private: + int initdevice(); + int writeusb(USBCB &usb); + int readusb(USBCB &usb); + int pop_image(); + int get_scanner_status(USBCB &usb); + int get_img_data(unsigned int bytes); + int writedown_device_configuration(bool type =false,HGSCANCONF_DSP *d = NULL); + void writedown_image_configuration(void); + void printf_devconfig(HGSCANCONF_DSP *d = NULL); + virtual std::string get_firmware_version(void) override; + virtual std::string get_serial_num(void) override; + HGSCANCONF_DSP dsp_config_; + int agreement(); + +private: + int pid_; + +public: + virtual int set_leaflet_scan(void);//鍗曞紶鎵弿 + virtual int get_abuot_info(void);//鑾峰彇杞欢鍏充簬淇℃伅 + virtual int restore_default_setting(void);//鎭㈠榛樿璁剧疆 + virtual int set_img_format(void); // 璁剧疆鍥惧儚澶勭悊鏈缁堣緭鍑猴紙final()锛夌殑鍥惧儚鏁版嵁鏍煎紡 + + virtual int get_compression_format(void);//鑾峰彇鏀寔鐨勫帇缂╂牸寮 + virtual int set_compression_format(void);//璁剧疆鍥惧儚鏁版嵁鏈缁堣緭鍑虹殑鍘嬬缉鏍煎紡 + virtual int set_auto_color_type(void);// 璁剧疆鑷姩鍖归厤棰滆壊妯″紡 + + virtual int get_device_code(void);//鑾峰彇璁惧缂栫爜 + virtual int get_sleep_time(SANE_Power* setime = NULL);//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛 + virtual int set_sleep_time(SANE_Power* setime);//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛 + + virtual int get_dogear_distance(void);//鑾峰彇鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int set_dogear_distance(void);// 璁剧疆鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int get_scanner_paperon(SANE_Bool* paperon=NULL);//鑾峰彇璁惧鏈夋棤绾稿紶 + virtual int set_scan_when_paper_on(void);//鑾峰彇鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_when_paper_on(void);//璁剧疆鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_with_hole(void);// 鑾峰彇鏄惁涓哄甫瀛旀壂鎻 + virtual int set_scan_with_hole(void);// 璁剧疆鏄惁涓哄甫瀛旀壂鎻 + + virtual int get_scan_is_sleep(void);//鑾峰彇璁惧鏄惁浼戠湢褰撲腑 +}; diff --git a/hgdriver/hgdev/hg_scanner_239.cpp b/hgdriver/hgdev/hg_scanner_239.cpp new file mode 100644 index 0000000..1a5ae83 --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_239.cpp @@ -0,0 +1,1832 @@ +锘#include "hg_scanner_239.h" +#include "../../sdk/hginclude/hg_log.h" +#include "filetools.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + +#include + +//#define SAVE_TO_FILE + +#define USB_REQ_GET_FPGA_REGS 0x40 +#define USB_REQ_SET_FPGA_REGS 0x41 +#define USB_REQ_GET_MOTOR_REGS 0x42 +#define USB_REQ_SET_MOTOR_REGS 0x43 + +#define USB_REQ_GET_DEV_STATUS 0x60 +#define USB_REQ_GET_DEV_CONFIGURATION 0x61 +#define USB_REQ_SET_DEV_CONFIGURATION 0x62 +#define USB_REQ_GET_DEV_REGS 0x63 +#define USB_REQ_SET_DEV_REGS 0x64 + + +static std::string jsontext("{\"device_type\":\"G239\",\"option_count\":50,\"1\":{\"category\":\"base\",\"name\":\"cfg-1\",\"title\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"desc\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"type\":\"button\",\"cur\":\"button\",\"default\":\"button\",\"size\":0},\"2\":{\"category\":\"advanced\",\"name\":\"cfg-2\",\"title\":\"\\u5e2e\\u52a9\",\"desc\":\"\\u663e\\u793a\\u8f6f\\u4ef6\\u5e2e\\u52a9\\u6587\\u6863\",\"type\":\"button\",\"cur\":\"true\",\"default\":\"true\",\"size\":4},\"3\":{\"category\":\"base\",\"name\":\"grp-1\",\"title\":\"\\u57fa\\u672c\\u8bbe\\u7f6e\",\"type\":\"group\"},\"4\":{\"category\":\"base\",\"name\":\"cfg-4\",\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6e\\u989c\\u8272\\u4f4d\\u6df1\",\"type\":\"string\",\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"size\":24,\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"]},\"5\":{\"category\":\"base\",\"name\":\"cfg-5\",\"title\":\"\\u7070\\u5ea6\\u6216\\u9ed1\\u767d\\u56fe\\u50cf - \\u9664\\u8272\",\"desc\":\"\\u9664\\u53bb\\u56fe\\u50cf\\u5f69\\u8272\",\"type\":\"string\",\"cur\":\"\\u4e0d\\u9664\\u8272\",\"default\":\"\\u4e0d\\u9664\\u8272\",\"size\":16,\"range\":[\"\\u4e0d\\u9664\\u8272\",\"\\u9664\\u7ea2\\u8272\",\"\\u9664\\u7eff\\u8272\",\"\\u9664\\u84dd\\u8272\",\"\\u7ea2\\u8272\\u589e\\u5f3a\",\"\\u7eff\\u8272\\u589e\\u5f3a\",\"\\u84dd\\u8272\\u589e\\u5f3a\"],\"depend_or\":[\"4==256\\u7ea7\\u7070\\u5ea6\",\"4==\\u9ed1\\u767d\"]},\"6\":{\"category\":\"base\",\"name\":\"cfg-6\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u591a\\u6d41\\u8f93\\u51fa\\u9664\\u7ea2\",\"desc\":\"\\u591a\\u901a\\u9053\\u8f93\\u51fa\\u4e2d\\uff0c\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"7\":{\"category\":\"base\",\"name\":\"cfg-7\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u7b54\\u9898\\u5361\\u9664\\u7ea2\",\"desc\":\"\\u7b54\\u9898\\u5361\\u626b\\u63cf\\u4e2d\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"8\":{\"category\":\"base\",\"name\":\"cfg-8\",\"title\":\"\\u80cc\\u666f\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u5f69\\u8272\\u56fe\\u50cf\\u80cc\\u666f\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"9\":{\"category\":\"base\",\"name\":\"cfg-9\",\"title\":\" \\u80cc\\u666f\\u8272\\u5f69\\u6d6e\\u52a8\\u8303\\u56f4\",\"desc\":\"\\u8bbe\\u5b9a\\u80cc\\u666f\\u8272\\u5f69\\u7684\\u6d6e\\u52a8\\u8303\\u56f4\\uff0c\\u5728\\u8be5\\u8303\\u56f4\\u5185\\u7684\\u90fd\\u5f53\\u4f5c\\u80cc\\u666f\\u79fb\\u9664\",\"type\":\"int\",\"cur\":10,\"default\":10,\"size\":4,\"range\":{\"min\":1,\"max\":40},\"depend_or\":[\"8==true\"]},\"10\":{\"category\":\"base\",\"name\":\"cfg-10\",\"title\":\"\\u9ed1\\u767d\\u56fe\\u50cf\\u566a\\u70b9\\u4f18\\u5316\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==\\u9ed1\\u767d\"]},\"11\":{\"category\":\"base\",\"name\":\"cfg-11\",\"title\":\" \\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"type\":\"int\",\"cur\":30,\"default\":30,\"size\":4,\"range\":{\"min\":10,\"max\":50},\"depend_or\":[\"10==true\"]},\"12\":{\"category\":\"base\",\"name\":\"cfg-12\",\"title\":\"\\u7eb8\\u5f20\\u5c3a\\u5bf8\",\"desc\":\"\\u8bbe\\u7f6e\\u7eb8\\u5f20\\u5927\\u5c0f\",\"type\":\"string\",\"cur\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"default\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"size\":44,\"range\":[\"A3\",\"8\\u5f00\",\"A4\",\"A4\\u6a2a\\u5411\",\"16\\u5f00\",\"16\\u5f00\\u6a2a\\u5411\",\"A5\",\"A5\\u6a2a\\u5411\",\"A6\",\"A6\\u6a2a\\u5411\",\"B4\",\"B5\",\"B5\\u6a2a\\u5411\",\"B6\",\"B6\\u6a2a\\u5411\",\"Letter\",\"Letter\\u6a2a\\u5411\",\"Double Letter\",\"LEGAL\",\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"\\u4e09\\u8054\\u8bd5\\u5377\"]},\"13\":{\"category\":\"base\",\"name\":\"cfg-13\",\"title\":\"\\u5c3a\\u5bf8\\u68c0\\u6d4b\",\"desc\":\"\\u68c0\\u6d4b\\u7eb8\\u5f20\\u5b9e\\u9645\\u5c3a\\u5bf8\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"12==A3\",\"12==A4\",\"12==A4\\u6a2a\\u5411\",\"12==A5\",\"12==A5\\u6a2a\\u5411\",\"12==A6\",\"12==A6\\u6a2a\\u5411\",\"12==B4\",\"12==B5\",\"12==B5\\u6a2a\\u5411\",\"12==B6\",\"12==B6\\u6a2a\\u5411\",\"12==Double Letter\",\"12==LEGAL\",\"12==Letter\",\"12==Letter\\u6a2a\\u5411\"]},\"14\":{\"category\":\"base\",\"name\":\"cfg-14\",\"title\":\"\\u626b\\u63cf\\u9875\\u9762\",\"desc\":\"\\u8bbe\\u7f6e\\u9875\\u9762\\u626b\\u63cf\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u53cc\\u9762\",\"default\":\"\\u53cc\\u9762\",\"size\":40,\"range\":[\"\\u5355\\u9762\",\"\\u53cc\\u9762\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\",\"\\u5bf9\\u6298\"]},\"15\":{\"category\":\"base\",\"name\":\"cfg-15\",\"title\":\" \\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7075\\u654f\\u5ea6\",\"desc\":\"\\u7075\\u654f\\u5ea6\\u8d8a\\u9ad8\\uff0c\\u5219\\u8d8a\\u5bb9\\u6613\\u8df3\\u8fc7\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":1,\"max\":100},\"depend_or\":[\"14==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"14==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\"]},\"16\":{\"category\":\"base\",\"name\":\"cfg-16\",\"title\":\"\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u626b\\u63cf\\u4eea\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":{\"min\":100,\"max\":300}},\"17\":{\"category\":\"base\",\"name\":\"cfg-17\",\"title\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"desc\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"14!=\\u5355\\u9762\"]},\"18\":{\"category\":\"base\",\"name\":\"cfg-18\",\"title\":\"\\u56fe\\u50cf\\u62c6\\u5206\",\"desc\":\"\\u81ea\\u52a8\\u62c6\\u5206\\u56fe\\u50cf\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"14!=\\u5bf9\\u6298\"]},\"19\":{\"category\":\"base\",\"name\":\"cfg-19\",\"title\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"desc\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4,\"depend_or\":[\"14!=\\u5bf9\\u6298\"]},\"20\":{\"category\":\"base\",\"name\":\"cfg-20\",\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u7eb8\\u5f20\\u4e2d\\u7684\\u7a7f\\u5b54\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"21\":{\"category\":\"base\",\"name\":\"cfg-21\",\"title\":\" \\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":0.500000},\"depend_or\":[\"20==true\"]},\"22\":{\"category\":\"base\",\"name\":\"grp-2\",\"title\":\"\\u4eae\\u5ea6\",\"type\":\"group\"},\"23\":{\"category\":\"base\",\"name\":\"cfg-23\",\"title\":\"\\u4eae\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4eae\\u5ea6\",\"type\":\"int\",\"cur\":128,\"default\":128,\"size\":4,\"range\":{\"min\":1,\"max\":255}},\"24\":{\"category\":\"base\",\"name\":\"cfg-24\",\"title\":\"\\u5bf9\\u6bd4\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u5bf9\\u6bd4\\u5ea6\",\"type\":\"int\",\"cur\":4,\"default\":4,\"size\":4,\"range\":{\"min\":1,\"max\":7}},\"25\":{\"category\":\"base\",\"name\":\"cfg-25\",\"title\":\"\\u4f3d\\u739b\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4f3d\\u739b\\u503c\",\"type\":\"float\",\"cur\":1.000000,\"default\":1.000000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":5.000000}},\"26\":{\"category\":\"base\",\"name\":\"grp-3\",\"title\":\"\\u56fe\\u50cf\\u5904\\u7406\",\"type\":\"group\"},\"27\":{\"category\":\"base\",\"name\":\"cfg-27\",\"title\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"desc\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"type\":\"string\",\"cur\":\"\\u65e0\",\"default\":\"\\u65e0\",\"size\":20,\"range\":[\"\\u65e0\",\"\\u9510\\u5316\",\"\\u8fdb\\u4e00\\u6b65\\u9510\\u5316\",\"\\u6a21\\u7cca\",\"\\u8fdb\\u4e00\\u6b65\\u6a21\\u7cca\"]},\"28\":{\"category\":\"base\",\"name\":\"cfg-28\",\"title\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"desc\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"29\":{\"category\":\"base\",\"name\":\"cfg-29\",\"title\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"desc\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"14!=\\u5bf9\\u6298\",\"28!=true\",\"12!=\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"19!=true\"]},\"30\":{\"category\":\"advanced\",\"name\":\"cfg-30\",\"title\":\"\\u9608\\u503c\",\"desc\":\"\\u9608\\u503c\",\"type\":\"int\",\"cur\":40,\"default\":40,\"size\":4,\"range\":{\"min\":30,\"max\":50},\"depend_or\":[\"28==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"19==true\"]},\"31\":{\"category\":\"advanced\",\"name\":\"cfg-31\",\"title\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"desc\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"type\":\"int\",\"cur\":8,\"default\":8,\"size\":4,\"range\":{\"min\":1,\"max\":20},\"depend_or\":[\"28==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"19==true\"]},\"32\":{\"category\":\"advanced\",\"name\":\"cfg-32\",\"title\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"desc\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"type\":\"int\",\"cur\":5,\"default\":5,\"size\":4,\"range\":{\"min\":5,\"max\":30},\"depend_or\":[\"28==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"19==true\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\"]},\"33\":{\"category\":\"advanced\",\"name\":\"cfg-33\",\"title\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"desc\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"default\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"size\":40,\"range\":[\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"\\u51f9\\u591a\\u8fb9\\u5f62\"],\"depend_or\":[\"28==true\"]},\"34\":{\"category\":\"base\",\"name\":\"cfg-34\",\"title\":\"\\u9632\\u6b62\\u6e17\\u900f\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"35\":{\"category\":\"base\",\"name\":\"cfg-35\",\"title\":\" \\u9632\\u6b62\\u6e17\\u900f\\u7b49\\u7ea7\",\"desc\":\"\",\"type\":\"string\",\"cur\":\"\\u8f83\\u5f31\",\"default\":\"\\u8f83\\u5f31\",\"size\":12,\"range\":[\"\\u8f83\\u5f31\",\"\\u5f31\",\"\\u4e00\\u822c\",\"\\u5f3a\",\"\\u8f83\\u5f3a\"],\"depend_or\":[\"34==true\"]},\"36\":{\"category\":\"base\",\"name\":\"cfg-36\",\"title\":\"\\u53bb\\u9664\\u6469\\u5c14\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"37\":{\"category\":\"base\",\"name\":\"cfg-37\",\"title\":\"\\u9519\\u8bef\\u6269\\u6563\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"38\":{\"category\":\"base\",\"name\":\"cfg-38\",\"title\":\"\\u9664\\u7f51\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"39\":{\"category\":\"base\",\"name\":\"grp-4\",\"title\":\"\\u9001\\u7eb8\\u65b9\\u5f0f\\u8bbe\\u7f6e\",\"type\":\"group\"},\"40\":{\"category\":\"base\",\"name\":\"cfg-40\",\"title\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\",\"desc\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\\u9001\\u7eb8\\u72b6\\u6001\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"41\":{\"category\":\"base\",\"name\":\"cfg-41\",\"title\":\"\\u88c5\\u8ba2\\u68c0\\u6d4b\",\"desc\":\"\\u68c0\\u6d4b\\u7eb8\\u5f20\\u4e0a\\u662f\\u5426\\u6709\\u9489\\u4e66\\u9489\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"42\":{\"category\":\"base\",\"name\":\"cfg-42\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u626b\\u63cf\\u7eb8\\u5f20\\u6570\\u91cf\",\"type\":\"string\",\"cur\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"default\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"size\":24,\"range\":[\"\\u8fde\\u7eed\\u626b\\u63cf\",\"\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"43\":{\"category\":\"base\",\"name\":\"cfg-43\",\"title\":\" \\u626b\\u63cf\\u6570\\u91cf\",\"desc\":\"\\u626b\\u63cf\\u6307\\u5b9a\\u6570\\u91cf\",\"type\":\"int\",\"cur\":1,\"default\":1,\"size\":4,\"depend_or\":[\"42==\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"44\":{\"category\":\"base\",\"name\":\"cfg-44\",\"title\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"desc\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"type\":\"string\",\"cur\":\"0\\u00b0\",\"default\":\"0\\u00b0\",\"size\":16,\"range\":[\"0\\u00b0\",\"90\\u00b0\",\"180\\u00b0\",\"-90\\u00b0\"]},\"45\":{\"category\":\"base\",\"name\":\"cfg-45\",\"title\":\"\\u80cc\\u9762\\u65cb\\u8f6c180\\u00b0\",\"desc\":\"\\u80cc\\u9762\\u626b\\u63cf\\u7684\\u56fe\\u50cf\\u65cb\\u8f6c180\\u00b0\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"14!=\\u5355\\u9762\",\"14!=\\u5bf9\\u6298\",\"44!=\\u81ea\\u52a8\\u6587\\u672c\\u65b9\\u5411\\u8bc6\\u522b\\u00b0\"]},\"46\":{\"category\":\"base\",\"name\":\"cfg-46\",\"title\":\"\\u6298\\u89d2\\u68c0\\u6d4b\",\"desc\":\"\\u7eb8\\u5f20\\u6298\\u89d2\\u68c0\\u6d4b\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"47\":{\"category\":\"base\",\"name\":\"cfg-47\",\"title\":\" \\u6298\\u89d2\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"desc\":\"\\u7eb8\\u5f20\\u6298\\u89d2\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"type\":\"int\",\"cur\":10,\"default\":10,\"size\":4,\"range\":{\"min\":0,\"max\":100},\"depend_or\":[\"46==true\"]},\"48\":{\"category\":\"base\",\"name\":\"cfg-48\",\"title\":\"\\u6b6a\\u659c\\u68c0\\u6d4b\",\"desc\":\"\\u7eb8\\u5f20\\u6b6a\\u659c\\u68c0\\u6d4b\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"49\":{\"category\":\"base\",\"name\":\"cfg-49\",\"title\":\" \\u6b6a\\u659c\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"desc\":\"\\u7eb8\\u5f20\\u6b6a\\u659c\\u68c0\\u6d4b\\u590d\\u6742\\u5ea6\",\"type\":\"int\",\"cur\":3,\"default\":3,\"size\":4,\"range\":{\"min\":1,\"max\":5},\"depend_or\":[\"48==true\"]}}"); +enum hg_239_setting_item +{ + HG_239_SETTING_RESTORE = 1, // 鎭㈠榛樿璁剧疆 + HG_239_SETTING_HELP, // 甯姪 + + HG__239_SETTING_GROUP_1, + + HG_239_SETTING_COLOR_MODE, // 棰滆壊妯″紡 + + HG_239_SETTING_ERASE_COLOR, // 闄よ壊 + HG_239_SETTING_ERASE_MULTI_RED, // 澶氭祦杈撳嚭闄ょ孩 + HG_239_SETTING_ERASE_ANSWER_RED, // 绛旈鍗¢櫎绾 + HG_239_SETTING_ERASE_BACKGROUND, // 绉婚櫎鑳屾櫙 + HG_239_SETTING_ERASE_BKG_RANGE, // 绉婚櫎鑳屾櫙鑼冨洿 + HG_239_SETTING_NOISE, // 榛戠櫧鍥惧儚鍣偣浼樺寲 + HG_239_SETTING_NOISE_SIZE, // 鍣偣浼樺寲灏哄 + HG_239_SETTING_PAPER_SIZE, // 绾稿紶灏哄 + HG_239_SETTING_PAPER_SIZE_CHECK, // 灏哄妫娴 + HG_239_SETTING_PAGE, // 鎵弿椤甸潰 + HG_239_SETTING_SKIP_NILL, // 璺宠繃绌虹櫧椤电伒鏁忓害 + HG_239_SETTING_RESOLUTION, // 鍒嗚鲸鐜 + HG_239_SETTING_REVERSE, // 浜ゆ崲姝e弽闈 + HG_239_SETTING_SPLIT, // 鍥惧儚鎷嗗垎 + HG_239_SETTING_CORRECT, // 鑷姩绾犲亸 + HG_239_SETTING_ERASE_HOLE, // 绌垮瓟绉婚櫎 + HG_239_SETTING_HOLE_SEARCH, // 绌垮瓟鎼滅储鑼冨洿 + + HG__239_SETTING_GROUP_2, + + HG_239_SETTING_LIGHT, // 浜害 + HG_239_SETTING_CONTRAST, // 瀵规瘮搴 + HG_239_SETTING_GAMMA, // 浼界帥 + + HG__239_SETTING_GROUP_3, + + HG_239_SETTING_SHARP, // 閿愬寲涓庢ā绯 + HG_239_SETTING_ERASE_BLACK_BORDER, // 娑堥櫎榛戞 + HG_239_SETTING_SAMPLE, // 娣辫壊鏍峰紶 + HG_239_SETTING_THRESHOLD, // 闃堝 + HG_239_SETTING_ANTI_NOISE, // 鑳屾櫙鎶楀櫔绛夌骇 + HG_239_SETTING_MARGIN, // 杈圭紭缂╄繘 + HG_239_SETTING_FILLING, // 鑳屾櫙濉厖鏂瑰紡 + HG_239_SETTING_PERMEATE, // 闃叉娓楅 + HG_239_SETTING_PERMEATE_lv, // 闃叉娓楅忕瓑绾 + HG_239_SETTING_REMOVE_MORR, // 鍘婚櫎鎽╁皵绾 + HG_239_SETTING_ERROR_EXTENTION, // 閿欒鎵╂暎 + HG_239_SETTING_REMOVE_TXTTURE, // 闄ょ綉绾 + + HG__239_SETTING_GROUP_4, + + HG_239_SETTING_ULTRASONIC, // 瓒呭0娉㈡娴 + HG_239_SETTING_STAPLE, // 瑁呰妫娴 + HG_239_SETTING_SCAN_METHOD, // 杩炵画鎵弿鎴栨壂鎻忔寚瀹氬紶鏁 + HG_239_SETTING_SCAN_COUNT, // 鎵弿鎸囧畾鏁伴噺 + HG_239_SETTING_DIRECTION, // 鏂囩ǹ鏂瑰悜 鍒犻櫎鏂囩ǹ鑷姩璇嗗埆 + HG_239_SETTING_ROTATE, // 鑳岄潰鏃嬭浆180掳 + HG_239_SETTING_FRACTATE, // 鎶樿妫娴 + HG_239_SETTING_FRACTATE_COMPLEX, // 鎶樿妫娴嬪鏉傚害 + HG_239_SETTING_SCREW, // 姝枩妫娴 + HG_239_SETTING_SCREW_COMPLEX, // 姝枩妫娴嬪鏉傚害 + + + HG_239_SETTING_END, + + //鏆傛椂灞忚斀 + HG_239_SETTING_MULTI_OUT = 500, // 澶氭祦杈撳嚭 +}; +enum Scanner_Reg_Defs +{ + SR_CMD, + SR_STATUS, + SR_SCAN_COUNT, + SR_OS, + SR_SENSORS, + SR_MOTOR, + SR_IM_TYPE, + SR_IM_COUNT, + SR_IM_TX, + SR_IM_FRONT_SIZE, + SR_IM_CLEAR, + SR_IM_TXING, + SR_IM_POP, + SR_IM_ABORT, + SR_COUNT, + SR_CONFIG_SCAN_PARAM, + SR_GET_FWVERSION, + SR_SET_FWERSION, + SR_GET_SERIALNUM, + SR_SET_SERIALNUM, + SR_CONFIF_IMGPROCPARAM, + SC_AUTOCORRECT, + SC_GET_CORRECT_PARAM, + SC_SET_CORRECT_PARAM, + SR_GET_H_RATIO, + SR_SET_H_RATIO, + SR_GET_V_RATIO, + SR_SET_V_RATIO, + SR_GET_SERIAL_LEN, + SR_GET_GRAY_SP, + SR_GET_COLOR_SP, + SR_SET_GRAY_SP, + SR_SET_COLOR_SP, + SR_SET_SLEEPTIME, + SR_GET_SLEEPTIME, + SR_GET_SLEEP_STAUTUS, + SR_GET_IMAGEPROCESSDONE, + SR_GET_KEEP_LAST_PAPER, + SR_GET_PAPERON, + SR_SET_SPEEDMODE, + SR_GET_SPEEDMODE, + SR_GET_CUO_ERROR = 0x50, + SR_GET_DOU_ERROR, + SR_GET_JAM_ERROR, + SR_GET_SCANN_NUM, + SR_CLR_ROLLER_NUM, + SR_GET_ROLLER_NUM, + SR_UPDATA_START = 0x100, + SR_UPDATA_STAUTUS = 0x101, + SR_UPDATA_MD5_RELUST = 0x102, + SR_UPDATA_RECOVERY = 0x103, + SR_UPDATA_REBOOT = 0x104, + SR_POWEROFF = 0x105, + SR_REBOOT = 0x106, + SR_FLAT_CLR_MAX_BRIGHT, + SR_FLAT_GRAY_MAX_BRIGHT, + SR_KERNEL_VERSION_INFO_LENGTH, + SR_GET_KERNEL_VERSION, + SR_GET_MBSTATUS, + SR_GET_IPADDR_LENGHT = 0x200, + SR_GET_MACADDR, + SR_GET_IPADDR, + SR_GET_MBVERSION_LENGHT, + SR_GET_MBVERSION, + SR_GET_USBVIDPID, + SR_SET_USBVIDPID +}; +enum Scanner_Cmd_Defs +{ + SC_START, + SC_STOP, + SC_CLEAR, + SC_COUNT +}; +enum HGType +{ + MtBoard = 1, + FPGA, + V4L2, + IMG, + AutoCorrect, + STOPSCAN, +}; +struct HGEIntInfo +{ + HGType From; + unsigned int Code; + unsigned int Img_Index; +}; +namespace settings +{ + // device status to hg_err + hg_err device_status_to_hg_err(HGEIntInfo* ds) + { + hg_err code = HG_ERR_OK; + + if (ds->From == HGType::FPGA) + code = HG_ERR_OK; + else if (ds->From == HGType::MtBoard) + { + switch (ds->Code) + { + case 0x00002: + code = HG_ERR_DEVICE_NO_PAPER; + break; + case 0x00004: + code = HG_ERR_DEVICE_COVER_OPENNED; + break; + case 0x00008: + code = HG_ERR_DEVICE_FEEDING_PAPER; + break; + case 0x00010: + code = HG_ERR_DEVICE_PAPER_JAMMED; + break; + case 0x00020: + code = HG_ERR_DEVICE_DOUBLE_FEEDING; + break; + case 0x00040: + code = HG_ERR_DEVICE_STAPLE_ON; + break; + case 0x00080: + code = HG_ERR_DEVICE_PAPER_SKEW; + break; + case 0x10000: + case 0x80000: + code = HG_ERR_TIMEOUT; + break; + case 0x20000: + code = HG_ERR_DEVICE_SIZE_CHECK; + break; + default: + break; + } + } + else if (ds->From == HGType::IMG) + { + if (ds->Code == 1) + code = HG_ERR_DEVICE_DOGEAR; + else if (ds->Code == 2) + code = HG_ERR_DEVICE_SIZE_CHECK; + } + else if (ds->From == HGType::V4L2) + { + if (ds->Code == 0) + code = HG_ERR_DEVICE_SCANN_ERROR; + else if (ds->Code == 1) + code = HG_ERR_DEVICE_NO_IMAGE; + } + else if (ds->From == HGType::STOPSCAN) + code = HG_ERR_DEVICE_STOPPED; + + return code; + } + + // resolution + static struct + { + float resolution; + int dev_value; + }resolution_map[] = + { + {100.0f, 1}, + {150.0f, 1}, + {200.0f, 1}, + {240.0f, 1}, + {300.0f, 0}, + {600.0f, 1} //涓存椂鐗堟湰鍙1 鍘熸潵鍙2 + }; + bool is_equal(float l, float r) + { + return fabs(l - r) < .000001f; + } + int match_best_resolution(int resolution, bool* exact) + { + int rsl = resolution, + ind = 0; + bool good = true, *r = exact ? exact : &good; + + if (rsl <= resolution_map[0].resolution) + { + ind = 0; + } + else if (rsl >= resolution_map[ARRAY_SIZE(resolution_map) - 1].resolution) + { + ind = ARRAY_SIZE(resolution_map) - 1; + } + else + { + for (int i = 1; i < ARRAY_SIZE(resolution_map); ++i) + { + if (is_equal(rsl, resolution_map[i].resolution)) + { + ind = i; + break; + } + if (rsl < resolution_map[i].resolution) + { + float dif_l = rsl - resolution_map[i - 1].resolution, + dif_r = resolution_map[i].resolution - rsl; + + if (dif_l > dif_r) + ind = i; + else + ind = i - 1; + break; + } + } + } + + *r = is_equal(rsl, resolution_map[ind].resolution); + + return ind; + } + + // paper + static struct + { + int paper; + TwSS type; + int dev_value; + }paper_map[] = + { + {PAPER_A3, TwSS::A3, 0}, + {PAPER_8K, TwSS::K8, 0}, + {PAPER_A4, TwSS::A4, 1}, + {PAPER_A4_LATERAL, TwSS::A4, 2}, + {PAPER_16K, TwSS::K16, 0}, + {PAPER_16K_LATERAL, TwSS::K16, 0}, + {PAPER_A5, TwSS::A5, 2}, + {PAPER_A5_LATERAL, TwSS::A5, 2}, + {PAPER_A6, TwSS::A6, 2}, + {PAPER_A6_LATERAL, TwSS::A6, 2}, + {PAPER_B4, TwSS::B4, 0}, + {PAPER_B5, TwSS::B5, 0}, + {PAPER_B5_LATERAL, TwSS::B5, 1}, + {PAPER_B6, TwSS::B6, 2}, + {PAPER_B6_LATERAL, TwSS::B6, 2}, + {PAPER_LETTER, TwSS::USLetter, 14}, + {PAPER_LETTER_LATERAL, TwSS::USLetter, 1}, + {PAPER_DOUBLE_LETTER, TwSS::USLedger, 2}, + {PAPER_LEGAL, TwSS::USLegal, 0}, + {PAPER_AUTO_MATCH, TwSS::None, 0}, + {PAPER_MAX_SIZE_CLIP, TwSS::USStatement, 16}, + {PAPER_MAX_SIZE, TwSS::MaxSize, 16}, + {PAPER_TRIGEMINY, TwSS::Trigeminy, 16} + }; + int match_best_paper(int& paper, bool* exact, TwSS* type) + { + int ind = 0; + bool good = true, * r = exact ? exact : &good; + + for (int i = 0; i < ARRAY_SIZE(paper_map); ++i) + { + if (paper == paper_map[i].paper) + { + ind = i; + break; + } + } + + if (paper == paper_map[ind].paper) + *r = true; + else + *r = false; + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Paper '%s' index = %d, device value = %d\n", paper_string(paper).c_str(), ind, paper_map[ind].dev_value); + paper = paper_map[ind].paper; + if (type) + *type = paper_map[ind].type; + + return ind; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class ... +hg_scanner_239::hg_scanner_239(const char* dev_name, int pid,usb_io* io) : hg_scanner(G20039Serial, dev_name, io) + , rewrite_conf_(false), reset_(false),pid_(pid) +{ + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "hg_scanner_239(%s) constructing ...\n", hg_log::format_ptr(this).c_str()); + dev_conf_.value = 0; + init_setting_map(setting_map_, ARRAY_SIZE(setting_map_)); + init_settings(jsontext.c_str()); + writedown_device_configuration(); // initialize the hardware settings + init_version(); +} +hg_scanner_239::~hg_scanner_239() +{ + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "hg_scanner_239(%s) destroyed.\n", hg_log::format_ptr(this).c_str()); +} + +int hg_scanner_239::read_register(int addr, int* val) +{ + std::lock_guard lock(io_lock_); + + int l = sizeof(*val), + ret = io_->control_io(0x0c0, USB_REQ_GET_DEV_REGS, addr, 0, val, &l); + + if (ret) + status_ = ret; + + return ret; +} +int hg_scanner_239::write_register(int addr, int val) +{ + std::lock_guard lock(io_lock_); + + int l = sizeof(val), + ret = io_->control_io(0x040, USB_REQ_SET_DEV_REGS, addr, 0, &val, &l); + + if (ret) + status_ = ret; + + return ret; +} +int hg_scanner_239::write_command(int cmd) +{ + return write_register(0, cmd); +} +std::string hg_scanner_239::control_fetch(int addr, int val, int size) +{ + int l = sizeof(val), + ret = 0; + std::string data(""); + + ret = write_register(addr, val); + if (ret == HG_ERR_OK) + { + std::lock_guard lock(io_lock_); + + l = size + 1; + data.resize(size + 2); + bzero(&data[0], size + 2); + ret = io_->read_bulk(&data[0], &l); + if (ret) + { + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "control_fetch(%d, %d) - read_bulk = %s\n", addr, val, hg_scanner::strerr((hg_err)ret).c_str()); + data.clear(); + } + } + else + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "control_fetch(%d, %d) = %s\n", addr, val, hg_scanner::strerr((hg_err)ret).c_str()); + + return data; +} + +std::string hg_scanner_239::get_fpga(void) +{ + int val = 0, + ret = read_register(SR_GET_MBVERSION_LENGHT, &val); + std::string fpga(""); + + if (ret == HG_ERR_OK) + { + fpga.resize(val + 2); + bzero(&fpga[0], val + 2); + + return control_fetch(SR_GET_MBVERSION, 0, val); + } + + return fpga; +} +int hg_scanner_239::clr_roller_num(void) +{ + int val = 0, + ret = read_register(SR_CLR_ROLLER_NUM, &val); + + if (ret == HG_ERR_OK) + return val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "clr_roller_num = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} +int hg_scanner_239::get_sleep_time(void) +{ + int val = 0, + ret = read_register(SR_GET_SLEEPTIME, &val); + + if (ret == HG_ERR_OK) + return val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_sleep_time = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} +int hg_scanner_239::get_scan_mode(void) +{ + int val = 0, + ret = read_register(SR_OS, &val); + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get scan mode error: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + status_ = ret; + + return -1; + } + else + return val; +} +int hg_scanner_239::get_status(void) +{ + int val = 0, + ret = read_register(SR_STATUS, &val); + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get status error: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + status_ = ret; + + return -1; + } + else + return val; +} +bool hg_scanner_239::is_dev_tx(void) +{ + int val = 0, + ret = read_register(SR_STATUS, &val); + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "is_dev_tx() error: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + status_ = ret; + + return false; + } + else + return val; +} +bool hg_scanner_239::is_dev_image_process_done(void) +{ + int val = 0, + ret = read_register(SR_GET_IMAGEPROCESSDONE, &val); + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "is_dev_image_process_done() error: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + status_ = ret; + + return true; + } + else + return val; +} +bool hg_scanner_239::is_dev_image_keep_last_paper(void) +{ + + int val = 0, + ret = read_register(SR_GET_KEEP_LAST_PAPER, &val); + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "is_dev_image_keep_last_paper() error: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + status_ = ret; + + return true; + } + else + return val; +} +int hg_scanner_239::get_scanned_count(void) +{ + int val = 0, + ret = read_register(SR_GET_SCANN_NUM, &val); + + if (ret == HG_ERR_OK) + return val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_scanned_count = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} +int hg_scanner_239::get_image_count(void) +{ + int val = 0, + ret = read_register(SR_IM_COUNT, &val); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "get_image_count() = %d, error: %s\n", val, hg_scanner::strerr((hg_err)ret).c_str()); + + if (ret == HG_ERR_OK) + return val; + else + return -1; +} +int hg_scanner_239::get_front_data_size(void) +{ + int val = 0, + ret = read_register(SR_IM_FRONT_SIZE, &val); + + if (ret == HG_ERR_OK) + return val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_front_data_size = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} +void hg_scanner_239::init_version(void) +{ + std::string fv(get_firmware_version()), + sn(get_serial_num()); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, " HG3399 firmware version: %s\n", fv.c_str()); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, " HG3399 serial number: %s\n", sn.c_str()); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, " HG3399 IP: %s\n", get_ip().c_str()); + + if (fv.length() >= 10 && (fv[5] > 'A' || atoi(fv.substr(6, 4).c_str()) >= 1209)) + { + is_kernelsnap_211209_ = atoi(get_fpga().c_str()) >= 35211210; + } + else + is_kernelsnap_211209_ = false; +} +void hg_scanner_239::image_process(std::shared_ptr>& buff) +{ + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "Process image with %u bytes content ...\n", buff->size()); + + hg_imgproc::IMGPRCPARAM param; + hg_imgproc::HIMGPRC handle = NULL; + hg_imgproc::IMGHEAD ih; + int err = HG_ERR_OK, + index = 0; + void* buf = NULL; + + bzero(¶m, sizeof(param)); + + param.bits = 8; + param.black_white = img_conf_.pixtype == COLOR_MODE_BLACK_WHITE; + param.channels = img_conf_.pixtype == COLOR_MODE_24_BITS ? 3 : 1; + param.color_mode = img_conf_.pixtype; + param.double_side = img_conf_.is_duplex; + param.dpi = img_conf_.resolution_dst; + + // printf("img_conf_ =%d\r\n",img_conf_.pixtype); + // printf("param.black_white :%d\r\n",param.black_white); + // printf("param.channels :%d\r\n",param.channels); + // printf("param.color_mode :%d\r\n",param.color_mode); + // printf("param.double_side :%d\r\n",param.double_side); + // printf("param.dpi :%d\r\n",param.dpi); + + handle = hg_imgproc::init(&img_conf_,¶m); + err = hg_imgproc::load_buffer(handle, buff); + err = hg_imgproc::decode(handle,pid_); + + if(image_prc_param_.bits.erase_bakground && !user_cancel_) + { + err = hg_imgproc::fadeback(handle,img_conf_.fadebackrange,img_conf_.is_duplex); + } + + if (image_prc_param_.bits.rid_red && img_conf_.pixtype == COLOR_MODE_24_BITS) + err = hg_imgproc::multi_out_red(handle); + + if (image_prc_param_.bits.multi_out != MULTI_OUT_NOT && !user_cancel_) + { + //err = hg_imgproc::multi_out(handle,image_prc_param_.bits.multi_out - 1); 闈炴硶鎸囦护 鏆傛椂灞忚斀 + } + + if (image_prc_param_.bits.split && !user_cancel_) + { + int colormode=1; + if(img_conf_.filter == RID_COLOR_NONE) + colormode=img_conf_.pixtype; + + err = hg_imgproc::split(handle,img_conf_.multiOutput,img_conf_.splitImage,img_conf_.multi_output_red,colormode,img_conf_.is_duplex); + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "img split-> multiOutput is:%d splitImage is:%d multi_output_red is:%d pixtype is:%d is_duplex:%d\r\n" + ,img_conf_.multiOutput + ,img_conf_.splitImage + ,img_conf_.multi_output_red + ,img_conf_.pixtype + ,img_conf_.is_duplex); + } + + if (!image_prc_param_.bits.split || !image_prc_param_.bits.rid_red || !image_prc_param_.bits.multi_out) + { + if(img_conf_.automaticcolor) + { + hg_imgproc::auto_matic_color(handle,img_conf_.automaticcolortype); + } + } + + hg_imgproc::final(handle); + while (hg_imgproc::get_final_data(handle, &ih, &buf, index++) == HG_ERR_OK && !user_cancel_) + { + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "Final picture %d (%d * %d * %d) with %u bytes!\n", index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + + if(!img_type_.empty()) + { + std::vectorbmpdata; + + hg_imgproc::imgtypechange(handle,img_type_,buf,bmpdata); + //cv::imencode(img_type_,*((cv::Mat*)buf),bmpdata); + if(bmpdata.empty()) + { + status_ = HG_ERR_NO_DATA; + return ; + } + buf = bmpdata.data(); + ih.total_bytes = bmpdata.size(); + + HG_VLOG_MINI_6(HG_LOG_LEVEL_DEBUG_INFO, "Set img type is:%s Final picture %d (%d * %d * %d) with %u bytes!\n",img_type_.c_str() ,index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + } + save_final_image(&ih, buf); + } + + hg_imgproc::release(handle); +} + +int hg_scanner_239::get_device_sleep_stautus(void) +{ + int ret =read_register(SR_GET_SLEEP_STAUTUS,0); + return ret; +} + +int hg_scanner_239::get_device_paperon_stautus(void) +{ + int ret =read_register(SR_GET_PAPERON,0); + return ret; +} +// int hg_scanner_239::writedown_device_configuration() +// { +// int ret = HG_ERR_OK; + + +// ret = write_register(SR_CONFIG_SCAN_PARAM, dev_conf_.value); +// HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Writedown scanner configuration(0x%x) = %s\n", dev_conf_.value, hg_scanner::strerr((hg_err)ret).c_str()); + +// return ret; +// } + +int hg_scanner_239::writedown_device_configuration(HGSCANCONF *dev_conf) +{ + int ret = HG_ERR_OK; + + if(!dev_conf) + dev_conf = &dev_conf_; + + ret = write_register(SR_CONFIG_SCAN_PARAM, dev_conf->value); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Writedown scanner configuration(0x%x) = %s\n", dev_conf->value, hg_scanner::strerr((hg_err)ret).c_str()); + return ret; +} + +int hg_scanner_239::writedown_image_configuration(void) +{ + int ret = write_register(SR_CONFIF_IMGPROCPARAM, sizeof(SCANCONF)); + if (ret != HG_ERR_OK) + return ret; + + SCANCONF ic; + int len = sizeof(ic); + + bzero(&ic, len); + + ic.papertype = paper_size_; + + if (is_lateral(image_prc_param_.bits.paper)) + ic.paperAlign = Rot270; + else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) + ic.paperAlign = AutoTextOrientation; + else + ic.paperAlign = Rot0; + + ic.en_sizecheck = dev_conf_.g200params.enable_sizecheck; + ic.imageRotateDegree = image_prc_param_.bits.text_direction * 90.0f; + ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE || image_prc_param_.bits.page == PAGE_OMIT_EMPTY || + image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT || image_prc_param_.bits.page ==PAGE_FOLIO); + + ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); + + ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; + + ic.automaticcolor = is_auto_matic_color; + + ic.automaticcolortype = 1;// ic.pixtype; //瀛樼枒 + ic.resolution_dst = resolution_; + ic.resolution_native = 200.0f; + ic.gamma = (float)gamma_; + ic.contrast = (contrast_ - 4) * 333.0; + ic.brightness = (bright_ - 128) * (2000.0 / 254.0); + ic.threshold = threshold_; + ic.is_autocontrast = 0; //鏃犲弬鏁 + ic.is_autocrop = (ic.papertype == TwSS::None || ic.papertype ==TwSS::USStatement); + ic.is_autodiscradblank_normal = image_prc_param_.bits.page == PAGE_OMIT_EMPTY; + ic.discardblank_percent = omit_empty_level_ > 70 ? 70 : omit_empty_level_; //榛樿20 + ic.is_autodiscradblank_vince = image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT; + ic.is_switchfrontback = image_prc_param_.bits.exchange; + ic.autodescrew = image_prc_param_.bits.automatic_skew; + //ic.multi_output_red = image_prc_param_.bits.rid_red; //蹇呴』灞忚斀,鍚﹀垯瓒呮椂,鏈煡閿欒涓嶅彲鎻忚堪銆 + ic.hsvcorrect = image_prc_param_.bits.rid_answer_red; + + ic.sharpen = image_prc_param_.bits.sharpen; + ic.enhance_color = image_prc_param_.bits.rid_color; + ic.fillbackground = image_prc_param_.bits.erase_black_frame; + ic.is_convex = (image_prc_param_.bits.fill_background == FILL_BKG_CONVEX_POLYGON); + ic.noise = image_prc_param_.bits.noise_optimize; + ic.indent = margin_; + ic.AutoCrop_threshold = threshold_; + if (test_1_paper_) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "scanning mode: testing ONE paper ...\n"); + ic.scannum = ic.is_duplex ? 2 : 1; + } + else + { + ic.scannum = (ic.is_duplex ? scan_count_ * 2 : scan_count_); + } + ic.is_backrotate180 = image_prc_param_.bits.rotate_back_180; + ic.is_dogeardetection = image_prc_param_.bits.fractate_check; + + ic.hardwarecaps.en_skrewdetect = dev_conf_.g200params.screw_detect_enable; + ic.hardwarecaps.en_doublefeed = dev_conf_.g200params.ultrasonic_enable; + ic.hardwarecaps.en_stapledetect = dev_conf_.g200params.staple_enbale; + ic.hardwarecaps.skrewdetectlevel = dev_conf_.g200params.screw_detect_level; + // ic.hardwarecaps.is_autopaper = dev_conf_.g200params.is_autopaper; + ic.hardwarecaps.capturepixtype = 0; //鏆傛棤鍙傛暟 鑾峰彇鍥惧儚绫诲瀷 + ic.hardwarecaps.lowpowermode = LowPowerMode::Min_None; //鏆傛棤鍙傛暟 璁剧疆浼戠湢鏃堕棿 涓や釜鍙傛暟3399鏈娇鐢 + + ic.fillhole.is_fillhole = image_prc_param_.bits.rid_hole; + ic.fillhole.fillholeratio = rid_hole_range_; + + + ic.detachnoise.is_detachnoise = image_prc_param_.bits.noise_optimize; + ic.detachnoise.detachnoise = noise_range_; + ic.is_autotext = image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO; + ic.isfillcolor = false;//鏆傛棤鍙傛暟 鏈娇鐢ㄥ埌 + ic.refuseInflow = image_prc_param_.bits.is_permeate; + ic.colorCorrection = 0; + ic.removeMorr = image_prc_param_.bits.remove_morr; + ic.errorExtention = image_prc_param_.bits.error_extention ;//鏆傛棤鍙傛暟 + ic.textureRemove = image_prc_param_.bits.remove_txtture;//鏆傛棤鍙傛暟 + ic.splitImage = image_prc_param_.bits.split; + ic.cropRect.enable = 0;//鏆傛棤鍙傛暟 + ic.cropRect.height = 0;//鏆傛棤鍙傛暟 + ic.cropRect.width = 0;//鏆傛棤鍙傛暟 + ic.cropRect.x = 0;//鏆傛棤鍙傛暟 + ic.cropRect.y = 0;//鏆傛棤鍙傛暟 + ic.multiOutput = MultiOutput::Unused; //鏆傛棤鍙傛暟 + ic.normalCrop = image_prc_param_.bits.dark_sample; + ic.dogeardistabce = fractate_level_; + ic.fadeback = image_prc_param_.bits.erase_bakground; + ic.fadebackrange = erase_bkg_range_; + + int filter_clr[] = { 3, 0, 1, 2, 5, 6, 7 }; + if(image_prc_param_.bits.color_mode == COLOR_MODE_24_BITS || image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH) + ic.filter = 3; + else + { + ic.filter = filter_clr[image_prc_param_.bits.rid_color]; + // if(ic.filter != 3) + // ic.pixtype = 2; + + ic.hsvcorrect = 0; + ic.multi_output_red = 0; + ic.multiOutput = MultiOutput::Unused; + ic.fadeback = false; + } + { + std::lock_guard lock(io_lock_); + + ret = io_->write_bulk(&ic, &len); + + this_thread::sleep_for(chrono::milliseconds(500)); + io_->set_timeout(2000);//蹇呰寤舵椂 + } + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "Write-down 0x%x bytes image process parameters\n", len); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.papertype=%d", ic.papertype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.AutoCrop_threshold=%d", ic.AutoCrop_threshold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.autodescrew=%d", ic.autodescrew); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.automaticcolor=%d", ic.automaticcolor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.brightness=%f", ic.brightness); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.contrast=%f", ic.contrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_fold=%d", ic.en_fold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_sizecheck=%d", ic.en_sizecheck); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.enhance_color=%d", ic.enhance_color); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillbackground=%d", ic.fillbackground); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.filter=%d", ic.filter); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.gamma=%f", ic.gamma); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.capturepixtype=%d", ic.hardwarecaps.capturepixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.en_doublefeed=%d", ic.hardwarecaps.en_doublefeed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hsvcorrect=%d", ic.hsvcorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.imageRotateDegree=%f", ic.imageRotateDegree); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.indent=%d", 5); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocontrast=%d", ic.is_autocontrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocrop=%d", ic.is_autocrop); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_normal=%d", ic.is_autodiscradblank_normal); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_vince=%d", ic.is_autodiscradblank_vince); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autotext=%d", ic.is_autotext); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_backrotate180=%d", ic.is_backrotate180); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_convex=%d", ic.is_convex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_duplex=%d", ic.is_duplex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_switchfrontback=%d", ic.is_switchfrontback); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_dogeardetection=%d", ic.is_dogeardetection); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.multi_output_red=%d", ic.multi_output_red); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.noise=%d", 8); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_dst=%f", ic.resolution_dst); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_native=%f", ic.resolution_native); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.scannum=%d", ic.scannum); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.sharpen=%d", ic.sharpen); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.discardblank_percent=%d", ic.discardblank_percent); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.is_detachnoise=%d", ic.detachnoise.is_detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.detachnoise=%d\r ", ic.detachnoise.detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.removeMorr=%d\r ", ic.removeMorr); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.errorExtention=%d\r ", ic.errorExtention); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.textureRemove=%d\r\n ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.is_fillhole=%d\r\n ", ic.fillhole.is_fillhole); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.ic.fillhole.fillholeratio=%d\r\n ", ic.fillhole.fillholeratio); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fadeback=%d\r\n ",ic.fadeback); + + img_conf_ = ic; + return ret; +} + +int hg_scanner_239::pop_first_image(void) +{ + int ret = write_register(SR_IM_POP, 1); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "popup first image = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} + +int hg_scanner_239::read_one_image_from_usb(void) +{ + //if (is_dev_tx()) + // return HG_ERR_OK; + + //if (!is_kernelsnap_211209_) + //{ + // if (image_prc_param_.bits.page == PAGE_DOUBLE && !image_prc_param_.bits.fractate_check) + // if (get_image_count() < 3) + // return HG_ERR_DEVICE_NO_IMAGE; + // if (get_image_count() < 2) + // return HG_ERR_DEVICE_NO_IMAGE; + //} + int r = 0, + total = get_front_data_size(), + off = 0, + ret = HG_ERR_OK; + + if (!waiting_for_memory_enough(total)) + { + status_ = HG_ERR_INSUFFICIENT_MEMORY; + + return status_; + } + if (user_cancel_) + return HG_ERR_USER_CANCELED; + + if (total == -1) + ret = status_; + else + { + std::shared_ptr> buf(new std::vector); + buf->resize(total); + // write reading command + ret = write_register(SR_IM_TX, 1); + + if (ret == HG_ERR_OK) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + r = total; + { + std::lock_guard lock(io_lock_); + + ret = io_->read_bulk(buf->data(), &r); + + while (ret == HG_ERR_OK) + { + off += r; + if (off >= total) + break; + + r = total - off; + ret = io_->read_bulk(buf->data() + off, &r); + } + } + if (ret == HG_ERR_OK) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "Read image from USB = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + ret = save_usb_data(buf); + if (ret == HG_ERR_OK) + { + pop_first_image(); + //if(image_prc_param_.bits.page == PAGE_SINGLE) + // pop_first_image(); + } + } + else + { + char msg[128]; + sprintf(msg, "Read image data from USB err: %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, msg); + notify_ui_working_status(msg); + notify_ui_working_status(STATU_DESC_HG_ERR_TIMEOUT, SANE_EVENT_ERROR); + } + } + } + + return ret; +} +void hg_scanner_239::discard_all_images(void) +{ + char buf[64]; + HGEIntInfo* info = (HGEIntInfo*)buf; + int size = sizeof(buf), + oto = 0, + pre_img = 0, + pre_int = 0, + ret = HG_ERR_OK; + + { + std::lock_guard lock(io_lock_); + oto = io_->get_timeout(); + io_->set_timeout(100); + ret = io_->read_interrupt(buf, &size); + } + while (ret == HG_ERR_OK) + { + pre_int++; + if (info->From == IMG) + { + while (get_image_count() > 0) + { + pop_first_image(); + pre_img++; + } + } + + size = sizeof(buf); + { + std::lock_guard lock(io_lock_); + ret = io_->read_interrupt(buf, &size); + } + } + { + std::lock_guard lock(io_lock_); + io_->set_timeout(oto); + } + if (pre_int) + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "prev-connection has left %d interrupt packet(s) && %d image(s).\n", pre_int, pre_img); +} + +void hg_scanner_239::init_setting_map(int* setting_map, int count) +{ + setting_map[HG_BASE_SETTING_INDEX_RESTORE_DEFAULT_SETTINGS] = HG_239_SETTING_RESTORE; + setting_map[HG_BASE_SETTING_INDEX_HELP] = HG_239_SETTING_HELP; + + setting_map[HG_BASE_SETTING_INDEX_COLOR_MODE] = HG_239_SETTING_COLOR_MODE; + setting_map[HG_BASE_SETTING_MULTI_OUT] = HG_239_SETTING_MULTI_OUT; + setting_map[HG_BASE_SETTING_INDEX_ERASE_COLOR] = HG_239_SETTING_ERASE_COLOR; + setting_map[HG_BASE_SETTING_INDEX_ERASE_MULTI_OUT_RED] = HG_239_SETTING_ERASE_MULTI_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_ANSWER_RED] = HG_239_SETTING_ERASE_ANSWER_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND] = HG_239_SETTING_ERASE_BACKGROUND; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE] = HG_239_SETTING_ERASE_BKG_RANGE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE] = HG_239_SETTING_NOISE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE] = HG_239_SETTING_NOISE_SIZE; + setting_map[HG_BASE_SETTING_INDEX_PAPER] = HG_239_SETTING_PAPER_SIZE; + setting_map[HG_BASE_SETTING_INDEX_PAPER_SIZE_CHECK] = HG_239_SETTING_PAPER_SIZE_CHECK; + setting_map[HG_BASE_SETTING_INDEX_PAGE] = HG_239_SETTING_PAGE; + setting_map[HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL] = HG_239_SETTING_SKIP_NILL; + setting_map[HG_BASE_SETTING_INDEX_RESOLUTION] = HG_239_SETTING_RESOLUTION; + setting_map[HG_BASE_SETTING_INDEX_EXCHANGE] = HG_239_SETTING_REVERSE; + setting_map[HG_BASE_SETTING_INDEX_SPLIT] = HG_239_SETTING_SPLIT; + setting_map[HG_BASE_SETTING_INDEX_AUTO_CORRECT] = HG_239_SETTING_CORRECT; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE] = HG_239_SETTING_ERASE_HOLE; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE_RANGE] = HG_239_SETTING_HOLE_SEARCH; + + setting_map[HG_BASE_SETTING_INDEX_BRIGHT] = HG_239_SETTING_LIGHT; + setting_map[HG_BASE_SETTING_INDEX_CONTRAST] = HG_239_SETTING_CONTRAST; + setting_map[HG_BASE_SETTING_INDEX_GAMMA] = HG_239_SETTING_GAMMA; + + setting_map[HG_BASE_SETTING_INDEX_SHARPEN] = HG_239_SETTING_SHARP; + setting_map[HG_BASE_SETTING_INDEX_DARK_SAMPLE] = HG_239_SETTING_SAMPLE; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BLACK_FRAME] = HG_239_SETTING_ERASE_BLACK_BORDER; + setting_map[HG_BASE_SETTING_INDEX_THRESHOLD] = HG_239_SETTING_THRESHOLD; + setting_map[HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL] = HG_239_SETTING_ANTI_NOISE; + setting_map[HG_BASE_SETTING_INDEX_MARGIN] = HG_239_SETTING_MARGIN; + setting_map[HG_BASE_SETTING_INDEX_FILL_BACKGROUND] = HG_239_SETTING_FILLING; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE] = HG_239_SETTING_PERMEATE; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE_LV] = HG_239_SETTING_PERMEATE_lv; + setting_map[HG_BASE_SETTING_REMOVE_MORR] = HG_239_SETTING_REMOVE_MORR; + setting_map[HG_BASE_SETTING_ERROR_EXTENTION] = HG_239_SETTING_ERROR_EXTENTION; + setting_map[HG_BASE_SETTING_REMOVE_TXTTURE] = HG_239_SETTING_REMOVE_TXTTURE; + + setting_map[HG_BASE_SETTING_INDEX_ULTRASONIC_CHECK] = HG_239_SETTING_ULTRASONIC; + setting_map[HG_BASE_SETTING_INDEX_STAPLE_CHECK] = HG_239_SETTING_STAPLE; + setting_map[HG_BASE_SETTING_INDEX_SCAN_MODE] = HG_239_SETTING_SCAN_METHOD; + setting_map[HG_BASE_SETTING_INDEX_SCAN_COUNT] = HG_239_SETTING_SCAN_COUNT; + setting_map[HG_BASE_SETTING_INDEX_TEXT_DIRECTION] = HG_239_SETTING_DIRECTION; + setting_map[HG_BASE_SETTING_INDEX_ROTATE_BKG_180] = HG_239_SETTING_ROTATE; + setting_map[HG_BASE_SETTING_INDEX_FRACTATE_CHECK] = HG_239_SETTING_FRACTATE; + setting_map[HG_BASE_SETTING_INDEX_FRACTATE_CHECK_LEVEL] = HG_239_SETTING_FRACTATE_COMPLEX; + setting_map[HG_BASE_SETTING_INDEX_SKEW_CHECK] = HG_239_SETTING_SCREW; + setting_map[HG_BASE_SETTING_INDEX_SKEW_CHECK_LEVEL] = HG_239_SETTING_SCREW_COMPLEX; + +} + +//color_mode =-1 闄よ壊閫夐」 +int hg_scanner_239::on_color_mode_changed(int& color_mode) +{ + int ret = HG_ERR_OK; + + if ((((color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) && !dev_conf_.g200params.color) || + ((color_mode != COLOR_MODE_24_BITS && color_mode != COLOR_MODE_AUTO_MATCH) && dev_conf_.g200params.color)) && color_mode != -1) + { + dev_conf_.g200params.color ^= 1; + ret = writedown_device_configuration(); + if(ret) + dev_conf_.g200params.color ^= 1; + } + if(color_mode == COLOR_MODE_AUTO_MATCH) + { + dev_conf_.g200params.color = 1; + } + + HGSCANCONF d = dev_conf_; + + if (image_prc_param_.bits.rid_color != RID_COLOR_NONE + &&(image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY || image_prc_param_.bits.color_mode == COLOR_MODE_BLACK_WHITE) + ) + { + d.g200params.color = 1; + ret = writedown_device_configuration(&d); + } + + if(image_prc_param_.bits.rid_color != RID_COLOR_NONE + && (image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY || image_prc_param_.bits.color_mode == COLOR_MODE_BLACK_WHITE) + && color_mode == -1) + { + d.g200params.color = 1; + } + else if (image_prc_param_.bits.rid_color == RID_COLOR_NONE + && (image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY || image_prc_param_.bits.color_mode == COLOR_MODE_BLACK_WHITE) + && color_mode == -1) + { + d.g200params.color = 0; + } + if(color_mode == -1) + ret = writedown_device_configuration(&d); + + return ret; +} +int hg_scanner_239::on_paper_changed(int& paper) +{ + bool exact = true; + int ind = settings::match_best_paper(paper, &exact, &paper_size_), + ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (dev_conf_.g200params.paper != settings::paper_map[ind].dev_value) + { + int old = dev_conf_.g200params.paper; + + dev_conf_.g200params.paper = settings::paper_map[ind].dev_value; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.paper = old; + for (int i = 0; i < ARRAY_SIZE(settings::paper_map); ++i) + { + if (settings::paper_map[i].dev_value == old) + { + paper = settings::paper_map[i].paper; + break; + } + } + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + } + + return ret; +} +int hg_scanner_239::on_paper_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dev_conf_.g200params.enable_sizecheck ^ check) + { + dev_conf_.g200params.enable_sizecheck = check; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.enable_sizecheck = !check; + check = dev_conf_.g200params.enable_sizecheck; + } + } + + return ret; +} +int hg_scanner_239::on_resolution_changed(int& dpi) +{ + bool exact = true; + int ind = settings::match_best_resolution(dpi, &exact), + ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (dev_conf_.g200params.dpi != settings::resolution_map[ind].dev_value) + { + int old = dev_conf_.g200params.dpi; + dev_conf_.g200params.dpi = settings::resolution_map[ind].dev_value; + dev_conf_.g200params.dpi = 1; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.dpi = old; + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + } + + if (ret == HG_ERR_NOT_EXACT) + dpi = settings::resolution_map[ind].resolution; + + return ret; +} + +int hg_scanner_239::on_ultrasonic_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dev_conf_.g200params.ultrasonic_enable ^ check) + { + dev_conf_.g200params.ultrasonic_enable = check; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.ultrasonic_enable = !check; + check = dev_conf_.g200params.ultrasonic_enable; + } + } + + return ret; +} +int hg_scanner_239::on_staple_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dev_conf_.g200params.staple_enbale ^ check) + { + dev_conf_.g200params.staple_enbale = check; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.staple_enbale = !check; + check = dev_conf_.g200params.staple_enbale; + } + } + + return ret; +} +int hg_scanner_239::on_skew_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dev_conf_.g200params.screw_detect_enable ^ check) + { + dev_conf_.g200params.screw_detect_enable = check; + ret = writedown_device_configuration(); + if (ret) + { + dev_conf_.g200params.screw_detect_enable = !check; + check = dev_conf_.g200params.screw_detect_enable; + } + } + + return ret; +} +int hg_scanner_239::on_skew_check_level_changed(int& check) +{ + int ret = HG_ERR_OK, + val = check, + old = dev_conf_.g200params.screw_detect_level; + HGSCANCONF cf; + + cf.g200params.screw_detect_level = -1; + + if (val < 0) + { + val = 0; + ret = HG_ERR_NOT_EXACT; + } + else if (val > cf.g200params.screw_detect_level) + { + val = cf.g200params.screw_detect_level; + ret = HG_ERR_NOT_EXACT; + } + if (val != dev_conf_.g200params.screw_detect_level) + { + dev_conf_.g200params.screw_detect_level = val; + ret = writedown_device_configuration(); + if (ret) + check = dev_conf_.g200params.screw_detect_level = old; + } + if (ret == HG_ERR_OK && val != check) + { + check = val; + ret = HG_ERR_NOT_EXACT; + } + + return ret; +} + +void hg_scanner_239::on_device_reconnected(void) +{ + int ret = writedown_device_configuration(); + init_version(); + + rewrite_conf_ = ret != HG_ERR_OK; + if (reset_) + { + reset_ = false; + start(); + } +} +int hg_scanner_239::on_scanner_closing(bool force) +{ + return HG_ERR_OK; +} + +void hg_scanner_239::thread_handle_usb_read(void) +{ + char buf[64]; + HGEIntInfo* info = (HGEIntInfo*)buf; + int size = sizeof(buf), + ret = HG_ERR_OK, + to_cnt = 0, + count = 0; + + status_ = HG_ERR_DEVICE_BUSY; + while (run_ )//&& !user_cancel_ + { + if (user_cancel_) + { + break; + } + size = sizeof(buf); + { + std::lock_guard lock(io_lock_); + + ret = io_->read_interrupt(buf, &size); + io_->set_timeout(1000); + } + if (ret == HG_ERR_TIMEOUT) + { + if (++to_cnt > 10) + { + status_ = ret; + notify_ui_working_status("閫氫俊瓒呮椂", SANE_EVENT_ERROR, ret); + stop(); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + if ((get_status() & 0x03) == 0) + { + status_ = HG_ERR_OK; + break; + } + continue; + } + else if (ret != HG_ERR_OK) + { + status_ = ret; + break; + } + + to_cnt = 0; + if (size == sizeof(buf)) + { + status_ = settings::device_status_to_hg_err(info); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "status from interrupt point is '%s'\n", hg_scanner::strerr((hg_err)status_).c_str()); + + if (status_ != HG_ERR_OK && status_ != HG_ERR_TIMEOUT && status_ != HG_ERR_DEVICE_STOPPED) + { + if (!is_dev_image_process_done()) + { + int st = status_; + while (!is_dev_image_process_done()) + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + + while (get_image_count() > 0) + { + ret = read_one_image_from_usb(); + count++; + if (ret != HG_ERR_OK + && ret != HG_ERR_CREATE_FILE_FAILED + && ret != HG_ERR_WRITE_FILE_FAILED) + break; + } + status_ = st; + } + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "鎵弿澶辫触, read interrupt杩斿洖鐘舵佷负锛%s\n", hg_scanner::error_description((hg_err)status_).c_str()); + //notify_ui_working_status(("鎵弿澶辫触 " + hg_scanner::error_description((hg_err)status_)).c_str(), SANE_EVENT_ERROR, status_); + break; + } + + if (info->From == IMG) + { + int s = get_status(); + // if (s > 0 && s < 4) // running ... + { + while (get_image_count() > 0) + { + count++; + ret = read_one_image_from_usb(); + if (ret != HG_ERR_OK + && ret != HG_ERR_CREATE_FILE_FAILED + && ret != HG_ERR_WRITE_FILE_FAILED) + break; + } + if (user_cancel_) + { + if (ret == HG_ERR_INSUFFICIENT_MEMORY) + stop(); + status_ = ret = HG_ERR_USER_CANCELED; + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鐢ㄦ埛鍙栨秷鎿嶄綔 '%s'\n", hg_scanner::strerr((hg_err)status_).c_str()); + break; + } + } + //else + // HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "status(0x%x) is not accept in usb thread\n", status); + } + else if (info->From == STOPSCAN) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "received 'STOPSCAN' message in usb thread, check remaining image and finish scanning ...\n"); + + // fetch all buffered images and exit ... + while(!is_dev_image_process_done()) + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + + while (get_image_count() > 0) + { + ret = read_one_image_from_usb(); + count++; + if (ret != HG_ERR_OK + && ret != HG_ERR_CREATE_FILE_FAILED + && ret != HG_ERR_WRITE_FILE_FAILED) + break; + } + if (count == 0) + { + std::lock_guard lock(io_lock_); + + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "First message received from USB is 'STOPSCAN'\n"); + // notify_ui_working_status(STATU_DESC_DEVICE_RESET); + // reset_ = true; + // ret = io_->reset(); + // status_ = HG_ERR_IO; + } + else if (status_ == HG_ERR_TIMEOUT || status_ == HG_ERR_IO || status_ == HG_ERR_DEVICE_STOPPED) + status_ = HG_ERR_OK; + + int s = get_status(); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "status after received 'STOPSCAN': 0x%x\n", s); + if (count || (s & 3) == 0) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "Really stopped ^_^\n"); + break; + } + } + else if (info->From == V4L2) + { + HG_VLOG_MINI_2(HG_LOG_LEVEL_WARNING, "V4L2 message received, code = %d, index = %d\n", info->Code, info->Img_Index); + // stop(); + break; + } + else + { + // error handling ... + HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Read HGEIntInfo: From = %d, Code = %d, Img_Index = %d, status = %s\n", info->From, info->Code, info->Img_Index, hg_scanner::strerr((hg_err)status_).c_str()); + } + } + else + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "read %d bytes, sizeof(buf) = %d\n", size, sizeof(buf)); + + this_thread::sleep_for(chrono::milliseconds(10)); + } + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "USB thread exit with code: %s, status = %s\n", hg_scanner::strerr((hg_err)ret).c_str(), hg_scanner::strerr((hg_err)status_).c_str()); +} + +int hg_scanner_239::start(void) +{ + int ret = HG_ERR_OK, + val = 0; + + user_cancel_ = false; + cb_mem_ = true; + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "scanner status: 0x%x\n", get_status()); + notify_ui_working_status(STATU_DESC_PREPARE_START); + + reset(); + get_roller_num(); + val = get_sleep_time(); + //if (val != 1) + //{ + // status_ = HG_ERR_DEVICE_SLEEPING; + // HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "scanner's status(0x%x) is considerring as sleepping.\n", val); + + // return status_; + //} + val = get_scan_mode(); + if (status_ == HG_ERR_DEVICE_NOT_FOUND) + return status_; + + if (val == -1) + return status_; + if (val & 1) + { + status_ = HG_ERR_DEVICE_COUNT_MODE; + + return status_; + } + + notify_ui_working_status(STATU_DESC_REWRITE_CONFIGURATION); + //if(rewrite_conf_) + { + //鍗忚璁剧疆鏂瑰紡涓瀹氳娉ㄦ剰 1锛氬崗璁粨鏋勪綋鍋氬ソ锛宻tart涓璧峰彂閫 2锛氭瘡娆¤缃竴娆″彂閫佷竴娆 娉ㄦ剰浜嬮」锛氶櫎鑹叉椂棰滆壊鍒囨崲缁村僵鑹蹭娇鐢ㄧ殑鏄复鏃跺彉閲忥紝涓嬪彂涓娆¤閲婃斁锛屽鏋渟tart鍐嶆鍙戝氨浼氬鑷村崗璁眰琚鐩 + //ret = writedown_device_configuration(); + } + + if(ret == HG_ERR_OK) + { + ret = writedown_image_configuration(); + } + + if (ret) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "Write down image process parameters fail is(%s), the result will be unpredictable.\n", hg_scanner::strerr((hg_err)ret).c_str()); + + std::string info(STATU_DESC_REWRITE_CONFIGURATION); + + info += "澶辫触锛" + hg_scanner::strerr((hg_err)ret); + notify_ui_working_status(info.c_str(), SANE_EVENT_ERROR, ret); + } + + if (ret != HG_ERR_OK) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "start status01 is(%s), the result will be unpredictable.\n", hg_scanner::strerr((hg_err)ret).c_str()); + return ret; + } + ret = get_scanner_paperon(); + if (ret == HG_ERR_DEVICE_NO_PAPER) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_PAPER, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_NO_PAPER); + return HG_ERR_OK; + } + + ret = write_command(SC_START); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "start status02 is(%s), the result will be unpredictable.\n", hg_scanner::strerr((hg_err)ret).c_str()); + io_->set_timeout(1000); + if (ret == HG_ERR_OK) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "Write SC_START command success, and now scanner stauts is %x\n", get_status()); + + //status_ = HG_ERR_DEVICE_BUSY; + wait_usb_.notify(); + this_thread::sleep_for(chrono::milliseconds(100)); + } + else + { + status_ = ret; + HG_VLOG_MINI_3(HG_LOG_LEVEL_WARNING, "(%s)[Thread %s]Send start command = %s\n", hg_log::current_time().c_str(), hg_log::format_current_thread_id().c_str(), hg_scanner::strerr((hg_err)ret).c_str()); + } + + return ret; +} +//int hg_scanner_239::get_image_info(IMG_PARAM* ii) +//{ +// int ret = HG_ERR_OK; +// IMH imh; +// +// bzero(&imh, sizeof(imh)); +// while((!wait_img_.is_waiting() || !wait_usb_.is_waiting()) && final_imgs_.Size() <= 0) +// this_thread::sleep_for(chrono::milliseconds(10)); +// +// if (final_imgs_.Size() <= 0) +// ret = status_; +// else +// { +// if (!final_imgs_.front(&imh)) +// ret = HG_ERR_NO_DATA; +// else +// { +// if (image_prc_param_.bits.color_mode == COLOR_MODE_24_BITS) +// ii->format = FRAME_RGB; +// else +// ii->format = FRAME_GRAY; +// ii->depth = 8; // 姝ゅ鎸囨瘡涓涓鑹插垎閲忕殑浣嶆繁锛屾垜浠殑鎵弿浠浐瀹氫负鈥8鈥 +// ii->last_frame = true; // 涓骞呭浘鐗囧鏋滃悇涓垎閲忕浉浜掑垎绂伙紝鍒欐渶鍚庝竴涓垎閲忕殑鏃跺欒缃负true銆傚僵鑹插浘鍍廟GB鏃朵篃鍙湁涓鈥滃抚鈥濓紝鎵浠ヤ篃涓簍rue +// ii->bytes_per_line = imh.line_bytes; +// ii->pixels_per_line = imh.width; +// ii->lines = imh.height; +// } +// } +// HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Get image info(%d * %d * %d) = %s\n", ii->pixels_per_line, ii->lines, imh.bits, hg_scanner::strerr((hg_err)ret).c_str()); +// +// return ret; +//} +//int hg_scanner_239::read_image_data(unsigned char* buf, int* len) +//{ +// if (!len) +// return HG_ERR_INVALID_PARAMETER; +// +// if (!buf) +// { +// IMH imh; +// final_imgs_.front(&imh); +// *len = imh.bytes; +// +// return HG_ERR_INSUFFICIENT_MEMORY; +// } +// +// if (final_imgs_.Size() > 0) +// { +// int fetch = *len; +// +// final_imgs_.fetch_front(buf, len); +// HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "[%s]UI fetch image 0x%x/0x%x to buf %s...\n", hg_log::format_current_thread_id().c_str(), *len, fetch +// , hg_log::format_ptr(buf).c_str()); +// +// return HG_ERR_OK; +// } +// else +// return HG_ERR_NO_DATA; +//} +int hg_scanner_239::stop(void) +{ + int ret = HG_ERR_OK; + + user_cancel_ = true; + ret = write_command(SC_STOP); + io_->set_timeout(500); + + return status_; +} +int hg_scanner_239::reset(void) +{ + final_imgs_.Clear(); + + while (paths_.Size() > 0) + remove(paths_.Take().c_str()); + + discard_all_images(); + + return status_; +} +int hg_scanner_239::device_io_control(unsigned long code, void* data, unsigned* len) +{ + int ret = HG_ERR_OUT_OF_RANGE; + + if (HG_CONTROL_CODE_OPTION_ENABLE == code) + { + OPTEN* opten = (OPTEN*)data; + + ret = HG_ERR_OK; + if (opten->opt_num == HG_239_SETTING_CORRECT && !opten->enabled) // for BUG-20 modified by Gongbing on 2022-03-12 + { + bool enable = true; + setting_automatic_skew(&enable); + } + } + else if (IO_CTRL_CODE_TEST_SINGLE == code) + { + test_1_paper_ = true; + ret = start(); + } + else + ret = hg_scanner::device_io_control(code, data, len); + + return ret; +} +std::string hg_scanner_239::get_firmware_version(void) +{ + return control_fetch(SR_GET_FWVERSION, 0, 20); +} +std::string hg_scanner_239::get_serial_num(void) +{ + return control_fetch(SR_GET_SERIALNUM, 0, 20); +} +std::string hg_scanner_239::get_ip(void) +{ + return control_fetch(SR_GET_IPADDR, 0, 40); +} +int hg_scanner_239::get_roller_num(void) +{ + int val = 0, + ret = read_register(SR_GET_ROLLER_NUM, &val); + + if (ret == HG_ERR_OK) + return val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_roller_num = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} +int hg_scanner_239::clear_roller_num(void) +{ + int val = 0, + ret = read_register(SR_CLR_ROLLER_NUM, &val); + + return ret; +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int hg_scanner_239::set_leaflet_scan(void) +{ + int ret = HG_ERR_OK; + test_1_paper_ = true; + + ret = start(); + return ret; +} +int hg_scanner_239::get_abuot_info(void) +{ + + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::restore_default_setting(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::set_final_image_format(SANE_FinalImgFormat* fmt) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_239::get_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::set_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::set_auto_color_type(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_239::get_device_code(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::get_sleep_time(SANE_Power* getime) +{ + int val = 0, + ret = read_register(SR_GET_SLEEPTIME, &val); + + if (ret == HG_ERR_OK) + *getime = (SANE_Power)val; + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_sleep_time = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } +} + +int hg_scanner_239::set_sleep_time(int setsleepime) +{ + int ret = write_register(SR_SET_SLEEPTIME, setsleepime); + + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "set sleep time to %d = %s\n", setsleepime, hg_scanner::strerr((hg_err)ret).c_str()); +} +int hg_scanner_239::get_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::set_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::get_scanner_paperon(SANE_Bool* paperon) +{ + int val = 0, + ret = read_register(SR_GET_PAPERON, &val); + + if (ret == HG_ERR_OK) + { + if (!val) + { + ret = HG_ERR_DEVICE_NO_PAPER; + if (paperon) + { + *paperon = true; + } + } + else + { + ret = HG_ERR_OK; + if (paperon) + { + *paperon = false; + } + } + } + else + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "get_scanner_paperon = %s\n", hg_scanner::strerr((hg_err)ret).c_str()); + return -1; + } + return ret; +} +int hg_scanner_239::set_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::get_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::get_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::set_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_239::get_scan_is_sleep() +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} diff --git a/hgdriver/hgdev/hg_scanner_239.h b/hgdriver/hgdev/hg_scanner_239.h new file mode 100644 index 0000000..c49219b --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_239.h @@ -0,0 +1,111 @@ +锘#pragma once + +// hg_scanner is the base class of kinds of scanners +// +// created on 2022-01-30 +// +#include +#include +#include +#include +#include + +#include "hg_scanner.h" + +class hg_scanner_239 : public hg_scanner +{ + //BlockingQueue>> final_imgs_; // JPG ... +// image_data final_imgs_; // JPG .. + int pid_; + HGSCANCONF dev_conf_; + bool rewrite_conf_; + bool reset_; + bool is_kernelsnap_211209_; + + int read_register(int addr, int* val); + int write_register(int addr, int val); + int write_command(int cmd); + std::string control_fetch(int addr, int val, int size); + + std::string get_fpga(void); + int clr_roller_num(void); + int get_sleep_time(void); + int get_scan_mode(void); + int get_status(void); + bool is_dev_tx(void); + bool is_dev_image_process_done(void); + bool is_dev_image_keep_last_paper(void); + int get_scanned_count(void); + int get_image_count(void); + int get_front_data_size(void); + void init_version(void); + int get_device_sleep_stautus(void); + int get_device_paperon_stautus(void); + + int writedown_device_configuration(HGSCANCONF *dev_conf = NULL); + int writedown_image_configuration(void); + int pop_first_image(void); + + int read_one_image_from_usb(void); + void discard_all_images(void); + +protected: + virtual void init_setting_map(int* setting_map, int count) override; + + virtual int on_color_mode_changed(int& color_mode) override; + virtual int on_paper_changed(int& paper) override; + virtual int on_paper_check_changed(bool& check) override; + virtual int on_resolution_changed(int& dpi) override; + + virtual int on_ultrasonic_check_changed(bool& check) override; + virtual int on_staple_check_changed(bool& check) override; + virtual int on_skew_check_changed(bool& check) override; + virtual int on_skew_check_level_changed(int& check) override; + + virtual void on_device_reconnected(void) override; + virtual int on_scanner_closing(bool force) override; + virtual void thread_handle_usb_read(void) override; + virtual void image_process(std::shared_ptr>& buff) override; + +public: + hg_scanner_239(const char* dev_name, int pid,usb_io* io); + ~hg_scanner_239(); + +public: + virtual int start(void) override; + //virtual int get_image_info(IMG_PARAM* ii) override; + //virtual int read_image_data(unsigned char* buf, int* len) override; + virtual int stop(void) override; + virtual int reset(void) override; + virtual int device_io_control(unsigned long code, void* data, unsigned* len) override; +public: + virtual int set_leaflet_scan(void);//鍗曞紶鎵弿 + virtual int get_abuot_info(void);//鑾峰彇杞欢鍏充簬淇℃伅 (鍩虹被瀹炵幇) + virtual int restore_default_setting(void);//鎭㈠榛樿璁剧疆 (鍩虹被瀹炵幇) + virtual int set_final_image_format(SANE_FinalImgFormat* fmt); // 璁剧疆鍥惧儚澶勭悊鏈缁堣緭鍑猴紙final()锛夌殑鍥惧儚鏁版嵁鏍煎紡 (鍩虹被瀹炵幇) *** + virtual int get_compression_format(void);//鑾峰彇鏀寔鐨勫帇缂╂牸寮 ***鍔熻兘涓嶆敮鎸 + + virtual int get_roller_num(void); //鑾峰彇婊氳疆寮犳暟 ***瀹屾垚 + virtual int clear_roller_num(void); // 娓呴櫎婊氳酱璁℃暟 ***瀹屾垚 + + virtual int set_compression_format(void);//璁剧疆鍥惧儚鏁版嵁鏈缁堣緭鍑虹殑鍘嬬缉鏍煎紡 ***鍔熻兘涓嶆敮鎸 + virtual int set_auto_color_type(void);// 璁剧疆鑷姩鍖归厤棰滆壊妯″紡 (鍩虹被瀹炵幇) ***瀹屾垚 + + virtual std::string get_firmware_version(void); + virtual std::string get_serial_num(void); + virtual std::string get_ip(void); + + virtual int get_device_code(void);//鑾峰彇璁惧缂栫爜 + virtual int get_sleep_time(SANE_Power* getime);//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛 + virtual int set_sleep_time(int setsleepime);//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛 + + virtual int get_dogear_distance(void);//鑾峰彇鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int set_dogear_distance(void);// 璁剧疆鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int get_scanner_paperon(SANE_Bool* paperon = NULL);//鑾峰彇璁惧鏈夋棤绾稿紶 + virtual int set_scan_when_paper_on(void);//鑾峰彇鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_when_paper_on(void);//璁剧疆鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_with_hole(void);// 鑾峰彇鏄惁涓哄甫瀛旀壂鎻 + virtual int set_scan_with_hole(void);// 璁剧疆鏄惁涓哄甫瀛旀壂鎻 + + virtual int get_scan_is_sleep(void);//鑾峰彇璁惧鏄惁浼戠湢褰撲腑 +}; diff --git a/hgdriver/hgdev/hg_scanner_300.cpp b/hgdriver/hgdev/hg_scanner_300.cpp new file mode 100644 index 0000000..526a911 --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_300.cpp @@ -0,0 +1,1275 @@ +锘#include "hg_scanner_300.h" +#include "../../sdk/hginclude/hg_log.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + + +static std::string jsontext("{\"device_type\":\"G300\",\"option_count\":44,\"1\":{\"category\":\"base\",\"name\":\"cfg-1\",\"title\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"desc\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"type\":\"button\",\"cur\":\"button\",\"default\":\"button\",\"size\":0},\"2\":{\"category\":\"advanced\",\"name\":\"cfg-2\",\"title\":\"\\u5e2e\\u52a9\",\"desc\":\"\\u663e\\u793a\\u8f6f\\u4ef6\\u5e2e\\u52a9\\u6587\\u6863\",\"type\":\"button\",\"cur\":\"true\",\"default\":\"true\",\"size\":4},\"3\":{\"category\":\"base\",\"name\":\"grp-1\",\"title\":\"\\u57fa\\u672c\\u8bbe\\u7f6e\",\"type\":\"group\"},\"4\":{\"category\":\"base\",\"name\":\"cfg-4\",\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6e\\u989c\\u8272\\u4f4d\\u6df1\",\"type\":\"string\",\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"size\":24,\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"]},\"5\":{\"category\":\"base\",\"name\":\"cfg-5\",\"title\":\"\\u7070\\u5ea6\\u6216\\u9ed1\\u767d\\u56fe\\u50cf - \\u9664\\u8272\",\"desc\":\"\\u9664\\u53bb\\u56fe\\u50cf\\u5f69\\u8272\",\"type\":\"string\",\"cur\":\"\\u4e0d\\u9664\\u8272\",\"default\":\"\\u4e0d\\u9664\\u8272\",\"size\":16,\"range\":[\"\\u4e0d\\u9664\\u8272\",\"\\u9664\\u7ea2\\u8272\",\"\\u9664\\u7eff\\u8272\",\"\\u9664\\u84dd\\u8272\",\"\\u7ea2\\u8272\\u589e\\u5f3a\",\"\\u7eff\\u8272\\u589e\\u5f3a\",\"\\u84dd\\u8272\\u589e\\u5f3a\"],\"depend_or\":[\"4==256\\u7ea7\\u7070\\u5ea6\",\"4==\\u9ed1\\u767d\"]},\"6\":{\"category\":\"base\",\"name\":\"cfg-6\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u591a\\u6d41\\u8f93\\u51fa\\u9664\\u7ea2\",\"desc\":\"\\u591a\\u901a\\u9053\\u8f93\\u51fa\\u4e2d\\uff0c\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"7\":{\"category\":\"base\",\"name\":\"cfg-7\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u7b54\\u9898\\u5361\\u9664\\u7ea2\",\"desc\":\"\\u7b54\\u9898\\u5361\\u626b\\u63cf\\u4e2d\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"8\":{\"category\":\"base\",\"name\":\"cfg-8\",\"title\":\"\\u80cc\\u666f\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u5f69\\u8272\\u56fe\\u50cf\\u80cc\\u666f\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"9\":{\"category\":\"base\",\"name\":\"cfg-9\",\"title\":\" \\u80cc\\u666f\\u8272\\u5f69\\u6d6e\\u52a8\\u8303\\u56f4\",\"desc\":\"\\u8bbe\\u5b9a\\u80cc\\u666f\\u8272\\u5f69\\u7684\\u6d6e\\u52a8\\u8303\\u56f4\\uff0c\\u5728\\u8be5\\u8303\\u56f4\\u5185\\u7684\\u90fd\\u5f53\\u4f5c\\u80cc\\u666f\\u79fb\\u9664\",\"type\":\"int\",\"cur\":10,\"default\":10,\"size\":4,\"range\":{\"min\":1,\"max\":40},\"depend_or\":[\"8==true\"]},\"10\":{\"category\":\"base\",\"name\":\"cfg-10\",\"title\":\"\\u9ed1\\u767d\\u56fe\\u50cf\\u566a\\u70b9\\u4f18\\u5316\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==\\u9ed1\\u767d\"]},\"11\":{\"category\":\"base\",\"name\":\"cfg-11\",\"title\":\" \\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"type\":\"int\",\"cur\":30,\"default\":30,\"size\":4,\"range\":{\"min\":10,\"max\":50},\"depend_or\":[\"10==true\"]},\"12\":{\"category\":\"base\",\"name\":\"cfg-12\",\"title\":\"\\u7eb8\\u5f20\\u5c3a\\u5bf8\",\"desc\":\"\\u8bbe\\u7f6e\\u7eb8\\u5f20\\u5927\\u5c0f\",\"type\":\"string\",\"cur\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"default\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"size\":36,\"range\":[\"A4\",\"A5\",\"A5\\u6a2a\\u5411\",\"A6\",\"A6\\u6a2a\\u5411\",\"B5\",\"B5\\u6a2a\\u5411\",\"B6\",\"B6\\u6a2a\\u5411\",\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\"]},\"13\":{\"category\":\"base\",\"name\":\"cfg-13\",\"title\":\"\\u626b\\u63cf\\u9875\\u9762\",\"desc\":\"\\u8bbe\\u7f6e\\u9875\\u9762\\u626b\\u63cf\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u53cc\\u9762\",\"default\":\"\\u53cc\\u9762\",\"size\":50,\"range\":[\"\\u5355\\u9762\",\"\\u53cc\\u9762\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\",\"\\u5bf9\\u6298\"]},\"14\":{\"category\":\"base\",\"name\":\"cfg-14\",\"title\":\" \\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7075\\u654f\\u5ea6\",\"desc\":\"\\u7075\\u654f\\u5ea6\\u8d8a\\u9ad8\\uff0c\\u5219\\u8d8a\\u5bb9\\u6613\\u8df3\\u8fc7\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":1,\"max\":100},\"depend_or\":[\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\"]},\"15\":{\"category\":\"base\",\"name\":\"cfg-15\",\"title\":\"\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u626b\\u63cf\\u4eea\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":{\"min\":100,\"max\":300}},\"16\":{\"category\":\"base\",\"name\":\"cfg-16\",\"title\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"desc\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\"]},\"17\":{\"category\":\"base\",\"name\":\"cfg-17\",\"title\":\"\\u56fe\\u50cf\\u62c6\\u5206\",\"desc\":\"\\u81ea\\u52a8\\u62c6\\u5206\\u56fe\\u50cf\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"18\":{\"category\":\"base\",\"name\":\"cfg-18\",\"title\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"desc\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"19\":{\"category\":\"base\",\"name\":\"cfg-19\",\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u7eb8\\u5f20\\u4e2d\\u7684\\u7a7f\\u5b54\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"20\":{\"category\":\"base\",\"name\":\"cfg-20\",\"title\":\" \\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":0.500000},\"depend_or\":[\"19==true\"]},\"21\":{\"category\":\"base\",\"name\":\"grp-2\",\"title\":\"\\u4eae\\u5ea6\",\"type\":\"group\"},\"22\":{\"category\":\"base\",\"name\":\"cfg-22\",\"title\":\"\\u4eae\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4eae\\u5ea6\",\"type\":\"int\",\"cur\":128,\"default\":128,\"size\":4,\"range\":{\"min\":1,\"max\":255}},\"23\":{\"category\":\"base\",\"name\":\"cfg-23\",\"title\":\"\\u5bf9\\u6bd4\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u5bf9\\u6bd4\\u5ea6\",\"type\":\"int\",\"cur\":4,\"default\":4,\"size\":4,\"range\":{\"min\":1,\"max\":7}},\"24\":{\"category\":\"base\",\"name\":\"cfg-24\",\"title\":\"\\u4f3d\\u739b\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4f3d\\u739b\\u503c\",\"type\":\"float\",\"cur\":1.000000,\"default\":1.000000,\"size\":4,\"range\":{\"min\":1.000000,\"max\":5.000000}},\"25\":{\"category\":\"base\",\"name\":\"grp-3\",\"title\":\"\\u56fe\\u50cf\\u5904\\u7406\",\"type\":\"group\"},\"26\":{\"category\":\"base\",\"name\":\"cfg-26\",\"title\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"desc\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"type\":\"string\",\"cur\":\"\\u65e0\",\"default\":\"\\u65e0\",\"size\":20,\"range\":[\"\\u65e0\",\"\\u9510\\u5316\",\"\\u8fdb\\u4e00\\u6b65\\u9510\\u5316\",\"\\u6a21\\u7cca\",\"\\u8fdb\\u4e00\\u6b65\\u6a21\\u7cca\"]},\"27\":{\"category\":\"base\",\"name\":\"cfg-27\",\"title\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"desc\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"28\":{\"category\":\"base\",\"name\":\"cfg-28\",\"title\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"desc\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5bf9\\u6298\",\"27!=true\",\"12!=\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18!=true\"]},\"29\":{\"category\":\"advanced\",\"name\":\"cfg-29\",\"title\":\"\\u9608\\u503c\",\"desc\":\"\\u9608\\u503c\",\"type\":\"int\",\"cur\":40,\"default\":40,\"size\":4,\"range\":{\"min\":30,\"max\":50},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"30\":{\"category\":\"advanced\",\"name\":\"cfg-30\",\"title\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"desc\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"type\":\"int\",\"cur\":8,\"default\":8,\"size\":4,\"range\":{\"min\":1,\"max\":20},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"31\":{\"category\":\"advanced\",\"name\":\"cfg-31\",\"title\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"desc\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"type\":\"int\",\"cur\":5,\"default\":5,\"size\":4,\"range\":{\"min\":5,\"max\":30},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\"]},\"32\":{\"category\":\"advanced\",\"name\":\"cfg-32\",\"title\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"desc\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"default\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"size\":40,\"range\":[\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"\\u51f9\\u591a\\u8fb9\\u5f62\"],\"depend_or\":[\"27==true\"]},\"33\":{\"category\":\"base\",\"name\":\"cfg-33\",\"title\":\"\\u9632\\u6b62\\u6e17\\u900f\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"34\":{\"category\":\"base\",\"name\":\"cfg-34\",\"title\":\" \\u9632\\u6b62\\u6e17\\u900f\\u7b49\\u7ea7\",\"desc\":\"\",\"type\":\"string\",\"cur\":\"\\u8f83\\u5f31\",\"default\":\"\\u8f83\\u5f31\",\"size\":12,\"range\":[\"\\u8f83\\u5f31\",\"\\u5f31\",\"\\u4e00\\u822c\",\"\\u5f3a\",\"\\u8f83\\u5f3a\"],\"depend_or\":[\"33==true\"]},\"35\":{\"category\":\"base\",\"name\":\"cfg-35\",\"title\":\"\\u53bb\\u9664\\u6469\\u5c14\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"36\":{\"category\":\"base\",\"name\":\"cfg-36\",\"title\":\"\\u9519\\u8bef\\u6269\\u6563\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"37\":{\"category\":\"base\",\"name\":\"cfg-37\",\"title\":\"\\u9664\\u7f51\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"38\":{\"category\":\"base\",\"name\":\"grp-4\",\"title\":\"\\u9001\\u7eb8\\u65b9\\u5f0f\\u8bbe\\u7f6e\",\"type\":\"group\"},\"39\":{\"category\":\"base\",\"name\":\"cfg-39\",\"title\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\",\"desc\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\\u9001\\u7eb8\\u72b6\\u6001\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"40\":{\"category\":\"base\",\"name\":\"cfg-40\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u626b\\u63cf\\u7eb8\\u5f20\\u6570\\u91cf\",\"type\":\"string\",\"cur\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"default\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"size\":24,\"range\":[\"\\u8fde\\u7eed\\u626b\\u63cf\",\"\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"41\":{\"category\":\"base\",\"name\":\"cfg-41\",\"title\":\" \\u626b\\u63cf\\u6570\\u91cf\",\"desc\":\"\\u626b\\u63cf\\u6307\\u5b9a\\u6570\\u91cf\",\"type\":\"int\",\"cur\":1,\"default\":1,\"size\":4,\"depend_or\":[\"40==\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"42\":{\"category\":\"base\",\"name\":\"cfg-42\",\"title\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"desc\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"type\":\"string\",\"cur\":\"0\\u00b0\",\"default\":\"0\\u00b0\",\"size\":16,\"range\":[\"0\\u00b0\",\"90\\u00b0\",\"180\\u00b0\",\"-90\\u00b0\"]},\"43\":{\"category\":\"base\",\"name\":\"cfg-43\",\"title\":\"\\u80cc\\u9762\\u65cb\\u8f6c180\\u00b0\",\"desc\":\"\\u80cc\\u9762\\u626b\\u63cf\\u7684\\u56fe\\u50cf\\u65cb\\u8f6c180\\u00b0\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\",\"13!=\\u5bf9\\u6298\",\"42!=\\u81ea\\u52a8\\u6587\\u672c\\u65b9\\u5411\\u8bc6\\u522b\\u00b0\"]}}"); +namespace settingsdsp_300 +{ + hg_err device_status_to_hg_err(int usbdata) + { + hg_err code = HG_ERR_NO_DATA; + switch (usbdata) + { + case HAVE_IMAGE: + code = HG_ERR_OK; + break; + case STOP_SCAN: + code = HG_ERR_DEVICE_STOPPED; + break; + case COUNT_MODE: + code = HG_ERR_DEVICE_COUNT_MODE; + break; + case NO_FEED: + code = HG_ERR_DEVICE_NO_PAPER; + break; + case OPEN_COVER: + code = HG_ERR_DEVICE_COVER_OPENNED; + break; + case FEED_IN_ERROR: + code = HG_ERR_DEVICE_FEEDING_PAPER; + break; + case PAPER_JAM: + code = HG_ERR_DEVICE_PAPER_JAMMED; + break; + case DETECT_DOUBLE_FEED: + code = HG_ERR_DEVICE_DOUBLE_FEEDING; + break; + case DETECT_STAPLE: + code = HG_ERR_DEVICE_STAPLE_ON; + break; + case PAPER_SKEW: + code = HG_ERR_DEVICE_PAPER_SKEW; + break; + case HARDWARE_ERROR: + code = HG_ERR_DEVICE_NOT_SUPPORT; + break; + case PC_SCAN_BUSY_or_ERROR: + code = HG_ERR_DEVICE_PC_BUSY; + break; + case SIZE_ERROR: + code = HG_ERR_DEVICE_SIZE_CHECK; + break; + default: + code = HG_ERR_NO_DATA; + break; + } + + return code; + } + + static struct + { + int paper; + TwSS type; + int dev_value; + }paper_map[] = + { + {PAPER_A3, TwSS::A3, G400_A3}, + {PAPER_A4, TwSS::A4, G400_A4}, + {PAPER_A4_LATERAL, TwSS::A4, G400_A4R}, + {PAPER_A5, TwSS::A5, G400_A5}, + {PAPER_A5_LATERAL, TwSS::A5, G400_A5R}, + {PAPER_A6, TwSS::A6, G400_A6}, + {PAPER_A6_LATERAL, TwSS::A6, G400_A6R}, + {PAPER_B4, TwSS::B4, G400_B4}, + {PAPER_B5, TwSS::B5, G400_B5}, + {PAPER_B5_LATERAL, TwSS::B5, G400_B5R}, + {PAPER_B6, TwSS::B6, G400_B6}, + {PAPER_B6_LATERAL, TwSS::B6, G400_B6R}, + {PAPER_LETTER, TwSS::USLetter, G400_LETTER}, + {PAPER_LETTER_LATERAL, TwSS::USLetter, G400_LETTERR}, + {PAPER_DOUBLE_LETTER, TwSS::USLedger, G400_DOUBLELETTER}, + {PAPER_LEGAL, TwSS::USLegal, G400_LEGAL}, + {PAPER_AUTO_MATCH, TwSS::None, G400_A4}, + {PAPER_MAX_SIZE_CLIP, TwSS::USStatement, G400_MAXSIZE}, + {PAPER_MAX_SIZE, TwSS::MaxSize, G400_MAXSIZE} + }; + int match_best_paper(int& paper, bool* exact, TwSS* type) + { + int ind = 0; + bool good = true, * r = exact ? exact : &good; + for (int i = 0; i < ARRAY_SIZE(paper_map); ++i) + { + if (paper == paper_map[i].paper) + { + ind = i; + break; + } + } + + if (paper == paper_map[ind].paper) + *r = true; + else + *r = false; + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Paper '%s' index = %d, device value = %d\n", paper_string(paper).c_str(), ind, paper_map[ind].dev_value); + paper = paper_map[ind].paper; + if (type) + *type = paper_map[ind].type; + + return ind; + } +} + + +enum hg_239_setting_item +{ + HG_239_SETTING_RESTORE = 1, // 鎭㈠榛樿璁剧疆 + HG_239_SETTING_HELP, // 甯姪 + + HG__239_SETTING_GROUP_1, + + HG_239_SETTING_COLOR_MODE, // 棰滆壊妯″紡 + + HG_239_SETTING_ERASE_COLOR, // 闄よ壊 + HG_239_SETTING_ERASE_MULTI_RED, // 澶氭祦杈撳嚭闄ょ孩 + HG_239_SETTING_ERASE_ANSWER_RED, // 绛旈鍗¢櫎绾 + HG_239_SETTING_ERASE_BACKGROUND, // 绉婚櫎鑳屾櫙 + HG_239_SETTING_ERASE_BKG_RANGE, // 绉婚櫎鑳屾櫙鑼冨洿 + HG_239_SETTING_NOISE, // 榛戠櫧鍥惧儚鍣偣浼樺寲 + HG_239_SETTING_NOISE_SIZE, // 鍣偣浼樺寲灏哄 + HG_239_SETTING_PAPER_SIZE, // 绾稿紶灏哄 + //HG_239_SETTING_PAPER_SIZE_CHECK, // 灏哄妫娴 + HG_239_SETTING_PAGE, // 鎵弿椤甸潰 + HG_239_SETTING_SKIP_NILL, // 璺宠繃绌虹櫧椤电伒鏁忓害 + HG_239_SETTING_RESOLUTION, // 鍒嗚鲸鐜 + HG_239_SETTING_REVERSE, // 浜ゆ崲姝e弽闈 + HG_239_SETTING_SPLIT, // 鍥惧儚鎷嗗垎 + HG_239_SETTING_CORRECT, // 鑷姩绾犲亸 + HG_239_SETTING_ERASE_HOLE, // 绌垮瓟绉婚櫎 + HG_239_SETTING_HOLE_SEARCH, // 绌垮瓟鎼滅储鑼冨洿 + + HG__239_SETTING_GROUP_2, + + HG_239_SETTING_LIGHT, // 浜害 + HG_239_SETTING_CONTRAST, // 瀵规瘮搴 + HG_239_SETTING_GAMMA, // 浼界帥 + + HG__239_SETTING_GROUP_3, + + HG_239_SETTING_SHARP, // 閿愬寲涓庢ā绯 + HG_239_SETTING_ERASE_BLACK_BORDER, // 娑堥櫎榛戞 + HG_239_SETTING_SAMPLE, // 娣辫壊鏍峰紶 + HG_239_SETTING_THRESHOLD, // 闃堝 + HG_239_SETTING_ANTI_NOISE, // 鑳屾櫙鎶楀櫔绛夌骇 + HG_239_SETTING_MARGIN, // 杈圭紭缂╄繘 + HG_239_SETTING_FILLING, // 鑳屾櫙濉厖鏂瑰紡 + HG_239_SETTING_PERMEATE, // 闃叉娓楅 + HG_239_SETTING_PERMEATE_lv, // 闃叉娓楅忕瓑绾 + HG_239_SETTING_REMOVE_MORR, // 鍘婚櫎鎽╁皵绾 + HG_239_SETTING_ERROR_EXTENTION, // 閿欒鎵╂暎 + HG_239_SETTING_REMOVE_TXTTURE, // 闄ょ綉绾 + + HG__239_SETTING_GROUP_4, + + HG_239_SETTING_ULTRASONIC, // 瓒呭0娉㈡娴 + HG_239_SETTING_SCAN_METHOD, // 杩炵画鎵弿鎴栨壂鎻忔寚瀹氬紶鏁 + HG_239_SETTING_SCAN_COUNT, // 鎵弿鎸囧畾鏁伴噺 + HG_239_SETTING_DIRECTION, // 鏂囩ǹ鏂瑰悜 鍒犻櫎鏂囩ǹ鑷姩璇嗗埆 + HG_239_SETTING_ROTATE, // 鑳岄潰鏃嬭浆180掳 + + HG_239_SETTING_END, + + //鏆傛椂灞忚斀 + HG_239_SETTING_MULTI_OUT = 500, // 澶氭祦杈撳嚭 +}; + + + +hg_scanner_300::hg_scanner_300(const char* dev_name,int pid, usb_io* io) : hg_scanner(G100Serial, dev_name, io),pid_(pid) +{ + string fw = get_firmware_version(); + + initdevice(); + init_setting_map(setting_map_, ARRAY_SIZE(setting_map_));//浼樺厛鍒濆鍖 + init_settings(jsontext.c_str()); + + printf_devconfig(); +} +hg_scanner_300::~hg_scanner_300() +{} + + +void hg_scanner_300::init_setting_map(int* setting_map, int count) +{ + setting_map[HG_BASE_SETTING_INDEX_RESTORE_DEFAULT_SETTINGS] = HG_239_SETTING_RESTORE; + setting_map[HG_BASE_SETTING_INDEX_HELP] = HG_239_SETTING_HELP; + + setting_map[HG_BASE_SETTING_INDEX_COLOR_MODE] = HG_239_SETTING_COLOR_MODE; + setting_map[HG_BASE_SETTING_MULTI_OUT] = HG_239_SETTING_MULTI_OUT; + setting_map[HG_BASE_SETTING_INDEX_ERASE_COLOR] = HG_239_SETTING_ERASE_COLOR; + setting_map[HG_BASE_SETTING_INDEX_ERASE_MULTI_OUT_RED] = HG_239_SETTING_ERASE_MULTI_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_ANSWER_RED] = HG_239_SETTING_ERASE_ANSWER_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND] = HG_239_SETTING_ERASE_BACKGROUND; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE] = HG_239_SETTING_ERASE_BKG_RANGE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE] = HG_239_SETTING_NOISE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE] = HG_239_SETTING_NOISE_SIZE; + setting_map[HG_BASE_SETTING_INDEX_PAPER] = HG_239_SETTING_PAPER_SIZE; + //setting_map[HG_BASE_SETTING_INDEX_PAPER_SIZE_CHECK] = HG_239_SETTING_PAPER_SIZE_CHECK; + setting_map[HG_BASE_SETTING_INDEX_PAGE] = HG_239_SETTING_PAGE; + setting_map[HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL] = HG_239_SETTING_SKIP_NILL; + setting_map[HG_BASE_SETTING_INDEX_RESOLUTION] = HG_239_SETTING_RESOLUTION; + setting_map[HG_BASE_SETTING_INDEX_EXCHANGE] = HG_239_SETTING_REVERSE; + setting_map[HG_BASE_SETTING_INDEX_SPLIT] = HG_239_SETTING_SPLIT; + setting_map[HG_BASE_SETTING_INDEX_AUTO_CORRECT] = HG_239_SETTING_CORRECT; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE] = HG_239_SETTING_ERASE_HOLE; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE_RANGE] = HG_239_SETTING_HOLE_SEARCH; + + setting_map[HG_BASE_SETTING_INDEX_BRIGHT] = HG_239_SETTING_LIGHT; + setting_map[HG_BASE_SETTING_INDEX_CONTRAST] = HG_239_SETTING_CONTRAST; + setting_map[HG_BASE_SETTING_INDEX_GAMMA] = HG_239_SETTING_GAMMA; + + setting_map[HG_BASE_SETTING_INDEX_SHARPEN] = HG_239_SETTING_SHARP; + setting_map[HG_BASE_SETTING_INDEX_DARK_SAMPLE] = HG_239_SETTING_SAMPLE; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BLACK_FRAME] = HG_239_SETTING_ERASE_BLACK_BORDER; + setting_map[HG_BASE_SETTING_INDEX_THRESHOLD] = HG_239_SETTING_THRESHOLD; + setting_map[HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL] = HG_239_SETTING_ANTI_NOISE; + setting_map[HG_BASE_SETTING_INDEX_MARGIN] = HG_239_SETTING_MARGIN; + setting_map[HG_BASE_SETTING_INDEX_FILL_BACKGROUND] = HG_239_SETTING_FILLING; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE] = HG_239_SETTING_PERMEATE; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE_LV] = HG_239_SETTING_PERMEATE_lv; + setting_map[HG_BASE_SETTING_REMOVE_MORR] = HG_239_SETTING_REMOVE_MORR; + setting_map[HG_BASE_SETTING_ERROR_EXTENTION] = HG_239_SETTING_ERROR_EXTENTION; + setting_map[HG_BASE_SETTING_REMOVE_TXTTURE] = HG_239_SETTING_REMOVE_TXTTURE; + + setting_map[HG_BASE_SETTING_INDEX_ULTRASONIC_CHECK] = HG_239_SETTING_ULTRASONIC; + setting_map[HG_BASE_SETTING_INDEX_SCAN_MODE] = HG_239_SETTING_SCAN_METHOD; + setting_map[HG_BASE_SETTING_INDEX_SCAN_COUNT] = HG_239_SETTING_SCAN_COUNT; + setting_map[HG_BASE_SETTING_INDEX_TEXT_DIRECTION] = HG_239_SETTING_DIRECTION; + setting_map[HG_BASE_SETTING_INDEX_ROTATE_BKG_180] = HG_239_SETTING_ROTATE; + + + } + + +int hg_scanner_300::on_scanner_closing(bool force) +{ + return HG_ERR_OK; +} +void hg_scanner_300::thread_handle_usb_read(void) +{ + int ret = HG_ERR_OK, + status = HG_ERR_OK; + StopWatch sw; + savestatus_.clear(); + + while (run_) + { + if (user_cancel_) + break; + + USBCB usb={0}; + ret = get_scanner_status(usb); + //printf("usb.u32_Data = %d ret = %d\r\n",usb.u32_Data,ret); + ////濡傛灉璁惧鍑虹幇鍗$焊 鎴栬呭弻寮犵瓑璁惧淇℃伅闂锛岄渶瑕佺瓑鍒拌澶囪繘琛屽彂閫乻top鎵嶈兘鍋滄銆 鎵浠ュ缁堜互 "鍋滄" 娑堟伅涓虹粨鏉熶俊鍙 + ////濡傛灉鏈夊浘鐨勬儏鍐典笅锛屽苟涓斿崱绾告垨鍙屽紶绛夛紝璁惧浼氬厛鍙戦佸崱绾稿弻寮犱俊鎭傛墍浠ユ帴鍙楀埌閿欒淇℃伅涔嬪悗锛屼粛闇瑕佹妸鍥惧儚鍙栧嚭鏉ャ + + if (ret == HG_ERR_DEVICE_STOPPED) + { + if (!savestatus_.empty()) + { + status_ = savestatus_[0]; //浠ョ涓涓秷鎭负鍑 + } + else + { + status_ = HG_ERR_OK; + } + savestatus_.clear(); + break; + } + if (ret != HG_ERR_OK && ret != HG_ERR_DEVICE_STOPPED && ret != HG_ERR_NO_DATA) + { + savestatus_.push_back(ret); + } + if (sw.elapsed_ms() > 30000)//闃叉鐘舵佷俊鎭竴鐩村彇涓嶄笂鏉ュ鑷村崱姝 + { + + if (!savestatus_.empty()) + { + status_ = savestatus_[0]; + } + else + { + status_ = HG_ERR_TIMEOUT; + } + savestatus_.clear(); + hg_log::log(HG_LOG_LEVEL_WARNING, "get status timeout,get image out\n"); + break; + } + + if (ret == HG_ERR_OK && usb.u32_Count > 0) + { + int totalNum = usb.u32_Count; + std::shared_ptr> imagedata(new std::vector (totalNum)); + + ret = get_img_data(imagedata); + io_->set_timeout(200); + + if (ret == HG_ERR_OK) + { + ret = pop_image(); + sw.reset(); + if (ret != HG_ERR_OK) + { + status_ = ret; + break; + } + } + else + { + status_ = ret; + break; + } + } + this_thread::sleep_for(chrono::milliseconds(10)); + } +} + + int hg_scanner_300::start(void) + { + int ret = HG_ERR_OK, + count = -1; + user_cancel_ = false; + + ret = writedown_device_configuration(true); + if (ret == HG_ERR_OK) + writedown_image_configuration(); + else + { + status_ = ret; + return ret; + } + printf_devconfig(); + + ret = get_scan_is_sleep(); + if (ret == HG_ERR_DEVICE_SLEEPING) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SLEEPING, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_SLEEPING); + return ret; + } + ret = get_scanner_paperon(); + if (ret == HG_ERR_DEVICE_NO_PAPER) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_PAPER, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_NO_PAPER); + return HG_ERR_OK; + } + + USBCB usb = {START_COMMAND, img_conf_.scannum, 0}; + ret = writeusb(usb); + io_->set_timeout(500); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start is.(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + if(ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_BUSY; + wait_usb_.notify(); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + else + status_ = ret; + + return ret; + } +int hg_scanner_300::stop(void) +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {STOP, 0, 0}; + ret = writeusb(usbcb); + + if (status_ == HG_ERR_DEVICE_BUSY) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO,"stop status is busy\r\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + } + //user_cancel_ = true; + if (ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_STOPPED; + } + + return status_; +} +int hg_scanner_300::writeusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(usb); + + ret = io_->write_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_300::readusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(USBCB); + + ret = io_->read_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_300::pop_image() +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {POP_IMAGE, 0, 0}; + ret = writeusb(usbcb); + + return ret; +} + +int hg_scanner_300::get_scanner_status(USBCB &usb) +{ + int ret = HG_ERR_OK; + + usb = {GET_DSP_STATUS, 0, 0}; + + ret = writeusb(usb); + if (ret != HG_ERR_OK) + return ret; + + io_->set_timeout(200); //蹇呰寤舵椂锛屼笖涓嶈兘灏忎簬杩欎釜鏁板 + + ret = readusb(usb); + + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + hg_err code = settingsdsp_300::device_status_to_hg_err(usb.u32_Data); + return code ; +} +int hg_scanner_300::get_img_data(std::shared_ptr> &imagedata) +{ + int total = imagedata->size(), + ret = HG_ERR_OK, + index = 0, + block = total; + + USBCB usb{GET_IMAGE, 0, total}; + ret = writeusb(usb); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + io_->set_timeout(500); + + printf_devconfig(); + + while (total > 0) + { + block = 512 * 1024; + + if (total < block) + block = total; + + ret = io_->read_bulk(imagedata->data() + index,&block); + + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + index += block; + total -= block; + } + if (ret == HG_ERR_OK) + { + ret = save_usb_data(imagedata); + } + status_ = ret; + + return ret; +} + +void hg_scanner_300::image_process(std::shared_ptr>& buffer) +{ + int ret = HG_ERR_OK; + hg_imgproc::IMGPRCPARAM param; + hg_imgproc::HIMGPRC handle = NULL; + hg_imgproc::IMGHEAD ih; + int err = HG_ERR_OK, + index = 0; + void* buf = NULL; + + param.bits = 8; + param.black_white = img_conf_.pixtype == COLOR_MODE_BLACK_WHITE; + param.channels = img_conf_.pixtype == COLOR_MODE_24_BITS ? 3 : 1; + param.color_mode = img_conf_.pixtype; + param.double_side = img_conf_.is_duplex; + param.dpi = img_conf_.resolution_dst; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.black_white :%d\r\n",param.black_white); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.channels :%d\r\n",param.channels); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.color_mode :%d\r\n",param.color_mode); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.double_side :%d\r\n",param.double_side); + + + + handle = hg_imgproc::init(&img_conf_,¶m); + hg_imgproc::load_buffer(handle, buffer); + hg_imgproc::decode(handle,pid_); + + + + /////////////////////////////////////111111111111111111111111111////////////////////////////////// + ret = hg_imgproc::auto_crop(handle); //棣栨蹇呴』鍏堣皟鐢ㄨ繖涓畻娉 + + if (img_conf_.fillhole.is_fillhole) + { + ret = hg_imgproc::fillhole(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"濉┛瀛斿紑鍚痋r\n"); + } + + if (img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince) + { + ret = hg_imgproc::discardBlank(handle); + printf("涓㈠純绌虹櫧椤靛紑鍚痋r\n"); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"涓㈠純绌虹櫧椤靛紑鍚痋r\n"); + } + if(img_conf_.fadeback) + { + hg_imgproc::fadeback(handle,img_conf_.fadebackrange,param.double_side); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鑳屾櫙闄よ壊寮鍚痋r\n"); + } + if (img_conf_.resolution_dst != img_conf_.resolution_native) + { + hg_imgproc::resolution_change(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"dpi鏀瑰彉寮鍚痋r\n"); + } + if (img_conf_.cropRect.enable && !img_conf_.is_autocrop) + { + hg_imgproc::croprect(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鎵嬪姩瑁佸浘寮鍚痋r\n"); + } + if (img_conf_.filter != ColorFilter::FILTER_NONE && (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE || img_conf_.pixtype == COLOR_MODE_256_GRAY)) + { + hg_imgproc::channel(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闄よ壊寮鍚痋r\n"); + } + /////////////////////////////////////2222222222222222222222222222222222222////////////////////////////////// + hg_imgproc::customgamma(handle,false); //涓存椂false + + if (img_conf_.pixtype == COLOR_MODE_24_BITS && img_conf_.hsvcorrect) + { + hg_imgproc::answerSheetFilterRed(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"绛旈鍗″嚭绾㈠紑鍚痋r\n"); + } + if (img_conf_.refuseInflow) + { + hg_imgproc::antiInflow(handle,image_prc_param_.bits.is_permeate_lv_); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闃叉娓楅忓紑鍚痋r\n"); + } + if (img_conf_.colorCorrection && img_conf_.pixtype != COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::colorCorrection(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"棰滆壊鏍℃寮鍚痋r\n"); + } + if ((img_conf_.is_autotext != TEXT_DIRECTION_0 || img_conf_.is_backrotate180) && (img_conf_.is_autotext != TEXT_DIRECTION_AUTO)) + { + hg_imgproc::orentation(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鑷姩鏃嬭浆寮鍚痋r\n"); + } + if (img_conf_.removeMorr) + { + hg_imgproc::textureRemove(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"闄ゆ懇灏旂汗寮鍚痋r\n"); + } + if (img_conf_.detachnoise.is_detachnoise) + { + hg_imgproc::nosieDetach(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鍣偣浼樺寲寮鍚痋r\n"); + } + if (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::errorextention(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"榛戠櫧鍥惧鐞嗗紑鍚痋r\n"); + } + if (img_conf_.en_fold) + { + hg_imgproc::fold(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"瀵规姌寮鍚痋r\n"); + } + + /////////////////////////////////// + + if (img_conf_.multi_output_red && img_conf_.pixtype == COLOR_MODE_24_BITS) + { + err = hg_imgproc::multi_out_red(handle); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"澶氭祦鍑虹孩寮鍚痋r\n"); + } + + + if (image_prc_param_.bits.multi_out != MULTI_OUT_NOT && !user_cancel_) + { + //err = hg_imgproc::multi_out(handle,image_prc_param_.bits.multi_out - 1); 闈炴硶鎸囦护 鏆傛椂灞忚斀 + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"澶氭祦杈撳嚭寮鍚痋r\n"); + } + if (img_conf_.sharpen) + { + hg_imgproc::sharpenType(handle); + } + if (image_prc_param_.bits.split) + { + int colormode=1; + if(img_conf_.filter == RID_COLOR_NONE) + colormode=img_conf_.pixtype; + + err = hg_imgproc::split(handle,img_conf_.multiOutput,img_conf_.splitImage,img_conf_.multi_output_red,colormode,img_conf_.is_duplex); + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "img split-> multiOutput is:%d splitImage is:%d multi_output_red is:%d pixtype is:%d is_duplex:%d\r\n" + ,img_conf_.multiOutput + ,img_conf_.splitImage + ,img_conf_.multi_output_red + ,img_conf_.pixtype + ,img_conf_.is_duplex); + + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"鍥惧儚鎷嗗垎寮鍚痋r\n"); + } + + if (!image_prc_param_.bits.split || !image_prc_param_.bits.rid_red || !image_prc_param_.bits.multi_out) + { + if(img_conf_.automaticcolor) + { + hg_imgproc::auto_matic_color(handle,img_conf_.automaticcolortype); + //HG_LOGHG_LOG_LEVEL_DEBUG_INFO,"棰滆壊鑷姩璇嗗埆寮鍚痋r\n"); + } + } + + hg_imgproc::final(handle); + while (hg_imgproc::get_final_data(handle, &ih, &buf, index++) == HG_ERR_OK && !user_cancel_) + { + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "Final picture %d (%d * %d * %d) with %u bytes!\n", index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + + if(!img_type_.empty()) + { + std::vectorbmpdata; + //cv::imencode(img_type_,*((cv::Mat*)buf),bmpdata); + hg_imgproc::imgtypechange(handle,img_type_,buf,bmpdata); + if(bmpdata.empty()) + { + status_ = HG_ERR_NO_DATA; + return ; + } + buf = bmpdata.data(); + ih.total_bytes = bmpdata.size(); + + HG_VLOG_MINI_6(HG_LOG_LEVEL_DEBUG_INFO, "Set img type is:%s Final picture %d (%d * %d * %d) with %u bytes!\n",img_type_.c_str() ,index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + } + save_final_image(&ih, buf); + } + + hg_imgproc::release(handle); +} + +int hg_scanner_300::writedown_device_configuration(bool type,HGSCANCONF_G400 *d) +{ + if (!type) + return HG_ERR_OK; + + int ret = HG_ERR_OK, + len = 0; + + if (!d) + d = &dsp_config; + + USBCB usbcb = {CONFIGURED_DATA, d->value, 0}; + len = sizeof(USBCB); + ret = io_->write_bulk(&usbcb,&len); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "writedown_device_configuration is .(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} + +int hg_scanner_300::on_color_mode_changed(int& color_mode) +{ + int ret = HG_ERR_OK; + // if ((((color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) && !dsp_config.params.color) || + // ((color_mode != COLOR_MODE_24_BITS && color_mode != COLOR_MODE_AUTO_MATCH) && dsp_config.params.color)) && color_mode != -1) + // { + // printf("color_mode111111111111111111111 =%d\r\n",color_mode); + // dsp_config.params.color ^= 1; + + // //ret = writedown_device_configuration(); + // printf("on_color_mode_changed ret =%d\r\n",ret); + // if(ret) + // dsp_config.params.color ^= 1; + // } + + if (color_mode == COLOR_MODE_BLACK_WHITE || color_mode == COLOR_MODE_256_GRAY) + { + dsp_config.params.isColor = 0; + } + else if(color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) + { + dsp_config.params.isColor = 1; + } + + HGSCANCONF_G400 d = dsp_config; + if(image_prc_param_.bits.rid_color != RID_COLOR_NONE && image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY && color_mode == -1) + { + d.params.isColor = 1; + } + else if (image_prc_param_.bits.rid_color == RID_COLOR_NONE && image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY && color_mode == -1) + { + d.params.isColor = 0; + } + if(color_mode == -1) + ret = writedown_device_configuration(false,&d); + + return ret; + +} +int hg_scanner_300::on_paper_changed(int& paper) +{ + bool exact = true; + int ind = settingsdsp_300::match_best_paper(paper, &exact, &paper_size_), + ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (dsp_config.params.pageSize != settingsdsp_300::paper_map[ind].dev_value) + { + int old = dsp_config.params.pageSize; + + dsp_config.params.pageSize = settingsdsp_300::paper_map[ind].dev_value; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.pageSize = old; + for (int i = 0; i < ARRAY_SIZE(settingsdsp_300::paper_map); ++i) + { + if (settingsdsp_300::paper_map[i].dev_value == old) + { + paper = settingsdsp_300::paper_map[i].paper; + break; + } + } + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + } + return ret; +} +int hg_scanner_300::on_paper_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + if(dsp_config.params.sizedetece ^ check) + { + dsp_config.params.sizedetece = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.sizedetece = !check; + check = dsp_config.params.sizedetece; + } + } + return ret; +} + +int hg_scanner_300::on_resolution_changed(int& dpi) +{ + int ret = HG_ERR_OK; + dsp_config.params.dpi = 1; + ret = writedown_device_configuration(); + return ret; +} + +int hg_scanner_300::on_ultrasonic_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dsp_config.params.doubleFeeded ^ check) + { + dsp_config.params.doubleFeeded = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.doubleFeeded = !check; + check = dsp_config.params.doubleFeeded; + } + } + + return ret; +} +int hg_scanner_300::agreement(TwSS tw,int align) +{ + int ret = HG_ERR_OK; + SIZE size; + HGSCANCONF_G400 d = dsp_config; + size = papersize.GetPaperSize(TwSS::A4,200,align); + dsp_config.params.dstHeight = (int)((size.cy + 200)/100); + if (img_conf_.is_autocrop) + { + dsp_config.params.pageSize = G400_AUTO; + } + + printf("img_conf_.filter = %d \r\n",img_conf_.filter); + if (img_conf_.filter != 3) + { + d.params.isColor = 1; + } + ret = writedown_device_configuration(true,&d); + io_->set_timeout(200); + + return ret; +} +int hg_scanner_300::initdevice() +{ + dsp_config.value = 0; + dsp_config.params.enableLed = 1; + dsp_config.params.isCorrect = 1; + dsp_config.params.reversed1 = 0; + dsp_config.params.reversed2 = 0; + dsp_config.params.sizedetece =0; + writedown_device_configuration(true); + printf_devconfig(); + + return HG_ERR_OK; +} + +void hg_scanner_300::writedown_image_configuration(void) +{ + SCANCONF ic; + + ic.papertype = paper_size_; + + if (is_lateral(image_prc_param_.bits.paper)) + ic.paperAlign = Rot270; + else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) + ic.paperAlign = AutoTextOrientation; + else + ic.paperAlign = Rot0; + + ic.en_sizecheck = dsp_config.params.sizedetece; + ic.imageRotateDegree = image_prc_param_.bits.text_direction * 90.0f; + ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE || image_prc_param_.bits.page == PAGE_OMIT_EMPTY || + image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT || image_prc_param_.bits.page ==PAGE_FOLIO); + + ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); + + ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; + + ic.automaticcolor = is_auto_matic_color; + + ic.automaticcolortype = 1;// ic.pixtype; //瀛樼枒 + ic.resolution_dst = resolution_; + ic.resolution_native = 200; + + ic.gamma = (float)gamma_; + ic.contrast = (float)contrast_ ; + ic.brightness = (float)bright_ ; + + printf("brightness = %f contrast = %f gamma = %f\r\n ",ic.brightness,ic.contrast,ic.gamma); + ic.threshold = threshold_; + ic.is_autocontrast = 0; //鏃犲弬鏁 + ic.is_autocrop = (ic.papertype == TwSS::None || ic.papertype ==TwSS::USStatement); + ic.is_autodiscradblank_normal = image_prc_param_.bits.page == PAGE_OMIT_EMPTY; + printf("image_prc_param_.bits.page = %d\r\n",image_prc_param_.bits.page); + ic.discardblank_percent = omit_empty_level_ > 70 ? 70 : omit_empty_level_; //榛樿20 + ic.is_autodiscradblank_vince = image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT; + ic.is_switchfrontback = image_prc_param_.bits.exchange; + ic.autodescrew = image_prc_param_.bits.automatic_skew; + ic.multi_output_red = image_prc_param_.bits.rid_red; + ic.hsvcorrect = image_prc_param_.bits.rid_answer_red; + + ic.sharpen = image_prc_param_.bits.sharpen; + //ic.enhance_color = image_prc_param_.bits.rid_color; + ic.fillbackground = image_prc_param_.bits.erase_black_frame; + ic.is_convex = (image_prc_param_.bits.fill_background == FILL_BKG_CONVEX_POLYGON); + ic.noise = image_prc_param_.bits.noise_optimize; + ic.indent = margin_; + ic.AutoCrop_threshold = threshold_; + if (test_1_paper_) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "scanning mode: testing ONE paper ...\n"); + ic.scannum = 1; + } + else + { + ic.scannum = scan_count_; + } + + ic.is_backrotate180 = image_prc_param_.bits.rotate_back_180; + ic.is_dogeardetection = image_prc_param_.bits.fractate_check; + + ic.hardwarecaps.en_skrewdetect = 0; + ic.hardwarecaps.en_doublefeed = dsp_config.params.doubleFeeded; + ic.hardwarecaps.en_stapledetect = 0; + ic.hardwarecaps.skrewdetectlevel = 0; + // ic.hardwarecaps.is_autopaper = dsp_config.params.is_autopaper; + ic.hardwarecaps.capturepixtype = 0; //鏆傛棤鍙傛暟 鑾峰彇鍥惧儚绫诲瀷 + ic.hardwarecaps.lowpowermode = LowPowerMode::Min_None; //鏆傛棤鍙傛暟 璁剧疆浼戠湢鏃堕棿 涓や釜鍙傛暟3399鏈娇鐢 + + ic.fillhole.is_fillhole = image_prc_param_.bits.rid_hole; + ic.fillhole.fillholeratio = rid_hole_range_; + + ic.detachnoise.is_detachnoise = image_prc_param_.bits.noise_optimize; + ic.detachnoise.detachnoise = noise_range_; + ic.is_autotext = image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO ? 1 :image_prc_param_.bits.text_direction; + ic.isfillcolor = false;//鏆傛棤鍙傛暟 鏈娇鐢ㄥ埌 + ic.refuseInflow = image_prc_param_.bits.is_permeate; + ic.colorCorrection = 0; + ic.removeMorr = image_prc_param_.bits.remove_morr; + ic.errorExtention = image_prc_param_.bits.error_extention ;//鏆傛棤鍙傛暟 + ic.textureRemove = image_prc_param_.bits.remove_txtture;//鏆傛棤鍙傛暟 + ic.splitImage = image_prc_param_.bits.split; + ic.cropRect.enable = 0;//鏆傛棤鍙傛暟 + ic.cropRect.height = 0;//鏆傛棤鍙傛暟 + ic.cropRect.width = 0;//鏆傛棤鍙傛暟 + ic.cropRect.x = 0;//鏆傛棤鍙傛暟 + ic.cropRect.y = 0;//鏆傛棤鍙傛暟 + ic.multiOutput = MultiOutput::Unused; //鏆傛棤鍙傛暟 + ic.normalCrop = image_prc_param_.bits.dark_sample; + ic.dogeardistabce = fractate_level_; + ic.fadeback = image_prc_param_.bits.erase_bakground; + ic.fadebackrange = erase_bkg_range_; + + + int filter_clr[] = { 3, 0, 1, 2, 5, 6, 7 }; + if(image_prc_param_.bits.color_mode == COLOR_MODE_24_BITS || image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH) + ic.filter = 3; + else + { + ic.filter = filter_clr[image_prc_param_.bits.rid_color]; + // if(ic.filter != 3) + // ic.pixtype = 2; + + ic.hsvcorrect = 0; + ic.multi_output_red = 0; + ic.multiOutput = MultiOutput::Unused; + ic.fadeback = false; + } + + img_conf_ = ic; + + int ret = agreement((TwSS)img_conf_.papertype,img_conf_.paperAlign); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ; + } + + printf ("ic.resolution_native =%f ic.resolution_dst = %f img_conf_.resolution_dst = %f \r\n",ic.resolution_native,ic.resolution_dst,img_conf_.resolution_dst); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.papertype=%d", ic.papertype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.AutoCrop_threshold=%d", ic.AutoCrop_threshold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.autodescrew=%d", ic.autodescrew); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.automaticcolor=%d", ic.automaticcolor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.brightness=%f", ic.brightness); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.contrast=%f", ic.contrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_fold=%d", ic.en_fold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_sizecheck=%d", ic.en_sizecheck); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.enhance_color=%d", ic.enhance_color); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillbackground=%d", ic.fillbackground); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.filter=%d", ic.filter); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.gamma=%f", ic.gamma); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.capturepixtype=%d", ic.hardwarecaps.capturepixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.en_doublefeed=%d", ic.hardwarecaps.en_doublefeed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hsvcorrect=%d", ic.hsvcorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.imageRotateDegree=%f", ic.imageRotateDegree); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.indent=%d", 5); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocontrast=%d", ic.is_autocontrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocrop=%d", ic.is_autocrop); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_normal=%d", ic.is_autodiscradblank_normal); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_vince=%d", ic.is_autodiscradblank_vince); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autotext=%d", ic.is_autotext); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_backrotate180=%d", ic.is_backrotate180); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_convex=%d", ic.is_convex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_duplex=%d", ic.is_duplex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_switchfrontback=%d", ic.is_switchfrontback); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_dogeardetection=%d", ic.is_dogeardetection); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.multi_output_red=%d", ic.multi_output_red); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.noise=%d", 8); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_dst=%f", ic.resolution_dst); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_native=%f", ic.resolution_native); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.scannum=%d", ic.scannum); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.sharpen=%d", ic.sharpen); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.discardblank_percent=%d", ic.discardblank_percent); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.is_detachnoise=%d", ic.detachnoise.is_detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.detachnoise=%d\r ", ic.detachnoise.detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.removeMorr=%d\r ", ic.removeMorr); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.errorExtention=%d\r ", ic.errorExtention); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.textureRemove=%d\r\n ", ic.textureRemove); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.is_fillhole=%d\r\n ", ic.fillhole.is_fillhole); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.ic.fillhole.fillholeratio=%f\r\n ", ic.fillhole.fillholeratio); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fadeback=%d\r\n ",ic.fadeback); +} + +void hg_scanner_300::printf_devconfig(HGSCANCONF_G400 *d) +{ + io_->set_timeout(200); + if (!d) + d = &dsp_config; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\r\ndsp_config.params.doubleFeeded:%d\r\n",d->params.doubleFeeded); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.dpi:%d\r\n",d->params.dpi); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.dstHeight:%d\r\n",d->params.dstHeight); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.enableLed:%d\r\n",d->params.enableLed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.enableUV:%d\r\n",d->params.enableUV); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.isColor:%d\r\n",d->params.isColor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.isCorrect:%d\r\n",d->params.isCorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.pageSize:%d\r\n",d->params.pageSize); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.reversed1:%d\r\n",d->params.reversed1); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.reversed2:%d\r\n",d->params.reversed2); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.sizedetece:%d\r\n",d->params.sizedetece); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.value:%d\r\n",d->value); +} + +std::string hg_scanner_300::get_firmware_version() +{ + char buf[20] = { 0 }; + int ret = HG_ERR_OK, + len = 10; //鍗忚瀹氫箟闀垮害涓10 100 200 =8 + USBCB cmd = {GET_FW_VERSION,len,0,}; + + ret = writeusb(cmd); + ret = io_->read_bulk(buf, &len); + + return buf; +} + + std::string hg_scanner_300::get_serial_num() + { + string SerialNum = ""; + int ret = HG_ERR_OK, + len = 14; + SerialNum.resize(len); + + USBCB cmd = {GET_SERIAL,len,0,}; + ret = writeusb(cmd); + + std::lock_guard lock(io_lock_); + ret = io_->read_bulk(&SerialNum[0],&len); + if (ret != HG_ERR_OK) + { + return "0000000000000000000"; + } + + return SerialNum; + } + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int hg_scanner_300::set_leaflet_scan(void) +{ + int ret = HG_ERR_OK; + test_1_paper_ = true; + + ret = start(); + return ret; +} +int hg_scanner_300::get_abuot_info(void) +{ + + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::restore_default_setting(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::set_final_image_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_300::get_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::set_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::set_auto_color_type(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_300::get_device_code(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::get_sleep_time(SANE_Power* getime) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_SLEEP_TIME, 0, 0}; + ret = writeusb(usbcb); + if (ret != HG_ERR_OK) + { + return ret; + } + len = sizeof(usbcb); + ret = io_->read_bulk(&usbcb,&len); + printf("usbcb.u32_Data = %d\r\n",usbcb.u32_Data); + if (ret == HG_ERR_OK) + { + if (getime) + { + *getime = (SANE_Power)usbcb.u32_Data; + } + if (usbcb.u32_Data) + { + return HG_ERR_DEVICE_SLEEPING; + } + } + return ret; +} +int hg_scanner_300::set_sleep_time(SANE_Power* setime) +{ + if (!setime) + { + return HG_ERR_NO_DATA; + } + + int ret = HG_ERR_OK, + time = *setime; + USBCB usbcb = {SET_SLEEP_TIME,time, 0}; + + ret = writeusb(usbcb); + + return ret; +} + +int hg_scanner_300::get_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::set_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::get_scanner_paperon(SANE_Bool* paperon) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_PAPER_STATUS, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + io_->set_timeout(500); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0) + ret = HG_ERR_DEVICE_NO_PAPER; + else + ret = HG_ERR_OK; + if (paperon) + { + *paperon = usbcb.u32_Data; + } + return ret; +} +int hg_scanner_300::set_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::get_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::get_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::set_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_300::get_scan_is_sleep() +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {0x100, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + + io_->set_timeout(200); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0x10) + { + return HG_ERR_OK; + } + else if (usbcb.u32_Data == 0x100) + { + return HG_ERR_DEVICE_SLEEPING; + } +} + + + + + + +///姝ゆ璁惧鏃犳鍔熻兘 +int hg_scanner_300::on_staple_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner_300::on_skew_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner_300::on_skew_check_level_changed(int& check) +{ + return HG_ERR_OK; +} + +int hg_scanner_300::agreement() +{ + int ret = HG_ERR_OK; + + if (image_prc_param_.bits.rid_color != RID_COLOR_NONE) + { + dsp_config.params.isColor = 1; + } + + ret = writedown_device_configuration(true); + io_->set_timeout(200); + + return ret; +} \ No newline at end of file diff --git a/hgdriver/hgdev/hg_scanner_300.h b/hgdriver/hgdev/hg_scanner_300.h new file mode 100644 index 0000000..1a07e1b --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_300.h @@ -0,0 +1,89 @@ +锘#pragma once + +// hg_scanner is the base class of kinds of scanners +// +// created on 2022-01-30 +// +#include +#include +#include +#include + +#include "hg_scanner.h" +#include "../../sdk/hginclude/hg_log.h" +#include "PaperSize.h" + + +class hg_scanner_300 : public hg_scanner +{ +protected: + virtual int on_scanner_closing(bool force) override; + virtual void thread_handle_usb_read(void) override; + virtual void image_process(std::shared_ptr>& buff) override; + + + virtual int on_color_mode_changed(int& color_mode); // COLOR_MODE_xxx + virtual int on_paper_changed(int& paper); // PAPER_xxx + virtual int on_paper_check_changed(bool& check); + virtual int on_resolution_changed(int& dpi); + virtual int on_ultrasonic_check_changed(bool& check); + + virtual int on_staple_check_changed(bool& check); + virtual int on_skew_check_changed(bool& check); + virtual int on_skew_check_level_changed(int& check); + +public: + hg_scanner_300(const char* dev_name,int pid, usb_io* io); + ~hg_scanner_300(); + +public: + virtual int start(void)override; + virtual int stop(void)override; + void init_setting_map(int* setting_map, int count)override; + +private: + int agreement(); + int agreement(TwSS tw,int align); + int initdevice(); + int writeusb(USBCB &usb); + int readusb(USBCB &usb); + int pop_image(void); + int get_scanner_status(USBCB &usb); + int get_img_data(std::shared_ptr> &imagedata); + int writedown_device_configuration(bool type =false,HGSCANCONF_G400 *d = NULL); + void writedown_image_configuration(void); + void printf_devconfig(HGSCANCONF_G400 *d = NULL); + virtual std::string get_firmware_version() override; + virtual std::string get_serial_num(void) override; + +private: + int pid_; + std::vector savestatus_; + HGSCANCONF_G400 dsp_config; + Device::PaperSize papersize; + +public: + virtual int set_leaflet_scan(void);//鍗曞紶鎵弿 + virtual int get_abuot_info(void);//鑾峰彇杞欢鍏充簬淇℃伅 + virtual int restore_default_setting(void);//鎭㈠榛樿璁剧疆 + virtual int set_final_image_format(void); // 璁剧疆鍥惧儚澶勭悊鏈缁堣緭鍑猴紙final()锛夌殑鍥惧儚鏁版嵁鏍煎紡 + + virtual int get_compression_format(void);//鑾峰彇鏀寔鐨勫帇缂╂牸寮 + virtual int set_compression_format(void);//璁剧疆鍥惧儚鏁版嵁鏈缁堣緭鍑虹殑鍘嬬缉鏍煎紡 + virtual int set_auto_color_type(void);// 璁剧疆鑷姩鍖归厤棰滆壊妯″紡 + + virtual int get_device_code(void);//鑾峰彇璁惧缂栫爜 + virtual int get_sleep_time(SANE_Power* setime = NULL);//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛 + virtual int set_sleep_time(SANE_Power* setime);//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛 + + virtual int get_dogear_distance(void);//鑾峰彇鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int set_dogear_distance(void);// 璁剧疆鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int get_scanner_paperon(SANE_Bool* paperon=NULL);//鑾峰彇璁惧鏈夋棤绾稿紶 + virtual int set_scan_when_paper_on(void);//鑾峰彇鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_when_paper_on(void);//璁剧疆鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_with_hole(void);// 鑾峰彇鏄惁涓哄甫瀛旀壂鎻 + virtual int set_scan_with_hole(void);// 璁剧疆鏄惁涓哄甫瀛旀壂鎻 + + virtual int get_scan_is_sleep(void);//鑾峰彇璁惧鏄惁浼戠湢褰撲腑 + +}; diff --git a/hgdriver/hgdev/hg_scanner_400.cpp b/hgdriver/hgdev/hg_scanner_400.cpp new file mode 100644 index 0000000..be6fa39 --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_400.cpp @@ -0,0 +1,1264 @@ +锘#include "hg_scanner_400.h" +#include "../../sdk/hginclude/hg_log.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + + +static std::string jsontext("{\"device_type\":\"G400\",\"option_count\":44,\"1\":{\"category\":\"base\",\"name\":\"cfg-1\",\"title\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"desc\":\"\\u6062\\u590d\\u9ed8\\u8ba4\\u8bbe\\u7f6e\",\"type\":\"button\",\"cur\":\"button\",\"default\":\"button\",\"size\":0},\"2\":{\"category\":\"advanced\",\"name\":\"cfg-2\",\"title\":\"\\u5e2e\\u52a9\",\"desc\":\"\\u663e\\u793a\\u8f6f\\u4ef6\\u5e2e\\u52a9\\u6587\\u6863\",\"type\":\"button\",\"cur\":\"true\",\"default\":\"true\",\"size\":4},\"3\":{\"category\":\"base\",\"name\":\"grp-1\",\"title\":\"\\u57fa\\u672c\\u8bbe\\u7f6e\",\"type\":\"group\"},\"4\":{\"category\":\"base\",\"name\":\"cfg-4\",\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6e\\u989c\\u8272\\u4f4d\\u6df1\",\"type\":\"string\",\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"size\":24,\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"]},\"5\":{\"category\":\"base\",\"name\":\"cfg-5\",\"title\":\"\\u7070\\u5ea6\\u6216\\u9ed1\\u767d\\u56fe\\u50cf - \\u9664\\u8272\",\"desc\":\"\\u9664\\u53bb\\u56fe\\u50cf\\u5f69\\u8272\",\"type\":\"string\",\"cur\":\"\\u4e0d\\u9664\\u8272\",\"default\":\"\\u4e0d\\u9664\\u8272\",\"size\":16,\"range\":[\"\\u4e0d\\u9664\\u8272\",\"\\u9664\\u7ea2\\u8272\",\"\\u9664\\u7eff\\u8272\",\"\\u9664\\u84dd\\u8272\",\"\\u7ea2\\u8272\\u589e\\u5f3a\",\"\\u7eff\\u8272\\u589e\\u5f3a\",\"\\u84dd\\u8272\\u589e\\u5f3a\"],\"depend_or\":[\"4==256\\u7ea7\\u7070\\u5ea6\",\"4==\\u9ed1\\u767d\"]},\"6\":{\"category\":\"base\",\"name\":\"cfg-6\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u591a\\u6d41\\u8f93\\u51fa\\u9664\\u7ea2\",\"desc\":\"\\u591a\\u901a\\u9053\\u8f93\\u51fa\\u4e2d\\uff0c\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"7\":{\"category\":\"base\",\"name\":\"cfg-7\",\"title\":\"24\\u4f4d\\u5f69\\u8272\\u56fe\\u50cf - \\u7b54\\u9898\\u5361\\u9664\\u7ea2\",\"desc\":\"\\u7b54\\u9898\\u5361\\u626b\\u63cf\\u4e2d\\u53bb\\u9664\\u7ea2\\u8272\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"8\":{\"category\":\"base\",\"name\":\"cfg-8\",\"title\":\"\\u80cc\\u666f\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u5f69\\u8272\\u56fe\\u50cf\\u80cc\\u666f\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==24\\u4f4d\\u5f69\\u8272\"]},\"9\":{\"category\":\"base\",\"name\":\"cfg-9\",\"title\":\" \\u80cc\\u666f\\u8272\\u5f69\\u6d6e\\u52a8\\u8303\\u56f4\",\"desc\":\"\\u8bbe\\u5b9a\\u80cc\\u666f\\u8272\\u5f69\\u7684\\u6d6e\\u52a8\\u8303\\u56f4\\uff0c\\u5728\\u8be5\\u8303\\u56f4\\u5185\\u7684\\u90fd\\u5f53\\u4f5c\\u80cc\\u666f\\u79fb\\u9664\",\"type\":\"int\",\"cur\":10,\"default\":10,\"size\":4,\"range\":{\"min\":1,\"max\":40},\"depend_or\":[\"8==true\"]},\"10\":{\"category\":\"base\",\"name\":\"cfg-10\",\"title\":\"\\u9ed1\\u767d\\u56fe\\u50cf\\u566a\\u70b9\\u4f18\\u5316\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"4==\\u9ed1\\u767d\"]},\"11\":{\"category\":\"base\",\"name\":\"cfg-11\",\"title\":\" \\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"desc\":\"\\u566a\\u70b9\\u4f18\\u5316\\u5c3a\\u5bf8\",\"type\":\"int\",\"cur\":30,\"default\":30,\"size\":4,\"range\":{\"min\":10,\"max\":50},\"depend_or\":[\"10==true\"]},\"12\":{\"category\":\"base\",\"name\":\"cfg-12\",\"title\":\"\\u7eb8\\u5f20\\u5c3a\\u5bf8\",\"desc\":\"\\u8bbe\\u7f6e\\u7eb8\\u5f20\\u5927\\u5c0f\",\"type\":\"string\",\"cur\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"default\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"size\":44,\"range\":[\"A3\",\"A4\",\"A4\\u6a2a\\u5411\",\"A5\",\"A5\\u6a2a\\u5411\",\"A6\",\"A6\\u6a2a\\u5411\",\"B4\",\"B5\",\"B5\\u6a2a\\u5411\",\"B6\",\"B6\\u6a2a\\u5411\",\"Letter\",\"Letter\\u6a2a\\u5411\",\"Double Letter\",\"LEGAL\",\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\"]},\"13\":{\"category\":\"base\",\"name\":\"cfg-13\",\"title\":\"\\u626b\\u63cf\\u9875\\u9762\",\"desc\":\"\\u8bbe\\u7f6e\\u9875\\u9762\\u626b\\u63cf\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u53cc\\u9762\",\"default\":\"\\u53cc\\u9762\",\"size\":50,\"range\":[\"\\u5355\\u9762\",\"\\u53cc\\u9762\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\",\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\",\"\\u5bf9\\u6298\"]},\"14\":{\"category\":\"base\",\"name\":\"cfg-14\",\"title\":\" \\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7075\\u654f\\u5ea6\",\"desc\":\"\\u7075\\u654f\\u5ea6\\u8d8a\\u9ad8\\uff0c\\u5219\\u8d8a\\u5bb9\\u6613\\u8df3\\u8fc7\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":1,\"max\":100},\"depend_or\":[\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u53d1\\u7968\\u7eb8\\uff09\",\"13==\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\uff08\\u901a\\u7528\\uff09\"]},\"15\":{\"category\":\"base\",\"name\":\"cfg-15\",\"title\":\"\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u626b\\u63cf\\u4eea\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":{\"min\":100,\"max\":300}},\"16\":{\"category\":\"base\",\"name\":\"cfg-16\",\"title\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"desc\":\"\\u4ea4\\u6362\\u6b63\\u53cd\\u9762\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\"]},\"17\":{\"category\":\"base\",\"name\":\"cfg-17\",\"title\":\"\\u56fe\\u50cf\\u62c6\\u5206\",\"desc\":\"\\u81ea\\u52a8\\u62c6\\u5206\\u56fe\\u50cf\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"18\":{\"category\":\"base\",\"name\":\"cfg-18\",\"title\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"desc\":\"\\u81ea\\u52a8\\u7ea0\\u504f\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4,\"depend_or\":[\"13!=\\u5bf9\\u6298\"]},\"19\":{\"category\":\"base\",\"name\":\"cfg-19\",\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\",\"desc\":\"\\u79fb\\u9664\\u7eb8\\u5f20\\u4e2d\\u7684\\u7a7f\\u5b54\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"20\":{\"category\":\"base\",\"name\":\"cfg-20\",\"title\":\" \\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.010000,\"max\":0.500000},\"depend_or\":[\"19==true\"]},\"21\":{\"category\":\"base\",\"name\":\"grp-2\",\"title\":\"\\u4eae\\u5ea6\",\"type\":\"group\"},\"22\":{\"category\":\"base\",\"name\":\"cfg-22\",\"title\":\"\\u4eae\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4eae\\u5ea6\",\"type\":\"int\",\"cur\":128,\"default\":128,\"size\":4,\"range\":{\"min\":1,\"max\":255}},\"23\":{\"category\":\"base\",\"name\":\"cfg-23\",\"title\":\"\\u5bf9\\u6bd4\\u5ea6\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u5bf9\\u6bd4\\u5ea6\",\"type\":\"int\",\"cur\":4,\"default\":4,\"size\":4,\"range\":{\"min\":1,\"max\":7}},\"24\":{\"category\":\"base\",\"name\":\"cfg-24\",\"title\":\"\\u4f3d\\u739b\",\"desc\":\"\\u8c03\\u6574\\u56fe\\u7247\\u4f3d\\u739b\\u503c\",\"type\":\"float\",\"cur\":1.000000,\"default\":1.000000,\"size\":4,\"range\":{\"min\":1.000000,\"max\":5.000000}},\"25\":{\"category\":\"base\",\"name\":\"grp-3\",\"title\":\"\\u56fe\\u50cf\\u5904\\u7406\",\"type\":\"group\"},\"26\":{\"category\":\"base\",\"name\":\"cfg-26\",\"title\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"desc\":\"\\u9510\\u5316\\u4e0e\\u6a21\\u7cca\",\"type\":\"string\",\"cur\":\"\\u65e0\",\"default\":\"\\u65e0\",\"size\":20,\"range\":[\"\\u65e0\",\"\\u9510\\u5316\",\"\\u8fdb\\u4e00\\u6b65\\u9510\\u5316\",\"\\u6a21\\u7cca\",\"\\u8fdb\\u4e00\\u6b65\\u6a21\\u7cca\"]},\"27\":{\"category\":\"base\",\"name\":\"cfg-27\",\"title\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"desc\":\"\\u6d88\\u9664\\u9ed1\\u6846\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"28\":{\"category\":\"base\",\"name\":\"cfg-28\",\"title\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"desc\":\"\\u6df1\\u8272\\u6837\\u5f20\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5bf9\\u6298\",\"27!=true\",\"12!=\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12!=\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18!=true\"]},\"29\":{\"category\":\"advanced\",\"name\":\"cfg-29\",\"title\":\"\\u9608\\u503c\",\"desc\":\"\\u9608\\u503c\",\"type\":\"int\",\"cur\":40,\"default\":40,\"size\":4,\"range\":{\"min\":30,\"max\":50},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"30\":{\"category\":\"advanced\",\"name\":\"cfg-30\",\"title\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"desc\":\"\\u80cc\\u666f\\u6297\\u566a\\u7b49\\u7ea7\",\"type\":\"int\",\"cur\":8,\"default\":8,\"size\":4,\"range\":{\"min\":1,\"max\":20},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\"]},\"31\":{\"category\":\"advanced\",\"name\":\"cfg-31\",\"title\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"desc\":\"\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"type\":\"int\",\"cur\":5,\"default\":5,\"size\":4,\"range\":{\"min\":5,\"max\":30},\"depend_or\":[\"27==true\",\"12==\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"18==true\",\"12==\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\"]},\"32\":{\"category\":\"advanced\",\"name\":\"cfg-32\",\"title\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"desc\":\"\\u80cc\\u666f\\u586b\\u5145\\u65b9\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"default\":\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"size\":40,\"range\":[\"\\u51f8\\u591a\\u8fb9\\u5f62\",\"\\u51f9\\u591a\\u8fb9\\u5f62\"],\"depend_or\":[\"27==true\"]},\"33\":{\"category\":\"base\",\"name\":\"cfg-33\",\"title\":\"\\u9632\\u6b62\\u6e17\\u900f\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"34\":{\"category\":\"base\",\"name\":\"cfg-34\",\"title\":\" \\u9632\\u6b62\\u6e17\\u900f\\u7b49\\u7ea7\",\"desc\":\"\",\"type\":\"string\",\"cur\":\"\\u8f83\\u5f31\",\"default\":\"\\u8f83\\u5f31\",\"size\":12,\"range\":[\"\\u8f83\\u5f31\",\"\\u5f31\",\"\\u4e00\\u822c\",\"\\u5f3a\",\"\\u8f83\\u5f3a\"],\"depend_or\":[\"33==true\"]},\"35\":{\"category\":\"base\",\"name\":\"cfg-35\",\"title\":\"\\u53bb\\u9664\\u6469\\u5c14\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"36\":{\"category\":\"base\",\"name\":\"cfg-36\",\"title\":\"\\u9519\\u8bef\\u6269\\u6563\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"37\":{\"category\":\"base\",\"name\":\"cfg-37\",\"title\":\"\\u9664\\u7f51\\u7eb9\",\"desc\":\"\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"38\":{\"category\":\"base\",\"name\":\"grp-4\",\"title\":\"\\u9001\\u7eb8\\u65b9\\u5f0f\\u8bbe\\u7f6e\",\"type\":\"group\"},\"39\":{\"category\":\"base\",\"name\":\"cfg-39\",\"title\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\",\"desc\":\"\\u8d85\\u58f0\\u6ce2\\u68c0\\u6d4b\\u9001\\u7eb8\\u72b6\\u6001\",\"type\":\"bool\",\"cur\":true,\"default\":true,\"size\":4},\"40\":{\"category\":\"base\",\"name\":\"cfg-40\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u626b\\u63cf\\u7eb8\\u5f20\\u6570\\u91cf\",\"type\":\"string\",\"cur\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"default\":\"\\u8fde\\u7eed\\u626b\\u63cf\",\"size\":24,\"range\":[\"\\u8fde\\u7eed\\u626b\\u63cf\",\"\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"41\":{\"category\":\"base\",\"name\":\"cfg-41\",\"title\":\" \\u626b\\u63cf\\u6570\\u91cf\",\"desc\":\"\\u626b\\u63cf\\u6307\\u5b9a\\u6570\\u91cf\",\"type\":\"int\",\"cur\":1,\"default\":1,\"size\":4,\"depend_or\":[\"40==\\u626b\\u63cf\\u6307\\u5b9a\\u5f20\\u6570\"]},\"42\":{\"category\":\"base\",\"name\":\"cfg-42\",\"title\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"desc\":\"\\u6587\\u7a3f\\u65b9\\u5411\",\"type\":\"string\",\"cur\":\"0\\u00b0\",\"default\":\"0\\u00b0\",\"size\":16,\"range\":[\"0\\u00b0\",\"90\\u00b0\",\"180\\u00b0\",\"-90\\u00b0\"]},\"43\":{\"category\":\"base\",\"name\":\"cfg-43\",\"title\":\"\\u80cc\\u9762\\u65cb\\u8f6c180\\u00b0\",\"desc\":\"\\u80cc\\u9762\\u626b\\u63cf\\u7684\\u56fe\\u50cf\\u65cb\\u8f6c180\\u00b0\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_and\":[\"13!=\\u5355\\u9762\",\"13!=\\u5bf9\\u6298\",\"42!=\\u81ea\\u52a8\\u6587\\u672c\\u65b9\\u5411\\u8bc6\\u522b\\u00b0\"]}}"); +namespace settingsdsp_400 +{ + hg_err device_status_to_hg_err(int usbdata) + { + hg_err code = HG_ERR_NO_DATA; + switch (usbdata) + { + case HAVE_IMAGE: + code = HG_ERR_OK; + break; + case STOP_SCAN: + code = HG_ERR_DEVICE_STOPPED; + break; + case COUNT_MODE: + code = HG_ERR_DEVICE_COUNT_MODE; + break; + case NO_FEED: + code = HG_ERR_DEVICE_NO_PAPER; + break; + case OPEN_COVER: + code = HG_ERR_DEVICE_COVER_OPENNED; + break; + case FEED_IN_ERROR: + code = HG_ERR_DEVICE_FEEDING_PAPER; + break; + case PAPER_JAM: + code = HG_ERR_DEVICE_PAPER_JAMMED; + break; + case DETECT_DOUBLE_FEED: + code = HG_ERR_DEVICE_DOUBLE_FEEDING; + break; + case DETECT_STAPLE: + code = HG_ERR_DEVICE_STAPLE_ON; + break; + case PAPER_SKEW: + code = HG_ERR_DEVICE_PAPER_SKEW; + break; + case HARDWARE_ERROR: + code = HG_ERR_DEVICE_NOT_SUPPORT; + break; + case PC_SCAN_BUSY_or_ERROR: + code = HG_ERR_DEVICE_PC_BUSY; + break; + case SIZE_ERROR: + code = HG_ERR_DEVICE_SIZE_CHECK; + break; + default: + code = HG_ERR_NO_DATA; + break; + } + + return code; + } + + + static struct + { + int paper; + TwSS type; + int dev_value; + }paper_map[] = + { + {PAPER_A3, TwSS::A3, G400_A3}, + {PAPER_A4, TwSS::A4, G400_A4}, + {PAPER_A4_LATERAL, TwSS::A4, G400_A4R}, + {PAPER_A5, TwSS::A5, G400_A5}, + {PAPER_A5_LATERAL, TwSS::A5, G400_A5R}, + {PAPER_A6, TwSS::A6, G400_A6}, + {PAPER_A6_LATERAL, TwSS::A6, G400_A6R}, + {PAPER_B4, TwSS::B4, G400_B4}, + {PAPER_B5, TwSS::B5, G400_B5}, + {PAPER_B5_LATERAL, TwSS::B5, G400_B5R}, + {PAPER_B6, TwSS::B6, G400_B6}, + {PAPER_B6_LATERAL, TwSS::B6, G400_B6R}, + {PAPER_LETTER, TwSS::USLetter, G400_LETTER}, + {PAPER_LETTER_LATERAL, TwSS::USLetter, G400_LETTERR}, + {PAPER_DOUBLE_LETTER, TwSS::USLedger, G400_DOUBLELETTER}, + {PAPER_LEGAL, TwSS::USLegal, G400_LEGAL}, + {PAPER_AUTO_MATCH, TwSS::None, G400_A3}, + {PAPER_MAX_SIZE_CLIP, TwSS::USStatement, G400_MAXSIZE}, + {PAPER_MAX_SIZE, TwSS::MaxSize, G400_MAXSIZE} + }; + int match_best_paper(int& paper, bool* exact, TwSS* type) + { + int ind = 0; + bool good = true, * r = exact ? exact : &good; + for (int i = 0; i < ARRAY_SIZE(paper_map); ++i) + { + if (paper == paper_map[i].paper) + { + ind = i; + break; + } + } + + if (paper == paper_map[ind].paper) + *r = true; + else + *r = false; + HG_VLOG_MINI_3(HG_LOG_LEVEL_DEBUG_INFO, "Paper '%s' index = %d, device value = %d\n", paper_string(paper).c_str(), ind, paper_map[ind].dev_value); + paper = paper_map[ind].paper; + if (type) + *type = paper_map[ind].type; + + return ind; + } +} + + +enum hg_239_setting_item +{ + HG_239_SETTING_RESTORE = 1, // 鎭㈠榛樿璁剧疆 + HG_239_SETTING_HELP, // 甯姪 + + HG__239_SETTING_GROUP_1, + + HG_239_SETTING_COLOR_MODE, // 棰滆壊妯″紡 + + HG_239_SETTING_ERASE_COLOR, // 闄よ壊 + HG_239_SETTING_ERASE_MULTI_RED, // 澶氭祦杈撳嚭闄ょ孩 + HG_239_SETTING_ERASE_ANSWER_RED, // 绛旈鍗¢櫎绾 + HG_239_SETTING_ERASE_BACKGROUND, // 绉婚櫎鑳屾櫙 + HG_239_SETTING_ERASE_BKG_RANGE, // 绉婚櫎鑳屾櫙鑼冨洿 + HG_239_SETTING_NOISE, // 榛戠櫧鍥惧儚鍣偣浼樺寲 + HG_239_SETTING_NOISE_SIZE, // 鍣偣浼樺寲灏哄 + HG_239_SETTING_PAPER_SIZE, // 绾稿紶灏哄 + //HG_239_SETTING_PAPER_SIZE_CHECK, // 灏哄妫娴 + HG_239_SETTING_PAGE, // 鎵弿椤甸潰 + HG_239_SETTING_SKIP_NILL, // 璺宠繃绌虹櫧椤电伒鏁忓害 + HG_239_SETTING_RESOLUTION, // 鍒嗚鲸鐜 + HG_239_SETTING_REVERSE, // 浜ゆ崲姝e弽闈 + HG_239_SETTING_SPLIT, // 鍥惧儚鎷嗗垎 + HG_239_SETTING_CORRECT, // 鑷姩绾犲亸 + HG_239_SETTING_ERASE_HOLE, // 绌垮瓟绉婚櫎 + HG_239_SETTING_HOLE_SEARCH, // 绌垮瓟鎼滅储鑼冨洿 + + HG__239_SETTING_GROUP_2, + + HG_239_SETTING_LIGHT, // 浜害 + HG_239_SETTING_CONTRAST, // 瀵规瘮搴 + HG_239_SETTING_GAMMA, // 浼界帥 + + HG__239_SETTING_GROUP_3, + + HG_239_SETTING_SHARP, // 閿愬寲涓庢ā绯 + HG_239_SETTING_ERASE_BLACK_BORDER, // 娑堥櫎榛戞 + HG_239_SETTING_SAMPLE, // 娣辫壊鏍峰紶 + HG_239_SETTING_THRESHOLD, // 闃堝 + HG_239_SETTING_ANTI_NOISE, // 鑳屾櫙鎶楀櫔绛夌骇 + HG_239_SETTING_MARGIN, // 杈圭紭缂╄繘 + HG_239_SETTING_FILLING, // 鑳屾櫙濉厖鏂瑰紡 + HG_239_SETTING_PERMEATE, // 闃叉娓楅 + HG_239_SETTING_PERMEATE_lv, // 闃叉娓楅忕瓑绾 + HG_239_SETTING_REMOVE_MORR, // 鍘婚櫎鎽╁皵绾 + HG_239_SETTING_ERROR_EXTENTION, // 閿欒鎵╂暎 + HG_239_SETTING_REMOVE_TXTTURE, // 闄ょ綉绾 + + HG__239_SETTING_GROUP_4, + + HG_239_SETTING_ULTRASONIC, // 瓒呭0娉㈡娴 + HG_239_SETTING_SCAN_METHOD, // 杩炵画鎵弿鎴栨壂鎻忔寚瀹氬紶鏁 + HG_239_SETTING_SCAN_COUNT, // 鎵弿鎸囧畾鏁伴噺 + HG_239_SETTING_DIRECTION, // 鏂囩ǹ鏂瑰悜 鍒犻櫎鏂囩ǹ鑷姩璇嗗埆 + HG_239_SETTING_ROTATE, // 鑳岄潰鏃嬭浆180掳 + + HG_239_SETTING_END, + + //鏆傛椂灞忚斀 + HG_239_SETTING_MULTI_OUT = 500, // 澶氭祦杈撳嚭 +}; + + + +hg_scanner_400::hg_scanner_400(const char* dev_name,int pid, usb_io* io) : hg_scanner(G100Serial, dev_name, io),pid_(pid) +{ + string fw = get_firmware_version(); + + initdevice(); + init_setting_map(setting_map_, ARRAY_SIZE(setting_map_));//浼樺厛鍒濆鍖 + init_settings(jsontext.c_str()); + + printf_devconfig(); +} +hg_scanner_400::~hg_scanner_400() +{} + + +void hg_scanner_400::init_setting_map(int* setting_map, int count) +{ + setting_map[HG_BASE_SETTING_INDEX_RESTORE_DEFAULT_SETTINGS] = HG_239_SETTING_RESTORE; + setting_map[HG_BASE_SETTING_INDEX_HELP] = HG_239_SETTING_HELP; + + setting_map[HG_BASE_SETTING_INDEX_COLOR_MODE] = HG_239_SETTING_COLOR_MODE; + setting_map[HG_BASE_SETTING_MULTI_OUT] = HG_239_SETTING_MULTI_OUT; + setting_map[HG_BASE_SETTING_INDEX_ERASE_COLOR] = HG_239_SETTING_ERASE_COLOR; + setting_map[HG_BASE_SETTING_INDEX_ERASE_MULTI_OUT_RED] = HG_239_SETTING_ERASE_MULTI_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_ANSWER_RED] = HG_239_SETTING_ERASE_ANSWER_RED; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND] = HG_239_SETTING_ERASE_BACKGROUND; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BACKGROUND_RANGE] = HG_239_SETTING_ERASE_BKG_RANGE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE] = HG_239_SETTING_NOISE; + setting_map[HG_BASE_SETTING_INDEX_NOISE_OPTIMIZE_SIZE] = HG_239_SETTING_NOISE_SIZE; + setting_map[HG_BASE_SETTING_INDEX_PAPER] = HG_239_SETTING_PAPER_SIZE; + //setting_map[HG_BASE_SETTING_INDEX_PAPER_SIZE_CHECK] = HG_239_SETTING_PAPER_SIZE_CHECK; + setting_map[HG_BASE_SETTING_INDEX_PAGE] = HG_239_SETTING_PAGE; + setting_map[HG_BASE_SETTING_INDEX_PAGE_OMIT_EMPTY_LEVEL] = HG_239_SETTING_SKIP_NILL; + setting_map[HG_BASE_SETTING_INDEX_RESOLUTION] = HG_239_SETTING_RESOLUTION; + setting_map[HG_BASE_SETTING_INDEX_EXCHANGE] = HG_239_SETTING_REVERSE; + setting_map[HG_BASE_SETTING_INDEX_SPLIT] = HG_239_SETTING_SPLIT; + setting_map[HG_BASE_SETTING_INDEX_AUTO_CORRECT] = HG_239_SETTING_CORRECT; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE] = HG_239_SETTING_ERASE_HOLE; + setting_map[HG_BASE_SETTING_INDEX_RID_HOLE_RANGE] = HG_239_SETTING_HOLE_SEARCH; + + setting_map[HG_BASE_SETTING_INDEX_BRIGHT] = HG_239_SETTING_LIGHT; + setting_map[HG_BASE_SETTING_INDEX_CONTRAST] = HG_239_SETTING_CONTRAST; + setting_map[HG_BASE_SETTING_INDEX_GAMMA] = HG_239_SETTING_GAMMA; + + setting_map[HG_BASE_SETTING_INDEX_SHARPEN] = HG_239_SETTING_SHARP; + setting_map[HG_BASE_SETTING_INDEX_DARK_SAMPLE] = HG_239_SETTING_SAMPLE; + setting_map[HG_BASE_SETTING_INDEX_ERASE_BLACK_FRAME] = HG_239_SETTING_ERASE_BLACK_BORDER; + setting_map[HG_BASE_SETTING_INDEX_THRESHOLD] = HG_239_SETTING_THRESHOLD; + setting_map[HG_BASE_SETTING_INDEX_ANTI_NOISE_LEVEL] = HG_239_SETTING_ANTI_NOISE; + setting_map[HG_BASE_SETTING_INDEX_MARGIN] = HG_239_SETTING_MARGIN; + setting_map[HG_BASE_SETTING_INDEX_FILL_BACKGROUND] = HG_239_SETTING_FILLING; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE] = HG_239_SETTING_PERMEATE; + setting_map[HG_BASE_SETTING_INDEX_PERMEATE_LV] = HG_239_SETTING_PERMEATE_lv; + setting_map[HG_BASE_SETTING_REMOVE_MORR] = HG_239_SETTING_REMOVE_MORR; + setting_map[HG_BASE_SETTING_ERROR_EXTENTION] = HG_239_SETTING_ERROR_EXTENTION; + setting_map[HG_BASE_SETTING_REMOVE_TXTTURE] = HG_239_SETTING_REMOVE_TXTTURE; + + setting_map[HG_BASE_SETTING_INDEX_ULTRASONIC_CHECK] = HG_239_SETTING_ULTRASONIC; + setting_map[HG_BASE_SETTING_INDEX_SCAN_MODE] = HG_239_SETTING_SCAN_METHOD; + setting_map[HG_BASE_SETTING_INDEX_SCAN_COUNT] = HG_239_SETTING_SCAN_COUNT; + setting_map[HG_BASE_SETTING_INDEX_TEXT_DIRECTION] = HG_239_SETTING_DIRECTION; + setting_map[HG_BASE_SETTING_INDEX_ROTATE_BKG_180] = HG_239_SETTING_ROTATE; + + + } + + +int hg_scanner_400::on_scanner_closing(bool force) +{ + return HG_ERR_OK; +} +void hg_scanner_400::thread_handle_usb_read(void) +{ + int ret = HG_ERR_OK, + status = HG_ERR_OK; + StopWatch sw; + + std::vector savestatus_; + while (run_) + { + if (user_cancel_) + break; + + USBCB usb={0}; + ret = get_scanner_status(usb); + //printf("usb.u32_Data = %d ret = %d\r\n",usb.u32_Data,ret); + ////濡傛灉璁惧鍑虹幇鍗$焊 鎴栬呭弻寮犵瓑璁惧淇℃伅闂锛岄渶瑕佺瓑鍒拌澶囪繘琛屽彂閫乻top鎵嶈兘鍋滄銆 鎵浠ュ缁堜互 "鍋滄" 娑堟伅涓虹粨鏉熶俊鍙 + ////濡傛灉鏈夊浘鐨勬儏鍐典笅锛屽苟涓斿崱绾告垨鍙屽紶绛夛紝璁惧浼氬厛鍙戦佸崱绾稿弻寮犱俊鎭傛墍浠ユ帴鍙楀埌閿欒淇℃伅涔嬪悗锛屼粛闇瑕佹妸鍥惧儚鍙栧嚭鏉ャ + + if (ret == HG_ERR_DEVICE_STOPPED) + { + if (!savestatus_.empty()) + { + status_ = savestatus_[0]; + } + else + { + status_ = HG_ERR_OK; + } + savestatus_.clear(); + break; + } + if (ret != HG_ERR_OK && ret != HG_ERR_DEVICE_STOPPED && ret != HG_ERR_NO_DATA) + { + savestatus_.push_back(ret); + } + if (sw.elapsed_s() > 30)//闃叉鐘舵佷俊鎭竴鐩村彇涓嶄笂鏉ュ鑷村崱姝 + { + if (!savestatus_.empty()) + { + status_ = savestatus_[0]; + } + else + { + status_ = HG_ERR_TIMEOUT; + } + savestatus_.clear(); + hg_log::log(HG_LOG_LEVEL_WARNING, "get status timeout,get image out\n"); + break; + } + + if (ret == HG_ERR_OK && usb.u32_Count > 0) + { + int totalNum = usb.u32_Count; + std::shared_ptr> imagedata(new std::vector (totalNum)); + + ret = get_img_data(imagedata); + io_->set_timeout(200); + + if (ret == HG_ERR_OK) + { + ret = pop_image(); + sw.reset(); + if (ret != HG_ERR_OK) + { + status_ = ret; + break; + } + } + else + { + status_ = ret; + break; + } + } + this_thread::sleep_for(chrono::milliseconds(10)); + } +} + +int hg_scanner_400::start(void) +{ + int ret = HG_ERR_OK, + count = -1; + user_cancel_ = false; + + ret = writedown_device_configuration(true); + if (ret == HG_ERR_OK) + writedown_image_configuration(); + else + { + status_ = ret; + return ret; + } + + printf_devconfig(); + ret = get_scan_is_sleep(); + if (ret == HG_ERR_DEVICE_SLEEPING) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_SLEEPING, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_SLEEPING); + return ret; + } + ret = get_scanner_paperon(); + if (ret == HG_ERR_DEVICE_NO_PAPER) + { + status_ = ret; + notify_ui_working_status(STATU_DESC_HG_ERR_DEVICE_NO_PAPER, SANE_EVENT_SCAN_FINISHED, status_); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start status is(%s)\n", STATU_DESC_HG_ERR_DEVICE_NO_PAPER); + return ret; + } + + USBCB usb = {START_COMMAND, img_conf_.scannum, 0}; + ret = writeusb(usb); + io_->set_timeout(1000); + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "device start is.(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + if(ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_BUSY; + wait_usb_.notify(); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + else + status_ = ret; + + return ret; +} +int hg_scanner_400::stop(void) +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {STOP, 0, 0}; + ret = writeusb(usbcb); + + if (status_ == HG_ERR_DEVICE_BUSY) + { + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "stop !but device is busy ,so sleep 2s(%s)\n", hg_scanner::strerr((hg_err)status_).c_str()); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + } + //user_cancel_ = true; + if (ret == HG_ERR_OK) + { + status_ = HG_ERR_DEVICE_STOPPED; + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "stop device:(%s)\n", hg_scanner::strerr((hg_err)status_).c_str()); + } + notify_ui_working_status("鍙栨秷鎵弿", SANE_EVENT_STATUS, status_); + + return status_; +} +int hg_scanner_400::writeusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(usb); + + ret = io_->write_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_400::readusb(USBCB &usb) +{ + std::lock_guard lock(io_lock_); + + int ret = HG_ERR_OK; + int len = sizeof(USBCB); + + ret = io_->read_bulk(&usb,&len); + if (ret) + { + status_ = ret; + } + + return ret; +} +int hg_scanner_400::pop_image() +{ + int ret = HG_ERR_OK; + + USBCB usbcb = {POP_IMAGE, 0, 0}; + ret = writeusb(usbcb); + + return ret; +} + +int hg_scanner_400::get_scanner_status(USBCB &usb) +{ + int ret = HG_ERR_OK; + + usb = {GET_DSP_STATUS, 0, 0}; + + ret = writeusb(usb); + if (ret != HG_ERR_OK) + return ret; + + io_->set_timeout(500); + + ret = readusb(usb); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + hg_err code = settingsdsp_400::device_status_to_hg_err(usb.u32_Data); + return code ; +} +int hg_scanner_400::get_img_data(std::shared_ptr> &imagedata) +{ + int total = imagedata->size(), + ret = HG_ERR_OK, + index = 0, + block = total; + + USBCB usb{GET_IMAGE, 0, total}; + ret = writeusb(usb); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + io_->set_timeout(500); + + printf_devconfig(); + + while (total > 0) + { + block = 512 * 1024; + + if (total < block) + block = total; + + ret = io_->read_bulk(imagedata->data() + index,&block); + + if (ret != HG_ERR_OK) + { + status_ = ret; + return ret; + } + index += block; + total -= block; + } + if (ret == HG_ERR_OK) + { + ret = save_usb_data(imagedata); + } + status_ = ret; + + return ret; +} + +void hg_scanner_400::image_process(std::shared_ptr>& buffer) +{ + int ret = HG_ERR_OK; + hg_imgproc::IMGPRCPARAM param; + hg_imgproc::HIMGPRC handle = NULL; + hg_imgproc::IMGHEAD ih; + int err = HG_ERR_OK, + index = 0; + void* buf = NULL; + + param.bits = 8; + param.black_white = img_conf_.pixtype == COLOR_MODE_BLACK_WHITE; + param.channels = img_conf_.pixtype == COLOR_MODE_24_BITS ? 3 : 1; + param.color_mode = img_conf_.pixtype; + param.double_side = img_conf_.is_duplex; + param.dpi = img_conf_.resolution_dst; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.black_white :%d\r\n",param.black_white); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.channels :%d\r\n",param.channels); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.color_mode :%d\r\n",param.color_mode); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.double_side :%d\r\n",param.double_side); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"param.dpi :%d\r\n",param.dpi); + + + handle = hg_imgproc::init(&img_conf_,¶m); + hg_imgproc::load_buffer(handle, buffer); + hg_imgproc::decode(handle,pid_); + + if (img_conf_.fillhole.is_fillhole) + { + ret = hg_imgproc::fillhole(handle); + //printf("濉┛瀛斿紑鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "濉┛瀛斿紑鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + + /////////////////////////////////////111111111111111111111111111////////////////////////////////// + ret = hg_imgproc::auto_crop(handle); + + if (img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince) + { + ret = hg_imgproc::discardBlank(handle); + printf("涓㈠純绌虹櫧椤靛紑鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "涓㈠純绌虹櫧椤靛紑鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if(img_conf_.fadeback) + { + hg_imgproc::fadeback(handle,img_conf_.fadebackrange,param.double_side); + //printf("鑳屾櫙闄よ壊寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鑳屾櫙闄よ壊寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.resolution_dst != img_conf_.resolution_native) + { + hg_imgproc::resolution_change(handle); + //printf("dpi鏀瑰彉寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "dpi鏀瑰彉寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.cropRect.enable && !img_conf_.is_autocrop) + { + hg_imgproc::croprect(handle); + //printf("鎵嬪姩瑁佸浘寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鎵嬪姩瑁佸浘寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.filter != ColorFilter::FILTER_NONE && (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE || img_conf_.pixtype == COLOR_MODE_256_GRAY)) + { + printf("闄よ壊寮鍚痋r\n"); + hg_imgproc::channel(handle); + + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "闄よ壊寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + /////////////////////////////////////2222222222222222222222222222222222222////////////////////////////////// + hg_imgproc::customgamma(handle,false); //涓存椂false + + if (img_conf_.pixtype == COLOR_MODE_24_BITS && img_conf_.hsvcorrect) + { + hg_imgproc::answerSheetFilterRed(handle); + //printf("绛旈鍗″嚭绾㈠紑鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "绛旈鍗″嚭绾㈠紑鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.refuseInflow) + { + hg_imgproc::antiInflow(handle,image_prc_param_.bits.is_permeate_lv_); + //printf("闃叉娓楅忓紑鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "闃叉娓楅忓紑鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.colorCorrection && img_conf_.pixtype != COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::colorCorrection(handle); + //printf("棰滆壊鏍℃寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "棰滆壊鏍℃寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if ((img_conf_.is_autotext != TEXT_DIRECTION_0 || img_conf_.is_backrotate180) && (img_conf_.is_autotext != TEXT_DIRECTION_AUTO)) + { + hg_imgproc::orentation(handle); + //printf("鑷姩鏃嬭浆寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鑷姩鏃嬭浆寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.removeMorr) + { + hg_imgproc::textureRemove(handle); + //printf("闄ゆ懇灏旂汗寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "闄ゆ懇灏旂汗寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.detachnoise.is_detachnoise) + { + hg_imgproc::nosieDetach(handle); + //printf("鍣偣浼樺寲寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鍣偣浼樺寲寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::errorextention(handle); + //printf("榛戠櫧鍥惧鐞嗗紑鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "榛戠櫧鍥惧鐞嗗紑鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + if (img_conf_.en_fold) + { + hg_imgproc::fold(handle); + //printf("瀵规姌寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "瀵规姌寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + + /////////////////////////////////// + + if (image_prc_param_.bits.rid_red && img_conf_.pixtype == COLOR_MODE_24_BITS) + { + err = hg_imgproc::multi_out_red(handle); + //printf("澶氭祦鍑虹孩寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "澶氭祦鍑虹孩寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + + + if (image_prc_param_.bits.multi_out != MULTI_OUT_NOT && !user_cancel_) + { + //err = hg_imgproc::multi_out(handle,image_prc_param_.bits.multi_out - 1); 闈炴硶鎸囦护 鏆傛椂灞忚斀 + //printf("澶氭祦杈撳嚭寮鍚痋r\n"); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "澶氭祦杈撳嚭寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + + if (img_conf_.sharpen) + { + hg_imgproc::sharpenType(handle); + } + + if (image_prc_param_.bits.split && !user_cancel_) + { + int colormode=1; + if(img_conf_.filter == RID_COLOR_NONE) + colormode=img_conf_.pixtype; + + err = hg_imgproc::split(handle,img_conf_.multiOutput,img_conf_.splitImage,img_conf_.multi_output_red,colormode,img_conf_.is_duplex); + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "img split-> multiOutput is:%d splitImage is:%d multi_output_red is:%d pixtype is:%d is_duplex:%d\r\n" + ,img_conf_.multiOutput + ,img_conf_.splitImage + ,img_conf_.multi_output_red + ,img_conf_.pixtype + ,img_conf_.is_duplex); + + + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "鍥惧儚鎷嗗垎寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + + if (!image_prc_param_.bits.split || !image_prc_param_.bits.rid_red || !image_prc_param_.bits.multi_out) + { + if(img_conf_.automaticcolor) + { + ret = hg_imgproc::auto_matic_color(handle,img_conf_.automaticcolortype); + //HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO, "棰滆壊鑷姩璇嗗埆妯″紡寮鍚:%s\n",hg_scanner::strerr((hg_err)ret).c_str()); + } + } + hg_imgproc::final(handle); + while (hg_imgproc::get_final_data(handle, &ih, &buf, index++) == HG_ERR_OK && !user_cancel_) + { + HG_VLOG_MINI_5(HG_LOG_LEVEL_DEBUG_INFO, "Final picture %d (%d * %d * %d) with %u bytes!\n", index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + + if(!img_type_.empty()) + { + std::vectorbmpdata; + + hg_imgproc::imgtypechange(handle,img_type_,buf,bmpdata); + //cv::imencode(img_type_,*((cv::Mat*)buf),bmpdata); + if(bmpdata.empty()) + { + status_ = HG_ERR_NO_DATA; + return ; + } + buf = bmpdata.data(); + ih.total_bytes = bmpdata.size(); + + HG_VLOG_MINI_6(HG_LOG_LEVEL_DEBUG_INFO, "Set img type is:%s Final picture %d (%d * %d * %d) with %u bytes!\n",img_type_.c_str() ,index + , ih.width, ih.height, ih.bits * ih.channels, ih.total_bytes); + } + + save_final_image(&ih, buf); + } + + hg_imgproc::release(handle); +} + +int hg_scanner_400::writedown_device_configuration(bool type,HGSCANCONF_G400 *d) +{ + if (!type) + return HG_ERR_OK; + + int ret = HG_ERR_OK, + len = 0; + + if (!d) + d = &dsp_config; + + USBCB usbcb = {CONFIGURED_DATA, d->value, 0}; + len = sizeof(USBCB); + ret = io_->write_bulk(&usbcb,&len); + + HG_VLOG_MINI_1(HG_LOG_LEVEL_WARNING, "writedown_device_configuration is .(%s)\n", hg_scanner::strerr((hg_err)ret).c_str()); + + return ret; +} + +int hg_scanner_400::on_color_mode_changed(int& color_mode) +{ + int ret = HG_ERR_OK; + //printf("color_mode =%d\r\n",color_mode); + // if ((((color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) && !dsp_config.params.color) || + // ((color_mode != COLOR_MODE_24_BITS && color_mode != COLOR_MODE_AUTO_MATCH) && dsp_config.params.color)) && color_mode != -1) + // { + // printf("color_mode111111111111111111111 =%d\r\n",color_mode); + // dsp_config.params.color ^= 1; + + // //ret = writedown_device_configuration(); + // printf("on_color_mode_changed ret =%d\r\n",ret); + // if(ret) + // dsp_config.params.color ^= 1; + // } + + if (color_mode == COLOR_MODE_BLACK_WHITE || color_mode == COLOR_MODE_256_GRAY) + { + dsp_config.params.isColor = 0; + } + else if(color_mode == COLOR_MODE_24_BITS || color_mode == COLOR_MODE_AUTO_MATCH) + { + dsp_config.params.isColor = 1; + } + + HGSCANCONF_G400 d = dsp_config; + if(image_prc_param_.bits.rid_color != RID_COLOR_NONE && image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY && color_mode == -1) + { + dsp_config.params.isColor = 1; + } + else if (image_prc_param_.bits.rid_color == RID_COLOR_NONE && image_prc_param_.bits.color_mode == COLOR_MODE_256_GRAY && color_mode == -1) + { + dsp_config.params.isColor = 0; + } + if(color_mode == -1) + ret = writedown_device_configuration(true); + + return ret; + +} +int hg_scanner_400::on_paper_changed(int& paper) +{ + bool exact = true; + int ind = settingsdsp_400::match_best_paper(paper, &exact, &paper_size_), + ret = exact ? HG_ERR_OK : HG_ERR_NOT_EXACT; + + if (dsp_config.params.pageSize != settingsdsp_400::paper_map[ind].dev_value) + { + int old = dsp_config.params.pageSize; + + dsp_config.params.pageSize = settingsdsp_400::paper_map[ind].dev_value; + //printf("dsp_config.params.paper :%d\r\n",dsp_config.params.pageSize); + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.pageSize = old; + for (int i = 0; i < ARRAY_SIZE(settingsdsp_400::paper_map); ++i) + { + if (settingsdsp_400::paper_map[i].dev_value == old) + { + paper = settingsdsp_400::paper_map[i].paper; + break; + } + } + } + else if (!exact) + ret = HG_ERR_NOT_EXACT; + } + return ret; +} +int hg_scanner_400::on_paper_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + if(dsp_config.params.sizedetece ^ check) + { + dsp_config.params.sizedetece = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.sizedetece = !check; + check = dsp_config.params.sizedetece; + } + } + return ret; +} + +int hg_scanner_400::on_resolution_changed(int& dpi) +{ + int ret = HG_ERR_OK; + dsp_config.params.dpi = 1; + ret = writedown_device_configuration(); + return ret; +} + +int hg_scanner_400::on_ultrasonic_check_changed(bool& check) +{ + int ret = HG_ERR_OK; + + if (dsp_config.params.doubleFeeded ^ check) + { + dsp_config.params.doubleFeeded = check; + ret = writedown_device_configuration(); + if (ret) + { + dsp_config.params.doubleFeeded = !check; + check = dsp_config.params.doubleFeeded; + } + } + + return ret; +} +int hg_scanner_400::agreement(TwSS tw,int align) +{ + int ret = HG_ERR_OK; + SIZE size; + HGSCANCONF_G400 d; + d = dsp_config; + size = papersize.GetPaperSize(img_conf_.papertype,200,img_conf_.paperAlign); + dsp_config.params.dstHeight = (int)((size.cy + 200)/100); + if (img_conf_.is_autocrop) + { + dsp_config.params.pageSize = G400_AUTO; + } + if (image_prc_param_.bits.rid_color != RID_COLOR_NONE && img_conf_.pixtype != 2) + { + d.params.isColor = 1; + } + + ret = writedown_device_configuration(true,&d); + io_->set_timeout(200); + + return ret; +} +int hg_scanner_400::initdevice() +{ + dsp_config.value = 0; + dsp_config.params.enableLed = 1; + dsp_config.params.isCorrect = 1; + dsp_config.params.reversed1 = 0; + dsp_config.params.reversed2 = 0; + dsp_config.params.sizedetece =0; + writedown_device_configuration(true); + printf_devconfig(); + + return HG_ERR_OK; +} + + +void hg_scanner_400::writedown_image_configuration(void) +{ + SCANCONF ic; + + ic.papertype = paper_size_; + + if (is_lateral(image_prc_param_.bits.paper)) + ic.paperAlign = Rot270; + else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) + ic.paperAlign = AutoTextOrientation; + else + ic.paperAlign = Rot0; + + ic.en_sizecheck = dsp_config.params.sizedetece; + ic.imageRotateDegree = image_prc_param_.bits.text_direction * 90.0f; + ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE || image_prc_param_.bits.page == PAGE_OMIT_EMPTY || + image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT || image_prc_param_.bits.page ==PAGE_FOLIO); + + ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); + + ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; + + ic.automaticcolor = is_auto_matic_color; + + ic.automaticcolortype = 1;// ic.pixtype; //瀛樼枒 + ic.resolution_dst = resolution_; + ic.resolution_native = 200; + + ic.gamma = (float)gamma_; + ic.contrast = contrast_ ; + ic.brightness = bright_ ; + //printf("gamma_ :%f contrast_:%d bright_:%d\r\n",gamma_,contrast_,bright_); + ic.threshold = threshold_; + ic.is_autocontrast = 0; //鏃犲弬鏁 + ic.is_autocrop = (ic.papertype == TwSS::None || ic.papertype ==TwSS::USStatement); + ic.is_autodiscradblank_normal = image_prc_param_.bits.page == PAGE_OMIT_EMPTY; + ic.discardblank_percent = omit_empty_level_ > 70 ? 70 : omit_empty_level_; //榛樿20 + ic.is_autodiscradblank_vince = image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT; + ic.is_switchfrontback = image_prc_param_.bits.exchange; + ic.autodescrew = image_prc_param_.bits.automatic_skew; + //ic.multi_output_red = image_prc_param_.bits.rid_red; + ic.hsvcorrect = image_prc_param_.bits.rid_answer_red; + + ic.sharpen = image_prc_param_.bits.sharpen; + //ic.enhance_color = image_prc_param_.bits.rid_color; + ic.fillbackground = image_prc_param_.bits.erase_black_frame; + ic.is_convex = (image_prc_param_.bits.fill_background == FILL_BKG_CONVEX_POLYGON); + ic.noise = image_prc_param_.bits.noise_optimize; + ic.indent = margin_; + ic.AutoCrop_threshold = threshold_; + if (test_1_paper_) + { + HG_LOG(HG_LOG_LEVEL_DEBUG_INFO, "scanning mode: testing ONE paper ...\n"); + ic.scannum = 1; + } + else + { + ic.scannum = scan_count_; + } + + + ic.is_backrotate180 = image_prc_param_.bits.rotate_back_180; + ic.is_dogeardetection = image_prc_param_.bits.fractate_check; + + ic.hardwarecaps.en_skrewdetect = 0; + ic.hardwarecaps.en_doublefeed = dsp_config.params.doubleFeeded; + ic.hardwarecaps.en_stapledetect = 0; + ic.hardwarecaps.skrewdetectlevel = 0; + // ic.hardwarecaps.is_autopaper = dsp_config.params.is_autopaper; + ic.hardwarecaps.capturepixtype = 0; //鏆傛棤鍙傛暟 鑾峰彇鍥惧儚绫诲瀷 + ic.hardwarecaps.lowpowermode = LowPowerMode::Min_None; //鏆傛棤鍙傛暟 璁剧疆浼戠湢鏃堕棿 涓や釜鍙傛暟3399鏈娇鐢 + + ic.fillhole.is_fillhole = image_prc_param_.bits.rid_hole; + ic.fillhole.fillholeratio = rid_hole_range_; + + ic.detachnoise.is_detachnoise = image_prc_param_.bits.noise_optimize; + ic.detachnoise.detachnoise = noise_range_; + ic.is_autotext = image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO ? 1 :image_prc_param_.bits.text_direction; + ic.isfillcolor = false;//鏆傛棤鍙傛暟 鏈娇鐢ㄥ埌 + ic.refuseInflow = image_prc_param_.bits.is_permeate; + ic.colorCorrection = 0; + ic.removeMorr = image_prc_param_.bits.remove_morr; + ic.errorExtention = image_prc_param_.bits.error_extention ;//鏆傛棤鍙傛暟 + ic.textureRemove = image_prc_param_.bits.remove_txtture;//鏆傛棤鍙傛暟 + ic.splitImage = image_prc_param_.bits.split; + ic.cropRect.enable = 0;//鏆傛棤鍙傛暟 + ic.cropRect.height = 0;//鏆傛棤鍙傛暟 + ic.cropRect.width = 0;//鏆傛棤鍙傛暟 + ic.cropRect.x = 0;//鏆傛棤鍙傛暟 + ic.cropRect.y = 0;//鏆傛棤鍙傛暟 + ic.multiOutput = MultiOutput::Unused; //鏆傛棤鍙傛暟 + ic.normalCrop = image_prc_param_.bits.dark_sample; + ic.dogeardistabce = fractate_level_; + ic.fadeback = image_prc_param_.bits.erase_bakground; + ic.fadebackrange = erase_bkg_range_; + + + int filter_clr[] = { 3, 0, 1, 2, 5, 6, 7 }; + if(image_prc_param_.bits.color_mode == COLOR_MODE_24_BITS || image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH) + ic.filter = 3; + else + { + ic.filter = filter_clr[image_prc_param_.bits.rid_color]; + // if(ic.filter != 3) + // ic.pixtype = 2; + + ic.hsvcorrect = 0; + ic.multi_output_red = 0; + ic.multiOutput = MultiOutput::Unused; + ic.fadeback = false; + } + + img_conf_ = ic; + + int ret = agreement((TwSS)img_conf_.papertype,img_conf_.paperAlign); + if (ret != HG_ERR_OK) + { + status_ = ret; + return ; + } + + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.papertype=%d", ic.papertype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.AutoCrop_threshold=%d", ic.AutoCrop_threshold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.autodescrew=%d", ic.autodescrew); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.automaticcolor=%d", ic.automaticcolor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.brightness=%f", ic.brightness); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.contrast=%f", ic.contrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_fold=%d", ic.en_fold); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.en_sizecheck=%d", ic.en_sizecheck); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.enhance_color=%d", ic.enhance_color); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillbackground=%d", ic.fillbackground); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.filter=%d", ic.filter); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.gamma=%f", ic.gamma); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.capturepixtype=%d", ic.hardwarecaps.capturepixtype); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hardwarecaps.en_doublefeed=%d", ic.hardwarecaps.en_doublefeed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.hsvcorrect=%d", ic.hsvcorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.imageRotateDegree=%f", ic.imageRotateDegree); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.indent=%d", 5); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocontrast=%d", ic.is_autocontrast); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autocrop=%d", ic.is_autocrop); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_normal=%d", ic.is_autodiscradblank_normal); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autodiscradblank_vince=%d", ic.is_autodiscradblank_vince); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_autotext=%d", ic.is_autotext); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_backrotate180=%d", ic.is_backrotate180); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_convex=%d", ic.is_convex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_duplex=%d", ic.is_duplex); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_switchfrontback=%d", ic.is_switchfrontback); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.is_dogeardetection=%d", ic.is_dogeardetection); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.multi_output_red=%d", ic.multi_output_red); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.noise=%d", 8); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_dst=%f", ic.resolution_dst); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.resolution_native=%f", ic.resolution_native); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.scannum=%d", ic.scannum); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.sharpen=%d", ic.sharpen); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.discardblank_percent=%d", ic.discardblank_percent); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.is_detachnoise=%d", ic.detachnoise.is_detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.detachnoise=%d\r ", ic.detachnoise.detachnoise); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.detachnoise.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.refuseInflow=%d\r ", ic.refuseInflow); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.removeMorr=%d\r ", ic.removeMorr); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.errorExtention=%d\r ", ic.errorExtention); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.textureRemove=%d\r\n ", ic.textureRemove); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fillhole.is_fillhole=%d\r\n ", ic.fillhole.is_fillhole); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.ic.fillhole.fillholeratio=%f\r\n ", ic.fillhole.fillholeratio); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\n ic.fadeback=%d\r\n ",ic.fadeback); +} + +void hg_scanner_400::printf_devconfig(HGSCANCONF_G400 *d) +{ + if (!d) + d = &dsp_config; + + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"\r\ndsp_config.params.doubleFeeded:%d\r\n",d->params.doubleFeeded); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.dpi:%d\r\n",d->params.dpi); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.dstHeight:%d\r\n",d->params.dstHeight); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.enableLed:%d\r\n",d->params.enableLed); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.enableUV:%d\r\n",d->params.enableUV); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.isColor:%d\r\n",d->params.isColor); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.isCorrect:%d\r\n",d->params.isCorrect); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.pageSize:%d\r\n",d->params.pageSize); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.reversed1:%d\r\n",d->params.reversed1); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.reversed2:%d\r\n",d->params.reversed2); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.sizedetece:%d\r\n",d->params.sizedetece); + HG_VLOG_MINI_1(HG_LOG_LEVEL_DEBUG_INFO,"dsp_config.params.value:%d\r\n",d->value); +} + +std::string hg_scanner_400::get_firmware_version() +{ + char buf[20] = { 0 }; + int ret = HG_ERR_OK, + len = 10; //鍗忚瀹氫箟闀垮害涓10 100 200 =10 + USBCB cmd = {GET_FW_VERSION, len, 0,}; + + ret = writeusb(cmd); + if(ret == HG_ERR_OK) + ret = io_->read_bulk(buf, &len); + + return buf; +} + + std::string hg_scanner_400::get_serial_num() + { + char buf[20] = { 0 }; + int ret = HG_ERR_OK, + len = 14; + USBCB cmd = {GET_SERIAL, len, 0,}; + + ret = writeusb(cmd); + if (ret == HG_ERR_OK) + { + std::lock_guard lock(io_lock_); + ret = io_->read_bulk(buf, &len); + } + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "get serial num = %s, serial num = %s\n", hg_scanner::strerr((hg_err)ret).c_str(), buf); + + return buf; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int hg_scanner_400::set_leaflet_scan(void) +{ + int ret = HG_ERR_OK; + test_1_paper_ = true; + + ret = start(); + return ret; +} +int hg_scanner_400::get_abuot_info(void) +{ + + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::restore_default_setting(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::set_final_image_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_400::get_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::set_compression_format(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::set_auto_color_type(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} + +int hg_scanner_400::get_device_code(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::get_sleep_time(int &getsleepime) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_SLEEP_TIME, 0, 0}; + ret = writeusb(usbcb); + if (ret != HG_ERR_OK) + { + return ret; + } + len = sizeof(usbcb); + ret = io_->read_bulk(&usbcb,&len); + printf("usbcb.u32_Data = %d\r\n",usbcb.u32_Data); + if (ret == HG_ERR_OK) + { + getsleepime = usbcb.u32_Data; + } + return ret; +} +int hg_scanner_400::set_sleep_time(int setsleepime) +{ + + int ret = HG_ERR_OK, + time = setsleepime; + USBCB usbcb = {SET_SLEEP_TIME,time, 0}; + + ret = writeusb(usbcb); + + return ret;; +} + +int hg_scanner_400::get_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::set_dogear_distance(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::get_scanner_paperon(SANE_Bool* paperon) +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {GET_PAPER_STATUS, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + io_->set_timeout(500); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0) + ret = HG_ERR_DEVICE_NO_PAPER; + else + ret = HG_ERR_OK; + if (paperon) + { + *paperon = usbcb.u32_Data; + } + return ret; +} +int hg_scanner_400::set_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::get_scan_when_paper_on(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::get_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::set_scan_with_hole(void) +{ + return HG_ERR_DEVICE_NOT_SUPPORT; +} +int hg_scanner_400::get_scan_is_sleep() +{ + int ret = HG_ERR_OK, + len = 0; + + USBCB usbcb = {0x100, 0, 0}; + len = sizeof(USBCB); + ret = writeusb(usbcb); + + io_->set_timeout(200); + + if (ret == HG_ERR_OK) + { + ret = io_->read_bulk(&usbcb,&len); + } + if (ret != HG_ERR_OK) + { + return ret; + } + if (usbcb.u32_Data == 0x10) + { + return HG_ERR_OK; + } + else if (usbcb.u32_Data == 0x100) + { + return HG_ERR_DEVICE_SLEEPING; + } +} + + + +////////////////////////////////start///////////////////////////// +///姝ゆ璁惧鏃犳鍔熻兘鏆傛椂棰勭暀 +int hg_scanner_400::on_staple_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner_400::on_skew_check_changed(bool& check) +{ + return HG_ERR_OK; +} +int hg_scanner_400::on_skew_check_level_changed(int& check) +{ + return HG_ERR_OK; +} +/////////////////////////////////////over/////////////////////////// \ No newline at end of file diff --git a/hgdriver/hgdev/hg_scanner_400.h b/hgdriver/hgdev/hg_scanner_400.h new file mode 100644 index 0000000..e4b21b5 --- /dev/null +++ b/hgdriver/hgdev/hg_scanner_400.h @@ -0,0 +1,89 @@ +锘#pragma once + +// hg_scanner is the base class of kinds of scanners +// +// created on 2022-01-30 +// +#include +#include +#include +#include + +#include "hg_scanner.h" +#include "../../sdk/hginclude/hg_log.h" +#include "PaperSize.h" + + + +class hg_scanner_400 : public hg_scanner +{ +protected: + virtual int on_scanner_closing(bool force) override; + virtual void thread_handle_usb_read(void) override; + virtual void image_process(std::shared_ptr>& buff) override; + + + virtual int on_color_mode_changed(int& color_mode); // COLOR_MODE_xxx + virtual int on_paper_changed(int& paper); // PAPER_xxx + virtual int on_paper_check_changed(bool& check); + virtual int on_resolution_changed(int& dpi); + virtual int on_ultrasonic_check_changed(bool& check); + + virtual int on_staple_check_changed(bool& check); + virtual int on_skew_check_changed(bool& check); + virtual int on_skew_check_level_changed(int& check); + +public: + hg_scanner_400(const char* dev_name,int vid, usb_io* io); + ~hg_scanner_400(); + +public: + virtual int start(void)override; + virtual int stop(void)override; + void init_setting_map(int* setting_map, int count)override; + +private: + int agreement(TwSS tw,int align); + int initdevice(); + int writeusb(USBCB &usb); + int readusb(USBCB &usb); + int pop_image(void); + int get_scanner_status(USBCB &usb); + int get_img_data(std::shared_ptr> &imagedata); + int writedown_device_configuration(bool type =false,HGSCANCONF_G400 *d = NULL); + void writedown_image_configuration(void); + void printf_devconfig(HGSCANCONF_G400 *d = NULL); + virtual std::string get_firmware_version(void) override; + virtual std::string get_serial_num(void) override; + + + +private: + int pid_; + HGSCANCONF_G400 dsp_config; + Device::PaperSize papersize; + +public: + virtual int set_leaflet_scan(void);//鍗曞紶鎵弿 + virtual int get_abuot_info(void);//鑾峰彇杞欢鍏充簬淇℃伅 + virtual int restore_default_setting(void);//鎭㈠榛樿璁剧疆 + virtual int set_final_image_format(void); // 璁剧疆鍥惧儚澶勭悊鏈缁堣緭鍑猴紙final()锛夌殑鍥惧儚鏁版嵁鏍煎紡 + + virtual int get_compression_format(void);//鑾峰彇鏀寔鐨勫帇缂╂牸寮 + virtual int set_compression_format(void);//璁剧疆鍥惧儚鏁版嵁鏈缁堣緭鍑虹殑鍘嬬缉鏍煎紡 + virtual int set_auto_color_type(void);// 璁剧疆鑷姩鍖归厤棰滆壊妯″紡 + + virtual int get_device_code(void);//鑾峰彇璁惧缂栫爜 + virtual int get_sleep_time(int& getsleepime);//鑾峰彇鍔熻楁ā寮忥紙浼戠湢锛 + virtual int set_sleep_time(int setsleepime);//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛//璁剧疆鍔熻楁ā寮忥紙浼戠湢锛 + + virtual int get_dogear_distance(void);//鑾峰彇鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int set_dogear_distance(void);// 璁剧疆鎶樿妫娴嬫渶灏忚窛绂婚槇鍊 + virtual int get_scanner_paperon(SANE_Bool* paperon=NULL);//鑾峰彇璁惧鏈夋棤绾稿紶 + virtual int set_scan_when_paper_on(void);//鑾峰彇鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_when_paper_on(void);//璁剧疆鏄惁涓烘娴嬪埌杩涚焊鐩樹笂鏈夌焊鍗冲紑濮嬫壂鎻 + virtual int get_scan_with_hole(void);// 鑾峰彇鏄惁涓哄甫瀛旀壂鎻 + virtual int set_scan_with_hole(void);// 璁剧疆鏄惁涓哄甫瀛旀壂鎻 + + virtual int get_scan_is_sleep(void);//鑾峰彇璁惧鏄惁浼戠湢褰撲腑 +}; diff --git a/hgdriver/hgdev/image_process.cpp b/hgdriver/hgdev/image_process.cpp new file mode 100644 index 0000000..9053d38 --- /dev/null +++ b/hgdriver/hgdev/image_process.cpp @@ -0,0 +1,901 @@ +锘#include "image_process.h" +#include "../../sdk/hginclude/hg_log.h" + +#include +#include +#ifndef WIN32 +#include +#else +#include +#include +#pragma comment(lib, "Shell32.lib") +#endif +#include +#include "ImageMatQueue.h" +#include "../ImageProcess/ImageApplyHeaders.h" +#include "ImageMultiOutput.h" +#include "PaperSize.h" + +#ifdef WIN32 +#include "scanner_manager.h" +#endif + +using namespace std; +#define GET_BYTE(a) ((a) & 0x0ff) +#define MAKE_INT(a, b, c, d) (GET_BYTE(a) | (GET_BYTE(b) << 8) | (GET_BYTE(c) << 16) | (GET_BYTE(d) << 24)) +#define IMAGE_DATA_BUF_CVMAT (void*)MAKE_INT('M', 'T', 'R', 'X') +#define IMAGE_DATA_BUF_CHAR (void*)MAKE_INT('C', 'H', 'A', 'R') + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// functional ... +//////////////////////////////////////////////////////////////////////////////// +// NEW锛宖low ... + static int num=0; + +namespace hg_imgproc +{ + class imgproc + { + std::string my_path_; + IMGPRCPARAM param_; + SCANCONF img_conf_; + std::shared_ptr raw_data_; + std::shared_ptr> buffer_; + std::vector mats_; + Device::PaperSize papersize_; + + + void swap_rgb(cv::Mat& mat) + { + unsigned char* first = (unsigned char*)mat.data, + * oper = first; + int line_bytes = mat.rows ? mat.total() * mat.channels() / mat.rows : mat.cols * mat.channels(); + for (int i = 0; i < mat.rows; ++i) + { + oper = first; + for (int j = 0; j < mat.cols; ++j) + { + unsigned char ch = oper[0]; + oper[0] = oper[2]; + oper[2] = ch; + oper += 3; + } + first += line_bytes; + } + } + + // construction + public: + imgproc(LPSCANCONF img_param,LPIMGPRCPARAM param) : img_conf_(*img_param),param_(*param) + { + my_path_ = hg_log::pe_path(); + } + ~imgproc() + {} + + // load data + public: + int load_raw_data(std::shared_ptr> buff) + { + buffer_.reset(new std::vector(*buff)); + mats_.clear(); + + return HG_ERR_OK; + } + int load_file(const char* path_file) + { + mats_.clear(); + + FILE* src = fopen(path_file, "rb"); + if (!src) + return HG_ERR_OPEN_FILE_FAILED; + + long len = 0; + fseek(src, 0, SEEK_END); + len = ftell(src); + fseek(src, 0, SEEK_SET); + if (len > 1 * 1024 * 1024 * 1024) + { + fclose(src); + + return HG_ERR_INSUFFICIENT_MEMORY; + } + + raw_data_.reset(new std::string()); + raw_data_->resize(len); + fread(&(*raw_data_)[0], 1, len, src); + fclose(src); + + return HG_ERR_OK; + } + + // image-processing + public: + int decode(int pid) + { + if (!buffer_) + return HG_ERR_NO_DATA; + + size_t origin = buffer_->size(); + std::vector>> buffs; + if(pid == 0x100 || pid == 0x200) + buffs = G200Decode(buffer_,img_conf_.is_duplex,img_conf_.is_switchfrontback).getImageBuffs(); + else if(pid == 0x139 || pid == 0x239) + buffs = GRawDecode(buffer_).getImageBuffs(); + else if(pid == 0x300 || pid == 0x400) + buffs = G400Decode(buffer_,img_conf_.is_duplex).getImageBuffs(); + + if(buffs.empty()) + return -1; + + buffer_.reset(); + + for (auto& buf : buffs) { + cv::ImreadModes rmc = param_.channels > 1 ? cv::IMREAD_COLOR : cv::IMREAD_GRAYSCALE; + try + { + if (buf->at(0) == -119 && buf->at(1) == 0x50 && buf->at(2) == 0x4e && buf->at(3) == 0x47) + { + rmc = cv::IMREAD_GRAYSCALE; + param_.black_white = true; + } + else + param_.black_white = false; + + if((pid == 0x100 || pid == 0x200 || pid == 0x300 || pid == 0x400) + && (img_conf_.filter != 3 + || img_conf_.multiOutput == 1 + || img_conf_.multiOutput == 2 + || img_conf_.multiOutput == 0)) + + { + rmc = cv::IMREAD_COLOR; + } + cv::Mat mat(cv::imdecode(*buf, rmc)); + //("/home/huagao/Desktop/1.jpg",mat); + if (mat.empty()) + { + HG_LOG(HG_LOG_LEVEL_FATAL, "decode image data error\n"); + continue; + } + if(pid == 0x100 || pid == 0x200 || pid == 0x139 || pid == 0x239) + { + mats_.push_back(mat); + } + else if(pid == 0x300 || pid == 0x400) + { + cv::Mat front = mat(cv::Rect(0, 0, mat.cols / 2, mat.rows)); + cv::Mat back = mat(cv::Rect(mat.cols / 2, 0, mat.cols / 2, mat.rows)); + + if(img_conf_.is_duplex) + { + mats_.push_back(img_conf_.is_switchfrontback ? back :front); + mats_.push_back(img_conf_.is_switchfrontback ? front :back); + } + else + { + mats_.push_back(front); + } + back.release(); + front.release(); + } + buf.reset(); + } + catch (const std::exception& e) + { + HG_LOG(HG_LOG_LEVEL_FATAL, e.what()); + } + } + buffs.clear(); + HG_VLOG_MINI_2(HG_LOG_LEVEL_DEBUG_INFO, "Decode %u bytes to %u picture(s)\n", origin, mats_.size()); + if(mats_.size() == 0) + { + return HG_ERR_NO_DATA; + } + return HG_ERR_OK; + } + public: + int correct_text(void) + { + std::string sample(my_path_ + "/data/img/osd.traineddata"); + CImageApplyRotation rot(CImageApplyRotation::RotationType::AutoTextOrientation, false, param_.dpi, sample.c_str()); + + rot.apply(mats_, img_conf_.is_duplex); + + return HG_ERR_OK; + } + + + int split(int multioutputtype,bool is_msplit,bool is_multiout_red,int colortype,bool is_duplex) + { + std::vector mats(mats_); + CImageApplySplit split(multioutputtype, is_msplit, is_multiout_red, colortype);; + + + mats_.clear(); + auto matexs = split.SplitMats(mats,is_duplex); + std::string filename ; + for(auto &matex : matexs) + { + cv::flip(matex.mat,matex.mat,0); + cv::flip(matex.mat,matex.mat,1); + + if(!matex.mat.empty()) + mats_.push_back(matex.mat); + } + return HG_ERR_OK; + } + int fadeback(int range,bool is_duplex) + { + std::vector mats(mats_); + + mats_.clear(); + + CImageApplyFadeBackGroudColor fade(100,0,range); + for(size_t i = 0; i < mats.size();i++) + { + fade.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + + // std::string filename = "/home/huagao/Desktop/fadeback("+std::to_string(num++)+").jpg"; + // cv::imwrite(filename,mats_[i]); + + // printf("fadeback.size :%d ,filename is =%s\r\n",mats_.size(),filename.c_str()); + + } + + return HG_ERR_OK; + } + int multi_out(void) + { + std::vector mats(mats_); + + mats_.clear(); + for (size_t i = 0; i < mats.size(); ++i) + { + if (mats[i].empty()) + continue; + + vector retmats; + std::vector> m_multiprc_list; + int multiout = 1, + colormode = 1; + + //if (m_param.imageProcess.filter == ColorFilter::FILTER_NONE) + //{ + // colormode = m_param.pixelType; + //} + if (param_.channels > 1) + m_multiprc_list.push_back(shared_ptr(new ImageMultiOutputRed(2))); + m_multiprc_list.push_back(shared_ptr(new IMageMulti(multiout))); + for (int j = 0; j < m_multiprc_list.size(); j++) + { + retmats = m_multiprc_list[j]->apply(mats[i]); + } + + CImageApplySplit isp(multiout, false, 1, colormode); + if (!retmats.size()) + { + std::vector matse; + matse.push_back(mats[i]); + auto matexs = isp.SplitMats(matse, img_conf_.is_duplex); + for (auto& matex : matexs) + { + mats_.push_back(matex.mat); + } + break; + } + else + { + auto matexs = isp.SplitMats(retmats, img_conf_.is_duplex); + for (auto& matex : matexs) + { + mats_.push_back(matex.mat); + } + } + } + + return HG_ERR_OK; + } + + int multi_out(int out_type) + { + std::vector mats(mats_); + + mats_.clear(); + + IMageMulti output(out_type); + + for(size_t i = 0;i < mats.size();i++) + { + output.apply(mats[i]); + mats_.push_back(mats[i]); + + // std::string filename = "/home/huagao/Desktop/multi_out("+std::to_string(num++)+").jpg"; + // cv::imwrite(filename,mats_[i]); + + // printf("multi_out.size :%d ,multi_out is =%s\r\n",mats_.size(),filename.c_str()); + } + return HG_ERR_OK; + } + int multi_out_red() + { + std::vector mats(mats_); + std::vector mat; + mats_.clear(); + mat.clear(); + + ImageMultiOutputRed outred(2); + + for(size_t i = 0;i < mats.size();i++) + { + mat = outred.apply(mats[i]); + for(size_t j = 0;j < mat.size();j++) + { + mats_.push_back(mat[j]); + } + + // std::string filename = "/home/huagao/Desktop/multi_out_red("+std::to_string(num++)+").jpg"; + // cv::imwrite(filename,mats_[i]); + // printf("fadeback.size :%d ,filename is =%s\r\n",mats_.size(),filename.c_str()); + } + return HG_ERR_OK; + } + int auto_matic_color(int color_type) + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + for(size_t i = 0;i < mats.size();i++) + { + CImageApplyColorRecognition ColorRecognition(color_type==1? + CImageApplyColorRecognition::ColorRecognitionMode::Color_Gray : + CImageApplyColorRecognition::ColorRecognitionMode::Color_Mono); + + ColorRecognition.apply(mats[i],img_conf_.is_duplex); + + mats_.push_back(mats[i]); + } + return ret; + } + /*pixtype 0 colcor; 1 gray; 2 bw*/ + int auto_crop() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + SIZE temp_Size = papersize_.GetPaperSize(img_conf_.papertype,200,img_conf_.paperAlign); + cv::Size cvSize(temp_Size.cx, temp_Size.cy); + + CImageApplyAutoCrop crop(img_conf_.is_autocrop,img_conf_.autodescrew,img_conf_.fillbackground,cvSize,true,img_conf_.isfillcolor); + + crop.apply(mats,img_conf_.is_duplex); + mats_ = mats; + //cv::imwrite("/home/huagao/Desktop/mats_.jpg",mats_[0]); + + return ret; + } + int fillhole() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + float scale = img_conf_.fillhole.fillholeratio / 100.0; + CImageApplyOutHole outh(200,scale,50); + + outh.apply(mats,img_conf_.is_duplex); + mats_ = mats; + + return ret; + } + int resolution_change() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyResize::ResizeType resizeType; + double ratio = 1.0; + SIZE reSize = papersize_.GetPaperSize(img_conf_.papertype, img_conf_.resolution_dst,img_conf_.paperAlign); + cv::Size cvSize(reSize.cx, reSize.cy); + if (img_conf_.is_autocrop || img_conf_.cropRect.enable) + { + resizeType = CImageApplyResize::ResizeType::RATIO; + ratio = img_conf_.resolution_dst / (float)img_conf_.resolution_native; + } + else + resizeType = CImageApplyResize::ResizeType::DSIZE; + + CImageApplyResize resize(resizeType,cvSize,ratio,ratio); + resize.apply(mats,img_conf_.is_duplex); + if(!mats.empty()) + mats_ = mats; + + return ret; + } + int croprect() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyCustomCrop rect(cv::Rect(img_conf_.cropRect.x, img_conf_.cropRect.y, img_conf_.cropRect.width, img_conf_.cropRect.height)); + for (size_t i = 0; i < mats.size(); ++i) + { + rect.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + + return ret; + } + int channel() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyChannel chl((CImageApplyChannel::channel)(img_conf_.filter)); + + for (size_t i = 0; i < mats.size(); i++) + { + chl.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + + return ret; + } + int customgamma(int is_customgamma) + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + if(is_customgamma) + { + //CImageApplyCustomGamma gamme(); //鏆傜暀 + } + else + { + + if (img_conf_.brightness != 128 ||img_conf_.contrast != 4 || ((img_conf_.gamma < (1.0f - 1e-2)) || (img_conf_.gamma > (1.0f + 1e-2))) ) + { + int temp_contrast = (img_conf_.contrast - 4) * 12; + CImageApplyAdjustColors justColors(img_conf_.brightness - 128, temp_contrast, img_conf_.gamma); + for (size_t i = 0; i < mats.size(); ++i) + { + justColors.apply(mats[i],img_conf_.is_duplex); + } + } + + mats_ = mats; + + //cv::imwrite("/home/huagao/Desktop/customgamma2.jpg",mats_[0]); + } + return ret; + } + //闃叉娓楅 + int antiInflow(int permeate_lv) + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyRefuseInflow Inflow(permeate_lv); + for (size_t i = 0; i < mats.size(); ++i) + { + Inflow.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + + return ret; + } + //鑹插僵鏍℃ + int colorCorrection() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyAutoContrast con; + con.apply(mats,img_conf_.is_duplex); + mats_= mats; + + return ret; + } + //鍥惧儚鏃嬭浆 + int orentation() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyRotation::RotationType rotatetype = CImageApplyRotation::RotationType::Invalid; + switch ((CImageApplyRotation::RotationType)img_conf_.is_autotext) + { + case CImageApplyRotation::RotationType::Rotate_90_clockwise: + rotatetype = CImageApplyRotation::RotationType::Rotate_90_clockwise; + break; + case CImageApplyRotation::RotationType::Rotate_180: + rotatetype = CImageApplyRotation::RotationType::Rotate_180; + break; + case CImageApplyRotation::RotationType::Rotate_90_anti_clockwise: + rotatetype = CImageApplyRotation::RotationType::Rotate_90_anti_clockwise; + break; + default: + break; + } + + if (img_conf_.is_autotext== TEXT_DIRECTION_AUTO) + rotatetype = CImageApplyRotation::RotationType::AutoTextOrientation; +#ifdef WIN32 + char szIniFile[MAX_PATH] = {0}; + SHGetSpecialFolderPathA(NULL, szIniFile, CSIDL_WINDOWS, TRUE); + strcat(szIniFile, "\\twain_32\\HuaGoScan\\tessdata"); + // m_iaList.push_back(shared_ptr(new CImageApplyRotation(rotatetype, imgparams.BackRotate180, imgparams.DestResulution, szIniFile))); +#else // WIN32 + CImageApplyRotation Rotation(rotatetype,img_conf_.is_backrotate180,img_conf_.resolution_native,"./tessdata"); + + Rotation.apply(mats,img_conf_.is_duplex); + mats_ = mats; + +#endif + return ret; + } + //闄ょ綉绾 + int textureRemove() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyTextureRemoval Removal; + + Removal.apply(mats,img_conf_.is_duplex); + mats_ = mats; + + return ret; + } + //閿愬寲 + int sharpenType() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyFilter::FilterMode sharpenType = (CImageApplyFilter::FilterMode)img_conf_.sharpen; + CImageApplyFilter Filte(sharpenType); + for (size_t i = 0; i < mats.size(); ++i) + { + Filte.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + return ret; + } + //榛戠櫧闄嶅櫔 + int nosieDetach() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyDetachNoise Noise(img_conf_.detachnoise.detachnoise); + for (size_t i = 0; i < mats.size(); ++i) + { + Noise.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + return ret; + + } + //閿欒鎵╂暎 + int errorextention() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyBWBinaray::ThresholdType thrtype; + if(img_conf_.errorExtention) + thrtype = CImageApplyBWBinaray::ThresholdType::ERROR_DIFFUSION; + else + thrtype = CImageApplyBWBinaray::ThresholdType::THRESH_BINARY; + + CImageApplyBWBinaray BWBinaray(thrtype); + for (size_t i = 0; i < mats.size(); ++i) + { + BWBinaray.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + return ret; + } + + int discardBlank() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + double threshold = 40; //榛樿鍊 + int edge = 150; + + CImageApplyDiscardBlank(threshold,edge,img_conf_.discardblank_percent); + + for (size_t i = 0; i < mats.size(); ++i) + { + bool b = CImageApplyDiscardBlank::apply(mats[i]); + + if (b) + mats[i].release(); + else + mats_.push_back(mats[i]); + } + return ret; + } + //绛旈鍗″嚭绾 + int answerSheetFilterRed() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyHSVCorrect correct((CImageApplyHSVCorrect::Red_Removal)); + for (size_t i = 0; i < mats.size(); ++i) + { + correct.apply(mats[i],img_conf_.is_duplex); + mats_.push_back(mats[i]); + } + return ret; + } + //瀵规姌 + int fold() + { + int ret = HG_ERR_OK; + std::vector mats(mats_); + mats_.clear(); + + CImageApplyConcatenation fold(CImageApplyConcatenation::horizontal); + fold.apply(mats,img_conf_.is_duplex); + mats_= mats; + + return ret; + } + // final + public: + int final(void) + { + std::vector mats(mats_); + + mats_.clear(); + for (size_t i = 0; i < mats.size(); ++i) + { + if (mats[i].empty()) + continue; + + int bpp = param_.black_white ? 8 : param_.bits * param_.channels; + MatEx out(mats[i], bpp); + + if (out.mat.channels() == 3) + swap_rgb(out.mat); + mats_.push_back(out.mat); + } + + return HG_ERR_OK; + } + int get_final_data(LPIMGHEAD pimh, void** buf, int index) + { + if ((index < 0 || index >= mats_.size())) + return HG_ERR_NO_DATA; + + pimh->width = mats_[index].cols; + pimh->height = mats_[index].rows; + pimh->bits = 8; //mats_[index].depth + pimh->channels = mats_[index].channels(); + + pimh->total_bytes = mats_[index].total() * pimh->channels; + pimh->line_bytes = pimh->height ? pimh->total_bytes / pimh->height : pimh->width * pimh->channels; + *buf = mats_[index].data; + //cv::imwrite(to_string(index)+"_final.jpg",mats_[index]); + + //printf("pimh->channels = %d \r\n",pimh->channels); + return HG_ERR_OK; + } + + int get_final_data(LPIMGHEAD pimh, std::vector* buf, int index) + { + if ((index < 0 || index >= mats_.size())) + return HG_ERR_NO_DATA; + + pimh->width = mats_[index].cols; + pimh->height = mats_[index].rows; + pimh->bits = 8; //mats_[index].depth + pimh->channels = mats_[index].channels(); + + if((pimh->width * pimh->channels) % 4) + { + int len = pimh->width * pimh->channels; + pimh->line_bytes = (len + 3) / 4 * 4; + pimh->total_bytes = pimh->line_bytes * pimh->height; + buf->resize(pimh->total_bytes); + + //printf("pimh->total_bytes=%d pimh->line_bytes=%d\r\n",pimh->total_bytes,pimh->line_bytes); + unsigned char* src = mats_[index].data, *dst = buf->data(); + for(int i = 0; i < pimh->height; ++i) + { + memcpy(dst, src, len); + dst += pimh->line_bytes; + src += len; + } + } + else + { + pimh->total_bytes = mats_[index].total() * pimh->channels; + pimh->line_bytes = pimh->height ? pimh->total_bytes / pimh->height : pimh->width * pimh->channels; + buf->resize(pimh->total_bytes); + memcpy(buf->data(), mats_[index].data, pimh->total_bytes); + //cv::imwrite(to_string(index)+"_final.jpg",mats_[index]); + } + + //printf("pimh->channels_01 = %d \r\n",pimh->channels); + return HG_ERR_OK; + } + + int imgtypechange(std::string img_type_,void *buf,std::vector &bmpdata) + { + int ret = HG_ERR_OK; + if(!buf) + { + return HG_ERR_NO_DATA; + } + cv::imencode(img_type_,*((cv::Mat*)buf),bmpdata); + return ret; + } + }; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // api ... + HIMGPRC init(LPSCANCONF parameter,LPIMGPRCPARAM param) + { + imgproc* obj = new imgproc(parameter,param); + + return (HIMGPRC)obj; + } + int load_buffer(HIMGPRC himg, std::shared_ptr> buff) + { + return ((imgproc*)himg)->load_raw_data(buff); + } + int load_file(HIMGPRC himg, const char* path_file) + { + return ((imgproc*)himg)->load_file(path_file); + } + + int decode(HIMGPRC himg,int pid) + { + return ((imgproc*)himg)->decode(pid); + } + int correct_text(HIMGPRC himg) + { + return ((imgproc*)himg)->correct_text(); + } + int split(HIMGPRC himg,int multioutputtype,bool is_msplit,bool is_multiout_red,int colortype,bool is_duplex) + { + return ((imgproc*)himg)->split( multioutputtype, is_msplit, is_multiout_red, colortype,is_duplex); + } + int fadeback(HIMGPRC himg,int range,bool is_duplex) + { + return ((imgproc*)himg)->fadeback(range,is_duplex); + } + int multi_out(HIMGPRC himg) + { + return ((imgproc*)himg)->multi_out(); + } + + int multi_out(HIMGPRC himg,int out_type) + { + return ((imgproc*)himg)->multi_out(out_type); + } + + int multi_out_red(HIMGPRC himg) + { + return ((imgproc*)himg)->multi_out_red(); + } + int auto_matic_color(HIMGPRC himg,int color_type) + { + return ((imgproc*)himg)->auto_matic_color(color_type); + } + int auto_crop(HIMGPRC himg) + { + return ((imgproc*)himg)->auto_crop(); + } + int fillhole(HIMGPRC himg) + { + return ((imgproc*)himg)->fillhole(); + } + int resolution_change(HIMGPRC himg) + { + return ((imgproc*)himg)->resolution_change(); + } + int croprect(HIMGPRC himg) + { + return ((imgproc*)himg)->croprect(); + } + int channel(HIMGPRC himg) + { + return ((imgproc*)himg)->channel(); + } + int customgamma(HIMGPRC himg,int is_custogamma) + { + return ((imgproc*)himg)->customgamma(is_custogamma); + } + int antiInflow(HIMGPRC himg,int permeate_lv) + { + return ((imgproc*)himg)->antiInflow(permeate_lv); + } + int colorCorrection(HIMGPRC himg) + { + return ((imgproc*)himg)->colorCorrection(); + } + int orentation(HIMGPRC himg) + { + return ((imgproc*)himg)->orentation(); + } + int textureRemove(HIMGPRC himg) + { + return ((imgproc*)himg)->textureRemove(); + } + int sharpenType(HIMGPRC himg) + { + return ((imgproc*)himg)->sharpenType(); + } + int nosieDetach(HIMGPRC himg) + { + return ((imgproc*)himg)->nosieDetach(); + } + int errorextention(HIMGPRC himg) + { + return ((imgproc*)himg)->errorextention(); + } + int discardBlank(HIMGPRC himg) + { + return ((imgproc*)himg)->discardBlank(); + } + int answerSheetFilterRed(HIMGPRC himg) + { + return ((imgproc*)himg)->answerSheetFilterRed(); + } + + //img_type_ = jpg png bmp + int imgtypechange(HIMGPRC himg,std::string img_type_,void *buf,std::vector &bmpdata) + { + return ((imgproc*)himg)->imgtypechange(img_type_,buf,bmpdata); + } + int fold(HIMGPRC himg) + { + return ((imgproc*)himg)->fold(); + } + int final(HIMGPRC himg) + { + return ((imgproc*)himg)->final(); + } + + int get_final_data(HIMGPRC himg, LPIMGHEAD pimh, void** buf, int index) + { + return ((imgproc*)himg)->get_final_data(pimh, buf, index); + } + + int get_final_data(HIMGPRC himg, LPIMGHEAD pimh, std::vector* buf, int index) + { + return ((imgproc*)himg)->get_final_data(pimh, buf, index); + } + + void release(HIMGPRC himg) + { + imgproc* obj = (imgproc*)himg; + + delete obj; + } +} + + + diff --git a/hgdriver/hgdev/image_process.h b/hgdriver/hgdev/image_process.h new file mode 100644 index 0000000..7690034 --- /dev/null +++ b/hgdriver/hgdev/image_process.h @@ -0,0 +1,207 @@ +#pragma once + +// API for image process +// +// created on 2022-03-01 +// +#include "huagao/hgscanner_error.h" +#include "common_setting.h" +#include +#include +//#include "common_setting.h" +namespace hg_imgproc +{ + typedef struct _img_data + { + int img_fmt; // 鍥剧墖鏍煎紡锛圔MP銆丳NG鈥︹︼紝鏆傛椂鏈敤锛 + int width; // 琛屽搴︼紙鍍忕礌鏁帮級 + int height; // 楂樺害锛堝儚绱犳暟锛 + int line_bytes; // 姣忚鐨勫瓧鑺傛暟 + int bits; // 姣忎釜鍍忕礌鐨勪綅娣 + bool is_bw; // 鏄惁涓洪粦鐧藉浘鐗囷紝鐢卞浘鍍忓鐞嗘ā鍧楄缃 + unsigned bytes; // 鍥惧儚鏁版嵁瀛楄妭鏁(buf涓殑瀛楄妭鏁) + void* ip_data; // 鍥惧儚澶勭悊妯″潡鐨勮嚜瀹氫箟鏁版嵁锛岃皟鐢ㄨ呬笉瑕侀殢鎰忔洿鏀规鍊硷紒锛侊紒 + void* buf; // 鍥惧儚鏁版嵁淇℃伅 + }IMGDATA, *LPIMGDATA; + + // 鐢ㄤ簬閲婃斁鍥惧儚澶勭悊妯″潡杩斿洖鐨勫悇绉嶅姩鎬佸唴瀛, mem[i] == NULL 鏃剁粓姝紝涓嬪垪鐩稿悓鍙傛暟鍧囩収姝よ鍒 + void imgproc_free_memory(LPIMGDATA* mem); + + // 鎵鏈夊嚱鏁板潎杩斿洖鈥渋nt鈥濆硷紝浠h〃閿欒浠g爜锛 0涓烘垚鍔燂紝鍏跺畠閿欒鐮佺敱鍥惧儚澶勭悊妯″潡寮鍙戣呭畾涔 + + // Function: 鐢ㄤ簬灏嗗師濮嬫暟鎹紙濡傚浘鍍忔枃浠跺唴瀹癸級锛岃В鐮佷负target_fmt鎸囧畾鐨勫浘鍍忔牸寮 + // + // Parameters: raw - 鍘熷鐨勫浘鍍忔暟鎹 + // + // bytes - raw涓暟鎹瓧鑺傛暟 + // + // bits - 浣嶆繁 + // + // out - 鐢ㄤ簬鎺ユ敹瑙g爜鍚庣殑鍥惧儚鏁版嵁銆傜敱鍥惧儚澶勭悊妯″潡鍒嗛厤鍐呭瓨锛岃皟鐢ㄨ呰皟鐢╥mgproc_free_memory閲婃斁 + // + // Return: 閿欒鐮 + // + // NOTE: 璇ュ嚱鏁拌繑鍥炵殑out缁撴瀯浣撲腑锛宐uf涓哄浘鍍忓鐞嗙殑涓棿鏁版嵁绫诲瀷锛堝cv::mat*锛夛紝瑕佹兂鑾峰彇鏈缁堢殑鍥剧墖鏁版嵁锛 + // 闇瑕佸啀璋冪敤 imgproc_final 鍑芥暟杩涜杞崲 + int imgproc_decode(void* buf, unsigned bytes, int bits, LPIMGDATA** out); + + // Function: 灏嗗炬枩鐨勬枃鏈牎姝 + // + // Parameters: raw - [in]: 鍘熷鐨勫浘鍍忔暟鎹紝*raw == NULL鏃剁粓姝 + // + // [out]: 鏈缁堢殑鍥惧儚鏁版嵁 + // + // out - 鐢ㄤ簬鎺ユ敹瑙g爜鍚庣殑鍥惧儚鏁版嵁銆傜敱鍥惧儚澶勭悊妯″潡鍒嗛厤鍐呭瓨锛岃皟鐢ㄨ呰皟鐢╥mgproc_free_memory閲婃斁 + // + // dpi - 鍥剧墖DPI + // + // double_face - 鏄惁鍙岄潰 + // + // Return: 閿欒鐮 + int imgproc_correct_text(LPIMGDATA** raw, int dpi, bool double_face); // 閫傜敤浜巖aw浠庡叾瀹冨浘鍍忓鐞嗗嚱鏁拌繑鍥炵殑鎯呭喌 + int imgproc_correct_text(LPIMGDATA* raw, LPIMGDATA** out, int dpi, bool double_face); // 閫傜敤浜巖aw浠庢枃浠舵垨鑰呭唴瀛樹腑杞崲鑰屾潵 + + // Function: 鑷姩鍖归厤棰滆壊 + // + // Parameters: raw - [in]: 鍘熷鐨勫浘鍍忔暟鎹紝*raw == NULL鏃剁粓姝 + // + // [out]: 鏈缁堢殑鍥惧儚鏁版嵁 + // + // out - 鐢ㄤ簬鎺ユ敹瑙g爜鍚庣殑鍥惧儚鏁版嵁銆傜敱鍥惧儚澶勭悊妯″潡鍒嗛厤鍐呭瓨锛岃皟鐢ㄨ呰皟鐢╥mgproc_free_memory閲婃斁 + // + // double_face - 鏄惁鍙岄潰 + // + // Return: 閿欒鐮 + int imgproc_auto_adjust_color(LPIMGDATA** raw, bool double_face); // 閫傜敤浜巖aw浠庡叾瀹冨浘鍍忓鐞嗗嚱鏁拌繑鍥炵殑鎯呭喌 + int imgproc_auto_adjust_color(LPIMGDATA* raw, LPIMGDATA** out, bool double_face); // 閫傜敤浜巖aw浠庢枃浠舵垨鑰呭唴瀛樹腑杞崲鑰屾潵 + + // Function: 鍥惧儚鎷嗗垎 + // + // Parameters: raw - [in]: 鍘熷鐨勫浘鍍忔暟鎹紝*raw == NULL鏃剁粓姝 + // + // [out]: 鏈缁堢殑鍥惧儚鏁版嵁 + // + // out - 鐢ㄤ簬鎺ユ敹瑙g爜鍚庣殑鍥惧儚鏁版嵁銆傜敱鍥惧儚澶勭悊妯″潡鍒嗛厤鍐呭瓨锛岃皟鐢ㄨ呰皟鐢╥mgproc_free_memory閲婃斁 + // + // Return: 閿欒鐮 + int imgproc_split(LPIMGDATA** raw); // 閫傜敤浜巖aw浠庡叾瀹冨浘鍍忓鐞嗗嚱鏁拌繑鍥炵殑鎯呭喌 + int imgproc_split(LPIMGDATA* raw, LPIMGDATA** out); // 閫傜敤浜巖aw浠庢枃浠舵垨鑰呭唴瀛樹腑杞崲鑰屾潵 + + // Function: 澶氭祦杈撳嚭 + // + // Parameters: raw - [in]: 鍘熷鐨勫浘鍍忔暟鎹紝*raw == NULL鏃剁粓姝 + // + // out - 鐢ㄤ簬鎺ユ敹瑙g爜鍚庣殑鍥惧儚鏁版嵁銆傜敱鍥惧儚澶勭悊妯″潡鍒嗛厤鍐呭瓨锛岃皟鐢ㄨ呰皟鐢╥mgproc_free_memory閲婃斁 + // + // dpi - 鍥剧墖DPI + // + // double_face - 鏄惁鍙岄潰 + // + // Return: 閿欒鐮 + int imgproc_multi_out(LPIMGDATA* raw, LPIMGDATA** out, int dpi, bool double_face); + + enum + { + TARGET_FORMAT_GRAY = 1 << 0, + TARGET_FORMAT_BLACK_WHITE = 1 << 1, + TARGET_FORMAT_RGB = 1 << 2, + }; + // Function: 鐢ㄤ簬灏嗗浘鍍忓鐞嗚繃绋嬬殑涓棿鎬佹暟鎹紝杞崲涓烘渶缁堢殑鍥剧墖鏁版嵁 + // + // Parameters: raw - [in]: 鍘熷鐨勫浘鍍忔暟鎹紝*raw == NULL鏃剁粓姝紝閫氬父璇ュ彉閲忔槸鐢卞叾瀹冨浘鍍忓鐞嗗嚱鏁拌繑鍥炵殑 + // + // [out]: 鏈缁堢殑鍥惧儚鏁版嵁 + // + // target_fmt - 棰勬湡瑙g爜鍒扮殑鏍煎紡锛孴ARGET_FORMAT_xxx + // + // Return: 閿欒鐮 + int imgproc_final(LPIMGDATA** raw, int target_fmt, int dpi); + + + + //////////////////////////////////////////////////////////////////////////////// + // NEW锛宖low ... + // + // 1 - call init to initialize an image processing object handle HIMGPRC + // + // 2 - call load_buffer or load_file to load raw data + // + // 3 - call image-processing methods + // + // 4 - repeat step-3 + // + // 5 - call final to got outputing data + // + // 6 - call get_final_data to get outputing data, while return HG_ERR_NO_DATA + // + // 7 - call release to free the HIMGPRC handle + // + //////////////////////////////////////////////////////////////////////////////// + typedef void* HIMGPRC; + typedef struct _img_proc_param + { + int dpi; + int bits; // 鍗曞垎閲忎綅娣 + int channels; + int color_mode; + bool double_side; + bool black_white; + }IMGPRCPARAM, *LPIMGPRCPARAM; + typedef struct _img_header + { + int width; // in pixel + int height; // in pixel + int bits; // per channel + int channels; // RGB - 3; GRAY - 1; ... + int line_bytes; // bytes of ONE line + unsigned total_bytes;// total bytes + }IMGHEAD, *LPIMGHEAD; + + + HIMGPRC init(LPSCANCONF parameter,LPIMGPRCPARAM param); + int load_buffer(HIMGPRC himg,std::shared_ptr> buff); + int load_file(HIMGPRC himg, const char* path_file); + + //鍥惧儚鏁版嵁杞崲 + int decode(HIMGPRC himg,int pid); + int correct_text(HIMGPRC himg); + + //鎷嗗垎 + int split(HIMGPRC himg,int multioutputtype,bool is_msplit,bool is_multiout_red,int colortype,bool is_duplex); + int fadeback(HIMGPRC himg,int range,bool is_duplex); + int multi_out(HIMGPRC himg); + int multi_out(HIMGPRC himg,int out_type); + int multi_out_red(HIMGPRC himg); + int auto_matic_color(HIMGPRC himg,int color_type); + int auto_crop(HIMGPRC himg); + int fillhole(HIMGPRC himg); + int resolution_change(HIMGPRC himg); + int croprect(HIMGPRC himg); + int channel(HIMGPRC himg); + int customgamma(HIMGPRC himg,int is_custogamma); + int antiInflow(HIMGPRC himg,int permeate_lv); + int colorCorrection(HIMGPRC himg); + int orentation(HIMGPRC himg); + int textureRemove(HIMGPRC himg); + int sharpenType(HIMGPRC himg); + int nosieDetach(HIMGPRC himg); + int errorextention(HIMGPRC himg); + int discardBlank(HIMGPRC himg); + int answerSheetFilterRed(HIMGPRC himg); + + int imgtypechange(HIMGPRC himg,std::string img_type_,void *buf,std::vector &bmpdata); + int fold(HIMGPRC himg); + int final(HIMGPRC himg); + + // pimh must not to be NULL, and pimh->total_bytes indicates the length of 'buf' + // + // if 'buf' was NULL, then return HG_ERR_INSUFFICIENT_MEMORY + // + // index is ZERO-base, if index is valid, fill the 'pimh' first, and copy data to buf if it was not NULL + // return HG_ERR_NO_DATA if it was out of range + // + // return HG_ERR_OK if index has valid image data and buf is large enough + int get_final_data(HIMGPRC himg, LPIMGHEAD pimh, void** buf, int index = 0); + int get_final_data(HIMGPRC himg, LPIMGHEAD pimh, std::vector* buf, int index); + void release(HIMGPRC himg); +} diff --git a/hgdriver/hgdev/json.hpp b/hgdriver/hgdev/json.hpp new file mode 100644 index 0000000..06da815 --- /dev/null +++ b/hgdriver/hgdev/json.hpp @@ -0,0 +1,22875 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.7.3 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 7 +#define NLOHMANN_JSON_VERSION_PATCH 3 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include + +// #include + + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // pair +// #include +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 11) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 11 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(__COMPCERT__) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) && JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +#else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif JSON_HEDLEY_TI_VERSION_CHECK(8,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,3,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(__cplusplus) && (__cplusplus >= 201703L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_UNREACHABLE() __assume(0) +#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_UNREACHABLE() std::_nassert(0) + #else + #define JSON_HEDLEY_UNREACHABLE() _nassert(0) + #endif + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value +#elif defined(EXIT_FAILURE) + #define JSON_HEDLEY_UNREACHABLE() abort() +#else + #define JSON_HEDLEY_UNREACHABLE() + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() +#endif + +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(JSON_HEDLEY_ARM_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) + #define JSON_HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) +#else + #define JSON_HEDLEY_ASSUME(expr) ((void) (expr)) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#if !defined(JSON_HEDLEY_BUILTIN_UNPREDICTABLE) + #define JSON_HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) +#endif +#elif \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else + #define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) + #define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif JSON_HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else + #define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) + #define JSON_HEDLEY_PRIVATE + #define JSON_HEDLEY_PUBLIC __declspec(dllexport) + #define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else + #if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + #define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) + #define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) + #else + #define JSON_HEDLEY_PRIVATE + #define JSON_HEDLEY_PUBLIC + #endif + #define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + (defined(__cplusplus) && JSON_HEDLEY_TI_VERSION_CHECK(8,3,0)) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_CPP_CAST(T, expr) static_cast(expr) +#else + #define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include + +// #include + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template